summaryrefslogtreecommitdiffstats
path: root/private/nw/svcdlls
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/nw/svcdlls
downloadNT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip
Diffstat (limited to 'private/nw/svcdlls')
-rw-r--r--private/nw/svcdlls/coffbase.txt360
-rw-r--r--private/nw/svcdlls/dirs25
-rw-r--r--private/nw/svcdlls/nwwks/client/authpkg.c952
-rw-r--r--private/nw/svcdlls/nwwks/client/bind.c189
-rw-r--r--private/nw/svcdlls/nwwks/client/caddress.c1183
-rw-r--r--private/nw/svcdlls/nwwks/client/ccache.c324
-rw-r--r--private/nw/svcdlls/nwwks/client/drawpie.c240
-rw-r--r--private/nw/svcdlls/nwwks/client/drawpie.h9
-rw-r--r--private/nw/svcdlls/nwwks/client/folderop.icobin0 -> 1078 bytes
-rw-r--r--private/nw/svcdlls/nwwks/client/getaddr.c3619
-rw-r--r--private/nw/svcdlls/nwwks/client/gtadrnr.c1935
-rw-r--r--private/nw/svcdlls/nwwks/client/logon.c2539
-rw-r--r--private/nw/svcdlls/nwwks/client/makefile6
-rw-r--r--private/nw/svcdlls/nwwks/client/makefile.inc1
-rw-r--r--private/nw/svcdlls/nwwks/client/ndscont.icobin0 -> 4846 bytes
-rw-r--r--private/nw/svcdlls/nwwks/client/nwapi.c1182
-rw-r--r--private/nw/svcdlls/nwwks/client/nwclient.h132
-rw-r--r--private/nw/svcdlls/nwwks/client/nwclsid.h35
-rw-r--r--private/nw/svcdlls/nwwks/client/nwdlg.c3964
-rw-r--r--private/nw/svcdlls/nwwks/client/nwdlg.h217
-rw-r--r--private/nw/svcdlls/nwwks/client/nwnp.reg47
-rw-r--r--private/nw/svcdlls/nwwks/client/nwprovau.def80
-rw-r--r--private/nw/svcdlls/nwwks/client/nwprovau.dlg385
-rw-r--r--private/nw/svcdlls/nwwks/client/nwprovau.rc136
-rw-r--r--private/nw/svcdlls/nwwks/client/nwprovau.resbin0 -> 12476 bytes
-rw-r--r--private/nw/svcdlls/nwwks/client/nwshcmn.h115
-rw-r--r--private/nw/svcdlls/nwwks/client/nwshext.cxx854
-rw-r--r--private/nw/svcdlls/nwwks/client/nwshext.h279
-rw-r--r--private/nw/svcdlls/nwwks/client/nwshhelp.h78
-rw-r--r--private/nw/svcdlls/nwwks/client/nwshmenu.cxx896
-rw-r--r--private/nw/svcdlls/nwwks/client/nwshmisc.cxx261
-rw-r--r--private/nw/svcdlls/nwwks/client/nwshprop.cxx1326
-rw-r--r--private/nw/svcdlls/nwwks/client/nwshrc.h296
-rw-r--r--private/nw/svcdlls/nwwks/client/nwshui.cxx1630
-rw-r--r--private/nw/svcdlls/nwwks/client/nwspl.c2924
-rw-r--r--private/nw/svcdlls/nwwks/client/nwspl.h76
-rw-r--r--private/nw/svcdlls/nwwks/client/nwutil.c872
-rw-r--r--private/nw/svcdlls/nwwks/client/nwutil.h120
-rw-r--r--private/nw/svcdlls/nwwks/client/port.c449
-rw-r--r--private/nw/svcdlls/nwwks/client/print.icobin0 -> 1078 bytes
-rw-r--r--private/nw/svcdlls/nwwks/client/provider.c3051
-rw-r--r--private/nw/svcdlls/nwwks/client/server.bmpbin0 -> 1270 bytes
-rw-r--r--private/nw/svcdlls/nwwks/client/server.icobin0 -> 1078 bytes
-rw-r--r--private/nw/svcdlls/nwwks/client/sources101
-rw-r--r--private/nw/svcdlls/nwwks/client/tconn.c556
-rw-r--r--private/nw/svcdlls/nwwks/client/tree.bmpbin0 -> 778 bytes
-rw-r--r--private/nw/svcdlls/nwwks/client/tree.icobin0 -> 1078 bytes
-rw-r--r--private/nw/svcdlls/nwwks/dirs26
-rw-r--r--private/nw/svcdlls/nwwks/idl.tmp34
-rw-r--r--private/nw/svcdlls/nwwks/imports.idl53
-rw-r--r--private/nw/svcdlls/nwwks/inc/imports.h15
-rw-r--r--private/nw/svcdlls/nwwks/inc/nwauth.h70
-rw-r--r--private/nw/svcdlls/nwwks/inc/nwlsa.h69
-rw-r--r--private/nw/svcdlls/nwwks/inc/nwmisc.h160
-rw-r--r--private/nw/svcdlls/nwwks/inc/nwreg.h85
-rw-r--r--private/nw/svcdlls/nwwks/inc/rnrdefs.h226
-rw-r--r--private/nw/svcdlls/nwwks/inc/sapcmn.h66
-rw-r--r--private/nw/svcdlls/nwwks/inc/splutil.h81
-rw-r--r--private/nw/svcdlls/nwwks/lib/lsa.c537
-rw-r--r--private/nw/svcdlls/nwwks/lib/makefile6
-rw-r--r--private/nw/svcdlls/nwwks/lib/misc.c785
-rw-r--r--private/nw/svcdlls/nwwks/lib/reg.c1010
-rw-r--r--private/nw/svcdlls/nwwks/lib/sources47
-rw-r--r--private/nw/svcdlls/nwwks/lib/splutil.c351
-rw-r--r--private/nw/svcdlls/nwwks/makefil062
-rw-r--r--private/nw/svcdlls/nwwks/nwwks.acf10
-rw-r--r--private/nw/svcdlls/nwwks/nwwks.idl479
-rw-r--r--private/nw/svcdlls/nwwks/reg.ini30
-rw-r--r--private/nw/svcdlls/nwwks/server/address.c42
-rw-r--r--private/nw/svcdlls/nwwks/server/connect.c1688
-rw-r--r--private/nw/svcdlls/nwwks/server/credentl.c1462
-rw-r--r--private/nw/svcdlls/nwwks/server/device.c2772
-rw-r--r--private/nw/svcdlls/nwwks/server/enum.c7595
-rw-r--r--private/nw/svcdlls/nwwks/server/gateway.c1258
-rw-r--r--private/nw/svcdlls/nwwks/server/handle.h154
-rw-r--r--private/nw/svcdlls/nwwks/server/inswks.c320
-rw-r--r--private/nw/svcdlls/nwwks/server/makefile6
-rw-r--r--private/nw/svcdlls/nwwks/server/nw.h587
-rw-r--r--private/nw/svcdlls/nwwks/server/nwmain.c1570
-rw-r--r--private/nw/svcdlls/nwwks/server/nwwks.def6
-rw-r--r--private/nw/svcdlls/nwwks/server/nwwks.rc9
-rw-r--r--private/nw/svcdlls/nwwks/server/queue.c1928
-rw-r--r--private/nw/svcdlls/nwwks/server/queue.h185
-rw-r--r--private/nw/svcdlls/nwwks/server/service.c148
-rw-r--r--private/nw/svcdlls/nwwks/server/sources80
-rw-r--r--private/nw/svcdlls/nwwks/server/spool.c2289
-rw-r--r--private/nw/svcdlls/nwwks/server/util.c161
87 files changed, 57906 insertions, 0 deletions
diff --git a/private/nw/svcdlls/coffbase.txt b/private/nw/svcdlls/coffbase.txt
new file mode 100644
index 000000000..eb94f80c8
--- /dev/null
+++ b/private/nw/svcdlls/coffbase.txt
@@ -0,0 +1,360 @@
+;
+; This file defines the base virtual address for Dynamic Link Libraries (DLL)
+; that are part of the NT OS/2 System. The first token on a line is the name
+; of the DLL and the second token is the base virtual address, in hexidecimal.
+; The third token is the maximum size of the DLL image file, including symbols.
+;
+; NOTE: I have used the convention of labelling unused areas as _FREExx_.
+; If you need an address, these are available. They were used by printer
+; drivers.
+
+usermode 0x00010000 0x60000000
+slm 0x11000000 0x40000000
+slmck 0x12000000 0x40000000
+sadmin 0x13000000 0x40000000
+smss 0x20000000 0x40000000
+;
+; Printer drivers: resource only data, overlaps SMSS
+;
+brhj770 0x20000000 0x00100000
+brother9 0x20000000 0x00100000
+brothr24 0x20000000 0x00100000
+canon10e 0x20000000 0x00100000
+canon130 0x20000000 0x00100000
+canon330 0x20000000 0x00100000
+canon800 0x20000000 0x00100000
+cit24us 0x20000000 0x00100000
+cit9us 0x20000000 0x00100000
+citoh 0x20000000 0x00100000
+dec24pin 0x20000000 0x00100000
+dec9pin 0x20000000 0x00100000
+diconix 0x20000000 0x00100000
+epson24 0x20000000 0x00100000
+epson9 0x20000000 0x00100000
+escp2e 0x20000000 0x00100000
+execjet 0x20000000 0x00100000
+fuji24 0x20000000 0x00100000
+fuji9 0x20000000 0x00100000
+hpdskjet 0x20000000 0x00100000
+hppcl 0x20000000 0x00100000
+hppcl5ms 0x20000000 0x00100000
+ibm238x 0x20000000 0x00100000
+ibm239x 0x20000000 0x00100000
+ibm5204 0x20000000 0x00100000
+ibm5204 0x20000000 0x00100000
+ibmport 0x20000000 0x00100000
+jp350 0x20000000 0x00100000
+kyocera 0x20000000 0x00100000
+mantal 0x20000000 0x00100000
+mantal24 0x20000000 0x00100000
+mantal90 0x20000000 0x00100000
+mantalbj 0x20000000 0x00100000
+mt735 0x20000000 0x00100000
+mt99 0x20000000 0x00100000
+nec24pin 0x20000000 0x00100000
+oki24 0x20000000 0x00100000
+oki9 0x20000000 0x00100000
+oki9ibm 0x20000000 0x00100000
+olidm24 0x20000000 0x00100000
+olidm9 0x20000000 0x00100000
+paintjet 0x20000000 0x00100000
+panson24 0x20000000 0x00100000
+panson9 0x20000000 0x00100000
+proprint 0x20000000 0x00100000
+proprn24 0x20000000 0x00100000
+ps1 0x20000000 0x00100000
+quietjet 0x20000000 0x00100000
+qwiii 0x20000000 0x00100000
+seiko 0x20000000 0x00100000
+seiko24e 0x20000000 0x00100000
+seikosh9 0x20000000 0x00100000
+star24e 0x20000000 0x00100000
+star9e 0x20000000 0x00100000
+starjet 0x20000000 0x00100000
+thinkjet 0x20000000 0x00100000
+ti850 0x20000000 0x00100000
+toshiba 0x20000000 0x00100000
+
+; End of printer overlap area.
+
+dbgss 0x30000000 0x30000000
+csrss 0x40000000 0x20000000
+cmd 0x50000000 0x10000000
+os2ss 0x40000000 0x20000000
+psxss 0x40000000 0x20000000
+ntdll 0x60100000 0x00100000
+dbgdll 0x60200000 0x00100000
+advapi32 0x60300000 0x00100000
+csrrtl 0x60400000 0x00100000
+csrsrv 0x60500000 0x00100000
+kernel32 0x60600000 0x00100000
+basertl 0x60700000 0x00100000
+basesrv 0x60800000 0x00100000
+user32 0x60A00000 0x00200000
+winsrv 0x60C00000 0x00700000
+rasdd 0x61300000 0x00100000
+sysmono 0x61400000 0x00100000
+courier 0x61500000 0x00100000
+helv 0x61600000 0x00100000
+times 0x61700000 0x00100000
+netapi32 0x61800000 0x00200000
+winmgr 0x62100000 0x00100000
+display 0x62200000 0x00100000
+vga 0x62200000 0x00400000
+winspool 0x62600000 0x00100000
+splsrv 0x62700000 0x00100000
+ntprint 0x62800000 0x00100000
+pscript 0x62900000 0x00100000
+halftone 0x62A00000 0x00100000
+lmspool 0x62B00000 0x00100000
+os2dll 0x63000000 0x00200000
+psxdll 0x63200000 0x00200000
+winnet 0x63400000 0x00100000
+msv1_0 0x63500000 0x00100000
+samlib 0x635A0000 0x00100000
+lsaap 0x63600000 0x00100000
+netrap 0x63700000 0x00100000
+TEMPNAME2 0x63800000 0x00700000
+console 0x63f00000 0x00100000
+gdi32 0x64000000 0x00200000
+media 0x65000000 0x00100000
+mediasrv 0x66000000 0x00100000
+
+; This space for sale
+_FREE00_ 0x66100000 0x01800000
+
+ntsdexts 0x67900000 0x00100000
+plotter 0x67A00000 0x00100000
+rpcrt4 0x67B00000 0x00100000
+winmm 0x67D00000 0x00100000
+rpcssp 0x67E00000 0x00100000
+mciwave 0x67F00000 0x00100000
+rpclts1 0x68000000 0x00100000
+rpcltc1 0x68100000 0x00100000
+ldrdll 0x68200000 0x00100000
+mcicda 0x68300000 0x00100000
+plottrui 0x68400000 0x00100000
+rasddui 0x68500000 0x00100000
+pscrptui 0x68600000 0x00100000
+mmio 0x68700000 0x00100000
+rpcnts1 0x68800000 0x00100000
+rpcntc1 0x68900000 0x00100000
+nlsapi 0x68A00000 0x00100000
+
+; This space for sale
+_FREE01_ 0x68B00000 0x00200000
+
+olesvr32 0x68D00000 0x00100000
+olecli32 0x68E00000 0x00100000
+
+; This space for sale
+_FREE02_ 0x68F00000 0x00200000
+
+sys003 0x69100000 0x00100000
+
+; This space for sale
+_FREE03_ 0x69200000 0x00800000
+
+nwwks 0x69800000 0x00100000
+nwprovau 0x69900000 0x00100000
+svcctrl 0x69A00000 0x00100000
+xactsrv 0x69B00000 0x00100000
+winreg 0x69C00000 0x00100000
+samsrv 0x69D00000 0x00100000
+mmdrv 0x69F00000 0x00100000
+dlcapi 0x6A000000 0x00100000
+win32spl 0x6A100000 0x00100000
+localspl 0x6A200000 0x00100000
+rpclts5 0x6A300000 0x00100000
+rpcltc5 0x6A400000 0x00100000
+netlogon 0x6A500000 0x00200000
+tmsre32 0x6A600000 0x00100000
+lsaudll 0x6A700000 0x00100000
+mciseq 0x6A800000 0x00100000
+winprint 0x6A900000 0x00100000
+localmon 0x6AA00000 0x00100000
+msaudite 0x6AB00000 0x00100000
+msobjs 0x6AC00000 0x00100000
+hpmon 0x6AD00000 0x00100000
+alrsvc 0x6AE00000 0x00100000
+srvsvc 0x6AF00000 0x00100000
+wkssvc 0x6B000000 0x00100000
+rpclts3 0x6B100000 0x00100000
+rpcltc3 0x6B200000 0x00100000
+htui 0x6B300000 0x00100000
+lsasrv 0x6B400000 0x00100000
+mmsndsrv 0x6B500000 0x00100000
+midi 0x6B600000 0x00100000
+bowsvc 0x6B700000 0x00100000
+drivers 0x6B800000 0x00100000
+lmmon 0x6B900000 0x00100000
+spoolss 0x6BA00000 0x00100000
+sndblst 0x6BC00000 0x00100000
+
+;
+; The following is an extension for NTSD which traces memory usage in the
+; ULIB utilities...
+;
+memtrace 0x6BF00000 0x00100000
+
+; Win32 shell apps/dlls
+
+control 0x70000000 0x00100000
+color 0x70100000 0x00100000
+sound 0x70200000 0x00100000
+ports 0x70300000 0x00100000
+intl 0x70400000 0x00100000
+date 0x70500000 0x00100000
+mouse 0x70600000 0x00100000
+keybd 0x70700000 0x00100000
+desktop 0x70800000 0x00100000
+fonts 0x70900000 0x00100000
+security 0x70A00000 0x00100000
+main 0x70B00000 0x00100000
+ups 0x70C00000 0x00100000
+cursors 0x70D00000 0x00100000
+
+; Win32 National Keyboard Layers
+
+kbdbe 0x72000000 0x00100000
+kbdca 0x72100000 0x00100000
+kbdda 0x72200000 0x00100000
+kbddv 0x72300000 0x00100000
+kbdes 0x72400000 0x00100000
+kbdfc 0x72500000 0x00100000
+kbdfi 0x72600000 0x00100000
+kbdfr 0x72700000 0x00100000
+kbdgr 0x72800000 0x00100000
+kbdhe 0x72900000 0x00100000
+kbdic 0x72A00000 0x00100000
+kbdit 0x72B00000 0x00100000
+kbdla 0x72C00000 0x00100000
+kbdne 0x72D00000 0x00100000
+kbdno 0x72E00000 0x00100000
+kbdpl1 0x72F00000 0x00100000
+kbdpo 0x73000000 0x00100000
+kbdru 0x73100000 0x00100000
+kbdsf 0x73200000 0x00100000
+kbdsg 0x73300000 0x00100000
+kbdsp 0x73400000 0x00100000
+kbdsw 0x73500000 0x00100000
+kbduk 0x73600000 0x00100000
+kbdus 0x73700000 0x00100000
+kbdusl 0x73800000 0x00100000
+kbdusr 0x73900000 0x00100000
+kbdusx 0x73A00000 0x00100000
+kbdcz 0x73B00000 0x00100000
+kbdhu 0x73C00000 0x00100000
+
+;
+; The following is not currently used until we switch to the new image
+; format with the new section headers.
+;
+executive 0xD0020000 0x18000000
+
+
+
+
+
+;
+; The following are the Mep extensions
+;
+tglcase 0x09080000 0x00100000
+pmatch 0x09090000 0x00100000
+justify 0x090a0000 0x00100000
+ulcase 0x090b0000 0x00100000
+filter 0x090c0000 0x00100000
+mhelp 0x090d0000 0x00100000
+mepparty 0x090e0000 0x00100000
+srmep 0x090f0000 0x00100000
+mshelp 0x09100000 0x00100000
+mbrowse 0x09110000 0x00100000
+
+
+
+;
+; The following are for the utilities
+;
+ulib 0x09800000 0x00200000
+uhpfs 0x09a10000 0x00100000
+ufat 0x09b20000 0x00100000
+untfs 0x09c30000 0x00100000
+ifsutil 0x09d40000 0x00100000
+cufat 0x09e50000 0x00100000
+cuhpfs 0x09f60000 0x00100000
+fmifs 0x0a070000 0x00100000
+uspifs 0x0a180000 0x00100000
+ureg 0x0a290000 0x00100000
+
+;
+; The following are for the net error messages
+;
+netmsg 0x09600000 0x00080000
+neth 0x09680000 0x00080000
+netevent 0x09700000 0x00080000
+
+;
+; The following is the I/O error log messages.
+;
+iologmsg 0x09700000 0x00080000
+
+;
+; The following is for the 16-bit loader for the OS/2 subsystem
+;
+loader 0x20000000 0x01000000
+
+;
+; The following is for the Streams/Sockets dll's
+;
+wsock32 0x75000000 0x00200000
+winstrm 0x75200000 0x00100000
+sockutil 0x75300000 0x00100000
+resolver 0x75400000 0x00100000
+inetmib1 0x75500000 0x00100000
+lmmib2 0x75600000 0x00100000
+tcpipsvc 0x75700000 0x00100000
+nbtsvc 0x75800000 0x00100000
+telnet 0x75900000 0x00100000
+wshtcpip 0x75a00000 0x00100000
+mgmtapi 0x75b00000 0x00100000
+lmalrt2 0x75c00000 0x00100000
+inettrap 0x75d00000 0x00100000
+testdll 0x75e00000 0x00100000
+lmhsvc 0x75f00000 0x00100000
+
+;
+; The following are for the windowed debugger (windbg)
+;
+
+eecanx86 0x40000000 0x00200000
+eecanmip 0x40000000 0x00200000
+eecxxx86 0x40000000 0x00200000
+eecxxmip 0x40000000 0x00200000
+emmip 0x48000000 0x00200000
+emx86 0x48000000 0x00200000
+shcoff 0x50000000 0x00200000
+shcv 0x50000000 0x00200000
+wdbgexts 0x57800000 0x00200000
+tlloc 0x58000000 0x00200000
+tlser 0x58000000 0x00200000
+tlpipe 0x58000000 0x00200000
+dm 0x58200000 0x00200000
+
+;
+; The following are for the NetUI components.
+;
+
+acledit 0x76000000 0x00100000
+mpr 0x76100000 0x00100000
+ntlanman 0x76200000 0x00100000
+srvmgr 0x76300000 0x00100000
+ncpa 0x76400000 0x00100000
+mprui 0x76500000 0x00100000
+lmuicmn0 0x77000000 0x00300000
+lmuicmn1 0x78000000 0x00300000
+
+;
+; Network Card detection
+;
+netdtect 0x79000000 0x00100000
+msncdet 0x79100000 0x00100000
diff --git a/private/nw/svcdlls/dirs b/private/nw/svcdlls/dirs
new file mode 100644
index 000000000..dfe24dcaf
--- /dev/null
+++ b/private/nw/svcdlls/dirs
@@ -0,0 +1,25 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+ Steve Wood (stevewo) 17-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+
+DIRS= \
+ nwwks
diff --git a/private/nw/svcdlls/nwwks/client/authpkg.c b/private/nw/svcdlls/nwwks/client/authpkg.c
new file mode 100644
index 000000000..88495c6e5
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/authpkg.c
@@ -0,0 +1,952 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ authpkg.c
+
+Abstract:
+
+ This module is the NetWare authentication package. It saves
+ credentials in LSA, and notifies the workstation of a logoff.
+
+Author:
+
+ Jim Kelly (jimk) 11-Mar-1991
+ Cliff Van Dyke (cliffv) 25-Apr-1991
+
+Revision History:
+
+ Rita Wong (ritaw) 1-Apr-1993 Cloned for NetWare
+
+--*/
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <nwclient.h>
+
+#include <nwlsa.h>
+#include <nwreg.h>
+#include <nwauth.h>
+
+//
+// Netware authentication manager credential
+//
+#define NW_CREDENTIAL_KEY "NWCS_Credential"
+
+
+//-------------------------------------------------------------------//
+// //
+// Local functions //
+// //
+//-------------------------------------------------------------------//
+
+NTSTATUS
+AuthpSetCredential(
+ IN PLUID LogonId,
+ IN LPWSTR UserName,
+ IN LPWSTR Password
+ );
+
+NTSTATUS
+AuthpGetCredential(
+ IN PLUID LogonId,
+ OUT PNWAUTH_GET_CREDENTIAL_RESPONSE CredBuf
+ );
+
+NTSTATUS
+NwAuthGetCredential(
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN PVOID ProtocolSubmitBuffer,
+ IN PVOID ClientBufferBase,
+ IN ULONG SubmitBufferSize,
+ OUT PVOID *ProtocolReturnBuffer,
+ OUT PULONG ReturnBufferSize,
+ OUT PNTSTATUS ProtocolStatus
+ );
+
+NTSTATUS
+NwAuthSetCredential(
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN PVOID ProtocolSubmitBuffer,
+ IN PVOID ClientBufferBase,
+ IN ULONG SubmitBufferSize,
+ OUT PVOID *ProtocolReturnBuffer,
+ OUT PULONG ReturnBufferSize,
+ OUT PNTSTATUS ProtocolStatus
+ );
+
+PVOID NwAuthHeap;
+ULONG NwAuthPackageId;
+LSA_DISPATCH_TABLE Lsa;
+
+//
+// LsaApCallPackage() function dispatch table
+//
+PLSA_AP_CALL_PACKAGE
+NwCallPackageDispatch[] = {
+ NwAuthGetCredential,
+ NwAuthSetCredential
+ };
+
+//
+// Structure of the credential saved in LSA.
+//
+typedef struct _NWCREDENTIAL {
+ LPWSTR UserName;
+ LPWSTR Password;
+} NWCREDENTIAL, *PNWCREDENTIAL;
+
+//-------------------------------------------------------------------//
+// //
+// Authentication package dispatch routines. //
+// //
+//-------------------------------------------------------------------//
+
+NTSTATUS
+LsaApInitializePackage (
+ IN ULONG AuthenticationPackageId,
+ IN PLSA_DISPATCH_TABLE LsaDispatchTable,
+ IN PSTRING Database OPTIONAL,
+ IN PSTRING Confidentiality OPTIONAL,
+ OUT PSTRING *AuthenticationPackageName
+ )
+
+/*++
+
+Routine Description:
+
+ This service is called once by the LSA during system initialization to
+ provide the DLL a chance to initialize itself.
+
+Arguments:
+
+ AuthenticationPackageId - The ID assigned to the authentication
+ package.
+
+ LsaDispatchTable - Provides the address of a table of LSA
+ services available to authentication packages. The services
+ of this table are ordered according to the enumerated type
+ LSA_DISPATCH_TABLE_API.
+
+ Database - This parameter is not used by this authentication package.
+
+ Confidentiality - This parameter is not used by this authentication
+ package.
+
+ AuthenticationPackageName - Receives the name of the
+ authentication package. The authentication package is
+ responsible for allocating the buffer that the string is in
+ (using the AllocateLsaHeap() service) and returning its
+ address here. The buffer will be deallocated by LSA when it
+ is no longer needed.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+
+--*/
+
+{
+
+ PSTRING NameString;
+ PCHAR NameBuffer;
+
+
+ UNREFERENCED_PARAMETER(Database);
+ UNREFERENCED_PARAMETER(Confidentiality);
+
+ //
+ // Use the process heap for memory allocations.
+ //
+ NwAuthHeap = RtlProcessHeap();
+
+
+ NwAuthPackageId = AuthenticationPackageId;
+
+ //
+ // Copy the LSA service dispatch table
+ //
+ Lsa.CreateLogonSession = LsaDispatchTable->CreateLogonSession;
+ Lsa.DeleteLogonSession = LsaDispatchTable->DeleteLogonSession;
+ Lsa.AddCredential = LsaDispatchTable->AddCredential;
+ Lsa.GetCredentials = LsaDispatchTable->GetCredentials;
+ Lsa.DeleteCredential = LsaDispatchTable->DeleteCredential;
+ Lsa.AllocateLsaHeap = LsaDispatchTable->AllocateLsaHeap;
+ Lsa.FreeLsaHeap = LsaDispatchTable->FreeLsaHeap;
+ Lsa.AllocateClientBuffer = LsaDispatchTable->AllocateClientBuffer;
+ Lsa.FreeClientBuffer = LsaDispatchTable->FreeClientBuffer;
+ Lsa.CopyToClientBuffer = LsaDispatchTable->CopyToClientBuffer;
+ Lsa.CopyFromClientBuffer = LsaDispatchTable->CopyFromClientBuffer;
+
+ //
+ // Allocate and return our package name
+ //
+ NameBuffer = (*Lsa.AllocateLsaHeap)(sizeof(NW_AUTH_PACKAGE_NAME));
+ strcpy(NameBuffer, NW_AUTH_PACKAGE_NAME);
+
+ NameString = (*Lsa.AllocateLsaHeap)(sizeof(STRING));
+ RtlInitString(NameString, NameBuffer);
+ (*AuthenticationPackageName) = NameString;
+
+ //
+ // Delete outdated credential information in the registry
+ //
+ NwDeleteCurrentUser();
+ (void) NwDeleteServiceLogon(NULL);
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+LsaApLogonUser (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN SECURITY_LOGON_TYPE LogonType,
+ IN PVOID ProtocolSubmitBuffer,
+ IN PVOID ClientBufferBase,
+ IN ULONG SubmitBufferSize,
+ OUT PVOID *ProfileBuffer,
+ OUT PULONG ProfileBufferSize,
+ OUT PLUID LogonId,
+ OUT PNTSTATUS SubStatus,
+ OUT PLSA_TOKEN_INFORMATION_TYPE TokenInformationType,
+ OUT PVOID *TokenInformation,
+ OUT PUNICODE_STRING *AccountName,
+ OUT PUNICODE_STRING *AuthenticatingAuthority
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to authenticate a user logon attempt. This may be
+ the user's initial logon, necessary to gain access to NT, or may
+ be a subsequent logon attempt. If the logon is the user's initial
+ logon, then a new LSA logon session will be established for the user
+ and a PrimaryToken will be returned. Otherwise, the authentication
+ package will associated appropriate credentials with the already logged
+ on user's existing LSA logon session.
+
+Arguments:
+
+ ClientRequest - Is a pointer to an opaque data structure
+ representing the client's request.
+
+ LogonType - Identifies the type of logon being attempted.
+
+ ProtocolSubmitBuffer - Supplies the authentication
+ information specific to the authentication package.
+
+ ClientBufferBase - Provides the address within the client
+ process at which the authentication information was resident.
+ This may be necessary to fix-up any pointers within the
+ authentication information buffer.
+
+ SubmitBufferSize - Indicates the Size, in bytes,
+ of the authentication information buffer.
+
+ ProfileBuffer - Is used to return the address of the profile
+ buffer in the client process. The authentication package is
+ responsible for allocating and returning the profile buffer
+ within the client process. However, if the LSA subsequently
+ encounters an error which prevents a successful logon, then
+ the LSA will take care of deallocating that buffer. This
+ buffer is expected to have been allocated with the
+ AllocateClientBuffer() service.
+
+ The format and semantics of this buffer are specific to the
+ authentication package.
+
+ ProfileBufferSize - Receives the size (in bytes) of the
+ returned profile buffer.
+
+ LogonId - Points to a buffer into which the authentication
+ package must return a logon ID that uniquely
+ identifies this logon session.
+
+ SubStatus - If the logon failed due to account restrictions, the
+ reason for the failure should be returned via this parameter.
+ The reason is authentication-package specific. The substatus
+ values for authentication package "MSV1.0" are:
+
+ STATUS_INVALID_LOGON_HOURS
+
+ STATUS_INVALID_WORKSTATION
+
+ STATUS_PASSWORD_EXPIRED
+
+ STATUS_ACCOUNT_DISABLED
+
+ TokenInformationType - If the logon is successful, this field is
+ used to indicate what level of information is being returned
+ for inclusion in the Token to be created. This information
+ is returned via the TokenInformation parameter.
+
+ TokenInformation - If the logon is successful, this parameter is
+ used by the authentication package to return information to
+ be included in the token. The format and content of the
+ buffer returned is indicated by the TokenInformationLevel
+ return value.
+
+ AccountName - A Unicode string describing the account name
+ being logged on to. This parameter must always be returned
+ regardless of the success or failure of the operation.
+
+ AuthenticatingAuthority - A Unicode string describing the Authenticating
+ Authority for the logon. This string may optionally be omitted.
+
+Return Value:
+
+ STATUS_NOT_IMPLEMENTED - NetWare authentication package does not
+ support login.
+
+--*/
+
+{
+ UNREFERENCED_PARAMETER(ClientRequest);
+ UNREFERENCED_PARAMETER(LogonType);
+ UNREFERENCED_PARAMETER(ProtocolSubmitBuffer);
+ UNREFERENCED_PARAMETER(ClientBufferBase);
+ UNREFERENCED_PARAMETER(SubmitBufferSize);
+ UNREFERENCED_PARAMETER(ProfileBuffer);
+ UNREFERENCED_PARAMETER(ProfileBufferSize);
+ UNREFERENCED_PARAMETER(LogonId);
+ UNREFERENCED_PARAMETER(SubStatus);
+ UNREFERENCED_PARAMETER(TokenInformationType);
+ UNREFERENCED_PARAMETER(TokenInformation);
+ UNREFERENCED_PARAMETER(AccountName);
+ UNREFERENCED_PARAMETER(AuthenticatingAuthority);
+
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+
+NTSTATUS
+LsaApCallPackage (
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN PVOID ProtocolSubmitBuffer,
+ IN PVOID ClientBufferBase,
+ IN ULONG SubmitBufferLength,
+ OUT PVOID *ProtocolReturnBuffer,
+ OUT PULONG ReturnBufferLength,
+ OUT PNTSTATUS ProtocolStatus
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is the dispatch routine for
+ LsaCallAuthenticationPackage().
+
+Arguments:
+
+ ClientRequest - Is a pointer to an opaque data structure
+ representing the client's request.
+
+ ProtocolSubmitBuffer - Supplies a protocol message specific to
+ the authentication package.
+
+ ClientSubmitBufferBase - Supplies the client address of the submitted
+ protocol message.
+
+ SubmitBufferLength - Indicates the length of the submitted
+ protocol message buffer.
+
+ ProtocolReturnBuffer - Is used to return the address of the
+ protocol buffer in the client process. The authentication
+ package is responsible for allocating and returning the
+ protocol buffer within the client process. This buffer is
+ expected to have been allocated with the
+ AllocateClientBuffer() service.
+
+ The format and semantics of this buffer are specific to the
+ authentication package.
+
+ ReturnBufferLength - Receives the length (in bytes) of the
+ returned protocol buffer.
+
+ ProtocolStatus - Assuming the services completion is
+ STATUS_SUCCESS, this parameter will receive completion status
+ returned by the specified authentication package. The list
+ of status values that may be returned are authentication
+ package specific.
+
+Return Status:
+
+--*/
+
+{
+
+ ULONG MessageType;
+
+ //
+ // Get the messsage type from the protocol submit buffer.
+ //
+
+ if ( SubmitBufferLength < sizeof(NWAUTH_MESSAGE_TYPE) ) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ MessageType =
+ (ULONG) *((PNWAUTH_MESSAGE_TYPE)(ProtocolSubmitBuffer));
+
+ if ( MessageType >=
+ (sizeof(NwCallPackageDispatch)/sizeof(NwCallPackageDispatch[0])) ) {
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Allow the dispatch routines to only set the return buffer information
+ // on success conditions.
+ //
+
+ *ProtocolReturnBuffer = NULL;
+ *ReturnBufferLength = 0;
+
+ //
+ // Call the appropriate routine for this message.
+ //
+
+ return (*(NwCallPackageDispatch[MessageType]))(
+ ClientRequest,
+ ProtocolSubmitBuffer,
+ ClientBufferBase,
+ SubmitBufferLength,
+ ProtocolReturnBuffer,
+ ReturnBufferLength,
+ ProtocolStatus
+ ) ;
+}
+
+
+VOID
+LsaApLogonTerminated (
+ IN PLUID LogonId
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to notify each authentication package when a logon
+ session terminates. A logon session terminates when the last token
+ referencing the logon session is deleted.
+
+Arguments:
+
+ LogonId - Is the logon ID that just logged off.
+
+Return Status:
+
+ None.
+
+--*/
+
+{
+ DWORD status;
+
+ LONG RegError;
+ HKEY WkstaKey = NULL;
+ HKEY WkstaLogonKey = NULL;
+ HKEY CurrentUserKey = NULL;
+
+ LPWSTR CurrentUser = NULL;
+ PLUID CurrentLogonId = NULL;
+
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("\nNWPROVAU: LsaApLogonTerminated\n"));
+ }
+#endif
+
+ RpcTryExcept {
+
+ //
+ // The logon ID may be for a service login
+ //
+ if (NwDeleteServiceLogon(LogonId) == NO_ERROR) {
+
+ //
+ // Tell workstation to log off the service.
+ //
+ (void) NwrLogoffUser(NULL, LogonId);
+ goto Done;
+ }
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters to delete the CurrentUser
+ // value to indicate that there is currently no interactively
+ // logged on user.
+ //
+ RegError = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ | KEY_WRITE | DELETE,
+ &WkstaKey
+ );
+
+ if (RegError == NO_ERROR) {
+
+ //
+ // Read the current user SID string so that we
+ // know which key is the current user key to open.
+ //
+ status = NwReadRegValue(
+ WkstaKey,
+ NW_CURRENTUSER_VALUENAME,
+ &CurrentUser
+ );
+
+ if (status == NO_ERROR) {
+
+ //
+ // Open the Logon key
+ //
+ RegError = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_LOGON_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ,
+ &WkstaLogonKey
+ );
+
+ //
+ // Open the <CurrentUser> key under Logon
+ //
+ if ( RegError == NO_ERROR ) {
+
+ RegError = RegOpenKeyExW( WkstaLogonKey,
+ CurrentUser,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ,
+ &CurrentUserKey );
+
+ }
+
+ if (RegError == NO_ERROR) {
+
+ //
+ // Read the logon ID
+ //
+ status = NwReadRegValue(
+ CurrentUserKey,
+ NW_LOGONID_VALUENAME,
+ (LPWSTR *) &CurrentLogonId
+ );
+
+ if (status == NO_ERROR) {
+
+ if (RtlEqualLuid(CurrentLogonId, LogonId)) {
+
+ //
+ // Only delete the current user value if the
+ // logon ID of the logging off user is the same
+ // as the current user's logon ID.
+ //
+ (void) RegDeleteValueW(
+ WkstaKey,
+ NW_CURRENTUSER_VALUENAME
+ );
+
+ }
+
+
+ //
+ // Tell workstation to log off the
+ // interactive user.
+ //
+ (void) NwrLogoffUser(NULL, LogonId);
+
+ }
+
+ }
+
+
+ }
+
+ }
+
+Done: ;
+
+ }
+ RpcExcept(1) {
+ //status = NwpMapRpcError(RpcExceptionCode());
+
+ }
+ RpcEndExcept
+
+ //
+ // free up before returning in case of premature exit (eg. RPC exceptions).
+ //
+ if (WkstaKey != NULL)
+ (void) RegCloseKey(WkstaKey);
+ if (WkstaLogonKey != NULL)
+ (void) RegCloseKey(WkstaLogonKey);
+ if (CurrentUserKey != NULL)
+ (void) RegCloseKey(CurrentUserKey);
+ if (CurrentLogonId != NULL)
+ (void) LocalFree((HLOCAL) CurrentLogonId);
+ if (CurrentUser != NULL)
+ (void) LocalFree((HLOCAL) CurrentUser);
+}
+
+
+NTSTATUS
+NwAuthGetCredential(
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN PVOID ProtocolSubmitBuffer,
+ IN PVOID ClientBufferBase,
+ IN ULONG SubmitBufferSize,
+ OUT PVOID *ProtocolReturnBuffer,
+ OUT PULONG ReturnBufferSize,
+ OUT PNTSTATUS ProtocolStatus
+ )
+/*++
+
+Routine Description:
+
+ This routine is the dispatch routine for LsaCallAuthenticationPackage()
+ with a message type of NwAuth_GetCredential. It is called by
+ the NetWare workstation service to get the username and password
+ associated with a logon ID.
+
+Arguments:
+
+ The arguments to this routine are identical to those of LsaApCallPackage.
+ Only the special attributes of these parameters as they apply to
+ this routine are mentioned here.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ PNWAUTH_GET_CREDENTIAL_RESPONSE LocalBuf;
+
+
+ UNREFERENCED_PARAMETER(ClientBufferBase);
+
+ //
+ // Ensure the specified Submit Buffer is of reasonable size.
+ //
+ if (SubmitBufferSize < sizeof(NWAUTH_GET_CREDENTIAL_REQUEST)) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Allocate a local buffer and a buffer in client's address space.
+ //
+ *ReturnBufferSize = sizeof(NWAUTH_GET_CREDENTIAL_RESPONSE);
+
+ LocalBuf = RtlAllocateHeap(NwAuthHeap, 0, *ReturnBufferSize);
+
+ if (LocalBuf == NULL) {
+ return STATUS_NO_MEMORY;
+ }
+
+ Status = (*Lsa.AllocateClientBuffer)(
+ ClientRequest,
+ *ReturnBufferSize,
+ (PVOID *) ProtocolReturnBuffer
+ );
+
+ if (! NT_SUCCESS( Status )) {
+ RtlFreeHeap(NwAuthHeap, 0, LocalBuf);
+ return Status;
+ }
+
+ //
+ // Get the credential from LSA
+ //
+ Status = AuthpGetCredential(
+ &(((PNWAUTH_GET_CREDENTIAL_REQUEST) ProtocolSubmitBuffer)->LogonId),
+ LocalBuf
+ );
+
+ if (! NT_SUCCESS(Status)) {
+ goto Cleanup;
+ }
+
+ //
+ // Copy the data to the client's address space.
+ //
+ Status = (*Lsa.CopyToClientBuffer)(
+ ClientRequest,
+ *ReturnBufferSize,
+ (PVOID) *ProtocolReturnBuffer,
+ (PVOID) LocalBuf
+ );
+
+Cleanup:
+
+ RtlFreeHeap(NwAuthHeap, 0, LocalBuf);
+
+ //
+ // If we weren't successful, free the buffer in the clients address space.
+ // Otherwise, the client will free the memory when done.
+ //
+
+ if (! NT_SUCCESS(Status)) {
+
+ (VOID) (*Lsa.FreeClientBuffer)(
+ ClientRequest,
+ *ProtocolReturnBuffer
+ );
+
+ *ProtocolReturnBuffer = NULL;
+ }
+
+ //
+ // Return status to the caller.
+ //
+ *ProtocolStatus = Status;
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+NwAuthSetCredential(
+ IN PLSA_CLIENT_REQUEST ClientRequest,
+ IN PVOID ProtocolSubmitBuffer,
+ IN PVOID ClientBufferBase,
+ IN ULONG SubmitBufferSize,
+ OUT PVOID *ProtocolReturnBuffer,
+ OUT PULONG ReturnBufferSize,
+ OUT PNTSTATUS ProtocolStatus
+ )
+/*++
+
+Routine Description:
+
+ This routine is the dispatch routine for LsaCallAuthenticationPackage()
+ with a message type of NwAuth_SetCredential. It is called by
+ the NetWare credential manager DLL on user logon to save the username
+ and password of the logon session.
+
+Arguments:
+
+ The arguments to this routine are identical to those of LsaApCallPackage.
+ Only the special attributes of these parameters as they apply to
+ this routine are mentioned here.
+
+Return Value:
+
+ STATUS_SUCCESS - Indicates the service completed successfully.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+
+ UNREFERENCED_PARAMETER(ClientBufferBase);
+
+
+ //
+ // Ensure the specified Submit Buffer is of reasonable size.
+ //
+ if (SubmitBufferSize < sizeof(NWAUTH_SET_CREDENTIAL_REQUEST)) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NwAuthSetCredential: LogonId %08lx%08lx Username %ws\n",
+ ((PNWAUTH_SET_CREDENTIAL_REQUEST) ProtocolSubmitBuffer)->LogonId.HighPart,
+ ((PNWAUTH_SET_CREDENTIAL_REQUEST) ProtocolSubmitBuffer)->LogonId.LowPart,
+ ((PNWAUTH_SET_CREDENTIAL_REQUEST) ProtocolSubmitBuffer)->UserName
+ ));
+ }
+#endif
+
+ //
+ // Set the credential in LSA
+ //
+ Status = AuthpSetCredential(
+ &(((PNWAUTH_SET_CREDENTIAL_REQUEST) ProtocolSubmitBuffer)->LogonId),
+ ((PNWAUTH_SET_CREDENTIAL_REQUEST) ProtocolSubmitBuffer)->UserName,
+ ((PNWAUTH_SET_CREDENTIAL_REQUEST) ProtocolSubmitBuffer)->Password
+ );
+
+ *ProtocolStatus = Status;
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+AuthpGetCredential(
+ IN PLUID LogonId,
+ OUT PNWAUTH_GET_CREDENTIAL_RESPONSE CredBuf
+ )
+/*++
+
+Routine Description:
+
+ This routine retrieves the credential saved in LSA given the
+ logon ID.
+
+Arguments:
+
+ LogonId - Supplies the logon ID for the logon session.
+
+ CredBuf - Buffer to receive the credential.
+
+Return Value:
+
+
+--*/
+{
+ NTSTATUS Status;
+
+ STRING KeyString;
+ STRING CredString;
+ ULONG QueryContext = 0;
+ ULONG KeyLength;
+
+ PNWCREDENTIAL Credential;
+
+
+ RtlInitString(&KeyString, NW_CREDENTIAL_KEY);
+
+ Status = (*Lsa.GetCredentials)(
+ LogonId,
+ NwAuthPackageId,
+ &QueryContext,
+ (BOOLEAN) FALSE, // Just retrieve matching key
+ &KeyString,
+ &KeyLength,
+ &CredString
+ );
+
+ if (! NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+ Credential = (PNWCREDENTIAL) CredString.Buffer;
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("AuthpGetCredential: Got CredentialSize %lu\n", CredString.Length));
+ }
+#endif
+
+ //
+ // Make the pointers absolute.
+ //
+ Credential->UserName = (LPWSTR) ((DWORD) Credential->UserName +
+ (DWORD) Credential);
+ Credential->Password = (LPWSTR) ((DWORD) Credential->Password +
+ (DWORD) Credential);
+
+ wcscpy(CredBuf->UserName, Credential->UserName);
+ wcscpy(CredBuf->Password, Credential->Password);
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+AuthpSetCredential(
+ IN PLUID LogonId,
+ IN LPWSTR UserName,
+ IN LPWSTR Password
+ )
+/*++
+
+Routine Description:
+
+ This routine saves the credential in LSA.
+
+Arguments:
+
+ LogonId - Supplies the logon ID for the logon session.
+
+ UserName, Password - Credential for the logon session.
+
+Return Value:
+
+
+--*/
+{
+ NTSTATUS Status;
+ PNWCREDENTIAL Credential;
+ DWORD CredentialSize;
+
+ STRING CredString;
+ STRING KeyString;
+
+
+ //
+ // Allocate memory to package the credential.
+ //
+ CredentialSize = sizeof(NWCREDENTIAL) +
+ (wcslen(UserName) + wcslen(Password) + 2) *
+ sizeof(WCHAR);
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("AuthpSetCredential: CredentialSize is %lu\n", CredentialSize));
+ }
+#endif
+ Credential = RtlAllocateHeap(NwAuthHeap, 0, CredentialSize);
+
+ if (Credential == NULL) {
+ return STATUS_NO_MEMORY;
+ }
+ RtlZeroMemory(Credential, CredentialSize);
+
+ //
+ // Pack the credential
+ //
+ Credential->UserName = (LPWSTR) (((DWORD) Credential) + sizeof(NWCREDENTIAL));
+ wcscpy(Credential->UserName, UserName);
+
+ Credential->Password = (LPWSTR) ((DWORD) Credential->UserName +
+ (wcslen(UserName) + 1) * sizeof(WCHAR));
+ wcscpy(Credential->Password, Password);
+
+ //
+ // Make the pointers self-relative.
+ //
+ Credential->UserName = (LPWSTR) ((DWORD) Credential->UserName -
+ (DWORD) Credential);
+ Credential->Password = (LPWSTR) ((DWORD) Credential->Password -
+ (DWORD) Credential);
+
+ //
+ // Add credential to logon session
+ //
+ RtlInitString(&KeyString, NW_CREDENTIAL_KEY);
+
+ CredString.Buffer = (PCHAR) Credential;
+ CredString.Length = (USHORT) CredentialSize;
+ CredString.MaximumLength = (USHORT) CredentialSize;
+
+ Status = (*Lsa.AddCredential)(
+ LogonId,
+ NwAuthPackageId,
+ &KeyString,
+ &CredString
+ );
+
+ if (! NT_SUCCESS(Status)) {
+ KdPrint(( "NWPROVAU: AuthpSetCredential: error from AddCredential %lX\n",
+ Status));
+ }
+
+ return Status;
+}
diff --git a/private/nw/svcdlls/nwwks/client/bind.c b/private/nw/svcdlls/nwwks/client/bind.c
new file mode 100644
index 000000000..c87aa7f7d
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/bind.c
@@ -0,0 +1,189 @@
+/*++
+
+Copyright (c) 1991-1993 Microsoft Corporation
+
+Module Name:
+
+ bind.c
+
+Abstract:
+
+ Contains the client-side RPC bind and unbind routines for Workstation
+ service.
+
+Author:
+
+ Rita Wong (ritaw) 12-Feb-1993
+
+Environment:
+
+ User Mode -Win32
+
+Revision History:
+
+--*/
+
+//
+// INCLUDES
+//
+#include <nwclient.h>
+#include <rpcutil.h> // RpcUtils for binding
+#include <svcs.h> // For the common pipe name to bind to
+
+
+
+handle_t
+NWWKSTA_IMPERSONATE_HANDLE_bind(
+ NWWKSTA_IMPERSONATE_HANDLE Reserved
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called from the Workstation service client when
+ it is necessary create an RPC binding to the server end with
+ impersonation level of impersonation.
+
+Arguments:
+
+
+Return Value:
+
+ The binding handle is returned to the stub routine. If the bind is
+ unsuccessful, a NULL will be returned.
+
+--*/
+{
+ handle_t BindHandle = 0;
+ RPC_STATUS RpcStatus;
+
+
+ UNREFERENCED_PARAMETER(Reserved);
+
+ RpcStatus = NetpBindRpc(
+ NULL,
+ SVCS_RPC_PIPE,
+ L"Security=Impersonation Dynamic False",
+ &BindHandle
+ );
+
+ if (RpcStatus != RPC_S_OK) {
+ KdPrint((
+ "NWWORKSTATION: Client NWWKSTA_IMPERSONATE_HANDLE_bind failed: %lu\n",
+ RpcStatus
+ ));
+ }
+
+ return BindHandle;
+}
+
+
+
+handle_t
+NWWKSTA_IDENTIFY_HANDLE_bind(
+ NWWKSTA_IDENTIFY_HANDLE Reserved
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called from the Workstation service client stubs when
+ it is necessary create an RPC binding to the server end with
+ identification level of impersonation.
+
+Arguments:
+
+
+Return Value:
+
+ The binding handle is returned to the stub routine. If the bind is
+ unsuccessful, a NULL will be returned.
+
+--*/
+{
+ handle_t BindHandle = 0;
+ RPC_STATUS RpcStatus;
+
+
+ UNREFERENCED_PARAMETER(Reserved);
+
+ RpcStatus = NetpBindRpc(
+ NULL,
+ SVCS_RPC_PIPE,
+ L"Security=Identification Dynamic False",
+ &BindHandle
+ );
+
+ if (RpcStatus != RPC_S_OK) {
+ KdPrint((
+ "NWWORKSTATION: Client NWWKSTA_IDENTIFY_HANDLE_bind failed: %lu\n",
+ RpcStatus
+ ));
+ }
+
+ return BindHandle;
+}
+
+
+
+void
+NWWKSTA_IMPERSONATE_HANDLE_unbind(
+ NWWKSTA_IMPERSONATE_HANDLE Reserved,
+ handle_t BindHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine unbinds the impersonation generic handle.
+
+Arguments:
+
+ Reserved -
+
+ BindingHandle - This is the binding handle that is to be closed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ UNREFERENCED_PARAMETER(Reserved);
+
+ NetpUnbindRpc(BindHandle);
+}
+
+
+
+void
+NWWKSTA_IDENTIFY_HANDLE_unbind(
+ NWWKSTA_IDENTIFY_HANDLE Reserved,
+ handle_t BindHandle
+ )
+
+/*++
+
+Routine Description:
+
+ This routine unbinds the identification generic handle.
+
+Arguments:
+
+ Reserved -
+
+ BindingHandle - This is the binding handle that is to be closed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ UNREFERENCED_PARAMETER(Reserved);
+
+ NetpUnbindRpc(BindHandle);
+}
diff --git a/private/nw/svcdlls/nwwks/client/caddress.c b/private/nw/svcdlls/nwwks/client/caddress.c
new file mode 100644
index 000000000..77b49d776
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/caddress.c
@@ -0,0 +1,1183 @@
+/*++
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ address.c
+
+Abstract:
+
+ This module contains the code to support NPGetAddressByName.
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 18-Apr-94
+
+Revision History:
+
+ yihsins Created
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <winsock2.h>
+#include "ncp.h"
+#include <wsipx.h>
+#include <ws2spi.h>
+#include <nwxchg.h>
+#include <ntddnwfs.h>
+#include <rpc.h>
+#include <rpcdce.h>
+#include "rnrdefs.h"
+#include "sapcmn.h"
+#include <time.h>
+#include <rnraddrs.h>
+
+
+//-------------------------------------------------------------------//
+// //
+// Special Externs
+// //
+//-------------------------------------------------------------------//
+
+NTSTATUS
+NwOpenAServer(
+ PWCHAR pwszServName,
+ PHANDLE ServerHandle,
+ BOOL fVerify
+ );
+
+
+//-------------------------------------------------------------------//
+// //
+// Local Function Prototypes //
+// //
+//-------------------------------------------------------------------//
+
+#define IPX_ADDRESS_LENGTH 12
+#define MAX_PROPERTY_BUFFER_LENGTH 128
+
+DWORD
+NwrpGetAddressByNameInner(
+ IN HANDLE hServer,
+ IN WORD nServiceType,
+ IN LPWSTR lpServiceName,
+ IN BOOL fAnsi,
+ IN OUT LPSOCKADDR_IPX lpSockAddr,
+ OUT PDWORD pdwVersion
+ );
+
+
+BOOL
+NwConvertToUnicode(
+ OUT LPWSTR *UnicodeOut,
+ IN LPSTR OemIn
+ );
+
+DWORD
+NwMapBinderyCompletionCode(
+ IN NTSTATUS ntstatus
+ );
+
+#if 0
+DWORD
+NwpFetchClassType(
+ HANDLE hServer,
+ PUNICODE_STRING pUString,
+ PBYTE pbBuffer
+ );
+#endif
+
+DWORD
+NwppGetClassInfo(
+ IN PWCHAR pwszServerName,
+ IN LPWSTR lpszServiceClassName,
+ IN LPGUID lpServiceClassType,
+ OUT PLONG plSpare,
+ OUT PDWORD pdwClassInfos,
+ OUT LPGUID lpFoundType,
+ OUT PWCHAR *ppwszFoundName,
+ IN LONG lSpace,
+ OUT PBYTE pbBuffer
+ );
+
+BOOL
+NwpEnumClassInfoServers(
+ IN OUT PHANDLE phServ,
+ IN OUT PLONG plIndex,
+ IN PWCHAR pwszServerName,
+ IN BOOL fVerify
+ );
+
+#if 0
+
+DWORD
+NwppSetClassInfo(
+ IN LPWSTR pwszClassInfoName,
+ IN LPGUID lpClassType,
+ IN PCHAR pbProperty,
+ IN LPWSTR pwszServerName
+ );
+
+#endif
+
+DWORD
+NwpCreateAndWriteProperty(
+ IN HANDLE hServer,
+ IN LPSTR lpszPropertyName,
+ IN PUNICODE_STRING pusObjectName,
+ IN WORD ObjectType,
+ IN PCHAR pbPropertyBuffer
+ );
+
+//-------------------------------------------------------------------//
+// //
+// Function Bodies //
+// //
+//-------------------------------------------------------------------//
+
+
+DWORD
+NwpGetHandleForServer(
+ PWCHAR pwszServerName,
+ PHANDLE phServer,
+ BOOL fVerify
+ )
+/*++
+Routine Description:
+ Find a handle to use, or make one. This calls into device.c to do the
+ real work.
+--*/
+{
+ DWORD err = NO_ERROR;
+
+ if(!*phServer)
+ {
+ if(!pwszServerName)
+ {
+ pwszServerName = NW_RDR_PREFERRED_SUFFIX;
+ }
+
+
+ err = NwOpenAServer(pwszServerName, phServer, fVerify);
+ }
+ return(err);
+}
+
+
+DWORD
+NwpGetRnRAddress(
+ IN OUT PHANDLE phServer,
+ IN LPWSTR lpszContext,
+ IN OUT PLONG plIndex,
+ IN LPWSTR lpServiceName,
+ IN WORD nType,
+ OUT PDWORD pdwVersion,
+ DWORD dwInSize,
+ OUT LPWSTR ServiceName,
+ OUT LPSOCKADDR_IPX lpSockAddr
+ )
+/*++
+Routine Description:
+ Called to get the name and address of the next item of nType type.
+ If a name is supplied as well, then there is no enumeration. This is
+ called from NSPLookupServiceNext and the parameters are close analogs
+ of the ones it receives.
+--*/
+{
+ NTSTATUS ntstatus;
+ CHAR szObjectName[48];
+ DWORD err = NO_ERROR;
+ PWCHAR pwszObjectName;
+ PWCHAR pwszConv;
+ BOOL fAll, fAnsi;
+
+ //
+ // Open a server for enumeration and querying
+ //
+
+ err = NwpGetHandleForServer(lpszContext, phServer, FALSE);
+ if(err == NO_ERROR)
+ {
+ if(!lpServiceName)
+ {
+ lpServiceName = L"*";
+ }
+ if(wcschr(lpServiceName, L'*'))
+ {
+ WORD ObjectType;
+ //
+ // we've no name, or we have an enumeration
+ //
+
+ UNICODE_STRING U;
+
+ RtlInitUnicodeString(&U, lpServiceName);
+
+ ntstatus = NwlibMakeNcp(
+ *phServer,
+ FSCTL_NWR_NCP_E3H,
+ 58,
+ 59,
+ "bdwU|dwc",
+ 0x37,
+ *plIndex,
+ nType,
+ &U,
+ plIndex,
+ &ObjectType,
+ &szObjectName);
+
+ if(NT_SUCCESS(ntstatus))
+ {
+
+ //
+ // got another one.
+ //
+
+ //
+ // got another one. Convert the name
+ //
+
+ if(!NwConvertToUnicode(&pwszConv, szObjectName))
+ {
+ //
+ // out of space ...
+ //
+
+ err = WN_NO_MORE_ENTRIES;
+ }
+
+ fAll = TRUE;
+
+ if(nType == OT_DIRSERVER)
+ {
+ //
+ // looking for DIRSERVERs is tricky and requires
+ // preserving the name intact. This includes some
+ // binary cruft, so special case it.
+ //
+ fAnsi = TRUE;
+ pwszObjectName = (PWCHAR)szObjectName;
+ }
+ else
+ {
+ fAnsi = FALSE;
+ pwszObjectName = pwszConv;
+ }
+ }
+ }
+ else
+ {
+ //
+ // a non-enumerattion name was given. Use it
+ //
+
+ fAnsi = FALSE;
+ pwszConv = pwszObjectName = lpServiceName;
+ fAll = FALSE;
+ ntstatus = 0;
+ }
+
+ if((err == NO_ERROR)
+ &&
+ NT_SUCCESS(ntstatus))
+ {
+ //
+ // we've a name and type to lookup. Call the old RnR
+ // serice routine to do it. First, return the name.
+ // But return the name first
+
+ DWORD dwLen;
+
+ if(fAnsi)
+ {
+ //
+ // it's an NDS tree server. Have to munge the name
+ // a bit
+ //
+
+ PWCHAR pwszTemp = &pwszConv[31];
+
+ while(*pwszTemp == L'_')
+ {
+ pwszTemp--;
+ }
+ dwLen = (PCHAR)pwszTemp - (PCHAR)pwszConv + sizeof(WCHAR);
+ }
+ else
+ {
+ dwLen = wcslen(pwszConv) * sizeof(WCHAR);
+ }
+
+ dwLen = min(dwInSize, dwLen);
+
+ RtlCopyMemory(ServiceName, pwszConv, dwLen);
+
+ memset(((PBYTE)ServiceName) + dwLen,
+ 0,
+ dwInSize - dwLen);
+
+ err = NwrpGetAddressByNameInner(
+ *phServer,
+ nType,
+ pwszObjectName,
+ fAnsi,
+ lpSockAddr,
+ pdwVersion);
+
+ if(fAll)
+ {
+ LocalFree(pwszConv);
+ }
+ }
+ }
+ if(err == NO_ERROR)
+ {
+ err = NwMapBinderyCompletionCode(ntstatus);
+ }
+ return(err);
+}
+
+DWORD
+NwpGetAddressByName(
+ IN LPWSTR Reserved,
+ IN WORD nServiceType,
+ IN LPWSTR lpServiceName,
+ IN OUT LPSOCKADDR_IPX lpSockAddr
+ )
+/*++
+
+Routine Description:
+
+ This routine returns address information about a specific service.
+
+Arguments:
+
+ Reserved - unused
+
+ nServiceType - netware service type
+
+ lpServiceName - unique string representing the service name, in the
+ Netware case, this is the server name
+
+ lpSockAddr - on return, will be filled with SOCKADDR_IPX
+
+Return Value:
+
+ Win32 error.
+
+--*/
+{
+
+ NTSTATUS ntstatus;
+ HANDLE hServer = 0;
+ DWORD err;
+
+ UNREFERENCED_PARAMETER( Reserved );
+
+ err = NwpGetHandleForServer( 0, &hServer, FALSE );
+
+ if (err == NO_ERROR)
+ {
+ err = NwrpGetAddressByNameInner(
+ hServer,
+ nServiceType,
+ lpServiceName,
+ FALSE,
+ lpSockAddr,
+ 0);
+ CloseHandle(hServer);
+ }
+ return(err);
+}
+
+DWORD
+NwrpGetAddressByNameInner(
+ IN HANDLE hServer,
+ IN WORD nServiceType,
+ IN LPWSTR lpServiceName,
+ IN BOOL fAnsi,
+ IN OUT LPSOCKADDR_IPX lpSockAddr,
+ OUT PDWORD pdwVersion
+ )
+/*++
+
+Routine Description:
+
+ This routine returns address information about a specific service.
+
+Arguments:
+
+ Reserved - unused
+
+ nServiceType - netware service type
+
+ lpServiceName - unique string representing the service name, in the
+ Netware case, this is the server name
+
+ lpSockAddr - on return, will be filled with SOCKADDR_IPX
+
+ fAnsi -- the input name is in ASCII. This happens only when looking
+ for a DIRSERVER.
+
+Return Value:
+
+ Win32 error.
+
+--*/
+{
+
+ NTSTATUS ntstatus;
+ UNICODE_STRING UServiceName;
+ STRING PropertyName;
+ BYTE PropertyValueBuffer[MAX_PROPERTY_BUFFER_LENGTH];
+ BYTE fMoreSegments;
+ PCHAR pszFormat;
+
+
+
+ //
+ // Send an ncp to find the address of the given service name
+ //
+ RtlInitString( &PropertyName, "NET_ADDRESS" );
+ if(!fAnsi)
+ {
+ RtlInitUnicodeString( &UServiceName, lpServiceName );
+ pszFormat = "bwUbp|rb";
+
+ ntstatus = NwlibMakeNcp(
+ hServer,
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 72, // Max request packet size
+ 132, // Max response packet size
+ pszFormat, // Format string
+ 0x3D, // Read Property Value
+ nServiceType, // Object Type
+ &UServiceName, // Object Name
+ 1, // Segment Number
+ PropertyName.Buffer, // Property Name
+ PropertyValueBuffer, // Ignore
+ MAX_PROPERTY_BUFFER_LENGTH, // size of buffer
+ &fMoreSegments // TRUE if there are more
+ // 128-byte segments
+ );
+
+ if ( NT_SUCCESS( ntstatus))
+ {
+ //
+ // IPX address should fit into the first 128 byte
+ //
+ ASSERT( !fMoreSegments );
+
+ //
+ // Fill in the return buffer
+ //
+ lpSockAddr->sa_family = AF_IPX;
+
+ RtlCopyMemory( lpSockAddr->sa_netnum,
+ PropertyValueBuffer,
+ IPX_ADDRESS_LENGTH );
+
+ if(pdwVersion)
+ {
+ //
+ // the caller wants the version as well. Get it
+ //
+ RtlInitString( &PropertyName, "VERSION" );
+ ntstatus = NwlibMakeNcp(
+ hServer,
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 72, // Max request packet size
+ 132, // Max response packet size
+ pszFormat, // Format string
+ 0x3D, // Read Property Value
+ nServiceType, // Object Type
+ &UServiceName, // Object Name
+ 1, // Segment Number
+ PropertyName.Buffer, // Property Name
+ PropertyValueBuffer, // Ignore
+ MAX_PROPERTY_BUFFER_LENGTH, // size of buffer
+ &fMoreSegments // TRUE if there are more
+ // 128-byte segments
+ );
+ if(NT_SUCCESS(ntstatus))
+ {
+ //
+ // have a version
+ //
+
+ *pdwVersion = *(PDWORD)PropertyValueBuffer;
+ }
+ else
+ {
+ ntstatus = STATUS_SUCCESS;
+ *pdwVersion = 0;
+ }
+ }
+ }
+ }
+ else
+ {
+ //
+ // exact match needed
+ //
+
+ pszFormat = "bwbrbp|rb";
+
+ ntstatus = NwlibMakeNcp(
+ hServer,
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 66, // Max request packet size
+ 132, // Max response packet size
+ pszFormat, // Format string
+ 0x3D, // Read Property Value
+ nServiceType, // Object Type
+ 48,
+ lpServiceName, // Object Name
+ 48,
+ 1, // Segment Number
+ PropertyName.Buffer, // Property Name
+ PropertyValueBuffer, // Ignore
+ MAX_PROPERTY_BUFFER_LENGTH, // size of buffer
+ &fMoreSegments // TRUE if there are more
+ // 128-byte segments
+ );
+
+ if ( NT_SUCCESS( ntstatus))
+ {
+ //
+ // IPX address should fit into the first 128 byte
+ //
+ ASSERT( !fMoreSegments );
+
+ //
+ // Fill in the return buffer
+ //
+ lpSockAddr->sa_family = AF_IPX;
+
+ RtlCopyMemory( lpSockAddr->sa_netnum,
+ PropertyValueBuffer,
+ IPX_ADDRESS_LENGTH );
+
+ if(pdwVersion)
+ {
+ //
+ // the caller wants the version as well. Get it
+ //
+ RtlInitString( &PropertyName, "VERSION" );
+ ntstatus = NwlibMakeNcp(
+ hServer,
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 66, // Max request packet size
+ 132, // Max response packet size
+ pszFormat, // Format string
+ 0x3D, // Read Property Value
+ nServiceType, // Object Type
+ 48,
+ lpServiceName, // Object Name
+ 48,
+ 1, // Segment Number
+ PropertyName.Buffer, // Property Name
+ PropertyValueBuffer, // Ignore
+ MAX_PROPERTY_BUFFER_LENGTH, // size of buffer
+ &fMoreSegments // TRUE if there are more
+ // 128-byte segments
+ );
+ if(NT_SUCCESS(ntstatus))
+ {
+ //
+ // have a version
+ //
+
+ *pdwVersion = *(PDWORD)PropertyValueBuffer;
+ }
+ else
+ {
+ ntstatus = STATUS_SUCCESS;
+ *pdwVersion = 0;
+ }
+ }
+ }
+
+ }
+ return NwMapBinderyCompletionCode(ntstatus);
+}
+
+#if 0
+DWORD
+NwpSetClassInfo(
+ IN LPWSTR lpszServiceClassName,
+ IN LPGUID lpServiceClassType,
+ IN PCHAR lpbProperty
+ )
+{
+ WCHAR wszServerName[48];
+ LONG lIndex = -1;
+ BOOL fFoundOne = FALSE;
+ HANDLE hServer = 0;
+
+ while(NwpEnumClassInfoServers( &hServer, &lIndex, wszServerName, FALSE))
+ {
+ DWORD Status = NwppSetClassInfo(
+ lpszServiceClassName,
+ lpServiceClassType,
+ lpbProperty,
+ wszServerName);
+
+ if(Status == NO_ERROR)
+ {
+ fFoundOne = TRUE;
+ }
+ }
+ if(fFoundOne)
+ {
+ return(NO_ERROR);
+ }
+ return(NO_DATA);
+}
+
+DWORD
+NwppSetClassInfo(
+ IN LPWSTR pwszClassInfoName,
+ IN LPGUID lpClassType,
+ IN PCHAR pbProperty,
+ IN LPWSTR pwszServerName
+ )
+{
+/*++
+Routine Description:
+ Inner routine for SetClassInfo. This is called for each class info
+ server and attempts to create and populate the object
+--*/
+ HANDLE hServer = 0;
+ DWORD err;
+ UNICODE_STRING UString;
+ WCHAR wszProp[48];
+ DWORD dwLen = wcslen(pwszClassInfoName);
+ PWCHAR pszProp;
+ NTSTATUS Status;
+
+ UuidToString(lpClassType, &pszProp);
+
+ memset(wszProp, 0, sizeof(wszProp));
+
+ dwLen = min(sizeof(wszProp) - sizeof(WCHAR), dwLen);
+
+ RtlMoveMemory(wszProp, pwszClassInfoName, dwLen);
+
+ RtlInitUnicodeString(&UString, pszProp);
+
+ err = NwpGetHandleForServer(pwszServerName, &hServer, TRUE);
+ if(err == NO_ERROR)
+ {
+
+ Status = NwlibMakeNcp(
+ hServer,
+ FSCTL_NWR_NCP_E3H,
+ 56,
+ 2,
+ "bbbwU|",
+ 0x32, // create
+ 0, // static
+ 0x20, // security
+ RNRCLASSSAPTYPE, // type
+ &UString);
+
+ if(!NT_SUCCESS(Status)
+ &&
+ ((Status & 0xff) != 0xEE))
+ {
+ err = NO_DATA; // can't do it here
+ }
+ else
+ {
+
+ //
+ // create and write each property
+ //
+
+
+ err = NwpCreateAndWriteProperty(
+ hServer,
+ RNRTYPE, // property name
+ &UString, // object name
+ RNRCLASSSAPTYPE, // object type
+ (PCHAR)pwszClassInfoName);
+
+ err = NwpCreateAndWriteProperty(
+ hServer,
+ RNRCLASSES,
+ &UString,
+ RNRCLASSSAPTYPE, // object type
+ pbProperty); // and this one too
+ }
+ }
+ if(hServer)
+ {
+ CloseHandle(hServer);
+ }
+
+ RpcStringFree(&pszProp);
+
+ return(err);
+}
+
+DWORD
+NwpGetClassInfo(
+ IN LPWSTR lpszServiceClassName,
+ IN LPGUID lpServiceClassType,
+ OUT PLONG plSpare,
+ OUT PDWORD pdwClassInfos,
+ OUT LPGUID lpFoundType,
+ OUT PWCHAR *ppwszFoundName,
+ IN LONG lSpace,
+ OUT PBYTE pbBuffer
+ )
+{
+/*++
+Routine Description:
+ Wrapper for the routine below. This comes up with the server name
+ and decides whether to enumerate servers
+
+--*/
+
+ HANDLE hServer = 0;
+ DWORD err;
+ NTSTATUS ntstatus;
+ LONG lIndex = -1;
+ HANDLE hServ = 0;
+ WCHAR wszObjectName[48];
+
+ while(NwpEnumClassInfoServers(&hServer, &lIndex, wszObjectName, FALSE))
+ {
+ WORD ObjectType;
+ PWCHAR pwszName;
+
+
+ err = NwppGetClassInfo(
+ wszObjectName,
+ lpszServiceClassName,
+ lpServiceClassType,
+ plSpare,
+ pdwClassInfos,
+ lpFoundType,
+ ppwszFoundName,
+ lSpace,
+ pbBuffer);
+ if((err == NO_ERROR)
+ ||
+ (err == WSAEFAULT))
+ {
+ CloseHandle(hServer);
+ break;
+ }
+ }
+ return(err);
+}
+
+BOOL
+NwpEnumClassInfoServers(
+ IN OUT PHANDLE phServer,
+ IN OUT PLONG plIndex,
+ OUT PWCHAR pwszServerName,
+ IN BOOL fVerify)
+{
+/*++
+Routine Description:
+ Common routine to enumerate Class Info servers. Nothing fancy just
+ a way to issue the NCP
+--*/
+ WORD ObjectType;
+ PWCHAR pwszName;
+ NTSTATUS Status;
+ CHAR szObjectName[48];
+ BOOL fRet;
+ DWORD err;
+
+ err = NwpGetHandleForServer(0, phServer, fVerify);
+ if(err == NO_ERROR)
+ {
+ Status = NwlibMakeNcp(
+ *phServer,
+ FSCTL_NWR_NCP_E3H,
+ 58,
+ 59,
+ "bdwp|dwc",
+ 0x37,
+ *plIndex,
+ CLASSINFOSAPID,
+ "*",
+ plIndex,
+ &ObjectType,
+ &szObjectName);
+ if(!NT_SUCCESS(Status))
+ {
+ err = NwMapBinderyCompletionCode(Status);
+ }
+ else if(!NwConvertToUnicode(&pwszName, szObjectName))
+ {
+ err = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ else
+ {
+ wcscpy(pwszServerName, pwszName);
+ LocalFree(pwszName);
+ }
+ }
+ if(err != NO_ERROR)
+ {
+ fRet = FALSE;
+ if(*phServer)
+ {
+ CloseHandle(*phServer);
+ *phServer = 0;
+ }
+ }
+ else
+ {
+ fRet = TRUE;
+ }
+ return(fRet);
+}
+
+DWORD
+NwppGetClassInfo(
+ IN PWCHAR pwszServerName,
+ IN LPWSTR lpszServiceClassName,
+ IN LPGUID lpServiceClassType,
+ OUT PLONG plSpare,
+ OUT PDWORD pdwClassInfos,
+ OUT LPGUID lpFoundType,
+ OUT PWCHAR *ppwszFoundName,
+ IN LONG lSpace,
+ OUT PBYTE pbBuffer
+ )
+{
+/*++
+Routine Description
+ Find and return the class info information for the given Class.
+ The general methodology is to look up the object
+ in the registry, suck out the RnR property, pack what is read into
+ Class Info structures, and voila!
+Arguments:
+ lpServiceClassName the class name
+ lpServiceClassType the class type
+ plSpare Space needed if no class infos returned
+ pdwClassInfos Number of class infos returned
+ lSpace the space available on input
+ pbBuffer the scratch are for building this
+
+
+This was originally an RPC method and the general structure has been preserved
+in case we want to revert to using RPC once again.
+--*/
+
+ DWORD err = NO_ERROR;
+ BYTE PropertyValueBuffer[MAX_PROPERTY_BUFFER_LENGTH]; // max segment size
+ STRING PropertyName;
+ UNICODE_STRING UString;
+ OEM_STRING OString;
+ LPWSANSCLASSINFOW pci = (LPWSANSCLASSINFO)pbBuffer;
+ LONG lFreeSpace = lSpace;
+ PBYTE pbFreeSpace = (PBYTE)((LONG)pbBuffer + lFreeSpace);
+ BYTE fMoreSegments;
+ HANDLE hServer = 0;
+ NTSTATUS ntstatus;
+ PWCHAR pwszName;
+
+ UuidToString(lpServiceClassType, &pwszName);
+
+ *pdwClassInfos = 0;
+ *plSpare = 0; // no space needed yet.
+ err = NwpGetHandleForServer(pwszServerName, &hServer, FALSE);
+
+ if(err == NO_ERROR)
+ {
+ DWORD Segment;
+ PBINDERYCLASSES pbc = (PBINDERYCLASSES)PropertyValueBuffer;
+ DWORD dwTotalSize;
+ DWORD dwSS;
+
+ //
+ // init the Class Info stuff
+ //
+
+ //
+ // pwszName is the name of the object we want to use. We must
+ // fetch all of the Class Info stuff to return.
+ //
+ //
+
+ RtlInitUnicodeString(&UString, pwszName);
+
+ RtlMoveMemory(lpFoundType,
+ lpServiceClassType,
+ sizeof(GUID));
+
+ RtlInitString(&PropertyName, RNRCLASSES); // where the data is
+ for(Segment = 1;; Segment++)
+ {
+ ntstatus = NwlibMakeNcp(
+ hServer,
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 72, // Max request packet size
+ 132, // Max response packet size
+ "bwUbp|rb", // Format string
+ 0x3D, // Read Property Value
+ RNRCLASSSAPTYPE, // Object Type
+ &UString, // Object Name
+ (BYTE)Segment,
+ PropertyName.Buffer, // Property Name
+ PropertyValueBuffer, // Ignore
+ MAX_PROPERTY_BUFFER_LENGTH, // size of buffer
+ &fMoreSegments // TRUE if there are more
+ // 128-byte segments
+ );
+ if(!NT_SUCCESS(ntstatus))
+ {
+ break;
+ }
+ //
+ // got another value. Stuff it in if it fits. In all
+ // cases, compute the space needed.
+ //
+
+
+ if((pbc->bType != BT_WORD)
+ &&
+ (pbc->bType != BT_DWORD))
+ {
+ //
+ // Don't know what to do with these ...
+ //
+
+ err = WSAEPFNOSUPPORT;
+ break;
+ }
+
+ dwSS = (DWORD)pbc->bSizeOfString;
+ dwTotalSize = (DWORD)pbc->bSizeOfType +
+ ((dwSS + 1) * sizeof(WCHAR)) +
+ sizeof(DWORD) - 1;
+
+ dwTotalSize &= ~(sizeof(DWORD) - 1);
+ *plSpare += (LONG)dwTotalSize + sizeof(WSANSCLASSINFO); // running total
+
+ lFreeSpace -= (LONG)dwTotalSize + sizeof(WSANSCLASSINFO);
+ if(lFreeSpace >= 0)
+ {
+ PBYTE pbString;
+ PCHAR pbData = (PCHAR)((PCHAR)pbc +
+ (DWORD)pbc->bOffset);
+ BYTE bRnRName[128];
+ PWCHAR pwszRnR;
+
+ //
+ // it fits. Pack it in
+ //
+
+ pbFreeSpace = (PBYTE)((DWORD)pbFreeSpace - dwTotalSize);
+ *pdwClassInfos += 1; // one more class info.
+ pci->dwNameSpace = (DWORD)ntohs(pbc->wNameSpace);
+ pci->dwValueType = REG_DWORD;
+ pci->dwValueSize = (DWORD)pbc->bSizeOfType;
+ pci->lpValue = (PVOID)(pbFreeSpace - pbBuffer);
+ pci->lpszName = (PWCHAR)((PBYTE)pci->lpValue +
+ pci->dwValueSize);
+ pci->dwConnectionFlags = (DWORD)pbc->bFlags;
+ pci++;
+
+ //
+ // now copy the values.
+ //
+
+
+ if(pbc->bType == BT_WORD)
+ {
+ *(PWORD)pbFreeSpace = ntohs(*(PWORD)pbData);
+ pbString = (PBYTE)((DWORD)pbFreeSpace + sizeof(WORD));
+ pbData = pbData + sizeof(WORD);
+ }
+ else
+ {
+ *(PDWORD)pbFreeSpace = ntohl(*(PDWORD)pbData);
+ pbString = (PBYTE)((DWORD)pbFreeSpace + sizeof(DWORD));
+ pbData = pbData + sizeof(DWORD);
+ }
+
+ //
+ // the name is in ASCII, and not null terminated.
+ //
+
+ RtlMoveMemory(bRnRName, pbData, dwSS);
+ bRnRName[dwSS] = 0;
+ if(!NwConvertToUnicode(&pwszRnR, bRnRName))
+ {
+ //
+ // bad news. Out of space.
+ //
+
+ err = GetLastError();
+ break;
+ }
+
+ RtlMoveMemory(pbString,
+ pwszRnR,
+ (dwSS + 1) * sizeof(WCHAR));
+ LocalFree(pwszRnR);
+
+ }
+ }
+ if(err == NO_ERROR)
+ {
+ if(!*ppwszFoundName)
+ {
+ LONG lLen;
+
+ //
+ // need to return the name
+ //
+
+ err = NwpFetchClassType(hServer,
+ &UString,
+ PropertyValueBuffer);
+
+ if(err == NO_ERROR)
+ {
+ lLen = (wcslen((PWCHAR)PropertyValueBuffer) + 1) *
+ sizeof(WCHAR);
+
+ lFreeSpace -= lLen;
+ *plSpare += lLen;
+
+ if(lFreeSpace >= 0)
+ {
+ //
+ // it fits. Move it
+
+ pbFreeSpace = (PBYTE)((DWORD)pbFreeSpace - lLen);
+ RtlMoveMemory(pbFreeSpace, PropertyValueBuffer, lLen);
+ *ppwszFoundName = (PWCHAR)(pbFreeSpace - pbBuffer);
+ }
+ if(lFreeSpace < 0)
+ {
+ err = WSAEFAULT;
+ }
+ }
+ }
+ }
+ else if(*pdwClassInfos == 0)
+ {
+ err = NO_DATA;
+ }
+ }
+
+ CloseHandle(hServer);
+ RpcStringFree(&pwszName);
+ return(err);
+}
+
+DWORD
+NwpFetchClassType(
+ HANDLE hServer,
+ PUNICODE_STRING pUString,
+ PBYTE pbBuffer)
+{
+/*++
+Routine Description
+ Common routine to read the class type buffer.
+--*/
+ BYTE fMoreSegments;
+ STRING PropertyName;
+ NTSTATUS ntstatus;
+
+ RtlInitString(&PropertyName, RNRTYPE); // where the GUID is
+
+ ntstatus = NwlibMakeNcp(
+ hServer,
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 72, // Max request packet size
+ 132, // Max response packet size
+ "bwUbp|rb", // Format string
+ 0x3D, // Read Property Value
+ RNRCLASSSAPTYPE, // Object Type
+ pUString, // Object Name
+ 1, // Segment Number
+ PropertyName.Buffer, // Property Name
+ pbBuffer,
+ MAX_PROPERTY_BUFFER_LENGTH, // size of buffer
+ &fMoreSegments // TRUE if there are more
+ // 128-byte segments
+ );
+
+ if(!NT_SUCCESS(ntstatus))
+ {
+ return(WSASERVICE_NOT_FOUND);
+ }
+ return(NO_ERROR);
+}
+
+#endif
+DWORD
+NwpCreateAndWriteProperty(
+ IN HANDLE hServer,
+ IN LPSTR lpszPropertyName,
+ IN PUNICODE_STRING pusObjectName,
+ IN WORD wObjectType,
+ IN PCHAR pbPropertyBuffer
+ )
+{
+/*++
+Routine Description:
+ Create the named property and write the data.
+Arguments:
+
+ hServer: handle to the server
+ lpszPropertyName Name of the property
+ pusObjectName Name of the object
+ wObjectType Type of the object
+ pbPropertyBuffer The property data. Must be 128 bytes
+
+Note that the return is always NO_ERROR for now. This may change in the future.
+--*/
+ NTSTATUS Status;
+
+ Status = NwlibMakeNcp(
+ hServer,
+ FSCTL_NWR_NCP_E3H,
+ 73,
+ 2,
+ "bwUbbp|",
+ 0x39, // create property
+ wObjectType,
+ pusObjectName,
+ 0, // static/item
+ 0x20, // security
+ lpszPropertyName
+ );
+
+ //
+ // Now write the porperty data
+ //
+ Status = NwlibMakeNcp(
+ hServer,
+ FSCTL_NWR_NCP_E3H,
+ 201,
+ 2,
+ "bwUbbpr|",
+ 0x3E, // write property
+ wObjectType,
+ pusObjectName,
+ 1, // one segment
+ 0,
+ lpszPropertyName,
+ pbPropertyBuffer, 128);
+
+ return(NO_ERROR);
+}
+
+
diff --git a/private/nw/svcdlls/nwwks/client/ccache.c b/private/nw/svcdlls/nwwks/client/ccache.c
new file mode 100644
index 000000000..9374352fc
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/ccache.c
@@ -0,0 +1,324 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ ccache.c
+
+Abstract:
+
+ This module contains the code to keep a cache of the user
+ credentials. The cache is used mainly for a user browsing from
+ winfile.
+
+Author:
+
+ Chuck Y Chan (chuckc) 4-Dec-93
+
+Revision History:
+
+ chuckc Created
+
+--*/
+
+#include <nwclient.h>
+#include <nwcanon.h>
+#include <nwapi.h>
+
+//-------------------------------------------------------------------//
+// //
+// Local Function Prototypes //
+// //
+//-------------------------------------------------------------------//
+
+DWORD
+ExtractServerName(
+ IN LPWSTR RemoteName,
+ OUT LPWSTR ServerName,
+ IN DWORD ServerNameSize
+) ;
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+static WCHAR CachedPassword[NW_MAX_PASSWORD_LEN+1] ;
+static WCHAR CachedUserName[NW_MAX_USERNAME_LEN+1] ;
+static WCHAR CachedServerName[NW_MAX_SERVER_LEN+1] ;
+static DWORD CachedCredentialsTime ;
+static UNICODE_STRING CachedPasswordUnicodeStr ;
+static UCHAR EncodeSeed = 0 ;
+
+//-------------------------------------------------------------------//
+// //
+// Function Bodies //
+// //
+//-------------------------------------------------------------------//
+
+DWORD
+NwpCacheCredentials(
+ IN LPWSTR RemoteName,
+ IN LPWSTR UserName,
+ IN LPWSTR Password
+ )
+/*++
+
+Routine Description:
+
+ This function caches the user credentials for the particular
+ server.
+
+Arguments:
+
+ RemoteName - path containg the server we are accessing. only the
+ server component is of interest.
+
+ UserName - user name to remember
+
+ Password - password to remember
+
+Return Value:
+
+ NO_ERROR - Successfully cached the credentials
+
+ Win32 error code otherwise.
+
+--*/
+{
+ DWORD status ;
+
+ //
+ // various paramter checks
+ //
+ if (!RemoteName || !UserName || !Password)
+ {
+ status = ERROR_INVALID_PARAMETER ;
+ goto ExitPoint ;
+ }
+
+ if (wcslen(UserName) >= sizeof(CachedUserName)/sizeof(CachedUserName[0]))
+ {
+ status = ERROR_INVALID_PARAMETER ;
+ goto ExitPoint ;
+ }
+
+ if (wcslen(Password) >= sizeof(CachedPassword)/sizeof(CachedPassword[0]))
+ {
+ status = ERROR_INVALID_PARAMETER ;
+ goto ExitPoint ;
+ }
+
+ //
+ // extract the server portion of the path
+ //
+ status = ExtractServerName(
+ RemoteName,
+ CachedServerName,
+ sizeof(CachedServerName)/sizeof(CachedServerName[0])) ;
+
+ if (status != NO_ERROR)
+ {
+ goto ExitPoint ;
+ }
+
+ //
+ // save away the credentials
+ //
+ wcscpy(CachedUserName, UserName) ;
+ wcscpy(CachedPassword, Password) ;
+
+ //
+ // encode it since it is in page pool
+ //
+ RtlInitUnicodeString(&CachedPasswordUnicodeStr, CachedPassword) ;
+ RtlRunEncodeUnicodeString(&EncodeSeed, &CachedPasswordUnicodeStr) ;
+
+ //
+ // mark the time this happened
+ //
+ CachedCredentialsTime = GetTickCount() ;
+
+ return NO_ERROR ;
+
+ExitPoint:
+
+ CachedServerName[0] = 0 ;
+ return status ;
+}
+
+
+BOOL
+NwpRetrieveCachedCredentials(
+ IN LPWSTR RemoteName,
+ OUT LPWSTR *UserName,
+ OUT LPWSTR *Password
+ )
+/*++
+
+Routine Description:
+
+ This function retrieves the cached user credentials for the particular
+ server.
+
+Arguments:
+
+ RemoteName - path containg the server we are accessing. only the
+ server component is of interest.
+
+ UserName - used to return user name
+
+ Password - used to return password
+
+Return Value:
+
+ NO_ERROR - Successfully returned at least one entry.
+
+ Win32 error code otherwise.
+
+--*/
+{
+ DWORD status ;
+ DWORD CurrentTime ;
+ WCHAR ServerName[NW_MAX_SERVER_LEN+1] ;
+
+ *UserName = NULL ;
+ *Password = NULL ;
+ CurrentTime = GetTickCount() ;
+
+ if (!RemoteName)
+ {
+ return FALSE ;
+ }
+
+ //
+ // if too old, bag out
+ //
+ if (((CurrentTime > CachedCredentialsTime) &&
+ (CurrentTime - CachedCredentialsTime) > 60000) ||
+ ((CurrentTime < CachedCredentialsTime) &&
+ (CurrentTime + (MAXULONG - CachedCredentialsTime)) >= 60000))
+ {
+ CachedServerName[0] = 0 ; // reset to nothing
+ return FALSE ;
+ }
+
+ status = ExtractServerName(
+ RemoteName,
+ ServerName,
+ sizeof(ServerName)/sizeof(ServerName[0])) ;
+
+ if (status != NO_ERROR)
+ {
+ return FALSE ;
+ }
+
+ //
+ // if dont compare, bag out
+ //
+ if (_wcsicmp(ServerName, CachedServerName) != 0)
+ {
+ return FALSE ;
+ }
+
+ //
+ // allocate memory to return data
+ //
+ if (!(*UserName = (LPWSTR) LocalAlloc(
+ LPTR,
+ (wcslen(CachedUserName)+1) * sizeof(WCHAR))))
+ {
+ return FALSE ;
+ }
+
+ if (!(*Password = (LPWSTR) LocalAlloc(
+ LPTR,
+ (wcslen(CachedPassword)+1) * sizeof(WCHAR))))
+ {
+ LocalFree((HLOCAL)*UserName) ;
+ *UserName = NULL ;
+ return FALSE ;
+ }
+
+ //
+ // decode the string,copy it and then reencode it
+ //
+ RtlRunDecodeUnicodeString(EncodeSeed, &CachedPasswordUnicodeStr) ;
+ wcscpy(*Password, CachedPassword) ;
+ RtlRunEncodeUnicodeString(&EncodeSeed, &CachedPasswordUnicodeStr) ;
+
+ wcscpy(*UserName, CachedUserName) ;
+
+ //
+ // update the tick count
+ //
+ CachedCredentialsTime = GetTickCount() ;
+ return TRUE ;
+}
+
+
+DWORD
+ExtractServerName(
+ IN LPWSTR RemoteName,
+ OUT LPWSTR ServerName,
+ IN DWORD ServerNameSize
+)
+/*++
+
+Routine Description:
+
+ This function extracts the server name out of a remote name
+
+Arguments:
+
+ RemoteName - the input string to extract the server name from.
+
+ ServerName - the return buffer for the server string
+
+ ServerNameSize - size o f buffer in chars
+
+Return Value:
+
+ NO_ERROR - Successfully cached the credentials
+
+ Win32 error code otherwise.
+
+--*/
+{
+ LPWSTR ServerStart ;
+ LPWSTR ServerEnd ;
+
+ //
+ // skip initial backslashes, then find next one delimiting the server name
+ //
+ ServerStart = RemoteName ;
+
+ while (*ServerStart == L'\\')
+ ServerStart++ ;
+
+ ServerEnd = wcschr(ServerStart, L'\\') ;
+ if (ServerEnd)
+ *ServerEnd = 0 ;
+
+ //
+ // make sure we can fit
+ //
+ if (wcslen(ServerStart) >= ServerNameSize)
+ {
+ if (ServerEnd)
+ *ServerEnd = L'\\' ;
+ return ERROR_INVALID_PARAMETER ;
+ }
+
+ //
+ // copy and restore the backslash
+ //
+ wcscpy(ServerName, ServerStart) ;
+
+ if (ServerEnd)
+ *ServerEnd = L'\\' ;
+
+ return NO_ERROR ;
+}
diff --git a/private/nw/svcdlls/nwwks/client/drawpie.c b/private/nw/svcdlls/nwwks/client/drawpie.c
new file mode 100644
index 000000000..2bb4f8e92
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/drawpie.c
@@ -0,0 +1,240 @@
+#include <windows.h>
+#include "drawpie.h"
+
+#ifdef WIN32
+#define MoveTo(_hdc,_x,_y) MoveToEx(_hdc, _x, _y, NULL)
+#endif // WIN32
+
+
+int NEAR IntSqrt(unsigned long dwNum)
+{
+ // We will keep shifting dwNum left and look at the top two bits.
+
+ // initialize sqrt and remainder to 0.
+ DWORD dwSqrt = 0, dwRemain = 0, dwTry;
+ int i;
+
+ // We iterate 16 times, once for each pair of bits.
+ for (i=0; i<16; ++i)
+ {
+ // Mask off the top two bits of dwNum and rotate them into the
+ // bottom of the remainder
+ dwRemain = (dwRemain<<2) | (dwNum>>30);
+
+ // Now we shift the sqrt left; next we'll determine whether the
+ // new bit is a 1 or a 0.
+ dwSqrt <<= 1;
+
+ // This is where we double what we already have, and try a 1 in
+ // the lowest bit.
+ dwTry = dwSqrt*2 + 1;
+
+ if (dwRemain >= dwTry)
+ {
+ // The remainder was big enough, so subtract dwTry from
+ // the remainder and tack a 1 onto the sqrt.
+ dwRemain -= dwTry;
+ dwSqrt |= 0x01;
+ }
+
+ // Shift dwNum to the left by 2 so we can work on the next few
+ // bits.
+ dwNum <<= 2;
+ }
+
+ return(dwSqrt);
+}
+
+
+
+VOID NEAR DrawPie(HDC hDC, LPCRECT lprcItem, UINT uPctX10, BOOL TrueZr100,
+ UINT uOffset, const COLORREF FAR *lpColors)
+{
+ int cx, cy, rx, ry, x, y;
+ int uQPctX10;
+ RECT rcItem;
+ HRGN hEllRect, hEllipticRgn, hRectRgn;
+ HBRUSH hBrush, hOldBrush;
+ HPEN hPen, hOldPen;
+
+ rcItem = *lprcItem;
+ rcItem.left = lprcItem->left;
+ rcItem.top = lprcItem->top;
+ rcItem.right = lprcItem->right - rcItem.left;
+ rcItem.bottom = lprcItem->bottom - rcItem.top - uOffset;
+
+ rx = rcItem.right / 2;
+ cx = rcItem.left + rx;
+ ry = rcItem.bottom / 2;
+ cy = rcItem.top + ry;
+ if (rx<=10 || ry<=10)
+ {
+ return;
+ }
+
+ rcItem.right = rcItem.left+2*rx;
+ rcItem.bottom = rcItem.top+2*ry;
+
+ if (uPctX10 > 1000)
+ {
+ uPctX10 = 1000;
+ }
+
+ /* Translate to first quadrant of a Cartesian system
+ */
+ uQPctX10 = (uPctX10 % 500) - 250;
+ if (uQPctX10 < 0)
+ {
+ uQPctX10 = -uQPctX10;
+ }
+
+ /* Calc x and y. I am trying to make the area be the right percentage.
+ ** I don't know how to calculate the area of a pie slice exactly, so I
+ ** approximate it by using the triangle area instead.
+ */
+ if (uQPctX10 < 120)
+ {
+ x = IntSqrt(((DWORD)rx*(DWORD)rx*(DWORD)uQPctX10*(DWORD)uQPctX10)
+ /((DWORD)uQPctX10*(DWORD)uQPctX10+(250L-(DWORD)uQPctX10)*(250L-(DWORD)uQPctX10)));
+
+ y = IntSqrt(((DWORD)rx*(DWORD)rx-(DWORD)x*(DWORD)x)*(DWORD)ry*(DWORD)ry/((DWORD)rx*(DWORD)rx));
+ }
+ else
+ {
+ y = IntSqrt((DWORD)ry*(DWORD)ry*(250L-(DWORD)uQPctX10)*(250L-(DWORD)uQPctX10)
+ /((DWORD)uQPctX10*(DWORD)uQPctX10+(250L-(DWORD)uQPctX10)*(250L-(DWORD)uQPctX10)));
+
+ x = IntSqrt(((DWORD)ry*(DWORD)ry-(DWORD)y*(DWORD)y)*(DWORD)rx*(DWORD)rx/((DWORD)ry*(DWORD)ry));
+ }
+
+ /* Switch on the actual quadrant
+ */
+ switch (uPctX10 / 250)
+ {
+ case 1:
+ y = -y;
+ break;
+
+ case 2:
+ break;
+
+ case 3:
+ x = -x;
+ break;
+
+ default: // case 0 and case 4
+ x = -x;
+ y = -y;
+ break;
+ }
+
+ /* Now adjust for the center.
+ */
+ x += cx;
+ y += cy;
+
+ /* Draw the shadows using regions (to reduce flicker).
+ */
+ hEllipticRgn = CreateEllipticRgnIndirect(&rcItem);
+ OffsetRgn(hEllipticRgn, 0, uOffset);
+ hEllRect = CreateRectRgn(rcItem.left, cy, rcItem.right, cy+uOffset);
+ hRectRgn = CreateRectRgn(0, 0, 0, 0);
+ CombineRgn(hRectRgn, hEllipticRgn, hEllRect, RGN_OR);
+ OffsetRgn(hEllipticRgn, 0, -(int)uOffset);
+ CombineRgn(hEllRect, hRectRgn, hEllipticRgn, RGN_DIFF);
+
+ /* Always draw the whole area in the free shadow/
+ */
+ hBrush = CreateSolidBrush(lpColors[DP_FREESHADOW]);
+ if (hBrush)
+ {
+ FillRgn(hDC, hEllRect, hBrush);
+ DeleteObject(hBrush);
+ }
+
+ /* Draw the used shadow only if the disk is at least half used.
+ */
+ if (uPctX10>500 && (hBrush=CreateSolidBrush(lpColors[DP_USEDSHADOW]))!=NULL)
+ {
+ DeleteObject(hRectRgn);
+ hRectRgn = CreateRectRgn(x, cy, rcItem.right, lprcItem->bottom);
+ CombineRgn(hEllipticRgn, hEllRect, hRectRgn, RGN_AND);
+ FillRgn(hDC, hEllipticRgn, hBrush);
+ DeleteObject(hBrush);
+ }
+
+ DeleteObject(hRectRgn);
+ DeleteObject(hEllipticRgn);
+ DeleteObject(hEllRect);
+
+ hPen = CreatePen(PS_SOLID, 1, GetSysColor(COLOR_WINDOWFRAME));
+ hOldPen = SelectObject(hDC, hPen);
+
+ if((uPctX10 < 100) && (cy == y))
+ {
+ hBrush = CreateSolidBrush(lpColors[DP_FREECOLOR]);
+ hOldBrush = SelectObject(hDC, hBrush);
+ if((TrueZr100 == FALSE) || (uPctX10 != 0))
+ {
+ Pie(hDC, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
+ rcItem.left, cy, x, y);
+ }
+ else
+ {
+ Ellipse(hDC, rcItem.left, rcItem.top, rcItem.right,
+ rcItem.bottom);
+ }
+ }
+ else if((uPctX10 > (1000 - 100)) && (cy == y))
+ {
+ hBrush = CreateSolidBrush(lpColors[DP_USEDCOLOR]);
+ hOldBrush = SelectObject(hDC, hBrush);
+ if((TrueZr100 == FALSE) || (uPctX10 != 1000))
+ {
+ Pie(hDC, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
+ rcItem.left, cy, x, y);
+ }
+ else
+ {
+ Ellipse(hDC, rcItem.left, rcItem.top, rcItem.right,
+ rcItem.bottom);
+ }
+ }
+ else
+ {
+ hBrush = CreateSolidBrush(lpColors[DP_USEDCOLOR]);
+ hOldBrush = SelectObject(hDC, hBrush);
+
+ Ellipse(hDC, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom);
+ SelectObject(hDC, hOldBrush);
+ DeleteObject(hBrush);
+
+ hBrush = CreateSolidBrush(lpColors[DP_FREECOLOR]);
+ hOldBrush = SelectObject(hDC, hBrush);
+ Pie(hDC, rcItem.left, rcItem.top, rcItem.right, rcItem.bottom,
+ rcItem.left, cy, x, y);
+ }
+ SelectObject(hDC, hOldBrush);
+ DeleteObject(hBrush);
+
+ /* Do not draw the lines if the %age is truely 0 or 100 (completely
+ ** empty disk or completly full disk)
+ */
+ if((TrueZr100 == FALSE) || ((uPctX10 != 0) && (uPctX10 != 1000)))
+ {
+ Arc(hDC, rcItem.left, rcItem.top+uOffset, rcItem.right, rcItem.bottom+uOffset,
+ rcItem.left, cy+uOffset, rcItem.right, cy+uOffset-1);
+ MoveTo(hDC, rcItem.left, cy);
+ LineTo(hDC, rcItem.left, cy+uOffset);
+ MoveTo(hDC, rcItem.right-1, cy);
+ LineTo(hDC, rcItem.right-1, cy+uOffset);
+
+ if (uPctX10 > 500)
+ {
+ MoveTo(hDC, x, y);
+ LineTo(hDC, x, y+uOffset);
+ }
+ }
+ SelectObject(hDC, hOldPen);
+ DeleteObject(hPen);
+}
diff --git a/private/nw/svcdlls/nwwks/client/drawpie.h b/private/nw/svcdlls/nwwks/client/drawpie.h
new file mode 100644
index 000000000..24edb4abc
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/drawpie.h
@@ -0,0 +1,9 @@
+int NEAR IntSqrt(unsigned long dwNum);
+
+#define DP_USEDCOLOR 0
+#define DP_FREECOLOR 1
+#define DP_USEDSHADOW 2
+#define DP_FREESHADOW 3
+
+VOID NEAR DrawPie(HDC hDC, LPCRECT prcItem, UINT uPctX10, BOOL TrueZr100,
+ UINT uOffset, const COLORREF FAR *lpColors);
diff --git a/private/nw/svcdlls/nwwks/client/folderop.ico b/private/nw/svcdlls/nwwks/client/folderop.ico
new file mode 100644
index 000000000..ab7b05d9d
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/folderop.ico
Binary files differ
diff --git a/private/nw/svcdlls/nwwks/client/getaddr.c b/private/nw/svcdlls/nwwks/client/getaddr.c
new file mode 100644
index 000000000..2673eddae
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/getaddr.c
@@ -0,0 +1,3619 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ getaddr.c
+
+Abstract:
+
+ This module contains the code to support NPGetAddressByName.
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 18-Apr-94
+ Glenn A. Curtis (glennc) 31-Jul-95
+ Arnold Miller (ArnoldM) 7-Dec-95
+
+Revision History:
+
+ yihsins Created
+ glennc Modified 31-Jul-95
+ ArnoldM Modified 7-Dec-95
+
+--*/
+
+
+#include <nwclient.h>
+#include <winsock.h>
+#include <wsipx.h>
+#include <nspapi.h>
+#include <nspapip.h>
+#include <wsnwlink.h>
+#include <svcguid.h>
+#include <nwsap.h>
+#include <align.h>
+#include <nwmisc.h>
+#include <rnrdefs.h>
+
+//-------------------------------------------------------------------//
+// //
+// Local Function Prototypes //
+// //
+//-------------------------------------------------------------------//
+
+#define NW_SAP_PRIORITY_VALUE_NAME L"SapPriority"
+#define NW_WORKSTATION_SVCPROVIDER_REGKEY L"System\\CurrentControlSet\\Services\\NWCWorkstation\\ServiceProvider"
+
+#define NW_GUID_VALUE_NAME L"GUID"
+#define NW_SERVICETYPES_KEY_NAME L"ServiceTypes"
+#define NW_SERVICE_TYPES_REGKEY L"System\\CurrentControlSet\\Control\\ServiceProvider\\ServiceTypes"
+
+#define DLL_VERSION 1
+#define WSOCK_VER_REQD 0x0101
+
+//
+// critical sections used
+//
+
+extern CRITICAL_SECTION NwServiceListCriticalSection;
+
+ // have been returned
+BOOL
+OldRnRCheckCancel(
+ PVOID pvArg
+ );
+
+DWORD
+OldRnRCheckSapData(
+ PSAP_BCAST_CONTROL psbc,
+ PSAP_IDENT_HEADER pSap,
+ PDWORD pdwErr
+ );
+
+DWORD
+SapGetSapForType(
+ PSAP_BCAST_CONTROL psbc,
+ WORD nServiceType
+ );
+
+DWORD
+SapFreeSapSocket(
+ SOCKET s
+ );
+
+DWORD
+SapGetSapSocket(
+ SOCKET * ppsocket
+ );
+
+VOID
+pFreeAllContexts();
+
+PSAP_RNR_CONTEXT
+SapGetContext(
+ IN HANDLE Handle
+ );
+
+PSAP_RNR_CONTEXT
+SapMakeContext(
+ IN HANDLE Handle,
+ IN DWORD dwExcess
+ );
+
+VOID
+SapReleaseContext(
+ PSAP_RNR_CONTEXT psrcContext
+ );
+
+INT
+SapGetAddressByName(
+ IN LPGUID lpServiceType,
+ IN LPWSTR lpServiceName,
+ IN LPDWORD lpdwProtocols,
+ IN DWORD dwResolution,
+ IN OUT LPVOID lpCsAddrBuffer,
+ IN OUT LPDWORD lpdwBufferLength,
+ IN OUT LPWSTR lpAliasBuffer,
+ IN OUT LPDWORD lpdwAliasBufferLength,
+ IN HANDLE hCancellationEvent
+);
+
+DWORD
+SapGetService (
+ IN LPGUID lpServiceType,
+ IN LPWSTR lpServiceName,
+ IN DWORD dwProperties,
+ IN BOOL fUnicodeBlob,
+ OUT LPSERVICE_INFO lpServiceInfo,
+ IN OUT LPDWORD lpdwBufferLen
+);
+
+DWORD
+SapSetService (
+ IN DWORD dwOperation,
+ IN DWORD dwFlags,
+ IN BOOL fUnicodeBlob,
+ IN LPSERVICE_INFO lpServiceInfo
+);
+
+DWORD
+NwpGetAddressViaSap(
+ IN WORD nServiceType,
+ IN LPWSTR lpServiceName,
+ IN DWORD nProt,
+ IN OUT LPVOID lpCsAddrBuffer,
+ IN OUT LPDWORD lpdwBufferLength,
+ IN HANDLE hCancellationEvent,
+ OUT LPDWORD lpcAddress
+);
+
+BOOL
+NwpLookupSapInRegistry(
+ IN LPGUID lpServiceType,
+ OUT PWORD pnSapType,
+ OUT PWORD pwPort,
+ IN OUT PDWORD pfConnectionOriented
+);
+
+DWORD
+NwpRnR2AddServiceType(
+ IN LPWSTR lpServiceTypeName,
+ IN LPGUID lpClassType,
+ IN WORD wSapId,
+ IN WORD wPort
+);
+
+DWORD
+NwpAddServiceType(
+ IN LPSERVICE_INFO lpServiceInfo,
+ IN BOOL fUnicodeBlob
+);
+
+DWORD
+NwpDeleteServiceType(
+ IN LPSERVICE_INFO lpServiceInfo,
+ IN BOOL fUnicodeBlob
+);
+
+DWORD
+FillBufferWithCsAddr(
+ IN LPBYTE pAddress,
+ IN DWORD nProt,
+ IN OUT LPVOID lpCsAddrBuffer,
+ IN OUT LPDWORD lpdwBufferLength,
+ OUT LPDWORD pcAddress
+);
+
+DWORD
+AddServiceToList(
+ IN LPSERVICE_INFO lpServiceInfo,
+ IN WORD nSapType,
+ IN BOOL fAdvertiseBySap,
+ IN INT nIndexIPXAddress
+);
+
+VOID
+RemoveServiceFromList(
+ IN PREGISTERED_SERVICE pSvc
+);
+
+DWORD
+pSapSetService2(
+ IN DWORD dwOperation,
+ IN LPWSTR lpszServiceInstance,
+ IN PBYTE pbAddress,
+ IN LPGUID pType,
+ IN WORD nServiceType
+ );
+
+DWORD
+pSapSetService(
+ IN DWORD dwOperation,
+ IN LPSERVICE_INFO lpServiceInfo,
+ IN WORD nServiceType
+ );
+
+//
+// Misc Functions
+//
+
+DWORD NwInitializeSocket(
+ IN HANDLE hEventHandle
+);
+
+DWORD
+NwAdvertiseService(
+ IN LPWSTR pServiceName,
+ IN WORD nSapType,
+ IN LPSOCKADDR_IPX pAddr,
+ IN HANDLE hEventHandle
+);
+
+DWORD SapFunc(
+ IN HANDLE hEventHandle
+);
+
+DWORD
+NwpGetAddressByName(
+ IN LPWSTR Reserved,
+ IN WORD nServiceType,
+ IN LPWSTR lpServiceName,
+ IN OUT LPSOCKADDR_IPX lpsockaddr
+);
+
+
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+//
+// This is the address we send to
+//
+
+UCHAR SapBroadcastAddress[] = {
+ AF_IPX, 0, // Address Family
+ 0x00, 0x00, 0x00, 0x00, // Dest. Net Number
+ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Dest. Node Number
+ 0x04, 0x52, // Dest. Socket
+ 0x04 // Packet type
+};
+
+PSAP_RNR_CONTEXT psrcSapContexts;
+
+//
+// Misc. variables used if we need to advertise ourselves, i.e.
+// when the SAP service is not installed/active.
+//
+
+BOOL fInitSocket = FALSE; // TRUE if we have created the second thread
+SOCKET socketSap; // Socket used to send SAP advertise packets
+PREGISTERED_SERVICE pServiceListHead = NULL; // Points to head of link list
+PREGISTERED_SERVICE pServiceListTail = NULL; // Points to tail of link list
+
+//
+// needed to map old and new RnR functions
+//
+DWORD oldRnRServiceRegister = SERVICE_REGISTER;
+DWORD oldRnRServiceDeRegister = SERVICE_DEREGISTER;
+
+
+//-------------------------------------------------------------------//
+// //
+// Function Bodies //
+// //
+//-------------------------------------------------------------------//
+
+VOID
+pFreeAllContexts()
+/*++
+Routine Description:
+ Called at Cleanup time to free all NSP resource
+--*/
+{
+ PSAP_RNR_CONTEXT psrcContext;
+
+ EnterCriticalSection( &NwServiceListCriticalSection );
+ while(psrcContext = psrcSapContexts)
+ {
+ (VOID)SapReleaseContext(psrcContext);
+ }
+ LeaveCriticalSection( &NwServiceListCriticalSection );
+}
+
+PSAP_RNR_CONTEXT
+SapGetContext(HANDLE Handle)
+/*++
+
+Routine Description:
+
+ This routine checks the existing SAP contexts to see if we have one
+ for this calll.
+
+Arguments:
+
+ Handle - the RnR handle, if appropriate
+
+--*/
+{
+ PSAP_RNR_CONTEXT psrcContext;
+
+ EnterCriticalSection( &NwServiceListCriticalSection );
+
+ for(psrcContext = psrcSapContexts;
+ psrcContext && (psrcContext->Handle != Handle);
+ psrcContext = psrcContext->pNextContext);
+
+ if(psrcContext)
+ {
+ ++psrcContext->lInUse;
+ }
+ LeaveCriticalSection( &NwServiceListCriticalSection );
+ return(psrcContext);
+}
+
+PSAP_RNR_CONTEXT
+SapMakeContext(
+ IN HANDLE Handle,
+ IN DWORD dwExcess
+ )
+{
+/*++
+
+Routine Description:
+
+ This routine makes a SAP conext for a given RnR handle
+
+Arguments:
+
+ Handle - the RnR handle. If NULL, use the context as the handle
+ dwType - the type of the context
+
+--*/
+ PSAP_RNR_CONTEXT psrcContext;
+
+ psrcContext = (PSAP_RNR_CONTEXT)
+ LocalAlloc(LPTR, sizeof(SAP_RNR_CONTEXT) +
+ dwExcess);
+ if(psrcContext)
+ {
+ InitializeCriticalSection(&psrcContext->u_type.sbc.csMonitor);
+ psrcContext->lInUse = 2;
+ psrcContext->Handle = (Handle ? Handle : (HANDLE)psrcContext);
+ psrcContext->lSig = RNR_SIG;
+ EnterCriticalSection( &NwServiceListCriticalSection );
+ psrcContext->pNextContext = psrcSapContexts;
+ psrcSapContexts = psrcContext;
+ LeaveCriticalSection( &NwServiceListCriticalSection );
+ }
+ return(psrcContext);
+}
+
+VOID
+SapReleaseContext(PSAP_RNR_CONTEXT psrcContext)
+/*++
+
+Routine Description:
+
+ Dereference an RNR Context and free it if it is no longer referenced.
+ Determining no referneces is a bit tricky because we try to avoid
+ obtaining the CritSec unless we think the context may be unneeded. Hence
+ the code goes through some fuss. It could be much simpler if we always
+ obtained the CritSec whenever we changed the reference count, but
+ this is faster for the nominal case.
+
+Arguments:
+
+ psrcContext -- The context
+
+--*/
+{
+ EnterCriticalSection( &NwServiceListCriticalSection );
+ if(--psrcContext->lInUse == 0)
+ {
+ PSAP_RNR_CONTEXT psrcX, psrcPrev;
+ PSAP_DATA psdData;
+
+ //
+ // Done with it. Remove from the lisgt
+ //
+
+ psrcPrev = 0;
+ for(psrcX = psrcSapContexts;
+ psrcX;
+ psrcX = psrcX->pNextContext)
+ {
+ if(psrcX == psrcContext)
+ {
+ //
+ // Found it.
+ //
+
+ if(psrcPrev)
+ {
+ psrcPrev->pNextContext = psrcContext->pNextContext;
+ }
+ else
+ {
+ psrcSapContexts = psrcContext->pNextContext;
+ }
+ break;
+ }
+ psrcPrev = psrcX;
+ }
+
+ ASSERT(psrcX);
+
+ //
+ // release SAP data, if any
+ //
+ if(psrcContext->dwUnionType == LOOKUP_TYPE_SAP)
+ {
+ for(psdData = psrcContext->u_type.sbc.psdHead;
+ psdData;)
+ {
+ PSAP_DATA psdTemp = psdData->sapNext;
+
+ LocalFree(psdData);
+ psdData = psdTemp;
+ }
+
+ if(psrcContext->u_type.sbc.s)
+ {
+ SapFreeSapSocket(psrcContext->u_type.sbc.s);
+ }
+ }
+ DeleteCriticalSection(&psrcContext->u_type.sbc.csMonitor);
+ if(psrcContext->hServer)
+ {
+ CloseHandle(psrcContext->hServer);
+ }
+ LocalFree(psrcContext);
+ }
+ LeaveCriticalSection( &NwServiceListCriticalSection );
+}
+
+INT
+APIENTRY
+NPLoadNameSpaces(
+ IN OUT LPDWORD lpdwVersion,
+ IN OUT LPNS_ROUTINE nsrBuffer,
+ IN OUT LPDWORD lpdwBufferLength
+ )
+{
+/*++
+
+Routine Description:
+
+ This routine returns name space info and functions supported in this
+ dll.
+
+Arguments:
+
+ lpdwVersion - dll version
+
+ nsrBuffer - on return, this will be filled with an array of
+ NS_ROUTINE structures
+
+ lpdwBufferLength - on input, the number of bytes contained in the buffer
+ pointed to by nsrBuffer. On output, the minimum number of bytes
+ to pass for the nsrBuffer to retrieve all the requested info
+
+Return Value:
+
+ The number of NS_ROUTINE structures returned, or SOCKET_ERROR (-1) if
+ the nsrBuffer is too small. Use GetLastError() to retrieve the
+ error code.
+
+--*/
+ DWORD err;
+ DWORD dwLengthNeeded;
+ HKEY providerKey;
+
+ DWORD dwSapPriority = NS_STANDARD_FAST_PRIORITY;
+
+ *lpdwVersion = DLL_VERSION;
+
+ //
+ // Check to see if the buffer is large enough
+ //
+ dwLengthNeeded = sizeof(NS_ROUTINE) + 4 * sizeof(LPFN_NSPAPI);
+
+ if ( ( *lpdwBufferLength < dwLengthNeeded )
+ || ( nsrBuffer == NULL )
+ )
+ {
+ *lpdwBufferLength = dwLengthNeeded;
+ SetLastError( ERROR_INSUFFICIENT_BUFFER );
+ return (DWORD) SOCKET_ERROR;
+ }
+
+ //
+ // Get the Sap priority from the registry. We will ignore all errors
+ // from the registry and have a default priority if we failed to read
+ // the value.
+ //
+ err = RegOpenKeyExW( HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_SVCPROVIDER_REGKEY,
+ 0,
+ KEY_READ,
+ &providerKey );
+
+ if ( !err )
+ {
+ DWORD BytesNeeded = sizeof( dwSapPriority );
+ DWORD ValueType;
+
+ err = RegQueryValueExW( providerKey,
+ NW_SAP_PRIORITY_VALUE_NAME,
+ NULL,
+ &ValueType,
+ (LPBYTE) &dwSapPriority,
+ &BytesNeeded );
+
+ if ( err ) // set default priority if error occurred
+ dwSapPriority = NS_STANDARD_FAST_PRIORITY;
+ }
+
+ //
+ // We only support 1 name space for now, so fill in the NS_ROUTINE.
+ //
+ nsrBuffer->dwFunctionCount = 3;
+ nsrBuffer->alpfnFunctions = (LPFN_NSPAPI *)
+ ((BYTE *) nsrBuffer + sizeof(NS_ROUTINE));
+ (nsrBuffer->alpfnFunctions)[NSPAPI_GET_ADDRESS_BY_NAME] =
+ (LPFN_NSPAPI) SapGetAddressByName;
+ (nsrBuffer->alpfnFunctions)[NSPAPI_GET_SERVICE] =
+ (LPFN_NSPAPI) SapGetService;
+ (nsrBuffer->alpfnFunctions)[NSPAPI_SET_SERVICE] =
+ (LPFN_NSPAPI) SapSetService;
+ (nsrBuffer->alpfnFunctions)[3] = NULL;
+
+ nsrBuffer->dwNameSpace = NS_SAP;
+ nsrBuffer->dwPriority = dwSapPriority;
+
+ return 1; // number of namespaces
+}
+
+INT
+SapGetAddressByName(
+ IN LPGUID lpServiceType,
+ IN LPWSTR lpServiceName,
+ IN LPDWORD lpdwProtocols,
+ IN DWORD dwResolution,
+ IN OUT LPVOID lpCsAddrBuffer,
+ IN OUT LPDWORD lpdwBufferLength,
+ IN OUT LPWSTR lpAliasBuffer,
+ IN OUT LPDWORD lpdwAliasBufferLength,
+ IN HANDLE hCancellationEvent
+ )
+/*++
+
+Routine Description:
+
+ This routine returns address information about a specific service.
+
+Arguments:
+
+ lpServiceType - pointer to the GUID for the service type
+
+ lpServiceName - unique string representing the service name, in the
+ Netware case, this is the server name
+
+ lpdwProtocols - a zero terminated array of protocol ids. This parameter
+ is optional; if lpdwProtocols is NULL, information on all available
+ Protocols is returned
+
+ dwResolution - can be one of the following values:
+ RES_SOFT_SEARCH, RES_FIND_MULTIPLE
+
+ lpCsAddrBuffer - on return, will be filled with CSADDR_INFO structures
+
+ lpdwBufferLength - on input, the number of bytes contained in the buffer
+ pointed to by lpCsAddrBuffer. On output, the minimum number of bytes
+ to pass for the lpCsAddrBuffer to retrieve all the requested info
+
+ lpAliasBuffer - not used
+
+ lpdwAliasBufferLength - not used
+
+ hCancellationEvent - the event which signals us to cancel the request
+
+Return Value:
+
+ The number of CSADDR_INFO structures returned, or SOCKET_ERROR (-1) if
+ the lpCsAddrBuffer is too small. Use GetLastError() to retrieve the
+ error code.
+
+--*/
+{
+ DWORD err;
+ WORD nServiceType;
+ DWORD cAddress = 0; // Count of the number of address returned
+ // in lpCsAddrBuffer
+ DWORD cProtocols = 0; // Count of the number of protocols contained
+ // in lpdwProtocols + 1 ( for zero terminate )
+ DWORD nProt = IPX_BIT | SPXII_BIT;
+ DWORD fConnectionOriented = (DWORD) -1;
+
+ if ( ARGUMENT_PRESENT( lpdwAliasBufferLength )
+ && ARGUMENT_PRESENT( lpAliasBuffer )
+ )
+ {
+ if ( *lpdwAliasBufferLength >= sizeof(WCHAR) )
+ *lpAliasBuffer = 0;
+ }
+
+ //
+ // Check for invalid parameters
+ //
+ if ( ( lpServiceType == NULL )
+ || ( lpServiceName == NULL )
+ || ( lpdwBufferLength == NULL )
+ )
+ {
+ SetLastError( ERROR_INVALID_PARAMETER );
+ return SOCKET_ERROR;
+ }
+
+ //
+ // If an array of protocol ids is passed in, check to see if
+ // the IPX protocol is requested. If not, return 0 since
+ // we only support IPX.
+ //
+ if ( lpdwProtocols != NULL )
+ {
+ INT i = -1;
+
+ nProt = 0;
+ while ( lpdwProtocols[++i] != 0 )
+ {
+ if ( lpdwProtocols[i] == NSPROTO_IPX )
+ nProt |= IPX_BIT;
+
+ if ( lpdwProtocols[i] == NSPROTO_SPX )
+ nProt |= SPX_BIT;
+
+ if ( lpdwProtocols[i] == NSPROTO_SPXII )
+ nProt |= SPXII_BIT;
+
+ }
+
+ if ( nProt == 0 )
+ return 0; // No address found
+
+ cProtocols = i+1;
+ }
+
+ //
+ // Check to see if the service type is supported in NetWare
+ //
+ if ( NwpLookupSapInRegistry( lpServiceType, &nServiceType, NULL,
+ &fConnectionOriented ))
+ {
+ if ( fConnectionOriented != -1 ) // Got value from registry
+ {
+ if ( fConnectionOriented )
+ {
+ nProt &= ~IPX_BIT;
+ }
+ else // connectionless
+ {
+ nProt &= ~(SPX_BIT | SPXII_BIT );
+ }
+
+ if ( nProt == 0 )
+ return 0; // No address found
+ }
+ }
+ else
+ {
+ //
+ // Couldn't find it in the registry, see if it is a well-known GUID
+ //
+ if ( IS_SVCID_NETWARE( lpServiceType ))
+ {
+ nServiceType = SAPID_FROM_SVCID_NETWARE( lpServiceType );
+ }
+ else
+ {
+ //
+ // Not a well-known GUID either
+ //
+ return 0; // No address found
+ }
+ }
+
+
+ if ((dwResolution & RES_SERVICE) != 0)
+ {
+ err = FillBufferWithCsAddr( NULL,
+ nProt,
+ lpCsAddrBuffer,
+ lpdwBufferLength,
+ &cAddress );
+
+ if ( err )
+ {
+ SetLastError( err );
+ return SOCKET_ERROR;
+ }
+
+ return cAddress;
+ }
+
+ //
+ // Try to get the address from the bindery first
+ //
+ RpcTryExcept
+ {
+ SOCKADDR_IPX sockaddr;
+
+ err = NwpGetAddressByName( NULL,
+ nServiceType,
+ lpServiceName,
+ &sockaddr );
+
+ if ( err == NO_ERROR )
+ {
+ err = FillBufferWithCsAddr( sockaddr.sa_netnum,
+ nProt,
+ lpCsAddrBuffer,
+ lpdwBufferLength,
+ &cAddress );
+ }
+
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+
+ if ( code == RPC_S_SERVER_UNAVAILABLE )
+ err = ERROR_SERVICE_NOT_ACTIVE;
+ else
+ err = NwpMapRpcError( code );
+ }
+ RpcEndExcept
+
+ if ( err
+ && ( err != ERROR_INSUFFICIENT_BUFFER )
+ )
+ {
+ if ( (!(dwResolution & RES_SOFT_SEARCH))
+ || ( err == ERROR_SERVICE_NOT_ACTIVE )
+ )
+ {
+ //
+ // We could not find the service name in the bindery, and we
+ // need to try harder ( RES_SOFT_SEARCH not defined ), so send out
+ // SAP query packets to see if we can find it.
+ //
+
+ err = NwpGetAddressViaSap(
+ nServiceType,
+ lpServiceName,
+ nProt,
+ lpCsAddrBuffer,
+ lpdwBufferLength,
+ hCancellationEvent,
+ &cAddress );
+#if DBG
+ IF_DEBUG(OTHER)
+ {
+ if ( err == NO_ERROR )
+ {
+ KdPrint(("Successfully got %d address for %ws from SAP.\n",
+ cAddress, lpServiceName ));
+ }
+ else
+ {
+ KdPrint(("Failed with err %d when getting address for %ws from SAP.\n", err, lpServiceName ));
+ }
+ }
+#endif
+ }
+ else
+ {
+ err = NO_ERROR;
+ cAddress = 0;
+ }
+ }
+
+ if ( err )
+ {
+ SetLastError( err );
+ return SOCKET_ERROR;
+ }
+
+ return cAddress;
+
+}
+
+DWORD
+SapGetService (
+ IN LPGUID lpServiceType,
+ IN LPWSTR lpServiceName,
+ IN DWORD dwProperties,
+ IN BOOL fUnicodeBlob,
+ OUT LPSERVICE_INFO lpServiceInfo,
+ IN OUT LPDWORD lpdwBufferLen
+ )
+/*++
+
+Routine Description:
+
+ This routine returns the service info for the given service type/name.
+
+Arguments:
+
+ lpServiceType - pointer to the GUID for the service type
+
+ lpServiceName - service name
+
+ dwProperties - the properties of the service to return
+
+ lpServiceInfo - points to a buffer to return store the return info
+
+ lpdwBufferLen - on input, the count of bytes in lpServiceInfo. On output,
+ the minimum buffer size that can be passed to this API
+ to retrieve all the requested information
+
+Return Value:
+
+ Win32 error code.
+
+--*/
+{
+ DWORD err;
+ WORD nServiceType;
+
+ //
+ // Check for invalid parameters
+ //
+ if ( ( dwProperties == 0 )
+ || ( lpServiceType == NULL )
+ || ( lpServiceName == NULL )
+ || ( lpServiceName[0] == 0 )
+ || ( lpdwBufferLen == NULL )
+ )
+ {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // Check to see if the service type is supported in NetWare
+ //
+ if ( !(NwpLookupSapInRegistry( lpServiceType, &nServiceType, NULL, NULL )))
+ {
+ //
+ // Couldn't find it in the registry, see if it is a well-known GUID
+ //
+ if ( IS_SVCID_NETWARE( lpServiceType ))
+ {
+ nServiceType = SAPID_FROM_SVCID_NETWARE( lpServiceType );
+ }
+ else
+ {
+ //
+ // Not a well-known GUID either, return error
+ //
+ return ERROR_SERVICE_NOT_FOUND;
+ }
+ }
+
+ UNREFERENCED_PARAMETER(fUnicodeBlob) ;
+
+ RpcTryExcept
+ {
+ err = NwrGetService( NULL,
+ nServiceType,
+ lpServiceName,
+ dwProperties,
+ (LPBYTE) lpServiceInfo,
+ *lpdwBufferLen,
+ lpdwBufferLen );
+
+ if ( err == NO_ERROR )
+ {
+ INT i ;
+ LPSERVICE_INFO p = (LPSERVICE_INFO) lpServiceInfo;
+ LPSERVICE_ADDRESS lpAddress ;
+
+ //
+ // fix up pointers n main structure (convert from offsets)
+ //
+ if ( p->lpServiceType != NULL )
+ p->lpServiceType = (LPGUID) ((DWORD) p->lpServiceType +
+ (LPBYTE) p);
+ if ( p->lpServiceName != NULL )
+ p->lpServiceName = (LPWSTR)
+ ((DWORD) p->lpServiceName + (LPBYTE) p);
+ if ( p->lpComment != NULL )
+ p->lpComment = (LPWSTR) ((DWORD) p->lpComment + (LPBYTE) p);
+ if ( p->lpLocale != NULL )
+ p->lpLocale = (LPWSTR) ((DWORD) p->lpLocale + (LPBYTE) p);
+ if ( p->lpMachineName != NULL )
+ p->lpMachineName = (LPWSTR)
+ ((DWORD) p->lpMachineName + (LPBYTE)p);
+ if ( p->lpServiceAddress != NULL )
+ p->lpServiceAddress = (LPSERVICE_ADDRESSES)
+ ((DWORD) p->lpServiceAddress + (LPBYTE) p);
+ if ( p->ServiceSpecificInfo.pBlobData != NULL )
+ p->ServiceSpecificInfo.pBlobData = (LPBYTE)
+ ((DWORD) p->ServiceSpecificInfo.pBlobData + (LPBYTE) p);
+
+ //
+ // fix up pointers in the array of addresses
+ //
+ for (i = p->lpServiceAddress->dwAddressCount;
+ i > 0;
+ i--)
+ {
+ lpAddress =
+ &(p->lpServiceAddress->Addresses[i-1]) ;
+ lpAddress->lpAddress =
+ ((LPBYTE)p) + (DWORD)lpAddress->lpAddress ;
+ lpAddress->lpPrincipal =
+ ((LPBYTE)p) + (DWORD)lpAddress->lpPrincipal ;
+ }
+ }
+ }
+ RpcExcept(1)
+ {
+ err = ERROR_SERVICE_NOT_ACTIVE;
+#if 0 // the following is a good idea, but hard to get right
+ DWORD code = RpcExceptionCode();
+
+ if ( (code == RPC_S_SERVER_UNAVAILABLE)
+ ||
+ (code == RPC_S_UNKNOWN_IF) )
+ err
+ err = ERROR_SERVICE_NOT_ACTIVE;
+ else
+ err = NwpMapRpcError( code );
+#endif
+ }
+ RpcEndExcept
+
+ if ( err == ERROR_SERVICE_NOT_ACTIVE )
+ {
+ //
+ //CSNW not available, going to get it ourselves
+ //
+ err = NwGetService( NULL,
+ nServiceType,
+ lpServiceName,
+ dwProperties,
+ (LPBYTE) lpServiceInfo,
+ *lpdwBufferLen,
+ lpdwBufferLen );
+
+ if ( err == NO_ERROR )
+ {
+ INT i ;
+ LPSERVICE_INFO p = (LPSERVICE_INFO) lpServiceInfo;
+ LPSERVICE_ADDRESS lpAddress ;
+
+ //
+ // fix up pointers n main structure (convert from offsets)
+ //
+ if ( p->lpServiceType != NULL )
+ p->lpServiceType = (LPGUID) ((DWORD) p->lpServiceType +
+ (LPBYTE) p);
+ if ( p->lpServiceName != NULL )
+ p->lpServiceName = (LPWSTR)
+ ((DWORD) p->lpServiceName + (LPBYTE) p);
+ if ( p->lpComment != NULL )
+ p->lpComment = (LPWSTR) ((DWORD) p->lpComment + (LPBYTE) p);
+ if ( p->lpLocale != NULL )
+ p->lpLocale = (LPWSTR) ((DWORD) p->lpLocale + (LPBYTE) p);
+ if ( p->lpMachineName != NULL )
+ p->lpMachineName = (LPWSTR)
+ ((DWORD) p->lpMachineName + (LPBYTE)p);
+ if ( p->lpServiceAddress != NULL )
+ p->lpServiceAddress = (LPSERVICE_ADDRESSES)
+ ((DWORD) p->lpServiceAddress + (LPBYTE) p);
+ if ( p->ServiceSpecificInfo.pBlobData != NULL )
+ p->ServiceSpecificInfo.pBlobData = (LPBYTE)
+ ((DWORD) p->ServiceSpecificInfo.pBlobData + (LPBYTE) p);
+
+ //
+ // fix up pointers in the array of addresses
+ //
+ for (i = p->lpServiceAddress->dwAddressCount;
+ i > 0;
+ i--)
+ {
+ lpAddress =
+ &(p->lpServiceAddress->Addresses[i-1]) ;
+ lpAddress->lpAddress =
+ ((LPBYTE)p) + (DWORD)lpAddress->lpAddress ;
+ lpAddress->lpPrincipal =
+ ((LPBYTE)p) + (DWORD)lpAddress->lpPrincipal ;
+ }
+ }
+ }
+
+ return err;
+}
+
+DWORD
+SapSetService (
+ IN DWORD dwOperation,
+ IN DWORD dwFlags,
+ IN BOOL fUnicodeBlob,
+ IN LPSERVICE_INFO lpServiceInfo
+ )
+/*++
+
+Routine Description:
+
+ This routine registers or deregisters the given service type/name.
+
+Arguments:
+
+ dwOperation - Either SERVICE_REGISTER, SERVICE_DEREGISTER,
+ SERVICE_ADD_TYPE, SERVICE_DELETE_TYPE,
+ or SERVICE_FLUSH
+
+ dwFlags - ignored
+
+ lpServiceInfo - Pointer to a SERVICE_INFO structure containing all info
+ about the service.
+
+Return Value:
+
+ Win32 error code.
+
+--*/
+{
+ DWORD err;
+ WORD nServiceType;
+
+ UNREFERENCED_PARAMETER( dwFlags );
+
+ //
+ // Check for invalid parameters
+ //
+ switch ( dwOperation )
+ {
+ case SERVICE_REGISTER:
+ case SERVICE_DEREGISTER:
+ case SERVICE_ADD_TYPE:
+ case SERVICE_DELETE_TYPE:
+ break;
+
+ case SERVICE_FLUSH:
+ //
+ // This is a no-op in our provider, so just return success
+ //
+ return NO_ERROR;
+
+ default:
+ //
+ // We can probably say all other operations which we have no
+ // knowledge of are ignored by us. So, just return success.
+ //
+ return NO_ERROR;
+ }
+
+ if ( ( lpServiceInfo == NULL )
+ || ( lpServiceInfo->lpServiceType == NULL )
+ || ( ((lpServiceInfo->lpServiceName == NULL) ||
+ (lpServiceInfo->lpServiceName[0] == 0 )) &&
+ ((dwOperation != SERVICE_ADD_TYPE) &&
+ (dwOperation != SERVICE_DELETE_TYPE))
+ )
+
+ )
+ {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // See if operation is adding or deleting a service type
+ //
+ if ( dwOperation == SERVICE_ADD_TYPE )
+ {
+ return NwpAddServiceType( lpServiceInfo, fUnicodeBlob );
+ }
+ else if ( dwOperation == SERVICE_DELETE_TYPE )
+ {
+ return NwpDeleteServiceType( lpServiceInfo, fUnicodeBlob );
+ }
+
+ //
+ // Check to see if the service type is supported in NetWare
+ //
+ if ( !(NwpLookupSapInRegistry( lpServiceInfo->lpServiceType, &nServiceType, NULL, NULL )))
+ {
+ //
+ // Couldn't find it in the registry, see if it is a well-known GUID
+ //
+ if ( IS_SVCID_NETWARE( lpServiceInfo->lpServiceType ))
+ {
+ nServiceType = SAPID_FROM_SVCID_NETWARE( lpServiceInfo->lpServiceType );
+ }
+ else
+ {
+ //
+ // Not a well-known GUID either, return error
+ //
+ return ERROR_SERVICE_NOT_FOUND;
+ }
+ }
+
+ //
+ // Operation is either SERVICE_REGISTER or SERVICE_DEREGISTER.
+ // Pass it on to the common code used by this and the RnR2
+ // SetService
+ //
+
+ err = pSapSetService(dwOperation, lpServiceInfo, nServiceType);
+ return(err);
+}
+
+DWORD
+pSapSetService2(
+ IN DWORD dwOperation,
+ IN LPWSTR lpszServiceInstance,
+ IN PBYTE pbAddress,
+ IN LPGUID pType,
+ IN WORD nServiceType
+ )
+/*++
+Routine Description:
+ Jacket routine called by the RnR2 SetService. This routine is
+ an impedance matcher to coerce data structures. It winds
+ up calling pSapSetService2 once it has constructed the
+ SERVICE_INFO structure.
+--*/
+{
+ SERVICE_INFO siInfo;
+ SERVICE_ADDRESSES ServiceAddresses;
+ LPSERVICE_ADDRESS psa = &ServiceAddresses.Addresses[0];
+
+ ServiceAddresses.dwAddressCount = 1;
+ memset(&siInfo, 0, sizeof(siInfo));
+ siInfo.lpServiceName = lpszServiceInstance;
+ siInfo.lpServiceAddress = &ServiceAddresses;
+ psa->dwAddressType = AF_IPX;
+ psa->dwAddressFlags = psa->dwPrincipalLength = 0;
+ psa->dwAddressLength = sizeof(SOCKADDR_IPX);
+ psa->lpPrincipal = 0;
+ psa->lpAddress = pbAddress;
+ siInfo.lpServiceType = pType;
+ return(pSapSetService(dwOperation, &siInfo, nServiceType));
+}
+
+
+DWORD
+pSapSetService(
+ IN DWORD dwOperation,
+ IN LPSERVICE_INFO lpServiceInfo,
+ IN WORD nServiceType)
+/*++
+Routine Description:
+ Common routine to do the SAP advertisement.
+--*/
+{
+ DWORD err;
+
+ RpcTryExcept
+ {
+ err = NwrSetService( NULL, dwOperation, lpServiceInfo, nServiceType );
+ }
+ RpcExcept(1)
+ {
+ err = ERROR_SERVICE_NOT_ACTIVE;
+#if 0
+ DWORD code = RpcExceptionCode();
+
+ if ( (code == RPC_S_SERVER_UNAVAILABLE)
+ ||
+ (code == RPC_S_UNKNOWN_IF) )
+ {
+ err = ERROR_SERVICE_NOT_ACTIVE;
+ }
+ else
+ {
+ err = NwpMapRpcError( code );
+ }
+#endif
+ }
+ RpcEndExcept
+
+ if ( err == ERROR_SERVICE_NOT_ACTIVE )
+ {
+ //
+ //CSNW not available, going to try use the SAP agent, else we do it ourselves
+ //
+ err = NO_ERROR;
+
+ //
+ // Check if all parameters passed in are valid
+ //
+ if ( wcslen( lpServiceInfo->lpServiceName ) > SAP_OBJECT_NAME_MAX_LENGTH-1 )
+ {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ switch ( dwOperation )
+ {
+ case SERVICE_REGISTER:
+ err = NwRegisterService( lpServiceInfo, nServiceType, NULL );
+ break;
+
+ case SERVICE_DEREGISTER:
+ err = NwDeregisterService( lpServiceInfo, nServiceType );
+ break;
+
+ default: //this should never occur, but just in case . . .
+ err = ERROR_INVALID_PARAMETER;
+ break;
+ }
+ }
+
+ return err;
+}
+
+DWORD
+SapFreeSapSocket(SOCKET s)
+{
+/*++
+Routine Description:
+
+ Release the socket and clean up
+--*/
+ DWORD err = NO_ERROR;
+
+ closesocket( s );
+ return(err);
+}
+
+DWORD
+SapGetSapSocket(SOCKET * ps)
+{
+/*++
+Routine Description:
+
+ Get a socket suitable for making SAP queries
+
+Arguments: None
+
+--*/
+ SOCKET socketSap;
+ WSADATA wsaData;
+ SOCKADDR_IPX socketAddr;
+ DWORD err = NO_ERROR;
+ INT nValue;
+ DWORD dwNonBlocking = 1;
+
+ //
+ // Initialize the socket interface
+ //
+// err = WSAStartup( WSOCK_VER_REQD, &wsaData );
+// if ( err )
+// {
+// return err;
+// }
+
+ //
+ // Open an IPX datagram socket
+ //
+ socketSap = socket( AF_IPX, SOCK_DGRAM, NSPROTO_IPX );
+ if ( socketSap == INVALID_SOCKET )
+ {
+ err = WSAGetLastError();
+// (VOID) WSACleanup();
+ return err;
+ }
+
+ //
+ // Set the socket to non-blocking
+ //
+ if ( ioctlsocket( socketSap, FIONBIO, &dwNonBlocking ) == SOCKET_ERROR )
+ {
+ err = WSAGetLastError();
+ goto ErrExit;
+ }
+
+ //
+ // Allow sending of broadcasts
+ //
+ nValue = 1;
+ if ( setsockopt( socketSap,
+ SOL_SOCKET,
+ SO_BROADCAST,
+ (PVOID) &nValue,
+ sizeof(INT)) == SOCKET_ERROR )
+ {
+ err = WSAGetLastError();
+ goto ErrExit;
+ }
+
+ //
+ // Bind the socket
+ //
+ memset( &socketAddr, 0, sizeof( SOCKADDR_IPX));
+ socketAddr.sa_family = AF_IPX;
+ socketAddr.sa_socket = 0; // no specific port
+
+ if ( bind( socketSap,
+ (PSOCKADDR) &socketAddr,
+ sizeof( SOCKADDR_IPX)) == SOCKET_ERROR )
+ {
+ err = WSAGetLastError();
+ goto ErrExit;
+ }
+
+ //
+ // Set the extended address option
+ //
+ nValue = 1;
+ if ( setsockopt( socketSap, // Socket Handle
+ NSPROTO_IPX, // Option Level
+ IPX_EXTENDED_ADDRESS, // Option Name
+ (PUCHAR)&nValue, // Ptr to on/off flag
+ sizeof(INT)) == SOCKET_ERROR ) // Length of flag
+ {
+ err = WSAGetLastError();
+ goto ErrExit;
+ }
+
+ *ps = socketSap;
+
+ return(err);
+
+ErrExit:
+ SapFreeSapSocket(socketSap); // cleans up lots of stuff
+ return(err);
+}
+
+
+DWORD
+NwpGetAddressForRnRViaSap(
+ IN HANDLE hRnRHandle,
+ IN WORD nServiceType,
+ IN LPWSTR lpServiceName,
+ IN DWORD nProt,
+ IN OUT LPVOID lpCsAddrBuffer,
+ IN OUT LPDWORD lpdwBufferLength,
+ IN HANDLE hCancellationEvent,
+ OUT LPDWORD lpcAddress
+ )
+{
+/*++
+Routine Description:
+
+ This routine uses SAP requests to find the address of the given service
+ name/type. It can handle looking up by type only, or by name and type.
+ The latter case is the same as the old RnR code, see below for
+ it and for a description of the arguments
+--*/
+ return(0);
+}
+
+#define MAX_LOOPS_FOR_SAP 4
+
+DWORD
+SapGetSapForType(
+ PSAP_BCAST_CONTROL psbc,
+ WORD nServiceType)
+{
+/*++
+Routine Description:
+ Does the work of send Sap queries and fetching results.
+ The first message sent is done according to the requester, and
+ may be limited to the local LAN or not.
+
+Arguments:
+ psbc -- pointer to the control information
+ wSapType -- Sap type
+--*/
+ SAP_REQUEST sapRequest;
+ UCHAR destAddr[SAP_ADDRESS_LENGTH];
+ DWORD startTickCount;
+ UCHAR recvBuffer[SAP_MAXRECV_LENGTH];
+ INT bytesReceived;
+ BOOL fFound = FALSE;
+ DWORD err = NO_ERROR;
+
+ sapRequest.QueryType = htons( psbc->wQueryType );
+ sapRequest.ServerType = htons( nServiceType );
+
+ //
+ // Set the address to send to
+ //
+ memcpy( destAddr, SapBroadcastAddress, SAP_ADDRESS_LENGTH );
+
+ //
+ // Ready to go. This might be the inital call, in which case
+ // we start off by sending. In all other cases, we start
+ // out receiving.
+ //
+
+ //
+ // In the full case,
+ // we will send out SAP requests 3 times and wait 1 sec for
+ // Sap responses the first time, 2 sec the second and 4 sec the
+ // third time.
+ //
+ for (; !fFound && (psbc->dwIndex < MAX_LOOPS_FOR_SAP); psbc->dwIndex++ )
+ {
+ DWORD dwRet;
+ DWORD dwTimeOut = (1 << psbc->dwIndex) * 1000;
+
+ if(psbc->dwTickCount)
+ {
+ dwRet = dwrcNil;
+ //
+ // Need to do some reading ...
+ //
+ do
+ {
+ PSAP_IDENT_HEADER pSap;
+
+
+ if((psbc->psrc->fFlags & SAP_F_END_CALLED)
+ ||
+ psbc->fCheckCancel(psbc->pvArg))
+ {
+ err = dwrcCancel;
+ goto CleanExit;
+ }
+
+ //
+ // Sleeps for 50 ms so that we might get something on first read
+ //
+ Sleep( 50 );
+
+ bytesReceived = recvfrom( psbc->s,
+ recvBuffer,
+ SAP_MAXRECV_LENGTH,
+ 0,
+ NULL,
+ NULL );
+
+ if ( bytesReceived == SOCKET_ERROR )
+ {
+ err = WSAGetLastError();
+ if ( err == WSAEWOULDBLOCK ) // no data on socket, continue looping
+ {
+ if(dwRet == dwrcNoWait)
+ {
+ fFound = TRUE;
+ }
+ err = NO_ERROR;
+ continue;
+ }
+ }
+
+ if ( ( err != NO_ERROR ) // err occurred in recvfrom
+ || ( bytesReceived == 0 ) // or socket closed
+ )
+ {
+ goto CleanExit;
+ }
+
+ //
+ // Skip over query type
+ //
+ bytesReceived -= sizeof(USHORT);
+ pSap = (PSAP_IDENT_HEADER) &(recvBuffer[sizeof(USHORT)]);
+
+ //
+ // Tell the caller we've something to look over
+ //
+ while ( bytesReceived >= sizeof( SAP_IDENT_HEADER ))
+ {
+
+ dwRet = psbc->Func(psbc, pSap, &err);
+ if((dwRet == dwrcDone)
+ ||
+ (dwRet == dwrcCancel))
+ {
+ fFound = TRUE;
+ break;
+ }
+
+ pSap++;
+ bytesReceived -= sizeof( SAP_IDENT_HEADER );
+ }
+ }
+ while ( !fFound
+ && ((GetTickCount() - psbc->dwTickCount) < dwTimeOut )
+ );
+ }
+
+
+ // Send the packet out
+ //
+ if((fFound && (dwRet == dwrcNoWait))
+ ||
+ (psbc->dwIndex == (MAX_LOOPS_FOR_SAP -1)))
+ {
+ goto CleanExit;
+ }
+ if ( sendto( psbc->s,
+ (PVOID) &sapRequest,
+ sizeof( sapRequest ),
+ 0,
+ (PSOCKADDR) destAddr,
+ SAP_ADDRESS_LENGTH ) == SOCKET_ERROR )
+ {
+ err = WSAGetLastError();
+ goto CleanExit;
+ }
+ psbc->dwTickCount = GetTickCount();
+ }
+
+ if(!fFound)
+ {
+ err = WSAEADDRNOTAVAIL;
+ }
+
+CleanExit:
+
+ return err;
+}
+
+BOOL
+NwpLookupSapInRegistry(
+ IN LPGUID lpServiceType,
+ OUT PWORD pnSapType,
+ OUT PWORD pwPort,
+ IN OUT PDWORD pfConnectionOriented
+ )
+/*++
+
+Routine Description:
+
+ This routine looks up the GUID in the registry under
+ Control\ServiceProvider\ServiceTypes and trys to read the SAP type
+ from the registry.
+
+Arguments:
+
+ lpServiceType - the GUID to look for
+ pnSapType - on return, contains the SAP type
+
+Return Value:
+
+ Returns FALSE if we can't get the SAP type, TRUE otherwise
+
+--*/
+{
+ DWORD err;
+ BOOL fFound = FALSE;
+
+ HKEY hkey = NULL;
+ HKEY hkeyServiceType = NULL;
+ DWORD dwIndex = 0;
+ WCHAR szBuffer[ MAX_PATH + 1];
+ DWORD dwLen;
+ FILETIME ftLastWrite;
+
+ //
+ // Open the service types key
+ //
+ err = RegOpenKeyExW( HKEY_LOCAL_MACHINE,
+ NW_SERVICE_TYPES_REGKEY,
+ 0,
+ KEY_READ,
+ &hkey );
+
+ if ( err )
+ {
+ // Cannot find the key because it is not created yet since no
+ // one called Add service type. We return FALSE indicating
+ // Sap type not found.
+ return FALSE;
+ }
+
+ //
+ // Loop through all subkey of service types to find the GUID
+ //
+ for ( dwIndex = 0; ; dwIndex++ )
+ {
+ GUID guid;
+
+ dwLen = sizeof( szBuffer ) / sizeof( WCHAR );
+ err = RegEnumKeyExW( hkey,
+ dwIndex,
+ szBuffer, // Buffer big enough to
+ // hold any key name
+ &dwLen, // in characters
+ NULL,
+ NULL,
+ NULL,
+ &ftLastWrite );
+
+ //
+ // We will break out of here on any error, this includes
+ // the error ERROR_NO_MORE_ITEMS which means that we have finish
+ // enumerating all the keys.
+ //
+ if ( err )
+ {
+ if ( err == ERROR_NO_MORE_ITEMS ) // No more to enumerate
+ err = NO_ERROR;
+ break;
+ }
+
+ err = RegOpenKeyExW( hkey,
+ szBuffer,
+ 0,
+ KEY_READ,
+ &hkeyServiceType );
+
+
+ if ( err )
+ break;
+
+ dwLen = sizeof( szBuffer );
+ err = RegQueryValueExW( hkeyServiceType,
+ NW_GUID_VALUE_NAME,
+ NULL,
+ NULL,
+ (LPBYTE) szBuffer, // Buffer big enough to
+ // hold any GUID
+ &dwLen ); // in bytes
+
+ if ( err == ERROR_FILE_NOT_FOUND )
+ continue; // continue with the next key
+ else if ( err )
+ break;
+
+
+ // Get rid of the end curly brace
+ szBuffer[ dwLen/sizeof(WCHAR) - 2] = 0;
+
+ err = UuidFromStringW( szBuffer + 1, // go past the first curly brace
+ &guid );
+
+ if ( err )
+ continue; // continue with the next key, err might be returned
+ // if buffer does not contain a valid GUID
+
+ if ( !memcmp( lpServiceType, &guid, sizeof(GUID)))
+ {
+ DWORD dwTmp;
+ dwLen = sizeof( dwTmp );
+ err = RegQueryValueExW( hkeyServiceType,
+ SERVICE_TYPE_VALUE_SAPID,
+ NULL,
+ NULL,
+ (LPBYTE) &dwTmp,
+ &dwLen ); // in bytes
+
+ if ( !err )
+ {
+ fFound = TRUE;
+ *pnSapType = (WORD) dwTmp;
+ if ( ARGUMENT_PRESENT( pwPort ))
+ {
+ err = RegQueryValueExW( hkeyServiceType,
+ L"Port",
+ NULL,
+ NULL,
+ (LPBYTE) &dwTmp,
+ &dwLen ); // in bytes
+
+ if ( !err )
+ {
+ *pwPort = (WORD)dwTmp;
+ }
+ }
+ if ( ARGUMENT_PRESENT( pfConnectionOriented ))
+ {
+ err = RegQueryValueExW( hkeyServiceType,
+ SERVICE_TYPE_VALUE_CONN,
+ NULL,
+ NULL,
+ (LPBYTE) &dwTmp,
+ &dwLen ); // in bytes
+
+ if ( !err )
+ *pfConnectionOriented = dwTmp? 1: 0;
+ }
+ }
+ else if ( err == ERROR_FILE_NOT_FOUND )
+ {
+ continue; // continue with the next key since we can't
+ // find Sap Id
+ }
+ break;
+ }
+
+ RegCloseKey( hkeyServiceType );
+ hkeyServiceType = NULL;
+ }
+
+ if ( hkeyServiceType != NULL )
+ RegCloseKey( hkeyServiceType );
+
+ if ( hkey != NULL )
+ RegCloseKey( hkey );
+
+ return fFound;
+}
+
+DWORD
+NwpRnR2AddServiceType(
+ IN LPWSTR lpServiceTypeName,
+ IN LPGUID lpClassType,
+ IN WORD wSapId,
+ IN WORD wPort
+)
+{
+ HKEY hKey, hKeyService;
+ PWCHAR pwszUuid;
+ DWORD dwDisposition, err;
+ DWORD dwValue = (DWORD)wSapId;
+ WCHAR wszUuid[36 + 1 + 2]; // to hold the GUID
+
+ err = RegCreateKeyEx( HKEY_LOCAL_MACHINE,
+ NW_SERVICE_TYPES_REGKEY,
+ 0,
+ TEXT(""),
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ | KEY_WRITE,
+ NULL,
+ &hKey,
+ &dwDisposition );
+
+ if(err)
+ {
+ return(GetLastError());
+ }
+
+ //
+ // Open the key corresponding to the service (create if not there).
+ //
+
+ err = RegCreateKeyEx(
+ hKey,
+ lpServiceTypeName,
+ 0,
+ TEXT(""),
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ | KEY_WRITE,
+ NULL,
+ &hKeyService,
+ &dwDisposition
+ );
+
+ if(!err)
+ {
+ //
+ // ready to put the GUID value in.
+ //
+
+ UuidToString(
+ lpClassType,
+ &pwszUuid);
+
+ wszUuid[0] = L'{';
+ memcpy(&wszUuid[1], pwszUuid, 36 * sizeof(WCHAR));
+ wszUuid[37] = L'}';
+ wszUuid[38] = 0;
+
+ RpcStringFree(&pwszUuid);
+
+ //
+ // write it
+ //
+
+ err = RegSetValueEx(
+ hKeyService,
+ L"GUID",
+ 0,
+ REG_SZ,
+ (LPBYTE)wszUuid,
+ 39 * sizeof(WCHAR));
+
+ if(!err)
+ {
+ err = RegSetValueEx(
+ hKeyService,
+ L"SAPID",
+ 0,
+ REG_DWORD,
+ (LPBYTE)&dwValue,
+ sizeof(DWORD));
+
+ dwValue = (DWORD)wPort;
+
+ err = RegSetValueEx(
+ hKeyService,
+ L"PORT",
+ 0,
+ REG_DWORD,
+ (LPBYTE)&dwValue,
+ sizeof(DWORD));
+ }
+ RegCloseKey(hKeyService);
+ }
+ RegCloseKey(hKey);
+ if(err)
+ {
+ err = GetLastError();
+ }
+ return(err);
+}
+
+
+DWORD
+NwpAddServiceType(
+ IN LPSERVICE_INFO lpServiceInfo,
+ IN BOOL fUnicodeBlob
+)
+/*++
+
+Routine Description:
+
+ This routine adds a new service type and its info to the registry under
+ Control\ServiceProvider\ServiceTypes
+
+Arguments:
+
+ lpServiceInfo - the ServiceSpecificInfo contains the service type info
+ fUnicodeBlob - TRUE if the above field contains unicode data,
+ FALSE otherwise
+
+Return Value:
+
+ Win32 error
+
+--*/
+{
+ DWORD err;
+ HKEY hkey = NULL;
+ HKEY hkeyType = NULL;
+
+ SERVICE_TYPE_INFO *pSvcTypeInfo = (SERVICE_TYPE_INFO *)
+ lpServiceInfo->ServiceSpecificInfo.pBlobData;
+ LPWSTR pszSvcTypeName;
+ UNICODE_STRING uniStr;
+ DWORD i;
+ PSERVICE_TYPE_VALUE pVal;
+
+ //
+ // Get the new service type name
+ //
+ if ( fUnicodeBlob )
+ {
+ pszSvcTypeName = (LPWSTR) (((LPBYTE) pSvcTypeInfo) +
+ pSvcTypeInfo->dwTypeNameOffset );
+ }
+ else
+ {
+ ANSI_STRING ansiStr;
+
+ RtlInitAnsiString( &ansiStr,
+ (LPSTR) (((LPBYTE) pSvcTypeInfo) +
+ pSvcTypeInfo->dwTypeNameOffset ));
+
+ err = RtlAnsiStringToUnicodeString( &uniStr, &ansiStr, TRUE );
+ if ( err )
+ return err;
+
+ pszSvcTypeName = uniStr.Buffer;
+ }
+
+ //
+ // If the service type name is an empty string, return error.
+ //
+ if ( ( pSvcTypeInfo->dwTypeNameOffset == 0 )
+ || ( pszSvcTypeName == NULL )
+ || ( *pszSvcTypeName == 0 ) // empty string
+ )
+ {
+ err = ERROR_INVALID_PARAMETER;
+ goto CleanExit;
+
+ }
+
+ //
+ // The following keys should have already been created
+ //
+ err = RegOpenKeyExW( HKEY_LOCAL_MACHINE,
+ NW_SERVICE_TYPES_REGKEY,
+ 0,
+ KEY_READ | KEY_WRITE,
+ &hkey );
+
+ if ( err )
+ goto CleanExit;
+
+ err = RegOpenKeyExW( hkey,
+ pszSvcTypeName,
+ 0,
+ KEY_READ | KEY_WRITE,
+ &hkeyType );
+
+ if ( err )
+ goto CleanExit;
+
+ //
+ // Loop through all values in the specific and add them one by one
+ // to the registry if it belongs to our name space
+ //
+ for ( i = 0, pVal = pSvcTypeInfo->Values;
+ i < pSvcTypeInfo->dwValueCount;
+ i++, pVal++ )
+ {
+ if ( ! ((pVal->dwNameSpace == NS_SAP) ||
+ (pVal->dwNameSpace == NS_DEFAULT)) )
+ {
+ continue; // ignore values not in our name space
+ }
+
+ if ( fUnicodeBlob )
+ {
+ err = RegSetValueExW(
+ hkeyType,
+ (LPWSTR) ( ((LPBYTE) pSvcTypeInfo) + pVal->dwValueNameOffset),
+ 0,
+ pVal->dwValueType,
+ (LPBYTE) ( ((LPBYTE) pSvcTypeInfo) + pVal->dwValueOffset),
+ pVal->dwValueSize
+ );
+ }
+ else
+ {
+ err = RegSetValueExA(
+ hkeyType,
+ (LPSTR) ( ((LPBYTE) pSvcTypeInfo) + pVal->dwValueNameOffset),
+ 0,
+ pVal->dwValueType,
+ (LPBYTE) ( ((LPBYTE) pSvcTypeInfo) + pVal->dwValueOffset),
+ pVal->dwValueSize
+ );
+ }
+ }
+
+CleanExit:
+
+ if ( !fUnicodeBlob )
+ RtlFreeUnicodeString( &uniStr );
+
+ if ( hkeyType != NULL )
+ RegCloseKey( hkeyType );
+
+ if ( hkey != NULL )
+ RegCloseKey( hkey );
+
+ return err;
+
+}
+
+DWORD
+NwpDeleteServiceType(
+ IN LPSERVICE_INFO lpServiceInfo,
+ IN BOOL fUnicodeBlob
+)
+/*++
+
+Routine Description:
+
+ This routine deletes a service type and its info from the registry under
+ Control\ServiceProvider\ServiceTypes
+
+Arguments:
+
+ lpServiceInfo - the ServiceSpecificInfo contains the service type info
+ fUnicodeBlob - TRUE if the above field contains unicode data,
+ FALSE otherwise
+
+Return Value:
+
+ Win32 error
+
+--*/
+{
+ DWORD err;
+ HKEY hkey = NULL;
+ SERVICE_TYPE_INFO *pSvcTypeInfo = (SERVICE_TYPE_INFO *)
+ lpServiceInfo->ServiceSpecificInfo.pBlobData;
+ LPWSTR pszSvcTypeName;
+ UNICODE_STRING uniStr;
+
+ //
+ // Get the service type name to be deleted
+ //
+ if ( fUnicodeBlob )
+ {
+ pszSvcTypeName = (LPWSTR) (((LPBYTE) pSvcTypeInfo) +
+ pSvcTypeInfo->dwTypeNameOffset );
+ }
+ else
+ {
+ ANSI_STRING ansiStr;
+
+ RtlInitAnsiString( &ansiStr,
+ (LPSTR) (((LPBYTE) pSvcTypeInfo) +
+ pSvcTypeInfo->dwTypeNameOffset ));
+
+ err = RtlAnsiStringToUnicodeString( &uniStr, &ansiStr, TRUE );
+ if ( err )
+ return err;
+
+ pszSvcTypeName = uniStr.Buffer;
+ }
+
+ //
+ // If the service type name is an empty string, return error.
+ //
+ if ( ( pSvcTypeInfo->dwTypeNameOffset == 0 )
+ || ( pszSvcTypeName == NULL )
+ || ( *pszSvcTypeName == 0 ) // empty string
+ )
+ {
+ err = ERROR_INVALID_PARAMETER;
+ goto CleanExit;
+
+ }
+
+ err = RegOpenKeyExW( HKEY_LOCAL_MACHINE,
+ NW_SERVICE_TYPES_REGKEY,
+ 0,
+ KEY_READ | KEY_WRITE,
+ &hkey );
+
+
+ if ( !err )
+ {
+ err = RegDeleteKey( hkey,
+ pszSvcTypeName );
+ }
+
+ if ( err == ERROR_FILE_NOT_FOUND )
+ {
+ // Perhaps before calling my provider, the router already deleted the
+ // this key, hence just return success;
+ err = NO_ERROR;
+ }
+
+CleanExit:
+
+ if ( !fUnicodeBlob )
+ RtlFreeUnicodeString( &uniStr );
+
+ if ( hkey != NULL )
+ RegCloseKey( hkey );
+
+ return err;
+
+}
+
+#define SOCKSIZE (sizeof(SOCKADDR_IPX) + sizeof(DWORD) - 1)
+
+DWORD
+FillBufferWithCsAddr(
+ IN LPBYTE pAddress,
+ IN DWORD nProt,
+ IN OUT LPVOID lpCsAddrBuffer,
+ IN OUT LPDWORD lpdwBufferLength,
+ OUT LPDWORD pcAddress
+)
+{
+ DWORD nAddrCount = 0;
+ CSADDR_INFO *pCsAddr;
+ SOCKADDR_IPX *pAddrLocal, *pAddrRemote;
+ DWORD i;
+ LPBYTE pBuffer;
+
+ if ( nProt & SPXII_BIT )
+ nAddrCount++;
+
+ if ( nProt & IPX_BIT )
+ nAddrCount++;
+
+ if ( nProt & SPX_BIT )
+ nAddrCount++;
+
+
+ if ( *lpdwBufferLength <
+ nAddrCount * ( sizeof( CSADDR_INFO) + (2*SOCKSIZE)))
+ {
+ *lpdwBufferLength = sizeof(DWORD) -1 + (nAddrCount *
+ ( sizeof( CSADDR_INFO) + (2 * SOCKSIZE)));
+ return ERROR_INSUFFICIENT_BUFFER;
+ }
+
+ pBuffer = ((LPBYTE) lpCsAddrBuffer) + sizeof( CSADDR_INFO) * nAddrCount;
+
+ for ( i = 0, pCsAddr = (CSADDR_INFO *)lpCsAddrBuffer;
+ (i < nAddrCount) && ( nProt != 0 );
+ i++, pCsAddr++ )
+ {
+ if ( nProt & SPXII_BIT )
+ {
+ pCsAddr->iSocketType = SOCK_SEQPACKET;
+ pCsAddr->iProtocol = NSPROTO_SPXII;
+ nProt &= ~SPXII_BIT;
+ }
+ else if ( nProt & IPX_BIT )
+ {
+ pCsAddr->iSocketType = SOCK_DGRAM;
+ pCsAddr->iProtocol = NSPROTO_IPX;
+ nProt &= ~IPX_BIT;
+ }
+ else if ( nProt & SPX_BIT )
+ {
+ pCsAddr->iSocketType = SOCK_SEQPACKET;
+ pCsAddr->iProtocol = NSPROTO_SPX;
+ nProt &= ~SPX_BIT;
+ }
+ else
+ {
+ break;
+ }
+
+ pCsAddr->LocalAddr.iSockaddrLength = sizeof( SOCKADDR_IPX );
+ pCsAddr->RemoteAddr.iSockaddrLength = sizeof( SOCKADDR_IPX );
+ pCsAddr->LocalAddr.lpSockaddr =
+ (LPSOCKADDR) pBuffer;
+ pCsAddr->RemoteAddr.lpSockaddr =
+ (LPSOCKADDR) ( pBuffer + sizeof(SOCKADDR_IPX));
+ pBuffer += 2 * sizeof( SOCKADDR_IPX );
+
+ pAddrLocal = (SOCKADDR_IPX *) pCsAddr->LocalAddr.lpSockaddr;
+ pAddrRemote = (SOCKADDR_IPX *) pCsAddr->RemoteAddr.lpSockaddr;
+
+ pAddrLocal->sa_family = AF_IPX;
+ pAddrRemote->sa_family = AF_IPX;
+
+ //
+ // The default local sockaddr is for IPX is
+ // sa_family = AF_IPX and all other bytes = 0.
+ //
+
+ RtlZeroMemory( pAddrLocal->sa_netnum,
+ IPX_ADDRESS_LENGTH );
+
+ //
+ // If pAddress is NULL, i.e. we are doing RES_SERVICE,
+ // just make all bytes in remote address zero.
+ //
+ if ( pAddress == NULL )
+ {
+ RtlZeroMemory( pAddrRemote->sa_netnum,
+ IPX_ADDRESS_LENGTH );
+ }
+ else
+ {
+ RtlCopyMemory( pAddrRemote->sa_netnum,
+ pAddress,
+ IPX_ADDRESS_LENGTH );
+ }
+ }
+
+ *pcAddress = nAddrCount;
+
+ return NO_ERROR;
+}
+
+VOID
+NwInitializeServiceProvider(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine initializes the service provider.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ // nothing more to do
+}
+
+VOID
+NwTerminateServiceProvider(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine cleans up the service provider.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PREGISTERED_SERVICE pSvc, pNext;
+
+ //
+ // Clean up the link list and stop sending all SAP advertise packets
+ //
+
+ EnterCriticalSection( &NwServiceListCriticalSection );
+
+ for ( pSvc = pServiceListHead; pSvc != NULL; pSvc = pNext )
+ {
+ pNext = pSvc->Next;
+
+ if ( pSvc->fAdvertiseBySap )
+ {
+ UNICODE_STRING uServer;
+ OEM_STRING oemServer;
+ NTSTATUS ntstatus;
+
+ RtlInitUnicodeString( &uServer, pSvc->pServiceInfo->lpServiceName );
+ ntstatus = RtlUnicodeStringToOemString( &oemServer, &uServer, TRUE);
+ if ( NT_SUCCESS( ntstatus ) )
+ {
+ (VOID) SapRemoveAdvertise( oemServer.Buffer,
+ pSvc->nSapType );
+ RtlFreeOemString( &oemServer );
+ }
+ }
+
+ (VOID) LocalFree( pSvc->pServiceInfo );
+ (VOID) LocalFree( pSvc );
+ }
+
+ LeaveCriticalSection( &NwServiceListCriticalSection );
+
+ //
+ // Clean up the SAP interface
+ //
+ (VOID) SapLibShutdown();
+
+ //
+ // Clean up the socket interface
+ //
+ if ( fInitSocket )
+ {
+ closesocket( socketSap );
+// (VOID) WSACleanup();
+ }
+
+}
+
+DWORD
+NwRegisterService(
+ IN LPSERVICE_INFO lpServiceInfo,
+ IN WORD nSapType,
+ IN HANDLE hEventHandle
+ )
+/*++
+
+Routine Description:
+
+ This routine registers the given service.
+
+Arguments:
+
+ lpServiceInfo - contains the service information
+
+ nSapType - The SAP type to advertise
+
+ hEventHandle - A handle to the NwDoneEvent if this code is running in
+ the context of Client Services for NetWare. If this is NULL,
+ then CSNW is not available and this code is running in the
+ context of a regular executable.
+
+Return Value:
+
+ Win32 error.
+
+--*/
+{
+ DWORD err = NO_ERROR;
+ NTSTATUS ntstatus;
+ DWORD i;
+ INT nIPX = -1;
+
+ //
+ // Check to see if the service address array contains IPX address,
+ // we will only use the first ipx address contained in the array.
+ //
+ if ( lpServiceInfo->lpServiceAddress == NULL )
+ return ERROR_INCORRECT_ADDRESS;
+
+ for ( i = 0; i < lpServiceInfo->lpServiceAddress->dwAddressCount; i++)
+ {
+ if ( lpServiceInfo->lpServiceAddress->Addresses[i].dwAddressType
+ == AF_IPX )
+ {
+ nIPX = (INT) i;
+ break;
+ }
+ }
+
+ //
+ // If we cannot find a IPX address, return error
+ //
+ if ( nIPX == -1 )
+ return ERROR_INCORRECT_ADDRESS;
+
+ //
+ // Try to deregister the service since the service might have
+ // been registered but not deregistered
+ //
+ err = NwDeregisterService( lpServiceInfo, nSapType );
+ if ( ( err != NO_ERROR ) // deregister successfully
+ && ( err != ERROR_SERVICE_NOT_FOUND ) // service not registered before
+ )
+ {
+ return err;
+ }
+
+ err = NO_ERROR;
+
+ //
+ // Try and see if SAP service can advertise the service for us.
+ //
+ ntstatus = SapLibInit();
+ if ( NT_SUCCESS( ntstatus ))
+ {
+ UNICODE_STRING uServer;
+ OEM_STRING oemServer;
+ INT sapRet;
+ BOOL fContinueLoop = FALSE;
+
+ RtlInitUnicodeString( &uServer, lpServiceInfo->lpServiceName );
+ ntstatus = RtlUnicodeStringToOemString( &oemServer, &uServer, TRUE );
+ if ( !NT_SUCCESS( ntstatus ))
+ return RtlNtStatusToDosError( ntstatus );
+
+
+ do
+ {
+ sapRet = SapAddAdvertise( oemServer.Buffer,
+ nSapType,
+ (LPBYTE) (((LPSOCKADDR_IPX) lpServiceInfo->lpServiceAddress->Addresses[nIPX].lpAddress)->sa_netnum),
+ FALSE );
+
+ switch ( sapRet )
+ {
+ case SAPRETURN_SUCCESS:
+ {
+ err = AddServiceToList( lpServiceInfo, nSapType, TRUE, nIPX );
+ if ( err )
+ (VOID) SapRemoveAdvertise( oemServer.Buffer, nSapType );
+ RtlFreeOemString( &oemServer );
+
+ return err;
+ }
+
+ case SAPRETURN_NOMEMORY:
+ err = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+
+ case SAPRETURN_EXISTS:
+ {
+ //
+ // Someone else is already advertising the service
+ // directly through SAP service. Remove it and
+ // readvertise with the new information.
+ //
+ sapRet = SapRemoveAdvertise( oemServer.Buffer, nSapType );
+ switch ( sapRet )
+ {
+ case SAPRETURN_SUCCESS:
+ fContinueLoop = TRUE; // go thru once more
+ break;
+
+ case SAPRETURN_NOMEMORY:
+ err = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+
+ case SAPRETURN_NOTEXIST:
+ case SAPRETURN_INVALIDNAME:
+ default: // Should not have any other errors
+ err = ERROR_INVALID_PARAMETER;
+ break;
+ }
+ break;
+ }
+
+ case SAPRETURN_INVALIDNAME:
+ err = ERROR_INVALID_PARAMETER;
+ break;
+
+ case SAPRETURN_DUPLICATE:
+ err = NO_ERROR;
+ break;
+
+ default:
+ break;
+ }
+ } while ( fContinueLoop );
+
+ RtlFreeOemString( &oemServer );
+
+ if ( err )
+ {
+ return err;
+ }
+ }
+
+ //
+ // At this point, we failed to ask Sap service to advertise the
+ // service for us. So we advertise it ourselves.
+ //
+
+ if ( !fInitSocket )
+ {
+ err = NwInitializeSocket( hEventHandle );
+ }
+
+ if ( err == NO_ERROR )
+ {
+ err = NwAdvertiseService( lpServiceInfo->lpServiceName,
+ nSapType,
+ ((LPSOCKADDR_IPX) lpServiceInfo->lpServiceAddress->Addresses[nIPX].lpAddress),
+ hEventHandle );
+
+ //
+ // Adding the service to the list will result in a resend
+ // of advertising packets every 60 seconds
+ //
+
+ if ( err == NO_ERROR )
+ {
+ err = AddServiceToList( lpServiceInfo, nSapType, FALSE, nIPX );
+ }
+ }
+
+ return err;
+}
+
+DWORD
+NwDeregisterService(
+ IN LPSERVICE_INFO lpServiceInfo,
+ IN WORD nSapType
+ )
+/*++
+
+Routine Description:
+
+ This routine deregisters the given service.
+
+Arguments:
+
+ lpServiceInfo - contains the service information
+
+ nSapType - SAP type to deregister
+
+Return Value:
+
+ Win32 error.
+
+--*/
+{
+ PREGISTERED_SERVICE pSvc;
+
+ //
+ // Check if the requested service type and name has already been registered.
+ // If yes, then return error.
+ //
+
+ pSvc = GetServiceItemFromList( nSapType, lpServiceInfo->lpServiceName );
+ if ( pSvc == NULL )
+ return ERROR_SERVICE_NOT_FOUND;
+
+ //
+ // If SAP service is advertising the service for us, ask
+ // the SAP service to stop advertising.
+ //
+
+ if ( pSvc->fAdvertiseBySap )
+ {
+ UNICODE_STRING uServer;
+ OEM_STRING oemServer;
+ NTSTATUS ntstatus;
+ INT sapRet;
+
+ RtlInitUnicodeString( &uServer, lpServiceInfo->lpServiceName );
+ ntstatus = RtlUnicodeStringToOemString( &oemServer, &uServer, TRUE );
+ if ( !NT_SUCCESS( ntstatus ) )
+ return RtlNtStatusToDosError( ntstatus );
+
+ sapRet = SapRemoveAdvertise( oemServer.Buffer, nSapType );
+ RtlFreeOemString( &oemServer );
+
+ switch ( sapRet )
+ {
+ case SAPRETURN_NOMEMORY:
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ case SAPRETURN_NOTEXIST:
+ case SAPRETURN_INVALIDNAME:
+ return ERROR_INVALID_PARAMETER;
+
+ case SAPRETURN_SUCCESS:
+ break;
+
+ // Should not have any other errors
+ default:
+ break;
+ }
+
+ }
+
+ //
+ // Remove the service item from the link list
+ //
+ RemoveServiceFromList( pSvc );
+
+ return NO_ERROR;
+}
+
+BOOL
+OldRnRCheckCancel(
+ PVOID pvArg
+ )
+/*++
+Routine Description:
+ Determine if the cancel event is signaled
+--*/
+{
+ POLDRNRSAP porns = (POLDRNRSAP)pvArg;
+
+ if(!WaitForSingleObject(porns->hCancel, 0))
+ {
+ return(TRUE);
+ }
+ return(FALSE);
+}
+
+
+DWORD
+OldRnRCheckSapData(
+ PSAP_BCAST_CONTROL psbc,
+ PSAP_IDENT_HEADER pSap,
+ PDWORD pdwErr
+ )
+{
+/*++
+Routine Description:
+ Coroutine called when a SAP reply is recevied. This checks to see
+ if the reply satisfies the request.
+Argument:
+ pvArg -- actually a pointer to an SAP_BCAST_CONTROL
+--*/
+ POLDRNRSAP porns = (POLDRNRSAP)psbc->pvArg;
+
+ if(strcmp(porns->poem->Buffer, pSap->ServerName) == 0)
+ {
+ //
+ // it matches. We are done!
+ //
+
+ *pdwErr = FillBufferWithCsAddr(pSap->Address,
+ porns->nProt,
+ porns->lpCsAddrBuffer,
+ porns->lpdwBufferLength,
+ porns->lpcAddress);
+ return(dwrcDone);
+ }
+ return(dwrcNil);
+}
+
+
+
+DWORD
+NwpGetAddressViaSap(
+ IN WORD nServiceType,
+ IN LPWSTR lpServiceName,
+ IN DWORD nProt,
+ IN OUT LPVOID lpCsAddrBuffer,
+ IN OUT LPDWORD lpdwBufferLength,
+ IN HANDLE hCancellationEvent,
+ OUT LPDWORD lpcAddress
+ )
+/*++
+
+Routine Description:
+
+ This routine uses SAP requests to find the address of the given service
+ name/type. It can handle looking up by name and type alone.
+
+Arguments:
+
+ Handle - the RnR handle, if appropriate
+
+ nServiceType - service type
+
+ lpServiceName - unique string representing the service name
+
+ lpCsAddrBuffer - on return, will be filled with CSADDR_INFO structures
+
+ lpdwBufferLength - on input, the number of bytes contained in the buffer
+ pointed to by lpCsAddrBuffer. On output, the minimum number of bytes
+ to pass for the lpCsAddrBuffer to retrieve all the requested info
+
+ hCancellationEvent - the event which signals us to cancel the request
+
+ lpcAddress - on output, the number of CSADDR_INFO structures returned
+
+Return Value:
+
+ Win32 error code.
+
+--*/
+{
+ DWORD err = NO_ERROR;
+ NTSTATUS ntstatus;
+ UNICODE_STRING UServiceName;
+ OEM_STRING OemServiceName;
+ SOCKET socketSap;
+ SAP_RNR_CONTEXT src;
+ PSAP_BCAST_CONTROL psbc = &src.u_type.sbc;
+ OLDRNRSAP ors;
+
+ *lpcAddress = 0;
+
+ _wcsupr( lpServiceName );
+ RtlInitUnicodeString( &UServiceName, lpServiceName );
+ ntstatus = RtlUnicodeStringToOemString( &OemServiceName,
+ &UServiceName,
+ TRUE );
+ if ( !NT_SUCCESS( ntstatus ))
+ return RtlNtStatusToDosError( ntstatus );
+
+ memset(&src, 0, sizeof(src));
+
+ err = SapGetSapSocket(&psbc->s);
+ if ( err )
+ {
+ RtlFreeOemString( &OemServiceName );
+ return err;
+ }
+
+ psbc->psrc = &src;
+ psbc->dwIndex = 0;
+ psbc->dwTickCount = 0;
+ psbc->pvArg = (PVOID)&ors;
+ psbc->Func = OldRnRCheckSapData;
+ psbc->fCheckCancel = OldRnRCheckCancel;
+ psbc->fFlags = 0;
+ psbc->wQueryType = QT_GENERAL_QUERY;
+
+
+
+ ors.poem = &OemServiceName;
+ ors.hCancel = hCancellationEvent,
+ ors.lpCsAddrBuffer = lpCsAddrBuffer;
+ ors.lpdwBufferLength = lpdwBufferLength;
+ ors.lpcAddress = lpcAddress;
+ ors.nProt = nProt;
+
+ err = SapGetSapForType(psbc, nServiceType);
+
+ RtlFreeOemString( &OemServiceName );
+
+ //
+ // Clean up the socket interface
+ //
+ (VOID)SapFreeSapSocket(psbc->s);
+
+ return err;
+}
+
+
+
+DWORD
+NwGetService(
+ IN LPWSTR Reserved,
+ IN WORD nSapType,
+ IN LPWSTR lpServiceName,
+ IN DWORD dwProperties,
+ OUT LPBYTE lpServiceInfo,
+ IN DWORD dwBufferLength,
+ OUT LPDWORD lpdwBytesNeeded
+ )
+/*++
+Routine Description:
+
+ This routine gets the service info.
+
+Arguments:
+
+ Reserved - unused
+
+ nSapType - SAP type
+
+ lpServiceName - service name
+
+ dwProperties - specifys the properties of the service info needed
+
+ lpServiceInfo - on output, contains the SERVICE_INFO
+
+ dwBufferLength - size of buffer pointed by lpServiceInfo
+
+ lpdwBytesNeeded - if the buffer pointed by lpServiceInfo is not large
+ enough, this will contain the bytes needed on output
+
+Return Value:
+
+ Win32 error.
+
+--*/
+{
+ DWORD err = NO_ERROR;
+ DWORD nSize = sizeof(SERVICE_INFO);
+ PREGISTERED_SERVICE pSvc;
+ PSERVICE_INFO pSvcInfo = (PSERVICE_INFO) lpServiceInfo;
+ LPBYTE pBufferStart;
+
+ UNREFERENCED_PARAMETER( Reserved );
+
+ //
+ // Check if all parameters passed in are valid
+ //
+ if ( wcslen( lpServiceName ) > SAP_OBJECT_NAME_MAX_LENGTH-1 )
+ return ERROR_INVALID_PARAMETER;
+
+ pSvc = GetServiceItemFromList( nSapType, lpServiceName );
+ if ( pSvc == NULL )
+ return ERROR_SERVICE_NOT_FOUND;
+
+ //
+ // Calculate the size needed to return the requested info
+ //
+ if ( (( dwProperties == PROP_ALL ) || ( dwProperties & PROP_COMMENT ))
+ && ( pSvc->pServiceInfo->lpComment != NULL )
+ )
+ {
+ nSize += ( wcslen( pSvc->pServiceInfo->lpComment) + 1) * sizeof(WCHAR);
+ }
+
+ if ( (( dwProperties == PROP_ALL ) || ( dwProperties & PROP_LOCALE ))
+ && ( pSvc->pServiceInfo->lpLocale != NULL )
+ )
+ {
+ nSize += ( wcslen( pSvc->pServiceInfo->lpLocale) + 1) * sizeof(WCHAR);
+ }
+
+ if ( (( dwProperties == PROP_ALL ) || ( dwProperties & PROP_MACHINE ))
+ && ( pSvc->pServiceInfo->lpMachineName != NULL )
+ )
+ {
+ nSize += ( wcslen( pSvc->pServiceInfo->lpMachineName) + 1) * sizeof(WCHAR);
+ }
+
+ if (( dwProperties == PROP_ALL ) || ( dwProperties & PROP_ADDRESSES ))
+ {
+ DWORD i;
+ DWORD dwCount = pSvc->pServiceInfo->lpServiceAddress->dwAddressCount;
+
+ nSize = ROUND_UP_COUNT( nSize, ALIGN_QUAD );
+ nSize += sizeof( SERVICE_ADDRESSES );
+ if ( dwCount > 1 )
+ nSize += ( dwCount - 1 ) * sizeof( SERVICE_ADDRESS );
+
+ for ( i = 0; i < dwCount; i++ )
+ {
+ SERVICE_ADDRESS *pAddr =
+ &(pSvc->pServiceInfo->lpServiceAddress->Addresses[i]);
+
+
+ nSize = ROUND_UP_COUNT( nSize, ALIGN_QUAD );
+ nSize += pAddr->dwAddressLength;
+ nSize = ROUND_UP_COUNT( nSize, ALIGN_QUAD );
+ nSize += pAddr->dwPrincipalLength;
+ }
+ }
+
+ if (( dwProperties == PROP_ALL ) || ( dwProperties & PROP_SD ))
+ {
+ nSize = ROUND_UP_COUNT( nSize, ALIGN_QUAD );
+ nSize += pSvc->pServiceInfo->ServiceSpecificInfo.cbSize;
+ }
+
+ //
+ // Return error if the buffer passed in is not big enough
+ //
+ if ( dwBufferLength < nSize )
+ {
+ *lpdwBytesNeeded = nSize;
+ return ERROR_INSUFFICIENT_BUFFER;
+ }
+
+ //
+ // Fill in all requested service info
+ //
+ memset( pSvcInfo, 0, sizeof(*pSvcInfo)); // Make all fields 0 i.e.
+ // all pointer fields NULL
+
+ pSvcInfo->dwDisplayHint = pSvc->pServiceInfo->dwDisplayHint;
+ pSvcInfo->dwVersion = pSvc->pServiceInfo->dwVersion;
+ pSvcInfo->dwTime = pSvc->pServiceInfo->dwTime;
+
+ pBufferStart = ((LPBYTE) pSvcInfo) + sizeof( *pSvcInfo );
+
+ if ( (( dwProperties == PROP_ALL ) || ( dwProperties & PROP_COMMENT ))
+ && ( pSvc->pServiceInfo->lpComment != NULL )
+ )
+ {
+ pSvcInfo->lpComment = (LPWSTR) pBufferStart;
+ wcscpy( pSvcInfo->lpComment, pSvc->pServiceInfo->lpComment );
+ pBufferStart += ( wcslen( pSvcInfo->lpComment ) + 1) * sizeof(WCHAR);
+
+ pSvcInfo->lpComment = (LPWSTR) ((LPBYTE) pSvcInfo->lpComment - lpServiceInfo );
+ }
+
+ if ( (( dwProperties == PROP_ALL ) || ( dwProperties & PROP_LOCALE ))
+ && ( pSvc->pServiceInfo->lpLocale != NULL )
+ )
+ {
+ pSvcInfo->lpLocale = (LPWSTR) pBufferStart;
+ wcscpy( pSvcInfo->lpLocale, pSvc->pServiceInfo->lpLocale );
+ pBufferStart += ( wcslen( pSvcInfo->lpLocale ) + 1) * sizeof(WCHAR);
+ pSvcInfo->lpLocale = (LPWSTR) ((LPBYTE) pSvcInfo->lpLocale - lpServiceInfo);
+ }
+
+ if ( (( dwProperties == PROP_ALL ) || ( dwProperties & PROP_MACHINE ))
+ && ( pSvc->pServiceInfo->lpMachineName != NULL )
+ )
+ {
+ pSvcInfo->lpMachineName = (LPWSTR) pBufferStart;
+ wcscpy( pSvcInfo->lpMachineName, pSvc->pServiceInfo->lpMachineName );
+ pBufferStart += ( wcslen( pSvcInfo->lpMachineName) + 1) * sizeof(WCHAR);
+ pSvcInfo->lpMachineName = (LPWSTR) ((LPBYTE) pSvcInfo->lpMachineName -
+ lpServiceInfo );
+ }
+
+ if (( dwProperties == PROP_ALL ) || ( dwProperties & PROP_ADDRESSES ))
+ {
+ DWORD i, dwCount, dwLen;
+
+ pBufferStart = ROUND_UP_POINTER( pBufferStart, ALIGN_QUAD );
+ pSvcInfo->lpServiceAddress = (LPSERVICE_ADDRESSES) pBufferStart;
+ dwCount = pSvcInfo->lpServiceAddress->dwAddressCount =
+ pSvc->pServiceInfo->lpServiceAddress->dwAddressCount;
+
+ pBufferStart += sizeof( SERVICE_ADDRESSES );
+
+ for ( i = 0; i < dwCount; i++ )
+ {
+ SERVICE_ADDRESS *pTmpAddr =
+ &( pSvcInfo->lpServiceAddress->Addresses[i]);
+
+ SERVICE_ADDRESS *pAddr =
+ &( pSvc->pServiceInfo->lpServiceAddress->Addresses[i]);
+
+ pTmpAddr->dwAddressType = pAddr->dwAddressType;
+ pTmpAddr->dwAddressFlags = pAddr->dwAddressFlags;
+
+ //
+ // setup Address
+ //
+ pBufferStart = ROUND_UP_POINTER( pBufferStart, ALIGN_QUAD );
+ pTmpAddr->lpAddress = (LPBYTE) ( pBufferStart - lpServiceInfo );
+ pTmpAddr->dwAddressLength = pAddr->dwAddressLength;
+ memcpy( pBufferStart, pAddr->lpAddress, pAddr->dwAddressLength );
+ pBufferStart += pAddr->dwAddressLength;
+
+ //
+ // setup Principal
+ //
+ pBufferStart = ROUND_UP_POINTER( pBufferStart, ALIGN_QUAD );
+ pTmpAddr->lpPrincipal = (LPBYTE) ( pBufferStart - lpServiceInfo );
+ pTmpAddr->dwPrincipalLength = pAddr->dwPrincipalLength;
+ memcpy(pBufferStart, pAddr->lpPrincipal, pAddr->dwPrincipalLength );
+ pBufferStart += pAddr->dwPrincipalLength;
+ }
+
+ pSvcInfo->lpServiceAddress = (LPSERVICE_ADDRESSES)
+ ((LPBYTE) pSvcInfo->lpServiceAddress - lpServiceInfo);
+ }
+
+ if (( dwProperties == PROP_ALL ) || ( dwProperties & PROP_SD ))
+ {
+ pBufferStart = ROUND_UP_POINTER( pBufferStart, ALIGN_QUAD );
+ pSvcInfo->ServiceSpecificInfo.cbSize =
+ pSvc->pServiceInfo->ServiceSpecificInfo.cbSize;
+ pSvcInfo->ServiceSpecificInfo.pBlobData = pBufferStart;
+ RtlCopyMemory( pSvcInfo->ServiceSpecificInfo.pBlobData,
+ pSvc->pServiceInfo->ServiceSpecificInfo.pBlobData,
+ pSvcInfo->ServiceSpecificInfo.cbSize );
+ pSvcInfo->ServiceSpecificInfo.pBlobData =
+ (LPBYTE) ( pSvcInfo->ServiceSpecificInfo.pBlobData - lpServiceInfo);
+ }
+
+ return NO_ERROR;
+}
+
+DWORD
+NwInitializeSocket(
+ IN HANDLE hEventHandle
+ )
+/*++
+
+Routine Description:
+
+ This routine initializes the socket needed for us to do the
+ SAP advertise ourselves.
+
+Arguments:
+
+ hEventHandle - A handle to the NwDoneEvent if this code is running in
+ the context of a service. Otherwise this code is running
+ in the context of a regular executable.
+
+Return Value:
+
+ Win32 error.
+
+--*/
+{
+ DWORD err = NO_ERROR;
+ WSADATA wsaData;
+ SOCKADDR_IPX socketAddr;
+ INT nValue;
+ HANDLE hThread;
+ DWORD dwThreadId;
+
+ if ( fInitSocket )
+ return NO_ERROR;
+
+ //
+ // Initialize the socket interface
+ //
+// err = WSAStartup( WSOCK_VER_REQD, &wsaData );
+// if ( err )
+// return err;
+
+ //
+ // Open an IPX datagram socket
+ //
+ socketSap = socket( AF_IPX, SOCK_DGRAM, NSPROTO_IPX );
+ if ( socketSap == INVALID_SOCKET )
+ return WSAGetLastError();
+
+ //
+ // Allow sending of broadcasts
+ //
+ nValue = 1;
+ if ( setsockopt( socketSap,
+ SOL_SOCKET,
+ SO_BROADCAST,
+ (PVOID) &nValue,
+ sizeof(INT)) == SOCKET_ERROR )
+ {
+ err = WSAGetLastError();
+ goto CleanExit;
+ }
+
+ //
+ // Bind the socket
+ //
+ memset( &socketAddr, 0, sizeof( SOCKADDR_IPX));
+ socketAddr.sa_family = AF_IPX;
+ socketAddr.sa_socket = 0; // no specific port
+
+ if ( bind( socketSap,
+ (PSOCKADDR) &socketAddr,
+ sizeof( SOCKADDR_IPX)) == SOCKET_ERROR )
+ {
+ err = WSAGetLastError();
+ goto CleanExit;
+ }
+
+ //
+ // Set the extended address option
+ //
+ nValue = 1;
+ if ( setsockopt( socketSap, // Socket Handle
+ NSPROTO_IPX, // Option Level
+ IPX_EXTENDED_ADDRESS, // Option Name
+ (PUCHAR)&nValue, // Ptr to on/off flag
+ sizeof(INT)) == SOCKET_ERROR ) // Length of flag
+ {
+
+ err = WSAGetLastError();
+ goto CleanExit;
+ }
+
+ //
+ // Create the thread that loops through the registered service
+ // link list and send out SAP advertise packets for each one of them
+ //
+
+ hThread = CreateThread( NULL, // no security attributes
+ 0, // default stack size
+ SapFunc, // thread function
+ hEventHandle, // argument to SapFunc
+ 0, // default creation flags
+ &dwThreadId );
+
+ if ( hThread == NULL )
+ {
+ err = GetLastError();
+ goto CleanExit;
+ }
+
+ fInitSocket = TRUE;
+
+CleanExit:
+
+ if ( err )
+ closesocket( socketSap );
+
+ return err;
+}
+
+DWORD
+NwAdvertiseService(
+ IN LPWSTR lpServiceName,
+ IN WORD nSapType,
+ IN LPSOCKADDR_IPX pAddr,
+ IN HANDLE hEventHandle
+ )
+/*++
+
+Routine Description:
+
+ This routine sends out SAP identification packets for the
+ given service name and type.
+
+Arguments:
+
+ lpServiceName - unique string representing the service name
+
+ nSapType - SAP type
+
+ pAddr - address of the service
+
+ hEventHandle - A handle to the NwDoneEvent if this code is running in
+ the context of a service. Otherwise this code is running
+ in the context of a regular executable.
+
+Return Value:
+
+ Win32 error.
+
+--*/
+{
+ NTSTATUS ntstatus;
+
+ UNICODE_STRING uServiceName;
+ OEM_STRING oemServiceName;
+
+ SAP_IDENT_HEADER_EX sapIdent;
+ UCHAR destAddr[SAP_ADDRESS_LENGTH];
+ PSOCKADDR_IPX pAddrTmp = pAddr;
+ SOCKADDR_IPX newAddr;
+ SOCKADDR_IPX bindAddr;
+ DWORD len = sizeof( SOCKADDR_IPX );
+ DWORD getsockname_rc ;
+
+ if ( !fInitSocket )
+ {
+ DWORD err = NwInitializeSocket( hEventHandle );
+ if ( err )
+ return err;
+ }
+
+ //
+ // get local addressing info. we are only interested in the net number.
+ //
+ getsockname_rc = getsockname( socketSap,
+ (PSOCKADDR) &bindAddr,
+ &len );
+
+ //
+ // Convert the service name to OEM string
+ //
+ RtlInitUnicodeString( &uServiceName, lpServiceName );
+ ntstatus = RtlUnicodeStringToOemString( &oemServiceName,
+ &uServiceName,
+ TRUE );
+ if ( !NT_SUCCESS( ntstatus ))
+ return RtlNtStatusToDosError( ntstatus );
+
+ _strupr( (LPSTR) oemServiceName.Buffer );
+
+ if ( !memcmp( pAddr->sa_netnum,
+ "\x00\x00\x00\x00",
+ IPX_ADDRESS_NETNUM_LENGTH ))
+ {
+ if ( getsockname_rc != SOCKET_ERROR )
+ {
+ // copy the ipx address to advertise
+ memcpy( &newAddr,
+ pAddr,
+ sizeof( SOCKADDR_IPX));
+
+ // replace the net number with the correct one
+ memcpy( &(newAddr.sa_netnum),
+ &(bindAddr.sa_netnum),
+ IPX_ADDRESS_NETNUM_LENGTH );
+
+ pAddrTmp = &newAddr;
+ }
+ }
+
+ //
+ // Format the SAP identification packet
+ //
+
+ sapIdent.ResponseType = htons( 2 );
+ sapIdent.ServerType = htons( nSapType );
+ memset( sapIdent.ServerName, '\0', SAP_OBJECT_NAME_MAX_LENGTH );
+ strcpy( sapIdent.ServerName, oemServiceName.Buffer );
+ RtlCopyMemory( sapIdent.Address, pAddrTmp->sa_netnum, IPX_ADDRESS_LENGTH );
+ sapIdent.HopCount = htons( 1 );
+
+ RtlFreeOemString( &oemServiceName );
+
+ //
+ // Set the address to send to
+ //
+ memcpy( destAddr, SapBroadcastAddress, SAP_ADDRESS_LENGTH );
+ if ( getsockname_rc != SOCKET_ERROR )
+ {
+ LPSOCKADDR_IPX newDestAddr = (LPSOCKADDR_IPX)destAddr ;
+
+ //
+ // replace the net number with the correct one
+ //
+ memcpy( &(newDestAddr->sa_netnum),
+ &(bindAddr.sa_netnum),
+ IPX_ADDRESS_NETNUM_LENGTH );
+
+ }
+
+ //
+ // Send the packet out
+ //
+ if ( sendto( socketSap,
+ (PVOID) &sapIdent,
+ sizeof( sapIdent ),
+ 0,
+ (PSOCKADDR) destAddr,
+ SAP_ADDRESS_LENGTH ) == SOCKET_ERROR )
+ {
+ return WSAGetLastError();
+ }
+
+ return NO_ERROR;
+}
+
+DWORD
+AddServiceToList(
+ IN LPSERVICE_INFO lpServiceInfo,
+ IN WORD nSapType,
+ IN BOOL fAdvertiseBySap,
+ IN INT nIndexIPXAddress
+ )
+/*++
+
+Routine Description:
+
+ This routine adds the service to the link list of services
+ we advertised.
+
+Arguments:
+
+ lpServiceInfo - service information
+
+ nSapType - SAP type
+
+ fAdvertiseBySap - TRUE if this service is advertised by SAP service,
+ FALSE if we are advertising ourselves.
+
+ nIndexIPXAddress - index of the ipx address
+
+Return Value:
+
+ Win32 error.
+
+--*/
+{
+ PREGISTERED_SERVICE pSvcNew;
+ PSERVICE_INFO pSI;
+ LPBYTE pBufferStart;
+ DWORD nSize = 0;
+
+ //
+ // Allocate a new entry for the service list
+ //
+
+ pSvcNew = LocalAlloc( LMEM_ZEROINIT, sizeof( REGISTERED_SERVICE ));
+ if ( pSvcNew == NULL )
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ //
+ // Calculate the size needed for the SERVICE_INFO structure
+ //
+ nSize = sizeof( *lpServiceInfo)
+ + sizeof( *(lpServiceInfo->lpServiceType));
+
+ if ( lpServiceInfo->lpServiceName != NULL )
+ nSize += ( wcslen( lpServiceInfo->lpServiceName) + 1) * sizeof(WCHAR);
+ if ( lpServiceInfo->lpComment != NULL )
+ nSize += ( wcslen( lpServiceInfo->lpComment) + 1) * sizeof(WCHAR);
+ if ( lpServiceInfo->lpLocale != NULL )
+ nSize += ( wcslen( lpServiceInfo->lpLocale) + 1) * sizeof(WCHAR);
+ if ( lpServiceInfo->lpMachineName != NULL )
+ nSize += ( wcslen( lpServiceInfo->lpMachineName) + 1) * sizeof(WCHAR);
+
+ nSize = ROUND_UP_COUNT( nSize, ALIGN_QUAD );
+
+ if ( lpServiceInfo->lpServiceAddress != NULL )
+ {
+ nSize += sizeof( SERVICE_ADDRESSES );
+ nSize = ROUND_UP_COUNT( nSize, ALIGN_QUAD );
+
+ nSize += lpServiceInfo->lpServiceAddress->Addresses[nIndexIPXAddress].dwAddressLength;
+ nSize = ROUND_UP_COUNT( nSize, ALIGN_QUAD );
+
+ nSize += lpServiceInfo->lpServiceAddress->Addresses[nIndexIPXAddress].dwPrincipalLength;
+ nSize = ROUND_UP_COUNT( nSize, ALIGN_QUAD );
+ }
+
+ nSize += lpServiceInfo->ServiceSpecificInfo.cbSize ;
+
+ //
+ // Allocate a SERVICE_INFO structure for the new list entry
+ //
+ pSI = LocalAlloc( LMEM_ZEROINIT, nSize );
+ if ( pSI == NULL )
+ {
+ LocalFree( pSvcNew );
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ //
+ // Copy the information of SERVICE_INFO into list entry
+ //
+ *pSI = *lpServiceInfo;
+
+ pBufferStart = (( (LPBYTE) pSI) + sizeof( *lpServiceInfo ));
+
+ pSI->lpServiceType = (LPGUID) pBufferStart;
+ *(pSI->lpServiceType) = *(lpServiceInfo->lpServiceType);
+ pBufferStart += sizeof( *(lpServiceInfo->lpServiceType) );
+
+ if ( lpServiceInfo->lpServiceName != NULL )
+ {
+ pSI->lpServiceName = (LPWSTR) pBufferStart;
+ wcscpy( pSI->lpServiceName, lpServiceInfo->lpServiceName );
+ _wcsupr( pSI->lpServiceName );
+ pBufferStart += ( wcslen( lpServiceInfo->lpServiceName ) + 1 )
+ * sizeof(WCHAR);
+ }
+
+ if ( lpServiceInfo->lpComment != NULL )
+ {
+ pSI->lpComment = (LPWSTR) pBufferStart;
+ wcscpy( pSI->lpComment, lpServiceInfo->lpComment );
+ pBufferStart += ( wcslen( lpServiceInfo->lpComment ) + 1 )
+ * sizeof(WCHAR);
+ }
+
+ if ( lpServiceInfo->lpLocale != NULL )
+ {
+ pSI->lpLocale = (LPWSTR) pBufferStart;
+ wcscpy( pSI->lpLocale, lpServiceInfo->lpLocale );
+ pBufferStart += ( wcslen( lpServiceInfo->lpLocale ) + 1 )
+ * sizeof(WCHAR);
+ }
+
+ if ( lpServiceInfo->lpMachineName != NULL )
+ {
+ pSI->lpMachineName = (LPWSTR) pBufferStart;
+ wcscpy( pSI->lpMachineName, lpServiceInfo->lpMachineName );
+ pBufferStart += (wcslen( lpServiceInfo->lpMachineName ) + 1)
+ * sizeof(WCHAR);
+ }
+
+ pBufferStart = ROUND_UP_POINTER( pBufferStart, ALIGN_QUAD) ;
+
+ if ( lpServiceInfo->lpServiceAddress != NULL )
+ {
+ DWORD nSize;
+
+ pSI->lpServiceAddress = (LPSERVICE_ADDRESSES) pBufferStart;
+ pSI->lpServiceAddress->dwAddressCount = 1; // Just 1 IPX address
+
+ memcpy( &(pSI->lpServiceAddress->Addresses[0]),
+ &(lpServiceInfo->lpServiceAddress->Addresses[nIndexIPXAddress]),
+ sizeof( SERVICE_ADDRESS) );
+ pBufferStart += sizeof( SERVICE_ADDRESSES);
+
+ pBufferStart = ROUND_UP_POINTER( pBufferStart, ALIGN_QUAD) ;
+ nSize = pSI->lpServiceAddress->Addresses[0].dwAddressLength;
+ pSI->lpServiceAddress->Addresses[0].lpAddress = pBufferStart;
+ memcpy( pBufferStart,
+ lpServiceInfo->lpServiceAddress->Addresses[nIndexIPXAddress].lpAddress,
+ nSize );
+ pBufferStart += nSize;
+
+ pBufferStart = ROUND_UP_POINTER( pBufferStart, ALIGN_QUAD) ;
+ nSize = pSI->lpServiceAddress->Addresses[0].dwPrincipalLength;
+ pSI->lpServiceAddress->Addresses[0].lpPrincipal = pBufferStart;
+ memcpy( pBufferStart,
+ lpServiceInfo->lpServiceAddress->Addresses[nIndexIPXAddress].lpPrincipal,
+ nSize );
+ pBufferStart += nSize;
+ pBufferStart = ROUND_UP_POINTER( pBufferStart, ALIGN_QUAD) ;
+ }
+
+ pSI->ServiceSpecificInfo.pBlobData = pBufferStart;
+ RtlCopyMemory( pSI->ServiceSpecificInfo.pBlobData,
+ lpServiceInfo->ServiceSpecificInfo.pBlobData,
+ pSI->ServiceSpecificInfo.cbSize );
+
+ //
+ // Fill in the data in the list entry
+ //
+ pSvcNew->nSapType = nSapType;
+ pSvcNew->fAdvertiseBySap = fAdvertiseBySap;
+ pSvcNew->Next = NULL;
+ pSvcNew->pServiceInfo = pSI;
+
+ //
+ // Add the newly created list entry into the service list
+ //
+ EnterCriticalSection( &NwServiceListCriticalSection );
+
+ if ( pServiceListHead == NULL )
+ pServiceListHead = pSvcNew;
+ else
+ pServiceListTail->Next = pSvcNew;
+
+ pServiceListTail = pSvcNew;
+
+ LeaveCriticalSection( &NwServiceListCriticalSection );
+
+ return NO_ERROR;
+}
+
+VOID
+RemoveServiceFromList(
+ PREGISTERED_SERVICE pSvc
+ )
+/*++
+
+Routine Description:
+
+ This routine removes the service from the link list of services
+ we advertised.
+
+Arguments:
+
+ pSvc - the registered service node to remove
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PREGISTERED_SERVICE pCur, pPrev;
+
+ EnterCriticalSection( &NwServiceListCriticalSection );
+
+ for ( pCur = pServiceListHead, pPrev = NULL ; pCur != NULL;
+ pPrev = pCur, pCur = pCur->Next )
+ {
+ if ( pCur == pSvc )
+ {
+ if ( pPrev == NULL ) // i.e. pCur == pSvc == pServiceListHead
+ {
+ pServiceListHead = pSvc->Next;
+ if ( pServiceListTail == pSvc )
+ pServiceListTail = NULL;
+ }
+ else
+ {
+ pPrev->Next = pSvc->Next;
+ if ( pServiceListTail == pSvc )
+ pServiceListTail = pPrev;
+ }
+
+ (VOID) LocalFree( pCur->pServiceInfo );
+ (VOID) LocalFree( pCur );
+ break;
+ }
+ }
+
+ LeaveCriticalSection( &NwServiceListCriticalSection );
+}
+
+PREGISTERED_SERVICE
+GetServiceItemFromList(
+ IN WORD nSapType,
+ IN LPWSTR pServiceName
+ )
+/*++
+
+Routine Description:
+
+ This routine returns the registered service node with the given
+ service name and type.
+
+Arguments:
+
+ nSapType - SAP type
+
+ pServiceName - service name
+
+Return Value:
+
+ Returns the pointer to the registered service node,
+ NULL if we cannot find the service type/name.
+
+--*/
+{
+ PREGISTERED_SERVICE pSvc;
+
+ EnterCriticalSection( &NwServiceListCriticalSection );
+
+ for ( pSvc = pServiceListHead; pSvc != NULL; pSvc = pSvc->Next )
+ {
+ if ( ( pSvc->nSapType == nSapType )
+ && ( _wcsicmp( pSvc->pServiceInfo->lpServiceName, pServiceName ) == 0)
+ )
+ {
+ LeaveCriticalSection( &NwServiceListCriticalSection );
+
+ return pSvc;
+ }
+ }
+
+ LeaveCriticalSection( &NwServiceListCriticalSection );
+ return NULL;
+}
+
+DWORD
+SapFunc(
+ HANDLE hEventHandle
+ )
+/*++
+
+Routine Description:
+
+ This routine is a separate thread that wakes up every 60 seconds
+ and advertise all the service contained in the service link list
+ that are not advertised by the SAP service.
+
+Arguments:
+
+ hEventHandle - used to notify thread that server is stopping
+
+Return Value:
+
+ Win32 error.
+
+--*/
+{
+ DWORD err = NO_ERROR;
+
+ //
+ // This thread loops until the service is shut down or when some error
+ // occurred in WaitForSingleObject
+ //
+
+ while ( TRUE )
+ {
+ DWORD rc;
+
+ if ( hEventHandle != NULL )
+ {
+ rc = WaitForSingleObject( hEventHandle, SAP_ADVERTISE_FREQUENCY );
+ }
+ else
+ {
+ Sleep( SAP_ADVERTISE_FREQUENCY );
+ rc = WAIT_TIMEOUT;
+ }
+
+ if ( rc == WAIT_FAILED )
+ {
+ err = GetLastError();
+ break;
+ }
+ else if ( rc == WAIT_OBJECT_0 )
+ {
+ //
+ // The service is stopping, break out of the loop and
+ // return, thus terminating the thread
+ //
+ break;
+ }
+ else if ( rc == WAIT_TIMEOUT )
+ {
+ PREGISTERED_SERVICE pSvc;
+ SOCKADDR_IPX bindAddr;
+ DWORD fGetAddr;
+
+ fGetAddr = FALSE;
+
+ //
+ // Time out occurred, time to send the SAP advertise packets
+ //
+
+ EnterCriticalSection( &NwServiceListCriticalSection );
+
+ if ( pServiceListHead == NULL )
+ {
+ LeaveCriticalSection( &NwServiceListCriticalSection );
+
+ //
+ // Clean up the SAP interface
+ //
+ (VOID) SapLibShutdown();
+
+ //
+ // Clean up the socket interface
+ //
+ if ( fInitSocket )
+ {
+ closesocket( socketSap );
+// (VOID) WSACleanup();
+ }
+
+ break;
+ }
+
+ for ( pSvc = pServiceListHead; pSvc != NULL; pSvc = pSvc->Next )
+ {
+ if ( !pSvc->fAdvertiseBySap )
+ {
+ //
+ // Ignore the error since we can't return
+ // nor pop up the error
+ //
+
+ SOCKADDR_IPX *pAddr = (SOCKADDR_IPX *)
+ pSvc->pServiceInfo->lpServiceAddress->Addresses[0].lpAddress;
+ SOCKADDR_IPX *pAddrToAdvertise = pAddr;
+ SOCKADDR_IPX newAddr;
+
+ if ( !memcmp( pAddr->sa_netnum,
+ "\x00\x00\x00\x00",
+ IPX_ADDRESS_NETNUM_LENGTH ))
+ {
+
+ if ( !fGetAddr )
+ {
+ DWORD len = sizeof( SOCKADDR_IPX );
+
+ rc = getsockname( socketSap,
+ (PSOCKADDR) &bindAddr,
+ &len );
+
+ if ( rc != SOCKET_ERROR )
+ fGetAddr = TRUE;
+ }
+
+ if ( fGetAddr )
+ {
+ // copy the ipx address to advertise
+ memcpy( &newAddr,
+ pAddr,
+ sizeof( SOCKADDR_IPX));
+
+ // replace the net number with the correct one
+ memcpy( &(newAddr.sa_netnum),
+ &(bindAddr.sa_netnum),
+ IPX_ADDRESS_NETNUM_LENGTH );
+
+ pAddr = &newAddr;
+ }
+ }
+
+ (VOID) NwAdvertiseService(
+ pSvc->pServiceInfo->lpServiceName,
+ pSvc->nSapType,
+ pAddr,
+ hEventHandle );
+ }
+ }
+
+ LeaveCriticalSection( &NwServiceListCriticalSection );
+ }
+ }
+
+ return err;
+}
diff --git a/private/nw/svcdlls/nwwks/client/gtadrnr.c b/private/nw/svcdlls/nwwks/client/gtadrnr.c
new file mode 100644
index 000000000..7b2227c11
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/gtadrnr.c
@@ -0,0 +1,1935 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ gtsadrnr.c
+
+Abstract:
+
+ This module contains the code to support the new RnR APIs. Some
+ support code is in getaddr.c, but the interface routines and
+ routines specific to RnR2 are herein.
+
+Author:
+
+ ArnoldM 4-Dec-1995
+Revision History:
+
+ ArnoldM Created
+
+--*/
+
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include "ncp.h"
+#include <rpc.h>
+#include <winsock2.h>
+#include <ws2spi.h>
+#include <wsipx.h>
+#include <rnrdefs.h>
+#include <svcguid.h>
+#include <rnraddrs.h>
+
+
+//-------------------------------------------------------------------//
+// //
+// Globals in getaddr.c and provider .c //
+// //
+//-------------------------------------------------------------------//
+
+DWORD
+NwpGetAddressByName(
+ IN LPWSTR Reserved,
+ IN WORD nServiceType,
+ IN LPWSTR lpServiceName,
+ IN OUT LPSOCKADDR_IPX lpsockaddr
+);
+
+DWORD
+NwpGetAddressViaSap(
+ IN WORD nServiceType,
+ IN LPWSTR lpServiceName,
+ IN DWORD nProt,
+ IN OUT LPVOID lpCsAddrBuffer,
+ IN OUT LPDWORD lpdwBufferLength,
+ IN HANDLE hCancellationEvent,
+ OUT LPDWORD lpcAddress
+);
+
+DWORD
+NwpRnR2AddServiceType(
+ IN LPWSTR lpServiceTypeName,
+ IN LPGUID lpClassType,
+ IN WORD wSapId,
+ IN WORD wPort
+);
+
+
+DWORD
+SapFreeSapSocket(
+ SOCKET s
+ );
+
+PSAP_RNR_CONTEXT
+SapMakeContext
+ (
+ IN HANDLE Handle,
+ DWORD dwExcess
+ );
+
+BOOL
+NwpLookupSapInRegistry(
+ IN LPGUID lpServiceType,
+ OUT PWORD pnSapType,
+ OUT PWORD pwPort,
+ IN OUT PDWORD pfConnectionOriented
+);
+
+PSAP_RNR_CONTEXT
+SapGetContext(
+ HANDLE h
+ );
+
+DWORD
+DoASap(
+ IN PSAP_RNR_CONTEXT psrcContext,
+ IN WORD QueryType,
+ IN PBYTE * ppData,
+ IN LPWSAQUERYSETW lpqsResults,
+ IN PLONG pl,
+ IN PDWORD pdw
+ );
+
+VOID
+SapReleaseContext(
+ IN PSAP_RNR_CONTEXT psrcContext
+ );
+
+DWORD
+SapGetSapSocket(
+ SOCKET * ps
+ );
+
+DWORD
+PrepareForSap(
+ IN PSAP_RNR_CONTEXT psrc
+ );
+
+DWORD
+SapGetSapForType(
+ PSAP_BCAST_CONTROL psbc,
+ WORD nServiceType
+ );
+
+DWORD
+FillBufferWithCsAddr(
+ IN LPBYTE pAddress,
+ IN DWORD nProt,
+ IN OUT LPVOID lpCsAddrBuffer,
+ IN OUT LPDWORD lpdwBufferLength,
+ OUT LPDWORD pcAddress
+ );
+
+DWORD
+pSapSetService2(
+ IN DWORD dwOperation,
+ IN LPWSTR lpszServiceInstanceName,
+ IN PBYTE pbAddress,
+ IN LPGUID pServiceType,
+ IN WORD nServiceType
+ );
+
+DWORD
+NwpGetRnRAddress(
+ PHANDLE phServer,
+ PWCHAR pwszContext,
+ PLONG plIndex,
+ LPWSTR lpServiceName,
+ WORD nServiceType,
+ PDWORD pdwVersion,
+ DWORD dwInSize,
+ LPWSTR OutName,
+ SOCKADDR_IPX * pSockAddr
+ );
+
+
+DWORD
+NwpSetClassInfo(
+ IN LPWSTR lpwszClassName,
+ IN LPGUID lpClassId,
+ IN PCHAR lpbProp
+ );
+
+VOID
+pFreeAllContexts();
+
+extern DWORD oldRnRServiceRegister, oldRnRServiceDeRegister;
+//-------------------------------------------------------------------//
+// //
+// Local Function Prototypes //
+// //
+//-------------------------------------------------------------------//
+
+INT WINAPI
+pGetServiceClassInfo(
+ IN LPGUID lpProviderId,
+ IN OUT LPDWORD lpdwBufSize,
+ IN OUT LPWSASERVICECLASSINFOW lpServiceClassInfo,
+ IN PBOOL pfAdvert
+);
+
+DWORD
+NwpSetClassInfoAdvertise(
+ IN LPGUID lpClassType,
+ IN WORD wSapId
+);
+
+BOOL
+NSPpCheckCancel(
+ PVOID pvArg
+ );
+
+DWORD
+NSPpGotSap(
+ PSAP_BCAST_CONTROL psbc,
+ PSAP_IDENT_HEADER pSap,
+ PDWORD pdwErr
+ );
+
+INT WINAPI
+NSPLookupServiceBegin(
+ IN LPGUID lpProviderId,
+ IN LPWSAQUERYSETW lpqsRestrictions,
+ IN LPWSASERVICECLASSINFOW lpServiceClassInfo,
+ IN DWORD dwControlFlags,
+ OUT LPHANDLE lphLookup
+ );
+
+INT WINAPI
+NSPLookupServiceNext(
+ IN HANDLE hLookup,
+ IN DWORD dwControlFlags,
+ IN OUT LPDWORD lpdwBufferLength,
+ OUT LPWSAQUERYSETW lpqsResults
+ );
+
+INT WINAPI
+NSPUnInstallNameSpace(
+ IN LPGUID lpProviderId
+ );
+
+INT WINAPI
+NSPCleanup(
+ IN LPGUID lpProviderId
+ );
+
+INT WINAPI
+NSPLookupServiceEnd(
+ IN HANDLE hLookup
+ );
+
+INT WINAPI
+NSPSetService(
+ IN LPGUID lpProviderId,
+ IN LPWSASERVICECLASSINFOW lpServiceClassInfo,
+ IN LPWSAQUERYSETW lpqsRegInfo,
+ IN WSAESETSERVICEOP essOperation,
+ IN DWORD dwControlFlags
+ );
+
+INT WINAPI
+NSPInstallServiceClass(
+ IN LPGUID lpProviderId,
+ IN LPWSASERVICECLASSINFOW lpServiceClassInfo
+ );
+
+INT WINAPI
+NSPRemoveServiceClass(
+ IN LPGUID lpProviderId,
+ IN LPGUID lpServiceCallId
+ );
+
+INT WINAPI
+NSPGetServiceClassInfo(
+ IN LPGUID lpProviderId,
+ IN OUT LPDWORD lpdwBufSize,
+ IN OUT LPWSASERVICECLASSINFOW lpServiceClassInfo
+ );
+//-------------------------------------------------------------------//
+// //
+// Data definitions //
+// //
+//-------------------------------------------------------------------//
+
+NSP_ROUTINE nsrVector = {
+ sizeof(NSP_ROUTINE),
+ 1, // major version
+ 1, // minor version
+ NSPCleanup,
+ NSPLookupServiceBegin,
+ NSPLookupServiceNext,
+ NSPLookupServiceEnd,
+ NSPSetService,
+ NSPInstallServiceClass,
+ NSPRemoveServiceClass,
+ NSPGetServiceClassInfo
+ };
+
+static
+GUID HostnameGuid = SVCID_HOSTNAME;
+
+static
+GUID InetHostname = SVCID_INET_HOSTADDRBYNAME;
+
+static
+GUID AddressGuid = SVCID_INET_HOSTADDRBYINETSTRING;
+
+static
+GUID IANAGuid = SVCID_INET_SERVICEBYNAME;
+
+#define GuidEqual(x,y) RtlEqualMemory(x, y, sizeof(GUID))
+
+
+//------------------------------------------------------------------//
+// //
+// Function Bodies //
+// //
+//------------------------------------------------------------------//
+
+DWORD
+NwpSetClassInfoAdvertise(
+ IN LPGUID lpClassType,
+ IN WORD wSapId
+)
+{
+/*++
+Routine Description:
+ Start a class info advertise. Called upon a SetService call
+--*/
+ PWCHAR pwszUuid;
+ SOCKADDR_IPX sock;
+
+ UuidToString(lpClassType, &pwszUuid);
+
+ memset(&sock, 0, sizeof(sock));
+
+ *(PWORD)&sock.sa_netnum = htons(wSapId); // encode the ID here
+
+ return(pSapSetService2( oldRnRServiceRegister,
+ pwszUuid,
+ (PBYTE)&sock,
+ lpClassType,
+ RNRCLASSSAPTYPE));
+}
+
+DWORD
+pRnRReturnString(
+ IN PWCHAR pwszSrc,
+ IN OUT PBYTE *ppData,
+ IN OUT PLONG plBytes
+ )
+{
+/*++
+Routine Description:
+ Utility routine to pack a string into the spare buffer space,
+ update pointers and counts. If the string fits, it is copied. If
+ not, the pointer and count are still updated so the caller can
+ compute the additional space needed
+--*/
+
+ PBYTE pSave = *ppData;
+ LONG lLen = (wcslen(pwszSrc) + 1) * sizeof(WCHAR);
+
+ *ppData = pSave + lLen; // update the buffer pointer
+
+ *plBytes = *plBytes - lLen; // and the count
+
+ if(*plBytes >= 0)
+ {
+ //
+ // it fits.
+ //
+
+ RtlMoveMemory(pSave,
+ pwszSrc,
+ lLen);
+ return(NO_ERROR);
+ }
+ return(WSAEFAULT);
+}
+
+DWORD
+pLocateSapIdAndProtocls(
+ IN LPGUID lpClassInfoType,
+ IN DWORD dwNumClassInfo,
+ IN LPWSANSCLASSINFOW lpClassInfoBuf,
+ OUT PWORD pwSapId,
+ OUT PDWORD pdwnProt
+ )
+/*++
+Routine Description:
+ Locate the SAP ID entry and return it. Also return
+ the protocols support.
+--*/
+{
+ DWORD err = NO_ERROR;
+
+ if(GuidEqual(lpClassInfoType, &HostnameGuid)
+ ||
+ GuidEqual(lpClassInfoType, &InetHostname) )
+ {
+ *pwSapId = 0x4;
+ }
+ else if(IS_SVCID_NETWARE(lpClassInfoType))
+ {
+ *pwSapId = SAPID_FROM_SVCID_NETWARE(lpClassInfoType);
+ }
+ else
+ {
+ for(; dwNumClassInfo; dwNumClassInfo--, lpClassInfoBuf++)
+ {
+ //
+ // We have some class info data. Rummage through it
+ // looking for what we want
+ //
+
+ if(!_wcsicmp(SERVICE_TYPE_VALUE_SAPIDW, lpClassInfoBuf->lpszName)
+ &&
+ (lpClassInfoBuf->dwValueType == REG_DWORD)
+ &&
+ (lpClassInfoBuf->dwValueSize >= sizeof(WORD)))
+ {
+ //
+ // got it
+ //
+
+ *pwSapId = *(PWORD)lpClassInfoBuf->lpValue;
+ break;
+
+ }
+ }
+ if(!dwNumClassInfo)
+ {
+ err = WSA_INVALID_PARAMETER;
+ }
+ }
+ *pdwnProt = SPX_BIT | SPXII_BIT | IPX_BIT;
+ return(err);
+}
+DWORD
+pRnRReturnResults(
+ IN PWCHAR pwszString,
+ IN LPGUID pgdServiceClass,
+ IN DWORD dwVersion,
+ IN OUT PBYTE *ppData,
+ IN OUT PLONG plBytes,
+ IN PBYTE lpSockAddr,
+ IN DWORD nProt,
+ IN DWORD dwControlFlags,
+ OUT LPWSAQUERYSETW lpqsResults
+ )
+{
+/*++
+Routine Description:
+ Return the requested results
+--*/
+ DWORD err;
+
+ lpqsResults->dwNameSpace = NS_SAP;
+
+ if(dwControlFlags & LUP_RETURN_TYPE)
+ {
+
+ lpqsResults->lpServiceClassId = (LPGUID)*ppData;
+ *ppData += sizeof(GUID);
+ *plBytes -= sizeof(GUID);
+ if(*plBytes >= 0)
+ {
+ *lpqsResults->lpServiceClassId = *pgdServiceClass;
+ }
+ }
+
+ if(dwVersion
+ &&
+ (dwControlFlags & LUP_RETURN_VERSION) )
+ {
+ //
+ // have a verion, and the caller wants it
+ //
+
+ lpqsResults->lpVersion = (LPWSAVERSION)*ppData;
+ *ppData += sizeof(WSAVERSION);
+ *plBytes -= sizeof(WSAVERSION);
+ if(*plBytes >= 0)
+ {
+ //
+ // and it fits. So return it
+ //
+ lpqsResults->lpVersion->dwVersion = dwVersion;
+ lpqsResults->lpVersion->ecHow = COMP_EQUAL;
+ }
+ }
+
+ if(dwControlFlags & LUP_RETURN_ADDR)
+ {
+ DWORD dwCsAddrLen;
+
+ if(*plBytes >= 0)
+ {
+ dwCsAddrLen = (DWORD)*plBytes; // all of it for now
+ }
+ else
+ {
+ dwCsAddrLen = 0;
+ }
+ lpqsResults->lpcsaBuffer = (PVOID)*ppData;
+
+ err = FillBufferWithCsAddr(
+ lpSockAddr,
+ nProt,
+ (PVOID)lpqsResults->lpcsaBuffer,
+ &dwCsAddrLen,
+ &lpqsResults->dwNumberOfCsAddrs);
+
+ //
+ // see if it fit. Whether it did or not, compute the space available,
+ // align, and do the rest
+ //
+
+
+ if(err == NO_ERROR)
+ {
+ //
+ // if it worked, we have to compute the space taken
+ //
+
+ dwCsAddrLen = lpqsResults->dwNumberOfCsAddrs * (sizeof(CSADDR_INFO) +
+ 2*sizeof(SOCKADDR_IPX));
+ }
+ else if(err == ERROR_INSUFFICIENT_BUFFER)
+ {
+ err = WSAEFAULT;
+ }
+
+ *plBytes = *plBytes - dwCsAddrLen;
+
+ *ppData = *ppData + dwCsAddrLen;
+ }
+ else
+ {
+ err = NO_ERROR;
+ }
+
+ //
+ // no padding needed.
+
+ if((dwControlFlags & LUP_RETURN_NAME))
+ {
+ lpqsResults->lpszServiceInstanceName = (LPWSTR)*ppData;
+ err = pRnRReturnString(
+ pwszString,
+ ppData,
+ plBytes);
+ }
+ if(pgdServiceClass)
+ {
+ //
+ // BUGBUG. Do we really return this?
+ //
+ }
+ return(err);
+}
+
+INT WINAPI
+NSPLookupServiceBegin(
+ IN LPGUID lpProviderId,
+ IN LPWSAQUERYSETW lpqsRestrictions,
+ IN LPWSASERVICECLASSINFOW lpServiceClassInfo,
+ IN DWORD dwControlFlags,
+ OUT LPHANDLE lphLookup
+ )
+/*++
+Routine Description:
+ This is the RnR routine that begins a lookup.
+--*/
+{
+ PSAP_RNR_CONTEXT psrc;
+ int err;
+ DWORD nProt, nProt1;
+ OEM_STRING OemServiceName;
+ LPWSTR pwszContext;
+ WORD wSapid;
+ DWORD dwNumClassInfo;
+ LPWSANSCLASSINFOW lpClassInfoBuf;
+
+ if(lpqsRestrictions->dwSize < sizeof(WSAQUERYSETW))
+ {
+ SetLastError(WSAEFAULT);
+ return(SOCKET_ERROR);
+ }
+
+ if(lpqsRestrictions->lpszContext
+ &&
+ (lpqsRestrictions->lpszContext[0] != 0)
+ &&
+ wcscmp(&lpqsRestrictions->lpszContext[0], L"\\") )
+ {
+ //
+ // if not the default context, we must copy it.
+ //
+ pwszContext = lpqsRestrictions->lpszContext;
+ }
+ else
+ {
+ //
+ // map all default contexts into "no context".
+ //
+ pwszContext = 0;
+ }
+
+ //
+ // Compute protocols to return, or return them all
+ //
+ if(lpqsRestrictions->lpafpProtocols)
+ {
+ //
+ // Make certain at least one IPX/SPX protocol is being requested
+ //
+
+ INT i;
+ DWORD dwNum = lpqsRestrictions->dwNumberOfProtocols;
+
+ nProt = 0;
+
+ for(i = 0; dwNum--;)
+ {
+
+ if((lpqsRestrictions->lpafpProtocols[i].iAddressFamily == AF_IPX)
+ ||
+ (lpqsRestrictions->lpafpProtocols[i].iAddressFamily == AF_UNSPEC)
+ )
+ {
+ switch(lpqsRestrictions->lpafpProtocols[i].iProtocol)
+ {
+ case NSPROTO_IPX:
+ nProt |= IPX_BIT;
+ break;
+ case NSPROTO_SPX:
+ nProt |= SPX_BIT;
+ break;
+ case NSPROTO_SPXII:
+ nProt |= SPXII_BIT;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ if(!nProt)
+ {
+ //
+ // if the caller doesn't want IPX/SPX addresses, why bother?
+ //
+ SetLastError(WSANO_DATA);
+ return(SOCKET_ERROR);
+ }
+ }
+ else
+ {
+ nProt = IPX_BIT | SPX_BIT | SPXII_BIT;
+ }
+
+ if(dwControlFlags & LUP_CONTAINERS)
+ {
+ if(pwszContext)
+ {
+BadGuid:
+ SetLastError(WSANO_DATA);
+ return(SOCKET_ERROR); // can't handle containers in containers
+ }
+ wSapid = 0x4;
+ nProt1 = IPX_BIT;
+ err = NO_ERROR;
+ }
+ else
+ {
+
+ LPGUID pgClass = lpqsRestrictions->lpServiceClassId;
+
+ if(!pgClass
+ ||
+ GuidEqual(pgClass, &AddressGuid)
+ ||
+ GuidEqual(pgClass, &IANAGuid) )
+ {
+ goto BadGuid;
+ }
+
+ if(!lpqsRestrictions->lpszServiceInstanceName
+ ||
+ !*lpqsRestrictions->lpszServiceInstanceName)
+ {
+ SetLastError(WSA_INVALID_PARAMETER);
+ return(SOCKET_ERROR);
+ }
+ if(!lpServiceClassInfo)
+ {
+ dwNumClassInfo = 0;
+ }
+ else
+ {
+ dwNumClassInfo = lpServiceClassInfo->dwCount;
+ lpClassInfoBuf = lpServiceClassInfo->lpClassInfos;
+ }
+
+ err = pLocateSapIdAndProtocls(pgClass,
+ dwNumClassInfo,
+ lpClassInfoBuf,
+ &wSapid,
+ &nProt1);
+ if(err)
+ {
+ if(dwNumClassInfo)
+ {
+ SetLastError(err);
+ return(SOCKET_ERROR);
+ }
+ else
+ {
+ nProt1 = nProt;
+ wSapid = 0;
+ err = 0;
+ }
+ }
+ }
+
+ nProt &= nProt1; // the relevant protocols
+
+ if(!nProt)
+ {
+ SetLastError(WSANO_DATA);
+ return(SOCKET_ERROR);
+ }
+
+ //
+ // Make sure a class ID is given since we copy it
+ //
+
+ if(!lpqsRestrictions->lpServiceClassId)
+ {
+ //
+ // not. So, fail
+ //
+ SetLastError(WSA_INVALID_PARAMETER);
+ return(SOCKET_ERROR);
+ }
+
+ //
+ // It looks like a query we can handle. Make a context
+ // BUGBUG. What about version????
+ //
+
+ psrc = SapMakeContext(0,
+ sizeof(WSAVERSION) - sizeof(PVOID));
+
+ if(!psrc)
+ {
+ SetLastError(WSA_NOT_ENOUGH_MEMORY);
+ return(SOCKET_ERROR);
+ }
+
+ //
+ // save things
+ //
+
+ psrc->gdType = *lpqsRestrictions->lpServiceClassId;
+ psrc->dwControlFlags = dwControlFlags; // save for Next processing
+ psrc->wSapId = wSapid;
+
+ if(pwszContext)
+ {
+ wcscpy(psrc->wszContext, pwszContext);
+ }
+
+ //
+ // save the relevant restrictions
+ // if the name is a wild-card, don't copy it. A NULL name
+ // serves as a wild-card to NSPLookupServiceNext
+ //
+
+ if(lpqsRestrictions->lpszServiceInstanceName
+ &&
+ *lpqsRestrictions->lpszServiceInstanceName
+ &&
+ wcscmp(lpqsRestrictions->lpszServiceInstanceName, L"*"))
+ {
+ DWORD dwLen = wcslen(lpqsRestrictions->lpszServiceInstanceName);
+ if(dwLen > 48)
+ {
+ err = WSA_INVALID_PARAMETER;
+ goto Done;
+ }
+ else
+ {
+ RtlMoveMemory(psrc->chwName,
+ lpqsRestrictions->lpszServiceInstanceName,
+ dwLen * sizeof(WCHAR));
+ _wcsupr(psrc->chwName);
+ }
+ }
+
+ psrc->fConnectionOriented = (DWORD) -1;
+
+ *lphLookup = (HANDLE)psrc;
+ psrc->nProt = nProt;
+ psrc->gdProvider = *lpProviderId;
+ if(lpqsRestrictions->lpVersion)
+ {
+ *(LPWSAVERSION)&psrc->pvVersion = *lpqsRestrictions->lpVersion;
+ }
+
+Done:
+ SapReleaseContext(psrc);
+ if(err != NO_ERROR)
+ {
+ SapReleaseContext(psrc);
+ SetLastError(err);
+ err = SOCKET_ERROR;
+ }
+ return(err);
+}
+
+INT WINAPI
+NSPLookupServiceNext(
+ IN HANDLE hLookup,
+ IN DWORD dwControlFlags,
+ IN OUT LPDWORD lpdwBufferLength,
+ OUT LPWSAQUERYSETW lpqsResults
+ )
+/*++
+Routine Description:
+ The continuation of the LookupServiceBegin. Called to fetch
+ a matching item.
+Arguments:
+ See RnR spec
+--*/
+{
+ DWORD err = NO_ERROR;
+ PSAP_RNR_CONTEXT psrc;
+ SOCKADDR_IPX SockAddr;
+ WCHAR OutName[48];
+ PBYTE pData = (PBYTE)(lpqsResults + 1);
+ LONG lSpare;
+ LONG lLastIndex;
+ DWORD dwVersion;
+ WSAQUERYSETW wsaqDummy;
+ BOOL fDoStateMachine;
+
+ if(*lpdwBufferLength < sizeof(WSAQUERYSETW))
+ {
+ lpqsResults = &wsaqDummy;
+ }
+ lSpare = (LONG)*lpdwBufferLength - sizeof(WSAQUERYSETW);
+
+ memset(lpqsResults, 0, sizeof(WSAQUERYSETW));
+ lpqsResults->dwNameSpace = NS_SAP;
+ lpqsResults->dwSize = sizeof(WSAQUERYSETW);
+ psrc = SapGetContext(hLookup);
+ if(!psrc)
+ {
+
+ SetLastError(WSA_INVALID_HANDLE);
+ return(SOCKET_ERROR);
+ }
+
+ //
+ // This is a valid context. Determine whether this is the first
+ // call to this. If so, we need to determine whether to
+ // get the information from the bindery or by using SAP.
+ //
+
+ //
+ // make sure we have the class info info
+ //
+
+ if(!psrc->wSapId)
+ {
+ //
+ // Need to get it
+ //
+
+ UCHAR Buffer[1000];
+ LPWSASERVICECLASSINFOW lpcli = (LPWSASERVICECLASSINFOW)Buffer;
+ DWORD dwBufSize;
+ DWORD nProt1;
+
+ dwBufSize = 1000;
+ lpcli->lpServiceClassId = &psrc->gdType;
+
+ if( (err = NSPGetServiceClassInfo(&psrc->gdProvider,
+ &dwBufSize,
+ lpcli)) != NO_ERROR)
+ {
+ goto DoneNext;
+ }
+
+ err = pLocateSapIdAndProtocls(&psrc->gdType,
+ lpcli->dwCount,
+ lpcli->lpClassInfos,
+ &psrc->wSapId,
+ &nProt1);
+ if(err)
+ {
+ SetLastError(err);
+ goto DoneNext;
+ }
+
+ psrc->nProt &= nProt1;
+ if(!psrc->nProt)
+ {
+ //
+ // no protocols match
+ //
+
+ err = WSANO_DATA;
+ goto DoneNext;
+ }
+ }
+
+ //
+ // this is the state machine for querying. It selects the bindery or
+ // SAP as appropriate.
+ //
+
+
+ fDoStateMachine = TRUE; // enter the machine
+
+ do
+ {
+ //
+ // switch on the current machine state.
+ //
+ switch(psrc->dwUnionType)
+ {
+
+ case LOOKUP_TYPE_NIL:
+ psrc->u_type.bc.lIndex = -1;
+ psrc->dwUnionType = LOOKUP_TYPE_BINDERY;
+ break; // reenter state machine
+
+ case LOOKUP_TYPE_BINDERY:
+
+ //
+ // try the bindery
+ //
+
+
+ if(psrc->dwControlFlags & LUP_NEAREST)
+ {
+ err = NO_DATA; // skip the bindery
+ }
+ else
+ {
+ //
+ // otherwise, try the bindery
+ //
+
+
+ EnterCriticalSection(&psrc->u_type.sbc.csMonitor);
+
+ lLastIndex = psrc->u_type.bc.lIndex; // save it
+
+
+ err = NwpGetRnRAddress(
+ &psrc->hServer,
+ (psrc->wszContext[0] ?
+ &psrc->wszContext[0] :
+ 0),
+ &psrc->u_type.bc.lIndex,
+ (psrc->chwName[0] ?
+ psrc->chwName :
+ 0),
+ psrc->wSapId,
+ &dwVersion,
+ 48 * sizeof(WCHAR),
+ OutName,
+ &SockAddr);
+
+ LeaveCriticalSection(&psrc->u_type.sbc.csMonitor);
+ }
+
+ if(err != NO_ERROR)
+ {
+
+ if((psrc->u_type.bc.lIndex == -1))
+ {
+ err = PrepareForSap(psrc);
+ if(err)
+ {
+ //
+ // if we can't, exit the state machine
+ //
+ fDoStateMachine = FALSE;
+ }
+ }
+ else
+ {
+ //
+ // no more bindery entries. We will leave the state machine
+ //
+
+ if(err == ERROR_NO_MORE_ITEMS)
+ {
+ err = WSA_E_NO_MORE;
+ }
+ fDoStateMachine = FALSE;
+ }
+ break;
+ }
+ else
+ {
+ LPWSAVERSION lpVersion = (LPWSAVERSION)&psrc->pvVersion;
+
+ if(lpVersion->dwVersion && dwVersion)
+ {
+ //
+ // need to checkout for version matching
+ //
+
+ switch(lpVersion->ecHow)
+ {
+ case COMP_EQUAL:
+ if(lpVersion->dwVersion != dwVersion)
+ {
+ continue; //reenter machine
+ }
+ break;
+
+ case COMP_NOTLESS:
+ if(lpVersion->dwVersion > dwVersion)
+ {
+ continue;
+ }
+ break;
+
+ default:
+ continue; // error. If we don't
+ // know how to compare, we
+ // must reject it.
+ }
+ }
+
+ //
+ // have a suitable item.
+ // return the name and type and all
+ // that
+
+ err = pRnRReturnResults(
+ OutName,
+ &psrc->gdType,
+ dwVersion,
+ &pData,
+ &lSpare,
+ (PBYTE)SockAddr.sa_netnum,
+ psrc->nProt,
+ psrc->dwControlFlags,
+ lpqsResults);
+
+ if(err == WSAEFAULT)
+ {
+ //
+ // no room. Return buffer size required and
+ // restore the index
+ //
+
+ *lpdwBufferLength =
+ (DWORD)((LONG)*lpdwBufferLength - lSpare);
+ psrc->u_type.bc.lIndex = lLastIndex;
+
+ }
+ fDoStateMachine = FALSE;
+ }
+ break;
+
+ case LOOKUP_TYPE_SAP:
+
+ //
+ // Use SAP.
+ //
+
+ {
+ WORD QueryType;
+
+ if(psrc->dwControlFlags & LUP_NEAREST)
+ {
+ QueryType = QT_NEAREST_QUERY;
+ }
+ else
+ {
+ QueryType = QT_GENERAL_QUERY;
+ }
+
+ err = DoASap(psrc,
+ QueryType,
+ &pData,
+ lpqsResults,
+ &lSpare,
+ lpdwBufferLength);
+
+ if((err == WSA_E_NO_MORE)
+ &&
+ !(psrc->fFlags & SAP_F_END_CALLED)
+ &&
+ (QueryType == QT_NEAREST_QUERY)
+ &&
+ (psrc->dwControlFlags & LUP_DEEP)
+ &&
+ !psrc->u_type.sbc.psdHead)
+ {
+ //
+ // didn't find it locally. Turn off LUP_NEAREST
+ // and do this as a general query. This might bring
+ // it back to a SAP query, but this time
+ // without LUP_NEAREST. But starting anew
+ // allows the use of the bindery and that
+ // might find things quickly.
+ //
+ psrc->dwControlFlags &= ~LUP_NEAREST;
+ psrc->dwUnionType = LOOKUP_TYPE_NIL;
+ if(psrc->u_type.sbc.s)
+ {
+ SapFreeSapSocket(psrc->u_type.sbc.s);
+ psrc->u_type.sbc.s = 0;
+ }
+
+ }
+ else
+ {
+ fDoStateMachine = FALSE;
+ }
+ break;
+ }
+ } // switch
+ } while(fDoStateMachine);
+
+DoneNext:
+ SapReleaseContext(psrc);
+ if((err != NO_ERROR)
+ &&
+ (err != (DWORD)SOCKET_ERROR))
+ {
+ SetLastError(err);
+ err = (DWORD)SOCKET_ERROR;
+ }
+ return((INT)err);
+}
+
+BOOL
+NSPpCheckCancel(
+ PVOID pvArg
+ )
+/*++
+Routine Description:
+ Coroutine called to check if the SAP lookup has been cancelled.
+ For now, this always returns FALSE as we use the flags word in
+ the control block instead
+--*/
+{
+ return(FALSE);
+}
+
+
+DWORD
+NSPpGotSap(
+ PSAP_BCAST_CONTROL psbc,
+ PSAP_IDENT_HEADER pSap,
+ PDWORD pdwErr
+ )
+/*++
+Routine Description:
+ Coroutine called for each SAP reply received. This decides
+ whether to keep the data or not and returns a code telling
+ the SAP engine whether to proceed
+
+Arguments:
+ psbc -- the SAP_BCAST_CONTROL
+ pSap -- the SAP reply
+ pdwErr -- where to put an error code
+--*/
+{
+ PSAP_DATA psdData;
+ LPWSAQUERYSETW Results = (LPWSAQUERYSETW)psbc->pvArg;
+ PSAP_RNR_CONTEXT psrc = psbc->psrc;
+ DWORD dwRet = dwrcNil;
+ PCHAR pServiceName = (PCHAR)psrc->chName;
+
+ EnterCriticalSection(&psbc->csMonitor);
+
+ //
+ // First, check if this is a lookup for a particular name. If so,
+ // accept only the name.
+ //
+
+ if(*pServiceName)
+ {
+ if(strcmp(pServiceName, pSap->ServerName))
+ {
+ goto nota;
+ }
+ if(!(psrc->dwControlFlags & LUP_NEAREST))
+ {
+ dwRet = dwrcDone;
+ psbc->fFlags |= SBC_FLAG_NOMORE;
+ }
+ }
+
+ //
+ // see if we want to keep this guy
+ // We keep it if we don't already have it in the list
+ //
+
+
+ for(psdData = psbc->psdHead;
+ psdData;
+ psdData = psdData->sapNext)
+ {
+ if(RtlEqualMemory( psdData->socketAddr,
+ &pSap->Address,
+ IPX_ADDRESS_LENGTH))
+ {
+ goto nota; // we have it already
+ }
+ }
+
+ psdData = (PSAP_DATA)LocalAlloc(LPTR, sizeof(SAP_DATA));
+ if(!psdData)
+ {
+ goto nota; // can't save it
+ }
+
+ psdData->sapid = pSap->ServerType;
+ RtlMoveMemory(psdData->sapname,
+ pSap->ServerName,
+ 48);
+ RtlMoveMemory(psdData->socketAddr,
+ &pSap->Address,
+ IPX_ADDRESS_LENGTH);
+
+ if(psbc->psdTail)
+ {
+ psbc->psdTail->sapNext = psdData;
+ }
+ else
+ {
+ psbc->psdHead = psdData;
+ }
+ psbc->psdTail = psdData;
+ if(!psbc->psdNext1)
+ {
+ psbc->psdNext1 = psdData;
+ }
+
+nota:
+
+ LeaveCriticalSection(&psbc->csMonitor);
+
+ if((dwRet == dwrcNil)
+ &&
+ psbc->psdNext1)
+ {
+ dwRet = dwrcNoWait;
+ }
+ return(dwRet);
+}
+
+INT WINAPI
+NSPUnInstallNameSpace(
+ IN LPGUID lpProviderId
+ )
+{
+ return(NO_ERROR);
+}
+
+INT WINAPI
+NSPCleanup(
+ IN LPGUID lpProviderId
+ )
+{
+ //
+ // Make sure all contexts are released
+ //
+
+// pFreeAllContexts(); // done in Dll Process detach
+ return(NO_ERROR);
+}
+
+INT WINAPI
+NSPLookupServiceEnd(
+ IN HANDLE hLookup
+ )
+{
+ PSAP_RNR_CONTEXT psrc;
+
+ psrc = SapGetContext(hLookup);
+ if(!psrc)
+ {
+
+ SetLastError(WSA_INVALID_HANDLE);
+ return(SOCKET_ERROR);
+ }
+
+ psrc->fFlags |= SAP_F_END_CALLED;
+ SapReleaseContext(psrc); // get rid of it
+ SapReleaseContext(psrc); // and close it. Context cleanup is
+ // done on the last derefernce.
+ return(NO_ERROR);
+}
+
+INT WINAPI
+NSPSetService(
+ IN LPGUID lpProviderId,
+ IN LPWSASERVICECLASSINFOW lpServiceClassInfo,
+ IN LPWSAQUERYSETW lpqsRegInfo,
+ IN WSAESETSERVICEOP essOperation,
+ IN DWORD dwControlFlags
+ )
+{
+/*++
+Routine Description:
+ The routine that implements the RnR SetService routine. Note that
+ context is ignored. There is no way to direct the registration to
+ a particular server.
+--*/
+ PBYTE pbAddress;
+ DWORD dwOperation;
+ PBYTE pbSocket;
+ DWORD dwAddrs;
+ DWORD err = NO_ERROR;
+ WORD wSapId;
+ DWORD nProt, dwAddress;
+ BOOL fAdvert = FALSE;
+ DWORD dwNumClassInfo;
+ LPWSANSCLASSINFOW lpClassInfoBuf;
+
+
+ //
+ // Verify all args present
+ //
+
+ if(!lpqsRegInfo->lpszServiceInstanceName
+ ||
+ !lpqsRegInfo->lpServiceClassId)
+ {
+ SetLastError(WSA_INVALID_PARAMETER);
+ return(SOCKET_ERROR);
+ }
+
+ if(!lpServiceClassInfo)
+ {
+ UCHAR Buffer[1000];
+ LPWSASERVICECLASSINFOW lpcli = (LPWSASERVICECLASSINFOW)Buffer;
+ DWORD dwBufSize;
+
+ dwBufSize = 1000;
+ lpcli->lpServiceClassId = lpqsRegInfo->lpServiceClassId;
+
+ if(pGetServiceClassInfo(lpProviderId,
+ &dwBufSize,
+ lpcli,
+ &fAdvert) != NO_ERROR)
+ {
+ return(SOCKET_ERROR);
+ }
+ dwNumClassInfo = lpcli->dwCount;
+ lpClassInfoBuf = lpcli->lpClassInfos;
+ }
+ else
+ {
+ dwNumClassInfo = lpServiceClassInfo->dwCount;
+ lpClassInfoBuf = lpServiceClassInfo->lpClassInfos;
+ }
+
+ //
+ // Find the IPX address in the input args
+ //
+
+ err = pLocateSapIdAndProtocls(lpqsRegInfo->lpServiceClassId,
+ dwNumClassInfo,
+ lpClassInfoBuf,
+ &wSapId,
+ &nProt);
+
+ if(err == NO_ERROR)
+ {
+ if(essOperation == RNRSERVICE_REGISTER)
+ {
+ PCSADDR_INFO pcsaAddress;
+
+ pcsaAddress = lpqsRegInfo->lpcsaBuffer;
+
+ try
+ {
+ for(dwAddrs = lpqsRegInfo->dwNumberOfCsAddrs;
+ dwAddrs;
+ dwAddrs--, pcsaAddress++)
+ {
+ if(pcsaAddress->LocalAddr.lpSockaddr->sa_family == AF_IPX)
+ {
+ pbSocket =
+ (PBYTE)pcsaAddress->LocalAddr.lpSockaddr;
+ break;
+ }
+ }
+ }
+ except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ err = GetExceptionCode();
+ }
+
+ if(err || !dwAddrs)
+ {
+ err = ERROR_INCORRECT_ADDRESS;
+ }
+ else if(fAdvert)
+ {
+ NwpSetClassInfoAdvertise(lpqsRegInfo->lpServiceClassId,
+ wSapId);
+ }
+ }
+ else
+ {
+ pbSocket = 0;
+ }
+ if(err == NO_ERROR)
+ {
+ //
+ // map the operation, and call the common worker
+ //
+
+ switch(essOperation)
+ {
+ case RNRSERVICE_REGISTER:
+ dwOperation = oldRnRServiceRegister;
+ break;
+ case RNRSERVICE_DEREGISTER:
+ case RNRSERVICE_DELETE:
+ dwOperation = oldRnRServiceDeRegister;
+ break;
+ default:
+ err = WSA_INVALID_PARAMETER;
+ break;
+ }
+ if(err == NO_ERROR)
+ {
+ err = pSapSetService2(
+ dwOperation,
+ lpqsRegInfo->lpszServiceInstanceName,
+ pbSocket,
+ lpqsRegInfo->lpServiceClassId,
+ wSapId);
+
+ }
+ }
+ }
+ if(err != NO_ERROR)
+ {
+ SetLastError(err);
+ err = (DWORD)SOCKET_ERROR;
+ }
+ return(err);
+}
+
+INT WINAPI
+NSPInstallServiceClass(
+ IN LPGUID lpProviderId,
+ IN LPWSASERVICECLASSINFOW lpServiceClassInfo
+ )
+{
+ LPWSANSCLASSINFOW pcli, pcli1 = 0;
+ DWORD dwIndex = lpServiceClassInfo->dwCount;
+ CHAR PropertyBuffer[128];
+ PBINDERYCLASSES pbc = (PBINDERYCLASSES)PropertyBuffer;
+ BYTE bData = (BYTE)&((PBINDERYCLASSES)0)->cDataArea[0];
+ PCHAR pszData = &pbc->cDataArea[0];
+ DWORD err;
+ WORD port = 0, sap;
+
+ //
+ // Find the SapId entry
+ //
+
+ for(pcli = lpServiceClassInfo->lpClassInfos;
+ dwIndex;
+ pcli++, dwIndex--)
+ {
+ WORD wTemp;
+
+ if(!_wcsicmp(pcli->lpszName, SERVICE_TYPE_VALUE_IPXPORTW)
+ &&
+ (pcli->dwValueSize == sizeof(WORD)))
+ {
+ //
+ // the value may not be aligned
+ //
+ ((PBYTE)&wTemp)[0] = ((PBYTE)pcli->lpValue)[0];
+ ((PBYTE)&wTemp)[1] = ((PBYTE)pcli->lpValue)[1];
+ port = wTemp;
+ } else if(!_wcsicmp(pcli->lpszName, SERVICE_TYPE_VALUE_SAPIDW)
+ &&
+ (pcli->dwValueSize >= sizeof(WORD)))
+ {
+ ((PBYTE)&wTemp)[0] = ((PBYTE)pcli->lpValue)[0];
+ ((PBYTE)&wTemp)[1] = ((PBYTE)pcli->lpValue)[1];
+ sap = wTemp;
+ pcli1 = pcli;
+ }
+ }
+
+ if(!(pcli = pcli1))
+ {
+ SetLastError(WSA_INVALID_PARAMETER);
+ return(SOCKET_ERROR);
+ }
+
+#if 0 // old way of doing this
+ //
+ // Found it. Build the property segment
+ //
+
+ memset(PropertyBuffer, 0, 128); // clear out everyting
+
+ pbc->bOffset = bData;
+ pbc->bSizeOfString = sizeof("Sapid");
+
+ pbc->bType = BT_WORD; // just a word, I assure you
+ pbc->bSizeOfType = 2;
+ pbc->wNameSpace = (WORD)NS_SAP; // it's us
+ *(PWORD)pszData = htons(*(PWORD)pcli->lpValue);
+ pszData += sizeof(WORD); // where the string goes
+ strcpy(pszData, "SapId");
+// pbc->bFlags = (BYTE)pcli->dwConnectionFlags;
+
+ err = NwpSetClassInfo(
+ lpServiceClassInfo->lpszServiceClassName,
+ lpServiceClassInfo->lpServiceClassId,
+ PropertyBuffer);
+#else
+ err = NwpRnR2AddServiceType(
+ lpServiceClassInfo->lpszServiceClassName,
+ lpServiceClassInfo->lpServiceClassId,
+ sap,
+ port);
+#endif
+ if(err != NO_ERROR)
+ {
+ SetLastError(err);
+ err = (DWORD)SOCKET_ERROR;
+ }
+
+ return(err);
+}
+
+INT WINAPI
+NSPRemoveServiceClass(
+ IN LPGUID lpProviderId,
+ IN LPGUID lpServiceCallId
+ )
+{
+ return(NO_ERROR);
+}
+
+INT WINAPI
+NSPGetServiceClassInfo(
+ IN LPGUID lpProviderId,
+ IN OUT LPDWORD lpdwBufSize,
+ IN OUT LPWSASERVICECLASSINFOW lpServiceClassInfo
+ )
+{
+/*++
+Routine Description:
+ Get the ClassInfo for this type. Class info data may be in the
+ registry, or available via SAP or the bindery. We try all three
+ as appropriate
+--*/
+ BOOL fAdvert;
+
+ return(pGetServiceClassInfo(
+ lpProviderId,
+ lpdwBufSize,
+ lpServiceClassInfo,
+ &fAdvert));
+}
+
+INT WINAPI
+pGetServiceClassInfo(
+ IN LPGUID lpProviderId,
+ IN OUT LPDWORD lpdwBufSize,
+ IN OUT LPWSASERVICECLASSINFOW lpServiceClassInfo,
+ IN PBOOL pfAdvert
+ )
+{
+/*++
+Routine Description:
+ Get the ClassInfo for this type. Class info data may be in the
+ registry, or available via SAP or the bindery. We try all three
+ as appropriate
+--*/
+ DWORD err;
+ LONG lInSize;
+ LONG lSizeNeeded;
+ PBYTE pbBuffer;
+ GUID gdDummy;
+ PWCHAR pwszUuid;
+ LPGUID pType;
+ WORD wSapId;
+ WORD wPort;
+ SOCKADDR_IPX sock;
+ PWCHAR pwszSaveName = lpServiceClassInfo->lpszServiceClassName;
+
+#define SIZENEEDED (sizeof(WSASERVICECLASSINFO) + \
+ sizeof(WSANSCLASSINFO) + \
+ sizeof(WSANSCLASSINFO) + \
+ 10 + 2 + \
+ sizeof(GUID) + 12 + 2)
+
+ *pfAdvert = FALSE;
+
+ lInSize = (LONG)*lpdwBufSize - SIZENEEDED;
+
+ pType = (LPGUID)(lpServiceClassInfo + 1);
+
+ pbBuffer = (PBYTE)(pType + 1);
+
+ if(lInSize < 0)
+ {
+ //
+ // it is too small already
+ //
+
+ pType = &gdDummy;
+ }
+
+ UuidToString(lpServiceClassInfo->lpServiceClassId, &pwszUuid);
+
+ //
+ // First, try the bindery
+ //
+
+ err = NwpGetAddressByName(0,
+ RNRCLASSSAPTYPE,
+ pwszUuid,
+ &sock);
+
+ if(err == NO_ERROR)
+ {
+ wSapId = ntohs(*(PWORD)&sock.sa_netnum);
+ wPort = ntohs(*(PWORD)&sock.sa_socket);
+ }
+ else
+ {
+ UCHAR Buffer[400];
+ DWORD dwLen = 400;
+ DWORD dwNum;
+ //
+ // try SAP
+ //
+
+ err = NwpGetAddressViaSap(RNRCLASSSAPTYPE,
+ pwszUuid,
+ IPX_BIT,
+ (PVOID)Buffer,
+ &dwLen,
+ 0,
+ &dwNum);
+ if((err == NO_ERROR)
+ &&
+ (dwNum > 0) )
+ {
+ PCSADDR_INFO psca = (PCSADDR_INFO)Buffer;
+ PSOCKADDR_IPX psock = (PSOCKADDR_IPX)psca->RemoteAddr.lpSockaddr;
+
+ wSapId = ntohs(*(PWORD)&psock->sa_netnum);
+ wPort = ntohs(*(PWORD)&sock.sa_socket);
+ }
+ else
+ {
+ //
+ // try the local bindery
+
+
+ if(!NwpLookupSapInRegistry(
+ lpServiceClassInfo->lpServiceClassId,
+ &wSapId,
+ &wPort,
+ NULL))
+ {
+ err = WSASERVICE_NOT_FOUND;
+ }
+ else
+ {
+ *pfAdvert = TRUE;
+ err = NO_ERROR;
+ }
+ }
+ }
+
+ RpcStringFree(&pwszUuid);
+
+ if(err != NO_ERROR)
+ {
+ SetLastError(err);
+ err = (DWORD)SOCKET_ERROR;
+ }
+ else
+ {
+ //
+ // we return the input structure and the found type. That's it.
+ // The space needed is a constant since we don't return the name
+ //
+
+ if(lInSize < 0)
+ {
+ SetLastError(WSAEFAULT);
+ *lpdwBufSize += (DWORD)(-lInSize);
+ err = (DWORD)SOCKET_ERROR;
+ }
+ else
+ {
+ LPWSANSCLASSINFOW pci = (LPWSANSCLASSINFOW)pbBuffer;
+ PUCHAR Buff;
+ //
+ // it will fit. SO let's go
+ //
+
+ if(wPort)
+ {
+ Buff = (PCHAR)(pci + 2);
+ }
+ else
+ {
+ Buff = (PCHAR)(pci + 1);
+ }
+
+ *pType = *lpServiceClassInfo->lpServiceClassId;
+ lpServiceClassInfo->lpServiceClassId = pType;
+ lpServiceClassInfo->lpszServiceClassName = 0; // not a
+ lpServiceClassInfo->dwCount = 1;
+ lpServiceClassInfo->lpClassInfos = pci;
+ pci->dwNameSpace = NS_SAP;
+ pci->dwValueType = REG_DWORD;
+ pci->dwValueSize = 2;
+ pci->lpszName = (LPWSTR)Buff;
+ wcscpy((PWCHAR)Buff, L"SapId");
+ Buff += 6 * sizeof(WCHAR);
+ pci->lpValue = (LPVOID)Buff;
+ *(PWORD)Buff = wSapId;
+ Buff += sizeof(WORD);
+ if(wPort)
+ {
+ lpServiceClassInfo->dwCount++;
+ pci++;
+ pci->dwNameSpace = NS_SAP;
+ pci->dwValueType = REG_DWORD;
+ pci->dwValueSize = 2;
+ pci->lpszName = (LPWSTR)Buff;
+ wcscpy((PWCHAR)Buff, L"Port");
+ Buff += 5 * sizeof(WCHAR);
+ pci->lpValue = (LPVOID)Buff;
+ *(PWORD)Buff = wPort;
+ }
+ }
+ }
+ return(err);
+}
+
+INT WINAPI
+NSPStartup(
+ IN LPGUID lpProviderId,
+ IN OUT LPNSP_ROUTINE lpsnpRoutines)
+{
+// DWORD dwSize = min(sizeof(nsrVector), lpsnpRoutines->cbSize);
+ DWORD dwSize = sizeof(nsrVector);
+ RtlCopyMemory(lpsnpRoutines,
+ &nsrVector,
+ dwSize);
+ return(NO_ERROR);
+}
+
+DWORD
+DoASap(
+ IN PSAP_RNR_CONTEXT psrc,
+ IN WORD QueryType,
+ IN PBYTE * ppData,
+ IN LPWSAQUERYSETW lpqsResults,
+ IN PLONG plSpare,
+ IN PDWORD lpdwBufferLength
+ )
+/*++
+ Small routine to construcst a SAP_BROADCAST pakcet and issue the SAP
+--*/
+{
+ DWORD err;
+
+ if(!psrc->u_type.sbc.s)
+ {
+ //
+ // this is the first time. We must init the
+ // structure
+ //
+ err = SapGetSapSocket(&psrc->u_type.sbc.s);
+ if(err)
+ {
+ psrc->u_type.sbc.s = 0; // make sure
+ return(err);
+ }
+ psrc->u_type.sbc.Func = NSPpGotSap;
+ psrc->u_type.sbc.fCheckCancel = NSPpCheckCancel;
+ psrc->u_type.sbc.dwIndex = 0; // just in case.
+ psrc->u_type.sbc.pvArg = (PVOID)lpqsResults;
+ psrc->u_type.sbc.psrc = psrc;
+ psrc->u_type.sbc.fFlags = 0;
+
+ }
+
+ psrc->u_type.sbc.wQueryType = QueryType;
+
+ if(!psrc->u_type.sbc.psdNext1
+ &&
+ !(psrc->u_type.sbc.fFlags & SBC_FLAG_NOMORE))
+ {
+ err = SapGetSapForType(&psrc->u_type.sbc, psrc->wSapId);
+ }
+
+ EnterCriticalSection(&psrc->u_type.sbc.csMonitor);
+ if(psrc->u_type.sbc.psdNext1)
+ {
+ //
+ // Got something to return. Let's do it
+ //
+
+
+ //
+ // Assume we have to return the name
+ //
+
+
+ //
+ // We have to convert the name to UNICODE so
+ // we can return it to the caller.
+ //
+ //
+
+ OEM_STRING Oem;
+ NTSTATUS status;
+ UNICODE_STRING UString;
+
+ RtlInitAnsiString(&Oem,
+ psrc->u_type.sbc.psdNext1->sapname);
+ status = RtlOemStringToUnicodeString(
+ &UString,
+ &Oem,
+ TRUE);
+ if(NT_SUCCESS(status))
+ {
+ if(psrc->wSapId == OT_DIRSERVER)
+ {
+ PWCHAR pwszTemp = &UString.Buffer[31];
+
+ while(*pwszTemp == L'_')
+ {
+ *pwszTemp-- = 0;
+ }
+ }
+ err = pRnRReturnResults(
+ UString.Buffer,
+ &psrc->gdType,
+ 0, // never a version
+ ppData,
+ plSpare,
+ psrc->u_type.sbc.psdNext1->socketAddr,
+ psrc->nProt,
+ psrc->dwControlFlags,
+ lpqsResults);
+ RtlFreeUnicodeString(&UString);
+ if(err == WSAEFAULT)
+ {
+ //
+ // no room. Return buffer size required
+ //
+
+ *lpdwBufferLength =
+ (DWORD)((LONG)*lpdwBufferLength - *plSpare);
+ }
+ }
+ else
+ {
+ err = (DWORD)status;
+ }
+ if(err == NO_ERROR)
+ {
+ //
+ // if we got it, step the item
+ //
+ psrc->u_type.sbc.psdNext1 =
+ psrc->u_type.sbc.psdNext1->sapNext;
+ }
+
+
+ }
+ else
+ {
+ err = (DWORD)WSA_E_NO_MORE;
+ }
+ LeaveCriticalSection(&psrc->u_type.sbc.csMonitor);
+ return(err);
+}
+
+
+DWORD
+PrepareForSap(
+ IN PSAP_RNR_CONTEXT psrc
+ )
+/*++
+Called when there is no bindery or the bindery does not have the
+entry. This initializes the values needed for a SAP search
+--*/
+{
+
+
+ OEM_STRING OemServiceName;
+ UNICODE_STRING UString;
+ NTSTATUS status;
+
+ //
+ // the bindery didn't work. Use SAP.
+ //
+
+
+ psrc->u_type.sbc.dwIndex =
+ psrc->u_type.sbc.dwTickCount = 0;
+ if(psrc->wszContext[0])
+ {
+ return(WSASERVICE_NOT_FOUND); // no contexts in SAP
+ }
+ RtlInitUnicodeString(&UString,
+ psrc->chwName);
+ status = RtlUnicodeStringToOemString(&OemServiceName,
+ &UString,
+ TRUE);
+ if(!NT_SUCCESS(status))
+ {
+ return( (DWORD)status);
+ }
+ strcpy((PCHAR)&psrc->chName,
+ OemServiceName.Buffer);
+ RtlFreeOemString(&OemServiceName);
+ psrc->dwUnionType = LOOKUP_TYPE_SAP;
+ psrc->u_type.sbc.s = 0;
+ return(NO_ERROR);
+}
+
diff --git a/private/nw/svcdlls/nwwks/client/logon.c b/private/nw/svcdlls/nwwks/client/logon.c
new file mode 100644
index 000000000..f539fce2c
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/logon.c
@@ -0,0 +1,2539 @@
+/*++
+
+Copyright (c) 1993, 1994 Microsoft Corporation
+
+Module Name:
+
+ logon.c
+
+Abstract:
+
+ This module contains NetWare credential management code.
+
+Author:
+
+ Rita Wong (ritaw) 15-Feb-1993
+
+Revision History:
+
+ Yi-Hsin Sung (yihsins) 10-July-1993
+ Moved all dialog handling to nwdlg.c
+
+--*/
+
+#include <nwclient.h>
+#include <ntmsv1_0.h>
+#include <nwsnames.h>
+#include <nwcanon.h>
+#include <validc.h>
+#include <nwevent.h>
+
+#include <nwdlg.h>
+
+#include <nwreg.h>
+#include <nwlsa.h>
+#include <nwauth.h>
+#include <nwapi.h>
+#include <nwmisc.h>
+#include <ndsapi32.h>
+
+
+//-------------------------------------------------------------------//
+// //
+// Local Function Prototypes //
+// //
+//-------------------------------------------------------------------//
+
+VOID
+NwpInitializeRegistry(
+ IN LPWSTR NewUserSid,
+ OUT LPWSTR PreferredServer,
+ IN DWORD PreferredServerSize,
+ OUT PDWORD LogonScriptOptions,
+ OUT PDWORD PrintOption
+ );
+
+DWORD
+NwpReadRegInfo(
+ IN HKEY WkstaKey,
+ IN LPWSTR CurrentUserSid,
+ OUT LPWSTR PreferredServer,
+ IN DWORD PreferredServerSize,
+ OUT PDWORD PrintOption
+ );
+
+DWORD
+NwpGetCurrentUser(
+ OUT LPWSTR *SidString,
+ OUT LPWSTR *UserName
+ );
+
+DWORD
+NwpGetUserSid(
+ IN PLUID LogonId,
+ OUT LPWSTR *UserSidString
+ );
+
+BOOL
+NwpPollWorkstationStart(
+ VOID
+ );
+
+VOID
+NwpSaveServiceCredential(
+ IN PLUID LogonId,
+ IN LPWSTR UserName,
+ IN LPWSTR Password
+ );
+
+DWORD
+NwpSetCredentialInLsa(
+ IN PLUID LogonId,
+ IN LPWSTR UserName,
+ IN LPWSTR Password
+ );
+
+NTSTATUS NwNdsOpenRdrHandle(
+ OUT PHANDLE phNwRdrHandle
+ );
+
+DWORD
+NwpReadLogonScriptOptions(
+ IN LPWSTR CurrentUserSid,
+ OUT PDWORD pLogonScriptOptions,
+ OUT PDWORD pPreferredServerExists
+ );
+
+LPWSTR
+NwpConstructLogonScript(
+ IN DWORD LogonScriptOptions
+ );
+
+VOID
+NwpSelectServers(
+ IN HWND DialogHandle,
+ IN PCHANGE_PW_DLG_PARAM Credential
+ );
+
+////////////////////////////////////////////////////////////////////////////
+
+DWORD
+APIENTRY
+NPLogonNotify(
+ PLUID lpLogonId,
+ LPCWSTR lpAuthentInfoType,
+ LPVOID lpAuthentInfo,
+ LPCWSTR lpPreviousAuthentInfoType,
+ LPVOID lpPreviousAuthentInfo,
+ LPWSTR lpStationName,
+ LPVOID StationHandle,
+ LPWSTR *lpLogonScript
+ )
+/*++
+
+Routine Description:
+
+ This function is called by Winlogon after the interactive
+ user has successfully logged on to the local machine. We
+ are given the username and password, which
+ are displayed in the NetWare specific logon dialog if
+ needed.
+
+Arguments:
+
+ lpLogonId - Ignored.
+
+ lpAuthentInfoType - Supplies a string which if is
+ L"MSV1_0:Interactive" means that the user has been logged
+ on by the Microsoft primary authenticator.
+
+ lpAuthentInfo - Supplies a pointer to the credentials which
+ the user was logged on with.
+
+ lpPreviousAuthentInfoType - Ignored.
+
+ lpPreviousAuthentInfo - Ignored.
+
+ lpStationName - Supplies a string which if it is L"WinSta_0"
+ means that Winlogon logged on the user.
+
+ StationHandle - Supplies the handle to the window which to display
+ our specific dialog.
+
+ lpLogonScripts - Receives a pointer to memory allocated by this
+ routine which contains a MULTI_SZ string of a program to run on
+ the command line with arguments, e.g. L"myprogram\0arg1\0arg2\0".
+ This memory must be freed by the caller with LocalFree.
+
+Return Value:
+
+ WN_SUCCESS - Successfully saved default credentials.
+
+ WN_NOT_SUPPORTED - Primary authenticator is not Microsoft or
+ is not interactive via Winlogon.
+
+ ERROR_FILE_NOT_FOUND - Could not get our own provider DLL handle.
+
+--*/
+{
+ DWORD status = NO_ERROR;
+ int Result = FALSE;
+ LPWSTR NewUserSid = NULL;
+ BOOL LogonAttempted = FALSE;
+ PMSV1_0_INTERACTIVE_LOGON NewLogonInfo =
+ (PMSV1_0_INTERACTIVE_LOGON) lpAuthentInfo;
+
+ WCHAR NwpServerBuffer[MAX_PATH + 1];
+ WCHAR NwpPasswordBuffer[NW_MAX_PASSWORD_LEN + 1];
+ DWORD NwpPrintOption = NW_PRINT_OPTION_DEFAULT;
+ DWORD NwpLogonScriptOptions = NW_LOGONSCRIPT_DEFAULT ;
+ BOOL cPasswordDlgClickOK = 0;
+ BOOL ServiceLogin = FALSE ;
+
+ DBG_UNREFERENCED_PARAMETER(lpPreviousAuthentInfoType);
+ DBG_UNREFERENCED_PARAMETER(lpPreviousAuthentInfo);
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("\nNWPROVAU: NPLogonNotify\n"));
+ }
+#endif
+
+ RpcTryExcept {
+
+ if (_wcsicmp(lpAuthentInfoType, L"MSV1_0:Interactive") != 0)
+ {
+
+ //
+ // We only handle a logon where Microsoft is the primary
+ // authenticator and it is an interactive logon via Winlogon.
+ //
+ status = WN_NOT_SUPPORTED;
+ goto EndOfTry;
+ }
+
+ if (_wcsicmp(lpStationName, L"SvcCtl") == 0)
+ {
+ ServiceLogin = TRUE ;
+ }
+
+
+ //
+ // Initialize credential variables
+ //
+ NwpServerBuffer[0] = NW_INVALID_SERVER_CHAR;
+ NwpServerBuffer[1] = 0;
+
+ RtlZeroMemory(NwpPasswordBuffer, sizeof(NwpPasswordBuffer));
+
+ if (NewLogonInfo->Password.Buffer != NULL) {
+
+ //
+ // check for max length to avoid overflowing.
+ //
+ if (NewLogonInfo->Password.Length >
+ (sizeof(NwpPasswordBuffer) - sizeof(WCHAR))) {
+
+ status = ERROR_INVALID_PARAMETER ;
+ goto EndOfTry;
+ }
+
+ wcsncpy(
+ NwpPasswordBuffer,
+ NewLogonInfo->Password.Buffer,
+ NewLogonInfo->Password.Length / sizeof(WCHAR)
+ );
+ }
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("\tMessageType : %lu\n", NewLogonInfo->MessageType));
+ KdPrint(("\tLogonDomainName : %ws\n", NewLogonInfo->LogonDomainName.Buffer));
+ KdPrint(("\tUserName : %ws\n", NewLogonInfo->UserName.Buffer));
+ KdPrint(("\tPassword : %ws\n", NwpPasswordBuffer));
+ }
+#endif
+
+ //
+ // if Interactive login, get user related info
+ //
+ if (!ServiceLogin)
+ {
+ //
+ // Get the user SID so that the user Netware username and
+ // preferred server is saved under a SID key rather than the
+ // LogonDomain*UserName key. We do this by making ourselves
+ // a logon process, and call the special MSV1.0 GetUserInfo
+ // interface.
+ //
+ status = NwpGetUserSid(lpLogonId, &NewUserSid);
+
+ if (status != NO_ERROR) {
+ goto EndOfTry;
+ }
+
+ //
+ // Initialize the registry:
+ // 1) Delete the CurrentUser value if it exists (was not clean up
+ // previously because user did not log off--rebooted machine).
+ // 2) Read the current user's PreferredServer and PrintOption
+ // value so that we can display the user's original
+ // preferred server.
+ //
+ NwpInitializeRegistry( NewUserSid,
+ NwpServerBuffer,
+ sizeof( NwpServerBuffer ) /
+ sizeof( NwpServerBuffer[0]),
+ &NwpLogonScriptOptions,
+ &NwpPrintOption );
+ }
+
+ //
+ // Poll until the NetWare workstation has started, then validate
+ // the user credential.
+ //
+ (void) NwpPollWorkstationStart();
+
+ //
+ // If service login, notify the redir with the username/passwd/
+ // LUID triplet and save the logon ID in the registry so that
+ // workstation can pick up if stopped and restarted.
+ //
+ if (ServiceLogin)
+ {
+ NwpSaveServiceCredential(
+ lpLogonId,
+ NewLogonInfo->UserName.Buffer,
+ NwpPasswordBuffer
+ );
+
+ (void) NwrLogonUser(
+ NULL,
+ lpLogonId,
+ NewLogonInfo->UserName.Buffer,
+ NwpPasswordBuffer,
+ NULL,
+ NULL,
+ 0
+ );
+
+ }
+ else
+ {
+ //
+ // We need to save the user credentials at least once so that
+ // the CURRENTUSER Value is stored in the registry.
+ // This must be done before any RPC calls but after polling
+ // workstation start.
+ //
+ NwpSaveLogonCredential(
+ NewUserSid,
+ lpLogonId,
+ NewLogonInfo->UserName.Buffer,
+ NwpPasswordBuffer,
+ NULL // Don't save the preferred server
+ );
+
+//
+// Let the logon set the printer options for each user.
+// Set the provider name and burst options in the service also.
+//
+ //
+ // We need to set the print option at least once.
+ // This also makes the redir aware of the provider name,
+ //
+ (void) NwrSetInfo(
+ NULL,
+ NwpPrintOption,
+ NULL // Need to be NULL so that we will not try to
+ // attach to the server before logging on
+ );
+
+
+ if (*NwpServerBuffer != NW_INVALID_SERVER_CHAR ) {
+
+ //
+ // Preferred server exists. So, try to log the user on.
+ //
+ INT nResult;
+
+ while (1)
+ {
+ WCHAR *DefaultTree = NULL ;
+ WCHAR *DefaultContext = NULL;
+ WCHAR *PreferredServer = NULL;
+ PROMPTDLGPARAM PasswdPromptParam;
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("\tNwrLogonUser\n"));
+ KdPrint(("\tUserName : %ws\n",NewLogonInfo->UserName.Buffer));
+ KdPrint(("\tServer : %ws\n", NwpServerBuffer));
+ }
+#endif
+
+
+ //
+ // make sure user is logged off
+ //
+ (void) NwrLogoffUser(NULL, lpLogonId) ;
+
+ status = NwrLogonUser(
+ NULL,
+ lpLogonId,
+ NewLogonInfo->UserName.Buffer,
+ NwpPasswordBuffer,
+ NwpServerBuffer, // now either TREE or SERVER
+ NULL,
+ 0
+ );
+
+
+ if (status != ERROR_INVALID_PASSWORD)
+ break ;
+
+ PasswdPromptParam.UserName = NewLogonInfo->UserName.Buffer;
+ PasswdPromptParam.ServerName = NwpServerBuffer ;
+ PasswdPromptParam.Password = NwpPasswordBuffer;
+ PasswdPromptParam.PasswordSize = sizeof(NwpPasswordBuffer)/
+ sizeof(NwpPasswordBuffer[0]) ;
+ Result = DialogBoxParamW(
+ hmodNW,
+ MAKEINTRESOURCEW(DLG_PASSWORD_PROMPT),
+ (HWND) StationHandle,
+ NwpPasswdPromptDlgProc,
+ (LPARAM) &PasswdPromptParam
+ );
+
+ if (Result == -1 || Result == IDCANCEL)
+ {
+ status = ERROR_INVALID_PASSWORD ;
+ break ;
+ }
+ else
+ {
+ cPasswordDlgClickOK++;
+ }
+ }
+
+ if (status == NW_PASSWORD_HAS_EXPIRED)
+ {
+ WCHAR szNumber[16] ;
+ DWORD status1, dwMsgId, dwGraceLogins = 0 ;
+ LPWSTR apszInsertStrings[3] ;
+
+ //
+ // get the grace login count
+ //
+ status1 = NwGetGraceLoginCount(
+ NwpServerBuffer, // BUGBUG
+ NewLogonInfo->UserName.Buffer,
+ &dwGraceLogins) ;
+
+ //
+ // if hit error, just dont use the number
+ //
+ if (status1 == NO_ERROR)
+ {
+ dwMsgId = IDS_PASSWORD_HAS_EXPIRED ; // use setpass.exe
+ wsprintfW(szNumber, L"%ld", dwGraceLogins) ;
+ }
+ else
+ {
+ dwMsgId = IDS_PASSWORD_HAS_EXPIRED1 ; // use setpass.exe
+ }
+
+ apszInsertStrings[0] = NwpServerBuffer ; // BUGBUG
+ apszInsertStrings[1] = szNumber ;
+ apszInsertStrings[2] = NULL ;
+
+ //
+ // put up message on password expiry
+ //
+ (void) NwpMessageBoxIns(
+ (HWND) StationHandle,
+ IDS_NETWARE_TITLE,
+ dwMsgId,
+ apszInsertStrings,
+ MB_OK | MB_SETFOREGROUND |
+ MB_ICONINFORMATION );
+
+ status = NO_ERROR ;
+ }
+
+
+ if ( status != NO_ERROR )
+ {
+ WCHAR *pszErrorLocation = NwpServerBuffer ; //BUGBUG
+ DWORD dwMsgId = IDS_LOGIN_FAILURE_WARNING;
+
+ if (status == ERROR_ACCOUNT_RESTRICTION)
+ {
+ dwMsgId = IDS_LOGIN_ACC_RESTRICTION;
+ }
+
+ if (status == ERROR_SHARING_PAUSED)
+ {
+ status = IDS_LOGIN_DISABLED;
+ }
+
+ if (*NwpServerBuffer == L'*')
+ {
+ //
+ // Format into nicer string for user
+ //
+ WCHAR *pszTmp = LocalAlloc(LMEM_ZEROINIT,
+ (wcslen(NwpServerBuffer)+2) *
+ sizeof(WCHAR)) ;
+ if (pszTmp)
+ {
+ pszErrorLocation = pszTmp ;
+
+ //
+ // This code formats the NDS
+ // tree UNC to: Tree(Context)
+ //
+ wcscpy(pszErrorLocation, NwpServerBuffer+1) ;
+
+ if (pszTmp = wcschr(pszErrorLocation, L'\\'))
+ {
+ *pszTmp = L'(' ;
+ wcscat(pszErrorLocation, L")") ;
+ }
+ }
+ }
+
+ nResult = NwpMessageBoxError(
+ (HWND) StationHandle,
+ IDS_AUTH_FAILURE_TITLE,
+ dwMsgId,
+ status,
+ pszErrorLocation,
+ MB_YESNO | MB_ICONEXCLAMATION );
+
+ if (pszErrorLocation != NwpServerBuffer)
+ {
+ (void) LocalFree(pszErrorLocation) ;
+ }
+
+ //
+ // User chose not to select another preferred server,
+ // hence just return success.
+ //
+ if ( nResult == IDNO ) {
+ status = NO_ERROR;
+ }
+ }
+
+ //
+ // The user might have changed the password in the password
+ // prompt dialog. Hence, we need to save the credentials
+ // ( the password ) again. Although the user might choose
+ // to select another server, he might canceled out of the
+ // login dialog. We must save logon credentials here no matter
+ // what.
+ //
+ NwpSaveLogonCredential(
+ NewUserSid,
+ lpLogonId,
+ NewLogonInfo->UserName.Buffer,
+ NwpPasswordBuffer,
+ NwpServerBuffer
+ );
+ }
+
+ //
+ // Only prompt user with the NetWare login dialog if
+ // no preferred server was found or an error occurred
+ // while authenticating the user.
+ //
+ if ( ( status != NO_ERROR)
+ || (*NwpServerBuffer == NW_INVALID_SERVER_CHAR)
+ )
+ {
+
+ LOGINDLGPARAM LoginParam;
+
+ if ( cPasswordDlgClickOK > 0 )
+ {
+ // Password might have changed in the password prompt
+ // dialog. We want to always first use the NT password
+ // when validating a user on a server. Hence,
+ // we need to copy back the original NT password into
+ // NwpPasswordBuffer.
+
+ RtlZeroMemory(NwpPasswordBuffer, sizeof(NwpPasswordBuffer));
+ if (NewLogonInfo->Password.Buffer != NULL)
+ {
+ wcsncpy(
+ NwpPasswordBuffer,
+ NewLogonInfo->Password.Buffer,
+ NewLogonInfo->Password.Length / sizeof(WCHAR)
+ );
+ }
+ }
+
+ LoginParam.UserName = NewLogonInfo->UserName.Buffer;
+ LoginParam.ServerName = NwpServerBuffer ;
+ LoginParam.Password = NwpPasswordBuffer;
+ LoginParam.NewUserSid = NewUserSid;
+ LoginParam.pLogonId = lpLogonId;
+ LoginParam.ServerNameSize = sizeof( NwpServerBuffer ) /
+ sizeof( NwpServerBuffer[0]);
+ LoginParam.PasswordSize = sizeof( NwpPasswordBuffer ) /
+ sizeof( NwpPasswordBuffer[0]);
+ LoginParam.LogonScriptOptions = NwpLogonScriptOptions;
+ Result = DialogBoxParamW(
+ hmodNW,
+ MAKEINTRESOURCEW(DLG_NETWARE_LOGIN),
+ (HWND) StationHandle,
+ NwpLoginDlgProc,
+ (LPARAM) &LoginParam
+ );
+
+ if (Result == -1) {
+ status = GetLastError();
+ KdPrint(("NWPROVAU: DialogBox failed %lu\n", status));
+ goto EndOfTry;
+ }
+
+ }
+ }
+
+EndOfTry: ;
+
+ }
+ RpcExcept(1) {
+
+#if DBG
+ DWORD XceptCode;
+
+
+ XceptCode = RpcExceptionCode();
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: NPLogonNotify: Exception code is x%08lx\n", XceptCode));
+ }
+ status = NwpMapRpcError(XceptCode);
+#else
+ status = NwpMapRpcError(RpcExceptionCode());
+#endif
+
+ }
+ RpcEndExcept;
+
+
+ if (!ServiceLogin) {
+ DWORD fPServer = 0;
+
+ NwpReadLogonScriptOptions( NewUserSid,
+ &NwpLogonScriptOptions,
+ &fPServer );
+ if ( fPServer && ( NwpLogonScriptOptions & NW_LOGONSCRIPT_ENABLED ) )
+ {
+ *lpLogonScript = NwpConstructLogonScript( NwpLogonScriptOptions );
+
+ //
+ // set scripts to run synchronously. ignore error if we cant.
+ // not a disaster.
+ //
+ (void) NwrSetLogonScript(NULL, SYNC_LOGONSCRIPT) ;
+ }
+ else
+ {
+ *lpLogonScript = LocalAlloc(LMEM_ZEROINIT, sizeof(WCHAR));
+ }
+ }
+ else
+ *lpLogonScript = NULL;
+
+ if (NewUserSid != NULL) {
+ (void) LocalFree((HLOCAL) NewUserSid);
+ }
+
+ //
+ // Clear the password
+ //
+ RtlZeroMemory(NwpPasswordBuffer, sizeof(NwpPasswordBuffer));
+
+ if (status == WN_NO_NETWORK) {
+ //
+ // We don't care if the workstation has not started because
+ // we tuck the logon credential in the registry to be picked
+ // up by the workstation when it starts up. If we return
+ // ERROR_NO_NETWORK, MPR will poll us forever, causing us
+ // to continuously display the login dialog over and over
+ // again.
+ //
+ status = NO_ERROR;
+ }
+
+ if (status != NO_ERROR) {
+ SetLastError(status);
+ }
+
+ return status;
+}
+
+
+
+DWORD
+APIENTRY
+NPPasswordChangeNotify(
+ LPCWSTR lpAuthentInfoType,
+ LPVOID lpAuthentInfo,
+ LPCWSTR lpPreviousAuthentInfoType,
+ LPVOID lpPreviousAuthentInfo,
+ LPWSTR lpStationName,
+ LPVOID StationHandle,
+ DWORD dwChangeInfo
+ )
+/*++
+
+Routine Description:
+
+ This function is called after the interactive user has selected to
+ change the password for the local logon via the Ctrl-Alt-Del dialog.
+ It is also called when the user cannot login because the password
+ has expired and must be changed.
+
+Arguments:
+
+ lpAuthentInfoType - Supplies a string which if is
+ L"MSV1_0:Interactive" means that the user has been logged
+ on by the Microsoft primary authenticator.
+
+ lpAuthentInfo - Supplies a pointer to the credentials to
+ change to.
+
+ lpPreviousAuthentInfoType - Supplies a pointer to the old
+ credentials.
+
+ lpPreviousAuthentInfo - Ignored.
+
+ lpStationName - Supplies a string which if it is L"WinSta_0"
+ means that Winlogon logged on the user.
+
+ StationHandle - Supplies the handle to the window which to display
+ our specific dialog.
+
+ dwChangeInfo - Ignored.
+
+Return Value:
+
+ WN_SUCCESS - successful operation.
+
+ WN_NOT_SUPPORTED - Only support change password if MS v1.0 is
+ the primary authenticator and is done through Winlogon.
+
+ WN_NO_NETWORK - Workstation service did not start.
+
+--*/
+{
+ DWORD status = NO_ERROR;
+
+
+ CHANGE_PW_DLG_PARAM Credential;
+
+ PMSV1_0_INTERACTIVE_LOGON NewCredential =
+ (PMSV1_0_INTERACTIVE_LOGON) lpAuthentInfo;
+ PMSV1_0_INTERACTIVE_LOGON OldCredential =
+ (PMSV1_0_INTERACTIVE_LOGON) lpPreviousAuthentInfo;
+
+
+
+ DBG_UNREFERENCED_PARAMETER(lpPreviousAuthentInfoType);
+ DBG_UNREFERENCED_PARAMETER(dwChangeInfo);
+
+ Credential.UserName = NULL;
+
+ RpcTryExcept {
+
+ if ((_wcsicmp(lpAuthentInfoType, L"MSV1_0:Interactive") != 0) ||
+ (_wcsicmp(lpStationName, L"WinSta0") != 0)) {
+
+ //
+ // We only handle a logon where Microsoft is the primary
+ // authenticator and it is an interactive logon via Winlogon.
+ //
+ status = WN_NOT_SUPPORTED;
+ goto EndOfTry;
+ }
+
+
+ if (NewCredential == NULL || OldCredential == NULL) {
+
+ //
+ // Credentials not given to us by Winlogon or
+ // user did not type the old and new passwords.
+ //
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: PasswordChangeNotify got NULL for new and old credential pointers\n"));
+ }
+#endif
+
+ (void) NwpMessageBoxError(
+ (HWND) StationHandle,
+ IDS_CHANGE_PASSWORD_TITLE,
+ IDS_BAD_PASSWORDS,
+ 0,
+ NULL,
+ MB_OK | MB_ICONSTOP
+ );
+
+ status = WN_NOT_SUPPORTED;
+ goto EndOfTry;
+ }
+
+ Credential.UserName = LocalAlloc(
+ LMEM_ZEROINIT,
+ (NW_MAX_USERNAME_LEN + 3 +
+ (2 * NW_MAX_PASSWORD_LEN)) * sizeof(WCHAR)
+ );
+
+ if (Credential.UserName == NULL) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ goto EndOfTry;
+ }
+
+ Credential.OldPassword = (LPWSTR) ((DWORD) Credential.UserName +
+ (NW_MAX_USERNAME_LEN + 1) * sizeof(WCHAR));
+ Credential.NewPassword = (LPWSTR) ((DWORD) Credential.OldPassword +
+ (NW_MAX_PASSWORD_LEN + 1) * sizeof(WCHAR));
+
+
+ if (NewCredential->UserName.Length == 0) {
+
+ //
+ // UserName is not specified. Try to get interactive user's name.
+ //
+
+ DWORD CharNeeded = NW_MAX_USERNAME_LEN + 1;
+
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: PasswordChangeNotify got empty string for username\n"));
+ }
+#endif
+
+ if (! GetUserNameW(Credential.UserName, &CharNeeded)) {
+
+ //
+ // Could not get interactive user's name. Give up.
+ //
+ (void) NwpMessageBoxError(
+ (HWND) StationHandle,
+ IDS_CHANGE_PASSWORD_TITLE,
+ 0,
+ ERROR_BAD_USERNAME,
+ NULL,
+ MB_OK | MB_ICONSTOP
+ );
+ }
+ }
+ else {
+ wcsncpy(
+ Credential.UserName,
+ NewCredential->UserName.Buffer,
+ NewCredential->UserName.Length / sizeof(WCHAR)
+ );
+ }
+
+ if (OldCredential->Password.Length > 0)
+ {
+ wcsncpy(
+ Credential.OldPassword,
+ OldCredential->Password.Buffer,
+ OldCredential->Password.Length / sizeof(WCHAR)
+ );
+ }
+ else
+ {
+ Credential.OldPassword[0] = 0;
+ }
+
+ if (NewCredential->Password.Length > 0)
+ {
+ wcsncpy(
+ Credential.NewPassword,
+ NewCredential->Password.Buffer,
+ NewCredential->Password.Length / sizeof(WCHAR)
+ );
+ }
+ else
+ {
+ Credential.NewPassword[0] = 0;
+ }
+
+ //
+ // Encode the passwords.
+ //
+ {
+ UCHAR EncodeSeed = NW_ENCODE_SEED2;
+ UNICODE_STRING PasswordStr;
+
+
+ RtlInitUnicodeString(&PasswordStr, Credential.OldPassword);
+ RtlRunEncodeUnicodeString(&EncodeSeed, &PasswordStr);
+
+ RtlInitUnicodeString(&PasswordStr, Credential.NewPassword);
+ RtlRunEncodeUnicodeString(&EncodeSeed, &PasswordStr);
+ }
+
+ NwpSelectServers(StationHandle, &Credential);
+
+EndOfTry: ;
+
+ }
+ RpcExcept(1) {
+
+#if DBG
+ DWORD XceptCode;
+
+
+ XceptCode = RpcExceptionCode();
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: NPPasswordChangeNotify: Exception code is x%08lx\n", XceptCode));
+ }
+ status = NwpMapRpcError(XceptCode);
+#else
+ status = NwpMapRpcError(RpcExceptionCode());
+#endif
+
+ }
+ RpcEndExcept;
+
+ if (Credential.UserName != NULL) {
+ LocalFree(Credential.UserName);
+ }
+
+ if (status != NO_ERROR) {
+ SetLastError(status);
+ }
+
+ return status;
+
+}
+
+
+VOID
+NwpInitializeRegistry(
+ IN LPWSTR NewUserSid,
+ OUT LPWSTR PreferredServer,
+ IN DWORD PreferredServerSize,
+ OUT PDWORD pLogonScriptOptions,
+ OUT PDWORD PrintOption
+ )
+/*++
+
+Routine Description:
+
+ This routine initializes the registry before putting up the
+ logon dialog.
+ 1) Deletes the CurrentUser value if it was not cleaned up from
+ the last logoff.
+ 2) Reads the current user's original PreferredServer value
+ 3) Reads the current user's PrintOption value
+
+Arguments:
+
+ NewUserSid - Supplies the newly logged on user's SID in string format
+ which is the key name to find the password and preferred server.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ LONG RegError;
+ HKEY WkstaKey;
+
+
+ NwDeleteCurrentUser();
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters\Option
+ //
+ RegError = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_OPTION_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ,
+ &WkstaKey
+ );
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpInitializeRegistry open NWCWorkstation\\Parameters\\Option key unexpected error %lu!\n", RegError));
+ return;
+ }
+
+ //
+ // Get user's preferred server information.
+ //
+ (void) NwpReadRegInfo(WkstaKey,
+ NewUserSid,
+ PreferredServer,
+ PreferredServerSize,
+ PrintOption
+ );
+
+ (void) RegCloseKey(WkstaKey);
+ (void) NwpReadLogonScriptOptions( NewUserSid, pLogonScriptOptions, NULL );
+}
+
+
+DWORD
+NwpReadRegInfo(
+ IN HKEY WkstaKey,
+ IN LPWSTR CurrentUserSid,
+ OUT LPWSTR PreferredServer,
+ IN DWORD PreferredServerSize,
+ OUT PDWORD PrintOption
+ )
+/*++
+
+Routine Description:
+
+ This routine reads the user's preferred server and print option
+ from the registry.
+
+Arguments:
+
+ WkstaKey - Supplies the handle to the parameters key under the NetWare
+ workstation service key.
+
+ CurrentUserSid - Supplies the SID string of the user whose information
+ to read.
+
+ PreferredServer - Receives the user's preferred server.
+
+ PrintOption - Receives the user's print option.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ LONG RegError;
+
+ HKEY UserKey;
+
+ DWORD ValueType;
+ DWORD BytesNeeded;
+
+ //
+ // Open current user's key to read the original preferred server.
+ //
+ RegError = RegOpenKeyExW(
+ WkstaKey,
+ CurrentUserSid,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ,
+ &UserKey
+ );
+
+ if (RegError != NO_ERROR) {
+
+ if ( (RegError == ERROR_FILE_NOT_FOUND) ||
+ (RegError == ERROR_PATH_NOT_FOUND) ) {
+
+ //
+ // If key doesnt exist assume first time. Use default
+ // if present.
+ //
+
+ LONG RegError1 ;
+ HKEY WkstaParamKey ;
+ DWORD Disposition, dwScriptOptions,
+ dwScriptOptionsSize = sizeof(dwScriptOptions);
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters
+ //
+ RegError1 = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ,
+ &WkstaParamKey
+ );
+
+ if (RegError1 != NO_ERROR) {
+
+ return (DWORD) RegError; // return original error
+ }
+
+ BytesNeeded = PreferredServerSize;
+
+ RegError1 = RegQueryValueExW(
+ WkstaParamKey,
+ NW_DEFAULTSERVER_VALUENAME,
+ NULL,
+ &ValueType,
+ (LPBYTE) PreferredServer,
+ &BytesNeeded
+ );
+
+
+ if (RegError1 != NO_ERROR) {
+
+ (void) RegCloseKey(WkstaParamKey);
+ PreferredServer[0] = NW_INVALID_SERVER_CHAR;
+ PreferredServer[1] = 0;
+ return (DWORD) RegError; // return original error
+ }
+
+ RegError1 = RegQueryValueExW(
+ WkstaParamKey,
+ NW_DEFAULTSCRIPTOPTIONS_VALUENAME,
+ NULL,
+ &ValueType,
+ (LPBYTE) &dwScriptOptions,
+ &dwScriptOptionsSize
+ );
+
+ (void) RegCloseKey(WkstaParamKey);
+
+ if (RegError1 != NO_ERROR) {
+
+ dwScriptOptions = NW_LOGONSCRIPT_ENABLED |
+ NW_LOGONSCRIPT_4X_ENABLED ;
+ }
+
+ //
+ // We have a default. now write out the info for the current
+ // user now. Note we also write out the login script option.
+ // Errors here are not reported.
+ //
+
+
+ //
+ // Create the key under NWCWorkstation\Parameters\Option\<usersid>
+ //
+ RegError = RegCreateKeyExW(
+ WkstaKey,
+ CurrentUserSid,
+ 0,
+ WIN31_CLASS,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE | WRITE_DAC,
+ NULL, // security attr
+ &UserKey,
+ &Disposition
+ );
+
+ if (RegError == NO_ERROR) {
+
+ RegError = NwLibSetEverybodyPermission( UserKey,
+ KEY_SET_VALUE );
+
+ if ( RegError == NO_ERROR )
+ {
+ //
+ // Write the PreferredServer. Errors ignored.
+ //
+ RegError = RegSetValueExW(
+ UserKey,
+ NW_SERVER_VALUENAME,
+ 0,
+ REG_SZ,
+ (LPVOID) PreferredServer,
+ (wcslen(PreferredServer) + 1) * sizeof(WCHAR)
+ );
+
+ (void) RegCloseKey(UserKey) ;
+
+ (void) NwpSaveLogonScriptOptions(
+ CurrentUserSid,
+ dwScriptOptions
+ ) ;
+ }
+ else {
+
+ (void) RegCloseKey(UserKey) ;
+ }
+ }
+
+
+ *PrintOption = NW_PRINT_OPTION_DEFAULT;
+ return NO_ERROR;
+
+ }
+ return (DWORD) RegError;
+ }
+
+
+ //
+ // Read PreferredServer value
+ //
+ BytesNeeded = PreferredServerSize;
+
+ RegError = RegQueryValueExW(
+ UserKey,
+ NW_SERVER_VALUENAME,
+ NULL,
+ &ValueType,
+ (LPBYTE) PreferredServer,
+ &BytesNeeded
+ );
+
+ ASSERT(BytesNeeded <= PreferredServerSize);
+
+ if (RegError != NO_ERROR) {
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: Attempt to read original preferred server failed %lu\n",
+ RegError));
+ }
+#endif
+ PreferredServer[0] = NW_INVALID_SERVER_CHAR; // Display login dialog
+ PreferredServer[1] = 0;
+ goto CleanExit;
+ }
+
+ //
+ // Read PrintOption value into NwpPrintOption.
+ //
+ BytesNeeded = sizeof(PrintOption);
+
+ RegError = RegQueryValueExW(
+ UserKey,
+ NW_PRINTOPTION_VALUENAME,
+ NULL,
+ &ValueType,
+ (LPBYTE) PrintOption,
+ &BytesNeeded
+ );
+
+ if (RegError != NO_ERROR ) {
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: Attempt to read original print option failed %lu\n", RegError));
+ }
+#endif
+
+ *PrintOption = NW_PRINT_OPTION_DEFAULT;
+ goto CleanExit;
+ }
+
+CleanExit:
+
+ (void) RegCloseKey(UserKey);
+
+ return NO_ERROR;
+}
+
+DWORD
+NwpReadLogonScriptOptions(
+ IN LPWSTR CurrentUserSid,
+ OUT PDWORD pLogonScriptOptions,
+ OUT PDWORD pPreferredServerExists
+
+ )
+/*++
+
+Routine Description:
+
+ This routine reads the user's logon script options from the registry.
+
+Arguments:
+
+ CurrentUserSid - Supplies the SID string of the user whose information
+ to read.
+
+ pLogonScriptOptions - Receives the user's script options
+
+ pPreferredServerExists - Prefered server specified
+
+Return Value:
+
+ None.
+
+--*/
+{
+ LONG RegError;
+
+ HKEY UserKey;
+
+ DWORD ValueType;
+ DWORD BytesNeeded;
+ HKEY WkstaKey;
+ WCHAR PreferredServer[MAX_PATH + 1];
+
+ //
+ // initialize output values
+ //
+ *pLogonScriptOptions = 0 ;
+ if (pPreferredServerExists)
+ *pPreferredServerExists = 0 ;
+
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters\Option
+ //
+ RegError = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_OPTION_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ,
+ &WkstaKey
+ );
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpReadLogonScriptOptions open NWCWorkstation\\Parameters\\Option key unexpected error %lu!\n", RegError));
+ return (DWORD) RegError;
+ }
+
+ //
+ // Open current user's key
+ //
+ RegError = RegOpenKeyExW(
+ WkstaKey,
+ CurrentUserSid,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ,
+ &UserKey
+ );
+
+ if (RegError != NO_ERROR) {
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: Open of CurrentUser %ws existing key failed %lu\n",
+ CurrentUserSid, RegError));
+ }
+#endif
+ (void) RegCloseKey(WkstaKey);
+ return (DWORD) RegError;
+ }
+
+
+ //
+ // Read LogonScriptOption value
+ //
+ BytesNeeded = sizeof(*pLogonScriptOptions);
+
+ RegError = RegQueryValueExW(
+ UserKey,
+ NW_LOGONSCRIPT_VALUENAME,
+ NULL,
+ &ValueType,
+ (LPBYTE) pLogonScriptOptions,
+ &BytesNeeded
+ );
+
+ if (RegError != NO_ERROR ) {
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: Attempt to read original logon script option failed %lu\n", RegError));
+ }
+#endif
+
+ // leave *pLogonScriptOptions as 0
+ }
+
+ if ( pPreferredServerExists != NULL ) {
+
+ //
+ // Read PreferredServer value
+ //
+ BytesNeeded = sizeof( PreferredServer );
+
+ RegError = RegQueryValueExW(
+ UserKey,
+ NW_SERVER_VALUENAME,
+ NULL,
+ &ValueType,
+ (LPBYTE) PreferredServer,
+ &BytesNeeded
+ );
+
+ ASSERT(BytesNeeded <= sizeof(PreferredServer));
+
+ if (RegError != NO_ERROR) {
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: Attempt to read original preferred server failed %lu\n",
+ RegError));
+ }
+#endif
+ *pPreferredServerExists = FALSE;
+ }
+ else {
+ if ( lstrcmp( PreferredServer, L"" ) )
+ *pPreferredServerExists = TRUE;
+ else
+ *pPreferredServerExists = FALSE;
+ }
+ }
+
+ (void) RegCloseKey(UserKey);
+ (void) RegCloseKey(WkstaKey);
+
+ return NO_ERROR;
+}
+
+LPWSTR
+NwpConstructLogonScript(
+ IN DWORD LogonScriptOptions
+)
+/*++
+
+Routine Description:
+
+ This routine constructs the multi-string for the logon script,
+ based on the options
+
+Arguments:
+
+ LogonScriptOptions - Logon Script options
+
+Return Value:
+
+ Allocated multi-string
+
+--*/
+{
+ LPWSTR pLogonScript;
+ DWORD BytesNeeded;
+
+#define NW_NETWARE_SCRIPT_NAME L"nwscript.exe"
+#define NW_NETWARE_DEBUG_NAME L"ntsd "
+
+ if ( !( LogonScriptOptions & NW_LOGONSCRIPT_ENABLED ) ) {
+ return NULL;
+ }
+
+ BytesNeeded = MAX_PATH * sizeof(WCHAR);
+
+ if (pLogonScript = LocalAlloc( LMEM_ZEROINIT, BytesNeeded))
+ {
+ DWORD dwSkipBytes = 0 ;
+ UINT retval ;
+
+#if DBG
+ //
+ // if have exact match then start under NTSD.
+ //
+ if ( LogonScriptOptions == (NW_LOGONSCRIPT_ENABLED |
+ NW_LOGONSCRIPT_4X_ENABLED |
+ NW_LOGONSCRIPT_DEBUG) ) {
+
+ retval = GetSystemDirectory(pLogonScript,
+ BytesNeeded );
+ if (retval == 0) {
+
+ (void)LocalFree(pLogonScript) ;
+ return(NULL) ;
+ }
+ wcscat( pLogonScript, L"\\" );
+ wcscat( pLogonScript, NW_NETWARE_DEBUG_NAME );
+ dwSkipBytes = (retval * sizeof(WCHAR)) +
+ sizeof(NW_NETWARE_DEBUG_NAME) ;
+ BytesNeeded -= dwSkipBytes ;
+ }
+#endif
+
+ retval = GetSystemDirectory(pLogonScript + (dwSkipBytes/sizeof(WCHAR)),
+ BytesNeeded );
+
+ if (retval == 0) {
+
+ (void)LocalFree(pLogonScript) ;
+ return(NULL) ;
+ }
+
+ wcscat( pLogonScript, L"\\" );
+ wcscat( pLogonScript, NW_NETWARE_SCRIPT_NAME );
+ }
+
+ return (pLogonScript);
+
+}
+
+DWORD
+NwpSaveLogonScriptOptions(
+ IN LPWSTR CurrentUserSid,
+ IN DWORD LogonScriptOptions
+ )
+/*++
+
+Routine Description:
+
+ This routine saves the logon script options in the registry.
+
+Arguments:
+
+ CurrentUserSid - Supplies the user's SID string
+
+ LogonScriptOptions - Logon script options
+
+Return Value:
+
+ Error from registry
+
+--*/
+{
+ LONG RegError;
+ HKEY WkstaOptionKey;
+ HKEY CurrentUserOptionKey;
+
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters\Option
+ //
+ RegError = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_OPTION_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE | KEY_CREATE_SUB_KEY | DELETE,
+ &WkstaOptionKey
+ );
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveLogonScriptOptions open NWCWorkstation\\Parameters\\Option key unexpected error %lu!\n", RegError));
+ return RegError;
+ }
+
+ //
+ // Open the <NewUser> key under Option
+ //
+ RegError = RegOpenKeyExW(
+ WkstaOptionKey,
+ CurrentUserSid,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE,
+ &CurrentUserOptionKey
+ );
+
+ (void) RegCloseKey(WkstaOptionKey);
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveLogonScriptOptions failed to save options %lu\n", RegError));
+ return RegError;
+ }
+
+ //
+ // Write the options
+ //
+ RegError = RegSetValueExW(
+ CurrentUserOptionKey,
+ NW_LOGONSCRIPT_VALUENAME,
+ 0,
+ REG_DWORD,
+ (LPVOID) &LogonScriptOptions,
+ sizeof(LogonScriptOptions)
+ );
+
+ (void) RegCloseKey(CurrentUserOptionKey);
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveLogonScriptOptions failed to save options %lu\n", RegError));
+ }
+
+ return RegError;
+
+}
+
+
+VOID
+NwpSaveLogonCredential(
+ IN LPWSTR NewUserSid,
+ IN PLUID LogonId,
+ IN LPWSTR UserName,
+ IN LPWSTR Password,
+ IN LPWSTR PreferredServer OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This routine saves the user logon credential in the registry
+ and LSA's memory. This is normally called when NwrLogonUser is
+ successful.
+
+Arguments:
+
+ NewUserSid - Supplies the newly logged on user's SID string to be
+ set as the CurrentUser value as well as the name of the key for
+ the user's preferred server.
+
+ LogonId - Supplies the user's logon ID. If NULL is specified,
+ just read the existing logon ID from the registry rather
+ than save a new one.
+
+ UserName - Supplies the name of the user.
+
+ Password - Supplies the password which the user wants to use on
+ the NetWare network.
+
+ PreferredServer - Supplies the name of the preferred server.
+
+Return Value:
+
+ Error from redirector if login is rejected.
+
+--*/
+{
+ DWORD status;
+
+ LONG RegError;
+ HKEY WkstaKey;
+ HKEY WkstaLogonKey;
+ HKEY WkstaOptionKey;
+ HKEY NewUserLogonKey;
+ HKEY NewUserOptionKey;
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential: %ws, %ws, %ws, %ws\n",
+ NewUserSid, UserName, Password, PreferredServer));
+ }
+#endif
+
+ //
+ // Write the logon credential to the registry. NewUserSid should be
+ // written last so that if it can be read by the workstation service,
+ // it's an indication that all other credential information has been
+ // written.
+ //
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters\Logon
+ //
+ RegError = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_LOGON_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE | KEY_CREATE_SUB_KEY | DELETE,
+ &WkstaLogonKey
+ );
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential open NWCWorkstation\\Parameters\\Logon key unexpected error %lu!\n", RegError));
+ return;
+ }
+
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters\Option
+ //
+ RegError = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_OPTION_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE | KEY_CREATE_SUB_KEY | DELETE,
+ &WkstaOptionKey
+ );
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential open NWCWorkstation\\Parameters\\Option key unexpected error %lu!\n", RegError));
+ (void) RegCloseKey( WkstaLogonKey );
+ return;
+ }
+
+ //
+ // Open the <NewUser> key under Logon
+ //
+ RegError = RegOpenKeyExW(
+ WkstaLogonKey,
+ NewUserSid,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE,
+ &NewUserLogonKey
+ );
+
+
+ if (RegError == ERROR_FILE_NOT_FOUND)
+ {
+ DWORD Disposition;
+
+ //
+ // Create <NewUser> key under NWCWorkstation\Parameters\Logon
+ //
+ RegError = RegCreateKeyExW(
+ WkstaLogonKey,
+ NewUserSid,
+ 0,
+ WIN31_CLASS,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE,
+ NULL, // security attr
+ &NewUserLogonKey,
+ &Disposition
+ );
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential create Logon\\%ws key unexpected error %lu!\n", NewUserSid, RegError));
+
+ (void) RegCloseKey(WkstaLogonKey);
+ (void) RegCloseKey(WkstaOptionKey);
+ return;
+ }
+ }
+ else if (RegError != NO_ERROR)
+ {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential open Logon\\%ws unexpected error %lu!\n", NewUserSid, RegError));
+
+ (void) RegCloseKey(WkstaLogonKey);
+ (void) RegCloseKey(WkstaOptionKey);
+ return;
+ }
+
+ (void) RegCloseKey(WkstaLogonKey);
+
+ //
+ // Open the <NewUser> key under Option
+ //
+ RegError = RegOpenKeyExW(
+ WkstaOptionKey,
+ NewUserSid,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE,
+ &NewUserOptionKey
+ );
+
+
+ if (RegError == ERROR_FILE_NOT_FOUND)
+ {
+ DWORD Disposition;
+
+ //
+ // Create <NewUser> key under NWCWorkstation\Parameters\Option
+ //
+ RegError = RegCreateKeyExW(
+ WkstaOptionKey,
+ NewUserSid,
+ 0,
+ WIN31_CLASS,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE | WRITE_DAC,
+ NULL, // security attr
+ &NewUserOptionKey,
+ &Disposition
+ );
+
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential create Option\\%ws key unexpected error %lu!\n", NewUserSid, RegError));
+
+ (void) RegCloseKey(WkstaOptionKey);
+ (void) RegCloseKey(NewUserLogonKey);
+ return;
+ }
+
+ RegError = NwLibSetEverybodyPermission( NewUserOptionKey,
+ KEY_SET_VALUE );
+
+ if ( RegError != NO_ERROR )
+ {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential set security on Option\\%ws key unexpected error %lu!\n", NewUserSid, RegError));
+
+ (void) RegCloseKey(WkstaOptionKey);
+ (void) RegCloseKey(NewUserLogonKey);
+ return;
+ }
+
+ }
+ else if (RegError != NO_ERROR)
+ {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential open Option\\%ws unexpected error %lu!\n", NewUserSid, RegError));
+
+ (void) RegCloseKey(WkstaOptionKey);
+ (void) RegCloseKey(NewUserLogonKey);
+ return;
+ }
+
+ (void) RegCloseKey(WkstaOptionKey);
+
+ //
+ // Successfully opened or created an existing user entry.
+ // We will now save the credential in LSA.
+ //
+ status = NwpSetCredentialInLsa(
+ LogonId,
+ UserName,
+ Password
+ );
+
+ if (status != NO_ERROR) {
+ //
+ // Could not save new credential.
+ //
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential failed to set credential %lu\n", status));
+ }
+
+ //
+ // Write the logon ID to the registry.
+ //
+ RegError = RegSetValueExW(
+ NewUserLogonKey,
+ NW_LOGONID_VALUENAME,
+ 0,
+ REG_BINARY,
+ (LPVOID) LogonId,
+ sizeof(LUID)
+ );
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential failed to save logon ID %lu\n", RegError));
+ }
+
+ (void) RegCloseKey(NewUserLogonKey);
+
+ //
+ // If PreferredServer is not supplied, then that means we don't want to
+ // save the preferred server into the registry.
+ //
+
+ if (ARGUMENT_PRESENT(PreferredServer))
+ {
+ //
+ // Write the PreferredServer
+ //
+ RegError = RegSetValueExW(
+ NewUserOptionKey,
+ NW_SERVER_VALUENAME,
+ 0,
+ REG_SZ,
+ (LPVOID) PreferredServer,
+ (wcslen(PreferredServer) + 1) * sizeof(WCHAR)
+ );
+
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential failed to save PreferredServer %ws %lu\n", PreferredServer, RegError));
+ }
+ }
+
+ (void) RegCloseKey(NewUserOptionKey);
+
+ //
+ // Write the NewUser string as the CurrentUser value.
+ //
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters
+ //
+ RegError = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE,
+ &WkstaKey
+ );
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential open NWCWorkstation\\Parameters key unexpected error %lu!\n", RegError));
+ return;
+ }
+
+ RegError = RegSetValueExW(
+ WkstaKey,
+ NW_CURRENTUSER_VALUENAME,
+ 0,
+ REG_SZ,
+ (LPVOID) NewUserSid,
+ (wcslen(NewUserSid) + 1) * sizeof(WCHAR)
+ );
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveLogonCredential failed to save NewUser %ws %lu\n", NewUserSid, RegError));
+ }
+
+ RegCloseKey( WkstaKey );
+
+}
+
+
+VOID
+NwpSaveServiceCredential(
+ IN PLUID LogonId,
+ IN LPWSTR UserName,
+ IN LPWSTR Password
+ )
+/*++
+
+Routine Description:
+
+ This routine saves the service logon ID in the registry and
+ the credential in LSA's memory.
+
+Arguments:
+
+ LogonId - Supplies the service's logon ID.
+
+ UserName - Supplies the name of the service.
+
+ Password - Supplies the password of the service.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ DWORD status;
+
+ LONG RegError;
+ HKEY ServiceLogonKey;
+ HKEY LogonIdKey;
+
+ DWORD Disposition;
+ WCHAR LogonIdKeyName[NW_MAX_LOGON_ID_LEN];
+
+ //
+ // Write the logon ID to the registry.
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters\ServiceLogon, create if does not exist
+ //
+ RegError = RegCreateKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_SERVICE_LOGON_REGKEY,
+ 0,
+ WIN31_CLASS,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE,
+ NULL, // security attr
+ &ServiceLogonKey,
+ &Disposition
+ );
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveServiceCredential open NWCWorkstation\\Parameters\\ServiceLogon key unexpected error %lu!\n", RegError));
+ return;
+ }
+
+ NwLuidToWStr(LogonId, LogonIdKeyName);
+
+ //
+ // Create the logon ID key under ServiceLogon
+ //
+ RegError = RegCreateKeyExW(
+ ServiceLogonKey,
+ LogonIdKeyName,
+ 0,
+ WIN31_CLASS,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE,
+ NULL, // security attr
+ &LogonIdKey,
+ &Disposition
+ );
+
+ RegCloseKey(ServiceLogonKey);
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpSaveServiceCredential create NWCWorkstation\\Parameters\\ServiceLogon\\<LogonId> key unexpected error %lu!\n", RegError));
+ return;
+ }
+
+ RegCloseKey(LogonIdKey);
+
+ //
+ // Save the service logon credential in LSA.
+ //
+ status = NwpSetCredentialInLsa(
+ LogonId,
+ UserName,
+ Password
+ );
+
+ if (status != NO_ERROR) {
+ //
+ // Could not save new credential.
+ //
+ KdPrint(("NWPROVAU: NwpSaveServiceCredential failed to set credential %lu\n", status));
+ }
+}
+
+
+DWORD
+NwpGetUserSid(
+ IN PLUID LogonId,
+ OUT LPWSTR *UserSidString
+ )
+/*++
+
+Routine Description:
+
+ This routine looks up the SID of a user given the user's logon ID.
+ It does this by making the current process a logon process and then
+ call to LSA to get the user SID.
+
+Arguments:
+
+ LogonId - Supplies the logon ID of the user to lookup the SID.
+
+ UserSidString - Receives a pointer to a buffer allocated by this routine
+ which contains the user SID in string form. This must be freed with
+ LocalFree when done.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+{
+ DWORD status;
+ NTSTATUS ntstatus;
+ NTSTATUS AuthPackageStatus;
+
+ STRING InputString;
+ LSA_OPERATIONAL_MODE SecurityMode = 0;
+
+ HANDLE LsaHandle;
+ ULONG AuthPackageId;
+
+ MSV1_0_GETUSERINFO_REQUEST UserInfoRequest;
+ PMSV1_0_GETUSERINFO_RESPONSE UserInfoResponse = NULL;
+ ULONG UserInfoResponseLength;
+
+
+
+
+ //
+ // Register this process as a logon process so that we can call
+ // MS V 1.0 authentication package.
+ //
+ RtlInitString(&InputString, "Microsoft NetWare Credential Manager");
+
+ ntstatus = LsaRegisterLogonProcess(
+ &InputString,
+ &LsaHandle,
+ &SecurityMode
+ );
+
+ if (! NT_SUCCESS(ntstatus)) {
+ KdPrint(("NWPROVAU: LsaRegisterLogonProcess returns x%08lx\n",
+ ntstatus));
+ return RtlNtStatusToDosError(ntstatus);
+ }
+
+ //
+ // Look up the MS V1.0 authentication package
+ //
+ RtlInitString(&InputString, MSV1_0_PACKAGE_NAME);
+
+ ntstatus = LsaLookupAuthenticationPackage(
+ LsaHandle,
+ &InputString,
+ &AuthPackageId
+ );
+
+ if (! NT_SUCCESS(ntstatus)) {
+ KdPrint(("NWPROVAU: LsaLookupAuthenticationPackage returns x%08lx\n",
+ ntstatus));
+ status = RtlNtStatusToDosError(ntstatus);
+ goto CleanExit;
+ }
+
+ //
+ // Ask authentication package for user information.
+ //
+ UserInfoRequest.MessageType = MsV1_0GetUserInfo;
+ RtlCopyLuid(&UserInfoRequest.LogonId, LogonId);
+
+ ntstatus = LsaCallAuthenticationPackage(
+ LsaHandle,
+ AuthPackageId,
+ &UserInfoRequest,
+ sizeof(MSV1_0_GETUSERINFO_REQUEST),
+ (PVOID *) &UserInfoResponse,
+ &UserInfoResponseLength,
+ &AuthPackageStatus
+ );
+
+ if (NT_SUCCESS(ntstatus)) {
+ ntstatus = AuthPackageStatus;
+ }
+ if (! NT_SUCCESS(ntstatus)) {
+ KdPrint(("NWPROVAU: LsaCallAuthenticationPackage returns x%08lx\n",
+ ntstatus));
+ status = RtlNtStatusToDosError(ntstatus);
+ goto CleanExit;
+ }
+
+ //
+ // Convert the SID to string. This routine also allocates the
+ // output buffer.
+ //
+ status = NwpConvertSid(
+ UserInfoResponse->UserSid,
+ UserSidString
+ );
+
+CleanExit:
+ if (UserInfoResponse != NULL) {
+ (void) LsaFreeReturnBuffer((PVOID) UserInfoResponse);
+ }
+
+ (void) LsaDeregisterLogonProcess(LsaHandle);
+
+ return status;
+}
+
+
+DWORD
+NwpConvertSid(
+ IN PSID Sid,
+ OUT LPWSTR *UserSidString
+ )
+{
+ NTSTATUS ntstatus;
+ UNICODE_STRING SidString;
+
+
+ //
+ // Initialize output pointer
+ //
+ *UserSidString = NULL;
+
+ ntstatus = RtlConvertSidToUnicodeString(
+ &SidString,
+ Sid,
+ TRUE // Allocate destination string
+ );
+
+ if (ntstatus != STATUS_SUCCESS) {
+ KdPrint(("NWPROVAU: RtlConvertSidToUnicodeString returns %08lx\n",
+ ntstatus));
+ return RtlNtStatusToDosError(ntstatus);
+ }
+
+ //
+ // Create the buffer to return the SID string
+ //
+ if ((*UserSidString = (LPVOID) LocalAlloc(
+ LMEM_ZEROINIT,
+ SidString.Length + sizeof(WCHAR)
+ )) == NULL) {
+ RtlFreeUnicodeString(&SidString);
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ memcpy(*UserSidString, SidString.Buffer, SidString.Length);
+
+ RtlFreeUnicodeString(&SidString);
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: NwpConvertSid got %ws\n", *UserSidString));
+ }
+#endif
+
+ return NO_ERROR;
+}
+
+
+BOOL
+NwpPollWorkstationStart(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine polls for the workstation to complete starting.
+ It gives up after 90 seconds.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Returns TRUE if the NetWare workstation is running; FALSE otherwise.
+
+--*/
+{
+ DWORD err;
+ SC_HANDLE ScManager = NULL;
+ SC_HANDLE Service = NULL;
+ SERVICE_STATUS ServiceStatus;
+ DWORD TryCount = 0;
+ BOOL Started = FALSE;
+
+
+ if ((ScManager = OpenSCManager(
+ NULL,
+ NULL,
+ SC_MANAGER_CONNECT
+ )) == (SC_HANDLE) NULL) {
+
+ err = GetLastError();
+
+ KdPrint(("NWPROVAU: NwpPollWorkstationStart: OpenSCManager failed %lu\n",
+ err));
+ goto CleanExit;
+ }
+
+ if ((Service = OpenService(
+ ScManager,
+ NW_WORKSTATION_SERVICE,
+ SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG
+ )) == (SC_HANDLE) NULL) {
+
+ err = GetLastError();
+
+ (void) CloseServiceHandle(ScManager);
+
+ KdPrint(("NWPROVAU: NwpPollWorkstationStart: OpenService failed %lu\n",
+ err));
+ goto CleanExit;
+ }
+
+
+ do {
+ if (! QueryServiceStatus(
+ Service,
+ &ServiceStatus
+ )) {
+
+ err = GetLastError();
+ KdPrint(("NWPROVAU: NwpPollWorkstationStart: QueryServiceStatus failed %lu\n",
+ err));
+ goto CleanExit;
+ }
+
+ if ( (ServiceStatus.dwCurrentState == SERVICE_RUNNING) ||
+ (ServiceStatus.dwCurrentState == SERVICE_CONTINUE_PENDING) ||
+ (ServiceStatus.dwCurrentState == SERVICE_PAUSE_PENDING) ||
+ (ServiceStatus.dwCurrentState == SERVICE_PAUSED) ) {
+
+ Started = TRUE;
+ }
+ else if (ServiceStatus.dwCurrentState == SERVICE_START_PENDING ||
+ (ServiceStatus.dwCurrentState == SERVICE_STOPPED &&
+ ServiceStatus.dwWin32ExitCode == ERROR_SERVICE_NEVER_STARTED)) {
+
+ //
+ // If workstation is stopped and never started before but it's
+ // not auto-start, don't poll.
+ //
+ if (TryCount == 0 &&
+ ServiceStatus.dwCurrentState == SERVICE_STOPPED &&
+ ServiceStatus.dwWin32ExitCode == ERROR_SERVICE_NEVER_STARTED) {
+
+ BYTE OutBuffer[sizeof(QUERY_SERVICE_CONFIGW) + 256];
+ DWORD BytesNeeded;
+
+
+ if (QueryServiceConfigW(
+ Service,
+ (LPQUERY_SERVICE_CONFIGW) OutBuffer,
+ sizeof(OutBuffer),
+ &BytesNeeded
+ )) {
+
+ if (((LPQUERY_SERVICE_CONFIGW) OutBuffer)->dwStartType !=
+ SERVICE_AUTO_START) {
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: NwpPollWorkstationStart: Not waiting for the workstation to start\n"));
+ }
+#endif
+
+ goto CleanExit;
+ }
+ }
+ else {
+ err = GetLastError();
+ KdPrint(("NWPROVAU: QueryServiceConfig failed %lu, BytesNeeded %lu\n",
+ err, BytesNeeded));
+ }
+
+ }
+
+
+ //
+ // Wait only if the workstation is start pending, or it has not
+ // been attempted to start before.
+ //
+
+ Sleep(5000); // Sleep for 5 seconds before rechecking.
+ TryCount++;
+ }
+ else {
+ goto CleanExit;
+ }
+
+ } while (! Started && TryCount < 18);
+
+ if (Started) {
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: NetWare workstation is started after we've polled %lu times\n",
+ TryCount));
+ }
+#endif
+
+ }
+
+CleanExit:
+ if (ScManager != NULL) {
+ (void) CloseServiceHandle(ScManager);
+ }
+
+ if (Service != NULL) {
+ (void) CloseServiceHandle(Service);
+ }
+
+ return Started;
+}
+
+
+
+DWORD
+NwpSetCredentialInLsa(
+ IN PLUID LogonId,
+ IN LPWSTR UserName,
+ IN LPWSTR Password
+ )
+/*++
+
+Routine Description:
+
+ This routine calls to the NetWare authentication package to save
+ the user credential.
+
+Arguments:
+
+ LogonId - Supplies the logon ID of the user.
+
+ UserName - Supplies the username.
+
+ Password - Supplies the password.
+
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+{
+ DWORD status;
+ NTSTATUS ntstatus;
+ NTSTATUS AuthPackageStatus;
+
+ STRING InputString;
+ LSA_OPERATIONAL_MODE SecurityMode = 0;
+
+ HANDLE LsaHandle;
+
+ ULONG AuthPackageId;
+
+ NWAUTH_SET_CREDENTIAL_REQUEST SetCredRequest;
+ PCHAR DummyOutput;
+ ULONG DummyOutputLength;
+
+ UNICODE_STRING PasswordStr;
+ UCHAR EncodeSeed = NW_ENCODE_SEED;
+
+
+ //
+ // Register this process as a logon process so that we can call
+ // NetWare authentication package.
+ //
+ RtlInitString(&InputString, "Microsoft NetWare Credential Manager");
+
+ ntstatus = LsaRegisterLogonProcess(
+ &InputString,
+ &LsaHandle,
+ &SecurityMode
+ );
+
+ if (! NT_SUCCESS(ntstatus)) {
+ KdPrint(("NWPROVAU: NwpSetCredential: LsaRegisterLogonProcess returns x%08lx\n",
+ ntstatus));
+ return RtlNtStatusToDosError(ntstatus);
+ }
+
+ //
+ // Look up the NetWare authentication package
+ //
+ RtlInitString(&InputString, NW_AUTH_PACKAGE_NAME);
+
+ ntstatus = LsaLookupAuthenticationPackage(
+ LsaHandle,
+ &InputString,
+ &AuthPackageId
+ );
+
+ if (! NT_SUCCESS(ntstatus)) {
+ KdPrint(("NWPROVAU: NwpSetCredential: LsaLookupAuthenticationPackage returns x%08lx\n",
+ ntstatus));
+ status = RtlNtStatusToDosError(ntstatus);
+ goto CleanExit;
+ }
+
+ //
+ // Ask authentication package for user information.
+ //
+ SetCredRequest.MessageType = NwAuth_SetCredential;
+ RtlCopyLuid(&SetCredRequest.LogonId, LogonId);
+ wcscpy(SetCredRequest.UserName, UserName);
+ wcscpy(SetCredRequest.Password, Password);
+
+ //
+ // Encode the password.
+ //
+ RtlInitUnicodeString(&PasswordStr, SetCredRequest.Password);
+ RtlRunEncodeUnicodeString(&EncodeSeed, &PasswordStr);
+
+ ntstatus = LsaCallAuthenticationPackage(
+ LsaHandle,
+ AuthPackageId,
+ &SetCredRequest,
+ sizeof(SetCredRequest),
+ (PVOID *) &DummyOutput,
+ &DummyOutputLength,
+ &AuthPackageStatus
+ );
+
+ if (NT_SUCCESS(ntstatus)) {
+ ntstatus = AuthPackageStatus;
+ }
+ if (! NT_SUCCESS(ntstatus)) {
+ KdPrint(("NWPROVAU: NwpSetCredential: LsaCallAuthenticationPackage returns x%08lx\n",
+ ntstatus));
+ status = RtlNtStatusToDosError(ntstatus);
+ }
+ else {
+ status = NO_ERROR;
+ }
+
+CleanExit:
+ (void) LsaDeregisterLogonProcess(LsaHandle);
+
+ return status;
+}
+
+NTSTATUS NwNdsOpenRdrHandle(
+ OUT PHANDLE phNwRdrHandle
+)
+{
+
+ NTSTATUS ntstatus;
+ IO_STATUS_BLOCK IoStatusBlock;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ ACCESS_MASK DesiredAccess = SYNCHRONIZE | GENERIC_READ;
+
+ WCHAR NameStr[] = L"\\Device\\NwRdr";
+ UNICODE_STRING uOpenName;
+
+ //
+ // Prepare the open name.
+ //
+
+ RtlInitUnicodeString( &uOpenName, NameStr );
+
+ //
+ // Set up the object attributes.
+ //
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &uOpenName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL );
+
+ ntstatus = NtOpenFile(
+ phNwRdrHandle,
+ DesiredAccess,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_VALID_FLAGS,
+ FILE_SYNCHRONOUS_IO_NONALERT );
+
+ if ( !NT_ERROR(ntstatus) &&
+ !NT_INFORMATION(ntstatus) &&
+ !NT_WARNING(ntstatus)) {
+
+ return IoStatusBlock.Status;
+
+ }
+
+ return ntstatus;
+}
+
+VOID
+NwpSelectServers(
+ IN HWND DialogHandle,
+ IN PCHANGE_PW_DLG_PARAM Credential
+ )
+/*++
+
+Routine Description:
+
+ This routine displays the dialog for user to select individual trees
+ to change password on. It then changes the password on the selected
+ list. After the password has been changed, it displays a dialog which lists
+ the 3.X bindery servers where the change could not be made.
+
+Arguments:
+
+ DialogHandle - Supplies the handle to display dialog.
+
+ Credential - Provides on input the old and new passwords, and
+ the logged in user's name. Other field are ignored
+ on input and consecuently used within this function.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ INT Result;
+
+ Credential->TreeList = NULL;
+ Credential->UserList = NULL;
+ Credential->Entries = 0;
+ Credential->ChangedOne = FALSE;
+
+ Result = DialogBoxParamW( hmodNW,
+ MAKEINTRESOURCEW(DLG_PW_SELECT_SERVERS),
+ (HWND) DialogHandle,
+ NwpSelectServersDlgProc,
+ (LPARAM) Credential );
+
+ if ( Result == IDOK )
+ {
+ //
+ // Display list of trees (if any) for which password was changed.
+ //
+ DialogBoxParamW( hmodNW,
+ MAKEINTRESOURCEW(DLG_PW_CHANGED),
+ (HWND) DialogHandle,
+ NwpChangePasswordSuccessDlgProc,
+ (LPARAM) Credential );
+
+ if ( Credential->TreeList != NULL )
+ {
+ LocalFree( Credential->TreeList );
+ }
+
+ //
+ // Display a dialog to tell users to use SetPass if they have an
+ // account on a NetWare 3.X server.
+ //
+ NwpMessageBoxError( DialogHandle,
+ IDS_NETWARE_TITLE,
+ IDS_CHANGE_PASSWORD_INFO,
+ 0,
+ NULL,
+ MB_OK );
+ }
+}
diff --git a/private/nw/svcdlls/nwwks/client/makefile b/private/nw/svcdlls/nwwks/client/makefile
new file mode 100644
index 000000000..f0db8e4a7
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS LINE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/nw/svcdlls/nwwks/client/makefile.inc b/private/nw/svcdlls/nwwks/client/makefile.inc
new file mode 100644
index 000000000..30eff4fcf
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/makefile.inc
@@ -0,0 +1 @@
+nwprovau.rc: nwprovau.dlg
diff --git a/private/nw/svcdlls/nwwks/client/ndscont.ico b/private/nw/svcdlls/nwwks/client/ndscont.ico
new file mode 100644
index 000000000..327d5ceac
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/ndscont.ico
Binary files differ
diff --git a/private/nw/svcdlls/nwwks/client/nwapi.c b/private/nw/svcdlls/nwwks/client/nwapi.c
new file mode 100644
index 000000000..ae92b4f68
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwapi.c
@@ -0,0 +1,1182 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ api.c
+
+Abstract:
+
+ This module contains exposed APIs that is used by the
+ NetWare Control Panel Applet.
+
+Author:
+
+ Yi-Hsin Sung 15-Jul-1993
+
+Revision History:
+
+ ChuckC 23-Jul-93 Completed the stubs
+
+--*/
+
+#include <nwclient.h>
+#include <nwcanon.h>
+#include <validc.h>
+#include <nwdlg.h>
+#include <nwreg.h>
+#include <nwapi.h>
+#include <ntddnwfs.h>
+
+//
+// forward declare
+//
+
+DWORD
+NwpGetCurrentUserRegKey(
+ IN DWORD DesiredAccess,
+ OUT HKEY *phKeyCurrentUser
+ );
+
+
+
+DWORD
+NwQueryInfo(
+ OUT PDWORD pnPrintOptions,
+ OUT LPWSTR *ppszPreferredSrv
+ )
+/*++
+
+Routine Description:
+ This routine gets the user's preferred server and print options from
+ the registry.
+
+Arguments:
+
+ pnPrintOptions - Receives the user's print option
+
+ ppszPreferredSrv - Receives the user's preferred server
+
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+
+ HKEY hKeyCurrentUser = NULL;
+ DWORD BufferSize;
+ DWORD BytesNeeded;
+ DWORD PrintOption;
+ DWORD ValueType;
+ LPWSTR PreferredServer ;
+ DWORD err ;
+
+ //
+ // get to right place in registry and allocate dthe buffer
+ //
+ if (err = NwpGetCurrentUserRegKey( KEY_READ, &hKeyCurrentUser))
+ {
+ //
+ // If somebody mess around with the registry and we can't find
+ // the registry, just use the defaults.
+ //
+ *ppszPreferredSrv = NULL;
+ *pnPrintOptions = NW_PRINT_OPTION_DEFAULT;
+ return NO_ERROR;
+ }
+
+ BufferSize = sizeof(WCHAR) * (MAX_PATH + 2) ;
+ PreferredServer = (LPWSTR) LocalAlloc(LPTR, BufferSize) ;
+ if (!PreferredServer)
+ return (GetLastError()) ;
+
+ //
+ // Read PreferredServer value into Buffer.
+ //
+ BytesNeeded = BufferSize ;
+
+ err = RegQueryValueExW( hKeyCurrentUser,
+ NW_SERVER_VALUENAME,
+ NULL,
+ &ValueType,
+ (LPBYTE) PreferredServer,
+ &BytesNeeded );
+
+ if (err != NO_ERROR)
+ {
+ //
+ // set to empty and carry on
+ //
+ PreferredServer[0] = 0;
+ }
+
+ //
+ // Read PrintOption value into PrintOption.
+ //
+ BytesNeeded = sizeof(PrintOption);
+
+ err = RegQueryValueExW( hKeyCurrentUser,
+ NW_PRINTOPTION_VALUENAME,
+ NULL,
+ &ValueType,
+ (LPBYTE) &PrintOption,
+ &BytesNeeded );
+
+ if (err != NO_ERROR)
+ {
+ //
+ // set to default and carry on
+ //
+ PrintOption = NW_PRINT_OPTION_DEFAULT;
+ }
+
+ if (hKeyCurrentUser != NULL)
+ (void) RegCloseKey(hKeyCurrentUser) ;
+ *ppszPreferredSrv = PreferredServer ;
+ *pnPrintOptions = PrintOption ;
+ return NO_ERROR ;
+}
+
+
+
+DWORD
+NwSetInfoInRegistry(
+ IN DWORD nPrintOptions,
+ IN LPWSTR pszPreferredSrv
+ )
+/*++
+
+Routine Description:
+
+ This routine set the user's print option and preferred server into
+ the registry.
+
+Arguments:
+
+ nPrintOptions - Supplies the print option.
+
+ pszPreferredSrv - Supplies the preferred server.
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+
+ HKEY hKeyCurrentUser = NULL;
+
+ DWORD err = NwpGetCurrentUserRegKey( KEY_WRITE,
+ &hKeyCurrentUser );
+ if (err != NO_ERROR)
+ return err;
+
+ err = RegSetValueEx(hKeyCurrentUser,
+ NW_SERVER_VALUENAME,
+ 0,
+ REG_SZ,
+ (CONST BYTE *)pszPreferredSrv,
+ (wcslen(pszPreferredSrv)+1) * sizeof(WCHAR)) ;
+
+ if (err != NO_ERROR)
+ {
+ if (hKeyCurrentUser != NULL)
+ (void) RegCloseKey(hKeyCurrentUser) ;
+ return err;
+ }
+
+ err = RegSetValueEx(hKeyCurrentUser,
+ NW_PRINTOPTION_VALUENAME,
+ 0,
+ REG_DWORD,
+ (CONST BYTE *)&nPrintOptions,
+ sizeof(nPrintOptions)) ;
+
+ if (hKeyCurrentUser != NULL)
+ (void) RegCloseKey(hKeyCurrentUser) ;
+ return err;
+}
+DWORD
+NwQueryLogonOptions(
+ OUT PDWORD pnLogonScriptOptions
+ )
+/*++
+
+Routine Description:
+ This routine gets the user's Logon script options from the registry.
+
+Arguments:
+
+ pnLogonScriptOptions - Receives the user's Logon script options
+
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+
+ HKEY hKeyCurrentUser;
+ DWORD BytesNeeded;
+ DWORD LogonScriptOption;
+ DWORD ValueType;
+ DWORD err ;
+
+ //
+ // get to right place in registry and allocate the buffer
+ //
+ if (err = NwpGetCurrentUserRegKey( KEY_READ, &hKeyCurrentUser))
+ {
+ //
+ // If somebody mess around with the registry and we can't find
+ // the registry, assume no.
+ //
+ *pnLogonScriptOptions = NW_LOGONSCRIPT_DEFAULT ;
+ return NO_ERROR;
+ }
+
+ //
+ // Read LogonScriptOption value into LogonScriptOption.
+ //
+ BytesNeeded = sizeof(LogonScriptOption);
+
+ err = RegQueryValueExW( hKeyCurrentUser,
+ NW_LOGONSCRIPT_VALUENAME,
+ NULL,
+ &ValueType,
+ (LPBYTE) &LogonScriptOption,
+ &BytesNeeded );
+
+ if (err != NO_ERROR)
+ {
+ //
+ // default to nothing and carry on
+ //
+ LogonScriptOption = NW_LOGONSCRIPT_DEFAULT;
+ }
+
+ *pnLogonScriptOptions = LogonScriptOption ;
+ return NO_ERROR ;
+}
+
+DWORD
+NwSetLogonOptionsInRegistry(
+ IN DWORD nLogonScriptOptions
+ )
+/*++
+
+Routine Description:
+
+ This routine set the logon script options in the registry.
+
+Arguments:
+
+ nLogonScriptOptions - Supplies the logon options
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+
+ HKEY hKeyCurrentUser;
+
+ DWORD err = NwpGetCurrentUserRegKey( KEY_WRITE,
+ &hKeyCurrentUser );
+ if (err != NO_ERROR)
+ return err;
+
+ err = RegSetValueEx(hKeyCurrentUser,
+ NW_LOGONSCRIPT_VALUENAME,
+ 0,
+ REG_DWORD,
+ (CONST BYTE *)&nLogonScriptOptions,
+ sizeof(nLogonScriptOptions)) ;
+
+ (void) RegCloseKey( hKeyCurrentUser );
+ return err;
+}
+
+
+DWORD
+NwSetInfoInWksta(
+ IN DWORD nPrintOption,
+ IN LPWSTR pszPreferredSrv
+)
+/*++
+
+Routine Description:
+
+ This routine notifies the workstation service and the redirector
+ about the user's new print option and preferred server.
+
+Arguments:
+
+ nPrintOptions - Supplies the print option.
+
+ pszPreferredSrv - Supplies the preferred server.
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+ DWORD err;
+
+ RpcTryExcept {
+
+ err = NwrSetInfo( NULL, nPrintOption, pszPreferredSrv );
+
+ }
+ RpcExcept(1) {
+
+ err = NwpMapRpcError(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ return err;
+
+}
+
+DWORD
+NwSetLogonScript(
+ IN DWORD ScriptOptions
+)
+/*++
+
+Routine Description:
+
+ This routine notifies the workstation service of login script
+ options.
+
+Arguments:
+
+ ScriptOptions - Supplies the options.
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+ DWORD err;
+
+ RpcTryExcept {
+
+ err = NwrSetLogonScript( NULL, ScriptOptions );
+
+ }
+ RpcExcept(1) {
+
+ err = NwpMapRpcError(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ return err;
+
+}
+
+
+DWORD
+NwValidateUser(
+ IN LPWSTR pszPreferredSrv
+)
+/*++
+
+Routine Description:
+
+ This routine checks to see if the user can be authenticated on the
+ chosen preferred server.
+
+Arguments:
+
+ pszPreferredSrv - Supplies the preferred server name.
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+ DWORD err;
+
+ //
+ // Don't need to validate if the preferred server is NULL or empty string
+ //
+ if ( ( pszPreferredSrv == NULL )
+ || ( *pszPreferredSrv == 0 )
+ )
+ {
+ return NO_ERROR;
+ }
+
+ //
+ // See if the name contains any invalid characters
+ //
+ if ( !IS_VALID_SERVER_TOKEN( pszPreferredSrv, wcslen( pszPreferredSrv )))
+ return ERROR_INVALID_NAME;
+
+ RpcTryExcept {
+
+ err = NwrValidateUser( NULL, pszPreferredSrv );
+
+ }
+ RpcExcept(1) {
+
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+
+
+DWORD
+NwpGetCurrentUserRegKey(
+ IN DWORD DesiredAccess,
+ OUT HKEY *phKeyCurrentUser
+ )
+/*++
+
+Routine Description:
+
+ This routine opens the current user's registry key under
+ \HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\NWCWorkstation\Parameters
+
+Arguments:
+
+ DesiredAccess - The access mask to open the key with
+
+ phKeyCurrentUser - Receives the opened key handle
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+ DWORD err;
+ HKEY hkeyWksta;
+ LPWSTR CurrentUser;
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters
+ //
+ err = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ,
+ &hkeyWksta
+ );
+
+ if ( err ) {
+ KdPrint(("NWPROVAU: NwGetCurrentUserRegKey open Parameters key unexpected error %lu!\n", err));
+ return err;
+ }
+ //
+ // Get the current user's SID string.
+ //
+ err = NwReadRegValue(
+ hkeyWksta,
+ NW_CURRENTUSER_VALUENAME,
+ &CurrentUser
+ );
+
+
+ if ( err ) {
+ KdPrint(("NWPROVAU: NwGetCurrentUserRegKey read CurrentUser value unexpected error %lu!\n", err));
+ (void) RegCloseKey( hkeyWksta );
+ return err;
+ }
+
+ (void) RegCloseKey( hkeyWksta );
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters\Option
+ //
+ err = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_OPTION_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ,
+ &hkeyWksta
+ );
+
+ if ( err ) {
+ KdPrint(("NWPROVAU: NwGetCurrentUserRegKey open Parameters\\Option key unexpected error %lu!\n", err));
+ return err;
+ }
+
+ //
+ // Open current user's key
+ //
+ err = RegOpenKeyExW(
+ hkeyWksta,
+ CurrentUser,
+ REG_OPTION_NON_VOLATILE,
+ DesiredAccess,
+ phKeyCurrentUser
+ );
+
+ if ( err == ERROR_FILE_NOT_FOUND)
+ {
+ DWORD Disposition;
+
+ //
+ // Create <NewUser> key under NWCWorkstation\Parameters\Option
+ //
+ err = RegCreateKeyExW(
+ hkeyWksta,
+ CurrentUser,
+ 0,
+ WIN31_CLASS,
+ REG_OPTION_NON_VOLATILE,
+ DesiredAccess,
+ NULL, // security attr
+ phKeyCurrentUser,
+ &Disposition
+ );
+
+ }
+
+ if ( err ) {
+ KdPrint(("NWPROVAU: NwGetCurrentUserRegKey open or create of Parameters\\Option\\%ws key failed %lu\n", CurrentUser, err));
+ }
+
+ (void) RegCloseKey( hkeyWksta );
+ (void) LocalFree((HLOCAL)CurrentUser) ;
+ return err;
+}
+
+DWORD
+NwEnumGWDevices(
+ LPDWORD Index,
+ LPBYTE Buffer,
+ DWORD BufferSize,
+ LPDWORD BytesNeeded,
+ LPDWORD EntriesRead
+ )
+/*++
+
+Routine Description:
+
+ This routine enumerates the special gateway devices (redirections)
+ that are cureently in use.
+
+Arguments:
+
+ Index - Point to start enumeration. Should be zero for first call.
+
+ Buffer - buffer for return data
+
+ BufferSize - size of buffer in bytes
+
+ BytesNeeded - number of bytes needed to return all the data
+
+ EntriesRead - number of entries read
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+ DWORD i, err ;
+ LPNETRESOURCE lpNetRes = (LPNETRESOURCE) Buffer ;
+
+ //
+ // call the implementing routine on server side
+ //
+ RpcTryExcept {
+
+ err = NwrEnumGWDevices( NULL,
+ Index,
+ Buffer,
+ BufferSize,
+ BytesNeeded,
+ EntriesRead) ;
+
+ if ( err == NO_ERROR)
+ {
+ //
+ // the change the offsets into real pointers
+ //
+ for (i = 0; i < *EntriesRead; i++)
+ {
+ lpNetRes->lpLocalName =
+ (LPWSTR) (Buffer+(DWORD)lpNetRes->lpLocalName) ;
+ lpNetRes->lpRemoteName =
+ (LPWSTR) (Buffer+(DWORD)lpNetRes->lpRemoteName) ;
+ lpNetRes->lpProvider =
+ (LPWSTR) (Buffer+(DWORD)lpNetRes->lpProvider) ;
+ lpNetRes++ ;
+ }
+ }
+ }
+ RpcExcept(1) {
+
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err ;
+}
+
+
+DWORD
+NwAddGWDevice(
+ LPWSTR DeviceName,
+ LPWSTR RemoteName,
+ LPWSTR AccountName,
+ LPWSTR Password,
+ DWORD Flags
+ )
+/*++
+
+Routine Description:
+
+ This routine adds a gateway redirection.
+
+Arguments:
+
+ DeviceName - the drive to redirect
+
+ RemoteName - the remote network resource to redirect to
+
+ Flags - supplies the options (eg. UpdateRegistry & make this sticky)
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+ DWORD err;
+
+ RpcTryExcept {
+
+ err = NwrAddGWDevice( NULL,
+ DeviceName,
+ RemoteName,
+ AccountName,
+ Password,
+ Flags) ;
+
+ }
+ RpcExcept(1) {
+
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+
+
+DWORD
+NwDeleteGWDevice(
+ LPWSTR DeviceName,
+ DWORD Flags
+ )
+/*++
+
+Routine Description:
+
+ This routine deletes a gateway redirection.
+
+Arguments:
+
+ DeviceName - the drive to delete
+
+ Flags - supplies the options (eg. UpdateRegistry & make this sticky)
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+ DWORD err;
+
+ RpcTryExcept {
+
+ err = NwrDeleteGWDevice(NULL, DeviceName, Flags) ;
+
+ }
+ RpcExcept(1) {
+
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+
+
+DWORD
+NwQueryGatewayAccount(
+ LPWSTR AccountName,
+ DWORD AccountNameLen,
+ LPDWORD AccountCharsNeeded,
+ LPWSTR Password,
+ DWORD PasswordLen,
+ LPDWORD PasswordCharsNeeded
+ )
+/*++
+
+Routine Description:
+
+ Query the gateway account info. specifically, the Account name and
+ the passeord stored as an LSA secret.
+
+Arguments:
+
+ AccountName - buffer used to return account name
+
+ AccountNameLen - length of buffer
+
+ Password - buffer used to return account name
+
+ PasswordLen - length of buffer
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+ DWORD err;
+
+ RpcTryExcept {
+
+ if (AccountName && AccountNameLen)
+ *AccountName = 0 ;
+
+ if (Password && PasswordLen)
+ *Password = 0 ;
+
+ err = NwrQueryGatewayAccount(NULL,
+ AccountName,
+ AccountNameLen,
+ AccountCharsNeeded,
+ Password,
+ PasswordLen,
+ PasswordCharsNeeded) ;
+ }
+ RpcExcept(1) {
+
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+
+DWORD
+NwSetGatewayAccount(
+ LPWSTR AccountName,
+ LPWSTR Password
+ )
+/*++
+
+Routine Description:
+
+ Set the account and password to be used for gateway access.
+
+Arguments:
+
+ AccountName - the account (NULL terminated)
+
+ Password - the password string (NULL terminated)
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+ DWORD err;
+
+ RpcTryExcept {
+
+ err = NwrSetGatewayAccount( NULL,
+ AccountName,
+ Password);
+ }
+ RpcExcept(1) {
+
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err;
+}
+
+
+DWORD
+NwLogonGatewayAccount(
+ LPWSTR AccountName,
+ LPWSTR Password,
+ LPWSTR Server
+ )
+/*++
+
+Routine Description:
+
+ Logon the SYSTEM process with the specified account/password.
+
+Arguments:
+
+ AccountName - the account (NULL terminated)
+
+ Password - the password string (NULL terminated)
+
+ Server - the server to authenticate against
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+
+ DWORD err ;
+ LUID SystemId = SYSTEM_LUID ;
+
+ RpcTryExcept {
+
+ (void) NwrLogoffUser(NULL, &SystemId);
+
+ err = NwrLogonUser( NULL,
+ &SystemId,
+ AccountName,
+ Password,
+ Server,
+ NULL,
+ 0 );
+ }
+ RpcExcept(1) {
+
+ err = NwpMapRpcError( RpcExceptionCode() );
+ }
+ RpcEndExcept
+
+ return err ;
+}
+
+NTSTATUS
+NwGetUserNameForServer(
+ PUNICODE_STRING ServerName,
+ PUNICODE_STRING UserName
+ )
+/*++
+
+Routine Description:
+
+ Calls the redir to get the User Name used to connect to the server
+ in question.
+
+Arguments:
+
+ ServerName - the server in question
+
+ UserName - used to return the user name
+
+Return Value:
+
+ Returns the appropriate NTSTATUS
+
+--*/
+{
+ NTSTATUS Status;
+ WCHAR LocalUserName[NW_MAX_USERNAME_LEN];
+ ULONG UserNameLen = sizeof(LocalUserName);
+
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ UNICODE_STRING DriverName;
+ HANDLE RdrHandle = NULL;
+ IO_STATUS_BLOCK IoStatus;
+
+ //
+ // Initialize variables
+ //
+
+ RtlInitUnicodeString( &DriverName, DD_NWFS_DEVICE_NAME_U );
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &DriverName,
+ 0,
+ NULL,
+ NULL
+ );
+
+ //
+ // open handle to the redir
+ //
+
+ Status = NtOpenFile(
+ &RdrHandle,
+ FILE_LIST_DIRECTORY | SYNCHRONIZE,
+ &ObjectAttributes,
+ &IoStatus,
+ FILE_SHARE_READ,
+ 0 // open options
+ );
+
+ if (!NT_SUCCESS(Status) ||
+ !NT_SUCCESS(IoStatus.Status) )
+ {
+ return( Status );
+ }
+
+
+ //
+ // Call the driver to get use the user name
+ //
+
+ Status = NtFsControlFile(
+ RdrHandle,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatus,
+ FSCTL_NWR_GET_USERNAME,
+ ServerName->Buffer,
+ ServerName->Length,
+ LocalUserName,
+ UserNameLen
+ );
+
+ NtClose(RdrHandle);
+
+ if (!NT_SUCCESS(Status))
+ {
+ return(Status);
+ }
+
+ //
+ // copy the info if it fits. set size required and fail otherwise.
+ //
+
+ if (UserName->MaximumLength >= IoStatus.Information)
+ {
+ UserName->Length = (USHORT) IoStatus.Information;
+
+ RtlCopyMemory( UserName->Buffer,
+ LocalUserName,
+ UserNameLen );
+ Status = STATUS_SUCCESS;
+ }
+ else
+ {
+ UserName->Length = (USHORT) IoStatus.Information;
+ Status = STATUS_BUFFER_TOO_SMALL;
+ }
+
+ return(Status);
+}
+
+
+NTSTATUS
+NwEncryptChallenge(
+ IN PUCHAR Challenge,
+ IN ULONG ObjectId,
+ IN OPTIONAL PUNICODE_STRING ServerName,
+ IN OPTIONAL PUNICODE_STRING Password,
+ OUT PUCHAR ChallengeResponse,
+ OUT OPTIONAL PUCHAR SessionKey
+ )
+/*++
+
+Routine Description:
+
+ Calls the redir to encrypt a challenge
+
+Arguments:
+
+ Challenge - Challenge key
+
+ ObjectId - User's object ID
+
+ ServerName - The server to authenticate against
+
+ Password - Password supplied
+
+ ChallengeResponse - Used to return the challenge response
+
+ SessionKey - Used to return the session key
+
+Return Value:
+
+ Returns the appropriate NTSTATUS
+
+--*/
+{
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ UNICODE_STRING DriverName;
+ HANDLE RdrHandle = NULL;
+ IO_STATUS_BLOCK IoStatus;
+ PNWR_GET_CHALLENGE_REQUEST ChallengeRequest = NULL;
+ NWR_GET_CHALLENGE_REPLY ChallengeReply;
+ ULONG ChallengeRequestSize;
+
+ //
+ // Initialize variables
+ //
+
+ RtlInitUnicodeString( &DriverName, DD_NWFS_DEVICE_NAME_U );
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &DriverName,
+ 0,
+ NULL,
+ NULL
+ );
+
+ //
+ // open handle to redirector
+ //
+
+ Status = NtOpenFile(
+ &RdrHandle,
+ FILE_LIST_DIRECTORY | SYNCHRONIZE,
+ &ObjectAttributes,
+ &IoStatus,
+ FILE_SHARE_READ,
+ 0 // open options
+ );
+
+ if (!NT_SUCCESS(Status) ||
+ !NT_SUCCESS(IoStatus.Status) )
+ {
+ return( Status );
+ }
+
+
+
+ ChallengeRequestSize = sizeof(NWR_GET_CHALLENGE_REQUEST) +
+ ((Password != NULL) ? Password->Length : 0) +
+ ((ServerName != NULL) ? ServerName->Length : 0);
+
+ ChallengeRequest = (PNWR_GET_CHALLENGE_REQUEST) RtlAllocateHeap(
+ RtlProcessHeap(),
+ 0,
+ ChallengeRequestSize
+ );
+
+ if (ChallengeRequest == NULL )
+ {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Cleanup;
+ }
+
+ //
+ // Marshall the challenge request structure. Only send servername if
+ // password has not been specified.
+ //
+
+ ChallengeRequest->ObjectId = ObjectId;
+ ChallengeRequest->Flags = 0;
+
+ //
+ // If both password and servername are present, use the password.
+ //
+
+ if ((Password != NULL) && (Password->Length != 0))
+ {
+
+ ChallengeRequest->ServerNameorPasswordLength = Password->Length;
+ RtlCopyMemory(
+ ChallengeRequest->ServerNameorPassword,
+ Password->Buffer,
+ Password->Length
+ );
+ ChallengeRequest->Flags = CHALLENGE_FLAGS_PASSWORD;
+
+ }
+ else if ((ServerName != NULL) && (ServerName->Length != 0))
+ {
+
+ ChallengeRequest->ServerNameorPasswordLength = ServerName->Length;
+
+ RtlCopyMemory(
+ ChallengeRequest->ServerNameorPassword,
+ ServerName->Buffer,
+ ServerName->Length
+ );
+
+ ChallengeRequest->Flags = CHALLENGE_FLAGS_SERVERNAME;
+ }
+
+ RtlCopyMemory(
+ ChallengeRequest->Challenge,
+ Challenge,
+ 8
+ );
+
+ //
+ // Issue FS control to redir to get challenge response
+ //
+
+ Status = NtFsControlFile(
+ RdrHandle,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatus,
+ FSCTL_NWR_CHALLENGE,
+ ChallengeRequest,
+ ChallengeRequestSize,
+ &ChallengeReply,
+ sizeof(ChallengeReply)
+ );
+ if (!NT_SUCCESS(Status) || !NT_SUCCESS(IoStatus.Status)) {
+ goto Cleanup;
+ }
+
+
+ RtlCopyMemory(
+ ChallengeResponse,
+ ChallengeReply.Challenge,
+ 8
+ );
+
+ if (SessionKey != NULL)
+ {
+ RtlCopyMemory(
+ ChallengeResponse,
+ ChallengeReply.Challenge,
+ 8
+ );
+ }
+
+Cleanup:
+
+ if (RdrHandle != NULL)
+ {
+ NtClose(RdrHandle);
+ }
+
+ if (ChallengeRequest != NULL)
+ {
+ RtlFreeHeap(
+ RtlProcessHeap(),
+ 0,
+ ChallengeRequest
+ );
+ }
+
+ return(Status);
+}
+
diff --git a/private/nw/svcdlls/nwwks/client/nwclient.h b/private/nw/svcdlls/nwwks/client/nwclient.h
new file mode 100644
index 000000000..5afc4b87e
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwclient.h
@@ -0,0 +1,132 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ nwclient.h
+
+Abstract:
+
+ Common header for Workstation client-side code.
+
+Author:
+
+ Rita Wong (ritaw) 25-Feb-1993
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+#ifndef _NWCLIENT_INCLUDED_
+#define _NWCLIENT_INCLUDED_
+
+#include <stdlib.h>
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include <npapi.h>
+
+#include <nwwks.h>
+
+//
+// Debug trace level bits for turning on/off trace statements in the
+// Workstation service
+//
+
+//
+// Initialization and reading info from registry
+//
+#define NW_DEBUG_INIT 0x00000001
+
+//
+// Connection APIs
+//
+#define NW_DEBUG_CONNECT 0x00000002
+
+//
+// Logon APIs
+//
+#define NW_DEBUG_LOGON 0x00000004
+
+//
+// Enum APIs
+//
+#define NW_DEBUG_ENUM 0x00000008
+
+//
+// Other APIs
+//
+#define NW_DEBUG_OTHER 0x00000010
+
+//
+// Print APIs
+//
+#define NW_DEBUG_PRINT 0x00000020
+
+//
+// hInstance of the dll ( nwprovau.dll )
+//
+extern HMODULE hmodNW;
+extern BOOL fIsWinnt;
+
+//
+// Debug stuff
+//
+
+#if DBG
+
+extern DWORD NwProviderTrace;
+
+#define IF_DEBUG(DebugCode) if (NwProviderTrace & NW_DEBUG_ ## DebugCode)
+
+#define STATIC
+
+#else
+
+#define IF_DEBUG(DebugCode) if (FALSE)
+
+#define STATIC static
+
+#endif // DBG
+
+DWORD
+NwpMapRpcError(
+ IN DWORD RpcError
+ );
+
+DWORD
+NwpConvertSid(
+ IN PSID Sid,
+ OUT LPWSTR *UserSidString
+ );
+
+DWORD
+NwpCacheCredentials(
+ IN LPWSTR RemoteName,
+ IN LPWSTR UserName,
+ IN LPWSTR Password
+ );
+
+BOOL
+NwpRetrieveCachedCredentials(
+ IN LPWSTR RemoteName,
+ OUT LPWSTR *UserName,
+ OUT LPWSTR *Password
+ );
+
+#ifndef NT1057
+VOID
+NwCleanupShellExtensions(
+ VOID
+ );
+#endif
+
+#endif // _NWCLIENT_INCLUDED_
diff --git a/private/nw/svcdlls/nwwks/client/nwclsid.h b/private/nw/svcdlls/nwwks/client/nwclsid.h
new file mode 100644
index 000000000..1f383d576
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwclsid.h
@@ -0,0 +1,35 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ nwclsid.h
+
+Abstract:
+
+ Contain the class IDs used in the shell extensions.
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 20-Oct-1995
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+#ifndef _NWCLSID_H_
+#define _NWCLSID_H_
+
+DEFINE_GUID( CLSID_NetWareObjectExt, 0x8e9d6600, 0xf84a, 0x11ce, 0x8d, 0xaa, 0x00, 0xaa, 0x00, 0x4a, 0x56, 0x91 );
+
+DEFINE_GUID( CLSID_NetWareFolderMenuExt, 0xe3f2bac0, 0x099f, 0x11cf, 0x8d, 0xaa, 0x00, 0xaa, 0x00, 0x4a, 0x56, 0x91 );
+
+DEFINE_GUID( CLSID_NetworkNeighborhoodMenuExt, 0x52c68510, 0x09a0, 0x11cf, 0x8d, 0xaa, 0x00, 0xaa, 0x00, 0x4a, 0x56, 0x91 );
+
+
+#endif // _NWCLSID_H_
diff --git a/private/nw/svcdlls/nwwks/client/nwdlg.c b/private/nw/svcdlls/nwwks/client/nwdlg.c
new file mode 100644
index 000000000..d22191af8
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwdlg.c
@@ -0,0 +1,3964 @@
+/*++
+
+Copyright (c) 1993, 1994 Microsoft Corporation
+
+Module Name:
+
+ nwdlg.c
+
+Abstract:
+
+ This module contains NetWare Network Provider Dialog code.
+ It contains all functions used in handling the dialogs
+ shown by the provider.
+
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 5-July-1993
+ Split from provider.c
+
+Revision History:
+
+ Rita Wong (ritaw) 10-Apr-1994
+ Added change password functionality.
+
+--*/
+
+#include <nwclient.h>
+#include <nwsnames.h>
+#include <nwcanon.h>
+#include <validc.h>
+#include <nwevent.h>
+#include <ntmsv1_0.h>
+#include <nwdlg.h>
+#include <tstr.h>
+#include <align.h>
+#include <nwpkstr.h>
+
+#include <nwreg.h>
+#include <nwlsa.h>
+#include <nwmisc.h>
+#include <nwauth.h>
+#include <nwutil.h>
+#include <ntddnwfs.h>
+#include <nds.h>
+
+#define NW_ENUM_EXTRA_BYTES 256
+
+#define IS_TREE(p) (*p == TREE_CHAR)
+
+//-------------------------------------------------------------------//
+// //
+// Local Function Prototypes //
+// //
+//-------------------------------------------------------------------//
+
+VOID
+NwpAddToComboBox(
+ IN HWND DialogHandle,
+ IN INT ControlId,
+ IN LPWSTR pszNone OPTIONAL,
+ IN BOOL AllowNone
+ );
+
+BOOL
+WINAPI
+NwpConnectDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM WParam,
+ LPARAM LParam
+ );
+
+VOID
+NwpCenterDialog(
+ IN HWND hwnd
+ );
+
+HWND
+NwpGetParentHwnd(
+ VOID
+ );
+
+VOID
+NwpGetNoneString(
+ LPWSTR pszNone,
+ DWORD cBufferSize
+ );
+
+VOID
+NwpAddNetWareTreeConnectionsToList(
+ IN HWND DialogHandle,
+ IN LPWSTR NtUserName,
+ IN LPDWORD lpdwUserLuid,
+ IN INT ControlId
+ );
+
+VOID
+NwpAddServersToControl(
+ IN HWND DialogHandle,
+ IN INT ControlId,
+ IN UINT Message,
+ IN INT ControlIdMatch OPTIONAL,
+ IN UINT FindMessage
+ );
+
+VOID
+NwpAddTreeNamesToControl(
+ IN HWND DialogHandle,
+ IN INT ControlId,
+ IN UINT Message,
+ IN INT ControlIdMatch OPTIONAL,
+ IN UINT FindMessage
+ );
+
+DWORD
+NwpGetTreesAndChangePw(
+ IN HWND DialogHandle,
+ IN LPWSTR ServerBuf,
+ IN DWORD UserLuid,
+ IN PCHANGE_PW_DLG_PARAM Credential
+ );
+
+BOOL
+WINAPI
+NwpOldPasswordDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM WParam,
+ LPARAM LParam
+ );
+
+BOOL
+WINAPI
+NwpAltUserNameDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM WParam,
+ LPARAM LParam
+ );
+
+VOID
+EnableAddRemove(
+ IN HWND DialogHandle
+ );
+
+
+BOOL
+WINAPI
+NwpLoginDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM WParam,
+ LPARAM LParam
+ )
+/*++
+
+Routine Description:
+
+ This function is the window management message handler which
+ initializes, and reads user input from the login dialog. It also
+ checks that the preferred server name is valid, notifies the user
+ if not, and dismisses the dialog when done.
+
+Arguments:
+
+ DialogHandle - Supplies a handle to the login dialog.
+
+ Message - Supplies the window management message.
+
+Return Value:
+
+ TRUE - the message was processed.
+
+ FALSE - the message was not processed.
+
+--*/
+{
+ static PLOGINDLGPARAM pLoginParam;
+ static WCHAR OrigPassword[NW_MAX_SERVER_LEN + 1];
+ static WCHAR pszNone[64];
+
+ DWORD status = NO_ERROR;
+ DWORD dwNoneIndex = 0;
+ BOOL enableServer = TRUE ;
+
+ switch (Message) {
+
+ case WM_INITDIALOG:
+
+ pLoginParam = (PLOGINDLGPARAM) LParam;
+
+ //
+ // Store the original password
+ //
+ wcscpy( OrigPassword, pLoginParam->Password );
+
+ //
+ // Position dialog
+ //
+ NwpCenterDialog(DialogHandle);
+
+ //
+ // Handle logon script button
+ //
+ if ( pLoginParam->LogonScriptOptions & NW_LOGONSCRIPT_ENABLED )
+ CheckDlgButton( DialogHandle, ID_LOGONSCRIPT, 1 );
+ else
+ CheckDlgButton( DialogHandle, ID_LOGONSCRIPT, 0 );
+
+ //
+ // Username. Just display the original.
+ //
+ SetDlgItemTextW(DialogHandle, ID_USERNAME, pLoginParam->UserName);
+
+ //
+ // Initialize the <None> string.
+ //
+ NwpGetNoneString( pszNone, sizeof( pszNone) );
+
+ //
+ // Set the values in combo-box list.
+ //
+ NwpAddToComboBox(DialogHandle, ID_SERVER, pszNone, TRUE);
+
+ //
+ // Initially, select the last entry in server list, which should
+ // be the <None> entry.
+ //
+ dwNoneIndex = SendDlgItemMessageW(
+ DialogHandle,
+ ID_SERVER,
+ CB_GETCOUNT,
+ 0,
+ 0 );
+
+ if ( dwNoneIndex != CB_ERR && dwNoneIndex > 0 )
+ dwNoneIndex -= 1;
+
+ (void) SendDlgItemMessageW(
+ DialogHandle,
+ ID_SERVER,
+ CB_SETCURSEL,
+ dwNoneIndex == CB_ERR ? 0 : dwNoneIndex,
+ 0 );
+
+ //
+ // Display the previously saved preferred server or context.
+ // Also set appropriate radio button
+ //
+ if ( *(pLoginParam->ServerName) != NW_INVALID_SERVER_CHAR )
+ {
+ if ( !IS_TREE(pLoginParam->ServerName) )
+ {
+ //
+ // regular server
+ //
+ if (SendDlgItemMessageW(
+ DialogHandle,
+ ID_SERVER,
+ CB_SELECTSTRING,
+ 0,
+ (LPARAM) pLoginParam->ServerName
+ ) == CB_ERR) {
+
+ //
+ // Did not find preferred server in the combo-box,
+ // just set the old value in the edit item.
+ //
+ SetDlgItemTextW( DialogHandle, ID_SERVER,
+ pLoginParam->ServerName);
+ }
+ }
+ else
+ {
+ //
+ // we are dealing with *tree\context. break it into
+ // tree and context
+ //
+
+ WCHAR *pszTmp = wcschr(pLoginParam->ServerName + 1, L'\\') ;
+
+ if (pszTmp)
+ *pszTmp = 0 ;
+
+ SetDlgItemTextW( DialogHandle, ID_DEFAULTTREE,
+ pLoginParam->ServerName + 1);
+
+ SetDlgItemTextW( DialogHandle, ID_DEFAULTCONTEXT,
+ pszTmp ? (pszTmp + 1) : L"");
+
+ if (pszTmp)
+ *pszTmp = L'\\' ; // restore the '\'
+
+ enableServer = FALSE ;
+
+ }
+ }
+
+
+ //
+ // enable appropriate buttons
+ //
+ CheckRadioButton( DialogHandle,
+ ID_PREFERREDSERVER_RB,
+ ID_DEFAULTCONTEXT_RB,
+ enableServer ?
+ ID_PREFERREDSERVER_RB :
+ ID_DEFAULTCONTEXT_RB) ;
+ EnableWindow ( GetDlgItem ( DialogHandle,
+ ID_SERVER ),
+ enableServer ) ;
+ EnableWindow ( GetDlgItem ( DialogHandle,
+ ID_DEFAULTTREE ),
+ !enableServer ) ;
+ EnableWindow ( GetDlgItem ( DialogHandle,
+ ID_DEFAULTCONTEXT ),
+ !enableServer ) ;
+ SetFocus ( GetDlgItem ( DialogHandle,
+ enableServer ? ID_SERVER : ID_DEFAULTTREE ) ) ;
+
+ //
+ // Preferred server name is limited to 48 characters.
+ // Tree is limited to 32. We limit context to 256 - MAXTREE - 3
+ //
+ SendDlgItemMessageW(
+ DialogHandle,
+ ID_SERVER,
+ CB_LIMITTEXT,
+ NW_MAX_SERVER_LEN - 1,
+ 0
+ );
+ SendDlgItemMessageW(
+ DialogHandle,
+ ID_DEFAULTTREE,
+ EM_LIMITTEXT,
+ NW_MAX_TREE_LEN - 1,
+ 0
+ );
+ SendDlgItemMessageW(
+ DialogHandle,
+ ID_DEFAULTCONTEXT,
+ EM_LIMITTEXT,
+ (256 - NW_MAX_TREE_LEN) - 4, // -4 for backslashes unc style
+ 0
+ );
+
+ return TRUE;
+
+
+ case WM_COMMAND:
+
+ switch (LOWORD(WParam)) {
+
+ case ID_DEFAULTCONTEXT_RB :
+ if ( (HIWORD(WParam) == BN_CLICKED )
+ || (HIWORD(WParam) == BN_DOUBLECLICKED )
+ )
+ {
+ CheckRadioButton( DialogHandle,
+ ID_PREFERREDSERVER_RB,
+ ID_DEFAULTCONTEXT_RB,
+ ID_DEFAULTCONTEXT_RB) ;
+ EnableWindow ( GetDlgItem ( DialogHandle,
+ ID_SERVER ),
+ FALSE ) ;
+ EnableWindow ( GetDlgItem ( DialogHandle,
+ ID_DEFAULTTREE ),
+ TRUE ) ;
+ EnableWindow ( GetDlgItem ( DialogHandle,
+ ID_DEFAULTCONTEXT ),
+ TRUE ) ;
+ SetFocus ( GetDlgItem ( DialogHandle,
+ ID_DEFAULTTREE ) ) ;
+ }
+ break ;
+
+ case ID_PREFERREDSERVER_RB :
+ if ( (HIWORD(WParam) == BN_CLICKED )
+ || (HIWORD(WParam) == BN_DOUBLECLICKED )
+ )
+ {
+ CheckRadioButton( DialogHandle,
+ ID_PREFERREDSERVER_RB,
+ ID_DEFAULTCONTEXT_RB,
+ ID_PREFERREDSERVER_RB) ;
+ EnableWindow ( GetDlgItem ( DialogHandle,
+ ID_SERVER ),
+ TRUE ) ;
+ EnableWindow ( GetDlgItem ( DialogHandle,
+ ID_DEFAULTTREE ),
+ FALSE ) ;
+ EnableWindow ( GetDlgItem ( DialogHandle,
+ ID_DEFAULTCONTEXT ),
+ FALSE ) ;
+ SetFocus ( GetDlgItem ( DialogHandle, ID_SERVER ) ) ;
+ }
+ break ;
+
+ //
+ // Use the user's original password when
+ // the user types in or selects a new server or context
+ //
+ case ID_DEFAULTTREE:
+ case ID_DEFAULTCONTEXT:
+ if ( HIWORD(WParam) == EN_CHANGE )
+ {
+ wcscpy( pLoginParam->Password, OrigPassword );
+ }
+ break;
+ case ID_SERVER:
+ if ( (HIWORD(WParam) == CBN_EDITCHANGE )
+ || (HIWORD(WParam) == CBN_SELCHANGE )
+ )
+ {
+ wcscpy( pLoginParam->Password, OrigPassword );
+ }
+ break;
+
+ case IDOK: {
+
+ LPWSTR pszLocation = NULL;
+
+ ASSERT(pLoginParam->ServerNameSize >= MAX_PATH) ;
+
+ //
+ // Allocate a buffer big enough to hold the Preferred
+ // Server name or the NDS Tree and context in the form:
+ // *Tree(Context). Therefore we allocate twice the space
+ // needed for a UNICODE Server name.
+ //
+ if ((pszLocation =
+ LocalAlloc(LMEM_ZEROINIT,
+ (pLoginParam->ServerNameSize * sizeof(WCHAR) * 2))
+ ) == NULL )
+ {
+ break;
+ }
+
+ //
+ // Read the server or tree/context and validate its value.
+ //
+ if (IsDlgButtonChecked(DialogHandle, ID_DEFAULTCONTEXT_RB))
+ {
+ //
+ // We are dealing with TREE/CONTEXT. Synthesize string
+ // in "*TREE\CONTEXT" format.
+ //
+ WCHAR *pTmp ;
+ *pszLocation = TREE_CHAR ;
+
+ if (!GetDlgItemTextW(
+ DialogHandle,
+ ID_DEFAULTTREE,
+ pszLocation + 1,
+ pLoginParam->ServerNameSize - 1
+ ))
+ {
+ //
+ // The tree name field was blank!
+ // Prompt user to provide a NDS tree name.
+ //
+ LocalFree( pszLocation );
+
+ (void) NwpMessageBoxError(
+ DialogHandle,
+ IDS_AUTH_FAILURE_TITLE,
+ IDS_TREE_NAME_MISSING,
+ 0,
+ NULL,
+ MB_OK | MB_ICONSTOP
+ );
+
+ //
+ // Put the focus where the user can fix the
+ // invalid tree name.
+ //
+ SetFocus(GetDlgItem(DialogHandle,ID_DEFAULTTREE));
+
+ SendDlgItemMessageW(
+ DialogHandle,
+ ID_DEFAULTTREE,
+ EM_SETSEL,
+ 0,
+ MAKELPARAM(0, -1)
+ );
+
+ return TRUE;
+ }
+
+ pTmp = pszLocation + wcslen( pszLocation );
+ *pTmp++ = L'\\' ;
+
+ if (!GetDlgItemTextW(
+ DialogHandle,
+ ID_DEFAULTCONTEXT,
+ pTmp,
+ pLoginParam->ServerNameSize - (pTmp-pszLocation)
+ ))
+ {
+ //
+ // The context name field was blank!
+ // Prompt user to provide a NDS context name.
+ //
+ LocalFree( pszLocation );
+
+ (void) NwpMessageBoxError(
+ DialogHandle,
+ IDS_AUTH_FAILURE_TITLE,
+ IDS_CONTEXT_MISSING,
+ 0,
+ NULL,
+ MB_OK | MB_ICONSTOP
+ );
+
+ //
+ // Put the focus where the user can fix the
+ // invalid context name.
+ //
+ SetFocus(GetDlgItem(DialogHandle,ID_DEFAULTCONTEXT));
+
+ SendDlgItemMessageW(
+ DialogHandle,
+ ID_DEFAULTCONTEXT,
+ EM_SETSEL,
+ 0,
+ MAKELPARAM(0, -1)
+ );
+
+ return TRUE;
+ }
+ }
+ else
+ {
+ //
+ // Straight server. Just read it in. If we cant get it
+ // or is empty, use <None>.
+ //
+ if (GetDlgItemTextW(
+ DialogHandle,
+ ID_SERVER,
+ pszLocation,
+ pLoginParam->ServerNameSize
+ ) == 0)
+ {
+ wcscpy( pszLocation, pszNone );
+ }
+ }
+
+ if (( lstrcmpi( pszLocation, pszNone ) != 0) &&
+ ( !IS_TREE( pszLocation )) &&
+ ( !IS_VALID_TOKEN( pszLocation,wcslen( pszLocation ))))
+ {
+ //
+ // Put up message box complaining about the bad
+ // server name.
+ //
+ (void) NwpMessageBoxError(
+ DialogHandle,
+ IDS_AUTH_FAILURE_TITLE,
+ IDS_INVALID_SERVER,
+ 0,
+ NULL,
+ MB_OK | MB_ICONSTOP
+ );
+
+ //
+ // Put the focus where the user can fix the
+ // invalid name.
+ //
+ SetFocus(GetDlgItem(DialogHandle, ID_SERVER));
+
+ SendDlgItemMessageW(
+ DialogHandle,
+ ID_SERVER,
+ EM_SETSEL,
+ 0,
+ MAKELPARAM(0, -1)
+ );
+
+ return TRUE;
+ }
+
+ //
+ // If the user select <NONE>,
+ // change it to empty string.
+ //
+ if (lstrcmpi( pszLocation, pszNone) == 0) {
+
+ wcscpy( pszLocation, L"" );
+ }
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("\n\t[OK] was pressed\n"));
+ KdPrint(("\tNwrLogonUser\n"));
+ KdPrint(("\tPassword : %ws\n",pLoginParam->Password));
+ KdPrint(("\tServer : %ws\n",pszLocation ));
+ }
+#endif
+
+
+ while(1)
+ {
+ PROMPTDLGPARAM PasswdPromptParam;
+ INT Result ;
+
+ //
+ // make sure this user is logged off
+ //
+ (void) NwrLogoffUser(
+ NULL,
+ pLoginParam->pLogonId
+ );
+
+ status = NwrLogonUser(
+ NULL,
+ pLoginParam->pLogonId,
+ pLoginParam->UserName,
+ pLoginParam->Password,
+ pszLocation,
+ NULL,
+ 0
+ );
+
+
+ if (status != ERROR_INVALID_PASSWORD)
+ break ;
+
+ PasswdPromptParam.UserName =
+ pLoginParam->UserName,
+ PasswdPromptParam.ServerName =
+ pszLocation;
+ PasswdPromptParam.Password =
+ pLoginParam->Password;
+ PasswdPromptParam.PasswordSize =
+ pLoginParam->PasswordSize ;
+
+ Result = DialogBoxParamW(
+ hmodNW,
+ MAKEINTRESOURCEW(DLG_PASSWORD_PROMPT),
+ (HWND) DialogHandle,
+ NwpPasswdPromptDlgProc,
+ (LPARAM) &PasswdPromptParam
+ );
+
+ if (Result == -1 || Result == IDCANCEL)
+ {
+ status = ERROR_INVALID_PASSWORD ;
+ break ;
+ }
+ }
+
+ if (status == NW_PASSWORD_HAS_EXPIRED)
+ {
+ WCHAR szNumber[16] ;
+ DWORD dwMsgId, dwGraceLogins = 0 ;
+ LPWSTR apszInsertStrings[3] ;
+
+ //
+ // get the grace login count
+ //
+ if (!IS_TREE(pszLocation))
+ {
+ DWORD status1 ;
+ status1 = NwGetGraceLoginCount(
+ pszLocation,
+ pLoginParam->UserName,
+ &dwGraceLogins) ;
+ //
+ // if hit error, just dont use the number
+ //
+ if (status1 == NO_ERROR)
+ {
+ dwMsgId = IDS_PASSWORD_HAS_EXPIRED ;
+ wsprintfW(szNumber, L"%ld", dwGraceLogins) ;
+ }
+ else
+ {
+ dwMsgId = IDS_PASSWORD_HAS_EXPIRED1 ;
+ }
+ }
+ else // BUGBUG - should get proper number
+ {
+ dwMsgId = IDS_PASSWORD_HAS_EXPIRED1 ;
+ }
+
+ apszInsertStrings[0] = pszLocation ;
+ apszInsertStrings[1] = szNumber ;
+ apszInsertStrings[2] = NULL ;
+
+ //
+ // put up message on password expiry
+ //
+ (void) NwpMessageBoxIns(
+ (HWND) DialogHandle,
+ IDS_NETWARE_TITLE,
+ dwMsgId,
+ apszInsertStrings,
+ MB_OK | MB_SETFOREGROUND |
+ MB_ICONINFORMATION );
+
+ status = NO_ERROR ;
+ }
+
+ //
+ // Check the LogonScript check box.
+ //
+ if (IsDlgButtonChecked(DialogHandle, ID_LOGONSCRIPT))
+ {
+ pLoginParam->LogonScriptOptions =
+ NW_LOGONSCRIPT_ENABLED | NW_LOGONSCRIPT_4X_ENABLED ;
+ }
+ else
+ {
+ pLoginParam->LogonScriptOptions =
+ NW_LOGONSCRIPT_DISABLED ;
+ }
+
+ if (status == NO_ERROR)
+ {
+ //
+ // Save the logon credential to the registry
+ //
+ NwpSaveLogonCredential(
+ pLoginParam->NewUserSid,
+ pLoginParam->pLogonId,
+ pLoginParam->UserName,
+ pLoginParam->Password,
+ pszLocation
+ );
+
+ // Clear the password buffer
+ RtlZeroMemory( OrigPassword, sizeof( OrigPassword));
+ NwpSaveLogonScriptOptions( pLoginParam->NewUserSid, pLoginParam->LogonScriptOptions );
+
+ EndDialog(DialogHandle, 0);
+ }
+ else
+ {
+ INT nResult;
+ DWORD dwMsgId = IDS_AUTH_FAILURE_WARNING;
+ WCHAR *pszErrorLocation = pszLocation ;
+
+ if (status == ERROR_ACCOUNT_RESTRICTION)
+ {
+ dwMsgId = IDS_AUTH_ACC_RESTRICTION;
+ }
+ if (status == ERROR_SHARING_PAUSED)
+ {
+ status = IDS_LOGIN_DISABLED;
+ }
+
+ if (IS_TREE(pszLocation))
+ {
+ //
+ // Format into nicer string for user
+ //
+ WCHAR *pszTmp = LocalAlloc(LMEM_ZEROINIT,
+ (wcslen(pszLocation)+2) *
+ sizeof(WCHAR)) ;
+ if (pszTmp)
+ {
+
+ pszErrorLocation = pszTmp ;
+
+ //
+ // This code formats the NDS
+ // tree UNC to: Tree(Context)
+ //
+ wcscpy(pszErrorLocation, pszLocation+1) ;
+
+ if (pszTmp = wcschr(pszErrorLocation, L'\\'))
+ {
+ *pszTmp = L'(' ;
+ wcscat(pszErrorLocation, L")") ;
+ }
+ }
+ }
+
+ nResult = NwpMessageBoxError(
+ DialogHandle,
+ IDS_AUTH_FAILURE_TITLE,
+ dwMsgId,
+ status,
+ pszErrorLocation,
+ MB_YESNO | MB_DEFBUTTON2
+ | MB_ICONEXCLAMATION
+ );
+
+ if (pszErrorLocation != pszLocation)
+ {
+ (void) LocalFree(pszErrorLocation) ;
+ }
+
+ if ( nResult == IDYES )
+ {
+ //
+ // Save the logon credential to the registry
+ //
+ NwpSaveLogonCredential(
+ pLoginParam->NewUserSid,
+ pLoginParam->pLogonId,
+ pLoginParam->UserName,
+ pLoginParam->Password,
+ pszLocation
+ );
+
+ // Clear the password buffer
+ RtlZeroMemory( OrigPassword, sizeof( OrigPassword));
+ NwpSaveLogonScriptOptions( pLoginParam->NewUserSid, pLoginParam->LogonScriptOptions );
+
+ EndDialog(DialogHandle, 0);
+ }
+ else
+ {
+ //
+ // Put the focus where the user can fix the
+ // invalid name.
+ //
+ DWORD controlId =
+ IsDlgButtonChecked(DialogHandle,
+ ID_DEFAULTCONTEXT_RB) ?
+ ID_DEFAULTTREE :
+ ID_SERVER ;
+
+ SetFocus(GetDlgItem(DialogHandle, controlId));
+
+ SendDlgItemMessageW(
+ DialogHandle,
+ controlId,
+ EM_SETSEL,
+ 0,
+ MAKELPARAM(0, -1)
+ );
+ }
+ }
+
+ LocalFree( pszLocation );
+ return TRUE;
+ }
+
+
+ case IDCANCEL:
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("\n\t[CANCEL] was pressed\n"));
+ KdPrint(("\tLast Preferred Server: %ws\n",
+ pLoginParam->ServerName));
+ KdPrint(("\tLast Password: %ws\n",
+ pLoginParam->Password ));
+ }
+#endif
+
+ if ( *(pLoginParam->ServerName) == NW_INVALID_SERVER_CHAR )
+ {
+ // No preferred server has been set.
+ // Pop up a warning to the user.
+
+ INT nResult = NwpMessageBoxError(
+ DialogHandle,
+ IDS_NETWARE_TITLE,
+ IDS_NO_PREFERRED,
+ 0,
+ NULL,
+ MB_YESNO | MB_ICONEXCLAMATION
+ );
+
+ //
+ // The user chose NO, return to the dialog.
+ //
+ if ( nResult == IDNO )
+ {
+ //
+ // Put the focus where the user can fix the
+ // invalid name.
+ //
+ DWORD controlId =
+ IsDlgButtonChecked(DialogHandle,
+ ID_DEFAULTCONTEXT_RB) ?
+ ID_DEFAULTTREE :
+ ID_SERVER ;
+
+ SetFocus(GetDlgItem(DialogHandle, controlId));
+
+ SendDlgItemMessageW(
+ DialogHandle,
+ controlId,
+ EM_SETSEL,
+ 0,
+ MAKELPARAM(0, -1)
+ );
+
+ return TRUE;
+ }
+
+ //
+ // Save the preferred server as empty string
+ //
+
+ NwpSaveLogonCredential(
+ pLoginParam->NewUserSid,
+ pLoginParam->pLogonId,
+ pLoginParam->UserName,
+ pLoginParam->Password,
+ L""
+ );
+ pLoginParam->LogonScriptOptions = NW_LOGONSCRIPT_DISABLED;
+ NwpSaveLogonScriptOptions( pLoginParam->NewUserSid, pLoginParam->LogonScriptOptions );
+
+ }
+
+ // The user has not logged on to any server.
+ // Logged the user on using NULL as preferred server.
+
+ NwrLogonUser(
+ NULL,
+ pLoginParam->pLogonId,
+ pLoginParam->UserName,
+ pLoginParam->Password,
+ NULL,
+ NULL,
+ 0
+ );
+
+ //
+ // Clear the password buffer
+ RtlZeroMemory( OrigPassword, sizeof( OrigPassword));
+ EndDialog(DialogHandle, 0);
+
+ return TRUE;
+
+
+ case IDHELP:
+ {
+ INT Result ;
+
+ Result = DialogBoxParamW(
+ hmodNW,
+ MAKEINTRESOURCEW(DLG_PREFERRED_SERVER_HELP),
+ (HWND) DialogHandle,
+ NwpHelpDlgProc,
+ (LPARAM) 0
+ );
+
+ // ignore any errors. should not fail, and if does,
+ // nothing we can do.
+
+ return TRUE ;
+
+ }
+
+
+ }
+
+ }
+
+ //
+ // We didn't process this message
+ //
+ return FALSE;
+}
+
+
+INT
+NwpMessageBoxError(
+ IN HWND hwndParent,
+ IN DWORD TitleId,
+ IN DWORD BodyId,
+ IN DWORD Error,
+ IN LPWSTR pszParameter,
+ IN UINT Style
+ )
+/*++
+
+Routine Description:
+
+ This routine puts up a message box error.
+
+Arguments:
+
+ hwndParent - Supplies the handle of the parent window.
+
+ TitleId - Supplies the ID of the title. ( LoadString )
+
+ BodyId - Supplies the ID of the message. ( LoadString )
+
+ Error - If BodyId != 0, then this supplies the ID of the
+ substitution string that will be substituted into
+ the string indicated by BodyId.
+ If BodyId == 0, then this will be the error message.
+ This id is a system error that we will get from FormatMessage
+ using FORMAT_MESSAGE_FROM_SYSTEM.
+
+ pszParameter - A substitution string that will be used as %2 or if
+ Error == 0, this string will be substituted as %1 into
+ the string indicated by BodyId.
+
+ Style - Supplies the style of the MessageBox.
+
+
+Return Value:
+
+ The return value from the MessageBox, 0 if any error is encountered.
+
+--*/
+{
+ DWORD nResult = 0;
+ DWORD nLength;
+
+ WCHAR szTitle[MAX_PATH];
+ WCHAR szBody[MAX_PATH];
+ LPWSTR pszError = NULL;
+ LPWSTR pszBuffer = NULL;
+
+ szTitle[0] = 0;
+ szBody[0] = 0;
+
+ //
+ // Get the Title string
+ //
+ nLength = LoadStringW(
+ hmodNW,
+ TitleId,
+ szTitle,
+ sizeof(szTitle) / sizeof(WCHAR)
+ );
+
+ if ( nLength == 0) {
+ KdPrint(("NWPROVAU: LoadStringW of Title failed with %lu\n",
+ GetLastError()));
+ return 0;
+ }
+
+ //
+ // Get the body string, if BodyId != 0
+ //
+ if ( BodyId != 0 )
+ {
+ nLength = LoadStringW(
+ hmodNW,
+ BodyId,
+ szBody,
+ sizeof(szBody) / sizeof(WCHAR)
+ );
+
+ if ( nLength == 0) {
+ KdPrint(("NWPROVAU: LoadStringW of Body failed with %lu\n",
+ GetLastError()));
+ return 0;
+ }
+ }
+
+ if ( (Error >= IDS_START) && (Error <= IDS_END) ) {
+
+ pszError = (WCHAR *) LocalAlloc(
+ LPTR,
+ 256 * sizeof(WCHAR)) ;
+ if (!pszError)
+ return 0 ;
+
+ nLength = LoadStringW(
+ hmodNW,
+ Error,
+ pszError,
+ 256
+ );
+
+ if ( nLength == 0 ) {
+
+ KdPrint(("NWPROVAU: LoadStringW of Error failed with %lu\n",
+ GetLastError()));
+ (void) LocalFree( (HLOCAL)pszError) ;
+ return 0;
+ }
+ }
+ else if ( Error != 0 ) {
+
+ if ( ( Error == WN_NO_MORE_ENTRIES )
+ || ( Error == ERROR_MR_MID_NOT_FOUND )) {
+
+ //
+ // Handle bogus error from the redirector
+ //
+
+ KdPrint(("NWPROVAU: The NetwareRedirector returned a bogus error as the reason for failure to authenticate. (See Kernel Debugger)\n"));
+ }
+
+ nLength = FormatMessageW(
+ FORMAT_MESSAGE_FROM_SYSTEM
+ | FORMAT_MESSAGE_ALLOCATE_BUFFER,
+ NULL,
+ Error,
+ 0,
+ (LPWSTR) &pszError,
+ MAX_PATH,
+ NULL
+ );
+
+
+ if ( nLength == 0 ) {
+
+ KdPrint(("NWPROVAU: FormatMessageW of Error failed with %lu\n",
+ GetLastError()));
+ return 0;
+ }
+ }
+
+ if ( ( *szBody != 0 )
+ && ( ( pszError != NULL ) || ( pszParameter != NULL) )) {
+
+ LPWSTR aInsertStrings[2];
+ aInsertStrings[0] = pszError? pszError : pszParameter;
+ aInsertStrings[1] = pszError? pszParameter : NULL;
+
+ nLength = FormatMessageW(
+ FORMAT_MESSAGE_FROM_STRING
+ | FORMAT_MESSAGE_ALLOCATE_BUFFER
+ | FORMAT_MESSAGE_ARGUMENT_ARRAY,
+ szBody,
+ 0, // Ignored
+ 0, // Ignored
+ (LPWSTR) &pszBuffer,
+ MAX_PATH,
+ (va_list *) aInsertStrings
+ );
+
+ if ( nLength == 0 ) {
+
+ KdPrint(("NWPROVAU:FormatMessageW(insertstring) failed with %lu\n",
+ GetLastError()));
+
+ if ( pszError != NULL )
+ (void) LocalFree( (HLOCAL) pszError );
+ return 0;
+ }
+
+ }
+ else if ( *szBody != 0 ) {
+
+ pszBuffer = szBody;
+ }
+ else if ( pszError != NULL ) {
+
+ pszBuffer = pszError;
+ }
+ else {
+
+ // We have neither the body nor the error string.
+ // Hence, don't popup the messagebox
+ return 0;
+ }
+
+ if ( pszBuffer != NULL )
+ {
+ nResult = MessageBoxW(
+ hwndParent,
+ pszBuffer,
+ szTitle,
+ Style
+ );
+ }
+
+ if ( ( pszBuffer != NULL )
+ && ( pszBuffer != szBody )
+ && ( pszBuffer != pszError ))
+ {
+ (void) LocalFree( (HLOCAL) pszBuffer );
+ }
+
+ if ( pszError != NULL )
+ (void) LocalFree( (HLOCAL) pszError );
+
+ return nResult;
+}
+
+
+INT
+NwpMessageBoxIns(
+ IN HWND hwndParent,
+ IN DWORD TitleId,
+ IN DWORD MessageId,
+ IN LPWSTR *InsertStrings,
+ IN UINT Style
+ )
+/*++
+
+Routine Description:
+
+ This routine puts up a message box error with array of insert strings
+
+Arguments:
+
+ hwndParent - Supplies the handle of the parent window.
+
+ TitleId - Supplies the ID of the title. ( LoadString )
+
+ MessageId - Supplies the ID of the message. ( LoadString )
+
+ InsertStrings - Array of insert strings for FormatMessage.
+
+ Style - Supplies the style of the MessageBox.
+
+
+Return Value:
+
+ The return value from the MessageBox, 0 if any error is encountered.
+
+--*/
+{
+ DWORD nResult = 0;
+ DWORD nLength;
+
+ WCHAR szTitle[MAX_PATH];
+ WCHAR szBody[MAX_PATH];
+ LPWSTR pszBuffer = NULL;
+
+ szTitle[0] = 0;
+ szBody[0] = 0;
+
+ //
+ // Get the Title string
+ //
+ nLength = LoadStringW(
+ hmodNW,
+ TitleId,
+ szTitle,
+ sizeof(szTitle) / sizeof(szTitle[0])
+ );
+
+ if ( nLength == 0) {
+ return 0;
+ }
+
+ //
+ // Get the message string
+ //
+ nLength = LoadStringW(
+ hmodNW,
+ MessageId,
+ szBody,
+ sizeof(szBody) / sizeof(szBody[0])
+ );
+
+ if ( nLength == 0) {
+ return 0;
+ }
+
+ nLength = FormatMessageW(
+ FORMAT_MESSAGE_FROM_STRING
+ | FORMAT_MESSAGE_ALLOCATE_BUFFER
+ | FORMAT_MESSAGE_ARGUMENT_ARRAY,
+ szBody,
+ 0, // Ignored
+ 0, // Ignored
+ (LPWSTR) &pszBuffer,
+ MAX_PATH,
+ (va_list *) InsertStrings
+ );
+
+ if ( nLength == 0 ) {
+ return 0;
+ }
+
+ if ( pszBuffer != NULL )
+ {
+ nResult = MessageBoxW(
+ hwndParent,
+ pszBuffer,
+ szTitle,
+ Style
+ );
+
+ (void) LocalFree( (HLOCAL) pszBuffer );
+ }
+
+ return nResult;
+}
+
+VOID
+NwpAddServersToControl(
+ IN HWND DialogHandle,
+ IN INT ControlId,
+ IN UINT Message,
+ IN INT ControlIdMatch OPTIONAL,
+ IN UINT FindMessage
+ )
+/*++
+
+Routine Description:
+
+ This function enumerates the servers on the network and adds each
+ server name to the specified Windows control.
+
+ If ControlIdMatch is specified (i.e. non 0), only servers that are
+ not found in ControlIdMatch list are added to the list specified
+ by ControlId.
+
+Arguments:
+
+ DialogHandle - Supplies a handle to the Windows dialog.
+
+ ControlId - Supplies id which specifies the control.
+
+ Message - Supplies the window management message to add string.
+
+ ControlIdMatch - Supplies the control ID which contains server
+ names that should not be in ControlId.
+
+ FindMessage - Supplies the window management message to find
+ string.
+
+Return Value:
+
+ TRUE - the message was processed.
+
+ FALSE - the message was not processed.
+
+--*/
+{
+ DWORD status = ERROR_NO_NETWORK;
+ HANDLE EnumHandle = (HANDLE) NULL;
+
+ LPNETRESOURCE NetR = NULL;
+ LPNETRESOURCEW SavePtr;
+ WCHAR FormattedNameBuf[MAX_NDS_NAME_CHARS];
+
+ LPWSTR lpFormattedName;
+ DWORD dwLength;
+
+ DWORD BytesNeeded = 512;
+ DWORD EntriesRead;
+ DWORD i;
+
+ //
+ // Retrieve the list of servers on the network
+ //
+ status = NPOpenEnum(
+ RESOURCE_GLOBALNET,
+ 0,
+ 0,
+ NULL,
+ &EnumHandle
+ );
+
+ if (status != NO_ERROR) {
+ EnumHandle = (HANDLE) NULL;
+ goto CleanExit;
+ }
+
+ //
+ // Allocate buffer to get servers on the net.
+ //
+ if ((NetR = (LPVOID) LocalAlloc(
+ 0,
+ BytesNeeded
+ )) == NULL) {
+
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ goto CleanExit;
+ }
+
+ do {
+
+ EntriesRead = 0xFFFFFFFF; // Read as many as possible
+
+ status = NPEnumResource(
+ EnumHandle,
+ &EntriesRead,
+ (LPVOID) NetR,
+ &BytesNeeded
+ );
+
+
+ if (status == WN_SUCCESS) {
+
+ SavePtr = NetR;
+
+ for (i = 0; i < EntriesRead; i++, NetR++)
+ {
+ if ( NetR->dwDisplayType == RESOURCEDISPLAYTYPE_TREE)
+ {
+ continue;
+ }
+ else
+ {
+ lpFormattedName = FormattedNameBuf;
+ }
+
+ dwLength = NW_MAX_SERVER_LEN + 1;
+
+ status = NPFormatNetworkName( NetR->lpRemoteName,
+ lpFormattedName,
+ &dwLength,
+ WNFMT_INENUM,
+ 0 );
+
+ lpFormattedName = FormattedNameBuf;
+
+ if ( status != WN_SUCCESS )
+ {
+ continue;
+ }
+
+ if ( dwLength > NW_MAX_SERVER_LEN + 1 )
+ {
+ continue;
+ }
+
+ if (ControlIdMatch != 0) {
+
+ INT Result;
+
+ //
+ // Add the server to list only if it's not found
+ // in the alternate list specified by ControlIdMatch.
+ //
+ Result = SendDlgItemMessageW(
+ DialogHandle,
+ ControlIdMatch,
+ FindMessage,
+ (WPARAM) -1,
+ (LPARAM) lpFormattedName
+ );
+
+ if (Result == LB_ERR) {
+
+ //
+ // Server name not found. Add to list.
+ //
+ SendDlgItemMessageW(
+ DialogHandle,
+ ControlId,
+ Message,
+ 0,
+ (LPARAM) lpFormattedName
+ );
+ }
+ }
+ else {
+
+ //
+ // No alternate list. Just add all servers.
+ //
+ SendDlgItemMessageW(
+ DialogHandle,
+ ControlId,
+ Message,
+ 0,
+ (LPARAM) lpFormattedName
+ );
+ }
+
+ }
+
+ NetR = SavePtr;
+
+ }
+ else if (status != WN_NO_MORE_ENTRIES) {
+
+ status = GetLastError();
+
+ if (status == WN_MORE_DATA) {
+
+ //
+ // Original buffer was too small. Free it and allocate
+ // the recommended size and then some to get as many
+ // entries as possible.
+ //
+
+ (void) LocalFree((HLOCAL) NetR);
+
+ BytesNeeded += NW_ENUM_EXTRA_BYTES;
+
+ if ((NetR = (LPVOID) LocalAlloc(
+ 0,
+ BytesNeeded
+ )) == NULL) {
+
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ goto CleanExit;
+ }
+ }
+ else {
+ goto CleanExit;
+ }
+ }
+
+ } while (status != WN_NO_MORE_ENTRIES);
+
+ if (status == WN_NO_MORE_ENTRIES) {
+ status = NO_ERROR;
+ }
+
+CleanExit:
+
+ if (EnumHandle != (HANDLE) NULL) {
+ (void) NPCloseEnum(EnumHandle);
+ }
+
+ if (NetR != NULL) {
+ (void) LocalFree((HLOCAL) NetR);
+ }
+}
+
+VOID
+NwpAddTreeNamesToControl(
+ IN HWND DialogHandle,
+ IN INT ControlId,
+ IN UINT Message,
+ IN INT ControlIdMatch OPTIONAL,
+ IN UINT FindMessage
+ )
+/*++
+
+Routine Description:
+
+ This function enumerates the NDS tree on the network and adds each
+ tree name to the specified Windows control.
+
+ If ControlIdMatch is specified (i.e. non 0), only trees that are
+ not found in ControlIdMatch list are added to the list specified
+ by ControlId.
+
+Arguments:
+
+ DialogHandle - Supplies a handle to the Windows dialog.
+
+ ControlId - Supplies id which specifies the control.
+
+ Message - Supplies the window management message to add string.
+
+ ControlIdMatch - Supplies the control ID which contains server
+ names that should not be in ControlId.
+
+ FindMessage - Supplies the window management message to find
+ string.
+
+Return Value:
+
+ TRUE - the message was processed.
+
+ FALSE - the message was not processed.
+
+--*/
+{
+ DWORD status = ERROR_NO_NETWORK;
+ HANDLE EnumHandle = (HANDLE) NULL;
+
+ LPNETRESOURCE NetR = NULL;
+ LPNETRESOURCEW SavePtr;
+ WCHAR FormattedNameBuf[MAX_NDS_NAME_CHARS];
+
+ LPWSTR lpFormattedName;
+ DWORD dwLength;
+
+ DWORD BytesNeeded = 512;
+ DWORD EntriesRead;
+ DWORD i;
+
+ //
+ // Retrieve the list of trees on the network
+ //
+ status = NPOpenEnum(
+ RESOURCE_GLOBALNET,
+ 0,
+ 0,
+ NULL,
+ &EnumHandle
+ );
+
+ if (status != NO_ERROR) {
+ EnumHandle = (HANDLE) NULL;
+ goto CleanExit;
+ }
+
+ //
+ // Allocate buffer to get trees on the net.
+ //
+ if ((NetR = (LPVOID) LocalAlloc(
+ 0,
+ BytesNeeded
+ )) == NULL) {
+
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ goto CleanExit;
+ }
+
+ do {
+
+ EntriesRead = 0xFFFFFFFF; // Read as many as possible
+
+ status = NPEnumResource(
+ EnumHandle,
+ &EntriesRead,
+ (LPVOID) NetR,
+ &BytesNeeded
+ );
+
+
+ if (status == WN_SUCCESS) {
+
+ SavePtr = NetR;
+
+ for (i = 0; i < EntriesRead; i++, NetR++)
+ {
+ if ( NetR->dwDisplayType == RESOURCEDISPLAYTYPE_TREE)
+ {
+ lpFormattedName = (LPWSTR) FormattedNameBuf;
+ }
+ else
+ {
+ continue;
+ }
+
+ dwLength = NW_MAX_SERVER_LEN + 1;
+
+ status = NPFormatNetworkName( NetR->lpRemoteName,
+ lpFormattedName,
+ &dwLength,
+ WNFMT_INENUM,
+ 0 );
+
+ lpFormattedName = FormattedNameBuf;
+
+ if ( status != WN_SUCCESS )
+ {
+ continue;
+ }
+
+ if ( dwLength > NW_MAX_SERVER_LEN + 1 )
+ {
+ continue;
+ }
+
+ if (ControlIdMatch != 0) {
+
+ INT Result;
+
+ //
+ // Add the server to list only if it's not found
+ // in the alternate list specified by ControlIdMatch.
+ //
+ Result = SendDlgItemMessageW(
+ DialogHandle,
+ ControlIdMatch,
+ FindMessage,
+ (WPARAM) -1,
+ (LPARAM) lpFormattedName
+ );
+
+ if (Result == LB_ERR) {
+
+ //
+ // Server name not found. Add to list.
+ //
+ SendDlgItemMessageW(
+ DialogHandle,
+ ControlId,
+ Message,
+ 0,
+ (LPARAM) lpFormattedName
+ );
+ }
+ }
+ else {
+
+ //
+ // No alternate list. Just add all servers.
+ //
+ SendDlgItemMessageW(
+ DialogHandle,
+ ControlId,
+ Message,
+ 0,
+ (LPARAM) lpFormattedName
+ );
+ }
+
+ }
+
+ NetR = SavePtr;
+
+ }
+ else if (status != WN_NO_MORE_ENTRIES) {
+
+ status = GetLastError();
+
+ if (status == WN_MORE_DATA) {
+
+ //
+ // Original buffer was too small. Free it and allocate
+ // the recommended size and then some to get as many
+ // entries as possible.
+ //
+
+ (void) LocalFree((HLOCAL) NetR);
+
+ BytesNeeded += NW_ENUM_EXTRA_BYTES;
+
+ if ((NetR = (LPVOID) LocalAlloc(
+ 0,
+ BytesNeeded
+ )) == NULL) {
+
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ goto CleanExit;
+ }
+ }
+ else {
+ goto CleanExit;
+ }
+ }
+
+ } while (status != WN_NO_MORE_ENTRIES);
+
+ if (status == WN_NO_MORE_ENTRIES) {
+ status = NO_ERROR;
+ }
+
+CleanExit:
+
+ if (EnumHandle != (HANDLE) NULL) {
+ (void) NPCloseEnum(EnumHandle);
+ }
+
+ if (NetR != NULL) {
+ (void) LocalFree((HLOCAL) NetR);
+ }
+}
+
+
+VOID
+NwpAddToComboBox(
+ IN HWND DialogHandle,
+ IN INT ControlId,
+ IN LPWSTR pszNone OPTIONAL,
+ IN BOOL AllowNone
+ )
+{
+
+ NwpAddServersToControl(DialogHandle, ControlId, CB_ADDSTRING, 0, 0);
+
+ //
+ // Combo-box will contain at least the <NONE> entry in its list.
+ //
+
+ if ( ARGUMENT_PRESENT(pszNone) && AllowNone) {
+
+ SendDlgItemMessageW(
+ DialogHandle,
+ ControlId,
+ CB_INSERTSTRING,
+ (WPARAM) -1,
+ (LPARAM) pszNone
+ );
+ }
+}
+
+
+DWORD
+NwpGetUserCredential(
+ IN HWND hParent,
+ IN LPWSTR Unc,
+ IN DWORD err,
+ IN LPWSTR pszConnectAsUserName,
+ OUT LPWSTR *UserName,
+ OUT LPWSTR *Password
+ )
+/*++
+
+Routine Description:
+
+ This function puts up a popup dialog for the user, whose default
+ credential denied browse directory access, to enter the correct
+ credential. If this function returns successful, the pointers
+ to memory allocated for the user entered username and password
+ are returned.
+
+Arguments:
+
+ Unc - Supplies the container name in \\Server\Volume format
+ under which the user wants to browse directories.
+
+ UserName - Receives the pointer to memory allocated for the
+ username gotten from the dialog. This pointer must be freed
+ with LocalFree when done.
+
+ Password - Receives the pointer to memory allocated for the
+ password gotten from the dialog. This pointer must be freed
+ with LocalFree when done.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+{
+ DWORD status;
+ INT Result;
+ HWND DialogHandle = hParent? hParent : NwpGetParentHwnd();
+ DWORD UserNameSize = NW_MAX_USERNAME_LEN + 1;
+ DWORD PasswordSize = NW_MAX_PASSWORD_LEN + 1;
+ CONNECTDLGPARAM ConnectParam;
+
+ *UserName = NULL;
+ *Password = NULL;
+
+ if (DialogHandle == NULL) {
+ return ERROR_WINDOW_NOT_DIALOG;
+ }
+
+ //
+ // Allocate memory to return UserName and Password
+ //
+ if ((*UserName = (LPVOID) LocalAlloc(
+ 0,
+ UserNameSize * sizeof(WCHAR)
+ )) == NULL)
+ {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ //
+ // Allocate memory to return UserName and Password
+ //
+ if ((*Password = (LPVOID) LocalAlloc(
+ 0,
+ PasswordSize * sizeof(WCHAR)
+ )) == NULL)
+ {
+
+ (void) LocalFree( *UserName );
+ *UserName = NULL;
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ ConnectParam.UncPath = Unc;
+ ConnectParam.ConnectAsUserName = pszConnectAsUserName;
+ ConnectParam.UserName = *UserName;
+ ConnectParam.Password = *Password;
+ ConnectParam.UserNameSize = UserNameSize;
+ ConnectParam.PasswordSize = PasswordSize;
+ ConnectParam.LastConnectionError = err;
+
+ Result = DialogBoxParamW(
+ hmodNW,
+ MAKEINTRESOURCEW(DLG_NETWORK_CREDENTIAL),
+ DialogHandle,
+ NwpConnectDlgProc,
+ (LPARAM) &ConnectParam
+ );
+
+ if ( Result == -1 )
+ {
+ status = GetLastError();
+ KdPrint(("NWPROVAU: NwpGetUserCredential: DialogBox failed %lu\n",
+ status));
+ goto ErrorExit;
+ }
+ else if ( Result == IDCANCEL )
+ {
+ //
+ // Cancel was pressed.
+ //
+ status = WN_CANCEL;
+ goto ErrorExit;
+ }
+
+ return NO_ERROR;
+
+ErrorExit:
+ (void) LocalFree((HLOCAL) *UserName);
+ (void) LocalFree((HLOCAL) *Password);
+ *UserName = NULL;
+ *Password = NULL;
+
+ return status;
+}
+
+
+BOOL
+WINAPI
+NwpConnectDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM WParam,
+ LPARAM LParam
+ )
+/*++
+
+Routine Description:
+
+ This function is the window management message handler which
+ initializes, and reads user input from the dialog put up when the
+ user fails to browse a directory on the default credential.
+
+Arguments:
+
+ DialogHandle - Supplies a handle to display the dialog.
+
+ Message - Supplies the window management message.
+
+ LParam - Supplies the pointer to a buffer which on input
+ contains the \\Server\Volume string under which the user
+ needs to type in a new credential before browsing. On
+ output, this pointer contains the username and password
+ strings entered to the dialog box.
+
+Return Value:
+
+ TRUE - the message was processed.
+
+ FALSE - the message was not processed.
+
+--*/
+{
+ static PCONNECTDLGPARAM pConnectParam;
+
+ switch (Message) {
+
+ case WM_INITDIALOG:
+
+ pConnectParam = (PCONNECTDLGPARAM) LParam;
+
+ //
+ // Position dialog
+ //
+ // NwpCenterDialog(DialogHandle);
+
+
+ //
+ // Display the \\Server\Volume string.
+ //
+ SetDlgItemTextW( DialogHandle,
+ ID_VOLUME_PATH,
+ pConnectParam->UncPath );
+
+ if ( pConnectParam->LastConnectionError == NO_ERROR )
+ {
+ WCHAR szTemp[256];
+
+ if ( LoadString( hmodNW, IDS_CONNECT_NO_ERROR_TEXT,
+ szTemp, sizeof( szTemp )/sizeof(WCHAR)))
+ {
+ SetDlgItemTextW( DialogHandle,
+ ID_CONNECT_TEXT,
+ szTemp );
+ }
+ }
+
+ //
+ // Username is limited to 256 characters.
+ //
+ SendDlgItemMessageW(
+ DialogHandle,
+ ID_CONNECT_AS,
+ EM_LIMITTEXT,
+ pConnectParam->UserNameSize - 1, // minus the space for '\0'
+ 0
+ );
+
+ //
+ // Password is limited to 256 characters.
+ //
+ SendDlgItemMessageW(
+ DialogHandle,
+ ID_CONNECT_PASSWORD,
+ EM_LIMITTEXT,
+ pConnectParam->PasswordSize - 1, // minus the space for '\0'
+ 0
+ );
+
+ //
+ // Display the User name string.
+ //
+ if ( pConnectParam->ConnectAsUserName )
+ {
+ SetDlgItemTextW( DialogHandle,
+ ID_CONNECT_AS,
+ pConnectParam->ConnectAsUserName );
+ }
+
+ return TRUE;
+
+
+ case WM_COMMAND:
+
+ switch (LOWORD(WParam)) {
+
+ case IDOK:
+
+ GetDlgItemTextW(
+ DialogHandle,
+ ID_CONNECT_AS,
+ pConnectParam->UserName,
+ pConnectParam->UserNameSize
+ );
+
+ GetDlgItemTextW(
+ DialogHandle,
+ ID_CONNECT_PASSWORD,
+ pConnectParam->Password,
+ pConnectParam->PasswordSize
+ );
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("\n\t[OK] was pressed\n"));
+ KdPrint(("\tUserName : %ws\n",
+ pConnectParam->UserName));
+ KdPrint(("\tPassword : %ws\n",
+ pConnectParam->Password));
+ }
+#endif
+
+ EndDialog(DialogHandle, (INT) IDOK); // OK
+
+ return TRUE;
+
+
+ case IDCANCEL:
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("\n\t[CANCEL] was pressed\n"));
+ }
+#endif
+
+ EndDialog(DialogHandle, (INT) IDCANCEL); // CANCEL
+
+ return TRUE;
+
+ case IDHELP:
+
+ WinHelp( DialogHandle,
+ NW_HELP_FILE,
+ HELP_CONTEXT,
+ IDH_DLG_NETWORK_CREDENTIAL_HELP );
+
+ return TRUE;
+ }
+ }
+
+ //
+ // We didn't process this message
+ //
+ return FALSE;
+}
+
+
+
+VOID
+NwpCenterDialog(
+ HWND hwnd
+ )
+/*++
+
+Routine Description:
+
+ This routine positions the dialog centered horizontally and 1/3
+ down the screen vertically. It should be called by the dlg proc
+ when processing the WM_INITDIALOG message. This code is stolen
+ from Visual Basic written by GustavJ.
+
+ Screen
+ -----------------------------
+ | 1/3 Above |
+ | --------------- |
+ | | Dialog | |
+ | | | |
+ | --------------- |
+ | 2/3 Below |
+ | |
+ -----------------------------
+
+Arguments:
+
+ hwnd - Supplies the handle to the dialog.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ RECT rect;
+ LONG nx; // New x
+ LONG ny; // New y
+ LONG width;
+ LONG height;
+
+ GetWindowRect( hwnd, &rect );
+
+ width = rect.right - rect.left;
+ height = rect.bottom - rect.top;
+
+ nx = (GetSystemMetrics(SM_CXSCREEN) - width) / 2;
+ ny = (GetSystemMetrics(SM_CYSCREEN) - height) / 3;
+
+ MoveWindow( hwnd, nx, ny, width, height, FALSE );
+}
+
+
+
+HWND
+NwpGetParentHwnd(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function gets the parent window handle so that a
+ dialog can be displayed in the current context.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Returns the parent window handle if successful; NULL otherwise.
+
+--*/
+{
+ HWND hwnd;
+ LONG lWinStyle;
+
+
+ //
+ // Get the current focus. This is presumably the button
+ // that was last clicked.
+ //
+ hwnd = GetFocus();
+
+ //
+ // We must make sure that we don't return the window handle
+ // for a child window. Hence, we traverse up the ancestors
+ // of this window handle until we find a non-child window.
+ // Then, we return that handle. If we ever find a NULL window
+ // handle before finding a non-child window, we are unsuccessful
+ // and will return NULL.
+ //
+ // Note on the bit manipulation below. A window is either
+ // an overlapped window, a popup window or a child window.
+ // Hence, we OR together the possible bit combinations of these
+ // possibilities. This should tell us which bits are used in
+ // the window style dword (although we know this becomes 0xC000
+ // today, we don't know if these will ever change later). Then,
+ // we AND the bit combination we with the given window style
+ // dword, and compare the result with WS_CHILD. This tells us
+ // whether or not the given window is a child window.
+ //
+ while (hwnd) {
+
+ lWinStyle = GetWindowLong (hwnd, GWL_STYLE);
+
+ if ((lWinStyle & (WS_OVERLAPPED | WS_POPUP | WS_CHILD)) != WS_CHILD) {
+ return hwnd;
+ }
+
+ hwnd = GetParent(hwnd);
+ }
+
+ return NULL;
+}
+
+
+BOOL
+WINAPI
+NwpPasswdPromptDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM WParam,
+ LPARAM LParam
+ )
+/*++
+
+Routine Description:
+
+ This function is the window management message handler for
+ the change password dialog.
+
+Arguments:
+
+ DialogHandle - Supplies a handle to display the dialog.
+
+ Message - Supplies the window management message.
+
+ LParam - Supplies the pointer to a buffer which on input
+ contains the Server string under which the user
+ needs to type in a new credential before browsing. On
+ output, this pointer contains the username and server
+ strings entered to the dialog box.
+
+Return Value:
+
+ TRUE - the message was processed.
+
+ FALSE - the message was not processed.
+
+--*/
+{
+ LPWSTR UserName;
+ LPWSTR ServerName;
+ static LPWSTR Password;
+ static DWORD PasswordSize ;
+ INT Result ;
+ PPROMPTDLGPARAM DlgParams ;
+ DWORD nLength;
+
+ WCHAR szLocation[MAX_PATH];
+
+ szLocation[0] = 0;
+
+
+ switch (Message) {
+
+ case WM_INITDIALOG:
+
+ DlgParams = (PPROMPTDLGPARAM) LParam;
+ UserName = DlgParams->UserName ;
+ ServerName = DlgParams->ServerName ;
+ Password = DlgParams->Password ;
+ PasswordSize = DlgParams->PasswordSize ;
+
+ ASSERT(ServerName) ;
+
+ //
+ // Position dialog
+ //
+ NwpCenterDialog(DialogHandle);
+
+ //
+ // Get the string "Server" or "Context".
+ //
+ nLength = LoadStringW(
+ hmodNW,
+ IS_TREE(ServerName) ? IDS_CONTEXT : IDS_SERVER,
+ szLocation,
+ sizeof(szLocation) / sizeof(szLocation[0])
+ );
+
+ if ( nLength == 0) {
+ szLocation[0] = 0; // missing text, but still works
+ }
+ SetDlgItemTextW(DialogHandle, ID_LOCATION, szLocation);
+
+ //
+ // Format the server/context string. Note we reuse the
+ // location buffer.
+ //
+ RtlZeroMemory(szLocation, sizeof(szLocation)) ;
+ nLength = wcslen(ServerName) ;
+
+ if ( IS_TREE(ServerName) &&
+ (nLength+1 < (sizeof(szLocation)/sizeof(szLocation[0]))))
+ {
+ //
+ // NDS tree & context
+ //
+ WCHAR *pszTmp ;
+
+ wcscpy(szLocation, ServerName+1) ; // skip the * if tree\context
+
+ if (pszTmp = wcschr(szLocation, L'\\'))
+ {
+ *pszTmp = L'(' ;
+ wcscat(szLocation, L")") ;
+ }
+ }
+ else
+ {
+ wcsncpy(szLocation, ServerName, nLength) ;
+ }
+
+ //
+ // show the user and server names.
+ //
+ SetDlgItemTextW(DialogHandle, ID_SERVER, szLocation);
+ SetDlgItemTextW(DialogHandle, ID_USERNAME, UserName);
+
+ //
+ // set limits
+ //
+ SendDlgItemMessageW( DialogHandle,
+ ID_PASSWORD,
+ EM_LIMITTEXT,
+ PasswordSize - 1, // minus space for '\0'
+ 0 );
+
+ return TRUE;
+
+
+ case WM_COMMAND:
+
+ switch (LOWORD(WParam)) {
+
+
+ case IDHELP:
+
+ DialogBoxParamW(
+ hmodNW,
+ MAKEINTRESOURCEW(DLG_ENTER_PASSWORD_HELP),
+ (HWND) DialogHandle,
+ NwpHelpDlgProc,
+ (LPARAM) 0
+ );
+
+ return TRUE;
+
+ case IDOK:
+
+ Result = GetDlgItemTextW( DialogHandle,
+ ID_PASSWORD,
+ Password,
+ PasswordSize
+ );
+
+ EndDialog(DialogHandle, (INT) IDOK); // OK
+
+ return TRUE;
+
+
+ case IDCANCEL:
+
+
+ EndDialog(DialogHandle, (INT) IDCANCEL); // CANCEL
+
+ return TRUE;
+ }
+ }
+
+ //
+ // We didn't process this message
+ //
+ return FALSE;
+}
+
+
+
+BOOL
+WINAPI
+NwpChangePasswordDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM WParam,
+ LPARAM LParam
+ )
+/*++
+
+Routine Description:
+
+ This function is the window management message handler for
+ the change password dialog.
+
+Arguments:
+
+ DialogHandle - Supplies a handle to display the dialog.
+
+ Message - Supplies the window management message.
+
+ LParam - Supplies the pointer to a buffer which on input
+ contains the Server string under which the user
+ needs to type in a new credential before browsing. On
+ output, this pointer contains the username and server
+ strings entered to the dialog box.
+
+Return Value:
+
+ TRUE - the message was processed.
+
+ FALSE - the message was not processed.
+
+--*/
+{
+ static PCHANGE_PASS_DLG_PARAM pChangePassParam ;
+
+ switch (Message)
+ {
+ case WM_INITDIALOG:
+
+ pChangePassParam = (PCHANGE_PASS_DLG_PARAM) LParam;
+
+ NwpCenterDialog(DialogHandle);
+
+
+ SetDlgItemTextW(DialogHandle, ID_SERVER, pChangePassParam->TreeName);
+ SetDlgItemTextW(DialogHandle, ID_USERNAME, pChangePassParam->UserName);
+
+ //
+ // set limits
+ //
+ SendDlgItemMessageW( DialogHandle,
+ ID_OLD_PASSWORD,
+ EM_LIMITTEXT,
+ NW_MAX_PASSWORD_LEN, // minus space for '\0'
+ 0 );
+
+ SendDlgItemMessageW( DialogHandle,
+ ID_NEW_PASSWORD,
+ EM_LIMITTEXT,
+ NW_MAX_PASSWORD_LEN, // minus space for '\0'
+ 0 );
+
+ SendDlgItemMessageW( DialogHandle,
+ ID_CONFIRM_PASSWORD,
+ EM_LIMITTEXT,
+ NW_MAX_PASSWORD_LEN, // minus space for '\0'
+ 0 );
+
+ return TRUE;
+
+
+ case WM_COMMAND:
+
+ switch (LOWORD(WParam))
+ {
+ case IDHELP:
+
+ DialogBoxParamW(
+ hmodNW,
+ MAKEINTRESOURCEW(DLG_CHANGE_PASSWORD_HELP),
+ (HWND) DialogHandle,
+ NwpHelpDlgProc,
+ (LPARAM) 0
+ );
+
+ return TRUE;
+
+ case IDOK:
+ {
+ INT Result;
+ WCHAR szConfirmPassword[NW_MAX_PASSWORD_LEN + 1];
+ UNICODE_STRING OldPasswordStr;
+ UNICODE_STRING NewPasswordStr;
+ UCHAR EncodeSeed = NW_ENCODE_SEED2;
+
+ Result = GetDlgItemTextW( DialogHandle,
+ ID_OLD_PASSWORD,
+ pChangePassParam->OldPassword,
+ NW_MAX_PASSWORD_LEN
+ );
+
+ Result = GetDlgItemTextW( DialogHandle,
+ ID_NEW_PASSWORD,
+ pChangePassParam->NewPassword,
+ NW_MAX_PASSWORD_LEN
+ );
+
+ Result = GetDlgItemTextW( DialogHandle,
+ ID_CONFIRM_PASSWORD,
+ szConfirmPassword,
+ NW_MAX_PASSWORD_LEN
+ );
+
+ if ( wcscmp( pChangePassParam->NewPassword,
+ szConfirmPassword ) )
+ {
+ //
+ // New and Confirm passwords don't match!
+ //
+ (void) NwpMessageBoxError(
+ DialogHandle,
+ IDS_CHANGE_PASSWORD_TITLE,
+ IDS_CHANGE_PASSWORD_CONFLICT,
+ 0,
+ NULL,
+ MB_OK | MB_ICONSTOP );
+
+ SetDlgItemText( DialogHandle,
+ ID_NEW_PASSWORD,
+ L"" );
+
+ SetDlgItemText( DialogHandle,
+ ID_CONFIRM_PASSWORD,
+ L"" );
+
+ SetFocus( GetDlgItem( DialogHandle,
+ ID_NEW_PASSWORD ));
+
+ return TRUE;
+ }
+
+ RtlInitUnicodeString( &OldPasswordStr,
+ pChangePassParam->OldPassword );
+ RtlInitUnicodeString( &NewPasswordStr,
+ pChangePassParam->NewPassword );
+ RtlRunEncodeUnicodeString(&EncodeSeed, &OldPasswordStr);
+ RtlRunEncodeUnicodeString(&EncodeSeed, &NewPasswordStr);
+
+ EndDialog(DialogHandle, (INT) IDOK); // OK
+
+ return TRUE;
+ }
+
+ case IDCANCEL:
+
+ EndDialog(DialogHandle, (INT) IDCANCEL); // CANCEL
+
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+ }
+
+ //
+ // We didn't process this message
+ //
+ return FALSE;
+}
+
+
+
+BOOL
+WINAPI
+NwpHelpDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM WParam,
+ LPARAM LParam
+ )
+//
+// This dialog is used for both Help and Question dialogs.
+//
+{
+ switch (Message) {
+
+ case WM_INITDIALOG:
+
+ NwpCenterDialog(DialogHandle);
+ return TRUE;
+
+ case WM_COMMAND:
+
+ switch (LOWORD(WParam))
+ {
+
+ case IDOK:
+ case IDCANCEL:
+ EndDialog(DialogHandle, IDOK);
+ return TRUE;
+
+ case IDYES:
+ EndDialog(DialogHandle, IDYES);
+ return TRUE;
+
+ case IDNO:
+ EndDialog(DialogHandle, IDNO);
+ return TRUE;
+
+ default:
+ return FALSE ;
+ }
+ }
+
+ //
+ // We didn't process this message
+ //
+ return FALSE;
+}
+
+
+
+VOID
+NwpGetNoneString(
+ LPWSTR pszNone,
+ DWORD cBufferSize
+ )
+/*++
+
+Routine Description:
+
+ This function gets the <NONE> string from the resource.
+
+Arguments:
+
+ pszNone - Supplies the buffer to store the string.
+
+ cBufferSize - Supplies the buffer size in bytes.
+
+Return Value:
+
+ None.
+--*/
+{
+ INT TextLength;
+
+
+ TextLength = LoadStringW( hmodNW,
+ IDS_NONE,
+ pszNone,
+ cBufferSize / sizeof( WCHAR) );
+
+ if ( TextLength == 0 )
+ *pszNone = 0;
+}
+
+
+
+VOID
+NwpAddNetWareTreeConnectionsToList(
+ IN HWND DialogHandle,
+ IN LPWSTR NtUserName,
+ IN LPDWORD lpdwUserLuid,
+ IN INT ControlId
+ )
+{
+ DWORD status = NO_ERROR;
+ DWORD BufferSize = 2048; // 2KB Buffer
+ BYTE pBuffer[2048];
+ DWORD EntriesRead;
+ INT Result ;
+
+ status = NwGetConnectedTrees( NtUserName,
+ pBuffer,
+ BufferSize,
+ &EntriesRead,
+ lpdwUserLuid );
+
+ if ( status == NO_ERROR && EntriesRead > 0 )
+ {
+ PCONN_INFORMATION pConnInfo = (PCONN_INFORMATION) pBuffer;
+ WCHAR tempTreeName[NW_MAX_TREE_LEN + 1];
+ DWORD dwSize;
+
+ while ( EntriesRead-- )
+ {
+ dwSize = sizeof( CONN_INFORMATION );
+ dwSize += pConnInfo->HostServerLength;
+ dwSize += pConnInfo->UserNameLength;
+
+ RtlZeroMemory( tempTreeName,
+ ( NW_MAX_TREE_LEN + 1 ) * sizeof(WCHAR) );
+
+ wcsncpy( tempTreeName,
+ pConnInfo->HostServer,
+ pConnInfo->HostServerLength / sizeof(WCHAR) );
+
+ CharUpperW( tempTreeName );
+
+ //
+ // Add the tree name to the list only
+ // if it's not added already.
+ //
+ Result = SendDlgItemMessageW( DialogHandle,
+ ControlId,
+ LB_FINDSTRING,
+ (WPARAM) -1,
+ (LPARAM) tempTreeName );
+
+ if (Result == LB_ERR)
+ {
+ Result = SendDlgItemMessageW( DialogHandle,
+ ControlId,
+ LB_ADDSTRING,
+ 0,
+ (LPARAM) tempTreeName );
+
+ if (Result != LB_ERR)
+ {
+ LPWSTR lpNdsUserName = NULL;
+
+ lpNdsUserName = (LPWSTR) LocalAlloc( LMEM_ZEROINIT,
+ pConnInfo->UserNameLength +
+ sizeof(WCHAR) );
+
+ if ( lpNdsUserName )
+ {
+ wcsncpy( lpNdsUserName,
+ pConnInfo->UserName,
+ pConnInfo->UserNameLength / sizeof(WCHAR) );
+
+ SendDlgItemMessageW( DialogHandle,
+ ControlId,
+ LB_SETITEMDATA,
+ (WPARAM) Result, // index of entry
+ (LPARAM) lpNdsUserName );
+ }
+ }
+ }
+
+ pConnInfo = (PCONN_INFORMATION) ( ((BYTE *)pConnInfo) + dwSize );
+ }
+ }
+ else
+ {
+ *lpdwUserLuid = 0;
+ }
+}
+
+
+
+BOOL
+WINAPI
+NwpChangePasswdDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM WParam,
+ LPARAM LParam
+ )
+/*++
+
+Routine Description:
+
+ This function is the window management message handler for
+ the change password dialog.
+
+Arguments:
+
+ DialogHandle - Supplies a handle to display the dialog.
+
+ Message - Supplies the window management message.
+
+ LParam - Supplies the pointer to a buffer which on input
+ contains the Server string under which the user
+ needs to type in a new credential before browsing. On
+ output, this pointer contains the username and server
+ strings entered to the dialog box.
+
+Return Value:
+
+ TRUE - the message was processed.
+
+ FALSE - the message was not processed.
+
+--*/
+{
+ static LPWSTR UserName;
+ static LPWSTR ServerName;
+ static DWORD UserNameSize ;
+ static DWORD ServerNameSize ;
+ INT Result ;
+ PPASSWDDLGPARAM DlgParams ;
+
+ switch (Message) {
+
+ case WM_INITDIALOG:
+
+ DlgParams = (PPASSWDDLGPARAM) LParam;
+ UserName = DlgParams->UserName ;
+ ServerName = DlgParams->ServerName ;
+ UserNameSize = DlgParams->UserNameSize ;
+ ServerNameSize = DlgParams->ServerNameSize ;
+
+ //
+ // Position dialog
+ //
+ NwpCenterDialog(DialogHandle);
+
+
+ //
+ // setup the default user and server names
+ //
+ SetDlgItemTextW(DialogHandle, ID_SERVER, ServerName);
+ SetDlgItemTextW(DialogHandle, ID_USERNAME, UserName);
+
+ //
+ // Username is limited to 256 characters.
+ //
+ SendDlgItemMessageW(DialogHandle,
+ ID_USERNAME,
+ EM_LIMITTEXT,
+ UserNameSize - 1, // minus space for '\0'
+ 0 );
+
+ //
+ // Server is limited to 256 characters.
+ //
+ SendDlgItemMessageW( DialogHandle,
+ ID_SERVER,
+ EM_LIMITTEXT,
+ ServerNameSize - 1, // minus space for '\0'
+ 0 );
+
+ //
+ // Add trees to list
+ //
+ NwpAddToComboBox( DialogHandle,
+ ID_SERVER,
+ NULL,
+ FALSE ) ;
+
+ return TRUE;
+
+
+ case WM_COMMAND:
+
+ switch (LOWORD(WParam)) {
+
+ case IDOK:
+
+ Result = GetDlgItemTextW( DialogHandle,
+ ID_USERNAME,
+ UserName,
+ UserNameSize );
+
+ Result = GetDlgItemTextW( DialogHandle,
+ ID_SERVER,
+ ServerName,
+ ServerNameSize );
+
+ EndDialog(DialogHandle, (INT) IDOK); // OK
+
+ return TRUE;
+
+
+ case IDCANCEL:
+
+ EndDialog(DialogHandle, (INT) IDCANCEL); // CANCEL
+
+ return TRUE;
+ }
+ }
+
+ //
+ // We didn't process this message
+ //
+ return FALSE;
+}
+
+
+
+BOOL
+WINAPI
+NwpOldPasswordDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM WParam,
+ LPARAM LParam
+ )
+//
+// This dialog lets the user retype the old password for a specific
+// server/tree.
+//
+{
+ static POLD_PW_DLG_PARAM OldPwParam;
+
+
+ switch (Message) {
+
+ case WM_INITDIALOG:
+
+ OldPwParam = (POLD_PW_DLG_PARAM) LParam;
+
+ NwpCenterDialog(DialogHandle);
+
+ SetDlgItemTextW(DialogHandle, ID_SERVER, OldPwParam->FailedServer);
+
+ SendDlgItemMessageW(
+ DialogHandle,
+ ID_PASSWORD,
+ EM_LIMITTEXT,
+ NW_MAX_PASSWORD_LEN,
+ 0
+ );
+
+ return TRUE;
+
+ case WM_COMMAND:
+
+ switch (LOWORD(WParam))
+ {
+
+ case IDCANCEL:
+ EndDialog(DialogHandle, IDCANCEL);
+ return TRUE;
+
+ case IDOK:
+ {
+ UCHAR EncodeSeed = NW_ENCODE_SEED2;
+ UNICODE_STRING PasswordStr;
+
+
+ RtlZeroMemory(
+ OldPwParam->OldPassword,
+ NW_MAX_PASSWORD_LEN * sizeof(WCHAR)
+ );
+
+ GetDlgItemTextW(
+ DialogHandle,
+ ID_PASSWORD,
+ OldPwParam->OldPassword,
+ NW_MAX_PASSWORD_LEN
+ );
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: Retyped password %ws\n",
+ OldPwParam->OldPassword));
+ }
+#endif
+ RtlInitUnicodeString(&PasswordStr, OldPwParam->OldPassword);
+ RtlRunEncodeUnicodeString(&EncodeSeed, &PasswordStr);
+
+ EndDialog(DialogHandle, IDOK);
+ return TRUE;
+ }
+
+ case IDHELP:
+
+ DialogBoxParamW(
+ hmodNW,
+ MAKEINTRESOURCEW(DLG_ENTER_OLD_PW_HELP),
+ (HWND) DialogHandle,
+ NwpHelpDlgProc,
+ (LPARAM) 0
+ );
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+ }
+
+ //
+ // We didn't process this message
+ //
+ return FALSE;
+}
+
+
+
+BOOL
+WINAPI
+NwpAltUserNameDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM WParam,
+ LPARAM LParam
+ )
+//
+// This dialog lets the user retype an alternate username for a specific
+// server/tree.
+//
+{
+ static PUSERNAME_DLG_PARAM UserNameParam;
+
+ switch (Message)
+ {
+ case WM_INITDIALOG:
+
+ UserNameParam = (PUSERNAME_DLG_PARAM) LParam;
+
+ NwpCenterDialog(DialogHandle);
+
+ //
+ // Display the server/tree.
+ //
+ SetDlgItemTextW(
+ DialogHandle,
+ ID_SERVER,
+ UserNameParam->TreeServerName
+ );
+
+ //
+ // Username is limited to 256 characters.
+ //
+ SendDlgItemMessageW(
+ DialogHandle,
+ ID_USERNAME,
+ EM_LIMITTEXT,
+ 256,
+ 0
+ );
+
+ SetDlgItemTextW(
+ DialogHandle,
+ ID_USERNAME,
+ UserNameParam->UserName
+ );
+
+ return TRUE;
+
+ case WM_COMMAND:
+
+ switch (LOWORD(WParam))
+ {
+
+ case IDCANCEL:
+ EndDialog(DialogHandle, IDCANCEL);
+ return TRUE;
+
+ case IDOK:
+ {
+ RtlZeroMemory(
+ UserNameParam->UserName,
+ 256 * sizeof(WCHAR)
+ );
+
+ GetDlgItemTextW(
+ DialogHandle,
+ ID_USERNAME,
+ UserNameParam->UserName,
+ 256
+ );
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWPROVAU: Retyped username %ws\n",
+ UserNameParam->UserName));
+ }
+#endif
+
+ EndDialog(DialogHandle, IDOK);
+ return TRUE;
+ }
+
+ case IDHELP:
+
+ DialogBoxParamW(
+ hmodNW,
+ MAKEINTRESOURCEW(DLG_ENTER_ALT_UN_HELP),
+ (HWND) DialogHandle,
+ NwpHelpDlgProc,
+ (LPARAM) 0
+ );
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+ }
+
+ //
+ // We didn't process this message
+ //
+ return FALSE;
+}
+
+
+VOID
+EnableAddRemove(
+ IN HWND DialogHandle
+ )
+/*++
+
+Routine Description:
+
+ This function enables and disables Add and Remove buttons
+ based on list box selections.
+
+Arguments:
+
+ DialogHandle - Supplies a handle to the windows dialog.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ INT cSel;
+
+
+ cSel = SendDlgItemMessageW(
+ DialogHandle,
+ ID_INACTIVE_LIST,
+ LB_GETSELCOUNT,
+ 0,
+ 0
+ );
+ EnableWindow(GetDlgItem(DialogHandle, ID_ADD), cSel != 0);
+
+ cSel = SendDlgItemMessageW(
+ DialogHandle,
+ ID_ACTIVE_LIST,
+ LB_GETSELCOUNT,
+ 0,
+ 0
+ );
+ EnableWindow(GetDlgItem(DialogHandle, ID_REMOVE), cSel != 0);
+}
+
+
+
+
+BOOL
+WINAPI
+NwpSelectServersDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM WParam,
+ LPARAM LParam
+ )
+/*++
+
+Routine Description:
+
+ This routine displays two listboxes--an active list which includes
+ the trees which the user is currently attached to, and an inactive
+ list which displays the rest of the trees on the net. The user
+ can select trees and move them back and forth between the list
+ boxes. When OK is selected, the password is changed on the trees
+ in the active listbox.
+
+Arguments:
+
+ DialogHandle - Supplies a handle to the login dialog.
+
+ Message - Supplies the window management message.
+
+ LParam - Supplies the user credential: username, old password and
+ new password. The list of trees from the active listbox
+ and the number of entries are returned.
+
+Return Value:
+
+ TRUE - the message was processed.
+
+ FALSE - the message was not processed.
+
+--*/
+{
+ WCHAR szServer[NW_MAX_SERVER_LEN + 1];
+ static PCHANGE_PW_DLG_PARAM Credential;
+ DWORD status;
+ DWORD UserLuid = 0;
+ DWORD ActiveListBoxCount;
+ DWORD InactiveListBoxCount;
+
+ switch (Message) {
+
+ case WM_INITDIALOG:
+
+ //
+ // Get the user credential passed in.
+ //
+ Credential = (PCHANGE_PW_DLG_PARAM) LParam;
+
+ //
+ // Position dialog
+ //
+ NwpCenterDialog(DialogHandle);
+
+ //
+ // Display the username.
+ //
+ SetDlgItemTextW(
+ DialogHandle,
+ ID_USERNAME,
+ Credential->UserName
+ );
+
+ //
+ // Display current NetWare tree connections in the active box.
+ //
+ NwpAddNetWareTreeConnectionsToList(
+ DialogHandle,
+ Credential->UserName,
+ &UserLuid,
+ ID_ACTIVE_LIST
+ );
+
+ //
+ // Display all trees in inactive list box.
+ //
+ NwpAddTreeNamesToControl(
+ DialogHandle,
+ ID_INACTIVE_LIST,
+ LB_ADDSTRING,
+ ID_ACTIVE_LIST,
+ LB_FINDSTRING
+ );
+
+ //
+ // Highlight the first entry of the inactive list.
+ //
+ SetFocus(GetDlgItem(DialogHandle, ID_INACTIVE_LIST));
+ SendDlgItemMessageW(
+ DialogHandle,
+ ID_INACTIVE_LIST,
+ LB_SETSEL,
+ TRUE,
+ 0
+ );
+
+ EnableAddRemove(DialogHandle);
+
+ ActiveListBoxCount = SendDlgItemMessageW( DialogHandle,
+ ID_ACTIVE_LIST,
+ LB_GETCOUNT,
+ 0,
+ 0 );
+
+ InactiveListBoxCount = SendDlgItemMessageW( DialogHandle,
+ ID_INACTIVE_LIST,
+ LB_GETCOUNT,
+ 0,
+ 0 );
+
+ if ( ActiveListBoxCount == 0 &&
+ InactiveListBoxCount == 0 )
+ {
+ (void) NwpMessageBoxError( DialogHandle,
+ IDS_NETWARE_TITLE,
+ IDS_NO_TREES_DETECTED,
+ 0,
+ NULL,
+ MB_OK );
+
+ EndDialog(DialogHandle, (INT) IDOK);
+ }
+
+ return TRUE;
+
+ case WM_COMMAND:
+
+ switch (LOWORD(WParam))
+ {
+ case IDOK:
+ {
+ if ((status = NwpGetTreesAndChangePw(
+ DialogHandle,
+ szServer,
+ UserLuid,
+ Credential
+ ) != NO_ERROR))
+ {
+ //
+ // System error: e.g. out of memory error.
+ //
+ (void) NwpMessageBoxError(
+ DialogHandle,
+ IDS_CHANGE_PASSWORD_TITLE,
+ 0,
+ status,
+ NULL,
+ MB_OK | MB_ICONSTOP );
+
+ EndDialog(DialogHandle, (INT) -1);
+ return TRUE;
+ }
+
+ EndDialog(DialogHandle, (INT) IDOK);
+ return TRUE;
+ }
+
+ case IDCANCEL:
+
+ EndDialog(DialogHandle, (INT) IDCANCEL);
+ return TRUE;
+
+
+ case IDHELP:
+
+ DialogBoxParamW(
+ hmodNW,
+ MAKEINTRESOURCEW(DLG_PW_SELECT_SERVERS_HELP),
+ (HWND) DialogHandle,
+ NwpHelpDlgProc,
+ (LPARAM) 0
+ );
+
+ return TRUE;
+
+
+
+ case ID_ACTIVE_LIST:
+ //
+ // When Remove is pressed the highlights follows
+ // the selected entries over to the other
+ // list box.
+ //
+ if (HIWORD(WParam) == LBN_SELCHANGE) {
+ //
+ // Unselect the other listbox
+ //
+ SendDlgItemMessageW(
+ DialogHandle,
+ ID_INACTIVE_LIST,
+ LB_SETSEL,
+ FALSE,
+ (LPARAM) -1
+ );
+
+ EnableAddRemove(DialogHandle);
+ }
+
+ return TRUE;
+
+ case ID_INACTIVE_LIST:
+
+ //
+ // When Add is pressed the highlights follows
+ // the selected entries over to the other
+ // list box.
+ //
+ if (HIWORD(WParam) == LBN_SELCHANGE) {
+ //
+ // Unselect the other listbox
+ //
+ SendDlgItemMessageW(
+ DialogHandle,
+ ID_ACTIVE_LIST,
+ LB_SETSEL,
+ FALSE,
+ (LPARAM) -1
+ );
+
+ EnableAddRemove(DialogHandle);
+ }
+
+ return TRUE;
+
+ case ID_ADD:
+ case ID_REMOVE:
+ {
+ INT idFrom;
+ INT idTo;
+ INT cSel;
+ INT SelItem;
+ INT iNew;
+ HWND hwndActiveList;
+ HWND hwndInactiveList;
+
+ hwndActiveList = GetDlgItem(DialogHandle, ID_ACTIVE_LIST);
+ hwndInactiveList = GetDlgItem(DialogHandle, ID_INACTIVE_LIST);
+
+ //
+ // Set to NOREDRAW to TRUE
+ //
+ SetWindowLong(hwndActiveList, GWL_STYLE,
+ GetWindowLong(hwndActiveList, GWL_STYLE) | LBS_NOREDRAW);
+ SetWindowLong(hwndInactiveList, GWL_STYLE,
+ GetWindowLong(hwndInactiveList, GWL_STYLE) | LBS_NOREDRAW);
+
+ if (LOWORD(WParam) == ID_ADD)
+ {
+ idFrom = ID_INACTIVE_LIST;
+ idTo = ID_ACTIVE_LIST;
+ }
+ else
+ {
+ idFrom = ID_ACTIVE_LIST;
+ idTo = ID_INACTIVE_LIST;
+ }
+
+ //
+ // Move current selection from idFrom to idTo
+ //
+
+ //
+ // Loop terminates when selection count is zero
+ //
+ for (;;) {
+ //
+ // Get count of selected strings
+ //
+ cSel = SendDlgItemMessageW(
+ DialogHandle,
+ idFrom,
+ LB_GETSELCOUNT,
+ 0,
+ 0
+ );
+
+ if (cSel == 0) {
+ //
+ // No more selection
+ //
+ break;
+ }
+
+ //
+ // To avoid flickering as strings are added and
+ // removed from listboxes, no redraw is set for
+ // both listboxes until we are transfering the
+ // last entry, in which case we reenable redraw
+ // so that both listboxes are updated once.
+ //
+ if (cSel == 1) {
+
+ SetWindowLong(
+ hwndActiveList,
+ GWL_STYLE,
+ GetWindowLong(hwndActiveList, GWL_STYLE) & ~LBS_NOREDRAW
+ );
+
+ SetWindowLong(
+ hwndInactiveList,
+ GWL_STYLE,
+ GetWindowLong(hwndInactiveList, GWL_STYLE) & ~LBS_NOREDRAW
+ );
+ }
+
+ //
+ // Get index of first selected item
+ //
+ SendDlgItemMessageW(
+ DialogHandle,
+ idFrom,
+ LB_GETSELITEMS,
+ 1,
+ (LPARAM) &SelItem
+ );
+
+ //
+ // Get server name from list
+ //
+ SendDlgItemMessageW(
+ DialogHandle,
+ idFrom,
+ LB_GETTEXT,
+ (WPARAM) SelItem,
+ (LPARAM) (LPWSTR) szServer
+ );
+
+ //
+ // Remove entry from list
+ //
+ SendDlgItemMessageW(
+ DialogHandle,
+ idFrom,
+ LB_DELETESTRING,
+ (WPARAM) SelItem,
+ 0
+ );
+
+ //
+ // Add entry to list
+ //
+ iNew = SendDlgItemMessageW(
+ DialogHandle,
+ idTo,
+ LB_ADDSTRING,
+ 0,
+ (LPARAM) (LPWSTR) szServer
+ );
+
+ //
+ // Select the new item
+ //
+ if (iNew != LB_ERR) {
+ SendDlgItemMessageW(
+ DialogHandle,
+ idTo,
+ LB_SETSEL,
+ TRUE,
+ iNew
+ );
+ }
+
+ } // for
+
+ EnableAddRemove(DialogHandle);
+
+ } // ID_ADD or ID_REMOVE
+ }
+
+ }
+
+ //
+ // We didn't process this message
+ //
+ return FALSE;
+}
+
+DWORD
+NwpGetTreesAndChangePw(
+ IN HWND DialogHandle,
+ IN LPWSTR ServerBuf,
+ IN DWORD UserLuid,
+ IN PCHANGE_PW_DLG_PARAM Credential
+ )
+/*++
+
+Routine Description:
+
+ This routine gets the selected trees from the active list box
+ and asks the redirector to change password on them. If a failure
+ is encountered when changing password on a tree, we pop up appropriate
+ dialogs to see if user can fix problem.
+
+Arguments:
+
+ DialogHandle - Supplies a handle to the login dialog.
+
+Return Value:
+
+ TRUE - the message was processed.
+
+ FALSE - the message was not processed.
+
+--*/
+{
+ DWORD status;
+ HCURSOR Cursor;
+ WCHAR tempOldPassword[NW_MAX_PASSWORD_LEN + 1];
+ WCHAR tempNewPassword[NW_MAX_PASSWORD_LEN + 1];
+ WCHAR tempUserName[MAX_NDS_NAME_CHARS];
+
+ //
+ // Turn cursor into hourglass
+ //
+ Cursor = LoadCursor(NULL, IDC_WAIT);
+ if (Cursor != NULL) {
+ SetCursor(Cursor);
+ ShowCursor(TRUE);
+ }
+
+ Credential->ChangedOne = FALSE;
+ Credential->TreeList = NULL;
+ Credential->UserList = NULL;
+
+ //
+ // Get the number of trees we have to change password on.
+ //
+ Credential->Entries = SendDlgItemMessageW(
+ DialogHandle,
+ ID_ACTIVE_LIST,
+ LB_GETCOUNT,
+ 0,
+ 0 );
+
+ if (Credential->Entries != 0) {
+
+ DWORD Entries; // Number of entries in remaining list
+ DWORD FullIndex; // Index to the whole tree list
+ DWORD i;
+ DWORD BytesNeeded = sizeof(LPWSTR) * Credential->Entries +
+ (NW_MAX_SERVER_LEN + 1) * sizeof(WCHAR) * Credential->Entries;
+ LPBYTE FixedPortion;
+ LPWSTR EndOfVariableData;
+ INT Result;
+
+ Entries = Credential->Entries;
+ Credential->TreeList = LocalAlloc(0, BytesNeeded);
+ Credential->UserList = LocalAlloc(0,
+ sizeof(LPWSTR) * Credential->Entries);
+
+ if (Credential->TreeList == NULL)
+ {
+ KdPrint(("NWPROVAU: No memory to change password\n"));
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ if (Credential->UserList == NULL)
+ {
+ KdPrint(("NWPROVAU: No memory to change password\n"));
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ FixedPortion = (LPBYTE) Credential->TreeList;
+ EndOfVariableData = (LPWSTR) ((DWORD) FixedPortion +
+ ROUND_DOWN_COUNT(BytesNeeded, ALIGN_DWORD));
+
+ for (i = 0; i < Entries; i++)
+ {
+ //
+ // Read the user selected list of servers from the dialog.
+ //
+
+ SendDlgItemMessageW(
+ DialogHandle,
+ ID_ACTIVE_LIST,
+ LB_GETTEXT,
+ (WPARAM) i,
+ (LPARAM) (LPWSTR) ServerBuf );
+
+ NwlibCopyStringToBuffer(
+ ServerBuf,
+ wcslen(ServerBuf),
+ (LPCWSTR) FixedPortion,
+ &EndOfVariableData,
+ &(Credential->TreeList)[i] );
+
+ Result = SendDlgItemMessageW( DialogHandle,
+ ID_ACTIVE_LIST,
+ LB_GETITEMDATA,
+ (WPARAM) i,
+ 0 );
+
+ if ( Result != LB_ERR )
+ {
+ (Credential->UserList)[i] = (LPWSTR) Result;
+ }
+ else
+ {
+ (Credential->UserList)[i] = NULL;
+ }
+
+ FixedPortion += sizeof(LPWSTR);
+ }
+
+ FullIndex = 0;
+
+ do
+ {
+ RtlZeroMemory( tempUserName, sizeof(tempUserName) );
+ RtlZeroMemory( tempOldPassword, sizeof(tempOldPassword) );
+ RtlZeroMemory( tempNewPassword, sizeof(tempNewPassword) );
+ RtlCopyMemory( tempOldPassword,
+ Credential->OldPassword,
+ sizeof(tempOldPassword) );
+ RtlCopyMemory( tempNewPassword,
+ Credential->NewPassword,
+ sizeof(tempNewPassword) );
+
+ if ( (Credential->UserList)[FullIndex] == NULL )
+ {
+ // We don't have any connections to tree <current entry>
+ // Prompt user to supply a user name for which account
+ // we are to change password, or skip . . .
+
+ USERNAME_DLG_PARAM UserNameParam;
+ CHANGE_PASS_DLG_PARAM ChangePassParam;
+
+ UserNameParam.UserName = tempUserName;
+ UserNameParam.TreeServerName = (Credential->TreeList)[FullIndex];
+
+ SetCursor(Cursor);
+ ShowCursor(FALSE);
+
+ Result = DialogBoxParamW(
+ hmodNW,
+ MAKEINTRESOURCEW(DLG_ENTER_ALT_USERNAME),
+ (HWND) DialogHandle,
+ NwpAltUserNameDlgProc,
+ (LPARAM) &UserNameParam );
+
+ Cursor = LoadCursor(NULL, IDC_WAIT);
+
+ if (Cursor != NULL)
+ {
+ SetCursor(Cursor);
+ ShowCursor(TRUE);
+ }
+
+ if ( Result != IDOK )
+ {
+ *((Credential->TreeList)[FullIndex]) = L'\0';
+ goto SkipEntry;
+ }
+
+ // Now go reverify the credentials for the user name
+ // entered by user.
+
+ ChangePassParam.UserName = tempUserName;
+ ChangePassParam.TreeName = (Credential->TreeList)[FullIndex];
+ ChangePassParam.OldPassword = tempOldPassword;
+ ChangePassParam.NewPassword = tempNewPassword;
+
+ SetCursor(Cursor);
+ ShowCursor(FALSE);
+
+ Result = DialogBoxParamW(
+ hmodNW,
+ MAKEINTRESOURCEW(DLG_CHANGE_PASSWORD3),
+ (HWND) DialogHandle,
+ NwpChangePasswordDlgProc,
+ (LPARAM) &ChangePassParam );
+
+ Cursor = LoadCursor(NULL, IDC_WAIT);
+
+ if (Cursor != NULL)
+ {
+ SetCursor(Cursor);
+ ShowCursor(TRUE);
+ }
+
+ if ( Result != IDOK )
+ {
+ *((Credential->TreeList)[FullIndex]) = L'\0';
+ goto SkipEntry;
+ }
+
+ goto Next;
+ }
+ else
+ {
+ wcscpy( tempUserName, (Credential->UserList)[FullIndex] );
+ LocalFree( (Credential->UserList)[FullIndex] );
+ (Credential->UserList)[FullIndex] = NULL;
+ }
+
+ // Test tempUserName with the user name in Credential->UserName
+ // to see if they are similar (i.e. The first part of the
+ // NDS distinguish name matches).
+
+ if ( _wcsnicmp( tempUserName + 3,
+ Credential->UserName,
+ wcslen( Credential->UserName ) ) )
+ {
+ // The names are not similar!
+ // Prompt user to ask if they really want to change
+ // passwords for dis-similar user on tree <current entry>
+ // or skip . . .
+
+ USERNAME_DLG_PARAM UserNameParam;
+ CHANGE_PASS_DLG_PARAM ChangePassParam;
+
+ UserNameParam.UserName = tempUserName;
+ UserNameParam.TreeServerName = (Credential->TreeList)[FullIndex];
+
+ SetCursor(Cursor);
+ ShowCursor(FALSE);
+
+ Result = DialogBoxParamW(
+ hmodNW,
+ MAKEINTRESOURCEW(DLG_ENTER_ALT_USERNAME),
+ (HWND) DialogHandle,
+ NwpAltUserNameDlgProc,
+ (LPARAM) &UserNameParam );
+
+ Cursor = LoadCursor(NULL, IDC_WAIT);
+
+ if (Cursor != NULL)
+ {
+ SetCursor(Cursor);
+ ShowCursor(TRUE);
+ }
+
+ if ( Result != IDOK )
+ {
+ *((Credential->TreeList)[FullIndex]) = L'\0';
+ goto SkipEntry;
+ }
+
+ // Now go reverify the credentials for the user name
+ // entered by user.
+
+ ChangePassParam.UserName = tempUserName;
+ ChangePassParam.TreeName = (Credential->TreeList)[FullIndex];
+ ChangePassParam.OldPassword = tempOldPassword;
+ ChangePassParam.NewPassword = tempNewPassword;
+
+ SetCursor(Cursor);
+ ShowCursor(FALSE);
+
+ Result = DialogBoxParamW(
+ hmodNW,
+ MAKEINTRESOURCEW(DLG_CHANGE_PASSWORD3),
+ (HWND) DialogHandle,
+ NwpChangePasswordDlgProc,
+ (LPARAM) &ChangePassParam );
+
+ Cursor = LoadCursor(NULL, IDC_WAIT);
+
+ if (Cursor != NULL)
+ {
+ SetCursor(Cursor);
+ ShowCursor(TRUE);
+ }
+
+ if ( Result != IDOK )
+ {
+ *((Credential->TreeList)[FullIndex]) = L'\0';
+ goto SkipEntry;
+ }
+ }
+
+Next:
+ status = NwrChangePassword(
+ NULL, // Reserved
+ UserLuid,
+ tempUserName,
+ tempOldPassword, // Encoded passwords
+ tempNewPassword,
+ (LPWSTR) (Credential->TreeList)[FullIndex] );
+
+ if (status == ERROR_INVALID_PASSWORD)
+ {
+ OLD_PW_DLG_PARAM OldPasswordParam;
+
+#if DBG
+ IF_DEBUG(LOGON)
+ {
+ KdPrint(("NWPROVAU: First attempt: wrong password on %ws\n",
+ (Credential->TreeList)[FullIndex]));
+ }
+#endif
+
+ //
+ // Display dialog to let user type in an alternate
+ // old password.
+ //
+
+ //
+ // Set up old password buffer to receive from dialog.
+ //
+ OldPasswordParam.OldPassword = tempOldPassword;
+
+ OldPasswordParam.FailedServer = (Credential->TreeList)[FullIndex];
+
+ SetCursor(Cursor);
+ ShowCursor(FALSE);
+
+ Result = DialogBoxParamW(
+ hmodNW,
+ MAKEINTRESOURCEW(DLG_ENTER_OLD_PASSWORD),
+ (HWND) DialogHandle,
+ NwpOldPasswordDlgProc,
+ (LPARAM) &OldPasswordParam );
+
+ Cursor = LoadCursor(NULL, IDC_WAIT);
+
+ if (Cursor != NULL)
+ {
+ SetCursor(Cursor);
+ ShowCursor(TRUE);
+ }
+
+ if (Result == IDOK)
+ {
+ //
+ // Retry change password with alternate old password on
+ // the failed server.
+ //
+ status = NwrChangePassword(
+ NULL, // Reserved
+ UserLuid,
+ tempUserName,
+ tempOldPassword, // Alternate old password
+ tempNewPassword,
+ (LPWSTR) (Credential->TreeList)[FullIndex] );
+ }
+ }
+
+ if (status != NO_ERROR)
+ {
+ //
+ // Either unrecoverable failure or user failed to change
+ // password on second attempt.
+ //
+#if DBG
+ IF_DEBUG(LOGON)
+ {
+ KdPrint(("NWPROVAU: Failed to change password on %ws %lu\n",
+ (Credential->TreeList)[FullIndex], status));
+ }
+#endif
+
+ // Pop up error dialog to let user know that password
+ // could not be changed.
+
+ (void) NwpMessageBoxError(
+ DialogHandle,
+ IDS_CHANGE_PASSWORD_TITLE,
+ IDS_CP_FAILURE_WARNING,
+ status,
+ (LPWSTR) (Credential->TreeList)[FullIndex],
+ MB_OK | MB_ICONSTOP );
+
+ *((Credential->TreeList)[FullIndex]) = L'\0';
+
+ if (status == ERROR_NOT_ENOUGH_MEMORY)
+ return status;
+ }
+
+SkipEntry:
+ //
+ // Continue to change password on the rest of the entries
+ //
+ FullIndex++;
+ Entries = Credential->Entries - FullIndex;
+
+ } while (Entries);
+
+ //
+ // Caller is responsible for freeing TreeList
+ //
+ }
+
+ SetCursor(Cursor);
+ ShowCursor(FALSE);
+
+ return NO_ERROR;
+}
+
+
+BOOL
+WINAPI
+NwpChangePasswordSuccessDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM WParam,
+ LPARAM LParam
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+ DialogHandle - Supplies a handle to the login dialog.
+
+ Message - Supplies the window management message.
+
+Return Value:
+
+ TRUE - the message was processed.
+
+ FALSE - the message was not processed.
+
+--*/
+{
+ static PCHANGE_PW_DLG_PARAM Credential;
+ DWORD Count;
+ DWORD i;
+
+ switch (Message)
+ {
+ case WM_INITDIALOG:
+
+ //
+ // Get the user credential passed in.
+ //
+ Credential = (PCHANGE_PW_DLG_PARAM) LParam;
+
+ //
+ // Position dialog
+ //
+ NwpCenterDialog(DialogHandle);
+
+ //
+ // Put list of NetWare trees that we changed password on in the
+ // list box.
+ // ID_SERVER );
+ for ( i = 0; i < Credential->Entries; i++ )
+ {
+ if ( *((Credential->TreeList)[i]) != L'\0' )
+ {
+ SendDlgItemMessageW( DialogHandle,
+ ID_SERVER,
+ LB_ADDSTRING,
+ 0,
+ (LPARAM) (Credential->TreeList)[i] );
+ }
+ }
+
+ Count = SendDlgItemMessageW( DialogHandle,
+ ID_SERVER,
+ LB_GETCOUNT,
+ 0,
+ 0 );
+
+ if ( Count == 0 )
+ EndDialog(DialogHandle, 0);
+
+ return TRUE;
+
+
+ case WM_COMMAND:
+
+ switch (LOWORD(WParam))
+ {
+ case IDOK:
+ case IDCANCEL:
+ EndDialog(DialogHandle, 0);
+ return TRUE;
+ }
+ }
+
+ //
+ // We didn't process this message
+ //
+ return FALSE;
+}
+
+
diff --git a/private/nw/svcdlls/nwwks/client/nwdlg.h b/private/nw/svcdlls/nwwks/client/nwdlg.h
new file mode 100644
index 000000000..e3c84b3dd
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwdlg.h
@@ -0,0 +1,217 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ nwdlg.h
+
+Abstract:
+
+ Dialog ID header for NetWare login dialog.
+
+Author:
+
+ Rita Wong (ritaw) 17-Mar-1993
+
+Revision History:
+
+--*/
+
+#ifndef _NWDLG_INCLUDED_
+#define _NWDLG_INCLUDED_
+
+#include "nwapi.h"
+#include "nwshrc.h"
+
+typedef struct _LOGIN_DLG_PARAMETERS
+{
+ LPWSTR UserName;
+ LPWSTR ServerName;
+ LPWSTR Password;
+ LPWSTR NewUserSid;
+ PLUID pLogonId;
+ DWORD ServerNameSize;
+ DWORD PasswordSize;
+ DWORD LogonScriptOptions;
+
+} LOGINDLGPARAM, *PLOGINDLGPARAM;
+
+typedef struct _PASSWD_DLG_PARAMETERS
+{
+ LPWSTR UserName;
+ LPWSTR ServerName;
+ DWORD UserNameSize;
+ DWORD ServerNameSize;
+
+} PASSWDDLGPARAM, *PPASSWDDLGPARAM;
+
+typedef struct _CHANGE_PW_DLG_PARAM
+{
+ PWCHAR UserName;
+ PWCHAR OldPassword;
+ PWCHAR NewPassword;
+ LPWSTR *TreeList;
+ LPWSTR *UserList;
+ DWORD Entries;
+ BOOL ChangedOne;
+
+} CHANGE_PW_DLG_PARAM, *PCHANGE_PW_DLG_PARAM;
+
+typedef struct _OLD_PW_DLG_PARAM
+{
+ PWCHAR OldPassword;
+ PWCHAR FailedServer;
+
+} OLD_PW_DLG_PARAM, *POLD_PW_DLG_PARAM;
+
+typedef struct _ALT_UN_DLG_PARAM
+{
+ PWCHAR UserName;
+ PWCHAR TreeServerName;
+
+} USERNAME_DLG_PARAM, *PUSERNAME_DLG_PARAM;
+
+typedef struct _PROMPT_DLG_PARAMETERS
+{
+ LPWSTR UserName;
+ LPWSTR ServerName;
+ LPWSTR Password;
+ DWORD PasswordSize;
+
+} PROMPTDLGPARAM, *PPROMPTDLGPARAM;
+
+typedef struct _CONNECT_DLG_PARAMETERS
+{
+ LPWSTR UncPath;
+ LPWSTR ConnectAsUserName;
+ LPWSTR UserName;
+ LPWSTR Password;
+ DWORD UserNameSize;
+ DWORD PasswordSize;
+ DWORD LastConnectionError;
+
+} CONNECTDLGPARAM, *PCONNECTDLGPARAM;
+
+typedef struct _CHANGE_PASS_DLG_PARAM
+{
+ PWCHAR UserName;
+ PWCHAR TreeName;
+ PWCHAR OldPassword;
+ PWCHAR NewPassword;
+
+} CHANGE_PASS_DLG_PARAM, *PCHANGE_PASS_DLG_PARAM;
+
+
+
+#define NW_INVALID_SERVER_CHAR L'.'
+
+BOOL
+WINAPI
+NwpLoginDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM Parameter1,
+ LPARAM Parameter2
+ );
+
+BOOL
+WINAPI
+NwpSelectServersDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM WParam,
+ LPARAM LParam
+ );
+
+BOOL
+WINAPI
+NwpChangePasswdDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM Parameter1,
+ LPARAM Parameter2
+ );
+
+BOOL
+WINAPI
+NwpPasswdPromptDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM Parameter1,
+ LPARAM Parameter2
+ );
+
+BOOL
+WINAPI
+NwpChangePasswordDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM Parameter1,
+ LPARAM Parameter2
+ );
+
+BOOL
+WINAPI
+NwpHelpDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM Parameter1,
+ LPARAM Parameter2
+ );
+
+BOOL
+WINAPI
+NwpChangePasswordSuccessDlgProc(
+ HWND DialogHandle,
+ UINT Message,
+ WPARAM WParam,
+ LPARAM LParam
+ );
+
+INT
+NwpMessageBoxError(
+ IN HWND hwndParent,
+ IN DWORD nTitleId,
+ IN DWORD nBodyId,
+ IN DWORD nParameterId,
+ IN LPWSTR szParameter2,
+ IN UINT nStyle
+ );
+
+INT
+NwpMessageBoxIns(
+ IN HWND hwndParent,
+ IN DWORD TitleId,
+ IN DWORD MessageId,
+ IN LPWSTR *InsertStrings,
+ IN UINT nStyle
+ );
+
+DWORD
+NwpGetUserCredential(
+ IN HWND hwndOwner,
+ IN LPWSTR Unc,
+ IN DWORD LastConnectionError,
+ IN LPWSTR pszConnectAsUserName,
+ OUT LPWSTR *UserName,
+ OUT LPWSTR *Password
+ );
+
+VOID
+NwpSaveLogonCredential(
+ IN LPWSTR NewUser,
+ IN PLUID LogonId OPTIONAL,
+ IN LPWSTR UserName,
+ IN LPWSTR Password,
+ IN LPWSTR PreferredServer OPTIONAL
+ );
+
+DWORD
+NwpSaveLogonScriptOptions(
+ IN LPWSTR CurrentUserSid,
+ IN DWORD LogonScriptOptions
+ );
+
+
+#endif
diff --git a/private/nw/svcdlls/nwwks/client/nwnp.reg b/private/nw/svcdlls/nwwks/client/nwnp.reg
new file mode 100644
index 000000000..4a038b683
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwnp.reg
@@ -0,0 +1,47 @@
+\Registry\MACHINE\SOFTWARE\Classes
+ NetWare_or_Compatible_Network
+ shellex
+ ContextMenuHandlers
+ NetWareMenus
+ = {8e9d6600-f84a-11ce-8daa-00aa004a5691}
+ PropertySheetHandlers
+ NetWarePage
+ = {8e9d6600-f84a-11ce-8daa-00aa004a5691}
+
+ CLSID
+ {8e9d6600-f84a-11ce-8daa-00aa004a5691}
+ = NetWare Objects
+ InProcServer32
+ = nwprovau.dll
+ ThreadingModel = Apartment
+
+ {e3f2bac0-099f-11cf-8daa-00aa004a5691}
+ = NetWare UNC Folder Menu
+ InProcServer32
+ = nwprovau.dll
+ ThreadingModel = Apartment
+
+ {52c68510-09a0-11cf-8daa-00aa004a5691}
+ = NetWare Hood Verbs
+ InProcServer32
+ = nwprovau.dll
+ ThreadingModel = Apartment
+
+ {208D2C60-3AEA-1069-A2D7-08002B30309D}
+ shellex
+ ContextMenuHandlers
+ NetWareMenus
+ = {52c68510-09a0-11cf-8daa-00aa004a5691}
+
+ Folder
+ shellex
+ ContextMenuHandlers
+ NetWareUNCMenu
+ = {e3f2bac0-099f-11cf-8daa-00aa004a5691}
+
+\Registry\MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion
+ Shell Extensions
+ Approved
+ {8e9d6600-f84a-11ce-8daa-00aa004a5691} = Shell extensions for NetWare
+ {e3f2bac0-099f-11cf-8daa-00aa004a5691} = Shell extensions for NetWare
+ {52c68510-09a0-11cf-8daa-00aa004a5691} = Shell extensions for NetWare
diff --git a/private/nw/svcdlls/nwwks/client/nwprovau.def b/private/nw/svcdlls/nwwks/client/nwprovau.def
new file mode 100644
index 000000000..d0a522ce9
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwprovau.def
@@ -0,0 +1,80 @@
+LIBRARY NWPROVAU
+
+DESCRIPTION 'Client Service for NetWare Provider and Authentication Package DLL'
+
+EXPORTS
+
+ NPGetCaps
+
+ NPGetUser
+
+ NPAddConnection
+ NPAddConnection3
+ NPCancelConnection
+ NPGetConnection
+ NPGetConnectionPerformance
+
+ NPOpenEnum
+ NPEnumResource
+
+ NPGetResourceInformation
+ NPGetResourceParent
+
+ NPCloseEnum
+
+ NPFormatNetworkName
+
+ NPLogonNotify
+ NPPasswordChangeNotify
+
+ NPGetUniversalName
+ NPLoadNameSpaces
+
+ LsaApInitializePackage
+ LsaApLogonUser
+ LsaApCallPackage
+ LsaApLogonTerminated
+
+ NwQueryInfo
+ NwSetInfoInRegistry
+ NwSetInfoInWksta
+ NwSetLogonScript
+ NwValidateUser
+
+ NwEnumConnections
+
+ NwAddGWDevice
+ NwDeleteGWDevice
+ NwEnumGWDevices
+
+ NwQueryGatewayAccount
+ NwSetGatewayAccount
+ NwLogonGatewayAccount
+
+ InitializePrintProvidor
+ InitializeDll
+
+ NwRegisterGatewayShare
+ NwClearGatewayShare
+ NwCleanupGatewayShares
+
+ NwGetUserNameForServer
+ NwEncryptChallenge
+
+ GetServiceItemFromList
+ NwDeregisterService
+ NwRegisterService
+ NwGetService
+ NwInitializeServiceProvider
+ NwTerminateServiceProvider
+
+ NwSetLogonOptionsInRegistry
+ NwQueryLogonOptions
+
+ ; RnR2 entry name
+
+ NSPStartup
+
+ ; The following are for shell extensions
+ DllCanUnloadNow PRIVATE ; DLL unloading
+ DllGetClassObject PRIVATE ; Component object model
diff --git a/private/nw/svcdlls/nwwks/client/nwprovau.dlg b/private/nw/svcdlls/nwwks/client/nwprovau.dlg
new file mode 100644
index 000000000..6ac7eed43
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwprovau.dlg
@@ -0,0 +1,385 @@
+1 DLGINCLUDE "nwshrc.h"
+
+DLG_NETWARE_LOGIN DIALOG 47, 26, 202, 156
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Select NetWare Logon"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ LTEXT "Username:", -1, 7, 7, 50, 8
+ LTEXT "", ID_USERNAME, 70, 7, 100, 8
+ GROUPBOX " ", -1, 7, 19, 190, 34
+ AUTORADIOBUTTON "&Preferred Server", ID_PREFERREDSERVER_RB, 12, 18, 70, 10
+ LTEXT "Pr&eferred Server:", -1, 13, 33, 85, 8
+ COMBOBOX ID_SERVER, 100, 31, 90, 56, CBS_DROPDOWN |
+ CBS_AUTOHSCROLL | CBS_SORT | WS_VSCROLL |
+ WS_GROUP | WS_TABSTOP
+ GROUPBOX "" , -1, 7, 59, 190, 54
+ AUTORADIOBUTTON "&Default Tree and Context", ID_DEFAULTCONTEXT_RB, 12, 58, 98, 10
+ LTEXT "&Tree:", -1, 13, 75, 55, 8
+ EDITTEXT ID_DEFAULTTREE, 58, 74, 110, 12, ES_AUTOHSCROLL
+ LTEXT "&Context:", -1, 13, 92, 55, 8
+ EDITTEXT ID_DEFAULTCONTEXT, 58, 91, 110, 12, ES_AUTOHSCROLL
+ AUTOCHECKBOX "&Run Login Script", ID_LOGONSCRIPT, 10, 118, 85, 12
+ DEFPUSHBUTTON "OK", IDOK, 25, 136, 40, 14, WS_GROUP
+ PUSHBUTTON "Cancel", IDCANCEL, 81, 136, 40, 14, WS_GROUP
+ PUSHBUTTON "&Help", IDHELP, 137, 136, 40, 14, WS_GROUP
+END
+
+DLG_NETWORK_CREDENTIAL DIALOG 20, 35, 266, 72
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Enter Network Password"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ LTEXT "Incorrect password or unknown username for:", ID_CONNECT_TEXT, 6, 8, 180, 8
+ LTEXT "Text", ID_VOLUME_PATH, 20, 20, 160, 8
+ LTEXT "&Connect As:", -1, 6, 36, 46, 8
+ EDITTEXT ID_CONNECT_AS, 56, 34, 140, 12, ES_AUTOHSCROLL | WS_GROUP
+ LTEXT "&Password:", -1, 6, 52, 46, 8
+ EDITTEXT ID_CONNECT_PASSWORD, 56, 50, 140, 12, ES_PASSWORD |
+ ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK", IDOK, 220, 6, 40, 14, WS_GROUP
+ PUSHBUTTON "Cancel", IDCANCEL, 220, 22, 40, 14, WS_GROUP
+ PUSHBUTTON "&Help", IDHELP, 220, 38, 40, 14, WS_GROUP
+END
+
+DLG_PASSWORD_PROMPT DIALOG 79, 48, 219, 74
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Enter Password"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ LTEXT "Incorrect password for:", -1, 10, 10, 78, 8
+ LTEXT "", ID_USERNAME, 90, 10, 90, 8
+ LTEXT "", ID_LOCATION, 10, 23, 78, 8
+ LTEXT "", ID_SERVER, 90, 23, 120, 8
+ LTEXT "&Password:", -1, 10, 36, 70, 8
+ EDITTEXT ID_PASSWORD, 90, 35, 90, 12, ES_PASSWORD |
+ ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK", IDOK, 18, 54, 40, 14, WS_GROUP
+ PUSHBUTTON "Cancel", IDCANCEL, 74, 54, 40, 14, WS_GROUP
+ PUSHBUTTON "&Help", IDHELP, 130, 54, 40, 14, WS_GROUP
+END
+
+DLG_PREFERRED_SERVER_HELP DIALOG 10, 21, 263, 140
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Select NetWare Logon Help"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ LTEXT "This dialog allows you to select a Netware® logon.",
+ -1, 9, 10, 245, 9
+ LTEXT "If your network uses NDS, click Default Tree And Context, and type in the tree name and your user context in the boxes.",
+ -1, 9, 20, 245, 18
+ LTEXT "If your network does not use NDS, click Preferred Server and type or select the name of your preferred server. You will be automatically connected to your preferred server every time you log on.",
+ -1, 9, 40, 245, 27
+ LTEXT "If you want your NetWare login script to run when you log on, click Run Login Script.",
+ -1, 9, 70, 245, 18
+ DEFPUSHBUTTON "OK", IDOK, 110, 110, 40, 14
+END
+
+DLG_ENTER_PASSWORD_HELP DIALOG 34, 54, 244, 74
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Enter Password Help"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ LTEXT "When you log on to Windows NT, you are also authenticated on your preferred NetWare server.",
+ -1, 10, 10, 230, 18
+ LTEXT "This is done using your Windows NT username and password. If your NetWare password is different, you will be asked to enter it.",
+ -1, 10, 30, 230, 18, NOT WS_GROUP
+ DEFPUSHBUTTON "OK", IDOK, 107, 54, 40, 14
+END
+
+DLG_CHANGE_PASSWORD DIALOG 22, 24, 217, 111
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Change Password on NetWare"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ LTEXT "&Username:", -1, 10, 10, 43, 8
+ EDITTEXT ID_USERNAME, 90, 9, 118, 12, ES_AUTOHSCROLL | WS_GROUP
+ LTEXT "&Server:", -1, 10, 24, 35, 8
+ COMBOBOX ID_SERVER, 90, 23, 118, 56, CBS_DROPDOWN |
+ CBS_AUTOHSCROLL | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+ LTEXT "&Old Password:", -1, 10, 44, 62, 8
+ EDITTEXT ID_OLD_PASSWORD, 90, 43, 118, 12, ES_PASSWORD |
+ ES_AUTOHSCROLL
+ LTEXT "&New Password:", -1, 10, 58, 54, 8
+ EDITTEXT ID_NEW_PASSWORD, 90, 57, 118, 12, ES_PASSWORD |
+ ES_AUTOHSCROLL
+ LTEXT "&Confirm New Password:", -1, 10, 72, 78, 8
+ EDITTEXT ID_CONFIRM_PASSWORD, 90, 71, 118, 12, ES_PASSWORD |
+ ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK", IDOK, 32, 91, 40, 14, WS_GROUP
+ PUSHBUTTON "Cancel", IDCANCEL, 88, 91, 40, 14, WS_GROUP
+ PUSHBUTTON "&Help", IDHELP, 144, 91, 40, 14, WS_GROUP
+END
+
+DLG_CHANGE_PASSWORD3 DIALOG 22, 24, 217, 111
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Change Password on NetWare"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ LTEXT "&Username:", -1, 10, 10, 40, 8
+ LTEXT "Text", ID_USERNAME, 90, 10, 118, 8
+ LTEXT "&Tree:", -1, 10, 24, 40, 8
+ LTEXT "Text", ID_SERVER, 90, 24, 118, 8
+ LTEXT "&Old Password:", -1, 10, 44, 62, 8
+ EDITTEXT ID_OLD_PASSWORD, 90, 43, 118, 12, ES_PASSWORD |
+ ES_AUTOHSCROLL
+ LTEXT "&New Password:", -1, 10, 58, 54, 8
+ EDITTEXT ID_NEW_PASSWORD, 90, 57, 118, 12, ES_PASSWORD |
+ ES_AUTOHSCROLL
+ LTEXT "&Confirm New Password:", -1, 10, 72, 78, 8
+ EDITTEXT ID_CONFIRM_PASSWORD, 90, 71, 118, 12, ES_PASSWORD |
+ ES_AUTOHSCROLL
+ DEFPUSHBUTTON "OK", IDOK, 32, 91, 40, 14, WS_GROUP
+ PUSHBUTTON "Cancel", IDCANCEL, 88, 91, 40, 14, WS_GROUP
+ PUSHBUTTON "&Help", IDHELP, 144, 91, 40, 14, WS_GROUP
+END
+
+DLG_CHANGE_PASSWORD_HELP DIALOG 34, 54, 244, 74
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Enter Password Help"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ LTEXT "You have indicated that you want to change passwords on a NetWare 4.X NDS tree for a different user. A set of password change credentials are needed for this user.", -1, 10, 10, 230, 40
+ DEFPUSHBUTTON "OK", IDOK, 107, 54, 40, 14
+END
+
+DLG_CHANGE_PASSWORD2 DIALOG 22, 24, 217, 66
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Change Password on NetWare"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ LTEXT "&Username:", -1, 10, 10, 43, 8
+ EDITTEXT ID_USERNAME, 90, 9, 118, 12, ES_AUTOHSCROLL | WS_GROUP
+ LTEXT "&Server:", -1, 10, 24, 35, 8
+ COMBOBOX ID_SERVER, 90, 23, 118, 56, CBS_DROPDOWN |
+ CBS_AUTOHSCROLL | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+ DEFPUSHBUTTON "OK", IDOK, 32, 46, 40, 14, WS_GROUP
+ PUSHBUTTON "Cancel", IDCANCEL, 88, 46, 40, 14, WS_GROUP
+ PUSHBUTTON "&Help", IDHELP, 144, 46, 40, 14, WS_GROUP
+END
+
+DLG_ENTER_OLD_PASSWORD DIALOG 22, 24, 224, 80
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Enter Old Password for NetWare"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ EDITTEXT ID_PASSWORD, 64, 28, 138, 12, ES_PASSWORD |
+ ES_AUTOHSCROLL | WS_GROUP
+ PUSHBUTTON "OK", IDOK, 27, 54, 40, 14
+ PUSHBUTTON "Cancel", IDCANCEL, 90, 54, 40, 14, WS_GROUP
+ LTEXT "Incorrect old password for:", -1, 5, 11, 88, 8
+ LTEXT "TEXT", ID_SERVER, 90, 11, 112, 8
+ PUSHBUTTON "&Help", IDHELP, 153, 54, 40, 14
+ LTEXT "&Old password:", -1, 5, 30, 50, 8
+END
+
+DLG_ENTER_ALT_USERNAME DIALOG 22, 24, 224, 80
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Change Password on NetWare"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ EDITTEXT ID_USERNAME, 10, 22, 125, 12, ES_AUTOHSCROLL | WS_GROUP
+ PUSHBUTTON "OK", IDOK, 27, 54, 40, 14
+ PUSHBUTTON "Cancel", IDCANCEL, 90, 54, 40, 14, WS_GROUP
+ LTEXT "on NetWare NDS tree:", -1, 5, 40, 80, 8
+ LTEXT "TEXT", ID_SERVER, 90, 40, 40, 8
+ PUSHBUTTON "&Help", IDHELP, 153, 54, 40, 14
+ LTEXT "&Enter fully qualified NDS user name:", -1, 5, 10, 140, 8
+END
+
+DLG_PW_SELECT_SERVERS DIALOG 22, 25, 346, 166
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Change Password on NetWare"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ LTEXT "Select NetWare NDS tree to change password on for user:",
+ -1, 5, 18, 200, 17
+ LTEXT "&Do not change password on:", 109, 198, 65, 135, 8
+ LISTBOX ID_INACTIVE_LIST, 198, 77, 143, 83, LBS_SORT |
+ LBS_EXTENDEDSEL | WS_VSCROLL | WS_HSCROLL | WS_GROUP |
+ WS_TABSTOP
+ DEFPUSHBUTTON "OK", IDOK, 301, 7, 40, 14, WS_GROUP
+ PUSHBUTTON "Cancel", IDCANCEL, 301, 26, 40, 14, WS_GROUP
+ PUSHBUTTON "&Help", IDHELP, 301, 45, 40, 14, WS_GROUP
+ LTEXT "&Change password on:", 108, 5, 65, 108, 8
+ LISTBOX ID_ACTIVE_LIST, 5, 76, 143, 83, LBS_SORT |
+ LBS_EXTENDEDSEL | WS_VSCROLL | WS_HSCROLL | WS_GROUP |
+ WS_TABSTOP
+ PUSHBUTTON "<- &Add", ID_ADD, 153, 98, 40, 14
+ PUSHBUTTON "&Remove ->", ID_REMOVE, 153, 121, 40, 14
+ LTEXT "Text", ID_USERNAME, 15, 33, 200, 8
+END
+
+DLG_PW_CHANGED DIALOG 22, 24, 210, 130
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "NetWare Tree Password(s) Changed"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ PUSHBUTTON "OK", IDOK, 85, 108, 40, 14, WS_GROUP
+ LTEXT "Successfully changed passwords on:", -1, 28, 12,
+ 154, 17
+ LISTBOX ID_SERVER, 26, 30, 157, 69, LBS_SORT | WS_VSCROLL |
+ WS_HSCROLL | WS_TABSTOP
+END
+
+DLG_PW_SELECT_SERVERS_HELP DIALOG 22, 38, 244, 154
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Change Password Help"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ LTEXT "This dialog allows you to change your password on one or more NetWare NDS trees.",
+ -1, 10, 10, 230, 18
+ LTEXT "Your Windows NT username and password will be used to change your password on the selected tree.",
+ -1, 10, 79, 230, 18, NOT WS_GROUP
+ DEFPUSHBUTTON "OK", IDOK, 101, 135, 40, 14
+ LTEXT "If your Windows NT old password is not the same as your NetWare NDS tree old password, you will be asked to enter the old password for that particular tree.",
+ -1, 10, 101, 230, 26
+ LTEXT "To select NetWare NDS trees:", -1, 10, 32, 213, 10
+ LTEXT "1. Select one or more trees from the Don't Change Password On box.",
+ -1, 11, 43, 225, 19
+ LTEXT "2. Choose the Add button.", -1, 10, 63, 224, 8
+END
+
+DLG_ENTER_OLD_PW_HELP DIALOG 22, 24, 200, 58
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Enter Old Password Help"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ PUSHBUTTON "OK", IDOK, 78, 38, 40, 14
+ LTEXT "The old password is incorrect. Please retype your old password for the server.",
+ -1, 9, 12, 182, 20
+END
+
+DLG_ENTER_ALT_UN_HELP DIALOG 22, 24, 200, 68
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Enter NetWare User Name Help"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ PUSHBUTTON "OK", IDOK, 78, 48, 40, 14
+ LTEXT "The user name was incorrectly specified. Please retype your user name as a fully qualified distinguished name (DN).",
+ -1, 9, 12, 182, 33
+END
+
+#ifndef NT1057
+DLG_NDS_SUMMARYINFO DIALOG DISCARDABLE 0, 0, 227, 215
+STYLE DS_MODALFRAME | DS_NOIDLEMSG | WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS |
+ WS_CAPTION | WS_SYSMENU |DS_3DLOOK | DS_CONTEXTHELP
+CAPTION "NetWare Directory Services"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ ICON IDI_TREE_ICON,IDC_STATIC,7,7,18,20
+ LTEXT "",IDC_STATIC,7,40,213,1,NOT WS_GROUP
+ LTEXT "Object name:",IDD_NDS_NAME_TXT,7,51,45,8
+ LTEXT "",IDD_NDS_NAME,55,51,134,8
+ LTEXT "Object class:",IDD_NDS_CLASS_TXT,7,70,45,8
+ LTEXT "",IDD_NDS_CLASS,55,70,117,8
+ LTEXT "Comment:",IDD_NDS_COMMENT_TXT,7,89,45,8
+ LTEXT "",IDD_NDS_COMMENT,55,89,117,8
+ LTEXT "",IDD_ERROR,7,130,185,48, NOT WS_VISIBLE
+END
+
+DLG_NDSCONT_SUMMARYINFO DIALOG DISCARDABLE 0, 0, 227, 215
+STYLE DS_MODALFRAME | DS_NOIDLEMSG | WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS |
+ WS_CAPTION | WS_SYSMENU |DS_3DLOOK | DS_CONTEXTHELP
+CAPTION "NetWare Directory Services"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ ICON IDI_NDSCONT_ICON,IDC_STATIC,7,7,18,20
+ LTEXT "",IDC_STATIC,7,40,213,1,NOT WS_GROUP
+ LTEXT "Object name:",IDD_NDS_NAME_TXT,7,51,45,8
+ LTEXT "",IDD_NDS_NAME,55,51,134,8
+ LTEXT "Object class:",IDD_NDS_CLASS_TXT,7,70,45,8
+ LTEXT "",IDD_NDS_CLASS,55,70,117,8
+ LTEXT "Comment:",IDD_NDS_COMMENT_TXT,7,89,45,8
+ LTEXT "",IDD_NDS_COMMENT,55,89,117,8
+ LTEXT "",IDD_ERROR,7,130,185,48, NOT WS_VISIBLE
+END
+
+DLG_SERVER_SUMMARYINFO DIALOG DISCARDABLE 0, 0, 227, 215
+STYLE DS_MODALFRAME | DS_NOIDLEMSG | WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS |
+ WS_CAPTION | WS_SYSMENU |DS_3DLOOK | DS_CONTEXTHELP
+CAPTION "Server information"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ ICON IDI_SERVER_ICON,IDC_STATIC,7,7,18,20
+ LTEXT "",IDD_SERVER_NAME,40,14,150,8
+ LTEXT "",IDC_STATIC,7,40,213,1,NOT WS_GROUP
+ LTEXT "Version :",IDD_SERVER_VERSION_TXT,7,51,40,8
+ LTEXT "",IDD_SERVER_VERSION,55,51,117,8
+ LTEXT "Revision :",IDD_SERVER_REVISION_TXT,7,63,40,8
+ LTEXT "",IDD_SERVER_REVISION,55,63,117,8
+ LTEXT "Comment:",IDD_SERVER_COMMENT_TXT,7,75,40,8, NOT WS_VISIBLE
+ LTEXT "",IDD_SERVER_COMMENT,55,75,117,8, NOT WS_VISIBLE
+ LTEXT "",IDC_STATIC,7,90,213,1,NOT WS_GROUP
+ LTEXT "Connections in use:",IDD_SERVER_CONNECT_TXT,7,98,100,8
+ RTEXT "",IDD_SERVER_CONNECT,130,98,30,8
+ LTEXT "Maximum number of connections:",IDD_SERVER_MAXCON_TXT,7,110,120,8
+ RTEXT "",IDD_SERVER_MAXCON,130,110,30,8
+ LTEXT "",IDD_ERROR,7,150,185,48, NOT WS_VISIBLE
+END
+
+DLG_SHARE_SUMMARYINFO DIALOG DISCARDABLE 0, 0, 227, 215
+STYLE DS_MODALFRAME | DS_NOIDLEMSG | WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS |
+ WS_CAPTION | WS_SYSMENU |DS_3DLOOK | DS_CONTEXTHELP
+CAPTION "General"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ ICON IDI_FOLDER_ICON,IDC_STATIC,7,7,18,20
+ LTEXT "",IDD_SHARE_NAME,40,14,150,8
+ LTEXT "",IDC_STATIC,7,40,213,1,NOT WS_GROUP
+ LTEXT "Server:",IDD_SHARE_SERVER_TXT,7,48,40,8
+ LTEXT "",IDD_SHARE_SERVER,52,48,134,8
+ LTEXT "Path:",IDD_SHARE_PATH_TXT,7,60,40,8
+ LTEXT "",IDD_SHARE_PATH,52,60,134,8,WS_HSCROLL
+ LTEXT "",IDC_STATIC,7,74,185,1,NOT WS_GROUP
+ CONTROL "",IDD_SHARE_USED_SPC_CLR,"Button",BS_OWNERDRAW |
+ WS_DISABLED | WS_BORDER,7,82,10,9
+ LTEXT "Used space:",IDD_SHARE_USED_SPC_TXT,21,82,46,8
+ RTEXT "",IDD_SHARE_USED_SPC,69,82,65,8
+ RTEXT "",IDD_SHARE_USED_SPC_MB,141,82,35,8
+ CONTROL "",IDD_SHARE_FREE_SPC_CLR,"Button",BS_OWNERDRAW |
+ WS_DISABLED | WS_BORDER,7,94,10,9
+ LTEXT "Free space:",IDD_SHARE_FREE_SPC_TXT,21,94,46,8
+ RTEXT "",IDD_SHARE_FREE_SPC,69,94,65,8
+ RTEXT "",IDD_SHARE_FREE_SPC_MB,141,94,35,8
+ LTEXT "",IDC_STATIC,7,106,185,1,NOT WS_GROUP
+ LTEXT "Capacity:",IDD_SHARE_MAX_SPC_TXT,7,118,46,8
+ RTEXT "",IDD_SHARE_MAX_SPC,69,118,65,8
+ RTEXT "",IDD_SHARE_MAX_SPC_MB,141,118,35,8
+ CONTROL "",IDD_SHARE_PIE,"Button",BS_OWNERDRAW | WS_DISABLED,72,
+ 150,82,39
+ LTEXT "Supports long filenames",IDD_SHARE_LFN_TXT,7,130,130,8
+ LTEXT "",IDD_ERROR,7,102,185,48, NOT WS_VISIBLE
+END
+
+DLG_PRINTER_SUMMARYINFO DIALOG DISCARDABLE 0, 0, 227, 215
+STYLE DS_MODALFRAME | DS_NOIDLEMSG | WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS |
+ WS_CAPTION | WS_SYSMENU |DS_3DLOOK | DS_CONTEXTHELP
+CAPTION "General"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ ICON IDI_PRINTER_ICON,IDC_STATIC,7,7,18,20
+ LTEXT "",IDD_PRINTER_NAME,40,14,150,8
+ LTEXT "",IDC_STATIC,7,40,213,1,NOT WS_GROUP
+ LTEXT "Queue file:",IDD_PRINTER_QUEUE_TXT,7,51,42,8
+ LTEXT "",IDD_PRINTER_QUEUE,52,51,134,8
+ LTEXT "",IDD_ERROR,7,102,185,48, NOT WS_VISIBLE
+END
+
+DLG_GLOBAL_WHOAMI DIALOG DISCARDABLE 0, 0, 371, 197
+STYLE DS_MODALFRAME | DS_NOIDLEMSG | WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS |
+ WS_CAPTION | WS_SYSMENU |DS_3DLOOK | DS_CONTEXTHELP | DS_CENTER
+CAPTION "Who Am I"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ ICON IDI_TREE_ICON,IDC_LOGOFRAME,7,7,18,20
+ CTEXT "",IDD_GLOBAL_SERVERLIST_T,40,5,260,32,NOT WS_GROUP
+ CTEXT "Who Am I information for currently attached servers",
+ IDD_GLOBAL_SVRLIST_DESC, 7,38,300,10,NOT WS_GROUP
+ CONTROL "",IDD_GLOBAL_SERVERLIST,"SysListView32",WS_BORDER |
+ WS_VSCROLL | WS_HSCROLL | LVS_REPORT | LVS_SORTASCENDING ,
+ 7,48,357,142
+ DEFPUSHBUTTON "OK",IDOK,314,7,50,14
+ PUSHBUTTON "&Detach",IDD_DETACH,314,25,50,14
+END
+#endif
diff --git a/private/nw/svcdlls/nwwks/client/nwprovau.rc b/private/nw/svcdlls/nwwks/client/nwprovau.rc
new file mode 100644
index 000000000..746f74b37
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwprovau.rc
@@ -0,0 +1,136 @@
+#include <windows.h>
+#include <commctrl.h>
+
+#include "nwdlg.h"
+#include "nwshrc.h"
+#include "nwprovau.dlg"
+
+#include <ntverp.h>
+
+STRINGTABLE
+BEGIN
+ IDS_NONE, "<None>"
+ IDS_SERVER, "Server:"
+ IDS_CONTEXT, "Tree(Context):"
+ IDS_NETWARE_PRINT_CAPTION, "NetWare Print Share"
+ IDS_NOTHING_TO_CONFIGURE, "There is nothing to configure for this port."
+ IDS_NETWARE_TITLE, "NetWare Network"
+ IDS_AUTH_FAILURE_TITLE, "NetWare Authentication Failure"
+ IDS_NO_PREFERRED, "You have not been authenticated on any server. No preferred server will be set. Do you want to continue?
+If you choose Yes, you can select a preferred server later in Control Panel."
+ IDS_LOGIN_FAILURE_WARNING, "You cannot be authenticated on %2!ws! due to the following reason: %1!ws!
+Do you want to select another preferred server or context?"
+ IDS_AUTH_FAILURE_WARNING, "You cannot be authenticated on %2!ws! due to the following reason: %1!ws!
+Do you really want to set the preferred server or context to %2!ws!?"
+ IDS_CHANGE_PASSWORD_INFO, "You may need to change your password separately on NetWare 2.X and
+3.X servers that are not part of a password synchronization scheme.
+Use SETPASS (located in the SYS\\PUBLIC directory on NetWare servers)
+to do this."
+ IDS_INVALID_SERVER, "The server name is invalid."
+ IDS_PASSWORD_HAS_EXPIRED, "Your password on %1 has expired with %2 grace logins remaining. Please change it with the SETPASS utility."
+ IDS_PASSWORD_HAS_EXPIRED0, "Your password on %1 has expired with %2 grace logins remaining. Please change it by pressing Ctrl-Alt-Del and choosing the Change Password button."
+ IDS_AUTH_ACC_RESTRICTION, "You cannot be authenticated on %2!ws! because of an account or station restriction. Check that you have not exceeded your account balance or the number of concurrent connections allowed.
+Do you really want to set the preferred server or context to %2!ws!?"
+ IDS_LOGIN_ACC_RESTRICTION, "You cannot be authenticated on %2!ws! because of an account or station restriction. Check that you have not exceeded your account balance or the number of concurrent connections allowed.
+Do you want to select another preferred server or context?"
+ IDS_PASSWORD_HAS_EXPIRED1, "Your password on %1 has expired. Please change it with the SETPASS utility."
+ IDS_PASSWORD_HAS_EXPIRED2, "Your password on %1 has expired. Please change it by pressing Ctrl-Alt-Del and choosing the Change Password button."
+ IDS_BAD_PASSWORDS, "You have not specified the old and new passwords. Your password was not changed."
+ IDS_CHANGE_PASSWORD_TITLE, "NetWare Password Change Failure"
+ IDS_START_WORKSTATION_FIRST, "Please start the Client Service for NetWare before changing your password."
+ IDS_START_WORKSTATION_FIRST_NTAS, "Please start the Gateway Service for NetWare before changing your password."
+ IDS_LOGIN_DISABLED, "Logins to this server has been disabled."
+ IDS_CONNECT_NO_ERROR_TEXT, "Type your password to log in to:"
+ IDS_TREE_NAME_MISSING, "Please provide a NDS Tree name."
+ IDS_CONTEXT_MISSING , "Please provide a NDS Tree context."
+ IDS_SERVER_MISSING , "Please provide a Server name, or select <None>."
+ IDS_CONTEXT_AUTH_FAILURE_WARNING, "You cannot be authenticated on NDS Tree(Context): %1 due to the following reason:\n %2\nDo you really want to set the NDS Tree(Context) to %1?"
+ IDS_CP_FAILURE_WARNING, "Your password could not be changed on %2!ws! due to the following reason: %1!ws!."
+ IDS_CHANGE_PASSWORD_CONFLICT, "The new password entered does not match the confirm password value, please enter new password again."
+ IDS_NO_TREES_DETECTED, "No NetWare NDS trees were detected, unable to perform change password operation."
+END
+
+#ifndef NT1057
+STRINGTABLE DISCARDABLE
+BEGIN
+(IDS_VERBS_BASE + IDO_VERB_WHOAMI), "&Who Am I..."
+(IDS_VERBS_BASE + IDO_VERB_LOGOUT), "&Log Out"
+(IDS_VERBS_BASE + IDO_VERB_ATTACHAS), "&Attach As..."
+(IDS_VERBS_BASE + IDO_VERB_GLOBALWHOAMI), "&Who Am I..."
+(IDS_VERBS_BASE + IDO_VERB_SETDEFAULTCONTEXT), "Set &Current Context"
+(IDS_VERBS_BASE + IDO_VERB_MAPNETWORKDRIVE), "&Map Network Drive..."
+(IDS_VERBS_BASE + IDO_VERB_TREEWHOAMI), "&Who Am I..."
+
+(IDS_VERBS_HELP_BASE + IDO_VERB_WHOAMI), "Displays the status of your connection to the server."
+(IDS_VERBS_HELP_BASE + IDO_VERB_LOGOUT), "Logs your computer out from the server."
+(IDS_VERBS_HELP_BASE + IDO_VERB_ATTACHAS), "Establishes a connection to the server."
+(IDS_VERBS_HELP_BASE + IDO_VERB_GLOBALWHOAMI), "Displays a list of your connections to NetWare servers."
+(IDS_VERBS_HELP_BASE + IDO_VERB_SETDEFAULTCONTEXT), "Sets the selected NDS container as the current context."
+(IDS_VERBS_HELP_BASE + IDO_VERB_MAPNETWORKDRIVE), "Assigns a drive letter to a network resource."
+(IDS_VERBS_HELP_BASE + IDO_VERB_TREEWHOAMI), "Displays the status of your connection to the NDS container."
+END
+
+STRINGTABLE DISCARDABLE
+BEGIN
+IDS_TITLE_LOGOUT, "Logging out"
+IDS_TITLE_WHOAMI, "WhoAmI Information"
+
+IDS_MESSAGE_CONTEXT_CHANGED "Your default name context changed from \n\r %1 \n\r to \n\r %2."
+IDS_MESSAGE_LOGOUT_QUESTION, "You are logged in to this server.\nDo you want to log out first?"
+IDS_MESSAGE_LOGOUT_FAILED, "You were unable to log out. A program may still be using this server."
+IDS_MESSAGE_LOGOUT_FROM_SERVER_FAILED, "You were unable to log out from %1. A program may still be using this server."
+IDS_MESSAGE_LOGOUT_CONFIRM, "If you log out from this server, you will no longer be connected to shared resources on it. Are you sure you want to log out now?"
+IDS_MESSAGE_NOT_ATTACHED, "This server is not attached."
+IDS_MESSAGE_NOT_ATTACHED_TO_TREE, "This NDS container is not attached."
+IDS_MESSAGE_DETACHED, "You have detached from the server."
+IDS_MESSAGE_ATTACHED, "Server:%1\nUser name:%2\nConnection type:%3"
+IDS_MESSAGE_ATTACHED_TO_TREE, "NDS Container:%1\nUser name:%2\nConnection type:%3"
+IDS_BYTES, "%s bytes"
+IDS_ORDERKB, "%sKB"
+IDS_ORDERMB, "%sMB"
+IDS_ORDERGB, "%sGB"
+IDS_ORDERTB, "%sTB"
+IDS_COLUMN_NAME, "Name"
+IDS_COLUMN_CONN_TYPE, "Type"
+IDS_COLUMN_CONN_NUMBER, "Conn #"
+IDS_COLUMN_USER, "User name"
+IDS_COLUMN_STATUS, "Status"
+IDS_STATE_NOT_LOGGED_IN "Not logged in"
+IDS_LOGIN_TYPE_NDS "NDS"
+IDS_LOGIN_TYPE_BINDERY "Bindery"
+IDS_LOGIN_STATUS_SEPARATOR, ", "
+IDS_LOGIN_STATUS_AUTHENTICATED "Authenticated"
+IDS_LOGIN_STATUS_NOT_AUTHENTICATED "Not authenticated"
+IDS_LOGIN_STATUS_LICENSED ", Licensed"
+IDS_LOGIN_STATUS_NOT_LICENSED ", Not licensed"
+IDS_LOGIN_STATUS_LOGGED_IN "Logged in"
+IDS_LOGIN_STATUS_ATTACHED_ONLY "Attached, but not logged in"
+IDS_LOGIN_STATUS_NOT_ATTACHED "Not attached"
+IDS_MESSAGE_CONNINFO_ERROR, "Error %1 occurred while trying to get connection information."
+IDS_MESSAGE_ADDCONN_ERROR, "Error %1 occurred while trying to connect to %2."
+IDS_MESSAGE_CONTEXT_ERROR, "Error %1 occurred while trying to change the current context to %2."
+IDS_MESSAGE_PROPERTIES_ERROR, "Error %1 occurred while trying to get the properties."
+IDS_MESSAGE_LOGGED_IN_TREE, "You are logged in to the directory tree %1 with user name %2.\nThe current workstation name context is %3."
+IDS_MESSAGE_NOT_LOGGED_IN_TREE, "You are not logged in to the directory tree %1.\n"
+IDS_MESSAGE_LOGGED_IN_SERVER, "You are logged in to the server %1 with user name %2.\n"
+IDS_MESSAGE_NOT_LOGGED_IN_SERVER, "You are not logged in to your preferred server %1.\n"
+IDS_MESSAGE_GETINFO_ERROR, "Unable to retrieve information due to the following error: %1"
+END
+
+IDI_TREE_ICON ICON DISCARDABLE "tree.ico"
+IDI_NDSCONT_ICON ICON DISCARDABLE "ndscont.ico"
+IDI_SERVER_ICON ICON DISCARDABLE "server.ico"
+IDI_FOLDER_ICON ICON DISCARDABLE "folderop.ico"
+IDI_PRINTER_ICON ICON DISCARDABLE "print.ico"
+
+IDB_SERVER_ICON BITMAP MOVEABLE PURE "server.bmp"
+IDB_TREE_ICON BITMAP MOVEABLE PURE "tree.bmp"
+
+#endif
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Client Service for NetWare Provider and Authentication Package DLL"
+#define VER_INTERNALNAME_STR "nwprovau.dll"
+
+#include "common.ver"
diff --git a/private/nw/svcdlls/nwwks/client/nwprovau.res b/private/nw/svcdlls/nwwks/client/nwprovau.res
new file mode 100644
index 000000000..3aac0734b
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwprovau.res
Binary files differ
diff --git a/private/nw/svcdlls/nwwks/client/nwshcmn.h b/private/nw/svcdlls/nwwks/client/nwshcmn.h
new file mode 100644
index 000000000..dd498b9e6
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwshcmn.h
@@ -0,0 +1,115 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ nwshcmn.h
+
+Abstract:
+
+ Common header file for shell extensions
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 20-Oct-1995
+
+Revision History:
+
+--*/
+
+#ifndef _NWSHCMN_H_
+#define _NWSHCMN_H_
+
+#if 0
+#define ODS(sz) OutputDebugString(sz)
+#else
+#define ODS(sz)
+#endif
+
+#define TREECHAR L'*'
+
+#define MAX_ONE_NETRES_SIZE 1024
+
+extern "C"
+{
+extern HINSTANCE hmodNW;
+}
+extern LONG g_cRefThisDll; // Reference count of this DLL.
+
+typedef UINT
+(WINAPI *SHELLGETNETRESOURCE)( HNRES hnres,
+ UINT iItem,
+ LPNETRESOURCE pnres,
+ UINT cbMax );
+
+typedef UINT
+(WINAPI *SHELLDRAGQUERYFILE)( HDROP hdrop,
+ UINT iItem,
+ LPWSTR pszItem,
+ UINT cbMax);
+
+typedef VOID
+(WINAPI *SHELLCHANGENOTIFY)( LONG wEventId,
+ UINT uFlags,
+ LPCVOID dwItem1,
+ LPCVOID dwItem2 );
+
+typedef BOOL
+(WINAPI *SHELLEXECUTEEX)( LPSHELLEXECUTEINFOW lpExecInfo );
+
+
+extern SHELLGETNETRESOURCE g_pFuncSHGetNetResource;
+extern SHELLDRAGQUERYFILE g_pFuncSHDragQueryFile;
+extern SHELLCHANGENOTIFY g_pFuncSHChangeNotify;
+extern SHELLEXECUTEEX g_pFuncSHExecuteEx;
+extern WCHAR g_szProviderName[];
+
+VOID HideControl( HWND hwndDlg, WORD wID );
+VOID UnHideControl( HWND hwndDlg, WORD wID );
+VOID EnableDlgItem( HWND hwndDlg, WORD wID, BOOL fEnable);
+
+DWORD MsgBoxPrintf( HWND hwnd, UINT uiMsg, UINT uiTitle, UINT uiFlags,...);
+DWORD MsgBoxErrorPrintf( HWND hwnd, UINT uiMsg, UINT uiTitle, UINT uiFlags, DWORD errNum, LPWSTR pszInsertStr );
+DWORD LoadMsgPrintf( LPWSTR *ppszMessage, UINT uiMsg, ...);
+DWORD LoadMsgErrorPrintf( LPWSTR *ppszMessage, UINT uiMsg, DWORD errNum );
+
+#if 0
+HRESULT
+NWUISetDefaultContext(
+ HWND hParent,
+ LPNETRESOURCE pNetRes
+);
+#endif
+
+HRESULT
+NWUIWhoAmI(
+ HWND hParent,
+ LPNETRESOURCE pNetRes
+);
+
+HRESULT
+NWUILogOut(
+ HWND hParent,
+ LPNETRESOURCE pNetRes,
+ PBOOL pfDisconnected
+);
+
+HRESULT
+NWUIAttachAs(
+ HWND hParent,
+ LPNETRESOURCE pNetRes
+);
+
+HRESULT
+NWUIMapNetworkDrive(
+ HWND hParent,
+ LPNETRESOURCE pNetRes
+);
+
+HRESULT
+NWUIGlobalWhoAmI(
+ HWND hParent
+);
+
+#endif // _NWSHCMN_H_
diff --git a/private/nw/svcdlls/nwwks/client/nwshext.cxx b/private/nw/svcdlls/nwwks/client/nwshext.cxx
new file mode 100644
index 000000000..4818a3823
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwshext.cxx
@@ -0,0 +1,854 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ nwshext.cxx
+
+Abstract:
+
+ This module implements the basics of shell extension classes.
+ It includes AddRef(), Release(), QueryInterface() of the
+ following classes.
+ CNWObjContextMenuClassFactory, CNWObjContextMenu
+ CNWFldContextMenuClassFactory, CNWFldContextMenu
+ CNWHoodContextMenuClassFactory, CNWHoodContextMenu
+
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 25-Oct-1995
+
+--*/
+
+extern "C"
+{
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include <shellapi.h>
+#include <shlobj.h>
+#include <stdio.h>
+#define DONT_WANT_SHELLDEBUG
+#include <shsemip.h>
+
+#include <nwreg.h>
+}
+
+#include "nwshcmn.h"
+#include "nwshext.h"
+
+//
+// Initialize GUIDs (should be done only and at-least once per DLL/EXE)
+//
+
+#pragma data_seg(".text")
+#define INITGUID
+#include <initguid.h>
+#include <shlguid.h>
+#include "nwclsid.h"
+#pragma data_seg()
+
+//
+// Global variables
+//
+LONG g_cRefThisDll = 0; // Reference count of this DLL.
+WCHAR g_szProviderName[256]; // Store the provider name
+
+HINSTANCE g_hShellLibrary = NULL;
+SHELLGETNETRESOURCE g_pFuncSHGetNetResource = NULL;
+SHELLDRAGQUERYFILE g_pFuncSHDragQueryFile = NULL;
+SHELLCHANGENOTIFY g_pFuncSHChangeNotify = NULL;
+SHELLEXECUTEEX g_pFuncSHExecuteEx = NULL;
+
+
+#if DBG
+WCHAR szDebugStr[256]; // For Debug Output
+#endif
+
+BOOL LoadShellDllEntries( VOID );
+
+extern "C"
+{
+//---------------------------------------------------------------------------
+// NwCleanupShellExtension
+//---------------------------------------------------------------------------
+
+VOID NwCleanupShellExtensions( VOID )
+{
+ if ( g_hShellLibrary )
+ {
+ FreeLibrary( g_hShellLibrary );
+ g_hShellLibrary = NULL;
+ }
+}
+}
+
+//---------------------------------------------------------------------------
+// DllCanUnloadNow
+//---------------------------------------------------------------------------
+
+STDAPI DllCanUnloadNow(void)
+{
+#if DBG
+ wsprintf( szDebugStr,L"In DLLCanUnloadNow: g_cRefThisDll = %d\r\n", g_cRefThisDll);
+ ODS( szDebugStr );
+#endif
+
+ return ResultFromScode((g_cRefThisDll == 0) ? S_OK : S_FALSE);
+}
+
+//---------------------------------------------------------------------------
+// DllGetClassObject
+//---------------------------------------------------------------------------
+
+STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppvOut)
+{
+ *ppvOut = NULL;
+
+ if ( !LoadShellDllEntries() )
+ return ResultFromScode(CLASS_E_CLASSNOTAVAILABLE);
+
+ if (IsEqualIID(rclsid, CLSID_NetWareObjectExt))
+ {
+ CNWObjContextMenuClassFactory *pcf = new CNWObjContextMenuClassFactory;
+
+ if ( pcf == NULL )
+ return ResultFromScode(E_OUTOFMEMORY);
+
+ HRESULT hr = pcf->QueryInterface(riid, ppvOut);
+
+ if ( FAILED(hr) )
+ delete pcf;
+
+ return hr;
+ }
+ else if (IsEqualIID(rclsid, CLSID_NetWareFolderMenuExt))
+ {
+ CNWFldContextMenuClassFactory *pcf = new CNWFldContextMenuClassFactory;
+
+ if ( pcf == NULL )
+ return ResultFromScode(E_OUTOFMEMORY);
+
+ HRESULT hr = pcf->QueryInterface(riid, ppvOut);
+
+ if ( FAILED(hr) )
+ delete pcf;
+
+ return hr;
+ }
+ else if (IsEqualIID(rclsid, CLSID_NetworkNeighborhoodMenuExt))
+ {
+ CNWHoodContextMenuClassFactory *pcf= new CNWHoodContextMenuClassFactory;
+
+ if ( pcf == NULL )
+ return ResultFromScode(E_OUTOFMEMORY);
+
+ HRESULT hr = pcf->QueryInterface(riid, ppvOut);
+
+ if ( FAILED(hr) )
+ delete pcf;
+
+ return hr;
+ }
+
+
+ return ResultFromScode(CLASS_E_CLASSNOTAVAILABLE);
+}
+
+BOOL LoadShellDllEntries( VOID )
+{
+ static BOOL s_fLoaded = FALSE;
+
+ if ( !s_fLoaded )
+ {
+ DWORD err;
+ HKEY hkey;
+
+ g_hShellLibrary = LoadLibrary( L"shell32.dll");
+ if ( g_hShellLibrary != NULL )
+ {
+ s_fLoaded = TRUE;
+
+ g_pFuncSHGetNetResource =
+ (SHELLGETNETRESOURCE) GetProcAddress( g_hShellLibrary,
+ (LPCSTR)(MAKELONG(SHGetNetResourceORD, 0)) );
+
+ g_pFuncSHDragQueryFile =
+ (SHELLDRAGQUERYFILE) GetProcAddress( g_hShellLibrary,
+ (LPCSTR) "DragQueryFileW");
+ g_pFuncSHChangeNotify =
+ (SHELLCHANGENOTIFY) GetProcAddress( g_hShellLibrary,
+ (LPCSTR) "SHChangeNotify");
+ g_pFuncSHExecuteEx =
+ (SHELLEXECUTEEX) GetProcAddress( g_hShellLibrary,
+ (LPCSTR) "ShellExecuteExW");
+ }
+
+ // Set the default provider name in case we fail to read
+ // it from the registry.
+ wcscpy( g_szProviderName, L"NetWare or Compatible Network");
+
+ //
+ // Read the Network Provider Name.
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\networkprovider
+ //
+ err = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_PROVIDER_PATH,
+ REG_OPTION_NON_VOLATILE, // options
+ KEY_READ, // desired access
+ &hkey
+ );
+
+ if ( err == NO_ERROR )
+ {
+ LPWSTR pszProviderName = NULL;
+
+ //
+ // ignore the return code. if fail, pszProviderName is NULL
+ //
+ err = NwReadRegValue(
+ hkey,
+ NW_PROVIDER_VALUENAME,
+ &pszProviderName // free with LocalFree
+ );
+
+ if ( err == NO_ERROR && pszProviderName != NULL )
+ {
+ wcscpy( g_szProviderName, pszProviderName );
+ LocalFree( pszProviderName );
+ }
+
+ RegCloseKey( hkey );
+ }
+ }
+
+ return s_fLoaded;
+}
+
+//---------------------------------------------------------------------------
+// CNWObjContextMenuClassFactory
+//---------------------------------------------------------------------------
+
+CNWObjContextMenuClassFactory::CNWObjContextMenuClassFactory()
+{
+ _cRef = 0L;
+ InterlockedIncrement( &g_cRefThisDll );
+
+#if DBG
+ wsprintf( szDebugStr,L"CNWObjContextMenuClassFactory::CNWObjContextMenuClassFactory(), g_cRefThisDll = %d\r\n", g_cRefThisDll);
+ ODS( szDebugStr );
+#endif
+}
+
+CNWObjContextMenuClassFactory::~CNWObjContextMenuClassFactory()
+{
+ InterlockedDecrement( &g_cRefThisDll );
+
+#if DBG
+ wsprintf( szDebugStr,L"CNWObjContextMenuClassFactory::~CNWObjContextMenuClassFactory(), g_cRefThisDll = %d\r\n", g_cRefThisDll);
+ ODS( szDebugStr );
+#endif
+}
+
+STDMETHODIMP CNWObjContextMenuClassFactory::QueryInterface(REFIID riid,
+ LPVOID FAR *ppv)
+{
+ *ppv = NULL;
+
+ // Any interface on this object is the object pointer
+
+ if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IClassFactory))
+ {
+ *ppv = (LPCLASSFACTORY)this;
+ AddRef();
+ return NOERROR;
+ }
+
+ return ResultFromScode(E_NOINTERFACE);
+}
+
+STDMETHODIMP_(ULONG) CNWObjContextMenuClassFactory::AddRef()
+{
+ return ++_cRef;
+}
+
+STDMETHODIMP_(ULONG) CNWObjContextMenuClassFactory::Release()
+{
+ if (--_cRef)
+ return _cRef;
+
+ delete this;
+
+ return 0L;
+}
+
+STDMETHODIMP CNWObjContextMenuClassFactory::CreateInstance(LPUNKNOWN pUnkOuter,
+ REFIID riid,
+ LPVOID *ppvObj)
+{
+ *ppvObj = NULL;
+
+ // Shell extensions typically don't support aggregation (inheritance)
+
+ if (pUnkOuter)
+ return ResultFromScode(CLASS_E_NOAGGREGATION);
+
+ // Create the main shell extension object. The shell will then call
+ // QueryInterface with IID_IShellExtInit--this is how shell extensions are
+ // initialized.
+
+ LPCNWOBJCONTEXTMENU pShellExt = new CNWObjContextMenu(); // Create the CNWObjContextMenu object
+
+ if (NULL == pShellExt)
+ return ResultFromScode(E_OUTOFMEMORY);
+
+ //
+ // We set the reference count of CNWObjContextMenu to one at initialization.
+ // Hence, we can call Release() after QueryInterface.
+ // So, if QueryInterface failed, Release will free the object.
+ //
+
+ HRESULT hr = pShellExt->QueryInterface(riid, ppvObj);
+ pShellExt->Release();
+
+ return hr;
+}
+
+STDMETHODIMP CNWObjContextMenuClassFactory::LockServer(BOOL fLock)
+{
+ return NOERROR;
+}
+
+//---------------------------------------------------------------------------
+// CNWObjContextMenu
+//---------------------------------------------------------------------------
+
+CNWObjContextMenu::CNWObjContextMenu()
+{
+ _cRef = 1L;
+ _pDataObj = NULL;
+
+ _fGotClusterInfo = FALSE;
+ _dwTotal = 0;
+ _dwFree = 0;
+
+ InterlockedIncrement( &g_cRefThisDll );
+
+#if DBG
+ wsprintf( szDebugStr,L"CNWObjContextMenu::CNWObjContextMenu(), g_cRefThisDll = %d\r\n", g_cRefThisDll);
+ ODS( szDebugStr );
+#endif
+}
+
+CNWObjContextMenu::~CNWObjContextMenu()
+{
+ if (_pDataObj)
+ _pDataObj->Release();
+
+ InterlockedDecrement( &g_cRefThisDll );
+
+#if DBG
+ wsprintf( szDebugStr,L"CNWObjContextMenu::~CNWObjContextMenu(), g_cRefThisDll = %d\r\n", g_cRefThisDll);
+ ODS( szDebugStr );
+#endif
+}
+
+STDMETHODIMP CNWObjContextMenu::QueryInterface(REFIID riid, LPVOID FAR *ppv)
+{
+ *ppv = NULL;
+
+ if (IsEqualIID(riid, IID_IShellExtInit) || IsEqualIID(riid, IID_IUnknown))
+ {
+ *ppv = (LPSHELLEXTINIT)this;
+ }
+ else if (IsEqualIID(riid, IID_IContextMenu))
+ {
+ *ppv = (LPCONTEXTMENU)this;
+ }
+ else if (IsEqualIID(riid, IID_IShellPropSheetExt))
+ {
+ *ppv = (LPSHELLPROPSHEETEXT)this;
+ }
+
+ if (*ppv)
+ {
+ AddRef();
+
+ return NOERROR;
+ }
+
+ return ResultFromScode(E_NOINTERFACE);
+}
+
+STDMETHODIMP_(ULONG) CNWObjContextMenu::AddRef()
+{
+ return ++_cRef;
+}
+
+STDMETHODIMP_(ULONG) CNWObjContextMenu::Release()
+{
+ if (--_cRef)
+ return _cRef;
+
+ delete this;
+
+ return 0L;
+}
+
+//
+// FUNCTION: CNWObjContextMenu::Initialize(LPCITEMIDLIST, LPDATAOBJECT, HKEY)
+//
+// PURPOSE: Called by the shell when initializing a context menu or property
+// sheet extension.
+//
+// PARAMETERS:
+// pIDFolder - Specifies the parent folder
+// pDataObj - Spefifies the set of items selected in that folder.
+// hRegKey - Specifies the type of the focused item in the selection.
+//
+// RETURN VALUE:
+//
+// NOERROR in all cases.
+//
+// COMMENTS: Note that at the time this function is called, we don't know
+// (or care) what type of shell extension is being initialized.
+// It could be a context menu or a property sheet.
+//
+
+STDMETHODIMP CNWObjContextMenu::Initialize( LPCITEMIDLIST pIDFolder,
+ LPDATAOBJECT pDataObj,
+ HKEY hRegKey)
+{
+ // We do not need the registry handle so ignore it.
+
+ // Initialize can be called more than once
+
+ if (_pDataObj)
+ _pDataObj->Release();
+
+ // Duplicate the object pointer
+
+ if (pDataObj)
+ {
+ _pDataObj = pDataObj;
+ pDataObj->AddRef();
+ }
+
+ return NOERROR;
+}
+
+//---------------------------------------------------------------------------
+// CNWFldContextMenuClassFactory
+//---------------------------------------------------------------------------
+
+CNWFldContextMenuClassFactory::CNWFldContextMenuClassFactory()
+{
+ _cRef = 0L;
+ InterlockedIncrement( &g_cRefThisDll );
+
+#if DBG
+ wsprintf( szDebugStr,L"CNWFldContextMenuClassFactory::CNWFldContextMenuClassFactory(), g_cRefThisDll = %d\r\n", g_cRefThisDll);
+ ODS( szDebugStr );
+#endif
+}
+
+CNWFldContextMenuClassFactory::~CNWFldContextMenuClassFactory()
+{
+ InterlockedDecrement( &g_cRefThisDll );
+
+#if DBG
+ wsprintf( szDebugStr,L"CNWFldContextMenuClassFactory::~CNWFldContextMenuClassFactory(), g_cRefThisDll = %d\r\n", g_cRefThisDll);
+ ODS( szDebugStr );
+#endif
+}
+
+STDMETHODIMP CNWFldContextMenuClassFactory::QueryInterface(REFIID riid,
+ LPVOID FAR *ppv)
+{
+ *ppv = NULL;
+
+ // Any interface on this object is the object pointer
+
+ if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IClassFactory))
+ {
+ *ppv = (LPCLASSFACTORY)this;
+ AddRef();
+ return NOERROR;
+ }
+
+ return ResultFromScode(E_NOINTERFACE);
+}
+
+STDMETHODIMP_(ULONG) CNWFldContextMenuClassFactory::AddRef()
+{
+ return ++_cRef;
+}
+
+STDMETHODIMP_(ULONG) CNWFldContextMenuClassFactory::Release()
+{
+ if (--_cRef)
+ return _cRef;
+
+ delete this;
+
+ return 0L;
+}
+
+STDMETHODIMP CNWFldContextMenuClassFactory::CreateInstance(LPUNKNOWN pUnkOuter,
+ REFIID riid,
+ LPVOID *ppvObj)
+{
+ *ppvObj = NULL;
+
+ // Shell extensions typically don't support aggregation (inheritance)
+
+ if (pUnkOuter)
+ return ResultFromScode(CLASS_E_NOAGGREGATION);
+
+ // Create the main shell extension object. The shell will then call
+ // QueryInterface with IID_IShellExtInit--this is how shell extensions are
+ // initialized.
+
+ LPCNWFLDCONTEXTMENU pShellExt = new CNWFldContextMenu(); // Create the CNWFldContextMenu object
+
+ if (NULL == pShellExt)
+ return ResultFromScode(E_OUTOFMEMORY);
+
+ //
+ // We set the reference count of CNWFldContextMenu to one at initialization.
+ // Hence, we can call Release() after QueryInterface.
+ // So, if QueryInterface failed, Release will free the object.
+ //
+
+ HRESULT hr = pShellExt->QueryInterface(riid, ppvObj);
+ pShellExt->Release();
+
+ return hr;
+}
+
+STDMETHODIMP CNWFldContextMenuClassFactory::LockServer(BOOL fLock)
+{
+ return NOERROR;
+}
+
+//---------------------------------------------------------------------------
+// CNWFldContextMenu
+//---------------------------------------------------------------------------
+
+CNWFldContextMenu::CNWFldContextMenu()
+{
+ _cRef = 1L;
+ _pDataObj = NULL;
+
+ InterlockedIncrement( &g_cRefThisDll );
+
+#if DBG
+ wsprintf( szDebugStr,L"CNWFldContextMenu::CNWFldContextMenu(), g_cRefThisDll = %d\r\n", g_cRefThisDll);
+ ODS( szDebugStr );
+#endif
+}
+
+CNWFldContextMenu::~CNWFldContextMenu()
+{
+ if (_pDataObj)
+ _pDataObj->Release();
+
+ InterlockedDecrement( &g_cRefThisDll );
+
+#if DBG
+ wsprintf( szDebugStr,L"CNWFldContextMenu::~CNWFldContextMenu(), g_cRefThisDll = %d\r\n", g_cRefThisDll);
+ ODS( szDebugStr );
+#endif
+}
+
+STDMETHODIMP CNWFldContextMenu::QueryInterface(REFIID riid, LPVOID FAR *ppv)
+{
+ *ppv = NULL;
+
+ if (IsEqualIID(riid, IID_IShellExtInit) || IsEqualIID(riid, IID_IUnknown))
+ {
+ *ppv = (LPSHELLEXTINIT)this;
+ }
+ else if (IsEqualIID(riid, IID_IContextMenu))
+ {
+ *ppv = (LPCONTEXTMENU)this;
+ }
+
+ if (*ppv)
+ {
+ AddRef();
+
+ return NOERROR;
+ }
+
+ return ResultFromScode(E_NOINTERFACE);
+}
+
+STDMETHODIMP_(ULONG) CNWFldContextMenu::AddRef()
+{
+ return ++_cRef;
+}
+
+STDMETHODIMP_(ULONG) CNWFldContextMenu::Release()
+{
+ if (--_cRef)
+ return _cRef;
+
+ delete this;
+
+ return 0L;
+}
+
+//
+// FUNCTION: CNWFldContextMenu::Initialize(LPCITEMIDLIST, LPDATAOBJECT, HKEY)
+//
+// PURPOSE: Called by the shell when initializing a context menu or property
+// sheet extension.
+//
+// PARAMETERS:
+// pIDFolder - Specifies the parent folder
+// pDataObj - Spefifies the set of items selected in that folder.
+// hRegKey - Specifies the type of the focused item in the selection.
+//
+// RETURN VALUE:
+//
+// NOERROR in all cases.
+//
+// COMMENTS: Note that at the time this function is called, we don't know
+// (or care) what type of shell extension is being initialized.
+// It could be a context menu or a property sheet.
+//
+
+STDMETHODIMP CNWFldContextMenu::Initialize( LPCITEMIDLIST pIDFolder,
+ LPDATAOBJECT pDataObj,
+ HKEY hRegKey)
+{
+ // We do not need the registry handle so ignore it.
+
+ // Initialize can be called more than once
+
+ if (_pDataObj)
+ _pDataObj->Release();
+
+ // Duplicate the object pointer
+
+ if (pDataObj)
+ {
+ _pDataObj = pDataObj;
+ pDataObj->AddRef();
+ }
+
+ return NOERROR;
+}
+
+//---------------------------------------------------------------------------
+// CNWHoodContextMenuClassFactory
+//---------------------------------------------------------------------------
+
+CNWHoodContextMenuClassFactory::CNWHoodContextMenuClassFactory()
+{
+ _cRef = 0L;
+ InterlockedIncrement( &g_cRefThisDll );
+
+#if DBG
+ wsprintf( szDebugStr,L"CNWHoodContextMenuClassFactory::CNWHoodContextMenuClassFactory(), g_cRefThisDll = %d\r\n", g_cRefThisDll);
+ ODS( szDebugStr );
+#endif
+}
+
+CNWHoodContextMenuClassFactory::~CNWHoodContextMenuClassFactory()
+{
+ InterlockedDecrement( &g_cRefThisDll );
+
+#if DBG
+ wsprintf( szDebugStr,L"CNWHoodContextMenuClassFactory::~CNWHoodContextMenuClassFactory(), g_cRefThisDll = %d\r\n", g_cRefThisDll);
+ ODS( szDebugStr );
+#endif
+}
+
+STDMETHODIMP CNWHoodContextMenuClassFactory::QueryInterface(REFIID riid,
+ LPVOID FAR *ppv)
+{
+ *ppv = NULL;
+
+ // Any interface on this object is the object pointer
+
+ if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IClassFactory))
+ {
+ *ppv = (LPCLASSFACTORY)this;
+ AddRef();
+ return NOERROR;
+ }
+
+ return ResultFromScode(E_NOINTERFACE);
+}
+
+STDMETHODIMP_(ULONG) CNWHoodContextMenuClassFactory::AddRef()
+{
+ return ++_cRef;
+}
+
+STDMETHODIMP_(ULONG) CNWHoodContextMenuClassFactory::Release()
+{
+ if (--_cRef)
+ return _cRef;
+
+ delete this;
+
+ return 0L;
+}
+
+STDMETHODIMP CNWHoodContextMenuClassFactory::CreateInstance(LPUNKNOWN pUnkOuter,
+ REFIID riid,
+ LPVOID *ppvObj)
+{
+ *ppvObj = NULL;
+
+ // Shell extensions typically don't support aggregation (inheritance)
+
+ if (pUnkOuter)
+ return ResultFromScode(CLASS_E_NOAGGREGATION);
+
+ // Create the main shell extension object. The shell will then call
+ // QueryInterface with IID_IShellExtInit--this is how shell extensions are
+ // initialized.
+
+ LPCNWHOODCONTEXTMENU pShellExt = new CNWHoodContextMenu(); // Create the CNWHoodContextMenu object
+
+ if (NULL == pShellExt)
+ return ResultFromScode(E_OUTOFMEMORY);
+
+ //
+ // We set the reference count of CNWHoodContextMenu to one at initialization.
+ // Hence, we can call Release() after QueryInterface.
+ // So, if QueryInterface failed, Release will free the object.
+ //
+
+ HRESULT hr = pShellExt->QueryInterface(riid, ppvObj);
+ pShellExt->Release();
+
+ return hr;
+}
+
+STDMETHODIMP CNWHoodContextMenuClassFactory::LockServer(BOOL fLock)
+{
+ return NOERROR;
+}
+
+//---------------------------------------------------------------------------
+// CNWHoodContextMenu
+//---------------------------------------------------------------------------
+
+CNWHoodContextMenu::CNWHoodContextMenu()
+{
+ _cRef = 1L;
+ _pDataObj = NULL;
+
+ InterlockedIncrement( &g_cRefThisDll );
+
+#if DBG
+ wsprintf( szDebugStr,L"CNWHoodContextMenu::CNWHoodContextMenu(), g_cRefThisDll = %d\r\n", g_cRefThisDll);
+ ODS( szDebugStr );
+#endif
+}
+
+CNWHoodContextMenu::~CNWHoodContextMenu()
+{
+ if (_pDataObj)
+ _pDataObj->Release();
+
+ InterlockedDecrement( &g_cRefThisDll );
+
+#if DBG
+ wsprintf( szDebugStr,L"CNWHoodContextMenu::~CNWHoodContextMenu(), g_cRefThisDll = %d\r\n", g_cRefThisDll);
+ ODS( szDebugStr );
+#endif
+}
+
+STDMETHODIMP CNWHoodContextMenu::QueryInterface(REFIID riid, LPVOID FAR *ppv)
+{
+ *ppv = NULL;
+
+ if (IsEqualIID(riid, IID_IShellExtInit) || IsEqualIID(riid, IID_IUnknown))
+ {
+ *ppv = (LPSHELLEXTINIT)this;
+ }
+ else if (IsEqualIID(riid, IID_IContextMenu))
+ {
+ *ppv = (LPCONTEXTMENU)this;
+ }
+
+ if (*ppv)
+ {
+ AddRef();
+
+ return NOERROR;
+ }
+
+ return ResultFromScode(E_NOINTERFACE);
+}
+
+STDMETHODIMP_(ULONG) CNWHoodContextMenu::AddRef()
+{
+ return ++_cRef;
+}
+
+STDMETHODIMP_(ULONG) CNWHoodContextMenu::Release()
+{
+ if (--_cRef)
+ return _cRef;
+
+ delete this;
+
+ return 0L;
+}
+
+//
+// FUNCTION: CNWHoodContextMenu::Initialize(LPCITEMIDLIST, LPDATAOBJECT, HKEY)
+//
+// PURPOSE: Called by the shell when initializing a context menu or property
+// sheet extension.
+//
+// PARAMETERS:
+// pIDFolder - Specifies the parent folder
+// pDataObj - Spefifies the set of items selected in that folder.
+// hRegKey - Specifies the type of the focused item in the selection.
+//
+// RETURN VALUE:
+//
+// NOERROR in all cases.
+//
+// COMMENTS: Note that at the time this function is called, we don't know
+// (or care) what type of shell extension is being initialized.
+// It could be a context menu or a property sheet.
+//
+
+STDMETHODIMP CNWHoodContextMenu::Initialize( LPCITEMIDLIST pIDFolder,
+ LPDATAOBJECT pDataObj,
+ HKEY hRegKey)
+{
+ // We do not need the registry handle so ignore it.
+
+ // Initialize can be called more than once
+
+ if (_pDataObj)
+ _pDataObj->Release();
+
+ // Duplicate the object pointer
+
+ if (pDataObj)
+ {
+ _pDataObj = pDataObj;
+ pDataObj->AddRef();
+ }
+
+ return NOERROR;
+}
+
diff --git a/private/nw/svcdlls/nwwks/client/nwshext.h b/private/nw/svcdlls/nwwks/client/nwshext.h
new file mode 100644
index 000000000..8e1230c20
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwshext.h
@@ -0,0 +1,279 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ nwshext.h
+
+Abstract:
+
+ All C++ classes used in implementing shell extensions.
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 20-Oct-1995
+
+Revision History:
+
+--*/
+
+#ifndef _NWSHEXT_H_
+#define _NWSHEXT_H_
+
+BOOL
+GetNetResourceFromShell(
+ LPDATAOBJECT pDataObj,
+ LPNETRESOURCE pBuffer,
+ UINT dwBufferSize
+);
+
+/******************************************************************************/
+
+// this class factory object creates context menu handlers for netware objects
+class CNWObjContextMenuClassFactory : public IClassFactory
+{
+protected:
+ ULONG _cRef;
+
+public:
+ CNWObjContextMenuClassFactory();
+ ~CNWObjContextMenuClassFactory();
+
+ // IUnknown members
+
+ STDMETHODIMP QueryInterface( REFIID, LPVOID FAR *);
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+
+ // IClassFactory members
+
+ STDMETHODIMP CreateInstance( LPUNKNOWN, REFIID, LPVOID FAR *);
+ STDMETHODIMP LockServer( BOOL);
+
+};
+
+typedef CNWObjContextMenuClassFactory *LPCNWOBJCONTEXTMENUCLASSFACTORY;
+
+typedef struct _NWMENUITEM
+{
+ UINT idResourceString;
+ UINT idCommand;
+} NWMENUITEM, *LPNWMENUITEM;
+
+// this is the actual context menu handler for netware objects
+class CNWObjContextMenu : public IContextMenu,
+ IShellExtInit,
+ IShellPropSheetExt
+{
+protected:
+ ULONG _cRef;
+ LPDATAOBJECT _pDataObj;
+ NWMENUITEM *_pIdTable;
+ BYTE _buffer[MAX_ONE_NETRES_SIZE];
+
+public:
+ BOOL _fGotClusterInfo;
+ DWORD _dwTotal;
+ DWORD _dwFree;
+
+ DWORD *_paHelpIds;
+
+ CNWObjContextMenu();
+ ~CNWObjContextMenu();
+
+ // IUnknown members
+
+ STDMETHODIMP QueryInterface( REFIID, LPVOID FAR *);
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+
+ // IShellContextMenu members
+
+ STDMETHODIMP QueryContextMenu( HMENU hMenu,
+ UINT indexMenu,
+ UINT idCmdFirst,
+ UINT idCmdLast,
+ UINT uFlags);
+
+ STDMETHODIMP InvokeCommand( LPCMINVOKECOMMANDINFO lpcmi);
+
+ STDMETHODIMP GetCommandString( UINT idCmd,
+ UINT uFlags,
+ UINT FAR *reserved,
+ LPSTR pszName,
+ UINT cchMax);
+
+ // IShellExtInit methods
+
+ STDMETHODIMP Initialize( LPCITEMIDLIST pIDFolder,
+ LPDATAOBJECT pDataObj,
+ HKEY hKeyID);
+
+ // IShellPropSheetExt methods
+
+ STDMETHODIMP AddPages( LPFNADDPROPSHEETPAGE lpfnAddPage,
+ LPARAM lParam);
+
+ STDMETHODIMP ReplacePage( UINT uPageID,
+ LPFNADDPROPSHEETPAGE lpfnReplaceWith,
+ LPARAM lParam);
+
+ VOID FillAndAddPage( LPFNADDPROPSHEETPAGE lpfnAddPage,
+ LPARAM lParam,
+ DLGPROC pfnDlgProc,
+ LPWSTR pszTemplate );
+
+ // Other misc methods
+
+ LPNETRESOURCE QueryNetResource()
+ { return ( LPNETRESOURCE ) _buffer; }
+
+};
+typedef CNWObjContextMenu *LPCNWOBJCONTEXTMENU;
+
+/******************************************************************************/
+
+// this class factory object creates context menu handlers for netware folders
+class CNWFldContextMenuClassFactory : public IClassFactory
+{
+protected:
+ ULONG _cRef;
+
+public:
+ CNWFldContextMenuClassFactory();
+ ~CNWFldContextMenuClassFactory();
+
+ // IUnknown members
+
+ STDMETHODIMP QueryInterface( REFIID, LPVOID FAR *);
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+
+ // IClassFactory members
+
+ STDMETHODIMP CreateInstance( LPUNKNOWN, REFIID, LPVOID FAR *);
+ STDMETHODIMP LockServer( BOOL);
+
+};
+
+typedef CNWFldContextMenuClassFactory *LPCNWFLDCONTEXTMENUCLASSFACTORY;
+
+// this is the actual context menu handler for netware objects
+class CNWFldContextMenu : public IContextMenu,
+ IShellExtInit
+{
+protected:
+ ULONG _cRef;
+ LPDATAOBJECT _pDataObj;
+ BYTE _buffer[MAX_ONE_NETRES_SIZE];
+
+public:
+ CNWFldContextMenu();
+ ~CNWFldContextMenu();
+
+ // IUnknown members
+
+ STDMETHODIMP QueryInterface( REFIID, LPVOID FAR *);
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+
+ // IShellContextMenu members
+
+ STDMETHODIMP QueryContextMenu( HMENU hMenu,
+ UINT indexMenu,
+ UINT idCmdFirst,
+ UINT idCmdLast,
+ UINT uFlags);
+
+ STDMETHODIMP InvokeCommand( LPCMINVOKECOMMANDINFO lpcmi);
+
+ STDMETHODIMP GetCommandString( UINT idCmd,
+ UINT uFlags,
+ UINT FAR *reserved,
+ LPSTR pszName,
+ UINT cchMax);
+
+ // IShellExtInit methods
+
+ STDMETHODIMP Initialize( LPCITEMIDLIST pIDFolder,
+ LPDATAOBJECT pDataObj,
+ HKEY hKeyID);
+
+ BOOL IsNetWareObject( VOID );
+ HRESULT GetFSObject( LPWSTR pszPath, UINT cbMaxPath );
+
+};
+typedef CNWFldContextMenu *LPCNWFLDCONTEXTMENU;
+
+// this class factory object creates context menu handlers
+// for Network Neighborhood
+
+class CNWHoodContextMenuClassFactory : public IClassFactory
+{
+protected:
+ ULONG _cRef;
+
+public:
+ CNWHoodContextMenuClassFactory();
+ ~CNWHoodContextMenuClassFactory();
+
+ // IUnknown members
+
+ STDMETHODIMP QueryInterface( REFIID, LPVOID FAR *);
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+
+ // IClassFactory members
+
+ STDMETHODIMP CreateInstance( LPUNKNOWN, REFIID, LPVOID FAR *);
+ STDMETHODIMP LockServer( BOOL);
+
+};
+
+typedef CNWHoodContextMenuClassFactory *LPCNWHOODCONTEXTMENUCLASSFACTORY;
+
+// this is the actual context menu handler for network neighborhood
+class CNWHoodContextMenu : public IContextMenu,
+ IShellExtInit
+{
+protected:
+ ULONG _cRef;
+ LPDATAOBJECT _pDataObj;
+
+public:
+ CNWHoodContextMenu();
+ ~CNWHoodContextMenu();
+
+ // IUnknown members
+
+ STDMETHODIMP QueryInterface( REFIID, LPVOID FAR *);
+ STDMETHODIMP_(ULONG) AddRef();
+ STDMETHODIMP_(ULONG) Release();
+
+ // IShellContextMenu members
+
+ STDMETHODIMP QueryContextMenu( HMENU hMenu,
+ UINT indexMenu,
+ UINT idCmdFirst,
+ UINT idCmdLast,
+ UINT uFlags);
+
+ STDMETHODIMP InvokeCommand( LPCMINVOKECOMMANDINFO lpcmi);
+
+ STDMETHODIMP GetCommandString( UINT idCmd,
+ UINT uFlags,
+ UINT FAR *reserved,
+ LPSTR pszName,
+ UINT cchMax);
+
+ // IShellExtInit methods
+
+ STDMETHODIMP Initialize( LPCITEMIDLIST pIDFolder,
+ LPDATAOBJECT pDataObj,
+ HKEY hKeyID);
+
+};
+typedef CNWHoodContextMenu *LPCNWHOODCONTEXTMENU;
+
+#endif // _NWSHEXT_H_
diff --git a/private/nw/svcdlls/nwwks/client/nwshhelp.h b/private/nw/svcdlls/nwwks/client/nwshhelp.h
new file mode 100644
index 000000000..214338c14
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwshhelp.h
@@ -0,0 +1,78 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ nwshhelp.h
+
+Abstract:
+
+ All help ids used in nwprovau.dll
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 20-Oct-1995
+
+Revision History:
+
+--*/
+
+#ifndef _NWSHHELP_H_
+#define _NWSHHELP_H_
+
+#define NO_HELP ((DWORD) -1) // Disables Help on a control
+
+#define IDH_DLG_NETWORK_CREDENTIAL_HELP 3001
+
+#ifndef NT1057
+
+// Global WhoAmI dialog
+#define IDH_GLOBAL_SERVERLIST 3005
+#define IDH_GLOBAL_CONTEXT 3006
+#define IDH_GLOBAL_DETACH 3007
+#define IDH_GLOBAL_CHGPWD 3008
+
+// Server summary sheet
+#define IDH_SERVERNAME 3020
+#define IDH_COMMENT 3021
+#define IDH_VERSION 3022
+#define IDH_REVISION 3023
+#define IDH_CONNINUSE 3024
+#define IDH_MAXCONNS 3025
+
+// Share summary sheet
+#define IDH_SHARE_NAME 3030
+#define IDH_SHARE_SERVER 3031
+#define IDH_SHARE_PATH 3032
+#define IDH_SHARE_USED_SPC 3034
+#define IDH_SHARE_FREE_SPC 3035
+#define IDH_SHARE_MAX_SPC 3036
+#define IDH_SHARE_LFN_TXT 3037
+#define IDH_SHARE_PIE 3038
+
+// NDS sheet
+#define IDH_NDS_NAME 3061
+#define IDH_NDS_CLASS 3062
+#define IDH_NDS_COMMENT 3063
+
+// Printer summary sheet
+#define IDH_PRINTER_NAME 3070
+#define IDH_PRINTER_QUEUE 3071
+
+#if 0
+// Wkgrp summary sheet
+#define IDH_WKGRP_NAME 3040
+#define IDH_WKGRP_TYPE 3041
+
+// NDS Admin page
+#define IDH_ENABLE_SYSPOL 3100
+#define IDH_VOLUME_LABEL 3101
+#define IDH_VOLUME 3102
+#define IDH_DIRECTORY_LABEL 3103
+#define IDH_DIRECTORY 3104
+
+#endif
+#endif
+
+#endif // _NWSHHELP_H_
diff --git a/private/nw/svcdlls/nwwks/client/nwshmenu.cxx b/private/nw/svcdlls/nwwks/client/nwshmenu.cxx
new file mode 100644
index 000000000..d4cc28bdb
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwshmenu.cxx
@@ -0,0 +1,896 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ nwshmenu.cxx
+
+Abstract:
+
+ This module implements the IContextMenu member functions necessary to support
+ the context menu of NetWare shell extension.
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 25-Oct-1995
+
+--*/
+
+
+extern "C"
+{
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <shellapi.h>
+#include <shlobj.h>
+#define DONT_WANT_SHELLDEBUG
+#include <shsemip.h>
+#include <winnetwk.h>
+#include <ntddnwfs.h>
+#include "nwshrc.h"
+#include "nwwks.h"
+#include "nwutil.h"
+}
+
+#include "nwshcmn.h"
+#include "nwshext.h"
+
+#define MAX_VERB_SIZE 128
+#define MAX_SHELL_IDLIST_SIZE 512
+
+BOOL g_cfNetResource = 0; // Clipboard format
+BOOL g_cfIDList = 0;
+
+NWMENUITEM aServerVerbs[] = { { IDO_VERB_WHOAMI, 0 },
+ { IDO_VERB_LOGOUT, 0 },
+ { IDO_VERB_ATTACHAS, 0 },
+ { 0, 0 } };
+
+NWMENUITEM aDSVerbs[] = { { IDO_VERB_TREEWHOAMI, 0 },
+ // { IDO_VERB_SETDEFAULTCONTEXT, 0 },
+ { 0, 0 } };
+
+NWMENUITEM aDSTreeVerbs[] = { { IDO_VERB_TREEWHOAMI, 0 },
+ { 0, 0 } };
+
+NWMENUITEM aGlobalVerbs[] = { { IDO_VERB_GLOBALWHOAMI, 0 },
+ { 0, 0 } };
+
+NWMENUITEM aDirectoryVerbs[] = { { IDO_VERB_MAPNETWORKDRIVE, 0 },
+ { 0, 0 } };
+
+
+NWMENUITEM aHoodVerbs[] = { { IDO_VERB_GLOBALWHOAMI, 0 },
+ { 0, 0 } };
+
+
+HRESULT
+InsertCommandsArray( HMENU hMenu,
+ UINT indexMenu,
+ UINT idCmdFirst,
+ LPNWMENUITEM aVerbs );
+
+UINT
+LookupCommand( LPNWMENUITEM aVerbs,
+ LPCSTR pszCmd );
+
+UINT
+LookupResource( LPNWMENUITEM aVerbs,
+ UINT uiResourceOffset );
+
+UINT WINAPI
+HIDA_GetIDList( LPIDA hida,
+ UINT i,
+ LPITEMIDLIST pidlOut,
+ UINT cbMax);
+
+//
+// FUNCTION: CNWObjContextMenu::QueryContextMenu(HMENU, UINT, UINT, UINT, UINT)
+//
+// PURPOSE: Called by the shell just before the context menu is displayed.
+// This is where you add your specific menu items.
+//
+// PARAMETERS:
+// hMenu - Handle to the context menu
+// indexMenu - Index of where to begin inserting menu items
+// idCmdFirst - Lowest value for new menu ID's
+// idCmtLast - Highest value for new menu ID's
+// uFlags - Specifies the context of the menu event
+//
+// RETURN VALUE:
+//
+//
+// COMMENTS:
+//
+
+STDMETHODIMP CNWObjContextMenu::QueryContextMenu( HMENU hMenu,
+ UINT indexMenu,
+ UINT idCmdFirst,
+ UINT idCmdLast,
+ UINT uFlags )
+{
+ HRESULT hres;
+ LPNETRESOURCE pNetRes = (LPNETRESOURCE) _buffer;
+
+ if ( !::GetNetResourceFromShell( _pDataObj,
+ pNetRes,
+ sizeof( _buffer )))
+ {
+ // We cannot get the net resource of the selected object.
+
+ // Must return number of menu items we added.
+ // Nothing added here.
+ return ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS, FACILITY_NULL, 0 ));
+ }
+
+ // First, add a menu separator
+ if ( InsertMenu( hMenu, indexMenu, MF_SEPARATOR | MF_BYPOSITION, 0, NULL))
+ indexMenu++;
+
+ // Next, add menu items depending on display types
+ switch ( pNetRes->dwDisplayType )
+ {
+ case RESOURCEDISPLAYTYPE_ROOT:
+ case RESOURCEDISPLAYTYPE_NETWORK:
+ hres = InsertCommandsArray( hMenu, indexMenu,
+ idCmdFirst, _pIdTable = aGlobalVerbs );
+
+ break;
+
+ case RESOURCEDISPLAYTYPE_TREE:
+ hres = InsertCommandsArray( hMenu, indexMenu,
+ idCmdFirst, _pIdTable = aDSTreeVerbs );
+ break;
+
+ case RESOURCEDISPLAYTYPE_NDSCONTAINER:
+ hres = InsertCommandsArray( hMenu, indexMenu,
+ idCmdFirst, _pIdTable = aDSVerbs );
+ break;
+
+ case RESOURCEDISPLAYTYPE_SERVER:
+ {
+ // BUGBUG: Do we need to check if the server name is local
+ // and disallow operation???
+
+ hres = InsertCommandsArray( hMenu, indexMenu,
+ idCmdFirst, _pIdTable = aServerVerbs );
+
+ if (!SUCCEEDED(hres))
+ break;
+
+ LPBYTE pBuffer = NULL;
+ DWORD EntriesRead = 0;
+ DWORD ResumeKey = 0;
+ WCHAR szServerName[MAX_PATH + 1];
+
+ NwExtractServerName( pNetRes->lpRemoteName, szServerName );
+
+ // See if we are connected.
+ DWORD err = NwGetConnectionStatus( szServerName,
+ &ResumeKey,
+ &pBuffer,
+ &EntriesRead );
+
+ if ( err == NO_ERROR && EntriesRead > 0 )
+ {
+ PCONN_STATUS pConnStatus = (PCONN_STATUS) pBuffer;
+
+ ASSERT( EntriesRead == 1 );
+
+ if ( pConnStatus->fPreferred )
+ {
+ // This is a implicit preferred server connection
+ // so, don't show the connection and don't let the user
+ // logout of it since rdr doesn't allow it.
+ ::EnableMenuItem( hMenu,
+ LookupResource( aServerVerbs,
+ IDO_VERB_LOGOUT),
+ MF_GRAYED | MF_BYCOMMAND);
+
+ }
+ else if ( pConnStatus->fNds )
+ {
+ BOOL fInDefaultTree = FALSE;
+
+ err = NwIsServerInDefaultTree( pNetRes->lpRemoteName, &fInDefaultTree );
+
+ if ( (err == NO_ERROR) && fInDefaultTree )
+ {
+ // NDS connection and in the default tree, disable the Attach As button
+ ::EnableMenuItem( hMenu,
+ LookupResource( aServerVerbs,
+ IDO_VERB_ATTACHAS),
+ MF_GRAYED | MF_BYCOMMAND );
+ }
+ }
+ }
+ else
+ {
+ // If we are not attached or if error occurred when getting
+ // connection status, then disable the Logout button.
+ ::EnableMenuItem( hMenu,
+ LookupResource( aServerVerbs,
+ IDO_VERB_LOGOUT),
+ MF_GRAYED | MF_BYCOMMAND);
+ }
+
+ if ( pBuffer != NULL )
+ {
+ LocalFree( pBuffer );
+ pBuffer = NULL;
+ }
+ break;
+ }
+
+ default:
+ // Must return number of menu items we added.
+ // Nothing added here.
+ hres = ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS, FACILITY_NULL, 0 ));
+ break;
+
+ }
+
+ return hres;
+}
+
+//
+// FUNCTION: CNWObjContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO)
+//
+// PURPOSE: Called by the shell after the user has selected on of the
+// menu items that was added in QueryContextMenu().
+//
+// PARAMETERS:
+// lpcmi - Pointer to an CMINVOKECOMMANDINFO structure
+//
+// RETURN VALUE:
+//
+//
+// COMMENTS:
+//
+
+STDMETHODIMP CNWObjContextMenu::InvokeCommand( LPCMINVOKECOMMANDINFO lpcmi )
+{
+ HRESULT hres = ResultFromScode(E_INVALIDARG);
+ UINT idCmd = LookupCommand( _pIdTable , lpcmi->lpVerb );
+
+ if ( !idCmd )
+ return hres;
+
+ LPNETRESOURCE pNetRes = (LPNETRESOURCE) _buffer;
+
+ switch ( idCmd )
+ {
+ case IDO_VERB_GLOBALWHOAMI:
+ hres = NWUIGlobalWhoAmI( lpcmi->hwnd );
+ break;
+
+ case IDO_VERB_TREEWHOAMI:
+ hres = NWUIWhoAmI( lpcmi->hwnd, pNetRes );
+ break;
+
+#if 0
+ case IDO_VERB_SETDEFAULTCONTEXT:
+ hres = NWUISetDefaultContext( lpcmi->hwnd, pNetRes );
+ break;
+#endif
+
+ case IDO_VERB_WHOAMI:
+ hres = NWUIWhoAmI( lpcmi->hwnd, pNetRes );
+ break;
+
+ case IDO_VERB_LOGOUT:
+ {
+ BOOL fDisconnected = FALSE;
+ hres = NWUILogOut( lpcmi->hwnd, pNetRes, &fDisconnected );
+ if ( hres == NOERROR && fDisconnected )
+ {
+ // Logout is successful, need to notify shell
+
+ FORMATETC fmte = { g_cfIDList ? g_cfIDList
+ : (g_cfIDList=RegisterClipboardFormat( CFSTR_SHELLIDLIST)),
+ (DVTARGETDEVICE FAR *)NULL,
+ DVASPECT_CONTENT,
+ -1,
+ TYMED_HGLOBAL };
+ STGMEDIUM medium;
+
+ hres = _pDataObj->GetData( &fmte, &medium);
+
+ if (SUCCEEDED(hres))
+ {
+ // We got pointer to IDList
+ LPIDA pida = (LPIDA)GlobalLock(medium.hGlobal);
+
+ if ( pida )
+ {
+ BYTE BufIDList[MAX_SHELL_IDLIST_SIZE];
+ LPITEMIDLIST pidl = (LPITEMIDLIST) BufIDList;
+
+ if ( pidl )
+ {
+ // Convert IDA to IDList for this call
+ HIDA_GetIDList( pida,
+ 0, // One object should present
+ pidl ,
+ MAX_SHELL_IDLIST_SIZE);
+
+ // Call SHchangeNotify
+ g_pFuncSHChangeNotify( SHCNE_SERVERDISCONNECT,
+ SHCNF_IDLIST,
+ pidl,
+ NULL);
+ }
+
+ GlobalUnlock(medium.hGlobal);
+ }
+ }
+
+ }
+ break;
+ }
+
+ case IDO_VERB_ATTACHAS:
+ hres = NWUIAttachAs( lpcmi->hwnd, pNetRes );
+ break;
+ }
+
+ return hres;
+}
+
+
+//
+// FUNCTION: CNWObjContextMenu::GetCommandString( UINT, UINT, UINT FAR *, LPSTR, UINT )
+//
+// PURPOSE: Called by the shell after the user has selected on of the
+// menu items that was added in QueryContextMenu().
+//
+// PARAMETERS:
+//
+// RETURN VALUE:
+//
+//
+// COMMENTS:
+//
+
+STDMETHODIMP CNWObjContextMenu::GetCommandString( UINT idCmd,
+ UINT uFlags,
+ UINT FAR *reserved,
+ LPSTR pszName,
+ UINT cchMax )
+{
+ if ( uFlags == GCS_HELPTEXT && _pIdTable != NULL )
+ {
+ ::LoadString( ::hmodNW,
+ IDS_VERBS_HELP_BASE + _pIdTable[idCmd].idResourceString,
+ (LPWSTR) pszName,
+ cchMax );
+
+ return NOERROR;
+ }
+
+ return E_NOTIMPL;
+}
+
+//
+// FUNCTION: CNWFldContextMenu::QueryContextMenu(HMENU, UINT, UINT, UINT, UINT)
+//
+// PURPOSE: Called by the shell just before the context menu is displayed.
+// This is where you add your specific menu items.
+//
+// PARAMETERS:
+// hMenu - Handle to the context menu
+// indexMenu - Index of where to begin inserting menu items
+// idCmdFirst - Lowest value for new menu ID's
+// idCmtLast - Highest value for new menu ID's
+// uFlags - Specifies the context of the menu event
+//
+// RETURN VALUE:
+//
+//
+// COMMENTS:
+//
+
+STDMETHODIMP CNWFldContextMenu::QueryContextMenu( HMENU hMenu,
+ UINT indexMenu,
+ UINT idCmdFirst,
+ UINT idCmdLast,
+ UINT uFlags )
+{
+ UINT idCmd = idCmdFirst;
+
+ if ( IsNetWareObject() )
+ {
+ WCHAR szFullPath[MAX_PATH+1];
+
+ if ( GetFSObject( szFullPath, sizeof( szFullPath )) == NOERROR )
+ {
+ BOOL fUNC = FALSE;
+
+ // Check if the name at least contains the "\\server\share\dir"
+ // We need to add "Map Network Drive" menu in this case.
+ if (( szFullPath[0] == L'\\') && ( szFullPath[1] == L'\\'))
+ {
+ LPWSTR pszLastSlash = wcschr( szFullPath + 2, L'\\');
+ if ( pszLastSlash )
+ pszLastSlash = wcschr( pszLastSlash+1, L'\\');
+
+ if ( pszLastSlash != NULL )
+ fUNC = TRUE;
+ }
+
+ if ( fUNC )
+ {
+ LPNETRESOURCE pNetRes = (LPNETRESOURCE) _buffer;
+ WCHAR szProvider[MAX_PATH+1];
+
+ // Build a net resource that can be used to connect
+
+ // store the provider name first
+ wcscpy( szProvider, pNetRes->lpProvider );
+
+ // zero out the memory cause it is filled by IsNetWareObject
+ RtlZeroMemory( pNetRes, sizeof(NETRESOURCE));
+
+ pNetRes->dwType = RESOURCETYPE_DISK;
+ pNetRes->lpRemoteName = (LPWSTR) ((DWORD)pNetRes + sizeof(NETRESOURCE));
+ wcscpy( pNetRes->lpRemoteName, szFullPath );
+
+ pNetRes->lpProvider = (LPWSTR) ((DWORD)pNetRes->lpRemoteName + (wcslen(szFullPath)+1)*sizeof(WCHAR));
+ wcscpy( pNetRes->lpProvider, szProvider );
+
+ if ( InsertMenu(hMenu, indexMenu, MF_SEPARATOR | MF_BYPOSITION, 0, NULL))
+ {
+ indexMenu++;
+ }
+
+ return InsertCommandsArray( hMenu, indexMenu,
+ idCmdFirst, aDirectoryVerbs );
+ }
+ }
+ }
+
+ // Must return number of menu items we added.
+ // Nothing added here.
+ return ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS, FACILITY_NULL, 0 ));
+
+}
+
+//
+// FUNCTION: CNWFldContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO)
+//
+// PURPOSE: Called by the shell after the user has selected on of the
+// menu items that was added in QueryContextMenu().
+//
+// PARAMETERS:
+// lpcmi - Pointer to an CMINVOKECOMMANDINFO structure
+//
+// RETURN VALUE:
+//
+//
+// COMMENTS:
+//
+
+STDMETHODIMP CNWFldContextMenu::InvokeCommand( LPCMINVOKECOMMANDINFO lpcmi )
+{
+ HRESULT hres = ResultFromScode(E_INVALIDARG);
+ UINT idCmd = LookupCommand( aDirectoryVerbs , lpcmi->lpVerb );
+
+ if ( !idCmd )
+ return hres;
+
+ LPNETRESOURCE pNetRes = (LPNETRESOURCE) _buffer;
+
+ switch ( idCmd )
+ {
+ case IDO_VERB_MAPNETWORKDRIVE:
+ hres = NWUIMapNetworkDrive( lpcmi->hwnd, pNetRes );
+ break;
+ }
+
+ return hres;
+}
+
+
+//
+// FUNCTION: CNWFldContextMenu::GetCommandString( UINT, UINT, UINT FAR *, LPSTR, UINT )
+//
+// PURPOSE: Called by the shell after the user has selected on of the
+// menu items that was added in QueryContextMenu().
+//
+// PARAMETERS:
+//
+// RETURN VALUE:
+//
+//
+// COMMENTS:
+//
+
+STDMETHODIMP CNWFldContextMenu::GetCommandString( UINT idCmd,
+ UINT uFlags,
+ UINT FAR *reserved,
+ LPSTR pszName,
+ UINT cchMax )
+{
+ if ( uFlags == GCS_HELPTEXT )
+ {
+ ::LoadString( ::hmodNW,
+ IDS_VERBS_HELP_BASE + IDO_VERB_MAPNETWORKDRIVE,
+ (LPWSTR) pszName,
+ cchMax );
+
+ return NOERROR;
+ }
+
+ return E_NOTIMPL;
+}
+
+//
+// Method checks if the selected object belongs the netware provider
+//
+BOOL CNWFldContextMenu::IsNetWareObject( VOID )
+{
+ LPNETRESOURCE pNetRes = (LPNETRESOURCE) _buffer;
+
+ if ( !::GetNetResourceFromShell( _pDataObj,
+ pNetRes,
+ sizeof(_buffer)))
+ {
+ // Cannot get the NETRESOURCE of the selected object,
+ // hence assume that the object is not a NetWare object.
+ return FALSE;
+ }
+
+ if ( ( pNetRes->lpProvider != NULL )
+ && ( _wcsicmp( pNetRes->lpProvider, g_szProviderName ) == 0 )
+ )
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+//
+// Method obtains file system name associated with selected shell object
+//
+HRESULT CNWFldContextMenu::GetFSObject( LPWSTR pszPath, UINT cbMaxPath )
+{
+ FORMATETC fmte = { CF_HDROP,
+ (DVTARGETDEVICE FAR *) NULL,
+ DVASPECT_CONTENT,
+ -1,
+ TYMED_HGLOBAL };
+
+ STGMEDIUM medium;
+ HRESULT hres = _pDataObj->GetData( &fmte, &medium);
+
+ if (SUCCEEDED(hres))
+ {
+ if ( g_pFuncSHDragQueryFile )
+ {
+ HDROP hdrop = (HDROP) medium.hGlobal;
+ UINT cFiles = (*g_pFuncSHDragQueryFile)( hdrop, (UINT)-1, NULL, 0 );
+
+ (*g_pFuncSHDragQueryFile)( hdrop, 0, pszPath, cbMaxPath );
+
+ ODS(L"CNWFldContextMenu::GetFSObject()\n");
+ ODS( pszPath );
+ ODS(L"\n");
+ }
+
+ //
+ // HACK: We are supposed to call ReleaseStgMedium. This is a temporary
+ // hack until OLE 2.01 for Chicago is released.
+ //
+ if (medium.pUnkForRelease)
+ {
+ medium.pUnkForRelease->Release();
+ }
+ else
+ {
+ GlobalFree(medium.hGlobal);
+ }
+ }
+
+ return hres;
+}
+
+
+// FUNCTION: CNWHoodContextMenu::QueryContextMenu(HMENU, UINT, UINT, UINT, UINT)
+//
+// PURPOSE: Called by the shell just before the context menu is displayed.
+// This is where you add your specific menu items.
+//
+// PARAMETERS:
+// hMenu - Handle to the context menu
+// indexMenu - Index of where to begin inserting menu items
+// idCmdFirst - Lowest value for new menu ID's
+// idCmtLast - Highest value for new menu ID's
+// uFlags - Specifies the context of the menu event
+//
+// RETURN VALUE:
+//
+//
+// COMMENTS:
+//
+
+STDMETHODIMP CNWHoodContextMenu::QueryContextMenu( HMENU hMenu,
+ UINT indexMenu,
+ UINT idCmdFirst,
+ UINT idCmdLast,
+ UINT uFlags )
+{
+ // First, insert a menu separator
+ if ( InsertMenu(hMenu, indexMenu, MF_SEPARATOR | MF_BYPOSITION, 0, NULL))
+ {
+ indexMenu++;
+ }
+
+ // Then, insert the verbs
+ return InsertCommandsArray( hMenu, indexMenu,
+ idCmdFirst, aHoodVerbs );
+}
+
+//
+// FUNCTION: CNWHoodContextMenu::InvokeCommand(LPCMINVOKECOMMANDINFO)
+//
+// PURPOSE: Called by the shell after the user has selected on of the
+// menu items that was added in QueryContextMenu().
+//
+// PARAMETERS:
+// lpcmi - Pointer to an CMINVOKECOMMANDINFO structure
+//
+// RETURN VALUE:
+//
+//
+// COMMENTS:
+//
+
+STDMETHODIMP CNWHoodContextMenu::InvokeCommand( LPCMINVOKECOMMANDINFO lpcmi )
+{
+ HRESULT hres = ResultFromScode(E_INVALIDARG);
+ UINT idCmd = LookupCommand( aHoodVerbs , lpcmi->lpVerb );
+
+ if ( !idCmd )
+ return hres;
+
+ switch ( idCmd )
+ {
+ case IDO_VERB_GLOBALWHOAMI:
+ hres = NWUIGlobalWhoAmI( lpcmi->hwnd );
+ break;
+ }
+
+ return hres;
+}
+
+
+//
+// FUNCTION: CNWHoodContextMenu::GetCommandString( UINT, UINT, UINT FAR *, LPSTR, UINT)
+//
+// PURPOSE: Called by the shell after the user has selected on of the
+// menu items that was added in QueryContextMenu().
+//
+// PARAMETERS:
+//
+// RETURN VALUE:
+//
+//
+// COMMENTS:
+//
+
+STDMETHODIMP CNWHoodContextMenu::GetCommandString( UINT idCmd,
+ UINT uFlags,
+ UINT FAR *reserved,
+ LPSTR pszName,
+ UINT cchMax )
+{
+ if ( uFlags == GCS_HELPTEXT )
+ {
+ ::LoadString( ::hmodNW,
+ IDS_VERBS_HELP_BASE + IDO_VERB_GLOBALWHOAMI,
+ (LPWSTR) pszName,
+ cchMax );
+
+ return NOERROR;
+ }
+
+ return E_NOTIMPL;
+}
+
+//
+// Method gets the NETRESOURCE of the selected object
+//
+BOOL GetNetResourceFromShell( LPDATAOBJECT pDataObj,
+ LPNETRESOURCE pNetRes,
+ UINT dwBufferSize )
+{
+ FORMATETC fmte = { g_cfNetResource ? g_cfNetResource
+ : (g_cfNetResource=RegisterClipboardFormat(CFSTR_NETRESOURCES)),
+ (DVTARGETDEVICE FAR *) NULL,
+ DVASPECT_CONTENT,
+ -1,
+ TYMED_HGLOBAL };
+
+ STGMEDIUM medium;
+ UINT cItems;
+
+ if ( pNetRes == NULL )
+ return FALSE;
+
+ memset( pNetRes, 0, dwBufferSize );
+
+ if ( !g_pFuncSHGetNetResource ) // Not loaded
+ return FALSE;
+
+ HRESULT hres = pDataObj->GetData( &fmte, &medium );
+
+ if (!SUCCEEDED(hres))
+ return FALSE;
+
+ HNRES hnres = medium.hGlobal;
+
+ // Get the number of selected items
+ cItems = (*g_pFuncSHGetNetResource)( hnres, (UINT)-1, NULL, 0);
+
+ if ( cItems == 0 ) // Nothing selected
+ return FALSE;
+
+ // Get the NETRESOURCE of the first item
+ (*g_pFuncSHGetNetResource)( hnres, 0, pNetRes, dwBufferSize);
+
+#if DBG
+ WCHAR szTemp[32];
+ wsprintf(szTemp, L"DisplayType = %d\n", pNetRes->dwDisplayType );
+
+ ODS(L"\n**** GetNetResourceFromShell ***\n");
+ ODS(pNetRes->lpProvider );
+ ODS(L"\n");
+ ODS(pNetRes->lpRemoteName );
+ ODS(L"\n");
+ ODS(szTemp );
+ ODS(L"\n\n");
+#endif
+
+ //
+ // HACK: We are supposed to call ReleaseStgMedium. This is a temporary
+ // hack until OLE 2.01 for Chicago is released.
+ //
+ if (medium.pUnkForRelease)
+ {
+ medium.pUnkForRelease->Release();
+ }
+ else
+ {
+ GlobalFree(medium.hGlobal);
+ }
+
+ return TRUE;
+}
+
+//-------------------------------------------------------------------//
+
+HRESULT InsertCommandsArray( HMENU hMenu,
+ UINT indexMenu,
+ UINT idCmdFirst,
+ LPNWMENUITEM aVerbs )
+{
+ UINT idNewCmdFirst = idCmdFirst;
+ WCHAR szVerb[MAX_VERB_SIZE];
+
+ for ( int i = 0; aVerbs[i].idResourceString ; i++)
+ {
+ if ( ::LoadString( ::hmodNW,
+ aVerbs[i].idResourceString + IDS_VERBS_BASE,
+ szVerb,
+ sizeof(szVerb)/sizeof(szVerb[0])))
+ {
+ if (::InsertMenu( hMenu,
+ indexMenu,
+ MF_STRING | MF_BYPOSITION,
+ idNewCmdFirst,
+ szVerb))
+ {
+ // Add command id to the array
+ aVerbs[i].idCommand = idNewCmdFirst;
+
+ // Update command id and index
+ idNewCmdFirst++;
+ if (indexMenu != (UINT)-1)
+ indexMenu++;
+ }
+ }
+ }
+
+ return ResultFromScode(MAKE_SCODE(SEVERITY_SUCCESS,
+ FACILITY_NULL,
+ (USHORT)(idNewCmdFirst-idCmdFirst)));
+}
+
+UINT LookupCommand( LPNWMENUITEM aVerbs, LPCSTR pszCmd )
+{
+ if (HIWORD(pszCmd))
+ {
+ // Possible that shell will use string commands, but unlikely
+
+ WCHAR szVerb[MAX_VERB_SIZE];
+ for ( int i=0; aVerbs[i].idResourceString; i++)
+ {
+ if ( ::LoadString( ::hmodNW,
+ aVerbs[i].idResourceString + IDS_VERBS_BASE,
+ szVerb,
+ sizeof(szVerb)/sizeof(szVerb[0])))
+ {
+ if( ::lstrcmpi( (LPCWSTR) pszCmd, szVerb) == 0)
+ return( aVerbs[i].idResourceString);
+ }
+ }
+
+ return 0;
+ }
+ else
+ {
+ return( aVerbs[LOWORD(pszCmd)].idResourceString);
+ }
+}
+
+UINT LookupResource( LPNWMENUITEM aVerbs, UINT uiResourceOffset )
+{
+ for ( int i = 0; aVerbs[i].idResourceString; i++ )
+ {
+ if ( aVerbs[i].idResourceString == uiResourceOffset )
+ return aVerbs[i].idCommand;
+ }
+
+ return 0;
+}
+
+//-------------------------------------------------------------------//
+
+#define _ILSkip(pidl, cb) ((LPITEMIDLIST)(((BYTE*)(pidl))+cb))
+#define _ILNext(pidl) _ILSkip(pidl, (pidl)->mkid.cb)
+
+#define HIDA_GetPIDLFolder(pida) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[0])
+#define HIDA_GetPIDLItem(pida, i) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[i+1])
+
+static
+UINT WINAPI MyILGetSize(LPCITEMIDLIST pidl)
+{
+ UINT cbTotal = 0;
+ if (pidl)
+ {
+ cbTotal += sizeof(pidl->mkid.cb); // Null terminator
+ while (pidl->mkid.cb)
+ {
+ cbTotal += pidl->mkid.cb;
+ pidl = _ILNext(pidl);
+ }
+ }
+
+ return cbTotal;
+}
+
+UINT WINAPI HIDA_GetIDList( LPIDA hida, UINT i, LPITEMIDLIST pidlOut, UINT cbMax)
+{
+ LPCITEMIDLIST pidlFolder = HIDA_GetPIDLFolder((LPIDA)hida);
+ LPCITEMIDLIST pidlItem = HIDA_GetPIDLItem((LPIDA)hida, i);
+
+ UINT cbFolder = MyILGetSize(pidlFolder)-sizeof(USHORT);
+ UINT cbItem = MyILGetSize(pidlItem);
+
+ if (cbMax < cbFolder+cbItem)
+ {
+ if (pidlOut)
+ pidlOut->mkid.cb = 0;
+ }
+ else
+ {
+ memmove(pidlOut, pidlFolder, cbFolder);
+ memmove(((LPBYTE)pidlOut)+cbFolder, pidlItem, cbItem);
+ }
+
+ return (cbFolder+cbItem);
+}
diff --git a/private/nw/svcdlls/nwwks/client/nwshmisc.cxx b/private/nw/svcdlls/nwwks/client/nwshmisc.cxx
new file mode 100644
index 000000000..f375d8559
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwshmisc.cxx
@@ -0,0 +1,261 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ nwshmisc.cxx
+
+Abstract:
+
+ This module implements misc methods used in the shell extension classes.
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 25-Oct-1995
+
+--*/
+
+extern "C"
+{
+#include <windows.h>
+#include <shellapi.h>
+#include <shlobj.h>
+#define DONT_WANT_SHELLDEBUG
+#include <shsemip.h>
+}
+
+#include "nwshcmn.h"
+
+#define MAX_RESOURCE_STRING_LENGTH 256
+
+VOID HideControl( HWND hwndDlg, WORD wID )
+{
+ HWND hwndTmp = ::GetDlgItem( hwndDlg, wID );
+ ::EnableWindow( hwndTmp, FALSE );
+ ::ShowWindow( hwndTmp, FALSE );
+}
+
+VOID UnHideControl( HWND hwndDlg, WORD wID )
+{
+ HWND hwndTmp = ::GetDlgItem( hwndDlg, wID );
+ ::EnableWindow( hwndTmp, TRUE );
+ ::ShowWindow( hwndTmp, TRUE );
+}
+
+VOID EnableDlgItem( HWND hwndDlg, WORD wID, BOOL fEnable)
+{
+ HWND hwndTmp = ::GetDlgItem( hwndDlg, wID );
+
+ ::EnableWindow( hwndTmp, fEnable);
+}
+
+/*
+ * LoadErrorPrintf
+ * -------------
+ *
+ * Uses normal printf style format string
+ */
+DWORD
+LoadMsgErrorPrintf(
+ LPWSTR *ppszMessage,
+ UINT uiMsg,
+ DWORD errNum
+)
+{
+ DWORD nLen = 0;
+ DWORD err = NO_ERROR;
+ LPWSTR pszError = NULL;
+ WCHAR szError[20];
+
+ *ppszMessage = NULL;
+
+ //
+ // Try to get the error string associated with the given number
+ // from the system. If we cannot find the string, then
+ // just show the number.
+ //
+
+ nLen = ::FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM
+ | FORMAT_MESSAGE_IGNORE_INSERTS
+ | FORMAT_MESSAGE_ALLOCATE_BUFFER,
+ NULL, // ignored
+ errNum, // Message resource id
+ 0, // Language id
+ (LPWSTR) &pszError,
+ // Return pointer to formatted text
+ 256, // Min.length
+ NULL );
+
+ if ( nLen == 0 || pszError == NULL )
+ {
+ wsprintf( szError, L"%d", errNum );
+ }
+
+ err = LoadMsgPrintf( ppszMessage, uiMsg, pszError? pszError : szError );
+
+ if ( pszError )
+ ::LocalFree( pszError );
+
+ return err;
+}
+
+/*
+ * LoadMsgPrintf
+ * -------------
+ *
+ * Uses normal printf style format string
+ */
+DWORD
+LoadMsgPrintf(
+ LPWSTR *ppszMessage,
+ UINT uiMsg,
+ ...
+ )
+{
+ WCHAR szMessage[512];
+ DWORD err = NO_ERROR;
+ DWORD nLen = 0;
+ va_list start;
+
+ va_start( start, uiMsg );
+
+ *ppszMessage = NULL;
+
+ if ( ::LoadString( ::hmodNW, uiMsg, szMessage,
+ sizeof(szMessage)/sizeof(szMessage[0])))
+ {
+ nLen = ::FormatMessage( FORMAT_MESSAGE_FROM_STRING
+ | FORMAT_MESSAGE_ALLOCATE_BUFFER,
+ szMessage,
+ 0, // Message resource id, ignored
+ 0, // Language id
+ (LPWSTR) ppszMessage,
+ // Return pointer to formatted text
+ 256, // Min.length
+ &start );
+
+ if ( nLen == 0 || *ppszMessage == NULL )
+ err = GetLastError();
+
+ }
+
+ va_end(start);
+
+ return err;
+}
+
+/*
+ * MsgBoxErrorPrintf
+ * ------------
+ *
+ * Message box routine
+ *
+ */
+DWORD
+MsgBoxErrorPrintf(
+ HWND hwnd,
+ UINT uiMsg,
+ UINT uiTitle,
+ UINT uiFlags,
+ DWORD errNum,
+ LPWSTR pszInsertStr
+ )
+{
+ DWORD nLen = 0;
+ DWORD err = NO_ERROR;
+ LPWSTR pszError = NULL;
+ WCHAR szError[20];
+
+ nLen = ::FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM
+ | FORMAT_MESSAGE_IGNORE_INSERTS
+ | FORMAT_MESSAGE_ALLOCATE_BUFFER,
+ NULL, // ignored
+ errNum, // Message resource id
+ 0, // Language id
+ (LPWSTR) &pszError,
+ // Return pointer to formatted text
+ 256, // Min.length
+ NULL );
+
+ if ( nLen == 0 || pszError == NULL )
+ wsprintf( szError, L"%d", errNum );
+
+ if ( pszInsertStr )
+ {
+ err = MsgBoxPrintf( hwnd, uiMsg, uiTitle, uiFlags,
+ pszError? pszError : szError, pszInsertStr );
+ }
+ else
+ {
+ err = MsgBoxPrintf( hwnd, uiMsg, uiTitle, uiFlags,
+ pszError? pszError : szError );
+ }
+
+ if ( pszError )
+ ::LocalFree( pszError );
+
+ return err;
+
+}
+
+
+/*
+ * MsgBoxPrintf
+ * ------------
+ *
+ * Message box routine
+ *
+ */
+DWORD
+MsgBoxPrintf(
+ HWND hwnd,
+ UINT uiMsg,
+ UINT uiTitle,
+ UINT uiFlags,
+ ...
+ )
+{
+ WCHAR szTitle[MAX_RESOURCE_STRING_LENGTH];
+ WCHAR szMessage[MAX_RESOURCE_STRING_LENGTH];
+ LPWSTR lpFormattedMessage = NULL;
+ DWORD err = NO_ERROR;
+ va_list start;
+
+ va_start(start,uiFlags);
+
+ if ( ::LoadString( ::hmodNW, uiMsg, szMessage, sizeof(szMessage)/sizeof(szMessage[0]))
+ && ::LoadString( ::hmodNW, uiTitle, szTitle, sizeof(szTitle)/sizeof(szTitle[0]))
+ )
+ {
+ DWORD nLen = ::FormatMessage( FORMAT_MESSAGE_FROM_STRING
+ | FORMAT_MESSAGE_ALLOCATE_BUFFER,
+ szMessage,
+ 0, // Resource Id, ignored
+ NULL, // Language Id, ignored
+ (LPWSTR) &lpFormattedMessage,
+ // Return pointer to formatted text
+ 256, // Min.length
+ &start );
+
+ if ( nLen == 0 || lpFormattedMessage == NULL )
+ {
+ err = GetLastError();
+ goto CleanExit;
+ }
+
+ err = ::MessageBox( hwnd,
+ lpFormattedMessage,
+ szTitle,
+ uiFlags );
+
+ ::LocalFree( lpFormattedMessage );
+ }
+
+CleanExit:
+
+ va_end(start);
+
+ return err;
+
+}
diff --git a/private/nw/svcdlls/nwwks/client/nwshprop.cxx b/private/nw/svcdlls/nwwks/client/nwshprop.cxx
new file mode 100644
index 000000000..01a3773a2
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwshprop.cxx
@@ -0,0 +1,1326 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ nwshprop.cxx
+
+Abstract:
+
+ This module implements the property pages of shell extension classes.
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 25-Oct-1995
+
+--*/
+
+extern "C"
+{
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include <commctrl.h>
+#include <shellapi.h>
+#include <shlobj.h>
+#define DONT_WANT_SHELLDEBUG
+#include <shsemip.h>
+
+#include <nwapi32.h>
+#include <ndsapi32.h>
+#include <nwmisc.h>
+#include <nds.h>
+#include "nwshrc.h"
+#include "nwutil.h"
+#include "drawpie.h"
+}
+
+#include "nwshcmn.h"
+#include "nwshext.h"
+
+
+LPWSTR WINAPI AddCommas( DWORD dw, LPWSTR pszResult, DWORD dwSize );
+LPWSTR WINAPI ShortSizeFormat64( ULONGLONG dw64, LPWSTR szBuf );
+
+#define NAMESPACE_DOS 0
+#define NAMESPACE_MAC 1
+#define NAMESPACE_UNIX 2
+#define NAMESPACE_FTAM 3
+#define NAMESPACE_OS2 4
+
+
+BOOL
+CALLBACK
+NDSPage_DlgProc(
+ HWND hDlg,
+ UINT uMessage,
+ WPARAM wParam ,
+ LPARAM lParam
+);
+
+BOOL
+CALLBACK
+NWPage_DlgProc(
+ HWND hDlg,
+ UINT uMessage,
+ WPARAM wParam ,
+ LPARAM lParam
+);
+
+//
+// FUNCTION: CNWObjContextMenu::AddPages(LPFNADDPROPSHEETPAGE, LPARAM)
+//
+// PURPOSE: Called by the shell just before the property sheet is displayed.
+//
+// PARAMETERS:
+// lpfnAddPage - Pointer to the Shell's AddPage function
+// lParam - Passed as second parameter to lpfnAddPage
+//
+// RETURN VALUE:
+//
+// NOERROR in all cases. If for some reason our pages don't get added,
+// the Shell still needs to bring up the Properties... sheet.
+//
+// COMMENTS:
+//
+
+STDMETHODIMP CNWObjContextMenu::AddPages(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
+{
+ LPNETRESOURCE pNetRes = (LPNETRESOURCE) _buffer;
+
+ if ( !::GetNetResourceFromShell( _pDataObj, pNetRes, sizeof( _buffer )))
+ {
+ // We could not get the net resource of the current object,
+ // hence we could not add the property pages
+ return NOERROR;
+ }
+
+ DWORD dwDialogId = 0;
+ BOOL fIsNds = NwIsNdsSyntax( pNetRes->lpRemoteName );
+
+ switch ( pNetRes->dwDisplayType )
+ {
+ case RESOURCEDISPLAYTYPE_SERVER:
+ dwDialogId = DLG_SERVER_SUMMARYINFO;
+ break;
+
+ case RESOURCEDISPLAYTYPE_NDSCONTAINER:
+ break;
+
+ case RESOURCEDISPLAYTYPE_TREE:
+ // We need to set fIsNds to TRUE since a tree name "\\tree"
+ // does not look like a NDS name
+ // and hence NwIsNDsSyntax will return FALSE.
+ fIsNds = TRUE;
+ break;
+
+ case RESOURCEDISPLAYTYPE_SHARE:
+ if ( pNetRes->dwType == RESOURCETYPE_PRINT )
+ dwDialogId = DLG_PRINTER_SUMMARYINFO;
+ else
+ dwDialogId = DLG_SHARE_SUMMARYINFO;
+ break;
+
+ case RESOURCEDISPLAYTYPE_ROOT:
+ case RESOURCEDISPLAYTYPE_NETWORK:
+ default:
+ // No property page need to be added here. Just return success.
+ return NOERROR;
+ }
+
+ if ( dwDialogId != 0 )
+ {
+ FillAndAddPage( lpfnAddPage, lParam,
+ (DLGPROC) ::NWPage_DlgProc,
+ MAKEINTRESOURCE( dwDialogId ));
+ }
+
+ // NOTE: Do we need to add another property page contain admin tools
+ // for chicago peer servers? Probably not!
+
+ if ( fIsNds )
+ {
+ FillAndAddPage( lpfnAddPage, lParam,
+ (DLGPROC) ::NDSPage_DlgProc,
+ pNetRes->dwDisplayType == RESOURCEDISPLAYTYPE_TREE ?
+ MAKEINTRESOURCE( DLG_NDS_SUMMARYINFO) :
+ MAKEINTRESOURCE( DLG_NDSCONT_SUMMARYINFO));
+
+ // NOTE: Need to add a page for system policy here if the user has admin privileges
+ // in the NDS tree.
+ }
+
+ return NOERROR;
+}
+
+//
+// FUNCTION: CNWObjContextMenu::ReplacePage(UINT, LPFNADDPROPSHEETPAGE, LPARAM)
+//
+// PURPOSE: Called by the shell only for Control Panel property sheet
+// extensions
+//
+// PARAMETERS:
+// uPageID - ID of page to be replaced
+// lpfnReplaceWith - Pointer to the Shell's Replace function
+// lParam - Passed as second parameter to lpfnReplaceWith
+//
+// RETURN VALUE:
+//
+// E_NOTIMPL, since we don't support this function. It should never be
+// called.
+
+// COMMENTS:
+//
+
+STDMETHODIMP CNWObjContextMenu::ReplacePage(UINT uPageID,
+ LPFNADDPROPSHEETPAGE lpfnReplaceWith,
+ LPARAM lParam)
+{
+ return E_NOTIMPL;
+}
+
+VOID CNWObjContextMenu::FillAndAddPage( LPFNADDPROPSHEETPAGE lpfnAddPage,
+ LPARAM lParam,
+ DLGPROC pfnDlgProc,
+ LPWSTR pszTemplate )
+{
+ PROPSHEETPAGE psp;
+ HPROPSHEETPAGE hpage;
+
+ psp.dwSize = sizeof(psp); // no extra data.
+ psp.dwFlags = PSP_USEREFPARENT;
+ psp.hInstance = ::hmodNW;
+ psp.pfnDlgProc = pfnDlgProc;
+ psp.pcRefParent = (UINT *) &g_cRefThisDll;
+ psp.pszTemplate = pszTemplate;
+ psp.hIcon = 0;
+ psp.pszTitle = NULL;
+ psp.pfnCallback = NULL;
+
+ psp.lParam = (LPARAM) this;
+ this->AddRef();
+
+ hpage = CreatePropertySheetPage(&psp);
+
+ if (hpage)
+ {
+ if (!lpfnAddPage(hpage, lParam))
+ DestroyPropertySheetPage(hpage);
+ }
+
+}
+
+// The following are arrays of help contexts for the property dialogs
+
+static DWORD aServerIds[] = { IDD_SERVER_NAME ,IDH_SERVERNAME,
+ IDD_SERVER_COMMENT_TXT ,IDH_COMMENT,
+ IDD_SERVER_COMMENT ,IDH_COMMENT,
+ IDD_SERVER_VERSION_TXT ,IDH_VERSION,
+ IDD_SERVER_VERSION ,IDH_VERSION,
+ IDD_SERVER_REVISION_TXT,IDH_REVISION,
+ IDD_SERVER_REVISION ,IDH_REVISION,
+ IDD_SERVER_CONNECT_TXT ,IDH_CONNINUSE,
+ IDD_SERVER_CONNECT ,IDH_CONNINUSE,
+ IDD_SERVER_MAXCON_TXT ,IDH_MAXCONNS,
+ IDD_SERVER_MAXCON ,IDH_MAXCONNS,
+ 0, 0 };
+
+static DWORD aPrinterIds[] = { IDD_PRINTER_NAME, IDH_PRINTER_NAME,
+ IDD_PRINTER_QUEUE_TXT, IDH_PRINTER_QUEUE,
+ IDD_PRINTER_QUEUE, IDH_PRINTER_QUEUE,
+ 0, 0 };
+
+static DWORD aNDSIds[] = { IDD_NDS_NAME_TXT, IDH_NDS_NAME,
+ IDD_NDS_NAME, IDH_NDS_NAME,
+ IDD_NDS_CLASS_TXT, IDH_NDS_CLASS,
+ IDD_NDS_CLASS, IDH_NDS_CLASS,
+ IDD_NDS_COMMENT_TXT, IDH_NDS_COMMENT,
+ IDD_NDS_COMMENT, IDH_NDS_COMMENT,
+ 0, 0 };
+
+static DWORD aShareIds[] = { IDD_SHARE_NAME, IDH_SHARE_NAME,
+ IDD_SHARE_SERVER_TXT, IDH_SHARE_SERVER,
+ IDD_SHARE_SERVER, IDH_SHARE_SERVER,
+ IDD_SHARE_PATH_TXT, IDH_SHARE_PATH,
+ IDD_SHARE_PATH, IDH_SHARE_PATH,
+ IDD_SHARE_USED_SPC_CLR,IDH_SHARE_USED_SPC,
+ IDD_SHARE_USED_SPC_TXT,IDH_SHARE_USED_SPC,
+ IDD_SHARE_USED_SPC, IDH_SHARE_USED_SPC,
+ IDD_SHARE_USED_SPC_MB, IDH_SHARE_USED_SPC,
+ IDD_SHARE_FREE_SPC_CLR,IDH_SHARE_FREE_SPC,
+ IDD_SHARE_FREE_SPC_TXT,IDH_SHARE_FREE_SPC,
+ IDD_SHARE_FREE_SPC, IDH_SHARE_FREE_SPC,
+ IDD_SHARE_FREE_SPC_MB, IDH_SHARE_FREE_SPC,
+ IDD_SHARE_MAX_SPC_TXT, IDH_SHARE_MAX_SPC,
+ IDD_SHARE_MAX_SPC, IDH_SHARE_MAX_SPC,
+ IDD_SHARE_MAX_SPC_MB, IDH_SHARE_MAX_SPC,
+ IDD_SHARE_PIE, IDH_SHARE_PIE,
+ IDD_SHARE_LFN_TXT, IDH_SHARE_LFN_TXT,
+ 0,0 };
+
+
+#if 0
+static DWORD aWGIds[] = { IDD_WRKGRP_NAME, IDH_WRKGRP_NAME,
+ IDD_WRKGRP_TYPE_TXT, IDH_WRKGRP_TYPE,
+ IDD_WRKGRP_TYPE, IDH_WRKGRP_TYPE,
+ 0, 0 };
+
+static DWORD aNDSAdminIds[] = { IDD_ENABLE_SYSPOL, IDH_ENABLE_SYSPOL,
+ IDD_VOLUME_LABEL, IDH_VOLUME_LABEL,
+ IDD_VOLUME, IDH_VOLUME,
+ IDD_DIRECTORY_LABEL,IDH_DIRECTORY_LABEL,
+ IDD_DIRECTORY, IDH_DIRECTORY,
+ 0, 0 };
+#endif
+
+
+void NDSPage_InitDialog(
+ HWND hDlg,
+ LPPROPSHEETPAGE psp
+ )
+{
+ CNWObjContextMenu* pPSClass = (CNWObjContextMenu*)psp->lParam;
+ LPNETRESOURCE pnr = NULL;
+ DWORD err = NO_ERROR;
+ NTSTATUS ntstatus = STATUS_SUCCESS;
+ HANDLE hTreeConn = NULL;
+
+ if ( pPSClass )
+ pnr = pPSClass->QueryNetResource();
+
+ if ( pnr == NULL )
+ {
+ ASSERT(FALSE);
+
+ // This should not happen. We can always get the net resource which is queried
+ // during AddPages.
+ return;
+ }
+
+ do { // not a loop, just wanted to break on error
+
+ LPWSTR pszRemoteName = pnr->lpRemoteName;
+
+ if ( pszRemoteName[0] == L' ') // tree names have a space in front " \\mardev"
+ pszRemoteName++;
+
+ if ( pnr->dwDisplayType == RESOURCEDISPLAYTYPE_TREE )
+ {
+ SetDlgItemText( hDlg, IDD_NDS_NAME, pszRemoteName + 2); // get past backslashes
+ }
+ else
+ {
+ SetDlgItemText( hDlg, IDD_NDS_NAME, wcschr( pszRemoteName + 2, L'\\') + 1);
+ }
+
+ DWORD dwOid;
+
+ err = NwOpenAndGetTreeInfo( pszRemoteName,
+ &hTreeConn,
+ &dwOid );
+
+ if ( err != NO_ERROR )
+ {
+ break;
+ }
+
+ BYTE RawResponse[TWO_KB];
+ DWORD RawResponseSize = sizeof(RawResponse);
+
+ ntstatus = NwNdsReadObjectInfo( hTreeConn,
+ dwOid,
+ RawResponse,
+ RawResponseSize );
+
+ if ( !NT_SUCCESS( ntstatus ))
+ {
+ err = RtlNtStatusToDosError(ntstatus);
+ break;
+ }
+
+ LPBYTE pObjectClass = RawResponse;
+
+ pObjectClass += sizeof( NDS_RESPONSE_GET_OBJECT_INFO ) + sizeof(DWORD);
+
+ ::SetDlgItemText( hDlg, IDD_NDS_CLASS, (LPWSTR) pObjectClass );
+
+ // NOTE: The description can only be read successfully with administrative privilege
+
+ DWORD iterHandle = (DWORD) -1;
+ UNICODE_STRING uAttrName;
+ PNDS_RESPONSE_READ_ATTRIBUTE pReadAttrResponse = (PNDS_RESPONSE_READ_ATTRIBUTE) RawResponse;
+
+ RtlInitUnicodeString( &uAttrName, L"Description");
+
+ ntstatus = NwNdsReadAttribute( hTreeConn,
+ dwOid,
+ &iterHandle,
+ &uAttrName,
+ RawResponse,
+ sizeof(RawResponse));
+
+ if ( !NT_SUCCESS( ntstatus )
+ || ( pReadAttrResponse->CompletionCode != 0 )
+ || ( pReadAttrResponse->NumAttributes == 0 )
+ )
+ {
+ // we don't need to set the error since this attribute can only be read by admins and
+ // we might get an error indicating this.
+ break;
+ }
+
+ PNDS_ATTRIBUTE pNdsAttribute = (PNDS_ATTRIBUTE)((DWORD) RawResponse+sizeof(NDS_RESPONSE_READ_ATTRIBUTE));
+
+ LPWSTR pszComment = (LPWSTR) ((DWORD) pNdsAttribute + 3*sizeof(DWORD)
+ + pNdsAttribute->AttribNameLength + sizeof(DWORD));
+ ::SetDlgItemText(hDlg,IDD_NDS_COMMENT, pszComment);
+
+ } while (FALSE);
+
+
+ if ( hTreeConn )
+ CloseHandle( hTreeConn );
+
+ if ( err != NO_ERROR )
+ {
+ LPWSTR pszMessage = NULL;
+
+ if ( ::LoadMsgErrorPrintf( &pszMessage,
+ IDS_MESSAGE_GETINFO_ERROR,
+ err ) == NO_ERROR )
+ {
+ UnHideControl( hDlg, IDD_ERROR );
+ SetDlgItemText( hDlg, IDD_ERROR, pszMessage);
+ ::LocalFree( pszMessage );
+ }
+ }
+
+ return;
+}
+
+BOOL
+CALLBACK
+NDSPage_DlgProc(
+ HWND hDlg,
+ UINT uMessage,
+ WPARAM wParam ,
+ LPARAM lParam)
+{
+
+ LPPROPSHEETPAGE psp = (LPPROPSHEETPAGE)GetWindowLong(hDlg, DWL_USER);
+
+ switch (uMessage)
+ {
+ //
+ // When the shell creates a dialog box for a property sheet page,
+ // it passes the pointer to the PROPSHEETPAGE data structure as
+ // lParam. The dialog procedures of extensions typically store it
+ // in the DWL_USER of the dialog box window.
+ //
+ case WM_INITDIALOG:
+ SetWindowLong(hDlg, DWL_USER, lParam);
+ psp = (LPPROPSHEETPAGE)lParam;
+
+ NDSPage_InitDialog( hDlg, psp);
+
+ break;
+
+ case WM_DESTROY:
+ {
+ CNWObjContextMenu* pPSClass = (CNWObjContextMenu *)(psp->lParam);
+
+ if (pPSClass)
+ pPSClass->Release();
+
+ SetWindowLong(hDlg, DWL_USER, NULL);
+ break;
+ }
+
+ case WM_COMMAND:
+ break;
+
+ case WM_NOTIFY:
+ {
+ switch (((NMHDR *)lParam)->code)
+ {
+ case PSN_SETACTIVE:
+ {
+ CNWObjContextMenu *pPSClass =
+ (CNWObjContextMenu *)(psp->lParam);
+
+ pPSClass->_paHelpIds = aNDSIds;
+ break;
+ }
+
+ default:
+ break;
+ }
+ break;
+ }
+
+ case WM_HELP:
+ {
+ CNWObjContextMenu* pPSClass = (CNWObjContextMenu *)(psp->lParam);
+
+ if ( pPSClass && pPSClass->_paHelpIds )
+ {
+ WinHelp( (HWND) ((LPHELPINFO)lParam)->hItemHandle,
+ NW_HELP_FILE,
+ HELP_WM_HELP,
+ (DWORD)(LPVOID)pPSClass->_paHelpIds );
+ }
+ break;
+ }
+
+
+ case WM_CONTEXTMENU:
+ {
+ CNWObjContextMenu* pPSClass = (CNWObjContextMenu*)(psp->lParam);
+
+ if (pPSClass && pPSClass->_paHelpIds)
+ {
+ WinHelp( (HWND)wParam,
+ NW_HELP_FILE,
+ HELP_CONTEXTMENU,
+ (DWORD)(LPVOID)pPSClass->_paHelpIds );
+ }
+ break;
+ }
+
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+
+}
+
+#define HIDWORD(_qw) (DWORD)(_qw>>32)
+#define LODWORD(_qw) (DWORD)(_qw)
+
+void Share_InitDialog(HWND hDlg, LPPROPSHEETPAGE psp)
+{
+ CNWObjContextMenu* pPSClass = (CNWObjContextMenu *)psp->lParam;
+ LPNETRESOURCE pnr;
+ DWORD err = NO_ERROR;
+ BOOL fDirectoryMap = FALSE;
+
+ if ( pPSClass )
+ pnr = pPSClass->QueryNetResource();
+
+ if ( pnr == NULL )
+ {
+ ASSERT(FALSE);
+
+ // This should not happen. We can always get the net resource which is queried
+ // during AddPages.
+ return;
+ }
+
+ do { // not a loop, just wanted to break out if error occurred
+
+ WCHAR szShare[MAX_PATH+1];
+ WCHAR szServer[MAX_PATH+1] = L"";
+
+ // Get the share name
+ NwExtractShareName( pnr->lpRemoteName, szShare );
+ SetDlgItemText( hDlg, IDD_SHARE_NAME, szShare );
+
+ HideControl( hDlg, IDD_SHARE_PATH_TXT);
+ HideControl( hDlg, IDD_SHARE_PATH);
+ HideControl( hDlg, IDD_SHARE_LFN_TXT);
+
+ // Get the server name
+ if ( NwIsNdsSyntax( pnr->lpRemoteName ))
+ {
+ NTSTATUS ntstatus = STATUS_SUCCESS;
+ HANDLE hTreeConn = NULL;
+ DWORD dwOid;
+
+ err = NwOpenAndGetTreeInfo( pnr->lpRemoteName,
+ &hTreeConn,
+ &dwOid );
+
+ if ( err != NO_ERROR )
+ break;
+
+ BYTE RawResponse[TWO_KB];
+ DWORD RawResponseSize = sizeof(RawResponse);
+
+ DWORD iterHandle = (DWORD) -1;
+ UNICODE_STRING uAttrName;
+ PNDS_RESPONSE_READ_ATTRIBUTE pReadAttrResponse = (PNDS_RESPONSE_READ_ATTRIBUTE) RawResponse;
+
+ RtlInitUnicodeString( &uAttrName, L"Path");
+
+ ntstatus = NwNdsReadAttribute( hTreeConn,
+ dwOid,
+ &iterHandle,
+ &uAttrName,
+ RawResponse,
+ sizeof(RawResponse));
+
+ CloseHandle( hTreeConn );
+ hTreeConn = NULL;
+
+ if ( NT_SUCCESS( ntstatus )
+ && ( pReadAttrResponse->CompletionCode == 0 )
+ && ( pReadAttrResponse->NumAttributes != 0 )
+ )
+ {
+ // We are successful in reading the attribute. Hence this is a directory map.
+ fDirectoryMap = TRUE;
+
+ PNDS_ATTRIBUTE pNdsAttribute = (PNDS_ATTRIBUTE)((DWORD) RawResponse+sizeof(NDS_RESPONSE_READ_ATTRIBUTE));
+
+ PDWORD pdwNameSpace = (PDWORD) ((DWORD) pNdsAttribute + 3*sizeof(DWORD)
+ + pNdsAttribute->AttribNameLength
+ + sizeof(WORD) // need this due to bug in return value???
+ + sizeof(DWORD));
+
+ // See if the directory supports long file name
+ // BUGBUG: what about other name spaces?
+ // Only on directory map will Win95 show LFN support or not.
+ if ( *pdwNameSpace == NAMESPACE_OS2 )
+ {
+ UnHideControl( hDlg, IDD_SHARE_LFN_TXT );
+ }
+
+ // Now, try to get the volume the directory map is on
+ PDWORD pdwVolumeLen = (PDWORD) ((DWORD) pdwNameSpace + sizeof(DWORD));
+ LPWSTR pszVolume = (LPWSTR) ((DWORD) pdwNameSpace + 2*sizeof(DWORD));
+ LPWSTR pszPath = (LPWSTR) ((DWORD) pszVolume + *pdwVolumeLen
+ + sizeof(WORD) // need this due to bug in return value???
+ + sizeof(DWORD));
+
+
+ WCHAR szFullPath[MAX_PATH+1];
+ LPWSTR pszTemp;
+ wcscpy( szFullPath, pnr->lpRemoteName );
+ if ( pszTemp = wcschr( szFullPath + 2, L'\\'))
+ *(pszTemp + 1) = 0;
+
+ wcscat( szFullPath, pszVolume );
+
+ err = NwGetNdsVolumeInfo( szFullPath, szServer, sizeof(szServer), szShare, sizeof(szShare));
+
+
+ // Now, display the path of the directory map
+ if ( err == NO_ERROR )
+ {
+ wcscpy( szFullPath, szShare );
+ wcscat( szFullPath, L"\\");
+ wcscat( szFullPath, pszPath );
+
+ UnHideControl(hDlg, IDD_SHARE_PATH_TXT);
+ UnHideControl(hDlg, IDD_SHARE_PATH);
+ SetDlgItemText( hDlg, IDD_SHARE_PATH, szFullPath );
+ }
+ }
+ else // this is a volume
+ {
+
+ // For NDS names, the unc path might not contain the server name.
+ // So, we need to get the server name that this share is on.
+ // Also, we need the original volume name like "SYS" instead of "MARS_SRV0_SYS"
+ err = NwGetNdsVolumeInfo( pnr->lpRemoteName, szServer, sizeof(szServer), szShare, sizeof(szShare));
+ }
+
+ if ( err != NO_ERROR )
+ break;
+ }
+ else // in the form \\server\sys
+ {
+ NwExtractServerName( pnr->lpRemoteName, szServer );
+ }
+
+ SetDlgItemText( hDlg, IDD_SHARE_SERVER, szServer);
+
+ NWCONN_HANDLE hConn = NULL;
+ if ( NWCAttachToFileServerW( szServer, 0, &hConn ) != SUCCESSFUL )
+ {
+ err = GetLastError();
+ break;
+ }
+
+ NWVOL_NUM nVolNum;
+ char szAnsiShare[MAX_PATH+1];
+
+ ::CharToOem( szShare, szAnsiShare );
+ if ( NWCGetVolumeNumber( hConn, szAnsiShare, &nVolNum ) != SUCCESSFUL )
+ {
+ err = GetLastError();
+ break;
+ }
+
+ DWORD dwSectorSize = 0x200;
+
+ DWORD dwTotalBlocks = 0;
+ DWORD dwFreeBlocks = 0;
+ DWORD dwPurgeable = 0;
+ DWORD dwNotYetPurged = 0;
+ DWORD dwSectors= 0;
+ DWORD dwTotalDir= 0;
+ DWORD dwAvailDir= 0;
+
+ ULONGLONG qwTot = 0;
+ ULONGLONG qwFree = 0;
+
+ WCHAR szFormat[30];
+ WCHAR szTemp[80];
+ WCHAR szTemp2[30];
+
+
+ // NOTE: 2.x servers does not support NWCGetVolumeUsage.
+ // Hence, for 2.x servers, an error will always be shown
+
+ if ( NWCGetVolumeUsage( hConn,
+ nVolNum,
+ &dwTotalBlocks,
+ &dwFreeBlocks,
+ &dwPurgeable,
+ &dwNotYetPurged,
+ &dwTotalDir,
+ &dwAvailDir,
+ (LPBYTE) &dwSectors ) != SUCCESSFUL )
+ {
+ err = GetLastError();
+ break;
+ }
+
+ dwFreeBlocks += dwPurgeable;
+
+ qwTot = (ULONGLONG) dwSectorSize * (ULONGLONG) dwSectors * (ULONGLONG) dwTotalBlocks;
+
+ qwFree = (ULONGLONG) dwSectorSize * (ULONGLONG) dwSectors * (ULONGLONG) dwFreeBlocks;
+
+ if (::LoadString(::hmodNW, IDS_BYTES, szFormat, sizeof(szFormat)/sizeof(szFormat[0])))
+ {
+ if (!HIDWORD(qwTot-qwFree))
+ {
+ wsprintf(szTemp, szFormat, AddCommas(LODWORD(qwTot) - LODWORD(qwFree), szTemp2, sizeof(szTemp2)/sizeof(szTemp2[0])));
+ SetDlgItemText(hDlg,IDD_SHARE_USED_SPC, szTemp);
+ }
+
+ if (!HIDWORD(qwFree))
+ {
+ wsprintf(szTemp, szFormat, AddCommas(LODWORD(qwFree), szTemp2, sizeof(szTemp2)/sizeof(szTemp2[0])));
+ SetDlgItemText(hDlg, IDD_SHARE_FREE_SPC, szTemp);
+ }
+
+ if (!HIDWORD(qwTot))
+ {
+ wsprintf(szTemp, szFormat, AddCommas(LODWORD(qwTot), szTemp2, sizeof(szTemp2)/sizeof(szTemp2[0])));
+ SetDlgItemText(hDlg, IDD_SHARE_MAX_SPC, szTemp);
+ }
+ }
+
+ ShortSizeFormat64(qwTot-qwFree, szTemp);
+ SetDlgItemText(hDlg, IDD_SHARE_USED_SPC_MB, szTemp);
+
+ ShortSizeFormat64(qwFree, szTemp);
+ SetDlgItemText(hDlg, IDD_SHARE_FREE_SPC_MB, szTemp);
+
+ ShortSizeFormat64(qwTot, szTemp);
+ SetDlgItemText(hDlg, IDD_SHARE_MAX_SPC_MB, szTemp);
+
+ pPSClass->_fGotClusterInfo = TRUE;
+ pPSClass->_dwTotal = dwTotalBlocks;
+ pPSClass->_dwFree = dwFreeBlocks;
+
+ (VOID) NWCDetachFromFileServer( hConn );
+
+ } while (FALSE);
+
+ if ( err != NO_ERROR )
+ {
+ LPWSTR pszMessage = NULL;
+
+ HideControl(hDlg, IDD_SHARE_USED_SPC_CLR);
+ HideControl(hDlg, IDD_SHARE_USED_SPC_TXT);
+ HideControl(hDlg, IDD_SHARE_USED_SPC);
+ HideControl(hDlg, IDD_SHARE_USED_SPC_MB);
+
+ HideControl(hDlg, IDD_SHARE_FREE_SPC_CLR);
+ HideControl(hDlg, IDD_SHARE_FREE_SPC_TXT);
+ HideControl(hDlg, IDD_SHARE_FREE_SPC);
+ HideControl(hDlg, IDD_SHARE_FREE_SPC_MB);
+
+ HideControl(hDlg, IDD_SHARE_MAX_SPC_TXT);
+ HideControl(hDlg, IDD_SHARE_MAX_SPC);
+ HideControl(hDlg, IDD_SHARE_MAX_SPC_MB);
+
+ HideControl(hDlg, IDD_SHARE_PIE);
+
+ if ( ::LoadMsgErrorPrintf( &pszMessage,
+ IDS_MESSAGE_GETINFO_ERROR,
+ err ) == NO_ERROR )
+ {
+ UnHideControl( hDlg, IDD_ERROR );
+ SetDlgItemText( hDlg, IDD_ERROR, pszMessage);
+ ::LocalFree( pszMessage );
+ }
+ }
+
+
+} /* endproc Share_InitDialog */
+
+void Printer_InitDialog(HWND hDlg, LPPROPSHEETPAGE psp)
+{
+ CNWObjContextMenu* pPSClass = (CNWObjContextMenu *)psp->lParam;
+ LPNETRESOURCE pnr;
+ DWORD err = NO_ERROR;
+
+ if ( pPSClass )
+ pnr = pPSClass->QueryNetResource();
+
+ if ( pnr == NULL )
+ {
+ ASSERT(FALSE);
+
+ // This should not happen. We can always get the net resource which is queried
+ // during AddPages.
+ return;
+ }
+
+ do { // not a loop, just wanted to break out if error occurred
+
+ WCHAR szShare[MAX_PATH];
+ NwExtractShareName( pnr->lpRemoteName, szShare );
+
+
+ SetDlgItemText(hDlg,IDD_PRINTER_NAME, szShare);
+
+ if ( NwIsNdsSyntax( pnr->lpRemoteName))
+ {
+ NTSTATUS ntstatus = STATUS_SUCCESS;
+ HANDLE hTreeConn = NULL;
+ DWORD dwOid;
+
+ err = NwOpenAndGetTreeInfo( pnr->lpRemoteName,
+ &hTreeConn,
+ &dwOid );
+
+ if ( err != NO_ERROR )
+ break;
+
+ BYTE RawResponse[TWO_KB];
+ DWORD RawResponseSize = sizeof(RawResponse);
+
+ DWORD iterHandle = (DWORD) -1;
+ UNICODE_STRING uAttrName;
+ PNDS_RESPONSE_READ_ATTRIBUTE pReadAttrResponse = (PNDS_RESPONSE_READ_ATTRIBUTE) RawResponse;
+
+ RtlInitUnicodeString( &uAttrName, L"Queue Directory");
+
+ ntstatus = NwNdsReadAttribute( hTreeConn,
+ dwOid,
+ &iterHandle,
+ &uAttrName,
+ RawResponse,
+ sizeof(RawResponse));
+
+ CloseHandle( hTreeConn );
+ hTreeConn = NULL;
+
+ if ( !NT_SUCCESS( ntstatus )
+ || ( pReadAttrResponse->CompletionCode != 0 )
+ || ( pReadAttrResponse->NumAttributes == 0 )
+ )
+ {
+ // we don't need to set the error since this attribute can only be read by admins and
+ // we might get an error indicating this.
+ break;
+ }
+
+ PNDS_ATTRIBUTE pNdsAttribute = (PNDS_ATTRIBUTE)((DWORD) RawResponse+sizeof(NDS_RESPONSE_READ_ATTRIBUTE));
+
+ LPWSTR pszQueueFile = (LPWSTR) ((DWORD) pNdsAttribute + 3*sizeof(DWORD)
+ + pNdsAttribute->AttribNameLength + sizeof(DWORD));
+ ::SetDlgItemText( hDlg, IDD_PRINTER_QUEUE, pszQueueFile);
+ }
+ else // bindery server
+ {
+ NWCONN_HANDLE hConn = NULL;
+ WCHAR szServer[MAX_PATH+1];
+
+ NwExtractServerName( pnr->lpRemoteName, szServer );
+
+ if ( NWCAttachToFileServerW( szServer, 0, &hConn ) != SUCCESSFUL )
+ err = GetLastError();
+
+ if ( err == NO_ERROR )
+ {
+ char szAnsiShare[MAX_PATH+1];
+ char Buffer[NW_DATA_SIZE];
+ NWFLAGS ucMoreFlag, ucPropertyFlag;
+
+ memset( Buffer, 0, sizeof(Buffer));
+ ::CharToOem( szShare, szAnsiShare );
+
+ if ( NWCReadPropertyValue( hConn,
+ szAnsiShare,
+ OT_PRINT_QUEUE,
+ "Q_DIRECTORY",
+ 1,
+ Buffer,
+ &ucMoreFlag,
+ &ucPropertyFlag ) != SUCCESSFUL )
+ {
+ err = GetLastError();
+ }
+
+ if ( err == NO_ERROR )
+ {
+ WCHAR uBuffer[NW_DATA_SIZE];
+
+ ::OemToChar( Buffer, uBuffer );
+
+ ::SetDlgItemText( hDlg, IDD_PRINTER_QUEUE, (LPWSTR) uBuffer);
+ }
+ else
+ {
+ err = NO_ERROR; // Only supervisor has read/write so don't show the error.
+ }
+
+ (VOID) NWCDetachFromFileServer( hConn );
+ }
+ }
+
+ } while (FALSE);
+
+ if ( err != NO_ERROR )
+ {
+ LPWSTR pszMessage = NULL;
+
+ if ( ::LoadMsgErrorPrintf( &pszMessage,
+ IDS_MESSAGE_GETINFO_ERROR,
+ err ) == NO_ERROR )
+ {
+ UnHideControl( hDlg, IDD_ERROR );
+ SetDlgItemText( hDlg, IDD_ERROR, pszMessage);
+ ::LocalFree( pszMessage );
+ }
+ }
+
+} /* endproc Printer_InitDialog */
+
+void Server_InitDialog(HWND hDlg, LPPROPSHEETPAGE psp)
+{
+ CNWObjContextMenu* pPSClass = (CNWObjContextMenu *)psp->lParam;
+ LPNETRESOURCE pnr;
+ DWORD err = NO_ERROR;
+
+ if ( pPSClass )
+ pnr = pPSClass->QueryNetResource();
+
+ if ( pnr == NULL )
+ {
+ ASSERT(FALSE);
+
+ // This should not happen. We can always get the net resource which is queried
+ // during AddPages.
+ return;
+ }
+
+ do { // not a loop, just wanted to break out if error occurred
+
+ WCHAR szServer[MAX_PATH];
+ NwExtractServerName( pnr->lpRemoteName, szServer );
+
+ SetDlgItemText( hDlg, IDD_SERVER_NAME, szServer );
+
+ //
+ // Get some server information
+ //
+
+ NWCONN_HANDLE hConn = NULL;
+ if ( NWCAttachToFileServerW( szServer, 0, &hConn ) != SUCCESSFUL )
+ {
+ err = GetLastError();
+ break;
+ }
+
+ VERSION_INFO vInfo;
+
+ if ( NWCGetFileServerVersionInfo( hConn, &vInfo ) != SUCCESSFUL )
+ {
+ err = GetLastError();
+ break;
+ }
+
+ WCHAR szTemp[512];
+ char szAnsiCompany[512];
+ char szAnsiVersion[512];
+ char szAnsiRevision[512];
+
+ if ( NWCGetFileServerDescription( hConn, szAnsiCompany, szAnsiVersion,
+ szAnsiRevision ) != SUCCESSFUL )
+ {
+ err = GetLastError();
+ break;
+ }
+
+ // OemToChar( szAnsiCompany, szTemp );
+ // wcscat( szTemp, L" " );
+ // OemToChar( szAnsiVersion, szTemp + wcslen( szTemp ));
+
+ OemToChar( szAnsiVersion, szTemp );
+
+ ::SetDlgItemText( hDlg, IDD_SERVER_VERSION, szTemp);
+
+ OemToChar( szAnsiRevision, szTemp );
+ ::SetDlgItemText( hDlg, IDD_SERVER_REVISION, szTemp );
+
+ WCHAR szNumber[12];
+
+ ::wsprintf(szNumber,L"%d", vInfo.connsInUse );
+ ::SetDlgItemText( hDlg, IDD_SERVER_CONNECT, szNumber);
+
+ ::wsprintf(szNumber,L"%4d", vInfo.ConnsSupported);
+ ::SetDlgItemText( hDlg, IDD_SERVER_MAXCON, szNumber);
+
+ (VOID) NWCDetachFromFileServer( hConn );
+
+#if 0
+ // Now deal with Chicago specific fields
+ if (pPSClass->_fIsPeerServer) {
+
+ pXNCPResp pxresp = (pXNCPResp) pPSClass->_bufServerExInfo.QueryPtr(); ;
+ pXGetServerInfoResp lpInfoPtr = (pXGetServerInfoResp)(pxresp+1);
+ CHAR szString[128];
+ STRING *pNWString;
+
+ // Next field is workgroup name
+ pNWString = (STRING *)(lpInfoPtr->passThruServer.str+lpInfoPtr->passThruServer.len);
+ pNWString = (STRING *)(pNWString->str+pNWString->len);
+
+ // And next after that is comment
+
+ ::OemToCharBuff((LPCSTR)pNWString->str,szString,pNWString->len);
+ szString[pNWString->len] = '\0';
+
+ UnHideControl( hDlg, IDD_SERVER_COMMENT_TXT );
+ UnHideControl( hDlg, IDD_SERVER_COMMENT );
+ ::SetDlgItemText(hDlg,IDD_SERVER_COMMENT,szString);
+
+ } else
+#endif
+
+ } while (FALSE);
+
+ if ( err != NO_ERROR )
+ {
+ LPWSTR pszMessage = NULL;
+
+ if ( ::LoadMsgErrorPrintf( &pszMessage,
+ IDS_MESSAGE_GETINFO_ERROR,
+ err ) == NO_ERROR )
+ {
+ UnHideControl( hDlg, IDD_ERROR );
+ SetDlgItemText( hDlg, IDD_ERROR, pszMessage);
+ ::LocalFree( pszMessage );
+ }
+ }
+
+} /* endproc Server_InitDialog */
+
+#if 0
+void Wrkgrp_InitDialog(HWND hDlg, LPPROPSHEETPAGE psp)
+{
+
+ CNWObjContextMenu *pPSClass = (CNWObjContextMenu *)psp->lParam;
+ LPNETRESOURCE pnr;
+
+ if ( pPSClass )
+ pnr = (LPNETRESOURCE)pPSClass->_bufNR.QueryPtr();
+
+ if ( pnr )
+ {
+ // Set name static control
+ SetDlgItemText(hDlg,IDD_WRKGRP_NAME, pnr->lpRemoteName);
+ }
+
+}
+#endif
+
+COLORREF c_crPieColors[] =
+{
+ RGB( 0, 0, 255), // Blue
+ RGB(255, 0, 255), // Red-Blue
+ RGB( 0, 0, 128), // 1/2 Blue
+ RGB(128, 0, 128), // 1/2 Red-Blue
+} ;
+
+void _DrvPrshtDrawItem(HWND hDlg, LPPROPSHEETPAGE psp, const DRAWITEMSTRUCT * lpdi)
+{
+ COLORREF crDraw;
+ RECT rcItem = lpdi->rcItem;
+ HBRUSH hbDraw, hbOld;
+ SIZE size;
+ HDC hDC;
+ CNWObjContextMenu* pPSClass = (CNWObjContextMenu *)psp->lParam;
+
+ if (pPSClass->_fGotClusterInfo == FALSE)
+ return;
+
+ switch (lpdi->CtlID)
+ {
+ case IDD_SHARE_PIE:
+
+ hDC = GetDC(hDlg);
+ GetTextExtentPoint(hDC, L"W", 1, &size);
+ ReleaseDC(hDlg, hDC);
+
+ DrawPie(lpdi->hDC, &lpdi->rcItem,
+ pPSClass->_dwTotal ? 1000*(pPSClass->_dwTotal-pPSClass->_dwFree)/pPSClass->_dwTotal : 1000,
+ pPSClass->_dwFree==0 || pPSClass->_dwFree==pPSClass->_dwTotal,
+ size.cy*2/3, c_crPieColors);
+
+ break;
+
+ case IDD_SHARE_USED_SPC_CLR:
+ crDraw = c_crPieColors[DP_USEDCOLOR];
+ goto DrawColor;
+
+ case IDD_SHARE_FREE_SPC_CLR:
+ crDraw = c_crPieColors[DP_FREECOLOR];
+ goto DrawColor;
+
+DrawColor:
+ hbDraw = CreateSolidBrush(crDraw);
+ if (hbDraw)
+ {
+ hbOld = (HBRUSH) SelectObject(lpdi->hDC, hbDraw);
+ if (hbOld)
+ {
+ PatBlt(lpdi->hDC, rcItem.left, rcItem.top,
+ rcItem.right-rcItem.left,
+ rcItem.bottom-rcItem.top,
+ PATCOPY);
+
+ SelectObject(lpdi->hDC, hbOld);
+ }
+
+ DeleteObject(hbDraw);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+BOOL CALLBACK NWPage_DlgProc(HWND hDlg, UINT uMessage, WPARAM wParam , LPARAM lParam)
+{
+ LPPROPSHEETPAGE psp = (LPPROPSHEETPAGE)GetWindowLong(hDlg, DWL_USER);
+
+ switch (uMessage)
+ {
+ //
+ // When the shell creates a dialog box for a property sheet page,
+ // it passes the pointer to the PROPSHEETPAGE data structure as
+ // lParam. The dialog procedures of extensions typically store it
+ // in the DWL_USER of the dialog box window.
+ //
+ case WM_INITDIALOG:
+ SetWindowLong(hDlg, DWL_USER, lParam);
+ psp = (LPPROPSHEETPAGE)lParam;
+
+ if (psp->pszTemplate == MAKEINTRESOURCE(DLG_SERVER_SUMMARYINFO))
+ Server_InitDialog(hDlg, psp);
+ else if (psp->pszTemplate == MAKEINTRESOURCE(DLG_SHARE_SUMMARYINFO))
+ Share_InitDialog(hDlg, psp);
+ else if (psp->pszTemplate == MAKEINTRESOURCE(DLG_PRINTER_SUMMARYINFO))
+ Printer_InitDialog(hDlg, psp);
+#if 0
+ else if (psp->pszTemplate == MAKEINTRESOURCE(DLG_WRKGRP_SUMMARYINFO))
+ Wrkgrp_InitDialog(hDlg, psp);
+#endif
+
+ break;
+
+ case WM_DRAWITEM:
+ _DrvPrshtDrawItem(hDlg, psp, (DRAWITEMSTRUCT *)lParam);
+ break;
+
+ case WM_DESTROY:
+ {
+ CNWObjContextMenu* pPSClass = (CNWObjContextMenu *)(psp->lParam);
+
+ if (pPSClass) {
+ pPSClass->Release();
+ }
+
+ SetWindowLong(hDlg, DWL_USER, NULL);
+ }
+ break;
+
+ case WM_COMMAND:
+ break;
+
+ case WM_NOTIFY:
+ switch (((NMHDR *)lParam)->code) {
+ case PSN_SETACTIVE:
+ {
+ CNWObjContextMenu *pPSClass = (CNWObjContextMenu *)(psp->lParam);
+
+ if (psp->pszTemplate == MAKEINTRESOURCE(DLG_SERVER_SUMMARYINFO))
+ pPSClass->_paHelpIds = aServerIds;
+ else if (psp->pszTemplate == MAKEINTRESOURCE(DLG_SHARE_SUMMARYINFO))
+ pPSClass->_paHelpIds = aShareIds;
+ else if (psp->pszTemplate == MAKEINTRESOURCE(DLG_PRINTER_SUMMARYINFO))
+ pPSClass->_paHelpIds = aPrinterIds;
+#if 0
+ else if (psp->pszTemplate == MAKEINTRESOURCE(DLG_WRKGRP_SUMMARYINFO))
+ pPSClass->_paHelpIds = aWGIds;
+#endif
+
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ break;
+
+ case WM_HELP:
+ {
+ CNWObjContextMenu* pPSClass = (CNWObjContextMenu *)(psp->lParam);
+
+ if (pPSClass && pPSClass->_paHelpIds)
+ {
+ WinHelp( (HWND) ((LPHELPINFO)lParam)->hItemHandle,
+ NW_HELP_FILE,
+ HELP_WM_HELP,
+ (DWORD)(LPVOID)pPSClass->_paHelpIds );
+ }
+ }
+
+ break;
+
+ case WM_CONTEXTMENU:
+ {
+ CNWObjContextMenu* pPSClass = (CNWObjContextMenu*)(psp->lParam);
+
+ if (pPSClass && pPSClass->_paHelpIds)
+ {
+ WinHelp( (HWND)wParam,
+ NW_HELP_FILE,
+ HELP_CONTEXTMENU,
+ (DWORD)(LPVOID)pPSClass->_paHelpIds );
+ }
+ break;
+ }
+
+ default:
+ return(FALSE);
+ }
+
+ return(TRUE);
+
+}
+
+// Regular StrToInt; stops at first non-digit.
+int WINAPI MyStrToInt(LPWSTR lpSrc) // atoi()
+{
+
+#define ISDIGIT(c) ((c) >= '0' && (c) <= '9')
+
+ int n = 0;
+ BOOL bNeg = FALSE;
+
+ if (*lpSrc == L'-') {
+ bNeg = TRUE;
+ lpSrc++;
+ }
+
+ while (ISDIGIT(*lpSrc)) {
+ n *= 10;
+ n += *lpSrc - L'0';
+ lpSrc++;
+ }
+ return bNeg ? -n : n;
+}
+
+// The following functions are stolen from win\core\shell\shelldll
+// takes a DWORD add commas etc to it and puts the result in the buffer
+LPWSTR WINAPI AddCommas( DWORD dw, LPWSTR pszResult, DWORD dwSize )
+{
+ WCHAR szTemp[30];
+ WCHAR szSep[5];
+ NUMBERFMT nfmt;
+
+ nfmt.NumDigits=0;
+ nfmt.LeadingZero=0;
+ GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, szSep, sizeof(szSep)/sizeof(szSep[0]));
+ nfmt.Grouping = MyStrToInt(szSep);
+ GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, szSep, sizeof(szSep)/sizeof(szSep[0]));
+ nfmt.lpDecimalSep = nfmt.lpThousandSep = szSep;
+ nfmt.NegativeOrder= 0;
+
+#pragma data_seg(".text", "CODE")
+ wsprintf(szTemp, L"%lu", dw);
+#pragma data_seg()
+
+ if (GetNumberFormat(LOCALE_USER_DEFAULT, 0, szTemp, &nfmt, pszResult, dwSize) == 0)
+ lstrcpy(pszResult, szTemp);
+
+ return pszResult;
+}
+
+const short pwOrders[] = {IDS_BYTES, IDS_ORDERKB, IDS_ORDERMB, IDS_ORDERGB, IDS_ORDERTB};
+
+LPWSTR WINAPI ShortSizeFormat64(ULONGLONG dw64, LPWSTR szBuf)
+{
+ int i;
+ UINT wInt, wLen, wDec;
+ WCHAR szTemp[10], szOrder[20], szFormat[5];
+
+ if (dw64 < 1000) {
+#pragma data_seg(".text", "CODE")
+ wsprintf(szTemp, L"%d", LODWORD(dw64));
+#pragma data_seg()
+ i = 0;
+ goto AddOrder;
+ }
+
+ for (i = 1; i< sizeof(pwOrders)/sizeof(pwOrders[0])-1 && dw64 >= 1000L * 1024L; dw64 >>= 10, i++);
+ /* do nothing */
+
+ wInt = LODWORD(dw64 >> 10);
+ AddCommas(wInt, szTemp, sizeof(szTemp)/sizeof(szTemp[0]));
+ wLen = lstrlen(szTemp);
+ if (wLen < 3)
+ {
+ wDec = LODWORD(dw64 - (ULONGLONG)wInt * 1024L) * 1000 / 1024;
+ // At this point, wDec should be between 0 and 1000
+ // we want get the top one (or two) digits.
+ wDec /= 10;
+ if (wLen == 2)
+ wDec /= 10;
+
+ // Note that we need to set the format before getting the
+ // intl char.
+#pragma data_seg(".text", "CODE")
+ lstrcpy(szFormat, L"%02d");
+#pragma data_seg()
+
+ szFormat[2] = L'0' + 3 - wLen;
+ GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL,
+ szTemp+wLen, sizeof(szTemp)/sizeof(szTemp[0])-wLen);
+ wLen = lstrlen(szTemp);
+ wLen += wsprintf(szTemp+wLen, szFormat, wDec);
+ }
+
+AddOrder:
+ ::LoadString(::hmodNW, pwOrders[i], szOrder, sizeof(szOrder)/sizeof(szOrder[0]));
+ wsprintf(szBuf, szOrder, (LPSTR)szTemp);
+
+ return szBuf;
+}
diff --git a/private/nw/svcdlls/nwwks/client/nwshrc.h b/private/nw/svcdlls/nwwks/client/nwshrc.h
new file mode 100644
index 000000000..cde22a11d
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwshrc.h
@@ -0,0 +1,296 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ nwshrc.h
+
+Abstract:
+
+ All resource ids used in nwprovau.dll
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 20-Oct-1995
+
+Revision History:
+
+--*/
+
+#ifndef _NWSHRC_H_
+#define _NWSHRC_H_
+
+#include "nwshhelp.h"
+
+#define IDC_STATIC -1
+#define IDC_LOGOFRAME 599
+
+//
+// Icon Ids
+//
+
+#define IDI_TREE_ICON 600
+#define IDI_SERVER_ICON 601
+#define IDI_FOLDER_ICON 602
+#define IDI_PRINTER_ICON 603
+#define IDI_NDSCONT_ICON 604
+
+// Bitmap Ids
+#define IDB_SERVER_ICON 701
+#define IDB_TREE_ICON 702
+
+//
+// Dialog Ids
+//
+
+#define DLG_NETWARE_LOGIN 1000
+#define DLG_NETWORK_CREDENTIAL 1001
+#define DLG_CHANGE_PASSWORD 1002
+#define DLG_CHANGE_PASSWORD2 1003
+#define DLG_PASSWORD_CHANGE 1004
+#define DLG_PASSWORD_PROMPT 1005
+#define DLG_PREFERRED_SERVER_HELP 1006
+#define DLG_ENTER_PASSWORD_HELP 1007
+#define DLG_ENTER_OLD_PASSWORD 1009
+#define DLG_PW_SELECT_SERVERS 1010
+#define DLG_PW_CHANGED 1011
+#define DLG_ENTER_OLD_PW_HELP 1012
+#define DLG_PW_SELECT_SERVERS_HELP 1013
+#define DLG_ENTER_ALT_USERNAME 1014
+#define DLG_ENTER_ALT_UN_HELP 1015
+#define DLG_CHANGE_PASSWORD3 1016
+#define DLG_CHANGE_PASSWORD_HELP 1017
+
+//
+// Dialog Ids for Shell Extension
+//
+#define DLG_NDS_SUMMARYINFO 1100
+#define DLG_SERVER_SUMMARYINFO 1101
+#define DLG_SHARE_SUMMARYINFO 1102
+#define DLG_PRINTER_SUMMARYINFO 1103
+#define DLG_GLOBAL_WHOAMI 1104
+#define DLG_NDSCONT_SUMMARYINFO 1105
+
+//
+// Help File and IDs
+//
+#define NW_HELP_FILE TEXT("nwdoc.hlp")
+
+//
+// Control Ids used by all dialogs
+//
+#ifdef NT1057
+#define IDHELP 100
+#endif
+
+//
+// Control Ids used in both the login dialog and the
+// change password dialog
+//
+#define ID_USERNAME 101
+#define ID_SERVER 102
+#define ID_LOCATION 103
+#define ID_PREFERREDSERVER_RB 104
+#define ID_DEFAULTCONTEXT_RB 105
+#define ID_DEFAULTTREE 106
+#define ID_DEFAULTCONTEXT 107
+
+//
+// Control Ids used in the login dialog
+//
+#define ID_PASSWORD 200
+
+//
+// Control Ids used in the change password dialog
+//
+#define ID_OLD_PASSWORD 300
+#define ID_NEW_PASSWORD 301
+#define ID_CONFIRM_PASSWORD 302
+
+#define ID_ADD 304
+#define ID_REMOVE 305
+#define ID_ACTIVE_LIST 306
+#define ID_INACTIVE_LIST 307
+#define ID_ACTIVE_LIST_TITLE 308
+#define ID_INACTIVE_LIST_TITLE 309
+
+//
+// Control Ids used in the network credential dialog
+//
+#define ID_VOLUME_PATH 400
+#define ID_CONNECT_AS 401
+#define ID_CONNECT_PASSWORD 402
+#define ID_CONNECT_TEXT 403
+
+//
+// Login script
+//
+#define ID_LOGONSCRIPT 501
+
+//
+// Controls common to summaryinfo dialogs
+//
+
+#define IDD_ERROR 200
+
+//
+// Controls Ids in DLG_NDS_SUMMARYINFO
+//
+#define IDD_NDS_NAME_TXT 101
+#define IDD_NDS_NAME 102
+#define IDD_NDS_CLASS_TXT 103
+#define IDD_NDS_CLASS 104
+#define IDD_NDS_COMMENT_TXT 105
+#define IDD_NDS_COMMENT 106
+
+//
+// Controls Ids in DLG_SERVER_SUMMARYINFO
+//
+#define IDD_SERVER_NAME 101
+#define IDD_SERVER_VERSION_TXT 102
+#define IDD_SERVER_VERSION 103
+#define IDD_SERVER_REVISION_TXT 104
+#define IDD_SERVER_REVISION 105
+#define IDD_SERVER_COMMENT_TXT 106
+#define IDD_SERVER_COMMENT 107
+#define IDD_SERVER_CONNECT_TXT 108
+#define IDD_SERVER_CONNECT 109
+#define IDD_SERVER_MAXCON_TXT 110
+#define IDD_SERVER_MAXCON 111
+
+//
+// Controls Ids in DLG_SHARE_SUMMARYINFO
+//
+#define IDD_SHARE_NAME 101
+#define IDD_SHARE_SERVER_TXT 102
+#define IDD_SHARE_SERVER 103
+#define IDD_SHARE_PATH_TXT 104
+#define IDD_SHARE_PATH 105
+#define IDD_SHARE_USED_SPC_CLR 106
+#define IDD_SHARE_USED_SPC_TXT 107
+#define IDD_SHARE_USED_SPC 108
+#define IDD_SHARE_USED_SPC_MB 109
+#define IDD_SHARE_FREE_SPC_CLR 110
+#define IDD_SHARE_FREE_SPC_TXT 111
+#define IDD_SHARE_FREE_SPC 112
+#define IDD_SHARE_FREE_SPC_MB 113
+#define IDD_SHARE_MAX_SPC_TXT 114
+#define IDD_SHARE_MAX_SPC 115
+#define IDD_SHARE_MAX_SPC_MB 116
+#define IDD_SHARE_PIE 117
+#define IDD_SHARE_LFN_TXT 118
+
+//
+// Controls Ids in DLG_PRINTER_SUMMARYINFO
+//
+#define IDD_PRINTER_NAME 101
+#define IDD_PRINTER_QUEUE_TXT 102
+#define IDD_PRINTER_QUEUE 103
+
+//
+// Controls Ids in DLG_GLOBAL_WHOAMI
+//
+#define IDD_GLOBAL_SERVERLIST_T 101
+#define IDD_GLOBAL_SERVERLIST 102
+#define IDD_GLOBAL_SVRLIST_DESC 103
+#define IDD_DETACH 104
+#define IDD_REFRESH 105
+
+//
+// String Ids
+//
+#define IDS_START 20000
+#define IDS_NONE (IDS_START + 0)
+#define IDS_NETWARE_PRINT_CAPTION (IDS_START + 1)
+#define IDS_NOTHING_TO_CONFIGURE (IDS_START + 2)
+#define IDS_NETWARE_TITLE (IDS_START + 3)
+#define IDS_AUTH_FAILURE_TITLE (IDS_START + 4)
+#define IDS_NO_PREFERRED (IDS_START + 5)
+#define IDS_LOGIN_FAILURE_WARNING (IDS_START + 6)
+#define IDS_AUTH_FAILURE_WARNING (IDS_START + 7)
+#define IDS_CHANGE_PASSWORD_INFO (IDS_START + 8)
+#define IDS_INVALID_SERVER (IDS_START + 9)
+#define IDS_PASSWORD_HAS_EXPIRED (IDS_START + 10)
+#define IDS_AUTH_ACC_RESTRICTION (IDS_START + 11)
+#define IDS_LOGIN_ACC_RESTRICTION (IDS_START + 12)
+#define IDS_PASSWORD_HAS_EXPIRED1 (IDS_START + 13)
+#define IDS_BAD_PASSWORDS (IDS_START + 14)
+#define IDS_CHANGE_PASSWORD_TITLE (IDS_START + 15)
+#define IDS_START_WORKSTATION_FIRST (IDS_START + 16)
+#define IDS_PASSWORD_HAS_EXPIRED0 (IDS_START + 17)
+#define IDS_PASSWORD_HAS_EXPIRED2 (IDS_START + 18)
+#define IDS_LOGIN_DISABLED (IDS_START + 19)
+#define IDS_START_WORKSTATION_FIRST_NTAS (IDS_START + 20)
+#define IDS_SERVER (IDS_START + 21)
+#define IDS_CONTEXT (IDS_START + 22)
+#define IDS_CONNECT_NO_ERROR_TEXT (IDS_START + 23)
+#define IDS_TITLE_LOGOUT (IDS_START + 24)
+#define IDS_MESSAGE_LOGOUT_QUESTION (IDS_START + 25)
+#define IDS_MESSAGE_LOGOUT_FAILED (IDS_START + 26)
+#define IDS_MESSAGE_NOT_ATTACHED (IDS_START + 27)
+#define IDS_MESSAGE_DETACHED (IDS_START + 28)
+#define IDS_MESSAGE_LOGOUT_CONFIRM (IDS_START + 29)
+#define IDS_TITLE_WHOAMI (IDS_START + 30)
+#define IDS_MESSAGE_ATTACHED (IDS_START + 31)
+#define IDS_BYTES (IDS_START + 32)
+#define IDS_ORDERKB (IDS_START + 33)
+#define IDS_ORDERMB (IDS_START + 34)
+#define IDS_ORDERGB (IDS_START + 35)
+#define IDS_ORDERTB (IDS_START + 36)
+#define IDS_STATE_NOT_LOGGED_IN (IDS_START + 37)
+#define IDS_MESSAGE_NOT_ATTACHED_TO_TREE (IDS_START + 38)
+#define IDS_MESSAGE_ATTACHED_TO_TREE (IDS_START + 39)
+#define IDS_LOGIN_TYPE_NDS (IDS_START + 40)
+#define IDS_LOGIN_TYPE_BINDERY (IDS_START + 41)
+#define IDS_LOGIN_STATUS_SEPARATOR (IDS_START + 42)
+#define IDS_LOGIN_STATUS_AUTHENTICATED (IDS_START + 43)
+#define IDS_LOGIN_STATUS_NOT_AUTHENTICATED (IDS_START + 44)
+#define IDS_LOGIN_STATUS_LICENSED (IDS_START + 45)
+#define IDS_LOGIN_STATUS_NOT_LICENSED (IDS_START + 46)
+#define IDS_LOGIN_STATUS_LOGGED_IN (IDS_START + 47)
+#define IDS_LOGIN_STATUS_ATTACHED_ONLY (IDS_START + 48)
+#define IDS_LOGIN_STATUS_NOT_ATTACHED (IDS_START + 49)
+#define IDS_MESSAGE_CONNINFO_ERROR (IDS_START + 50)
+#define IDS_MESSAGE_ADDCONN_ERROR (IDS_START + 51)
+#define IDS_MESSAGE_CONTEXT_ERROR (IDS_START + 52)
+#define IDS_MESSAGE_LOGGED_IN_TREE (IDS_START + 53)
+#define IDS_MESSAGE_NOT_LOGGED_IN_TREE (IDS_START + 54)
+#define IDS_MESSAGE_LOGGED_IN_SERVER (IDS_START + 55)
+#define IDS_MESSAGE_NOT_LOGGED_IN_SERVER (IDS_START + 56)
+#define IDS_MESSAGE_PROPERTIES_ERROR (IDS_START + 57)
+#define IDS_TREE_NAME_MISSING (IDS_START + 58)
+#define IDS_CONTEXT_MISSING (IDS_START + 59)
+#define IDS_SERVER_MISSING (IDS_START + 60)
+#define IDS_CONTEXT_AUTH_FAILURE_WARNING (IDS_START + 61)
+#define IDS_COLUMN_NAME (IDS_START + 62)
+#define IDS_COLUMN_CONN_TYPE (IDS_START + 63)
+#define IDS_COLUMN_CONN_NUMBER (IDS_START + 64)
+#define IDS_COLUMN_USER (IDS_START + 65)
+#define IDS_COLUMN_STATUS (IDS_START + 66)
+#define IDS_MESSAGE_GETINFO_ERROR (IDS_START + 67)
+#define IDS_CP_FAILURE_WARNING (IDS_START + 68)
+#define IDS_CHANGE_PASSWORD_CONFLICT (IDS_START + 69)
+#define IDS_NO_TREES_DETECTED (IDS_START + 70)
+#define IDS_MESSAGE_LOGOUT_FROM_SERVER_FAILED (IDS_START + 71)
+
+//
+// String Ids for Shell Extension
+//
+#define IDS_MESSAGE_CONTEXT_CHANGED (IDS_START + 100)
+
+#define IDS_VERBS_BASE (IDS_START + 150)
+#define IDS_VERBS_HELP_BASE (IDS_START + 200)
+
+#define IDO_VERB_WHOAMI 1
+#define IDO_VERB_LOGOUT 2
+#define IDO_VERB_ATTACHAS 3
+#define IDO_VERB_GLOBALWHOAMI 4
+#define IDO_VERB_SETDEFAULTCONTEXT 5
+#define IDO_VERB_MAPNETWORKDRIVE 6
+#define IDO_VERB_TREEWHOAMI 7
+
+
+#define IDS_END (IDS_START + 1000)
+
+#endif // _NWSHRC_H_
diff --git a/private/nw/svcdlls/nwwks/client/nwshui.cxx b/private/nw/svcdlls/nwwks/client/nwshui.cxx
new file mode 100644
index 000000000..48dea8f96
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwshui.cxx
@@ -0,0 +1,1630 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ nwshui.cxx
+
+Abstract:
+
+ This module implements the context menu actions of shell extension classes.
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 25-Oct-1995
+
+--*/
+
+extern "C"
+{
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <commctrl.h>
+#include <shellapi.h>
+#include <shlobj.h>
+#define DONT_WANT_SHELLDEBUG
+#include <shsemip.h>
+#include <winnetwk.h>
+#include <npapi.h>
+#include <ntddnwfs.h>
+#include <ndsapi32.h>
+#include <nwapi.h>
+#include <nwwks.h>
+#include <nwmisc.h>
+#include "nwutil.h"
+}
+
+#include "nwshcmn.h"
+#include "nwshrc.h"
+
+extern "C"
+{
+NTSTATUS
+NwNdsOpenRdrHandle(
+ OUT PHANDLE phandleRdr );
+}
+
+#define MAX_ONE_CONN_INFORMATION_SIZE 512
+#define NW_ENUM_EXTRA_BYTES 256
+#define GLOBAL_WHOAMI_REFRESH_INTERVAL 30000 // in milliseconds, Win95 uses 10000
+
+DWORD
+LogoutFromServer(
+ LPWSTR pszServer,
+ PBOOL pfDisconnected
+);
+
+BOOL
+CALLBACK
+GlobalWhoAmIDlgProc(
+ HWND hwndDlg,
+ UINT msg,
+ WPARAM wParam,
+ LPARAM lParam );
+
+VOID
+GetConnectionStatusString(
+ PCONN_STATUS pConnStatus,
+ LPBYTE Buffer,
+ DWORD nSize
+);
+
+HRESULT
+NWUIWhoAmI(
+ HWND hParent,
+ LPNETRESOURCE pNetRes
+)
+{
+ DWORD err = NO_ERROR;
+ DWORD ResumeKey = 0;
+ LPBYTE pBuffer = NULL;
+ DWORD EntriesRead = 0;
+ DWORD dwMessageId;
+ WCHAR szUserName[MAX_PATH+1] = L"";
+ WCHAR szConnType[128];
+ WCHAR szRemoteName[MAX_PATH + 1];
+
+ szConnType[0] = 0;
+
+ if ( pNetRes->dwDisplayType == RESOURCEDISPLAYTYPE_SERVER )
+ {
+ // Need to extract the server name from full UNC path
+ NwExtractServerName( pNetRes->lpRemoteName, szRemoteName );
+ dwMessageId = IDS_MESSAGE_NOT_ATTACHED;
+ }
+ else // NDS container name
+ {
+ // Need to extract the tree name from the full UNC path
+ szRemoteName[0] = TREECHAR;
+ NwExtractTreeName( pNetRes->lpRemoteName, szRemoteName+1);
+ dwMessageId = IDS_MESSAGE_NOT_ATTACHED_TO_TREE;
+ }
+
+ err = NwGetConnectionStatus( szRemoteName,
+ &ResumeKey,
+ &pBuffer,
+ &EntriesRead );
+
+ if ( err == NO_ERROR && EntriesRead > 0 )
+ // For trees, we'll get more than one entry
+ {
+ PCONN_STATUS pConnStatus = (PCONN_STATUS) pBuffer;
+ LPWSTR pszStart = szConnType;
+ DWORD nSize = sizeof(szConnType)/sizeof(WCHAR);
+
+ if ( EntriesRead > 1 && szRemoteName[0] == TREECHAR )
+ {
+ // If there is more than one entry for trees,
+ // then we need to find one entry where username is not null
+ // and the login type is NDS.
+ // If we cannot find one, then just use the first one.
+
+ DWORD i;
+ PCONN_STATUS pConnStatusTmp = pConnStatus;
+ PCONN_STATUS pConnStatusUser = NULL;
+ PCONN_STATUS pConnStatusNoUser = NULL;
+
+ for ( i = 0; i < EntriesRead ; i++ )
+ {
+ if ( pConnStatusTmp->fNds )
+ {
+ pConnStatusNoUser = pConnStatusTmp;
+
+ if ( ( pConnStatusTmp->pszUserName != NULL )
+ && ( ( pConnStatusTmp->dwConnType == NW_CONN_NDS_AUTHENTICATED_NO_LICENSE )
+ || ( pConnStatusTmp->dwConnType == NW_CONN_NDS_AUTHENTICATED_LICENSED )
+ )
+ )
+ {
+ // Found it
+ pConnStatusUser = pConnStatusTmp;
+ break;
+ }
+ }
+
+ // Continue with the next item
+ pConnStatusTmp = (PCONN_STATUS) ( (DWORD) pConnStatusTmp
+ + pConnStatusTmp->dwTotalLength);
+ }
+
+ if ( pConnStatusUser ) // found one nds entry with a user name
+ pConnStatus = pConnStatusUser;
+ else if ( pConnStatusNoUser ) // use an nds entry with no user name
+ pConnStatus = pConnStatusNoUser;
+ // else use the first entry
+
+ }
+
+ if ( szRemoteName[0] == TREECHAR // A tree
+ || !pConnStatus->fPreferred // A server but not preferred
+ )
+ {
+ // Show this conneciton only if this is a tree or if this is
+ // not a implicit connection to the preferred server.
+
+ dwMessageId = pNetRes->dwDisplayType == RESOURCEDISPLAYTYPE_SERVER ?
+ IDS_MESSAGE_ATTACHED : IDS_MESSAGE_ATTACHED_TO_TREE;
+
+ if ( pConnStatus->pszUserName )
+ {
+ wcscpy( szUserName, pConnStatus->pszUserName );
+ }
+
+ if ( pConnStatus->fNds ) // NDS
+ {
+ LoadString( ::hmodNW, IDS_LOGIN_TYPE_NDS, pszStart, nSize );
+ nSize -= wcslen( pszStart );
+ pszStart += wcslen( pszStart);
+
+ }
+ else // Bindery
+ {
+ LoadString( ::hmodNW, IDS_LOGIN_TYPE_BINDERY, pszStart, nSize );
+ nSize -= wcslen( pszStart );
+ pszStart += wcslen( pszStart);
+ }
+
+ LoadString( ::hmodNW, IDS_LOGIN_STATUS_SEPARATOR, pszStart, nSize );
+ nSize -= wcslen( pszStart );
+ pszStart += wcslen( pszStart);
+
+ GetConnectionStatusString( pConnStatus, (LPBYTE) pszStart, nSize );
+ }
+ }
+
+ if ( err == NO_ERROR )
+ {
+ // Popup the message now.
+ ::MsgBoxPrintf( hParent,
+ dwMessageId,
+ IDS_TITLE_WHOAMI,
+ MB_OK | MB_SETFOREGROUND | MB_ICONINFORMATION,
+ szRemoteName[0] == TREECHAR? szRemoteName + 1 : szRemoteName,
+ szUserName,
+ szConnType );
+ }
+ else // error occurred
+ {
+ ::MsgBoxErrorPrintf( hParent,
+ IDS_MESSAGE_CONNINFO_ERROR,
+ IDS_TITLE_WHOAMI,
+ MB_OK | MB_SETFOREGROUND | MB_ICONSTOP,
+ err,
+ NULL );
+ }
+
+ if ( pBuffer != NULL )
+ {
+ LocalFree( pBuffer );
+ pBuffer = NULL;
+ }
+
+ return NOERROR;
+}
+
+VOID
+GetConnectionStatusString(
+ PCONN_STATUS pConnStatus,
+ LPBYTE Buffer,
+ DWORD nSize
+)
+{
+ LPWSTR pszStart = (LPWSTR) Buffer;
+ DWORD dwMessageId = 0;
+
+ if ( pConnStatus->fNds ) // NDS
+ {
+ if ( pConnStatus->dwConnType == NW_CONN_NOT_AUTHENTICATED )
+ {
+ dwMessageId = IDS_LOGIN_STATUS_NOT_AUTHENTICATED;
+ }
+ else if ( pConnStatus->dwConnType == NW_CONN_DISCONNECTED )
+ {
+ dwMessageId = IDS_LOGIN_STATUS_NOT_ATTACHED;
+ }
+ else // authenticated, licensed or unlicensed
+ {
+ LoadString( ::hmodNW, IDS_LOGIN_STATUS_AUTHENTICATED,
+ pszStart, nSize );
+ nSize -= wcslen( pszStart );
+ pszStart += wcslen( pszStart);
+
+ if ( pConnStatus->dwConnType == NW_CONN_NDS_AUTHENTICATED_LICENSED )
+ dwMessageId = IDS_LOGIN_STATUS_LICENSED;
+ else // NW_CONN_NDS_AUTHENTICATED_NO_LICENSE
+ dwMessageId = IDS_LOGIN_STATUS_NOT_LICENSED;
+ }
+ }
+ else // Bindery
+ {
+ if ( pConnStatus->dwConnType == NW_CONN_BINDERY_LOGIN )
+ dwMessageId = IDS_LOGIN_STATUS_LOGGED_IN;
+ else if ( pConnStatus->dwConnType == NW_CONN_DISCONNECTED )
+ dwMessageId = IDS_LOGIN_STATUS_NOT_ATTACHED;
+ else
+ dwMessageId = IDS_LOGIN_STATUS_ATTACHED_ONLY;
+ }
+
+ LoadString( ::hmodNW, dwMessageId, pszStart, nSize );
+}
+
+HRESULT
+NWUIGlobalWhoAmI(
+ HWND hParent
+)
+{
+ ::DialogBoxParam( ::hmodNW,
+ MAKEINTRESOURCE(DLG_GLOBAL_WHOAMI),
+ hParent,
+ (DLGPROC) ::GlobalWhoAmIDlgProc,
+ NULL );
+
+ return NOERROR;
+}
+
+HRESULT
+NWUILogOut(
+ HWND hParent,
+ LPNETRESOURCE pNetRes,
+ PBOOL pfDisconnected
+)
+{
+ DWORD err = NO_ERROR;
+ WCHAR szServer[MAX_PATH+1];
+ BOOL fAttached;
+ BOOL fAuthenticated;
+ DWORD dwMessageId;
+
+ *pfDisconnected = FALSE;
+
+ // Need to extract the server name from full UNC path
+ NwExtractServerName( pNetRes->lpRemoteName, szServer );
+
+ err = NwIsServerOrTreeAttached( szServer, &fAttached, &fAuthenticated );
+
+ if ( err == NO_ERROR && !fAttached )
+ {
+ dwMessageId = IDS_MESSAGE_NOT_ATTACHED;
+ }
+ else if ( err == NO_ERROR ) // attached
+ {
+ int nRet = ::MsgBoxPrintf( hParent,
+ IDS_MESSAGE_LOGOUT_CONFIRM,
+ IDS_TITLE_LOGOUT,
+ MB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION);
+
+ if ( nRet != IDYES )
+ return NOERROR;
+
+ err = LogoutFromServer( szServer, pfDisconnected );
+
+ if ( err == NO_ERROR )
+ dwMessageId = IDS_MESSAGE_DETACHED;
+ else
+ dwMessageId = IDS_MESSAGE_LOGOUT_FAILED;
+ }
+ else // error occurred
+ {
+ ::MsgBoxErrorPrintf( hParent,
+ IDS_MESSAGE_CONNINFO_ERROR,
+ IDS_TITLE_LOGOUT,
+ MB_OK | MB_SETFOREGROUND | MB_ICONSTOP,
+ err,
+ NULL );
+
+ return NOERROR;
+ }
+
+ ::MsgBoxPrintf( hParent,
+ dwMessageId,
+ IDS_TITLE_LOGOUT,
+ MB_OK | MB_SETFOREGROUND | MB_ICONINFORMATION );
+
+ return NOERROR;
+}
+
+DWORD
+LogoutFromServer(
+ LPWSTR pszServer,
+ PBOOL pfDisconnected
+)
+{
+ DWORD err = NO_ERROR;
+ HANDLE EnumHandle = (HANDLE) NULL;
+
+ LPNETRESOURCE NetR = NULL;
+ LPNETRESOURCEW SavePtr;
+
+ DWORD BytesNeeded = MAX_ONE_NETRES_SIZE;
+ DWORD EntriesRead = 0;
+ DWORD i;
+
+ *pfDisconnected = FALSE;
+
+ err = NPOpenEnum( RESOURCE_CONNECTED,
+ 0,
+ 0,
+ NULL,
+ &EnumHandle );
+
+ if ( err != NO_ERROR)
+ {
+ EnumHandle = (HANDLE) NULL;
+ goto CleanExit;
+ }
+
+ //
+ // Allocate buffer to get server list.
+ //
+ if ((NetR = (LPNETRESOURCE) LocalAlloc( 0, BytesNeeded )) == NULL)
+ {
+ err = ERROR_NOT_ENOUGH_MEMORY;
+ goto CleanExit;
+ }
+
+ do {
+
+ EntriesRead = 0xFFFFFFFF; // Read as many as possible
+
+ err = NwEnumConnections( EnumHandle,
+ &EntriesRead,
+ (LPVOID) NetR,
+ &BytesNeeded,
+ TRUE );
+
+
+ if ( err == WN_SUCCESS)
+ {
+ SavePtr = NetR;
+
+ for (i = 0; i < EntriesRead; i++, NetR++)
+ {
+ BYTE Buffer[MAX_ONE_CONN_INFORMATION_SIZE];
+ BOOL fImplicit;
+ LPWSTR pszCurrentServer;
+
+ fImplicit = FALSE;
+
+ if ( NwIsNdsSyntax( NetR->lpRemoteName))
+ {
+ // For Nds name, the server name might not be in the full UNC name.
+ // Hence we need to get the server name from the Rdr.
+
+ DWORD err1 = NwGetConnectionInformation(
+ NetR->lpLocalName? NetR->lpLocalName : NetR->lpRemoteName,
+ Buffer,
+ sizeof(Buffer));
+
+ if ( err1 != NO_ERROR )
+ continue; // continue with the next entry if error occurred
+
+ pszCurrentServer = ((PCONN_INFORMATION) Buffer)->HostServer;
+
+ // Need to NULL terminate the server name, this will probably used the space
+ // occupied by UserName but since we are not using it, it is probably ok.
+ LPWSTR pszTemp = (LPWSTR) ((DWORD) pszCurrentServer
+ + ((PCONN_INFORMATION) Buffer)->HostServerLength );
+ *pszTemp = 0;
+
+ }
+ else // in the form \\server\sys
+ {
+ LPWSTR pszTemp;
+ wcscpy( (LPWSTR) Buffer, NetR->lpRemoteName + 2 ); // go past two backslashes
+
+ if ( pszTemp = wcschr( (LPWSTR) Buffer, L'\\' ))
+ *pszTemp = 0;
+ else
+ {
+ // The remote name contains only \\server, hence if the local name
+ // is null, this is a implicit connection
+ if ( NetR->lpLocalName == NULL )
+ fImplicit = TRUE;
+ }
+
+ pszCurrentServer = (LPWSTR) Buffer;
+ }
+
+
+ if ( _wcsicmp( pszCurrentServer, pszServer ) == 0 )
+ {
+
+ do {
+
+ // for implicit connections, we need to try and disconnect until
+ // we deleted all the implicit connections, i.e. until we
+ // get the invalid handle error
+
+ // NOTE: If we don't pass in CONNECT_UPDATE_PROFILE, shell won't update
+ // the windows that got disconnected. What do we want to do here?
+ err = WNetCancelConnection2(
+ NetR->lpLocalName? NetR->lpLocalName : NetR->lpRemoteName,
+ 0, // CONNECT_UPDATE_PROFILE,
+ TRUE );
+
+ if ( err == NO_ERROR )
+ *pfDisconnected = TRUE;
+
+ } while ( fImplicit && ( err == NO_ERROR));
+
+ if ( err == ERROR_INVALID_HANDLE )
+ {
+ // implicit connection will sometimes return this if the explicit connection
+ // is already disconnected
+ err = NO_ERROR;
+ }
+
+ if ( err != NO_ERROR )
+ {
+ NetR = SavePtr;
+ goto CleanExit;
+ }
+ }
+ }
+
+ NetR = SavePtr;
+ }
+ else if ( err != WN_NO_MORE_ENTRIES)
+ {
+ if ( err == WN_MORE_DATA)
+ {
+
+ //
+ // Original buffer was too small. Free it and allocate
+ // the recommended size and then some to get as many
+ // entries as possible.
+ //
+
+ (void) LocalFree((HLOCAL) NetR);
+
+ BytesNeeded += NW_ENUM_EXTRA_BYTES;
+
+ if ((NetR = (LPNETRESOURCE) LocalAlloc( 0, BytesNeeded )) == NULL)
+ {
+ err = ERROR_NOT_ENOUGH_MEMORY;
+ goto CleanExit;
+ }
+ }
+ else
+ {
+ goto CleanExit;
+ }
+ }
+
+ } while (err != WN_NO_MORE_ENTRIES);
+
+ if ( err == WN_NO_MORE_ENTRIES)
+ err = NO_ERROR;
+
+CleanExit:
+
+ if (EnumHandle != (HANDLE) NULL)
+ (void) NPCloseEnum( EnumHandle);
+
+ if (NetR != NULL)
+ {
+ (void) LocalFree( (HLOCAL) NetR);
+ NetR = NULL;
+ }
+
+ return err;
+}
+
+HRESULT
+NWUIAttachAs(
+ HWND hParent,
+ LPNETRESOURCE pNetRes
+)
+{
+ DWORD err = NO_ERROR;
+ WCHAR szServerName[MAX_PATH+1];
+ BOOL fAttached;
+ BOOL fAuthenticated;
+
+ // First, see if we are attached to the server.
+ // Note, Attach as menu will be disabled on the NDS servers that we are already logged into.
+
+ // Need to extract the server name from full UNC path
+ szServerName[0] = szServerName[1] = L'\\';
+ NwExtractServerName( pNetRes->lpRemoteName, szServerName + 2 );
+
+ err = NwIsServerOrTreeAttached( szServerName + 2, &fAttached, &fAuthenticated );
+
+ if ( err == NO_ERROR && fAttached && fAuthenticated )
+ {
+ // Already attached and authenticated to the server.
+ // So, ask the user if he wants to log out first.
+
+ int nRet = ::MsgBoxPrintf( hParent,
+ IDS_MESSAGE_LOGOUT_QUESTION,
+ IDS_TITLE_LOGOUT,
+ MB_YESNO | MB_SETFOREGROUND | MB_ICONQUESTION );
+
+ if ( nRet != IDYES )
+ return NOERROR;
+
+ BOOL fDisconnected = FALSE; // can be used to refresh the shell if needed
+ err = LogoutFromServer( szServerName + 2, &fDisconnected );
+
+ if ( err != NO_ERROR )
+ {
+ ::MsgBoxPrintf( hParent,
+ IDS_MESSAGE_LOGOUT_FAILED,
+ IDS_TITLE_LOGOUT,
+ MB_OK | MB_SETFOREGROUND | MB_ICONSTOP );
+
+ return NOERROR;
+ }
+ }
+
+ // If error occurred, just assume we are not attached to the server and continue on
+ // to login to the server.
+
+ DWORD dwConnFlags = CONNECT_INTERACTIVE | CONNECT_PROMPT;
+
+ // See if the server is in the default context tree.
+ // If yes, then we don't need to prompt the password first before connecting
+ // in WNetAddConnection3.
+
+ BOOL fInDefaultTree = FALSE;
+ err = NwIsServerInDefaultTree( pNetRes->lpRemoteName, &fInDefaultTree );
+
+ if ( (err == NO_ERROR ) && fInDefaultTree )
+ dwConnFlags = CONNECT_INTERACTIVE;
+
+ //
+ // Now call WNetAddConnection3
+ //
+
+ // NOTE: net use \\mars_srv0 will succeed
+ // but net use \\marsdev\cn=mars_srv0... will failed
+ // Hence, just use the server name to connect.
+ LPWSTR pszSave = pNetRes->lpRemoteName;
+ pNetRes->lpRemoteName = szServerName;
+
+ err = WNetAddConnection3( hParent,
+ pNetRes,
+ NULL,
+ NULL,
+ dwConnFlags );
+
+ if ( err != WN_SUCCESS && err != WN_CANCEL )
+ {
+ ::MsgBoxErrorPrintf( hParent,
+ IDS_MESSAGE_ADDCONN_ERROR,
+ IDS_NETWARE_TITLE,
+ MB_OK | MB_SETFOREGROUND | MB_ICONSTOP,
+ err,
+ pNetRes->lpRemoteName );
+ }
+
+ pNetRes->lpRemoteName = pszSave; // restore the original remote name just in case
+
+ return NOERROR;
+}
+
+
+#if 0
+HRESULT
+NWUISetDefaultContext(
+ HWND hParent,
+ LPNETRESOURCE pNetRes
+)
+{
+ DWORD dwPrintOptions;
+ LPWSTR pszCurrentContext = NULL;
+ WCHAR szNewContext[MAX_PATH+1];
+
+ DWORD err = NO_ERROR;
+ HANDLE handleRdr = NULL;
+
+ UNICODE_STRING uTree;
+ UNICODE_STRING uContext;
+ LPWSTR pszContext = NULL;
+
+ // Open a handle to the redirector
+ err = RtlNtStatusToDosError( ::NwNdsOpenRdrHandle( &handleRdr ));
+
+ if ( err != NO_ERROR )
+ goto CleanExit;
+
+ // Get the print option so that we can use it later
+ err = ::NwQueryInfo( &dwPrintOptions, &pszCurrentContext );
+
+ if ( err != NO_ERROR )
+ goto CleanExit;
+
+ wcscpy( szNewContext, pNetRes->lpRemoteName + 1 ); // get past 1st '\\'
+ szNewContext[0] = TREECHAR; // in the format "*TREE\CONTEXT"
+
+ if ( (pszContext = wcschr( szNewContext, L'\\' )) != NULL )
+ {
+ *pszContext = 0;
+ RtlInitUnicodeString( &uContext, pszContext + 1 );
+ }
+ else
+ RtlInitUnicodeString( &uContext, L"");
+
+ RtlInitUnicodeString( &uTree, szNewContext+1 );
+
+ if ( (err = RtlNtStatusToDosError( ::NwNdsSetTreeContext( handleRdr, &uTree, &uContext)))
+ == NO_ERROR )
+ {
+ *pszContext = L'\\';
+
+ if ((err = ::NwSetInfoInRegistry( dwPrintOptions, szNewContext )) == NO_ERROR )
+ {
+ ::MsgBoxPrintf( hParent,
+ IDS_MESSAGE_CONTEXT_CHANGED,
+ IDS_NETWARE_TITLE,
+ MB_OK | MB_SETFOREGROUND | MB_ICONINFORMATION,
+ pszCurrentContext
+ ? ( *pszCurrentContext == TREECHAR
+ ? pszCurrentContext + 1
+ : pszCurrentContext )
+ : L"",
+ szNewContext+1 );
+ }
+ }
+
+CleanExit:
+
+ if ( err != NO_ERROR )
+ {
+ ::MsgBoxErrorPrintf( hParent,
+ IDS_MESSAGE_CONTEXT_ERROR,
+ IDS_NETWARE_TITLE,
+ MB_OK | MB_SETFOREGROUND | MB_ICONSTOP,
+ err,
+ szNewContext+1);
+ }
+
+ if ( pszCurrentContext != NULL )
+ {
+ LocalFree( pszCurrentContext );
+ pszCurrentContext = NULL;
+ }
+
+ if ( handleRdr != NULL )
+ {
+ ::NtClose( handleRdr );
+ handleRdr = NULL;
+ }
+
+ return NOERROR;
+}
+#endif
+
+HRESULT
+NWUIMapNetworkDrive(
+ HWND hParent,
+ LPNETRESOURCE pNetRes
+)
+{
+ HRESULT hres;
+ CONNECTDLGSTRUCT cds;
+
+ cds.cbStructure = sizeof(cds);
+ cds.hwndOwner = hParent;
+ cds.lpConnRes = pNetRes;
+ cds.dwFlags = CONNDLG_RO_PATH;
+
+ if ( (( hres = WNetConnectionDialog1( &cds )) == WN_SUCCESS )
+ && ( g_pFuncSHChangeNotify )
+ )
+ {
+ WCHAR szPath[4];
+
+ szPath[0] = ((USHORT) cds.dwDevNum) - 1 + L'A';
+ szPath[1] = L':';
+ szPath[2] = L'\\';
+ szPath[3] = 0;
+
+ // Notify shell about added redirection
+ (*g_pFuncSHChangeNotify)( SHCNE_DRIVEADD,
+ SHCNF_FLUSH | SHCNF_PATH,
+ szPath,
+ NULL );
+
+ // And we need to open shell window on this path
+ if (g_pFuncSHExecuteEx)
+ {
+ SHELLEXECUTEINFO ExecInfo;
+
+ ::memset(&ExecInfo,0,sizeof(ExecInfo));
+
+ ExecInfo.hwnd = hParent;
+ ExecInfo.lpVerb = 0;
+ ExecInfo.lpFile = szPath;
+ ExecInfo.lpParameters = NULL;
+ ExecInfo.lpDirectory = NULL;
+ ExecInfo.nShow = SW_NORMAL | SW_SHOW;
+ ExecInfo.fMask = SEE_MASK_CLASSNAME;
+ ExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
+ ExecInfo.lpClass = (LPWSTR) L"Folder";
+ ExecInfo.hkeyClass = NULL;
+
+ (*g_pFuncSHExecuteEx)(&ExecInfo);
+ }
+ }
+
+ return hres;
+}
+
+
+
+#define LB_PCT_NAME 25
+#define LB_PCT_TYPE 11
+#define LB_PCT_CONN 11
+#define LB_PCT_USER 25
+#define LB_PCT_STATUS 27
+
+#define BITMAP_WIDTH 16
+#define BITMAP_HEIGHT 16
+
+#define MAPCOLOR RGB(0, 255, 0)
+
+static UINT uiNDSIconIndex = 0;
+static UINT uiServerIconIndex = 0;
+
+/*
+ * FillConnectionsListView
+ * -----------------------
+ *
+ * Fill list box with information on active connections
+ */
+VOID
+FillConnectionsListView(
+ HWND hwndDlg
+)
+{
+ HWND hwndLV = ::GetDlgItem(hwndDlg,IDD_GLOBAL_SERVERLIST);
+ LV_ITEM lvI;
+ WCHAR szSubItemText[MAX_PATH+1];
+ UINT uiInsertedIndex;
+
+ DWORD ResumeKey = 0;
+ LPBYTE pConnBuffer = NULL;
+ DWORD EntriesRead = 0;
+ DWORD err = NO_ERROR;
+
+ // Prepare ListView structure
+ lvI.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM | LVIF_STATE;
+ lvI.state = 0;
+ lvI.stateMask = 0;
+
+ do {
+
+ if ( pConnBuffer != NULL )
+ {
+ LocalFree( pConnBuffer );
+ pConnBuffer = NULL;
+ }
+
+ err = NwGetConnectionStatus( NULL,
+ &ResumeKey,
+ &pConnBuffer,
+ &EntriesRead );
+
+
+ if ( ( err != NO_ERROR )
+ || ( EntriesRead == 0 )
+ )
+ {
+ goto CleanExit;
+ }
+
+ PCONN_STATUS pConnStatus = (PCONN_STATUS) pConnBuffer;
+
+ for ( DWORD i = 0; i < EntriesRead; i++)
+ {
+ // Allocate and initialize new item structure for use in the listbox
+
+ DWORD dwSize;
+
+ //
+ // Don't need to show preferred server with only implicit
+ // connections since we can't disconnect from it.
+ //
+ if ( pConnStatus->fPreferred )
+ {
+ // Continue with the next item
+ pConnStatus = (PCONN_STATUS) ( (DWORD) pConnStatus
+ + pConnStatus->dwTotalLength);
+ continue;
+ }
+
+ //
+ // Allocate and copy the connection information to be store with
+ // the listbox
+ //
+ PCONN_STATUS pConnStatusKeep =
+ (PCONN_STATUS) LocalAlloc( LMEM_ZEROINIT, pConnStatus->dwTotalLength );
+ if ( pConnStatusKeep == NULL )
+ {
+ err = ERROR_NOT_ENOUGH_MEMORY;
+ goto CleanExit;
+ }
+
+ memcpy( pConnStatusKeep, pConnStatus, pConnStatus->dwTotalLength );
+
+ dwSize = sizeof(CONN_STATUS);
+
+ if ( pConnStatus->pszServerName )
+ {
+ pConnStatusKeep->pszServerName =
+ (LPWSTR) ((DWORD)pConnStatusKeep + dwSize );
+
+ NwMakePrettyDisplayName( pConnStatusKeep->pszServerName );
+
+ dwSize += (wcslen(pConnStatus->pszServerName)+1)*sizeof(WCHAR);
+ }
+
+ if ( pConnStatus->pszUserName )
+ {
+ pConnStatusKeep->pszUserName =
+ (LPWSTR) ((DWORD)pConnStatusKeep + dwSize );
+
+ dwSize += (wcslen(pConnStatus->pszUserName)+1) * sizeof(WCHAR);
+
+ NwAbbreviateUserName( pConnStatus->pszUserName,
+ pConnStatusKeep->pszUserName );
+
+ NwMakePrettyDisplayName( pConnStatusKeep->pszUserName );
+ }
+
+ if ( pConnStatus->pszTreeName )
+ {
+ pConnStatusKeep->pszTreeName =
+ (LPWSTR) ((DWORD)pConnStatusKeep + dwSize );
+ }
+
+ //
+ // Construct the item to add to the listbox
+ //
+ lvI.iItem = i;
+ lvI.iSubItem = 0;
+ lvI.pszText = szSubItemText;
+ lvI.cchTextMax = sizeof(szSubItemText);
+
+ // Select proper icon
+ lvI.iImage = pConnStatusKeep->fNds? uiNDSIconIndex
+ : uiServerIconIndex;
+
+ lvI.lParam = (LPARAM) pConnStatusKeep;
+
+ wcscpy( szSubItemText, pConnStatusKeep->pszServerName );
+
+ // Insert the item
+ uiInsertedIndex = ListView_InsertItem( hwndLV, &lvI);
+
+ if ( uiInsertedIndex != -1 )
+ {
+ // Added item itself - now for specific columns
+
+ // First, add the column indicating bindery or nds connection
+ if ( pConnStatusKeep->fNds )
+ {
+ LoadString( ::hmodNW, IDS_LOGIN_TYPE_NDS, szSubItemText,
+ sizeof(szSubItemText)/sizeof(szSubItemText[0]));
+ }
+ else
+ {
+ LoadString( ::hmodNW, IDS_LOGIN_TYPE_BINDERY, szSubItemText,
+ sizeof(szSubItemText)/sizeof(szSubItemText[0]));
+ }
+
+ ListView_SetItemText( hwndLV,
+ uiInsertedIndex,
+ 1, // SubItem id
+ szSubItemText );
+
+ // Next, Add the column indicating the connection number
+
+ if ( ( pConnStatusKeep->pszServerName[0] != TREECHAR )
+ && ( pConnStatusKeep->dwConnType != NW_CONN_DISCONNECTED )
+ )
+ {
+ // Add connection number only if it is a connection
+ // to a server and not a tree.
+ // A connection number only makes sense when not disconnected
+ ::wsprintf(szSubItemText,L"%4d",pConnStatusKeep->nConnNum );
+ ListView_SetItemText( hwndLV,
+ uiInsertedIndex,
+ 2, // SubItem id
+ szSubItemText );
+ }
+
+ // Then, add the column indicating the user name
+
+ *szSubItemText = L'\0';
+ if ( pConnStatusKeep->pszUserName )
+ {
+ wcscpy( szSubItemText, pConnStatusKeep->pszUserName );
+
+ ListView_SetItemText( hwndLV,
+ uiInsertedIndex,
+ 3, // SubItem id
+ szSubItemText );
+ }
+
+ // Last of all, add the column indicating the connection status
+
+ *szSubItemText = L'\0';
+ GetConnectionStatusString( pConnStatusKeep,
+ (LPBYTE) szSubItemText,
+ sizeof(szSubItemText)/sizeof(szSubItemText[0]));
+
+ ListView_SetItemText( hwndLV,
+ uiInsertedIndex,
+ 4, // SubItem id
+ szSubItemText );
+
+ }
+ else
+ {
+ // Failed inserting item in the list,
+ // need to delete the allocated CONN_STATUS
+ ASSERT( FALSE );
+ LocalFree( pConnStatusKeep );
+ pConnStatusKeep = NULL;
+ }
+
+ // Continue with the next item
+ pConnStatus = (PCONN_STATUS) ( (DWORD) pConnStatus
+ + pConnStatus->dwTotalLength);
+ }
+
+ } while ( ResumeKey != 0 );
+
+CleanExit:
+
+ if ( pConnBuffer != NULL )
+ {
+ LocalFree( pConnBuffer );
+ pConnBuffer = NULL;
+ }
+
+ if ( err != NO_ERROR )
+ {
+ // If error occurred, we will end the dialog
+
+ ::MsgBoxErrorPrintf( hwndDlg,
+ IDS_MESSAGE_CONNINFO_ERROR,
+ IDS_NETWARE_TITLE,
+ MB_OK | MB_SETFOREGROUND | MB_ICONSTOP,
+ err,
+ NULL );
+
+ ::EndDialog( hwndDlg, FALSE);
+ }
+}
+
+
+/*
+ * CreateConnectionsListView
+ * -------------------------
+ *
+ * Initialize the column headers of the list box
+ */
+VOID
+CreateConnectionsListView(
+ HWND hwndDlg
+)
+{
+ HWND hwndLV;
+ HIMAGELIST hSmall;
+ UINT uiLBoxWidth;
+
+ InitCommonControls();
+
+ // Get the handle of the listbox
+ hwndLV = ::GetDlgItem(hwndDlg,IDD_GLOBAL_SERVERLIST);
+
+ RECT rc;
+ ::GetWindowRect( ::GetDlgItem( hwndDlg, IDD_GLOBAL_SERVERLIST ), &rc );
+ uiLBoxWidth = rc.right - rc.left;
+
+ // Load image list
+ hSmall = ::ImageList_Create(BITMAP_WIDTH,BITMAP_HEIGHT,ILC_MASK,4,0);
+
+ // Load bitmap of the tree/server icon
+ HBITMAP hbm;
+ hbm = ::LoadBitmap(::hmodNW,MAKEINTRESOURCE(IDB_TREE_ICON));
+ if ((uiNDSIconIndex = ImageList_AddMasked(hSmall,hbm,MAPCOLOR)) == -1)
+ ASSERT(FALSE);
+
+ hbm = ::LoadBitmap(::hmodNW,MAKEINTRESOURCE(IDB_SERVER_ICON));
+ if ((uiServerIconIndex = ImageList_AddMasked(hSmall,hbm,MAPCOLOR)) == -1)
+ ASSERT(FALSE);
+
+ ImageList_SetBkColor(hSmall, CLR_NONE);
+
+ // Associate image list with list view control
+ ListView_SetImageList(hwndLV,hSmall,LVSIL_SMALL);
+
+ // Initialize columns in header
+ LV_COLUMN lvC;
+ UINT uiColumnIndex = 0;
+ WCHAR szColumnName[128];
+
+ lvC.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
+ lvC.fmt = LVCFMT_LEFT;
+ lvC.cx = (uiLBoxWidth*LB_PCT_NAME)/100;
+ lvC.pszText = szColumnName;
+
+ // Add the column header representing the server name
+ *szColumnName = L'\0';
+ ::LoadString( ::hmodNW,
+ IDS_COLUMN_NAME,
+ szColumnName,
+ sizeof(szColumnName)/sizeof(szColumnName[0]));
+ if ( ListView_InsertColumn(hwndLV,uiColumnIndex++,&lvC) == -1)
+ ASSERT(FALSE);
+
+
+ // Add the column header representing the conneciton type
+ *szColumnName = L'\0';
+ lvC.cx = (uiLBoxWidth*LB_PCT_TYPE)/100;
+ ::LoadString( ::hmodNW,
+ IDS_COLUMN_CONN_TYPE,
+ szColumnName,
+ sizeof(szColumnName)/sizeof(szColumnName[0]));
+ if ( ListView_InsertColumn(hwndLV,uiColumnIndex++,&lvC) == -1)
+ ASSERT(FALSE);
+
+ // Add the column header representing the connection number
+ *szColumnName = L'\0';
+ lvC.cx = (uiLBoxWidth*LB_PCT_CONN)/100;
+ lvC.fmt = LVCFMT_RIGHT;
+
+ ::LoadString( ::hmodNW,
+ IDS_COLUMN_CONN_NUMBER,
+ szColumnName,
+ sizeof(szColumnName)/sizeof(szColumnName[0]));
+ if ( ListView_InsertColumn(hwndLV,uiColumnIndex++,&lvC) == -1)
+ ASSERT(FALSE);
+
+ // Add the column header representing the name of the user
+ *szColumnName = L'\0';
+ lvC.cx = (uiLBoxWidth*LB_PCT_USER)/100;
+ lvC.fmt = LVCFMT_LEFT;
+
+ ::LoadString( ::hmodNW,
+ IDS_COLUMN_USER,
+ szColumnName,
+ sizeof(szColumnName)/sizeof(szColumnName[0]));
+
+ if ( ListView_InsertColumn(hwndLV,uiColumnIndex++,&lvC) == -1)
+ ASSERT(FALSE);
+
+ // Add the column header representing the status of the connection
+ *szColumnName = L'\0';
+ lvC.cx = (uiLBoxWidth*LB_PCT_STATUS)/100;
+ lvC.fmt = LVCFMT_LEFT;
+
+ ::LoadString( ::hmodNW,
+ IDS_COLUMN_STATUS,
+ szColumnName,
+ sizeof(szColumnName)/sizeof(szColumnName[0]));
+
+ if ( ListView_InsertColumn(hwndLV,uiColumnIndex++,&lvC) == -1)
+ ASSERT(FALSE);
+
+ // Now fill list view window with connection information
+ FillConnectionsListView( hwndDlg );
+
+} /* endproc CreateConnectionsListView */
+
+
+/*
+ * GlobalWhoAmI_ListViewCompareProc
+ * --------------------------------
+ *
+ */
+LRESULT CALLBACK
+GlobalWhoAmI_ListViewCompareProc(
+ LPARAM lParam1,
+ LPARAM lParam2,
+ LPARAM lParamSort
+)
+{
+ PCONN_STATUS pConnItem1 = (PCONN_STATUS)lParam1;
+ PCONN_STATUS pConnItem2 = (PCONN_STATUS)lParam2;
+
+ int iResult = 0;
+
+ if ( pConnItem1 && pConnItem2 )
+ {
+ switch(lParamSort)
+ {
+ case 0:
+ iResult = ::_wcsicmp( pConnItem1->pszServerName,
+ pConnItem2->pszServerName);
+ break;
+
+ case 1:
+ iResult = pConnItem1->fNds - pConnItem2->fNds;
+ break;
+
+ case 2:
+ iResult = pConnItem1->nConnNum - pConnItem2->nConnNum;
+ break;
+
+ case 3:
+ {
+ // pszUserName might be NULL, so we need to be careful when
+ // comparing them.
+ if ( pConnItem1->pszUserName )
+ {
+ if ( pConnItem2->pszUserName )
+ {
+ iResult = ::_wcsicmp( pConnItem1->pszUserName,
+ pConnItem2->pszUserName);
+ }
+ else
+ {
+ iResult = 1;
+ }
+ }
+ else
+ {
+ iResult = -1;
+ }
+ break;
+ }
+
+ case 4:
+ iResult = pConnItem1->dwConnType - pConnItem2->dwConnType;
+ break;
+
+ default:
+ iResult = 0;
+ break;
+ }
+ }
+
+ return (iResult);
+
+}
+
+LRESULT
+NotifyHandler(
+ HWND hwndDlg,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam
+)
+{
+ NM_LISTVIEW *lpNm = (NM_LISTVIEW *)lParam;
+ NMHDR *lphdr = &lpNm->hdr;
+
+ HWND hwndLV = ::GetDlgItem(hwndDlg,IDD_GLOBAL_SERVERLIST);
+
+ switch(lphdr->code)
+ {
+ case LVN_ITEMCHANGED:
+ // Check for change in item state, make sure item has received focus
+ if (lpNm->uChanged & LVIF_STATE)
+ {
+ UINT uiSelectedItems = 0;
+
+ uiSelectedItems = ListView_GetSelectedCount(hwndLV);
+
+ EnableWindow( GetDlgItem(hwndDlg, IDD_DETACH),
+ uiSelectedItems? TRUE : FALSE);
+
+ return TRUE;
+ }
+ break;
+
+ case LVN_COLUMNCLICK:
+ ListView_SortItems( hwndLV,
+ GlobalWhoAmI_ListViewCompareProc,
+ (LPARAM)(lpNm->iSubItem));
+ return TRUE; /* we processed a message */
+
+ case LVN_DELETEITEM:
+ // Free memory
+ LocalFree( (HLOCAL) lpNm->lParam );
+ lpNm->lParam = NULL;
+ break;
+
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+
+BOOL
+DetachResourceProc(
+ HWND hwndDlg
+)
+{
+ BOOL fDetached = FALSE;
+ LV_ITEM lvitem;
+ int index;
+ DWORD err;
+
+ HWND hwndLV = ::GetDlgItem( hwndDlg, IDD_GLOBAL_SERVERLIST);
+
+ index = -1; // Start at beginning of item list.
+
+ while ((index = ListView_GetNextItem(hwndLV, index, LVNI_SELECTED)) != -1)
+ {
+ lvitem.iItem = index;
+ lvitem.mask = LVIF_PARAM;
+ lvitem.iSubItem = 0;
+
+ if ( ListView_GetItem( hwndLV, &lvitem ))
+ {
+ PCONN_STATUS pConnStatus = (PCONN_STATUS) lvitem.lParam;
+ BOOL fDisconnected = FALSE; // Can be used to refresh
+ // the shell if needed
+
+ err = LogoutFromServer( pConnStatus->pszServerName,
+ &fDisconnected );
+
+ if ( err == NO_ERROR )
+ {
+ fDetached = TRUE;
+ }
+ else
+ {
+ NwMakePrettyDisplayName(pConnStatus->pszServerName);
+ ::MsgBoxPrintf( hwndDlg,
+ IDS_MESSAGE_LOGOUT_FROM_SERVER_FAILED,
+ IDS_TITLE_LOGOUT,
+ MB_OK | MB_SETFOREGROUND | MB_ICONINFORMATION,
+ pConnStatus->pszServerName );
+ }
+ }
+ }
+
+ return fDetached;
+}
+
+static DWORD aWhoAmIIds[] = { IDC_LOGOFRAME, NO_HELP,
+ IDD_GLOBAL_SERVERLIST_T,IDH_GLOBAL_SERVERLIST,
+ IDD_GLOBAL_SERVERLIST, IDH_GLOBAL_SERVERLIST,
+ IDD_DETACH, IDH_GLOBAL_DETACH,
+ IDD_GLOBAL_SVRLIST_DESC,IDH_GLOBAL_SERVERLIST,
+ 0, 0 };
+
+/*
+ * GlobalWhoAmIDlgProc
+ * -------------------
+ *
+ * WhoAmI information for list of attached servers
+ */
+BOOL
+CALLBACK
+GlobalWhoAmIDlgProc(
+ HWND hwndDlg,
+ UINT msg,
+ WPARAM wParam,
+ LPARAM lParam
+)
+{
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ LPWSTR pszCurrentContext = NULL;
+ DWORD dwPrintOptions;
+
+ // Get the current default tree or server name
+ DWORD err = ::NwQueryInfo( &dwPrintOptions, &pszCurrentContext );
+ if ( err == NO_ERROR )
+ {
+ LPWSTR pszName;
+ WCHAR szUserName[MAX_PATH+1] = L"";
+ WCHAR szNoName[2] = L"";
+ DWORD ResumeKey = 0;
+ LPBYTE pBuffer = NULL;
+ DWORD EntriesRead = 0;
+
+ DWORD dwMessageId;
+
+ UNICODE_STRING uContext;
+ WCHAR szContext[MAX_PATH+1];
+
+ szContext[0] = 0;
+ uContext.Buffer = szContext;
+ uContext.Length = uContext.MaximumLength
+ = sizeof(szContext)/sizeof(szContext[0]);
+
+ if ( pszCurrentContext )
+ {
+ pszName = pszCurrentContext;
+ }
+ else
+ {
+ pszName = szNoName;
+ }
+
+ if ( pszName[0] == TREECHAR )
+ {
+ // Get the tree name from the full name *TREE\CONTEXT
+
+ LPWSTR pszTemp;
+ if ( pszTemp = wcschr( pszName, L'\\' ))
+ *pszTemp = 0;
+
+ dwMessageId = IDS_MESSAGE_NOT_LOGGED_IN_TREE;
+ }
+ else
+ {
+ dwMessageId = IDS_MESSAGE_NOT_LOGGED_IN_SERVER;
+ }
+
+ if ( pszName[0] != 0 ) // there is preferred server/tree
+ {
+ err = NwGetConnectionStatus( pszName,
+ &ResumeKey,
+ &pBuffer,
+ &EntriesRead );
+ }
+
+ if ( err == NO_ERROR && EntriesRead > 0 )
+ // For trees, we'll get more than one entry
+ {
+ PCONN_STATUS pConnStatus = (PCONN_STATUS) pBuffer;
+
+ if ( EntriesRead > 1 && pszName[0] == TREECHAR )
+ {
+ // If there is more than one entry for trees,
+ // then we need to find one entry where username is not null.
+ // If we cannot find one, then just use the first one.
+
+ DWORD i;
+ PCONN_STATUS pConnStatusTmp = pConnStatus;
+ PCONN_STATUS pConnStatusUser = NULL;
+ PCONN_STATUS pConnStatusNoUser = NULL;
+
+ for ( i = 0; i < EntriesRead ; i++ )
+ {
+ if ( pConnStatusTmp->fNds )
+ {
+ pConnStatusNoUser = pConnStatusTmp;
+
+ if ( ( pConnStatusTmp->pszUserName != NULL )
+ && ( ( pConnStatusTmp->dwConnType
+ == NW_CONN_NDS_AUTHENTICATED_NO_LICENSE )
+ || ( pConnStatusTmp->dwConnType
+ == NW_CONN_NDS_AUTHENTICATED_LICENSED )
+ )
+ )
+ {
+ // Found it
+ pConnStatusUser = pConnStatusTmp;
+ break;
+ }
+ }
+
+ // Continue with the next item
+ pConnStatusTmp = (PCONN_STATUS)
+ ( (DWORD) pConnStatusTmp
+ + pConnStatusTmp->dwTotalLength);
+ }
+
+ if ( pConnStatusUser )
+ {
+ // found one nds entry with a user name
+ pConnStatus = pConnStatusUser;
+ }
+ else if ( pConnStatusNoUser )
+ {
+ // use an nds entry with no user name
+ pConnStatus = pConnStatusNoUser;
+ }
+ // else use the first entry
+ }
+
+ if ( ( pConnStatus->pszUserName )
+ && ( pConnStatus->pszUserName[0] != 0 )
+ )
+ {
+ NwAbbreviateUserName( pConnStatus->pszUserName,
+ szUserName);
+
+ NwMakePrettyDisplayName( szUserName );
+
+ if ( pszName[0] != TREECHAR )
+ {
+ dwMessageId = IDS_MESSAGE_LOGGED_IN_SERVER;
+ }
+ else
+ {
+ dwMessageId = IDS_MESSAGE_LOGGED_IN_TREE;
+ }
+ }
+
+ if ( pszName[0] == TREECHAR )
+ {
+ // For trees, we need to get the current context
+
+ // Open a handle to the redirector
+ HANDLE handleRdr = NULL;
+ err = RtlNtStatusToDosError(
+ ::NwNdsOpenRdrHandle( &handleRdr ));
+
+ if ( err == NO_ERROR )
+ {
+ UNICODE_STRING uTree;
+ RtlInitUnicodeString( &uTree, pszName+1 ); // get past '*'
+
+ // Get the current context in the default tree
+ err = RtlNtStatusToDosError(
+ ::NwNdsGetTreeContext( handleRdr,
+ &uTree,
+ &uContext));
+ }
+
+ if ( handleRdr != NULL )
+ ::NtClose( handleRdr );
+ }
+ }
+
+ if ( !err )
+ {
+ LPWSTR pszText = NULL;
+ err = ::LoadMsgPrintf( &pszText,
+ dwMessageId,
+ pszName[0] == TREECHAR?
+ pszName + 1 : pszName,
+ szUserName,
+ szContext );
+
+ if ( err == NO_ERROR )
+ {
+ ::SetDlgItemText( hwndDlg, IDD_GLOBAL_SERVERLIST_T,
+ pszText);
+ ::LocalFree( pszText );
+ }
+ }
+
+ if ( pBuffer != NULL )
+ {
+ LocalFree( pBuffer );
+ pBuffer = NULL;
+ }
+ }
+
+ if ( pszCurrentContext != NULL )
+ {
+ LocalFree( pszCurrentContext );
+ pszCurrentContext = NULL;
+ }
+
+ if ( err != NO_ERROR )
+ {
+ ::MsgBoxErrorPrintf( hwndDlg,
+ IDS_MESSAGE_CONNINFO_ERROR,
+ IDS_NETWARE_TITLE,
+ MB_OK | MB_SETFOREGROUND | MB_ICONSTOP,
+ err,
+ NULL );
+ ::EndDialog( hwndDlg, FALSE);
+ return TRUE;
+ }
+
+ // Fill listview control with connection parameters
+ CreateConnectionsListView(hwndDlg);
+
+ UnHideControl( hwndDlg, IDD_DETACH);
+
+ // List view fill defaults to no selected server, disable Detach.
+ EnableWindow( GetDlgItem( hwndDlg, IDD_DETACH), FALSE);
+
+ // Set up timer for automatic refresh interval
+ ::SetTimer( hwndDlg, 1, GLOBAL_WHOAMI_REFRESH_INTERVAL, NULL);
+
+ // Set focus to list box
+ ::SetFocus( ::GetDlgItem( hwndDlg, IDD_GLOBAL_SERVERLIST));
+
+ return FALSE; /* we set the focus */
+ }
+
+ case WM_DESTROY:
+ ::KillTimer( hwndDlg, 1);
+ break;
+
+ case WM_COMMAND:
+
+ switch (wParam)
+ {
+ case IDOK:
+ case IDCANCEL:
+ ::EndDialog( hwndDlg, FALSE);
+ return TRUE; /* we processed a message */
+
+ case IDD_DETACH:
+ // Attempt to detach server connection currently selected
+ if ( DetachResourceProc( hwndDlg ))
+ {
+ // If succeeded - refresh window
+ ::SendMessage(hwndDlg,WM_COMMAND,IDD_REFRESH,0L);
+ }
+
+ return TRUE; /* we processed a message */
+
+ case IDD_REFRESH:
+ {
+ // Refresh connection listbox
+
+ HWND hwndLV = ::GetDlgItem( hwndDlg, IDD_GLOBAL_SERVERLIST);
+
+ ::SetFocus( hwndLV );
+
+ // Clear list
+ ListView_DeleteAllItems( hwndLV );
+
+ // Now refill list view window
+ FillConnectionsListView( hwndDlg );
+
+ // List view refill unsets selected server focus, disable Detach.
+ EnableWindow( GetDlgItem( hwndDlg, IDD_DETACH ), FALSE );
+
+ return TRUE; /* we processed a message */
+ }
+
+ default:
+ break;
+ }
+ break;
+
+ case WM_NOTIFY:
+ // Handle notifications
+ if ( NotifyHandler( hwndDlg, msg, wParam, lParam))
+ return TRUE; /* we processed a message */
+ break;
+
+ case WM_TIMER:
+ ::SendMessage( hwndDlg, WM_COMMAND, IDD_REFRESH, 0L);
+ break;
+
+ case WM_HELP:
+ ::WinHelp( (HWND) ((LPHELPINFO)lParam)->hItemHandle,
+ NW_HELP_FILE,
+ HELP_WM_HELP,
+ (DWORD) (LPVOID) aWhoAmIIds );
+ return TRUE;
+
+ case WM_CONTEXTMENU:
+ ::WinHelp( (HWND) wParam,
+ NW_HELP_FILE,
+ HELP_CONTEXTMENU,
+ (DWORD) (LPVOID) aWhoAmIIds );
+ return TRUE;
+
+ }
+
+ return FALSE; /* we didn't process the message */
+
+}
diff --git a/private/nw/svcdlls/nwwks/client/nwspl.c b/private/nw/svcdlls/nwwks/client/nwspl.c
new file mode 100644
index 000000000..e92ad0f5e
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwspl.c
@@ -0,0 +1,2924 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ nwspl.c
+
+Abstract:
+
+ This module contains the Netware print provider.
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 15-Apr-1993
+
+Revision History:
+ Yi-Hsin Sung (yihsins) 15-May-1993
+ Moved most of the functionality to the server side
+
+ Ram Viswanathan (ramv) 09-Aug-1995
+ Added functionality to Add and Delete Printer.
+
+
+--*/
+
+#include <stdio.h>
+
+#include <nwclient.h>
+#include <winspool.h>
+#include <winsplp.h>
+#include <ntlsa.h>
+
+#include <nwpkstr.h>
+#include <splutil.h>
+#include <nwreg.h>
+#include <nwspl.h>
+#include <nwmisc.h>
+
+//------------------------------------------------------------------
+//
+// Local Functions
+//
+//------------------------------------------------------------------
+
+DWORD
+InitializePortNames(
+ VOID
+);
+
+VOID
+NwpGetUserInfo(
+ LPWSTR *ppszUser,
+ BOOL *pfGateway
+);
+
+DWORD
+NwpGetThreadUserInfo(
+ LPWSTR *ppszUser,
+ LPWSTR *ppszUserSid
+);
+
+DWORD
+NwpGetUserNameFromSid(
+ PSID pUserSid,
+ LPWSTR *ppszUserName
+);
+
+DWORD
+NwpGetLogonUserInfo(
+ LPWSTR *ppszUserSid
+);
+
+VOID
+pFreeAllContexts();
+
+//------------------------------------------------------------------
+//
+// Global Variables
+//
+//------------------------------------------------------------------
+
+HMODULE hmodNW = NULL;
+BOOL fIsWinnt = FALSE ;
+WCHAR *pszRegistryPath = NULL;
+WCHAR *pszRegistryPortNames=L"PortNames";
+WCHAR szMachineName[MAX_COMPUTERNAME_LENGTH + 3];
+PNWPORT pNwFirstPort = NULL;
+CRITICAL_SECTION NwSplSem;
+CRITICAL_SECTION NwServiceListCriticalSection; // Used to protect linked
+ // list of registered services
+STATIC HANDLE handleDummy; // This is a dummy handle used to
+ // return to the clients if we have previously
+ // opened the given printer successfully
+ // and the netware workstation service is not
+ // currently available.
+
+STATIC
+PRINTPROVIDOR PrintProvidor = { OpenPrinter,
+ SetJob,
+ GetJob,
+ EnumJobs,
+ AddPrinter, // NOT SUPPORTED
+ DeletePrinter, // NOT SUPPORTED
+ SetPrinter,
+ GetPrinter,
+ EnumPrinters,
+ AddPrinterDriver, // NOT SUPPORTED
+ EnumPrinterDrivers, // NOT SUPPORTED
+ GetPrinterDriverW, // NOT SUPPORTED
+ GetPrinterDriverDirectory, // NOT SUPPORTED
+ DeletePrinterDriver, // NOT SUPPORTED
+ AddPrintProcessor, // NOT SUPPORTED
+ EnumPrintProcessors, // NOT SUPPORTED
+ GetPrintProcessorDirectory, // NOT SUPPORTED
+ DeletePrintProcessor, // NOT SUPPORTED
+ EnumPrintProcessorDatatypes,// NOT SUPPORTED
+ StartDocPrinter,
+ StartPagePrinter, // NOT SUPPORTED
+ WritePrinter,
+ EndPagePrinter, // NOT SUPPORTED
+ AbortPrinter,
+ ReadPrinter, // NOT SUPPORTED
+ EndDocPrinter,
+ AddJob,
+ ScheduleJob,
+ GetPrinterData, // NOT SUPPORTED
+ SetPrinterData, // NOT SUPPORTED
+ WaitForPrinterChange,
+ ClosePrinter,
+ AddForm, // NOT SUPPORTED
+ DeleteForm, // NOT SUPPORTED
+ GetForm, // NOT SUPPORTED
+ SetForm, // NOT SUPPORTED
+ EnumForms, // NOT SUPPORTED
+ EnumMonitors, // NOT SUPPORTED
+ EnumPorts,
+ AddPort, // NOT SUPPORTED
+ ConfigurePort,
+ DeletePort,
+ CreatePrinterIC, // NOT SUPPORTED
+ PlayGdiScriptOnPrinterIC, // NOT SUPPORTED
+ DeletePrinterIC, // NOT SUPPORTED
+ AddPrinterConnection, // NOT SUPPORTED
+ DeletePrinterConnection, // NOT SUPPORTED
+ PrinterMessageBox, // NOT SUPPORTED
+ AddMonitor, // NOT SUPPORTED
+ DeleteMonitor // NOT SUPPORTED
+};
+
+
+//------------------------------------------------------------------
+//
+// Initialization Functions
+//
+//------------------------------------------------------------------
+
+
+BOOL InitializeDll(
+ HINSTANCE hdll,
+ DWORD dwReason,
+ LPVOID lpReserved
+)
+{
+ NT_PRODUCT_TYPE ProductType ;
+
+ UNREFERENCED_PARAMETER( lpReserved );
+
+ if ( dwReason == DLL_PROCESS_ATTACH )
+ {
+ DisableThreadLibraryCalls( hdll );
+
+ hmodNW = hdll;
+
+ //
+ // are we a winnt machine?
+ //
+ fIsWinnt = RtlGetNtProductType(&ProductType) ?
+ (ProductType == NtProductWinNt) :
+ FALSE ;
+
+ //
+ // Initialize the critical section for maintaining the registered
+ // service list
+ //
+ InitializeCriticalSection( &NwServiceListCriticalSection );
+ }
+ else if ( dwReason == DLL_PROCESS_DETACH )
+ {
+ //
+ // Free up memories used by the port link list
+ //
+ DeleteAllPortEntries();
+
+ //
+ // Get rid of Service List and Shutdown SAP library
+ //
+ NwTerminateServiceProvider();
+
+#ifndef NT1057
+ //
+ // Clean up shell extensions
+ //
+ NwCleanupShellExtensions();
+#endif
+ pFreeAllContexts(); // clean up RNR stuff
+ DeleteCriticalSection( &NwServiceListCriticalSection );
+ }
+
+ return TRUE;
+}
+
+
+
+DWORD
+InitializePortNames(
+ VOID
+)
+/*++
+
+Routine Description:
+
+ This is called by the InitializePrintProvidor to initialize the ports
+ names used in this providor.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Returns NO_ERROR or the error that occurred.
+
+--*/
+{
+ DWORD err;
+ HKEY hkeyPath;
+ HKEY hkeyPortNames;
+
+ err = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
+ pszRegistryPath,
+ 0,
+ KEY_READ,
+ &hkeyPath );
+
+ if ( !err )
+ {
+ err = RegOpenKeyEx( hkeyPath,
+ pszRegistryPortNames,
+ 0,
+ KEY_READ,
+ &hkeyPortNames );
+
+ if ( !err )
+ {
+ DWORD i = 0;
+ WCHAR Buffer[MAX_PATH];
+ DWORD BufferSize;
+
+ while ( !err )
+ {
+ BufferSize = sizeof Buffer;
+
+ err = RegEnumValue( hkeyPortNames,
+ i,
+ Buffer,
+ &BufferSize,
+ NULL,
+ NULL,
+ NULL,
+ NULL );
+
+ if ( !err )
+ CreatePortEntry( Buffer );
+
+ i++;
+ }
+
+ /* We expect RegEnumKeyEx to return ERROR_NO_MORE_ITEMS
+ * when it gets to the end of the keys, so reset the status:
+ */
+ if( err == ERROR_NO_MORE_ITEMS )
+ err = NO_ERROR;
+
+ RegCloseKey( hkeyPortNames );
+ }
+#if DBG
+ else
+ {
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [RegOpenKeyEx] (%ws) failed: Error = %d\n",
+ pszRegistryPortNames, err ));
+ }
+#endif
+
+ RegCloseKey( hkeyPath );
+ }
+#if DBG
+ else
+ {
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [RegOpenKeyEx] (%ws) failed: Error = %d\n",
+ pszRegistryPath, err ));
+ }
+#endif
+
+ return err;
+}
+
+//------------------------------------------------------------------
+//
+// Print Provider Functions supported by NetWare provider
+//
+//------------------------------------------------------------------
+
+
+BOOL
+InitializePrintProvidor(
+ LPPRINTPROVIDOR pPrintProvidor,
+ DWORD cbPrintProvidor,
+ LPWSTR pszFullRegistryPath
+)
+/*++
+
+Routine Description:
+
+ This is called by the spooler subsystem to initialize the print
+ providor.
+
+Arguments:
+
+ pPrintProvidor - Pointer to the print providor structure to be
+ filled in by this function
+ cbPrintProvidor - Count of bytes of the print providor structure
+ pszFullRegistryPath - Full path to the registry key of this print providor
+
+Return Value:
+
+ Always TRUE.
+
+--*/
+{
+ DWORD dwLen;
+
+ if ( !pPrintProvidor || !pszFullRegistryPath || !*pszFullRegistryPath )
+ {
+ SetLastError( ERROR_INVALID_PARAMETER );
+ return FALSE;
+ }
+
+ memcpy( pPrintProvidor,
+ &PrintProvidor,
+ min( sizeof(PRINTPROVIDOR), cbPrintProvidor) );
+
+ //
+ // Store the registry path for this print providor
+ //
+ if ( !(pszRegistryPath = AllocNwSplStr(pszFullRegistryPath)) )
+ return FALSE;
+
+ //
+ // Store the local machine name
+ //
+ szMachineName[0] = szMachineName[1] = L'\\';
+ dwLen = MAX_COMPUTERNAME_LENGTH;
+ GetComputerName( szMachineName + 2, &dwLen );
+
+#if DBG
+ IF_DEBUG(PRINT)
+ {
+ KdPrint(("NWSPL [InitializePrintProvidor] "));
+ KdPrint(("RegistryPath = %ws, ComputerName = %ws\n",
+ pszRegistryPath, szMachineName ));
+ }
+#endif
+
+ InitializeCriticalSection( &NwSplSem );
+
+ //
+ // Ignore the error returned from InitializePortNames.
+ // The provider can still function if we cannot get all the port
+ // names.
+ //
+ InitializePortNames();
+
+ return TRUE;
+}
+
+
+
+BOOL
+OpenPrinterW(
+ LPWSTR pszPrinterName,
+ LPHANDLE phPrinter,
+ LPPRINTER_DEFAULTS pDefault
+)
+/*++
+
+Routine Description:
+
+ This routine retrieves a handle identifying the specified printer.
+
+Arguments:
+
+ pszPrinterName - Name of the printer
+ phPrinter - Receives the handle that identifies the given printer
+ pDefault - Points to a PRINTER_DEFAULTS structure. Can be NULL.
+
+Return Value:
+
+ TRUE if the function succeeds, FALSE otherwise. Use GetLastError() for
+ extended error information.
+
+--*/
+{
+ DWORD err;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(( "NWSPL [OpenPrinter] Name = %ws\n", pszPrinterName ));
+#endif
+
+ UNREFERENCED_PARAMETER( pDefault );
+
+ if ( !pszPrinterName )
+ {
+ SetLastError( ERROR_INVALID_NAME );
+ return FALSE;
+ }
+
+ RpcTryExcept
+ {
+ err = NwrOpenPrinter( NULL,
+ pszPrinterName,
+ PortKnown( pszPrinterName ),
+ (LPNWWKSTA_PRINTER_CONTEXT) phPrinter );
+
+ //
+ // Make sure there is a port of this name so that
+ // EnumPorts will return it.
+ //
+
+ if ( !err )
+ {
+
+ if ( !PortExists( pszPrinterName, &err ) && !err )
+ {
+ //
+ // We will ignore the errors since it is
+ // still OK if we can't add the port.
+ // BUG BUG :: Cannot delete once created,dont create
+ // We should not create port entry and registry entry
+
+ if ( CreatePortEntry( pszPrinterName ) )
+ CreateRegistryEntry( pszPrinterName );
+
+ }
+
+ }
+
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+
+ if ( code == RPC_S_SERVER_UNAVAILABLE )
+ {
+ if ( PortKnown( pszPrinterName ))
+ {
+ *phPrinter = &handleDummy;
+ err = NO_ERROR;
+ }
+ else
+ {
+ err = ERROR_INVALID_NAME;
+ }
+ }
+ else
+ {
+ err = NwpMapRpcError( code );
+ }
+ }
+ RpcEndExcept
+
+ if ( err )
+ {
+ SetLastError( err );
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [OpenPrinter] err = %d\n", err));
+#endif
+ }
+
+ return ( err == NO_ERROR );
+
+}
+
+
+
+BOOL
+ClosePrinter(
+ HANDLE hPrinter
+)
+/*++
+
+Routine Description:
+
+ This routine closes the given printer object.
+
+Arguments:
+
+ hPrinter - Handle of the printer object
+
+Return Value:
+
+ TRUE if the function succeeds, FALSE otherwise. Use GetLastError() for
+ extended error information.
+
+--*/
+{
+ DWORD err;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(( "NWSPL [ClosePrinter]\n"));
+#endif
+
+ //
+ // Just return success if the handle is a dummy one
+ //
+ if ( hPrinter == &handleDummy )
+ return TRUE;
+
+ RpcTryExcept
+ {
+ err = NwrClosePrinter( (LPNWWKSTA_PRINTER_CONTEXT) &hPrinter );
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+
+ if ( code == RPC_S_SERVER_UNAVAILABLE )
+ err = ERROR_INVALID_HANDLE;
+ else
+ err = NwpMapRpcError( code );
+ }
+ RpcEndExcept
+
+ if ( err )
+ SetLastError( err );
+
+ return err == NO_ERROR;
+
+}
+
+
+
+BOOL
+GetPrinter(
+ HANDLE hPrinter,
+ DWORD dwLevel,
+ LPBYTE pbPrinter,
+ DWORD cbBuf,
+ LPDWORD pcbNeeded
+)
+/*++
+
+Routine Description:
+
+ The routine retrieves information about the given printer.
+
+Arguments:
+
+ hPrinter - Handle of the printer
+ dwLevel - Specifies the level of the structure to which pbPrinter points.
+ pbPrinter - Points to a buffer that receives the PRINTER_INFO object.
+ cbBuf - Size, in bytes of the array pbPrinter points to.
+ pcbNeeded - Points to a value which specifies the number of bytes copied
+ if the function succeeds or the number of bytes required if
+ cbBuf was too small.
+
+Return Value:
+
+ TRUE if the function succeeds and FALSE otherwise. GetLastError() can be
+ used to retrieve extended error information.
+
+--*/
+{
+ DWORD err;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(( "NWSPL [GetPrinter] Level = %d\n", dwLevel ));
+#endif
+
+ if ( hPrinter == &handleDummy )
+ {
+ SetLastError( ERROR_NO_NETWORK );
+ return FALSE;
+ }
+ else if ( ( dwLevel != 1 ) && ( dwLevel != 2 ) && (dwLevel != 3 ))
+ {
+ SetLastError( ERROR_INVALID_LEVEL );
+ return FALSE;
+ }
+
+ RpcTryExcept
+ {
+ err = NwrGetPrinter( (NWWKSTA_PRINTER_CONTEXT) hPrinter,
+ dwLevel,
+ pbPrinter,
+ cbBuf,
+ pcbNeeded );
+
+ if ( !err )
+ {
+ if ( dwLevel == 1 )
+ MarshallUpStructure( pbPrinter, PrinterInfo1Offsets, pbPrinter);
+ else
+ MarshallUpStructure( pbPrinter, PrinterInfo2Offsets, pbPrinter);
+ }
+
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+
+ if ( code == RPC_S_SERVER_UNAVAILABLE )
+ err = ERROR_INVALID_HANDLE;
+ else
+ err = NwpMapRpcError( code );
+ }
+ RpcEndExcept
+
+ if ( err )
+ SetLastError( err );
+
+ return err == NO_ERROR;
+}
+
+
+
+BOOL
+SetPrinter(
+ HANDLE hPrinter,
+ DWORD dwLevel,
+ LPBYTE pbPrinter,
+ DWORD dwCommand
+)
+/*++
+
+Routine Description:
+
+ The routine sets the specified by pausing printing, resuming printing, or
+ clearing all print jobs.
+
+Arguments:
+
+ hPrinter - Handle of the printer
+ dwLevel - Specifies the level of the structure to which pbPrinter points.
+ pbPrinter - Points to a buffer that supplies the PRINTER_INFO object.
+ dwCommand - Specifies the new printer state.
+
+Return Value:
+
+ TRUE if the function succeeds and FALSE otherwise. GetLastError() can be
+ used to retrieve extended error information.
+
+--*/
+{
+ DWORD err = NO_ERROR;
+
+ UNREFERENCED_PARAMETER( pbPrinter );
+
+#if DBG
+ IF_DEBUG(PRINT)
+ {
+ KdPrint(( "NWSPL [SetPrinter] Level = %d Command = %d\n",
+ dwLevel, dwCommand ));
+ }
+#endif
+
+ if ( hPrinter == &handleDummy )
+ {
+ SetLastError( ERROR_NO_NETWORK );
+ return FALSE;
+ }
+
+ switch ( dwLevel )
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ break;
+
+ default:
+ SetLastError( ERROR_INVALID_LEVEL );
+ return FALSE;
+ }
+
+ RpcTryExcept
+ {
+ err = NwrSetPrinter( (NWWKSTA_PRINTER_CONTEXT) hPrinter,
+ dwCommand );
+
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+
+ if ( code == RPC_S_SERVER_UNAVAILABLE )
+ err = ERROR_INVALID_HANDLE;
+ else
+ err = NwpMapRpcError( code );
+ }
+ RpcEndExcept
+
+ if ( err )
+ SetLastError( err );
+
+ return err == NO_ERROR;
+}
+
+
+
+BOOL
+EnumPrintersW(
+ DWORD dwFlags,
+ LPWSTR pszName,
+ DWORD dwLevel,
+ LPBYTE pbPrinter,
+ DWORD cbBuf,
+ LPDWORD pcbNeeded,
+ LPDWORD pcReturned
+)
+/*++
+
+Routine Description:
+
+ This routine enumerates the available providers, servers, printers
+ depending on the given pszName.
+
+Arguments:
+
+ dwFlags - Printer type requested
+ pszName - The name of the container object
+ dwLevel - The structure level requested
+ pbPrinter - Points to the array to receive the PRINTER_INFO objects
+ cbBuf - Size, in bytes of pbPrinter
+ pcbNeeded - Count of bytes needed
+ pcReturned - Count of PRINTER_INFO objects
+
+Return Value:
+
+ TRUE if the function succeeds, FALSE otherwise.
+
+--*/
+{
+ DWORD err = NO_ERROR;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ {
+ KdPrint(("NWSPL [EnumPrinters] Flags = %d Level = %d",dwFlags,dwLevel));
+ if ( pszName )
+ KdPrint((" PrinterName = %ws\n", pszName ));
+ else
+ KdPrint(("\n"));
+ }
+#endif
+
+ if ( (dwLevel != 1) && (dwLevel != 2) )
+ {
+ SetLastError( ERROR_INVALID_NAME ); // should be level, but winspool
+ // is silly.
+ return FALSE;
+ }
+ else if ( !pcbNeeded || !pcReturned )
+ {
+ SetLastError( ERROR_INVALID_PARAMETER );
+ return FALSE;
+ }
+
+ RpcTryExcept
+ {
+ *pcReturned = 0;
+ *pcbNeeded = 0;
+
+ if ( ( dwFlags & PRINTER_ENUM_NAME )
+ && ( dwLevel == 1 )
+ )
+ {
+ err = NwrEnumPrinters( NULL,
+ pszName,
+ pbPrinter,
+ cbBuf,
+ pcbNeeded,
+ pcReturned );
+
+ if ( !err )
+ {
+ DWORD i;
+ for ( i = 0; i < *pcReturned; i++ )
+ MarshallUpStructure( pbPrinter + i*sizeof(PRINTER_INFO_1W),
+ PrinterInfo1Offsets,
+ pbPrinter );
+ }
+ }
+ else
+ {
+ err = ERROR_INVALID_NAME;
+ }
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+
+ if ( code == RPC_S_SERVER_UNAVAILABLE )
+ err = ERROR_INVALID_NAME;
+ else
+ err = NwpMapRpcError( code );
+ }
+ RpcEndExcept
+
+ if ( err )
+ SetLastError( err );
+
+ return err == NO_ERROR;
+}
+
+
+
+DWORD
+StartDocPrinter(
+ HANDLE hPrinter,
+ DWORD dwLevel,
+ LPBYTE lpbDocInfo
+)
+/*++
+
+Routine Description:
+
+ This routine informs the print spooler that a document is to be spooled
+ for printing.
+
+Arguments:
+
+ hPrinter - Handle of the printer
+ dwLevel - Level of the structure pointed to by lpbDocInfo. Must be 1.
+ lpbDocInfo - Points to the DOC_INFO_1 object
+
+Return Value:
+
+ TRUE if the function succeeds, FALSE otherwise. The extended error
+ can be retrieved through GetLastError().
+
+--*/
+{
+ DWORD err;
+ DOC_INFO_1 *pDocInfo1 = (DOC_INFO_1 *) lpbDocInfo;
+ LPWSTR pszUser = NULL;
+ BOOL fGateway = FALSE;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ {
+ KdPrint(( "NWSPL [StartDocPrinter] " ));
+ if ( pDocInfo1 )
+ {
+ if ( pDocInfo1->pDocName )
+ KdPrint(("Document %ws", pDocInfo1->pDocName ));
+ if ( pDocInfo1->pOutputFile )
+ KdPrint(("OutputFile %ws", pDocInfo1->pOutputFile ));
+ if ( pDocInfo1->pDatatype )
+ KdPrint(("Datatype %ws", pDocInfo1->pDatatype ));
+ }
+ KdPrint(("\n"));
+ }
+#endif
+
+ if ( hPrinter == &handleDummy )
+ {
+ SetLastError( ERROR_NO_NETWORK );
+ return FALSE;
+ }
+ else if ( dwLevel != 1 )
+ {
+ SetLastError( ERROR_INVALID_PARAMETER );
+ return FALSE;
+ }
+
+ // ignore the error, just use default value
+ NwpGetUserInfo( &pszUser, &fGateway );
+
+ RpcTryExcept
+ {
+ err = NwrStartDocPrinter( (NWWKSTA_PRINTER_CONTEXT) hPrinter,
+ pDocInfo1? pDocInfo1->pDocName : NULL,
+ pszUser,
+ fGateway );
+
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+
+ if ( code == RPC_S_SERVER_UNAVAILABLE )
+ err = ERROR_INVALID_HANDLE;
+ else
+ err = NwpMapRpcError( code );
+ }
+ RpcEndExcept
+
+ LocalFree( pszUser );
+
+ if ( err )
+ SetLastError( err );
+
+ return err == NO_ERROR;
+}
+
+
+
+BOOL
+WritePrinter(
+ HANDLE hPrinter,
+ LPVOID pBuf,
+ DWORD cbBuf,
+ LPDWORD pcbWritten
+)
+/*++
+
+Routine Description:
+
+ This routine informs the print spooler that the specified data should be
+ written to the given printer.
+
+Arguments:
+
+ hPrinter - Handle of the printer object
+ pBuf - Address of array that contains printer data
+ cbBuf - Size, in bytes of pBuf
+ pcbWritten - Receives the number of bytes actually written to the printer
+
+Return Value:
+
+ TRUE if it succeeds, FALSE otherwise. Use GetLastError() to get extended
+ error.
+
+--*/
+{
+ DWORD err;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(( "NWSPL [WritePrinter]\n"));
+
+ if ( hPrinter == &handleDummy )
+ {
+ SetLastError( ERROR_NO_NETWORK );
+ return FALSE;
+ }
+#endif
+
+ RpcTryExcept
+ {
+ err = NwrWritePrinter( (NWWKSTA_PRINTER_CONTEXT) hPrinter,
+ pBuf,
+ cbBuf,
+ pcbWritten );
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+
+ if ( code == RPC_S_SERVER_UNAVAILABLE )
+ err = ERROR_INVALID_HANDLE;
+ else
+ err = NwpMapRpcError( code );
+ }
+ RpcEndExcept
+
+ if ( err )
+ SetLastError( err );
+
+ return err == NO_ERROR;
+}
+
+
+
+BOOL
+AbortPrinter(
+ HANDLE hPrinter
+)
+/*++
+
+Routine Description:
+
+ This routine deletes a printer's spool file if the printer is configured
+ for spooling.
+
+Arguments:
+
+ hPrinter - Handle of the printer object
+
+Return Value:
+
+ TRUE if the function succeeds, FALSE otherwise.
+
+--*/
+{
+ DWORD err;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(( "NWSPL [AbortPrinter]\n"));
+
+ if ( hPrinter == &handleDummy )
+ {
+ SetLastError( ERROR_NO_NETWORK );
+ return FALSE;
+ }
+#endif
+
+ RpcTryExcept
+ {
+ err = NwrAbortPrinter( (NWWKSTA_PRINTER_CONTEXT) hPrinter );
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+
+ if ( code == RPC_S_SERVER_UNAVAILABLE )
+ err = ERROR_INVALID_HANDLE;
+ else
+ err = NwpMapRpcError( code );
+ }
+ RpcEndExcept
+
+ if ( err )
+ SetLastError( err );
+
+ return err == NO_ERROR;
+
+}
+
+
+
+BOOL
+EndDocPrinter(
+ HANDLE hPrinter
+)
+/*++
+
+Routine Description:
+
+ This routine ends the print job for the given printer.
+
+Arguments:
+
+ hPrinter - Handle of the printer object
+
+Return Value:
+
+ TRUE if the function succeeds, FALSE otherwise.
+
+--*/
+{
+ DWORD err;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(( "NWSPL [EndDocPrinter]\n"));
+#endif
+
+ if ( hPrinter == &handleDummy )
+ {
+ SetLastError( ERROR_NO_NETWORK );
+ return FALSE;
+ }
+
+ RpcTryExcept
+ {
+ err = NwrEndDocPrinter( (NWWKSTA_PRINTER_CONTEXT) hPrinter );
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+
+ if ( code == RPC_S_SERVER_UNAVAILABLE )
+ err = ERROR_INVALID_HANDLE;
+ else
+ err = NwpMapRpcError( code );
+ }
+ RpcEndExcept
+
+ if ( err )
+ SetLastError( err );
+
+ return err == NO_ERROR;
+}
+
+
+
+BOOL
+GetJob(
+ HANDLE hPrinter,
+ DWORD dwJobId,
+ DWORD dwLevel,
+ LPBYTE pbJob,
+ DWORD cbBuf,
+ LPDWORD pcbNeeded
+)
+/*++
+
+Routine Description:
+
+ This routine retrieves print-job data for the given printer.
+
+Arguments:
+
+ hPrinter - Handle of the printer
+ dwJobId - Job identifition number
+ dwLevel - Data structure level of pbJob
+ pbJob - Address of data-structure array
+ cbBuf - Count of bytes in array
+ pcbNeeded - Count of bytes retrieved or required
+
+Return Value:
+
+ TRUE if the function succeeds, FALSE otherwise.
+
+--*/
+{
+ DWORD err;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [GetJob] JobId = %d Level = %d\n", dwJobId, dwLevel));
+#endif
+
+ if ( hPrinter == &handleDummy )
+ {
+ SetLastError( ERROR_NO_NETWORK );
+ return FALSE;
+ }
+ else if (( dwLevel != 1 ) && ( dwLevel != 2 ))
+ {
+ SetLastError( ERROR_INVALID_LEVEL );
+ return FALSE;
+ }
+
+ RpcTryExcept
+ {
+ err = NwrGetJob( (NWWKSTA_PRINTER_CONTEXT) hPrinter,
+ dwJobId,
+ dwLevel,
+ pbJob,
+ cbBuf,
+ pcbNeeded );
+
+ if ( !err )
+ {
+ if ( dwLevel == 1 )
+ MarshallUpStructure( pbJob, JobInfo1Offsets, pbJob );
+ else
+ MarshallUpStructure( pbJob, JobInfo2Offsets, pbJob );
+ }
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+
+ if ( code == RPC_S_SERVER_UNAVAILABLE )
+ err = ERROR_INVALID_HANDLE;
+ else
+ err = NwpMapRpcError( code );
+ }
+ RpcEndExcept
+
+ if ( err )
+ SetLastError( err );
+
+ return err == NO_ERROR;
+
+}
+
+
+
+BOOL
+EnumJobs(
+ HANDLE hPrinter,
+ DWORD dwFirstJob,
+ DWORD dwNoJobs,
+ DWORD dwLevel,
+ LPBYTE pbJob,
+ DWORD cbBuf,
+ LPDWORD pcbNeeded,
+ LPDWORD pcReturned
+)
+/*++
+
+Routine Description:
+
+ This routine initializes the array of JOB_INFO_1 or JOB_INFO_2 structures
+ with data describing the specified print jobs for the given printer.
+
+Arguments:
+
+ hPrinter - Handle of the printer
+ dwFirstJob - Location of first job in the printer
+ dwNoJobs - Number of jobs to enumerate
+ dwLevel - Data structure level
+ pbJob - Address of structure array
+ cbBuf - Size of pbJob, in bytes
+ pcbNeeded - Receives the number of bytes copied or required
+ pcReturned - Receives the number of jobs copied
+
+Return Value:
+
+ TRUE if the function succeeds, FALSE otherwise.
+
+--*/
+{
+ DWORD err;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [EnumJobs] Level = %d FirstJob = %d NoJobs = %d\n",
+ dwLevel, dwFirstJob, dwNoJobs));
+#endif
+
+ if ( hPrinter == &handleDummy )
+ {
+ SetLastError( ERROR_NO_NETWORK );
+ return FALSE;
+ }
+ else if ( ( dwLevel != 1 ) && ( dwLevel != 2 ) )
+ {
+ SetLastError( ERROR_INVALID_LEVEL );
+ return FALSE;
+ }
+
+ RpcTryExcept
+ {
+ *pcReturned = 0;
+ *pcbNeeded = 0;
+
+ err = NwrEnumJobs( (NWWKSTA_PRINTER_CONTEXT) hPrinter,
+ dwFirstJob,
+ dwNoJobs,
+ dwLevel,
+ pbJob,
+ cbBuf,
+ pcbNeeded,
+ pcReturned );
+
+ if ( !err )
+ {
+ DWORD i;
+ DWORD cbStruct;
+ DWORD *pOffsets;
+
+ if ( dwLevel == 1 )
+ {
+ cbStruct = sizeof( JOB_INFO_1W );
+ pOffsets = JobInfo1Offsets;
+ }
+ else // dwLevel == 2
+ {
+ cbStruct = sizeof( JOB_INFO_2W );
+ pOffsets = JobInfo2Offsets;
+ }
+
+ for ( i = 0; i < *pcReturned; i++ )
+ MarshallUpStructure( pbJob + i * cbStruct, pOffsets, pbJob );
+ }
+
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+
+ if ( code == RPC_S_SERVER_UNAVAILABLE )
+ err = ERROR_INVALID_HANDLE;
+ else
+ err = NwpMapRpcError( code );
+ }
+ RpcEndExcept
+
+ if ( err )
+ SetLastError( err );
+
+ return err == NO_ERROR;
+
+}
+
+
+
+BOOL
+SetJob(
+ HANDLE hPrinter,
+ DWORD dwJobId,
+ DWORD dwLevel,
+ LPBYTE pbJob,
+ DWORD dwCommand
+)
+/*++
+
+Routine Description:
+
+ This routine pauses, cancels, resumes, restarts the specified print job
+ in the given printer. The function can also be used to set print job
+ parameters such as job position, and so on.
+
+Arguments:
+
+ hPrinter - Handle of the printer
+ dwJobId - Job indentification number
+ dwLevel - Data structure level
+ pbJob - Address of data structure
+ dwCommand - Specify the operation to be performed
+
+Return Value:
+
+ TRUE if the function succeeds, FALSE otherwise.
+
+--*/
+{
+ DWORD err;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ {
+ KdPrint(("NWSPL [SetJob] Level = %d JobId = %d Command = %d\n",
+ dwLevel, dwJobId, dwCommand));
+ }
+#endif
+
+ if ( hPrinter == &handleDummy )
+ {
+ SetLastError( ERROR_NO_NETWORK );
+ return FALSE;
+ }
+ else if ( ( dwLevel != 0 ) && ( dwLevel != 1 ) && ( dwLevel != 2 ) )
+ {
+ SetLastError( ERROR_INVALID_LEVEL );
+ return FALSE;
+ }
+ else if ( ( dwLevel == 0 ) && ( pbJob != NULL ) )
+ {
+ SetLastError( ERROR_INVALID_PARAMETER );
+ return FALSE;
+ }
+
+ RpcTryExcept
+ {
+ NW_JOB_INFO NwJobInfo;
+
+ if ( dwLevel == 1 )
+ {
+ NwJobInfo.nPosition = ((LPJOB_INFO_1W) pbJob)->Position;
+ NwJobInfo.pUserName = ((LPJOB_INFO_1W) pbJob)->pUserName;
+ NwJobInfo.pDocument = ((LPJOB_INFO_1W) pbJob)->pDocument;
+ }
+ else if ( dwLevel == 2 )
+ {
+ NwJobInfo.nPosition = ((LPJOB_INFO_2W) pbJob)->Position;
+ NwJobInfo.pUserName = ((LPJOB_INFO_2W) pbJob)->pUserName;
+ NwJobInfo.pDocument = ((LPJOB_INFO_2W) pbJob)->pDocument;
+ }
+
+ err = NwrSetJob( (NWWKSTA_PRINTER_CONTEXT) hPrinter,
+ dwJobId,
+ dwLevel,
+ dwLevel == 0 ? NULL : &NwJobInfo,
+ dwCommand );
+
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+
+ if ( code == RPC_S_SERVER_UNAVAILABLE )
+ err = ERROR_INVALID_HANDLE;
+ else
+ err = NwpMapRpcError( code );
+ }
+ RpcEndExcept
+
+ if ( err )
+ SetLastError( err );
+
+ return err == NO_ERROR;
+
+}
+
+
+
+BOOL
+AddJob(
+ HANDLE hPrinter,
+ DWORD dwLevel,
+ LPBYTE pbAddJob,
+ DWORD cbBuf,
+ LPDWORD pcbNeeded
+)
+/*++
+
+Routine Description:
+
+ This routine returns a full path and filename of a file that can be used
+ to store a print job.
+
+Arguments:
+
+ hPrinter - Handle of the printer
+ dwLevel - Data structure level
+ pbAddJob - Points to a ADD_INFO_1 structure
+ cbBuf - Size of pbAddJob, in bytes
+ pcbNeeded - Receives the bytes copied or required
+
+Return Value:
+
+ TRUE if the function succeeds, FALSE otherwise.
+
+--*/
+{
+ DWORD err;
+
+ ADDJOB_INFO_1W TempBuffer;
+ PADDJOB_INFO_1W OutputBuffer;
+ DWORD OutputBufferSize;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(( "NWSPL [AddJob]\n"));
+#endif
+
+ if ( hPrinter == &handleDummy )
+ {
+ SetLastError( ERROR_NO_NETWORK );
+ return FALSE;
+ }
+ else if ( dwLevel != 1 )
+ {
+ SetLastError( ERROR_INVALID_PARAMETER );
+ return FALSE;
+ }
+
+ //
+ // The output buffer size must be at least the size of the fixed
+ // portion of the structure marshalled by RPC or RPC will not
+ // call the server-side to get the pcbNeeded. Use our own temporary
+ // buffer to force RPC to call the server-side if output buffer
+ // specified by the caller is too small.
+ //
+ if (cbBuf < sizeof(ADDJOB_INFO_1W)) {
+ OutputBuffer = &TempBuffer;
+ OutputBufferSize = sizeof(ADDJOB_INFO_1W);
+ }
+ else {
+ OutputBuffer = (LPADDJOB_INFO_1W) pbAddJob;
+ OutputBufferSize = cbBuf;
+ }
+
+ RpcTryExcept
+ {
+ err = NwrAddJob( (NWWKSTA_PRINTER_CONTEXT) hPrinter,
+ OutputBuffer,
+ OutputBufferSize,
+ pcbNeeded );
+
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+
+ if ( code == RPC_S_SERVER_UNAVAILABLE )
+ err = ERROR_INVALID_HANDLE;
+ else
+ err = NwpMapRpcError( code );
+ }
+ RpcEndExcept
+
+ if ( err )
+ SetLastError( err );
+
+ return err == NO_ERROR;
+}
+
+
+
+BOOL
+ScheduleJob(
+ HANDLE hPrinter,
+ DWORD dwJobId
+)
+/*++
+
+Routine Description:
+
+ This routine informs the print spooler that the specified job can be
+ scheduled for spooling.
+
+Arguments:
+
+ hPrinter - Handle of the printer
+ dwJobId - Job number that can be scheduled
+
+Return Value:
+
+ TRUE if the function succeeds, FALSE otherwise.
+
+--*/
+{
+ DWORD err;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(( "NWSPL [ScheduleJob] JobId = %d\n", dwJobId ));
+#endif
+
+ if ( hPrinter == &handleDummy )
+ {
+ SetLastError( ERROR_NO_NETWORK );
+ return FALSE;
+ }
+
+ RpcTryExcept
+ {
+ err = NwrScheduleJob( (NWWKSTA_PRINTER_CONTEXT) hPrinter,
+ dwJobId );
+
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+
+ if ( code == RPC_S_SERVER_UNAVAILABLE )
+ err = ERROR_INVALID_HANDLE;
+ else
+ err = NwpMapRpcError( code );
+ }
+ RpcEndExcept
+
+ if ( err )
+ SetLastError( err );
+
+ return err == NO_ERROR;
+}
+
+
+
+DWORD
+WaitForPrinterChange(
+ HANDLE hPrinter,
+ DWORD dwFlags
+)
+/*++
+
+Routine Description:
+
+ This function returns when one or more requested changes occur on a
+ print server or if the function times out.
+
+Arguments:
+
+ hPrinter - Handle of the printer to wait on
+ dwFlags - A bitmask that specifies the changes that the application
+ wishes to be notified of.
+
+Return Value:
+
+ Return a bitmask that indicates the changes that occurred.
+
+--*/
+{
+ DWORD err;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [WaitForPrinterChange] Flags = %d\n", dwFlags));
+#endif
+
+ if ( hPrinter == &handleDummy )
+ {
+ SetLastError( ERROR_NO_NETWORK );
+ return 0;
+ }
+
+ RpcTryExcept
+ {
+ err = NwrWaitForPrinterChange( (NWWKSTA_PRINTER_CONTEXT) hPrinter,
+ &dwFlags );
+
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+
+ if ( code == RPC_S_SERVER_UNAVAILABLE )
+ err = ERROR_INVALID_HANDLE;
+ else
+ err = NwpMapRpcError( code );
+ }
+ RpcEndExcept
+
+ if ( err )
+ {
+ SetLastError( err );
+ return 0;
+ }
+
+ return dwFlags;
+}
+
+
+
+BOOL
+EnumPortsW(
+ LPWSTR pszName,
+ DWORD dwLevel,
+ LPBYTE pbPort,
+ DWORD cbBuf,
+ LPDWORD pcbNeeded,
+ LPDWORD pcReturned
+)
+/*++
+
+Routine Description:
+
+ This function enumerates the ports available for printing on a
+ specified server.
+
+Arguments:
+
+ pszName - Name of the server to enumerate on
+ dwLevel - Structure level
+ pbPort - Address of array to receive the port information
+ cbBuf - Size, in bytes, of pbPort
+ pcbNeeded - Address to store the number of bytes needed or copied
+ pcReturned - Address to store the number of entries copied
+
+Return Value:
+
+ TRUE if the function succeeds, FALSE otherwise.
+
+--*/
+{
+ DWORD err = NO_ERROR;
+ DWORD cb = 0;
+ PNWPORT pNwPort;
+ LPPORT_INFO_1W pPortInfo1;
+ LPBYTE pEnd = pbPort + cbBuf;
+ LPBYTE pFixedDataEnd = pbPort;
+ BOOL FitInBuffer;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [EnumPorts]\n"));
+#endif
+
+ if ( dwLevel != 1 )
+ {
+ SetLastError( ERROR_INVALID_NAME );
+ return FALSE;
+ }
+ else if ( !IsLocalMachine( pszName ) )
+ {
+ SetLastError( ERROR_INVALID_NAME );
+ return FALSE;
+ }
+
+ EnterCriticalSection( &NwSplSem );
+
+ pNwPort = pNwFirstPort;
+ while ( pNwPort )
+ {
+ cb += sizeof(PORT_INFO_1W) + ( wcslen( pNwPort->pName)+1)*sizeof(WCHAR);
+ pNwPort = pNwPort->pNext;
+ }
+
+ *pcbNeeded = cb;
+ *pcReturned = 0;
+
+ if ( cb <= cbBuf )
+ {
+ pEnd = pbPort + cbBuf;
+
+ pNwPort = pNwFirstPort;
+ while ( pNwPort )
+ {
+ pPortInfo1 = (LPPORT_INFO_1W) pFixedDataEnd;
+ pFixedDataEnd += sizeof( PORT_INFO_1W );
+
+ FitInBuffer = NwlibCopyStringToBuffer( pNwPort->pName,
+ wcslen( pNwPort->pName),
+ (LPCWSTR) pFixedDataEnd,
+ (LPWSTR *) &pEnd,
+ &pPortInfo1->pName );
+ ASSERT( FitInBuffer );
+
+ pNwPort = pNwPort->pNext;
+ (*pcReturned)++;
+ }
+ }
+ else
+ {
+ err = ERROR_INSUFFICIENT_BUFFER;
+ }
+
+ LeaveCriticalSection( &NwSplSem );
+
+ if ( err )
+ SetLastError( err );
+
+ return err == NO_ERROR;
+}
+
+
+
+BOOL
+DeletePortW(
+ LPWSTR pszName,
+ HWND hWnd,
+ LPWSTR pszPortName
+)
+/*++
+
+Routine Description:
+
+ This routine deletes the port given on the server. A dialog can
+ be displayed if needed.
+
+Arguments:
+
+ pszName - Name of the server for which the port should be deleted
+ hWnd - Parent window
+ pszPortName - The name of the port to delete
+
+Return Value:
+
+ TRUE if the function succeeds, FALSE otherwise.
+
+--*/
+{
+ DWORD err;
+ BOOL fPortDeleted;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [DeletePort]\n"));
+#endif
+
+ if ( !IsLocalMachine( pszName ) )
+ {
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+ }
+
+ fPortDeleted = DeletePortEntry( pszPortName );
+
+ if ( fPortDeleted )
+ {
+ err = DeleteRegistryEntry( pszPortName );
+ }
+ else
+ {
+ err = ERROR_UNKNOWN_PORT;
+ }
+
+ if ( err )
+ SetLastError( err );
+
+ return err == NO_ERROR;
+
+}
+
+
+
+BOOL
+ConfigurePortW(
+ LPWSTR pszName,
+ HWND hWnd,
+ LPWSTR pszPortName
+)
+/*++
+
+Routine Description:
+
+ This routine displays the port configuration dialog box
+ for the given port on the given server.
+
+Arguments:
+
+ pszName - Name of the server on which the given port exist
+ hWnd - Parent window
+ pszPortName - The name of the port to be configured
+
+Return Value:
+
+ TRUE if the function succeeds, FALSE otherwise.
+
+--*/
+{
+ DWORD nCurrentThreadId;
+ DWORD nWindowThreadId;
+ WCHAR szCaption[MAX_PATH];
+ WCHAR szMessage[MAX_PATH];
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [ConfigurePort] PortName = %ws\n", pszPortName));
+#endif
+
+ if ( !IsLocalMachine( pszName ) )
+ {
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+ }
+ else if ( !PortKnown( pszPortName ) )
+ {
+ SetLastError( ERROR_UNKNOWN_PORT );
+ return FALSE;
+ }
+
+ nCurrentThreadId = GetCurrentThreadId();
+ nWindowThreadId = GetWindowThreadProcessId( hWnd, NULL );
+
+ if ( !AttachThreadInput( nCurrentThreadId, nWindowThreadId, TRUE ))
+ KdPrint(("[NWSPL] AttachThreadInput failed with %d.\n",GetLastError()));
+
+ if ( LoadStringW( hmodNW,
+ IDS_NETWARE_PRINT_CAPTION,
+ szCaption,
+ sizeof( szCaption ) / sizeof( WCHAR )))
+ {
+ if ( LoadStringW( hmodNW,
+ IDS_NOTHING_TO_CONFIGURE,
+ szMessage,
+ sizeof( szMessage ) / sizeof( WCHAR )))
+ {
+ MessageBox( hWnd, szMessage, szCaption,
+ MB_OK | MB_ICONINFORMATION );
+ }
+ else
+ {
+ KdPrint(("[NWSPL] LoadString failed with %d.\n",GetLastError()));
+ }
+ }
+ else
+ {
+ KdPrint(("[NWSPL] LoadString failed with %d.\n",GetLastError()));
+ }
+
+ if ( !AttachThreadInput( nCurrentThreadId, nWindowThreadId, FALSE ))
+ KdPrint(("[NWSPL] DetachThreadInput failed with %d.\n",GetLastError()));
+
+ return TRUE;
+}
+
+//------------------------------------------------------------------
+//
+// Print Provider Functions not supported by NetWare provider
+//
+//------------------------------------------------------------------
+
+BOOL
+AddPrinterConnectionW(
+ LPWSTR pszPrinterName
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ {
+ KdPrint(("NWSPL [AddPrinterConnection] PrinterName = %ws\n",
+ pszPrinterName));
+ }
+#endif
+
+ SetLastError( ERROR_INVALID_NAME );
+ return FALSE;
+}
+
+BOOL
+EnumMonitorsW(
+ LPWSTR pszName,
+ DWORD dwLevel,
+ LPBYTE pbMonitor,
+ DWORD cbBuf,
+ LPDWORD pcbNeeded,
+ LPDWORD pcReturned
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [EnumMonitors]\n"));
+#endif
+
+ SetLastError( ERROR_INVALID_NAME );
+ return FALSE;
+}
+
+
+BOOL
+AddPortW(
+ LPWSTR pszName,
+ HWND hWnd,
+ LPWSTR pszMonitorName
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [AddPort]\n"));
+#endif
+
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+}
+
+
+HANDLE
+AddPrinterW(
+ LPWSTR pszName,
+ DWORD dwLevel,
+ LPBYTE pbPrinter
+)
+
+// Creates a print queue on the netware server and returns a handle to it
+{
+#ifdef NOT_USED
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [AddPrinterW]\n"));
+#endif
+
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return FALSE;
+#else
+
+ LPTSTR pszPrinterName = NULL;
+ LPTSTR pszPServer = NULL;
+ LPTSTR pszQueue = NULL;
+ HANDLE hPrinter = NULL;
+ DWORD err;
+ PPRINTER_INFO_2 pPrinterInfo;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [AddPrinterW]\n"));
+#endif
+
+ pPrinterInfo = (PPRINTER_INFO_2)pbPrinter;
+
+
+ if (dwLevel != 2)
+ {
+ err = ERROR_INVALID_PARAMETER;
+ goto ErrorExit;
+ }
+
+
+ if (!(pszPrinterName = (LPTSTR)LocalAlloc(LPTR, (wcslen(((PRINTER_INFO_2 *)pbPrinter)->pPrinterName)+1)* sizeof(WCHAR))))
+ {
+ err = ERROR_NOT_ENOUGH_MEMORY;
+ goto ErrorExit;
+ }
+
+ wcscpy(pszPrinterName,pPrinterInfo->pPrinterName);
+
+ // PrinterName is the name represented as \\server\share
+ //The pszPServer parameter could have multiple fields separated by semicolons
+
+
+ if ( ( !ValidateUNCName( pszPrinterName ) )
+ || ( (pszQueue = wcschr( pszPrinterName + 2, L'\\')) == NULL )
+ || ( pszQueue == (pszPrinterName + 2) )
+ || ( *(pszQueue + 1) == L'\0' )
+ )
+ {
+ err = ERROR_INVALID_NAME;
+ goto ErrorExit;
+ }
+
+
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(( "NWSPL [AddPrinter] Name = %ws\n",pszPServer));
+#endif
+
+ if ( pszPrinterName == NULL )
+//PrinterName is a mandatory field but not the list of PServers
+ {
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(( "NWSPL [AddPrinter] Printername not supplied\n" ));
+#endif
+
+ SetLastError( ERROR_INVALID_NAME );
+ goto ErrorExit;
+ }
+
+ //Check to see if there is a port of the same name
+ // If so, abort the addprinter operation.
+ // This code was commented earlier
+
+ if (PortExists(pszPrinterName, &err ) && !err )
+ {
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(( "NWSPL [AddPrinter], = %ws; Port exists with same name\n", pszPrinterName ));
+#endif
+ SetLastError(ERROR_ALREADY_ASSIGNED);
+ goto ErrorExit;
+ }
+
+
+ // Put all the relevant information into the PRINTER_INFO_2 structure
+
+ RpcTryExcept
+ {
+ err = NwrAddPrinter ( NULL,
+ (LPPRINTER_INFO_2W) pPrinterInfo,
+ (LPNWWKSTA_PRINTER_CONTEXT) &hPrinter
+ );
+ if (!err)
+ {
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(( "NWSPL [AddPrinter] Name = %ws\n", pszPrinterName ));
+#endif
+ goto ErrorExit;
+ }
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+ err = NwpMapRpcError( code );
+ goto ErrorExit;
+ }
+ RpcEndExcept
+ if ( !pszPrinterName)
+ (void) LocalFree((HLOCAL)pszPrinterName);
+
+ return hPrinter;
+
+ErrorExit:
+ if ( !pszPrinterName)
+ (void) LocalFree((HLOCAL)pszPrinterName);
+
+ SetLastError( err);
+ return (HANDLE)0x0;
+
+#endif // #ifdef NOT_USED
+}
+
+BOOL
+DeletePrinter(
+ HANDLE hPrinter
+)
+{
+#ifdef NOT_USED
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [DeletePrinter]\n"));
+#endif
+
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return FALSE;
+#else
+ LPWSTR pszPrinterName = NULL ; // Used to delete entry from registry
+ DWORD err = NO_ERROR;
+ DWORD DoesPortExist;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [DeletePrinter]\n"));
+#endif
+
+ pszPrinterName = (LPWSTR)LocalAlloc(LPTR,sizeof(WCHAR)*MAX_PATH);
+
+ if(pszPrinterName == NULL)
+ {
+ err = ERROR_NOT_ENOUGH_MEMORY;
+ return FALSE;
+ }
+ //FIXFIX Should you delete the registry and port entries
+ //
+ // Just return success if the handle is a dummy one
+ //
+ if ( hPrinter == &handleDummy )
+ {
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [DeletePrinter] Dummy handle \n"));
+#endif
+ SetLastError(ERROR_NO_NETWORK);
+ return FALSE;
+ }
+ RpcTryExcept
+ {
+
+
+ err = NwrDeletePrinter( NULL,
+ // pszPrinterName,
+ (LPNWWKSTA_PRINTER_CONTEXT) &hPrinter );
+
+ }
+ RpcExcept(1)
+ {
+ DWORD code = RpcExceptionCode();
+
+ if ( code == RPC_S_SERVER_UNAVAILABLE )
+ err = ERROR_INVALID_HANDLE;
+ else
+ err = NwpMapRpcError( code );
+ }
+ RpcEndExcept
+
+ if (!err && PortExists(pszPrinterName, &DoesPortExist) && DoesPortExist)
+ {
+
+ if ( DeleteRegistryEntry (pszPrinterName))
+ (void) DeletePortEntry(pszPrinterName);
+
+ }
+
+
+ if ( err )
+ SetLastError( err );
+
+ return err == NO_ERROR;
+
+#endif // #ifdef NOT_USED
+}
+
+
+BOOL
+DeletePrinterConnectionW(
+ LPWSTR pszName
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [DeletePrinterConnection]\n"));
+#endif
+
+ SetLastError( ERROR_INVALID_NAME );
+ return FALSE;
+}
+
+
+BOOL
+AddPrinterDriverW(
+ LPWSTR pszName,
+ DWORD dwLevel,
+ LPBYTE pbPrinter
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [AddPrinterDriver]\n"));
+#endif
+
+ SetLastError( ERROR_INVALID_NAME );
+ return FALSE;
+}
+
+BOOL
+EnumPrinterDriversW(
+ LPWSTR pszName,
+ LPWSTR pszEnvironment,
+ DWORD dwLevel,
+ LPBYTE pbDriverInfo,
+ DWORD cbBuf,
+ LPDWORD pcbNeeded,
+ LPDWORD pcReturned
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [EnumPrinterDrivers]\n"));
+#endif
+
+ SetLastError( ERROR_INVALID_NAME );
+ return FALSE;
+}
+
+BOOL
+GetPrinterDriverW(
+ HANDLE hPrinter,
+ LPWSTR pszEnvironment,
+ DWORD dwLevel,
+ LPBYTE pbDriverInfo,
+ DWORD cbBuf,
+ LPDWORD pcbNeeded
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [GetPrinterDriver]\n"));
+#endif
+
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+}
+
+BOOL
+GetPrinterDriverDirectoryW(
+ LPWSTR pszName,
+ LPWSTR pszEnvironment,
+ DWORD dwLevel,
+ LPBYTE pbDriverDirectory,
+ DWORD cbBuf,
+ LPDWORD pcbNeeded
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [GetPrinterDriverDirectory]\n"));
+#endif
+
+ SetLastError( ERROR_INVALID_NAME );
+ return FALSE;
+}
+
+BOOL
+DeletePrinterDriverW(
+ LPWSTR pszName,
+ LPWSTR pszEnvironment,
+ LPWSTR pszDriverName
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [DeletePrinterDriver]\n"));
+#endif
+
+ SetLastError( ERROR_INVALID_NAME );
+ return FALSE;
+}
+
+BOOL
+AddPrintProcessorW(
+ LPWSTR pszName,
+ LPWSTR pszEnvironment,
+ LPWSTR pszPathName,
+ LPWSTR pszPrintProcessorName
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [AddPrintProcessor]\n"));
+#endif
+
+ SetLastError( ERROR_INVALID_NAME );
+ return FALSE;
+}
+
+BOOL
+EnumPrintProcessorsW(
+ LPWSTR pszName,
+ LPWSTR pszEnvironment,
+ DWORD dwLevel,
+ LPBYTE pbPrintProcessorInfo,
+ DWORD cbBuf,
+ LPDWORD pcbNeeded,
+ LPDWORD pcReturned
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [EnumPrintProcessors]\n"));
+#endif
+
+ SetLastError( ERROR_INVALID_NAME );
+ return FALSE;
+}
+
+BOOL
+EnumPrintProcessorDatatypesW(
+ LPWSTR pszName,
+ LPWSTR pszPrintProcessorName,
+ DWORD dwLevel,
+ LPBYTE pbDatatypes,
+ DWORD cbBuf,
+ LPDWORD pcbNeeded,
+ LPDWORD pcReturned
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [EnumPrintProcessorDatatypes]\n"));
+#endif
+
+ SetLastError( ERROR_INVALID_NAME );
+ return FALSE;
+}
+
+BOOL
+GetPrintProcessorDirectoryW(
+ LPWSTR pszName,
+ LPWSTR pszEnvironment,
+ DWORD dwLevel,
+ LPBYTE pbPrintProcessorDirectory,
+ DWORD cbBuf,
+ LPDWORD pcbNeeded
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [GetPrintProcessorDirectory]\n"));
+#endif
+
+ SetLastError( ERROR_INVALID_NAME );
+ return FALSE;
+}
+
+BOOL
+StartPagePrinter(
+ HANDLE hPrinter
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [StartPagePrinter]\n"));
+#endif
+
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+}
+
+BOOL
+EndPagePrinter(
+ HANDLE hPrinter
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [EndPagePrinter]\n"));
+#endif
+
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+}
+
+BOOL
+ReadPrinter(
+ HANDLE hPrinter,
+ LPVOID pBuf,
+ DWORD cbBuf,
+ LPDWORD pcbRead
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [ReadPrinter]\n"));
+#endif
+
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+}
+
+DWORD
+GetPrinterDataW(
+ HANDLE hPrinter,
+ LPWSTR pszValueName,
+ LPDWORD pdwType,
+ LPBYTE pbData,
+ DWORD cbBuf,
+ LPDWORD pcbNeeded
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [GetPrinterData]\n"));
+#endif
+
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+}
+
+DWORD
+SetPrinterDataW(
+ HANDLE hPrinter,
+ LPWSTR pszValueName,
+ DWORD dwType,
+ LPBYTE pbData,
+ DWORD cbData
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [SetPrinterData]\n"));
+#endif
+
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+}
+
+BOOL
+AddForm(
+ HANDLE hPrinter,
+ DWORD dwLevel,
+ LPBYTE pbForm
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [AddForm]\n"));
+#endif
+
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+}
+
+BOOL
+DeleteFormW(
+ HANDLE hPrinter,
+ LPWSTR pszFormName
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [DeleteForm]\n"));
+#endif
+
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+}
+
+BOOL
+GetFormW(
+ HANDLE hPrinter,
+ LPWSTR pszFormName,
+ DWORD dwLevel,
+ LPBYTE pbForm,
+ DWORD cbBuf,
+ LPDWORD pcbNeeded
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [GetForm]\n"));
+#endif
+
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+}
+
+BOOL
+SetFormW(
+ HANDLE hPrinter,
+ LPWSTR pszFormName,
+ DWORD dwLevel,
+ LPBYTE pbForm
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [SetForm]\n"));
+#endif
+
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+}
+
+BOOL
+EnumForms(
+ HANDLE hPrinter,
+ DWORD dwLevel,
+ LPBYTE pbForm,
+ DWORD cbBuf,
+ LPDWORD pcbNeeded,
+ LPDWORD pcReturned
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [EnumForms]\n"));
+#endif
+
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+}
+
+
+HANDLE
+CreatePrinterIC(
+ HANDLE hPrinter,
+ LPDEVMODE pDevMode
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [CreatePrinterIC]\n"));
+#endif
+
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+}
+
+BOOL
+PlayGdiScriptOnPrinterIC(
+ HANDLE hPrinterIC,
+ LPBYTE pbIn,
+ DWORD cbIn,
+ LPBYTE pbOut,
+ DWORD cbOut,
+ DWORD ul
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [PlayGdiScriptOnPrinterIC]\n"));
+#endif
+
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+}
+
+BOOL
+DeletePrinterIC(
+ HANDLE hPrinterIC
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [DeletePrinterIC]\n"));
+#endif
+
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+}
+
+DWORD
+PrinterMessageBoxW(
+ HANDLE hPrinter,
+ DWORD dwError,
+ HWND hWnd,
+ LPWSTR pszText,
+ LPWSTR pszCaption,
+ DWORD dwType
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [PrinterMessageBox]\n"));
+#endif
+
+ SetLastError( ERROR_NOT_SUPPORTED );
+ return FALSE;
+}
+
+BOOL
+AddMonitorW(
+ LPWSTR pszName,
+ DWORD dwLevel,
+ LPBYTE pbMonitorInfo
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [AddMonitor]\n"));
+#endif
+
+ SetLastError( ERROR_INVALID_NAME );
+ return FALSE;
+}
+
+BOOL
+DeleteMonitorW(
+ LPWSTR pszName,
+ LPWSTR pszEnvironment,
+ LPWSTR pszMonitorName
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [DeleteMonitor]\n"));
+#endif
+
+ SetLastError( ERROR_INVALID_NAME );
+ return FALSE;
+}
+
+BOOL
+DeletePrintProcessorW(
+ LPWSTR pszName,
+ LPWSTR pszEnvironment,
+ LPWSTR pszPrintProcessorName
+)
+{
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NWSPL [DeletePrintProcessor]\n"));
+#endif
+
+ SetLastError( ERROR_INVALID_NAME );
+ return FALSE;
+}
+
+//------------------------------------------------------------------
+//
+// Print Provider Miscellaneous Functions
+//
+//------------------------------------------------------------------
+
+VOID
+NwpGetUserInfo(
+ LPWSTR *ppszUser,
+ BOOL *pfGateway
+)
+/*++
+
+Routine Description:
+
+ Get the user information of the impersonating client.
+
+Arguments:
+
+ ppszUser - A pointer to buffer to store the Unicode string if
+ the impersonated client's user name can be looked up
+ successfully. If the conversion was unsuccessful, it points
+ to NULL.
+
+ pfGateway - A pointer to a boolean to store whether the user is
+ printing through a gateway or not. We assume that
+ if the user sid and the current logon user sid is not
+ the same, then the user is printing through gateway.
+ Else the user is printing locally.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ DWORD err;
+ LPWSTR pszUserSid = NULL;
+ LPWSTR pszLogonUserSid = NULL;
+
+ //
+ // If any error occurs while trying to get the username, just
+ // assume no user name and not gateway printing.
+ //
+ *pfGateway = TRUE;
+ *ppszUser = NULL;
+
+ if ( ((err = NwpGetThreadUserInfo( ppszUser, &pszUserSid )) == NO_ERROR)
+ && ((err = NwpGetLogonUserInfo( &pszLogonUserSid )) == NO_ERROR)
+ )
+ {
+ if ( _wcsicmp( pszUserSid, pszLogonUserSid ) == 0 )
+ *pfGateway = FALSE;
+
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NwpGetUserInfo: Thread User= %ws, Thread SID = %ws,\nCurrent SID = %ws fGateway = %d\n",
+ *ppszUser, pszUserSid, pszLogonUserSid, *pfGateway ));
+#endif
+
+ LocalFree( pszUserSid );
+ LocalFree( pszLogonUserSid );
+ }
+
+}
+
+#define SIZE_OF_TOKEN_INFORMATION \
+ sizeof( TOKEN_USER ) \
+ + sizeof( SID ) \
+ + sizeof( ULONG ) * SID_MAX_SUB_AUTHORITIES
+
+DWORD
+NwpGetThreadUserInfo(
+ LPWSTR *ppszUser,
+ LPWSTR *ppszUserSid
+)
+/*++
+
+Routine Description:
+
+ Get the user name and user sid string of the impersonating client.
+
+Arguments:
+
+ ppszUser - A pointer to buffer to store the Unicode string
+ if the impersonated client's can be looked up. If the
+ lookup was unsuccessful, it points to NULL.
+
+ ppszUserSid - A pointer to buffer to store the string if the impersonated
+ client's SID can be expanded successfully into unicode string.
+ If the conversion was unsuccessful, it points to NULL.
+
+Return Value:
+
+ The error code.
+
+--*/
+{
+ DWORD err;
+ HANDLE TokenHandle;
+ UCHAR TokenInformation[ SIZE_OF_TOKEN_INFORMATION ];
+ ULONG ReturnLength;
+
+ *ppszUser = NULL;
+ *ppszUserSid = NULL;
+
+ // We can use OpenThreadToken because this server thread
+ // is impersonating a client
+
+ if ( !OpenThreadToken( GetCurrentThread(),
+ TOKEN_READ,
+ TRUE, /* Open as self */
+ &TokenHandle ))
+ {
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NwpGetThreadUserInfo: OpenThreadToken failed: Error %d\n",
+ GetLastError()));
+#endif
+ return(GetLastError());
+ }
+
+ // notice that we've allocated enough space for the
+ // TokenInformation structure. so if we fail, we
+ // return a NULL pointer indicating failure
+
+
+ if ( !GetTokenInformation( TokenHandle,
+ TokenUser,
+ TokenInformation,
+ sizeof( TokenInformation ),
+ &ReturnLength ))
+ {
+#if DBG
+ IF_DEBUG(PRINT)
+ KdPrint(("NwpGetThreadUserInfo: GetTokenInformation failed: Error %d\n",
+ GetLastError()));
+#endif
+ return(GetLastError());
+ }
+
+ CloseHandle( TokenHandle );
+
+ // convert the Sid (pointed to by pSid) to its
+ // equivalent Unicode string representation.
+
+ err = NwpGetUserNameFromSid( ((PTOKEN_USER)TokenInformation)->User.Sid,
+ ppszUser );
+ err = err? err : NwpConvertSid( ((PTOKEN_USER)TokenInformation)->User.Sid,
+ ppszUserSid );
+
+ if ( err )
+ {
+ if ( *ppszUser )
+ LocalFree( *ppszUser );
+
+ if ( *ppszUserSid )
+ LocalFree( *ppszUserSid );
+ }
+
+ return err;
+}
+
+DWORD
+NwpGetUserNameFromSid(
+ PSID pUserSid,
+ LPWSTR *ppszUserName
+)
+/*++
+
+Routine Description:
+
+ Lookup the user name given the user SID.
+
+Arguments:
+
+ pUserSid - Points to the user sid to be looked up
+
+ ppszUserName - A pointer to buffer to store the string if the impersonated
+ client's SID can be expanded successfully into unicode string.
+ If the conversion was unsuccessful, it points to NULL.
+
+Return Value:
+
+ The error code.
+
+--*/
+{
+ NTSTATUS ntstatus;
+
+ LSA_HANDLE hlsa;
+ OBJECT_ATTRIBUTES oa;
+ SECURITY_QUALITY_OF_SERVICE sqos;
+ PLSA_REFERENCED_DOMAIN_LIST plsardl = NULL;
+ PLSA_TRANSLATED_NAME plsatn = NULL;
+
+ sqos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
+ sqos.ImpersonationLevel = SecurityImpersonation;
+ sqos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ sqos.EffectiveOnly = FALSE;
+ InitializeObjectAttributes( &oa, NULL, 0L, NULL, NULL );
+ oa.SecurityQualityOfService = &sqos;
+
+ ntstatus = LsaOpenPolicy( NULL,
+ &oa,
+ POLICY_LOOKUP_NAMES,
+ &hlsa );
+
+ if ( NT_SUCCESS( ntstatus ))
+ {
+ ntstatus = LsaLookupSids( hlsa,
+ 1,
+ &pUserSid,
+ &plsardl,
+ &plsatn );
+
+ if ( NT_SUCCESS( ntstatus ))
+ {
+ UNICODE_STRING *pUnicodeStr = &((*plsatn).Name);
+
+ *ppszUserName = LocalAlloc( LMEM_ZEROINIT,
+ pUnicodeStr->Length+sizeof(WCHAR));
+
+ if ( *ppszUserName != NULL )
+ {
+ memcpy( *ppszUserName, pUnicodeStr->Buffer, pUnicodeStr->Length );
+ }
+ else
+ {
+ ntstatus = STATUS_NO_MEMORY;
+ }
+
+ LsaFreeMemory( plsardl );
+ LsaFreeMemory( plsatn );
+ }
+#if DBG
+ else
+ {
+ KdPrint(("NwpGetUserNameFromSid: LsaLookupSids failed: Error = %d\n",
+ GetLastError()));
+ }
+#endif
+
+ LsaClose( hlsa );
+ }
+#if DBG
+ else
+ {
+ KdPrint(("NwpGetUserNameFromSid: LsaOpenPolicy failed: Error = %d\n",
+ GetLastError()));
+ }
+#endif
+
+ return RtlNtStatusToDosError( ntstatus );
+
+}
+
+DWORD
+NwpGetLogonUserInfo(
+ LPWSTR *ppszUserSid
+)
+/*++
+
+Routine Description:
+
+ Get the logon user sid string from the registry.
+
+Arguments:
+
+ ppszUserSid - On return, this points to the current logon user
+ sid string.
+
+Return Value:
+
+ The error code.
+
+--*/
+{
+ DWORD err;
+ HKEY WkstaKey;
+
+ LPWSTR CurrentUser = NULL;
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters to get the sid of the CurrentUser
+ //
+ err = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ,
+ &WkstaKey
+ );
+
+ if ( err == NO_ERROR) {
+
+ //
+ // Read the current user SID string so that we
+ // know which key is the current user key to open.
+ //
+ err = NwReadRegValue(
+ WkstaKey,
+ NW_CURRENTUSER_VALUENAME,
+ &CurrentUser
+ );
+
+ RegCloseKey( WkstaKey );
+
+ if ( err == NO_ERROR) {
+ *ppszUserSid = CurrentUser;
+ }
+ }
+
+ return(err);
+}
diff --git a/private/nw/svcdlls/nwwks/client/nwspl.h b/private/nw/svcdlls/nwwks/client/nwspl.h
new file mode 100644
index 000000000..2a67f1aa4
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwspl.h
@@ -0,0 +1,76 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ nwspl.h
+
+Abstract:
+
+ Common header for print provider client-side code.
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 15-May-1993
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+#ifndef _NWSPL_INCLUDED_
+#define _NWSPL_INCLUDED_
+
+#include "nwdlg.h"
+
+typedef struct _NWPORT {
+ DWORD cb;
+ struct _NWPORT *pNext;
+ LPWSTR pName;
+} NWPORT, *PNWPORT;
+
+extern LPWSTR pszRegistryPath;
+extern LPWSTR pszRegistryPortNames;
+extern WCHAR szMachineName[];
+extern PNWPORT pNwFirstPort;
+extern CRITICAL_SECTION NwSplSem;
+
+BOOL IsLocalMachine(
+ LPWSTR pszName
+);
+
+BOOL PortExists(
+ LPWSTR pszPortName,
+ LPDWORD pError
+);
+
+BOOL PortKnown(
+ LPWSTR pszPortName
+);
+
+PNWPORT CreatePortEntry(
+ LPWSTR pszPortName
+);
+
+BOOL DeletePortEntry(
+ LPWSTR pszPortName
+);
+
+VOID DeleteAllPortEntries(
+ VOID
+);
+
+DWORD CreateRegistryEntry(
+ LPWSTR pszPortName
+);
+
+DWORD DeleteRegistryEntry(
+ LPWSTR pszPortName
+);
+
+
+#endif // _NWSPL_INCLUDED_
diff --git a/private/nw/svcdlls/nwwks/client/nwutil.c b/private/nw/svcdlls/nwwks/client/nwutil.c
new file mode 100644
index 000000000..f39a0de0d
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwutil.c
@@ -0,0 +1,872 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ nwutil.c
+
+Abstract:
+
+ Contains some misc functions used by shell extensions
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 25-Oct-1995
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <ntddnwfs.h>
+#include <ndsapi32.h>
+#include <nwmisc.h>
+#include "nwclient.h"
+#include "nwapi.h"
+#include "nwutil.h"
+
+#define EXTRA_BYTES 256
+
+BOOL
+NwIsNdsSyntax(
+ IN LPWSTR lpstrUnc
+)
+{
+ HANDLE hTreeConn;
+ DWORD dwOid;
+ DWORD status = NO_ERROR;
+
+ if ( lpstrUnc == NULL )
+ return FALSE;
+
+ status = NwOpenAndGetTreeInfo( lpstrUnc, &hTreeConn, &dwOid );
+
+ if ( status != NO_ERROR )
+ {
+ return FALSE;
+ }
+
+ CloseHandle( hTreeConn );
+
+ return TRUE;
+}
+
+VOID
+NwAbbreviateUserName(
+ IN LPWSTR pszFullName,
+ OUT LPWSTR pszUserName
+)
+{
+ if ( pszUserName == NULL )
+ return;
+
+ if ( NwIsNdsSyntax( pszFullName ))
+ {
+ //
+ // BUGBUG - This part of the code never gets called due to the
+ // change in how NwIsNdsSyntax works. Post NT 4.0, get rid of the
+ // NwIsNdsSyntax test and run this section of code no matter what.
+ // This bug was not fixed in NT4.0 due to the extremely close time
+ // to the ship date.
+ //
+ LPWSTR pszTemp = pszFullName;
+ LPWSTR pszLast = pszTemp;
+
+ *pszUserName = 0;
+
+ while ( pszTemp = wcschr( pszTemp, L'='))
+ {
+ WCHAR NextChar;
+
+ NextChar = *(++pszTemp);
+
+ while ( NextChar != 0 && NextChar != L'.' )
+ {
+ *(pszUserName++) = *pszTemp;
+ NextChar = *(++pszTemp);
+ }
+
+ if ( NextChar == 0 )
+ {
+ pszLast = NULL;
+ break;
+ }
+
+ *(pszUserName++) = *pszTemp; // put back the '.'
+ pszLast = ++pszTemp;
+ }
+
+ if ( pszLast != NULL )
+ {
+ while ( *pszLast != 0 )
+ *(pszUserName++) = *(pszLast++);
+ }
+
+ *pszUserName = 0;
+ }
+ else
+ {
+ wcscpy( pszUserName, pszFullName );
+ }
+}
+
+VOID
+NwMakePrettyDisplayName(
+ IN LPWSTR pszName
+)
+{
+ if ( pszName )
+ {
+ CharLower( pszName );
+ CharUpperBuff( pszName, 1);
+ }
+}
+
+VOID
+NwExtractTreeName(
+ IN LPWSTR pszUNCPath,
+ OUT LPWSTR pszTreeName
+)
+{
+ LPWSTR pszTemp = NULL;
+
+ if ( pszTreeName == NULL )
+ return;
+
+ pszTreeName[0] = 0;
+
+ if ( pszUNCPath == NULL )
+ return;
+
+ if ( pszUNCPath[0] == L' ')
+ pszUNCPath++;
+
+ if ( ( pszUNCPath[0] != L'\\')
+ || ( pszUNCPath[1] != L'\\')
+ )
+ {
+ return;
+ }
+
+ wcscpy( pszTreeName, pszUNCPath + 2 ); // get past "\\"
+
+ if ( pszTemp = wcschr( pszTreeName, L'\\' ))
+ *pszTemp = 0;
+}
+
+VOID
+NwExtractServerName(
+ IN LPWSTR pszUNCPath,
+ OUT LPWSTR pszServerName
+)
+{
+ LPWSTR pszTemp = NULL;
+
+ if ( pszServerName == NULL )
+ return;
+
+ pszServerName[0] = 0;
+
+ if ( ( pszUNCPath == NULL )
+ || ( pszUNCPath[0] != L'\\')
+ || ( pszUNCPath[1] != L'\\')
+ )
+ {
+ return;
+ }
+
+ if ( NwIsNdsSyntax( pszUNCPath ))
+ {
+ pszTemp = wcschr( pszUNCPath + 2, L'\\' ); // get past "\\"
+ wcscpy( pszServerName, pszTemp + 1 ); // get past "\"
+
+ if ( pszTemp = wcschr( pszServerName, L'.' ))
+ *pszTemp = 0;
+
+ }
+ else
+ {
+ wcscpy( pszServerName, pszUNCPath + 2 ); // get past "\\"
+
+ if ( pszTemp = wcschr( pszServerName, L'\\' ))
+ *pszTemp = 0;
+ }
+}
+
+VOID
+NwExtractShareName(
+ IN LPWSTR pszUNCPath,
+ OUT LPWSTR pszShareName
+)
+{
+ LPWSTR pszTemp = NULL;
+
+ if ( pszShareName == NULL )
+ return;
+
+ pszShareName[0] = 0;
+
+ if ( ( pszUNCPath == NULL )
+ || ( pszUNCPath[0] != L'\\')
+ || ( pszUNCPath[1] != L'\\')
+ )
+ {
+ return;
+ }
+
+ if ( NwIsNdsSyntax( pszUNCPath ))
+ {
+ pszTemp = wcschr( pszUNCPath + 2, L'\\' ); // get past "\\"
+ wcscpy( pszShareName, pszTemp + 1 ); // get past "\"
+
+ if ( pszTemp = wcschr( pszShareName, L'.' ))
+ *pszTemp = 0;
+ }
+ else
+ {
+ pszTemp = wcschr( pszUNCPath + 2, L'\\' ); // get past "\\"
+ wcscpy( pszShareName, pszTemp + 1); // get past "\"
+
+ if ( pszTemp = wcschr( pszShareName, L'\\' ))
+ *pszTemp = 0;
+ }
+}
+
+DWORD
+NwIsServerInDefaultTree(
+ IN LPWSTR pszFullServerName,
+ OUT BOOL *pfInDefaultTree
+)
+{
+ DWORD err = NO_ERROR;
+ LPWSTR pszCurrentContext = NULL;
+ DWORD dwPrintOptions;
+ WCHAR szTreeName[MAX_PATH + 1];
+
+ *pfInDefaultTree = FALSE;
+
+ if ( !NwIsNdsSyntax( pszFullServerName ))
+ {
+ // The full server name does not contain any NDS information
+ // In this case, assume the server is not in the tree.
+ // If a server belongs the default tree, we would get the full
+ // NDS information.
+ return NO_ERROR;
+ }
+
+ // Get the current default tree or server name
+ err = NwQueryInfo( &dwPrintOptions, &pszCurrentContext );
+
+ if ( (err == NO_ERROR) && ( *pszCurrentContext == TREECHAR))
+ {
+ // Yes, there is a default tree.
+ // So, get the tree name out of *TREE\CONTEXT
+ LPWSTR pszTemp = wcschr( pszCurrentContext, L'\\');
+ if ( pszTemp )
+ *pszTemp = 0;
+
+ // Need to extract the tree name from full UNC path
+ NwExtractTreeName( pszFullServerName, szTreeName );
+
+ if ( _wcsicmp( szTreeName,
+ pszCurrentContext + 1) == 0 ) // get past the tree char
+ {
+ *pfInDefaultTree = TRUE;
+ }
+ }
+
+ if ( pszCurrentContext != NULL )
+ LocalFree( pszCurrentContext );
+
+ return err;
+}
+
+DWORD
+NwIsServerOrTreeAttached(
+ IN LPWSTR pszName,
+ OUT BOOL *pfAttached,
+ OUT BOOL *pfAuthenticated
+)
+{
+ DWORD err = NO_ERROR;
+ DWORD EntriesRead = 0;
+ DWORD ResumeKey = 0;
+ LPBYTE Buffer = NULL;
+
+ err = NwGetConnectionStatus(
+ pszName,
+ &ResumeKey,
+ &Buffer,
+ &EntriesRead );
+
+ *pfAttached = FALSE;
+ *pfAuthenticated = FALSE;
+
+ if (( err == NO_ERROR ) && ( EntriesRead > 0 ))
+ {
+ // For trees, we might get more than one entries back.
+
+ PCONN_STATUS pConnStatus = (PCONN_STATUS) Buffer;
+
+ if ( !pConnStatus->fPreferred )
+ {
+ // We only need to return as attached if this is not a preferred
+ // server implicit connection since we don't want the user to know
+ // about this connection ( which rdr does not allow user to delete)
+
+ *pfAttached = TRUE;
+ *pfAuthenticated = (pConnStatus->dwConnType != NW_CONN_NOT_AUTHENTICATED);
+ }
+ }
+
+ if ( Buffer != NULL )
+ {
+ LocalFree( Buffer );
+ Buffer = NULL;
+ }
+
+ return err;
+}
+
+DWORD
+NwGetConnectionInformation(
+ IN LPWSTR pszName,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize
+)
+{
+ NTSTATUS ntstatus = STATUS_SUCCESS;
+ HANDLE handleRdr = NULL;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ IO_STATUS_BLOCK IoStatusBlock;
+ UNICODE_STRING uRdrName;
+ WCHAR RdrPrefix[] = L"\\Device\\NwRdr\\*";
+
+ PNWR_REQUEST_PACKET RequestPacket = NULL;
+ DWORD RequestPacketSize = 0;
+ DWORD dwNameLen = 0;
+
+ if ( pszName == NULL )
+ return ERROR_INVALID_PARAMETER;
+
+ //
+ // Set up the object attributes.
+ //
+
+ RtlInitUnicodeString( &uRdrName, RdrPrefix );
+
+ InitializeObjectAttributes( &ObjectAttributes,
+ &uRdrName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL );
+
+ ntstatus = NtOpenFile( &handleRdr,
+ SYNCHRONIZE | FILE_LIST_DIRECTORY,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_VALID_FLAGS,
+ FILE_SYNCHRONOUS_IO_NONALERT );
+
+ if ( !NT_SUCCESS(ntstatus) )
+ goto CleanExit;
+
+ dwNameLen = wcslen(pszName) * sizeof(WCHAR);
+
+ RequestPacketSize = sizeof( NWR_REQUEST_PACKET ) + dwNameLen;
+
+ RequestPacket = (PNWR_REQUEST_PACKET) LocalAlloc( LMEM_ZEROINIT,
+ RequestPacketSize );
+
+ if ( RequestPacket == NULL )
+ {
+ ntstatus = STATUS_NO_MEMORY;
+ goto CleanExit;
+ }
+
+ //
+ // Fill out the request packet for FSCTL_NWR_GET_CONN_INFO.
+ //
+
+ RequestPacket->Version = REQUEST_PACKET_VERSION;
+ RequestPacket->Parameters.GetConnInfo.ConnectionNameLength = dwNameLen;
+
+ RtlCopyMemory( &(RequestPacket->Parameters.GetConnInfo.ConnectionName[0]),
+ pszName,
+ dwNameLen );
+
+ ntstatus = NtFsControlFile( handleRdr,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_GET_CONN_INFO,
+ (PVOID) RequestPacket,
+ RequestPacketSize,
+ (PVOID) Buffer,
+ BufferSize );
+
+ if ( NT_SUCCESS( ntstatus ))
+ ntstatus = IoStatusBlock.Status;
+
+CleanExit:
+
+ if ( handleRdr != NULL )
+ NtClose( handleRdr );
+
+ if ( RequestPacket != NULL )
+ LocalFree( RequestPacket );
+
+ return RtlNtStatusToDosError( ntstatus );
+}
+
+DWORD
+NWPGetConnectionStatus(
+ IN LPWSTR pszRemoteName,
+ IN OUT PDWORD ResumeKey,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT PDWORD BytesNeeded,
+ OUT PDWORD EntriesRead
+)
+{
+ NTSTATUS ntstatus = STATUS_SUCCESS;
+ HANDLE handleRdr = NULL;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ IO_STATUS_BLOCK IoStatusBlock;
+ UNICODE_STRING uRdrName;
+ WCHAR RdrPrefix[] = L"\\Device\\NwRdr\\*";
+
+ PNWR_REQUEST_PACKET RequestPacket = NULL;
+ DWORD RequestPacketSize = 0;
+ DWORD dwRemoteNameLen = 0;
+
+ //
+ // Set up the object attributes.
+ //
+
+ RtlInitUnicodeString( &uRdrName, RdrPrefix );
+
+ InitializeObjectAttributes( &ObjectAttributes,
+ &uRdrName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL );
+
+ ntstatus = NtOpenFile( &handleRdr,
+ SYNCHRONIZE | FILE_LIST_DIRECTORY,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_VALID_FLAGS,
+ FILE_SYNCHRONOUS_IO_NONALERT );
+
+ if ( !NT_SUCCESS(ntstatus) )
+ goto CleanExit;
+
+ dwRemoteNameLen = pszRemoteName? wcslen(pszRemoteName)*sizeof(WCHAR) : 0;
+
+ RequestPacketSize = sizeof( NWR_REQUEST_PACKET ) + dwRemoteNameLen;
+
+ RequestPacket = (PNWR_REQUEST_PACKET) LocalAlloc( LMEM_ZEROINIT,
+ RequestPacketSize );
+
+ if ( RequestPacket == NULL )
+ {
+ ntstatus = STATUS_NO_MEMORY;
+ goto CleanExit;
+ }
+
+ //
+ // Fill out the request packet for FSCTL_NWR_GET_CONN_STATUS.
+ //
+
+ RequestPacket->Parameters.GetConnStatus.ResumeKey = *ResumeKey;
+
+ RequestPacket->Version = REQUEST_PACKET_VERSION;
+ RequestPacket->Parameters.GetConnStatus.ConnectionNameLength = dwRemoteNameLen;
+
+ RtlCopyMemory( &(RequestPacket->Parameters.GetConnStatus.ConnectionName[0]),
+ pszRemoteName,
+ dwRemoteNameLen );
+
+ ntstatus = NtFsControlFile( handleRdr,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_GET_CONN_STATUS,
+ (PVOID) RequestPacket,
+ RequestPacketSize,
+ (PVOID) Buffer,
+ BufferSize );
+
+ if ( NT_SUCCESS( ntstatus ))
+ ntstatus = IoStatusBlock.Status;
+
+ *EntriesRead = RequestPacket->Parameters.GetConnStatus.EntriesReturned;
+ *ResumeKey = RequestPacket->Parameters.GetConnStatus.ResumeKey;
+ *BytesNeeded = RequestPacket->Parameters.GetConnStatus.BytesNeeded;
+
+CleanExit:
+
+ if ( handleRdr != NULL )
+ NtClose( handleRdr );
+
+ if ( RequestPacket != NULL )
+ LocalFree( RequestPacket );
+
+ return RtlNtStatusToDosError( ntstatus );
+}
+
+
+DWORD
+NwGetConnectionStatus(
+ IN LPWSTR pszRemoteName,
+ OUT PDWORD ResumeKey,
+ OUT LPBYTE *Buffer,
+ OUT PDWORD EntriesRead
+)
+{
+ DWORD err = NO_ERROR;
+ DWORD dwBytesNeeded = 0;
+ DWORD dwBufferSize = TWO_KB;
+
+ *Buffer = NULL;
+ *EntriesRead = 0;
+
+ do {
+
+ *Buffer = (LPBYTE) LocalAlloc( LMEM_ZEROINIT, dwBufferSize );
+
+ if ( *Buffer == NULL )
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ err = NWPGetConnectionStatus( pszRemoteName,
+ ResumeKey,
+ *Buffer,
+ dwBufferSize,
+ &dwBytesNeeded,
+ EntriesRead );
+
+ if ( err == ERROR_INSUFFICIENT_BUFFER )
+ {
+ dwBufferSize = dwBytesNeeded + EXTRA_BYTES;
+ LocalFree( *Buffer );
+ *Buffer = NULL;
+ }
+
+ } while ( err == ERROR_INSUFFICIENT_BUFFER );
+
+ if ( err == ERROR_INVALID_PARAMETER ) // not attached
+ {
+ err = NO_ERROR;
+ *EntriesRead = 0;
+ }
+
+ return err;
+}
+
+DWORD
+NwGetNdsVolumeInfo(
+ IN LPWSTR pszName,
+ OUT LPWSTR pszServerBuffer,
+ IN WORD wServerBufferSize, // in bytes
+ OUT LPWSTR pszVolumeBuffer,
+ IN WORD wVolumeBufferSize // in bytes
+)
+{
+ NTSTATUS ntstatus = STATUS_SUCCESS;
+ HANDLE handleNdsTree;
+
+ LPWSTR pszTree, pszVolume, pszTemp;
+ UNICODE_STRING uTree, uVolume;
+
+ UNICODE_STRING uHostServer, uHostVolume;
+ WCHAR HostVolumeBuffer[256];
+
+ pszTree = pszName + 2; // get past two backslashes
+
+ pszTemp = wcschr( pszTree, L'\\' );
+ if ( pszTemp )
+ *pszTemp = 0;
+ else
+ return ERROR_INVALID_PARAMETER;
+
+ pszVolume = pszTemp + 1;
+
+ RtlInitUnicodeString( &uTree, pszTree );
+ RtlInitUnicodeString( &uVolume, pszVolume );
+
+ //
+ // Open up a handle to the tree.
+ //
+
+ ntstatus = NwNdsOpenTreeHandle( &uTree,
+ &handleNdsTree );
+
+ if ( !NT_SUCCESS( ntstatus ))
+ goto CleanExit;
+
+ //
+ // Set up the reply strings.
+ //
+
+ uHostServer.Length = 0;
+ uHostServer.MaximumLength = wServerBufferSize;
+ uHostServer.Buffer = pszServerBuffer;
+
+ RtlZeroMemory( pszServerBuffer, wServerBufferSize );
+
+ if ( pszVolumeBuffer != NULL )
+ {
+ uHostVolume.Length = 0;
+ uHostVolume.MaximumLength = wVolumeBufferSize;
+ uHostVolume.Buffer = pszVolumeBuffer;
+
+ RtlZeroMemory( pszVolumeBuffer, wVolumeBufferSize );
+ }
+ else
+ {
+ uHostVolume.Length = 0;
+ uHostVolume.MaximumLength = sizeof( HostVolumeBuffer );
+ uHostVolume.Buffer = HostVolumeBuffer;
+ }
+
+ ntstatus = NwNdsGetVolumeInformation( handleNdsTree,
+ &uVolume,
+ &uHostServer,
+ &uHostVolume );
+
+ CloseHandle( handleNdsTree );
+
+CleanExit:
+
+ if ( pszTemp )
+ *pszTemp = L'\\';
+
+ return RtlNtStatusToDosError( ntstatus );
+}
+
+DWORD
+NwOpenAndGetTreeInfo(
+ LPWSTR pszNdsUNCPath,
+ HANDLE *phTreeConn,
+ DWORD *pdwOid
+)
+{
+ NTSTATUS ntstatus = STATUS_SUCCESS;
+ WCHAR lpServerName[NW_MAX_SERVER_LEN];
+ UNICODE_STRING ServerName;
+
+ UNICODE_STRING ObjectName;
+
+ *phTreeConn = NULL;
+
+ ServerName.Length = 0;
+ ServerName.MaximumLength = sizeof( lpServerName );
+ ServerName.Buffer = lpServerName;
+
+ ObjectName.Buffer = NULL;
+ ObjectName.MaximumLength = ( wcslen( pszNdsUNCPath) + 1 ) * sizeof( WCHAR );
+
+ ObjectName.Length = NwParseNdsUncPath( (LPWSTR *) &ObjectName.Buffer,
+ pszNdsUNCPath,
+ PARSE_NDS_GET_TREE_NAME );
+
+ if ( ObjectName.Length == 0 || ObjectName.Buffer == NULL )
+ {
+ return ERROR_PATH_NOT_FOUND;
+ }
+
+ //
+ // Open a NDS tree connection handle to \\treename
+ //
+ ntstatus = NwNdsOpenTreeHandle( &ObjectName, phTreeConn );
+
+ if ( !NT_SUCCESS( ntstatus ))
+ {
+ return RtlNtStatusToDosError( ntstatus );
+ }
+
+ //
+ // Get the path to the container to open.
+ //
+ ObjectName.Length = NwParseNdsUncPath( (LPWSTR *) &ObjectName.Buffer,
+ pszNdsUNCPath,
+ PARSE_NDS_GET_PATH_NAME );
+
+ if ( ObjectName.Length == 0 )
+ {
+ UNICODE_STRING Root;
+
+ RtlInitUnicodeString(&Root, L"[Root]");
+
+ //
+ // Resolve the path to get a NDS object id.
+ //
+ ntstatus = NwNdsResolveName( *phTreeConn,
+ &Root,
+ pdwOid,
+ &ServerName,
+ NULL,
+ 0 );
+
+ }
+ else
+ {
+ //
+ // Resolve the path to get a NDS object id.
+ //
+ ntstatus = NwNdsResolveName( *phTreeConn,
+ &ObjectName,
+ pdwOid,
+ &ServerName,
+ NULL,
+ 0 );
+
+ }
+
+ if ( ntstatus == STATUS_SUCCESS && ServerName.Length )
+ {
+ DWORD dwHandleType;
+
+ //
+ // NwNdsResolveName succeeded, but we were referred to
+ // another server, though pdwOid is still valid.
+
+ if ( *phTreeConn )
+ CloseHandle( *phTreeConn );
+
+ *phTreeConn = NULL;
+
+ //
+ // Open a NDS generic connection handle to \\ServerName
+ //
+ ntstatus = NwNdsOpenGenericHandle( &ServerName,
+ &dwHandleType,
+ phTreeConn );
+
+ if ( ntstatus != STATUS_SUCCESS )
+ {
+ return RtlNtStatusToDosError(ntstatus);
+ }
+
+ ASSERT( dwHandleType != HANDLE_TYPE_NCP_SERVER );
+ }
+
+ if ( !NT_SUCCESS( ntstatus ))
+ {
+
+ if ( *phTreeConn != NULL )
+ {
+ CloseHandle( *phTreeConn );
+ *phTreeConn = NULL;
+ }
+ return RtlNtStatusToDosError(ntstatus);
+ }
+
+ return NO_ERROR;
+
+}
+
+DWORD
+NwGetConnectedTrees(
+ IN LPWSTR pszNtUserName,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT LPDWORD lpEntriesRead,
+ OUT LPDWORD lpUserLUID
+)
+{
+ NTSTATUS ntstatus = STATUS_SUCCESS;
+ HANDLE handleRdr = NULL;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ IO_STATUS_BLOCK IoStatusBlock;
+ WCHAR RdrPrefix[] = L"\\Device\\NwRdr\\*";
+ UNICODE_STRING uRdrName;
+ UNICODE_STRING uNtUserName;
+
+ PNWR_NDS_REQUEST_PACKET Request = NULL;
+ BYTE RequestBuffer[2048];
+ DWORD RequestSize = 0;
+
+ *lpEntriesRead = 0;
+
+ //
+ // Convert the user name to unicode.
+ //
+
+ RtlInitUnicodeString( &uNtUserName, pszNtUserName );
+
+ //
+ // Set up the object attributes.
+ //
+
+ RtlInitUnicodeString( &uRdrName, RdrPrefix );
+
+ InitializeObjectAttributes( &ObjectAttributes,
+ &uRdrName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL );
+
+ ntstatus = NtOpenFile( &handleRdr,
+ SYNCHRONIZE | FILE_LIST_DIRECTORY,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_VALID_FLAGS,
+ FILE_SYNCHRONOUS_IO_NONALERT );
+
+ if ( !NT_SUCCESS(ntstatus) )
+ goto CleanExit;
+
+ //
+ // Fill out the request packet for FSCTL_NWR_NDS_LIST_TREES;
+ //
+
+ Request = ( PNWR_NDS_REQUEST_PACKET ) RequestBuffer;
+
+ Request->Parameters.ListTrees.NtUserNameLength = uNtUserName.Length;
+
+ RtlCopyMemory( &(Request->Parameters.ListTrees.NtUserName[0]),
+ uNtUserName.Buffer,
+ uNtUserName.Length );
+
+ RequestSize = sizeof( Request->Parameters.ListTrees ) +
+ uNtUserName.Length +
+ sizeof( DWORD );
+
+ ntstatus = NtFsControlFile( handleRdr,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_NDS_LIST_TREES,
+ (PVOID) Request,
+ RequestSize,
+ (PVOID) Buffer,
+ BufferSize );
+
+ if ( NT_SUCCESS( ntstatus ))
+ {
+ ntstatus = IoStatusBlock.Status;
+ *lpEntriesRead = Request->Parameters.ListTrees.TreesReturned;
+ *lpUserLUID = Request->Parameters.ListTrees.UserLuid.LowPart;
+ }
+
+CleanExit:
+
+ if ( handleRdr != NULL )
+ NtClose( handleRdr );
+
+ return RtlNtStatusToDosError( ntstatus );
+}
+
+
diff --git a/private/nw/svcdlls/nwwks/client/nwutil.h b/private/nw/svcdlls/nwwks/client/nwutil.h
new file mode 100644
index 000000000..bf94a62c5
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/nwutil.h
@@ -0,0 +1,120 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ nwutil.h
+
+Abstract:
+
+ Common header for Workstation client-side code.
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 25-Oct-1995
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+#ifndef _NWUTIL_H_
+#define _NWUTIL_H_
+
+#define TREECHAR L'*'
+#define TWO_KB 2048
+
+BOOL
+NwIsNdsSyntax(
+ IN LPWSTR lpstrUnc
+);
+
+VOID
+NwAbbreviateUserName(
+ IN LPWSTR pszFullName,
+ OUT LPWSTR pszUserName
+);
+
+VOID
+NwMakePrettyDisplayName(
+ IN LPWSTR pszName
+);
+
+VOID
+NwExtractTreeName(
+ IN LPWSTR pszUNCPath,
+ OUT LPWSTR pszTreeName
+);
+
+
+VOID
+NwExtractServerName(
+ IN LPWSTR pszUNCPath,
+ OUT LPWSTR pszServerName
+);
+
+
+VOID
+NwExtractShareName(
+ IN LPWSTR pszUNCPath,
+ OUT LPWSTR pszShareName
+);
+
+DWORD
+NwIsServerInDefaultTree(
+ IN LPWSTR pszFullServerName,
+ OUT BOOL *pfInDefaultTree
+);
+
+DWORD
+NwIsServerOrTreeAttached(
+ IN LPWSTR pszServerName,
+ OUT BOOL *pfAttached,
+ OUT BOOL *pfAuthenticated
+);
+
+DWORD
+NwGetConnectionInformation(
+ IN LPWSTR pszName,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize
+);
+
+DWORD
+NwGetConnectionStatus(
+ IN LPWSTR pszServerName,
+ IN OUT PDWORD ResumeKey,
+ OUT LPBYTE *Buffer,
+ OUT PDWORD EntriesRead
+);
+
+DWORD
+NwGetNdsVolumeInfo(
+ IN LPWSTR pszName,
+ OUT LPWSTR pszServerBuffer,
+ IN WORD wServerBufferSize, // in bytes
+ OUT LPWSTR pszVolumeBuffer,
+ IN WORD wVolumeBufferSize // in bytes
+);
+
+DWORD
+NwOpenAndGetTreeInfo(
+ LPWSTR pszNdsUNCPath,
+ HANDLE *phTreeConn,
+ DWORD *pdwOid
+);
+
+DWORD
+NwGetConnectedTrees(
+ IN LPWSTR pszNtUserName,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT LPDWORD lpEntriesRead,
+ OUT LPDWORD lpUserLUID
+);
+
+#endif // _NWUTIL_H_
diff --git a/private/nw/svcdlls/nwwks/client/port.c b/private/nw/svcdlls/nwwks/client/port.c
new file mode 100644
index 000000000..4cd287e5c
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/port.c
@@ -0,0 +1,449 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ port.c
+
+Abstract:
+
+ This module contains the code for port handling
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 15-May-1993
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#include <windef.h>
+#include <winbase.h>
+#include <winreg.h>
+#include <wingdi.h>
+#include <winspool.h>
+
+#include <splutil.h>
+#include <nwspl.h>
+
+//------------------------------------------------------------------
+//
+// Local Functions
+//
+//------------------------------------------------------------------
+
+HMODULE hSpoolssDll = NULL;
+FARPROC pfnSpoolssEnumPorts = NULL;
+
+HANDLE
+RevertToPrinterSelf(
+ VOID
+);
+
+BOOL
+ImpersonatePrinterClient(
+ HANDLE hToken
+);
+
+
+
+BOOL
+IsLocalMachine(
+ LPWSTR pszName
+)
+{
+ if ( !pszName || !*pszName )
+ return TRUE;
+
+ if ( *pszName == L'\\' && *(pszName+1) == L'\\')
+ if ( !lstrcmpi( pszName, szMachineName) )
+ return TRUE;
+
+ return FALSE;
+
+}
+
+
+BOOL
+PortExists(
+ LPWSTR pPortName,
+ LPDWORD pError
+)
+/* PortExists
+ *
+ * Calls EnumPorts to check whether the port name already exists.
+ * This asks every monitor, rather than just this one.
+ * The function will return TRUE if the specified port is in the list.
+ * If an error occurs, the return is FALSE and the variable pointed
+ * to by pError contains the return from GetLastError().
+ * The caller must therefore always check that *pError == NO_ERROR.
+ */
+{
+ DWORD cbNeeded;
+ DWORD cReturned;
+ DWORD cbPorts;
+ LPPORT_INFO_1W pPorts;
+ DWORD i;
+ BOOL Found = FALSE;
+
+ *pError = NO_ERROR;
+
+ if ( !hSpoolssDll )
+ {
+ if ( hSpoolssDll = LoadLibrary( L"SPOOLSS.DLL" ))
+ {
+ pfnSpoolssEnumPorts = GetProcAddress(hSpoolssDll, "EnumPortsW");
+ if ( !pfnSpoolssEnumPorts )
+ {
+ *pError = GetLastError();
+ FreeLibrary( hSpoolssDll );
+ hSpoolssDll = NULL;
+ }
+ }
+ else
+ {
+ *pError = GetLastError();
+ }
+ }
+
+ if ( !pfnSpoolssEnumPorts )
+ return FALSE;
+
+ if ( !(*pfnSpoolssEnumPorts)( NULL, 1, NULL, 0, &cbNeeded, &cReturned) )
+ {
+ if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
+ {
+ cbPorts = cbNeeded;
+
+ EnterCriticalSection( &NwSplSem );
+
+ pPorts = AllocNwSplMem( LMEM_ZEROINIT, cbPorts );
+ if ( pPorts )
+ {
+ if ( (*pfnSpoolssEnumPorts)( NULL, 1, (LPBYTE)pPorts, cbPorts,
+ &cbNeeded, &cReturned))
+ {
+ for ( i = 0; i < cReturned; i++)
+ {
+ if ( !lstrcmpi( pPorts[i].pName, pPortName) )
+ Found = TRUE;
+ }
+ }
+ else
+ {
+ *pError = GetLastError();
+ }
+
+ FreeNwSplMem( pPorts, cbPorts );
+ }
+ else
+ {
+ *pError = ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ LeaveCriticalSection( &NwSplSem );
+
+ }
+ else
+ {
+ *pError = GetLastError();
+ }
+ }
+
+ return Found;
+}
+
+
+
+BOOL
+PortKnown(
+ LPWSTR pPortName
+)
+{
+ PNWPORT pNwPort;
+
+ EnterCriticalSection( &NwSplSem );
+
+ pNwPort = pNwFirstPort;
+
+ while ( pNwPort )
+ {
+ if ( !lstrcmpi( pNwPort->pName, pPortName ) )
+ {
+ LeaveCriticalSection( &NwSplSem );
+ return TRUE;
+ }
+
+ pNwPort = pNwPort->pNext;
+ }
+
+ LeaveCriticalSection( &NwSplSem );
+ return FALSE;
+
+}
+
+
+
+PNWPORT
+CreatePortEntry(
+ LPWSTR pPortName
+)
+{
+ PNWPORT pNwPort, pPort;
+ DWORD cb = sizeof(NWPORT) + (wcslen(pPortName) + 1) * sizeof(WCHAR);
+
+ if ( pNwPort = AllocNwSplMem( LMEM_ZEROINIT, cb))
+ {
+ pNwPort->pName = wcscpy((LPWSTR)(pNwPort+1), pPortName);
+ pNwPort->cb = cb;
+ pNwPort->pNext = NULL;
+
+ EnterCriticalSection( &NwSplSem );
+
+ if ( pPort = pNwFirstPort )
+ {
+ while ( pPort->pNext )
+ pPort = pPort->pNext;
+
+ pPort->pNext = pNwPort;
+ }
+ else
+ {
+ pNwFirstPort = pNwPort;
+ }
+
+ LeaveCriticalSection( &NwSplSem );
+ }
+
+ return pNwPort;
+}
+
+
+
+BOOL
+DeletePortEntry(
+ LPWSTR pPortName
+)
+/*
+ Return TRUE when the port name is found and deleted. FALSE otherwise.
+*/
+{
+ BOOL fRetVal;
+ PNWPORT pPort, pPrevPort;
+
+ EnterCriticalSection( &NwSplSem );
+
+ pPort = pNwFirstPort;
+ while ( pPort && lstrcmpi(pPort->pName, pPortName))
+ {
+ pPrevPort = pPort;
+ pPort = pPort->pNext;
+ }
+
+ if (pPort)
+ {
+ if (pPort == pNwFirstPort)
+ {
+ pNwFirstPort = pPort->pNext;
+ }
+ else
+ {
+ pPrevPort->pNext = pPort->pNext;
+ }
+
+ FreeNwSplMem( pPort, pPort->cb );
+ fRetVal = TRUE;
+ }
+ else
+ {
+ fRetVal = FALSE;
+ }
+
+ LeaveCriticalSection( &NwSplSem );
+
+ return fRetVal;
+}
+
+
+
+VOID
+DeleteAllPortEntries(
+ VOID
+)
+{
+ PNWPORT pPort, pNextPort;
+
+ for ( pPort = pNwFirstPort; pPort; pPort = pNextPort )
+ {
+ pNextPort = pPort->pNext;
+ FreeNwSplMem( pPort, pPort->cb );
+ }
+}
+
+
+
+DWORD
+CreateRegistryEntry(
+ LPWSTR pPortName
+)
+{
+ DWORD err;
+ HANDLE hToken;
+ HKEY hkeyPath;
+ HKEY hkeyPortNames;
+
+ hToken = RevertToPrinterSelf();
+
+ err = RegCreateKeyEx( HKEY_LOCAL_MACHINE, pszRegistryPath, 0,
+ NULL, 0, KEY_WRITE, NULL, &hkeyPath, NULL );
+
+ if ( !err )
+ {
+ err = RegCreateKeyEx( hkeyPath, pszRegistryPortNames, 0,
+ NULL, 0, KEY_WRITE, NULL, &hkeyPortNames, NULL );
+
+ if ( !err )
+ {
+ err = RegSetValueEx( hkeyPortNames,
+ pPortName,
+ 0,
+ REG_SZ,
+ (LPBYTE) L"",
+ 0 );
+
+ RegCloseKey( hkeyPortNames );
+ }
+ else
+ {
+ KdPrint(("RegCreateKeyEx (%ws) failed: Error = %d\n",
+ pszRegistryPortNames, err ) );
+ }
+
+ RegCloseKey( hkeyPath );
+ }
+ else
+ {
+ KdPrint(("RegCreateKeyEx (%ws) failed: Error = %d\n",
+ pszRegistryPath, err ) );
+ }
+
+ if ( hToken )
+ ImpersonatePrinterClient(hToken);
+
+ return err;
+}
+
+
+
+DWORD
+DeleteRegistryEntry(
+ LPWSTR pPortName
+)
+{
+ DWORD err;
+ HANDLE hToken;
+ HKEY hkeyPath;
+ HKEY hkeyPortNames;
+
+ hToken = RevertToPrinterSelf();
+
+ err = RegOpenKeyEx( HKEY_LOCAL_MACHINE, pszRegistryPath, 0,
+ KEY_WRITE, &hkeyPath );
+
+ if ( !err )
+ {
+
+ err = RegOpenKeyEx( hkeyPath, pszRegistryPortNames, 0,
+ KEY_WRITE, &hkeyPortNames );
+
+ if ( !err )
+ {
+ err = RegDeleteValue( hkeyPortNames, pPortName );
+ RegCloseKey( hkeyPortNames );
+ }
+ else
+ {
+ KdPrint(("RegOpenKeyEx (%ws) failed: Error = %d\n",
+ pszRegistryPortNames, err ) );
+ }
+
+ RegCloseKey( hkeyPath );
+
+ }
+ else
+ {
+ KdPrint(("RegOpenKeyEx (%ws) failed: Error = %d\n",
+ pszRegistryPath, err ) );
+ }
+
+ if ( hToken )
+ ImpersonatePrinterClient(hToken);
+
+ return err;
+}
+
+
+
+HANDLE
+RevertToPrinterSelf(
+ VOID
+)
+{
+ HANDLE NewToken = NULL;
+ HANDLE OldToken;
+ NTSTATUS ntstatus;
+
+ ntstatus = NtOpenThreadToken(
+ NtCurrentThread(),
+ TOKEN_IMPERSONATE,
+ TRUE,
+ &OldToken
+ );
+
+ if ( !NT_SUCCESS(ntstatus) ) {
+ SetLastError(ntstatus);
+ return FALSE;
+ }
+
+ ntstatus = NtSetInformationThread(
+ NtCurrentThread(),
+ ThreadImpersonationToken,
+ (PVOID)&NewToken,
+ (ULONG)sizeof(HANDLE)
+ );
+
+ if ( !NT_SUCCESS(ntstatus) ) {
+ SetLastError(ntstatus);
+ return FALSE;
+ }
+
+ return OldToken;
+}
+
+
+
+BOOL
+ImpersonatePrinterClient(
+ HANDLE hToken
+)
+{
+ NTSTATUS ntstatus = NtSetInformationThread(
+ NtCurrentThread(),
+ ThreadImpersonationToken,
+ (PVOID) &hToken,
+ (ULONG) sizeof(HANDLE));
+
+ if ( !NT_SUCCESS(ntstatus) ) {
+ SetLastError( ntstatus );
+ return FALSE;
+ }
+
+ (VOID) NtClose(hToken);
+
+ return TRUE;
+}
diff --git a/private/nw/svcdlls/nwwks/client/print.ico b/private/nw/svcdlls/nwwks/client/print.ico
new file mode 100644
index 000000000..81ee083bc
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/print.ico
Binary files differ
diff --git a/private/nw/svcdlls/nwwks/client/provider.c b/private/nw/svcdlls/nwwks/client/provider.c
new file mode 100644
index 000000000..4f8bf2c30
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/provider.c
@@ -0,0 +1,3051 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ provider.c
+
+Abstract:
+
+ This module contains NetWare Network Provider code. It is the
+ client-side wrapper for APIs supported by the Workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 15-Feb-1993
+
+Revision History:
+
+ Yi-Hsin Sung (yihsins) 10-July-1993
+ Moved all dialog handling to nwdlg.c
+
+--*/
+
+#include <nwclient.h>
+#include <nwsnames.h>
+#include <nwcanon.h>
+#include <validc.h>
+#include <nwevent.h>
+#include <ntmsv1_0.h>
+#include <nwdlg.h>
+#include <nwreg.h>
+#include <nwauth.h>
+#include <mpr.h> // WNFMT_ manifests
+#include <nwmisc.h>
+
+#ifndef NT1057
+#include <nwutil.h>
+#endif
+
+//-------------------------------------------------------------------//
+// //
+// Local Function Prototypes //
+// //
+//-------------------------------------------------------------------//
+
+STATIC
+BOOL
+NwpWorkstationStarted(
+ VOID
+ );
+
+STATIC
+DWORD
+NwpMapNameToUNC(
+ IN LPWSTR pszName,
+ OUT LPWSTR *ppszUNC
+ );
+
+STATIC
+VOID
+NwpGetUncInfo(
+ IN LPWSTR lpstrUnc,
+ OUT WORD * slashCount,
+ OUT BOOL * isNdsUnc
+ );
+
+STATIC
+DWORD
+NwpGetUncObjectName(
+ IN LPWSTR ContainerName
+ );
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+#if DBG
+DWORD NwProviderTrace = 0;
+#endif
+
+
+DWORD
+APIENTRY
+NPGetCaps(
+ IN DWORD QueryVal
+ )
+/*++
+
+Routine Description:
+
+ This function returns the functionality supported by this network
+ provider.
+
+Arguments:
+
+ QueryVal - Supplies a value which determines the type of information
+ queried regarding the network provider's support in this area.
+
+Return Value:
+
+ Returns a value which indicates the level of support given by this
+ provider.
+
+--*/
+{
+
+#if DBG
+ IF_DEBUG(INIT) {
+ KdPrint(("\nNWPROVAU: NPGetCaps %lu\n", QueryVal));
+ }
+#endif
+
+ switch (QueryVal) {
+
+ case WNNC_SPEC_VERSION:
+ return 0x00040000;
+
+ case WNNC_NET_TYPE:
+ return WNNC_NET_NETWARE ;
+
+ case WNNC_USER:
+ return WNNC_USR_GETUSER;
+
+ case WNNC_CONNECTION:
+ return (WNNC_CON_ADDCONNECTION |
+ WNNC_CON_ADDCONNECTION3 |
+ WNNC_CON_CANCELCONNECTION |
+ WNNC_CON_GETPERFORMANCE |
+ WNNC_CON_GETCONNECTIONS);
+
+ case WNNC_ENUMERATION:
+ return ( WNNC_ENUM_GLOBAL |
+ WNNC_ENUM_CONTEXT |
+ WNNC_ENUM_LOCAL );
+
+ case WNNC_START:
+ if (NwpWorkstationStarted()) {
+ return 1;
+ }
+ else {
+ return 0xffffffff; // don't know
+ }
+
+ case WNNC_DIALOG:
+ return WNNC_DLG_FORMATNETWORKNAME
+#ifdef NT1057
+ ;
+#else
+ | WNNC_DLG_GETRESOURCEPARENT | WNNC_DLG_GETRESOURCEINFORMATION;
+#endif
+
+ //
+ // The rest are not supported by the NetWare provider
+ //
+ default:
+ return 0;
+ }
+
+}
+
+#define NW_EVENT_MESSAGE_FILE L"nwevent.dll"
+
+
+
+DWORD
+APIENTRY
+NPGetUser(
+ LPWSTR lpName,
+ LPWSTR lpUserName,
+ LPDWORD lpUserNameLen
+ )
+/*++
+
+Routine Description:
+
+ This is used to determine either the current default username, or the
+ username used to establish a network connection.
+
+Arguments:
+
+ lpName - Contains the name of the local device the caller is interested
+ in, or a network name that the user has made a connection to. This
+ may be NULL or the empty string if the caller is interested in the
+ name of the user currently logged on to the system. If a network
+ name is passed in, and the user is connected to that resource using
+ different names, it is possible that a provider cannont resolve
+ which username to return. In this case the provider may make an
+ arbitrary choice amonst the possible usernames.
+
+ lpUserName - Points to a buffer to receive the user name. this should
+ be a name that can be passed into the NPAddConnection or
+ NPAddConnection3 function to re-establish the connection with the
+ same user name.
+
+ lpBufferSize - This is used to specify the size (in characters) of the
+ buffer passed in. If the call fails because the buffer is not big
+ enough, this location will be used to return the required buffer size.
+
+Return Value:
+
+ WN_SUCCESS - If the call is successful. Otherwise, an error code is,
+ returned, which may include:
+
+ WN_NOT_CONNECTED - lpName not a redirected device nor a connected network
+ name.
+
+ WN_MORE_DATA - The buffer is too small.
+
+ WN_NO_NETWORK - Network not present.
+
+--*/
+{
+ DWORD status;
+ DWORD dwUserNameBufferSize = *lpUserNameLen * sizeof(WCHAR);
+ DWORD CharsRequired = 0;
+
+ if (lpName == NULL)
+ {
+ return WN_NOT_CONNECTED;
+ }
+
+ RtlZeroMemory( lpUserName, dwUserNameBufferSize );
+
+#if DBG
+ IF_DEBUG(CONNECT)
+ {
+ KdPrint(("\nNWPROVAU: NPGetUser %ws\n", lpName));
+ }
+#endif
+
+ RpcTryExcept
+ {
+ status = NwrGetUser(
+ NULL,
+ lpName,
+ (LPBYTE) lpUserName,
+ dwUserNameBufferSize,
+ &CharsRequired
+ );
+
+ if (status == WN_MORE_DATA)
+ {
+ //
+ // Output buffer too small.
+ //
+ *lpUserNameLen = CharsRequired;
+ }
+ }
+ RpcExcept(1)
+ {
+ status = NwpMapRpcError(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ if (status != NO_ERROR)
+ {
+ SetLastError(status);
+ }
+
+ return status;
+}
+
+
+DWORD
+APIENTRY
+NPAddConnection(
+ LPNETRESOURCEW lpNetResource,
+ LPWSTR lpPassword,
+ LPWSTR lpUserName
+ )
+/*++
+
+Routine Description:
+
+ This function creates a remote connection.
+
+Arguments:
+
+ lpNetResource - Supplies the NETRESOURCE structure which specifies
+ the local DOS device to map, the remote resource to connect to
+ and other attributes related to the connection.
+
+ lpPassword - Supplies the password to connect with.
+
+ lpUserName - Supplies the username to connect with.
+
+Return Value:
+
+ NO_ERROR - Successful.
+
+ WN_BAD_VALUE - Invalid value specifed in lpNetResource.
+
+ WN_BAD_NETNAME - Invalid remote resource name.
+
+ WN_BAD_LOCALNAME - Invalid local DOS device name.
+
+ WN_BAD_PASSWORD - Invalid password.
+
+ WN_ALREADY_CONNECTED - Local DOS device name is already in use.
+
+ Other network errors.
+
+--*/
+{
+ DWORD status = NO_ERROR;
+ LPWSTR pszRemoteName = NULL;
+
+ UCHAR EncodeSeed = NW_ENCODE_SEED3;
+ UNICODE_STRING PasswordStr;
+
+ LPWSTR CachedUserName = NULL ;
+ LPWSTR CachedPassword = NULL ;
+
+ PasswordStr.Length = 0;
+
+ status = NwpMapNameToUNC(
+ lpNetResource->lpRemoteName,
+ &pszRemoteName );
+
+ if (status != NO_ERROR)
+ {
+ SetLastError(status);
+ return status;
+ }
+
+#if DBG
+ IF_DEBUG(CONNECT) {
+ KdPrint(("\nNWPROVAU: NPAddConnection %ws\n", pszRemoteName));
+ }
+#endif
+
+ RpcTryExcept
+ {
+ if (lpNetResource->dwType != RESOURCETYPE_ANY &&
+ lpNetResource->dwType != RESOURCETYPE_DISK &&
+ lpNetResource->dwType != RESOURCETYPE_PRINT)
+ {
+ status = WN_BAD_VALUE;
+ }
+ else
+ {
+#ifdef NT1057
+ //
+ // no credentials specified, see if we have cached credentials
+ //
+ if (!lpPassword && !lpUserName)
+ {
+ (void) NwpRetrieveCachedCredentials(
+ pszRemoteName,
+ &CachedUserName,
+ &CachedPassword) ;
+
+ //
+ // these values will be NULL still if nothing found
+ //
+ lpPassword = CachedPassword ;
+ lpUserName = CachedUserName ;
+ }
+#endif
+
+ //
+ // Encode password.
+ //
+ RtlInitUnicodeString(&PasswordStr, lpPassword);
+ RtlRunEncodeUnicodeString(&EncodeSeed, &PasswordStr);
+
+ status = NwrCreateConnection(
+ NULL,
+ lpNetResource->lpLocalName,
+ pszRemoteName,
+ lpNetResource->dwType,
+ lpPassword,
+ lpUserName
+ );
+
+ if (CachedUserName)
+ {
+ (void)LocalFree((HLOCAL)CachedUserName);
+ }
+
+ if (CachedPassword)
+ {
+ RtlZeroMemory(CachedPassword,
+ wcslen(CachedPassword) *
+ sizeof(WCHAR)) ;
+ (void)LocalFree((HLOCAL)CachedPassword);
+ }
+ }
+ }
+ RpcExcept(1)
+ {
+ status = NwpMapRpcError(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ if (PasswordStr.Length != 0 && !CachedPassword)
+ {
+ //
+ // Restore password to original state
+ //
+ RtlRunDecodeUnicodeString(NW_ENCODE_SEED3, &PasswordStr);
+ }
+
+ if (status == ERROR_SHARING_PAUSED)
+ {
+ HMODULE MessageDll;
+ WCHAR Buffer[1024];
+ DWORD MessageLength;
+ DWORD err;
+ HKEY hkey;
+ LPWSTR pszProviderName = NULL;
+
+ //
+ // Load the netware message file DLL
+ //
+ MessageDll = LoadLibraryW(NW_EVENT_MESSAGE_FILE);
+
+ if (MessageDll == NULL)
+ {
+ goto ExitPoint ;
+ }
+
+ //
+ // Read the Network Provider Name.
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\networkprovider
+ //
+ err = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_PROVIDER_PATH,
+ REG_OPTION_NON_VOLATILE, // options
+ KEY_READ, // desired access
+ &hkey
+ );
+
+ if ( !err )
+ {
+ //
+ // ignore the return code. if fail, pszProviderName is NULL
+ //
+ err = NwReadRegValue(
+ hkey,
+ NW_PROVIDER_VALUENAME,
+ &pszProviderName // free with LocalFree
+ );
+
+ RegCloseKey( hkey );
+ }
+
+ if (err)
+ {
+ (void) FreeLibrary(MessageDll);
+ goto ExitPoint ;
+ }
+
+ RtlZeroMemory(Buffer, sizeof(Buffer)) ;
+
+ //
+ // Get string from message file
+ //
+ MessageLength = FormatMessageW(
+ FORMAT_MESSAGE_FROM_HMODULE,
+ (LPVOID) MessageDll,
+ NW_LOGIN_DISABLED,
+ 0,
+ Buffer,
+ sizeof(Buffer) / sizeof(WCHAR),
+ NULL
+ );
+
+ if (MessageLength != 0)
+ {
+ status = WN_EXTENDED_ERROR ;
+ WNetSetLastErrorW(NW_LOGIN_DISABLED,
+ Buffer,
+ pszProviderName) ;
+ }
+
+ (void) LocalFree( (HLOCAL) pszProviderName );
+ (void) FreeLibrary(MessageDll);
+
+ }
+
+ExitPoint:
+
+ if (status != NO_ERROR)
+ {
+ SetLastError(status);
+ }
+
+ LocalFree( (HLOCAL) pszRemoteName );
+ return status;
+}
+
+
+DWORD
+APIENTRY
+NPAddConnection3(
+ HWND hwndOwner,
+ LPNETRESOURCEW lpNetResource,
+ LPWSTR lpPassword,
+ LPWSTR lpUserName,
+ DWORD dwConnFlags
+ )
+/*++
+
+Routine Description:
+
+ This function creates a remote connection.
+
+Arguments:
+
+ hwndOwner - Owner window handle for dialog boxes
+
+ lpNetResource - Supplies the NETRESOURCE structure which specifies
+ the local DOS device to map, the remote resource to connect to
+ and other attributes related to the connection.
+
+ lpPassword - Supplies the password to connect with.
+
+ lpUserName - Supplies the username to connect with.
+
+ dwConnFlags - CONNECT_UPDATE_PROFILE...
+
+Return Value:
+
+ NO_ERROR - Successful.
+
+ WN_BAD_VALUE - Invalid value specifed in lpNetResource.
+
+ WN_BAD_NETNAME - Invalid remote resource name.
+
+ WN_BAD_LOCALNAME - Invalid local DOS device name.
+
+ WN_BAD_PASSWORD - Invalid password.
+
+ WN_ALREADY_CONNECTED - Local DOS device name is already in use.
+
+ Other network errors.
+
+--*/
+{
+ DWORD err = NO_ERROR;
+ LPWSTR UserName = NULL;
+ LPWSTR Password = NULL;
+
+ if ( ( dwConnFlags & CONNECT_PROMPT )
+ && !( dwConnFlags & CONNECT_INTERACTIVE )
+ )
+ {
+ return WN_BAD_VALUE;
+ }
+
+ if ( !(dwConnFlags & CONNECT_PROMPT ))
+ {
+ err = NPAddConnection( lpNetResource,
+ lpPassword,
+ lpUserName );
+
+ if ( ( err == NO_ERROR )
+ || !( dwConnFlags & CONNECT_INTERACTIVE ) // Cannot popup dialog
+ )
+ {
+ return err;
+ }
+ }
+
+ for (;;)
+ {
+ if ( ( err != NO_ERROR ) // CONNECT_PROMPT
+ && ( err != WN_BAD_PASSWORD )
+ && ( err != WN_ACCESS_DENIED )
+ && ( err != ERROR_NO_SUCH_USER )
+ )
+ {
+ // Errors not related to access problems
+ break;
+ }
+
+ if ( UserName )
+ {
+ (void) LocalFree( UserName );
+ UserName = NULL;
+ }
+
+ if ( Password )
+ {
+ memset( Password, 0, wcslen(Password) * sizeof(WCHAR));
+ (void) LocalFree( Password );
+ Password = NULL;
+ }
+
+ //
+ // Put up dialog to get username
+ // and password.
+ //
+ err = NwpGetUserCredential( hwndOwner,
+ lpNetResource->lpRemoteName,
+ err,
+ lpUserName,
+ &UserName,
+ &Password );
+
+ if ( err != NO_ERROR )
+ break;
+
+ err = NPAddConnection( lpNetResource,
+ Password,
+ UserName );
+
+ if ( err == NO_ERROR )
+ {
+#if 0
+ if ( (UserName != NULL) && (Password != NULL))
+ {
+ // Checking UserName and Password is to make sure that
+ // we have prompted for password
+ (VOID) NwpCacheCredentials( lpNetResource->lpRemoteName,
+ UserName,
+ Password ) ;
+ }
+#endif
+ break;
+ }
+ }
+
+ if ( UserName )
+ (void) LocalFree( UserName );
+
+ if ( Password )
+ {
+ memset( Password, 0, wcslen(Password) * sizeof(WCHAR));
+ (void) LocalFree( Password );
+ }
+
+ return err;
+}
+
+
+
+DWORD
+APIENTRY
+NPCancelConnection(
+ LPWSTR lpName,
+ BOOL fForce
+ )
+/*++
+
+Routine Description:
+
+ This function deletes a remote connection.
+
+Arguments:
+
+ lpName - Supplies the local DOS device, or the remote resource name
+ if it is a UNC connection to delete.
+
+ fForce - Supplies the force level to break the connection. TRUE means
+ to forcefully delete the connection, FALSE means end the connection
+ only if there are no opened files.
+
+Return Value:
+
+ NO_ERROR - Successful.
+
+ WN_BAD_NETNAME - Invalid remote resource name.
+
+ WN_NOT_CONNECTED - Connection could not be found.
+
+ WN_OPEN_FILES - fForce is FALSE and there are opened files on the
+ connection.
+
+ Other network errors.
+
+--*/
+{
+ DWORD status = NO_ERROR;
+ LPWSTR pszName = NULL;
+
+ //
+ // We only need to map remote resource name
+ //
+
+ if ( NwLibValidateLocalName( lpName ) != NO_ERROR )
+ {
+ status = NwpMapNameToUNC(
+ lpName,
+ &pszName
+ );
+
+ if (status != NO_ERROR) {
+ SetLastError(status);
+ return status;
+ }
+ }
+
+#if DBG
+ IF_DEBUG(CONNECT) {
+ KdPrint(("\nNWPROVAU: NPCancelConnection %ws, Force %u\n",
+ pszName? pszName : lpName, fForce));
+ }
+#endif
+
+ RpcTryExcept {
+
+ status = NwrDeleteConnection(
+ NULL,
+ pszName? pszName : lpName,
+ (DWORD) fForce
+ );
+
+ }
+ RpcExcept(1) {
+ status = NwpMapRpcError(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ if (status != NO_ERROR) {
+ SetLastError(status);
+ }
+
+ LocalFree( (HLOCAL) pszName );
+ return status;
+
+}
+
+
+
+DWORD
+APIENTRY
+NPGetConnection(
+ LPWSTR lpLocalName,
+ LPWSTR lpRemoteName,
+ LPDWORD lpnBufferLen
+ )
+/*++
+
+Routine Description:
+
+ This function returns the remote resource name for a given local
+ DOS device.
+
+Arguments:
+
+ lpLocalName - Supplies the local DOS device to look up.
+
+ lpRemoteName - Output buffer to receive the remote resource name
+ mapped to lpLocalName.
+
+ lpnBufferLen - On input, supplies length of the lpRemoteName buffer
+ in number of characters. On output, if error returned is
+ WN_MORE_DATA, receives the number of characters required of
+ the output buffer to hold the output string.
+
+Return Value:
+
+ NO_ERROR - Successful.
+
+ WN_BAD_LOCALNAME - Invalid local DOS device.
+
+ WN_NOT_CONNECTED - Connection could not be found.
+
+ WN_MORE_DATA - Output buffer is too small.
+
+ Other network errors.
+
+--*/
+{
+
+ DWORD status = NO_ERROR;
+ DWORD CharsRequired;
+
+#if DBG
+ IF_DEBUG(CONNECT) {
+ KdPrint(("\nNWPROVAU: NPGetConnection %ws\n", lpLocalName));
+ }
+#endif
+
+ RpcTryExcept {
+
+ if (lpRemoteName && *lpnBufferLen)
+ *lpRemoteName = 0 ;
+
+ status = NwrQueryServerResource(
+ NULL,
+ lpLocalName,
+ (*lpnBufferLen == 0? NULL : lpRemoteName),
+ *lpnBufferLen,
+ &CharsRequired
+ );
+
+ if (status == ERROR_INSUFFICIENT_BUFFER)
+ status = WN_MORE_DATA;
+
+ if (status == WN_MORE_DATA) {
+ *lpnBufferLen = CharsRequired;
+ }
+
+ }
+ RpcExcept(1) {
+ status = NwpMapRpcError(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ if (status != NO_ERROR) {
+ SetLastError(status);
+ }
+
+#if DBG
+ IF_DEBUG(CONNECT) {
+ KdPrint(("\nNWPROVAU: NPGetConnection returns %lu\n", status));
+ if (status == NO_ERROR) {
+ KdPrint((" %ws, BufferLen %lu, CharsRequired %lu\n", lpRemoteName, *lpnBufferLen, CharsRequired));
+
+ }
+ }
+#endif
+
+ return status;
+}
+
+
+DWORD
+APIENTRY
+NPGetConnectionPerformance(
+ LPCWSTR lpRemoteName,
+ LPNETCONNECTINFOSTRUCT lpNetConnectInfo
+ )
+/*++
+
+Routine Description:
+
+ This function returns information about the expected performance of a
+ connection used to access a network resource. The request can only be
+ for a network resource to which there is currently a connection.
+
+Arguments:
+
+ lpRemoteName - Contains the local name or remote name for a resource
+ for which a connection exists.
+
+ lpNetConnectInfo - This is a pointer to a NETCONNECTINFOSTRUCT structure
+ which is to be filled if the connection performance
+ of connection lpRemoteName can be determined.
+
+Return Value:
+
+ NO_ERROR - Successful.
+
+ WN_NOT_CONNECTED - Connection could not be found.
+
+ WN_NONETWORK - Network is not present.
+
+ Other network errors.
+
+--*/
+{
+ DWORD status = NO_ERROR;
+ LPWSTR pszRemoteName;
+
+ if ( lpNetConnectInfo == NULL )
+ {
+ status = ERROR_INVALID_PARAMETER;
+ SetLastError(status);
+ return status;
+ }
+
+ pszRemoteName = (LPWSTR) LocalAlloc( LMEM_ZEROINIT,
+ ( wcslen(lpRemoteName) + 1 ) *
+ sizeof(WCHAR) );
+
+ if ( pszRemoteName == NULL )
+ {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ SetLastError(status);
+ return status;
+ }
+
+ wcscpy( pszRemoteName, lpRemoteName );
+ _wcsupr( pszRemoteName );
+
+#if DBG
+ IF_DEBUG(CONNECT) {
+ KdPrint(("\nNWPROVAU: NPGetConnectionPerformance %ws\n", pszRemoteName));
+ }
+#endif
+
+ RpcTryExcept {
+
+ status = NwrGetConnectionPerformance(
+ NULL,
+ pszRemoteName,
+ (LPBYTE) lpNetConnectInfo,
+ sizeof(NETCONNECTINFOSTRUCT) );
+
+ }
+ RpcExcept(1)
+ {
+ status = NwpMapRpcError(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ if (status != NO_ERROR)
+ {
+ SetLastError(status);
+ }
+
+ LocalFree( (HLOCAL) pszRemoteName );
+ return status;
+}
+
+
+
+DWORD
+APIENTRY
+NPGetUniversalName(
+#ifdef NT1057
+ LPWSTR lpLocalPath,
+#else
+ LPCWSTR lpLocalPath,
+#endif
+ DWORD dwInfoLevel,
+ LPVOID lpBuffer,
+ LPDWORD lpBufferSize
+ )
+/*++
+
+Routine Description:
+
+ This function returns the universal resource name for a given local
+ path.
+
+Arguments:
+
+ lpLocalPath - Supplies the local DOS Path to look up.
+
+ dwInfoLevel - Info level requested.
+
+ lpBuffer - Output buffer to receive the appropruatye structure.
+
+ lpBufferLen - On input, supplies length of the buffer in number of
+ bytes. On output, if error returned is WN_MORE_DATA, receives
+ the number of bytes required of the output buffer.
+
+Return Value:
+
+ NO_ERROR - Successful.
+
+ WN_BAD_LOCALNAME - Invalid local DOS device.
+
+ WN_NOT_CONNECTED - Connection could not be found.
+
+ WN_MORE_DATA - Output buffer is too small.
+
+ Other network errors.
+
+--*/
+{
+
+ DWORD status = NO_ERROR;
+ DWORD dwCharsRequired = MAX_PATH + 1 ;
+ DWORD dwBytesNeeded ;
+ DWORD dwLocalLength ;
+ LPWSTR lpRemoteBuffer ;
+ WCHAR szDrive[3] ;
+
+ //
+ // check for bad info level
+ //
+ if ((dwInfoLevel != UNIVERSAL_NAME_INFO_LEVEL) &&
+ (dwInfoLevel != REMOTE_NAME_INFO_LEVEL))
+ {
+ return WN_BAD_VALUE ;
+ }
+
+ //
+ // check for bad pointers
+ //
+ if (!lpLocalPath || !lpBuffer || !lpBufferSize)
+ {
+ return WN_BAD_POINTER ;
+ }
+
+ //
+ // local path must at least have "X:"
+ //
+ if (((dwLocalLength = wcslen(lpLocalPath)) < 2) ||
+ (lpLocalPath[1] != L':') ||
+ ((dwLocalLength > 2) && (lpLocalPath[2] != L'\\')))
+ {
+ return WN_BAD_VALUE ;
+ }
+
+ //
+ // preallocate some memory
+ //
+ if (!(lpRemoteBuffer = (LPWSTR) LocalAlloc(
+ LPTR,
+ dwCharsRequired * sizeof(WCHAR))))
+ {
+ status = GetLastError() ;
+ goto ErrorExit ;
+ }
+
+ szDrive[2] = 0 ;
+ wcsncpy(szDrive, lpLocalPath, 2) ;
+
+ //
+ // get the remote path by calling the existing API
+ //
+ status = NPGetConnection(
+ szDrive,
+ lpRemoteBuffer,
+ &dwCharsRequired) ;
+
+ if (status == WN_MORE_DATA)
+ {
+ //
+ // reallocate the correct size
+ //
+
+ if (!(lpRemoteBuffer = (LPWSTR) LocalReAlloc(
+ (HLOCAL) lpRemoteBuffer,
+ dwCharsRequired * sizeof(WCHAR),
+ LMEM_MOVEABLE)))
+ {
+ status = GetLastError() ;
+ goto ErrorExit ;
+ }
+
+ status = NPGetConnection(
+ szDrive,
+ lpRemoteBuffer,
+ &dwCharsRequired) ;
+ }
+
+ if (status != WN_SUCCESS)
+ {
+ goto ErrorExit ;
+ }
+
+ //
+ // at minimum we will need this size of the UNC name
+ // the -2 is because we loose the drive letter & colon.
+ //
+ dwBytesNeeded = (wcslen(lpRemoteBuffer) +
+ dwLocalLength - 2 + 1) * sizeof(WCHAR) ;
+
+ switch (dwInfoLevel)
+ {
+ case UNIVERSAL_NAME_INFO_LEVEL:
+ {
+ LPUNIVERSAL_NAME_INFO lpUniversalNameInfo ;
+
+ //
+ // calculate how many bytes we really need
+ //
+ dwBytesNeeded += sizeof(UNIVERSAL_NAME_INFO) ;
+
+ if (*lpBufferSize < dwBytesNeeded)
+ {
+ *lpBufferSize = dwBytesNeeded ;
+ status = WN_MORE_DATA ;
+ break ;
+ }
+
+ //
+ // now we are all set. just stick the data in the buffer
+ //
+ lpUniversalNameInfo = (LPUNIVERSAL_NAME_INFO) lpBuffer ;
+
+ lpUniversalNameInfo->lpUniversalName = (LPWSTR)
+ (((LPBYTE)lpBuffer) + sizeof(UNIVERSAL_NAME_INFO)) ;
+ wcscpy(lpUniversalNameInfo->lpUniversalName,
+ lpRemoteBuffer) ;
+ wcscat(lpUniversalNameInfo->lpUniversalName,
+ lpLocalPath+2) ;
+
+ break ;
+ }
+
+ case REMOTE_NAME_INFO_LEVEL :
+ {
+ LPREMOTE_NAME_INFO lpRemoteNameInfo ;
+
+ //
+ // calculate how many bytes we really need
+ //
+ dwBytesNeeded *= 2 ; // essentially twice the info + terminator
+ dwBytesNeeded += (sizeof(REMOTE_NAME_INFO) + sizeof(WCHAR)) ;
+
+ if (*lpBufferSize < dwBytesNeeded)
+ {
+ *lpBufferSize = dwBytesNeeded ;
+ status = WN_MORE_DATA ;
+ break ;
+ }
+
+ //
+ // now we are all set. just stick the data in the buffer
+ //
+ lpRemoteNameInfo = (LPREMOTE_NAME_INFO) lpBuffer ;
+
+ lpRemoteNameInfo->lpUniversalName = (LPWSTR)
+ (((LPBYTE)lpBuffer) + sizeof(REMOTE_NAME_INFO)) ;
+ wcscpy(lpRemoteNameInfo->lpUniversalName,
+ lpRemoteBuffer) ;
+ wcscat(lpRemoteNameInfo->lpUniversalName,
+ lpLocalPath+2) ;
+
+ lpRemoteNameInfo->lpConnectionName =
+ lpRemoteNameInfo->lpUniversalName +
+ wcslen(lpRemoteNameInfo->lpUniversalName) + 1 ;
+ wcscpy(lpRemoteNameInfo->lpConnectionName,
+ lpRemoteBuffer) ;
+
+ lpRemoteNameInfo->lpRemainingPath =
+ lpRemoteNameInfo->lpConnectionName +
+ wcslen(lpRemoteNameInfo->lpConnectionName) + 1 ;
+ wcscpy(lpRemoteNameInfo->lpRemainingPath,
+ lpLocalPath+2) ;
+
+ break ;
+ }
+
+ default:
+ //
+ // yikes!
+ //
+ status = WN_BAD_VALUE ;
+ ASSERT(FALSE);
+ }
+
+ErrorExit:
+
+ if (lpRemoteBuffer)
+ {
+ (void) LocalFree((HLOCAL)lpRemoteBuffer) ;
+ }
+ return status;
+}
+
+
+
+DWORD
+APIENTRY
+NPOpenEnum(
+ DWORD dwScope,
+ DWORD dwType,
+ DWORD dwUsage,
+ LPNETRESOURCEW lpNetResource,
+ LPHANDLE lphEnum
+ )
+/*++
+
+Routine Description:
+
+ This function initiates an enumeration of either connections, or
+ browsing of network resource.
+
+Arguments:
+
+ dwScope - Supplies the category of enumeration to do--either
+ connection or network browsing.
+
+ dwType - Supplies the type of resource to get--either disk,
+ print, or it does not matter.
+
+ dwUsage - Supplies the object type to get--either container,
+ or connectable usage.
+
+ lpNetResource - Supplies, in the lpRemoteName field, the container
+ name to enumerate under.
+
+ lphEnum - Receives the resumable context handle to be used on all
+ subsequent calls to get the list of objects under the container.
+
+Return Value:
+
+ NO_ERROR - Successful.
+
+ WN_BAD_VALUE - Either the dwScope, dwType, or the dwUsage specified
+ is not acceptable.
+
+ WN_BAD_NETNAME - Invalid remote resource name.
+
+ WN_NOT_CONTAINER - Remote resource name is not a container.
+
+ Other network errors.
+
+--*/
+{
+ DWORD status = NO_ERROR;
+
+#if DBG
+ IF_DEBUG(ENUM)
+ {
+ KdPrint(("\nNWPROVAU: NPOpenEnum\n"));
+ }
+#endif
+
+
+ RpcTryExcept
+ {
+ if ( ( dwType & RESOURCETYPE_DISK ) ||
+ ( dwType & RESOURCETYPE_PRINT ) ||
+ ( dwType == RESOURCETYPE_ANY ) )
+ {
+ switch ( dwScope )
+ {
+ case RESOURCE_CONNECTED:
+
+ status = NwrOpenEnumConnections( NULL,
+ dwType,
+ (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
+ break;
+
+ case RESOURCE_CONTEXT:
+
+ status = NwrOpenEnumContextInfo( NULL,
+ dwType,
+ (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
+ break;
+
+ case RESOURCE_GLOBALNET:
+
+ if ( lpNetResource == NULL )
+ {
+ //
+ // Enumerating servers and NDS trees
+ //
+ if ( dwUsage & RESOURCEUSAGE_CONTAINER || dwUsage == 0 )
+ {
+ status = NwrOpenEnumServersAndNdsTrees( NULL,
+ (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
+ }
+ else
+ {
+ //
+ // There is no such thing as a connectable server
+ // object.
+ //
+ status = WN_BAD_VALUE;
+ }
+ }
+ else
+ {
+ BOOL IsEnumVolumes = TRUE;
+ LPWSTR pszRemoteName = NULL;
+ WORD slashCount;
+ BOOL isNdsUnc;
+
+ NwpGetUncInfo( lpNetResource->lpRemoteName,
+ &slashCount,
+ &isNdsUnc );
+
+ //
+ // Either enumerating volumes, directories, or NDS subtrees
+ //
+
+ if ( dwUsage & RESOURCEUSAGE_CONNECTABLE ||
+ dwUsage & RESOURCEUSAGE_CONTAINER ||
+ dwUsage == 0 )
+ {
+ LPWSTR tempStrPtr = lpNetResource->lpRemoteName;
+ DWORD dwClassType = 0;
+
+ if ( tempStrPtr[0] == L' ' &&
+ tempStrPtr[1] == L'\\' &&
+ tempStrPtr[2] == L'\\' )
+ tempStrPtr = &tempStrPtr[1];
+
+ if ( lpNetResource->dwDisplayType == RESOURCEDISPLAYTYPE_TREE )
+ {
+ if ( ( dwType == RESOURCETYPE_ANY ) ||
+ ( ( dwType & RESOURCETYPE_DISK ) &&
+ ( dwType & RESOURCETYPE_PRINT ) ) )
+ {
+ status = NwrOpenEnumNdsSubTrees_Any( NULL,
+ tempStrPtr,
+ NULL,
+ (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
+ }
+ else if ( dwType & RESOURCETYPE_DISK )
+ {
+ status = NwrOpenEnumNdsSubTrees_Disk( NULL,
+ tempStrPtr,
+ NULL,
+ (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
+ }
+ else if ( dwType & RESOURCETYPE_PRINT )
+ {
+ status = NwrOpenEnumNdsSubTrees_Print( NULL,
+ tempStrPtr,
+ NULL,
+ (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
+ }
+ else
+ {
+ KdPrint(("NWOpenEnum: Unhandled dwType %lu\n", dwType));
+ }
+ }
+ else if (
+ ( slashCount < 4 ) &&
+ ( ( dwType == RESOURCETYPE_ANY ) ||
+ ( ( dwType & RESOURCETYPE_DISK ) &&
+ ( dwType & RESOURCETYPE_PRINT ) ) ) &&
+ ( ( status = NwrOpenEnumNdsSubTrees_Any( NULL,
+ tempStrPtr,
+ &dwClassType,
+ (LPNWWKSTA_CONTEXT_HANDLE) lphEnum ) )
+ ==NO_ERROR )
+ )
+ {
+ status = NO_ERROR;
+ }
+ else if (
+ ( slashCount < 4 ) &&
+ ( dwType & RESOURCETYPE_DISK ) &&
+ ( ( status = NwrOpenEnumNdsSubTrees_Disk( NULL,
+ tempStrPtr,
+ &dwClassType,
+ (LPNWWKSTA_CONTEXT_HANDLE) lphEnum ) )
+ ==NO_ERROR )
+ )
+ {
+ status = NO_ERROR;
+ }
+ else if (
+ ( slashCount < 4 ) &&
+ ( dwType & RESOURCETYPE_PRINT ) &&
+ ( ( status = NwrOpenEnumNdsSubTrees_Print( NULL,
+ tempStrPtr,
+ &dwClassType,
+ (LPNWWKSTA_CONTEXT_HANDLE) lphEnum ) )
+ ==NO_ERROR )
+ )
+ {
+ status = NO_ERROR;
+ }
+ else if (
+ (slashCount < 4
+ &&
+ (status == ERROR_NETWORK_ACCESS_DENIED
+ ||
+ status == ERROR_GEN_FAILURE
+ ||
+ status == ERROR_ACCESS_DENIED
+ ||
+ status == ERROR_BAD_NETPATH
+ ||
+ status == WN_BAD_NETNAME
+ ||
+ status == ERROR_INVALID_NAME))
+ ||
+ ( slashCount > 3 && status == NO_ERROR )
+ )
+ {
+ if (( status == ERROR_NETWORK_ACCESS_DENIED ) &&
+ ( dwClassType == CLASS_TYPE_NCP_SERVER ))
+ {
+ status = NO_ERROR;
+ isNdsUnc = TRUE;
+ IsEnumVolumes = TRUE;
+ }
+ else if ( ( status == ERROR_NETWORK_ACCESS_DENIED ) &&
+ ( ( dwClassType == CLASS_TYPE_VOLUME ) ||
+ ( dwClassType == CLASS_TYPE_DIRECTORY_MAP ) ) )
+ {
+ status = NO_ERROR;
+ isNdsUnc = TRUE;
+ IsEnumVolumes = FALSE;
+ }
+ else
+ {
+ //
+ // A third backslash means that we want to
+ // enumerate the directories.
+ //
+
+ if ( isNdsUnc && slashCount > 3 )
+ IsEnumVolumes = FALSE;
+
+ if ( !isNdsUnc && slashCount > 2 )
+ IsEnumVolumes = FALSE;
+
+ if ( lpNetResource->dwDisplayType == RESOURCEDISPLAYTYPE_SHARE )
+ IsEnumVolumes = FALSE;
+ }
+
+ status = NwpMapNameToUNC( tempStrPtr,
+ &pszRemoteName );
+
+ if ( status == NO_ERROR )
+ {
+ if ( IsEnumVolumes )
+ {
+ LPWSTR pszServerName = pszRemoteName;
+
+ // BUGBUG - The following 10 lines are a hack to
+ // allow the provider to browse past the CN=<server>
+ // object in an NDS tree.
+ if ( slashCount == 3 && isNdsUnc == TRUE )
+ {
+ pszServerName = (LPWSTR)
+ NwpGetUncObjectName( pszRemoteName );
+
+ if ( pszServerName == NULL )
+ pszServerName = pszRemoteName;
+ }
+ else if ( dwUsage & RESOURCEUSAGE_ATTACHED )
+ {
+#ifndef NT1057
+ // This is a bindery server.
+ // Return WN_NOT_AUTHENTICATED if
+ // we are not already attached so
+ // that clients ( explorer ) will
+ // do NPAddConnection3 to make
+ // a connection to the server.
+ BOOL fAttached;
+ BOOL fAuthenticated;
+
+ status = NwIsServerOrTreeAttached(
+ pszServerName + 2,
+ &fAttached,
+ &fAuthenticated );
+
+ if ( status != NO_ERROR )
+ break;
+
+ if ( !fAttached || !fAuthenticated)
+ {
+ // See if the server belongs to
+ // our provider.
+ status = NwrOpenEnumVolumes(
+ NULL,
+ pszServerName,
+ (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
+
+ if ( status == NO_ERROR )
+ {
+ // The server belongs to us.
+ // Close the handle and
+ // return not attached if
+ // callee passed in dwUsage
+ // flag:
+ // RESOURCEUSAGE_ATTACHED.
+ // Note: handle will be null
+ // after return from
+ // NwrCloseEnum
+
+ NwrCloseEnum( (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
+
+ status = WN_NOT_AUTHENTICATED;
+ }
+ else
+ {
+ // else the server does not
+ // belong to us.
+ status = WN_BAD_NETNAME;
+ }
+ break;
+ }
+#endif
+ } // else, this is a bindery server and
+ // client does not care whether we
+ // are bindery authenticated.
+
+ if ( ( dwType == RESOURCETYPE_ANY ) ||
+ ( ( dwType & RESOURCETYPE_DISK ) &&
+ ( dwType & RESOURCETYPE_PRINT ) ) )
+ {
+ status = NwrOpenEnumVolumesQueues(
+ NULL,
+ pszServerName,
+ (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
+ }
+ else if ( dwType & RESOURCETYPE_DISK )
+ {
+ status = NwrOpenEnumVolumes(
+ NULL,
+ pszServerName,
+ (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
+ }
+ else if ( dwType & RESOURCETYPE_PRINT )
+ {
+ status = NwrOpenEnumQueues(
+ NULL,
+ pszServerName,
+ (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
+ }
+ }
+ else
+ {
+ LPWSTR CachedUserName = NULL ;
+ LPWSTR CachedPassword = NULL ;
+
+#ifdef NT1057 // Make OpenEnum not interactive on SUR
+ (void) NwpRetrieveCachedCredentials( pszRemoteName,
+ &CachedUserName,
+ &CachedPassword );
+
+#endif
+ status = NwrOpenEnumDirectories(
+ NULL,
+ pszRemoteName,
+ CachedUserName,
+ CachedPassword,
+ (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
+
+#ifndef NT1057 // Make OpenEnum not interactive on SUR
+ if ( (status == ERROR_INVALID_PASSWORD)
+ || (status == ERROR_NO_SUCH_USER )
+ )
+ {
+ status = WN_NOT_AUTHENTICATED;
+ break;
+ }
+
+#else
+ if ( CachedUserName )
+ {
+ (void) LocalFree( (HLOCAL) CachedUserName );
+ }
+
+ if ( CachedPassword )
+ {
+ RtlZeroMemory( CachedPassword,
+ wcslen(CachedPassword) *
+ sizeof( WCHAR ) );
+
+ (void) LocalFree( ( HLOCAL ) CachedPassword );
+ }
+
+ if ( ( status == ERROR_INVALID_PASSWORD ) ||
+ ( status == ERROR_NO_SUCH_USER ) )
+ {
+ LPWSTR UserName;
+ LPWSTR Password;
+ LPWSTR TmpPtr;
+
+ //
+ // Put up dialog to get username
+ // and password.
+ //
+ status = NwpGetUserCredential( NULL,
+ tempStrPtr,
+ status,
+ NULL,
+ &UserName,
+ &Password);
+
+ if ( status == NO_ERROR )
+ {
+ status = NwrOpenEnumDirectories(
+ NULL,
+ pszRemoteName,
+ UserName,
+ Password,
+ (LPNWWKSTA_CONTEXT_HANDLE) lphEnum );
+
+ if ( status == NO_ERROR )
+ {
+ status = NwpCacheCredentials(
+ pszRemoteName,
+ UserName,
+ Password ) ;
+ }
+
+ (void) LocalFree( UserName );
+
+ //
+ // Clear the password
+ //
+ TmpPtr = Password;
+ while ( *TmpPtr != 0 )
+ *TmpPtr++ = 0;
+
+ (void) LocalFree( Password );
+ }
+ else if ( status == ERROR_WINDOW_NOT_DIALOG )
+ {
+ //
+ // Caller is not a GUI app.
+ //
+ status = ERROR_INVALID_PASSWORD;
+ }
+ else if ( status == WN_CANCEL )
+ {
+ //
+ // Cancel was pressed but we still
+ // have to return success or MPR
+ // will popup the error. Return
+ // a bogus enum handle.
+ //
+ *lphEnum = (HANDLE) 0xFFFFFFFF;
+ status = NO_ERROR;
+ }
+ }
+#endif
+ }
+ }
+ else
+ {
+ status = WN_BAD_NETNAME;
+ }
+ }
+ }
+ else
+ {
+ status = WN_BAD_VALUE;
+ }
+
+ if ( pszRemoteName != NULL )
+ LocalFree( (HLOCAL) pszRemoteName );
+ }
+
+ break;
+
+ default:
+ KdPrint(("NWPROVIDER: Invalid dwScope %lu\n", dwScope));
+ status = WN_BAD_VALUE;
+ } // end switch
+ }
+ else
+ {
+ status = WN_BAD_VALUE;
+ }
+ }
+ RpcExcept( 1 )
+ {
+ status = NwpMapRpcError(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ if ( status == ERROR_FILE_NOT_FOUND )
+ status = WN_BAD_NETNAME;
+
+ if ( status != NO_ERROR )
+ {
+ SetLastError( status );
+ }
+
+ return status;
+}
+
+
+DWORD
+APIENTRY
+NPEnumResource(
+ HANDLE hEnum,
+ LPDWORD lpcCount,
+ LPVOID lpBuffer,
+ LPDWORD lpBufferSize
+ )
+/*++
+
+Routine Description:
+
+ This function returns a lists of objects within the container
+ specified by the enumeration context handle.
+
+Arguments:
+
+ hEnum - Supplies the resumable enumeration context handle.
+
+ NOTE: If this value is 0xFFFFFFFF, it is not a context
+ handle and this routine is required to return
+ WN_NO_MORE_ENTRIES. This hack is to handle the
+ case where the user cancelled out of the network
+ credential dialog on NwrOpenEnumDirectories and we
+ cannot return an error there or we generate an error
+ popup.
+
+ lpcCount - On input, supplies the number of entries to get.
+ On output, if NO_ERROR is returned, receives the number
+ of entries NETRESOURCE returned in lpBuffer.
+
+ lpBuffer - Receives an array of NETRESOURCE entries, each
+ entry describes an object within the container.
+
+ lpBufferSize - On input, supplies the size of lpBuffer in
+ bytes. On output, if WN_MORE_DATA is returned, receives
+ the number of bytes needed in the buffer to get the
+ next entry.
+
+Return Value:
+
+
+ NO_ERROR - Successfully returned at least one entry.
+
+ WN_NO_MORE_ENTRIES - Reached the end of enumeration and nothing
+ is returned.
+
+ WN_MORE_DATA - lpBuffer is too small to even get one entry.
+
+ WN_BAD_HANDLE - The enumeration handle is invalid.
+
+ Other network errors.
+
+--*/
+{
+ DWORD status = NO_ERROR;
+ DWORD BytesNeeded = 0;
+ DWORD EntriesRead = 0;
+
+#if DBG
+ IF_DEBUG(ENUM) {
+ KdPrint(("\nNWPROVAU: NPEnumResource\n"));
+ }
+#endif
+
+ RpcTryExcept {
+
+ if (hEnum == (HANDLE) 0xFFFFFFFF) {
+ status = WN_NO_MORE_ENTRIES;
+ goto EndOfTry;
+ }
+
+ status = NwrEnum(
+ (NWWKSTA_CONTEXT_HANDLE) hEnum,
+ *lpcCount,
+ (LPBYTE) lpBuffer,
+ *lpBufferSize,
+ &BytesNeeded,
+ &EntriesRead
+ );
+
+ if (status == WN_MORE_DATA) {
+
+ //
+ // Output buffer too small to fit a single entry.
+ //
+ *lpBufferSize = BytesNeeded;
+ }
+ else if (status == NO_ERROR) {
+ *lpcCount = EntriesRead;
+ }
+
+EndOfTry: ;
+
+ }
+ RpcExcept(1) {
+ status = NwpMapRpcError(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ if (status != NO_ERROR && status != WN_NO_MORE_ENTRIES) {
+ SetLastError(status);
+ }
+ else
+ {
+
+ //
+ // Convert offsets of strings to pointers
+ //
+ if (EntriesRead > 0) {
+
+ DWORD i;
+ LPNETRESOURCEW NetR;
+
+
+ NetR = lpBuffer;
+
+ for (i = 0; i < EntriesRead; i++, NetR++) {
+
+ if (NetR->lpLocalName != NULL) {
+ NetR->lpLocalName = (LPWSTR) ((DWORD) lpBuffer +
+ (DWORD) NetR->lpLocalName);
+ }
+
+ NetR->lpRemoteName = (LPWSTR) ((DWORD) lpBuffer +
+ (DWORD) NetR->lpRemoteName);
+
+ if (NetR->lpComment != NULL) {
+ NetR->lpComment = (LPWSTR) ((DWORD) lpBuffer +
+ (DWORD) NetR->lpComment);
+ }
+
+ if (NetR->lpProvider != NULL) {
+ NetR->lpProvider = (LPWSTR) ((DWORD) lpBuffer +
+ (DWORD) NetR->lpProvider);
+ }
+ }
+ }
+ }
+
+ return status;
+}
+
+
+DWORD
+APIENTRY
+NPGetResourceInformation(
+ LPNETRESOURCEW lpNetResource,
+ LPVOID lpBuffer,
+ LPDWORD cbBuffer,
+ LPWSTR * lplpSystem
+ )
+/*++
+
+Routine Description:
+
+ This function returns an object which details information
+ about a specified network resource.
+
+Arguments:
+
+ lpNetResource - This specifies the network resource for which the
+ information is required. The lpRemoteName field of the NETRESOURCE
+ specifies the remote name of the network resource whose information
+ is required. If the calling program knows the values for the
+ lpProvider and dwType fields, then it should fill them in, otherwise,
+ it should set them to NULL. All other fields in the NETRESOURCE are
+ ignored and are not initialized.
+
+ lpBuffer - A pointer to the buffer to receive the result, which is
+ returned as a single NETRESOURCE entry representing the parent
+ resource. The lpRemoteName, lpProvider, dwType, and dwUsage fields
+ are returned, all other fields being set to NULL. The remote name
+ returned should be in the same syntax as that returned from an
+ enumeration, so that the caller can do a case sensitive string
+ compare to determine whether an enumerated resource is this resource.
+ If the provider owns a parent of the network resource, (in other
+ words is known to be the correct network to respond to this request),
+ then lpProvider should be filled in with a non-null entry. If it is
+ known that a network owns a parent of the resource, but that the
+ resource itself is not valid, then lpProvider is returned as a
+ non-null value together with a return status of WN_BAD_VALUE. dwScope
+ is returned as RESOURCE_CONTEXT if the network resource is part of
+ the user's network context, otherwise it is returned as zero.
+
+ cbBuffer - This specifies the size in bytes of the buffer passed to the
+ function call. If the result is WN_MORE_DATA, this will contain the
+ buffer size required (in bytes) to hold the NETRESOURCE information.
+
+ lplpSystem - Returned pointer to a string in the buffer pointed to by
+ lpBuffer that specifies the part of the resource that is accessed
+ through resource type specific system APIs rather than WNet APIs.
+ For example, if the input remote resource name was
+ "\\server\share\dir", then lpRemoteName is returned pointing to
+ "\\server\share" and lplpSystem points to "\dir", both strings
+ being stored in the buffer pointed to by lpBuffer.
+
+Return Value:
+
+
+ WN_SUCCESS - If the call is successful.
+
+ WN_MORE_DATA - If input buffer is too small.
+
+ WN_BAD_VALUE - Invalid dwScope or dwUsage or dwType, or bad combination
+ of parameters is specified (e.g. lpRemoteName does not correspond
+ to dwType).
+
+ WN_BAD_NETNAME - The resource is not recognized by this provider.
+
+--*/
+{
+ DWORD status;
+ LPWSTR pszRemoteName = NULL;
+ DWORD BytesNeeded = 0;
+ DWORD SystemOffset = 0;
+
+ *lplpSystem = NULL;
+
+ status = NwpMapNameToUNC( lpNetResource->lpRemoteName, &pszRemoteName );
+
+ if (status != NO_ERROR)
+ {
+ SetLastError(status);
+ return status;
+ }
+
+#if DBG
+ IF_DEBUG(CONNECT)
+ {
+ KdPrint(("\nNWPROVAU: NPGetResourceInformation %ws\n", pszRemoteName));
+ }
+#endif
+
+ RpcTryExcept
+ {
+ if (lpNetResource->dwType != RESOURCETYPE_ANY &&
+ lpNetResource->dwType != RESOURCETYPE_DISK &&
+ lpNetResource->dwType != RESOURCETYPE_PRINT)
+ {
+ status = WN_BAD_VALUE;
+ }
+ else
+ {
+ status = NwrGetResourceInformation(
+ NULL,
+ pszRemoteName,
+ lpNetResource->dwType,
+ (LPBYTE) lpBuffer,
+ *cbBuffer,
+ &BytesNeeded,
+ &SystemOffset
+ );
+
+ if (status == WN_MORE_DATA)
+ {
+ //
+ // Output buffer too small.
+ //
+ *cbBuffer = BytesNeeded;
+ }
+ }
+ }
+ RpcExcept(1)
+ {
+ status = NwpMapRpcError(RpcExceptionCode());
+ }
+ RpcEndExcept
+ if ( pszRemoteName )
+ LocalFree( (HLOCAL) pszRemoteName );
+
+ if (status != NO_ERROR)
+ {
+ SetLastError(status);
+ }
+ else
+ {
+ //
+ // Convert offsets of strings to pointers
+ //
+ DWORD i;
+ LPNETRESOURCEW NetR = lpBuffer;
+
+ if (NetR->lpLocalName != NULL)
+ {
+ NetR->lpLocalName = (LPWSTR) ((DWORD) lpBuffer +
+ (DWORD) NetR->lpLocalName);
+ }
+
+ NetR->lpRemoteName = (LPWSTR) ((DWORD) lpBuffer +
+ (DWORD) NetR->lpRemoteName);
+
+ if (NetR->lpComment != NULL)
+ {
+ NetR->lpComment = (LPWSTR) ((DWORD) lpBuffer +
+ (DWORD) NetR->lpComment);
+ }
+
+ if (NetR->lpProvider != NULL)
+ {
+ NetR->lpProvider = (LPWSTR) ((DWORD) lpBuffer +
+ (DWORD) NetR->lpProvider);
+ }
+
+ if (SystemOffset != 0)
+ {
+ *lplpSystem = (LPWSTR) ((DWORD) lpBuffer + SystemOffset);
+ }
+ }
+
+ return status;
+}
+
+
+
+DWORD
+APIENTRY
+NPGetResourceParent(
+ LPNETRESOURCEW lpNetResource,
+ LPVOID lpBuffer,
+ LPDWORD cbBuffer
+ )
+/*++
+
+Routine Description:
+
+ This function returns an object which details information
+ about the parent of a specified network resource.
+
+Arguments:
+
+ lpNetResource - This specifies the network resource for which the
+ parent name is required. The NETRESOURCE could have been obtained via
+ previous NPEnumResource, or constructed by the caller. The lpRemoteName
+ field of the NETRESOURCE specifies the remote name of the network
+ resouce whose parent name is required. If the calling program knows
+ the values for the lpProvider and dwType fields, then it can fill
+ them in, otherwise, they are set to NULL. If the lpProvider field is
+ not NULL, then the network provider DLL can assume that the resource
+ is owned by its network, but if it is NULL, then it must assume
+ that the resource could be for some other network and do whatever
+ checking is neccessary to ensure that the result returned is accurate.
+ For example, if being asked for the parent of a server, and the server
+ is not part of a workgroup, the the network provider DLL should check
+ to ensure that the server is part of its network and, if so, return
+ its provider name. All other fields in the NETRESOURCE are ignored and
+ are not initialized.
+
+ lpBuffer - A pointer to the buffer to receive the result, which is
+ returned as a single NETRESOURCE entry representing the parent
+ resource. The lpRemoteName, lpProvider, dwType, and dwUsage fields
+ are returned, all other fields being set to NULL. lpProvider should
+ be set to NULL if the provider has only done a syntactic check (i.e.
+ does not know that the resource is specific to its network). If the
+ provider owns a parent of the network resource, (in other words is
+ known to be the correct network to respond to this request), then
+ lpProvider should be filled in with a non-null entry, even if the
+ return is WN_BAD_VALUE. The remote name returned should be in the
+ same syntax as that returned from an enumeration, so that the caller
+ can do a case sensitive string compare to determine whether an
+ enumerated resource is this resource. If a resource has no browse
+ parent on the network, the lpRemoteName is returned as NULL. The
+ RESOURCEUSAGE_CONNECTABLE value in the dwUsage field does not
+ indicate that the resource can currently be connected to, but that
+ the resource is connectable when it is available on the network.
+
+ cbBuffer - This specifies the size in bytes of the buffer passed to the
+ function call. If the result is WN_MORE_DATA, this will contain the
+ buffer size required (in bytes) to hold the NETRESOURCE information.
+
+Return Value:
+
+ WN_SUCCESS - If the call is successful.
+
+ WN_MORE_DATA - If input buffer is too small.
+
+ WN_BAD_VALUE - Invalid dwScope or dwUsage or dwType, or bad combination
+ of parameters is specified (e.g. lpRemoteName does not correspond
+ to dwType).
+
+--*/
+{
+ DWORD status;
+ LPWSTR pszRemoteName = NULL;
+ DWORD BytesNeeded = 0;
+
+ status = NwpMapNameToUNC( lpNetResource->lpRemoteName, &pszRemoteName );
+
+ if (status != NO_ERROR)
+ {
+ SetLastError(status);
+ return status;
+ }
+
+#if DBG
+ IF_DEBUG(CONNECT)
+ {
+ KdPrint(("\nNWPROVAU: NPGetResourceParent %ws\n", pszRemoteName));
+ }
+#endif
+
+ RpcTryExcept
+ {
+ if (lpNetResource->dwType != RESOURCETYPE_ANY &&
+ lpNetResource->dwType != RESOURCETYPE_DISK &&
+ lpNetResource->dwType != RESOURCETYPE_PRINT)
+ {
+ status = WN_BAD_VALUE;
+ }
+ else
+ {
+ status = NwrGetResourceParent(
+ NULL,
+ pszRemoteName,
+ lpNetResource->dwType,
+ (LPBYTE) lpBuffer,
+ *cbBuffer,
+ &BytesNeeded
+ );
+
+ if (status == WN_MORE_DATA)
+ {
+ //
+ // Output buffer too small.
+ //
+ *cbBuffer = BytesNeeded;
+ }
+ }
+ }
+ RpcExcept(1)
+ {
+ status = NwpMapRpcError(RpcExceptionCode());
+ }
+ RpcEndExcept
+ if ( pszRemoteName )
+ LocalFree( (HLOCAL) pszRemoteName );
+
+
+ if (status != NO_ERROR)
+ {
+ SetLastError(status);
+ }
+ else
+ {
+ //
+ // Convert offsets of strings to pointers
+ //
+ DWORD i;
+ LPNETRESOURCEW NetR = lpBuffer;
+
+ if (NetR->lpLocalName != NULL)
+ {
+ NetR->lpLocalName = (LPWSTR) ((DWORD) lpBuffer +
+ (DWORD) NetR->lpLocalName);
+ }
+
+ NetR->lpRemoteName = (LPWSTR) ((DWORD) lpBuffer +
+ (DWORD) NetR->lpRemoteName);
+
+ if (NetR->lpComment != NULL)
+ {
+ NetR->lpComment = (LPWSTR) ((DWORD) lpBuffer +
+ (DWORD) NetR->lpComment);
+ }
+
+ if (NetR->lpProvider != NULL)
+ {
+ NetR->lpProvider = (LPWSTR) ((DWORD) lpBuffer +
+ (DWORD) NetR->lpProvider);
+ }
+ }
+
+ return status;
+}
+
+
+
+DWORD
+APIENTRY
+NwEnumConnections(
+ HANDLE hEnum,
+ LPDWORD lpcCount,
+ LPVOID lpBuffer,
+ LPDWORD lpBufferSize,
+ BOOL fImplicitConnections
+ )
+/*++
+
+Routine Description:
+
+ This function returns a lists of connections.
+
+Arguments:
+
+ hEnum - Supplies the resumable enumeration context handle.
+
+ NOTE: If this value is 0xFFFFFFFF, it is not a context
+ handle and this routine is required to return
+ WN_NO_MORE_ENTRIES. This hack is to handle the
+ case where the user cancelled out of the network
+ credential dialog on NwrOpenEnumDirectories and we
+ cannot return an error there or we generate an error
+ popup.
+
+ lpcCount - On input, supplies the number of entries to get.
+ On output, if NO_ERROR is returned, receives the number
+ of entries NETRESOURCE returned in lpBuffer.
+
+ lpBuffer - Receives an array of NETRESOURCE entries, each
+ entry describes an object within the container.
+
+ lpBufferSize - On input, supplies the size of lpBuffer in
+ bytes. On output, if WN_MORE_DATA is returned, receives
+ the number of bytes needed in the buffer to get the
+ next entry.
+
+ fImplicitConnections - TRUE is we also want all implicit connections,
+ FALSE otherwise.
+
+Return Value:
+
+
+ NO_ERROR - Successfully returned at least one entry.
+
+ WN_NO_MORE_ENTRIES - Reached the end of enumeration and nothing
+ is returned.
+
+ WN_MORE_DATA - lpBuffer is too small to even get one entry.
+
+ WN_BAD_HANDLE - The enumeration handle is invalid.
+
+ Other network errors.
+
+--*/
+{
+ DWORD status = NO_ERROR;
+ DWORD BytesNeeded = 0;
+ DWORD EntriesRead = 0;
+
+#if DBG
+ IF_DEBUG(ENUM) {
+ KdPrint(("\nNWPROVAU: NPEnumResource\n"));
+ }
+#endif
+
+ RpcTryExcept {
+
+ if (hEnum == (HANDLE) 0xFFFFFFFF) {
+ status = WN_NO_MORE_ENTRIES;
+ goto EndOfTry;
+ }
+
+ status = NwrEnumConnections(
+ (NWWKSTA_CONTEXT_HANDLE) hEnum,
+ *lpcCount,
+ (LPBYTE) lpBuffer,
+ *lpBufferSize,
+ &BytesNeeded,
+ &EntriesRead,
+ fImplicitConnections
+ );
+
+ if (status == WN_MORE_DATA) {
+
+ //
+ // Output buffer too small to fit a single entry.
+ //
+ *lpBufferSize = BytesNeeded;
+ }
+ else if (status == NO_ERROR) {
+ *lpcCount = EntriesRead;
+ }
+
+EndOfTry: ;
+
+ }
+ RpcExcept(1) {
+ status = NwpMapRpcError(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ if (status != NO_ERROR && status != WN_NO_MORE_ENTRIES) {
+ SetLastError(status);
+ }
+
+ //
+ // Convert offsets of strings to pointers
+ //
+ if (EntriesRead > 0) {
+
+ DWORD i;
+ LPNETRESOURCEW NetR;
+
+
+ NetR = lpBuffer;
+
+ for (i = 0; i < EntriesRead; i++, NetR++) {
+
+ if (NetR->lpLocalName != NULL) {
+ NetR->lpLocalName = (LPWSTR) ((DWORD) lpBuffer +
+ (DWORD) NetR->lpLocalName);
+ }
+
+ NetR->lpRemoteName = (LPWSTR) ((DWORD) lpBuffer +
+ (DWORD) NetR->lpRemoteName);
+
+ if (NetR->lpComment != NULL) {
+ NetR->lpComment = (LPWSTR) ((DWORD) lpBuffer +
+ (DWORD) NetR->lpComment);
+ }
+
+ if (NetR->lpProvider != NULL) {
+ NetR->lpProvider = (LPWSTR) ((DWORD) lpBuffer +
+ (DWORD) NetR->lpProvider);
+ }
+ }
+ }
+
+ return status;
+}
+
+
+DWORD
+APIENTRY
+NPCloseEnum(
+ HANDLE hEnum
+ )
+/*++
+
+Routine Description:
+
+ This function closes the enumeration context handle.
+
+Arguments:
+
+ hEnum - Supplies the enumeration context handle.
+
+ NOTE: If this value is 0xFFFFFFFF, it is not a context
+ handle. Just return success.
+
+Return Value:
+
+ NO_ERROR - Successfully returned at least one entry.
+
+ WN_BAD_HANDLE - The enumeration handle is invalid.
+
+--*/
+{
+ DWORD status = NO_ERROR;
+
+#if DBG
+ IF_DEBUG(ENUM) {
+ KdPrint(("\nNWPROVAU: NPCloseEnum\n"));
+ }
+#endif
+
+ RpcTryExcept
+ {
+ if (hEnum == (HANDLE) 0xFFFFFFFF) {
+ status = NO_ERROR;
+ }
+ else {
+ status = NwrCloseEnum(
+ (LPNWWKSTA_CONTEXT_HANDLE) &hEnum
+ );
+ }
+ }
+ RpcExcept(1) {
+ status = NwpMapRpcError(RpcExceptionCode());
+ }
+ RpcEndExcept
+
+ if (status != NO_ERROR) {
+ SetLastError(status);
+ }
+ return status;
+}
+
+
+DWORD
+APIENTRY
+NPFormatNetworkName(
+ LPWSTR lpRemoteName,
+ LPWSTR lpFormattedName,
+ LPDWORD lpnLength,
+ DWORD dwFlags,
+ DWORD dwAveCharPerLine
+ )
+/*++
+
+Routine Description:
+
+ This function takes a fully-qualified UNC name and formats it
+ into a shorter form for display. Only the name of the object
+ within the container is returned for display.
+
+ We only support formatting of the remote resource name to the
+ abbreviated form for display during enumeration where the container
+ name is displayed prior to the object within it.
+
+Arguments:
+
+ lpRemoteName - Supplies the fully-qualified UNC name.
+
+ lpFormatedName - Output buffer to receive the formatted name.
+
+ lpnLength - On input, supplies the length of the lpFormattedName
+ buffer in characters. On output, if WN_MORE_DATA is returned,
+ receives the length in number of characters required of the
+ output buffer to hold the formatted name.
+
+ dwFlags - Supplies a bitwise set of flags indicating the type
+ of formatting required on lpRemoteName.
+
+ dwAveCharPerLine - Ignored.
+
+Return Value:
+
+ NO_ERROR - Successfully returned at least one entry.
+
+ WN_MORE_DATA - lpFormattedName buffer is too small.
+
+ WN_BAD_VALUE - lpRemoteName is NULL.
+
+ ERROR_NOT_SUPPORTED - dwFlags that does not contain the
+ WNFMT_INENUM bit.
+
+--*/
+{
+ DWORD status = NO_ERROR;
+
+ LPWSTR NextBackSlash;
+ LPWSTR Source;
+ DWORD SourceLen;
+
+#if DBG
+ IF_DEBUG(OTHER)
+ KdPrint(("\nNWPROVAU: NPFormatNetworkName\n"));
+#endif
+
+ if (lpRemoteName == NULL)
+ {
+ status = WN_BAD_VALUE;
+ goto CleanExit;
+ }
+
+ if (dwFlags & WNFMT_INENUM)
+ {
+ BYTE i;
+ WORD length = wcslen( lpRemoteName );
+ WORD slashCount = 0;
+ WORD dotCount = 0;
+ WORD Start = 0;
+ WORD End = length;
+ BOOL isNdsUnc = FALSE;
+
+ for ( i = 0; i < length; i++ )
+ {
+ if ( lpRemoteName[i] == L'\\' )
+ {
+ slashCount++;
+ if ( i + 1 < length )
+ {
+ Start = i + 1;
+ }
+ }
+
+ if ( ( lpRemoteName[i] == L'.' ) ||
+ ( lpRemoteName[i] == L'=' ) )
+ isNdsUnc = TRUE;
+
+ if ( dotCount < 1 && isNdsUnc && lpRemoteName[i] == L'.' )
+ {
+ End = i - 1;
+ dotCount++;
+ }
+ }
+
+ if ( i > length )
+ End = length - 1;
+
+ if ( slashCount > 3 || ( isNdsUnc != TRUE && slashCount != 3 && dotCount == 0 ) )
+ End = i - 1;
+
+ Source = &lpRemoteName[Start];
+ SourceLen = End - Start + 1;
+
+ if ( SourceLen + 1 > *lpnLength )
+ {
+ *lpnLength = SourceLen + 1;
+ status = WN_MORE_DATA;
+ }
+ else
+ {
+ wcsncpy( lpFormattedName, Source, SourceLen );
+ lpFormattedName[SourceLen] = 0x00000000;
+ status = NO_ERROR;
+ }
+ }
+ else if ( dwFlags & WNFMT_MULTILINE )
+ {
+
+ DWORD i, j, k = 0;
+ DWORD nLastBackSlash = 0;
+ DWORD BytesNeeded = ( wcslen( lpRemoteName ) + 1 +
+ 2 * wcslen( lpRemoteName ) / dwAveCharPerLine
+ ) * sizeof( WCHAR);
+
+ if ( *lpnLength < (BytesNeeded/sizeof(WCHAR)) )
+ {
+ *lpnLength = BytesNeeded/sizeof(WCHAR);
+ status = WN_MORE_DATA;
+ goto CleanExit;
+ }
+
+ for ( i = 0, j = 0; lpRemoteName[i] != 0; i++, j++ )
+ {
+ if ( lpRemoteName[i] == L'\\' )
+ nLastBackSlash = i;
+
+ if ( k == dwAveCharPerLine )
+ {
+ if ( lpRemoteName[i] != L'\\' )
+ {
+ DWORD m, n;
+ for ( n = nLastBackSlash, m = ++j ; n <= i ; n++, m-- )
+ {
+ lpFormattedName[m] = lpFormattedName[m-1];
+ }
+ lpFormattedName[m] = L'\n';
+ k = i - nLastBackSlash - 1;
+ }
+ else
+ {
+ lpFormattedName[j++] = L'\n';
+ k = 0;
+ }
+ }
+
+ lpFormattedName[j] = lpRemoteName[i];
+ k++;
+ }
+
+ lpFormattedName[j] = 0;
+
+ }
+ else if ( dwFlags & WNFMT_ABBREVIATED )
+ {
+ //
+ // we dont support abbreviated form for now because we look bad
+ // in comdlg (fileopen) if we do.
+ //
+
+ DWORD nLength;
+ nLength = wcslen( lpRemoteName ) + 1 ;
+ if (nLength > *lpnLength)
+ {
+ *lpnLength = nLength;
+ status = WN_MORE_DATA;
+ goto CleanExit;
+ }
+ else
+ {
+ wcscpy( lpFormattedName, lpRemoteName );
+ }
+
+#if 0
+ DWORD i, j, k;
+ DWORD BytesNeeded = dwAveCharPerLine * sizeof( WCHAR);
+ DWORD nLength;
+
+ if ( *lpnLength < BytesNeeded )
+ {
+ *lpnLength = BytesNeeded;
+ status = WN_MORE_DATA;
+ goto CleanExit;
+ }
+
+ nLength = wcslen( lpRemoteName );
+ if ( ( nLength + 1) <= dwAveCharPerLine )
+ {
+ wcscpy( lpFormattedName, lpRemoteName );
+ }
+ else
+ {
+ lpFormattedName[0] = lpRemoteName[0];
+ lpFormattedName[1] = lpRemoteName[1];
+
+ for ( i = 2; lpRemoteName[i] != L'\\'; i++ )
+ lpFormattedName[i] = lpRemoteName[i];
+
+ for ( j = dwAveCharPerLine-1, k = nLength; j >= (i+3); j--, k-- )
+ {
+ lpFormattedName[j] = lpRemoteName[k];
+ if ( lpRemoteName[k] == L'\\' )
+ {
+ j--;
+ break;
+ }
+ }
+
+ lpFormattedName[j] = lpFormattedName[j-1] = lpFormattedName[j-2] = L'.';
+
+ for ( k = i; k < (j-2); k++ )
+ lpFormattedName[k] = lpRemoteName[k];
+
+ }
+
+#endif
+
+ }
+ else // some unknown flags
+ {
+ status = ERROR_NOT_SUPPORTED;
+ }
+
+CleanExit:
+
+ if (status != NO_ERROR)
+ SetLastError(status);
+
+ return status;
+}
+
+
+STATIC
+BOOL
+NwpWorkstationStarted(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function queries the service controller to see if the
+ NetWare workstation service has started. If in doubt, it returns
+ FALSE.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Returns TRUE if the NetWare workstation service has started,
+ FALSE otherwise.
+
+--*/
+{
+ SC_HANDLE ScManager;
+ SC_HANDLE Service;
+ SERVICE_STATUS ServiceStatus;
+ BOOL IsStarted = FALSE;
+
+ ScManager = OpenSCManagerW(
+ NULL,
+ NULL,
+ SC_MANAGER_CONNECT
+ );
+
+ if (ScManager == NULL) {
+ return FALSE;
+ }
+
+ Service = OpenServiceW(
+ ScManager,
+ NW_WORKSTATION_SERVICE,
+ SERVICE_QUERY_STATUS
+ );
+
+ if (Service == NULL) {
+ CloseServiceHandle(ScManager);
+ return FALSE;
+ }
+
+ if (! QueryServiceStatus(Service, &ServiceStatus)) {
+ CloseServiceHandle(ScManager);
+ CloseServiceHandle(Service);
+ return FALSE;
+ }
+
+
+ if ( (ServiceStatus.dwCurrentState == SERVICE_RUNNING) ||
+ (ServiceStatus.dwCurrentState == SERVICE_CONTINUE_PENDING) ||
+ (ServiceStatus.dwCurrentState == SERVICE_PAUSE_PENDING) ||
+ (ServiceStatus.dwCurrentState == SERVICE_PAUSED) ) {
+
+ IsStarted = TRUE;
+ }
+
+ CloseServiceHandle(ScManager);
+ CloseServiceHandle(Service);
+
+ return IsStarted;
+}
+
+
+
+DWORD
+NwpMapNameToUNC(
+ IN LPWSTR pszName,
+ OUT LPWSTR *ppszUNC
+ )
+/*++
+
+Routine Description:
+
+ This routine validates the given name as a netwarepath or UNC path.
+ If it is a netware path, this routine will convert the
+ Netware path name to UNC name.
+
+Arguments:
+
+ pszName - Supplies the netware name or UNC name
+ ppszUNC - Points to the converted UNC name
+
+Return Value:
+
+ NO_ERROR or the error that occurred.
+
+--*/
+{
+ DWORD err = NO_ERROR;
+
+ LPWSTR pszSrc = pszName;
+ LPWSTR pszDest;
+
+ BOOL fSlash = FALSE;
+ BOOL fColon = FALSE;
+ DWORD nServerLen = 0;
+ DWORD nVolLen = 0;
+ BOOL fFirstToken = TRUE;
+
+ *ppszUNC = NULL;
+
+#if DBG
+ IF_DEBUG(CONNECT)
+ KdPrint(("NwpMapNameToUNC: Source = %ws\n", pszName ));
+#endif
+
+
+ //
+ // Check if the given name is a valid UNC name
+ //
+ err = NwLibCanonRemoteName( NULL, // "\\Server" is valid UNC path
+ pszName,
+ ppszUNC,
+ NULL );
+
+ //
+ // The given name is a valid UNC name, so return success!
+ //
+ if ( err == NO_ERROR )
+ return err;
+
+ //
+ // The name cannot be NULL or empty string
+ //
+ if ( pszName == NULL || *pszName == 0)
+ return WN_BAD_NETNAME;
+
+ //
+ // Allocate the buffer to store the mapped UNC name
+ // We allocate 3 extra characters, two for the backslashes in front
+ // and one for the ease of parsing below.
+ //
+ if ((*ppszUNC = (LPVOID) LocalAlloc(
+ LMEM_ZEROINIT,
+ (wcslen( pszName) + 4) * sizeof( WCHAR)
+ )) == NULL )
+ {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ wcscpy( *ppszUNC, L"\\\\" );
+ pszDest = *ppszUNC + 2; // Skip past two backslashes
+
+ //
+ // Parse the given string and put the converted string into *ppszUNC
+ // In the converted string, we will substitute 0 for all slashes
+ // for the time being.
+ //
+ for ( ; *pszSrc != 0; pszSrc++ )
+ {
+ if ( ( *pszSrc == L'/' )
+ || ( *pszSrc == L'\\' )
+ )
+ {
+ //
+ // Two consecutive backslashes are bad
+ //
+ if ( (*(pszSrc+1) == L'/') || (*(pszSrc+1) == L'\\'))
+ {
+ LocalFree( *ppszUNC );
+ *ppszUNC = NULL;
+ return WN_BAD_NETNAME;
+ }
+
+ if ( !fSlash )
+ fSlash = TRUE;
+
+ *pszDest++ = 0;
+ }
+ else if ( (*pszSrc == L':') && fSlash && !fColon )
+ {
+ fColon = TRUE;
+ if ( *(pszSrc+1) != 0 )
+ *pszDest++ = 0;
+
+ }
+ else
+ {
+ *pszDest++ = *pszSrc;
+ if (( fSlash ) && ( !fColon))
+ nVolLen++;
+ else if ( !fSlash )
+ nServerLen++;
+ }
+ }
+
+ //
+ // Note: *ppszUNC is already terminated with two '\0' because we initialized
+ // the whole buffer to zero.
+ //
+
+ if ( ( nServerLen == 0 )
+ || ( fSlash && nVolLen == 0 )
+ || ( fSlash && nVolLen != 0 && !fColon )
+ )
+ {
+ LocalFree( *ppszUNC );
+ *ppszUNC = NULL;
+ return WN_BAD_NETNAME;
+ }
+
+ //
+ // At this point, we know the name is a valid Netware syntax
+ // i.e. SERVER[/VOL:/dir]
+ // We now need to validate that all the characters used in the
+ // servername, volume, directory are valid characters
+ //
+
+ pszDest = *ppszUNC + 2; // Skip past the first two backslashes
+ while ( *pszDest != 0 )
+ {
+ DWORD nLen = wcslen( pszDest );
+
+ if ( ( fFirstToken && !IS_VALID_SERVER_TOKEN( pszDest, nLen ))
+ || ( !fFirstToken && !IS_VALID_TOKEN( pszDest, nLen ))
+ )
+ {
+ LocalFree( *ppszUNC );
+ *ppszUNC = NULL;
+ return WN_BAD_NETNAME;
+ }
+
+ fFirstToken = FALSE;
+ pszDest += nLen + 1;
+ }
+
+ //
+ // The netware name is valid! Convert 0 back to backslash in
+ // converted string.
+ //
+
+ pszDest = *ppszUNC + 2; // Skip past the first two backslashes
+ while ( *pszDest != 0 )
+ {
+ if ( (*(pszDest+1) == 0 ) && (*(pszDest+2) != 0 ) )
+ {
+ *(pszDest+1) = L'\\';
+ }
+ pszDest++;
+ }
+
+#if DBG
+ IF_DEBUG(CONNECT)
+ KdPrint(("NwpMapNameToUNC: Destination = %ws\n", *ppszUNC ));
+#endif
+ return NO_ERROR;
+}
+
+
+STATIC
+VOID
+NwpGetUncInfo(
+ IN LPWSTR lpstrUnc,
+ OUT WORD * slashCount,
+ OUT BOOL * isNdsUnc
+ )
+{
+ BYTE i;
+ WORD length = wcslen( lpstrUnc );
+
+ *isNdsUnc = FALSE;
+ *slashCount = 0;
+
+ for ( i = 0; i < length; i++ )
+ {
+ if ( ( lpstrUnc[i] == L'.' ) && ( *slashCount == 3 ) )
+ {
+ *isNdsUnc = TRUE;
+ }
+
+ if ( lpstrUnc[i] == L'\\' )
+ {
+ *slashCount += 1;
+ }
+ }
+}
+
+
+STATIC
+DWORD
+NwpGetUncObjectName(
+ IN LPWSTR ContainerName
+)
+{
+ WORD length = 2;
+ WORD totalLength = wcslen( ContainerName );
+
+ if ( totalLength < 2 )
+ return 0;
+
+ while ( length < totalLength )
+ {
+ if ( ContainerName[length] == L'.' )
+ ContainerName[length] = L'\0';
+
+ length++;
+ }
+
+ length = 2;
+
+ while ( length < totalLength && ContainerName[length] != L'\\' )
+ {
+ length++;
+ }
+
+ if ( ( ContainerName[length + 1] == L'C' ||
+ ContainerName[length + 1] == L'c' ) &&
+ ( ContainerName[length + 2] == L'N' ||
+ ContainerName[length + 2] == L'n' ) &&
+ ContainerName[length + 3] == L'=' )
+ {
+ ContainerName[length + 2] = L'\\';
+ ContainerName[length + 3] = L'\\';
+
+ return (DWORD) (ContainerName + length + 2);
+ }
+
+ ContainerName[length - 1] = L'\\';
+
+ return (DWORD) (ContainerName + length - 1);
+}
+
+
+STATIC
+WORD
+NwpGetSlashCount(
+ IN LPWSTR lpstrUnc
+ )
+{
+ WORD count = 0;
+ BYTE i;
+ WORD length = wcslen( lpstrUnc );
+
+ for ( i = 0; i < length; i++ )
+ {
+ if ( lpstrUnc[i] == L'\\' )
+ {
+ count++;
+ }
+ }
+
+ return count;
+}
+
+
+DWORD
+NwpMapRpcError(
+ IN DWORD RpcError
+ )
+/*++
+
+Routine Description:
+
+ This routine maps the RPC error into a more meaningful windows
+ error for the caller.
+
+Arguments:
+
+ RpcError - Supplies the exception error raised by RPC
+
+Return Value:
+
+ Returns the mapped error.
+
+--*/
+{
+
+ switch (RpcError) {
+
+ case RPC_S_UNKNOWN_IF:
+ case RPC_S_SERVER_UNAVAILABLE:
+ return WN_NO_NETWORK;
+
+ case RPC_S_INVALID_BINDING:
+ case RPC_X_SS_IN_NULL_CONTEXT:
+ case RPC_X_SS_CONTEXT_DAMAGED:
+ case RPC_X_SS_HANDLES_MISMATCH:
+ case ERROR_INVALID_HANDLE:
+ return ERROR_INVALID_HANDLE;
+
+ case RPC_X_NULL_REF_POINTER:
+ return ERROR_INVALID_PARAMETER;
+
+ case EXCEPTION_ACCESS_VIOLATION:
+ return ERROR_INVALID_ADDRESS;
+
+ default:
+ return RpcError;
+ }
+}
+
+DWORD
+NwRegisterGatewayShare(
+ IN LPWSTR ShareName,
+ IN LPWSTR DriveName
+ )
+/*++
+
+Routine Description:
+
+ This routine remembers that a gateway share has been created so
+ that it can be cleanup up when NWCS is uninstalled.
+
+Arguments:
+
+ ShareName - name of share
+ DriveName - name of drive that is shared
+
+Return Status:
+
+ Win32 error of any failure.
+
+--*/
+{
+ return ( NwpRegisterGatewayShare(ShareName, DriveName) ) ;
+}
+
+DWORD
+NwCleanupGatewayShares(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine cleans up all persistent share info and also tidies
+ up the registry for NWCS. Later is not needed in uninstall, but is
+ there so we have a single routine that cvompletely disables the
+ gateway.
+
+Arguments:
+
+ None.
+
+Return Status:
+
+ Win32 error for failed APIs.
+
+--*/
+{
+ return ( NwpCleanupGatewayShares() ) ;
+}
+
+DWORD
+NwClearGatewayShare(
+ IN LPWSTR ShareName
+ )
+/*++
+
+Routine Description:
+
+ This routine deletes a specific share from the remembered gateway
+ shares in the registry.
+
+Arguments:
+
+ ShareName - share value to delete
+
+Return Status:
+
+ Win32 status code.
+
+--*/
+{
+ return ( NwpClearGatewayShare( ShareName ) ) ;
+}
diff --git a/private/nw/svcdlls/nwwks/client/server.bmp b/private/nw/svcdlls/nwwks/client/server.bmp
new file mode 100644
index 000000000..200dc12ba
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/server.bmp
Binary files differ
diff --git a/private/nw/svcdlls/nwwks/client/server.ico b/private/nw/svcdlls/nwwks/client/server.ico
new file mode 100644
index 000000000..1d628da58
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/server.ico
Binary files differ
diff --git a/private/nw/svcdlls/nwwks/client/sources b/private/nw/svcdlls/nwwks/client/sources
new file mode 100644
index 000000000..97d12a350
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/sources
@@ -0,0 +1,101 @@
+!IF 0
+
+Copyright (c) 1989-93 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+ Rita Wong (ritaw) 14-Feb-1993
+
+
+Revision History:
+
+!ENDIF
+
+MAJORCOMP=nw
+MINORCOMP=service
+
+TARGETNAME=nwprovau
+TARGETPATH=\nt\public\sdk\lib
+TARGETTYPE=DYNLINK
+DLLBASE = 0x69900000
+
+
+TARGETLIBS= \
+ \nt\public\sdk\lib\*\rpcrt4.lib \
+ \nt\public\sdk\lib\*\advapi32.lib \
+ \nt\public\sdk\lib\*\kernel32.lib \
+ \nt\public\sdk\lib\*\netapi32.lib \
+ \nt\public\sdk\lib\*\rpcutil.lib \
+ \nt\public\sdk\lib\*\netlib.lib\
+ ..\lib\obj\*\nwwlib.lib \
+ ..\..\..\nwlib\obj\*\nwlib.lib \
+ \nt\public\sdk\lib\*\user32.lib \
+ \nt\public\sdk\lib\*\lsadll.lib \
+ \nt\public\sdk\lib\*\mpr.lib \
+ \nt\public\sdk\lib\*\ws2_32.lib \
+ \nt\public\sdk\lib\*\nwsaplib.lib \
+ \nt\public\sdk\lib\*\ole32.lib \
+ \nt\public\sdk\lib\*\uuid.lib \
+ \nt\public\sdk\lib\*\gdi32.lib \
+ \nt\public\sdk\lib\*\comctl32.lib \
+ obj\*\nwprovau.res
+
+
+DLLENTRY=InitializeDll
+
+INCLUDES=.;..\inc;..\..\..\inc;$(_NTROOT)\private\inc; $(_NTROOT)\private\net\inc;$(_NTROOT)\private\windows\inc;$(_NTROOT)\private\windows\inc16
+
+NTDEBUGTYPE=both
+
+UNICODE=1
+
+SOURCES= \
+ bind.c \
+ logon.c \
+ provider.c \
+ nwdlg.c \
+ authpkg.c \
+ nwspl.c \
+ port.c \
+ nwapi.c \
+ ccache.c \
+ getaddr.c \
+ gtadrnr.c \
+ nwwks_c.c \
+ nwutil.c \
+ nwshext.cxx \
+ nwshprop.cxx \
+ nwshmenu.cxx \
+ nwshmisc.cxx \
+ drawpie.c \
+ nwshui.cxx \
+ caddress.c
+
+USE_CRTDLL=1
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+NET_C_DEFINES=-DRPC_NO_WINDOWS_H -DUNICODE -DNOT_USED
+DLLDEF=nwprovau.def
+
+
+UMTYPE=console
+UMTESt=tconn
+UMLIBS= \
+ \nt\public\sdk\lib\*\nwprovau.lib
+
+NTTARGETFILE0=nwprovau.rc
+RCCODEPAGE=1252
+
diff --git a/private/nw/svcdlls/nwwks/client/tconn.c b/private/nw/svcdlls/nwwks/client/tconn.c
new file mode 100644
index 000000000..36057cc3e
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/tconn.c
@@ -0,0 +1,556 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ tconnect.c
+
+Abstract:
+
+ Test for workstation connection APIs.
+
+Author:
+
+ Rita Wong (ritaw) 17-Feb-1993
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+#ifndef UNICODE
+#define UNICODE
+#endif
+
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windef.h>
+#include <winbase.h>
+#include <winnetwk.h>
+#include <npapi.h>
+
+#define REDIR_NOT_READY
+
+DWORD
+TestCreateConnection(
+ IN LPWSTR LocalName OPTIONAL,
+ IN LPWSTR RemoteName,
+ IN LPWSTR Password OPTIONAL,
+ IN LPWSTR UserName OPTIONAL,
+ IN DWORD ExpectedError
+ );
+
+DWORD
+TestDeleteConnection(
+ IN LPWSTR ConnectionName,
+ IN BOOL ForceFlag,
+ IN DWORD ExpectedError
+ );
+
+DWORD
+TestOpenEnum(
+ IN DWORD Scope,
+ IN LPNETRESOURCEW NetR OPTIONAL,
+ OUT LPHANDLE EnumHandle,
+ IN DWORD ExpectedError
+ );
+
+DWORD
+TestEnum(
+ IN HANDLE EnumHandle,
+ IN DWORD EntriesRequested,
+ IN LPVOID Buffer,
+ IN DWORD BufferSize,
+ OUT LPDWORD BytesNeeded,
+ IN DWORD ExpectedError
+ );
+
+VOID
+PrintNetResource(
+ LPNETRESOURCE NetR
+ );
+
+
+BYTE WorkBuffer[1024];
+BYTE WorkBuffer2[1024];
+
+void _CRTAPI1
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ DWORD status;
+
+ LPWSTR LocalName;
+ LPWSTR RemoteName;
+ LPWSTR Password;
+ LPWSTR UserName;
+
+ NETRESOURCEW NetR;
+ HANDLE EnumHandle;
+ DWORD BytesNeeded;
+
+
+ LocalName = L"E:";
+ RemoteName = L"\\\\MyServerName\\A_Volume\\A_Directory";
+ Password = L"MyPassword";
+ UserName = L"MyUserName";
+
+
+ TestCreateConnection(
+ LocalName,
+ RemoteName,
+ Password,
+ UserName,
+ WN_SUCCESS
+ );
+
+ TestDeleteConnection(
+ LocalName,
+ TRUE,
+ WN_SUCCESS
+ );
+
+ //-------------------------//
+
+ TestCreateConnection(
+ NULL,
+ RemoteName,
+ NULL,
+ NULL,
+ WN_SUCCESS
+ );
+
+ TestDeleteConnection(
+ RemoteName,
+ TRUE,
+ WN_SUCCESS
+ );
+
+ //-------------------------//
+
+ TestCreateConnection(
+ L"LPT1",
+ RemoteName,
+ NULL,
+ NULL,
+ ERROR_INVALID_PARAMETER
+ );
+
+ //-------------------------//
+
+ TestCreateConnection(
+ LocalName,
+ L"\\\\Server",
+ NULL,
+ NULL,
+ ERROR_INVALID_NAME
+ );
+
+ //-------------------------//
+
+ printf("\n");
+
+ //-------------------------//
+
+#ifdef REDIR_NOT_READY
+
+ if (argc == 2) {
+
+ ANSI_STRING AStr;
+ UNICODE_STRING UStr;
+
+
+ RtlZeroMemory(WorkBuffer2, sizeof(WorkBuffer2));
+ UStr.Buffer = WorkBuffer2;
+ UStr.MaximumLength = sizeof(WorkBuffer2);
+
+ RtlInitString(&AStr, argv[1]);
+
+ RtlAnsiStringToUnicodeString(
+ &UStr,
+ &AStr,
+ FALSE
+ );
+
+ NetR.lpRemoteName = UStr.Buffer;
+ }
+ else {
+ NetR.lpRemoteName = L"lanman";
+ }
+
+ //-------------------------//
+
+ TestOpenEnum(
+ RESOURCE_GLOBALNET,
+ &NetR,
+ &EnumHandle,
+ WN_SUCCESS
+ );
+
+ TestEnum(
+ EnumHandle,
+ 0xFFFFFFFF,
+ WorkBuffer,
+ sizeof(WorkBuffer),
+ &BytesNeeded,
+ WN_SUCCESS
+ );
+
+ TestEnum(
+ EnumHandle,
+ 0xFFFFFFFF,
+ WorkBuffer,
+ sizeof(WorkBuffer),
+ &BytesNeeded,
+ WN_NO_MORE_ENTRIES
+ );
+
+ (void) NPCloseEnum(EnumHandle);
+
+ //-------------------------//
+
+ printf("\n");
+
+ //-------------------------//
+
+ TestOpenEnum(
+ RESOURCE_GLOBALNET,
+ &NetR,
+ &EnumHandle,
+ WN_SUCCESS
+ );
+
+ TestEnum(
+ EnumHandle,
+ 0xFFFFFFFF,
+ WorkBuffer,
+ 200,
+ &BytesNeeded,
+ WN_SUCCESS
+ );
+
+ TestEnum(
+ EnumHandle,
+ 0xFFFFFFFF,
+ WorkBuffer,
+ sizeof(NETRESOURCEW) + 5,
+ &BytesNeeded,
+ WN_MORE_DATA
+ );
+
+ TestEnum(
+ EnumHandle,
+ 0xFFFFFFFF,
+ WorkBuffer,
+ BytesNeeded,
+ &BytesNeeded,
+ WN_SUCCESS
+ );
+
+ TestEnum(
+ EnumHandle,
+ 0xFFFFFFFF,
+ WorkBuffer,
+ sizeof(WorkBuffer),
+ &BytesNeeded,
+ WN_SUCCESS
+ );
+
+ TestEnum(
+ EnumHandle,
+ 0xFFFFFFFF,
+ WorkBuffer,
+ 0,
+ &BytesNeeded,
+ WN_NO_MORE_ENTRIES
+ );
+
+ (void) NPCloseEnum(EnumHandle);
+
+#else
+
+ NetR.lpRemoteName = L"\\\\S";
+
+ TestOpenEnum(
+ RESOURCE_GLOBALNET,
+ &NetR,
+ &EnumHandle,
+ WN_SUCCESS
+ );
+
+ //-------------------------//
+
+ NetR.lpRemoteName = L"\\\\A Long Server Name";
+
+ TestOpenEnum(
+ RESOURCE_GLOBALNET,
+ &NetR,
+ &EnumHandle,
+ WN_SUCCESS
+ );
+
+ //-------------------------//
+
+ NetR.lpRemoteName = L"\\\\S\\";
+
+ TestOpenEnum(
+ RESOURCE_GLOBALNET,
+ &NetR,
+ &EnumHandle,
+ ERROR_INVALID_NAME
+ );
+
+ //-------------------------//
+
+ NetR.lpRemoteName = L"lanman";
+
+ TestOpenEnum(
+ RESOURCE_GLOBALNET,
+ &NetR,
+ &EnumHandle,
+ ERROR_INVALID_NAME
+ );
+
+ //-------------------------//
+
+ NetR.lpRemoteName = L"\\\\S\\Y";
+
+ TestOpenEnum(
+ RESOURCE_GLOBALNET,
+ &NetR,
+ &EnumHandle,
+ WN_SUCCESS
+ );
+
+ //-------------------------//
+
+ NetR.lpRemoteName = L"\\\\Server\\Volume\\Dir";
+
+ TestOpenEnum(
+ RESOURCE_GLOBALNET,
+ &NetR,
+ &EnumHandle,
+ WN_SUCCESS
+ );
+
+#endif
+
+}
+
+
+DWORD
+TestCreateConnection(
+ IN LPWSTR LocalName OPTIONAL,
+ IN LPWSTR RemoteName,
+ IN LPWSTR Password OPTIONAL,
+ IN LPWSTR UserName OPTIONAL,
+ IN DWORD ExpectedError
+ )
+{
+ DWORD status;
+ NETRESOURCEW NetR;
+
+
+ printf("\nTestCreateConnection: Local %ws, Remote %ws", LocalName, RemoteName);
+
+ if (ARGUMENT_PRESENT(UserName)) {
+ printf(" UserName %ws", UserName);
+ }
+
+ if (ARGUMENT_PRESENT(Password)) {
+ printf(" Password %ws", Password);
+
+ }
+
+ printf("\n");
+
+ NetR.lpLocalName = LocalName;
+ NetR.lpRemoteName = RemoteName;
+
+ NetR.dwType = RESOURCETYPE_ANY;
+
+ status = NPAddConnection(
+ &NetR,
+ Password,
+ UserName
+ );
+
+ if (status != WN_SUCCESS) {
+ status = GetLastError();
+ }
+
+ if (status != ExpectedError) {
+ printf(" FAILED: expected %lu got %lu\n", ExpectedError, status);
+ }
+ else {
+ printf(" SUCCESS: got %lu as expected\n", status);
+ }
+
+ return status;
+
+}
+
+DWORD
+TestDeleteConnection(
+ IN LPWSTR ConnectionName,
+ IN BOOL ForceFlag,
+ IN DWORD ExpectedError
+ )
+{
+ DWORD status;
+
+
+ printf("\nTestDeleteConnection: Connection %ws, ForceFlag %u\n",
+ ConnectionName, ForceFlag);
+
+ status = NPCancelConnection(
+ ConnectionName,
+ ForceFlag
+ );
+
+ if (status != WN_SUCCESS) {
+ status = GetLastError();
+ }
+
+ if (status != ExpectedError) {
+ printf(" FAILED: expected %lu got %lu\n", ExpectedError, status);
+ }
+ else {
+ printf(" SUCCESS: got %lu as expected\n", status);
+ }
+
+ return status;
+}
+
+DWORD
+TestOpenEnum(
+ IN DWORD Scope,
+ IN LPNETRESOURCEW NetR OPTIONAL,
+ OUT LPHANDLE EnumHandle,
+ IN DWORD ExpectedError
+ )
+{
+ DWORD status;
+
+
+ if (NetR != NULL) {
+ printf("\nTestOpenEnum: Remote %ws\n", NetR->lpRemoteName);
+ }
+
+ status = NPOpenEnum(
+ Scope,
+ 0,
+ 0,
+ NetR,
+ EnumHandle
+ );
+
+
+ if (status != WN_SUCCESS) {
+ status = GetLastError();
+ }
+
+ if (status != ExpectedError) {
+ printf(" FAILED: expected %lu got %lu\n", ExpectedError, status);
+ }
+ else {
+ printf(" SUCCESS: got %lu as expected\n", status);
+ }
+
+ return status;
+}
+
+DWORD
+TestEnum(
+ IN HANDLE EnumHandle,
+ IN DWORD EntriesRequested,
+ IN LPVOID Buffer,
+ IN DWORD BufferSize,
+ OUT LPDWORD BytesNeeded,
+ IN DWORD ExpectedError
+ )
+{
+ DWORD status;
+ DWORD EntriesRead = EntriesRequested;
+
+ DWORD i;
+ LPNETRESOURCE NetR = Buffer;
+
+
+ *BytesNeeded = BufferSize;
+
+ printf("\nTestEnum: EntriesRequested x%08lx, BufferSize %lu\n",
+ EntriesRead, *BytesNeeded);
+
+ status = NPEnumResource(
+ EnumHandle,
+ &EntriesRead,
+ Buffer,
+ BytesNeeded
+ );
+
+ if (status == WN_SUCCESS) {
+
+ printf(" EntriesRead is %lu\n", EntriesRead);
+
+ for (i = 0; i < EntriesRead; i++, NetR++) {
+ PrintNetResource(NetR);
+ }
+
+ }
+ else if (status != WN_NO_MORE_ENTRIES) {
+
+ status = GetLastError();
+
+ if (status == WN_MORE_DATA) {
+ printf(" BytesNeeded is %lu\n", *BytesNeeded);
+ }
+ }
+
+ if (status != ExpectedError) {
+ printf(" FAILED: expected %lu got %lu\n", ExpectedError, status);
+ }
+ else {
+ printf(" SUCCESS: got %lu as expected\n", status);
+ }
+
+ return status;
+}
+
+VOID
+PrintNetResource(
+ LPNETRESOURCE NetR
+ )
+{
+ if (NetR->lpLocalName != NULL) {
+ printf("%-7ws", NetR->lpLocalName);
+ }
+
+ printf(" %-ws\n", NetR->lpRemoteName);
+
+ if (NetR->lpComment != NULL) {
+ printf(" %-ws\n", NetR->lpComment);
+ }
+
+ if (NetR->lpProvider != NULL) {
+ printf(" %-ws\n", NetR->lpProvider);
+ }
+
+ printf("Scope: x%lx", NetR->dwScope);
+ printf(" Type: x%lx", NetR->dwType);
+ printf(" DisplayType: x%lx", NetR->dwDisplayType);
+ printf(" Usage: x%lx\n", NetR->dwUsage);
+
+
+}
diff --git a/private/nw/svcdlls/nwwks/client/tree.bmp b/private/nw/svcdlls/nwwks/client/tree.bmp
new file mode 100644
index 000000000..e63395971
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/tree.bmp
Binary files differ
diff --git a/private/nw/svcdlls/nwwks/client/tree.ico b/private/nw/svcdlls/nwwks/client/tree.ico
new file mode 100644
index 000000000..23ea78077
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/client/tree.ico
Binary files differ
diff --git a/private/nw/svcdlls/nwwks/dirs b/private/nw/svcdlls/nwwks/dirs
new file mode 100644
index 000000000..d7b8400d7
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/dirs
@@ -0,0 +1,26 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ dirs.
+
+Abstract:
+
+ This file specifies the subdirectories of the current directory that
+ contain component makefiles.
+
+
+Author:
+
+ Steve Wood (stevewo) 17-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\dirs.tpl
+
+!ENDIF
+
+
+DIRS=lib \
+ server \
+ client
diff --git a/private/nw/svcdlls/nwwks/idl.tmp b/private/nw/svcdlls/nwwks/idl.tmp
new file mode 100644
index 000000000..8b9404c32
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/idl.tmp
@@ -0,0 +1,34 @@
+
+
+
+//
+// Sample .idl definition
+//
+
+typedef [switch_type(unsigned long)] union _NW_PRINTER_INFO {
+ [case(1)]
+ LPPRINTER_INFO_1 PrinterInfo1;
+ [case(2)]
+ LPPRINTER_INFO_2 PrinterInfo2;
+ [default]
+ ;
+} NW_PRINTER_INFO, *PNW_PRINTER_INFO, *LPNW_PRINTER_INFO;
+
+DWORD
+NwrGetPrinter(
+ [in] NWWKSTA_PRINTER_CONTEXT PrinterHandle,
+ [in] DWORD Level,
+ [out,switch_is(Level)] LPPRINTER_INFO PrinterInfo,
+ [in] DWORD BufferSize,
+ [out] LPDWORD BytesNeeded
+ );
+
+
+----------------------------------------------------------------------
+
+
+//
+// Sample .acf definition (put this between the curly braces)
+//
+
+NwrGetPrinter([byte_count(BufferSize)] PrinterInfo);
diff --git a/private/nw/svcdlls/nwwks/imports.idl b/private/nw/svcdlls/nwwks/imports.idl
new file mode 100644
index 000000000..e680f45d5
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/imports.idl
@@ -0,0 +1,53 @@
+/*++
+
+Copyright (c) 1991-1993 Microsoft Corporation
+
+Module Name:
+
+ imports.idl
+
+Abstract:
+
+ This file is useful for creating RPC interfaces that require the use
+ of windef types. The .idl file for the RPC product should contain a
+ line in the interface body that imports this file. For example:
+
+ import "imports.h";
+
+ Doing this causes the MIDL generated header file to contain the
+ following line:
+
+ #include "imports.h"
+
+ If this technique is not used, and instead the .idl file for the RPC
+ product simply contains #include <importsf.h>, then the contents of
+ imports.h will be expanded in the MIDL generated header file. This
+ can lead to duplicate definition problems later when the RPC client
+ or RPC server code needs to include both the MIDL generated header file
+ and a file that is included in imports.h.
+
+Author:
+
+ Dan Lafferty (danl) 20-Mar-1991
+ Rita Wong (ritaw) 14-Feb-1993
+
+Environment:
+
+ User Mode - Win32 - for use with the MIDL compiler
+
+
+Revision History:
+
+--*/
+
+[
+ version(0.0),
+ local
+]
+interface imports
+
+{
+#define MIDL_PASS
+#include "imports.h"
+
+}
diff --git a/private/nw/svcdlls/nwwks/inc/imports.h b/private/nw/svcdlls/nwwks/inc/imports.h
new file mode 100644
index 000000000..93176c732
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/inc/imports.h
@@ -0,0 +1,15 @@
+#include <windef.h>
+
+#ifdef MIDL_PASS
+#define LPWSTR [string] wchar_t*
+#define PSECURITY_DESCRIPTOR DWORD
+#endif
+
+#include <stdarg.h>
+#include <winbase.h>
+#include <wingdi.h>
+#include <winspool.h>
+#define _INC_WINDOWS
+#include <winsock2.h>
+#include <wsipx.h>
+#include <nspapi.h>
diff --git a/private/nw/svcdlls/nwwks/inc/nwauth.h b/private/nw/svcdlls/nwwks/inc/nwauth.h
new file mode 100644
index 000000000..72f78e3a8
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/inc/nwauth.h
@@ -0,0 +1,70 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ nwauth.h
+
+Abstract:
+
+ Header for data structures provided by the NetWare
+ authentication package.
+
+Author:
+
+ Rita Wong (ritaw) 4-Feb-1994
+
+Revision History:
+
+--*/
+
+#ifndef _NWAUTH_INCLUDED_
+#define _NWAUTH_INCLUDED_
+
+#include <nwcons.h>
+
+//
+// Name of the authentication package.
+//
+#define NW_AUTH_PACKAGE_NAME "NETWARE_AUTHENTICATION_PACKAGE_V1_0"
+
+//
+// LsaCallAuthenticationPackage() submission and response
+// message types.
+//
+
+typedef enum _NWAUTH_MESSAGE_TYPE {
+ NwAuth_GetCredential = 0,
+ NwAuth_SetCredential
+} NWAUTH_MESSAGE_TYPE, *PNWAUTH_MESSAGE_TYPE;
+
+//
+// NwAuth_GetCredential submit buffer and response
+//
+typedef struct _NWAUTH_GET_CREDENTIAL_REQUEST {
+ NWAUTH_MESSAGE_TYPE MessageType;
+ LUID LogonId;
+} NWAUTH_GET_CREDENTIAL_REQUEST, *PNWAUTH_GET_CREDENTIAL_REQUEST;
+
+typedef struct _NWAUTH_GET_CREDENTIAL_RESPONSE {
+ WCHAR UserName[NW_MAX_USERNAME_LEN + 1];
+ WCHAR Password[NW_MAX_PASSWORD_LEN + 1];
+} NWAUTH_GET_CREDENTIAL_RESPONSE, *PNWAUTH_GET_CREDENTIAL_RESPONSE;
+
+
+//
+// NwAuth_SetCredential submit buffer
+//
+typedef struct _NWAUTH_SET_CREDENTIAL_REQUEST {
+ NWAUTH_MESSAGE_TYPE MessageType;
+ LUID LogonId;
+ WCHAR UserName[NW_MAX_USERNAME_LEN + 1];
+ WCHAR Password[NW_MAX_PASSWORD_LEN + 1];
+} NWAUTH_SET_CREDENTIAL_REQUEST, *PNWAUTH_SET_CREDENTIAL_REQUEST;
+
+#define NW_ENCODE_SEED 0x5C
+#define NW_ENCODE_SEED2 0xA9
+#define NW_ENCODE_SEED3 0x83
+
+#endif // _NWAUTH_INCLUDED_
diff --git a/private/nw/svcdlls/nwwks/inc/nwlsa.h b/private/nw/svcdlls/nwwks/inc/nwlsa.h
new file mode 100644
index 000000000..cb2245c5d
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/inc/nwlsa.h
@@ -0,0 +1,69 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ nwlsa.h
+
+Abstract:
+
+ Header for LSA helper routines used by both the client and
+ server sides of the workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 22-Mar-1993
+
+Revision History:
+
+--*/
+
+#ifndef _NWLSA_INCLUDED_
+#define _NWLSA_INCLUDED_
+
+#include <ntlsa.h>
+
+
+DWORD
+NwOpenPolicy(
+ IN ACCESS_MASK DesiredAccess,
+ OUT LSA_HANDLE *PolicyHandle
+ );
+
+DWORD
+NwOpenSecret(
+ IN ACCESS_MASK DesiredAccess,
+ IN LSA_HANDLE PolicyHandle,
+ IN LPWSTR LsaSecretName,
+ OUT PLSA_HANDLE SecretHandle
+ );
+
+DWORD
+NwFormSecretName(
+ IN LPWSTR UserName,
+ OUT LPWSTR *LsaSecretName
+ );
+
+DWORD
+NwSetPassword(
+ IN LPWSTR UserName,
+ IN LPWSTR Password
+ );
+
+DWORD
+NwDeletePassword(
+ IN LPWSTR UserName
+ );
+
+DWORD
+NwGetPassword(
+ IN LPWSTR UserName,
+ OUT PUNICODE_STRING *Password,
+ OUT PUNICODE_STRING *OldPassword
+ );
+
+#define LAST_USER L"LastUser"
+#define GATEWAY_USER L"GatewayUser"
+
+#endif // _NWLSA_INCLUDED_
diff --git a/private/nw/svcdlls/nwwks/inc/nwmisc.h b/private/nw/svcdlls/nwwks/inc/nwmisc.h
new file mode 100644
index 000000000..8b58de22d
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/inc/nwmisc.h
@@ -0,0 +1,160 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ nwmisc.h
+
+Abstract:
+
+ Header which specifies the misc routines used by the workstation service.
+
+Author:
+
+ Chuck Y Chan (chuckc) 2-Mar-1994
+
+Revision History:
+
+ Glenn A Curtis (glennc) 18-Jul-1995
+
+--*/
+
+#ifndef _NWMISC_INCLUDED_
+#define _NWMISC_INCLUDED_
+
+#include <winsock2.h>
+#include <basetyps.h>
+#include <nspapi.h>
+#include "sapcmn.h"
+
+DWORD
+NwGetGraceLoginCount(
+ LPWSTR Server,
+ LPWSTR UserName,
+ LPDWORD lpResult
+ );
+
+//
+// Commonly reference value for NCP Server name length
+//
+#define NW_MAX_SERVER_LEN 48
+
+
+//
+// Flags used for the function NwParseNdsUncPath()
+//
+#define PARSE_NDS_GET_TREE_NAME 0
+#define PARSE_NDS_GET_PATH_NAME 1
+#define PARSE_NDS_GET_OBJECT_NAME 2
+
+
+WORD
+NwParseNdsUncPath(
+ IN OUT LPWSTR * Result,
+ IN LPWSTR ContainerName,
+ IN ULONG flag
+ );
+
+//
+// NDS Object class type identifiers
+//
+#define CLASS_TYPE_ALIAS 1
+#define CLASS_TYPE_AFP_SERVER 2
+#define CLASS_TYPE_BINDERY_OBJECT 3
+#define CLASS_TYPE_BINDERY_QUEUE 4
+#define CLASS_TYPE_COMPUTER 5
+#define CLASS_TYPE_COUNTRY 6
+#define CLASS_TYPE_DIRECTORY_MAP 7
+#define CLASS_TYPE_GROUP 8
+#define CLASS_TYPE_LOCALITY 9
+#define CLASS_TYPE_NCP_SERVER 10
+#define CLASS_TYPE_ORGANIZATION 11
+#define CLASS_TYPE_ORGANIZATIONAL_ROLE 12
+#define CLASS_TYPE_ORGANIZATIONAL_UNIT 13
+#define CLASS_TYPE_PRINTER 14
+#define CLASS_TYPE_PRINT_SERVER 15
+#define CLASS_TYPE_PROFILE 16
+#define CLASS_TYPE_QUEUE 17
+#define CLASS_TYPE_TOP 18
+#define CLASS_TYPE_UNKNOWN 19
+#define CLASS_TYPE_USER 20
+#define CLASS_TYPE_VOLUME 21
+
+#define CLASS_NAME_ALIAS L"Alias"
+#define CLASS_NAME_AFP_SERVER L"AFP Server"
+#define CLASS_NAME_BINDERY_OBJECT L"Bindery Object"
+#define CLASS_NAME_BINDERY_QUEUE L"Bindery Queue"
+#define CLASS_NAME_COMPUTER L"Computer"
+#define CLASS_NAME_COUNTRY L"Country"
+#define CLASS_NAME_DIRECTORY_MAP L"Directory Map"
+#define CLASS_NAME_GROUP L"Group"
+#define CLASS_NAME_LOCALITY L"Locality"
+#define CLASS_NAME_NCP_SERVER L"NCP Server"
+#define CLASS_NAME_ORGANIZATION L"Organization"
+#define CLASS_NAME_ORGANIZATIONAL_ROLE L"Organizational Role"
+#define CLASS_NAME_ORGANIZATIONAL_UNIT L"Organizational Unit"
+#define CLASS_NAME_PRINTER L"Printer"
+#define CLASS_NAME_PRINT_SERVER L"Print Server"
+#define CLASS_NAME_PROFILE L"Profile"
+#define CLASS_NAME_QUEUE L"Queue"
+#define CLASS_NAME_TOP L"Top"
+#define CLASS_NAME_UNKNOWN L"Unknown"
+#define CLASS_NAME_USER L"User"
+#define CLASS_NAME_VOLUME L"Volume"
+
+
+//
+// Node structure in the registered service link list and
+// functions to add/remove items from the link list
+//
+
+typedef struct _REGISTERED_SERVICE {
+ WORD nSapType; // SAP Type
+ BOOL fAdvertiseBySap; // TRUE if advertise by SAP agent
+ LPSERVICE_INFO pServiceInfo; // Info about this service
+ struct _REGISTERED_SERVICE *Next; // Points to the next service node
+} REGISTERED_SERVICE, *PREGISTERED_SERVICE;
+
+
+PREGISTERED_SERVICE
+GetServiceItemFromList(
+ IN WORD nSapType,
+ IN LPWSTR pServiceName
+);
+
+DWORD
+NwRegisterService(
+ IN LPSERVICE_INFO lpServiceInfo,
+ IN WORD nSapType,
+ IN HANDLE hEventHandle
+);
+
+DWORD
+NwDeregisterService(
+ IN LPSERVICE_INFO lpServiceInfo,
+ IN WORD nSapType
+);
+
+DWORD
+NwGetService(
+ IN LPWSTR Reserved,
+ IN WORD nSapType,
+ IN LPWSTR lpServiceName,
+ IN DWORD dwProperties,
+ OUT LPBYTE lpServiceInfo,
+ IN DWORD dwBufferLength,
+ OUT LPDWORD lpdwBytesNeeded
+);
+
+VOID
+NwInitializeServiceProvider(
+ VOID
+ );
+
+VOID
+NwTerminateServiceProvider(
+ VOID
+ );
+
+#endif // _NWMISC_INCLUDED_
diff --git a/private/nw/svcdlls/nwwks/inc/nwreg.h b/private/nw/svcdlls/nwwks/inc/nwreg.h
new file mode 100644
index 000000000..c7b4a162c
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/inc/nwreg.h
@@ -0,0 +1,85 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ nwreg.h
+
+Abstract:
+
+ Header which specifies the misc registry parameters and helper
+ routines used by the workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 22-Mar-1993
+
+Revision History:
+
+ ChuckC 11-Dec-93 Split off the registry names to nwrnames.h
+
+--*/
+
+#ifndef _NWREG_INCLUDED_
+#define _NWREG_INCLUDED_
+
+#include <nwrnames.h>
+
+//
+// Default print option
+//
+#define NW_PRINT_OPTION_DEFAULT 0x98
+#define NW_GATEWAY_PRINT_OPTION_DEFAULT 0x88
+
+#define NW_DOMAIN_USER_SEPARATOR L'*'
+#define NW_DOMAIN_USER_SEPARATOR_STR L"*"
+
+#define NW_MAX_LOGON_ID_LEN 17
+
+DWORD
+NwReadRegValue(
+ IN HKEY Key,
+ IN LPWSTR ValueName,
+ OUT LPWSTR *Value
+ );
+
+VOID
+NwLuidToWStr(
+ IN PLUID LogonId,
+ OUT LPWSTR LogonIdStr
+ );
+
+VOID
+NwWStrToLuid(
+ IN LPWSTR LogonIdStr,
+ OUT PLUID LogonId
+ );
+
+VOID
+NwDeleteCurrentUser(
+ VOID
+ );
+
+DWORD
+NwDeleteServiceLogon(
+ IN PLUID Id OPTIONAL
+ );
+
+DWORD
+NwpRegisterGatewayShare(
+ IN LPWSTR ShareName,
+ IN LPWSTR DriveName
+ );
+
+DWORD
+NwpClearGatewayShare(
+ IN LPWSTR ShareName
+ );
+
+DWORD
+NwpCleanupGatewayShares(
+ VOID
+ );
+
+#endif // _NWREG_INCLUDED_
diff --git a/private/nw/svcdlls/nwwks/inc/rnrdefs.h b/private/nw/svcdlls/nwwks/inc/rnrdefs.h
new file mode 100644
index 000000000..72c405180
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/inc/rnrdefs.h
@@ -0,0 +1,226 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ nwmisc.h
+
+Abstract:
+
+ Header which specifies the misc routines used by the workstation service.
+
+Author:
+
+ Arnold Miller (arnoldm) 15-Feb-1996
+
+Revision History:
+
+
+--*/
+#ifndef __RNRDEFS_H__
+#define __RNRDEFS_H__
+
+#include "sapcmn.h"
+
+//
+// Bit defs for the protocols
+//
+
+#define IPX_BIT 1
+#define SPX_BIT 2
+#define SPXII_BIT 4
+
+//
+// forwards\
+//
+
+struct _SAP_RNR_CONTEXT;
+//
+// Bindery control
+//
+
+typedef struct _BinderyControl
+{
+ LONG lIndex;
+} BINDERYCONTROL, *PBINDERYCONTROL;
+
+//
+// SAP RnR context information. This is linked off of the
+// SAP_BCAST_CONTROL defined ahead
+//
+
+typedef struct _SAP_DATA
+{
+ struct _SAP_DATA * sapNext;
+ // save everything except hop count
+ WORD sapid; // for a sanity check
+ CHAR sapname[48]; // what we don't know
+ BYTE socketAddr[IPX_ADDRESS_LENGTH]; // and what we seek
+} SAP_DATA, *PSAP_DATA;
+
+//
+//
+// Sap bcast control
+// An important note. fFlags is set only by the thread executing
+// a LookupServiceBegin or a LookupServiceNext. It may be tested by
+// any thread. Its counterpart, dwControlFlags in SAP_RNR_CONTEXT
+// is reserved for setting by LookupServiceBegin and LookupServiceEnd. Once
+// again any thread may look at it. This insures no loss of data on an
+// MP machine without needing a critical section.
+//
+
+typedef struct _SAP_BCAST_CONTROL
+{
+ DWORD dwIndex; // loop control
+ DWORD dwTickCount; // tick count of last send
+ DWORD fFlags; // various flags
+ PVOID pvArg;
+ SOCKET s;
+ CRITICAL_SECTION csMonitor; // This is to keep
+ // out internal structures sane. Note
+ // it does not provide rational
+ // serialization. In particular, if
+ // multiple threads use the same
+ // handle simultaneously, there is no
+ // guaranteed serialization.
+ PSAP_DATA psdNext1; // next to return
+ PSAP_DATA psdHead; // list head
+ PSAP_DATA psdTail;
+ struct _SAP_RNR_CONTEXT * psrc; // need this
+ DWORD (*Func)(PVOID pvArg1, PSAP_IDENT_HEADER pSap, PDWORD pdwErr);
+ BOOL (*fCheckCancel)(PVOID pvArg1);
+ WORD wQueryType;
+} SAP_BCAST_CONTROL, *PSAP_BCAST_CONTROL;
+
+//
+// Flags for above
+
+#define SBC_FLAG_NOMORE 0x1
+
+//
+// Structure used by the old RnR Sap lookup as the pvArg value in
+// SAP_BCAST control
+//
+
+#ifndef _NTDEF_
+typedef struct _STRING {
+ USHORT Length;
+ USHORT MaximumLength;
+#ifdef MIDL_PASS
+ [size_is(MaximumLength), length_is(Length) ]
+#endif // MIDL_PASS
+ PCHAR Buffer;
+} OEM_STRING;
+#endif
+
+typedef struct _OldRnRSap
+{
+ OEM_STRING * poem;
+ HANDLE hCancel;
+ LPVOID lpCsAddrBuffer;
+ LPDWORD lpdwBufferLength;
+ DWORD nProt;
+ LPDWORD lpcAddress;
+} OLDRNRSAP, *POLDRNRSAP;
+
+//
+// Return codes from the coroutine
+//
+
+#define dwrcDone 1 // all done, return success
+#define dwrcCancel 2 // all done, return cancelled
+#define dwrcNoWait 3 // keep going, but never wait.
+#define dwrcNil 4 // do whatever you want
+
+//
+// Sap service query packet format
+//
+
+typedef struct _SAP_REQUEST {
+ USHORT QueryType;
+ USHORT ServerType;
+} SAP_REQUEST, *PSAP_REQUEST;
+
+#define QT_GENERAL_QUERY 1
+#define QT_NEAREST_QUERY 3
+
+// The context information we put inside of an RNRNSHANDLE structure
+// to keep track of what we are doing
+// N.B. See comment on SAP_BCAST_CONTROL about the use of dwControlFlags.
+//
+
+typedef struct _SAP_RNR_CONTEXT
+{
+ struct _SAP_RNR_CONTEXT * pNextContext;
+ LONG lSig;
+ LONG lInUse;
+ DWORD dwCount; // count of queries made
+ DWORD fFlags; // always nice to have
+ DWORD dwControlFlags;
+ DWORD fConnectionOriented;
+ WORD wSapId; // the type desired
+ HANDLE Handle; // the corresponding RnR handle
+ DWORD nProt;
+ GUID gdType; // the type we are seeking
+ GUID gdProvider;
+ HANDLE hServer;
+ WCHAR wszContext[48];
+ WCHAR chwName[48]; // the name, if any
+ CHAR chName[48]; // OEM form of the name for SAP
+ DWORD dwUnionType; // type of lookup, once we know
+ union
+ {
+ SAP_BCAST_CONTROL sbc;
+ BINDERYCONTROL bc;
+ } u_type;
+ PVOID pvVersion; // a trick to get the version here.
+} SAP_RNR_CONTEXT, *PSAP_RNR_CONTEXT;
+
+#define RNR_SIG 0xaabbccdd
+//
+// union types
+//
+
+#define LOOKUP_TYPE_NIL 0
+#define LOOKUP_TYPE_SAP 1
+#define LOOKUP_TYPE_BINDERY 2
+
+
+#define SAP_F_END_CALLED 0x1 // generic cancel
+
+
+//
+// Defs for the bindery Class info
+// BUGBUG. These probably should be elsewhere
+// This defines the format of each ClassInfo property segement. It looks
+// somewhat like an actual ClassInfo, but considerably compressed. Note
+// due to marshalling problems, any complex value, such as a GUID,
+// should be stored as a string and then imported. Hence, we define
+// types for what we can anticipate.
+//
+
+typedef struct _BinderyClasses
+{
+ BYTE bType;
+ BYTE bSizeOfType;
+ BYTE bSizeOfString;
+ BYTE bOffset; // where the data area begins
+ BYTE bFlags;
+ BYTE bFiller;
+ WORD wNameSpace; // the applicable namespace
+ CHAR cDataArea[120]; // where the type and string are placed
+} BINDERYCLASSES, *PBINDERYCLASSES;
+
+#define BT_DWORD 1 // DWORD
+#define BT_WORD 2 // WORD
+#define BT_GUID 3 // a string GUID (ASCII)
+#define BT_STR 3 // an OEM string
+#define BT_OID 4 // an object ID (TBD)
+#define BT_BSTR 5 // a binary string (very dangerous)
+#define BT_WSTR 6 // UNICODE string. Unmarshalled!
+
+
+#define RNRTYPE "RNR_TYPE" // prop containing the GUID
+#define RNRCLASSES "RNR_CLASSES" // the other property
+#endif
diff --git a/private/nw/svcdlls/nwwks/inc/sapcmn.h b/private/nw/svcdlls/nwwks/inc/sapcmn.h
new file mode 100644
index 000000000..fa8f8449e
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/inc/sapcmn.h
@@ -0,0 +1,66 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ sapcmn.h.h
+
+Abstract:
+
+ Header containing the most basic common SAP definitions. This
+ was derived from the larger file, nwmisc.h in order to
+ be available to either the old RNR or the new RNR routines.
+
+Author:
+
+ Arnold Miller (ArnoldM) 8-Dec-95
+
+Revision History:
+
+ ArnoldM 8-Dec-95 Created from pieces of nwmisc.h
+--*/
+
+#ifndef __SAPCMN_H__
+#define __SAPCMN_H__
+//
+// Definitions common to client and server side files (getaddr.c and service.c)
+//
+
+#define IPX_ADDRESS_LENGTH 12
+#define IPX_ADDRESS_NETNUM_LENGTH 4
+#define SAP_ADDRESS_LENGTH 15
+#define SAP_ADVERTISE_FREQUENCY 60000 // 60 seconds
+#define SAP_MAXRECV_LENGTH 544
+#define SAP_OBJECT_NAME_MAX_LENGTH 48
+
+//
+// N.B. Keep the following defines in synch.
+//
+#define NW_RDR_PREFERRED_SERVER L"\\Device\\Nwrdr\\*"
+#define NW_RDR_NAME L"\\Device\\Nwrdr\\"
+#define NW_RDR_PREFERRED_SUFFIX L"*"
+//
+// Sap server identification packet format
+//
+
+typedef struct _SAP_IDENT_HEADER {
+ USHORT ServerType;
+ UCHAR ServerName[48];
+ UCHAR Address[IPX_ADDRESS_LENGTH];
+ USHORT HopCount;
+} SAP_IDENT_HEADER, *PSAP_IDENT_HEADER;
+
+
+//
+// Sap server identification packet format - Extended
+//
+
+typedef struct _SAP_IDENT_HEADER_EX {
+ USHORT ResponseType;
+ USHORT ServerType;
+ UCHAR ServerName[SAP_OBJECT_NAME_MAX_LENGTH];
+ UCHAR Address[IPX_ADDRESS_LENGTH];
+ USHORT HopCount;
+} SAP_IDENT_HEADER_EX, *PSAP_IDENT_HEADER_EX;
+#endif
diff --git a/private/nw/svcdlls/nwwks/inc/splutil.h b/private/nw/svcdlls/nwwks/inc/splutil.h
new file mode 100644
index 000000000..8e8169eac
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/inc/splutil.h
@@ -0,0 +1,81 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ splutil.h
+
+Abstract:
+
+ Header file for utilities used in Novell Print Provider
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 12-Apr-1993
+
+Revision History:
+
+--*/
+
+#ifndef _SPLUTIL_H_
+#define _SPLUTIL_H_
+
+#define offsetof(type, identifier) (DWORD)(&(((type)0)->identifier))
+
+extern DWORD PrinterInfo1Offsets[];
+extern DWORD PrinterInfo2Offsets[];
+extern DWORD PrinterInfo3Offsets[];
+extern DWORD JobInfo1Offsets[];
+extern DWORD JobInfo2Offsets[];
+extern DWORD AddJobInfo1Offsets[];
+
+VOID
+MarshallUpStructure(
+ LPBYTE lpStructure,
+ LPDWORD lpOffsets,
+ LPBYTE lpBufferStart
+);
+
+VOID
+MarshallDownStructure(
+ LPBYTE lpStructure,
+ LPDWORD lpOffsets,
+ LPBYTE lpBufferStart
+);
+
+LPVOID
+AllocNwSplMem(
+ IN DWORD flags,
+ IN DWORD cb
+ );
+
+VOID
+FreeNwSplMem(
+ IN LPVOID pMem,
+ IN DWORD cb
+ );
+
+LPWSTR
+AllocNwSplStr(
+ IN LPWSTR pStr
+ );
+
+VOID
+FreeNwSplStr(
+ IN LPWSTR pStr
+);
+
+BOOL
+ValidateUNCName(
+ IN LPWSTR pName
+);
+
+LPWSTR
+GetNextElement(
+ OUT LPWSTR *pPtr,
+ IN WCHAR token
+);
+
+
+#endif // _SPLUTIL_H
diff --git a/private/nw/svcdlls/nwwks/lib/lsa.c b/private/nw/svcdlls/nwwks/lib/lsa.c
new file mode 100644
index 000000000..1dc664aa4
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/lib/lsa.c
@@ -0,0 +1,537 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ lsa.c
+
+Abstract:
+
+ This module provides helpers to call LSA, particularly for
+ manipulating secret objects.
+
+Author:
+
+ Rita Wong (ritaw) 22-Apr-1993
+
+--*/
+
+#include <stdlib.h>
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#include <windef.h>
+#include <winerror.h>
+#include <winbase.h>
+
+#include <nwlsa.h>
+
+//-------------------------------------------------------------------//
+// //
+// Constants and Macros //
+// //
+//-------------------------------------------------------------------//
+
+#define NW_SECRET_PREFIX L"_MS_NWCS_"
+
+
+DWORD
+NwOpenPolicy(
+ IN ACCESS_MASK DesiredAccess,
+ OUT LSA_HANDLE *PolicyHandle
+ )
+/*++
+
+Routine Description:
+
+ This function gets a handle to the local security policy by calling
+ LsaOpenPolicy.
+
+Arguments:
+
+ DesiredAccess - Supplies the desired access to the local security
+ policy.
+
+ PolicyHandle - Receives a handle to the opened policy.
+
+Return Value:
+
+ NO_ERROR - Policy handle is returned.
+
+ Error from LSA.
+
+--*/
+{
+ NTSTATUS ntstatus;
+ OBJECT_ATTRIBUTES ObjAttributes;
+
+
+ //
+ // Open a handle to the local security policy. Initialize the
+ // objects attributes structure first.
+ //
+ InitializeObjectAttributes(
+ &ObjAttributes,
+ NULL,
+ 0L,
+ NULL,
+ NULL
+ );
+
+ ntstatus = LsaOpenPolicy(
+ NULL,
+ &ObjAttributes,
+ DesiredAccess,
+ PolicyHandle
+ );
+
+ if (! NT_SUCCESS(ntstatus)) {
+ KdPrint(("NWWORKSTATION: LsaOpenPolicy returns %08lx\n",
+ ntstatus));
+
+ return RtlNtStatusToDosError(ntstatus);
+ }
+
+ return NO_ERROR;
+}
+
+
+DWORD
+NwOpenSecret(
+ IN ACCESS_MASK DesiredAccess,
+ IN LSA_HANDLE PolicyHandle,
+ IN LPWSTR LsaSecretName,
+ OUT PLSA_HANDLE SecretHandle
+ )
+/*++
+
+Routine Description:
+
+ This function opens a handle to the specified secret object.
+
+Arguments:
+
+ DesiredAccess - Supplies the desired access to the secret object.
+
+ PolicyHandle - Supplies a handle to an already opened LSA policy.
+
+ LsaSecretName - Supplies the name of the secret to open.
+
+ SecretHandle - Receives the handle of the opened secret.
+
+Return Value:
+
+ NO_ERROR - Secret handle is returned.
+
+ Error from LSA.
+
+--*/
+{
+ NTSTATUS ntstatus;
+ UNICODE_STRING SecretNameString;
+
+
+ RtlInitUnicodeString(&SecretNameString, LsaSecretName);
+
+ ntstatus = LsaOpenSecret(
+ PolicyHandle,
+ &SecretNameString,
+ DesiredAccess,
+ SecretHandle
+ );
+
+
+ if (! NT_SUCCESS(ntstatus)) {
+ KdPrint(("NWWORKSTATION: LsaOpenSecret %ws returns %08lx\n",
+ LsaSecretName, ntstatus));
+
+ return RtlNtStatusToDosError(ntstatus);
+ }
+
+ return NO_ERROR;
+}
+
+
+DWORD
+NwFormSecretName(
+ IN LPWSTR Qualifier,
+ OUT LPWSTR *LsaSecretName
+ )
+/*++
+
+Routine Description:
+
+ This function creates a secret name from the user name.
+ It also allocates the buffer to return the created secret name which
+ must be freed by the caller using LocalFree when done with it.
+
+Arguments:
+
+ Qualifier - Supplies the qualifier which forms part of part of the secret
+ object name we are creating.
+
+ LsaSecretName - Receives a pointer to the buffer which contains the
+ secret object name.
+
+Return Value:
+
+ NO_ERROR - Successfully returned secret name.
+
+ ERROR_NOT_ENOUGH_MEMORY - Failed to allocate buffer to hold the secret
+ name.
+
+--*/
+{
+ if ((*LsaSecretName = (LPWSTR)LocalAlloc(
+ 0,
+ (wcslen(NW_SECRET_PREFIX) +
+ wcslen(Qualifier) +
+ 1) * sizeof(WCHAR)
+ )) == NULL) {
+
+ KdPrint(("NWWORKSTATION: NwFormSecretName: LocalAlloc failed %lu\n",
+ GetLastError()));
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ wcscpy(*LsaSecretName, NW_SECRET_PREFIX);
+ wcscat(*LsaSecretName, Qualifier);
+
+ return NO_ERROR;
+}
+
+
+
+DWORD
+NwSetPassword(
+ IN LPWSTR Qualifier,
+ IN LPWSTR Password
+ )
+/*++
+
+Routine Description:
+
+ This function opens ( creates one if needed ) the LSA secret object,
+ and sets it with the specified password.
+
+Arguments:
+
+ Qualifier - Supplies the qualifier which forms part of part of the secret
+ object name to be created.
+
+ Password - Supplies the user specified password for an account.
+
+Return Value:
+
+ NO_ERROR - Secret object for the password is created and set with new value.
+
+ Error from LSA.
+
+--*/
+{
+ DWORD status;
+ NTSTATUS ntstatus;
+ LSA_HANDLE PolicyHandle;
+
+ LSA_HANDLE SecretHandle;
+ UNICODE_STRING SecretNameString;
+ LPWSTR LsaSecretName;
+
+ UNICODE_STRING NewPasswordString;
+ UNICODE_STRING OldPasswordString;
+
+
+ //
+ // Open a handle to the local security policy.
+ //
+ if ((status = NwOpenPolicy(
+ POLICY_CREATE_SECRET,
+ &PolicyHandle
+ )) != NO_ERROR) {
+ KdPrint(("NWWORKSTATION: NwCreatePassword: NwOpenPolicy failed\n"));
+ return status;
+ }
+
+ //
+ // Create the LSA secret object. But first, form a secret name.
+ //
+ if ((status = NwFormSecretName(
+ Qualifier,
+ &LsaSecretName
+ )) != NO_ERROR) {
+ (void) LsaClose(PolicyHandle);
+ return status;
+ }
+
+ RtlInitUnicodeString(&SecretNameString, LsaSecretName);
+
+ ntstatus = LsaCreateSecret(
+ PolicyHandle,
+ &SecretNameString,
+ SECRET_SET_VALUE | DELETE,
+ &SecretHandle
+ );
+
+
+ //
+ // note: ignore object already exists error. just use the existing
+ // object. this could be because the user didnt completely cleanup
+ // during deinstall. the unique names makes it unlikely to be a real
+ // collision.
+ //
+ if ( ntstatus == STATUS_OBJECT_NAME_COLLISION ) {
+ ntstatus = NwOpenSecret(
+ SECRET_SET_VALUE,
+ PolicyHandle,
+ LsaSecretName,
+ &SecretHandle
+ );
+ }
+
+ //
+ // Don't need the name or policy handle anymore.
+ //
+ (void) LocalFree((HLOCAL) LsaSecretName);
+ (void) LsaClose(PolicyHandle);
+
+ if (! NT_SUCCESS(ntstatus)) {
+
+ KdPrint(("NWWORKSTATION: NwCreatePassword: LsaCreateSecret or LsaOpenSecret returned %08lx for %ws\n", ntstatus, LsaSecretName));
+
+ return RtlNtStatusToDosError(ntstatus);
+ }
+
+
+ RtlInitUnicodeString(&OldPasswordString, NULL);
+ RtlInitUnicodeString(&NewPasswordString, Password);
+
+ ntstatus = LsaSetSecret(
+ SecretHandle,
+ &NewPasswordString,
+ &OldPasswordString
+ );
+
+ if (! NT_SUCCESS(ntstatus)) {
+ KdPrint(("NWWORKSTATION NwCreatePassword: LsaSetSecret returned %08lx\n",
+ ntstatus));
+
+ status = RtlNtStatusToDosError(ntstatus);
+
+ //
+ // Delete the secret object
+ //
+ ntstatus = LsaDelete(SecretHandle);
+
+ if (! NT_SUCCESS(ntstatus)) {
+ KdPrint(("NWWORKSTATION: NwCreatePassword: LsaDelete to restore back to original returned %08lx\n",
+ ntstatus));
+ (void) LsaClose(SecretHandle);
+ }
+
+ //
+ // Secret handle is closed by LsaDelete if successfully deleted.
+ //
+ return status;
+ }
+
+ (void) LsaClose(SecretHandle);
+
+ return NO_ERROR;
+}
+
+
+
+DWORD
+NwDeletePassword(
+ IN LPWSTR Qualifier
+ )
+/*++
+
+Routine Description:
+
+ This function deletes the LSA secret object whose name is derived
+ from the specified Qualifier.
+
+Arguments:
+
+ Qualifier - Supplies the qualifier which forms part of part of the secret
+ object name to be deleted.
+
+Return Value:
+
+ NO_ERROR - Secret object for password is deleted.
+
+ Error from LSA.
+
+--*/
+{
+ DWORD status;
+ NTSTATUS ntstatus;
+
+ LSA_HANDLE PolicyHandle;
+
+ LSA_HANDLE SecretHandle;
+ LPWSTR LsaSecretName;
+
+
+ //
+ // Open a handle to the local security policy.
+ //
+ if ((status = NwOpenPolicy(
+ POLICY_VIEW_LOCAL_INFORMATION,
+ &PolicyHandle
+ )) != NO_ERROR) {
+ KdPrint(("NWWORKSTATION: NwDeletePassword: NwOpenPolicy failed\n"));
+ return status;
+ }
+
+ //
+ // Get the secret object name from the specified user name.
+ //
+ if ((status = NwFormSecretName(
+ Qualifier,
+ &LsaSecretName
+ )) != NO_ERROR) {
+ (void) LsaClose(PolicyHandle);
+ return status;
+ }
+
+ status = NwOpenSecret(
+ DELETE,
+ PolicyHandle,
+ LsaSecretName,
+ &SecretHandle
+ );
+
+ //
+ // Don't need the name or policy handle anymore.
+ //
+ (void) LocalFree((HLOCAL) LsaSecretName);
+ (void) LsaClose(PolicyHandle);
+
+ if (status != NO_ERROR) {
+ KdPrint(("NWWORKSTATION: NwDeletePassword: NwOpenSecret failed\n"));
+ return status;
+ }
+
+ ntstatus = LsaDelete(SecretHandle);
+
+ if (! NT_SUCCESS(ntstatus)) {
+ KdPrint(("NWWORKSTATION: NwDeletePassword: LsaDelete returned %08lx\n",
+ ntstatus));
+ (void) LsaClose(SecretHandle);
+ return RtlNtStatusToDosError(ntstatus);
+ }
+
+ return NO_ERROR;
+}
+
+
+
+DWORD
+NwGetPassword(
+ IN LPWSTR Qualifier,
+ OUT PUNICODE_STRING *Password,
+ OUT PUNICODE_STRING *OldPassword
+ )
+/*++
+
+Routine Description:
+
+ This function retrieves the current password and old password
+ values from the service secret object given the user name.
+
+Arguments:
+
+ Qualifier - Supplies the qualifier which forms part of the key to the
+ secret object name.
+
+ Password - Receives a pointer to the string structure that contains
+ the password.
+
+ OldPassword - Receives a pointer to the string structure that
+ contains the old password.
+
+Return Value:
+
+ NO_ERROR - Secret object for password is changed to new value.
+
+ Error from LSA.
+
+--*/
+{
+ DWORD status;
+ NTSTATUS ntstatus;
+
+ LSA_HANDLE PolicyHandle;
+ LSA_HANDLE SecretHandle;
+
+ LPWSTR LsaSecretName;
+
+
+ //
+ // Open a handle to the local security policy to read the
+ // value of the secret.
+ //
+ if ((status = NwOpenPolicy(
+ POLICY_VIEW_LOCAL_INFORMATION,
+ &PolicyHandle
+ )) != NO_ERROR) {
+ return status;
+ }
+
+ //
+ // Get the secret object name from the specified user name.
+ //
+ if ((status = NwFormSecretName(
+ Qualifier,
+ &LsaSecretName
+ )) != NO_ERROR) {
+ (void) LsaClose(PolicyHandle);
+ return status;
+ }
+
+ status = NwOpenSecret(
+ SECRET_QUERY_VALUE,
+ PolicyHandle,
+ LsaSecretName,
+ &SecretHandle
+ );
+
+ //
+ // Don't need the name or policy handle anymore.
+ //
+ (void) LocalFree((HLOCAL) LsaSecretName);
+ (void) LsaClose(PolicyHandle);
+
+ if (status != NO_ERROR) {
+ KdPrint(("NWWORKSTATION: ScGetSecret: ScOpenSecret failed\n"));
+ return status;
+ }
+
+ //
+ // Query the old value of the secret object so that we can
+ // we can restore it if we fail to change the password later.
+ //
+ ntstatus = LsaQuerySecret(
+ SecretHandle,
+ Password,
+ NULL, // don't need set time
+ OldPassword,
+ NULL // don't need set time
+ );
+
+ (void) LsaClose(SecretHandle);
+
+ if (! NT_SUCCESS(ntstatus)) {
+ KdPrint(("NWWORKSTATION: NwGetPassword: LsaQuerySecret for previous values returned %08lx\n",
+ ntstatus));
+
+ return RtlNtStatusToDosError(ntstatus);
+ }
+
+ return NO_ERROR;
+}
diff --git a/private/nw/svcdlls/nwwks/lib/makefile b/private/nw/svcdlls/nwwks/lib/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/lib/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/nw/svcdlls/nwwks/lib/misc.c b/private/nw/svcdlls/nwwks/lib/misc.c
new file mode 100644
index 000000000..e28db0a84
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/lib/misc.c
@@ -0,0 +1,785 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ api.c
+
+Abstract:
+
+ This module contains misc APIs that are used by the
+ NWC wksta.
+
+Author:
+
+ ChuckC 2-Mar-94 Created
+
+
+Revision History:
+
+--*/
+
+
+#include <stdlib.h>
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <nwcons.h>
+#include <nwmisc.h>
+#include <nwapi32.h>
+#include "nwstatus.h"
+#include "nwevent.h"
+
+DWORD
+NwMapStatus(
+ IN NTSTATUS NtStatus
+ );
+
+DWORD
+NwOpenPreferredServer(
+ PHANDLE ServerHandle
+ );
+
+NTSTATUS
+NwOpenHandle(
+ IN PUNICODE_STRING ObjectName,
+ IN BOOL ValidateFlag,
+ OUT PHANDLE ObjectHandle
+ );
+
+NTSTATUS
+NwCallNtOpenFile(
+ OUT PHANDLE ObjectHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN PUNICODE_STRING ObjectName,
+ IN ULONG OpenOptions
+ );
+
+
+//
+// list of error mappings known for E3H calls. we do not have a single list
+// because Netware reuses the numbers depending on call.
+//
+
+typedef struct _ERROR_MAP_ENTRY
+{
+ UCHAR NetError;
+ NTSTATUS ResultingStatus;
+} ERROR_MAP_ENTRY ;
+
+ERROR_MAP_ENTRY Error_Map_Bindery[] =
+{
+
+ //
+ // NetWare specific error mappings. Specific to E3H.
+ //
+ { 1, STATUS_DISK_FULL },
+ {128, STATUS_SHARING_VIOLATION },
+ {129, STATUS_INSUFF_SERVER_RESOURCES },
+ {130, STATUS_ACCESS_DENIED },
+ {131, STATUS_DATA_ERROR },
+ {132, STATUS_ACCESS_DENIED },
+ {133, STATUS_ACCESS_DENIED },
+ {134, STATUS_ACCESS_DENIED },
+ {135, STATUS_OBJECT_NAME_INVALID },
+ {136, STATUS_INVALID_HANDLE },
+ {137, STATUS_ACCESS_DENIED },
+ {138, STATUS_ACCESS_DENIED },
+ {139, STATUS_ACCESS_DENIED },
+ {140, STATUS_ACCESS_DENIED },
+ {141, STATUS_SHARING_VIOLATION },
+ {142, STATUS_SHARING_VIOLATION },
+ {143, STATUS_ACCESS_DENIED },
+ {144, STATUS_ACCESS_DENIED },
+ {145, STATUS_OBJECT_NAME_COLLISION },
+ {146, STATUS_OBJECT_NAME_COLLISION },
+ {147, STATUS_ACCESS_DENIED },
+ {148, STATUS_ACCESS_DENIED },
+ {150, STATUS_INSUFF_SERVER_RESOURCES },
+ {151, STATUS_NO_SPOOL_SPACE },
+ {152, STATUS_NO_SUCH_DEVICE },
+ {153, STATUS_DISK_FULL },
+ {154, STATUS_NOT_SAME_DEVICE },
+ {155, STATUS_INVALID_HANDLE },
+ {156, STATUS_OBJECT_PATH_NOT_FOUND },
+ {157, STATUS_INSUFF_SERVER_RESOURCES },
+ {158, STATUS_OBJECT_PATH_INVALID },
+ {159, STATUS_SHARING_VIOLATION },
+ {160, STATUS_DIRECTORY_NOT_EMPTY },
+ {161, STATUS_DATA_ERROR },
+ {162, STATUS_SHARING_VIOLATION },
+ {192, STATUS_ACCESS_DENIED },
+ {198, STATUS_ACCESS_DENIED },
+ {211, STATUS_ACCESS_DENIED },
+ {212, STATUS_PRINT_QUEUE_FULL },
+ {213, STATUS_PRINT_CANCELLED },
+ {214, STATUS_ACCESS_DENIED },
+ {215, STATUS_PASSWORD_RESTRICTION },
+ {216, STATUS_PASSWORD_RESTRICTION },
+ {220, STATUS_ACCOUNT_DISABLED },
+ {222, STATUS_PASSWORD_EXPIRED },
+ {223, STATUS_PASSWORD_EXPIRED },
+ {239, STATUS_OBJECT_NAME_INVALID },
+ {240, STATUS_OBJECT_NAME_INVALID },
+ {251, STATUS_INVALID_PARAMETER },
+ {252, STATUS_NO_MORE_ENTRIES },
+ {253, STATUS_FILE_LOCK_CONFLICT },
+ {254, STATUS_FILE_LOCK_CONFLICT },
+ {255, STATUS_UNSUCCESSFUL}
+};
+
+
+ERROR_MAP_ENTRY Error_Map_General[] =
+{
+ { 1, STATUS_DISK_FULL },
+ {128, STATUS_SHARING_VIOLATION },
+ {129, STATUS_INSUFF_SERVER_RESOURCES },
+ {130, STATUS_ACCESS_DENIED },
+ {131, STATUS_DATA_ERROR },
+ {132, STATUS_ACCESS_DENIED },
+ {133, STATUS_ACCESS_DENIED },
+ {134, STATUS_ACCESS_DENIED },
+ {135, STATUS_OBJECT_NAME_INVALID },
+ {136, STATUS_INVALID_HANDLE },
+ {137, STATUS_ACCESS_DENIED },
+ {138, STATUS_ACCESS_DENIED },
+ {139, STATUS_ACCESS_DENIED },
+ {140, STATUS_ACCESS_DENIED },
+ {141, STATUS_SHARING_VIOLATION },
+ {142, STATUS_SHARING_VIOLATION },
+ {143, STATUS_ACCESS_DENIED },
+ {144, STATUS_ACCESS_DENIED },
+ {145, STATUS_OBJECT_NAME_COLLISION },
+ {146, STATUS_OBJECT_NAME_COLLISION },
+ {147, STATUS_ACCESS_DENIED },
+ {148, STATUS_ACCESS_DENIED },
+ {150, STATUS_INSUFF_SERVER_RESOURCES },
+ {151, STATUS_NO_SPOOL_SPACE },
+ {152, STATUS_NO_SUCH_DEVICE },
+ {153, STATUS_DISK_FULL },
+ {154, STATUS_NOT_SAME_DEVICE },
+ {155, STATUS_INVALID_HANDLE },
+ {156, STATUS_OBJECT_PATH_NOT_FOUND },
+ {157, STATUS_INSUFF_SERVER_RESOURCES },
+ {158, STATUS_OBJECT_PATH_INVALID },
+ {159, STATUS_SHARING_VIOLATION },
+ {160, STATUS_DIRECTORY_NOT_EMPTY },
+ {161, STATUS_DATA_ERROR },
+ {162, STATUS_SHARING_VIOLATION },
+ {192, STATUS_ACCESS_DENIED },
+ {198, STATUS_ACCESS_DENIED },
+ {211, STATUS_ACCESS_DENIED },
+ {212, STATUS_PRINT_QUEUE_FULL },
+ {213, STATUS_PRINT_CANCELLED },
+ {214, STATUS_ACCESS_DENIED },
+ {215, STATUS_DEVICE_BUSY },
+ {216, STATUS_DEVICE_DOES_NOT_EXIST },
+ {220, STATUS_ACCOUNT_DISABLED },
+ {222, STATUS_PASSWORD_EXPIRED },
+ {223, STATUS_PASSWORD_EXPIRED },
+ {239, STATUS_OBJECT_NAME_INVALID },
+ {240, STATUS_OBJECT_NAME_INVALID },
+ {251, STATUS_INVALID_PARAMETER },
+ {252, STATUS_NO_MORE_ENTRIES },
+ {253, STATUS_FILE_LOCK_CONFLICT },
+ {254, STATUS_FILE_LOCK_CONFLICT },
+ {255, STATUS_UNSUCCESSFUL}
+};
+
+#define NUM_ERRORS(x) (sizeof(x)/sizeof(x[0]))
+
+DWORD
+NwMapBinderyCompletionCode(
+ IN NTSTATUS NtStatus
+ )
+/*++
+
+Routine Description:
+
+ This function takes a bindery completion code embedded in an NT status
+ code and maps it to the appropriate Win32 error code. Used specifically
+ for E3H operations.
+
+Arguments:
+
+ NtStatus - Supplies the NT status (that contains the code in low 16 bits)
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+ DWORD i; UCHAR code ;
+
+ //
+ // A small optimization for the most common case.
+ //
+ if (NtStatus == STATUS_SUCCESS)
+ return NO_ERROR;
+
+ //
+ // Map connection errors specially.
+ //
+
+ if ( ( (NtStatus & 0xFFFF0000) == 0xC0010000) &&
+ ( (NtStatus & 0xFF00) != 0 ) )
+ {
+ return ERROR_UNEXP_NET_ERR;
+ }
+
+ //
+ // if facility code not set, assume it is NT Status
+ //
+ if ( (NtStatus & 0xFFFF0000) != 0xC0010000)
+ return RtlNtStatusToDosError(NtStatus);
+
+ code = NtStatus & 0x000000FF ;
+ for (i = 0; i < NUM_ERRORS(Error_Map_Bindery); i++)
+ {
+ if (Error_Map_Bindery[i].NetError == code)
+ return( NwMapStatus(Error_Map_Bindery[i].ResultingStatus));
+ }
+
+ //
+ // if cannot find let NwMapStatus do its best
+ //
+ return NwMapStatus(NtStatus);
+}
+
+
+
+DWORD
+NwMapStatus(
+ IN NTSTATUS NtStatus
+ )
+/*++
+
+Routine Description:
+
+ This function takes an NT status code and maps it to the appropriate
+ Win32 error code. If facility code is set, assume it is NW specific
+
+Arguments:
+
+ NtStatus - Supplies the NT status.
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+ DWORD i; UCHAR code ;
+
+ //
+ // A small optimization for the most common case.
+ //
+ if (NtStatus == STATUS_SUCCESS)
+ return NO_ERROR;
+
+ //
+ // Map connection errors specially.
+ //
+
+ if ( ( (NtStatus & 0xFFFF0000) == 0xC0010000) &&
+ ( (NtStatus & 0xFF00) != 0 ) )
+ {
+ return ERROR_UNEXP_NET_ERR;
+ }
+
+ //
+ // if facility code set, assume it is NW Completion code
+ //
+ if ( (NtStatus & 0xFFFF0000) == 0xC0010000)
+ {
+ code = NtStatus & 0x000000FF ;
+ for (i = 0; i < NUM_ERRORS(Error_Map_General); i++)
+ {
+ if (Error_Map_General[i].NetError == code)
+ {
+ //
+ // map it to NTSTATUS and then drop thru to map to Win32
+ //
+ NtStatus = Error_Map_General[i].ResultingStatus ;
+ break ;
+ }
+ }
+ }
+
+ switch (NtStatus) {
+ case STATUS_OBJECT_NAME_COLLISION:
+ return ERROR_ALREADY_ASSIGNED;
+
+ case STATUS_OBJECT_NAME_NOT_FOUND:
+ return ERROR_NOT_CONNECTED;
+
+ case STATUS_IMAGE_ALREADY_LOADED:
+ case STATUS_REDIRECTOR_STARTED:
+ return ERROR_SERVICE_ALREADY_RUNNING;
+
+ case STATUS_REDIRECTOR_HAS_OPEN_HANDLES:
+ return ERROR_REDIRECTOR_HAS_OPEN_HANDLES;
+
+ case STATUS_NO_MORE_FILES:
+ case STATUS_NO_MORE_ENTRIES:
+ return WN_NO_MORE_ENTRIES;
+
+ case STATUS_MORE_ENTRIES:
+ return WN_MORE_DATA;
+
+ case STATUS_CONNECTION_IN_USE:
+ return ERROR_DEVICE_IN_USE;
+
+ case NWRDR_PASSWORD_HAS_EXPIRED:
+ return NW_PASSWORD_HAS_EXPIRED;
+
+ case STATUS_INVALID_DEVICE_REQUEST:
+ return ERROR_CONNECTION_INVALID;
+
+ default:
+ return RtlNtStatusToDosError(NtStatus);
+ }
+}
+
+DWORD
+NwGetGraceLoginCount(
+ LPWSTR Server,
+ LPWSTR UserName,
+ LPDWORD lpResult
+ )
+/*++
+
+Routine Description:
+
+ Get the number grace logins for a user.
+
+Arguments:
+
+ Server - the server to authenticate against
+
+ UserName - the user account
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+ DWORD status ;
+ HANDLE hConn ;
+ CHAR UserNameO[NW_MAX_USERNAME_LEN+1] ;
+ BYTE LoginControl[128] ;
+ BYTE MoreFlags, PropFlags ;
+
+ //
+ // skip the backslashes if present
+ //
+ if (*Server == L'\\')
+ Server += 2 ;
+
+ //
+ // attach to the NW server
+ //
+ if (status = NWAttachToFileServerW(Server,
+ 0,
+ &hConn))
+ {
+ return status ;
+ }
+
+ //
+ // convert unicode UserName to OEM, and then call the NCP
+ //
+ if ( !WideCharToMultiByte(CP_OEMCP,
+ 0,
+ UserName,
+ -1,
+ UserNameO,
+ sizeof(UserNameO),
+ NULL,
+ NULL))
+ {
+ status = GetLastError() ;
+ }
+ else
+ {
+ status = NWReadPropertyValue( hConn,
+ UserNameO,
+ OT_USER,
+ "LOGIN_CONTROL",
+ 1,
+ LoginControl,
+ &MoreFlags,
+ &PropFlags) ;
+ }
+
+ //
+ // dont need these anymore. if any error, bag out
+ //
+ (void) NWDetachFromFileServer(hConn) ;
+
+
+ if (status == NO_ERROR)
+ *lpResult = (DWORD) LoginControl[7] ;
+
+ return status ;
+}
+
+
+WORD
+NwParseNdsUncPath(
+ IN OUT LPWSTR * Result,
+ IN LPWSTR ContainerName,
+ IN ULONG flag
+)
+/*++
+
+Routine Description:
+
+ This function is used to extract either the tree name, fully distinguished
+ name path to an object, or object name, out of a complete NDS UNC path.
+
+Arguments:
+
+ Result - parsed result buffer.
+ ContainerName - Complete NDS UNC path that is to be parsed.
+ flag - Flag indicating operation to be performed:
+
+ PARSE_NDS_GET_TREE_NAME
+ PARSE_NDS_GET_PATH_NAME
+ PARSE_NDS_GET_OBJECT_NAME
+
+
+Return Value:
+
+ Length of string in result buffer. If error occured, 0 is returned.
+
+--*/ // NwParseNdsUncPath
+{
+ unsigned short length = 2;
+ unsigned short totalLength = wcslen( ContainerName );
+
+ if ( totalLength < 2 )
+ return 0;
+
+ //
+ // First get length to indicate the character in the string that indicates the
+ // "\" in between the tree name and the rest of the UNC path.
+ //
+ // Example: \\<tree name>\<path to object>[\|.]<object>
+ // ^
+ // |
+ //
+ while ( length < totalLength && ContainerName[length] != L'\\' )
+ {
+ length++;
+ }
+
+ if ( flag == PARSE_NDS_GET_TREE_NAME )
+ {
+ *Result = (LPWSTR) ( ContainerName + 2 );
+
+ return ( length - 2 ) * sizeof( WCHAR ); // Take off 2 for the two \\'s
+ }
+
+ if ( flag == PARSE_NDS_GET_PATH_NAME && length == totalLength )
+ {
+ *Result = ContainerName;
+
+ return 0;
+ }
+
+ if ( flag == PARSE_NDS_GET_PATH_NAME )
+ {
+ *Result = ContainerName + length + 1;
+
+ return ( totalLength - length - 1 ) * sizeof( WCHAR );
+ }
+
+ *Result = ContainerName + totalLength - 1;
+ length = 1;
+
+ while ( **Result != L'\\' )
+ {
+ *Result--;
+ length++;
+ }
+
+ *Result++;
+ length--;
+
+ return length * sizeof( WCHAR );
+}
+
+
+DWORD
+NwOpenAServer(
+ PWCHAR pwszServName,
+ PHANDLE ServerHandle,
+ BOOL fVerify
+ )
+/*++
+
+Routine Description:
+
+ This routine opens a handle to a server.
+
+Arguments:
+
+ ServerHandle - Receives an opened handle to the preferred or
+ nearest server.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+{
+ UNICODE_STRING AServer;
+ WCHAR wszName[sizeof(NW_RDR_NAME) + (48 * sizeof(WCHAR))];
+ DWORD wLen;
+
+
+ if(!pwszServName)
+ {
+ pwszServName = NW_RDR_PREFERRED_SERVER;
+ RtlInitUnicodeString(&AServer, wszName);
+ }
+ else
+ {
+ wLen = wcslen(pwszServName);
+ if(wLen > 47)
+ {
+ return(WSAEFAULT);
+ }
+ wcscpy(wszName, NW_RDR_NAME);
+ wcscat(wszName, pwszServName);
+ RtlInitUnicodeString(&AServer, wszName);
+ }
+
+ return RtlNtStatusToDosError(
+ NwOpenHandle(&AServer, fVerify, ServerHandle)
+ );
+
+}
+
+
+DWORD
+NwOpenPreferredServer(
+ PHANDLE ServerHandle
+ )
+/*++
+
+Routine Description:
+
+ This routine opens a handle to the preferred server. If the
+ preferred server has not been specified, a handle to the
+ nearest server is opened instead.
+
+Arguments:
+
+ ServerHandle - Receives an opened handle to the preferred or
+ nearest server.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+{
+ UNICODE_STRING PreferredServer;
+
+
+ //
+ // The NetWare redirector recognizes "*" to mean the preferred
+ // or nearest server.
+ //
+ RtlInitUnicodeString(&PreferredServer, NW_RDR_PREFERRED_SERVER);
+
+ return RtlNtStatusToDosError(
+ NwOpenHandle(&PreferredServer, FALSE, ServerHandle)
+ );
+
+}
+
+
+NTSTATUS
+NwOpenHandle(
+ IN PUNICODE_STRING ObjectName,
+ IN BOOL ValidateFlag,
+ OUT PHANDLE ObjectHandle
+ )
+/*++
+
+Routine Description:
+
+ This function opens a handle to \Device\Nwrdr\<ObjectName>.
+
+Arguments:
+
+ ObjectName - Supplies the name of the redirector object to open.
+
+ ValidateFlag - Supplies a flag which if TRUE, opens the handle to
+ the object by validating the default user account.
+
+ ObjectHandle - Receives a pointer to the opened object handle.
+
+Return Value:
+
+ STATUS_SUCCESS or reason for failure.
+
+--*/
+{
+ ACCESS_MASK DesiredAccess = SYNCHRONIZE;
+
+
+ if (ValidateFlag) {
+
+ //
+ // The redirector only authenticates the default user credential
+ // if the remote resource is opened with write access.
+ //
+ DesiredAccess |= FILE_WRITE_DATA;
+ }
+
+
+ *ObjectHandle = NULL;
+
+ return NwCallNtOpenFile(
+ ObjectHandle,
+ DesiredAccess,
+ ObjectName,
+ FILE_SYNCHRONOUS_IO_NONALERT
+ );
+
+}
+
+
+NTSTATUS
+NwCallNtOpenFile(
+ OUT PHANDLE ObjectHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN PUNICODE_STRING ObjectName,
+ IN ULONG OpenOptions
+ )
+{
+
+ NTSTATUS ntstatus;
+ IO_STATUS_BLOCK IoStatusBlock;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+
+
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ ObjectName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ ntstatus = NtOpenFile(
+ ObjectHandle,
+ DesiredAccess,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_VALID_FLAGS,
+ OpenOptions
+ );
+
+ if (!NT_ERROR(ntstatus) &&
+ !NT_INFORMATION(ntstatus) &&
+ !NT_WARNING(ntstatus)) {
+
+ ntstatus = IoStatusBlock.Status;
+
+ }
+
+ return ntstatus;
+}
+
+
+BOOL
+NwConvertToUnicode(
+ OUT LPWSTR *UnicodeOut,
+ IN LPSTR OemIn
+ )
+/*++
+
+Routine Description:
+
+ This function converts the given OEM string to a Unicode string.
+ The Unicode string is returned in a buffer allocated by this
+ function and must be freed with LocalFree.
+
+Arguments:
+
+ UnicodeOut - Receives a pointer to the Unicode string.
+
+ OemIn - This is a pointer to an ansi string that is to be converted.
+
+Return Value:
+
+ TRUE - The conversion was successful.
+
+ FALSE - The conversion was unsuccessful. In this case a buffer for
+ the unicode string was not allocated.
+
+--*/
+{
+ NTSTATUS ntstatus;
+ DWORD BufSize;
+ UNICODE_STRING UnicodeString;
+ OEM_STRING OemString;
+
+
+ //
+ // Allocate a buffer for the unicode string.
+ //
+
+ BufSize = (strlen(OemIn) + 1) * sizeof(WCHAR);
+
+ *UnicodeOut = LocalAlloc(LMEM_ZEROINIT, BufSize);
+
+ if (*UnicodeOut == NULL) {
+ KdPrint(("NWWORKSTATION: NwConvertToUnicode:LocalAlloc failed %lu\n",
+ GetLastError()));
+ return FALSE;
+ }
+
+ //
+ // Initialize the string structures
+ //
+ RtlInitAnsiString((PANSI_STRING) &OemString, OemIn);
+
+ UnicodeString.Buffer = *UnicodeOut;
+ UnicodeString.MaximumLength = (USHORT) BufSize;
+ UnicodeString.Length = 0;
+
+ //
+ // Call the conversion function.
+ //
+ ntstatus = RtlOemStringToUnicodeString(
+ &UnicodeString, // Destination
+ &OemString, // Source
+ FALSE // Allocate the destination
+ );
+
+ if (ntstatus != STATUS_SUCCESS) {
+
+ KdPrint(("NWWORKSTATION: NwConvertToUnicode: RtlOemStringToUnicodeString failure x%08lx\n",
+ ntstatus));
+
+ (void) LocalFree((HLOCAL) *UnicodeOut);
+ *UnicodeOut = NULL;
+ return FALSE;
+ }
+
+ *UnicodeOut = UnicodeString.Buffer;
+
+ return TRUE;
+
+}
diff --git a/private/nw/svcdlls/nwwks/lib/reg.c b/private/nw/svcdlls/nwwks/lib/reg.c
new file mode 100644
index 000000000..2259d0f90
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/lib/reg.c
@@ -0,0 +1,1010 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ reg.c
+
+Abstract:
+
+ This module provides helpers to call the registry used by both
+ the client and server sides of the workstation.
+
+Author:
+
+ Rita Wong (ritaw) 22-Apr-1993
+
+--*/
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#include <windef.h>
+#include <winerror.h>
+#include <winbase.h>
+#include <winreg.h>
+
+#include <nwsnames.h>
+#include <nwreg.h>
+#include <nwapi.h>
+#include <lmcons.h>
+#include <lmerr.h>
+
+#define LMSERVER_LINKAGE_REGKEY L"System\\CurrentControlSet\\Services\\LanmanServer\\Linkage"
+#define OTHERDEPS_VALUENAME L"OtherDependencies"
+
+//
+// Forward Declare
+//
+
+static
+DWORD
+NwRegQueryValueExW(
+ IN HKEY hKey,
+ IN LPWSTR lpValueName,
+ OUT LPDWORD lpReserved,
+ OUT LPDWORD lpType,
+ OUT LPBYTE lpData,
+ IN OUT LPDWORD lpcbData
+ );
+
+static
+DWORD
+EnumAndDeleteShares(
+ VOID
+ ) ;
+
+DWORD
+CalcNullNullSize(
+ WCHAR *pszNullNull
+ ) ;
+
+WCHAR *
+FindStringInNullNull(
+ WCHAR *pszNullNull,
+ WCHAR *pszString
+ ) ;
+
+VOID
+RemoveNWCFromNullNullList(
+ WCHAR *OtherDeps
+ ) ;
+
+
+
+
+DWORD
+NwReadRegValue(
+ IN HKEY Key,
+ IN LPWSTR ValueName,
+ OUT LPWSTR *Value
+ )
+/*++
+
+Routine Description:
+
+ This function allocates the output buffer and reads the requested
+ value from the registry into it.
+
+Arguments:
+
+ Key - Supplies opened handle to the key to read from.
+
+ ValueName - Supplies name of the value to retrieve data.
+
+ Value - Returns a pointer to the output buffer which points to
+ the memory allocated and contains the data read in from the
+ registry. This pointer must be freed with LocalFree when done.
+
+Return Value:
+
+ ERROR_NOT_ENOUGH_MEMORY - Failed to create buffer to read value into.
+
+ Error from registry call.
+
+--*/
+{
+ LONG RegError;
+ DWORD NumRequired = 0;
+ DWORD ValueType;
+
+
+ //
+ // Set returned buffer pointer to NULL.
+ //
+ *Value = NULL;
+
+ RegError = NwRegQueryValueExW(
+ Key,
+ ValueName,
+ NULL,
+ &ValueType,
+ (LPBYTE) NULL,
+ &NumRequired
+ );
+
+ if (RegError != ERROR_SUCCESS && NumRequired > 0) {
+
+ if ((*Value = (LPWSTR) LocalAlloc(
+ LMEM_ZEROINIT,
+ (UINT) NumRequired
+ )) == NULL) {
+
+ KdPrint(("NWWORKSTATION: NwReadRegValue: LocalAlloc of size %lu failed %lu\n",
+ NumRequired, GetLastError()));
+
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ RegError = NwRegQueryValueExW(
+ Key,
+ ValueName,
+ NULL,
+ &ValueType,
+ (LPBYTE) *Value,
+ &NumRequired
+ );
+ }
+ else if (RegError == ERROR_SUCCESS) {
+ KdPrint(("NWWORKSTATION: NwReadRegValue got SUCCESS with NULL buffer."));
+ return ERROR_FILE_NOT_FOUND;
+ }
+
+ if (RegError != ERROR_SUCCESS) {
+
+ if (*Value != NULL) {
+ (void) LocalFree((HLOCAL) *Value);
+ *Value = NULL;
+ }
+
+ return (DWORD) RegError;
+ }
+
+ return NO_ERROR;
+}
+
+
+static
+DWORD
+NwRegQueryValueExW(
+ IN HKEY hKey,
+ IN LPWSTR lpValueName,
+ OUT LPDWORD lpReserved,
+ OUT LPDWORD lpType,
+ OUT LPBYTE lpData,
+ IN OUT LPDWORD lpcbData
+ )
+/*++
+
+Routine Description:
+
+ This routine supports the same functionality as Win32 RegQueryValueEx
+ API, except that it works. It returns the correct lpcbData value when
+ a NULL output buffer is specified.
+
+ This code is stolen from the service controller.
+
+Arguments:
+
+ same as RegQueryValueEx
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+{
+ NTSTATUS ntstatus;
+ UNICODE_STRING ValueName;
+ PKEY_VALUE_FULL_INFORMATION KeyValueInfo;
+ DWORD BufSize;
+
+
+ UNREFERENCED_PARAMETER(lpReserved);
+
+ //
+ // Make sure we have a buffer size if the buffer is present.
+ //
+ if ((ARGUMENT_PRESENT(lpData)) && (! ARGUMENT_PRESENT(lpcbData))) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ RtlInitUnicodeString(&ValueName, lpValueName);
+
+ //
+ // Allocate memory for the ValueKeyInfo
+ //
+ BufSize = *lpcbData + sizeof(KEY_VALUE_FULL_INFORMATION) +
+ ValueName.Length
+ - sizeof(WCHAR); // subtract memory for 1 char because it's included
+ // in the sizeof(KEY_VALUE_FULL_INFORMATION).
+
+ KeyValueInfo = (PKEY_VALUE_FULL_INFORMATION) LocalAlloc(
+ LMEM_ZEROINIT,
+ (UINT) BufSize
+ );
+
+ if (KeyValueInfo == NULL) {
+ KdPrint(("NWWORKSTATION: NwRegQueryValueExW: LocalAlloc failed %lu\n",
+ GetLastError()));
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ ntstatus = NtQueryValueKey(
+ hKey,
+ &ValueName,
+ KeyValueFullInformation,
+ (PVOID) KeyValueInfo,
+ (ULONG) BufSize,
+ (PULONG) &BufSize
+ );
+
+ if ((NT_SUCCESS(ntstatus) || (ntstatus == STATUS_BUFFER_OVERFLOW))
+ && ARGUMENT_PRESENT(lpcbData)) {
+
+ *lpcbData = KeyValueInfo->DataLength;
+ }
+
+ if (NT_SUCCESS(ntstatus)) {
+
+ if (ARGUMENT_PRESENT(lpType)) {
+ *lpType = KeyValueInfo->Type;
+ }
+
+
+ if (ARGUMENT_PRESENT(lpData)) {
+ memcpy(
+ lpData,
+ (LPBYTE)KeyValueInfo + KeyValueInfo->DataOffset,
+ KeyValueInfo->DataLength
+ );
+ }
+ }
+
+ (void) LocalFree((HLOCAL) KeyValueInfo);
+
+ return RtlNtStatusToDosError(ntstatus);
+
+}
+
+VOID
+NwLuidToWStr(
+ IN PLUID LogonId,
+ OUT LPWSTR LogonIdStr
+ )
+/*++
+
+Routine Description:
+
+ This routine converts a LUID into a string in hex value format so
+ that it can be used as a registry key.
+
+Arguments:
+
+ LogonId - Supplies the LUID.
+
+ LogonIdStr - Receives the string. This routine assumes that this
+ buffer is large enough to fit 17 characters.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ swprintf(LogonIdStr, L"%08lx%08lx", LogonId->HighPart, LogonId->LowPart);
+}
+
+VOID
+NwWStrToLuid(
+ IN LPWSTR LogonIdStr,
+ OUT PLUID LogonId
+ )
+/*++
+
+Routine Description:
+
+ This routine converts a string in hex value format into a LUID.
+
+Arguments:
+
+ LogonIdStr - Supplies the string.
+
+ LogonId - Receives the LUID.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ swscanf(LogonIdStr, L"%08lx%08lx", &LogonId->HighPart, &LogonId->LowPart);
+}
+
+VOID
+NwDeleteCurrentUser(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine deletes the current user value under the parameters key.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ LONG RegError;
+ HKEY WkstaKey;
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters
+ //
+ RegError = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ | KEY_WRITE | DELETE,
+ &WkstaKey
+ );
+
+ if (RegError != NO_ERROR) {
+ KdPrint(("NWPROVAU: NwpInitializeRegistry open NWCWorkstation\\Parameters key unexpected error %lu!\n",
+ RegError));
+ return;
+ }
+
+ //
+ // Delete CurrentUser value first so that the workstation won't be
+ // reading this stale value. Ignore error since it may not exist.
+ //
+ (void) RegDeleteValueW(
+ WkstaKey,
+ NW_CURRENTUSER_VALUENAME
+ );
+
+ (void) RegCloseKey(WkstaKey);
+}
+
+DWORD
+NwDeleteServiceLogon(
+ IN PLUID Id OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This routine deletes a specific service logon ID key in the registry
+ if a logon ID is specified, otherwise it deletes all service logon
+ ID keys.
+
+Arguments:
+
+ Id - Supplies the logon ID to delete. NULL means delete all.
+
+Return Status:
+
+ None.
+
+--*/
+{
+ LONG RegError;
+ LONG DelError;
+ HKEY ServiceLogonKey;
+
+ WCHAR LogonIdKey[NW_MAX_LOGON_ID_LEN];
+
+
+ RegError = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_SERVICE_LOGON_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ | KEY_WRITE | DELETE,
+ &ServiceLogonKey
+ );
+
+ if (RegError != ERROR_SUCCESS) {
+ return RegError;
+ }
+
+ if (ARGUMENT_PRESENT(Id)) {
+
+ //
+ // Delete the key specified.
+ //
+ NwLuidToWStr(Id, LogonIdKey);
+
+ DelError = RegDeleteKeyW(ServiceLogonKey, LogonIdKey);
+
+ }
+ else {
+
+ //
+ // Delete all service logon ID keys.
+ //
+
+ do {
+
+ RegError = RegEnumKeyW(
+ ServiceLogonKey,
+ 0,
+ LogonIdKey,
+ sizeof(LogonIdKey) / sizeof(WCHAR)
+ );
+
+ if (RegError == ERROR_SUCCESS) {
+
+ //
+ // Got a logon id key, delete it.
+ //
+
+ DelError = RegDeleteKeyW(ServiceLogonKey, LogonIdKey);
+ }
+ else if (RegError != ERROR_NO_MORE_ITEMS) {
+ KdPrint((" NwDeleteServiceLogon: failed to enum logon IDs %lu\n", RegError));
+ }
+
+ } while (RegError == ERROR_SUCCESS);
+ }
+
+ (void) RegCloseKey(ServiceLogonKey);
+
+ return ((DWORD) DelError);
+}
+
+
+
+DWORD
+NwpRegisterGatewayShare(
+ IN LPWSTR ShareName,
+ IN LPWSTR DriveName
+ )
+/*++
+
+Routine Description:
+
+ This routine remembers that a gateway share has been created so
+ that it can be cleanup up when NWCS is uninstalled.
+
+Arguments:
+
+ ShareName - name of share
+ DriveName - name of drive that is shared
+
+Return Status:
+
+ Win32 error of any failure.
+
+--*/
+{
+ DWORD status ;
+
+
+ //
+ // make sure we have valid parameters
+ //
+ if (ShareName && DriveName)
+ {
+ HKEY hKey ;
+ DWORD dwDisposition ;
+
+ //
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Shares (create it if not there)
+ //
+ status = RegCreateKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_GATEWAY_SHARES,
+ 0,
+ L"",
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE, // desired access
+ NULL, // default security
+ &hKey,
+ &dwDisposition // ignored
+ );
+
+ if ( status )
+ return status ;
+
+ //
+ // wtite out value with valuename=sharename, valuedata=drive
+ //
+ status = RegSetValueExW(
+ hKey,
+ ShareName,
+ 0,
+ REG_SZ,
+ (LPBYTE) DriveName,
+ (wcslen(DriveName)+1) * sizeof(WCHAR)) ;
+
+ (void) RegCloseKey( hKey );
+ }
+ else
+ status = ERROR_INVALID_PARAMETER ;
+
+ return status ;
+
+}
+
+DWORD
+NwpCleanupGatewayShares(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine cleans up all persistent share info and also tidies
+ up the registry for NWCS. Later is not needed in uninstall, but is
+ there so we have a single routine that completely disables the
+ gateway.
+
+Arguments:
+
+ None.
+
+Return Status:
+
+ Win32 error for failed APIs.
+
+--*/
+{
+ DWORD status, FinalStatus = NO_ERROR ;
+ HKEY WkstaKey = NULL,
+ ServerLinkageKey = NULL ;
+ LPWSTR OtherDeps = NULL ;
+
+ //
+ // Enumeratre and delete all shares
+ //
+ FinalStatus = status = EnumAndDeleteShares() ;
+
+ //
+ // if update registry by cleaning out both Drive and Shares keys.
+ // ignore return values here. the keys may not be present.
+ //
+ (void) RegDeleteKeyW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_GATEWAY_DRIVES
+ ) ;
+
+ (void) RegDeleteKeyW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_GATEWAY_SHARES
+ ) ;
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters
+ //
+ status = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_REGKEY,
+ REG_OPTION_NON_VOLATILE, // options
+ KEY_WRITE, // desired access
+ &WkstaKey
+ );
+
+ if (status == ERROR_SUCCESS)
+ {
+ //
+ // delete the gateway account and gateway enabled flag.
+ // ignore failures here (the values may not be present)
+ //
+ (void) RegDeleteValueW(
+ WkstaKey,
+ NW_GATEWAYACCOUNT_VALUENAME
+ ) ;
+ (void) RegDeleteValueW(
+ WkstaKey,
+ NW_GATEWAY_ENABLE
+ ) ;
+
+ (void) RegCloseKey( WkstaKey );
+ }
+
+ //
+ // store new status if necessary
+ //
+ if (FinalStatus == NO_ERROR)
+ FinalStatus = status ;
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \LanmanServer\Linkage
+ //
+ status = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ LMSERVER_LINKAGE_REGKEY,
+ REG_OPTION_NON_VOLATILE, // options
+ KEY_WRITE | KEY_READ, // desired access
+ &ServerLinkageKey
+ );
+
+ if (status == ERROR_SUCCESS)
+ {
+ //
+ // remove us from the OtherDependencies.
+ // ignore read failures here (it may not be present)
+ //
+ status = NwReadRegValue(
+ ServerLinkageKey,
+ OTHERDEPS_VALUENAME,
+ &OtherDeps
+ );
+
+ if (status == NO_ERROR)
+ {
+ //
+ // this call munges the list to remove NWC if there.
+ //
+ RemoveNWCFromNullNullList(OtherDeps) ;
+
+ status = RegSetValueExW(
+ ServerLinkageKey,
+ OTHERDEPS_VALUENAME,
+ 0,
+ REG_MULTI_SZ,
+ (BYTE *)OtherDeps,
+ CalcNullNullSize(OtherDeps) * sizeof(WCHAR)) ;
+
+ (void) LocalFree(OtherDeps) ;
+ }
+ else
+ {
+ status = NO_ERROR ;
+ }
+
+ (void) RegCloseKey( ServerLinkageKey );
+ }
+
+ //
+ // store new status if necessary
+ //
+ if (FinalStatus == NO_ERROR)
+ FinalStatus = status ;
+
+
+ return (FinalStatus) ;
+}
+
+DWORD
+NwpClearGatewayShare(
+ IN LPWSTR ShareName
+ )
+/*++
+
+Routine Description:
+
+ This routine deletes a specific share from the remembered gateway
+ shares in the registry.
+
+Arguments:
+
+ ShareName - share value to delete
+
+Return Status:
+
+ Win32 status code.
+
+--*/
+{
+ DWORD status = NO_ERROR ;
+
+ //
+ // check that paramter is non null
+ //
+ if (ShareName)
+ {
+ HKEY hKey ;
+
+ //
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Drives
+ //
+ status = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_GATEWAY_SHARES,
+ REG_OPTION_NON_VOLATILE, // options
+ KEY_WRITE, // desired access
+ &hKey
+ );
+
+ if ( status )
+ return status ;
+
+ status = RegDeleteValueW(
+ hKey,
+ ShareName
+ ) ;
+
+ (void) RegCloseKey( hKey );
+ }
+ else
+ status = ERROR_INVALID_PARAMETER ;
+
+ return status ;
+}
+
+typedef NET_API_STATUS (*PF_NETSHAREDEL) (
+ LPWSTR server,
+ LPWSTR name,
+ DWORD reserved) ;
+
+#define NETSHAREDELSTICKY_API "NetShareDelSticky"
+#define NETSHAREDEL_API "NetShareDel"
+#define NETAPI_DLL L"NETAPI32"
+
+DWORD
+EnumAndDeleteShares(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine removes all persister share info in the server for
+ all gateway shares.
+
+Arguments:
+
+ None.
+
+Return Status:
+
+ Win32 error code.
+
+--*/
+{
+ DWORD err, i, type ;
+ HKEY hKey = NULL ;
+ FILETIME FileTime ;
+ HANDLE hNetapi = NULL ;
+ PF_NETSHAREDEL pfNetShareDel, pfNetShareDelSticky ;
+ WCHAR Class[256], Share[NNLEN+1], Device[MAX_PATH+1] ;
+ DWORD dwClass, dwSubKeys, dwMaxSubKey, dwMaxClass,
+ dwValues, dwMaxValueName, dwMaxValueData, dwSDLength,
+ dwShareLength, dwDeviceLength ;
+
+ //
+ // load the library so that not everyone needs link to netapi32
+ //
+ if (!(hNetapi = LoadLibraryW(NETAPI_DLL)))
+ return (GetLastError()) ;
+
+ //
+ // get addresses of the 2 functions we are interested in
+ //
+ if (!(pfNetShareDel = (PF_NETSHAREDEL) GetProcAddress(hNetapi,
+ NETSHAREDEL_API)))
+ {
+ err = GetLastError() ;
+ goto ExitPoint ;
+ }
+
+ if (!(pfNetShareDelSticky = (PF_NETSHAREDEL) GetProcAddress(hNetapi,
+ NETSHAREDELSTICKY_API)))
+ {
+ err = GetLastError() ;
+ goto ExitPoint ;
+ }
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCGateway\Shares
+ //
+ err = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_GATEWAY_SHARES,
+ REG_OPTION_NON_VOLATILE, // options
+ KEY_READ, // desired access
+ &hKey
+ );
+
+ if ( err )
+ goto ExitPoint ;
+
+ //
+ // read the info about that key
+ //
+ dwClass = sizeof(Class)/sizeof(Class[0]) ;
+ err = RegQueryInfoKeyW(hKey,
+ Class,
+ &dwClass,
+ NULL,
+ &dwSubKeys,
+ &dwMaxSubKey,
+ &dwMaxClass,
+ &dwValues,
+ &dwMaxValueName,
+ &dwMaxValueData,
+ &dwSDLength,
+ &FileTime) ;
+ if ( err )
+ {
+ goto ExitPoint ;
+ }
+
+ //
+ // for each value found, we have a share to delete
+ //
+ for (i = 0; i < dwValues; i++)
+ {
+ dwShareLength = sizeof(Share)/sizeof(Share[0]) ;
+ dwDeviceLength = sizeof(Device) ;
+ type = REG_SZ ;
+ err = RegEnumValueW(hKey,
+ i,
+ Share,
+ &dwShareLength,
+ NULL,
+ &type,
+ (LPBYTE)Device,
+ &dwDeviceLength) ;
+
+ //
+ // cleanup the share. try delete the share proper. if not
+ // there, remove the sticky info instead.
+ //
+ if (!err)
+ {
+ err = (*pfNetShareDel)(NULL, Share, 0) ;
+
+ if (err == NERR_NetNameNotFound)
+ {
+ (void) (*pfNetShareDelSticky)(NULL, Share, 0) ;
+ }
+ }
+
+ //
+ // ignore errors within the loop. we can to carry on to
+ // cleanup as much as possible.
+ //
+ err = NO_ERROR ;
+ }
+
+
+
+ExitPoint:
+
+ if (hKey)
+ (void) RegCloseKey( hKey );
+
+ if (hNetapi)
+ (void) FreeLibrary(hNetapi) ;
+
+ return err ;
+}
+
+
+DWORD
+CalcNullNullSize(
+ WCHAR *pszNullNull
+ )
+/*++
+
+Routine Description:
+
+ Walk thru a NULL NULL string, counting the number of
+ characters, including the 2 nulls at the end.
+
+Arguments:
+
+ Pointer to a NULL NULL string
+
+Return Status:
+
+ Count of number of *characters*. See description.
+
+--*/
+{
+
+ DWORD dwSize = 0 ;
+ WCHAR *pszTmp = pszNullNull ;
+
+ if (!pszNullNull)
+ return 0 ;
+
+ while (*pszTmp)
+ {
+ DWORD dwLen = wcslen(pszTmp) + 1 ;
+
+ dwSize += dwLen ;
+ pszTmp += dwLen ;
+ }
+
+ return (dwSize+1) ;
+}
+
+WCHAR *
+FindStringInNullNull(
+ WCHAR *pszNullNull,
+ WCHAR *pszString
+)
+/*++
+
+Routine Description:
+
+ Walk thru a NULL NULL string, looking for the search string
+
+Arguments:
+
+ pszNullNull: the string list we will search.
+ pszString: what we are searching for.
+
+Return Status:
+
+ The start of the string if found. Null, otherwise.
+
+--*/
+{
+ WCHAR *pszTmp = pszNullNull ;
+
+ if (!pszNullNull || !*pszNullNull)
+ return NULL ;
+
+ do {
+
+ if (_wcsicmp(pszTmp,pszString)==0)
+ return pszTmp ;
+
+ pszTmp += wcslen(pszTmp) + 1 ;
+
+ } while (*pszTmp) ;
+
+ return NULL ;
+}
+
+VOID
+RemoveNWCFromNullNullList(
+ WCHAR *OtherDeps
+ )
+/*++
+
+Routine Description:
+
+ Remove the NWCWorkstation string from a null null string.
+
+Arguments:
+
+ OtherDeps: the string list we will munge.
+
+Return Status:
+
+ None.
+
+--*/
+{
+ LPWSTR pszTmp0, pszTmp1 ;
+
+ //
+ // find the NWCWorkstation string
+ //
+ pszTmp0 = FindStringInNullNull(OtherDeps, NW_WORKSTATION_SERVICE) ;
+
+ if (!pszTmp0)
+ return ;
+
+ pszTmp1 = pszTmp0 + wcslen(pszTmp0) + 1 ; // skip past it
+
+ //
+ // shift the rest up
+ //
+ memmove(pszTmp0, pszTmp1, CalcNullNullSize(pszTmp1)*sizeof(WCHAR)) ;
+}
diff --git a/private/nw/svcdlls/nwwks/lib/sources b/private/nw/svcdlls/nwwks/lib/sources
new file mode 100644
index 000000000..5911ce45b
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/lib/sources
@@ -0,0 +1,47 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=nw
+MINORCOMP=nwwlib
+
+TARGETNAME=nwwlib
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+INCLUDES=..\inc;..\..\..\inc;$(_NTROOT)\private\inc
+
+SOURCES= \
+ lsa.c \
+ reg.c \
+ misc.c \
+ splutil.c
+
+UNICODE=1
+
+NET_C_DEFINES=-DRPC_NO_WINDOWS_H -DNOT_USED
+
+UMTYPE=console
+
+UMLIBS= \
+ obj\*\nwwlib.lib
+
+OPTIONAL_UMTEST=
diff --git a/private/nw/svcdlls/nwwks/lib/splutil.c b/private/nw/svcdlls/nwwks/lib/splutil.c
new file mode 100644
index 000000000..4f0c6fd9b
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/lib/splutil.c
@@ -0,0 +1,351 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ splutil.c
+
+Abstract:
+
+ This module provides all the utility functions for the netware print
+ provider.
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 15-Apr-1993
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#include <windows.h>
+#include <winspool.h>
+#include <splutil.h>
+
+DWORD PrinterInfo1Offsets[]={offsetof(LPPRINTER_INFO_1W, pDescription),
+ offsetof(LPPRINTER_INFO_1W, pName),
+ offsetof(LPPRINTER_INFO_1W, pComment),
+ 0xFFFFFFFF};
+
+DWORD PrinterInfo2Offsets[]={offsetof(LPPRINTER_INFO_2W, pServerName),
+ offsetof(LPPRINTER_INFO_2W, pPrinterName),
+ offsetof(LPPRINTER_INFO_2W, pShareName),
+ offsetof(LPPRINTER_INFO_2W, pPortName),
+ offsetof(LPPRINTER_INFO_2W, pDriverName),
+ offsetof(LPPRINTER_INFO_2W, pComment),
+ offsetof(LPPRINTER_INFO_2W, pLocation),
+ offsetof(LPPRINTER_INFO_2W, pDevMode),
+ offsetof(LPPRINTER_INFO_2W, pSepFile),
+ offsetof(LPPRINTER_INFO_2W, pPrintProcessor),
+ offsetof(LPPRINTER_INFO_2W, pDatatype),
+ offsetof(LPPRINTER_INFO_2W, pParameters),
+ offsetof(LPPRINTER_INFO_2W, pSecurityDescriptor),
+ 0xFFFFFFFF};
+
+DWORD PrinterInfo3Offsets[]={offsetof(LPPRINTER_INFO_3, pSecurityDescriptor),
+ 0xFFFFFFFF};
+
+DWORD JobInfo1Offsets[]={offsetof(LPJOB_INFO_1W, pPrinterName),
+ offsetof(LPJOB_INFO_1W, pMachineName),
+ offsetof(LPJOB_INFO_1W, pUserName),
+ offsetof(LPJOB_INFO_1W, pDocument),
+ offsetof(LPJOB_INFO_1W, pDatatype),
+ offsetof(LPJOB_INFO_1W, pStatus),
+ 0xFFFFFFFF};
+
+DWORD JobInfo2Offsets[]={offsetof(LPJOB_INFO_2W, pPrinterName),
+ offsetof(LPJOB_INFO_2W, pMachineName),
+ offsetof(LPJOB_INFO_2W, pUserName),
+ offsetof(LPJOB_INFO_2W, pDocument),
+ offsetof(LPJOB_INFO_2W, pNotifyName),
+ offsetof(LPJOB_INFO_2W, pDatatype),
+ offsetof(LPJOB_INFO_2W, pPrintProcessor),
+ offsetof(LPJOB_INFO_2W, pParameters),
+ offsetof(LPJOB_INFO_2W, pDriverName),
+ offsetof(LPJOB_INFO_2W, pDevMode),
+ offsetof(LPJOB_INFO_2W, pStatus),
+ offsetof(LPJOB_INFO_2W, pSecurityDescriptor),
+ 0xFFFFFFFF};
+
+DWORD AddJobInfo1Offsets[]={offsetof(LPADDJOB_INFO_1W, Path),
+ 0xFFFFFFFF};
+
+
+VOID
+MarshallUpStructure(
+ LPBYTE lpStructure,
+ LPDWORD lpOffsets,
+ LPBYTE lpBufferStart
+)
+{
+ register DWORD i=0;
+
+ while (lpOffsets[i] != -1) {
+
+ if ((*(LPBYTE *)(lpStructure+lpOffsets[i]))) {
+ (*(LPBYTE *)(lpStructure+lpOffsets[i]))+=(DWORD)lpBufferStart;
+ }
+
+ i++;
+ }
+}
+
+
+
+VOID
+MarshallDownStructure(
+ LPBYTE lpStructure,
+ LPDWORD lpOffsets,
+ LPBYTE lpBufferStart
+)
+{
+ register DWORD i=0;
+
+ if (!lpStructure)
+ return;
+
+ while (lpOffsets[i] != -1) {
+
+ if ((*(LPBYTE*)(lpStructure+lpOffsets[i]))) {
+ (*(LPBYTE*)(lpStructure+lpOffsets[i]))-=(DWORD)lpBufferStart;
+ }
+
+ i++;
+ }
+}
+
+
+
+LPVOID
+AllocNwSplMem(
+ DWORD flags,
+ DWORD cb
+)
+/*++
+
+Routine Description:
+
+ This function will allocate local memory. It will possibly allocate extra
+ memory and fill this with debugging information for the debugging version.
+
+Arguments:
+
+ flags - Flags to be passed to LocalAlloc
+
+ cb - The amount of memory to allocate in bytes
+
+Return Value:
+
+ NON-NULL - A pointer to the allocated memory
+
+--*/
+{
+ LPDWORD pMem;
+ DWORD cbNew;
+
+#if DBG
+ cbNew = cb + 2*sizeof(DWORD);
+ if (cbNew & 3)
+ cbNew += sizeof(DWORD) - (cbNew & 3);
+#else
+ cbNew = cb;
+#endif
+
+ pMem = (LPDWORD) LocalAlloc( flags, cbNew );
+
+ if ( !pMem )
+ {
+ KdPrint(("Memory Allocation in AllocNwSplMem failed for %d bytes\n", cbNew));
+ return NULL;
+ }
+
+#if DBG
+ *pMem = cb;
+ *(LPDWORD)((LPBYTE)pMem+cbNew-sizeof(DWORD)) = 0xdeadbeef;
+ return (LPVOID) (pMem + 1);
+#else
+ return (LPVOID) pMem;
+#endif
+
+}
+
+
+
+VOID
+FreeNwSplMem(
+ LPVOID pMem,
+ DWORD cb
+)
+/*++
+
+Routine Description:
+
+ This function will frees the local memory allocated by AllocSplMem.
+ Extra checking will be performed in the debug version to ensure that
+ the size to be freed is indeed the size we allocated through AllocSplMem.
+
+Arguments:
+
+ pMem - A pointer to the allocated memory
+ cb - The amount of memory to free
+
+Return Value:
+
+--*/
+{
+ DWORD cbNew;
+ LPDWORD pNewMem;
+
+ if ( !pMem )
+ return;
+
+ pNewMem = pMem;
+#if DBG
+ pNewMem--;
+ cbNew = cb + 2*sizeof(DWORD);
+ if ( cbNew & 3 )
+ cbNew += sizeof(DWORD) - (cbNew & 3);
+
+ if ( ( *pNewMem != cb )
+ || (*(LPDWORD)((LPBYTE)pNewMem + cbNew - sizeof(DWORD)) != 0xdeadbeef)
+ )
+ {
+ KdPrint(("Corrupt Memory in FreeNwSplMem : %0lx\n", pNewMem ));
+ return;
+ }
+#else
+ cbNew = cb;
+#endif
+
+ LocalFree( (LPVOID) pNewMem );
+}
+
+
+
+LPWSTR
+AllocNwSplStr(
+ LPWSTR pStr
+)
+/*++
+
+Routine Description:
+
+ This function will allocate enough local memory to store the specified
+ string, and copy that string to the allocated memory
+
+Arguments:
+
+ pStr - Pointer to the string that needs to be allocated and stored
+
+Return Value:
+
+ NON-NULL - A pointer to the allocated memory containing the string
+
+--*/
+{
+ LPWSTR pMem;
+
+ if ( !pStr )
+ return NULL;
+
+ if ( pMem = AllocNwSplMem(0, (wcslen(pStr) + 1) * sizeof(WCHAR)))
+ wcscpy(pMem, pStr);
+
+ return pMem;
+}
+
+
+
+VOID
+FreeNwSplStr(
+ LPWSTR pStr
+)
+/*++
+
+Routine Description:
+
+ This function will frees the string allocated by AllocSplStr.
+ Extra checking will be performed in the debug version to ensure that
+ the size to be freed is indeed the size we allocated through AllocSplStr.
+
+Arguments:
+
+ pStr - A pointer to the allocated string
+
+Return Value:
+
+--*/
+{
+ if ( pStr )
+ FreeNwSplMem(pStr, (wcslen(pStr) + 1) * sizeof(WCHAR));
+}
+
+
+
+BOOL
+ValidateUNCName(
+ LPWSTR pName
+)
+/*++
+
+Routine Description:
+
+ This function will checks whether the given name is a valid UNC
+ name ( in the form \\server\name) or not.
+
+Arguments:
+
+ pName - The supplied name
+
+Return Value:
+
+ TRUE - The name given is a valid UNC name
+ FALSE - Otherwise
+
+--*/
+{
+ if ( pName
+ && (*pName++ == L'\\')
+ && (*pName++ == L'\\')
+ && (wcschr(pName, L'\\'))
+ )
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+#ifndef NOT_USED
+
+LPWSTR
+GetNextElement(LPWSTR *pPtr, WCHAR token)
+{
+ LPWSTR pszRestOfString = *pPtr;
+ LPWSTR pszRetval = NULL;
+ LPWSTR pszStr = NULL;
+
+ if ( *pszRestOfString == L'\0')
+ return NULL;
+
+ if ((pszStr = wcschr (pszRestOfString, token))== NULL )
+ {
+ pszRetval = *pPtr;
+ *pPtr += wcslen(*pPtr);
+ return pszRetval;
+ }
+ else
+ {
+ *pszStr = L'\0';
+ pszRetval = *pPtr ;
+ *pPtr = ++pszStr ;
+ return pszRetval ;
+ }
+}
+
+#endif
diff --git a/private/nw/svcdlls/nwwks/makefil0 b/private/nw/svcdlls/nwwks/makefil0
new file mode 100644
index 000000000..a65ed314d
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/makefil0
@@ -0,0 +1,62 @@
+#
+# This is the MIDL compile phase of the build process.
+#
+
+# The following is where you put the name of your .idl file without
+# the .idl extension:
+
+IDL_NAME = nwwks
+IMPORT = imports
+
+#
+#
+
+!IF "$(QFE_BUILD)" != "1"
+!INCLUDE $(NTMAKEENV)\makefile.plt
+!ENDIF
+
+UNICODE=1
+
+SDKINC = \nt\public\sdk\inc
+SDKCRTINC = \nt\public\sdk\inc\crt
+PRIVINC = ..\..\..\..\inc
+
+INCS = -I$(SDKINC) -I$(SDKCRTINC) -I$(PRIVINC) -I.\inc
+
+TARGETS = client\$(IDL_NAME)_c.c \
+ server\$(IDL_NAME)_s.c \
+ inc\$(IDL_NAME).h
+
+EXTRN_DEPENDS = $(SDKINC)\winbase.h \
+ $(SDKINC)\windef.h \
+ inc\imports.h \
+ $(IDL_NAME).acf
+
+NET_C_DEFINES= -DINCL_32= -DNT -DRPC_NO_WINDOWS_H
+CPP = -cpp_cmd "$(MIDL_CPP)" -cpp_opt "-nologo -E $(MIDL_FLAGS) $(INCS) $(C_DEFINES) $(NET_C_DEFINES)"
+
+
+#
+# Define Products and Dependencies
+#
+
+all: $(TARGETS) $(EXTRN_DEPENDS)
+!IF "$(BUILDMSG)" != ""
+ @ech ; $(BUILDMSG) ;
+!ENDIF
+
+clean: delsrc all
+
+delsrc:
+ erase $(TARGETS)
+
+
+#
+# MIDL COMPILE
+#
+
+$(TARGETS) : .\$(IDL_NAME).idl $(EXTRN_DEPENDS)
+ midl -oldnames -error ref -ms_ext -c_ext $(CPP) .\$(IDL_NAME).idl $(INCS)
+ IF EXIST $(IDL_NAME)_c.c copy $(IDL_NAME)_c.c .\client & del $(IDL_NAME)_c.c
+ IF EXIST $(IDL_NAME)_s.c copy $(IDL_NAME)_s.c .\server & del $(IDL_NAME)_s.c
+ IF EXIST $(IDL_NAME).h copy $(IDL_NAME).h .\inc & del $(IDL_NAME).h
diff --git a/private/nw/svcdlls/nwwks/nwwks.acf b/private/nw/svcdlls/nwwks/nwwks.acf
new file mode 100644
index 000000000..272fdae73
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/nwwks.acf
@@ -0,0 +1,10 @@
+[ implicit_handle( handle_t nwwks_bhandle )]
+
+interface nwwks
+
+{
+NwrAddJob([byte_count(BufferSize)] AddInfo1);
+
+typedef [allocate(all_nodes)] LPSERVICE_INFOW;
+
+}
diff --git a/private/nw/svcdlls/nwwks/nwwks.idl b/private/nw/svcdlls/nwwks/nwwks.idl
new file mode 100644
index 000000000..25fbcca9d
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/nwwks.idl
@@ -0,0 +1,479 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ nwwks.idl
+
+Abstract:
+
+ This is the IDL file that describes the RPC interface for the
+ NetWare workstation internal APIs.
+
+Author:
+
+ Rita Wong (ritaw) 11-Feb-1993
+
+Environment:
+
+ User Mode -Win32
+
+Revision History:
+ Yi-Hsin Sung ( yihsins ) 24-Apr-1993
+ Added NwrOpenEnumPrintServers and NwrOpenEnumPrintQueues
+
+ Glenn A. Curtis (glennc) 19-Oct-1995
+ Added NwrGetUser, NwrGetResourceInformation, and NwrGetResourceParent
+
+--*/
+
+//
+// Interface Attributes
+//
+
+[
+ uuid(E67AB081-9844-3521-9D32-834F038001C0),
+ version(1.0),
+ pointer_default(unique)
+]
+
+//
+// Interface Keyword
+//
+
+interface nwwks
+
+//
+// Interface Body
+//
+
+{
+
+import "imports.idl";
+
+//
+// Handle types
+//
+
+typedef [handle] wchar_t * NWWKSTA_IMPERSONATE_HANDLE;
+
+typedef [handle] wchar_t * NWWKSTA_IDENTIFY_HANDLE;
+
+typedef [context_handle] PVOID NWWKSTA_CONTEXT_HANDLE;
+
+typedef NWWKSTA_CONTEXT_HANDLE *LPNWWKSTA_CONTEXT_HANDLE;
+
+typedef [context_handle] PVOID NWWKSTA_PRINTER_CONTEXT;
+
+typedef NWWKSTA_PRINTER_CONTEXT *LPNWWKSTA_PRINTER_CONTEXT;
+
+typedef struct _NW_JOB_INFO {
+ DWORD nPosition;
+ [string, unique] wchar_t *pDocument;
+ [string, unique] wchar_t *pUserName;
+} NW_JOB_INFO, *PNW_JOB_INFO;
+
+//
+// Function prototype
+//
+
+DWORD
+NwrCreateConnection(
+ [in,string,unique] NWWKSTA_IMPERSONATE_HANDLE Reserved,
+ [in,string,unique] wchar_t * LocalName,
+ [in,string] wchar_t * RemoteName,
+ [in] DWORD Type,
+ [in,string,unique] wchar_t * Password,
+ [in,string,unique] wchar_t * UserName
+ );
+
+typedef struct _NWSERVER {
+ [string] wchar_t * ServerName;
+} NWSERVER, *LPNWSERVER;
+
+DWORD
+NwrChangePassword(
+ [in,string,unique] NWWKSTA_IDENTIFY_HANDLE Reserved,
+ [in] DWORD UserLuid,
+ [in,string] wchar_t * UserName,
+ [in,string,unique] wchar_t * OldPassword,
+ [in,string,unique] wchar_t * NewPassword,
+ [in,string] wchar_t * TreeName
+ );
+
+DWORD
+NwrDeleteConnection(
+ [in,string,unique] NWWKSTA_IMPERSONATE_HANDLE Reserved,
+ [in,string] wchar_t * ConnectionName,
+ [in] DWORD UseForce
+ );
+
+DWORD
+NwrQueryServerResource(
+ [in,string,unique] NWWKSTA_IMPERSONATE_HANDLE Reserved,
+ [in,string] wchar_t * LocalName,
+ [in, out,string, unique, size_is(RemoteNameLen * sizeof(wchar_t))] wchar_t * RemoteName,
+ [in] DWORD RemoteNameLen,
+ [out] LPDWORD CharsRequired
+ );
+
+DWORD
+NwrOpenEnumConnections(
+ [in,string,unique] NWWKSTA_IDENTIFY_HANDLE Reserved,
+ [in] DWORD ConnectionType,
+ [out] LPNWWKSTA_CONTEXT_HANDLE EnumHandle
+ );
+
+DWORD
+NwrOpenEnumContextInfo(
+ [in,string,unique] NWWKSTA_IDENTIFY_HANDLE Reserved,
+ [in] DWORD ConnectionType,
+ [out] LPNWWKSTA_CONTEXT_HANDLE EnumHandle
+ );
+
+DWORD
+NwrOpenEnumServersAndNdsTrees(
+ [in,string,unique] NWWKSTA_IDENTIFY_HANDLE Reserved,
+ [out] LPNWWKSTA_CONTEXT_HANDLE EnumHandle
+ );
+
+DWORD
+NwrOpenEnumNdsSubTrees_Disk(
+ [in,string,unique] NWWKSTA_IDENTIFY_HANDLE Reserved,
+ [in,string] wchar_t * ParentPathName,
+ [in,out,unique] LPDWORD ClassTypeOfNDSLeaf,
+ [out] LPNWWKSTA_CONTEXT_HANDLE EnumHandle
+ );
+
+DWORD
+NwrOpenEnumNdsSubTrees_Print(
+ [in,string,unique] NWWKSTA_IDENTIFY_HANDLE Reserved,
+ [in,string] wchar_t * ParentPathName,
+ [in,out,unique] LPDWORD ClassTypeOfNDSLeaf,
+ [out] LPNWWKSTA_CONTEXT_HANDLE EnumHandle
+ );
+
+DWORD
+NwrOpenEnumNdsSubTrees_Any(
+ [in,string,unique] NWWKSTA_IDENTIFY_HANDLE Reserved,
+ [in,string] wchar_t * ParentPathName,
+ [in,out,unique] LPDWORD ClassTypeOfNDSLeaf,
+ [out] LPNWWKSTA_CONTEXT_HANDLE EnumHandle
+ );
+
+DWORD
+NwrOpenEnumVolumes(
+ [in,string,unique] NWWKSTA_IDENTIFY_HANDLE Reserved,
+ [in,string] wchar_t * ServerName,
+ [out] LPNWWKSTA_CONTEXT_HANDLE EnumHandle
+ );
+
+DWORD
+NwrOpenEnumQueues(
+ [in,string,unique] NWWKSTA_IDENTIFY_HANDLE Reserved,
+ [in,string] wchar_t * ServerName,
+ [out] LPNWWKSTA_CONTEXT_HANDLE EnumHandle
+ );
+
+DWORD
+NwrOpenEnumVolumesQueues(
+ [in,string,unique] NWWKSTA_IDENTIFY_HANDLE Reserved,
+ [in,string] wchar_t * ServerName,
+ [out] LPNWWKSTA_CONTEXT_HANDLE EnumHandle
+ );
+
+DWORD
+NwrOpenEnumDirectories(
+ [in,string,unique] NWWKSTA_IDENTIFY_HANDLE Reserved,
+ [in,string] wchar_t * VolumeName,
+ [in,string,unique] wchar_t * UserName,
+ [in,string,unique] wchar_t * Password,
+ [out] LPNWWKSTA_CONTEXT_HANDLE EnumHandle
+ );
+
+DWORD
+NwrEnum(
+ [in] NWWKSTA_CONTEXT_HANDLE EnumHandle,
+ [in] DWORD EntriesRequested,
+ [out,size_is(BufferSize)] LPBYTE Buffer,
+ [in] DWORD BufferSize,
+ [out] LPDWORD BytesNeeded,
+ [out] LPDWORD EntriesRead
+ );
+
+DWORD
+NwrEnumConnections(
+ [in] NWWKSTA_CONTEXT_HANDLE EnumHandle,
+ [in] DWORD EntriesRequested,
+ [out,size_is(BufferSize)] LPBYTE Buffer,
+ [in] DWORD BufferSize,
+ [out] LPDWORD BytesNeeded,
+ [out] LPDWORD EntriesRead,
+ [in] DWORD fImplicitConnections
+ );
+
+DWORD
+NwrCloseEnum(
+ [in,out] LPNWWKSTA_CONTEXT_HANDLE EnumHandle
+ );
+
+DWORD
+NwrLogonUser(
+ [in,string,unique] NWWKSTA_IDENTIFY_HANDLE Reserved,
+ [in] PLUID LogonId,
+ [in,string] wchar_t * UserName,
+ [in,string,unique] wchar_t * Password,
+ [in,string,unique] wchar_t * ServerName,
+ [in,out,string,unique,size_is(LogonCommandLength * sizeof(wchar_t))] wchar_t * LogonCommand,
+ [in] DWORD LogonCommandLength
+ );
+
+DWORD
+NwrLogoffUser(
+ [in,string,unique] NWWKSTA_IDENTIFY_HANDLE Reserved,
+ [in] PLUID LogonId
+ );
+
+DWORD
+NwrSetInfo(
+ [in,string,unique] NWWKSTA_IDENTIFY_HANDLE Reserved,
+ [in] DWORD PrintOption,
+ [in,string,unique] wchar_t * ServerName
+ );
+
+DWORD
+NwrValidateUser(
+ [in,string,unique] NWWKSTA_IDENTIFY_HANDLE Reserved,
+ [in,string,unique] wchar_t * ServerName
+ );
+
+DWORD
+NwrOpenPrinter(
+ [in,string,unique] NWWKSTA_IDENTIFY_HANDLE Reserved,
+ [in,string] wchar_t *PrinterName,
+ [in] DWORD fKnownPrinter,
+ [out] LPNWWKSTA_PRINTER_CONTEXT PrinterHandle
+ );
+
+DWORD
+NwrClosePrinter(
+ [in,out] LPNWWKSTA_PRINTER_CONTEXT PrinterHandle
+ );
+
+DWORD
+NwrGetPrinter(
+ [in] NWWKSTA_PRINTER_CONTEXT PrinterHandle,
+ [in] DWORD Level,
+ [in,out,unique,size_is(BufferSize)] LPBYTE PrinterInfo,
+ [in] DWORD BufferSize,
+ [out] LPDWORD BytesNeeded
+ );
+
+DWORD
+NwrSetPrinter(
+ [in] NWWKSTA_PRINTER_CONTEXT PrinterHandle,
+ [in] DWORD Command
+ );
+
+DWORD
+NwrEnumPrinters(
+ [in,string,unique] NWWKSTA_IDENTIFY_HANDLE Reserved,
+ [in,string,unique] wchar_t *ContainerName,
+ [in,out,unique,size_is(BufferSize)] LPBYTE Buffer,
+ [in] DWORD BufferSize,
+ [out] LPDWORD BytesNeeded,
+ [out] LPDWORD EntriesRead
+ );
+
+DWORD
+NwrStartDocPrinter(
+ [in] NWWKSTA_PRINTER_CONTEXT PrinterHandle,
+ [in,string,unique] wchar_t *DocumentName,
+ [in,string,unique] wchar_t *UserName,
+ [in] DWORD fGateway
+ );
+
+DWORD
+NwrWritePrinter(
+ [in] NWWKSTA_PRINTER_CONTEXT PrinterHandle,
+ [in,size_is(BufferSize)] LPBYTE Buffer,
+ [in] DWORD BufferSize,
+ [out] LPDWORD BytesWritten
+ );
+
+DWORD
+NwrAbortPrinter(
+ [in] NWWKSTA_PRINTER_CONTEXT PrinterHandle
+ );
+
+DWORD
+NwrEndDocPrinter(
+ [in] NWWKSTA_PRINTER_CONTEXT PrinterHandle
+ );
+
+DWORD
+NwrEnumJobs(
+ [in] NWWKSTA_PRINTER_CONTEXT PrinterHandle,
+ [in] DWORD FirstJobRequested,
+ [in] DWORD EntriesRequested,
+ [in] DWORD Level,
+ [in,out,unique,size_is(BufferSize)] LPBYTE Buffer,
+ [in] DWORD BufferSize,
+ [out] LPDWORD BytesNeeded,
+ [out] LPDWORD EntriesRead
+ );
+
+DWORD
+NwrGetJob(
+ [in] NWWKSTA_PRINTER_CONTEXT PrinterHandle,
+ [in] DWORD JobId,
+ [in] DWORD Level,
+ [in,out,unique,size_is(BufferSize)] LPBYTE JobInfo,
+ [in] DWORD BufferSize,
+ [out] LPDWORD BytesNeeded
+ );
+
+DWORD
+NwrSetJob(
+ [in] NWWKSTA_PRINTER_CONTEXT PrinterHandle,
+ [in] DWORD JobId,
+ [in] DWORD Level,
+ [in, unique] PNW_JOB_INFO pNwJobInfo,
+ [in] DWORD Command
+ );
+
+
+DWORD
+NwrAddJob(
+ [in] NWWKSTA_PRINTER_CONTEXT PrinterHandle,
+ [out] LPADDJOB_INFO_1W AddInfo1,
+ [in] DWORD BufferSize,
+ [out] LPDWORD BytesNeeded
+ );
+
+DWORD
+NwrScheduleJob(
+ [in] NWWKSTA_PRINTER_CONTEXT PrinterHandle,
+ [in] DWORD JobId
+ );
+
+DWORD
+NwrWaitForPrinterChange(
+ [in] NWWKSTA_PRINTER_CONTEXT PrinterHandle,
+ [in,out] LPDWORD Flags
+ );
+
+DWORD
+NwrEnumGWDevices(
+ [in,string,unique] NWWKSTA_IDENTIFY_HANDLE Reserved,
+ [in, out] LPDWORD Index,
+ [in,out,unique,size_is(BufferSize)] LPBYTE Buffer,
+ [in] DWORD BufferSize,
+ [out] LPDWORD BytesNeeded,
+ [out] LPDWORD EntriesRead
+ );
+
+DWORD
+NwrAddGWDevice(
+ [in,string,unique] NWWKSTA_IDENTIFY_HANDLE Reserved,
+ [in,string,unique] wchar_t *DeviceName,
+ [in,string,unique] wchar_t *RemoteName,
+ [in,string,unique] wchar_t *AccountName,
+ [in,string,unique] wchar_t *Password,
+ [in] DWORD Flags
+ );
+
+DWORD
+NwrDeleteGWDevice(
+ [in,string,unique] NWWKSTA_IDENTIFY_HANDLE Reserved,
+ [in,string,unique] wchar_t *DeviceName,
+ [in] DWORD Flags
+ );
+
+DWORD
+NwrQueryGatewayAccount(
+ [in,string,unique] NWWKSTA_IMPERSONATE_HANDLE Reserved,
+ [in, out,string, unique, size_is(AccountNameLen * sizeof(wchar_t))] wchar_t * AccountName,
+ [in] DWORD AccountNameLen,
+ [out] LPDWORD AccountCharsNeeded,
+ [in, out,string, unique, size_is(PasswordLen * sizeof(wchar_t))] wchar_t * Password,
+ [in] DWORD PasswordLen,
+ [out] LPDWORD PasswordCharsNeeded
+ );
+
+DWORD
+NwrSetGatewayAccount(
+ [in,string,unique] NWWKSTA_IMPERSONATE_HANDLE Reserved,
+ [in,string] wchar_t * AccountName,
+ [in,string] wchar_t * Passwoed
+ );
+
+#ifndef QFE_BUILD
+
+DWORD
+NwrGetService(
+ [in, string, unique] NWWKSTA_IDENTIFY_HANDLE Reserved,
+ [in] WORD nServiceType,
+ [in, string] wchar_t *lpServiceName,
+ [in] DWORD dwProperties,
+ [in, out, unique, size_is(dwBufferLength)] LPBYTE lpServiceInfo,
+ [in] DWORD dwBufferLength,
+ [out] LPDWORD lpdwBytesNeeded
+ );
+
+DWORD
+NwrSetService(
+ [in, string, unique] NWWKSTA_IDENTIFY_HANDLE Reserved,
+ [in] DWORD dwOperation,
+ [in] LPSERVICE_INFOW lpServiceInfo,
+ [in] WORD nServiceType
+ );
+
+DWORD
+NwrGetUser(
+ [in, string, unique] NWWKSTA_IDENTIFY_HANDLE Reserved,
+ [in, string, unique] wchar_t * lpRemoteName,
+ [out, size_is(dwUserNameBufferSize)] LPBYTE lpUserName,
+ [in] DWORD dwUserNameBufferSize,
+ [out] LPDWORD lpdwCharsRequired
+ );
+
+DWORD
+NwrGetResourceInformation(
+ [in, string, unique] NWWKSTA_IDENTIFY_HANDLE Reserved,
+ [in, string, unique] wchar_t * lpRemoteName,
+ [in] DWORD dwType,
+ [out, size_is(dwBufferSize)] LPBYTE lpBuffer,
+ [in] DWORD dwBufferSize,
+ [out] LPDWORD lpdwBytesNeeded,
+ [out] LPDWORD lpdwSystemOffset
+ );
+
+DWORD
+NwrGetConnectionPerformance(
+ [in, string, unique] NWWKSTA_IDENTIFY_HANDLE Reserved,
+ [in, string, unique] wchar_t * lpRemoteName,
+ [out, size_is(dwBufferSize)] LPBYTE lpNetConnectInfo,
+ [in] DWORD dwBufferSize
+ );
+
+DWORD
+NwrGetResourceParent(
+ [in, string, unique] NWWKSTA_IDENTIFY_HANDLE Reserved,
+ [in, string, unique] wchar_t * lpRemoteName,
+ [in] DWORD dwType,
+ [out, size_is(dwBufferSize)] LPBYTE lpBuffer,
+ [in] DWORD dwBufferSize,
+ [out] LPDWORD lpdwBytesNeeded
+ );
+
+DWORD
+NwrSetLogonScript(
+ [in,string,unique] NWWKSTA_IDENTIFY_HANDLE Reserved,
+ [in] DWORD ScriptOptions
+ );
+
+
+#endif
+}
diff --git a/private/nw/svcdlls/nwwks/reg.ini b/private/nw/svcdlls/nwwks/reg.ini
new file mode 100644
index 000000000..be73d277a
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/reg.ini
@@ -0,0 +1,30 @@
+\Registry\Machine\System\CurrentControlSet\Services
+ NetwareWorkstation
+ Type = REG_DWORD 0x00000020
+ Start = REG_DWORD 0x00000003
+ ErrorControl = REG_DWORD 0x00000001
+ ImagePath = REG_EXPAND_SZ %SystemRoot%\System32\nwsvc.exe
+ DisplayName = NetWareWorkstation
+ ObjectName = LocalSystem
+ DependOnService = REG_MULTI_SZ "Mup" "MSIPX" "Streams"
+ Linkage
+ Bind = REG_MULTI_SZ "\Device\Streams\IPX"
+ NetworkProvider
+ Devicename = \Device\NetWareWorkstation
+ ProviderPath = REG_EXPAND_SZ %SystemRoot%\System32\nwprovau.dll
+ Name = Novell Network
+ Parameters
+ NwRdr
+ Type = REG_DWORD 0x00000002
+ Start = REG_DWORD 0x00000003
+ ErrorControl = REG_DWORD 0x00000001
+ ImagePath = REG_EXPAND_SZ \SystemRoot\System32\drivers\nwrdr.sys
+ Parameters
+ Eventlog
+ System
+ NetWareWorkstation
+ EventMessageFile = REG_EXPAND_SZ %SystemRoot%\System32\nwevent.dll
+ TypeSupported = REG_DWORD 0x7
+ NwRdr
+ EventMessageFile = REG_EXPAND_SZ %SystemRoot%\System32\nwevent.dll
+ TypeSupported = REG_DWORD 0x7
diff --git a/private/nw/svcdlls/nwwks/server/address.c b/private/nw/svcdlls/nwwks/server/address.c
new file mode 100644
index 000000000..675152be2
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/server/address.c
@@ -0,0 +1,42 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ address.c
+
+Abstract:
+
+ This module contains the code to support NPGetAddressByName.
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 18-Apr-94
+
+Revision History:
+
+ yihsins Created
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <winsock2.h>
+#include <wsipx.h>
+#include <nwxchg.h>
+#include <ntddnwfs.h>
+#include <rpc.h>
+#include <rpcdce.h>
+#include "handle.h"
+#include "rnrdefs.h"
+#include "sapcmn.h"
+#include <time.h>
+
+VOID
+DummyRoutine()
+{
+}
+
diff --git a/private/nw/svcdlls/nwwks/server/connect.c b/private/nw/svcdlls/nwwks/server/connect.c
new file mode 100644
index 000000000..546a745f2
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/server/connect.c
@@ -0,0 +1,1688 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ connect.c
+
+Abstract:
+
+ This module contains tree connections routines supported by
+ NetWare Workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 15-Feb-1993
+
+Revision History:
+
+--*/
+
+#include <nw.h>
+#include <handle.h>
+#include <nwauth.h>
+#include <nwcanon.h>
+
+#define NW_ENUM_EXTRA_BYTES 256
+
+//-------------------------------------------------------------------//
+// //
+// Local Function Prototypes //
+// //
+//-------------------------------------------------------------------//
+
+DWORD
+NwAllocAndGetUncName(
+ IN LPWSTR LocalName,
+ IN DWORD LocalNameLength,
+ OUT LPWSTR *UncName
+ );
+
+//-------------------------------------------------------------------//
+
+
+
+DWORD
+NwrCreateConnection(
+ IN LPWSTR Reserved OPTIONAL,
+ IN LPWSTR LocalName OPTIONAL,
+ IN LPWSTR RemoteName,
+ IN DWORD Type,
+ IN LPWSTR Password OPTIONAL,
+ IN LPWSTR UserName OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function creates a tree connection to the specified RemoteName
+ (UNC name) and maps it to the LocalName (local device name), if
+ it is specified. The password and user name are the credentials
+ used to create the connection, if specified; otherwise, the
+ interactive logged on user's credentials are used by default.
+
+ NOTE: This code now calls a helper routine to do the work, this helper
+ routine (NwCreateConnection) is identical to the code that used to be
+ here with the exception that the helper does call ImpersonateClient().
+ We now do the client impersonation outside of the helper routine.
+
+Arguments:
+
+ Reserved - Must be NULL.
+
+ LocalName - Supplies the local device name to map to the created tree
+ connection. Only drive letter device names are accepted. (No
+ LPT or COM).
+
+ RemoteName - Supplies the UNC name of the remote resource in the format
+ of Server\Volume\Directory. It must be a disk resource.
+
+ Type - Supplies the connection type.
+
+ Password - Supplies the password to use to make the connection to the
+ server.
+
+ UserName - Supplies the user name to use to make the connection.
+
+Return Value:
+
+ NO_ERROR - Operation was successful.
+
+ ERROR_NOT_ENOUGH_MEMORY - Out of memory allocating internal work buffers.
+
+ WN_BAD_NETNAME - Remote resource name is invalid.
+
+ WN_BAD_LOCALNAME - Local DOS device name is invalid.
+
+ ERROR_BAD_NETPATH - The UNC name does not exist on the network.
+
+ ERROR_INVALID_PARAMETER - LPT or COM LocalName was specified.
+
+ Other errors from the redirector.
+
+--*/
+{
+ DWORD status;
+ BOOL Impersonate = FALSE ;
+
+ UNREFERENCED_PARAMETER(Reserved);
+
+ //
+ // Impersonate the client
+ //
+ if ((status = NwImpersonateClient()) != NO_ERROR)
+ {
+ goto CleanExit;
+ }
+
+ Impersonate = TRUE ;
+
+ status = NwCreateConnection( LocalName,
+ RemoteName,
+ Type,
+ Password,
+ UserName );
+
+CleanExit:
+
+ if (Impersonate) {
+ (void) NwRevertToSelf() ;
+ }
+
+#if DBG
+ IF_DEBUG(CONNECT) {
+ KdPrint(("NWWORKSTATION: NwrCreateConnection returns %lu\n", status));
+ }
+#endif
+
+ return status;
+}
+
+
+DWORD
+NwrDeleteConnection(
+ IN LPWSTR Reserved OPTIONAL,
+ IN LPWSTR ConnectionName,
+ IN DWORD UseForce
+ )
+/*++
+
+Routine Description:
+
+ This function deletes an existing connection.
+
+Arguments:
+
+ Reserved - Must be NULL.
+
+ ConnectionName - Supplies the local device name or UNC name which
+ specifies the connection to delete. If UNC name is specified,
+ the UNC connection must exist.
+
+
+ UseForce - Supplies a flag which if TRUE specifies to tear down
+ the connection eventhough files are opened. If FALSE, the
+ connection is deleted only if there are no opened files.
+
+Return Value:
+
+ NO_ERROR - Operation was successful.
+
+ ERROR_NOT_ENOUGH_MEMORY - Out of memory allocating internal work buffers.
+
+ WN_BAD_NETNAME - ConnectionName is invalid.
+
+ ERROR_BAD_NETPATH - The UNC name does not exist on the network.
+
+ ERROR_INVALID_PARAMETER - LPT or COM LocalName was specified.
+
+ Other errors from the redirector.
+
+--*/
+{
+ DWORD status;
+
+ LPWSTR ConnectName = NULL;
+ DWORD ConnectLength;
+
+ LPWSTR LocalName;
+ LPWSTR UncName = NULL;
+
+ BOOL Impersonate = FALSE ;
+
+ UNREFERENCED_PARAMETER(Reserved);
+
+ if (*ConnectionName == 0) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+#if DBG
+ IF_DEBUG(CONNECT) {
+ KdPrint(("\nNWWORKSTATION: NwrDeleteConnection: ConnectionName %ws, Force %lu\n",
+ ConnectionName, UseForce));
+ }
+#endif
+
+ if ((status = NwLibCanonLocalName(
+ ConnectionName,
+ &ConnectName,
+ &ConnectLength
+ )) == NO_ERROR) {
+
+ //
+ // Get the UNC name mapped to this drive letter so that we can
+ // open a handle to it for deletion.
+ //
+ if ((status = NwAllocAndGetUncName(
+ ConnectName,
+ ConnectLength,
+ &UncName
+ )) != NO_ERROR) {
+
+ if (status == WN_NOT_CONNECTED &&
+ NwGetGatewayResource(ConnectName,
+ NULL,
+ 0,
+ NULL) == WN_MORE_DATA)
+ {
+ status = ERROR_DEVICE_IN_USE ;
+ }
+
+ (void) LocalFree((HLOCAL) ConnectName);
+
+ return status;
+ }
+
+ LocalName = ConnectName;
+
+ }
+ else {
+
+ //
+ // Not a device name. See if it is a UNC name.
+ //
+ if ((status = NwLibCanonRemoteName(
+ NULL,
+ ConnectionName,
+ &ConnectName,
+ NULL
+ )) != NO_ERROR) {
+
+ return status;
+ }
+
+ UncName = ConnectName;
+ LocalName = NULL;
+
+ }
+
+ if ((status = NwImpersonateClient()) != NO_ERROR)
+ {
+ goto CleanExit;
+ }
+ Impersonate = TRUE ;
+
+ //
+ // To delete a connection, a tree connection handle must be opened to
+ // it so that the handle can be specified to the redirector to delete
+ // the connection.
+ //
+ status = NwOpenHandleToDeleteConn(
+ UncName,
+ LocalName,
+ UseForce,
+ FALSE
+ );
+
+ if ( status == ERROR_FILE_NOT_FOUND )
+ status = ERROR_BAD_NETPATH;
+
+CleanExit:
+
+ if (Impersonate) {
+ (void) NwRevertToSelf() ;
+ }
+ if (UncName != NULL && UncName != ConnectName) {
+ (void) LocalFree((HLOCAL) UncName);
+ }
+
+ if (ConnectName != NULL) {
+ (void) LocalFree((HLOCAL) ConnectName);
+ }
+
+#if DBG
+ IF_DEBUG(CONNECT) {
+ KdPrint(("NWWORKSTATION: NwrDeleteConnection returns %lu\n", status));
+ }
+#endif
+
+ return status;
+}
+
+
+DWORD
+NwrQueryServerResource(
+ IN LPWSTR Reserved OPTIONAL,
+ IN LPWSTR LocalName,
+ OUT LPWSTR RemoteName,
+ IN DWORD RemoteNameLen,
+ OUT LPDWORD CharsRequired
+ )
+/*++
+
+Routine Description:
+
+ This function looks up the UNC name associated with the given DOS
+ device name.
+
+Arguments:
+
+ Reserved - Must be NULL.
+
+ LocalName - Supplies the local device name to look up.
+
+ RemoteName - Receives the UNC name mapped to the LocalName.
+
+ RemoteNameLen - Supplies the length of the RemoteName buffer.
+
+ CharsRequired - Receives the length required of the RemoteName buffer
+ to get the UNC name. This value is only returned if the return
+ code is ERROR_MORE_DATA.
+
+Return Value:
+
+ NO_ERROR - Operation was successful.
+
+ WN_BAD_LOCALNAME - LocalName was invalid.
+
+ ERROR_INVALID_PARAMETER - LPT or COM LocalName was specified.
+
+ ERROR_MORE_DATA - RemoteName buffer was too small.
+
+ ERROR_NOT_CONNECTED - LocalName does not map to any server resource.
+
+--*/
+{
+ DWORD status;
+
+ LPWSTR Local;
+ DWORD LocalLength;
+
+ BOOL Impersonate = FALSE ;
+
+ UNREFERENCED_PARAMETER(Reserved);
+
+#if DBG
+ IF_DEBUG(CONNECT) {
+ KdPrint(("\nNWWORKSTATION: NwrQueryServerResource: LocalName %ws, RemoteNameLen %lu\n",
+ LocalName, RemoteNameLen));
+ }
+#endif
+
+ //
+ // Canonicalize the LocalName
+ //
+ if ((status = NwLibCanonLocalName(
+ LocalName,
+ &Local,
+ &LocalLength
+ )) != NO_ERROR) {
+
+ return WN_BAD_LOCALNAME;
+ }
+
+ if ((status = NwImpersonateClient()) != NO_ERROR)
+ {
+ goto CleanExit;
+ }
+
+ Impersonate = TRUE ;
+
+ status = NwGetServerResource(
+ Local,
+ LocalLength,
+ RemoteName,
+ RemoteNameLen,
+ CharsRequired
+ );
+
+ if (status == WN_NOT_CONNECTED)
+ {
+ status = NwGetGatewayResource(
+ Local,
+ RemoteName,
+ RemoteNameLen,
+ CharsRequired
+ );
+ }
+
+CleanExit:
+
+ if (Impersonate) {
+ (void) NwRevertToSelf() ;
+ }
+
+ (void) LocalFree((HLOCAL) Local);
+
+#if DBG
+ IF_DEBUG(CONNECT) {
+ KdPrint(("NWWORKSTATION: NwrQueryServerResource returns %lu\n", status));
+
+ if (status == NO_ERROR) {
+ KdPrint((" RemoteName is %ws\n", RemoteName));
+ }
+ else if (status == ERROR_MORE_DATA) {
+ KdPrint((" RemoteNameLen %lu too small. Need %lu\n",
+ RemoteNameLen, *CharsRequired));
+ }
+ }
+#endif
+
+ return status;
+}
+
+
+DWORD
+NwrOpenEnumConnections(
+ IN LPWSTR Reserved OPTIONAL,
+ IN DWORD ConnectionType,
+ OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
+ )
+/*++
+
+Routine Description:
+
+ This function creates a new context handle and initializes it
+ for enumerating the connections.
+
+Arguments:
+
+ Reserved - Unused.
+
+ EnumHandle - Receives the newly created context handle.
+
+Return Value:
+
+ ERROR_NOT_ENOUGH_MEMORY - if the memory for the context could
+ not be allocated.
+
+ NO_ERROR - Call was successful.
+
+--*/
+{
+ LPNW_ENUM_CONTEXT ContextHandle;
+
+
+ UNREFERENCED_PARAMETER(Reserved);
+
+#if DBG
+ IF_DEBUG(CONNECT) {
+ KdPrint(("\nNWWORKSTATION: NwrOpenEnumConnections\n"));
+ }
+#endif
+
+ //
+ // Allocate memory for the context handle structure.
+ //
+ ContextHandle = (PVOID) LocalAlloc(
+ LMEM_ZEROINIT,
+ sizeof(NW_ENUM_CONTEXT)
+ );
+
+ if (ContextHandle == NULL) {
+ KdPrint(("NWWORKSTATION: NwrOpenEnumConnections LocalAlloc Failed %lu\n",
+ GetLastError()));
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ //
+ // Initialize contents of the context handle structure.
+ //
+ ContextHandle->Signature = NW_HANDLE_SIGNATURE;
+ ContextHandle->HandleType = NwsHandleListConnections;
+ ContextHandle->ResumeId = 0;
+ ContextHandle->ConnectionType = 0;
+
+ if ( ConnectionType == RESOURCETYPE_ANY ) {
+ ContextHandle->ConnectionType = CONNTYPE_ANY;
+ }
+ else {
+
+ if ( ConnectionType & RESOURCETYPE_DISK )
+ ContextHandle->ConnectionType |= CONNTYPE_DISK;
+
+ if ( ConnectionType & RESOURCETYPE_PRINT )
+ ContextHandle->ConnectionType |= CONNTYPE_PRINT;
+ }
+
+
+
+ //
+ // Return the newly created context.
+ //
+ *EnumHandle = (LPNWWKSTA_CONTEXT_HANDLE) ContextHandle;
+
+ return NO_ERROR;
+}
+
+
+DWORD
+NwrGetConnectionPerformance(
+ IN LPWSTR Reserved OPTIONAL,
+ IN LPWSTR lpRemoteName,
+ OUT LPBYTE lpNetConnectInfo,
+ IN DWORD dwBufferSize
+ )
+/*++
+
+Routine Description:
+
+ This function returns information about the expected performance of a
+ connection used to access a network resource. The request can only be
+ for a network resource to which there is currently a connection.
+
+Arguments:
+
+ Reserved - Unused.
+
+ lpRemoteName - Contains the local name or remote name for a resource
+ for which a connection exists.
+
+ lpNetConnectInfo - This is a pointer to a NETCONNECTINFOSTRUCT structure
+ which is to be filled if the connection performance
+ of connection lpRemoteName can be determined.
+
+Return Value:
+
+ NO_ERROR - Successful.
+
+ WN_NOT_CONNECTED - Connection could not be found.
+
+ WN_NONETWORK - Network is not present.
+
+ Other network errors.
+
+--*/
+{
+ DWORD status = NO_ERROR;
+ LPNETCONNECTINFOSTRUCT lpNetConnInfo =
+ (LPNETCONNECTINFOSTRUCT) lpNetConnectInfo;
+ NTSTATUS ntstatus;
+ IO_STATUS_BLOCK IoStatusBlock;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ ACCESS_MASK DesiredAccess = SYNCHRONIZE | FILE_LIST_DIRECTORY;
+ HANDLE hRdr;
+
+ WCHAR OpenString[] = L"\\Device\\Nwrdr\\*";
+ UNICODE_STRING OpenName;
+ UNICODE_STRING ConnectionName;
+
+ PNWR_REQUEST_PACKET Request;
+ ULONG BufferSize = sizeof(NWR_REQUEST_PACKET) +
+ ( ( wcslen(lpRemoteName) + 1 ) * sizeof(WCHAR) );
+ ULONG RequestSize;
+ BOOL Impersonate = FALSE ;
+
+ UNREFERENCED_PARAMETER(Reserved);
+ UNREFERENCED_PARAMETER(dwBufferSize);
+
+ //
+ // Impersonate the client
+ //
+ if ((status = NwImpersonateClient()) != NO_ERROR)
+ {
+ goto ExitWithClose;
+ }
+
+ Impersonate = TRUE;
+
+ //
+ // Allocate buffer space.
+ //
+ Request = (PNWR_REQUEST_PACKET) LocalAlloc( LMEM_ZEROINIT, BufferSize );
+
+ if ( Request == NULL )
+ {
+ KdPrint(("NWWORKSTATION: NwrGetConnectionPerformance LocalAlloc Failed %lu\n",
+ GetLastError()));
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ RtlInitUnicodeString( &OpenName, OpenString );
+
+ InitializeObjectAttributes( &ObjectAttributes,
+ &OpenName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL );
+
+ ntstatus = NtOpenFile( &hRdr,
+ DesiredAccess,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_VALID_FLAGS,
+ FILE_SYNCHRONOUS_IO_NONALERT );
+
+ if ( !NT_SUCCESS(ntstatus) )
+ {
+ status = RtlNtStatusToDosError(ntstatus);
+ goto ExitWithClose;
+ }
+
+ //
+ // Fill out the request packet for FSCTL_NWR_GET_CONN_PERFORMANCE.
+ //
+ RtlInitUnicodeString( &ConnectionName, lpRemoteName );
+
+ Request->Parameters.GetConnPerformance.RemoteNameLength =
+ ConnectionName.Length;
+ RtlCopyMemory( Request->Parameters.GetConnPerformance.RemoteName,
+ ConnectionName.Buffer,
+ ConnectionName.Length );
+
+ RequestSize = sizeof( NWR_REQUEST_PACKET ) + ConnectionName.Length;
+
+ ntstatus = NtFsControlFile( hRdr,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_GET_CONN_PERFORMANCE,
+ (PVOID) Request,
+ RequestSize,
+ NULL,
+ 0 );
+
+ if ( !NT_SUCCESS( ntstatus ) )
+ {
+ status = RtlNtStatusToDosError(ntstatus);
+ goto ExitWithClose;
+ }
+
+ lpNetConnInfo->cbStructure = sizeof(NETCONNECTINFOSTRUCT);
+ lpNetConnInfo->dwFlags = Request->Parameters.GetConnPerformance.dwFlags;
+ lpNetConnInfo->dwSpeed = Request->Parameters.GetConnPerformance.dwSpeed;
+ lpNetConnInfo->dwDelay = Request->Parameters.GetConnPerformance.dwDelay;
+ lpNetConnInfo->dwOptDataSize =
+ Request->Parameters.GetConnPerformance.dwOptDataSize;
+
+ExitWithClose:
+ if ( Request )
+ LocalFree( Request );
+
+ if ( Impersonate )
+ {
+ (void) NwRevertToSelf() ;
+ }
+
+ if ( hRdr )
+ NtClose( hRdr );
+
+ return status;
+}
+
+
+
+DWORD
+NwAllocAndGetUncName(
+ IN LPWSTR LocalName,
+ IN DWORD LocalNameLength,
+ OUT LPWSTR *UncName
+ )
+/*++
+
+Routine Description:
+
+ This function calls an internal routine to ask the redirector for the
+ UNC name of a given DOS device name. It also allocates the output
+ buffer to hold the UNC name.
+
+Arguments:
+
+ LocalName - Supplies the DOS device name.
+
+ LocalNameLength - Supplies the length of the DOS device name (chars).
+
+ UncName - Receives a pointer to the output buffer allocated by this
+ routine which contains the UNC name of the DOS device.
+
+Return Value:
+
+ NO_ERROR - Operation was successful.
+
+ ERROR_NOT_ENOUGH_MEMORY - Could not allocate output buffer.
+
+ Other errors from the redirector.
+--*/
+{
+ DWORD status;
+ DWORD UncNameLength;
+
+
+
+ *UncName = (PVOID) LocalAlloc(
+ LMEM_ZEROINIT,
+ (MAX_PATH + 1) * sizeof(WCHAR)
+ );
+
+ if (*UncName == NULL) {
+ KdPrint(("NWWORKSTATION: NwAllocAndGetUncName LocalAlloc Failed %lu\n",
+ GetLastError()));
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ status = NwGetServerResource(
+ LocalName,
+ LocalNameLength,
+ *UncName,
+ MAX_PATH + 1,
+ &UncNameLength
+ );
+
+ if ((status == ERROR_MORE_DATA) || (status == ERROR_INSUFFICIENT_BUFFER)) {
+
+ //
+ // Our output buffer was too small. Try again.
+ //
+ (void) LocalFree((HLOCAL) *UncName);
+
+ *UncName = (PVOID) LocalAlloc(
+ LMEM_ZEROINIT,
+ UncNameLength * sizeof(WCHAR)
+ );
+
+ if (*UncName == NULL) {
+ KdPrint(("NWWORKSTATION: NwAllocAndGetUncName LocalAlloc Failed %lu\n",
+ GetLastError()));
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ status = NwGetServerResource(
+ LocalName,
+ LocalNameLength,
+ *UncName,
+ UncNameLength,
+ &UncNameLength
+ );
+
+ }
+
+ //
+ // callers will only free this if success.
+ //
+ if (status != NO_ERROR)
+ {
+ (void) LocalFree((HLOCAL) *UncName);
+ *UncName = NULL ;
+ }
+
+ return status;
+}
+
+
+DWORD
+NwOpenHandleToDeleteConn(
+ IN LPWSTR UncName,
+ IN LPWSTR LocalName OPTIONAL,
+ IN DWORD UseForce,
+ IN BOOL IsStopWksta
+ )
+/*++
+
+Routine Description:
+
+ This function deletes an active connection by opening a tree connection
+ handle to the connection first, and specifying this handle to the
+ redirector to delete. This is because the workstation service does
+ not keep any connection information.
+
+Arguments:
+
+ UncName - Supplies the UNC name of the connection to delete.
+
+ LocalName - Supplies the DOS device name of the connection, if any.
+
+ UseForce - Supplies a flag which if TRUE specifies to tear down
+ the connection eventhough files are opened. If FALSE, the
+ connection is deleted only if there are no opened files.
+
+ IsStopWksta - Supplies a flag which if TRUE indicates that we must
+ delete the symbolic link, even when we have failed to delete the
+ connection in the redirector. As much as possible must be cleaned
+ up because the workstation service is stopping. A value of FALSE,
+ indicates that the delete is aborted if we cannot delete it in
+ the redirector.
+
+Return Value:
+
+ NO_ERROR - Operation was successful.
+
+ ERROR_NOT_ENOUGH_MEMORY - Could not allocate output buffer.
+
+ Other errors from the redirector.
+--*/
+{
+ DWORD status;
+ NTSTATUS ntstatus ;
+
+ UNICODE_STRING TreeConnectStr;
+ HANDLE TreeConnection = NULL;
+
+
+
+ TreeConnectStr.Buffer = NULL;
+
+ //
+ // Create an NT-style tree connection name, either: \Device\Nwrdr\Server\Vol
+ // or \Device\Nwrdr\X:\Server\Vol, if LocalName is specified.
+ //
+ if ((status = NwCreateTreeConnectName(
+ UncName,
+ LocalName,
+ &TreeConnectStr
+ )) != NO_ERROR) {
+ return status;
+ }
+
+ ntstatus = NwCallNtOpenFile( &TreeConnection,
+ SYNCHRONIZE | DELETE,
+ &TreeConnectStr,
+ FILE_CREATE_TREE_CONNECTION
+ | FILE_SYNCHRONOUS_IO_NONALERT
+ | FILE_DELETE_ON_CLOSE
+ );
+ //
+ // treat the 2 as the same in order to return nicer error to user
+ //
+ if (ntstatus == STATUS_OBJECT_NAME_INVALID)
+ ntstatus = STATUS_OBJECT_NAME_NOT_FOUND ;
+ status = NwMapStatus(ntstatus) ;
+
+ if (status == NO_ERROR) {
+
+ //
+ // Ask the redirector to delete the tree connection.
+ //
+ status = NwNukeConnection(
+ TreeConnection,
+ UseForce
+ );
+
+ (void) CloseHandle(TreeConnection);
+ }
+
+ if (ARGUMENT_PRESENT(LocalName) &&
+ (status == NO_ERROR || IsStopWksta)) {
+
+ //
+ // Delete the symbolic link we created.
+ //
+ NwDeleteSymbolicLink(
+ LocalName,
+ TreeConnectStr.Buffer
+ );
+ }
+
+ if (TreeConnectStr.Buffer != NULL) {
+ (void) LocalFree((HLOCAL) TreeConnectStr.Buffer);
+ }
+
+ return status;
+}
+
+
+VOID
+DeleteAllConnections(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function deletes all active connections returned by the
+ redirector ENUMERATE_CONNECTIONS fsctl on workstation termination.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ DWORD status;
+ NWWKSTA_CONTEXT_HANDLE EnumHandle;
+ LPNETRESOURCEW NetR = NULL;
+
+ DWORD BytesNeeded = 256;
+ DWORD EntriesRead;
+
+
+ status = NwrOpenEnumConnections(NULL, RESOURCETYPE_ANY, &EnumHandle);
+ if ( status != NO_ERROR )
+ return;
+
+ //
+ // Allocate buffer to get connection list.
+ //
+ if ((NetR = (LPVOID) LocalAlloc(
+ 0,
+ BytesNeeded
+ )) == NULL) {
+
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ goto CleanExit;
+ }
+
+ do {
+
+ status = NwEnumerateConnections(
+ &((LPNW_ENUM_CONTEXT) EnumHandle)->ResumeId,
+ 0xFFFFFFFF,
+ (LPBYTE) NetR,
+ BytesNeeded,
+ &BytesNeeded,
+ &EntriesRead,
+ CONNTYPE_ANY
+ );
+
+ if (status == NO_ERROR) {
+
+ DWORD i;
+ LPNETRESOURCEW SavePtr = NetR;
+ LPWSTR Local;
+
+
+ for (i = 0; i < EntriesRead; i++, NetR++) {
+
+ Local = NetR->lpLocalName;
+
+ if (NetR->lpLocalName && *(NetR->lpLocalName) == 0) {
+ Local = NULL;
+ }
+
+ (void) NwOpenHandleToDeleteConn(
+ NetR->lpRemoteName,
+ Local,
+ TRUE,
+ TRUE
+ );
+ }
+
+ NetR = SavePtr;
+
+ }
+ else if (status == WN_MORE_DATA) {
+
+ //
+ // Original buffer was too small. Free it and allocate
+ // the recommended size and then some to get as many
+ // entries as possible.
+ //
+
+ (void) LocalFree((HLOCAL) NetR);
+
+ BytesNeeded += NW_ENUM_EXTRA_BYTES;
+
+ if ((NetR = (LPVOID) LocalAlloc(
+ 0,
+ BytesNeeded
+ )) == NULL) {
+
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ goto CleanExit;
+ }
+ }
+ else {
+ // give up if see any other return code
+ break ;
+ }
+
+ } while (status != WN_NO_MORE_ENTRIES);
+
+CleanExit:
+ (void) NwrCloseEnum(&EnumHandle);
+
+ if (NetR != NULL) {
+ (void) LocalFree((HLOCAL) NetR);
+ }
+}
+
+
+
+DWORD
+NwCreateSymbolicLink(
+ IN LPWSTR Local,
+ IN LPWSTR TreeConnectStr
+ )
+/*++
+
+Routine Description:
+
+ This function creates a symbolic link object for the specified local
+ device name which is linked to the tree connection name that has a
+ format of \Device\NwRdr\Device:\Server\Volume\Directory.
+
+Arguments:
+
+ Local - Supplies the local device name.
+
+ TreeConnectStr - Supplies the tree connection name string which is
+ the link target of the symbolick link object.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+{
+ WCHAR TempBuf[64];
+
+ DWORD LocalLength = wcslen(Local);
+
+
+ if (LocalLength > 2) {
+
+ LPWSTR UncName;
+
+
+ //
+ // Local device is LPTn:
+ //
+
+ //
+ // Check to see if we already have this UNC name mapped.
+ //
+ if (NwAllocAndGetUncName(
+ Local,
+ LocalLength,
+ &UncName
+ ) == NO_ERROR) {
+
+ LocalFree((HLOCAL) UncName);
+ return ERROR_ALREADY_ASSIGNED;
+ }
+
+ }
+ else {
+
+ //
+ // Local device is X:
+ //
+
+ if (! QueryDosDeviceW(
+ Local,
+ TempBuf,
+ 64
+ )) {
+
+ if (GetLastError() != ERROR_FILE_NOT_FOUND) {
+
+ //
+ // Most likely failure occurred because our output
+ // buffer is too small. It still means someone already
+ // has an existing symbolic link for this device.
+ //
+
+ return ERROR_ALREADY_ASSIGNED;
+ }
+
+ //
+ // ERROR_FILE_NOT_FOUND (translated from OBJECT_NAME_NOT_FOUND)
+ // means it does not exist and we can redirect this device.
+ //
+ }
+ else {
+
+ //
+ // QueryDosDevice successfully an existing symbolic link--
+ // somebody is already using this device.
+ //
+ return ERROR_ALREADY_ASSIGNED;
+ }
+ }
+
+ //
+ // Create a symbolic link object to the device we are redirecting
+ //
+ if (! DefineDosDeviceW(
+ DDD_RAW_TARGET_PATH | DDD_NO_BROADCAST_SYSTEM,
+ Local,
+ TreeConnectStr
+ )) {
+
+ return GetLastError();
+ }
+
+ return NO_ERROR;
+}
+
+
+
+VOID
+NwDeleteSymbolicLink(
+ IN LPWSTR LocalDeviceName,
+ IN LPWSTR TreeConnectStr
+ )
+/*++
+
+Routine Description:
+
+ This function deletes the symbolic link we had created earlier for
+ the device.
+
+Arguments:
+
+ LocalDeviceName - Supplies the local device name string of which the
+ symbolic link object is created.
+
+ TreeConnectStr - Supplies a pointer to the Unicode string which
+ contains the link target string we want to match and delete.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ if (LocalDeviceName != NULL) {
+
+ if (! DefineDosDeviceW(
+ DDD_REMOVE_DEFINITION | DDD_RAW_TARGET_PATH |
+ DDD_EXACT_MATCH_ON_REMOVE | DDD_NO_BROADCAST_SYSTEM,
+ LocalDeviceName,
+ TreeConnectStr
+ )) {
+
+#if DBG
+ IF_DEBUG(CONNECT) {
+ KdPrint(("NWWORKSTATION: DefineDosDevice DEL of %ws %ws returned %lu\n",
+ LocalDeviceName, TreeConnectStr, GetLastError()));
+ }
+#endif
+ }
+#if DBG
+ else {
+ IF_DEBUG(CONNECT) {
+ KdPrint(("NWWORKSTATION: DefineDosDevice DEL of %ws %ws returned successful\n",
+ LocalDeviceName, TreeConnectStr));
+ }
+
+ }
+#endif
+
+ }
+}
+
+DWORD
+NwCreateGWConnection(
+ IN LPWSTR RemoteName,
+ IN LPWSTR UserName,
+ IN LPWSTR Password,
+ IN BOOL KeepConnection
+ )
+/*++
+
+Routine Description:
+
+ This function creates a tree connection to the specified RemoteName
+ (UNC name). It is only used by the Gateway and DOES NOT impersonate.
+
+Arguments:
+
+ RemoteName - Supplies the UNC name of the remote resource in the format
+ of Server\Volume\Directory. It must be a disk resource.
+
+Return Value:
+
+ NO_ERROR - Operation was successful.
+
+ ERROR_NOT_ENOUGH_MEMORY - Out of memory allocating internal work buffers.
+
+ WN_BAD_NETNAME - Remote resource name is invalid.
+
+ WN_BAD_LOCALNAME - Local DOS device name is invalid.
+
+ ERROR_BAD_NETPATH - The UNC name does not exist on the network.
+
+ ERROR_INVALID_PARAMETER - LPT or COM LocalName was specified.
+
+ Other errors from the redirector.
+
+--*/
+{
+ DWORD status;
+ LPWSTR Unc = NULL;
+ LPWSTR User = NULL;
+ UNICODE_STRING TreeConnectStr;
+ HANDLE TreeConnection;
+
+ TreeConnectStr.Buffer = NULL;
+
+ //
+ // Canonicalize the remote name, if it is not \\Server.
+ //
+ if ((status = NwLibCanonRemoteName(
+ NULL,
+ RemoteName,
+ &Unc, // Must be freed with LocalFree when done.
+ NULL
+ )) != NO_ERROR)
+ {
+ status = WN_BAD_NETNAME;
+ goto CleanExit;
+ }
+
+ if (UserName != NULL) {
+
+ //
+ // Canonicalize username
+ //
+ if ((status = NwLibCanonUserName(
+ UserName,
+ &User, // Must be freed with LocalFree when done.
+ NULL
+ )) != NO_ERROR) {
+
+ status = WN_BAD_VALUE;
+ goto CleanExit;
+ }
+ }
+
+ //
+ // Create an NT-style tree connection name
+ //
+ if ((status = NwCreateTreeConnectName(
+ Unc,
+ NULL,
+ &TreeConnectStr
+ )) != NO_ERROR)
+ {
+ goto CleanExit;
+ }
+
+
+ //
+ // Create the tree connection while impersonating the client so
+ // that redirector can get to caller's logon id.
+ //
+ status = NwOpenCreateConnection(
+ &TreeConnectStr,
+ User,
+ Password,
+ Unc,
+ SYNCHRONIZE | GENERIC_WRITE,
+ FILE_CREATE, // Fail if already exist
+ FILE_CREATE_TREE_CONNECTION |
+ FILE_SYNCHRONOUS_IO_NONALERT,
+ RESOURCETYPE_DISK,
+ &TreeConnection,
+ NULL
+ );
+
+
+ if (status != NO_ERROR) {
+
+ if ( (status == ERROR_NOT_CONNECTED)
+ || (status == ERROR_FILE_NOT_FOUND )
+ )
+ {
+ status = ERROR_BAD_NETPATH;
+ }
+ }
+ else
+ {
+
+ //
+ // Just close the connection handle.
+ //
+ (void) NtClose(TreeConnection);
+
+
+ //
+ // delete the connect we just created. ignore this error.
+ //
+ if (!KeepConnection)
+ {
+ (void) NwOpenHandleToDeleteConn(
+ RemoteName,
+ NULL,
+ FALSE,
+ FALSE
+ );
+ }
+ }
+
+
+CleanExit:
+
+ if (User != NULL) {
+ (void) LocalFree((HLOCAL) User);
+ }
+
+ if (Unc != NULL) {
+ (void) LocalFree((HLOCAL) Unc);
+ }
+
+ if (TreeConnectStr.Buffer != NULL) {
+ (void) LocalFree((HLOCAL) TreeConnectStr.Buffer);
+ }
+
+ return status;
+}
+
+DWORD
+NwDeleteGWConnection(
+ IN LPWSTR ConnectionName
+ )
+/*++
+
+Routine Description:
+
+ This function deletes an existing connection.
+
+Arguments:
+
+ ConnectionName - Supplies the local device name or UNC name which
+ specifies the connection to delete. If UNC name is specified,
+ the UNC connection must exist.
+
+Return Value:
+
+ NO_ERROR - Operation was successful.
+
+ ERROR_NOT_ENOUGH_MEMORY - Out of memory allocating internal work buffers.
+
+ WN_BAD_NETNAME - ConnectionName is invalid.
+
+ ERROR_BAD_NETPATH - The UNC name does not exist on the network.
+
+ ERROR_INVALID_PARAMETER - LPT or COM LocalName was specified.
+
+ Other errors from the redirector.
+
+--*/
+{
+ DWORD status;
+
+ LPWSTR ConnectName = NULL;
+ DWORD ConnectLength;
+
+ if (!ConnectionName || *ConnectionName == 0) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ //
+ // See if it is a UNC name.
+ //
+ if ((status = NwLibCanonRemoteName(
+ NULL,
+ ConnectionName,
+ &ConnectName,
+ NULL
+ )) != NO_ERROR) {
+
+ return status;
+ }
+
+ //
+ // To delete a connection, a tree connection handle must be opened to
+ // it so that the handle can be specified to the redirector to delete
+ // the connection.
+ //
+ status = NwOpenHandleToDeleteConn(
+ ConnectName,
+ NULL,
+ TRUE,
+ FALSE
+ );
+
+ if ( status == ERROR_FILE_NOT_FOUND )
+ status = ERROR_BAD_NETPATH;
+
+ if (ConnectName != NULL) {
+ (void) LocalFree((HLOCAL) ConnectName);
+ }
+
+ return status;
+}
+
+
+DWORD
+NwCreateConnection(
+ IN LPWSTR LocalName OPTIONAL,
+ IN LPWSTR RemoteName,
+ IN DWORD Type,
+ IN LPWSTR Password OPTIONAL,
+ IN LPWSTR UserName OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function creates a tree connection to the specified RemoteName
+ (UNC name) and maps it to the LocalName (local device name), if
+ it is specified. The password and user name are the credentials
+ used to create the connection, if specified; otherwise, the
+ interactive logged on user's credentials are used by default.
+
+ NOTE: This code used to be NwrCreateConnection, except that it used
+ to have the ImpersonateClient() call in it. Now this code is here, and
+ NwrCreateConnection calls this function and handles the client
+ impersonation there. The reason for this is to allow the print spooler
+ code to call this helper routine without calling Impersonate client a
+ second time, thus reverting the credentials to that of services.exe.
+
+Arguments:
+
+ LocalName - Supplies the local device name to map to the created tree
+ connection. Only drive letter device names are accepted. (No
+ LPT or COM).
+
+ RemoteName - Supplies the UNC name of the remote resource in the format
+ of Server\Volume\Directory. It must be a disk resource.
+
+ Type - Supplies the connection type.
+
+ Password - Supplies the password to use to make the connection to the
+ server.
+
+ UserName - Supplies the user name to use to make the connection.
+
+Return Value:
+
+ NO_ERROR - Operation was successful.
+
+ ERROR_NOT_ENOUGH_MEMORY - Out of memory allocating internal work buffers.
+
+ WN_BAD_NETNAME - Remote resource name is invalid.
+
+ WN_BAD_LOCALNAME - Local DOS device name is invalid.
+
+ ERROR_BAD_NETPATH - The UNC name does not exist on the network.
+
+ ERROR_INVALID_PARAMETER - LPT or COM LocalName was specified.
+
+ Other errors from the redirector.
+
+--*/
+{
+ DWORD status;
+
+ DWORD LocalLength;
+
+ LPWSTR Local = NULL;
+ LPWSTR Unc = NULL;
+ LPWSTR User = NULL;
+
+ UNICODE_STRING TreeConnectStr;
+ UNICODE_STRING EncodedPassword;
+ HANDLE TreeConnection;
+
+ TreeConnectStr.Buffer = NULL;
+
+ EncodedPassword.Length = 0;
+
+ //
+ // If local device is an empty string, it will be treated as a pointer to
+ // NULL.
+ //
+ if (LocalName != NULL && *LocalName != 0) {
+
+ //
+ // Local device name is not NULL, canonicalize it
+ //
+#if DBG
+ IF_DEBUG(CONNECT) {
+ KdPrint(("\nNWWORKSTATION: NwCreateConnection: LocalName %ws\n", LocalName));
+ }
+#endif
+
+ if ((status = NwLibCanonLocalName(
+ LocalName,
+ &Local, // Must be freed with LocalFree when done.
+ &LocalLength
+ )) != NO_ERROR) {
+
+ return WN_BAD_LOCALNAME;
+ }
+ }
+
+#if DBG
+ IF_DEBUG(CONNECT) {
+ KdPrint(("NWWORKSTATION: NwCreateConnection: RemoteName %ws\n", RemoteName));
+ }
+#endif
+
+ //
+ // Canonicalize the remote name, if it is not \\Server.
+ //
+ status = NwLibCanonRemoteName(
+ Local,
+ RemoteName,
+ &Unc, // Must be freed with LocalFree when done.
+ NULL
+ );
+
+ if (status != NO_ERROR)
+ {
+ status = WN_BAD_NETNAME;
+ goto CleanExit;
+ }
+
+ //
+ // Canonicalize user name.
+ //
+ if (UserName != NULL) {
+
+ //
+ // Canonicalize username
+ //
+#if DBG
+ IF_DEBUG(CONNECT) {
+ KdPrint(("NWWORKSTATION: NwCreateConnection: UserName %ws\n",
+ UserName));
+ }
+#endif
+
+ if ((status = NwLibCanonUserName(
+ UserName,
+ &User, // Must be freed with LocalFree when done.
+ NULL
+ )) != NO_ERROR) {
+
+#ifdef QFE_BUILD
+ //
+ // if not valid, just ignore the username. this works
+ // around MPR bug where if you pass say domain\user to NWRDR
+ // as first provider, and he throws it out, then the next one
+ // doesnt get a chance.
+ //
+ // BUGBUG - this should be removed when MPR bug #4051 is fixed
+ // and all platforms we ship NWRDR have that fix.
+ //
+ UserName = NULL ;
+ status = NO_ERROR;
+#else
+ status = WN_BAD_VALUE;
+ goto CleanExit;
+#endif
+ }
+ }
+
+ //
+ // For password any syntax or length is accepted.
+ //
+ if (Password != NULL) {
+
+#if DBG
+ IF_DEBUG(CONNECT) {
+ KdPrint(("NWWORKSTATION: NwCreateConnection: Password %ws\n",
+ Password));
+ }
+#endif
+ //
+ // Decode the password
+ //
+ RtlInitUnicodeString(&EncodedPassword, Password);
+ RtlRunDecodeUnicodeString(NW_ENCODE_SEED3, &EncodedPassword);
+ }
+
+ //
+ // Create an NT-style tree connection name
+ //
+ if ((status = NwCreateTreeConnectName(
+ Unc,
+ Local,
+ &TreeConnectStr
+ )) != NO_ERROR) {
+ goto CleanExit;
+ }
+
+ if (Local != NULL) {
+
+ //
+ // Create symbolic link for local device name.
+ //
+ if ((status = NwCreateSymbolicLink(
+ Local,
+ TreeConnectStr.Buffer
+ )) != NO_ERROR) {
+
+ goto CleanExit;
+ }
+ }
+
+ //
+ // Create the tree connection while impersonating the client so
+ // that redirector can get to caller's logon id.
+ //
+ status = NwOpenCreateConnection(
+ &TreeConnectStr,
+ User,
+ Password,
+ Unc,
+ SYNCHRONIZE | GENERIC_WRITE,
+ FILE_CREATE, // Fail if already exist
+ FILE_CREATE_TREE_CONNECTION |
+ FILE_SYNCHRONOUS_IO_NONALERT,
+ Type,
+ &TreeConnection,
+ NULL
+ );
+
+ //
+ // If there's a problem creating the tree connection, remove symbolic
+ // link if any.
+ //
+ if (status != NO_ERROR) {
+
+ if ( (status == ERROR_NOT_CONNECTED) ||
+ (status == ERROR_FILE_NOT_FOUND) ||
+ (status == ERROR_INVALID_NAME) )
+ {
+ status = ERROR_BAD_NETPATH;
+ }
+
+ if ( status == ERROR_CONNECTION_INVALID )
+ {
+ status = WN_BAD_NETNAME;
+ }
+
+ //
+ // Delete the symbolic link we created.
+ //
+ NwDeleteSymbolicLink(
+ Local,
+ TreeConnectStr.Buffer
+ );
+ }
+ else {
+
+ //
+ // Just close the connection handle.
+ //
+ (void) NtClose(TreeConnection);
+ }
+
+CleanExit:
+ if (Local != NULL) {
+ (void) LocalFree((HLOCAL) Local);
+ }
+
+ if (Unc != NULL) {
+ (void) LocalFree((HLOCAL) Unc);
+ }
+
+ if (User != NULL) {
+ (void) LocalFree((HLOCAL) User);
+ }
+
+ if (TreeConnectStr.Buffer != NULL) {
+ (void) LocalFree((HLOCAL) TreeConnectStr.Buffer);
+ }
+
+ //
+ // Put the password back the way we found it.
+ //
+ if (EncodedPassword.Length != 0) {
+
+ UCHAR Seed = NW_ENCODE_SEED3;
+
+ RtlRunEncodeUnicodeString(&Seed, &EncodedPassword);
+ }
+
+
+#if DBG
+ IF_DEBUG(CONNECT) {
+ KdPrint(("NWWORKSTATION: NwCreateConnection returns %lu\n", status));
+ }
+#endif
+
+ return status;
+}
+
+
diff --git a/private/nw/svcdlls/nwwks/server/credentl.c b/private/nw/svcdlls/nwwks/server/credentl.c
new file mode 100644
index 000000000..b26544c0f
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/server/credentl.c
@@ -0,0 +1,1462 @@
+/*++
+
+Copyright (c) 1993, 1994 Microsoft Corporation
+
+Module Name:
+
+ credentl.c
+
+Abstract:
+
+ This module contains credential management routines supported by
+ NetWare Workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 15-Feb-1993
+
+Revision History:
+
+ 13-Apr-1994 Added change password code written by ColinW, AndyHe,
+ TerenceS, and RitaW.
+
+--*/
+
+#include <nw.h>
+#include <nwreg.h>
+#include <nwlsa.h>
+#include <nwauth.h>
+#include <nwxchg.h>
+#include <nwapi.h>
+
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+//
+// Variables to coordinate reading of user logon credential from the
+// registry if the user logged on before the workstation is started.
+//
+STATIC BOOL NwLogonNotifiedRdr;
+
+
+STATIC
+DWORD
+NwpRegisterLogonProcess(
+ OUT PHANDLE LsaHandle,
+ OUT PULONG AuthPackageId
+ );
+
+STATIC
+VOID
+NwpGetServiceCredentials(
+ IN HANDLE LsaHandle,
+ IN ULONG AuthPackageId
+ );
+
+STATIC
+DWORD
+NwpGetCredentialInLsa(
+ IN HANDLE LsaHandle,
+ IN ULONG AuthPackageId,
+ IN PLUID LogonId,
+ OUT LPWSTR *UserName,
+ OUT LPWSTR *Password
+ );
+
+
+DWORD
+NwrLogonUser(
+ IN LPWSTR Reserved OPTIONAL,
+ IN PLUID LogonId,
+ IN LPWSTR UserName,
+ IN LPWSTR Password OPTIONAL,
+ IN LPWSTR PreferredServerName OPTIONAL,
+ OUT LPWSTR LogonCommand OPTIONAL,
+ IN DWORD LogonCommandLength
+ )
+/*++
+
+Routine Description:
+
+ This function logs on the user to NetWare network. It passes the
+ user logon credential to the redirector to be used as the default
+ credential when attaching to any server.
+
+Arguments:
+
+ Reserved - Must be NULL.
+
+ UserName - Specifies the name of the user who logged on.
+
+ Password - Specifies the password of the user who logged on.
+
+ PreferredServerName - Specifies the user's preferred server.
+
+ LogonCommand - Receives the string which is the command to execute
+ on the command prompt for the user if logon is successful.
+
+Return Value:
+
+ NO_ERROR or error from redirector.
+
+--*/
+{
+ DWORD status;
+ LUID SystemId = SYSTEM_LUID ;
+
+ UNREFERENCED_PARAMETER(Reserved);
+
+ EnterCriticalSection(&NwLoggedOnCritSec);
+
+ status = NwRdrLogonUser(
+ LogonId,
+ UserName,
+ wcslen(UserName) * sizeof(WCHAR),
+ Password,
+ (ARGUMENT_PRESENT(Password) ?
+ wcslen(Password) * sizeof(WCHAR) :
+ 0),
+ PreferredServerName,
+ (ARGUMENT_PRESENT(PreferredServerName) ?
+ wcslen(PreferredServerName) * sizeof(WCHAR) :
+ 0)
+ );
+
+ if (status == NO_ERROR || status == NW_PASSWORD_HAS_EXPIRED) {
+ NwLogonNotifiedRdr = TRUE;
+ if (RtlEqualLuid(LogonId, &SystemId))
+ GatewayLoggedOn = TRUE ;
+ }
+
+ LeaveCriticalSection(&NwLoggedOnCritSec);
+
+
+ if (ARGUMENT_PRESENT(LogonCommand) && (LogonCommandLength >= sizeof(WCHAR))) {
+ LogonCommand[0] = 0;
+ }
+
+ return status;
+}
+
+
+DWORD
+NwrLogoffUser(
+ IN LPWSTR Reserved OPTIONAL,
+ IN PLUID LogonId
+ )
+/*++
+
+Routine Description:
+
+ This function tells the redirector to log off the interactive
+ user.
+
+Arguments:
+
+ Reserved - Must be NULL.
+
+ LogonId - PLUID identifying the logged on process. if NULL, then gateway.
+
+Return Value:
+
+
+--*/
+{
+ DWORD status = NO_ERROR ;
+ LUID SystemId = SYSTEM_LUID ;
+
+ UNREFERENCED_PARAMETER(Reserved);
+
+ EnterCriticalSection(&NwLoggedOnCritSec);
+
+ if (GatewayLoggedOn || !RtlEqualLuid(LogonId, &SystemId))
+ status = NwRdrLogoffUser(LogonId);
+
+ if (status == NO_ERROR && RtlEqualLuid(LogonId, &SystemId))
+ GatewayLoggedOn = FALSE ;
+
+ LeaveCriticalSection(&NwLoggedOnCritSec);
+
+ return status ;
+}
+
+
+DWORD
+NwrSetInfo(
+ IN LPWSTR Reserved OPTIONAL,
+ IN DWORD PrintOption,
+ IN LPWSTR PreferredServerName OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function sets the preferred server and print option in
+ the redirector for the interactive user.
+
+Arguments:
+
+ Reserved - Must be NULL.
+
+ PreferredServerName - Specifies the user's preferred server.
+
+ PrintOption - Specifies the user's print option flag
+
+Return Value:
+
+ NO_ERROR or error from redirector.
+
+--*/
+{
+ DWORD err;
+
+ UNREFERENCED_PARAMETER(Reserved);
+
+ NwPrintOption = PrintOption;
+
+ err = NwRdrSetInfo(
+ PrintOption,
+ NwPacketBurstSize, // just reset to current
+ PreferredServerName,
+ (PreferredServerName != NULL ?
+ wcslen( PreferredServerName) * sizeof( WCHAR ) : 0 ),
+ NwProviderName, // just reset to current
+ wcslen( NwProviderName ) * sizeof( WCHAR )
+ );
+
+ return err;
+}
+
+DWORD
+NwrSetLogonScript(
+ IN LPWSTR Reserved OPTIONAL,
+ IN DWORD ScriptOptions
+ )
+/*++
+
+Routine Description:
+
+ This function sets logon script related info. Currently, all that is
+ supported is to turn the Run Logon Scripts Synchronously flag on and off.
+ We do this using the global flag and not per user because at NPLogonNotify
+ time we dont have per user registry yet. And rather than turn on & leave
+ on, we turn on as need so that users that dont run NW scripts dont need
+ wait.
+
+Arguments:
+
+ Reserved - Must be NULL.
+
+ ScriptOptions - options for logon scripts.
+
+Return Value:
+
+ Win32 error from calls made.
+
+--*/
+{
+ DWORD dwSync, err = NO_ERROR ;
+ HKEY hKeyWinLogon = NULL, hKeyNWC = NULL ;
+
+ UNREFERENCED_PARAMETER(Reserved);
+
+ //
+ // *** Note that in this function we intentionally do not impersonate ***
+ // *** since we are modifying registry under \SOFTWARE & \SYSTEM. ***
+ //
+
+ //
+ // Check the parameters.
+ //
+ if (ScriptOptions == SYNC_LOGONSCRIPT)
+ {
+ dwSync = 1 ; // this is value WinLogon needs to sync login scripts.
+ }
+ else if (ScriptOptions == RESET_SYNC_LOGONSCRIPT)
+ {
+ dwSync = 0 ;
+ }
+ else
+ {
+ return(ERROR_INVALID_PARAMETER) ;
+ }
+
+ //
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentVersion\Services\NwcWorkstation
+ // \Parameters. We use this location to record the fact we temporarily
+ // turned on the Sync Scripts Flag.
+ //
+ err = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_REGKEY,
+ 0,
+ KEY_READ | KEY_WRITE, // desired access
+ &hKeyNWC) ;
+ if ( err )
+ {
+ return err ;
+ }
+
+ //
+ // We are resetting. Check if we turned the flag on. If no, then leave
+ // it be.
+ //
+ if (ScriptOptions == RESET_SYNC_LOGONSCRIPT)
+ {
+ DWORD dwType, dwValue = 0 ;
+ DWORD dwSize = sizeof(dwValue) ;
+
+ err = RegQueryValueExW(
+ hKeyNWC,
+ NW_SYNCLOGONSCRIPT_VALUENAME,
+ NULL,
+ &dwType, // ignored
+ (LPBYTE) &dwValue,
+ &dwSize) ;
+
+ if ((err != NO_ERROR) || (dwValue == 0))
+ {
+ //
+ // value not there or zero. ie. assume we didnt set. quit now.
+ //
+ goto ExitPoint ;
+ }
+ }
+
+ //
+ //
+ // Open HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion
+ // \WinLogon.
+ //
+ err = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ WINLOGON_REGKEY,
+ 0,
+ KEY_READ | KEY_WRITE, // desired access
+ &hKeyWinLogon) ;
+ if ( err )
+ {
+ goto ExitPoint ;
+ }
+
+ //
+ // We are setting. Check if flag is already on. If yes, then leave
+ // it be.
+ //
+ if (ScriptOptions == SYNC_LOGONSCRIPT)
+ {
+ DWORD dwType, dwValue = 0 ;
+ DWORD dwSize = sizeof(dwValue) ;
+
+ err = RegQueryValueExW(
+ hKeyWinLogon,
+ SYNCLOGONSCRIPT_VALUENAME,
+ NULL,
+ &dwType, // ignored
+ (LPBYTE) &dwValue,
+ &dwSize) ;
+
+ if ((err == NO_ERROR) && (dwValue == 1))
+ {
+ //
+ // already on. nothing to do. just return.
+ //
+ goto ExitPoint ;
+ }
+ }
+ //
+ // Write out value to make logon scripts synchronous. Or to reset it.
+ //
+ err = RegSetValueExW(
+ hKeyWinLogon,
+ SYNCLOGONSCRIPT_VALUENAME,
+ 0,
+ REG_DWORD,
+ (LPBYTE) &dwSync, // either 1 or 0.
+ sizeof(dwSync)) ;
+
+ if (err == NO_ERROR)
+ {
+ DWORD dwValue = (ScriptOptions == SYNC_LOGONSCRIPT) ? 1 : 0 ;
+ //
+ // We have successfully set WinLogon flag. Record (or clear)
+ // our own flag.
+ //
+ err = RegSetValueExW(
+ hKeyNWC,
+ NW_SYNCLOGONSCRIPT_VALUENAME,
+ 0,
+ REG_DWORD,
+ (LPBYTE) &dwValue,
+ sizeof(dwValue)) ;
+ }
+
+ExitPoint:
+
+ if (hKeyWinLogon)
+ (void) RegCloseKey( hKeyWinLogon );
+ if (hKeyNWC)
+ (void) RegCloseKey( hKeyNWC );
+
+ return err;
+}
+
+
+DWORD
+NwrValidateUser(
+ IN LPWSTR Reserved OPTIONAL,
+ IN LPWSTR PreferredServerName
+ )
+/*++
+
+Routine Description:
+
+ This function checks whether the user can be authenticated
+ successfully on the given server.
+
+Arguments:
+
+ Reserved - Must be NULL.
+
+ PreferredServerName - Specifies the user's preferred server.
+
+Return Value:
+
+ NO_ERROR or error that occurred during authentication.
+
+--*/
+{
+ DWORD status ;
+ UNREFERENCED_PARAMETER(Reserved);
+
+
+ if ( ( PreferredServerName != NULL )
+ && ( *PreferredServerName != 0 )
+ )
+ {
+ //
+ // Impersonate the client
+ //
+ if ((status = NwImpersonateClient()) != NO_ERROR)
+ {
+ return status ;
+ }
+
+ status = NwConnectToServer( PreferredServerName ) ;
+
+ (void) NwRevertToSelf() ;
+
+ return status ;
+
+ }
+
+ return NO_ERROR;
+}
+
+
+VOID
+NwInitializeLogon(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function initializes the data in the workstation which handles
+ user logon. It is called by the initialization thread.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ //
+ // Initialize logon flag. When the redirector LOGON FsCtl has been
+ // called, this flag will be set to TRUE. Initialize the
+ // critical section to serialize access to NwLogonNotifiedRdr flag.
+ //
+ NwLogonNotifiedRdr = FALSE;
+
+
+}
+
+
+
+VOID
+NwGetLogonCredential(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function reads the user and service logon IDs from the registry so
+ that it can get the credentials from LSA.
+
+ It handles the case where the user has logged on before the workstation
+ is started. This function is called by the initialization thread
+ after opening up the RPC interface so that if user logon is happening
+ concurrently, the provider is given a chance to call the NwrLogonUser API
+ first, making it no longer necessary for the workstation to also
+ retrieve the credential from the registry.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ DWORD status;
+ LONG RegError;
+
+ HANDLE LsaHandle;
+ ULONG AuthPackageId;
+
+ HKEY WkstaKey = NULL;
+ HKEY WkstaLogonKey = NULL;
+ HKEY WkstaOptionKey = NULL;
+ HKEY CurrentUserLogonKey = NULL;
+ HKEY CurrentUserOptionKey = NULL;
+
+ LPWSTR PreferredServer = NULL;
+ LPWSTR CurrentUser = NULL;
+ LPWSTR UserName = NULL;
+ LPWSTR Password = NULL;
+ PLUID LogonId = NULL;
+ PDWORD PrintOption = NULL;
+
+
+
+ EnterCriticalSection(&NwLoggedOnCritSec);
+
+ if (NwLogonNotifiedRdr) {
+ //
+ // Logon credential's already made known to the redirector by
+ // the provider calling the NwrLogonUser API.
+ //
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("\nNWWORKSTATION: Redirector already has logon credential\n"));
+ }
+#endif
+ LeaveCriticalSection(&NwLoggedOnCritSec);
+ return;
+ }
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWWORKSTATION: Main init--NwGetLogonCredential\n"));
+ }
+#endif
+
+ status = NwpRegisterLogonProcess(&LsaHandle, &AuthPackageId);
+
+ if (status != NO_ERROR) {
+ LeaveCriticalSection(&NwLoggedOnCritSec);
+ return;
+ }
+
+ //
+ // Tell the redirector about service credentials
+ //
+ NwpGetServiceCredentials(LsaHandle, AuthPackageId);
+
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters
+ //
+ RegError = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_REGKEY,
+ REG_OPTION_NON_VOLATILE, // options
+ KEY_READ, // desired access
+ &WkstaKey
+ );
+
+ if (RegError != ERROR_SUCCESS) {
+ KdPrint(("NWWORKSTATION: NwGetLogonCredential: RegOpenKeyExW Parameter returns unexpected error %lu!!\n", RegError));
+ goto CleanExit;
+ }
+
+ //
+ // Read the current user SID string from the registry so we can
+ // read user information stored under the SID key.
+ //
+ status = NwReadRegValue(
+ WkstaKey,
+ NW_CURRENTUSER_VALUENAME,
+ &CurrentUser
+ );
+
+ if (status != NO_ERROR) {
+ goto CleanExit;
+ }
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWWORKSTATION: Read the current user SID value %ws\n", CurrentUser));
+ }
+#endif
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters\Logon
+ //
+ RegError = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_LOGON_REGKEY,
+ REG_OPTION_NON_VOLATILE, // options
+ KEY_READ, // desired access
+ &WkstaLogonKey
+ );
+
+ if (RegError != ERROR_SUCCESS) {
+ KdPrint(("NWWORKSTATION: NwGetLogonCredential: RegOpenKeyExW Parameter\\Logon returns unexpected error %lu!!\n",
+ RegError));
+ goto CleanExit;
+ }
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters\Option
+ //
+ RegError = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_OPTION_REGKEY,
+ REG_OPTION_NON_VOLATILE, // options
+ KEY_READ, // desired access
+ &WkstaOptionKey
+ );
+
+ if (RegError != ERROR_SUCCESS) {
+ KdPrint(("NWWORKSTATION: NwGetLogonCredential: RegOpenKeyExW Parameter\\Option returns unexpected error %lu!!\n",
+ RegError));
+ goto CleanExit;
+ }
+
+ //
+ // Open the <CurrentUser> key under Logon
+ //
+ RegError = RegOpenKeyExW(
+ WkstaLogonKey,
+ CurrentUser,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ,
+ &CurrentUserLogonKey
+ );
+
+ if (RegError != ERROR_SUCCESS) {
+ KdPrint(("NWWORKSTATION: NwGetLogonCredential: Open %ws key under Logon failed %lu\n", CurrentUser, RegError));
+ goto CleanExit;
+ }
+
+ //
+ // Open the <CurrentUser> key under Option
+ //
+ RegError = RegOpenKeyExW(
+ WkstaOptionKey,
+ CurrentUser,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ,
+ &CurrentUserOptionKey
+ );
+
+ if (RegError == NO_ERROR) {
+
+ //
+ // Read the logon ID value.
+ //
+ status = NwReadRegValue(
+ CurrentUserLogonKey,
+ NW_LOGONID_VALUENAME,
+ (LPWSTR *) &LogonId
+ );
+
+ if (status != NO_ERROR) {
+ KdPrint(("NWWORKSTATION: NwGetLogonCredential: Could not read logon ID from reg %lu\n",
+ status));
+ LogonId = NULL;
+ goto CleanExit;
+ }
+
+ //
+ // Get the username and password from LSA
+ //
+ status = NwpGetCredentialInLsa(
+ LsaHandle,
+ AuthPackageId,
+ LogonId,
+ &UserName,
+ &Password
+ );
+
+ if (status != NO_ERROR) {
+ KdPrint(("NWWORKSTATION: NwGetLogonCredential: Could not get username & password from LSA %lu\n",
+ status));
+ UserName = NULL;
+ goto CleanExit;
+ }
+
+ //
+ // Read the preferred server value.
+ //
+ status = NwReadRegValue(
+ CurrentUserOptionKey,
+ NW_SERVER_VALUENAME,
+ &PreferredServer
+ );
+
+ if (status != NO_ERROR) {
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWWORKSTATION: NwGetLogonCredential: Could not read preferred server from reg %lu\n", status));
+ }
+#endif
+
+ PreferredServer = NULL;
+ }
+
+ //
+ // Read the print option value.
+ //
+ status = NwReadRegValue(
+ CurrentUserOptionKey,
+ NW_PRINTOPTION_VALUENAME,
+ (LPWSTR *) &PrintOption
+ );
+ if (status != NO_ERROR) {
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWWORKSTATION: NwGetLogonCredential: Could not read print option from reg %lu\n", status));
+ }
+#endif
+ NwPrintOption = NW_PRINT_OPTION_DEFAULT;
+ } else {
+ NwPrintOption = *PrintOption;
+ }
+
+ }
+ else {
+ KdPrint(("NWWORKSTATION: NwGetLogonCredential: Open %ws key under Option failed %lu\n", CurrentUser, RegError));
+ goto CleanExit;
+ }
+
+ //
+ // Pass the interactive user credential to the redirector
+ //
+ (void) NwRdrLogonUser(
+ LogonId,
+ UserName,
+ wcslen(UserName) * sizeof(WCHAR),
+ Password,
+ wcslen(Password) * sizeof(WCHAR),
+ PreferredServer,
+ ((PreferredServer != NULL) ?
+ wcslen(PreferredServer) * sizeof(WCHAR) :
+ 0)
+ );
+
+ //
+ // NwGetLogonCredential is called after NwInitializeWkstaInfo.
+ // Hence, provider name and packet size is read already
+ //
+ (void) NwRdrSetInfo(
+ NwPrintOption,
+ NwPacketBurstSize,
+ PreferredServer,
+ ((PreferredServer != NULL) ?
+ wcslen(PreferredServer) * sizeof(WCHAR) : 0),
+ NwProviderName,
+ ((NwProviderName != NULL) ?
+ wcslen(NwProviderName) * sizeof(WCHAR) : 0 )
+ );
+
+CleanExit:
+ (void) LsaDeregisterLogonProcess(LsaHandle);
+
+ if (UserName != NULL) {
+ //
+ // Freeing the UserName pointer frees both the
+ // username and password buffers.
+ //
+ (void) LsaFreeReturnBuffer((PVOID) UserName);
+ }
+
+ if (LogonId != NULL) {
+ (void) LocalFree((HLOCAL) LogonId);
+ }
+
+ if (PrintOption != NULL) {
+ (void) LocalFree((HLOCAL) PrintOption);
+ }
+
+ if (PreferredServer != NULL) {
+ (void) LocalFree((HLOCAL) PreferredServer);
+ }
+
+ if (CurrentUser != NULL) {
+ (void) LocalFree((HLOCAL) CurrentUser);
+ }
+
+
+ if ( WkstaLogonKey ) {
+ (void) RegCloseKey(WkstaLogonKey);
+ }
+
+ if ( WkstaOptionKey ) {
+ (void) RegCloseKey(WkstaOptionKey);
+ }
+
+ if ( CurrentUserLogonKey ) {
+ (void) RegCloseKey(CurrentUserLogonKey);
+ }
+
+ if ( CurrentUserOptionKey ) {
+ (void) RegCloseKey(CurrentUserOptionKey);
+ }
+
+ (void) RegCloseKey(WkstaKey);
+
+ LeaveCriticalSection(&NwLoggedOnCritSec);
+}
+
+STATIC
+VOID
+NwpGetServiceCredentials(
+ IN HANDLE LsaHandle,
+ IN ULONG AuthPackageId
+ )
+/*++
+
+Routine Description:
+
+ This function reads the service logon IDs from the registry
+ so that it can get the service credentials from LSA. It then
+ notifies the redirector of the service logons.
+
+Arguments:
+
+ LsaHandle - Supplies the handle to LSA.
+
+ AuthPackageId - Supplies the NetWare authentication package ID.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ DWORD status;
+ LONG RegError;
+
+ LPWSTR UserName = NULL;
+ LPWSTR Password = NULL;
+
+ HKEY ServiceLogonKey;
+ DWORD Index = 0;
+ WCHAR LogonIdKey[NW_MAX_LOGON_ID_LEN];
+ LUID LogonId;
+
+
+ RegError = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_SERVICE_LOGON_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ,
+ &ServiceLogonKey
+ );
+
+ if (RegError == ERROR_SUCCESS) {
+
+ do {
+
+ RegError = RegEnumKeyW(
+ ServiceLogonKey,
+ Index,
+ LogonIdKey,
+ sizeof(LogonIdKey) / sizeof(WCHAR)
+ );
+
+ if (RegError == ERROR_SUCCESS) {
+
+ //
+ // Got a logon id key.
+ //
+
+ NwWStrToLuid(LogonIdKey, &LogonId);
+
+ status = NwpGetCredentialInLsa(
+ LsaHandle,
+ AuthPackageId,
+ &LogonId,
+ &UserName,
+ &Password
+ );
+
+ if (status == NO_ERROR) {
+
+ (void) NwRdrLogonUser(
+ &LogonId,
+ UserName,
+ wcslen(UserName) * sizeof(WCHAR),
+ Password,
+ wcslen(Password) * sizeof(WCHAR),
+ NULL,
+ 0
+ );
+
+ //
+ // Freeing the UserName pointer frees both the
+ // username and password buffers.
+ //
+ (void) LsaFreeReturnBuffer((PVOID) UserName);
+
+ }
+
+ }
+ else if (RegError != ERROR_NO_MORE_ITEMS) {
+ KdPrint(("NWWORKSTATION: NwpGetServiceCredentials failed to enum logon IDs RegError=%lu\n",
+ RegError));
+ }
+
+ Index++;
+
+ } while (RegError == ERROR_SUCCESS);
+ }
+
+ (void) RegCloseKey(ServiceLogonKey);
+}
+
+
+DWORD
+NwGatewayLogon(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function reads the gateway logon credential from the registry,
+ LSA secret, and does the gateway logon.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+{
+
+ DWORD status = NO_ERROR;
+ LONG RegError;
+ LUID LogonId = SYSTEM_LUID ;
+ DWORD GatewayEnabled, RegValueType, GatewayEnabledSize ;
+
+ HKEY WkstaKey = NULL;
+ LPWSTR GatewayAccount = NULL;
+
+ PUNICODE_STRING Password = NULL;
+ PUNICODE_STRING OldPassword = NULL;
+
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters
+ //
+ RegError = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_REGKEY,
+ REG_OPTION_NON_VOLATILE, // options
+ KEY_READ, // desired access
+ &WkstaKey
+ );
+
+ if (RegError != ERROR_SUCCESS) {
+ return RegError;
+ }
+
+ //
+ // Check to see if it is enabled
+ //
+ RegValueType = REG_DWORD ;
+ GatewayEnabled = 0 ;
+ GatewayEnabledSize = sizeof(GatewayEnabled) ;
+ RegError = RegQueryValueExW(
+ WkstaKey,
+ NW_GATEWAY_ENABLE,
+ NULL,
+ &RegValueType,
+ (LPBYTE)&GatewayEnabled,
+ &GatewayEnabledSize) ;
+
+ if (status != NO_ERROR || GatewayEnabled == 0) {
+ goto CleanExit;
+ }
+
+
+ //
+ // Read the gateway account from the registry.
+ //
+ status = NwReadRegValue(
+ WkstaKey,
+ NW_GATEWAYACCOUNT_VALUENAME,
+ &GatewayAccount
+ );
+
+ if (status != NO_ERROR) {
+ goto CleanExit;
+ }
+
+ //
+ // Read the password from its secret object in LSA.
+ //
+ status = NwGetPassword(
+ GATEWAY_USER,
+ &Password, // Must be freed with LsaFreeMemory
+ &OldPassword // Must be freed with LsaFreeMemory
+ );
+
+ if (status != NO_ERROR) {
+ goto CleanExit;
+ }
+
+ EnterCriticalSection(&NwLoggedOnCritSec);
+
+ status = NwRdrLogonUser(
+ &LogonId,
+ GatewayAccount,
+ ((GatewayAccount != NULL) ?
+ wcslen(GatewayAccount) * sizeof(WCHAR) :
+ 0),
+ Password->Buffer,
+ Password->Length,
+ NULL,
+ 0 );
+
+ if (status == NO_ERROR)
+ GatewayLoggedOn = TRUE ;
+
+ LeaveCriticalSection(&NwLoggedOnCritSec);
+
+ if (status != NO_ERROR)
+ {
+ //
+ // log the error in the event log
+ //
+
+ WCHAR Number[16] ;
+ LPWSTR InsertStrings[1] ;
+
+ wsprintfW(Number, L"%d", status) ;
+ InsertStrings[0] = Number ;
+
+ NwLogEvent(EVENT_NWWKSTA_GATEWAY_LOGON_FAILED,
+ 1,
+ InsertStrings,
+ 0) ;
+ }
+ else
+ {
+
+ //
+ // create the gateway redirections if any. not fatal if error.
+ // the function will log any errors to event log.
+ //
+ if (Password->Length)
+ {
+ LPWSTR Passwd = (LPWSTR) LocalAlloc(LPTR,
+ Password->Length + sizeof(WCHAR)) ;
+ if (Passwd)
+ {
+ wcsncpy(Passwd,
+ Password->Buffer,
+ Password->Length / sizeof(WCHAR)) ;
+ (void) NwCreateRedirections(GatewayAccount,
+ Passwd) ;
+ RtlZeroMemory((LPBYTE)Passwd,
+ Password->Length) ;
+ (void) LocalFree((HLOCAL)Passwd);
+ }
+ }
+ else
+ {
+ (void) NwCreateRedirections(GatewayAccount,
+ NULL) ;
+ }
+ }
+
+
+CleanExit:
+
+ if (Password != NULL) {
+ if (Password->Buffer)
+ RtlZeroMemory(Password->Buffer, Password->Length) ;
+ (void) LsaFreeMemory((PVOID) Password);
+ }
+
+ if (OldPassword != NULL) {
+ if (OldPassword->Buffer)
+ RtlZeroMemory(OldPassword->Buffer, OldPassword->Length) ;
+ (void) LsaFreeMemory((PVOID) OldPassword);
+ }
+
+ if (GatewayAccount != NULL) {
+ (void) LocalFree((HLOCAL) GatewayAccount);
+ }
+
+ (void) RegCloseKey(WkstaKey);
+ return status ;
+}
+
+DWORD
+NwGatewayLogoff(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function logs off the gateway account.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+{
+
+ DWORD status = NO_ERROR;
+ LUID LogonId = SYSTEM_LUID ;
+
+ EnterCriticalSection(&NwLoggedOnCritSec);
+
+ if (GatewayLoggedOn)
+ {
+ status = NwRdrLogoffUser(&LogonId);
+
+ if (status == NO_ERROR)
+ GatewayLoggedOn = FALSE ;
+ }
+
+ LeaveCriticalSection(&NwLoggedOnCritSec);
+
+ return status ;
+
+}
+
+STATIC
+DWORD
+NwpRegisterLogonProcess(
+ OUT PHANDLE LsaHandle,
+ OUT PULONG AuthPackageId
+ )
+/*++
+
+Routine Description:
+
+ This function registers the workstation service as a logon process
+ so that it can call LSA to retrieve user credentials.
+
+Arguments:
+
+ LsaHandle - Receives the handle to LSA.
+
+ AuthPackageId - Receives the NetWare authentication package ID.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+{
+ DWORD status = NO_ERROR;
+ NTSTATUS ntstatus;
+ STRING InputString;
+ LSA_OPERATIONAL_MODE SecurityMode = 0;
+
+ //
+ // Register this process as a logon process so that we can call
+ // NetWare authentication package.
+ //
+ RtlInitString(&InputString, "Client Service for NetWare");
+
+ ntstatus = LsaRegisterLogonProcess(
+ &InputString,
+ LsaHandle,
+ &SecurityMode
+ );
+
+ if (! NT_SUCCESS(ntstatus)) {
+ KdPrint(("NWPROVAU: NwInitializeLogon: LsaRegisterLogonProcess returns x%08lx\n",
+ ntstatus));
+ return RtlNtStatusToDosError(ntstatus);
+ }
+
+ //
+ // Look up the Netware authentication package
+ //
+ RtlInitString(&InputString, NW_AUTH_PACKAGE_NAME);
+
+ ntstatus = LsaLookupAuthenticationPackage(
+ *LsaHandle,
+ &InputString,
+ AuthPackageId
+ );
+
+ if (! NT_SUCCESS(ntstatus)) {
+ KdPrint(("NWPROVAU: NwpSetCredential: LsaLookupAuthenticationPackage returns x%08lx\n",
+ ntstatus));
+
+ (void) LsaDeregisterLogonProcess(*LsaHandle);
+ }
+
+ status = RtlNtStatusToDosError(ntstatus);
+
+ return status;
+}
+
+STATIC
+DWORD
+NwpGetCredentialInLsa(
+ IN HANDLE LsaHandle,
+ IN ULONG AuthPackageId,
+ IN PLUID LogonId,
+ OUT LPWSTR *UserName,
+ OUT LPWSTR *Password
+ )
+/*++
+
+Routine Description:
+
+ This function retrieves the username and password information
+ from LSA given the logon ID.
+
+Arguments:
+
+ LsaHandle - Supplies the handle to LSA.
+
+ AuthPackageId - Supplies the NetWare authentication package ID.
+
+ LogonId - Supplies the logon ID.
+
+ UserName - Receives a pointer to the username.
+
+ Password - Receives a pointer to the password.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+{
+ DWORD status;
+ NTSTATUS ntstatus;
+ NTSTATUS AuthPackageStatus;
+
+ NWAUTH_GET_CREDENTIAL_REQUEST GetCredRequest;
+ PNWAUTH_GET_CREDENTIAL_RESPONSE GetCredResponse;
+ ULONG ResponseLength;
+
+ UNICODE_STRING PasswordStr;
+
+ //
+ // Ask authentication package for credential.
+ //
+ GetCredRequest.MessageType = NwAuth_GetCredential;
+ RtlCopyLuid(&GetCredRequest.LogonId, LogonId);
+
+ ntstatus = LsaCallAuthenticationPackage(
+ LsaHandle,
+ AuthPackageId,
+ &GetCredRequest,
+ sizeof(GetCredRequest),
+ (PVOID *) &GetCredResponse,
+ &ResponseLength,
+ &AuthPackageStatus
+ );
+
+ if (NT_SUCCESS(ntstatus)) {
+ ntstatus = AuthPackageStatus;
+ }
+ if (! NT_SUCCESS(ntstatus)) {
+ KdPrint(("NWPROVAU: NwpGetCredentialInLsa: LsaCallAuthenticationPackage returns x%08lx\n",
+ ntstatus));
+ status = RtlNtStatusToDosError(ntstatus);
+ }
+ else {
+
+ *UserName = GetCredResponse->UserName;
+ *Password = GetCredResponse->Password;
+
+ //
+ // Decode the password.
+ //
+ RtlInitUnicodeString(&PasswordStr, GetCredResponse->Password);
+ RtlRunDecodeUnicodeString(NW_ENCODE_SEED, &PasswordStr);
+
+ status = NO_ERROR;
+ }
+
+ return status;
+}
+
+DWORD
+NwrChangePassword(
+ IN LPWSTR Reserved OPTIONAL,
+ IN DWORD UserLuid,
+ IN LPWSTR UserName,
+ IN LPWSTR OldPassword,
+ IN LPWSTR NewPassword,
+ IN LPWSTR TreeName
+ )
+/*++
+
+Routine Description:
+
+ This function changes the password for the specified user on
+ the list of servers. If we encounter a failure on changing
+ password for a particular server, we:
+
+ 1) Send the new password over to the server to verify if it is
+ already the current password.
+
+ 2) If not, return ERROR_INVALID_PASSWORD and the index into
+ the Servers array indicating the server which failed so that
+ we can prompt the user to enter an alternate old password.
+
+ When the password has been changed successfully on a server, we
+ notify the redirector so that the cached credential can be updated.
+
+ NOTE: All errors returned from this routine, except for the fatal
+ ERROR_NOT_ENOUGH_MEMORY error, indicates that the password
+ could not be changed on a particular server indexed by
+ LastProcessed. The client-side continues to call us with
+ the remaining list of servers.
+
+ If you add to this routine to return other fatal errors,
+ please make sure the client-side code aborts from calling
+ us with the rest of the servers on getting those errors.
+
+Arguments:
+
+ Reserved - Must be NULL.
+
+
+Return Value:
+
+ ERROR_BAD_NETPATH - Could not connect to the server indexed by
+ LastProcessed.
+
+ ERROR_BAD_USERNAME - The username could not be found on the server
+ indexed by LastProcessed.
+
+ ERROR_INVALID_PASSWORD - The change password operation failed on
+ the server indexed by LastProcessed.
+
+ ERROR_NOT_ENOUGH_MEMORY - Out of memory error. This fatal error
+ will terminate the client-side from trying to process password
+ change request on the remaining servers.
+
+--*/
+{
+ DWORD status;
+ NTSTATUS ntstatus;
+ HANDLE hNwRdr = NULL;
+ UNICODE_STRING UserNameStr;
+ UNICODE_STRING OldPasswordStr;
+ UNICODE_STRING NewPasswordStr;
+ UNICODE_STRING TreeNameStr;
+ BOOL fImpersonateClient = FALSE;
+
+ UNREFERENCED_PARAMETER( Reserved ) ;
+ UNREFERENCED_PARAMETER( UserLuid ) ;
+
+ RtlInitUnicodeString( &UserNameStr, UserName );
+
+ RtlInitUnicodeString( &OldPasswordStr, OldPassword );
+ RtlRunDecodeUnicodeString( NW_ENCODE_SEED2, &OldPasswordStr );
+
+ RtlInitUnicodeString( &NewPasswordStr, NewPassword );
+ RtlRunDecodeUnicodeString( NW_ENCODE_SEED2, &NewPasswordStr );
+
+ RtlInitUnicodeString( &TreeNameStr, TreeName );
+
+ //
+ // Impersonate the client
+ //
+ if ((status = NwImpersonateClient()) != NO_ERROR)
+ {
+ goto ErrorExit;
+ }
+
+ fImpersonateClient = TRUE;
+
+ //
+ // Open a NDS tree connection handle to \\treename
+ //
+ ntstatus = NwNdsOpenTreeHandle( &TreeNameStr, &hNwRdr );
+
+ if ( ntstatus != STATUS_SUCCESS )
+ {
+ status = RtlNtStatusToDosError(ntstatus);
+ goto ErrorExit;
+ }
+
+ (void) NwRevertToSelf() ;
+ fImpersonateClient = FALSE;
+
+ ntstatus = NwNdsChangePassword( hNwRdr,
+ &TreeNameStr,
+ &UserNameStr,
+ &OldPasswordStr,
+ &NewPasswordStr );
+
+ if ( ntstatus != NO_ERROR )
+ {
+ status = RtlNtStatusToDosError(ntstatus);
+ goto ErrorExit;
+ }
+
+ CloseHandle( hNwRdr );
+ hNwRdr = NULL;
+
+ return NO_ERROR ;
+
+ErrorExit:
+
+ if ( fImpersonateClient )
+ (void) NwRevertToSelf() ;
+
+ if ( hNwRdr )
+ CloseHandle( hNwRdr );
+
+ hNwRdr = NULL;
+
+ return status;
+}
+
diff --git a/private/nw/svcdlls/nwwks/server/device.c b/private/nw/svcdlls/nwwks/server/device.c
new file mode 100644
index 000000000..a280f3374
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/server/device.c
@@ -0,0 +1,2772 @@
+
+/*++
+
+Copyright (c) 1991-1993 Microsoft Corporation
+
+Module Name:
+
+ device.c
+
+Abstract:
+
+ This module contains the support routines for the APIs that call
+ into the NetWare redirector
+
+Author:
+
+ Rita Wong (ritaw) 20-Feb-1991
+ Colin Watson (colinw) 30-Dec-1992
+
+Revision History:
+
+--*/
+
+#include <nw.h>
+#include <nwcons.h>
+#include <nwxchg.h>
+#include <nwapi32.h>
+#include <nwstatus.h>
+#include <nwmisc.h>
+#include <nwcons.h>
+#include <nds.h>
+#include <svcguid.h>
+#include <tdi.h>
+
+#define NW_LINKAGE_REGISTRY_PATH L"NWCWorkstation\\Linkage"
+#define NW_BIND_VALUENAME L"Bind"
+
+#define TWO_KB 2048
+#define EIGHT_KB 8192
+#define EXTRA_BYTES 256
+
+#define TREECHAR L'*'
+#define BUFFSIZE 1024
+
+//-------------------------------------------------------------------//
+// //
+// Local Function Prototypes //
+// //
+//-------------------------------------------------------------------//
+
+
+STATIC
+NTSTATUS
+BindToEachTransport(
+ IN PWSTR ValueName,
+ IN ULONG ValueType,
+ IN PVOID ValueData,
+ IN ULONG ValueLength,
+ IN PVOID Context,
+ IN PVOID EntryContext
+ );
+
+DWORD
+NwBindTransport(
+ IN LPWSTR TransportName,
+ IN DWORD QualityOfService
+ );
+
+DWORD
+GetConnectedBinderyServers(
+ OUT LPNW_ENUM_CONTEXT ContextHandle
+ );
+
+DWORD
+GetTreeEntriesFromBindery(
+ OUT LPNW_ENUM_CONTEXT ContextHandle
+ );
+
+DWORD
+NwGetConnectionStatus(
+ IN LPWSTR pszServerName,
+ IN OUT PDWORD ResumeKey,
+ OUT LPBYTE *Buffer,
+ OUT PDWORD EntriesRead
+ );
+
+VOID
+GetNearestDirServer(
+ IN LPWSTR TreeName,
+ OUT LPDWORD lpdwReplicaAddressSize,
+ OUT LPBYTE lpReplicaAddress
+ );
+
+BOOL
+NwpCompareTreeNames(
+ LPWSTR lpServiceInstanceName,
+ LPWSTR lpTreeName
+ );
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+//
+// Handle to the Redirector FSD
+//
+STATIC HANDLE RedirDeviceHandle = NULL;
+
+//
+// Redirector name in NT string format
+//
+STATIC UNICODE_STRING RedirDeviceName;
+
+
+DWORD
+NwInitializeRedirector(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine initializes the NetWare redirector FSD.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+{
+ DWORD error;
+ NWR_REQUEST_PACKET Rrp;
+
+
+ //
+ // Initialize global handles
+ //
+ RedirDeviceHandle = NULL;
+
+ //
+ // Initialize the global NT-style redirector device name string.
+ //
+ RtlInitUnicodeString(&RedirDeviceName, DD_NWFS_DEVICE_NAME_U);
+
+ //
+ // Load driver
+ //
+ error = NwLoadOrUnloadDriver(TRUE);
+
+ if (error != NO_ERROR && error != ERROR_SERVICE_ALREADY_RUNNING) {
+ return error;
+ }
+
+ if ((error = NwOpenRedirector()) != NO_ERROR) {
+
+ //
+ // Unload the redirector driver
+ //
+ (void) NwLoadOrUnloadDriver(FALSE);
+ return error;
+ }
+
+ //
+ // Send the start FSCTL to the redirector
+ //
+ Rrp.Version = REQUEST_PACKET_VERSION;
+
+ return NwRedirFsControl(
+ RedirDeviceHandle,
+ FSCTL_NWR_START,
+ &Rrp,
+ sizeof(NWR_REQUEST_PACKET),
+ NULL,
+ 0,
+ NULL
+ );
+}
+
+
+
+DWORD
+NwOpenRedirector(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine opens the NT NetWare redirector FSD.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+{
+ return RtlNtStatusToDosError(
+ NwOpenHandle(&RedirDeviceName, FALSE, &RedirDeviceHandle)
+ );
+}
+
+
+
+DWORD
+NwShutdownRedirector(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine stops the NetWare Redirector FSD and unloads it if
+ possible.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NO_ERROR or ERROR_REDIRECTOR_HAS_OPEN_HANDLES
+
+--*/
+{
+ NWR_REQUEST_PACKET Rrp;
+ DWORD error;
+
+
+ Rrp.Version = REQUEST_PACKET_VERSION;
+
+ error = NwRedirFsControl(
+ RedirDeviceHandle,
+ FSCTL_NWR_STOP,
+ &Rrp,
+ sizeof(NWR_REQUEST_PACKET),
+ NULL,
+ 0,
+ NULL
+ );
+
+ (void) NtClose(RedirDeviceHandle);
+
+ RedirDeviceHandle = NULL;
+
+ if (error != ERROR_REDIRECTOR_HAS_OPEN_HANDLES) {
+
+ //
+ // Unload the redirector only if all its open handles are closed.
+ //
+ (void) NwLoadOrUnloadDriver(FALSE);
+ }
+
+ return error;
+}
+
+
+DWORD
+NwLoadOrUnloadDriver(
+ BOOL Load
+ )
+/*++
+
+Routine Description:
+
+ This routine loads or unloads the NetWare redirector driver.
+
+Arguments:
+
+ Load - Supplies the flag which if TRUE load the driver; otherwise
+ unloads the driver.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+{
+
+ LPWSTR DriverRegistryName;
+ UNICODE_STRING DriverRegistryString;
+ NTSTATUS ntstatus;
+ BOOLEAN WasEnabled;
+
+
+ DriverRegistryName = (LPWSTR) LocalAlloc(
+ LMEM_FIXED,
+ (UINT) (sizeof(SERVICE_REGISTRY_KEY) +
+ (wcslen(NW_DRIVER_NAME) *
+ sizeof(WCHAR)))
+ );
+
+ if (DriverRegistryName == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ ntstatus = RtlAdjustPrivilege(
+ SE_LOAD_DRIVER_PRIVILEGE,
+ TRUE,
+ FALSE,
+ &WasEnabled
+ );
+
+ if (! NT_SUCCESS(ntstatus)) {
+ (void) LocalFree(DriverRegistryName);
+ return RtlNtStatusToDosError(ntstatus);
+ }
+
+ wcscpy(DriverRegistryName, SERVICE_REGISTRY_KEY);
+ wcscat(DriverRegistryName, NW_DRIVER_NAME);
+
+ RtlInitUnicodeString(&DriverRegistryString, DriverRegistryName);
+
+ if (Load) {
+ ntstatus = NtLoadDriver(&DriverRegistryString);
+ }
+ else {
+ ntstatus = NtUnloadDriver(&DriverRegistryString);
+ }
+
+ (void) RtlAdjustPrivilege(
+ SE_LOAD_DRIVER_PRIVILEGE,
+ WasEnabled,
+ FALSE,
+ &WasEnabled
+ );
+
+ (void) LocalFree(DriverRegistryName);
+
+ if (Load) {
+ if (ntstatus != STATUS_SUCCESS && ntstatus != STATUS_IMAGE_ALREADY_LOADED) {
+ LPWSTR SubString[1];
+
+ KdPrint(("NWWORKSTATION: NtLoadDriver returned %08lx\n", ntstatus));
+
+ SubString[0] = NW_DRIVER_NAME;
+
+ NwLogEvent(
+ EVENT_NWWKSTA_CANT_CREATE_REDIRECTOR,
+ 1,
+ SubString,
+ ntstatus
+ );
+ }
+ }
+
+ if (ntstatus == STATUS_OBJECT_NAME_NOT_FOUND) {
+ return ERROR_FILE_NOT_FOUND;
+ }
+
+ return NwMapStatus(ntstatus);
+}
+
+
+DWORD
+NwRedirFsControl(
+ IN HANDLE FileHandle,
+ IN ULONG RedirControlCode,
+ IN PNWR_REQUEST_PACKET Rrp,
+ IN ULONG RrpLength,
+ IN PVOID SecondBuffer OPTIONAL,
+ IN ULONG SecondBufferLength,
+ OUT PULONG Information OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+Arguments:
+
+ FileHandle - Supplies a handle to the file or device on which the service
+ is being performed.
+
+ RedirControlCode - Supplies the NtFsControlFile function code given to
+ the redirector.
+
+ Rrp - Supplies the redirector request packet.
+
+ RrpLength - Supplies the length of the redirector request packet.
+
+ SecondBuffer - Supplies the second buffer in call to NtFsControlFile.
+
+ SecondBufferLength - Supplies the length of the second buffer.
+
+ Information - Returns the information field of the I/O status block.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+
+{
+ NTSTATUS ntstatus;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+
+ //
+ // Send the request to the Redirector FSD.
+ //
+ ntstatus = NtFsControlFile(
+ FileHandle,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ RedirControlCode,
+ (PVOID) Rrp,
+ RrpLength,
+ SecondBuffer,
+ SecondBufferLength
+ );
+
+ if (ntstatus == STATUS_SUCCESS) {
+ ntstatus = IoStatusBlock.Status;
+ }
+
+ if (ARGUMENT_PRESENT(Information)) {
+ *Information = IoStatusBlock.Information;
+ }
+
+#if DBG
+ if (ntstatus != STATUS_SUCCESS) {
+ IF_DEBUG(DEVICE) {
+ KdPrint(("NWWORKSTATION: fsctl to redir returns %08lx\n", ntstatus));
+ }
+ }
+#endif
+
+ return NwMapStatus(ntstatus);
+}
+
+
+DWORD
+NwBindToTransports(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This routine binds to every transport specified under the linkage
+ key of the NetWare Workstation service.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NET_API_STATUS - success/failure of the operation.
+
+--*/
+
+{
+ NTSTATUS ntstatus;
+ PRTL_QUERY_REGISTRY_TABLE QueryTable;
+ ULONG NumberOfBindings = 0;
+
+
+ //
+ // Ask the RTL to call us back for each subvalue in the MULTI_SZ
+ // value \NWCWorkstation\Linkage\Bind.
+ //
+
+ if ((QueryTable = (PVOID) LocalAlloc(
+ LMEM_ZEROINIT,
+ sizeof(RTL_QUERY_REGISTRY_TABLE) * 2
+ )) == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ QueryTable[0].QueryRoutine = (PRTL_QUERY_REGISTRY_ROUTINE) BindToEachTransport;
+ QueryTable[0].Flags = 0;
+ QueryTable[0].Name = NW_BIND_VALUENAME;
+ QueryTable[0].EntryContext = NULL;
+ QueryTable[0].DefaultType = REG_NONE;
+ QueryTable[0].DefaultData = NULL;
+ QueryTable[0].DefaultLength = 0;
+
+ QueryTable[1].QueryRoutine = NULL;
+ QueryTable[1].Flags = 0;
+ QueryTable[1].Name = NULL;
+
+ ntstatus = RtlQueryRegistryValues(
+ RTL_REGISTRY_SERVICES,
+ NW_LINKAGE_REGISTRY_PATH,
+ QueryTable,
+ &NumberOfBindings,
+ NULL
+ );
+
+ (void) LocalFree((HLOCAL) QueryTable);
+
+ //
+ // If failed to bind to any transports, the workstation will
+ // not start.
+ //
+
+ if (! NT_SUCCESS(ntstatus)) {
+#if DBG
+ IF_DEBUG(INIT) {
+ KdPrint(("NwBindToTransports: RtlQueryRegistryValues failed: "
+ "%lx\n", ntstatus));
+ }
+#endif
+ return RtlNtStatusToDosError(ntstatus);
+ }
+
+ if (NumberOfBindings == 0) {
+
+ NwLogEvent(
+ EVENT_NWWKSTA_NO_TRANSPORTS,
+ 0,
+ NULL,
+ NO_ERROR
+ );
+
+ KdPrint(("NWWORKSTATION: NwBindToTransports: could not bind "
+ "to any transport\n"));
+
+ return ERROR_INVALID_PARAMETER; // BUGBUG: Bad return code.
+ // Create a NetWare specific set?
+ }
+
+ return NO_ERROR;
+}
+
+
+STATIC
+NTSTATUS
+BindToEachTransport(
+ IN PWSTR ValueName,
+ IN ULONG ValueType,
+ IN PVOID ValueData,
+ IN ULONG ValueLength,
+ IN PVOID Context,
+ IN PVOID EntryContext
+ )
+{
+ DWORD error;
+ LPDWORD NumberOfBindings = Context;
+ LPWSTR SubStrings[2];
+ static DWORD QualityOfService = 65536;
+
+
+ UNREFERENCED_PARAMETER(ValueName);
+ UNREFERENCED_PARAMETER(ValueLength);
+ UNREFERENCED_PARAMETER(EntryContext);
+
+ //
+ // The value type must be REG_SZ (translated from REG_MULTI_SZ by
+ // the RTL).
+ //
+ if (ValueType != REG_SZ) {
+
+ SubStrings[0] = ValueName;
+ SubStrings[1] = NW_LINKAGE_REGISTRY_PATH;
+
+ NwLogEvent(
+ EVENT_NWWKSTA_INVALID_REGISTRY_VALUE,
+ 2,
+ SubStrings,
+ NO_ERROR
+ );
+
+ KdPrint(("NWWORKSTATION: Skipping invalid value %ws\n", ValueName));
+
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // The value data is the name of the transport device object.
+ //
+
+ //
+ // Bind to the transport.
+ //
+
+#if DBG
+ IF_DEBUG(INIT) {
+ KdPrint(("NWWORKSTATION: Binding to transport %ws with QOS %lu\n",
+ ValueData, QualityOfService));
+ }
+#endif
+
+ error = NwBindTransport(ValueData, QualityOfService--);
+
+ if (error != NO_ERROR) {
+
+ //
+ // If failed to bind to one transport, don't fail starting yet.
+ // Try other transports.
+ //
+ SubStrings[0] = ValueData;
+
+ NwLogEvent(
+ EVENT_NWWKSTA_CANT_BIND_TO_TRANSPORT,
+ 1,
+ SubStrings,
+ error
+ );
+ }
+ else {
+ (*NumberOfBindings)++;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+DWORD
+NwBindTransport(
+ IN LPWSTR TransportName,
+ IN DWORD QualityOfService
+ )
+/*++
+
+Routine Description:
+
+ This function binds the specified transport to the redirector
+ and the datagram receiver.
+
+ NOTE: The transport name length pass to the redirector and
+ datagram receiver is the number of bytes.
+
+Arguments:
+
+ TransportName - Supplies the name of the transport to bind to.
+
+ QualityOfService - Supplies a value which specifies the search
+ order of the transport with respect to other transports. The
+ highest value is searched first.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+{
+ DWORD status;
+ DWORD RequestPacketSize;
+ DWORD TransportNameSize = wcslen(TransportName) * sizeof(WCHAR);
+
+ PNWR_REQUEST_PACKET Rrp;
+
+
+ //
+ // Size of request packet buffer
+ //
+ RequestPacketSize = TransportNameSize + sizeof(NWR_REQUEST_PACKET);
+
+ //
+ // Allocate memory for redirector/datagram receiver request packet
+ //
+ if ((Rrp = (PVOID) LocalAlloc(LMEM_ZEROINIT, (UINT) RequestPacketSize)) == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ //
+ // Get redirector to bind to transport
+ //
+ Rrp->Version = REQUEST_PACKET_VERSION;
+ Rrp->Parameters.Bind.QualityOfService = QualityOfService;
+
+ Rrp->Parameters.Bind.TransportNameLength = TransportNameSize;
+ wcscpy((LPWSTR) Rrp->Parameters.Bind.TransportName, TransportName);
+
+ if ((status = NwRedirFsControl(
+ RedirDeviceHandle,
+ FSCTL_NWR_BIND_TO_TRANSPORT,
+ Rrp,
+ RequestPacketSize,
+ NULL,
+ 0,
+ NULL
+ )) != NO_ERROR) {
+
+ KdPrint(("NWWORKSTATION: NwBindTransport fsctl to bind to transport %ws failed\n",
+ TransportName));
+ }
+
+ (void) LocalFree((HLOCAL) Rrp);
+ return status;
+}
+
+
+DWORD
+NwCreateTreeConnectName(
+ IN LPWSTR UncName,
+ IN LPWSTR LocalName OPTIONAL,
+ OUT PUNICODE_STRING TreeConnectStr
+ )
+/*++
+
+Routine Description:
+
+ This function replaces \\ with \Device\NwRdr\LocalName:\ in the
+ UncName to form the NT-style tree connection name. LocalName:\ is part
+ of the tree connection name only if LocalName is specified. A buffer
+ is allocated by this function and returned as the output string.
+
+Arguments:
+
+ UncName - Supplies the UNC name of the shared resource.
+
+ LocalName - Supplies the local device name for the redirection.
+
+ TreeConnectStr - Returns a string with a newly allocated buffer that
+ contains the NT-style tree connection name.
+
+Return Value:
+
+ NO_ERROR - the operation was successful.
+
+ ERROR_NOT_ENOUGH_MEMORY - Could not allocate output buffer.
+
+--*/
+{
+ DWORD UncNameLength = wcslen(UncName);
+
+
+ //
+ // Initialize tree connect string maximum length to hold
+ // \Device\NwRdr\LocalName:\Server\Volume\Path
+ //
+ TreeConnectStr->MaximumLength = RedirDeviceName.Length +
+ sizeof(WCHAR) + // For '\'
+ (ARGUMENT_PRESENT(LocalName) ? (wcslen(LocalName) * sizeof(WCHAR)) : 0) +
+ (USHORT) (UncNameLength * sizeof(WCHAR)); // Includes '\' and
+ // term char
+
+
+ if ((TreeConnectStr->Buffer = (PWSTR) LocalAlloc(
+ LMEM_ZEROINIT,
+ (UINT) TreeConnectStr->MaximumLength
+ )) == NULL) {
+ KdPrint(("NWWORKSTATION: NwCreateTreeConnectName LocalAlloc failed %lu\n",
+ GetLastError()));
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ //
+ // Copy \Device\NwRdr
+ //
+ RtlCopyUnicodeString(TreeConnectStr, &RedirDeviceName);
+
+ //
+ // Concatenate \LocalName:
+ //
+ if (ARGUMENT_PRESENT(LocalName)) {
+
+ wcscat(TreeConnectStr->Buffer, L"\\");
+ TreeConnectStr->Length += sizeof(WCHAR);
+
+ wcscat(TreeConnectStr->Buffer, LocalName);
+
+ TreeConnectStr->Length += (USHORT) (wcslen(LocalName) * sizeof(WCHAR));
+ }
+
+ //
+ // Concatenate \Server\Volume\Path
+ //
+ wcscat(TreeConnectStr->Buffer, &UncName[1]);
+ TreeConnectStr->Length += (USHORT) ((UncNameLength - 1) * sizeof(WCHAR));
+
+#if DBG
+ IF_DEBUG(CONNECT) {
+ KdPrint(("NWWORKSTATION: NwCreateTreeConnectName %ws, maxlength %u, length %u\n",
+ TreeConnectStr->Buffer, TreeConnectStr->MaximumLength,
+ TreeConnectStr->Length));
+ }
+#endif
+
+ return NO_ERROR;
+}
+
+
+
+DWORD
+NwOpenCreateConnection(
+ IN PUNICODE_STRING TreeConnectionName,
+ IN LPWSTR UserName OPTIONAL,
+ IN LPWSTR Password OPTIONAL,
+ IN LPWSTR UncName,
+ IN ACCESS_MASK DesiredAccess,
+ IN ULONG CreateDisposition,
+ IN ULONG CreateOptions,
+ IN ULONG ConnectionType,
+ OUT PHANDLE TreeConnectionHandle,
+ OUT PULONG Information OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This function asks the redirector to either open an existing tree
+ connection (CreateDisposition == FILE_OPEN), or create a new tree
+ connection if one does not exist (CreateDisposition == FILE_CREATE).
+
+ The password and user name passed to the redirector via the EA buffer
+ in the NtCreateFile call. The EA buffer is NULL if neither password
+ or user name is specified.
+
+ The redirector expects the EA descriptor strings to be in ANSI
+ but the password and username themselves are in Unicode.
+
+Arguments:
+
+ TreeConnectionName - Supplies the name of the tree connection in NT-style
+ file name format: \Device\NwRdr\Server\Volume\Directory
+
+ UserName - Supplies the user name to create the tree connection with.
+
+ Password - Supplies the password to create the tree connection with.
+
+ DesiredAccess - Supplies the access need on the connection handle.
+
+ CreateDisposition - Supplies the create disposition value to either
+ open or create the tree connection.
+
+ CreateOptions - Supplies the options used when creating or opening
+ the tree connection.
+
+ ConnectionType - Supplies the type of the connection (DISK, PRINT,
+ or ANY).
+
+ TreeConnectionHandle - Returns the handle to the tree connection
+ created/opened by the redirector.
+
+ Information - Returns the information field of the I/O status block.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+{
+ DWORD status;
+ NTSTATUS ntstatus;
+
+ OBJECT_ATTRIBUTES UncNameAttributes;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ PFILE_FULL_EA_INFORMATION EaBuffer = NULL;
+ PFILE_FULL_EA_INFORMATION Ea;
+ ULONG EaBufferSize = 0;
+
+ UCHAR EaNamePasswordSize = (UCHAR) (ROUND_UP_COUNT(
+ strlen(EA_NAME_PASSWORD) + sizeof(CHAR),
+ ALIGN_WCHAR
+ ) - sizeof(CHAR));
+ UCHAR EaNameUserNameSize = (UCHAR) (ROUND_UP_COUNT(
+ strlen(EA_NAME_USERNAME) + sizeof(CHAR),
+ ALIGN_WCHAR
+ ) - sizeof(CHAR));
+
+ UCHAR EaNameTypeSize = (UCHAR) (ROUND_UP_COUNT(
+ strlen(EA_NAME_TYPE) + sizeof(CHAR),
+ ALIGN_DWORD
+ ) - sizeof(CHAR));
+
+ USHORT PasswordSize = 0;
+ USHORT UserNameSize = 0;
+ USHORT TypeSize = sizeof(ULONG);
+
+
+
+ InitializeObjectAttributes(
+ &UncNameAttributes,
+ TreeConnectionName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ //
+ // Calculate the number of bytes needed for the EA buffer to put the
+ // password or user name.
+ //
+ if (ARGUMENT_PRESENT(Password)) {
+
+#if DBG
+ IF_DEBUG(CONNECT) {
+ KdPrint(("NWWORKSTATION: NwOpenCreateConnection password is %ws\n",
+ Password));
+ }
+#endif
+
+ PasswordSize = (USHORT) (wcslen(Password) * sizeof(WCHAR));
+
+ EaBufferSize = ROUND_UP_COUNT(
+ FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
+ EaNamePasswordSize + sizeof(CHAR) +
+ PasswordSize,
+ ALIGN_DWORD
+ );
+ }
+
+ if (ARGUMENT_PRESENT(UserName)) {
+
+#if DBG
+ IF_DEBUG(CONNECT) {
+ KdPrint(("NWWORKSTATION: NwOpenCreateConnection username is %ws\n",
+ UserName));
+ }
+#endif
+
+ UserNameSize = (USHORT) (wcslen(UserName) * sizeof(WCHAR));
+
+ EaBufferSize += ROUND_UP_COUNT(
+ FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
+ EaNameUserNameSize + sizeof(CHAR) +
+ UserNameSize,
+ ALIGN_DWORD
+ );
+ }
+
+ EaBufferSize += FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
+ EaNameTypeSize + sizeof(CHAR) +
+ TypeSize;
+
+ //
+ // Allocate the EA buffer
+ //
+ if ((EaBuffer = (PFILE_FULL_EA_INFORMATION) LocalAlloc(
+ LMEM_ZEROINIT,
+ (UINT) EaBufferSize
+ )) == NULL) {
+ status = GetLastError();
+ goto FreeMemory;
+ }
+
+ Ea = EaBuffer;
+
+ if (ARGUMENT_PRESENT(Password)) {
+
+ //
+ // Copy the EA name into EA buffer. EA name length does not
+ // include the zero terminator.
+ //
+ strcpy((LPSTR) Ea->EaName, EA_NAME_PASSWORD);
+ Ea->EaNameLength = EaNamePasswordSize;
+
+ //
+ // Copy the EA value into EA buffer. EA value length does not
+ // include the zero terminator.
+ //
+ wcscpy(
+ (LPWSTR) &(Ea->EaName[EaNamePasswordSize + sizeof(CHAR)]),
+ Password
+ );
+
+ Ea->EaValueLength = PasswordSize;
+
+ Ea->NextEntryOffset = ROUND_UP_COUNT(
+ FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
+ EaNamePasswordSize + sizeof(CHAR) +
+ PasswordSize,
+ ALIGN_DWORD
+ );
+
+ Ea->Flags = 0;
+
+ (ULONG) Ea += Ea->NextEntryOffset;
+ }
+
+ if (ARGUMENT_PRESENT(UserName)) {
+
+ //
+ // Copy the EA name into EA buffer. EA name length does not
+ // include the zero terminator.
+ //
+ strcpy((LPSTR) Ea->EaName, EA_NAME_USERNAME);
+ Ea->EaNameLength = EaNameUserNameSize;
+
+ //
+ // Copy the EA value into EA buffer. EA value length does not
+ // include the zero terminator.
+ //
+ wcscpy(
+ (LPWSTR) &(Ea->EaName[EaNameUserNameSize + sizeof(CHAR)]),
+ UserName
+ );
+
+ Ea->EaValueLength = UserNameSize;
+
+ Ea->NextEntryOffset = ROUND_UP_COUNT(
+ FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName[0]) +
+ EaNameUserNameSize + sizeof(CHAR) +
+ UserNameSize,
+ ALIGN_DWORD
+ );
+ Ea->Flags = 0;
+
+ (ULONG) Ea += Ea->NextEntryOffset;
+
+ }
+
+ //
+ // Copy the connection type name into EA buffer. EA name length
+ // does not include the zero terminator.
+ //
+ strcpy((LPSTR) Ea->EaName, EA_NAME_TYPE);
+ Ea->EaNameLength = EaNameTypeSize;
+
+ *((PULONG) &(Ea->EaName[EaNameTypeSize + sizeof(CHAR)])) = ConnectionType;
+
+ Ea->EaValueLength = TypeSize;
+
+ //
+ // Terminate the EA.
+ //
+ Ea->NextEntryOffset = 0;
+ Ea->Flags = 0;
+
+ //
+ // Create or open a tree connection
+ //
+ ntstatus = NtCreateFile(
+ TreeConnectionHandle,
+ DesiredAccess,
+ &UncNameAttributes,
+ &IoStatusBlock,
+ NULL,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_VALID_FLAGS,
+ CreateDisposition,
+ CreateOptions,
+ (PVOID) EaBuffer,
+ EaBufferSize
+ );
+
+ if (ntstatus == NWRDR_PASSWORD_HAS_EXPIRED) {
+ //
+ // wait till other thread is not using the popup data struct.
+ // if we timeout, then we just lose the popup.
+ //
+ switch (WaitForSingleObject(NwPopupDoneEvent, 3000))
+ {
+ case WAIT_OBJECT_0:
+ {
+ LPWSTR lpServerStart, lpServerEnd ;
+ WCHAR UserNameW[NW_MAX_USERNAME_LEN+1] ;
+ DWORD dwUserNameWSize = sizeof(UserNameW)/sizeof(UserNameW[0]) ;
+ DWORD dwServerLength, dwGraceLogins ;
+ DWORD dwMessageId = NW_PASSWORD_HAS_EXPIRED ;
+
+ //
+ // get the current username
+ //
+ if (UserName)
+ {
+ wcscpy(UserNameW, UserName) ;
+ }
+ else
+ {
+ if (!GetUserNameW(UserNameW, &dwUserNameWSize))
+ {
+ SetEvent(NwPopupDoneEvent) ;
+ break ;
+ }
+ }
+
+ //
+ // allocate string and fill in the username
+ //
+ if (!(PopupData.InsertStrings[0] =
+ (LPWSTR)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,
+ sizeof(WCHAR) * (wcslen(UserNameW)+1))))
+ {
+ SetEvent(NwPopupDoneEvent) ;
+ break ;
+ }
+ wcscpy(PopupData.InsertStrings[0], UserNameW) ;
+
+ //
+ // find the server name from unc name
+ //
+ lpServerStart = (*UncName == L'\\') ? UncName+2 : UncName ;
+ lpServerEnd = wcschr(lpServerStart,L'\\') ;
+ dwServerLength = lpServerEnd ? (lpServerEnd-lpServerStart) :
+ wcslen(lpServerStart) ;
+
+ //
+ // allocate string and fill in the server insert string
+ //
+ if (!(PopupData.InsertStrings[1] =
+ (LPWSTR)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,
+ sizeof(WCHAR) * (dwServerLength+1))))
+ {
+ (void) LocalFree((HLOCAL) PopupData.InsertStrings[0]);
+ SetEvent(NwPopupDoneEvent) ;
+ break ;
+ }
+ wcsncpy(PopupData.InsertStrings[1],
+ lpServerStart,
+ dwServerLength) ;
+
+ //
+ // now call the NCP. if an error occurs while getting
+ // the grace login count, dont use it.
+ //
+ if (NwGetGraceLoginCount(
+ PopupData.InsertStrings[1],
+ UserNameW,
+ &dwGraceLogins) != NO_ERROR)
+ {
+ dwMessageId = NW_PASSWORD_HAS_EXPIRED1 ;
+ dwGraceLogins = 0 ;
+ }
+
+ //
+ // stick the number of grace logins in second insert string.
+ //
+ if (!(PopupData.InsertStrings[2] =
+ (LPWSTR)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,
+ sizeof(WCHAR) * 16)))
+ {
+ (void) LocalFree((HLOCAL) PopupData.InsertStrings[0]);
+ (void) LocalFree((HLOCAL) PopupData.InsertStrings[1]);
+ SetEvent(NwPopupDoneEvent) ;
+ break ;
+ }
+
+ wsprintfW(PopupData.InsertStrings[2], L"%d", dwGraceLogins);
+ PopupData.InsertCount = 3 ;
+ PopupData.MessageId = dwMessageId ;
+
+ //
+ // all done at last, trigger the other thread do the popup
+ //
+ SetEvent(NwPopupEvent) ;
+ break ;
+
+ }
+
+ default:
+ break ; // dont bother if we cannot
+ }
+ }
+
+ if (NT_SUCCESS(ntstatus)) {
+ ntstatus = IoStatusBlock.Status;
+ }
+
+ if (ntstatus == NWRDR_PASSWORD_HAS_EXPIRED) {
+ ntstatus = STATUS_SUCCESS ;
+ }
+
+ if (ARGUMENT_PRESENT(Information)) {
+ *Information = IoStatusBlock.Information;
+ }
+
+#if DBG
+ IF_DEBUG(CONNECT) {
+ KdPrint(("NWWORKSTATION: NtCreateFile returns %lx\n", ntstatus));
+ }
+#endif
+
+ status = NwMapStatus(ntstatus);
+
+FreeMemory:
+ if (EaBuffer != NULL) {
+ RtlZeroMemory( EaBuffer, EaBufferSize ); // Clear the password
+ (void) LocalFree((HLOCAL) EaBuffer);
+ }
+
+ return status;
+}
+
+
+DWORD
+NwNukeConnection(
+ IN HANDLE TreeConnection,
+ IN DWORD UseForce
+ )
+/*++
+
+Routine Description:
+
+ This function asks the redirector to delete an existing tree
+ connection.
+
+Arguments:
+
+ TreeConnection - Supplies the handle to an existing tree connection.
+
+ UseForce - Supplies the force flag to delete the tree connection.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+{
+ DWORD status;
+ NWR_REQUEST_PACKET Rrp; // Redirector request packet
+
+
+ //
+ // Tell the redirector to delete the tree connection
+ //
+ Rrp.Version = REQUEST_PACKET_VERSION;
+ Rrp.Parameters.DeleteConn.UseForce = (BOOLEAN) UseForce;
+
+ status = NwRedirFsControl(
+ TreeConnection,
+ FSCTL_NWR_DELETE_CONNECTION,
+ &Rrp,
+ sizeof(NWR_REQUEST_PACKET),
+ NULL,
+ 0,
+ NULL
+ );
+
+ return status;
+}
+
+
+DWORD
+NwGetServerResource(
+ IN LPWSTR LocalName,
+ IN DWORD LocalNameLength,
+ OUT LPWSTR RemoteName,
+ IN DWORD RemoteNameLen,
+ OUT LPDWORD CharsRequired
+ )
+/*++
+
+Routine Description:
+
+ This function
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+{
+ DWORD status = NO_ERROR;
+
+ BYTE Buffer[sizeof(NWR_REQUEST_PACKET) + 2 * sizeof(WCHAR)];
+ PNWR_REQUEST_PACKET Rrp = (PNWR_REQUEST_PACKET) Buffer;
+
+
+ //
+ // local device name should not be longer than 4 characters e.g. LPTx, X:
+ //
+ if ( LocalNameLength > 4 )
+ return ERROR_INVALID_PARAMETER;
+
+ Rrp->Version = REQUEST_PACKET_VERSION;
+
+ wcsncpy(Rrp->Parameters.GetConn.DeviceName, LocalName, LocalNameLength);
+ Rrp->Parameters.GetConn.DeviceNameLength = LocalNameLength * sizeof(WCHAR);
+
+ status = NwRedirFsControl(
+ RedirDeviceHandle,
+ FSCTL_NWR_GET_CONNECTION,
+ Rrp,
+ sizeof(NWR_REQUEST_PACKET) +
+ Rrp->Parameters.GetConn.DeviceNameLength,
+ RemoteName,
+ RemoteNameLen * sizeof(WCHAR),
+ NULL
+ );
+
+ if (status == ERROR_INSUFFICIENT_BUFFER) {
+ *CharsRequired = Rrp->Parameters.GetConn.BytesNeeded / sizeof(WCHAR);
+ }
+ else if (status == ERROR_FILE_NOT_FOUND) {
+
+ //
+ // Redirector could not find the specified LocalName
+ //
+ status = WN_NOT_CONNECTED;
+ }
+
+ return status;
+
+}
+
+
+DWORD
+NwEnumerateConnections(
+ IN OUT LPDWORD ResumeId,
+ IN DWORD EntriesRequested,
+ IN LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT LPDWORD BytesNeeded,
+ OUT LPDWORD EntriesRead,
+ IN DWORD ConnectionType
+ )
+/*++
+
+Routine Description:
+
+ This function asks the redirector to enumerate all existing
+ connections.
+
+Arguments:
+
+ ResumeId - On input, supplies the resume ID of the next entry
+ to begin the enumeration. This ID is an integer value that
+ is either the smaller or the same value as the ID of the
+ next entry to return. On output, this ID indicates the next
+ entry to start resuming from for the subsequent call.
+
+ EntriesRequested - Supplies the number of entries to return. If
+ this value is 0xffffffff, return all available entries.
+
+ Buffer - Receives the entries we are listing.
+
+ BufferSize - Supplies the size of the output buffer.
+
+ BytesNeeded - Receives the number of bytes required to get the
+ first entry. This value is returned iff ERROR_MORE_DATA is
+ the return code, and Buffer is too small to even fit one
+ entry.
+
+ EntriesRead - Receives the number of entries returned in Buffer.
+ This value is only returned iff NO_ERROR is the return code.
+ NO_ERROR is returned as long as at least one entry was written
+ into Buffer but does not necessarily mean that it's the number
+ of EntriesRequested.
+
+ ConnectionType - The type of connected resource wanted ( DISK, PRINT, ...)
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+{
+ DWORD status;
+ NWR_REQUEST_PACKET Rrp; // Redirector request packet
+
+
+ //
+ // Tell the redirector to enumerate all connections.
+ //
+ Rrp.Version = REQUEST_PACKET_VERSION;
+
+ Rrp.Parameters.EnumConn.ResumeKey = *ResumeId;
+ Rrp.Parameters.EnumConn.EntriesRequested = EntriesRequested;
+ Rrp.Parameters.EnumConn.ConnectionType = ConnectionType;
+
+ status = NwRedirFsControl(
+ RedirDeviceHandle,
+ FSCTL_NWR_ENUMERATE_CONNECTIONS,
+ &Rrp,
+ sizeof(NWR_REQUEST_PACKET),
+ Buffer, // User output buffer
+ BufferSize,
+ NULL
+ );
+
+ *EntriesRead = Rrp.Parameters.EnumConn.EntriesReturned;
+
+ if (status == WN_MORE_DATA) {
+ *BytesNeeded = Rrp.Parameters.EnumConn.BytesNeeded;
+
+ //
+ // NP specs expect WN_SUCCESS in this case.
+ //
+ if (*EntriesRead)
+ status = WN_SUCCESS ;
+ }
+
+ *ResumeId = Rrp.Parameters.EnumConn.ResumeKey;
+
+ return status;
+}
+
+
+DWORD
+NwGetNextServerEntry(
+ IN HANDLE PreferredServer,
+ IN OUT LPDWORD LastObjectId,
+ OUT LPSTR ServerName
+ )
+/*++
+
+Routine Description:
+
+ This function uses an opened handle to the preferred server to
+ scan it bindery for all file server objects.
+
+Arguments:
+
+ PreferredServer - Supplies the handle to the preferred server on
+ which to scan the bindery.
+
+ LastObjectId - On input, supplies the object ID to the last file
+ server object returned, which is the resume handle to get the
+ next file server object. On output, receives the object ID
+ of the file server object returned.
+
+ ServerName - Receives the name of the returned file server object.
+
+Return Value:
+
+ NO_ERROR - Successfully gotten a file server name.
+
+ WN_NO_MORE_ENTRIES - No other file server object past the one
+ specified by LastObjectId.
+
+--*/
+{
+ NTSTATUS ntstatus;
+ WORD ObjectType;
+
+
+#if DBG
+ IF_DEBUG(ENUM) {
+ KdPrint(("NWWORKSTATION: NwGetNextServerEntry LastObjectId %lu\n",
+ *LastObjectId));
+ }
+#endif
+
+ ntstatus = NwlibMakeNcp(
+ PreferredServer,
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 58, // Max request packet size
+ 59, // Max response packet size
+ "bdwp|dwc", // Format string
+ 0x37, // Scan bindery object
+ *LastObjectId, // Previous ID
+ 0x4, // File server object
+ "*", // Wildcard to match all
+ LastObjectId, // Current ID
+ &ObjectType, // Ignore
+ ServerName // Currently returned server
+ );
+
+#if DBG
+ if (ntstatus == STATUS_SUCCESS) {
+ IF_DEBUG(ENUM) {
+ KdPrint(("NWWORKSTATION: NwGetNextServerEntry NewObjectId %08lx, ServerName %s\n",
+ *LastObjectId, ServerName));
+ }
+ }
+#endif
+
+ return NwMapBinderyCompletionCode(ntstatus);
+}
+
+
+DWORD
+GetConnectedBinderyServers(
+ OUT LPNW_ENUM_CONTEXT ContextHandle
+ )
+/*++
+
+Routine Description:
+
+ This function is a helper routine for the function
+ NwGetNextServerConnection. It allocates a buffer to cache
+ bindery server names returned from calls to the redirector. Since the
+ redirector may return duplicate bindery server names, this
+ function checks to see if the server name already exist in the buffer
+ before adding it.
+
+Arguments:
+
+ ContextHandle - Used to track cached bindery information and the
+ current server name pointer in the cache buffer.
+
+Return Value:
+
+ NO_ERROR - Successfully returned a server name and cache buffer.
+
+ WN_NO_MORE_ENTRIES - No other server object past the one
+ specified by CH->ResumeId.
+
+ ERROR_NOT_ENOUGH_MEMORY - Function was unable to allocate a buffer.
+
+++*/
+{
+ DWORD ResumeKey = 0;
+ LPBYTE pBuffer = NULL;
+ DWORD EntriesRead = 0;
+ BYTE tokenIter;
+ LPWSTR tokenPtr;
+ BOOL fAddToList;
+ DWORD status = NwGetConnectionStatus( NULL,
+ &ResumeKey,
+ &pBuffer,
+ &EntriesRead );
+
+ if ( status == NO_ERROR && EntriesRead > 0 )
+ {
+ DWORD i;
+ PCONN_STATUS pConnStatus = (PCONN_STATUS) pBuffer;
+
+ ContextHandle->ResumeId = 0;
+ ContextHandle->NdsRawDataCount = 0;
+ ContextHandle->NdsRawDataSize = (NW_MAX_SERVER_LEN + 2) * EntriesRead;
+ ContextHandle->NdsRawDataBuffer =
+ (DWORD) LocalAlloc( LMEM_ZEROINIT,
+ ContextHandle->NdsRawDataSize );
+
+ if ( ContextHandle->NdsRawDataBuffer == 0 )
+ {
+ KdPrint(("NWWORKSTATION: GetConnectedBinderyServers LocalAlloc failed %lu\n",
+ GetLastError()));
+
+ ContextHandle->NdsRawDataSize = 0;
+
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ for ( i = 0; i < EntriesRead ; i++ )
+ {
+ fAddToList = FALSE;
+
+ if ( pConnStatus->fNds == 0 &&
+ ( pConnStatus->dwConnType == NW_CONN_BINDERY_LOGIN ||
+ pConnStatus->dwConnType == NW_CONN_NDS_AUTHENTICATED_NO_LICENSE ||
+ pConnStatus->dwConnType == NW_CONN_NDS_AUTHENTICATED_LICENSED ||
+ pConnStatus->dwConnType == NW_CONN_DISCONNECTED ) )
+ {
+ fAddToList = TRUE;
+ tokenPtr = (LPWSTR) ContextHandle->NdsRawDataBuffer;
+ tokenIter = 0;
+
+ //
+ // Walk through buffer to see if the tree name already exists.
+ //
+ while ( tokenIter < ContextHandle->NdsRawDataCount )
+ {
+ if ( !wcscmp( tokenPtr, pConnStatus->pszServerName ) )
+ {
+ fAddToList = FALSE;
+ }
+
+ tokenPtr = tokenPtr + wcslen( tokenPtr ) + 1;
+ tokenIter++;
+ }
+ }
+
+ //
+ // Add the new tree name to end of buffer if needed.
+ //
+ if ( fAddToList )
+ {
+ wcscpy( tokenPtr, pConnStatus->pszServerName );
+ _wcsupr( tokenPtr );
+ ContextHandle->NdsRawDataCount += 1;
+ }
+
+ pConnStatus = (PCONN_STATUS) ( (DWORD) pConnStatus +
+ pConnStatus->dwTotalLength );
+ }
+
+ if ( pBuffer != NULL )
+ {
+ LocalFree( pBuffer );
+ pBuffer = NULL;
+ }
+
+ if ( ContextHandle->NdsRawDataCount > 0 )
+ {
+ //
+ // Set ResumeId to point to the first entry in buffer
+ // and have NdsRawDataCount set to the number
+ // of tree entries left in buffer (ie. substract 1)
+ //
+ ContextHandle->ResumeId = ContextHandle->NdsRawDataBuffer;
+ ContextHandle->NdsRawDataCount -= 1;
+ }
+
+ return NO_ERROR;
+ }
+
+ return WN_NO_MORE_ENTRIES;
+}
+
+
+DWORD
+NwGetNextServerConnection(
+ OUT LPNW_ENUM_CONTEXT ContextHandle
+ )
+/*++
+
+Routine Description:
+
+ This function queries the redirector for bindery server connections
+
+Arguments:
+
+ ContextHandle - Receives the name of the returned bindery server.
+
+Return Value:
+
+ NO_ERROR - Successfully returned a server name.
+
+ WN_NO_MORE_ENTRIES - No other server objects past the one
+ specified by CH->ResumeId exist.
+
+--*/
+{
+#if DBG
+ IF_DEBUG(ENUM) {
+ KdPrint(("NWWORKSTATION: NwGetNextServerConnection ResumeId %lu\n",
+ ContextHandle->ResumeId));
+ }
+#endif
+
+ if ( ContextHandle->ResumeId == 0xFFFFFFFF &&
+ ContextHandle->NdsRawDataBuffer == 0 &&
+ ContextHandle->NdsRawDataCount == 0 )
+ {
+ //
+ // Fill the buffer and point ResumeId to the last
+ // server entry name in it. NdsRawDataCount will be
+ // set to one less than the number of server names in buffer.
+ //
+ return GetConnectedBinderyServers( ContextHandle );
+ }
+
+ if ( ContextHandle->NdsRawDataBuffer != 0 &&
+ ContextHandle->NdsRawDataCount > 0 )
+ {
+ //
+ // Move ResumeId to point to the next entry in the buffer
+ // and decrement the NdsRawDataCount by one. Watch for case
+ // where we backed up to 0xFFFFFFFF.
+ //
+ if (ContextHandle->ResumeId == 0xFFFFFFFF) {
+
+ //
+ // Reset to start of buffer.
+ //
+ ContextHandle->ResumeId = ContextHandle->NdsRawDataBuffer;
+ }
+ else {
+
+ //
+ // Treat as pointer and advance as need.
+ //
+ ContextHandle->ResumeId =
+ ContextHandle->ResumeId +
+ ( ( wcslen( (LPWSTR) ContextHandle->ResumeId ) + 1 ) *
+ sizeof(WCHAR) );
+ }
+ ContextHandle->NdsRawDataCount -= 1;
+
+ return NO_ERROR;
+ }
+
+ if ( ContextHandle->NdsRawDataBuffer != 0 &&
+ ContextHandle->NdsRawDataCount == 0 )
+ {
+ //
+ // We already have a buffer and processed all server names
+ // in it, and there is no more data to get.
+ // So free the memory used for the buffer and return
+ // WN_NO_MORE_ENTRIES to tell WinFile that we are done.
+ //
+ (void) LocalFree( (HLOCAL) ContextHandle->NdsRawDataBuffer );
+
+ ContextHandle->NdsRawDataBuffer = 0;
+ ContextHandle->NdsRawDataSize = 0;
+
+ return WN_NO_MORE_ENTRIES;
+ }
+
+ //
+ // Were done
+ //
+ return WN_NO_MORE_ENTRIES;
+}
+
+
+DWORD
+GetTreeEntriesFromBindery(
+ OUT LPNW_ENUM_CONTEXT ContextHandle
+ )
+/*++
+
+Routine Description:
+
+ This function is a helper routine for the function NwGetNextNdsTreeEntry.
+ It allocates a buffer (if needed) to cache NDS tree names returned from
+ calls to the bindery. Since the bindery often returns duplicates of a
+ NDS tree name, this function checks to see if the tree name already
+ exist in the buffer before adding it to it if not present.
+
+Arguments:
+
+ ContextHandle - Used to track cached bindery information and the
+ current tree name pointer in the cache buffer.
+
+Return Value:
+
+ NO_ERROR - Successfully returned a NDS tree name and cache buffer.
+
+ WN_NO_MORE_ENTRIES - No other NDS tree object past the one
+ specified by CH->ResumeId.
+
+ ERROR_NOT_ENOUGH_MEMORY - Function was unable to allocate a buffer.
+
+++*/
+{
+ NTSTATUS ntstatus = STATUS_SUCCESS;
+ SERVERNAME TreeName;
+ LPWSTR UTreeName = NULL; //Unicode tree name
+ DWORD tempDataId;
+ WORD ObjectType;
+ BYTE iter;
+ BYTE tokenIter;
+ LPWSTR tokenPtr;
+ BOOL fAddToList;
+
+ //
+ // Check to see if we need to allocate a buffer for use
+ //
+ if ( ContextHandle->NdsRawDataBuffer == 0x00000000 )
+ {
+ ContextHandle->NdsRawDataId = ContextHandle->ResumeId;
+ ContextHandle->NdsRawDataSize = EIGHT_KB;
+ ContextHandle->NdsRawDataBuffer =
+ (DWORD) LocalAlloc( LMEM_FIXED | LMEM_ZEROINIT,
+ ContextHandle->NdsRawDataSize );
+
+ if ( ContextHandle->NdsRawDataBuffer == 0 )
+ {
+ KdPrint(("NWWORKSTATION: GetTreeEntriesFromBindery LocalAlloc failed %lu\n",
+ GetLastError()));
+
+ ContextHandle->NdsRawDataSize = 0;
+ ContextHandle->NdsRawDataId = 0xFFFFFFFF;
+
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+ }
+
+ //
+ // Repeatedly call bindery to fill buffer with NDS tree names until
+ // buffer is full.
+ //
+ while ( ntstatus == STATUS_SUCCESS )
+ {
+ RtlZeroMemory( TreeName, sizeof( TreeName ) );
+
+ tempDataId = ContextHandle->NdsRawDataId;
+
+ ntstatus = NwlibMakeNcp(
+ ContextHandle->TreeConnectionHandle,
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 58, // Max request packet size
+ 59, // Max response packet size
+ "bdwp|dwc", // Format string
+ 0x37, // Scan bindery object
+ ContextHandle->NdsRawDataId, // Previous ID
+ 0x278, // Directory server object
+ "*", // Wildcard to match all
+ &ContextHandle->NdsRawDataId, // Current ID
+ &ObjectType, // Ignore
+ TreeName // Currently returned NDS tree
+ );
+
+ //
+ // We got a tree name, clean it up (i.e. get rid of underscores ),
+ // and add it to buffer if unique.
+ //
+ if ( ntstatus == STATUS_SUCCESS )
+ {
+ iter = 31;
+
+ while ( TreeName[iter] == '_' && iter > 0 )
+ {
+ iter--;
+ }
+
+ TreeName[iter + 1] = '\0';
+
+ //
+ // Convert tree name to a UNICODE string and proccess it,
+ // else just skip it and move on to the next tree name.
+ //
+ if ( NwConvertToUnicode( &UTreeName, TreeName ) )
+ {
+ tokenPtr = (LPWSTR) ContextHandle->NdsRawDataBuffer;
+ tokenIter = 0;
+ fAddToList = TRUE;
+
+ //
+ // Walk through buffer to see if the tree name already exists.
+ //
+ while ( tokenIter < ContextHandle->NdsRawDataCount )
+ {
+ if ( !wcscmp( tokenPtr, UTreeName ) )
+ {
+ fAddToList = FALSE;
+ }
+
+ tokenPtr = tokenPtr + wcslen( tokenPtr ) + 1;
+ tokenIter++;
+ }
+
+ //
+ // Add the new tree name to end of buffer if needed.
+ //
+ if ( fAddToList )
+ {
+ DWORD BytesNeededToAddTreeName = (wcslen(UTreeName)+1) * sizeof(WCHAR);
+ DWORD NumberOfBytesAvailable = ContextHandle->NdsRawDataBuffer +
+ ContextHandle->NdsRawDataSize -
+ (DWORD) tokenPtr;
+
+ if ( BytesNeededToAddTreeName < NumberOfBytesAvailable )
+ {
+ wcscpy( tokenPtr, UTreeName );
+ ContextHandle->NdsRawDataCount += 1;
+ }
+ else
+ {
+ ContextHandle->NdsRawDataId = tempDataId;
+ ntstatus = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ }
+
+ (void) LocalFree((HLOCAL) UTreeName);
+ }
+ }
+ }
+
+ //
+ // We are done filling buffer, and there are no more tree names
+ // to request. Set id to indicate last value.
+ //
+ if ( ntstatus == STATUS_NO_MORE_ENTRIES )
+ {
+ ContextHandle->NdsRawDataId = 0xFFFFFFFF;
+ ntstatus = STATUS_SUCCESS;
+ }
+
+ //
+ // We are done because the buffer is full. So we return NO_ERROR to
+ // indicate completion, and leave ContextHandle->NdsRawDataId as is
+ // to indicate where we left off.
+ //
+ if ( ntstatus == ERROR_NOT_ENOUGH_MEMORY )
+ {
+ ntstatus = STATUS_SUCCESS;
+ }
+
+ if ( ContextHandle->NdsRawDataCount == 0 )
+ {
+ if ( ContextHandle->NdsRawDataBuffer )
+ (void) LocalFree( (HLOCAL) ContextHandle->NdsRawDataBuffer );
+
+ ContextHandle->NdsRawDataBuffer = 0;
+ ContextHandle->NdsRawDataSize = 0;
+ ContextHandle->NdsRawDataId = 0xFFFFFFFF;
+
+ return WN_NO_MORE_ENTRIES;
+ }
+
+ if ( ContextHandle->NdsRawDataCount > 0 )
+ {
+ //
+ // Set ResumeId to point to the first entry in buffer
+ // and have NdsRawDataCount set to the number
+ // of tree entries left in buffer (ie. substract 1)
+ //
+ ContextHandle->ResumeId = ContextHandle->NdsRawDataBuffer;
+ ContextHandle->NdsRawDataCount -= 1;
+
+ return NO_ERROR;
+ }
+
+ if ( ContextHandle->NdsRawDataBuffer )
+ (void) LocalFree( (HLOCAL) ContextHandle->NdsRawDataBuffer );
+
+ ContextHandle->NdsRawDataBuffer = 0;
+ ContextHandle->NdsRawDataSize = 0;
+ ContextHandle->NdsRawDataId = 0xFFFFFFFF;
+
+ return NwMapStatus( ntstatus );
+}
+
+
+DWORD
+NwGetNextNdsTreeEntry(
+ OUT LPNW_ENUM_CONTEXT ContextHandle
+ )
+/*++
+
+Routine Description:
+
+ This function uses an opened handle to the preferred server to
+ scan it bindery for all NDS tree objects.
+
+Arguments:
+
+ ContextHandle - Receives the name of the returned NDS tree object
+ given the current preferred server connection and CH->ResumeId.
+
+Return Value:
+
+ NO_ERROR - Successfully returned a NDS tree name.
+
+ WN_NO_MORE_ENTRIES - No other NDS tree objects past the one
+ specified by CH->ResumeId exist.
+
+--*/
+{
+#if DBG
+ IF_DEBUG(ENUM) {
+ KdPrint(("NWWORKSTATION: NwGetNextNdsTreeEntry ResumeId %lu\n",
+ ContextHandle->ResumeId));
+ }
+#endif
+
+ if ( ContextHandle->ResumeId == 0xFFFFFFFF &&
+ ContextHandle->NdsRawDataBuffer == 0 &&
+ ContextHandle->NdsRawDataCount == 0 )
+ {
+ //
+ // Fill the buffer and point ResumeId to the last
+ // tree entry name in it. NdsRawDataCount will be
+ // set to one less than the number of tree names in buffer.
+ //
+ return GetTreeEntriesFromBindery( ContextHandle );
+ }
+
+ if ( ContextHandle->NdsRawDataBuffer != 0 &&
+ ContextHandle->NdsRawDataCount > 0 )
+ {
+ //
+ // Move ResumeId to point to the next entry in the buffer
+ // and decrement the NdsRawDataCount by one. Watch for case
+ // where we backed up to 0xFFFFFFFF.
+ //
+ if (ContextHandle->ResumeId == 0xFFFFFFFF) {
+
+ //
+ // Reset to start of buffer.
+ //
+ ContextHandle->ResumeId = ContextHandle->NdsRawDataBuffer;
+ }
+ else {
+
+ //
+ // Move ResumeId to point to the next entry in the buffer
+ // and decrement the NdsRawDataCount by one
+ //
+ ContextHandle->ResumeId =
+ ContextHandle->ResumeId +
+ ( ( wcslen( (LPWSTR) ContextHandle->ResumeId ) + 1 ) *
+ sizeof(WCHAR) );
+ }
+
+ ContextHandle->NdsRawDataCount -= 1;
+
+ return NO_ERROR;
+ }
+
+ if ( ContextHandle->NdsRawDataBuffer != 0 &&
+ ContextHandle->NdsRawDataCount == 0 &&
+ ContextHandle->NdsRawDataId != 0xFFFFFFFF )
+ {
+ //
+ // We already have a buffer and processed all tree names
+ // in it, and there is more data in the bindery to get.
+ // So go get it and point ResumeId to the last tree
+ // entry name in the buffer and set NdsRawDataCount to
+ // one less than the number of tree names in buffer.
+ //
+ return GetTreeEntriesFromBindery( ContextHandle );
+ }
+
+ if ( ContextHandle->NdsRawDataBuffer != 0 &&
+ ContextHandle->NdsRawDataCount == 0 &&
+ ContextHandle->NdsRawDataId == 0xFFFFFFFF )
+ {
+ //
+ // We already have a buffer and processed all tree names
+ // in it, and there is no more data in the bindery to get.
+ // So free the memory used for the buffer and return
+ // WN_NO_MORE_ENTRIES to tell WinFile that we are done.
+ //
+ (void) LocalFree( (HLOCAL) ContextHandle->NdsRawDataBuffer );
+
+ ContextHandle->NdsRawDataBuffer = 0;
+ ContextHandle->NdsRawDataSize = 0;
+
+ return WN_NO_MORE_ENTRIES;
+ }
+
+ //
+ // We should never hit this area!
+ //
+ return WN_NO_MORE_ENTRIES;
+}
+
+
+DWORD
+NwGetNextVolumeEntry(
+ IN HANDLE ServerConnection,
+ IN DWORD NextVolumeNumber,
+ OUT LPSTR VolumeName
+ )
+/*++
+
+Routine Description:
+
+ This function lists the volumes on the server specified by
+ an opened tree connection handle to the server.
+
+Arguments:
+
+ ServerConnection - Supplies the tree connection handle to the
+ server to enumerate volumes from.
+
+ NextVolumeNumber - Supplies the volume number which to look
+ up the name.
+
+ VolumeName - Receives the name of the volume associated with
+ NextVolumeNumber.
+
+Return Value:
+
+ NO_ERROR - Successfully gotten the volume name.
+
+ WN_NO_MORE_ENTRIES - No other volume name associated with the
+ specified volume number.
+
+--*/
+{
+ NTSTATUS ntstatus;
+
+#if DBG
+ IF_DEBUG(ENUM) {
+ KdPrint(("NWWORKSTATION: NwGetNextVolumeEntry volume number %lu\n",
+ NextVolumeNumber));
+ }
+#endif
+
+ ntstatus = NwlibMakeNcp(
+ ServerConnection,
+ FSCTL_NWR_NCP_E2H, // Directory function
+ 4, // Max request packet size
+ 19, // Max response packet size
+ "bb|p", // Format string
+ 0x6, // Get volume name
+ (BYTE) NextVolumeNumber, // Previous ID
+ VolumeName // Currently returned server
+ );
+
+ return NwMapStatus(ntstatus);
+}
+
+
+DWORD
+NwRdrLogonUser(
+ IN PLUID LogonId,
+ IN LPWSTR UserName,
+ IN DWORD UserNameSize,
+ IN LPWSTR Password OPTIONAL,
+ IN DWORD PasswordSize,
+ IN LPWSTR PreferredServer OPTIONAL,
+ IN DWORD PreferredServerSize
+ )
+/*++
+
+Routine Description:
+
+ This function tells the redirector the user logon credential.
+
+Arguments:
+
+ UserName - Supplies the user name.
+
+ UserNameSize - Supplies the size in bytes of the user name string without
+ the NULL terminator.
+
+ Password - Supplies the password.
+
+ PasswordSize - Supplies the size in bytes of the password string without
+ the NULL terminator.
+
+ PreferredServer - Supplies the preferred server name.
+
+ PreferredServerSize - Supplies the size in bytes of the preferred server
+ string without the NULL terminator.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+{
+ DWORD status;
+
+ PNWR_REQUEST_PACKET Rrp; // Redirector request packet
+
+ DWORD RrpSize = sizeof(NWR_REQUEST_PACKET) +
+ UserNameSize +
+ PasswordSize +
+ PreferredServerSize;
+ LPBYTE Dest;
+ BYTE lpReplicaAddress[sizeof(TDI_ADDRESS_IPX)];
+ DWORD ReplicaAddressSize = 0;
+
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ BYTE PW[128];
+
+
+ RtlZeroMemory(PW, sizeof(PW));
+
+ if (PasswordSize > (sizeof(PW) - 1)) {
+ memcpy(PW, Password, sizeof(PW) - 1);
+ }
+ else {
+ memcpy(PW, Password, PasswordSize);
+ }
+
+ KdPrint(("NWWORKSTATION: NwRdrLogonUser: UserName %ws\n", UserName));
+ KdPrint((" Password %ws\n", PW));
+ if ( PreferredServer )
+ KdPrint((" Server %ws\n", PreferredServer ));
+ }
+#endif
+
+ if ( PreferredServer && PreferredServer[0] == TREECHAR )
+ {
+ WCHAR TreeName[MAX_NDS_NAME_CHARS + 1];
+ LPWSTR lpTemp;
+
+ //
+ // Find the nearest dir server for the tree that the user wants to
+ // connect to.
+ //
+
+ wcscpy( TreeName, PreferredServer + 1 );
+ lpTemp = wcschr( TreeName, L'\\' );
+ lpTemp[0] = L'\0';
+
+ GetNearestDirServer( TreeName,
+ &ReplicaAddressSize,
+ lpReplicaAddress );
+
+ RrpSize += ReplicaAddressSize;
+ }
+
+ //
+ // Allocate the request packet
+ //
+ if ((Rrp = (PVOID) LocalAlloc(
+ LMEM_ZEROINIT,
+ RrpSize
+ )) == NULL) {
+
+ KdPrint(("NWWORKSTATION: NwRdrLogonUser LocalAlloc failed %lu\n",
+ GetLastError()));
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ //
+ // Tell the redirector the user logon credential.
+ //
+ Rrp->Version = REQUEST_PACKET_VERSION;
+
+ RtlCopyLuid(&(Rrp->Parameters.Logon.LogonId), LogonId);
+
+#if DBG
+ IF_DEBUG(LOGON) {
+ KdPrint(("NWWORKSTATION: NwRdrLogonUser passing to Rdr logon ID %lu %lu\n",
+ *LogonId, *((PULONG) ((DWORD) LogonId + sizeof(ULONG)))));
+ }
+#endif
+
+ Rrp->Parameters.Logon.UserNameLength = UserNameSize;
+ Rrp->Parameters.Logon.PasswordLength = PasswordSize;
+ Rrp->Parameters.Logon.ServerNameLength = PreferredServerSize;
+ Rrp->Parameters.Logon.ReplicaAddrLength = ReplicaAddressSize;
+
+ memcpy(Rrp->Parameters.Logon.UserName, UserName, UserNameSize);
+ Dest = (LPBYTE) ((DWORD) Rrp->Parameters.Logon.UserName + UserNameSize);
+
+ if (PasswordSize > 0)
+ {
+ memcpy(Dest, Password, PasswordSize);
+ Dest = (LPBYTE) ((DWORD) Dest + PasswordSize);
+ }
+
+ if (PreferredServerSize > 0)
+ {
+ memcpy(Dest, PreferredServer, PreferredServerSize);
+
+ if (ReplicaAddressSize > 0)
+ {
+ Dest = (LPBYTE) ((DWORD) Dest + PreferredServerSize);
+ memcpy(Dest, lpReplicaAddress, ReplicaAddressSize);
+ }
+ }
+
+ status = NwRedirFsControl(
+ RedirDeviceHandle,
+ FSCTL_NWR_LOGON,
+ Rrp,
+ RrpSize,
+ NULL, // No logon script in this release
+ 0,
+ NULL
+ );
+
+ RtlZeroMemory(Rrp, RrpSize); // Clear the password
+ (void) LocalFree((HLOCAL) Rrp);
+
+ return status;
+}
+
+
+VOID
+NwRdrChangePassword(
+ IN PNWR_REQUEST_PACKET Rrp
+ )
+/*++
+
+Routine Description:
+
+ This function tells the redirector the new password for a user on
+ a particular server.
+
+Arguments:
+
+ Rrp - Supplies the username, new password and servername.
+
+ RrpSize - Supplies the size of the request packet.
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ //
+ // Tell the redirector the user new password.
+ //
+ Rrp->Version = REQUEST_PACKET_VERSION;
+
+ (void) NwRedirFsControl(
+ RedirDeviceHandle,
+ FSCTL_NWR_CHANGE_PASS,
+ Rrp,
+ sizeof(NWR_REQUEST_PACKET) +
+ Rrp->Parameters.ChangePass.UserNameLength +
+ Rrp->Parameters.ChangePass.PasswordLength +
+ Rrp->Parameters.ChangePass.ServerNameLength,
+ NULL,
+ 0,
+ NULL
+ );
+
+}
+
+
+DWORD
+NwRdrSetInfo(
+ IN DWORD PrintOption,
+ IN DWORD PacketBurstSize,
+ IN LPWSTR PreferredServer OPTIONAL,
+ IN DWORD PreferredServerSize,
+ IN LPWSTR ProviderName OPTIONAL,
+ IN DWORD ProviderNameSize
+ )
+/*++
+
+Routine Description:
+
+ This function passes some workstation configuration and current user's
+ preference to the redirector. This includes the network provider name, the
+ packet burst size, the user's selected preferred server and print option.
+
+Arguments:
+
+ PrintOption - The current user's print option
+
+ PacketBurstSize - The packet burst size stored in the registry
+
+ PreferredServer - The preferred server the current user selected
+ PreferredServerSize - Supplies the size in bytes of the preferred server
+ string without the NULL terminator.
+
+ ProviderName - Supplies the provider name.
+ ProviderNameSize - Supplies the size in bytes of the provider name
+ string without the NULL terminator.
+
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+{
+ DWORD status;
+
+ PNWR_REQUEST_PACKET Rrp; // Redirector request packet
+
+ DWORD RrpSize = sizeof(NWR_REQUEST_PACKET) +
+ PreferredServerSize +
+ ProviderNameSize;
+
+ LPBYTE Dest;
+
+ //
+ // Allocate the request packet
+ //
+ if ((Rrp = (PVOID) LocalAlloc(
+ LMEM_ZEROINIT,
+ RrpSize
+ )) == NULL) {
+
+ KdPrint(("NWWORKSTATION: NwRdrSetInfo LocalAlloc failed %lu\n",
+ GetLastError()));
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ Rrp->Version = REQUEST_PACKET_VERSION;
+
+ Rrp->Parameters.SetInfo.PrintOption = PrintOption;
+ Rrp->Parameters.SetInfo.MaximumBurstSize = PacketBurstSize;
+
+ Rrp->Parameters.SetInfo.PreferredServerLength = PreferredServerSize;
+ Rrp->Parameters.SetInfo.ProviderNameLength = ProviderNameSize;
+
+ if (ProviderNameSize > 0) {
+ memcpy( Rrp->Parameters.SetInfo.PreferredServer,
+ PreferredServer, PreferredServerSize);
+ }
+
+ Dest = (LPBYTE) ((DWORD) Rrp->Parameters.SetInfo.PreferredServer
+ + PreferredServerSize);
+
+ if (ProviderNameSize > 0) {
+ memcpy(Dest, ProviderName, ProviderNameSize);
+ }
+
+ status = NwRedirFsControl(
+ RedirDeviceHandle,
+ FSCTL_NWR_SET_INFO,
+ Rrp,
+ RrpSize,
+ NULL,
+ 0,
+ NULL
+ );
+
+ (void) LocalFree((HLOCAL) Rrp);
+
+
+ if ( status != NO_ERROR )
+ {
+ KdPrint(("NwRedirFsControl: FSCTL_NWR_SET_INFO failed with %d\n",
+ status ));
+ }
+
+ return status;
+}
+
+
+DWORD
+NwRdrLogoffUser(
+ IN PLUID LogonId
+ )
+/*++
+
+Routine Description:
+
+ This function asks the redirector to log off the interactive user.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+{
+ DWORD status;
+ NWR_REQUEST_PACKET Rrp; // Redirector request packet
+
+
+ //
+ // Tell the redirector to logoff user.
+ //
+ Rrp.Version = REQUEST_PACKET_VERSION;
+
+ RtlCopyLuid(&Rrp.Parameters.Logoff.LogonId, LogonId);
+
+ status = NwRedirFsControl(
+ RedirDeviceHandle,
+ FSCTL_NWR_LOGOFF,
+ &Rrp,
+ sizeof(NWR_REQUEST_PACKET),
+ NULL,
+ 0,
+ NULL
+ );
+
+ return status;
+}
+
+
+DWORD
+NwConnectToServer(
+ IN LPWSTR ServerName
+ )
+/*++
+
+Routine Description:
+
+ This function opens a handle to \Device\Nwrdr\ServerName, given
+ ServerName, and then closes the handle if the open was successful.
+ It is to validate that the current user credential can access
+ the server.
+
+Arguments:
+
+ ServerName - Supplies the name of the server to validate the
+ user credential.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+{
+ DWORD status;
+ UNICODE_STRING ServerStr;
+ HANDLE ServerHandle;
+
+
+
+ ServerStr.MaximumLength = (wcslen(ServerName) + 2) *
+ sizeof(WCHAR) + // \ServerName0
+ RedirDeviceName.Length; // \Device\Nwrdr
+
+ if ((ServerStr.Buffer = (PWSTR) LocalAlloc(
+ LMEM_ZEROINIT,
+ (UINT) ServerStr.MaximumLength
+ )) == NULL) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ //
+ // Copy \Device\NwRdr
+ //
+ RtlCopyUnicodeString(&ServerStr, &RedirDeviceName);
+
+ //
+ // Concatenate \ServerName
+ //
+ wcscat(ServerStr.Buffer, L"\\");
+ ServerStr.Length += sizeof(WCHAR);
+
+ wcscat(ServerStr.Buffer, ServerName);
+ ServerStr.Length += (USHORT) (wcslen(ServerName) * sizeof(WCHAR));
+
+
+ status = NwOpenCreateConnection(
+ &ServerStr,
+ NULL,
+ NULL,
+ ServerName,
+ SYNCHRONIZE | FILE_WRITE_DATA,
+ FILE_OPEN,
+ FILE_SYNCHRONOUS_IO_NONALERT,
+ RESOURCETYPE_DISK,
+ &ServerHandle,
+ NULL
+ );
+
+ if (status == ERROR_FILE_NOT_FOUND) {
+ status = ERROR_BAD_NETPATH;
+ }
+
+ (void) LocalFree((HLOCAL) ServerStr.Buffer);
+
+ if (status == NO_ERROR || status == NW_PASSWORD_HAS_EXPIRED) {
+ (void) NtClose(ServerHandle);
+ }
+
+ return status;
+}
+
+DWORD
+NWPGetConnectionStatus(
+ IN LPWSTR pszRemoteName,
+ IN OUT PDWORD ResumeKey,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT PDWORD BytesNeeded,
+ OUT PDWORD EntriesRead
+)
+{
+ NTSTATUS ntstatus = STATUS_SUCCESS;
+ HANDLE handleRdr = NULL;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ IO_STATUS_BLOCK IoStatusBlock;
+ UNICODE_STRING uRdrName;
+ WCHAR RdrPrefix[] = L"\\Device\\NwRdr\\*";
+
+ PNWR_REQUEST_PACKET RequestPacket = NULL;
+ DWORD RequestPacketSize = 0;
+ DWORD dwRemoteNameLen = 0;
+
+ //
+ // Set up the object attributes.
+ //
+
+ RtlInitUnicodeString( &uRdrName, RdrPrefix );
+
+ InitializeObjectAttributes( &ObjectAttributes,
+ &uRdrName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL );
+
+ ntstatus = NtOpenFile( &handleRdr,
+ SYNCHRONIZE | FILE_LIST_DIRECTORY,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_VALID_FLAGS,
+ FILE_SYNCHRONOUS_IO_NONALERT );
+
+ if ( !NT_SUCCESS(ntstatus) )
+ goto CleanExit;
+
+ dwRemoteNameLen = pszRemoteName? wcslen(pszRemoteName)*sizeof(WCHAR) : 0;
+
+ RequestPacketSize = sizeof( NWR_REQUEST_PACKET ) + dwRemoteNameLen;
+
+ RequestPacket = (PNWR_REQUEST_PACKET) LocalAlloc( LMEM_ZEROINIT,
+ RequestPacketSize );
+
+ if ( RequestPacket == NULL )
+ {
+ ntstatus = STATUS_NO_MEMORY;
+ goto CleanExit;
+ }
+
+ //
+ // Fill out the request packet for FSCTL_NWR_GET_CONN_STATUS.
+ //
+
+ RequestPacket->Parameters.GetConnStatus.ResumeKey = *ResumeKey;
+
+ RequestPacket->Version = REQUEST_PACKET_VERSION;
+ RequestPacket->Parameters.GetConnStatus.ConnectionNameLength = dwRemoteNameLen;
+
+ RtlCopyMemory( &(RequestPacket->Parameters.GetConnStatus.ConnectionName[0]),
+ pszRemoteName,
+ dwRemoteNameLen );
+
+ ntstatus = NtFsControlFile( handleRdr,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_GET_CONN_STATUS,
+ (PVOID) RequestPacket,
+ RequestPacketSize,
+ (PVOID) Buffer,
+ BufferSize );
+
+ if ( NT_SUCCESS( ntstatus ))
+ ntstatus = IoStatusBlock.Status;
+
+ *EntriesRead = RequestPacket->Parameters.GetConnStatus.EntriesReturned;
+ *ResumeKey = RequestPacket->Parameters.GetConnStatus.ResumeKey;
+ *BytesNeeded = RequestPacket->Parameters.GetConnStatus.BytesNeeded;
+
+CleanExit:
+
+ if ( handleRdr != NULL )
+ NtClose( handleRdr );
+
+ if ( RequestPacket != NULL )
+ LocalFree( RequestPacket );
+
+ return RtlNtStatusToDosError( ntstatus );
+}
+
+DWORD
+NwGetConnectionStatus(
+ IN LPWSTR pszRemoteName,
+ OUT PDWORD ResumeKey,
+ OUT LPBYTE *Buffer,
+ OUT PDWORD EntriesRead
+)
+{
+ DWORD err = NO_ERROR;
+ DWORD dwBytesNeeded = 0;
+ DWORD dwBufferSize = TWO_KB;
+
+ *Buffer = NULL;
+ *EntriesRead = 0;
+
+ do {
+
+ *Buffer = (LPBYTE) LocalAlloc( LMEM_ZEROINIT, dwBufferSize );
+
+ if ( *Buffer == NULL )
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ err = NWPGetConnectionStatus( pszRemoteName,
+ ResumeKey,
+ *Buffer,
+ dwBufferSize,
+ &dwBytesNeeded,
+ EntriesRead );
+
+ if ( err == ERROR_INSUFFICIENT_BUFFER )
+ {
+ dwBufferSize = dwBytesNeeded + EXTRA_BYTES;
+ LocalFree( *Buffer );
+ *Buffer = NULL;
+ }
+
+ } while ( err == ERROR_INSUFFICIENT_BUFFER );
+
+ if ( err == ERROR_INVALID_PARAMETER ) // not attached
+ {
+ err = NO_ERROR;
+ *EntriesRead = 0;
+ }
+
+ return err;
+}
+
+VOID
+GetNearestDirServer(
+ IN LPWSTR TreeName,
+ OUT LPDWORD lpdwReplicaAddressSize,
+ OUT LPBYTE lpReplicaAddress
+ )
+{
+ WCHAR Buffer[BUFFSIZE];
+ PWSAQUERYSETW Query = (PWSAQUERYSETW)Buffer;
+ HANDLE hRnr;
+ DWORD dwQuerySize = BUFFSIZE;
+ GUID gdService = SVCID_NETWARE(0x278);
+ WSADATA wsaData;
+ WCHAR ServiceInstanceName[] = L"*";
+
+ WSAStartup(MAKEWORD(1, 1), &wsaData);
+
+ memset(Query, 0, sizeof(*Query));
+
+ //
+ // putting a "*" in the lpszServiceInstanceName causes
+ // the query to look for all server instances. Putting a
+ // specific name in here will search only for instance of
+ // that name. If you have a specific name to look for,
+ // put a pointer to the name here.
+ //
+ Query->lpszServiceInstanceName = ServiceInstanceName;
+ Query->dwNameSpace = NS_SAP;
+ Query->dwSize = sizeof(*Query);
+ Query->lpServiceClassId = &gdService;
+
+ //
+ // Find the servers. The flags indicate:
+ // LUP_NEAREST: look for nearest servers
+ // LUP_DEEP : if none are found on the local segement look
+ // for server using a general query
+ // LUP_RETURN_NAME: return the name
+ // LUP_RETURN_ADDR: return the server address
+ //
+ // if only servers on the local segment are acceptable, omit
+ // setting LUP_DEEP
+ //
+ if( WSALookupServiceBegin( Query,
+ LUP_NEAREST |
+ LUP_DEEP |
+ LUP_RETURN_NAME |
+ LUP_RETURN_ADDR,
+ &hRnr ) == SOCKET_ERROR )
+ {
+ //
+ // Something went wrong, return no address. The redirector will
+ // have to come up with a dir server on its own.
+ //
+ *lpdwReplicaAddressSize = 0;
+ return ;
+ }
+ else
+ {
+ //
+ // Ready to actually look for one of the suckers ...
+ //
+ Query->dwSize = BUFFSIZE;
+
+ while( WSALookupServiceNext( hRnr,
+ 0,
+ &dwQuerySize,
+ Query ) == NO_ERROR )
+ {
+ //
+ // Found a dir server, now see if it is a server for the NDS tree
+ // TreeName.
+ //
+ if ( NwpCompareTreeNames( Query->lpszServiceInstanceName,
+ TreeName ) )
+ {
+ *lpdwReplicaAddressSize = sizeof(TDI_ADDRESS_IPX);
+ memcpy( lpReplicaAddress,
+ Query->lpcsaBuffer->RemoteAddr.lpSockaddr->sa_data,
+ sizeof(TDI_ADDRESS_IPX) );
+
+ WSALookupServiceEnd(hRnr);
+ return ;
+ }
+ }
+
+ //
+ // Could not find a dir server, return no address. The redirector will
+ // have to come up with a dir server on its own.
+ //
+ *lpdwReplicaAddressSize = 0;
+ WSALookupServiceEnd(hRnr);
+ }
+}
+
+BOOL
+NwpCompareTreeNames(
+ LPWSTR lpServiceInstanceName,
+ LPWSTR lpTreeName
+ )
+{
+ DWORD iter = 31;
+
+ while ( lpServiceInstanceName[iter] == '_' && iter > 0 )
+ {
+ iter--;
+ }
+
+ lpServiceInstanceName[iter + 1] = '\0';
+
+ if ( !_wcsicmp( lpServiceInstanceName, lpTreeName ) )
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
diff --git a/private/nw/svcdlls/nwwks/server/enum.c b/private/nw/svcdlls/nwwks/server/enum.c
new file mode 100644
index 000000000..38b3d859f
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/server/enum.c
@@ -0,0 +1,7595 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ enum.c
+
+Abstract:
+
+ This module contains server, volume, and directory enumeration
+ routines supported by NetWare Workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 15-Feb-1993
+
+Revision History:
+
+--*/
+
+#include <stdlib.h>
+#include <nw.h>
+#include <splutil.h>
+#include <nwmisc.h>
+#include <nwreg.h>
+#include <nds.h>
+#include <nwapi32.h>
+
+//-------------------------------------------------------------------//
+// //
+// Definitions //
+// //
+//-------------------------------------------------------------------//
+
+//
+// Other definitions
+//
+#define ONE_KB 1024
+#define TWO_KB 2048
+#define FOUR_KB 4096
+#define EIGHT_KB 8192
+
+#define TREECHAR L'*'
+
+#define NW_VOLUME_NAME_LEN 256
+#define NW_MAX_VOLUME_NUMBER 64
+
+//
+// This structure is orginally defined in nwapi32.c, it is redefined
+// here so that the routine NWGetFileServerVersionInfo() can be called
+// with it.
+//
+typedef struct _NWC_SERVER_INFO {
+ HANDLE hConn ;
+ UNICODE_STRING ServerString ;
+} NWC_SERVER_INFO ;
+
+//-------------------------------------------------------------------//
+// //
+// Local Function Prototypes //
+// //
+//-------------------------------------------------------------------//
+
+DWORD
+NwrOpenEnumServersCommon(
+ IN NW_ENUM_TYPE EnumType,
+ OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
+ );
+
+DWORD
+NwrOpenEnumCommon(
+ IN LPWSTR ContainerName,
+ IN NW_ENUM_TYPE EnumType,
+ IN DWORD StartingPoint,
+ IN BOOL ValidateUserFlag,
+ IN LPWSTR UserName OPTIONAL,
+ IN LPWSTR Password OPTIONAL,
+ IN ULONG CreateDisposition,
+ IN ULONG CreateOptions,
+ OUT LPDWORD ClassTypeOfNDSLeaf,
+ OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
+ );
+
+DWORD
+NwEnumContextInfo(
+ IN LPNW_ENUM_CONTEXT ContextHandle,
+ IN DWORD EntriesRequested,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT LPDWORD BytesNeeded,
+ OUT LPDWORD EntriesRead
+ );
+
+DWORD
+NwEnumServersAndNdsTrees(
+ IN LPNW_ENUM_CONTEXT ContextHandle,
+ IN DWORD EntriesRequested,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT LPDWORD BytesNeeded,
+ OUT LPDWORD EntriesRead
+ );
+
+DWORD
+NwEnumPrintServers(
+ IN LPNW_ENUM_CONTEXT ContextHandle,
+ IN DWORD EntriesRequested,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT LPDWORD BytesNeeded,
+ OUT LPDWORD EntriesRead
+ );
+
+DWORD
+NwEnumVolumes(
+ IN LPNW_ENUM_CONTEXT ContextHandle,
+ IN DWORD EntriesRequested,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT LPDWORD BytesNeeded,
+ OUT LPDWORD EntriesRead
+ );
+
+DWORD
+NwEnumNdsSubTrees_Disk(
+ IN LPNW_ENUM_CONTEXT ContextHandle,
+ IN DWORD EntriesRequested,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT LPDWORD BytesNeeded,
+ OUT LPDWORD EntriesRead
+ );
+
+DWORD
+NwEnumNdsSubTrees_Print(
+ IN LPNW_ENUM_CONTEXT ContextHandle,
+ IN DWORD EntriesRequested,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT LPDWORD BytesNeeded,
+ OUT LPDWORD EntriesRead
+ );
+
+DWORD
+NwEnumNdsSubTrees_Any(
+ IN LPNW_ENUM_CONTEXT ContextHandle,
+ IN DWORD EntriesRequested,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT LPDWORD BytesNeeded,
+ OUT LPDWORD EntriesRead
+ );
+
+DWORD
+NwEnumQueues(
+ IN LPNW_ENUM_CONTEXT ContextHandle,
+ IN DWORD EntriesRequested,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT LPDWORD BytesNeeded,
+ OUT LPDWORD EntriesRead
+ );
+
+DWORD
+NwEnumVolumesQueues(
+ IN LPNW_ENUM_CONTEXT ContextHandle,
+ IN DWORD EntriesRequested,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT LPDWORD BytesNeeded,
+ OUT LPDWORD EntriesRead
+ );
+
+DWORD
+NwEnumDirectories(
+ IN LPNW_ENUM_CONTEXT ContextHandle,
+ IN DWORD EntriesRequested,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT LPDWORD BytesNeeded,
+ OUT LPDWORD EntriesRead
+ );
+
+DWORD
+NwEnumPrintQueues(
+ IN LPNW_ENUM_CONTEXT ContextHandle,
+ IN DWORD EntriesRequested,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT LPDWORD BytesNeeded,
+ OUT LPDWORD EntriesRead
+ );
+
+DWORD
+NwGetFirstDirectoryEntry(
+ IN HANDLE DirHandle,
+ OUT LPWSTR *DirEntry
+ );
+
+DWORD
+NwGetNextDirectoryEntry(
+ IN HANDLE DirHandle,
+ OUT LPWSTR *DirEntry
+ );
+
+DWORD
+NwGetFirstNdsSubTreeEntry(
+ OUT LPNW_ENUM_CONTEXT ContextHandle,
+ IN DWORD BufferSize
+ );
+
+DWORD
+NwGetNextNdsSubTreeEntry(
+ OUT LPNW_ENUM_CONTEXT ContextHandle
+ );
+
+BYTE
+NwGetSubTreeData(
+ IN DWORD NdsRawDataPtr,
+ OUT LPDWORD SubTreeName,
+ OUT LPDWORD ResourceScope,
+ OUT LPDWORD ResourceType,
+ OUT LPDWORD ResourceDisplayType,
+ OUT LPDWORD ResourceUsage,
+ OUT LPWSTR * StrippedObjectName
+ );
+
+VOID
+NwStripNdsUncName(
+ IN LPWSTR ObjectName,
+ OUT LPWSTR * StrippedObjectName
+ );
+
+#define VERIFY_ERROR_NOT_A_NDS_TREE 0x1010FFF0
+#define VERIFY_ERROR_PATH_NOT_FOUND 0x1010FFF1
+
+DWORD
+NwVerifyNDSObject(
+ IN LPWSTR lpNDSObjectNamePath,
+ OUT LPWSTR * lpFullNDSObjectNamePath,
+ OUT LPDWORD lpClassType,
+ OUT LPDWORD lpResourceScope,
+ OUT LPDWORD lpResourceType,
+ OUT LPDWORD lpResourceDisplayType,
+ OUT LPDWORD lpResourceUsage
+ );
+
+DWORD
+NwVerifyBinderyObject(
+ IN LPWSTR lpBinderyObjectNamePath,
+ OUT LPWSTR * lpFullBinderyObjectNamePath,
+ OUT LPDWORD lpClassType,
+ OUT LPDWORD lpResourceScope,
+ OUT LPDWORD lpResourceType,
+ OUT LPDWORD lpResourceDisplayType,
+ OUT LPDWORD lpResourceUsage
+ );
+
+DWORD
+NwGetNDSPathInfo(
+ IN LPWSTR lpNDSObjectNamePath,
+ OUT LPWSTR * lpSystemObjectNamePath,
+ OUT LPWSTR * lpSystemPathPart,
+ OUT LPDWORD lpClassType,
+ OUT LPDWORD lpResourceScope,
+ OUT LPDWORD lpResourceType,
+ OUT LPDWORD lpResourceDisplayType,
+ OUT LPDWORD lpResourceUsage
+ );
+
+DWORD
+NwGetBinderyPathInfo(
+ IN LPWSTR lpBinderyObjectNamePath,
+ OUT LPWSTR * lpSystemObjectNamePath,
+ OUT LPWSTR * lpSystemPathPart,
+ OUT LPDWORD lpClassType,
+ OUT LPDWORD lpResourceScope,
+ OUT LPDWORD lpResourceType,
+ OUT LPDWORD lpResourceDisplayType,
+ OUT LPDWORD lpResourceUsage
+ );
+
+BOOL
+NwGetRemoteNameParent(
+ IN LPWSTR lpRemoteName,
+ OUT LPWSTR * lpRemoteNameParent
+ );
+
+int _CRTAPI1
+SortFunc(
+ IN CONST VOID *p1,
+ IN CONST VOID *p2
+ );
+
+DWORD
+NwGetConnectionInformation(
+ IN LPWSTR lpName,
+ OUT LPWSTR lpUserName,
+ OUT LPWSTR lpHostServer
+ );
+
+
+VOID
+NwpGetUncInfo(
+ IN LPWSTR lpstrUnc,
+ OUT WORD * slashCount,
+ OUT BOOL * isNdsUnc,
+ OUT LPWSTR * FourthSlash
+ );
+
+DWORD
+NwpGetCurrentUserRegKey(
+ IN DWORD DesiredAccess,
+ OUT HKEY *phKeyCurrentUser
+ );
+
+DWORD
+NwQueryInfo(
+ OUT LPWSTR *ppszPreferredSrv
+ );
+
+
+DWORD
+NwrOpenEnumContextInfo(
+ IN LPWSTR Reserved OPTIONAL,
+ IN DWORD ConnectionType,
+ OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
+ )
+/*++
+
+Routine Description:
+
+ This function creates a new context handle and initializes it
+ for enumerating context information (i.e. NDS user context objects
+ and/or NetWare bindery server connections).
+
+Arguments:
+
+ Reserved - Unused.
+
+ EnumHandle - Receives the newly created context handle.
+
+Return Value:
+
+ ERROR_NOT_ENOUGH_MEMORY - if the memory for the context could
+ not be allocated.
+
+ NO_ERROR - Call was successful.
+
+--*/
+{
+ LPWSTR pszCurrentContext = NULL;
+ DWORD dwPrintOptions;
+ DWORD status = NwQueryInfo( &pszCurrentContext );
+ WCHAR Context[MAX_NDS_NAME_CHARS];
+ LPNW_ENUM_CONTEXT ContextHandle;
+
+ UNREFERENCED_PARAMETER(Reserved);
+ UNREFERENCED_PARAMETER(ConnectionType);
+
+
+#if DBG
+ IF_DEBUG(ENUM) {
+ KdPrint(("\nNWWORKSTATION: NwrOpenEnumContextInfo\n"));
+ }
+#endif
+
+ if ( status == NO_ERROR )
+ {
+ if ( pszCurrentContext[0] == TREECHAR )
+ {
+ wcscpy( Context, L"\\\\" );
+ wcscat( Context, pszCurrentContext + 1 );
+
+ LocalFree( pszCurrentContext );
+ pszCurrentContext = NULL;
+
+ return NwrOpenEnumCommon(
+ Context,
+ NwsHandleListContextInfo_Tree,
+ 0xFFFFFFFF,
+ FALSE,
+ NULL,
+ NULL,
+ 0,
+ 0,
+ NULL,
+ EnumHandle
+ );
+ }
+ else
+ {
+ //
+ // The user does not have a preferred NDS tree and context. They
+ // may have only a preferred server.
+ //
+ if ( pszCurrentContext[0] != 0 )
+ {
+ //
+ // There is a prefered server.
+ //
+ LocalFree( pszCurrentContext );
+ pszCurrentContext = NULL;
+
+ ContextHandle = (PVOID) LocalAlloc(
+ LMEM_ZEROINIT,
+ sizeof(NW_ENUM_CONTEXT)
+ );
+
+ if (ContextHandle == NULL)
+ {
+ KdPrint(("NWWORKSTATION: NwrOpenEnumContextInfo LocalAlloc Failed %lu\n", GetLastError()));
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ //
+ // Initialize contents of the context handle structure.
+ //
+ ContextHandle->Signature = NW_HANDLE_SIGNATURE;
+ ContextHandle->HandleType = NwsHandleListContextInfo_Server;
+ ContextHandle->dwUsingNds = CURRENTLY_ENUMERATING_NON_NDS;
+ ContextHandle->ResumeId = 0xFFFFFFFF;
+
+ // The following are set to zero due to the LMEM_ZEROINIT.
+ // ContextHandle->NdsRawDataBuffer = 0;
+ // ContextHandle->NdsRawDataSize = 0;
+ // ContextHandle->NdsRawDataId = 0;
+ // ContextHandle->NdsRawDataCount = 0;
+ // ContextHandle->TreeConnectionHandle = 0;
+ // ContextHandle->ConnectionType = 0;
+
+ //
+ // Return the newly created context.
+ //
+ *EnumHandle = (LPNWWKSTA_CONTEXT_HANDLE) ContextHandle;
+
+ return NO_ERROR;
+ }
+ }
+ }
+
+ //
+ // There is no information in the registry about the current user.
+ // We go ahead and make an enumeration handle and return success.
+ // Later, during a call to NPEnumResource, we will return zero items.
+ // This is done because there is no valid return code to tell the
+ // callee that we have no context information to provide.
+ //
+ ContextHandle = (PVOID) LocalAlloc( LMEM_ZEROINIT,
+ sizeof(NW_ENUM_CONTEXT) );
+
+ if (ContextHandle == NULL)
+ {
+ KdPrint(("NWWORKSTATION: NwrOpenEnumContextInfo LocalAlloc Failed %lu\n", GetLastError()));
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ //
+ // Initialize contents of the context handle structure.
+ //
+ ContextHandle->Signature = NW_HANDLE_SIGNATURE;
+ ContextHandle->HandleType = NwsHandleListContextInfo_Server;
+ ContextHandle->dwUsingNds = CURRENTLY_ENUMERATING_NON_NDS;
+ ContextHandle->ResumeId = 0; // This will tell NwrEnum to
+ // give up (i.e. we are done).
+
+ // The following are set to zero due to the LMEM_ZEROINIT.
+ // ContextHandle->NdsRawDataBuffer = 0;
+ // ContextHandle->NdsRawDataSize = 0;
+ // ContextHandle->NdsRawDataId = 0;
+ // ContextHandle->NdsRawDataCount = 0;
+ // ContextHandle->TreeConnectionHandle = 0;
+ // ContextHandle->ConnectionType = 0;
+
+ //
+ // Return the newly created context.
+ //
+ *EnumHandle = (LPNWWKSTA_CONTEXT_HANDLE) ContextHandle;
+
+ return NO_ERROR;
+}
+
+
+
+DWORD
+NwrOpenEnumServersAndNdsTrees(
+ IN LPWSTR Reserved OPTIONAL,
+ OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
+ )
+/*++
+
+Routine Description:
+
+ This function creates a new context handle and initializes it
+ for enumerating the servers and NDS trees on the network.
+
+Arguments:
+
+ Reserved - Unused.
+
+ EnumHandle - Receives the newly created context handle.
+
+Return Value:
+
+ ERROR_NOT_ENOUGH_MEMORY - if the memory for the context could
+ not be allocated.
+
+ NO_ERROR - Call was successful.
+
+--*/ // NwrOpenEnumServersAndNdsTrees
+{
+ UNREFERENCED_PARAMETER(Reserved);
+
+#if DBG
+ IF_DEBUG(ENUM) {
+ KdPrint( ("\nNWWORKSTATION: NwrOpenEnumServersAndNdsTrees\n") );
+ }
+#endif
+
+ return NwrOpenEnumServersCommon(
+ NwsHandleListServersAndNdsTrees,
+ EnumHandle
+ );
+}
+
+
+
+DWORD
+NwOpenEnumPrintServers(
+ OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
+ )
+/*++
+
+Routine Description:
+
+ This function creates a new context handle and initializes it
+ for enumerating the print servers on the network.
+
+Arguments:
+
+ Reserved - Unused.
+ EnumHandle - Receives the newly created context handle.
+
+Return Value:
+
+ ERROR_NOT_ENOUGH_MEMORY - if the memory for the context could
+ not be allocated.
+
+ NO_ERROR - Call was successful.
+
+--*/ // NwOpenEnumPrintServers
+{
+
+#if DBG
+ IF_DEBUG(ENUM) {
+ KdPrint( ("\nNWWORKSTATION: NwOpenEnumPrintServers\n") );
+ }
+#endif
+
+ return NwrOpenEnumServersCommon(
+ NwsHandleListPrintServers,
+ EnumHandle
+ );
+}
+
+
+DWORD
+NwrOpenEnumVolumes(
+ IN LPWSTR Reserved OPTIONAL,
+ IN LPWSTR ServerName,
+ OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
+ )
+/*++
+
+Routine Description:
+
+ This function calls a common routine which creates a new context
+ handle and initializes it for enumerating the volumes on a server.
+
+Arguments:
+
+ Reserved - Unused.
+
+ ServerName - Supplies the name of the server to enumerate volumes.
+ This name is prefixed by \\.
+
+ EnumHandle - Receives the newly created context handle.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/ // NwrOpenEnumVolumes
+{
+ UNREFERENCED_PARAMETER(Reserved);
+
+#if DBG
+ IF_DEBUG(ENUM) {
+ KdPrint(("\nNWWORKSTATION: NwrOpenEnumVolumes %ws\n",
+ ServerName));
+ }
+#endif
+
+ return NwrOpenEnumCommon(
+ ServerName,
+ NwsHandleListVolumes,
+ 0,
+ FALSE,
+ NULL,
+ NULL,
+ FILE_OPEN,
+ FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL,
+ EnumHandle
+ );
+}
+
+
+DWORD
+NwrOpenEnumNdsSubTrees_Disk(
+ IN LPWSTR Reserved OPTIONAL,
+ IN LPWSTR ParentPathName,
+ OUT LPDWORD ClassTypeOfNDSLeaf,
+ OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
+ )
+/*++
+
+Routine Description:
+
+ This function calls a common routine which creates a new context
+ handle and initializes it for enumerating the DISK object types
+ and containers of a sub-tree in a NDS tree.
+
+Arguments:
+
+ Reserved - Unused.
+
+ ParentPathName - Supplies the name of the tree and the path to a container
+ to enumerate sub-trees.
+
+ EnumHandle - Receives the newly created context handle.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/ // NwrOpenEnumNdsSubTrees_Disk
+{
+
+ UNREFERENCED_PARAMETER(Reserved);
+
+#if DBG
+ IF_DEBUG(ENUM) {
+ KdPrint(("\nNWWORKSTATION: NwrOpenEnumNdsSubTrees_Disk %ws\n",
+ ParentPathName));
+ }
+#endif
+
+ return NwrOpenEnumCommon(
+ ParentPathName,
+ NwsHandleListNdsSubTrees_Disk,
+ 0,
+ FALSE,
+ NULL,
+ NULL,
+ 0,
+ 0,
+ ClassTypeOfNDSLeaf,
+ EnumHandle
+ );
+}
+
+
+DWORD
+NwrOpenEnumNdsSubTrees_Print(
+ IN LPWSTR Reserved OPTIONAL,
+ IN LPWSTR ParentPathName,
+ OUT LPDWORD ClassTypeOfNDSLeaf,
+ OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
+ )
+/*++
+
+Routine Description:
+
+ This function calls a common routine which creates a new context
+ handle and initializes it for enumerating the PRINT object types
+ and containers of a sub-tree in a NDS tree.
+
+Arguments:
+
+ Reserved - Unused.
+
+ ParentPathName - Supplies the name of the tree and the path to a container
+ to enumerate sub-trees.
+
+ EnumHandle - Receives the newly created context handle.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/ // NwrOpenEnumNdsSubTrees_Print
+{
+#if DBG
+ IF_DEBUG(ENUM) {
+ KdPrint(("\nNWWORKSTATION: NwrOpenEnumNdsSubTrees_Print %ws\n",
+ ParentPathName));
+ }
+#endif
+
+ return NwrOpenEnumCommon(
+ ParentPathName,
+ NwsHandleListNdsSubTrees_Print,
+ 0,
+ FALSE,
+ NULL,
+ NULL,
+ 0,
+ 0,
+ ClassTypeOfNDSLeaf,
+ EnumHandle
+ );
+}
+
+
+DWORD
+NwrOpenEnumNdsSubTrees_Any(
+ IN LPWSTR Reserved OPTIONAL,
+ IN LPWSTR ParentPathName,
+ OUT LPDWORD ClassTypeOfNDSLeaf,
+ OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
+ )
+/*++
+
+Routine Description:
+
+ This function calls a common routine which creates a new context
+ handle and initializes it for enumerating the ANY object types
+ and containers of a sub-tree in a NDS tree.
+
+Arguments:
+
+ Reserved - Unused.
+
+ ParentPathName - Supplies the name of the tree and the path to a container
+ to enumerate sub-trees.
+
+ EnumHandle - Receives the newly created context handle.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/ // NwrOpenEnumNdsSubTrees_Any
+{
+
+ UNREFERENCED_PARAMETER(Reserved);
+
+#if DBG
+ IF_DEBUG(ENUM) {
+ KdPrint(("\nNWWORKSTATION: NwrOpenEnumNdsSubTrees_Any %ws\n",
+ ParentPathName));
+ }
+#endif
+
+ return NwrOpenEnumCommon(
+ ParentPathName,
+ NwsHandleListNdsSubTrees_Any,
+ 0,
+ FALSE,
+ NULL,
+ NULL,
+ 0,
+ 0,
+ ClassTypeOfNDSLeaf,
+ EnumHandle
+ );
+}
+
+
+DWORD
+NwrOpenEnumQueues(
+ IN LPWSTR Reserved OPTIONAL,
+ IN LPWSTR ServerName,
+ OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
+ )
+/*++
+
+Routine Description:
+
+ This function calls a common routine which creates a new context
+ handle and initializes it for enumerating the volumes on a server.
+
+Arguments:
+
+ Reserved - Unused.
+
+ ServerName - Supplies the name of the server to enumerate volumes.
+ This name is prefixed by \\.
+
+ EnumHandle - Receives the newly created context handle.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/ // NwrOpenEnumQueues
+{
+
+ UNREFERENCED_PARAMETER(Reserved);
+
+#if DBG
+ IF_DEBUG(ENUM) {
+ KdPrint(("\nNWWORKSTATION: NwrOpenEnumQueues %ws\n",
+ ServerName));
+ }
+#endif
+
+ return NwrOpenEnumCommon(
+ ServerName,
+ NwsHandleListQueues,
+ 0xFFFFFFFF,
+ TRUE,
+ NULL,
+ NULL,
+ FILE_OPEN,
+ FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL,
+ EnumHandle
+ );
+}
+
+
+DWORD
+NwrOpenEnumVolumesQueues(
+ IN LPWSTR Reserved OPTIONAL,
+ IN LPWSTR ServerName,
+ OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
+ )
+/*++
+
+Routine Description:
+
+ This function calls a common routine which creates a new context
+ handle and initializes it for enumerating the volumes/queues on a server.
+
+Arguments:
+
+ Reserved - Unused.
+
+ ServerName - Supplies the name of the server to enumerate volumes.
+ This name is prefixed by \\.
+
+ EnumHandle - Receives the newly created context handle.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/ // NwrOpenEnumVolumesQueues
+{
+
+ DWORD status;
+ UNREFERENCED_PARAMETER(Reserved);
+
+#if DBG
+ IF_DEBUG(ENUM) {
+ KdPrint(("\nNWWORKSTATION: NwrOpenEnumVolumesQueues %ws\n",
+ ServerName));
+ }
+#endif
+
+ status = NwrOpenEnumCommon(
+ ServerName,
+ NwsHandleListVolumesQueues,
+ 0,
+ FALSE,
+ NULL,
+ NULL,
+ FILE_OPEN,
+ FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL,
+ EnumHandle
+ );
+
+ if ( status == NO_ERROR )
+ ((LPNW_ENUM_CONTEXT) *EnumHandle)->ConnectionType = CONNTYPE_DISK;
+
+ return status;
+}
+
+
+DWORD
+NwrOpenEnumDirectories(
+ IN LPWSTR Reserved OPTIONAL,
+ IN LPWSTR ParentPathName,
+ IN LPWSTR UserName OPTIONAL,
+ IN LPWSTR Password OPTIONAL,
+ OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
+ )
+/*++
+
+Routine Description:
+
+ This function calls a common routine which creates a new context
+ handle and initializes it for enumerating the volumes on a server.
+
+Arguments:
+
+ Reserved - Unused.
+
+ ParentPathName - Supplies the parent path name in the format of
+ \\Server\Volume.
+
+ UserName - Supplies the username to connect with.
+
+ Password - Supplies the password to connect with.
+
+ EnumHandle - Receives the newly created context handle.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/ //NwrOpenEnumDirectories
+{
+ UNREFERENCED_PARAMETER(Reserved);
+
+#if DBG
+ IF_DEBUG(ENUM) {
+ KdPrint(("\nNWWORKSTATION: NwrOpenEnumDirectories %ws\n",
+ ParentPathName));
+ }
+#endif
+
+ return NwrOpenEnumCommon(
+ ParentPathName,
+ NwsHandleListDirectories,
+ 0,
+ FALSE,
+ UserName,
+ Password,
+ FILE_CREATE,
+ FILE_CREATE_TREE_CONNECTION |
+ FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL,
+ EnumHandle
+ );
+}
+
+
+DWORD
+NwOpenEnumPrintQueues(
+ IN LPWSTR ServerName,
+ OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
+ )
+/*++
+
+Routine Description:
+
+ This function calls a common routine which creates a new context
+ handle and initializes it for enumerating the print queues on a server.
+
+Arguments:
+
+ Reserved - Unused.
+
+ ServerName - Supplies the name of the server to enumerate volumes.
+ This name is prefixed by \\.
+
+ EnumHandle - Receives the newly created context handle.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/ // NwOpenEnumPrintQueues
+{
+
+#if DBG
+ IF_DEBUG(ENUM) {
+ KdPrint(("\nNWWORKSTATION: NwOpenEnumPrintQueues %ws\n",
+ ServerName));
+ }
+#endif
+
+ return NwrOpenEnumCommon(
+ ServerName,
+ NwsHandleListPrintQueues,
+ 0xFFFFFFFF,
+ TRUE,
+ NULL,
+ NULL,
+ FILE_OPEN,
+ FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL,
+ EnumHandle
+ );
+}
+
+
+DWORD
+NwrOpenEnumServersCommon(
+ IN NW_ENUM_TYPE EnumType,
+ OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
+ )
+/*++
+
+Routine Description:
+
+ This function creates a new context handle and initializes it
+ for enumerating the servers on the network.
+
+Arguments:
+
+ EnumType - Supplies the type of the object we want to enumerate
+
+ EnumHandle - Receives the newly created context handle.
+
+Return Value:
+
+ ERROR_NOT_ENOUGH_MEMORY - if the memory for the context could
+ not be allocated.
+
+ NO_ERROR - Call was successful.
+
+--*/ // NwrOpenEnumServersCommon
+{
+ DWORD status = NO_ERROR;
+ LPNW_ENUM_CONTEXT ContextHandle = NULL;
+
+ //
+ // Allocate memory for the context handle structure.
+ //
+ ContextHandle = (PVOID) LocalAlloc(
+ LMEM_ZEROINIT,
+ sizeof(NW_ENUM_CONTEXT)
+ );
+
+ if (ContextHandle == NULL) {
+ KdPrint((
+ "NWWORKSTATION: NwrOpenEnumServersCommon LocalAlloc Failed %lu\n",
+ GetLastError()));
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ //
+ // Initialize contents of the context handle structure.
+ //
+ ContextHandle->Signature = NW_HANDLE_SIGNATURE;
+ ContextHandle->HandleType = EnumType;
+ ContextHandle->ResumeId = 0xFFFFFFFF;
+ ContextHandle->NdsRawDataBuffer = 0x00000000;
+ ContextHandle->NdsRawDataSize = 0x00000000;
+ ContextHandle->NdsRawDataId = 0x00000000;
+ ContextHandle->NdsRawDataCount = 0x00000000;
+
+ //
+ // Set flag to indicate that we are going to enumerate NDS trees first.
+ //
+ ContextHandle->dwUsingNds = CURRENTLY_ENUMERATING_NDS;
+
+ //
+ // Impersonate the client
+ //
+ if ((status = NwImpersonateClient()) != NO_ERROR)
+ {
+ goto CleanExit;
+ }
+
+ //
+ // We enum servers and nds trees from the preferred server.
+ //
+ status = NwOpenPreferredServer(
+ &ContextHandle->TreeConnectionHandle
+ );
+
+ (void) NwRevertToSelf() ;
+
+ if (status == NO_ERROR)
+ {
+ //
+ // Return the newly created context.
+ //
+ *EnumHandle = (LPNWWKSTA_CONTEXT_HANDLE) ContextHandle;
+
+ return status;
+ }
+
+CleanExit:
+ if ( ContextHandle )
+ {
+ ContextHandle->Signature = 0x0BADBAD0;
+
+ (void) LocalFree((HLOCAL) ContextHandle);
+ }
+
+ return status;
+}
+
+
+DWORD
+NwrOpenEnumCommon(
+ IN LPWSTR ContainerName,
+ IN NW_ENUM_TYPE EnumType,
+ IN DWORD StartingPoint,
+ IN BOOL ValidateUserFlag,
+ IN LPWSTR UserName OPTIONAL,
+ IN LPWSTR Password OPTIONAL,
+ IN ULONG CreateDisposition,
+ IN ULONG CreateOptions,
+ OUT LPDWORD ClassTypeOfNDSLeaf,
+ OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
+ )
+/*++
+
+Routine Description:
+
+ This function is common code for creating a new context handle
+ and initializing it for enumerating either volumes, directories,
+ or NDS subtrees.
+
+Arguments:
+
+ ContainerName - Supplies the full path name to the container object
+ we are enumerating from.
+
+ EnumType - Supplies the type of the object we want to enumerate
+
+ StartingPoint - Supplies the initial resume ID.
+
+ UserName - Supplies the username to connect with.
+
+ Password - Supplies the password to connect with.
+
+ EnumHandle - Receives the newly created context handle.
+
+Return Value:
+
+ ERROR_NOT_ENOUGH_MEMORY - if the memory for the context could
+ not be allocated.
+
+ NO_ERROR - Call was successful.
+
+ Other errors from failure to open a handle to the server.
+
+--*/ // NwrOpenEnumCommon
+{
+ DWORD status = NO_ERROR;
+ NTSTATUS ntstatus = STATUS_SUCCESS;
+ LPNW_ENUM_CONTEXT ContextHandle = NULL;
+ LPWSTR StrippedContainerName = NULL;
+ BOOL fImpersonate = FALSE ;
+
+ if ( ClassTypeOfNDSLeaf )
+ *ClassTypeOfNDSLeaf = 0;
+
+ //
+ // Before we do anything, we need to convert the UNC passed to
+ // us. We need to get rid of any CN=XXX.OU=YYY.O=ZZZ references, and
+ // convert them to XXX.YYY.ZZZ format. Any NETRESOURCE that we generate
+ // will look like \\TREE\XXX.YYY.ZZZ for a NDS Unc. We do this to
+ // work around to a bug in WOW.EXE, that prevents 16 bit apps from
+ // being launched when the user types NDS paths with the CN= stuff in it.
+ //
+ NwStripNdsUncName( ContainerName, &StrippedContainerName );
+
+ if ( StrippedContainerName == NULL )
+ {
+ KdPrint(("NWWORKSTATION: NwrOpenEnumCommon LocalAlloc Failed %lu\n",
+ GetLastError()));
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ //
+ // Allocate memory for the context handle structure and space for
+ // the ContainerName plus \. Now need one more for NULL terminator
+ // because it's already included in the structure.
+ //
+ ContextHandle = (PVOID) LocalAlloc(
+ LMEM_ZEROINIT,
+ sizeof(NW_ENUM_CONTEXT) +
+ (wcslen(StrippedContainerName) + 1) * sizeof(WCHAR)
+ );
+
+ if (ContextHandle == NULL)
+ {
+ if ( StrippedContainerName )
+ {
+ (void) LocalFree((HLOCAL) StrippedContainerName);
+ StrippedContainerName = NULL;
+ }
+
+ KdPrint(("NWWORKSTATION: NwrOpenEnumCommon LocalAlloc Failed %lu\n",
+ GetLastError()));
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ //
+ // Initialize contents of the context handle structure.
+ //
+ ContextHandle->Signature = NW_HANDLE_SIGNATURE;
+ ContextHandle->HandleType = EnumType;
+ ContextHandle->ResumeId = StartingPoint;
+
+ //
+ // These are set to zero due to LMEM_ZEROINIT.
+ //
+ // ContextHandle->NdsRawDataBuffer = 0;
+ // ContextHandle->NdsRawDataSize = 0;
+ // ContextHandle->NdsRawDataId = 0;
+ // ContextHandle->NdsRawDataCount = 0;
+ // ContextHandle->TreeConnectionHandle = 0;
+
+ //
+ // Impersonate the client
+ //
+ if ( ( status = NwImpersonateClient() ) != NO_ERROR )
+ {
+ goto ErrorExit;
+ }
+
+ fImpersonate = TRUE;
+
+ if ( EnumType == NwsHandleListNdsSubTrees_Disk ||
+ EnumType == NwsHandleListNdsSubTrees_Print ||
+ EnumType == NwsHandleListNdsSubTrees_Any ||
+ EnumType == NwsHandleListContextInfo_Tree )
+ {
+ WCHAR lpServerName[NW_MAX_SERVER_LEN];
+ UNICODE_STRING ServerName;
+ UNICODE_STRING ObjectName;
+
+ ServerName.Length = 0;
+ ServerName.MaximumLength = sizeof( lpServerName );
+ ServerName.Buffer = lpServerName;
+
+ ObjectName.Buffer = NULL;
+
+ if ( EnumType == NwsHandleListContextInfo_Tree )
+ {
+ ContextHandle->dwUsingNds = CURRENTLY_ENUMERATING_NON_NDS;
+ }
+ else
+ {
+ ContextHandle->dwUsingNds = CURRENTLY_ENUMERATING_NDS;
+ }
+
+ ObjectName.MaximumLength = ( wcslen( StrippedContainerName ) + 1 ) *
+ sizeof( WCHAR );
+
+ ObjectName.Length = NwParseNdsUncPath( (LPWSTR *) &ObjectName.Buffer,
+ StrippedContainerName,
+ PARSE_NDS_GET_TREE_NAME );
+
+ if ( ObjectName.Length == 0 || ObjectName.Buffer == NULL )
+ {
+ status = ERROR_PATH_NOT_FOUND;
+ goto ErrorExit;
+ }
+
+ //
+ // Open a NDS tree connection handle to \\treename
+ //
+ ntstatus = NwNdsOpenTreeHandle( &ObjectName,
+ &ContextHandle->TreeConnectionHandle );
+
+ if ( ntstatus != STATUS_SUCCESS )
+ {
+ status = RtlNtStatusToDosError(ntstatus);
+ goto ErrorExit;
+ }
+
+
+ //
+ // Get the path to the container to open.
+ //
+ ObjectName.Length = NwParseNdsUncPath( (LPWSTR *) &ObjectName.Buffer,
+ StrippedContainerName,
+ PARSE_NDS_GET_PATH_NAME
+ );
+
+ if ( ObjectName.Length == 0 )
+ {
+ UNICODE_STRING Root;
+
+ RtlInitUnicodeString(&Root, L"[Root]");
+
+ //
+ // Resolve the path to get a NDS object id of [Root].
+ //
+ ntstatus = NwNdsResolveName( ContextHandle->TreeConnectionHandle,
+ &Root,
+ &ContextHandle->dwOid,
+ &ServerName,
+ NULL,
+ 0 );
+
+ if ( ntstatus != STATUS_SUCCESS )
+ {
+ status = RtlNtStatusToDosError(ntstatus);
+ goto ErrorExit;
+ }
+
+ wcscpy(ContextHandle->ContainerName, StrippedContainerName);
+ }
+ else
+ {
+ //
+ // Resolve the path to get a NDS object id.
+ //
+ ntstatus = NwNdsResolveName( ContextHandle->TreeConnectionHandle,
+ &ObjectName,
+ &ContextHandle->dwOid,
+ &ServerName,
+ NULL,
+ 0 );
+
+ if ( ntstatus != STATUS_SUCCESS )
+ {
+ status = RtlNtStatusToDosError(ntstatus);
+ goto ErrorExit;
+ }
+
+ wcscpy(ContextHandle->ContainerName, StrippedContainerName);
+ }
+
+ if ( ServerName.Length )
+ {
+ DWORD dwHandleType;
+
+ //
+ // NwNdsResolveName succeeded, but we were referred to
+ // another server, though ContextHandle->dwOid is still valid.
+
+ if ( ContextHandle->TreeConnectionHandle )
+ CloseHandle( ContextHandle->TreeConnectionHandle );
+
+ ContextHandle->TreeConnectionHandle = 0;
+
+ //
+ // Open a NDS generic connection handle to \\ServerName
+ //
+ ntstatus = NwNdsOpenGenericHandle( &ServerName,
+ &dwHandleType,
+ &ContextHandle->TreeConnectionHandle );
+
+ if ( ntstatus != STATUS_SUCCESS )
+ {
+ status = RtlNtStatusToDosError(ntstatus);
+ goto ErrorExit;
+ }
+
+ ASSERT( dwHandleType == HANDLE_TYPE_NCP_SERVER );
+ }
+
+ //
+ // Check to see if object is either a Server, Directory Map, or Volume.
+ // If so, the object is a known leaf in terms of NDS, and therefore cannot
+ // be enumerated through NwNdsList API calls. We fail the OpenEnum call in these
+ // cases and pass back the type of object the leaf node was. This way the code in
+ // NWPROVAU!NPOpenEnum can call NwrOpenEnumServer, NwrOpenEnumVolume, or
+ // NwrOpenEnumDirectories accordingly.
+ //
+ {
+ BYTE RawResponse[TWO_KB];
+ DWORD RawResponseSize = sizeof(RawResponse);
+ DWORD dwStrLen;
+ PBYTE pbRawGetInfo;
+
+ ntstatus = NwNdsReadObjectInfo( ContextHandle->TreeConnectionHandle,
+ ContextHandle->dwOid,
+ RawResponse,
+ RawResponseSize );
+
+ if ( ntstatus != NO_ERROR )
+ {
+ status = RtlNtStatusToDosError(ntstatus);
+ goto ErrorExit;
+ }
+
+ (void) NwRevertToSelf() ;
+ fImpersonate = FALSE;
+
+ pbRawGetInfo = RawResponse;
+
+ //
+ // The structure of a NDS_RESPONSE_GET_OBJECT_INFO consists of 4 DWORDs
+ // followed by two standard NDS format UNICODE strings. Below we jump pbRawGetInfo
+ // into the buffer, past the 4 DWORDs.
+ //
+ pbRawGetInfo += sizeof ( NDS_RESPONSE_GET_OBJECT_INFO );
+
+ //
+ // Now we get the length of the first string (Base Class).
+ //
+ dwStrLen = * ( DWORD * ) pbRawGetInfo;
+
+ //
+ // Now we point pbRawGetInfo to the first WCHAR of the first string (Base Class).
+ //
+ pbRawGetInfo += sizeof( DWORD );
+
+ //
+ // If the object is either a NCP Server, Volume, or a Directory Map, we fail
+ // the OpenEnum call and return the class type of the NDS leaf object. We do
+ // this because we cannot enumerate through NwNdsList() calls any subordinates,
+ // all browsing below these types are done through system redirector calls. So
+ // the client side of the provider will instead call NwOpenEnumVolumes or
+ // NwOpenEnumDirectories, respectively.
+ //
+ if ( !wcscmp( (LPWSTR) pbRawGetInfo, L"NCP Server" ) )
+ {
+ if ( ClassTypeOfNDSLeaf )
+ *ClassTypeOfNDSLeaf = CLASS_TYPE_NCP_SERVER;
+ status = ERROR_NETWORK_ACCESS_DENIED;
+ goto ErrorExit;
+ }
+
+ if ( !wcscmp( (LPWSTR) pbRawGetInfo, L"Volume" ) )
+ {
+ if ( ClassTypeOfNDSLeaf )
+ *ClassTypeOfNDSLeaf = CLASS_TYPE_VOLUME;
+ status = ERROR_NETWORK_ACCESS_DENIED;
+ goto ErrorExit;
+ }
+
+ if ( !wcscmp( (LPWSTR) pbRawGetInfo, L"Directory Map" ) )
+ {
+ if ( ClassTypeOfNDSLeaf )
+ *ClassTypeOfNDSLeaf = CLASS_TYPE_DIRECTORY_MAP;
+ status = ERROR_NETWORK_ACCESS_DENIED;
+ goto ErrorExit;
+ }
+ } // End of block
+ }
+ else // EnumType is something other than a NDS Sub-tree
+ {
+ UNICODE_STRING TreeConnectStr;
+
+ TreeConnectStr.Buffer = NULL;
+ ContextHandle->dwUsingNds = CURRENTLY_ENUMERATING_NON_NDS;
+
+ wcscpy(ContextHandle->ContainerName, StrippedContainerName);
+ wcscat(ContextHandle->ContainerName, L"\\");
+
+ //
+ // Open a tree connection handle to \Device\NwRdr\ContainerName
+ //
+ status = NwCreateTreeConnectName( StrippedContainerName,
+ NULL,
+ &TreeConnectStr );
+
+ if ( status != NO_ERROR )
+ {
+ goto ErrorExit;
+ }
+
+ status = NwOpenCreateConnection( &TreeConnectStr,
+ UserName,
+ Password,
+ StrippedContainerName,
+ FILE_LIST_DIRECTORY | SYNCHRONIZE |
+ ( ValidateUserFlag? FILE_WRITE_DATA : 0 ),
+ CreateDisposition,
+ CreateOptions,
+ RESOURCETYPE_DISK, // When connecting beyond servername
+ &ContextHandle->TreeConnectionHandle,
+ NULL );
+
+ (void) LocalFree((HLOCAL) TreeConnectStr.Buffer);
+ }
+
+ if (status == NO_ERROR)
+ {
+ VERSION_INFO vInfo;
+
+ if ( EnumType == NwsHandleListVolumes ||
+ EnumType == NwsHandleListVolumesQueues )
+ {
+ NWC_SERVER_INFO ServerInfo;
+
+ ServerInfo.hConn = ContextHandle->TreeConnectionHandle;
+ ServerInfo.ServerString.Length = 0;
+ ServerInfo.ServerString.MaximumLength = 0;
+ ServerInfo.ServerString.Buffer = NULL;
+
+ status = NWGetFileServerVersionInfo( (HANDLE) &ServerInfo,
+ &vInfo );
+
+ if ( status )
+ {
+ ContextHandle->dwMaxVolumes = NW_MAX_VOLUME_NUMBER;
+ status = NO_ERROR;
+ }
+ else
+ {
+ ContextHandle->dwMaxVolumes = (DWORD) vInfo.maxVolumes;
+
+ if ( ContextHandle->dwMaxVolumes == 0 )
+ {
+ ContextHandle->dwMaxVolumes = NW_MAX_VOLUME_NUMBER;
+ }
+ }
+ }
+
+ (void) NwRevertToSelf() ;
+ fImpersonate = FALSE;
+
+ if ( StrippedContainerName )
+ {
+ (void) LocalFree((HLOCAL) StrippedContainerName);
+ StrippedContainerName = NULL;
+ }
+
+ //
+ // Return the newly created context.
+ //
+ *EnumHandle = (LPNWWKSTA_CONTEXT_HANDLE) ContextHandle;
+
+ return status;
+ }
+
+ErrorExit:
+
+ if ( fImpersonate )
+ (void) NwRevertToSelf() ;
+
+ if ( StrippedContainerName )
+ {
+ (void) LocalFree((HLOCAL) StrippedContainerName);
+ }
+
+ if ( ContextHandle )
+ {
+ if ( ContextHandle->TreeConnectionHandle )
+ CloseHandle( ContextHandle->TreeConnectionHandle );
+
+ ContextHandle->Signature = 0x0BADBAD0;
+
+ (void) LocalFree((HLOCAL) ContextHandle);
+ }
+
+ *EnumHandle = NULL;
+
+ if (status == ERROR_NOT_CONNECTED)
+ {
+ //
+ // Object name not found. We should return path not found.
+ //
+ status = ERROR_PATH_NOT_FOUND;
+ }
+
+ return status;
+}
+
+
+DWORD
+NwrEnum(
+ IN NWWKSTA_CONTEXT_HANDLE EnumHandle,
+ IN DWORD EntriesRequested,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT LPDWORD BytesNeeded,
+ OUT LPDWORD EntriesRead
+ )
+/*++
+
+Routine Description:
+
+ This function
+
+Arguments:
+
+ EnumHandle - Supplies a pointer to the context handle which identifies
+ what type of object we are enumerating and the string of the
+ container name to concatenate to the returned object.
+
+ EntriesRequested - Supplies the number of entries to return. If
+ this value is 0xffffffff, return all available entries.
+
+ Buffer - Receives the entries we are listing.
+
+ BufferSize - Supplies the size of the output buffer.
+
+ BytesNeeded - Receives the number of bytes required to get the
+ first entry. This value is returned iff WN_MORE_DATA is
+ the return code, and Buffer is too small to even fit one
+ entry.
+
+ EntriesRead - Receives the number of entries returned in Buffer.
+ This value is only returned iff NO_ERROR is the return code.
+ NO_ERROR is returned as long as at least one entry was written
+ into Buffer but does not necessarily mean that it's the number
+ of EntriesRequested.
+
+Return Value:
+
+ NO_ERROR - At least one entry was written to output buffer,
+ irregardless of the number requested.
+
+ WN_NO_MORE_ENTRIES - No entries left to return.
+
+ WN_MORE_DATA - The buffer was too small to fit a single entry.
+
+ WN_BAD_HANDLE - The specified enumeration handle is invalid.
+
+--*/ // NwrEnum
+{
+ DWORD status;
+ LPNW_ENUM_CONTEXT ContextHandle = (LPNW_ENUM_CONTEXT) EnumHandle;
+ BOOL fImpersonate = FALSE ;
+
+ if (ContextHandle->Signature != NW_HANDLE_SIGNATURE) {
+ return WN_BAD_HANDLE;
+ }
+
+ //
+ // Impersonate the client
+ //
+ if ((status = NwImpersonateClient()) != NO_ERROR)
+ {
+ goto CleanExit;
+ }
+ fImpersonate = TRUE ;
+
+ *EntriesRead = 0;
+ *BytesNeeded = 0;
+
+ RtlZeroMemory(Buffer, BufferSize);
+
+ switch (ContextHandle->HandleType) {
+ case NwsHandleListConnections:
+ {
+ if (!(ContextHandle->ConnectionType & CONNTYPE_SYMBOLIC))
+ {
+ status = NwEnumerateConnections(
+ &ContextHandle->ResumeId,
+ EntriesRequested,
+ Buffer,
+ BufferSize,
+ BytesNeeded,
+ EntriesRead,
+ ContextHandle->ConnectionType
+ );
+ if (status != ERROR_NO_MORE_ITEMS)
+ break;
+ else
+ {
+ //
+ // finished with all redir connections. look for
+ // symbolic ones. we got NO MORE ITEMS back, so we just
+ // carry one with the next set with the same buffers.
+ //
+ ContextHandle->ConnectionType |= CONNTYPE_SYMBOLIC ;
+ ContextHandle->ResumeId = 0 ;
+ }
+ }
+
+ if (ContextHandle->ConnectionType & CONNTYPE_SYMBOLIC)
+ {
+ //
+ // BUGBUG - This works around a weirdness in
+ // QueryDosDevices called by NwrEnumGWDevices.
+ // While impersonating the Win32 API will just fail.
+ //
+ (void) NwRevertToSelf() ;
+ fImpersonate = FALSE ;
+
+ status = NwrEnumGWDevices(
+ NULL,
+ &ContextHandle->ResumeId,
+ Buffer,
+ BufferSize,
+ BytesNeeded,
+ EntriesRead) ;
+
+ //
+ // if we have more items, MPR expects success. map
+ // accordingly.
+ //
+ if ((status == ERROR_MORE_DATA) && *EntriesRead)
+ {
+ status = NO_ERROR ;
+ }
+
+ //
+ // if nothing left, map to the distinguished MPR error
+ //
+ else if ((status == NO_ERROR) && (*EntriesRead == 0))
+ {
+ status = ERROR_NO_MORE_ITEMS ;
+ }
+ break ;
+ }
+ }
+
+ case NwsHandleListContextInfo_Tree:
+ case NwsHandleListContextInfo_Server:
+
+ status = NwEnumContextInfo(
+ ContextHandle,
+ EntriesRequested,
+ Buffer,
+ BufferSize,
+ BytesNeeded,
+ EntriesRead
+ );
+ break;
+
+ case NwsHandleListServersAndNdsTrees:
+
+ status = NwEnumServersAndNdsTrees(
+ ContextHandle,
+ EntriesRequested,
+ Buffer,
+ BufferSize,
+ BytesNeeded,
+ EntriesRead
+ );
+ break;
+
+ case NwsHandleListVolumes:
+
+ status = NwEnumVolumes(
+ ContextHandle,
+ EntriesRequested,
+ Buffer,
+ BufferSize,
+ BytesNeeded,
+ EntriesRead
+ );
+ break;
+
+ case NwsHandleListNdsSubTrees_Disk:
+
+ status = NwEnumNdsSubTrees_Disk(
+ ContextHandle,
+ EntriesRequested,
+ Buffer,
+ BufferSize,
+ BytesNeeded,
+ EntriesRead
+ );
+
+ break;
+
+ case NwsHandleListNdsSubTrees_Print:
+
+ status = NwEnumNdsSubTrees_Print(
+ ContextHandle,
+ EntriesRequested,
+ Buffer,
+ BufferSize,
+ BytesNeeded,
+ EntriesRead
+ );
+
+ break;
+
+ case NwsHandleListNdsSubTrees_Any:
+
+ status = NwEnumNdsSubTrees_Any(
+ ContextHandle,
+ EntriesRequested,
+ Buffer,
+ BufferSize,
+ BytesNeeded,
+ EntriesRead
+ );
+
+ break;
+
+ case NwsHandleListQueues:
+
+ status = NwEnumQueues(
+ ContextHandle,
+ EntriesRequested,
+ Buffer,
+ BufferSize,
+ BytesNeeded,
+ EntriesRead
+ );
+ break;
+
+ case NwsHandleListVolumesQueues:
+
+ status = NwEnumVolumesQueues(
+ ContextHandle,
+ EntriesRequested,
+ Buffer,
+ BufferSize,
+ BytesNeeded,
+ EntriesRead
+ );
+ break;
+
+ case NwsHandleListDirectories:
+
+ status = NwEnumDirectories(
+ ContextHandle,
+ EntriesRequested,
+ Buffer,
+ BufferSize,
+ BytesNeeded,
+ EntriesRead
+ );
+
+ break;
+
+ case NwsHandleListPrintServers:
+
+ status = NwEnumPrintServers(
+ ContextHandle,
+ EntriesRequested,
+ Buffer,
+ BufferSize,
+ BytesNeeded,
+ EntriesRead
+ );
+ break;
+
+ case NwsHandleListPrintQueues:
+
+ status = NwEnumPrintQueues(
+ ContextHandle,
+ EntriesRequested,
+ Buffer,
+ BufferSize,
+ BytesNeeded,
+ EntriesRead
+ );
+ break;
+
+ default:
+ KdPrint(("NWWORKSTATION: NwrEnum unexpected handle type %lu\n",
+ ContextHandle->HandleType));
+ ASSERT(FALSE);
+ status = WN_BAD_HANDLE;
+ goto CleanExit ;
+ }
+
+ if (*EntriesRead > 0) {
+
+ switch ( ContextHandle->HandleType ) {
+ case NwsHandleListConnections:
+ case NwsHandleListContextInfo_Tree:
+ case NwsHandleListContextInfo_Server:
+ case NwsHandleListServersAndNdsTrees:
+ case NwsHandleListVolumes:
+ case NwsHandleListQueues:
+ case NwsHandleListVolumesQueues:
+ case NwsHandleListDirectories:
+ case NwsHandleListNdsSubTrees_Disk:
+ case NwsHandleListNdsSubTrees_Any:
+ {
+ DWORD i;
+ LPNETRESOURCEW NetR = (LPNETRESOURCEW) Buffer;
+
+ //
+ // Replace pointers to strings with offsets as need
+ //
+
+ if ((ContextHandle->HandleType == NwsHandleListConnections)
+ && (ContextHandle->ConnectionType & CONNTYPE_SYMBOLIC))
+ {
+ //
+ // NwrEnumGWDevices already return offsets.
+ //
+ break ;
+ }
+
+ for (i = 0; i < *EntriesRead; i++, NetR++) {
+
+ if (NetR->lpLocalName != NULL) {
+ NetR->lpLocalName = (LPWSTR)
+ ((DWORD) (NetR->lpLocalName) - (DWORD) Buffer);
+ }
+
+ NetR->lpRemoteName =
+ (LPWSTR) ((DWORD) (NetR->lpRemoteName) - (DWORD)Buffer);
+
+ if (NetR->lpComment != NULL) {
+ NetR->lpComment = (LPWSTR) ((DWORD) (NetR->lpComment) -
+ (DWORD) Buffer);
+ }
+
+ if (NetR->lpProvider != NULL) {
+ NetR->lpProvider =
+ (LPWSTR) ((DWORD) (NetR->lpProvider) -
+ (DWORD) Buffer);
+ }
+ }
+ break;
+ }
+
+ case NwsHandleListPrintServers:
+ case NwsHandleListPrintQueues:
+ case NwsHandleListNdsSubTrees_Print:
+ {
+ DWORD i;
+ PRINTER_INFO_1W *pPrinterInfo1 = (PRINTER_INFO_1W *) Buffer;
+
+ //
+ // Sort the entries in the buffer
+ //
+ if ( *EntriesRead > 1 )
+ qsort( Buffer, *EntriesRead,
+ sizeof( PRINTER_INFO_1W ), SortFunc );
+
+ //
+ // Replace pointers to strings with offsets
+ //
+ for (i = 0; i < *EntriesRead; i++, pPrinterInfo1++) {
+
+ MarshallDownStructure( (LPBYTE) pPrinterInfo1,
+ PrinterInfo1Offsets,
+ Buffer );
+ }
+ break;
+ }
+
+ default:
+ KdPrint(("NWWORKSTATION: NwrEnum (pointer to offset code) unexpected handle type %lu\n", ContextHandle->HandleType));
+ ASSERT( FALSE );
+ break;
+ }
+ }
+
+CleanExit:
+
+ if (fImpersonate)
+ (void) NwRevertToSelf() ;
+
+ return status;
+}
+
+
+DWORD
+NwrEnumConnections(
+ IN NWWKSTA_CONTEXT_HANDLE EnumHandle,
+ IN DWORD EntriesRequested,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT LPDWORD BytesNeeded,
+ OUT LPDWORD EntriesRead,
+ IN DWORD fImplicitConnections
+ )
+/*++
+
+Routine Description:
+
+ This function is an alternate to NwrEnum. It only accepts handles
+ that are opened with ListConnections. This function takes a flag
+ indicating whether we need to show all implicit connections or not.
+
+Arguments:
+
+ ContextHandle - Supplies the enum context handle.
+
+ EntriesRequested - Supplies the number of entries to return. If
+ this value is 0xffffffff, return all available entries.
+
+ Buffer - Receives the entries we are listing.
+
+ BufferSize - Supplies the size of the output buffer.
+
+ BytesNeeded - Receives the number of bytes required to get the
+ first entry. This value is returned iff ERROR_MORE_DATA is
+ the return code, and Buffer is too small to even fit one
+ entry.
+
+ EntriesRead - Receives the number of entries returned in Buffer.
+ This value is only returned iff NO_ERROR is the return code.
+ NO_ERROR is returned as long as at least one entry was written
+ into Buffer but does not necessarily mean that it's the number
+ of EntriesRequested.
+
+ fImplicitConnections - TRUE if we also want to get implicit connections,
+ FALSE otherwise.
+
+Return Value:
+
+ NO_ERROR - At least one entry was written to output buffer,
+ irregardless of the number requested.
+
+ WN_NO_MORE_ENTRIES - No entries left to return.
+
+ ERROR_MORE_DATA - The buffer was too small to fit a single entry.
+
+--*/ // NwrEnumConnections
+{
+ DWORD status;
+ LPNW_ENUM_CONTEXT ContextHandle = (LPNW_ENUM_CONTEXT) EnumHandle;
+
+ if ( (ContextHandle->Signature != NW_HANDLE_SIGNATURE)
+ || ( ContextHandle->HandleType != NwsHandleListConnections )
+ )
+ {
+ return WN_BAD_HANDLE;
+ }
+
+ *EntriesRead = 0;
+ *BytesNeeded = 0;
+
+ RtlZeroMemory(Buffer, BufferSize);
+
+ if ( fImplicitConnections )
+ ContextHandle->ConnectionType |= CONNTYPE_IMPLICIT;
+
+ status = NwEnumerateConnections(
+ &ContextHandle->ResumeId,
+ EntriesRequested,
+ Buffer,
+ BufferSize,
+ BytesNeeded,
+ EntriesRead,
+ ContextHandle->ConnectionType
+ );
+
+ if (*EntriesRead > 0) {
+
+ //
+ // Replace pointers to strings with offsets
+ //
+
+ DWORD i;
+ LPNETRESOURCEW NetR = (LPNETRESOURCEW) Buffer;
+
+ for (i = 0; i < *EntriesRead; i++, NetR++) {
+
+ if (NetR->lpLocalName != NULL) {
+ NetR->lpLocalName = (LPWSTR)
+ ((DWORD) (NetR->lpLocalName) - (DWORD) Buffer);
+ }
+
+ NetR->lpRemoteName =
+ (LPWSTR) ((DWORD) (NetR->lpRemoteName) - (DWORD)Buffer);
+
+ if (NetR->lpComment != NULL) {
+ NetR->lpComment = (LPWSTR) ((DWORD) (NetR->lpComment) -
+ (DWORD) Buffer);
+ }
+
+ if (NetR->lpProvider != NULL) {
+ NetR->lpProvider = (LPWSTR) ((DWORD) (NetR->lpProvider) -
+ (DWORD) Buffer);
+ }
+ }
+ }
+
+ return status;
+}
+
+
+DWORD
+NwEnumContextInfo(
+ IN LPNW_ENUM_CONTEXT ContextHandle,
+ IN DWORD EntriesRequested,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT LPDWORD BytesNeeded,
+ OUT LPDWORD EntriesRead
+ )
+/*++
+
+Routine Description:
+
+ This function enumerates all of the bindery servers that are currently
+ connected, then sets the context handle so that the next NPEnumResource
+ call goes to the NDS subtree for the user's NDS context information
+ (if using NDS).
+
+Arguments:
+
+ ContextHandle - Supplies the enum context handle.
+
+ EntriesRequested - Supplies the number of entries to return. If
+ this value is 0xffffffff, return all available entries.
+
+ Buffer - Receives the entries we are listing.
+
+ BufferSize - Supplies the size of the output buffer.
+
+ BytesNeeded - Receives the number of bytes required to get the
+ first entry. This value is returned iff WN_MORE_DATA is
+ the return code, and Buffer is too small to even fit one
+ entry.
+
+ EntriesRead - Receives the number of entries returned in Buffer.
+ This value is only returned iff NO_ERROR is the return code.
+ NO_ERROR is returned as long as at least one entry was written
+ into Buffer but does not necessarily mean that it's the number
+ of EntriesRequested.
+
+Return Value:
+
+ NO_ERROR - At least one entry was written to output buffer,
+ irregardless of the number requested.
+
+ WN_NO_MORE_ENTRIES - No entries left to return.
+
+ WN_MORE_DATA - The buffer was too small to fit a single entry.
+
+--*/ // NwEnumContextInfo
+{
+ DWORD status = NO_ERROR;
+ DWORD tempResumeId = 0;
+
+ LPBYTE FixedPortion = Buffer;
+ LPWSTR EndOfVariableData = (LPWSTR) ((DWORD) FixedPortion +
+ ROUND_DOWN_COUNT(BufferSize,ALIGN_DWORD));
+
+ BOOL FitInBuffer = TRUE;
+ DWORD EntrySize;
+ DWORD LastObjectId = ContextHandle->ResumeId;
+
+ while ( ContextHandle->dwUsingNds == CURRENTLY_ENUMERATING_NON_NDS &&
+ FitInBuffer &&
+ EntriesRequested > *EntriesRead &&
+ status == NO_ERROR )
+ {
+ tempResumeId = ContextHandle->ResumeId;
+
+ status = NwGetNextServerConnection( ContextHandle );
+
+ if ( status == NO_ERROR && ContextHandle->ResumeId != 0 )
+ {
+ //
+ // Pack bindery server name into output buffer.
+ //
+ status = NwWriteNetResourceEntry(
+ &FixedPortion,
+ &EndOfVariableData,
+ L"\\\\",
+ NULL,
+ (LPWSTR) ContextHandle->ResumeId, // A server name
+ RESOURCE_CONTEXT,
+ RESOURCEDISPLAYTYPE_SERVER,
+ RESOURCEUSAGE_CONTAINER,
+ RESOURCETYPE_ANY,
+ NULL,
+ NULL,
+ &EntrySize
+ );
+
+ if (status == WN_MORE_DATA)
+ {
+ //
+ // Could not write current entry into output buffer,
+ // backup ResumeId to previous entry.
+ //
+ ContextHandle->ResumeId = tempResumeId;
+ ContextHandle->NdsRawDataCount += 1;
+
+ if (*EntriesRead)
+ {
+ //
+ // Still return success because we got at least one.
+ //
+ status = NO_ERROR;
+ }
+ else
+ {
+ *BytesNeeded = EntrySize;
+ }
+
+ FitInBuffer = FALSE;
+ }
+ else if (status == NO_ERROR)
+ {
+ //
+ // Note that we've returned the current entry.
+ //
+ (*EntriesRead)++;
+ }
+ }
+ else if ( status == WN_NO_MORE_ENTRIES )
+ {
+ //
+ // We processed the last item in list, so
+ // start enumerating servers.
+ //
+ ContextHandle->ResumeId = 0;
+ LastObjectId = 0;
+
+ if ( ContextHandle->HandleType == NwsHandleListContextInfo_Tree )
+ {
+ ContextHandle->dwUsingNds = CURRENTLY_ENUMERATING_NDS;
+ }
+ }
+ }
+
+ if ( ContextHandle->dwUsingNds == CURRENTLY_ENUMERATING_NDS )
+ {
+ ContextHandle->HandleType = NwsHandleListNdsSubTrees_Any;
+ status = NO_ERROR;
+ }
+
+ //
+ // User asked for more than there are entries. We just say that
+ // all is well.
+ //
+ // This is incompliance with the wierd provider API definition where
+ // if user gets NO_ERROR, and EntriesRequested > *EntriesRead, and
+ // at least one entry fit into output buffer, there's no telling if
+ // the buffer was too small for more entries or there are no more
+ // entries. The user has to call this API again and get WN_NO_MORE_ENTRIES
+ // before knowing that the last call had actually reached the end of list.
+ //
+ if (*EntriesRead && status == WN_NO_MORE_ENTRIES)
+ {
+ status = NO_ERROR;
+ }
+
+ return status;
+}
+
+
+DWORD
+NwEnumServersAndNdsTrees(
+ IN LPNW_ENUM_CONTEXT ContextHandle,
+ IN DWORD EntriesRequested,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT LPDWORD BytesNeeded,
+ OUT LPDWORD EntriesRead
+ )
+/*++
+
+Routine Description:
+
+ This function enumerates all the servers and NDS trees on the local
+ network by: 1) scanning the bindery for file server objects on the
+ preferred server and 2) scanning the bindery for directory servers
+ (NDS trees) on the preferred server. The server and tree entries are
+ returned in an array of NETRESOURCE entries; each servername is
+ prefixed by \\.
+
+ The ContextHandle->ResumeId field is initially 0xffffffff before
+ enumeration begins and contains the object ID of the last server
+ or NDS tree object returned, depending on the value of
+ ContextHandle->dwUsingNds.
+
+Arguments:
+
+ ContextHandle - Supplies the enum context handle.
+
+ EntriesRequested - Supplies the number of entries to return. If
+ this value is 0xffffffff, return all available entries.
+
+ Buffer - Receives the entries we are listing.
+
+ BufferSize - Supplies the size of the output buffer.
+
+ BytesNeeded - Receives the number of bytes required to get the
+ first entry. This value is returned iff WN_MORE_DATA is
+ the return code, and Buffer is too small to even fit one
+ entry.
+
+
+ This value is only returned iff NO_ERROR is the return code.
+ NO_ERROR is returned as long as at least one entry was written
+ into Buffer but does not necessarily mean that it's the number
+ of EntriesRequested.
+
+Return Value:
+
+ NO_ERROR - At least one entry was written to output buffer,
+ irregardless of the number requested.
+
+ WN_NO_MORE_ENTRIES - No entries left to return.
+
+ WN_MORE_DATA - The buffer was too small to fit a single entry.
+
+--*/ // NwEnumServersAndNdsTrees
+{
+ DWORD status = NO_ERROR;
+ DWORD tempResumeId = 0;
+
+ LPBYTE FixedPortion = Buffer;
+ LPWSTR EndOfVariableData = (LPWSTR) ((DWORD) FixedPortion +
+ ROUND_DOWN_COUNT(BufferSize,ALIGN_DWORD));
+
+ BOOL FitInBuffer = TRUE;
+ DWORD EntrySize;
+
+ SERVERNAME ServerName; // OEM server name
+ LPWSTR UServerName = NULL; // Unicode server name
+ DWORD LastObjectId = ContextHandle->ResumeId;
+
+ while ( ContextHandle->dwUsingNds == CURRENTLY_ENUMERATING_NDS &&
+ FitInBuffer &&
+ EntriesRequested > *EntriesRead &&
+ status == NO_ERROR )
+ {
+ tempResumeId = ContextHandle->ResumeId;
+
+ //
+ // Call the scan bindery object NCP to scan for all NDS
+ // tree objects.
+ //
+ status = NwGetNextNdsTreeEntry( ContextHandle );
+
+ if ( status == NO_ERROR && ContextHandle->ResumeId != 0 )
+ {
+ //
+ // Pack tree name into output buffer.
+ //
+ status = NwWriteNetResourceEntry(
+ &FixedPortion,
+ &EndOfVariableData,
+ L" \\\\",
+ NULL,
+ (LPWSTR) ContextHandle->ResumeId, // This is a NDS tree name
+ RESOURCE_GLOBALNET,
+ RESOURCEDISPLAYTYPE_TREE,
+ RESOURCEUSAGE_CONTAINER,
+ RESOURCETYPE_ANY,
+ NULL,
+ NULL,
+ &EntrySize
+ );
+
+ if (status == WN_MORE_DATA)
+ {
+ //
+ // Could not write current entry into output buffer, backup ResumeId to
+ // previous entry.
+ //
+ ContextHandle->ResumeId = tempResumeId;
+ ContextHandle->NdsRawDataCount += 1;
+
+ if (*EntriesRead)
+ {
+ //
+ // Still return success because we got at least one.
+ //
+ status = NO_ERROR;
+ }
+ else
+ {
+ *BytesNeeded = EntrySize;
+ }
+
+ FitInBuffer = FALSE;
+ }
+ else if (status == NO_ERROR)
+ {
+ //
+ // Note that we've returned the current entry.
+ //
+ (*EntriesRead)++;
+ }
+ }
+ else if ( status == WN_NO_MORE_ENTRIES )
+ {
+ //
+ // We processed the last item in list, so
+ // start enumerating servers.
+ //
+ ContextHandle->dwUsingNds = CURRENTLY_ENUMERATING_NON_NDS;
+ ContextHandle->ResumeId = 0xFFFFFFFF;
+ LastObjectId = 0xFFFFFFFF;
+ }
+ }
+
+ if ( status == WN_NO_MORE_ENTRIES)
+ {
+ status = NO_ERROR;
+ }
+
+ while ( ContextHandle->dwUsingNds == CURRENTLY_ENUMERATING_NON_NDS &&
+ FitInBuffer &&
+ EntriesRequested > *EntriesRead &&
+ status == NO_ERROR )
+ {
+ RtlZeroMemory(ServerName, sizeof(ServerName));
+
+ //
+ // Call the scan bindery object NCP to scan for all file
+ // server objects.
+ //
+ status = NwGetNextServerEntry(
+ ContextHandle->TreeConnectionHandle,
+ &LastObjectId,
+ ServerName
+ );
+
+ if (status == NO_ERROR && NwConvertToUnicode(&UServerName, ServerName))
+ {
+ //
+ // Pack server name into output buffer.
+ //
+ status = NwWriteNetResourceEntry(
+ &FixedPortion,
+ &EndOfVariableData,
+ L"\\\\",
+ NULL,
+ UServerName,
+ RESOURCE_GLOBALNET,
+ RESOURCEDISPLAYTYPE_SERVER,
+ RESOURCEUSAGE_CONTAINER,
+ RESOURCETYPE_ANY,
+ NULL,
+ NULL,
+ &EntrySize
+ );
+
+ if (status == WN_MORE_DATA)
+ {
+ //
+ // Could not write current entry into output buffer.
+ //
+
+ if (*EntriesRead)
+ {
+ //
+ // Still return success because we got at least one.
+ //
+ status = NO_ERROR;
+ }
+ else
+ {
+ *BytesNeeded = EntrySize;
+ }
+
+ FitInBuffer = FALSE;
+ }
+ else if (status == NO_ERROR)
+ {
+ //
+ // Note that we've returned the current entry.
+ //
+ (*EntriesRead)++;
+
+ ContextHandle->ResumeId = LastObjectId;
+ }
+
+ (void) LocalFree((HLOCAL) UServerName);
+ }
+ }
+
+ //
+ // User asked for more than there are entries. We just say that
+ // all is well.
+ //
+ // This is incompliance with the wierd provider API definition where
+ // if user gets NO_ERROR, and EntriesRequested > *EntriesRead, and
+ // at least one entry fit into output buffer, there's no telling if
+ // the buffer was too small for more entries or there are no more
+ // entries. The user has to call this API again and get WN_NO_MORE_ENTRIES
+ // before knowing that the last call had actually reached the end of list.
+ //
+ if (*EntriesRead && status == WN_NO_MORE_ENTRIES)
+ {
+ status = NO_ERROR;
+ }
+
+ return status;
+}
+
+
+
+DWORD
+NwEnumVolumes(
+ IN LPNW_ENUM_CONTEXT ContextHandle,
+ IN DWORD EntriesRequested,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT LPDWORD BytesNeeded,
+ OUT LPDWORD EntriesRead
+ )
+/*++
+
+Routine Description:
+
+ This function enumerates all the volumes on a server by
+ iteratively getting the volume name for each volume number from
+ 0 - 31 until we run into the first volume number that does not
+ map to a volume name (this method assumes that volume numbers
+ are used contiguously in ascending order). The volume entries
+ are returned in an array of NETRESOURCE entries; each volume
+ name if prefixed by \\Server\.
+
+ The ContextHandle->ResumeId field always indicates the next
+ volume entry to return. It is initially set to 0, which indicates
+ the first volume number to get.
+
+Arguments:
+
+ ContextHandle - Supplies the enum context handle.
+
+ EntriesRequested - Supplies the number of entries to return. If
+ this value is 0xffffffff, return all available entries.
+
+ Buffer - Receives the entries we are listing.
+
+ BufferSize - Supplies the size of the output buffer.
+
+ BytesNeeded - Receives the number of bytes required to get the
+ first entry. This value is returned iff WN_MORE_DATA is
+ the return code, and Buffer is too small to even fit one
+ entry.
+
+ EntriesRead - Receives the number of entries returned in Buffer.
+ This value is only returned iff NO_ERROR is the return code.
+ NO_ERROR is returned as long as at least one entry was written
+ into Buffer but does not necessarily mean that it's the number
+ of EntriesRequested.
+
+Return Value:
+
+ NO_ERROR - At least one entry was written to output buffer,
+ irregardless of the number requested.
+
+ WN_NO_MORE_ENTRIES - No entries left to return.
+
+ WN_MORE_DATA - The buffer was too small to fit a single entry.
+
+--*/ // NwEnumVolumes
+{
+ DWORD status = NO_ERROR;
+
+ LPBYTE FixedPortion = Buffer;
+ LPWSTR EndOfVariableData = (LPWSTR) ((DWORD) FixedPortion +
+ ROUND_DOWN_COUNT(BufferSize,ALIGN_DWORD));
+
+ BOOL FitInBuffer = TRUE;
+ DWORD EntrySize;
+
+ CHAR VolumeName[NW_VOLUME_NAME_LEN]; // OEM volume name
+ LPWSTR UVolumeName = NULL; // Unicode volume name
+ DWORD NextVolumeNumber = ContextHandle->ResumeId;
+ DWORD MaxVolumeNumber = ContextHandle->dwMaxVolumes;
+
+ if (NextVolumeNumber == MaxVolumeNumber) {
+ //
+ // Reached the end of enumeration
+ //
+ return WN_NO_MORE_ENTRIES;
+ }
+
+ while (FitInBuffer &&
+ EntriesRequested > *EntriesRead &&
+ NextVolumeNumber < MaxVolumeNumber &&
+ status == NO_ERROR) {
+
+ RtlZeroMemory(VolumeName, sizeof(VolumeName));
+
+ //
+ // Call the scan bindery object NCP to scan for all file
+ // volume objects.
+ //
+ status = NwGetNextVolumeEntry(
+ ContextHandle->TreeConnectionHandle,
+ NextVolumeNumber++,
+ VolumeName
+ );
+
+ if (status == NO_ERROR) {
+
+ if (VolumeName[0] == 0) {
+
+ //
+ // Got an empty volume name back for the next volume number
+ // which indicates there is no volume associated with the
+ // volume number but still got error success.
+ //
+ // Treat this as having reached the end of the enumeration.
+ //
+ NextVolumeNumber = MaxVolumeNumber;
+ ContextHandle->ResumeId = MaxVolumeNumber;
+
+ if (*EntriesRead == 0) {
+ status = WN_NO_MORE_ENTRIES;
+ }
+
+ }
+ else if (NwConvertToUnicode(&UVolumeName, VolumeName)) {
+
+ //
+ // Pack volume name into output buffer.
+ //
+ status = NwWriteNetResourceEntry(
+ &FixedPortion,
+ &EndOfVariableData,
+ ContextHandle->ContainerName,
+ NULL,
+ UVolumeName,
+ RESOURCE_GLOBALNET,
+ RESOURCEDISPLAYTYPE_SHARE,
+#ifdef NT1057
+ RESOURCEUSAGE_CONNECTABLE |
+ RESOURCEUSAGE_CONTAINER,
+#else
+ RESOURCEUSAGE_CONNECTABLE |
+ RESOURCEUSAGE_NOLOCALDEVICE,
+#endif
+ RESOURCETYPE_DISK,
+ NULL,
+ NULL,
+ &EntrySize
+ );
+
+ if (status == WN_MORE_DATA) {
+
+ //
+ // Could not write current entry into output buffer.
+ //
+
+ if (*EntriesRead) {
+ //
+ // Still return success because we got at least one.
+ //
+ status = NO_ERROR;
+ }
+ else {
+ *BytesNeeded = EntrySize;
+ }
+
+ FitInBuffer = FALSE;
+ }
+ else if (status == NO_ERROR) {
+
+ //
+ // Note that we've returned the current entry.
+ //
+ (*EntriesRead)++;
+
+ ContextHandle->ResumeId = NextVolumeNumber;
+ }
+
+ (void) LocalFree((HLOCAL) UVolumeName);
+ }
+ }
+ }
+
+ //
+ // User asked for more than there are entries. We just say that
+ // all is well.
+ //
+ // This is incompliance with the wierd provider API definition where
+ // if user gets NO_ERROR, and EntriesRequested > *EntriesRead, and
+ // at least one entry fit into output buffer, there's no telling if
+ // the buffer was too small for more entries or there are no more
+ // entries. The user has to call this API again and get WN_NO_MORE_ENTRIES
+ // before knowing that the last call had actually reached the end of list.
+ //
+ if (*EntriesRead && status == WN_NO_MORE_ENTRIES) {
+ status = NO_ERROR;
+ }
+
+ return status;
+}
+
+
+DWORD
+NwEnumNdsSubTrees_Disk(
+ IN LPNW_ENUM_CONTEXT ContextHandle,
+ IN DWORD EntriesRequested,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT LPDWORD BytesNeeded,
+ OUT LPDWORD EntriesRead
+ )
+/*++
+
+Routine Description:
+
+ This function enumerates the sub-trees of a given NDS tree
+ handle. It returns the fully-qualified UNC path of the sub-tree
+ entries in an array of NETRESOURCE entries.
+
+ The ContextHandle->ResumeId field is 0 initially, and contains
+ a pointer to the subtree name string of the last sub-tree
+ returned. If there are no more sub-trees to return, this
+ field is set to 0xffffffff.
+
+Arguments:
+
+ ContextHandle - Supplies the enum context handle. It contains
+ an opened NDS tree handle.
+
+ EntriesRequested - Supplies the number of entries to return. If
+ this value is 0xffffffff, return all available entries.
+
+ Buffer - Receives the entries we are listing.
+
+ BufferSize - Supplies the size of the output buffer.
+
+ BytesNeeded - Receives the number of bytes required to get the
+ first entry. This value is returned iff WN_MORE_DATA is
+ the return code, and Buffer is too small to even fit one
+ entry.
+
+ EntriesRead - Receives the number of entries returned in Buffer.
+ This value is only returned iff NO_ERROR is the return code.
+ NO_ERROR is returned as long as at least one entry was written
+ into Buffer but does not necessarily mean that it's the number
+ of EntriesRequested.
+
+Return Value:
+
+ NO_ERROR - At least one entry was written to output buffer,
+ irregardless of the number requested.
+
+ WN_NO_MORE_ENTRIES - No entries left to return.
+
+ WN_MORE_DATA - The buffer was too small to fit a single entry.
+
+--*/ // NwEnumNdsSubTrees_Disk
+{
+ DWORD status = NO_ERROR;
+
+ LPBYTE FixedPortion = Buffer;
+ LPWSTR EndOfVariableData = (LPWSTR) ((DWORD) FixedPortion +
+ ROUND_DOWN_COUNT(BufferSize,ALIGN_DWORD));
+
+ BOOL FitInBuffer = TRUE;
+ DWORD EntrySize = 0;
+
+ DWORD SubTreeName = 0;
+ DWORD ResourceScope = 0;
+ DWORD ResourceType = 0;
+ DWORD ResourceDisplayType = 0;
+ DWORD ResourceUsage = 0;
+ LPWSTR StrippedObjectName = NULL;
+
+ if (ContextHandle->ResumeId == 0xffffffff)
+ {
+ //
+ // Reached the end of enumeration.
+ //
+ return WN_NO_MORE_ENTRIES;
+ }
+
+ while (FitInBuffer &&
+ EntriesRequested > *EntriesRead &&
+ status == NO_ERROR)
+ {
+ if ( ContextHandle->ResumeId == 0 )
+ {
+ //
+ // Get the first subtree entry.
+ //
+ status = NwGetFirstNdsSubTreeEntry( ContextHandle, BufferSize );
+ }
+
+ //
+ // Either ResumeId contains the first entry we just got from
+ // NwGetFirstDirectoryEntry or it contains the next directory
+ // entry to return.
+ //
+ if (status == NO_ERROR && ContextHandle->ResumeId != 0)
+ {
+ BYTE ClassType;
+ LPWSTR newPathStr = NULL;
+ LPWSTR tempStr = NULL;
+ WORD tempStrLen;
+
+ //
+ // Get current subtree data from ContextHandle
+ //
+ ClassType = NwGetSubTreeData( ContextHandle->ResumeId,
+ &SubTreeName,
+ &ResourceScope,
+ &ResourceType,
+ &ResourceDisplayType,
+ &ResourceUsage,
+ &StrippedObjectName );
+
+ if ( StrippedObjectName == NULL )
+ {
+ KdPrint(("NWWORKSTATION: NwEnumNdsSubTrees_Disk LocalAlloc Failed %lu\n",
+ GetLastError()));
+
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ switch( ClassType )
+ {
+ case CLASS_TYPE_COUNTRY:
+ case CLASS_TYPE_DIRECTORY_MAP:
+ case CLASS_TYPE_NCP_SERVER:
+ case CLASS_TYPE_ORGANIZATION:
+ case CLASS_TYPE_ORGANIZATIONAL_UNIT:
+ case CLASS_TYPE_VOLUME:
+
+ //
+ // Need to build a string with the new NDS UNC path for subtree object
+ //
+ newPathStr = (PVOID) LocalAlloc( LMEM_ZEROINIT,
+ ( wcslen( StrippedObjectName ) +
+ wcslen( ContextHandle->ContainerName ) +
+ 3 ) * sizeof(WCHAR) );
+
+ if ( newPathStr == NULL )
+ {
+ KdPrint(("NWWORKSTATION: NwEnumNdsSubTrees_Disk LocalAlloc Failed %lu\n",
+ GetLastError()));
+
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ tempStrLen = NwParseNdsUncPath( (LPWSTR *) &tempStr,
+ ContextHandle->ContainerName,
+ PARSE_NDS_GET_TREE_NAME );
+
+ tempStrLen /= sizeof( WCHAR );
+
+ if ( tempStrLen > 0 )
+ {
+ wcscpy( newPathStr, L"\\\\" );
+ wcsncat( newPathStr, tempStr, tempStrLen );
+ wcscat( newPathStr, L"\\" );
+ wcscat( newPathStr, StrippedObjectName );
+ }
+
+ (void) LocalFree((HLOCAL) StrippedObjectName );
+ StrippedObjectName = NULL;
+
+ tempStrLen = NwParseNdsUncPath( (LPWSTR *) &tempStr,
+ ContextHandle->ContainerName,
+ PARSE_NDS_GET_PATH_NAME );
+
+ tempStrLen /= sizeof( WCHAR );
+
+ if ( tempStrLen > 0 )
+ {
+ wcscat( newPathStr, L"." );
+ wcsncat( newPathStr, tempStr, tempStrLen );
+ }
+
+ //
+ // Pack subtree name into output buffer.
+ //
+ status = NwWriteNetResourceEntry(
+ &FixedPortion,
+ &EndOfVariableData,
+ NULL,
+ NULL,
+ newPathStr,
+ ResourceScope,
+ ResourceDisplayType,
+ ResourceUsage,
+ ResourceType,
+ NULL,
+ NULL,
+ &EntrySize );
+
+ if ( status == NO_ERROR )
+ {
+ //
+ // Note that we've returned the current entry.
+ //
+ (*EntriesRead)++;
+ }
+
+ if ( newPathStr )
+ (void) LocalFree( (HLOCAL) newPathStr );
+
+ break;
+
+ case CLASS_TYPE_ALIAS:
+ case CLASS_TYPE_AFP_SERVER:
+ case CLASS_TYPE_BINDERY_OBJECT:
+ case CLASS_TYPE_BINDERY_QUEUE:
+ case CLASS_TYPE_COMPUTER:
+ case CLASS_TYPE_GROUP:
+ case CLASS_TYPE_LOCALITY:
+ case CLASS_TYPE_ORGANIZATIONAL_ROLE:
+ case CLASS_TYPE_PRINTER:
+ case CLASS_TYPE_PRINT_SERVER:
+ case CLASS_TYPE_PROFILE:
+ case CLASS_TYPE_QUEUE:
+ case CLASS_TYPE_TOP:
+ case CLASS_TYPE_UNKNOWN:
+ case CLASS_TYPE_USER:
+ break;
+
+ default:
+ KdPrint(("NWWORKSTATION: NwEnumNdsSubTrees_Disk - Unhandled switch statement case %lu\n", ClassType ));
+ ASSERT( FALSE );
+ break;
+ }
+
+ if (status == WN_MORE_DATA)
+ {
+ //
+ // Could not write current entry into output buffer.
+ //
+
+ if (*EntriesRead)
+ {
+ //
+ // Still return success because we got at least one.
+ //
+ status = NO_ERROR;
+ }
+ else
+ {
+ *BytesNeeded = EntrySize;
+ }
+
+ FitInBuffer = FALSE;
+ }
+ else if (status == NO_ERROR)
+ {
+ //
+ // Get next directory entry.
+ //
+ status = NwGetNextNdsSubTreeEntry( ContextHandle );
+ }
+ }
+
+ if (status == WN_NO_MORE_ENTRIES)
+ {
+ ContextHandle->ResumeId = 0xffffffff;
+ }
+ }
+
+ //
+ // User asked for more than there are entries. We just say that
+ // all is well.
+ //
+ // This is incompliance with the wierd provider API definition where
+ // if user gets NO_ERROR, and EntriesRequested > *EntriesRead, and
+ // at least one entry fit into output buffer, there's no telling if
+ // the buffer was too small for more entries or there are no more
+ // entries. The user has to call this API again and get WN_NO_MORE_ENTRIES
+ // before knowing that the last call had actually reached the end of list.
+ //
+ if (*EntriesRead && status == WN_NO_MORE_ENTRIES) {
+ status = NO_ERROR;
+ }
+
+#if DBG
+ IF_DEBUG(ENUM)
+ {
+ KdPrint(("NwEnumNdsSubTrees_Disk returns %lu\n", status));
+ }
+#endif
+
+ return status;
+}
+
+
+DWORD
+NwEnumNdsSubTrees_Print(
+ IN LPNW_ENUM_CONTEXT ContextHandle,
+ IN DWORD EntriesRequested,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT LPDWORD BytesNeeded,
+ OUT LPDWORD EntriesRead
+ )
+/*++
+
+Routine Description:
+
+ This function enumerates all the NDS subtree objects that are either containers,
+ queues, printers, or servers from a given NDS tree or subtree. The entries are
+ returned in an array of PRINTER_INFO_1 entries and each name is prefixed
+ by the parent path in NDS UNC style (ex. \\tree\CN=foo.OU=bar.O=blah).
+
+ The ContextHandle->ResumeId field is initially 0xffffffff before
+ enumeration begins and contains the object ID of the last NDS object returned.
+
+Arguments:
+
+ ContextHandle - Supplies the enum context handle.
+
+ EntriesRequested - Supplies the number of entries to return. If
+ this value is 0xffffffff, return all available entries.
+
+ Buffer - Receives the entries we are listing.
+
+ BufferSize - Supplies the size of the output buffer.
+
+ BytesNeeded - Receives the number of bytes copied or required to get all
+ the requested entries.
+
+ EntriesRead - Receives the number of entries returned in Buffer.
+ This value is only returned iff NO_ERROR is the return code.
+
+Return Value:
+
+ NO_ERROR - Buffer contains all the entries requested.
+
+ WN_NO_MORE_ENTRIES - No entries left to return.
+
+ WN_MORE_DATA - The buffer was too small to fit the requested entries.
+
+--*/ // NwEnumNdsSubTrees_Print
+{
+ DWORD status = NO_ERROR;
+
+ LPBYTE FixedPortion = Buffer;
+ LPWSTR EndOfVariableData = (LPWSTR) ((DWORD) FixedPortion +
+ ROUND_DOWN_COUNT(BufferSize,ALIGN_DWORD));
+
+ DWORD EntrySize;
+ BOOL FitInBuffer = TRUE;
+
+ DWORD SubTreeName = 0;
+ DWORD ResourceScope = 0;
+ DWORD ResourceType = 0;
+ DWORD ResourceDisplayType = 0;
+ DWORD ResourceUsage = 0;
+ LPWSTR StrippedObjectName = NULL;
+ BYTE ClassType = 0;
+ LPWSTR newPathStr = NULL;
+ LPWSTR tempStr = NULL;
+ WORD tempStrLen = 0;
+
+ while ( EntriesRequested > *EntriesRead &&
+ ( (status == NO_ERROR) || (status == ERROR_INSUFFICIENT_BUFFER)))
+ {
+ if (ContextHandle->ResumeId == 0)
+ {
+ //
+ // Get the first subtree entry.
+ //
+ status = NwGetFirstNdsSubTreeEntry( ContextHandle, BufferSize );
+ }
+
+ //
+ // Either ResumeId contains the first entry we just got from
+ // NwGetFirstDirectoryEntry or it contains the next directory
+ // entry to return.
+ //
+ if (status == NO_ERROR && ContextHandle->ResumeId != 0)
+ {
+
+ //
+ // Get current subtree data from ContextHandle
+ //
+ ClassType = NwGetSubTreeData( ContextHandle->ResumeId,
+ &SubTreeName,
+ &ResourceScope,
+ &ResourceType,
+ &ResourceDisplayType,
+ &ResourceUsage,
+ &StrippedObjectName );
+
+ if ( StrippedObjectName == NULL )
+ {
+ KdPrint(("NWWORKSTATION: NwEnumNdsSubTrees_Print LocalAlloc Failed %lu\n",
+ GetLastError()));
+
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ switch( ClassType )
+ {
+
+ case CLASS_TYPE_COUNTRY:
+ case CLASS_TYPE_ORGANIZATION:
+ case CLASS_TYPE_ORGANIZATIONAL_UNIT:
+ case CLASS_TYPE_NCP_SERVER:
+ case CLASS_TYPE_QUEUE:
+ //
+ // Need to build a string with the new NDS UNC path for subtree object
+ //
+ newPathStr = (LPWSTR) LocalAlloc( LMEM_ZEROINIT,
+ ( wcslen( StrippedObjectName ) +
+ wcslen( ContextHandle->ContainerName ) +
+ 2 ) * sizeof(WCHAR) );
+
+ if ( newPathStr == NULL )
+ {
+ KdPrint(("NWWORKSTATION: NwEnumNdsSubTrees_Print LocalAlloc Failed %lu\n",
+ GetLastError()));
+
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ tempStrLen = NwParseNdsUncPath( (LPWSTR *) &tempStr,
+ ContextHandle->ContainerName,
+ PARSE_NDS_GET_TREE_NAME );
+
+ tempStrLen /= sizeof( WCHAR );
+
+ if ( tempStrLen > 0 )
+ {
+ wcsncpy( newPathStr, tempStr, tempStrLen );
+ wcscat( newPathStr, L"\\" );
+ wcscat( newPathStr, StrippedObjectName );
+ }
+
+ (void) LocalFree((HLOCAL) StrippedObjectName );
+ StrippedObjectName = NULL;
+
+ tempStrLen = NwParseNdsUncPath( (LPWSTR *) &tempStr,
+ ContextHandle->ContainerName,
+ PARSE_NDS_GET_PATH_NAME );
+
+ tempStrLen /= sizeof( WCHAR );
+
+ if ( tempStrLen > 0 )
+ {
+ wcscat( newPathStr, L"." );
+ wcsncat( newPathStr, tempStr, tempStrLen );
+ }
+
+ switch( ClassType )
+ {
+ case CLASS_TYPE_COUNTRY:
+ case CLASS_TYPE_ORGANIZATION:
+ case CLASS_TYPE_ORGANIZATIONAL_UNIT:
+ //
+ // Pack sub-tree container name into output buffer.
+ //
+ status = NwWritePrinterInfoEntry(
+ &FixedPortion,
+ &EndOfVariableData,
+ NULL,
+ newPathStr,
+ PRINTER_ENUM_CONTAINER | PRINTER_ENUM_ICON1,
+ &EntrySize );
+
+ break;
+
+ case CLASS_TYPE_NCP_SERVER:
+ //
+ // Pack server name into output buffer.
+ //
+ status = NwWritePrinterInfoEntry(
+ &FixedPortion,
+ &EndOfVariableData,
+ NULL,
+ newPathStr,
+ PRINTER_ENUM_CONTAINER | PRINTER_ENUM_ICON3,
+ &EntrySize );
+
+ break;
+
+ case CLASS_TYPE_QUEUE:
+ //
+ // Pack print server queue name into output buffer.
+ //
+ status = NwWritePrinterInfoEntry(
+ &FixedPortion,
+ &EndOfVariableData,
+ L"\\\\",
+ newPathStr,
+ PRINTER_ENUM_ICON8,
+ &EntrySize );
+ break;
+
+ default:
+KdPrint(("NWWORKSTATION: NwEnumNdsSubTrees_Print - Unhandled switch statement case %lu\n", ClassType ));
+ ASSERT(FALSE);
+ break;
+ }
+
+ switch ( status )
+ {
+ case ERROR_INSUFFICIENT_BUFFER:
+ FitInBuffer = FALSE;
+ // Falls through
+
+ case NO_ERROR:
+ *BytesNeeded += EntrySize;
+ (*EntriesRead)++;
+ break;
+
+ default:
+ break;
+ }
+
+ if ( newPathStr )
+ (void) LocalFree( (HLOCAL) newPathStr );
+
+ break;
+
+ case CLASS_TYPE_ALIAS:
+ case CLASS_TYPE_AFP_SERVER:
+ case CLASS_TYPE_BINDERY_OBJECT:
+ case CLASS_TYPE_BINDERY_QUEUE:
+ case CLASS_TYPE_COMPUTER:
+ case CLASS_TYPE_DIRECTORY_MAP:
+ case CLASS_TYPE_GROUP:
+ case CLASS_TYPE_LOCALITY:
+ case CLASS_TYPE_ORGANIZATIONAL_ROLE:
+ case CLASS_TYPE_PRINTER:
+ case CLASS_TYPE_PRINT_SERVER:
+ case CLASS_TYPE_PROFILE:
+ case CLASS_TYPE_TOP:
+ case CLASS_TYPE_UNKNOWN:
+ case CLASS_TYPE_USER:
+ case CLASS_TYPE_VOLUME:
+ break;
+
+ default:
+KdPrint(("NWWORKSTATION: NwEnumNdsSubTrees_Print - Unhandled switch statement case %lu\n", ClassType ));
+ ASSERT( FALSE );
+ break;
+ }
+
+ if ( status == NO_ERROR || status == ERROR_INSUFFICIENT_BUFFER )
+ {
+ //
+ // Get next directory entry.
+ //
+ status = NwGetNextNdsSubTreeEntry( ContextHandle );
+ }
+ }
+ }
+
+ //
+ // This is incompliance with the wierd provider API definition where
+ // if user gets NO_ERROR, and EntriesRequested > *EntriesRead, and
+ // at least one entry fit into output buffer, there's no telling if
+ // the buffer was too small for more entries or there are no more
+ // entries. The user has to call this API again and get WN_NO_MORE_ENTRIES
+ // before knowing that the last call had actually reached the end of list.
+ //
+ if ( !FitInBuffer )
+ {
+ *EntriesRead = 0;
+ status = ERROR_INSUFFICIENT_BUFFER;
+ }
+ else if (*EntriesRead && status == WN_NO_MORE_ENTRIES)
+ {
+ status = NO_ERROR;
+ }
+
+ return status;
+}
+
+
+DWORD
+NwEnumNdsSubTrees_Any(
+ IN LPNW_ENUM_CONTEXT ContextHandle,
+ IN DWORD EntriesRequested,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT LPDWORD BytesNeeded,
+ OUT LPDWORD EntriesRead
+ )
+/*++
+
+Routine Description:
+
+ This function enumerates the sub-trees of a given NDS tree
+ handle. It returns the fully-qualified UNC path of ANY sub-tree
+ entries in an array of NETRESOURCE entries.
+
+ The ContextHandle->ResumeId field is 0 initially, and contains
+ a pointer to the subtree name string of the last sub-tree
+ returned. If there are no more sub-trees to return, this
+ field is set to 0xffffffff.
+
+Arguments:
+
+ ContextHandle - Supplies the enum context handle. It contains
+ an opened NDS tree handle.
+
+ EntriesRequested - Supplies the number of entries to return. If
+ this value is 0xffffffff, return all available entries.
+
+ Buffer - Receives the entries we are listing.
+
+ BufferSize - Supplies the size of the output buffer.
+
+ BytesNeeded - Receives the number of bytes required to get the
+ first entry. This value is returned iff WN_MORE_DATA is
+ the return code, and Buffer is too small to even fit one
+ entry.
+
+ EntriesRead - Receives the number of entries returned in Buffer.
+ This value is only returned iff NO_ERROR is the return code.
+ NO_ERROR is returned as long as at least one entry was written
+ into Buffer but does not necessarily mean that it's the number
+ of EntriesRequested.
+
+Return Value:
+
+ NO_ERROR - At least one entry was written to output buffer,
+ irregardless of the number requested.
+
+ WN_NO_MORE_ENTRIES - No entries left to return.
+
+ WN_MORE_DATA - The buffer was too small to fit a single entry.
+
+--*/ // NwEnumNdsSubTrees_Any
+{
+ DWORD status = NO_ERROR;
+
+ LPBYTE FixedPortion = Buffer;
+ LPWSTR EndOfVariableData = (LPWSTR) ((DWORD) FixedPortion +
+ ROUND_DOWN_COUNT(BufferSize,ALIGN_DWORD));
+
+ BOOL FitInBuffer = TRUE;
+ DWORD EntrySize = 0;
+
+ DWORD SubTreeName = 0;
+ DWORD ResourceScope = 0;
+ DWORD ResourceType = 0;
+ DWORD ResourceDisplayType = 0;
+ DWORD ResourceUsage = 0;
+ LPWSTR StrippedObjectName = NULL;
+
+ if (ContextHandle->ResumeId == 0xffffffff)
+ {
+ //
+ // Reached the end of enumeration.
+ //
+ return WN_NO_MORE_ENTRIES;
+ }
+
+ while (FitInBuffer &&
+ EntriesRequested > *EntriesRead &&
+ status == NO_ERROR)
+ {
+ if ( ContextHandle->ResumeId == 0 )
+ {
+ //
+ // Get the first subtree entry.
+ //
+ status = NwGetFirstNdsSubTreeEntry( ContextHandle, BufferSize );
+ }
+
+ //
+ // Either ResumeId contains the first entry we just got from
+ // NwGetFirstDirectoryEntry or it contains the next directory
+ // entry to return.
+ //
+ if (status == NO_ERROR && ContextHandle->ResumeId != 0)
+ {
+ BYTE ClassType;
+ LPWSTR newPathStr = NULL;
+ LPWSTR tempStr = NULL;
+ WORD tempStrLen;
+
+ //
+ // Get current subtree data from ContextHandle
+ //
+ ClassType = NwGetSubTreeData( ContextHandle->ResumeId,
+ &SubTreeName,
+ &ResourceScope,
+ &ResourceType,
+ &ResourceDisplayType,
+ &ResourceUsage,
+ &StrippedObjectName );
+
+ if ( StrippedObjectName == NULL )
+ {
+ KdPrint(("NWWORKSTATION: NwEnumNdsSubTrees_Any LocalAlloc Failed %lu\n",
+ GetLastError()));
+
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ switch( ClassType )
+ {
+ case CLASS_TYPE_COUNTRY:
+ case CLASS_TYPE_ORGANIZATION:
+ case CLASS_TYPE_ORGANIZATIONAL_UNIT:
+ case CLASS_TYPE_VOLUME:
+ case CLASS_TYPE_DIRECTORY_MAP:
+ case CLASS_TYPE_NCP_SERVER:
+ case CLASS_TYPE_QUEUE:
+
+ //
+ // Need to build a string with the new NDS UNC path for subtree object
+ //
+ newPathStr = (PVOID) LocalAlloc( LMEM_ZEROINIT,
+ ( wcslen( StrippedObjectName ) +
+ wcslen( ContextHandle->ContainerName ) +
+ 3 ) * sizeof(WCHAR) );
+
+ if ( newPathStr == NULL )
+ {
+ KdPrint(("NWWORKSTATION: NwEnumNdsSubTrees_Any LocalAlloc Failed %lu\n",
+ GetLastError()));
+
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ tempStrLen = NwParseNdsUncPath( (LPWSTR *) &tempStr,
+ ContextHandle->ContainerName,
+ PARSE_NDS_GET_TREE_NAME );
+
+ tempStrLen /= sizeof( WCHAR );
+
+ if ( tempStrLen > 0 )
+ {
+ wcscpy( newPathStr, L"\\\\" );
+ wcsncat( newPathStr, tempStr, tempStrLen );
+ wcscat( newPathStr, L"\\" );
+ wcscat( newPathStr, StrippedObjectName );
+ }
+
+ (void) LocalFree((HLOCAL) StrippedObjectName );
+ StrippedObjectName = NULL;
+
+ tempStrLen = NwParseNdsUncPath( (LPWSTR *) &tempStr,
+ ContextHandle->ContainerName,
+ PARSE_NDS_GET_PATH_NAME );
+
+ tempStrLen /= sizeof( WCHAR );
+
+ if ( tempStrLen > 0 )
+ {
+ wcscat( newPathStr, L"." );
+ wcsncat( newPathStr, tempStr, tempStrLen );
+ }
+
+ //
+ // Pack subtree name into output buffer.
+ //
+ status = NwWriteNetResourceEntry(
+ &FixedPortion,
+ &EndOfVariableData,
+ NULL,
+ NULL,
+ newPathStr,
+ ResourceScope,
+ ResourceDisplayType,
+ ResourceUsage,
+ ResourceType,
+ NULL,
+ NULL,
+ &EntrySize );
+
+ if ( status == NO_ERROR )
+ {
+ //
+ // Note that we've returned the current entry.
+ //
+ (*EntriesRead)++;
+ }
+
+ if ( newPathStr )
+ (void) LocalFree( (HLOCAL) newPathStr );
+
+ break;
+
+ case CLASS_TYPE_ALIAS:
+ case CLASS_TYPE_AFP_SERVER:
+ case CLASS_TYPE_BINDERY_OBJECT:
+ case CLASS_TYPE_BINDERY_QUEUE:
+ case CLASS_TYPE_COMPUTER:
+ case CLASS_TYPE_GROUP:
+ case CLASS_TYPE_LOCALITY:
+ case CLASS_TYPE_ORGANIZATIONAL_ROLE:
+ case CLASS_TYPE_PRINTER:
+ case CLASS_TYPE_PRINT_SERVER:
+ case CLASS_TYPE_PROFILE:
+ case CLASS_TYPE_TOP:
+ case CLASS_TYPE_UNKNOWN:
+ case CLASS_TYPE_USER:
+ break;
+
+ default:
+ KdPrint(("NWWORKSTATION: NwEnumNdsSubTrees_Any - Unhandled switch statement case %lu\n", ClassType ));
+ ASSERT( FALSE );
+ break;
+ }
+
+ if (status == WN_MORE_DATA)
+ {
+ //
+ // Could not write current entry into output buffer.
+ //
+
+ if (*EntriesRead)
+ {
+ //
+ // Still return success because we got at least one.
+ //
+ status = NO_ERROR;
+ }
+ else
+ {
+ *BytesNeeded = EntrySize;
+ }
+
+ FitInBuffer = FALSE;
+ }
+ else if (status == NO_ERROR)
+ {
+ //
+ // Get next directory entry.
+ //
+ status = NwGetNextNdsSubTreeEntry( ContextHandle );
+ }
+ }
+
+ if (status == WN_NO_MORE_ENTRIES)
+ {
+ ContextHandle->ResumeId = 0xffffffff;
+ }
+ }
+
+ //
+ // User asked for more than there are entries. We just say that
+ // all is well.
+ //
+ // This is incompliance with the wierd provider API definition where
+ // if user gets NO_ERROR, and EntriesRequested > *EntriesRead, and
+ // at least one entry fit into output buffer, there's no telling if
+ // the buffer was too small for more entries or there are no more
+ // entries. The user has to call this API again and get WN_NO_MORE_ENTRIES
+ // before knowing that the last call had actually reached the end of list.
+ //
+ if (*EntriesRead && status == WN_NO_MORE_ENTRIES) {
+ status = NO_ERROR;
+ }
+
+#if DBG
+ IF_DEBUG(ENUM)
+ {
+ KdPrint(("NwEnumNdsSubTrees_Any returns %lu\n", status));
+ }
+#endif
+
+ return status;
+}
+
+
+DWORD
+NwEnumVolumesQueues(
+ IN LPNW_ENUM_CONTEXT ContextHandle,
+ IN DWORD EntriesRequested,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT LPDWORD BytesNeeded,
+ OUT LPDWORD EntriesRead
+ )
+/*++
+
+Routine Description:
+
+ This function enumerates all the volumes and queues on a server.
+ The queue entries are returned in an array of NETRESOURCE entries;
+ each queue name is prefixed by \\Server\.
+
+Arguments:
+
+ ContextHandle - Supplies the enum context handle.
+
+ EntriesRequested - Supplies the number of entries to return. If
+ this value is 0xffffffff, return all available entries.
+
+ Buffer - Receives the entries we are listing.
+
+ BufferSize - Supplies the size of the output buffer.
+
+ BytesNeeded - Receives the number of bytes required to get the
+ first entry. This value is returned iff WN_MORE_DATA is
+ the return code, and Buffer is too small to even fit one
+ entry.
+
+ EntriesRead - Receives the number of entries returned in Buffer.
+ This value is only returned iff NO_ERROR is the return code.
+ NO_ERROR is returned as long as at least one entry was written
+ into Buffer but does not necessarily mean that it's the number
+ of EntriesRequested.
+
+Return Value:
+
+ NO_ERROR - At least one entry was written to output buffer,
+ irregardless of the number requested.
+
+ WN_NO_MORE_ENTRIES - No entries left to return.
+
+ WN_MORE_DATA - The buffer was too small to fit a single entry.
+
+--*/ // NwEnumVolumesQueues
+{
+ DWORD status = NO_ERROR;
+
+ LPBYTE FixedPortion = Buffer;
+ LPWSTR EndOfVariableData = (LPWSTR) ((DWORD) FixedPortion +
+ ROUND_DOWN_COUNT(BufferSize,ALIGN_DWORD));
+
+ BOOL FitInBuffer = TRUE;
+ DWORD EntrySize;
+
+ CHAR VolumeName[NW_VOLUME_NAME_LEN]; // OEM volume name
+ LPWSTR UVolumeName = NULL; // Unicode volume name
+ DWORD NextObject = ContextHandle->ResumeId;
+ DWORD MaxVolumeNumber = ContextHandle->dwMaxVolumes;
+
+ while (FitInBuffer &&
+ EntriesRequested > *EntriesRead &&
+ ContextHandle->ConnectionType == CONNTYPE_DISK &&
+ (NextObject >= 0 && NextObject < MaxVolumeNumber) &&
+ status == NO_ERROR) {
+
+
+ RtlZeroMemory(VolumeName, sizeof(VolumeName));
+
+ //
+ // Call the scan bindery object NCP to scan for all file
+ // volume objects.
+ //
+ status = NwGetNextVolumeEntry(
+ ContextHandle->TreeConnectionHandle,
+ NextObject++,
+ VolumeName
+ );
+
+ if (status == NO_ERROR) {
+
+ if (VolumeName[0] == 0) {
+
+ //
+ // Got an empty volume name back for the next volume number
+ // which indicates there is no volume associated with the
+ // volume number but still got error success.
+ //
+ // Treat this as having reached the end of the enumeration.
+ //
+ NextObject = 0xFFFFFFFF;
+ ContextHandle->ResumeId = 0xFFFFFFFF;
+ ContextHandle->ConnectionType = CONNTYPE_PRINT;
+
+ }
+ else if (NwConvertToUnicode(&UVolumeName, VolumeName)) {
+
+ //
+ // Pack volume name into output buffer.
+ //
+ status = NwWriteNetResourceEntry(
+ &FixedPortion,
+ &EndOfVariableData,
+ ContextHandle->ContainerName,
+ NULL,
+ UVolumeName,
+ RESOURCE_GLOBALNET,
+ RESOURCEDISPLAYTYPE_SHARE,
+#ifdef NT1057
+ RESOURCEUSAGE_CONNECTABLE |
+ RESOURCEUSAGE_CONTAINER,
+#else
+ RESOURCEUSAGE_CONNECTABLE |
+ RESOURCEUSAGE_NOLOCALDEVICE,
+#endif
+ RESOURCETYPE_DISK,
+ NULL,
+ NULL,
+ &EntrySize
+ );
+
+ if (status == WN_MORE_DATA) {
+
+ //
+ // Could not write current entry into output buffer.
+ //
+
+ if (*EntriesRead) {
+ //
+ // Still return success because we got at least one.
+ //
+ status = NO_ERROR;
+ }
+ else {
+ *BytesNeeded = EntrySize;
+ }
+
+ FitInBuffer = FALSE;
+ }
+ else if (status == NO_ERROR) {
+
+ //
+ // Note that we've returned the current entry.
+ //
+ (*EntriesRead)++;
+
+ ContextHandle->ResumeId = NextObject;
+ }
+
+ (void) LocalFree((HLOCAL) UVolumeName);
+ }
+ }
+ }
+
+ //
+ // User asked for more than there are entries. We just say that
+ // all is well.
+ //
+ if (*EntriesRead && status == WN_NO_MORE_ENTRIES)
+ {
+ status = NO_ERROR;
+ }
+
+ if ( *EntriesRead == 0 &&
+ status == NO_ERROR &&
+ ContextHandle->ConnectionType == CONNTYPE_DISK )
+ {
+ ContextHandle->ConnectionType = CONNTYPE_PRINT;
+ ContextHandle->ResumeId = 0xFFFFFFFF;
+ }
+
+ //
+ // The user needs to be validated on a netware311 server to
+ // get the print queues. So, we need to close the handle and
+ // open a new one with WRITE access. If any error occurred while
+ // we are enumerating the print queues, we will abort and
+ // assume there are no print queues on the server.
+ //
+
+ if ( FitInBuffer &&
+ EntriesRequested > *EntriesRead &&
+ ContextHandle->ConnectionType == CONNTYPE_PRINT &&
+ status == NO_ERROR )
+ {
+ UNICODE_STRING TreeConnectStr;
+ DWORD QueueEntriesRead = 0;
+
+ (void) NtClose(ContextHandle->TreeConnectionHandle);
+
+ //
+ // Open a tree connection handle to \Device\NwRdr\ContainerName
+ //
+ status = NwCreateTreeConnectName(
+ ContextHandle->ContainerName,
+ NULL,
+ &TreeConnectStr );
+
+ if (status != NO_ERROR)
+ return (*EntriesRead? NO_ERROR: WN_NO_MORE_ENTRIES );
+
+
+ status = NwOpenCreateConnection(
+ &TreeConnectStr,
+ NULL,
+ NULL,
+ ContextHandle->ContainerName,
+ FILE_LIST_DIRECTORY | SYNCHRONIZE | FILE_WRITE_DATA,
+ FILE_OPEN,
+ FILE_SYNCHRONOUS_IO_NONALERT,
+ RESOURCETYPE_PRINT, // Only matters when connecting beyond servername
+ &ContextHandle->TreeConnectionHandle,
+ NULL );
+
+ (void) LocalFree((HLOCAL) TreeConnectStr.Buffer);
+
+ if (status != NO_ERROR)
+ return (*EntriesRead? NO_ERROR: WN_NO_MORE_ENTRIES );
+
+ status = NwEnumQueues(
+ ContextHandle,
+ EntriesRequested == 0xFFFFFFFF?
+ EntriesRequested : (EntriesRequested - *EntriesRead),
+ FixedPortion,
+ ((LPBYTE) EndOfVariableData - (LPBYTE) FixedPortion),
+ BytesNeeded,
+ &QueueEntriesRead );
+
+ if ( status == NO_ERROR )
+ {
+ *EntriesRead += QueueEntriesRead;
+ }
+ else if ( *EntriesRead )
+ {
+ //
+ // As long as we read something into the buffer,
+ // we should return success.
+ //
+ status = NO_ERROR;
+ *BytesNeeded = 0;
+ }
+
+ }
+
+ if ( status == NO_ERROR &&
+ *EntriesRead == 0 &&
+ ContextHandle->ConnectionType == CONNTYPE_PRINT )
+ {
+ return WN_NO_MORE_ENTRIES;
+ }
+
+ return status;
+
+}
+
+
+
+DWORD
+NwEnumQueues(
+ IN LPNW_ENUM_CONTEXT ContextHandle,
+ IN DWORD EntriesRequested,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT LPDWORD BytesNeeded,
+ OUT LPDWORD EntriesRead
+ )
+/*++
+
+Routine Description:
+
+ This function enumerates all the queues on a server.
+ The queue entries are returned in an array of NETRESOURCE entries;
+ each queue name is prefixed by \\Server\.
+
+Arguments:
+
+ ContextHandle - Supplies the enum context handle.
+
+ EntriesRequested - Supplies the number of entries to return. If
+ this value is 0xffffffff, return all available entries.
+
+ Buffer - Receives the entries we are listing.
+
+ BufferSize - Supplies the size of the output buffer.
+
+ BytesNeeded - Receives the number of bytes required to get the
+ first entry. This value is returned iff WN_MORE_DATA is
+ the return code, and Buffer is too small to even fit one
+ entry.
+
+ EntriesRead - Receives the number of entries returned in Buffer.
+ This value is only returned iff NO_ERROR is the return code.
+ NO_ERROR is returned as long as at least one entry was written
+ into Buffer but does not necessarily mean that it's the number
+ of EntriesRequested.
+
+Return Value:
+
+ NO_ERROR - At least one entry was written to output buffer,
+ irregardless of the number requested.
+
+ WN_NO_MORE_ENTRIES - No entries left to return.
+
+ WN_MORE_DATA - The buffer was too small to fit a single entry.
+
+--*/ // NwEnumQueues
+{
+ DWORD status = NO_ERROR;
+
+ LPBYTE FixedPortion = Buffer;
+ LPWSTR EndOfVariableData = (LPWSTR) ((DWORD) FixedPortion +
+ ROUND_DOWN_COUNT(BufferSize,ALIGN_DWORD));
+
+ BOOL FitInBuffer = TRUE;
+ DWORD EntrySize;
+
+ DWORD NextObject = ContextHandle->ResumeId;
+
+ SERVERNAME QueueName; // OEM queue name
+ LPWSTR UQueueName = NULL; // Unicode queue name
+
+ while ( FitInBuffer &&
+ EntriesRequested > *EntriesRead &&
+ status == NO_ERROR ) {
+
+ RtlZeroMemory(QueueName, sizeof(QueueName));
+
+ //
+ // Call the scan bindery object NCP to scan for all file
+ // volume objects.
+ //
+ status = NwGetNextQueueEntry(
+ ContextHandle->TreeConnectionHandle,
+ &NextObject,
+ QueueName
+ );
+
+ if (status == NO_ERROR && NwConvertToUnicode(&UQueueName, QueueName)) {
+
+ //
+ // Pack server name into output buffer.
+ //
+ status = NwWriteNetResourceEntry(
+ &FixedPortion,
+ &EndOfVariableData,
+ ContextHandle->ContainerName,
+ NULL,
+ UQueueName,
+ RESOURCE_GLOBALNET,
+ RESOURCEDISPLAYTYPE_SHARE,
+ RESOURCEUSAGE_CONNECTABLE,
+ RESOURCETYPE_PRINT,
+ NULL,
+ NULL,
+ &EntrySize
+ );
+
+ if (status == WN_MORE_DATA) {
+
+ //
+ // Could not write current entry into output buffer.
+ //
+
+ if (*EntriesRead) {
+ //
+ // Still return success because we got at least one.
+ //
+ status = NO_ERROR;
+ }
+ else {
+ *BytesNeeded = EntrySize;
+ }
+
+ FitInBuffer = FALSE;
+ }
+ else if (status == NO_ERROR) {
+
+ //
+ // Note that we've returned the current entry.
+ //
+ (*EntriesRead)++;
+
+ ContextHandle->ResumeId = NextObject;
+ }
+
+ (void) LocalFree((HLOCAL) UQueueName);
+ }
+ }
+
+ if (*EntriesRead && status == WN_NO_MORE_ENTRIES) {
+ status = NO_ERROR;
+ }
+
+ return status;
+}
+
+
+DWORD
+NwEnumDirectories(
+ IN LPNW_ENUM_CONTEXT ContextHandle,
+ IN DWORD EntriesRequested,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT LPDWORD BytesNeeded,
+ OUT LPDWORD EntriesRead
+ )
+/*++
+
+Routine Description:
+
+ This function enumerates the directories of a given directory
+ handle by calling NtQueryDirectoryFile. It returns the
+ fully-qualified UNC path of the directory entries in an array
+ of NETRESOURCE entries.
+
+ The ContextHandle->ResumeId field is 0 initially, and contains
+ a pointer to the directory name string of the last directory
+ returned. If there are no more directories to return, this
+ field is set to 0xffffffff.
+
+Arguments:
+
+ ContextHandle - Supplies the enum context handle. It contains
+ an opened directory handle.
+
+ EntriesRequested - Supplies the number of entries to return. If
+ this value is 0xffffffff, return all available entries.
+
+ Buffer - Receives the entries we are listing.
+
+ BufferSize - Supplies the size of the output buffer.
+
+ BytesNeeded - Receives the number of bytes required to get the
+ first entry. This value is returned iff WN_MORE_DATA is
+ the return code, and Buffer is too small to even fit one
+ entry.
+
+ EntriesRead - Receives the number of entries returned in Buffer.
+ This value is only returned iff NO_ERROR is the return code.
+ NO_ERROR is returned as long as at least one entry was written
+ into Buffer but does not necessarily mean that it's the number
+ of EntriesRequested.
+
+Return Value:
+
+ NO_ERROR - At least one entry was written to output buffer,
+ irregardless of the number requested.
+
+ WN_NO_MORE_ENTRIES - No entries left to return.
+
+ WN_MORE_DATA - The buffer was too small to fit a single entry.
+
+--*/ // NwEnumDirectories
+{
+ DWORD status = NO_ERROR;
+
+ LPBYTE FixedPortion = Buffer;
+ LPWSTR EndOfVariableData = (LPWSTR) ((DWORD) FixedPortion +
+ ROUND_DOWN_COUNT(BufferSize,ALIGN_DWORD));
+
+ BOOL FitInBuffer = TRUE;
+ DWORD EntrySize;
+
+ if (ContextHandle->ResumeId == 0xffffffff) {
+ //
+ // Reached the end of enumeration.
+ //
+ return WN_NO_MORE_ENTRIES;
+ }
+
+ while (FitInBuffer &&
+ EntriesRequested > *EntriesRead &&
+ status == NO_ERROR) {
+
+ if (ContextHandle->ResumeId == 0) {
+
+ //
+ // Get the first directory entry.
+ //
+ status = NwGetFirstDirectoryEntry(
+ ContextHandle->TreeConnectionHandle,
+ (LPWSTR *) &ContextHandle->ResumeId
+ );
+ }
+
+ //
+ // Either ResumeId contains the first entry we just got from
+ // NwGetFirstDirectoryEntry or it contains the next directory
+ // entry to return.
+ //
+ if (ContextHandle->ResumeId != 0) {
+
+ //
+ // Pack directory name into output buffer.
+ //
+ status = NwWriteNetResourceEntry(
+ &FixedPortion,
+ &EndOfVariableData,
+ ContextHandle->ContainerName,
+ NULL,
+ (LPWSTR) ContextHandle->ResumeId,
+ RESOURCE_GLOBALNET,
+ RESOURCEDISPLAYTYPE_SHARE,
+#ifdef NT1057
+ RESOURCEUSAGE_CONNECTABLE |
+ RESOURCEUSAGE_CONTAINER,
+#else
+ RESOURCEUSAGE_CONNECTABLE |
+ RESOURCEUSAGE_NOLOCALDEVICE,
+#endif
+ RESOURCETYPE_DISK,
+ NULL,
+ NULL,
+ &EntrySize
+ );
+
+ if (status == WN_MORE_DATA) {
+
+ //
+ // Could not write current entry into output buffer.
+ //
+
+ if (*EntriesRead) {
+ //
+ // Still return success because we got at least one.
+ //
+ status = NO_ERROR;
+ }
+ else {
+ *BytesNeeded = EntrySize;
+ }
+
+ FitInBuffer = FALSE;
+ }
+ else if (status == NO_ERROR) {
+
+ //
+ // Note that we've returned the current entry.
+ //
+ (*EntriesRead)++;
+
+ //
+ // Free memory allocated to save resume point, which is
+ // a buffer that contains the last directory we returned.
+ //
+ if (ContextHandle->ResumeId != 0) {
+ (void) LocalFree((HLOCAL) ContextHandle->ResumeId);
+ ContextHandle->ResumeId = 0;
+ }
+
+ //
+ // Get next directory entry.
+ //
+ status = NwGetNextDirectoryEntry(
+ (LPWSTR) ContextHandle->TreeConnectionHandle,
+ (LPWSTR *) &ContextHandle->ResumeId
+ );
+
+ }
+ }
+
+ if (status == WN_NO_MORE_ENTRIES) {
+ ContextHandle->ResumeId = 0xffffffff;
+ }
+ }
+
+ //
+ // User asked for more than there are entries. We just say that
+ // all is well.
+ //
+ // This is incompliance with the wierd provider API definition where
+ // if user gets NO_ERROR, and EntriesRequested > *EntriesRead, and
+ // at least one entry fit into output buffer, there's no telling if
+ // the buffer was too small for more entries or there are no more
+ // entries. The user has to call this API again and get WN_NO_MORE_ENTRIES
+ // before knowing that the last call had actually reached the end of list.
+ //
+ if (*EntriesRead && status == WN_NO_MORE_ENTRIES) {
+ status = NO_ERROR;
+ }
+
+#if DBG
+ IF_DEBUG(ENUM) {
+ KdPrint(("EnumDirectories returns %lu\n", status));
+ }
+#endif
+
+ return status;
+}
+
+
+DWORD
+NwEnumPrintServers(
+ IN LPNW_ENUM_CONTEXT ContextHandle,
+ IN DWORD EntriesRequested,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT LPDWORD BytesNeeded,
+ OUT LPDWORD EntriesRead
+ )
+/*++
+
+Routine Description:
+
+ This function enumerates all the servers and NDS tree on the local network
+ by scanning the bindery for file server or directory objects on the
+ preferred server. The server and tree entries are returned in an
+ array of PRINTER_INFO_1 entries; each entry name is prefixed by
+ \\.
+
+ The ContextHandle->ResumeId field is initially 0xffffffff before
+ enumeration begins and contains the object ID of the last server
+ object returned.
+
+Arguments:
+
+ ContextHandle - Supplies the enum context handle.
+
+ EntriesRequested - Supplies the number of entries to return. If
+ this value is 0xffffffff, return all available entries.
+
+ Buffer - Receives the entries we are listing.
+
+ BufferSize - Supplies the size of the output buffer.
+
+ BytesNeeded - Receives the number of bytes copied or required to get all
+ the requested entries.
+
+ EntriesRead - Receives the number of entries returned in Buffer.
+ This value is only returned iff NO_ERROR is the return code.
+
+Return Value:
+
+ NO_ERROR - Buffer contains all the entries requested.
+
+ WN_NO_MORE_ENTRIES - No entries left to return.
+
+ WN_MORE_DATA - The buffer was too small to fit the requested entries.
+
+--*/ // NwEnumPrintServers
+{
+ DWORD status = NO_ERROR;
+
+ LPBYTE FixedPortion = Buffer;
+ LPWSTR EndOfVariableData = (LPWSTR) ((DWORD) FixedPortion +
+ ROUND_DOWN_COUNT(BufferSize,ALIGN_DWORD));
+
+ DWORD EntrySize;
+ BOOL FitInBuffer = TRUE;
+
+ SERVERNAME ServerName; // OEM server name
+ LPWSTR UServerName = NULL; // Unicode server name
+ DWORD LastObjectId = ContextHandle->ResumeId;
+ WCHAR TempBuffer[50];
+
+ while ( EntriesRequested > *EntriesRead &&
+ ContextHandle->dwUsingNds == CURRENTLY_ENUMERATING_NDS &&
+ ((status == NO_ERROR) || (status == ERROR_INSUFFICIENT_BUFFER)))
+ {
+ //
+ // Call the scan bindery object NCP to scan for all NDS
+ // tree objects.
+ //
+ status = NwGetNextNdsTreeEntry( ContextHandle );
+
+ if ( status == NO_ERROR && ContextHandle->ResumeId != 0 )
+ {
+ //
+ // Put tree name into a buffer prefixed with a ' ' <space> character
+ // so that PrintMan displays trees as a group before servers.
+ //
+ RtlZeroMemory( TempBuffer, 50 );
+ wcscpy( TempBuffer, L" " );
+ wcscat( TempBuffer, (LPWSTR) ContextHandle->ResumeId );
+
+ //
+ // Pack server name into output buffer.
+ //
+ status = NwWritePrinterInfoEntry(
+ &FixedPortion,
+ &EndOfVariableData,
+ NULL,
+ TempBuffer, // This is a NDS tree name
+ PRINTER_ENUM_CONTAINER | PRINTER_ENUM_ICON1,
+ &EntrySize
+ );
+
+ switch ( status )
+ {
+ case ERROR_INSUFFICIENT_BUFFER:
+ FitInBuffer = FALSE;
+ // Falls through
+
+ case NO_ERROR:
+ *BytesNeeded += EntrySize;
+ (*EntriesRead)++;
+ // ContextHandle->ResumeId = LastObjectId;
+ break;
+
+ default:
+ break;
+ }
+ }
+ else if ( status == WN_NO_MORE_ENTRIES )
+ {
+ //
+ // We processed the last item in list, so
+ // start enumerating servers.
+ //
+ ContextHandle->dwUsingNds = CURRENTLY_ENUMERATING_NON_NDS;
+ ContextHandle->ResumeId = 0xFFFFFFFF;
+ LastObjectId = 0xFFFFFFFF;
+ }
+ }
+
+ status = NO_ERROR;
+
+ while ( EntriesRequested > *EntriesRead &&
+ ContextHandle->dwUsingNds == CURRENTLY_ENUMERATING_NON_NDS &&
+ ((status == NO_ERROR) || (status == ERROR_INSUFFICIENT_BUFFER))) {
+
+ RtlZeroMemory(ServerName, sizeof(ServerName));
+
+ //
+ // Call the scan bindery object NCP to scan for all file
+ // server objects.
+ //
+ status = NwGetNextServerEntry(
+ ContextHandle->TreeConnectionHandle,
+ &LastObjectId,
+ ServerName
+ );
+
+ if (status == NO_ERROR && NwConvertToUnicode(&UServerName,ServerName)) {
+
+ //
+ // Pack server name into output buffer.
+ //
+ status = NwWritePrinterInfoEntry(
+ &FixedPortion,
+ &EndOfVariableData,
+ NULL,
+ UServerName,
+ PRINTER_ENUM_CONTAINER | PRINTER_ENUM_ICON3,
+ &EntrySize
+ );
+
+ switch ( status )
+ {
+ case ERROR_INSUFFICIENT_BUFFER:
+ FitInBuffer = FALSE;
+ // Falls through
+
+ case NO_ERROR:
+ *BytesNeeded += EntrySize;
+ (*EntriesRead)++;
+ ContextHandle->ResumeId = LastObjectId;
+ break;
+
+ default:
+ break;
+ }
+
+ (void) LocalFree((HLOCAL) UServerName);
+ }
+ }
+
+ //
+ // This is incompliance with the wierd provider API definition where
+ // if user gets NO_ERROR, and EntriesRequested > *EntriesRead, and
+ // at least one entry fit into output buffer, there's no telling if
+ // the buffer was too small for more entries or there are no more
+ //
+ // This is incompliance with the wierd provider API definition where
+ // if user gets NO_ERROR, and EntriesRequested > *EntriesRead, and
+ // at least one entry fit into output buffer, there's no telling if
+ // the buffer was too small for more entries or there are no more
+ // entries. The user has to call this API again and get WN_NO_MORE_ENTRIES
+ // before knowing that the last call had actually reached the end of list.
+ //
+ if ( !FitInBuffer ) {
+ *EntriesRead = 0;
+ status = ERROR_INSUFFICIENT_BUFFER;
+ }
+ else if (*EntriesRead && status == WN_NO_MORE_ENTRIES) {
+ status = NO_ERROR;
+ }
+
+ return status;
+}
+
+
+DWORD
+NwEnumPrintQueues(
+ IN LPNW_ENUM_CONTEXT ContextHandle,
+ IN DWORD EntriesRequested,
+ OUT LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT LPDWORD BytesNeeded,
+ OUT LPDWORD EntriesRead
+ )
+/*++
+
+Routine Description:
+
+ This function enumerates all the print queues on a server by scanning
+ the bindery on the server for print queues objects.
+ The print queues entries are returned in an array of PRINTER_INFO_1 entries
+ and each printer name is prefixed by \\Server\.
+
+ The ContextHandle->ResumeId field is initially 0xffffffff before
+ enumeration begins and contains the object ID of the last print queue
+ object returned.
+
+Arguments:
+
+ ContextHandle - Supplies the enum context handle.
+
+ EntriesRequested - Supplies the number of entries to return. If
+ this value is 0xffffffff, return all available entries.
+
+ Buffer - Receives the entries we are listing.
+
+ BufferSize - Supplies the size of the output buffer.
+
+ BytesNeeded - Receives the number of bytes copied or required to get all
+ the requested entries.
+
+ EntriesRead - Receives the number of entries returned in Buffer.
+ This value is only returned iff NO_ERROR is the return code.
+
+Return Value:
+
+ NO_ERROR - Buffer contains all the entries requested.
+
+ WN_NO_MORE_ENTRIES - No entries left to return.
+
+ WN_MORE_DATA - The buffer was too small to fit the requested entries.
+
+--*/ // NwEnumPrintQueues
+{
+ DWORD status = NO_ERROR;
+
+ LPBYTE FixedPortion = Buffer;
+ LPWSTR EndOfVariableData = (LPWSTR) ((DWORD) FixedPortion +
+ ROUND_DOWN_COUNT(BufferSize,ALIGN_DWORD));
+
+ DWORD EntrySize;
+ BOOL FitInBuffer = TRUE;
+
+ SERVERNAME QueueName; // OEM queue name
+ LPWSTR UQueueName = NULL; // Unicode queue name
+ DWORD LastObjectId = ContextHandle->ResumeId;
+
+ while ( EntriesRequested > *EntriesRead &&
+ ( (status == NO_ERROR) || (status == ERROR_INSUFFICIENT_BUFFER))) {
+
+ RtlZeroMemory(QueueName, sizeof(QueueName));
+
+ //
+ // Call the scan bindery object NCP to scan for all file
+ // volume objects.
+ //
+ status = NwGetNextQueueEntry(
+ ContextHandle->TreeConnectionHandle,
+ &LastObjectId,
+ QueueName
+ );
+
+ if (status == NO_ERROR && NwConvertToUnicode(&UQueueName, QueueName)) {
+
+ //
+ // Pack server name into output buffer.
+ //
+ status = NwWritePrinterInfoEntry(
+ &FixedPortion,
+ &EndOfVariableData,
+ ContextHandle->ContainerName,
+ UQueueName,
+ PRINTER_ENUM_ICON8,
+ &EntrySize
+ );
+
+ switch ( status )
+ {
+ case ERROR_INSUFFICIENT_BUFFER:
+ FitInBuffer = FALSE;
+ // Falls through
+
+ case NO_ERROR:
+ *BytesNeeded += EntrySize;
+ (*EntriesRead)++;
+ ContextHandle->ResumeId = LastObjectId;
+ break;
+
+ default:
+ break;
+ }
+
+ (void) LocalFree((HLOCAL) UQueueName);
+ }
+ }
+
+ //
+ // This is incompliance with the wierd provider API definition where
+ // if user gets NO_ERROR, and EntriesRequested > *EntriesRead, and
+ // at least one entry fit into output buffer, there's no telling if
+ // the buffer was too small for more entries or there are no more
+ // entries. The user has to call this API again and get WN_NO_MORE_ENTRIES
+ // before knowing that the last call had actually reached the end of list.
+ //
+ if ( !FitInBuffer ) {
+ *EntriesRead = 0;
+ status = ERROR_INSUFFICIENT_BUFFER;
+ }
+ else if (*EntriesRead && status == WN_NO_MORE_ENTRIES) {
+ status = NO_ERROR;
+ }
+
+ return status;
+}
+
+
+DWORD
+NwrCloseEnum(
+ IN OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
+ )
+/*++
+
+Routine Description:
+
+ This function closes an enum context handle.
+
+Arguments:
+
+ EnumHandle - Supplies a pointer to the enum context handle.
+
+Return Value:
+
+ WN_BAD_HANDLE - Handle is not recognizable.
+
+ NO_ERROR - Call was successful.
+
+--*/ // NwrCloseEnum
+{
+
+ LPNW_ENUM_CONTEXT ContextHandle = (LPNW_ENUM_CONTEXT) *EnumHandle;
+ DWORD status = NO_ERROR ;
+
+#if DBG
+ IF_DEBUG(ENUM)
+ {
+ KdPrint(("\nNWWORKSTATION: NwrCloseEnum\n"));
+ }
+#endif
+
+ if (ContextHandle->Signature != NW_HANDLE_SIGNATURE)
+ {
+ ASSERT(FALSE);
+ return WN_BAD_HANDLE;
+ }
+
+ //
+ // Resume handle for listing directories is a buffer which contains
+ // the last directory returned.
+ //
+ if (ContextHandle->HandleType == NwsHandleListDirectories &&
+ ContextHandle->ResumeId != 0 &&
+ ContextHandle->ResumeId != 0xFFFFFFFF)
+ {
+ (void) LocalFree((HLOCAL) ContextHandle->ResumeId);
+ }
+
+ //
+ // NdsRawDataBuffer handle for listing NDS tree subordinates is a buffer which contains
+ // the last data chunk returned from redirector.
+ //
+ if ( ( ContextHandle->HandleType == NwsHandleListNdsSubTrees_Disk ||
+ ContextHandle->HandleType == NwsHandleListNdsSubTrees_Print ||
+ ContextHandle->HandleType == NwsHandleListNdsSubTrees_Any ||
+ ContextHandle->HandleType == NwsHandleListServersAndNdsTrees ) &&
+ ContextHandle->NdsRawDataBuffer )
+ {
+ (void) LocalFree((HLOCAL) ContextHandle->NdsRawDataBuffer);
+ ContextHandle->NdsRawDataBuffer = 0;
+ }
+
+ if (ContextHandle->TreeConnectionHandle != (HANDLE) NULL)
+ {
+ if (ContextHandle->HandleType == NwsHandleListDirectories)
+ {
+ //
+ // Delete the UNC connection created so that we can browse
+ // directories.
+ //
+ (void) NwNukeConnection(ContextHandle->TreeConnectionHandle, TRUE);
+ }
+
+ if ( ContextHandle->HandleType == NwsHandleListNdsSubTrees_Disk ||
+ ContextHandle->HandleType == NwsHandleListNdsSubTrees_Print ||
+ ContextHandle->HandleType == NwsHandleListNdsSubTrees_Any )
+ {
+ //
+ // Get rid of the connection to the NDS tree.
+ //
+ (void) CloseHandle(ContextHandle->TreeConnectionHandle);
+ ContextHandle->TreeConnectionHandle = 0;
+ }
+ else
+ {
+ (void) NtClose(ContextHandle->TreeConnectionHandle);
+ ContextHandle->TreeConnectionHandle = 0;
+ }
+ }
+
+ ContextHandle->Signature = 0x0BADBAD0;
+ (void) LocalFree((HLOCAL) ContextHandle);
+
+ *EnumHandle = NULL;
+
+ return status;
+}
+
+
+DWORD
+NwrGetUser(
+ IN LPWSTR Reserved OPTIONAL,
+ IN LPWSTR lpName,
+ OUT LPBYTE lpUserName,
+ IN DWORD dwUserNameBufferSize,
+ OUT LPDWORD lpdwCharsRequired
+ )
+/*++
+
+Routine Description:
+
+ This is used to determine either the current default username, or the
+ username used to establish a network connection.
+
+Arguments:
+
+ Reserved - Unused.
+
+ lpName - The connection for which user information is requested.
+
+ lpUserName - The buffer to receive the user name associated with the
+ connection referred to by lpName.
+
+ dwUserNameLen - The size of the buffer lpUserName.
+
+ lpdwCharsRequired - If return status is WN_MORE_DATA, then this is set to
+ the value which indicates the number of characters that the buffer
+ lpUserName must hold. Otherwise, this is not set.
+
+
+Return Value:
+
+ WN_SUCCESS - If the call is successful. Otherwise, an error code is,
+ returned, which may include:
+
+ WN_NOT_CONNECTED - lpName not a redirected device nor a connected network
+ name.
+
+ WN_MORE_DATA - The buffer is too small.
+
+--*/ // NwrGetUser
+{
+ DWORD status = NO_ERROR;
+ WCHAR lpTempUserName[512];
+ WCHAR lpTempHostName[512];
+
+ status = NwGetConnectionInformation( lpName, lpTempUserName, lpTempHostName );
+
+ if ( status != NO_ERROR )
+ {
+ return status;
+ }
+
+ if ( ( ( wcslen( lpTempUserName ) + 1 ) * sizeof(WCHAR) ) > dwUserNameBufferSize )
+ {
+ *lpdwCharsRequired = wcslen( lpTempUserName ) + 1;
+ return WN_MORE_DATA;
+ }
+
+ wcscpy( (LPWSTR) lpUserName, lpTempUserName );
+
+ return WN_SUCCESS;
+}
+
+
+DWORD
+NwrGetResourceInformation(
+ IN LPWSTR Reserved OPTIONAL,
+ IN LPWSTR lpRemoteName,
+ IN DWORD dwType,
+ OUT LPBYTE lpBuffer,
+ IN DWORD dwBufferSize,
+ OUT LPDWORD lpdwBytesNeeded,
+ OUT LPDWORD lpdwSystemOffset
+ )
+/*++
+
+Routine Description:
+
+ This function returns an object which details information
+ about a specified network resource.
+
+Arguments:
+
+ Reserved - Unused.
+ lpRemoteName - The full path name to be verified.
+ dwType - The type of the value, if the calling client knows it.
+ lpBuffer - A pointer to a buffer to receive a single NETRESOURCE entry.
+ dwBufferSize - The size of the buffer.
+ lpdwBytesNeeded - The buffer size needed if WN_MORE_DATA is returned.
+ lpdwSystemOffset - A DWORD that is an offset value to the beginning of a
+ string that specifies the part of the resource that is accessed through
+ resource type specific APIs rather than WNet APIs. The string is stored
+ in the same buffer as the returned NETRESOURCE structure, lpBuffer.
+
+Return Value:
+
+ WN_SUCCESS - If the call is successful.
+
+ WN_MORE_DATA - If input buffer is too small.
+
+ WN_BAD_VALUE - Invalid dwScope or dwUsage or dwType, or bad combination
+ of parameters is specified (e.g. lpRemoteName does not correspond
+ to dwType).
+
+ WN_BAD_NETNAME - The resource is not recognized by this provider.
+
+--*/ // NwrGetResourceInformation
+{
+ DWORD status = NO_ERROR;
+ DWORD EntrySize;
+
+ LPBYTE FixedPortion = lpBuffer;
+ LPWSTR EndOfVariableData = (LPWSTR) ((DWORD) FixedPortion +
+ ROUND_DOWN_COUNT(dwBufferSize,ALIGN_DWORD));
+ LPWSTR lpObjectPathName = NULL;
+ LPWSTR lpSystemPathPart = NULL;
+ LPWSTR lpSystem = NULL;
+ DWORD ClassType;
+ DWORD ResourceScope;
+ DWORD ResourceType;
+ DWORD ResourceDisplayType;
+ DWORD ResourceUsage;
+ BOOL fReturnBadNetName = FALSE;
+
+ *lpdwSystemOffset = 0;
+
+ status = NwGetNDSPathInfo( lpRemoteName,
+ &lpObjectPathName,
+ &lpSystemPathPart,
+ &ClassType,
+ &ResourceScope,
+ &ResourceType,
+ &ResourceDisplayType,
+ &ResourceUsage );
+
+ if ( status == VERIFY_ERROR_NOT_A_NDS_TREE )
+ {
+ //
+ // Code to handle \\SERVER\VOL\... here!
+ //
+ status = NwGetBinderyPathInfo( lpRemoteName,
+ &lpObjectPathName,
+ &lpSystemPathPart,
+ &ClassType,
+ &ResourceScope,
+ &ResourceType,
+ &ResourceDisplayType,
+ &ResourceUsage );
+ }
+
+ if ( status == VERIFY_ERROR_PATH_NOT_FOUND )
+ {
+ fReturnBadNetName = TRUE;
+ status = NO_ERROR;
+ }
+
+ if ( status == NO_ERROR &&
+ dwType != RESOURCETYPE_ANY &&
+ ResourceType != RESOURCETYPE_ANY &&
+ dwType != ResourceType )
+ {
+ status = WN_BAD_VALUE;
+ }
+
+ if ( status == NO_ERROR )
+ {
+ //
+ // Pack subtree name into output buffer.
+ //
+ status = NwWriteNetResourceEntry( &FixedPortion,
+ &EndOfVariableData,
+ NULL,
+ NULL,
+ lpObjectPathName == NULL ? NwProviderName : lpObjectPathName,
+ ResourceScope,
+ ResourceDisplayType,
+ ResourceUsage,
+ ResourceType,
+ lpSystemPathPart,
+ &lpSystem,
+ &EntrySize );
+
+ if ( lpObjectPathName )
+ (void) LocalFree( (HLOCAL) lpObjectPathName );
+ }
+ else
+ {
+ if ( lpSystemPathPart != NULL )
+ {
+ (void) LocalFree( (HLOCAL) lpSystemPathPart );
+ lpSystemPathPart = NULL;
+ }
+
+ return status;
+ }
+
+ if ( status != NO_ERROR )
+ {
+ if (status == WN_MORE_DATA)
+ {
+ //
+ // Could not write current entry into output buffer.
+ //
+ *lpdwBytesNeeded = EntrySize;
+ }
+
+ if ( lpSystemPathPart != NULL )
+ {
+ (void) LocalFree( (HLOCAL) lpSystemPathPart );
+ lpSystemPathPart = NULL;
+ }
+
+ if ( fReturnBadNetName )
+ return WN_BAD_NETNAME;
+
+ return status;
+ }
+ else
+ {
+ LPNETRESOURCEW NetR = (LPNETRESOURCEW) lpBuffer;
+
+ //
+ // Replace pointers to strings with offsets as need
+ //
+
+ if (NetR->lpLocalName != NULL)
+ {
+ NetR->lpLocalName = (LPWSTR) ((DWORD) (NetR->lpLocalName) - (DWORD) lpBuffer);
+ }
+
+ NetR->lpRemoteName = (LPWSTR) ((DWORD) (NetR->lpRemoteName) - (DWORD) lpBuffer);
+
+ if (NetR->lpComment != NULL)
+ {
+ NetR->lpComment = (LPWSTR) ((DWORD) (NetR->lpComment) - (DWORD) lpBuffer);
+ }
+
+ if (NetR->lpProvider != NULL)
+ {
+ NetR->lpProvider = (LPWSTR) ((DWORD) (NetR->lpProvider) - (DWORD) lpBuffer);
+ }
+
+ if (lpSystem != NULL)
+ {
+ *lpdwSystemOffset = (DWORD) lpSystem - (DWORD) lpBuffer;
+ }
+
+ if ( lpSystemPathPart != NULL )
+ {
+ (void) LocalFree( (HLOCAL) lpSystemPathPart );
+ lpSystemPathPart = NULL;
+ }
+
+ if ( fReturnBadNetName )
+ return WN_BAD_NETNAME;
+
+ return WN_SUCCESS;
+ }
+}
+
+
+DWORD
+NwrGetResourceParent(
+ IN LPWSTR Reserved OPTIONAL,
+ IN LPWSTR lpRemoteName,
+ IN DWORD dwType,
+ OUT LPBYTE lpBuffer,
+ IN DWORD dwBufferSize,
+ OUT LPDWORD lpdwBytesNeeded
+ )
+/*++
+
+Routine Description:
+
+ This function returns an object which details information
+ about the parent of a specified network resource.
+
+Arguments:
+
+ Reserved - Unused.
+ lpRemoteName - The full path name of object to find the parent of.
+ dwType - The type of the value, if the calling client knows it.
+ lpBuffer - A pointer to a buffer to receive a single NETRESOURCE entry.
+ dwBufferSize - The size of the buffer.
+ lpdwBytesNeeded - The buffer size needed if WN_MORE_DATA is returned.
+
+Return Value:
+
+ WN_SUCCESS - If the call is successful.
+
+ WN_MORE_DATA - If input buffer is too small.
+
+ WN_BAD_VALUE - Invalid dwScope or dwUsage or dwType, or bad combination
+ of parameters is specified (e.g. lpRemoteName does not correspond
+ to dwType).
+
+--*/ // NwrGetResourceParent
+{
+ DWORD status = NO_ERROR;
+ DWORD EntrySize;
+
+ LPBYTE FixedPortion = lpBuffer;
+ LPWSTR EndOfVariableData = (LPWSTR) ((DWORD) FixedPortion +
+ ROUND_DOWN_COUNT(dwBufferSize,ALIGN_DWORD));
+ LPWSTR lpRemoteNameParent = NULL;
+ LPWSTR lpFullObjectPathName = NULL;
+ DWORD ClassType;
+ DWORD ResourceScope;
+ DWORD ResourceType;
+ DWORD ResourceDisplayType;
+ DWORD ResourceUsage;
+ BOOL fReturnBadNetName = FALSE;
+
+ if ( ! NwGetRemoteNameParent( lpRemoteName, &lpRemoteNameParent ) )
+ {
+ return WN_BAD_NETNAME;
+ }
+
+ status = NwVerifyNDSObject( lpRemoteNameParent,
+ &lpFullObjectPathName,
+ &ClassType,
+ &ResourceScope,
+ &ResourceType,
+ &ResourceDisplayType,
+ &ResourceUsage );
+
+ if ( status == VERIFY_ERROR_NOT_A_NDS_TREE )
+ {
+ status = NwVerifyBinderyObject( lpRemoteNameParent,
+ &lpFullObjectPathName,
+ &ClassType,
+ &ResourceScope,
+ &ResourceType,
+ &ResourceDisplayType,
+ &ResourceUsage );
+ }
+
+ if ( lpRemoteNameParent )
+ (void) LocalFree( (HLOCAL) lpRemoteNameParent );
+
+ if ( status == VERIFY_ERROR_PATH_NOT_FOUND )
+ {
+ fReturnBadNetName = TRUE;
+ status = NO_ERROR;
+ }
+
+ if ( status == NO_ERROR )
+ {
+ //
+ // Pack subtree name into output buffer.
+ //
+ status = NwWriteNetResourceEntry( &FixedPortion,
+ &EndOfVariableData,
+ NULL,
+ NULL,
+ lpFullObjectPathName == NULL ? NwProviderName : lpFullObjectPathName,
+ ResourceScope,
+ ResourceDisplayType,
+ ResourceUsage,
+ ResourceType,
+ NULL,
+ NULL,
+ &EntrySize );
+
+ if ( lpFullObjectPathName )
+ (void) LocalFree( (HLOCAL) lpFullObjectPathName );
+ }
+ else
+ {
+ return status;
+ }
+
+ if ( status != NO_ERROR )
+ {
+ if (status == WN_MORE_DATA)
+ {
+ //
+ // Could not write current entry into output buffer.
+ //
+ *lpdwBytesNeeded = EntrySize;
+ }
+
+ if ( fReturnBadNetName )
+ return WN_BAD_NETNAME;
+
+ return status;
+ }
+ else
+ {
+ LPNETRESOURCEW NetR = (LPNETRESOURCEW) lpBuffer;
+
+ //
+ // Replace pointers to strings with offsets as need
+ //
+
+ if (NetR->lpLocalName != NULL)
+ {
+ NetR->lpLocalName = (LPWSTR) ((DWORD) (NetR->lpLocalName) - (DWORD) lpBuffer);
+ }
+
+ NetR->lpRemoteName = (LPWSTR) ((DWORD) (NetR->lpRemoteName) - (DWORD) lpBuffer);
+
+ if (NetR->lpComment != NULL)
+ {
+ NetR->lpComment = (LPWSTR) ((DWORD) (NetR->lpComment) - (DWORD) lpBuffer);
+ }
+
+ if (NetR->lpProvider != NULL)
+ {
+ NetR->lpProvider = (LPWSTR) ((DWORD) (NetR->lpProvider) - (DWORD) lpBuffer);
+ }
+
+ if ( fReturnBadNetName )
+ return WN_BAD_NETNAME;
+
+ return WN_SUCCESS;
+ }
+}
+
+
+VOID
+NWWKSTA_CONTEXT_HANDLE_rundown(
+ IN NWWKSTA_CONTEXT_HANDLE EnumHandle
+ )
+/*++
+
+Routine Description:
+
+ This function is called by RPC when a client terminates with an
+ opened handle. This allows us to clean up and deallocate any context
+ data associated with the handle.
+
+Arguments:
+
+ EnumHandle - Supplies the handle opened for an enumeration.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ //
+ // Call our close handle routine.
+ //
+ NwrCloseEnum(&EnumHandle);
+}
+
+
+DWORD
+NwGetFirstNdsSubTreeEntry(
+ OUT LPNW_ENUM_CONTEXT ContextHandle,
+ IN DWORD BufferSize
+ )
+/*++
+
+Routine Description:
+
+ This function is called by NwEnumNdsSubTrees to get the first
+ subtree entry given a handle to a NDS tree. It allocates
+ the output buffer to hold the returned subtree name; the
+ caller should free this output buffer with LocalFree when done.
+
+Arguments:
+
+Return Value:
+
+ NO_ERROR - The operation was successful.
+
+ ERROR_NOT_ENOUGH_MEMORY - Out of memory allocating output
+ buffer.
+
+ Other errors from NwNdsList.
+
+--*/ // NwGetFirstNdsSubTreeEntry
+{
+ NTSTATUS ntstatus;
+
+ ContextHandle->NdsRawDataSize = BufferSize;
+
+ //
+ // Determine size of NDS raw data buffer to use.
+ //
+ if ( ContextHandle->NdsRawDataSize < EIGHT_KB )
+ ContextHandle->NdsRawDataSize = EIGHT_KB;
+
+ //
+ // Create NDS raw data buffer.
+ //
+ ContextHandle->NdsRawDataBuffer = (DWORD)
+ LocalAlloc( LMEM_ZEROINIT,
+ ContextHandle->NdsRawDataSize );
+
+ if ( ContextHandle->NdsRawDataBuffer == 0 )
+ {
+ KdPrint(("NWWORKSTATION: NwGetFirstNdsSubTreeEntry LocalAlloc Failed %lu\n", GetLastError()));
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ //
+ // Set up to get initial NDS subordinate list.
+ //
+ ContextHandle->NdsRawDataId = INITIAL_ITERATION;
+
+ ntstatus = NwNdsList( ContextHandle->TreeConnectionHandle,
+ ContextHandle->dwOid,
+ &ContextHandle->NdsRawDataId,
+ (LPBYTE) ContextHandle->NdsRawDataBuffer,
+ ContextHandle->NdsRawDataSize );
+
+ //
+ // If error, clean up the ContextHandle and return.
+ //
+ if ( ntstatus != STATUS_SUCCESS ||
+ ((PNDS_RESPONSE_SUBORDINATE_LIST)
+ ContextHandle->NdsRawDataBuffer)->SubordinateEntries == 0 )
+ {
+ if ( ContextHandle->NdsRawDataBuffer )
+ (void) LocalFree( (HLOCAL) ContextHandle->NdsRawDataBuffer );
+ ContextHandle->NdsRawDataBuffer = 0;
+ ContextHandle->NdsRawDataSize = 0;
+ ContextHandle->NdsRawDataId = INITIAL_ITERATION;
+ ContextHandle->NdsRawDataCount = 0;
+ ContextHandle->ResumeId = 0;
+
+ return WN_NO_MORE_ENTRIES;
+ }
+
+ ContextHandle->NdsRawDataCount = ((PNDS_RESPONSE_SUBORDINATE_LIST)
+ ContextHandle->NdsRawDataBuffer)->SubordinateEntries - 1;
+
+ ContextHandle->ResumeId = ContextHandle->NdsRawDataBuffer +
+ sizeof( NDS_RESPONSE_SUBORDINATE_LIST );
+
+ return RtlNtStatusToDosError(ntstatus);
+}
+
+
+DWORD
+NwGetNextNdsSubTreeEntry(
+ OUT LPNW_ENUM_CONTEXT ContextHandle
+ )
+/*++
+
+Routine Description:
+
+ This function is called by NwEnumNdsSubTrees to get the next
+ NDS subtree entry given a handle to a NDS tree. It allocates
+ the output buffer to hold the returned subtree name; the
+ caller should free this output buffer with LocalFree when done.
+
+Arguments:
+
+Return Value:
+
+ NO_ERROR - The operation was successful.
+
+ ERROR_NOT_ENOUGH_MEMORY - Out of memory allocating output
+ buffer.
+
+ Other errors from NwNdsList.
+
+--*/ // NwGetNextDirectoryEntry
+{
+ NTSTATUS ntstatus = STATUS_SUCCESS;
+ PBYTE pbRaw;
+ DWORD dwStrLen;
+
+
+ if ( ContextHandle->NdsRawDataCount == 0 &&
+ ContextHandle->NdsRawDataId == INITIAL_ITERATION )
+ return WN_NO_MORE_ENTRIES;
+
+ if ( ContextHandle->NdsRawDataCount == 0 &&
+ ContextHandle->NdsRawDataId != INITIAL_ITERATION )
+ {
+ ntstatus = NwNdsList( ContextHandle->TreeConnectionHandle,
+ ContextHandle->dwOid,
+ &ContextHandle->NdsRawDataId,
+ (LPBYTE) ContextHandle->NdsRawDataBuffer,
+ ContextHandle->NdsRawDataSize );
+
+ //
+ // If error, clean up the ContextHandle and return.
+ //
+ if (ntstatus != STATUS_SUCCESS)
+ {
+ if ( ContextHandle->NdsRawDataBuffer )
+ (void) LocalFree( (HLOCAL) ContextHandle->NdsRawDataBuffer );
+ ContextHandle->NdsRawDataBuffer = 0;
+ ContextHandle->NdsRawDataSize = 0;
+ ContextHandle->NdsRawDataId = INITIAL_ITERATION;
+ ContextHandle->NdsRawDataCount = 0;
+
+ return WN_NO_MORE_ENTRIES;
+ }
+
+ ContextHandle->NdsRawDataCount = ((PNDS_RESPONSE_SUBORDINATE_LIST)
+ ContextHandle->NdsRawDataBuffer)->SubordinateEntries - 1;
+
+ ContextHandle->ResumeId = ContextHandle->NdsRawDataBuffer +
+ sizeof( NDS_RESPONSE_SUBORDINATE_LIST );
+
+ return RtlNtStatusToDosError(ntstatus);
+ }
+
+ ContextHandle->NdsRawDataCount--;
+
+ //
+ // Move pointer past the fixed header portion of a NDS_RESPONSE_SUBORDINATE_ENTRY
+ //
+ pbRaw = (BYTE *) ContextHandle->ResumeId;
+ pbRaw += sizeof( NDS_RESPONSE_SUBORDINATE_ENTRY );
+
+ //
+ // Move pointer past the length value of the Class Name string
+ // of a NDS_RESPONSE_SUBORDINATE_ENTRY
+ //
+ dwStrLen = * (DWORD *) pbRaw;
+ pbRaw += sizeof( DWORD );
+
+ //
+ // Move pointer past the Class Name string of a NDS_RESPONSE_SUBORDINATE_ENTRY
+ //
+ pbRaw += ROUNDUP4( dwStrLen );
+
+ //
+ // Move pointer past the length value of the Object Name string
+ // of a NDS_RESPONSE_SUBORDINATE_ENTRY
+ //
+ dwStrLen = * (DWORD *) pbRaw;
+ pbRaw += sizeof( DWORD );
+
+ ContextHandle->ResumeId = (DWORD) ( pbRaw + ROUNDUP4( dwStrLen ) );
+
+ return RtlNtStatusToDosError(ntstatus);
+}
+
+
+BYTE
+NwGetSubTreeData(
+ IN DWORD NdsRawDataPtr,
+ OUT LPDWORD SubTreeName,
+ OUT LPDWORD ResourceScope,
+ OUT LPDWORD ResourceType,
+ OUT LPDWORD ResourceDisplayType,
+ OUT LPDWORD ResourceUsage,
+ OUT LPWSTR * StrippedObjectName
+ )
+/*++
+
+Routine Description:
+
+ This function is called by NwEnumNdsSubTrees to get the information
+ needed to describe a single NETRESOURCE from an entry in the
+ NdsRawDataBuffer.
+
+Arguments:
+
+ NdsRawDataPtr - Supplies the pointer to a buffer with the NDS raw data.
+
+ SubTreeName - Receives a pointer to the returned subtree object name
+ found in buffer.
+
+ ResourceScope - Receives the value of the scope for the subtree object
+ found in buffer.
+
+ ResourceType - Receives the value of the type for the subtree object
+ found in buffer.
+
+ ResourceDisplayType - Receives the value of the display type for the
+ subtree object found in buffer.
+
+ ResourceUsage - Receives the value of the usage for the subtree object
+ found in buffer.
+
+ StrippedObjectName - A pointer to receive the address of a buffer which
+ will contain the formatted object name. Callee must
+ free buffer with LocalFree().
+
+Return Value:
+
+ A DWORD with a value that is used to represent NDS object class type..
+
+--*/ // NwGetSubTreeData
+{
+ PNDS_RESPONSE_SUBORDINATE_ENTRY pSubEntry =
+ (PNDS_RESPONSE_SUBORDINATE_ENTRY) NdsRawDataPtr;
+ PBYTE pbRaw;
+ DWORD dwStrLen;
+ LPWSTR ClassNameStr;
+
+ pbRaw = (BYTE *) pSubEntry;
+
+ //
+ // The structure of a NDS_RESPONSE_SUBORDINATE_ENTRY consists of 4 DWORDs
+ // followed by two standard NDS format UNICODE strings. Below we jump pbRaw
+ // into the buffer, past the 4 DWORDs.
+ //
+ pbRaw += sizeof( NDS_RESPONSE_SUBORDINATE_ENTRY );
+
+ //
+ // Now we get the length of the first string (Base Class).
+ //
+ dwStrLen = * (DWORD *) pbRaw;
+
+ //
+ // Now we point pbRaw to the first WCHAR of the first string (Base Class).
+ //
+ pbRaw += sizeof( DWORD );
+
+ ClassNameStr = (LPWSTR) pbRaw;
+
+ //
+ // Move pbRaw into the buffer, past the first UNICODE string (WORD aligned)
+ //
+ pbRaw += ROUNDUP4( dwStrLen );
+
+ //
+ // Now we get the length of the second string (Entry Name).
+ //
+ dwStrLen = * (DWORD *) pbRaw;
+
+ //
+ // Now we point pbRaw to the first WCHAR of the second string (Entry Name).
+ //
+ pbRaw += sizeof( DWORD );
+
+ *SubTreeName = (DWORD) pbRaw;
+
+ //
+ // Strip off any CN= stuff from the object name.
+ //
+ NwStripNdsUncName( (LPWSTR) *SubTreeName, StrippedObjectName );
+
+ *ResourceScope = RESOURCE_GLOBALNET;
+
+ if ( !wcscmp( ClassNameStr, CLASS_NAME_ALIAS ) )
+ {
+ *ResourceType = RESOURCETYPE_ANY;
+ *ResourceDisplayType = RESOURCEDISPLAYTYPE_GENERIC;
+ *ResourceUsage = 0;
+
+ return CLASS_TYPE_ALIAS;
+ }
+
+ if ( !wcscmp( ClassNameStr, CLASS_NAME_AFP_SERVER ) )
+ {
+ *ResourceType = RESOURCETYPE_ANY;
+ *ResourceDisplayType = RESOURCEDISPLAYTYPE_GENERIC;
+ *ResourceUsage = 0;
+
+ return CLASS_TYPE_AFP_SERVER;
+ }
+
+ if ( !wcscmp( ClassNameStr, CLASS_NAME_BINDERY_OBJECT ) )
+ {
+ *ResourceType = RESOURCETYPE_ANY;
+ *ResourceDisplayType = RESOURCEDISPLAYTYPE_GENERIC;
+ *ResourceUsage = 0;
+
+ return CLASS_TYPE_BINDERY_OBJECT;
+ }
+
+ if ( !wcscmp( ClassNameStr, CLASS_NAME_BINDERY_QUEUE ) )
+ {
+ *ResourceType = RESOURCETYPE_ANY;
+ *ResourceDisplayType = RESOURCEDISPLAYTYPE_GENERIC;
+ *ResourceUsage = 0;
+
+ return CLASS_TYPE_BINDERY_QUEUE;
+ }
+
+ if ( !wcscmp( ClassNameStr, CLASS_NAME_COMPUTER ) )
+ {
+ *ResourceType = RESOURCETYPE_ANY;
+ *ResourceDisplayType = RESOURCEDISPLAYTYPE_GENERIC;
+ *ResourceUsage = 0;
+
+ return CLASS_TYPE_COMPUTER;
+ }
+
+ if ( !wcscmp( ClassNameStr, CLASS_NAME_COUNTRY ) )
+ {
+ *ResourceType = RESOURCETYPE_ANY;
+ *ResourceDisplayType = RESOURCEDISPLAYTYPE_NDSCONTAINER;
+ *ResourceUsage = RESOURCEUSAGE_CONTAINER;
+
+ return CLASS_TYPE_COUNTRY;
+ }
+
+ if ( !wcscmp( ClassNameStr, CLASS_NAME_DIRECTORY_MAP ) )
+ {
+ *ResourceType = RESOURCETYPE_DISK;
+ *ResourceDisplayType = RESOURCEDISPLAYTYPE_SHARE;
+#ifdef NT1057
+ *ResourceUsage = RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_CONTAINER;
+#else
+ *ResourceUsage = RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_NOLOCALDEVICE;
+#endif
+
+ return CLASS_TYPE_DIRECTORY_MAP;
+ }
+
+ if ( !wcscmp( ClassNameStr, CLASS_NAME_GROUP ) )
+ {
+ *ResourceType = RESOURCETYPE_ANY;
+ *ResourceDisplayType = RESOURCEDISPLAYTYPE_GROUP;
+ *ResourceUsage = 0;
+
+ return CLASS_TYPE_GROUP;
+ }
+
+ if ( !wcscmp( ClassNameStr, CLASS_NAME_LOCALITY ) )
+ {
+ *ResourceType = RESOURCETYPE_ANY;
+ *ResourceDisplayType = RESOURCEDISPLAYTYPE_GENERIC;
+ *ResourceUsage = 0;
+
+ return CLASS_TYPE_LOCALITY;
+ }
+
+ if ( !wcscmp( ClassNameStr, CLASS_NAME_NCP_SERVER ) )
+ {
+ *ResourceType = RESOURCETYPE_ANY;
+ *ResourceDisplayType = RESOURCEDISPLAYTYPE_SERVER;
+ *ResourceUsage = RESOURCEUSAGE_CONTAINER;
+
+ return CLASS_TYPE_NCP_SERVER;
+ }
+
+ if ( !wcscmp( ClassNameStr, CLASS_NAME_ORGANIZATION ) )
+ {
+ *ResourceType = RESOURCETYPE_ANY;
+ *ResourceDisplayType = RESOURCEDISPLAYTYPE_NDSCONTAINER;
+ *ResourceUsage = RESOURCEUSAGE_CONTAINER;
+
+ return CLASS_TYPE_ORGANIZATION;
+ }
+
+ if ( !wcscmp( ClassNameStr, CLASS_NAME_ORGANIZATIONAL_ROLE ) )
+ {
+ *ResourceType = RESOURCETYPE_ANY;
+ *ResourceDisplayType = RESOURCEDISPLAYTYPE_GENERIC;
+ *ResourceUsage = 0;
+
+ return CLASS_TYPE_ORGANIZATIONAL_ROLE;
+ }
+
+ if ( !wcscmp( ClassNameStr, CLASS_NAME_ORGANIZATIONAL_UNIT ) )
+ {
+ *ResourceType = RESOURCETYPE_ANY;
+ *ResourceDisplayType = RESOURCEDISPLAYTYPE_NDSCONTAINER;
+ *ResourceUsage = RESOURCEUSAGE_CONTAINER;
+
+ return CLASS_TYPE_ORGANIZATIONAL_UNIT;
+ }
+
+ if ( !wcscmp( ClassNameStr, CLASS_NAME_PRINTER ) )
+ {
+ *ResourceType = RESOURCETYPE_PRINT;
+ *ResourceDisplayType = RESOURCEDISPLAYTYPE_SHARE;
+ *ResourceUsage = RESOURCEUSAGE_CONNECTABLE;
+
+ return CLASS_TYPE_PRINTER;
+ }
+
+ if ( !wcscmp( ClassNameStr, CLASS_NAME_PRINT_SERVER ) )
+ {
+ *ResourceType = RESOURCETYPE_PRINT;
+ *ResourceDisplayType = RESOURCEDISPLAYTYPE_SERVER;
+ *ResourceUsage = RESOURCEUSAGE_CONTAINER;
+
+ return CLASS_TYPE_PRINT_SERVER;
+ }
+
+ if ( !wcscmp( ClassNameStr, CLASS_NAME_PROFILE ) )
+ {
+ *ResourceType = RESOURCETYPE_ANY;
+ *ResourceDisplayType = RESOURCEDISPLAYTYPE_GENERIC;
+ *ResourceUsage = 0;
+
+ return CLASS_TYPE_PROFILE;
+ }
+
+ if ( !wcscmp( ClassNameStr, CLASS_NAME_QUEUE ) )
+ {
+ *ResourceType = RESOURCETYPE_PRINT;
+ *ResourceDisplayType = RESOURCEDISPLAYTYPE_SHARE;
+ *ResourceUsage = RESOURCEUSAGE_CONNECTABLE;
+
+ return CLASS_TYPE_QUEUE;
+ }
+
+ if ( !wcscmp( ClassNameStr, CLASS_NAME_TOP ) )
+ {
+ *ResourceType = RESOURCETYPE_ANY;
+ *ResourceDisplayType = RESOURCEDISPLAYTYPE_GENERIC;
+ *ResourceUsage = 0;
+
+ return CLASS_TYPE_TOP;
+ }
+
+ if ( !wcscmp( ClassNameStr, CLASS_NAME_USER ) )
+ {
+ *ResourceType = RESOURCETYPE_ANY;
+ *ResourceDisplayType = RESOURCEDISPLAYTYPE_GENERIC;
+ *ResourceUsage = 0;
+
+ return CLASS_TYPE_USER;
+ }
+
+ if ( !wcscmp( ClassNameStr, CLASS_NAME_VOLUME ) )
+ {
+ *ResourceType = RESOURCETYPE_DISK;
+ *ResourceDisplayType = RESOURCEDISPLAYTYPE_SHARE;
+#ifdef NT1057
+ *ResourceUsage = RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_CONTAINER;
+#else
+ *ResourceUsage = RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_NOLOCALDEVICE;
+#endif
+
+ return CLASS_TYPE_VOLUME;
+ }
+
+ //
+ // Otherwise if ClassNameStr is something other than Unknown, report it
+ //
+ if ( wcscmp( ClassNameStr, CLASS_NAME_UNKNOWN ) )
+ {
+ KdPrint(("NWWORKSTATION: NwGetSubTreeData failed to recognize"));
+ KdPrint((" ClassName: %S\n", ClassNameStr));
+ KdPrint((" Setting object attributes to Unknown for now . . .\n"));
+ }
+
+ *ResourceType = RESOURCETYPE_ANY;
+ *ResourceDisplayType = RESOURCEDISPLAYTYPE_GENERIC;
+ *ResourceUsage = 0;
+
+ return CLASS_TYPE_UNKNOWN;
+}
+
+
+VOID
+NwStripNdsUncName(
+ IN LPWSTR ObjectName,
+ OUT LPWSTR * StrippedObjectName
+ )
+{
+ WORD slashCount;
+ BOOL isNdsUnc;
+ LPWSTR FourthSlash;
+ LPWSTR TreeName;
+ LPWSTR ObjectPath;
+ DWORD TreeNameLen;
+ DWORD ObjectPathLen;
+ DWORD PrefixBytes;
+ DWORD CurrentPathIndex;
+ DWORD StrippedNameLen;
+ DWORD StrippedNameMaxLen = MAX_NDS_NAME_CHARS;
+ WCHAR StrippedName[MAX_NDS_NAME_CHARS];
+
+ *StrippedObjectName = (LPWSTR) LocalAlloc( LMEM_ZEROINIT,
+ (wcslen(ObjectName) + 1) *
+ sizeof(WCHAR) );
+
+ if ( *StrippedObjectName == NULL )
+ {
+ return;
+ }
+
+ NwpGetUncInfo( ObjectName, &slashCount, &isNdsUnc, &FourthSlash );
+
+ if ( slashCount >= 2 )
+ {
+ TreeNameLen = NwParseNdsUncPath( &TreeName,
+ ObjectName,
+ PARSE_NDS_GET_TREE_NAME );
+
+ TreeNameLen /= sizeof(WCHAR);
+
+ wcscpy( *StrippedObjectName, L"\\\\" );
+ wcsncat( *StrippedObjectName, TreeName, TreeNameLen );
+
+ ObjectPathLen = NwParseNdsUncPath( &ObjectPath,
+ ObjectName,
+ PARSE_NDS_GET_PATH_NAME );
+
+ if ( ObjectPathLen == 0 )
+ {
+ _wcsupr( *StrippedObjectName );
+
+ return;
+ }
+
+ wcscat( *StrippedObjectName, L"\\" );
+ }
+ else
+ {
+ wcscpy( *StrippedObjectName, L"" );
+
+ ObjectPath = ObjectName;
+ ObjectPathLen = wcslen(ObjectName) * sizeof(WCHAR);
+ }
+
+ CurrentPathIndex = 0;
+ PrefixBytes = 0;
+ StrippedNameLen = 0;
+
+ //
+ // All of these indexes are in BYTES, not WCHARS!
+ //
+ while ( ( CurrentPathIndex < ObjectPathLen ) &&
+ ( StrippedNameLen < StrippedNameMaxLen ) )
+ {
+ if ( ObjectPath[CurrentPathIndex / sizeof( WCHAR )] == L'=' )
+ {
+ CurrentPathIndex += sizeof( WCHAR );
+ StrippedNameLen -= PrefixBytes;
+ PrefixBytes = 0;
+
+ continue;
+ }
+
+ StrippedName[StrippedNameLen / sizeof( WCHAR )] =
+ ObjectPath[CurrentPathIndex / sizeof( WCHAR )];
+
+ StrippedNameLen += sizeof( WCHAR );
+ CurrentPathIndex += sizeof( WCHAR );
+
+ if ( ObjectPath[CurrentPathIndex / sizeof( WCHAR )] == L'.' )
+ {
+ PrefixBytes = 0;
+ PrefixBytes -= sizeof( WCHAR );
+ }
+ else
+ {
+ PrefixBytes += sizeof( WCHAR );
+ }
+ }
+
+ StrippedName[StrippedNameLen / sizeof( WCHAR )] = L'\0';
+
+ wcscat( *StrippedObjectName, StrippedName );
+ _wcsupr( *StrippedObjectName );
+}
+
+
+DWORD
+NwVerifyNDSObject(
+ IN LPWSTR lpNDSObjectNamePath,
+ OUT LPWSTR * lpFullNDSObjectNamePath,
+ OUT LPDWORD lpClassType,
+ OUT LPDWORD lpResourceScope,
+ OUT LPDWORD lpResourceType,
+ OUT LPDWORD lpResourceDisplayType,
+ OUT LPDWORD lpResourceUsage
+ )
+{
+ DWORD status = NO_ERROR;
+ NTSTATUS ntstatus = STATUS_SUCCESS;
+ UNICODE_STRING TreeServerName;
+ UNICODE_STRING PathString;
+ HANDLE ConnectionHandle = NULL;
+ DWORD dwHandleType;
+ DWORD dwOid;
+ BOOL fImpersonate = FALSE ;
+
+ if ( lpNDSObjectNamePath == NULL )
+ {
+ //
+ // Handle this as if we are at the root of our provider hierarchy.
+ //
+ *lpResourceScope = RESOURCE_GLOBALNET;
+ *lpResourceType = RESOURCETYPE_ANY;
+#ifdef NT1057
+ *lpResourceDisplayType = 0;
+#else
+ *lpResourceDisplayType = RESOURCEDISPLAYTYPE_NETWORK;
+#endif
+ *lpResourceUsage = RESOURCEUSAGE_CONTAINER;
+
+ *lpFullNDSObjectNamePath = NULL;
+
+ return NO_ERROR;
+ }
+
+ TreeServerName.Buffer = NULL;
+ PathString.Buffer = NULL;
+ TreeServerName.MaximumLength = ( wcslen( lpNDSObjectNamePath ) + 1 ) * sizeof( WCHAR );
+ PathString.MaximumLength = ( wcslen( lpNDSObjectNamePath ) + 1 ) * sizeof( WCHAR );
+
+ TreeServerName.Length = NwParseNdsUncPath( (LPWSTR *) &TreeServerName.Buffer,
+ lpNDSObjectNamePath,
+ PARSE_NDS_GET_TREE_NAME );
+
+ if ( TreeServerName.Length == 0 || TreeServerName.Buffer == NULL )
+ {
+ //
+ // lpNDSObjectNamePath is not in the form \\name[\blah.blah.blah][\foo][\bar]...
+ //
+ status = WN_BAD_NETNAME;
+ goto ErrorExit;
+ }
+
+ //
+ // Impersonate the client
+ //
+ if ( ( status = NwImpersonateClient() ) != NO_ERROR )
+ {
+ goto ErrorExit;
+ }
+
+ fImpersonate = TRUE;
+
+ //
+ // Open a connection handle to \\name
+ //
+ ntstatus = NwNdsOpenGenericHandle( &TreeServerName,
+ &dwHandleType,
+ &ConnectionHandle );
+
+ if ( ntstatus != STATUS_SUCCESS )
+ {
+ //
+ // The first part of lpNDSObjectNamePath was neither a NDS tree nor a NCP Server.
+ //
+ status = WN_BAD_NETNAME;
+ goto ErrorExit;
+ }
+
+ if ( dwHandleType != HANDLE_TYPE_NDS_TREE )
+ {
+ //
+ // The first part of lpNDSObjectNamePath was not a NDS tree.
+ //
+ status = VERIFY_ERROR_NOT_A_NDS_TREE;
+ goto ErrorExit;
+ }
+
+ //
+ // Adjust TreeServerName.Length to number of characters.
+ //
+ TreeServerName.Length /= sizeof(WCHAR);
+
+ //
+ // The lpNDSObjectNamePath points to a NDS tree. Now verify that the path is valid.
+ //
+ PathString.Length = NwParseNdsUncPath( (LPWSTR *) &PathString.Buffer,
+ lpNDSObjectNamePath,
+ PARSE_NDS_GET_PATH_NAME );
+
+ if ( PathString.Length == 0 )
+ {
+ LPWSTR treeNameStr = NULL;
+
+ if ( fImpersonate )
+ (void) NwRevertToSelf() ;
+
+ if ( ConnectionHandle )
+ CloseHandle( ConnectionHandle );
+
+ *lpResourceScope = RESOURCE_GLOBALNET;
+ *lpResourceType = RESOURCETYPE_ANY;
+ *lpResourceDisplayType = RESOURCEDISPLAYTYPE_TREE;
+ *lpResourceUsage = RESOURCEUSAGE_CONTAINER;
+
+ //
+ // Need to build a string with the new NDS UNC path for subtree object
+ //
+ treeNameStr = (PVOID) LocalAlloc( LMEM_ZEROINIT,
+ ( TreeServerName.Length + 3 ) * sizeof(WCHAR) );
+
+ if ( treeNameStr == NULL )
+ {
+ KdPrint(("NWWORKSTATION: NwVerifyNDSObject LocalAlloc Failed %lu\n",
+ GetLastError()));
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ goto ErrorExit;
+ }
+
+ wcscpy( treeNameStr, L"\\\\" );
+ wcsncat( treeNameStr, TreeServerName.Buffer, TreeServerName.Length );
+ _wcsupr( treeNameStr );
+
+ *lpFullNDSObjectNamePath = treeNameStr;
+
+ return NO_ERROR;
+ }
+ else
+ {
+ WCHAR lpServerName[NW_MAX_SERVER_LEN];
+ UNICODE_STRING ServerName;
+
+ ServerName.Length = 0;
+ ServerName.MaximumLength = sizeof( lpServerName );
+ ServerName.Buffer = lpServerName;
+
+ //
+ // Resolve the path to get a NDS object id.
+ //
+ ntstatus = NwNdsResolveName( ConnectionHandle,
+ &PathString,
+ &dwOid,
+ &ServerName,
+ NULL,
+ 0 );
+
+ if ( ntstatus == STATUS_SUCCESS && ServerName.Length )
+ {
+ DWORD dwHandleType;
+
+ //
+ // NwNdsResolveName succeeded, but we were referred to
+ // another server, though ContextHandle->dwOid is still valid.
+
+ if ( ConnectionHandle )
+ CloseHandle( ConnectionHandle );
+
+ ConnectionHandle = NULL;
+
+ //
+ // Open a NDS generic connection handle to \\ServerName
+ //
+ ntstatus = NwNdsOpenGenericHandle( &ServerName,
+ &dwHandleType,
+ &ConnectionHandle );
+
+ if ( ntstatus != STATUS_SUCCESS )
+ {
+ status = RtlNtStatusToDosError(ntstatus);
+ goto ErrorExit;
+ }
+
+ ASSERT( dwHandleType != HANDLE_TYPE_NCP_SERVER );
+ }
+ }
+
+ if ( ntstatus != STATUS_SUCCESS )
+ {
+ LPWSTR treeNameStr = NULL;
+
+ *lpResourceScope = RESOURCE_GLOBALNET;
+ *lpResourceType = RESOURCETYPE_ANY;
+ *lpResourceDisplayType = RESOURCEDISPLAYTYPE_TREE;
+ *lpResourceUsage = RESOURCEUSAGE_CONTAINER;
+
+ //
+ // Need to build a string with the new NDS UNC path for subtree object
+ //
+ treeNameStr = (PVOID) LocalAlloc( LMEM_ZEROINIT,
+ ( TreeServerName.Length + 3 ) * sizeof(WCHAR) );
+
+ if ( treeNameStr == NULL )
+ {
+ KdPrint(("NWWORKSTATION: NwVerifyNDSObject LocalAlloc Failed %lu\n",
+ GetLastError()));
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ goto ErrorExit;
+ }
+
+ wcscpy( treeNameStr, L"\\\\" );
+ wcsncat( treeNameStr, TreeServerName.Buffer, TreeServerName.Length );
+ _wcsupr( treeNameStr );
+
+ *lpFullNDSObjectNamePath = treeNameStr;
+
+ status = VERIFY_ERROR_PATH_NOT_FOUND;
+ goto ErrorExit;
+ }
+
+ //
+ // Check to see what kind of object is pointed to by lpRemoteName.
+ //
+ {
+ BYTE RawResponse[TWO_KB];
+ PBYTE pbRawGetInfo;
+ DWORD RawResponseSize = sizeof(RawResponse);
+ DWORD dwStrLen;
+ DWORD TreeObjectName;
+ LPWSTR StrippedObjectName = NULL;
+ LPWSTR newPathStr = NULL;
+
+ ntstatus = NwNdsReadObjectInfo( ConnectionHandle,
+ dwOid,
+ RawResponse,
+ RawResponseSize );
+
+ if ( ntstatus != NO_ERROR )
+ {
+ status = RtlNtStatusToDosError(ntstatus);
+ goto ErrorExit;
+ }
+
+ //
+ // Get current subtree data from ContextHandle
+ //
+ *lpClassType = NwGetSubTreeData( (DWORD) RawResponse,
+ &TreeObjectName,
+ lpResourceScope,
+ lpResourceType,
+ lpResourceDisplayType,
+ lpResourceUsage,
+ &StrippedObjectName );
+
+ if ( StrippedObjectName == NULL )
+ {
+ KdPrint(("NWWORKSTATION: NwVerifyNDSObject LocalAlloc Failed %lu\n",
+ GetLastError()));
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ goto ErrorExit;
+ }
+
+ //
+ // Need to build a string with the new NDS UNC path for subtree object
+ //
+ newPathStr = (PVOID) LocalAlloc( LMEM_ZEROINIT,
+ ( wcslen( StrippedObjectName ) +
+ TreeServerName.Length + 4 )
+ * sizeof(WCHAR) );
+
+ if ( newPathStr == NULL )
+ {
+ (void) LocalFree((HLOCAL) StrippedObjectName);
+
+ KdPrint(("NWWORKSTATION: NwVerifyNDSObject LocalAlloc Failed %lu\n",
+ GetLastError()));
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ goto ErrorExit;
+ }
+
+ wcscpy( newPathStr, L"\\\\" );
+ wcsncat( newPathStr, TreeServerName.Buffer, TreeServerName.Length );
+ wcscat( newPathStr, L"\\" );
+ wcscat( newPathStr, StrippedObjectName );
+ _wcsupr( newPathStr );
+
+ //
+ // Don't need the StrippedObjectName string anymore
+ //
+ (void) LocalFree((HLOCAL) StrippedObjectName);
+ StrippedObjectName = NULL;
+
+ *lpFullNDSObjectNamePath = newPathStr;
+ status = NO_ERROR;
+ } // End of Block
+
+ErrorExit:
+
+ if ( fImpersonate )
+ (void) NwRevertToSelf() ;
+
+ if ( ConnectionHandle )
+ CloseHandle( ConnectionHandle );
+
+ return status;
+}
+
+
+DWORD
+NwVerifyBinderyObject(
+ IN LPWSTR lpBinderyObjectPathName,
+ OUT LPWSTR * lpFullBinderyObjectPathName,
+ OUT LPDWORD lpClassType,
+ OUT LPDWORD lpResourceScope,
+ OUT LPDWORD lpResourceType,
+ OUT LPDWORD lpResourceDisplayType,
+ OUT LPDWORD lpResourceUsage
+ )
+{
+ DWORD status = NO_ERROR;
+ HANDLE ConnectionHandle = NULL;
+ BOOL fImpersonate = FALSE ;
+ BOOL fResourceTypeDisk = FALSE ;
+ BOOL fIsNdsUnc = FALSE ;
+ UNICODE_STRING BinderyConnectStr;
+ ULONG CreateDisposition = 0;
+ ULONG CreateOptions = 0;
+ WORD wSlashCount;
+ LPWSTR FourthSlash;
+
+ if ( lpBinderyObjectPathName == NULL )
+ {
+ //
+ // Handle this as if we are at the root of our provider hierarchy.
+ //
+ *lpResourceScope = RESOURCE_GLOBALNET;
+ *lpResourceType = RESOURCETYPE_ANY;
+#ifdef NT1057
+ *lpResourceDisplayType = 0;
+#else
+ *lpResourceDisplayType = RESOURCEDISPLAYTYPE_NETWORK;
+#endif
+ *lpResourceUsage = RESOURCEUSAGE_CONTAINER;
+
+ *lpFullBinderyObjectPathName = NULL;
+
+ return NO_ERROR;
+ }
+
+ //
+ // Find out if we are looking at a \\server, \\server\vol, or
+ // \\server\vol\dir . . .
+ //
+ NwpGetUncInfo( lpBinderyObjectPathName,
+ &wSlashCount,
+ &fIsNdsUnc,
+ &FourthSlash );
+
+ if ( wSlashCount > 2 )
+ fResourceTypeDisk = TRUE;
+
+ //
+ // Impersonate the client
+ //
+ if ( ( status = NwImpersonateClient() ) != NO_ERROR )
+ {
+ goto ErrorExit;
+ }
+
+ fImpersonate = TRUE;
+
+ //
+ // Open a connection handle to \\server\vol\...
+ //
+
+ BinderyConnectStr.Buffer = NULL;
+
+ //
+ // Open a tree connection handle to \Device\NwRdr\ContainerName
+ //
+ status = NwCreateTreeConnectName( lpBinderyObjectPathName,
+ NULL,
+ &BinderyConnectStr );
+
+ if ( status != NO_ERROR )
+ {
+ status = WN_BAD_NETNAME;
+ goto ErrorExit;
+ }
+
+ CreateDisposition = FILE_OPEN;
+ CreateOptions = FILE_SYNCHRONOUS_IO_NONALERT;
+
+ status = NwOpenCreateConnection( &BinderyConnectStr,
+ NULL,
+ NULL,
+ lpBinderyObjectPathName,
+ FILE_LIST_DIRECTORY | SYNCHRONIZE,
+ CreateDisposition,
+ CreateOptions,
+ RESOURCETYPE_DISK, // When connecting beyond servername
+ &ConnectionHandle,
+ NULL );
+
+ if ( status == NO_ERROR )
+ {
+ LPWSTR BinderyNameStr = NULL;
+
+ //
+ // Need to build a string with the new UNC path for bindery object
+ //
+ BinderyNameStr = (PVOID) LocalAlloc( LMEM_ZEROINIT,
+ ( wcslen( lpBinderyObjectPathName ) + 1 )
+ * sizeof(WCHAR) );
+
+ if ( BinderyNameStr == NULL )
+ {
+ KdPrint(("NWWORKSTATION: NwVerifyBinderyObject LocalAlloc Failed %lu\n",
+ GetLastError()));
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ goto ErrorExit;
+ }
+
+ wcscpy( BinderyNameStr, lpBinderyObjectPathName );
+ _wcsupr( BinderyNameStr );
+
+ *lpFullBinderyObjectPathName = BinderyNameStr;
+
+ if ( BinderyConnectStr.Buffer )
+ (void) LocalFree((HLOCAL) BinderyConnectStr.Buffer);
+
+ if ( fImpersonate )
+ (void) NwRevertToSelf() ;
+
+ if ( ConnectionHandle )
+ {
+ *lpResourceScope = RESOURCE_GLOBALNET;
+ *lpResourceType = fResourceTypeDisk ? RESOURCETYPE_DISK : RESOURCETYPE_ANY;
+ *lpResourceDisplayType = fResourceTypeDisk ? RESOURCEDISPLAYTYPE_SHARE : RESOURCEDISPLAYTYPE_SERVER;
+#ifdef NT1057
+ *lpResourceUsage = fResourceTypeDisk ? RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_CONTAINER : RESOURCEUSAGE_CONTAINER;
+#else
+ *lpResourceUsage = fResourceTypeDisk ? RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_NOLOCALDEVICE : RESOURCEUSAGE_CONTAINER;
+#endif
+
+ CloseHandle( ConnectionHandle );
+ }
+
+ return NO_ERROR;
+ }
+
+ErrorExit:
+
+ *lpFullBinderyObjectPathName = NULL;
+
+ if ( BinderyConnectStr.Buffer )
+ (void) LocalFree((HLOCAL) BinderyConnectStr.Buffer);
+
+ if ( fImpersonate )
+ (void) NwRevertToSelf() ;
+
+ if ( ConnectionHandle )
+ CloseHandle( ConnectionHandle );
+
+ return WN_BAD_NETNAME;
+}
+
+
+DWORD
+NwGetNDSPathInfo(
+ IN LPWSTR lpNDSObjectNamePath,
+ OUT LPWSTR * lppSystemObjectNamePath,
+ OUT LPWSTR * lpSystemPathPart,
+ OUT LPDWORD lpClassType,
+ OUT LPDWORD lpResourceScope,
+ OUT LPDWORD lpResourceType,
+ OUT LPDWORD lpResourceDisplayType,
+ OUT LPDWORD lpResourceUsage
+ )
+{
+ DWORD status = NO_ERROR;
+ WORD slashCount;
+ BOOL isNdsUnc;
+ BOOL fReturnBadNetName = FALSE;
+ LPWSTR FourthSlash;
+ LPWSTR lpSystemPath = NULL;
+
+ *lpSystemPathPart = NULL;
+
+ NwpGetUncInfo( lpNDSObjectNamePath,
+ &slashCount,
+ &isNdsUnc,
+ &FourthSlash );
+
+ if ( slashCount <= 3 )
+ {
+ //
+ // Path is to a possible NDS object, check to see if so and if valid...
+ //
+
+ status = NwVerifyNDSObject( lpNDSObjectNamePath,
+ lppSystemObjectNamePath,
+ lpClassType,
+ lpResourceScope,
+ lpResourceType,
+ lpResourceDisplayType,
+ lpResourceUsage );
+
+ *lpSystemPathPart = NULL;
+
+ return status;
+ }
+ else
+ {
+ //
+ // Path is to a directory, see if directory exists . . .
+ //
+ status = NwVerifyBinderyObject( lpNDSObjectNamePath,
+ lppSystemObjectNamePath,
+ lpClassType,
+ lpResourceScope,
+ lpResourceType,
+ lpResourceDisplayType,
+ lpResourceUsage );
+ }
+
+ if ( status == WN_BAD_NETNAME )
+ {
+ fReturnBadNetName = TRUE;
+ status = NO_ERROR;
+ }
+
+ if ( status == NO_ERROR )
+ {
+ WCHAR TempNDSObjectNamePath[256];
+
+ //
+ // Test \\tree\obj.obj... component and
+ // return network resource for valid parent and the string,
+ // lpSystemPathPart, for the directory part ( \dir1\...).
+ //
+
+ if ( *lppSystemObjectNamePath != NULL )
+ {
+ (void) LocalFree( (HLOCAL) (*lppSystemObjectNamePath) );
+ *lppSystemObjectNamePath = NULL;
+ }
+
+ lpSystemPath = (LPWSTR) LocalAlloc( LMEM_ZEROINIT,
+ ( wcslen( FourthSlash ) + 1 ) *
+ sizeof( WCHAR ) );
+
+ if ( lpSystemPath == NULL )
+ {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ wcscpy( lpSystemPath, FourthSlash );
+ *FourthSlash = L'\0';
+
+ wcscpy( TempNDSObjectNamePath, lpNDSObjectNamePath );
+ *FourthSlash = L'\\';
+
+ //
+ // See if \\tree\obj.obj.... exists . . .
+ //
+ status = NwVerifyNDSObject( TempNDSObjectNamePath,
+ lppSystemObjectNamePath,
+ lpClassType,
+ lpResourceScope,
+ lpResourceType,
+ lpResourceDisplayType,
+ lpResourceUsage );
+
+ if ( status != NO_ERROR )
+ {
+ LocalFree( lpSystemPath );
+ lpSystemPath = NULL;
+ }
+ }
+
+ *lpSystemPathPart = lpSystemPath;
+
+ //
+ // The provider spec for this function used to tell us to create a
+ // NETRESOURCE, even if the system part of the path was invalid, while
+ // returning WN_BAD_NETNAME. Now we return SUCCESS and the NETRESOURCE,
+ // irregardless of whether the lpSystem part is valid.
+ // if ( fReturnBadNetName == TRUE )
+ // {
+ // return WN_BAD_NETNAME;
+ // }
+
+ return status;
+}
+
+
+DWORD
+NwGetBinderyPathInfo(
+ IN LPWSTR lpBinderyObjectNamePath,
+ OUT LPWSTR * lppSystemObjectNamePath,
+ OUT LPWSTR * lpSystemPathPart,
+ OUT LPDWORD lpClassType,
+ OUT LPDWORD lpResourceScope,
+ OUT LPDWORD lpResourceType,
+ OUT LPDWORD lpResourceDisplayType,
+ OUT LPDWORD lpResourceUsage
+ )
+{
+ DWORD status = NO_ERROR;
+ WORD slashCount;
+ BOOL isNdsUnc;
+ LPWSTR FourthSlash;
+ LPWSTR lpSystemPath = NULL;
+
+ *lpSystemPathPart = NULL;
+
+ NwpGetUncInfo( lpBinderyObjectNamePath,
+ &slashCount,
+ &isNdsUnc,
+ &FourthSlash );
+
+ if ( slashCount <= 3 )
+ {
+ //
+ // Path is to a server or volume, check to see which and if valid . . .
+ //
+
+ status = NwVerifyBinderyObject( lpBinderyObjectNamePath,
+ lppSystemObjectNamePath,
+ lpClassType,
+ lpResourceScope,
+ lpResourceType,
+ lpResourceDisplayType,
+ lpResourceUsage );
+
+ *lpSystemPathPart = NULL;
+
+ return status;
+ }
+ else
+ {
+ //
+ // Path is to a directory, see if directory exists . . .
+ //
+ status = NwVerifyBinderyObject( lpBinderyObjectNamePath,
+ lppSystemObjectNamePath,
+ lpClassType,
+ lpResourceScope,
+ lpResourceType,
+ lpResourceDisplayType,
+ lpResourceUsage );
+ }
+
+ if ( status == WN_BAD_NETNAME )
+ {
+ WCHAR TempBinderyObjectNamePath[256];
+
+ //
+ // Path is to a invalid directory. Test \\server\volume component and
+ // return network resource for valid parent and the string,
+ // lpSystemPathPart, for the directory part ( \dir1\...).
+ //
+
+ lpSystemPath = (LPWSTR) LocalAlloc( LMEM_ZEROINIT,
+ ( wcslen( FourthSlash ) + 1 ) *
+ sizeof( WCHAR ) );
+
+ if ( lpSystemPath == NULL )
+ {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ wcscpy( lpSystemPath, FourthSlash );
+ *FourthSlash = L'\0';
+
+ wcscpy( TempBinderyObjectNamePath, lpBinderyObjectNamePath );
+ *FourthSlash = L'\\';
+
+ //
+ // See if \\server\volume exists . . .
+ //
+ status = NwVerifyBinderyObject( TempBinderyObjectNamePath,
+ lppSystemObjectNamePath,
+ lpClassType,
+ lpResourceScope,
+ lpResourceType,
+ lpResourceDisplayType,
+ lpResourceUsage );
+
+ if ( status != NO_ERROR )
+ {
+ LocalFree( lpSystemPath );
+ lpSystemPath = NULL;
+ }
+
+ //
+ // Return SUCCESS, since the NETRESOURCE for \\server\volume that
+ // we are describing is at least valid, even though the lpSystem
+ // part in not. This is a change in the provider spec (4/25/96).
+ //
+ // else
+ // {
+ // status = WN_BAD_NETNAME;
+ // }
+ }
+ else
+ {
+ //
+ // Path is to a valid directory. Return resource information for the
+ // \\server\volume component and the string, lpSystemPathPart, for the
+ // directory part ( \dir1\...).
+ //
+ NwpGetUncInfo( *lppSystemObjectNamePath,
+ &slashCount,
+ &isNdsUnc,
+ &FourthSlash );
+
+ lpSystemPath = (LPWSTR) LocalAlloc( LMEM_ZEROINIT,
+ ( wcslen( FourthSlash ) + 1 ) *
+ sizeof( WCHAR ) );
+
+ if ( lpSystemPath == NULL )
+ {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ wcscpy( lpSystemPath, FourthSlash );
+ *FourthSlash = L'\0';
+
+ *lpResourceScope = RESOURCE_GLOBALNET;
+ *lpResourceType = RESOURCETYPE_DISK;
+ *lpResourceDisplayType = RESOURCEDISPLAYTYPE_SHARE;
+#ifdef NT1057
+ *lpResourceUsage = RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_CONTAINER;
+#else
+ *lpResourceUsage = RESOURCEUSAGE_CONNECTABLE | RESOURCEUSAGE_NOLOCALDEVICE;
+#endif
+
+ status = NO_ERROR;
+ }
+
+ *lpSystemPathPart = lpSystemPath;
+
+ return status;
+}
+
+
+BOOL
+NwGetRemoteNameParent(
+ IN LPWSTR lpRemoteName,
+ OUT LPWSTR * lpRemoteNameParent
+ )
+{
+ unsigned short iter = 0;
+ unsigned short totalLength = wcslen( lpRemoteName );
+ unsigned short slashCount = 0;
+ unsigned short dotCount = 0;
+ unsigned short thirdSlash = 0;
+ unsigned short lastSlash = 0;
+ unsigned short parentNDSSubTree = 0;
+ LPWSTR newRemoteNameParent = NULL;
+
+ if ( totalLength < 2 )
+ return FALSE;
+
+ //
+ // Get thirdSlash to indicate the character in the string that indicates the
+ // "\" in between the tree name and the rest of the UNC path. Set parentNDSSubTree
+ // if available. And always set lastSlash to the most recent "\" seen as you walk.
+ //
+ // Example: \\<tree name>\path.to.object[\|.]<object>
+ // ^ ^
+ // | |
+ // thirdSlash parentNDSSubTree
+ //
+ while ( iter < totalLength )
+ {
+ if ( lpRemoteName[iter] == L'\\' )
+ {
+ slashCount += 1;
+ if ( slashCount == 3 )
+ thirdSlash = iter;
+
+ lastSlash = iter;
+ }
+
+ if ( lpRemoteName[iter] == L'.' )
+ {
+ dotCount += 1;
+ if ( dotCount == 1 )
+ parentNDSSubTree = iter;
+ }
+
+ iter++;
+ }
+
+ if ( slashCount > 3 )
+ {
+ newRemoteNameParent = (PVOID) LocalAlloc( LMEM_ZEROINIT,
+ ( lastSlash + 1 ) *
+ sizeof(WCHAR));
+
+ if ( newRemoteNameParent == NULL )
+ {
+ KdPrint(("NWWORKSTATION: NwGetRemoteNameParent LocalAlloc Failed %lu\n",
+ GetLastError()));
+ return FALSE;
+ }
+
+ wcsncpy( newRemoteNameParent, lpRemoteName, lastSlash );
+ _wcsupr( newRemoteNameParent );
+
+ *lpRemoteNameParent = newRemoteNameParent;
+
+ return TRUE;
+ }
+
+ if ( slashCount == 3 )
+ {
+ if ( dotCount == 0 )
+ {
+ newRemoteNameParent = (PVOID) LocalAlloc( LMEM_ZEROINIT,
+ ( lastSlash + 1 ) *
+ sizeof(WCHAR));
+
+ if ( newRemoteNameParent == NULL )
+ {
+ KdPrint(("NWWORKSTATION: NwGetRemoteNameParent LocalAlloc Failed %lu\n",
+ GetLastError()));
+ return FALSE;
+ }
+
+ wcsncpy( newRemoteNameParent, lpRemoteName, lastSlash );
+ _wcsupr( newRemoteNameParent );
+
+ *lpRemoteNameParent = newRemoteNameParent;
+
+ return TRUE;
+ }
+ else
+ {
+ newRemoteNameParent = (PVOID) LocalAlloc( LMEM_ZEROINIT,
+ ( totalLength -
+ ( parentNDSSubTree - thirdSlash )
+ + 1 )
+ * sizeof(WCHAR) );
+
+ if ( newRemoteNameParent == NULL )
+ {
+ KdPrint(("NWWORKSTATION: NwGetRemoteNameParent LocalAlloc Failed %lu\n",
+ GetLastError()));
+ return FALSE;
+ }
+
+ wcsncpy( newRemoteNameParent, lpRemoteName, thirdSlash + 1 );
+ wcscat( newRemoteNameParent, &lpRemoteName[parentNDSSubTree+1] );
+ _wcsupr( newRemoteNameParent );
+
+ *lpRemoteNameParent = newRemoteNameParent;
+
+ return TRUE;
+ }
+ }
+
+ // Else we set lpRemoteNameParent to NULL, to indicate that we are at the top and
+ // return TRUE.
+ *lpRemoteNameParent = NULL;
+
+ return TRUE;
+}
+
+
+DWORD
+NwGetFirstDirectoryEntry(
+ IN HANDLE DirHandle,
+ OUT LPWSTR *DirEntry
+ )
+/*++
+
+Routine Description:
+
+ This function is called by NwEnumDirectories to get the first
+ directory entry given a handle to the directory. It allocates
+ the output buffer to hold the returned directory name; the
+ caller should free this output buffer with LocalFree when done.
+
+Arguments:
+
+ DirHandle - Supplies the opened handle to the container
+ directory find a directory within it.
+
+ DirEntry - Receives a pointer to the returned directory
+ found.
+
+Return Value:
+
+ NO_ERROR - The operation was successful.
+
+ ERROR_NOT_ENOUGH_MEMORY - Out of memory allocating output
+ buffer.
+
+ Other errors from NtQueryDirectoryFile.
+
+--*/ // NwGetFirstDirectoryEntry
+{
+ DWORD status = NO_ERROR;
+ NTSTATUS ntstatus = STATUS_SUCCESS;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ PFILE_DIRECTORY_INFORMATION DirInfo;
+
+ UNICODE_STRING StartFileName;
+
+#if DBG
+ DWORD i = 0;
+#endif
+
+ //
+ // Allocate a large buffer to get one directory information entry.
+ //
+ DirInfo = (PVOID) LocalAlloc(
+ LMEM_ZEROINIT,
+ sizeof(FILE_DIRECTORY_INFORMATION) +
+ (MAX_PATH * sizeof(WCHAR))
+ );
+
+ if (DirInfo == NULL) {
+ KdPrint(("NWWORKSTATION: NwGetFirstDirectoryEntry LocalAlloc Failed %lu\n",
+ GetLastError()));
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ RtlInitUnicodeString(&StartFileName, L"*");
+
+ ntstatus = NtQueryDirectoryFile(
+ DirHandle,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ DirInfo,
+ sizeof(FILE_DIRECTORY_INFORMATION) +
+ (MAX_PATH * sizeof(WCHAR)),
+ FileDirectoryInformation, // Info class requested
+ TRUE, // Return single entry
+ &StartFileName, // Redirector needs this
+ TRUE // Restart scan
+ );
+
+ //
+ // For now, if buffer to NtQueryDirectoryFile is too small, just give
+ // up. We may want to try to reallocate a bigger buffer at a later time.
+ //
+
+ if (ntstatus == STATUS_SUCCESS) {
+ ntstatus = IoStatusBlock.Status;
+ }
+
+ if (ntstatus != STATUS_SUCCESS) {
+
+ if (ntstatus == STATUS_NO_MORE_FILES) {
+ //
+ // We ran out of entries.
+ //
+ status = WN_NO_MORE_ENTRIES;
+ }
+ else {
+ KdPrint(("NWWORKSTATION: NwGetFirstDirectoryEntry: NtQueryDirectoryFile returns %08lx\n",
+ ntstatus));
+ status = RtlNtStatusToDosError(ntstatus);
+ }
+
+ goto CleanExit;
+ }
+
+#if DBG
+ IF_DEBUG(ENUM) {
+ KdPrint(("GetFirst(%u) got %ws, attributes %08lx\n", ++i,
+ DirInfo->FileName, DirInfo->FileAttributes));
+ }
+#endif
+
+ //
+ // Scan until we find the first directory entry that is not "." or ".."
+ //
+ while (!(DirInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
+ memcmp(DirInfo->FileName, L".", DirInfo->FileNameLength) == 0 ||
+ memcmp(DirInfo->FileName, L"..", DirInfo->FileNameLength) == 0) {
+
+ ntstatus = NtQueryDirectoryFile(
+ DirHandle,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ DirInfo,
+ sizeof(FILE_DIRECTORY_INFORMATION) +
+ (MAX_PATH * sizeof(WCHAR)),
+ FileDirectoryInformation, // Info class requested
+ TRUE, // Return single entry
+ NULL,
+ FALSE // Restart scan
+ );
+
+ if (ntstatus == STATUS_SUCCESS) {
+ ntstatus = IoStatusBlock.Status;
+ }
+
+ if (ntstatus != STATUS_SUCCESS) {
+
+ if (ntstatus == STATUS_NO_MORE_FILES) {
+ //
+ // We ran out of entries.
+ //
+ status = WN_NO_MORE_ENTRIES;
+ }
+ else {
+ KdPrint(("NWWORKSTATION: NwGetFirstDirectoryEntry: NtQueryDirectoryFile returns %08lx\n",
+ ntstatus));
+ status = RtlNtStatusToDosError(ntstatus);
+ }
+
+ goto CleanExit;
+ }
+
+#if DBG
+ IF_DEBUG(ENUM) {
+ KdPrint(("GetFirst(%u) got %ws, attributes %08lx\n", ++i,
+ DirInfo->FileName, DirInfo->FileAttributes));
+ }
+#endif
+ }
+
+ //
+ // Allocate the output buffer for the returned directory name
+ //
+ *DirEntry = (PVOID) LocalAlloc(
+ LMEM_ZEROINIT,
+ DirInfo->FileNameLength + sizeof(WCHAR)
+ );
+
+ if (*DirEntry == NULL) {
+ KdPrint(("NWWORKSTATION: NwGetFirstDirectoryEntry LocalAlloc Failed %lu\n",
+ GetLastError()));
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ goto CleanExit;
+ }
+
+ memcpy(*DirEntry, DirInfo->FileName, DirInfo->FileNameLength);
+
+#if DBG
+ IF_DEBUG(ENUM) {
+ KdPrint(("NWWORKSTATION: NwGetFirstDirectoryEntry returns %ws\n",
+ *DirEntry));
+ }
+#endif
+
+ status = NO_ERROR;
+
+CleanExit:
+ (void) LocalFree((HLOCAL) DirInfo);
+
+ //
+ // We could not find any directories under the requested
+ // so we need to treat this as no entries.
+ //
+ if ( status == ERROR_FILE_NOT_FOUND )
+ status = WN_NO_MORE_ENTRIES;
+
+ return status;
+}
+
+
+
+DWORD
+NwGetNextDirectoryEntry(
+ IN HANDLE DirHandle,
+ OUT LPWSTR *DirEntry
+ )
+/*++
+
+Routine Description:
+
+ This function is called by NwEnumDirectories to get the next
+ directory entry given a handle to the directory. It allocates
+ the output buffer to hold the returned directory name; the
+ caller should free this output buffer with LocalFree when done.
+
+Arguments:
+
+ DirHandle - Supplies the opened handle to the container
+ directory find a directory within it.
+
+ DirEntry - Receives a pointer to the returned directory
+ found.
+
+Return Value:
+
+ NO_ERROR - The operation was successful.
+
+ ERROR_NOT_ENOUGH_MEMORY - Out of memory allocating output
+ buffer.
+
+ Other errors from NtQueryDirectoryFile.
+
+--*/ // NwGetNextDirectoryEntry
+{
+ DWORD status = NO_ERROR;
+ NTSTATUS ntstatus = STATUS_SUCCESS;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ PFILE_DIRECTORY_INFORMATION DirInfo;
+
+ //
+ // Allocate a large buffer to get one directory information entry.
+ //
+ DirInfo = (PVOID) LocalAlloc(
+ LMEM_ZEROINIT,
+ sizeof(FILE_DIRECTORY_INFORMATION) +
+ (MAX_PATH * sizeof(WCHAR))
+ );
+
+ if (DirInfo == NULL) {
+ KdPrint(("NWWORKSTATION: NwGetNextDirectoryEntry LocalAlloc Failed %lu\n",
+ GetLastError()));
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ do {
+
+ ntstatus = NtQueryDirectoryFile(
+ DirHandle,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ DirInfo,
+ sizeof(FILE_DIRECTORY_INFORMATION) +
+ (MAX_PATH * sizeof(WCHAR)),
+ FileDirectoryInformation, // Info class requested
+ TRUE, // Return single entry
+ NULL,
+ FALSE // Restart scan
+ );
+
+ if (ntstatus == STATUS_SUCCESS) {
+ ntstatus = IoStatusBlock.Status;
+ }
+
+ } while (ntstatus == STATUS_SUCCESS &&
+ !(DirInfo->FileAttributes & FILE_ATTRIBUTE_DIRECTORY));
+
+
+ if (ntstatus != STATUS_SUCCESS) {
+
+ if (ntstatus == STATUS_NO_MORE_FILES) {
+ //
+ // We ran out of entries.
+ //
+ status = WN_NO_MORE_ENTRIES;
+ }
+ else {
+ KdPrint(("NWWORKSTATION: NwGetNextDirectoryEntry: NtQueryDirectoryFile returns %08lx\n",
+ ntstatus));
+ status = RtlNtStatusToDosError(ntstatus);
+ }
+
+ goto CleanExit;
+ }
+
+
+ //
+ // Allocate the output buffer for the returned directory name
+ //
+ *DirEntry = (PVOID) LocalAlloc(
+ LMEM_ZEROINIT,
+ DirInfo->FileNameLength + sizeof(WCHAR)
+ );
+
+ if (*DirEntry == NULL) {
+ KdPrint(("NWWORKSTATION: NwGetNextDirectoryEntry LocalAlloc Failed %lu\n",
+ GetLastError()));
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ goto CleanExit;
+ }
+
+ memcpy(*DirEntry, DirInfo->FileName, DirInfo->FileNameLength);
+
+#if DBG
+ IF_DEBUG(ENUM) {
+ KdPrint(("NWWORKSTATION: NwGetNextDirectoryEntry returns %ws\n",
+ *DirEntry));
+ }
+#endif
+
+ status = NO_ERROR;
+
+CleanExit:
+ (void) LocalFree((HLOCAL) DirInfo);
+
+ return status;
+}
+
+
+DWORD
+NwWriteNetResourceEntry(
+ IN OUT LPBYTE * FixedPortion,
+ IN OUT LPWSTR * EndOfVariableData,
+ IN LPWSTR ContainerName OPTIONAL,
+ IN LPWSTR LocalName OPTIONAL,
+ IN LPWSTR RemoteName,
+ IN DWORD ScopeFlag,
+ IN DWORD DisplayFlag,
+ IN DWORD UsageFlag,
+ IN DWORD ResourceType,
+ IN LPWSTR SystemPath OPTIONAL,
+ OUT LPWSTR * lppSystem OPTIONAL,
+ OUT LPDWORD EntrySize
+ )
+/*++
+
+Routine Description:
+
+ This function packages a NETRESOURCE entry into the user output buffer.
+ It is called by the various enum resource routines.
+
+Arguments:
+
+ FixedPortion - Supplies a pointer to the output buffer where the next
+ entry of the fixed portion of the use information will be written.
+ This pointer is updated to point to the next fixed portion entry
+ after a NETRESOURCE entry is written.
+
+ EndOfVariableData - Supplies a pointer just off the last available byte
+ in the output buffer. This is because the variable portion of the
+ user information is written into the output buffer starting from
+ the end.
+
+ This pointer is updated after any variable length information is
+ written to the output buffer.
+
+ ContainerName - Supplies the full path qualifier to make RemoteName
+ a full UNC name.
+
+ LocalName - Supplies the local device name, if any.
+
+ RemoteName - Supplies the remote resource name.
+
+ ScopeFlag - Supplies the flag which indicates whether this is a
+ CONNECTED or GLOBALNET resource.
+
+ DisplayFlag - Supplies the flag which tells the UI how to display
+ the resource.
+
+ UsageFlag - Supplies the flag which indicates that the RemoteName
+ is either a container or a connectable resource or both.
+
+ SystemPath - Supplies the optional system path data to be stored in the
+ NETRESOURCE buffer. This is used by the NPGetResourceInformation
+ helper routines.
+
+ lppSystem - If SystemPath is provided, this will point to the location
+ in the NETRESOURCE buffer that contains the system path string.
+
+ EntrySize - Receives the size of the NETRESOURCE entry in bytes.
+
+Return Value:
+
+ NO_ERROR - Successfully wrote entry into user buffer.
+
+ ERROR_NOT_ENOUGH_MEMORY - Failed to allocate work buffer.
+
+ WN_MORE_DATA - Buffer was too small to fit entry.
+
+--*/ // NwWriteNetResourceEntry
+{
+ BOOL FitInBuffer = TRUE;
+ LPNETRESOURCEW NetR = (LPNETRESOURCEW) *FixedPortion;
+ LPWSTR RemoteBuffer;
+ LPWSTR lpSystem;
+
+ *EntrySize = sizeof(NETRESOURCEW) +
+ (wcslen(RemoteName) + wcslen(NwProviderName) + 2) *
+ sizeof(WCHAR);
+
+
+ if (ARGUMENT_PRESENT(LocalName)) {
+ *EntrySize += (wcslen(LocalName) + 1) * sizeof(WCHAR);
+ }
+
+ if (ARGUMENT_PRESENT(ContainerName)) {
+ *EntrySize += wcslen(ContainerName) * sizeof(WCHAR);
+ }
+
+ if (ARGUMENT_PRESENT(SystemPath)) {
+ *EntrySize += wcslen(SystemPath) * sizeof(WCHAR);
+ }
+
+ *EntrySize = ROUND_UP_COUNT( *EntrySize, ALIGN_DWORD);
+
+ //
+ // See if buffer is large enough to fit the entry.
+ //
+ if (((DWORD) *FixedPortion + *EntrySize) >
+ (DWORD) *EndOfVariableData) {
+
+ return WN_MORE_DATA;
+ }
+
+ NetR->dwScope = ScopeFlag;
+ NetR->dwType = ResourceType;
+ NetR->dwDisplayType = DisplayFlag;
+ NetR->dwUsage = UsageFlag;
+ NetR->lpComment = NULL;
+
+ //
+ // Update fixed entry pointer to next entry.
+ //
+ (DWORD) (*FixedPortion) += sizeof(NETRESOURCEW);
+
+ //
+ // RemoteName
+ //
+ if (ARGUMENT_PRESENT(ContainerName)) {
+
+ //
+ // Prefix the RemoteName with its container name making the
+ // it a fully-qualified UNC name.
+ //
+ RemoteBuffer = (PVOID) LocalAlloc(
+ LMEM_ZEROINIT,
+ (wcslen(RemoteName) + wcslen(ContainerName) + 1) *
+ sizeof(WCHAR)
+ );
+
+ if (RemoteBuffer == NULL) {
+ KdPrint(("NWWORKSTATION: NwWriteNetResourceEntry LocalAlloc failed %lu\n",
+ GetLastError()));
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ wcscpy(RemoteBuffer, ContainerName);
+ wcscat(RemoteBuffer, RemoteName);
+ }
+ else {
+ RemoteBuffer = RemoteName;
+ }
+
+ FitInBuffer = NwlibCopyStringToBuffer(
+ RemoteBuffer,
+ wcslen(RemoteBuffer),
+ (LPCWSTR) *FixedPortion,
+ EndOfVariableData,
+ &NetR->lpRemoteName
+ );
+
+ if (ARGUMENT_PRESENT(ContainerName)) {
+ (void) LocalFree((HLOCAL) RemoteBuffer);
+ }
+
+ ASSERT(FitInBuffer);
+
+ //
+ // LocalName
+ //
+ if (ARGUMENT_PRESENT(LocalName)) {
+ FitInBuffer = NwlibCopyStringToBuffer(
+ LocalName,
+ wcslen(LocalName),
+ (LPCWSTR) *FixedPortion,
+ EndOfVariableData,
+ &NetR->lpLocalName
+ );
+
+ ASSERT(FitInBuffer);
+ }
+ else {
+ NetR->lpLocalName = NULL;
+ }
+
+ //
+ // SystemPath
+ //
+ if (ARGUMENT_PRESENT(SystemPath)) {
+ FitInBuffer = NwlibCopyStringToBuffer(
+ SystemPath,
+ wcslen(SystemPath),
+ (LPCWSTR) *FixedPortion,
+ EndOfVariableData,
+ &lpSystem
+ );
+
+ ASSERT(FitInBuffer);
+ }
+ else {
+ lpSystem = NULL;
+ }
+
+ if (ARGUMENT_PRESENT(lppSystem)) {
+ *lppSystem = lpSystem;
+ }
+
+ //
+ // ProviderName
+ //
+ FitInBuffer = NwlibCopyStringToBuffer(
+ NwProviderName,
+ wcslen(NwProviderName),
+ (LPCWSTR) *FixedPortion,
+ EndOfVariableData,
+ &NetR->lpProvider
+ );
+
+ ASSERT(FitInBuffer);
+
+ if (! FitInBuffer) {
+ return WN_MORE_DATA;
+ }
+
+ return NO_ERROR;
+}
+
+
+DWORD
+NwWritePrinterInfoEntry(
+ IN OUT LPBYTE *FixedPortion,
+ IN OUT LPWSTR *EndOfVariableData,
+ IN LPWSTR ContainerName OPTIONAL,
+ IN LPWSTR RemoteName,
+ IN DWORD Flags,
+ OUT LPDWORD EntrySize
+ )
+/*++
+
+Routine Description:
+
+ This function packages a PRINTER_INFO_1 entry into the user output buffer.
+
+Arguments:
+
+ FixedPortion - Supplies a pointer to the output buffer where the next
+ entry of the fixed portion of the use information will be written.
+ This pointer is updated to point to the next fixed portion entry
+ after a PRINT_INFO_1 entry is written.
+
+ EndOfVariableData - Supplies a pointer just off the last available byte
+ in the output buffer. This is because the variable portion of the
+ user information is written into the output buffer starting from
+ the end.
+
+ This pointer is updated after any variable length information is
+ written to the output buffer.
+
+ ContainerName - Supplies the full path qualifier to make RemoteName
+ a full UNC name.
+
+ RemoteName - Supplies the remote resource name.
+
+ Flags - Supplies the flag which indicates that the RemoteName
+ is either a container or not and the icon to use.
+
+ EntrySize - Receives the size of the PRINTER_INFO_1 entry in bytes.
+
+Return Value:
+
+ NO_ERROR - Successfully wrote entry into user buffer.
+
+ ERROR_NOT_ENOUGH_MEMORY - Failed to allocate work buffer.
+
+ ERROR_INSUFFICIENT_BUFFER - Buffer was too small to fit entry.
+
+--*/ // NwWritePrinterInfoEntry
+{
+ BOOL FitInBuffer = TRUE;
+ PRINTER_INFO_1W *pPrinterInfo1 = (PRINTER_INFO_1W *) *FixedPortion;
+ LPWSTR RemoteBuffer;
+
+ *EntrySize = sizeof(PRINTER_INFO_1W) +
+ ( 2 * wcslen(RemoteName) + 2) * sizeof(WCHAR);
+
+ if (ARGUMENT_PRESENT(ContainerName)) {
+ *EntrySize += wcslen(ContainerName) * sizeof(WCHAR);
+ }
+ else {
+ // 3 is for the length of "!\\"
+ *EntrySize += (wcslen(NwProviderName) + 3) * sizeof(WCHAR);
+ }
+
+ *EntrySize = ROUND_UP_COUNT( *EntrySize, ALIGN_DWORD);
+
+ //
+ // See if buffer is large enough to fit the entry.
+ //
+ if (((DWORD) *FixedPortion + *EntrySize) >
+ (DWORD) *EndOfVariableData) {
+
+ return ERROR_INSUFFICIENT_BUFFER;
+ }
+
+ pPrinterInfo1->Flags = Flags;
+ pPrinterInfo1->pComment = NULL;
+
+ //
+ // Update fixed entry pointer to next entry.
+ //
+ (DWORD) (*FixedPortion) += sizeof(PRINTER_INFO_1W);
+
+ //
+ // Name
+ //
+ if (ARGUMENT_PRESENT(ContainerName)) {
+
+ //
+ // Prefix the RemoteName with its container name making the
+ // it a fully-qualified UNC name.
+ //
+ RemoteBuffer = (PVOID) LocalAlloc(
+ LMEM_ZEROINIT,
+ (wcslen(ContainerName) + wcslen(RemoteName)
+ + 1) * sizeof(WCHAR) );
+
+ if (RemoteBuffer == NULL) {
+ KdPrint(("NWWORKSTATION: NwWritePrinterInfoEntry LocalAlloc failed %lu\n", GetLastError()));
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ wcscpy(RemoteBuffer, ContainerName);
+ wcscat(RemoteBuffer, RemoteName);
+ }
+ else {
+ //
+ // Prefix the RemoteName with its provider name
+ //
+ RemoteBuffer = (PVOID) LocalAlloc(
+ LMEM_ZEROINIT,
+ (wcslen(RemoteName) +
+ wcslen(NwProviderName) + 4)
+ * sizeof(WCHAR) );
+
+ if (RemoteBuffer == NULL) {
+ KdPrint(("NWWORKSTATION: NwWritePrinterInfoEntry LocalAlloc failed %lu\n", GetLastError()));
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ wcscpy(RemoteBuffer, NwProviderName );
+ wcscat(RemoteBuffer, L"!\\\\" );
+ wcscat(RemoteBuffer, RemoteName);
+ }
+
+ FitInBuffer = NwlibCopyStringToBuffer(
+ RemoteBuffer,
+ wcslen(RemoteBuffer),
+ (LPCWSTR) *FixedPortion,
+ EndOfVariableData,
+ &pPrinterInfo1->pName );
+
+ (void) LocalFree((HLOCAL) RemoteBuffer);
+
+ ASSERT(FitInBuffer);
+
+ //
+ // Description
+ //
+ FitInBuffer = NwlibCopyStringToBuffer(
+ RemoteName,
+ wcslen(RemoteName),
+ (LPCWSTR) *FixedPortion,
+ EndOfVariableData,
+ &pPrinterInfo1->pDescription );
+
+ ASSERT(FitInBuffer);
+
+ if (! FitInBuffer) {
+ return ERROR_INSUFFICIENT_BUFFER;
+ }
+
+ return NO_ERROR;
+}
+
+
+int _CRTAPI1
+SortFunc(
+ IN CONST VOID *p1,
+ IN CONST VOID *p2
+)
+/*++
+
+Routine Description:
+
+ This function is used in qsort to compare the descriptions of
+ two printer_info_1 structure.
+
+Arguments:
+
+ p1 - Points to a PRINTER_INFO_1 structure
+ p2 - Points to a PRINTER_INFO_1 structure to compare with p1
+
+Return Value:
+
+ Same as return value of lstrccmpi.
+
+--*/
+{
+ PRINTER_INFO_1W *pFirst = (PRINTER_INFO_1W *) p1;
+ PRINTER_INFO_1W *pSecond = (PRINTER_INFO_1W *) p2;
+
+ return lstrcmpiW( pFirst->pDescription, pSecond->pDescription );
+}
+
+
+
+DWORD
+NwGetConnectionInformation(
+ IN LPWSTR lpName,
+ OUT LPWSTR lpUserName,
+ OUT LPWSTR lpHostServer
+ )
+{
+ DWORD status = NO_ERROR;
+ NTSTATUS ntstatus = STATUS_SUCCESS;
+ IO_STATUS_BLOCK IoStatusBlock;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ ACCESS_MASK DesiredAccess = SYNCHRONIZE | FILE_LIST_DIRECTORY;
+ HANDLE hRdr = NULL;
+ BOOL fImpersonate = FALSE ;
+
+ WCHAR OpenString[] = L"\\Device\\Nwrdr\\*";
+ UNICODE_STRING OpenName;
+
+ OEM_STRING OemArg;
+ UNICODE_STRING ConnectionName;
+ WCHAR ConnectionBuffer[512];
+
+ ULONG BufferSize = 512;
+ ULONG RequestSize, ReplyLen;
+ PNWR_REQUEST_PACKET Request;
+ BYTE *Reply;
+
+ PCONN_INFORMATION pConnInfo;
+ UNICODE_STRING Name;
+
+ //
+ // Allocate buffer space.
+ //
+
+ Request = (PNWR_REQUEST_PACKET) LocalAlloc( LMEM_ZEROINIT, BufferSize );
+
+ if ( !Request )
+ {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+
+ goto ErrorExit;
+ }
+
+ //
+ // Impersonate the client
+ //
+ if ( ( status = NwImpersonateClient() ) != NO_ERROR )
+ {
+ goto ErrorExit;
+ }
+
+ fImpersonate = TRUE;
+
+ //
+ // Convert the connect name to unicode.
+ //
+ ConnectionName.Length = wcslen( lpName )* sizeof(WCHAR);
+ ConnectionName.MaximumLength = sizeof( ConnectionBuffer );
+ ConnectionName.Buffer = ConnectionBuffer;
+
+ wcscpy( ConnectionName.Buffer, lpName );
+ _wcsupr( ConnectionName.Buffer );
+
+ //
+ // Set up the object attributes.
+ //
+
+ RtlInitUnicodeString( &OpenName, OpenString );
+
+ InitializeObjectAttributes( &ObjectAttributes,
+ &OpenName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL );
+
+ ntstatus = NtOpenFile( &hRdr,
+ DesiredAccess,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_VALID_FLAGS,
+ FILE_SYNCHRONOUS_IO_NONALERT );
+
+ if ( ntstatus != STATUS_SUCCESS )
+ {
+ status = RtlNtStatusToDosError(ntstatus);
+
+ goto ErrorExit;
+ }
+
+ //
+ // Fill out the request packet for FSCTL_NWR_GET_CONN_INFO.
+ //
+
+ Request->Parameters.GetConnInfo.ConnectionNameLength = ConnectionName.Length;
+ RtlCopyMemory( &(Request->Parameters.GetConnInfo.ConnectionName[0]),
+ ConnectionBuffer,
+ ConnectionName.Length );
+
+ RequestSize = sizeof( Request->Parameters.GetConnInfo ) + ConnectionName.Length;
+ Reply = ((PBYTE)Request) + RequestSize;
+ ReplyLen = BufferSize - RequestSize;
+
+ ntstatus = NtFsControlFile( hRdr,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_GET_CONN_INFO,
+ (PVOID) Request,
+ RequestSize,
+ (PVOID) Reply,
+ ReplyLen );
+
+ if ( ntstatus != STATUS_SUCCESS )
+ {
+ status = RtlNtStatusToDosError(ntstatus);
+
+ goto ErrorExit;
+ }
+
+ (void) NwRevertToSelf() ;
+ fImpersonate = FALSE;
+
+ NtClose( hRdr );
+
+ pConnInfo = (PCONN_INFORMATION) Reply;
+ wcscpy( lpUserName, pConnInfo->UserName );
+ wcscpy( lpHostServer, pConnInfo->HostServer );
+
+ LocalFree( Request );
+
+ return NO_ERROR;
+
+ErrorExit:
+
+ if ( fImpersonate )
+ (void) NwRevertToSelf() ;
+
+ if ( Request )
+ LocalFree( Request );
+
+ if ( hRdr )
+ NtClose( hRdr );
+
+ return status;
+}
+
+
+VOID
+NwpGetUncInfo(
+ IN LPWSTR lpstrUnc,
+ OUT WORD * slashCount,
+ OUT BOOL * isNdsUnc,
+ OUT LPWSTR * FourthSlash
+ )
+{
+ BYTE i;
+ WORD length = wcslen( lpstrUnc );
+
+ *isNdsUnc = FALSE;
+ *slashCount = 0;
+ *FourthSlash = NULL;
+
+ for ( i = 0; i < length; i++ )
+ {
+ if ( lpstrUnc[i] == L'=' )
+ {
+ *isNdsUnc = TRUE;
+ }
+
+ if ( lpstrUnc[i] == L'\\' )
+ {
+ *slashCount += 1;
+
+ if ( *slashCount == 4 )
+ {
+ *FourthSlash = &lpstrUnc[i];
+ }
+ }
+ }
+}
+
+
+DWORD
+NwpGetCurrentUserRegKey(
+ IN DWORD DesiredAccess,
+ OUT HKEY *phKeyCurrentUser
+ )
+/*++
+
+Routine Description:
+
+ This routine opens the current user's registry key under
+ \HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\NWCWorkstation\Parameters
+
+Arguments:
+
+ DesiredAccess - The access mask to open the key with
+
+ phKeyCurrentUser - Receives the opened key handle
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+ DWORD err;
+ HKEY hkeyWksta;
+ LPWSTR CurrentUser;
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters
+ //
+ err = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ,
+ &hkeyWksta
+ );
+
+ if ( err ) {
+ KdPrint(("NWPROVAU: NwGetCurrentUserRegKey open Parameters key unexpected error %lu!\n", err));
+ return err;
+ }
+ //
+ // Get the current user's SID string.
+ //
+ err = NwReadRegValue(
+ hkeyWksta,
+ NW_CURRENTUSER_VALUENAME,
+ &CurrentUser
+ );
+
+
+ if ( err ) {
+ KdPrint(("NWPROVAU: NwGetCurrentUserRegKey read CurrentUser value unexpected error %lu!\n", err));
+ (void) RegCloseKey( hkeyWksta );
+ return err;
+ }
+
+ (void) RegCloseKey( hkeyWksta );
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters\Option
+ //
+ err = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_OPTION_REGKEY,
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ,
+ &hkeyWksta
+ );
+
+ if ( err ) {
+ KdPrint(("NWPROVAU: NwGetCurrentUserRegKey open Parameters\\Option key unexpected error %lu!\n", err));
+ return err;
+ }
+
+ //
+ // Open current user's key
+ //
+ err = RegOpenKeyExW(
+ hkeyWksta,
+ CurrentUser,
+ REG_OPTION_NON_VOLATILE,
+ DesiredAccess,
+ phKeyCurrentUser
+ );
+
+ if ( err == ERROR_FILE_NOT_FOUND)
+ {
+ DWORD Disposition;
+
+ //
+ // Create <NewUser> key under NWCWorkstation\Parameters\Option
+ //
+ err = RegCreateKeyExW(
+ hkeyWksta,
+ CurrentUser,
+ 0,
+ WIN31_CLASS,
+ REG_OPTION_NON_VOLATILE,
+ DesiredAccess,
+ NULL, // security attr
+ phKeyCurrentUser,
+ &Disposition
+ );
+
+ }
+
+ if ( err ) {
+ KdPrint(("NWPROVAU: NwGetCurrentUserRegKey open or create of Parameters\\Option\\%ws key failed %lu\n", CurrentUser, err));
+ }
+
+ (void) RegCloseKey( hkeyWksta );
+ (void) LocalFree((HLOCAL)CurrentUser) ;
+ return err;
+}
+
+
+DWORD
+NwQueryInfo(
+ OUT LPWSTR *ppszPreferredSrv
+ )
+/*++
+
+Routine Description:
+ This routine gets the user's preferred server and print options from
+ the registry.
+
+Arguments:
+
+ ppszPreferredSrv - Receives the user's preferred server
+
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+
+ HKEY hKeyCurrentUser = NULL;
+ DWORD BufferSize;
+ DWORD BytesNeeded;
+ DWORD ValueType;
+ LPWSTR PreferredServer ;
+ DWORD err ;
+
+ //
+ // get to right place in registry and allocate dthe buffer
+ //
+ if (err = NwpGetCurrentUserRegKey( KEY_READ, &hKeyCurrentUser))
+ {
+ //
+ // If somebody mess around with the registry and we can't find
+ // the registry, just use the defaults.
+ //
+ *ppszPreferredSrv = NULL;
+ return NO_ERROR;
+ }
+
+ BufferSize = sizeof(WCHAR) * (MAX_PATH + 2) ;
+ PreferredServer = (LPWSTR) LocalAlloc(LPTR, BufferSize) ;
+ if (!PreferredServer)
+ return (GetLastError()) ;
+
+ //
+ // Read PreferredServer value into Buffer.
+ //
+ BytesNeeded = BufferSize ;
+
+ err = RegQueryValueExW( hKeyCurrentUser,
+ NW_SERVER_VALUENAME,
+ NULL,
+ &ValueType,
+ (LPBYTE) PreferredServer,
+ &BytesNeeded );
+
+ if (err != NO_ERROR)
+ {
+ //
+ // set to empty and carry on
+ //
+ PreferredServer[0] = 0;
+ }
+
+ if (hKeyCurrentUser != NULL)
+ (void) RegCloseKey(hKeyCurrentUser) ;
+ *ppszPreferredSrv = PreferredServer ;
+ return NO_ERROR ;
+}
+
+
diff --git a/private/nw/svcdlls/nwwks/server/gateway.c b/private/nw/svcdlls/nwwks/server/gateway.c
new file mode 100644
index 000000000..48b8ecf3b
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/server/gateway.c
@@ -0,0 +1,1258 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ gateway.c
+
+Abstract:
+
+ This module contains gateway devices routines supported by
+ NetWare Workstation service.
+
+Author:
+
+ Chuck Y Chan (ChuckC) 31-Oct-1993
+
+Revision History:
+
+--*/
+
+#include <nw.h>
+#include <handle.h>
+#include <nwreg.h>
+#include <nwlsa.h>
+#include <nwapi.h>
+
+#include <lmcons.h>
+#include <lmshare.h>
+
+
+//-------------------------------------------------------------------//
+// //
+// Local Function Prototypes //
+// //
+//-------------------------------------------------------------------//
+
+//
+// wrapper round the RPC routines.
+//
+
+DWORD
+NwrEnumGWDevices(
+ LPWSTR Reserved,
+ LPDWORD Index,
+ LPBYTE Buffer,
+ DWORD BufferSize,
+ LPDWORD BytesNeeded,
+ LPDWORD EntriesRead
+ )
+/*++
+
+Routine Description:
+
+ This routine enumerates the special gateway devices (redirections)
+ that are cureently in use.
+
+Arguments:
+
+ Index - Point to start enumeration. Should be zero for first call.
+ This is set by the function and can be used to resume the
+ enumeration.
+
+ Buffer - buffer for return data
+
+ BufferSize - size of buffer in bytes
+
+ BytesNeeded - number of bytes needed to return all the data
+
+ EntriesRead - number of entries read
+
+Return Value:
+
+ Returns the appropriate Win32 error. If NO_ERROR or ERROR_MORE_DATA
+ then EntriesRead will indicated the number of valid entries in buffer.
+
+--*/
+{
+ UNREFERENCED_PARAMETER(Reserved);
+
+ return ( NwEnumerateGWDevices(
+ Index,
+ Buffer,
+ BufferSize,
+ BytesNeeded,
+ EntriesRead
+ ) ) ;
+}
+
+
+DWORD
+NwrAddGWDevice(
+ LPWSTR Reserved,
+ LPWSTR DeviceName,
+ LPWSTR RemoteName,
+ LPWSTR AccountName,
+ LPWSTR Password,
+ DWORD Flags
+ )
+/*++
+
+Routine Description:
+
+ This routine adds a gateway redirection.
+
+Arguments:
+
+ DeviceName - the drive to redirect
+
+ RemoteName - the remote network resource to redirect to
+
+ Flags - supplies the options (eg. UpdateRegistry & make this sticky)
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+ DWORD status ;
+ UNREFERENCED_PARAMETER(Reserved);
+
+ //
+ // make connection to the server to ensure we have a connection.
+ // if GatewayConnectionAlways is false, the function will immediately
+ // delete the connection, so this will just be an access check.
+ //
+ status = NwCreateGWConnection( RemoteName,
+ AccountName,
+ Password,
+ GatewayConnectionAlways) ;
+
+ if (status != NO_ERROR)
+ {
+ return status ;
+ }
+
+ //
+ // make the symbolic link
+ //
+ return ( NwCreateGWDevice(
+ DeviceName,
+ RemoteName,
+ Flags
+ ) ) ;
+}
+
+
+DWORD
+NwrDeleteGWDevice(
+ LPWSTR Reserved,
+ LPWSTR DeviceName,
+ DWORD Flags
+ )
+/*++
+
+Routine Description:
+
+ This routine enumerates the special gateway devices (redirections)
+ that are cureently in use.
+
+Arguments:
+
+ Index - Point to start enumeration. Should be zero for first call.
+ This is set by the function and can be used to resume the
+ enumeration.
+
+ Buffer - buffer for return data
+
+ BufferSize - size of buffer in bytes
+
+ BytesNeeded - number of bytes needed to return all the data
+
+ EntriesRead - number of entries read
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+ UNREFERENCED_PARAMETER(Reserved);
+
+ return ( NwRemoveGWDevice(
+ DeviceName,
+ Flags
+ ) ) ;
+}
+
+
+DWORD
+NwrQueryGatewayAccount(
+ LPWSTR Reserved,
+ LPWSTR AccountName,
+ DWORD AccountNameLen,
+ LPDWORD AccountCharsNeeded,
+ LPWSTR Password,
+ DWORD PasswordLen,
+ LPDWORD PasswordCharsNeeded
+ )
+/*++
+
+Routine Description:
+
+ Query the gateway account info. specifically, the Account name and
+ the passeord stored as an LSA secret.
+
+Arguments:
+
+ AccountName - buffer used to return account name
+
+ AccountNameLen - length of buffer
+
+ AccountCharsNeeded - number of chars needed. only set properly if
+ AccountNameLen is too small.
+
+ Password - buffer used to return account name
+
+ PasswordLen - length of buffer
+
+ PasswordCharsNeeded - number of chars needed, only set properly if
+ PasswordLen is too small.
+
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+ UNREFERENCED_PARAMETER(Reserved);
+
+ return ( NwQueryGWAccount(
+ AccountName,
+ AccountNameLen,
+ AccountCharsNeeded,
+ Password,
+ PasswordLen,
+ PasswordCharsNeeded
+ ) ) ;
+}
+
+
+DWORD
+NwrSetGatewayAccount(
+ LPWSTR Reserved,
+ LPWSTR AccountName,
+ LPWSTR Password
+ )
+/*++
+
+Routine Description:
+
+ Set the account and password to be used for gateway access.
+
+Arguments:
+
+ AccountName - the account (NULL terminated)
+
+ Password - the password string (NULL terminated)
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+ UNREFERENCED_PARAMETER(Reserved);
+
+ return ( NwSetGWAccount(
+ AccountName,
+ Password
+ ) ) ;
+}
+
+
+//
+// actual functions
+//
+
+
+
+DWORD
+NwEnumerateGWDevices(
+ LPDWORD Index,
+ LPBYTE Buffer,
+ DWORD BufferSize,
+ LPDWORD BytesNeeded,
+ LPDWORD EntriesRead
+ )
+/*++
+
+Routine Description:
+
+ This routine enumerates the special gateway devices (redirections)
+ that are cureently in use.
+
+Arguments:
+
+ Index - Point to start enumeration. Should be zero for first call.
+ This is set by the function and can be used to resume the
+ enumeration.
+
+ Buffer - buffer for return data
+
+ BufferSize - size of buffer in bytes
+
+ BytesNeeded - number of bytes needed to return all the data
+
+ EntriesRead - number of entries read
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+ DWORD NwRdrNameLength, NwProviderNameSize ;
+ DWORD i, status ;
+ DWORD Length, Count, BytesRequired, SkipCount ;
+ WCHAR Drive[3] ;
+ WCHAR Path[MAX_PATH+1] ;
+ NETRESOURCEW *lpNetRes = (NETRESOURCEW *) Buffer ;
+ LPBYTE BufferEnd = Buffer + ROUND_DOWN_COUNT(BufferSize,ALIGN_WCHAR) ;
+
+ //
+ // init the parts of the drives string we never change
+ //
+ Drive[1] = L':' ;
+ Drive[2] = 0 ;
+
+ Count = 0 ;
+ BytesRequired = 0 ;
+ SkipCount = *Index ;
+ NwProviderNameSize = wcslen(NwProviderName) + 1 ;
+ NwRdrNameLength = sizeof(DD_NWFS_DEVICE_NAME_U)/sizeof(WCHAR) - 1 ;
+
+
+ //
+ // for all logical drives
+ //
+ for (i = 0; i <26 ; i++)
+ {
+ BOOL GatewayDrive = FALSE ;
+
+ Drive[0] = L'A' + (USHORT)i ;
+
+ //
+ // get the symbolic link
+ //
+ Length = QueryDosDeviceW(Drive,
+ Path,
+ sizeof(Path)/sizeof(Path[0])) ;
+
+ //
+ // the value must be at least as long as our device name
+ //
+ if (Length >= NwRdrNameLength + 4)
+ {
+ //
+ // and it must match the following criteria:
+ // 1) start with \device\nwrdr
+ // 2) must have '\' after \device\nwrdr
+ // 3) must not have colon (ie. must be`
+ // \\device\nwrdr\server\share, and not
+ // \\device\nwrdr\x:\server\share
+ //
+
+ if ((_wcsnicmp(Path,DD_NWFS_DEVICE_NAME_U,NwRdrNameLength)
+ == 0)
+ && (Path[NwRdrNameLength] == '\\')
+ && (Path[NwRdrNameLength+2] != ':'))
+ {
+ //
+ // if this is an indexed read, skip the first N.
+ // this is inefficient, but we do not expect to
+ // have to go thru this very often. there are few
+ // such devices, and any reasonable buffer (even 1K)
+ // should get them all first time.
+ //
+
+ if (SkipCount)
+ SkipCount-- ;
+ else
+ GatewayDrive = TRUE ; // found a drive we want
+ }
+ }
+
+ if (GatewayDrive)
+ {
+ //
+ // we meet all criteria above
+ //
+
+ DWORD UncSize ;
+
+ UncSize = Length - NwRdrNameLength + 2 ;
+ BytesRequired += ( sizeof(NETRESOURCE) +
+ (UncSize * sizeof(WCHAR)) +
+ (NwProviderNameSize * sizeof(WCHAR)) +
+ (3 * sizeof(WCHAR))) ; // 3 for drive, X:\0
+
+ if (BytesRequired <= BufferSize)
+ {
+ LPWSTR lpStr = (LPWSTR) BufferEnd ;
+
+ Count++ ;
+
+ //
+ // copy the drive name
+ //
+ lpStr -= 3 ;
+ wcscpy(lpStr, Drive) ;
+ lpNetRes->lpLocalName = (LPWSTR) ((LPBYTE)lpStr - Buffer) ;
+
+ //
+ // copy the UNC name
+ //
+ lpStr -= UncSize ; // for the UNC name
+ lpStr[0] = L'\\' ;
+ wcscpy(lpStr+1, Path+NwRdrNameLength) ;
+ lpNetRes->lpRemoteName = (LPWSTR) ((LPBYTE)lpStr - Buffer) ;
+
+ //
+ // copy the provider name
+ //
+ lpStr -= NwProviderNameSize ; // for the provider name
+ wcscpy(lpStr, NwProviderName) ;
+ lpNetRes->lpProvider = (LPWSTR) ((LPBYTE)lpStr - Buffer) ;
+
+ //
+ // set up the rest of the structure
+ //
+ lpNetRes->dwScope = RESOURCE_CONNECTED ;
+ lpNetRes->dwType = RESOURCETYPE_DISK ;
+ lpNetRes->dwDisplayType = 0 ;
+ lpNetRes->dwUsage = 0 ;
+ lpNetRes->lpComment = 0 ;
+
+ lpNetRes++ ;
+ BufferEnd = (LPBYTE) lpStr ;
+ }
+ }
+
+ }
+
+
+ *EntriesRead = Count ; // set number of entries
+ *Index += Count ; // move index
+ *BytesNeeded = BytesRequired ; // set bytes needed
+
+ if (BytesRequired == 0) // no info left
+ return (ERROR_NO_MORE_ITEMS) ;
+
+ if (BytesRequired > BufferSize)
+ {
+ return (ERROR_MORE_DATA) ;
+ }
+
+ return NO_ERROR ;
+}
+
+
+DWORD
+NwCreateGWDevice(
+ LPWSTR DeviceName,
+ LPWSTR RemoteName,
+ DWORD Flags
+ )
+/*++
+
+Routine Description:
+
+ This routine adds a gateway redirection.
+
+Arguments:
+
+ DeviceName - the drive to redirect
+
+ RemoteName - the remote network resource to redirect to
+
+ Flags - supplies the options (eg. UpdateRegistry & make this sticky)
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+ LPWSTR ConnectName = NULL;
+ DWORD status ;
+ WCHAR Path [MAX_PATH + 1] ;
+
+ //
+ // validate/canon the name. Use a drive to specific we allow UNC only.
+ //
+ if ((status = NwLibCanonRemoteName( L"A:",
+ RemoteName,
+ &ConnectName,
+ NULL
+ )) != NO_ERROR)
+ {
+ return status;
+ }
+
+ //
+ // build up the full name of \device\nwrdr\server\volume
+ //
+ wcscpy(Path, DD_NWFS_DEVICE_NAME_U) ;
+ wcscat(Path, ConnectName+1 ) ;
+
+ //
+ // create the symbolic link
+ //
+ status = NwCreateSymbolicLink(DeviceName, Path) ;
+
+ (void) LocalFree((HLOCAL) ConnectName);
+
+ //
+ // if update registry is set, write it out
+ //
+ if ((status == NO_ERROR) && (Flags & NW_GW_UPDATE_REGISTRY))
+ {
+ HKEY hKey ;
+ DWORD dwDisposition ;
+
+ //
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Drives (create it if not there)
+ //
+ status = RegCreateKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_GATEWAY_DRIVES,
+ 0,
+ L"",
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE, // desired access
+ NULL, // default security
+ &hKey,
+ &dwDisposition // ignored
+ );
+
+ if ( status )
+ return status ;
+
+ status = RegSetValueExW(
+ hKey,
+ DeviceName,
+ 0,
+ REG_SZ,
+ (LPBYTE) RemoteName,
+ (wcslen(RemoteName)+1) * sizeof(WCHAR)) ;
+
+ RegCloseKey( hKey );
+ }
+
+ return status ;
+
+}
+
+DWORD
+NwRemoveGWDevice(
+ LPWSTR DeviceName,
+ DWORD Flags
+ )
+/*++
+
+Routine Description:
+
+ This routine deletes a gateway redirection.
+
+Arguments:
+
+ DeviceName - the drive to delete
+
+ Flags - supplies the options (eg. UpdateRegistry & make this sticky)
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+ DWORD status ;
+ LPWSTR Local = NULL ;
+
+ if (status = NwLibCanonLocalName(DeviceName,
+ &Local,
+ NULL))
+ return status ;
+
+ //
+ // delete the symbolic link
+ //
+ if (! DefineDosDeviceW(DDD_REMOVE_DEFINITION | DDD_RAW_TARGET_PATH,
+ Local,
+ DD_NWFS_DEVICE_NAME_U))
+ {
+ status = ERROR_INVALID_DRIVE ;
+ }
+
+ //
+ // If cleanup deleted (dangling) share is set go do it now.
+ // We loop thru all the shares looking for one that matches that drive.
+ // Then we ask the server to nuke that dangling share.
+ //
+ if ((status == NO_ERROR) && (Flags & NW_GW_CLEANUP_DELETED))
+ {
+ HKEY hKey ;
+ DWORD err ;
+
+ //
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Shares
+ //
+ err = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_GATEWAY_SHARES,
+ REG_OPTION_NON_VOLATILE, // options
+ KEY_READ | KEY_WRITE, // desired access
+ &hKey
+ );
+
+ if (err == NO_ERROR)
+ {
+ WCHAR Path[MAX_PATH + 1], ShareName[MAX_PATH+1] ;
+ DWORD dwType, i = 0, dwPathSize, dwShareNameSize ;
+
+ do {
+
+ dwPathSize = sizeof(Path),
+ dwShareNameSize = sizeof(ShareName)/sizeof(ShareName[0]) ;
+ dwType = REG_SZ ;
+
+ err = RegEnumValueW(hKey,
+ i,
+ ShareName,
+ &dwShareNameSize,
+ NULL,
+ &dwType,
+ (LPBYTE)Path,
+ &dwPathSize) ;
+
+ //
+ // Look for matching drive, eg. "X:"
+ // If have match, cleanup as best we can and break out now.
+ //
+ if ((err == NO_ERROR) &&
+ (_wcsnicmp(Path,DeviceName,2) == 0))
+ {
+ (void) NetShareDelSticky( NULL,
+ ShareName,
+ 0 ) ;
+ (void) RegDeleteValueW(hKey,
+ ShareName) ;
+ break ;
+ }
+
+ i++ ;
+
+ } while (err == NO_ERROR) ;
+
+ RegCloseKey( hKey );
+ }
+
+ }
+
+ //
+ // if update registry is set, write it out
+ //
+ if ((status == NO_ERROR) && (Flags & NW_GW_UPDATE_REGISTRY))
+ {
+ HKEY hKey ;
+ WCHAR Path[MAX_PATH + 1] ;
+ DWORD dwType, dwSize = sizeof(Path) ;
+
+ //
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Drives
+ //
+ status = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_GATEWAY_DRIVES,
+ REG_OPTION_NON_VOLATILE, // options
+ KEY_READ | KEY_WRITE, // desired access
+ &hKey
+ );
+
+ if ( status )
+ goto ExitPoint ;
+
+ if (GatewayConnectionAlways)
+ {
+ //
+ // Read the remote path and delete the connection
+ // if we made one in the first place.
+ //
+ status = RegQueryValueExW(
+ hKey,
+ Local,
+ NULL,
+ &dwType,
+ (LPBYTE) Path,
+ &dwSize
+ );
+
+ if (status == NO_ERROR)
+ {
+ (void) NwDeleteGWConnection(Path) ;
+ }
+ }
+
+ status = RegDeleteValueW(
+ hKey,
+ DeviceName
+ ) ;
+
+ RegCloseKey( hKey );
+ }
+
+ExitPoint:
+
+ if (Local)
+ (void) LocalFree((HLOCAL)Local) ;
+
+ return status ;
+}
+
+DWORD
+NwGetGatewayResource(
+ IN LPWSTR LocalName,
+ OUT LPWSTR RemoteName,
+ IN DWORD RemoteNameLen,
+ OUT LPDWORD CharsRequired
+ )
+/*++
+
+Routine Description:
+
+ For a gateway devicename, get the network resource associated with it.
+
+Arguments:
+
+ LocalName - name of devive to query
+
+ RemoteName - buffer to return the network resource
+
+ RemoteNameLen - size of buffer (chars)
+
+ CharsRequired - the number of chars needed
+
+Return Value:
+
+ WN_SUCCESS - success (the device is a gateway redirection)
+
+ WN_MORE_DATA - buffer too small, but device is a gateway redirection
+
+ WN_NOT_CONNECTED - not a gateway redirection
+
+--*/
+{
+ WCHAR Path[MAX_PATH+1] ;
+ DWORD Length ;
+ DWORD NwRdrNameLength ;
+
+ NwRdrNameLength = sizeof(DD_NWFS_DEVICE_NAME_U)/sizeof(WCHAR) - 1 ;
+
+ //
+ // retrieve symbolic link for the device
+ //
+ Length = QueryDosDeviceW(LocalName,
+ Path,
+ sizeof(Path)/sizeof(Path[0])) ;
+
+ //
+ // the result is only interesting if it can at least fit:
+ // \device\nwrdr\x\y
+ //
+ if (Length >= NwRdrNameLength + 4)
+ {
+ //
+ // check to make sure that the prefix is coreect, and that
+ // it is not of form: \device\nwrdr\x:\...
+ //
+ if ((_wcsnicmp(Path,DD_NWFS_DEVICE_NAME_U,NwRdrNameLength) == 0)
+ && (Path[NwRdrNameLength] == '\\')
+ && (Path[NwRdrNameLength+2] != ':'))
+ {
+ //
+ // check buffer size
+ //
+ if (RemoteNameLen < ((Length - NwRdrNameLength) + 1))
+ {
+ if (CharsRequired)
+ *CharsRequired = ((Length - NwRdrNameLength) + 1) ;
+ return WN_MORE_DATA ;
+ }
+
+ *RemoteName = L'\\' ;
+ wcscpy(RemoteName+1,Path+NwRdrNameLength) ;
+ return WN_SUCCESS ;
+ }
+ }
+
+ return WN_NOT_CONNECTED ;
+}
+
+
+DWORD
+NwQueryGWAccount(
+ LPWSTR AccountName,
+ DWORD AccountNameLen,
+ LPDWORD AccountCharsNeeded,
+ LPWSTR Password,
+ DWORD PasswordLen,
+ LPDWORD PasswordCharsNeeded
+ )
+/*++
+
+Routine Description:
+
+ Query the gateway account info. specifically, the Account name and
+ the passeord stored as an LSA secret.
+
+Arguments:
+
+ AccountName - buffer used to return account name
+
+ AccountNameLen - length of buffer
+
+ AccountCharsNeeded - number of chars needed. only set properly if
+ AccountNameLen is too small.
+
+ Password - buffer used to return account name
+
+ PasswordLen - length of buffer
+
+ PasswordCharsNeeded - number of chars needed, only set properly if
+ PasswordLen is too small.
+
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+ DWORD status = NO_ERROR ;
+ LONG RegError;
+
+ HKEY WkstaKey = NULL;
+ LPWSTR GatewayAccount = NULL;
+
+ PUNICODE_STRING StoredPassword = NULL;
+ PUNICODE_STRING StoredOldPassword = NULL;
+
+ *AccountCharsNeeded = 0 ;
+ *PasswordCharsNeeded = 0 ;
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters
+ //
+ RegError = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_REGKEY,
+ REG_OPTION_NON_VOLATILE, // options
+ KEY_READ, // desired access
+ &WkstaKey
+ );
+
+ if (RegError != ERROR_SUCCESS)
+ {
+ return (RegError);
+ }
+
+ //
+ // Read the gateway account from the registry.
+ //
+ status = NwReadRegValue(
+ WkstaKey,
+ NW_GATEWAYACCOUNT_VALUENAME,
+ &GatewayAccount
+ );
+
+ if (status != NO_ERROR)
+ {
+ if (status != ERROR_FILE_NOT_FOUND)
+ goto CleanExit;
+
+ if (AccountNameLen > 0)
+ *AccountName = 0 ;
+ status = NO_ERROR ;
+ }
+ else
+ {
+ *AccountCharsNeeded = wcslen(GatewayAccount) + 1 ;
+ if (*AccountCharsNeeded > AccountNameLen)
+ {
+ status = ERROR_INSUFFICIENT_BUFFER ;
+ goto CleanExit;
+ }
+ wcscpy(AccountName,GatewayAccount);
+ }
+
+ //
+ // Read the password from its secret object in LSA.
+ //
+ status = NwGetPassword(
+ GATEWAY_USER,
+ &StoredPassword, // Must be freed with LsaFreeMemory
+ &StoredOldPassword // Must be freed with LsaFreeMemory
+ );
+
+ if (status != NO_ERROR)
+ {
+ if (status != ERROR_FILE_NOT_FOUND)
+ goto CleanExit;
+
+ if (PasswordLen > 0)
+ *Password = 0 ;
+
+ status = NO_ERROR ;
+ }
+ else
+ {
+ *PasswordCharsNeeded = StoredPassword->Length/sizeof(WCHAR) + 1 ;
+ if ((StoredPassword->Length/sizeof(WCHAR)) >= PasswordLen)
+ {
+ status = ERROR_INSUFFICIENT_BUFFER ;
+ goto CleanExit;
+ }
+ wcsncpy(Password,
+ StoredPassword->Buffer,
+ StoredPassword->Length/sizeof(WCHAR));
+ Password[StoredPassword->Length/sizeof(WCHAR)] = 0 ;
+ }
+
+
+CleanExit:
+
+ if (StoredPassword != NULL) {
+ (void) LsaFreeMemory((PVOID) StoredPassword);
+ }
+
+ if (StoredOldPassword != NULL) {
+ (void) LsaFreeMemory((PVOID) StoredOldPassword);
+ }
+
+ if (GatewayAccount != NULL) {
+ (void) LocalFree((HLOCAL) GatewayAccount);
+ }
+
+ (void) RegCloseKey(WkstaKey);
+
+ return status ;
+}
+
+DWORD
+NwSetGWAccount(
+ LPWSTR AccountName,
+ LPWSTR Password
+ )
+/*++
+
+Routine Description:
+
+ Set the account and password to be used for gateway access.
+
+Arguments:
+
+ AccountName - the account (NULL terminated)
+
+ Password - the password string (NULL terminated)
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+ DWORD status ;
+ HKEY WkstaKey = NULL;
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters
+ //
+ status = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_REGKEY,
+ REG_OPTION_NON_VOLATILE, // options
+ KEY_WRITE, // desired access
+ &WkstaKey
+ );
+
+ if (status != ERROR_SUCCESS)
+ {
+ return (status);
+ }
+
+ //
+ // Write the account name out
+ //
+ status = RegSetValueExW(
+ WkstaKey,
+ NW_GATEWAYACCOUNT_VALUENAME,
+ 0,
+ REG_SZ,
+ (LPVOID) AccountName,
+ (wcslen(AccountName) + 1) * sizeof(WCHAR)
+ );
+
+ if (status == NO_ERROR)
+ {
+ status = NwSetPassword(
+ GATEWAY_USER,
+ Password) ;
+ }
+
+ return status ;
+}
+
+
+DWORD
+NwCreateRedirections(
+ LPWSTR Account,
+ LPWSTR Password
+ )
+/*++
+
+Routine Description:
+
+ Create the gateway redirections from what is stored in registry.
+ As we go along, we validate that we have access to it using the
+ gateway account.
+
+Arguments:
+
+ AccountName - the account (NULL terminated)
+
+ Password - the password string (NULL terminated)
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+ DWORD err, i, type ;
+ HKEY hKey ;
+ FILETIME FileTime ;
+ WCHAR Class[256], Device[64], Path[MAX_PATH+1] ;
+ DWORD dwClass, dwSubKeys, dwMaxSubKey, dwMaxClass,
+ dwValues, dwMaxValueName, dwMaxValueData, dwSDLength,
+ dwDeviceLength, dwPathLength ;
+
+ //
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCGateway\Parameters
+ //
+ err = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_GATEWAY_DRIVES,
+ REG_OPTION_NON_VOLATILE, // options
+ KEY_READ, // desired access
+ &hKey
+ );
+
+ if ( err )
+ return err ;
+
+ dwClass = sizeof(Class)/sizeof(Class[0]) ;
+ err = RegQueryInfoKeyW(hKey,
+ Class,
+ &dwClass,
+ NULL,
+ &dwSubKeys,
+ &dwMaxSubKey,
+ &dwMaxClass,
+ &dwValues,
+ &dwMaxValueName,
+ &dwMaxValueData,
+ &dwSDLength,
+ &FileTime) ;
+ if ( err )
+ {
+ RegCloseKey( hKey );
+ return err ;
+ }
+
+ //
+ // for each value we have a redirection to recreate
+ //
+ for (i = 0; i < dwValues; i++)
+ {
+ dwDeviceLength = sizeof(Device)/sizeof(Device[0]) ;
+ dwPathLength = sizeof(Path) ;
+ type = REG_SZ ;
+ err = RegEnumValueW(hKey,
+ i,
+ Device,
+ &dwDeviceLength,
+ NULL,
+ &type,
+ (LPBYTE)Path,
+ &dwPathLength) ;
+
+ //
+ // connect to the server. this will take up a connection but
+ // it will also make sure we have one. on a low limit server, if
+ // we rely on UNC then it is quite likely that other people will
+ // come along & use up all the connections, preventing the Gateway
+ // from getting to it.
+ //
+ // user may turn this off by setting Registry value that results
+ // GatewayConnectionAlways being false.
+ //
+ // regardless of result, we carry on. so if server is down & comes
+ // up later, the symbolic link to UNC will still work.
+ //
+ if (!err)
+ {
+ (void) NwCreateGWConnection( Path,
+ Account,
+ Password,
+ GatewayConnectionAlways) ;
+ }
+
+ //
+ // create the symbolic link
+ //
+ if (!err)
+ {
+ err = NwCreateGWDevice(Device, Path, 0L) ;
+ }
+
+ if (err)
+ {
+ //
+ // log the error in the event log
+ //
+
+ WCHAR Number[16] ;
+ LPWSTR InsertStrings[3] ;
+
+ wsprintfW(Number, L"%d", err) ;
+ InsertStrings[0] = Device ;
+ InsertStrings[1] = Path ;
+ InsertStrings[2] = Number ;
+
+ NwLogEvent(EVENT_NWWKSTA_CANNOT_REDIRECT_DEVICES,
+ 3,
+ InsertStrings,
+ 0) ;
+ }
+ }
+
+ RegCloseKey( hKey );
+
+
+ return NO_ERROR ;
+}
+
+DWORD
+NwDeleteRedirections(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Delete all gateway devices
+
+Arguments:
+
+Return Value:
+
+ Returns the appropriate Win32 error.
+
+--*/
+{
+ LPBYTE Buffer ;
+ DWORD i, status, Index, BufferSize, EntriesRead, BytesNeeded ;
+ LPNETRESOURCE lpNetRes ;
+
+ Index = 0 ;
+
+ //
+ // below is good initial guess
+ //
+ BufferSize = 26 * (sizeof(NETRESOURCE) +
+ (3 + MAX_PATH + 1 + MAX_PATH + 1) * sizeof(WCHAR)) ;
+ Buffer = (LPBYTE) LocalAlloc(LPTR, BufferSize) ;
+
+ if (!Buffer)
+ return (GetLastError()) ;
+
+ lpNetRes = (LPNETRESOURCE) Buffer ;
+
+ status = NwrEnumGWDevices(NULL,
+ &Index,
+ Buffer,
+ BufferSize,
+ &BytesNeeded,
+ &EntriesRead) ;
+
+ //
+ // reallocate as need
+ //
+ if (status == ERROR_MORE_DATA || status == ERROR_INSUFFICIENT_BUFFER)
+ {
+ Buffer = (LPBYTE) LocalReAlloc((HLOCAL)Buffer,
+ BytesNeeded,
+ LMEM_ZEROINIT) ;
+ if (!Buffer)
+ return (GetLastError()) ;
+ Index = 0 ;
+ BufferSize = BytesNeeded ;
+ status = NwrEnumGWDevices(NULL,
+ &Index,
+ Buffer,
+ BufferSize,
+ &BytesNeeded,
+ &EntriesRead) ;
+
+ }
+
+ if (status != NO_ERROR)
+ return status ;
+
+ //
+ // loop thru and delete all the devices
+ //
+ for (i = 0; i < EntriesRead; i++)
+ {
+ status = NwrDeleteGWDevice(NULL,
+ (LPWSTR)((LPBYTE)Buffer +
+ (DWORD)lpNetRes->lpLocalName),
+ 0L) ;
+
+ //
+ // no need report the error, since we are shutting down.
+ // there is no real deletion here - just removing the symbolic link.
+ //
+
+ lpNetRes++ ;
+ }
+
+ return NO_ERROR ;
+}
+
+
+
diff --git a/private/nw/svcdlls/nwwks/server/handle.h b/private/nw/svcdlls/nwwks/server/handle.h
new file mode 100644
index 000000000..043b17e9c
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/server/handle.h
@@ -0,0 +1,154 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ handle.h
+
+Abstract:
+
+ Header which defines the context handle structure.
+
+Author:
+
+ Rita Wong (ritaw) 18-Feb-1993
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+#ifndef _NW_HANDLE_INLUDED_
+#define _NW_HANDLE_INLUDED_
+
+//
+// Signature value in handle
+//
+#define NW_HANDLE_SIGNATURE 0x77442323
+
+//
+// Flags used to indicate whether Context Handles are using NDS or not
+//
+#define CURRENTLY_ENUMERATING_NON_NDS 0
+#define CURRENTLY_ENUMERATING_NDS 1
+
+//
+// Context handle type
+//
+typedef enum _NW_ENUM_TYPE {
+
+ NwsHandleListConnections = 10,
+ NwsHandleListContextInfo_Tree,
+ NwsHandleListContextInfo_Server,
+ NwsHandleListServersAndNdsTrees,
+ NwsHandleListVolumes,
+ NwsHandleListQueues,
+ NwsHandleListVolumesQueues,
+ NwsHandleListDirectories,
+ NwsHandleListPrintServers,
+ NwsHandleListPrintQueues,
+ NwsHandleListNdsSubTrees_Disk,
+ NwsHandleListNdsSubTrees_Print,
+ NwsHandleListNdsSubTrees_Any
+
+} NW_ENUM_TYPE, *PNW_ENUM_TYPE;
+
+//
+// Data associated with each opened context handle
+//
+typedef struct _NW_ENUM_CONTEXT {
+
+ //
+ // For block identification
+ //
+ DWORD Signature;
+
+ //
+ // Handle type
+ //
+ NW_ENUM_TYPE HandleType;
+
+ //
+ // Resume ID. This may be the identifier for the next entry
+ // to list or may be the last entry listed for the connection handle
+ // indicated by the flag dwUsingNds.
+ //
+ DWORD ResumeId;
+
+ //
+ // Type of object requested. Valid only when the handle type
+ // is NwsHandleListConnections.
+ //
+ DWORD ConnectionType;
+
+ //
+ // Internal handle to the object we have opened to perform
+ // the enumeration. This value exists only if the handle
+ // type is NwsHandleListVolumes, NwsHandleListDirectories,
+ // or NwsHandleListNdsSubTrees.
+ //
+ HANDLE TreeConnectionHandle;
+
+ //
+ // Value used to indicate the maximum number of volumes supported on
+ // a server. This is used for connection handles that enumerate volumes
+ // or volumes and queues (NwsHandleListVolumes or
+ // NwsHandleListVolumesQueues).
+ //
+ DWORD dwMaxVolumes;
+
+ //
+ // Flag used to indicate whether enumeration ResumeId is for
+ // NDS trees or servers.
+ //
+ DWORD dwUsingNds;
+
+ //
+ // Object identifier for NDS tree enumeration. The Oid of the
+ // container/oject in the path of ContainerName.
+ //
+ DWORD dwOid;
+
+ //
+ // The size of the buffer used for caching rdr data under enumeration.
+ //
+ DWORD NdsRawDataSize;
+
+ //
+ // The object identifier of the last object read from the rdr that was
+ // put into the local cache buffer NdsRawDataBuffer.
+ //
+ DWORD NdsRawDataId;
+
+ //
+ // The number of objects currently in the local cache buffer NdsRawDataBuffer.
+ //
+ DWORD NdsRawDataCount;
+
+ //
+ // The local cache buffer used for rdr data enumeration.
+ //
+ DWORD NdsRawDataBuffer;
+
+ //
+ // Full path name of the container object we are enumerating
+ // from.
+ //
+ // For NwsHandleListVolumes handle type this string points to:
+ // "\\ServerName"
+ //
+ // For NwsHandleListDirectories handle type this string points to:
+ // "\\ServerName\Volume\"
+ // or
+ // "\\ServerName\Volume\Directory\"
+ //
+ WCHAR ContainerName[1];
+
+} NW_ENUM_CONTEXT, *LPNW_ENUM_CONTEXT;
+
+
+#endif // _NW_HANDLE_INLUDED_
diff --git a/private/nw/svcdlls/nwwks/server/inswks.c b/private/nw/svcdlls/nwwks/server/inswks.c
new file mode 100644
index 000000000..5aed5e1a9
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/server/inswks.c
@@ -0,0 +1,320 @@
+/*++
+
+Copyright (c) 1991-92 Microsoft Corporation
+
+Module Name:
+
+ tacc.c
+
+Abstract:
+
+ Test for accounts.
+
+Author:
+
+ Rita Wong (ritaw) 02-May-1992
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#ifndef UNICODE
+#define UNICODE
+#endif
+
+#include <windows.h>
+
+#include <nwsnames.h>
+
+
+
+DWORD
+TestOpenSCManager(
+ OUT LPSC_HANDLE hScManager,
+ IN LPWSTR DatabaseName,
+ IN DWORD DesiredAccess,
+ IN DWORD ExpectedError
+ );
+
+DWORD
+TestCreateService(
+ IN SC_HANDLE hScManager,
+ IN LPWSTR ServiceName,
+ IN DWORD ServiceType,
+ IN LPWSTR BinaryPath,
+ IN LPWSTR Dependencies
+ );
+
+
+void _CRTAPI1
+main(
+ void
+ )
+{
+ DWORD status;
+ SC_HANDLE hScManager;
+ SC_HANDLE hService;
+ LONG RegError;
+ HKEY ServiceKey;
+ HKEY LinkageKey;
+ DWORD Disposition;
+ DWORD Type = 0x00000007;
+
+ PWCHAR Dependencies = L"MSIPX\0Streams\0Mup\0";
+
+
+ //
+ // Valid desired access
+ //
+ if (TestOpenSCManager(
+ &hScManager,
+ NULL,
+ SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE,
+ NO_ERROR
+ ) == NO_ERROR) {
+
+ //
+ // Install NwRdr file system driver
+ //
+ status = TestCreateService(
+ hScManager,
+ L"NwRdr",
+ SERVICE_FILE_SYSTEM_DRIVER,
+ L"\\SystemRoot\\System32\\Drivers\\nwrdr.sys",
+ NULL
+ );
+
+ if (status != NO_ERROR) {
+ (void) CloseServiceHandle(hScManager);
+ return;
+ }
+
+ //
+ // Install NWCWorkstation service own process
+ //
+ status = TestCreateService(
+ hScManager,
+ NW_SERVICE_WORKSTATION,
+ SERVICE_WIN32_SHARE_PROCESS,
+ L"%SystemRoot%\\System32\\nwsvc.exe",
+ Dependencies
+ );
+
+ (void) CloseServiceHandle(hScManager);
+
+ if (status != NO_ERROR) {
+ return;
+ }
+
+ //
+ // Write the linkage key under the NWCWorkstation key
+ //
+ RegError = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ L"System\\CurrentControlSet\\Services\\NWCWorkstation",
+ REG_OPTION_NON_VOLATILE,
+ KEY_READ | KEY_CREATE_SUB_KEY,
+ &ServiceKey
+ );
+
+ if (RegError != ERROR_SUCCESS) {
+ printf("RegOpenKeyExW failed %ld\n", RegError);
+ return;
+ }
+
+ RegError = RegCreateKeyExW(
+ ServiceKey,
+ L"Linkage",
+ 0,
+ WIN31_CLASS,
+ REG_OPTION_NON_VOLATILE, // options
+ KEY_WRITE,
+ NULL,
+ &LinkageKey,
+ &Disposition
+ );
+
+ RegCloseKey(ServiceKey);
+
+ if (RegError != ERROR_SUCCESS) {
+ printf("RegCreateKeyExW failed %ld\n", RegError);
+ return;
+ }
+
+ RegError = RegSetValueExW(
+ LinkageKey,
+ L"Bind",
+ 0,
+ REG_MULTI_SZ,
+ L"\\Device\\Streams\\IPX\0",
+ (wcslen(L"\\Device\\Streams\\IPX\0") + 1)
+ * sizeof(WCHAR)
+ );
+
+ RegCloseKey(LinkageKey);
+
+ if (RegError != ERROR_SUCCESS) {
+ printf("RegSetValueEx failed %ld\n", RegError);
+ return;
+ }
+
+ //
+ // Add a system event entry for the NetWare workstation
+ //
+ RegError = RegCreateKeyExW(
+ HKEY_LOCAL_MACHINE,
+ L"System\\CurrentControlSet\\Services\\Eventlog\\System\\NWCWorkstation",
+ 0,
+ WIN31_CLASS,
+ REG_OPTION_NON_VOLATILE,
+ KEY_WRITE,
+ NULL,
+ &ServiceKey,
+ &Disposition
+ );
+
+ if (RegError != ERROR_SUCCESS) {
+ printf("RegCreateKeyExW of eventlog entry failed %ld\n", RegError);
+ return;
+ }
+
+ RegError = RegSetValueExW(
+ ServiceKey,
+ L"EventMessageFile",
+ 0,
+ REG_EXPAND_SZ,
+ L"%SystemRoot%\\System32\\nwevent.dll",
+ wcslen(L"%SystemRoot%\\System32\\nwevent.dll")
+ * sizeof(WCHAR)
+ );
+
+ if (RegError != ERROR_SUCCESS) {
+ printf("RegSetValueExW of EventMessageFile value failed %ld\n", RegError);
+ RegCloseKey(ServiceKey);
+ return;
+ }
+
+ RegError = RegSetValueExW(
+ ServiceKey,
+ L"TypesSupported",
+ 0,
+ REG_DWORD,
+ &Type,
+ sizeof(DWORD)
+ );
+
+ RegCloseKey(ServiceKey);
+
+ if (RegError != ERROR_SUCCESS) {
+ printf("RegSetValueExW of TypesSupported value failed %ld\n", RegError);
+ return;
+ }
+
+ printf("Successfully installed transport for NWCWorkstation\n");
+ }
+
+}
+
+
+DWORD
+TestOpenSCManager(
+ OUT LPSC_HANDLE hScManager,
+ IN LPWSTR DatabaseName,
+ IN DWORD DesiredAccess,
+ IN DWORD ExpectedError
+ )
+{
+ DWORD status = NO_ERROR;
+
+
+ if (DatabaseName != NULL) {
+ printf("OpenSCManager: DatabaseName=%ws, DesiredAccess=%08lx\n",
+ DatabaseName, DesiredAccess);
+ }
+ else {
+ printf("OpenSCManager: DatabaseName=(null), DesiredAccess=%08lx\n",
+ DesiredAccess);
+ }
+
+ *hScManager = OpenSCManager(
+ NULL,
+ DatabaseName,
+ DesiredAccess
+ );
+
+ if (*hScManager == (SC_HANDLE) NULL) {
+
+ status = GetLastError();
+
+ if (ExpectedError != status) {
+ printf(" FAILED. Expected %lu, got %lu\n",
+ ExpectedError, status);
+ return status;
+ }
+ }
+ else {
+ if (ExpectedError != NO_ERROR) {
+ printf(" FAILED. Expected %lu, got NO_ERROR\n",
+ ExpectedError);
+ return NO_ERROR;
+ }
+ }
+
+ printf(" Got %lu as expected\n", status);
+
+ return status;
+
+}
+
+DWORD
+TestCreateService(
+ IN SC_HANDLE hScManager,
+ IN LPWSTR ServiceName,
+ IN DWORD ServiceType,
+ IN LPWSTR BinaryPath,
+ IN LPWSTR Dependencies
+ )
+{
+ DWORD status = NO_ERROR;
+ SC_HANDLE hService;
+
+
+ hService = CreateService(
+ hScManager,
+ ServiceName,
+ NULL,
+ 0,
+ ServiceType,
+ SERVICE_DEMAND_START,
+ SERVICE_ERROR_NORMAL,
+ BinaryPath,
+ NULL,
+ NULL,
+ Dependencies,
+ NULL,
+ NULL
+ );
+
+ if (hService == (SC_HANDLE) NULL) {
+ status = GetLastError();
+ printf("CreateService: %ws failed %lu\n", ServiceName, status);
+ return status;
+ }
+
+ printf("CreateService: Successfully created %ws\n", ServiceName);
+
+ (void) CloseServiceHandle(hService);
+
+ return status;
+}
diff --git a/private/nw/svcdlls/nwwks/server/makefile b/private/nw/svcdlls/nwwks/server/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/server/makefile
@@ -0,0 +1,6 @@
+#
+# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source
+# file to this component. This file merely indirects to the real make file
+# that is shared by all the components of NT OS/2
+#
+!INCLUDE $(NTMAKEENV)\makefile.def
diff --git a/private/nw/svcdlls/nwwks/server/nw.h b/private/nw/svcdlls/nwwks/server/nw.h
new file mode 100644
index 000000000..daf64591d
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/server/nw.h
@@ -0,0 +1,587 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ nw.h
+
+Abstract:
+
+ Main header of the NetWare Workstation service included by all
+ modules.
+
+Author:
+
+ Rita Wong (ritaw) 11-Dec-1992
+
+Environment:
+
+ User Mode -Win32
+
+Revision History:
+
+--*/
+
+#ifndef _NW_INCLUDED_
+#define _NW_INCLUDED_
+
+//
+// Includes
+//
+#include <stdlib.h>
+#include <string.h>
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <windef.h>
+#include <winbase.h>
+#include <winerror.h>
+#include <winsvc.h>
+#include <winreg.h>
+#include <winspool.h>
+
+#include <services.h> // LMSVCS_ENTRY_POINT
+
+#include <align.h>
+#include <nwcanon.h>
+#include <nwpkstr.h>
+#include <nwrpcp.h>
+
+#include <rpc.h>
+#include <nwwks.h>
+
+#include <nwevent.h>
+#include <ntddnwfs.h>
+#include <nwsvc.h>
+#include <handle.h>
+#include <ndsapi32.h>
+#include <ntddnwfs.h>
+
+#define NW_DRIVER_NAME DD_NWFS_FILESYS_NAME_U
+
+
+//
+// Debug trace level bits for turning on/off trace statements in the
+// Workstation service
+//
+
+//
+// Initialization and reading info from registry
+//
+#define NW_DEBUG_INIT 0x00000001
+
+//
+// Connection APIs
+//
+#define NW_DEBUG_CONNECT 0x00000002
+
+//
+// Enumeration APIs
+//
+#define NW_DEBUG_ENUM 0x00000004
+
+//
+// Credential management APIs
+//
+#define NW_DEBUG_LOGON 0x00000008
+
+//
+// Queue management APIs
+//
+#define NW_DEBUG_QUEUE 0x00000010
+
+//
+// Print Provider APIs
+//
+#define NW_DEBUG_PRINT 0x00000020
+
+//
+// Calls to redirector
+//
+#define NW_DEBUG_DEVICE 0x00000040
+
+//
+// Message APIs
+//
+#define NW_DEBUG_MESSAGE 0x00000080
+
+#if DBG
+
+extern DWORD WorkstationTrace;
+
+#define IF_DEBUG(DebugCode) if (WorkstationTrace & NW_DEBUG_ ## DebugCode)
+
+#define STATIC
+
+#else
+
+#define IF_DEBUG(DebugCode) if (FALSE)
+
+#define STATIC static
+
+#endif // DBG
+
+//
+// Initialization states
+//
+#define NW_EVENTS_CREATED 0x00000001
+#define NW_RDR_INITIALIZED 0x00000002
+#define NW_BOUND_TO_TRANSPORTS 0x00000004
+#define NW_RPC_SERVER_STARTED 0x00000008
+#define NW_INITIALIZED_MESSAGE 0x00000010
+#define NW_GATEWAY_LOGON 0x00000020
+
+//
+// Key path to redirector driver entry
+//
+#define SERVICE_REGISTRY_KEY L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\"
+
+
+//
+// Event that will be signaled when the service is stopping
+//
+extern HANDLE NwDoneEvent;
+
+//
+// Events for controlling popups, and the global popup data.
+//
+extern HANDLE NwPopupEvent;
+extern HANDLE NwPopupDoneEvent;
+
+typedef struct _NWWKS_POPUP_DATA {
+ DWORD MessageId ;
+ DWORD InsertCount ;
+ LPWSTR InsertStrings[10] ;
+} NWWKS_POPUP_DATA, *LPNWWKS_POPUP_DATA ;
+
+extern NWWKS_POPUP_DATA PopupData ;
+
+//
+// Flag to control DBCS translations
+//
+
+extern LONG Japan;
+
+//
+// Name of the network provider and print provider
+//
+extern WCHAR NwProviderName[];
+extern DWORD NwPacketBurstSize;
+extern DWORD NwPrintOption;
+extern DWORD NwGatewayPrintOption;
+extern BOOL GatewayLoggedOn ;
+extern BOOL GatewayConnectionAlways ;
+
+//
+// critical sections used
+//
+extern CRITICAL_SECTION NwLoggedOnCritSec;
+extern CRITICAL_SECTION NwServiceListCriticalSection;
+extern CRITICAL_SECTION NwPrintCritSec;
+//
+// Functions from device.c
+//
+DWORD
+NwInitializeRedirector(
+ VOID
+ );
+
+DWORD
+NwOpenRedirector(
+ VOID
+ );
+
+DWORD
+NwShutdownRedirector(
+ VOID
+ );
+
+DWORD
+NwLoadOrUnloadDriver(
+ BOOL Load
+ );
+
+DWORD
+NwBindToTransports(
+ VOID
+ );
+
+DWORD
+NwOpenPreferredServer(
+ PHANDLE ServerHandle
+ );
+
+VOID
+NwInitializePrintProvider(
+ VOID
+ );
+
+VOID
+NwTerminatePrintProvider(
+ VOID
+ );
+
+DWORD
+NwRedirFsControl(
+ IN HANDLE FileHandle,
+ IN ULONG RedirControlCode,
+ IN PNWR_REQUEST_PACKET Rrp,
+ IN ULONG RrpLength,
+ IN PVOID SecondBuffer OPTIONAL,
+ IN ULONG SecondBufferLength,
+ OUT PULONG Information OPTIONAL
+ );
+
+DWORD
+NwCreateTreeConnectName(
+ IN LPWSTR UncName,
+ IN LPWSTR LocalName OPTIONAL,
+ OUT PUNICODE_STRING TreeConnectStr
+ );
+
+DWORD
+NwOpenCreateConnection(
+ IN PUNICODE_STRING TreeConnectionName,
+ IN LPWSTR UserName OPTIONAL,
+ IN LPWSTR Password OPTIONAL,
+ IN LPWSTR UncName,
+ IN ACCESS_MASK DesiredAccess,
+ IN ULONG CreateDisposition,
+ IN ULONG CreateOptions,
+ IN ULONG ConnectionType,
+ OUT PHANDLE TreeConnectionHandle,
+ OUT PULONG Information OPTIONAL
+ );
+
+DWORD
+NwNukeConnection(
+ IN HANDLE TreeConnection,
+ IN DWORD UseForce
+ );
+
+DWORD
+NwGetServerResource(
+ IN LPWSTR LocalName,
+ IN DWORD LocalNameLength,
+ OUT LPWSTR RemoteName,
+ IN DWORD RemoteNameLen,
+ OUT LPDWORD CharsRequired
+ );
+
+DWORD
+NwEnumerateConnections(
+ IN OUT LPDWORD ResumeId,
+ IN DWORD EntriesRequested,
+ IN LPBYTE Buffer,
+ IN DWORD BufferSize,
+ OUT LPDWORD BytesNeeded,
+ OUT LPDWORD EntriesRead,
+ IN DWORD ConnectionType
+ );
+
+DWORD
+NwGetNextServerEntry(
+ IN HANDLE PreferredServer,
+ IN OUT LPDWORD LastObjectId,
+ OUT LPSTR ServerName
+ );
+
+DWORD
+NwGetNextServerConnection(
+ OUT LPNW_ENUM_CONTEXT ContextHandle
+ );
+
+DWORD
+NwGetNextNdsTreeEntry(
+ OUT LPNW_ENUM_CONTEXT ContextHandle
+ );
+
+DWORD
+NwGetNextVolumeEntry(
+ IN HANDLE ServerConnection,
+ IN DWORD LastObjectId,
+ OUT LPSTR VolumeName
+ );
+
+DWORD
+NwRdrLogonUser(
+ IN PLUID LogonId,
+ IN LPWSTR UserName,
+ IN DWORD UserNameSize,
+ IN LPWSTR Password OPTIONAL,
+ IN DWORD PasswordSize,
+ IN LPWSTR PreferredServer OPTIONAL,
+ IN DWORD PreferredServerSize
+ );
+
+VOID
+NwRdrChangePassword(
+ IN PNWR_REQUEST_PACKET Rrp
+ );
+
+DWORD
+NwRdrSetInfo(
+ IN DWORD PrintOption,
+ IN DWORD PacketBurstSize,
+ IN LPWSTR PreferredServer,
+ IN DWORD PreferredServerSize,
+ IN LPWSTR ProviderName,
+ IN DWORD ProviderNameSize
+ );
+
+DWORD
+NwRdrLogoffUser(
+ IN PLUID LogonId
+ );
+
+DWORD
+NwConnectToServer(
+ IN LPWSTR ServerName
+ );
+
+NTSTATUS
+NwOpenHandle(
+ IN PUNICODE_STRING ObjectName,
+ IN BOOL ValidateFlag,
+ OUT PHANDLE ObjectHandle
+ );
+
+NTSTATUS
+NwCallNtOpenFile(
+ OUT PHANDLE ObjectHandle,
+ IN ACCESS_MASK DesiredAccess,
+ IN PUNICODE_STRING ObjectName,
+ IN ULONG OpenOptions
+ );
+
+//
+// Functions from queue.c
+//
+DWORD
+NwGetNextQueueEntry(
+ IN HANDLE PreferredServer,
+ IN OUT LPDWORD LastObjectId,
+ OUT LPSTR QueueName
+ );
+
+DWORD
+NwAttachToNetwareServer(
+ IN LPWSTR ServerName,
+ OUT LPHANDLE phandleServer
+ );
+
+//
+// Functions from enum.c
+//
+DWORD
+NwOpenEnumPrintServers(
+ OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
+ );
+
+DWORD
+NwOpenEnumPrintQueues(
+ IN LPWSTR ServerName,
+ OUT LPNWWKSTA_CONTEXT_HANDLE EnumHandle
+ );
+
+DWORD
+NwWriteNetResourceEntry(
+ IN OUT LPBYTE * FixedPortion,
+ IN OUT LPWSTR * EndOfVariableData,
+ IN LPWSTR ContainerName OPTIONAL,
+ IN LPWSTR LocalName OPTIONAL,
+ IN LPWSTR RemoteName,
+ IN DWORD ScopeFlags,
+ IN DWORD DisplayFlags,
+ IN DWORD UsageFlags,
+ IN DWORD ResourceType,
+ IN LPWSTR SystemPath OPTIONAL,
+ OUT LPWSTR * lppSystem OPTIONAL,
+ OUT LPDWORD BytesNeeded
+ );
+
+DWORD
+NwWritePrinterInfoEntry(
+ IN OUT LPBYTE *FixedPortion,
+ IN OUT LPWSTR *EndOfVariableData,
+ IN LPWSTR ContainerName OPTIONAL,
+ IN LPWSTR RemoteName,
+ IN DWORD Flags,
+ OUT LPDWORD BytesNeeded
+ );
+
+//
+// Functions from credentl.c
+//
+VOID
+NwInitializeLogon(
+ VOID
+ );
+
+VOID
+NwGetLogonCredential(
+ VOID
+ );
+
+DWORD
+NwGatewayLogon(
+ VOID
+ );
+
+DWORD
+NwGatewayLogoff(
+ VOID
+ );
+
+//
+// Functions from util.c
+//
+DWORD
+NwMapStatus(
+ IN NTSTATUS NtStatus
+ );
+
+DWORD
+NwMapBinderyCompletionCode(
+ IN NTSTATUS NtStatus
+ );
+
+DWORD
+NwImpersonateClient(
+ VOID
+ );
+
+DWORD
+NwRevertToSelf(
+ VOID
+ );
+
+VOID
+NwLogEvent(
+ DWORD MessageId,
+ DWORD NumberOfSubStrings,
+ LPWSTR *SubStrings,
+ DWORD ErrorCode
+ );
+
+BOOL
+NwConvertToUnicode(
+ OUT LPWSTR *UnicodeOut,
+ IN LPSTR OemIn
+ );
+
+VOID
+DeleteAllConnections(
+ VOID
+ );
+DWORD
+
+//
+// Functions from connect.c
+//
+NwCreateSymbolicLink(
+ IN LPWSTR Local,
+ IN LPWSTR TreeConnectStr
+ );
+
+VOID
+NwDeleteSymbolicLink(
+ IN LPWSTR LocalDeviceName,
+ IN LPWSTR TreeConnectStr
+ );
+
+DWORD
+NwOpenHandleToDeleteConn(
+ IN LPWSTR UncName,
+ IN LPWSTR LocalName OPTIONAL,
+ IN DWORD UseForce,
+ IN BOOL IsStopWksta
+ );
+
+DWORD
+NwCreateConnection(
+ IN LPWSTR LocalName OPTIONAL,
+ IN LPWSTR RemoteName,
+ IN DWORD Type,
+ IN LPWSTR Password OPTIONAL,
+ IN LPWSTR UserName OPTIONAL
+ );
+
+//
+// Functions from gateway.c
+//
+DWORD
+NwEnumerateGWDevices(
+ LPDWORD Index,
+ LPBYTE Buffer,
+ DWORD BufferSize,
+ LPDWORD BytesNeeded,
+ LPDWORD EntriesRead
+ ) ;
+
+DWORD
+NwCreateGWDevice(
+ LPWSTR DeviceName,
+ LPWSTR RemoteName,
+ DWORD Flags
+ ) ;
+
+DWORD
+NwRemoveGWDevice(
+ LPWSTR DeviceName,
+ DWORD Flags
+ ) ;
+
+DWORD
+NwQueryGWAccount(
+ LPWSTR AccountName,
+ DWORD AccountNameLen,
+ LPDWORD AccountCharsNeeded,
+ LPWSTR Password,
+ DWORD PasswordLen,
+ LPDWORD PasswordCharsNeeded
+ ) ;
+
+DWORD
+NwSetGWAccount(
+ LPWSTR AccountName,
+ LPWSTR Password
+ ) ;
+
+DWORD
+NwGetGatewayResource(
+ IN LPWSTR LocalName,
+ OUT LPWSTR RemoteName,
+ IN DWORD RemoteNameLen,
+ OUT LPDWORD CharsRequired
+ );
+
+DWORD
+NwCreateRedirections(
+ LPWSTR Account,
+ LPWSTR Password
+ );
+
+DWORD
+NwDeleteRedirections(
+ VOID
+ );
+
+DWORD
+NwCreateGWConnection(
+ IN LPWSTR RemoteName,
+ IN LPWSTR UserName,
+ IN LPWSTR Password,
+ IN BOOL KeepConnection
+ );
+
+DWORD
+NwDeleteGWConnection(
+ IN LPWSTR RemoteName
+ );
+
+
+#endif // _NW_INCLUDED_
diff --git a/private/nw/svcdlls/nwwks/server/nwmain.c b/private/nw/svcdlls/nwwks/server/nwmain.c
new file mode 100644
index 000000000..de213bf7a
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/server/nwmain.c
@@ -0,0 +1,1570 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ nwmain.c
+
+Abstract:
+
+ Main module of the NetWare workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 11-Dec-1992
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+#include <nw.h>
+#include <nwreg.h>
+#include <wsipx.h>
+#include <wsnwlink.h>
+#include <nwmisc.h>
+
+
+//------------------------------------------------------------------
+//
+// Local Definitions
+//
+//------------------------------------------------------------------
+
+#define NW_EVENT_MESSAGE_FILE L"nwevent.dll"
+#define NW_MAX_POPUP_MESSAGE_LENGTH 512
+
+#define REG_WORKSTATION_PROVIDER_PATH L"System\\CurrentControlSet\\Services\\NWCWorkstation\\networkprovider"
+#define REG_PROVIDER_VALUE_NAME L"Name"
+
+#define REG_WORKSTATION_PARAMETERS_PATH L"System\\CurrentControlSet\\Services\\NWCWorkstation\\Parameters"
+#define REG_BURST_VALUE_NAME L"MaxBurstSize"
+#define REG_GATEWAY_VALUE_NAME L"GatewayPrintOption"
+#define REG_DISABLEPOPUP_VALUE_NAME L"DisablePopup"
+#define REG_GW_NOCONNECT_VALUE_NAME L"GWDontConnectAtStart"
+
+//
+// QFE release does not have this. so for QFE, we make it a no-op bit.
+//
+#ifdef QFE_BUILD
+#define MB_SERVICE_NOTIFICATION 0
+#endif
+
+//-------------------------------------------------------------------//
+// //
+// Local Function Prototypes //
+// //
+//-------------------------------------------------------------------//
+
+DWORD
+NwInitialize(
+ OUT LPDWORD NwInitState
+ );
+
+VOID
+NwInitializeCritSects(
+ VOID
+ );
+
+VOID
+NwInitializeWkstaInfo(
+ VOID
+ );
+
+DWORD
+NwInitializeMessage(
+ VOID
+ );
+
+BOOL NwShutdownNotify(
+ DWORD dwCtrlType
+ );
+
+VOID
+NwShutdown(
+ IN DWORD ErrorCode,
+ IN DWORD NwInitState
+ );
+
+VOID
+NwShutdownMessage(
+ VOID
+ );
+
+VOID
+NwControlHandler(
+ IN DWORD Opcode
+ );
+
+DWORD
+NwUpdateStatus(
+ VOID
+ );
+
+VOID
+NwMessageThread(
+ IN HANDLE RdrHandle
+ );
+
+VOID
+NwDisplayMessage(
+ IN LPWSTR Server,
+ IN LPWSTR Message
+ );
+
+VOID
+NwDisplayPopup(
+ IN LPNWWKS_POPUP_DATA lpPopupData
+ );
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+//
+// For service control
+//
+STATIC SERVICE_STATUS NwStatus;
+STATIC SERVICE_STATUS_HANDLE NwStatusHandle = (SERVICE_STATUS_HANDLE) NULL;
+HANDLE NwDoneEvent = NULL ;
+
+//
+// For popping up errors.
+//
+HANDLE NwPopupEvent = NULL ;
+HANDLE NwPopupDoneEvent = NULL ;
+NWWKS_POPUP_DATA PopupData ;
+
+//
+// For NTAS vs Winnt
+//
+BOOL fIsWinnt ;
+
+//
+// Flag to control DBCS translations
+//
+
+extern LONG Japan = 0;
+
+//
+// Data global to nwsvc.exe
+//
+PLMSVCS_GLOBAL_DATA NwsvcGlobalData;
+
+//
+// Handle for receiving server messages
+//
+STATIC HANDLE NwRdrMessageHandle;
+
+//
+// Stores the network and print provider name
+//
+WCHAR NwProviderName[MAX_PATH] = L"";
+
+// Stores the packet burst size
+DWORD NwPacketBurstSize = 32 * 1024;
+
+//
+// remember if gateway is logged on
+//
+BOOL GatewayLoggedOn = FALSE ;
+
+//
+// should gateway always take up a connection?
+//
+BOOL GatewayConnectionAlways = TRUE ;
+
+//
+// critical sections used
+//
+CRITICAL_SECTION NwLoggedOnCritSec;
+CRITICAL_SECTION NwPrintCritSec; // protect the linked list of printers
+
+
+//-------------------------------------------------------------------//
+
+
+VOID
+LMSVCS_ENTRY_POINT( // (NWWKS_main)
+ DWORD NumArgs,
+ LPTSTR *ArgsArray,
+ PLMSVCS_GLOBAL_DATA LmsvcsGlobalData,
+ HANDLE SvcRefHandle
+ )
+/*++
+
+Routine Description:
+
+ This is the main entry point of the NetWare workstation service. After
+ the service has been initialized, this thread will wait on NwDoneEvent
+ for a signal to terminate the service.
+
+Arguments:
+
+ NumArgs - Supplies the number of strings specified in ArgsArray.
+
+ ArgsArray - Supplies string arguments that are specified in the
+ StartService API call. This parameter is ignored.
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ DWORD NwInitState = 0;
+
+
+ UNREFERENCED_PARAMETER(NumArgs);
+ UNREFERENCED_PARAMETER(ArgsArray);
+ UNREFERENCED_PARAMETER(SvcRefHandle);
+
+ //
+ // Make nwsvc.exe data global to this DLL.
+ //
+ NwsvcGlobalData = LmsvcsGlobalData;
+
+ if (NwInitialize(&NwInitState) != NO_ERROR) {
+ return;
+ }
+
+ //
+ // Wait until we are told to stop.
+ //
+ (void) WaitForSingleObject(
+ NwDoneEvent,
+ INFINITE
+ );
+
+ NwShutdown(
+ NO_ERROR, // Normal termination
+ NwInitState
+ );
+}
+
+
+DWORD
+NwInitialize(
+ OUT LPDWORD NwInitState
+ )
+/*++
+
+Routine Description:
+
+ This function initializes the NetWare workstation service.
+
+Arguments:
+429
+ NwInitState - Returns a flag to indicate how far we got with initializing
+ the service before an error occurred.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+Notes:
+
+ See IMPORTANT NOTE below.
+
+--*/
+{
+ DWORD status;
+ NT_PRODUCT_TYPE ProductType ;
+ LCID lcid;
+
+ //
+ // are we a winnt machine?
+ //
+ fIsWinnt = RtlGetNtProductType(&ProductType) ?
+ (ProductType == NtProductWinNt) :
+ FALSE ;
+
+ //
+ // initialize all our critical sections as soon as we can
+ //
+ NwInitializeCritSects() ;
+
+ //
+ // Initialize all the status fields so that subsequent calls to
+ // SetServiceStatus need to only update fields that changed.
+ //
+ NwStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;
+ NwStatus.dwCurrentState = SERVICE_START_PENDING;
+ NwStatus.dwControlsAccepted = 0;
+ NwStatus.dwCheckPoint = 1;
+ NwStatus.dwWaitHint = 5000;
+ NwStatus.dwWin32ExitCode = NO_ERROR;
+ NwStatus.dwServiceSpecificExitCode = 0;
+
+ //
+ // Initialize workstation to receive service requests by registering the
+ // control handler.
+ //
+ if ((NwStatusHandle = RegisterServiceCtrlHandlerW(
+ NW_WORKSTATION_SERVICE,
+ NwControlHandler
+ )) == (SERVICE_STATUS_HANDLE) NULL) {
+
+ status = GetLastError();
+ KdPrint(("NWWORKSTATION: RegisterServiceCtrlHandlerW error %lu\n", status));
+ return status;
+ }
+
+ //
+ // Tell Service Controller that we are start pending.
+ //
+ (void) NwUpdateStatus();
+
+ //
+ // Create events to synchronize message popups
+ //
+ if (((NwPopupEvent = CreateEvent(
+ NULL, // no security descriptor
+ FALSE, // use automatic reset
+ FALSE, // initial state: not signalled
+ NULL // no name
+ )) == NULL)
+ || ((NwPopupDoneEvent = CreateEvent(
+ NULL, // no security descriptor
+ FALSE, // use automatic reset
+ TRUE, // initial state: signalled
+ NULL // no name
+ )) == NULL))
+ {
+
+ status = GetLastError();
+ NwShutdown(status, *NwInitState);
+ return status;
+ }
+
+ //
+ // Create event to synchronize termination
+ //
+ if ((NwDoneEvent = CreateEvent(
+ NULL, // no security descriptor
+ TRUE, // do not use automatic reset
+ FALSE, // initial state: not signalled
+ NULL // no name
+ )) == NULL) {
+
+ status = GetLastError();
+ NwShutdown(status, *NwInitState);
+ return status;
+ }
+ (*NwInitState) |= NW_EVENTS_CREATED;
+
+
+ //
+ // Load the redirector.
+ //
+ if ((status = NwInitializeRedirector()) != NO_ERROR) {
+ NwShutdown(status, *NwInitState);
+ return status;
+ }
+ (*NwInitState) |= NW_RDR_INITIALIZED;
+
+ //
+ // Service still start pending. Update checkpoint to reflect that
+ // we are making progress.
+ //
+ NwStatus.dwCheckPoint++;
+ (void) NwUpdateStatus();
+
+ //
+ // Bind to transports
+ //
+ if ((status = NwBindToTransports()) != NO_ERROR) {
+ NwShutdown(status, *NwInitState);
+ return status;
+ }
+ (*NwInitState) |= NW_BOUND_TO_TRANSPORTS;
+
+ //
+ // Service still start pending. Update checkpoint to reflect that
+ // we are making progress.
+ //
+ NwStatus.dwCheckPoint++;
+ (void) NwUpdateStatus();
+
+ //
+ // Initialize credential management.
+ //
+ NwInitializeLogon();
+
+ //
+ // Setup thread to receive server messages. Even if not successful,
+ // just press on as the workstation is mostly functional.
+ //
+ if ((status = NwInitializeMessage()) == NO_ERROR) {
+ (*NwInitState) |= NW_INITIALIZED_MESSAGE;
+ }
+
+ //
+ // Service still start pending. Update checkpoint to reflect that
+ // we are making progress.
+ //
+ NwStatus.dwCheckPoint++;
+ (void) NwUpdateStatus();
+
+ //
+ // Read some workstation information stored in the registry
+ // and passes some info to the redirector. This has to be
+ // done before opening up the RPC interface.
+ //
+ NwInitializeWkstaInfo();
+
+ //
+ // Initialize the server side print provider.
+ //
+ NwInitializePrintProvider();
+
+ //
+ // Initialize the service provider.
+ //
+ NwInitializeServiceProvider();
+
+ //
+ // Login the Gateway account. if not setup, this is a no-op.
+ // Failures are non fatal.
+ //
+ if ((status = NwGatewayLogon()) == NO_ERROR) {
+ (*NwInitState) |= NW_GATEWAY_LOGON;
+ }
+
+ //
+ // Service still start pending. Update checkpoint to reflect that
+ // we are making progress.
+ //
+ NwStatus.dwCheckPoint++;
+ (void) NwUpdateStatus();
+
+ //
+ // Open up the RPC interface
+ //
+ status = NwsvcGlobalData->StartRpcServer(
+ NwsvcGlobalData->SvcsRpcPipeName,
+ nwwks_ServerIfHandle
+ );
+
+ if (status != NO_ERROR) {
+ NwShutdown(status, *NwInitState);
+ return status;
+ }
+ (*NwInitState) |= NW_RPC_SERVER_STARTED;
+
+ //
+ // Set up the hook to handle computer shut down.
+ //
+ // IMPORTANT NOTE: this is the last step after everything else
+ // has suceeded. When shutdown handler is called, it assumes that
+ // the redir is fully initialized.
+ //
+ if ( !SetConsoleCtrlHandler( NwShutdownNotify, TRUE ))
+ {
+ KdPrint(("SetConsoleCtrlHandler failed with %d\n", GetLastError()));
+ NwShutdown( status, *NwInitState );
+ return GetLastError();
+ }
+
+ //
+ // We are done with workstation startup.
+ //
+ NwStatus.dwCurrentState = SERVICE_RUNNING;
+ NwStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
+ NwStatus.dwCheckPoint = 0;
+ NwStatus.dwWaitHint = 0;
+ NwStatus.dwWin32ExitCode = NO_ERROR;
+
+ if ((status = NwUpdateStatus()) != NO_ERROR) {
+ NwShutdown(status, *NwInitState);
+ return status;
+ }
+
+ //
+ // Read user and service logon credentias from the registry, in
+ // case user logged on before workstation was started.
+ // Eg. restart workstation.
+ //
+ NwGetLogonCredential();
+
+
+#if 0
+ //
+ // check that the NWLINK has the right sockopts
+ //
+ // see comment on the actual function
+ //
+ if (!NwIsNWLinkVersionOK())
+ {
+ //
+ // log the error in the event log
+ //
+
+ LPWSTR InsertStrings[1] ;
+
+ NwLogEvent(EVENT_NWWKSTA_WRONG_NWLINK_VERSION,
+ 0,
+ InsertStrings,
+ 0) ;
+ }
+#endif
+
+ //
+ // Check to see if we're in a DBCS environment.
+ //
+ NtQueryDefaultLocale( TRUE, &lcid );
+ Japan = 0;
+ if (PRIMARYLANGID(lcid) == LANG_JAPANESE ||
+ PRIMARYLANGID(lcid) == LANG_KOREAN ||
+ PRIMARYLANGID(lcid) == LANG_CHINESE) {
+
+ Japan = 1;
+ }
+
+ //
+ // Successful initialization
+ //
+ return NO_ERROR;
+}
+
+
+BOOL NwShutdownNotify(
+ IN DWORD dwCtrlType
+ )
+/*++
+
+Routine Description:
+
+ This function is a control handler used in SetConsoleCtrlHandler.
+ We are only interested in CTRL_SHUTDOWN_EVENT. On shutdown, we
+ need to notify redirector to shut down and then delete the
+ CurrentUser key in the registry.
+
+Arguments:
+
+ dwCtrlType - The control type that occurred. We will only
+ process CTRL_SHUTDOWN_EVENT.
+
+Return Value:
+
+ TRUE if we don't want the default or other handlers to be called.
+ FALSE otherwise.
+
+Note:
+
+ This Handler is registered after all the Init steps have completed.
+ As such, it does not check for what state the service is in as it
+ cleans up.
+
+--*/
+{
+ DWORD err;
+
+#if DBG
+ IF_DEBUG(INIT)
+ KdPrint(("NwShutdownNotify\n"));
+#endif
+
+ if ( dwCtrlType != CTRL_SHUTDOWN_EVENT )
+ return TRUE;
+
+ //
+ // stop the RPC server
+ //
+ (void) NwsvcGlobalData->StopRpcServer(nwwks_ServerIfHandle);
+
+ //
+ // get rid of all connections
+ //
+ (void) DeleteAllConnections();
+
+ NwGatewayLogoff() ;
+
+ err = NwShutdownRedirector();
+
+ if ( err != NO_ERROR )
+ KdPrint(("Shut down redirector failed with %d\n", err ));
+#if DBG
+ else
+ {
+ IF_DEBUG(INIT)
+ KdPrint(("NwShutdownRedirector success!\n"));
+ }
+#endif
+
+ //
+ // Delete all logon session information in the registry.
+ //
+ NwDeleteCurrentUser();
+ (void) NwDeleteServiceLogon(NULL);
+
+ return FALSE; // The default handler will terminate the process.
+}
+
+
+VOID
+NwShutdown(
+ IN DWORD ErrorCode,
+ IN DWORD NwInitState
+ )
+/*++
+
+Routine Description:
+
+ This function shuts down the Workstation service.
+
+Arguments:
+
+ ErrorCode - Supplies the error code of the failure
+
+ NwInitState - Supplies a flag to indicate how far we got with initializing
+ the service before an error occurred, thus the amount of clean up
+ needed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ DWORD status = NO_ERROR;
+
+ //
+ // Service stop still pending. Update checkpoint counter and the
+ // status with the Service Controller.
+ //
+ (NwStatus.dwCheckPoint)++;
+ (void) NwUpdateStatus();
+
+ if (NwInitState & NW_RPC_SERVER_STARTED) {
+ NwsvcGlobalData->StopRpcServer(nwwks_ServerIfHandle);
+ }
+
+ if (NwInitState & NW_INITIALIZED_MESSAGE) {
+ NwShutdownMessage();
+ }
+
+ if (NwInitState & NW_GATEWAY_LOGON)
+ {
+ (void) NwDeleteRedirections() ;
+ }
+
+ //
+ // Service stop still pending. Update checkpoint counter and the
+ // status with the Service Controller.
+ //
+ (NwStatus.dwCheckPoint)++;
+ (void) NwUpdateStatus();
+
+ if (NwInitState & NW_BOUND_TO_TRANSPORTS) {
+ DeleteAllConnections();
+ NwGatewayLogoff() ;
+ }
+
+ //
+ // Clean up the service provider.
+ //
+ // NwTerminateServiceProvider(); NOT CALLED! This is done at DLL unload time already.
+
+ //
+ // Clean up the server side print provider
+ //
+ NwTerminatePrintProvider();
+
+ //
+ // Service stop still pending. Update checkpoint counter and the
+ // status with the Service Controller.
+ //
+ (NwStatus.dwCheckPoint)++;
+ (void) NwUpdateStatus();
+
+ if (NwInitState & NW_RDR_INITIALIZED) {
+ //
+ // Unload the redirector
+ //
+ status = NwShutdownRedirector();
+ }
+
+ if (NwInitState & NW_EVENTS_CREATED) {
+ //
+ // Close handle to termination event and popup event
+ //
+ if (NwDoneEvent) CloseHandle(NwDoneEvent);
+ if (NwPopupEvent) CloseHandle(NwPopupEvent);
+ if (NwPopupDoneEvent) CloseHandle(NwPopupDoneEvent);
+ }
+
+ //
+ // We are done with cleaning up. Tell Service Controller that we are
+ // stopped.
+ //
+ NwStatus.dwCurrentState = SERVICE_STOPPED;
+ NwStatus.dwControlsAccepted = 0;
+
+ if ((ErrorCode == NO_ERROR) &&
+ (status == ERROR_REDIRECTOR_HAS_OPEN_HANDLES)) {
+ ErrorCode = status;
+ }
+
+ //
+ // Deregister the control handler
+ //
+ (void) SetConsoleCtrlHandler( NwShutdownNotify, FALSE ) ;
+
+ //
+ // BUGBUG: If error is within Win32 error range, assign it to
+ // dwWin32ExitCode field, otherwise assign it to the
+ // dwServiceSpecificExitCode field;
+ //
+ NwStatus.dwWin32ExitCode = ErrorCode;
+ NwStatus.dwServiceSpecificExitCode = 0;
+
+ NwStatus.dwCheckPoint = 0;
+ NwStatus.dwWaitHint = 0;
+
+ (void) NwUpdateStatus();
+}
+
+
+VOID
+NwControlHandler(
+ IN DWORD Opcode
+ )
+/*++
+
+Routine Description:
+
+ This is the service control handler of the Workstation service.
+
+Arguments:
+
+ Opcode - Supplies a value which specifies the action for the
+ service to perform.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ switch (Opcode) {
+
+ case SERVICE_CONTROL_STOP:
+
+ if (NwStatus.dwCurrentState != SERVICE_STOP_PENDING) {
+
+ NwStatus.dwCurrentState = SERVICE_STOP_PENDING;
+ NwStatus.dwCheckPoint = 1;
+ NwStatus.dwWaitHint = 60000;
+
+ //
+ // Send the status response.
+ //
+ (void) NwUpdateStatus();
+
+ if (! SetEvent(NwDoneEvent)) {
+
+ //
+ // Problem with setting event to terminate Workstation
+ // service.
+ //
+ KdPrint(("NWWORKSTATION: Error setting NwDoneEvent %lu\n",
+ GetLastError()));
+
+ ASSERT(FALSE);
+ }
+ return;
+ }
+ break;
+
+ case SERVICE_CONTROL_INTERROGATE:
+ break;
+
+ }
+
+ //
+ // Send the status response.
+ //
+ (void) NwUpdateStatus();
+}
+
+
+DWORD
+NwUpdateStatus(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function updates the workstation service status with the Service
+ Controller.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Return code from SetServiceStatus.
+
+--*/
+{
+ DWORD status = NO_ERROR;
+
+
+ if (NwStatusHandle == (SERVICE_STATUS_HANDLE) NULL) {
+ KdPrint(("NWWORKSTATION: Cannot call SetServiceStatus, no status handle.\n"));
+ return ERROR_INVALID_HANDLE;
+ }
+
+ if (! SetServiceStatus(NwStatusHandle, &NwStatus)) {
+
+ status = GetLastError();
+
+ KdPrint(("NWWORKSTATION: SetServiceStatus error %lu\n", status));
+ }
+
+ return status;
+}
+
+
+
+VOID
+NwInitializeWkstaInfo(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function reads some workstation info, including the packet burst
+ size and the provider name. We will ignore all errors that occurred when
+ reading from the registry and use the default values instead.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ DWORD err;
+ HKEY hkey;
+ DWORD dwTemp;
+ DWORD dwSize = sizeof( dwTemp );
+ LPWSTR pszProviderName = NULL;
+
+ //
+ // Read the Network and Print Provider Name.
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\networkprovider
+ //
+ err = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ REG_WORKSTATION_PROVIDER_PATH,
+ REG_OPTION_NON_VOLATILE, // options
+ KEY_READ, // desired access
+ &hkey
+ );
+
+ if ( !err )
+ {
+ //
+ // Read the network provider name
+ //
+ err = NwReadRegValue(
+ hkey,
+ REG_PROVIDER_VALUE_NAME,
+ &pszProviderName
+ );
+
+ if ( !err )
+ {
+ wcscpy( NwProviderName, pszProviderName );
+ (void) LocalFree( (HLOCAL) pszProviderName );
+
+#if DBG
+ IF_DEBUG(INIT)
+ {
+ KdPrint(("\nNWWORKSTATION: Provider Name = %ws\n",
+ NwProviderName ));
+ }
+#endif
+ }
+
+ RegCloseKey( hkey );
+ }
+
+ if ( err )
+ {
+ KdPrint(("Error %d when reading provider name.\n", err ));
+ }
+
+
+ //
+ // Read the Packet Burst Size
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters
+ //
+ err = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ REG_WORKSTATION_PARAMETERS_PATH,
+ REG_OPTION_NON_VOLATILE, // options
+ KEY_READ, // desired access
+ &hkey
+ );
+
+ if ( !err )
+ {
+ err = RegQueryValueExW( hkey,
+ REG_BURST_VALUE_NAME,
+ NULL,
+ NULL,
+ (LPBYTE) &dwTemp,
+ &dwSize );
+
+ if ( !err )
+ {
+ NwPacketBurstSize = dwTemp;
+
+#if DBG
+ IF_DEBUG(INIT)
+ {
+ KdPrint(("\nNWWORKSTATION: Packet Burst Size = %d\n",
+ NwPacketBurstSize ));
+ }
+#endif
+ }
+
+ err = RegQueryValueExW( hkey,
+ REG_GATEWAY_VALUE_NAME,
+ NULL,
+ NULL,
+ (LPBYTE) &dwTemp,
+ &dwSize );
+
+ if ( !err )
+ {
+ NwGatewayPrintOption = dwTemp;
+
+#if DBG
+ IF_DEBUG(INIT)
+ {
+ KdPrint(("\nNWWORKSTATION: Gateway Print Option = %d\n",
+ NwGatewayPrintOption ));
+ }
+#endif
+ }
+
+ err = RegQueryValueExW( hkey,
+ REG_GW_NOCONNECT_VALUE_NAME,
+ NULL,
+ NULL,
+ (LPBYTE) &dwTemp,
+ &dwSize );
+
+ if ( !err )
+ {
+ if (dwTemp == 1)
+ GatewayConnectionAlways = FALSE ;
+ }
+
+ RegCloseKey( hkey );
+ }
+
+ //
+ // Passes the information to the redirector
+ //
+ (void) NwRdrSetInfo(
+ NW_PRINT_OPTION_DEFAULT,
+ NwPacketBurstSize,
+ NULL,
+ 0,
+ NwProviderName,
+ ((NwProviderName != NULL) ?
+ wcslen( NwProviderName) * sizeof( WCHAR ) : 0 )
+ );
+
+}
+
+
+
+DWORD
+NwInitializeMessage(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine opens a handle to the redirector device to receive
+ server messages and creates a thread to wait for the incoming
+ messages.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+{
+ DWORD status;
+ UNICODE_STRING RdrName;
+
+ HKEY hkey;
+ DWORD dwTemp;
+ DWORD dwSize = sizeof( dwTemp );
+ BOOL fDisablePopup = FALSE ;
+
+ HANDLE ThreadHandle;
+ DWORD ThreadId;
+
+ //
+ // Read the Disable Popup Flag. By default it is cleared.
+ // We only set to TRUE if we find the value.
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters
+ //
+ status = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ REG_WORKSTATION_PARAMETERS_PATH,
+ REG_OPTION_NON_VOLATILE, // options
+ KEY_READ, // desired access
+ &hkey
+ );
+
+ if ( status == NO_ERROR )
+ {
+ status = RegQueryValueExW( hkey,
+ REG_DISABLEPOPUP_VALUE_NAME,
+ NULL,
+ NULL,
+ (LPBYTE) &dwTemp,
+ &dwSize );
+
+ if ( status == NO_ERROR )
+ {
+ fDisablePopup = (dwTemp == 1);
+ }
+
+ RegCloseKey( hkey );
+ }
+
+ if (fDisablePopup)
+ {
+ return NO_ERROR ;
+ }
+
+ RtlInitUnicodeString(&RdrName, DD_NWFS_DEVICE_NAME_U);
+
+ status = NwMapStatus(
+ NwCallNtOpenFile(
+ &NwRdrMessageHandle,
+ FILE_GENERIC_READ | SYNCHRONIZE,
+ &RdrName,
+ 0 // Handle for async call
+ )
+ );
+
+ if (status != NO_ERROR) {
+ return status;
+ }
+
+ //
+ // Create the thread to wait for incoming messages
+ //
+ ThreadHandle = CreateThread(
+ NULL,
+ 0,
+ (LPTHREAD_START_ROUTINE) NwMessageThread,
+ (LPVOID) NwRdrMessageHandle,
+ 0,
+ &ThreadId
+ );
+
+ if (ThreadHandle == NULL) {
+ (void) NtClose(NwRdrMessageHandle);
+ return GetLastError();
+ }
+
+ return NO_ERROR;
+}
+
+
+VOID
+NwShutdownMessage(
+ VOID
+ )
+{
+ (void) NtClose(NwRdrMessageHandle);
+}
+
+
+VOID
+NwMessageThread(
+ IN HANDLE RdrHandle
+ )
+{
+ NTSTATUS getmsg_ntstatus;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ DWORD ReturnVal, NumEventsToWaitOn ;
+ HANDLE EventsToWaitOn[3];
+
+ BYTE OutputBuffer[48 * sizeof(WCHAR) + 256 * sizeof(WCHAR)];
+ PNWR_SERVER_MESSAGE ServerMessage = (PNWR_SERVER_MESSAGE) OutputBuffer;
+ BOOL DoFsctl = TRUE ;
+ NWWKS_POPUP_DATA LocalPopupData ;
+
+
+ EventsToWaitOn[0] = NwDoneEvent;
+ EventsToWaitOn[1] = NwPopupEvent;
+ EventsToWaitOn[2] = RdrHandle;
+
+ while (TRUE) {
+ if (DoFsctl)
+ {
+ getmsg_ntstatus = NtFsControlFile(
+ RdrHandle,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_GET_MESSAGE,
+ NULL,
+ 0,
+ OutputBuffer,
+ sizeof(OutputBuffer)
+ );
+
+ DoFsctl = FALSE ;
+ }
+
+ if (NT_SUCCESS(getmsg_ntstatus))
+ {
+ NumEventsToWaitOn = 3 ;
+ }
+ else
+ {
+ NumEventsToWaitOn = 2 ;
+ }
+
+ ReturnVal = WaitForMultipleObjects(
+ NumEventsToWaitOn,
+ EventsToWaitOn,
+ FALSE, // Wait for any one
+ INFINITE
+ );
+
+ switch (ReturnVal) {
+
+ case WAIT_OBJECT_0 :
+ //
+ // Workstation is terminating. Just die.
+ //
+ ExitThread(0);
+ break;
+
+ case WAIT_OBJECT_0 + 1:
+ //
+ // We have a popup to do. Grab the data and Set the
+ // event so that the structure can be used once more.
+ //
+ LocalPopupData = PopupData ;
+ RtlZeroMemory(&PopupData, sizeof(PopupData)) ;
+ if (! SetEvent(NwPopupDoneEvent)) {
+ //
+ // should not happen
+ //
+ KdPrint(("NWWORKSTATION: Error setting NwPopupDoneEvent %lu\n",
+ GetLastError()));
+
+ ASSERT(FALSE);
+ }
+
+ NwDisplayPopup(&LocalPopupData) ;
+ break;
+
+ case WAIT_OBJECT_0 + 2:
+ {
+ NTSTATUS ntstatus ;
+
+ //
+ // GET_MESSAGE fsctl completed.
+ //
+ ntstatus = IoStatusBlock.Status;
+ DoFsctl = TRUE ;
+
+ if (ntstatus == STATUS_SUCCESS) {
+ NwDisplayMessage(
+ ServerMessage->Server,
+ (LPWSTR) ((DWORD) ServerMessage +
+ ServerMessage->MessageOffset)
+ );
+ }
+ else {
+ KdPrint(("NWWORKSTATION: GET_MESSAGE fsctl failed %08lx\n", ntstatus));
+ }
+
+ break;
+ }
+
+ case WAIT_FAILED:
+ default:
+ //
+ // Don't care.
+ //
+ break;
+ }
+
+ }
+}
+
+
+VOID
+NwDisplayMessage(
+ IN LPWSTR Server,
+ IN LPWSTR Message
+ )
+/*++
+
+Routine Description:
+
+ This routine puts up a popup message with the text received from
+ a server.
+
+Arguments:
+
+ Server - Supplies the name of the server which the message was
+ received from.
+
+ Message - Supplies the message to put up received from the server.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ HMODULE MessageDll;
+
+ WCHAR Title[128];
+ WCHAR Buffer[NW_MAX_POPUP_MESSAGE_LENGTH];
+
+ DWORD MessageLength;
+ DWORD CharsToCopy;
+
+#if DBG
+ IF_DEBUG(MESSAGE)
+ {
+ KdPrint(("Server: (%ws), Message: (%ws)\n", Server, Message));
+ }
+#endif
+
+ //
+ // Load the netware message file DLL
+ //
+ MessageDll = LoadLibraryW(NW_EVENT_MESSAGE_FILE);
+
+ if (MessageDll == NULL) {
+ return;
+ }
+
+ RtlZeroMemory(Buffer, sizeof(Buffer)) ;
+ MessageLength = FormatMessageW(
+ FORMAT_MESSAGE_FROM_HMODULE,
+ (LPVOID) MessageDll,
+ fIsWinnt? NW_MESSAGE_TITLE : NW_MESSAGE_TITLE_NTAS,
+ 0,
+ Title,
+ sizeof(Title) / sizeof(WCHAR),
+ NULL
+ );
+
+ if (MessageLength == 0) {
+ KdPrint(("NWWORKSTATION: FormatMessageW of title failed\n"));
+ return;
+ }
+
+
+ //
+ // Get string from message file to display where the message come
+ // from.
+ //
+ MessageLength = FormatMessageW(
+ FORMAT_MESSAGE_FROM_HMODULE |
+ FORMAT_MESSAGE_ARGUMENT_ARRAY,
+ (LPVOID) MessageDll,
+ NW_MESSAGE_FROM_SERVER,
+ 0,
+ Buffer,
+ sizeof(Buffer) / sizeof(WCHAR),
+ (va_list *) &Server
+ );
+
+
+ if (MessageLength != 0) {
+
+ CharsToCopy = wcslen(Message);
+
+ if (MessageLength + 1 + CharsToCopy > NW_MAX_POPUP_MESSAGE_LENGTH) {
+
+ //
+ // Message is too big. Truncate the message.
+ //
+ CharsToCopy = NW_MAX_POPUP_MESSAGE_LENGTH - (MessageLength + 1);
+
+ }
+
+ wcsncpy(&Buffer[MessageLength], Message, CharsToCopy);
+
+ (void) MessageBeep(MB_ICONEXCLAMATION);
+
+ (void) MessageBoxW(
+ NULL,
+ Buffer,
+ Title,
+ MB_OK | MB_SETFOREGROUND |
+ MB_SYSTEMMODAL | MB_SERVICE_NOTIFICATION
+ );
+ }
+ else {
+ KdPrint(("NWWORKSTATION: FormatMessageW failed %lu\n", GetLastError()));
+ }
+
+ (void) FreeLibrary(MessageDll);
+}
+
+VOID
+NwDisplayPopup(
+ IN LPNWWKS_POPUP_DATA lpPopupData
+ )
+/*++
+
+Routine Description:
+
+ This routine puts up a popup message for the given Id.
+
+Arguments:
+
+ MessageId - Supplies the message to put up.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ HMODULE MessageDll;
+
+ WCHAR Title[128];
+ WCHAR Buffer[NW_MAX_POPUP_MESSAGE_LENGTH];
+
+ DWORD MessageLength;
+ DWORD i ;
+
+ //
+ // Load the netware message file DLL
+ //
+ MessageDll = LoadLibraryW(NW_EVENT_MESSAGE_FILE);
+
+ if (MessageDll == NULL) {
+ return;
+ }
+
+ MessageLength = FormatMessageW(
+ FORMAT_MESSAGE_FROM_HMODULE,
+ (LPVOID) MessageDll,
+ NW_MESSAGE_TITLE,
+ 0,
+ Title,
+ sizeof(Title) / sizeof(WCHAR),
+ NULL
+ );
+
+ if (MessageLength == 0) {
+ KdPrint(("NWWORKSTATION: FormatMessageW of title failed\n"));
+ return;
+ }
+
+
+ //
+ // Get string from message file to display where the message come
+ // from.
+ //
+ MessageLength = FormatMessageW(
+ FORMAT_MESSAGE_FROM_HMODULE |
+ FORMAT_MESSAGE_ARGUMENT_ARRAY,
+ (LPVOID) MessageDll,
+ lpPopupData->MessageId,
+ 0,
+ Buffer,
+ sizeof(Buffer) / sizeof(WCHAR),
+ (va_list *) &(lpPopupData->InsertStrings)
+ );
+
+ for (i = 0; i < lpPopupData->InsertCount; i++)
+ (void) LocalFree((HLOCAL)lpPopupData->InsertStrings[i]) ;
+
+
+ if (MessageLength != 0) {
+
+ (void) MessageBeep(MB_ICONEXCLAMATION);
+
+ (void) MessageBoxW(
+ NULL,
+ Buffer,
+ Title,
+ MB_OK | MB_SETFOREGROUND |
+ MB_SYSTEMMODAL | MB_SERVICE_NOTIFICATION
+ );
+ }
+ else {
+ KdPrint(("NWWORKSTATION: FormatMessageW failed %lu\n", GetLastError()));
+ }
+
+ (void) FreeLibrary(MessageDll);
+}
+
+#if 0
+
+//
+// This code was needed when we used to have a version of NwLink from MCS
+// that didnt do the sockopts we needed. It used to be called by NwInitialize()
+// and if the check failed, we logged an event
+//
+
+BOOL
+NwIsNWLinkVersionOK(
+ void
+ )
+/*++
+
+Routine Description:
+
+ This routine puts checks if the NWLINK version supports the
+ sockopts added for IPX/SPX. if not, barf.
+
+ Arguments:
+
+ None.
+
+ Return Value:
+
+ TRUE is the version is OK, FALSE otherwise.
+
+--*/
+{
+ int err ;
+ SOCKET s ;
+ WORD VersionRequested ;
+ WSADATA wsaData ;
+ IPX_NETNUM_DATA buf;
+ int buflen = sizeof(buf);
+
+ BOOL NeedCleanup = FALSE ;
+ BOOL NeedClose = FALSE ;
+ BOOL result = TRUE ;
+
+ VersionRequested = MAKEWORD(1,1) ;
+
+ if (err = WSAStartup(VersionRequested,
+ &wsaData))
+ {
+ //
+ // cant even get winsock initialized. this is not a question
+ // of wrong version. we will fail later. return TRUE
+ //
+ result = TRUE ;
+ goto ErrorExit ;
+ }
+ NeedCleanup = TRUE ;
+
+ s = socket(AF_IPX,
+ SOCK_DGRAM,
+ NSPROTO_IPX
+ );
+
+ if (s == INVALID_SOCKET)
+ {
+ //
+ // cant even open socket. this is not a question
+ // of wrong version. we will fail later. return TRUE
+ //
+ result = TRUE ;
+ goto ErrorExit ;
+ }
+ NeedClose = TRUE ;
+
+ if (err = getsockopt(s,
+ NSPROTO_IPX,
+ IPX_GETNETINFO,
+ (char FAR*)&buf,
+ &buflen
+ ))
+ {
+ err = WSAGetLastError() ;
+ if (err == WSAENOPROTOOPT)
+ {
+ //
+ // we got a no supported call. we know this is OLD
+ // return FALSE
+ //
+ result = FALSE ;
+ goto ErrorExit ;
+ }
+ }
+
+ //
+ // everything dandy. return TRUE
+ //
+ result = TRUE ;
+
+ErrorExit:
+
+ if (NeedClose)
+ closesocket(s) ;
+ if (NeedCleanup)
+ WSACleanup() ;
+
+ return result ;
+}
+
+#endif
+
+
+VOID
+NwInitializeCritSects(
+ VOID
+ )
+{
+ //
+ // Initialize the critical section to serialize access to
+ // NwLogonNotifiedRdr flag. This is also used to serialize
+ // access to GetewayLoggedOnFlag
+ //
+ InitializeCriticalSection(&NwLoggedOnCritSec);
+
+ //
+ // Initialize the critical section used by the print provider
+ //
+ InitializeCriticalSection( &NwPrintCritSec );
+
+}
diff --git a/private/nw/svcdlls/nwwks/server/nwwks.def b/private/nw/svcdlls/nwwks/server/nwwks.def
new file mode 100644
index 000000000..ffacf0b33
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/server/nwwks.def
@@ -0,0 +1,6 @@
+NAME nwwks.dll
+
+DESCRIPTION 'Client Service for NetWare Workstation Service'
+
+EXPORTS
+ ServiceEntry
diff --git a/private/nw/svcdlls/nwwks/server/nwwks.rc b/private/nw/svcdlls/nwwks/server/nwwks.rc
new file mode 100644
index 000000000..4cdad5e39
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/server/nwwks.rc
@@ -0,0 +1,9 @@
+#include <windows.h>
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Client Service for Netware"
+#define VER_INTERNALNAME_STR "nwwks.dll"
+
+#include "common.ver"
diff --git a/private/nw/svcdlls/nwwks/server/queue.c b/private/nw/svcdlls/nwwks/server/queue.c
new file mode 100644
index 000000000..2dfae997b
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/server/queue.c
@@ -0,0 +1,1928 @@
+/*++
+
+Copyright (c) 1991-1993 Microsoft Corporation
+
+Module Name:
+
+ queue.c
+
+Abstract:
+
+ This module contains the support routines for the queue APIs that call
+ into the NetWare redirector
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 24-Apr-1993
+
+Revision History:
+
+--*/
+
+#include <nw.h>
+#include <nwxchg.h>
+#include <nwapi.h>
+#include <nwreg.h>
+#include <queue.h>
+#include <splutil.h>
+//-------------------------------------------------------------------//
+// //
+// Local Function Prototypes //
+// //
+//-------------------------------------------------------------------//
+
+DWORD
+NwWriteJobInfoEntry(
+ IN OUT LPBYTE *FixedPortion,
+ IN OUT LPWSTR *EndOfVariableData,
+ IN DWORD Level,
+ IN WORD JobId,
+ IN LPWSTR PrinterName,
+ IN LPWSTR JobDescription,
+ IN LPWSTR UserName,
+ IN BYTE JobControlFlags,
+ IN BYTE JobPosition,
+ IN LPBYTE JobEntryTime,
+ IN JOBTIME TargetExecutionTime,
+ IN DWORD FileSize
+ );
+
+DWORD
+ConvertToSystemTime(
+ IN JOBTIME JobTime,
+ OUT LPSYSTEMTIME pSystemTime
+ );
+
+//-------------------------------------------------------------------//
+// //
+// Global variables //
+// //
+//-------------------------------------------------------------------//
+
+#define NW_RDR_SERVER_PREFIX L"\\Device\\Nwrdr\\"
+
+#define QF_USER_HOLD 0x40
+#define QF_OPERATOR_HOLD 0x80
+
+//
+// Stores the current user's print control options
+//
+DWORD NwPrintOption = NW_PRINT_OPTION_DEFAULT;
+ // Default Print Control Flags: Suppress form
+ // feed, banner on, notify on
+DWORD NwGatewayPrintOption = NW_GATEWAY_PRINT_OPTION_DEFAULT;
+ // Gateway default print control flags:
+ // Suppress form feed, banner on, notify off
+
+
+
+DWORD
+NwAttachToNetwareServer(
+ IN LPWSTR ServerName,
+ OUT LPHANDLE phandleServer
+ )
+/*++
+
+Routine Description:
+
+ This routine opens a handle to the given server.
+
+Arguments:
+
+ ServerName - The server name to attach to.
+ phandleServer - Receives an opened handle to the preferred or
+ nearest server.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+{
+ NTSTATUS ntstatus;
+ IO_STATUS_BLOCK IoStatusBlock;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+
+ LPWSTR FullName;
+ UNICODE_STRING UServerName;
+
+ FullName = (LPWSTR) LocalAlloc( LMEM_ZEROINIT,
+ (UINT) ( wcslen( NW_RDR_SERVER_PREFIX) +
+ wcslen( ServerName ) - 1) *
+ sizeof(WCHAR)
+ );
+
+ if ( FullName == NULL ) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ wcscpy( FullName, NW_RDR_SERVER_PREFIX );
+ wcscat( FullName, ServerName + 2 ); // Skip past the prefix "\\"
+
+ RtlInitUnicodeString( &UServerName, FullName );
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &UServerName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ //
+ // Open a handle to the preferred server.
+ //
+ ntstatus = NtOpenFile(
+ phandleServer,
+ SYNCHRONIZE | GENERIC_WRITE,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_VALID_FLAGS,
+ FILE_SYNCHRONOUS_IO_NONALERT
+ );
+
+ if ( NT_SUCCESS(ntstatus)) {
+ ntstatus = IoStatusBlock.Status;
+ }
+
+ if (! NT_SUCCESS(ntstatus)) {
+ *phandleServer = NULL;
+ }
+
+ LocalFree( FullName );
+ return RtlNtStatusToDosError(ntstatus);
+}
+
+
+
+DWORD
+NwGetNextQueueEntry(
+ IN HANDLE PreferredServer,
+ IN OUT LPDWORD LastObjectId,
+ OUT LPSTR QueueName
+ )
+/*++
+
+Routine Description:
+
+ This function uses an opened handle to the preferred server to
+ scan it bindery for all print queue objects.
+
+Arguments:
+
+ PreferredServer - Supplies the handle to the preferred server on
+ which to scan the bindery.
+
+ LastObjectId - On input, supplies the object ID to the last print
+ queue object returned, which is the resume handle to get the
+ next print queue object. On output, receives the object ID
+ of the print queue object returned.
+
+ QueueName - Receives the name of the returned print queue object.
+
+Return Value:
+
+ NO_ERROR - Successfully gotten a print name.
+
+ WN_NO_MORE_ENTRIES - No other print queue object past the one
+ specified by LastObjectId.
+
+--*/
+{
+ NTSTATUS ntstatus;
+ WORD ObjectType;
+
+#if DBG
+ IF_DEBUG(ENUM) {
+ KdPrint(("NWWORKSTATION: NwGetNextQueueEntry LastObjectId %lu\n",
+ *LastObjectId));
+ }
+#endif
+
+ ntstatus = NwlibMakeNcp(
+ PreferredServer,
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 58, // Max request packet size
+ 59, // Max response packet size
+ "bdwp|dwc", // Format string
+ 0x37, // Scan bindery object
+ *LastObjectId, // Previous ID
+ 0x3, // Print Queue object
+ "*", // Wildcard to match all
+ LastObjectId, // Current ID
+ &ObjectType, // Ignore
+ QueueName // Currently returned print queue
+ );
+
+ //
+ // Unmap Japanese special chars
+ //
+ UnmapSpecialJapaneseChars(QueueName,(WORD)lstrlenA(QueueName));
+
+#if DBG
+ if ( NT_SUCCESS(ntstatus)) {
+ IF_DEBUG(ENUM) {
+ KdPrint(("NWWORKSTATION: NwGetNextQueueEntry NewObjectId %08lx, QueueName %s\n", *LastObjectId, QueueName));
+ }
+ }
+#endif
+
+ return NwMapBinderyCompletionCode(ntstatus);
+}
+
+
+
+DWORD
+NwGetQueueId(
+ IN HANDLE handleServer,
+ IN LPWSTR QueueName,
+ OUT LPDWORD QueueId
+ )
+/*++
+
+Routine Description:
+
+ This function opens a handle to the server and scan its bindery
+ for the given queue object id.
+
+Arguments:
+ handleServer - Supplies the handle of the server on which to
+ scan the bindery.
+
+ QueueName - Supplies the name of the print queue.
+
+ QueueId - On output, supplies the object ID of the given queue.
+
+
+Return Value:
+
+ NO_ERROR - Successfully gotten a file server name.
+
+--*/
+{
+
+ NTSTATUS ntstatus;
+
+ UNICODE_STRING UQueueName;
+ OEM_STRING OemQueueName;
+
+#if DBG
+ IF_DEBUG(QUEUE) {
+ KdPrint(("NWWORKSTATION: NwGetQueueId %ws\n",
+ QueueName ));
+ }
+#endif
+
+ RtlInitUnicodeString( &UQueueName, QueueName);
+ ntstatus = RtlUnicodeStringToOemString( &OemQueueName, &UQueueName, TRUE);
+
+ //
+ // Map Japanese special characters
+ //
+ MapSpecialJapaneseChars(OemQueueName.Buffer,OemQueueName.Length);
+
+ if ( NT_SUCCESS(ntstatus))
+ {
+ ntstatus = NwlibMakeNcp(
+ handleServer,
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 58, // Max request packet size
+ 59, // Max response packet size
+ "bdwp|d", // Format string
+ 0x37, // Scan bindery object
+ 0xFFFFFFFF, // Previous ID
+ 0x3, // Print Queue object
+ OemQueueName.Buffer, // Queue Name
+ QueueId // Queue ID
+ );
+ }
+
+#if DBG
+ if ( NT_SUCCESS(ntstatus)) {
+ IF_DEBUG(QUEUE) {
+ KdPrint(("NWWORKSTATION: NwGetQueueId QueueId %08lx\n",
+ *QueueId ));
+ }
+ }
+#endif
+
+ RtlFreeOemString( &OemQueueName );
+ return NwMapBinderyCompletionCode(ntstatus);
+
+}
+
+
+
+DWORD
+NwCreateQueueJobAndFile(
+ IN HANDLE handleServer,
+ IN DWORD QueueId,
+ IN LPWSTR DocumentName,
+ IN LPWSTR UserName,
+ IN DWORD fGateway,
+ IN LPWSTR QueueName,
+ OUT LPWORD JobId
+ )
+/*++
+
+Routine Description:
+
+ This function uses an opened handle to a server to
+ enter a new job into the queue with the given QueueId.
+
+Arguments:
+
+ handleServer - Supplies the handle to the server on
+ which add the job.
+
+ QueueId - Supplies the id of the queue in which to add the job.
+ DocumentName - Supplies the name of the document to be printed
+ UserName - Supplies the banner name to be printed
+ fGateway - TRUE if gateway printing, FALSE otherwise
+ QueueName - Supplies the header name to be printed
+ JobId - Receives the job id of the newly added job.
+
+Return Value:
+
+ NO_ERROR - Successfully added the job to the queue.
+
+--*/
+{
+ NTSTATUS ntstatus = STATUS_SUCCESS;
+
+ UNICODE_STRING UDocumentName;
+ OEM_STRING OemDocumentName;
+ UNICODE_STRING UUserName;
+ OEM_STRING OemUserName;
+ UNICODE_STRING UQueueName;
+ OEM_STRING OemQueueName;
+
+#if DBG
+ IF_DEBUG(QUEUE) {
+ KdPrint(("NWWORKSTATION: NwCreateQueueJobAndFile QueueId %08lx\n",
+ QueueId ));
+ }
+#endif
+
+ if ( UserName )
+ {
+ RtlInitUnicodeString( &UUserName, UserName);
+ ntstatus = RtlUnicodeStringToOemString( &OemUserName,
+ &UUserName,
+ TRUE );
+ }
+
+ if ( NT_SUCCESS(ntstatus) && DocumentName )
+ {
+ RtlInitUnicodeString( &UDocumentName, DocumentName);
+ ntstatus = RtlUnicodeStringToOemString( &OemDocumentName,
+ &UDocumentName,
+ TRUE );
+ }
+
+ if ( NT_SUCCESS(ntstatus) && QueueName )
+ {
+ RtlInitUnicodeString( &UQueueName, QueueName);
+ ntstatus = RtlUnicodeStringToOemString( &OemQueueName,
+ &UQueueName,
+ TRUE );
+ }
+
+ if ( NT_SUCCESS( ntstatus)) {
+
+ LPSTR pszDocument, pszUser, pszQueue;
+
+ pszDocument = DocumentName? OemDocumentName.Buffer : "";
+ pszUser = UserName? OemUserName.Buffer : "";
+ pszQueue = QueueName? OemQueueName.Buffer : "";
+
+ ntstatus = NwlibMakeNcp(
+ handleServer,
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 263, // Max request packet size
+ 56, // Max response packet size
+ "bd_ddw_b_Cbbwwww_C-C-_|_w", // Format string
+ 0x68, // Create Queue Job and File object
+ QueueId, // Queue ID
+ 6, // Skip bytes
+ 0xffffffff, // Target Server ID number
+ 0xffffffff, 0xffff, // Target Execution time
+ 11, // Skip bytes
+ 0x00, // Job Control Flags
+ 26, // Skip bytes
+ pszDocument, // TextJobDescription
+ 50, // Skip bytes
+ 0, // Version number (clientarea)
+ 8, // Tab Size
+ 1, // Number of copies
+ NwPrintOption, // Print Control Flags
+ 0x3C, // Maximum lines
+ 0x84, // Maximum characters
+ 22, // Skip bytes
+ pszUser, // Banner Name
+ 12, // Max Length of pszUser
+ pszQueue, // Header Name
+ 12, // Max Length of pszQueue
+ 14 + 80, // Skip remainder of client area
+ 22, // Skip bytes
+ JobId // Job ID
+ );
+
+ }
+
+#if DBG
+ if ( NT_SUCCESS( ntstatus)) {
+ IF_DEBUG(QUEUE) {
+ KdPrint(("NWWORKSTATION: NwCreateQueueJobAndFile JobId %d\n",
+ *JobId ));
+ }
+ }
+#endif
+
+ RtlFreeOemString( &OemDocumentName );
+ RtlFreeOemString( &OemUserName );
+ RtlFreeOemString( &OemQueueName );
+ return NwMapStatus(ntstatus);
+}
+
+
+
+DWORD
+NwCloseFileAndStartQueueJob(
+ IN HANDLE handleServer,
+ IN DWORD QueueId,
+ IN WORD JobId
+ )
+/*++
+
+Routine Description:
+
+ This function uses an opened handle to a server to
+ close a job file and mark the job file ready for service.
+
+Arguments:
+
+ handleServer - Supplies the handle to the server on
+ which add the job.
+
+ QueueId - Supplies the id of the queue in which to add the job.
+ JobId - Supplies the job id.
+
+Return Value:
+
+ NO_ERROR - Successfully added the job to the queue.
+
+--*/
+{
+ NTSTATUS ntstatus;
+
+#if DBG
+ IF_DEBUG(QUEUE) {
+ KdPrint(("NWWORKSTATION: NwCloseFileAndStartQueueJob QueueId %08lx JobId %d\n", QueueId, JobId ));
+ }
+#endif
+
+ // Two versions of CloseFileAndStartQueueJobNCP
+
+ ntstatus = NwlibMakeNcp(
+ handleServer,
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 9, // Max request packet size
+ 2, // Max response packet size
+ "bdw|", // Format string
+ 0x69, // Close File And Start Queue Job
+ QueueId, // Queue ID
+ JobId ); // Job ID
+
+ return NwMapStatus(ntstatus);
+}
+
+
+
+DWORD
+NwRemoveJobFromQueue(
+ IN HANDLE handleServer,
+ IN DWORD QueueId,
+ IN WORD JobId
+ )
+/*++
+
+Routine Description:
+
+ This function removes a job from a queue and closes the associate file.
+
+Arguments:
+
+ handleServer - Supplies the handle to the server on
+ which to remove the job.
+
+ QueueId - Supplies the id of the queue in which to remove the job.
+ JobId - Supplies the job id to be removed.
+
+Return Value:
+
+ NO_ERROR - Successfully removed the job from the queue.
+
+--*/
+{
+ NTSTATUS ntstatus;
+
+#if DBG
+ IF_DEBUG(QUEUE) {
+ KdPrint(("NWWORKSTATION: NwRemoveJobFromQueue QueueId %08lx JobId %d\n",
+ QueueId, JobId ));
+ }
+#endif
+
+ ntstatus = NwlibMakeNcp(
+ handleServer,
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 9, // Max request packet size
+ 2, // Max response packet size
+ "bdw|", // Format string
+ 0x6A, // Remove Job From Queue
+ QueueId, // Queue ID
+ JobId ); // Job ID
+
+ return NwMapStatus(ntstatus);
+}
+
+
+DWORD
+NwRemoveAllJobsFromQueue(
+ IN HANDLE handleServer,
+ IN DWORD QueueId
+ )
+/*++
+
+Routine Description:
+
+ This function removes all jobs from a queue.
+
+Arguments:
+
+ handleServer - Supplies the handle to the server on
+ which to remove all jobs.
+
+ QueueId - Supplies the id of the queue in which to remove all jobs.
+
+Return Value:
+
+ NO_ERROR - Successfully removed all jobs from the queue.
+
+--*/
+{
+ DWORD err;
+ WORD JobCount;
+ WORD pwJobList[250];
+ WORD i;
+
+#if DBG
+ IF_DEBUG(QUEUE)
+ {
+ KdPrint(("NWWORKSTATION: NwRemoveAllJobsFromQueue QueueId %08lx\n",
+ QueueId ));
+ }
+#endif
+
+ err = NwGetQueueJobList( handleServer,
+ QueueId,
+ &JobCount,
+ pwJobList );
+
+ for ( i = 0; !err && i < JobCount; i++ )
+ {
+ err = NwRemoveJobFromQueue( handleServer,
+ QueueId,
+ pwJobList[i] );
+
+ }
+
+ return err;
+}
+
+
+DWORD
+NwReadQueueCurrentStatus(
+ IN HANDLE handleServer,
+ IN DWORD QueueId,
+ OUT LPBYTE QueueStatus,
+ OUT LPBYTE NumberOfJobs
+ )
+/*++
+
+Routine Description:
+
+ This function uses an opened handle to a server to
+ query the status of the queue with the given QueueId.
+
+Arguments:
+
+ handleServer - Supplies the handle to the server on
+ which add the job.
+ QueueId - Supplies the id of the queue
+ QueueStatus - Receives the status of the queue
+ NumberOfJobs - Receives the number of jobs in the queue.
+
+Return Value:
+
+ NO_ERROR - Successfully retrieved the status of the queue.
+
+--*/
+{
+ NTSTATUS ntstatus;
+
+#if DBG
+ IF_DEBUG(QUEUE) {
+ KdPrint(("NWWORKSTATION: NwReadQueueCurrentStatus QueueId %08lx\n",
+ QueueId ));
+ }
+#endif
+
+ ntstatus = NwlibMakeNcp(
+ handleServer,
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 7, // Max request packet size
+ 135, // Max response packet size
+ "bd|==bb", // Format string
+ 0x66, // ReadQueueCurrentStatus
+ QueueId, // Queue ID
+ QueueStatus, // Queue status
+ NumberOfJobs // Number of jobs in the queue
+ );
+
+#if DBG
+ if ( NT_SUCCESS( ntstatus)) {
+ IF_DEBUG(QUEUE) {
+ KdPrint(("NWWORKSTATION: NwReadQueueCurrentStatus QueueStatus %d Number of Jobs %d\n", *QueueStatus, *NumberOfJobs ));
+ }
+ }
+#endif
+
+ return NwMapStatus(ntstatus);
+}
+
+
+DWORD
+NwSetQueueCurrentStatus(
+ IN HANDLE handleServer,
+ IN DWORD QueueId,
+ IN BYTE QueueStatus
+ )
+/*++
+
+Routine Description:
+
+ This function uses an opened handle to a server to
+ set the status (pause/ready...) of the queue with the given QueueId.
+
+Arguments:
+
+ handleServer - Supplies the handle to the server on
+ which add the job.
+ QueueId - Supplies the id of the queue
+ QueueStatus - Supplies the status of the queue
+
+Return Value:
+
+ NO_ERROR - Successfully set the status of the queue.
+
+--*/
+{
+ NTSTATUS ntstatus;
+
+#if DBG
+ IF_DEBUG(QUEUE) {
+ KdPrint(("NWWORKSTATION: NwSetQueueCurrentStatus QueueId %08lx\n",
+ QueueId ));
+ }
+#endif
+
+ ntstatus = NwlibMakeNcp(
+ handleServer,
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 8, // Max request packet size
+ 2, // Max response packet size
+ "bdb|", // Format string
+ 0x67, // ReadQueueCurrentStatus
+ QueueId, // Queue ID
+ QueueStatus // Queue status
+ );
+
+ return NwMapStatus(ntstatus);
+}
+
+
+DWORD
+NwGetQueueJobList(
+ IN HANDLE handleServer,
+ IN DWORD QueueId,
+ OUT LPWORD NumberOfJobs,
+ OUT LPWORD JobIdList
+ )
+/*++
+
+Routine Description:
+
+ This function uses an opened handle to a server to
+ get the job list of the queue with the given QueueId.
+
+Arguments:
+
+ handleServer - Supplies the handle to the server on
+ which add the job.
+ QueueId - Supplies the id of the queue
+ NumberOfJobs - Receives the number of jobs in the queue.
+ JobIdList - Receives the array of job ids in the queue
+
+Return Value:
+
+ NO_ERROR - Successfully added the job to the queue.
+
+--*/
+{
+ NTSTATUS ntstatus;
+#if DBG
+ WORD i;
+
+ IF_DEBUG(QUEUE) {
+ KdPrint(("NWWORKSTATION: NwGetQueueJobList QueueId %08lx\n",
+ QueueId ));
+ }
+#endif
+
+ ntstatus = NwlibMakeNcp(
+ handleServer,
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 7, // Max request packet size
+ 506, // Max response packet size
+ "bd|W", // Format string
+ 0x6B, // Get Queue Job List
+ QueueId, // Queue ID
+ NumberOfJobs, // Number of jobs in the queue
+ JobIdList // Array of job ids
+ );
+
+#if DBG
+ if ( NT_SUCCESS(ntstatus)) {
+ IF_DEBUG(QUEUE) {
+ KdPrint(("NWWORKSTATION: NwGetQueueJobList Number of Jobs %d\nJob List = ", *NumberOfJobs ));
+ for ( i = 0; i < *NumberOfJobs; i++ )
+ KdPrint(("%d ", JobIdList[i] ));
+ KdPrint(("\n"));
+ }
+ }
+#endif
+
+ return NwMapStatus(ntstatus);
+}
+
+
+
+DWORD
+NwReadQueueJobEntry(
+ IN HANDLE handleServer,
+ IN DWORD QueueId,
+ IN WORD JobId,
+ OUT JOBTIME TargetExecutionTime,
+ OUT JOBTIME JobEntryTime,
+ OUT LPBYTE JobPosition,
+ OUT LPBYTE JobControlFlags,
+ OUT LPSTR TextJobDescription,
+ OUT LPSTR UserName
+ )
+/*++
+
+Routine Description:
+
+ This function uses an opened handle to a server to
+ get the information about the job with the given JobId
+ in the given QueueId.
+
+Arguments:
+
+ handleServer - Supplies the handle to the server on
+ which add the job.
+ QueueId - Supplies the id of the queue
+ JobId - Supplies the job we are interested in
+
+ TargetExecutionTime -
+ JobEntryTime -
+ JobPosition -
+ JobControlsFlags -
+ TextJobDescription -
+
+Return Value:
+
+ NO_ERROR - Successfully added the job to the queue.
+
+--*/
+{
+ NTSTATUS ntstatus;
+
+#if DBG
+ IF_DEBUG(QUEUE) {
+ KdPrint(("NWWORKSTATION: NwReadQueueJobEntry QueueId %08lx JobId %d\n",
+ QueueId, JobId ));
+ }
+#endif
+
+ ntstatus = NwlibMakeNcp(
+ handleServer,
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 9, // Max request packet size
+ 258, // Max response packet size
+ "bdw|_rr==bb_C_c", // Format string
+ 0x6C, // Read Queue Job Entry
+ QueueId, // Queue ID
+ JobId, // Job ID
+ 10, // Skip bytes
+ TargetExecutionTime, // Array storing execution time
+ 6, // Size of TargetExecutionTime
+ JobEntryTime, // Array storing job entry time
+ 6, // Size of JobEntryTime
+ JobPosition, // Job Position
+ JobControlFlags, // Job Control Flag
+ 26, // Skip bytes
+ TextJobDescription, // Array storing the description
+ 50, // Maximum size in the above array
+ 32, // Skip bytes
+ UserName // Banner Name
+ );
+
+#if DBG
+ if ( NT_SUCCESS( ntstatus)) {
+ IF_DEBUG(QUEUE) {
+ KdPrint(("NWWORKSTATION: NwReadQueueJobEntry JobPosition %d Status %d Description %s\n", *JobPosition, *JobControlFlags, TextJobDescription ));
+ }
+ }
+#endif
+
+ return NwMapStatus(ntstatus);
+}
+
+
+
+DWORD
+NwGetQueueJobsFileSize(
+ IN HANDLE handleServer,
+ IN DWORD QueueId,
+ IN WORD JobId,
+ OUT LPDWORD FileSize
+ )
+/*++
+
+Routine Description:
+
+ This function uses an opened handle to a server to
+ get the file size of the given job.
+
+Arguments:
+
+ handleServer - Supplies the handle to the server on
+ which add the job.
+ QueueId - Supplies the id of the queue
+ JobId - Identifying the job we are interested in
+ FileSize - Receives the file size of the given job
+
+Return Value:
+
+ NO_ERROR - Successfully retrieved the file size.
+
+--*/
+{
+ NTSTATUS ntstatus;
+
+#if DBG
+ IF_DEBUG(QUEUE) {
+ KdPrint(("NWWORKSTATION: NwGetQueueJobsFileSize QueueId %08lx JobId %d\n", QueueId, JobId ));
+ }
+#endif
+
+ ntstatus = NwlibMakeNcp(
+ handleServer,
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 9, // Max request packet size
+ 12, // Max response packet size
+ "bdw|===d", // Format string
+ 0x78, // Get Queue Job's File Size
+ QueueId, // Queue ID
+ JobId, // Job ID
+ FileSize // File Size
+ );
+
+#if DBG
+ if ( NT_SUCCESS( ntstatus)) {
+ IF_DEBUG(QUEUE) {
+ KdPrint(("NWWORKSTATION: NwGetQueueJobsFileSize File Size %d\n",
+ *FileSize ));
+ }
+ }
+#endif
+
+ return NwMapStatus(ntstatus);
+}
+
+
+
+DWORD
+NwChangeQueueJobPosition(
+ IN HANDLE handleServer,
+ IN DWORD QueueId,
+ IN WORD JobId,
+ IN BYTE NewPosition
+ )
+/*++
+
+Routine Description:
+
+ This function uses an opened handle to a server to
+ get the change a job's position in a queue.
+
+Arguments:
+
+ handleServer - Supplies the handle to the server on
+ which add the job.
+ QueueId - Supplies the id of the queue
+ JobId - Identifying the job we are interested in
+ NewPosition - Supplies the new position of the job
+
+Return Value:
+
+ NO_ERROR - Successfully retrieved the file size.
+
+--*/
+{
+ NTSTATUS ntstatus;
+
+#if DBG
+ IF_DEBUG(QUEUE) {
+ KdPrint(("NWWORKSTATION: NwChangeQueueJobPosition QueueId %08lx JobId %d NewPosition %d\n", QueueId, JobId, NewPosition ));
+ }
+#endif
+
+ ntstatus = NwlibMakeNcp(
+ handleServer,
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 10, // Max request packet size
+ 2, // Max response packet size
+ "bdwb|", // Format string
+ 0x6E, // Change Queue Job Position
+ QueueId, // Queue ID
+ JobId, // Job ID
+ NewPosition // New position of the job
+ );
+
+ return NwMapStatus(ntstatus);
+}
+
+
+
+DWORD
+NwChangeQueueJobEntry(
+ IN HANDLE handleServer,
+ IN DWORD QueueId,
+ IN WORD JobId,
+ IN DWORD dwCommand,
+ IN PNW_JOB_INFO pNwJobInfo
+ )
+/*++
+
+Routine Description:
+
+ This function uses an opened handle to a server to
+ get the change a job's position in a queue.
+
+Arguments:
+
+ handleServer - Supplies the handle to the server on
+ which add the job.
+ QueueId - Supplies the id of the queue
+ JobId - Identifying the job we are interested in
+ JobControlFlags - Supplies the new job control flags
+ pNwJobInfo -
+
+Return Value:
+
+ NO_ERROR - Successfully retrieved the file size.
+
+--*/
+{
+ NTSTATUS ntstatus;
+ DWORD TargetServerId;
+ JOBTIME TargetExecutionTime;
+ WORD JobType;
+ BYTE JobControlFlags;
+ BYTE TextJobDescription[50];
+ BYTE ClientRecordArea[152];
+
+ UNICODE_STRING UDocumentName;
+ OEM_STRING OemDocumentName;
+ UNICODE_STRING UUserName;
+ OEM_STRING OemUserName;
+ LPSTR pszDocument, pszUser;
+
+#if DBG
+ IF_DEBUG(QUEUE) {
+ KdPrint(("NWWORKSTATION: NwChangeQueueJobEntry QueueId %08lx JobId %d dwCommand %d\n", QueueId, JobId, dwCommand ));
+ }
+#endif
+
+ if ( pNwJobInfo )
+ {
+ if ( pNwJobInfo->pUserName )
+ {
+ RtlInitUnicodeString( &UUserName, pNwJobInfo->pUserName);
+ ntstatus = RtlUnicodeStringToOemString( &OemUserName,
+ &UUserName,
+ TRUE );
+ }
+
+ if ( NT_SUCCESS(ntstatus) && pNwJobInfo->pDocument )
+ {
+ RtlInitUnicodeString( &UDocumentName, pNwJobInfo->pDocument);
+ ntstatus = RtlUnicodeStringToOemString( &OemDocumentName,
+ &UDocumentName,
+ TRUE );
+ }
+
+ if ( NT_SUCCESS( ntstatus))
+ {
+ pszDocument = pNwJobInfo->pDocument? OemDocumentName.Buffer : "";
+ pszUser = pNwJobInfo->pUserName? OemUserName.Buffer: "";
+ }
+ }
+
+ if ( NT_SUCCESS( ntstatus))
+ {
+ ntstatus = NwlibMakeNcp(
+ handleServer,
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 9, // Max request packet size
+ 258, // Max response packet size
+ "bdw|_dr_w-b_rr", // Format string
+ 0x6C, // Read Queue Job Entry
+ QueueId, // Queue ID
+ JobId, // Job ID
+ 6, // Skip bytes
+ &TargetServerId, // Target Server ID Number
+ TargetExecutionTime, // Target Execution Time
+ 6, // sizeof TargetExecutionTime
+ 8, // Skip bytes
+ &JobType, // Job Type
+ &JobControlFlags, // Job Control flags
+ 26, // Skip bytes
+ TextJobDescription, // TextJobDescription
+ 50, // sizeof TextJobDescription
+ ClientRecordArea, // Client record area
+ 152 // sizeof ClientRecordArea
+ );
+ }
+
+ if ( NT_SUCCESS( ntstatus))
+ {
+ switch ( dwCommand )
+ {
+ case JOB_CONTROL_PAUSE:
+ JobControlFlags |= QF_USER_HOLD;
+ break;
+
+ case JOB_CONTROL_RESUME:
+ JobControlFlags &= ~( QF_USER_HOLD | QF_OPERATOR_HOLD );
+ break;
+
+ default:
+ break;
+
+ }
+
+ ntstatus = NwlibMakeNcp(
+ handleServer,
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 263, // Max request packet size
+ 2, // Max response packet size
+ "bd_dr_ww-b_CrCr|", // Format string
+ 0x6D, // Change Queue Job Entry
+ QueueId, // Queue ID
+ 6, // Skip bytes
+ TargetServerId, // Target Server ID Number
+ TargetExecutionTime, // Target Execution Time
+ 6, // sizeof TargetExecutionTime
+ 6, // Skip bytes
+ JobId, // Job ID
+ JobType, // Job Type
+ JobControlFlags, // Job Control Flags
+ 26, // Skip bytes
+ pNwJobInfo? pszDocument
+ : TextJobDescription, // Description
+ 50, // Skip bytes of Description
+ ClientRecordArea, // Client Record Area
+ 32, // First 32 bytes of the above
+ pNwJobInfo? pszUser
+ : (LPSTR) &ClientRecordArea[32], // Banner Name
+ 13, // sizeof BannerName
+ &ClientRecordArea[45], // Rest of the Client Area
+ 107 // sizeof the above
+ );
+
+ if ( pNwJobInfo )
+ {
+ if ( pNwJobInfo->pDocument )
+ RtlFreeOemString( &OemDocumentName );
+
+ if ( pNwJobInfo->pUserName )
+ RtlFreeOemString( &OemUserName );
+ }
+ }
+
+ return NwMapStatus(ntstatus);
+}
+
+
+
+DWORD
+NwGetQueueJobs(
+ IN HANDLE handleServer,
+ IN DWORD QueueId,
+ IN LPWSTR PrinterName,
+ IN DWORD FirstJobRequested,
+ IN DWORD EntriesRequested,
+ IN DWORD Level,
+ OUT LPBYTE Buffer,
+ IN DWORD cbBuf,
+ OUT LPDWORD BytesNeeded,
+ OUT LPDWORD Entries
+ )
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ handleServer - Supplies the handle to the server on
+ which add the job.
+ QueueId - Supplies the id of the queue
+
+Return Value:
+
+
+--*/
+{
+ DWORD err = NO_ERROR;
+
+ DWORD i;
+ WORD JobCount;
+ WORD pwJobList[250];
+
+ DWORD EntrySize = 0;
+ LPBYTE FixedPortion = Buffer;
+ LPWSTR EndOfVariableData = ( LPWSTR ) ((DWORD) Buffer + cbBuf );
+
+#if DBG
+ IF_DEBUG(QUEUE)
+ KdPrint(("NWWORKSTATION: NwGetQueueJobs QueueId %08lx\n", QueueId));
+#endif
+
+ *BytesNeeded = 0;
+ *Entries = 0;
+
+ err = NwGetQueueJobList( handleServer,
+ QueueId,
+ &JobCount,
+ pwJobList );
+
+
+ if ( err )
+ {
+ KdPrint(("NWWORKSTATION: NwGetQueueJobList Error %d\n", err ));
+ return err;
+ }
+
+ for ( i = 0; (i < EntriesRequested) && ( i+FirstJobRequested+1 <= JobCount);
+ i++ )
+ {
+ err = NwGetQueueJobInfo( handleServer,
+ QueueId,
+ pwJobList[i+FirstJobRequested],
+ PrinterName,
+ Level,
+ &FixedPortion,
+ &EndOfVariableData,
+ &EntrySize );
+
+ if ( err != NO_ERROR && err != ERROR_INSUFFICIENT_BUFFER )
+ break;
+
+ *BytesNeeded += EntrySize;
+ }
+
+
+ if ( err == ERROR_INSUFFICIENT_BUFFER )
+ {
+ *Entries = 0;
+ }
+ else if ( err == NO_ERROR )
+ {
+ *Entries = i;
+ }
+
+ return err;
+}
+
+
+
+DWORD
+NwGetQueueJobInfo(
+ IN HANDLE handleServer,
+ IN DWORD QueueId,
+ IN WORD JobId,
+ IN LPWSTR PrinterName,
+ IN DWORD Level,
+ IN OUT LPBYTE *FixedPortion,
+ IN OUT LPWSTR *EndOfVariableData,
+ OUT LPDWORD EntrySize
+ )
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ handleServer - Supplies the handle to the server on
+ which add the job.
+ QueueId - Supplies the id of the queue
+
+Return Value:
+
+
+--*/
+{
+ DWORD err;
+ LPWSTR UTextJobDescription = NULL;
+ LPWSTR UUserName = NULL;
+
+ JOBTIME TargetExecutionTime;
+ JOBTIME JobEntryTime;
+ BYTE JobPosition;
+ BYTE JobControlFlags;
+ CHAR UserName[14];
+ CHAR TextJobDescription[50];
+ DWORD FileSize = 0;
+
+ err = NwReadQueueJobEntry( handleServer,
+ QueueId,
+ JobId,
+ TargetExecutionTime,
+ JobEntryTime,
+ &JobPosition,
+ &JobControlFlags,
+ TextJobDescription,
+ UserName );
+
+ if ( err )
+ {
+ KdPrint(("NWWORKSTATION: NwReadQueueJobEntry JobId %d Error %d\n",
+ JobId, err ));
+ return err;
+ }
+
+ if (!NwConvertToUnicode( &UTextJobDescription, TextJobDescription ))
+ {
+ err = ERROR_NOT_ENOUGH_MEMORY ;
+ goto ErrorExit ;
+ }
+
+ if (!NwConvertToUnicode( &UUserName, UserName ))
+ {
+ err = ERROR_NOT_ENOUGH_MEMORY ;
+ goto ErrorExit ;
+ }
+
+ *EntrySize = ( Level == 1? sizeof( JOB_INFO_1W ) : sizeof( JOB_INFO_2W ))
+ + ( wcslen( UTextJobDescription ) + wcslen( UUserName) +
+ wcslen( PrinterName ) + 3 ) * sizeof( WCHAR );
+ //
+ // See if the buffer is large enough to fit the entry
+ //
+ if ( ((DWORD) *FixedPortion + *EntrySize ) > (DWORD) *EndOfVariableData )
+ {
+ err = ERROR_INSUFFICIENT_BUFFER;
+ goto ErrorExit ;
+ }
+
+ if ( Level == 2 )
+ {
+ err = NwGetQueueJobsFileSize( handleServer,
+ QueueId,
+ JobId,
+ &FileSize );
+
+ if ( err )
+ {
+ KdPrint(("NWWORKSTATION: NwGetQueueJobsFileSize JobId %d Error %d\n", JobId, err ));
+ goto ErrorExit ;
+ }
+ }
+
+ err = NwWriteJobInfoEntry( FixedPortion,
+ EndOfVariableData,
+ Level,
+ JobId,
+ PrinterName,
+ UTextJobDescription,
+ UUserName,
+ JobControlFlags,
+ JobPosition,
+ JobEntryTime,
+ TargetExecutionTime,
+ FileSize );
+
+ErrorExit:
+
+ if (UTextJobDescription)
+ (void) LocalFree((HLOCAL) UTextJobDescription) ;
+ if (UUserName)
+ (void) LocalFree((HLOCAL) UUserName) ;
+
+ return err;
+}
+
+
+
+DWORD
+NwWriteJobInfoEntry(
+ IN OUT LPBYTE *FixedPortion,
+ IN OUT LPWSTR *EndOfVariableData,
+ IN DWORD Level,
+ IN WORD JobId,
+ IN LPWSTR PrinterName,
+ IN LPWSTR JobDescription,
+ IN LPWSTR UserName,
+ IN BYTE JobControlFlags,
+ IN BYTE JobPosition,
+ IN JOBTIME JobEntryTime,
+ IN JOBTIME TargetExecutionTime,
+ IN DWORD FileSize
+ )
+/*++
+
+Routine Description:
+
+ This function packages a JOB_INFO_1 or JOB_INFO_2 entry into the
+ user output buffer.
+
+Arguments:
+
+ FixedPortion - Supplies a pointer to the output buffer where the next
+ entry of the fixed portion of the use information will be written.
+ This pointer is updated to point to the next fixed portion entry
+ after a PRINT_INFO_1 entry is written.
+
+ EndOfVariableData - Supplies a pointer just off the last available byte
+ in the output buffer. This is because the variable portion of the
+ user information is written into the output buffer starting from
+ the end.
+
+ This pointer is updated after any variable length information is
+ written to the output buffer.
+
+Return Value:
+
+ NO_ERROR - Successfully wrote entry into user buffer.
+
+ ERROR_INSUFFICIENT_BUFFER - Buffer was too small to fit entry.
+
+--*/
+{
+ DWORD err = NO_ERROR;
+ BOOL FitInBuffer = TRUE;
+ DWORD JobStatus = 0;
+
+ JOB_INFO_1W *pJobInfo1 = (JOB_INFO_1W *) *FixedPortion;
+ JOB_INFO_2W *pJobInfo2 = (JOB_INFO_2W *) *FixedPortion;
+
+
+ if ( ( JobControlFlags & QF_USER_HOLD )
+ || ( JobControlFlags & QF_OPERATOR_HOLD )
+ )
+ {
+ JobStatus = JOB_STATUS_PAUSED;
+ }
+
+ //
+ // See if buffer is large enough to fit the entry.
+ //
+
+ if ( Level == 1 )
+ {
+ pJobInfo1->JobId = JobId;
+ pJobInfo1->Position = JobPosition;
+ pJobInfo1->Status = JobStatus;
+ if ( err = ConvertToSystemTime( JobEntryTime, &pJobInfo1->Submitted ))
+ return err;
+
+ pJobInfo1->pMachineName = NULL;
+ pJobInfo1->pDatatype = NULL;
+ pJobInfo1->pStatus = NULL;
+ pJobInfo1->Priority = 0;
+ pJobInfo1->TotalPages = 0;
+ pJobInfo1->PagesPrinted = 0;
+
+ //
+ // Update fixed entry pointer to next entry.
+ //
+ (DWORD) (*FixedPortion) += sizeof(JOB_INFO_1W);
+
+ //
+ // PrinterName
+ //
+ FitInBuffer = NwlibCopyStringToBuffer(
+ PrinterName,
+ wcslen(PrinterName),
+ (LPCWSTR) *FixedPortion,
+ EndOfVariableData,
+ &pJobInfo1->pPrinterName
+ );
+
+ ASSERT(FitInBuffer);
+
+ //
+ // UserName
+ //
+ FitInBuffer = NwlibCopyStringToBuffer(
+ UserName,
+ wcslen(UserName),
+ (LPCWSTR) *FixedPortion,
+ EndOfVariableData,
+ &pJobInfo1->pUserName
+ );
+
+ ASSERT(FitInBuffer);
+
+ //
+ // Description
+ //
+ FitInBuffer = NwlibCopyStringToBuffer(
+ JobDescription,
+ wcslen(JobDescription),
+ (LPCWSTR) *FixedPortion,
+ EndOfVariableData,
+ &pJobInfo1->pDocument
+ );
+
+ ASSERT(FitInBuffer);
+ }
+ else // Level == 2
+ {
+ pJobInfo2->JobId = JobId;
+ pJobInfo2->Position = JobPosition;
+ pJobInfo2->Status = JobStatus;
+ if ( err = ConvertToSystemTime( JobEntryTime, &pJobInfo2->Submitted ))
+ return err;
+
+ pJobInfo2->StartTime = 0;
+ pJobInfo2->Size = FileSize;
+
+ pJobInfo2->pMachineName = NULL;
+ pJobInfo2->pNotifyName = NULL;
+ pJobInfo2->pDatatype = NULL;
+ pJobInfo2->pPrintProcessor = NULL;
+ pJobInfo2->pParameters = NULL;
+ pJobInfo2->pDriverName = NULL;
+ pJobInfo2->pDevMode = NULL;
+ pJobInfo2->pStatus = NULL;
+ pJobInfo2->pSecurityDescriptor = NULL;
+ pJobInfo2->Priority = 0;
+ pJobInfo2->TotalPages = 0;
+ pJobInfo2->UntilTime = 0;
+ pJobInfo2->Time = 0;
+ pJobInfo2->PagesPrinted = 0;
+
+ //
+ // Update fixed entry pointer to next entry.
+ //
+ (DWORD) (*FixedPortion) += sizeof(JOB_INFO_2W);
+
+ //
+ // PrinterName
+ //
+ FitInBuffer = NwlibCopyStringToBuffer(
+ PrinterName,
+ wcslen(PrinterName),
+ (LPCWSTR) *FixedPortion,
+ EndOfVariableData,
+ &pJobInfo2->pPrinterName
+ );
+
+ ASSERT(FitInBuffer);
+
+ //
+ // UserName
+ //
+ FitInBuffer = NwlibCopyStringToBuffer(
+ UserName,
+ wcslen(UserName),
+ (LPCWSTR) *FixedPortion,
+ EndOfVariableData,
+ &pJobInfo2->pUserName
+ );
+
+ ASSERT(FitInBuffer);
+
+ //
+ // Description
+ //
+ FitInBuffer = NwlibCopyStringToBuffer(
+ JobDescription,
+ wcslen(JobDescription),
+ (LPCWSTR) *FixedPortion,
+ EndOfVariableData,
+ &pJobInfo2->pDocument
+ );
+
+ ASSERT(FitInBuffer);
+ }
+
+ if (!FitInBuffer)
+ return ERROR_INSUFFICIENT_BUFFER;
+
+ return NO_ERROR;
+}
+
+
+
+DWORD
+ConvertToSystemTime(
+ IN JOBTIME JobTime,
+ OUT LPSYSTEMTIME pSystemTime
+)
+/*++
+
+Routine Description:
+
+Arguments:
+ JobTime -
+ pSystemTime -
+
+Return Value:
+
+--*/
+{
+ FILETIME fileTimeLocal, fileTimeUTC;
+
+ pSystemTime->wYear = JobTime[0] + 1900;
+ pSystemTime->wMonth = JobTime[1];
+ pSystemTime->wDay = JobTime[2];
+ pSystemTime->wDayOfWeek = 0;
+ pSystemTime->wHour = JobTime[3];
+ pSystemTime->wMinute = JobTime[4];
+ pSystemTime->wSecond = JobTime[5];
+ pSystemTime->wMilliseconds = 0;
+
+ if ( ( !SystemTimeToFileTime( pSystemTime, &fileTimeLocal ) )
+ || ( !LocalFileTimeToFileTime( &fileTimeLocal, &fileTimeUTC ) )
+ || ( !FileTimeToSystemTime( &fileTimeUTC, pSystemTime ) )
+ )
+ {
+ KdPrint(("NWWORKSTATION: Time Conversion Error = %d\n",GetLastError()));
+ return GetLastError();
+ }
+
+ return NO_ERROR;
+}
+
+#ifndef NOT_USED
+
+DWORD
+
+ NwCreateQueue ( IN HANDLE hServer,
+ IN LPWSTR pszQueue,
+ OUT LPDWORD pQueueId
+ )
+
+/*+++
+Routine Description:
+
+ Uses the handle opened to a server to create a queue on the server.
+ Return the Queue Id if successful.
+
+Arguments:
+
+ hServer : Handle to the file Server
+ pszQueue : Name of the queue that you are creating on the server
+ pQueueId : Address of QueueId
+
+
+Return Value:
+
+ An error condition as it arises.
+ NO_ERROR: Successful in adding printer name
+ ERROR : otherwise
+--*/
+
+{
+ NTSTATUS ntstatus;
+ WORD ObjectType;
+ UNICODE_STRING UQueueName;
+ OEM_STRING OemQueueName;
+
+ *pQueueId = 0;
+#if DBG
+ IF_DEBUG(PRINT) {
+ KdPrint(("NWWORKSTATION: NwCreateQueue : %ws\n",
+ pszQueue));
+ }
+#endif
+
+ RtlInitUnicodeString( &UQueueName, pszQueue);
+ ntstatus = RtlUnicodeStringToOemString( &OemQueueName, &UQueueName, TRUE);
+
+ if ( NT_SUCCESS(ntstatus))
+ {
+
+ ntstatus = NwlibMakeNcp(
+ hServer,
+ FSCTL_NWR_NCP_E3H,
+ 174,
+ 6,
+ "bwpbp|d",
+ 0x64, //Create Queue
+ 0x0003, // Queue Type = Print Queue
+ OemQueueName.Buffer, //Queue Name
+ 0x00, // Directory Handle
+ "SYS:SYSTEM", //queue created in SYS:SYSTEM directory
+ pQueueId
+ );
+
+
+ }
+ else
+ {
+ goto Exit;
+ }
+
+ if ( NT_SUCCESS(ntstatus)) {
+#if DBG
+ IF_DEBUG(ENUM) {
+ KdPrint(("NWWORKSTATION: NwCreateQueue successful\n" ));
+ }
+#endif
+
+ }
+ else
+ goto FreeExit;
+
+ // Change Property Security on Q_OPERATORS
+
+ ntstatus = NwlibMakeNcp (
+ hServer,
+ FSCTL_NWR_NCP_E3H,
+ 70,
+ 2,
+ "bwpbp|",
+ 0x3B,
+ 0x0003,
+ OemQueueName.Buffer,
+ 0x1, //New Property security
+ "Q_OPERATORS"
+ );
+
+
+
+ if ( NT_SUCCESS(ntstatus)) {
+#if DBG
+ IF_DEBUG(PRINT) {
+ KdPrint(("NWWORKSTATION: Change Property Security successful\n" ));
+ }
+#endif
+
+ }
+ else
+ //unable to add new property security, so destroy queue and go to end
+ {
+ (void) NwDestroyQueue( hServer,
+ *pQueueId );
+
+ goto FreeExit;
+ }
+
+
+ // Add Bindery Object of Type Queue to Set
+
+ ntstatus = NwlibMakeNcp (
+ hServer,
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 122,
+ 2,
+ "bwppwp|",
+ 0x41,
+ 0x0003,
+ OemQueueName.Buffer,
+ "Q_OPERATORS",
+ 0x0001,
+ "SUPERVISOR"
+ );
+
+
+
+ if ( NT_SUCCESS(ntstatus)) {
+
+#if DBG
+ IF_DEBUG(PRINT) {
+ KdPrint(("NWWORKSTATION: Add Bindery Object:Q_OPERATORS\n" ));
+ }
+#endif
+
+ }
+ else
+ {
+ (void)NwDestroyQueue(hServer,*pQueueId);
+ goto FreeExit;
+
+ }
+ // Add Bindery Object to Set of Q_USERS
+
+ ntstatus = NwlibMakeNcp (
+ hServer,
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 122,
+ 2,
+ "bwppwp|",
+ 0x41,
+ 0x0003,
+ OemQueueName.Buffer,
+ "Q_USERS",
+ 0x0002,
+ "EVERYONE"
+ );
+
+ // bunch of parameters to Add Bindery Object to Set Q_USERS
+
+
+ if ( NT_SUCCESS(ntstatus)) {
+#if DBG
+ IF_DEBUG(PRINT) {
+ KdPrint(("NWWORKSTATION: AddBinderyObjecttoSet Q_USERS\n" ));
+ }
+#endif
+
+
+ }
+
+
+FreeExit: RtlFreeOemString( &OemQueueName);
+Exit:
+ return NwMapBinderyCompletionCode(ntstatus);
+}
+
+
+DWORD
+NwAssocPServers ( IN HANDLE hServer,
+ IN LPWSTR pszQueue,
+ IN LPWSTR pszPServer
+ )
+
+/*+++
+Routine Description:
+
+ Associates a list of Q Servers with a queue id. This list is supplied
+ to this routine as pszPServer with entries separated by semicolons
+
+Arguments:
+
+ hServer : Handle to the file Server
+ pszQueue : Name of the queue to which to associate the Q servers
+ pszPServer : List of Q Servers.
+
+
+Return Value:
+
+ An error condition as it arises.
+ 0x0 is returned if there is no error
+
+ BUGBUG: Current implementation does not return an error condition in any case
+
+--*/
+
+{
+ LPWSTR pszPServerlist = NULL;
+ LPWSTR pszNextPServer = NULL;
+ DWORD err = 0x00000000 ;
+ NTSTATUS ntstatus ;
+ UNICODE_STRING UQueueName, UNextPServer;
+ OEM_STRING OemQueueName,OemNextPServer;
+
+
+ if (pszPServer == NULL)
+ return NO_ERROR;
+
+ if((pszPServerlist = AllocNwSplStr(pszPServer)) == NULL)
+ {
+ err = ERROR_NOT_ENOUGH_MEMORY;
+ return err;
+ }
+
+ RtlInitUnicodeString( &UQueueName, pszQueue);
+ ntstatus = RtlUnicodeStringToOemString( &OemQueueName, &UQueueName, TRUE);
+
+ if (! NT_SUCCESS(ntstatus))
+ {
+ goto Exit;
+ }
+
+ while( (pszNextPServer = GetNextElement(&pszPServerlist, L';')) != NULL )
+ {
+ RtlInitUnicodeString( &UNextPServer, pszNextPServer);
+ ntstatus = RtlUnicodeStringToOemString( &OemNextPServer, &UNextPServer, TRUE);
+
+
+ if ( !NT_SUCCESS(ntstatus))
+ {
+ RtlFreeOemString(&OemNextPServer);
+ goto Exit;
+ }
+ //NwlibMakeNcp should associate a print server with a printer
+
+ // Add Bindery Object to Set
+
+ ntstatus = NwlibMakeNcp (
+ hServer,
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 122,
+ 2,
+ "bwppwp|",
+ 0x41,
+ 0x0003,
+ OemQueueName.Buffer,
+ "Q_SERVERS",
+ 0x0007, // Object of type Print Server
+ OemNextPServer.Buffer
+ );
+
+ RtlFreeOemString(&OemNextPServer);
+ if (!( NT_SUCCESS(ntstatus)))
+ {
+ RtlFreeOemString(&OemNextPServer);
+ goto Exit;
+
+ }
+ }
+ RtlFreeOemString(&OemQueueName);
+
+Exit:
+
+ return NwMapBinderyCompletionCode(ntstatus);
+
+}
+
+
+DWORD
+ NwDestroyQueue (HANDLE hServer,
+ DWORD dwQueueId)
+
+/*+++
+Routine Description:
+
+ Makes the Ncp call to destroy the queue given by dwQueueId
+
+
+Arguments:
+
+ dwQueueId : Id of the queue you are creating.
+
+Return Value:
+
+ An error condition as it arises.
+ 0x0 is returned if there is no error
+
+---*/
+
+{
+
+ NTSTATUS ntstatus;
+
+ ntstatus = NwlibMakeNcp(
+ hServer,
+ FSCTL_NWR_NCP_E3H,
+ 7,
+ 2,
+ "bd|",
+ 0x65,
+ dwQueueId
+ );
+
+#if DBG
+ if ( NT_SUCCESS(ntstatus)) {
+ IF_DEBUG(PRINT) {
+ KdPrint(("NWWORKSTATION: Queue successfully destroyed\n"));
+ }
+ }
+#endif
+
+ return NwMapBinderyCompletionCode(ntstatus);
+
+}
+
+#endif // #ifndef NOT_USED
diff --git a/private/nw/svcdlls/nwwks/server/queue.h b/private/nw/svcdlls/nwwks/server/queue.h
new file mode 100644
index 000000000..58f5561ed
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/server/queue.h
@@ -0,0 +1,185 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ queue.h
+
+Abstract:
+
+ Header file included by the print provider
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 15-May-1993
+
+Environment:
+
+ User Mode -Win32
+
+Revision History:
+
+--*/
+
+#ifndef _NW_QUEUE_INCLUDED_
+#define _NW_QUEUE_INCLUDED_
+
+
+//
+// Functions from queue.c
+//
+
+typedef BYTE JOBTIME[6];
+
+DWORD
+NwGetQueueId(
+ IN HANDLE handleServer,
+ IN LPWSTR QueueName,
+ OUT LPDWORD QueueId
+ );
+
+DWORD
+NwCreateQueueJobAndFile(
+ IN HANDLE handleServer,
+ IN DWORD QueueId,
+ IN LPWSTR DocumentName,
+ IN LPWSTR UserName,
+ IN DWORD fGateway,
+ IN LPWSTR QueueName,
+ OUT LPWORD JobId
+ );
+
+DWORD
+NwCloseFileAndStartQueueJob(
+ IN HANDLE handleServer,
+ IN DWORD QueueId,
+ IN WORD JobId
+ );
+
+DWORD
+NwRemoveJobFromQueue(
+ IN HANDLE handleServer,
+ IN DWORD QueueId,
+ IN WORD JobId
+ );
+
+DWORD
+NwRemoveAllJobsFromQueue(
+ IN HANDLE handleServer,
+ IN DWORD QueueId
+ );
+
+DWORD
+NwReadQueueCurrentStatus(
+ IN HANDLE handleServer,
+ IN DWORD QueueId,
+ OUT LPBYTE QueueStatus,
+ OUT LPBYTE NumberOfJobs
+ );
+
+DWORD
+NwSetQueueCurrentStatus(
+ IN HANDLE handleServer,
+ IN DWORD QueueId,
+ IN BYTE QueueStatus
+ );
+
+DWORD
+NwGetQueueJobList(
+ IN HANDLE handleServer,
+ IN DWORD QueueId,
+ OUT LPWORD NumberOfJobs,
+ OUT LPWORD JobIdList
+ );
+
+DWORD
+NwReadQueueJobEntry(
+ IN HANDLE handleServer,
+ IN DWORD QueueId,
+ IN WORD JobId,
+ OUT JOBTIME TargetExecutionTime,
+ OUT JOBTIME JobEntryTime,
+ OUT LPBYTE JobPosition,
+ OUT LPBYTE JobControlFlags,
+ OUT LPSTR TextJobDescription,
+ OUT LPSTR UserName
+ );
+
+DWORD
+NwGetQueueJobsFileSize(
+ IN HANDLE handleServer,
+ IN DWORD QueueId,
+ IN WORD JobId,
+ OUT LPDWORD FileSize
+ );
+
+DWORD
+NwChangeQueueJobPosition(
+ IN HANDLE handleServer,
+ IN DWORD QueueId,
+ IN WORD JobId,
+ IN BYTE NewPosition
+ );
+
+DWORD
+NwChangeQueueJobEntry(
+ IN HANDLE handleServer,
+ IN DWORD QueueId,
+ IN WORD JobId,
+ IN DWORD dwCommand,
+ IN PNW_JOB_INFO pNwJobInfo
+ );
+
+DWORD
+NwGetQueueJobs(
+ IN HANDLE handleServer,
+ IN DWORD QueueId,
+ IN LPWSTR PrinterName,
+ IN DWORD FirstJobRequested,
+ IN DWORD EntriesRequested,
+ IN DWORD Level,
+ OUT LPBYTE Buffer,
+ IN DWORD cbBuf,
+ OUT LPDWORD BytesNeeded,
+ OUT LPDWORD Entries
+ );
+
+DWORD
+NwGetQueueJobInfo(
+ IN HANDLE handleServer,
+ IN DWORD QueueId,
+ IN WORD JobId,
+ IN LPWSTR PrinterName,
+ IN DWORD Level,
+ IN OUT LPBYTE *FixedPortion,
+ IN OUT LPWSTR *EndOfVariableData,
+ OUT LPDWORD EntrySize
+ );
+
+#ifndef NOT_USED
+
+DWORD
+NwDestroyQueue (
+ IN HANDLE hServer,
+ IN DWORD dwQueueId
+);
+
+DWORD
+NwAssocPServers (
+ IN HANDLE hServer,
+ IN LPWSTR pswQueue,
+ IN LPWSTR pszPServer
+ );
+
+
+DWORD
+NwCreateQueue (
+ IN HANDLE hServer,
+ IN LPWSTR pszQueue,
+ OUT LPDWORD pQueueId
+ );
+
+#endif // #ifndef NOT_USED
+
+#endif // _NW_QUEUE_INCLUDED_
diff --git a/private/nw/svcdlls/nwwks/server/service.c b/private/nw/svcdlls/nwwks/server/service.c
new file mode 100644
index 000000000..e6859020f
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/server/service.c
@@ -0,0 +1,148 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ getaddr.c
+
+Abstract:
+
+ This module contains the code to support NPGetAddressByName.
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 18-Apr-94
+ Glenn A. Curtis (glennc) 18-Jul-95
+
+Revision History:
+
+ yihsins Created
+ glennc Modified 18-Jul-95
+
+--*/
+
+#ifndef QFE_BUILD
+
+#include <nw.h>
+#include <winsock.h>
+#include <wsipx.h>
+#include <nspapi.h>
+#include <nspapip.h>
+#include <wsnwlink.h>
+#include <svcguid.h>
+#include <nwsap.h>
+#include <align.h>
+#include <nwmisc.h>
+
+#define WSOCK_VER_REQD 0x0101
+
+DWORD
+NwrGetService(
+ IN LPWSTR Reserved,
+ IN WORD nSapType,
+ IN LPWSTR lpServiceName,
+ IN DWORD dwProperties,
+ OUT LPBYTE lpServiceInfo,
+ IN DWORD dwBufferLength,
+ OUT LPDWORD lpdwBytesNeeded
+ )
+/*++
+
+Routine Description:
+
+ This routine calls NwGetService to, in turn, get the service info.
+
+Arguments:
+
+ Reserved - unused
+
+ nSapType - SAP type
+
+ lpServiceName - service name
+
+ dwProperties - specifys the properties of the service info needed
+
+ lpServiceInfo - on output, contains the SERVICE_INFO
+
+ dwBufferLength - size of buffer pointed by lpServiceInfo
+
+ lpdwBytesNeeded - if the buffer pointed by lpServiceInfo is not large
+ enough, this will contain the bytes needed on output
+
+Return Value:
+
+ Win32 error.
+
+--*/
+{
+ return NwGetService( Reserved,
+ nSapType,
+ lpServiceName,
+ dwProperties,
+ lpServiceInfo,
+ dwBufferLength,
+ lpdwBytesNeeded );
+}
+
+DWORD
+NwrSetService(
+ IN LPWSTR Reserved,
+ IN DWORD dwOperation,
+ IN LPSERVICE_INFO lpServiceInfo,
+ IN WORD nSapType
+ )
+/*++
+
+Routine Description:
+
+ This routine registers or deregisters the service info.
+
+Arguments:
+
+ Reserved - unused
+
+ dwOperation - SERVICE_REGISTER or SERVICE_DEREGISTER
+
+ lpServiceInfo - contains the service information
+
+ nSapType - SAP type
+
+Return Value:
+
+ Win32 error.
+
+--*/
+{
+ DWORD err = NO_ERROR;
+
+ UNREFERENCED_PARAMETER( Reserved );
+
+ //
+ // Check if all parameters passed in are valid
+ //
+
+ if ( wcslen( lpServiceInfo->lpServiceName ) > SAP_OBJECT_NAME_MAX_LENGTH-1 )
+ {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ switch ( dwOperation )
+ {
+ case SERVICE_REGISTER:
+ err = NwRegisterService( lpServiceInfo, nSapType, NwDoneEvent );
+ break;
+
+ case SERVICE_DEREGISTER:
+ err = NwDeregisterService( lpServiceInfo, nSapType );
+ break;
+
+ default:
+ err = ERROR_INVALID_PARAMETER;
+ break;
+ }
+
+ return err;
+}
+
+#endif
diff --git a/private/nw/svcdlls/nwwks/server/sources b/private/nw/svcdlls/nwwks/server/sources
new file mode 100644
index 000000000..e85c0fd3e
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/server/sources
@@ -0,0 +1,80 @@
+!IF 0
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ sources.
+
+Abstract:
+
+ This file specifies the target component being built and the list of
+ sources files needed to build that component. Also specifies optional
+ compiler switches and libraries that are unique for the component being
+ built.
+
+
+Author:
+
+Revision History:
+
+!ENDIF
+
+MAJORCOMP=nw
+MINORCOMP=svcdlls
+
+TARGETNAME=nwwks
+TARGETPATH=\nt\public\sdk\lib
+TARGETTYPE=DYNLINK
+DLLBASE = 0x69800000
+
+!IF "$(QFE_BUILD)" != "1"
+NET_C_DEFINES= -DINCL_32= -DNT -DRPC_NO_WINDOWS_H
+!ELSE
+NET_C_DEFINES= -DINCL_32= -DNT -DRPC_NO_WINDOWS_H -DQFE_BUILD=1
+!ENDIF
+
+UNICODE=1
+NET_C_DEFINES=-DUNICODE -DNOT_USED
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+TARGETLIBS= \
+ \nt\public\sdk\lib\*\kernel32.lib \
+ \nt\public\sdk\lib\*\rpcrt4.lib \
+ \nt\public\sdk\lib\*\advapi32.lib \
+ \nt\public\sdk\lib\*\user32.lib \
+ \nt\public\sdk\lib\*\lsadll.lib \
+ \nt\public\sdk\lib\*\nwsaplib.lib \
+ \nt\public\sdk\lib\*\netapi32.lib \
+ \nt\public\sdk\lib\*\nwprovau.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\nwapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\ws2_32.lib \
+ \nt\public\sdk\lib\*\rpcutil.lib \
+ \nt\public\sdk\lib\*\netlib.lib\
+ ..\lib\obj\*\nwwlib.lib
+
+INCLUDES=.;..\inc;..\..\..\inc;$(_NTROOT)\private\inc;$(_NTROOT)\private\net\inc
+
+SOURCES= \
+ service.c \
+ nwwks.rc \
+ nwmain.c \
+ address.c \
+ util.c \
+ device.c \
+ connect.c \
+ enum.c \
+ credentl.c \
+ queue.c \
+ spool.c \
+ gateway.c \
+ nwwks_s.c
+
+UMTYPE=console
+#UMTEST=
+
+
+USE_CRTDLL=1
+
+
diff --git a/private/nw/svcdlls/nwwks/server/spool.c b/private/nw/svcdlls/nwwks/server/spool.c
new file mode 100644
index 000000000..ab3ac4069
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/server/spool.c
@@ -0,0 +1,2289 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ spool.c
+
+Abstract:
+
+ This module contains the Netware print provider.
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 15-May-1993
+
+Revision History:
+
+--*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <nw.h>
+#include <nwreg.h>
+#include <nwpkstr.h>
+#include <splutil.h>
+#include <queue.h>
+#include <nwmisc.h>
+
+//------------------------------------------------------------------
+//
+// Local Definitions
+//
+//------------------------------------------------------------------
+
+#define NW_SIGNATURE 0x574E /* "NW" is the signature */
+
+#define SPOOL_STATUS_STARTDOC 0x00000001
+#define SPOOL_STATUS_ADDJOB 0x00000002
+#define SPOOL_STATUS_ABORT 0x00000003
+
+#define PRINTER_CHANGE_VALID 0x55770F07
+#define PRINTER_CHANGE_DEFAULT_TIMEOUT_VALUE 10000
+#define PRINTER_CHANGE_MINIMUM_TIMEOUT_VALUE 1000
+#define REG_TIMEOUT_PATH L"System\\CurrentControlSet\\Services\\NWCWorkstation\\Parameters"
+#define REG_TIMEOUT_VALUE_NAME L"PrintNotifyTimeout"
+
+#define NDS_MAX_NAME_CHARS 256
+#define NDS_MAX_NAME_SIZE ( NDS_MAX_NAME_CHARS * 2 )
+
+//
+// Printer structure
+//
+typedef struct _NWPRINTER {
+ LPWSTR pszServer; // Server Name
+ LPWSTR pszQueue; // Queue Name
+ LPWSTR pszUncConnection; // UNC Connection Name
+ // (only present if NDS print queue
+ DWORD nQueueId; // Queue Id
+ struct _NWPRINTER *pNextPrinter; // Points to the next printer
+ struct _NWSPOOL *pSpoolList; // Points to the list of open handles
+} NWPRINTER, *PNWPRINTER;
+
+//
+// Handle structure
+//
+typedef struct _NWSPOOL {
+ DWORD nSignature; // Signature
+ DWORD errOpenPrinter; // OpenPrinter API will always return
+ // success on known printers. This will
+ // contain the error that we get
+ // if something went wrong in the API.
+ PNWPRINTER pPrinter; // Points to the corresponding printer
+ HANDLE hServer; // Opened handle to the server
+ struct _NWSPOOL *pNextSpool; // Points to the next handle
+ DWORD nStatus; // Status
+ DWORD nJobNumber; // StartDocPrinter/AddJob: Job Number
+ HANDLE hChangeEvent; // WaitForPrinterChange: event to wait on
+ DWORD nWaitFlags; // WaitForPrinterChange: flags to wait on
+ DWORD nChangeFlags; // Changes that occurred to the printer
+} NWSPOOL, *PNWSPOOL;
+
+//------------------------------------------------------------------
+//
+// Global Variables
+//
+//------------------------------------------------------------------
+
+
+// Stores the timeout value used in WaitForPrinterChange ( in milliseconds )
+STATIC DWORD NwTimeOutValue = PRINTER_CHANGE_DEFAULT_TIMEOUT_VALUE;
+
+// Points to the link list of printers
+STATIC PNWPRINTER NwPrinterList = NULL;
+
+//------------------------------------------------------------------
+//
+// Local Function Prototypes
+//
+//------------------------------------------------------------------
+
+VOID
+NwSetPrinterChange(
+ IN PNWSPOOL pSpool,
+ IN DWORD nFlags
+);
+
+PNWPRINTER
+NwFindPrinterEntry(
+ IN LPWSTR pszServer,
+ IN LPWSTR pszQueue
+);
+
+DWORD
+NwCreatePrinterEntry(
+ IN LPWSTR pszServer,
+ IN LPWSTR pszQueue,
+ OUT PNWPRINTER *ppPrinter,
+ OUT PHANDLE phServer
+);
+
+VOID
+NwRemovePrinterEntry(
+ IN PNWPRINTER pPrinter
+);
+
+DWORD
+NwGetUncObjectName(
+ IN LPWSTR ContainerName
+);
+
+
+
+VOID
+NwInitializePrintProvider(
+ VOID
+)
+/*++
+
+Routine Description:
+
+ This routine initializes the server side print provider when
+ the workstation service starts up.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+--*/
+{
+ DWORD err;
+ HKEY hkey;
+ DWORD dwTemp;
+ DWORD dwSize = sizeof( dwTemp );
+
+ //
+ // Read the time out value from the registry.
+ // We will ignore all errors since we can always have a default time out.
+ // The default will be used if the key does not exist.
+ //
+ err = RegOpenKeyExW( HKEY_LOCAL_MACHINE,
+ REG_TIMEOUT_PATH,
+ 0,
+ KEY_READ,
+ &hkey );
+
+ if ( !err )
+ {
+ err = RegQueryValueExW( hkey,
+ REG_TIMEOUT_VALUE_NAME,
+ NULL,
+ NULL,
+ (LPBYTE) &dwTemp,
+ &dwSize );
+
+ if ( !err )
+ {
+ NwTimeOutValue = dwTemp;
+
+ // Use the minimum timeout value if the
+ // value set in the registry is too small.
+ if ( NwTimeOutValue >= 0
+ && NwTimeOutValue <= PRINTER_CHANGE_MINIMUM_TIMEOUT_VALUE
+ )
+ {
+ NwTimeOutValue = PRINTER_CHANGE_MINIMUM_TIMEOUT_VALUE;
+ }
+ }
+
+ RegCloseKey( hkey );
+ }
+
+}
+
+
+
+VOID
+NwTerminatePrintProvider(
+ VOID
+)
+/*++
+
+Routine Description:
+
+ This routine cleans up the server side print provider when
+ the workstation service shut downs.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+--*/
+{
+ PNWPRINTER pPrinter, pNext;
+ PNWSPOOL pSpool, pNextSpool;
+
+ for ( pPrinter = NwPrinterList; pPrinter; pPrinter = pNext )
+ {
+ pNext = pPrinter->pNextPrinter;
+
+ pPrinter->pNextPrinter = NULL;
+
+ for ( pSpool = pPrinter->pSpoolList; pSpool; pSpool = pNextSpool )
+ {
+ pNextSpool = pSpool->pNextSpool;
+ if ( pSpool->hChangeEvent )
+ CloseHandle( pSpool->hChangeEvent );
+ (VOID) NtClose( pSpool->hServer );
+
+ //
+ // Free all memory associated with the context handle
+ //
+ FreeNwSplMem( pSpool, sizeof( NWSPOOL) );
+ }
+
+ pPrinter->pSpoolList = NULL;
+ FreeNwSplStr( pPrinter->pszServer );
+ FreeNwSplStr( pPrinter->pszQueue );
+ if ( pPrinter->pszUncConnection )
+ {
+ (void) NwrDeleteConnection( NULL,
+ pPrinter->pszUncConnection,
+ FALSE );
+ FreeNwSplStr( pPrinter->pszUncConnection );
+ }
+ FreeNwSplMem( pPrinter, sizeof( NWPRINTER));
+ }
+
+}
+
+
+
+DWORD
+NwrOpenPrinter(
+ IN LPWSTR Reserved,
+ IN LPWSTR pszPrinterName,
+ IN DWORD fKnownPrinter,
+ OUT LPNWWKSTA_PRINTER_CONTEXT phPrinter
+)
+/*++
+
+Routine Description:
+
+ This routine retrieves a handle identifying the specified printer.
+
+Arguments:
+
+ Reserved - Unused
+ pszPrinterName - Name of the printer
+ fKnownPrinter - TRUE if we have successfully opened the printer before,
+ FALSE otherwise.
+ phPrinter - Receives the handle that identifies the given printer
+
+Return Value:
+
+
+--*/
+{
+ DWORD err;
+ PNWSPOOL pSpool = NULL;
+ LPWSTR pszServer = NULL;
+ LPWSTR pszQueue = NULL;
+ PNWPRINTER pPrinter = NULL;
+ BOOL fImpersonate = FALSE ;
+ HANDLE hServer;
+
+ UNREFERENCED_PARAMETER( Reserved );
+
+ if ( (pszServer = AllocNwSplStr( pszPrinterName )) == NULL )
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ CharUpperW( pszServer ); // convert in place
+
+ //
+ // ValidatePrinterName
+ //
+ if ( ( !ValidateUNCName( pszPrinterName ) )
+ || ( (pszQueue = wcschr( pszServer + 2, L'\\')) == NULL )
+ || ( pszQueue == (pszServer + 2) )
+ || ( *(pszQueue + 1) == L'\0' )
+ )
+ {
+ FreeNwSplStr( pszServer );
+ return ERROR_INVALID_NAME;
+ }
+
+ *pszQueue = L'\0'; // put a '\0' in place of '\\'
+ pszQueue++; // Get past the '\0'
+
+ if ( !(pSpool = AllocNwSplMem( LMEM_ZEROINIT, sizeof( NWSPOOL))))
+ {
+ err = ERROR_NOT_ENOUGH_MEMORY;
+ goto ErrorExit;
+ }
+
+ //
+ // Impersonate the client
+ //
+ if ((err = NwImpersonateClient()) != NO_ERROR)
+ {
+ goto ErrorExit;
+ }
+ fImpersonate = TRUE ;
+
+ EnterCriticalSection( &NwPrintCritSec );
+
+ if ((err = NwCreatePrinterEntry( pszServer, pszQueue, &pPrinter, &hServer)))
+ {
+ if ( !fKnownPrinter )
+ {
+ LeaveCriticalSection( &NwPrintCritSec );
+ goto ErrorExit;
+ }
+ }
+
+ //
+ // Construct the print queue context handle to give back to the caller
+ //
+ pSpool->nSignature = NW_SIGNATURE;
+ pSpool->errOpenPrinter = err;
+
+ pSpool->hServer = hServer;
+ pSpool->nStatus = 0;
+ pSpool->nJobNumber = 0;
+ pSpool->hChangeEvent= NULL;
+ pSpool->nWaitFlags = 0;
+ pSpool->nChangeFlags= 0;
+
+ if ( !err )
+ {
+ pSpool->pPrinter = pPrinter;
+ pSpool->pNextSpool = pPrinter->pSpoolList;
+ pPrinter->pSpoolList= pSpool;
+ }
+ else
+ {
+ pSpool->pPrinter = NULL;
+ pSpool->pNextSpool = NULL;
+ }
+
+ // We know about this printer before but failed to retrieve
+ // it this time. Clean up the error and return successfully.
+ // The error code is stored in the handle above which
+ // will be returned on subsequent calls using this
+ // dummy handle.
+ err = NO_ERROR;
+
+ LeaveCriticalSection( &NwPrintCritSec );
+
+ErrorExit:
+
+ if (fImpersonate)
+ (void) NwRevertToSelf() ;
+
+ if ( err )
+ {
+ if ( pSpool )
+ FreeNwSplMem( pSpool, sizeof( NWSPOOL) );
+ }
+ else
+ {
+ *phPrinter = (NWWKSTA_PRINTER_CONTEXT) pSpool;
+ }
+
+ //
+ // Free up all allocated memories
+ //
+ *(pszServer + wcslen( pszServer)) = L'\\';
+ FreeNwSplStr( pszServer );
+
+ return err;
+
+}
+
+
+
+DWORD
+NwrClosePrinter(
+ IN OUT LPNWWKSTA_PRINTER_CONTEXT phPrinter
+)
+/*++
+
+Routine Description:
+
+ This routine closes the given printer object.
+
+Arguments:
+
+ phPrinter - Handle of the printer object
+
+Return Value:
+
+--*/
+{
+ PNWSPOOL pSpool = (PNWSPOOL) *phPrinter;
+ PNWPRINTER pPrinter;
+ PNWSPOOL pCur, pPrev = NULL;
+
+
+ if ( !pSpool || ( pSpool->nSignature != NW_SIGNATURE ))
+ return ERROR_INVALID_HANDLE;
+
+ //
+ // If OpenPrinter failed, then this is a dummy handle.
+ // We just need to free up the memory.
+ //
+ if ( pSpool->errOpenPrinter )
+ {
+ //
+ // invalidate the signature, but leave a recognizable value
+ //
+ pSpool->nSignature += 1 ;
+ FreeNwSplMem( pSpool, sizeof( NWSPOOL) );
+ *phPrinter = NULL;
+ return NO_ERROR;
+ }
+
+ pPrinter = pSpool->pPrinter;
+ ASSERT( pPrinter );
+
+ //
+ // Call EndDocPrinter if the user has not already done so
+ //
+ if ( pSpool->nStatus == SPOOL_STATUS_STARTDOC )
+ {
+ (void) NwrEndDocPrinter( *phPrinter );
+ }
+ else if ( pSpool->nStatus == SPOOL_STATUS_ADDJOB )
+ {
+ (void) NwrScheduleJob( *phPrinter, pSpool->nJobNumber );
+ }
+
+ if ( pSpool->hChangeEvent )
+ CloseHandle( pSpool->hChangeEvent );
+
+ pSpool->hChangeEvent = NULL;
+ pSpool->nChangeFlags = 0;
+ (VOID) NtClose( pSpool->hServer );
+
+
+ EnterCriticalSection( &NwPrintCritSec );
+
+ for ( pCur = pPrinter->pSpoolList; pCur;
+ pPrev = pCur, pCur = pCur->pNextSpool )
+ {
+ if ( pCur == pSpool )
+ {
+ if ( pPrev )
+ pPrev->pNextSpool = pCur->pNextSpool;
+ else
+ pPrinter->pSpoolList = pCur->pNextSpool;
+ break;
+ }
+
+ }
+
+ ASSERT( pCur );
+
+ if ( pPrinter->pSpoolList == NULL )
+ {
+#if DBG
+ IF_DEBUG(PRINT)
+ {
+ KdPrint(("*************DELETED PRINTER ENTRY: %ws\\%ws\n\n",
+ pPrinter->pszServer, pPrinter->pszQueue ));
+ }
+#endif
+
+ NwRemovePrinterEntry( pPrinter );
+ }
+
+ LeaveCriticalSection( &NwPrintCritSec );
+
+ //
+ // invalidate the signature, but leave a recognizable value
+ //
+ pSpool->nSignature += 1 ;
+
+ pSpool->pNextSpool = NULL;
+ pSpool->pPrinter = NULL;
+
+ //
+ // Free all memory associated with the context handle
+ //
+ FreeNwSplMem( pSpool, sizeof( NWSPOOL) );
+
+ //
+ // indicate to RPC we are done
+ //
+ *phPrinter = NULL;
+
+ return NO_ERROR;
+}
+
+
+
+DWORD
+NwrGetPrinter(
+ IN NWWKSTA_PRINTER_CONTEXT hPrinter,
+ IN DWORD dwLevel,
+ IN OUT LPBYTE pbPrinter,
+ IN DWORD cbBuf,
+ OUT LPDWORD pcbNeeded
+)
+/*++
+
+Routine Description:
+
+ The routine retrieves information about the given printer.
+
+Arguments:
+
+ hPrinter - Handle of the printer
+ dwLevel - Specifies the level of the structure to which pbPrinter points.
+ pbPrinter - Points to a buffer that receives the PRINTER_INFO object.
+ cbBuf - Size, in bytes of the array pbPrinter points to.
+ pcbNeeded - Points to a value which specifies the number of bytes copied
+ if the function succeeds or the number of bytes required if
+ cbBuf was too small.
+
+Return Value:
+
+--*/
+{
+ PNWSPOOL pSpool = (PNWSPOOL) hPrinter;
+ PNWPRINTER pPrinter;
+
+ LPBYTE pbEnd = pbPrinter + cbBuf;
+ BOOL fFitInBuffer;
+ DWORD *pOffsets;
+
+ if ( !pSpool || pSpool->nSignature != NW_SIGNATURE )
+ {
+ return ERROR_INVALID_HANDLE;
+ }
+ else if ( pSpool->errOpenPrinter )
+ {
+ return pSpool->errOpenPrinter;
+ }
+ else if ( ( dwLevel != 1 ) && ( dwLevel != 2 ) && ( dwLevel != 3 ))
+ {
+ return ERROR_INVALID_LEVEL;
+ }
+
+ pPrinter = pSpool->pPrinter;
+ ASSERT( pPrinter );
+
+ if ( dwLevel == 1 )
+ {
+ PRINTER_INFO_1W *pPrinterInfo1 = (PRINTER_INFO_1W *) pbPrinter;
+ LPBYTE pbFixedEnd = pbPrinter + sizeof( PRINTER_INFO_1W );
+
+ //
+ // Calculate size needed
+ //
+ *pcbNeeded = sizeof( PRINTER_INFO_1W ) +
+ ( wcslen( pPrinter->pszServer )
+ + wcslen( pPrinter->pszQueue ) + 2 ) * sizeof( WCHAR );
+
+ if ( cbBuf < *pcbNeeded )
+ return ERROR_INSUFFICIENT_BUFFER;
+
+ pOffsets = PrinterInfo1Offsets;
+
+ //
+ // Fill in the structure
+ //
+ pPrinterInfo1->Flags = PRINTER_ENUM_REMOTE | PRINTER_ENUM_NAME;
+ pPrinterInfo1->pComment = NULL;
+
+ fFitInBuffer = NwlibCopyStringToBuffer(
+ pPrinter->pszServer,
+ wcslen( pPrinter->pszServer ),
+ (LPWSTR) pbFixedEnd,
+ (LPWSTR *) &pbEnd,
+ &pPrinterInfo1->pDescription );
+
+ ASSERT( fFitInBuffer );
+
+ fFitInBuffer = NwlibCopyStringToBuffer(
+ pPrinter->pszQueue,
+ wcslen( pPrinter->pszQueue ),
+ (LPWSTR) pbFixedEnd,
+ (LPWSTR *) &pbEnd,
+ &pPrinterInfo1->pName );
+
+ ASSERT( fFitInBuffer );
+
+ }
+ else if ( dwLevel == 2 )
+ {
+ DWORD err;
+ BYTE nQueueStatus;
+ BYTE nNumJobs;
+ PRINTER_INFO_2W *pPrinterInfo2 = (PRINTER_INFO_2W *) pbPrinter;
+ LPBYTE pbFixedEnd = pbPrinter + sizeof( PRINTER_INFO_2W );
+
+ //
+ // Check if the buffer is big enough to hold all the data
+ //
+
+ *pcbNeeded = sizeof( PRINTER_INFO_2W ) +
+ ( 2*wcslen( pPrinter->pszServer ) +
+ 2*wcslen( pPrinter->pszQueue ) + 4 ) * sizeof( WCHAR );
+
+ if ( cbBuf < *pcbNeeded )
+ return ERROR_INSUFFICIENT_BUFFER;
+
+ pOffsets = PrinterInfo2Offsets;
+
+ err = NwReadQueueCurrentStatus( pSpool->hServer,
+ pPrinter->nQueueId,
+ &nQueueStatus,
+ &nNumJobs );
+
+ if ( err )
+ return err;
+
+ pPrinterInfo2->Status = (nQueueStatus & 0x05)? PRINTER_STATUS_PAUSED
+ : 0;
+ pPrinterInfo2->cJobs = nNumJobs;
+
+ fFitInBuffer = NwlibCopyStringToBuffer(
+ pPrinter->pszServer,
+ wcslen( pPrinter->pszServer ),
+ (LPCWSTR) pbFixedEnd,
+ (LPWSTR *) &pbEnd,
+ &pPrinterInfo2->pServerName );
+
+ ASSERT( fFitInBuffer );
+
+ pbEnd -= ( wcslen( pPrinter->pszQueue) + 1 ) * sizeof( WCHAR );
+ wcscpy( (LPWSTR) pbEnd, pPrinter->pszQueue );
+ pbEnd -= ( wcslen( pPrinter->pszServer) + 1 ) * sizeof( WCHAR );
+ wcscpy( (LPWSTR) pbEnd, pPrinter->pszServer );
+ *(pbEnd + wcslen( pPrinter->pszServer )*sizeof(WCHAR))= L'\\';
+ pPrinterInfo2->pPrinterName = (LPWSTR) pbEnd;
+
+ fFitInBuffer = NwlibCopyStringToBuffer(
+ pPrinter->pszQueue,
+ wcslen( pPrinter->pszQueue ),
+ (LPCWSTR) pbFixedEnd,
+ (LPWSTR *) &pbEnd,
+ &pPrinterInfo2->pShareName );
+
+ ASSERT( fFitInBuffer );
+
+ pPrinterInfo2->pPortName = NULL;
+ pPrinterInfo2->pDriverName = NULL;
+ pPrinterInfo2->pComment = NULL;
+ pPrinterInfo2->pLocation = NULL;
+ pPrinterInfo2->pDevMode = NULL;
+ pPrinterInfo2->pSepFile = NULL;
+ pPrinterInfo2->pPrintProcessor = NULL;
+ pPrinterInfo2->pDatatype = NULL;
+ pPrinterInfo2->pParameters = NULL;
+ pPrinterInfo2->pSecurityDescriptor = NULL;
+ pPrinterInfo2->Attributes = PRINTER_ATTRIBUTE_QUEUED;
+ pPrinterInfo2->Priority = 0;
+ pPrinterInfo2->DefaultPriority = 0;
+ pPrinterInfo2->StartTime = 0;
+ pPrinterInfo2->UntilTime = 0;
+ pPrinterInfo2->AveragePPM = 0;
+ }
+ else // Level == 3
+ {
+ PRINTER_INFO_3 *pPrinterInfo3 = (PRINTER_INFO_3 *) pbPrinter;
+
+ *pcbNeeded = sizeof( PRINTER_INFO_3 );
+
+ if ( cbBuf < *pcbNeeded )
+ return ERROR_INSUFFICIENT_BUFFER;
+
+ pOffsets = PrinterInfo3Offsets;
+ pPrinterInfo3->pSecurityDescriptor = NULL;
+ }
+
+ MarshallDownStructure( pbPrinter, pOffsets, pbPrinter );
+ return NO_ERROR;
+}
+
+
+
+DWORD
+NwrSetPrinter(
+ IN NWWKSTA_PRINTER_CONTEXT hPrinter,
+ IN DWORD dwCommand
+)
+/*++
+
+Routine Description:
+
+ The routine sets information about the given printer.
+
+Arguments:
+
+ hPrinter - Handle of the printer
+ dwCommand - Specifies the new printer state
+
+Return Value:
+
+--*/
+{
+ PNWSPOOL pSpool = (PNWSPOOL) hPrinter;
+ DWORD err = NO_ERROR;
+ PNWPRINTER pPrinter;
+
+ if ( !pSpool || pSpool->nSignature != NW_SIGNATURE )
+ {
+ return ERROR_INVALID_HANDLE;
+ }
+ else if ( pSpool->errOpenPrinter )
+ {
+ return pSpool->errOpenPrinter;
+ }
+
+ pPrinter = pSpool->pPrinter;
+ ASSERT( pPrinter );
+
+ switch ( dwCommand )
+ {
+ case PRINTER_CONTROL_PAUSE:
+ case PRINTER_CONTROL_RESUME:
+ {
+ BYTE nQueueStatus = 0;
+ BYTE nNumJobs;
+
+ //
+ // Get the original queue status so that we don't overwrite
+ // some of the bits.
+ //
+ err = NwReadQueueCurrentStatus( pSpool->hServer,
+ pPrinter->nQueueId,
+ &nQueueStatus,
+ &nNumJobs );
+
+ if ( !err )
+ {
+ //
+ // Clear the pause bits, and leave the rest alone.
+ //
+ nQueueStatus &= ~0x05;
+ }
+
+ if ( dwCommand == PRINTER_CONTROL_PAUSE )
+ {
+ nQueueStatus |= 0x04;
+ }
+
+ err = NwSetQueueCurrentStatus( pSpool->hServer,
+ pPrinter->nQueueId,
+ nQueueStatus );
+ if ( !err )
+ NwSetPrinterChange( pSpool, PRINTER_CHANGE_SET_PRINTER );
+ break;
+ }
+
+ case PRINTER_CONTROL_PURGE:
+
+ err = NwRemoveAllJobsFromQueue( pSpool->hServer,
+ pPrinter->nQueueId );
+ if ( !err )
+ NwSetPrinterChange( pSpool, PRINTER_CHANGE_SET_PRINTER |
+ PRINTER_CHANGE_DELETE_JOB );
+ break;
+
+ default:
+ //
+ // dwCommand is 0 so that means
+ // some properties of the printer has changed.
+ // We will ignore the properties that
+ // are being modified since most properties
+ // are stored in the registry by spooler.
+ // All we need to do is to signal WaitForPrinterChange to
+ // return so that print manager will refresh its data.
+ //
+
+ ASSERT( dwCommand == 0 );
+ NwSetPrinterChange( pSpool, PRINTER_CHANGE_SET_PRINTER );
+ break;
+ }
+
+ return err;
+}
+
+
+
+DWORD
+NwrEnumPrinters(
+ IN LPWSTR Reserved,
+ IN LPWSTR pszName,
+ IN OUT LPBYTE pbPrinter,
+ IN DWORD cbBuf,
+ OUT LPDWORD pcbNeeded,
+ OUT LPDWORD pcReturned
+)
+/*++
+
+Routine Description:
+
+ This routine enumerates the available providers, servers, printers
+ depending on the given pszName.
+
+Arguments:
+
+ Reserved - Unused
+ pszName - The name of the container object
+ pbPrinter - Points to the array to receive the PRINTER_INFO objects
+ cbBuf - Size, in bytes of pbPrinter
+ pcbNeeded - Count of bytes needed
+ pcReturned - Count of PRINTER_INFO objects
+
+Return Value:
+
+--*/
+{
+ PRINTER_INFO_1W *pPrinterInfo1 = (PRINTER_INFO_1W *) pbPrinter;
+
+ *pcbNeeded = 0;
+ *pcReturned = 0;
+
+ if ( !pszName ) // Enumerate the provider name
+ {
+ BOOL fFitInBuffer;
+ LPBYTE pbFixedEnd = pbPrinter + sizeof( PRINTER_INFO_1W );
+ LPBYTE pbEnd = pbPrinter + cbBuf;
+
+ *pcbNeeded = sizeof( PRINTER_INFO_1W ) +
+ ( 2 * wcslen( NwProviderName ) +
+ + 2) * sizeof(WCHAR);
+
+ if ( *pcbNeeded > cbBuf )
+ return ERROR_INSUFFICIENT_BUFFER;
+
+ pPrinterInfo1->Flags = PRINTER_ENUM_ICON1 |
+ PRINTER_ENUM_CONTAINER |
+ PRINTER_ENUM_EXPAND;
+ pPrinterInfo1->pComment = NULL;
+
+ fFitInBuffer = NwlibCopyStringToBuffer(
+ NwProviderName,
+ wcslen( NwProviderName ),
+ (LPWSTR) pbFixedEnd,
+ (LPWSTR *) &pbEnd,
+ &pPrinterInfo1->pDescription );
+
+ ASSERT( fFitInBuffer );
+
+ fFitInBuffer = NwlibCopyStringToBuffer(
+ NwProviderName,
+ wcslen( NwProviderName ),
+ (LPWSTR) pbFixedEnd,
+ (LPWSTR *) &pbEnd,
+ &pPrinterInfo1->pName );
+
+ ASSERT( fFitInBuffer );
+
+ MarshallDownStructure( pbPrinter, PrinterInfo1Offsets, pbPrinter );
+ *pcReturned = 1;
+ }
+
+ else if ( pszName && *pszName )
+ {
+ DWORD err;
+ WCHAR szFullName[MAX_PATH];
+ LPWSTR pszServer;
+ NWWKSTA_CONTEXT_HANDLE handle;
+
+ wcscpy( szFullName, pszName );
+ pszServer = wcschr( szFullName, L'!');
+
+ if ( pszServer )
+ *pszServer++ = 0;
+
+ if ( lstrcmpiW( szFullName, NwProviderName ) )
+ return ERROR_INVALID_NAME;
+
+ if ( !pszServer ) // Enumerate servers
+ {
+ err = NwOpenEnumPrintServers( &handle );
+
+ if ( err != NO_ERROR )
+ {
+ return err;
+ }
+
+ err = NwrEnum( handle,
+ 0xFFFFFFFF,
+ pbPrinter,
+ cbBuf,
+ pcbNeeded,
+ pcReturned );
+
+ if ( err != NO_ERROR )
+ {
+ NwrCloseEnum( &handle );
+ return err;
+ }
+
+ err = NwrCloseEnum( &handle );
+
+ if ( err != NO_ERROR )
+ {
+ return err;
+ }
+ }
+ else // Enumerate NDS sub-trees or print queues
+ {
+ LPWSTR tempStrPtr = pszServer;
+ DWORD dwClassType = 0;
+
+ if ( tempStrPtr[0] == L'\\' &&
+ tempStrPtr[1] == L'\\' &&
+ tempStrPtr[2] == L' ' )
+ tempStrPtr = &tempStrPtr[1];
+
+ err = NwrOpenEnumNdsSubTrees_Print( NULL, tempStrPtr, &dwClassType, &handle );
+
+ if ( err == ERROR_NETWORK_ACCESS_DENIED && dwClassType == CLASS_TYPE_NCP_SERVER )
+ {
+ // An error code from the above NwOpenEnumNdsSubTrees could have
+ // failed because the object was a server, which cannot be enumerated
+ // with the NDS tree APIs. If so we try to get the print queues with the
+ // regular NW APIs.
+
+ // BUGBUG - Temporary hack to browse past CN=<server> in an NDS tree.
+ tempStrPtr = (LPWSTR) NwGetUncObjectName( tempStrPtr );
+
+ err = NwOpenEnumPrintQueues( tempStrPtr, &handle );
+
+ if ( err != NO_ERROR )
+ {
+ return err;
+ }
+ }
+
+ if ( err != NO_ERROR )
+ {
+ // An error code from the above NwOpenEnumNdsSubTrees could have
+ // failed because the object was not a part of an NDS tree.
+ // So we try to get the print queues with the regular NW APIs.
+
+ err = NwOpenEnumPrintQueues( tempStrPtr, &handle );
+
+ if ( err != NO_ERROR )
+ {
+ return err;
+ }
+ }
+
+ err = NwrEnum( handle,
+ 0xFFFFFFFF,
+ pbPrinter,
+ cbBuf,
+ pcbNeeded,
+ pcReturned );
+
+ if ( err != NO_ERROR )
+ {
+ NwrCloseEnum( &handle );
+ return err;
+ }
+
+ err = NwrCloseEnum( &handle );
+
+ if ( err != NO_ERROR )
+ {
+ return err;
+ }
+ }
+ }
+
+ return NO_ERROR;
+}
+
+
+DWORD
+NwrStartDocPrinter(
+ IN NWWKSTA_PRINTER_CONTEXT hPrinter,
+ IN LPWSTR pszDocument,
+ IN LPWSTR pszUser,
+ IN DWORD fGateway
+)
+/*++
+
+Routine Description:
+
+ This routine informs the print spooler that a document is to be spooled
+ for printing.
+
+Arguments:
+
+ hPrinter - Handle of the printer
+ pszDocument - Name of the document to be printed
+ pszUser - Name of the user submitting the print job
+ fGateway - TRUE if it is gateway printing
+
+Return Value:
+
+--*/
+{
+ DWORD err;
+ PNWSPOOL pSpool = (PNWSPOOL) hPrinter;
+
+ if ( !pSpool || (pSpool->nSignature != NW_SIGNATURE) )
+ {
+ err = ERROR_INVALID_HANDLE;
+ }
+ else if ( pSpool->errOpenPrinter )
+ {
+ err = pSpool->errOpenPrinter;
+ }
+ else if ( pSpool->nStatus != 0 )
+ {
+ err = ERROR_INVALID_PARAMETER;
+ }
+ else
+ {
+ //
+ // Get pSpool->nJobNumber from CreateQueueJobAndFile
+ //
+
+ PNWPRINTER pPrinter = pSpool->pPrinter;
+ WORD nJobNumber;
+
+ ASSERT( pPrinter );
+ err = NwCreateQueueJobAndFile( pSpool->hServer,
+ pPrinter->nQueueId,
+ pszDocument,
+ pszUser,
+ fGateway,
+ pPrinter->pszQueue,
+ &nJobNumber );
+
+ if ( !err )
+ {
+ pSpool->nJobNumber = nJobNumber;
+ pSpool->nStatus = SPOOL_STATUS_STARTDOC;
+ NwSetPrinterChange( pSpool, PRINTER_CHANGE_ADD_JOB |
+ PRINTER_CHANGE_SET_PRINTER );
+ }
+ }
+
+ return err;
+}
+
+
+
+DWORD
+NwrWritePrinter(
+ IN NWWKSTA_PRINTER_CONTEXT hPrinter,
+ IN LPBYTE pBuf,
+ IN DWORD cbBuf,
+ OUT LPDWORD pcbWritten
+)
+/*++
+
+Routine Description:
+
+ This routine informs the print spooler that the specified data should be
+ written to the given printer.
+
+Arguments:
+
+ hPrinter - Handle of the printer object
+ pBuf - Address of array that contains printer data
+ cbBuf - Size, in bytes of pBuf
+ pcbWritten - Receives the number of bytes actually written to the printer
+
+Return Value:
+
+--*/
+{
+ DWORD err = NO_ERROR;
+ PNWSPOOL pSpool = (PNWSPOOL) hPrinter;
+
+ if ( !pSpool || ( pSpool->nSignature != NW_SIGNATURE))
+ {
+ err = ERROR_INVALID_HANDLE;
+ }
+ else if ( pSpool->errOpenPrinter )
+ {
+ err = pSpool->errOpenPrinter;
+ }
+ else if ( pSpool->nStatus != SPOOL_STATUS_STARTDOC )
+ {
+ err = ERROR_INVALID_PARAMETER;
+ }
+ else
+ {
+ NTSTATUS ntstatus;
+ IO_STATUS_BLOCK IoStatusBlock;
+ PNWPRINTER pPrinter = pSpool->pPrinter;
+
+ ASSERT( pPrinter );
+ ntstatus = NtWriteFile( pSpool->hServer,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ pBuf,
+ cbBuf,
+ NULL,
+ NULL );
+
+ if ( NT_SUCCESS(ntstatus))
+ ntstatus = IoStatusBlock.Status;
+
+ if ( NT_SUCCESS(ntstatus) )
+ {
+ *pcbWritten = IoStatusBlock.Information;
+ NwSetPrinterChange( pSpool, PRINTER_CHANGE_WRITE_JOB );
+ }
+ else
+ {
+ KdPrint(("NWWORKSTATION: NtWriteFile failed 0x%08lx\n", ntstatus));
+ *pcbWritten = 0;
+ err = RtlNtStatusToDosError( ntstatus );
+ }
+ }
+
+ return err;
+}
+
+
+
+DWORD
+NwrAbortPrinter(
+ IN NWWKSTA_PRINTER_CONTEXT hPrinter
+)
+/*++
+
+Routine Description:
+
+ This routine deletes a printer's spool file if the printer is configured
+ for spooling.
+
+Arguments:
+
+ hPrinter - Handle of the printer object
+
+Return Value:
+
+--*/
+{
+ DWORD err;
+ PNWSPOOL pSpool = (PNWSPOOL) hPrinter;
+
+ if ( !pSpool || ( pSpool->nSignature != NW_SIGNATURE ))
+ {
+ err = ERROR_INVALID_HANDLE;
+ }
+ else if ( pSpool->errOpenPrinter )
+ {
+ err = pSpool->errOpenPrinter;
+ }
+ else if ( pSpool->nStatus != SPOOL_STATUS_STARTDOC )
+ {
+ err = ERROR_INVALID_PARAMETER;
+ }
+ else
+ {
+ PNWPRINTER pPrinter = pSpool->pPrinter;
+
+ ASSERT( pPrinter );
+ err = NwRemoveJobFromQueue( pSpool->hServer,
+ pPrinter->nQueueId,
+ (WORD) pSpool->nJobNumber );
+
+ if ( !err )
+ {
+ pSpool->nJobNumber = 0;
+ pSpool->nStatus = SPOOL_STATUS_ABORT;
+ NwSetPrinterChange( pSpool, PRINTER_CHANGE_SET_JOB );
+ }
+ }
+
+ return err;
+}
+
+
+
+DWORD
+NwrEndDocPrinter(
+ IN NWWKSTA_PRINTER_CONTEXT hPrinter
+)
+/*++
+
+Routine Description:
+
+ This routine ends the print job for the given printer.
+
+Arguments:
+
+ hPrinter - Handle of the printer object
+
+Return Value:
+
+--*/
+{
+ DWORD err = NO_ERROR;
+ PNWSPOOL pSpool = (PNWSPOOL) hPrinter;
+
+ if ( !pSpool || ( pSpool->nSignature != NW_SIGNATURE ))
+ {
+ err = ERROR_INVALID_HANDLE;
+ }
+ else if ( pSpool->errOpenPrinter )
+ {
+ err = pSpool->errOpenPrinter;
+ }
+ else if ( ( pSpool->nStatus != SPOOL_STATUS_STARTDOC )
+ && ( pSpool->nStatus != SPOOL_STATUS_ABORT )
+ )
+ {
+ err = ERROR_INVALID_PARAMETER;
+ }
+ else
+ {
+ PNWPRINTER pPrinter = pSpool->pPrinter;
+
+ ASSERT( pPrinter );
+
+ if ( pSpool->nStatus == SPOOL_STATUS_STARTDOC )
+ {
+ err = NwCloseFileAndStartQueueJob( pSpool->hServer,
+ pPrinter->nQueueId,
+ (WORD) pSpool->nJobNumber );
+
+ if ( !err )
+ NwSetPrinterChange( pSpool, PRINTER_CHANGE_SET_JOB );
+ }
+
+ if ( !err )
+ {
+ pSpool->nJobNumber = 0;
+ pSpool->nStatus = 0;
+ }
+ }
+
+ return err;
+}
+
+
+
+DWORD
+NwrGetJob(
+ IN NWWKSTA_PRINTER_CONTEXT hPrinter,
+ IN DWORD dwJobId,
+ IN DWORD dwLevel,
+ IN OUT LPBYTE pbJob,
+ IN DWORD cbBuf,
+ OUT LPDWORD pcbNeeded
+)
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ hPrinter - Handle of the printer
+ dwJobId -
+ dwLevel -
+ pbJob -
+ cbBuf -
+ pcbNeeded -
+
+Return Value:
+
+--*/
+{
+ DWORD err;
+ PNWSPOOL pSpool = (PNWSPOOL) hPrinter;
+
+ if ( !pSpool || pSpool->nSignature != NW_SIGNATURE )
+ {
+ err = ERROR_INVALID_HANDLE;
+ }
+ else if ( pSpool->errOpenPrinter )
+ {
+ err = pSpool->errOpenPrinter;
+ }
+ else if (( dwLevel != 1 ) && ( dwLevel != 2 ))
+ {
+ err = ERROR_INVALID_LEVEL;
+ }
+ else
+ {
+ DWORD nPrinterLen;
+ LPWSTR pszPrinter;
+ LPBYTE FixedPortion = pbJob;
+ LPWSTR EndOfVariableData = (LPWSTR) ((DWORD) pbJob + cbBuf);
+ PNWPRINTER pPrinter = pSpool->pPrinter;
+
+ ASSERT( pPrinter );
+
+ pszPrinter = AllocNwSplMem( LMEM_ZEROINIT,
+ nPrinterLen = ( wcslen( pPrinter->pszServer) +
+ wcslen( pPrinter->pszQueue) + 2) * sizeof(WCHAR));
+
+ if ( pszPrinter == NULL )
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ wcscpy( pszPrinter, pPrinter->pszServer );
+ wcscat( pszPrinter, L"\\" );
+ wcscat( pszPrinter, pPrinter->pszQueue );
+
+ *pcbNeeded = 0;
+ err = NwGetQueueJobInfo( pSpool->hServer,
+ pPrinter->nQueueId,
+ (WORD) dwJobId,
+ pszPrinter,
+ dwLevel,
+ &FixedPortion,
+ &EndOfVariableData,
+ pcbNeeded );
+
+ FreeNwSplMem( pszPrinter, nPrinterLen );
+
+ if ( !err )
+ {
+ switch( dwLevel )
+ {
+ case 1:
+ MarshallDownStructure( pbJob, JobInfo1Offsets, pbJob );
+ break;
+
+ case 2:
+ MarshallDownStructure( pbJob, JobInfo2Offsets, pbJob );
+ break;
+
+ default:
+ ASSERT( FALSE );
+ break;
+ }
+ }
+
+ }
+
+ return err;
+}
+
+
+
+DWORD
+NwrEnumJobs(
+ IN NWWKSTA_PRINTER_CONTEXT hPrinter,
+ IN DWORD dwFirstJob,
+ IN DWORD dwNoJobs,
+ IN DWORD dwLevel,
+ IN OUT LPBYTE pbJob,
+ IN DWORD cbBuf,
+ OUT LPDWORD pcbNeeded,
+ OUT LPDWORD pcReturned
+)
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ hPrinter - Handle of the printer
+ dwFirstJob -
+ dwNoJobs -
+ dwLevel -
+ pbJob -
+ cbBuf -
+ pcbNeeded -
+ pcReturned -
+
+Return Value:
+
+--*/
+{
+ DWORD err;
+ PNWSPOOL pSpool = (PNWSPOOL) hPrinter;
+
+
+ if ( !pSpool || pSpool->nSignature != NW_SIGNATURE )
+ {
+ err = ERROR_INVALID_HANDLE;
+ }
+ else if ( pSpool->errOpenPrinter )
+ {
+ err = pSpool->errOpenPrinter;
+ }
+ else if ( ( dwLevel != 1 ) && ( dwLevel != 2 ) )
+ {
+ err = ERROR_INVALID_LEVEL;
+ }
+ else
+ {
+ PNWPRINTER pPrinter = pSpool->pPrinter;
+ LPWSTR pszPrinter;
+ DWORD nPrinterLen;
+
+ ASSERT( pPrinter );
+ pszPrinter = AllocNwSplMem( LMEM_ZEROINIT,
+ nPrinterLen = ( wcslen( pPrinter->pszServer ) +
+ wcslen( pPrinter->pszQueue) + 2) * sizeof(WCHAR));
+
+ if ( pszPrinter == NULL )
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ wcscpy( pszPrinter, pPrinter->pszServer );
+ wcscat( pszPrinter, L"\\" );
+ wcscat( pszPrinter, pPrinter->pszQueue );
+
+ err = NwGetQueueJobs( pSpool->hServer,
+ pPrinter->nQueueId,
+ pszPrinter,
+ dwFirstJob,
+ dwNoJobs,
+ dwLevel,
+ pbJob,
+ cbBuf,
+ pcbNeeded,
+ pcReturned );
+
+ FreeNwSplMem( pszPrinter, nPrinterLen );
+
+ if ( !err )
+ {
+ DWORD *pOffsets;
+ DWORD cbStruct;
+ DWORD cReturned = *pcReturned;
+ LPBYTE pbBuffer = pbJob;
+
+ switch( dwLevel )
+ {
+ case 1:
+ pOffsets = JobInfo1Offsets;
+ cbStruct = sizeof( JOB_INFO_1W );
+ break;
+
+ case 2:
+ pOffsets = JobInfo2Offsets;
+ cbStruct = sizeof( JOB_INFO_2W );
+ break;
+
+ default:
+ ASSERT( FALSE );
+ break;
+ }
+
+ while ( cReturned-- )
+ {
+ MarshallDownStructure( pbBuffer, pOffsets, pbJob );
+ pbBuffer += cbStruct;
+ }
+ }
+ }
+
+ return err;
+}
+
+
+
+DWORD
+NwrSetJob(
+ IN NWWKSTA_PRINTER_CONTEXT hPrinter,
+ IN DWORD dwJobId,
+ IN DWORD dwLevel,
+ IN PNW_JOB_INFO pNwJobInfo,
+ IN DWORD dwCommand
+)
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ hPrinter - Handle of the printer
+ dwJobId -
+ dwLevel -
+ pNwJobInfo-
+ dwCommand -
+
+Return Value:
+
+--*/
+{
+ DWORD err = NO_ERROR;
+ PNWSPOOL pSpool = (PNWSPOOL) hPrinter;
+ PNWPRINTER pPrinter;
+
+ if ( !pSpool || pSpool->nSignature != NW_SIGNATURE )
+ {
+ err = ERROR_INVALID_HANDLE;
+ }
+ else if ( pSpool->errOpenPrinter )
+ {
+ err = pSpool->errOpenPrinter;
+ }
+ else if ( ( dwLevel != 0 ) && ( dwLevel != 1 ) && ( dwLevel != 2 ) )
+ {
+ err = ERROR_INVALID_LEVEL;
+ }
+
+ if ( err )
+ return err;
+
+ pPrinter = pSpool->pPrinter;
+ ASSERT( pPrinter );
+
+ if ( dwCommand == JOB_CONTROL_CANCEL )
+ {
+ err = NwRemoveJobFromQueue( pSpool->hServer,
+ pPrinter->nQueueId,
+ (WORD) dwJobId );
+
+ if ( !err )
+ NwSetPrinterChange( pSpool, PRINTER_CHANGE_DELETE_JOB |
+ PRINTER_CHANGE_SET_PRINTER );
+
+ // Since the job is removed, we don't need to change other
+ // information about it.
+ }
+ else
+ {
+ if ( dwLevel != 0 )
+ {
+ if ( pNwJobInfo->nPosition != JOB_POSITION_UNSPECIFIED )
+ {
+ err = NwChangeQueueJobPosition( pSpool->hServer,
+ pPrinter->nQueueId,
+ (WORD) dwJobId,
+ (BYTE) pNwJobInfo->nPosition );
+ }
+ }
+
+ if ( ( !err ) && ( dwCommand == JOB_CONTROL_RESTART ))
+ {
+ err = ERROR_NOT_SUPPORTED;
+ }
+ else if ( !err )
+ {
+ err = NwChangeQueueJobEntry( pSpool->hServer,
+ pPrinter->nQueueId,
+ (WORD) dwJobId,
+ dwCommand,
+ pNwJobInfo );
+ }
+
+ if ( !err )
+ NwSetPrinterChange( pSpool, PRINTER_CHANGE_SET_JOB );
+ }
+
+ return err;
+}
+
+
+
+DWORD
+NwrAddJob(
+ IN NWWKSTA_PRINTER_CONTEXT hPrinter,
+ OUT LPADDJOB_INFO_1W pAddInfo1,
+ IN DWORD cbBuf,
+ OUT LPDWORD pcbNeeded
+ )
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ hPrinter - Handle of the printer.
+ pAddInfo1 - Output buffer to hold ADDJOB_INFO_1W structure.
+ cbBuf - Output buffer size in bytes.
+ pcbNeeded - Required output buffer size in bytes.
+
+Return Value:
+
+--*/
+{
+ PNWSPOOL pSpool = (PNWSPOOL) hPrinter;
+ PNWPRINTER pPrinter;
+
+
+ if ( !pSpool || ( pSpool->nSignature != NW_SIGNATURE )) {
+ return ERROR_INVALID_HANDLE;
+ }
+
+ if ( pSpool->errOpenPrinter ) {
+ return pSpool->errOpenPrinter;
+ }
+
+ if ( pSpool->nStatus != 0 ) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ pPrinter = pSpool->pPrinter;
+ ASSERT( pPrinter );
+
+ *pcbNeeded = sizeof(ADDJOB_INFO_1W) +
+ (wcslen(pPrinter->pszServer) +
+ wcslen(pPrinter->pszQueue) + 2) * sizeof(WCHAR);
+
+ if (cbBuf < *pcbNeeded) {
+ return ERROR_INSUFFICIENT_BUFFER;
+ }
+
+ //
+ // Write UNC path name into the output buffer.
+ //
+ pAddInfo1->Path = (LPWSTR) ((DWORD) pAddInfo1 + sizeof(ADDJOB_INFO_1W));
+ wcscpy(pAddInfo1->Path, pPrinter->pszServer);
+ wcscat(pAddInfo1->Path, L"\\" );
+ wcscat(pAddInfo1->Path, pPrinter->pszQueue);
+
+ //
+ // Return special job id value which the client (winspool.drv) looks
+ // for and does an FSCTL call to our redirector to get the real
+ // job id. We cannot return a real job id at this point because
+ // the CreateQueueJobAndFile NCP is not issue until the client opens
+ // the UNC name we return in this API.
+ //
+ pAddInfo1->JobId = (DWORD) -1;
+
+ //
+ // Save context information
+ //
+ pSpool->nJobNumber = pAddInfo1->JobId;
+ pSpool->nStatus = SPOOL_STATUS_ADDJOB;
+
+#if DBG
+ IF_DEBUG(PRINT) {
+ KdPrint(("NWWORKSTATION: NwrAddJob Path=%ws, JobId=%lu, BytesNeeded=%lu\n",
+ pAddInfo1->Path, pAddInfo1->JobId, *pcbNeeded));
+ }
+#endif
+
+ NwSetPrinterChange( pSpool, PRINTER_CHANGE_ADD_JOB |
+ PRINTER_CHANGE_SET_PRINTER );
+
+ return NO_ERROR;
+}
+
+
+
+DWORD
+NwrScheduleJob(
+ IN NWWKSTA_PRINTER_CONTEXT hPrinter,
+ IN DWORD dwJobId
+ )
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ hPrinter - Handle of the printer
+ dwJobId - Job identification number
+
+Return Value:
+
+--*/
+{
+ PNWSPOOL pSpool = (PNWSPOOL) hPrinter;
+ PNWPRINTER pPrinter;
+
+
+ if ( !pSpool || ( pSpool->nSignature != NW_SIGNATURE )) {
+ return ERROR_INVALID_HANDLE;
+ }
+
+ if ( pSpool->errOpenPrinter ) {
+ return pSpool->errOpenPrinter;
+ }
+
+ if (pSpool->nStatus != SPOOL_STATUS_ADDJOB) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ pPrinter = pSpool->pPrinter;
+ ASSERT( pPrinter );
+
+ pSpool->nJobNumber = 0;
+ pSpool->nStatus = 0;
+
+ NwSetPrinterChange( pSpool, PRINTER_CHANGE_SET_JOB );
+
+ return NO_ERROR;
+}
+
+
+
+DWORD
+NwrWaitForPrinterChange(
+ IN NWWKSTA_PRINTER_CONTEXT hPrinter,
+ IN OUT LPDWORD pdwFlags
+)
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+ hPrinter - Handle of the printer
+ pdwFlags -
+
+Return Value:
+
+--*/
+{
+ PNWSPOOL pSpool = (PNWSPOOL) hPrinter;
+ HANDLE hChangeEvent = NULL;
+ DWORD nRetVal;
+ HANDLE ahWaitEvents[2];
+ DWORD err = NO_ERROR;
+
+ if ( !pSpool || ( pSpool->nSignature != NW_SIGNATURE ))
+ {
+ return ERROR_INVALID_HANDLE;
+ }
+ else if ( pSpool->errOpenPrinter )
+ {
+ return pSpool->errOpenPrinter;
+ }
+ else if ( pSpool->hChangeEvent )
+ {
+ return ERROR_ALREADY_WAITING;
+ }
+ else if ( !(*pdwFlags & PRINTER_CHANGE_VALID )) // BUGBUG: do we need this?
+ {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+
+ if ( pSpool->nChangeFlags & *pdwFlags )
+ {
+ //
+ // There is a change since we last called
+ //
+
+ *pdwFlags &= pSpool->nChangeFlags;
+
+ EnterCriticalSection( &NwPrintCritSec );
+ pSpool->nChangeFlags = 0;
+ LeaveCriticalSection( &NwPrintCritSec );
+
+ return NO_ERROR;
+ }
+
+ hChangeEvent = CreateEvent( NULL,
+ FALSE, // automatic reset
+ FALSE, // initial state not signalled
+ NULL );
+
+ if ( !hChangeEvent )
+ {
+ KdPrint(("WaitForPrinterChange: CreateEvent failed with error %d\n",
+ GetLastError() ));
+ return GetLastError();
+ }
+
+
+ pSpool->nWaitFlags = *pdwFlags;
+
+ EnterCriticalSection( &NwPrintCritSec );
+ pSpool->hChangeEvent = hChangeEvent;
+ pSpool->nChangeFlags = 0;
+ LeaveCriticalSection( &NwPrintCritSec );
+
+ ahWaitEvents[0] = pSpool->hChangeEvent;
+ ahWaitEvents[1] = NwDoneEvent;
+
+ nRetVal = WaitForMultipleObjects( 2, // Two events to wait for
+ ahWaitEvents,
+ FALSE, // Wait for one to signal
+ NwTimeOutValue );
+
+ switch ( nRetVal )
+ {
+ case WAIT_FAILED:
+ err = GetLastError();
+ break;
+
+ case WAIT_TIMEOUT:
+ case WAIT_OBJECT_0 + 1: // treats service stopping as timeout
+ *pdwFlags |= PRINTER_CHANGE_TIMEOUT;
+ break;
+
+ case WAIT_OBJECT_0:
+ *pdwFlags &= pSpool->nChangeFlags;
+ break;
+
+ default:
+ KdPrint(("WaitForPrinterChange: WaitForMultipleObjects returned with %d\n", nRetVal ));
+ *pdwFlags |= PRINTER_CHANGE_TIMEOUT;
+ break;
+ }
+
+ if ( ( !err ) && ( nRetVal != WAIT_OBJECT_0 + 1 ) )
+ {
+ pSpool->nWaitFlags = 0;
+
+ EnterCriticalSection( &NwPrintCritSec );
+ pSpool->nChangeFlags = 0;
+ pSpool->hChangeEvent = NULL;
+ LeaveCriticalSection( &NwPrintCritSec );
+ }
+
+ if ( !CloseHandle( hChangeEvent ) )
+ {
+ KdPrint(("WaitForPrinterChange: CloseHandle failed with error %d\n",
+ GetLastError()));
+ }
+
+ return err;
+}
+
+
+
+VOID
+NwSetPrinterChange(
+ PNWSPOOL pSpool,
+ DWORD nFlags
+)
+{
+ PNWPRINTER pPrinter = pSpool->pPrinter;
+ PNWSPOOL pCurSpool = pSpool;
+
+ EnterCriticalSection( &NwPrintCritSec );
+
+ do {
+
+ if ( pCurSpool->nWaitFlags & nFlags )
+ {
+ pCurSpool->nChangeFlags |= nFlags;
+
+ if ( pCurSpool->hChangeEvent )
+ {
+ SetEvent( pCurSpool->hChangeEvent );
+ pCurSpool->hChangeEvent = NULL;
+ }
+ }
+
+ pCurSpool = pCurSpool->pNextSpool;
+ if ( pCurSpool == NULL )
+ pCurSpool = pPrinter->pSpoolList;
+
+ } while ( pCurSpool && (pCurSpool != pSpool) );
+
+ LeaveCriticalSection( &NwPrintCritSec );
+}
+
+
+
+PNWPRINTER
+NwFindPrinterEntry(
+ IN LPWSTR pszServer,
+ IN LPWSTR pszQueue
+)
+{
+ PNWPRINTER pPrinter = NULL;
+
+ //
+ // Check to see if we already have the given printer in our printer
+ // link list. If yes, return the printer.
+ //
+
+ for ( pPrinter = NwPrinterList; pPrinter; pPrinter = pPrinter->pNextPrinter)
+ {
+ if ( ( lstrcmpiW( pPrinter->pszServer, pszServer ) == 0 )
+ && ( lstrcmpiW( pPrinter->pszQueue, pszQueue ) == 0 )
+ )
+ {
+ return pPrinter;
+ }
+ }
+
+ return NULL;
+}
+
+
+
+DWORD
+NwCreatePrinterEntry(
+ IN LPWSTR pszServer,
+ IN LPWSTR pszQueue,
+ OUT PNWPRINTER *ppPrinter,
+ OUT PHANDLE phServer
+)
+{
+ DWORD err = NO_ERROR;
+ DWORD nQueueId = 0;
+ HANDLE TreeHandle = NULL;
+ UNICODE_STRING TreeName;
+ PNWPRINTER pNwPrinter = NULL;
+ BOOL fCreatedNWConnection = FALSE;
+
+ LPWSTR lpRemoteName = NULL;
+ DWORD dwBufSize = ( wcslen(pszServer) + wcslen(pszQueue) + 2 )
+ * sizeof(WCHAR);
+
+ lpRemoteName = (LPWSTR) AllocNwSplMem( LMEM_ZEROINIT, dwBufSize );
+
+ if ( lpRemoteName == NULL )
+ return ERROR_NOT_ENOUGH_MEMORY;
+
+ wcscpy( lpRemoteName, pszServer );
+ wcscat( lpRemoteName, L"\\" );
+ wcscat( lpRemoteName, pszQueue );
+
+ *ppPrinter = NULL;
+ *phServer = NULL;
+
+ //
+ // See if we already know about this print queue.
+ //
+ pNwPrinter = NwFindPrinterEntry( pszServer, pszQueue );
+
+ if ( pNwPrinter == NULL )
+ {
+ // We don't know about this NetWare print queue. We need to see if
+ // we are authorized to use this queue. If so, then go ahead
+ // and continue to open printer. Otherwise, fail with not
+ // authorized error code.
+
+ err = NwCreateConnection( NULL,
+ lpRemoteName,
+ RESOURCETYPE_PRINT,
+ NULL,
+ NULL );
+
+ if ( err != NO_ERROR )
+ {
+ if ( ( err == ERROR_INVALID_PASSWORD ) ||
+ ( err == ERROR_ACCESS_DENIED ) ||
+ ( err == ERROR_NO_SUCH_USER ) )
+ {
+ // BUGBUG err = ERROR_INVALID_PASSWORD;
+ err = ERROR_ACCESS_DENIED;
+ }
+
+ FreeNwSplMem( lpRemoteName, dwBufSize );
+ if ( TreeHandle )
+ CloseHandle( TreeHandle );
+
+ return err;
+ }
+
+ fCreatedNWConnection = TRUE;
+ }
+
+ //
+ // See if pszServer is really a NDS tree name, if so call
+ // NwNdsGetQueueInformation to get the QueueId and possible referred
+ // server for which we open handle.
+ //
+
+ RtlInitUnicodeString( &TreeName, pszServer + 2 );
+
+ err = NwNdsOpenTreeHandle( &TreeName, &TreeHandle );
+
+ if ( err == NO_ERROR )
+ {
+ NTSTATUS ntstatus;
+ WCHAR szRefServer[NDS_MAX_NAME_CHARS];
+ UNICODE_STRING ObjectName;
+ UNICODE_STRING QueuePath;
+
+ ObjectName.Buffer = szRefServer;
+ ObjectName.MaximumLength = NDS_MAX_NAME_CHARS;
+ ObjectName.Length = 0;
+
+ RtlInitUnicodeString( &QueuePath, pszQueue );
+
+ ntstatus = NwNdsGetQueueInformation( TreeHandle,
+ &QueuePath,
+ &ObjectName,
+ &nQueueId );
+
+ if ( TreeHandle )
+ {
+ CloseHandle( TreeHandle );
+ TreeHandle = NULL;
+ }
+
+ if ( ntstatus )
+ {
+ err = RtlNtStatusToDosError( ntstatus );
+ goto ErrorExit;
+ }
+
+ //
+ // If we got a referred server, it's name would look like:
+ // "CN=SERVER.OU=DEV.O=MICROSOFT" . . . Convert it to "C\\SERVER"
+ //
+ if ( ObjectName.Length > 0 )
+ {
+ WORD i;
+ LPWSTR EndOfServerName = NULL;
+
+ //
+ // First convert the referred server name to
+ // "C\\SERVER.OU=DEV.O=MICROSOFT"
+ //
+ szRefServer[1] = L'\\';
+ szRefServer[2] = L'\\';
+
+ //
+ // Put a NULL terminator at the first '.'
+ //
+ EndOfServerName = wcschr( szRefServer + 3, L'.' );
+ if (EndOfServerName)
+ *EndOfServerName = L'\0';
+
+ //
+ // pszServer now equals the referred server "C\\SERVER"
+ //
+
+ //
+ // Get the handle of the referred server skipping the 'C' character.
+ //
+ err = NwAttachToNetwareServer( szRefServer + 1, phServer);
+ }
+ }
+ else // Not an NDS tree, so get handle of server.
+ {
+
+ err = NwAttachToNetwareServer( pszServer, phServer);
+
+ if ( err == NO_ERROR )
+ {
+ if ( err = NwGetQueueId( *phServer, pszQueue, &nQueueId))
+ err = ERROR_INVALID_NAME;
+ }
+ }
+
+ if ( ( err == ERROR_INVALID_PASSWORD ) ||
+ ( err == ERROR_ACCESS_DENIED ) ||
+ ( err == ERROR_NO_SUCH_USER ) )
+ {
+ // BUGBUG err = ERROR_INVALID_PASSWORD;
+ err = ERROR_ACCESS_DENIED;
+ goto ErrorExit;
+ }
+ else if ( err != NO_ERROR )
+ {
+ err = ERROR_INVALID_NAME;
+ goto ErrorExit;
+ }
+
+ //
+ // Test to see if there already was a entry for this print queue. If so,
+ // we can now return with NO_ERROR since pNwPrinter and phServer are
+ // now set.
+ //
+ if ( pNwPrinter )
+ {
+ if ( lpRemoteName )
+ {
+ FreeNwSplMem( lpRemoteName, dwBufSize );
+ }
+
+ *ppPrinter = pNwPrinter;
+
+ return NO_ERROR;
+ }
+
+ //
+ // The printer entry was not found in our list of printers in the
+ // call to NwFindPrinterEntry. So, we must create one.
+ //
+ if ( *ppPrinter = AllocNwSplMem( LMEM_ZEROINIT, sizeof(NWPRINTER) ))
+ {
+ if ( !( (*ppPrinter)->pszServer = AllocNwSplStr( pszServer )) )
+ {
+ err = ERROR_NOT_ENOUGH_MEMORY;
+ goto ErrorExit;
+ }
+ else if ( !( (*ppPrinter)->pszQueue = AllocNwSplStr( pszQueue )))
+ {
+ err = ERROR_NOT_ENOUGH_MEMORY;
+ goto ErrorExit;
+ }
+
+ if ( fCreatedNWConnection )
+ {
+ if ( !( (*ppPrinter)->pszUncConnection =
+ AllocNwSplStr( lpRemoteName )) )
+ {
+ err = ERROR_NOT_ENOUGH_MEMORY;
+ goto ErrorExit;
+ }
+
+ FreeNwSplMem( lpRemoteName, dwBufSize );
+ lpRemoteName = NULL;
+ }
+ else
+ {
+ (*ppPrinter)->pszUncConnection = NULL;
+ }
+
+#if DBG
+ IF_DEBUG(PRINT)
+ {
+ KdPrint(("*************CREATED PRINTER ENTRY: %ws\\%ws\n\n",
+ (*ppPrinter)->pszServer, (*ppPrinter)->pszQueue ));
+ }
+#endif
+
+ (*ppPrinter)->nQueueId = nQueueId;
+ (*ppPrinter)->pSpoolList = NULL;
+ (*ppPrinter)->pNextPrinter = NwPrinterList;
+ NwPrinterList = *ppPrinter;
+
+ err = NO_ERROR;
+ }
+ else
+ {
+ err = ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ if ( err == NO_ERROR )
+ return err;
+
+ErrorExit:
+
+ if ( *phServer )
+ {
+ (VOID) NtClose( *phServer );
+ *phServer = NULL;
+ }
+
+ if ( *ppPrinter )
+ {
+ if ( (*ppPrinter)->pszServer )
+ {
+ FreeNwSplStr( (*ppPrinter)->pszServer );
+ }
+
+ if ( (*ppPrinter)->pszQueue )
+ {
+ FreeNwSplStr( (*ppPrinter)->pszQueue );
+ }
+
+ if ( (*ppPrinter)->pszUncConnection )
+ {
+ (void) NwrDeleteConnection( NULL,
+ (*ppPrinter)->pszUncConnection,
+ FALSE );
+ FreeNwSplStr( (*ppPrinter)->pszUncConnection );
+ }
+
+ FreeNwSplMem( *ppPrinter, sizeof( NWPRINTER));
+ *ppPrinter = NULL;
+ }
+
+ if ( lpRemoteName )
+ {
+ FreeNwSplMem( lpRemoteName, dwBufSize );
+ }
+
+ return err;
+}
+
+
+
+VOID
+NwRemovePrinterEntry(
+ IN PNWPRINTER pPrinter
+)
+{
+ PNWPRINTER pCur, pPrev = NULL;
+
+ ASSERT( pPrinter->pSpoolList == NULL );
+ pPrinter->pSpoolList = NULL;
+
+ for ( pCur = NwPrinterList; pCur; pPrev = pCur, pCur = pCur->pNextPrinter )
+ {
+ if ( pCur == pPrinter )
+ {
+ if ( pPrev )
+ pPrev->pNextPrinter = pCur->pNextPrinter;
+ else
+ NwPrinterList = pCur->pNextPrinter;
+ break;
+ }
+ }
+
+ ASSERT( pCur );
+
+ pPrinter->pNextPrinter = NULL;
+ FreeNwSplStr( pPrinter->pszServer );
+ FreeNwSplStr( pPrinter->pszQueue );
+ if ( pPrinter->pszUncConnection )
+ {
+ (void) NwrDeleteConnection( NULL,
+ pPrinter->pszUncConnection,
+ FALSE );
+ FreeNwSplStr( pPrinter->pszUncConnection );
+ }
+ FreeNwSplMem( pPrinter, sizeof( NWPRINTER));
+}
+
+
+
+VOID
+NWWKSTA_PRINTER_CONTEXT_rundown(
+ IN NWWKSTA_PRINTER_CONTEXT hPrinter
+ )
+/*++
+
+Routine Description:
+
+ This function is called by RPC when a client terminates with an
+ opened handle. This allows us to clean up and deallocate any context
+ data associated with the handle.
+
+Arguments:
+
+ hPrinter - Supplies the opened handle
+
+Return Value:
+
+ None.
+
+--*/
+{
+ (void) NwrClosePrinter(&hPrinter);
+}
+
+
+
+DWORD
+NwGetUncObjectName(
+ IN LPWSTR ContainerName
+)
+{
+ WORD length = 2;
+ WORD totalLength = wcslen( ContainerName );
+
+ if ( totalLength < 2 )
+ return 0;
+
+ while ( length < totalLength )
+ {
+ if ( ContainerName[length] == L'.' )
+ ContainerName[length] = L'\0';
+
+ length++;
+ }
+
+ length = 2;
+
+ while ( length < totalLength && ContainerName[length] != L'\\' )
+ {
+ length++;
+ }
+
+ ContainerName[length + 2] = L'\\';
+ ContainerName[length + 3] = L'\\';
+
+ return (DWORD) (ContainerName + length + 2);
+}
+
diff --git a/private/nw/svcdlls/nwwks/server/util.c b/private/nw/svcdlls/nwwks/server/util.c
new file mode 100644
index 000000000..5d9772376
--- /dev/null
+++ b/private/nw/svcdlls/nwwks/server/util.c
@@ -0,0 +1,161 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ util.c
+
+Abstract:
+
+ This module contains miscellaneous utility routines used by the
+ NetWare Workstation service.
+
+Author:
+
+ Rita Wong (ritaw) 08-Feb-1993
+
+Revision History:
+
+--*/
+
+#include <nw.h>
+#include <nwstatus.h>
+
+
+//
+// Debug trace flag for selecting which trace statements to output
+//
+#if DBG
+
+DWORD WorkstationTrace = 0;
+
+#endif // DBG
+
+
+
+DWORD
+NwImpersonateClient(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function calls RpcImpersonateClient to impersonate the current caller
+ of an API.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+{
+ DWORD status;
+
+
+ if ((status = RpcImpersonateClient(NULL)) != NO_ERROR) {
+ KdPrint(("NWWORKSTATION: Fail to impersonate client %ld\n", status));
+ }
+
+ return status;
+}
+
+
+DWORD
+NwRevertToSelf(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function calls RpcRevertToSelf to undo an impersonation.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+{
+ DWORD status;
+
+
+ if ((status = RpcRevertToSelf()) != NO_ERROR) {
+ KdPrint(("NWWORKSTATION: Fail to revert to self %ld\n", status));
+ ASSERT(FALSE);
+ }
+
+ return status;
+}
+
+
+VOID
+NwLogEvent(
+ DWORD MessageId,
+ DWORD NumberOfSubStrings,
+ LPWSTR *SubStrings,
+ DWORD ErrorCode
+ )
+{
+
+ HANDLE LogHandle;
+
+
+ LogHandle = RegisterEventSourceW (
+ NULL,
+ NW_WORKSTATION_SERVICE
+ );
+
+ if (LogHandle == NULL) {
+ KdPrint(("NWWORKSTATION: RegisterEventSourceW failed %lu\n",
+ GetLastError()));
+ return;
+ }
+
+ if (ErrorCode == NO_ERROR) {
+
+ //
+ // No error codes were specified
+ //
+ (void) ReportEventW(
+ LogHandle,
+ EVENTLOG_ERROR_TYPE,
+ 0, // event category
+ MessageId,
+ (PSID) NULL,
+ (WORD) NumberOfSubStrings,
+ 0,
+ SubStrings,
+ (PVOID) NULL
+ );
+
+ }
+ else {
+
+ //
+ // Log the error code specified as binary data
+ //
+ (void) ReportEventW(
+ LogHandle,
+ EVENTLOG_ERROR_TYPE,
+ 0, // event category
+ MessageId,
+ (PSID) NULL,
+ (WORD) NumberOfSubStrings,
+ sizeof(DWORD),
+ SubStrings,
+ (PVOID) &ErrorCode
+ );
+ }
+
+ DeregisterEventSource(LogHandle);
+}