summaryrefslogtreecommitdiffstats
path: root/private/nw
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
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')
-rw-r--r--private/nw/convert/dirs1
-rw-r--r--private/nw/convert/logview/fv300.icobin0 -> 766 bytes
-rw-r--r--private/nw/convert/logview/fvfile.c231
-rw-r--r--private/nw/convert/logview/fvfind.c193
-rw-r--r--private/nw/convert/logview/fvinit.c155
-rw-r--r--private/nw/convert/logview/fvopen.c131
-rw-r--r--private/nw/convert/logview/fvprint.c389
-rw-r--r--private/nw/convert/logview/logview.c767
-rw-r--r--private/nw/convert/logview/logview.dlg51
-rw-r--r--private/nw/convert/logview/logview.h214
-rw-r--r--private/nw/convert/logview/logview.rc111
-rw-r--r--private/nw/convert/logview/makefile6
-rw-r--r--private/nw/convert/logview/note300.icobin0 -> 766 bytes
-rw-r--r--private/nw/convert/logview/sources23
-rw-r--r--private/nw/convert/logview/version.h52
-rw-r--r--private/nw/convert/nwconv/aboutbox.c39
-rw-r--r--private/nw/convert/nwconv/columnlb.c2986
-rw-r--r--private/nw/convert/nwconv/columnlb.h329
-rw-r--r--private/nw/convert/nwconv/constant.h36
-rw-r--r--private/nw/convert/nwconv/convapi.h29
-rw-r--r--private/nw/convert/nwconv/ctlspriv.h172
-rw-r--r--private/nw/convert/nwconv/debug.c459
-rw-r--r--private/nw/convert/nwconv/debug.h34
-rw-r--r--private/nw/convert/nwconv/encrypt.c328
-rw-r--r--private/nw/convert/nwconv/error.c69
-rw-r--r--private/nw/convert/nwconv/error.h19
-rw-r--r--private/nw/convert/nwconv/fastcopy.c244
-rw-r--r--private/nw/convert/nwconv/fcopy.c1162
-rw-r--r--private/nw/convert/nwconv/filedlg.c1074
-rw-r--r--private/nw/convert/nwconv/filedlg.h34
-rw-r--r--private/nw/convert/nwconv/fileicon.bmpbin0 -> 630 bytes
-rw-r--r--private/nw/convert/nwconv/filesel.c2349
-rw-r--r--private/nw/convert/nwconv/filesel.h130
-rw-r--r--private/nw/convert/nwconv/fpnwapi.h76
-rw-r--r--private/nw/convert/nwconv/globals.h48
-rw-r--r--private/nw/convert/nwconv/helpid.h70
-rw-r--r--private/nw/convert/nwconv/hierchk.bmpbin0 -> 502 bytes
-rw-r--r--private/nw/convert/nwconv/hierdraw.c403
-rw-r--r--private/nw/convert/nwconv/hierdraw.h65
-rw-r--r--private/nw/convert/nwconv/hierfile.c506
-rw-r--r--private/nw/convert/nwconv/hierfile.h74
-rw-r--r--private/nw/convert/nwconv/hiericon.bmpbin0 -> 886 bytes
-rw-r--r--private/nw/convert/nwconv/loghours.c310
-rw-r--r--private/nw/convert/nwconv/loghours.h52
-rw-r--r--private/nw/convert/nwconv/makefile6
-rw-r--r--private/nw/convert/nwconv/map.c1682
-rw-r--r--private/nw/convert/nwconv/map.h62
-rw-r--r--private/nw/convert/nwconv/mem.c381
-rw-r--r--private/nw/convert/nwconv/mem.h40
-rw-r--r--private/nw/convert/nwconv/moveit.icobin0 -> 766 bytes
-rw-r--r--private/nw/convert/nwconv/netutil.c437
-rw-r--r--private/nw/convert/nwconv/netutil.h87
-rw-r--r--private/nw/convert/nwconv/ntnetapi.c3644
-rw-r--r--private/nw/convert/nwconv/ntnetapi.h85
-rw-r--r--private/nw/convert/nwconv/ntsecapi.h28
-rw-r--r--private/nw/convert/nwconv/nwconv.c1509
-rw-r--r--private/nw/convert/nwconv/nwconv.h25
-rw-r--r--private/nw/convert/nwconv/nwconv.rc954
-rw-r--r--private/nw/convert/nwconv/nwlog.c746
-rw-r--r--private/nw/convert/nwconv/nwlog.h39
-rw-r--r--private/nw/convert/nwconv/nwnetapi.c2692
-rw-r--r--private/nw/convert/nwconv/nwnetapi.h133
-rw-r--r--private/nw/convert/nwconv/nwrights.h168
-rw-r--r--private/nw/convert/nwconv/resource.h245
-rw-r--r--private/nw/convert/nwconv/sbrowse.c1153
-rw-r--r--private/nw/convert/nwconv/sbrowse.h44
-rw-r--r--private/nw/convert/nwconv/servlist.c2094
-rw-r--r--private/nw/convert/nwconv/servlist.h311
-rw-r--r--private/nw/convert/nwconv/sizebarh.curbin0 -> 326 bytes
-rw-r--r--private/nw/convert/nwconv/sources56
-rw-r--r--private/nw/convert/nwconv/statbox.c568
-rw-r--r--private/nw/convert/nwconv/statbox.h46
-rw-r--r--private/nw/convert/nwconv/strings.h478
-rw-r--r--private/nw/convert/nwconv/switches.h12
-rw-r--r--private/nw/convert/nwconv/tab.c1512
-rw-r--r--private/nw/convert/nwconv/tab.h71
-rw-r--r--private/nw/convert/nwconv/transfer.c2381
-rw-r--r--private/nw/convert/nwconv/transfer.h19
-rw-r--r--private/nw/convert/nwconv/userdlg.c1266
-rw-r--r--private/nw/convert/nwconv/userdlg.h49
-rw-r--r--private/nw/convert/nwconv/usrprop.c592
-rw-r--r--private/nw/convert/nwconv/usrprop.h77
-rw-r--r--private/nw/convert/nwconv/utils.c830
-rw-r--r--private/nw/convert/nwconv/utils.h33
-rw-r--r--private/nw/convert/nwconv/version.h52
-rw-r--r--private/nw/convert/nwconv/vlist.h95
-rw-r--r--private/nw/convert/nwconv/vlistint.h89
-rw-r--r--private/nw/dirs36
-rw-r--r--private/nw/event/dummy.c4
-rw-r--r--private/nw/event/events.rc12
-rw-r--r--private/nw/event/makefile6
-rw-r--r--private/nw/event/nwevent.def7
-rw-r--r--private/nw/event/nwevent.mc178
-rw-r--r--private/nw/event/reg.ini7
-rw-r--r--private/nw/event/sources21
-rw-r--r--private/nw/inc/ncp.h488
-rw-r--r--private/nw/inc/nds.h226
-rw-r--r--private/nw/inc/ndsapi32.h330
-rw-r--r--private/nw/inc/ntddnwfs.h625
-rw-r--r--private/nw/inc/nwapi.h197
-rw-r--r--private/nw/inc/nwapi32.h810
-rw-r--r--private/nw/inc/nwcanon.h55
-rw-r--r--private/nw/inc/nwcons.h39
-rw-r--r--private/nw/inc/nwpapi32.h297
-rw-r--r--private/nw/inc/nwpkstr.h37
-rw-r--r--private/nw/inc/nwrnames.h55
-rw-r--r--private/nw/inc/nwrpcp.h138
-rw-r--r--private/nw/inc/nwsnames.h27
-rw-r--r--private/nw/inc/nwstatus.h27
-rw-r--r--private/nw/inc/nwsvc.h88
-rw-r--r--private/nw/inc/nwxchg.h45
-rw-r--r--private/nw/inc/usa/messages.inc41
-rw-r--r--private/nw/inc/validc.h87
-rw-r--r--private/nw/install/dirs25
-rw-r--r--private/nw/install/setupdll/common.h122
-rw-r--r--private/nw/install/setupdll/dllinit.c65
-rw-r--r--private/nw/install/setupdll/lodctr.c1531
-rw-r--r--private/nw/install/setupdll/makefile10
-rw-r--r--private/nw/install/setupdll/nwcfg.cxx318
-rw-r--r--private/nw/install/setupdll/nwcfg.def19
-rw-r--r--private/nw/install/setupdll/nwcfg.h22
-rw-r--r--private/nw/install/setupdll/nwcfg.hxx28
-rw-r--r--private/nw/install/setupdll/nwcfg.prf1
-rw-r--r--private/nw/install/setupdll/nwcfg.rc20
-rw-r--r--private/nw/install/setupdll/removesz.c58
-rw-r--r--private/nw/install/setupdll/rules.mk4
-rw-r--r--private/nw/install/setupdll/setvalue.c696
-rw-r--r--private/nw/install/setupdll/sources48
-rw-r--r--private/nw/install/setupdll/unlodctr.c1006
-rw-r--r--private/nw/install/wksta/alpha.txt3
-rw-r--r--private/nw/install/wksta/eng.txt55
-rw-r--r--private/nw/install/wksta/files10.txt94
-rw-r--r--private/nw/install/wksta/files10a.txt37
-rw-r--r--private/nw/install/wksta/i386.txt3
-rw-r--r--private/nw/install/wksta/makefile1
-rw-r--r--private/nw/install/wksta/makefile.inc29
-rw-r--r--private/nw/install/wksta/mips.txt3
-rw-r--r--private/nw/install/wksta/nw.inf1224
-rw-r--r--private/nw/install/wksta/nwperf.ini37
-rw-r--r--private/nw/install/wksta/nwperfm.h24
-rw-r--r--private/nw/install/wksta/ppc.txt4
-rw-r--r--private/nw/install/wksta/sources42
-rw-r--r--private/nw/makefil063
-rw-r--r--private/nw/ndsutils/browser.c273
-rw-r--r--private/nw/ndsutils/conninfo.c156
-rw-r--r--private/nw/ndsutils/cx.c133
-rw-r--r--private/nw/ndsutils/getps.c102
-rw-r--r--private/nw/ndsutils/getuser.c133
-rw-r--r--private/nw/ndsutils/listconn.c235
-rw-r--r--private/nw/ndsutils/makefile6
-rw-r--r--private/nw/ndsutils/ndschpw.c173
-rw-r--r--private/nw/ndsutils/netperf.c138
-rw-r--r--private/nw/ndsutils/rdstrm.c195
-rw-r--r--private/nw/ndsutils/setshare.c88
-rw-r--r--private/nw/ndsutils/sources46
-rw-r--r--private/nw/ndsutils/treebn.c182
-rw-r--r--private/nw/ndsutils/userfrag.c149
-rw-r--r--private/nw/ndsutils/volinfo.c119
-rw-r--r--private/nw/nw16/dirs23
-rw-r--r--private/nw/nw16/dll/debug.c584
-rw-r--r--private/nw/nw16/dll/locks.c677
-rw-r--r--private/nw/nw16/dll/makefile6
-rw-r--r--private/nw/nw16/dll/ncp.c3383
-rw-r--r--private/nw/nw16/dll/nwapi16.rc12
-rw-r--r--private/nw/nw16/dll/nwapi16.src10
-rw-r--r--private/nw/nw16/dll/procs.h159
-rw-r--r--private/nw/nw16/dll/sources71
-rw-r--r--private/nw/nw16/drv/dllentry.asm81
-rw-r--r--private/nw/nw16/drv/ints.asm368
-rw-r--r--private/nw/nw16/drv/makefile127
-rw-r--r--private/nw/nw16/drv/netware.def21
-rw-r--r--private/nw/nw16/drv/netware.h61
-rw-r--r--private/nw/nw16/drv/nwasmutl.asm70
-rw-r--r--private/nw/nw16/drv/nwerror.h46
-rw-r--r--private/nw/nw16/drv/nwinit.c81
-rw-r--r--private/nw/nw16/inc/makefile34
-rw-r--r--private/nw/nw16/inc/nwdos.h220
-rw-r--r--private/nw/nw16/tsr/asmmacro.inc343
-rw-r--r--private/nw/nw16/tsr/debugmac.inc356
-rw-r--r--private/nw/nw16/tsr/makefile192
-rw-r--r--private/nw/nw16/tsr/nw16.asm517
-rw-r--r--private/nw/nw16/tsr/resident.asm1039
-rw-r--r--private/nw/nw16/tsr/segorder.inc113
-rw-r--r--private/nw/nwapi32/makefile6
-rw-r--r--private/nw/nwapi32/nwapi32.c3
-rw-r--r--private/nw/nwapi32/nwapi32.rc12
-rw-r--r--private/nw/nwapi32/nwapi32.src118
-rw-r--r--private/nw/nwapi32/sources62
-rw-r--r--private/nw/nwlib/canon.c480
-rw-r--r--private/nw/nwlib/exchange.c571
-rw-r--r--private/nw/nwlib/makefile6
-rw-r--r--private/nw/nwlib/ndsapi32.c2144
-rw-r--r--private/nw/nwlib/nwapi32.c1661
-rw-r--r--private/nw/nwlib/nwcapi32.c798
-rw-r--r--private/nw/nwlib/nwpapi32.c1011
-rw-r--r--private/nw/nwlib/packstr.c125
-rw-r--r--private/nw/nwlib/procs.h55
-rw-r--r--private/nw/nwlib/regacl.c161
-rw-r--r--private/nw/nwlib/sources66
-rw-r--r--private/nw/nwlib/tpath.c238
-rw-r--r--private/nw/nwscript/attach.c235
-rw-r--r--private/nw/nwscript/break.c85
-rw-r--r--private/nw/nwscript/capture.c1067
-rw-r--r--private/nw/nwscript/common.c400
-rw-r--r--private/nw/nwscript/date.c78
-rw-r--r--private/nw/nwscript/dbcs.c98
-rw-r--r--private/nw/nwscript/display.c101
-rw-r--r--private/nw/nwscript/drive.c327
-rw-r--r--private/nw/nwscript/drvstat.c268
-rw-r--r--private/nw/nwscript/env.c352
-rw-r--r--private/nw/nwscript/helpers.c96
-rw-r--r--private/nw/nwscript/inc/common.h195
-rw-r--r--private/nw/nwscript/inc/dbcs.h26
-rw-r--r--private/nw/nwscript/inc/ntnw.h46
-rw-r--r--private/nw/nwscript/inc/nwlibs.h371
-rw-r--r--private/nw/nwscript/inc/nwscript.h321
-rw-r--r--private/nw/nwscript/lsparse.c315
-rw-r--r--private/nw/nwscript/makefile6
-rw-r--r--private/nw/nwscript/maplist.c252
-rw-r--r--private/nw/nwscript/ncp.c356
-rw-r--r--private/nw/nwscript/nds.c1505
-rw-r--r--private/nw/nwscript/nt.c617
-rw-r--r--private/nw/nwscript/ntcap.c329
-rw-r--r--private/nw/nwscript/ntnw.c163
-rw-r--r--private/nw/nwscript/ntscript.c304
-rw-r--r--private/nw/nwscript/nwapi1.c47
-rw-r--r--private/nw/nwscript/nwapi2.c49
-rw-r--r--private/nw/nwscript/nwapi3.c1457
-rw-r--r--private/nw/nwscript/nwscript.c84
-rw-r--r--private/nw/nwscript/nwscript.rc243
-rw-r--r--private/nw/nwscript/parspath.c341
-rw-r--r--private/nw/nwscript/ps40db.c597
-rw-r--r--private/nw/nwscript/psdb.c396
-rw-r--r--private/nw/nwscript/script.c3968
-rw-r--r--private/nw/nwscript/sources85
-rw-r--r--private/nw/nwscript/strings.c132
-rw-r--r--private/nw/nwscript/time.c184
-rw-r--r--private/nw/nwscript/unc.c142
-rw-r--r--private/nw/nwscript/version.c68
-rw-r--r--private/nw/nwscript/wide.c149
-rw-r--r--private/nw/perf/makefile6
-rw-r--r--private/nw/perf/nwdata.c296
-rw-r--r--private/nw/perf/nwperf.c398
-rw-r--r--private/nw/perf/nwperf.h144
-rw-r--r--private/nw/perf/nwperf.rc11
-rw-r--r--private/nw/perf/perfnw.def8
-rw-r--r--private/nw/perf/prfutil.c203
-rw-r--r--private/nw/perf/prfutil.h42
-rw-r--r--private/nw/perf/sources46
-rw-r--r--private/nw/rdr/attach.c5169
-rw-r--r--private/nw/rdr/cache.c698
-rw-r--r--private/nw/rdr/callback.c190
-rw-r--r--private/nw/rdr/cleanup.c591
-rw-r--r--private/nw/rdr/close.c571
-rw-r--r--private/nw/rdr/const.h166
-rw-r--r--private/nw/rdr/convert.c732
-rw-r--r--private/nw/rdr/convert.h33
-rw-r--r--private/nw/rdr/create.c3398
-rw-r--r--private/nw/rdr/create4.c2075
-rw-r--r--private/nw/rdr/crypto.h139
-rw-r--r--private/nw/rdr/data.c373
-rw-r--r--private/nw/rdr/data.h238
-rw-r--r--private/nw/rdr/debug.c780
-rw-r--r--private/nw/rdr/deviosup.c153
-rw-r--r--private/nw/rdr/dir.c1672
-rw-r--r--private/nw/rdr/encrypt.c322
-rw-r--r--private/nw/rdr/errorlog.c201
-rw-r--r--private/nw/rdr/except.c160
-rw-r--r--private/nw/rdr/exchange.c4887
-rw-r--r--private/nw/rdr/exchange.h114
-rw-r--r--private/nw/rdr/fileinfo.c2905
-rw-r--r--private/nw/rdr/filobsup.c175
-rw-r--r--private/nw/rdr/fragex.c783
-rw-r--r--private/nw/rdr/fsctl.c5930
-rw-r--r--private/nw/rdr/fspdisp.c232
-rw-r--r--private/nw/rdr/init.c460
-rw-r--r--private/nw/rdr/ipx.c1749
-rw-r--r--private/nw/rdr/kdext/dirs26
-rw-r--r--private/nw/rdr/kdext/ntsd/makefile6
-rw-r--r--private/nw/rdr/kdext/ntsd/nw.def15
-rw-r--r--private/nw/rdr/kdext/ntsd/nw.rc12
-rw-r--r--private/nw/rdr/kdext/ntsd/sources49
-rw-r--r--private/nw/rdr/kdext/nwrdrkd.c2223
-rw-r--r--private/nw/rdr/kdext/windbg/makefile6
-rw-r--r--private/nw/rdr/kdext/windbg/nwdbg.def15
-rw-r--r--private/nw/rdr/kdext/windbg/nwdbg.rc12
-rw-r--r--private/nw/rdr/kdext/windbg/sources51
-rw-r--r--private/nw/rdr/lock.c1357
-rw-r--r--private/nw/rdr/lockcode.c168
-rw-r--r--private/nw/rdr/makefile6
-rw-r--r--private/nw/rdr/ndsfsctl.c2128
-rw-r--r--private/nw/rdr/ndslogin.c3365
-rw-r--r--private/nw/rdr/ndsprocs.h822
-rw-r--r--private/nw/rdr/ndsread.c1190
-rw-r--r--private/nw/rdr/nodetype.h63
-rw-r--r--private/nw/rdr/nwrdr.rc11
-rw-r--r--private/nw/rdr/pid.c454
-rw-r--r--private/nw/rdr/procs.h1830
-rw-r--r--private/nw/rdr/read.c2838
-rw-r--r--private/nw/rdr/scavengr.c689
-rw-r--r--private/nw/rdr/security.c1009
-rw-r--r--private/nw/rdr/sources90
-rw-r--r--private/nw/rdr/string.c378
-rw-r--r--private/nw/rdr/strucsup.c3127
-rw-r--r--private/nw/rdr/struct.h1357
-rw-r--r--private/nw/rdr/synch.txt74
-rw-r--r--private/nw/rdr/timer.c537
-rw-r--r--private/nw/rdr/util.c385
-rw-r--r--private/nw/rdr/volinfo.c1278
-rw-r--r--private/nw/rdr/workque.c829
-rw-r--r--private/nw/rdr/write.c3063
-rw-r--r--private/nw/rpc/client.c184
-rw-r--r--private/nw/rpc/makefile6
-rw-r--r--private/nw/rpc/midluser.c211
-rw-r--r--private/nw/rpc/server.c362
-rw-r--r--private/nw/rpc/sources41
-rw-r--r--private/nw/service/dirs25
-rw-r--r--private/nw/service/nwsvc/makefile6
-rw-r--r--private/nw/service/nwsvc/nwsvc.c230
-rw-r--r--private/nw/service/nwsvc/nwsvc.rc9
-rw-r--r--private/nw/service/nwsvc/sources47
-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
-rw-r--r--private/nw/test/attach.c404
-rw-r--r--private/nw/test/encrypt.c243
-rw-r--r--private/nw/test/fileio.c210
-rw-r--r--private/nw/test/getmsg.c178
-rw-r--r--private/nw/test/lock.c80
-rw-r--r--private/nw/test/lock2.c65
-rw-r--r--private/nw/test/lock3.c159
-rw-r--r--private/nw/test/lock4.c157
-rw-r--r--private/nw/test/makefile6
-rw-r--r--private/nw/test/open.c144
-rw-r--r--private/nw/test/sources44
-rw-r--r--private/nw/test/testnw.c124
-rw-r--r--private/nw/test/tex.c740
-rw-r--r--private/nw/test/tp1.c1637
-rw-r--r--private/nw/test/tp2.c1560
-rw-r--r--private/nw/test/tp3.c1715
-rw-r--r--private/nw/test/tp4.c1668
-rw-r--r--private/nw/test/userncp.c246
-rw-r--r--private/nw/vwipxspx/dirs23
-rw-r--r--private/nw/vwipxspx/dll/makefile6
-rw-r--r--private/nw/vwipxspx/dll/socket.c2309
-rw-r--r--private/nw/vwipxspx/dll/socket.h318
-rw-r--r--private/nw/vwipxspx/dll/sources64
-rw-r--r--private/nw/vwipxspx/dll/util.c2966
-rw-r--r--private/nw/vwipxspx/dll/util.h233
-rw-r--r--private/nw/vwipxspx/dll/vw.h57
-rw-r--r--private/nw/vwipxspx/dll/vwasync.c167
-rw-r--r--private/nw/vwipxspx/dll/vwasync.h27
-rw-r--r--private/nw/vwipxspx/dll/vwdebug.c825
-rw-r--r--private/nw/vwipxspx/dll/vwdebug.h184
-rw-r--r--private/nw/vwipxspx/dll/vwdll.c502
-rw-r--r--private/nw/vwipxspx/dll/vwdll.h27
-rw-r--r--private/nw/vwipxspx/dll/vwdos.c1502
-rw-r--r--private/nw/vwipxspx/dll/vwinapi.c1185
-rw-r--r--private/nw/vwipxspx/dll/vwinapi.h186
-rw-r--r--private/nw/vwipxspx/dll/vwint.h146
-rw-r--r--private/nw/vwipxspx/dll/vwipx.c839
-rw-r--r--private/nw/vwipxspx/dll/vwipx.h122
-rw-r--r--private/nw/vwipxspx/dll/vwipxspx.def64
-rw-r--r--private/nw/vwipxspx/dll/vwipxspx.h586
-rw-r--r--private/nw/vwipxspx/dll/vwipxspx.rc11
-rw-r--r--private/nw/vwipxspx/dll/vwmisc.c74
-rw-r--r--private/nw/vwipxspx/dll/vwmisc.h27
-rw-r--r--private/nw/vwipxspx/dll/vwspx.c1339
-rw-r--r--private/nw/vwipxspx/dll/vwspx.h62
-rw-r--r--private/nw/vwipxspx/dll/vwvdm.h151
-rw-r--r--private/nw/vwipxspx/tsr/asmmacro.inc343
-rw-r--r--private/nw/vwipxspx/tsr/debugmac.inc356
-rw-r--r--private/nw/vwipxspx/tsr/makefile159
-rw-r--r--private/nw/vwipxspx/tsr/segorder.inc113
-rw-r--r--private/nw/vwipxspx/tsr/vwipxspx.asm711
459 files changed, 231385 insertions, 0 deletions
diff --git a/private/nw/convert/dirs b/private/nw/convert/dirs
new file mode 100644
index 000000000..75b12589b
--- /dev/null
+++ b/private/nw/convert/dirs
@@ -0,0 +1 @@
+DIRS=nwconv logview
diff --git a/private/nw/convert/logview/fv300.ico b/private/nw/convert/logview/fv300.ico
new file mode 100644
index 000000000..b68c9cb86
--- /dev/null
+++ b/private/nw/convert/logview/fv300.ico
Binary files differ
diff --git a/private/nw/convert/logview/fvfile.c b/private/nw/convert/logview/fvfile.c
new file mode 100644
index 000000000..466ec9c4c
--- /dev/null
+++ b/private/nw/convert/logview/fvfile.c
@@ -0,0 +1,231 @@
+/*
+ +-------------------------------------------------------------------------+
+ | MDI Text File Viewer - File IO Routines |
+ +-------------------------------------------------------------------------+
+ | (c) Copyright 1994 |
+ | Microsoft Corp. |
+ | All rights reserved |
+ | |
+ | Program : [mpfile.c] |
+ | Programmer : Arthur Hanson |
+ | Original Program Date : [Jul 27, 1993 |
+ | Last Update : [Jul 30, 1993] Time : 18:30 |
+ | |
+ | Version: 0.10 |
+ | |
+ | Description: |
+ | |
+ | History: |
+ | arth Jul 27, 1993 0.10 Original Version. |
+ | |
+ +-------------------------------------------------------------------------+
+*/
+
+#include "LogView.h"
+#include <fcntl.h>
+#include <SYS\types.h>
+#include <SYS\stat.h>
+#include <io.h>
+#include <string.h>
+
+VOID APIENTRY GetFileName(HWND hwnd, PSTR);
+
+OFSTRUCT of;
+
+
+/*+-------------------------------------------------------------------------+
+ | AlreadyOpen() |
+ | |
+ | Checks to see if a file is already opened. Returns a handle to |
+ | the file's window if it is opened, otherwise NULL. |
+ | |
+ +-------------------------------------------------------------------------+*/
+HWND AlreadyOpen(CHAR *szFile) {
+ INT iDiff;
+ HWND hwndCheck;
+ CHAR szChild[64];
+ LPSTR lpChild, lpFile;
+ HFILE wFileTemp;
+
+ // Open the file with the OF_PARSE flag to obtain the fully qualified
+ // pathname in the OFSTRUCT structure.
+ wFileTemp = OpenFile((LPSTR)szFile, (LPOFSTRUCT)&of, OF_PARSE);
+ if (! wFileTemp)
+ return(NULL);
+ _lclose(wFileTemp);
+
+ // Check each MDI child window in LogView
+ for ( hwndCheck = GetWindow(hwndMDIClient, GW_CHILD);
+ hwndCheck;
+ hwndCheck = GetWindow(hwndCheck, GW_HWNDNEXT) ) {
+ // Initialization for comparison
+ lpChild = szChild;
+ lpFile = (LPSTR)AnsiUpper((LPSTR) of.szPathName);
+ iDiff = 0;
+
+ // Skip icon title windows
+ if (GetWindow(hwndCheck, GW_OWNER))
+ continue;
+
+ // Get current child window's name
+ GetWindowText(hwndCheck, lpChild, 64);
+
+ // Compare window name with given name
+ while ((*lpChild) && (*lpFile) && (!iDiff)) {
+ if (*lpChild++ != *lpFile++)
+ iDiff = 1;
+ }
+
+ // If the two names matched, the file is already open - return handle to matching
+ // child window.
+ if (!iDiff)
+ return(hwndCheck);
+ }
+
+ // No match found -- file is not open -- return NULL handle
+ return(NULL);
+
+} // AlreadyOpen
+
+
+/*+-------------------------------------------------------------------------+
+ | AddFile() |
+ | |
+ | Create a new MDI Window, and loads specified file into Window. |
+ | |
+ +-------------------------------------------------------------------------+*/
+HWND APIENTRY AddFile(CHAR * pName) {
+ HWND hwnd;
+
+ CHAR sz[160];
+ MDICREATESTRUCT mcs;
+
+ if (!pName) {
+ // The pName parameter is NULL -- load the "Untitled" string from STRINGTABLE
+ // and set the title field of the MDI CreateStruct.
+ LoadString (hInst, IDS_UNTITLED, sz, sizeof(sz));
+ mcs.szTitle = (LPSTR)sz;
+ }
+ else
+ // Title the window with the fully qualified pathname obtained by calling
+ // OpenFile() with the OF_PARSE flag (in function AlreadyOpen(), which is called
+ // before AddFile().
+ mcs.szTitle = pName;
+
+ mcs.szClass = szChild;
+ mcs.hOwner = hInst;
+
+ // Use the default size for the window
+ mcs.x = mcs.cx = CW_USEDEFAULT;
+ mcs.y = mcs.cy = CW_USEDEFAULT;
+
+ // Set the style DWORD of the window to default
+ mcs.style = 0L;
+
+ // tell the MDI Client to create the child
+ hwnd = (HWND)SendMessage (hwndMDIClient,
+ WM_MDICREATE,
+ 0,
+ (LONG)(LPMDICREATESTRUCT)&mcs);
+
+ // Did we get a file? Read it into the window
+ if (pName){
+ if (!LoadFile(hwnd, pName)){
+ // File couldn't be loaded -- close window
+ SendMessage(hwndMDIClient, WM_MDIDESTROY, (DWORD) hwnd, 0L);
+ }
+ }
+
+ return hwnd;
+
+} // AddFile
+
+
+/*+-------------------------------------------------------------------------+
+ | LoadFile() |
+ | |
+ | Loads file into specified MDI window's edit control. |
+ | |
+ +-------------------------------------------------------------------------+*/
+INT APIENTRY LoadFile ( HWND hwnd, CHAR * pName) {
+ LONG wLength;
+ HANDLE hT;
+ LPSTR lpB;
+ HWND hwndEdit;
+ HFILE fh;
+ OFSTRUCT of;
+
+ hwndEdit = (HWND)GetWindowLong (hwnd, GWL_HWNDEDIT);
+
+ // The file has a title, so reset the UNTITLED flag.
+ SetWindowWord(hwnd, GWW_UNTITLED, FALSE);
+
+ fh = OpenFile(pName, &of, OF_READ); // JAP was 0, which is OF_READ)
+
+ // Make sure file has been opened correctly
+ if ( fh < 0 )
+ goto error;
+
+ // Find the length of the file
+ wLength = (DWORD)_llseek(fh, 0L, 2);
+ _llseek(fh, 0L, 0);
+
+ // Attempt to reallocate the edit control's buffer to the file size
+ hT = (HANDLE)SendMessage (hwndEdit, EM_GETHANDLE, 0, 0L);
+ if (LocalReAlloc(hT, wLength+1, LHND) == NULL) {
+ // Couldn't reallocate to new size -- error
+ _lclose(fh);
+ goto error;
+ }
+
+ // read the file into the buffer
+ if (wLength != (LONG)_lread(fh, (lpB = (LPSTR)LocalLock (hT)), (UINT)wLength))
+ MPError (hwnd, MB_OK|MB_ICONHAND, IDS_CANTREAD, (LPSTR)pName);
+
+ // Zero terminate the edit buffer
+ lpB[wLength] = 0;
+ LocalUnlock (hT);
+
+ SendMessage (hwndEdit, EM_SETHANDLE, (UINT)hT, 0L);
+ _lclose(fh);
+
+ return TRUE;
+
+error:
+ // Report the error and quit
+ MPError(hwnd, MB_OK | MB_ICONHAND, IDS_CANTOPEN, (LPSTR)pName);
+ return FALSE;
+
+} // LoadFile
+
+
+/*+-------------------------------------------------------------------------+
+ | MyReadFile() |
+ | |
+ | Asks user for a filename. |
+ | |
+ +-------------------------------------------------------------------------+*/
+VOID APIENTRY MyReadFile(HWND hwnd) {
+ CHAR szFile[128];
+ HWND hwndFile;
+
+ GetFileName (hwnd, szFile);
+
+ // If the result is not the empty string -- take appropriate action
+ if (*szFile) {
+ // Is file already open??
+ if (hwndFile = AlreadyOpen(szFile)) {
+ // Yes -- bring the file's window to the top
+ BringWindowToTop(hwndFile);
+ }
+ else {
+ // No -- make a new window and load file into it
+ AddFile(szFile);
+ }
+ }
+ UNREFERENCED_PARAMETER(hwnd);
+
+} // MyReadFile
+
+
+
diff --git a/private/nw/convert/logview/fvfind.c b/private/nw/convert/logview/fvfind.c
new file mode 100644
index 000000000..459469477
--- /dev/null
+++ b/private/nw/convert/logview/fvfind.c
@@ -0,0 +1,193 @@
+/*
+ +-------------------------------------------------------------------------+
+ | MDI Text File Viewer - Text Search Routines |
+ +-------------------------------------------------------------------------+
+ | (c) Copyright 1994 |
+ | Microsoft Corp. |
+ | All rights reserved |
+ | |
+ | Program : [FVFind.c] |
+ | Programmer : Arthur Hanson |
+ | Original Program Date : [Jul 27, 1993 |
+ | Last Update : [Jul 30, 1993] Time : 18:30 |
+ | |
+ | Version: 0.10 |
+ | |
+ | Description: |
+ | |
+ | History: |
+ | arth Jul 27, 1993 0.10 Original Version. |
+ | |
+ +-------------------------------------------------------------------------+
+*/
+
+#include "LogView.h"
+#include <string.h>
+#include <stdlib.h>
+
+#undef HIWORD
+#undef LOWORD
+
+#define HIWORD(l) (((WORD*)&(l))[1])
+#define LOWORD(l) (((WORD*)&(l))[0])
+
+BOOL fCase = FALSE; // Turn case sensitivity off
+CHAR szSearch[160] = ""; // Initialize search string
+
+
+extern HWND hDlgFind; /* handle to modeless FindText window */
+
+LPTSTR ReverseScan(
+ LPTSTR lpSource,
+ LPTSTR lpLast,
+ LPTSTR lpSearch,
+ BOOL fCaseSensitive ) {
+ int iLen = lstrlen(lpSearch);
+
+ if (!lpLast)
+ lpLast = lpSource + lstrlen(lpSource);
+
+ do {
+ if (lpLast == lpSource)
+ return NULL;
+
+ --lpLast;
+
+ if (fCaseSensitive) {
+ if (*lpLast != *lpSearch)
+ continue;
+ } else {
+ if (CharUpper ((LPTSTR)MAKELONG((WORD)*lpLast, 0)) != CharUpper ((LPTSTR)MAKELONG((WORD)*lpSearch, 0)))
+ continue;
+ }
+
+ if (fCaseSensitive) {
+ if (!strncmp( lpLast, lpSearch, iLen))
+ break;
+ } else {
+ if (!_strnicmp (lpLast, lpSearch, iLen))
+ break;
+ }
+ } while (TRUE);
+
+ return lpLast;
+} // ReverseScan
+
+
+LPTSTR ForwardScan(LPTSTR lpSource, LPTSTR lpSearch, BOOL fCaseSensitive ) {
+ int iLen = lstrlen(lpSearch);
+
+ while (*lpSource) {
+ if (fCaseSensitive) {
+ if (*lpSource != *lpSearch) {
+ lpSource++;
+ continue;
+ }
+ } else {
+ if (CharUpper ((LPTSTR)MAKELONG((WORD)*lpSource, 0)) != CharUpper ((LPTSTR)MAKELONG((WORD)*lpSearch, 0))) {
+ lpSource++;
+ continue;
+ }
+ }
+
+ if (fCaseSensitive) {
+ if (!strncmp( lpSource, lpSearch, iLen))
+ break;
+ } else {
+ if (!_strnicmp( lpSource, lpSearch, iLen))
+ break;
+ }
+
+ lpSource++;
+ }
+
+ return *lpSource ? lpSource : NULL;
+} // ForwardScan
+
+
+// search forward or backward in the edit control text for the given pattern
+void FAR Search (TCHAR * szKey) {
+ HANDLE hText;
+ TCHAR * pStart, *pMatch;
+ DWORD StartIndex, LineNum, EndIndex;
+ DWORD SelStart, SelEnd, i;
+ DWORD dwSel;
+ INT cbText;
+
+ if (!*szKey)
+ return;
+
+ SetCursor(hWaitCursor);
+ dwSel= SendMessage(hwndActiveEdit, EM_GETSEL, (WPARAM)&SelStart, (LPARAM)&SelEnd);
+
+ /*
+ * Allocate hText and read edit control text into it.
+ * Lock hText and fall through to existing code.
+ */
+
+ cbText= SendMessage(hwndActiveEdit, WM_GETTEXTLENGTH, 0, 0L) + 1;
+ hText= LocalAlloc( LPTR, cbText );
+ if( !hText ) // quiet exit if not enough memory
+ return;
+ if( !(pStart= LocalLock(hText)) ) {
+ LocalFree(hText);
+ return;
+ }
+
+ SendMessage(hwndActiveEdit, WM_GETTEXT, cbText, (LPARAM)pStart);
+
+ if (fReverse) {
+ // Get current line number
+ LineNum= SendMessage(hwndActiveEdit, EM_LINEFROMCHAR, SelStart, 0);
+ // Get index to start of the line
+ StartIndex= SendMessage(hwndActiveEdit, EM_LINEINDEX, LineNum, 0);
+ // Set upper limit for search text
+ EndIndex= SelStart;
+ pMatch= NULL;
+
+ // Search line by line, from LineNum to 0
+ i = LineNum;
+ while (TRUE) {
+ pMatch= ReverseScan(pStart+StartIndex,pStart+EndIndex,szKey,fCase);
+ if (pMatch)
+ break;
+ // current StartIndex is the upper limit for the next search
+ EndIndex= StartIndex;
+
+ if (i) {
+ // Get start of the next line
+ i-- ;
+ StartIndex= SendMessage(hwndActiveEdit, EM_LINEINDEX, i, 0);
+ } else
+ break ;
+ }
+ } else {
+ pMatch= ForwardScan(pStart + SelEnd, szKey, fCase);
+ }
+ LocalUnlock(hText);
+ LocalFree( hText );
+ SetCursor(hStdCursor);
+
+ if (pMatch == NULL) {
+ TCHAR Message[256], AppName[256] ;
+
+ if (!LoadString(hInst, IDS_CANTFINDSTR, Message,
+ sizeof(Message)/sizeof(Message[0]))) {
+ Message[0] = 0 ;
+ }
+
+ if (!LoadString(hInst, IDS_APPNAME, AppName,
+ sizeof(AppName)/sizeof(AppName[0]))) {
+ AppName[0] = 0 ;
+ }
+
+ MessageBox(hwndFrame, Message, AppName,
+ MB_APPLMODAL | MB_OK | MB_ICONASTERISK);
+ } else {
+ SelStart = pMatch - pStart;
+ SendMessage(hwndActiveEdit, EM_SETSEL, SelStart, SelStart+lstrlen(szKey));
+ SendMessage(hwndActiveEdit, EM_SCROLLCARET, 0, 0);
+ }
+
+} // Search
+
diff --git a/private/nw/convert/logview/fvinit.c b/private/nw/convert/logview/fvinit.c
new file mode 100644
index 000000000..11330fb2a
--- /dev/null
+++ b/private/nw/convert/logview/fvinit.c
@@ -0,0 +1,155 @@
+/*
+ +-------------------------------------------------------------------------+
+ | Initialization Code |
+ +-------------------------------------------------------------------------+
+ | (c) Copyright 1993 |
+ | Microsoft Corp. |
+ | All rights reserved |
+ | |
+ | Program : [mpinit.c] |
+ | Programmer : Arthur Hanson |
+ | Original Program Date : [Jul 27, 1993 |
+ | Last Update : [Jul 30, 1993] Time : 18:30 |
+ | |
+ | Version: 0.10 |
+ | |
+ | Description: |
+ | |
+ | History: |
+ | arth Jul 27, 1993 0.10 Original Version. |
+ | |
+ +-------------------------------------------------------------------------+
+*/
+#include "LogView.h"
+
+CHAR szFrame[] = "mpframe"; // Class name for "frame" window
+CHAR szChild[] = "mpchild"; // Class name for MDI window
+
+/*+-------------------------------------------------------------------------+
+ | InitializeApplication() |
+ | |
+ +-------------------------------------------------------------------------+*/
+BOOL APIENTRY InitializeApplication() {
+ WNDCLASS wc;
+
+ // Register the frame class
+ wc.style = 0;
+ wc.lpfnWndProc = (WNDPROC) MPFrameWndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = 0;
+ wc.hInstance = hInst;
+ wc.hIcon = LoadIcon(hInst,IDLOGVIEW);
+ wc.hCursor = LoadCursor(NULL,IDC_ARROW);
+ wc.hbrBackground = (HBRUSH) (COLOR_APPWORKSPACE+1);
+ wc.lpszMenuName = IDLOGVIEW;
+ wc.lpszClassName = szFrame;
+
+ if (!RegisterClass (&wc) )
+ return FALSE;
+
+ // Register the MDI child class
+ wc.lpfnWndProc = (WNDPROC) MPMDIChildWndProc;
+ wc.hIcon = LoadIcon(hInst,IDNOTE);
+ wc.lpszMenuName = NULL;
+ wc.cbWndExtra = CBWNDEXTRA;
+ wc.lpszClassName = szChild;
+
+ if (!RegisterClass(&wc))
+ return FALSE;
+
+ return TRUE;
+
+} // InitializeApplication
+
+
+/*+-------------------------------------------------------------------------+
+ | InitializeInstance() |
+ | |
+ +-------------------------------------------------------------------------+*/
+BOOL APIENTRY InitializeInstance(LPSTR lpCmdLine, INT nCmdShow) {
+ extern HWND hwndMDIClient;
+ CHAR sz[80], *pCmdLine, *pFileName, *pChar;
+ HDC hdc;
+ HMENU hmenu;
+
+ // Get the base window title
+ LoadString (hInst, IDS_APPNAME, sz, sizeof(sz));
+
+ hStdCursor= LoadCursor( NULL,IDC_ARROW );
+ hWaitCursor= LoadCursor( NULL, IDC_WAIT );
+
+ // Create the frame
+ hwndFrame = CreateWindow (szFrame, sz, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
+ CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL,
+ NULL, hInst, NULL);
+
+ if ((!hwndFrame) || (!hwndMDIClient))
+ return FALSE;
+
+ // Load main menu accelerators
+ if (!(hAccel = LoadAccelerators (hInst, IDLOGVIEW)))
+ return FALSE;
+
+ // init.fields of the FINDREPLACE struct used by FindText()
+ FR.lStructSize = sizeof(FINDREPLACE);
+ FR.hwndOwner = hwndFrame;
+ FR.Flags = FR_DOWN | FR_HIDEWHOLEWORD;
+ FR.lpstrReplaceWith = (LPTSTR)NULL;
+ FR.wReplaceWithLen = 0;
+ FR.lpfnHook = NULL;
+
+ /* determine the message number to be used for communication with
+ * Find dialog
+ */
+ if (!(wFRMsg = RegisterWindowMessage ((LPTSTR)FINDMSGSTRING)))
+ return FALSE;
+ if (!(wHlpMsg = RegisterWindowMessage ((LPTSTR)HELPMSGSTRING)))
+ return FALSE;
+
+ // Display the frame window
+ ShowWindow (hwndFrame, nCmdShow);
+ UpdateWindow (hwndFrame);
+
+ // If the command line string is empty, nullify the pointer to it else copy
+ // command line into our data segment
+ if ( lpCmdLine && !(*lpCmdLine)) {
+ pCmdLine = NULL;
+
+ // Add the first MDI window
+ AddFile (pCmdLine);
+
+ } else {
+ pCmdLine = (CHAR *) LocalAlloc(LPTR, lstrlen(lpCmdLine) + 1);
+
+ if (pCmdLine) {
+ lstrcpy(pCmdLine, lpCmdLine);
+
+ pFileName = pChar = pCmdLine;
+
+ while (*pChar) {
+ if (*pChar == ' ') {
+ *pChar = '\0';
+ AddFile(pFileName);
+ *pChar = ' ';
+ pChar++;
+ pFileName = pChar;
+ } else
+ pChar++;
+ }
+ AddFile(pFileName);
+
+ } else
+
+ // Add the first MDI window
+ AddFile (pCmdLine);
+ }
+
+ // if we allocated a buffer then free it
+ if (pCmdLine)
+ LocalFree((LOCALHANDLE) pCmdLine);
+
+ return TRUE;
+ UNREFERENCED_PARAMETER(hmenu);
+ UNREFERENCED_PARAMETER(hdc);
+
+} // InitializeInstance
diff --git a/private/nw/convert/logview/fvopen.c b/private/nw/convert/logview/fvopen.c
new file mode 100644
index 000000000..97a0458eb
--- /dev/null
+++ b/private/nw/convert/logview/fvopen.c
@@ -0,0 +1,131 @@
+/*
+ +-------------------------------------------------------------------------+
+ | MDI Text File View - File open Functions |
+ +-------------------------------------------------------------------------+
+ | (c) Copyright 1994 |
+ | Microsoft Corp. |
+ | All rights reserved |
+ | |
+ | Program : [FVOpen.c] |
+ | Programmer : Arthur Hanson |
+ | Original Program Date : [Feb 11, 1994] |
+ | Last Update : [Feb 11, 1994] |
+ | |
+ | Version: 0.10 |
+ | |
+ | Description: |
+ | |
+ | History: |
+ | arth Jul 27, 1993 0.10 Original Version. |
+ | |
+ +-------------------------------------------------------------------------+
+*/
+
+#include "LogView.h"
+#include <fcntl.h>
+#include <io.h>
+#include <string.h>
+
+#define MAXFILENAME 256
+
+
+CHAR szPropertyName [] = "FILENAME"; // Name of the File name property list item
+
+
+/////////////////////////////////////////////////////////////////////////
+BOOL
+FileExists(
+ PSTR pch
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ int fh;
+
+ if ((fh = _open(pch, O_RDONLY)) < 0)
+ return(FALSE);
+
+ _lclose(fh);
+ return(TRUE);
+} // FileExists
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID APIENTRY
+GetFileName(
+ HWND hwnd,
+ PSTR pstr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ CHAR szFmt[128];
+ OPENFILENAME ofn;
+ CHAR szFilterSpec[128];
+ CHAR szDefExt[10];
+ CHAR szFileName[MAXFILENAME];
+ CHAR szFileTitle[MAXFILENAME];
+
+ strcpy(szFileName, ""); // these need be NULL
+ strcpy(szFileTitle, "");
+ memset(&ofn,0,sizeof(ofn)) ;
+ memset(szFilterSpec,0,sizeof(szFilterSpec)) ;
+
+ LoadString (hInst, (WORD)IDS_OPENTEXT,
+ (LPSTR)szFmt, sizeof (szFmt));
+ LoadString (hInst, (WORD)IDS_OPENFILTER,
+ (LPSTR)szFilterSpec, sizeof (szFilterSpec));
+
+
+ ofn.lStructSize = sizeof(OPENFILENAME);
+ ofn.hwndOwner = hwnd;
+ ofn.lpstrFilter = szFilterSpec;
+ ofn.lpstrCustomFilter = NULL;
+ ofn.nMaxCustFilter = 0;
+ ofn.nFilterIndex = 0;
+ ofn.lpstrFile = szFileName;
+ ofn.nMaxFile = MAXFILENAME;
+ ofn.lpstrInitialDir = NULL;
+ ofn.lpstrFileTitle = szFileTitle;
+ ofn.nMaxFileTitle = MAXFILENAME;
+ ofn.lpstrTitle = szFmt;
+
+ LoadString (hInst, (WORD)IDS_DEFEXT, (LPSTR)szDefExt, sizeof (szDefExt));
+ ofn.lpstrDefExt = szDefExt;
+ ofn.Flags = OFN_FILEMUSTEXIST;
+
+ // Use standard open dialog
+ if (!GetOpenFileName ((LPOPENFILENAME)&ofn)) {
+ *pstr = 0;
+ }
+ else {
+ strcpy(pstr, ofn.lpstrFile);
+ }
+
+ return;
+
+} // GetFileName
diff --git a/private/nw/convert/logview/fvprint.c b/private/nw/convert/logview/fvprint.c
new file mode 100644
index 000000000..675c46c8d
--- /dev/null
+++ b/private/nw/convert/logview/fvprint.c
@@ -0,0 +1,389 @@
+/*
+ +-------------------------------------------------------------------------+
+ | MDI Text File Viewer - Printing Routines |
+ +-------------------------------------------------------------------------+
+ | (c) Copyright 1994 |
+ | Microsoft Corp. |
+ | All rights reserved |
+ | |
+ | Program : [FVPrint.c] |
+ | Programmer : Arthur Hanson |
+ | Original Program Date : [Feb 11, 1994] |
+ | Last Update : [Feb 11, 1994] |
+ | |
+ | Version: 0.10 |
+ | |
+ | Description: |
+ | |
+ | History: |
+ | arth Jul 27, 1993 0.10 Original Version. |
+ | |
+ +-------------------------------------------------------------------------+
+*/
+
+#include "LogView.h"
+
+BOOL fAbort;
+HWND hwndPDlg;
+CHAR szDevice[160];
+PSTR szDriver;
+PSTR szPort;
+PSTR szTitle;
+INT iPrinter = 0; // level of available printer support.
+ // 0 - no printer available
+ // 1 - printer available
+ // 2 - driver supports 3.0 device initialization
+HANDLE hInitData=NULL;
+
+CHAR szExtDeviceMode[] = "EXTDEVICEMODE";
+
+
+/*+-------------------------------------------------------------------------+
+ | GetPrinterDC() |
+ | |
+ | Creates a printer display context for the default device. As a side |
+ | effect, it sets the szDevice and szPort variables. It also sets |
+ | iPrinter to the supported level of printing. |
+ | |
+ +-------------------------------------------------------------------------+*/
+HDC APIENTRY GetPrinterDC(BOOL bInformation) {
+ HDC hdc;
+ LPDEVMODE lpdevmode = NULL;
+
+ iPrinter = 0;
+
+ // Get the printer information from win.ini into a buffer and null terminate it.
+ GetProfileString ( TEXT("windows"), TEXT("device"), TEXT(""), szDevice, sizeof(szDevice));
+ for (szDriver = szDevice; *szDriver && *szDriver != TEXT(','); szDriver++)
+ ;
+ if (*szDriver)
+ *szDriver++ = 0;
+
+ // From the current position in the buffer, null teminate the list of ports
+ for (szPort = szDriver; *szPort && *szPort != TEXT(','); szPort++)
+ ;
+ if (*szPort)
+ *szPort++ = 0;
+
+ // if the device, driver and port buffers all contain meaningful data, proceed.
+ if (!*szDevice || !*szDriver || !*szPort){
+ *szDevice = 0;
+ return NULL;
+ }
+
+ // Create the printer display context
+ if (hInitData){
+ // Get a pointer to the initialization data
+ lpdevmode = (LPDEVMODE) LocalLock (hInitData);
+
+ if (lstrcmp (szDevice, (LPSTR)lpdevmode)) {
+ // User has changed the device... cancel this setup, as it is invalid
+ // (although if we worked harder we could retain some of it).
+ lpdevmode = NULL;
+ LocalUnlock (hInitData);
+ LocalFree (hInitData);
+ hInitData = NULL;
+ }
+ }
+
+ if (bInformation)
+ hdc = CreateIC (szDriver, szDevice, szPort, lpdevmode);
+ else
+ hdc = CreateDC (szDriver, szDevice, szPort, lpdevmode);
+
+ // Unlock initialization data
+ if (hInitData)
+ LocalUnlock (hInitData);
+
+ if (!hdc)
+ return NULL;
+
+
+ iPrinter = 1;
+
+ // Find out if ExtDeviceMode() is supported and set flag appropriately
+ if (GetProcAddress (LoadLibrary(szDriver), szExtDeviceMode))
+ iPrinter = 2;
+
+ return hdc;
+
+} // GetPrinterDC
+
+
+/*+-------------------------------------------------------------------------+
+ | AbortProc() |
+ | |
+ | Checks for user abort. |
+ | |
+ +-------------------------------------------------------------------------+*/
+INT APIENTRY AbortProc ( HDC hdc, WORD reserved) {
+ MSG msg;
+
+ // Allow other apps to run, or get abort messages
+ while (!fAbort && PeekMessage (&msg, NULL, 0, 0, TRUE))
+ if (!hwndPDlg || !IsDialogMessage (hwndPDlg, &msg)) {
+ TranslateMessage (&msg);
+ DispatchMessage (&msg);
+ }
+
+ return !fAbort;
+
+ UNREFERENCED_PARAMETER(hdc);
+ UNREFERENCED_PARAMETER(reserved);
+
+} // AbortProc
+
+
+/*+-------------------------------------------------------------------------+
+ | PrintDlgProc() |
+ | |
+ | Print/Cancel dialog box. |
+ | |
+ +-------------------------------------------------------------------------+*/
+BOOL APIENTRY PrintDlgProc(HWND hwnd, UINT msg, WORD wParam, LONG lParam) {
+ switch (msg) {
+ case WM_INITDIALOG:
+ // Set up information in dialog box
+ SetDlgItemText (hwnd, IDD_PRINTTITLE, (LPSTR)szTitle);
+ break;
+
+ case WM_COMMAND:
+ // abort printing if the only button gets hit
+ fAbort = TRUE;
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+
+ UNREFERENCED_PARAMETER(wParam);
+ UNREFERENCED_PARAMETER(lParam);
+} // PrintDlgProc
+
+
+/*+-------------------------------------------------------------------------+
+ | PrintFile() |
+ | |
+ | Prints the contents of the edit control. |
+ | |
+ +-------------------------------------------------------------------------+*/
+VOID APIENTRY PrintFile(HWND hwnd) {
+ HDC hdc;
+ INT yExtPage;
+ CHAR sz[32];
+ int cch;
+ WORD ich;
+ PSTR pch;
+ WORD iLine;
+ WORD nLinesEc;
+ WORD i;
+ HANDLE hT;
+ HWND hwndPDlg;
+ DWORD dy;
+ INT yExtSoFar;
+ WORD fError = TRUE;
+ HWND hwndEdit;
+ HFONT hFont, hOldFont;
+
+ hwndEdit = (HWND)GetWindowLong(hwnd,GWL_HWNDEDIT);
+
+ // Create the job title by loading the title string from STRINGTABLE
+ cch = LoadString (hInst, IDS_PRINTJOB, sz, sizeof(sz));
+ szTitle = sz + cch;
+ cch += GetWindowText (hwnd, sz + cch, 32 - cch);
+ sz[31] = 0;
+
+ // Initialize the printer
+ hdc = GetPrinterDC(FALSE);
+ if (!hdc)
+ goto getout5;
+
+ SetMapMode(hdc, MM_TEXT);
+ SelectObject(hdc, GetStockObject(ANSI_FIXED_FONT));
+
+ // Disable the main application window and create the Cancel dialog
+ EnableWindow (hwndFrame, FALSE);
+
+ hwndPDlg = CreateDialog (hInst, IDD_PRINT, hwnd, (DLGPROC) PrintDlgProc);
+
+ if (!hwndPDlg)
+ goto getout3;
+
+ ShowWindow (hwndPDlg, SW_SHOW);
+ UpdateWindow (hwndPDlg);
+
+ // Allow the app. to inform GDI of the escape function to call
+ if (Escape(hdc, SETABORTPROC, 0, (LPSTR)AbortProc, NULL) < 0)
+ goto getout1;
+
+ // Initialize the document
+ if (Escape(hdc, STARTDOC, cch, (LPSTR)sz, NULL) < 0)
+ goto getout1;
+
+ // Get the height of one line and the height of a page
+ {
+ SIZE tmp;
+ GetTextExtentPoint(hdc, "CC", 2, &tmp );
+ dy = tmp.cy;
+ }
+
+ yExtPage = GetDeviceCaps(hdc, VERTRES);
+
+ // Get the lines in document and and a handle to the text buffer
+ iLine = 0;
+ yExtSoFar = 0;
+ nLinesEc = (WORD)SendMessage (hwndEdit, EM_GETLINECOUNT, 0, 0L);
+ hT = (HANDLE)SendMessage (hwndEdit, EM_GETHANDLE, 0, 0L);
+
+ // While more lines print out the text
+ while (iLine < nLinesEc) {
+ if (yExtSoFar + (int) dy > yExtPage) {
+ // Reached the end of a page. Tell the device driver to eject a page
+ if (Escape(hdc, NEWFRAME, 0, NULL, NULL) < 0 || fAbort)
+ goto getout2;
+ yExtSoFar = 0;
+ }
+
+ // Get the length and position of the line in the buffer and lock from that
+ // offset into the buffer.
+ ich = (WORD)SendMessage (hwndEdit, EM_LINEINDEX, iLine, 0L);
+ cch = (WORD)SendMessage (hwndEdit, EM_LINELENGTH, ich, 0L);
+ pch = (PSTR)LocalLock(hT) + ich;
+
+ // Print the line and unlock the text handle
+ TextOut (hdc, 0, yExtSoFar, (LPSTR)pch, cch);
+ LocalUnlock (hT);
+
+ // Test and see if the Abort flag has been set. If yes, exit.
+ if (fAbort)
+ goto getout2;
+
+ // Move down the page
+ yExtSoFar += dy;
+ iLine++;
+ }
+
+ // Eject the last page.
+ if (Escape(hdc, NEWFRAME, 0, NULL, NULL) < 0)
+ goto getout2;
+
+ // Complete the document.
+ if (Escape(hdc, ENDDOC, 0, NULL, NULL) < 0) {
+getout2:
+ // Ran into a problem before NEWFRAME? Abort the document
+ Escape( hdc, ABORTDOC, 0, NULL, NULL);
+ } else
+ fError=FALSE;
+
+getout3:
+ // Close the cancel dialog and re-enable main app. window
+ EnableWindow (hwndFrame, TRUE);
+ DestroyWindow (hwndPDlg);
+
+getout1:
+ DeleteDC(hdc);
+
+getout5:
+#ifdef WIN16
+ // Get rid of dialog procedure instances
+ FreeProcInstance (lpfnPDlg);
+#endif
+
+#ifdef WIN16
+getout4:
+ FreeProcInstance (lpfnAbort);
+getout:
+#endif
+
+ // Error? make sure the user knows...
+ if (fError)
+ MPError (hwnd, MB_OK | MB_ICONEXCLAMATION, IDS_PRINTERROR, (LPSTR)szTitle);
+
+ return;
+ UNREFERENCED_PARAMETER(i);
+
+} // PrintFile
+
+
+/*+-------------------------------------------------------------------------+
+ | GetInitializationData() |
+ | |
+ | Gets DC initialization data from a printer driver supporting |
+ | ExtDeviceMode(). Called in response to the File/Printer setup menu |
+ | selection. |
+ | |
+ | This function allows the user to change the printer settings FOR |
+ | LOGVIEW ONLY. This allows LogView to print in a variety of settings |
+ | without messing up any othe applications. |
+ | |
+ +-------------------------------------------------------------------------+*/
+BOOL APIENTRY GetInitializationData( HWND hwnd ) {
+ LPSTR lpOld;
+ LPSTR lpNew;
+ FARPROC lpfn;
+ HANDLE hT,hDrv;
+ CHAR sz[32];
+ int cb;
+ INT flag;
+
+ // Pop up dialog for user and retain data in app buffer
+ flag = DM_PROMPT | DM_COPY;
+
+ // Load the device driver and find the ExtDeviceMode() function
+ wsprintf (sz, "%s.drv", (LPSTR)szDriver);
+ if ((int)(hDrv = LoadLibrary (sz)) < 32)
+ return FALSE;
+ if (!(lpfn = (DLGPROC) GetProcAddress (hDrv, szExtDeviceMode)))
+ return FALSE;
+
+ if (hInitData) {
+ // We have some old data... we want to modify the previously specified
+ // setup rather than starting with the default setup.
+ lpOld = (LPSTR)LocalLock(hInitData);
+ flag |= DM_MODIFY;
+ }
+ else
+ lpOld = NULL;
+
+ // Get the number of bytes needed for the init data
+ cb = (*lpfn) (hwnd, hDrv, (LPDEVMODE)NULL, (LPSTR)szDevice, (LPSTR)szPort, (LPDEVMODE)NULL, (LPSTR)NULL, 0);
+
+ // Grab some memory for the new data and lock it.
+ hT = LocalAlloc (LHND,cb);
+ if(!hT) {
+ MessageBox(hwnd, TEXT("<GetInitializationData> Not enough memory."), NULL, MB_OK | MB_ICONHAND);
+ LocalUnlock(hInitData);
+ LocalFree(hInitData);
+ FreeLibrary(hDrv);
+ return(FALSE);
+ }
+
+ lpNew = (LPSTR)LocalLock (hT);
+
+ // Post the device mode dialog. 0 flag iff user hits OK button
+ if ((*lpfn) (hwnd, hDrv, (LPDEVMODE)lpNew, (LPSTR)szDevice, (LPSTR)szPort, (LPDEVMODE)lpOld, (LPSTR)NULL, flag) == IDOK)
+ flag = 0;
+
+ // Unlock the input structures
+ LocalUnlock (hT);
+
+ if (hInitData)
+ LocalUnlock (hInitData);
+
+ // If the user hit OK and everything worked, free the original init. data and
+ // retain the new one. Otherwise, toss the new buffer.
+ if (flag)
+ LocalFree (hT);
+ else {
+ if (hInitData)
+ LocalFree (hInitData);
+
+ hInitData = hT;
+ }
+
+ FreeLibrary(hDrv);
+ return (!flag);
+
+} // GetInitializationData
diff --git a/private/nw/convert/logview/logview.c b/private/nw/convert/logview/logview.c
new file mode 100644
index 000000000..a682edec0
--- /dev/null
+++ b/private/nw/convert/logview/logview.c
@@ -0,0 +1,767 @@
+/*++
+
+Copyright (c) 1993-1995 Microsoft Corporation
+
+Module Name:
+
+ LogView.C
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) 27-Jul-1993
+
+Revision History:
+
+--*/
+
+#include "LogView.h"
+#include <string.h>
+#include <stdio.h>
+//#include <dos.h>
+//#include <direct.h>
+#include <shellapi.h>
+
+// global variables used in this module or among more than one module
+HANDLE hInst;
+HANDLE hAccel;
+HWND hwndFrame = NULL;
+HWND hwndMDIClient = NULL;
+HWND hwndActive = NULL;
+HWND hwndActiveEdit = NULL;
+HWND hDlgFind = NULL;
+LPSTR lpMenu = IDLOGVIEW;
+TCHAR szAppName[] = "LogView";
+
+FINDREPLACE FR;
+PRINTDLG PD;
+UINT wFRMsg;
+UINT wHlpMsg;
+BOOL fReverse = FALSE; // Flag for direction of search
+TCHAR szSearch[CCHKEYMAX]; // Search String
+
+HANDLE hStdCursor; // handle to arrow or beam cursor
+HANDLE hWaitCursor; // handle to hour glass cursor
+
+void FAR Search (TCHAR * szKey);
+
+
+// Forward declarations of helper functions in this module
+VOID NEAR PASCAL InitializeMenu (HANDLE);
+VOID NEAR PASCAL CommandHandler (HWND, UINT, LONG);
+LPSTR GetCmdLine( VOID );
+BOOL CenterWindow( HWND hwndChild, HWND hwndParent );
+
+#define HELP_FILE TEXT("logview.hlp")
+
+/////////////////////////////////////////////////////////////////////////
+int PASCAL
+WinMain(
+ HINSTANCE hInstance,
+ HINSTANCE hPrevInstance,
+ LPSTR lpCmdLine,
+ int nCmdShow
+ )
+
+/*++
+
+Routine Description:
+
+ Creates the "frame" window, does some initialization and enters the
+ message loop.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ MSG msg;
+
+ hInst = hInstance;
+
+ // If this is the first instance of the app. register window classes
+ if (!hPrevInstance){
+ if (!InitializeApplication ())
+ return 0;
+ }
+
+ lpCmdLine = GetCmdLine();
+
+ // Create the frame and do other initialization
+ if (!InitializeInstance (lpCmdLine, nCmdShow))
+ return 0;
+
+ while (GetMessage (&msg, NULL, 0, 0)){
+ // If a keyboard message is for the MDI , let the MDI client take care of it.
+ // Otherwise, check to see if it's a normal accelerator key (like F3 = find next).
+ // Otherwise, just handle the message as usual.
+ if (!hDlgFind || !IsDialogMessage(hDlgFind, &msg)) {
+ if ( !TranslateMDISysAccel (hwndMDIClient, &msg) &&
+ !TranslateAccelerator (hwndFrame, hAccel, &msg)) {
+ TranslateMessage (&msg);
+ DispatchMessage (&msg);
+ }
+ }
+ }
+
+ return 0;
+
+} // WinMain
+
+
+/////////////////////////////////////////////////////////////////////////
+LONG APIENTRY
+MPFrameWndProc (
+ HWND hwnd,
+ UINT msg,
+ UINT wParam,
+ LONG lParam
+ )
+
+/*++
+
+Routine Description:
+
+ The window function for the "frame" window, which controls the menu
+ and encompasses all the MDI child windows.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ LPFINDREPLACE lpfr;
+ DWORD dwFlags;
+
+ switch (msg) {
+ case WM_CREATE: {
+
+ CLIENTCREATESTRUCT ccs;
+ HDC hdc;
+
+ // Find window menu where children will be listed
+ ccs.hWindowMenu = GetSubMenu (GetMenu(hwnd), WINDOWMENU);
+ ccs.idFirstChild = IDM_WINDOWCHILD;
+
+ // Create the MDI client filling the client area
+ hwndMDIClient = CreateWindow ("mdiclient", NULL,
+ WS_CHILD | WS_CLIPCHILDREN | WS_VSCROLL | WS_HSCROLL,
+ 0, 0, 0, 0, hwnd, (HMENU) 0xCAC, hInst, (LPSTR) &ccs);
+
+ ShowWindow (hwndMDIClient,SW_SHOW);
+
+ // Check if printer can be initialized
+ if (hdc = GetPrinterDC (TRUE)) {
+ DeleteDC (hdc);
+ }
+
+ break;
+ }
+
+ case WM_INITMENU:
+ // Set up the menu state
+ InitializeMenu ((HMENU)wParam);
+ break;
+
+ case WM_WININICHANGE:
+ case WM_DEVMODECHANGE:{
+
+ // If control panel changes default printer characteristics, reinitialize our
+ // printer information...
+ HDC hdc;
+
+ if (hdc = GetPrinterDC (TRUE))
+ DeleteDC (hdc);
+
+ break;
+ }
+
+ case WM_COMMAND:
+ // Direct all menu selection or accelerator commands to another function
+ CommandHandler(hwnd, wParam, lParam);
+ break;
+
+ case WM_CLOSE:
+ DestroyWindow (hwnd);
+ break;
+
+ case WM_DESTROY:
+ PostQuitMessage (0);
+ break;
+
+ default:
+ if (msg == wFRMsg)
+ {
+ lpfr = (LPFINDREPLACE)lParam;
+ dwFlags = lpfr->Flags;
+
+ fReverse = (dwFlags & FR_DOWN ? FALSE : TRUE);
+ fCase = (dwFlags & FR_MATCHCASE ? TRUE : FALSE);
+
+ if (dwFlags & FR_FINDNEXT)
+ Search (szSearch);
+ else if (dwFlags & FR_DIALOGTERM)
+ hDlgFind = NULL; /* invalidate modeless window handle */
+ break;
+ }
+
+ // use DefFrameProc() instead of DefWindowProc() since there are things
+ // that have to be handled differently because of MDI
+ return DefFrameProc (hwnd,hwndMDIClient,msg,wParam,lParam);
+ }
+
+ return 0;
+
+} // MPFrameWndProc
+
+
+/////////////////////////////////////////////////////////////////////////
+LONG APIENTRY
+MPMDIChildWndProc (
+ HWND hwnd,
+ UINT msg,
+ UINT wParam,
+ LONG lParam
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ HWND hwndEdit;
+ HFONT hFont;
+ LRESULT ret;
+
+ switch (msg) {
+ case WM_CREATE:
+ hwndEdit = CreateWindow ("edit", NULL,
+ WS_CHILD | WS_HSCROLL | WS_MAXIMIZE | WS_VISIBLE |
+ WS_VSCROLL | ES_AUTOHSCROLL | ES_AUTOVSCROLL |
+ ES_MULTILINE | ES_READONLY | ES_NOHIDESEL,
+ 0, 0, 0, 0,
+ hwnd, (HMENU) ID_EDIT, hInst, NULL);
+
+ // Remember the window handle and initialize some window attributes
+ SetWindowLong (hwnd, GWL_HWNDEDIT, (LONG) hwndEdit);
+ SetWindowWord (hwnd, GWW_CHANGED, FALSE);
+ SetWindowWord (hwnd, GWL_WORDWRAP, FALSE);
+ SetWindowWord (hwnd, GWW_UNTITLED, TRUE);
+
+ hFont = GetStockObject(SYSTEM_FIXED_FONT);
+ ret = SendMessage(hwndEdit, WM_SETFONT, (WPARAM) hFont, (LPARAM) MAKELONG((WORD) TRUE, 0));
+
+ SetFocus (hwndEdit);
+ break;
+
+ case WM_MDIACTIVATE:
+ // If we're activating this child, remember it
+ if (GET_WM_MDIACTIVATE_FACTIVATE(hwnd, wParam, lParam)) {
+ hwndActive = hwnd;
+ hwndActiveEdit = (HWND)GetWindowLong (hwnd, GWL_HWNDEDIT);
+ }
+ else {
+ hwndActive = NULL;
+ hwndActiveEdit = NULL;
+ }
+ break;
+
+ case WM_CLOSE:
+ goto CallDCP;
+
+ case WM_SIZE:{
+ RECT rc;
+
+ // On creation or resize, size the edit control.
+ hwndEdit = (HWND)GetWindowLong (hwnd, GWL_HWNDEDIT);
+ GetClientRect (hwnd, &rc);
+ MoveWindow (hwndEdit,
+ rc.left,
+ rc.top,
+ rc.right-rc.left,
+ rc.bottom-rc.top,
+ TRUE);
+ goto CallDCP;
+ }
+
+ case WM_SETFOCUS:
+ SetFocus ((HWND)GetWindowLong (hwnd, GWL_HWNDEDIT));
+ break;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)){
+ case ID_EDIT:
+ switch (GET_WM_COMMAND_CMD(wParam, lParam)) {
+
+ case EN_ERRSPACE:
+ // If the control is out of space, beep
+ MessageBeep (0);
+ break;
+
+ default:
+ goto CallDCP;
+ }
+ break;
+
+ default:
+ goto CallDCP;
+ }
+ break;
+
+ default:
+CallDCP:
+ return DefMDIChildProc (hwnd, msg, wParam, lParam);
+ }
+ return FALSE;
+
+} // MPMDIChildWndProc
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID NEAR PASCAL
+InitializeMenu (
+ register HANDLE hmenu
+ )
+
+/*++
+
+Routine Description:
+
+ Sets up greying, enabling and checking of main menu items.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ WORD status;
+ WORD i;
+ INT j;
+
+ // Is there any active child to talk to?
+ if (hwndActiveEdit) {
+
+ // Set the word wrap state for the window
+ if ((WORD) SendMessage(hwndActive, WM_COMMAND, GET_WM_COMMAND_MPS(IDM_EDITWRAP, 0, 0)))
+ status = MF_CHECKED;
+ else
+ status = MF_UNCHECKED;
+
+ CheckMenuItem (hmenu, IDM_EDITWRAP, status);
+
+ // Enable search menu items only if there is a search string
+ if (*szSearch)
+ status = MF_ENABLED;
+ else
+ status = MF_GRAYED;
+
+ EnableMenuItem (hmenu, IDM_SEARCHNEXT, status);
+ EnableMenuItem (hmenu, IDM_SEARCHPREV, status);
+
+ // Enable File/Print only if a printer is available
+ status = (WORD) (iPrinter ? MF_ENABLED : MF_GRAYED);
+ EnableMenuItem (hmenu, IDM_FILEPRINT, status);
+
+ // select all and wrap toggle always enabled
+ status = MF_ENABLED;
+ EnableMenuItem(hmenu, IDM_EDITSELECT, status);
+ EnableMenuItem(hmenu, IDM_EDITWRAP, status);
+ EnableMenuItem(hmenu, IDM_SEARCHFIND, status);
+ } else {
+ // There are no active child windows
+ status = MF_GRAYED;
+
+ // No active window, so disable everything
+ for (i = IDM_EDITFIRST; i <= IDM_EDITLAST; i++)
+ EnableMenuItem (hmenu, i, status);
+
+ CheckMenuItem (hmenu, IDM_EDITWRAP, MF_UNCHECKED);
+
+ for (i = IDM_SEARCHFIRST; i <= IDM_SEARCHLAST; i++)
+ EnableMenuItem (hmenu, i, status);
+
+ EnableMenuItem (hmenu, IDM_FILEPRINT, status);
+
+ }
+
+ // The following menu items are enabled if there is an active window
+ EnableMenuItem (hmenu, IDM_WINDOWTILE, status);
+ EnableMenuItem (hmenu, IDM_WINDOWCASCADE, status);
+ EnableMenuItem (hmenu, IDM_WINDOWICONS, status);
+ EnableMenuItem (hmenu, IDM_WINDOWCLOSEALL, status);
+
+ // Allow printer setup only if printer driver supports device initialization
+ if (iPrinter < 2)
+ status = MF_GRAYED;
+
+ EnableMenuItem ( hmenu, IDM_FILESETUP, status);
+ UNREFERENCED_PARAMETER(j);
+
+} // InitializeMenu
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID NEAR PASCAL
+CloseAllChildren ()
+
+/*++
+
+Routine Description:
+
+ Destroys all MDI child windows.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ register HWND hwndT;
+
+ // hide the MDI client window to avoid multiple repaints
+ ShowWindow(hwndMDIClient,SW_HIDE);
+
+ // As long as the MDI client has a child, destroy it
+ while ( hwndT = GetWindow (hwndMDIClient, GW_CHILD)){
+
+ // Skip the icon title windows
+ while (hwndT && GetWindow (hwndT, GW_OWNER))
+ hwndT = GetWindow (hwndT, GW_HWNDNEXT);
+
+ if (!hwndT)
+ break;
+
+ SendMessage (hwndMDIClient, WM_MDIDESTROY, (UINT)hwndT, 0L);
+ }
+
+} // CloseAllChildren
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID NEAR PASCAL
+CommandHandler (
+ HWND hwnd,
+ UINT wParam,
+ LONG lParam
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ DLGPROC lpfnDlg;
+
+ switch (LOWORD(wParam)){
+ case IDM_FILENEW:
+ // Add a new, empty MDI child
+ AddFile (NULL);
+ break;
+
+ case IDM_FILEOPEN:
+ MyReadFile (hwnd);
+ break;
+
+ case IDM_FILEPRINT:
+ // Print the active child MDI
+ PrintFile (hwndActive);
+ break;
+
+ case IDM_FILESETUP:
+ // Set up the printer environment for this app
+ GetInitializationData (hwnd);
+ break;
+
+ case IDM_FILEMENU: {
+ // lengthen / shorten the size of the MDI menu
+ HMENU hMenu;
+ HMENU hWindowMenu;
+ INT i;
+
+ if (lpMenu == IDLOGVIEW) {
+ lpMenu = IDLOGVIEW2;
+ i = SHORTMENU;
+ }
+ else {
+ lpMenu = IDLOGVIEW;
+ i = WINDOWMENU;
+ }
+
+ hMenu = LoadMenu (hInst, lpMenu);
+ hWindowMenu = GetSubMenu (hMenu, i);
+
+ // Set the new menu
+ hMenu = (HMENU)SendMessage (hwndMDIClient,
+ WM_MDISETMENU,
+ (UINT)hMenu,
+ (LONG)hWindowMenu);
+
+ DestroyMenu (hMenu);
+ DrawMenuBar (hwndFrame);
+ break;
+ }
+
+ case IDM_FILEEXIT:
+ // Close LogView
+ SendMessage (hwnd, WM_CLOSE, 0, 0L);
+ break;
+
+ case IDM_HELPABOUT:
+ // Just let the shell display the about box...
+ ShellAbout(hwnd, szAppName, szAppName, LoadIcon(hInst, IDLOGVIEW));
+ break;
+
+ // The following are edit commands. Pass these off to the active child'd edit
+ // control window.
+ case IDM_EDITWRAP:
+ SendMessage(hwndActive, WM_COMMAND, GET_WM_COMMAND_MPS(IDM_EDITWRAP, 1, 0));
+ break;
+
+ case IDM_SEARCHPREV:
+ if (szSearch[0]) {
+ fReverse = TRUE;
+ Search(szSearch);
+ break;
+ }
+ // else fall through and bring up find dialog
+
+ case IDM_SEARCHNEXT:
+ if (szSearch[0]) {
+ fReverse = FALSE;
+ Search(szSearch);
+ break;
+ }
+ // else fall through and bring up find dialog
+
+ case IDM_SEARCHFIND:
+ if (hDlgFind)
+ SetFocus(hDlgFind);
+ else {
+ FR.lpstrFindWhat = szSearch;
+ FR.wFindWhatLen = CCHKEYMAX;
+ hDlgFind = FindText((LPFINDREPLACE)&FR);
+ }
+
+ break;
+
+ // The following are window commands - these are handled by the MDI Client.
+ case IDM_WINDOWTILE:
+ // Tile MDI windows
+ SendMessage (hwndMDIClient, WM_MDITILE, 0, 0L);
+ break;
+
+ case IDM_WINDOWCASCADE:
+ // Cascade MDI windows
+ SendMessage (hwndMDIClient, WM_MDICASCADE, 0, 0L);
+ break;
+
+ case IDM_WINDOWICONS:
+ // Auto - arrange MDI icons
+ SendMessage (hwndMDIClient, WM_MDIICONARRANGE, 0, 0L);
+ break;
+
+ case IDM_WINDOWCLOSEALL:
+ CloseAllChildren();
+
+ // Show the window since CloseAllChilren() hides the window for fewer repaints
+ ShowWindow( hwndMDIClient, SW_SHOW);
+ break;
+
+ case ID_HELP_CONT:
+ WinHelp(hwnd, HELP_FILE, HELP_CONTENTS, 0L);
+ break;
+
+ case ID_HELP_INDEX:
+ WinHelp(hwnd, HELP_FILE, HELP_PARTIALKEY, (DWORD) TEXT("\0"));
+ break;
+
+ case ID_HELP_USING:
+ WinHelp(hwnd, HELP_FILE, HELP_HELPONHELP, (DWORD) TEXT("\0"));
+ break;
+
+ default:
+ // This is essential, since there are frame WM_COMMANDS generated by the MDI
+ // system for activating child windows via the window menu.
+ DefFrameProc(hwnd, hwndMDIClient, WM_COMMAND, wParam, lParam);
+ }
+
+} // CommandHandler
+
+
+/////////////////////////////////////////////////////////////////////////
+SHORT
+MPError(
+ HWND hwnd,
+ WORD bFlags,
+ WORD id,
+ char *psz
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ CHAR sz[160];
+ CHAR szFmt[128];
+
+ LoadString (hInst, id, szFmt, sizeof (szFmt));
+ sprintf (sz, szFmt, psz );
+ LoadString (hInst, (WORD)IDS_APPNAME, (LPSTR)szFmt, sizeof (szFmt));
+ return( (SHORT)MessageBox (hwndFrame, sz, szFmt, bFlags));
+
+ UNREFERENCED_PARAMETER(hwnd);
+} // MPError
+
+
+/////////////////////////////////////////////////////////////////////////
+LPSTR
+GetCmdLine(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ LPSTR lpCmdLine, lpT;
+
+ lpCmdLine = GetCommandLine();
+
+ // on Win32, lpCmdLine's first string includes its own name, remove this
+ if (*lpCmdLine) {
+ lpT = strchr(lpCmdLine, ' '); // skip self name
+
+ if (lpT) {
+ lpCmdLine = lpT;
+
+ while (*lpCmdLine == ' ') {
+ lpCmdLine++; // skip spaces to end or first cmd
+ }
+
+ } else {
+ lpCmdLine += strlen(lpCmdLine); // point to NULL
+ }
+ }
+ return(lpCmdLine);
+
+} // GetCmdLine
+
+
+#define CY_SHADOW 4
+#define CX_SHADOW 4
+
+/////////////////////////////////////////////////////////////////////////
+BOOL
+CenterWindow(
+ HWND hwndChild,
+ HWND hwndParent
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ RECT rChild, rParent;
+ int wChild, hChild, wParent, hParent;
+ int wScreen, hScreen, xNew, yNew;
+ HDC hdc;
+
+ // Get the Height and Width of the child window
+ GetWindowRect (hwndChild, &rChild);
+ wChild = rChild.right - rChild.left;
+ hChild = rChild.bottom - rChild.top;
+
+ // Get the Height and Width of the parent window
+ GetWindowRect (hwndParent, &rParent);
+ wParent = rParent.right - rParent.left;
+ hParent = rParent.bottom - rParent.top;
+
+ // Get the display limits
+ hdc = GetDC (hwndChild);
+ wScreen = GetDeviceCaps (hdc, HORZRES);
+ hScreen = GetDeviceCaps (hdc, VERTRES);
+ ReleaseDC (hwndChild, hdc);
+
+ // Calculate new X position, then adjust for screen
+ xNew = rParent.left + ((wParent - wChild) /2);
+ if (xNew < 0)
+ xNew = 0;
+ else if ((xNew+wChild) > wScreen)
+ xNew = wScreen - wChild;
+
+ // Calculate new Y position, then adjust for screen
+ yNew = rParent.top + ((hParent - hChild) /2);
+ if (yNew < 0)
+ yNew = 0;
+ else if ((yNew+hChild) > hScreen)
+ yNew = hScreen - hChild;
+
+ // Set it, and return
+ return SetWindowPos (hwndChild, NULL, xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
+
+} // CenterWindow
diff --git a/private/nw/convert/logview/logview.dlg b/private/nw/convert/logview/logview.dlg
new file mode 100644
index 000000000..961272f7d
--- /dev/null
+++ b/private/nw/convert/logview/logview.dlg
@@ -0,0 +1,51 @@
+
+ABOUTBOX DIALOG DISCARDABLE 80, 40, 256, 94
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "About LogView"
+FONT 8, "MS Shell Dlg"
+BEGIN
+ ICON "LogView", -1, 6, 6, 0, 0
+ CTEXT "Logging Tool for NetWare in Windows NT Server",IDC_STATIC,45,
+ 5,200,8,NOT WS_GROUP
+ CTEXT "LogView Version 3.51",IDC_STATIC,45,15,200,8,NOT WS_GROUP
+ CTEXT "Copyright(c) Microsoft Corporation 1993-1996.",IDC_STATIC,45,25,200,8
+ CTEXT "All Rights Reserved.",IDC_STATIC,45,35,200,8
+
+ CONTROL "",IDC_STATIC,"Static",SS_BLACKRECT,45,45,200,1
+ LTEXT "Physical Memory:",IDC_STATIC,56,55,63,8,NOT WS_GROUP
+ LTEXT "(Avail mem)",IDC_PHYSICAL_MEM,127,55,129,8,NOT WS_GROUP
+ LTEXT "Disk Space:",IDC_STATIC,56,65,49,8,NOT WS_GROUP
+ LTEXT "(Disk space)",IDC_DISK_SPACE,127,65,128,8,NOT WS_GROUP
+
+ DEFPUSHBUTTON "OK",IDOK,108,77,40,14
+
+END
+
+
+// "Search/Find..." dialog box
+
+IDD_FIND DIALOG LOADONCALL MOVEABLE DISCARDABLE 18, 13, 167, 69
+FONT 8, "MS Shell Dlg"
+CAPTION "Find"
+STYLE WS_BORDER | DS_MODALFRAME | WS_CAPTION | WS_DLGFRAME | WS_POPUP | WS_SYSMENU
+BEGIN
+ CONTROL "&Find:", 100, "static", SS_RIGHT | WS_CHILD, 6, 12, 31, 10
+ CONTROL "", IDD_SEARCH, "edit", ES_LEFT | WS_BORDER | WS_TABSTOP | WS_CHILD, 43, 11, 114, 12
+ CONTROL "&Case Sensitive", IDD_CASE, "button", BS_CHECKBOX | WS_TABSTOP | WS_CHILD, 19, 28, 137, 12
+ CONTROL "&Next", IDOK, "button", BS_DEFPUSHBUTTON | WS_TABSTOP | WS_CHILD, 11, 48, 45, 14
+ CONTROL "&Previous", IDD_PREV, "button", BS_PUSHBUTTON | WS_TABSTOP | WS_CHILD, 63, 48, 45, 14
+ CONTROL "C&ancel", IDCANCEL, "button", BS_PUSHBUTTON | WS_TABSTOP | WS_CHILD, 116, 48, 43, 14
+END
+
+
+// Cancel dialog for printing
+
+IDD_PRINT DIALOG LOADONCALL MOVEABLE DISCARDABLE 20, 20, 90, 64
+FONT 8, "MS Shell Dlg"
+STYLE WS_BORDER | DS_MODALFRAME | WS_CAPTION | WS_DLGFRAME | WS_POPUP | WS_SYSMENU
+CAPTION "Print"
+BEGIN
+ CTEXT "Now Printing", -1, 0, 8, 90, 8
+ CTEXT "", IDD_PRINTTITLE, 0, 18, 90, 8, SS_NOPREFIX
+ DEFPUSHBUTTON "Cancel" IDOK, 29, 44, 32, 14
+END
diff --git a/private/nw/convert/logview/logview.h b/private/nw/convert/logview/logview.h
new file mode 100644
index 000000000..69734edff
--- /dev/null
+++ b/private/nw/convert/logview/logview.h
@@ -0,0 +1,214 @@
+
+#include "windows.h"
+
+#ifndef WIN16
+#ifndef WIN32
+ #define WIN32 1 // placed because RC can't pass in C_DEFINES
+#endif
+ #include <commdlg.h>
+#endif
+
+#define CCHKEYMAX 32 // max characters in search string
+
+#define GET_EM_SETSEL_MPS(iStart, iEnd) (UINT)(iStart), (LONG)(iEnd)
+#define GET_WM_COMMAND_CMD(wp, lp) HIWORD(wp)
+#define GET_WM_COMMAND_HWND(wp, lp) (HWND)(lp)
+#define GET_WM_COMMAND_MPS(id, hwnd, cmd) (UINT)MAKELONG(id, cmd), (LONG)(hwnd)
+#define GET_EM_SETSEL_MPS(iStart, iEnd) (UINT)(iStart), (LONG)(iEnd)
+#define GET_WM_MDIACTIVATE_FACTIVATE(hwnd, wp, lp) (lp == (LONG)hwnd)
+
+#define WINDOWMENU 2 // position of window menu
+#define SHORTMENU 2 // position of short version window menu
+
+#define DEFFILESEARCH (LPSTR) "*.LOG"
+
+#ifdef RC_INVOKED
+#define ID(id) id
+#else
+#define ID(id) MAKEINTRESOURCE(id)
+#endif
+
+// edit control identifier
+#define ID_EDIT 0xCAC
+
+// resource ID's
+#define IDLOGVIEW ID(1)
+#define IDLOGVIEW2 ID(3)
+#define IDNOTE ID(2)
+
+// Window word values for child windows
+#define GWL_HWNDEDIT 0
+#define GWW_CHANGED 4
+#define GWL_WORDWRAP 6
+#define GWW_UNTITLED 10
+#define CBWNDEXTRA 12
+
+// menu ID's
+#define IDM_FILENEW 1001
+#define IDM_FILEOPEN 1002
+#define ID_HELP_INDEX 1003
+#define ID_HELP_USING 1004
+#define ID_HELP_CONT 1005
+#define IDM_FILEPRINT 1006
+#define IDM_FILEEXIT 1007
+#define IDM_FILEABOUT 1008
+#define IDM_FILESETUP 1009
+#define IDM_FILEMENU 1010
+
+#define IDM_EDITUNDO 2001
+#define IDM_EDITCUT 2002
+#define IDM_EDITCOPY 2003
+#define IDM_EDITPASTE 2004
+#define IDM_EDITCLEAR 2005
+#define IDM_EDITSELECT 2006
+#define IDM_EDITTIME 2007
+#define IDM_EDITWRAP 2008
+#define IDM_EDITFONT 2009
+#define IDM_EDITFIRST IDM_EDITUNDO
+#define IDM_EDITLAST IDM_EDITFONT
+
+#define IDM_SEARCHFIND 3001
+#define IDM_SEARCHNEXT 3002
+#define IDM_SEARCHPREV 3003
+#define IDM_SEARCHFIRST IDM_SEARCHFIND
+#define IDM_SEARCHLAST IDM_SEARCHPREV
+
+#define IDM_WINDOWTILE 4001
+#define IDM_WINDOWCASCADE 4002
+#define IDM_WINDOWCLOSEALL 4003
+#define IDM_WINDOWICONS 4004
+
+#define IDM_WINDOWCHILD 4100
+
+#define IDM_HELPHELP 5001
+#define IDM_HELPABOUT 5002
+#define IDM_HELPSPOT 5003
+
+#define IDD_FILEOPEN ID(200)
+#define IDD_FILENAME 201
+#define IDD_FILES 202
+#define IDD_PATH 203
+#define IDD_DIRS 204
+
+// dialog ids
+#define IDD_ABOUT ID(300)
+
+#define IDD_FIND ID(400)
+#define IDD_SEARCH 401
+#define IDD_PREV 402
+#define IDD_NEXT IDOK
+#define IDD_CASE 403
+
+#define IDD_SAVEAS ID(500)
+#define IDD_SAVEFROM 501
+#define IDD_SAVETO 502
+
+#define IDD_PRINT ID(600)
+#define IDD_PRINTDEVICE 601
+#define IDD_PRINTPORT 602
+#define IDD_PRINTTITLE 603
+
+#define IDD_FONT ID(700)
+#define IDD_FACES 701
+#define IDD_SIZES 702
+#define IDD_BOLD 703
+#define IDD_ITALIC 704
+#define IDD_FONTTITLE 705
+
+// +------------------------------------------------------------------------+
+// About Box
+// +------------------------------------------------------------------------+
+#define IDC_AVAIL_MEM 101
+#define IDC_PHYSICAL_MEM 101
+#define IDC_LICENSEE_COMPANY 104
+#define IDC_LICENSEE_NAME 105
+#define IDD_SPLASH 105
+#define IDC_MATH_COPR 106
+#define IDC_DISK_SPACE 107
+#define IDC_BIGICON 1001
+
+// strings
+#define IDS_CANTOPEN 1
+#define IDS_CANTREAD 2
+#define IDS_CANTCREATE 3
+#define IDS_CANTWRITE 4
+#define IDS_ILLFNM 5
+#define IDS_ADDEXT 6
+#define IDS_CLOSESAVE 7
+#define IDS_CANTFIND 8
+#define IDS_HELPNOTAVAIL 9
+#define IDS_CANTFINDSTR 10
+
+#define IDS_CLIENTTITLE 16
+#define IDS_UNTITLED 17
+#define IDS_APPNAME 18
+
+#define IDS_PRINTJOB 24
+#define IDS_PRINTERROR 25
+
+#define IDS_DISK_SPACE_UNAVAIL 26
+#define IDS_DISK_SPACE 27
+#define IDS_MATH_COPR_NOTPRESENT 28
+#define IDS_MATH_COPR_PRESENT 29
+#define IDS_AVAIL_MEM 30
+#define IDS_PHYSICAL_MEM 31
+
+#define IDS_OPENTEXT 32
+#define IDS_OPENFILTER 33
+#define IDS_DEFEXT 34
+
+#define IDC_STATIC -1
+
+
+// attribute flags for DlgDirList
+#define ATTR_DIRS 0xC010 // find drives and directories
+#define ATTR_FILES 0x0000 // find ordinary files
+#define PROP_FILENAME szPropertyName // name of property for dialog
+
+
+// External variable declarations
+extern HANDLE hInst; // application instance handle
+extern HANDLE hAccel; // resource handle of accelerators
+extern HWND hwndFrame; // main window handle
+extern HWND hwndMDIClient; // handle of MDI Client window
+extern HWND hwndActive; // handle of current active MDI child
+extern HWND hwndActiveEdit; // handle of edit control in active child
+extern LONG styleDefault; // default child creation state
+extern CHAR szChild[]; // class of child
+extern CHAR szSearch[]; // search string
+extern CHAR *szDriver; // name of printer driver
+extern CHAR szPropertyName[]; // filename property for dialog box
+extern INT iPrinter; // level of printing capability
+extern BOOL fCase; // searches case sensitive
+extern WORD cFonts; // number of fonts enumerated
+
+extern FINDREPLACE FR;
+extern UINT wHlpMsg;
+extern UINT wFRMsg;
+extern BOOL fReverse;
+
+extern HANDLE hStdCursor, hWaitCursor;
+
+// externally declared functions
+extern BOOL APIENTRY InitializeApplication(VOID);
+extern BOOL APIENTRY InitializeInstance(LPSTR,INT);
+extern BOOL APIENTRY AboutDlgProc(HWND,UINT,UINT,LONG);
+extern HWND APIENTRY AddFile(CHAR *);
+extern VOID APIENTRY MyReadFile(HWND);
+extern INT APIENTRY LoadFile(HWND, CHAR *);
+extern VOID APIENTRY PrintFile(HWND);
+extern BOOL APIENTRY GetInitializationData(HWND);
+extern SHORT MPError(HWND,WORD,WORD, char *);
+extern VOID APIENTRY Find(VOID);
+extern VOID APIENTRY FindNext(VOID);
+extern VOID APIENTRY FindPrev(VOID);
+extern LONG APIENTRY MPFrameWndProc(HWND,UINT,UINT,LONG);
+extern LONG APIENTRY MPMDIChildWndProc(HWND,UINT,UINT,LONG);
+extern HDC APIENTRY GetPrinterDC(BOOL);
+extern VOID NEAR PASCAL SetSaveFrom (HWND, PSTR);
+extern BOOL NEAR PASCAL RealSlowCompare (PSTR, PSTR);
+extern VOID APIENTRY FindPrev (VOID);
+extern VOID APIENTRY FindNext (VOID);
+extern BOOL NEAR PASCAL IsWild (PSTR);
+extern VOID NEAR PASCAL SelectFile (HWND);
+extern VOID NEAR PASCAL Local_FindText ( INT );
diff --git a/private/nw/convert/logview/logview.rc b/private/nw/convert/logview/logview.rc
new file mode 100644
index 000000000..16e69a74e
--- /dev/null
+++ b/private/nw/convert/logview/logview.rc
@@ -0,0 +1,111 @@
+
+#include "logview.h"
+#include "version.h"
+
+IDLOGVIEW ICON FV300.ico // main icon
+LogView ICON FV300.ICO
+IDNOTE ICON note300.ico // icon for child windows
+
+// frame window menu
+
+IDLOGVIEW MENU
+BEGIN
+ POPUP "&File"
+ BEGIN
+ MENUITEM "&Open...", IDM_FILEOPEN
+ MENUITEM SEPARATOR
+ MENUITEM "&Print", IDM_FILEPRINT
+ MENUITEM "Printer Se&tup...", IDM_FILESETUP
+ MENUITEM SEPARATOR
+ MENUITEM "E&xit", IDM_FILEEXIT
+ END
+ POPUP "&Search"
+ BEGIN
+ MENUITEM "&Find...", IDM_SEARCHFIND
+ MENUITEM "&Next\tF3", IDM_SEARCHNEXT
+ MENUITEM "&Previous\tF4", IDM_SEARCHPREV
+ END
+ POPUP "&Window"
+ BEGIN
+ MENUITEM "&Tile", IDM_WINDOWTILE
+ MENUITEM "&Cascade", IDM_WINDOWCASCADE
+ MENUITEM "Arrange &Icons", IDM_WINDOWICONS
+ MENUITEM "Close &All", IDM_WINDOWCLOSEALL
+ END
+ POPUP "&Help"
+ {
+ MENUITEM "&Contents", ID_HELP_CONT
+ MENUITEM "&Search for Help on...", ID_HELP_INDEX
+ MENUITEM "&How to Use Help", ID_HELP_USING
+ MENUITEM SEPARATOR
+ MENUITEM "&About LogView...", IDM_HELPABOUT
+ }
+END
+
+// short frame window menu
+IDLOGVIEW2 MENU
+BEGIN
+ POPUP "&File"
+ BEGIN
+ MENUITEM "&Open...", IDM_FILEOPEN
+ MENUITEM SEPARATOR
+ MENUITEM "&Print...", IDM_FILEPRINT
+ MENUITEM SEPARATOR
+ MENUITEM "E&xit", IDM_FILEEXIT
+ MENUITEM SEPARATOR
+ MENUITEM "&About logview...", IDM_HELPABOUT
+ END
+ POPUP "&Window"
+ BEGIN
+ MENUITEM "&Arrange", IDM_WINDOWTILE
+ END
+END
+
+// frame menu accelerators
+
+IDLOGVIEW ACCELERATORS
+BEGIN
+ VK_INSERT, IDM_EDITCOPY, VIRTKEY, CONTROL
+ VK_INSERT, IDM_EDITPASTE, VIRTKEY, SHIFT
+ VK_DELETE, IDM_EDITCUT, VIRTKEY, SHIFT
+ VK_BACK, IDM_EDITUNDO, VIRTKEY, ALT
+ VK_F5, IDM_EDITTIME, VIRTKEY
+ VK_F3, IDM_SEARCHNEXT, VIRTKEY
+ VK_F4, IDM_SEARCHPREV, VIRTKEY
+ VK_F1, IDM_HELPHELP, VIRTKEY
+ VK_F1, IDM_HELPSPOT, VIRTKEY, SHIFT
+END
+
+
+
+STRINGTABLE
+BEGIN
+
+IDS_CANTOPEN "Can't open the file '%s'"
+IDS_CANTREAD "Can't read the file '%s'"
+IDS_CANTCREATE "Can't create the file '%s'"
+IDS_CANTWRITE "Can't write the file '%s'"
+IDS_ADDEXT ".TXT"
+IDS_ILLFNM "Invalid filename: '%s'"
+IDS_CLOSESAVE "%s has been changed. Save file before closing?"
+IDS_CANTFIND "Can't find '%s'"
+IDS_HELPNOTAVAIL "Can't load Windows Help application"
+IDS_CLIENTTITLE "LogView App, Version 3.51"
+IDS_UNTITLED "Untitled"
+IDS_PRINTJOB "LogView - "
+IDS_PRINTERROR "Cannot print %s!"
+IDS_APPNAME "LogView"
+IDS_CANTFINDSTR "Can't find string"
+
+ IDS_DISK_SPACE_UNAVAIL "Unavailable"
+ IDS_DISK_SPACE "%lu KB Free"
+ IDS_MATH_COPR_NOTPRESENT "Not present"
+ IDS_MATH_COPR_PRESENT "Present"
+ IDS_PHYSICAL_MEM "%lu KB Free"
+
+IDS_OPENTEXT "Open TextFiles"
+IDS_OPENFILTER "Log Files(*.LOG)\0*.LOG\0"
+IDS_DEFEXT "LOG"
+END
+
+#include "logview.dlg"
diff --git a/private/nw/convert/logview/makefile b/private/nw/convert/logview/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/nw/convert/logview/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/convert/logview/note300.ico b/private/nw/convert/logview/note300.ico
new file mode 100644
index 000000000..0446c5579
--- /dev/null
+++ b/private/nw/convert/logview/note300.ico
Binary files differ
diff --git a/private/nw/convert/logview/sources b/private/nw/convert/logview/sources
new file mode 100644
index 000000000..46f19230d
--- /dev/null
+++ b/private/nw/convert/logview/sources
@@ -0,0 +1,23 @@
+TARGETNAME=logview
+TARGETPATH=obj
+TARGETTYPE=PROGRAM
+
+TARGETLIBS=\
+ $(BASEDIR)\public\sdk\lib\*\shell32.lib \
+ $(BASEDIR)\public\sdk\lib\*\comdlg32.lib
+
+C_DEFINES=-DWIN32 -DWINVER=0x0400
+
+SUBSYSTEM_VERSION=4.00
+
+SOURCES=\
+ LOGVIEW.RC \
+ LOGVIEW.C \
+ FVFILE.C \
+ FVFIND.C \
+ FVINIT.C \
+ FVOPEN.C \
+ FVPRINT.C
+
+UMTYPE=windows
+UMENTRY=winmain
diff --git a/private/nw/convert/logview/version.h b/private/nw/convert/logview/version.h
new file mode 100644
index 000000000..ba9fb71b6
--- /dev/null
+++ b/private/nw/convert/logview/version.h
@@ -0,0 +1,52 @@
+/*
+** Template for version resources. Place this in your .rc file,
+** editing the values for VER_FILETYPE, VER_FILESUBTYPE,
+** VER_FILEDESCRIPTION_STR and VER_INTERNALNAME_STR as needed.
+** See winver.h for possible values.
+**
+** Ntverp.h defines several global values that don't need to be
+** changed except for official releases such as betas, sdk updates, etc.
+**
+** Common.ver has the actual version resource structure that all these
+** #defines eventually initialize.
+*/
+
+/* #include <windows.h> needed if this will be the .rc file */
+
+#include <ntverp.h>
+
+/*-----------------------------------------------*/
+/* the following lines are specific to this file */
+/*-----------------------------------------------*/
+
+/* VER_FILETYPE, VER_FILESUBTYPE, VER_FILEDESCRIPTION_STR
+ * and VER_INTERNALNAME_STR must be defined before including COMMON.VER
+ * The strings don't need a '\0', since common.ver has them.
+ */
+#define VER_FILETYPE VFT_APP
+/* possible values: VFT_UNKNOWN
+ VFT_APP
+ VFT_DLL
+ VFT_DRV
+ VFT_FONT
+ VFT_VXD
+ VFT_STATIC_LIB
+*/
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+/* possible values VFT2_UNKNOWN
+ VFT2_DRV_PRINTER
+ VFT2_DRV_KEYBOARD
+ VFT2_DRV_LANGUAGE
+ VFT2_DRV_DISPLAY
+ VFT2_DRV_MOUSE
+ VFT2_DRV_NETWORK
+ VFT2_DRV_SYSTEM
+ VFT2_DRV_INSTALLABLE
+ VFT2_DRV_SOUND
+ VFT2_DRV_COMM
+*/
+#define VER_FILEDESCRIPTION_STR "Migration Tool for NetWare Log File Viewer"
+#define VER_INTERNALNAME_STR "LogView"
+#define VER_ORIGINALFILENAME_STR "LOGVIEW.EXE"
+
+#include "common.ver"
diff --git a/private/nw/convert/nwconv/aboutbox.c b/private/nw/convert/nwconv/aboutbox.c
new file mode 100644
index 000000000..d44a898d4
--- /dev/null
+++ b/private/nw/convert/nwconv/aboutbox.c
@@ -0,0 +1,39 @@
+/*
+ +-------------------------------------------------------------------------+
+ | About Box Routine |
+ +-------------------------------------------------------------------------+
+ | (c) Copyright 1993-1994 |
+ | Microsoft Corp. |
+ | All rights reserved |
+ | |
+ | Program : [AboutBox.c] |
+ | Programmer : Arthur Hanson |
+ | Original Program Date : [Jul 27, 1993] |
+ | Last Update : [Jun 18, 1994] |
+ | |
+ | Version: 1.00 |
+ | |
+ | Description: |
+ | About box code, nuff said. |
+ | |
+ | |
+ | History: |
+ | arth Jun 18, 1994 1.00 Original Version. |
+ | |
+ +-------------------------------------------------------------------------+
+*/
+
+#include "globals.h"
+#include <shellapi.h>
+
+extern TCHAR szAppName[];
+
+/*+-------------------------------------------------------------------------+
+ | AboutBox_Do()
+ |
+ +-------------------------------------------------------------------------+*/
+void AboutBox_Do(HWND hDlg) {
+
+ ShellAbout(hDlg, szAppName, szAppName, LoadIcon(hInst, szAppName));
+
+} // AboutBox_Do
diff --git a/private/nw/convert/nwconv/columnlb.c b/private/nw/convert/nwconv/columnlb.c
new file mode 100644
index 000000000..468595ba1
--- /dev/null
+++ b/private/nw/convert/nwconv/columnlb.c
@@ -0,0 +1,2986 @@
+// ==================================================================================================
+// COPYRIGHT 1992,1993 MICROSOFT CORP ALL RIGHTS RESERVED
+// ==================================================================================================
+//
+// - Custom control to display Columns/Titles above a list box
+//
+// TERMINOLOGY:
+// PHYSICAL COLUMNS: The index of the original columns in their original order
+// VIRtUAL COLUMNS: The index of the column as the display is currently showing it based on
+// the current order.
+//
+// History:
+// -------
+// RickD 04/10/92 created TitleListBox
+// Tom Laird-McConnell 12/30/92 Ported to Win32, added to BH project
+// Tom Laird-McConnell 05/01/93 gutted titlelist and used as base for complete rewrite as ColumnLB
+// Tom Laird-McConnell 08/18/93 Added tabbed-delimited string support
+// Arth 03/24/94 Added Unicode support
+// ==================================================================================================
+#define STRICT 1
+#include "switches.h"
+
+#include <windows.h>
+#include <windowsx.h>
+
+#include <string.h>
+#include <stdlib.h>
+
+// #include <dprintf.h>
+
+#include "columnlb.h"
+#include "vlist.h"
+
+#include "utils.h"
+#include "debug.h"
+#include "mem.h"
+
+
+//#define DEBUG_HSCROLL 1
+
+#define WHITESPACE 8
+
+#define IDL_COLUMNLISTBOX (CLB_BASE + 1)
+#define IDL_COLUMNTITLELISTBOX (CLB_BASE + 2)
+
+#define LB16_ADDSTRING (WM_USER+1)
+#define LB16_INSERTSTRING (WM_USER+2)
+
+typedef struct _COLRECORD
+{
+ DWORD itemData;
+ LPTSTR pString[];
+} COLRECORD;
+typedef COLRECORD *LPCOLRECORD;
+
+// -------------------------------------------------------------------------------------
+//
+// window procs
+//
+LRESULT CALLBACK ColumnLBClass_ListBoxWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+LRESULT CALLBACK ColumnLBClass_WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+//
+// system message handlers
+//
+BOOL ColumnLBClass_OnNCCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct);
+void ColumnLBClass_OnNCDestroy(HWND hwnd);
+void ColumnLBClass_OnDestroy(HWND hwnd);
+void ColumnLBClass_OnPaint(HWND hwnd);
+void ColumnLBClass_OnSize(HWND hwnd, UINT state, int cx, int cy);
+void ColumnLBClass_OnSetFont(HWND hwnd, HFONT hfont, BOOL fRedraw);
+LRESULT ColumnLBClass_OnHScroll(HWND hwnd, HWND hwndCtl, UINT code, int pos);
+void ColumnLBClass_OnDrawItem(HWND hwnd, const DRAWITEMSTRUCT * lpDrawItem);
+void ColumnLBClass_OnMeasureItem(HWND hwnd, LPMEASUREITEMSTRUCT lpMeasureItem);
+void ColumnLBClass_OnDeleteItem(HWND hwnd, const DELETEITEMSTRUCT *lpDeleteItem);
+int ColumnLBClass_OnCharToItem(HWND hwnd, UINT ch, HWND hwndListbox, int iCaret);
+
+//
+// WM_USER message handlers
+//
+BYTE ColumnLBClass_OnNumberCols(HWND hwnd, BYTE NewNumberCols, BOOL fSetColumns);
+int ColumnLBClass_OnColWidth(HWND hwnd, BYTE Column, int NewWidth, BOOL fSetWidth);
+LPTSTR ColumnLBClass_OnColTitle(HWND hwnd, BYTE Column, LPTSTR lpTitle, BOOL fSetTitle);
+BYTE ColumnLBClass_OnSortCol(HWND hwnd, BYTE NewSortCol, BOOL fSetSortCol);
+LPBYTE ColumnLBClass_OnColOrder(HWND hwnd, LPBYTE NewColOrder, BOOL fSetOrder);
+LPINT ColumnLBClass_OnColOffsets(HWND hwnd, LPINT NewOffsetTable, BOOL fSetOffsets);
+LRESULT ColumnLBClass_OnAutoWidth(HWND hwnd, BYTE ColumnToCompute);
+
+
+//
+// mouse movement messages
+//
+void ColumnLBClass_OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags);
+void ColumnLBClass_OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags);
+void ColumnLBClass_OnLButtonUp(HWND hwnd, int x, int y, UINT keyFlags);
+void ColumnLBClass_OnRButtonDown (HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags);
+
+// helper functions
+int ColumnLBClass_ComputeOffsets(HWND hwnd);
+
+// -------------------------------------------------------------------------------------
+//
+// Locals
+//
+BOOL fWIN32s; // flag for whether win32s (instead of win32/NT)
+
+
+// -----------------------------------------------------------------
+//
+// ColumnLBClass_Register()
+//
+// This function is used to register the Column LB class with the system
+//
+// HISTORY:
+// Tom Laird-McConnell 4/17/93 Created
+//
+// -----------------------------------------------------------------
+BOOL ColumnLBClass_Register(HINSTANCE hInstance)
+{
+ WNDCLASS WndClass;
+
+ fWIN32s = ((DWORD)GetVersion() & 0x80000000) ? TRUE : FALSE;
+
+ //
+ // Create the COLUMNLISTBOX class
+ //
+ WndClass.style = CS_PARENTDC | CS_DBLCLKS; // CS_GLOBALCLASS;
+ WndClass.lpfnWndProc = ColumnLBClass_WndProc;
+ WndClass.cbClsExtra = 0;
+ WndClass.cbWndExtra = sizeof(LPCOLUMNLBSTRUCT); // we store a pointer as instance data
+ WndClass.hInstance = hInstance;
+ WndClass.hIcon = 0;
+ WndClass.hCursor = LoadCursor(0, IDC_ARROW);
+ WndClass.hbrBackground = 0;
+ WndClass.lpszMenuName = 0;
+ WndClass.lpszClassName = COLUMNLBCLASS_CLASSNAME;
+
+ /* Register the normal title list box control */
+ return RegisterClass((LPWNDCLASS)&WndClass);
+}
+
+
+// -----------------------------------------------------------------
+//
+// ColumnVLBClass_Register()
+//
+// This function is used to register the Column VLIST LB class with the system
+//
+// HISTORY:
+// Tom Laird-McConnell 4/17/93 Created
+//
+// -----------------------------------------------------------------
+BOOL ColumnVLBClass_Register(HINSTANCE hInstance)
+{
+ WNDCLASS WndClass;
+
+ fWIN32s = ((DWORD)GetVersion() & 0x80000000) ? TRUE : FALSE;
+
+ //
+ // Create the COLUMNVLISTBOX class
+ //
+ WndClass.style = CS_PARENTDC | CS_DBLCLKS; // CS_GLOBALCLASS;
+ WndClass.lpfnWndProc = ColumnLBClass_WndProc;
+ WndClass.cbClsExtra = 0;
+ WndClass.cbWndExtra = sizeof(LPCOLUMNLBSTRUCT); // we store a pointer as instance data
+ WndClass.hInstance = hInstance;
+ WndClass.hIcon = 0;
+ WndClass.hCursor = LoadCursor(0, IDC_ARROW);
+ WndClass.hbrBackground = 0;
+ WndClass.lpszMenuName = 0;
+ WndClass.lpszClassName = COLUMNVLBCLASS_CLASSNAME; // NOTE: this is a different name
+
+ /* Register the new control */
+ return(RegisterClass((LPWNDCLASS)&WndClass));
+}
+
+
+
+// -----------------------------------------------------------------
+//
+// ColumnLBClass_Unregister()
+//
+// This function is used to deregister the Column LB class with the system
+//
+// HISTORY:
+// Tom Laird-McConnell 4/17/93 Created
+//
+// -----------------------------------------------------------------
+BOOL ColumnLBClass_Unregister(HINSTANCE hInstance)
+{
+ return(UnregisterClass(COLUMNLBCLASS_CLASSNAME, hInstance));
+}
+
+// -----------------------------------------------------------------
+//
+// ColumnVLBClass_Unregister()
+//
+// This function is used to deregister the Column VLIST LB class with the system
+//
+// HISTORY:
+// Tom Laird-McConnell 4/17/93 Created
+//
+// -----------------------------------------------------------------
+BOOL ColumnVLBClass_Unregister(HINSTANCE hInstance)
+{
+ return(UnregisterClass(COLUMNVLBCLASS_CLASSNAME, hInstance));
+}
+
+// -----------------------------------------------------------------
+// ColumnLBClass_ListBoxWndProc
+//
+// Window proc used in sub-classing the internal listbox to catch
+// internal scroll events to keep title in sync with it...
+//
+// HISTORY:
+// Tom Laird-McConnell 4/17/93 Created
+//
+// -----------------------------------------------------------------
+LRESULT CALLBACK ColumnLBClass_ListBoxWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ DWORD result;
+ LPCOLUMNLBSTRUCT lpColumnLB;
+
+ // Everthing goes to normal listbox for processing
+ lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(GetParent(hwnd), (DWORD)0);
+
+ // preprocessing
+ switch (msg)
+ {
+
+ case WM_HSCROLL:
+ // do title hscroll first..
+ result = SendMessage(lpColumnLB->hwndTitleList, WM_HSCROLL, wParam, lParam);
+ break;
+
+ case WM_SETFOCUS:
+ lpColumnLB->fHasFocus = TRUE;
+ //dprintf ("SetFocus to ColumnLB\n");
+ break;
+
+ case WM_KILLFOCUS:
+ lpColumnLB->fHasFocus = FALSE;
+ //dprintf ("KillFocus to ColumnLB\n");
+ break;
+
+
+ }
+
+ //
+ // call the original listbox window proc
+ //
+ result = CallWindowProc((WNDPROC)(lpColumnLB->OldListboxProc), hwnd, msg, (WPARAM) wParam, (LPARAM)lParam);
+
+
+ //
+ // or if our parent has CLBS_NOTIFYLMOUSE style, then foward LMOUSE buttons
+ // or if our parent has CLBS_NOTIFYRMOUSE style, then foward RMOUSE buttons
+ //
+ switch (msg)
+ {
+ case WM_HSCROLL:
+ //
+ // if it is a Horizontal scrolls, then we foward to our parent so title can be moved
+ // in sync with listbox....
+ //
+ SendMessage(GetParent(hwnd), CLB_HSCROLL, wParam, lParam );
+ break;
+
+ case VLB_RESETCONTENT:
+ case LB_RESETCONTENT:
+ //
+ // if it is a LB_RESETCONTENT, or VLB_RESETCONTENT, then reset x position
+ //
+ SendMessage(GetParent(hwnd), CLB_HSCROLL, (WPARAM)SB_TOP, (LPARAM)NULL);
+ break;
+
+ case WM_LBUTTONDOWN :
+ case WM_LBUTTONUP :
+ case WM_LBUTTONDBLCLK :
+ //
+ // forward message to parent
+ //
+ if (GetWindowLong(GetParent(hwnd), GWL_EXSTYLE) & CLBS_NOTIFYLMOUSE)
+ SendMessage(GetParent(hwnd), msg, wParam, lParam);
+ break;
+
+ case WM_RBUTTONDOWN :
+// case WM_RBUTTONUP :
+ case WM_RBUTTONDBLCLK :
+
+ //
+ // forward message to parent
+ //
+
+ // if (GetWindowLong(GetParent(hwnd), GWL_EXSTYLE) & CLBS_NOTIFYRMOUSE)
+ SendMessage(GetParent(hwnd), msg, wParam, lParam);
+ break;
+
+
+ default:
+ break;
+ }
+
+ return(result);
+}
+
+// -----------------------------------------------------------------
+// ColumnLBClass_TitleListBoxWndProc
+//
+// Window proc used in sub-classing the internal Title listbox to catch
+// internal mouse click events...
+//
+// HISTORY:
+// Tom Laird-McConnell 4/17/93 Created
+//
+// -----------------------------------------------------------------
+LRESULT CALLBACK ColumnLBClass_TitleListBoxWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ DWORD result;
+ LPCOLUMNLBSTRUCT lpColumnLB;
+
+ // Everthing goes to normal listbox for processing
+ lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(GetParent(hwnd), (DWORD)0);
+
+ //
+ // call the original listbox window proc
+ //
+ result = CallWindowProc((WNDPROC)(lpColumnLB->OldTitleListboxProc) , hwnd, msg, (WPARAM) wParam, (LPARAM)lParam);
+
+ //
+ // foward LMOUSE buttons, foward RMOUSE buttons
+ //
+ switch (msg)
+ {
+#ifdef DEBUG_HSCROLL
+ case WM_HSCROLL:
+ dprintf(TEXT("ColumnLBClass_TitleListBoxProc: CallWindowProc(OldListboxProc) returned %ld to hwnd=%lx, wParam=%u, lParam=%u\n"), hwnd, wParam, lParam);
+ break;
+#endif
+ case WM_MOUSEMOVE:
+ case WM_LBUTTONDOWN :
+ case WM_LBUTTONUP :
+ case WM_LBUTTONDBLCLK :
+ case WM_RBUTTONDOWN :
+ case WM_RBUTTONUP :
+ case WM_RBUTTONDBLCLK :
+ SendMessage(GetParent(hwnd), msg, wParam, lParam);
+ break;
+
+ case WM_SETFOCUS:
+ // we don't ever want the focus, so give it back to the parent...
+ SendMessage(GetParent(hwnd), msg, wParam, lParam);
+ break;
+
+ case WM_SIZE:
+ // we need to reset our idea of what our current scroll position is...
+ break;
+ }
+
+ return(result);
+}
+
+
+
+// -----------------------------------------------------------------
+// ColumnLBClass_WndProc
+//
+// Main window proc for handling messages for both the ColumnLB and
+// ColumnVLB classes...
+//
+// HISTORY:
+// Tom Laird-McConnell 4/17/93 Created
+//
+// -----------------------------------------------------------------
+LRESULT CALLBACK ColumnLBClass_WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0);
+ LPCOLRECORD lpRecord;
+ int result;
+
+ //
+ // check for ListBox message coming from application
+ // and forward them onto the listbox itself...
+ //
+ if ( ((fWIN32s == TRUE) && (msg >= WM_USER && msg < (WM_USER + LB_MSGMAX - LB_ADDSTRING + 1)) ) || // WIN32s version BUGBUG
+ ((fWIN32s == FALSE) && (msg >= LB_ADDSTRING && msg < LB_MSGMAX)) || // NT version BUGBUG
+ ((msg >= VLB_TOVLIST_MSGMIN) && (msg <= VLB_TOVLIST_MSGMAX)) // vlb sepcific APP->VLIST messages should be fowarded...
+ )
+ {
+ //
+ // OWNERDRAW parent, so just send it to the hwnd list child
+ //
+ return(SendMessage(lpColumnLB->hwndList, msg, wParam, lParam));
+ }
+
+ //
+ // check to see if message is a TO APPLCATION message from the child listbox
+ // which should be forwarded to application parent window
+ //
+ if ((msg >= VLB_TOAPP_MSGMIN) && (msg <= VLB_TOAPP_MSGMAX))
+ return(SendMessage(GetParent(hwnd), msg, wParam, lParam)); // forward to parent...
+
+ //
+ // since it's not a message passing through, then process this message
+ // as our own...
+ //
+ switch (msg)
+ {
+ HANDLE_MSG(hwnd, WM_NCCREATE, ColumnLBClass_OnNCCreate);
+ HANDLE_MSG(hwnd, WM_NCDESTROY, ColumnLBClass_OnNCDestroy);
+ HANDLE_MSG(hwnd, WM_DESTROY, ColumnLBClass_OnDestroy);
+ HANDLE_MSG(hwnd, WM_PAINT, ColumnLBClass_OnPaint);
+ HANDLE_MSG(hwnd, WM_SIZE, ColumnLBClass_OnSize);
+ HANDLE_MSG(hwnd, WM_DRAWITEM, ColumnLBClass_OnDrawItem);
+ HANDLE_MSG(hwnd, WM_MEASUREITEM, ColumnLBClass_OnMeasureItem);
+ HANDLE_MSG(hwnd, WM_DELETEITEM, ColumnLBClass_OnDeleteItem);
+
+ HANDLE_MSG(hwnd, WM_LBUTTONDOWN, ColumnLBClass_OnLButtonDown);
+ HANDLE_MSG(hwnd, WM_LBUTTONDBLCLK, ColumnLBClass_OnLButtonDown);
+ HANDLE_MSG(hwnd, WM_LBUTTONUP, ColumnLBClass_OnLButtonUp);
+ HANDLE_MSG(hwnd, WM_MOUSEMOVE, ColumnLBClass_OnMouseMove);
+
+ case WM_RBUTTONDOWN:
+ // figure out what item we are on and tell our parent.
+ HANDLE_WM_RBUTTONDOWN ( hwnd, wParam, lParam, ColumnLBClass_OnRButtonDown );
+ break;
+
+ case WM_CREATE:
+ {
+ LPCREATESTRUCT lpCreate = (LPCREATESTRUCT) lParam;
+
+ ColumnLBClass_OnSize(hwnd, SIZE_RESTORED, lpCreate->cx, lpCreate->cy);
+ }
+ break;
+
+
+ // -------------------------------------------------------------------
+ // put System messages here which explicitly need to be passed to LISTBOX
+ //
+ case WM_SETFONT:
+ HANDLE_WM_SETFONT(hwnd, wParam, lParam, ColumnLBClass_OnSetFont);
+ break;
+
+ // put the focus on the list box if we get it
+ case WM_SETFOCUS:
+ lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0);
+ SetFocus(lpColumnLB->hwndList);
+ break;
+
+ case SBM_SETPOS :
+ case SBM_SETRANGE :
+ case SBM_SETRANGEREDRAW :
+ //
+ // need to foward SBM messages to both listboxes...
+ //
+ SendMessage(lpColumnLB->hwndTitleList, msg, wParam, lParam);
+ return(SendMessage(lpColumnLB->hwndList, msg, wParam, lParam));
+
+ case SBM_GETPOS :
+ case SBM_GETRANGE :
+ case SBM_ENABLE_ARROWS :
+ return(SendMessage(lpColumnLB->hwndList, msg, wParam, lParam));
+
+ // ------------------------------------------------------------------------------
+ //
+ // LB_XXXXXXX Messages (disguised as CLB_XXXXXX messages)
+ // to pass on to child listbox, if the parent didn't want us to
+ // be owner draw, then we implement ownerddraw default behavior ourself
+ //
+ // ------------------------------------------------------------------------------
+ case CLB_ADDSTRING:
+ case CLB_INSERTSTRING:
+ //
+ // if the parent is NOT handling OWNERDRAW, then we need to handle
+ //
+ if ( ! (lpColumnLB->Style & (LBS_OWNERDRAWFIXED | VLBS_OWNERDRAWFIXED)) )
+ {
+ LPTSTR lpColStart,lpTab;
+ LPTSTR lpStringBuffer;
+ int i;
+
+ lpRecord = (LPCOLRECORD)GlobalAllocPtr(GPTR, sizeof(COLRECORD) + sizeof(LPTSTR) * lpColumnLB->nColumns);
+ lpStringBuffer = (LPTSTR) GlobalAllocPtr(GPTR, (lstrlen((LPTSTR)lParam) * sizeof(TCHAR)) + sizeof(TCHAR));
+
+ if ((lpRecord) && (lpStringBuffer))
+ {
+ // now parse the tab-deliminated string and put into each pointer
+ lstrcpy(lpStringBuffer, (LPTSTR)lParam);
+ lpColStart = lpStringBuffer;
+ lpTab = lstrchr(lpStringBuffer, TEXT('\t'));
+
+ // fill in pointer table
+ for (i=0; i < lpColumnLB->nColumns; i++)
+ {
+ if (lpTab)
+ *lpTab = '\0';
+ else
+ {
+ // there was an error, not enough tabs!
+ GlobalFreePtr(lpRecord);
+ GlobalFreePtr(lpStringBuffer);
+ return(LB_ERR);
+ }
+ // store this pointer.
+ lpRecord->pString[i] = lpColStart;
+ // advance to next column text
+ lpColStart = lpTab + 1;
+ lpTab = lstrchr(lpColStart, TEXT('\t'));
+ }
+ lpRecord->itemData = 0;
+
+ // and now pass on our new lpRecord as the item being added to the listbox
+ return(SendMessage(lpColumnLB->hwndList, msg - CLB_BASE, wParam, (LPARAM)lpRecord));
+ }
+ else // an error has occured, free up any memory and return failure
+ {
+ if (lpStringBuffer)
+ GlobalFreePtr(lpStringBuffer);
+ if (lpRecord)
+ GlobalFreePtr(lpRecord);
+ return(LB_ERR);
+ }
+ }
+ else
+ //
+ // Parent is owner draw, so send it to the hwnd list child,
+ // but translate the message first to real LB_ message
+ //
+ return(SendMessage(lpColumnLB->hwndList, msg - CLB_BASE, wParam, lParam));
+
+ // and we also need to check for LB_GETTEXT to make it look like a string
+ case CLB_GETTEXT:
+ //
+ // if the parent is NOT handling OWNERDRAW, then we need to handle
+ //
+ if ( ! (lpColumnLB->Style & (LBS_OWNERDRAWFIXED | VLBS_OWNERDRAWFIXED)) )
+ {
+ LPTSTR p = (LPTSTR)lParam;
+ DWORD Length = 0;
+ DWORD x;
+ int i;
+
+ *p = '\0';
+
+ // and now pass on to get the text...
+ lpRecord = (LPCOLRECORD)SendMessage(lpColumnLB->hwndList, LB_GETITEMDATA, wParam, 0);
+
+ if (lpRecord == (LPCOLRECORD)LB_ERR)
+ return(LB_ERR);
+
+ for (i=0; i < lpColumnLB->nColumns ; i++ )
+ {
+ lstrcpy(p, lpRecord->pString[lpColumnLB->ColumnOrderTable[i]]);
+ lstrcat(p, TEXT("\t"));
+ x = lstrlen(p);
+ Length += x + sizeof(TCHAR);
+ p += x;
+ }
+ return(Length);
+ }
+ else
+ //
+ // Parent is owner draw, so send it to the hwnd list child,
+ // but translate the message first to real LB_ message
+ //
+ return(SendMessage(lpColumnLB->hwndList, msg - CLB_BASE, wParam, lParam));
+
+ case CLB_GETTEXTPTRS:
+
+ if ( ! (lpColumnLB->Style & (LBS_OWNERDRAWFIXED | VLBS_OWNERDRAWFIXED)) )
+ {
+ lpRecord = (LPCOLRECORD)SendMessage(lpColumnLB->hwndList, LB_GETITEMDATA, wParam, 0);
+
+ if (lpRecord == (LPCOLRECORD)LB_ERR)
+ return(LB_ERR);
+
+ return (LRESULT)lpRecord->pString;
+ }
+ else
+ return (LRESULT)NULL;
+
+
+ // we need to handle LB_GETTEXTLEN to return full tabbed length...
+ case CLB_GETTEXTLEN:
+ //
+ // if the parent is NOT handling OWNERDRAW, then we need to handle
+ //
+ if ( ! (lpColumnLB->Style & (LBS_OWNERDRAWFIXED | VLBS_OWNERDRAWFIXED)) )
+ {
+ LPTSTR p = (LPTSTR)lParam;
+ DWORD Length = 0;
+ int i;
+
+ // and now pass on to get the text...
+ lpRecord = (LPCOLRECORD)SendMessage(lpColumnLB->hwndList, LB_GETITEMDATA, wParam, 0);
+
+ if (lpRecord == (LPCOLRECORD)LB_ERR)
+ return(LB_ERR);
+
+ for (i=0; i < lpColumnLB->nColumns ; i++ )
+ {
+ Length += lstrlen(lpRecord->pString[lpColumnLB->ColumnOrderTable[i]]) + sizeof(TCHAR);
+ }
+ return(Length);
+ }
+ else
+ //
+ // Parent is owner draw, so send it to the hwnd list child,
+ // but translate the message first to real LB_ message
+ //
+ return(SendMessage(lpColumnLB->hwndList, msg - CLB_BASE, wParam, lParam));
+
+ case CLB_GETITEMDATA:
+ //
+ // if the parent is NOT handling OWNERDRAW, then we need to handle
+ //
+ if ( ! (lpColumnLB->Style & (LBS_OWNERDRAWFIXED | VLBS_OWNERDRAWFIXED)) )
+ {
+ lpRecord = (LPCOLRECORD)SendMessage(lpColumnLB->hwndList, LB_GETITEMDATA, wParam, 0);
+ if (lpRecord != (LPCOLRECORD)LB_ERR)
+ return(lpRecord->itemData);
+ else
+ return(LB_ERR);
+ }
+ else
+ //
+ // Parent is owner draw, so send it to the hwnd list child,
+ // but translate the message first to real LB_ message
+ //
+ return(SendMessage(lpColumnLB->hwndList, msg - CLB_BASE, wParam, lParam));
+
+
+ case CLB_SETITEMDATA:
+ //
+ // if the parent is NOT handling OWNERDRAW, then we need to handle
+ //
+ if ( ! (lpColumnLB->Style & (LBS_OWNERDRAWFIXED | VLBS_OWNERDRAWFIXED)) )
+ {
+ lpRecord = (LPCOLRECORD)SendMessage(lpColumnLB->hwndList, LB_GETITEMDATA, wParam, 0);
+
+ if (lpRecord != (LPCOLRECORD)LB_ERR)
+ return(lpRecord->itemData = lParam);
+ else
+ return(LB_ERR);
+ }
+ else
+ //
+ // Parent is owner draw, so send it to the hwnd list child,
+ // but translate the message first to real LB_ message
+ //
+ return(SendMessage(lpColumnLB->hwndList, msg - CLB_BASE, wParam, lParam));
+
+ //
+ // if it is a HORIZONTAL exntent message, then we need to massage...
+ //
+ case CLB_SETHORIZONTALEXTENT :
+ //
+ // send the message to the title listbox as well
+ //
+ SendMessage(lpColumnLB->hwndTitleList, LB_SETHORIZONTALEXTENT, wParam, lParam);
+
+ //
+ // pass it on to the child listbox, using VLB_SETHOR if appropriate...
+ //
+ return(SendMessage(lpColumnLB->hwndList,
+ (lpColumnLB->fUseVlist) ? VLB_SETHORIZONTALEXTENT : LB_SETHORIZONTALEXTENT,
+ wParam, lParam));
+
+ //
+ // we need to massage the GETITEMRECT to handle the incorrect rect returned due to titlelistbox.
+ //
+ case CLB_GETITEMRECT:
+ {
+ int retcode;
+ int height;
+ LPRECT lpRect = (LPRECT)lParam;
+
+ //
+ // send it to the hwnd list child, but translate the message first to real LB_ message
+ //
+ retcode = SendMessage(lpColumnLB->hwndList, msg - CLB_BASE, wParam, lParam);
+ height = lpRect->bottom-lpRect->top;
+ lpRect->top = lpRect->bottom + 1;
+ lpRect->bottom = lpRect->top + height;
+ return(retcode);
+ }
+ break;
+
+ case CLB_DELETESTRING :
+ case CLB_SELITEMRANGEEX :
+ case CLB_RESETCONTENT :
+ case CLB_SETSEL :
+ case CLB_SETCURSEL :
+ case CLB_GETSEL :
+ case CLB_GETCURSEL :
+ case CLB_GETCOUNT :
+ case CLB_SELECTSTRING :
+ case CLB_DIR :
+ case CLB_GETTOPINDEX :
+ case CLB_FINDSTRING :
+ case CLB_GETSELCOUNT :
+ case CLB_GETSELITEMS :
+ case CLB_SETTABSTOPS :
+ case CLB_GETHORIZONTALEXTENT :
+ case CLB_SETCOLUMNWIDTH :
+ case CLB_ADDFILE :
+ case CLB_SETTOPINDEX :
+ case CLB_SELITEMRANGE :
+ case CLB_SETANCHORINDEX :
+ case CLB_GETANCHORINDEX :
+ case CLB_SETCARETINDEX :
+ case CLB_GETCARETINDEX :
+ case CLB_SETITEMHEIGHT :
+ case CLB_GETITEMHEIGHT :
+ case CLB_FINDSTRINGEXACT :
+ case CLB_SETLOCALE :
+ case CLB_GETLOCALE :
+ case CLB_SETCOUNT :
+ //
+ // Simply send it to the hwnd list child, but translate the message first to real LB_ message
+ //
+ return(SendMessage(lpColumnLB->hwndList, msg - CLB_BASE, wParam, lParam));
+
+ // -------------------------------------------------------------------
+ // put messages here which explicitly need to be passed to our PARENT
+ //
+ case WM_COMMAND:
+ /* if this is a notification message from our child translate */
+ /* it to look like it is from us and pass on to our parent */
+
+ if (LOWORD(wParam) == IDL_COLUMNLISTBOX)
+ return(SendMessage( GetParent(hwnd),
+ msg,
+ MAKELONG( GetDlgCtrlID(hwnd) ,HIWORD(wParam)),
+ (LPARAM)hwnd )); // make it look like we were the ones sending the message...
+ else
+ return(SendMessage(GetParent(hwnd), msg, wParam, (LPARAM)hwnd));
+
+ case WM_VKEYTOITEM:
+ // pass on to our parent but using our hwnd...
+ if (lpColumnLB->Style & (LBS_WANTKEYBOARDINPUT | VLBS_WANTKEYBOARDINPUT) )
+ return(SendMessage(GetParent(hwnd), msg, wParam, (LPARAM)hwnd));
+ else
+ return(-1); // perform default action...
+
+ case WM_CHARTOITEM:
+ if (lpColumnLB->Style & (LBS_WANTKEYBOARDINPUT | VLBS_WANTKEYBOARDINPUT) )
+ if ((result = SendMessage(GetParent(hwnd), msg, wParam, (LPARAM)hwnd)) != -1)
+ return(result);
+
+ return HANDLE_WM_CHARTOITEM(hwnd, wParam, lParam, ColumnLBClass_OnCharToItem);
+
+ case WM_COMPAREITEM:
+ {
+ LPCOMPAREITEMSTRUCT lpCompareItem = (LPCOMPAREITEMSTRUCT)lParam;
+ int result;
+
+ if ((lpColumnLB->Style & LBS_OWNERDRAWFIXED) ||
+ (lpColumnLB->Style & VLBS_OWNERDRAWFIXED))
+ {
+ //
+ // substitute our values in the COMPAREITEMSTRUCT...
+ //
+ lpCompareItem->CtlID = GetDlgCtrlID(hwnd);
+ lpCompareItem->hwndItem = hwnd;
+
+ //
+ // then pass to our parent as our WM_COMPAREITEM, with the current physcial sort column as wParam
+ //
+ result = (int)SendMessage(GetParent(hwnd), WM_COMPAREITEM, (WPARAM)lpColumnLB->SortColumn, (LPARAM)lpCompareItem);
+ return(result);
+ }
+ else
+ {
+ LPTSTR lpString1;
+ LPTSTR lpString2;
+ LPCOLRECORD lpColRecord1;
+ LPCOLRECORD lpColRecord2;
+
+ // handle ourselves assuming item data is pointer to array of LPTSTR's
+ lpColRecord1 = (LPCOLRECORD)lpCompareItem->itemData1;
+ lpColRecord2 = (LPCOLRECORD)lpCompareItem->itemData2;
+ lpString1 = lpColRecord1->pString[lpColumnLB->SortColumn];
+ lpString2 = lpColRecord2->pString[lpColumnLB->SortColumn];
+ if (lpString1 && lpString2)
+ return(lstrcmpi(lpString1, lpString2));
+ else
+ return(0);
+ }
+ }
+ break;
+
+ // ---------------------------------------------------------
+ // handle our own messages
+ // ---------------------------------------------------------
+
+ //
+ // NUMBER COLUMNS
+ //
+ case CLB_GETNUMBERCOLS : // get the number of columns (ret=NumCols)
+ return ColumnLBClass_OnNumberCols(hwnd, 0, FALSE);
+
+ case CLB_SETNUMBERCOLS : // set the number of columns (wparam=NumCols)
+ return ColumnLBClass_OnNumberCols(hwnd, (BYTE)wParam, TRUE);
+
+ // ----------------------------------------------------------------
+ // Following messages use physical column numbers
+ // ----------------------------------------------------------------
+ //
+ // COLUMN WIDTH
+ //
+ case CLB_GETCOLWIDTH : // get a column width (wParm=Physical Column ret=ColWidth in LU's)
+ return ColumnLBClass_OnColWidth(hwnd, (BYTE)wParam, (int)0, FALSE);
+
+ case CLB_SETCOLWIDTH : // set a column width (wParm=Physical Column lParam=Width)
+ return ColumnLBClass_OnColWidth(hwnd, (BYTE)wParam, (int)lParam, TRUE);
+
+ case CLB_AUTOWIDTH : // auto-matically set column widths using titles... (wParam = Physical Column to auto width)
+ return ColumnLBClass_OnAutoWidth(hwnd, (BYTE)wParam);
+
+ //
+ // COLUMN TITLE
+ //
+ case CLB_GETCOLTITLE : // get a column's title (wParm=Physical Column, ret=Title)
+ return (LRESULT)ColumnLBClass_OnColTitle(hwnd, (BYTE)wParam, (LPTSTR)NULL, FALSE);
+
+ case CLB_SETCOLTITLE : // set a column's title (wParm=Physical Col, lParm=Title)
+ return (LRESULT)ColumnLBClass_OnColTitle(hwnd, (BYTE)wParam, (LPTSTR)lParam, TRUE);
+
+ case CLB_GETROWCOLTEXT:
+ //
+ // if the parent is NOT handling OWNERDRAW, then we need to handle
+ //
+ if ( ! (lpColumnLB->Style & (LBS_OWNERDRAWFIXED | VLBS_OWNERDRAWFIXED)) )
+ {
+ INT WhichCol = (INT)(wParam);
+ INT WhichRow = (INT)(lParam);
+
+ // and now pass on to get the text...
+ lpRecord = (LPCOLRECORD)SendMessage(lpColumnLB->hwndList, LB_GETITEMDATA, WhichRow, 0);
+
+ if (lpRecord == (LPCOLRECORD)LB_ERR)
+ return((LRESULT)NULL);
+
+ return (LRESULT)lpRecord->pString[WhichCol];
+ }
+ return((LRESULT)NULL); // owner draw, the owner has to figure this out themselves
+
+
+ //
+ // SORT COLUMN
+ //
+ case CLB_GETSORTCOL : // get the physical sort column (ret=Physical Col)
+ return (LRESULT)ColumnLBClass_OnSortCol(hwnd, 0, FALSE);
+
+ case CLB_SETSORTCOL : // set the physical sort column (wParm=Physical Col)
+ return (LRESULT)ColumnLBClass_OnSortCol(hwnd, (BYTE)wParam, TRUE);
+
+ //
+ // COLUMN ORDER
+ //
+ case CLB_GETCOLORDER : // get the virtual order of physical columns (ret = LPDWORD order table)
+ return (LRESULT)ColumnLBClass_OnColOrder(hwnd, (LPBYTE)0, FALSE);
+
+ case CLB_SETCOLORDER : // get the virtual order of physical columns (wParam = LPDWORD order table)
+ return (LRESULT)ColumnLBClass_OnColOrder(hwnd, (LPBYTE)lParam, TRUE);
+
+
+
+ case CLB_CHECKFOCUS: // does the listbox have the focus?
+// if (lpColumnLB->fUseVlist) // then we have to ask vlist the info
+// return
+// else
+ return lpColumnLB->fHasFocus;
+
+
+ // ----------------------------------------------------------------
+ // Following messages use virtual column numbers
+ // ----------------------------------------------------------------
+
+ //
+ // COLUMN OFFSETS
+ //
+ case CLB_GETCOLOFFSETS : // gets the incremental virtual col offsets (ret=LPDWORD)
+ return (LRESULT)ColumnLBClass_OnColOffsets(hwnd, (LPINT)wParam, FALSE);
+
+// case CLB_SETCOLOFFSETS : // gets the incremental virtual col offsets (ret=LPDWORD)
+// return (LRESULT)ColumnLBClass_OnColOffsets(hwnd, (LPDWORD)wParam, TRUE);
+
+
+ // =================================================================
+ // INTERNAL
+ //
+ case CLB_HSCROLL : // a hscroll event (INTERNAL)
+ return ColumnLBClass_OnHScroll(hwnd, (HWND)(lParam), (int)LOWORD(wParam), (int)HIWORD(wParam));
+
+
+ //
+ // GET FOCUS
+ //
+ case CLB_GETFOCUS : // get the handle for the window of CLB with the key focus
+ if ( lpColumnLB->fUseVlist )
+ // we must ask the column list box below us for the information.
+ return SendMessage ( lpColumnLB->hwndList, VLB_GETFOCUSHWND, 0,0 );
+ return (LRESULT) lpColumnLB->hwndList;
+
+ default:
+ return(DefWindowProc(hwnd, msg, wParam, lParam));
+ }
+
+ return(TRUE);
+}
+
+
+// ------------------------------------------------------------------
+// ColumnLBClass_OnNCCreate()
+//
+// Handles WM_NCCCREATE message
+//
+// HISTORY:
+// Tom Laird-McConnell 4/18/93 Created
+// ------------------------------------------------------------------
+BOOL ColumnLBClass_OnNCCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
+{
+ LPCOLUMNLBSTRUCT lpColumnLB;
+ HWND hwndList;
+ HWND hwndTitleList;
+ BOOL fUseVlist;
+ HDC hdc;
+ TEXTMETRIC tm;
+ RECT rect;
+ WORD ncxBorder;
+ WORD ncyBorder;
+ WORD yTitle;
+
+ ncxBorder = GetSystemMetrics(SM_CXBORDER);
+ ncyBorder = GetSystemMetrics(SM_CYBORDER);
+
+ //
+ // if the classname is for ColumnVLB class, then they want the Column Virtual list box...
+ //
+ if (lstrcmpi(lpCreateStruct->lpszClass, COLUMNVLBCLASS_CLASSNAME) == 0)
+ fUseVlist = TRUE;
+ else
+ fUseVlist = FALSE;
+
+ hdc = GetDC(hwnd);
+ GetTextMetrics(hdc, &tm);
+ ReleaseDC(hwnd, hdc);
+
+ yTitle = tm.tmHeight + 2*ncyBorder;
+
+ GetClientRect(hwnd, &rect);
+
+ //
+ // create the title list box window...
+ //
+ hwndTitleList = CreateWindow( (LPTSTR) TEXT("LISTBOX"),
+ (LPTSTR) TEXT(""),
+ (lpCreateStruct->style & ~WS_BORDER) |
+ LBS_NOINTEGRALHEIGHT |
+ LBS_OWNERDRAWFIXED |
+ WS_VISIBLE |
+ WS_CHILD,
+ ncxBorder,
+ ncyBorder,
+ lpCreateStruct->cx - (2*ncxBorder) - GetSystemMetrics(SM_CXVSCROLL),
+ yTitle,
+ hwnd,
+ (HMENU)IDL_COLUMNTITLELISTBOX,
+ lpCreateStruct->hInstance,
+ NULL);
+
+ if (fUseVlist == TRUE)
+ //
+ // create a Vlist window...
+ //
+ hwndList = CreateWindow((LPTSTR)VLIST_CLASSNAME,
+ (LPTSTR) TEXT(""),
+ (lpCreateStruct->style & ~(WS_BORDER | VLBS_HASSTRINGS)) | // NOTE: This can _never_ be hasstrings
+ VLBS_NOTIFY |
+ VLBS_USETABSTOPS |
+ VLBS_NOINTEGRALHEIGHT |
+ VLBS_OWNERDRAWFIXED | // we always force this as either we will owner draw, or our parent will
+ WS_HSCROLL |
+ WS_VSCROLL |
+ VLBS_DISABLENOSCROLL |
+ WS_VISIBLE |
+ WS_CHILD,
+ ncxBorder,
+ yTitle + ncyBorder,
+ lpCreateStruct->cx - ncxBorder, // -(2*ncxBorder)
+ lpCreateStruct->cy - yTitle - ncyBorder,
+ hwnd,
+ (HMENU)IDL_COLUMNLISTBOX,
+ lpCreateStruct->hInstance,
+ NULL);
+ else
+ //
+ // create a list box window...
+ //
+#ifdef H_SCROLL
+ hwndList = CreateWindow((LPTSTR) TEXT("LISTBOX"),
+ (LPTSTR) TEXT(""),
+ (lpCreateStruct->style & ~(WS_BORDER | LBS_HASSTRINGS)) | // NOTE: This can _never_ be hasstrings
+ LBS_NOTIFY |
+ LBS_USETABSTOPS |
+ LBS_NOINTEGRALHEIGHT |
+ LBS_OWNERDRAWFIXED | // we always force this as either we will owner draw, or our parent will
+ WS_HSCROLL |
+ WS_VSCROLL |
+ LBS_DISABLENOSCROLL |
+ WS_VISIBLE |
+ WS_CHILD,
+ ncxBorder,
+ yTitle + ncyBorder,
+ lpCreateStruct->cx - ncxBorder, // -(2*ncxBorder)
+ lpCreateStruct->cy - yTitle - ncyBorder,
+ hwnd,
+ (HMENU)IDL_COLUMNLISTBOX,
+ lpCreateStruct->hInstance,
+ NULL);
+#else
+ hwndList = CreateWindow((LPTSTR) TEXT("LISTBOX"),
+ (LPTSTR) TEXT(""),
+ (lpCreateStruct->style & ~(WS_BORDER | LBS_HASSTRINGS)) | // NOTE: This can _never_ be hasstrings
+ LBS_NOTIFY |
+ LBS_USETABSTOPS |
+ LBS_NOINTEGRALHEIGHT |
+ LBS_OWNERDRAWFIXED | // we always force this as either we will owner draw, or our parent will
+ WS_VSCROLL |
+ LBS_DISABLENOSCROLL |
+ WS_VISIBLE |
+ WS_CHILD,
+ ncxBorder,
+ yTitle + ncyBorder,
+ lpCreateStruct->cx - ncxBorder, // -(2*ncxBorder)
+ lpCreateStruct->cy - yTitle - ncyBorder,
+ hwnd,
+ (HMENU)IDL_COLUMNLISTBOX,
+ lpCreateStruct->hInstance,
+ NULL);
+#endif
+
+ //
+ // if we succeeded...
+ //
+ if (hwndList)
+ {
+ //
+ // create a ColumnLB struct to keep track of all of the pertinent instance
+ // info for this instance of a ColumnLB window
+ //
+ lpColumnLB = (LPCOLUMNLBSTRUCT)GlobalAllocPtr(GPTR, sizeof(COLUMNLBSTRUCT));
+
+ if (lpColumnLB)
+ {
+ BYTE i;
+
+ //
+ // store it in the window data for this window
+ //
+ SetWindowLong(hwnd, 0, (DWORD)lpColumnLB);
+
+ memset(lpColumnLB, '\0', sizeof(COLUMNLBSTRUCT));
+
+ //
+ // fill in pertinent info
+ //
+ lpColumnLB->Style = lpCreateStruct->style;
+
+ lpColumnLB->hInstance = (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE);
+
+ lpColumnLB->fUseVlist = fUseVlist;
+ lpColumnLB->fSorting = FALSE;
+ lpColumnLB->fMouseState = 0;
+
+ lpColumnLB->hwndList = hwndList;
+ lpColumnLB->OldListboxProc = (FARPROC)GetWindowLong(hwndList, GWL_WNDPROC);
+ lpColumnLB->NewListboxProc = MakeProcInstance((FARPROC)ColumnLBClass_ListBoxWndProc, hInst);
+
+ lpColumnLB->hwndTitleList = hwndTitleList;
+ lpColumnLB->OldTitleListboxProc = (FARPROC)GetWindowLong(hwndTitleList, GWL_WNDPROC);
+ lpColumnLB->NewTitleListboxProc = MakeProcInstance((FARPROC)ColumnLBClass_TitleListBoxWndProc, hInst);
+
+ lpColumnLB->nColumns=1;
+
+ lpColumnLB->yTitle = yTitle;
+
+ //
+ // init sort order
+ //
+ for (i=0; i < MAX_COLUMNS ; i++ )
+ lpColumnLB->ColumnOrderTable[i] = i;
+
+ //
+ // sub-class the listbox window by substituting our window proc for the
+ // normal one...
+ //
+ SetWindowLong(hwndList, GWL_WNDPROC,(DWORD)lpColumnLB->NewListboxProc);
+
+ //
+ // sub-class the title listbox window by substituting our window proc for the
+ // normal one...
+ //
+ SetWindowLong(hwndTitleList, GWL_WNDPROC,(DWORD)lpColumnLB->NewTitleListboxProc);
+
+ //
+ // add the lpColumnLB struct as the only item in the title listbox
+ //
+ ListBox_AddString(lpColumnLB->hwndTitleList, (DWORD)lpColumnLB);
+
+ //
+ // pass this on to the default window proc
+ //
+ return(FORWARD_WM_NCCREATE(hwnd, lpCreateStruct, DefWindowProc));
+ }
+ else
+ {
+ return(FALSE);
+ }
+ }
+ else
+ return(FALSE);
+}
+
+
+// ------------------------------------------------------------------
+// ColumnLBClass_OnDestroy()
+//
+// Handles WM_DESTROY message
+//
+// HISTORY:
+// Tom Laird-McConnell 7/18/93 Created
+// ------------------------------------------------------------------
+void ColumnLBClass_OnDestroy(HWND hwnd)
+{
+ LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0);
+
+ DestroyWindow(lpColumnLB->hwndTitleList);
+ DestroyWindow(lpColumnLB->hwndList);
+}
+
+// ------------------------------------------------------------------
+// ColumnLBClass_OnNCDestroy()
+//
+// Handles WM_NCDESTROY
+//
+// HISTORY:
+// Tom Laird-McConnell 4/18/93 Created
+// ------------------------------------------------------------------
+void ColumnLBClass_OnNCDestroy(HWND hwnd)
+{
+ LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0);
+ FreeProcInstance(lpColumnLB->NewListboxProc);
+
+ GlobalFreePtr(lpColumnLB);
+}
+
+// ------------------------------------------------------------------
+// ColumnLBClass_OnPaint()
+//
+// Handles WM_PAINT message, draws column titles as appropriate...
+//
+// HISTORY:
+// Tom Laird-McConnell 4/18/93 Created
+// ------------------------------------------------------------------
+void ColumnLBClass_OnPaint(HWND hwnd)
+{
+ LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0);
+ PAINTSTRUCT ps;
+ HBRUSH hFrameBrush, hGreyBrush;
+ RECT rect;
+ HDC hdc;
+ int ncxBorder = GetSystemMetrics(SM_CXBORDER);
+ int ncyBorder = GetSystemMetrics(SM_CYBORDER);
+
+ BeginPaint(hwnd, (LPPAINTSTRUCT)&ps);
+
+ // Draw border around title and listbox
+ GetClientRect(hwnd, (LPRECT)&rect);
+
+ hdc = ps.hdc;
+
+ hFrameBrush = CreateSolidBrush(GetSysColor(COLOR_WINDOWFRAME));
+ FrameRect(hdc, (LPRECT)&rect, hFrameBrush);
+
+ // make bottom the height of a HSCROLL bar
+ // make left side the width of a VSCROLL bar
+ rect.top += ncyBorder;
+ rect.right -= ncxBorder;
+ rect.left = rect.right - GetSystemMetrics(SM_CXVSCROLL);
+ rect.bottom = lpColumnLB->yTitle+ncyBorder;
+
+ hGreyBrush = CreateSolidBrush(GetSysColor(COLOR_SCROLLBAR));
+ FillRect(hdc, &rect, hGreyBrush);
+
+ rect.right = rect.left+1;
+ FillRect(hdc, &rect, hFrameBrush);
+
+ // destroy brushes...
+ DeleteBrush(hFrameBrush);
+ DeleteBrush(hGreyBrush);
+
+ EndPaint(hwnd, (LPPAINTSTRUCT)&ps);
+}
+
+
+
+// ------------------------------------------------------------------
+// ColumnLBClass_OnSize()
+//
+// Handles WM_SIZE message
+//
+// HISTORY:
+// Tom Laird-McConnell 4/18/93 Created
+// ------------------------------------------------------------------
+void ColumnLBClass_OnSize(HWND hwnd, UINT state, int cx, int cy)
+{
+ WORD ncxBorder;
+ WORD ncyBorder;
+ RECT rect;
+ DWORD cxExtent;
+
+ LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0);
+
+ if (lpColumnLB->hwndList != (HWND)NULL)
+ {
+ ncxBorder = GetSystemMetrics(SM_CXBORDER);
+ ncyBorder = GetSystemMetrics(SM_CYBORDER);
+
+ // position title listbox at top
+ MoveWindow(lpColumnLB->hwndTitleList,
+ ncxBorder,
+ ncyBorder,
+ cx-(2*ncxBorder) - GetSystemMetrics(SM_CXVSCROLL),
+ lpColumnLB->yTitle,
+ TRUE);
+
+ // position list box below title listbox
+ MoveWindow(lpColumnLB->hwndList,
+ ncxBorder,
+ lpColumnLB->yTitle + ncyBorder,
+ cx - ncxBorder, // -(2*ncxBorder)
+ cy-lpColumnLB->yTitle - ncyBorder,
+ TRUE);
+
+ cxExtent = ColumnLBClass_ComputeOffsets(hwnd);
+
+ GetClientRect(hwnd, &rect);
+
+ //
+ // if the new extent is smaller then the space available, move the position
+ //
+ if ((DWORD)rect.right > cxExtent)
+ {
+// #ifdef DEBUG
+// dprintf(TEXT("Reset HSCROLL pos to far left\n"));
+// #endif
+ // move position to far left
+ SendMessage(lpColumnLB->hwndList,
+ (lpColumnLB->fUseVlist) ? VLB_HSCROLL : WM_HSCROLL,
+ MAKEWPARAM(SB_TOP, 0), 0);
+
+ // do the same for the title list
+ SendMessage(lpColumnLB->hwndTitleList,
+ WM_HSCROLL, MAKEWPARAM(SB_TOP, 0), 0);
+ }
+
+ InvalidateRect(lpColumnLB->hwndList, NULL, TRUE);
+ InvalidateRect(lpColumnLB->hwndTitleList, NULL, TRUE);
+ }
+ InvalidateRect(lpColumnLB->hwndTitleList, 0, TRUE);
+ InvalidateRect(lpColumnLB->hwndList, 0, TRUE);
+}
+
+// ------------------------------------------------------------------
+// ColumnLBClass_OnSetFont()
+//
+// Handles WM_SETFONT message
+//
+// HISTORY:
+// Tom Laird-McConnell 4/18/93 Created
+// ------------------------------------------------------------------
+void ColumnLBClass_OnSetFont(HWND hwnd, HFONT hFont, BOOL fRedraw)
+{
+ LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0);
+ HDC hdc;
+ TEXTMETRIC tm;
+ RECT rect;
+
+ lpColumnLB->hFont = hFont;
+
+ hdc = GetDC(hwnd);
+ SelectFont(hdc, (HFONT)hFont);
+ GetTextMetrics(hdc, &tm);
+
+ //
+ // forward on to listbox window
+ //
+ if (lpColumnLB->hwndList != (HWND)NULL)
+ FORWARD_WM_SETFONT(lpColumnLB->hwndList, hFont, fRedraw, SendMessage);
+
+ if (lpColumnLB->hwndTitleList != (HWND)NULL)
+ FORWARD_WM_SETFONT(lpColumnLB->hwndTitleList, hFont, fRedraw, SendMessage);
+
+ //
+ // store text height...
+ //
+ lpColumnLB->yTitle = tm.tmHeight + 2*GetSystemMetrics(SM_CYBORDER);
+
+ //
+ // change height appropriately
+ //
+ ListBox_SetItemHeight(lpColumnLB->hwndTitleList, 0, lpColumnLB->yTitle);
+
+ SendMessage(lpColumnLB->hwndList,
+ (lpColumnLB->fUseVlist) ? VLB_SETITEMHEIGHT : LB_SETITEMHEIGHT,
+ 0,
+ tm.tmHeight);
+
+ //
+ // since we changed the font size, forze a WM_SIZE to recalc the size of the window
+ //
+ GetClientRect(hwnd, &rect);
+ ColumnLBClass_OnSize(hwnd, SIZE_RESTORED, rect.right-rect.left, rect.bottom-rect.top);
+
+ ReleaseDC(hwnd, hdc);
+}
+
+
+// ------------------------------------------------------------------
+// ColumnLBClass_OnHScroll()
+//
+//
+// Handles OnHScroll messages to keep title bar in ssync with listbox...
+//
+// HISTORY:
+// Tom Laird-McConnell 5/1/93 Created
+// ------------------------------------------------------------------
+LRESULT ColumnLBClass_OnHScroll(HWND hwnd, HWND hwndCtl, UINT code, int pos)
+{
+ LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0);
+
+ long lPos;
+ WORD nPos;
+ RECT rect;
+ int cxExtent;
+
+ switch (code)
+ {
+ case SB_THUMBPOSITION:
+ case SB_THUMBTRACK:
+ nPos = pos;
+ break;
+
+ case SB_LINEUP:
+ case SB_LINEDOWN:
+ case SB_PAGEUP:
+ case SB_PAGEDOWN:
+ case SB_TOP:
+ case SB_BOTTOM:
+ case SB_ENDSCROLL:
+ if (lpColumnLB->fUseVlist)
+ nPos = (WORD)SendMessage(lpColumnLB->hwndList, VLB_GETSCROLLPOS, 0, 0);
+ else
+ nPos = GetScrollPos((hwndCtl) ? hwndCtl : lpColumnLB->hwndList, SB_HORZ);
+// nPos = GetScrollPos(lpColumnLB->hwndList, SB_HORZ);
+ break;
+
+ default:
+ return(TRUE);
+ }
+
+ GetClientRect(lpColumnLB->hwndList, (LPRECT)&rect);
+
+ //... if it is a VLIST, then there is an error in the client calculation when it has VSCROLL bars, so
+ // we need to adjust ourselves by width of VSCROLL bar...
+ if (lpColumnLB->fUseVlist)
+ rect.right -= GetSystemMetrics(SM_CXVSCROLL);
+
+ cxExtent = (DWORD)SendMessage(lpColumnLB->hwndList,
+ (lpColumnLB->fUseVlist) ? VLB_GETHORIZONTALEXTENT : LB_GETHORIZONTALEXTENT, 0, 0L);
+ if (cxExtent >= rect.right)
+ {
+ // then the listbox size is > then client's display size
+ // so we need to calculate how much is not on the client display
+ cxExtent -= rect.right;
+ }
+ else
+ // else set the amount left over to 0 to nullify (technical term) the
+ // effects of this calculation...
+ cxExtent = 0;
+
+ lPos = -(((LONG)nPos*(LONG)cxExtent)/100);
+ if (lPos > 0)
+ lpColumnLB->xPos = 0;
+ else
+ if (lPos < -cxExtent)
+ lpColumnLB->xPos = -cxExtent;
+ else
+ lpColumnLB->xPos = (int)lPos;
+}
+
+
+
+// ------------------------------------------------------------------
+// ColumnLBClass_OnMeasureItem()
+//
+//
+// Handles telling the parent how to draw each column accordingly...
+//
+//
+// HISTORY:
+// Tom Laird-McConnell 4/18/93 Created
+// ------------------------------------------------------------------
+void ColumnLBClass_OnMeasureItem(HWND hwnd, LPMEASUREITEMSTRUCT lpMeasureItem)
+{
+ LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0L);
+ TEXTMETRIC tm;
+ HDC hdc;
+
+ if (lpMeasureItem->CtlID == IDL_COLUMNTITLELISTBOX)
+ {
+ if (lpColumnLB)
+ lpMeasureItem->itemHeight = lpColumnLB->yTitle;
+ else
+ {
+ hdc = GetDC(hwnd);
+ GetTextMetrics(hdc, &tm);
+ ReleaseDC(hwnd, hdc);
+
+ lpMeasureItem->itemHeight = tm.tmHeight;
+ }
+ }
+ else
+ //
+ // it should be passed to parent
+ //
+ FORWARD_WM_MEASUREITEM(GetParent(hwnd), lpMeasureItem, SendMessage);
+}
+
+// ------------------------------------------------------------------
+// ColumnLBClass_OnDeleteItem()
+//
+//
+// Handles deleting items if necessary...
+//
+//
+// HISTORY:
+// Tom Laird-McConnell 08/18/93 Created
+// ------------------------------------------------------------------
+void ColumnLBClass_OnDeleteItem(HWND hwnd, const DELETEITEMSTRUCT *lpDeleteItem)
+{
+ LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0L);
+ LPCOLRECORD lpRecord;
+
+ // don't actually do the delete if we are sorting...
+ if (lpColumnLB->fSorting == TRUE)
+ return;
+
+ if (lpDeleteItem->CtlID == IDL_COLUMNLISTBOX)
+ {
+ // if the style is that the owner is handling the owner draw stuff
+ // then we need to pass to the parent ELSE free our memory...
+ if ((lpColumnLB) && (lpColumnLB->Style & LBS_OWNERDRAWFIXED))
+ //
+ // it should be passed to parent
+ //
+ FORWARD_WM_DELETEITEM(GetParent(hwnd), lpDeleteItem, SendMessage);
+ else
+ // this is our item data, so we need to free it
+ if (lpDeleteItem->itemData)
+ {
+ lpRecord = (LPCOLRECORD)lpDeleteItem->itemData;
+ // NOTE that the first pointer is actually the string buffer...
+ if (lpRecord->pString[0])
+ GlobalFreePtr(lpRecord->pString[0]);
+ GlobalFreePtr(lpRecord);
+ }
+ }
+}
+
+// ------------------------------------------------------------------
+// ColumnLBClass_OnDrawItem()
+//
+//
+// Handles telling the parent to draw each column accordingly...
+//
+//
+// HISTORY:
+// Tom Laird-McConnell 4/18/93 Created
+// ------------------------------------------------------------------
+void ColumnLBClass_OnDrawItem(HWND hwnd, const DRAWITEMSTRUCT * lpDrawItem)
+{
+ LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0L);
+ HWND hwndParent = GetParent(hwnd);
+ BYTE i;
+ BYTE PhysCol;
+ CLBDRAWITEMSTRUCT CLBDrawItemStruct;
+ RECT rect;
+ int ncxBorder = GetSystemMetrics(SM_CXBORDER);
+ int ncyBorder = GetSystemMetrics(SM_CYBORDER);
+ HPEN hFramePen;
+ HPEN hShadowPen;
+ HPEN hHighlightPen;
+ HPEN hOldPen;
+ HBRUSH hBackgroundBrush;
+ DWORD Col;
+ DWORD cyChar;
+ TEXTMETRIC tm;
+ BYTE PhysColumn;
+ RECT ClientRect;
+
+ GetClientRect(lpDrawItem->hwndItem, &ClientRect);
+
+ //
+ // figure out which window sent us the DrawItem
+ //
+ switch (lpDrawItem->CtlID)
+ {
+ //
+ // handle drawing the title listbox
+ //
+ case IDL_COLUMNTITLELISTBOX:
+ {
+
+ LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0);
+
+ if (lpDrawItem->itemAction == ODA_DRAWENTIRE)
+ {
+ GetTextMetrics(lpDrawItem->hDC, &tm);
+
+ cyChar = tm.tmHeight;
+
+ //
+ // create all of our pens for our drawing
+ //
+ hHighlightPen = CreatePen(PS_SOLID, ncyBorder, GetSysColor(COLOR_BTNHIGHLIGHT));
+ hShadowPen = CreatePen(PS_SOLID, ncyBorder, GetSysColor(COLOR_BTNSHADOW));
+ hFramePen = CreatePen(PS_SOLID, ncyBorder, GetSysColor(COLOR_WINDOWFRAME));
+
+ hBackgroundBrush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
+
+ //
+ // get the window rect we are going to work with
+ //
+ CopyRect(&rect, &lpDrawItem->rcItem);
+ FillRect(lpDrawItem->hDC, &rect, hBackgroundBrush);
+
+ //
+ // Draw frame color line below title section of property window
+ //
+ hOldPen = SelectObject(lpDrawItem->hDC, hFramePen);
+ MoveToEx(lpDrawItem->hDC, rect.left, rect.bottom, NULL);
+ LineTo(lpDrawItem->hDC, rect.right, rect.bottom);
+
+ //
+ // we start at the current left
+ //
+ rect.top += 2*ncyBorder;
+
+ SetTextColor(lpDrawItem->hDC, GetSysColor(COLOR_BTNTEXT));
+ SetBkColor(lpDrawItem->hDC, GetSysColor(COLOR_BTNFACE));
+
+ SetBkMode(lpDrawItem->hDC, TRANSPARENT);
+
+ for (Col=0; Col < lpColumnLB->nColumns; Col++)
+ {
+ //
+ // get the index number of the current column
+ //
+ PhysColumn = lpColumnLB->ColumnOrderTable[Col];
+
+ //
+ // adjust right side to be left side plus width of current column
+ //
+ rect.right = rect.left + lpColumnLB->ColumnInfoTable[PhysColumn].Width;
+
+ //
+ // if the button is dpressed, then draw it that way
+ //
+ if (lpColumnLB->ColumnInfoTable[PhysColumn].fDepressed)
+ {
+ //
+ // pick the shadow pen and draw the top-left depressed
+ //
+ SelectObject(lpDrawItem->hDC, hShadowPen);
+ MoveToEx(lpDrawItem->hDC, rect.left, rect.bottom, NULL);
+ LineTo(lpDrawItem->hDC, rect.left, rect.top-2*ncyBorder); // bottom-left --> top-left
+ LineTo(lpDrawItem->hDC, rect.right, rect.top-2*ncyBorder); // top-left --> top-right
+
+ //
+ // pick the Frame pen and draw the Column seperater
+ //
+ SelectObject(lpDrawItem->hDC, hFramePen);
+ MoveToEx(lpDrawItem->hDC, rect.right+ncxBorder, rect.top-2*ncyBorder, NULL);
+ LineTo(lpDrawItem->hDC, rect.right+ncxBorder, rect.bottom); // bottom-left --> top-left
+
+ //
+ // move the cursor for whitespace to draw text
+ //
+ rect.left += WHITESPACE/2;
+
+ // draw the title of the column in the current slot
+ DrawText(lpDrawItem->hDC,
+ lpColumnLB->ColumnInfoTable[PhysColumn].lpTitle,
+ -1,
+ (LPRECT)&rect,
+ DT_SINGLELINE | DT_LEFT | DT_TOP);
+ rect.left -= WHITESPACE/2; // restore the left position...
+ }
+ else
+ {
+ // it is not depressed, draw it normal
+
+ //
+ // pick the white pen and draw the top-left highlight
+ //
+ SelectObject(lpDrawItem->hDC, hHighlightPen);
+ MoveToEx(lpDrawItem->hDC, rect.left, rect.bottom-ncyBorder, NULL);
+ LineTo(lpDrawItem->hDC, rect.left, rect.top-2*ncyBorder); // bottom-left --> top-left
+ LineTo(lpDrawItem->hDC, rect.right, rect.top-2*ncyBorder); // top-left --> top-right
+
+ //
+ // pick the shadow pen and draw the bottom-right dark shadow
+ //
+ SelectObject(lpDrawItem->hDC, hShadowPen);
+ LineTo(lpDrawItem->hDC, rect.right, rect.bottom-ncyBorder); // top-right --> bottom-right
+ LineTo(lpDrawItem->hDC, rect.left, rect.bottom-ncyBorder); // bottom-right --> bottom-left
+
+ //
+ // pick the Frame pen and draw the Column seperater
+ //
+ SelectObject(lpDrawItem->hDC, hFramePen);
+ MoveToEx(lpDrawItem->hDC, rect.right+ncxBorder, rect.top-2*ncyBorder, NULL);
+ LineTo(lpDrawItem->hDC, rect.right+ncxBorder, rect.bottom); // bottom-left --> top-left
+
+ //
+ // move the cursor for whitespace to draw text
+ //
+ rect.left += WHITESPACE/4;
+
+ rect.top -= ncyBorder;
+
+ // draw the title of the column in the current slot
+ DrawText(lpDrawItem->hDC,
+ lpColumnLB->ColumnInfoTable[PhysColumn].lpTitle,
+ -1,
+ (LPRECT)&rect,
+ DT_SINGLELINE | DT_LEFT | DT_TOP);
+
+ rect.top += ncyBorder;
+ }
+
+ //
+ // adjust the left side of the rect for the width of this column
+ //
+ rect.left = rect.right+2*ncxBorder;
+ }
+
+ // select the original brush
+ SelectObject(lpDrawItem->hDC, hOldPen);
+
+ // delete my pens
+ DeletePen(hFramePen);
+ DeletePen(hHighlightPen);
+ DeletePen(hShadowPen);
+
+ DeleteBrush(hBackgroundBrush);
+ }
+ }
+ break;
+
+ //
+ // handle sending CLB_DRAWITEM MESSAGES to parent
+ //
+ case IDL_COLUMNLISTBOX:
+ {
+ //
+ // make a copy of the drawitem portion of the struct
+ //
+ memcpy(&CLBDrawItemStruct.DrawItemStruct, lpDrawItem, sizeof(DRAWITEMSTRUCT));
+
+ //
+ // fake parent window into thinking our id is the listbox
+ //
+ CLBDrawItemStruct.DrawItemStruct.CtlID = GetDlgCtrlID(hwnd);
+ CLBDrawItemStruct.lpColOrder = lpColumnLB->ColumnOrderTable;
+ CLBDrawItemStruct.nColumns = lpColumnLB->nColumns;
+
+ CopyRect(&rect, &lpDrawItem->rcItem);
+
+ //
+ // move the cursor for whitespace to draw text
+ //
+ rect.left += WHITESPACE/4;
+
+ //
+ // tell the parent to draw each physical column in the appropriate rectangle
+ //
+ for (i=0; i < lpColumnLB->nColumns ;i++ )
+ {
+ //
+ // get physical column number
+ //
+ PhysCol = lpColumnLB->ColumnOrderTable[i];
+
+ //
+ // massage the rect's right to be the left plus the width of the column
+ //
+ rect.right = rect.left + lpColumnLB->ColumnInfoTable[PhysCol].Width - WHITESPACE/4;
+
+ //
+ // copy it
+ //
+ CopyRect(&CLBDrawItemStruct.rect[i], &rect);
+
+ //
+ // massage the rect's left to be the right + 1
+ //
+ rect.left = rect.right + WHITESPACE/4 + 2*ncxBorder ;
+ }
+
+ if ((lpColumnLB->Style & LBS_OWNERDRAWFIXED) ||
+ (lpColumnLB->Style & VLBS_OWNERDRAWFIXED) )
+ //
+ // send a draw message with the physical column order list
+ // to the parent as they want to draw it
+ //
+ SendMessage(hwndParent, CLBN_DRAWITEM, (WPARAM)0, (WPARAM)&CLBDrawItemStruct);
+ else
+ {
+ //
+ // we want to draw it ourselves...
+ // NOTE: This assumes that we are LBS_HASSTRINGS and NOT LBS_OWNERDRAWFIXED
+ //
+ switch(lpDrawItem->itemAction)
+ {
+ case ODA_FOCUS:
+ DrawFocusRect(lpDrawItem->hDC,(LPRECT)&(lpDrawItem->rcItem));
+ break;
+
+ case ODA_DRAWENTIRE:
+ case ODA_SELECT:
+ // only if we have data...
+ if (lpDrawItem->itemData)
+ {
+ LPCOLRECORD lpColRecord = (LPCOLRECORD)lpDrawItem->itemData;
+
+ if ((lpColRecord == NULL) ||
+ (lpColRecord == (LPCOLRECORD)LB_ERR))
+ break; // bogus data
+
+
+ // Are we highlighted? (highlit?)
+ if (lpDrawItem->itemState & ODS_SELECTED)
+ {
+ hBackgroundBrush = CreateSolidBrush(GetSysColor(COLOR_HIGHLIGHT));
+ SetBkColor(lpDrawItem->hDC, GetSysColor(COLOR_HIGHLIGHT));
+ SetTextColor(lpDrawItem->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
+ }
+ else
+ {
+ hBackgroundBrush = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
+ SetBkColor(lpDrawItem->hDC, GetSysColor(COLOR_WINDOW));
+ SetTextColor(lpDrawItem->hDC, GetSysColor(COLOR_WINDOWTEXT));
+ }
+ // FillRect(lpDrawItem->hDC,(LPRECT)&(lpDrawItem->rcItem), hBackgroundBrush);
+
+ //
+ // either way, draw column borders now...
+ //
+ hFramePen = CreatePen(PS_SOLID, ncyBorder, GetSysColor(COLOR_WINDOWFRAME));
+
+ hOldPen = SelectObject(lpDrawItem->hDC, hFramePen);
+
+ //
+ // now draw each column in the approved order...
+ //
+ for (i=0; i < CLBDrawItemStruct.nColumns ; i++)
+ {
+ //
+ // draw line of text...
+ //
+ ExtTextOut( lpDrawItem->hDC,
+ CLBDrawItemStruct.rect[i].left,
+ CLBDrawItemStruct.rect[i].top,
+ ETO_CLIPPED | ETO_OPAQUE,
+ &CLBDrawItemStruct.rect[i],
+ lpColRecord->pString[CLBDrawItemStruct.lpColOrder[i]], // pointer to string
+ lstrlen(lpColRecord->pString[CLBDrawItemStruct.lpColOrder[i]]), // length
+ (LPINT)NULL);
+
+ // draw column seperator
+ ColumnLB_DrawColumnBorder( lpDrawItem->hDC, &CLBDrawItemStruct.rect[i], ClientRect.bottom, hBackgroundBrush);
+ }
+
+ // restore old pen
+ SelectObject(lpDrawItem->hDC, hOldPen);
+
+ // destroy pen
+ DeletePen(hFramePen);
+
+ DeleteBrush(hBackgroundBrush);
+ }
+ break;
+ } // end of switch on drawitem action
+ }
+
+ }
+ break;
+ }
+}
+
+// ------------------------------------------------------------------
+// ColumnLBClass_OnCharToItem()
+//
+// Handles converting keystrokes to items
+//
+// HISTORY:
+// Tom Laird-McConnell 10/18/93 Created
+// ------------------------------------------------------------------
+int ColumnLBClass_OnCharToItem(HWND hwnd, UINT ch, HWND hwndListbox, int iCaret)
+{
+ LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0);
+ LPCOLRECORD lpColRecord;
+ int nCount;
+ int nCurSel;
+ int nNewSel;
+ TCHAR cKey;
+ TCHAR cLBText;
+
+ if (hwndListbox != lpColumnLB->hwndTitleList)
+ {
+ RECT ClientRect;
+ GetClientRect(hwnd, &ClientRect);
+
+ //
+ // if the parent is NOT ownerdraw, then we are doing it ourselves, and
+ // so need to translate the WM_CHAR --> the correct item based on the
+ // current sort column...
+ //
+ if (! (lpColumnLB->Style & (LBS_OWNERDRAWFIXED | VLBS_OWNERDRAWFIXED)) )
+ {
+ nCurSel = ListBox_GetCurSel(lpColumnLB->hwndList);
+ if (IsCharAlphaNumeric((TCHAR)ch))
+ {
+ nNewSel = nCurSel + 1;
+ nCount = ListBox_GetCount(lpColumnLB->hwndList);
+ cKey = toupper( (TCHAR)ch );
+
+ // loop thru items starting with the one just after
+ // the current selection, until we are too far along,
+ // then wrap around to the beginning and
+ // keep going until we hit our original selection.
+ for (; nNewSel != nCurSel ; nNewSel++ )
+ {
+ // make sure that we do't try to compare at location -1
+ if( nNewSel == -1)
+ continue;
+
+ lpColRecord = (LPCOLRECORD)ListBox_GetItemData(lpColumnLB->hwndList, nNewSel);
+
+ // if this comes back as LB_ERR then we are off the end of the list
+ if( lpColRecord == (LPCOLRECORD)LB_ERR )
+ {
+ nNewSel = -1; // increment will move to 0
+ continue;
+ }
+
+ cLBText = toupper( *lpColRecord->pString[
+ lpColumnLB->ColumnOrderTable[
+ lpColumnLB->SortColumn ]] );
+
+ if ( cLBText == cKey )
+ {
+ // we found it ...
+ // change the current selection
+ if( lpColumnLB->Style & LBS_MULTIPLESEL )
+ {
+ // multiple selection LB, just move fuzzy rect
+ ListBox_SetCaretIndex(lpColumnLB->hwndList, nNewSel);
+
+ // BUGBUG change of caret does not have a notification?
+ }
+ else
+ {
+ // single sel LB, change the sel
+ ListBox_SetCurSel(lpColumnLB->hwndList, nNewSel);
+
+ // notify our parent if we need to
+ if( lpColumnLB->Style & LBS_NOTIFY )
+ {
+ SendMessage( GetParent( hwnd ),
+ WM_COMMAND,
+ MAKEWPARAM( GetDlgCtrlID( hwnd), LBN_SELCHANGE),
+ (LPARAM)hwnd); // NOTE: substitute ourselves
+ // as the source of the message
+ }
+ }
+
+ return(-1); // we handled it...
+ }
+ else if (nNewSel == nCount-1)
+ {
+ // we have gone beyond it
+ // or are at the end of the list...
+
+ // we need to wrap to the beginning
+ // (this will get incremented above prior to use)
+ nNewSel = -1;
+ continue;
+ }
+ }
+
+ // we did not find our target
+ return(nCurSel);
+ }
+ else
+ // not an alphanumeric, just return the current selection
+ return(nCurSel);
+ }
+ else
+ //
+ // pass on to parent as a WM_CHARTOITEM, but with the HIWORD(wParam) == SORT COLUMN
+ //
+ return(SendMessage( GetParent(hwnd),
+ CLBN_CHARTOITEM,
+ MAKEWPARAM(ch, lpColumnLB->SortColumn),
+ (LPARAM)hwnd));
+ }
+}
+
+
+// ------------------------------------------------------------------
+// ColumnLBClass_OnNumberCols()
+//
+// case CLB_GETNUMBERCOLS : // get the number of columns (ret=NumCols)
+// case CLB_SETNUMBERCOLS : // set the number of columns (wparam=NumCols)
+//
+// HISTORY:
+// Tom Laird-McConnell 4/18/93 Created
+// ------------------------------------------------------------------
+BYTE ColumnLBClass_OnNumberCols(HWND hwnd, BYTE NewNumberCols, BOOL fSetColumns)
+{
+ LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0L);
+
+ //
+ // if we are modifying it
+ //
+ if (fSetColumns)
+ {
+ //
+ // if the value is a new value
+ //
+ if (lpColumnLB->nColumns != NewNumberCols)
+ {
+ lpColumnLB->nColumns = NewNumberCols;
+
+ // force a redraw of the entire columnlb...
+ InvalidateRect(hwnd, NULL, TRUE);
+ }
+ }
+ return lpColumnLB->nColumns;
+}
+
+// ------------------------------------------------------------------
+// ColumnLBClass_OnColWidth()
+//
+// case CLB_GETCOLWIDTH : // get a column width (wParm=Physical Column ret=ColWidth in DU's)
+// case CLB_SETCOLWIDTH : // set a column width (wParm=Physical Column lParam=Width)
+//
+// HISTORY:
+// Tom Laird-McConnell 4/18/93 Created
+// ------------------------------------------------------------------
+int ColumnLBClass_OnColWidth(HWND hwnd, BYTE Column, int NewWidth, BOOL fSetWidth)
+{
+ LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0L);
+ int cxExtent;
+ RECT rect;
+
+ //
+ // if we are modifying it
+ //
+ if (fSetWidth)
+ {
+ //
+ // if the value is a new value
+ //
+ if (lpColumnLB->ColumnInfoTable[Column].Width != NewWidth)
+ {
+ lpColumnLB->ColumnInfoTable[Column].Width = NewWidth;
+
+ cxExtent = ColumnLBClass_ComputeOffsets(hwnd);
+
+ GetClientRect(hwnd, &rect);
+
+ //
+ // send the message to the title listbox as well
+ //
+ SendMessage(lpColumnLB->hwndTitleList, LB_SETHORIZONTALEXTENT, cxExtent, 0L);
+
+ //
+ // pass it on to the child listbox, using VLB_SETHOR if appropriate...
+ //
+ SendMessage(lpColumnLB->hwndList,
+ (lpColumnLB->fUseVlist) ? VLB_SETHORIZONTALEXTENT : LB_SETHORIZONTALEXTENT, cxExtent, 0L);
+
+ //
+ // if the new extent is smaller then the space available, move the position
+ //
+ if (rect.right > cxExtent)
+ {
+// #ifdef DEBUG
+// dprintf(TEXT("Reset HSCROLL pos to far left\n"));
+// #endif
+ // move position to far left
+ SendMessage(lpColumnLB->hwndList,
+ (lpColumnLB->fUseVlist) ? VLB_HSCROLL : WM_HSCROLL,
+ MAKEWPARAM(SB_TOP, 0), 0);
+
+ // do the same for the title list
+ SendMessage(lpColumnLB->hwndTitleList,
+ WM_HSCROLL, MAKEWPARAM(SB_TOP, 0), 0);
+ }
+
+ InvalidateRect(lpColumnLB->hwndList, NULL, TRUE);
+ InvalidateRect(lpColumnLB->hwndTitleList, NULL, TRUE);
+ }
+ }
+ return (DWORD)lpColumnLB->ColumnInfoTable[Column].Width;
+}
+
+// ------------------------------------------------------------------
+// ColumnLBClass_OnColTitle()
+//
+// case CLB_GETCOLTITLE : // get a column's title (wParm=Physical Column, ret=Title)
+// case CLB_SETCOLTITLE : // set a column's title (wParm=Physical Col, lParm=Title)
+//
+// HISTORY:
+// Tom Laird-McConnell 4/18/93 Created
+// ------------------------------------------------------------------
+LPTSTR ColumnLBClass_OnColTitle(HWND hwnd, BYTE Column, LPTSTR lpTitle, BOOL fSetTitle)
+{
+ LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0L);
+
+ //
+ // if we are modifying it
+ //
+ if (fSetTitle)
+ {
+ //
+ // if the value is a new value
+ //
+ if (lpColumnLB->ColumnInfoTable[Column].lpTitle != lpTitle)
+ {
+ //
+ // BUGBUG, is there more to do here?
+ //
+ lpColumnLB->ColumnInfoTable[Column].lpTitle = lpTitle;
+
+ //
+ // invalidate the title
+ //
+ InvalidateRect(lpColumnLB->hwndTitleList, NULL, TRUE);
+ }
+ }
+ return (LPTSTR)lpColumnLB->ColumnInfoTable[Column].lpTitle;
+}
+
+
+
+
+// ------------------------------------------------------------------
+// ColumnLBClass_OnSortCol()
+//
+// case CLB_GETSORTCOL : // get the sort column (ret=Physical Col)
+// case CLB_SETSORTCOL : // set the sort column (wParm=Physical Col)
+//
+// HISTORY:
+// Tom Laird-McConnell 4/18/93 Created
+// ------------------------------------------------------------------
+BYTE ColumnLBClass_OnSortCol(HWND hwnd, BYTE NewSortCol, BOOL fSetSortCol)
+{
+ LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0L);
+ DWORD nCount;
+ LPDWORD lpListboxContents;
+ DWORD i;
+ int nCurSel;
+ DWORD ItemData;
+ HCURSOR hCursor;
+
+ //
+ // if we are modifying it
+ //
+ if (fSetSortCol)
+ {
+ hCursor = SetCursor(LoadCursor(0, IDC_WAIT));
+
+ // set new sort value
+ lpColumnLB->SortColumn = NewSortCol;
+
+ // need to resort listbox
+ nCount = ListBox_GetCount(lpColumnLB->hwndList);
+
+ // need to get current select
+ nCurSel = ListBox_GetCurSel(lpColumnLB->hwndList);
+
+ // and it's item data
+ ItemData = ListBox_GetItemData(lpColumnLB->hwndList, nCurSel);
+
+ SetWindowRedraw(lpColumnLB->hwndList, FALSE);
+
+ //
+ // allocate space for the listbox contents
+ //
+ lpListboxContents = (LPDWORD) GlobalAllocPtr(GPTR, sizeof(DWORD) * nCount);
+
+ //
+ // retrieve all of the data values
+ //
+ for (i=0; i<nCount; i++)
+ lpListboxContents[i] = ListBox_GetItemData(lpColumnLB->hwndList, i);
+
+ //
+ // reset the listbox contents
+ //
+ lpColumnLB->fSorting = TRUE; // disable deleting while sorting...
+ SendMessage(lpColumnLB->hwndList, LB_RESETCONTENT, 0, 0);
+ lpColumnLB->fSorting = FALSE; // reenable it...
+
+ //
+ // now re-add all of the items, with the new sort column
+ //
+ for (i=0; i<nCount ; i++ )
+ {
+ nCurSel = ListBox_AddString(lpColumnLB->hwndList, lpListboxContents[i]);
+ }
+
+ // reselect selected item...
+ for (i=0; i < nCount ; i++)
+ {
+ if (ItemData == (DWORD)ListBox_GetItemData(lpColumnLB->hwndList, i))
+ // then select it
+ ListBox_SetCurSel(lpColumnLB->hwndList, i);
+ }
+
+ GlobalFreePtr(lpListboxContents);
+
+ SetWindowRedraw(lpColumnLB->hwndList, TRUE);
+
+ InvalidateRect(lpColumnLB->hwndList, NULL, TRUE);
+
+ SetCursor(hCursor);
+ }
+ return lpColumnLB->SortColumn;
+}
+
+// ------------------------------------------------------------------
+// ColumnLBClass_OnColOrder()
+//
+// case CLB_GETCOLORDER : // get the virtual order of the physical columns (ret=LPDWORD order table)
+// case CLB_SETCOLORDER : // set the virtual order of the physical columns (ret=LPDWORD order table, wParamn=LPDWORD new order)
+//
+// HISTORY:
+// Tom Laird-McConnell 4/18/93 Created
+// ------------------------------------------------------------------
+LPBYTE ColumnLBClass_OnColOrder(HWND hwnd, LPBYTE NewColOrder, BOOL fSetOrder)
+{
+ LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0L);
+
+ //
+ // if we are modifying it
+ //
+ if (fSetOrder)
+ {
+ //
+ // copy the new order over the old order
+ //
+ memcpy(lpColumnLB->ColumnOrderTable, NewColOrder, lpColumnLB->nColumns);
+
+ ColumnLBClass_ComputeOffsets(hwnd);
+
+ //
+ // cause listbox to be redrawn
+ //
+ InvalidateRect(lpColumnLB->hwndTitleList, NULL, TRUE);
+ InvalidateRect(lpColumnLB->hwndList, NULL, TRUE);
+ }
+
+ return lpColumnLB->ColumnOrderTable;
+}
+
+
+// ------------------------------------------------------------------
+// ColumnLBClass_OnColOffsets()
+//
+// case CLB_GETCOLOFFSETS : // gets the incremental col offsets (ret=LPDWORD)
+// case CLB_SETCOLOFFSETS : // sets the incremental col offsets (wParam = LPDWORD)
+//
+// HISTORY:
+// Tom Laird-McConnell 4/18/93 Created
+// ------------------------------------------------------------------
+LPINT ColumnLBClass_OnColOffsets(HWND hwnd, LPINT NewOffsetTable, BOOL fSetOffsets)
+{
+ LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0L);
+
+ //
+ // if we are modifying it
+ //
+// if (fSetOffsets)
+// {
+// for (i=0; i < lpColumnLB->nColumns ; i++ )
+// {
+// lpColumnLB->ColumnOffsetTable[i] = NewOffsetTable[i];
+// }
+// }
+ return (lpColumnLB->ColumnOffsetTable);
+}
+
+// ------------------------------------------------------------------
+// ColumnLBClass_OnAutoWidths()
+//
+//
+// Handles CLB_AUTOWIDTHS messages to calculate the width of each field, and
+// to calculate the offsets automatically... (if column is -1 , then all columns)
+// ColumnToCompute is in Physical Columns
+//
+// returns: The horiztonal extent of all of the columns...
+//
+// HISTORY:
+// Tom Laird-McConnell 5/1/93 Created
+// ------------------------------------------------------------------
+LRESULT ColumnLBClass_OnAutoWidth(HWND hwnd, BYTE ColumnToCompute)
+{
+ HDC hdc;
+ BYTE nColumn;
+ LONG cxExtent;
+ SIZE Size;
+ TEXTMETRIC tm;
+ LPCOLUMNINFO lpColumnInfo, lpPrevColumnInfo;
+
+ LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0);
+ HFONT hOldFont;
+ DWORD OldStyle, NewStyle;
+
+ hdc = GetDC(hwnd);
+ GetTextMetrics(hdc, &tm);
+ hOldFont = SelectFont(hdc, lpColumnLB->hFont);
+ lpPrevColumnInfo = NULL;
+
+ //
+ // based on column order, compute the widths and offsets of each column
+ // NOTE: nColumn is the physical column
+ //
+ lpColumnInfo = lpColumnLB->ColumnInfoTable;
+ cxExtent = 0;
+ for (nColumn=0; nColumn < lpColumnLB->nColumns; nColumn++, lpColumnInfo++)
+ {
+ // bail out if column title is not there...
+ if ((lpColumnInfo->lpTitle == NULL) ||
+ (lpColumnInfo->lpTitle[0] == '\0'))
+ continue; // try next column
+
+ //
+ // only if it is a column we are supposed to change
+ //
+ if ((ColumnToCompute == (BYTE)-1) ||
+ (nColumn == ColumnToCompute))
+ {
+ GetTextExtentPoint( hdc,
+ (LPTSTR)lpColumnInfo->lpTitle,
+ lstrlen(lpColumnInfo->lpTitle),
+ &Size);
+
+ //
+ // the width is the text extent of the string plus some whitespace
+ //
+ lpColumnInfo->Width = (WHITESPACE/2) + Size.cx;
+ }
+ }
+
+ SelectFont(hdc, hOldFont);
+ ReleaseDC(hwnd, hdc);
+
+ //
+ // now adjust the offsets to show new values
+ //
+ cxExtent = ColumnLBClass_ComputeOffsets(hwnd);
+
+ if (lpColumnLB->fUseVlist)
+ OldStyle = SendMessage(lpColumnLB->hwndList, VLB_GETLISTBOXSTYLE, 0L, 0L);
+ else
+ OldStyle = GetWindowLong(lpColumnLB->hwndList, GWL_STYLE);
+
+ //
+ // send the message to the title listbox as well
+ //
+ SendMessage(lpColumnLB->hwndTitleList, LB_SETHORIZONTALEXTENT, cxExtent, 0L);
+
+ SendMessage(lpColumnLB->hwndList,
+ (lpColumnLB->fUseVlist) ? VLB_SETHORIZONTALEXTENT : LB_SETHORIZONTALEXTENT, cxExtent, 0L);
+
+ if (lpColumnLB->fUseVlist)
+ NewStyle = SendMessage(lpColumnLB->hwndList, VLB_GETLISTBOXSTYLE, 0L, 0L);
+ else
+ NewStyle = GetWindowLong(lpColumnLB->hwndList, GWL_STYLE);
+
+ //
+ // if the horizontal scroll bar is gone, then reset hscroll position
+ //
+ if ((NewStyle & WS_HSCROLL) !=
+ (OldStyle & WS_HSCROLL))
+ {
+ // move position to far left
+ SendMessage(lpColumnLB->hwndList,
+ (lpColumnLB->fUseVlist) ? VLB_HSCROLL : WM_HSCROLL,
+ MAKEWPARAM(SB_TOP, 0), 0);
+ }
+
+ InvalidateRect(lpColumnLB->hwndList, NULL, TRUE);
+ InvalidateRect(lpColumnLB->hwndTitleList, NULL, TRUE);
+
+ return(cxExtent);
+}
+
+
+// ------------------------------------------------------------------
+// ColumnLBClass_ComputeOffsets()
+//
+// returns text extent...
+//
+// HISTORY:
+// Tom Laird-McConnell 5/3/93 Created
+// ------------------------------------------------------------------
+int ColumnLBClass_ComputeOffsets(HWND hwnd)
+{
+ BYTE i;
+ LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0L);
+ LPINT lpOffset;
+ LPBYTE lpOrder;
+ LPCOLUMNINFO lpColumnInfo;
+ BYTE PhysColumn;
+ int ncxBorder = GetSystemMetrics(SM_CXBORDER);
+
+ //
+ // recalc the offsets table using the current virtual order
+ //
+ lpOffset = lpColumnLB->ColumnOffsetTable;
+ lpOrder = lpColumnLB->ColumnOrderTable;
+ lpColumnInfo = lpColumnLB->ColumnInfoTable;
+ //
+ // first offset is always 0
+ //
+ lpOffset[0] = 0;
+ for (i=1; i < lpColumnLB->nColumns + 1 ; i++ )
+ {
+ PhysColumn = lpOrder[i-1];
+
+ //
+ // this offset is the previous offset plus the previous width
+ //
+ lpOffset[i] = lpOffset[i-1] + lpColumnInfo[PhysColumn].Width + 2 * ncxBorder;
+ }
+ //
+ // last offset is also new text extent...
+ return(lpOffset[lpColumnLB->nColumns]);
+}
+
+// ------------------------------------------------------------------
+// ColumnLBClass_OnLButtonDown()
+//
+// Handles WM_LBUTTONDOWN and WM_LBUTTONDBLCLK messages from the client
+// area above the listbox
+//
+//
+// HISTORY:
+// Tom Laird-McConnell 5/3/93 Created
+// ------------------------------------------------------------------
+void ColumnLBClass_OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags)
+{
+ LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0L);
+ BYTE i;
+ int AdjustedX = x - lpColumnLB->xPos;
+ HCURSOR hCursor;
+ BYTE PhysColumn;
+ BYTE VirtColumn;
+ RECT rect;
+ POINT point;
+
+ point.x = x;
+ point.y = y;
+ GetClientRect(lpColumnLB->hwndTitleList, &rect);
+
+ // only if this is a right mouse button from
+ if (PtInRect(&rect, point))
+ {
+ //
+ // if this is a down-click, and it is on a column border, then go into resize mode
+ //
+ for(i=1; i < lpColumnLB->nColumns+1; i++)
+ {
+ //
+ // check to see if this is a column offset
+ //
+ if ((AdjustedX > lpColumnLB->ColumnOffsetTable[i]-4) &&
+ (AdjustedX < lpColumnLB->ColumnOffsetTable[i]+4))
+ {
+ VirtColumn = i-1;
+ PhysColumn = lpColumnLB->ColumnOrderTable[VirtColumn];
+
+ //
+ // x is the right-side of the column i-1
+ //
+ lpColumnLB->fMouseState = MOUSE_COLUMNRESIZE;
+ lpColumnLB->xPrevPos = 0;
+ lpColumnLB->ColClickStart = VirtColumn; // virtual column
+ SetCapture(hwnd);
+
+ hCursor = LoadCursor(lpColumnLB->hInstance, TEXT("SizebarHCursor"));
+ SetCursor(hCursor);
+ return;
+ }
+#ifdef DRAG
+ else
+ //
+ // if this is a down-click, and it is on a column title,
+ //
+ if ((AdjustedX > lpColumnLB->ColumnOffsetTable[i-1]) &&
+ (AdjustedX < lpColumnLB->ColumnOffsetTable[i]))
+ {
+ //
+ // whether it is a double-or single click, we need to draw down button state
+ //
+ VirtColumn = i-1;
+ PhysColumn = lpColumnLB->ColumnOrderTable[VirtColumn];
+
+ lpColumnLB->ColumnInfoTable[PhysColumn].fDepressed = TRUE;
+
+ GetClientRect(lpColumnLB->hwndTitleList, &rect);
+ rect.left = lpColumnLB->ColumnOffsetTable[VirtColumn] + lpColumnLB->xPos;
+ rect.right = lpColumnLB->ColumnOffsetTable[VirtColumn+1] + lpColumnLB->xPos;
+
+ //
+ // if this is a double-click, AND we are in sort mode then handle this as a sort request on the
+ // column double-clicked on
+ //
+ if (fDoubleClick)
+ {
+ if (GetWindowLong(hwnd, GWL_STYLE) & LBS_SORT)
+ {
+ //
+ // then default to doing a sort
+ //
+ SendMessage(hwnd, CLB_SETSORTCOL, (WPARAM)PhysColumn, (LPARAM)0);
+ }
+ else
+ {
+ //
+ // tell parent that the user double-clicked on PhysColumn
+ //
+ SendMessage(GetParent(hwnd), CLBN_TITLEDBLCLK, (WPARAM)GetDlgCtrlID(hwnd), (LPARAM) PhysColumn);
+ }
+ //
+ // we are done with double-click, so redraw window
+ //
+ lpColumnLB->ColumnInfoTable[PhysColumn].fDepressed = FALSE;
+
+ InvalidateRect(lpColumnLB->hwndTitleList, &rect, FALSE);
+
+ return;
+ }
+ else
+ {
+ // then go into single click mode/or column drag mode
+
+ //
+ // then x, y is in column i-1
+ //
+ lpColumnLB->fMouseState = MOUSE_COLUMNCLICK;
+ lpColumnLB->ColClickStart = VirtColumn;
+
+ CopyRect(&lpColumnLB->ColClickRect, &rect);
+
+ // lpColumnLB->ColClickRect.left += (lpColumnLB->ColClickRect.right - lpColumnLB->ColClickRect.left)/3;
+ // lpColumnLB->ColClickRect.right -= (lpColumnLB->ColClickRect.right - lpColumnLB->ColClickRect.left)/3;
+
+ SetCapture(hwnd);
+ InvalidateRect(lpColumnLB->hwndTitleList, &rect, FALSE);
+
+ GetWindowRect(lpColumnLB->hwndTitleList, &rect);
+ ClipCursor(&rect);
+ return;
+ }
+ }
+#endif
+ }
+ }
+}
+
+// ------------------------------------------------------------------
+// ColumnLBClass_OnMouseMove()
+//
+// Handles Mouse movement messages from the client
+// area above the listbox
+//
+//
+// HISTORY:
+// Tom Laird-McConnell 5/3/93 Created
+// ------------------------------------------------------------------
+void ColumnLBClass_OnMouseMove(HWND hwnd, int x, int y, UINT keyFlags)
+{
+ LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0L);
+ RECT rect;
+ HDC hdc;
+ BYTE i;
+ int AdjustedX = x - lpColumnLB->xPos;
+ POINT Point;
+ HCURSOR hCursor;
+
+ switch (lpColumnLB->fMouseState)
+ {
+ case 0 : // not in mouse mode at all, so just track changing cursor when over column border
+ for(i=1; i < lpColumnLB->nColumns + 1; i++)
+ {
+ //
+ // check to see if this is a column offset
+ //
+ if ((AdjustedX > lpColumnLB->ColumnOffsetTable[i]-4) &&
+ (AdjustedX < lpColumnLB->ColumnOffsetTable[i]+4))
+ {
+ //
+ // it is, so set the cursor and return
+ //
+ hCursor = LoadCursor(lpColumnLB->hInstance, TEXT("SizebarHCursor"));
+ SetCursor(hCursor);
+ return;
+ }
+ }
+ SetCursor(LoadCursor(0,IDC_ARROW));
+ break;
+
+ case MOUSE_COLUMNRESIZE:
+ GetClientRect(hwnd, &rect);
+
+ //
+ // as long as we haven't moved past the previous column, and we haven't moved out of the rect
+ //
+ if (AdjustedX < lpColumnLB->ColumnOffsetTable[lpColumnLB->ColClickStart]+8)
+ {
+ x += (lpColumnLB->ColumnOffsetTable[lpColumnLB->ColClickStart]+8)-AdjustedX;
+ AdjustedX = lpColumnLB->ColumnOffsetTable[lpColumnLB->ColClickStart]+8;
+ }
+
+ if (x < rect.right)
+ {
+ hdc = GetDC(hwnd);
+
+ // un invert previous postion
+ if (lpColumnLB->xPrevPos)
+ {
+ rect.left = lpColumnLB->xPrevPos;
+ rect.right = rect.left+1;
+ InvertRect(hdc, &rect);
+ }
+
+ lpColumnLB->xPrevPos = x;
+
+ // invert new position
+ rect.left = x;
+ rect.right = rect.left+1;
+ InvertRect(hdc, &rect);
+
+ ReleaseDC(hwnd, hdc);
+ }
+ break;
+
+ case MOUSE_COLUMNDRAG:
+ //
+ // if this is a column drag, we track the messages until the mouse has moved
+ // back INTO the original column rectangle, if it does this, then we switchback to
+ // COLUMNCLICK mode, until they let go, or move back out
+ //
+ Point.x = x;
+ Point.y = y;
+
+ GetClientRect(lpColumnLB->hwndTitleList, &rect);
+
+ // if it is on far RIGHT generate WM_HSCROLL right message
+ if (x >= rect.right-2)
+ {
+ SendMessage(lpColumnLB->hwndList, (lpColumnLB->fUseVlist) ? VLB_HSCROLL : WM_HSCROLL, MAKEWPARAM(SB_LINEDOWN, 0), (LPARAM)NULL);
+ return;
+ }
+
+ // if it is on far RIGHT generate WM_HSCROLL left message
+ if (x <= rect.left+2)
+ {
+ SendMessage(lpColumnLB->hwndList, (lpColumnLB->fUseVlist) ? VLB_HSCROLL : WM_HSCROLL, MAKEWPARAM(SB_LINEUP, 0), (LPARAM)NULL);
+ return;
+ }
+
+// rect.right -= lpColumnLB->xPos;
+//
+// //
+// // it if is out of the title area, or if it is in the original column
+// //
+// if ((PtInRect(&lpColumnLB->ColClickRect, Point) == TRUE) || // original column
+// (PtInRect(&rect, Point) == FALSE) ) // title area
+// {
+// //
+// // then it has moved back into the original column, switch to
+// //COLUMNCLICK mode
+// //
+// lpColumnLB->fMouseState = MOUSE_COLUMNCLICK;
+//
+// SetCursor(LoadCursor(0, IDC_ARROW));
+// return;
+// }
+ break;
+
+ case MOUSE_COLUMNCLICK:
+ //
+ // if this is a column click, we track the messages until the mouse has moved
+ // outside of the original column rectangle, if it does this, then we switch to
+ // COLUMNDRAG mode, until they let go, or until they move back to the original
+ // column.
+ //
+ Point.x = x;
+ Point.y = y;
+
+ GetClientRect(lpColumnLB->hwndTitleList, &rect);
+ rect.right -= lpColumnLB->xPos;
+
+ //
+ // if it is outside of the original column, and inside title area, then swtich to
+ // DRAG mode
+ //
+ if ((PtInRect(&lpColumnLB->ColClickRect, Point) == FALSE) && //
+ (PtInRect(&rect, Point) == TRUE) ) // title area
+ {
+
+ //
+ // then it has moved outside of the column, switch to
+ //COLUMNDRAG mode
+ //
+ lpColumnLB->fMouseState = MOUSE_COLUMNDRAG;
+
+ hCursor = LoadCursor(lpColumnLB->hInstance, TEXT("ColDragCursor"));
+ SetCursor(hCursor);
+ }
+ break;
+ }
+}
+
+
+// ------------------------------------------------------------------
+// ColumnLBClass_OnLButtonUp()
+//
+// Handles WM_LBUTTONUp messages from the client
+// area above the listbox
+//
+//
+// HISTORY:
+// Tom Laird-McConnell 5/3/93 Created
+// ------------------------------------------------------------------
+void ColumnLBClass_OnLButtonUp(HWND hwnd, int x, int y, UINT keyFlags)
+{
+ LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0L);
+
+ BYTE PhysColumn = lpColumnLB->ColumnOrderTable[lpColumnLB->ColClickStart];
+ BYTE PhysSourceColumn;
+
+ int AdjustedX = x - lpColumnLB->xPos;
+
+ POINT Point;
+
+ BYTE NewOrderTable[MAX_COLUMNS];
+
+ LPBYTE lpNewOrderTable = NewOrderTable;
+ LPBYTE lpOrderTable = lpColumnLB->ColumnOrderTable;
+
+
+ BYTE CurrentCol;
+ BYTE DestCol;
+ BYTE SourceCol;
+ TCHAR Direction;
+
+ BYTE i;
+ HDC hdc;
+ RECT rect;
+
+
+ SetCursor(LoadCursor(0, IDC_ARROW)); // go back to arrow
+
+ switch (lpColumnLB->fMouseState)
+ {
+ case MOUSE_COLUMNRESIZE:
+ //
+ // if we were in resize column mode, then resize the column to the left of the border
+ //
+ ReleaseCapture();
+ ClipCursor(NULL);
+
+ lpColumnLB->fMouseState = 0;
+
+ // clean up line
+ GetClientRect(hwnd, &rect);
+ hdc = GetDC(hwnd);
+
+ // massage the value to make sure it's in the right range...
+ if (AdjustedX < lpColumnLB->ColumnOffsetTable[lpColumnLB->ColClickStart]+8)
+ AdjustedX = lpColumnLB->ColumnOffsetTable[lpColumnLB->ColClickStart]+8;
+
+ // un invert previous postion
+ if (lpColumnLB->xPrevPos)
+ {
+ rect.left = lpColumnLB->xPrevPos;
+ rect.right = rect.left+1;
+ InvertRect(hdc, &rect);
+ }
+
+ ReleaseDC(hwnd, hdc);
+
+ //
+ // set the physical column width to be the current x position - the current virtual column offset
+ //
+ SendMessage(hwnd,
+ CLB_SETCOLWIDTH,
+ (WPARAM)PhysColumn,
+ (LPARAM)AdjustedX - lpColumnLB->ColumnOffsetTable[lpColumnLB->ColClickStart]);
+ break;
+
+ case MOUSE_COLUMNDRAG:
+
+ lpColumnLB->fMouseState = 0;
+
+ ReleaseCapture();
+ ClipCursor(NULL);
+
+ lpColumnLB->ColumnInfoTable[PhysColumn].fDepressed = FALSE;
+
+ //
+ // we need to figure out what column we ended up on
+ //
+ for(i=1; i < lpColumnLB->nColumns+1; i++)
+ {
+ //
+ // if it fits in this columns area, then this is the destination column
+ //
+ if ((AdjustedX > lpColumnLB->ColumnOffsetTable[i-1]) &&
+ (AdjustedX < lpColumnLB->ColumnOffsetTable[i]))
+ {
+ //
+ // make duplicate of the table
+ //
+ memcpy(NewOrderTable, lpOrderTable, sizeof(BYTE)*lpColumnLB->nColumns);
+
+ //
+ // i-1 is the destination column! (virtual)
+ //
+ SourceCol = lpColumnLB->ColClickStart; // virtual
+ DestCol = i-1; // virtual
+ PhysSourceColumn = lpColumnLB->ColumnOrderTable[SourceCol]; // physical
+
+ Direction = (SourceCol > DestCol) ? -1 : 1;
+
+ CurrentCol = SourceCol;
+ while (CurrentCol != DestCol)
+ {
+ NewOrderTable[CurrentCol] = NewOrderTable[CurrentCol + Direction];
+ CurrentCol += Direction;
+ }
+
+ //
+ // ok, it's equal to destination, so let's put the source Physical value into the destination
+ //
+ NewOrderTable[CurrentCol] = PhysSourceColumn;
+
+ //
+ // ok, so now set the order to the new order
+ //
+ SendMessage(hwnd, CLB_SETCOLORDER, (WPARAM)0, (LPARAM)NewOrderTable);
+ }
+ }
+
+ GetClientRect(lpColumnLB->hwndTitleList, &rect);
+ rect.left = lpColumnLB->ColumnOffsetTable[lpColumnLB->ColClickStart] + lpColumnLB->xPos;
+ rect.right = lpColumnLB->ColumnOffsetTable[lpColumnLB->ColClickStart+1] + lpColumnLB->xPos;
+ InvalidateRect(lpColumnLB->hwndTitleList, &rect, FALSE);
+
+ break;
+
+ case MOUSE_COLUMNCLICK:
+ //
+ // if this is a column click, we track the messages until the mouse has moved
+ //
+ lpColumnLB->fMouseState = 0;
+
+ ReleaseCapture();
+ ClipCursor(NULL);
+
+ lpColumnLB->ColumnInfoTable[PhysColumn].fDepressed = FALSE;
+
+ GetClientRect(lpColumnLB->hwndTitleList, &rect);
+ rect.left = lpColumnLB->ColumnOffsetTable[lpColumnLB->ColClickStart] + lpColumnLB->xPos;
+ rect.right = lpColumnLB->ColumnOffsetTable[lpColumnLB->ColClickStart+1] + lpColumnLB->xPos;
+ InvalidateRect(lpColumnLB->hwndTitleList, &rect, FALSE);
+
+ //
+ // now send a CLBN_SINGLECLICK message to the parent, only if the mousebutton was let up in the original
+ // column
+ //
+ Point.x = AdjustedX;
+ Point.y = y;
+ if (PtInRect(&lpColumnLB->ColClickRect, Point) == TRUE)
+ SendMessage(GetParent(hwnd), CLBN_TITLESINGLECLK, (WPARAM)GetDlgCtrlID(hwnd), (LPARAM)PhysColumn);
+ return;
+ }
+}
+
+// ------------------------------------------------------------------
+// ColumnLBClass_OnRButtonDown()
+//
+// Handles WM_RBUTTON_DOWN messages
+// alg:
+//
+// figure out where we are
+// determine listbox item
+// find height of rows
+// translate mouse Y into a row number
+// are we VLIST?
+// Yes, Get TopIndex
+// are we Owner Draw?
+// Yes
+// Send message to VLIST to get the data for row number
+// No
+// item number + TopIndex is the info the parent needs.
+// No
+// item number is the info the parent needs
+// determine column
+// calc which column taking into account scrolling
+// send message to parent with info
+// The parent will receive the column in wParam and the item in lParam. lParam
+// needs to be the item because it might be the owner draw data.
+//
+//
+//
+// HISTORY:
+// Steve Hiskey 10/19/93 Created
+// ------------------------------------------------------------------
+void ColumnLBClass_OnRButtonDown (HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags)
+{
+ LPCOLUMNLBSTRUCT lpColumnLB = (LPCOLUMNLBSTRUCT)GetWindowLong(hwnd, 0L);
+ // get the item height here and not when the OnFont message is
+ // processed. ?? we get different/wrong results otherwise.
+ int ItemHeight = (int)ListBox_GetItemHeight(hwnd,1);
+ int Item = y / ItemHeight;
+ BYTE i;
+ int AdjustedX = x - lpColumnLB->xPos;
+ BYTE VirtColumn;
+ DWORD TopIndex;
+ CLBRBUTTONSTRUCT RButtonStruct;
+ BOOL GotOne = FALSE;
+ BOOL fTemp;
+
+
+ RButtonStruct.x = x;
+ RButtonStruct.y = y + lpColumnLB->yTitle;
+ RButtonStruct.hwndChild = hwnd;
+
+ //
+ // we already have the item (non VList), figure out which column
+ //
+ for(i=1; i < lpColumnLB->nColumns+1; i++)
+ {
+ if ((AdjustedX > lpColumnLB->ColumnOffsetTable[i-1]) &&
+ (AdjustedX < lpColumnLB->ColumnOffsetTable[i]))
+ {
+ //
+ // we have our column. Get the Physical Column. The parent of this column
+ // list box know what columns are interesting... and how the physical columns
+ // map to the virtual columns.
+ //
+ VirtColumn = i-1;
+ RButtonStruct.PhysColumn = lpColumnLB->ColumnOrderTable[VirtColumn];
+ GotOne = TRUE;
+ break;
+ }
+ }
+ if ( !GotOne)
+ return;
+
+ // are we VLIST?
+
+ if ( lpColumnLB->fUseVlist )
+ {
+ DWORD Style;
+
+ // are we owner draw? If so, then we don't care about TopIndex, we just want
+ // the instance data
+ Style = (DWORD)SendMessage(lpColumnLB->hwndList, VLB_GETVLISTSTYLE, 0, 0L);
+ if ( Style && VLBS_USEDATAVALUES )
+ {
+ // we are use data values. This means that we must ask the VList for the
+ // data and this is the data is the identifier of the row. ie, the data the
+ // VList stores is the structure needed to identify and display the line.
+
+ RButtonStruct.Index = ListBox_GetItemData(lpColumnLB->hwndList, Item );
+
+ }
+ else
+ {
+ // we are a normal vlist box. Get the top index and add our offset
+ // from top of listbox to it.
+
+ TopIndex = (DWORD)SendMessage(lpColumnLB->hwndList, LB_GETTOPINDEX, 0, 0L);
+ RButtonStruct.Index = TopIndex + Item;
+ }
+
+ }
+ else
+ {
+ // we are a normal list box. We need to know what item we are looking at.
+ // ask the listbox for the top index.
+ TopIndex = (DWORD)SendMessage(lpColumnLB->hwndList, LB_GETTOPINDEX, 0, 0L);
+ RButtonStruct.Index = TopIndex + Item;
+ }
+
+ // if they have hit rButton, we should set the focus to this item (lButton)... since
+ // WE are providing the CLB_SETCURSEL, we must tell the parent... some weird rule about
+ // if the user does a set cur sel, then the parent is notified, but if the parent does
+ // the set cur sel, the parent is not notified... since we are neither the parent or the
+ // user, we have to do both.
+
+
+ // if VLIST, we need to send just the item, top TopIndex + Item...
+
+ if ( lpColumnLB->fUseVlist )
+ fTemp = ListBox_SetCurSel(lpColumnLB->hwndList, Item);
+ else
+ fTemp = ListBox_SetCurSel(lpColumnLB->hwndList, RButtonStruct.Index);
+
+ if ( fTemp )
+ SendMessage(GetParent(hwnd), WM_COMMAND,
+ GetDlgCtrlID( lpColumnLB->hwndList),
+ MAKELPARAM(lpColumnLB->hwndList, LBN_SELCHANGE));
+
+ // we are ready to send which column and which row to the parent.
+
+ SendMessage ( GetParent (hwnd), CLBN_RBUTTONDOWN, (WORD)0, (LONG) &RButtonStruct );
+
+}
+
+
+
+// ------------------------------------------------------------------
+// ColumnLBClass_DrawColumnBorder()
+//
+//
+// HISTORY:
+// Tom Laird-McConnell 3/15/94 created
+// ------------------------------------------------------------------
+void ColumnLB_DrawColumnBorder(HDC hDC, RECT *lpRect, int Bottom, HBRUSH hBackgroundBrush)
+{
+ int ncxBorder = GetSystemMetrics(SM_CXBORDER);
+ RECT rect;
+
+ CopyRect(&rect, lpRect);
+
+ // fill in left side of rect
+ rect.right = rect.left;
+ rect.left -= (WHITESPACE/4);
+ FillRect(hDC, &rect, hBackgroundBrush);
+
+ // fill in right side of rect
+ rect.left = lpRect->right;
+ rect.right = lpRect->right + ncxBorder;
+ FillRect(hDC, &rect, hBackgroundBrush);
+
+ // draw the line itself
+ MoveToEx( hDC, rect.right, rect.top, NULL);
+ LineTo( hDC, rect.right, Bottom);
+}
+
diff --git a/private/nw/convert/nwconv/columnlb.h b/private/nw/convert/nwconv/columnlb.h
new file mode 100644
index 000000000..00d024ff4
--- /dev/null
+++ b/private/nw/convert/nwconv/columnlb.h
@@ -0,0 +1,329 @@
+// ==================================================================
+// Copyright 1990-1993 Microsoft corporation
+// all rights reservered
+// ==================================================================
+//
+// MODULE: COLUMNLB.H
+// PURPOSE: Definitions of all external procedure prototypes for custom
+// window class ColumnLB
+//
+// ------ TABSTOP = 4 -------------------
+//
+// HISTORY
+// -------
+// Tom Laird-McConnell 5/1/93 Created
+// ==================================================================
+
+#ifndef _COLUMNLB_
+#define _COLUMNLB_
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+//
+// CONTROL STYLES
+//
+#define CLBS_NOTIFYLMOUSE 0x0200L // pass on WM_LMOUSE messages to perent
+#define CLBS_NOTIFYRMOUSE 0x0800L // pass on WM_RMOUSE messages to parent
+
+//
+// CONTROL MESSAGES
+//
+
+#define CLB_BASE (WM_USER+4000)
+
+#define CLB_MSGMIN (CLB_BASE)
+
+#define CLB_GETNUMBERCOLS (CLB_BASE+0) // get the number of columns (ret=NumCols)
+#define CLB_SETNUMBERCOLS (CLB_BASE+1) // set the number of columns (wparam=NumCols)
+
+#define CLB_GETCOLWIDTH (CLB_BASE+2) // get a column width (wParm=Column ret=ColWidth in DU's)
+#define CLB_SETCOLWIDTH (CLB_BASE+3) // set a column width (wParm=Column lParam=Width)
+
+#define CLB_GETCOLTITLE (CLB_BASE+4) // get a column's title (wParm=Column, ret=Title)
+#define CLB_SETCOLTITLE (CLB_BASE+5) // set a column's title (wParm=Col, lParm=Title)
+
+#define CLB_GETSORTCOL (CLB_BASE+6) // get the sort column (ret=Col)
+#define CLB_SETSORTCOL (CLB_BASE+7) // set the sort column (wParm=Col)
+
+#define CLB_AUTOWIDTH (CLB_BASE+8) // auto-matically set column widths using titles...
+
+#define CLB_GETCOLOFFSETS (CLB_BASE+9) // gets the incremental col offsets (ret=LPINT array)
+#define CLB_SETCOLOFFSETS (CLB_BASE+10)
+
+#define CLB_GETCOLORDER (CLB_BASE+11) // get the order that columns should be displayed(ret=LPBYTE table)
+#define CLB_SETCOLORDER (CLB_BASE+12) // set the order that columns should be displayed(LParm=LPBYTE TABLE)
+
+#define CLB_HSCROLL (CLB_BASE+13) // a hscroll event (INTERNAL)
+
+#define CLB_GETFOCUS (CLB_BASE+14) // get the primary key focus window of CLB
+
+#define CLB_GETROWCOLTEXT (CLB_BASE+15) // given a row AND a column, give me the text for the physical column.
+
+#define CLB_GETTEXTPTRS (CLB_BASE+16) // just like gettext, but it give the array of pointers to the strings.
+
+#define CLB_CHECKFOCUS (CLB_BASE+17) // Does this listbox have the focus?
+
+/*
+ * Listbox messages (Defined as CLB_BASE+0+LB_ADDSTRING...to get original LB_ message, just take msg-CLB_BASE to get LB_
+ */
+
+#define CLB_LISTBOX_MSGMIN (CLB_MSGMIN+LB_ADDSTRING )
+
+#define CLB_ADDSTRING (CLB_MSGMIN+LB_ADDSTRING )
+#define CLB_INSERTSTRING (CLB_MSGMIN+LB_INSERTSTRING )
+#define CLB_DELETESTRING (CLB_MSGMIN+LB_DELETESTRING )
+#define CLB_SELITEMRANGEEX (CLB_MSGMIN+LB_SELITEMRANGEEX )
+#define CLB_RESETCONTENT (CLB_MSGMIN+LB_RESETCONTENT )
+#define CLB_SETSEL (CLB_MSGMIN+LB_SETSEL )
+#define CLB_SETCURSEL (CLB_MSGMIN+LB_SETCURSEL )
+#define CLB_GETSEL (CLB_MSGMIN+LB_GETSEL )
+#define CLB_GETCURSEL (CLB_MSGMIN+LB_GETCURSEL )
+#define CLB_GETTEXT (CLB_MSGMIN+LB_GETTEXT )
+#define CLB_GETTEXTLEN (CLB_MSGMIN+LB_GETTEXTLEN )
+#define CLB_GETCOUNT (CLB_MSGMIN+LB_GETCOUNT )
+#define CLB_SELECTSTRING (CLB_MSGMIN+LB_SELECTSTRING )
+#define CLB_DIR (CLB_MSGMIN+LB_DIR )
+#define CLB_GETTOPINDEX (CLB_MSGMIN+LB_GETTOPINDEX )
+#define CLB_FINDSTRING (CLB_MSGMIN+LB_FINDSTRING )
+#define CLB_GETSELCOUNT (CLB_MSGMIN+LB_GETSELCOUNT )
+#define CLB_GETSELITEMS (CLB_MSGMIN+LB_GETSELITEMS )
+#define CLB_SETTABSTOPS (CLB_MSGMIN+LB_SETTABSTOPS )
+#define CLB_GETHORIZONTALEXTENT (CLB_MSGMIN+LB_GETHORIZONTALEXTENT)
+#define CLB_SETHORIZONTALEXTENT (CLB_MSGMIN+LB_SETHORIZONTALEXTENT)
+#define CLB_SETCOLUMNWIDTH (CLB_MSGMIN+LB_SETCOLUMNWIDTH )
+#define CLB_ADDFILE (CLB_MSGMIN+LB_ADDFILE )
+#define CLB_SETTOPINDEX (CLB_MSGMIN+LB_SETTOPINDEX )
+#define CLB_GETITEMRECT (CLB_MSGMIN+LB_GETITEMRECT )
+#define CLB_GETITEMDATA (CLB_MSGMIN+LB_GETITEMDATA )
+#define CLB_SETITEMDATA (CLB_MSGMIN+LB_SETITEMDATA )
+#define CLB_SELITEMRANGE (CLB_MSGMIN+LB_SELITEMRANGE )
+#define CLB_SETANCHORINDEX (CLB_MSGMIN+LB_SETANCHORINDEX )
+#define CLB_GETANCHORINDEX (CLB_MSGMIN+LB_GETANCHORINDEX )
+#define CLB_SETCARETINDEX (CLB_MSGMIN+LB_SETCARETINDEX )
+#define CLB_GETCARETINDEX (CLB_MSGMIN+LB_GETCARETINDEX )
+#define CLB_SETITEMHEIGHT (CLB_MSGMIN+LB_SETITEMHEIGHT )
+#define CLB_GETITEMHEIGHT (CLB_MSGMIN+LB_GETITEMHEIGHT )
+#define CLB_FINDSTRINGEXACT (CLB_MSGMIN+LB_FINDSTRINGEXACT )
+#define CLB_SETLOCALE (CLB_MSGMIN+LB_SETLOCALE )
+#define CLB_GETLOCALE (CLB_MSGMIN+LB_GETLOCALE )
+#define CLB_SETCOUNT (CLB_MSGMIN+LB_SETCOUNT )
+
+#define CLB_LISTBOX_MSGMAX CLB_SETCOUNT
+
+#define CLB_MSGMAX CLB_LISTBOX_MSGMAX
+
+
+//
+// NOTIFICATION MESSAGES
+//
+#define CLBN_MSGMIN (CLB_MSGMAX + 1)
+
+#define CLBN_DRAWITEM CLBN_MSGMIN // ask the parent to do a XXXXitem lParam = LPDRAWITEMSTRUCT)
+//#define CLBN_COMPAREITEM CLBN_MSGMIN+2) // ask the parent to do a XXXXitem (wParam=PhysCol, lParam = LPCOMPAREITEMSTRUCT)
+#define CLBN_CHARTOITEM (CLBN_MSGMIN+3) // ask the parent to do a XXXXitem (wParam=PhysCol, )
+
+#define CLBN_TITLESINGLECLK (CLBN_MSGMIN+4) // notify the parent that a user clicked on a title (wParam = CTLID, lParam=Col)
+#define CLBN_TITLEDBLCLK (CLBN_MSGMIN+5) // notify the parent that a user double-clicked on a title (wParam = CTLID, lParam=Col)
+
+#define CLBN_COLREORDER (CLBN_MSGMIN+6) // notify the parent that someone changed the column order (LPARAM=LPINT order)
+#define CLBN_COLSIZE (CLBN_MSGMIN+7) // notify the parent that someone changed the column size (lParam=LPINT widths)
+#define CLBN_RBUTTONDOWN (CLBN_MSGMIN+8) // notify the parent on rbutton which row and column
+#define CLBN_RBUTTONUP (CLBN_MSGMIN+9) // notify the parent on rbutton which row and column
+
+#define CLBN_MSGMAX CLBN_RBUTTONUP
+
+
+#define MAX_COLUMNS 32
+
+
+//
+// structure used to keep track of column info
+//
+typedef struct _ColumnInfo
+{
+ int Width; // width in LU's of column
+ LPTSTR lpTitle; // pointer to title string
+ BOOL fDepressed; // flag for whether this columns header button is depressed or not
+} COLUMNINFO;
+typedef COLUMNINFO *LPCOLUMNINFO;
+
+
+//
+// structure used for doing a Column DrawItem
+//
+typedef struct _CLBDrawItemStruct
+{
+ DRAWITEMSTRUCT DrawItemStruct;
+ LPBYTE lpColOrder;
+ DWORD nColumns;
+ RECT rect[MAX_COLUMNS];
+} CLBDRAWITEMSTRUCT;
+typedef CLBDRAWITEMSTRUCT *LPCLBDRAWITEMSTRUCT;
+
+#define MOUSE_COLUMNDRAG 1
+#define MOUSE_COLUMNRESIZE 2
+#define MOUSE_COLUMNCLICK 3
+
+//
+// internal datastructure used to keep track of everything internal to the
+// ColumnLB class...
+//
+typedef struct _ColumnLBstruct
+{
+ DWORD Style; // style of columnlb
+
+ HWND hwndList; // handle to the internal listbox
+ HWND hwndTitleList; // handle to title listbox
+
+ HFONT hFont; // font in use...
+
+ HINSTANCE hInstance; // hInstance of the app which created this
+
+ BYTE nColumns; // number of columns in the column listbox
+ COLUMNINFO ColumnInfoTable[MAX_COLUMNS]; // table of ColumnInfoStructures
+ int ColumnOffsetTable[MAX_COLUMNS]; // table of offsets from front of listbox (in lu's)
+ BYTE ColumnOrderTable[MAX_COLUMNS]; // indexes of columns in order of display
+ BYTE SortColumn; // index of current sort column
+
+ int xPos; // current x position in scroll-box
+ int yTitle; // height of title portion...
+
+ FARPROC OldListboxProc; // old listbox proc
+ FARPROC NewListboxProc; // New listbox proc
+
+ FARPROC OldTitleListboxProc; // old listbox proc
+ FARPROC NewTitleListboxProc; // New listbox proc
+
+ BYTE fUseVlist:1; // flag for whether to use VLIST class or not...
+ BYTE fMouseState:3; // state for moving
+ BYTE fSorting:1; // flag to signifiy that we are sorting so ignore DELETEITEM's
+ BYTE fHasFocus:1; // does the listbox have the focus?
+
+ int xPrevPos ; // previous x mouse position
+ BYTE ColClickStart; // column of click start
+ RECT ColClickRect; // rect of click column
+} COLUMNLBSTRUCT;
+typedef COLUMNLBSTRUCT FAR *LPCOLUMNLBSTRUCT;
+
+#define COLUMNLBCLASS_CLASSNAME TEXT("ColumnListBox") // normal Column listbox
+#define COLUMNVLBCLASS_CLASSNAME TEXT("ColumnVListBox") // Vlist Column box
+
+//
+// structure used for RBUTTONDOWN messages. The Column list box tells the parent
+// which column and index.
+//
+typedef struct _CLBRButtonStruct
+{
+ HWND hwndChild;
+ BYTE PhysColumn;
+ DWORD Index;
+ int x;
+ int y;
+} CLBRBUTTONSTRUCT;
+
+typedef CLBRBUTTONSTRUCT *LPCLBRBUTTONSTRUCT;
+
+
+//
+// function prototypes
+//
+BOOL ColumnLBClass_Register(HINSTANCE hInstance);
+BOOL ColumnVLBClass_Register(HINSTANCE hInstance);
+BOOL ColumnLBClass_Unregister(HINSTANCE hInstance);
+BOOL ColumnVLBClass_Unregister(HINSTANCE hInstance);
+
+void ColumnLB_DrawColumnBorder(HDC hdc, RECT *rect, int Bottom, HBRUSH hBrush);
+
+// -----------------------------------------------------------------------------------
+//
+// ColumnListBox_ Macros (uses CLB_ messages) New definitions
+//
+#define ColumnLB_GetNumberCols(hwndCtl) ((int)(DWORD)SendMessage((hwndCtl), CLB_GETNUMBERCOLS, 0L, (LPARAM)0))
+#define ColumnLB_SetNumberCols(hwndCtl,Number) ((int)(DWORD)SendMessage((hwndCtl), CLB_SETNUMBERCOLS, (WPARAM)Number, (LPARAM)0))
+#define ColumnLB_GetColWidth(hwndCtl,Column) ((int)(DWORD)SendMessage((hwndCtl), CLB_GETCOLWIDTH, (WPARAM)Column, (LPARAM)0))
+#define ColumnLB_SetColWidth(hwndCtl,Column, Width) ((int)(DWORD)SendMessage((hwndCtl), CLB_SETCOLWIDTH, (WPARAM)Column, (LPARAM)Width))
+#define ColumnLB_GetColTitle(hwndCtl,Column) ((LPTSTR)(DWORD)SendMessage((hwndCtl), CLB_GETCOLTITLE, (WPARAM)Column, (LPARAM)0))
+#define ColumnLB_SetColTitle(hwndCtl,Column, Title) ((LPTSTR)(DWORD)SendMessage((hwndCtl), CLB_SETCOLTITLE, (WPARAM)Column, (LPARAM)Title))
+#define ColumnLB_GetSortCol(hwndCtl) ((DWORD)SendMessage((hwndCtl), CLB_GETSORTCOL, (WPARAM)0, (LPARAM)0))
+#define ColumnLB_SetSortCol(hwndCtl,Column) ((int)(DWORD)SendMessage((hwndCtl), CLB_SETSORTCOL, (WPARAM)Column, (LPARAM)0))
+#define ColumnLB_AutoWidth(hwndCtl, Width) ((int)(DWORD)SendMessage((hwndCtl), CLB_AUTOWIDTH, (WPARAM)Width, (LPARAM)0))
+#define ColumnLB_GetColOffsets(hwndCtl) ((LPINT)(DWORD)SendMessage((hwndCtl), CLB_GETCOLOFFSETS, (WPARAM)0, (LPARAM)0))
+#define ColumnLB_SetColOffsets(hwndCtl,Offsets) ((int)(DWORD)SendMessage((hwndCtl), CLB_SETCOLOFFSETS, (WPARAM)Offsets, (LPARAM)0))
+#define ColumnLB_GetColOrder(hwndCtl) ((LPBYTE)(DWORD)SendMessage((hwndCtl), CLB_GETCOLORDER, (WPARAM)0, (LPARAM)0))
+#define ColumnLB_SetColOrder(hwndCtl, Order) ((LPBYTE)(DWORD)SendMessage((hwndCtl), CLB_SETCOLORDER, (WPARAM)0, (LPARAM)Order))
+#define ColumnLB_CheckFocus(hwndCtl) ((BOOL)(DWORD)SendMessage((hwndCtl), CLB_CHECKFOCUS, (WPARAM)0, (LPARAM)0))
+
+// -----------------------------------------------------------------------------------
+//
+// ColumnListBox_ Macros (uses CLB_ messages) Listbox definitions
+//
+#define ColumnLB_Enable(hwndCtl, fEnable) EnableWindow((hwndCtl), (fEnable))
+
+#define ColumnLB_GetCount(hwndCtl) ((int)(DWORD)SendMessage((hwndCtl), CLB_GETCOUNT, 0L, 0L))
+#define ColumnLB_ResetContent(hwndCtl) ((BOOL)(DWORD)SendMessage((hwndCtl), CLB_RESETCONTENT, 0L, 0L))
+
+#define ColumnLB_AddString(hwndCtl, lpsz) ((int)(DWORD)SendMessage((hwndCtl), CLB_ADDSTRING, 0L, (LPARAM)(LPCTSTR)(lpsz)))
+#define ColumnLB_InsertString(hwndCtl, index, lpsz) ((int)(DWORD)SendMessage((hwndCtl), CLB_INSERTSTRING, (WPARAM)(int)(index), (LPARAM)(LPCTSTR)(lpsz)))
+
+#define ColumnLB_AddItemData(hwndCtl, data) ((int)(DWORD)SendMessage((hwndCtl), CLB_ADDSTRING, 0L, (LPARAM)(data)))
+#define ColumnLB_InsertItemData(hwndCtl, index, data) ((int)(DWORD)SendMessage((hwndCtl), CLB_INSERTSTRING, (WPARAM)(int)(index), (LPARAM)(data)))
+
+#define ColumnLB_DeleteString(hwndCtl, index) ((int)(DWORD)SendMessage((hwndCtl), CLB_DELETESTRING, (WPARAM)(int)(index), 0L))
+
+#define ColumnLB_GetTextLen(hwndCtl, index) ((int)(DWORD)SendMessage((hwndCtl), CLB_GETTEXTLEN, (WPARAM)(int)(index), 0L))
+#define ColumnLB_GetText(hwndCtl, index, lpszBuffer) ((int)(DWORD)SendMessage((hwndCtl), CLB_GETTEXT, (WPARAM)(int)(index), (LPARAM)(LPCTSTR)(lpszBuffer)))
+#define ColumnLB_GetTextPtrs(hwndCtl, index) ((LPTSTR *)(DWORD)SendMessage((hwndCtl), CLB_GETTEXTPTRS, (WPARAM)(int)(index), (LPARAM)0))
+#define ColumnLB_GetRowColText(hwndCtl, index, col) (LPBYTE)(DWORD)SendMessage((hwndCtl), CLB_GETROWCOLTEXT, (WPARAM)(int) (col), (LPARAM)(int)(index))
+
+#define ColumnLB_GetItemData(hwndCtl, index) ((LRESULT)(DWORD)SendMessage((hwndCtl), CLB_GETITEMDATA, (WPARAM)(int)(index), 0L))
+#define ColumnLB_SetItemData(hwndCtl, index, data) ((int)(DWORD)SendMessage((hwndCtl), CLB_SETITEMDATA, (WPARAM)(int)(index), (LPARAM)(data)))
+
+#if (WINVER >= 0x030a)
+#define ColumnLB_FindString(hwndCtl, indexStart, lpszFind) ((int)(DWORD)SendMessage((hwndCtl), CLB_FINDSTRING, (WPARAM)(int)(indexStart), (LPARAM)(LPCTSTR)(lpszFind)))
+#define ColumnLB_FindItemData(hwndCtl, indexStart, data) ((int)(DWORD)SendMessage((hwndCtl), CLB_FINDSTRING, (WPARAM)(int)(indexStart), (LPARAM)(data)))
+
+#define ColumnLB_SetSel(hwndCtl, fSelect, index) ((int)(DWORD)SendMessage((hwndCtl), CLB_SETSEL, (WPARAM)(BOOL)(fSelect), (LPARAM)(index)))
+#define ColumnLB_SelItemRange(hwndCtl, fSelect, first, last) ((int)(DWORD)SendMessage((hwndCtl), CLB_SELITEMRANGE, (WPARAM)(BOOL)(fSelect), MAKELPARAM((first), (last))))
+
+#define ColumnLB_GetCurSel(hwndCtl) ((int)(DWORD)SendMessage((hwndCtl), CLB_GETCURSEL, 0L, 0L))
+#define ColumnLB_SetCurSel(hwndCtl, index) ((int)(DWORD)SendMessage((hwndCtl), CLB_SETCURSEL, (WPARAM)(int)(index), 0L))
+
+#define ColumnLB_SelectString(hwndCtl, indexStart, lpszFind) ((int)(DWORD)SendMessage((hwndCtl), CLB_SELECTSTRING, (WPARAM)(int)(indexStart), (LPARAM)(LPCTSTR)(lpszFind)))
+#define ColumnLB_SelectItemData(hwndCtl, indexStart, data) ((int)(DWORD)SendMessage((hwndCtl), CLB_SELECTSTRING, (WPARAM)(int)(indexStart), (LPARAM)(data)))
+
+#define ColumnLB_GetSel(hwndCtl, index) ((int)(DWORD)SendMessage((hwndCtl), CLB_GETSEL, (WPARAM)(int)(index), 0L))
+#define ColumnLB_GetSelCount(hwndCtl) ((int)(DWORD)SendMessage((hwndCtl), CLB_GETSELCOUNT, 0L, 0L))
+#define ColumnLB_GetTopIndex(hwndCtl) ((int)(DWORD)SendMessage((hwndCtl), CLB_GETTOPINDEX, 0L, 0L))
+#define ColumnLB_GetSelItems(hwndCtl, cItems, lpItems) ((int)(DWORD)SendMessage((hwndCtl), CLB_GETSELITEMS, (WPARAM)(int)(cItems), (LPARAM)(int *)(lpItems)))
+
+#define ColumnLB_SetTopIndex(hwndCtl, indexTop) ((int)(DWORD)SendMessage((hwndCtl), CLB_SETTOPINDEX, (WPARAM)(int)(indexTop), 0L))
+
+#define ColumnLB_SetColumnWidth(hwndCtl, cxColumn) ((void)SendMessage((hwndCtl), CLB_SETCOLUMNWIDTH, (WPARAM)(int)(cxColumn), 0L))
+#define ColumnLB_GetHorizontalExtent(hwndCtl) ((int)(DWORD)SendMessage((hwndCtl), CLB_GETHORIZONTALEXTENT, 0L, 0L))
+#define ColumnLB_SetHorizontalExtent(hwndCtl, cxExtent) ((void)SendMessage((hwndCtl), CLB_SETHORIZONTALEXTENT, (WPARAM)(int)(cxExtent), 0L))
+
+#define ColumnLB_SetTabStops(hwndCtl, cTabs, lpTabs) ((BOOL)(DWORD)SendMessage((hwndCtl), CLB_SETTABSTOPS, (WPARAM)(int)(cTabs), (LPARAM)(int *)(lpTabs)))
+
+#define ColumnLB_GetItemRect(hwndCtl, index, lprc) ((int)(DWORD)SendMessage((hwndCtl), CLB_GETITEMRECT, (WPARAM)(int)(index), (LPARAM)(RECT *)(lprc)))
+
+#define ColumnLB_SetCaretIndex(hwndCtl, index) ((int)(DWORD)SendMessage((hwndCtl), CLB_SETCARETINDEX, (WPARAM)(int)(index), 0L))
+#define ColumnLB_GetCaretIndex(hwndCtl) ((int)(DWORD)SendMessage((hwndCtl), CLB_GETCARETINDEX, 0L, 0L))
+
+#define ColumnLB_FindStringExact(hwndCtl, indexStart, lpszFind) ((int)(DWORD)SendMessage((hwndCtl), CLB_FINDSTRINGEXACT, (WPARAM)(int)(indexStart), (LPARAM)(LPCTSTR)(lpszFind)))
+
+#define ColumnLB_SetItemHeight(hwndCtl, index, cy) ((int)(DWORD)SendMessage((hwndCtl), CLB_SETITEMHEIGHT, (WPARAM)(int)(index), MAKELPARAM((cy), 0)))
+#define ColumnLB_GetItemHeight(hwndCtl, index) ((int)(DWORD)SendMessage((hwndCtl), CLB_GETITEMHEIGHT, (WPARAM)(int)(index), 0L))
+#endif /* WINVER >= 0x030a */
+
+#define ColumnLB_Dir(hwndCtl, attrs, lpszFileSpec) ((int)(DWORD)SendMessage((hwndCtl), CLB_DIR, (WPARAM)(UINT)(attrs), (LPARAM)(LPCTSTR)(lpszFileSpec)))
+
+#define ColumnLB_GetFocus(hwndCtl) ((HWND)(DWORD)SendMessage((hwndCtl), CLB_GETFOCUS, 0L, 0L))
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/nw/convert/nwconv/constant.h b/private/nw/convert/nwconv/constant.h
new file mode 100644
index 000000000..9179fa067
--- /dev/null
+++ b/private/nw/convert/nwconv/constant.h
@@ -0,0 +1,36 @@
+/*+-------------------------------------------------------------------------+
+ | Copyright 1993-1994 (C) Microsoft Corporation - All rights reserved. |
+ +-------------------------------------------------------------------------+*/
+
+#ifndef _CONSTANT_
+#define _CONSTANT_
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+#define MAX_NW_OBJECT_NAME_LEN 48
+#define MAX_NT_SERVER_NAME_LEN 15
+
+// These constants are just a safe max of the NT and NW constants
+#define MAX_SERVER_NAME_LEN 48
+#define MAX_USER_NAME_LEN 256 // must add room for nw4 names...
+#define MAX_NT_USER_NAME_LEN 20
+#define MAX_SHARE_NAME_LEN 16
+#define MAX_GROUP_NAME_LEN 50
+#define MAX_NT_GROUP_NAME_LEN 20
+#define MAX_DOMAIN_NAME_LEN 15
+#define MAX_UNC_PATH MAX_PATH + MAX_SERVER_NAME_LEN + MAX_SHARE_NAME_LEN + 265
+
+#define MAX_PW_LEN 14
+#define MAX_UCONST_LEN 10
+#define MAX_DOMAIN_LEN 15
+
+#define TMP_STR_LEN_256 256
+#define TMP_STR_LEN_80 80
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/nw/convert/nwconv/convapi.h b/private/nw/convert/nwconv/convapi.h
new file mode 100644
index 000000000..64ab3f954
--- /dev/null
+++ b/private/nw/convert/nwconv/convapi.h
@@ -0,0 +1,29 @@
+/*+-------------------------------------------------------------------------+
+ | Copyright 1993-1994 (C) Microsoft Corporation - All rights reserved. |
+ +-------------------------------------------------------------------------+*/
+
+#ifndef _HCONVAPI_
+#define _HCONVAPI_
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+#include "netutil.h"
+#include "filesel.h"
+#include "servlist.h"
+void TreeRecurseCurrentShareSet(SHARE_BUFFER *CShare);
+void TreeRootInit(SHARE_BUFFER *CShare, LPTSTR NewPath);
+void TreeFillRecurse(UINT Level, LPTSTR Path, DIR_BUFFER *Dir);
+ULONG TotalFileSizeGet();
+void FileSelect_Do(HWND hDlg, SOURCE_SERVER_BUFFER *SourceServ, SHARE_BUFFER *CShare);
+#include "sbrowse.h"
+#include "statbox.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+
diff --git a/private/nw/convert/nwconv/ctlspriv.h b/private/nw/convert/nwconv/ctlspriv.h
new file mode 100644
index 000000000..34a681f34
--- /dev/null
+++ b/private/nw/convert/nwconv/ctlspriv.h
@@ -0,0 +1,172 @@
+#define STRICT
+
+/* disable "non-standard extension" warnings in our code
+#ifndef RC_INVOKED
+#pragma warning(disable:4001)
+#endif
+ */
+
+#include <windows.h>
+#include <windowsx.h>
+//#include <port1632.h>
+
+#define OFFSETOF(x) x
+#define Static
+
+#define UNICODE_FONT_NAME TEXT("Lucida Sans Unicode")
+#define COUNTOF(x) (sizeof(x)/sizeof(*x))
+#define ByteCountOf(x) ((x) * sizeof(TCHAR))
+#define LONG2POINT(l, pt) ((pt).x = (SHORT)LOWORD(l), (pt).y = (SHORT)HIWORD(l))
+
+#include <commctrl.h>
+
+extern HINSTANCE hInst;
+
+BOOL FAR PASCAL InitToolbarClass(HINSTANCE hInstance);
+
+BOOL FAR PASCAL InitStatusClass(HINSTANCE hInstance);
+
+BOOL FAR PASCAL InitHeaderClass(HINSTANCE hInstance);
+
+BOOL FAR PASCAL InitButtonListBoxClass(HINSTANCE hInstance);
+
+BOOL FAR PASCAL InitTrackBar(HINSTANCE hInstance);
+
+BOOL FAR PASCAL InitUpDownClass(HINSTANCE hInstance);
+
+void FAR PASCAL NewSize(HWND hWnd, int nClientHeight, LONG style,
+ int left, int top, int width, int height);
+
+#define IDS_SPACE 0x0400
+
+/* System MenuHelp
+ */
+#define MH_SYSMENU (0x8000 - MINSYSCOMMAND)
+#define IDS_SYSMENU (MH_SYSMENU-16)
+#define IDS_HEADER (MH_SYSMENU-15)
+#define IDS_HEADERADJ (MH_SYSMENU-14)
+#define IDS_TOOLBARADJ (MH_SYSMENU-13)
+
+/* Cursor ID's
+ */
+#define IDC_SPLIT 100
+#define IDC_MOVEBUTTON 102
+
+#define IDC_STOP 103
+#define IDC_COPY 104
+#define IDC_MOVE 105
+
+/* Icon ID's
+ */
+#define IDI_INSERT 150
+
+/* AdjustDlgProc stuff
+ */
+#define ADJUSTDLG 200
+#define IDC_BUTTONLIST 201
+#define IDC_RESET 202
+#define IDC_CURRENT 203
+#define IDC_REMOVE 204
+#define IDC_MOVEUP 205
+#define IDC_MOVEDOWN 206
+
+/* bitmap IDs
+ */
+
+#define IDB_THUMB 300
+
+/* These are the internal structures used for a status bar. The header
+ * bar code needs this also
+ */
+typedef struct tagSTRINGINFO
+{
+ LPTSTR pString;
+ UINT uType;
+ int right;
+} STRINGINFO, *PSTRINGINFO;
+
+typedef struct tagSTATUSINFO
+{
+ HFONT hStatFont;
+ BOOL bDefFont;
+
+ int nFontHeight;
+ int nMinHeight;
+ int nBorderX, nBorderY, nBorderPart;
+
+ STRINGINFO sSimple;
+
+ int nParts;
+ STRINGINFO sInfo[1];
+
+} STATUSINFO, *PSTATUSINFO;
+
+#define GWL_PSTATUSINFO 0 /* Window word index for status info */
+#define SBT_NOSIMPLE 0x00ff /* Flags to indicate normal status bar */
+
+/* This is the default status bar face name
+ */
+extern TCHAR szSansSerif[];
+
+/* Note that window procedures in protect mode only DLL's may be called
+ * directly.
+ */
+void FAR PASCAL PaintStatusWnd(HWND hWnd, PSTATUSINFO pStatusInfo,
+ PSTRINGINFO pStringInfo, int nParts, int nBorderX, BOOL bHeader);
+LRESULT CALLBACK StatusWndProc(HWND hWnd, UINT uMessage, WPARAM wParam,
+ LPARAM lParam);
+
+/* toolbar.c */
+#define GWL_PTBSTATE 0
+
+typedef struct tagTBBMINFO /* info for recreating the bitmaps */
+{
+ int nButtons;
+ HINSTANCE hInst;
+ WORD wID;
+ HBITMAP hbm;
+
+} TBBMINFO, NEAR *PTBBMINFO;
+
+typedef struct tagTBSTATE /* instance data for toolbar window */
+{
+ PTBBUTTON pCaptureButton;
+ HWND hdlgCust;
+ HWND hwndCommand;
+ int nBitmaps;
+ PTBBMINFO pBitmaps;
+ int iNumButtons;
+ int nSysColorChanges;
+ TBBUTTON Buttons[1];
+
+} TBSTATE, NEAR *PTBSTATE;
+
+extern HBITMAP FAR PASCAL SelectBM(HDC hDC, PTBSTATE pTBState, int nButton);
+extern void FAR PASCAL DrawButton(HDC hdc, int x, int y, int dx, int dy,
+ PTBSTATE pTBState, PTBBUTTON ptButton);
+extern int FAR PASCAL TBHitTest(PTBSTATE pTBState, int xPos, int yPos);
+extern int FAR PASCAL PositionFromID(PTBSTATE pTBState, int id);
+
+/* tbcust.c */
+extern BOOL FAR PASCAL SaveRestore(HWND hWnd, PTBSTATE pTBState, BOOL bWrite,
+ LPTSTR FAR *lpNames);
+extern void FAR PASCAL CustomizeTB(HWND hWnd, PTBSTATE pTBState, int iPos);
+extern void FAR PASCAL MoveButton(HWND hwndToolbar, PTBSTATE pTBState,
+ int nSource);
+
+
+/* cutils.c */
+void FAR PASCAL NewSize(HWND hWnd, int nHeight, LONG style, int left, int top, int width, int height);
+BOOL FAR PASCAL CreateDitherBrush(BOOL bIgnoreCount); /* creates hbrDither */
+BOOL FAR PASCAL FreeDitherBrush(void);
+void FAR PASCAL CreateThumb(BOOL bIgnoreCount);
+void FAR PASCAL DestroyThumb(void);
+void FAR PASCAL CheckSysColors(void);
+
+extern HBRUSH hbrDither;
+extern HBITMAP hbmThumb;
+extern int nSysColorChanges;
+extern DWORD rgbFace; // globals used a lot
+extern DWORD rgbShadow;
+extern DWORD rgbHilight;
+extern DWORD rgbFrame;
diff --git a/private/nw/convert/nwconv/debug.c b/private/nw/convert/nwconv/debug.c
new file mode 100644
index 000000000..0a883f8c6
--- /dev/null
+++ b/private/nw/convert/nwconv/debug.c
@@ -0,0 +1,459 @@
+
+#define DUMP // leave enabled...
+
+#include "globals.h"
+#include "switches.h"
+#include <windows.h>
+#include <windowsx.h>
+#include <io.h>
+#include <malloc.h>
+#include <string.h>
+
+#if DBG
+void __cdecl dprintf(LPTSTR szFormat, ...) {
+ static TCHAR tmpStr[1024];
+ va_list marker;
+
+ va_start(marker, szFormat);
+ wvsprintf(tmpStr, szFormat, marker);
+ OutputDebugString(tmpStr);
+ va_end(marker);
+
+} // dprintf
+
+BOOL fDoEntireList = FALSE;
+
+void DumpConvertList(CONVERT_LIST *pConvertList)
+{
+ if (pConvertList)
+ {
+ dprintf(
+ TEXT("+++CONVERT_LIST(0x%08lx)"),
+ pConvertList
+ );
+
+ dprintf(
+ TEXT("\r\n\tnext = 0x%08lx")
+ TEXT("\r\n\tprev = 0x%08lx")
+ TEXT("\r\n\tSourceServ = 0x%08lx")
+ TEXT("\r\n\tFileServ = 0x%08lx")
+ TEXT("\r\n\tConvertOptions = 0x%08lx")
+ TEXT("\r\n\tFileOptions = 0x%08lx\r\n"),
+ pConvertList->next,
+ pConvertList->prev,
+ pConvertList->SourceServ,
+ pConvertList->FileServ,
+ pConvertList->ConvertOptions,
+ pConvertList->FileOptions
+ );
+
+ if (pConvertList->SourceServ)
+ DumpSourceServerBuffer(pConvertList->SourceServ);
+
+ if (pConvertList->FileServ)
+ DumpDestServerBuffer(pConvertList->FileServ);
+
+ if (fDoEntireList && pConvertList->next)
+ DumpConvertList(pConvertList->next);
+
+ dprintf(
+ TEXT("---CONVERT_LIST(0x%08lx)\r\n"),
+ pConvertList
+ );
+ }
+ else
+ dprintf(TEXT("Null pConvertList\r\n"));
+
+} // DumpConvertList
+
+
+void DumpDestServerBuffer(DEST_SERVER_BUFFER *pDestServerBuffer)
+{
+ if (pDestServerBuffer)
+ {
+ dprintf(
+ TEXT("+++DEST_SERVER_BUFFER(0x%08lx)"),
+ pDestServerBuffer
+ );
+
+ dprintf(
+ TEXT("\r\n\tnext = 0x%08lx")
+ TEXT("\r\n\tprev = 0x%08lx")
+ TEXT("\r\n\tIndex = 0x%08lx")
+ TEXT("\r\n\tType = 0x%08lx")
+ TEXT("\r\n\tVerMaj = 0x%08lx")
+ TEXT("\r\n\tVerMin = 0x%08lx")
+ TEXT("\r\n\tLName = %s")
+ TEXT("\r\n\tName = %s")
+ TEXT("\r\n\tShareList = 0x%08lx")
+ TEXT("\r\n\tNumVShares = 0x%08lx")
+ TEXT("\r\n\tVShareStart = 0x%08lx")
+ TEXT("\r\n\tVShareEnd = 0x%08lx")
+ TEXT("\r\n\tUseCount = 0x%08lx")
+ TEXT("\r\n\tIsNTAS = 0x%08lx")
+ TEXT("\r\n\tIsFPNW = 0x%08lx")
+ TEXT("\r\n\tInDomain = 0x%08lx")
+ TEXT("\r\n\tDomain = 0x%08lx")
+ TEXT("\r\n\tDriveList = 0x%08lx\r\n"),
+ pDestServerBuffer->next,
+ pDestServerBuffer->prev,
+ pDestServerBuffer->Index,
+ pDestServerBuffer->Type,
+ pDestServerBuffer->VerMaj,
+ pDestServerBuffer->VerMin,
+ pDestServerBuffer->LName,
+ pDestServerBuffer->Name,
+ pDestServerBuffer->ShareList,
+ pDestServerBuffer->NumVShares,
+ pDestServerBuffer->VShareStart,
+ pDestServerBuffer->VShareEnd,
+ pDestServerBuffer->UseCount,
+ pDestServerBuffer->IsNTAS,
+ pDestServerBuffer->IsFPNW,
+ pDestServerBuffer->InDomain,
+ pDestServerBuffer->Domain,
+ pDestServerBuffer->DriveList
+ );
+
+ if (pDestServerBuffer->DriveList)
+ DumpDriveList(pDestServerBuffer->DriveList);
+
+ if (pDestServerBuffer->ShareList)
+ DumpShareList(pDestServerBuffer->ShareList);
+
+ if (pDestServerBuffer->VShareStart)
+ DumpVirtualShareBuffer(pDestServerBuffer->VShareStart);
+
+ if (pDestServerBuffer->Domain)
+ DumpDomainBuffer(pDestServerBuffer->Domain);
+
+ if (fDoEntireList && pDestServerBuffer->next)
+ DumpDestServerBuffer(pDestServerBuffer->next);
+
+ dprintf(
+ TEXT("---DEST_SERVER_BUFFER(0x%08lx)\r\n"),
+ pDestServerBuffer
+ );
+ }
+ else
+ dprintf(TEXT("Null pDestServerBuffer\r\n"));
+
+} // DumpDestServerBuffer
+
+
+void DumpSourceServerBuffer(SOURCE_SERVER_BUFFER *pSourceServerBuffer)
+{
+ if (pSourceServerBuffer)
+ {
+ dprintf(
+ TEXT("+++SOURCE_SERVER_BUFFER(0x%08lx)"),
+ pSourceServerBuffer
+ );
+
+ dprintf(
+ TEXT("\r\n\tnext = 0x%08lx")
+ TEXT("\r\n\tprev = 0x%08lx")
+ TEXT("\r\n\tIndex = 0x%08lx")
+ TEXT("\r\n\tType = 0x%08lx")
+ TEXT("\r\n\tVerMaj = 0x%08lx")
+ TEXT("\r\n\tVerMin = 0x%08lx")
+ TEXT("\r\n\tLName = %s")
+ TEXT("\r\n\tName = %s")
+ TEXT("\r\n\tShareList = 0x%08lx\r\n"),
+ pSourceServerBuffer->next,
+ pSourceServerBuffer->prev,
+ pSourceServerBuffer->Index,
+ pSourceServerBuffer->Type,
+ pSourceServerBuffer->VerMaj,
+ pSourceServerBuffer->VerMin,
+ pSourceServerBuffer->LName,
+ pSourceServerBuffer->Name,
+ pSourceServerBuffer->ShareList
+ );
+
+ if (pSourceServerBuffer->ShareList)
+ DumpShareList(pSourceServerBuffer->ShareList);
+
+ if (fDoEntireList && pSourceServerBuffer->next)
+ DumpSourceServerBuffer(pSourceServerBuffer->next);
+
+ dprintf(
+ TEXT("---SOURCE_SERVER_BUFFER(0x%08lx)\r\n"),
+ pSourceServerBuffer
+ );
+ }
+ else
+ dprintf(TEXT("Null pSourceServerBuffer\r\n"));
+
+} // DumpSourceServerBuffer
+
+
+void DumpDomainBuffer(DOMAIN_BUFFER *pDomainBuffer)
+{
+ if (pDomainBuffer)
+ {
+ dprintf(
+ TEXT("+++DOMAIN_BUFFER(0x%08lx)"),
+ pDomainBuffer
+ );
+
+ dprintf(
+ TEXT("\r\n\tnext = 0x%08lx")
+ TEXT("\r\n\tprev = 0x%08lx")
+ TEXT("\r\n\tIndex = 0x%08lx")
+ TEXT("\r\n\tName = %s")
+ TEXT("\r\n\tPDCName = %s")
+ TEXT("\r\n\tType = 0x%08lx")
+ TEXT("\r\n\tVerMaj = 0x%08lx")
+ TEXT("\r\n\tVerMin = 0x%08lx")
+ TEXT("\r\n\tUseCount = 0x%08lx\r\n"),
+ pDomainBuffer->next,
+ pDomainBuffer->prev,
+ pDomainBuffer->Index,
+ pDomainBuffer->Name,
+ pDomainBuffer->PDCName,
+ pDomainBuffer->Type,
+ pDomainBuffer->VerMaj,
+ pDomainBuffer->VerMin,
+ pDomainBuffer->UseCount
+ );
+
+ if (fDoEntireList && pDomainBuffer->next)
+ DumpDomainBuffer(pDomainBuffer->next);
+
+ dprintf(
+ TEXT("---DOMAIN_BUFFER(0x%08lx)\r\n"),
+ pDomainBuffer
+ );
+ }
+ else
+ dprintf(TEXT("Null pDomainBuffer\r\n"));
+
+} // DumpDomainBuffer
+
+
+void DumpVirtualShareBuffer(VIRTUAL_SHARE_BUFFER *pVirtualShareBuffer)
+{
+ if (pVirtualShareBuffer)
+ {
+ dprintf(
+ TEXT("+++VIRTUAL_SHARE_BUFFER(0x%08lx)"),
+ pVirtualShareBuffer
+ );
+
+ dprintf(
+ TEXT("\r\n\tVFlag = 0x%08lx")
+ TEXT("\r\n\tnext = 0x%08lx")
+ TEXT("\r\n\tprev = 0x%08lx")
+ TEXT("\r\n\tIndex = 0x%08lx")
+ TEXT("\r\n\tName = %s")
+ TEXT("\r\n\tUseCount = 0x%08lx")
+ TEXT("\r\n\tPath = %s")
+ TEXT("\r\n\tDrive = 0x%08lx\r\n"),
+ pVirtualShareBuffer->VFlag,
+ pVirtualShareBuffer->next,
+ pVirtualShareBuffer->prev,
+ pVirtualShareBuffer->Index,
+ pVirtualShareBuffer->Name,
+ pVirtualShareBuffer->UseCount,
+ pVirtualShareBuffer->Path,
+ pVirtualShareBuffer->Drive
+ );
+
+ if (pVirtualShareBuffer->Drive)
+ DumpDriveBuffer(pVirtualShareBuffer->Drive);
+
+ if (fDoEntireList && pVirtualShareBuffer->next)
+ DumpVirtualShareBuffer(pVirtualShareBuffer->next);
+
+ dprintf(
+ TEXT("---VIRTUAL_SHARE_BUFFER(0x%08lx)\r\n"),
+ pVirtualShareBuffer
+ );
+ }
+ else
+ dprintf(TEXT("Null pVirtualShareBuffer\r\n"));
+
+} // DumpVirtualShareBuffer
+
+
+void DumpShareList(SHARE_LIST *pShareList)
+{
+ ULONG index;
+ if (pShareList)
+ {
+ dprintf(
+ TEXT(">>>SHARE_LIST(0x%08lx)(%d entries, %d converting, %s)\r\n"),
+ pShareList,
+ pShareList->Count,
+ pShareList->ConvertCount,
+ pShareList->Fixup ? TEXT("FIXUP") : TEXT("NO FIXUP")
+ );
+ for (index = 0; index < pShareList->Count; index++ )
+ DumpShareBuffer(&pShareList->SList[index]);
+ }
+ else
+ dprintf(TEXT("Null pShareList\r\n"));
+
+} // DumpShareList
+
+
+void DumpShareBuffer(SHARE_BUFFER *pShareBuffer)
+{
+ if (pShareBuffer)
+ {
+ dprintf(
+ TEXT("+++SHARE_BUFFER(0x%08lx)"),
+ pShareBuffer
+ );
+
+ dprintf(
+ TEXT("\r\n\tVFlag = 0x%08lx")
+ TEXT("\r\n\tIndex = 0x%08lx")
+ TEXT("\r\n\tName = %s")
+ TEXT("\r\n\tConvert = 0x%08lx")
+ TEXT("\r\n\tHiddenFiles = 0x%08lx")
+ TEXT("\r\n\tSystemFiles = 0x%08lx")
+ TEXT("\r\n\tToFat = 0x%08lx")
+ TEXT("\r\n\tRoot = 0x%08lx")
+ TEXT("\r\n\tDrive = 0x%08lx")
+ TEXT("\r\n\tSize = 0x%08lx")
+ TEXT("\r\n\tPath = %s")
+ TEXT("\r\n\tSubDir = %s")
+ TEXT("\r\n\tVirtual = 0x%08lx")
+ TEXT("\r\n\tDestShare = 0x%08lx\r\n"),
+ pShareBuffer->VFlag,
+ pShareBuffer->Index,
+ pShareBuffer->Name,
+ pShareBuffer->Convert,
+ pShareBuffer->HiddenFiles,
+ pShareBuffer->SystemFiles,
+ pShareBuffer->ToFat,
+ pShareBuffer->Root,
+ pShareBuffer->Drive,
+ pShareBuffer->Size,
+ pShareBuffer->Path,
+ pShareBuffer->SubDir,
+ pShareBuffer->Virtual,
+ pShareBuffer->DestShare
+ );
+
+ if (pShareBuffer->Root)
+ DumpDirBuffer(pShareBuffer->Root);
+
+ if (pShareBuffer->Drive)
+ DumpDriveBuffer(pShareBuffer->Drive);
+
+ if (pShareBuffer->DestShare)
+ if (!pShareBuffer->Virtual)
+ DumpShareBuffer(pShareBuffer->DestShare);
+ else
+ DumpVirtualShareBuffer((VIRTUAL_SHARE_BUFFER *)pShareBuffer->DestShare);
+
+ dprintf(
+ TEXT("---SHARE_BUFFER(0x%08lx)\r\n"),
+ pShareBuffer
+ );
+ }
+ else
+ dprintf(TEXT("Null pShareBuffer\r\n"));
+
+} // DumpShareBuffer
+
+
+void DumpDriveList(DRIVE_LIST *pDriveList)
+{
+ ULONG index;
+ if (pDriveList)
+ {
+ dprintf(TEXT(">>>DRIVE_LIST(0x%08lx)(%d entries)\r\n"), pDriveList, pDriveList->Count);
+ for (index = 0; index < pDriveList->Count; index++ )
+ DumpDriveBuffer(&pDriveList->DList[index]);
+ }
+ else
+ dprintf(TEXT("Null pDriveList\r\n"));
+
+} // DumpDriveList
+
+
+void DumpDriveBuffer(DRIVE_BUFFER *pDriveBuffer)
+{
+ if (pDriveBuffer)
+ {
+ dprintf(
+ TEXT("+++DRIVE_BUFFER(0x%08lx)"),
+ pDriveBuffer
+ );
+
+ dprintf(
+ TEXT("\r\n\tType = 0x%08lx")
+ TEXT("\r\n\tDrive = %s")
+ TEXT("\r\n\tDriveType = %s")
+ TEXT("\r\n\tName = %s")
+ TEXT("\r\n\tTotalSpace = 0x%08lx")
+ TEXT("\r\n\tFreeSpace = 0x%08lx")
+ TEXT("\r\n\tAllocSpace = 0x%08lx\r\n"),
+ pDriveBuffer->Type,
+ pDriveBuffer->Drive,
+ pDriveBuffer->DriveType,
+ pDriveBuffer->Name,
+ pDriveBuffer->TotalSpace,
+ pDriveBuffer->FreeSpace,
+ pDriveBuffer->AllocSpace
+ );
+
+ dprintf(
+ TEXT("---DRIVE_BUFFER(0x%08lx)\r\n"),
+ pDriveBuffer
+ );
+ }
+ else
+ dprintf(TEXT("Null pDriveBuffer)\r\n"));
+
+} // DumpDriveBuffer
+
+
+void DumpDirBuffer(DIR_BUFFER *pDirBuffer)
+{
+ if (pDirBuffer)
+ {
+ dprintf(
+ TEXT("+++DIR_BUFFER(0x%08lx)"),
+ pDirBuffer
+ );
+
+ dprintf(
+ TEXT("\r\n\tName = %s")
+ TEXT("\r\n\tparent = 0x%08lx")
+ TEXT("\r\n\tLast = 0x%08lx")
+ TEXT("\r\n\tAttributes = 0x%08lx")
+ TEXT("\r\n\tConvert = 0x%08lx")
+ TEXT("\r\n\tSpecial = 0x%08lx")
+ TEXT("\r\n\tDirList = 0x%08lx")
+ TEXT("\r\n\tFileList = 0x%08lx\r\n"),
+ pDirBuffer->Name,
+ pDirBuffer->parent,
+ pDirBuffer->Last,
+ pDirBuffer->Attributes,
+ pDirBuffer->Convert,
+ pDirBuffer->Special,
+ pDirBuffer->DirList,
+ pDirBuffer->FileList
+ );
+
+// if (pDirBuffer->DirList)
+// DumpDirList(pDirBuffer->DirList);
+
+// if (pDirBuffer->FileList)
+// DumpFileList(pDirBuffer->FileList);
+
+ dprintf(
+ TEXT("---DIR_BUFFER(0x%08lx)\r\n"),
+ pDirBuffer
+ );
+ }
+ else
+ dprintf(TEXT("Null pDirBuffer\r\n"));
+
+} // DumpDirBuffer
+#endif
diff --git a/private/nw/convert/nwconv/debug.h b/private/nw/convert/nwconv/debug.h
new file mode 100644
index 000000000..19cfcdb95
--- /dev/null
+++ b/private/nw/convert/nwconv/debug.h
@@ -0,0 +1,34 @@
+
+#ifndef _DEBUG_
+#define _DEBUG_
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+void __cdecl dprintf(LPTSTR szFormat, ...);
+
+#ifdef DUMP
+
+#include "netutil.h"
+#include "filesel.h"
+#include "servlist.h"
+
+void DumpConvertList(CONVERT_LIST *pConvertList);
+void DumpDestServerBuffer(DEST_SERVER_BUFFER *pDestServerBuffer);
+void DumpSourceServerBuffer(SOURCE_SERVER_BUFFER *pSourceServerBuffer);
+void DumpDomainBuffer(DOMAIN_BUFFER *pDomainBuffer);
+void DumpVirtualShareBuffer(VIRTUAL_SHARE_BUFFER *pVirtualShareBuffer);
+void DumpShareList(SHARE_LIST *pShareList);
+void DumpShareBuffer(SHARE_BUFFER *pShareBuffer);
+void DumpDriveList(DRIVE_LIST *pDriveList);
+void DumpDriveBuffer(DRIVE_BUFFER *pDriveBuffer);
+void DumpDirBuffer(DIR_BUFFER *pDirBuffer);
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/nw/convert/nwconv/encrypt.c b/private/nw/convert/nwconv/encrypt.c
new file mode 100644
index 000000000..bfff89fe0
--- /dev/null
+++ b/private/nw/convert/nwconv/encrypt.c
@@ -0,0 +1,328 @@
+
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ encrypt.c
+
+Abstract:
+
+ This module implements the routines for the NetWare
+ redirector to mangle an objectid, challenge key and
+ password such that a NetWare server will accept the
+ password as valid.
+
+ This program uses information published in Byte Magazine.
+
+Author:
+
+ Colin Watson [ColinW] 15-Mar-1993
+
+Revision History:
+
+--*/
+
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+// #include <nwsutil.h>
+#include <usrprop.h>
+#include <crypt.h>
+// #include <ncpcomm.h>
+
+#define SWAPWORD(w) ((WORD)((w & 0xFF) << 8)|(WORD)(w >> 8))
+#define SWAPLONG(l) MAKELONG(SWAPWORD(HIWORD(l)),SWAPWORD(LOWORD(l)))
+
+#define NCP_WELL_KNOWN_SUPERVISOR_ID (ULONG) 0x00000001
+#define NCP_WELL_KNOWN_SUPERVISOR_ID_SWAPPED (ULONG) 0x01000000
+#define NCP_WELL_KNOWN_SUPERVISOR_ID_CHICAGO (ULONG) 0x00010000
+#define NCP_WELL_KNOWN_PSERVER_ID (ULONG) 0x00000002
+
+#define NCP_PSERVER_SIGNATURE L"PS_"
+
+#define SWAP_OBJECT_ID(id) (id == NCP_WELL_KNOWN_SUPERVISOR_ID) ? \
+ NCP_WELL_KNOWN_SUPERVISOR_ID_SWAPPED : \
+ MAKELONG(LOWORD(id),SWAPWORD(HIWORD(id)))
+
+
+UCHAR Table[] =
+{0x7,0x8,0x0,0x8,0x6,0x4,0xE,0x4,0x5,0xC,0x1,0x7,0xB,0xF,0xA,0x8,
+ 0xF,0x8,0xC,0xC,0x9,0x4,0x1,0xE,0x4,0x6,0x2,0x4,0x0,0xA,0xB,0x9,
+ 0x2,0xF,0xB,0x1,0xD,0x2,0x1,0x9,0x5,0xE,0x7,0x0,0x0,0x2,0x6,0x6,
+ 0x0,0x7,0x3,0x8,0x2,0x9,0x3,0xF,0x7,0xF,0xC,0xF,0x6,0x4,0xA,0x0,
+ 0x2,0x3,0xA,0xB,0xD,0x8,0x3,0xA,0x1,0x7,0xC,0xF,0x1,0x8,0x9,0xD,
+ 0x9,0x1,0x9,0x4,0xE,0x4,0xC,0x5,0x5,0xC,0x8,0xB,0x2,0x3,0x9,0xE,
+ 0x7,0x7,0x6,0x9,0xE,0xF,0xC,0x8,0xD,0x1,0xA,0x6,0xE,0xD,0x0,0x7,
+ 0x7,0xA,0x0,0x1,0xF,0x5,0x4,0xB,0x7,0xB,0xE,0xC,0x9,0x5,0xD,0x1,
+ 0xB,0xD,0x1,0x3,0x5,0xD,0xE,0x6,0x3,0x0,0xB,0xB,0xF,0x3,0x6,0x4,
+ 0x9,0xD,0xA,0x3,0x1,0x4,0x9,0x4,0x8,0x3,0xB,0xE,0x5,0x0,0x5,0x2,
+ 0xC,0xB,0xD,0x5,0xD,0x5,0xD,0x2,0xD,0x9,0xA,0xC,0xA,0x0,0xB,0x3,
+ 0x5,0x3,0x6,0x9,0x5,0x1,0xE,0xE,0x0,0xE,0x8,0x2,0xD,0x2,0x2,0x0,
+ 0x4,0xF,0x8,0x5,0x9,0x6,0x8,0x6,0xB,0xA,0xB,0xF,0x0,0x7,0x2,0x8,
+ 0xC,0x7,0x3,0xA,0x1,0x4,0x2,0x5,0xF,0x7,0xA,0xC,0xE,0x5,0x9,0x3,
+ 0xE,0x7,0x1,0x2,0xE,0x1,0xF,0x4,0xA,0x6,0xC,0x6,0xF,0x4,0x3,0x0,
+ 0xC,0x0,0x3,0x6,0xF,0x8,0x7,0xB,0x2,0xD,0xC,0x6,0xA,0xA,0x8,0xD};
+
+UCHAR Keys[32] =
+{0x48,0x93,0x46,0x67,0x98,0x3D,0xE6,0x8D,
+ 0xB7,0x10,0x7A,0x26,0x5A,0xB9,0xB1,0x35,
+ 0x6B,0x0F,0xD5,0x70,0xAE,0xFB,0xAD,0x11,
+ 0xF4,0x47,0xDC,0xA7,0xEC,0xCF,0x50,0xC0};
+
+#define XorArray( DEST, SRC ) { \
+ PULONG D = (PULONG)DEST; \
+ PULONG S = (PULONG)SRC; \
+ int i; \
+ for ( i = 0; i <= 7 ; i++ ) { \
+ D[i] ^= S[i]; \
+ } \
+}
+
+int
+Scramble(
+ int iSeed,
+ UCHAR achBuffer[32]
+ );
+
+VOID
+Shuffle(
+ UCHAR *achObjectId,
+ UCHAR *szUpperPassword,
+ int iPasswordLen,
+ UCHAR *achOutputBuffer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine shuffles around the object ID with the password
+
+Arguments:
+
+ IN achObjectId - Supplies the 4 byte user's bindery object id
+
+ IN szUpperPassword - Supplies the user's uppercased password on the
+ first call to process the password. On the second and third calls
+ this parameter contains the OutputBuffer from the first call
+
+ IN iPasswordLen - length of uppercased password
+
+ OUT achOutputBuffer - Returns the 8 byte sub-calculation
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ int iTempIndex;
+ int iOutputIndex;
+ UCHAR achTemp[32];
+
+ //
+ // Truncate all trailing zeros from the password.
+ //
+
+ while (iPasswordLen > 0 && szUpperPassword[iPasswordLen-1] == 0 ) {
+ iPasswordLen--;
+ }
+
+ //
+ // Initialize the achTemp buffer. Initialization consists of taking
+ // the password and dividing it up into chunks of 32. Any bytes left
+ // over are the remainder and do not go into the initialization.
+ //
+ // achTemp[0] = szUpperPassword[0] ^ szUpperPassword[32] ^ szUpper...
+ // achTemp[1] = szUpperPassword[1] ^ szUpperPassword[33] ^ szUpper...
+ // etc.
+ //
+
+ if ( iPasswordLen > 32) {
+
+ // At least one chunk of 32. Set the buffer to the first chunk.
+
+ RtlCopyMemory( achTemp, szUpperPassword, 32 );
+
+ szUpperPassword +=32; // Remove the first chunk
+ iPasswordLen -=32;
+
+ while ( iPasswordLen >= 32 ) {
+ //
+ // Xor this chunk with the characters already loaded into
+ // achTemp.
+ //
+
+ XorArray( achTemp, szUpperPassword);
+
+ szUpperPassword +=32; // Remove this chunk
+ iPasswordLen -=32;
+ }
+
+ } else {
+
+ // No chunks of 32 so set the buffer to zero's
+
+ RtlZeroMemory( achTemp, sizeof(achTemp));
+
+ }
+
+ //
+ // achTemp is now initialized. Load the remainder into achTemp.
+ // The remainder is repeated to fill achTemp.
+ //
+ // The corresponding character from Keys is taken to seperate
+ // each repitition.
+ //
+ // As an example, take the remainder "ABCDEFG". The remainder is expanded
+ // to "ABCDEFGwABCDEFGxABCDEFGyABCDEFGz" where w is Keys[7],
+ // x is Keys[15], y is Keys[23] and z is Keys[31].
+ //
+ //
+
+ if (iPasswordLen > 0) {
+ int iPasswordOffset = 0;
+ for (iTempIndex = 0; iTempIndex < 32; iTempIndex++) {
+
+ if (iPasswordLen == iPasswordOffset) {
+ iPasswordOffset = 0;
+ achTemp[iTempIndex] ^= Keys[iTempIndex];
+ } else {
+ achTemp[iTempIndex] ^= szUpperPassword[iPasswordOffset++];
+ }
+ }
+ }
+
+ //
+ // achTemp has been loaded with the users password packed into 32
+ // bytes. Now take the objectid that came from the server and use
+ // that to munge every byte in achTemp.
+ //
+
+ for (iTempIndex = 0; iTempIndex < 32; iTempIndex++)
+ achTemp[iTempIndex] ^= achObjectId[ iTempIndex & 3];
+
+ Scramble( Scramble( 0, achTemp ), achTemp );
+
+ //
+ // Finally take pairs of bytes in achTemp and return the two
+ // nibbles obtained from Table. The pairs of bytes used
+ // are achTemp[n] and achTemp[n+16].
+ //
+
+ for (iOutputIndex = 0; iOutputIndex < 16; iOutputIndex++) {
+
+ achOutputBuffer[iOutputIndex] =
+ Table[achTemp[iOutputIndex << 1]] |
+ (Table[achTemp[(iOutputIndex << 1) + 1]] << 4);
+ }
+
+ return;
+}
+
+int
+Scramble(
+ int iSeed,
+ UCHAR achBuffer[32]
+ )
+
+/*++
+
+Routine Description:
+
+ This routine scrambles around the contents of the buffer. Each buffer
+ position is updated to include the contents of at least two character
+ positions plus an EncryptKey value. The buffer is processed left to right
+ and so if a character position chooses to merge with a buffer position
+ to its left then this buffer position will include bits derived from at
+ least 3 bytes of the original buffer contents.
+
+Arguments:
+
+ IN iSeed
+ IN OUT achBuffer[32]
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ int iBufferIndex;
+
+ for (iBufferIndex = 0; iBufferIndex < 32; iBufferIndex++) {
+ achBuffer[iBufferIndex] =
+ (UCHAR)(
+ ((UCHAR)(achBuffer[iBufferIndex] + iSeed)) ^
+ ((UCHAR)( achBuffer[(iBufferIndex+iSeed) & 31] -
+ Keys[iBufferIndex] )));
+
+ iSeed += achBuffer[iBufferIndex];
+ }
+ return iSeed;
+}
+
+NTSTATUS
+ReturnNetwareForm(
+ const char * pszSecretValue,
+ DWORD dwUserId,
+ const WCHAR * pchNWPassword,
+ UCHAR * pchEncryptedNWPassword
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes the ObjectId and encrypts it with the user
+ supplied password to develop a credential for the intermediate form.
+
+Arguments:
+ DWORD dwUserId - Supplies the 4 byte user's object id
+ const WCHAR * pchNWPassword - Supplies the user's password
+
+ UCHAR * pchEncryptedNWPassword - 16 characters where the result goes.
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ DWORD dwStatus;
+ DWORD chObjectId = SWAP_OBJECT_ID (dwUserId);
+ UNICODE_STRING uniNWPassword;
+ OEM_STRING oemNWPassword;
+
+ //
+ // shuffle actually uses 32 bytes, not just 16. It only returns 16 though.
+ //
+
+ UCHAR pszShuffledNWPassword[NT_OWF_PASSWORD_LENGTH * 2];
+
+ uniNWPassword.Buffer = (WCHAR *) pchNWPassword;
+ uniNWPassword.Length = lstrlenW (pchNWPassword)*sizeof(WCHAR);
+ uniNWPassword.MaximumLength = uniNWPassword.Length;
+
+ if ((dwStatus = RtlUpcaseUnicodeStringToOemString (&oemNWPassword,
+ &uniNWPassword,
+ TRUE)) == STATUS_SUCCESS)
+ {
+ Shuffle((UCHAR *) &chObjectId, oemNWPassword.Buffer, oemNWPassword.Length, pszShuffledNWPassword);
+
+ // Encrypt with LSA secret.
+ dwStatus = RtlEncryptNtOwfPwdWithUserKey(
+ (PNT_OWF_PASSWORD) pszShuffledNWPassword,
+ (PUSER_SESSION_KEY) pszSecretValue,
+ (PENCRYPTED_NT_OWF_PASSWORD) pchEncryptedNWPassword);
+ }
+
+ return (dwStatus);
+}
diff --git a/private/nw/convert/nwconv/error.c b/private/nw/convert/nwconv/error.c
new file mode 100644
index 000000000..d1bf87831
--- /dev/null
+++ b/private/nw/convert/nwconv/error.c
@@ -0,0 +1,69 @@
+/*
+ +-------------------------------------------------------------------------+
+ | Error Handling Routines |
+ +-------------------------------------------------------------------------+
+ | (c) Copyright 1993-1994 |
+ | Microsoft Corp. |
+ | All rights reserved |
+ | |
+ | Program : [Error.c] |
+ | Programmer : Arthur Hanson |
+ | Original Program Date : [Jul 27, 1993] |
+ | Last Update : [Jun 18, 1994] |
+ | |
+ | Version: 1.00 |
+ | |
+ | Description: |
+ | |
+ | History: |
+ | arth Jun 18, 1994 1.00 Original Version. |
+ | |
+ +-------------------------------------------------------------------------+
+*/
+
+#include "globals.h"
+
+
+/*+-------------------------------------------------------------------------+
+ | CriticalErrorExit()
+ |
+ | This should only be called when there is an unrecoverable error and
+ | the program must abort (such as running out of disk space on main
+ | system or out of memory).
+ |
+ | Can't dynamically load the error string (must do this at program
+ | init), because at time or error we might not be able to load it!
+ |
+ +-------------------------------------------------------------------------+*/
+void CriticalErrorExit(LPTSTR ErrorString) {
+ MessageBox(NULL, ErrorString, Lids(IDS_E_1), MB_ICONHAND | MB_SYSTEMMODAL | MB_OK);
+ exit(0);
+
+} // CriticalErrorExit
+
+
+
+/*+-------------------------------------------------------------------------+
+ | WarningError()
+ |
+ | Pops up a warning message to the user - this should only be used
+ | when the user must be notified of something (the program stops until
+ | the user responds), but it is not so critical the the program has to
+ | abort.
+ |
+ | An example of this is if a config file is corrupt and the program
+ | will ignore it.
+ |
+ +-------------------------------------------------------------------------+*/
+void WarningError(LPTSTR ErrorString, ...) {
+ static TCHAR tmpStr[TMP_STR_LEN_256];
+ va_list marker;
+
+ va_start(marker, ErrorString);
+ wvsprintf(tmpStr, ErrorString, marker);
+ MessageBox(NULL, tmpStr, Lids(IDS_E_2), MB_ICONHAND | MB_OK);
+ va_end(marker);
+
+} // WarningError
+
+
diff --git a/private/nw/convert/nwconv/error.h b/private/nw/convert/nwconv/error.h
new file mode 100644
index 000000000..10d2451a4
--- /dev/null
+++ b/private/nw/convert/nwconv/error.h
@@ -0,0 +1,19 @@
+/*+-------------------------------------------------------------------------+
+ | Copyright 1993-1994 (C) Microsoft Corporation - All rights reserved. |
+ +-------------------------------------------------------------------------+*/
+
+#ifndef _HERROR_
+#define _HERROR_
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+void CriticalErrorExit(LPTSTR ErrorString);
+void WarningError(LPTSTR ErrorString, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/nw/convert/nwconv/fastcopy.c b/private/nw/convert/nwconv/fastcopy.c
new file mode 100644
index 000000000..e97191e10
--- /dev/null
+++ b/private/nw/convert/nwconv/fastcopy.c
@@ -0,0 +1,244 @@
+/*
+ +-------------------------------------------------------------------------+
+ | File Copying Routines |
+ +-------------------------------------------------------------------------+
+ | (c) Copyright 1993-1994 |
+ | Microsoft Corp. |
+ | All rights reserved |
+ | |
+ | Program : [FastCopy.c] |
+ | Programmer : Arthur Hanson |
+ | Original Program Date : [Jul 27, 1993] |
+ | Last Update : [Jun 18, 1994] |
+ | |
+ | Version: 1.00 |
+ | |
+ | Use multiple threads to whack data from one file to another |
+ | |
+ | Modifications: |
+ | 18-Oct-1990 w-barry Removed 'dead' code. |
+ | 21-Nov-1990 w-barry Updated API's to the Win32 set. |
+ | |
+ +-------------------------------------------------------------------------+
+*/
+
+#define INCL_DOSPROCESS
+#define INCL_DOSSEMAPHORES
+
+#include "globals.h"
+#include <stdio.h>
+#include <process.h>
+#include <windows.h>
+#include <malloc.h>
+#include "mem.h"
+#include "debug.h"
+#include "utils.h"
+#include "convapi.h"
+
+#define BUFSIZE 0xFE00 // full segment minus sector
+#define STACKSIZE 256 // stack size for child thread
+
+typedef struct BUF BUF;
+
+struct BUF {
+ BOOL flag;
+ DWORD cbBuf;
+ BUF *fpbufNext;
+ BYTE ach[BUFSIZE];
+};
+
+#define LAST TRUE
+#define NOTLAST FALSE
+
+static HANDLE hevQNotEmpty;
+static CRITICAL_SECTION hcrtQLock;
+static BUF *fpbufHead = NULL;
+static BUF *fpbufTail = NULL;
+static HANDLE hfSrc, hfDst;
+static HANDLE hThread;
+static BOOLEAN fAbort;
+
+// forward type definitions
+
+LPTSTR writer( void );
+DWORD reader( void );
+BUF *dequeue( void );
+void enqueue( BUF *fpbuf );
+TCHAR *fastcopy( HANDLE hfSrcParm, HANDLE hfDstParm );
+
+/*+-------------------------------------------------------------------------+
+ | writer()
+ |
+ +-------------------------------------------------------------------------+*/
+LPTSTR writer ( void ) {
+ BUF *fpbuf;
+ DWORD cbBytesOut;
+ BOOL f = !LAST;
+ LPTSTR npsz = NULL;
+ ULONG BytesCopied = 0;
+
+ TCHAR buffer[MAX_PATH];
+
+ Status_Bytes(lToStr(BytesCopied));
+
+ while (f != LAST && npsz == NULL) {
+ fpbuf = dequeue ();
+
+ if (fpbuf) {
+ if ((f = fpbuf->flag) != LAST) {
+ if (!WriteFile(hfDst, fpbuf->ach, fpbuf->cbBuf, &cbBytesOut, NULL)) {
+ npsz = Lids(IDS_L_4);
+#ifdef DEBUG
+dprintf(TEXT("ERROR: WriteFile\n"));
+#endif
+ } else {
+ BytesCopied += cbBytesOut;
+ Status_Bytes(lToStr(BytesCopied));
+
+
+ if (cbBytesOut != (DWORD)fpbuf->cbBuf) {
+ npsz = Lids(IDS_L_5);
+#ifdef DEBUG
+dprintf(TEXT("ERROR: WriteFile: out-of-space\n"));
+#endif
+ }
+ }
+ }
+ else
+ npsz = *(LPTSTR *)fpbuf->ach;
+
+ FreeMemory(fpbuf);
+
+ } else {
+ wsprintf (buffer, Lids(IDS_L_4), fpbuf);
+#ifdef DEBUG
+dprintf(TEXT("ERROR: WriteFile - FileBuffer is NULL\n"));
+#endif
+ }
+ }
+
+ if (f != LAST)
+ fAbort = TRUE;
+
+ WaitForSingleObject(hThread, (DWORD)-1);
+ CloseHandle(hThread);
+ CloseHandle(hevQNotEmpty);
+ DeleteCriticalSection(&hcrtQLock);
+
+ return (npsz);
+} // writer
+
+
+/*+-------------------------------------------------------------------------+
+ | reader()
+ |
+ +-------------------------------------------------------------------------+*/
+DWORD reader( void ) {
+ BUF *fpbuf;
+ BOOL f = !LAST;
+
+ while (!fAbort && f != LAST) {
+ if ((fpbuf = AllocMemory(sizeof(BUF))) == NULL) {
+#ifdef DEBUG
+dprintf(TEXT("ERROR: MemoryAlloc error %ld\n"), GetLastError());
+#endif
+ return(0);
+ }
+
+ f = fpbuf->flag = NOTLAST;
+ if (!ReadFile(hfSrc, fpbuf->ach, BUFSIZE, &fpbuf->cbBuf, NULL) || fpbuf->cbBuf == 0) {
+ f = fpbuf->flag = LAST;
+ *(LPTSTR *)(fpbuf->ach) = NULL;
+ }
+
+ enqueue (fpbuf);
+ }
+
+ return(0);
+} // reader
+
+
+/*+-------------------------------------------------------------------------+
+ | dequeue()
+ |
+ +-------------------------------------------------------------------------+*/
+BUF *dequeue( void ) {
+ BUF *fpbuf;
+
+ while (TRUE) {
+ if (fpbufHead != NULL) {
+ EnterCriticalSection(&hcrtQLock);
+ fpbufHead = (fpbuf = fpbufHead)->fpbufNext;
+
+ if (fpbufTail == fpbuf)
+ fpbufTail = NULL;
+
+ LeaveCriticalSection(&hcrtQLock);
+ break;
+ }
+
+ // the head pointer is null so the list is empty. Block on eventsem
+ // until enqueue posts (ie. adds to queue)
+ WaitForSingleObject(hevQNotEmpty, (DWORD)-1);
+ }
+
+ return fpbuf;
+} // dequeue
+
+
+/*+-------------------------------------------------------------------------+
+ | enqueue()
+ |
+ +-------------------------------------------------------------------------+*/
+void enqueue( BUF *fpbuf ) {
+ fpbuf->fpbufNext = NULL;
+
+ EnterCriticalSection(&hcrtQLock);
+
+ if (fpbufTail == NULL)
+ fpbufHead = fpbuf;
+ else
+ fpbufTail->fpbufNext = fpbuf;
+
+ fpbufTail = fpbuf;
+ LeaveCriticalSection(&hcrtQLock);
+
+ SetEvent(hevQNotEmpty);
+} // enqueue
+
+
+/*+-------------------------------------------------------------------------+
+ | fastcopy()
+ |
+ | hfSrcParm file handle to read from
+ | hfDstParm file handle to write to
+ |
+ | returns NULL if successful
+ | pointer to error string otherwise
+ +-------------------------------------------------------------------------+*/
+TCHAR *fastcopy (HANDLE hfSrcParm, HANDLE hfDstParm) {
+ DWORD dwReader;
+
+ hfSrc = hfSrcParm;
+ hfDst = hfDstParm;
+
+
+ hevQNotEmpty = CreateEvent(NULL, (BOOL)FALSE, (BOOL)FALSE, NULL);
+ if (hevQNotEmpty == INVALID_HANDLE_VALUE)
+ return NULL;
+
+ InitializeCriticalSection(&hcrtQLock);
+
+ fAbort = FALSE;
+ hThread = CreateThread(0, STACKSIZE, (LPTHREAD_START_ROUTINE)reader, 0, 0, &dwReader);
+ if (hThread == INVALID_HANDLE_VALUE) {
+#ifdef DEBUG
+dprintf(TEXT("ERROR: Can't create thread - FileCopy\n"));
+#endif
+ return Lids(IDS_L_6);
+ }
+
+ return(writer());
+} // fastcopy
+
+
diff --git a/private/nw/convert/nwconv/fcopy.c b/private/nw/convert/nwconv/fcopy.c
new file mode 100644
index 000000000..6046ebad6
--- /dev/null
+++ b/private/nw/convert/nwconv/fcopy.c
@@ -0,0 +1,1162 @@
+/*++
+
+Copyright (c) 1993-1995 Microsoft Corporation
+
+Module Name:
+
+ FCopy.c
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) 16-Jun-1994
+
+Revision History:
+
+--*/
+
+
+#include "globals.h"
+
+#include <limits.h>
+
+#include "nwconv.h"
+#include "convapi.h"
+#include "ntnetapi.h"
+#include "nwnetapi.h"
+#include "userdlg.h"
+#include "statbox.h"
+#include "filedlg.h"
+
+//
+// Defines used in CopyNode routine - used for figuring out if we are doing
+// the home-directories in the MAIL sub-dir of the SYS vol.
+//
+#define DIR_TYPE_NORMAL 0
+#define DIR_TYPE_MAIL 1
+#define DIR_TYPE_LOGIN 2
+
+static TCHAR SourcePath[MAX_UNC_PATH];
+static LPTSTR spPtr;
+static FILE_OPTIONS *FileOptions = NULL;
+static CONVERT_OPTIONS *CurrentConvertOptions = NULL;
+static ULONG Count;
+static ULONG ServShareLen = 0;
+
+static USER_LIST *Users;
+static ULONG UserCount;
+static GROUP_LIST *Groups;
+static ULONG GroupCount;
+static BOOL IsNTFSDrive;
+
+static PSECURITY_DESCRIPTOR pSD = NULL;
+static PACL pACLNew = NULL;
+static PSID pSID = NULL;
+static ULONG CurSizeTotal;
+static ULONG CurNumFiles;
+static ULONG TotalSizeTotal;
+static BOOL SysRoot = FALSE;
+static BOOL SysVol = FALSE;
+
+extern UINT TotFiles;
+extern TCHAR UserServerName[];
+
+#define NWRIGHTSALL 0xFF
+
+#define BASE_16 16
+
+#define SWAPWORD(w) ((WORD)((w & 0xFF) << 8)|(WORD)(w >> 8))
+#define SWAPLONG(l) MAKELONG(SWAPWORD(HIWORD(l)),SWAPWORD(LOWORD(l)))
+
+
+TCHAR *fastcopy( HANDLE hfSrcParm, HANDLE hfDstParm );
+
+USER_BUFFER *FindUserMatch(LPTSTR Name, USER_LIST *UserList, BOOL NewName);
+GROUP_BUFFER *FindGroupMatch(LPTSTR Name, GROUP_LIST *GroupList, BOOL NewName);
+BOOL NTFile_AccessRightsAdd(LPTSTR ServerName, LPTSTR pUserName, LPTSTR pFileName, ULONG Rights, BOOL Dir);
+VOID ErrorIt(LPTSTR szFormat, ...);
+
+TCHAR SrcPath[MAX_UNC_PATH]; // +3 for slashes
+TCHAR DestPath[MAX_UNC_PATH]; // +3 for slashes
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ConvertFilesInit(
+ HWND hDlg
+ )
+
+/*++
+
+Routine Description:
+
+ Initialization routine called before doing the file copying. Sets up
+ the information panel dialog and fills in the directory tree structures.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ static TCHAR NewPath[MAX_UNC_PATH];
+ SOURCE_SERVER_BUFFER *SServ;
+ DEST_SERVER_BUFFER *DServ;
+ SHARE_LIST *ShareList;
+ SHARE_BUFFER *SList;
+ SHARE_BUFFER *CurrentShare;
+ DRIVE_BUFFER *Drive;
+ VIRTUAL_SHARE_BUFFER *VShare;
+ ULONG i;
+
+ // Just to be safe init this.
+ FillDirInit();
+ TotFiles = 0;
+ TotalSizeTotal = 0;
+
+ // Clear out old alloc space calculations
+ DServListSpaceFree();
+
+ CurrentConvertList = ConvertListStart;
+ while (CurrentConvertList) {
+ SServ = CurrentConvertList->SourceServ;
+ DServ = CurrentConvertList->FileServ;
+ ShareList = SServ->ShareList;
+
+ FileOptions = (FILE_OPTIONS *) CurrentConvertList->FileOptions;
+ if (FileOptions->TransferFileInfo) {
+ if (ShareList) {
+ SList = ShareList->SList;
+ // First expand all the file trees
+ for (i = 0; i < ShareList->Count; i++) {
+ CurrentShare = &SList[i];
+ if (CurrentShare->Convert) {
+ Panel_Line(1, Lids(IDS_D_1));
+ Panel_Line(6, TEXT("%s\\%s:"), SServ->Name, CurrentShare->Name);
+ Panel_Line(2, Lids(IDS_D_2));
+ Panel_Line(3, Lids(IDS_D_3));
+ Panel_Line(4, Lids(IDS_D_4));
+ wsprintf(NewPath, TEXT("\\\\%s\\%s\\"), SServ->Name, CurrentShare->Name);
+
+ if (CurrentShare->Root == NULL)
+ TreeRootInit(CurrentShare, NewPath);
+
+ TreeFillRecurse(1, NewPath, CurrentShare->Root);
+
+ // Now increment allocated space on dest drive
+ if (CurrentShare->DestShare != NULL)
+ if (CurrentShare->Virtual) {
+ VShare = (VIRTUAL_SHARE_BUFFER *) CurrentShare->DestShare;
+ Drive = VShare->Drive;
+
+ if (Drive != NULL)
+ Drive->AllocSpace += TotalFileSizeGet();
+
+ } else {
+ Drive = CurrentShare->DestShare->Drive;
+ if (Drive != NULL)
+ Drive->AllocSpace += TotalFileSizeGet();
+ }
+
+ }
+ } // expand the file trees...
+ }
+
+ } // if transfer files
+
+ CurrentConvertList = CurrentConvertList->next;
+ } // loop through servers
+
+} // ConvertFilesInit
+
+
+/////////////////////////////////////////////////////////////////////////
+PSECURITY_DESCRIPTOR
+SecurityDescriptorCreate(
+ LPTSTR ServerName
+ )
+
+/*++
+
+Routine Description:
+
+ Creates a security descriptor.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ DWORD cbACL = 1024;
+ DWORD cbSID = 1024;
+ LPTSTR lpszAccount;
+ TCHAR lpszDomain[80];
+ DWORD cchDomainName = 80;
+ UCHAR psnuType[1024];
+ ACCESS_ALLOWED_ACE *pAAAce;
+
+ lpszAccount = Lids(IDS_S_1);
+
+ // Initialize a new security descriptor.
+ pSD = (PSECURITY_DESCRIPTOR) AllocMemory(SECURITY_DESCRIPTOR_MIN_LENGTH);
+ if (pSD == NULL)
+ return NULL;
+
+ if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) {
+ FreeMemory(pSD);
+ return NULL;
+ }
+
+ // Initialize a new ACL.
+ pACLNew = (PACL) AllocMemory(cbACL);
+ if (pACLNew == NULL) {
+ goto Cleanup;
+ }
+
+ if (!InitializeAcl(pACLNew, cbACL, ACL_REVISION2)) {
+ goto Cleanup;
+ }
+
+ // Retrieve the SID for UserABC.
+ pSID = (PSID) AllocMemory(cbSID);
+ if (pSID == NULL) {
+ goto Cleanup;
+ }
+
+ if (!LookupAccountName(ServerName, lpszAccount, pSID, &cbSID,
+ lpszDomain, &cchDomainName, (PSID_NAME_USE) psnuType)) {
+ goto Cleanup;
+ }
+
+ // Set access permissions
+ if (!AddAccessAllowedAce(pACLNew, ACL_REVISION2, GENERIC_ALL, pSID)) {
+ goto Cleanup;
+ }
+
+ if (!GetAce(pACLNew, 0, (LPVOID *) &pAAAce))
+ goto Cleanup;
+
+ pAAAce->Header.AceFlags |= CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE;
+ pAAAce->Mask = GENERIC_ALL;
+
+ // Add a new ACL to the security descriptor.
+ if (!SetSecurityDescriptorDacl(pSD, TRUE, pACLNew, FALSE)) {
+ goto Cleanup;
+ }
+
+ return pSD;
+
+Cleanup:
+
+ if (pSID != NULL)
+ FreeSid(pSID);
+
+ if(pSD != NULL)
+ FreeMemory(pSD);
+
+ if(pACLNew != NULL)
+ FreeMemory(pACLNew);
+
+ return NULL;
+
+} // SecurityDescriptorCreate
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+MakeDir (
+ DEST_SERVER_BUFFER *DServ,
+ VIRTUAL_SHARE_BUFFER *VShare
+ )
+
+/*++
+
+Routine Description:
+
+ Given a path, this will start at the root of the path and create a
+ directory tree up to the ending node.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ static TCHAR NewPath[MAX_UNC_PATH];
+ TCHAR oc;
+ LPTSTR ptr;
+ TCHAR ServerName[MAX_SERVER_NAME_LEN + 3];
+ SECURITY_ATTRIBUTES sa;
+
+ // First need to construct a root path in the correct form
+ wsprintf(NewPath, TEXT("\\\\%s\\%s"), DServ->Name, VShare->Path);
+
+ ptr = NewPath;
+ if (*ptr == TEXT('\0'))
+ return;
+
+ // Look for ":" and change to the "$"
+ while (*ptr && *ptr != TEXT(':'))
+ ptr++;
+
+ if (*ptr == TEXT(':'))
+ *ptr = TEXT('$');
+ else
+ return;
+
+ // Go to initial backslash (one right after drive designator)
+ while (*ptr && *ptr != TEXT('\\'))
+ ptr++;
+
+ // We are pointing at the first char of the path - now loop through
+ // the path - looking for each backslash and make each sub-dir
+ // individually.
+ while (*ptr) {
+ // skip over backslash we are on
+ ptr++;
+
+ while (*ptr && *ptr != TEXT('\\'))
+ ptr++;
+
+ // sitting on next backslash - truncate path and make the dir
+ oc = *ptr;
+ *ptr = TEXT('\0');
+
+ wsprintf(ServerName, TEXT("\\\\%s"), DServ->Name);
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa.lpSecurityDescriptor = SecurityDescriptorCreate(ServerName);
+ sa.bInheritHandle = TRUE;
+
+ CreateDirectory(NewPath, &sa);
+
+ // Now cleanup the allocated security stuff
+ if (pSID != NULL)
+ FreeSid(pSID);
+
+ if(pSD != NULL)
+ FreeMemory(pSD);
+
+ if(pACLNew != NULL)
+ FreeMemory(pACLNew);
+
+ *ptr = oc;
+ }
+
+} // MakeDir
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+VSharesCreate(
+ DEST_SERVER_BUFFER *DServ,
+ BOOL TConversion
+ )
+
+/*++
+
+Routine Description:
+
+ Given a virtual share struct, creates the share on the destination
+ server, include both an NT share and FPNW share if applicable. Will
+ also create any directories to point the share at if needed.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ CONVERT_OPTIONS *cvo;
+ VIRTUAL_SHARE_BUFFER *VShare;
+ BOOL FPNWChk;
+
+ LogWriteLog(0, Lids(IDS_L_7));
+ VShare = CurrentConvertList->FileServ->VShareStart;
+ cvo = (CONVERT_OPTIONS *) CurrentConvertList->ConvertOptions;
+
+ FPNWChk = DServ->IsFPNW;
+
+ while (VShare) {
+ if (VShare->UseCount > 0) {
+ LogWriteLog(1, TEXT("%s \r\n"), VShare->Name);
+ LogWriteLog(2, Lids(IDS_L_8), VShare->Path);
+
+ if (!TConversion) {
+ MakeDir(DServ, VShare);
+ if ((cvo->NetWareInfo) && FPNWChk)
+ FPNWShareAdd(VShare->Name, VShare->Path);
+
+ NTShareAdd(VShare->Name, VShare->Path);
+ }
+
+ }
+
+ VShare = VShare->next;
+ }
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+
+} // VSharesCreate
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+FileSecurityTransfer(
+ LPTSTR SrcPath,
+ LPTSTR DestPath,
+ BOOL TConversion,
+ BOOL Dir
+ )
+
+/*++
+
+Routine Description:
+
+ Given a source and destination path, will take all the file permissions
+ from the source and apply them to the destination. Will automatically
+ convert any user names to their new equivalence.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ BOOL match;
+ LPTSTR fnPtr;
+ USER_RIGHTS_LIST *secUsers = NULL;
+ ULONG secUserCount;
+ ULONG i;
+ USER_BUFFER *FoundUser;
+ GROUP_BUFFER *FoundGroup;
+ LPTSTR NewName;
+ ACCESS_MASK AccessMask;
+ NTSTATUS ntstatus;
+ BOOL DidEveryone = FALSE;
+
+ fnPtr = &SrcPath[ServShareLen];
+
+ lstrcat(SourcePath, fnPtr);
+ ErrorItemSet(Lids(IDS_L_9), SourcePath);
+
+#ifdef DEBUG
+dprintf(TEXT("Getting Rights for: %s\n"), SourcePath);
+#endif
+ if (!NWFileRightsEnum(SourcePath, &secUsers, &secUserCount, (CurrentConvertList->SourceServ->VerMaj < 3))) {
+
+ if (VerboseFileLogging() && (secUserCount > 0))
+ if (Dir)
+ LogWriteLog(2, Lids(IDS_L_10));
+ else
+ LogWriteLog(3, Lids(IDS_L_10));
+
+ for (i = 0; i < secUserCount; i++) {
+#ifdef DEBUG
+ dprintf(TEXT("%s %s\n"), NWRightsLog(secUsers[i].Rights), secUsers[i].Name);
+#endif
+
+ match = FALSE;
+ FoundUser = FindUserMatch(secUsers[i].Name, Users, FALSE);
+
+ // Check if this is "EVERYONE"
+ if (!lstrcmpi(secUsers[i].Name, Lids(IDS_S_31)))
+ DidEveryone = TRUE;
+
+ if (FoundUser == NULL) {
+ FoundGroup = FindGroupMatch(secUsers[i].Name, Groups, FALSE);
+ if (FoundGroup != NULL) {
+ match = TRUE;
+ NewName = FoundGroup->NewName;
+
+ }
+ } else {
+ match = TRUE;
+ NewName = FoundUser->NewName;
+ }
+
+ if (!match)
+ NewName = NWSpecialNamesMap(secUsers[i].Name);
+
+ // Map the NW rights to NT access mask
+ AccessMask = 0x0;
+
+ if (Dir)
+ ntstatus = MapNwRightsToNTAccess(secUsers[i].Rights, &DirRightsMapping, &AccessMask);
+ else
+ ntstatus = MapNwRightsToNTAccess(secUsers[i].Rights, &FileRightsMapping, &AccessMask);
+
+ if (VerboseFileLogging())
+ if (Dir)
+ LogWriteLog(3, TEXT("%s %-20s -> %-20s %s\r\n"), NWRightsLog(secUsers[i].Rights), secUsers[i].Name, NewName, NTAccessLog(AccessMask));
+ else
+ LogWriteLog(4, TEXT("%s %-20s -> %-20s %s\r\n"), NWRightsLog(secUsers[i].Rights), secUsers[i].Name, NewName, NTAccessLog(AccessMask));
+
+ if (NT_SUCCESS(ntstatus)) {
+#ifdef DEBUG
+dprintf(TEXT("Server: %s\n"), UserServerName);
+#endif
+ if (!TConversion)
+ NTFile_AccessRightsAdd(UserServerName, NewName, DestPath, AccessMask, Dir);
+ }
+#ifdef DEBUG
+ else
+ dprintf(TEXT("NwAddRight: MapNwRightsToNTAccess failed\n"));
+#endif
+ }
+
+ FreeMemory(secUsers);
+ }
+
+ //
+ // If this is the root of the sys vol, and the rights for Everyone weren't
+ // transferred, then Give everyone access. NT and NetWare handle
+ // permissions a bit differently. In NW if a user has permission
+ // nested down in a sub-dir, then NW will back-port S permission down
+ // to the root so the user can get access into the sub-dir. NT does
+ // access from the root up. Giving the user RX access provides a
+ // workaround.
+ //
+ if (SysRoot && !DidEveryone) {
+ // Use "Domain Users" for the user - equiv of everyone.
+ NewName = Lids(IDS_S_33);
+ // Map the NW rights to NT access mask
+ AccessMask = 0x0;
+
+ ntstatus = MapNwRightsToNTAccess(NW_FILE_READ, &DirRightsMapping, &AccessMask);
+
+ if (VerboseFileLogging())
+ LogWriteLog(3, TEXT("%s %-20s -> %-20s %s\r\n"), NWRightsLog(NW_FILE_READ), Lids(IDS_S_31), NewName, NTAccessLog(AccessMask));
+
+ if (NT_SUCCESS(ntstatus) && !TConversion)
+ NTFile_AccessRightsAdd(UserServerName, NewName, DestPath, AccessMask, Dir);
+ }
+
+ // re-truncate share name
+ *spPtr = TEXT('\0');
+
+} // FileSecurityTransfer
+
+
+/////////////////////////////////////////////////////////////////////////
+LPTSTR
+fcopy (
+ LPTSTR src,
+ LPTSTR dst
+ )
+
+/*++
+
+Routine Description:
+
+ fcopy (source file, destination file) copies the source to the destination
+ preserving attributes and filetimes. Returns NULL if OK or a char pointer
+ to the corresponding text of the error
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ static TCHAR fcopyErrorText[128];
+ HANDLE srcfh = INVALID_HANDLE_VALUE;
+ HANDLE dstfh = INVALID_HANDLE_VALUE;
+ LPTSTR result = NULL;
+ DWORD attribs;
+ FILETIME CreationTime, LastAccessTime, LastWriteTime;
+
+ attribs = GetFileAttributes(src);
+ if (attribs == FILE_ATTRIBUTE_DIRECTORY) {
+ result = Lids(IDS_L_11);
+ goto done;
+ }
+
+ if( ( srcfh = CreateFile( src, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ) ) == (HANDLE)-1 ) {
+ wsprintf( fcopyErrorText, Lids(IDS_L_12), GetLastError() );
+ result = fcopyErrorText;
+ goto done;
+ }
+
+ if (!GetFileTime(srcfh, &CreationTime, &LastAccessTime, &LastWriteTime)) {
+ result = Lids(IDS_L_13);
+ goto done;
+ }
+
+ if( ( dstfh = CreateFile( dst, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, srcfh ) ) == INVALID_HANDLE_VALUE) {
+ wsprintf( fcopyErrorText, Lids(IDS_L_14), GetLastError() );
+ result = fcopyErrorText;
+ goto done;
+ }
+
+ result = fastcopy( srcfh, dstfh );
+
+ if( result != NULL ) {
+ if (dstfh != INVALID_HANDLE_VALUE) {
+ CloseHandle( dstfh );
+ dstfh = INVALID_HANDLE_VALUE;
+ }
+
+ DeleteFile( dst );
+ goto done;
+ }
+
+ if (!SetFileTime(dstfh, &CreationTime, &LastAccessTime, &LastWriteTime)) {
+ result = Lids(IDS_L_15);
+ goto done;
+ }
+
+ if (attribs != 0xFFFFFFFF)
+ if (!SetFileAttributes(dst, attribs)) {
+ result = Lids(IDS_L_16);
+ goto done;
+ }
+
+done:
+ if (srcfh != INVALID_HANDLE_VALUE)
+ CloseHandle( srcfh );
+
+ if (dstfh != INVALID_HANDLE_VALUE)
+ CloseHandle( dstfh );
+
+ return result;
+} // fcopy
+
+
+/////////////////////////////////////////////////////////////////////////
+BOOL
+LoginDirConvert(
+ LPTSTR OldName,
+ LPTSTR NewName
+ )
+
+/*++
+
+Routine Description:
+
+ We need to copy login scripts, these reside in the mail directory on
+ the NetWare server. Each user's OBJECT_ID is a sub-directory. What
+ needs to be done is scan the sub-directories and convert the
+ OBJECT_ID for each user to the corresponding new OBJECT_ID on the
+ NT Server and rename the sub-dir to this new OBJECT_ID value. Then
+ copy the files.
+
+ The copy of the files and saving of the directory is done in
+ CopyNode, this routine converts the OBJECT-ID's.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ DWORD OldID, NewID;
+ USER_BUFFER *FoundUser;
+ char aOldName[MAX_PATH + 1];
+ TCHAR OldUserName[MAX_PATH + 1];
+ LPTSTR NewUserName;
+ BOOL ret = TRUE;
+
+ //
+ // Need to take several conversion steps. We are passed in a string
+ // representation of the OBJECT-ID. This needs to be changed into
+ // the new string represenation of the new OBJECT-ID as follows:
+ //
+ // 1. Str-OBJECTID -> OBJECT-ID (DWORD)
+ // 2. OBJECT-ID -> Name (User Name on NW server)
+ // 3. Name -> New Name (on NT Server)
+ // 4. New Name -> OBJECT-ID (DWORD)
+ // 5. OBJECT-ID -> Str-OBJECTID
+ //
+
+ //
+ // Init just in case
+ //
+ lstrcpy(NewName, TEXT(""));
+ strcpy(aOldName, "");
+ OldID = NewID = 0;
+
+ //
+ // 1. Str-OBJECTID -> OBJECT-ID (DWORD)
+ //
+ WideCharToMultiByte(CP_ACP, 0, OldName, -1, aOldName, sizeof(aOldName), NULL, NULL);
+ RtlCharToInteger(aOldName, BASE_16, &OldID);
+ SWAPWORDS(OldID);
+
+ //
+ // If we didn't convert it, or not an Object ID, then use original string
+ //
+ if (OldID == 0) {
+ lstrcpy(NewName, OldName);
+ ret = FALSE;
+ goto LoginDirConvertExit;
+ }
+
+ //
+ // 2. OBJECT-ID -> Name (User Name on NW server)
+ //
+ if (!NWObjectNameGet(OldID, OldUserName)) {
+ lstrcpy(NewName, OldName);
+ ret = FALSE;
+ goto LoginDirConvertExit;
+ }
+
+ //
+ // 3. Name -> New Name (on NT Server)
+ //
+ FoundUser = FindUserMatch(OldUserName, Users, FALSE);
+ NewUserName = OldUserName;
+
+ if (FoundUser != NULL)
+ NewUserName = FoundUser->NewName;
+
+ //
+ // 4. New Name -> OBJECT-ID (DWORD)
+ //
+ NewID = NTObjectIDGet(NewUserName);
+
+ if (NewID == 0) {
+ lstrcpy(NewName, OldName);
+ ret = FALSE;
+ goto LoginDirConvertExit;
+ }
+
+ //
+ // 5. OBJECT-ID -> Str-OBJECTID
+ //
+ wsprintf(NewName, TEXT("%lX"), MAKELONG(HIWORD(NewID),SWAPWORD(LOWORD(NewID))) );
+
+LoginDirConvertExit:
+#ifdef DEBUG
+ if (ret)
+ dprintf(TEXT("Converting Login Dir for [%s]: %s -> %s\n"), OldUserName, OldName, NewName);
+#endif
+
+ return ret;
+
+} // LoginDirConvert
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+CopyNode(
+ DIR_BUFFER *Dir,
+ BOOL First,
+ BOOL TConversion,
+ DWORD DirType
+ )
+
+/*++
+
+Routine Description:
+
+ A node in this case is a sub-directory. This is a recursive function that
+ will copy all files and sub-directories under a given sub-directory.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ LPTSTR ErrText;
+ DIR_LIST *DirList = NULL;
+ FILE_LIST *FileList = NULL;
+ DIR_BUFFER *DList;
+ FILE_BUFFER *FList;
+ LPTSTR pSrcPath, pDestPath;
+ TCHAR Attributes[10];
+ ULONG i;
+ DWORD ChildDirType;
+ DWORD attribs;
+
+ if (Dir == NULL)
+ return;
+
+ if (!Dir->Convert)
+ return;
+
+ SysRoot = FALSE;
+
+ // 1. Make dir if need be.
+ // 2. Copy all files in this dir
+ // 3. For each sub-dir recurse into this function, building up the path
+
+ //
+ // 1. Make Dir
+ //
+ if (!First) {
+ lstrcat(SrcPath, Dir->Name);
+
+ //
+ // If a HOME directory, then we need to convert the name to the new
+ // USER-ID
+ //
+ if (DirType == DIR_TYPE_LOGIN) {
+ TCHAR NewDirName[MAX_PATH + 1];
+
+ if (!LoginDirConvert(Dir->Name, NewDirName))
+ return;
+
+ lstrcat(DestPath, NewDirName);
+ } else
+ lstrcat(DestPath, Dir->Name);
+
+ if (!TConversion) {
+ attribs = GetFileAttributes(SrcPath);
+ CreateDirectory(DestPath, NULL);
+
+ if (attribs != 0xFFFFFFFF)
+ SetFileAttributes(DestPath, attribs);
+ }
+
+ } else {
+ lstrcat(DestPath, TEXT("\\"));
+
+ // Check if this is the root of the sys dir (for special security transfer).
+ if (SysVol)
+ SysRoot = TRUE;
+ }
+
+ if (VerboseFileLogging()) {
+ LogWriteLog(0, Lids(IDS_CRLF));
+ LogWriteLog(2, Lids(IDS_L_17), SrcPath);
+ LogWriteLog(2, Lids(IDS_L_18), DestPath);
+ }
+
+ if (IsNTFSDrive)
+ FileSecurityTransfer(SrcPath, DestPath, TConversion, TRUE);
+
+ // No need for this anymore
+ SysRoot = FALSE;
+
+ // Fixup and remember our path
+ lstrcat(SrcPath, TEXT("\\"));
+
+ if (!First)
+ lstrcat(DestPath, TEXT("\\"));
+
+ Status_ItemLabel(Lids(IDS_L_19), NicePath(50, SrcPath));
+
+ // Remember where end of source and dest paths are - so we don't have to
+ // store them on the stack all the time
+ pSrcPath = SrcPath;
+ while (*pSrcPath)
+ pSrcPath++;
+
+ pDestPath = DestPath;
+ while (*pDestPath)
+ pDestPath++;
+
+ Status_CurNum((UINT) Count+1);
+
+ //
+ // 2. Copy All Files in this dir
+ //
+ FileList = Dir->FileList;
+ if (FileList) {
+
+ if (FileList->Count > 0)
+ LogWriteLog(2, Lids(IDS_L_20));
+
+ FList = FileList->FileBuffer;
+ for (i = 0; i < FileList->Count; i++)
+ if (FList[i].Convert) {
+ ErrText = NULL;
+ lstrcat(SrcPath, FList[i].Name);
+ lstrcat(DestPath, FList[i].Name);
+ Status_CurNum((UINT) Count+1);
+#ifdef DEBUG
+dprintf(TEXT("FC: %s -> %s\n"), SrcPath, DestPath);
+#endif
+ ErrorItemSet(Lids(IDS_L_19), SrcPath);
+ Status_Item(FList[i].Name);
+ TotFiles++;
+ CurNumFiles++;
+ Status_TotFiles(TotFiles);
+ Count++;
+
+ Status_TotBytes(lToStr(FList[i].Size));
+
+ if (!TConversion) {
+ ErrText = fcopy(SrcPath, DestPath);
+
+ if (IsNTFSDrive)
+ FileSecurityTransfer(SrcPath, DestPath, TConversion, FALSE);
+ }
+
+ if (VerboseFileLogging()) {
+ lstrcpy(Attributes, Lids(IDS_L_21));
+ if (!(FList[i].Attributes & FILE_ATTRIBUTE_READONLY))
+ Attributes[1] = TEXT(' ');
+
+ if (!(FList[i].Attributes & FILE_ATTRIBUTE_ARCHIVE))
+ Attributes[2] = TEXT(' ');
+
+ if (!(FList[i].Attributes & FILE_ATTRIBUTE_HIDDEN))
+ Attributes[3] = TEXT(' ');
+
+ if (!(FList[i].Attributes & FILE_ATTRIBUTE_SYSTEM))
+ Attributes[4] = TEXT(' ');
+
+ LogWriteLog(3, TEXT("%13s %s %s\r\n"), lToStr(FList[i].Size), Attributes, FList[i].Name);
+ }
+
+ if (ErrText != NULL) {
+ if (!VerboseFileLogging())
+ LogWriteLog(3, Lids(IDS_L_22), SrcPath, DestPath);
+
+ LogWriteLog(4, TEXT("%s\r\n"), ErrText);
+ ErrorIt(TEXT("%s\r\n"), ErrText);
+ } else {
+ CurSizeTotal += FList[i].Size;
+ TotalSizeTotal += FList[i].Size;
+ }
+
+ // reset our paths to the right place
+ *pSrcPath = TEXT('\0');
+ *pDestPath = TEXT('\0');
+ }
+
+ }
+
+ //
+ // 3. Recurse the sub-dirs
+ //
+ DirList = Dir->DirList;
+ if (DirList) {
+ DList = DirList->DirBuffer;
+ for (i = 0; i < DirList->Count; i++)
+ if (DList[i].Convert) {
+
+ //
+ // Reset child dir type...
+ //
+ ChildDirType = DIR_TYPE_NORMAL;
+
+ //
+ // If this is the mail sub-dir, then the children are home-dirs
+ //
+ if (DirType == DIR_TYPE_MAIL)
+ ChildDirType = DIR_TYPE_LOGIN;
+
+ // recurse into this dir - check if this is the mail dir
+ if (SysVol && First && !lstrcmpi(DList[i].Name, TEXT("MAIL")) && CurrentConvertList->FileServ->IsFPNW )
+ ChildDirType = DIR_TYPE_MAIL;
+
+ CopyNode(&DList[i], FALSE, TConversion, ChildDirType);
+
+ // reset our paths to the right place
+ *pSrcPath = TEXT('\0');
+ *pDestPath = TEXT('\0');
+ }
+ } // Recursing Sub-dirs
+
+} // CopyNode
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ConvertFiles(
+ HWND hDlg,
+ BOOL TConversion,
+ USER_LIST *iUsers,
+ GROUP_LIST *iGroups
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ DIR_LIST *DirList = NULL;
+ SOURCE_SERVER_BUFFER *SServ;
+ DEST_SERVER_BUFFER *DServ;
+ VIRTUAL_SHARE_BUFFER *VShare;
+ SHARE_LIST *ShareList;
+ SHARE_BUFFER *SList;
+ SHARE_BUFFER *CurrentShare;
+ ULONG i;
+
+ Users = iUsers;
+ Groups = iGroups;
+
+ if (iUsers != NULL)
+ UserCount = iUsers->Count;
+ else
+ UserCount = 0;
+
+ if (iGroups != NULL)
+ GroupCount = iGroups->Count;
+ else
+ GroupCount = 0;
+
+ Count = 0;
+ FileOptions = (FILE_OPTIONS *) CurrentConvertList->FileOptions;
+ CurrentConvertOptions = (CONVERT_OPTIONS *) CurrentConvertList->ConvertOptions;
+ SServ = CurrentConvertList->SourceServ;
+ DServ = CurrentConvertList->FileServ;
+
+ // Synchronize the domain
+ NTDomainSynch(DServ);
+
+ // Following steps are taken:
+ // 1. Enumerate / create all virtual shares
+ // 2. Enumerate volumes and destinations to convert
+ // 3. Go to each volume - copy that tree
+
+ ShareList = SServ->ShareList;
+
+ if (VerboseFileLogging()) {
+ LogWriteLog(0, Lids(IDS_LINE));
+ LogWriteLog(0, Lids(IDS_BRACE), Lids(IDS_L_23));
+ LogWriteLog(0, Lids(IDS_BRACE), Lids(IDS_L_24));
+ LogWriteLog(0, Lids(IDS_BRACE), Lids(IDS_L_25));
+ LogWriteLog(0, Lids(IDS_BRACE), Lids(IDS_L_26));
+ LogWriteLog(0, Lids(IDS_BRACE), Lids(IDS_L_27));
+ LogWriteLog(0, Lids(IDS_BRACE), Lids(IDS_L_28));
+ LogWriteLog(0, Lids(IDS_BRACE), Lids(IDS_L_29));
+ LogWriteLog(0, Lids(IDS_BRACE), Lids(IDS_L_30));
+ LogWriteLog(0, Lids(IDS_BRACE), Lids(IDS_L_31));
+ LogWriteLog(0, Lids(IDS_BRACE), Lids(IDS_L_32));
+ LogWriteLog(0, Lids(IDS_BRACE), Lids(IDS_L_33));
+ LogWriteLog(0, Lids(IDS_BRACE), Lids(IDS_L_34));
+ LogWriteLog(0, Lids(IDS_BRACE), Lids(IDS_L_35));
+ LogWriteLog(0, Lids(IDS_BRACE), Lids(IDS_L_36));
+ LogWriteLog(0, Lids(IDS_BRACE), Lids(IDS_L_37));
+ LogWriteLog(0, Lids(IDS_BRACE), Lids(IDS_L_38));
+ LogWriteLog(0, Lids(IDS_BRACE), Lids(IDS_L_39));
+ LogWriteLog(0, Lids(IDS_BRACE), Lids(IDS_L_40));
+ LogWriteLog(0, Lids(IDS_BRACE), Lids(IDS_L_41));
+ LogWriteLog(0, Lids(IDS_BRACE), Lids(IDS_L_42));
+ LogWriteLog(0, Lids(IDS_BRACE), Lids(IDS_L_43));
+ LogWriteLog(0, Lids(IDS_BRACE), Lids(IDS_L_44));
+ LogWriteLog(0, Lids(IDS_BRACE), Lids(IDS_L_45));
+ LogWriteLog(0, Lids(IDS_BRACE), Lids(IDS_L_46));
+ LogWriteLog(0, Lids(IDS_BRACE), Lids(IDS_L_47));
+ LogWriteLog(0, Lids(IDS_BRACE), Lids(IDS_L_48));
+ LogWriteLog(0, Lids(IDS_BRACE), Lids(IDS_L_49));
+ LogWriteLog(0, Lids(IDS_LINE));
+ }
+
+ LogWriteLog(0, Lids(IDS_L_20));
+ Status_ConvTxt(Lids(IDS_L_50));
+ Status_ItemLabel(Lids(IDS_L_51));
+
+ if (ShareList) {
+ SList = ShareList->SList;
+
+ for (i = 0; i < ShareList->Count; i++) {
+ Count = 0;
+ CurrentShare = &SList[i];
+ if (CurrentShare->Root)
+ Status_CurTot((UINT) TreeCount(CurrentShare->Root));
+
+ if (CurrentShare->Convert) {
+ // Set root paths for this conversion
+ memset(SrcPath, 0, sizeof(SrcPath));
+ wsprintf(SrcPath, TEXT("\\\\%s\\%s"), SServ->Name, CurrentShare->Name);
+ ServShareLen = lstrlen(SrcPath) + 1;
+
+ // create sharename for access rights query in form of "SHARE:"
+ memset(SourcePath, 0, sizeof(SourcePath));
+ lstrcpy(SourcePath, CurrentShare->Name);
+ lstrcat(SourcePath, TEXT(":"));
+
+ // Check if this is the root of the sys dir (for special security transfer).
+ SysVol = FALSE;
+ if (!lstrcmpi(CurrentShare->Name, Lids(IDS_S_6)))
+ SysVol = TRUE;
+
+ // point spPtr to ending NULL so we can truncate it back
+ spPtr = &SourcePath[lstrlen(SourcePath)];
+ LogWriteSummary(0, Lids(IDS_CRLF));
+ LogWriteLog(0, Lids(IDS_CRLF));
+ LogWriteLog(1, Lids(IDS_L_52), CurrentShare->Name);
+ LogWriteSummary(1, Lids(IDS_L_52), CurrentShare->Name);
+
+ if (CurrentShare->Virtual) {
+ VShare = (VIRTUAL_SHARE_BUFFER *) CurrentShare->DestShare;
+ //
+ // NOTE: The DestShare->Name may be that of the ncp server so
+ // instead of formatting the destination \\server\share\<foo>,
+ // which means nothing to the smb server if the share is fpnw,
+ // we format the destination \\server\d$\path\<foo>.
+ //
+ wsprintf(DestPath, TEXT("\\\\%s\\%s"), DServ->Name, VShare->Path);
+ DestPath[2+lstrlen(DServ->Name)+1+1] = TEXT('$'); // replace ':' with '$' in unc path
+
+ if (VShare->Drive != NULL)
+ IsNTFSDrive = (VShare->Drive->Type == DRIVE_TYPE_NTFS);
+ else
+ IsNTFSDrive = FALSE;
+
+ LogWriteLog(1, Lids(IDS_L_53), VShare->Name);
+ LogWriteSummary(1, Lids(IDS_L_53), VShare->Name);
+ } else {
+ //
+ // NOTE: The DestShare->Name may be that of the ncp server so
+ // instead of formatting the destination \\server\share\<foo>,
+ // which means nothing to the smb server if the share is fpnw,
+ // we format the destination \\server\d$\path\<foo>.
+ //
+ wsprintf(DestPath, TEXT("\\\\%s\\%s"), DServ->Name, CurrentShare->DestShare->Path);
+ DestPath[2+lstrlen(DServ->Name)+1+1] = TEXT('$'); // replace ':' with '$' in unc path
+
+ if (CurrentShare->DestShare->Drive != NULL)
+ IsNTFSDrive = (CurrentShare->DestShare->Drive->Type == DRIVE_TYPE_NTFS);
+ else
+ IsNTFSDrive = FALSE;
+
+ LogWriteLog(1, Lids(IDS_L_53), CurrentShare->DestShare->Name);
+ LogWriteSummary(1, Lids(IDS_L_53), CurrentShare->DestShare->Name);
+ }
+
+ CurSizeTotal = 0;
+ CurNumFiles = 0;
+ CopyNode(CurrentShare->Root, TRUE, TConversion, (DWORD) DIR_TYPE_NORMAL);
+ LogWriteSummary(2, Lids(IDS_L_54), lToStr(CurNumFiles));
+ LogWriteSummary(2, Lids(IDS_L_55), lToStr(CurSizeTotal));
+
+ // Whack it down to minimum size to conserve memory
+ TreePrune(CurrentShare->Root);
+ }
+ }
+ }
+
+} // ConvertFiles
diff --git a/private/nw/convert/nwconv/filedlg.c b/private/nw/convert/nwconv/filedlg.c
new file mode 100644
index 000000000..567106a6b
--- /dev/null
+++ b/private/nw/convert/nwconv/filedlg.c
@@ -0,0 +1,1074 @@
+/*
+ +-------------------------------------------------------------------------+
+ | File Operations |
+ +-------------------------------------------------------------------------+
+ | (c) Copyright 1994 |
+ | Microsoft Corp. |
+ | All rights reserved |
+ | |
+ | Program : [FileDLG.c] |
+ | Programmer : Arthur Hanson |
+ | Original Program Date : [Feb 10, 1994] |
+ | Last Update : [Jun 16, 1994] |
+ | |
+ | Version: 1.00 |
+ | |
+ | Description: |
+ | |
+ | History: |
+ | arth Feb 10, 1994 1.00 Original Version. |
+ | |
+ +-------------------------------------------------------------------------+
+*/
+
+
+#include "globals.h"
+
+#include "convapi.h"
+#include "filedlg.h"
+#include "ntnetapi.h"
+#include "nwnetapi.h"
+#include "columnlb.h"
+
+static SOURCE_SERVER_BUFFER *SServ;
+static DEST_SERVER_BUFFER *DServ;
+
+static FILE_OPTIONS *FileOptions;
+static SHARE_LIST *ShareList;
+static SHARE_BUFFER *SList;
+static SHARE_BUFFER *CurrentShare;
+static SHARE_BUFFER *CurrentDShare;
+static int SelectType;
+static int NewShareType;
+
+#define SELECT_TYPE_MODIFY 1
+#define SELECT_TYPE_ADD 2
+
+static BOOL ConvertFiles = TRUE;
+
+void FileSelect_Do(HWND hDlg, SOURCE_SERVER_BUFFER *SourceServ, SHARE_BUFFER *CShare);
+BOOL MapShare(SHARE_BUFFER *Share, DEST_SERVER_BUFFER *DServ);
+
+
+/*+-------------------------------------------------------------------------+
+ | FileOptionsDefaultsSet()
+ |
+ +-------------------------------------------------------------------------+*/
+void FileOptionsDefaultsSet(void *tfo) {
+ FILE_OPTIONS *fo = tfo;
+
+ if (fo->TransferFileInfo)
+ ConvertFiles = TRUE;
+ else
+ ConvertFiles = FALSE;
+
+} // FileOptionsDefaultsSet
+
+
+/*+-------------------------------------------------------------------------+
+ | FileOptionsDefaultsReset()
+ |
+ +-------------------------------------------------------------------------+*/
+void FileOptionsDefaultsReset() {
+ ConvertFiles = TRUE;
+
+} // FileOptionsDefaultsReset
+
+
+/*+-------------------------------------------------------------------------+
+ | FileOptionsInit()
+ |
+ +-------------------------------------------------------------------------+*/
+void FileOptionsInit(void **lpfo) {
+ FILE_OPTIONS *fo;
+
+ fo = (FILE_OPTIONS *) *lpfo;
+
+ // if we need to allocate space, do so
+ if (fo == NULL)
+ fo = AllocMemory(sizeof(FILE_OPTIONS));
+
+ // make sure it was allocated
+ if (fo == NULL)
+ return;
+
+ memset(fo, 0, sizeof(FILE_OPTIONS));
+ fo->TransferFileInfo = ConvertFiles;
+ *lpfo = (void *) fo;
+
+} // FileOptionsInit
+
+
+/*+-------------------------------------------------------------------------+
+ | FileOptionsLoad()
+ |
+ +-------------------------------------------------------------------------+*/
+void FileOptionsLoad(HANDLE hFile, void **lpfo) {
+ FILE_OPTIONS *fo;
+ DWORD wrote;
+
+ fo = (FILE_OPTIONS *) *lpfo;
+
+ // if we need to allocate space, do so
+ if (fo == NULL)
+ fo = AllocMemory(sizeof(FILE_OPTIONS));
+
+ // make sure it was allocated
+ if (fo == NULL)
+ return;
+
+ ReadFile(hFile, fo, sizeof(FILE_OPTIONS), &wrote, NULL);
+ *lpfo = (void *) fo;
+
+} // FileOptionsLoad
+
+
+/*+-------------------------------------------------------------------------+
+ | FileOptionsSave()
+ |
+ +-------------------------------------------------------------------------+*/
+void FileOptionsSave(HANDLE hFile, void *fo) {
+ DWORD wrote;
+
+ WriteFile(hFile, fo, sizeof(FILE_OPTIONS), &wrote, NULL);
+
+} // FileOptionsSave
+
+
+/*+-------------------------------------------------------------------------+
+ | Share Modify/Create Dialog Routines |
+ +-------------------------------------------------------------------------+*/
+
+/*+-------------------------------------------------------------------------+
+ | ShareNewPathValidate()
+ |
+ +-------------------------------------------------------------------------+*/
+BOOL ShareNewPathValidate(HWND hWnd, LPTSTR Path, DRIVE_BUFFER **pDrive) {
+ VIRTUAL_SHARE_BUFFER *VShare;
+ DRIVE_BUFFER *DList;
+ ULONG i;
+ TCHAR Drive[2];
+
+ // must be long enough to hold drive, colon and path
+ if (lstrlen(Path) < 3)
+ goto ShareNewValidateFail;
+
+ if (Path[1] != TEXT(':'))
+ goto ShareNewValidateFail;
+
+ if (Path[2] != TEXT('\\'))
+ goto ShareNewValidateFail;
+
+ if (DServ->DriveList == NULL)
+ return FALSE;
+
+ // Scan drive list looking for match to share path
+ Drive[1] = TEXT('\0');
+ DList = DServ->DriveList->DList;
+ for (i = 0; i < DServ->DriveList->Count; i++) {
+ // Get first char from path - should be drive letter
+ Drive[0] = Path[0];
+ if (!lstrcmpi(Drive, DList[i].Drive)) {
+ // Found match
+ *pDrive = &DList[i];
+
+ if (NewShareType == SELECT_TYPE_MODIFY)
+ if (CurrentDShare->VFlag) {
+ VShare = (VIRTUAL_SHARE_BUFFER *) CurrentDShare;
+ VShare->Drive = &DList[i];
+ } else
+ CurrentDShare->Drive = &DList[i];
+
+ return TRUE;
+ }
+ }
+
+ShareNewValidateFail:
+ MessageBox(hWnd, Lids(IDS_E_3), Lids(IDS_E_2), MB_OK);
+ return FALSE;
+
+} // ShareNewPathValidate
+
+
+/*+-------------------------------------------------------------------------+
+ | ShareNewShareValidate()
+ |
+ +-------------------------------------------------------------------------+*/
+BOOL ShareNewShareValidate(HWND hWnd, LPTSTR ShareName) {
+ ULONG i;
+ VIRTUAL_SHARE_BUFFER *VShare;
+ SHARE_BUFFER *VList;
+
+ // Loop through share list seeing if the share already exists (same name)
+ if (DServ->ShareList != NULL) {
+ VList = DServ->ShareList->SList;
+
+ for (i = 0; i < DServ->ShareList->Count; i++)
+ if (!lstrcmpi(VList[i].Name, ShareName))
+ goto ShareNewShareVFail;
+
+ }
+
+ // Now do the same for the virtual share list
+ VShare = DServ->VShareStart;
+ while (VShare) {
+
+ if (!lstrcmpi(VShare->Name, ShareName))
+ goto ShareNewShareVFail;
+
+ VShare = VShare->next;
+ }
+
+ return TRUE;
+
+ShareNewShareVFail:
+ MessageBox(hWnd, Lids(IDS_E_4), Lids(IDS_E_2), MB_OK);
+ return FALSE;
+
+} // ShareNewShareValidate
+
+
+/*+-------------------------------------------------------------------------+
+ | NWShareNew()
+ |
+ +-------------------------------------------------------------------------+*/
+LRESULT CALLBACK NWShareNew(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
+ HWND hCtrl;
+ BOOL Enable;
+ int wmId, wmEvent;
+ TCHAR Path[MAX_PATH + 1];
+ TCHAR NewShare[MAX_SHARE_NAME_LEN + 1];
+ VIRTUAL_SHARE_BUFFER *VShare;
+ DRIVE_BUFFER *Drive;
+ BOOL ok;
+
+ switch (message) {
+ case WM_INITDIALOG:
+ // Center the dialog over the application window
+ CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
+
+ hCtrl = GetDlgItem(hDlg, IDC_EDIT1);
+ PostMessage(hCtrl, EM_LIMITTEXT, (WPARAM) MAX_SHARE_NAME_LEN, 0);
+ hCtrl = GetDlgItem(hDlg, IDC_EDIT2);
+ PostMessage(hCtrl, EM_LIMITTEXT, (WPARAM) MAX_PATH, 0);
+
+ if (NewShareType == SELECT_TYPE_MODIFY) {
+ SendMessage(hDlg, WM_SETTEXT, (WPARAM) 0, (LPARAM) Lids(IDS_D_5));
+ hCtrl = GetDlgItem(hDlg, IDC_EDIT1);
+ EnableWindow(hCtrl, FALSE);
+ ShowWindow(hCtrl, SW_HIDE);
+
+ hCtrl = GetDlgItem(hDlg, IDC_SHARENAME);
+ if (CurrentDShare->VFlag) {
+ VShare = (VIRTUAL_SHARE_BUFFER *) CurrentDShare;
+ SendMessage(hCtrl, WM_SETTEXT, (WPARAM) 0, (LPARAM) VShare->Name);
+ } else
+ SendMessage(hCtrl, WM_SETTEXT, (WPARAM) 0, (LPARAM) CurrentDShare->Name);
+
+ } else {
+ hCtrl = GetDlgItem(hDlg, IDC_SHARENAME);
+ EnableWindow(hCtrl, FALSE);
+ ShowWindow(hCtrl, SW_HIDE);
+
+ hCtrl = GetDlgItem(hDlg, IDOK);
+ EnableWindow(hCtrl, FALSE);
+ }
+
+ PostMessage(hDlg, WM_COMMAND, ID_INIT, 0L);
+ return (TRUE);
+
+ case WM_COMMAND:
+ wmId = LOWORD(wParam);
+ wmEvent = HIWORD(wParam);
+
+ switch (wmId) {
+
+ case IDOK:
+ ok = TRUE;
+
+ if (NewShareType == SELECT_TYPE_ADD) {
+ hCtrl = GetDlgItem(hDlg, IDC_EDIT1);
+ * (WORD *)NewShare = sizeof(NewShare);
+ SendMessage(hCtrl, EM_GETLINE, 0, (LPARAM) NewShare);
+
+ if (!ShareNewShareValidate(hDlg, NewShare))
+ ok = FALSE;
+ }
+
+ if (ok) {
+ hCtrl = GetDlgItem(hDlg, IDC_EDIT2);
+ * (WORD *)Path = sizeof(Path);
+ SendMessage(hCtrl, EM_GETLINE, 0, (LPARAM) Path);
+
+ if (!ShareNewPathValidate(hDlg, Path, &Drive))
+ ok = FALSE;
+ }
+
+ if (ok) {
+ if (NewShareType == SELECT_TYPE_ADD) {
+ // If we are in ADD - then we might have added a virtual
+ // share when we did the match, if so get rid of it...
+ if ((CurrentShare !=NULL) && (CurrentShare->DestShare != NULL))
+ if (CurrentShare->Virtual) {
+ VShare = (VIRTUAL_SHARE_BUFFER *) CurrentShare->DestShare;
+ VShareListDelete(DServ, VShare);
+ CurrentShare->DestShare = NULL;
+ }
+
+ // Got rid of old one, now need to create new one.
+ CurrentShare->Virtual = TRUE;
+ VShare = VShareListAdd(DServ, NewShare, Path);
+ VShare->Drive = Drive;
+ VShare->UseCount++;
+ CurrentShare->DestShare = (SHARE_BUFFER *) VShare;
+ CurrentDShare = (SHARE_BUFFER *) VShare;
+ } else
+ // Modify so update the values of the path/drive with
+ // the new stuff.
+ if ((CurrentShare !=NULL) && (CurrentShare->DestShare != NULL))
+ if (CurrentShare->Virtual) {
+ VShare = (VIRTUAL_SHARE_BUFFER *) CurrentShare->DestShare;
+ lstrcpy(VShare->Path, Path);
+ VShare->Drive = Drive;
+ }
+
+
+ EndDialog(hDlg, 0);
+ }
+
+ break;
+
+ case IDCANCEL:
+ EndDialog(hDlg, 0);
+ break;
+
+ case IDHELP:
+ if (NewShareType == SELECT_TYPE_MODIFY)
+ WinHelp(hDlg, HELP_FILE, HELP_CONTEXT, (DWORD) IDC_HELP_SHAREPROP);
+ else
+ WinHelp(hDlg, HELP_FILE, HELP_CONTEXT, (DWORD) IDC_HELP_SHARENEW);
+
+ break;
+
+ case ID_INIT:
+ // Modify should only be for a virtual share
+ if (NewShareType == SELECT_TYPE_MODIFY) {
+ hCtrl = GetDlgItem(hDlg, IDC_EDIT2);
+ if (CurrentDShare->VFlag) {
+ VShare = (VIRTUAL_SHARE_BUFFER *) CurrentDShare;
+ SendMessage(hCtrl, WM_SETTEXT, (WPARAM) 0, (LPARAM) VShare->Path);
+ }
+ }
+
+ case IDC_EDIT1:
+ case IDC_EDIT2:
+ if (wmEvent == EN_CHANGE) {
+ Enable = TRUE;
+ hCtrl = GetDlgItem(hDlg, IDC_EDIT1);
+
+ if (NewShareType == SELECT_TYPE_ADD)
+ if (!SendMessage(hCtrl, EM_LINELENGTH, 0, 0))
+ Enable = FALSE;
+
+ hCtrl = GetDlgItem(hDlg, IDC_EDIT2);
+ if (SendMessage(hCtrl, EM_LINELENGTH, 0, 0) < 3)
+ Enable = FALSE;
+
+ hCtrl = GetDlgItem(hDlg, IDOK);
+ EnableWindow(hCtrl, Enable);
+
+ }
+ break;
+
+ }
+ return TRUE;
+
+ }
+
+ return (FALSE); // Didn't process the message
+
+ lParam;
+} // NWShareNew
+
+
+/*+-------------------------------------------------------------------------+
+ | NWShareNew_Do()
+ |
+ +-------------------------------------------------------------------------+*/
+void NWShareNew_Do(HWND hDlg) {
+ DLGPROC lpfnDlg;
+
+ lpfnDlg = MakeProcInstance( (DLGPROC) NWShareNew, hInst);
+ DialogBox(hInst, TEXT("NWShareAdd"), hDlg, lpfnDlg) ;
+ FreeProcInstance(lpfnDlg);
+
+} // NWShareNew_Do
+
+
+/*+-------------------------------------------------------------------------+
+ | Add / Modify Share Selection Dialog Routines |
+ +-------------------------------------------------------------------------+*/
+
+/*+-------------------------------------------------------------------------+
+ | FixShare()
+ |
+ +-------------------------------------------------------------------------+*/
+void FixShare(LPTSTR OrigShare, LPTSTR ServName, LPTSTR DestShare) {
+ LPTSTR pShare = OrigShare;
+
+ lstrcpy(DestShare, TEXT(""));
+
+ // Assume it is in the form \\server\share
+ // Skip over leading double-back for server
+ if ((pShare[0] == '\\') && (pShare[1] == '\\'))
+ pShare+= 2;
+
+ // Now skip over the server name
+ while (*pShare && (*pShare != '\\'))
+ pShare++;
+
+ // pShare should point to the share-name, append this to the server-name
+ if (*ServName != '\\')
+ lstrcat(DestShare, TEXT("\\\\"));
+
+ lstrcat(DestShare, ServName);
+ lstrcat(DestShare, pShare);
+
+} // FixShare
+
+
+/*+-------------------------------------------------------------------------+
+ | NTShareListFill()
+ |
+ +-------------------------------------------------------------------------+*/
+void NTShareListFill(HWND hDlg) {
+ HWND hCtrl;
+ SHARE_LIST *ShareList = NULL;
+ SHARE_BUFFER *SList;
+ DWORD i, dwIndex;
+ BOOL Match = FALSE;
+ VIRTUAL_SHARE_BUFFER *VShare;
+
+ // Clear it out
+ hCtrl = GetDlgItem(hDlg, IDC_COMBO2);
+ SendMessage(hCtrl, CB_RESETCONTENT, 0, 0L);
+
+ // First enum all the regular shares
+ ShareList = DServ->ShareList;
+ if (ShareList != NULL) {
+ SList = ShareList->SList;
+
+ for (i = 0; i < ShareList->Count; i++) {
+ dwIndex = SendMessage(hCtrl, CB_ADDSTRING, (WPARAM) 0, (LPARAM) SList[i].Name);
+ SendMessage(hCtrl, CB_SETITEMDATA, (WPARAM) dwIndex, (LPARAM) &SList[i]);
+ }
+
+ }
+
+ // Now enum all the virtual shares
+ VShare = DServ->VShareStart;
+ while (VShare) {
+ dwIndex = SendMessage(hCtrl, CB_ADDSTRING, (WPARAM) 0, (LPARAM) VShare->Name);
+ SendMessage(hCtrl, CB_SETITEMDATA, (WPARAM) dwIndex, (LPARAM) VShare);
+ VShare = VShare->next;
+ }
+
+ // Match the combo-box to the given share
+ if (CurrentShare->DestShare != NULL)
+ if (CurrentShare->Virtual) {
+ VShare = (VIRTUAL_SHARE_BUFFER *) CurrentShare->DestShare;
+ SendMessage(hCtrl, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) VShare->Name);
+ } else
+ SendMessage(hCtrl, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) CurrentShare->DestShare->Name);
+
+} // NTShareListFill
+
+
+/*+-------------------------------------------------------------------------+
+ | NWShareSelect()
+ |
+ +-------------------------------------------------------------------------+*/
+LRESULT CALLBACK NWShareSelect(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
+ static TCHAR ServName[MAX_UNC_PATH+1];
+ VIRTUAL_SHARE_BUFFER *VShare;
+ SHARE_BUFFER *OrigShare = NULL;
+ SHARE_BUFFER *NewShare;
+ HWND hCtrl;
+ DWORD dwData, dwIndex;
+ int wmId, wmEvent;
+ ULONG i;
+
+ switch (message) {
+ case WM_INITDIALOG:
+ // Center the dialog over the application window
+ CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
+
+ if (SelectType == SELECT_TYPE_MODIFY) {
+ SendMessage(hDlg, WM_SETTEXT, (WPARAM) 0, (LPARAM) Lids(IDS_D_6));
+ // Disable Source combo box...
+ hCtrl = GetDlgItem(hDlg, IDC_COMBO1);
+ EnableWindow(hCtrl, FALSE);
+ ShowWindow(hCtrl, SW_HIDE);
+ OrigShare = CurrentShare->DestShare;
+ }
+
+ hCtrl = GetDlgItem(hDlg, IDC_FSERVER);
+ lstrcpy(ServName, Lids(IDS_D_7));
+ lstrcat(ServName, SServ->Name);
+ SendMessage(hCtrl, WM_SETTEXT, (WPARAM) 0, (LPARAM) ServName);
+
+ hCtrl = GetDlgItem(hDlg, IDC_TSERVER);
+ lstrcpy(ServName, Lids(IDS_D_8));
+ lstrcat(ServName, DServ->Name);
+ SendMessage(hCtrl, WM_SETTEXT, (WPARAM) 0, (LPARAM) ServName);
+
+ PostMessage(hDlg, WM_COMMAND, ID_INIT, 0L);
+
+ hCtrl = GetDlgItem(hDlg, IDC_EDIT1);
+ PostMessage(hCtrl, EM_LIMITTEXT, (WPARAM) MAX_PATH, 0);
+
+ if (SelectType == SELECT_TYPE_MODIFY)
+ SendMessage(hCtrl, WM_SETTEXT, (WPARAM) 0, (LPARAM) CurrentShare->SubDir);
+
+ return (TRUE);
+
+ case WM_COMMAND:
+ wmId = LOWORD(wParam);
+ wmEvent = HIWORD(wParam);
+
+ switch (wmId) {
+
+ case IDOK:
+ CurrentShare->Convert = TRUE; // only really needed for add
+
+ hCtrl = GetDlgItem(hDlg, IDC_COMBO2);
+ dwIndex = SendMessage(hCtrl, CB_GETCURSEL, 0, 0L);
+
+ if (dwIndex != CB_ERR) {
+ dwData = SendMessage(hCtrl, CB_GETITEMDATA, dwIndex, 0L);
+ NewShare = (SHARE_BUFFER *) dwData;
+
+ if (OrigShare != NewShare) {
+ CurrentShare->DestShare = NewShare;
+
+ // this is actually a flag for the destination share
+ CurrentShare->Virtual = NewShare->VFlag;
+ }
+ }
+
+ hCtrl = GetDlgItem(hDlg, IDC_EDIT1);
+ * (WORD *)CurrentShare->SubDir = sizeof(CurrentShare->SubDir);
+ SendMessage(hCtrl, EM_GETLINE, 0, (LPARAM) CurrentShare->SubDir);
+
+ EndDialog(hDlg, 0);
+ break;
+
+ case IDCANCEL:
+ if (SelectType == SELECT_TYPE_ADD) {
+
+ // If we are in ADD - then we might have added a virtual
+ // share when we did the match, if so get rid of it...
+ if ((CurrentShare !=NULL) && (CurrentShare->DestShare != NULL))
+ if (CurrentShare->Virtual) {
+ VShare = (VIRTUAL_SHARE_BUFFER *) CurrentShare->DestShare;
+ VShareListDelete(DServ, VShare);
+ CurrentShare->DestShare = NULL;
+ }
+
+ CurrentShare = NULL;
+ }
+
+ EndDialog(hDlg, 0);
+ break;
+
+ case IDHELP:
+ if (SelectType == SELECT_TYPE_MODIFY)
+ WinHelp(hDlg, HELP_FILE, HELP_CONTEXT, (DWORD) IDC_HELP_SHAREMOD);
+ else
+ WinHelp(hDlg, HELP_FILE, HELP_CONTEXT, (DWORD) IDC_HELP_SHAREADD);
+ break;
+
+ case IDC_NEWSHARE:
+ CurrentDShare = NULL;
+ NewShareType = SELECT_TYPE_ADD;
+ NWShareNew_Do(hDlg);
+
+ // Match the combo-box to the given share
+ NTShareListFill(hDlg);
+ PostMessage(hDlg, WM_COMMAND, ID_UPDATECOMBO, 0L);
+ break;
+
+ case IDC_PROPERTIES:
+ NewShareType = SELECT_TYPE_MODIFY;
+ hCtrl = GetDlgItem(hDlg, IDC_COMBO2);
+ dwIndex = SendMessage(hCtrl, CB_GETCURSEL, 0, 0L);
+
+ if (dwIndex != CB_ERR) {
+ dwData = SendMessage(hCtrl, CB_GETITEMDATA, dwIndex, 0L);
+ CurrentDShare = (SHARE_BUFFER *) dwData;
+ NWShareNew_Do(hDlg);
+ }
+
+ break;
+
+ case ID_INIT:
+ if (SelectType == SELECT_TYPE_ADD) {
+ hCtrl = GetDlgItem(hDlg, IDC_COMBO1);
+
+ if (ShareList == NULL)
+ break;
+
+ CurrentShare = NULL;
+ for (i = 0; i < ShareList->Count; i++)
+ if (!SList[i].Convert) {
+ if (CurrentShare == NULL)
+ CurrentShare = &SList[i];
+
+ dwIndex = SendMessage(hCtrl, CB_ADDSTRING, (WPARAM) 0, (LPARAM) SList[i].Name);
+ SendMessage(hCtrl, CB_SETITEMDATA, (WPARAM) dwIndex, (LPARAM) &SList[i]);
+ }
+
+ if (CurrentShare != NULL) {
+ SendMessage(hCtrl, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) CurrentShare->Name);
+ MapShare(CurrentShare, DServ);
+ }
+
+ } else {
+ // Display the static text
+ hCtrl = GetDlgItem(hDlg, IDC_VOLUME);
+ EnableWindow(hCtrl, TRUE);
+ ShowWindow(hCtrl, SW_SHOW);
+ SendMessage(hCtrl, WM_SETTEXT, (WPARAM) 0, (LPARAM) CurrentShare->Name);
+ }
+
+ NTShareListFill(hDlg);
+ PostMessage(hDlg, WM_COMMAND, ID_UPDATECOMBO, 0L);
+ break;
+
+ // Used to update which volume we are pointing at
+ case ID_UPDATELIST:
+ // We might have added a virtual share when we did the
+ // match, if so get rid of it...
+ if ((CurrentShare !=NULL) && (CurrentShare->DestShare != NULL))
+ if (CurrentShare->Virtual) {
+ VShare = (VIRTUAL_SHARE_BUFFER *) CurrentShare->DestShare;
+ VShareListDelete(DServ, VShare);
+ CurrentShare->DestShare = NULL;
+ }
+
+ hCtrl = GetDlgItem(hDlg, IDC_COMBO1);
+ dwIndex = SendMessage(hCtrl, CB_GETCURSEL, 0, 0L);
+
+ if (dwIndex != CB_ERR) {
+ dwData = SendMessage(hCtrl, CB_GETITEMDATA, dwIndex, 0L);
+ CurrentShare = (SHARE_BUFFER *) dwData;
+
+ // Now need to map this to a new share
+ if (CurrentShare != NULL) {
+ MapShare(CurrentShare, DServ);
+
+ // Match the combo-box to the given share
+ NTShareListFill(hDlg);
+ }
+ }
+
+ break;
+
+ // updateded the share list selection
+ case ID_UPDATECOMBO:
+ hCtrl = GetDlgItem(hDlg, IDC_COMBO2);
+ dwIndex = SendMessage(hCtrl, CB_GETCURSEL, 0, 0L);
+
+ if (dwIndex != CB_ERR) {
+ dwData = SendMessage(hCtrl, CB_GETITEMDATA, dwIndex, 0L);
+ CurrentDShare = (SHARE_BUFFER *) dwData;
+ hCtrl = GetDlgItem(hDlg, IDC_PROPERTIES);
+
+ if (CurrentDShare->VFlag) {
+ EnableWindow(hCtrl, TRUE);
+ } else {
+ EnableWindow(hCtrl, FALSE);
+ }
+
+ }
+ break;
+
+ case IDC_COMBO1:
+ if (wmEvent == CBN_SELCHANGE)
+ PostMessage(hDlg, WM_COMMAND, ID_UPDATELIST, 0L);
+
+ break;
+
+ case IDC_COMBO2:
+ if (wmEvent == CBN_SELCHANGE)
+ PostMessage(hDlg, WM_COMMAND, ID_UPDATECOMBO, 0L);
+
+ break;
+ }
+ return TRUE;
+
+ }
+
+ return (FALSE); // Didn't process the message
+
+ lParam;
+} // NWShareSelect
+
+
+/*+-------------------------------------------------------------------------+
+ | ShareSelect_Do()
+ |
+ +-------------------------------------------------------------------------+*/
+void NWShareSelect_Do(HWND hDlg) {
+ DLGPROC lpfnDlg;
+
+ lpfnDlg = MakeProcInstance((DLGPROC)NWShareSelect, hInst);
+ DialogBox(hInst, TEXT("NWShareSelect"), hDlg, lpfnDlg) ;
+ FreeProcInstance(lpfnDlg);
+
+} // NWShareSelect_Do
+
+
+/*+-------------------------------------------------------------------------+
+ | Main File Options Dialog Routines |
+ +-------------------------------------------------------------------------+*/
+
+/*+-------------------------------------------------------------------------+
+ | FileOptionsToggleControls()
+ |
+ +-------------------------------------------------------------------------+*/
+void FileOptionsToggleControls(HWND hDlg, BOOL Toggle) {
+ HWND hCtrl;
+
+ hCtrl = GetDlgItem(hDlg, IDC_DELETE);
+ EnableWindow(hCtrl, Toggle);
+ hCtrl = GetDlgItem(hDlg, IDC_MODIFY);
+ EnableWindow(hCtrl, Toggle);
+ hCtrl = GetDlgItem(hDlg, IDC_FILES);
+ EnableWindow(hCtrl, Toggle);
+
+} // FileOptionsToggleControls
+
+
+/*+-------------------------------------------------------------------------+
+ | FileDialogToggle()
+ |
+ +-------------------------------------------------------------------------+*/
+void FileDialogToggle(HWND hDlg, BOOL Toggle) {
+ HWND hCtrl;
+
+ hCtrl = GetDlgItem(hDlg, IDC_LIST1);
+ EnableWindow(hCtrl, Toggle);
+ FileOptionsToggleControls(hDlg, Toggle);
+
+ hCtrl = GetDlgItem(hDlg, IDC_ADD);
+ if (Toggle == FALSE)
+ EnableWindow(hCtrl, FALSE);
+ else
+ if (ShareList && ShareList->Count != ShareList->ConvertCount)
+ EnableWindow(hCtrl, TRUE);
+ else
+ EnableWindow(hCtrl, FALSE);
+
+} // FileDialogToggle
+
+
+/*+-------------------------------------------------------------------------+
+ | DlgFileOptions_Save()
+ |
+ +-------------------------------------------------------------------------+*/
+void DlgFileOptions_Save(HWND hDlg) {
+ HWND hCtrl;
+
+ hCtrl = GetDlgItem(hDlg, IDC_CHKFILES);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 1)
+ FileOptions->TransferFileInfo = TRUE;
+ else
+ FileOptions->TransferFileInfo = FALSE;
+
+} // DlgFileOptions_Save
+
+
+/*+-------------------------------------------------------------------------+
+ | DlgFileOptions_Setup()
+ |
+ +-------------------------------------------------------------------------+*/
+void DlgFileOptions_Setup(HWND hDlg) {
+ HWND hCtrl;
+
+ hCtrl = GetDlgItem(hDlg, IDC_CHKFILES);
+ if (FileOptions->TransferFileInfo) {
+ SendMessage(hCtrl, BM_SETCHECK, 1, 0);
+ FileDialogToggle(hDlg, TRUE);
+ } else {
+ SendMessage(hCtrl, BM_SETCHECK, 0, 0);
+ FileDialogToggle(hDlg, FALSE);
+ }
+
+} // DlgFileOptions_Setup
+
+
+/*+-------------------------------------------------------------------------+
+ | DlgFileOptions_ListboxAdd()
+ |
+ +-------------------------------------------------------------------------+*/
+void DlgFileOptions_ListboxAdd(HWND hDlg, SHARE_BUFFER *CurrentShare, DWORD *wItem, BOOL Insert ) {
+ HWND hCtrl;
+ static TCHAR AddLine[256];
+ VIRTUAL_SHARE_BUFFER *VShare;
+ DWORD wItemNum;
+
+ wItemNum = *wItem;
+ hCtrl = GetDlgItem(hDlg, IDC_LIST1);
+ if (CurrentShare->Virtual) {
+ VShare = (VIRTUAL_SHARE_BUFFER *) CurrentShare->DestShare;
+ wsprintf(AddLine, TEXT("%s\\%s:\t\\\\%s\\%s\t"), SServ->Name, CurrentShare->Name, DServ->Name, VShare->Name);
+ } else
+ wsprintf(AddLine, TEXT("%s\\%s:\t\\\\%s\\%s\t"), SServ->Name, CurrentShare->Name, DServ->Name, CurrentShare->DestShare->Name);
+
+ if (Insert)
+ ColumnLB_InsertString(hCtrl, wItemNum, AddLine);
+ else
+ wItemNum = ColumnLB_AddString(hCtrl, AddLine);
+
+ ColumnLB_SetItemData(hCtrl, wItemNum, (DWORD) CurrentShare);
+ *wItem = wItemNum;
+
+} // DlgFileOptions_ListboxAdd
+
+
+/*+-------------------------------------------------------------------------+
+ | DlgFileOptions()
+ |
+ +-------------------------------------------------------------------------+*/
+LRESULT CALLBACK DlgFileOptions(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
+ HWND hCtrl;
+ DWORD wItemNum;
+ DWORD dwData;
+ static short FilesTab, FileOptionsTab;
+ int wmId, wmEvent;
+ ULONG i;
+ SHARE_BUFFER *pShare;
+ VIRTUAL_SHARE_BUFFER *VShare;
+ RECT rc;
+ int TabStop;
+
+ switch (message) {
+ case WM_INITDIALOG:
+ // Center the dialog over the application window
+ CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
+
+ hCtrl = GetDlgItem(hDlg, IDC_LIST1);
+ GetClientRect(hCtrl, &rc);
+
+ // Size is half width of listbox - vertical scrollbar
+ TabStop = (((rc.right - rc.left) - GetSystemMetrics(SM_CXVSCROLL)) / 2);
+ ColumnLB_SetNumberCols(hCtrl, 2);
+ ColumnLB_SetColTitle(hCtrl, 0, Lids(IDS_D_9));
+ ColumnLB_SetColTitle(hCtrl, 1, Lids(IDS_D_10));
+ ColumnLB_SetColWidth(hCtrl, 0, TabStop);
+
+ // Calculate 2nd this way instead of just using TabStop to get rid of roundoff
+ ColumnLB_SetColWidth(hCtrl, 1, (rc.right - rc.left) - TabStop);
+
+ DlgFileOptions_Setup(hDlg);
+
+ // Fill listbox and set selection (is assumed there is always a selection)...
+ PostMessage(hDlg, WM_COMMAND, ID_INIT, 0L);
+ return (TRUE);
+
+ case WM_COMMAND:
+ wmId = LOWORD(wParam);
+ wmEvent = HIWORD(wParam);
+
+ switch (wmId) {
+ case IDOK:
+ DlgFileOptions_Save(hDlg);
+ FileOptionsDefaultsSet(FileOptions);
+ EndDialog(hDlg, 0);
+ return (TRUE);
+
+ case IDCANCEL:
+ EndDialog(hDlg, 0);
+ return (TRUE);
+
+ case IDHELP:
+ WinHelp(hDlg, HELP_FILE, HELP_CONTEXT, (DWORD) IDC_HELP_FILE);
+ return (TRUE);
+
+ case IDC_CHKFILES:
+ hCtrl = GetDlgItem(hDlg, IDC_CHKFILES);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 1)
+ FileDialogToggle(hDlg, TRUE);
+ else
+ FileDialogToggle(hDlg, FALSE);
+
+ return (TRUE);
+
+ case IDC_ADD:
+ SelectType = SELECT_TYPE_ADD;
+ CurrentShare = NULL;
+ NWShareSelect_Do(hDlg);
+
+ if (CurrentShare != NULL) {
+ DlgFileOptions_ListboxAdd(hDlg, CurrentShare, &wItemNum, FALSE );
+
+ // Check if Add button needs to be disabled
+ ShareList->ConvertCount++;
+ if (ShareList->Count == ShareList->ConvertCount) {
+ hCtrl = GetDlgItem(hDlg, IDC_ADD);
+ EnableWindow(hCtrl, FALSE);
+ }
+
+ // Buttons need to be re-enabled
+ FileOptionsToggleControls(hDlg, TRUE);
+
+ // Now make sure focus is set
+ hCtrl = GetDlgItem(hDlg, IDC_LIST1);
+ ColumnLB_SetCurSel(hCtrl, wItemNum);
+ wItemNum = ColumnLB_GetCurSel(hCtrl);
+ if (wItemNum == LB_ERR)
+ ColumnLB_SetCurSel(hCtrl, 0);
+
+ };
+
+ return (TRUE);
+
+ case IDC_DELETE:
+ hCtrl = GetDlgItem(hDlg, IDC_LIST1);
+ wItemNum = ColumnLB_GetCurSel(hCtrl);
+ if (wItemNum != LB_ERR) {
+ dwData = ColumnLB_GetItemData(hCtrl, wItemNum);
+ pShare = (SHARE_BUFFER *)dwData;
+ pShare->Convert = FALSE;
+ ShareList->ConvertCount--;
+
+ // Now need to delete dest share, or reduce use count
+ if (pShare->DestShare != NULL)
+ if (pShare->Virtual) {
+ VShare = (VIRTUAL_SHARE_BUFFER *) pShare->DestShare;
+ VShareListDelete(DServ, VShare);
+ pShare->DestShare = NULL;
+ }
+
+ ColumnLB_DeleteString(hCtrl, wItemNum);
+ }
+
+ if (!ShareList->ConvertCount)
+ FileOptionsToggleControls(hDlg, FALSE);
+ else {
+ wItemNum = ColumnLB_GetCurSel(hCtrl);
+ if (wItemNum == LB_ERR)
+ ColumnLB_SetCurSel(hCtrl, 0);
+ }
+
+ if (ShareList->Count != ShareList->ConvertCount) {
+ hCtrl = GetDlgItem(hDlg, IDC_ADD);
+ EnableWindow(hCtrl, TRUE);
+ }
+
+ return (TRUE);
+
+ case IDC_MODIFY:
+ SelectType = SELECT_TYPE_MODIFY;
+ hCtrl = GetDlgItem(hDlg, IDC_LIST1);
+ wItemNum = ColumnLB_GetCurSel(hCtrl);
+ if (wItemNum != LB_ERR) {
+ dwData = ColumnLB_GetItemData(hCtrl, wItemNum);
+ CurrentShare = (SHARE_BUFFER *)dwData;
+ NWShareSelect_Do(hDlg);
+
+ // Now update listbox to reflect any changes
+ ColumnLB_DeleteString(hCtrl, wItemNum);
+
+ DlgFileOptions_ListboxAdd(hDlg, CurrentShare, &wItemNum, TRUE );
+
+ // now reset focus back to this item
+ ColumnLB_SetCurSel(hCtrl, wItemNum);
+ }
+ return (TRUE);
+
+ case IDC_FILES:
+ hCtrl = GetDlgItem(hDlg, IDC_LIST1);
+ wItemNum = ColumnLB_GetCurSel(hCtrl);
+ if (wItemNum != LB_ERR) {
+ dwData = ColumnLB_GetItemData(hCtrl, wItemNum);
+ CurrentShare = (SHARE_BUFFER *)dwData;
+ FileSelect_Do(hDlg, SServ, CurrentShare);
+ }
+ return (TRUE);
+
+ case IDC_FOPTIONS:
+ return (TRUE);
+
+ case ID_INIT:
+
+ if (ShareList != NULL) {
+ SList = ShareList->SList;
+
+ for (i = 0; i < ShareList->Count; i++)
+ if (SList[i].Convert) {
+ DlgFileOptions_ListboxAdd(hDlg, &SList[i], &wItemNum, FALSE );
+ hCtrl = GetDlgItem(hDlg, IDC_LIST1);
+ ColumnLB_SetCurSel(hCtrl, 0);
+ }
+
+ if (ShareList->Count == ShareList->ConvertCount) {
+ hCtrl = GetDlgItem(hDlg, IDC_ADD);
+ EnableWindow(hCtrl, FALSE);
+ }
+
+ if (!ShareList->ConvertCount)
+ FileOptionsToggleControls(hDlg, FALSE);
+
+ } else
+ FileOptionsToggleControls(hDlg, FALSE);
+
+ return (TRUE);
+
+ case IDC_LIST1:
+ switch (wmEvent) {
+ case LBN_DBLCLK:
+ PostMessage(hDlg, WM_COMMAND, IDC_MODIFY, 0L);
+ break;
+
+ case LBN_SELCHANGE:
+ if (!ShareList || !ShareList->ConvertCount)
+ FileOptionsToggleControls(hDlg, TRUE);
+
+ break;
+
+ }
+ break;
+
+ }
+ break;
+ }
+
+ return (FALSE); // Didn't process the message
+
+ lParam;
+} // DlgFileOptions
+
+
+/*+-------------------------------------------------------------------------+
+ | FileOptions_Do()
+ |
+ +-------------------------------------------------------------------------+*/
+void FileOptions_Do(HWND hDlg, void *ConvOptions, SOURCE_SERVER_BUFFER *SourceServer, DEST_SERVER_BUFFER *DestServer) {
+ DLGPROC lpfnDlg;
+
+ SServ = SourceServer;
+ DServ = DestServer;
+
+ NWServerFree();
+ NWServerSet(SourceServer->Name);
+ NTServerSet(DestServer->Name);
+ FileOptions = (FILE_OPTIONS *) ConvOptions;
+ ShareList = SServ->ShareList;
+
+ lpfnDlg = MakeProcInstance((DLGPROC)DlgFileOptions, hInst);
+ DialogBox(hInst, TEXT("FileMain"), hDlg, lpfnDlg) ;
+
+ FreeProcInstance(lpfnDlg);
+} // FileOptions_Do
diff --git a/private/nw/convert/nwconv/filedlg.h b/private/nw/convert/nwconv/filedlg.h
new file mode 100644
index 000000000..fbadf75db
--- /dev/null
+++ b/private/nw/convert/nwconv/filedlg.h
@@ -0,0 +1,34 @@
+/*+-------------------------------------------------------------------------+
+ | Copyright 1993-1994 (C) Microsoft Corporation - All rights reserved. |
+ +-------------------------------------------------------------------------+*/
+
+#ifndef _FILEDLG_
+#define _FILEDLG_
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+typedef struct _FILE_OPTIONS {
+ BOOL TransferFileInfo;
+ BOOL Validated; // Has user validated our mappings?
+} FILE_OPTIONS;
+
+
+void FileOptions_Do(HWND hDlg, void *ConvOptions, SOURCE_SERVER_BUFFER *SourceServ, DEST_SERVER_BUFFER *DestServ);
+void FileOptionsInit(void **lpfo);
+void FileOptionsDefaultsReset();
+void FileOptionsLoad(HANDLE hFile, void **lpfo);
+void FileOptionsSave(HANDLE hFile, void *fo);
+
+// These are actually in filesel.h - but for simplicity put them here
+void FillDirInit();
+void TreeFillRecurse(UINT Level, LPTSTR Path, DIR_BUFFER *Dir);
+void TreeCompact(DIR_BUFFER *Dir);
+void TreeRootInit(SHARE_BUFFER *CShare, LPTSTR NewPath);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/nw/convert/nwconv/fileicon.bmp b/private/nw/convert/nwconv/fileicon.bmp
new file mode 100644
index 000000000..39afde299
--- /dev/null
+++ b/private/nw/convert/nwconv/fileicon.bmp
Binary files differ
diff --git a/private/nw/convert/nwconv/filesel.c b/private/nw/convert/nwconv/filesel.c
new file mode 100644
index 000000000..8d8b8161a
--- /dev/null
+++ b/private/nw/convert/nwconv/filesel.c
@@ -0,0 +1,2349 @@
+/*++
+
+Copyright (c) 1993-1995 Microsoft Corporation
+
+Module Name:
+
+ FileSel.c
+
+Abstract:
+
+ Handles processing for the file selection listbox. This is a
+ hierarchical file/directory tree with checkboxes besides the files
+ and directories.
+
+ If the checkbox is checked then that file or directory will be
+ copied, otherwise it won't. There are some directories that are
+ excluded by default. These are directories on the NW server that
+ are known to contain binaries that are not needed on the NT side
+ (such as the NetWare administration tools).
+
+Author:
+
+ Arthur Hanson (arth) 10-Feb-1994
+
+Revision History:
+
+--*/
+
+#include "globals.h"
+
+#include "hierfile.h"
+#include "nwconv.h"
+#include "convapi.h"
+#include "ntnetapi.h"
+#include "nwnetapi.h"
+#include "columnlb.h"
+#include "statbox.h"
+#include "userdlg.h"
+
+#include <math.h> // For pow function
+
+#define X_BORDER 10
+#define Y_BORDER 10
+#define SPLITTER_WIDTH 3
+#define BUTTON_Y_BORDER 6
+#define BUTTON_X_BORDER 10
+
+static int List1Width;
+static int Splitter_Left;
+static int Splitter_Bottom;
+static HCURSOR CursorSplitter;
+static HWND hwndList2 = 0;
+static HWND hwndList1 = 0;
+
+static SHARE_BUFFER *CurrentShare;
+static SOURCE_SERVER_BUFFER *SServ;
+static BOOL HiddenFiles = FALSE;
+static BOOL SystemFiles = FALSE;
+static DIR_BUFFER *oDir = NULL;
+
+static ULONG TotalFileSize = 0;
+static LPARAM mouseHit = 0;
+static WORD LastFocus = 0;
+static HWND ListFocus = NULL;
+
+static ULONG TCount;
+
+static WNDPROC _wpOrigWndProc;
+static WNDPROC _wpOrigWndProc2;
+
+
+HEIRDRAWSTRUCT HierDrawStruct;
+BOOL SysDir = FALSE;
+
+#define ROWS 2
+#define COLS 2
+
+// define a scratch buffer for quickly building up dir/file lists
+#define DEF_NUM_RECS 50
+#define DEF_REC_DELTA 25
+static UINT BufferSize = 0;
+static WIN32_FIND_DATA *ffd = NULL;
+
+
+/*+-------------------------------------------------------------------------+
+ | Routines for Directory/File Trees |
+ +-------------------------------------------------------------------------+*/
+
+
+/////////////////////////////////////////////////////////////////////////
+FILE_PATH_BUFFER *
+FilePathInit()
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ FILE_PATH_BUFFER *fpBuf = NULL;
+
+ fpBuf = AllocMemory(sizeof(FILE_PATH_BUFFER));
+ if (fpBuf == NULL)
+ return NULL;
+
+ memset(fpBuf, 0, sizeof(FILE_PATH_BUFFER));
+ return fpBuf;
+
+} // FilePathInit
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+FilePathServerSet(
+ FILE_PATH_BUFFER *fpBuf,
+ LPTSTR Server
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ fpBuf->Server = Server;
+
+ if (fpBuf->Server == NULL)
+ return;
+
+ wsprintf(fpBuf->FullPath, TEXT("\\\\%s\\"), Server);
+
+} // FilePathServerSet
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+FilePathShareSet(
+ FILE_PATH_BUFFER *fpBuf,
+ LPTSTR Share
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ fpBuf->Share = Share;
+ if ((fpBuf->Server == NULL) || (fpBuf->Share == NULL))
+ return;
+
+ wsprintf(fpBuf->FullPath, TEXT("\\\\%s\\%s"), fpBuf->Server, Share);
+ fpBuf->Path = &fpBuf->FullPath[lstrlen(fpBuf->FullPath)];
+
+} // FilePathShareSet
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+FilePathPathSet(
+ FILE_PATH_BUFFER *fpBuf,
+ LPTSTR Path
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ if ((fpBuf->Server == NULL) || (fpBuf->Share == NULL))
+ return;
+
+ *fpBuf->Path = TEXT('\0');
+ if (Path == NULL)
+ return;
+
+ lstrcat(fpBuf->FullPath, Path);
+
+} // FilePathPathSet
+
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+TreeDelete(
+ DIR_BUFFER *Dir
+ )
+
+/*++
+
+Routine Description:
+
+ Walks an in-memory directory tree and free's up all the memory associated
+ with it and all child nodes.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ DIR_BUFFER *DList;
+ ULONG i;
+
+ if (Dir == NULL)
+ return;
+
+ if (Dir->DirList) {
+ DList = Dir->DirList->DirBuffer;
+ for (i = 0; i < Dir->DirList->Count; i++)
+ TreeDelete(&DList[i]);
+
+ FreeMemory(Dir->DirList);
+ }
+
+ if (Dir->FileList)
+ FreeMemory(Dir->FileList);
+
+} // TreeDelete
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+TreePrune(
+ DIR_BUFFER *Dir
+ )
+
+/*++
+
+Routine Description:
+
+ Prunes a tree down by removing un-needed nodes. If a node is marked
+ as CONVERT_ALL or CONVERT_NONE then we don't need any of the child
+ leaves as we know these will be the same. Only CONVERT_PARTIAL
+ needs to be saved.
+
+ This is used so we only copy/save the minimum needed.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ BYTE Convert;
+ DIR_BUFFER *DList;
+ ULONG i;
+
+ if (Dir == NULL)
+ return;
+
+ // First visit all of the children sub-dirs and prune them. Next:
+ // if partial convert then we can't delete this node, else we can
+ // clean up all the children - we leave it to the parent node to
+ // delete the current node.
+ Convert = Dir->Convert;
+ if (Dir->DirList) {
+ DList = Dir->DirList->DirBuffer;
+ for (i = 0; i < Dir->DirList->Count; i++)
+ TreePrune(&DList[i]);
+
+ if (Convert == CONVERT_PARTIAL)
+ return;
+
+ if (Dir->Special && (Convert == CONVERT_ALL))
+ return;
+
+ for (i = 0; i < Dir->DirList->Count; i++)
+ TreeDelete(&DList[i]);
+
+ Dir->DirList = NULL;
+ }
+
+ if (Convert == CONVERT_PARTIAL)
+ return;
+
+ if (Dir->FileList)
+ FreeMemory(Dir->FileList);
+
+ Dir->FileList = NULL;
+
+} // TreePrune
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+_TreeCountR(
+ DIR_BUFFER *Dir
+ )
+
+/*++
+
+Routine Description:
+
+ Count all the files under a sub-dir. This recuses down all the child
+ nodes.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ BYTE Convert;
+ DIR_BUFFER *DList;
+ ULONG i;
+
+ if ((Dir == NULL) || (!Dir->Convert))
+ return;
+
+ Convert = Dir->Convert;
+ if (Dir->DirList) {
+ DList = Dir->DirList->DirBuffer;
+ for (i = 0; i < Dir->DirList->Count; i++)
+ _TreeCountR(&DList[i]);
+
+ }
+
+ if (Dir->FileList)
+ for (i = 0; i < Dir->FileList->Count; i++)
+ if (Dir->FileList->FileBuffer[i].Convert)
+ TCount++;
+
+} // _TreeCountR
+
+
+ULONG TreeCount(DIR_BUFFER *Dir) {
+ TCount = 0;
+ if (Dir == NULL)
+ return TCount;
+
+ _TreeCountR(Dir);
+ return TCount;
+
+} // TreeCount
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+_TreeCopyR(
+ DIR_BUFFER *Dir
+ )
+
+/*++
+
+Routine Description:
+
+ Duplicates a directory/File tree structure in memory.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ DIR_LIST *DList;
+ DIR_BUFFER *DBuff;
+ FILE_LIST *FList;
+ FILE_BUFFER *FBuff;
+ ULONG Size;
+ ULONG i;
+
+ if (Dir == NULL)
+ return;
+
+ if (Dir->FileList) {
+ // Create clone of file list
+ Size = sizeof(FILE_LIST) + (sizeof(FILE_BUFFER) * Dir->FileList->Count);
+ FList = AllocMemory(Size);
+
+ if (FList != NULL)
+ memcpy(FList, Dir->FileList, Size);
+
+ // Copied it, now fixup the internal pointers.
+ FList->parent = Dir;
+
+ FBuff = FList->FileBuffer;
+ for (i = 0; i < FList->Count; i++)
+ FBuff[i].parent = FList;
+
+ // Now replace pointer with cloned tree
+ Dir->FileList = FList;
+ }
+
+ if (Dir->DirList) {
+ // Create clone of Dir List
+ Size = sizeof(DIR_LIST) + (sizeof(DIR_BUFFER) * Dir->DirList->Count);
+ DList = AllocMemory(Size);
+
+ if (DList != NULL)
+ memcpy(DList, Dir->DirList, Size);
+
+ // Copied it, now fixup the internal pointers.
+ DList->parent = Dir;
+
+ DBuff = DList->DirBuffer;
+ for (i = 0; i < DList->Count; i++)
+ DBuff[i].parent = DList;
+
+ // Now replace pointer with cloned tree
+ Dir->DirList = DList;
+
+ // Now recurse into children and fix them up
+ for (i = 0; i < DList->Count; i++)
+ _TreeCopyR(&DBuff[i]);
+
+ }
+
+} // _TreeCopyR
+
+
+/////////////////////////////////////////////////////////////////////////
+DIR_BUFFER *
+TreeCopy(
+ DIR_BUFFER *Dir
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ DIR_BUFFER *nDir = NULL;
+
+ if (Dir == NULL)
+ return NULL;
+
+ nDir = AllocMemory(sizeof(DIR_BUFFER));
+ if (nDir != NULL) {
+ memcpy(nDir, Dir, sizeof(DIR_BUFFER));
+ _TreeCopyR(nDir);
+ }
+
+ return nDir;
+
+} // TreeCopy
+
+
+
+/////////////////////////////////////////////////////////////////////////
+int __cdecl
+FileBufferCompare(
+ const VOID *arg1,
+ const VOID *arg2
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ WIN32_FIND_DATA *Farg1, *Farg2;
+
+ Farg1 = (WIN32_FIND_DATA *) arg1;
+ Farg2 = (WIN32_FIND_DATA *) arg2;
+
+ // This works as the first item of the structure is the string
+ return lstrcmpi( Farg1->cFileName, Farg2->cFileName);
+
+} // FileBufferCompare
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+DirAdjustConvert(
+ DIR_BUFFER *Dir
+ )
+
+/*++
+
+Routine Description:
+
+ Need to adjust convert flag (none, full, partial) down the tree to the
+ root.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ BOOL Partial = FALSE;
+ ULONG ChildCount = 0;
+ ULONG ChildSelected = 0;
+ DIR_LIST *DirList = NULL;
+ FILE_LIST *FileList = NULL;
+ ULONG i;
+
+ if (Dir == NULL)
+ return;
+
+ // if no files or dirs don't try to re-adjust current setting.
+ if ((Dir->DirList == NULL) && (Dir->FileList == NULL))
+ goto DirAdjRecurse;
+
+ // Scan the children directories to see what is to be converted.
+ DirList = Dir->DirList;
+ if (DirList != NULL) {
+ ChildCount += DirList->Count;
+ for (i = 0; i < DirList->Count; i++)
+ if (DirList->DirBuffer[i].Convert == CONVERT_PARTIAL)
+ Partial = TRUE;
+ else
+ ChildSelected += DirList->DirBuffer[i].Convert;
+ }
+
+ // if any of the children were partial convert then it is easy, as
+ // we are partial convert as well.
+ if (Partial) {
+ Dir->Convert = CONVERT_PARTIAL;
+ goto DirAdjRecurse;
+ }
+
+
+ // Scan the children files to see what is to be converted.
+ FileList = Dir->FileList;
+ if (FileList != NULL) {
+ ChildCount += FileList->Count;
+ for (i = 0; i < FileList->Count; i++)
+ ChildSelected += FileList->FileBuffer[i].Convert;
+ }
+
+ if (ChildSelected == ChildCount)
+ Dir->Convert = CONVERT_ALL;
+ else
+ if (ChildSelected == 0)
+ Dir->Convert = CONVERT_NONE;
+ else
+ Dir->Convert = CONVERT_PARTIAL;
+
+DirAdjRecurse:
+ DirList = Dir->parent;
+ if (DirList != NULL)
+ DirAdjustConvert(DirList->parent);
+
+} // DirAdjustConvert
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+DirAdjustConvertChildren(
+ DIR_BUFFER *Dir,
+ BYTE Convert
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ DIR_LIST *DirList = NULL;
+ FILE_LIST *FileList = NULL;
+ ULONG i;
+
+ if (Dir == NULL)
+ return;
+
+ Dir->Convert = Convert;
+
+ // Scan the children files
+ FileList = Dir->FileList;
+ if (FileList != NULL)
+ for (i = 0; i < FileList->Count; i++)
+ FileList->FileBuffer[i].Convert = Convert;
+
+ // Scan the children directories
+ DirList = Dir->DirList;
+ if (DirList != NULL)
+ for (i = 0; i < DirList->Count; i++)
+ DirAdjustConvertChildren(&DirList->DirBuffer[i], Convert);
+
+} // DirAdjustConvertChildren
+
+
+/////////////////////////////////////////////////////////////////////////
+BOOL
+SubdirRestrict(
+ LPTSTR Path,
+ LPTSTR Subdir
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i = 0;
+ LPTSTR RestrictPath[5];
+ CONVERT_OPTIONS * ConvertOptions;
+
+ // if the user has specified the 'transfer netware specific info'
+ // option the we should transfer the mail directory by default...
+ ConvertOptions = (CONVERT_OPTIONS *)CurrentConvertList->ConvertOptions;
+
+ if (ConvertOptions->NetWareInfo)
+ RestrictPath[i++] = Lids(IDS_S_3);
+
+ RestrictPath[i++] = Lids(IDS_S_2);
+ RestrictPath[i++] = Lids(IDS_S_4);
+ RestrictPath[i++] = Lids(IDS_S_5);
+ RestrictPath[i++] = NULL;
+
+ i = 0;
+ while(RestrictPath[i] != NULL) {
+ if (!lstrcmpi(RestrictPath[i], Subdir))
+ return TRUE;
+
+ i++;
+ }
+
+ return FALSE;
+
+} // SubdirRestrict
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+FillDirInit()
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ if (ffd == NULL) {
+ ffd = AllocMemory(sizeof(WIN32_FIND_DATA) * DEF_NUM_RECS);
+ BufferSize = DEF_NUM_RECS;
+ }
+
+} // FillDirInit
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+FillDir(
+ UINT Level,
+ LPTSTR Path,
+ DIR_BUFFER *Dir,
+ BOOL DoDirs
+ )
+
+/*++
+
+Routine Description:
+
+ Given a DIR_BUFFER, enumerate the files and sub-dirs under it and
+ attach them (one level-deep only).
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ static TCHAR NewPath[MAX_UNC_PATH + 1];
+ DIR_LIST *DirList = NULL;
+ DIR_LIST *OldDirList = NULL;
+ DIR_BUFFER *DBuff;
+
+ FILE_LIST *FileList = NULL;
+ FILE_LIST *OldFileList = NULL;
+ FILE_BUFFER *FBuff;
+
+ HANDLE fHandle = NULL;
+ ULONG DirCount = 0;
+ ULONG FileCount = 0;
+ ULONG Count = 0;
+ BOOL ret = TRUE;
+ ULONG i;
+ BYTE Convert;
+ BOOL ConvFlag;
+
+ FixPathSlash(NewPath, Path);
+ lstrcat(NewPath, TEXT("*.*"));
+
+#ifdef DEBUG
+dprintf(TEXT("Working on dir: %u %s\r\n"), Level, Path);
+#endif
+
+ Panel_Line(7, TEXT("%s"), Path);
+ fHandle = FindFirstFile(NewPath, &ffd[Count]);
+
+ ret = (fHandle != INVALID_HANDLE_VALUE);
+
+ // loop filling in the temp buffer - figure out how many dirs and files
+ // we have to remember - and build up a temporary buffer of them
+ while (ret) {
+
+ if (ffd[Count].dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ ConvFlag = TRUE;
+
+ if (!((lstrcmp(ffd[Count].cFileName, TEXT("."))) && (lstrcmp(ffd[Count].cFileName, TEXT("..")))))
+ ConvFlag = FALSE;
+
+ if (!HiddenFiles && (ffd[Count].dwFileAttributes & FILE_ATTRIBUTE_HIDDEN))
+ ConvFlag = FALSE;
+
+ if (!SystemFiles && (ffd[Count].dwFileAttributes & FILE_ATTRIBUTE_SYSTEM))
+ ConvFlag = FALSE;
+
+ // Use the cAlternateFileName as a flag whether to convert
+ if (ConvFlag) {
+ ffd[Count].cAlternateFileName[0] = 1;
+ DirCount++;
+ } else
+ ffd[Count].cAlternateFileName[0] = 0;
+
+ } else {
+ ConvFlag = TRUE;
+
+ if (!HiddenFiles && (ffd[Count].dwFileAttributes & FILE_ATTRIBUTE_HIDDEN))
+ ConvFlag = FALSE;
+
+ if (!SystemFiles && (ffd[Count].dwFileAttributes & FILE_ATTRIBUTE_SYSTEM))
+ ConvFlag = FALSE;
+
+ if (ConvFlag) {
+ ffd[Count].cAlternateFileName[0] = 1;
+ FileCount++;
+ } else
+ ffd[Count].cAlternateFileName[0] = 0;
+
+ }
+
+ Count++;
+
+ // check if we are going to run out of space in our buffer - if so
+ // allocate more space
+ if (Count >= BufferSize) {
+ BufferSize += DEF_REC_DELTA;
+ ffd = ReallocMemory(ffd, sizeof(WIN32_FIND_DATA) * BufferSize);
+ }
+
+ if (ffd == NULL) {
+ FindClose(fHandle);
+ return;
+ } else
+ ret = FindNextFile(fHandle, &ffd[Count]);
+
+ }
+
+ FindClose(fHandle);
+
+#ifdef DEBUG
+dprintf(TEXT(" Num Dirs / Files: %li %li\r\n"), DirCount, FileCount);
+#endif
+
+ // Temp buffer is all filled in at this point. Sort it first
+ if (Count != 0)
+ qsort((void *) ffd, (size_t) Count, sizeof(WIN32_FIND_DATA), FileBufferCompare);
+
+ // Now create the actual list structures
+ if (DoDirs && DirCount)
+ DirList = AllocMemory(sizeof(DIR_LIST) + (sizeof(DIR_BUFFER) * DirCount));
+
+ if (FileCount)
+ FileList = AllocMemory(sizeof(FILE_LIST) + (sizeof(FILE_BUFFER) * FileCount));
+
+ // if there is no dirlist and there is an old one, clean up the old-one.
+ if (DoDirs && (DirList == NULL) && (Dir->DirList != NULL)) {
+ // save off file list so it isn't nuked
+ OldFileList = (FILE_LIST *) Dir->FileList;
+ Dir->FileList = NULL;
+ TreeDelete(Dir);
+ Dir->DirList = NULL;
+
+ // Now restore file list
+ Dir->FileList = OldFileList;
+ }
+
+ // same for file list.
+ if ((FileList == NULL) && (Dir->FileList != NULL)) {
+ FreeMemory(Dir->FileList);
+ Dir->FileList = NULL;
+ }
+
+ // If nothing to copy, or couldn't alloc memory then no reason to continue
+ // further...
+ if ((DirList == NULL) && (FileList == NULL))
+ return;
+
+ if (Dir->Convert == CONVERT_PARTIAL)
+ Convert = CONVERT_ALL;
+ else
+ Convert = Dir->Convert;
+
+ if (DoDirs && (DirList != NULL)) {
+ DirList->Count = DirCount;
+ DirList->Level = Level;
+ DirList->parent = Dir;
+ DirList->DirBuffer[DirCount - 1].Last = TRUE;
+ }
+
+ if (FileList != NULL) {
+ FileList->Count = FileCount;
+ FileList->parent = Dir;
+ }
+
+ // transfer the temp buffers to our list structures
+ DirCount = FileCount = 0;
+ for (i = 0; i < Count; i++) {
+ if (ffd[i].dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
+ // Directories
+ if (DoDirs) {
+ // Check our Flag
+ if (ffd[i].cAlternateFileName[0] == 1) {
+ DBuff = &DirList->DirBuffer[DirCount];
+ DBuff->Attributes = ffd[i].dwFileAttributes;
+ lstrcpy(DBuff->Name, ffd[i].cFileName);
+ DBuff->parent = DirList;
+
+ // Check against the subdirs we don't want to convert by default
+ // if the user has already toggled that these should be converted,
+ // then the copy of the old info below will fix it back up.
+ if (SysDir && (Level == 1) && SubdirRestrict(Path, DBuff->Name)) {
+ DBuff->Convert = CONVERT_NONE;
+ DBuff->Special = TRUE;
+ } else
+ DBuff->Convert = Convert;
+
+ DirCount++;
+ }
+ }
+ } else {
+ // Files
+ Panel_Line(8, TEXT("%s"), ffd[i].cFileName);
+
+ // Check our Flag
+ if (ffd[i].cAlternateFileName[0] == 1) {
+ FBuff = &FileList->FileBuffer[FileCount];
+ FBuff->Attributes = ffd[i].dwFileAttributes;
+ lstrcpy(FBuff->Name, ffd[i].cFileName);
+ FBuff->parent = FileList;
+ FBuff->Convert = Convert;
+ FBuff->Size = ffd[i].nFileSizeLow;
+
+ TotalFileSize += ffd[i].nFileSizeLow;
+ Panel_Line(9, TEXT("%s"), lToStr(TotalFileSize));
+ FileCount++;
+ }
+ }
+
+ }
+
+
+ // Now have the new lists filled in. If there was an old list then we must
+ // now merge ... can we say pain in the #$$
+ if (DoDirs) {
+ OldDirList = (DIR_LIST *) Dir->DirList;
+ Dir->DirList = DirList;
+ }
+
+ OldFileList = (FILE_LIST *) Dir->FileList;
+ Dir->FileList = FileList;
+
+ // Check the directories
+ if (DoDirs && (OldDirList != NULL) && (DirList != NULL)) {
+ int cmp;
+
+ DirCount = 0;
+ i = 0;
+
+ while (i < OldDirList->Count) {
+ do {
+ cmp = lstrcmpi(OldDirList->DirBuffer[i].Name, DirList->DirBuffer[DirCount].Name);
+
+ // a match so copy old data into new...
+ if (!cmp) {
+ DBuff = &DirList->DirBuffer[DirCount];
+ DBuff->Convert = OldDirList->DirBuffer[i].Convert;
+ DBuff->DirList = OldDirList->DirBuffer[i].DirList;
+ DBuff->FileList = OldDirList->DirBuffer[i].FileList;
+
+ // Now point these back to the new structures
+ if (DBuff->DirList)
+ DBuff->DirList->parent = DBuff;
+
+ if (DBuff->FileList)
+ DBuff->FileList->parent = DBuff;
+ }
+
+ // keep incrementing new dir list until we go past old server
+ // list, then must skip out and increment old server list
+ DirCount++;
+ } while ((DirCount < DirList->Count) && (cmp > 0));
+
+ if (DirCount >= DirList->Count)
+ break;
+
+ i++;
+ }
+ }
+
+ // Same stuff for the files
+ if ((OldFileList != NULL) && (FileList != NULL)) {
+ int cmp;
+
+ FileCount = 0;
+ i = 0;
+
+ while (i < OldFileList->Count) {
+ do {
+ cmp = lstrcmpi(OldFileList->FileBuffer[i].Name, FileList->FileBuffer[FileCount].Name);
+
+ // a match so copy old data into new...
+ if (!cmp)
+ FileList->FileBuffer[FileCount].Convert = OldFileList->FileBuffer[i].Convert;
+
+ FileCount++;
+ } while ((FileCount < FileList->Count) && (cmp > 0));
+
+ if (FileCount >= FileList->Count)
+ break;
+
+ i++;
+ }
+ }
+
+ // Clean up any old lists
+ if (OldDirList != NULL)
+ FreeMemory(OldDirList);
+
+ if (OldFileList != NULL)
+ FreeMemory(OldFileList);
+
+ DirAdjustConvert(Dir);
+
+} // FillDir
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+_TreeFillRecurse(
+ UINT Level,
+ LPTSTR Path,
+ DIR_BUFFER *Dir
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ TCHAR NewPath[MAX_UNC_PATH + 1];
+ DIR_LIST *DirList = NULL;
+ ULONG i;
+
+ FillDir(Level, Path, Dir, TRUE);
+ DirList = Dir->DirList;
+
+ if (DirList != NULL)
+ for (i = 0; i < DirList->Count; i++) {
+
+ if (Panel_Cancel())
+ return;
+
+ if (DirList->DirBuffer[i].Convert) {
+ wsprintf(NewPath, TEXT("%s\\%s"), Path, DirList->DirBuffer[i].Name);
+ _TreeFillRecurse(Level + 1, NewPath, &DirList->DirBuffer[i]);
+ }
+ }
+
+} // _TreeFillRecurse
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+TreeFillRecurse(
+ UINT Level,
+ LPTSTR Path,
+ DIR_BUFFER *Dir
+ )
+{
+ TotalFileSize = 0;
+ FillDirInit();
+ _TreeFillRecurse(Level, Path, Dir);
+} // TreeFillRecurse
+
+
+/////////////////////////////////////////////////////////////////////////
+ULONG
+TotalFileSizeGet()
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ return TotalFileSize;
+} // TotalFileSizeGet
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+TreeRecurseCurrentShareSet(
+ SHARE_BUFFER *CShare
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ CurrentShare = CShare;
+
+ SysDir = FALSE;
+ if (!CShare)
+ return;
+
+ if (!lstrcmpi(CShare->Name, Lids(IDS_S_6)))
+ SysDir = TRUE;
+
+ return;
+
+} // TreeRecurseCurrentShareSet
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ TreeRootInit(
+ SHARE_BUFFER *CShare,
+ LPTSTR NewPath
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ TreeRecurseCurrentShareSet(CShare);
+
+ if (CShare->Root)
+ return;
+
+ CShare->Root = AllocMemory(sizeof(DIR_BUFFER));
+ lstrcpy(CShare->Root->Name, NewPath);
+ CShare->Root->Last = TRUE;
+ CShare->Root->Attributes = FILE_ATTRIBUTE_DIRECTORY;
+
+ CShare->Root->DirList = NULL;
+ CShare->Root->FileList = NULL;
+ CShare->Root->parent = NULL;
+
+ // have to set this to preserve user selection of special excluded dirs...
+ if (SysDir)
+ CShare->Root->Special = TRUE;
+
+ CShare->Root->Convert = CONVERT_ALL;
+
+ FillDir(1, CShare->Root->Name, CShare->Root, TRUE);
+ DirAdjustConvert(CShare->Root);
+
+} // TreeRootInit
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ControlsResize(
+ HWND hDlg,
+ int Height,
+ int Width
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ HWND hCtrl;
+ int nHeight, nWidth, BtnWidth, BtnHeight;
+ RECT rc;
+
+ hCtrl = GetDlgItem(hDlg, IDOK);
+ GetWindowRect(hCtrl, &rc);
+ BtnWidth = (rc.right - rc.left);
+ BtnHeight = (rc.bottom - rc.top);
+
+ // Get size of first listbox and figure height as it is same for both
+ hCtrl = GetDlgItem(hDlg, IDC_LIST1);
+ GetWindowRect(hCtrl, &rc);
+ List1Width = (rc.right - rc.left);
+ nHeight = Height - Y_BORDER - BtnHeight - (2 * BUTTON_Y_BORDER); // subtract out borders
+ Splitter_Bottom = nHeight + Y_BORDER;
+ Splitter_Left = List1Width + X_BORDER;
+
+ // First Listbox never changes width on Window resize
+ MoveWindow(hCtrl, X_BORDER, Y_BORDER, List1Width, nHeight, TRUE);
+ GetWindowRect(hCtrl, &rc);
+ nHeight = (rc.bottom - rc.top);
+
+ // Second Listbox has width based on first and new size of Window.
+ if (Width > (2 * X_BORDER) + SPLITTER_WIDTH)
+ nWidth = Width - ( 2 * X_BORDER) - SPLITTER_WIDTH;
+ else
+ nWidth = 1;
+
+ // Now must take off from first listbox
+ if (nWidth > List1Width)
+ nWidth -= List1Width;
+ else
+ nWidth = 1;
+
+ hCtrl = GetDlgItem(hDlg, IDC_LIST2);
+ MoveWindow(hCtrl, X_BORDER + List1Width + SPLITTER_WIDTH, Y_BORDER, nWidth, nHeight, TRUE);
+
+ // Figure out where to put the buttons
+ nWidth = (Width / 2) - (((3 * BtnWidth) + (2 * BUTTON_X_BORDER)) / 2);
+ nHeight = Height - BtnHeight - BUTTON_Y_BORDER;
+ hCtrl = GetDlgItem(hDlg, IDOK);
+ MoveWindow(hCtrl, nWidth, nHeight, BtnWidth, BtnHeight, TRUE);
+ nWidth += BtnWidth + BUTTON_X_BORDER;
+
+ hCtrl = GetDlgItem(hDlg, IDCANCEL);
+ MoveWindow(hCtrl, nWidth, nHeight, BtnWidth, BtnHeight, TRUE);
+ nWidth += BtnWidth + BUTTON_X_BORDER;
+
+ hCtrl = GetDlgItem(hDlg, IDHELP);
+ MoveWindow(hCtrl, nWidth, nHeight, BtnWidth, BtnHeight, TRUE);
+
+} // ControlsResize
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+FileSelect_OnDrawItem(
+ HWND hwnd,
+ DRAWITEMSTRUCT FAR* lpDrawItem
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ TCHAR szText[MAX_PATH + 1];
+ DWORD dwData;
+ int nLevel = 0;
+ int nTempLevel;
+ int nRow = 0;
+ int nColumn = 0;
+ int chkColumn = 0;
+ DWORD dwConnectLevel = 0;
+ DWORD dwMask;
+ DIR_BUFFER *Dir, *Dir2;
+ FILE_BUFFER *File;
+ DIR_LIST *DirList, *DirList2;
+ BOOL FileBox = FALSE;
+
+ dwData = lpDrawItem->itemData;
+ if (dwData == 0)
+ return;
+
+ if ((lpDrawItem->CtlID != IDC_LIST1) && (lpDrawItem->CtlID != IDC_LIST2))
+ return;
+
+ if (lpDrawItem->CtlID == IDC_LIST2)
+ FileBox = TRUE;
+
+ // Select the correct icon, open folder, closed folder, or document.
+
+ if (FileBox) {
+ File = (FILE_BUFFER *) dwData;
+ lstrcpy(szText, File->Name);
+ nRow = 1;
+ chkColumn = File->Convert;
+ } else {
+
+ Dir2 = Dir = (DIR_BUFFER *) dwData;
+ lstrcpy(szText, Dir->Name);
+ chkColumn = Dir->Convert;
+ DirList2 = DirList = (DIR_LIST *) Dir->parent;
+
+ if (DirList != NULL)
+ nLevel = DirList->Level;
+
+ // Is it open ?
+ if ( HierFile_IsOpened(&HierDrawStruct, dwData) )
+ nColumn = 1;
+ else
+ nColumn = 0;
+
+ // Connect level figures out what connecting lines should be drawn
+ // - stored as a bitmask.
+ if (nLevel == 0)
+ dwConnectLevel = 0;
+ else {
+ nTempLevel = nLevel - 1;
+
+ // First bit to set
+ dwMask = (DWORD) pow(2, nLevel - 1);
+
+ // Now go through and figure out what else to set...
+ while (nTempLevel >= 0) {
+ // Check if last child at this level...
+ if (!Dir2->Last)
+ dwConnectLevel |= dwMask;
+
+ // figure out next parent...
+ Dir2 = DirList2->parent;
+ DirList2 = Dir2->parent;
+
+ // move mask bit over, and up a level.
+ dwMask /= 2;
+
+ // move up one level.
+ nTempLevel--;
+ }
+ }
+ }
+
+ // All set to call drawing function.
+ HierFile_OnDrawItem(hwnd, lpDrawItem, nLevel, dwConnectLevel, szText,
+ nRow, nColumn, chkColumn, &HierDrawStruct);
+
+ return;
+
+} // FileSelect_OnDrawItem
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+RecursePath(
+ LPTSTR Path,
+ DIR_BUFFER *Dir
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ DIR_LIST *DirList;
+ ULONG i;
+
+ DirList = (DIR_LIST *) Dir->parent;
+
+ if (DirList != NULL)
+ RecursePath(Path, (DIR_BUFFER *) DirList->parent);
+
+ i = lstrlen(Path);
+ if (i)
+ if (Path[i-1] != TEXT('\\'))
+ lstrcat(Path, TEXT("\\"));
+
+ lstrcat(Path, Dir->Name);
+} // RecursePath
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+CloseList(
+ HWND hWndList,
+ DIR_BUFFER *Dir,
+ WORD wItemNum
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ DWORD dwIncr;
+ DWORD Count;
+ DIR_LIST *DirList;
+
+ HierFile_CloseItem(&HierDrawStruct, (DWORD) Dir);
+ DirList = (DIR_LIST *) Dir->DirList;
+
+ if (DirList != NULL) {
+ Count = DirList->Count;
+
+ // Remove the child items. Close any children that are open on the way.
+ // wItem can stay constant - we are moveing stuff up in the listbox as we are deleting.
+ wItemNum++;
+ dwIncr = SendMessage(hWndList, LB_GETITEMDATA, wItemNum, 0L);
+
+ while (Count) {
+ // Is this child open ?
+ if ( HierFile_IsOpened(&HierDrawStruct, dwIncr) ) {
+ CloseList(hWndList, (DIR_BUFFER *) dwIncr, wItemNum);
+ }
+
+ SendMessage(hWndList, LB_DELETESTRING, wItemNum, 0L);
+ dwIncr = SendMessage(hWndList, LB_GETITEMDATA, wItemNum, 0L);
+ Count--;
+ }
+
+ }
+
+} // CloseList
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+FileSelect_ActionItem(
+ HWND hWndList,
+ DWORD dwData,
+ WORD wItemNum
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ static TCHAR NewPath[MAX_PATH + 1];
+ DWORD Count = 0;
+ DIR_BUFFER *Dir;
+ DIR_LIST *DirList;
+ FILE_LIST *FileList;
+ ULONG i;
+
+ if (!dwData)
+ return;
+
+ // Is it open ?
+ if ( HierFile_IsOpened(&HierDrawStruct, dwData) ) {
+
+ // It's open ... Close it
+ Dir = (DIR_BUFFER *) dwData;
+ DirList = (DIR_LIST *) Dir->DirList;
+ CloseList(hWndList, Dir, wItemNum);
+ } else {
+
+ // It's closed ... Open it
+ HierFile_OpenItem(&HierDrawStruct, dwData);
+
+ SendMessage(hWndList, WM_SETREDRAW, FALSE, 0L); // Disable redrawing.
+
+ CursorHourGlass();
+
+ Dir = (DIR_BUFFER *) dwData;
+#ifdef DEBUG
+dprintf(TEXT("Opening dir: %s\r\n"), Dir->Name);
+#endif
+
+ DirList = (DIR_LIST *) Dir->parent;
+ if (DirList == NULL)
+ FillDir(1, Dir->Name, Dir, TRUE);
+ else {
+ // recurse backwards to create full path
+ lstrcpy(NewPath, TEXT(""));
+ RecursePath(NewPath, Dir);
+ FillDir(DirList->Level + 1, NewPath, Dir, TRUE);
+ }
+
+ DirList = NULL;
+
+ // Check if we have visited this node, if not allocate and fill it in.
+ if (Dir->DirList != NULL) {
+ DirList = (DIR_LIST *) Dir->DirList;
+ Count = DirList->Count;
+
+ for (i = 0; i < DirList->Count; i++) {
+ SendMessage(hWndList, LB_INSERTSTRING, (WPARAM) wItemNum + i + 1, (LPARAM) &DirList->DirBuffer[i]);
+ }
+
+ }
+
+#ifdef DEBUG
+if (Dir->FileList == NULL)
+ dprintf(TEXT("FileList NULL\r\n"));
+#endif
+
+ if (Dir->FileList != NULL) {
+ FileList = (FILE_LIST *) Dir->FileList;
+
+#ifdef DEBUG
+dprintf(TEXT("FileList Count: %li\r\n"), FileList->Count);
+#endif
+ SendMessage(hwndList2, LB_RESETCONTENT, (WPARAM) 0, (LPARAM) 0L);
+ for (i = 0; i < FileList->Count; i++) {
+ SendMessage(hwndList2, LB_ADDSTRING, (WPARAM) 0, (LPARAM) &FileList->FileBuffer[i]);
+ }
+
+ }
+
+ // Make sure as many child items as possible are showing
+ HierFile_ShowKids(&HierDrawStruct, hWndList, (WORD) wItemNum, (WORD) Count );
+
+ CursorNormal();
+ }
+
+ SendMessage(hWndList, WM_SETREDRAW, TRUE, 0L); // Enable redrawing.
+ InvalidateRect(hWndList, NULL, TRUE); // Force redraw
+
+} // FileSelect_ActionItem
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+TreeOpenRecurse(
+ DIR_BUFFER *Dir,
+ WORD *wItemNum
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ DIR_LIST *DirList = NULL;
+ ULONG i;
+
+ // if it's closed, open it
+ if ( !HierFile_IsOpened(&HierDrawStruct, (DWORD) Dir) )
+ FileSelect_ActionItem(hwndList1, (DWORD) Dir, *wItemNum);
+
+ DirList = Dir->DirList;
+ if (DirList != NULL) {
+
+ for (i = 0; i < DirList->Count; i++) {
+ (*wItemNum)++;
+ // Now recurse down the children
+ TreeOpenRecurse(&DirList->DirBuffer[i], wItemNum);
+ }
+ }
+
+} // TreeOpenRecurse
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+FileSelect_UpdateFiles(
+ HWND hDlg
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ static TCHAR NewPath[MAX_PATH + 1];
+ WORD wItemNum;
+ DWORD dwData;
+ HWND hCtrl;
+ ULONG i;
+ FILE_LIST *FileList;
+ DIR_BUFFER *Dir;
+ DIR_LIST *DirList;
+
+ hCtrl = GetDlgItem(hDlg, IDC_LIST1);
+ wItemNum = (WORD) SendMessage(hCtrl, LB_GETCURSEL, 0, 0L);
+
+ if (wItemNum == (WORD) LB_ERR)
+ wItemNum = LastFocus;
+
+ // Check first listbox and resynch the directory information so that we
+ // pick up the current file list.
+ LastFocus = wItemNum;
+ dwData = (DWORD) SendMessage(hCtrl, LB_GETITEMDATA, wItemNum, 0L);
+
+ if ((dwData == (DWORD) LB_ERR) || (dwData == 0))
+ return;
+
+ Dir = (DIR_BUFFER *) dwData;
+#ifdef DEBUG
+dprintf(TEXT("Opening dir: %lX %s\r\n"), Dir->parent, Dir->Name);
+#endif
+
+ DirList = (DIR_LIST *) Dir->parent;
+ if (DirList == NULL)
+ FillDir(1, Dir->Name, Dir, FALSE);
+ else {
+ // recurse backwards to create full path
+ lstrcpy(NewPath, TEXT(""));
+ RecursePath(NewPath, Dir);
+ FillDir(DirList->Level + 1, NewPath, Dir, FALSE);
+ }
+
+ // Since Dir pointer was changed need to update listbox pointer
+ SendMessage(hCtrl, LB_SETITEMDATA, wItemNum, (LPARAM) Dir);
+
+ // We have not re-synched the directory so we have the correct file info
+ // now reset the file listbox and fill it with the new file-list.
+ SendMessage(hwndList2, LB_RESETCONTENT, (WPARAM) 0, (LPARAM) 0L);
+
+ if (Dir->FileList != NULL) {
+ FileList = (FILE_LIST *) Dir->FileList;
+
+#ifdef DEBUG
+dprintf(TEXT("FileList Count: %li\r\n"), FileList->Count);
+#endif
+ for (i = 0; i < FileList->Count; i++)
+ SendMessage(hwndList2, LB_ADDSTRING, (WPARAM) 0, (LPARAM) &FileList->FileBuffer[i]);
+
+ }
+
+} // FileSelect_UpdateFiles
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+FileSelect_OnCommand(
+ HWND hDlg,
+ WPARAM wParam,
+ LPARAM lParam
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ static TCHAR NewPath[MAX_PATH + 1];
+ int wmId, wmEvent;
+ WORD wItemNum;
+ DWORD dwData;
+ HWND hCtrl;
+ DIR_LIST *DirList;
+ FILE_LIST *FileList;
+ DIR_BUFFER *Dir;
+ int nLevel = 0;
+
+ wmId = LOWORD(wParam);
+ wmEvent = HIWORD(wParam);
+
+ switch (wmId) {
+
+ case IDOK:
+ // If we are on either of the listboxes, then use the Enter key to
+ // activate the selected item
+ if (ListFocus == hwndList1) {
+ hCtrl = GetDlgItem(hDlg, IDC_LIST1);
+ wItemNum = (WORD) SendMessage(hCtrl, LB_GETCURSEL, 0, 0L);
+ dwData = (DWORD) SendMessage(hCtrl, LB_GETITEMDATA, wItemNum, 0L);
+
+ if ((wItemNum == (WORD) LB_ERR) || (dwData == LB_ERR) || (dwData == 0))
+ break;
+
+ FileSelect_ActionItem(hCtrl, dwData, wItemNum);
+ } else {
+ CurrentShare->HiddenFiles = HiddenFiles;
+ CurrentShare->SystemFiles = SystemFiles;
+
+ // Delete our copy of the tree and prune the tree down to minimum
+ // storage space
+ TreeDelete(oDir);
+ TreePrune(CurrentShare->Root);
+
+ EndDialog(hDlg, 0);
+ }
+
+ break;
+
+ case IDCANCEL:
+
+ // Disable listboxes since we are deleting their item data
+ SendDlgItemMessage(hDlg, IDC_LIST1, WM_SETREDRAW, FALSE, 0L);
+ SendDlgItemMessage(hDlg, IDC_LIST2, WM_SETREDRAW, FALSE, 0L);
+
+ // Get back our old tree
+ if (CurrentShare->Root != NULL)
+ TreeDelete(CurrentShare->Root);
+ CurrentShare->Root = oDir;
+
+ EndDialog(hDlg, 0);
+ break;
+
+ case IDHELP:
+ WinHelp(hDlg, HELP_FILE, HELP_CONTEXT, (DWORD) IDC_HELP_FTRANS);
+ break;
+
+ case IDM_EXP_ONE:
+ // expand the current node
+ hCtrl = GetDlgItem(hDlg, IDC_LIST1);
+ wItemNum = (WORD) SendMessage(hCtrl, LB_GETCURSEL, 0, 0L);
+ dwData = (DWORD) SendMessage(hCtrl, LB_GETITEMDATA, wItemNum, 0L);
+
+ if ((wItemNum == (WORD) LB_ERR) || (dwData == LB_ERR) || (dwData == 0))
+ break;
+
+ if ( !HierFile_IsOpened(&HierDrawStruct, dwData) )
+ FileSelect_ActionItem(hCtrl, dwData, wItemNum);
+
+ break;
+
+ case IDM_EXP_ALL:
+ // Open all children from the root
+ wItemNum = 0;
+ hCtrl = GetDlgItem(hDlg, IDC_LIST1);
+ goto ExpandTree;
+
+ case IDM_EXP_BRANCH:
+ // Traverse down the branch, opening up all children
+ hCtrl = GetDlgItem(hDlg, IDC_LIST1);
+ wItemNum = (WORD) SendMessage(hCtrl, LB_GETCURSEL, 0, 0L);
+
+ExpandTree:
+ dwData = (DWORD) SendMessage(hCtrl, LB_GETITEMDATA, wItemNum, 0L);
+ if ((wItemNum == (WORD) LB_ERR) || (dwData == LB_ERR) || (dwData == 0))
+ break;
+
+ Dir = (DIR_BUFFER *) dwData;
+ CursorHourGlass();
+
+ TreeOpenRecurse(Dir, &wItemNum);
+
+ CursorNormal();
+
+ // Force redraw
+ InvalidateRect(hwndList1, NULL, TRUE);
+ break;
+
+ case IDM_COLLAPSE:
+ // Close the current branch
+ hCtrl = GetDlgItem(hDlg, IDC_LIST1);
+ wItemNum = (WORD) SendMessage(hCtrl, LB_GETCURSEL, 0, 0L);
+ dwData = (DWORD) SendMessage(hCtrl, LB_GETITEMDATA, wItemNum, 0L);
+
+ if ((wItemNum == (WORD) LB_ERR) || (dwData == LB_ERR) || (dwData == 0))
+ break;
+
+ if ( HierFile_IsOpened(&HierDrawStruct, dwData) )
+ FileSelect_ActionItem(hCtrl, dwData, wItemNum);
+
+ break;
+
+ // Not used currently
+ case IDM_VIEW_BOTH:
+ case IDM_VIEW_TREE:
+ case IDM_VIEW_DIR:
+ break;
+
+ case IDM_HIDDEN:
+ HiddenFiles = !HiddenFiles;
+ CheckMenuItem((HMENU) wParam, IDM_HIDDEN, HiddenFiles ? MF_CHECKED : MF_UNCHECKED);
+ PostMessage(hDlg, WM_COMMAND, ID_REDRAWLIST, 0L);
+ break;
+
+ case IDM_SYSTEM:
+ SystemFiles = !SystemFiles;
+ CheckMenuItem((HMENU) wParam, IDM_SYSTEM, SystemFiles ? MF_CHECKED : MF_UNCHECKED);
+ PostMessage(hDlg, WM_COMMAND, ID_REDRAWLIST, 0L);
+ break;
+
+ case ID_REDRAWLIST:
+ FileSelect_UpdateFiles(hDlg);
+ break;
+
+ case IDC_LIST1:
+
+ switch (wmEvent) {
+ case LBN_SETFOCUS:
+ ListFocus = hwndList1;
+ break;
+
+ case LBN_KILLFOCUS:
+ ListFocus = NULL;
+ break;
+
+ case LBN_DBLCLK:
+ // Disregard the DBLCLK message if it is inside a checkbox.
+ hCtrl = GetDlgItem(hDlg, IDC_LIST1);
+
+ // First figure out where checkbox is located in listbox
+ wItemNum = (WORD) SendMessage(hCtrl, LB_GETCURSEL, 0, 0L);
+ dwData = (DWORD) SendMessage(hCtrl, LB_GETITEMDATA, wItemNum, 0L);
+
+ if ((wItemNum == (WORD) LB_ERR) || (dwData == LB_ERR) || (dwData == 0))
+ break;
+
+ Dir = (DIR_BUFFER *) dwData;
+ DirList = (DIR_LIST *) Dir->parent;
+
+ if (DirList != NULL)
+ nLevel = DirList->Level;
+
+ if (!HierFile_InCheck(nLevel, LOWORD(mouseHit), &HierDrawStruct)) {
+ CursorHourGlass();
+ FileSelect_ActionItem(hCtrl, dwData, wItemNum);
+ CursorNormal();
+ }
+
+ break;
+
+ case LBN_SELCHANGE:
+ FileSelect_UpdateFiles(hDlg);
+ break;
+
+ }
+
+ break;
+
+ case ID_UPDATELIST:
+ hCtrl = GetDlgItem(hDlg, IDC_LIST1);
+ if (CurrentShare->Root == NULL) {
+ wsprintf(NewPath, TEXT("\\\\%s\\%s\\"), SServ->Name, CurrentShare->Name);
+
+#ifdef DEBUG
+// lstrcpy(NewPath, TEXT("c:\\"));
+dprintf(TEXT("Root Path: %s\n"), NewPath);
+#endif
+ TreeRootInit(CurrentShare, NewPath);
+ }
+
+ PostMessage(hCtrl, LB_SETCURSEL, (WPARAM) 0, 0L);
+ LastFocus = 0;
+
+ DirList = (DIR_LIST *) CurrentShare->Root->DirList;
+ FileList = (FILE_LIST *) CurrentShare->Root->FileList;
+
+ wItemNum = (WORD) SendMessage(hCtrl, LB_ADDSTRING, (WPARAM) 0, (LPARAM) CurrentShare->Root);
+ PostMessage(hDlg, WM_COMMAND, ID_REDRAWLIST, 0L);
+
+ break;
+
+ }
+
+} // FileSelect_OnCommand
+
+
+/////////////////////////////////////////////////////////////////////////
+LRESULT CALLBACK
+FileSelectSubClassProc(
+ HWND hWnd,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam
+ )
+
+/*++
+
+Routine Description:
+
+ Handles key processing for the hierarchical listbox. Specifically
+ the up/down arrow keys and the letter keys.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ LRESULT lResult = 0;
+ BOOL fCallOrigProc = TRUE;
+ WORD wItemNum, wNewNum;
+ DWORD dwData;
+ DIR_BUFFER *Dir;
+ DIR_LIST *DirList;
+ FILE_BUFFER *File;
+ int nLevel = 0;
+ int lHeight, ListHeight, PageCount;
+
+ RECT rc;
+
+ switch (message) {
+ case WM_LBUTTONDOWN:
+ // Send message to check if button-down is within our checkbox
+ wItemNum = (WORD) SendMessage(hWnd, LB_GETCURSEL, 0, 0L);
+
+ // Save the location off so we can check it during a DBLCLK message
+ // to see if we are inside a checkbox.
+ mouseHit = lParam;
+
+ // Post the message so the current selection is correct
+ PostMessage(hWnd, WM_COMMAND, ID_CHECKCHECK, lParam);
+ break;
+
+ case WM_COMMAND:
+ // Code for handling checkbox clicking
+ if (wParam == ID_CHECKCHECK) {
+ // First figure out where checkbox is located in listbox
+ wItemNum = (WORD) SendMessage(hWnd, LB_GETCURSEL, 0, 0L);
+ dwData = (DWORD) SendMessage(hWnd, LB_GETITEMDATA, wItemNum, 0L);
+
+ if (wItemNum == (WORD) LB_ERR)
+ break;
+
+ if (hWnd == hwndList2) {
+ File = (FILE_BUFFER *) dwData;
+
+ if (HierFile_InCheck(nLevel, LOWORD(lParam), &HierDrawStruct)) {
+ if (File->Convert == CONVERT_NONE)
+ File->Convert = CONVERT_ALL;
+ else
+ File->Convert = CONVERT_NONE;
+
+ DirAdjustConvert(File->parent->parent);
+ }
+
+ } else {
+ Dir = (DIR_BUFFER *) dwData;
+ DirList = (DIR_LIST *) Dir->parent;
+
+ if (DirList != NULL)
+ nLevel = DirList->Level;
+
+ if (HierFile_InCheck(nLevel, LOWORD(lParam), &HierDrawStruct)) {
+ if (Dir->Convert == CONVERT_NONE)
+ DirAdjustConvertChildren(Dir, CONVERT_ALL);
+ else
+ DirAdjustConvertChildren(Dir, CONVERT_NONE);
+
+ DirAdjustConvert(Dir);
+ }
+ }
+
+ // Now check if button click was within checkbox boundaries...
+ if (HierFile_InCheck(nLevel, LOWORD(lParam), &HierDrawStruct)) {
+
+ // We have set the checkbox state correctly on the current item,
+ // and propageted this up and down the tree as necessary, now
+ // update the screen to reflect these changes.
+ InvalidateRect(hWnd, NULL, TRUE); // Force redraw
+
+ // if this is the directory listbox then also need to redraw
+ // the file listbox
+ if (hWnd == hwndList1)
+ InvalidateRect(hwndList2, NULL, TRUE);
+
+ }
+ }
+
+ break;
+
+ case WM_KEYDOWN:
+ wItemNum = (WORD) SendMessage(hWnd, LB_GETCURSEL, 0, 0L);
+ if (wItemNum != (WORD) LB_ERR)
+ dwData = (DWORD) SendMessage(hWnd, LB_GETITEMDATA, wItemNum, 0L);
+ else {
+ wItemNum = 0;
+ dwData = (DWORD) SendMessage(hWnd, LB_GETITEMDATA, wItemNum, 0L);
+ }
+
+ if ((wItemNum == (WORD) LB_ERR) || (dwData == LB_ERR) || (dwData == 0))
+ break;
+
+ fCallOrigProc = FALSE;
+
+ switch (LOWORD(wParam)) {
+
+ case VK_PRIOR:
+ // Need to figure out the number of items to page. This
+ // would be the listbox height divided by the height of
+ // a listbox item
+ lHeight = SendMessage(hWnd, LB_GETITEMHEIGHT, 0, 0L);
+ GetWindowRect(hWnd, &rc);
+ ListHeight = (rc.bottom - rc.top);
+
+ if (ListHeight)
+ PageCount = ListHeight / lHeight;
+ else
+ PageCount = 0;
+
+ // See if we page past the top - if so adjust it.
+ if (wItemNum > PageCount)
+ wNewNum = wItemNum - PageCount;
+ else
+ wNewNum = 0;
+
+ PostMessage(hWnd, LB_SETCURSEL, (WPARAM) wNewNum, 0L);
+
+ if (hWnd == hwndList1)
+ PostMessage(GetParent(hWnd), WM_COMMAND, ID_REDRAWLIST, 0L);
+
+ break;
+
+ case VK_NEXT:
+ // Need to figure out the number of items to page. This
+ // would be the listbox height divided by the height of
+ // a listbox item
+ lHeight = SendMessage(hWnd, LB_GETITEMHEIGHT, 0, 0L);
+ GetWindowRect(hWnd, &rc);
+ ListHeight = (rc.bottom - rc.top);
+
+ if (ListHeight)
+ PageCount = ListHeight / lHeight;
+ else
+ PageCount = 0;
+
+ // Figure out if we page past the end - if so adjust it
+ wItemNum = wItemNum + PageCount;
+ wNewNum = (WORD) SendMessage(hWnd, LB_GETCOUNT, (WPARAM) 0, 0L);
+
+ if (wItemNum < wNewNum)
+ PostMessage(hWnd, LB_SETCURSEL, (WPARAM) wItemNum, 0L);
+ else
+ PostMessage(hWnd, LB_SETCURSEL, (WPARAM) (wNewNum - 1), 0L);
+
+ if (hWnd == hwndList1)
+ PostMessage(GetParent(hWnd), WM_COMMAND, ID_REDRAWLIST, 0L);
+
+ break;
+
+ case VK_END:
+ wItemNum = (WORD) SendMessage(hWnd, LB_GETCOUNT, (WPARAM) 0, 0L);
+
+ if (wItemNum != (WORD) LB_ERR)
+ PostMessage(hWnd, LB_SETCURSEL, (WPARAM) (wItemNum - 1), 0L);
+
+ if (hWnd == hwndList1)
+ PostMessage(GetParent(hWnd), WM_COMMAND, ID_REDRAWLIST, 0L);
+
+ break;
+
+ case VK_HOME:
+ PostMessage(hWnd, LB_SETCURSEL, (WPARAM) 0, 0L);
+
+ if (hWnd == hwndList1)
+ PostMessage(GetParent(hWnd), WM_COMMAND, ID_REDRAWLIST, 0L);
+
+ break;
+
+ case VK_UP:
+ if (wItemNum > 0) {
+ wItemNum--;
+ PostMessage(hWnd, LB_SETCURSEL, (WPARAM) wItemNum, 0L);
+
+ if (hWnd == hwndList1)
+ PostMessage(GetParent(hWnd), WM_COMMAND, ID_REDRAWLIST, 0L);
+
+ }
+ break;
+
+ case VK_DOWN:
+ wItemNum++;
+ PostMessage(hWnd, LB_SETCURSEL, (WPARAM) wItemNum, 0L);
+
+ if (hWnd == hwndList1)
+ PostMessage(GetParent(hWnd), WM_COMMAND, ID_REDRAWLIST, 0L);
+
+ break;
+
+ case VK_F1:
+ fCallOrigProc = TRUE;
+ break;
+
+ case VK_SPACE:
+ // First figure out where checkbox is located in listbox
+ wItemNum = (WORD) SendMessage(hWnd, LB_GETCURSEL, 0, 0L);
+ dwData = (DWORD) SendMessage(hWnd, LB_GETITEMDATA, wItemNum, 0L);
+
+ if (wItemNum == (WORD) LB_ERR)
+ break;
+
+ if (hWnd == hwndList2) {
+ File = (FILE_BUFFER *) dwData;
+
+ if (File->Convert == CONVERT_NONE)
+ File->Convert = CONVERT_ALL;
+ else
+ File->Convert = CONVERT_NONE;
+
+ DirAdjustConvert(File->parent->parent);
+ } else {
+ Dir = (DIR_BUFFER *) dwData;
+ DirList = (DIR_LIST *) Dir->parent;
+
+ if (DirList != NULL)
+ nLevel = DirList->Level;
+
+ if (Dir->Convert == CONVERT_NONE)
+ DirAdjustConvertChildren(Dir, CONVERT_ALL);
+ else
+ DirAdjustConvertChildren(Dir, CONVERT_NONE);
+
+ DirAdjustConvert(Dir);
+ }
+
+ // We have set the checkbox state correctly on the current item,
+ // and propageted this up and down the tree as necessary, now
+ // update the screen to reflect these changes.
+ InvalidateRect(hWnd, NULL, TRUE); // Force redraw
+
+ // if this is the directory listbox then also need to redraw
+ // the file listbox
+ if (hWnd == hwndList1)
+ InvalidateRect(hwndList2, NULL, TRUE);
+
+ break;
+
+ default:
+ break;
+
+ }
+
+ break;
+
+ }
+
+ if (fCallOrigProc)
+ if (hWnd == hwndList2)
+ lResult = CallWindowProc(_wpOrigWndProc2, hWnd, message, wParam, lParam);
+ else
+ lResult = CallWindowProc(_wpOrigWndProc, hWnd, message, wParam, lParam);
+
+ return (lResult);
+
+} // FileSelectSubClassProc
+
+
+/////////////////////////////////////////////////////////////////////////
+LRESULT CALLBACK
+DlgFileSelect(
+ HWND hDlg,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ int xPos;
+ int yPos;
+ BOOL InRange;
+ RECT rc;
+ HWND hCtrl;
+
+ switch (message) {
+ case WM_INITDIALOG:
+ // Copy the tree
+ oDir = CurrentShare->Root;
+ CurrentShare->Root = TreeCopy(oDir);
+
+ // Center the dialog over the application window
+ CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
+ GetClientRect(hDlg, &rc);
+ ControlsResize(hDlg, (rc.bottom - rc.top), (rc.right - rc.left));
+
+ // subclass listbox handler
+ hCtrl = GetDlgItem(hDlg, IDC_LIST1);
+ _wpOrigWndProc = SubclassWindow(hCtrl, FileSelectSubClassProc);
+ hCtrl = GetDlgItem(hDlg, IDC_LIST2);
+ _wpOrigWndProc2 = SubclassWindow(hCtrl, FileSelectSubClassProc);
+
+ FillDirInit();
+
+ hwndList2 = hCtrl = GetDlgItem(hDlg, IDC_LIST2);
+ hwndList1 = GetDlgItem(hDlg, IDC_LIST1);
+
+ // Fill listbox and set selection (is assumed there is always a selection)...
+ PostMessage(hDlg, WM_COMMAND, ID_UPDATELIST, 0L);
+ return (TRUE);
+
+ case WM_INITMENU:
+ if (GetMenu(hDlg) != (HMENU) wParam)
+ break;
+
+ CheckMenuItem((HMENU) wParam, IDM_HIDDEN, HiddenFiles ? MF_CHECKED : MF_UNCHECKED);
+ CheckMenuItem((HMENU) wParam, IDM_SYSTEM, SystemFiles ? MF_CHECKED : MF_UNCHECKED);
+ break;
+
+ case WM_SIZE:
+ ControlsResize(hDlg, HIWORD(lParam), LOWORD(lParam));
+ break;
+
+ case WM_SETFONT:
+ // Set the text height
+ HierFile_DrawSetTextHeight(GetDlgItem(hDlg, IDC_LIST1), (HFONT)wParam, &HierDrawStruct);
+ break;
+
+ case WM_DRAWITEM:
+ FileSelect_OnDrawItem(hDlg, (DRAWITEMSTRUCT FAR*)(lParam));
+ return TRUE;
+
+ case WM_MEASUREITEM:
+ HierFile_OnMeasureItem(hDlg, (MEASUREITEMSTRUCT FAR*)(lParam), &HierDrawStruct);
+ return TRUE;
+
+ case WM_MOUSEMOVE: {
+ xPos = LOWORD(lParam);
+ yPos = HIWORD(lParam);
+
+ InRange = TRUE;
+
+ // Check if it is correct Y-coordinate
+ if ((yPos <= Y_BORDER) || (yPos >= Splitter_Bottom))
+ InRange = FALSE;
+
+ // Now Check X-coordinate
+ if ((xPos < Splitter_Left) || (xPos > (Splitter_Left + X_BORDER)))
+ InRange = FALSE;
+
+ // Is within range of splitter, so handle it...
+ }
+ break;
+
+ case WM_COMMAND:
+ FileSelect_OnCommand(hDlg, wParam, lParam);
+ break;
+ }
+
+ return (FALSE); // Didn't process the message
+
+ lParam;
+} // DlgFileSelect
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+FileSelect_Do(
+ HWND hDlg,
+ SOURCE_SERVER_BUFFER *SourceServ,
+ SHARE_BUFFER *CShare
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ DLGPROC lpfnDlg;
+
+ TreeRecurseCurrentShareSet(CShare);
+ SServ = SourceServ;
+
+ HiddenFiles = CurrentShare->HiddenFiles;
+ SystemFiles = CurrentShare->SystemFiles;
+
+ // Init the Hier Draw stuff - Need to do this here so we have a value
+ // for WM_MEASUREITEM which is sent before the WM_INITDIALOG message
+ HierFile_DrawInit(hInst, IDR_FILEICONS, IDR_CHECKICONS, ROWS, COLS, TRUE, &HierDrawStruct, TRUE );
+
+ lpfnDlg = MakeProcInstance((DLGPROC)DlgFileSelect, hInst);
+ DialogBox(hInst, TEXT("FileSelect"), hDlg, lpfnDlg) ;
+ FreeProcInstance(lpfnDlg);
+
+ HierFile_DrawTerm(&HierDrawStruct);
+
+} // FileSelect_Do
diff --git a/private/nw/convert/nwconv/filesel.h b/private/nw/convert/nwconv/filesel.h
new file mode 100644
index 000000000..f09ac1676
--- /dev/null
+++ b/private/nw/convert/nwconv/filesel.h
@@ -0,0 +1,130 @@
+/*+-------------------------------------------------------------------------+
+ | Copyright 1993-1994 (C) Microsoft Corporation - All rights reserved. |
+ +-------------------------------------------------------------------------+*/
+
+#ifndef _HFILESEL_
+#define _HFILESEL_
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+// Forward references as the structures are recursively linked
+struct _DIR_BUFFER;
+struct _DIR_LIST;
+struct _FILE_BUFFER;
+struct _FILE_LIST;
+struct SHARE_BUFFER;
+
+/*+-------------------------------------------------------------------------+
+ |
+ | The dir/file lists contain all the information used when working with
+ | a file tree. The naming convention is that a buffer (I.E.
+ | DIR_BUFFER) contains the information for one entry (one directory
+ | or one file). A List is an array of buffers of the appropriate
+ | type (DIR_LIST contains an array of DIR_BUFFERS).
+ |
+ | The whole mess starts with a root DIR_BUFFER, the DIR_BUFFER then
+ | points to a cascading chain of DIR and FILE LISTS.
+ |
+ | Almost all of the structures are a doubly linked list with a pointer back
+ | to their parent. A buffer parent pointer, points to it's parent list
+ | structure. The List structure then has a back pointer to the parent
+ | DIR_BUFFER. This facilitates recursing up and down the chain when
+ | an item is checked/un-checked and it's parent and/or children are affected.
+ |
+ | +--------+ +----------+
+ | | Dir |<--->| Dir List |
+ | | Buffer |<-+ +----------+
+ | +--------+ | | Dir |-->Dir List...
+ | | | Buffer |-->File List...
+ | | + - - - - -+
+ | | | |
+ | | + - - - - -+
+ | | | |
+ | |
+ | |
+ | | +-----------+
+ | +->| File List |
+ | +-----------+
+ | | File |
+ | | Buffer |
+ | + - - - - - +
+ | | |
+ | + - - - - - +
+ | | |
+ |
+ +-------------------------------------------------------------------------+*/
+#define CONVERT_NONE 0
+#define CONVERT_ALL 1
+#define CONVERT_PARTIAL 2
+
+// A dir buffer holds a directory or sub-directory entry, with pointers to the
+// files and other dirs within it.
+typedef struct _DIR_BUFFER {
+ TCHAR Name[MAX_PATH];
+ struct _DIR_LIST *parent;
+ BOOL Last; // Flag is last dir-buffer in list
+ DWORD Attributes;
+ BYTE Convert; // None, All or Partial
+ BOOL Special;
+
+ struct _DIR_LIST *DirList; // Directory List structure
+ struct _FILE_LIST *FileList; // File List structure
+} DIR_BUFFER;
+
+// A dir list contains the sub-directories in a directory - basically a count
+// and then array of sub-dirs.
+typedef struct _DIR_LIST {
+ ULONG Count;
+ DIR_BUFFER *parent;
+ UINT Level; // how deeply nested in file tree (nesting level)
+ DIR_BUFFER DirBuffer[];
+} DIR_LIST;
+
+
+// Structures to hold information on individual files selected/de-selected for
+// conversion
+typedef struct _FILE_BUFFER {
+ TCHAR Name[MAX_PATH];
+ struct _FILE_LIST *parent;
+ BOOL Convert;
+ DWORD Attributes;
+ ULONG Size;
+} FILE_BUFFER;
+
+typedef struct _FILE_LIST {
+ ULONG Count;
+ DIR_BUFFER *parent;
+ FILE_BUFFER FileBuffer[];
+} FILE_LIST;
+
+
+typedef struct _FILE_PATH_BUFFER {
+ LPTSTR Server;
+ LPTSTR Share;
+ LPTSTR Path;
+ TCHAR FullPath[MAX_PATH + 1];
+} FILE_PATH_BUFFER;
+
+
+/*+-------------------------------------------------------------------------+
+ | Function Prototypes |
+ +-------------------------------------------------------------------------+*/
+void TreeDelete(DIR_BUFFER *Dir);
+void TreePrune(DIR_BUFFER *Dir);
+ULONG TreeCount(DIR_BUFFER *Dir);
+DIR_BUFFER *TreeCopy(DIR_BUFFER *Dir);
+
+FILE_PATH_BUFFER *FilePathInit();
+void FilePathServerSet(FILE_PATH_BUFFER *fpBuf, LPTSTR Server);
+void FilePathShareSet(FILE_PATH_BUFFER *fpBuf, LPTSTR Share);
+void FilePathPathSet(FILE_PATH_BUFFER *fpBuf, LPTSTR Path);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+
diff --git a/private/nw/convert/nwconv/fpnwapi.h b/private/nw/convert/nwconv/fpnwapi.h
new file mode 100644
index 000000000..a5d7b372d
--- /dev/null
+++ b/private/nw/convert/nwconv/fpnwapi.h
@@ -0,0 +1,76 @@
+/*+-------------------------------------------------------------------------+
+ | Copyright 1993-1994 (C) Microsoft Corporation - All rights reserved. |
+ +-------------------------------------------------------------------------+*/
+
+// Munged needed defines from FPNW code - fpnwapi.h file
+
+#ifndef _FPNWAPI_H_
+#define _FPNWAPI_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+//
+// Volume types : disk or printer
+//
+
+#define NWVOL_TYPE_DISKTREE FPNWVOL_TYPE_DISKTREE
+#define NWVOL_TYPE_CDROM FPNWVOL_TYPE_CDROM
+#define NWVOL_TYPE_REMOVABLE FPNWVOL_TYPE_REMOVABLE
+
+#define NWVOL_MAX_USES_UNLIMITED ((ULONG)-1)
+
+//
+// Volume flags returned by VolumeGetInfo
+//
+
+#define FPNWVOL_TYPE_DISKTREE 0
+#define FPNWVOL_TYPE_CDROM 104
+#define FPNWVOL_TYPE_REMOVABLE 105
+
+//
+// Permissions flags returned in structure FPNWFILEINFO
+//
+
+#define FPNWFILE_PERM_NONE 0
+#define FPNWFILE_PERM_READ 0x01
+#define FPNWFILE_PERM_WRITE 0x02
+#define FPNWFILE_PERM_CREATE 0x04
+#define FPNWFILE_PERM_EXEC 0x08
+#define FPNWFILE_PERM_DELETE 0x10
+#define FPNWFILE_PERM_ATRIB 0x20
+#define FPNWFILE_PERM_PERM 0x40
+
+typedef BYTE FPNWSERVERADDR[12]; // Network address, first 4 bytes is
+ // the network number, and bytes
+ // 5-10 is the physical node
+ // address. The last two bytes are
+ // reserved.
+
+//
+// This is the level 1 structure for FpnwVolumeAdd, FpnwVolumeDel, FpnwVolumeEnum,
+// FpnwVolumeGetInfo, & FpnwVolumeSetInfo.
+//
+
+typedef struct _FPNWVolumeInfo
+{
+ LPWSTR lpVolumeName; // Name of the volume
+ DWORD dwType; // The type of the volume. It can be one of the
+ // following: FPNWVOL_TYPE_DISK, FPNWVOL_TYPE_PRINT
+ DWORD dwMaxUses; // Maximum number of connections that are
+ // allowed to the volume
+ DWORD dwCurrentUses; // Current number of connections to the volume
+ LPWSTR lpPath; // Path of the volume
+
+} FPNWVOLUMEINFO, *PFPNWVOLUMEINFO;
+
+typedef FPNWVOLUMEINFO NWVOLUMEINFO, *PNWVOLUMEINFO;
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
+
+
diff --git a/private/nw/convert/nwconv/globals.h b/private/nw/convert/nwconv/globals.h
new file mode 100644
index 000000000..d641ed67c
--- /dev/null
+++ b/private/nw/convert/nwconv/globals.h
@@ -0,0 +1,48 @@
+/*+-------------------------------------------------------------------------+
+ | Copyright 1993-1994 (C) Microsoft Corporation - All rights reserved. |
+ +-------------------------------------------------------------------------+*/
+
+#ifndef _HGLOBALS_
+#define _HGLOBALS_
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+#include "switches.h"
+#include "constant.h"
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <windowsx.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <winnetwk.h>
+#include <lm.h>
+#include <commdlg.h>
+
+#include "resource.h"
+#include "debug.h"
+#include "nwlog.h"
+#include "tab.h"
+#include "mem.h"
+#include "error.h"
+#include "strings.h"
+#include "utils.h"
+
+extern HINSTANCE hInst;
+
+#define XCHG(x) MAKEWORD( HIBYTE(x), LOBYTE(x) )
+#define DXCHG(x) MAKELONG( XCHG(HIWORD(x)), XCHG(LOWORD(x)) )
+#define SWAPBYTES(w) ((w) = XCHG(w))
+#define SWAPWORDS(d) ((d) = DXCHG(d))
+
+#define HELP_FILE TEXT("NWConv.HLP")
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/nw/convert/nwconv/helpid.h b/private/nw/convert/nwconv/helpid.h
new file mode 100644
index 000000000..9ade43cc9
--- /dev/null
+++ b/private/nw/convert/nwconv/helpid.h
@@ -0,0 +1,70 @@
+/*+-------------------------------------------------------------------------+
+ | Copyright 1993-1994 (C) Microsoft Corporation - All rights reserved. |
+ +-------------------------------------------------------------------------+*/
+
+#ifndef _HHELPID_
+#define _HHELPID_
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+// Identifiers for help contexts
+
+// Main dialog box
+#define IDM_HELP_OPEN 500
+#define IDM_HELP_INDEX 501
+#define IDM_HELP_USING 502
+#define IDC_HELP_MAIN 503
+
+#define IDM_HELP_RCONFIG 504
+#define IDM_HELP_SCONFIG 505
+#define IDM_HELP_RDCONFIG 506
+#define IDM_HELP_EXIT 507
+
+#define IDM_HELP_HELPABOUT 508
+
+// Select Servers for Conversion & Server Browse
+#define IDC_HELP_ADD 520
+#define IDC_HELP_BROWSENW 521
+#define IDC_HELP_BROWSENT 522
+
+// Logging dialog box
+#define IDC_HELP_LOGGING 530
+
+// User Options
+#define IDC_HELP_USER 540
+#define IDC_HELP_USERADV 541
+#define IDC_HELP_TRUSTED 542
+
+// File Options
+#define IDC_HELP_FILE 550
+
+// Add/Modify Share
+#define IDC_HELP_SHAREADD 560
+#define IDC_HELP_SHAREMOD 561
+#define IDC_HELP_SHARENEW 562
+#define IDC_HELP_SHAREPROP 563
+
+// Files to Transfer
+#define IDC_HELP_FTRANS 570
+#define IDM_HELP_EXPL 571
+#define IDM_HELP_EXPB 572
+#define IDM_HELP_EXPA 573
+#define IDM_HELP_COLLAPSE 574
+#define IDM_HELP_VTDIR 575
+#define IDM_HELP_VTREE 576
+#define IDM_HELP_VDIR 577
+#define IDM_HELP_SPLIT 578
+#define IDM_HELP_THIDDEN 579
+#define IDM_HELP_TSYSTEM 580
+#define IDM_HELP_TKEEP 581
+
+// Mapping Create
+#define IDC_HELP_CMAP 590
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/nw/convert/nwconv/hierchk.bmp b/private/nw/convert/nwconv/hierchk.bmp
new file mode 100644
index 000000000..27af032f3
--- /dev/null
+++ b/private/nw/convert/nwconv/hierchk.bmp
Binary files differ
diff --git a/private/nw/convert/nwconv/hierdraw.c b/private/nw/convert/nwconv/hierdraw.c
new file mode 100644
index 000000000..7fe150924
--- /dev/null
+++ b/private/nw/convert/nwconv/hierdraw.c
@@ -0,0 +1,403 @@
+#include <windows.h>
+#include <windowsx.h>
+#include <malloc.h>
+#include <string.h>
+
+#include "hierdraw.h"
+
+
+VOID HierDraw_DrawTerm(LPHEIRDRAWSTRUCT lpHierDrawStruct) {
+ if (lpHierDrawStruct->hbmIcons) {
+ if (lpHierDrawStruct->hbmMem)
+ SelectObject(lpHierDrawStruct->hdcMem,lpHierDrawStruct->hbmMem);
+ lpHierDrawStruct->hbmMem = NULL;
+ DeleteObject(lpHierDrawStruct->hbmIcons);
+ lpHierDrawStruct->hbmIcons = NULL;
+ }
+
+ if ( lpHierDrawStruct->hdcMem ) {
+ DeleteDC(lpHierDrawStruct->hdcMem);
+ lpHierDrawStruct->hdcMem = NULL;
+ }
+} // HierDraw_DrawTerm
+
+VOID HierDraw_DrawCloseAll(LPHEIRDRAWSTRUCT lpHierDrawStruct ) {
+ lpHierDrawStruct->NumOpened= 0;
+ if ( lpHierDrawStruct->Opened ) {
+ _ffree(lpHierDrawStruct->Opened);
+ }
+ lpHierDrawStruct->Opened = NULL;
+} // HierDraw_DrawCloseAll
+
+VOID HierDraw_OnMeasureItem(HWND hwnd, MEASUREITEMSTRUCT FAR* lpMeasureItem,
+ LPHEIRDRAWSTRUCT lpHierDrawStruct) {
+ lpMeasureItem->itemHeight = max(lpHierDrawStruct->nBitmapHeight,
+ lpHierDrawStruct->nTextHeight);
+} // HierDraw_OnMeasureItem
+
+VOID HierDraw_DrawSetTextHeight (HWND hwndList, HFONT hFont, LPHEIRDRAWSTRUCT lpHierDrawStruct ) {
+ TEXTMETRIC TextMetrics;
+ HANDLE hOldFont=NULL;
+ HDC hdc;
+
+ //
+ // This sure looks like a lot of work to find the character height
+ //
+ hdc = GetDC(hwndList);
+
+ hOldFont = SelectObject(hdc, hFont);
+ GetTextMetrics(hdc, &TextMetrics);
+ SelectObject(hdc, hOldFont);
+ ReleaseDC(hwndList, hdc);
+
+ lpHierDrawStruct->nTextHeight = TextMetrics.tmHeight;
+
+ lpHierDrawStruct->nLineHeight =
+ max(lpHierDrawStruct->nBitmapHeight, lpHierDrawStruct->nTextHeight);
+
+ if ( hwndList != NULL )
+ SendMessage(hwndList, LB_SETITEMHEIGHT, 0,
+ MAKELPARAM(lpHierDrawStruct->nLineHeight, 0));
+} // HierDraw_DrawSetTextHeight
+
+static DWORD near RGB2BGR(DWORD rgb) {
+ return RGB(GetBValue(rgb),GetGValue(rgb),GetRValue(rgb));
+} // RGB2BGR
+
+/*
+ * Creates the objects used while drawing the tree. This may be called
+ * repeatedly in the event of a WM_SYSCOLORCHANGED message.
+ *
+ * WARNING: the Tree icons bitmap is assumed to be a 16 color DIB!
+ */
+
+BOOL HierDraw_DrawInit(HINSTANCE hInstance,
+ int nBitmap,
+ int nRows,
+ int nColumns,
+ BOOL bLines,
+ LPHEIRDRAWSTRUCT lpHierDrawStruct,
+ BOOL bInit) {
+ HANDLE hRes;
+ HANDLE hResMem;
+ LPBITMAPINFOHEADER lpbiReadOnly;
+ LPBITMAPINFOHEADER lpbiReadWrite;
+ DWORD *lpColorTable;
+ LPSTR lpBits;
+ int biSize;
+ int bc;
+ HDC hDC;
+
+ if ( bInit ) {
+ lpHierDrawStruct->NumOpened = 0;
+ lpHierDrawStruct->Opened = NULL;
+ lpHierDrawStruct->bLines = bLines;
+ }
+
+ //
+ // If the Memory DC is not created yet do that first.
+ //
+ if (!lpHierDrawStruct->hdcMem) {
+ //
+ // get a screen DC
+ //
+ hDC = GetDC(NULL);
+ //
+ // Create a memory DC compatible with the screen
+ //
+ lpHierDrawStruct->hdcMem = CreateCompatibleDC(hDC);
+ //
+ // Release the Screen DC
+ ReleaseDC(NULL,hDC);
+
+ if (!lpHierDrawStruct->hdcMem)
+ return FALSE;
+
+ lpHierDrawStruct->hbmMem = NULL;
+ }
+
+ //
+ // (Re)Load the Bitmap ( original from disk )
+ //
+ // Use the FindResource,LoadResource,LockResource since it makes
+ // it easy to get the pointer to the BITMAPINFOHEADER we need.
+ //
+ //
+ hRes = FindResource(hInstance, MAKEINTRESOURCE(nBitmap), RT_BITMAP);
+ if (!hRes)
+ return FALSE;
+
+ hResMem = LoadResource(hInstance, hRes);
+ if (!hResMem)
+ return FALSE;
+
+ // Now figure out the bitmaps background color.
+ // This code assumes the lower left corner is a
+ // bit in the background color.
+ lpbiReadOnly = (LPBITMAPINFOHEADER)LockResource(hResMem);
+ if (!lpbiReadOnly)
+ return FALSE;
+
+ // Determine size of bitmap information header plus color table entries
+ biSize = lpbiReadOnly->biSize + ((1 << (lpbiReadOnly->biBitCount)) * sizeof(RGBQUAD));
+
+ // Allocate copy of the bitmap information to munge on
+ lpbiReadWrite = (LPBITMAPINFOHEADER)GlobalAlloc(GPTR, biSize);
+ if (!lpbiReadWrite)
+ return FALSE;
+
+ memcpy(lpbiReadWrite, lpbiReadOnly, biSize);
+
+ // Color table immediately follows bitmap information header
+ lpColorTable = (DWORD FAR *)((LPBYTE)lpbiReadWrite + lpbiReadWrite->biSize);
+
+ // No need to munge bits so use original
+ lpBits = (LPBYTE)lpbiReadOnly + biSize;
+
+ bc = (lpBits[0] & 0xF0) >> 4; // ASSUMES LOWER LEFT CORNER IS BG!!
+
+ lpColorTable[bc] = RGB2BGR(GetSysColor(COLOR_WINDOW));
+
+ hDC = GetDC(NULL);
+
+ lpHierDrawStruct->hbmIcons = CreateDIBitmap(
+ hDC,
+ lpbiReadWrite,
+ CBM_INIT,
+ lpBits,
+ (LPBITMAPINFO)lpbiReadWrite,
+ DIB_RGB_COLORS
+ );
+
+ ReleaseDC(NULL,hDC);
+
+ lpHierDrawStruct->nBitmapHeight = (WORD)lpbiReadWrite->biHeight / nRows;
+ lpHierDrawStruct->nBitmapWidth = (WORD)lpbiReadWrite->biWidth / nColumns;
+
+ lpHierDrawStruct->nLineHeight =
+ max(lpHierDrawStruct->nBitmapHeight, lpHierDrawStruct->nTextHeight);
+
+ GlobalFree(lpbiReadWrite);
+ UnlockResource(hResMem);
+ FreeResource(hResMem);
+
+ if (!lpHierDrawStruct->hbmIcons)
+ return FALSE;
+
+ lpHierDrawStruct->hbmMem = SelectObject(lpHierDrawStruct->hdcMem,lpHierDrawStruct->hbmIcons);
+ if (!lpHierDrawStruct->hbmMem)
+ return FALSE;
+
+ return TRUE;
+} // HierDraw_DrawInit
+
+
+
+VOID HierDraw_OnDrawItem(HWND hwnd,
+ const DRAWITEMSTRUCT FAR* lpDrawItem,
+ int nLevel,
+ DWORD dwConnectLevel,
+ TCHAR *szText,
+ int nRow,
+ int nColumn,
+ LPHEIRDRAWSTRUCT lpHierDrawStruct) {
+ HDC hDC;
+ WORD wIndent, wTopBitmap, wTopText;
+ RECT rcTemp;
+
+
+ if ( lpDrawItem->itemID == (UINT)-1 )
+ return ;
+
+ hDC = lpDrawItem->hDC;
+ CopyRect(&rcTemp, &lpDrawItem->rcItem);
+
+ wIndent = rcTemp.left + ((int)(nLevel) * lpHierDrawStruct->nBitmapWidth) + XBMPOFFSET;
+ rcTemp.left = wIndent + lpHierDrawStruct->nBitmapWidth;
+ wTopText = rcTemp.top + ((rcTemp.bottom - rcTemp.top) / 2) - (lpHierDrawStruct->nTextHeight / 2);
+ wTopBitmap = rcTemp.top + ((rcTemp.bottom - rcTemp.top) / 2) - (lpHierDrawStruct->nBitmapHeight / 2);
+
+ if (lpDrawItem->itemAction == ODA_FOCUS)
+ goto DealWithFocus;
+ else if (lpDrawItem->itemAction == ODA_SELECT)
+ goto DealWithSelection;
+
+ //
+ // Draw some lions, if we like lions
+ //
+
+ if (lpHierDrawStruct->bLines && nLevel)
+ {
+ DWORD dwMask = 1;
+ int nTempLevel;
+ int x,y;
+
+ // draw lines in text color
+ SetBkColor(hDC,GetSysColor(COLOR_WINDOWTEXT));
+
+ //
+ // Draw a series of | lines for outer levels
+ //
+
+ x = lpHierDrawStruct->nBitmapWidth/2 + XBMPOFFSET;
+
+ for ( nTempLevel = 0; nTempLevel < nLevel ; nTempLevel++)
+ {
+ if ( dwConnectLevel & dwMask )
+ FastRect(hDC,x,rcTemp.top,1,rcTemp.bottom - rcTemp.top);
+
+ x += lpHierDrawStruct->nBitmapWidth;
+ dwMask *= 2;
+ }
+
+
+ //
+ // Draw the short vert line up towards the parent
+ //
+ nTempLevel = nLevel-1;
+ dwMask *= 2;
+
+ x = nTempLevel * lpHierDrawStruct->nBitmapWidth + lpHierDrawStruct->nBitmapWidth / 2 + XBMPOFFSET;
+
+ if ( dwConnectLevel & dwMask )
+ y = rcTemp.bottom;
+ else
+ y = rcTemp.bottom - lpHierDrawStruct->nLineHeight / 2;
+
+ FastRect(hDC,x,rcTemp.top,1,y-rcTemp.top);
+
+ //
+ // Draw short horiz bar to right
+ //
+ FastRect(hDC,x,rcTemp.bottom-lpHierDrawStruct->nLineHeight/2,lpHierDrawStruct->nBitmapWidth/2,1);
+ }
+
+ //
+ // Draw the selected bitmap
+ //
+
+ BitBlt(hDC,
+ wIndent,wTopBitmap,
+ lpHierDrawStruct->nBitmapWidth,lpHierDrawStruct->nBitmapHeight,
+ lpHierDrawStruct->hdcMem,
+ nColumn*lpHierDrawStruct->nBitmapWidth,
+ nRow*lpHierDrawStruct->nBitmapHeight,
+ SRCCOPY);
+
+DealWithSelection:
+
+ if (lpDrawItem->itemState & ODS_SELECTED)
+ {
+ SetBkColor(hDC,GetSysColor(COLOR_HIGHLIGHT));
+ SetTextColor(hDC,GetSysColor(COLOR_HIGHLIGHTTEXT));
+ }
+ else
+ {
+ SetBkColor(hDC,GetSysColor(COLOR_WINDOW));
+ SetTextColor(hDC,GetSysColor(COLOR_WINDOWTEXT));
+ }
+
+
+ ExtTextOut(hDC, rcTemp.left + 1, wTopText, ETO_CLIPPED|ETO_OPAQUE,
+ &rcTemp,szText,lstrlen(szText), NULL);
+
+ if (lpDrawItem->itemState & ODS_FOCUS && lpDrawItem->itemAction != ODA_SELECT) {
+DealWithFocus:
+ DrawFocusRect(hDC, &rcTemp);
+ }
+
+
+} // HierDraw_OnDrawItem
+
+
+//
+// draw a solid color rectangle quickly
+//
+static VOID near FastRect(HDC hDC, int x, int y, int cx, int cy) {
+ RECT rc;
+
+ rc.left = x;
+ rc.right = x+cx;
+ rc.top = y;
+ rc.bottom = y+cy;
+ ExtTextOut(hDC,x,y,ETO_OPAQUE,&rc,NULL,0,NULL);
+} // FastRect
+
+
+BOOL HierDraw_IsOpened(LPHEIRDRAWSTRUCT lpHierDrawStruct, DWORD dwData) {
+ // For Now just a dumb search
+ //
+ int Count;
+
+ for ( Count = 0; Count < lpHierDrawStruct->NumOpened; Count++ ) {
+ if ( lpHierDrawStruct->Opened[Count] == dwData ) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+
+} // HierDraw_IsOpened
+
+
+VOID HierDraw_OpenItem(LPHEIRDRAWSTRUCT lpHierDrawStruct, DWORD dwData) {
+ lpHierDrawStruct->NumOpened++;
+
+ if (lpHierDrawStruct->Opened == NULL )
+ lpHierDrawStruct->Opened =
+ (DWORD FAR *)_fmalloc(sizeof(DWORD)*lpHierDrawStruct->NumOpened);
+ else
+ lpHierDrawStruct->Opened =
+ (DWORD FAR *)_frealloc(lpHierDrawStruct->Opened,
+ sizeof(DWORD)*lpHierDrawStruct->NumOpened);
+
+ lpHierDrawStruct->Opened[lpHierDrawStruct->NumOpened-1] = dwData;
+} // HierDraw_OpenItem
+
+
+VOID HierDraw_CloseItem(LPHEIRDRAWSTRUCT lpHierDrawStruct, DWORD dwData) {
+ // For Now just a dumb search
+ //
+ int Count;
+
+ for ( Count = 0; Count < lpHierDrawStruct->NumOpened; Count++ ) {
+ if ( lpHierDrawStruct->Opened[Count] == dwData ) {
+ if (--lpHierDrawStruct->NumOpened == 0 ) {
+ _ffree(lpHierDrawStruct->Opened);
+ lpHierDrawStruct->Opened = NULL;
+ }
+ else {
+ if ( Count < lpHierDrawStruct->NumOpened ) {
+ _fmemmove(&(lpHierDrawStruct->Opened[Count]),
+ &(lpHierDrawStruct->Opened[Count+1]),
+ sizeof(DWORD)*(lpHierDrawStruct->NumOpened-Count));
+ }
+ lpHierDrawStruct->Opened =
+ (DWORD FAR *)_frealloc(lpHierDrawStruct->Opened,
+ sizeof(DWORD)*lpHierDrawStruct->NumOpened);
+ }
+ }
+ }
+} // HierDraw_CloseItem
+
+
+VOID HierDraw_ShowKids(LPHEIRDRAWSTRUCT lpHierDrawStruct,
+ HWND hwndList, WORD wCurrentSelection, WORD wKids) {
+ WORD wBottomIndex;
+ WORD wTopIndex;
+ WORD wNewTopIndex;
+ WORD wExpandInView;
+ RECT rc;
+
+ wTopIndex = (WORD)SendMessage(hwndList, LB_GETTOPINDEX, 0, 0L);
+ GetClientRect(hwndList, &rc);
+ wBottomIndex = wTopIndex + (rc.bottom+1) / lpHierDrawStruct->nLineHeight;
+
+ wExpandInView = (wBottomIndex - wCurrentSelection);
+
+ if (wKids >= wExpandInView) {
+ wNewTopIndex = min(wCurrentSelection, wTopIndex + wKids - wExpandInView + 1);
+ SendMessage(hwndList, LB_SETTOPINDEX, (WORD)wNewTopIndex, 0L);
+ }
+
+} // HierDraw_ShowKids
diff --git a/private/nw/convert/nwconv/hierdraw.h b/private/nw/convert/nwconv/hierdraw.h
new file mode 100644
index 000000000..f8007fbee
--- /dev/null
+++ b/private/nw/convert/nwconv/hierdraw.h
@@ -0,0 +1,65 @@
+#define XBMPOFFSET 2
+
+
+typedef struct _HierDrawStruct {
+ HDC hdcMem;
+ HBITMAP hbmIcons;
+ HBITMAP hbmMem;
+ int nBitmapHeight;
+ int nBitmapWidth;
+ int nTextHeight;
+ int nLineHeight;
+ BOOL bLines;
+ int NumOpened;
+ DWORD FAR *Opened;
+
+} HEIRDRAWSTRUCT;
+
+typedef HEIRDRAWSTRUCT FAR * LPHEIRDRAWSTRUCT ;
+
+
+//
+// Interface functions
+//
+VOID HierDraw_DrawTerm(LPHEIRDRAWSTRUCT lpHierDrawStruct);
+
+VOID HierDraw_DrawSetTextHeight (HWND hwnd, HFONT hFont, LPHEIRDRAWSTRUCT lpHierDrawStruct );
+
+BOOL HierDraw_DrawInit(HINSTANCE hInstance,
+ int nBitmap,
+ int nRows,
+ int nColumns,
+ BOOL bLines,
+ LPHEIRDRAWSTRUCT lpHierDrawStruct,
+ BOOL bInit);
+
+
+VOID HierDraw_OnDrawItem(HWND hwnd,
+ const DRAWITEMSTRUCT FAR* lpDrawItem,
+ int nLevel,
+ DWORD dwConnectLevel,
+ TCHAR *szText,
+ int nRow,
+ int nColumn,
+ LPHEIRDRAWSTRUCT lpHierDrawStruct);
+
+
+VOID HierDraw_OnMeasureItem(HWND hwnd, MEASUREITEMSTRUCT FAR* lpMeasureItem,
+ LPHEIRDRAWSTRUCT lpHierDrawStruct);
+
+BOOL HierDraw_IsOpened(LPHEIRDRAWSTRUCT lpHierDrawStruct, DWORD dwData);
+
+VOID HierDraw_OpenItem(LPHEIRDRAWSTRUCT lpHierDrawStruct, DWORD dwData);
+
+VOID HierDraw_CloseItem(LPHEIRDRAWSTRUCT lpHierDrawStruct, DWORD dwData);
+
+VOID HierDraw_DrawCloseAll(LPHEIRDRAWSTRUCT lpHierDrawStruct );
+
+VOID HierDraw_ShowKids(LPHEIRDRAWSTRUCT lpHierDrawStruct,
+ HWND hwndList, WORD wCurrentSelection, WORD wKids);
+
+//
+// Support functions
+//
+static VOID near FastRect(HDC hDC, int x, int y, int cx, int cy);
+static DWORD near RGB2BGR(DWORD rgb);
diff --git a/private/nw/convert/nwconv/hierfile.c b/private/nw/convert/nwconv/hierfile.c
new file mode 100644
index 000000000..edb8481dd
--- /dev/null
+++ b/private/nw/convert/nwconv/hierfile.c
@@ -0,0 +1,506 @@
+#include <windows.h>
+#include <windowsx.h>
+#include <malloc.h>
+#include <string.h>
+
+#include "debug.h"
+#include "HierFile.h"
+
+
+VOID HierFile_DrawTerm(LPHEIRDRAWSTRUCT lpHierFileStruct) {
+ if (lpHierFileStruct->hbmIcons1) {
+ if (lpHierFileStruct->hbmMem1)
+ SelectObject(lpHierFileStruct->hdcMem1, lpHierFileStruct->hbmMem1);
+ lpHierFileStruct->hbmMem1 = NULL;
+ DeleteObject(lpHierFileStruct->hbmIcons1);
+ lpHierFileStruct->hbmIcons1 = NULL;
+ }
+
+ if ( lpHierFileStruct->hdcMem1 ) {
+ DeleteDC(lpHierFileStruct->hdcMem1);
+ lpHierFileStruct->hdcMem1 = NULL;
+ }
+
+ if (lpHierFileStruct->hbmIcons2) {
+ if (lpHierFileStruct->hbmMem2)
+ SelectObject(lpHierFileStruct->hdcMem2, lpHierFileStruct->hbmMem2);
+ lpHierFileStruct->hbmMem2 = NULL;
+ DeleteObject(lpHierFileStruct->hbmIcons2);
+ lpHierFileStruct->hbmIcons2 = NULL;
+ }
+
+ if ( lpHierFileStruct->hdcMem2 ) {
+ DeleteDC(lpHierFileStruct->hdcMem2);
+ lpHierFileStruct->hdcMem2 = NULL;
+ }
+
+} // HierFile_DrawTerm
+
+VOID HierFile_DrawCloseAll(LPHEIRDRAWSTRUCT lpHierFileStruct ) {
+ lpHierFileStruct->NumOpened= 0;
+ if ( lpHierFileStruct->Opened ) {
+ _ffree(lpHierFileStruct->Opened);
+ }
+ lpHierFileStruct->Opened = NULL;
+} // HierFile_DrawCloseAll
+
+VOID HierFile_OnMeasureItem(HWND hwnd, MEASUREITEMSTRUCT FAR* lpMeasureItem,
+ LPHEIRDRAWSTRUCT lpHierFileStruct) {
+ lpMeasureItem->itemHeight = max(lpHierFileStruct->nBitmapHeight1,
+ lpHierFileStruct->nTextHeight);
+} // HierFile_OnMeasureItem
+
+VOID HierFile_DrawSetTextHeight (HWND hwndList, HFONT hFont, LPHEIRDRAWSTRUCT lpHierFileStruct ) {
+ TEXTMETRIC TextMetrics;
+ HANDLE hOldFont=NULL;
+ HDC hdc;
+
+ //
+ // This sure looks like a lot of work to find the character height
+ //
+ hdc = GetDC(hwndList);
+
+ hOldFont = SelectObject(hdc, hFont);
+ GetTextMetrics(hdc, &TextMetrics);
+ SelectObject(hdc, hOldFont);
+ ReleaseDC(hwndList, hdc);
+
+ lpHierFileStruct->nTextHeight = TextMetrics.tmHeight;
+
+ lpHierFileStruct->nLineHeight =
+ max(lpHierFileStruct->nBitmapHeight1, lpHierFileStruct->nTextHeight);
+
+ if ( hwndList != NULL )
+ SendMessage(hwndList, LB_SETITEMHEIGHT, 0,
+ MAKELPARAM(lpHierFileStruct->nLineHeight, 0));
+} // HierFile_DrawSetTextHeight
+
+static DWORD near RGB2BGR(DWORD rgb) {
+ return RGB(GetBValue(rgb),GetGValue(rgb),GetRValue(rgb));
+} // RGB2BGR
+
+
+
+/*
+ * Creates the objects used while drawing the tree. This may be called
+ * repeatedly in the event of a WM_SYSCOLORCHANGED message.
+ */
+
+BOOL HierFile_DrawInit(HINSTANCE hInstance,
+ int nBitmap1,
+ int nBitmap2,
+ int nRows,
+ int nColumns,
+ BOOL bLines,
+ LPHEIRDRAWSTRUCT lpHierFileStruct,
+ BOOL bInit) {
+ HANDLE hRes;
+ HANDLE hResMem;
+ LPBITMAPINFOHEADER lpbiReadOnly;
+ LPBITMAPINFOHEADER lpbiReadWrite;
+ DWORD FAR * lpColorTable;
+ LPSTR lpBits;
+ int biSize;
+ int bc;
+ HDC hDC;
+
+
+ if ( bInit ) {
+ lpHierFileStruct->NumOpened = 0;
+ lpHierFileStruct->Opened = NULL;
+ lpHierFileStruct->bLines = bLines;
+ }
+
+ // If the Memory DC is not created yet do that first.
+ if (!lpHierFileStruct->hdcMem1) {
+
+ // get a screen DC
+ hDC = GetDC(NULL);
+
+ // Create a memory DC compatible with the screen
+ lpHierFileStruct->hdcMem1 = CreateCompatibleDC(hDC);
+
+ // Release the Screen DC
+ ReleaseDC(NULL, hDC);
+
+ if (!lpHierFileStruct->hdcMem1)
+ return FALSE;
+
+ lpHierFileStruct->hbmMem1 = NULL;
+ }
+
+ // If the Memory DC is not created yet do that first.
+ if (!lpHierFileStruct->hdcMem2) {
+
+ // get a screen DC
+ hDC = GetDC(NULL);
+
+ // Create a memory DC compatible with the screen
+ lpHierFileStruct->hdcMem2 = CreateCompatibleDC(hDC);
+
+ // Release the Screen DC
+ ReleaseDC(NULL, hDC);
+
+ if (!lpHierFileStruct->hdcMem2)
+ return FALSE;
+
+ lpHierFileStruct->hbmMem2 = NULL;
+ }
+
+ /*+----------------------------------------------------------------------------------+
+ | For First Bitmap |
+ +----------------------------------------------------------------------------------+*/
+
+ // (Re)Load the Bitmap ( original from disk )
+
+ // Use the FindResource,LoadResource,LockResource since it makes it easy to get the
+ // pointer to the BITMAPINFOHEADER we need.
+ hRes = FindResource(hInstance, MAKEINTRESOURCE(nBitmap1), RT_BITMAP);
+ if (!hRes)
+ return FALSE;
+
+ hResMem = LoadResource(hInstance, hRes);
+ if (!hResMem)
+ return FALSE;
+
+ // Now figure out the bitmaps background color.
+ // This code assumes the lower left corner is a
+ // bit in the background color.
+ lpbiReadOnly = (LPBITMAPINFOHEADER)LockResource(hResMem);
+ if (!lpbiReadOnly)
+ return FALSE;
+
+ // Determine size of bitmap information header plus color table entries
+ biSize = lpbiReadOnly->biSize + ((1 << (lpbiReadOnly->biBitCount)) * sizeof(RGBQUAD));
+
+ // Allocate copy of the bitmap information to munge on
+ lpbiReadWrite = (LPBITMAPINFOHEADER)GlobalAlloc(GPTR, biSize);
+ if (!lpbiReadWrite)
+ return FALSE;
+
+ memcpy(lpbiReadWrite, lpbiReadOnly, biSize);
+
+ // Color table immediately follows bitmap information header
+ lpColorTable = (DWORD FAR *)((LPBYTE)lpbiReadWrite + lpbiReadWrite->biSize);
+
+ // No need to munge bits so use original
+ lpBits = (LPBYTE)lpbiReadOnly + biSize;
+
+ bc = (lpBits[0] & 0xF0) >> 4; // ASSUMES LOWER LEFT CORNER IS BG!!
+
+ lpColorTable[bc] = RGB2BGR(GetSysColor(COLOR_WINDOW));
+
+ hDC = GetDC(NULL);
+ lpHierFileStruct->hbmIcons1 = CreateDIBitmap(
+ hDC,
+ lpbiReadWrite,
+ CBM_INIT,
+ lpBits,
+ (LPBITMAPINFO)lpbiReadWrite,
+ DIB_RGB_COLORS
+ );
+ ReleaseDC(NULL, hDC);
+
+ lpHierFileStruct->nBitmapHeight1 = (WORD)lpbiReadWrite->biHeight / nRows;
+ lpHierFileStruct->nBitmapWidth1 = (WORD)lpbiReadWrite->biWidth / nColumns;
+
+ lpHierFileStruct->nLineHeight =
+ max(lpHierFileStruct->nBitmapHeight1, lpHierFileStruct->nTextHeight);
+
+ GlobalFree(lpbiReadWrite);
+ UnlockResource(hResMem);
+ FreeResource(hResMem);
+
+ if (!lpHierFileStruct->hbmIcons1)
+ return FALSE;
+
+ lpHierFileStruct->hbmMem1 = SelectObject(lpHierFileStruct->hdcMem1, lpHierFileStruct->hbmIcons1);
+ if (!lpHierFileStruct->hbmMem1)
+ return FALSE;
+
+ /*+----------------------------------------------------------------------+
+ | For Second Bitmap |
+ +----------------------------------------------------------------------+*/
+
+ // (Re)Load the Bitmap ( original from disk )
+ hRes = FindResource(hInstance, MAKEINTRESOURCE(nBitmap2), RT_BITMAP);
+ if (!hRes)
+ return FALSE;
+
+ hResMem = LoadResource(hInstance, hRes);
+ if (!hResMem)
+ return FALSE;
+
+ // Now figure out the bitmaps background color.
+ // This code assumes the lower left corner is a
+ // bit in the background color.
+ lpbiReadOnly = (LPBITMAPINFOHEADER) LockResource(hResMem);
+ if (!lpbiReadOnly)
+ return FALSE;
+
+ // Determine size of bitmap information header plus color table entries
+ biSize = lpbiReadOnly->biSize + ((1 << (lpbiReadOnly->biBitCount)) * sizeof(RGBQUAD));
+
+ // Allocate copy of the bitmap information to munge on
+ lpbiReadWrite = (LPBITMAPINFOHEADER)GlobalAlloc(GPTR, biSize);
+ if (!lpbiReadWrite)
+ return FALSE;
+
+ memcpy(lpbiReadWrite, lpbiReadOnly, biSize);
+
+ // Color table immediately follows bitmap information header
+ lpColorTable = (DWORD FAR *)((LPBYTE)lpbiReadWrite + lpbiReadWrite->biSize);
+
+ // No need to munge bits so use original
+ lpBits = (LPBYTE)lpbiReadOnly + biSize;
+
+ bc = (lpBits[0] & 0xF0) >> 4; // ASSUMES LOWER LEFT CORNER IS BG!!
+
+ lpColorTable[bc] = RGB2BGR(GetSysColor(COLOR_WINDOW));
+
+ hDC = GetDC(NULL);
+ lpHierFileStruct->hbmIcons2 = CreateDIBitmap(
+ hDC,
+ lpbiReadWrite,
+ CBM_INIT,
+ lpBits,
+ (LPBITMAPINFO)lpbiReadWrite,
+ DIB_RGB_COLORS
+ );
+ ReleaseDC(NULL, hDC);
+
+ // These are hard-coded as 1 row and 3 columns for the checkbox bitmap
+ lpHierFileStruct->nBitmapHeight2 = (WORD)lpbiReadWrite->biHeight / 1;
+ lpHierFileStruct->nBitmapWidth2 = (WORD)lpbiReadWrite->biWidth / 3;
+
+ GlobalFree(lpbiReadWrite);
+ UnlockResource(hResMem);
+ FreeResource(hResMem);
+
+ if (!lpHierFileStruct->hbmIcons2)
+ return FALSE;
+
+ lpHierFileStruct->hbmMem2 = SelectObject(lpHierFileStruct->hdcMem2, lpHierFileStruct->hbmIcons2);
+ if (!lpHierFileStruct->hbmMem2)
+ return FALSE;
+
+ return TRUE;
+} // HierFile_DrawInit
+
+
+BOOL HierFile_InCheck(int nLevel, int xPos, LPHEIRDRAWSTRUCT lpHierFileStruct) {
+ WORD wIndent;
+
+ wIndent = ((int)(nLevel) * lpHierFileStruct->nBitmapWidth1) + XBMPOFFSET;
+
+ if ((xPos > wIndent) && (xPos < wIndent + lpHierFileStruct->nBitmapWidth2))
+ return TRUE;
+
+ return FALSE;
+
+} // HierFile_InCheck
+
+
+VOID HierFile_OnDrawItem(HWND hwnd,
+ const DRAWITEMSTRUCT FAR* lpDrawItem,
+ int nLevel,
+ DWORD dwConnectLevel,
+ TCHAR *szText,
+ int nRow,
+ int nColumn,
+ int nColumn2,
+ LPHEIRDRAWSTRUCT lpHierFileStruct) {
+ HDC hDC;
+ WORD wIndent, wTopBitmap, wTopText;
+ RECT rcTemp;
+
+
+ if ( lpDrawItem->itemID == (UINT)-1 )
+ return ;
+
+ hDC = lpDrawItem->hDC;
+ CopyRect(&rcTemp, &lpDrawItem->rcItem);
+
+ wIndent = rcTemp.left + ((int)(nLevel) * lpHierFileStruct->nBitmapWidth1) + XBMPOFFSET;
+ rcTemp.left = wIndent + lpHierFileStruct->nBitmapWidth1 + lpHierFileStruct->nBitmapWidth2;
+ wTopText = rcTemp.top + ((rcTemp.bottom - rcTemp.top) / 2) - (lpHierFileStruct->nTextHeight / 2);
+ wTopBitmap = rcTemp.top + ((rcTemp.bottom - rcTemp.top) / 2) - (lpHierFileStruct->nBitmapHeight1 / 2);
+
+ if (lpDrawItem->itemAction == ODA_FOCUS)
+ goto DealWithFocus;
+ else if (lpDrawItem->itemAction == ODA_SELECT)
+ goto DealWithSelection;
+
+ /*+-----------------------------------------------------------------------+
+ | Connecting Line code |
+ +-----------------------------------------------------------------------+*/
+ if (lpHierFileStruct->bLines && nLevel) {
+ DWORD dwMask = 1;
+ int nTempLevel;
+ int x,y;
+
+ // draw lines in text color
+ SetBkColor(hDC, GetSysColor(COLOR_WINDOWTEXT));
+
+ // Draw a series of | lines for outer levels
+ x = lpHierFileStruct->nBitmapWidth1 / 2 + XBMPOFFSET;
+
+ for ( nTempLevel = 0; nTempLevel < nLevel ; nTempLevel++)
+ {
+ if ( dwConnectLevel & dwMask )
+ FastRect(hDC, x, rcTemp.top, 1, rcTemp.bottom - rcTemp.top);
+
+ x += lpHierFileStruct->nBitmapWidth2;
+ dwMask *= 2;
+ }
+
+
+ // Draw the short vert line up towards the parent
+ nTempLevel = nLevel-1;
+ dwMask *= 2;
+
+ x = nTempLevel * lpHierFileStruct->nBitmapWidth1 + lpHierFileStruct->nBitmapWidth1 / 2 + XBMPOFFSET;
+
+ if ( dwConnectLevel & dwMask )
+ y = rcTemp.bottom;
+ else
+ y = rcTemp.bottom - lpHierFileStruct->nLineHeight / 2;
+
+ FastRect(hDC, x, rcTemp.top, 1, y - rcTemp.top);
+
+ // Draw short horiz bar to right
+ FastRect(hDC, x, rcTemp.bottom-lpHierFileStruct->nLineHeight / 2, lpHierFileStruct->nBitmapWidth1 / 2, 1);
+ }
+
+ /*+-----------------------------------------------------------------------+
+ | Bitmaps |
+ +-----------------------------------------------------------------------+*/
+ // Draw the checkbox bitmap
+ BitBlt(hDC,
+ wIndent, wTopBitmap,
+ lpHierFileStruct->nBitmapWidth2, lpHierFileStruct->nBitmapHeight2,
+ lpHierFileStruct->hdcMem2,
+ nColumn2 * lpHierFileStruct->nBitmapWidth2,
+ 0 * lpHierFileStruct->nBitmapHeight2,
+ SRCCOPY);
+
+ // Now the other app specific bitmap adjusted over for the checkbox bitmap
+ BitBlt(hDC,
+ wIndent + lpHierFileStruct->nBitmapWidth2, wTopBitmap,
+ lpHierFileStruct->nBitmapWidth1, lpHierFileStruct->nBitmapHeight1,
+ lpHierFileStruct->hdcMem1,
+ nColumn * lpHierFileStruct->nBitmapWidth1,
+ nRow * lpHierFileStruct->nBitmapHeight1,
+ SRCCOPY);
+
+DealWithSelection:
+
+ if (lpDrawItem->itemState & ODS_SELECTED) {
+ SetBkColor(hDC, GetSysColor(COLOR_HIGHLIGHT));
+ SetTextColor(hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
+ } else {
+ SetBkColor(hDC, GetSysColor(COLOR_WINDOW));
+ SetTextColor(hDC, GetSysColor(COLOR_WINDOWTEXT));
+ }
+
+
+ ExtTextOut(hDC, rcTemp.left + 1, wTopText, ETO_CLIPPED | ETO_OPAQUE,
+ &rcTemp, szText, lstrlen(szText), NULL);
+
+ if (lpDrawItem->itemState & ODS_FOCUS && lpDrawItem->itemAction != ODA_SELECT) {
+DealWithFocus:
+ DrawFocusRect(hDC, &rcTemp);
+ }
+
+
+} // HierFile_OnDrawItem
+
+
+// draw a solid color rectangle quickly
+static VOID near FastRect(HDC hDC, int x, int y, int cx, int cy) {
+ RECT rc;
+
+ rc.left = x;
+ rc.right = x+cx;
+ rc.top = y;
+ rc.bottom = y+cy;
+ ExtTextOut(hDC,x,y,ETO_OPAQUE,&rc,NULL,0,NULL);
+} // FastRect
+
+
+BOOL HierFile_IsOpened(LPHEIRDRAWSTRUCT lpHierFileStruct, DWORD dwData) {
+ // For Now just a dumb search
+ //
+ int Count;
+
+ for ( Count = 0; Count < lpHierFileStruct->NumOpened; Count++ ) {
+ if ( lpHierFileStruct->Opened[Count] == dwData ) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+
+} // HierFile_IsOpened
+
+
+VOID HierFile_OpenItem(LPHEIRDRAWSTRUCT lpHierFileStruct, DWORD dwData) {
+ lpHierFileStruct->NumOpened++;
+
+ if (lpHierFileStruct->Opened == NULL )
+ lpHierFileStruct->Opened =
+ (DWORD FAR *)_fmalloc(sizeof(DWORD)*lpHierFileStruct->NumOpened);
+ else
+ lpHierFileStruct->Opened =
+ (DWORD FAR *)_frealloc(lpHierFileStruct->Opened,
+ sizeof(DWORD)*lpHierFileStruct->NumOpened);
+
+ lpHierFileStruct->Opened[lpHierFileStruct->NumOpened-1] = dwData;
+} // HierFile_OpenItem
+
+VOID HierFile_CloseItem(LPHEIRDRAWSTRUCT lpHierFileStruct, DWORD dwData) {
+ // For Now just a dumb search
+ //
+ int Count;
+
+ for ( Count = 0; Count < lpHierFileStruct->NumOpened; Count++ ) {
+ if ( lpHierFileStruct->Opened[Count] == dwData ) {
+ if (--lpHierFileStruct->NumOpened == 0 ) {
+ _ffree(lpHierFileStruct->Opened);
+ lpHierFileStruct->Opened = NULL;
+ }
+ else {
+ if ( Count < lpHierFileStruct->NumOpened ) {
+ _fmemmove(&(lpHierFileStruct->Opened[Count]),
+ &(lpHierFileStruct->Opened[Count+1]),
+ sizeof(DWORD)*(lpHierFileStruct->NumOpened-Count));
+ }
+ lpHierFileStruct->Opened =
+ (DWORD FAR *)_frealloc(lpHierFileStruct->Opened,
+ sizeof(DWORD)*lpHierFileStruct->NumOpened);
+ }
+ }
+ }
+} // HierFile_CloseItem
+
+
+VOID HierFile_ShowKids(LPHEIRDRAWSTRUCT lpHierFileStruct,
+ HWND hwndList, WORD wCurrentSelection, WORD wKids) {
+ WORD wBottomIndex;
+ WORD wTopIndex;
+ WORD wNewTopIndex;
+ WORD wExpandInView;
+ RECT rc;
+
+ wTopIndex = (WORD)SendMessage(hwndList, LB_GETTOPINDEX, 0, 0L);
+ GetClientRect(hwndList, &rc);
+ wBottomIndex = wTopIndex + (rc.bottom+1) / lpHierFileStruct->nLineHeight;
+
+ wExpandInView = (wBottomIndex - wCurrentSelection);
+
+ if (wKids >= wExpandInView) {
+ wNewTopIndex = min(wCurrentSelection, wTopIndex + wKids - wExpandInView + 1);
+ SendMessage(hwndList, LB_SETTOPINDEX, (WORD)wNewTopIndex, 0L);
+ }
+
+} // HierFile_ShowKids
diff --git a/private/nw/convert/nwconv/hierfile.h b/private/nw/convert/nwconv/hierfile.h
new file mode 100644
index 000000000..55baa9cea
--- /dev/null
+++ b/private/nw/convert/nwconv/hierfile.h
@@ -0,0 +1,74 @@
+#define XBMPOFFSET 2
+
+
+typedef struct _HierFileStruct {
+ HDC hdcMem1;
+ HDC hdcMem2;
+ HBITMAP hbmIcons1;
+ HBITMAP hbmMem1;
+ int nBitmapHeight1;
+ int nBitmapWidth1;
+ HBITMAP hbmIcons2;
+ HBITMAP hbmMem2;
+ int nBitmapHeight2;
+ int nBitmapWidth2;
+ int nTextHeight;
+ int nLineHeight;
+ BOOL bLines;
+ int NumOpened;
+ DWORD FAR *Opened;
+
+} HEIRDRAWSTRUCT;
+
+typedef HEIRDRAWSTRUCT FAR * LPHEIRDRAWSTRUCT ;
+
+
+//
+// Interface functions
+//
+VOID HierFile_DrawTerm(LPHEIRDRAWSTRUCT lpHierFileStruct);
+
+VOID HierFile_DrawSetTextHeight (HWND hwnd, HFONT hFont, LPHEIRDRAWSTRUCT lpHierFileStruct );
+
+BOOL HierFile_DrawInit(HINSTANCE hInstance,
+ int nBitmap1,
+ int nBitmap2,
+ int nRows,
+ int nColumns,
+ BOOL bLines,
+ LPHEIRDRAWSTRUCT lpHierFileStruct,
+ BOOL bInit);
+
+
+VOID HierFile_OnDrawItem(HWND hwnd,
+ const DRAWITEMSTRUCT FAR* lpDrawItem,
+ int nLevel,
+ DWORD dwConnectLevel,
+ TCHAR *szText,
+ int nRow,
+ int nColumn,
+ int nColumn2,
+ LPHEIRDRAWSTRUCT lpHierFileStruct);
+
+
+VOID HierFile_OnMeasureItem(HWND hwnd, MEASUREITEMSTRUCT FAR* lpMeasureItem,
+ LPHEIRDRAWSTRUCT lpHierFileStruct);
+
+BOOL HierFile_IsOpened(LPHEIRDRAWSTRUCT lpHierFileStruct, DWORD dwData);
+
+VOID HierFile_OpenItem(LPHEIRDRAWSTRUCT lpHierFileStruct, DWORD dwData);
+
+VOID HierFile_CloseItem(LPHEIRDRAWSTRUCT lpHierFileStruct, DWORD dwData);
+
+VOID HierFile_DrawCloseAll(LPHEIRDRAWSTRUCT lpHierFileStruct );
+
+VOID HierFile_ShowKids(LPHEIRDRAWSTRUCT lpHierFileStruct,
+ HWND hwndList, WORD wCurrentSelection, WORD wKids);
+
+BOOL HierFile_InCheck(int nLevel, int xPos, LPHEIRDRAWSTRUCT lpHierFileStruct);
+
+//
+// Support functions
+//
+static VOID near FastRect(HDC hDC, int x, int y, int cx, int cy);
+static DWORD near RGB2BGR(DWORD rgb);
diff --git a/private/nw/convert/nwconv/hiericon.bmp b/private/nw/convert/nwconv/hiericon.bmp
new file mode 100644
index 000000000..c4d0b5af3
--- /dev/null
+++ b/private/nw/convert/nwconv/hiericon.bmp
Binary files differ
diff --git a/private/nw/convert/nwconv/loghours.c b/private/nw/convert/nwconv/loghours.c
new file mode 100644
index 000000000..5276767a5
--- /dev/null
+++ b/private/nw/convert/nwconv/loghours.c
@@ -0,0 +1,310 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ loghours.c
+
+Abstract:
+
+ Private routines to support rotation of logon hours between local time
+ and GMT time.
+
+Author:
+
+ Cliff Van Dyke (cliffv) 16-Mar-93
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#include <windef.h>
+#include <winbase.h>
+
+#include <limits.h>
+#include <math.h>
+
+#include <lmcons.h>
+#include <lmaccess.h>
+#include <loghours.h>
+
+
+
+BOOLEAN
+NetpRotateLogonHoursPhase1(
+ IN BOOL ConvertToGmt,
+ OUT PULONG RotateCount
+ )
+
+/*++
+
+Routine Description:
+
+ Determine the amount to rotate the logon hours by to convert to/from GMT
+
+Arguments:
+
+ ConvertToGmt -
+ True to convert the logon hours from local time to GMT relative
+ False to convert the logon hours from GMT relative to local time
+
+ RotateCount - Returns the number of bits to shift by.
+
+Return Value:
+
+ TRUE if the RotateCount could be computed
+ FALSE if a RotateCount could not be computed
+
+--*/
+{
+ RTL_TIME_ZONE_INFORMATION tzi;
+ LONG BiasInHours;
+ NTSTATUS Status;
+
+ //
+ // Get the timezone data from the registry
+ //
+
+ Status = RtlQueryTimeZoneInformation( &tzi );
+ if ( !NT_SUCCESS(Status) ) {
+ return FALSE;
+ }
+
+ //
+ // Compute the amount to rotate the logon hours by
+ //
+ // Round the bias in minutes to the closest bias in hours.
+ // Take into consideration that Bias can be negative.
+ // Do this by forcing the Bias to be positive, rounding,
+ // then adjusting it back negative again.
+ //
+
+ ASSERT( tzi.Bias > -(24*60) );
+ BiasInHours = ((tzi.Bias + (24*60) + 30)/60) - 24;
+
+ if ( !ConvertToGmt ) {
+ BiasInHours = - BiasInHours;
+ }
+
+ *RotateCount = BiasInHours;
+ return TRUE;
+
+}
+
+
+BOOLEAN
+NetpRotateLogonHoursPhase2(
+ IN PBYTE LogonHours,
+ IN DWORD UnitsPerWeek,
+ IN LONG RotateCount
+ )
+
+/*++
+
+Routine Description:
+
+ Rotate the LogonHours bit mask by the required amount.
+
+
+Arguments:
+
+ LogonHours - Pointer to LogonHour bit mask
+
+ UnitsPerWeek - Number of bits in the bit mask. Must be UNITS_PER_WEEK (168).
+
+ RotateCount - Number of bits to rotate by. Must be between 31 and -31.
+ Negative means to rotate left.
+ Positive means to rotate right.
+
+Return Value:
+
+ TRUE if the rotation succeeded.
+ FALSE if a parameter was out of range
+
+--*/
+{
+ //
+ // Useful constants
+ //
+
+#define DWORDS_PER_WEEK ((UNITS_PER_WEEK+31)/32)
+#define BYTES_PER_WEEK (UNITS_PER_WEEK/8)
+
+ DWORD AlignedLogonHours[DWORDS_PER_WEEK+1];
+ LONG i;
+
+ BOOLEAN RotateLeft;
+
+ //
+ // Ensure there are 8 bits per byte,
+ // 32 bits per DWORD and
+ // units per week is even number of bytes.
+ //
+
+ ASSERT( CHAR_BIT == 8 );
+ ASSERT( sizeof(DWORD) * CHAR_BIT == 32 );
+ ASSERT( UNITS_PER_WEEK/8*8 == UNITS_PER_WEEK );
+
+
+ //
+ // Validate the input parameters
+ //
+
+ if ( UnitsPerWeek != UNITS_PER_WEEK ) {
+ ASSERT( UnitsPerWeek == UNITS_PER_WEEK );
+ return FALSE;
+ }
+
+ if ( RotateCount == 0 ) {
+ return TRUE;
+ }
+
+ RotateLeft = (RotateCount < 0);
+ RotateCount = labs( RotateCount );
+ if ( RotateCount > 31 ) {
+ ASSERT ( RotateCount <= 31 );
+ return FALSE;
+ }
+
+
+ //
+ // Do the left rotate.
+ //
+
+ if (RotateLeft) {
+
+
+ //
+ // Copy the logon hours to a DWORD aligned buffer.
+ //
+ // Duplicate the first dword at the end of the buffer to make
+ // the rotation code trivial.
+ //
+
+ RtlCopyMemory(AlignedLogonHours, LogonHours, BYTES_PER_WEEK );
+
+ RtlCopyMemory( ((PBYTE)AlignedLogonHours)+BYTES_PER_WEEK,
+ LogonHours,
+ sizeof(DWORD) );
+
+ //
+ // Actually rotate the data.
+ //
+
+ for ( i=0; i < DWORDS_PER_WEEK; i++ ) {
+ AlignedLogonHours[i] =
+ (AlignedLogonHours[i] >> RotateCount) |
+ (AlignedLogonHours[i+1] << (32-RotateCount));
+ }
+
+ //
+ // Copy the logon hours back to the input buffer.
+ //
+
+ RtlCopyMemory( LogonHours, AlignedLogonHours, BYTES_PER_WEEK );
+
+
+ //
+ // Do the right rotate.
+ //
+
+ } else {
+
+
+ //
+ // Copy the logon hours to a DWORD aligned buffer.
+ //
+ // Duplicate the last DWORD at the front of the buffer to make
+ // the rotation code trivial.
+ //
+
+ RtlCopyMemory( &AlignedLogonHours[1], LogonHours, BYTES_PER_WEEK );
+ RtlCopyMemory( AlignedLogonHours,
+ &LogonHours[BYTES_PER_WEEK-4],
+ sizeof(DWORD));
+
+ //
+ // Actually rotate the data.
+ //
+
+ for ( i=DWORDS_PER_WEEK-1; i>=0; i-- ) {
+ AlignedLogonHours[i+1] =
+ (AlignedLogonHours[i+1] << RotateCount) |
+ (AlignedLogonHours[i] >> (32-RotateCount));
+ }
+
+ //
+ // Copy the logon hours back to the input buffer.
+ //
+
+ RtlCopyMemory( LogonHours, &AlignedLogonHours[1], BYTES_PER_WEEK );
+
+ }
+
+ //
+ // Done
+ //
+
+ return TRUE;
+
+}
+
+
+
+BOOLEAN
+NetpRotateLogonHours(
+ IN PBYTE LogonHours,
+ IN DWORD UnitsPerWeek,
+ IN BOOL ConvertToGmt
+ )
+
+/*++
+
+Routine Description:
+
+ Rotate the LogonHours bit mask to/from GMT relative time.
+
+
+Arguments:
+
+ LogonHours - Pointer to LogonHour bit mask
+
+ UnitsPerWeek - Number of bits in the bit mask. Must be UNITS_PER_WEEK (168).
+
+ ConvertToGmt -
+ True to convert the logon hours from local time to GMT relative
+ False to convert the logon hours from GMT relative to local time
+
+Return Value:
+
+ TRUE if the rotation succeeded.
+ FALSE if a parameter was out of range
+
+--*/
+{
+ ULONG RotateCount;
+
+ //
+ // Break the functionality into two phases so that if the caller is doing
+ // this multiple time, he just calls Phase 1 once and Phase 2 multiple
+ // times.
+ //
+
+ if ( !NetpRotateLogonHoursPhase1( ConvertToGmt, &RotateCount ) ) {
+ return FALSE;
+ }
+
+ return NetpRotateLogonHoursPhase2( LogonHours, UnitsPerWeek, RotateCount );
+
+}
diff --git a/private/nw/convert/nwconv/loghours.h b/private/nw/convert/nwconv/loghours.h
new file mode 100644
index 000000000..b6dfc11dd
--- /dev/null
+++ b/private/nw/convert/nwconv/loghours.h
@@ -0,0 +1,52 @@
+/*++
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ loghours.h
+
+Abstract:
+
+ Private routines to support rotation of logon hours between local time
+ and GMT time.
+
+Author:
+
+ Cliff Van Dyke (cliffv) 16-Mar-93
+
+Environment:
+
+ User mode only.
+ Contains NT-specific code.
+ Requires ANSI C extensions: slash-slash comments, long external names.
+
+Revision History:
+
+--*/
+
+
+
+//
+// Procedure forwards from loghours.c
+//
+
+BOOLEAN
+NetpRotateLogonHoursPhase1(
+ IN BOOL ConvertToGmt,
+ OUT PULONG RotateCount
+ );
+
+BOOLEAN
+NetpRotateLogonHoursPhase2(
+ IN PBYTE LogonHours,
+ IN DWORD UnitsPerWeek,
+ IN LONG RotateCount
+ );
+
+BOOLEAN
+NetpRotateLogonHours(
+ IN PBYTE LogonHours,
+ IN DWORD UnitsPerWeek,
+ IN BOOL ConvertToGmt
+ );
diff --git a/private/nw/convert/nwconv/makefile b/private/nw/convert/nwconv/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/nw/convert/nwconv/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/convert/nwconv/map.c b/private/nw/convert/nwconv/map.c
new file mode 100644
index 000000000..74530ca84
--- /dev/null
+++ b/private/nw/convert/nwconv/map.c
@@ -0,0 +1,1682 @@
+/*
+ +-------------------------------------------------------------------------+
+ | User Options Dialog |
+ +-------------------------------------------------------------------------+
+ | (c) Copyright 1993-1994 |
+ | Microsoft Corp. |
+ | All rights reserved |
+ | |
+ | Program : [MAP.c] |
+ | Programmer : Arthur Hanson |
+ | Original Program Date : [Sep 28, 1994] |
+ | Last Update : [Sep 28, 1994] |
+ | |
+ | Version: 1.00 |
+ | |
+ | Description: |
+ | |
+ | History: |
+ | arth Sep 28, 1994 1.00 Original Version. |
+ | |
+ +-------------------------------------------------------------------------+
+*/
+
+
+#include "globals.h"
+#include "convapi.h"
+#include "ntnetapi.h"
+#include "nwnetapi.h"
+#include "map.h"
+#include "nwlog.h"
+#include "nwconv.h"
+
+// from userdlg.c
+DWORD UserFileGet(HWND hwnd, LPTSTR FilePath);
+
+// The map file is kept as a doubly-linked list of sections, each of which has
+// a doubly-linked list of lines in that section. A header is used to point to
+// the first section, and also contains a pointer to any lines that appear
+// before the first section (usually only comment lines) - it also keeps the
+// current line and section pointer.
+//
+// All the linked lists have a dummy header and tail, with the tail pointing
+// back on itself (this simplifies the logic for list manipulation)...
+//
+// +-------------+ +----------------+
+// | Dummy | | Dummy |
+// | head node v v tail node |
+// | +-----------+ +-----------+ +-----------+ |
+// | | Node 1 |-->| Node 2 |-->| Node 3 |----+
+// +--| (not used)|<--| (data) |<--| (not used)|
+// +-----------+ +-----------+ +-----------+
+//
+// The dummy head/tail nodes make it easy to keep track of the start and
+// end of the list (we never have to worry about updating the head and tail
+// pointers in the owning data structures, since they never change). It does,
+// however, make for some obtuse cases in the add/delete node code.
+
+typedef struct _LINKED_LIST {
+ struct _LINKED_LIST *prev;
+ struct _LINKED_LIST *next;
+
+} LINKED_LIST;
+
+typedef struct _LINK_HEAD {
+ LINKED_LIST *Head;
+ LINKED_LIST *Tail;
+
+ LINKED_LIST *Current;
+ ULONG Count;
+
+ TCHAR Name[];
+} LINK_HEAD;
+
+
+static TCHAR MappingFile[MAX_PATH + 1];
+static TCHAR PasswordConstant[MAX_PW_LEN + 1];
+static BOOL DoUsers = TRUE;
+static BOOL DoGroups = TRUE;
+static UINT PasswordOption = 0;
+static LPTSTR SourceServ;
+
+/*+-------------------------------------------------------------------------+
+ | Common Linked List Routines
+ +-------------------------------------------------------------------------+*/
+
+/*+-------------------------------------------------------------------------+
+ | ll_Init()
+ |
+ +-------------------------------------------------------------------------+*/
+LINKED_LIST *ll_Init(ULONG Size) {
+ LINKED_LIST *llHead;
+ LINKED_LIST *llTail;
+
+ llHead = (LINKED_LIST *) AllocMemory(Size);
+ if (llHead == NULL)
+ return NULL;
+
+ llTail = (LINKED_LIST *) AllocMemory(Size);
+ if (llTail == NULL) {
+ FreeMemory(llHead);
+ return NULL;
+ }
+
+ llHead->prev = llHead;
+ llHead->next = llTail;
+
+ llTail->next = llTail;
+ llTail->prev = llHead;
+
+ return llHead;
+
+} // ll_Init
+
+
+/*+-------------------------------------------------------------------------+
+ | ll_Next()
+ |
+ +-------------------------------------------------------------------------+*/
+LINKED_LIST *ll_Next(void *vllCurrent) {
+ LINKED_LIST *llCurrent;
+
+ llCurrent = (LINKED_LIST *) vllCurrent;
+
+ if ((llCurrent == NULL) || (llCurrent->next == NULL) || (llCurrent->prev == NULL))
+ return NULL;
+
+ llCurrent = llCurrent->next;
+
+ if (llCurrent->next == llCurrent)
+ return NULL;
+ else
+ return llCurrent;
+
+} // ll_Next
+
+
+/*+-------------------------------------------------------------------------+
+ | ll_Prev()
+ |
+ +-------------------------------------------------------------------------+*/
+LINKED_LIST *ll_Prev(void *vllCurrent) {
+ LINKED_LIST *llCurrent;
+
+ llCurrent = (LINKED_LIST *) vllCurrent;
+
+ if ((llCurrent == NULL) || (llCurrent->next == NULL) || (llCurrent->prev == NULL))
+ return NULL;
+
+ llCurrent = llCurrent->prev;
+
+ if (llCurrent->prev == llCurrent)
+ return NULL;
+ else
+ return llCurrent;
+
+} // ll_Prev
+
+
+/*+-------------------------------------------------------------------------+
+ | ll_InsertAfter()
+ |
+ +-------------------------------------------------------------------------+*/
+LINKED_LIST *ll_InsertAfter(void *vllCurrent, void *vllNew) {
+ LINKED_LIST *llCurrent;
+ LINKED_LIST *llNew;
+
+ llCurrent = (LINKED_LIST *) vllCurrent;
+ llNew = (LINKED_LIST *) vllNew;
+
+ if ((vllCurrent == NULL) || (llNew == NULL))
+ return NULL;
+
+ // change pointers to insert it into the list
+ llNew->next = llCurrent->next;
+
+ // check if at end of list
+ if (llCurrent->next == llCurrent)
+ llNew->prev = llCurrent->prev;
+ else
+ llNew->prev = llCurrent;
+
+ llNew->prev->next = llNew;
+ llNew->next->prev = llNew;
+ return llNew;
+} // ll_InsertAfter
+
+
+/*+-------------------------------------------------------------------------+
+ | ll_InsertBefore()
+ |
+ +-------------------------------------------------------------------------+*/
+LINKED_LIST *ll_InsertBefore(void *vllCurrent, void *vllNew) {
+ LINKED_LIST *llCurrent;
+ LINKED_LIST *llNew;
+
+ llCurrent = (LINKED_LIST *) vllCurrent;
+ llNew = (LINKED_LIST *) vllNew;
+
+ if ((vllCurrent == NULL) || (llNew == NULL))
+ return NULL;
+
+ // change pointers to insert it into the list
+ llNew->prev = llCurrent->prev;
+
+ // check if at start of list
+ if (llCurrent->prev = llCurrent)
+ llNew->next = llCurrent->next;
+ else
+ llNew->next = llCurrent;
+
+ llNew->prev->next = llNew;
+ llNew->next->prev = llNew;
+ return llNew;
+} // ll_InsertBefore
+
+
+/*+-------------------------------------------------------------------------+
+ | ll_Delete()
+ |
+ +-------------------------------------------------------------------------+*/
+LINKED_LIST *ll_Delete(void *vllCurrent) {
+ LINKED_LIST *llCurrent;
+
+ llCurrent = (LINKED_LIST *) vllCurrent;
+
+ if ((llCurrent == NULL) || (llCurrent->next == NULL) || (llCurrent->prev == NULL))
+ return NULL;
+
+ // make sure not on one of the dummy end headers
+ if ((llCurrent->next == llCurrent) || (llCurrent->prev == llCurrent))
+ return NULL;
+
+ // changed pointers to remove it from list
+ llCurrent->prev->next = llCurrent->next;
+ llCurrent->next->prev = llCurrent->prev;
+
+ // Get which one to return as new current record - we generally want to
+ // go to the previous record - unless we deleted the first record, in
+ // which case get the next record. If there are no records, then return
+ // the list head
+ llCurrent = llCurrent->prev;
+
+ // check if at start of list
+ if (llCurrent->prev == llCurrent)
+ llCurrent = llCurrent->next;
+
+ // make sure not at end of list (may have moved here if empty list) - if
+ // so we want to return the starting node
+ if (llCurrent->next == llCurrent)
+ llCurrent = llCurrent->prev;
+
+ return llCurrent;
+} // ll_Delete
+
+
+/*+-------------------------------------------------------------------------+
+ | ll_Home()
+ |
+ +-------------------------------------------------------------------------+*/
+LINKED_LIST *ll_Home(void *vllCurrent) {
+ LINKED_LIST *llCurrent;
+
+ llCurrent = (LINKED_LIST *) vllCurrent;
+ if (llCurrent == NULL)
+ return (LINKED_LIST *) NULL;
+
+ // make sure at start of list
+ while (llCurrent->prev != llCurrent)
+ llCurrent = llCurrent->prev;
+
+ return llCurrent;
+
+} // ll_Home
+
+
+/*+-------------------------------------------------------------------------+
+ | ll_End()
+ |
+ +-------------------------------------------------------------------------+*/
+LINKED_LIST *ll_End(void *vllCurrent) {
+ LINKED_LIST *llCurrent;
+
+ llCurrent = (LINKED_LIST *) vllCurrent;
+ if (llCurrent == NULL)
+ return (LINKED_LIST *) NULL;
+
+ // make sure at end of list
+ while (llCurrent->next != llCurrent)
+ llCurrent = llCurrent->next;
+
+ return llCurrent;
+
+} // ll_End
+
+
+/*+-------------------------------------------------------------------------+
+ | ll_ListFree()
+ |
+ +-------------------------------------------------------------------------+*/
+void ll_ListFree(void *vllHead) {
+ LINKED_LIST *llCurrent;
+ LINKED_LIST *llNext;
+
+ llCurrent = (LINKED_LIST *) vllHead;
+ if (llCurrent == NULL)
+ return;
+
+ // make sure at start of list
+ while (llCurrent->prev != llCurrent)
+ llCurrent = llCurrent->prev;
+
+ // walk the chain - freeing it
+ while ((llCurrent != NULL) && (llCurrent->next != llCurrent)) {
+ llNext = llCurrent->next;
+ FreeMemory(llCurrent);
+ llCurrent = llNext;
+ }
+
+ // at the ending node - kill it as well
+ FreeMemory(llCurrent);
+
+} // ll_ListFree
+
+
+
+
+/*+-------------------------------------------------------------------------+
+ | List Routines
+ +-------------------------------------------------------------------------+*/
+
+/*+-------------------------------------------------------------------------+
+ | map_LineListInit()
+ |
+ +-------------------------------------------------------------------------+*/
+MAP_LINE *map_LineListInit() {
+ MAP_LINE *NewHead;
+ MAP_LINE *NewTail;
+
+ // Create our linked list
+ NewHead = (MAP_LINE *) ll_Init(sizeof(MAP_LINE));
+ if (NewHead == NULL)
+ return (MAP_LINE *) NULL;
+
+ NewTail = NewHead->next;
+
+ // Now init them as appropriate
+ NewHead->Line = NULL;
+ NewTail->Line = NULL;
+
+ return NewHead;
+
+} // map_LineListInit
+
+
+/*+-------------------------------------------------------------------------+
+ | map_SectionListInit()
+ |
+ +-------------------------------------------------------------------------+*/
+MAP_SECTION *map_SectionListInit() {
+ MAP_SECTION *NewHead;
+ MAP_SECTION *NewTail;
+
+ // Create our linked list
+ NewHead = (MAP_SECTION *) ll_Init(sizeof(MAP_SECTION));
+ if (NewHead == NULL)
+ return (MAP_SECTION *) NULL;
+
+ NewTail = NewHead->next;
+
+ // Now init them as appropriate
+ NewHead->Name = NULL;
+ NewTail->Name = NULL;
+
+ NewHead->FirstLine = NewHead->LastLine = NewTail->FirstLine = NewTail->LastLine = NULL;
+ NewHead->LineCount = NewTail->LineCount = 0;
+
+ return NewHead;
+
+} // map_SectionListInit
+
+
+/*+-------------------------------------------------------------------------+
+ | map_LineListFree()
+ |
+ +-------------------------------------------------------------------------+*/
+void map_LineListFree(MAP_LINE *CurrentLine) {
+ MAP_LINE *NextLine;
+
+ if (CurrentLine == NULL)
+ return;
+
+ // make sure at start of list
+ while (CurrentLine->prev != CurrentLine)
+ CurrentLine = CurrentLine->prev;
+
+ // walk the chain - freeing it
+ while ((CurrentLine != NULL) && (CurrentLine->next != CurrentLine)) {
+ NextLine = CurrentLine->next;
+
+ if (CurrentLine->Line != NULL)
+ FreeMemory(CurrentLine->Line);
+
+ FreeMemory(CurrentLine);
+ CurrentLine = NextLine;
+ }
+
+ // at the ending node - kill it as well
+ FreeMemory(CurrentLine);
+
+} // map_LineListFree
+
+
+/*+-------------------------------------------------------------------------+
+ | map_SectionListFree()
+ |
+ +-------------------------------------------------------------------------+*/
+void map_SectionListFree(MAP_SECTION *CurrentSection) {
+ MAP_SECTION *NextSection;
+
+ if (CurrentSection == NULL)
+ return;
+
+ // make sure at start of list
+ while (CurrentSection->prev != CurrentSection)
+ CurrentSection = CurrentSection->prev;
+
+ // walk the chain - freeing it
+ while ((CurrentSection != NULL) && (CurrentSection->next != CurrentSection)) {
+ NextSection = CurrentSection->next;
+
+ map_LineListFree(CurrentSection->FirstLine);
+
+ if (CurrentSection->Name != NULL)
+ FreeMemory(CurrentSection->Name);
+
+ FreeMemory(CurrentSection);
+ CurrentSection = NextSection;
+ }
+
+ // at the ending node - kill it as well
+ FreeMemory(CurrentSection);
+
+} // map_SectionListFree
+
+
+
+
+/*+-------------------------------------------------------------------------+
+ | Section Routines
+ +-------------------------------------------------------------------------+*/
+
+/*+-------------------------------------------------------------------------+
+ | map_SectionInit()
+ |
+ +-------------------------------------------------------------------------+*/
+MAP_SECTION *map_SectionInit(LPTSTR Section) {
+ MAP_SECTION *NewSection;
+ MAP_LINE *FirstLine;
+
+ NewSection = (MAP_SECTION *) AllocMemory(sizeof(MAP_SECTION));
+ if (NewSection == NULL)
+ return (MAP_SECTION *) NULL;
+
+ // Init the section name
+ NewSection->Name = (LPTSTR) AllocMemory((lstrlen(Section) + 1) * sizeof(TCHAR));
+
+ if (NewSection->Name == NULL) {
+ FreeMemory(NewSection);
+ return (MAP_SECTION *) NULL;
+ }
+
+ // Now create the line list
+ FirstLine = map_LineListInit();
+ if (FirstLine == NULL) {
+ FreeMemory(NewSection->Name);
+ FreeMemory(NewSection);
+ return (MAP_SECTION *) NULL;
+ }
+
+ lstrcpy(NewSection->Name, Section);
+ NewSection->LineCount = 0;
+ NewSection->FirstLine = FirstLine;
+ NewSection->LastLine = FirstLine->next;
+
+ return NewSection;
+} // map_SectionInit
+
+
+/*+-------------------------------------------------------------------------+
+ | map_SectionAdd()
+ |
+ +-------------------------------------------------------------------------+*/
+void map_SectionAdd(MAP_FILE *hMap, LPTSTR Section) {
+ MAP_SECTION *NewSection;
+
+ NewSection = map_SectionInit(Section);
+ if (NewSection == NULL)
+ return;
+
+ // Add it to the section list
+ ll_InsertBefore((void *) hMap->LastSection, (void *) NewSection);
+
+ // Init it so the added section is currently selected
+ hMap->CurrentSection = NewSection;
+ hMap->CurrentLine = hMap->CurrentSection->FirstLine;
+
+} // map_SectionAdd
+
+
+/*+-------------------------------------------------------------------------+
+ | map_SectionDelete()
+ |
+ +-------------------------------------------------------------------------+*/
+void map_SectionDelete(MAP_FILE *hMap) {
+ MAP_SECTION *CurrentSection;
+ MAP_SECTION *NewSection;
+
+ // if no section is currently selected then get out
+ CurrentSection = hMap->CurrentSection;
+ if (CurrentSection == NULL)
+ return;
+
+ // Remove this from the chain
+ NewSection = (MAP_SECTION *) ll_Delete((void *) CurrentSection);
+
+ // walk the lines and remove them...
+ map_LineListFree(CurrentSection->FirstLine);
+
+ // All lines have been removed, so remove section header itself
+ FreeMemory(CurrentSection->Name);
+ FreeMemory(CurrentSection);
+
+ // Update Section count
+ if (hMap->SectionCount > 0)
+ hMap->SectionCount--;
+
+} // map_SectionDelete
+
+
+/*+-------------------------------------------------------------------------+
+ | map_SectionInsertBefore()
+ |
+ +-------------------------------------------------------------------------+*/
+void map_SectionInsertBefore(MAP_FILE *hMap, LPTSTR Section) {
+ MAP_SECTION *CurrentSection;
+
+ if (hMap->CurrentSection == NULL)
+ return;
+
+ CurrentSection = map_SectionInit(Section);
+ if (CurrentSection == NULL)
+ return;
+
+ ll_InsertBefore((void *) hMap->CurrentSection, (void *) CurrentSection);
+
+} // map_SectionInsertBefore
+
+
+/*+-------------------------------------------------------------------------+
+ | map_SectionInsertAfter()
+ |
+ +-------------------------------------------------------------------------+*/
+void map_SectionInsertAfter(MAP_FILE *hMap, LPTSTR Section) {
+ MAP_SECTION *CurrentSection;
+
+ if (hMap->CurrentSection == NULL)
+ return;
+
+ CurrentSection = map_SectionInit(Section);
+ if (CurrentSection == NULL)
+ return;
+
+ ll_InsertAfter((void *) hMap->CurrentSection, (void *) CurrentSection);
+
+} // map_SectionInsertAfter
+
+
+/*+-------------------------------------------------------------------------+
+ | map_SectionNext()
+ |
+ +-------------------------------------------------------------------------+*/
+MAP_SECTION *map_SectionNext(MAP_FILE *hMap) {
+ MAP_SECTION *CurrentSection;
+
+ if (hMap->CurrentSection == NULL)
+ return NULL;
+
+ CurrentSection = (MAP_SECTION *) ll_Next((void *) hMap->CurrentSection);
+ if (CurrentSection != NULL)
+ hMap->CurrentSection = CurrentSection;
+
+ return CurrentSection;
+} // map_SectionNext
+
+
+/*+-------------------------------------------------------------------------+
+ | map_SectionPrev()
+ |
+ +-------------------------------------------------------------------------+*/
+MAP_SECTION *map_SectionPrev(MAP_FILE *hMap) {
+ MAP_SECTION *CurrentSection;
+
+ if (hMap->CurrentSection == NULL)
+ return NULL;
+
+ CurrentSection = (MAP_SECTION *) ll_Prev((void *) hMap->CurrentSection);
+
+ if (CurrentSection != NULL)
+ hMap->CurrentSection = CurrentSection;
+
+ return CurrentSection;
+} // map_SectionPrev
+
+
+/*+-------------------------------------------------------------------------+
+ | map_SectionFind()
+ |
+ +-------------------------------------------------------------------------+*/
+MAP_SECTION *map_SectionFind(MAP_FILE *hMap, LPTSTR Section) {
+ MAP_SECTION *CurrentSection;
+
+ CurrentSection = hMap->FirstSection;
+ while (CurrentSection && lstrcmpi(CurrentSection->Name, Section))
+ CurrentSection = (MAP_SECTION *) ll_Next((void *) CurrentSection);
+
+ if (CurrentSection != NULL) {
+ hMap->CurrentSection = CurrentSection;
+ hMap->CurrentLine = hMap->CurrentSection->FirstLine;
+ }
+
+ return CurrentSection;
+} // map_SectionFind
+
+
+/*+-------------------------------------------------------------------------+
+ | map_SectionHome()
+ |
+ +-------------------------------------------------------------------------+*/
+void map_SectionHome(MAP_FILE *hMap) {
+ hMap->CurrentSection = hMap->FirstSection;
+
+ hMap->CurrentLine = hMap->CurrentSection->FirstLine;
+} // map_SectionHome
+
+
+/*+-------------------------------------------------------------------------+
+ | map_SectionEnd()
+ |
+ +-------------------------------------------------------------------------+*/
+void map_SectionEnd(MAP_FILE *hMap) {
+ MAP_SECTION *CurrentSection;
+
+ CurrentSection = hMap->FirstSection;
+ while (CurrentSection && (CurrentSection->next != NULL))
+ CurrentSection = CurrentSection->next;
+
+ hMap->CurrentSection = CurrentSection;
+ hMap->CurrentLine = CurrentSection->FirstLine;
+} // map_SectionEnd
+
+
+
+
+/*+-------------------------------------------------------------------------+
+ | Line Routines
+ +-------------------------------------------------------------------------+*/
+
+/*+-------------------------------------------------------------------------+
+ | map_LineInit()
+ |
+ +-------------------------------------------------------------------------+*/
+MAP_LINE *map_LineInit(LPTSTR Line) {
+ MAP_LINE *NewLine;
+
+ NewLine = (MAP_LINE *) AllocMemory(sizeof(MAP_LINE));
+ if (NewLine == NULL)
+ return (MAP_LINE *) NULL;
+
+ NewLine->Line = (LPTSTR) AllocMemory((lstrlen(Line) + 1) * sizeof(TCHAR));
+
+ if (NewLine->Line == NULL) {
+ FreeMemory(NewLine);
+ return (MAP_LINE *) NULL;
+ }
+
+ lstrcpy(NewLine->Line, Line);
+ return NewLine;
+
+} // map_LineInit
+
+
+/*+-------------------------------------------------------------------------+
+ | map_LineAdd()
+ |
+ +-------------------------------------------------------------------------+*/
+MAP_LINE *map_LineAdd(MAP_FILE *hMap, LPTSTR Line) {
+ MAP_LINE *NewLine;
+
+ // make sure there is something to add it to
+ if ((hMap->CurrentSection == NULL) || (hMap->CurrentSection->LastLine == NULL))
+ return (MAP_LINE *) NULL;
+
+ // Create the new line
+ NewLine = map_LineInit(Line);
+
+ if (NewLine->Line == NULL)
+ return (MAP_LINE *) NULL;
+
+ // ...and add it to our list
+ ll_InsertBefore((void *) hMap->CurrentSection->LastLine, (void *) NewLine);
+
+ // Init it so the added line is currently selected
+ hMap->CurrentLine = NewLine;
+ hMap->CurrentSection->LineCount++;
+ return NewLine;
+
+} // map_LineAdd
+
+
+/*+-------------------------------------------------------------------------+
+ | map_LineDelete()
+ |
+ +-------------------------------------------------------------------------+*/
+void map_LineDelete(MAP_FILE *hMap) {
+ MAP_LINE *CurrentLine;
+ MAP_LINE *NewLine;
+
+ // if no section is currently selected then get out
+ if ((hMap->CurrentSection == NULL) || (hMap->CurrentLine == NULL))
+ return;
+
+ CurrentLine = hMap->CurrentLine;
+ NewLine = (MAP_LINE *) ll_Delete((void *) CurrentLine);
+
+ // All lines have been removed, so remove section header itself
+ FreeMemory(CurrentLine->Line);
+ FreeMemory(CurrentLine);
+
+ // update hMap
+ if (NewLine != NULL)
+ hMap->CurrentLine = NewLine;
+ else
+ hMap->CurrentLine = hMap->CurrentSection->FirstLine;
+
+ if (hMap->CurrentSection->LineCount > 0)
+ hMap->CurrentSection->LineCount--;
+
+} // map_LineDelete
+
+
+/*+-------------------------------------------------------------------------+
+ | map_LineInsertBefore()
+ |
+ +-------------------------------------------------------------------------+*/
+void map_LineInsertBefore(MAP_FILE *hMap, LPTSTR Line) {
+ MAP_LINE *NewLine;
+
+ if ((hMap->CurrentSection == NULL) || (hMap->CurrentLine == NULL))
+ return;
+
+ NewLine = map_LineInit(Line);
+ if (NewLine == NULL)
+ return;
+
+ ll_InsertBefore((void *) hMap->CurrentLine, (void *) NewLine);
+} // map_LineInsertBefore
+
+
+/*+-------------------------------------------------------------------------+
+ | map_LineInsertAfter()
+ |
+ +-------------------------------------------------------------------------+*/
+void map_LineInsertAfter(MAP_FILE *hMap, LPTSTR Line) {
+ MAP_LINE *NewLine;
+
+ if ((hMap->CurrentSection == NULL) || (hMap->CurrentLine == NULL))
+ return;
+
+ NewLine = map_LineInit(Line);
+ if (NewLine == NULL)
+ return;
+
+ ll_InsertAfter((void *) hMap->CurrentLine, (void *) NewLine);
+} // map_LineInsertAfter
+
+
+/*+-------------------------------------------------------------------------+
+ | map_LineNext()
+ |
+ +-------------------------------------------------------------------------+*/
+MAP_LINE *map_LineNext(MAP_FILE *hMap) {
+ MAP_LINE *CurrentLine;
+
+ if ((hMap->CurrentSection == NULL) || (hMap->CurrentLine == NULL))
+ return NULL;
+
+ CurrentLine = (MAP_LINE *) ll_Next((void *) hMap->CurrentLine);
+ if (CurrentLine != NULL)
+ hMap->CurrentLine = CurrentLine;
+
+ return CurrentLine;
+} // map_LineNext
+
+
+/*+-------------------------------------------------------------------------+
+ | map_LinePrev()
+ |
+ +-------------------------------------------------------------------------+*/
+MAP_LINE *map_LinePrev(MAP_FILE *hMap) {
+ MAP_LINE *CurrentLine;
+
+ if ((hMap->CurrentSection == NULL) || (hMap->CurrentLine == NULL))
+ return NULL;
+
+ CurrentLine = (MAP_LINE *) ll_Prev((void *) hMap->CurrentLine);
+ if (CurrentLine != NULL)
+ hMap->CurrentLine = CurrentLine;
+
+ return CurrentLine;
+} // map_LinePrev
+
+
+/*+-------------------------------------------------------------------------+
+ | map_LineHome()
+ |
+ +-------------------------------------------------------------------------+*/
+void map_LineHome(MAP_FILE *hMap) {
+
+ if ((hMap->CurrentSection == NULL) || (hMap->CurrentLine == NULL))
+ return;
+
+ hMap->CurrentLine = (MAP_LINE *) ll_Home((void *) hMap->CurrentLine);
+} // map_LineHome
+
+
+/*+-------------------------------------------------------------------------+
+ | map_LineEnd()
+ |
+ +-------------------------------------------------------------------------+*/
+void map_LineEnd(MAP_FILE *hMap) {
+ if ((hMap->CurrentSection == NULL) || (hMap->CurrentLine == NULL))
+ return;
+
+ hMap->CurrentLine = (MAP_LINE *) ll_End((void *) hMap->CurrentLine);
+} // map_LineEnd
+
+
+
+
+/*+-------------------------------------------------------------------------+
+ | Map File Routines
+ +-------------------------------------------------------------------------+*/
+
+/*+-------------------------------------------------------------------------+
+ | map_Home()
+ |
+ +-------------------------------------------------------------------------+*/
+void map_Home(MAP_FILE *hMap) {
+ hMap->CurrentSection = hMap->FirstSection;
+
+} // map_Home
+
+
+/*+-------------------------------------------------------------------------+
+ | map_End()
+ |
+ +-------------------------------------------------------------------------+*/
+void map_End(MAP_FILE *hMap) {
+ MAP_SECTION *CurrentSection;
+ MAP_LINE *CurrentLine = NULL;
+
+ CurrentSection = hMap->FirstSection;
+ while (CurrentSection->next != NULL)
+ CurrentSection = CurrentSection->next;
+
+ CurrentLine = CurrentSection->FirstLine;
+
+ if (CurrentLine != NULL)
+ while (CurrentLine->next != NULL)
+ CurrentLine = CurrentLine->next;
+
+ hMap->CurrentSection = CurrentSection;
+ hMap->CurrentLine = CurrentLine;
+} // map_End
+
+
+/*+-------------------------------------------------------------------------+
+ | map_Open()
+ |
+ +-------------------------------------------------------------------------+*/
+MAP_FILE *map_Open(TCHAR *FileName) {
+ MAP_FILE *hMap = NULL;
+ HANDLE hFile = NULL;
+ char *FileCache = NULL;
+ char *chA;
+ char *pchA;
+ DWORD wrote;
+ char lpszA[MAX_LINE_LEN + 1];
+ TCHAR lpsz[MAX_LINE_LEN + 1];
+ TCHAR tmpStr[MAX_LINE_LEN + 1];
+ char FileNameA[MAX_PATH + 1];
+ ULONG Size;
+ TCHAR *ch;
+ TCHAR *pch;
+ TCHAR *lch;
+ DWORD FSize;
+
+ MAP_SECTION *CurrentSection = NULL;
+ MAP_LINE *CurrentLine = NULL;
+
+ WideCharToMultiByte(CP_ACP, 0, FileName, -1, FileNameA, sizeof(FileNameA), NULL, NULL);
+
+ hFile = CreateFileA( FileNameA, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (hFile == (HANDLE) INVALID_HANDLE_VALUE)
+ return hMap;
+
+ FSize = GetFileSize(hFile, NULL);
+
+ FileCache = (char *) AllocMemory(FSize + 1);
+ if (FileCache == NULL)
+ goto map_OpenExit;
+
+ hMap = (MAP_FILE *) AllocMemory(sizeof(MAP_FILE) + ((lstrlen(FileName) + 1) * sizeof(TCHAR)));
+ if (hMap == NULL)
+ goto map_OpenExit;
+
+ // Init the section list
+ CurrentSection = map_SectionListInit();
+ if (CurrentSection == NULL) {
+ FreeMemory(hMap);
+ hMap = NULL;
+ goto map_OpenExit;
+ }
+
+ hMap->FirstSection = CurrentSection;
+ hMap->CurrentSection = CurrentSection;
+ hMap->LastSection = CurrentSection->next;
+
+ // Init the header info
+ if (hMap != NULL) {
+ hMap->hf = hFile;
+ hMap->Modified = FALSE;
+ hMap->CurrentLine = NULL;
+ hMap->SectionCount = 0;
+
+ lstrcpy(hMap->Name, FileName);
+ }
+
+ memset(FileCache, 0, FSize + 1);
+
+ // Read in the whole file then parse it - it shouldn't be that large, a
+ // very full NW server generated ~20K map file
+ if (!ReadFile(hFile, FileCache, FSize, &wrote, NULL))
+ goto map_OpenExit;
+
+ // Now walk and parse the buffer - remember it's in ASCII
+ chA = FileCache;
+ while (*chA) {
+ // Get past any white space junk at beginning of line
+ while(*chA && ((*chA == ' ') || (*chA == '\t')))
+ chA++;
+
+ // transfer a line of text
+ Size = 0;
+ pchA = lpszA;
+ while (*chA && (*chA != '\n') && (*chA != '\r') && (Size < MAX_LINE_LEN))
+ *pchA++ = *chA++;
+
+ *pchA = '\0';
+ if (*chA == '\r')
+ chA++;
+ if (*chA == '\n')
+ chA++;
+
+ // ...convert line to Unicode
+ MultiByteToWideChar(CP_ACP, 0, lpszA, -1, lpsz, sizeof(lpsz) );
+
+ //
+ // Now have a line of text - figure out what it is and update data
+ // structures
+ //
+
+ // ...Check for section header
+ ch = lpsz;
+ lch = pch = tmpStr;
+ if (*ch == TEXT(SECTION_BEGIN_CHAR)) {
+ // Find end section brace - keeping track of last non-white space char
+ // anything after end-section-brace is discarded. Any trailing/
+ // leading whitespace in section header is removed
+ ch++; // get past section-begin
+
+ // remove any leading whitespace
+ while(*ch && ((*ch == TEXT(' ')) || (*ch == TEXT('\t'))))
+ ch++;
+
+ // transfer it to tmpStr (via pch pointer)
+ while (*ch && (*ch != TEXT(SECTION_END_CHAR))) {
+ // keep track of last non-whitespace
+ if ((*ch != TEXT(' ')) && (*ch != TEXT('\t'))) {
+ lch = pch;
+ lch++;
+ }
+
+ *pch++ = *ch++;
+ }
+
+ // NULL terminate before last section of whitespace
+ *lch = TEXT('\0');
+
+ // Allocate a new section-header block and init
+ map_SectionAdd(hMap, tmpStr);
+ } else {
+ // Not section header, so normal line - copy to tmpStr via pch pointer
+ while (*ch) {
+ // keep track of last non-whitespace
+ if ((*ch != TEXT(' ')) && (*ch != TEXT('\t'))) {
+ lch = pch;
+ lch++;
+ }
+
+ *pch++ = *ch++;
+ }
+
+ // NULL terminate before last section of whitespace
+ *lch = TEXT('\0');
+
+ // ...add it to the list
+ map_LineAdd(hMap, tmpStr);
+ }
+
+ // Done with the line of text, so just loop back up to parse next
+ // line
+ }
+
+map_OpenExit:
+
+ FreeMemory(FileCache);
+ return hMap;
+
+} // map_Open
+
+
+/*+-------------------------------------------------------------------------+
+ | map_Close()
+ |
+ +-------------------------------------------------------------------------+*/
+void map_Close(MAP_FILE *hMap) {
+ // If file modified then re-write file.
+ if ( hMap->Modified )
+ ;
+
+ // Write out cache of file, close it and clean up all the data structures
+ CloseHandle( hMap->hf );
+ hMap = NULL;
+} // map_Close
+
+
+/*+-------------------------------------------------------------------------+
+ | ParseWord()
+ |
+ +-------------------------------------------------------------------------+*/
+void ParseWord(TCHAR **lpch, LPTSTR tmpStr) {
+ TCHAR *ch;
+ TCHAR *lch;
+ TCHAR *pch;
+
+ ch = *lpch;
+ lch = pch = tmpStr;
+
+ // remove any leading whitespace
+ while(*ch && ((*ch == TEXT(' ')) || (*ch == TEXT('\t'))))
+ ch++;
+
+ // transfer it to tmpStr (via pch pointer)
+ while (*ch && (*ch != TEXT(WORD_DELIMITER))) {
+ // keep track of last non-whitespace
+ if ((*ch != TEXT(' ')) && (*ch != TEXT('\t'))) {
+ lch = pch;
+ lch++;
+ }
+
+ *pch++ = *ch++;
+ }
+
+ if (*ch == TEXT(WORD_DELIMITER))
+ ch++;
+
+ // NULL terminate before last section of whitespace
+ *lch = TEXT('\0');
+ *lpch = ch;
+
+} // ParseWord
+
+
+/*+-------------------------------------------------------------------------+
+ | map_ParseUser()
+ |
+ +-------------------------------------------------------------------------+*/
+void map_ParseUser(LPTSTR Line, LPTSTR Name, LPTSTR NewName, LPTSTR Password) {
+ TCHAR *pch = Line;
+
+ lstrcpy(Name, TEXT(""));
+ lstrcpy(NewName, TEXT(""));
+ lstrcpy(Password, TEXT(""));
+
+ if (Line == NULL)
+ return;
+
+ ParseWord(&pch, Name);
+ if (lstrlen(Name) >= MAX_USER_NAME_LEN)
+ lstrcpy(Name, TEXT(""));
+
+ ParseWord(&pch, NewName);
+ if (lstrlen(NewName) >= MAX_USER_NAME_LEN)
+ lstrcpy(NewName, TEXT(""));
+
+ ParseWord(&pch, Password);
+ if (lstrlen(Password) > MAX_PW_LEN)
+ lstrcpy(Password, TEXT(""));
+
+} // map_ParseUser
+
+
+/*+-------------------------------------------------------------------------+
+ | map_ParseGroup()
+ |
+ +-------------------------------------------------------------------------+*/
+void map_ParseGroup(LPTSTR Line, LPTSTR Name, LPTSTR NewName) {
+ TCHAR *pch = Line;
+
+ lstrcpy(Name, TEXT(""));
+ lstrcpy(NewName, TEXT(""));
+
+ if (Line == NULL)
+ return;
+
+ ParseWord(&pch, Name);
+ if (lstrlen(Name) >= MAX_GROUP_NAME_LEN)
+ lstrcpy(Name, TEXT(""));
+
+ ParseWord(&pch, NewName);
+ if (lstrlen(NewName) >= MAX_GROUP_NAME_LEN)
+ lstrcpy(NewName, TEXT(""));
+
+} // map_ParseGroup
+
+
+/*+-------------------------------------------------------------------------+
+ | map_UsersEnum()
+ |
+ +-------------------------------------------------------------------------+*/
+DWORD map_UsersEnum(MAP_FILE *hMap, USER_LIST **lpUsers) {
+ DWORD ret = 0;
+ USER_LIST *UserList = NULL;
+ USER_BUFFER *UserBuffer = NULL;
+ MAP_SECTION *CurrentSection = NULL;
+ MAP_LINE *CurrentLine = NULL;
+ ULONG Entries = 0;
+ ULONG ActualEntries = 0;
+ TCHAR Name[MAX_LINE_LEN + 1];
+ TCHAR NewName[MAX_LINE_LEN + 1];
+ TCHAR Password[MAX_LINE_LEN + 1];
+ ULONG i;
+
+ CurrentSection = map_SectionFind(hMap, Lids(IDS_M_7));
+ if (CurrentSection != NULL)
+ Entries = CurrentSection->LineCount;
+
+ UserList = AllocMemory(sizeof(USER_LIST) + (sizeof(USER_BUFFER) * Entries));
+
+ if (!UserList) {
+ ret = ERROR_NOT_ENOUGH_MEMORY;
+ } else {
+ UserBuffer = UserList->UserBuffer;
+
+ for (i = 0; i < Entries; i++) {
+ CurrentLine = map_LineNext(hMap);
+
+ if (CurrentLine != NULL) {
+ map_ParseUser(CurrentLine->Line, Name, NewName, Password);
+
+ if (lstrlen(Name)) {
+ lstrcpy(UserBuffer[ActualEntries].Name, Name);
+ lstrcpy(UserBuffer[ActualEntries].NewName, NewName);
+ lstrcpy(UserBuffer[ActualEntries].Password, Password);
+
+ if (lstrcmpi(Name, NewName))
+ UserBuffer[ActualEntries].IsNewName = TRUE;
+
+ ActualEntries++;
+ }
+ }
+ }
+
+ if (ActualEntries != Entries)
+ UserList = (USER_LIST *) ReallocMemory((HGLOBAL) UserList, sizeof(USER_LIST) + (sizeof(USER_BUFFER)* ActualEntries));
+
+ if (UserList == NULL)
+ ret = ERROR_NOT_ENOUGH_MEMORY;
+ else {
+ // Sort the server list before putting it in the dialog
+ UserBuffer = UserList->UserBuffer;
+ qsort((void *) UserBuffer, (size_t) ActualEntries, sizeof(USER_BUFFER), UserListCompare);
+ UserList->Count = ActualEntries;
+ }
+
+ *lpUsers = UserList;
+ }
+
+ return ret;
+} // map_UsersEnum
+
+
+/*+-------------------------------------------------------------------------+
+ | map_GroupsEnum()
+ |
+ +-------------------------------------------------------------------------+*/
+DWORD map_GroupsEnum(MAP_FILE *hMap, GROUP_LIST **lpGroups) {
+ DWORD ret = 0;
+ GROUP_LIST *GroupList = NULL;
+ GROUP_BUFFER *GroupBuffer = NULL;
+ MAP_SECTION *CurrentSection = NULL;
+ MAP_LINE *CurrentLine = NULL;
+ ULONG Entries = 0;
+ ULONG ActualEntries = 0;
+ TCHAR Name[MAX_LINE_LEN + 1];
+ TCHAR NewName[MAX_LINE_LEN + 1];
+ ULONG i;
+
+
+ CurrentSection = map_SectionFind(hMap, Lids(IDS_M_8));
+ if (CurrentSection != NULL)
+ Entries = CurrentSection->LineCount;
+
+ GroupList = AllocMemory(sizeof(GROUP_LIST) + (sizeof(GROUP_BUFFER) * Entries));
+
+ if (!GroupList) {
+ ret = ERROR_NOT_ENOUGH_MEMORY;
+ } else {
+ GroupBuffer = GroupList->GroupBuffer;
+
+ for (i = 0; i < Entries; i++) {
+ CurrentLine = map_LineNext(hMap);
+
+ if (CurrentLine != NULL) {
+ map_ParseGroup(CurrentLine->Line, Name, NewName);
+
+ if (lstrlen(Name)) {
+ lstrcpy(GroupBuffer[ActualEntries].Name, Name);
+ lstrcpy(GroupBuffer[ActualEntries].NewName, NewName);
+
+ if (lstrcmpi(Name, NewName))
+ GroupBuffer[ActualEntries].IsNewName = TRUE;
+
+ ActualEntries++;
+ }
+ }
+ }
+
+ if (ActualEntries != Entries)
+ GroupList = (GROUP_LIST *) ReallocMemory((HGLOBAL) GroupList, sizeof(GROUP_LIST) + (sizeof(GROUP_BUFFER)* ActualEntries));
+
+ if (GroupList == NULL)
+ ret = ERROR_NOT_ENOUGH_MEMORY;
+ else
+ GroupList->Count = ActualEntries;
+
+ *lpGroups = GroupList;
+ }
+
+ return ret;
+} // map_GroupsEnum
+
+
+/*+-------------------------------------------------------------------------+
+ | MapFileWrite()
+ |
+ +-------------------------------------------------------------------------+*/
+BOOL MapFileWrite(HANDLE hFile, LPTSTR String) {
+ DWORD wrote;
+ static char tmpStr[MAX_LINE_LEN + 1];
+
+ WideCharToMultiByte(CP_ACP, 0, String, -1, tmpStr, sizeof(tmpStr), NULL, NULL);
+
+ if (!WriteFile(hFile, tmpStr, strlen(tmpStr), &wrote, NULL))
+ return FALSE;
+
+ return TRUE;
+} // MapFileWrite
+
+
+/*+-------------------------------------------------------------------------+
+ | MapFileOpen()
+ |
+ +-------------------------------------------------------------------------+*/
+HANDLE MapFileOpen(LPTSTR FileNameW) {
+ int ret;
+ HANDLE hFile = NULL;
+ char FileName[MAX_PATH + 1];
+
+ WideCharToMultiByte(CP_ACP, 0, FileNameW, -1, FileName, sizeof(FileName), NULL, NULL);
+
+ DeleteFile(FileNameW);
+
+ // Now do the actual creation with error handling...
+ do {
+ ret = IDOK;
+ hFile = CreateFileA( FileName, GENERIC_WRITE, 0, NULL, CREATE_NEW,
+ FILE_ATTRIBUTE_NORMAL, NULL );
+
+ if (hFile == INVALID_HANDLE_VALUE)
+ ret = ErrorBoxRetry(Lids(IDS_MAPCREATEFAIL));
+
+ } while(ret == IDRETRY);
+
+ return(hFile);
+
+} // MapFileOpen
+
+
+
+/*+-------------------------------------------------------------------------+
+ | MappingFileCreate()
+ |
+ | Creates a mapping file. This allows the admin to specify for each
+ | user a new username and password.
+ |
+ +-------------------------------------------------------------------------+*/
+BOOL MappingFileCreate(LPTSTR FileName, LPTSTR Server) {
+ BOOL status = FALSE;
+ DWORD ret = 0;
+ USER_LIST *Users;
+ DWORD UserCount;
+ GROUP_LIST *Groups;
+ GROUP_BUFFER *GroupBuffer;
+ USER_BUFFER *UserBuffer;
+ DWORD GroupCount;
+ int Count;
+ HANDLE hFile = NULL;
+ static TCHAR tmpStr[MAX_LINE_LEN + 1];
+ static TCHAR tmpStr2[MAX_LINE_LEN + 1];
+
+ // Create Empty map file
+ hFile = MapFileOpen(FileName);
+ if (hFile == INVALID_HANDLE_VALUE)
+ return FALSE;
+
+ CursorHourGlass();
+
+ // Now write out header gunk
+ status = MapFileWrite(hFile, Lids(IDS_LINE));
+ wsprintf(tmpStr, Lids(IDS_M_1), Server);
+ wsprintf(tmpStr2, Lids(IDS_BRACE), tmpStr);
+ if (status)
+ status = MapFileWrite(hFile, tmpStr2);
+
+ wsprintf(tmpStr, Lids(IDS_BRACE), Lids(IDS_M_2));
+ if (status)
+ status = MapFileWrite(hFile, tmpStr);
+
+ wsprintf(tmpStr, Lids(IDS_BRACE), TEXT(""));
+ if (status)
+ status = MapFileWrite(hFile, tmpStr);
+
+ wsprintf(tmpStr, Lids(IDS_BRACE), Lids(IDS_M_3));
+ if (status)
+ status = MapFileWrite(hFile, tmpStr);
+
+ wsprintf(tmpStr, Lids(IDS_BRACE), Lids(IDS_M_4));
+ if (status)
+ status = MapFileWrite(hFile, tmpStr);
+
+ wsprintf(tmpStr, Lids(IDS_BRACE), TEXT(""));
+ if (status)
+ status = MapFileWrite(hFile, tmpStr);
+
+ if (status)
+ status = MapFileWrite(hFile, Lids(IDS_LINE));
+
+ // [USERS] section header
+ if (DoUsers && status)
+ status = MapFileWrite(hFile, Lids(IDS_M_5));
+
+ // If anything went wrong with writing header, get out
+ if (!status) {
+ CursorNormal();
+ return FALSE;
+ }
+
+ // Header is all done - now lets do the actual users and such
+ if (!(ret = NWServerSet(Server))) {
+ //
+ // If users were selected then put them into the map file
+ //
+ if (DoUsers) {
+ if (!NWUsersEnum(&Users, FALSE) && (Users != NULL)) {
+ UserCount = Users->Count;
+ UserBuffer = Users->UserBuffer;
+
+ for (Count = 0; Count < (int) UserCount; Count++) {
+ if (status) {
+ switch(PasswordOption) {
+ case 0: // No password
+ wsprintf(tmpStr, TEXT("%s, %s,\r\n"), UserBuffer[Count].Name, UserBuffer[Count].Name);
+ break;
+
+ case 1: // Password is username
+ wsprintf(tmpStr, TEXT("%s, %s, %s\r\n"), UserBuffer[Count].Name, UserBuffer[Count].Name, UserBuffer[Count].Name);
+ break;
+
+ case 2: // Password is constant
+ wsprintf(tmpStr, TEXT("%s, %s, %s\r\n"), UserBuffer[Count].Name, UserBuffer[Count].Name, PasswordConstant);
+ break;
+ }
+ status = MapFileWrite(hFile, tmpStr);
+ }
+ }
+
+ FreeMemory((LPBYTE) Users);
+ }
+ }
+
+ //
+ // If groups were selected then put them in map file
+ //
+ if (DoGroups) {
+ // [GROUPS] section header
+ if (status)
+ status = MapFileWrite(hFile, Lids(IDS_M_6));
+
+ if (!NWGroupsEnum(&Groups, FALSE) && (Groups != NULL)) {
+ GroupCount = Groups->Count;
+ GroupBuffer = Groups->GroupBuffer;
+
+ for (Count = 0; Count < (int) GroupCount; Count++) {
+ if (status) {
+ wsprintf(tmpStr, TEXT("%s, %s\r\n"), GroupBuffer[Count].Name, GroupBuffer[Count].Name);
+ status = MapFileWrite(hFile, tmpStr);
+ }
+ }
+
+ FreeMemory((LPBYTE) Groups);
+ }
+
+ }
+
+ NWServerFree();
+ }
+
+ CloseHandle( hFile );
+ CursorNormal();
+ return status;
+
+} // MappingFileCreate
+
+
+/*+-------------------------------------------------------------------------+
+ | MappingFileNameResolve()
+ |
+ +-------------------------------------------------------------------------+*/
+BOOL MappingFileNameResolve(HWND hDlg) {
+ HWND hCtrl;
+ static char FileNameA[MAX_PATH + 1];
+ static char CmdLine[MAX_PATH + 1 + 12]; // Editor + file
+ TCHAR drive[MAX_DRIVE + 1];
+ TCHAR dir[MAX_PATH + 1];
+ TCHAR fname[MAX_PATH + 1];
+ TCHAR ext[_MAX_EXT + 1];
+ UINT uReturn;
+ BOOL ret = TRUE;
+
+ // First check filename
+ hCtrl = GetDlgItem(hDlg, IDC_MAPPINGFILE);
+ * (WORD *)MappingFile = sizeof(MappingFile);
+ SendMessage(hCtrl, EM_GETLINE, 0, (LPARAM) MappingFile);
+ lsplitpath(MappingFile, drive, dir, fname, ext);
+
+ // remake path so it is fully qualified
+ if ((drive[0] == TEXT('\0')) && (dir[0] == TEXT('\0')))
+ lstrcpy(dir, ProgPath);
+
+ if (ext[0] == TEXT('\0'))
+ lstrcpy(ext, Lids(IDS_S_36));
+
+ lmakepath(MappingFile, drive, dir, fname, ext);
+
+ if (MappingFileCreate(MappingFile, SourceServ)) {
+ if (MessageBox(hDlg, Lids(IDS_MAPCREATED), Lids(IDS_APPNAME), MB_YESNO | MB_ICONQUESTION) == IDYES) {
+
+ WideCharToMultiByte(CP_ACP, 0, MappingFile, -1, FileNameA, sizeof(FileNameA), NULL, NULL);
+
+ wsprintfA(CmdLine, "Notepad %s", FileNameA);
+ uReturn = WinExec(CmdLine, SW_SHOW);
+ }
+ } else {
+ MessageBox(hDlg, Lids(IDS_MAPCREATEFAIL), Lids(IDS_TXTWARNING), MB_OK);
+ ret = FALSE;
+ }
+
+ return ret;
+
+} // MappingFileNameResolve
+
+
+/*+-------------------------------------------------------------------------+
+ | MapCreate()
+ |
+ +-------------------------------------------------------------------------+*/
+LRESULT CALLBACK MapCreateProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
+ HWND hCtrl;
+ int wmId, wmEvent;
+ static short UserNameTab, GroupNameTab, PasswordsTab, DefaultsTab;
+
+ switch (message) {
+
+ case WM_INITDIALOG:
+ // Center the dialog over the application window
+ CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
+
+ // limit edit field lengths
+ hCtrl = GetDlgItem(hDlg, IDC_MAPPINGFILE);
+ PostMessage(hCtrl, EM_LIMITTEXT, (WPARAM) MAX_PATH, 0);
+ hCtrl = GetDlgItem(hDlg, IDC_PWCONST);
+ PostMessage(hCtrl, EM_LIMITTEXT, (WPARAM) MAX_PW_LEN, 0);
+
+ // set mapping file name and init OK button appropriatly
+ hCtrl = GetDlgItem(hDlg, IDC_MAPPINGFILE);
+ SendMessage(hCtrl, WM_SETTEXT, 0, (LPARAM) MappingFile);
+ hCtrl = GetDlgItem(hDlg, IDC_MAPPINGFILE);
+
+ if (SendMessage(hCtrl, EM_LINELENGTH, 0, 0)) {
+ hCtrl = GetDlgItem(hDlg, IDOK);
+ EnableWindow(hCtrl, TRUE);
+ } else {
+ hCtrl = GetDlgItem(hDlg, IDOK);
+ EnableWindow(hCtrl, FALSE);
+ }
+
+ // check Users and Groups checkbox's
+ hCtrl = GetDlgItem(hDlg, IDC_USERS);
+ SendMessage(hCtrl, BM_SETCHECK, 1, 0);
+ hCtrl = GetDlgItem(hDlg, IDC_GROUPS);
+ SendMessage(hCtrl, BM_SETCHECK, 1, 0);
+ CheckRadioButton(hDlg, IDC_RADIO1, IDC_RADIO3, IDC_RADIO1);
+ return (TRUE);
+
+ case WM_COMMAND:
+ wmId = LOWORD(wParam);
+ wmEvent = HIWORD(wParam);
+
+ switch (wmId) {
+ // [OK] button
+ case IDOK:
+ // Figure out what password option is checked...
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO1);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 1)
+ PasswordOption = 0;
+
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO2);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 1)
+ PasswordOption = 1;
+
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO3);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 1)
+ PasswordOption = 2;
+
+ hCtrl = GetDlgItem(hDlg, IDC_PWCONST);
+ * (WORD *)PasswordConstant = sizeof(PasswordConstant);
+ SendMessage(hCtrl, EM_GETLINE, 0, (LPARAM) PasswordConstant);
+
+ EnableWindow(hDlg, FALSE);
+ MappingFileNameResolve(hDlg);
+ EnableWindow(hDlg, TRUE);
+ EndDialog(hDlg, 0);
+ return (TRUE);
+ break;
+
+ // [CANCEL] button
+ case IDCANCEL:
+ EndDialog(hDlg, 0);
+ return (TRUE);
+ break;
+
+ // [HELP] button
+ case IDHELP:
+ WinHelp(hDlg, HELP_FILE, HELP_CONTEXT, (DWORD) IDC_HELP_CMAP);
+ return (TRUE);
+ break;
+
+ // Checkbox for Users
+ case IDC_USERS:
+ DoUsers = !DoUsers;
+
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO1);
+ EnableWindow(hCtrl, DoUsers);
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO2);
+ EnableWindow(hCtrl, DoUsers);
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO3);
+ EnableWindow(hCtrl, DoUsers);
+ hCtrl = GetDlgItem(hDlg, IDC_PWCONST);
+ EnableWindow(hCtrl, DoUsers);
+
+ return (TRUE);
+ break;
+
+ // Checkbox for Groups
+ case IDC_GROUPS:
+ DoGroups = !DoGroups;
+ return (TRUE);
+ break;
+
+ // Edit field for password constant
+ case IDC_PWCONST:
+
+ if (wmEvent == EN_CHANGE)
+ CheckRadioButton(hDlg, IDC_RADIO1, IDC_RADIO3, IDC_RADIO3);
+
+ break;
+
+ // Edit field for password constant
+ case IDC_MAPPINGFILE:
+ if (wmEvent == EN_CHANGE) {
+ hCtrl = GetDlgItem(hDlg, IDC_MAPPINGFILE);
+
+ if (SendMessage(hCtrl, EM_LINELENGTH, 0, 0)) {
+ hCtrl = GetDlgItem(hDlg, IDOK);
+ EnableWindow(hCtrl, TRUE);
+ } else {
+ hCtrl = GetDlgItem(hDlg, IDOK);
+ EnableWindow(hCtrl, FALSE);
+ }
+
+ }
+ break;
+
+ // [...] button for mapping file
+ case IDC_BTNMAPPINGFILE:
+ // Let the user browse for a file
+ if (!UserFileGet(hDlg, MappingFile)) {
+ hCtrl = GetDlgItem(hDlg, IDC_MAPPINGFILE);
+ SendMessage(hCtrl, WM_SETTEXT, 0, (LPARAM) MappingFile);
+ SetFocus(hCtrl);
+ }
+ return (TRUE);
+ break;
+
+ }
+ break;
+ }
+
+ return (FALSE); // Didn't process the message
+
+ lParam;
+} // MapCreateProc
+
+
+
+/*+-------------------------------------------------------------------------+
+ | MapFileCreate()
+ |
+ +-------------------------------------------------------------------------+*/
+BOOL MapFileCreate(HWND hDlg, LPTSTR FileName, LPTSTR Server) {
+ DLGPROC lpfnDlg;
+
+ DoUsers = TRUE;
+ DoGroups = TRUE;
+ PasswordOption = 0;
+ lstrcpy(MappingFile, FileName);
+ lstrcpy(PasswordConstant, TEXT(""));
+ SourceServ = Server;
+
+ lpfnDlg = MakeProcInstance((DLGPROC)MapCreateProc, hInst);
+ DialogBox(hInst, TEXT("MAPCreate"), hDlg, lpfnDlg) ;
+ FreeProcInstance(lpfnDlg);
+
+ lstrcpy(FileName, MappingFile);
+ return TRUE;
+} // MapFileCreate
+
+
diff --git a/private/nw/convert/nwconv/map.h b/private/nw/convert/nwconv/map.h
new file mode 100644
index 000000000..58007d7d8
--- /dev/null
+++ b/private/nw/convert/nwconv/map.h
@@ -0,0 +1,62 @@
+/*+-------------------------------------------------------------------------+
+ | Copyright 1993-1994 (C) Microsoft Corporation - All rights reserved. |
+ +-------------------------------------------------------------------------+*/
+
+#ifndef _HMAPFILE_
+#define _HMAPFILE_
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+
+typedef struct _MAP_LINE {
+ struct _MAP_LINE *next;
+ struct _MAP_LINE *prev;
+
+ LPTSTR Line;
+} MAP_LINE;
+
+typedef struct _MAP_SECTION {
+ struct _MAP_SECTION *next;
+ struct _MAP_SECTION *prev;
+
+ LPTSTR Name;
+ MAP_LINE *FirstLine;
+ MAP_LINE *LastLine;
+ ULONG LineCount;
+} MAP_SECTION;
+
+typedef struct _MAP_FILE {
+ HANDLE hf;
+ BOOL Modified;
+
+ MAP_SECTION *FirstSection;
+ MAP_SECTION *LastSection;
+ MAP_SECTION *CurrentSection;
+ MAP_LINE *CurrentLine;
+ ULONG SectionCount;
+ TCHAR Name[];
+} MAP_FILE;
+
+#define FILE_CACHE_SIZE 2048
+#define MAX_LINE_LEN 512
+
+#define SECTION_BEGIN_CHAR '['
+#define SECTION_END_CHAR ']'
+#define COMMENT_CHAR ';'
+#define WORD_DELIMITER ','
+
+BOOL MapFileCreate(HWND hDlg, LPTSTR FileName, LPTSTR Server);
+DWORD map_UsersEnum(MAP_FILE *hMap, USER_LIST **lpUsers);
+DWORD map_GroupsEnum(MAP_FILE *hMap, GROUP_LIST **lpGroups);
+
+MAP_FILE *map_Open(TCHAR *FileName);
+void map_Close(MAP_FILE *hMap);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/private/nw/convert/nwconv/mem.c b/private/nw/convert/nwconv/mem.c
new file mode 100644
index 000000000..2d499c0ed
--- /dev/null
+++ b/private/nw/convert/nwconv/mem.c
@@ -0,0 +1,381 @@
+//=============================================================================
+// Microsoft (R) Bloodhound (tm). Copyright (C) 1991-1992.
+//
+// MODULE: mem.c
+//
+// Modification History
+//
+// raypa 01/28/93 Created
+// stevehi 07/21/93 Added memory checking functionality
+// raypa 07/21/93 Changed to use LPTR rather than LocalLock.
+// Tom Laird-McConnell 08/16/93 Changed to fix no size info in functions
+// arth 06/16/94 Added extra debug tracing code
+//=============================================================================
+
+#include "switches.h"
+
+#include <windows.h>
+#include "mem.h"
+#include <memory.h>
+#include <stdio.h>
+#include "debug.h"
+#include "strings.h"
+#include <process.h>
+#include "utils.h"
+
+#ifdef DEBUG_MEM
+BOOL _rtlpheapvalidateoncall = TRUE;
+
+typedef struct _MEM_BUFFER {
+ struct _MEM_BUFFER *Next;
+ struct _MEM_BUFFER *Prev;
+ ULONG Size;
+
+} MEM_BUFFER;
+
+MEM_BUFFER *MemList = NULL;
+ULONG AllocCount = 0;
+#endif
+
+//=============================================================================
+// Define Tags used by the following routines to stick into memory
+// and compare later.
+//=============================================================================
+
+#define AllocTagTakeStart 0x3e4b4154 //... TAK>
+#define AllocTagTakeStop 0x4b41543c //... <TAK
+#define AllocTagReAllocStart 0x3e414552 //... REA>
+#define AllocTagReAllocStop 0x4145523c //... <REA
+#define AllocTagFreeStart 0x3e455246 //... FRE>
+#define AllocTagFreeStop 0x4552463c //... <FRE
+
+// Just load the dynamic strings so we don't have to do it if we run out of
+// memory (otherwise we might not be able to load them then).
+void MemInit() {
+ LPTSTR x;
+
+ x = Lids(IDS_S_7);
+ x = Lids(IDS_S_8);
+
+} // MemInit
+
+
+#ifdef DEBUG_MEM
+
+void DBG_MemEnum(void) {
+ MEM_BUFFER *lpMem;
+ ULONG TotSize = 0;
+ ULONG i = 1;
+ UNALIGNED LPBYTE lpByte;
+
+ lpMem = MemList;
+
+ dprintf(TEXT("\nMem Dump\n# Allocations: %lu\n\n"), AllocCount);
+
+ while (lpMem != NULL) {
+ TotSize += lpMem->Size;
+
+ lpByte = (LPBYTE) lpMem;
+ lpByte -= sizeof(DWORD);
+
+ dprintf(TEXT("%lu %lX) Alloc Size: %lu\n"), i++, lpMem, lpMem->Size);
+
+ if ( *(LPDWORD)(lpByte) != AllocTagTakeStart ) {
+ dprintf(TEXT("AllocTagStart Corrupt\n"));
+ dprintf(TEXT("Total Alloc Mem: %lu\n"), TotSize);
+ DebugBreak;
+ }
+
+ lpByte += sizeof(DWORD) + sizeof(MEM_BUFFER) + lpMem->Size;
+ if ( *(LPDWORD)(lpByte) != AllocTagTakeStop ) {
+ dprintf(TEXT("AllocTagStop Corrupt\n"));
+ dprintf(TEXT("Total Alloc Mem: %lu\n"), TotSize);
+ DebugBreak;
+ }
+
+ lpMem = lpMem->Next;
+ }
+
+ dprintf(TEXT("\nTotal Alloc Mem: %lu\n"), TotSize);
+} // DBG_MemEnum
+
+#endif
+
+
+//=============================================================================
+// FUNCTION: AllocMemory()
+//
+// Modification History
+//
+// raypa 01/28/93 Created.
+// stevehi 07/21/93 Added memory checking functionality
+// raypa 07/21/93 Changed to use LPTR rather than LocalLock.
+// raypa 07/21/93 Return NULL if size is zero.
+//=============================================================================
+
+LPVOID WINAPI AllocMemory(DWORD size) {
+ register LPBYTE lpByte;
+ int ret;
+
+#ifndef DEBUG_MEM
+ do {
+ ret = IDOK;
+ lpByte = LocalAlloc(LPTR, size);
+
+ if ((lpByte == NULL) && (size != 0)) {
+ MessageBeep(MB_ICONHAND);
+ ret = MessageBox(NULL, Lids(IDS_S_7), Lids(IDS_S_8), MB_ICONHAND | MB_SYSTEMMODAL | MB_RETRYCANCEL);
+
+ if (ret == IDCANCEL)
+ exit(1);
+ }
+
+ } while (ret == IDRETRY);
+
+ return (LPVOID) lpByte;
+#else
+ DWORD ActualSize;
+ MEM_BUFFER *lpMem;
+
+ if ( size != 0 ) {
+ do {
+ ret = IDOK;
+ // take into account 2 tags and buffer header
+ lpByte = LocalAlloc(LPTR, size + (2 * sizeof(DWORD)) + sizeof(MEM_BUFFER));
+
+ if (lpByte == NULL) {
+ MessageBeep(MB_ICONHAND);
+ DBG_MemEnum();
+ ret = MessageBox(NULL, TEXT("Out of Memory"), TEXT("NwConv - Error"), MB_ICONHAND | MB_SYSTEMMODAL | MB_RETRYCANCEL);
+
+ if (ret == IDCANCEL)
+ exit(1);
+ }
+
+ } while (ret == IDRETRY);
+
+ AllocCount++;
+ ActualSize = LocalSize(lpByte) - (2 * sizeof(DWORD)) - sizeof(MEM_BUFFER);
+ *((LPDWORD)(lpByte)) = AllocTagTakeStart;
+ lpMem = (MEM_BUFFER *) &lpByte[sizeof(DWORD)];
+
+ lpMem->Next = MemList;
+ lpMem->Prev = NULL;
+ if (MemList != NULL)
+ MemList->Prev = lpMem;
+
+ lpMem->Size = ActualSize;
+ MemList = lpMem;
+
+ *((LPDWORD)(lpByte + ActualSize + sizeof(DWORD) + sizeof(MEM_BUFFER))) = AllocTagTakeStop;
+
+ return (LPVOID) &lpByte[sizeof(DWORD) + sizeof(MEM_BUFFER)];
+ }
+
+ return NULL;
+#endif
+
+} // AllocMemory
+
+
+//=============================================================================
+// FUNCTION: ReallocMemory()
+//
+// Modification History
+//
+// raypa 01/28/93 Created.
+// stevehi 07/21/93 Added memory checking functionality
+// raypa 07/21/93 Changed to use LPTR rather than LocalLock.
+// raypa 10/22/93 If the ptr is NULL then use AllocMemory.
+//=============================================================================
+
+LPVOID WINAPI ReallocMemory(LPVOID ptr, DWORD NewSize) {
+#ifdef DEBUG_MEM
+ DWORD GSize;
+ MEM_BUFFER *lpMem;
+ MEM_BUFFER *OldMem;
+#endif
+
+ //=========================================================================
+ // If the ptr is NULL then use AllocMemory.
+ //=========================================================================
+
+ if ( ptr == NULL ) {
+ return AllocMemory(NewSize);
+ }
+
+#ifndef DEBUG_MEM
+ return LocalReAlloc(ptr, NewSize, LHND);
+#else
+ // we are reallocing... might as well check the tags here...
+
+ (LPBYTE)ptr -= (sizeof(DWORD) + sizeof(MEM_BUFFER));
+
+ GSize = LocalSize (ptr);
+
+ if ( *(LPDWORD)(ptr) != AllocTagTakeStart )
+ DebugBreak;
+
+ // get the size and check the end tag
+
+ if (GSize && ( *(LPDWORD)((LPBYTE)ptr + GSize - sizeof(DWORD))) != AllocTagTakeStop ) {
+ DebugBreak;
+ }
+
+ // just for grins, mark the realloc part.
+
+ *(LPDWORD)(ptr) = AllocTagReAllocStart;
+
+ if ( GSize )
+ *(LPDWORD)((LPBYTE)ptr + GSize - sizeof(DWORD) ) = AllocTagReAllocStop;
+
+ OldMem = (MEM_BUFFER *) ((LPBYTE)ptr + sizeof(DWORD));
+
+ ptr = LocalReAlloc(ptr, NewSize + sizeof(MEM_BUFFER) + (2 * sizeof(DWORD)), LHND);
+
+ if (ptr == NULL ) {
+ dprintf(TEXT("NWConv - Local Realloc failed with %ld.\r\n"), GetLastError() );
+
+ DebugBreak;
+
+ return NULL;
+ }
+
+ lpMem = (MEM_BUFFER *) ((LPBYTE)ptr + sizeof(DWORD));
+
+ if (MemList == OldMem)
+ MemList = lpMem;
+
+ if (lpMem->Prev != NULL)
+ lpMem->Prev->Next = lpMem;
+
+ if (lpMem->Next != NULL)
+ lpMem->Next->Prev = lpMem;
+
+ lpMem->Size = NewSize;
+
+ *((LPDWORD)ptr) = AllocTagTakeStart;
+ *((LPDWORD)((LPBYTE)ptr + NewSize + sizeof(MEM_BUFFER) + sizeof(DWORD))) = AllocTagTakeStop;
+
+ return (LPVOID)((LPBYTE)ptr + sizeof(DWORD) + sizeof(MEM_BUFFER));
+#endif
+} // ReAllocMemory
+
+
+//=============================================================================
+// FUNCTION: FreeMemory()
+//
+// Modification History
+//
+// raypa 01/28/93 Created.
+// stevehi 07/21/93 Added memory checking functionality
+// raypa 07/21/93 Changed to use LPTR rather than LocalLock.
+// raypa 07/21/93 Fixed GP-fault on NULL ptr.
+// raypa 11/21/93 Allow freeing of NULL pointer.
+//=============================================================================
+
+VOID WINAPI FreeMemory(LPBYTE ptr) {
+ //=========================================================================
+ // If the pointer is NULL, exit.
+ //=========================================================================
+
+ if ( ptr != NULL ) {
+#ifdef DEBUG_MEM
+ register DWORD Size;
+ register LPDWORD DwordPtr;
+ MEM_BUFFER *lpMem;
+
+ ptr -= (sizeof(DWORD) + sizeof(MEM_BUFFER));
+ lpMem = (MEM_BUFFER *) ((LPBYTE)ptr + sizeof(DWORD));
+
+ Size = LocalSize(ptr);
+
+ //... Check start tag
+ DwordPtr = (LPDWORD) &ptr[0];
+
+ if ( *DwordPtr != AllocTagTakeStart ) {
+ dprintf(TEXT("NWConv - FreeMemory: Invalid start signature: ptr = %X\r\n"), ptr);
+
+ DebugBreak();
+ }
+ else {
+ *DwordPtr = AllocTagFreeStart;
+ }
+
+ //... get the size and check the end tag
+
+ DwordPtr = (LPDWORD) &ptr[Size - sizeof(DWORD)];
+
+ if ( *DwordPtr != AllocTagTakeStop ) {
+ dprintf(TEXT("NWConv - FreeMemory: Invalid end signature: ptr = %X\r\n"), ptr);
+
+ DebugBreak();
+ }
+ else {
+ *DwordPtr = AllocTagFreeStop;
+ }
+
+ AllocCount--;
+ if (MemList == lpMem)
+ MemList = lpMem->Next;
+
+ if (lpMem->Prev != NULL)
+ lpMem->Prev->Next = lpMem->Next;
+
+ if (lpMem->Next != NULL)
+ lpMem->Next->Prev = lpMem->Prev;
+#endif
+
+ LocalFree(ptr);
+ }
+} // FreeMemory
+
+
+//=============================================================================
+// FUNCTION: MemorySize()
+//
+// Modification History
+//
+// Tom Laird-McConnell 08/02/93 Created.
+// Tom Laird-McConnell 08/02/93 Changed to use local var for size...
+//=============================================================================
+
+DWORD WINAPI MemorySize(LPBYTE ptr) {
+#ifdef DEBUG_MEM
+ register DWORD Size;
+
+ if ( ptr != NULL ) {
+ register LPDWORD DwordPtr;
+
+ ptr -= (sizeof(DWORD) + sizeof(MEM_BUFFER));
+
+ Size = LocalSize(ptr);
+
+ DwordPtr = (LPDWORD) &ptr[0];
+
+ // Check start tag
+
+ if ( *DwordPtr != AllocTagTakeStart ) {
+ dprintf(TEXT("NWConv - MemorySize: Invalid start signature!\r\n"));
+
+ DebugBreak;
+ }
+
+ // get the size and check the end tag
+
+ DwordPtr = (LPDWORD) &ptr[Size - sizeof(DWORD)];
+
+ if ( *DwordPtr != AllocTagTakeStop ) {
+ dprintf(TEXT("NWConv - MemorySize: Invalid end signature!\r\n"));
+
+ DebugBreak;
+ }
+
+ return (Size - (2 * sizeof(DWORD)) - sizeof(MEM_BUFFER));
+ }
+#endif
+
+ return LocalSize(ptr);
+} // MemorySize
+
+
diff --git a/private/nw/convert/nwconv/mem.h b/private/nw/convert/nwconv/mem.h
new file mode 100644
index 000000000..f3c5cf329
--- /dev/null
+++ b/private/nw/convert/nwconv/mem.h
@@ -0,0 +1,40 @@
+//=============================================================================
+// Microsoft (R) Bloodhound (tm). Copyright (C) 1991-1993.
+//
+// MODULE: objmgr.h
+//
+// Modification History
+//
+// raypa 03/17/93 Created.
+//=============================================================================
+
+#if !defined(_OBJMGR_)
+
+
+#define _OBJMGR_
+#pragma pack(1)
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+//=============================================================================
+// Memory functions.
+//=============================================================================
+
+extern LPVOID WINAPI AllocMemory(DWORD size);
+
+extern LPVOID WINAPI ReallocMemory(LPVOID ptr, DWORD NewSize);
+
+extern VOID WINAPI FreeMemory(LPVOID ptr);
+
+extern DWORD WINAPI MemorySize(LPVOID ptr);
+
+void MemInit();
+
+#ifdef __cplusplus
+}
+#endif
+
+#pragma pack()
+#endif
diff --git a/private/nw/convert/nwconv/moveit.ico b/private/nw/convert/nwconv/moveit.ico
new file mode 100644
index 000000000..44d2cb60c
--- /dev/null
+++ b/private/nw/convert/nwconv/moveit.ico
Binary files differ
diff --git a/private/nw/convert/nwconv/netutil.c b/private/nw/convert/nwconv/netutil.c
new file mode 100644
index 000000000..7a99ba6b9
--- /dev/null
+++ b/private/nw/convert/nwconv/netutil.c
@@ -0,0 +1,437 @@
+/*
+ +-------------------------------------------------------------------------+
+ | Network Utility Functions |
+ +-------------------------------------------------------------------------+
+ | (c) Copyright 1993-1994 |
+ | Microsoft Corp. |
+ | All rights reserved |
+ | |
+ | Program : [NetUtil.c] |
+ | Programmer : Arthur Hanson |
+ | Original Program Date : [Feb 16, 1993] |
+ | Last Update : [Jun 16, 1994] |
+ | |
+ | Version: 1.00 |
+ | |
+ | Description: |
+ | |
+ | History: |
+ | arth Jun 16, 1994 1.00 Original Version. |
+ | |
+ +-------------------------------------------------------------------------+
+*/
+
+#include "globals.h"
+#include "netutil.h"
+
+static LPTSTR ServName;
+static TCHAR szPassword[PWLEN+1];
+static TCHAR szUserName[MAX_USER_NAME_LEN + 1];
+
+LRESULT CALLBACK PasswordDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+BOOL BadPassword;
+
+/*+-------------------------------------------------------------------------+
+ | FixPathSlash()
+ |
+ +-------------------------------------------------------------------------+*/
+void FixPathSlash(LPTSTR NewPath, LPTSTR Path) {
+ UINT PathLen;
+ lstrcpy(NewPath, Path);
+
+ PathLen = lstrlen(Path);
+ // If ending character is not a slash then put one on
+ if (PathLen && (Path[PathLen - 1] != '\\'))
+ lstrcat(NewPath, TEXT("\\"));
+
+} // FixPathSlash
+
+
+/*+-------------------------------------------------------------------------+
+ | ShareNameParse()
+ |
+ +-------------------------------------------------------------------------+*/
+LPTSTR ShareNameParse(LPTSTR ShareName) {
+ ULONG i;
+
+ i = lstrlen(ShareName);
+ if (!i)
+ return ShareName;
+
+ // Scan backwards for first slash
+ i--;
+ while (i && ShareName[i] != TEXT('\\'))
+ i--;
+
+ // if found slash then increment past it
+ if (i)
+ i++;
+
+ return &ShareName[i];
+
+} // ShareNameParse
+
+
+static LPTSTR LocName = NULL;
+/*+-------------------------------------------------------------------------+
+ | GetLocalName()
+ |
+ +-------------------------------------------------------------------------+*/
+void GetLocalName(LPTSTR *lpLocalName) {
+ int size;
+
+ if (LocName != NULL) {
+ *lpLocalName = LocName;
+ } else {
+ LocName = AllocMemory((MAX_COMPUTERNAME_LENGTH + 1) * sizeof(TCHAR));
+ size = MAX_COMPUTERNAME_LENGTH + 1;
+
+ if (LocName) {
+ GetComputerName(LocName, &size);
+ *lpLocalName = LocName;
+ } else
+ *lpLocalName = NULL;
+ }
+
+} // GetLocalName
+
+
+/*+-------------------------------------------------------------------------+
+ | SetProvider()
+ |
+ +-------------------------------------------------------------------------+*/
+BOOL SetProvider(LPTSTR Provider, NETRESOURCE *ResourceBuf) {
+ ResourceBuf->dwScope = RESOURCE_GLOBALNET;
+ ResourceBuf->dwType = RESOURCETYPE_DISK;
+ ResourceBuf->dwDisplayType = RESOURCEDISPLAYTYPE_GENERIC;
+
+ // Don't take the frigging _RESERVED flag out - it isn't documented except in the include
+ // file and it crashes without it!
+ ResourceBuf->dwUsage = RESOURCEUSAGE_CONTAINER | RESOURCEUSAGE_RESERVED;
+
+ ResourceBuf->lpLocalName = NULL;
+ ResourceBuf->lpRemoteName = Provider;
+ ResourceBuf->lpComment = NULL;
+ ResourceBuf->lpProvider = Provider;
+ return TRUE;
+
+} // SetProvider
+
+
+/*+-------------------------------------------------------------------------+
+ | AllocEnumBuffer()
+ |
+ +-------------------------------------------------------------------------+*/
+ENUM_REC *AllocEnumBuffer() {
+ ENUM_REC *Buf;
+
+ Buf = (ENUM_REC *) AllocMemory(sizeof(ENUM_REC));
+
+ if (Buf) {
+ // Init the record
+ Buf->next = NULL;
+ Buf->cEntries = 0;
+ Buf->cbBuffer = 0;
+ Buf->lpnr = NULL;
+ }
+
+ return Buf;
+
+} // AllocEnumBuffer
+
+
+
+/*+-------------------------------------------------------------------------+
+ | EnumBufferBuild()
+ |
+ | Uses WNetEnum to enumerate the resource. WNetEnum is really brain-
+ | dead so we need to create a temporary holding array and then build
+ | up a finalized complete buffer in the end. A linked list of inter-
+ | mediate buffer records is created first.
+ |
+ +-------------------------------------------------------------------------+*/
+DWORD FAR PASCAL EnumBufferBuild(ENUM_REC **BufHead, int *NumBufs, NETRESOURCE ResourceBuf) {
+ DWORD status = ERROR_NO_NETWORK;
+ ENUM_REC *CurrBuf;
+ DWORD dwResultEnum;
+ HANDLE hEnum = NULL;
+ DWORD cbBuffer = 16384; // 16K default buffer size.
+ DWORD cEntries = 0xFFFFFFFF; // enumerate all possible entries
+ ENUM_REC **lppEnumRec;
+ LPNETRESOURCE lpnrLocal;
+
+ status = WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_DISK, 0, &ResourceBuf, &hEnum);
+
+ if (status == NO_ERROR) {
+
+ *BufHead = NULL;
+ lppEnumRec = BufHead;
+
+ do {
+
+ cbBuffer = 16384; // 16K default buffer size
+ cEntries = 0xFFFFFFFF; // enumerate all possible entries
+
+ // Allocate memory for NETRESOURCE structures.
+ lpnrLocal = (LPNETRESOURCE) AllocMemory(cbBuffer);
+
+ if (lpnrLocal == NULL) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+ }
+
+ dwResultEnum = WNetEnumResource(hEnum, &cEntries, (LPVOID) lpnrLocal, &cbBuffer);
+
+ if (dwResultEnum == NO_ERROR) {
+ // Create a new Enum rec and link it to the chain
+ *lppEnumRec = AllocEnumBuffer();
+
+ if (*lppEnumRec == NULL) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+ }
+
+ CurrBuf = *lppEnumRec;
+
+ // Init for next loop through buffer
+ lppEnumRec = &CurrBuf->next;
+
+ // Put enumeration buffer in our Enum rec.
+ CurrBuf->lpnr = lpnrLocal;
+ CurrBuf->cEntries = cEntries;
+ CurrBuf->cbBuffer = cbBuffer;
+ (*NumBufs)++;
+
+ } else { // Since this is not assigned in a rec we need to free it here.
+ FreeMemory((HGLOBAL) lpnrLocal);
+
+ if (dwResultEnum != ERROR_NO_MORE_ITEMS) {
+ status = dwResultEnum;
+ break;
+ }
+ }
+
+ } while (dwResultEnum != ERROR_NO_MORE_ITEMS);
+
+ status = WNetCloseEnum(hEnum);
+
+ }
+
+ return status;
+
+} // EnumBufferBuild
+
+
+/*+-------------------------------------------------------------------------+
+ | UseAddPswd()
+ |
+ | Attempts to make connections to \\szServer\admin$, asking for
+ | passwords if necessary.
+ |
+ | Returns TRUE if use was added,
+ | FALSE otherwise
+ |
+ +-------------------------------------------------------------------------+*/
+BOOL UseAddPswd(HWND hwnd, LPTSTR UserName, LPTSTR lpszServer, LPTSTR lpszShare, LPTSTR Provider) {
+ WORD nState;
+ WORD fCancel;
+ DLGPROC lpProc;
+ NETRESOURCE nr;
+ NET_API_STATUS retcode;
+ LPTSTR lpPassword;
+ static TCHAR szTmp[MAX_UNC_PATH+1];
+
+ ServName = lpszServer;
+
+ nr.dwScope = 0;
+ nr.dwType = RESOURCETYPE_DISK;
+ nr.dwDisplayType = 0;
+ nr.dwUsage = 0;
+ nr.lpProvider = NULL;
+
+ nState = 1; // try default password
+ lpPassword = NULL;
+ BadPassword = FALSE;
+ lstrcpy(szUserName, UserName);
+
+ for(;;) {
+ // Concatenate server and share
+ wsprintf(szTmp, TEXT("%s\\%s"), lpszServer, lpszShare);
+
+ // Fill in data structure
+ nr.lpLocalName = NULL;
+ nr.lpRemoteName = szTmp;
+ nr.lpProvider = Provider;
+
+ // Try to make the connection
+ if (lstrlen(szUserName))
+ retcode = WNetAddConnection2(&nr, lpPassword, szUserName, 0);
+ else
+ retcode = WNetAddConnection2(&nr, lpPassword, NULL, 0);
+
+ switch(retcode) {
+ case NERR_Success:
+ lstrcpy(UserName, szUserName);
+ return TRUE;
+
+ case ERROR_INVALID_PASSWORD:
+ BadPassword = TRUE;
+ break;
+
+ case ERROR_ACCESS_DENIED:
+ case ERROR_NETWORK_ACCESS_DENIED:
+ case ERROR_SESSION_CREDENTIAL_CONFLICT:
+ case ERROR_NO_SUCH_USER:
+ case ERROR_NO_MORE_ITEMS:
+ case ERROR_LOGON_FAILURE:
+ BadPassword = FALSE;
+ break;
+
+ case ERROR_BAD_NETPATH:
+ case ERROR_BAD_NET_NAME:
+
+ default:
+ return FALSE;
+ }
+
+ // Get new password from user
+ lpProc = MakeProcInstance(PasswordDlgProc, hInst);
+ fCancel = !DialogBoxParam(hInst, TEXT("PasswordEnter"), hwnd, lpProc, 0);
+
+ // Gamble call only once
+ FreeProcInstance(lpProc);
+
+ // Save...
+ if(!fCancel) {
+ if(nState) {
+ nState = 2; // try specified password
+ lpPassword = szPassword;
+ } else {
+ nState = 1; // try default password
+ lpPassword = NULL;
+ }
+ } else {
+ SetLastError(ERROR_SUCCESS); // just aborting...
+ return FALSE;
+ }
+ }
+
+} // UseAddPswd
+
+
+/*+-------------------------------------------------------------------------+
+ | PasswordDlgProc()
+ |
+ | Gets a password from the user and copies it into the string pointed
+ | to by lParam. This string must have room for at least (PWLEN + 1)
+ | characters. Returns TRUE if OK is pressed, or FALSE if Cancel
+ |
+ +-------------------------------------------------------------------------+*/
+LRESULT CALLBACK PasswordDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ CursorNormal();
+
+ // Center the dialog over the application window
+ CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
+
+ SetDlgItemText(hDlg, IDC_SERVNAME, ServName);
+ SendDlgItemMessage(hDlg, IDC_PASSWORD, EM_LIMITTEXT, PWLEN, 0L);
+ SendDlgItemMessage(hDlg, IDC_USERNAME, EM_LIMITTEXT, MAX_USER_NAME_LEN, 0L);
+ PostMessage(hDlg, WM_COMMAND, ID_INIT, 0L);
+ break;
+
+ case WM_COMMAND:
+ switch(wParam) {
+ case IDOK:
+ CursorHourGlass();
+ GetDlgItemText(hDlg, IDC_PASSWORD, szPassword, PWLEN+1);
+ GetDlgItemText(hDlg, IDC_USERNAME, szUserName, MAX_USER_NAME_LEN+1);
+
+ EndDialog(hDlg, TRUE);
+ break;
+
+ case IDCANCEL:
+ CursorHourGlass();
+ EndDialog(hDlg, FALSE);
+ break;
+
+ case ID_INIT:
+ SendDlgItemMessage(hDlg, IDC_USERNAME, WM_SETTEXT, 0, (LPARAM) szUserName);
+
+ if (BadPassword)
+ SetFocus(GetDlgItem(hDlg, IDC_PASSWORD));
+ else {
+ SetFocus(GetDlgItem(hDlg, IDC_USERNAME));
+ SendDlgItemMessage(hDlg, IDC_USERNAME, EM_SETSEL, 0, (LPARAM) MAKELPARAM(0, -1) );
+ }
+
+ break;
+
+ default:
+ return FALSE;
+ }
+ break;
+
+ default:
+ return FALSE;
+ }
+ return TRUE;
+
+} // PasswordDlgProc
+
+
+/*+-------------------------------------------------------------------------+
+ | NicePath()
+ |
+ +-------------------------------------------------------------------------+*/
+LPTSTR NicePath(int Len, LPTSTR Path) {
+ static TCHAR NewPath[MAX_PATH + 80];
+ int eptr, fptr;
+
+ // If the path fits then just return it
+ if (lstrlen(Path) <= Len) {
+ lstrcpy(NewPath, Path);
+ return NewPath;
+ }
+
+ // The path doesn't fit, so need to reduce it down in size - to do this first try
+ // to get the last part of the path looking for slash that starts it.
+ eptr = fptr = 0;
+ while (Path[eptr] != TEXT('\0'))
+ eptr++;
+
+ // back up before ending NULL also before any ending slash
+ eptr--;
+ while ((Path[eptr] == TEXT('\\')) && eptr > 0)
+ eptr--;
+
+ // now try to find beginning slash
+ while ((Path[eptr] != TEXT('\\')) && eptr > 0)
+ eptr--;
+
+ // if at beginning of string then is just one name so copy all of it we can
+ if (eptr == 0) {
+ lstrcpyn(NewPath, Path, Len);
+ return NewPath;
+ }
+
+ // check if the name after the last slash can all fit - also take into account
+ // the "..." we are going to tack into the name
+ fptr = lstrlen(Path) - eptr;
+ fptr += 4;
+ if (fptr >= Len) {
+ lstrcpyn(NewPath, &Path[eptr], Len);
+ return NewPath;
+ }
+
+ // We need to create a path shortening to the desired length by removing the mid
+ // part of the path and replacing it with "..."
+ fptr = Len - fptr;
+ lstrcpyn(NewPath, Path, fptr);
+ lstrcat(NewPath, TEXT("..."));
+ lstrcat(NewPath, &Path[eptr]);
+ return NewPath;
+
+} // NicePath
diff --git a/private/nw/convert/nwconv/netutil.h b/private/nw/convert/nwconv/netutil.h
new file mode 100644
index 000000000..f2402d759
--- /dev/null
+++ b/private/nw/convert/nwconv/netutil.h
@@ -0,0 +1,87 @@
+/*+-------------------------------------------------------------------------+
+ | Copyright 1993-1994 (C) Microsoft Corporation - All rights reserved. |
+ +-------------------------------------------------------------------------+*/
+
+#ifndef _HNETUTIL_
+#define _HNETUTIL_
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+// made to match usri3 info structure for easy save/retrieval
+typedef struct _NT_USER_INFO {
+ LPWSTR name;
+ LPWSTR password;
+ DWORD password_age;
+ DWORD priv;
+ LPWSTR home_dir;
+ LPWSTR comment;
+ DWORD flags;
+ LPWSTR script_path;
+ DWORD auth_flags;
+ LPWSTR full_name;
+ LPWSTR usr_comment;
+ LPWSTR parms;
+ LPWSTR workstations;
+ DWORD last_logon;
+ DWORD last_logoff;
+ DWORD acct_expires;
+ DWORD max_storage;
+ DWORD units_per_week;
+ PBYTE logon_hours;
+ DWORD bad_pw_count;
+ DWORD num_logons;
+ LPWSTR logon_server;
+ DWORD country_code;
+ DWORD code_page;
+ DWORD user_id;
+ DWORD primary_group_id;
+ LPWSTR profile;
+ LPWSTR home_dir_drive;
+ DWORD password_expired;
+} NT_USER_INFO, *PNT_USER_INFO, *LPNT_USER_INFO;
+
+
+typedef struct _FPNW_INFO {
+ WORD MaxConnections;
+ WORD PasswordInterval;
+ BYTE GraceLoginAllowed;
+ BYTE GraceLoginRemaining;
+ LPWSTR LoginFrom;
+ LPWSTR HomeDir;
+} FPNW_INFO, *PFPNW_INFO, *LPFPNW_INFO;
+
+
+
+// made to match USER_MODALS_INFO_0 info structure for easy save/retrieval
+typedef struct _NT_DEFAULTS {
+ DWORD min_passwd_len;
+ DWORD max_passwd_age;
+ DWORD min_passwd_age;
+ DWORD force_logoff;
+ DWORD password_hist_len;
+} NT_DEFAULTS, *PNT_DEFAULTS, *LPNT_DEFAULTS;
+
+
+typedef struct _EnumRec {
+ struct _EnumRec *next;
+ DWORD cEntries;
+ DWORD cbBuffer;
+ LPNETRESOURCE lpnr;
+} ENUM_REC;
+
+void FixPathSlash(LPTSTR NewPath, LPTSTR Path);
+LPTSTR ShareNameParse(LPTSTR ShareName);
+void GetLocalName(LPTSTR *lpLocalName);
+BOOL SetProvider(LPTSTR Provider, NETRESOURCE *ResourceBuf);
+ENUM_REC *AllocEnumBuffer();
+DWORD FAR PASCAL EnumBufferBuild(ENUM_REC **BufHead, int *NumBufs, NETRESOURCE ResourceBuf);
+BOOL UseAddPswd(HWND hwnd, LPTSTR UserName, LPTSTR lpszServer, LPTSTR lpszShare, LPTSTR Provider);
+LPTSTR NicePath(int Len, LPTSTR Path);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/nw/convert/nwconv/ntnetapi.c b/private/nw/convert/nwconv/ntnetapi.c
new file mode 100644
index 000000000..a363bf99d
--- /dev/null
+++ b/private/nw/convert/nwconv/ntnetapi.c
@@ -0,0 +1,3644 @@
+/*++
+
+Copyright (c) 1993-1995 Microsoft Corporation
+
+Module Name:
+
+ NTNetaAPI.c
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) 16-Jun-1994
+
+Revision History:
+
+--*/
+
+
+#include "globals.h"
+
+#include <ntlsa.h>
+#include <ntsam.h>
+#include <ntddnwfs.h>
+#include <align.h>
+
+#include <math.h>
+#include "convapi.h"
+#include "ntnetapi.h"
+#include "nwnetapi.h"
+#include "loghours.h"
+#include "usrprop.h"
+#include "crypt.h"
+// #include <nwstruct.h>
+#include <nwconv.h>
+#include "fpnwapi.h"
+
+#ifdef DEBUG
+int ErrorBoxRetry(LPTSTR szFormat, ...);
+#endif
+
+void ErrorIt(LPTSTR szFormat, ...);
+NTSTATUS ACEAdd( PSECURITY_DESCRIPTOR pSD, PSID pSid, ACCESS_MASK AccessMask, ULONG AceFlags, PSECURITY_DESCRIPTOR *ppNewSD );
+
+static LPTSTR LocalName = NULL;
+
+// +3 for leading slashes and ending NULL
+static TCHAR CachedServer[MAX_SERVER_NAME_LEN+3];
+static BOOL LocalMachine = FALSE;
+
+// keep this around so we don't have to keep re-do query
+static LPSERVER_INFO_101 ServInfo = NULL;
+// #define TYPE_DOMAIN SV_TYPE_DOMAIN_CTRL | SV_TYPE_DOMAIN_BAKCTRL | SV_TYPE_DOMAIN_MEMBER
+#define TYPE_DOMAIN SV_TYPE_DOMAIN_CTRL | SV_TYPE_DOMAIN_BAKCTRL
+
+static SAM_HANDLE SAMHandle = (SAM_HANDLE) 0;
+static SAM_HANDLE DomainHandle = (SAM_HANDLE) 0;
+static PSID DomainID;
+static WCHAR UserParms[1024];
+
+#define NCP_LSA_SECRET_KEY L"G$MNSEncryptionKey"
+#define NCP_LSA_SECRET_LENGTH USER_SESSION_KEY_LENGTH
+
+static char Secret[USER_SESSION_KEY_LENGTH];
+static HANDLE FPNWLib = NULL;
+static DWORD (FAR * NWVolumeAdd) (LPWSTR, DWORD, PNWVOLUMEINFO) = NULL;
+
+//
+// This bit is set when the server is running on a NTAS machine or
+// the object is from a trusted domain. NWConv is always on NTAS
+//
+
+#define BINDLIB_REMOTE_DOMAIN_BIAS 0x10000000
+
+//
+// misc macros that are useful
+//
+#define SWAPWORD(w) ((WORD)((w & 0xFF) << 8)|(WORD)(w >> 8))
+#define SWAPLONG(l) MAKELONG(SWAPWORD(HIWORD(l)),SWAPWORD(LOWORD(l)))
+
+
+typedef struct _NT_CONN_BUFFER {
+ struct _NT_CONN_BUFFER *next;
+ struct _NT_CONN_BUFFER *prev;
+
+ LPTSTR Name;
+} NT_CONN_BUFFER;
+
+static NT_CONN_BUFFER *NTConnListStart = NULL;
+static NT_CONN_BUFFER *NTConnListEnd = NULL;
+
+HANDLE FpnwHandle = NULL;
+static FARPROC pFpnwVolumeEnum = NULL;
+static FARPROC pFpnwApiBufferFree = NULL;
+
+
+/////////////////////////////////////////////////////////////////////////
+LPSTR
+FPNWSecretGet(
+ LPTSTR ServerName
+ )
+
+/*++
+
+Routine Description:
+
+ Checks the given machine for the FPNW secret.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ SECURITY_QUALITY_OF_SERVICE QualityOfService;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ LSA_HANDLE PolicyHandle;
+ LSA_HANDLE SecretHandle;
+ NTSTATUS Status;
+ UNICODE_STRING UnicodeSecretName;
+ PUNICODE_STRING punicodeCurrentValue;
+ PUSER_INFO_2 pUserInfo = NULL;
+
+ static TCHAR LocServer[MAX_SERVER_NAME_LEN+3];
+ UNICODE_STRING UnicodeServerName;
+ BOOL ret = TRUE;
+
+ memset(Secret, 0, USER_SESSION_KEY_LENGTH);
+ wsprintf(LocServer, TEXT("\\\\%s"), ServerName);
+
+ // Verify & init secret name.
+ RtlInitUnicodeString( &UnicodeSecretName, NCP_LSA_SECRET_KEY );
+ RtlInitUnicodeString( &UnicodeServerName, LocServer);
+
+ // Prepare to open the policy object.
+ QualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
+ QualityOfService.ImpersonationLevel = SecurityImpersonation;
+ QualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ QualityOfService.EffectiveOnly = FALSE;
+
+ InitializeObjectAttributes( &ObjectAttributes, NULL, 0L, NULL, NULL );
+ ObjectAttributes.SecurityQualityOfService = &QualityOfService;
+
+ // Open a handle to the target machine's LSA policy.
+ Status = LsaOpenPolicy( &UnicodeServerName, &ObjectAttributes, 0, &PolicyHandle );
+
+ if( !NT_SUCCESS( Status ) ) {
+ ret = FALSE;
+ goto FatalExit0;
+ }
+
+ // Open the secret object.
+ Status = LsaOpenSecret( PolicyHandle, &UnicodeSecretName, SECRET_QUERY_VALUE, &SecretHandle );
+
+ if( !NT_SUCCESS( Status ) ) {
+ ret = FALSE;
+ goto FatalExit1;
+ }
+
+ // Query the secret.
+ Status = LsaQuerySecret( SecretHandle, &punicodeCurrentValue, NULL, NULL, NULL );
+
+ if( !NT_SUCCESS( Status ) ) {
+ ret = FALSE;
+ goto FatalExit2;
+ }
+
+ if (punicodeCurrentValue != NULL) {
+ memcpy(Secret, punicodeCurrentValue->Buffer, USER_SESSION_KEY_LENGTH);
+ LsaFreeMemory( (PVOID)punicodeCurrentValue );
+ }
+
+FatalExit2:
+ LsaClose( SecretHandle );
+
+FatalExit1:
+ LsaClose( PolicyHandle );
+
+FatalExit0:
+
+ if (ret)
+ return Secret;
+ else
+ return NULL;
+
+} // FPNWSecretGet
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+SetGraceLoginAllowed(
+ USHORT ushGraceLoginAllowed
+ )
+
+/*++
+
+Routine Description:
+
+ Store Grace Login Allowed in UserParms.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS err = NERR_Success;
+ USHORT ushTemp = ushGraceLoginAllowed;
+ UNICODE_STRING uniGraceLoginAllowed;
+ LPWSTR lpNewUserParms = NULL;
+ BOOL fUpdate;
+
+ uniGraceLoginAllowed.Buffer = &ushTemp;
+ uniGraceLoginAllowed.Length = 2;
+ uniGraceLoginAllowed.MaximumLength = 2;
+
+ err = SetUserProperty (UserParms, GRACELOGINALLOWED, uniGraceLoginAllowed, USER_PROPERTY_TYPE_ITEM, &lpNewUserParms, &fUpdate);
+ if ((err == NERR_Success) && (lpNewUserParms != NULL)) {
+ if (fUpdate)
+ lstrcpyW(UserParms, lpNewUserParms);
+
+ LocalFree(lpNewUserParms);
+ }
+
+ return err;
+} // SetGraceLoginAllowed
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+SetGraceLoginRemainingTimes(
+ USHORT ushGraceLoginRemainingTimes
+ )
+
+/*++
+
+Routine Description:
+
+ Store Grace Login Remaining Times in UserParms. if ushGraceLogin is 0,
+ "GraceLogin" field will be deleted from UserParms.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS err = NERR_Success;
+ USHORT ushTemp = ushGraceLoginRemainingTimes;
+ UNICODE_STRING uniGraceLoginRemainingTimes;
+ LPWSTR lpNewUserParms = NULL;
+ BOOL fUpdate;
+
+ uniGraceLoginRemainingTimes.Buffer = &ushTemp;
+ uniGraceLoginRemainingTimes.Length = 2;
+ uniGraceLoginRemainingTimes.MaximumLength = 2;
+
+ err = SetUserProperty (UserParms, GRACELOGINREMAINING, uniGraceLoginRemainingTimes, USER_PROPERTY_TYPE_ITEM, &lpNewUserParms, &fUpdate);
+ if ((err == NERR_Success) && (lpNewUserParms != NULL)) {
+ if (fUpdate)
+ lstrcpyW(UserParms, lpNewUserParms);
+
+ LocalFree(lpNewUserParms);
+ }
+
+ return err;
+} // SetGraceLoginRemainingTimes
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+SetMaxConnections(
+ USHORT ushMaxConnections
+ )
+
+/*++
+
+Routine Description:
+
+ Store Maximum Concurret Connections in UserParms. If ushMaxConnections
+ is 0xffff or 0, "MaxConnections" field will be deleted from UserParms,
+ otherwise the value is stored.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS err = NERR_Success;
+ USHORT ushTemp = ushMaxConnections;
+ UNICODE_STRING uniMaxConnections;
+ LPWSTR lpNewUserParms = NULL;
+ BOOL fUpdate;
+
+ uniMaxConnections.Buffer = &ushMaxConnections;
+ uniMaxConnections.Length = 2;
+ uniMaxConnections.MaximumLength = 2;
+
+ err = SetUserProperty (UserParms, MAXCONNECTIONS, uniMaxConnections, USER_PROPERTY_TYPE_ITEM, &lpNewUserParms, &fUpdate);
+ if ((err == NERR_Success) && (lpNewUserParms != NULL)) {
+ if (fUpdate)
+ lstrcpyW(UserParms, lpNewUserParms);
+
+ LocalFree(lpNewUserParms);
+ }
+
+ return err;
+} // SetMaxConnections
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+SetNWPasswordAge(
+ BOOL fExpired
+ )
+
+/*++
+
+Routine Description:
+
+ If fExpired is TRUE, set the NWPasswordSet field to be all fs.
+ otherwise set it to be the current time.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS err = NERR_Success;
+ LARGE_INTEGER currentTime;
+ LPWSTR lpNewUserParms = NULL;
+ UNICODE_STRING uniPasswordAge;
+ BOOL fUpdate;
+
+ if (fExpired) {
+ currentTime.HighPart = 0xffffffff;
+ currentTime.LowPart = 0xffffffff;
+ } else
+ NtQuerySystemTime (&currentTime);
+
+ uniPasswordAge.Buffer = (PWCHAR) &currentTime;
+ uniPasswordAge.Length = sizeof (LARGE_INTEGER);
+ uniPasswordAge.MaximumLength = sizeof (LARGE_INTEGER);
+
+ err = SetUserProperty (UserParms, NWTIMEPASSWORDSET, uniPasswordAge, USER_PROPERTY_TYPE_ITEM, &lpNewUserParms, &fUpdate);
+ if ((err == NERR_Success) &&(lpNewUserParms != NULL)) {
+ if (fUpdate)
+ lstrcpyW(UserParms, lpNewUserParms);
+
+ LocalFree(lpNewUserParms);
+ }
+
+ return err;
+} // SetNWPasswordAge
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+SetNWPassword(
+ DWORD dwUserId,
+ const TCHAR *pchNWPassword,
+ BOOL ForcePasswordChange
+ )
+
+/*++
+
+Routine Description:
+
+ Set "NWPassword" field is fIsNetWareUser is TRUE, Otherwise, delete
+ the field from UserParms.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS err;
+ TCHAR pchEncryptedNWPassword[NWENCRYPTEDPASSWORDLENGTH + 1];
+ LPWSTR lpNewUserParms = NULL;
+ BOOL fUpdate;
+ UNICODE_STRING uniPassword;
+
+#ifdef DEBUG
+ dprintf(TEXT("Set NetWare password: [%s]\r\n"), pchNWPassword);
+#endif
+
+ do {
+ // Now munge the UserID to the format required by ReturnNetwareForm...
+ dwUserId |= BINDLIB_REMOTE_DOMAIN_BIAS;
+ err = ReturnNetwareForm( Secret, dwUserId, pchNWPassword, (UCHAR *) pchEncryptedNWPassword );
+
+ if ( err != NERR_Success )
+ break;
+
+ uniPassword.Buffer = pchEncryptedNWPassword;
+ uniPassword.Length = NWENCRYPTEDPASSWORDLENGTH * sizeof (WCHAR);
+ uniPassword.MaximumLength = NWENCRYPTEDPASSWORDLENGTH * sizeof (WCHAR);
+
+ err = SetUserProperty (UserParms, NWPASSWORD, uniPassword, USER_PROPERTY_TYPE_ITEM, &lpNewUserParms, &fUpdate);
+ if ((err == NERR_Success) && (lpNewUserParms != NULL)) {
+ if (fUpdate)
+ lstrcpyW(UserParms, lpNewUserParms);
+
+ LocalFree(lpNewUserParms);
+
+ if ((err = SetNWPasswordAge (ForcePasswordChange)) != NERR_Success )
+ break;
+ }
+ } while (FALSE);
+
+ return err;
+} // SetNWPassword
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+SetNWWorkstations(
+ const TCHAR * pchNWWorkstations
+ )
+
+/*++
+
+Routine Description:
+
+ Store NetWare allowed workstation addresses to UserParms. If
+ pchNWWorkstations is NULL, this function will delete "NWLgonFrom"
+ field from UserParms.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS err = NERR_Success;
+ UNICODE_STRING uniNWWorkstations;
+ CHAR * pchTemp = NULL;
+ LPWSTR lpNewUserParms = NULL;
+ BOOL fUpdate;
+
+ if (pchNWWorkstations == NULL) {
+ uniNWWorkstations.Buffer = NULL;
+ uniNWWorkstations.Length = 0;
+ uniNWWorkstations.MaximumLength = 0;
+ } else {
+ BOOL fDummy;
+ INT nStringLength = lstrlen(pchNWWorkstations) + 1;
+ pchTemp = (CHAR *) LocalAlloc (LPTR, nStringLength);
+
+ if ( pchTemp == NULL )
+ err = ERROR_NOT_ENOUGH_MEMORY;
+
+ if ( err == NERR_Success && !WideCharToMultiByte (CP_ACP, 0, pchNWWorkstations, nStringLength, pchTemp, nStringLength, NULL, &fDummy))
+ err = GetLastError();
+
+ if ( err == NERR_Success ) {
+ uniNWWorkstations.Buffer = (WCHAR *) pchTemp;
+ uniNWWorkstations.Length = nStringLength;
+ uniNWWorkstations.MaximumLength = nStringLength;
+ }
+ }
+
+ err = err? err: SetUserProperty (UserParms, NWLOGONFROM, uniNWWorkstations, USER_PROPERTY_TYPE_ITEM, &lpNewUserParms, &fUpdate);
+
+ if ((err == NERR_Success) && (lpNewUserParms != NULL)) {
+ if (fUpdate)
+ lstrcpyW(UserParms, lpNewUserParms);
+
+ LocalFree(lpNewUserParms);
+ }
+
+ if (pchTemp != NULL)
+ LocalFree (pchTemp);
+
+ return err;
+} // SetNWWorkstations
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS SetNWHomeDir(
+ const TCHAR * pchNWHomeDir
+ )
+
+/*++
+
+Routine Description:
+
+ Store NetWare Home Directory to UserParms If pchNWWorkstations is NULL,
+ this function will delete "NWLgonFrom" field from UserParms.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS err = NERR_Success;
+ UNICODE_STRING uniNWHomeDir;
+ CHAR * pchTemp = NULL;
+ LPWSTR lpNewUserParms = NULL;
+ BOOL fUpdate;
+
+ if (pchNWHomeDir == NULL) {
+ uniNWHomeDir.Buffer = NULL;
+ uniNWHomeDir.Length = 0;
+ uniNWHomeDir.MaximumLength = 0;
+ } else {
+ BOOL fDummy;
+ INT nStringLength = lstrlen(pchNWHomeDir) + 1;
+ pchTemp = (CHAR *) LocalAlloc (LPTR, nStringLength);
+
+ if ( pchTemp == NULL )
+ err = ERROR_NOT_ENOUGH_MEMORY;
+
+ if ( err == NERR_Success && !WideCharToMultiByte (CP_ACP, 0, pchNWHomeDir, nStringLength, pchTemp, nStringLength, NULL, &fDummy))
+ err = GetLastError();
+
+ if ( err == NERR_Success ) {
+ uniNWHomeDir.Buffer = (WCHAR *) pchTemp;
+ uniNWHomeDir.Length = nStringLength;
+ uniNWHomeDir.MaximumLength = nStringLength;
+ }
+ }
+
+ err = err? err : SetUserProperty (UserParms, NWHOMEDIR, uniNWHomeDir, USER_PROPERTY_TYPE_ITEM, &lpNewUserParms, &fUpdate);
+
+ if ((err == NERR_Success) && (lpNewUserParms != NULL)) {
+ if (fUpdate)
+ lstrcpyW(UserParms, lpNewUserParms);
+
+ LocalFree(lpNewUserParms);
+ }
+
+ if (pchTemp != NULL)
+ LocalFree (pchTemp);
+
+ return err;
+} // SetNWHomeDir
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+NTObjectIDGet(
+ LPTSTR ObjectName
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS status;
+ UNICODE_STRING UniNewObjectName;
+ PULONG pRids = NULL;
+ PSID_NAME_USE pSidNameUse = NULL;
+ ULONG ObjectID = 0;
+
+ RtlInitUnicodeString(&UniNewObjectName, ObjectName);
+
+ status = SamLookupNamesInDomain(DomainHandle,
+ 1,
+ &UniNewObjectName,
+ &pRids,
+ &pSidNameUse);
+
+ if ((status == STATUS_SUCCESS) && (pRids != NULL) && (pSidNameUse != NULL)) {
+ // Found the stupid name - so copy and free SAM garbage
+ ObjectID = pRids[0];
+ ObjectID |= BINDLIB_REMOTE_DOMAIN_BIAS;
+
+ SamFreeMemory(pRids);
+ SamFreeMemory(pSidNameUse);
+ }
+
+ return ObjectID;
+} // NTObjectIDGet
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+NTSAMParmsSet(
+ LPTSTR ObjectName,
+ FPNW_INFO fpnw,
+ LPTSTR Password,
+ BOOL ForcePasswordChange
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS status;
+ UNICODE_STRING UniNewObjectName;
+ PULONG pRids = NULL;
+ PSID_NAME_USE pSidNameUse = NULL;
+ ULONG ObjectID;
+ SID_NAME_USE SidNameUse;
+ SAM_HANDLE Handle = (SAM_HANDLE) 0;
+ PUSER_PARAMETERS_INFORMATION UserParmInfo = NULL;
+ USER_PARAMETERS_INFORMATION NewUserParmInfo;
+ LPWSTR lpNewUserParms = NULL;
+
+ RtlInitUnicodeString(&UniNewObjectName, ObjectName);
+
+ status = SamLookupNamesInDomain(DomainHandle,
+ 1,
+ &UniNewObjectName,
+ &pRids,
+ &pSidNameUse);
+
+ if ((status == STATUS_SUCCESS) && (pRids != NULL) && (pSidNameUse != NULL)) {
+ // Found the stupid name - so copy and free SAM garbage
+ ObjectID = pRids[0];
+ SidNameUse = pSidNameUse[0];
+
+ SamFreeMemory(pRids);
+ SamFreeMemory(pSidNameUse);
+
+ status = SamOpenUser(DomainHandle,
+ STANDARD_RIGHTS_READ |
+ STANDARD_RIGHTS_WRITE |
+ USER_ALL_ACCESS,
+ ObjectID,
+ &Handle);
+
+
+ // Now get the user parms
+ if (status == STATUS_SUCCESS)
+ status = SamQueryInformationUser(Handle, UserParametersInformation, (PVOID *) &UserParmInfo);
+
+ memset(UserParms, 0, sizeof(UserParms));
+ if ((status == STATUS_SUCCESS) && (UserParmInfo != NULL)) {
+ memcpy(UserParms, UserParmInfo->Parameters.Buffer, UserParmInfo->Parameters.Length * sizeof(WCHAR));
+ SamFreeMemory(UserParmInfo);
+
+ if (
+ ((status = SetNWPassword (ObjectID, Password, ForcePasswordChange)) == NERR_Success) &&
+ ((status = SetMaxConnections (fpnw.MaxConnections)) == NERR_Success) &&
+ ((status = SetGraceLoginAllowed (fpnw.GraceLoginAllowed)) == NERR_Success) &&
+ ((status = SetGraceLoginRemainingTimes (fpnw.GraceLoginRemaining)) == NERR_Success) &&
+ ((status = SetNWWorkstations (fpnw.LoginFrom)) == NERR_Success) &&
+ ((status = SetNWHomeDir (fpnw.HomeDir)) == NERR_Success) )
+ {
+ RtlInitUnicodeString(&NewUserParmInfo.Parameters, UserParms);
+ status = SamSetInformationUser(Handle, UserParametersInformation, (PVOID) &NewUserParmInfo);
+ }
+
+ }
+ }
+
+ if (Handle != (SAM_HANDLE) 0)
+ SamCloseHandle(Handle);
+
+ return 0;
+} // NTSAMParmsSet
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+NTSAMConnect(
+ LPTSTR ServerName,
+ LPTSTR DomainName
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS status;
+ OBJECT_ATTRIBUTES object_attrib;
+ UNICODE_STRING UniDomainName;
+ UNICODE_STRING UniServerName;
+
+ //
+ // Do all the garbage for connecting to SAM
+ //
+ RtlInitUnicodeString(&UniServerName, ServerName);
+ RtlInitUnicodeString(&UniDomainName, DomainName);
+ InitializeObjectAttributes(&object_attrib, NULL, 0, NULL, NULL);
+ status = SamConnect(&UniServerName, &SAMHandle, SAM_SERVER_ALL_ACCESS, &object_attrib);
+
+ if (status == STATUS_SUCCESS)
+ status = SamLookupDomainInSamServer(SAMHandle, &UniDomainName, &DomainID);
+
+ if (status == STATUS_SUCCESS)
+ status = SamOpenDomain(SAMHandle, DOMAIN_ALL_ACCESS, DomainID, &DomainHandle);
+
+ FPNWSecretGet(ServerName);
+ return (DWORD) status;
+} // NTSAMConnect
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+NTSAMClose()
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ if (DomainHandle != (SAM_HANDLE) 0)
+ SamCloseHandle(DomainHandle);
+
+ if (DomainID != (PSID) 0)
+ SamFreeMemory(DomainID);
+
+ if (SAMHandle != (SAM_HANDLE) 0)
+ SamCloseHandle(SAMHandle);
+
+ SAMHandle = (SAM_HANDLE) 0;
+ DomainHandle = (SAM_HANDLE) 0;
+ DomainID = (PSID) 0;
+
+} // NTSAMClose
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+NTServerSet(
+ LPTSTR ServerName
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+
+ // Fixup the destination server name
+ lstrcpy(CachedServer, TEXT("\\\\"));
+ lstrcat(CachedServer, ServerName);
+
+ if (!LocalName)
+ GetLocalName(&LocalName);
+
+ if (lstrcmpi(ServerName, LocalName) == 0)
+ LocalMachine = TRUE;
+ else
+ LocalMachine = FALSE;
+
+ if (FpnwHandle == NULL)
+ FpnwHandle = LoadLibrary(TEXT("FPNWCLNT.DLL"));
+
+ if ((FpnwHandle != NULL) && (pFpnwVolumeEnum == NULL)) {
+ pFpnwVolumeEnum = GetProcAddress(FpnwHandle, ("FpnwVolumeEnum"));
+ pFpnwApiBufferFree = GetProcAddress(FpnwHandle, ("FpnwApiBufferFree"));
+ }
+
+ return (0);
+
+} // NTServerSet
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+NTServerFree()
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ LocalMachine = FALSE;
+ lstrcpy(CachedServer, TEXT(""));
+ return (0);
+
+} // NTServerFree
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+FPNWShareAdd(
+ LPTSTR ShareName,
+ LPTSTR Path
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NET_API_STATUS Status = 0;
+ NWVOLUMEINFO NWVol;
+
+ if (FPNWLib == NULL)
+ FPNWLib = LoadLibrary(TEXT("FPNWCLNT.DLL"));
+
+ if (FPNWLib == NULL)
+ return 1;
+
+ if (NWVolumeAdd == NULL)
+ NWVolumeAdd = (DWORD (FAR * ) (LPWSTR, DWORD, PNWVOLUMEINFO)) GetProcAddress(FPNWLib, "NwVolumeAdd");
+
+ NWVol.lpVolumeName = AllocMemory((lstrlen(ShareName) + 1) * sizeof(TCHAR));
+ NWVol.lpPath = AllocMemory((lstrlen(Path) + 1) * sizeof(TCHAR));
+ if ((NWVol.lpVolumeName == NULL) || (NWVol.lpPath == NULL))
+ return 1;
+
+ lstrcpy(NWVol.lpVolumeName, ShareName);
+ NWVol.dwType = NWVOL_TYPE_DISKTREE;
+ NWVol.dwMaxUses = NWVOL_MAX_USES_UNLIMITED;
+ NWVol.dwCurrentUses = 0;
+ lstrcpy(NWVol.lpPath, Path);
+
+ if (LocalMachine)
+ Status = NWVolumeAdd(NULL, 1, &NWVol);
+ else
+ Status = NWVolumeAdd(CachedServer, 1, &NWVol);
+
+ return Status;
+
+} // FPNWShareAdd
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+NTShareAdd(
+ LPTSTR ShareName,
+ LPTSTR Path
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NET_API_STATUS Status = 0;
+ SHARE_INFO_2 shi2;
+ DWORD parmerr;
+
+ memset(&shi2, 0, sizeof(SHARE_INFO_2));
+
+ shi2.shi2_netname = ShareName;
+ shi2.shi2_type = STYPE_DISKTREE;
+ shi2.shi2_max_uses = (DWORD) 0xffffffff;
+ shi2.shi2_path = Path;
+
+ if (LocalMachine)
+ Status = NetShareAdd(NULL, 2, (LPBYTE) &shi2, &parmerr);
+ else
+ Status = NetShareAdd(CachedServer, 2, (LPBYTE) &shi2, &parmerr);
+
+ return Status;
+
+} // NTShareAdd
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+NTUsersEnum(
+ USER_LIST **lpUserList
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ LPUSER_INFO_0 buffer = NULL;
+ NET_API_STATUS Status = 0;
+ DWORD prefmaxlength = 0xffffffff;
+ DWORD totalentries = 0;
+ DWORD entriesread = 0;
+ DWORD resumehandle = 0;
+ DWORD i;
+ USER_LIST *UserList = NULL;
+ USER_BUFFER *UserBuffer = NULL;
+
+ if (LocalMachine)
+ Status = NetUserEnum(NULL, 0, 0, (LPBYTE *) &buffer, prefmaxlength, &entriesread, &totalentries, &resumehandle);
+ else
+ Status = NetUserEnum(CachedServer, 0, 0, (LPBYTE *) &buffer, prefmaxlength, &entriesread, &totalentries, &resumehandle);
+
+ if (Status == NO_ERROR) {
+
+ UserList = AllocMemory(sizeof(USER_LIST) + (sizeof(USER_BUFFER) * entriesread));
+
+ if (!UserList) {
+ Status = ERROR_NOT_ENOUGH_MEMORY;
+ } else {
+ UserBuffer = UserList->UserBuffer;
+
+ for (i = 0; i < entriesread; i++) {
+ lstrcpy(UserBuffer[i].Name, buffer[i].usri0_name);
+ lstrcpy(UserBuffer[i].NewName, buffer[i].usri0_name);
+ }
+
+ qsort((void *) UserBuffer, (size_t) entriesread, sizeof(USER_BUFFER), UserListCompare);
+ }
+ }
+
+ if (buffer != NULL)
+ NetApiBufferFree((LPVOID) buffer);
+
+ if (UserList != NULL)
+ UserList->Count = entriesread;
+
+ *lpUserList = UserList;
+ return Status;
+
+} // NTUsersEnum
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+NTGroupsEnum(
+ GROUP_LIST **lpGroupList
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ LPGROUP_INFO_0 buffer = NULL;
+ NET_API_STATUS Status = 0;
+ DWORD prefmaxlength = 0xffffffff;
+ DWORD totalentries = 0;
+ DWORD entriesread = 0;
+ DWORD resumehandle = 0;
+ DWORD i;
+ GROUP_LIST *GroupList = NULL;
+ GROUP_BUFFER *GroupBuffer = NULL;
+
+ if (LocalMachine)
+ Status = NetGroupEnum(NULL, 0, (LPBYTE *) &buffer, prefmaxlength, &entriesread, &totalentries, &resumehandle);
+ else
+ Status = NetGroupEnum(CachedServer, 0, (LPBYTE *) &buffer, prefmaxlength, &entriesread, &totalentries, &resumehandle);
+
+ if (Status == NO_ERROR) {
+
+ GroupList = AllocMemory(sizeof(GROUP_LIST) + (sizeof(GROUP_BUFFER) * entriesread));
+
+ if (!GroupList) {
+ Status = ERROR_NOT_ENOUGH_MEMORY;
+ } else {
+ GroupBuffer = GroupList->GroupBuffer;
+
+ for (i = 0; i < entriesread; i++) {
+ lstrcpy(GroupBuffer[i].Name, buffer[i].grpi0_name);
+ lstrcpy(GroupBuffer[i].NewName, buffer[i].grpi0_name);
+ }
+ }
+ }
+
+ if (buffer != NULL)
+ NetApiBufferFree((LPVOID) buffer);
+
+ if (GroupList != NULL)
+ GroupList->Count = entriesread;
+
+ *lpGroupList = GroupList;
+ return Status;
+
+} // NTGroupsEnum
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+NTDomainEnum(
+ SERVER_BROWSE_LIST **lpServList
+ )
+
+/*++
+
+Routine Description:
+
+ Enumerates all NT servers in a given domain.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ LPSERVER_INFO_101 buffer = NULL;
+ NET_API_STATUS Status = 0;
+ DWORD prefmaxlength = 0xffffffff;
+ DWORD totalentries = 0;
+ DWORD entriesread = 0;
+ DWORD resumehandle = 0;
+ DWORD i;
+ BOOL Container = FALSE;
+ SERVER_BROWSE_LIST *ServList = NULL;
+ SERVER_BROWSE_BUFFER *SList;
+
+ Status = NetServerEnum(NULL, 101, (LPBYTE *) &buffer, prefmaxlength, &entriesread, &totalentries, SV_TYPE_DOMAIN_ENUM, NULL, &resumehandle);
+
+ if (Status == NO_ERROR) {
+
+ ServList = AllocMemory(sizeof(SERVER_BROWSE_LIST) + (sizeof(SERVER_BROWSE_BUFFER) * entriesread));
+
+ if (!ServList) {
+ Status = ERROR_NOT_ENOUGH_MEMORY;
+ } else {
+ ServList->Count = entriesread;
+ SList = (SERVER_BROWSE_BUFFER *) &ServList->SList;
+
+ for (i = 0; i < entriesread; i++) {
+ lstrcpy(SList[i].Name, buffer[i].sv101_name);
+ lstrcpy(SList[i].Description, buffer[i].sv101_comment);
+ SList[i].Container = FALSE;
+ SList[i].child = NULL;
+ }
+ }
+ }
+
+ if (buffer != NULL)
+ NetApiBufferFree((LPVOID) buffer);
+
+ *lpServList = ServList;
+ return Status;
+
+} // NTDomainEnum
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+NTServerEnum(
+ LPTSTR szContainer,
+ SERVER_BROWSE_LIST **lpServList
+ )
+
+/*++
+
+Routine Description:
+
+ Enumerates all NT servers in a given domain.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ LPSERVER_INFO_101 buffer = NULL;
+ NET_API_STATUS Status = 0;
+ DWORD prefmaxlength = 0xffffffff;
+ DWORD totalentries = 0;
+ DWORD entriesread = 0;
+ DWORD resumehandle = 0;
+ DWORD i;
+ BOOL Container = FALSE;
+ SERVER_BROWSE_LIST *ServList = NULL;
+ SERVER_BROWSE_BUFFER *SList;
+
+ if (((szContainer != NULL) && (lstrlen(szContainer))))
+ Container = TRUE;
+
+ if (Container)
+ Status = NetServerEnum(NULL, 101, (LPBYTE *) &buffer, prefmaxlength, &entriesread, &totalentries, SV_TYPE_NT, szContainer, &resumehandle);
+ else
+ Status = NetServerEnum(NULL, 101, (LPBYTE *) &buffer, prefmaxlength, &entriesread, &totalentries, SV_TYPE_DOMAIN_ENUM, NULL, &resumehandle);
+
+ if (Status == NO_ERROR) {
+
+ ServList = AllocMemory(sizeof(SERVER_BROWSE_LIST) + (sizeof(SERVER_BROWSE_BUFFER) * entriesread));
+
+ if (!ServList) {
+ Status = ERROR_NOT_ENOUGH_MEMORY;
+ } else {
+ ServList->Count = entriesread;
+ SList = (SERVER_BROWSE_BUFFER *) &ServList->SList;
+
+ for (i = 0; i < entriesread; i++) {
+ lstrcpy(SList[i].Name, buffer[i].sv101_name);
+ lstrcpy(SList[i].Description, buffer[i].sv101_comment);
+ SList[i].Container = !Container;
+ SList[i].child = NULL;
+ }
+ }
+ }
+
+ if (buffer != NULL)
+ NetApiBufferFree((LPVOID) buffer);
+
+ *lpServList = ServList;
+ return Status;
+
+} // NTServerEnum
+
+
+/////////////////////////////////////////////////////////////////////////
+BOOL
+NTShareNameValidate(
+ LPTSTR szShareName
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ TCHAR *ptr = szShareName;
+ BOOL ret;
+
+ ret = TRUE;
+
+ if (*ptr) {
+ // go to end
+ while (*ptr)
+ ptr++;
+
+ // now back up to last character
+ ptr--;
+ if (*ptr == TEXT('$')) // for ADMIN$, IPC$, etc...
+ ret = FALSE;
+
+ } else
+ // Don't allow zero length - not sure why we would ever get these...
+ ret = FALSE;
+
+ return ret;
+
+} // NTShareNameValidate
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+NTSharesEnum(
+ SHARE_LIST **lpShares,
+ DRIVE_LIST *Drives
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ LPSHARE_INFO_2 buffer = NULL;
+ NET_API_STATUS Status = 0;
+ DWORD prefmaxlength = 0xffffffff;
+ DWORD totalentries = 0;
+ DWORD entriesread = 0;
+ DWORD ActualEntries = 0;
+ DWORD resumehandle = 0;
+ DWORD i, di;
+ SHARE_LIST *ShareList = NULL;
+ SHARE_BUFFER *SList;
+ DRIVE_BUFFER *DList;
+ ULONG TotalDrives;
+ TCHAR Drive[2];
+ PFPNWVOLUMEINFO VolumeInfo, VolumeEntry;
+ BOOL Found;
+
+ if (LocalMachine)
+ Status = NetShareEnum(NULL, 2, (LPBYTE *) &buffer, prefmaxlength, &entriesread, &totalentries, &resumehandle);
+ else
+ Status = NetShareEnum(CachedServer, 2, (LPBYTE *) &buffer, prefmaxlength, &entriesread, &totalentries, &resumehandle);
+
+ if (Status == NO_ERROR) {
+
+ // We have the list - but need to prune out IPC$, Admin$, etc...
+ for (i = 0; i < entriesread; i++)
+ if ((buffer[i].shi2_type == STYPE_DISKTREE) && NTShareNameValidate(buffer[i].shi2_netname))
+ ActualEntries++;
+
+ ShareList = AllocMemory(sizeof(SHARE_LIST) + (sizeof(SHARE_BUFFER) * ActualEntries));
+
+ if (!ShareList) {
+ Status = ERROR_NOT_ENOUGH_MEMORY;
+ } else {
+ SList = (SHARE_BUFFER *) &ShareList->SList;
+
+ ShareList->Count = ActualEntries;
+ ActualEntries = 0;
+
+ TotalDrives = 0;
+ Drive[1] = TEXT('\0');
+ if (Drives != NULL) {
+ DList = Drives->DList;
+ TotalDrives = Drives->Count;
+ }
+
+ // loop through copying the data
+ for (i = 0; i < entriesread; i++)
+ if ((buffer[i].shi2_type == STYPE_DISKTREE) && NTShareNameValidate(buffer[i].shi2_netname)) {
+ lstrcpy(SList[ActualEntries].Name, buffer[i].shi2_netname);
+ lstrcpy(SList[ActualEntries].Path, buffer[i].shi2_path);
+ SList[ActualEntries].Index = (USHORT) ActualEntries;
+
+ // Scan drive list looking for match to share path
+ for (di = 0; di < TotalDrives; di++) {
+ // Get first char from path - should be drive letter
+ Drive[0] = *buffer[i].shi2_path;
+ if (!lstrcmpi(Drive, DList[di].Drive))
+ SList[ActualEntries].Drive = &DList[di];
+ }
+
+ ActualEntries++;
+ }
+
+ }
+ }
+
+ //
+ // Now loop through any FPNW shares and tack those on as well
+ //
+ VolumeInfo = NULL;
+ resumehandle = entriesread = 0;
+
+ if (pFpnwVolumeEnum != NULL)
+ if (LocalMachine)
+ Status = (*pFpnwVolumeEnum) ( NULL, 1, (LPBYTE *)&VolumeInfo, &entriesread, &resumehandle );
+ else
+ Status = (*pFpnwVolumeEnum) ( CachedServer, 1, (LPBYTE *)&VolumeInfo, &entriesread, &resumehandle );
+
+#if DBG
+dprintf(TEXT("Status: 0x%lX Entries: %lu\n"), Status, entriesread);
+#endif
+ if ( !Status && entriesread ) {
+
+ if (ShareList)
+ ShareList = ReallocMemory(ShareList, sizeof(SHARE_LIST) + (sizeof(SHARE_BUFFER) * (ActualEntries + entriesread)));
+ else
+ ShareList = AllocMemory(sizeof(SHARE_LIST) + (sizeof(SHARE_BUFFER) * entriesread));
+
+ if (!ShareList) {
+ Status = ERROR_NOT_ENOUGH_MEMORY;
+ } else {
+ SList = (SHARE_BUFFER *) &ShareList->SList; // reset pointer...
+
+ // loop through copying the data
+ for (i = 0; i < entriesread; i++) {
+ //
+ // Make sure not in NT Share list already
+ //
+ Found = FALSE;
+
+ for (di = 0; di < ShareList->Count; di++)
+ if (!lstrcmpi(SList[di].Name, VolumeInfo[i].lpVolumeName))
+ Found = TRUE;
+
+ if ((!Found) && (VolumeInfo[i].dwType == FPNWVOL_TYPE_DISKTREE) ) {
+ lstrcpy(SList[ActualEntries].Name, VolumeInfo[i].lpVolumeName);
+ lstrcpy(SList[ActualEntries].Path, VolumeInfo[i].lpPath);
+ SList[ActualEntries].Index = (USHORT) ActualEntries;
+
+ // Scan drive list looking for match to share path
+ for (di = 0; di < TotalDrives; di++) {
+ // Get first char from path - should be drive letter
+ Drive[0] = *VolumeInfo[i].lpPath;
+ if (!lstrcmpi(Drive, DList[di].Drive))
+ SList[ActualEntries].Drive = &DList[di];
+ }
+
+ ActualEntries++;
+ }
+ }
+ }
+ }
+
+ if (ShareList)
+ ShareList->Count = ActualEntries;
+
+ if (VolumeInfo && (pFpnwApiBufferFree != NULL))
+ (*pFpnwApiBufferFree) ( VolumeInfo );
+
+ if (buffer != NULL)
+ NetApiBufferFree((LPVOID) buffer);
+
+ *lpShares = ShareList;
+ return Status;
+} // NTSharesEnum
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+NTGroupSave(
+ LPTSTR Name
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ static NET_API_STATUS Status = 0;
+ GROUP_INFO_0 grpi0;
+ DWORD err;
+
+ grpi0.grpi0_name = Name;
+
+ if (LocalMachine)
+ Status = NetGroupAdd(NULL, 0, (LPBYTE) &grpi0, &err);
+ else {
+ Status = NetGroupAdd(CachedServer, 0, (LPBYTE) &grpi0, &err);
+ }
+ return Status;
+
+} // NTGroupSave
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+NTGroupUserAdd(
+ LPTSTR GroupName,
+ LPTSTR UserName,
+ BOOL Local
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NET_API_STATUS Status = 0;
+ SID *pUserSID = NULL;
+
+ if (LocalMachine)
+ if (Local) {
+ pUserSID = NTSIDGet(NULL, UserName);
+
+ if (pUserSID != NULL)
+ Status = NetLocalGroupAddMember(NULL, GroupName, pUserSID);
+
+ } else
+ Status = NetGroupAddUser(NULL, GroupName, UserName);
+ else {
+ if (Local) {
+ pUserSID = NTSIDGet(CachedServer, UserName);
+
+ if (pUserSID != NULL)
+ Status = NetLocalGroupAddMember(CachedServer, GroupName, pUserSID);
+ } else
+ Status = NetGroupAddUser(CachedServer, GroupName, UserName);
+ }
+
+ // If complaining because user is already there, ignore
+ if (Status == NERR_UserInGroup)
+ Status = 0;
+
+ return Status;
+
+} // NTGroupUserAdd
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+NTUserInfoSave(
+ NT_USER_INFO *NT_UInfo,
+ PFPNW_INFO fpnw
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ static NET_API_STATUS Status = 0;
+ struct _USER_INFO_3 *usri3;
+ DWORD err;
+
+ usri3 = (struct _USER_INFO_3 *) NT_UInfo;
+
+ // Map logon hours to GMT time - as NetAPI re-fixes it
+ NetpRotateLogonHours(NT_UInfo->logon_hours, UNITS_PER_WEEK, TRUE);
+
+ if (LocalMachine)
+ Status = NetUserAdd(NULL, 3, (LPBYTE) usri3, &err);
+ else
+ Status = NetUserAdd(CachedServer, 3, (LPBYTE) usri3, &err);
+
+ if ((!Status) && (fpnw != NULL)) {
+ // Need to get user info via LSA call before calling setuserparms
+ }
+
+ return Status;
+
+} // NTUserInfoSave
+
+
+#define NEW_NULL_PASSWD TEXT(" ")
+/////////////////////////////////////////////////////////////////////////
+DWORD
+NTUserInfoSet(
+ NT_USER_INFO *NT_UInfo,
+ PFPNW_INFO fpnw
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ LPWSTR Password;
+ LPWSTR Name;
+ static NET_API_STATUS Status = 0;
+ struct _USER_INFO_3 *usri3;
+ DWORD err;
+
+ // Tell it not to replace the password
+ Password = NT_UInfo->password;
+ NT_UInfo->password = NULL;
+
+ Name = NT_UInfo->name;
+ usri3 = (struct _USER_INFO_3 *) NT_UInfo;
+
+ // Map logon hours to GMT time - as NetAPI re-fixes it
+ NetpRotateLogonHours(NT_UInfo->logon_hours, UNITS_PER_WEEK, TRUE);
+
+ if (LocalMachine)
+ Status = NetUserSetInfo(NULL, Name, 3, (LPBYTE) usri3, &err);
+ else
+ Status = NetUserSetInfo(CachedServer, Name, 3, (LPBYTE) usri3, &err);
+
+ if ((!Status) && (fpnw != NULL)) {
+ }
+
+ // Reset the password in our data structure.
+ NT_UInfo->password = Password;
+ return Status;
+
+} // NTUserInfoSet
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+NTUserRecInit(
+ LPTSTR UserName,
+ NT_USER_INFO *NT_UInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Initializes a user record, uses static variables for string holders
+ so is not re-entrant, and will overwrite previous records data if
+ called again.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ static TCHAR uname[UNLEN + 1];
+ static TCHAR upassword[ENCRYPTED_PWLEN];
+ static TCHAR uhomedir[PATHLEN + 1];
+ static TCHAR ucomment[MAXCOMMENTSZ + 1];
+ static TCHAR uscriptpath[PATHLEN + 1];
+ static TCHAR ufullname[MAXCOMMENTSZ + 1];
+ static TCHAR uucomment[MAXCOMMENTSZ + 1];
+ static TCHAR uparms[MAXCOMMENTSZ + 1];
+ static TCHAR uworkstations[1];
+ static BYTE ulogonhours[21];
+ static TCHAR ulogonserver[1];
+ static TCHAR uprofile[1];
+ static TCHAR uhome_dir_drive[1];
+
+ // init all the static data holders.
+ memset(uname, 0, sizeof( uname ));
+ lstrcpy(uname, UserName);
+
+ memset(upassword, 0, sizeof( upassword ));
+ memset(uhomedir, 0, sizeof( uhomedir ));
+ memset(ucomment, 0, sizeof( ucomment ));
+ memset(uscriptpath, 0, sizeof( uscriptpath ));
+ memset(ufullname, 0, sizeof( ufullname ));
+ memset(uucomment, 0, sizeof( uucomment ));
+ memset(uparms, 0, sizeof( uparms ));
+ memset(uworkstations, 0, sizeof( uworkstations ));
+ memset(ulogonhours, 0, sizeof( ulogonhours ));
+ memset(ulogonserver, 0, sizeof( ulogonserver ));
+ memset(uprofile, 0, sizeof( uprofile ));
+ memset(uhome_dir_drive, 0, sizeof( uhome_dir_drive ));
+
+ memset(NT_UInfo, 0, sizeof(NT_USER_INFO));
+
+ // point the passed in record to these data holders
+ NT_UInfo->name = uname;
+ NT_UInfo->password = upassword;
+ NT_UInfo->home_dir = uhomedir;
+ NT_UInfo->comment = ucomment;
+ NT_UInfo->script_path = uscriptpath;
+ NT_UInfo->full_name = ufullname;
+ NT_UInfo->usr_comment = uucomment;
+ NT_UInfo->parms = uparms;
+ NT_UInfo->workstations = uworkstations;
+ NT_UInfo->logon_hours = ulogonhours;
+ NT_UInfo->logon_server = ulogonserver;
+ NT_UInfo->profile = uprofile;
+ NT_UInfo->home_dir_drive = uhome_dir_drive;
+ NT_UInfo->units_per_week = UNITS_PER_WEEK;
+
+ // Set the default values for special fields
+ NT_UInfo->primary_group_id = DOMAIN_GROUP_RID_USERS;
+ NT_UInfo->priv = USER_PRIV_USER;
+ NT_UInfo->acct_expires = TIMEQ_FOREVER;
+ NT_UInfo->max_storage = USER_MAXSTORAGE_UNLIMITED;
+ NT_UInfo->flags = UF_SCRIPT;
+
+} // NTUserRecInit
+
+
+/////////////////////////////////////////////////////////////////////////
+LPTSTR
+NTDriveShare(
+ LPTSTR DriveLetter
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ static TCHAR RootPath[MAX_SERVER_NAME_LEN + 3];
+
+ if (LocalMachine)
+ wsprintf(RootPath, TEXT("%s:\\"), DriveLetter);
+ else
+ wsprintf(RootPath, TEXT("%s\\%s$\\"), CachedServer, DriveLetter);
+
+ return RootPath;
+
+} // NTDriveShare
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+NTDriveInfoGet(
+ DRIVE_BUFFER *DBuff
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ DWORD volMaxCompLength, volFileSystemFlags;
+ DWORD sectorsPC, bytesPS, FreeClusters, Clusters;
+ TCHAR NameBuffer[20];
+ TCHAR volName[20];
+ LPTSTR RootPath;
+ UINT previousErrorMode;
+
+ //
+ // Disable DriveAccess Error, because no media is inserted onto specified drive.
+ //
+ previousErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS|
+ SEM_NOOPENFILEERRORBOX);
+
+ volMaxCompLength = volFileSystemFlags = 0;
+ sectorsPC = bytesPS = FreeClusters = Clusters = 0;
+
+ // First get file system type
+ RootPath = NTDriveShare(DBuff->Drive);
+ if (GetVolumeInformation(RootPath, volName, sizeof(volName), NULL, &volMaxCompLength, &volFileSystemFlags, NameBuffer, sizeof(NameBuffer))) {
+ if (GetDriveType(RootPath) == DRIVE_CDROM)
+ lstrcpy(DBuff->DriveType, Lids(IDS_S_49));
+ else
+ lstrcpyn(DBuff->DriveType, NameBuffer, sizeof(DBuff->DriveType)-1);
+
+ lstrcpyn(DBuff->Name, volName, sizeof(DBuff->Name)-1);
+
+ if (!lstrcmpi(NameBuffer, Lids(IDS_S_9)))
+ DBuff->Type = DRIVE_TYPE_NTFS;
+ }
+ else {
+ if (GetDriveType(RootPath) == DRIVE_CDROM)
+ lstrcpy(DBuff->DriveType, Lids(IDS_S_49));
+ else
+ lstrcpy(DBuff->DriveType, TEXT("\0"));
+
+ lstrcpy(DBuff->Name, TEXT("\0"));
+
+ if (!lstrcmpi(NameBuffer, Lids(IDS_S_9)))
+ DBuff->Type = DRIVE_TYPE_NTFS;
+ }
+
+ if (GetDiskFreeSpace(RootPath, &sectorsPC, &bytesPS, &FreeClusters, &Clusters)) {
+ DBuff->TotalSpace = Clusters * sectorsPC * bytesPS;
+ DBuff->FreeSpace = FreeClusters * sectorsPC * bytesPS;
+ }
+ else {
+ DBuff->TotalSpace = 0;
+ DBuff->FreeSpace = 0;
+ }
+
+ //
+ // Back to original error mode.
+ //
+ SetErrorMode(previousErrorMode);
+
+} // NTDriveInfoGet
+
+
+/////////////////////////////////////////////////////////////////////////
+BOOL
+NTDriveValidate(
+ TCHAR DriveLetter
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ BOOL ret = FALSE;
+
+ // Just make sure it isn't one of the two floppys
+ if (!((DriveLetter == TEXT('A')) || (DriveLetter == TEXT('B'))))
+ ret = TRUE;
+
+ return ret;
+
+} // NTDriveValidate
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+NTDrivesEnum(
+ DRIVE_LIST **lpDrives
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ TCHAR *buffer = NULL;
+ NET_API_STATUS Status = 0;
+ DWORD entriesread, totalentries, resumehandle, actualentries, i;
+ DRIVE_LIST *DriveList;
+ DRIVE_BUFFER *DList;
+
+ entriesread = totalentries = resumehandle = actualentries = 0;
+
+ if (LocalMachine)
+ Status = NetServerDiskEnum(NULL, 0, (LPBYTE *) &buffer, 0xFFFFFFFF, &entriesread, &totalentries, &resumehandle);
+ else
+ Status = NetServerDiskEnum(CachedServer, 0, (LPBYTE *) &buffer, 0xFFFFFFFF, &entriesread, &totalentries, &resumehandle);
+
+ if (Status == NO_ERROR) {
+ // We have the list - but need to prune out A:, B:
+ for (i = 0; i < entriesread; i++)
+ if (NTDriveValidate(buffer[i * 3]))
+ actualentries++;
+
+ // temporarily use i to hold total size of data structure
+ i = sizeof(DRIVE_LIST) + (sizeof(DRIVE_BUFFER) * actualentries);
+ DriveList = AllocMemory(i);
+
+ if (!DriveList) {
+ Status = ERROR_NOT_ENOUGH_MEMORY;
+ } else {
+ memset(DriveList, 0, i);
+ DList = (DRIVE_BUFFER *) &DriveList->DList;
+ DriveList->Count = actualentries;
+
+ // Now fill in the individual data items
+ actualentries = 0;
+ for (i = 0; i < entriesread; i++)
+ if (NTDriveValidate(buffer[i * 3])) {
+ DList[actualentries].Drive[0] = buffer[i * 3];
+ NTDriveInfoGet(&DList[actualentries]);
+ actualentries++;
+ }
+ }
+ }
+
+ if (buffer != NULL)
+ NetApiBufferFree((LPVOID) buffer);
+
+ *lpDrives = DriveList;
+ return;
+
+} // NTDrivesEnum
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+NTServerGetInfo(
+ LPTSTR ServerName
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ TCHAR LocServer[MAX_SERVER_NAME_LEN + 3];
+ NET_API_STATUS Status = 0;
+
+ if (ServInfo != NULL)
+ NetApiBufferFree((LPVOID) ServInfo);
+
+ ServInfo = NULL;
+
+ wsprintf(LocServer, TEXT("\\\\%s"), ServerName);
+
+ if (!LocalName)
+ GetLocalName(&LocalName);
+
+ if (lstrcmpi(ServerName, LocalName) == 0)
+ Status = NetServerGetInfo(NULL, 101, (LPBYTE *) &ServInfo);
+ else
+ Status = NetServerGetInfo(LocServer, 101, (LPBYTE *) &ServInfo);
+
+ if (Status) {
+ ServInfo = NULL;
+ return;
+ }
+
+} // NTServerGetInfo
+
+
+/////////////////////////////////////////////////////////////////////////
+NT_CONN_BUFFER *
+NTConnListFind(
+ LPTSTR ServerName
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ BOOL Found = FALSE;
+ static NT_CONN_BUFFER *ServList;
+
+ ServList = NTConnListStart;
+
+ while ((ServList && !Found)) {
+ if (!lstrcmpi(ServList->Name, ServerName))
+ Found = TRUE;
+ else
+ ServList = ServList->next;
+ }
+
+ if (!Found)
+ ServList = NULL;
+
+ return (ServList);
+
+} // NTConnListFind
+
+
+/////////////////////////////////////////////////////////////////////////
+NT_CONN_BUFFER *
+NTConnListAdd(
+ LPTSTR ServerName
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ static NT_CONN_BUFFER *tmpPtr;
+ ULONG Size, strlen1;
+
+ tmpPtr = NULL;
+ strlen1 = (lstrlen(ServerName) + 1) * sizeof(TCHAR);
+ Size = sizeof(NT_CONN_BUFFER) + strlen1;
+ tmpPtr = AllocMemory(Size);
+
+ if (tmpPtr != NULL) {
+ // init it to NULL's
+ memset(tmpPtr, 0, Size);
+ tmpPtr->Name = (LPTSTR) ((BYTE *) tmpPtr + sizeof(NT_CONN_BUFFER));
+ lstrcpy(tmpPtr->Name, ServerName);
+
+ // link it into the list
+ if (!NTConnListStart)
+ NTConnListStart = NTConnListEnd = tmpPtr;
+ else {
+ NTConnListEnd->next = tmpPtr;
+ tmpPtr->prev = NTConnListEnd;
+ NTConnListEnd = tmpPtr;
+ }
+ }
+
+ return (tmpPtr);
+
+} // NTConnListAdd
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+NTConnListDelete(
+ NT_CONN_BUFFER *tmpPtr
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NT_CONN_BUFFER *PrevPtr;
+ NT_CONN_BUFFER *NextPtr;
+
+ if (tmpPtr == NULL)
+ return;
+
+ // Now unlink the actual server record
+ PrevPtr = tmpPtr->prev;
+ NextPtr = tmpPtr->next;
+
+ if (PrevPtr)
+ PrevPtr->next = NextPtr;
+
+ if (NextPtr)
+ NextPtr->prev = PrevPtr;
+
+ // Check if at end of list
+ if (NTConnListEnd == tmpPtr)
+ NTConnListEnd = PrevPtr;
+
+ // Check if at start of list
+ if (NTConnListStart == tmpPtr)
+ NTConnListStart = NextPtr;
+
+ FreeMemory(tmpPtr);
+
+} // NTConnListDelete
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+NTConnListDeleteAll()
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ static TCHAR LocServer[MAX_SERVER_NAME_LEN + 3];
+ NT_CONN_BUFFER *ServList;
+ NT_CONN_BUFFER *ServListNext;
+
+ // Now remove the entries from the internal list
+ ServList = NTConnListStart;
+
+ while (ServList) {
+ ServListNext = ServList->next;
+
+ wsprintf(LocServer, Lids(IDS_S_10), ServList->Name);
+ WNetCancelConnection2(LocServer, 0, FALSE);
+
+ NTConnListDelete(ServList);
+ ServList = ServListNext;
+ }
+
+} // NTConnListDeleteAll
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+NTUseDel(
+ LPTSTR ServerName
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ static TCHAR LocServer[MAX_SERVER_NAME_LEN + 3];
+ NT_CONN_BUFFER *NTConn;
+
+ // Find it in our connection list - if it exists get rid of it.
+ NTConn = NTConnListFind(ServerName);
+ if (NTConn != NULL)
+ NTConnListDelete(NTConn);
+
+ NTServerFree();
+ wsprintf(LocServer, Lids(IDS_S_10), ServerName);
+ WNetCancelConnection2(LocServer, 0, FALSE);
+
+} // NTUseDel
+
+
+/////////////////////////////////////////////////////////////////////////
+BOOL
+NTServerValidate(
+ HWND hWnd,
+ LPTSTR ServerName
+ )
+
+/*++
+
+Routine Description:
+
+ Validates a given server - makes sure it can be connected to and
+ that the user has admin privs on it.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ BOOL ret = FALSE;
+ DWORD idsErr = 0;
+ DWORD lastErr = 0;
+ DWORD Size;
+ NET_API_STATUS Status;
+ LPUSER_INFO_1 UserInfo1 = NULL;
+ TCHAR UserName[MAX_NT_USER_NAME_LEN + 1];
+ TCHAR ServName[MAX_SERVER_NAME_LEN + 3]; // +3 for leading slashes and ending NULL
+ LPVOID lpMessageBuffer = NULL;
+
+ NTServerSet(ServerName);
+
+ // server already connected then return success
+ if (NTConnListFind(ServerName)) {
+ return TRUE;
+ }
+
+ CursorHourGlass();
+
+ // Get Current Logged On User
+ lstrcpy(UserName, TEXT(""));
+ Size = sizeof(UserName);
+ WNetGetUser(NULL, UserName, &Size);
+
+ // Fixup the destination server name
+ lstrcpy(ServName, TEXT( "\\\\" ));
+ lstrcat(ServName, ServerName);
+
+ // Make an ADMIN$ connection to the server
+ if (UseAddPswd(hWnd, UserName, ServName, Lids(IDS_S_11), NT_PROVIDER)) {
+
+ // Double check we have admin privs
+ // Get connection to the system and check for admin privs...
+ Status = NetUserGetInfo(ServName, UserName, 1, (LPBYTE *) &UserInfo1);
+
+ if (Status == ERROR_SUCCESS) {
+
+ // Got User info, now make sure admin flag is set
+ if (!(UserInfo1->usri1_priv & USER_PRIV_ADMIN)) {
+ idsErr = IDS_E_6;
+ goto cleanup;
+ }
+
+ // We may have a connection to admin$ and we may have proven
+ // that the user we made the connection with really is an admin
+ // but sitting at the local machine we still may have a problem
+ // acquiring all of the administrative information necessary to
+ // accomplish a successful conversion. each and every one of the
+ // functions that make network calls should return errors and if
+ // that were the case then the errors would propagate up and we
+ // could deal with the access denial reasonably. unfortunately,
+ // alot of assumptions are made after the success of this call
+ // so we must perform yet another test here...
+ if (LocalMachine) {
+
+ DWORD EntriesRead = 0;
+ DWORD TotalEntries = 0;
+ LPSHARE_INFO_2 ShareInfo2 = NULL;
+
+ Status = NetShareEnum(
+ ServName,
+ 2,
+ (LPBYTE *) &ShareInfo2,
+ MAX_PREFERRED_LENGTH,
+ &EntriesRead,
+ &TotalEntries,
+ NULL
+ );
+
+ if (ShareInfo2 != NULL)
+ NetApiBufferFree((LPVOID) ShareInfo2); // discarded...
+
+ if (Status != ERROR_SUCCESS) {
+ idsErr = (Status == ERROR_ACCESS_DENIED) ? IDS_E_6 : IDS_E_5;
+ goto cleanup;
+ }
+ }
+
+ // Now get server info and make certain this is an NT server
+ // instead of an LM type server. Note: Info from the call is
+ // cached and used later, so don't remove it!!
+ NTServerGetInfo(ServerName);
+
+ if (ServInfo) {
+
+ if (ServInfo->sv101_platform_id == SV_PLATFORM_ID_NT) {
+
+ if (ServInfo->sv101_type & (TYPE_DOMAIN)) {
+
+ // If NTAS and have admin privs then we are set
+ // then add it to our connection list...
+ NTConnListAdd(ServerName);
+ ret = TRUE;
+
+ } else {
+ idsErr = IDS_E_8;
+ }
+
+ } else {
+ idsErr = IDS_E_8;
+ }
+
+ } else {
+ idsErr = IDS_E_7;
+ }
+
+ } else {
+
+ // determine error string id and bail out...
+ idsErr = (Status == ERROR_ACCESS_DENIED) ? IDS_E_6 : IDS_E_5;
+ }
+
+ } else if (lastErr = GetLastError()) {
+
+ // error string id
+ idsErr = IDS_E_9;
+
+ // use system default language resource
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ lastErr,
+ 0,
+ (LPTSTR)&lpMessageBuffer,
+ 0,
+ NULL
+ );
+
+ }
+
+cleanup:
+
+ if (lpMessageBuffer) {
+ WarningError(Lids((WORD)(DWORD)idsErr), ServerName, lpMessageBuffer);
+ LocalFree(lpMessageBuffer);
+ } else if (idsErr) {
+ WarningError(Lids((WORD)(DWORD)idsErr), ServerName);
+ }
+
+ if (UserInfo1 != NULL)
+ NetApiBufferFree((LPVOID) UserInfo1);
+
+ CursorNormal();
+ return ret;
+
+} // NTServerValidate
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+NTServerInfoReset(
+ HWND hWnd,
+ DEST_SERVER_BUFFER *DServ,
+ BOOL ResetDomain
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ LPTSTR apiPDCName = NULL;
+ TCHAR PDCName[MAX_SERVER_NAME_LEN + 1];
+ TCHAR LocServer[MAX_SERVER_NAME_LEN + 3];
+ DOMAIN_BUFFER *DBuff;
+ TCHAR Domain[DNLEN + 1];
+ NET_API_STATUS Status = 0;
+
+ lstrcpy(PDCName, TEXT(""));
+
+ if (ServInfo) {
+ DServ->Type = ServInfo->sv101_type;
+ DServ->VerMaj = ServInfo->sv101_version_major;
+ DServ->VerMin = ServInfo->sv101_version_minor;
+ DServ->IsNTAS = IsNTAS(DServ->Name);
+ DServ->IsFPNW = IsFPNW(DServ->Name);
+
+ // If there was no old domain, don't worry about reseting it
+ if (ResetDomain && (DServ->Domain == NULL))
+ ResetDomain = FALSE;
+
+ // Check if we are a member of a domain.
+ if (ServInfo->sv101_type & (TYPE_DOMAIN)) {
+ wsprintf(LocServer, TEXT("\\\\%s"), DServ->Name);
+ Status = NetGetDCName(LocServer, NULL, (LPBYTE *) &apiPDCName);
+
+ if (!Status) {
+ // get rid of leading 2 backslashes
+ if (lstrlen(apiPDCName) > 2)
+ lstrcpy(PDCName, &apiPDCName[2]);
+
+ if (NTServerValidate(hWnd, PDCName)) {
+ DServ->IsFPNW = IsFPNW(PDCName);
+ DServ->InDomain = TRUE;
+
+ // Get Domain
+ memset(Domain, 0, sizeof(Domain));
+ NTDomainGet(DServ->Name, Domain);
+
+ if (ResetDomain) {
+ DomainListDelete(DServ->Domain);
+ DServ->Domain = NULL;
+ }
+
+ // Check if we need to add server to server list
+ DBuff = DomainListFind(Domain);
+
+ if (DBuff == NULL) {
+ DBuff = DomainListAdd(Domain, PDCName);
+ DBuff->Type = ServInfo->sv101_type;
+ DBuff->VerMaj = ServInfo->sv101_version_major;
+ DBuff->VerMin = ServInfo->sv101_version_minor;
+ }
+
+ DBuff->UseCount++;
+ DServ->Domain = DBuff;
+ } // if Domain valid
+
+ if (apiPDCName != NULL)
+ NetApiBufferFree((LPVOID) apiPDCName);
+ }
+
+ }
+
+ }
+
+ // make sure we are pointing to the right one
+ NTServerSet(DServ->Name);
+
+ // Fill in Drive Lists
+ NTDrivesEnum(&DServ->DriveList);
+
+} // NTServerInfoReset
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+NTServerInfoSet(
+ HWND hWnd,
+ LPTSTR ServerName,
+ DEST_SERVER_BUFFER *DServ
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ LPTSTR apiPDCName = NULL;
+ NET_API_STATUS Status = 0;
+
+ CursorHourGlass();
+ DServ->UseCount++;
+
+ NTServerInfoReset(hWnd, DServ, FALSE);
+
+ // Fill in share and Drive Lists
+ NTSharesEnum(&DServ->ShareList, DServ->DriveList);
+
+
+#ifdef DEBUG
+{
+ DWORD i;
+
+ dprintf(TEXT("Adding NT Server: %s\n"), DServ->Name);
+ dprintf(TEXT(" Version: %lu.%lu\n"), DServ->VerMaj, DServ->VerMin);
+
+ if (DServ->InDomain && DServ->Domain)
+ dprintf(TEXT(" In Domain: %s [\\\\%s]\n"), DServ->Domain->Name, DServ->Domain->PDCName);
+
+ dprintf(TEXT("\n"));
+ dprintf(TEXT(" Drives:\n"));
+ dprintf(TEXT(" +-------------------+\n"));
+ for (i = 0; i < DServ->DriveList->Count; i++)
+ dprintf(TEXT(" %s\n"), DServ->DriveList->DList[i].Drive);
+ dprintf(TEXT("\n"));
+
+ dprintf(TEXT(" Shares:\n"));
+ dprintf(TEXT(" +-------------------+\n"));
+ for (i = 0; i < DServ->ShareList->Count; i++)
+ dprintf(TEXT(" %s\n"), DServ->ShareList->SList[i].Name);
+ dprintf(TEXT("\n"));
+
+}
+#endif
+
+ CursorNormal();
+
+} // NTServerInfoSet
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+NTLoginTimesLog(
+ BYTE *Times
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ TCHAR *szDays[7];
+ DWORD Day;
+ DWORD Hours;
+ int Bit = 0;
+ static TCHAR szHours[80];
+
+ szDays[0] = Lids(IDS_SUN);
+ szDays[1] = Lids(IDS_MON);
+ szDays[2] = Lids(IDS_TUE);
+ szDays[3] = Lids(IDS_WED);
+ szDays[4] = Lids(IDS_THU);
+ szDays[5] = Lids(IDS_FRI);
+ szDays[6] = Lids(IDS_SAT);
+
+ LogWriteLog(1, Lids(IDS_CRLF));
+ LogWriteLog(1, Lids(IDS_L_56));
+
+ // while these should be indent 2, there isn't room on 80 cols - so indent 1
+ LogWriteLog(1, Lids(IDS_L_1));
+ LogWriteLog(1, Lids(IDS_L_2));
+ LogWriteLog(1, Lids(IDS_L_3));
+
+ for (Day = 0; Day < 7; Day++) {
+ LogWriteLog(1, szDays[Day]);
+ lstrcpy(szHours, TEXT(" "));
+
+ for (Hours = 0; Hours < 24; Hours++) {
+ if (BitTest(Bit, Times))
+ lstrcat(szHours, TEXT("**"));
+ else
+ lstrcat(szHours, TEXT(" "));
+
+ Bit++;
+
+ lstrcat(szHours, TEXT(" "));
+ }
+
+ LogWriteLog(0, szHours);
+ LogWriteLog(0, Lids(IDS_CRLF));
+ }
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+
+} // NTLoginTimesLog
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+NTUserRecLog(
+ NT_USER_INFO NT_UInfo
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ LPTSTR tmpStr;
+
+ LogWriteLog(1, Lids(IDS_L_57));
+
+ LogWriteLog(2, Lids(IDS_L_58), NT_UInfo.full_name);
+ LogWriteLog(2, Lids(IDS_L_59), NT_UInfo.password);
+
+ switch(NT_UInfo.priv) {
+ case 0:
+ tmpStr = Lids(IDS_L_60);
+ break;
+
+ case 1:
+ tmpStr = Lids(IDS_L_61);
+ break;
+
+ case 2:
+ tmpStr = Lids(IDS_L_62);
+ break;
+ }
+
+ LogWriteLog(2, Lids(IDS_L_63), tmpStr);
+
+ LogWriteLog(2, Lids(IDS_L_64), NT_UInfo.home_dir);
+ LogWriteLog(2, Lids(IDS_L_65), NT_UInfo.comment);
+
+ // Flags
+ LogWriteLog(2, Lids(IDS_L_66));
+ if (NT_UInfo.flags & 0x01)
+ LogWriteLog(3, Lids(IDS_L_67), Lids(IDS_YES));
+ else
+ LogWriteLog(3, Lids(IDS_L_67), Lids(IDS_NO));
+
+ if (NT_UInfo.flags & 0x02)
+ LogWriteLog(3, Lids(IDS_L_68), Lids(IDS_YES));
+ else
+ LogWriteLog(3, Lids(IDS_L_68), Lids(IDS_NO));
+
+ if (NT_UInfo.flags & 0x04)
+ LogWriteLog(3, Lids(IDS_L_69), Lids(IDS_YES));
+ else
+ LogWriteLog(3, Lids(IDS_L_69), Lids(IDS_NO));
+
+ if (NT_UInfo.flags & 0x08)
+ LogWriteLog(3, Lids(IDS_L_70), Lids(IDS_YES));
+ else
+ LogWriteLog(3, Lids(IDS_L_70), Lids(IDS_NO));
+
+ if (NT_UInfo.flags & 0x20)
+ LogWriteLog(3, Lids(IDS_L_71), Lids(IDS_NO));
+ else
+ LogWriteLog(3, Lids(IDS_L_71), Lids(IDS_YES));
+
+ if (NT_UInfo.flags & 0x40)
+ LogWriteLog(3, Lids(IDS_L_72), Lids(IDS_NO));
+ else
+ LogWriteLog(3, Lids(IDS_L_72), Lids(IDS_YES));
+
+ // Script path
+ LogWriteLog(2, Lids(IDS_L_73), NT_UInfo.script_path);
+
+ LogWriteLog(2, Lids(IDS_L_74), NT_UInfo.full_name);
+
+ LogWriteLog(2, Lids(IDS_L_75), NT_UInfo.logon_server);
+
+ NTLoginTimesLog((BYTE *) NT_UInfo.logon_hours);
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+
+} // NTUserRecLog
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+NTDomainSynch(
+ DEST_SERVER_BUFFER *DServ
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ LPBYTE buffer = NULL;
+ BOOL UsePDC = FALSE;
+ NET_API_STATUS Status;
+ TCHAR LocServer[MAX_SERVER_NAME_LEN + 3];
+
+ wsprintf(LocServer, TEXT("\\\\%s"), DServ->Name);
+
+ if ((DServ->InDomain) && (DServ->Domain != NULL)) {
+ wsprintf(LocServer, TEXT("\\\\%s"), DServ->Domain->PDCName);
+ UsePDC = TRUE;
+ }
+
+ if (UsePDC)
+ Status = I_NetLogonControl(LocServer, NETLOGON_CONTROL_PDC_REPLICATE, 1, &buffer);
+ else
+ Status = I_NetLogonControl(LocServer, NETLOGON_CONTROL_SYNCHRONIZE, 1, &buffer);
+
+ if (buffer != NULL)
+ NetApiBufferFree(buffer);
+
+} // NTDomainSynch
+
+
+/////////////////////////////////////////////////////////////////////////
+BOOL
+NTDomainInSynch(
+ LPTSTR Server
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ PNETLOGON_INFO_1 buffer = NULL;
+ NET_API_STATUS Status;
+
+ Status = I_NetLogonControl(Server, NETLOGON_CONTROL_QUERY, 1, (PBYTE *) &buffer);
+
+ if (Status) {
+ return TRUE;
+ }
+
+ if (buffer && buffer->netlog1_flags)
+ return FALSE;
+
+ if (buffer != NULL)
+ NetApiBufferFree(buffer);
+
+ return TRUE;
+
+} // NTDomainInSynch
+
+
+/////////////////////////////////////////////////////////////////////////
+BOOL
+NTDomainGet(
+ LPTSTR ServerName,
+ LPTSTR Domain
+ )
+
+/*++
+
+Routine Description:
+
+ Gee - what a simple way to get the domain a server is part of!
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ static TCHAR Serv[MAX_SERVER_NAME_LEN + 3];
+ UNICODE_STRING us;
+ NTSTATUS ret;
+ OBJECT_ATTRIBUTES oa;
+ ACCESS_MASK am;
+ SECURITY_QUALITY_OF_SERVICE qos;
+ LSA_HANDLE hLSA;
+ PPOLICY_PRIMARY_DOMAIN_INFO pvBuffer;
+
+ if (ServerName[0] == TEXT('\\'))
+ lstrcpy(Serv, ServerName);
+ else
+ wsprintf(Serv, TEXT("\\\\%s"), ServerName);
+
+ // Set up unicode string structure
+ us.Length = lstrlen(Serv) * sizeof(TCHAR);
+ us.MaximumLength = us.Length + sizeof(TCHAR);
+ us.Buffer = Serv;
+
+ // only need read access
+ am = POLICY_READ | POLICY_VIEW_LOCAL_INFORMATION;
+
+ // set up quality of service
+ qos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
+ qos.ImpersonationLevel = SecurityImpersonation;
+ qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ qos.EffectiveOnly = FALSE;
+
+ // Macro sets everything except security field
+ InitializeObjectAttributes( &oa, NULL, 0L, NULL, NULL );
+ oa.SecurityQualityOfService = &qos;
+
+ ret = LsaOpenPolicy(&us, &oa, am, &hLSA);
+
+ if (!ret) {
+ ret = LsaQueryInformationPolicy(hLSA, PolicyPrimaryDomainInformation, (PVOID *) &pvBuffer);
+ LsaClose(hLSA);
+ if ((!ret) && (pvBuffer != NULL)) {
+ lstrcpy(Domain, pvBuffer->Name.Buffer);
+ LsaFreeMemory((PVOID) pvBuffer);
+ }
+ }
+
+ if (ret)
+ return FALSE;
+ else
+ return TRUE;
+
+} // NTDomainGet
+
+
+/////////////////////////////////////////////////////////////////////////
+BOOL
+IsFPNW(
+ LPTSTR ServerName
+ )
+
+/*++
+
+Routine Description:
+
+ Checks the given machine for the FPNW secret.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ return (FPNWSecretGet(ServerName) != NULL);
+
+} // IsFPNW
+
+
+/////////////////////////////////////////////////////////////////////////
+BOOL
+IsNTAS(
+ LPTSTR ServerName
+ )
+
+/*++
+
+Routine Description:
+
+ Checks the given machines registry to determine if it is an NTAS
+ system. The new 'Server' type is also counted as NTAS as all we
+ use this for is to determine if local or global groups should be
+ used.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ HKEY hKey, hKey2;
+ DWORD dwType, dwSize;
+ static TCHAR LocServer[MAX_SERVER_NAME_LEN + 3];
+ static TCHAR Type[50];
+ LONG Status;
+ BOOL ret = FALSE;
+
+ wsprintf(LocServer, TEXT("\\\\%s"), ServerName);
+
+ dwSize = sizeof(Type);
+ if (RegConnectRegistry(LocServer, HKEY_LOCAL_MACHINE, &hKey) == ERROR_SUCCESS)
+ if ((Status = RegOpenKeyEx(hKey, Lids(IDS_S_12), 0, KEY_READ, &hKey2)) == ERROR_SUCCESS)
+ if ((Status = RegQueryValueEx(hKey2, Lids(IDS_S_13), NULL, &dwType, (LPBYTE) Type, &dwSize)) == ERROR_SUCCESS)
+ if (!lstrcmpi(Type, Lids(IDS_S_14)))
+ ret = TRUE;
+
+ RegCloseKey(hKey);
+ return ret;
+
+} // IsNTAS
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+NTTrustedDomainsEnum(
+ LPTSTR ServerName,
+ TRUSTED_DOMAIN_LIST **pTList
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ static TCHAR Serv[MAX_SERVER_NAME_LEN + 3];
+ TRUSTED_DOMAIN_LIST *TList = NULL;
+ UNICODE_STRING us;
+ NTSTATUS ret;
+ OBJECT_ATTRIBUTES oa;
+ ACCESS_MASK am;
+ SECURITY_QUALITY_OF_SERVICE qos;
+ LSA_HANDLE hLSA;
+ PPOLICY_PRIMARY_DOMAIN_INFO pvBuffer = NULL;
+ LSA_ENUMERATION_HANDLE lsaenumh = 0;
+ LSA_TRUST_INFORMATION *lsat;
+ ULONG maxrequested = 0xffff;
+ ULONG cItems;
+ ULONG i;
+
+ if (ServerName[0] == TEXT('\\'))
+ lstrcpy(Serv, ServerName);
+ else
+ wsprintf(Serv, TEXT("\\\\%s"), ServerName);
+
+ // Set up unicode string structure
+ us.Length = lstrlen(Serv) * sizeof(TCHAR);
+ us.MaximumLength = us.Length + sizeof(TCHAR);
+ us.Buffer = Serv;
+
+ // only need read access
+ am = POLICY_READ | POLICY_VIEW_LOCAL_INFORMATION;
+
+ // set up quality of service
+ qos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
+ qos.ImpersonationLevel = SecurityImpersonation;
+ qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ qos.EffectiveOnly = FALSE;
+
+ // Macro sets everything except security field
+ InitializeObjectAttributes( &oa, NULL, 0L, NULL, NULL );
+ oa.SecurityQualityOfService = &qos;
+
+ ret = LsaOpenPolicy(&us, &oa, am, &hLSA);
+
+ if (!ret) {
+ ret = LsaEnumerateTrustedDomains(hLSA, &lsaenumh, (PVOID *) &pvBuffer, maxrequested, &cItems);
+ LsaClose(hLSA);
+ if ((!ret) && (pvBuffer != NULL)) {
+ lsat = (LSA_TRUST_INFORMATION *) pvBuffer;
+ TList = (TRUSTED_DOMAIN_LIST *) AllocMemory(sizeof(TRUSTED_DOMAIN_LIST) + (cItems * ((MAX_DOMAIN_NAME_LEN + 1) * sizeof(TCHAR))));
+ memset(TList, 0, sizeof(TRUSTED_DOMAIN_LIST) + (cItems * ((MAX_DOMAIN_NAME_LEN + 1) * sizeof(TCHAR))));
+
+ if (TList != NULL) {
+ TList->Count = cItems;
+
+ for (i = 0; i < cItems; i++)
+ memcpy(TList->Name[i], lsat[i].Name.Buffer, lsat[i].Name.Length);
+ }
+ LsaFreeMemory((PVOID) pvBuffer);
+ }
+ }
+
+ *pTList = TList;
+
+} // NTTrustedDomainsEnum
+
+
+/////////////////////////////////////////////////////////////////////////
+DOMAIN_BUFFER *
+NTTrustedDomainSet(
+ HWND hWnd,
+ LPTSTR Server,
+ LPTSTR TrustedDomain
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ LPTSTR apiPDCName = NULL;
+ TCHAR PDCName[MAX_SERVER_NAME_LEN];
+ TCHAR LocServer[MAX_SERVER_NAME_LEN + 3];
+ static DOMAIN_BUFFER *DBuff;
+ NET_API_STATUS Status = 0;
+
+ DBuff = NULL;
+ lstrcpy(PDCName, TEXT(""));
+
+ wsprintf(LocServer, TEXT("\\\\%s"), Server);
+ Status = NetGetDCName(LocServer, TrustedDomain, (LPBYTE *) &apiPDCName);
+
+ if (!Status) {
+ // get rid of leading 2 backslashes
+ if (lstrlen(apiPDCName) > 2)
+ lstrcpy(PDCName, &apiPDCName[2]);
+
+ if (NTServerValidate(hWnd, PDCName)) {
+ // Check if we need to add domain to domain list
+ DBuff = DomainListFind(TrustedDomain);
+
+ if (DBuff == NULL) {
+ DBuff = DomainListAdd(TrustedDomain, PDCName);
+ DBuff->Type = ServInfo->sv101_type;
+ DBuff->VerMaj = ServInfo->sv101_version_major;
+ DBuff->VerMin = ServInfo->sv101_version_minor;
+ }
+
+ DBuff->UseCount++;
+ } // if Domain valid
+
+ if (apiPDCName != NULL)
+ NetApiBufferFree((LPVOID) apiPDCName);
+ }
+
+ // make sure we are pointing to the right one
+ NTServerSet(Server);
+ return DBuff;
+
+} // NTTrustedDomainSet
+
+
+/////////////////////////////////////////////////////////////////////////
+SID *
+NTSIDGet(
+ LPTSTR ServerName,
+ LPTSTR pUserName
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ static TCHAR lpszDomain[80];
+ DWORD dwDomainLength = 80;
+
+ static UCHAR psnuType[1024];
+ static SID UserSID[1024];
+ DWORD dwSIDBufSize = 1024;
+ BOOL Retry = TRUE;
+
+ // Get SID for user
+ while (Retry) {
+ if (!LookupAccountName(ServerName, pUserName, UserSID, &dwSIDBufSize,
+ lpszDomain, &dwDomainLength, (PSID_NAME_USE) psnuType)) {
+#ifdef DEBUG
+ dprintf(TEXT("Error %d: LookupAccountName\n"), GetLastError());
+#endif
+ if (GetLastError() == ERROR_NONE_MAPPED)
+ if (NTDomainInSynch(ServerName))
+ Retry = FALSE;
+ else
+ Sleep(5000L);
+
+ } else
+ return UserSID;
+ }
+
+ return NULL;
+
+} // NTSIDGet
+
+
+#define SD_SIZE (65536 + SECURITY_DESCRIPTOR_MIN_LENGTH)
+
+/////////////////////////////////////////////////////////////////////////
+BOOL
+NTFile_AccessRightsAdd(
+ LPTSTR ServerName,
+ LPTSTR pUserName,
+ LPTSTR pFileName,
+ ACCESS_MASK AccessMask,
+ BOOL Dir
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NTSTATUS ret;
+ SID *pUserSID;
+
+ // File SD variables
+ static UCHAR ucSDbuf[SD_SIZE];
+ PSECURITY_DESCRIPTOR pFileSD = (PSECURITY_DESCRIPTOR) ucSDbuf;
+ DWORD dwSDLengthNeeded = 0;
+
+ // New SD variables
+ UCHAR NewSD[SECURITY_DESCRIPTOR_MIN_LENGTH];
+ PSECURITY_DESCRIPTOR psdNewSD=(PSECURITY_DESCRIPTOR)NewSD;
+
+
+ // +-----------------------------------------------------------------+
+ // | Main Code |
+ // +-----------------------------------------------------------------+
+ pUserSID = NTSIDGet(ServerName, pUserName);
+ if (pUserSID == NULL) {
+ LogWriteLog(5, Lids(IDS_L_76), GetLastError());
+ ErrorIt(Lids(IDS_L_77), GetLastError(), pUserName);
+ return FALSE;
+ }
+
+ // Get security descriptor (SD) for file
+ if(!GetFileSecurity(pFileName, (SECURITY_INFORMATION) (DACL_SECURITY_INFORMATION),
+ pFileSD, SD_SIZE, (LPDWORD) &dwSDLengthNeeded)) {
+#ifdef DEBUG
+ dprintf(TEXT("Error %d: GetFileSecurity\n"), GetLastError());
+#endif
+ LogWriteLog(5, Lids(IDS_L_76), GetLastError());
+ ErrorIt(Lids(IDS_L_77), GetLastError(), pUserName);
+ return (FALSE);
+ }
+
+ if (Dir)
+ ret = ACEAdd(pFileSD, pUserSID, AccessMask, DirRightsMapping.NtAceFlags, &psdNewSD );
+ else
+ ret = ACEAdd(pFileSD, pUserSID, AccessMask, FileRightsMapping.NtAceFlags, &psdNewSD );
+
+ if (ret) {
+#ifdef DEBUG
+ dprintf(TEXT("Error %d: NWAddRight\n"), GetLastError());
+#endif
+ LogWriteLog(5, Lids(IDS_L_76), GetLastError());
+ ErrorIt(Lids(IDS_L_77), GetLastError(), pUserName);
+ return FALSE;
+ }
+
+ // Set the SD to the File
+ if (!SetFileSecurity(pFileName, DACL_SECURITY_INFORMATION, psdNewSD)) {
+#ifdef DEBUG
+ dprintf(TEXT("Error %d: SetFileSecurity\n"), GetLastError());
+#endif
+ LogWriteLog(5, Lids(IDS_L_76), GetLastError());
+ ErrorIt(Lids(IDS_L_77), GetLastError(), pUserName);
+
+ if (psdNewSD != pFileSD)
+ NW_FREE(psdNewSD);
+ return FALSE;
+ }
+
+ // Free the memory allocated for the new ACL, but only if
+ // it was allocated.
+ if (psdNewSD != pFileSD)
+ NW_FREE(psdNewSD);
+ return TRUE;
+
+} // NTFile_AccessRightsAdd
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+ACEAdd(
+ PSECURITY_DESCRIPTOR pSD,
+ PSID pSid,
+ ACCESS_MASK AccessMask,
+ ULONG AceFlags,
+ PSECURITY_DESCRIPTOR *ppNewSD
+ )
+
+/*++
+
+Routine Description:
+
+ ACEAdd() - Taken from ChuckC's NWRights.C
+
+Arguments:
+
+ psd - The security desciptor to modify. This must be a valid
+ security descriptor.
+
+ psid - The SID of the user/group for which we are adding this right.
+
+ AccessMask - The access mask that we wish to add.
+
+ ppNewSD - used to return the new Security descriptor.
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ ACL Acl ;
+ PACL pAcl, pNewAcl = NULL ;
+ PACCESS_ALLOWED_ACE pAccessAce, pNewAce = NULL ;
+ NTSTATUS ntstatus ;
+ BOOLEAN fDaclPresent, fDaclDefaulted;
+ BOOLEAN Found = FALSE;
+ LONG i ;
+
+ // validate and initialize
+ if (!pSD || !pSid || !ppNewSD || !RtlValidSecurityDescriptor(pSD)) {
+#ifdef DEBUG
+ dprintf(TEXT("ACEAdd: got invalid parm\n"));
+#endif
+ return (STATUS_INVALID_PARAMETER) ;
+ }
+
+ // if AccessMask == 0, no need to add the ACE
+
+ if( !AccessMask ) {
+
+ *ppNewSD = pSD;
+ return( STATUS_SUCCESS );
+ }
+
+ *ppNewSD = NULL ;
+
+ // extract the DACL from the securiry descriptor
+ ntstatus = RtlGetDaclSecurityDescriptor(pSD, &fDaclPresent, &pAcl, &fDaclDefaulted) ;
+ if (!NT_SUCCESS(ntstatus)) {
+#ifdef DEBUG
+ dprintf(TEXT("ACEAdd: RtlGetDaclSecurityDescriptor failed\n"));
+#endif
+ goto CleanupAndExit ;
+ }
+
+ // if no DACL present, we create one
+ if ((!fDaclPresent) || (pAcl == NULL)) {
+ // create Dacl
+ ntstatus = RtlCreateAcl(&Acl, sizeof(Acl), ACL_REVISION) ;
+
+ if (!NT_SUCCESS(ntstatus)) {
+#ifdef DEBUG
+ dprintf(TEXT("ACEAdd: RtlCreateAcl failed\n"));
+#endif
+ goto CleanupAndExit ;
+ }
+
+ pAcl = &Acl ;
+ }
+
+ // loop thru ACEs, looking for entry with the user/group SID
+ pAccessAce = NULL ;
+ for (i = 0; i < pAcl->AceCount; i++) {
+ ACE_HEADER *pAce ;
+
+ ntstatus = RtlGetAce(pAcl,i,&pAce) ;
+
+ if (!NT_SUCCESS(ntstatus)) {
+#ifdef DEBUG
+ dprintf(TEXT("ACEAdd: RtlGetAce failed\n"));
+#endif
+ goto CleanupAndExit ;
+ }
+
+ if (pAce->AceType == ACCESS_ALLOWED_ACE_TYPE) {
+ // found a granting ace, which is what we want
+ PSID pAceSid ;
+
+ pAccessAce = (ACCESS_ALLOWED_ACE *) pAce ;
+ pAceSid = (PSID) &pAccessAce->SidStart ;
+
+ //
+ // is this the same SID?
+ // if yes, modify access mask and carry on.
+ //
+ if (RtlEqualSid(pAceSid, pSid)) {
+
+ ACCESS_MASK access_mask ;
+
+ ASSERT(pAccessAce != NULL) ;
+
+ access_mask = pAccessAce->Mask ;
+
+ if ( (access_mask & AccessMask) == access_mask ) {
+ ntstatus = STATUS_MEMBER_IN_GROUP ;
+ goto CleanupAndExit ;
+ }
+
+ pAccessAce->Mask = AccessMask ;
+ Found = TRUE ;
+ }
+ } else {
+ // ignore it. we only deal with granting aces.
+ }
+ }
+
+ if ( !Found ) { // now set the DACL to have the desired rights
+ // reached end of ACE list without finding match. so we need to
+ // create a new ACE.
+ USHORT NewAclSize, NewAceSize ;
+
+ // calculate the sizes
+ NewAceSize = (USHORT)(sizeof(ACE_HEADER) +
+ sizeof(ACCESS_MASK) +
+ RtlLengthSid(pSid));
+
+ NewAclSize = pAcl->AclSize + NewAceSize ;
+
+ // allocate new ACE and new ACL (since we are growing it)
+ pNewAce = (PACCESS_ALLOWED_ACE) NW_ALLOC(NewAceSize) ;
+ if (!pNewAce) {
+ #ifdef DEBUG
+ dprintf(TEXT("ACEAdd: memory allocation failed for new ACE\n"));
+ #endif
+ ntstatus = STATUS_INSUFFICIENT_RESOURCES ;
+ goto CleanupAndExit ;
+ }
+
+ pNewAce->Header.AceFlags = (UCHAR)AceFlags;
+ pNewAce->Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
+ pNewAce->Header.AceSize = NewAceSize;
+ pNewAce->Mask = AccessMask;
+ RtlCopySid( RtlLengthSid(pSid), (PSID)(&pNewAce->SidStart), pSid );
+
+ pNewAcl = (PACL) NW_ALLOC(NewAclSize) ;
+ if (!pNewAcl) {
+ #ifdef DEBUG
+ dprintf(TEXT("ACEAdd: memory allocation failed for new ACL\n"));
+ #endif
+ ntstatus = STATUS_INSUFFICIENT_RESOURCES ;
+ goto CleanupAndExit ;
+ }
+
+ RtlCopyMemory(pNewAcl, pAcl, pAcl->AclSize) ;
+ pNewAcl->AclSize = NewAclSize ;
+
+ // Add the ACE to the end of the ACL
+ ntstatus = RtlAddAce(pNewAcl, ACL_REVISION, pNewAcl->AceCount, pNewAce, NewAceSize) ;
+
+ if (!NT_SUCCESS(ntstatus)) {
+ #ifdef DEBUG
+ dprintf(TEXT("ACEAdd: RtlAddAce failed\n"));
+ #endif
+ goto CleanupAndExit ;
+ }
+
+ pAcl = pNewAcl ;
+ }
+
+
+
+ // set the dacl back into the security descriptor. we need create
+ // a new security descriptor, since the old one may not have space
+ // for any additional ACE.
+ ntstatus = CreateNewSecurityDescriptor(ppNewSD, pSD, pAcl) ;
+
+ if (!NT_SUCCESS(ntstatus)) {
+#ifdef DEBUG
+ dprintf(TEXT("ACEAdd: CreateNewSecurityDescriptor failed\n"));
+#endif
+ }
+
+CleanupAndExit:
+
+ if (pNewAcl)
+ NW_FREE(pNewAcl) ;
+
+ if (pNewAce)
+ NW_FREE(pNewAce) ;
+
+ return ntstatus ;
+} // ACEAdd
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+CreateNewSecurityDescriptor(
+ PSECURITY_DESCRIPTOR *ppNewSD,
+ PSECURITY_DESCRIPTOR pSD,
+ PACL pAcl
+ )
+
+/*++
+
+Routine Description:
+
+ From a SD and a Dacl, create a new SD. The new SD will be fully self
+ contained (it is self relative) and does not have pointers to other
+ structures.
+
+Arguments:
+
+ ppNewSD - used to return the new SD. Caller should free with NW_FREE
+
+ pSD - the self relative SD we use to build the new SD
+
+ pAcl - the new DACL that will be used for the new SD
+
+Return Value:
+
+ NTSTATUS code
+
+--*/
+
+{
+ PACL pSacl ;
+ PSID psidGroup, psidOwner ;
+ BOOLEAN fSaclPresent ;
+ BOOLEAN fSaclDefaulted, fGroupDefaulted, fOwnerDefaulted ;
+ ULONG NewSDSize ;
+ SECURITY_DESCRIPTOR NewSD ;
+ PSECURITY_DESCRIPTOR pNewSD ;
+ NTSTATUS ntstatus ;
+
+
+ // extract the originals from the securiry descriptor
+ ntstatus = RtlGetSaclSecurityDescriptor(pSD, &fSaclPresent, &pSacl, &fSaclDefaulted) ;
+ if (!NT_SUCCESS(ntstatus))
+ return ntstatus ;
+
+ ntstatus = RtlGetOwnerSecurityDescriptor(pSD, &psidOwner, &fOwnerDefaulted) ;
+ if (!NT_SUCCESS(ntstatus))
+ return ntstatus ;
+
+ ntstatus = RtlGetGroupSecurityDescriptor(pSD, &psidGroup, &fGroupDefaulted) ;
+ if (!NT_SUCCESS(ntstatus))
+ return ntstatus ;
+
+ // now create a new SD and set the info in it. we cannot return this one
+ // since it has pointers to old SD.
+ ntstatus = RtlCreateSecurityDescriptor(&NewSD, SECURITY_DESCRIPTOR_REVISION) ;
+ if (!NT_SUCCESS(ntstatus))
+ return ntstatus ;
+
+ ntstatus = RtlSetDaclSecurityDescriptor(&NewSD, TRUE, pAcl, FALSE) ;
+
+ if (!NT_SUCCESS(ntstatus))
+ return ntstatus ;
+
+ ntstatus = RtlSetSaclSecurityDescriptor(&NewSD, fSaclPresent, pSacl, fSaclDefaulted) ;
+ if (!NT_SUCCESS(ntstatus))
+ return ntstatus ;
+
+ ntstatus = RtlSetOwnerSecurityDescriptor(&NewSD, psidOwner, fOwnerDefaulted) ;
+ if (!NT_SUCCESS(ntstatus))
+ return ntstatus ;
+
+ ntstatus = RtlSetGroupSecurityDescriptor(&NewSD, psidGroup, fGroupDefaulted) ;
+ if (!NT_SUCCESS(ntstatus))
+ return ntstatus ;
+
+ // calculate size needed for the returned SD and allocated it
+ NewSDSize = RtlLengthSecurityDescriptor(&NewSD) ;
+
+ pNewSD = (PSECURITY_DESCRIPTOR) NW_ALLOC(NewSDSize) ;
+
+ if (!pNewSD)
+ return (STATUS_INSUFFICIENT_RESOURCES) ;
+
+ // convert the absolute to self relative
+ ntstatus = RtlAbsoluteToSelfRelativeSD(&NewSD, pNewSD, &NewSDSize) ;
+
+ if (NT_SUCCESS(ntstatus))
+ *ppNewSD = pNewSD ;
+ else
+ NW_FREE(pNewSD) ;
+
+ return ntstatus ;
+} // CreateNewSecurityDescriptor
+
+
+/////////////////////////////////////////////////////////////////////////
+LPTSTR
+NTAccessLog(
+ ACCESS_MASK AccessMask
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ static TCHAR AccessDesc[80];
+ TCHAR AccessStr[80];
+
+ if (AccessMask == 0) {
+ lstrcpy(AccessDesc, Lids(IDS_L_78));
+ return AccessDesc;
+ } else
+ if ((AccessMask & GENERIC_ALL) == GENERIC_ALL) {
+ lstrcpy(AccessDesc, Lids(IDS_L_79));
+ return AccessDesc;
+ } else {
+ lstrcpy(AccessStr, TEXT("("));
+
+ if ((AccessMask & GENERIC_READ) == GENERIC_READ)
+ lstrcat(AccessStr, Lids(IDS_L_80));
+
+ if ((AccessMask & GENERIC_WRITE) == GENERIC_WRITE)
+ lstrcat(AccessStr, Lids(IDS_L_81));
+
+ if ((AccessMask & GENERIC_EXECUTE) == GENERIC_EXECUTE)
+ lstrcat(AccessStr, Lids(IDS_L_82));
+
+ if ((AccessMask & DELETE) == DELETE)
+ lstrcat(AccessStr, Lids(IDS_L_83));
+
+ if ((AccessMask & WRITE_DAC) == WRITE_DAC)
+ lstrcat(AccessStr, Lids(IDS_L_84));
+
+ lstrcat(AccessStr, TEXT(")"));
+
+ // Figured out the individual rights, now need to see if this corresponds
+ // to a generic mapping
+ if (!lstrcmpi(AccessStr, Lids(IDS_L_85))) {
+ lstrcpy(AccessDesc, Lids(IDS_L_86));
+ return AccessDesc;
+ }
+
+ if (!lstrcmpi(AccessStr, Lids(IDS_L_87))) {
+ lstrcpy(AccessDesc, Lids(IDS_L_88));
+ return AccessDesc;
+ }
+
+ if (!lstrcmpi(AccessStr, Lids(IDS_L_89))) {
+ lstrcpy(AccessDesc, Lids(IDS_L_90));
+ return AccessDesc;
+ }
+
+ if (!lstrcmpi(AccessStr, Lids(IDS_L_91))) {
+ lstrcpy(AccessDesc, Lids(IDS_L_92));
+ return AccessDesc;
+ }
+
+ wsprintf(AccessDesc, Lids(IDS_L_93), AccessStr);
+ }
+
+ return AccessDesc;
+
+} // NTAccessLog
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+NTUserDefaultsGet(
+ NT_DEFAULTS **UDefaults
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ USER_MODALS_INFO_0 *NTDefaults = NULL;
+ NET_API_STATUS Status = 0;
+
+ if (LocalMachine)
+ Status = NetUserModalsGet(NULL, 0, (LPBYTE *) &NTDefaults);
+ else
+ Status = NetUserModalsGet(CachedServer, 0, (LPBYTE *) &NTDefaults);
+
+ if (Status) {
+ NTDefaults = NULL;
+ return;
+ }
+
+ *UDefaults = (NT_DEFAULTS *) NTDefaults;
+} // NTUserDefaultsGet
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+NTUserDefaultsSet(
+ NT_DEFAULTS NTDefaults
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NET_API_STATUS Status = 0;
+ DWORD err;
+
+ if (LocalMachine)
+ Status = NetUserModalsSet(NULL, 0, (LPBYTE) &NTDefaults, &err);
+ else
+ Status = NetUserModalsSet(CachedServer, 0, (LPBYTE) &NTDefaults, &err);
+
+ return Status;
+
+} // NTUserDefaultsSet
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+NTUserDefaultsLog(
+ NT_DEFAULTS UDefaults
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ LogWriteLog(1, Lids(IDS_L_94), UDefaults.min_passwd_len);
+
+ // Age is in seconds, convert to days
+ LogWriteLog(1, Lids(IDS_L_95), UDefaults.max_passwd_age / 86400);
+ LogWriteLog(1, Lids(IDS_L_96), UDefaults.force_logoff);
+ LogWriteLog(0, Lids(IDS_CRLF));
+} // NTUserDefaultsLog
diff --git a/private/nw/convert/nwconv/ntnetapi.h b/private/nw/convert/nwconv/ntnetapi.h
new file mode 100644
index 000000000..b2e3c6da6
--- /dev/null
+++ b/private/nw/convert/nwconv/ntnetapi.h
@@ -0,0 +1,85 @@
+/*+-------------------------------------------------------------------------+
+ | Copyright 1993-1994 (C) Microsoft Corporation - All rights reserved. |
+ +-------------------------------------------------------------------------+*/
+
+#ifndef _HNTNETAPI_
+#define _HNTNETAPI_
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+#ifndef NTSTATUS
+typedef LONG NTSTATUS;
+#endif
+
+DWORD NTSAMConnect(LPTSTR FileServer, LPTSTR DomainName);
+void NTSAMClose();
+DWORD NTSAMParmsSet(LPTSTR ObjectName, FPNW_INFO fpnw, LPTSTR Password, BOOL ForcePasswordChange);
+DWORD NTObjectIDGet( LPTSTR ObjectName );
+
+DWORD NTShareAdd(LPTSTR ShareName, LPTSTR Path);
+DWORD FPNWShareAdd(LPTSTR ShareName, LPTSTR Path);
+void NTUseDel(LPTSTR ServerName);
+
+DWORD NTServerEnum(LPTSTR Container, SERVER_BROWSE_LIST **ServList);
+DWORD NTDomainEnum(SERVER_BROWSE_LIST **lpServList);
+DWORD NTGroupsEnum(GROUP_LIST **lpGroupList);
+DWORD NTUsersEnum(USER_LIST **lpUserList);
+
+DWORD NTGroupSave(LPTSTR Name);
+DWORD NTUserInfoSave(NT_USER_INFO *NT_UInfo, PFPNW_INFO fpnw);
+void NTServerInfoReset(HWND hWnd, DEST_SERVER_BUFFER *DServ, BOOL ResetDomain);
+DWORD NTUserInfoSet(NT_USER_INFO *NT_UInfo, PFPNW_INFO fpnw);
+DWORD NTGroupUserAdd(LPTSTR GroupName, LPTSTR UserName, BOOL Local);
+
+void NTUserRecInit(LPTSTR UserName, NT_USER_INFO *NT_UInfo);
+void NTUserRecLog(NT_USER_INFO NT_UInfo);
+
+DWORD NTSharesEnum(SHARE_LIST **lpShares, DRIVE_LIST *Drives);
+
+void NTUseDel(LPTSTR ServerName);
+void NTConnListDeleteAll();
+
+DWORD NTServerSet(LPTSTR FileServer);
+void NTServerInfoSet(HWND hWnd, LPTSTR ServerName, DEST_SERVER_BUFFER *DServ);
+BOOL NTServerValidate(HWND hWnd, LPTSTR ServerName);
+void NTServerGetInfo(LPTSTR ServerName);
+void NTDomainSynch(DEST_SERVER_BUFFER *DServ);
+BOOL NTDomainInSynch(LPTSTR Server);
+
+// #define these so they can be changed easily. these macros
+// should be used to free the memory allocated by the routines in
+// this module.
+#define NW_ALLOC(x) ((LPBYTE)LocalAlloc(LPTR,x))
+#define NW_FREE(p) ((void)LocalFree((HLOCAL)p))
+
+
+NTSTATUS NwAddRight( PSECURITY_DESCRIPTOR pSD, PSID pSid, ACCESS_MASK AccessMask, PSECURITY_DESCRIPTOR *ppNewSD ) ;
+NTSTATUS CreateNewSecurityDescriptor( PSECURITY_DESCRIPTOR *ppNewSD, PSECURITY_DESCRIPTOR pSD, PACL pAcl) ;
+
+typedef struct _TRUSTED_DOMAIN_LIST {
+ ULONG Count;
+ TCHAR Name[][MAX_DOMAIN_NAME_LEN + 1];
+} TRUSTED_DOMAIN_LIST;
+
+
+BOOL NTDomainGet(LPTSTR ServerName, LPTSTR Domain);
+BOOL IsNTAS(LPTSTR Server);
+void NTTrustedDomainsEnum(LPTSTR ServerName, TRUSTED_DOMAIN_LIST **pTList);
+DOMAIN_BUFFER *NTTrustedDomainSet(HWND hWnd, LPTSTR Server, LPTSTR TrustedDomain);
+
+SID *NTSIDGet(LPTSTR ServerName, LPTSTR pUserName);
+BOOL NTFile_AccessRightsAdd(LPTSTR ServerName, LPTSTR pUserName, LPTSTR pFileName, ACCESS_MASK AccessMask, BOOL Dir);
+LPTSTR NTAccessLog(ACCESS_MASK AccessMask);
+
+void NTUserDefaultsGet(NT_DEFAULTS **UDefaults);
+DWORD NTUserDefaultsSet(NT_DEFAULTS UDefaults);
+void NTUserDefaultsLog(NT_DEFAULTS UDefaults);
+BOOL IsFPNW(LPTSTR ServerName);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/nw/convert/nwconv/ntsecapi.h b/private/nw/convert/nwconv/ntsecapi.h
new file mode 100644
index 000000000..9db3fedcd
--- /dev/null
+++ b/private/nw/convert/nwconv/ntsecapi.h
@@ -0,0 +1,28 @@
+/*+-------------------------------------------------------------------------+
+ | Copyright 1993-1994 (C) Microsoft Corporation - All rights reserved. |
+ +-------------------------------------------------------------------------+*/
+
+#ifndef _NTSECAPI_
+#define _NTSECAPI_
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+typedef struct _TRUSTED_DOMAIN_LIST {
+ ULONG Count;
+ TCHAR Name[][MAX_DOMAIN_NAME_LEN + 1];
+} TRUSTED_DOMAIN_LIST;
+
+
+BOOL NTDomainGet(LPTSTR ServerName, LPTSTR Domain);
+BOOL IsNTAS(LPTSTR Server);
+void NTTrustedDomainsEnum(LPTSTR ServerName, TRUSTED_DOMAIN_LIST **pTList);
+SID *NTSIDGet(LPTSTR ServerName, LPTSTR pUserName);
+BOOL NTFile_AccessRightsAdd(LPTSTR ServerName, LPTSTR pUserName, LPTSTR pFileName, ULONG Rights, BOOL Dir);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/nw/convert/nwconv/nwconv.c b/private/nw/convert/nwconv/nwconv.c
new file mode 100644
index 000000000..a0dbcbbaa
--- /dev/null
+++ b/private/nw/convert/nwconv/nwconv.c
@@ -0,0 +1,1509 @@
+/*++
+
+Copyright (c) 1993-1995 Microsoft Corporation
+
+Module Name:
+
+ nwconv.c
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) 16-Jun-1994
+
+Revision History:
+
+--*/
+
+
+#include "globals.h"
+
+#include "nwconv.h"
+#include "convapi.h"
+#include "userdlg.h"
+#include "filedlg.h"
+#include "transfer.h"
+#include "columnlb.h"
+#include "ntnetapi.h"
+#include "nwnetapi.h"
+
+HINSTANCE hInst; // current instance
+
+TCHAR szAppName[] = TEXT("NWConv"); // The name of this application
+TCHAR ProgPath[MAX_PATH + 1];
+
+TCHAR NT_PROVIDER[60];
+TCHAR NW_PROVIDER[60];
+TCHAR NW_SERVICE_NAME[80];
+
+#define DEF_CONFIG_FILE TEXT("NWConv.DAT")
+
+// version as x.yz expressed as xyz (no decimal point).
+#define CONFIG_VER 026
+#define CHECK_CONST 0xA5A56572
+
+SOURCE_SERVER_BUFFER *lpSourceServer;
+DEST_SERVER_BUFFER *lpDestServer;
+
+BOOL TrialConversion = TRUE;
+BOOL IsNetWareBrowse;
+BOOL FirstTime = TRUE;
+HICON MyIcon;
+UINT NumServerPairs = 0;
+HWND hDlgMain;
+
+HHOOK hhkMsgFilter = NULL;
+UINT wHelpMessage;
+
+UINT uMenuID;
+HMENU hMenu;
+UINT uMenuFlags;
+
+#ifdef DEBUG
+int DebugFlag = 0;
+#endif
+
+CONVERT_LIST *ConvertListStart = NULL;
+CONVERT_LIST *ConvertListEnd = NULL;
+CONVERT_LIST *CurrentConvertList = NULL;
+int TotalConvertCount = 0;
+
+SOURCE_SERVER_BUFFER *SServListStart = NULL;
+SOURCE_SERVER_BUFFER *SServListEnd = NULL;
+SOURCE_SERVER_BUFFER *SServListCurrent = NULL;
+DEST_SERVER_BUFFER *DServListStart = NULL;
+DEST_SERVER_BUFFER *DServListEnd = NULL;
+DEST_SERVER_BUFFER *DServListCurrent = NULL;
+DOMAIN_BUFFER *DomainListStart = NULL;
+DOMAIN_BUFFER *DomainListEnd = NULL;
+
+BOOL SuccessfulConversion = FALSE;
+BOOL ViewLogs = FALSE;
+BOOL InConversion = FALSE;
+
+/*+-------------------------------------------------------------------------+
+ | Function Prototypes. |
+ +-------------------------------------------------------------------------+*/
+LRESULT CALLBACK DlgUsers(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
+LRESULT CALLBACK DlgMoveIt(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
+VOID AboutBox_Do(HWND hDlg);
+VOID ToggleControls(HWND hDlg, BOOL Toggle);
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID NTServInfoDlg_SwitchControls(
+ HWND hDlg,
+ BOOL Toggle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ HWND hCtrl;
+
+ // The NW Controls
+ hCtrl = GetDlgItem(hDlg, IDC_T_VOLUMES);
+ ShowWindow(hCtrl, Toggle);
+ EnableWindow(hCtrl, Toggle);
+
+ hCtrl = GetDlgItem(hDlg, IDC_LIST3);
+ ShowWindow(hCtrl, Toggle);
+ EnableWindow(hCtrl, Toggle);
+
+ // The NT Controls
+ hCtrl = GetDlgItem(hDlg, IDC_LIST1);
+ ShowWindow(hCtrl, !Toggle);
+ EnableWindow(hCtrl, !Toggle);
+
+ hCtrl = GetDlgItem(hDlg, IDC_LIST2);
+ ShowWindow(hCtrl, !Toggle);
+ EnableWindow(hCtrl, !Toggle);
+
+ hCtrl = GetDlgItem(hDlg, IDC_T_DRIVES);
+ ShowWindow(hCtrl, !Toggle);
+ EnableWindow(hCtrl, !Toggle);
+
+ hCtrl = GetDlgItem(hDlg, IDC_T_SHARES);
+ ShowWindow(hCtrl, !Toggle);
+ EnableWindow(hCtrl, !Toggle);
+
+} // NTServInfoDlg_SwitchControls
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+NTServInfoDlg_EnableNT(
+ HWND hDlg
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ TCHAR VerStr[TMP_STR_LEN_256];
+
+ SetDlgItemText(hDlg, IDC_TYPE, Lids(IDS_S_15));
+ wsprintf(VerStr, TEXT("%lu.%lu"), CurrentConvertList->FileServ->VerMaj, CurrentConvertList->FileServ->VerMin);
+ SetDlgItemText(hDlg, IDC_VERSION, VerStr);
+
+ NTServInfoDlg_SwitchControls(hDlg, FALSE);
+
+} // NTServInfoDlg_EnableNT
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+NTServInfoDlg_EnableNW(
+ HWND hDlg
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ TCHAR VerStr[TMP_STR_LEN_256];
+
+ SetDlgItemText(hDlg, IDC_TYPE, Lids(IDS_S_16));
+ wsprintf(VerStr, TEXT("%u.%u"), CurrentConvertList->SourceServ->VerMaj, CurrentConvertList->SourceServ->VerMin);
+ SetDlgItemText(hDlg, IDC_VERSION, VerStr);
+
+ NTServInfoDlg_SwitchControls(hDlg, TRUE);
+
+} // NTServInfoDlg_EnableNW
+
+
+/////////////////////////////////////////////////////////////////////////
+LRESULT CALLBACK
+NTServInfoDlg(
+ HWND hDlg,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ static TCHAR AddLine[TMP_STR_LEN_256];
+ HWND hCtrl;
+ DWORD dwData, dwIndex;
+ int wmId, wmEvent;
+ ULONG i;
+ DRIVE_LIST *DriveList;
+ SHARE_LIST *ShareList;
+
+ switch (message) {
+ case WM_INITDIALOG:
+ // Center the dialog over the application window
+ CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
+
+ // Add the servers to the combo-box and select the source server
+ hCtrl = GetDlgItem(hDlg, IDC_COMBO1);
+ dwIndex = SendMessage(hCtrl, CB_ADDSTRING, (WPARAM) 0, (LPARAM) CurrentConvertList->FileServ->Name);
+ SendMessage(hCtrl, CB_SETITEMDATA, (WPARAM) dwIndex, (LPARAM) CurrentConvertList->FileServ);
+ dwIndex = SendMessage(hCtrl, CB_ADDSTRING, (WPARAM) 0, (LPARAM) CurrentConvertList->SourceServ->Name);
+ SendMessage(hCtrl, CB_SETITEMDATA, (WPARAM) dwIndex, (LPARAM) CurrentConvertList->SourceServ);
+
+ SendMessage(hCtrl, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) CurrentConvertList->SourceServ->Name);
+
+ PostMessage(hDlg, WM_COMMAND, ID_INIT, 0L);
+ return (TRUE);
+
+ case WM_COMMAND:
+ wmId = LOWORD(wParam);
+ wmEvent = HIWORD(wParam);
+
+ switch (wmId) {
+ case IDOK:
+ EndDialog(hDlg, 0);
+ return (TRUE);
+ break;
+
+ case ID_INIT:
+ // Fill in the Drive and share lists for NT system
+ hCtrl = GetDlgItem(hDlg, IDC_LIST1);
+ DriveList = CurrentConvertList->FileServ->DriveList;
+ if (DriveList != NULL) {
+ for (i = 0; i < DriveList->Count; i++) {
+ wsprintf(AddLine, TEXT("%s: [%4s] %s"), DriveList->DList[i].Drive, DriveList->DList[i].DriveType, DriveList->DList[i].Name);
+ SendMessage(hCtrl, LB_ADDSTRING, (WPARAM) 0, (LPARAM) AddLine);
+
+ wsprintf(AddLine, Lids(IDS_S_17), lToStr(DriveList->DList[i].FreeSpace));
+ SendMessage(hCtrl, LB_ADDSTRING, (WPARAM) 0, (LPARAM) AddLine);
+ }
+ }
+
+ hCtrl = GetDlgItem(hDlg, IDC_LIST2);
+ ShareList = CurrentConvertList->FileServ->ShareList;
+ if (ShareList != NULL)
+ for (i = 0; i < ShareList->Count; i++) {
+ SendMessage(hCtrl, LB_ADDSTRING, (WPARAM) 0, (LPARAM) ShareList->SList[i].Name);
+ wsprintf(AddLine, Lids(IDS_S_18), ShareList->SList[i].Path);
+ SendMessage(hCtrl, LB_ADDSTRING, (WPARAM) 0, (LPARAM) AddLine);
+ }
+
+
+ hCtrl = GetDlgItem(hDlg, IDC_LIST3);
+ ShareList = CurrentConvertList->SourceServ->ShareList;
+ if (ShareList != NULL)
+ for (i = 0; i < ShareList->Count; i++) {
+ SendMessage(hCtrl, LB_ADDSTRING, (WPARAM) 0, (LPARAM) ShareList->SList[i].Name);
+ wsprintf(AddLine, Lids(IDS_S_19), lToStr(ShareList->SList[i].Size));
+ SendMessage(hCtrl, LB_ADDSTRING, (WPARAM) 0, (LPARAM) AddLine);
+ }
+
+
+ PostMessage(hDlg, WM_COMMAND, ID_UPDATECOMBO, 0L);
+ break;
+
+ case ID_UPDATECOMBO:
+ hCtrl = GetDlgItem(hDlg, IDC_COMBO1);
+ dwIndex = SendMessage(hCtrl, CB_GETCURSEL, 0, 0L);
+
+ if (dwIndex != CB_ERR) {
+ dwData = SendMessage(hCtrl, CB_GETITEMDATA, dwIndex, 0L);
+ if (dwData == (DWORD) CurrentConvertList->FileServ)
+ NTServInfoDlg_EnableNT(hDlg);
+
+ if (dwData == (DWORD) CurrentConvertList->SourceServ)
+ NTServInfoDlg_EnableNW(hDlg);
+
+ }
+ break;
+
+ case IDC_COMBO1:
+ if (wmEvent == CBN_SELCHANGE)
+ PostMessage(hDlg, WM_COMMAND, ID_UPDATECOMBO, 0L);
+
+ break;
+
+ }
+
+ break;
+ }
+
+ return (FALSE); // Didn't process the message
+
+ lParam;
+} // NTServInfoDlg
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+NTServInfoDlg_Do(
+ HWND hDlg
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ DLGPROC lpfnDlg;
+
+ lpfnDlg = MakeProcInstance((DLGPROC)NTServInfoDlg, hInst);
+ DialogBox(hInst, TEXT("NTServInfo"), hDlg, lpfnDlg) ;
+ FreeProcInstance(lpfnDlg);
+
+} // NTServInfoDlg_Do
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+MainListbox_Add(
+ HWND hDlg,
+ DWORD Data,
+ LPTSTR SourceServ,
+ LPTSTR DestServ
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ TCHAR AddLine[256];
+ CONVERT_LIST *ptr;
+ CONVERT_LIST *nptr = NULL;
+ DWORD dwData, ret;
+ DWORD wItemNum = 0;
+ HWND hCtrl;
+ BOOL match = FALSE;
+ ULONG nPairs;
+
+ // We want to insert this after any any other conversion to this source
+ // machine - unfortuantly it is cumbersome to do this.
+ hCtrl = GetDlgItem(hDlg, IDC_LIST1);
+
+ // start count at one less as it will always be one ahead of us
+ nPairs = SendMessage(hCtrl, LB_GETCOUNT, 0, 0L);
+
+ // Try to find a matching destination server for this in the listbox
+ ptr = (CONVERT_LIST *) Data;
+ while((wItemNum < nPairs) && !match) {
+ dwData = ColumnLB_GetItemData(hCtrl, wItemNum);
+ if (dwData != LB_ERR) {
+ nptr = (CONVERT_LIST *) dwData;
+ if (!lstrcmpi(ptr->FileServ->Name, nptr->FileServ->Name))
+ match = TRUE;
+ }
+
+ if (!match)
+ wItemNum++;
+ }
+
+ if (match) {
+ // have a match - so go to the end of the matching servers...
+ while((wItemNum < nPairs) && match) {
+ dwData = ColumnLB_GetItemData(hCtrl, wItemNum);
+ if (dwData != LB_ERR) {
+ nptr = (CONVERT_LIST *) dwData;
+ if (lstrcmpi(ptr->FileServ->Name, nptr->FileServ->Name))
+ match = FALSE;
+ }
+
+ if (match)
+ wItemNum++;
+ }
+
+ } else {
+ if (ptr->FileServ->InDomain && ptr->FileServ->Domain) {
+ wItemNum = 0;
+
+ // No matching servers, so try to find matching domain
+ while((wItemNum < nPairs) && !match) {
+ dwData = ColumnLB_GetItemData(hCtrl, wItemNum);
+ if (dwData != LB_ERR) {
+ nptr = (CONVERT_LIST *) dwData;
+
+ if (nptr->FileServ->InDomain && nptr->FileServ->Domain)
+ if (!lstrcmpi(ptr->FileServ->Domain->Name, nptr->FileServ->Domain->Name))
+ match = TRUE;
+ }
+
+ if (!match)
+ wItemNum++;
+ }
+
+ if (match) {
+ // have a match - so go to the end of the matching domain...
+ while((wItemNum < nPairs) && match) {
+ dwData = ColumnLB_GetItemData(hCtrl, wItemNum);
+ if (dwData != LB_ERR) {
+ nptr = (CONVERT_LIST *) dwData;
+
+ if (nptr->FileServ->InDomain && nptr->FileServ->Domain) {
+ if (lstrcmpi(ptr->FileServ->Domain->Name, nptr->FileServ->Domain->Name))
+ match = FALSE;
+ } else
+ match = FALSE;
+ }
+
+ if (match)
+ wItemNum++;
+ }
+ }
+ } // if domain
+ }
+
+ wsprintf(AddLine, TEXT("%s\t%s\t"), SourceServ, DestServ);
+
+ wItemNum = ColumnLB_InsertString(hCtrl, wItemNum, AddLine);
+ ret = ColumnLB_SetItemData(hCtrl, wItemNum, Data);
+ ColumnLB_SetCurSel(hCtrl, wItemNum);
+
+} // MainListbox_Add
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ConfigurationReset(
+ HWND hDlg
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ // Remove the listbox entries
+ ColumnLB_ResetContent(GetDlgItem(hDlg, IDC_LIST1));
+
+ ToggleControls(hDlg, FALSE);
+ SetFocus(GetDlgItem(hDlg, IDC_ADD));
+
+ ConvertListDeleteAll();
+ UserOptionsDefaultsReset();
+ FileOptionsDefaultsReset();
+ LogOptionsInit();
+
+} // ConfigurationReset
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ConfigurationSave(
+ LPTSTR FileName
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ DWORD wrote;
+ HANDLE hFile;
+ CHAR FileNameA[MAX_PATH + 1];
+ DWORD Check, Ver;
+
+ WideCharToMultiByte(CP_ACP, 0, FileName, -1, FileNameA, sizeof(FileNameA), NULL, NULL);
+
+ // Create it no matter what
+ hFile = CreateFileA( FileNameA, GENERIC_WRITE, 0,
+ NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
+
+ if (hFile != INVALID_HANDLE_VALUE) {
+ // Save out our check value and the version info
+ Check = CHECK_CONST;
+ WriteFile(hFile, &Check, sizeof(Check), &wrote, NULL);
+ Ver = CONFIG_VER;
+ WriteFile(hFile, &Ver, sizeof(Ver), &wrote, NULL);
+
+ // Save global log file options
+ LogOptionsSave(hFile);
+
+ // Save out convert Lists
+ ConvertListSaveAll(hFile);
+
+ }
+
+ if (hFile != INVALID_HANDLE_VALUE)
+ CloseHandle(hFile);
+
+ return;
+
+} // ConfigurationSave
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ConfigurationLoad(
+ HWND hDlg,
+ LPTSTR FileName
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ static TCHAR AddLine[256];
+ DWORD wrote;
+ HANDLE hFile;
+ CHAR FileNameA[MAX_PATH + 1];
+ DWORD Check, Ver;
+
+ WideCharToMultiByte(CP_ACP, 0, FileName, -1, FileNameA, sizeof(FileNameA), NULL, NULL);
+
+ // Open, but fail if already exists.
+ hFile = CreateFileA( FileNameA, GENERIC_READ, 0,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
+
+ if (hFile != INVALID_HANDLE_VALUE) {
+ ConfigurationReset(hDlg);
+ ReadFile(hFile, &Check, sizeof(Check), &wrote, NULL);
+
+ if (Check != CHECK_CONST) {
+ CloseHandle(hFile);
+ ErrorBox(Lids(IDS_E_10));
+ return;
+ }
+
+ ReadFile(hFile, &Ver, sizeof(Ver), &wrote, NULL);
+
+ if (Ver != CONFIG_VER) {
+ CloseHandle(hFile);
+ ErrorBox(Lids(IDS_E_11));
+ return;
+ }
+
+ LogOptionsLoad(hFile);
+
+ // Load in convert list and all associated info...
+ ConvertListLoadAll(hFile);
+
+ // Everything from the file is loaded in - but now the painful part
+ // begins. We need to take the following steps:
+ //
+ // 1. Walk all lists and refix pointers from their index
+ // 2. Re-Validate servers, shares, domains, etc. to make sure they
+ // haven't changed underneath us since we saved out the file.
+ // 3. Re-create info that wasn't saved out (like drive lists).
+
+ // 1. Walk and refix lists
+ ConvertListFixup(hDlg);
+
+ // Now add them to the listbox
+ CurrentConvertList = ConvertListStart;
+ while (CurrentConvertList) {
+ MainListbox_Add(hDlg, (DWORD) CurrentConvertList, CurrentConvertList->SourceServ->Name, CurrentConvertList->FileServ->Name);
+ CurrentConvertList = CurrentConvertList->next;
+ }
+
+ // Re-enable all the toggles
+ if (NumServerPairs)
+ PostMessage(hDlg, WM_COMMAND, (WPARAM) IDM_ADDSEL, 0);
+
+ }
+
+ if (hFile != INVALID_HANDLE_VALUE)
+ CloseHandle(hFile);
+
+ return;
+
+} // ConfigurationLoad
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+CanonServerName(
+ LPTSTR ServerName
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ LPTSTR TmpStr = ServerName;
+
+ while (*TmpStr == TEXT('\\'))
+ TmpStr++;
+
+ lstrcpy(ServerName, TmpStr);
+
+} // CanonServerName
+
+
+/////////////////////////////////////////////////////////////////////////
+int
+MessageFilter(
+ INT nCode,
+ WPARAM wParam,
+ LPMSG lpMsg
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ if (nCode < 0)
+ goto DefHook;
+
+ if (nCode == MSGF_MENU) {
+
+ if (lpMsg->message == WM_KEYDOWN && lpMsg->wParam == VK_F1) {
+ // Window of menu we want help for is in loword of lParam.
+
+ PostMessage(hDlgMain, wHelpMessage, MSGF_MENU, (LPARAM)lpMsg->hwnd);
+ return 1;
+ }
+
+ }
+ else
+ if (nCode == MSGF_DIALOGBOX) {
+
+ if (lpMsg->message == WM_KEYDOWN && lpMsg->wParam == VK_F1) {
+ // Dialog box we want help for is in loword of lParam
+
+ PostMessage(hDlgMain, wHelpMessage, MSGF_DIALOGBOX, (LPARAM)lpMsg->hwnd);
+ return 1;
+ }
+
+ } else
+
+DefHook:
+ return (INT)DefHookProc(nCode, wParam, (DWORD)lpMsg, &hhkMsgFilter);
+
+ return 0;
+} // MessageFilter
+
+
+/////////////////////////////////////////////////////////////////////////
+int APIENTRY
+WinMain(
+ HINSTANCE hInstance,
+ HINSTANCE hPrevInstance,
+ LPSTR lpCmdLine,
+ int nCmdShow
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ LPTSTR ptr;
+ DLGPROC lpproc;
+ HACCEL haccel;
+ MSG msg;
+
+ hInst = hInstance;
+
+ if (!hPrevInstance) {
+ BookTab_Initialize(hInst);
+ ColumnLBClass_Register(hInst);
+ }
+
+ MultiByteToWideChar(CP_ACP, 0, _pgmptr, -1, ProgPath, sizeof(ProgPath) );
+
+ // go to the end and rewind to remove program name
+ ptr = ProgPath;
+ while (*ptr)
+ ptr++;
+
+ while (*ptr != TEXT('\\'))
+ ptr--;
+
+ ptr++;
+
+ *ptr = TEXT('\0');
+
+ MemInit();
+ MyIcon = LoadIcon(hInst, szAppName);
+
+ lpproc = MakeProcInstance((DLGPROC) DlgMoveIt, hInst);
+ hDlgMain = CreateDialog(hInst, szAppName, NULL, lpproc);
+ wHelpMessage = RegisterWindowMessage(TEXT("ShellHelp"));
+ haccel = LoadAccelerators(hInst, TEXT("MainAcc"));
+ hhkMsgFilter = SetWindowsHook(WH_MSGFILTER, (HOOKPROC)MessageFilter);
+
+ while (GetMessage(&msg, NULL, 0, 0)) {
+ if (!TranslateAccelerator(hDlgMain, haccel, &msg))
+ if ((hDlgMain == 0) || !IsDialogMessage(hDlgMain, &msg)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+
+ FreeProcInstance(lpproc);
+
+ ColumnLBClass_Unregister(hInst);
+ DestroyIcon(MyIcon);
+ StringTableDestroy();
+
+ return msg.wParam;
+
+} // WinMain
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+ToggleControls(
+ HWND hDlg,
+ BOOL Toggle
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ HWND hCtrl;
+
+ hCtrl = GetDlgItem(hDlg, IDOK);
+ EnableWindow(hCtrl, Toggle);
+ hCtrl = GetDlgItem(hDlg, IDC_TRIAL);
+ EnableWindow(hCtrl, Toggle);
+ hCtrl = GetDlgItem(hDlg, IDC_DELETE);
+ EnableWindow(hCtrl, Toggle);
+ hCtrl = GetDlgItem(hDlg, IDC_USERINF);
+ EnableWindow(hCtrl, Toggle);
+
+ hCtrl = GetDlgItem(hDlg, IDC_FILEINF);
+ EnableWindow(hCtrl, Toggle);
+
+} // ToggleControls
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+ConfigFileGet(
+ HWND hwnd
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ OPENFILENAME ofn;
+ TCHAR szDirName[MAX_PATH+1];
+ TCHAR szFile[256], szFileTitle[256];
+ UINT i, cbString;
+ TCHAR chReplace;
+ TCHAR szFilter[256];
+ LPTSTR szExt = TEXT("CNF");
+
+ lstrcpy(szDirName, ProgPath);
+ lstrcpy(szFile, TEXT(""));
+
+ if ((cbString = LoadString(hInst, IDS_MAINFILTERSTRING, szFilter, sizeof(szFilter))) == 0) {
+ // Error occured
+ return 1L;
+ }
+
+ chReplace = szFilter[cbString - 1]; // Retrieve wild character
+
+ for (i = 0; szFilter[i] != TEXT('\0'); i++) {
+ if (szFilter[i] == chReplace)
+ szFilter[i] = TEXT('\0');
+ }
+
+ // Set all structure members to zero
+ memset(&ofn, 0, sizeof(OPENFILENAME));
+
+ ofn.lStructSize = sizeof(OPENFILENAME);
+ ofn.hwndOwner = hwnd;
+ ofn.lpstrFilter = szFilter;
+ ofn.nFilterIndex = 1;
+ ofn.lpstrFile = szFile;
+ ofn.nMaxFile = sizeof(szFile);
+ ofn.lpstrFileTitle = szFileTitle;
+ ofn.nMaxFileTitle = sizeof(szFileTitle);
+ ofn.lpstrInitialDir = szDirName;
+ ofn.lpstrDefExt = szExt;
+ ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_NOCHANGEDIR;
+
+ if (GetOpenFileName(&ofn)) {
+ // Load the configuration
+ ConfigurationLoad(hwnd, ofn.lpstrFile);
+ return 0L;
+ } else {
+ // Couldn't open the dang file
+ return 1L;
+ }
+
+} // ConfigFileGet
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+ConfigFileSave(
+ HWND hwnd
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ OPENFILENAME ofn;
+ TCHAR szDirName[MAX_PATH + 1];
+ TCHAR szFile[MAX_PATH + 1], szFileTitle[MAX_PATH + 1];
+ UINT i, cbString;
+ TCHAR chReplace;
+ TCHAR szFilter[256];
+ LPTSTR szExt;
+
+ szExt = Lids(IDS_S_20);
+
+ lstrcpy(szDirName, ProgPath);
+ lstrcpy(szFile, TEXT(""));
+
+ if ((cbString = LoadString(hInst, IDS_MAINFILTERSTRING, szFilter, sizeof(szFilter))) == 0) {
+ // Error occured
+ return 1L;
+ }
+
+ chReplace = szFilter[cbString - 1]; // Retrieve wild character
+
+ for (i = 0; szFilter[i] != TEXT('\0'); i++) {
+ if (szFilter[i] == chReplace)
+ szFilter[i] = TEXT('\0');
+ }
+
+ // Set all structure members to zero
+ memset(&ofn, 0, sizeof(OPENFILENAME));
+
+ ofn.lStructSize = sizeof(OPENFILENAME);
+ ofn.hwndOwner = hwnd;
+ ofn.lpstrFilter = szFilter;
+ ofn.nFilterIndex = 1;
+ ofn.lpstrFile = szFile;
+ ofn.nMaxFile = sizeof(szFile);
+ ofn.lpstrFileTitle = szFileTitle;
+ ofn.nMaxFileTitle = sizeof(szFileTitle);
+ ofn.lpstrInitialDir = szDirName;
+ ofn.lpstrDefExt = szExt;
+ ofn.Flags = OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY | OFN_NOCHANGEDIR | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
+
+ if (GetSaveFileName(&ofn)) {
+ // Save Configuration
+ ConfigurationSave( ofn.lpstrFile);
+ return 0L;
+ } else {
+ // Couldn't save it
+ return 1L;
+ }
+
+} // ConfigFileSave
+
+
+/////////////////////////////////////////////////////////////////////////
+BOOL
+ProvidersInit()
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ HKEY hKey;
+ DWORD dwType, dwSize;
+ LONG Status;
+ BOOL ret = FALSE;
+
+ dwSize = sizeof(NW_PROVIDER);
+ if ((Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, Lids(IDS_S_23), 0, KEY_READ, &hKey)) == ERROR_SUCCESS)
+ if ((Status = RegQueryValueEx(hKey, Lids(IDS_S_21), NULL, &dwType, (LPBYTE) NW_PROVIDER, &dwSize)) == ERROR_SUCCESS)
+ ret = TRUE;
+
+ RegCloseKey(hKey);
+
+ if (ret) {
+ ret = FALSE;
+ hKey = 0;
+ dwSize = sizeof(NT_PROVIDER);
+ if ((Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, Lids(IDS_S_25), 0, KEY_READ, &hKey)) == ERROR_SUCCESS)
+ if ((Status = RegQueryValueEx(hKey, Lids(IDS_S_21), NULL, &dwType, (LPBYTE) NT_PROVIDER, &dwSize)) == ERROR_SUCCESS)
+ ret = TRUE;
+
+ RegCloseKey(hKey);
+ }
+
+ if (ret) {
+ ret = FALSE;
+ hKey = 0;
+ dwSize = sizeof(NW_SERVICE_NAME);
+ if ((Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, Lids(IDS_S_24), 0, KEY_READ, &hKey)) == ERROR_SUCCESS)
+ if ((Status = RegQueryValueEx(hKey, Lids(IDS_S_22), NULL, &dwType, (LPBYTE) Lids(IDS_S_24), &dwSize)) == ERROR_SUCCESS)
+ ret = TRUE;
+
+ RegCloseKey(hKey);
+ }
+
+ return ret;
+
+} // ProvidersInit
+
+
+/////////////////////////////////////////////////////////////////////////
+BOOL
+CheckServiceInstall()
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ SC_HANDLE hSC;
+ DWORD dwBytesNeeded, dwNumEntries, dwhResume;
+ ENUM_SERVICE_STATUS *lpStatus = NULL;
+ BOOL ret = FALSE;
+
+ if (!ProvidersInit())
+ return FALSE;
+
+ // initialize variables for enumeration...
+ dwBytesNeeded = dwNumEntries = dwhResume = 0;
+
+ // acquire handle to svc controller to query for netware client...
+ if (hSC = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE)) {
+
+ UINT i;
+ LPTSTR lpServiceName = Lids(IDS_S_26); // NWCWorkstation
+
+ // ask for buffer size...
+ ret = EnumServicesStatus(
+ hSC,
+ SERVICE_WIN32,
+ SERVICE_ACTIVE,
+ NULL,
+ 0,
+ &dwBytesNeeded,
+ &dwNumEntries,
+ &dwhResume
+ );
+
+ // intentionally called function with no buffer to size...
+ if ((ret == FALSE) && (GetLastError() == ERROR_MORE_DATA)) {
+
+ // allocate buffer with size passed back...
+ if (lpStatus = AllocMemory(dwBytesNeeded)) {
+
+ // ask for svc entries...
+ if (EnumServicesStatus(
+ hSC,
+ SERVICE_WIN32,
+ SERVICE_ACTIVE,
+ lpStatus,
+ dwBytesNeeded,
+ &dwBytesNeeded,
+ &dwNumEntries,
+ &dwhResume)) {
+
+ // search service names for match...
+ for (i = 0; ((i < dwNumEntries) && (ret == FALSE)); i++) {
+ if (!lstrcmpi(lpStatus[i].lpServiceName, lpServiceName)) {
+ ret = TRUE; // found it...
+ }
+ }
+ }
+
+ FreeMemory(lpStatus);
+ }
+ }
+
+ CloseServiceHandle(hSC);
+ }
+
+ return ret;
+
+} // CheckServiceInstall
+
+
+/////////////////////////////////////////////////////////////////////////
+LRESULT CALLBACK
+DlgMoveIt(
+ HWND hDlg,
+ UINT message,
+ WPARAM wParam,
+ LPARAM lParam
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ static TCHAR AddLine[256];
+ int wmId, wmEvent;
+ HWND hCtrl;
+ PAINTSTRUCT ps;
+ HDC hDC;
+ RECT rc;
+ DWORD Index;
+ DWORD dwData;
+ int TabStop;
+
+ switch (message) {
+ case WM_INITDIALOG:
+
+ ConvertListStart = ConvertListEnd = NULL;
+ UserOptionsDefaultsReset();
+ FileOptionsDefaultsReset();
+ LogOptionsInit();
+
+ // Disable controls until server pair is choosen...
+ ToggleControls(hDlg, FALSE);
+
+ hCtrl = GetDlgItem(hDlg, IDC_LIST1);
+ GetClientRect(hCtrl, &rc);
+
+ // Size is half width of listbox - vertical scrollbar
+ TabStop = (((rc.right - rc.left) - GetSystemMetrics(SM_CXVSCROLL)) / 2);
+ ColumnLB_SetNumberCols(hCtrl, 2);
+ ColumnLB_SetColTitle(hCtrl, 0, Lids(IDS_D_11));
+ ColumnLB_SetColTitle(hCtrl, 1, Lids(IDS_D_12));
+ ColumnLB_SetColWidth(hCtrl, 0, TabStop);
+ // Calculate 2nd this way instead of just TabStop to get rid of roundoff
+ ColumnLB_SetColWidth(hCtrl, 1, (rc.right - rc.left) - TabStop);
+
+ // This is needed as otherwise only the Add box will display - weird...
+ ShowWindow(hDlg, SW_SHOWNORMAL);
+
+ // Check if NWCS is installed
+ PostMessage(hDlg, WM_COMMAND, ID_INIT, 0L);
+
+ break;
+
+ case WM_ERASEBKGND:
+
+ // Process so icon background isn't painted grey - main dlg
+ // can't be DS_MODALFRAME either, or else a frame is painted around
+ // the icon.
+ if (IsIconic(hDlg))
+ return TRUE;
+
+ break;
+
+ case WM_DESTROY:
+ NTConnListDeleteAll();
+ PostQuitMessage(0);
+ break;
+
+ case WM_PAINT:
+ hDC = BeginPaint(hDlg, &ps);
+ if (IsIconic(hDlg)) {
+ GetClientRect(hDlg, &rc);
+ DrawIcon(hDC, rc.left, rc.top, MyIcon);
+ }
+
+ EndPaint(hDlg, &ps);
+ break;
+
+ case WM_COMMAND:
+ wmId = LOWORD(wParam);
+ wmEvent = HIWORD(wParam);
+
+ // If we are currently doing a conversion then get out
+ if (InConversion)
+ break;
+
+ switch (wmId) {
+ case IDOK:
+ InConversion = TRUE;
+ DoConversion(hDlg, FALSE);
+ InConversion = FALSE;
+
+ if (ConversionSuccessful()) {
+ ConfigurationReset(hDlg);
+ DeleteFile(DEF_CONFIG_FILE);
+ }
+
+ break;
+
+ case ID_INIT:
+ CursorHourGlass();
+ if (!CheckServiceInstall()) {
+ CursorNormal();
+ WarningError(Lids(IDS_E_12));
+ PostMessage(hDlg, WM_DESTROY, 0, 0);
+ } else {
+
+ ConfigurationLoad(hDlg, DEF_CONFIG_FILE);
+ CursorNormal();
+
+ if (!NumServerPairs) {
+ // Put up the add dialog box
+ if (FirstTime) {
+ FirstTime = FALSE;
+ PostMessage(hDlg, WM_COMMAND, IDC_ADD, 0);
+ }
+ }
+
+ }
+
+ break;
+
+ case IDC_TRIAL:
+ InConversion = TRUE;
+ DoConversion(hDlg, TRUE);
+ InConversion = FALSE;
+ break;
+
+ case IDCANCEL:
+ case IDC_EXIT:
+ CursorHourGlass();
+
+ if (NumServerPairs)
+ ConfigurationSave(DEF_CONFIG_FILE);
+ else {
+ DeleteFile(DEF_CONFIG_FILE);
+ }
+
+ ConfigurationReset(hDlg);
+ CursorNormal();
+ PostMessage(hDlg, WM_DESTROY, 0, 0);
+ break;
+
+ case ID_FILE_OPEN:
+ ConfigFileGet(hDlg);
+ break;
+
+ case ID_FILE_SAVE:
+ ConfigFileSave(hDlg);
+ break;
+
+ case ID_FILE_DEFAULT:
+ if (MessageBox(hDlg, Lids(IDS_RESTOREDEFAULTS), Lids(IDS_TXTWARNING), MB_OKCANCEL | MB_ICONEXCLAMATION) == IDOK) {
+ // Remove the listbox entries
+ hCtrl = GetDlgItem(hDlg, IDC_LIST1);
+ ColumnLB_ResetContent(hCtrl);
+
+ ToggleControls(hDlg, FALSE);
+
+ ConvertListDeleteAll();
+ UserOptionsDefaultsReset();
+ FileOptionsDefaultsReset();
+ ViewLogs = FALSE;
+ }
+ break;
+
+ case ID_LOGGING:
+ DoLoggingDlg(hDlg);
+ return TRUE;
+
+ break;
+
+ case IDC_USERINF:
+ // Figure out which server pair is selected and pass server pair to user config dialog
+ hCtrl = GetDlgItem(hDlg, IDC_LIST1);
+ Index = ColumnLB_GetCurSel(hCtrl);
+ dwData = ColumnLB_GetItemData(hCtrl, Index);
+ CurrentConvertList = (CONVERT_LIST *) dwData;
+
+ UserOptions_Do(hDlg, CurrentConvertList->ConvertOptions, CurrentConvertList->SourceServ, CurrentConvertList->FileServ);
+ return TRUE;
+
+ case IDC_FILEINF:
+ // Figure out which server pair is selected and pass server pair to file config dialog
+ hCtrl = GetDlgItem(hDlg, IDC_LIST1);
+ Index = ColumnLB_GetCurSel(hCtrl);
+ dwData = ColumnLB_GetItemData(hCtrl, Index);
+ CurrentConvertList = (CONVERT_LIST *) dwData;
+
+ FileOptions_Do(hDlg, CurrentConvertList->FileOptions, CurrentConvertList->SourceServ, CurrentConvertList->FileServ);
+ break;
+
+ case IDHELP:
+ WinHelp(hDlg, HELP_FILE, HELP_CONTEXT, (DWORD) IDC_HELP_MAIN);
+ break;
+
+ case ID_HELP_CONT:
+ WinHelp(hDlg, HELP_FILE, HELP_CONTENTS, 0L);
+ break;
+
+ case ID_HELP_INDEX:
+ WinHelp(hDlg, HELP_FILE, HELP_PARTIALKEY, (DWORD) TEXT("\0"));
+ break;
+
+ case ID_HELP_USING:
+ WinHelp(hDlg, HELP_FILE, HELP_HELPONHELP, (DWORD) TEXT("\0"));
+ break;
+
+ case IDC_ADD:
+ if (!DialogServerBrowse(hInst, hDlg, &lpSourceServer, &lpDestServer)) {
+ dwData = (DWORD) ConvertListAdd(lpSourceServer, lpDestServer);
+ MainListbox_Add(hDlg, dwData, lpSourceServer->Name, lpDestServer->Name);
+ PostMessage(hDlg, WM_COMMAND, (WPARAM) IDM_ADDSEL, 0);
+ }
+
+ return TRUE;
+
+ case IDC_DELETE:
+ hCtrl = GetDlgItem(hDlg, IDC_LIST1);
+ Index = ColumnLB_GetCurSel(hCtrl);
+
+ if (Index != LB_ERR) {
+ dwData = ColumnLB_GetItemData(hCtrl, Index);
+ ConvertListDelete((CONVERT_LIST *) dwData);
+ ColumnLB_DeleteString(hCtrl, Index);
+ }
+
+ if (!NumServerPairs) {
+ hCtrl = GetDlgItem(hDlg, IDC_ADD);
+ SetFocus(hCtrl);
+ ToggleControls(hDlg, FALSE);
+ UserOptionsDefaultsReset();
+ FileOptionsDefaultsReset();
+
+ } else {
+ Index = ColumnLB_GetCurSel(hCtrl);
+
+ if (Index == LB_ERR)
+ ColumnLB_SetCurSel(hCtrl, 0);
+ }
+
+ break;
+
+ case IDM_ADDSEL:
+ ToggleControls(hDlg, TRUE);
+ break;
+
+ case IDC_LIST1:
+ if (wmEvent == LBN_SELCHANGE) {
+ if (NumServerPairs)
+ ToggleControls(hDlg, TRUE);
+ } else
+ if (wmEvent == LBN_DBLCLK) {
+ hCtrl = GetDlgItem(hDlg, IDC_LIST1);
+ Index = ColumnLB_GetCurSel(hCtrl);
+
+ if (Index != LB_ERR) {
+ dwData = ColumnLB_GetItemData(hCtrl, Index);
+ if (dwData != 0) {
+ CurrentConvertList = (CONVERT_LIST *) dwData;
+
+ NTServInfoDlg_Do(hDlg);
+ }
+ }
+ }
+ break;
+
+ case ID_APP_ABOUT:
+ AboutBox_Do(hDlg);
+ return TRUE;
+
+ }
+
+ break;
+
+ case WM_MENUSELECT:
+ // when a menu is selected we must remember which one it was so that
+ // when F1 is pressed we know what help to bring up.
+ if (GET_WM_MENUSELECT_HMENU(wParam, lParam)) {
+
+ // Save the menu the user selected
+ uMenuID = GET_WM_MENUSELECT_CMD(wParam, lParam);
+ uMenuFlags = GET_WM_MENUSELECT_FLAGS(wParam, lParam);
+ hMenu = GET_WM_MENUSELECT_HMENU(wParam, lParam);
+ }
+
+ break;
+
+ default:
+ if (message == wHelpMessage) {
+
+ if (GET_WM_COMMAND_ID(wParam, lParam) == MSGF_MENU) {
+ // Get outta menu mode if help for a menu item
+ if (uMenuID && hMenu) {
+ // save and restore menu vars so they aren't overwritten by
+ // the message we are sending
+ UINT m = uMenuID;
+ HMENU hM = hMenu;
+ UINT mf = uMenuFlags;
+
+ SendMessage(hDlg, WM_CANCELMODE, 0, 0L);
+
+ uMenuID = m;
+ hMenu = hM;
+ uMenuFlags = mf;
+ }
+
+ if (!(uMenuFlags & MF_POPUP)) {
+ switch(uMenuID) {
+ case ID_FILE_OPEN:
+ WinHelp(hDlg, HELP_FILE, HELP_CONTEXT, (DWORD) IDM_HELP_RCONFIG);
+ break;
+
+ case ID_FILE_SAVE:
+ WinHelp(hDlg, HELP_FILE, HELP_CONTEXT, (DWORD) IDM_HELP_SCONFIG);
+ break;
+
+ case ID_FILE_DEFAULT:
+ WinHelp(hDlg, HELP_FILE, HELP_CONTEXT, (DWORD) IDM_HELP_RDCONFIG);
+ break;
+
+ case IDC_EXIT:
+ WinHelp(hDlg, HELP_FILE, HELP_CONTEXT, (DWORD) IDM_HELP_EXIT);
+ break;
+
+ }
+
+#ifdef fooo
+ // According to winhelp: GetSystemMenu, uMenuID >= 0x7000
+ // means system menu items!
+ //
+ // This should not be nec since MF_SYSMENU is set!
+ if (uMenuFlags & MF_SYSMENU || uMenuID >= 0xf000)
+ dwContext = bMDIFrameSysMenu ? IDH_SYSMENU : IDH_SYSMENUCHILD;
+
+ WFHelp(hwnd);
+#endif
+ }
+
+ }
+#ifdef fooo
+ else if (GET_WM_COMMAND_ID(wParam, lParam) == MSGF_DIALOGBOX) {
+
+ // context range for message boxes
+ if (dwContext >= IDH_MBFIRST && dwContext <= IDH_MBLAST)
+ WFHelp(hwnd);
+ else
+ // let dialog box deal with it
+ PostMessage(GetRealParent((HWND)lParam), wHelpMessage, 0, 0L);
+ }
+#endif
+
+ }
+
+ break;
+ }
+
+
+ return (FALSE); // Didn't process the message
+
+} // DlgMoveIt
diff --git a/private/nw/convert/nwconv/nwconv.h b/private/nw/convert/nwconv/nwconv.h
new file mode 100644
index 000000000..85a3212d5
--- /dev/null
+++ b/private/nw/convert/nwconv/nwconv.h
@@ -0,0 +1,25 @@
+/*+-------------------------------------------------------------------------+
+ | Copyright 1993-1994 (C) Microsoft Corporation - All rights reserved. |
+ +-------------------------------------------------------------------------+*/
+
+#ifndef _HNWCONV_
+#define _HNWCONV_
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+extern TCHAR NT_PROVIDER[];
+extern TCHAR NW_PROVIDER[];
+
+// Common utility routines
+void CanonServerName(LPTSTR ServerName);
+
+extern HINSTANCE hInst; // current instance
+extern TCHAR ProgPath[];
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/nw/convert/nwconv/nwconv.rc b/private/nw/convert/nwconv/nwconv.rc
new file mode 100644
index 000000000..324fed0e0
--- /dev/null
+++ b/private/nw/convert/nwconv/nwconv.rc
@@ -0,0 +1,954 @@
+
+#include <windows.h>
+#include "resource.h"
+#include "strings.h"
+#include "columnlb.h"
+
+// version info
+#include "version.h"
+
+NWConv ICON moveit.ico
+IDR_LISTICONS BITMAP hiericon.bmp
+IDR_FILEICONS BITMAP fileicon.bmp
+IDR_CHECKICONS BITMAP hierchk.bmp
+SizebarHCursor CURSOR sizebarh.cur
+
+DIALOGMENU MENU PRELOAD DISCARDABLE
+{
+ POPUP "&File"
+ {
+ MENUITEM "&Restore Configuration...\tCtrl+R", ID_FILE_OPEN
+ MENUITEM "&Save Configuration...\tCtrl+S", ID_FILE_SAVE
+ MENUITEM "Restore &Default Config...\tCtrl+D", ID_FILE_DEFAULT
+ MENUITEM SEPARATOR
+ MENUITEM "E&xit", IDC_EXIT
+ }
+ POPUP "&Help"
+ {
+ MENUITEM "&Contents", ID_HELP_CONT
+ MENUITEM "&Search for Help on...", ID_HELP_INDEX
+ MENUITEM "&How to Use Help", ID_HELP_USING
+ MENUITEM SEPARATOR
+ MENUITEM "&About NWConv...", ID_APP_ABOUT
+ }
+}
+
+MainAcc ACCELERATORS PRELOAD DISCARDABLE
+{
+ VK_F1, ID_HELP_CONT, VIRTKEY
+ "^R", ID_FILE_OPEN
+ "^S", ID_FILE_SAVE
+ "^D", ID_FILE_DEFAULT
+}
+
+NWCONV DIALOG DISCARDABLE 15, 40, 331, 148
+STYLE WS_MINIMIZEBOX | WS_CAPTION | WS_SYSMENU | WS_VISIBLE | WS_POPUP | DS_3DLOOK
+CAPTION "Migration Tool for NetWare"
+MENU DIALOGMENU
+FONT 8, "MS Shell Dlg"
+{
+ DEFPUSHBUTTON "&Start Migration",IDOK,262,6,63,14
+ PUSHBUTTON "&Trial Migration",IDC_TRIAL,262,23,63,14
+ PUSHBUTTON "&Logging...",ID_LOGGING,262,40,63,14
+ PUSHBUTTON "E&xit",IDC_EXIT,262,57,63,14
+ PUSHBUTTON "Help",IDHELP,262,74,63,14
+ GROUPBOX "Servers For Migration",IDC_STATIC,5,3,250,137
+ CONTROL "", IDC_LIST1, "ColumnListBox", WS_BORDER | WS_TABSTOP,
+ 11,14,172,119
+ PUSHBUTTON "&Add...",IDC_ADD,190,13,60,14
+ PUSHBUTTON "&Delete",IDC_DELETE,190,30,60,14
+ PUSHBUTTON "&User Options...",IDC_USERINF,190,47,60,14
+ PUSHBUTTON "File &Options...",IDC_FILEINF,190,64,60,14
+}
+
+DlgGetServ DIALOG DISCARDABLE 0, 0, 293, 64
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | DS_3DLOOK
+CAPTION "Select Servers For Migration"
+FONT 8, "MS Shell Dlg"
+{
+ DEFPUSHBUTTON "OK",IDOK,242,6,45,14,WS_DISABLED
+ PUSHBUTTON "Cancel",IDCANCEL,242,23,45,14
+ PUSHBUTTON "&Help",IDHELP,242,40,45,14
+ LTEXT "&From NetWare Server:",IDC_STATIC,9,17,79,8
+ EDITTEXT IDC_EDITNWSERV,89,15,73,12,ES_AUTOHSCROLL
+ PUSHBUTTON "...",IDC_NWBROWSE,166,15,21,12
+ LTEXT "&To Windows NT Server:",IDC_STATIC,9,36,79,8
+ EDITTEXT IDC_EDITNTSERV,89,34,73,13,ES_AUTOHSCROLL
+ PUSHBUTTON "...",IDC_NTBROWSE,166,34,21,13
+}
+
+
+
+DLGNEWUSERS DIALOG DISCARDABLE 0, 0, 276, 163
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | DS_3DLOOK
+CAPTION "User and Group Options"
+FONT 8, "MS Shell Dlg"
+{
+ DEFPUSHBUTTON "OK",IDOK,220,6,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,220,23,50,14
+ PUSHBUTTON "&Help",IDHELP,220,40,50,14
+ PUSHBUTTON "&Advanced >>",IDC_ADVANCED,220,116,50,14
+
+ CONTROL "&Transfer Users and Groups", IDC_CHKUSERS,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,5,5,100,8
+
+ CONTROL "Use &Mappings in File:", IDC_CHKMAPPING,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,5,17,90,8
+ EDITTEXT IDC_MAPPINGFILE,105,15,57,12,ES_AUTOHSCROLL
+ PUSHBUTTON "Create",IDC_BTNMAPPINGFILE,167,10,30,12
+ PUSHBUTTON "Edit",IDC_BTNMAPPINGEDIT,167,24,30,12
+
+ CONTROL "", IDC_TABUSERS, "BOOKTAB", WS_CLIPSIBLINGS |
+ WS_CLIPCHILDREN | WS_TABSTOP, 5, 40, 205, 95
+
+ CONTROL "No Password",IDC_RADIO1,"Button",BS_AUTORADIOBUTTON |
+ WS_GROUP | WS_TABSTOP,17,70,75,8
+ CONTROL "Password is Username",IDC_RADIO2,"Button",
+ BS_AUTORADIOBUTTON,17,85,85,8
+ CONTROL "Password is:",IDC_RADIO3,"Button",BS_AUTORADIOBUTTON,
+ 17,100,57,8
+ EDITTEXT IDC_PWCONST,75,100,57,12,ES_AUTOHSCROLL
+ CONTROL "User Must Change Password",IDC_CHKPWFORCE,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,17,120,126,8
+
+ LTEXT "Duplicate Names:",IDC_STATDUP,12,60,70,8, NOT WS_VISIBLE
+ CONTROL "Log Error",IDC_RADIO4,"Button",
+ BS_AUTORADIOBUTTON | NOT WS_VISIBLE | WS_GROUP | WS_TABSTOP,
+ 17,70,105,8
+ CONTROL "Ignore",IDC_RADIO5,"Button",
+ BS_AUTORADIOBUTTON | NOT WS_VISIBLE,17,85,100,8
+ CONTROL "Overwrite with new Info",IDC_RADIO6,"Button",
+ BS_AUTORADIOBUTTON | NOT WS_VISIBLE,17,100,100,8
+ CONTROL "Add prefix:",IDC_RADIO7,"Button",
+ BS_AUTORADIOBUTTON | NOT WS_VISIBLE,17,115,45,8
+ EDITTEXT IDC_USERCONST,75,115,57,12,ES_AUTOHSCROLL | NOT WS_VISIBLE
+
+
+ CONTROL "Log Error",IDC_RADIO8,"Button",
+ BS_AUTORADIOBUTTON | NOT WS_VISIBLE | WS_GROUP | WS_TABSTOP,
+ 17,70,105,8
+ CONTROL "Ignore",IDC_RADIO9,"Button",
+ BS_AUTORADIOBUTTON | NOT WS_VISIBLE,17,85,100,8
+ CONTROL "Add prefix:",IDC_RADIO10,"Button",
+ BS_AUTORADIOBUTTON | NOT WS_VISIBLE,17,100,45,8
+ EDITTEXT IDC_GROUPCONST,75,100,57,12,ES_AUTOHSCROLL | NOT WS_VISIBLE
+
+
+ CONTROL "Use Supervisor Defaults", IDC_CHKSUPER,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP | NOT WS_VISIBLE,
+ 17,70,100,8
+ CONTROL "Add Supervisors to the Administrators Group", IDC_CHKADMIN,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP | NOT WS_VISIBLE,
+ 17,85,180,8
+ CONTROL "Migrate NetWare Specific Account Information", IDC_CHKFPNW,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP | NOT WS_VISIBLE,
+ 17,100,180,8
+
+ CONTROL "", IDC_DEFAULTBOX, "static", SS_BLACKRECT | WS_CHILD,
+ 272, 144, 4, 4
+
+ CONTROL "Transfer Users to Trusted &Domain:", IDC_CHKTRUSTED,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,
+ 5,151,132,8
+ COMBOBOX IDC_TRUSTED,140,149,85,60,CBS_DROPDOWNLIST | CBS_SORT |
+ WS_VSCROLL | WS_TABSTOP
+
+}
+
+
+MAPCREATE DIALOG DISCARDABLE 0, 0, 250, 140
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | DS_3DLOOK
+CAPTION "Create Mapping File"
+FONT 8, "MS Shell Dlg"
+{
+ DEFPUSHBUTTON "OK",IDOK,195,6,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,195,23,50,14
+
+ LTEXT "Use &Mappings in File:",IDC_STATIC,5,17,90,8
+ EDITTEXT IDC_MAPPINGFILE,105,15,57,12,ES_AUTOHSCROLL
+ PUSHBUTTON "...",IDC_BTNMAPPINGFILE,167,15,21,12
+
+
+ GROUPBOX " ",IDC_STATIC,5,35,183,85
+ CONTROL "Include User Names", IDC_USERS,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,9,35,100,8
+
+ LTEXT "Default Password:",IDC_STATIC,10,50,70,8
+ CONTROL "No Password",IDC_RADIO1,"Button",BS_AUTORADIOBUTTON |
+ WS_GROUP | WS_TABSTOP,15,65,75,8
+ CONTROL "Password is Username",IDC_RADIO2,"Button",
+ BS_AUTORADIOBUTTON,15,80,85,8
+ CONTROL "Password is:",IDC_RADIO3,"Button",BS_AUTORADIOBUTTON,
+ 15,95,57,8
+ EDITTEXT IDC_PWCONST,85,95,57,12,ES_AUTOHSCROLL
+
+ CONTROL "Include Group Names", IDC_GROUPS,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,5,125,100,8
+
+}
+
+
+DLGSERVSEL DIALOG DISCARDABLE 0, 0, 231, 190
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | DS_3DLOOK
+CAPTION "Select Windows NT Server"
+FONT 8, "MS Shell Dlg"
+{
+ PUSHBUTTON "OK",IDC_ALTOK,175,6,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,175,23,50,14
+ PUSHBUTTON "&Help",IDHELP,175,40,50,14
+ LTEXT "&Server:",IDC_STATIC,5,25,30,8
+ EDITTEXT IDC_EDIT1,35,23,78,12,ES_AUTOHSCROLL
+ LTEXT "Select Server",IDC_STATIC,2,48,90,8
+ LISTBOX IDC_LIST1,2,58,226,130, LBS_OWNERDRAWFIXED |
+ LBS_WANTKEYBOARDINPUT | WS_VSCROLL | WS_TABSTOP | LBS_NOTIFY
+}
+
+
+DLGLOGGING DIALOG DISCARDABLE 0, 0, 185, 88
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | DS_3DLOOK
+CAPTION "Logging"
+FONT 8, "MS Shell Dlg"
+{
+ DEFPUSHBUTTON "OK",IDOK,119,6,60,14
+ PUSHBUTTON "Cancel",IDCANCEL,119,23,60,14
+ PUSHBUTTON "&Help",IDHELP,119,40,60,14
+
+ CONTROL "Popup on errors",IDC_CHKERROR,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,5,20,76,8
+ CONTROL "Verbose User/Group Logging",IDC_CHKUVERBOSE,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,5,35,110,8
+ CONTROL "Verbose File Logging",IDC_CHKFVERBOSE,"Button",BS_AUTOCHECKBOX |
+ WS_TABSTOP,5,50,110,8
+
+ PUSHBUTTON "View Log Files...",IDC_VIEWLOG,119,71,60,14
+}
+
+
+STATUSDLG DIALOG DISCARDABLE 0, 0, 270, 127
+STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | DS_3DLOOK
+CAPTION "Converting..."
+FONT 8, "MS Shell Dlg"
+{
+ LTEXT "Processing Migration:",IDC_STATIC,5,10,80,10
+ LTEXT "0",IDC_S_CUR_CONV,90,10,25,8
+ LTEXT "of:",IDC_STATIC,125,10,15,10
+ LTEXT "0",IDC_S_TOT_CONV,145,10,25,7
+ LTEXT "From:",IDC_STATIC,5,25,25,8
+ LTEXT "To:",IDC_STATIC,5,35,25,8
+ LTEXT "",IDC_S_SRCSERV,35,25,84,8
+ LTEXT "",IDC_S_DESTSERV,35,35,85,8
+
+ GROUPBOX "Totals",IDC_STATIC,130,25,135,68
+ LTEXT "Migrations Completed:",IDC_STATIC,137,37,83,8
+ LTEXT "Groups Transferred:",IDC_STATIC,137,47,68,8
+ LTEXT "Users Transferred:",IDC_STATIC,137,57,68,8
+ LTEXT "Files Transferred:",IDC_STATIC,137,67,68,8
+ LTEXT "Errors:",IDC_STATIC,137,77,75,7
+ RTEXT "0",IDC_S_TOT_COMP,225,37,35,8
+ RTEXT "0",IDC_S_TOT_GROUPS,225,47,35,8
+ RTEXT "0",IDC_S_TOT_USERS,225,57,35,8
+ RTEXT "0",IDC_S_TOT_FILES,225,67,35,8
+ RTEXT "0",IDC_S_TOT_ERRORS,225,77,35,8
+
+ LTEXT "",IDC_S_CONVTXT,5,55,70,8
+ RTEXT "0",IDC_S_CUR_NUM,25,65,35,8
+ LTEXT "of:",IDC_STATIC,64,65,10,8
+ RTEXT "0",IDC_S_CUR_TOT,77,65,51,8
+
+ LTEXT "",IDC_PANEL1,5,75,30,8
+ RTEXT "",IDC_PANEL2,8,85,52,8
+ LTEXT "",IDC_PANEL4,64,85,10,8
+ RTEXT "",IDC_PANEL3,77,85,51,8
+
+ GROUPBOX "",IDC_S_ITEMLABEL,5,95,260,25
+
+ LTEXT "",IDC_S_STATUSITEM,9,107,245,8
+}
+
+
+CONVERSIONEND DIALOG DISCARDABLE 0, 0, 200, 75
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | DS_3DLOOK
+CAPTION "Transfer Completed"
+FONT 8, "MS Shell Dlg"
+{
+ DEFPUSHBUTTON "OK",IDOK,135,6,60,14
+ PUSHBUTTON "View Log Files...",IDC_VIEWLOG,135,23,60,14
+
+ GROUPBOX "Totals",IDC_STATIC,5,5,125,65
+ LTEXT "Migrations Completed:",IDC_STATIC,10,15,83,8
+ LTEXT "Groups Transferred:",IDC_STATIC,10,25,68,8
+ LTEXT "Users Transferred:",IDC_STATIC,10,35,68,8
+ LTEXT "Files Transferred:",IDC_STATIC,10,45,68,8
+ LTEXT "Errors:",IDC_STATIC,10,55,68,8
+ RTEXT "0",IDC_S_TOT_COMP,95,15,30,8
+ RTEXT "0",IDC_S_TOT_GROUPS,95,25,30,8
+ RTEXT "0",IDC_S_TOT_USERS,95,35,30,8
+ RTEXT "0",IDC_S_TOT_FILES,95,45,30,8
+ RTEXT "0",IDC_S_TOT_ERRORS,95,55,30,8
+
+}
+
+
+FILEMAIN DIALOG DISCARDABLE 0, 0, 308, 135
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | DS_3DLOOK
+CAPTION "File Options"
+FONT 8, "MS Shell Dlg"
+{
+ DEFPUSHBUTTON "OK",IDOK,251,6,50,14
+ PUSHBUTTON "Cancel",IDCANCEL,251,23,50,14
+ PUSHBUTTON "&Help",IDHELP,251,40,50,14
+
+ GROUPBOX " ",IDC_STATIC,5,5,241,128
+ CONTROL "Transfer Files", IDC_CHKFILES,"Button",
+ BS_AUTOCHECKBOX | WS_TABSTOP,9,5,100,8
+
+ CONTROL "", IDC_LIST1, "ColumnListBox", WS_BORDER | WS_TABSTOP,
+ 11,17,172,107
+
+ PUSHBUTTON "&Add...",IDC_ADD,188,25,50,14
+ PUSHBUTTON "&Delete",IDC_DELETE,188,42,50,14
+ PUSHBUTTON "&Modify...",IDC_MODIFY,188,59,50,14
+ PUSHBUTTON "&Files...",IDC_FILES,188,76,50,14
+}
+
+
+FSELECTMENU MENU PRELOAD DISCARDABLE
+{
+ POPUP "&Tree"
+ {
+ MENUITEM "E&xpand One Level", IDM_EXP_ONE
+ MENUITEM "Expand &Branch", IDM_EXP_BRANCH
+ MENUITEM "Expand &All", IDM_EXP_ALL
+ MENUITEM "&Collapse Branch", IDM_COLLAPSE
+ }
+ POPUP "T&ransfer"
+ {
+ MENUITEM "&Hidden Files", IDM_HIDDEN
+ MENUITEM "&System Files", IDM_SYSTEM
+ }
+}
+
+
+FILESELECT DIALOG DISCARDABLE 0, 0, 308, 135
+STYLE WS_THICKFRAME | WS_MAXIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | DS_3DLOOK
+CAPTION "Files To Transfer"
+MENU FSELECTMENU
+FONT 8, "MS Shell Dlg"
+{
+ DEFPUSHBUTTON "OK",IDOK,77,106,45,14
+ PUSHBUTTON "Cancel",IDCANCEL,132,106,45,14
+ PUSHBUTTON "&Help",IDHELP,187,106,45,14
+
+ LISTBOX IDC_LIST1,10,10,142,101, LBS_OWNERDRAWFIXED | LBS_WANTKEYBOARDINPUT |
+ WS_VSCROLL | WS_TABSTOP
+
+ LISTBOX IDC_LIST2,155,10,142,101, LBS_OWNERDRAWFIXED |
+ LBS_WANTKEYBOARDINPUT | WS_VSCROLL | WS_TABSTOP
+}
+
+
+NWShareSelect DIALOG DISCARDABLE 0, 0, 310, 103
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | DS_3DLOOK
+CAPTION "Add Volume to Copy"
+FONT 8, "MS Shell Dlg"
+{
+ DEFPUSHBUTTON "OK",IDOK, 263,6,45,14
+ PUSHBUTTON "Cancel",IDCANCEL,263,23,45,14
+ PUSHBUTTON "&Help",IDHELP,263,40,45,14
+
+ GROUPBOX "",IDC_FSERVER,5,5,250,30
+ LTEXT "Volume:",IDC_STATIC,10,18,30,8
+ LTEXT "",IDC_VOLUME,60,18,105,8, NOT WS_VISIBLE
+ COMBOBOX IDC_COMBO1,60,16,85,60,CBS_DROPDOWNLIST | CBS_SORT |
+ WS_VSCROLL | WS_TABSTOP
+
+ GROUPBOX "",IDC_TSERVER,5,44,250,51
+ LTEXT "Share:",IDC_STATIC,10,57,25,8
+ COMBOBOX IDC_COMBO2,60,55,85,60,CBS_DROPDOWNLIST | CBS_SORT |
+ WS_VSCROLL | WS_TABSTOP
+
+ PUSHBUTTON "New Share...",IDC_NEWSHARE,200,55,50,14
+ PUSHBUTTON "Properties...",IDC_PROPERTIES,200,72,50,14
+
+ LTEXT "Subdirectory:",IDC_STATIC,10,78,45,8
+ EDITTEXT IDC_EDIT1,60,75,135,12,ES_AUTOHSCROLL
+}
+
+
+NWShareAdd DIALOG DISCARDABLE 0, 0, 280, 60
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | DS_3DLOOK
+CAPTION "New Share"
+FONT 8, "MS Shell Dlg"
+{
+ DEFPUSHBUTTON "OK",IDOK, 230,6,45,14
+ PUSHBUTTON "Cancel",IDCANCEL,230,23,45,14
+ PUSHBUTTON "&Help",IDHELP,230,40,45,14
+
+ LTEXT "&Share Name:",IDC_STATIC,10,10,45,8
+ LTEXT "",IDC_SHARENAME,60,10,100,8
+ EDITTEXT IDC_EDIT1,60,7,100,12,ES_AUTOHSCROLL
+
+ LTEXT "&Path:",IDC_STATIC,10,30,45,8
+ EDITTEXT IDC_EDIT2,60,27,160,12,ES_AUTOHSCROLL
+
+}
+
+
+PasswordEnter DIALOG DISCARDABLE 52, 26, 225, 70
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | DS_3DLOOK
+CAPTION "Enter Network Credentials"
+FONT 8, "MS Shell Dlg"
+{
+ DEFPUSHBUTTON "OK", IDOK, 175,6,45,14
+ PUSHBUTTON "Cancel", IDCANCEL, 175,23,45,14
+
+ LTEXT "Incorrect user credentials for:", IDC_STATIC, 7, 7, 99, 8
+ LTEXT "", IDC_SERVNAME, 12, 17, 99, 8
+ LTEXT "&Connect As:", IDC_STATIC, 7, 32, 56, 8
+ EDITTEXT IDC_USERNAME, 55, 30, 103, 12
+ LTEXT "&Password:", IDC_STATIC, 7, 52, 56, 8
+ EDITTEXT IDC_PASSWORD, 55, 50, 103, 12, ES_PASSWORD
+}
+
+
+PanelDLG DIALOG DISCARDABLE 0, 0, 200, 84
+STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | DS_3DLOOK
+CAPTION ""
+FONT 8, "MS Shell Dlg"
+{
+ LTEXT "",IDC_PANEL1,5,10,195,8
+ LTEXT "",IDC_PANEL2,5,20,45,8
+ LTEXT "",IDC_PANEL3,5,30,45,8
+ LTEXT "",IDC_PANEL4,5,40,45,8
+ LTEXT "",IDC_PANEL5,5,50,45,8
+
+ LTEXT "",IDC_PANEL6,52,10,140,8
+ LTEXT "",IDC_PANEL7,52,20,140,8
+ LTEXT "",IDC_PANEL8,52,30,140,8
+ LTEXT "",IDC_PANEL9,52,40,140,8
+ LTEXT "",IDC_PANEL10,52,50,140,8
+
+ PUSHBUTTON "Cancel", IDCANCEL, 77,65,45,14
+}
+
+
+ALERTSEL DIALOG DISCARDABLE 0, 0, 183, 135
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | DS_3DLOOK
+CAPTION "Warning"
+FONT 8, "MS Shell Dlg"
+{
+ CTEXT "",IDC_PANEL1,5,10,173,40
+
+ LISTBOX IDC_LIST1,5,55,173,40, WS_VSCROLL | WS_TABSTOP
+
+ CTEXT "Do you want to continue with the conversion?",
+ IDC_STATIC,5,100,173,8
+
+ PUSHBUTTON "&Yes", IDYES, 44, 113, 45, 14
+ PUSHBUTTON "&No", IDNO, 94, 113, 45, 14
+
+}
+
+
+NTSERVINFO DIALOG DISCARDABLE 0, 0, 183, 185
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | DS_3DLOOK
+CAPTION "Server Info"
+FONT 8, "MS Shell Dlg"
+{
+ LTEXT "&Server:",IDC_STATIC,5,10,50,8
+ COMBOBOX IDC_COMBO1,55,7,100,40,CBS_DROPDOWNLIST | CBS_SORT |
+ WS_VSCROLL | WS_TABSTOP
+
+ LTEXT "Type:",IDC_STATIC,5,22,50,8
+ LTEXT "",IDC_TYPE,55,22,130,8
+
+ LTEXT "Version:",IDC_STATIC,5,32,50,8
+ LTEXT "",IDC_VERSION,55,32,130,8
+
+ LTEXT "&Drives:",IDC_T_DRIVES,5,45,50,8, NOT WS_VISIBLE
+ LISTBOX IDC_LIST1,5,55,173,40, WS_VSCROLL | WS_TABSTOP | NOT WS_VISIBLE
+
+ LTEXT "S&hares:",IDC_T_SHARES,5,105,50,8, NOT WS_VISIBLE
+ LISTBOX IDC_LIST2,5,115,173,40, WS_VSCROLL | WS_TABSTOP | NOT WS_VISIBLE
+
+ LTEXT "&Volumes:",IDC_T_VOLUMES,5,45,50,8
+ LISTBOX IDC_LIST3,5,55,173,100, WS_VSCROLL | WS_TABSTOP
+
+ DEFPUSHBUTTON "OK",IDOK,71,165,40,14
+
+}
+
+
+NAMEERROR DIALOG DISCARDABLE 0, 0, 183, 90
+STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | DS_3DLOOK
+CAPTION ""
+FONT 8, "MS Shell Dlg"
+{
+ CTEXT "",IDC_PANEL1, 5,10,173,20
+
+ LTEXT "Name:", IDC_STATIC, 5, 40, 50, 8
+ LTEXT "", IDC_OLDNAME, 60, 40, 115, 8
+
+ LTEXT "New Name:", IDC_STATIC, 5, 55, 50, 8
+ EDITTEXT IDC_NEWNAME, 60,52,115,12,ES_AUTOHSCROLL
+
+ DEFPUSHBUTTON "&OK", IDRETRY, 19, 70, 45, 14
+ PUSHBUTTON "&Cancel", IDIGNORE, 69, 70, 45, 14
+ PUSHBUTTON "&Abort", IDABORT, 119, 70, 45, 14
+
+}
+
+
+
+STRINGTABLE DISCARDABLE
+{
+ IDS_APPNAME "NWConv"
+
+ IDS_DISK_SPACE_UNAVAIL "Unavailable"
+ IDS_DISK_SPACE "%13s KB Free"
+ IDS_MATH_COPR_NOTPRESENT "Not present"
+ IDS_MATH_COPR_PRESENT "Present"
+ IDS_PHYSICAL_MEM "%13s KB Free"
+
+ IDS_USERFILTERSTRING "Mapping Files(*.MAP)|*.map|"
+ IDS_MAINFILTERSTRING "Configuration Files(*.CNF)|*.cnf|"
+
+ IDS_TXTWARNING "Warning"
+ IDS_RESTOREDEFAULTS "This will delete all servers from\nthe list of servers to convert\nand all the configuration information."
+
+ IDS_MAPNEWNAME "NewName"
+ IDS_MAPNEWPASSWORD "Password"
+ IDS_MAPGROUPS "__GROUPS__"
+
+ IDS_NOREADMAP "Can't access the specified mapping file"
+ IDS_MAPCREATED "Mapping file created succesfully.\nDo you want to edit it?"
+ IDS_MAPCREATEFAIL "Mapping file could not be created"
+ IDS_NOEDITMAP "Can't access the specified mapping file, do you want to browse for it?"
+
+ IDS_CRLF "\r\n"
+
+ IDS_SUN "Sun"
+ IDS_MON "Mon"
+ IDS_TUE "Tue"
+ IDS_WED "Wed"
+ IDS_THU "Thu"
+ IDS_FRI "Fri"
+ IDS_SAT "Sat"
+
+ IDS_SUNDAY "Sunday"
+ IDS_MONDAY "Monday"
+ IDS_TUESDAY "Tuesday"
+ IDS_WEDNESDAY "Wednesday"
+ IDS_THURSDAY "Thursday"
+ IDS_FRIDAY "Friday"
+ IDS_SATURDAY "Saturday"
+
+ IDS_NWCANT_CON "Cannot connect to server: %s"
+ IDS_NWNO_ADMIN "You do not have administrator or supervisor privileges on %s"
+
+ IDS_LINE ";+----------------------------------------------------------------------------+\r\n"
+ IDS_BRACE ";| %-74s |\r\n"
+ IDS_YES "Yes"
+ IDS_NO "No"
+
+ IDS_E_NWLOG "Error: Can't write LogFile"
+
+ IDS_LOCKED_OUT "Locked out"
+
+ IDS_D_1 "Volume:"
+ IDS_D_2 "Dir:"
+ IDS_D_3 "File:"
+ IDS_D_4 "Total Bytes:"
+ IDS_D_5 "Share Properties"
+ IDS_D_6 "Modify Destination"
+ IDS_D_7 "From Server: "
+ IDS_D_8 "To Server: "
+ IDS_D_9 "Source Files"
+ IDS_D_10 "Destination"
+
+ IDS_D_11 "NetWare Server"
+ IDS_D_12 "Windows NT Server"
+
+ IDS_D_13 " Free: %-14s Allocated: %-14s"
+ IDS_D_14 " (Drive Invalid)"
+
+ IDS_D_15 "The following file conversions point to drives that are not NTFS.\n\nFile permissions will only be transfered if the destination drive is an NTFS Drive."
+ IDS_D_16 "There does not appear to be enough space on the destination drives for the following file transfers to take place."
+
+ IDS_D_17 "Transfer Aborted"
+
+ IDS_D_18 "Converting User:"
+ IDS_D_19 "User:"
+
+ IDS_D_20 "Converting Group:"
+ IDS_D_21 "Group:"
+
+ IDS_D_22 "Verifying Information"
+
+ IDS_S_1 "Administrators"
+ IDS_S_2 "SYSTEM"
+ IDS_S_3 "MAIL"
+ IDS_S_4 "LOGIN"
+ IDS_S_5 "ETC"
+ IDS_S_6 "SYS"
+
+ IDS_S_7 "Out of Memory"
+ IDS_S_8 "NwConv - Error"
+
+ IDS_S_9 "NTFS"
+ IDS_S_10 "\\\\%s\\ADMIN$"
+ IDS_S_11 "ADMIN$"
+
+ IDS_S_12 "System\\CurrentControlSet\\Control\\ProductOptions"
+ IDS_S_13 "ProductType"
+ IDS_S_14 "LanmanNT"
+
+ IDS_S_15 "Windows NT(R) Server"
+ IDS_S_16 "NetWare(R) Server"
+ IDS_S_17 " Free Space: %13s"
+ IDS_S_18 " Path: %s"
+ IDS_S_19 " Size: %13s"
+
+ IDS_S_20 "CNF"
+
+ IDS_S_21 "Name"
+ IDS_S_22 "DisplayName"
+ IDS_S_23 "System\\CurrentControlSet\\Services\\NWCWorkstation\\networkProvider"
+ IDS_S_24 "System\\CurrentControlSet\\Services\\NWCWorkstation"
+ IDS_S_25 "System\\CurrentControlSet\\Services\\LanmanWorkstation\\networkProvider"
+ IDS_S_26 "NWCWorkstation"
+
+ IDS_S_27 "\\\\%s\\SYS"
+
+ IDS_S_28 "SUPERVISOR"
+ IDS_S_29 "GUEST"
+ IDS_S_30 "ADMIN"
+ IDS_S_31 "EVERYONE"
+ IDS_S_32 "Domain Admins"
+ IDS_S_33 "Domain Users"
+
+ IDS_S_34 "[SRWCEMFA]"
+
+ IDS_S_35 "Select NetWare Server"
+
+ IDS_S_36 ".MAP"
+ IDS_S_37 "MAP"
+
+ IDS_S_38 "Passwords"
+ IDS_S_39 "Usernames"
+ IDS_S_40 "Group Names"
+ IDS_S_41 "Defaults"
+
+ IDS_S_42 "Domain Admins"
+ IDS_S_43 "Print Operators"
+
+ IDS_S_44 "Enumerating Users:"
+ IDS_S_45 "Enumerating Groups:"
+ IDS_S_46 "Adding Users to Group: %s"
+ IDS_S_47 "Adding Security Equivalences for User: %s"
+ IDS_S_48 "Adding Print Operators: %s"
+
+ IDS_S_49 "CDFS"
+
+
+ IDS_E_1 "NWConv - CRITICAL ERROR"
+ IDS_E_2 "Error"
+ IDS_E_3 "Path must be in the form of: drive:\\path"
+ IDS_E_4 "The share already exists"
+ IDS_E_5 "Cannot connect to server: %s"
+ IDS_E_6 "No Admin Priviledge on: %s"
+ IDS_E_7 "Could not get Server Information for: %s"
+ IDS_E_8 "%s is not a Windows NT Server"
+ IDS_E_9 "Cannot connect to server: %s\n\n%s"
+
+ IDS_E_10 "Configuration File Corrupt"
+ IDS_E_11 "The configuration file was created with a different version of the program"
+
+ IDS_E_12 "Client Services for NetWare not installed"
+
+ IDS_E_13 "Error Creating Log File: %s"
+
+ IDS_E_14 "Already Converting %s"
+
+ IDS_E_15 "This will end the conversion. Are you sure you want to do this?"
+ IDS_E_16 "Warning"
+
+ IDS_E_17 "The specified mapping file already exists, do you want to overwrite it?"
+
+ IDS_E_18 "%s is not a NetWare Server"
+
+ IDS_M_1 "NWConv Mapping for: %s"
+ IDS_M_2 "Version: 1.1"
+ IDS_M_3 "Format Is:"
+ IDS_M_4 " OldName, NewName, Password"
+ IDS_M_5 "[USERS]\r\n"
+ IDS_M_6 "\r\n[GROUPS]\r\n"
+
+ IDS_M_7 "USERS"
+ IDS_M_8 "GROUPS"
+
+ IDS_L_1 " Midnight AM Noon PM\r\n"
+ IDS_L_2 " 12 1 2 3 4 5 6 7 8 9 10 11 12 1 2 3 4 5 6 7 8 9 10 11\r\n"
+ IDS_L_3 " +-----------------------------------------------------------------------+\r\n"
+
+ IDS_L_4 "Error: Writing File"
+ IDS_L_5 "Error: Writing File - out-of-space"
+ IDS_L_6 "Error: Can't create thread to copy file"
+
+ IDS_L_7 "[New Shares]\r\n"
+ IDS_L_8 "Path: %s \r\n"
+ IDS_L_9 "Access Rights for: %s"
+ IDS_L_10 "[Access Rights]\r\n"
+
+ IDS_L_11 "Unable to open source"
+ IDS_L_12 "Unable to open source, error code %d"
+ IDS_L_13 "Unable to get time of source"
+ IDS_L_14 "Unable to create destination, error code %d"
+ IDS_L_15 "Unable to set time of destination"
+ IDS_L_16 "Unable to set attributes of destination"
+
+ IDS_L_17 "Source Directory: %s\r\n"
+ IDS_L_18 "Dest Directory : %s\r\n"
+ IDS_L_19 "File: %s"
+ IDS_L_20 "[Files]\r\n"
+
+ IDS_L_21 "[RAHS]"
+ IDS_L_22 "Error Copying File: %s -> %s\r\n"
+
+ IDS_L_23 "Files are shown in the following format:"
+ IDS_L_24 " [Files]"
+ IDS_L_25 " 1,234,567 [RAHS] TEST.TXT"
+ IDS_L_26 " | |||| |"
+ IDS_L_27 " | |||| +- Name of the File"
+ IDS_L_28 " | ||||"
+ IDS_L_29 " | |||+---- System"
+ IDS_L_30 " | ||+----- Hidden"
+ IDS_L_31 " | |+------ Archived"
+ IDS_L_32 " | +------- Read Only"
+ IDS_L_33 " |"
+ IDS_L_34 " +---------- File size (in Bytes)"
+ IDS_L_35 ""
+ IDS_L_36 "The Access Rights are shown in the following format:"
+ IDS_L_37 " [Access Rights]"
+ IDS_L_38 " [SRWCEMFA] TestUser"
+ IDS_L_39 " |||||||| |"
+ IDS_L_40 " |||||||| +- User or Group the Rights apply to"
+ IDS_L_41 " ||||||||"
+ IDS_L_42 " |||||||+---- Access Control"
+ IDS_L_43 " ||||||+----- File Scan"
+ IDS_L_44 " |||||+------ Modify"
+ IDS_L_45 " ||||+------- Erase"
+ IDS_L_46 " |||+-------- Create"
+ IDS_L_47 " ||+--------- Write"
+ IDS_L_48 " |+---------- Read"
+ IDS_L_49 " +----------- Supervisory"
+
+ IDS_L_50 "Converting Files:"
+ IDS_L_51 "File:"
+
+ IDS_L_52 "Copying Files From Volume: %s\r\n"
+ IDS_L_53 "To Share: %s\r\n"
+ IDS_L_54 "Total Files Converted: %s\r\n"
+ IDS_L_55 "Bytes Transferred: %s\r\n"
+
+ IDS_L_56 "Logon Hours:\r\n"
+ IDS_L_57 "New Account Info:\r\n"
+ IDS_L_58 "Name: %s\r\n"
+ IDS_L_59 "Password: %s\r\n"
+ IDS_L_60 "Guest"
+ IDS_L_61 "User"
+ IDS_L_62 "Admin"
+ IDS_L_63 "Privilege: %s\r\n"
+ IDS_L_64 "Home Dir: %s\r\n"
+ IDS_L_65 "Comment: %s\r\n"
+ IDS_L_66 "Flags:\r\n"
+ IDS_L_67 "Execute login script: %s\r\n"
+ IDS_L_68 "Account disabled: %s\r\n"
+ IDS_L_69 "Deleting prohibited: %s\r\n"
+ IDS_L_70 "Home dir required: %s\r\n"
+ IDS_L_71 "Password required: %s\r\n"
+ IDS_L_72 "User can change password: %s\r\n"
+ IDS_L_73 "Script path: %s\r\n"
+ IDS_L_74 "Full Name: %s\r\n"
+ IDS_L_75 "Logon Server: %s\r\n"
+
+ IDS_L_76 "Error %d: Adding Access Rights\r\n"
+ IDS_L_77 "Error %d: Adding Access Rights. User/Group: %s\r\n"
+
+ IDS_L_78 "No Access (None)"
+ IDS_L_79 "Full Control (All)"
+ IDS_L_80 "R"
+ IDS_L_81 "W"
+ IDS_L_82 "X"
+ IDS_L_83 "D"
+ IDS_L_84 "P"
+ IDS_L_85 "(RX)"
+ IDS_L_86 "Read (RX)"
+ IDS_L_87 "(WX)"
+ IDS_L_88 "Add (WX)"
+ IDS_L_89 "(RWX)"
+ IDS_L_90 "Add & Read (RWX)"
+ IDS_L_91 "(RWXD)"
+ IDS_L_92 "Change (RWXD)"
+ IDS_L_93 "Special Access %s"
+
+ IDS_L_94 "Minimum Password Length: %li\r\n"
+ IDS_L_95 "Maximum Password Age: %li days\r\n"
+ IDS_L_96 "Force Logoff: %li\r\n"
+
+ IDS_L_97 ";| Created: %-65s |\r\n"
+ IDS_L_98 ";| System : %-65s |\r\n"
+ IDS_L_99 ";| Admin : %-65s |\r\n"
+ IDS_L_100 ";| Version: %1d.%-63d |\r\n"
+
+ IDS_L_101 "Migration Tool for NetWare Error Log File"
+ IDS_L_102 "Migration Tool for NetWare Log File"
+ IDS_L_103 "Migration Tool for NetWare Summary Log File"
+
+ IDS_L_104 "Login Times:\r\n"
+
+ IDS_L_105 "Original Account Info:\r\n"
+ IDS_L_106 "Name: "
+ IDS_L_107 "(Never)"
+ IDS_L_108 "(Unlimited)"
+ IDS_L_109 "Account expires: "
+ IDS_L_110 "Restrictions:\r\n"
+ IDS_L_111 "Only SUPERVISOR can change password\r\n"
+ IDS_L_112 "Anyone who knows password can change it\r\n"
+ IDS_L_113 "Unique passwords required: %s\r\n"
+ IDS_L_114 "# days to Password Expiration: "
+ IDS_L_115 "Initial Grace Logins: "
+ IDS_L_116 "Minimum Password Length: %u\r\n"
+ IDS_L_117 "Maximum Number of Connections: "
+ IDS_L_118 "Default Current Account Balance: %lu\r\n"
+ IDS_L_119 "Default Credit Limit: %lu\r\n"
+ IDS_L_120 "Max Disk Blocks: "
+ IDS_L_121 "Account disabled: %s\r\n"
+ IDS_L_122 "Password expires: "
+ IDS_L_123 "Grace Logins: "
+ IDS_L_124 "Number of login failures: %u\r\n"
+
+ IDS_L_125 "[Shares]\r\n"
+
+ IDS_L_126 "Number of Migrations = %i\r\n"
+ IDS_L_127 "[Servers]\r\n"
+ IDS_L_128 "From: %-15s To: %s\r\n"
+ IDS_L_129 "Server Information"
+ IDS_L_130 "Windows NT(R) Server\r\n"
+ IDS_L_131 "Version: %lu.%lu\r\n"
+ IDS_L_132 "[Drives]\r\n"
+ IDS_L_133 "Free Space: %s\r\n"
+ IDS_L_134 "NetWare(R) Server\r\n"
+ IDS_L_135 "Version: %u.%u\r\n"
+
+ IDS_L_136 "[Transfer Options]\r\n"
+ IDS_L_137 "Convert Users and Groups: %s\r\n"
+ IDS_L_138 "Use mapping file: %s\r\n"
+ IDS_L_139 "Mapping File: %s\r\n"
+ IDS_L_140 "User Transfer Options:\r\n"
+ IDS_L_141 "Passwords: "
+ IDS_L_142 "1 - Use NULL\r\n"
+ IDS_L_143 "2 - Password is username\r\n"
+ IDS_L_144 "3 - Use constant: %s\r\n"
+ IDS_L_145 "User must change password: %s\r\n"
+ IDS_L_146 "Duplicate Names: "
+ IDS_L_147 "Duplicate Groups: "
+ IDS_L_148 "Log Error\r\n"
+ IDS_L_149 "Ignore\r\n"
+ IDS_L_150 "Pre-pend constant: %s\r\n"
+ IDS_L_151 "Overwrite with new Info\r\n"
+ IDS_L_152 "Use supervisor defaults: %s\r\n"
+ IDS_L_153 "Add Supervisors to the Administrators Group: %s\r\n"
+ IDS_L_154 "Transfer users to trusted domain: %s\r\n"
+
+ IDS_L_155 "[File Options]\r\n"
+ IDS_L_156 "Convert Files: %s\r\n"
+
+ IDS_L_157 "From: %-15s"
+ IDS_L_158 "To: %-15s")
+ IDS_L_159 "Converted: %-40s"
+
+ IDS_L_160 "Users to Transfer\r\n"
+ IDS_L_161 "+--------------------------------------------------+\r\n"
+
+ IDS_L_162 "(Error: Name too Long)"
+ IDS_L_163 "(Duplicate)"
+ IDS_L_164 "(Error: Name Invalid)"
+
+ IDS_L_165 "User Name too long"
+ IDS_L_166 "Duplicate User Name"
+ IDS_L_167 "User Name Contains Invalid Characters"
+ IDS_L_168 "Error Transfering User Name"
+
+ IDS_L_169 "[Users]\r\n"
+ IDS_L_170 "Number of Users = %i\r\n\r\n"
+ IDS_L_171 "[Transferring Users]\r\n"
+
+ IDS_L_172 "(Duplicate)\r\n\r\n"
+ IDS_L_173 "Error: Duplicate User Name\r\n"
+ IDS_L_174 "(Error: Name too Long)\r\n\r\n"
+ IDS_L_175 "Error: User Name too long\r\n"
+ IDS_L_176 "(Error: Name Invalid)\r\n\r\n"
+ IDS_L_177 "Error: User Name Invalid\r\n"
+
+ IDS_L_178 "(Error: Saving User)\r\n"
+ IDS_L_179 "Error: Saving User Name\r\n"
+
+ IDS_L_180 "(Added)"
+
+ IDS_L_181 "Total Users Converted: %s\r\n"
+
+ IDS_L_182 "Group Name too long"
+ IDS_L_183 "Duplicate Group Name"
+ IDS_L_184 "Group Name Contains Invalid Characters"
+ IDS_L_185 "Error Transfering Group Name"
+
+ IDS_L_186 "[Transferring Groups]\r\n"
+ IDS_L_187 "[Groups]\r\n"
+ IDS_L_188 "Number Groups = %li\r\n"
+
+ IDS_L_189 "Error: Duplicate Group Name\r\n"
+ IDS_L_190 "Error: Group Name too long\r\n"
+ IDS_L_191 "Error: Group Name Invalid\r\n"
+ IDS_L_192 "(Error: Saving Group)"
+ IDS_L_193 "Error: Saving Group Name\r\n"
+
+ IDS_L_194 "[Adding Users to Groups]\r\n"
+ IDS_L_195 "Error: Adding user [%s] to Group: %s\r\n"
+ IDS_L_196 "(Error: Adding User)"
+ IDS_L_197 "Could not enumerate users for Group: %s\r\n"
+
+ IDS_L_198 "[Converting Security Equivalences]\r\n"
+ IDS_L_199 "[Security Equivalences]\r\n"
+
+ IDS_L_200 "[Converting Print Operators]\r\n"
+ IDS_L_201 "[Print Operators]\r\n"
+
+ IDS_L_202 "Total Groups Converted: %s\r\n"
+
+ IDS_L_203 "Setting User Defaults from Supervisor Defaults"
+ IDS_L_204 "[New Defaults: %s]\r\n"
+ IDS_L_205 "[Original Defaults: %s]\r\n"
+ IDS_L_206 "[Supervisor Defaults: %s]\r\n"
+
+ IDS_L_207 "Conversion = Trial\r\n"
+ IDS_L_208 "Conversion = Regular\r\n"
+
+ IDS_L_209 "Error: Accessing Server: %s\r\n"
+ IDS_L_210 "Error: Getting Users on Server: %s\r\n"
+ IDS_L_211 "Error: Getting Groups on Server: %s\r\n"
+
+ IDS_L_212 "[Transferring Files]\r\n"
+
+ IDS_L_213 "Bytes:"
+ IDS_L_214 "of:"
+
+ IDS_L_215 "Conversion Aborted: %s\r\n"
+ IDS_L_216 "Conversion Finished: %s\r\n"
+
+ IDS_L_217 "Error: Accessing Mapping File: %s\r\n"
+
+ IDS_L_218 "Error: Getting Users from Mapping File: %s\r\n"
+ IDS_L_219 "Error: Getting Groups from Mapping File: %s\r\n"
+
+}
diff --git a/private/nw/convert/nwconv/nwlog.c b/private/nw/convert/nwconv/nwlog.c
new file mode 100644
index 000000000..fd88a56b0
--- /dev/null
+++ b/private/nw/convert/nwconv/nwlog.c
@@ -0,0 +1,746 @@
+/*
+ +-------------------------------------------------------------------------+
+ | Logging Routines |
+ +-------------------------------------------------------------------------+
+ | (c) Copyright 1994 |
+ | Microsoft Corp. |
+ | All rights reserved |
+ | |
+ | Program : [NWLog.c] |
+ | Programmer : Arthur Hanson |
+ | Original Program Date : [Dec 01, 1993] |
+ | Last Update : [Jun 16, 1994] |
+ | |
+ | Version: 1.00 |
+ | |
+ | Description: |
+ | |
+ | History: |
+ | arth Jun 16, 1994 1.00 Original Version. |
+ | |
+ +-------------------------------------------------------------------------+
+*/
+
+
+#include "globals.h"
+
+#include <io.h>
+#include <malloc.h>
+#include <string.h>
+
+#define VER_HI 1
+#define VER_LOW 1
+
+HANDLE hErr = NULL;
+HANDLE hLog = NULL;
+HANDLE hSummary = NULL;
+
+#define STD_EXT "LOG"
+#define ERR_FILENAME "Error."
+#define LOG_FILENAME "LogFile."
+#define SUMMARY_FILENAME "Summary."
+
+#define MAX_LOG_STR 1024
+#define FILENAME_LOG "LogFile.LOG"
+#define FILENAME_ERROR "Error.LOG"
+#define FILENAME_SUMMARY "Summary.LOG"
+
+static char LogFileName[MAX_PATH + 1];
+static char ErrorLogFileName[MAX_PATH + 1];
+static char SummaryLogFileName[MAX_PATH + 1];
+static char Spaces[] = " ";
+
+static BOOL ErrorFlag;
+static TCHAR ErrorContext[MAX_LOG_STR];
+static TCHAR ErrorCategory[MAX_LOG_STR];
+static TCHAR ErrorItem[MAX_LOG_STR];
+static TCHAR ErrorText[MAX_LOG_STR];
+static TCHAR tmpStr[MAX_LOG_STR];
+static BOOL CategoryWritten;
+static BOOL ContextWritten;
+static BOOL CategorySet;
+static BOOL ItemSet;
+
+static BOOL VerboseULogging = TRUE;
+static BOOL VerboseFLogging = TRUE;
+static BOOL ErrorBreak = FALSE;
+
+static BOOL LogCancel = FALSE;
+
+/*+-------------------------------------------------------------------------+
+ | ErrorResetAll()
+ |
+ +-------------------------------------------------------------------------+*/
+void ErrorResetAll() {
+ ErrorFlag = FALSE;
+ CategoryWritten = FALSE;
+ ContextWritten = FALSE;
+ CategorySet = FALSE;
+ ItemSet = FALSE;
+
+ lstrcpy(ErrorContext, TEXT(""));
+ lstrcpy(ErrorCategory, TEXT(""));
+
+} // ErrorResetAll
+
+
+/*+-------------------------------------------------------------------------+
+ | ErrorContextSet()
+ |
+ | Sets the context for the error message, generally this would be
+ | the source and destination server pair.
+ |
+ +-------------------------------------------------------------------------+*/
+void ErrorContextSet(LPTSTR szFormat, ...) {
+ va_list marker;
+
+ va_start(marker, szFormat);
+ wvsprintf(ErrorContext, szFormat, marker);
+
+ // Do category and item as well since context is higher level
+ lstrcpy(ErrorCategory, TEXT(""));
+ lstrcpy(ErrorItem, TEXT(""));
+
+ ContextWritten = FALSE;
+ CategoryWritten = FALSE;
+ va_end(marker);
+
+} // ErrorContextSet
+
+
+/*+-------------------------------------------------------------------------+
+ | ErrorCategorySet()
+ |
+ | Sets the category for the error message, generally this would tell
+ | what type of items is being converted: "Converting Users"
+ |
+ +-------------------------------------------------------------------------+*/
+void ErrorCategorySet(LPTSTR szFormat, ...) {
+ va_list marker;
+
+ va_start(marker, szFormat);
+ wvsprintf(ErrorCategory, szFormat, marker);
+ CategorySet = TRUE;
+ ItemSet = FALSE;
+ lstrcpy(ErrorItem, TEXT(""));
+ CategoryWritten = FALSE;
+ va_end(marker);
+
+} // ErrorCategorySet
+
+
+/*+-------------------------------------------------------------------------+
+ | ErrorItemSet()
+ |
+ | Defines the specific item that error'd. This is usually a user,
+ | group or file name.
+ |
+ +-------------------------------------------------------------------------+*/
+void ErrorItemSet(LPTSTR szFormat, ...) {
+ va_list marker;
+
+ va_start(marker, szFormat);
+ ItemSet = TRUE;
+ wvsprintf(ErrorItem, szFormat, marker);
+ va_end(marker);
+
+} // ErrorItemSet
+
+
+/*+-------------------------------------------------------------------------+
+ | ErrorReset()
+ |
+ +-------------------------------------------------------------------------+*/
+void ErrorReset() {
+ ErrorFlag = FALSE;
+ lstrcpy(ErrorText, TEXT(""));
+
+} // ErrorReset
+
+
+/*+-------------------------------------------------------------------------+
+ | ErrorSet()
+ |
+ +-------------------------------------------------------------------------+*/
+void ErrorSet(LPTSTR szFormat, ...) {
+ va_list marker;
+
+ va_start(marker, szFormat);
+ wvsprintf(ErrorText, szFormat, marker);
+ ErrorFlag = TRUE;
+ va_end(marker);
+
+} // ErrorSet
+
+
+BOOL ErrorOccured() {
+ return ErrorFlag;
+
+} // ErrorOccured
+
+
+/*+-------------------------------------------------------------------------+
+ | ErrorBox()
+ |
+ +-------------------------------------------------------------------------+*/
+void ErrorBox(LPTSTR szFormat, ...) {
+ va_list marker;
+
+ va_start(marker, szFormat);
+ wvsprintf(ErrorText, szFormat, marker);
+
+ MessageBeep(MB_ICONASTERISK);
+ MessageBox(NULL, ErrorText, (LPTSTR) Lids(IDS_E_2), MB_TASKMODAL | MB_ICONEXCLAMATION | MB_OK);
+
+ va_end(marker);
+} // ErrorBox
+
+
+/*+-------------------------------------------------------------------------+
+ | ErrorBoxRetry()
+ |
+ +-------------------------------------------------------------------------+*/
+int ErrorBoxRetry(LPTSTR szFormat, ...) {
+ int ret;
+ LPVOID lpMessageBuffer;
+ va_list marker;
+
+ va_start(marker, szFormat);
+ wvsprintf(ErrorText, szFormat, marker);
+
+ FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, GetLastError(), 0,
+ (LPTSTR) &lpMessageBuffer, 0, NULL );
+
+ MessageBeep(MB_ICONASTERISK);
+ ret = MessageBox(NULL, (LPTSTR) lpMessageBuffer, ErrorText,
+ MB_TASKMODAL | MB_ICONEXCLAMATION | MB_RETRYCANCEL);
+ LocalFree(lpMessageBuffer);
+
+ va_end(marker);
+
+ return ret;
+} // ErrorBoxRetry
+
+
+/*+-------------------------------------------------------------------------+
+ | FileOpenBackup()
+ |
+ | Tries to open a file, if it already exists then creates a backup
+ | with the extension in the form (.001 to .999). It tries .001 first
+ | and if already used then tries .002, etc...
+ |
+ +-------------------------------------------------------------------------+*/
+HANDLE FileOpenBackup(CHAR *FileRoot, CHAR *FileExt) {
+ int ret;
+ HANDLE hFile = NULL;
+ DWORD dwFileNumber;
+ char FileName[MAX_PATH + 1];
+ char buffer[MAX_PATH + 1];
+ TCHAR FileNameW[MAX_PATH + 1];
+
+ wsprintfA(FileName, "%s%s", FileRoot, FileExt);
+
+ // Open, but fail if already exists.
+ hFile = CreateFileA( FileName, GENERIC_READ | GENERIC_WRITE, 0,
+ NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL );
+
+ // Check if Error - file exists
+ if(hFile == INVALID_HANDLE_VALUE) {
+ dwFileNumber = 0;
+
+ // Find next backup number...
+ // Files are backed up as .xxx where xxx is a number in the form .001,
+ // the first backup is stored as .001, second as .002, etc...
+ do {
+ dwFileNumber++;
+ wsprintfA(FileName, "%s%03u", FileRoot, dwFileNumber);
+
+ hFile = CreateFileA( FileName, GENERIC_READ, 0, NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (hFile != (HANDLE) INVALID_HANDLE_VALUE)
+ CloseHandle( hFile );
+
+ } while ( (hFile != INVALID_HANDLE_VALUE) );
+
+ // Rename the last log file to the first available number
+ wsprintfA( buffer, "%s%s", FileRoot, FileExt);
+ MoveFileA( buffer, FileName);
+
+ lstrcpyA(FileName, buffer);
+ // Create the new log file
+ hFile = CreateFileA( FileName, GENERIC_READ | GENERIC_WRITE, 0,
+ NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL );
+
+
+ if (hFile != (HANDLE) INVALID_HANDLE_VALUE)
+ CloseHandle( hFile );
+
+ } else
+ CloseHandle(hFile);
+
+ wsprintfA(FileName, "%s%s", FileRoot, FileExt);
+
+ // Now do the actual creation with error handling...
+ do {
+ ret = IDOK;
+ hFile = CreateFileA( FileName, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, NULL );
+
+ if (hFile == INVALID_HANDLE_VALUE) {
+ MultiByteToWideChar(CP_ACP, 0, FileName, -1, FileNameW, sizeof(FileNameW) );
+ ret = ErrorBoxRetry(Lids(IDS_E_13), FileNameW);
+ }
+
+ } while(ret == IDRETRY);
+
+ return(hFile);
+
+} // FileOpenBackup
+
+
+/*+-------------------------------------------------------------------------+
+ | GetTime()
+ |
+ +-------------------------------------------------------------------------+*/
+void GetTime(TCHAR *str) {
+ SYSTEMTIME st;
+ static TCHAR *aszDay[7];
+
+ aszDay[0] = Lids(IDS_SUNDAY);
+ aszDay[1] = Lids(IDS_MONDAY);
+ aszDay[2] = Lids(IDS_TUESDAY);
+ aszDay[3] = Lids(IDS_WEDNESDAY);
+ aszDay[4] = Lids(IDS_THURSDAY);
+ aszDay[5] = Lids(IDS_FRIDAY);
+ aszDay[6] = Lids(IDS_SATURDAY);
+
+ GetLocalTime(&st);
+
+ wsprintf(str, TEXT("%s, %02u/%02u/%4u (%02u:%02u:%02u)"),
+ aszDay[st.wDayOfWeek], st.wMonth,
+ st.wDay, st.wYear,
+ st.wHour, st.wMinute, st.wSecond);
+} // GetTime
+
+
+/*+-------------------------------------------------------------------------+
+ | WriteLog()
+ |
+ +-------------------------------------------------------------------------+*/
+DWORD WriteLog(HANDLE hFile, int Level, LPTSTR String) {
+ int ret;
+ DWORD wrote;
+ static char tmpStr[MAX_LOG_STR];
+ static char LogStr[MAX_LOG_STR];
+
+ // If the user canceled writing to the log, then don't keep trying
+ if (LogCancel)
+ return 1;
+
+ // Put ending NULL at correct place
+ Spaces[Level * 3] = '\0';
+
+ // Build up indented ANSI string to write out
+ lstrcpyA(tmpStr, Spaces);
+ WideCharToMultiByte(CP_ACP, 0, String, -1, LogStr, sizeof(LogStr), NULL, NULL);
+ lstrcatA(tmpStr, LogStr);
+
+ // reset for later writes
+ Spaces[Level * 3] = ' ';
+
+
+ // Now do the actual write with error handling...
+ do {
+ ret = IDOK;
+
+ if (!WriteFile(hFile, tmpStr, strlen(tmpStr), &wrote, NULL)) {
+ ret = ErrorBoxRetry(Lids(IDS_E_NWLOG));
+ }
+
+ } while(ret == IDRETRY);
+
+ if (ret == IDCANCEL) {
+ LogCancel = TRUE;
+ return 1;
+ }
+ else
+ return 0;
+
+} // WriteLog
+
+
+/*+-------------------------------------------------------------------------+
+ | LogHeader()
+ |
+ +-------------------------------------------------------------------------+*/
+void LogHeader(HANDLE hFile, TCHAR *Title) {
+ DWORD ret;
+ DWORD wrote;
+ static TCHAR time[40];
+ static TCHAR tmpStr[MAX_LOG_STR];
+ static TCHAR tmpStr2[MAX_LOG_STR];
+ static TCHAR *line;
+
+ ret = WriteLog(hFile, 0, Lids(IDS_LINE));
+ wsprintf(tmpStr, Lids(IDS_BRACE), Title);
+
+ if (!ret)
+ ret = WriteLog(hFile, 0, tmpStr);
+
+ if (!ret)
+ ret = WriteLog(hFile, 0, Lids(IDS_LINE));
+
+ GetTime(time);
+ wsprintf(tmpStr, Lids(IDS_L_97), time);
+
+ if (!ret)
+ ret = WriteLog(hFile, 0, tmpStr);
+
+ wrote = sizeof(tmpStr2);
+ GetComputerName(tmpStr2, &wrote);
+ wsprintf(tmpStr, Lids(IDS_L_98), tmpStr2);
+
+ if (!ret)
+ WriteLog(hFile, 0, tmpStr);
+
+ wrote = sizeof(tmpStr);
+ WNetGetUser(NULL, tmpStr2, &wrote);
+ wsprintf(tmpStr, Lids(IDS_L_99), tmpStr2);
+
+ if (!ret)
+ ret = WriteLog(hFile, 0, tmpStr);
+
+ wsprintf(tmpStr, Lids(IDS_L_100), VER_HI, VER_LOW);
+
+ if (!ret)
+ ret = WriteLog(hFile, 0, tmpStr);
+
+ if (!ret)
+ ret = WriteLog(hFile, 0, Lids(IDS_LINE));
+
+} // LogHeader
+
+
+/*+-------------------------------------------------------------------------+
+ | LogInit()
+ |
+ +-------------------------------------------------------------------------+*/
+void LogInit() {
+ lstrcpyA(LogFileName, FILENAME_LOG);
+ lstrcpyA(ErrorLogFileName, FILENAME_ERROR);
+ lstrcpyA(SummaryLogFileName, FILENAME_SUMMARY);
+
+ LogCancel = FALSE;
+
+ hErr = FileOpenBackup(ERR_FILENAME, STD_EXT);
+ hLog = FileOpenBackup(LOG_FILENAME, STD_EXT);
+ hSummary = FileOpenBackup(SUMMARY_FILENAME, STD_EXT);
+
+ LogHeader(hErr, Lids(IDS_L_101));
+ CloseHandle(hErr);
+
+ LogHeader(hLog, Lids(IDS_L_102));
+ CloseHandle(hLog);
+
+ LogHeader(hSummary, Lids(IDS_L_103));
+ CloseHandle(hSummary);
+
+} // LogInit
+
+
+/*+-------------------------------------------------------------------------+
+ | LogWriteLog()
+ |
+ +-------------------------------------------------------------------------+*/
+void LogWriteLog(int Level, LPTSTR szFormat, ...) {
+ va_list marker;
+
+ va_start(marker, szFormat);
+ wvsprintf(tmpStr, szFormat, marker);
+
+ hLog = CreateFileA( LogFileName, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
+ SetFilePointer(hLog, 0, NULL, FILE_END);
+ WriteLog(hLog, Level, tmpStr);
+ CloseHandle(hLog);
+ va_end(marker);
+
+} // LogWriteLog
+
+
+/*+-------------------------------------------------------------------------+
+ | LogWritErr()
+ |
+ +-------------------------------------------------------------------------+*/
+void LogWriteErr(LPTSTR szFormat, ...) {
+ int Indent = 3;
+ DWORD wrote;
+ static char LogStr[MAX_LOG_STR];
+ va_list marker;
+
+ va_start(marker, szFormat);
+ wvsprintf(tmpStr, szFormat, marker);
+
+ hErr = CreateFileA( ErrorLogFileName, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
+ SetFilePointer(hErr, 0, NULL, FILE_END);
+
+ if (!ContextWritten) {
+ ContextWritten = TRUE;
+ WideCharToMultiByte(CP_ACP, 0, ErrorContext, -1, LogStr, sizeof(LogStr), NULL, NULL);
+ WriteFile(hErr, LogStr, strlen(LogStr), &wrote, NULL);
+ }
+
+ if (CategorySet && !CategoryWritten) {
+ CategoryWritten = TRUE;
+ Spaces[3] = '\0';
+ WriteFile(hLog, Spaces, strlen(Spaces), &wrote, NULL);
+ WideCharToMultiByte(CP_ACP, 0, ErrorCategory, -1, LogStr, sizeof(LogStr), NULL, NULL);
+ WriteFile(hErr, LogStr, strlen(LogStr), &wrote, NULL);
+ Spaces[3] = ' ';
+ }
+
+ if (ItemSet) {
+ Spaces[6] = '\0';
+ WriteFile(hLog, Spaces, strlen(Spaces), &wrote, NULL);
+ WideCharToMultiByte(CP_ACP, 0, ErrorItem, -1, LogStr, sizeof(LogStr), NULL, NULL);
+ WriteFile(hErr, LogStr, strlen(LogStr), &wrote, NULL);
+ Spaces[6] = ' ';
+ }
+
+ if (CategorySet)
+ Indent +=3;
+
+ if (ItemSet)
+ Indent +=3;
+
+ Spaces[Indent] = '\0';
+ WriteFile(hLog, Spaces, strlen(Spaces), &wrote, NULL);
+ WideCharToMultiByte(CP_ACP, 0, tmpStr, -1, LogStr, sizeof(LogStr), NULL, NULL);
+ WriteFile(hErr, LogStr, strlen(LogStr), &wrote, NULL);
+ Spaces[Indent] = ' ';
+ CloseHandle(hErr);
+ va_end(marker);
+
+} // LogWriteErr
+
+
+/*+-------------------------------------------------------------------------+
+ | LogWriteSummary()
+ |
+ +-------------------------------------------------------------------------+*/
+void LogWriteSummary(int Level, LPTSTR szFormat, ...) {
+ DWORD wrote;
+ static char LogStr[MAX_LOG_STR];
+ va_list marker;
+
+ va_start(marker, szFormat);
+ Spaces[Level * 3] = '\0';
+ wvsprintf(tmpStr, szFormat, marker);
+ WideCharToMultiByte(CP_ACP, 0, tmpStr, -1, LogStr, sizeof(LogStr), NULL, NULL);
+
+ hSummary = CreateFileA( SummaryLogFileName, GENERIC_WRITE, 0,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
+ SetFilePointer(hSummary, 0, NULL, FILE_END);
+ WriteFile(hLog, Spaces, strlen(Spaces), &wrote, NULL);
+ WriteFile(hSummary, LogStr, strlen(LogStr), &wrote, NULL);
+ Spaces[Level * 3] = ' ';
+ CloseHandle(hSummary);
+ va_end(marker);
+
+} // LogWriteSummary
+
+
+
+/*+-------------------------------------------------------------------------+
+ | DlgLogging()
+ |
+ +-------------------------------------------------------------------------+*/
+LRESULT CALLBACK DlgLogging(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
+ HANDLE hFile;
+ static BOOL UserLogging, FileLogging, ErrorFlag;
+ int wmId, wmEvent;
+ static char CmdLine[256];
+ HWND hCtrl;
+
+ switch (message) {
+ case WM_INITDIALOG:
+ // Center the dialog over the application window
+ CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
+
+ // Toggle User Logging Control
+ UserLogging = VerboseULogging;
+ hCtrl = GetDlgItem(hDlg, IDC_CHKUVERBOSE);
+ if (VerboseULogging)
+ SendMessage(hCtrl, BM_SETCHECK, 1, 0);
+ else
+ SendMessage(hCtrl, BM_SETCHECK, 0, 0);
+
+ // Toggle File Logging Control
+ FileLogging = VerboseFLogging;
+ hCtrl = GetDlgItem(hDlg, IDC_CHKFVERBOSE);
+ if (VerboseFLogging)
+ SendMessage(hCtrl, BM_SETCHECK, 1, 0);
+ else
+ SendMessage(hCtrl, BM_SETCHECK, 0, 0);
+
+ // Toggle Error Popup Control
+ ErrorFlag = ErrorBreak;
+ hCtrl = GetDlgItem(hDlg, IDC_CHKERROR);
+ if (ErrorBreak)
+ SendMessage(hCtrl, BM_SETCHECK, 1, 0);
+ else
+ SendMessage(hCtrl, BM_SETCHECK, 0, 0);
+
+ hCtrl = GetDlgItem(hDlg, IDC_VIEWLOG);
+
+ // check if logfile exists, if it does allow log file viewing...
+ hFile = CreateFileA( FILENAME_LOG, GENERIC_READ, 0, NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (hFile != (HANDLE) INVALID_HANDLE_VALUE)
+ CloseHandle( hFile );
+ else
+ EnableWindow(hCtrl, FALSE);
+
+ return (TRUE);
+
+ case WM_COMMAND:
+ wmId = LOWORD(wParam);
+ wmEvent = HIWORD(wParam);
+
+ switch (wmId) {
+ case IDOK:
+ VerboseULogging = UserLogging;
+ VerboseFLogging = FileLogging;
+ ErrorBreak = ErrorFlag;
+ EndDialog(hDlg, 0);
+ return (TRUE);
+ break;
+
+ case IDCANCEL:
+ EndDialog(hDlg, 0);
+ return (TRUE);
+
+ case IDHELP:
+ WinHelp(hDlg, HELP_FILE, HELP_CONTEXT, (DWORD) IDC_HELP_LOGGING);
+ break;
+
+ case IDC_VIEWLOG:
+ lstrcpyA(CmdLine, "LogView ");
+ lstrcatA(CmdLine, "Error.LOG Summary.LOG LogFile.LOG");
+ WinExec(CmdLine, SW_SHOW);
+ return (TRUE);
+ break;
+
+ case IDC_CHKUVERBOSE:
+ UserLogging = !UserLogging;
+ return (TRUE);
+ break;
+
+ case IDC_CHKFVERBOSE:
+ FileLogging = !FileLogging;
+ return (TRUE);
+ break;
+
+ case IDC_CHKERROR:
+ ErrorFlag = !ErrorFlag;
+ return (TRUE);
+ break;
+ }
+
+ break;
+ }
+
+ return (FALSE); // Didn't process the message
+
+ lParam;
+} // DlgLogging
+
+
+/*+-------------------------------------------------------------------------+
+ | LogOptionsInit()
+ |
+ +-------------------------------------------------------------------------+*/
+void LogOptionsInit() {
+ ErrorBreak = FALSE;
+ VerboseULogging = TRUE;
+ VerboseFLogging = FALSE;
+} // LogOptionsInit
+
+
+/*+-------------------------------------------------------------------------+
+ | LogOptionsSave()
+ |
+ +-------------------------------------------------------------------------+*/
+void LogOptionsSave( HANDLE hFile ) {
+ DWORD wrote;
+
+ WriteFile(hFile, &ErrorBreak, sizeof(ErrorBreak), &wrote, NULL);
+ WriteFile(hFile, &VerboseFLogging, sizeof(VerboseFLogging), &wrote, NULL);
+ WriteFile(hFile, &VerboseULogging, sizeof(VerboseULogging), &wrote, NULL);
+} // LogOptionsSave
+
+
+/*+-------------------------------------------------------------------------+
+ | LogOptionsLoad()
+ |
+ +-------------------------------------------------------------------------+*/
+void LogOptionsLoad( HANDLE hFile ) {
+ DWORD wrote;
+
+ ReadFile(hFile, &ErrorBreak, sizeof(ErrorBreak), &wrote, NULL);
+ ReadFile(hFile, &VerboseFLogging, sizeof(VerboseFLogging), &wrote, NULL);
+ ReadFile(hFile, &VerboseULogging, sizeof(VerboseULogging), &wrote, NULL);
+
+#ifdef DEBUG
+dprintf(TEXT("<Log Options Load>\n"));
+dprintf(TEXT(" Error Break: %lx\n"), ErrorBreak);
+dprintf(TEXT(" Verbose File Logging: %lx\n"), VerboseFLogging);
+dprintf(TEXT(" Verbose User Logging: %lx\n\n"), VerboseULogging);
+#endif
+} // LogOptionsLoad
+
+
+/*+-------------------------------------------------------------------------+
+ | PopupOnError()
+ |
+ +-------------------------------------------------------------------------+*/
+BOOL PopupOnError() {
+ return ErrorBreak;
+
+} // PopupOnError
+
+
+/*+-------------------------------------------------------------------------+
+ | VerboseFileLogging()
+ |
+ +-------------------------------------------------------------------------+*/
+BOOL VerboseFileLogging() {
+ return VerboseFLogging;
+
+} // VerboseFileLogging
+
+
+/*+-------------------------------------------------------------------------+
+ | VerboseUserLogging()
+ |
+ +-------------------------------------------------------------------------+*/
+BOOL VerboseUserLogging() {
+ return VerboseULogging;
+
+} // VerboseUserLogging
+
+
+/*+-------------------------------------------------------------------------+
+ | DoLoggingDlg()
+ |
+ +-------------------------------------------------------------------------+*/
+void DoLoggingDlg(HWND hDlg) {
+ DLGPROC lpfnDlg;
+
+ lpfnDlg = MakeProcInstance((DLGPROC)DlgLogging, hInst);
+ DialogBox(hInst, TEXT("DlgLogging"), hDlg, lpfnDlg) ;
+ FreeProcInstance(lpfnDlg);
+
+} // DoLoggingDlg
diff --git a/private/nw/convert/nwconv/nwlog.h b/private/nw/convert/nwconv/nwlog.h
new file mode 100644
index 000000000..ceb44e407
--- /dev/null
+++ b/private/nw/convert/nwconv/nwlog.h
@@ -0,0 +1,39 @@
+/*+-------------------------------------------------------------------------+
+ | Copyright 1993-1994 (C) Microsoft Corporation - All rights reserved. |
+ +-------------------------------------------------------------------------+*/
+
+#ifndef _NWLOG_
+#define _NWLOG_
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+void LogInit();
+void LogWriteLog(int Level, LPTSTR szFormat, ...);
+void LogWriteErr(LPTSTR szFormat, ...);
+void LogWriteSummary(int Level, LPTSTR szFormat, ...);
+void GetTime(TCHAR *str);
+void ErrorResetAll();
+void ErrorContextSet(LPTSTR szFormat, ...);
+void ErrorCategorySet(LPTSTR szFormat, ...);
+void ErrorItemSet(LPTSTR szFormat, ...);
+void ErrorReset();
+void ErrorSet(LPTSTR szFormat, ...);
+BOOL ErrorOccured();
+void ErrorBox(LPTSTR szFormat, ...);
+int ErrorBoxRetry(LPTSTR szFormat, ...);
+
+void LogOptionsInit();
+void LogOptionsSave( HANDLE hFile );
+void LogOptionsLoad( HANDLE hFile );
+BOOL PopupOnError();
+BOOL VerboseFileLogging();
+BOOL VerboseUserLogging();
+void DoLoggingDlg(HWND hDlg);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/nw/convert/nwconv/nwnetapi.c b/private/nw/convert/nwconv/nwnetapi.c
new file mode 100644
index 000000000..2815484ee
--- /dev/null
+++ b/private/nw/convert/nwconv/nwnetapi.c
@@ -0,0 +1,2692 @@
+/*++
+
+Copyright (c) 1993-1995 Microsoft Corporation
+
+Module Name:
+
+ nwnetapi.c
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) 16-Jun-1994
+
+Revision History:
+
+--*/
+
+
+#include "globals.h"
+
+#include <nwapi32.h>
+#include "nwconv.h"
+#include "convapi.h"
+#include "nwnetapi.h"
+#include "statbox.h"
+
+#define SUPERVISOR "SUPERVISOR"
+#define ACCOUNT_LOCKOUT "ACCT_LOCKOUT"
+#define GROUP_MEMBERS "GROUP_MEMBERS"
+#define GROUPS_IM_IN "GROUPS_I'M_IN"
+#define IDENTIFICATION "IDENTIFICATION"
+#define LOGIN_CONTROL "LOGIN_CONTROL"
+#define PS_OPERATORS "PS_OPERATORS"
+#define SECURITY_EQUALS "SECURITY_EQUALS"
+#define USER_DEFAULTS "USER_DEFAULTS"
+#define MS_EXTENDED_NCPS "MS_EXTENDED_NCPS"
+#define FPNW_PDC "FPNWPDC"
+
+#ifdef DEBUG
+int ErrorBoxRetry(LPTSTR szFormat, ...);
+#endif
+
+// Couple of NetWare specific data structures - see NetWare programming books for
+// info on these structures
+#pragma pack(1)
+typedef struct tagLoginControl {
+ BYTE byAccountExpires[3];
+ BYTE byAccountDisabled;
+ BYTE byPasswordExpires[3];
+ BYTE byGraceLogins;
+ WORD wPasswordInterval;
+ BYTE byGraceLoginReset;
+ BYTE byMinPasswordLength;
+ WORD wMaxConnections;
+ BYTE byLoginTimes[42];
+ BYTE byLastLogin[6];
+ BYTE byRestrictions;
+ BYTE byUnused;
+ long lMaxDiskBlocks;
+ WORD wBadLogins;
+ LONG lNextResetTime;
+ BYTE byBadLoginAddr[12];
+}; // tagLoginControl
+#pragma pack()
+
+
+typedef struct tagAccountBalance {
+ long lBalance;
+ long lCreditLimit;
+}; // tagAccountBalance
+
+
+typedef struct tagUserDefaults {
+ BYTE byAccountExpiresYear;
+ BYTE byAccountExpiresMonth;
+ BYTE byAccountExpiresDay;
+ BYTE byRestrictions;
+ WORD wPasswordInterval;
+ BYTE byGraceLoginReset;
+ BYTE byMinPasswordLength;
+ WORD wMaxConnections;
+ BYTE byLoginTimes[42];
+ long lBalance;
+ long lCreditLimit;
+ long lMaxDiskBlocks;
+}; // tagUserDefaults
+
+
+// define the mapping for FILE objects. Note: we only use GENERIC and
+// STANDARD bits!
+RIGHTS_MAPPING FileRightsMapping = {
+ 0,
+ { FILE_GENERIC_READ,
+ FILE_GENERIC_WRITE,
+ FILE_GENERIC_EXECUTE,
+ FILE_ALL_ACCESS
+ },
+ { { NW_FILE_READ, GENERIC_READ},
+ { NW_FILE_WRITE, GENERIC_WRITE},
+ { NW_FILE_CREATE, 0},
+ { NW_FILE_DELETE, GENERIC_WRITE|DELETE},
+ { NW_FILE_PERM, WRITE_DAC},
+ { NW_FILE_SCAN, 0},
+ { NW_FILE_MODIFY, GENERIC_WRITE},
+ { NW_FILE_SUPERVISOR, GENERIC_ALL},
+ { 0, 0 }
+ }
+} ;
+
+RIGHTS_MAPPING DirRightsMapping = {
+ CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE,
+ { FILE_GENERIC_READ,
+ FILE_GENERIC_WRITE,
+ FILE_GENERIC_EXECUTE,
+ FILE_ALL_ACCESS
+ },
+ { { NW_FILE_READ, GENERIC_READ|GENERIC_EXECUTE},
+ { NW_FILE_WRITE, GENERIC_WRITE},
+ { NW_FILE_CREATE, GENERIC_WRITE},
+ { NW_FILE_DELETE, DELETE},
+ { NW_FILE_PERM, WRITE_DAC},
+ { NW_FILE_SCAN, GENERIC_READ},
+ { NW_FILE_MODIFY, GENERIC_WRITE},
+ { NW_FILE_SUPERVISOR, GENERIC_ALL},
+ { 0, 0 }
+ }
+} ;
+
+VOID UserInfoLog(LPTSTR UserName, struct tagLoginControl tag);
+VOID Moveit(NWCONN_HANDLE Conn, LPTSTR UserName);
+VOID UserRecInit(NT_USER_INFO *UserInfo);
+BOOL IsNCPServerFPNW(NWCONN_HANDLE Conn);
+
+static NWCONN_HANDLE CachedConn = 0;
+
+static TCHAR CachedServer[MAX_SERVER_NAME_LEN + 1];
+static DWORD CachedServerTime = 0xffffffff; // minutes since 1985...
+
+typedef struct _PRINT_SERVER_BUFFER {
+ TCHAR Name[20];
+} PRINT_SERVER_BUFFER;
+
+typedef struct _PRINT_SERVER_LIST {
+ ULONG Count;
+ PRINT_SERVER_BUFFER PSList[];
+} PRINT_SERVER_LIST;
+
+
+
+/////////////////////////////////////////////////////////////////////////
+int
+NWGetMaxServerNameLen()
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ return MAX_SERVER_NAME_LEN;
+
+} // NWGetMaxServerNameLen
+
+
+/////////////////////////////////////////////////////////////////////////
+int
+NWGetMaxUserNameLen()
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ return MAX_USER_NAME_LEN;
+
+} // NWGetMaxUserNameLen
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+NWServerFree()
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+
+ if (CachedConn)
+ NWDetachFromFileServer(CachedConn);
+
+ CachedConn = 0;
+
+ return (0);
+
+} // NWServerFree
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+NWServerSet(
+ LPTSTR FileServer
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ NWLOCAL_SCOPE ScopeFlag = 0;
+ NWCONN_HANDLE Conn = 0;
+ NWCCODE ret = 0;
+ char szAnsiFileServer[MAX_SERVER_NAME_LEN + 1];
+
+ NWServerFree();
+
+ lstrcpy(CachedServer, FileServer);
+ CharToOem(FileServer, szAnsiFileServer);
+
+ ret = NWAttachToFileServer(szAnsiFileServer, ScopeFlag, &Conn);
+
+ if (!ret) {
+ CachedConn = Conn;
+ NWServerTimeGet();
+ }
+
+ return ((DWORD) ret);
+
+} // NWServerSet
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+NWUseDel(
+ LPTSTR ServerName
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ static TCHAR LocServer[MAX_SERVER_NAME_LEN+3];
+
+ NWServerFree();
+ wsprintf(LocServer, Lids(IDS_S_27), ServerName);
+ WNetCancelConnection2(LocServer, 0, FALSE);
+
+} // NWUseDel
+
+
+/////////////////////////////////////////////////////////////////////////
+BOOL
+NWUserNameValidate(
+ LPTSTR szUserName
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ TCHAR UserName[MAX_USER_NAME_LEN + 1];
+ DWORD Size;
+
+ // if same as logged on user then don't convert or overwrite
+ Size = sizeof(UserName);
+ WNetGetUser(NULL, UserName, &Size);
+ if (!lstrcmpi(szUserName, UserName))
+ return FALSE;
+
+ // Now check for other special names
+ if (!lstrcmpi(szUserName, Lids(IDS_S_28)))
+ return FALSE;
+
+ if (!lstrcmpi(szUserName, Lids(IDS_S_29)))
+ return FALSE;
+
+ if (!lstrcmpi(szUserName, Lids(IDS_S_30)))
+ return FALSE;
+
+ return TRUE;
+
+} // NWUserNameValidate
+
+
+/////////////////////////////////////////////////////////////////////////
+BOOL
+NWGroupNameValidate(
+ LPTSTR szGroupName
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+
+ if (!lstrcmpi(szGroupName, Lids(IDS_S_31)))
+ return FALSE;
+
+ if (!lstrcmpi(szGroupName, Lids(IDS_S_28)))
+ return FALSE;
+
+ return TRUE;
+
+} // NWGroupNameValidate
+
+
+/////////////////////////////////////////////////////////////////////////
+LPTSTR
+NWSpecialNamesMap(
+ LPTSTR Name
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG i;
+ static BOOL InitStrings = FALSE;
+ static LPTSTR SpecialNames[5];
+ static LPTSTR SpecialMap[5];
+
+ if (!InitStrings) {
+ InitStrings = TRUE;
+ SpecialNames[0] = Lids(IDS_S_28);
+ SpecialNames[1] = Lids(IDS_S_30);
+ SpecialNames[2] = Lids(IDS_S_31);
+ SpecialNames[3] = Lids(IDS_S_29);
+ SpecialNames[4] = NULL;
+
+ SpecialMap[0] = Lids(IDS_S_32);
+ SpecialMap[1] = Lids(IDS_S_32);
+ SpecialMap[2] = Lids(IDS_S_33);
+ SpecialMap[3] = Lids(IDS_S_29);
+ SpecialMap[4] = NULL;
+ }
+
+ i = 0;
+ while(SpecialNames[i] != NULL) {
+ if (!lstrcmpi(SpecialNames[i], Name)) {
+ return SpecialMap[i];
+ }
+
+ i++;
+ }
+
+ return Name;
+
+} // NWSpecialNamesMap
+
+
+/////////////////////////////////////////////////////////////////////////
+BOOL
+NWIsAdmin(
+ LPTSTR UserName
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ char szAnsiUserName[MAX_USER_NAME_LEN];
+ NWCCODE ret;
+
+ CharToOem(UserName, szAnsiUserName);
+ ret = NWIsObjectInSet(CachedConn, szAnsiUserName, OT_USER, SECURITY_EQUALS,
+ SUPERVISOR, OT_USER);
+
+ if (ret == SUCCESSFUL)
+ return TRUE;
+ else
+ return FALSE;
+
+} // NWIsAdmin
+
+
+/////////////////////////////////////////////////////////////////////////
+BOOL
+NWObjectNameGet(
+ DWORD ObjectID,
+ LPTSTR ObjectName
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ char szAnsiObjectName[MAX_USER_NAME_LEN + 1];
+ WORD wFoundUserType = 0;
+ NWCCODE ret;
+
+ lstrcpy(ObjectName, TEXT(""));
+
+ if (!(ret = NWGetObjectName(CachedConn, ObjectID, szAnsiObjectName, &wFoundUserType))) {
+
+ //
+ // Got user - now convert and save off the information
+ //
+ OemToChar(szAnsiObjectName, ObjectName);
+ }
+
+ if (ret == SUCCESSFUL)
+ return TRUE;
+ else
+ return FALSE;
+
+} // NWObjectNameGet
+
+
+#define DEF_NUM_RECS 200
+/////////////////////////////////////////////////////////////////////////
+DWORD
+NWUsersEnum(
+ USER_LIST **lpUsers,
+ BOOL Display
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ USER_LIST *Users = NULL;
+ USER_BUFFER *UserBuffer = NULL;
+ DWORD NumRecs = DEF_NUM_RECS; // Default 200 names
+ DWORD Count = 0;
+ DWORD status = 0;
+ char szAnsiUserName[MAX_USER_NAME_LEN + 1];
+ TCHAR szUserName[MAX_USER_NAME_LEN + 1];
+ WORD wFoundUserType = 0;
+ DWORD dwObjectID = 0xFFFFFFFFL;
+ BYTE byPropertiesFlag = 0;
+ BYTE byObjectFlag = 0;
+ BYTE byObjectSecurity = 0;
+ NWCCODE ret;
+
+ if (Display)
+ Status_ItemLabel(Lids(IDS_S_44));
+
+ Users = (USER_LIST *) AllocMemory(sizeof(USER_LIST) + (sizeof(USER_BUFFER) * NumRecs));
+
+ if (!Users) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ } else {
+ UserBuffer = Users->UserBuffer;
+
+ // init to NULL so doesn't have garbage if call fails
+ lstrcpyA(szAnsiUserName, "");
+
+ // Loop through bindery getting all the users.
+ while ((ret = NWScanObject(CachedConn, "*", OT_USER, &dwObjectID, szAnsiUserName,
+ &wFoundUserType, &byPropertiesFlag,
+ &byObjectFlag, &byObjectSecurity)) == SUCCESSFUL) {
+
+ // Got user - now convert and save off the information
+ OemToChar(szAnsiUserName, szUserName);
+
+ if (NWUserNameValidate(szUserName)) {
+ if (Display)
+ Status_Item(szUserName);
+
+ lstrcpy(UserBuffer[Count].Name, szUserName);
+ lstrcpy(UserBuffer[Count].NewName, szUserName);
+ Count++;
+ }
+
+ // Check if we have to re-allocate buffer
+ if (Count >= NumRecs) {
+ NumRecs += DEF_NUM_RECS;
+ Users = (USER_LIST *) ReallocMemory((HGLOBAL) Users, sizeof(USER_LIST) + (sizeof(USER_BUFFER) * NumRecs));
+
+ if (!Users) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+ }
+
+ UserBuffer = Users->UserBuffer;
+ }
+
+ }
+
+ // Gotta clear this out from the last loop
+ if (Count)
+ ret = 0;
+
+ }
+
+ // check if error occured...
+ if (ret)
+ status = ret;
+
+ // Now slim down the list to just what we need.
+ if (!status) {
+ Users = (USER_LIST *) ReallocMemory((HGLOBAL) Users, sizeof(USER_LIST) + (sizeof(USER_BUFFER)* Count));
+
+ if (!Users)
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ else {
+ // Sort the server list before putting it in the dialog
+ UserBuffer = Users->UserBuffer;
+ qsort((void *) UserBuffer, (size_t) Count, sizeof(USER_BUFFER), UserListCompare);
+ }
+ }
+
+ if (Users != NULL)
+ Users->Count = Count;
+
+ *lpUsers = Users;
+
+ return status;
+
+} // NWUsersEnum
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+NWGroupsEnum(
+ GROUP_LIST **lpGroups,
+ BOOL Display
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ GROUP_LIST *Groups = NULL;
+ GROUP_BUFFER *GroupBuffer;
+ DWORD NumRecs = DEF_NUM_RECS; // Default 200 names
+ DWORD Count = 0;
+ DWORD status = 0;
+ char szAnsiGroupName[MAX_GROUP_NAME_LEN + 1];
+ TCHAR szGroupName[MAX_GROUP_NAME_LEN + 1];
+ WORD wFoundGroupType = 0;
+ DWORD dwObjectID = 0xFFFFFFFFL;
+ BYTE byPropertiesFlag = 0;
+ BYTE byObjectFlag = 0;
+ BYTE byObjectSecurity = 0;
+ NWCCODE ret;
+
+ if (Display)
+ Status_ItemLabel(Lids(IDS_S_45));
+
+ Groups = (GROUP_LIST *) AllocMemory(sizeof(GROUP_LIST) + (sizeof(GROUP_BUFFER) * NumRecs));
+
+ if (!Groups) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ } else {
+ GroupBuffer = Groups->GroupBuffer;
+
+ // init to NULL so doesn't have garbage if call fails
+ lstrcpyA(szAnsiGroupName, "");
+
+ // Loop through bindery getting all the users.
+ while ((ret = NWScanObject(CachedConn, "*", OT_USER_GROUP, &dwObjectID, szAnsiGroupName,
+ &wFoundGroupType, &byPropertiesFlag,
+ &byObjectFlag, &byObjectSecurity)) == SUCCESSFUL) {
+
+ // Got user - now convert and save off the information
+ OemToChar(szAnsiGroupName, szGroupName);
+
+ if (NWGroupNameValidate(szGroupName)) {
+ if (Display)
+ Status_Item(szGroupName);
+
+ lstrcpy(GroupBuffer[Count].Name, szGroupName);
+ lstrcpy(GroupBuffer[Count].NewName, szGroupName);
+ Count++;
+ }
+
+ // Check if we have to re-allocate buffer
+ if (Count >= NumRecs) {
+ NumRecs += DEF_NUM_RECS;
+ Groups = (GROUP_LIST *) ReallocMemory((HGLOBAL) Groups, sizeof(GROUP_LIST) + (sizeof(GROUP_BUFFER) * NumRecs));
+
+ if (!Groups) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+ }
+
+ GroupBuffer = Groups->GroupBuffer;
+ }
+
+ }
+
+ // Gotta clear this out from the last loop
+ if (Count)
+ ret = 0;
+
+ }
+
+ // check if error occured...
+ if (ret)
+ status = ret;
+
+ // Now slim down the list to just what we need.
+ if (!status) {
+ Groups = (GROUP_LIST *) ReallocMemory((HGLOBAL) Groups, sizeof(GROUP_LIST) + (sizeof(GROUP_BUFFER) * Count));
+
+ if (!Groups)
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ else {
+ // Sort the server list before putting it in the dialog
+ GroupBuffer = Groups->GroupBuffer;
+ qsort((void *) GroupBuffer, (size_t) Count, sizeof(GROUP_BUFFER), UserListCompare);
+ }
+ }
+
+ if (Groups != NULL)
+ Groups->Count = Count;
+
+ *lpGroups = Groups;
+
+ return status;
+
+} // NWGroupsEnum
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+NWGroupUsersEnum(
+ LPTSTR GroupName,
+ USER_LIST **lpUsers
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ USER_LIST *Users = NULL;
+ USER_BUFFER *UserBuffer;
+ DWORD NumRecs = DEF_NUM_RECS; // Default 200 names
+ DWORD Count = 0;
+ DWORD i = 0;
+ DWORD status = 0;
+ char szAnsiUserName[MAX_USER_NAME_LEN + 1];
+ char szAnsiGroupName[MAX_GROUP_NAME_LEN + 1];
+ TCHAR szUserName[MAX_USER_NAME_LEN + 1];
+ WORD wFoundUserType = 0;
+ BYTE byPropertyFlags = 0;
+ BYTE byObjectFlag = 0;
+ BYTE byObjectSecurity = 0;
+ UCHAR Segment = 1;
+ DWORD bySegment[32];
+ BYTE byMoreSegments;
+ NWCCODE ret;
+
+ Users = (USER_LIST *) AllocMemory(sizeof(USER_LIST) + (sizeof(USER_BUFFER) * NumRecs));
+
+ if (!Users) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ } else {
+ UserBuffer = Users->UserBuffer;
+
+ // init to NULL so doesn't have garbage if call fails
+ lstrcpyA(szAnsiUserName, "");
+ CharToOem(GroupName, szAnsiGroupName);
+
+ // Loop through bindery getting all the users.
+ do {
+ if (!(ret = NWReadPropertyValue(CachedConn, szAnsiGroupName, OT_USER_GROUP, GROUP_MEMBERS,
+ Segment, (BYTE *) bySegment, &byMoreSegments, &byPropertyFlags))) {
+
+ Segment++;
+ // loop through properties converting them to user names
+ i = 0;
+ while ((bySegment[i]) && (i < 32)) {
+ if (!(ret = NWGetObjectName(CachedConn, bySegment[i], szAnsiUserName, &wFoundUserType))) {
+ // Got user - now convert and save off the information
+ OemToChar(szAnsiUserName, szUserName);
+
+ lstrcpy(UserBuffer[Count].Name, szUserName);
+ lstrcpy(UserBuffer[Count].NewName, szUserName);
+ Count++;
+
+ // Check if we have to re-allocate buffer
+ if (Count >= NumRecs) {
+ NumRecs += DEF_NUM_RECS;
+ Users = (USER_LIST *) ReallocMemory((HGLOBAL) Users, sizeof(USER_LIST) + (sizeof(USER_BUFFER) * NumRecs));
+
+ if (!Users) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+ }
+
+ UserBuffer = Users->UserBuffer;
+ }
+ }
+ i++;
+ }
+
+ } else // if NWReadPropertyValue
+ byMoreSegments = 0;
+
+ } while (byMoreSegments);
+
+ // Gotta clear this out from the last loop
+ if (Count)
+ ret = 0;
+
+ }
+
+ // check if error occured...
+ if (ret)
+ status = ret;
+
+ // Now slim down the list to just what we need.
+ if (!status) {
+ Users = (USER_LIST *) ReallocMemory((HGLOBAL) Users, sizeof(USER_LIST) + (sizeof(USER_BUFFER) * Count));
+
+ if (!Users)
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ else {
+ // Sort the server list before putting it in the dialog
+ UserBuffer = Users->UserBuffer;
+ qsort((void *) UserBuffer, (size_t) Count, sizeof(USER_BUFFER), UserListCompare);
+ }
+ }
+
+ if (Users != NULL)
+ Users->Count = Count;
+
+ *lpUsers = Users;
+
+ return status;
+
+} // NWGroupUsersEnum
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+NWUserEquivalenceEnum(
+ LPTSTR UserName,
+ USER_LIST **lpUsers
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ USER_LIST *Users;
+ USER_BUFFER *UserBuffer;
+ DWORD NumRecs = DEF_NUM_RECS; // Default 200 names
+ DWORD Count = 0;
+ DWORD i = 0;
+ DWORD status = 0;
+ char szAnsiUserName[MAX_USER_NAME_LEN + 1];
+ char szAnsiName[MAX_GROUP_NAME_LEN + 1];
+ TCHAR szUserName[MAX_USER_NAME_LEN + 1];
+ WORD wFoundUserType = 0;
+ DWORD dwObjectID = 0xFFFFFFFFL;
+ BYTE byPropertyFlags = 0;
+ BYTE byObjectFlag = 0;
+ BYTE byObjectSecurity = 0;
+ UCHAR Segment = 1;
+ DWORD bySegment[32];
+ BYTE byMoreSegments;
+ NWCCODE ret;
+
+ Users = (USER_LIST *) AllocMemory(sizeof(USER_LIST) + (sizeof(USER_BUFFER) * NumRecs));
+
+ if (!Users) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ } else {
+ UserBuffer = Users->UserBuffer;
+
+ // init to NULL so doesn't have garbage if call fails
+ lstrcpyA(szAnsiUserName, "");
+ CharToOem(UserName, szAnsiName);
+
+ // Loop through bindery getting all the users.
+ do {
+ if (!(ret = NWReadPropertyValue(CachedConn, szAnsiName, OT_USER, SECURITY_EQUALS,
+ Segment, (BYTE *) bySegment, &byMoreSegments, &byPropertyFlags))) {
+
+ Segment++;
+ // loop through properties converting them to user names
+ i = 0;
+ while ((bySegment[i]) && (i < 32)) {
+ if (!(ret = NWGetObjectName(CachedConn, bySegment[i], szAnsiUserName, &wFoundUserType))) {
+ // Got user - now convert and save off the information
+ OemToChar(szAnsiUserName, szUserName);
+
+ // Map out Everyone equivalence
+ if (lstrcmpi(szUserName, Lids(IDS_S_31))) {
+ lstrcpy(UserBuffer[Count].Name, szUserName);
+ lstrcpy(UserBuffer[Count].NewName, szUserName);
+ Count++;
+ }
+
+ // Check if we have to re-allocate buffer
+ if (Count >= NumRecs) {
+ NumRecs += DEF_NUM_RECS;
+ Users = (USER_LIST *) ReallocMemory((HGLOBAL) Users, sizeof(USER_LIST) + (sizeof(USER_BUFFER) * NumRecs));
+
+ if (!Users) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+ }
+
+ UserBuffer = Users->UserBuffer;
+ }
+ }
+ i++;
+ }
+
+ } else // if NWReadPropertyValue
+ byMoreSegments = 0;
+
+ } while (byMoreSegments);
+
+ // Gotta clear this out from the last loop
+ if (Count)
+ ret = 0;
+
+ }
+
+ // check if error occured...
+ if (ret)
+ status = ret;
+
+ // Now slim down the list to just what we need.
+ if (!status) {
+ Users = (USER_LIST *) ReallocMemory((HGLOBAL) Users, sizeof(USER_LIST) + (sizeof(USER_BUFFER) * Count));
+
+ if (!Users)
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ else {
+ // Sort the server list before putting it in the dialog
+ UserBuffer = Users->UserBuffer;
+ qsort((void *) UserBuffer, (size_t) Count, sizeof(USER_BUFFER), UserListCompare);
+ }
+ }
+
+ if (Users != NULL)
+ Users->Count = Count;
+
+ *lpUsers = Users;
+
+ return status;
+
+} // NWUserEquivalenceEnum
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+NWFileRightsEnum(
+ LPTSTR FileName,
+ USER_RIGHTS_LIST **lpUsers,
+ DWORD *UserCount,
+ BOOL DownLevel
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ BOOL Continue = TRUE;
+ USER_RIGHTS_LIST *Users = NULL;
+ DWORD NumRecs = DEF_NUM_RECS; // Default 200 names
+ DWORD Count = 0;
+ DWORD i = 0;
+ DWORD status = 0;
+ char szAnsiUserName[MAX_USER_NAME_LEN + 1];
+ char szAnsiSearchDir[MAX_PATH + 1];
+ TCHAR szUserName[MAX_USER_NAME_LEN + 1];
+ WORD wFoundUserType = 0;
+ NWCCODE ret;
+ char FoundDir[16];
+ ULONG Entries;
+
+ TRUSTEE_INFO ti[20];
+ BYTE DirHandle, nDirHandle;
+ BYTE Sequence;
+ BYTE NumEntries = 0;
+ NWDATE_TIME dtime = 0;
+ NWOBJ_ID ownerID = 0;
+
+ if (DownLevel) {
+ Entries = 5;
+ Sequence = 1;
+ } else {
+ Entries = 20;
+ Sequence = 0;
+ }
+
+ DirHandle = nDirHandle = 0;
+ memset(ti, 0, sizeof(ti));
+
+ Users = (USER_RIGHTS_LIST *) AllocMemory(NumRecs * sizeof(USER_RIGHTS_LIST));
+
+ if (!Users) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ } else {
+
+ // init to NULL so doesn't have garbage if call fails
+ lstrcpyA(szAnsiUserName, "");
+ CharToOem(FileName, szAnsiSearchDir);
+
+ // Loop through bindery getting all the users.
+ do {
+ if (DownLevel)
+ ret = NWCScanDirectoryForTrustees2(CachedConn, nDirHandle, szAnsiSearchDir,
+ &Sequence, FoundDir, &dtime, &ownerID, ti);
+ else
+ ret = NWCScanForTrustees(CachedConn, nDirHandle, szAnsiSearchDir,
+ &Sequence, &NumEntries, ti);
+
+ if (!ret) {
+ // loop through properties converting them to user names
+ for (i = 0; i < Entries; i++) {
+ if (ti[i].objectID != 0) {
+ if (!(ret = NWGetObjectName(CachedConn, ti[i].objectID, szAnsiUserName, &wFoundUserType))) {
+ // Got user - now convert and save off the information
+ OemToChar(szAnsiUserName, szUserName);
+
+ lstrcpy(Users[Count].Name, szUserName);
+ Users[Count].Rights = ti[i].objectRights;
+ Count++;
+
+ // Check if we have to re-allocate buffer
+ if (Count >= NumRecs) {
+ NumRecs += DEF_NUM_RECS;
+ Users = (USER_RIGHTS_LIST *) ReallocMemory((HGLOBAL) Users, NumRecs * sizeof(USER_RIGHTS_LIST));
+
+ if (!Users) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+ }
+ } // if realloc buffer
+ }
+ } // if objectID != 0
+ }
+
+ } else // NWScan failed
+ Continue = FALSE;
+
+ } while (Continue);
+
+ // Gotta clear this out from the last loop
+ if (Count)
+ ret = 0;
+
+ }
+
+ // check if error occured...
+ if (ret) {
+ status = ret;
+
+ if (Users != NULL) {
+ FreeMemory(Users);
+ Count = 0;
+ }
+ }
+
+ // Now slim down the list to just what we need.
+ if (!status) {
+ Users = (USER_RIGHTS_LIST *) ReallocMemory((HGLOBAL) Users, Count * sizeof(USER_RIGHTS_LIST));
+
+ if (!Users)
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ else
+ // Sort the server list before putting it in the dialog
+ qsort((void *) Users, (size_t) Count, sizeof(USER_RIGHTS_LIST), UserListCompare);
+ }
+
+ *UserCount = Count;
+ *lpUsers = Users;
+
+ return status;
+
+} // NWFileRightsEnum
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+NWLoginTimesMap(
+ BYTE *Times,
+ BYTE *NewTimes
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ DWORD i, j;
+ int Bit = 0;
+ int Val;
+ BYTE BitSet;
+
+ for (i = 0; i < 21; i++) {
+ BitSet = 0;
+
+ for (j = 0; j < 8; j++) {
+ if (BitTest(Bit, Times) || BitTest(Bit+1, Times)) {
+ Val = 0x1 << j;
+ BitSet = BitSet + (BYTE) Val;
+ }
+
+ Bit++; Bit++;
+ }
+
+ NewTimes[i] = BitSet;
+ }
+
+
+} // NWLoginTimesMap
+
+
+/*+-------------------------------------------------------------------------+
+ | Time Conversion |
+ +-------------------------------------------------------------------------+*/
+
+#define IS_LEAP(y) ((y % 4 == 0) && (y % 100 != 0 || y % 400 == 0))
+#define DAYS_IN_YEAR(y) (IS_LEAP(y) ? 366 : 365)
+#define DAYS_IN_MONTH(m,y) (IS_LEAP(y) ? _days_month_leap[m] : _days_month[m])
+#define SECS_IN_DAY (60L * 60L * 24L)
+#define SECS_IN_HOUR (60L * 60L)
+#define SECS_IN_MINUTE (60L)
+
+static short _days_month_leap[] = { 31,29,31,30,31,30,31,31,30,31,30,31 };
+static short _days_month[] = { 31,28,31,30,31,30,31,31,30,31,30,31 };
+
+/////////////////////////////////////////////////////////////////////////
+BOOL
+NWTimeMap(
+ DWORD Days,
+ DWORD dwMonth,
+ DWORD dwYear,
+ DWORD dwBasis,
+ ULONG *Time
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ DWORD dw = 0;
+ DWORD dwDays = 0;
+
+ // Zero
+ *Time = 0L;
+
+ // Adjust year
+ if(dwYear < 70)
+ dwYear += 2000L;
+ else
+ if(dwYear < 100)
+ dwYear += 1900L;
+
+ if (dwYear < dwBasis)
+ return FALSE;
+
+ // Calculate days in previous years, take -1 so we skip current year
+ dw = dwYear - 1;
+ while(dw >= dwBasis) {
+ dwDays += DAYS_IN_YEAR(dw);
+ --dw;
+ }
+
+ // Days from month
+ if((dwMonth < 1)||(dwMonth > 12))
+ return FALSE;
+
+ // Loop through adding number of days in each month. Take -2 (-1 to skip
+ // current month, and -1 to make 0 based).
+ dw = dwMonth;
+ while(dw > 1) {
+ dwDays += DAYS_IN_MONTH(dw-2, dwYear);
+ --dw;
+ }
+
+ // Convert days
+ dw = Days;
+ if((dw >= 1) && (dw <= (DWORD) DAYS_IN_MONTH(dwMonth - 1, dwYear)))
+ dwDays += dw;
+ else
+ return FALSE; // out of range
+
+ *Time += dwDays * SECS_IN_DAY;
+ return TRUE;
+} // NWTimeMap
+
+
+/////////////////////////////////////////////////////////////////////////
+LPTSTR
+NWUserNameGet(
+ LPTSTR szUserName
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ static TCHAR UserName[128];
+ char szAnsiUserName[MAX_USER_NAME_LEN];
+ NWCCODE ret;
+ BYTE bySegment[128];
+ BYTE byMoreSegments, byPropertyFlags;
+ LPSTR szAnsiFullName;
+
+ CharToOem(szUserName, szAnsiUserName);
+ ret = NWReadPropertyValue(CachedConn, szAnsiUserName, OT_USER, IDENTIFICATION,
+ 1, bySegment, &byMoreSegments, &byPropertyFlags);
+
+ if (ret == SUCCESSFUL) {
+ szAnsiFullName = (LPSTR) bySegment;
+ OemToChar(szAnsiFullName, UserName);
+ return UserName;
+ }
+
+ return NULL;
+
+} // NWUserNameGet
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+NWNetUserMapInfo (
+ LPTSTR szUserName,
+ VOID *UInfo,
+ NT_USER_INFO *NT_UInfo
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG expires;
+ struct tagLoginControl *tag;
+ LPTSTR FullName;
+
+ tag = (struct tagLoginControl *) UInfo;
+
+ FullName = NWUserNameGet(szUserName);
+ if (FullName != NULL)
+ lstrcpyn(NT_UInfo->full_name, FullName, MAXCOMMENTSZ);
+
+ // Account disabled
+ if (tag->byAccountDisabled)
+ NT_UInfo->flags = NT_UInfo->flags | 0x02;
+
+ // account locked out
+ if ((tag->wBadLogins == 0xffff) &&
+ (tag->lNextResetTime > (LONG)CachedServerTime))
+ NT_UInfo->flags = NT_UInfo->flags | 0x02; // disable account...
+
+ // user can change password
+ if ((tag->byRestrictions & 0x01))
+ NT_UInfo->flags = NT_UInfo->flags | 0x40;
+
+ NWLoginTimesMap(tag->byLoginTimes, NT_UInfo->logon_hours);
+
+ // account expires
+ if (tag->byAccountExpires[0] == 0)
+ NT_UInfo->acct_expires = TIMEQ_FOREVER;
+ else
+ if (tag->byAccountExpires[0] < 70)
+ NT_UInfo->acct_expires = 0;
+ else {
+ // fits within time range so convert to #seconds since 1970
+ expires = 0;
+ NWTimeMap((DWORD) tag->byAccountExpires[2],
+ (DWORD) tag->byAccountExpires[1],
+ (DWORD) tag->byAccountExpires[0], 1970, &expires);
+ NT_UInfo->acct_expires = expires;
+ }
+
+} // NWNetUserMapInfo
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+NWFPNWMapInfo(
+ VOID *NWUInfo,
+ PFPNW_INFO fpnw
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ struct tagLoginControl *tag;
+
+ tag = (struct tagLoginControl *) NWUInfo;
+
+ fpnw->MaxConnections = tag->wMaxConnections;
+ fpnw->PasswordInterval = tag->wPasswordInterval;
+ fpnw->GraceLoginAllowed = tag->byGraceLogins;
+ fpnw->GraceLoginRemaining = tag->byGraceLoginReset;
+ fpnw->LoginFrom = NULL;
+ fpnw->HomeDir = NULL;
+
+} // NWFPNWMapInfo
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+NWUserDefaultsGet(
+ VOID **UDefaults
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ struct tagUserDefaults *UserDefaults = NULL;
+ NWCCODE ret;
+ BYTE bySegment[128];
+ BYTE byMoreSegments, byPropertyFlags;
+
+ ret = NWReadPropertyValue(CachedConn, SUPERVISOR, OT_USER, USER_DEFAULTS, 1, bySegment, &byMoreSegments, &byPropertyFlags);
+
+ if (ret == SUCCESSFUL) {
+ UserDefaults = AllocMemory(sizeof(struct tagUserDefaults));
+ memcpy(UserDefaults, bySegment, sizeof (struct tagUserDefaults));
+
+ // Now put the data in 'normal' Intel format
+ SWAPBYTES(UserDefaults->wPasswordInterval);
+ SWAPBYTES(UserDefaults->wMaxConnections);
+ SWAPWORDS(UserDefaults->lBalance);
+ SWAPWORDS(UserDefaults->lCreditLimit);
+ SWAPWORDS(UserDefaults->lMaxDiskBlocks);
+ }
+
+ *UDefaults = (void *) UserDefaults;
+
+} // NWUserDefaultsGet
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+NWUserDefaultsMap(
+ VOID *NWUDefaults,
+ NT_DEFAULTS *NTDefaults
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ struct tagUserDefaults *UserDefaults = NULL;
+
+ if ((NWUDefaults == NULL) || (NTDefaults == NULL))
+ return;
+
+ UserDefaults = (struct tagUserDefaults *) NWUDefaults;
+
+ NTDefaults->min_passwd_len = (DWORD) UserDefaults->byMinPasswordLength;
+ NTDefaults->max_passwd_age = (DWORD) UserDefaults->wPasswordInterval * 86400;
+ NTDefaults->force_logoff = (DWORD) UserDefaults->byGraceLoginReset;
+
+ // These fields aren't used/converted
+ // NTDefaults->min-passwd_age - no such thing for NetWare
+ // NTDefaults->password_hist_len - no such thing for NetWare
+
+} // NWUserDefaultsMap
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+NWLoginTimesLog(
+ BYTE *Times
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ TCHAR *szDays[7];
+ DWORD Day;
+ DWORD Hours;
+ int Bit = 0, i;
+ static TCHAR szHours[80];
+
+ szDays[0] = Lids(IDS_SUN);
+ szDays[1] = Lids(IDS_MON);
+ szDays[2] = Lids(IDS_TUE);
+ szDays[3] = Lids(IDS_WED);
+ szDays[4] = Lids(IDS_THU);
+ szDays[5] = Lids(IDS_FRI);
+ szDays[6] = Lids(IDS_SAT);
+
+ LogWriteLog(1, Lids(IDS_CRLF));
+ LogWriteLog(1, Lids(IDS_L_104));
+
+ // while these should be level 2, there isn't room on 80 cols - so level 1
+ LogWriteLog(1, Lids(IDS_L_1));
+ LogWriteLog(1, Lids(IDS_L_2));
+ LogWriteLog(1, Lids(IDS_L_3));
+
+ for (Day = 0; Day < 7; Day++) {
+ LogWriteLog(1, szDays[Day]);
+ lstrcpy(szHours, TEXT(" "));
+
+ for (Hours = 0; Hours < 24; Hours++) {
+ for (i = 0; i < 2; i++) {
+ if (BitTest(Bit, Times))
+ lstrcat(szHours, TEXT("*"));
+ else
+ lstrcat(szHours, TEXT(" "));
+
+ Bit++;
+ }
+
+ lstrcat(szHours, TEXT(" "));
+ }
+
+ LogWriteLog(0, szHours);
+ LogWriteLog(0, Lids(IDS_CRLF));
+ }
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+
+} // NWLoginTimesLog
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+NWUserDefaultsLog(
+ VOID *UDefaults
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ struct tagUserDefaults *tag;
+
+ tag = (struct tagUserDefaults *) UDefaults;
+
+ // Account expires
+ LogWriteLog(1, Lids(IDS_L_109));
+ if (tag->byAccountExpiresYear == 0)
+ LogWriteLog(0, Lids(IDS_L_107));
+ else
+ LogWriteLog(0, TEXT("%02u/%02u/%04u"), (UINT) tag->byAccountExpiresDay,
+ (UINT) tag->byAccountExpiresMonth, (UINT) 1900 + tag->byAccountExpiresYear);
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+
+ // Restrictions
+ LogWriteLog(1, Lids(IDS_L_110));
+ // user can change password
+ if ((tag->byRestrictions & 0x01))
+ LogWriteLog(2, Lids(IDS_L_111));
+ else
+ LogWriteLog(2, Lids(IDS_L_112));
+
+ // unique password required
+ if ((tag->byRestrictions & 0x02))
+ LogWriteLog(2, Lids(IDS_L_113), Lids(IDS_YES));
+ else
+ LogWriteLog(2, Lids(IDS_L_113), Lids(IDS_NO));
+
+ // Password interval
+ LogWriteLog(1, Lids(IDS_L_114));
+ if (tag->wPasswordInterval == 0)
+ LogWriteLog(0, Lids(IDS_L_107));
+ else
+ LogWriteLog(0, TEXT("%u"), (UINT) tag->wPasswordInterval);
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+
+ // Grace Logins
+ LogWriteLog(1, Lids(IDS_L_115));
+ if (tag->byGraceLoginReset == 0xff)
+ LogWriteLog(0, Lids(IDS_L_108));
+ else
+ LogWriteLog(0, TEXT("%u"), (UINT) tag->byGraceLoginReset);
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+
+ LogWriteLog(1, Lids(IDS_L_116), (UINT) tag->byMinPasswordLength);
+
+ // Max Connections
+ LogWriteLog(1, Lids(IDS_L_117));
+ if (tag->wMaxConnections == 0)
+ LogWriteLog(0, Lids(IDS_L_108));
+ else
+ LogWriteLog(0, TEXT("%u"), (UINT) tag->wMaxConnections);
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+
+ LogWriteLog(1, Lids(IDS_L_118), (ULONG) tag->lBalance);
+ LogWriteLog(1, Lids(IDS_L_119), (ULONG) tag->lCreditLimit);
+
+ // Max Disk blocks
+ LogWriteLog(1, Lids(IDS_L_120));
+ if (tag->lMaxDiskBlocks == 0x7FFFFFFF)
+ LogWriteLog(0, Lids(IDS_L_108));
+ else
+ LogWriteLog(0, TEXT("%lu"), (ULONG) tag->lMaxDiskBlocks);
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+ LogWriteLog(0, Lids(IDS_CRLF));
+
+} // NWUserDefaultsLog
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+NWUserInfoLog(
+ LPTSTR szUserName,
+ VOID *UInfo
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ struct tagLoginControl *tag;
+ LPTSTR FullName;
+
+ tag = (struct tagLoginControl *) UInfo;
+
+ LogWriteLog(1, Lids(IDS_L_105));
+
+ // Full Name
+ LogWriteLog(2, Lids(IDS_L_106));
+
+ FullName = NWUserNameGet(szUserName);
+ if (FullName != NULL)
+ LogWriteLog(2, FullName);
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+
+ // Account disabled
+ if (tag->byAccountDisabled == 0xff)
+ LogWriteLog(2, Lids(IDS_L_121), Lids(IDS_YES));
+ else if ((tag->wBadLogins == 0xffff) &&
+ (tag->lNextResetTime > (LONG)CachedServerTime))
+ LogWriteLog(2, Lids(IDS_L_121), Lids(IDS_LOCKED_OUT));
+ else
+ LogWriteLog(2, Lids(IDS_L_121), Lids(IDS_NO));
+
+ // Account expires
+ LogWriteLog(2, Lids(IDS_L_109));
+ if (tag->byAccountExpires[0] == 0)
+ LogWriteLog(0, Lids(IDS_L_107));
+ else
+ LogWriteLog(0, TEXT("%02u/%02u/%04u"), (UINT) tag->byAccountExpires[1],
+ (UINT) tag->byAccountExpires[2], (UINT) 1900 + tag->byAccountExpires[0]);
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+
+ // Password Expires
+ LogWriteLog(2, Lids(IDS_L_122));
+ if (tag->byPasswordExpires[0] == 0)
+ LogWriteLog(0, Lids(IDS_L_107));
+ else
+ LogWriteLog(0, TEXT("%02u/%02u/19%02u"), (int) tag->byPasswordExpires[1],
+ (int) tag->byPasswordExpires[2], (int) tag->byPasswordExpires[0]);
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+
+ // Grace logins
+ LogWriteLog(2, Lids(IDS_L_123));
+ if (tag->byGraceLogins == 0xff)
+ LogWriteLog(0, Lids(IDS_L_108));
+ else
+ LogWriteLog(0, TEXT("%u"), (UINT) tag->byGraceLogins);
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+
+ // initial grace logins
+ LogWriteLog(2, Lids(IDS_L_115));
+ if (tag->byGraceLoginReset == 0xff)
+ LogWriteLog(0, Lids(IDS_L_108));
+ else
+ LogWriteLog(0, TEXT("%u"), (UINT) tag->byGraceLoginReset);
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+
+ // Min password length
+ LogWriteLog(2, Lids(IDS_L_116), (UINT) tag->byMinPasswordLength);
+
+ // Password expiration
+ LogWriteLog(2, Lids(IDS_L_114));
+ if (tag->wPasswordInterval == 0)
+ LogWriteLog(0, Lids(IDS_L_107));
+ else
+ LogWriteLog(0, TEXT("%u"), (UINT) tag->wPasswordInterval);
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+
+ // Max connections
+ LogWriteLog(2, Lids(IDS_L_117));
+ if (tag->wMaxConnections == 0)
+ LogWriteLog(0, Lids(IDS_L_108));
+ else
+ LogWriteLog(0, TEXT("%u"), (UINT) tag->wMaxConnections);
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+
+ // Restrictions
+ // user can change password
+ LogWriteLog(2, Lids(IDS_L_110));
+ if ((tag->byRestrictions & 0x01))
+ LogWriteLog(3, Lids(IDS_L_111));
+ else
+ LogWriteLog(3, Lids(IDS_L_112));
+
+ // unique password required
+ if ((tag->byRestrictions & 0x02))
+ LogWriteLog(3, Lids(IDS_L_113), Lids(IDS_YES));
+ else
+ LogWriteLog(3, Lids(IDS_L_113), Lids(IDS_NO));
+
+ LogWriteLog(2, Lids(IDS_L_124), (UINT) tag->wBadLogins);
+
+ // Max Disk Blocks
+ LogWriteLog(2, Lids(IDS_L_120));
+ if (tag->lMaxDiskBlocks == 0x7FFFFFFF)
+ LogWriteLog(0, Lids(IDS_L_108));
+ else
+ LogWriteLog(0, TEXT("%lX"), tag->lMaxDiskBlocks);
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+
+ // Login Times
+ NWLoginTimesLog(tag->byLoginTimes);
+
+} // NWUserInfoLog
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+NWUserInfoGet(
+ LPTSTR szUserName,
+ VOID **UInfo
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ static struct tagLoginControl xUI;
+ struct tagLoginControl *UserInfo = NULL;
+ char szAnsiUserName[MAX_USER_NAME_LEN];
+ NWCCODE ret;
+ BYTE bySegment[128];
+ BYTE byMoreSegments, byPropertyFlags;
+
+ CharToOem(szUserName, szAnsiUserName);
+ ret = NWReadPropertyValue(CachedConn, szAnsiUserName, OT_USER, LOGIN_CONTROL, 1, bySegment, &byMoreSegments, &byPropertyFlags);
+
+ if (ret == SUCCESSFUL) {
+ UserInfo = &xUI;
+ memset(UserInfo, 0, sizeof(struct tagLoginControl));
+ memcpy(UserInfo, bySegment, sizeof (struct tagLoginControl));
+
+ // Now put the data in 'normal' Intel format
+ SWAPBYTES(UserInfo->wPasswordInterval);
+ SWAPBYTES(UserInfo->wMaxConnections);
+ SWAPWORDS(UserInfo->lMaxDiskBlocks);
+ SWAPBYTES(UserInfo->wBadLogins);
+ SWAPWORDS(UserInfo->lNextResetTime);
+ }
+
+ *UInfo = (void *) UserInfo;
+
+} // NWUserInfoGet
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+NWServerEnum(
+ LPTSTR Container,
+ SERVER_BROWSE_LIST **lpServList
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ int NumBufs = 0;
+ DWORD TotalEntries = 0;
+ ENUM_REC *BufHead, *CurrBuf, *OldBuf;
+ SERVER_BROWSE_LIST *ServList = NULL;
+ SERVER_BROWSE_BUFFER *SList = NULL;
+ DWORD status = 0;
+ DWORD i, j;
+ NETRESOURCE ResourceBuf;
+
+ // Container is ignored - NW is a flat network topology...
+ SetProvider(NW_PROVIDER, &ResourceBuf);
+
+ BufHead = CurrBuf = OldBuf = NULL;
+ status = EnumBufferBuild(&BufHead, &NumBufs, ResourceBuf);
+
+ if (!status) {
+ // We have 0 to xxx Enum recs each with a buffer sitting off of it. Now
+ // need to consolidate these into one global enum list...
+ if (NumBufs) {
+ CurrBuf = BufHead;
+
+ // Figure out how many total entries there are
+ while (CurrBuf) {
+ TotalEntries += CurrBuf->cEntries;
+ CurrBuf = CurrBuf->next;
+ }
+
+ CurrBuf = BufHead;
+
+ // Now create a Server List to hold all of these.
+ ServList = AllocMemory(sizeof(SERVER_BROWSE_LIST) + TotalEntries * sizeof(SERVER_BROWSE_BUFFER));
+
+ if (ServList == NULL) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ } else {
+ ServList->Count = TotalEntries;
+ SList = (SERVER_BROWSE_BUFFER *) &ServList->SList;
+
+ j = 0;
+
+ // Now loop through copying the data...
+ while (CurrBuf) {
+ for(i = 0; i < CurrBuf->cEntries; i++) {
+ if (CurrBuf->lpnr[i].lpRemoteName != NULL)
+ if (CurrBuf->lpnr[i].lpRemoteName[0] == TEXT('\\') && CurrBuf->lpnr[i].lpRemoteName[1] == TEXT('\\'))
+ lstrcpy(SList[j].Name, &CurrBuf->lpnr[i].lpRemoteName[2]);
+ else
+ lstrcpy(SList[j].Name, CurrBuf->lpnr[i].lpRemoteName);
+ else
+ lstrcpy(SList[j].Name, TEXT(""));
+
+ if (CurrBuf->lpnr[i].lpComment != NULL)
+ lstrcpy(SList[j].Description, CurrBuf->lpnr[i].lpComment);
+ else
+ lstrcpy(SList[j].Description, TEXT(""));
+
+ SList[j].Container = FALSE;
+ j++;
+ }
+
+ OldBuf = CurrBuf;
+ CurrBuf = CurrBuf->next;
+
+ // Free the old buffer
+ FreeMemory((HGLOBAL) OldBuf);
+
+ } // while
+
+ } // else (ServList)
+
+ } // if (numbufs)
+
+ }
+
+ *lpServList = ServList;
+ return status;
+
+} // NWServerEnum
+
+
+/////////////////////////////////////////////////////////////////////////
+ULONG
+NWShareSizeGet(
+ LPTSTR Share
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ static TCHAR RootPath[MAX_PATH + 1];
+ DWORD sectorsPC, bytesPS, FreeClusters, Clusters;
+ DWORD TotalSpace, FreeSpace;
+
+ TotalSpace = FreeSpace = 0;
+
+ wsprintf(RootPath, TEXT("\\\\%s\\%s\\"), CachedServer, Share);
+ if (GetDiskFreeSpace(RootPath, &sectorsPC, &bytesPS, &FreeClusters, &Clusters)) {
+ TotalSpace = Clusters * sectorsPC * bytesPS;
+ FreeSpace = FreeClusters * sectorsPC * bytesPS;
+ }
+
+ // total - free = approx allocated space (if multiple shares on drive then
+ // this doesn't take that into account, we just want an approximation...
+ return TotalSpace - FreeSpace;
+
+} // NWShareSizeGet
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+NWSharesEnum(
+ SHARE_LIST **lpShares
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ int NumBufs = 0;
+ DWORD TotalEntries = 0;
+ ENUM_REC *BufHead, *CurrBuf, *OldBuf;
+ SHARE_LIST *ShareList = NULL;
+ SHARE_BUFFER *SList = NULL;
+ DWORD status;
+ DWORD i, j;
+ NETRESOURCE ResourceBuf;
+
+ // Setup NETRESOURCE data structure
+ SetProvider(NW_PROVIDER, &ResourceBuf);
+
+ ResourceBuf.lpRemoteName = CachedServer;
+ ResourceBuf.dwUsage = RESOURCEUSAGE_CONTAINER;
+
+ status = EnumBufferBuild(&BufHead, &NumBufs, ResourceBuf);
+
+ if (!status) {
+ // We have 0 to xxx Enum recs each with a buffer sitting off of it. Now
+ // need to consolidate these into one global enum list...
+ if (NumBufs) {
+ CurrBuf = BufHead;
+
+ // Figure out how many total entries there are
+ while (CurrBuf) {
+ TotalEntries += CurrBuf->cEntries;
+ CurrBuf = CurrBuf->next;
+ }
+
+ CurrBuf = BufHead;
+
+ // Now create a Server List to hold all of these.
+ ShareList = (SHARE_LIST *) AllocMemory(sizeof(SHARE_LIST) + (TotalEntries * sizeof(SHARE_BUFFER)));
+
+ if (ShareList == NULL) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ } else {
+ j = 0;
+
+ // Zero out everything and get pointer to list
+ memset(ShareList, 0, sizeof(SHARE_LIST) + (TotalEntries * sizeof(SHARE_BUFFER)));
+ ShareList->Count = TotalEntries;
+ SList = (SHARE_BUFFER *) &ShareList->SList;
+
+ // Now loop through copying the data...
+ while (CurrBuf) {
+ for(i = 0; i < CurrBuf->cEntries; i++) {
+ if (CurrBuf->lpnr[i].lpRemoteName != NULL)
+ lstrcpy(SList[j].Name, ShareNameParse(CurrBuf->lpnr[i].lpRemoteName));
+ else
+ lstrcpy(SList[j].Name, TEXT(""));
+
+ SList[j].Size = NWShareSizeGet(SList[j].Name);
+ SList[j].Index = (USHORT) j;
+ j++;
+ }
+
+ OldBuf = CurrBuf;
+ CurrBuf = CurrBuf->next;
+
+ // Free the old buffer
+ FreeMemory((HGLOBAL) OldBuf);
+
+ } // while
+
+ } // else (ShareList)
+
+ } // if (numbufs)
+
+ }
+
+ *lpShares = ShareList;
+ return status;
+
+} // NWSharesEnum
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+NWServerInfoReset(
+ SOURCE_SERVER_BUFFER *SServ
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ static VERSION_INFO NWInfo;
+ NWCCODE ret = 0;
+
+ ret = NWGetFileServerVersionInfo(CachedConn, &NWInfo);
+
+ // BUGBUG: This API returns fail (8801) - but is actually succeding,
+ // just ignore error for right now as it really doesn't matter for the
+ // version info.
+// if (ret == SUCCESSFUL) {
+ SServ->VerMaj = NWInfo.Version;
+ SServ->VerMin = NWInfo.SubVersion;
+// }
+
+} // NWServerInfoReset
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+NWServerInfoSet(
+ LPTSTR ServerName,
+ SOURCE_SERVER_BUFFER *SServ
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ static VERSION_INFO NWInfo;
+ NWCCODE ret = 0;
+
+ CursorHourGlass();
+ lstrcpy(SServ->Name, ServerName);
+ NWServerInfoReset(SServ);
+
+ // Fill in share list
+ NWSharesEnum(&SServ->ShareList);
+
+#ifdef DEBUG
+{
+ DWORD i;
+
+ dprintf(TEXT("Adding NW Server: %s\n"), SServ->Name);
+ dprintf(TEXT(" Version: %u.%u\n"), (UINT) SServ->VerMaj, (UINT) SServ->VerMin);
+ dprintf(TEXT(" Shares\n"));
+ dprintf(TEXT(" +---------------------------------------+\n"));
+ if (SServ->ShareList) {
+ for (i = 0; i < SServ->ShareList->Count; i++) {
+ dprintf(TEXT(" %-15s AllocSpace %lu\n"), SServ->ShareList->SList[i].Name, SServ->ShareList->SList[i].Size);
+ }
+ }
+ else
+ dprintf(TEXT(" <Serv List enum failed!!>\n"));
+
+ dprintf(TEXT("\n"));
+
+}
+#endif
+
+ CursorNormal();
+
+} // NWServerInfoSet
+
+
+/////////////////////////////////////////////////////////////////////////
+BOOL
+NWServerValidate(
+ HWND hWnd,
+ LPTSTR ServerName,
+ BOOL DupFlag
+ )
+
+/*++
+
+Routine Description:
+
+ Validates a given server - makes sure it can be connected to and
+ that the user has admin privs on it.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ DWORD Status;
+ BOOL ret = FALSE;
+ SOURCE_SERVER_BUFFER *SServ = NULL;
+ DWORD dwObjectID = 0;
+ DWORD Size;
+ BYTE AccessLevel;
+ TCHAR UserName[MAX_USER_NAME_LEN + 1];
+ static TCHAR LocServer[MAX_SERVER_NAME_LEN+3];
+ LPVOID lpMessageBuffer;
+
+ CursorHourGlass();
+
+ if (DupFlag)
+ SServ = SServListFind(ServerName);
+
+ if (SServ == NULL) {
+ // Get Current Logged On User
+ lstrcpy(UserName, TEXT(""));
+ Size = sizeof(UserName);
+ WNetGetUser(NULL, UserName, &Size);
+
+ lstrcpy(LocServer, TEXT("\\\\"));
+ lstrcat(LocServer, ServerName);
+
+ if (UseAddPswd(hWnd, UserName, LocServer, Lids(IDS_S_6), NW_PROVIDER)) {
+
+ Status = NWServerSet(ServerName);
+
+ if (Status) {
+
+ if (GetLastError() != 0)
+ WarningError(Lids(IDS_NWCANT_CON), ServerName);
+
+ } else {
+ if (IsNCPServerFPNW(CachedConn))
+ WarningError(Lids(IDS_E_18), ServerName);
+ else {
+ Status = NWCGetBinderyAccessLevel(CachedConn, &AccessLevel, &dwObjectID);
+
+ if (!Status) {
+ AccessLevel &= BS_SUPER_READ;
+ if (AccessLevel == BS_SUPER_READ)
+ ret = TRUE;
+ else
+ WarningError(Lids(IDS_NWNO_ADMIN), ServerName);
+ }
+ }
+ }
+ } else {
+ FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, GetLastError(), 0,
+ (LPTSTR) &lpMessageBuffer, 0, NULL );
+
+ if (GetLastError() != 0)
+ WarningError(Lids(IDS_E_9), ServerName, lpMessageBuffer);
+
+ LocalFree(lpMessageBuffer);
+ }
+ } else {
+ // Already in source server list - can't appear more then once
+ WarningError(Lids(IDS_E_14), ServerName);
+ }
+
+ CursorNormal();
+ return ret;
+
+} // NWServerValidate
+
+
+/////////////////////////////////////////////////////////////////////////
+LPTSTR
+NWRightsLog(
+ DWORD Rights
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ static TCHAR NWRights[15];
+
+ lstrcpy(NWRights, Lids(IDS_S_34));
+
+ // Read
+ if (!(Rights & 0x01))
+ NWRights[2] = TEXT(' ');
+
+ // Write
+ if (!(Rights & 0x02))
+ NWRights[3] = TEXT(' ');
+
+ // Create
+ if (!(Rights & 0x08))
+ NWRights[4] = TEXT(' ');
+
+ // Delete (Erase)
+ if (!(Rights & 0x10))
+ NWRights[5] = TEXT(' ');
+
+ // Parental
+ if (!(Rights & 0x20))
+ NWRights[8] = TEXT(' ');
+
+ // Search
+ if (!(Rights & 0x40))
+ NWRights[7] = TEXT(' ');
+
+ // Modify
+ if (!(Rights & 0x80))
+ NWRights[6] = TEXT(' ');
+
+ // Supervisor (all rights set)
+ if ((Rights & 0xFB) != 0xFB)
+ NWRights[1] = TEXT(' ');
+
+ return NWRights;
+
+} // NWRightsLog
+
+
+/////////////////////////////////////////////////////////////////////////
+NTSTATUS
+MapNwRightsToNTAccess(
+ ULONG NWRights,
+ PRIGHTS_MAPPING pMap,
+ ACCESS_MASK *pAccessMask
+ )
+
+/*++
+
+Routine Description:
+
+ Map a NW Right to the appropriate NT AccessMask
+
+Arguments:
+
+ NWRights - Netware rights we wish to map
+ pMap - pointer to structure that defines the mapping
+
+Return Value:
+
+ The NT AccessMask corresponding to the NW Rights
+
+--*/
+
+{
+ PNW_TO_NT_MAPPING pNWToNtMap = pMap->Nw2NtMapping ;
+ ACCESS_MASK AccessMask = 0 ;
+
+ if (!pAccessMask)
+ return STATUS_INVALID_PARAMETER ;
+
+ *pAccessMask = 0x0 ;
+
+ // go thru the mapping structuring, OR-ing in bits along the way
+ while (pNWToNtMap->NWRight) {
+
+ if (pNWToNtMap->NWRight & NWRights)
+ AccessMask |= pNWToNtMap->NTAccess ;
+
+ pNWToNtMap++ ;
+ }
+
+ *pAccessMask = AccessMask ;
+
+ return STATUS_SUCCESS ;
+} // MapNwRightsToNTAccess
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+NWPrintServersEnum(
+ PRINT_SERVER_LIST **PS
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ PRINT_SERVER_LIST *psl;
+ PRINT_SERVER_BUFFER *pbuff;
+ DWORD NumRecs = DEF_NUM_RECS; // Default 200 names
+ DWORD Count = 0;
+ DWORD status = 0;
+ char szAnsiPrinterName[MAX_USER_NAME_LEN + 1];
+ TCHAR szPrinterName[MAX_USER_NAME_LEN + 1];
+ WORD wFoundUserType = 0;
+ DWORD dwObjectID = 0xFFFFFFFFL;
+ BYTE byPropertiesFlag = 0;
+ BYTE byObjectFlag = 0;
+ BYTE byObjectSecurity = 0;
+ NWCCODE ret;
+
+ psl = (PRINT_SERVER_LIST *) AllocMemory(sizeof(PRINT_SERVER_LIST) + (NumRecs * sizeof(PRINT_SERVER_BUFFER)));
+
+ if (!psl) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ } else {
+ pbuff = psl->PSList;
+
+ // init to NULL so doesn't have garbage if call fails
+ lstrcpyA(szAnsiPrinterName, "");
+
+ // Loop through bindery getting all the users.
+ while ((ret = NWScanObject(CachedConn, "*", OT_PRINT_SERVER, &dwObjectID, szAnsiPrinterName,
+ &wFoundUserType, &byPropertiesFlag,
+ &byObjectFlag, &byObjectSecurity)) == SUCCESSFUL) {
+
+ // Got user - now convert and save off the information
+ OemToChar(szAnsiPrinterName, szPrinterName);
+
+ lstrcpy(pbuff[Count].Name, szPrinterName);
+ Count++;
+
+ // Check if we have to re-allocate buffer
+ if (Count >= NumRecs) {
+ NumRecs += DEF_NUM_RECS;
+ psl = (PRINT_SERVER_LIST *) ReallocMemory((HGLOBAL) psl, sizeof(PRINT_SERVER_LIST) + (NumRecs * sizeof(PRINT_SERVER_BUFFER)));
+ pbuff = psl->PSList;
+
+ if (!psl) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+ }
+
+ }
+
+ }
+
+ // Gotta clear this out from the last loop
+ if (Count)
+ ret = 0;
+
+ }
+
+ // check if error occured...
+ if (ret)
+ status = ret;
+
+ // Now slim down the list to just what we need.
+ if (!status) {
+ psl = (PRINT_SERVER_LIST *) ReallocMemory((HGLOBAL) psl, sizeof(PRINT_SERVER_LIST) + (Count * sizeof(PRINT_SERVER_BUFFER)));
+
+ if (!psl)
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ psl->Count = Count;
+ *PS = psl;
+
+ return status;
+
+} // NWPrintServersEnum
+
+
+/////////////////////////////////////////////////////////////////////////
+DWORD
+NWPrintOpsEnum(
+ USER_LIST **lpUsers
+ )
+
+/*++
+
+Routine Description:
+
+ First need to enumerate all the print servers on the NetWare system
+ we are pointing to. Next loop through each of these print servers
+ and enumerate the print operators on each of them.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ PRINT_SERVER_LIST *psl = NULL;
+ PRINT_SERVER_BUFFER *PSList;
+ ULONG pCount;
+ USER_LIST *Users = NULL;
+ USER_BUFFER *UserBuffer;
+ DWORD NumRecs = DEF_NUM_RECS; // Default 200 names
+ DWORD Count = 0;
+ DWORD ipsl = 0, iseg = 0;
+ DWORD status = 0;
+ char szAnsiUserName[MAX_USER_NAME_LEN + 1];
+ char szAnsiName[MAX_GROUP_NAME_LEN + 1];
+ TCHAR szUserName[MAX_USER_NAME_LEN + 1];
+ WORD wFoundUserType = 0;
+ DWORD dwObjectID = 0xFFFFFFFFL;
+ BYTE byPropertyFlags = 0;
+ BYTE byObjectFlag = 0;
+ BYTE byObjectSecurity = 0;
+ UCHAR Segment = 1;
+ DWORD bySegment[32];
+ BYTE byMoreSegments;
+ NWCCODE ret;
+
+ *lpUsers = NULL;
+
+ // Enumerate the print servers - if there are none, then there are no printer ops
+ NWPrintServersEnum(&psl);
+ if ((psl == NULL) || (psl->Count == 0)) {
+ if (psl != NULL)
+ FreeMemory(psl);
+
+ return 0;
+ }
+
+ // Got some servers - loop through them enumerating users
+ Users = (USER_LIST *) AllocMemory(sizeof(USER_LIST) + (sizeof(USER_BUFFER) * NumRecs));
+
+ if (!Users) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ } else {
+ UserBuffer = Users->UserBuffer;
+ PSList = psl->PSList;
+
+ for (pCount = 0; pCount < psl->Count; pCount++) {
+ // init to NULL so doesn't have garbage if call fails
+ lstrcpyA(szAnsiUserName, "");
+ CharToOem(PSList[ipsl++].Name, szAnsiName);
+
+ // Loop through bindery getting all the users.
+ do {
+ if (!(ret = NWReadPropertyValue(CachedConn, szAnsiName, OT_PRINT_SERVER, PS_OPERATORS,
+ Segment, (BYTE *) bySegment, &byMoreSegments, &byPropertyFlags))) {
+
+ Segment++;
+ // loop through properties converting them to user names
+ iseg = 0;
+ while ((bySegment[iseg]) && (iseg < 32)) {
+ if (!(ret = NWGetObjectName(CachedConn, bySegment[iseg], szAnsiUserName, &wFoundUserType))) {
+ // Got user - now convert and save off the information
+ OemToChar(szAnsiUserName, szUserName);
+
+ // Map out Supervisor (already print-op privs)
+ if (lstrcmpi(szUserName, Lids(IDS_S_28))) {
+ lstrcpy(UserBuffer[Count].Name, szUserName);
+ lstrcpy(UserBuffer[Count].NewName, szUserName);
+ Count++;
+ }
+
+ // Check if we have to re-allocate buffer
+ if (Count >= NumRecs) {
+ NumRecs += DEF_NUM_RECS;
+ Users = (USER_LIST *) ReallocMemory((HGLOBAL) Users, sizeof(USER_LIST) + (sizeof(USER_BUFFER) * NumRecs));
+
+ if (!Users) {
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+ }
+
+ UserBuffer = Users->UserBuffer;
+ }
+ }
+ iseg++;
+ }
+
+ } else // if NWReadPropertyValue
+ byMoreSegments = 0;
+
+ } while (byMoreSegments);
+
+ // Gotta clear this out from the last loop
+ if (Count)
+ ret = 0;
+ }
+ }
+
+ // check if error occured...
+ if (ret)
+ status = ret;
+
+ // Now slim down the list to just what we need.
+ if (!status) {
+ Users = (USER_LIST *) ReallocMemory((HGLOBAL) Users, sizeof(USER_LIST) + (sizeof(USER_BUFFER) * Count));
+
+ if (!Users)
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ else {
+ // Sort the server list before putting it in the dialog
+ UserBuffer = Users->UserBuffer;
+ qsort((void *) UserBuffer, (size_t) Count, sizeof(USER_BUFFER), UserListCompare);
+ }
+ }
+
+ Users->Count = Count;
+ *lpUsers = Users;
+
+ return status;
+
+} // NWPrintOpsEnum
+
+
+
+/////////////////////////////////////////////////////////////////////////
+
+VOID
+NWServerTimeGet(
+ )
+
+/*++
+
+Routine Description:
+
+ Queries server for it's current local time which is then converted to
+ elasped minutes since 1985 in order to compare with the lNextResetTime
+ field of the LOGIN_CONTROL structure (which must be byte-aligned).
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ DWORD dwYear = 0;
+ DWORD dwMonth = 0;
+ DWORD dwDay = 0;
+ DWORD dwHour = 0;
+ DWORD dwMinute = 0;
+ DWORD dwSecond = 0;
+ DWORD dwDayOfWeek = 0;
+ DWORD dwServerTime = 0;
+
+ CachedServerTime = 0xffffffff; // re-initialize...
+
+ if (!NWGetFileServerDateAndTime(
+ CachedConn,
+ (LPBYTE)&dwYear,
+ (LPBYTE)&dwMonth,
+ (LPBYTE)&dwDay,
+ (LPBYTE)&dwHour,
+ (LPBYTE)&dwMinute,
+ (LPBYTE)&dwSecond,
+ (LPBYTE)&dwDayOfWeek))
+ {
+ if (NWTimeMap(dwDay, dwMonth, dwYear, 1985, &dwServerTime))
+ {
+ dwServerTime += dwHour * 3600;
+ dwServerTime += dwMinute * 60;
+ dwServerTime += dwSecond;
+
+ CachedServerTime = dwServerTime / 60; // convert to minutes...
+ }
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+BOOL
+IsNCPServerFPNW(
+ NWCONN_HANDLE Conn
+ )
+
+/*++
+
+Routine Description:
+
+ Check if this an FPNW server by checking for a specific object
+ type and property.
+
+Arguments:
+
+ Conn - connection id of ncp server.
+
+Return Value:
+
+ Returns true if ncp server is fpnw.
+
+--*/
+
+{
+ NWCCODE ret;
+ BYTE bySegment[128];
+ BYTE byMoreSegments, byPropertyFlags;
+
+ memset(bySegment, 0, sizeof(bySegment));
+
+ ret = NWReadPropertyValue(
+ CachedConn,
+ MS_EXTENDED_NCPS,
+ 0x3B06,
+ FPNW_PDC,
+ 1,
+ bySegment,
+ &byMoreSegments,
+ &byPropertyFlags
+ );
+
+ return (ret == SUCCESSFUL) && (BOOL)(BYTE)bySegment[0];
+}
diff --git a/private/nw/convert/nwconv/nwnetapi.h b/private/nw/convert/nwconv/nwnetapi.h
new file mode 100644
index 000000000..d9089a520
--- /dev/null
+++ b/private/nw/convert/nwconv/nwnetapi.h
@@ -0,0 +1,133 @@
+/*+-------------------------------------------------------------------------+
+ | Copyright 1993-1994 (C) Microsoft Corporation - All rights reserved. |
+ +-------------------------------------------------------------------------+*/
+
+#ifndef _HNWNETAPI_
+#define _HNWNETAPI_
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+#ifndef NTSTATUS
+typedef LONG NTSTATUS;
+#endif
+
+typedef struct _USER_RIGHTS_LIST {
+ TCHAR Name[MAX_USER_NAME_LEN];
+ DWORD Rights;
+} USER_RIGHTS_LIST;
+
+int NWGetMaxServerNameLen();
+int NWGetMaxUserNameLen();
+
+BOOL NWObjectNameGet( DWORD ObjectID, LPTSTR ObjectName );
+
+VOID NWUserInfoGet(LPTSTR szUserName, VOID **UInfo);
+VOID NWUserInfoLog(LPTSTR szUserName, VOID *UInfo);
+DWORD NWServerSet(LPTSTR FileServer);
+DWORD NWServerFree();
+
+DWORD NWUsersEnum(USER_LIST **lpUsers, BOOL Display);
+DWORD NWGroupsEnum(GROUP_LIST **lpGroups, BOOL Display);
+DWORD NWGroupUsersEnum(LPTSTR GroupName, USER_LIST **lpUsers);
+DWORD NWUserEquivalenceEnum(LPTSTR UserName, USER_LIST **lpUsers);
+DWORD NWServerEnum(LPTSTR Container, SERVER_BROWSE_LIST **lpServList);
+DWORD NWSharesEnum(SHARE_LIST **lpShares);
+
+VOID NWUserDefaultsGet(VOID **UDefaults);
+VOID NWUserDefaultsMap(VOID *NWUDefaults, NT_DEFAULTS *NTDefaults);
+VOID NWUserDefaultsLog(VOID *UDefaults);
+VOID NWNetUserMapInfo (LPTSTR szUserName, VOID *UInfo, NT_USER_INFO *NT_UInfo );
+VOID NWFPNWMapInfo(VOID *NWUInfo, PFPNW_INFO fpnw);
+
+VOID NWUseDel(LPTSTR ServerName);
+BOOL NWIsAdmin(LPTSTR UserName);
+VOID NWServerInfoReset(SOURCE_SERVER_BUFFER *SServ);
+VOID NWServerInfoSet(LPTSTR ServerName, SOURCE_SERVER_BUFFER *SServ);
+BOOL NWServerValidate(HWND hWnd, LPTSTR ServerName, BOOL DupFlag);
+DWORD NWFileRightsEnum(LPTSTR FileName, USER_RIGHTS_LIST **lpUsers, DWORD *UserCount, BOOL DownLevel);
+LPTSTR NWRightsLog(DWORD Rights);
+LPTSTR NWSpecialNamesMap(LPTSTR Name);
+
+VOID NWServerTimeGet();
+
+typedef struct _NW_TO_NT_MAPPING {
+ ULONG NWRight ;
+ ULONG NTAccess ;
+} NW_TO_NT_MAPPING, *PNW_TO_NT_MAPPING ;
+
+
+//
+// structure used to define how the Rights for a Netware object maps
+// to the corresponding NT AccessMasks.
+//
+// first entry is the AceFlags to distinguish between ACE for the Object
+// and ACE for inheritted objects
+//
+// the GENERIC_MAPPING structure should match that already defined for
+// the NT object in question.
+//
+// the array of NW mappings defines the NT Access Mask for each NW Right
+// the object uses. the last entry should be {0, 0}.
+//
+// for example, file object mappings:
+//
+// RIGHTS_MAPPING FileRightsMapping =
+// {
+// 0,
+// { FILE_GENERIC_READ,
+// FILE_GENERIC_WRITE,
+// FILE_GENERIC_EXECUTE,
+// FILE_ALL_ACCESS
+// },
+// { { NW_FILE_READ, GENERIC_READ }
+// { NW_FILE_WRITE, GENERIC_WRITE }
+// { NW_FILE_CREATE, 0 }
+// { NW_FILE_DELETE, GENERIC_WRITE }
+// { NW_FILE_PERM, WRITE_DAC }
+// { NW_FILE_SCAN, 0 }
+// { NW_FILE_MODIFY, GENERIC_WRITE }
+// { NW_FILE_SUPERVISOR, GENERIC_ALL }
+// { 0, 0 }
+// }
+// } ;
+//
+//
+
+typedef struct _RIGHTS_MAPPING {
+ ULONG NtAceFlags ;
+ GENERIC_MAPPING GenericMapping ;
+ NW_TO_NT_MAPPING Nw2NtMapping[] ;
+} RIGHTS_MAPPING, *PRIGHTS_MAPPING ;
+
+
+// predefined mappings (defined in nwnetapi.c)
+extern RIGHTS_MAPPING FileRightsMapping ;
+extern RIGHTS_MAPPING DirRightsMapping ;
+extern RIGHTS_MAPPING PrintRightsMapping ;
+extern RIGHTS_MAPPING JobRightsMapping ;
+
+// define the NW_FILE_* rights
+#define NW_FILE_READ 0x0001
+#define NW_FILE_WRITE 0x0002
+#define NW_FILE_CREATE 0x0008
+#define NW_FILE_DELETE 0x0010
+#define NW_FILE_PERM 0x0020
+#define NW_FILE_SCAN 0x0040
+#define NW_FILE_MODIFY 0x0080
+#define NW_FILE_SUPERVISOR 0x0100
+
+#define NW_PRINT_USER 0x0001
+#define NW_PRINT_ADMIN 0x0002
+#define NW_PRINTJOB_ADMIN 0x0004
+
+NTSTATUS MapNwRightsToNTAccess( ULONG NWRights, PRIGHTS_MAPPING pMap, ACCESS_MASK *pAccessMask ) ;
+DWORD NWPrintOpsEnum(USER_LIST **lpUsers);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/private/nw/convert/nwconv/nwrights.h b/private/nw/convert/nwconv/nwrights.h
new file mode 100644
index 000000000..ff78cb9e8
--- /dev/null
+++ b/private/nw/convert/nwconv/nwrights.h
@@ -0,0 +1,168 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+
+Module Name:
+
+ nwrights.h
+
+Abstract:
+
+ This module contains the prototypes for the
+ routines called to manipulate security descriptors.
+
+Author:
+
+ Chuck Y. Chan (chuckc)
+
+Revision History:
+
+ ChuckC 24th Oct 1993 Created
+
+--*/
+
+
+//
+// structure used to define how a single NW Right maps to
+// an NT Access mask.
+//
+
+typedef struct _NW_TO_NT_MAPPING {
+ ULONG NWRight ;
+ ULONG NTAccess ;
+} NW_TO_NT_MAPPING, *PNW_TO_NT_MAPPING ;
+
+
+//
+// structure used to define how the Rights for a Netware object maps
+// to the corresponding NT AccessMasks.
+//
+// first entry is the AceFlags to distinguish between ACE for the Object
+// and ACE for inheritted objects
+//
+// the GENERIC_MAPPING structure should match that already defined for
+// the NT object in question.
+//
+// the array of NW mappings defines the NT Access Mask for each NW Right
+// the object uses. the last entry should be {0, 0}.
+//
+// for example, file object mappings:
+//
+// RIGHTS_MAPPING FileRightsMapping =
+// {
+// 0,
+// { FILE_GENERIC_READ,
+// FILE_GENERIC_WRITE,
+// FILE_GENERIC_EXECUTE,
+// FILE_ALL_ACCESS
+// },
+// { { NW_FILE_READ, GENERIC_READ }
+// { NW_FILE_WRITE, GENERIC_WRITE }
+// { NW_FILE_CREATE, 0 }
+// { NW_FILE_DELETE, GENERIC_WRITE }
+// { NW_FILE_PERM, WRITE_DAC }
+// { NW_FILE_SCAN, 0 }
+// { NW_FILE_MODIFY, GENERIC_WRITE }
+// { NW_FILE_SUPERVISOR, GENERIC_ALL }
+// { 0, 0 }
+// }
+// } ;
+//
+//
+
+typedef struct _RIGHTS_MAPPING {
+ ULONG NtAceFlags ;
+ GENERIC_MAPPING GenericMapping ;
+ NW_TO_NT_MAPPING Nw2NtMapping[] ;
+} RIGHTS_MAPPING, *PRIGHTS_MAPPING ;
+
+//
+// define the NW_FILE_* rights
+//
+
+#define NW_FILE_READ 0x0001
+#define NW_FILE_WRITE 0x0002
+#define NW_FILE_CREATE 0x0008
+#define NW_FILE_DELETE 0x0010
+#define NW_FILE_PERM 0x0020
+#define NW_FILE_SCAN 0x0040
+#define NW_FILE_MODIFY 0x0080
+#define NW_FILE_SUPERVISOR 0x0100
+
+#define NW_PRINT_USER 0x0001
+#define NW_PRINT_ADMIN 0x0002
+#define NW_PRINTJOB_ADMIN 0x0004
+
+//
+// #define these so they can be changed easily. these macros
+// should be used to free the memory allocated by the routines in
+// this module.
+//
+
+#define NW_ALLOC(x) ((LPBYTE)LocalAlloc(LPTR,x))
+#define NW_FREE(p) ((void)LocalFree((HLOCAL)p))
+
+//
+// predefined mappings (defined in nwrights.c)
+//
+
+extern RIGHTS_MAPPING FileRightsMapping ;
+extern RIGHTS_MAPPING DirRightsMapping ;
+extern RIGHTS_MAPPING PrintRightsMapping ;
+extern RIGHTS_MAPPING JobRightsMapping ;
+
+//
+// function prototypes. details of parameters can be found in nwrights.c
+//
+
+NTSTATUS
+NwAddRight(
+ PSECURITY_DESCRIPTOR pSD,
+ PSID pSid,
+ ULONG Rights,
+ PRIGHTS_MAPPING pMap,
+ PSECURITY_DESCRIPTOR *ppNewSD
+ ) ;
+
+NTSTATUS
+NwRemoveRight(
+ PSECURITY_DESCRIPTOR pSD,
+ PSID pSid,
+ ULONG Rights,
+ PRIGHTS_MAPPING pMap
+ ) ;
+
+NTSTATUS
+NwCheckTrusteeRights(
+ PSECURITY_DESCRIPTOR pSD,
+ PSID pSid,
+ ULONG Rights,
+ PRIGHTS_MAPPING pMap
+ ) ;
+
+NTSTATUS
+NwScanTrustees(
+ PSECURITY_DESCRIPTOR pSD,
+ PSID **pppSids,
+ ULONG **ppRights,
+ ULONG *pCount,
+ BOOL fAccessRightsOnly,
+ PRIGHTS_MAPPING pMapObject,
+ PRIGHTS_MAPPING pMapNewObject
+ ) ;
+
+NTSTATUS MapNwRightsToNTAccess(
+ ULONG NWRights,
+ PRIGHTS_MAPPING pMap,
+ ACCESS_MASK *pAccessMask
+ ) ;
+
+NTSTATUS MapSpecificToGeneric(
+ ACCESS_MASK * pAccessMask,
+ PGENERIC_MAPPING pGenMapping ) ;
+
+NTSTATUS CreateNewSecurityDescriptor(
+ PSECURITY_DESCRIPTOR *ppNewSD,
+ PSECURITY_DESCRIPTOR pSD,
+ PACL pAcl) ;
diff --git a/private/nw/convert/nwconv/resource.h b/private/nw/convert/nwconv/resource.h
new file mode 100644
index 000000000..8c0ef69e1
--- /dev/null
+++ b/private/nw/convert/nwconv/resource.h
@@ -0,0 +1,245 @@
+/*+-------------------------------------------------------------------------+
+ | Copyright 1993-1994 (C) Microsoft Corporation - All rights reserved. |
+ +-------------------------------------------------------------------------+*/
+
+#ifndef _HRESOURCE_
+#define _HRESOURCE_
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+#include "helpid.h"
+
+// +------------------------------------------------------------------------+
+// | Main MoveIt Dialog
+// +------------------------------------------------------------------------+
+
+#define IDR_MAINFRAME 1
+#define IDD_ABOUTBOX 999
+
+#define ID_FILE_OPEN 1000
+#define ID_HELP_CONT 1001
+#define ID_HELP_INDEX 1002
+#define ID_HELP_USING 1003
+#define ID_APP_ABOUT 1004
+#define ID_FILE_SAVE 1005
+#define ID_LOGGING 1006
+#define ID_FILE_DEFAULT 1007
+#define IDC_EXIT 1008
+
+#define IDC_EDIT1 1010
+#define IDC_LIST1 1011
+#define IDC_TRIAL 1012
+#define IDC_USERINF 1013
+#define IDC_ADD 1014
+#define IDC_DELETE 1015
+#define IDC_FILEINF 1016
+
+#ifndef IDHELP
+#define IDHELP 1017
+#endif // IDHELP
+
+// Common Advanced>> button
+#define IDC_ADVANCED 1018
+#define ID_INIT 1019
+
+// +------------------------------------------------------------------------+
+// | Get Servers Dialog
+// +------------------------------------------------------------------------+
+#define IDC_EDITNWSERV 1020
+#define IDC_EDITNTSERV 1021
+#define IDC_NWBROWSE 1022
+#define IDC_NTBROWSE 1023
+#define ID_SETSEL 1024
+
+// +------------------------------------------------------------------------+
+// | User Dialog
+// +------------------------------------------------------------------------+
+#define IDC_CHKUSERS 1030
+#define IDC_TABUSERS 1031
+
+#define IDC_PWCONST 1032
+
+#define IDC_CHKPWFORCE 1033
+#define IDC_USERCONST 1034
+#define IDC_GROUPCONST 1035
+
+#define IDC_CHKSUPER 1036
+#define IDC_CHKADMIN 1037
+#define IDC_CHKNETWARE 1038
+#define IDC_CHKMAPPING 1039
+#define IDC_MAPPINGFILE 1040
+#define IDC_BTNMAPPINGFILE 1041
+#define IDC_BTNMAPPINGEDIT 1042
+#define IDC_CHKFPNW 1043
+
+// These must be contiguous...
+#define IDC_RADIO1 1045
+#define IDC_RADIO2 1046
+#define IDC_RADIO3 1047
+#define IDC_RADIO4 1048
+#define IDC_RADIO5 1049
+#define IDC_RADIO6 1050
+#define IDC_RADIO7 1051
+#define IDC_RADIO8 1052
+#define IDC_RADIO9 1053
+#define IDC_RADIO10 1054
+
+#define IDC_TRUSTED 1055
+#define IDC_CHKTRUSTED 1056
+#define IDC_BTNTRUSTED 1057
+
+#define IDC_STATDUP 1058
+
+#define IDC_DEFAULTBOX 1059
+
+// +------------------------------------------------------------------------+
+// +------------------------------------------------------------------------+
+
+#define IDC_DOMAIN 1060
+#define IDC_LOADNWSERV 1061
+#define IDC_LOADNTSERV 1062
+#define IDC_LOADDOMAIN 1063
+#define IDC_ALTOK 1064
+
+// +------------------------------------------------------------------------+
+// +------------------------------------------------------------------------+
+#define IDC_CHKUVERBOSE 1070
+#define IDC_CHKFVERBOSE 1071
+#define IDC_CHKERROR 1072
+#define IDC_VIEWLOG 1073
+
+// +------------------------------------------------------------------------+
+// +------------------------------------------------------------------------+
+
+#define IDC_S_CUR_CONV 1100
+#define IDC_S_TOT_CONV 1101
+#define IDC_S_SRCSERV 1102
+#define IDC_S_DESTSERV 1103
+#define IDC_S_CONVTXT 1104
+#define IDC_S_CUR_NUM 1105
+#define IDC_S_CUR_TOT 1106
+#define IDC_S_ITEMLABEL 1107
+#define IDC_S_STATUSITEM 1108
+#define IDC_S_TOT_COMP 1109
+#define IDC_S_TOT_GROUPS 1110
+#define IDC_S_TOT_USERS 1111
+#define IDC_S_TOT_FILES 1112
+#define IDC_S_TOT_ERRORS 1113
+
+// +------------------------------------------------------------------------+
+// +------------------------------------------------------------------------+
+#define IDC_PANEL1 1130
+#define IDC_PANEL2 1131
+#define IDC_PANEL3 1132
+#define IDC_PANEL4 1133
+#define IDC_PANEL5 1134
+
+#define IDC_PANEL6 1135
+#define IDC_PANEL7 1136
+#define IDC_PANEL8 1137
+#define IDC_PANEL9 1138
+#define IDC_PANEL10 1139
+
+#define IDR_LISTICONS 1200
+
+// +------------------------------------------------------------------------+
+// +------------------------------------------------------------------------+
+
+#define IDC_CHKFILES 1301
+#define IDC_MODIFY 1302
+#define IDC_FILES 1303
+#define IDC_FOPTIONS 1304
+#define IDC_TABFILES 1305
+#define IDC_LIST2 1306
+
+#define IDC_COMBO1 1307
+#define IDC_COMBO2 1308
+#define IDC_NEWSHARE 1309
+#define IDC_PROPERTIES 1310
+
+#define ID_UPDATELIST 1311
+#define IDC_VOLUME 1312
+#define IDC_FSERVER 1313
+#define IDC_TSERVER 1314
+
+#define IDR_FILEICONS 1320
+#define IDR_CHECKICONS 1321
+#define ID_CHECKCHECK 1322
+
+// Menus for the dialog
+#define IDM_EXP_ONE 1330
+#define IDM_EXP_BRANCH 1331
+#define IDM_EXP_ALL 1332
+#define IDM_COLLAPSE 1333
+
+#define IDM_VIEW_BOTH 1334
+#define IDM_VIEW_TREE 1335
+#define IDM_VIEW_DIR 1336
+
+#define IDM_HIDDEN 1337
+#define IDM_SYSTEM 1338
+
+// +------------------------------------------------------------------------+
+// +------------------------------------------------------------------------+
+#define IDC_PASSWORD 1340
+#define IDC_SERVNAME 1341
+#define IDC_USERNAME 1342
+
+// +------------------------------------------------------------------------+
+// +------------------------------------------------------------------------+
+#define IDC_SHARENAME 1350
+#define IDC_EDIT2 1352
+#define IDC_EDIT3 1353
+#define ID_UPDATECOMBO 1354
+
+// +------------------------------------------------------------------------+
+// +------------------------------------------------------------------------+
+#define IDC_OLDNAME 1360
+#define IDC_NEWNAME 1361
+
+// +------------------------------------------------------------------------+
+// +------------------------------------------------------------------------+
+#define IDC_LIST3 1370
+#define IDC_T_DRIVES 1371
+#define IDC_T_SHARES 1372
+#define IDC_T_VOLUMES 1373
+#define IDC_VERSION 1374
+#define IDC_TYPE 1375
+
+#define ID_REDRAWLIST 1380
+
+// +------------------------------------------------------------------------+
+// +------------------------------------------------------------------------+
+#define IDC_USERS 1390
+#define IDC_GROUPS 1391
+
+// +------------------------------------------------------------------------+
+// +------------------------------------------------------------------------+
+#define IDC_DLGMAIN 1999
+#define IDC_STATIC -1
+
+#define IDH_H 3000
+
+// +------------------------------------------------------------------------+
+// | About Box
+// +------------------------------------------------------------------------+
+#define IDC_AVAIL_MEM 101
+#define IDC_PHYSICAL_MEM 101
+#define IDC_LICENSEE_COMPANY 104
+#define IDC_LICENSEE_NAME 105
+#define IDD_SPLASH 105
+#define IDC_MATH_COPR 106
+#define IDC_DISK_SPACE 107
+#define IDC_BIGICON 1001
+
+
+#define IDM_ADDSEL 2000
+#define IDC_SIZEHORZ 500
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/nw/convert/nwconv/sbrowse.c b/private/nw/convert/nwconv/sbrowse.c
new file mode 100644
index 000000000..9ee80657c
--- /dev/null
+++ b/private/nw/convert/nwconv/sbrowse.c
@@ -0,0 +1,1153 @@
+/*
+ +-------------------------------------------------------------------------+
+ | Server Browsing Dialog Routines |
+ +-------------------------------------------------------------------------+
+ | (c) Copyright 1993-1994 |
+ | Microsoft Corp. |
+ | All rights reserved |
+ | |
+ | Program : [sbrowse.c] |
+ | Programmer : Arthur Hanson |
+ | Original Program Date : [Dec 01, 1993] |
+ | Last Update : [Jun 16, 1994] |
+ | |
+ | Version: 1.00 |
+ | |
+ | Description: |
+ | |
+ | History: |
+ | arth Jun 16, 1994 1.00 Original Version. |
+ | |
+ +-------------------------------------------------------------------------+
+*/
+
+#include "globals.h"
+
+#include <math.h>
+
+#include "nwconv.h"
+#include "hierdraw.h"
+#include "convapi.h"
+#include "ntnetapi.h"
+#include "nwnetapi.h"
+
+#define SERVER_TYPE_NT 0
+#define SERVER_TYPE_NW 1
+
+extern BOOL IsNetWareBrowse;
+static int BrowseType;
+
+
+TCHAR NetProvider[30];
+
+
+// Internal defines
+VOID DlgServSel_OnDrawItem(HWND hwnd, DRAWITEMSTRUCT FAR* lpDrawItem);
+BOOL DlgServSel_OnCommand(HWND hDlg, WPARAM wParam, LPARAM lParam);
+VOID DlgServSel_ActionItem(HWND hWndList, DWORD dwData, WORD wItemNum);
+LRESULT CALLBACK DlgServSel(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
+BOOL EnumServs(HWND hDlg);
+
+
+#define ROWS 2
+#define COLS 3
+
+HEIRDRAWSTRUCT HierDrawStruct;
+
+static HWND hEdit;
+static HWND hParent;
+static SERVER_BROWSE_LIST *ServList = NULL;
+static TCHAR SourceServer[256];
+static TCHAR DestServer[256];
+static DEST_SERVER_BUFFER *DServ = NULL;
+static SOURCE_SERVER_BUFFER *SServ = NULL;
+BOOL DlgOk;
+
+
+/*+-------------------------------------------------------------------------+
+ | BrowseListCompare()
+ |
+ +-------------------------------------------------------------------------+*/
+int __cdecl BrowseListCompare(const void *arg1, const void *arg2) {
+ SERVER_BROWSE_BUFFER *SLarg1, *SLarg2;
+
+ SLarg1 = (SERVER_BROWSE_BUFFER *) arg1;
+ SLarg2 = (SERVER_BROWSE_BUFFER *) arg2;
+
+ // This works as the first item of the structure is the string
+ return lstrcmpi( SLarg1->Name, SLarg2->Name);
+
+} // BrowseListCompare
+
+
+/*+-------------------------------------------------------------------------+
+ | BrowseListInit()
+ |
+ +-------------------------------------------------------------------------+*/
+void BrowseListInit(HWND hDlg, int ServerType) {
+ SERVER_BROWSE_BUFFER *SList;
+ DWORD Status;
+ DWORD Count;
+ DWORD Index;
+ DWORD i;
+ HWND hCtrl;
+
+ switch (BrowseType) {
+ case BROWSE_TYPE_NT:
+ Status = NTServerEnum(NULL, &ServList);
+ break;
+
+ case BROWSE_TYPE_NW:
+ Status = NWServerEnum(NULL, &ServList);
+ break;
+ }
+
+ if (ServList) {
+ Count = ServList->Count;
+ SList = ServList->SList;
+
+ // Sort the server list before putting it in the dialog
+ qsort((void *) SList, (size_t) Count, sizeof(SERVER_BROWSE_BUFFER), BrowseListCompare);
+
+ hCtrl = GetDlgItem(hDlg, IDC_LIST1);
+ for (i = 0; i < Count; i++) {
+ Index = i;
+ SendMessage(hCtrl, LB_ADDSTRING, (WPARAM) 0, (LPARAM) Index);
+ }
+
+ }
+
+} // BrowseListInit
+
+
+/*+-------------------------------------------------------------------------+
+ | BrowseListFree()
+ |
+ +-------------------------------------------------------------------------+*/
+void BrowseListFree(SERVER_BROWSE_LIST *ServList) {
+ DWORD i;
+ DWORD Count;
+ SERVER_BROWSE_BUFFER *SList;
+
+ if (!ServList)
+ return;
+
+ SList = ServList->SList;
+ Count = ServList->Count;
+ for (i = 0; i < Count; i++)
+ if (SList[i].child)
+ FreeMemory((LPBYTE) SList[i].child);
+
+ FreeMemory((LPBYTE) ServList);
+ ServList = NULL;
+
+} // BrowseListFree
+
+
+/*+-------------------------------------------------------------------------+
+ | DlgServSel_Do()
+ |
+ +-------------------------------------------------------------------------+*/
+int DlgServSel_Do(int BType, HWND hwndOwner) {
+ int result;
+ DLGPROC lpfndp;
+
+ BrowseType = BType;
+
+ // Init the Hier Draw stuff - Need to do this here so we have a value
+ // for WM_MEASUREITEM which is sent before the WM_INITDIALOG message
+ HierDraw_DrawInit(hInst, IDR_LISTICONS, ROWS, COLS, FALSE, &HierDrawStruct, TRUE );
+
+ lpfndp = (DLGPROC)MakeProcInstance((FARPROC)DlgServSel, hInst);
+ result = DialogBox(hInst, TEXT("DlgServSel"), hwndOwner, lpfndp) ;
+
+ FreeProcInstance((FARPROC)lpfndp);
+ HierDraw_DrawTerm(&HierDrawStruct);
+
+ return result;
+} // DlgServSel_Do
+
+
+
+/*+-------------------------------------------------------------------------+
+ | BrowseListFind()
+ |
+ +-------------------------------------------------------------------------+*/
+SERVER_BROWSE_BUFFER *BrowseListFind(DWORD dwData) {
+ DWORD ContainerNum;
+ DWORD Index = 0;
+ SERVER_BROWSE_LIST *ServerSubList;
+
+ Index = LOWORD(dwData);
+ ContainerNum = HIWORD(dwData);
+
+ if (!ContainerNum)
+ return(&ServList->SList[Index]);
+ else {
+ ContainerNum--; // Adjust for 0 index
+ ServerSubList = (SERVER_BROWSE_LIST *) ServList->SList[ContainerNum].child;
+ return(&ServerSubList->SList[Index]);
+ }
+
+} // BrowseListFind
+
+
+/*+-------------------------------------------------------------------------+
+ | DlgServSel_OnDrawItem()
+ |
+ +-------------------------------------------------------------------------+*/
+VOID DlgServSel_OnDrawItem(HWND hwnd, DRAWITEMSTRUCT FAR* lpDrawItem) {
+ TCHAR szText[MAX_PATH + 1];
+ DWORD dwData;
+ int nLevel = 0;
+ int nRow = 0;
+ int nColumn = 0;
+ DWORD dwConnectLevel = 0;
+ SERVER_BROWSE_BUFFER *SList;
+
+ // if there is nothing to browse then don't need to draw anything.
+ if (ServList == NULL)
+ return;
+
+ dwData = lpDrawItem->itemData;
+
+ SList = BrowseListFind(dwData);
+
+ // if there is a double back-slash then trim it off...
+ if ((SList->Name[0] == TEXT('\\')) && (SList->Name[1] == TEXT('\\')))
+ lstrcpy(szText, &(SList->Name[2]));
+ else
+ lstrcpy(szText, SList->Name);
+
+ // Select the correct icon, open folder, closed folder, or document.
+ switch(BrowseType) {
+ case BROWSE_TYPE_NT:
+ break;
+
+ case BROWSE_TYPE_NW:
+ nRow = 1;
+ break;
+
+ }
+
+ // Can this item be opened ?
+ if (SList->Container) {
+
+ // Is it open ?
+ if ( HierDraw_IsOpened(&HierDrawStruct, dwData) )
+ nColumn = 1;
+ else
+ nColumn = 0;
+ }
+ else {
+ if (!IsNetWareBrowse)
+ nLevel = 1;
+
+ nColumn = 2;
+ }
+
+ // All set to call drawing function.
+ HierDraw_OnDrawItem(hwnd, lpDrawItem, nLevel, dwConnectLevel, szText, nRow, nColumn, &HierDrawStruct);
+
+ return;
+
+} // DlgServSel_OnDrawItem
+
+
+
+/*+-------------------------------------------------------------------------+
+ | DlgServSel_OnCommand()
+ |
+ +-------------------------------------------------------------------------+*/
+BOOL DlgServSel_OnCommand(HWND hDlg, WPARAM wParam, LPARAM lParam) {
+ int wmId, wmEvent;
+ WORD wItemNum;
+ DWORD dwData;
+ HWND hCtrl;
+ TCHAR ServerName[MAX_NW_OBJECT_NAME_LEN + 1];
+ HWND hParent;
+ SERVER_BROWSE_BUFFER *SList;
+
+ wmId = LOWORD(wParam);
+ wmEvent = HIWORD(wParam);
+
+ switch (wmId) {
+ case IDOK:
+ hCtrl = GetDlgItem(hDlg, IDC_LIST1);
+
+ wItemNum = (WORD) SendMessage(hCtrl, LB_GETCURSEL, 0, 0L);
+
+ if (wItemNum != (WORD) LB_ERR) {
+ dwData = (DWORD) SendMessage(hCtrl, LB_GETITEMDATA, wItemNum, 0L);
+
+ if (dwData != LB_ERR)
+ DlgServSel_ActionItem(hCtrl, dwData, (WORD) wItemNum);
+ }
+ break;
+
+ case IDC_ALTOK:
+ hCtrl = GetDlgItem(hDlg, IDC_EDIT1);
+ * (WORD *)ServerName = sizeof(ServerName);
+ SendMessage(hCtrl, EM_GETLINE, 0, (LPARAM) ServerName);
+
+ CanonServerName((LPTSTR) ServerName);
+
+ hParent = GetWindow (hDlg, GW_OWNER);
+
+ if (IsNetWareBrowse)
+ hCtrl = GetDlgItem(hParent, IDC_EDITNWSERV);
+ else
+ hCtrl = GetDlgItem(hParent, IDC_EDITNTSERV);
+
+ SendMessage(hCtrl, WM_SETTEXT, (WPARAM) 0, (LPARAM) ServerName);
+ EndDialog(hDlg, 0);
+ break;
+
+ case ID_INIT:
+ SetFocus(GetDlgItem(hDlg, IDC_EDIT1));
+ break;
+
+ case IDCANCEL:
+ EndDialog(hDlg, 0);
+ break;
+
+ case IDHELP:
+ switch (BrowseType) {
+ case BROWSE_TYPE_NT:
+ WinHelp(hDlg, HELP_FILE, HELP_CONTEXT, (DWORD) IDC_HELP_BROWSENT);
+ break;
+
+ case BROWSE_TYPE_NW:
+ WinHelp(hDlg, HELP_FILE, HELP_CONTEXT, (DWORD) IDC_HELP_BROWSENW);
+ break;
+
+ }
+ break;
+
+ case IDC_CHKUSERS:
+ break;
+
+ case IDC_LOADNWSERV:
+ BrowseListInit(hDlg, SERVER_TYPE_NW);
+ break;
+
+ case IDC_LOADNTSERV:
+ BrowseListInit(hDlg, SERVER_TYPE_NT);
+ break;
+
+ case IDC_LOADDOMAIN:
+ BrowseListInit(hDlg, SERVER_TYPE_NT);
+ break;
+
+ case ID_SETSEL:
+ hCtrl = GetDlgItem(hDlg, IDC_LIST1);
+ wItemNum = (WORD) SendMessage(hCtrl, LB_GETCURSEL, 0, 0L);
+
+ if (wItemNum != (WORD) LB_ERR) {
+ dwData = (DWORD) SendMessage(hCtrl, LB_GETITEMDATA, wItemNum, 0L);
+
+ if (dwData != LB_ERR) {
+ SList = BrowseListFind(dwData);
+
+ // Is this an item or folder
+ if (!SList->Container) {
+ // is a server - so put it up in edit box
+ if (SList->Name[0] == TEXT('\\') && SList->Name[1] == TEXT('\\'))
+ SendMessage(hEdit, WM_SETTEXT, (WPARAM) 0, (LPARAM) &SList->Name[2]);
+ else
+ SendMessage(hEdit, WM_SETTEXT, (WPARAM) 0, (LPARAM) SList->Name);
+
+ hCtrl = GetDlgItem(hDlg, IDC_ALTOK);
+ EnableWindow(hCtrl, TRUE);
+ } else {
+ SendMessage(hEdit, WM_SETTEXT, (WPARAM) 0, (LPARAM) TEXT(""));
+ hCtrl = GetDlgItem(hDlg, IDC_ALTOK);
+ EnableWindow(hCtrl, FALSE);
+ }
+ }
+ }
+ break;
+
+ case IDC_LIST1:
+
+ switch (wmEvent) {
+ case LBN_DBLCLK:
+ hCtrl = GetDlgItem(hDlg, IDC_LIST1);
+ wItemNum = (WORD) SendMessage(hCtrl, LB_GETCURSEL, 0, 0L);
+
+ if (wItemNum != (WORD) LB_ERR) {
+ dwData = (DWORD) SendMessage(hCtrl, LB_GETITEMDATA, wItemNum, 0L);
+
+ if (dwData != LB_ERR)
+ DlgServSel_ActionItem(hCtrl, dwData, wItemNum);
+ }
+ break;
+
+ case LBN_SELCHANGE:
+ PostMessage(hDlg, WM_COMMAND, (WPARAM) ID_SETSEL, 0L);
+ break;
+
+ }
+ break;
+
+ case IDC_EDIT1:
+
+ if (wmEvent == EN_CHANGE) {
+ hCtrl = GetDlgItem(hDlg, IDC_EDIT1);
+
+ if (SendMessage(hCtrl, EM_LINELENGTH, 0, 0)) {
+ hCtrl = GetDlgItem(hDlg, IDC_ALTOK);
+ EnableWindow(hCtrl, TRUE);
+ } else {
+ hCtrl = GetDlgItem(hDlg, IDC_ALTOK);
+ EnableWindow(hCtrl, FALSE);
+ }
+
+ }
+
+ break;
+
+ default:
+ return FALSE;
+
+ }
+
+ return TRUE;
+} // DlgServSel_OnCommand
+
+
+
+/*+-------------------------------------------------------------------------+
+ | DlgServSel_ActionItem()
+ |
+ +-------------------------------------------------------------------------+*/
+VOID DlgServSel_ActionItem(HWND hWndList, DWORD dwData, WORD wItemNum) {
+ DWORD dwIncr;
+ DWORD Parent;
+ DWORD Status;
+ DWORD Count;
+ SERVER_BROWSE_BUFFER *SList;
+ SERVER_BROWSE_LIST *SubList;
+ DWORD wItem, wCount, dwAddItem;
+
+ if (dwData == LB_ERR)
+ return;
+
+ SList = BrowseListFind(dwData);
+
+ // Is this an item or folder
+ if (!SList->Container) {
+ // is a server - so put it up in edit box
+ if (SList->Name[0] == TEXT('\\') && SList->Name[1] == TEXT('\\'))
+ SendMessage(hEdit, WM_SETTEXT, (WPARAM) 0, (LPARAM) &SList->Name[2]);
+ else
+ SendMessage(hEdit, WM_SETTEXT, (WPARAM) 0, (LPARAM) SList->Name);
+
+ PostMessage(hParent, WM_COMMAND, (WPARAM) IDC_ALTOK, (LPARAM) 0);
+
+ }
+ else {
+ SendMessage(hEdit, WM_SETTEXT, (WPARAM) 0, (LPARAM) TEXT(""));
+
+ // Is it open ?
+ if ( HierDraw_IsOpened(&HierDrawStruct, dwData) ) {
+
+ // It's open ... Close it
+ HierDraw_CloseItem(&HierDrawStruct, dwData);
+
+ // Remove the child items. Close any children that are
+ // open on the way.
+
+ // wItem can stay constant as we are moveing stuff up in the listbox as we
+ // are deleting.
+ wItemNum++;
+ dwIncr = SendMessage(hWndList, LB_GETITEMDATA, wItemNum, 0L);
+ SList = BrowseListFind(dwIncr);
+
+ while (!SList->Container) {
+ SendMessage(hWndList, LB_DELETESTRING, wItemNum, 0L);
+ dwIncr = SendMessage(hWndList, LB_GETITEMDATA, wItemNum, 0L);
+ SList = BrowseListFind(dwIncr);
+ }
+
+ Parent = HIWORD(dwIncr);
+ if (Parent) {
+ Parent--;
+ SList = BrowseListFind(Parent);
+ FreeMemory((LPBYTE) SList->child);
+ SList->child = NULL;
+ }
+
+ }
+ else {
+
+ // It's closed ... Open it
+ HierDraw_OpenItem(&HierDrawStruct, dwData);
+
+ SendMessage(hWndList, WM_SETREDRAW, FALSE, 0L); // Disable redrawing.
+
+ CursorHourGlass();
+
+ // Enumerate the servers in this container (domain)
+ Status = NTServerEnum(SList->Name, (SERVER_BROWSE_LIST **) &SList->child);
+
+ if (!Status) {
+ Parent = LOWORD(dwData);
+ SubList = (SERVER_BROWSE_LIST *) SList->child;
+ Count = SubList->Count;
+ SList = SubList->SList;
+
+ // Sort the server list before putting it in the dialog
+ qsort((void *) SList, (size_t) Count, sizeof(SERVER_BROWSE_BUFFER), BrowseListCompare);
+
+ for (wItem = wItemNum, wCount = 0, wItem++;
+ wCount < SubList->Count; wItem++, wCount++) {
+ dwAddItem = ((Parent + 1) << 16) + wCount;
+ SendMessage(hWndList, LB_INSERTSTRING, wItem, dwAddItem);
+ }
+
+ }
+
+ // Make sure as many child items as possible are showing
+ HierDraw_ShowKids(&HierDrawStruct, hWndList, (WORD) wItemNum, (WORD) SubList->Count );
+
+ CursorNormal();
+
+ SendMessage(hWndList, WM_SETREDRAW, TRUE, 0L); // Enable redrawing.
+ InvalidateRect(hWndList, NULL, TRUE); // Force redraw
+ }
+ }
+
+} // DlgServSel_ActionItem
+
+
+/*+-------------------------------------------------------------------------+
+ | ServerListScan()
+ |
+ | Given a key, scans the list of servers to find a matching first
+ | letter in the server name.
+ |
+ +-------------------------------------------------------------------------+*/
+WORD ServerListScan(HWND hWnd, DWORD dwData, TCHAR Key) {
+ BOOL Found = FALSE;
+ WORD wItemNum;
+ DWORD dwFindItem;
+ DWORD dwData2;
+ DWORD ContainerNum;
+ WORD Index = 0;
+ DWORD Count = 0;
+ SERVER_BROWSE_LIST *ptrServList;
+ SERVER_BROWSE_BUFFER *SList;
+
+ Index = LOWORD(dwData);
+ ContainerNum = HIWORD(dwData);
+
+ // Get the head of the Server list for the current item
+ if (!ContainerNum)
+ ptrServList = ServList;
+ else {
+ ptrServList = (SERVER_BROWSE_LIST *) ServList->SList[ContainerNum-1].child;
+ }
+
+ // Now iterate down through the list trying to find the key...
+ SList = ptrServList->SList;
+ while ((!Found) && (Count < ptrServList->Count)) {
+ if (SList[Count].Name[0] == Key) {
+ dwFindItem = Count;
+ Found = TRUE;
+ } else
+ Count++;
+ }
+
+ // If we found the key now have to find the appropriate index value
+ if (Found && (Index != Count)) {
+ // Fix up the new item data to find
+ if (ContainerNum)
+ dwFindItem += (ContainerNum << 16);
+
+ wItemNum = (WORD) SendMessage(hWnd, LB_GETCURSEL, 0, 0L);
+ Found = FALSE;
+ if (Index < Count) {
+ // search forward in list
+ dwData2 = 0;
+ while ((dwData2 != LB_ERR) && !Found) {
+ dwData2 = (DWORD) SendMessage(hWnd, LB_GETITEMDATA, wItemNum, 0L);
+
+ if (dwData2 == dwFindItem)
+ Found = TRUE;
+ else
+ wItemNum++;
+
+ }
+ } else {
+ // search backwards in list
+ Count = Index;
+ while ((wItemNum > 0) && !Found) {
+ wItemNum--;
+ dwData2 = (DWORD) SendMessage(hWnd, LB_GETITEMDATA, wItemNum, 0L);
+ Count--;
+
+ if (dwData2 == dwFindItem)
+ Found = TRUE;
+ }
+ }
+ } else
+ return (Index);
+
+ if (Found)
+ return(wItemNum);
+ else
+ return (Index);
+
+} // ServerListScan
+
+
+
+static WNDPROC _wpOrigWndProc;
+#define LISTBOX_COUNT 13
+
+/*+-------------------------------------------------------------------------+
+ | ListSubClassProc()
+ |
+ | Handles key processing for the hierarchical listbox. Specifically
+ | the up/down arrow keys and the letter keys.
+ |
+ +-------------------------------------------------------------------------+*/
+LRESULT CALLBACK ListSubClassProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
+ LRESULT lResult = 0;
+ BOOL fCallOrigProc = TRUE;
+ DWORD wItemNum;
+ DWORD wNewNum;
+ DWORD dwData;
+
+ switch (message) {
+
+ case WM_KEYDOWN:
+ wItemNum = (DWORD) SendMessage(hWnd, LB_GETCURSEL, 0, 0L);
+ if (wItemNum != (DWORD) LB_ERR)
+ dwData = (DWORD) SendMessage(hWnd, LB_GETITEMDATA, (WPARAM) wItemNum, 0L);
+ else {
+ wItemNum = 0;
+ SendMessage(hWnd, LB_SETCURSEL, (WPARAM) 0, 0L);
+ dwData = (DWORD) SendMessage(hWnd, LB_GETITEMDATA, (WPARAM) wItemNum, 0L);
+
+ if ((dwData == LB_ERR) || (dwData == LB_ERR))
+ break;
+ }
+
+ fCallOrigProc = FALSE;
+
+ switch (LOWORD(wParam)) {
+
+ case VK_PRIOR:
+ if (wItemNum > LISTBOX_COUNT)
+ wNewNum = wItemNum - LISTBOX_COUNT;
+ else
+ wNewNum = 0;
+
+ PostMessage(hWnd, LB_SETCURSEL, (WPARAM) wNewNum, 0L);
+ PostMessage(hParent, WM_COMMAND, (WPARAM) ID_SETSEL, 0L);
+ break;
+
+ case VK_NEXT:
+ wItemNum = wItemNum + LISTBOX_COUNT;
+ wNewNum = (WORD) SendMessage(hWnd, LB_GETCOUNT, (WPARAM) 0, 0L);
+
+ if (wItemNum < wNewNum)
+ PostMessage(hWnd, LB_SETCURSEL, (WPARAM) wItemNum, 0L);
+ else
+ PostMessage(hWnd, LB_SETCURSEL, (WPARAM) (wNewNum - 1), 0L);
+
+ PostMessage(hParent, WM_COMMAND, (WPARAM) ID_SETSEL, 0L);
+ break;
+
+ case VK_END:
+ wItemNum = (WORD) SendMessage(hWnd, LB_GETCOUNT, (WPARAM) 0, 0L);
+
+ if (wItemNum != LB_ERR)
+ PostMessage(hWnd, LB_SETCURSEL, (WPARAM) (wItemNum - 1), 0L);
+
+ PostMessage(hParent, WM_COMMAND, (WPARAM) ID_SETSEL, 0L);
+ break;
+
+ case VK_HOME:
+ PostMessage(hWnd, LB_SETCURSEL, (WPARAM) 0, 0L);
+ PostMessage(hParent, WM_COMMAND, (WPARAM) ID_SETSEL, 0L);
+ break;
+
+ case VK_UP:
+ if (wItemNum > 0) {
+ wItemNum--;
+ PostMessage(hWnd, LB_SETCURSEL, (WPARAM) wItemNum, 0L);
+ }
+ PostMessage(hParent, WM_COMMAND, (WPARAM) ID_SETSEL, 0L);
+ break;
+
+ case VK_DOWN:
+ wItemNum++;
+ PostMessage(hWnd, LB_SETCURSEL, (WPARAM) wItemNum, 0L);
+ PostMessage(hParent, WM_COMMAND, (WPARAM) ID_SETSEL, 0L);
+ break;
+
+ case VK_F1:
+ fCallOrigProc = TRUE;
+ break;
+
+ default:
+ wItemNum = ServerListScan(hWnd, dwData, (TCHAR) wParam);
+ PostMessage(hWnd, LB_SETCURSEL, (WPARAM) wItemNum, 0L);
+ PostMessage(hParent, WM_COMMAND, (WPARAM) ID_SETSEL, 0L);
+ break;
+
+ }
+
+ break;
+
+ }
+
+ if (fCallOrigProc)
+ lResult = CallWindowProc(_wpOrigWndProc, hWnd, message, wParam, lParam);
+
+ return (lResult);
+
+} // ListSubClassProc
+
+
+
+/*+-------------------------------------------------------------------------+
+ | DlgServSel()
+ |
+ +-------------------------------------------------------------------------+*/
+LRESULT CALLBACK DlgServSel(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
+ HWND hCtrl;
+
+ switch (message) {
+
+ case WM_INITDIALOG:
+ // Center the dialog over the application window
+ CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
+ hParent = hDlg;
+
+ // Disable the Add button until server is selected
+ hCtrl = GetDlgItem(hDlg, IDC_ALTOK);
+ EnableWindow(hCtrl, FALSE);
+
+ // subclass listbox handler
+ hCtrl = GetDlgItem(hDlg, IDC_LIST1);
+ _wpOrigWndProc = SubclassWindow(hCtrl, ListSubClassProc);
+
+ switch (BrowseType) {
+ case BROWSE_TYPE_NT:
+ PostMessage(hDlg, WM_COMMAND, IDC_LOADNTSERV, 0);
+ lstrcpy( NetProvider, NT_PROVIDER);
+ hCtrl = GetDlgItem(hDlg, IDC_EDIT1);
+ PostMessage(hCtrl, EM_LIMITTEXT, (WPARAM) MAX_NT_SERVER_NAME_LEN, 0);
+ break;
+
+ case BROWSE_TYPE_NW:
+ lstrcpy( NetProvider, NW_PROVIDER);
+ SetWindowText(hDlg, Lids(IDS_S_35));
+ PostMessage(hDlg, WM_COMMAND, IDC_LOADNWSERV, 0);
+ hCtrl = GetDlgItem(hDlg, IDC_EDIT1);
+ PostMessage(hCtrl, EM_LIMITTEXT, (WPARAM) MAX_NW_OBJECT_NAME_LEN, 0);
+ break;
+
+ }
+
+ hEdit = GetDlgItem(hDlg, IDC_EDIT1);
+ PostMessage(hDlg, WM_COMMAND, ID_INIT, 0);
+ return (TRUE);
+
+ case WM_DESTROY:
+ BrowseListFree(ServList);
+ break;
+
+ case WM_SETFONT:
+ // Set the text height
+ HierDraw_DrawSetTextHeight(GetDlgItem(hDlg, IDC_LIST1), (HFONT)wParam, &HierDrawStruct);
+
+ break;
+
+ case WM_COMMAND:
+ return DlgServSel_OnCommand(hDlg, wParam, lParam);
+
+ case WM_DRAWITEM:
+ DlgServSel_OnDrawItem(hDlg, (DRAWITEMSTRUCT FAR*)(lParam));
+ return TRUE;
+ break;
+
+ case WM_MEASUREITEM:
+ HierDraw_OnMeasureItem(hDlg, (MEASUREITEMSTRUCT FAR*)(lParam), &HierDrawStruct);
+ return TRUE;
+ break;
+
+ }
+
+ return (FALSE);
+
+ lParam;
+
+} // DlgServSel
+
+
+/*+-------------------------------------------------------------------------+
+ | DlgGetServ_OnInitDialog()
+ |
+ +-------------------------------------------------------------------------+*/
+BOOL DlgGetServ_OnInitDialog(HWND hDlg, HWND hwndFocus, LPARAM lParam) {
+ HWND hCtrl;
+
+ // Center the dialog over the application window
+ CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
+
+ // Disable the Add button until both servers are selected
+ hCtrl = GetDlgItem(hDlg, IDOK);
+ EnableWindow(hCtrl, FALSE);
+
+ hCtrl = GetDlgItem(hDlg, IDC_EDITNWSERV);
+ PostMessage(hCtrl, EM_LIMITTEXT, (WPARAM) MAX_NW_OBJECT_NAME_LEN, 0);
+ hCtrl = GetDlgItem(hDlg, IDC_EDITNTSERV);
+ PostMessage(hCtrl, EM_LIMITTEXT, (WPARAM) MAX_NT_SERVER_NAME_LEN, 0);
+ PostMessage(hDlg, WM_COMMAND, ID_INIT, 0);
+ return (TRUE);
+
+} // DlgGetServ_OnInitDialog
+
+
+/*+-------------------------------------------------------------------------+
+ | MapShare()
+ |
+ +-------------------------------------------------------------------------+*/
+// CODEWORK: This routine can be condensed - all the virtual add share stuff
+// can be compacted to a subroutine
+BOOL MapShare(SHARE_BUFFER *Share, DEST_SERVER_BUFFER *DServ) {
+ static TCHAR Path[MAX_PATH + 1];
+ SHARE_LIST *ShareList;
+ SHARE_BUFFER *SList;
+ SHARE_BUFFER *MatchShare;
+ VIRTUAL_SHARE_BUFFER *VShare;
+ DRIVE_BUFFER *DList;
+ DRIVE_BUFFER *MaxNTFS = NULL;
+ DRIVE_BUFFER *MaxFAT = NULL;
+ BOOL Match = FALSE;
+ BOOL NTFS = FALSE;
+ BOOL Virtual = FALSE;
+ DWORD i;
+
+ // First see if there is a 1:1 share correspondence already in existance
+
+ // the normal shares first...
+ ShareList = DServ->ShareList;
+ SList = ShareList->SList;
+
+ if (ShareList != NULL)
+ for (i = 0; ((i < ShareList->Count) && (!Match)); i++) {
+ if (!lstrcmpi(SList[i].Name, Share->Name)) {
+ MatchShare = &SList[i];
+ Match = TRUE;
+ }
+ } // match normal share 1:1
+
+ if (!Match) {
+ VShare = DServ->VShareStart;
+
+ while(VShare && !Match) {
+ if (!lstrcmpi(VShare->Name, Share->Name)) {
+ // will use VShare to point to matched share
+ Virtual = TRUE;
+ Match = TRUE;
+ } else
+ VShare = VShare->next;
+ }
+ } // match VShare 1:1
+
+ if (!Match) {
+ // No match so make share name the same - try to find NTFS drive with
+ // enough room to put it on.
+ DList = DServ->DriveList->DList;
+ if (DList != NULL)
+ for (i = 0; ((i < DServ->DriveList->Count) && (!Match)); i++) {
+ if (DList[i].Type == DRIVE_TYPE_NTFS) {
+ // Find our Max NTFS
+ if (!MaxNTFS)
+ MaxNTFS = &DList[i];
+ else
+ if ( (MaxNTFS->FreeSpace - MaxNTFS->AllocSpace) < (DList[i].FreeSpace - DList[i].AllocSpace) )
+ MaxNTFS = &DList[i];
+
+ // Is an NTFS Drive - check for space
+ if ((DList[i].FreeSpace - DList[i].AllocSpace) > Share->Size) {
+ // Checks out - create a new virutal share for this
+ Match = TRUE;
+ Virtual = TRUE;
+ wsprintf(Path, TEXT("%s:\\%s"), DList[i].Drive, Share->Name);
+ VShare = VShareListAdd(DServ, Share->Name, Path);
+ VShare->Drive = &DList[i];
+ }
+ }
+ }
+ } // match new NTFS share
+
+ if (!Match) {
+ // No NTFS drive so try other drives...
+ DList = DServ->DriveList->DList;
+ if (DList != NULL)
+ for (i = 0; ((i < DServ->DriveList->Count) && (!Match)); i++) {
+ if (DList[i].Type != DRIVE_TYPE_NTFS) {
+ // Find our Max FAT
+ if (!MaxFAT)
+ MaxFAT = &DList[i];
+ else
+ if ( (MaxFAT->FreeSpace - MaxFAT->AllocSpace) < (DList[i].FreeSpace - DList[i].AllocSpace) )
+ MaxFAT = &DList[i];
+
+ // Is an other Drive - check for space
+ if ((DList[i].FreeSpace - DList[i].AllocSpace) > Share->Size) {
+ // Checks out - create a new virutal share for this
+ Match = TRUE;
+ Virtual = TRUE;
+ wsprintf(Path, TEXT("%s:\\%s"), DList[i].Drive, Share->Name);
+ VShare = VShareListAdd(DServ, Share->Name, Path);
+ VShare->Drive = &DList[i];
+ }
+ }
+ }
+ } // match new FAT share
+
+ if (!Match) {
+ // we are going to assign some virtual share for this
+ Virtual = TRUE;
+
+ // use max space for NTFS else FAT
+ if (MaxNTFS) {
+ // if also have some fat partitions if they have more space use them
+ if (!(MaxFAT && ( (MaxNTFS->FreeSpace - MaxNTFS->AllocSpace) < (MaxFAT->FreeSpace - MaxFAT->AllocSpace) ))) {
+ Match = TRUE;
+ wsprintf(Path, TEXT("%s:\\%s"), MaxNTFS->Drive, Share->Name);
+ VShare = VShareListAdd(DServ, Share->Name, Path);
+ VShare->Drive = MaxNTFS;
+ }
+ }
+
+ // if we couldn't match with NTFS then use other drive types
+ if (!Match) {
+ Match = TRUE;
+ wsprintf(Path, TEXT("%s:\\%s"), MaxFAT->Drive, Share->Name);
+ VShare = VShareListAdd(DServ, Share->Name, Path);
+ VShare->Drive = MaxFAT;
+ }
+
+ } // match anything!!!
+
+ if (Match) {
+ // Have the match so adjust the params
+ if (!Virtual) {
+ Share->Virtual = FALSE;
+ Share->DestShare = MatchShare;
+
+ // if there is no drive specified (can be the case if the share points
+ // to an invalid drive - like a floppy) then we skip out and ignore it
+ // for right now. We will alert the user when they try to do the
+ // transfer.
+ if (MatchShare->Drive == NULL)
+ return TRUE;
+
+ MatchShare->Drive->AllocSpace += Share->Size;
+#ifdef DEBUG
+dprintf(TEXT("Matched Share: %s -> %s\n"), Share->Name, MatchShare->Name);
+#endif
+ } else {
+ Share->Virtual = TRUE;
+ Share->DestShare = (SHARE_BUFFER *) VShare;
+ VShare->Drive->AllocSpace += Share->Size;
+ VShare->UseCount++;
+#ifdef DEBUG
+dprintf(TEXT("Matched Virtual Share: %s -> %s\n"), Share->Name, VShare->Path);
+#endif
+ }
+
+ return TRUE;
+ } else {
+#ifdef DEBUG
+dprintf(TEXT("Couldn't Map Share: %s\n"), Share->Name);
+#endif
+ // Bad news - the destination server just can't handle this!
+ return FALSE;
+ }
+
+} // MapShare
+
+
+/*+-------------------------------------------------------------------------+
+ | ShareListInit()
+ |
+ +-------------------------------------------------------------------------+*/
+void ShareListInit(SHARE_LIST *ShareList, DEST_SERVER_BUFFER *DServ) {
+ SHARE_BUFFER *SList;
+ DWORD i;
+
+ // Mark that we want to convert all the shares
+ if (ShareList != NULL) {
+ SList = ShareList->SList;
+ ShareList->ConvertCount = ShareList->Count;
+
+ for (i = 0; i < ShareList->Count; i++) {
+ if (MapShare(&SList[i], DServ))
+ SList[i].Convert = TRUE;
+ }
+ }
+
+} // ShareListInit
+
+
+/*+-------------------------------------------------------------------------+
+ | DlgGetServ_OnCommand()
+ |
+ +-------------------------------------------------------------------------+*/
+BOOL DlgGetServ_OnCommand(HWND hDlg, int wmId, HWND hwndCtl, UINT wmEvent) {
+ HWND hCtrl;
+ BOOL Enable = FALSE;
+
+ switch (wmId) {
+ case IDOK:
+ hCtrl = GetDlgItem(hDlg, IDC_EDITNWSERV);
+ * (WORD *)SourceServer = sizeof(SourceServer);
+ * (WORD *)DestServer = sizeof(DestServer);
+ SendMessage(hCtrl, EM_GETLINE, 0, (LPARAM) SourceServer);
+ hCtrl = GetDlgItem(hDlg, IDC_EDITNTSERV);
+ SendMessage(hCtrl, EM_GETLINE, 0, (LPARAM) DestServer);
+
+ CanonServerName(SourceServer);
+ CanonServerName(DestServer);
+
+ if ( NWServerValidate(hDlg, SourceServer, TRUE) ) {
+ if ( NTServerValidate(hDlg, DestServer) ) {
+ // Check if we need to add server to server list
+ DServ = DServListFind(DestServer);
+
+ if (DServ == NULL) {
+ DServ = DServListAdd(DestServer);
+ NTServerInfoSet(hDlg, DestServer, DServ);
+ } else
+ DServ->UseCount++;
+
+ SServ = SServListFind(SourceServer);
+
+ if (SServ == NULL) {
+ SServ = SServListAdd(SourceServer);
+ NWServerInfoSet(SourceServer, SServ);
+ ShareListInit(SServ->ShareList, DServ);
+ }
+
+ DlgOk = TRUE;
+ EndDialog(hDlg, 0);
+ } else {
+ // Clean up connections that the Validation routines made
+ NWUseDel(SourceServer);
+ NTUseDel(DestServer);
+ hCtrl = GetDlgItem(hDlg, IDC_EDITNTSERV);
+ SetFocus(hCtrl);
+ }
+ } else {
+ // Clean up use validation routine made
+ NWUseDel(SourceServer);
+ hCtrl = GetDlgItem(hDlg, IDC_EDITNWSERV);
+ SetFocus(hCtrl);
+ }
+
+ return (TRUE);
+
+ case ID_INIT:
+ SetFocus(GetDlgItem(hDlg, IDC_EDITNWSERV));
+ break;
+
+ case IDCANCEL:
+ EndDialog(hDlg, 0);
+ DlgOk = FALSE;
+ return (TRUE);
+ break;
+
+ case IDHELP:
+ WinHelp(hDlg, HELP_FILE, HELP_CONTEXT, (DWORD) IDC_HELP_ADD);
+ return (TRUE);
+ break;
+
+ case IDC_NWBROWSE:
+ IsNetWareBrowse = TRUE;
+ DlgServSel_Do(BROWSE_TYPE_NW, hDlg);
+ return (TRUE);
+ break;
+
+ case IDC_NTBROWSE:
+ IsNetWareBrowse = FALSE;
+ DlgServSel_Do(BROWSE_TYPE_NT, hDlg);
+ return (TRUE);
+ break;
+
+ case IDC_EDITNWSERV:
+ case IDC_EDITNTSERV:
+ if (wmEvent == EN_CHANGE) {
+ hCtrl = GetDlgItem(hDlg, IDC_EDITNWSERV);
+
+ if (SendMessage(hCtrl, EM_LINELENGTH, 0, 0)) {
+ hCtrl = GetDlgItem(hDlg, IDC_EDITNTSERV);
+ if (SendMessage(hCtrl, EM_LINELENGTH, 0, 0))
+ Enable = TRUE;
+ }
+
+ hCtrl = GetDlgItem(hDlg, IDOK);
+ if (Enable)
+ EnableWindow(hCtrl, TRUE);
+ else
+ EnableWindow(hCtrl, FALSE);
+
+ }
+ break;
+ }
+
+} // DlgGetServ_OnCommand
+
+
+
+/*+-------------------------------------------------------------------------+
+ | DlgGetServ()
+ |
+ +-------------------------------------------------------------------------+*/
+LRESULT CALLBACK DlgGetServ(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
+
+ switch (msg) {
+ HANDLE_MSG(hDlg, WM_INITDIALOG, DlgGetServ_OnInitDialog);
+ HANDLE_MSG(hDlg, WM_COMMAND, DlgGetServ_OnCommand);
+
+ }
+
+ return (FALSE); // Didn't process the message
+
+ lParam;
+} // DlgGetServ
+
+
+/*+-------------------------------------------------------------------------+
+ | DialogGetServ()
+ |
+ +-------------------------------------------------------------------------+*/
+DWORD DialogServerBrowse(HINSTANCE hInst, HWND hDlg, SOURCE_SERVER_BUFFER **lpSourceServer, DEST_SERVER_BUFFER **lpDestServer) {
+ DLGPROC lpfnDlg;
+
+ SServListCurrent = NULL;
+ DServListCurrent = NULL;
+ SServ = NULL;
+ DServ = NULL;
+
+ lpfnDlg = MakeProcInstance((DLGPROC)DlgGetServ, hInst);
+ DialogBox(hInst, TEXT("DlgGetServ"), hDlg, lpfnDlg) ;
+ FreeProcInstance(lpfnDlg);
+
+ if (DlgOk) {
+ *lpSourceServer = SServ;
+ *lpDestServer = DServ;
+ return 0;
+ } else
+ return 1;
+
+} // DialogServerBrowse
diff --git a/private/nw/convert/nwconv/sbrowse.h b/private/nw/convert/nwconv/sbrowse.h
new file mode 100644
index 000000000..efd7d6fdb
--- /dev/null
+++ b/private/nw/convert/nwconv/sbrowse.h
@@ -0,0 +1,44 @@
+/*+-------------------------------------------------------------------------+
+ | Copyright 1993-1994 (C) Microsoft Corporation - All rights reserved. |
+ +-------------------------------------------------------------------------+*/
+
+#ifndef _HSBROWSE_
+#define _HSBROWSE_
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+#define BROWSE_TYPE_NT 0
+#define BROWSE_TYPE_NW 1
+#define BROWSE_TYPE_DOMAIN 2
+
+/*+-------------------------------------------------------------------------+
+ | Server Browsing Stuff |
+ +-------------------------------------------------------------------------+*/
+typedef struct _SERVER_BROWSE_BUFFER {
+ TCHAR Name[MAX_SERVER_NAME_LEN+1];
+ TCHAR Description[MAX_PATH+1];
+ BOOL Container;
+ struct _SERVER_BROWSE_LIST *child; // Points to a server list
+} SERVER_BROWSE_BUFFER;
+
+
+typedef struct _SERVER_BROWSE_LIST {
+ DWORD Count;
+ SERVER_BROWSE_BUFFER SList[];
+} SERVER_BROWSE_LIST;
+
+
+/*+-------------------------------------------------------------------------+
+ | Function Prototypes |
+ +-------------------------------------------------------------------------+*/
+LRESULT CALLBACK DlgServSel(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
+DWORD DialogServerBrowse(HINSTANCE hInst, HWND hDlg, SOURCE_SERVER_BUFFER **lpSourceServer, DEST_SERVER_BUFFER **lpDestServer);
+int DlgServSel_Do(int BType, HWND hwndOwner);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/nw/convert/nwconv/servlist.c b/private/nw/convert/nwconv/servlist.c
new file mode 100644
index 000000000..97a272746
--- /dev/null
+++ b/private/nw/convert/nwconv/servlist.c
@@ -0,0 +1,2094 @@
+/*
+ +-------------------------------------------------------------------------+
+ | Server Linked List Manipulation Routines |
+ +-------------------------------------------------------------------------+
+ | (c) Copyright 1993-1994 |
+ | Microsoft Corp. |
+ | All rights reserved |
+ | |
+ | Program : [ServList.c] |
+ | Programmer : Arthur Hanson |
+ | Original Program Date : [Feb 15, 1994] |
+ | Last Update : [Jun 16, 1994] |
+ | |
+ | Version: 1.00 |
+ | |
+ | Description: |
+ | Bunch of similar utility functions for adding to, deleting from, |
+ | saving and restoring data lists. |
+ | |
+ | |
+ | History: |
+ | arth Jun 16, 1994 1.00 Original Version. |
+ | |
+ +-------------------------------------------------------------------------+
+*/
+
+#include "globals.h"
+#include "convapi.h"
+#include "columnlb.h"
+#include "ntnetapi.h"
+#include "nwnetapi.h"
+#include "userdlg.h"
+#include "filedlg.h"
+
+// define from SBrowse.c -> for SourceShareListFixup
+BOOL MapShare(SHARE_BUFFER *Share, DEST_SERVER_BUFFER *DServ);
+
+static ULONG NumDServs = 0;
+static ULONG NumSServs = 0;
+static ULONG NumDomains = 0;
+
+// Note: Most of these routines are a bunch of doubly-linked list functions
+// as such it should be possible to condense them to use some common
+// functions for add/delete/insert. However virtual-shares will have
+// to be different as the Virtual BOOL has to be the first field.
+
+
+/*+-------------------------------------------------------------------------+
+ | Routines for Directory/File Trees |
+ +-------------------------------------------------------------------------+*/
+
+/*+-------------------------------------------------------------------------+
+ | TreeSave()
+ |
+ +-------------------------------------------------------------------------+*/
+void _TreeSaveR(HANDLE hFile, DIR_BUFFER *Dir) {
+ DIR_BUFFER *DList;
+ ULONG Size;
+ ULONG i;
+ DWORD wrote;
+
+ if (Dir == NULL)
+ return;
+
+ // First save out the file list for this node
+ if (Dir->FileList) {
+ WriteFile(hFile, &Dir->FileList->Count, sizeof(Dir->FileList->Count), &wrote, NULL);
+ Size = sizeof(FILE_LIST) + (Dir->FileList->Count * sizeof(FILE_BUFFER));
+ WriteFile(hFile, Dir->FileList, Size, &wrote, NULL);
+ }
+
+ if (Dir->DirList) {
+ DList = Dir->DirList->DirBuffer;
+
+ // first write out this dirlist then recurse down tree.
+ WriteFile(hFile, &Dir->DirList->Count, sizeof(Dir->DirList->Count), &wrote, NULL);
+ Size = sizeof(DIR_LIST) + (Dir->DirList->Count * sizeof(DIR_BUFFER));
+ WriteFile(hFile, Dir->DirList, Size, &wrote, NULL);
+
+ for (i = 0; i < Dir->DirList->Count; i++)
+ _TreeSaveR(hFile, &DList[i]);
+
+ }
+
+} // _TreeSaveR
+
+
+void TreeSave(HANDLE hFile, DIR_BUFFER *Dir) {
+ DWORD wrote;
+
+ if (Dir == NULL)
+ return;
+
+ // Make sure we save the minimum amount
+ TreePrune(Dir);
+
+ // write out the actual dir
+ WriteFile(hFile, Dir, sizeof(DIR_BUFFER), &wrote, NULL);
+
+ // now save it's child info recursively
+ _TreeSaveR(hFile, Dir);
+
+} // TreeSave
+
+
+/*+-------------------------------------------------------------------------+
+ | TreeLoad()
+ |
+ +-------------------------------------------------------------------------+*/
+void _TreeLoadR(HANDLE hFile, DIR_BUFFER *Dir) {
+ DIR_LIST *DList;
+ DIR_BUFFER *DBuff;
+ FILE_LIST *FList;
+ FILE_BUFFER *FBuff;
+ ULONG Size;
+ ULONG i;
+ DWORD wrote;
+ ULONG Count;
+
+ if (Dir == NULL)
+ return;
+
+ // First save out the file list for this node
+ if (Dir->FileList) {
+ ReadFile(hFile, &Count, sizeof(Count), &wrote, NULL);
+ Size = sizeof(FILE_LIST) + (sizeof(FILE_BUFFER) * Count);
+ FList = AllocMemory(Size);
+ ReadFile(hFile, FList, Size, &wrote, NULL);
+
+ // Read it in, now fixup the internal pointers.
+ FList->parent = Dir;
+
+ FBuff = FList->FileBuffer;
+ for (i = 0; i < FList->Count; i++)
+ FBuff[i].parent = FList;
+
+ Dir->FileList = FList;
+ }
+
+ if (Dir->DirList) {
+ ReadFile(hFile, &Count, sizeof(Count), &wrote, NULL);
+ Size = sizeof(DIR_LIST) + (sizeof(DIR_BUFFER) * Count);
+ DList = AllocMemory(Size);
+ ReadFile(hFile, DList, Size, &wrote, NULL);
+
+ // Read it in, now fixup the internal pointers.
+ DList->parent = Dir;
+
+ DBuff = DList->DirBuffer;
+ for (i = 0; i < DList->Count; i++)
+ DBuff[i].parent = DList;
+
+ Dir->DirList = DList;
+
+ // Now recurse into children and fix them up
+ for (i = 0; i < DList->Count; i++)
+ _TreeLoadR(hFile, &DBuff[i]);
+
+ }
+
+} // _TreeLoadR
+
+
+void TreeLoad(HANDLE hFile, DIR_BUFFER **pDir) {
+ DIR_BUFFER *Dir;
+ DWORD wrote;
+
+ if (pDir == NULL)
+ return;
+
+ // Allocate space for the new root
+ Dir = AllocMemory(sizeof(DIR_BUFFER));
+ if (Dir == NULL)
+ return;
+
+ memset(Dir, 0, sizeof(DIR_BUFFER));
+
+ // set passed in var to new memory and read in values from file
+ *pDir = Dir;
+ ReadFile(hFile, Dir, sizeof(DIR_BUFFER), &wrote, NULL);
+
+ // now recursively load child information
+ _TreeLoadR(hFile, Dir);
+
+} // TreeLoad
+
+
+/*+-------------------------------------------------------------------------+
+ | Routines for User Lists |
+ +-------------------------------------------------------------------------+*/
+
+/*+-------------------------------------------------------------------------+
+ | UserListCompare()
+ |
+ +-------------------------------------------------------------------------+*/
+int __cdecl UserListCompare(const void *arg1, const void *arg2) {
+ USER_BUFFER *ULarg1, *ULarg2;
+
+ ULarg1 = (USER_BUFFER *) arg1;
+ ULarg2 = (USER_BUFFER *) arg2;
+
+ // This works as the first item of the structure is the string
+ return lstrcmpi( ULarg1->Name, ULarg2->Name);
+
+} // UserListCompare
+
+
+/*+-------------------------------------------------------------------------+
+ | Routines for Share Lists |
+ +-------------------------------------------------------------------------+*/
+
+/*+-------------------------------------------------------------------------+
+ | ShareListDelete()
+ |
+ +-------------------------------------------------------------------------+*/
+void ShareListDelete(SHARE_LIST *ShareList) {
+ SHARE_BUFFER *SList;
+ VIRTUAL_SHARE_BUFFER *VShare;
+ ULONG i;
+
+ if (ShareList == NULL)
+ return;
+
+ SList = ShareList->SList;
+ for (i = 0; i < ShareList->Count; i++) {
+ if (SList[i].Root != NULL) {
+ TreeDelete(SList[i].Root);
+ FreeMemory(SList[i].Root);
+ }
+
+ // Note: SList[i].Drive points to a dest server drive, so we don't
+ // free it here.
+ if (!ShareList->Fixup)
+ if (SList[i].Virtual) {
+ VShare = (VIRTUAL_SHARE_BUFFER *) SList[i].DestShare;
+
+ if ((VShare != NULL) && VShare->UseCount)
+ VShare->UseCount--;
+ }
+
+ }
+
+} // ShareListDelete
+
+
+/*+-------------------------------------------------------------------------+
+ | SourceShareListFixup()
+ |
+ +-------------------------------------------------------------------------+*/
+void SourceShareListFixup(DEST_SERVER_BUFFER *DServ, SHARE_LIST *ShareList) {
+ TCHAR tmpName[MAX_SHARE_NAME_LEN + 1];
+ SHARE_BUFFER *SList;
+ ULONG i;
+ DWORD *VMap = NULL;
+
+ if (ShareList == NULL)
+ return;
+
+ ShareList->Fixup = FALSE;
+ // Generate Virtual Share Map
+ VShareListIndexMapGet(DServ, &VMap);
+
+ SList = ShareList->SList;
+ for (i = 0; i < ShareList->Count; i++) {
+ if (SList[i].DestShare != NULL)
+ if (SList[i].Virtual)
+ SList[i].DestShare = (SHARE_BUFFER *) VMap[(DWORD) SList[i].DestShare - 1];
+ else {
+ // Not a virtual share - now need to take name from path and rematch it
+ // to the new destination share list (dest sharename was stored in
+ // path in the ShareListSave routine).
+ lstrcpy(tmpName, SList[i].Name);
+ lstrcpy(SList[i].Name, SList[i].Path);
+
+ // clear out path and the old DestShare
+ memset(SList[i].Path, 0, sizeof(SList[i].Path));
+ SList[i].DestShare = NULL;
+
+ // Now map it to a dest share
+ MapShare(&SList[i], DServ);
+
+ // Restore real name
+ lstrcpy(SList[i].Name, tmpName);
+ }
+ }
+
+ if (VMap != NULL)
+ FreeMemory(VMap);
+
+} // SourceShareListFixup
+
+
+/*+-------------------------------------------------------------------------+
+ | DestShareListFixup()
+ |
+ +-------------------------------------------------------------------------+*/
+void DestShareListFixup(DEST_SERVER_BUFFER *DServ) {
+ SHARE_BUFFER *SList, *oSList;
+ SHARE_LIST *oShareList;
+ ULONG i, oi;
+ DRIVE_LIST *DList;
+ DRIVE_BUFFER *DBuff;
+ BOOL match;
+
+ if (DServ->ShareList == NULL)
+ return;
+
+ if (DServ->ShareList->Fixup == FALSE)
+ return; // do not fixup twice...
+
+ DServ->ShareList->Fixup = FALSE;
+ DList = DServ->DriveList;
+ DBuff = DList->DList;
+
+ oShareList = DServ->ShareList;
+ NTSharesEnum(&DServ->ShareList, DServ->DriveList);
+ if (DServ->ShareList == NULL)
+ return;
+
+ SList = DServ->ShareList->SList;
+ oSList = oShareList->SList;
+ for (i = 0; i < DServ->ShareList->Count; i++) {
+ match = FALSE;
+ oi = 0;
+
+ while ((!match) && (oi < oShareList->Count)) {
+ if (!lstrcmpi(SList[i].Name, oSList[oi].Name)) {
+ SList[i].Convert = oSList[oi].Convert;
+ SList[i].HiddenFiles = oSList[oi].HiddenFiles;
+ SList[i].SystemFiles = oSList[oi].SystemFiles;
+ match = TRUE;
+ }
+
+ oi++;
+ }
+
+ }
+
+ FreeMemory(oShareList);
+
+} // DestShareListFixup
+
+
+/*+-------------------------------------------------------------------------+
+ | ShareListSave()
+ |
+ +-------------------------------------------------------------------------+*/
+void ShareListSave(HANDLE hFile, SHARE_LIST *ShareList) {
+ SHARE_BUFFER *SList;
+ VIRTUAL_SHARE_BUFFER *VShare;
+ DWORD wrote;
+ ULONG Size, i;
+ DWORD *sb = NULL;
+
+ if (ShareList == NULL)
+ return;
+
+ Size = sizeof(SHARE_LIST) + (ShareList->Count * sizeof(SHARE_BUFFER));
+ WriteFile(hFile, &Size, sizeof(Size), &wrote, NULL);
+
+ // Need to make a temp array to hold DestShare info
+ sb = AllocMemory(ShareList->Count * sizeof(DWORD));
+
+ SList = ShareList->SList;
+
+ // Copy DestShares to temp holding place and replace them with
+ // their index
+ for (i = 0; i < ShareList->Count; i++) {
+ sb[i] = (DWORD) SList[i].DestShare;
+
+ if (SList[i].DestShare != NULL)
+ if (SList[i].Virtual) {
+ VShare = (VIRTUAL_SHARE_BUFFER *) SList[i].DestShare;
+ SList[i].DestShare = (SHARE_BUFFER *) (VShare->Index + 1);
+ } else {
+ // put the dest sharename into the path (temporarily) so that on load
+ // we have the name to match to the new share list. Can't match just
+ // the sharename to dest-sharename as the admin may have pointed it to
+ // a new one.
+ lstrcpy(SList[i].Path, SList[i].DestShare->Name);
+ SList[i].DestShare = (SHARE_BUFFER *) (SList[i].DestShare->Index + 1);
+ }
+ }
+
+ ShareList->Fixup = TRUE;
+ WriteFile(hFile, ShareList, Size, &wrote, NULL);
+ ShareList->Fixup = FALSE;
+
+ // Restore DestShare pointers
+ for (i = 0; i < ShareList->Count; i++)
+ SList[i].DestShare = (SHARE_BUFFER *) sb[i];
+
+
+ if (sb != NULL)
+ FreeMemory(sb);
+
+ // Share list array is saved out - now save out linked information
+ for (i = 0; i < ShareList->Count; i++)
+ if (SList[i].Root != NULL)
+ TreeSave(hFile, SList[i].Root);
+
+} // ShareListSave
+
+
+/*+-------------------------------------------------------------------------+
+ | ShareListLoad()
+ |
+ +-------------------------------------------------------------------------+*/
+void ShareListLoad(HANDLE hFile, SHARE_LIST **lpShareList) {
+ SHARE_BUFFER *SList;
+ SHARE_LIST *ShareList;
+ ULONG Size, i;
+ DWORD wrote;
+
+ // Get how long this record is
+ ReadFile(hFile, &Size, sizeof(Size), &wrote, NULL);
+
+ ShareList = AllocMemory(Size);
+ if (ShareList == NULL)
+ return;
+
+ ReadFile(hFile, ShareList, Size, &wrote, NULL);
+
+ // Share list array is read in - now load linked information
+ SList = ShareList->SList;
+ for (i = 0; i < ShareList->Count; i++)
+ if (SList[i].Root != NULL) {
+ SList[i].Root = NULL;
+ TreeLoad(hFile, &SList[i].Root);
+ }
+
+ *lpShareList = ShareList;
+
+#ifdef DEBUG
+dprintf(TEXT("<Share List Load>\n"));
+dprintf(TEXT(" Number of Entries: %lu\n\n"), ShareList->Count);
+
+for (i = 0; i < ShareList->Count; i++) {
+ dprintf(TEXT(" Name: %s\n"), SList[i].Name);
+ dprintf(TEXT(" Path: %s\n"), SList[i].Path);
+}
+dprintf(TEXT("\n"));
+#endif
+
+} // ShareListLoad
+
+
+/*+-------------------------------------------------------------------------+
+ | ShareListLog()
+ |
+ +-------------------------------------------------------------------------+*/
+void ShareListLog(SHARE_LIST *ShareList, BOOL DestServer) {
+ ULONG i;
+
+ if ((ShareList == NULL) || (ShareList->Count == 0))
+ return;
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+ LogWriteLog(1, Lids(IDS_L_125));
+
+ for (i = 0; i < ShareList->Count; i++) {
+ LogWriteLog(2, TEXT("%s\r\n"), ShareList->SList[i].Name);
+
+ if (DestServer)
+ LogWriteLog(3, Lids(IDS_L_8), ShareList->SList[i].Path);
+ }
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+
+} // ShareListLog
+
+
+/*+-------------------------------------------------------------------------+
+ | Routines for Source Server List |
+ +-------------------------------------------------------------------------+*/
+
+/*+-------------------------------------------------------------------------+
+ | SServListAdd()
+ |
+ +-------------------------------------------------------------------------+*/
+SOURCE_SERVER_BUFFER *SServListAdd(LPTSTR ServerName) {
+ SOURCE_SERVER_BUFFER *tmpPtr;
+ ULONG Size;
+
+ Size = sizeof(SOURCE_SERVER_BUFFER) + ((lstrlen(ServerName) + 3) * sizeof(TCHAR));
+ tmpPtr = AllocMemory(Size);
+
+ if (tmpPtr != NULL) {
+ // init it to NULL's
+ memset(tmpPtr, 0, Size);
+ tmpPtr->Name = (LPTSTR) ((BYTE *) tmpPtr + sizeof(SOURCE_SERVER_BUFFER));
+ tmpPtr->LName = tmpPtr->Name;
+ lstrcpy(tmpPtr->Name, TEXT("\\\\"));
+ tmpPtr->Name += 2;
+ lstrcpy(tmpPtr->Name, ServerName);
+
+ // link it into the list
+ if (!SServListStart)
+ SServListStart = SServListEnd = tmpPtr;
+ else {
+ SServListEnd->next = tmpPtr;
+ tmpPtr->prev = SServListEnd;
+ SServListEnd = tmpPtr;
+ }
+
+ NumSServs++;
+ }
+
+ return (tmpPtr);
+
+} // SServListAdd
+
+
+/*+-------------------------------------------------------------------------+
+ | SServListDelete()
+ |
+ +-------------------------------------------------------------------------+*/
+void SServListDelete(SOURCE_SERVER_BUFFER *tmpPtr) {
+ SOURCE_SERVER_BUFFER *PrevPtr;
+ SOURCE_SERVER_BUFFER *NextPtr;
+
+ if (tmpPtr == NULL)
+ return;
+
+ // Delete any attached share list first
+ ShareListDelete(tmpPtr->ShareList);
+
+ // Free any admin share we made
+ NWUseDel(tmpPtr->Name);
+
+ // Now unlink the actual server record
+ PrevPtr = tmpPtr->prev;
+ NextPtr = tmpPtr->next;
+
+ if (PrevPtr)
+ PrevPtr->next = NextPtr;
+
+ if (NextPtr)
+ NextPtr->prev = PrevPtr;
+
+ // Check if at end of list
+ if (SServListEnd == tmpPtr)
+ SServListEnd = PrevPtr;
+
+ // Check if at start of list
+ if (SServListStart == tmpPtr)
+ SServListStart = NextPtr;
+
+ FreeMemory(tmpPtr);
+ NumSServs--;
+
+ if (!NumSServs)
+ SServListStart = SServListEnd = NULL;
+
+} // SServListDelete
+
+
+/*+-------------------------------------------------------------------------+
+ | SServListDeleteAll()
+ |
+ +-------------------------------------------------------------------------+*/
+void SServListDeleteAll() {
+ SOURCE_SERVER_BUFFER *ServList;
+ SOURCE_SERVER_BUFFER *ServListNext;
+
+ // Now remove the entries from the internal list
+ ServList = SServListStart;
+
+ while (ServList) {
+ ServListNext = ServList->next;
+ SServListDelete(ServList);
+ ServList = ServListNext;
+ }
+
+} // SServListDeleteAll
+
+
+/*+-------------------------------------------------------------------------+
+ | SServListFind()
+ |
+ +-------------------------------------------------------------------------+*/
+SOURCE_SERVER_BUFFER *SServListFind(LPTSTR ServerName) {
+ BOOL Found = FALSE;
+ SOURCE_SERVER_BUFFER *ServList;
+
+ ServList = SServListStart;
+
+ while ((ServList && !Found)) {
+ if (!lstrcmpi(ServList->Name, ServerName))
+ Found = TRUE;
+ else
+ ServList = ServList->next;
+ }
+
+ if (!Found)
+ ServList = NULL;
+
+ return (ServList);
+
+} // SServListFind
+
+
+/*+-------------------------------------------------------------------------+
+ | SServListIndex()
+ |
+ | The index routines place an index number into each item of the list,
+ | and is called just before saving out the list. This is so when we
+ | read them in we can reference pointer links. In their normal state
+ | these data structures are cross referenced via pointers, but once
+ | saved/restored the pointers have no meaning, so we use the index to
+ | re-reference the pointers to the correct data items when they are
+ | read back in.
+ |
+ +-------------------------------------------------------------------------+*/
+void SServListIndex() {
+ SOURCE_SERVER_BUFFER *ServList;
+ USHORT Count = 0;
+
+ ServList = SServListStart;
+ while (ServList) {
+ ServList->Index = Count++;
+ ServList = ServList->next;
+ }
+
+} // SServListIndex
+
+
+/*+-------------------------------------------------------------------------+
+ | SServListIndexMapGet()
+ |
+ +-------------------------------------------------------------------------+*/
+void SServListIndexMapGet(DWORD **pMap) {
+ DWORD *Map = NULL;
+ SOURCE_SERVER_BUFFER *ServList;
+
+ Map = AllocMemory(NumSServs * sizeof(DWORD));
+
+ *pMap = Map;
+ if (Map == NULL)
+ return;
+
+ ServList = SServListStart;
+ while (ServList) {
+ Map[ServList->Index] = (DWORD) ServList;
+ ServList = ServList->next;
+ }
+
+} // SServListIndexMapGet
+
+
+/*+-------------------------------------------------------------------------+
+ | SServListFixup()
+ |
+ +-------------------------------------------------------------------------+*/
+void SServListFixup() {
+
+ // There is nothing to fixup in the source server list right now - the
+ // share list is done later...
+
+} // SServListFixup
+
+
+/*+-------------------------------------------------------------------------+
+ | SServListSave()
+ |
+ +-------------------------------------------------------------------------+*/
+void SServListSave(HANDLE hFile) {
+ DWORD wrote;
+ ULONG Size;
+ SOURCE_SERVER_BUFFER *ServList;
+
+ ServList = SServListStart;
+
+ while (ServList) {
+ Size = (lstrlen(ServList->Name) + 3) * sizeof(TCHAR);
+ Size += sizeof(SOURCE_SERVER_BUFFER);
+ WriteFile(hFile, &Size, sizeof(Size), &wrote, NULL);
+ WriteFile(hFile, ServList, Size, &wrote, NULL);
+
+ // Now save out linked information
+ ShareListSave(hFile, ServList->ShareList);
+
+ ServList = ServList->next;
+ }
+
+} // SServListSave
+
+
+/*+-------------------------------------------------------------------------+
+ | SServListLoad()
+ |
+ +-------------------------------------------------------------------------+*/
+void SServListLoad(HANDLE hFile) {
+ BOOL Continue = TRUE;
+ ULONG Size;
+ SOURCE_SERVER_BUFFER *tmpPtr;
+ DWORD wrote;
+
+ while (Continue) {
+ // Get how long this record is
+ ReadFile(hFile, &Size, sizeof(Size), &wrote, NULL);
+
+ tmpPtr = AllocMemory(Size);
+ if (tmpPtr == NULL)
+ return;
+
+ // Read in the record
+ ReadFile(hFile, tmpPtr, Size, &wrote, NULL);
+
+ // See if there are more records
+ if (tmpPtr->next == NULL)
+ Continue = FALSE;
+
+ // clear out the old links - and fixup new ones
+ tmpPtr->next = NULL;
+ tmpPtr->prev = NULL;
+
+ tmpPtr->LName = (LPTSTR) ((BYTE *) tmpPtr + sizeof(SOURCE_SERVER_BUFFER));
+ tmpPtr->Name = tmpPtr->LName + 2;
+
+ // link it into the list
+ if (!SServListStart)
+ SServListStart = SServListEnd = tmpPtr;
+ else {
+ SServListEnd->next = tmpPtr;
+ tmpPtr->prev = SServListEnd;
+ SServListEnd = tmpPtr;
+ }
+
+#ifdef DEBUG
+dprintf(TEXT("<Source Server Load>\n"));
+dprintf(TEXT(" Name: %s\n"), tmpPtr->Name);
+dprintf(TEXT(" Version: %u.%u\n\n"), (UINT) tmpPtr->VerMaj, (UINT) tmpPtr->VerMin);
+#endif
+
+ // Now load in linked information
+ if (tmpPtr->ShareList != NULL) {
+ tmpPtr->ShareList = NULL;
+ ShareListLoad(hFile, &tmpPtr->ShareList);
+ }
+
+ NumSServs++;
+ }
+
+} // SServListLoad
+
+
+
+/*+-------------------------------------------------------------------------+
+ | Routines for Destination Server List |
+ +-------------------------------------------------------------------------+*/
+
+/*+-------------------------------------------------------------------------+
+ | DServListAdd()
+ |
+ +-------------------------------------------------------------------------+*/
+DEST_SERVER_BUFFER *DServListAdd(LPTSTR ServerName) {
+ DEST_SERVER_BUFFER *tmpPtr;
+ ULONG Size, strlen1;
+
+ strlen1 = (lstrlen(ServerName) + 3) * sizeof(TCHAR);
+ Size = sizeof(DEST_SERVER_BUFFER) + strlen1;
+ tmpPtr = AllocMemory(Size);
+
+ if (tmpPtr != NULL) {
+ // init it to NULL's
+ memset(tmpPtr, 0, Size);
+ tmpPtr->Name = (LPTSTR) ((BYTE *) tmpPtr + sizeof(DEST_SERVER_BUFFER));
+ tmpPtr->LName = tmpPtr->Name;
+ lstrcpy(tmpPtr->Name, TEXT("\\\\"));
+ tmpPtr->Name += 2;
+ lstrcpy(tmpPtr->Name, ServerName);
+
+ // link it into the list
+ if (!DServListStart)
+ DServListStart = DServListEnd = tmpPtr;
+ else {
+ DServListEnd->next = tmpPtr;
+ tmpPtr->prev = DServListEnd;
+ DServListEnd = tmpPtr;
+ }
+
+ NumDServs++;
+ }
+
+ return (tmpPtr);
+
+} // DServListAdd
+
+
+/*+-------------------------------------------------------------------------+
+ | DServListDelete()
+ |
+ +-------------------------------------------------------------------------+*/
+void DServListDelete(DEST_SERVER_BUFFER *tmpPtr) {
+ DEST_SERVER_BUFFER *PrevPtr;
+ DEST_SERVER_BUFFER *NextPtr;
+
+ if (tmpPtr == NULL)
+ return;
+
+ if (tmpPtr->UseCount) {
+ // Decrement use count, only delete if actually gone.
+ tmpPtr->UseCount--;
+ if (tmpPtr->UseCount)
+ return;
+ }
+
+ // Delete any attached share list first
+ ShareListDelete(tmpPtr->ShareList);
+
+ // ...and virtual share list
+ VShareListDeleteAll(tmpPtr);
+
+ // ...and any drive list
+ if (tmpPtr->DriveList)
+ FreeMemory(tmpPtr->DriveList);
+
+ // ... domain as well
+ DomainListDelete(tmpPtr->Domain);
+
+ // -- don't free for user convenience, we will clean up at end, otherwise
+ // user must enter in password if delete/add
+ // Free any admin share we made
+ // NTUseDel(tmpPtr->Name);
+
+ // Now unlink the actual server record
+ PrevPtr = tmpPtr->prev;
+ NextPtr = tmpPtr->next;
+
+ if (PrevPtr)
+ PrevPtr->next = NextPtr;
+
+ if (NextPtr)
+ NextPtr->prev = PrevPtr;
+
+ // Check if at end of list
+ if (DServListEnd == tmpPtr)
+ DServListEnd = PrevPtr;
+
+ // Check if at start of list
+ if (DServListStart == tmpPtr)
+ DServListStart = NextPtr;
+
+ FreeMemory(tmpPtr);
+ NumDServs--;
+
+ if (!NumDServs)
+ DServListStart = DServListEnd = NULL;
+
+} // DServListDelete
+
+
+/*+-------------------------------------------------------------------------+
+ | DServListDeleteAll()
+ |
+ +-------------------------------------------------------------------------+*/
+void DServListDeleteAll() {
+ DEST_SERVER_BUFFER *ServList;
+ DEST_SERVER_BUFFER *ServListNext;
+
+ // Now remove the entries from the internal list
+ ServList = DServListStart;
+
+ while (ServList) {
+ ServListNext = ServList->next;
+ ServList->UseCount = 0;
+ DServListDelete(ServList);
+ ServList = ServListNext;
+ }
+
+} // DServListDeleteAll
+
+
+/*+-------------------------------------------------------------------------+
+ | DServListFind()
+ |
+ +-------------------------------------------------------------------------+*/
+DEST_SERVER_BUFFER *DServListFind(LPTSTR ServerName) {
+ BOOL Found = FALSE;
+ DEST_SERVER_BUFFER *ServList;
+
+ ServList = DServListStart;
+
+ while ((ServList && !Found)) {
+ if (!lstrcmpi(ServList->Name, ServerName))
+ Found = TRUE;
+ else
+ ServList = ServList->next;
+ }
+
+ if (!Found)
+ ServList = NULL;
+
+ return (ServList);
+
+} // DServListFind
+
+
+/*+-------------------------------------------------------------------------+
+ | DServListIndex()
+ |
+ +-------------------------------------------------------------------------+*/
+void DServListIndex() {
+ DEST_SERVER_BUFFER *ServList;
+ USHORT Count = 0;
+
+ ServList = DServListStart;
+ while (ServList) {
+ ServList->Index = Count++;
+
+ // Now index the virtual shares for this server
+ VShareListIndex(ServList);
+ ServList = ServList->next;
+ }
+
+} // DServListIndex
+
+
+/*+-------------------------------------------------------------------------+
+ | DServListIndexMapGet()
+ |
+ +-------------------------------------------------------------------------+*/
+void DServListIndexMapGet(DWORD **pMap) {
+ DWORD *Map = NULL;
+ DEST_SERVER_BUFFER *ServList;
+
+ Map = AllocMemory(NumDServs * sizeof(DWORD));
+
+ *pMap = Map;
+ if (Map == NULL)
+ return;
+
+ ServList = DServListStart;
+ while (ServList) {
+ Map[ServList->Index] = (DWORD) ServList;
+ ServList = ServList->next;
+ }
+
+} // DServListIndexMapGet
+
+
+/*+-------------------------------------------------------------------------+
+ | DServListFixup()
+ |
+ +-------------------------------------------------------------------------+*/
+void DServListFixup() {
+ DEST_SERVER_BUFFER *ServList;
+ DWORD *DMap = NULL;
+
+ DomainListIndexMapGet(&DMap);
+
+ if (DMap == NULL)
+ return;
+
+ // Just need to fixup the domain list pointers
+ ServList = DServListStart;
+ while (ServList) {
+
+ if (ServList->Domain != NULL)
+ ServList->Domain = (DOMAIN_BUFFER *) DMap[(DWORD) ServList->Domain - 1];
+
+ ServList = ServList->next;
+ }
+
+ FreeMemory(DMap);
+
+} // DServListFixup
+
+
+/*+-------------------------------------------------------------------------+
+ | DServListSave()
+ |
+ +-------------------------------------------------------------------------+*/
+void DServListSave(HANDLE hFile) {
+ DWORD wrote;
+ ULONG Size;
+ DEST_SERVER_BUFFER *ServList;
+ DOMAIN_BUFFER *db;
+
+ ServList = DServListStart;
+
+ while (ServList) {
+ Size = (lstrlen(ServList->Name) + 3) * sizeof(TCHAR);
+ Size += sizeof(DEST_SERVER_BUFFER);
+ WriteFile(hFile, &Size, sizeof(Size), &wrote, NULL);
+
+ // Need to de-reference the domain pointers to their index, and after
+ // saving restore them.
+ db = ServList->Domain;
+ if (ServList->Domain != NULL)
+ ServList->Domain = (DOMAIN_BUFFER *) (ServList->Domain->Index + 1);
+
+ WriteFile(hFile, ServList, Size, &wrote, NULL);
+ ServList->Domain = db;
+
+ // Now save out linked information
+ ShareListSave(hFile, ServList->ShareList);
+ VShareListSave(hFile, ServList);
+
+ ServList = ServList->next;
+ }
+
+} // DServListSave
+
+
+/*+-------------------------------------------------------------------------+
+ | DServListLoad()
+ |
+ +-------------------------------------------------------------------------+*/
+void DServListLoad(HANDLE hFile) {
+ BOOL Continue = TRUE;
+ ULONG Size;
+ DEST_SERVER_BUFFER *tmpPtr;
+ DWORD wrote;
+
+ while (Continue) {
+ // Get how long this record is
+ ReadFile(hFile, &Size, sizeof(Size), &wrote, NULL);
+
+ tmpPtr = AllocMemory(Size);
+ if (tmpPtr == NULL)
+ return;
+
+ // Read in the record
+ ReadFile(hFile, tmpPtr, Size, &wrote, NULL);
+
+ // See if there are more records
+ if (tmpPtr->next == NULL)
+ Continue = FALSE;
+
+ // clear out the old links - and fixup new ones
+ tmpPtr->next = NULL;
+ tmpPtr->prev = NULL;
+
+ tmpPtr->LName = (LPTSTR) ((BYTE *) tmpPtr + sizeof(DEST_SERVER_BUFFER));
+ tmpPtr->Name = tmpPtr->LName + 2;
+
+ // link it into the list
+ if (!DServListStart)
+ DServListStart = DServListEnd = tmpPtr;
+ else {
+ DServListEnd->next = tmpPtr;
+ tmpPtr->prev = DServListEnd;
+ DServListEnd = tmpPtr;
+ }
+
+ // The drive list is no longer valid - so clear it out.
+ tmpPtr->DriveList = NULL;
+
+#ifdef DEBUG
+dprintf(TEXT("<Dest Server Load>\n"));
+dprintf(TEXT(" Name: %s\n"), tmpPtr->Name);
+dprintf(TEXT(" Version: %u.%u\n\n"), (UINT) tmpPtr->VerMaj, (UINT) tmpPtr->VerMin);
+#endif
+
+ // Now load in linked information
+ if (tmpPtr->ShareList != NULL) {
+ tmpPtr->ShareList = NULL;
+ ShareListLoad(hFile, &tmpPtr->ShareList);
+ }
+
+ if (tmpPtr->VShareStart != NULL) {
+ tmpPtr->NumVShares = 0;
+ tmpPtr->VShareStart = tmpPtr->VShareEnd = NULL;
+ VShareListLoad(hFile, tmpPtr);
+ }
+
+ NumDServs++;
+ }
+
+} // DServListLoad
+
+
+/*+-------------------------------------------------------------------------+
+ | DServListSpaceFree()
+ |
+ +-------------------------------------------------------------------------+*/
+void DServListSpaceFree() {
+ DEST_SERVER_BUFFER *ServList;
+ DRIVE_BUFFER *Drive;
+ DRIVE_LIST *DList;
+ ULONG i;
+
+ ServList = DServListStart;
+ while (ServList) {
+ DList = ServList->DriveList;
+
+ if (DList != NULL) {
+ Drive = DList->DList;
+ for (i = 0; i < DList->Count; i++)
+ Drive[i].AllocSpace = 0;
+ }
+
+ ServList = ServList->next;
+ }
+
+} // DServListSpaceFree
+
+
+
+/*+-------------------------------------------------------------------------+
+ | Routines for Virtual Share Lists |
+ +-------------------------------------------------------------------------+*/
+
+/*+-------------------------------------------------------------------------+
+ | VShareListAdd()
+ |
+ +-------------------------------------------------------------------------+*/
+VIRTUAL_SHARE_BUFFER *VShareListAdd(DEST_SERVER_BUFFER *DServ, LPTSTR ShareName, LPTSTR Path) {
+ VIRTUAL_SHARE_BUFFER *tmpPtr;
+ ULONG Size, strlen1;
+
+ // base struct + both strings and 2 ending NULL's
+ strlen1 = ((lstrlen(ShareName)+ 1) * sizeof(TCHAR));
+ Size = sizeof(VIRTUAL_SHARE_BUFFER) + strlen1;
+ tmpPtr = AllocMemory(Size);
+
+ if (tmpPtr != NULL) {
+ // init it to NULL's
+ memset(tmpPtr, 0, Size);
+ tmpPtr->VFlag = TRUE;
+ tmpPtr->Name = (LPTSTR) ((BYTE *) tmpPtr + sizeof(VIRTUAL_SHARE_BUFFER));
+ lstrcpy(tmpPtr->Name, ShareName);
+ lstrcpy(tmpPtr->Path, Path);
+
+ // link it into the list
+ if (!DServ->VShareStart)
+ DServ->VShareStart = DServ->VShareEnd = tmpPtr;
+ else {
+ DServ->VShareEnd->next = tmpPtr;
+ tmpPtr->prev = DServ->VShareEnd;
+ DServ->VShareEnd = tmpPtr;
+ }
+
+ DServ->NumVShares++;
+ }
+
+ return (tmpPtr);
+
+} // VShareListAdd
+
+
+/*+-------------------------------------------------------------------------+
+ | VShareListDelete()
+ |
+ +-------------------------------------------------------------------------+*/
+void VShareListDelete(DEST_SERVER_BUFFER *DServ, VIRTUAL_SHARE_BUFFER *tmpPtr) {
+ VIRTUAL_SHARE_BUFFER *PrevPtr;
+ VIRTUAL_SHARE_BUFFER *NextPtr;
+
+ if (tmpPtr == NULL)
+ return;
+
+ if (tmpPtr->UseCount) {
+ // Decrement use count, only delete if actually gone.
+ tmpPtr->UseCount--;
+ if (tmpPtr->UseCount > 0)
+ return;
+ }
+
+ PrevPtr = tmpPtr->prev;
+ NextPtr = tmpPtr->next;
+
+ if (PrevPtr)
+ PrevPtr->next = NextPtr;
+
+ if (NextPtr)
+ NextPtr->prev = PrevPtr;
+
+ // Check if at end of list
+ if (DServ->VShareEnd == tmpPtr)
+ DServ->VShareEnd = PrevPtr;
+
+ // Check if at start of list
+ if (DServ->VShareStart == tmpPtr)
+ DServ->VShareStart = NextPtr;
+
+ FreeMemory(tmpPtr);
+ DServ->NumVShares--;
+
+ if (!DServ->NumVShares)
+ DServ->VShareStart = DServ->VShareEnd = NULL;
+
+} // VShareListDelete
+
+
+/*+-------------------------------------------------------------------------+
+ | VShareListDeleteAll()
+ |
+ +-------------------------------------------------------------------------+*/
+void VShareListDeleteAll(DEST_SERVER_BUFFER *DServ) {
+ VIRTUAL_SHARE_BUFFER *ShareList;
+ VIRTUAL_SHARE_BUFFER *ShareListNext;
+
+ // Now remove the entries from the internal list
+ ShareList = DServ->VShareStart;
+
+ while (ShareList) {
+ ShareListNext = ShareList->next;
+
+ // Flag to zero to make sure destroyed
+ ShareList->UseCount = 0;
+ VShareListDelete(DServ, ShareList);
+ ShareList = ShareListNext;
+ }
+
+} // VShareListDeleteAll
+
+
+/*+-------------------------------------------------------------------------+
+ | VShareListIndex()
+ |
+ +-------------------------------------------------------------------------+*/
+void VShareListIndex(DEST_SERVER_BUFFER *DServ) {
+ VIRTUAL_SHARE_BUFFER *ShareList;
+ USHORT Count = 0;
+
+ ShareList = DServ->VShareStart;
+ while (ShareList) {
+ ShareList->Index = Count++;
+ ShareList = ShareList->next;
+ }
+
+} // VShareListIndex
+
+
+/*+-------------------------------------------------------------------------+
+ | VShareListIndexMapGet()
+ |
+ +-------------------------------------------------------------------------+*/
+void VShareListIndexMapGet(DEST_SERVER_BUFFER *DServ, DWORD **pMap) {
+ DWORD *Map = NULL;
+ VIRTUAL_SHARE_BUFFER *ShareList;
+
+ Map = AllocMemory( DServ->NumVShares * sizeof(DWORD) );
+
+ *pMap = Map;
+ if (Map == NULL)
+ return;
+
+ ShareList = DServ->VShareStart;
+ while (ShareList) {
+ Map[ShareList->Index] = (DWORD) ShareList;
+ ShareList = ShareList->next;
+ }
+
+} // VShareListIndexMapGet
+
+
+/*+-------------------------------------------------------------------------+
+ | VShareListSave()
+ |
+ +-------------------------------------------------------------------------+*/
+void VShareListSave(HANDLE hFile, DEST_SERVER_BUFFER *DServ) {
+ DWORD wrote;
+ ULONG Size;
+ VIRTUAL_SHARE_BUFFER *ShareList;
+
+ ShareList = DServ->VShareStart;
+
+ while (ShareList) {
+ Size = (lstrlen(ShareList->Name) + 1) * sizeof(TCHAR);
+ Size += sizeof(VIRTUAL_SHARE_BUFFER);
+ WriteFile(hFile, &Size, sizeof(Size), &wrote, NULL);
+ WriteFile(hFile, ShareList, Size, &wrote, NULL);
+ ShareList = ShareList->next;
+ }
+
+} // VShareListSave
+
+
+/*+-------------------------------------------------------------------------+
+ | VShareListLoad()
+ |
+ +-------------------------------------------------------------------------+*/
+void VShareListLoad(HANDLE hFile, DEST_SERVER_BUFFER *DServ) {
+ BOOL Continue = TRUE;
+ ULONG Size;
+ VIRTUAL_SHARE_BUFFER *tmpPtr;
+ DWORD wrote;
+
+ while (Continue) {
+ // Get how long this record is
+ ReadFile(hFile, &Size, sizeof(Size), &wrote, NULL);
+
+ tmpPtr = AllocMemory(Size);
+ if (tmpPtr == NULL)
+ return;
+
+ // Read in the record
+ ReadFile(hFile, tmpPtr, Size, &wrote, NULL);
+
+ // See if there are more records
+ if (tmpPtr->next == NULL)
+ Continue = FALSE;
+
+ // clear out the old links - and fixup new ones
+ tmpPtr->next = NULL;
+ tmpPtr->prev = NULL;
+ tmpPtr->Name = (LPTSTR) ((BYTE *) tmpPtr + sizeof(VIRTUAL_SHARE_BUFFER));
+ tmpPtr->Drive = NULL;
+
+ // link it into the list
+ if (!DServ->VShareStart)
+ DServ->VShareStart = DServ->VShareEnd = tmpPtr;
+ else {
+ DServ->VShareEnd->next = tmpPtr;
+ tmpPtr->prev = DServ->VShareEnd;
+ DServ->VShareEnd = tmpPtr;
+ }
+
+ DServ->NumVShares++;
+ }
+
+} // VShareListLoad
+
+
+/*+-------------------------------------------------------------------------+
+ | VShareListFixup()
+ |
+ +-------------------------------------------------------------------------+*/
+void VShareListFixup(DEST_SERVER_BUFFER *DServ) {
+ VIRTUAL_SHARE_BUFFER *VShare;
+ USHORT Count = 0;
+ DWORD di;
+ DRIVE_LIST *Drives;
+ DRIVE_BUFFER *DList;
+ ULONG TotalDrives;
+ TCHAR Drive[2];
+
+ if (DServ->DriveList == NULL)
+ return;
+
+ TotalDrives = 0;
+ Drives = DServ->DriveList;
+ Drive[1] = TEXT('\0');
+ DList = Drives->DList;
+ TotalDrives = Drives->Count;
+
+ VShare = DServ->VShareStart;
+ while (VShare) {
+ VShare->Drive = NULL;
+
+ // Scan drive list looking for match to share path
+ for (di = 0; di < TotalDrives; di++) {
+ // Get first char from path - should be drive letter
+ Drive[0] = *VShare->Path;
+ if (!lstrcmpi(Drive, DList[di].Drive))
+ VShare->Drive = &DList[di];
+ }
+
+ VShare = VShare->next;
+ }
+
+} // VShareListFixup
+
+
+
+/*+-------------------------------------------------------------------------+
+ | Routines for Domain Lists |
+ +-------------------------------------------------------------------------+*/
+
+/*+-------------------------------------------------------------------------+
+ | DomainListAdd()
+ |
+ +-------------------------------------------------------------------------+*/
+DOMAIN_BUFFER *DomainListAdd(LPTSTR DomainName, LPTSTR PDCName) {
+ DOMAIN_BUFFER *tmpPtr;
+ ULONG Size, strlen1, strlen2;
+
+ // base struct + both strings and 2 ending NULL's
+ strlen1 = ((lstrlen(DomainName) + 1) * sizeof(TCHAR));
+ strlen2 = ((lstrlen(PDCName) + 1) * sizeof(TCHAR));
+ Size = sizeof(DOMAIN_BUFFER) + strlen1 + strlen2;
+ tmpPtr = AllocMemory(Size);
+
+ if (tmpPtr != NULL) {
+ // init it to NULL's
+ memset(tmpPtr, 0, Size);
+ tmpPtr->Name = (LPTSTR) ((BYTE *) tmpPtr + sizeof(DOMAIN_BUFFER));
+ tmpPtr->PDCName = (LPTSTR) ((BYTE *) tmpPtr + sizeof(DOMAIN_BUFFER) + strlen1);
+ lstrcpy(tmpPtr->Name, DomainName);
+ lstrcpy(tmpPtr->PDCName, PDCName);
+
+ // link it into the list
+ if (!DomainListStart)
+ DomainListStart = DomainListEnd = tmpPtr;
+ else {
+ DomainListEnd->next = tmpPtr;
+ tmpPtr->prev = DomainListEnd;
+ DomainListEnd = tmpPtr;
+ }
+
+ NumDomains++;
+ }
+
+ return (tmpPtr);
+
+} // DomainListAdd
+
+
+/*+-------------------------------------------------------------------------+
+ | DomainListDelete()
+ |
+ +-------------------------------------------------------------------------+*/
+void DomainListDelete(DOMAIN_BUFFER *tmpPtr) {
+ DOMAIN_BUFFER *PrevPtr;
+ DOMAIN_BUFFER *NextPtr;
+
+ if (tmpPtr == NULL)
+ return;
+
+ if (tmpPtr->UseCount) {
+ // Decrement use count, only delete if actually gone.
+ tmpPtr->UseCount--;
+ if (tmpPtr->UseCount)
+ return;
+ }
+
+ PrevPtr = tmpPtr->prev;
+ NextPtr = tmpPtr->next;
+
+ if (PrevPtr)
+ PrevPtr->next = NextPtr;
+
+ if (NextPtr)
+ NextPtr->prev = PrevPtr;
+
+ // Check if at end of list
+ if (DomainListEnd == tmpPtr)
+ DomainListEnd = PrevPtr;
+
+ // Check if at start of list
+ if (DomainListStart == tmpPtr)
+ DomainListStart = NextPtr;
+
+ FreeMemory(tmpPtr);
+ NumDomains--;
+
+ if (!NumDomains)
+ DomainListStart = DomainListEnd = NULL;
+
+} // DomainListDelete
+
+
+/*+-------------------------------------------------------------------------+
+ | DomainListDeleteAll()
+ |
+ +-------------------------------------------------------------------------+*/
+void DomainListDeleteAll() {
+ DOMAIN_BUFFER *DomList;
+ DOMAIN_BUFFER *DomListNext;
+
+ // Now remove the entries from the internal list
+ DomList = DomainListStart;
+
+ while (DomList) {
+ DomListNext = DomList->next;
+ DomList->UseCount = 0;
+ DomainListDelete(DomList);
+ DomList = DomListNext;
+ }
+
+} // DomainListDeleteAll
+
+
+/*+-------------------------------------------------------------------------+
+ | DomainListFind()
+ |
+ +-------------------------------------------------------------------------+*/
+DOMAIN_BUFFER *DomainListFind(LPTSTR DomainName) {
+ BOOL Found = FALSE;
+ DOMAIN_BUFFER *DomainList;
+
+ DomainList = DomainListStart;
+
+ while ((DomainList && !Found)) {
+ if (!lstrcmpi(DomainList->Name, DomainName))
+ Found = TRUE;
+ else
+ DomainList = DomainList->next;
+ }
+
+ if (!Found)
+ DomainList = NULL;
+
+ return (DomainList);
+
+} // DomainListFind
+
+
+/*+-------------------------------------------------------------------------+
+ | DomainListIndex()
+ |
+ +-------------------------------------------------------------------------+*/
+void DomainListIndex() {
+ DOMAIN_BUFFER *DomList;
+ USHORT Count = 0;
+
+ DomList = DomainListStart;
+ while (DomList) {
+ DomList->Index = Count++;
+ DomList = DomList->next;
+ }
+
+} // DomainListIndex
+
+
+/*+-------------------------------------------------------------------------+
+ | DomainListIndexMapGet()
+ |
+ +-------------------------------------------------------------------------+*/
+void DomainListIndexMapGet(DWORD **pMap) {
+ DWORD *Map = NULL;
+ DOMAIN_BUFFER *DomList;
+
+ Map = AllocMemory(NumDomains * sizeof(DWORD));
+
+ *pMap = Map;
+ if (Map == NULL)
+ return;
+
+ DomList = DomainListStart;
+ while (DomList) {
+ Map[DomList->Index] = (DWORD) DomList;
+ DomList = DomList->next;
+ }
+
+} // DomainListIndexMapGet
+
+
+/*+-------------------------------------------------------------------------+
+ | DomainListSave()
+ |
+ +-------------------------------------------------------------------------+*/
+void DomainListSave(HANDLE hFile) {
+ DWORD wrote;
+ ULONG Size;
+ DOMAIN_BUFFER *DomList;
+
+ WriteFile(hFile, &NumDomains, sizeof(NumDomains), &wrote, NULL);
+
+ DomList = DomainListStart;
+ while (DomList) {
+ Size = (lstrlen(DomList->Name) + 1) * sizeof(TCHAR);
+ Size += (lstrlen(DomList->PDCName) + 1) * sizeof(TCHAR);
+ Size += sizeof(DOMAIN_BUFFER);
+ WriteFile(hFile, &Size, sizeof(Size), &wrote, NULL);
+ WriteFile(hFile, DomList, Size, &wrote, NULL);
+ DomList = DomList->next;
+ }
+
+} // DomainListSave
+
+
+/*+-------------------------------------------------------------------------+
+ | DomainListLoad()
+ |
+ +-------------------------------------------------------------------------+*/
+void DomainListLoad(HANDLE hFile) {
+ BOOL Continue = TRUE;
+ ULONG Size, namelen;
+ DOMAIN_BUFFER *tmpPtr;
+ DWORD wrote;
+
+ ReadFile(hFile, &NumDomains, sizeof(NumDomains), &wrote, NULL);
+ if (NumDomains == 0)
+ Continue = FALSE;
+
+ NumDomains = 0;
+ while (Continue) {
+ // Get how long this record is
+ ReadFile(hFile, &Size, sizeof(Size), &wrote, NULL);
+
+ tmpPtr = AllocMemory(Size);
+ if (tmpPtr == NULL)
+ return;
+
+ // Read in the record
+ ReadFile(hFile, tmpPtr, Size, &wrote, NULL);
+
+ // See if there are more records
+ if (tmpPtr->next == NULL)
+ Continue = FALSE;
+
+ // clear out the old links - and fixup new ones
+ tmpPtr->next = NULL;
+ tmpPtr->prev = NULL;
+ tmpPtr->Name = (LPTSTR) ((BYTE *) tmpPtr + sizeof(DOMAIN_BUFFER));
+ namelen = ((lstrlen(tmpPtr->Name) + 1) * sizeof(TCHAR));
+ tmpPtr->PDCName = (LPTSTR) ((BYTE *) tmpPtr + sizeof(DOMAIN_BUFFER) + namelen);
+
+#ifdef DEBUG
+dprintf(TEXT("<Domain List Load>\n"));
+dprintf(TEXT(" Name: %s\n"), tmpPtr->Name);
+dprintf(TEXT(" PDC Name: %s\n\n"), tmpPtr->PDCName);
+#endif
+
+ // link it into the list
+ if (!DomainListStart)
+ DomainListStart = DomainListEnd = tmpPtr;
+ else {
+ DomainListEnd->next = tmpPtr;
+ tmpPtr->prev = DomainListEnd;
+ DomainListEnd = tmpPtr;
+ }
+
+ NumDomains++;
+ }
+
+} // DomainListLoad
+
+
+
+/*+-------------------------------------------------------------------------+
+ | Routines for Convert Lists |
+ +-------------------------------------------------------------------------+*/
+
+/*+-------------------------------------------------------------------------+
+ | ConvertListAdd()
+ |
+ | Allocates and inserts a new record for holding a server pair and
+ | associated conversion options.
+ |
+ | Comments:
+ | The convert list is a doubly linked list of records. Each record
+ | contains the source and destination server (server-pair) and a
+ | pointer to the conversion specific information. The exact info
+ | is specific to the platform being converted from (source server type)
+ |
+ +-------------------------------------------------------------------------+*/
+CONVERT_LIST *ConvertListAdd(SOURCE_SERVER_BUFFER *SourceServer, DEST_SERVER_BUFFER *DestServer) {
+ CONVERT_OPTIONS *cvo;
+ CONVERT_LIST *tmpPtr;
+ CONVERT_LIST *cPtr;
+ BOOL match = FALSE;
+
+ tmpPtr = AllocMemory(sizeof(CONVERT_LIST));
+
+ if (tmpPtr != NULL) {
+ // zero it out
+ memset(tmpPtr, 0, sizeof(CONVERT_LIST));
+
+ tmpPtr->SourceServ = SourceServer;
+ tmpPtr->FileServ = DestServer;
+
+ // initialize the conversion options
+ UserOptionsInit(&tmpPtr->ConvertOptions);
+
+ // Set NetWareInfo based on current FPNW setting...
+ cvo = (CONVERT_OPTIONS *) tmpPtr->ConvertOptions;
+ cvo->NetWareInfo = DestServer->IsFPNW;
+
+ FileOptionsInit(&tmpPtr->FileOptions);
+
+ // link it into the list - we want to link right after another
+ // convert to this same server, or to the same domain. otherwise
+ // tag it onto the end of the list.
+ if (!ConvertListStart)
+ ConvertListStart = ConvertListEnd = tmpPtr;
+ else {
+ // Have some files in the list - find out if destination server
+ // is already in the list and if so append after it.
+ if (DestServer->UseCount > 1) {
+ // should be in the list so find it.
+ cPtr = ConvertListStart;
+ while (cPtr && !match) {
+ if (!lstrcmpi(DestServer->Name, cPtr->FileServ->Name))
+ match = TRUE;
+ else
+ cPtr = cPtr->next;
+ }
+
+ if (match) {
+ // have a match - so go to the end of the matching pairs...
+ while (cPtr && match) {
+ if (lstrcmpi(DestServer->Name, cPtr->FileServ->Name))
+ match = FALSE;
+ else
+ cPtr = cPtr->next;
+ }
+
+ match = !match;
+ }
+ }
+
+ // if didn't find a server match - look for a domain match
+ if (!match) {
+ if (DestServer->InDomain && DestServer->Domain) {
+ // should be in the list so find it.
+ cPtr = ConvertListStart;
+ while (cPtr && !match) {
+ if (cPtr->FileServ->InDomain && cPtr->FileServ->Domain)
+ if (!lstrcmpi(DestServer->Domain->Name, cPtr->FileServ->Domain->Name))
+ match = TRUE;
+
+ if (!match)
+ cPtr = cPtr->next;
+ }
+
+ if (match) {
+ // have a match - so go to the end of the matching pairs...
+ while (cPtr && match) {
+ if (cPtr->FileServ->InDomain && cPtr->FileServ->Domain) {
+ if (lstrcmpi(DestServer->Domain->Name, cPtr->FileServ->Domain->Name))
+ match = FALSE;
+ } else
+ match = FALSE;
+
+ if (match)
+ cPtr = cPtr->next;
+ }
+
+ match = !match;
+ }
+ }
+ }
+
+ // if match then in middle of buffer, else use end
+ if (match && (cPtr != ConvertListEnd)) {
+ tmpPtr->prev = cPtr->prev;
+ tmpPtr->next = cPtr;
+ cPtr->prev->next = tmpPtr;
+ } else {
+ // only one in list so add at end
+ ConvertListEnd->next = tmpPtr;
+ tmpPtr->prev = ConvertListEnd;
+ ConvertListEnd = tmpPtr;
+ }
+
+ }
+
+ NumServerPairs++;
+ }
+
+ return (tmpPtr);
+
+} // ConvertListAdd
+
+
+/*+-------------------------------------------------------------------------+
+ | ConvertListDelete()
+ |
+ | Removes and deallocates a convert list entry from the conversion
+ | list.
+ |
+ +-------------------------------------------------------------------------+*/
+void ConvertListDelete(CONVERT_LIST *tmpPtr) {
+ CONVERT_LIST *PrevPtr;
+ CONVERT_LIST *NextPtr;
+
+ // First get rid of the source and destination server this is pointing to -
+ // those routines take care of freeing up chained data structures. Also
+ // DServList will only be delete if it's UseCount goes to 0.
+ SServListDelete(tmpPtr->SourceServ);
+ DServListDelete(tmpPtr->FileServ);
+
+ // Now unlink the actual convert list record
+ PrevPtr = tmpPtr->prev;
+ NextPtr = tmpPtr->next;
+
+ if (PrevPtr)
+ PrevPtr->next = NextPtr;
+
+ if (NextPtr)
+ NextPtr->prev = PrevPtr;
+
+ // Check if at end of list
+ if (ConvertListEnd == tmpPtr)
+ ConvertListEnd = PrevPtr;
+
+ // Check if at start of list
+ if (ConvertListStart == tmpPtr)
+ ConvertListStart = NextPtr;
+
+ FreeMemory(tmpPtr->ConvertOptions);
+ FreeMemory(tmpPtr);
+ NumServerPairs--;
+
+ if (!NumServerPairs)
+ ConvertListStart = ConvertListEnd = NULL;
+
+} // ConvertListDelete
+
+
+/*+-------------------------------------------------------------------------+
+ | ConvertListDeleteAll()
+ |
+ | Removes and deallocates all the convert list entrys and removes them
+ | from the listbox.
+ |
+ +-------------------------------------------------------------------------+*/
+void ConvertListDeleteAll() {
+ CONVERT_LIST *ConvList;
+ CONVERT_LIST *ConvListNext;
+
+ // Now remove the entries from the internal list
+ ConvList = ConvertListStart;
+
+ while (ConvList) {
+ ConvListNext = ConvList->next;
+ ConvertListDelete(ConvList);
+ ConvList = ConvListNext;
+ }
+
+} // ConvertListDeleteAll
+
+
+/*+-------------------------------------------------------------------------+
+ | ConvertListSaveAll()
+ |
+ +-------------------------------------------------------------------------+*/
+void ConvertListSaveAll(HANDLE hFile) {
+ DWORD wrote;
+ SOURCE_SERVER_BUFFER *ss;
+ DEST_SERVER_BUFFER *ds;
+
+ // First need to walk source and dest server lists and re-synch the
+ // index numbers as we can't use pointers loading them back in.
+ SServListIndex();
+ DServListIndex(); // Takes care of the VShares
+ DomainListIndex();
+
+ // Done with re-synch of indexes - do actual saving.
+ CurrentConvertList = ConvertListStart;
+ while (CurrentConvertList) {
+ // Need to de-reference the server pointers to their index, and after
+ // saving restore them.
+ ss = CurrentConvertList->SourceServ;
+ ds = CurrentConvertList->FileServ;
+ CurrentConvertList->SourceServ = (SOURCE_SERVER_BUFFER *) CurrentConvertList->SourceServ->Index;
+ CurrentConvertList->FileServ = (DEST_SERVER_BUFFER *) CurrentConvertList->FileServ->Index;
+
+ WriteFile(hFile, CurrentConvertList, sizeof(CONVERT_LIST), &wrote, NULL);
+
+ CurrentConvertList->SourceServ = ss;
+ CurrentConvertList->FileServ = ds;
+
+ // Now the options
+ UserOptionsSave(hFile, CurrentConvertList->ConvertOptions);
+ FileOptionsSave(hFile, CurrentConvertList->FileOptions);
+
+ CurrentConvertList = CurrentConvertList->next;
+ }
+
+ // Saved out the convert list - now need to save out the linked
+ // information (these routines save their linked info as well).
+ DomainListSave(hFile);
+ DServListSave(hFile);
+ SServListSave(hFile);
+
+} // ConvertListSaveAll
+
+
+/*+-------------------------------------------------------------------------+
+ | ConvertListLoadAll()
+ |
+ +-------------------------------------------------------------------------+*/
+void ConvertListLoadAll(HANDLE hFile) {
+ CONVERT_LIST *tmpPtr;
+ BOOL Continue = TRUE;
+ DWORD wrote;
+
+ while (Continue) {
+ tmpPtr = AllocMemory(sizeof(CONVERT_LIST));
+ if (tmpPtr == NULL)
+ return;
+
+ // Read in the record
+ ReadFile(hFile, tmpPtr, sizeof(CONVERT_LIST), &wrote, NULL);
+
+ // See if there are more records
+ if (tmpPtr->next == NULL)
+ Continue = FALSE;
+
+ // clear out the old links - and fixup new ones
+ tmpPtr->next = NULL;
+ tmpPtr->prev = NULL;
+ tmpPtr->ConvertOptions = NULL;
+ tmpPtr->FileOptions = NULL;
+
+ // Read in options - note the trusted domain must be fixed up later
+ // after the domain list is read in
+ UserOptionsLoad(hFile, &tmpPtr->ConvertOptions);
+ FileOptionsLoad(hFile, &tmpPtr->FileOptions);
+
+ // link it into the list
+ if (!ConvertListStart)
+ ConvertListStart = ConvertListEnd = tmpPtr;
+ else {
+ ConvertListEnd->next = tmpPtr;
+ tmpPtr->prev = ConvertListEnd;
+ ConvertListEnd = tmpPtr;
+ }
+
+ NumServerPairs++;
+ }
+
+ // Loaded the convert list - now need to bring in the linked
+ // information (these routines load their linked info as well).
+ DomainListLoad(hFile);
+ DServListLoad(hFile);
+ SServListLoad(hFile);
+
+} // ConvertListLoadAll
+
+
+/*+-------------------------------------------------------------------------+
+ | ConvertListFixup()
+ |
+ +-------------------------------------------------------------------------+*/
+void ConvertListFixup(HWND hWnd) {
+ BOOL ok = TRUE;
+ DWORD *SMap = NULL;
+ DWORD *DMap = NULL;
+ CONVERT_LIST *NextList;
+ CONVERT_OPTIONS *cvto;
+
+ // Generate Source and Destination server Index -> pointer maps
+ DServListIndexMapGet(&DMap);
+ SServListIndexMapGet(&SMap);
+
+ if ((DMap == NULL) || (SMap == NULL))
+ return;
+
+ CurrentConvertList = ConvertListStart;
+ while (CurrentConvertList) {
+ CurrentConvertList->FileServ = (DEST_SERVER_BUFFER *) DMap[(DWORD) CurrentConvertList->FileServ];
+ CurrentConvertList->SourceServ = (SOURCE_SERVER_BUFFER *) SMap[(DWORD) CurrentConvertList->SourceServ];
+
+ CurrentConvertList = CurrentConvertList->next;
+ }
+
+ // Free up the index maps
+ FreeMemory(DMap);
+ FreeMemory(SMap);
+
+ // Fixed the convert list - now need to fix the linked information
+ // (these routines fix-up their linked info as well).
+ DServListFixup();
+ SServListFixup();
+
+ // For the user options later on
+ DomainListIndexMapGet(&DMap);
+
+ // Now validate each of the machines
+ CurrentConvertList = ConvertListStart;
+ while (CurrentConvertList) {
+ ok = TRUE;
+ if (!NTServerValidate(hWnd, CurrentConvertList->FileServ->Name))
+ ok = FALSE;
+
+ if (ok)
+ if (!NWServerValidate(hWnd, CurrentConvertList->SourceServ->Name, FALSE))
+ ok = FALSE;
+
+ NextList = CurrentConvertList->next;
+
+ if (!ok) {
+ ConvertListDelete(CurrentConvertList);
+ } else {
+ // The connections were okay to these so resynch their information
+ NWServerInfoReset(CurrentConvertList->SourceServ);
+ NTServerInfoReset(hWnd, CurrentConvertList->FileServ, TRUE);
+ DestShareListFixup(CurrentConvertList->FileServ);
+ VShareListFixup(CurrentConvertList->FileServ);
+ SourceShareListFixup(CurrentConvertList->FileServ, CurrentConvertList->SourceServ->ShareList);
+
+ // need to fixup the trusted domain in the user options to the new
+ // domain list...
+ cvto = (CONVERT_OPTIONS *) CurrentConvertList->ConvertOptions;
+
+ if (cvto->UseTrustedDomain)
+ cvto->TrustedDomain = (DOMAIN_BUFFER *) DMap[(DWORD) cvto->TrustedDomain];
+ else
+ cvto->TrustedDomain = NULL;
+
+ }
+
+ CurrentConvertList = NextList;
+ }
+
+ // Free up the domain index map
+ FreeMemory(DMap);
+
+} // ConvertListFixup
+
+
+/*+-------------------------------------------------------------------------+
+ | ConvertListLog()
+ |
+ +-------------------------------------------------------------------------+*/
+void ConvertListLog() {
+ DEST_SERVER_BUFFER *DServ = NULL;
+ SOURCE_SERVER_BUFFER *SServ = NULL;
+ ULONG i;
+
+ CurrentConvertList = ConvertListStart;
+
+ // Log format is:
+ //
+ // Number of Servers = 1
+ //
+ // [Servers]
+ // From: Source1 To: Dest1
+ // From: Source2 To: Dest2
+ LogWriteLog(0, Lids(IDS_L_126), NumServerPairs);
+ LogWriteLog(0, Lids(IDS_CRLF));
+ LogWriteLog(0, Lids(IDS_L_127));
+
+ LogWriteSummary(0, Lids(IDS_L_126), NumServerPairs);
+ LogWriteSummary(0, Lids(IDS_CRLF));
+ LogWriteSummary(0, Lids(IDS_L_127));
+
+ // Loop through the conversion pairs (they are already in order)
+ while (CurrentConvertList) {
+ LogWriteLog(1, Lids(IDS_L_128), CurrentConvertList->SourceServ->Name, CurrentConvertList->FileServ->Name);
+ LogWriteSummary(1, Lids(IDS_L_128), CurrentConvertList->SourceServ->Name, CurrentConvertList->FileServ->Name);
+ CurrentConvertList = CurrentConvertList->next;
+ }
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+ LogWriteSummary(0, Lids(IDS_CRLF));
+
+ // Now write detailed info on each server
+ CurrentConvertList = ConvertListStart;
+
+ // header for this section
+ LogWriteLog(0, Lids(IDS_LINE));
+ LogWriteLog(0, Lids(IDS_BRACE), Lids(IDS_L_129));
+ LogWriteLog(0, Lids(IDS_LINE));
+ LogWriteLog(0, Lids(IDS_CRLF));
+
+ while (CurrentConvertList) {
+ // Make sure to only log each source server once
+ if (DServ != CurrentConvertList->FileServ) {
+ // remember this new server
+ DServ = CurrentConvertList->FileServ;
+
+ LogWriteLog(0, TEXT("[%s]\r\n"), CurrentConvertList->FileServ->Name);
+ LogWriteLog(1, Lids(IDS_L_130));
+ LogWriteLog(1, Lids(IDS_L_131), DServ->VerMaj, DServ->VerMin);
+
+ if (DServ->DriveList) {
+ LogWriteLog(0, Lids(IDS_CRLF));
+ LogWriteLog(1, Lids(IDS_L_132));
+
+ for (i = 0; i < DServ->DriveList->Count; i++) {
+ LogWriteLog(2, TEXT("%s: [%4s] %s\r\n"), DServ->DriveList->DList[i].Drive, DServ->DriveList->DList[i].DriveType, DServ->DriveList->DList[i].Name);
+ LogWriteLog(3, Lids(IDS_L_133), lToStr(DServ->DriveList->DList[i].FreeSpace));
+ }
+ }
+
+ // Log Shares
+ ShareListLog(DServ->ShareList, TRUE);
+ }
+
+ // Now for the source server
+ SServ = CurrentConvertList->SourceServ;
+ LogWriteLog(0, TEXT("[%s]\r\n"), CurrentConvertList->SourceServ->Name);
+ LogWriteLog(1, Lids(IDS_L_134));
+ LogWriteLog(1, Lids(IDS_L_135), (UINT) SServ->VerMaj, (UINT) SServ->VerMin);
+
+ ShareListLog(SServ->ShareList, FALSE);
+
+ CurrentConvertList = CurrentConvertList->next;
+ }
+
+} // ConvertListLog
+
+
+/*+-------------------------------------------------------------------------+
+ | UserServerNameGet()
+ |
+ +-------------------------------------------------------------------------+*/
+LPTSTR UserServerNameGet(DEST_SERVER_BUFFER *DServ, void *COpt) {
+ CONVERT_OPTIONS *ConvOpt;
+ LPTSTR ServName = NULL;
+
+ ConvOpt = (CONVERT_OPTIONS *) COpt;
+ // If going to a trusted domain then point to it's PDC for user transfers
+ if (ConvOpt->UseTrustedDomain && (ConvOpt->TrustedDomain != NULL) && (ConvOpt->TrustedDomain->PDCName != NULL)) {
+ ServName = ConvOpt->TrustedDomain->PDCName;
+ } else
+ // If in a domain then point to the PDC for user transfers
+ if (DServ->InDomain && DServ->Domain) {
+ ServName = DServ->Domain->PDCName;
+ } else {
+ ServName = DServ->Name;
+ }
+
+ return ServName;
+} // UserServerNameGet
diff --git a/private/nw/convert/nwconv/servlist.h b/private/nw/convert/nwconv/servlist.h
new file mode 100644
index 000000000..ec4075e91
--- /dev/null
+++ b/private/nw/convert/nwconv/servlist.h
@@ -0,0 +1,311 @@
+/*+-------------------------------------------------------------------------+
+ | Copyright 1993-1994 (C) Microsoft Corporation - All rights reserved. |
+ +-------------------------------------------------------------------------+*/
+
+#ifndef _HSERVLIST_
+#define _HSERVLIST_
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+/*+-------------------------------------------------------------------------+
+ | User List |
+ +-------------------------------------------------------------------------+*/
+typedef struct _USER_BUFFER {
+ TCHAR Name[MAX_USER_NAME_LEN + 1];
+ TCHAR NewName[MAX_USER_NAME_LEN + MAX_UCONST_LEN];
+ TCHAR Password[MAX_PW_LEN + 1];
+
+ USHORT err;
+ BOOL Overwrite;
+ BOOL IsNewName;
+} USER_BUFFER;
+
+typedef struct _USER_LIST {
+ ULONG Count;
+ USER_BUFFER UserBuffer[];
+} USER_LIST;
+
+
+/*+-------------------------------------------------------------------------+
+ | Group List |
+ +-------------------------------------------------------------------------+*/
+typedef struct _GROUP_BUFFER {
+ TCHAR Name[MAX_GROUP_NAME_LEN + 1];
+ TCHAR NewName[MAX_GROUP_NAME_LEN + MAX_UCONST_LEN];
+
+ USHORT err;
+ BOOL IsNewName;
+} GROUP_BUFFER;
+
+typedef struct _GROUP_LIST {
+ ULONG Count;
+ GROUP_BUFFER GroupBuffer[];
+} GROUP_LIST;
+
+
+/*+-------------------------------------------------------------------------+
+ | Drive Lists |
+ +-------------------------------------------------------------------------+*/
+
+// Drive type of 1 is okay to set perms on, all others are not...
+#define DRIVE_TYPE_NTFS 1
+
+// Drive list is only kept for destination servers and is mainly used to track
+// remaining free space on the drive.
+typedef struct _DRIVE_BUFFER {
+ UINT Type;
+ TCHAR Drive[2];
+ TCHAR DriveType[20];
+ TCHAR Name[20];
+ ULONG TotalSpace;
+ ULONG FreeSpace; // Free space before stuff we are transfering
+ ULONG AllocSpace; // Approx size of stuff we are transferring
+} DRIVE_BUFFER;
+
+typedef struct _DRIVE_LIST {
+ ULONG Count;
+ DRIVE_BUFFER DList[];
+} DRIVE_LIST;
+
+
+// Stores a server share and a link to the structures describing the files
+// to convert
+typedef struct _SHARE_BUFFER {
+ BOOL VFlag; // to distinguish between virtual and normal share
+
+ USHORT Index;
+
+ TCHAR Name[MAX_SHARE_NAME_LEN + 1];
+ BOOL Convert;
+ BOOL HiddenFiles;
+ BOOL SystemFiles;
+ BOOL ToFat; // Flag if user says OK to go to a FAT drive
+ DIR_BUFFER *Root;
+ DRIVE_BUFFER *Drive; // Used only for dest shares - for free space calcs.
+ ULONG Size; // Used for source shares - approx allocated space
+ TCHAR Path[MAX_PATH + 1];
+ TCHAR SubDir[MAX_PATH + 1]; // for source - subdir to copy to
+
+ // Following only used for source shares - describes destination share.
+ // Virtual tells whether data pointed to is a SHARE_BUFFER or
+ // VIRTUAL_SHARE_BUFFER. (Note: Use struct SHARE_BUFFER even though
+ // it may be VIRTUAL to simplify logic if it is a SHARE_BUFFER).
+ BOOL Virtual;
+ struct _SHARE_BUFFER *DestShare;
+} SHARE_BUFFER;
+
+// List of all server shares and files under them to convert.
+typedef struct _SHARE_LIST {
+ ULONG Count;
+ ULONG ConvertCount;
+ BOOL Fixup;
+ SHARE_BUFFER SList[];
+} SHARE_LIST;
+
+
+// Virtual shares are ones that we need to create - these are only for
+// destination shares as all source shares must obviously already exist.
+// This must be a linked list as it can be dynamically added to / deleted from.
+typedef struct _VIRTUAL_SHARE_BUFFER {
+ BOOL VFlag;
+
+ struct _VIRTUAL_SHARE_BUFFER *next;
+ struct _VIRTUAL_SHARE_BUFFER *prev;
+
+ USHORT Index;
+
+ LPTSTR Name;
+ LONG UseCount; // How many things going to it (so can del if unused)
+ TCHAR Path[MAX_PATH + 1];
+ DRIVE_BUFFER *Drive;
+} VIRTUAL_SHARE_BUFFER;
+
+
+// Keep the domain buffers for when servers are part of a domain to point
+// to the PDC and also when you choose a trusted domain. It contains an
+// abreviated form of a server buffer for the PDC info.
+typedef struct _DOMAIN_BUFFER {
+ struct _DOMAIN_BUFFER *next;
+ struct _DOMAIN_BUFFER *prev;
+
+ USHORT Index;
+
+ LPTSTR Name;
+
+ // PDC Info
+ LPTSTR PDCName;
+ DWORD Type;
+ DWORD VerMaj;
+ DWORD VerMin;
+ LONG UseCount;
+} DOMAIN_BUFFER;
+
+
+#define SERV_TYPE_NETWARE 1
+#define SERV_TYPE_NT 2
+/*+-------------------------------------------------------------------------+
+ | Server Lists
+ |
+ | The source and destination SERVER_BUFFER lists are kept independantly
+ | of the conversion list as multiple conversions can be done to one server.
+ | This allows easier tracking of virtual shares that need to be created and
+ | total free space on the server after all of the conversions are counted in
+ | (to see if there is enough free space to transfer files).
+ |
+ | Source server has no use count as a source server can only be included once
+ | in a conversion. Also no drive list as we can't get the info (on NetWare
+ | at least) and it isn't needed.
+ |
+ +-------------------------------------------------------------------------+*/
+typedef struct _SOURCE_SERVER_BUFFER {
+ struct _SOURCE_SERVER_BUFFER *next;
+ struct _SOURCE_SERVER_BUFFER *prev;
+
+ USHORT Index;
+
+ UINT Type; // Type of server (NetWare only for now)
+ BYTE VerMaj;
+ BYTE VerMin;
+ LPTSTR LName;
+ LPTSTR Name;
+ SHARE_LIST *ShareList;
+} SOURCE_SERVER_BUFFER;
+
+
+// Destination server must contain useage count as several source servers can
+// be merged into it. If UseCount goes to 0, then we can purge this record
+// out as it is no longer needed.
+typedef struct _DEST_SERVER_BUFFER {
+ struct _DEST_SERVER_BUFFER *next;
+ struct _DEST_SERVER_BUFFER *prev;
+
+ USHORT Index;
+
+ DWORD Type;
+ DWORD VerMaj;
+ DWORD VerMin;
+ LPTSTR LName; // Name with double whack pre-pended
+ LPTSTR Name;
+ SHARE_LIST *ShareList;
+
+ ULONG NumVShares;
+ VIRTUAL_SHARE_BUFFER *VShareStart;
+ VIRTUAL_SHARE_BUFFER *VShareEnd;
+ ULONG UseCount;
+ BOOL IsNTAS;
+ BOOL IsFPNW;
+ BOOL InDomain;
+ DOMAIN_BUFFER *Domain;
+ DRIVE_LIST *DriveList;
+} DEST_SERVER_BUFFER;
+
+
+/*+-------------------------------------------------------------------------+
+ | Convert List
+ |
+ | This is the main data structure that everything links off of. The
+ | ConvertOptions is a void type to allow it to be based on the type
+ | of server being converted. This allows the program to be (relatively)
+ | easily modified to work with other source server types (like
+ | LAN Server).
+ |
+ | The Server list constructs (SOURCE_SERVER_BUFFER and
+ | DEST_SERVER_BUFFER) are kept in addition to this, but this contains
+ | some links to those structures.
+ |
+ +-------------------------------------------------------------------------+*/
+typedef struct _CONVERT_LIST {
+ struct _CONVERT_LIST *next;
+ struct _CONVERT_LIST *prev;
+ SOURCE_SERVER_BUFFER *SourceServ;
+ DEST_SERVER_BUFFER *FileServ;
+
+ void *ConvertOptions;
+ void *FileOptions;
+} CONVERT_LIST;
+
+
+/*+-------------------------------------------------------------------------+
+ | Function Prototypes |
+ +-------------------------------------------------------------------------+*/
+void TreeSave(HANDLE hFile, DIR_BUFFER *Dir);
+void TreeLoad(HANDLE hFile, DIR_BUFFER **pDir);
+
+int __cdecl UserListCompare(const void *arg1, const void *arg2);
+
+void ShareListDelete(SHARE_LIST *ShareList);
+void DestShareListFixup(DEST_SERVER_BUFFER *DServ);
+void ShareListSave(HANDLE hFile, SHARE_LIST *ShareList);
+void ShareListLoad(HANDLE hFile, SHARE_LIST **lpShareList);
+
+SOURCE_SERVER_BUFFER *SServListAdd(LPTSTR ServerName);
+void SServListDelete(SOURCE_SERVER_BUFFER *tmpPtr);
+void SServListDeleteAll();
+SOURCE_SERVER_BUFFER *SServListFind(LPTSTR ServerName);
+void SServListIndex();
+void SServListIndexMapGet(DWORD **pMap);
+void SServListFixup();
+void SServListSave(HANDLE hFile);
+void SServListLoad(HANDLE hFile);
+
+DEST_SERVER_BUFFER *DServListAdd(LPTSTR ServerName);
+void DServListDelete(DEST_SERVER_BUFFER *tmpPtr);
+void DServListDeleteAll();
+DEST_SERVER_BUFFER *DServListFind(LPTSTR ServerName);
+void DServListIndex();
+void DServListIndexMapGet(DWORD **pMap);
+void DServListFixup();
+void DServListSave(HANDLE hFile);
+void DServListLoad(HANDLE hFile);
+void DServListSpaceFree();
+
+VIRTUAL_SHARE_BUFFER *VShareListAdd(DEST_SERVER_BUFFER *DServ, LPTSTR ShareName, LPTSTR Path);
+void VShareListDelete(DEST_SERVER_BUFFER *DServ, VIRTUAL_SHARE_BUFFER *tmpPtr);
+void VShareListDeleteAll(DEST_SERVER_BUFFER *DServ);
+void VShareListIndex(DEST_SERVER_BUFFER *DServ) ;
+void VShareListIndexMapGet(DEST_SERVER_BUFFER *DServ, DWORD **pMap);
+void VShareListSave(HANDLE hFile, DEST_SERVER_BUFFER *DServ);
+void VShareListLoad(HANDLE hFile, DEST_SERVER_BUFFER *DServ);
+
+DOMAIN_BUFFER *DomainListAdd(LPTSTR DomainName, LPTSTR PDCName);
+void DomainListDelete(DOMAIN_BUFFER *tmpPtr);
+void DomainListDeleteAll();
+DOMAIN_BUFFER *DomainListFind(LPTSTR DomainName);
+void DomainListIndex();
+void DomainListIndexMapGet(DWORD **pMap);
+void DomainListSave(HANDLE hFile);
+void DomainListLoad(HANDLE hFile);
+
+CONVERT_LIST *ConvertListAdd(SOURCE_SERVER_BUFFER *SourceServer, DEST_SERVER_BUFFER *DestServer);
+void ConvertListDelete(CONVERT_LIST *tmpPtr);
+void ConvertListDeleteAll();
+void ConvertListSaveAll(HANDLE hFile);
+void ConvertListLoadAll(HANDLE hFile);
+void ConvertListFixup(HWND hWnd);
+void ConvertListLog();
+
+LPTSTR UserServerNameGet(DEST_SERVER_BUFFER *DServ, void *ConvOpt);
+
+// Internal Data Structures
+extern UINT NumServerPairs;
+extern CONVERT_LIST *ConvertListStart;
+extern CONVERT_LIST *ConvertListEnd;
+extern CONVERT_LIST ConvertListDefault;
+extern CONVERT_LIST *CurrentConvertList;
+
+extern SOURCE_SERVER_BUFFER *SServListStart;
+extern SOURCE_SERVER_BUFFER *SServListEnd;
+extern SOURCE_SERVER_BUFFER *SServListCurrent;
+extern DEST_SERVER_BUFFER *DServListStart;
+extern DEST_SERVER_BUFFER *DServListEnd;
+extern DEST_SERVER_BUFFER *DServListCurrent;
+extern DOMAIN_BUFFER *DomainListStart;
+extern DOMAIN_BUFFER *DomainListEnd;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/nw/convert/nwconv/sizebarh.cur b/private/nw/convert/nwconv/sizebarh.cur
new file mode 100644
index 000000000..dda3483bb
--- /dev/null
+++ b/private/nw/convert/nwconv/sizebarh.cur
Binary files differ
diff --git a/private/nw/convert/nwconv/sources b/private/nw/convert/nwconv/sources
new file mode 100644
index 000000000..c53d61c57
--- /dev/null
+++ b/private/nw/convert/nwconv/sources
@@ -0,0 +1,56 @@
+TARGETNAME=nwconv
+TARGETPATH=obj
+TARGETTYPE=PROGRAM
+
+TARGETLIBS=\
+ $(BASEDIR)\public\sdk\lib\*\netapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\ntdll.lib \
+ $(BASEDIR)\public\sdk\lib\*\mpr.lib \
+ $(BASEDIR)\public\sdk\lib\*\shell32.lib \
+ $(BASEDIR)\public\sdk\lib\*\comdlg32.lib \
+ $(BASEDIR)\public\sdk\lib\*\nwapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\samlib.lib
+
+INCLUDES=\
+ $(BASEDIR)\private\nw\inc;\
+ $(BASEDIR)\private\inc;
+
+LINKER_FLAGS=/STACK:0x100000,0x10000
+C_DEFINES=-DWIN32 -DUNICODE -D_UNICODE -DWINVER=0x0400
+
+SUBSYSTEM_VERSION=4.00
+MSC_WARNING_LEVEL=/W3 /WX
+
+SOURCES=\
+ NWCONV.RC \
+ NWCONV.C \
+ ABOUTBOX.C \
+ DEBUG.C \
+ ERROR.C \
+ MEM.C \
+ UTILS.C \
+ NETUTIL.C \
+ TAB.C \
+ HIERFILE.C \
+ HIERDRAW.C \
+ COLUMNLB.C \
+ SERVLIST.C \
+ NTNETAPI.C \
+ NWNETAPI.C \
+ SBROWSE.C \
+ USERDLG.C \
+ FILEDLG.C \
+ FILESEL.C \
+ NWLOG.C \
+ STATBOX.C \
+ FCOPY.C \
+ FASTCOPY.C \
+ TRANSFER.C \
+ LOGHOURS.C \
+ MAP.C \
+ USRPROP.C \
+ ENCRYPT.C
+
+UMTYPE=windows
+UMENTRY=winmain
+
diff --git a/private/nw/convert/nwconv/statbox.c b/private/nw/convert/nwconv/statbox.c
new file mode 100644
index 000000000..70d2b42a8
--- /dev/null
+++ b/private/nw/convert/nwconv/statbox.c
@@ -0,0 +1,568 @@
+/*
+ +-------------------------------------------------------------------------+
+ | Status Dialog box routines - Used during Conversion |
+ +-------------------------------------------------------------------------+
+ | (c) Copyright 1993-1994 |
+ | Microsoft Corp. |
+ | All rights reserved |
+ | |
+ | Program : [StatBox.c] |
+ | Programmer : Arthur Hanson |
+ | Original Program Date : [Feb 08, 1994] |
+ | Last Update : [Feb 08, 1994] |
+ | |
+ | Version: 1.00 |
+ | |
+ | Description: |
+ | |
+ | History: |
+ | arth Feb 08, 1994 1.00 Original Version. |
+ | |
+ +-------------------------------------------------------------------------+
+*/
+
+
+#include "globals.h"
+#include "convapi.h"
+
+static TCHAR tmpStr[1024];
+static TCHAR PanelTitle[80];
+static HANDLE hStatus = NULL;
+static HANDLE hPanel = NULL;
+static BOOL DoCancel = FALSE;
+
+/*+-------------------------------------------------------------------------+
+ | Routines for status dialog put up during conversion. |
+ +-------------------------------------------------------------------------+*/
+
+/*+-------------------------------------------------------------------------+
+ | DrainPump()
+ +-------------------------------------------------------------------------+*/
+void DrainPump() {
+ MSG msg;
+
+ while (PeekMessage(&msg, hStatus, 0, 0xfff, PM_REMOVE)) {
+ if ((IsDialogMessage(hStatus, &msg) == FALSE) &&
+ (IsDialogMessage(hPanel, &msg) == FALSE)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ }
+
+} // DrainPump
+
+
+/*+-------------------------------------------------------------------------+
+ | Status_CurConv()
+ +-------------------------------------------------------------------------+*/
+// Current server pair being converted
+void Status_CurConv(UINT Num) {
+ wsprintf(tmpStr, TEXT("%5u"), Num);
+ SendDlgItemMessage(hStatus, IDC_S_CUR_CONV, WM_SETTEXT, 0, (LPARAM) tmpStr);
+ SendMessage(hStatus, WM_PAINT, 0, 0L);
+ DrainPump();
+} // Status_CurConv
+
+
+/*+-------------------------------------------------------------------------+
+ | Status_TotConv()
+ +-------------------------------------------------------------------------+*/
+// Total number of server pairs to convert
+void Status_TotConv(UINT Num) {
+ wsprintf(tmpStr, TEXT("%5u"), Num);
+ SendDlgItemMessage(hStatus, IDC_S_TOT_CONV, WM_SETTEXT, 0, (LPARAM) tmpStr);
+ SendMessage(hStatus, WM_PAINT, 0, 0L);
+ DrainPump();
+} // Status_TotConv
+
+
+/*+-------------------------------------------------------------------------+
+ | Status_SrcServ()
+ +-------------------------------------------------------------------------+*/
+// Current source server of server pair being converted
+void Status_SrcServ(LPTSTR Server) {
+ wsprintf(tmpStr, TEXT("%-15s"), Server);
+ SendDlgItemMessage(hStatus, IDC_S_SRCSERV, WM_SETTEXT, 0, (LPARAM) tmpStr);
+ SendMessage(hStatus, WM_PAINT, 0, 0L);
+ DrainPump();
+} // Status_SrcServ
+
+
+/*+-------------------------------------------------------------------------+
+ | Status_DestServ()
+ +-------------------------------------------------------------------------+*/
+// Current destination server of server pair being converted
+void Status_DestServ(LPTSTR Server) {
+ wsprintf(tmpStr, TEXT("%-15s"), Server);
+ SendDlgItemMessage(hStatus, IDC_S_DESTSERV, WM_SETTEXT, 0, (LPARAM) tmpStr);
+ SendMessage(hStatus, WM_PAINT, 0, 0L);
+ DrainPump();
+} // Status_DestServ
+
+
+/*+-------------------------------------------------------------------------+
+ | Status_ConvTxt()
+ +-------------------------------------------------------------------------+*/
+// Text describing what is being converted (Groups, Users Files)
+void Status_ConvTxt(LPTSTR Text) {
+ wsprintf(tmpStr, TEXT("%-20s"), Text);
+ SendDlgItemMessage(hStatus, IDC_S_CONVTXT, WM_SETTEXT, 0, (LPARAM) tmpStr);
+ SendMessage(hStatus, WM_PAINT, 0, 0L);
+ DrainPump();
+} // Status_ConvTxt
+
+
+/*+-------------------------------------------------------------------------+
+ | Status_CurNum()
+ +-------------------------------------------------------------------------+*/
+// Current item number being converted (current group # or User # or file #)...
+void Status_CurNum(UINT Num) {
+ wsprintf(tmpStr, TEXT("%7s"), lToStr(Num));
+ SendDlgItemMessage(hStatus, IDC_S_CUR_NUM, WM_SETTEXT, 0, (LPARAM) tmpStr);
+ SendMessage(hStatus, WM_PAINT, 0, 0L);
+ DrainPump();
+} // Status_CurNum
+
+
+/*+-------------------------------------------------------------------------+
+ | Status_CurTot()
+ +-------------------------------------------------------------------------+*/
+// Total items in set being converted (user, group, files...)
+void Status_CurTot(UINT Num) {
+ wsprintf(tmpStr, TEXT("%7s"), lToStr(Num));
+ SendDlgItemMessage(hStatus, IDC_S_CUR_TOT, WM_SETTEXT, 0, (LPARAM) tmpStr);
+ SendMessage(hStatus, WM_PAINT, 0, 0L);
+ DrainPump();
+} // Status_CurTot
+
+
+/*+-------------------------------------------------------------------------+
+ | Status_ItemLabel()
+ +-------------------------------------------------------------------------+*/
+// Label for set being converted ("Group:", "User:")
+void Status_ItemLabel(LPTSTR Text, ...) {
+ va_list marker;
+
+ va_start(marker, Text);
+
+ wvsprintf(tmpStr, Text, marker);
+ SendDlgItemMessage(hStatus, IDC_S_ITEMLABEL, WM_SETTEXT, 0, (LPARAM) tmpStr);
+ SendMessage(hStatus, WM_PAINT, 0, 0L);
+ DrainPump();
+
+ va_end(marker);
+
+} // Status_ItemLabel
+
+
+/*+-------------------------------------------------------------------------+
+ | Status_Item()
+ +-------------------------------------------------------------------------+*/
+// Name of current thing being converted (actual user or group name)
+void Status_Item(LPTSTR Text) {
+ wsprintf(tmpStr, TEXT("%-15s"), Text);
+ SendDlgItemMessage(hStatus, IDC_S_STATUSITEM, WM_SETTEXT, 0, (LPARAM) tmpStr);
+ SendMessage(hStatus, WM_PAINT, 0, 0L);
+ DrainPump();
+} // Status_Item
+
+
+/*+-------------------------------------------------------------------------+
+ | Status_TotComplete()
+ +-------------------------------------------------------------------------+*/
+// Total #server pairs converted so far
+void Status_TotComplete(UINT Num) {
+ wsprintf(tmpStr, TEXT("%7s"), lToStr(Num));
+ SendDlgItemMessage(hStatus, IDC_S_TOT_COMP, WM_SETTEXT, 0, (LPARAM) tmpStr);
+ SendMessage(hStatus, WM_PAINT, 0, 0L);
+ DrainPump();
+} // Status_TotComplete
+
+
+/*+-------------------------------------------------------------------------+
+ | Status_TotGroups()
+ +-------------------------------------------------------------------------+*/
+void Status_TotGroups(UINT Num) {
+ wsprintf(tmpStr, TEXT("%7s"), lToStr(Num));
+ SendDlgItemMessage(hStatus, IDC_S_TOT_GROUPS, WM_SETTEXT, 0, (LPARAM) tmpStr);
+ SendMessage(hStatus, WM_PAINT, 0, 0L);
+ DrainPump();
+} // Status_TotGroup
+
+
+/*+-------------------------------------------------------------------------+
+ | Status_TotUsers()
+ +-------------------------------------------------------------------------+*/
+void Status_TotUsers(UINT Num) {
+ wsprintf(tmpStr, TEXT("%7s"), lToStr(Num));
+ SendDlgItemMessage(hStatus, IDC_S_TOT_USERS, WM_SETTEXT, 0, (LPARAM) tmpStr);
+ SendMessage(hStatus, WM_PAINT, 0, 0L);
+ DrainPump();
+} // Status_TotUsers
+
+
+/*+-------------------------------------------------------------------------+
+ | Status_TotFiles()
+ +-------------------------------------------------------------------------+*/
+void Status_TotFiles(UINT Num) {
+ wsprintf(tmpStr, TEXT("%7s"), lToStr(Num));
+ SendDlgItemMessage(hStatus, IDC_S_TOT_FILES, WM_SETTEXT, 0, (LPARAM) tmpStr);
+ SendMessage(hStatus, WM_PAINT, 0, 0L);
+ DrainPump();
+} // Status_TotFiles
+
+
+/*+-------------------------------------------------------------------------+
+ | Status_TotErrors()
+ +-------------------------------------------------------------------------+*/
+void Status_TotErrors(UINT Num) {
+ wsprintf(tmpStr, TEXT("%7s"), lToStr(Num));
+ SendDlgItemMessage(hStatus, IDC_S_TOT_ERRORS, WM_SETTEXT, 0, (LPARAM) tmpStr);
+ SendMessage(hStatus, WM_PAINT, 0, 0L);
+ DrainPump();
+} // Status_TotErrors
+
+
+/*+-------------------------------------------------------------------------+
+ | Status_BytesTxt()
+ +-------------------------------------------------------------------------+*/
+void Status_BytesTxt(LPTSTR Text) {
+ wsprintf(tmpStr, TEXT("%-15s"), Text);
+ SendDlgItemMessage(hStatus, IDC_PANEL1, WM_SETTEXT, 0, (LPARAM) tmpStr);
+ SendMessage(hStatus, WM_PAINT, 0, 0L);
+ DrainPump();
+} // Status_BytesTxt
+
+
+/*+-------------------------------------------------------------------------+
+ | Status_Bytes()
+ +-------------------------------------------------------------------------+*/
+void Status_Bytes(LPTSTR Text) {
+ wsprintf(tmpStr, TEXT("%s"), Text);
+ SendDlgItemMessage(hStatus, IDC_PANEL2, WM_SETTEXT, 0, (LPARAM) tmpStr);
+ SendMessage(hStatus, WM_PAINT, 0, 0L);
+ DrainPump();
+} // Status_Bytes
+
+
+/*+-------------------------------------------------------------------------+
+ | Status_TotBytes()
+ +-------------------------------------------------------------------------+*/
+void Status_TotBytes(LPTSTR Text) {
+ wsprintf(tmpStr, TEXT("%s"), Text);
+ SendDlgItemMessage(hStatus, IDC_PANEL3, WM_SETTEXT, 0, (LPARAM) tmpStr);
+ SendMessage(hStatus, WM_PAINT, 0, 0L);
+ DrainPump();
+} // Status_TotBytes
+
+
+/*+-------------------------------------------------------------------------+
+ | Status_BytesSep()
+ +-------------------------------------------------------------------------+*/
+void Status_BytesSep(LPTSTR Text) {
+ wsprintf(tmpStr, TEXT("%s"), Text);
+ SendDlgItemMessage(hStatus, IDC_PANEL4, WM_SETTEXT, 0, (LPARAM) tmpStr);
+ SendMessage(hStatus, WM_PAINT, 0, 0L);
+ DrainPump();
+} // Status_BytesSep
+
+
+/*+-------------------------------------------------------------------------+
+ | DlgStatus()
+ +-------------------------------------------------------------------------+*/
+LRESULT CALLBACK DlgStatus(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
+ RECT rc;
+
+ switch (message) {
+ case WM_INITDIALOG:
+ // Center the dialog over the application window
+ CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
+ return (TRUE);
+
+ case WM_SETFOCUS:
+ GetWindowRect(hDlg, &rc);
+ InvalidateRect(hDlg, &rc, FALSE);
+ SendMessage(hStatus, WM_PAINT, 0, 0L);
+ break;
+ }
+
+ return (FALSE); // Didn't process the message
+
+ lParam;
+} // DlgStatus
+
+
+
+/*+-------------------------------------------------------------------------+
+ | DoStatusDlg()
+ +-------------------------------------------------------------------------+*/
+void DoStatusDlg(HWND hDlg) {
+ DLGPROC lpfnDlg;
+
+ lpfnDlg = MakeProcInstance((DLGPROC)DlgStatus, hInst);
+ hStatus = CreateDialog(hInst, TEXT("StatusDlg"), hDlg, lpfnDlg) ;
+ FreeProcInstance(lpfnDlg);
+
+} // DoStatusDlg
+
+
+/*+-------------------------------------------------------------------------+
+ | StatusDlgKill()
+ +-------------------------------------------------------------------------+*/
+void StatusDlgKill() {
+ DestroyWindow(hStatus);
+ hStatus = NULL;
+
+} // StatusDlgKill
+
+
+
+/*+-------------------------------------------------------------------------+
+ | Information (Panel) Dialog Routines |
+ +-------------------------------------------------------------------------+*/
+
+/*+-------------------------------------------------------------------------+
+ | Panel_Line()
+ +-------------------------------------------------------------------------+*/
+void Panel_Line(int Line, LPTSTR szFormat, ...) {
+ va_list marker;
+
+ if (hPanel == NULL)
+ return;
+
+ va_start(marker, szFormat);
+
+ wvsprintf(tmpStr, szFormat, marker);
+ tmpStr[60] = TEXT('\0');
+ SendDlgItemMessage(hPanel, IDC_PANEL1 - 1 + Line, WM_SETTEXT, 0, (LPARAM) tmpStr);
+ SendMessage(hPanel, WM_PAINT, 0, 0L);
+ DrainPump();
+
+ va_end(marker);
+
+} // Panel_ConvTxt
+
+
+/*+-------------------------------------------------------------------------+
+ | Panel_Cancel()
+ +-------------------------------------------------------------------------+*/
+BOOL Panel_Cancel() {
+
+ if ((hPanel == NULL) || !DoCancel)
+ return FALSE;
+
+ return TRUE;
+
+} // Panel_Cancel
+
+
+/*+-------------------------------------------------------------------------+
+ | DlgPanel()
+ +-------------------------------------------------------------------------+*/
+LRESULT CALLBACK DlgPanel(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
+ int wmId, wmEvent;
+ RECT rc;
+
+ switch (message) {
+ case WM_INITDIALOG:
+ // Center the dialog over the application window
+ DoCancel = FALSE;
+ CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
+ SendMessage(hDlg, WM_SETTEXT, (WPARAM) 0, (LPARAM) PanelTitle);
+ return (TRUE);
+
+ case WM_SETFOCUS:
+ GetWindowRect(hDlg, &rc);
+ InvalidateRect(hDlg, &rc, FALSE);
+ SendMessage(hPanel, WM_PAINT, 0, 0L);
+ break;
+
+ case WM_COMMAND:
+ wmId = LOWORD(wParam);
+ wmEvent = HIWORD(wParam);
+
+ switch (wmId) {
+ case IDCANCEL:
+ DoCancel = TRUE;
+ return (TRUE);
+
+ break;
+ }
+
+ break;
+ }
+
+ return (FALSE); // Didn't process the message
+
+ lParam;
+} // DlgPanel
+
+
+/*+-------------------------------------------------------------------------+
+ | PanelDlg_Do()
+ +-------------------------------------------------------------------------+*/
+void PanelDlg_Do(HWND hDlg, LPTSTR Title) {
+ DLGPROC lpfnDlg;
+
+ lstrcpy(PanelTitle, Title);
+ lpfnDlg = MakeProcInstance((DLGPROC)DlgPanel, hInst);
+ hPanel = CreateDialog(hInst, TEXT("PanelDLG"), hDlg, lpfnDlg) ;
+ FreeProcInstance(lpfnDlg);
+
+} // PanelDlg_Do
+
+
+/*+-------------------------------------------------------------------------+
+ | PanelDlgKill()
+ +-------------------------------------------------------------------------+*/
+void PanelDlgKill() {
+
+ if (hPanel == NULL)
+ return;
+
+ DestroyWindow(hPanel);
+ hPanel = NULL;
+ DoCancel = FALSE;
+
+} // PanelDlgKill
+
+
+/*+-------------------------------------------------------------------------+
+ | Name Error Dialog (User and Group) |
+ +-------------------------------------------------------------------------+*/
+
+static TCHAR OldName[60];
+static TCHAR NewName[60];
+static LPTSTR DlgTitle;
+static LPTSTR NameErrorProblem;
+static ULONG RetType;
+static ULONG MaxNameLen;
+/*+-------------------------------------------------------------------------+
+ | NameErrorDlg()
+ |
+ +-------------------------------------------------------------------------+*/
+LRESULT CALLBACK NameErrorDlg(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
+ int wmId, wmEvent;
+
+ switch (message) {
+ case WM_INITDIALOG:
+ // Center the dialog over the application window
+ CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
+
+ // Init all the display fields
+ SendMessage(hDlg, WM_SETTEXT, (WPARAM) 0, (LPARAM) DlgTitle);
+ SendMessage(GetDlgItem(hDlg, IDC_OLDNAME), WM_SETTEXT, (WPARAM) 0, (LPARAM) OldName);
+ SendMessage(GetDlgItem(hDlg, IDC_NEWNAME), WM_SETTEXT, (WPARAM) 0, (LPARAM) NewName);
+ SendMessage(GetDlgItem(hDlg, IDC_PANEL1), WM_SETTEXT, (WPARAM) 0, (LPARAM) NameErrorProblem);
+
+ // limit the name length
+ PostMessage(GetDlgItem(hDlg, IDC_NEWNAME), EM_LIMITTEXT, (WPARAM) MaxNameLen, 0);
+ return (TRUE);
+
+ case WM_COMMAND:
+ wmId = LOWORD(wParam);
+ wmEvent = HIWORD(wParam);
+
+ switch (wmId) {
+ case IDABORT:
+ // This will cancel the conversion - but need to check with the user
+ // if this is what they really want to do
+ if (MessageBox(hDlg, Lids(IDS_E_15), Lids(IDS_E_16), MB_YESNO) == IDYES) {
+ RetType = (ULONG) IDABORT;
+ EndDialog(hDlg, 0);
+ }
+
+ return (TRUE);
+ break;
+
+ case IDRETRY:
+ * (WORD *)NewName = (WORD) MaxNameLen + 1;
+ SendMessage(GetDlgItem(hDlg, IDC_NEWNAME), EM_GETLINE, 0, (LPARAM) NewName);
+ RetType = (ULONG) IDRETRY;
+ EndDialog(hDlg, 0);
+ return (TRUE);
+ break;
+
+ case IDIGNORE:
+ RetType = (ULONG) IDCANCEL;
+ EndDialog(hDlg, 0);
+ return (TRUE);
+ break;
+
+ }
+
+ break;
+ }
+
+ return (FALSE); // Didn't process the message
+
+ lParam;
+} // NameErrorDlg
+
+
+/*+-------------------------------------------------------------------------+
+ | UserNameErrorDlg_Do()
+ |
+ +-------------------------------------------------------------------------+*/
+ULONG UserNameErrorDlg_Do(LPTSTR Title, LPTSTR Problem, USER_BUFFER *User) {
+ DLGPROC lpfnDlg;
+
+ lstrcpy(OldName, User->Name);
+ lstrcpy(NewName, User->NewName);
+ MaxNameLen = MAX_USER_NAME_LEN;
+
+ DlgTitle = Title;
+ NameErrorProblem = Problem;
+ lpfnDlg = MakeProcInstance((DLGPROC) NameErrorDlg, hInst);
+ CursorNormal();
+ DialogBox(hInst, TEXT("NameError"), hStatus, lpfnDlg) ;
+ CursorHourGlass();
+ FreeProcInstance(lpfnDlg);
+
+ if (RetType == IDRETRY) {
+ User->err = 0;
+ lstrcpy(User->NewName, NewName);
+
+ // if the name is different then flag that it is a new name
+ if (lstrcmpi(User->NewName, User->Name))
+ User->IsNewName = TRUE;
+ }
+
+ return RetType;
+
+} // UserNameErrorDlg_Do
+
+
+/*+-------------------------------------------------------------------------+
+ | GroupNameErrorDlg_Do()
+ |
+ +-------------------------------------------------------------------------+*/
+ULONG GroupNameErrorDlg_Do(LPTSTR Title, LPTSTR Problem, GROUP_BUFFER *Group) {
+ DLGPROC lpfnDlg;
+
+ lstrcpy(OldName, Group->Name);
+ lstrcpy(NewName, Group->NewName);
+ MaxNameLen = MAX_NT_GROUP_NAME_LEN;
+
+ DlgTitle = Title;
+ NameErrorProblem = Problem;
+ lpfnDlg = MakeProcInstance((DLGPROC) NameErrorDlg, hInst);
+ CursorNormal();
+ DialogBox(hInst, TEXT("NameError"), hStatus, lpfnDlg) ;
+ CursorHourGlass();
+ FreeProcInstance(lpfnDlg);
+
+ if (RetType == IDRETRY) {
+ Group->err = 0;
+ lstrcpy(Group->NewName, NewName);
+
+ // if the name is different then flag that it is a new name
+ if (lstrcmpi(Group->NewName, Group->Name))
+ Group->IsNewName = TRUE;
+ }
+
+ return RetType;
+
+} // GroupNameErrorDlg_Do
+
+
diff --git a/private/nw/convert/nwconv/statbox.h b/private/nw/convert/nwconv/statbox.h
new file mode 100644
index 000000000..070f67ce6
--- /dev/null
+++ b/private/nw/convert/nwconv/statbox.h
@@ -0,0 +1,46 @@
+/*+-------------------------------------------------------------------------+
+ | Copyright 1993-1994 (C) Microsoft Corporation - All rights reserved. |
+ +-------------------------------------------------------------------------+*/
+
+#ifndef _HSTATBOX_
+#define _HSTATBOX_
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+void Status_CurConv(UINT Num);
+void Status_TotConv(UINT Num);
+void Status_SrcServ(LPTSTR Server);
+void Status_DestServ(LPTSTR Server);
+void Status_ConvTxt(LPTSTR Text);
+void Status_CurNum(UINT Num);
+void Status_CurTot(UINT Num);
+void Status_ItemLabel(LPTSTR Text, ...);
+void Status_Item(LPTSTR Text);
+void Status_TotComplete(UINT Num);
+void Status_TotGroups(UINT Num);
+void Status_TotUsers(UINT Num);
+void Status_TotFiles(UINT Num);
+void Status_TotErrors(UINT Num);
+void Status_BytesTxt(LPTSTR Text);
+void Status_Bytes(LPTSTR Text);
+void Status_TotBytes(LPTSTR Text);
+void Status_BytesSep(LPTSTR Text);
+
+void DoStatusDlg(HWND hDlg);
+void StatusDlgKill();
+
+void Panel_Line(int Line, LPTSTR szFormat, ...);
+void PanelDlg_Do(HWND hDlg, LPTSTR Title);
+BOOL Panel_Cancel();
+void PanelDlgKill();
+
+ULONG UserNameErrorDlg_Do(LPTSTR Title, LPTSTR Problem, USER_BUFFER *User);
+ULONG GroupNameErrorDlg_Do(LPTSTR Title, LPTSTR Problem, GROUP_BUFFER *Group);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/nw/convert/nwconv/strings.h b/private/nw/convert/nwconv/strings.h
new file mode 100644
index 000000000..212d31f08
--- /dev/null
+++ b/private/nw/convert/nwconv/strings.h
@@ -0,0 +1,478 @@
+/*+-------------------------------------------------------------------------+
+ | Copyright 1993-1994 (C) Microsoft Corporation - All rights reserved. |
+ +-------------------------------------------------------------------------+*/
+
+#ifndef _STRINGS_
+#define _STRINGS_
+
+#define MAX_STRING_SIZE 256
+
+#define IDS_STRINGBASE (1500)
+
+#define IDS_APPNAME (IDS_STRINGBASE+1)
+
+#define IDS_DISK_SPACE_UNAVAIL (IDS_STRINGBASE+2)
+#define IDS_DISK_SPACE (IDS_STRINGBASE+3)
+#define IDS_MATH_COPR_NOTPRESENT (IDS_STRINGBASE+4)
+#define IDS_MATH_COPR_PRESENT (IDS_STRINGBASE+5)
+#define IDS_PHYSICAL_MEM (IDS_STRINGBASE+6)
+
+#define IDS_USERFILTERSTRING (IDS_STRINGBASE+7)
+#define IDS_MAINFILTERSTRING (IDS_STRINGBASE+8)
+
+#define IDS_TXTWARNING (IDS_STRINGBASE+9)
+#define IDS_RESTOREDEFAULTS (IDS_STRINGBASE+10)
+
+#define IDS_MAPNEWNAME (IDS_STRINGBASE+11)
+#define IDS_MAPNEWPASSWORD (IDS_STRINGBASE+12)
+#define IDS_MAPGROUPS (IDS_STRINGBASE+13)
+
+#define IDS_NOREADMAP (IDS_STRINGBASE+14)
+#define IDS_MAPCREATED (IDS_STRINGBASE+15)
+#define IDS_MAPCREATEFAIL (IDS_STRINGBASE+16)
+
+#define IDS_CRLF (IDS_STRINGBASE+17)
+
+#define IDS_SUN (IDS_STRINGBASE+18)
+#define IDS_MON (IDS_STRINGBASE+19)
+#define IDS_TUE (IDS_STRINGBASE+20)
+#define IDS_WED (IDS_STRINGBASE+21)
+#define IDS_THU (IDS_STRINGBASE+22)
+#define IDS_FRI (IDS_STRINGBASE+23)
+#define IDS_SAT (IDS_STRINGBASE+24)
+
+#define IDS_SUNDAY (IDS_STRINGBASE+25)
+#define IDS_MONDAY (IDS_STRINGBASE+26)
+#define IDS_TUESDAY (IDS_STRINGBASE+27)
+#define IDS_WEDNESDAY (IDS_STRINGBASE+28)
+#define IDS_THURSDAY (IDS_STRINGBASE+29)
+#define IDS_FRIDAY (IDS_STRINGBASE+30)
+#define IDS_SATURDAY (IDS_STRINGBASE+31)
+
+#define IDS_NWCANT_CON (IDS_STRINGBASE+32)
+#define IDS_NWNO_ADMIN (IDS_STRINGBASE+33)
+
+#define IDS_LINE (IDS_STRINGBASE+34)
+#define IDS_BRACE (IDS_STRINGBASE+35)
+#define IDS_YES (IDS_STRINGBASE+36)
+#define IDS_NO (IDS_STRINGBASE+37)
+
+#define IDS_E_NWLOG (IDS_STRINGBASE+38)
+#define IDS_NOEDITMAP (IDS_STRINGBASE+39)
+
+#define IDS_LOCKED_OUT (IDS_STRINGBASE+40)
+
+/////////////////
+#define IDS_D_STRINGBASE (IDS_STRINGBASE+40)
+
+#define IDS_D_1 (IDS_D_STRINGBASE+1)
+#define IDS_D_2 (IDS_D_STRINGBASE+2)
+#define IDS_D_3 (IDS_D_STRINGBASE+3)
+#define IDS_D_4 (IDS_D_STRINGBASE+4)
+#define IDS_D_5 (IDS_D_STRINGBASE+5)
+#define IDS_D_6 (IDS_D_STRINGBASE+6)
+#define IDS_D_7 (IDS_D_STRINGBASE+7)
+#define IDS_D_8 (IDS_D_STRINGBASE+8)
+#define IDS_D_9 (IDS_D_STRINGBASE+9)
+#define IDS_D_10 (IDS_D_STRINGBASE+10)
+
+#define IDS_D_11 (IDS_D_STRINGBASE+11)
+#define IDS_D_12 (IDS_D_STRINGBASE+12)
+
+#define IDS_D_13 (IDS_D_STRINGBASE+13)
+#define IDS_D_14 (IDS_D_STRINGBASE+14)
+
+#define IDS_D_15 (IDS_D_STRINGBASE+15)
+#define IDS_D_16 (IDS_D_STRINGBASE+16)
+
+#define IDS_D_17 (IDS_D_STRINGBASE+17)
+
+#define IDS_D_18 (IDS_D_STRINGBASE+18)
+#define IDS_D_19 (IDS_D_STRINGBASE+19)
+
+#define IDS_D_20 (IDS_D_STRINGBASE+20)
+#define IDS_D_21 (IDS_D_STRINGBASE+21)
+
+#define IDS_D_22 (IDS_D_STRINGBASE+22)
+
+
+////////////
+#define IDS_S_STRINGBASE (IDS_D_STRINGBASE+22)
+
+#define IDS_S_1 (IDS_S_STRINGBASE+1)
+#define IDS_S_2 (IDS_S_STRINGBASE+2)
+#define IDS_S_3 (IDS_S_STRINGBASE+3)
+#define IDS_S_4 (IDS_S_STRINGBASE+4)
+#define IDS_S_5 (IDS_S_STRINGBASE+5)
+#define IDS_S_6 (IDS_S_STRINGBASE+6)
+
+#define IDS_S_7 (IDS_S_STRINGBASE+7)
+#define IDS_S_8 (IDS_S_STRINGBASE+8)
+
+#define IDS_S_9 (IDS_S_STRINGBASE+9)
+#define IDS_S_10 (IDS_S_STRINGBASE+10)
+#define IDS_S_11 (IDS_S_STRINGBASE+11)
+
+#define IDS_S_12 (IDS_S_STRINGBASE+12)
+#define IDS_S_13 (IDS_S_STRINGBASE+13)
+#define IDS_S_14 (IDS_S_STRINGBASE+14)
+
+#define IDS_S_15 (IDS_S_STRINGBASE+15)
+#define IDS_S_16 (IDS_S_STRINGBASE+16)
+#define IDS_S_17 (IDS_S_STRINGBASE+17)
+#define IDS_S_18 (IDS_S_STRINGBASE+18)
+#define IDS_S_19 (IDS_S_STRINGBASE+19)
+
+#define IDS_S_20 (IDS_S_STRINGBASE+20)
+
+#define IDS_S_21 (IDS_S_STRINGBASE+21)
+#define IDS_S_22 (IDS_S_STRINGBASE+22)
+#define IDS_S_23 (IDS_S_STRINGBASE+23)
+#define IDS_S_24 (IDS_S_STRINGBASE+24)
+#define IDS_S_25 (IDS_S_STRINGBASE+25)
+#define IDS_S_26 (IDS_S_STRINGBASE+26)
+
+#define IDS_S_27 (IDS_S_STRINGBASE+27)
+
+#define IDS_S_28 (IDS_S_STRINGBASE+28)
+#define IDS_S_29 (IDS_S_STRINGBASE+29)
+#define IDS_S_30 (IDS_S_STRINGBASE+30)
+#define IDS_S_31 (IDS_S_STRINGBASE+31)
+#define IDS_S_32 (IDS_S_STRINGBASE+32)
+#define IDS_S_33 (IDS_S_STRINGBASE+33)
+
+#define IDS_S_34 (IDS_S_STRINGBASE+34)
+
+#define IDS_S_35 (IDS_S_STRINGBASE+35)
+
+#define IDS_S_36 (IDS_S_STRINGBASE+36)
+#define IDS_S_37 (IDS_S_STRINGBASE+37)
+
+#define IDS_S_38 (IDS_S_STRINGBASE+38)
+#define IDS_S_39 (IDS_S_STRINGBASE+39)
+#define IDS_S_40 (IDS_S_STRINGBASE+40)
+#define IDS_S_41 (IDS_S_STRINGBASE+41)
+
+#define IDS_S_42 (IDS_S_STRINGBASE+42)
+#define IDS_S_43 (IDS_S_STRINGBASE+43)
+
+#define IDS_S_44 (IDS_S_STRINGBASE+44)
+#define IDS_S_45 (IDS_S_STRINGBASE+45)
+#define IDS_S_46 (IDS_S_STRINGBASE+46)
+#define IDS_S_47 (IDS_S_STRINGBASE+47)
+#define IDS_S_48 (IDS_S_STRINGBASE+48)
+
+#define IDS_S_49 (IDS_S_STRINGBASE+49)
+
+////////////
+#define IDS_E_STRINGBASE (IDS_S_STRINGBASE+49)
+
+#define IDS_E_1 (IDS_E_STRINGBASE+1)
+#define IDS_E_2 (IDS_E_STRINGBASE+2)
+#define IDS_E_3 (IDS_E_STRINGBASE+3)
+#define IDS_E_4 (IDS_E_STRINGBASE+4)
+#define IDS_E_5 (IDS_E_STRINGBASE+5)
+#define IDS_E_6 (IDS_E_STRINGBASE+6)
+#define IDS_E_7 (IDS_E_STRINGBASE+7)
+#define IDS_E_8 (IDS_E_STRINGBASE+8)
+#define IDS_E_9 (IDS_E_STRINGBASE+9)
+
+#define IDS_E_10 (IDS_E_STRINGBASE+10)
+#define IDS_E_11 (IDS_E_STRINGBASE+11)
+
+#define IDS_E_12 (IDS_E_STRINGBASE+12)
+
+#define IDS_E_13 (IDS_E_STRINGBASE+13)
+
+#define IDS_E_14 (IDS_E_STRINGBASE+14)
+
+#define IDS_E_15 (IDS_E_STRINGBASE+15)
+#define IDS_E_16 (IDS_E_STRINGBASE+16)
+
+#define IDS_E_17 (IDS_E_STRINGBASE+17)
+
+#define IDS_E_18 (IDS_E_STRINGBASE+18)
+
+////////////
+#define IDS_M_STRINGBASE (IDS_E_STRINGBASE+18)
+
+#define IDS_M_1 (IDS_M_STRINGBASE+1)
+#define IDS_M_2 (IDS_M_STRINGBASE+2)
+#define IDS_M_3 (IDS_M_STRINGBASE+3)
+#define IDS_M_4 (IDS_M_STRINGBASE+4)
+#define IDS_M_5 (IDS_M_STRINGBASE+5)
+#define IDS_M_6 (IDS_M_STRINGBASE+6)
+#define IDS_M_7 (IDS_M_STRINGBASE+7)
+#define IDS_M_8 (IDS_M_STRINGBASE+8)
+
+////////////
+#define IDS_L_STRINGBASE (IDS_M_STRINGBASE+8)
+
+#define IDS_L_1 (IDS_L_STRINGBASE+1)
+#define IDS_L_2 (IDS_L_STRINGBASE+2)
+#define IDS_L_3 (IDS_L_STRINGBASE+3)
+
+#define IDS_L_4 (IDS_L_STRINGBASE+4)
+#define IDS_L_5 (IDS_L_STRINGBASE+5)
+#define IDS_L_6 (IDS_L_STRINGBASE+6)
+
+#define IDS_L_7 (IDS_L_STRINGBASE+7)
+#define IDS_L_8 (IDS_L_STRINGBASE+8)
+#define IDS_L_9 (IDS_L_STRINGBASE+9)
+#define IDS_L_10 (IDS_L_STRINGBASE+10)
+
+#define IDS_L_11 (IDS_L_STRINGBASE+11)
+#define IDS_L_12 (IDS_L_STRINGBASE+12)
+#define IDS_L_13 (IDS_L_STRINGBASE+13)
+#define IDS_L_14 (IDS_L_STRINGBASE+14)
+#define IDS_L_15 (IDS_L_STRINGBASE+15)
+#define IDS_L_16 (IDS_L_STRINGBASE+16)
+
+#define IDS_L_17 (IDS_L_STRINGBASE+17)
+#define IDS_L_18 (IDS_L_STRINGBASE+18)
+#define IDS_L_19 (IDS_L_STRINGBASE+19)
+#define IDS_L_20 (IDS_L_STRINGBASE+20)
+
+#define IDS_L_21 (IDS_L_STRINGBASE+21)
+#define IDS_L_22 (IDS_L_STRINGBASE+22)
+
+#define IDS_L_23 (IDS_L_STRINGBASE+23)
+#define IDS_L_24 (IDS_L_STRINGBASE+24)
+#define IDS_L_25 (IDS_L_STRINGBASE+25)
+#define IDS_L_26 (IDS_L_STRINGBASE+26)
+#define IDS_L_27 (IDS_L_STRINGBASE+27)
+#define IDS_L_28 (IDS_L_STRINGBASE+28)
+#define IDS_L_29 (IDS_L_STRINGBASE+29)
+#define IDS_L_30 (IDS_L_STRINGBASE+30)
+#define IDS_L_31 (IDS_L_STRINGBASE+31)
+#define IDS_L_32 (IDS_L_STRINGBASE+32)
+#define IDS_L_33 (IDS_L_STRINGBASE+33)
+#define IDS_L_34 (IDS_L_STRINGBASE+34)
+#define IDS_L_35 (IDS_L_STRINGBASE+35)
+#define IDS_L_36 (IDS_L_STRINGBASE+36)
+#define IDS_L_37 (IDS_L_STRINGBASE+37)
+#define IDS_L_38 (IDS_L_STRINGBASE+38)
+#define IDS_L_39 (IDS_L_STRINGBASE+39)
+#define IDS_L_40 (IDS_L_STRINGBASE+40)
+#define IDS_L_41 (IDS_L_STRINGBASE+41)
+#define IDS_L_42 (IDS_L_STRINGBASE+42)
+#define IDS_L_43 (IDS_L_STRINGBASE+43)
+#define IDS_L_44 (IDS_L_STRINGBASE+44)
+#define IDS_L_45 (IDS_L_STRINGBASE+45)
+#define IDS_L_46 (IDS_L_STRINGBASE+46)
+#define IDS_L_47 (IDS_L_STRINGBASE+47)
+#define IDS_L_48 (IDS_L_STRINGBASE+48)
+#define IDS_L_49 (IDS_L_STRINGBASE+49)
+
+#define IDS_L_50 (IDS_L_STRINGBASE+50)
+#define IDS_L_51 (IDS_L_STRINGBASE+51)
+
+#define IDS_L_52 (IDS_L_STRINGBASE+52)
+#define IDS_L_53 (IDS_L_STRINGBASE+53)
+#define IDS_L_54 (IDS_L_STRINGBASE+54)
+#define IDS_L_55 (IDS_L_STRINGBASE+55)
+
+#define IDS_L_56 (IDS_L_STRINGBASE+56)
+#define IDS_L_57 (IDS_L_STRINGBASE+57)
+#define IDS_L_58 (IDS_L_STRINGBASE+58)
+#define IDS_L_59 (IDS_L_STRINGBASE+59)
+#define IDS_L_60 (IDS_L_STRINGBASE+60)
+#define IDS_L_61 (IDS_L_STRINGBASE+61)
+#define IDS_L_62 (IDS_L_STRINGBASE+62)
+#define IDS_L_63 (IDS_L_STRINGBASE+63)
+#define IDS_L_64 (IDS_L_STRINGBASE+64)
+#define IDS_L_65 (IDS_L_STRINGBASE+65)
+#define IDS_L_66 (IDS_L_STRINGBASE+66)
+#define IDS_L_67 (IDS_L_STRINGBASE+67)
+#define IDS_L_68 (IDS_L_STRINGBASE+68)
+#define IDS_L_69 (IDS_L_STRINGBASE+69)
+#define IDS_L_70 (IDS_L_STRINGBASE+70)
+#define IDS_L_71 (IDS_L_STRINGBASE+71)
+#define IDS_L_72 (IDS_L_STRINGBASE+72)
+#define IDS_L_73 (IDS_L_STRINGBASE+73)
+#define IDS_L_74 (IDS_L_STRINGBASE+74)
+#define IDS_L_75 (IDS_L_STRINGBASE+75)
+
+#define IDS_L_76 (IDS_L_STRINGBASE+76)
+#define IDS_L_77 (IDS_L_STRINGBASE+77)
+
+#define IDS_L_78 (IDS_L_STRINGBASE+78)
+#define IDS_L_79 (IDS_L_STRINGBASE+79)
+#define IDS_L_80 (IDS_L_STRINGBASE+80)
+#define IDS_L_81 (IDS_L_STRINGBASE+81)
+#define IDS_L_82 (IDS_L_STRINGBASE+82)
+#define IDS_L_83 (IDS_L_STRINGBASE+83)
+#define IDS_L_84 (IDS_L_STRINGBASE+84)
+#define IDS_L_85 (IDS_L_STRINGBASE+85)
+#define IDS_L_86 (IDS_L_STRINGBASE+86)
+#define IDS_L_87 (IDS_L_STRINGBASE+87)
+#define IDS_L_88 (IDS_L_STRINGBASE+88)
+#define IDS_L_89 (IDS_L_STRINGBASE+89)
+#define IDS_L_90 (IDS_L_STRINGBASE+90)
+#define IDS_L_91 (IDS_L_STRINGBASE+91)
+#define IDS_L_92 (IDS_L_STRINGBASE+92)
+#define IDS_L_93 (IDS_L_STRINGBASE+93)
+
+#define IDS_L_94 (IDS_L_STRINGBASE+94)
+#define IDS_L_95 (IDS_L_STRINGBASE+95)
+#define IDS_L_96 (IDS_L_STRINGBASE+96)
+
+#define IDS_L_97 (IDS_L_STRINGBASE+97)
+#define IDS_L_98 (IDS_L_STRINGBASE+98)
+#define IDS_L_99 (IDS_L_STRINGBASE+99)
+#define IDS_L_100 (IDS_L_STRINGBASE+100)
+
+#define IDS_L_101 (IDS_L_STRINGBASE+101)
+#define IDS_L_102 (IDS_L_STRINGBASE+102)
+#define IDS_L_103 (IDS_L_STRINGBASE+103)
+
+#define IDS_L_104 (IDS_L_STRINGBASE+104)
+
+#define IDS_L_105 (IDS_L_STRINGBASE+105)
+#define IDS_L_106 (IDS_L_STRINGBASE+106)
+#define IDS_L_107 (IDS_L_STRINGBASE+107)
+#define IDS_L_108 (IDS_L_STRINGBASE+108)
+#define IDS_L_109 (IDS_L_STRINGBASE+109)
+#define IDS_L_110 (IDS_L_STRINGBASE+110)
+#define IDS_L_111 (IDS_L_STRINGBASE+111)
+#define IDS_L_112 (IDS_L_STRINGBASE+112)
+#define IDS_L_113 (IDS_L_STRINGBASE+113)
+#define IDS_L_114 (IDS_L_STRINGBASE+114)
+#define IDS_L_115 (IDS_L_STRINGBASE+115)
+#define IDS_L_116 (IDS_L_STRINGBASE+116)
+#define IDS_L_117 (IDS_L_STRINGBASE+117)
+#define IDS_L_118 (IDS_L_STRINGBASE+118)
+#define IDS_L_119 (IDS_L_STRINGBASE+119)
+#define IDS_L_120 (IDS_L_STRINGBASE+120)
+#define IDS_L_121 (IDS_L_STRINGBASE+121)
+#define IDS_L_122 (IDS_L_STRINGBASE+122)
+#define IDS_L_123 (IDS_L_STRINGBASE+123)
+#define IDS_L_124 (IDS_L_STRINGBASE+124)
+
+#define IDS_L_125 (IDS_L_STRINGBASE+125)
+
+#define IDS_L_126 (IDS_L_STRINGBASE+126)
+#define IDS_L_127 (IDS_L_STRINGBASE+127)
+#define IDS_L_128 (IDS_L_STRINGBASE+128)
+#define IDS_L_129 (IDS_L_STRINGBASE+129)
+#define IDS_L_130 (IDS_L_STRINGBASE+130)
+#define IDS_L_131 (IDS_L_STRINGBASE+131)
+#define IDS_L_132 (IDS_L_STRINGBASE+132)
+#define IDS_L_133 (IDS_L_STRINGBASE+133)
+#define IDS_L_134 (IDS_L_STRINGBASE+134)
+#define IDS_L_135 (IDS_L_STRINGBASE+135)
+
+#define IDS_L_136 (IDS_L_STRINGBASE+136)
+#define IDS_L_137 (IDS_L_STRINGBASE+137)
+#define IDS_L_138 (IDS_L_STRINGBASE+138)
+#define IDS_L_139 (IDS_L_STRINGBASE+139)
+#define IDS_L_140 (IDS_L_STRINGBASE+140)
+#define IDS_L_141 (IDS_L_STRINGBASE+141)
+#define IDS_L_142 (IDS_L_STRINGBASE+142)
+#define IDS_L_143 (IDS_L_STRINGBASE+143)
+#define IDS_L_144 (IDS_L_STRINGBASE+144)
+#define IDS_L_145 (IDS_L_STRINGBASE+145)
+#define IDS_L_146 (IDS_L_STRINGBASE+146)
+#define IDS_L_147 (IDS_L_STRINGBASE+147)
+#define IDS_L_148 (IDS_L_STRINGBASE+148)
+#define IDS_L_149 (IDS_L_STRINGBASE+149)
+#define IDS_L_150 (IDS_L_STRINGBASE+150)
+#define IDS_L_151 (IDS_L_STRINGBASE+151)
+#define IDS_L_152 (IDS_L_STRINGBASE+152)
+#define IDS_L_153 (IDS_L_STRINGBASE+153)
+#define IDS_L_154 (IDS_L_STRINGBASE+154)
+
+#define IDS_L_155 (IDS_L_STRINGBASE+155)
+#define IDS_L_156 (IDS_L_STRINGBASE+156)
+
+#define IDS_L_157 (IDS_L_STRINGBASE+157)
+#define IDS_L_158 (IDS_L_STRINGBASE+158)
+#define IDS_L_159 (IDS_L_STRINGBASE+159)
+
+#define IDS_L_160 (IDS_L_STRINGBASE+160)
+#define IDS_L_161 (IDS_L_STRINGBASE+161)
+
+#define IDS_L_162 (IDS_L_STRINGBASE+162)
+#define IDS_L_163 (IDS_L_STRINGBASE+163)
+#define IDS_L_164 (IDS_L_STRINGBASE+164)
+
+#define IDS_L_165 (IDS_L_STRINGBASE+165)
+#define IDS_L_166 (IDS_L_STRINGBASE+166)
+#define IDS_L_167 (IDS_L_STRINGBASE+167)
+#define IDS_L_168 (IDS_L_STRINGBASE+168)
+
+#define IDS_L_169 (IDS_L_STRINGBASE+169)
+#define IDS_L_170 (IDS_L_STRINGBASE+170)
+#define IDS_L_171 (IDS_L_STRINGBASE+171)
+
+#define IDS_L_172 (IDS_L_STRINGBASE+172)
+#define IDS_L_173 (IDS_L_STRINGBASE+173)
+#define IDS_L_174 (IDS_L_STRINGBASE+174)
+#define IDS_L_175 (IDS_L_STRINGBASE+175)
+#define IDS_L_176 (IDS_L_STRINGBASE+176)
+#define IDS_L_177 (IDS_L_STRINGBASE+177)
+
+#define IDS_L_178 (IDS_L_STRINGBASE+178)
+#define IDS_L_179 (IDS_L_STRINGBASE+179)
+
+#define IDS_L_180 (IDS_L_STRINGBASE+180)
+
+#define IDS_L_181 (IDS_L_STRINGBASE+181)
+
+#define IDS_L_182 (IDS_L_STRINGBASE+182)
+#define IDS_L_183 (IDS_L_STRINGBASE+183)
+#define IDS_L_184 (IDS_L_STRINGBASE+184)
+#define IDS_L_185 (IDS_L_STRINGBASE+185)
+
+#define IDS_L_186 (IDS_L_STRINGBASE+186)
+#define IDS_L_187 (IDS_L_STRINGBASE+187)
+#define IDS_L_188 (IDS_L_STRINGBASE+188)
+
+#define IDS_L_189 (IDS_L_STRINGBASE+189)
+#define IDS_L_190 (IDS_L_STRINGBASE+190)
+#define IDS_L_191 (IDS_L_STRINGBASE+191)
+#define IDS_L_192 (IDS_L_STRINGBASE+192)
+#define IDS_L_193 (IDS_L_STRINGBASE+193)
+
+#define IDS_L_194 (IDS_L_STRINGBASE+194)
+#define IDS_L_195 (IDS_L_STRINGBASE+195)
+#define IDS_L_196 (IDS_L_STRINGBASE+196)
+#define IDS_L_197 (IDS_L_STRINGBASE+197)
+
+#define IDS_L_198 (IDS_L_STRINGBASE+198)
+#define IDS_L_199 (IDS_L_STRINGBASE+199)
+
+#define IDS_L_200 (IDS_L_STRINGBASE+200)
+#define IDS_L_201 (IDS_L_STRINGBASE+201)
+
+#define IDS_L_202 (IDS_L_STRINGBASE+202)
+
+#define IDS_L_203 (IDS_L_STRINGBASE+203)
+#define IDS_L_204 (IDS_L_STRINGBASE+204)
+#define IDS_L_205 (IDS_L_STRINGBASE+205)
+#define IDS_L_206 (IDS_L_STRINGBASE+206)
+
+#define IDS_L_207 (IDS_L_STRINGBASE+207)
+#define IDS_L_208 (IDS_L_STRINGBASE+208)
+
+#define IDS_L_209 (IDS_L_STRINGBASE+209)
+#define IDS_L_210 (IDS_L_STRINGBASE+210)
+#define IDS_L_211 (IDS_L_STRINGBASE+211)
+
+#define IDS_L_212 (IDS_L_STRINGBASE+212)
+
+#define IDS_L_213 (IDS_L_STRINGBASE+213)
+#define IDS_L_214 (IDS_L_STRINGBASE+214)
+
+#define IDS_L_215 (IDS_L_STRINGBASE+215)
+#define IDS_L_216 (IDS_L_STRINGBASE+216)
+
+#define IDS_L_217 (IDS_L_STRINGBASE+217)
+
+#define IDS_L_218 (IDS_L_STRINGBASE+218)
+#define IDS_L_219 (IDS_L_STRINGBASE+219)
+
+#define STRING_TABLE_END (IDS_L_STRINGBASE+219)
+
+#define TOTAL_STRINGS (STRING_TABLE_END - IDS_STRINGBASE)
+#endif
diff --git a/private/nw/convert/nwconv/switches.h b/private/nw/convert/nwconv/switches.h
new file mode 100644
index 000000000..a42c1f031
--- /dev/null
+++ b/private/nw/convert/nwconv/switches.h
@@ -0,0 +1,12 @@
+/*+-------------------------------------------------------------------------+
+ | Copyright 1993-1994 (C) Microsoft Corporation - All rights reserved. |
+ +-------------------------------------------------------------------------+*/
+
+#ifndef _SWITCHES_
+#define _SWITCHES_
+
+#if DBG && !defined(DEBUG)
+#define DEBUG
+#endif
+
+#endif
diff --git a/private/nw/convert/nwconv/tab.c b/private/nw/convert/nwconv/tab.c
new file mode 100644
index 000000000..f5a917331
--- /dev/null
+++ b/private/nw/convert/nwconv/tab.c
@@ -0,0 +1,1512 @@
+//////////////////////////////////////////////////////////////////////////////
+// Copyright 1990-1993 Microsoft corporation
+// all rights reservered
+//////////////////////////////////////////////////////////////////////////////
+//
+// Program: (nominally)Bloodhound
+// Module: tab.c
+// Purpose: creates and operates a book tab (big file folder) custom control
+//
+// Note: the current implementation is limited to 4 booktabs, sorry.
+//
+//
+// ---------------------------- TABSTOP = 4 -------------------
+//
+// Entry Points:
+//
+// History
+// Arthur Brooking 8/06/93 created
+//////////////////////////////////////////////////////////////////////////////
+
+//////////////////////////////////////////////////////////////////////////////
+// BookTab
+//
+//
+//
+// Input:
+// hwnd - our window handle
+//
+// Returns:
+//
+//
+// History
+// Arthur Brooking 8/06/93 created
+//////////////////////////////////////////////////////////////////////////////
+#define STRICT
+#include "switches.h"
+#include <windows.h>
+#include <windowsx.h>
+
+#include "tab.h"
+// #include "..\bhmem.h"
+
+//////////////////////////////////////////////////////////////////////////////
+// Constants
+//////////////////////////////////////////////////////////////////////////////
+#define MAX_TABS 4
+#define MAX_TAB_LABEL_LEN 128
+
+#define ANGLE_X 5
+#define ANGLE_Y 5
+
+#define CARAT_X 2
+#define CARAT_Y 2
+
+#define FLUFF_X 0
+#define FLUFF_Y 0
+
+#define FOOTROOM_Y 3
+
+// We use the selected tab for these calculations:
+//
+// tab_rect:
+//
+// ANGLE_X|--|
+//
+// - BBBBBBBBBBBBBBB
+// ANGLE_Y | BWWWWWWWWWWWWWWW
+// | BWWWWWWWWWWWWWWWW
+// - BWWW
+// BWW * <-- this is where the text_rect starts
+// BWW
+// BWW
+//
+//
+// text_rect: (defined by the *'s)
+//
+// FLUFF_X|----|
+//
+// - * *
+// |
+// FLUFF_Y |
+// | CARAT_X
+// | |---|
+// - ............................. -
+// . . |
+// . . | CARAT_Y
+// . . |
+// - . XXXXX XXXXX X X XXXXX . -
+// text hght| . X X X X X .
+// is from | . X XXX X X .
+// font | . X X X X X .
+// _ . X XXXXX X X X .
+// . .
+// . .
+// . .
+// .............................
+//
+// |---------------------|
+// text width is directly
+// from the font itself
+// * *
+//
+//
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Data Structures for this file
+//////////////////////////////////////////////////////////////////////////////
+typedef struct _ONETAB
+{
+ TCHAR label[ MAX_TAB_LABEL_LEN + 1 ];
+ DWORD data;
+ RECT tab_rect;
+ RECT text_rect;
+} ONETAB;
+
+typedef struct _TABDATA
+{
+ // do the tabs need updating ?
+ BOOL fUpdate;
+ RECT tabs_rect;
+
+ // font data
+ HFONT hfSelected;
+ HFONT hfUnselected;
+
+ // windows data
+ HWND hwndParent;
+
+ // tab data
+ UINT total_tabs;
+ UINT selected_tab;
+ ONETAB tab[ MAX_TABS ];
+
+} TABDATA;
+typedef TABDATA *LPTABDATA;
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Variables Global to this file
+//////////////////////////////////////////////////////////////////////////////
+TCHAR szBookTabName[]=BOOK_TAB_CONTROL;
+
+//////////////////////////////////////////////////////////////////////////////
+// Macros Global to this file
+//////////////////////////////////////////////////////////////////////////////
+#define GetInstanceDataPtr(hwnd) ((LPTABDATA)GetWindowLong(hwnd, 0))
+#define SetInstanceDataPtr(hwnd,x) (SetWindowLong(hwnd, 0, (DWORD)x))
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Functional Prototypes for Functions Local to this File
+//////////////////////////////////////////////////////////////////////////////
+LRESULT CALLBACK BookTab_WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
+BOOL BookTab_OnCreate(HWND hwnd, CREATESTRUCT FAR* lpCreateStruct);
+void BookTab_OnDestroy(HWND hwnd);
+void BookTab_OnLButtonDown(HWND hwnd, BOOL fDblClk, int x, int y, UINT keyFlags);
+void BookTab_OnPaint(HWND hwnd);
+UINT BookTab_OnGetDlgCode(HWND hwnd, MSG FAR* lpmsg);
+void BookTab_OnSize(HWND hwnd, UINT state, int cx, int cy);
+void BookTab_OnSetFocus(HWND hwnd, HWND hwndOldFocus);
+void BookTab_OnKillFocus(HWND hwnd, HWND hwndNewFocus);
+void BookTab_OnKey(HWND hwnd, UINT vk, BOOL fDown, int cRepeat, UINT flags);
+void BookTab_OnEnable(HWND hwnd, BOOL fEnable);
+
+UINT BookTab_OnAddItem( HWND hwnd, LPTSTR text );
+UINT BookTab_OnInsertItem( HWND hwnd, UINT index, LPTSTR text);
+BOOL BookTab_OnDeleteItem( HWND hwnd, UINT index );
+BOOL BookTab_OnDeleteAllItems( HWND hwnd);
+BOOL BookTab_OnSetItem( HWND hwnd, UINT index, LPTSTR text );
+BOOL BookTab_OnGetItem( HWND hwnd, UINT index, LPTSTR text );
+UINT BookTab_OnSetCurSel( HWND hand, UINT newsel );
+UINT BookTab_OnGetCurSel( HWND hand );
+UINT BookTab_OnGetItemCount( HWND hwnd );
+BOOL BookTab_OnSetItemData( HWND hwnd, UINT index, DWORD data );
+DWORD BookTab_OnGetItemData( HWND hwnd, UINT index);
+void BookTab_OnPutInBack( HWND hwnd );
+
+BOOL IsPointInRect( int given_x, int given_y, LPRECT pRect );
+void BookTab_UpdateButtons( HWND hwnd );
+
+//////////////////////////////////////////////////////////////////////////////
+// BookTab_Initialize()
+//
+// Initializes and registers the BookTab custom class
+//
+// Input:
+// hInstance - the handle to our parent's instance
+//
+// Returns:
+// True if successful, else False
+//
+// History
+// Arthur Brooking 8/06/93 created
+//////////////////////////////////////////////////////////////////////////////
+void BookTab_Initialize(HINSTANCE hInstance)
+{
+ WNDCLASS wndclass;
+
+ wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_GLOBALCLASS | CS_PARENTDC;
+ wndclass.lpfnWndProc = BookTab_WndProc;
+ wndclass.cbClsExtra = 0;
+ wndclass.cbWndExtra = sizeof( LPTABDATA );
+ wndclass.hInstance = hInstance;
+ wndclass.hIcon = NULL;
+ wndclass.hCursor = LoadCursor(NULL,IDC_ARROW);
+ wndclass.hbrBackground = NULL;
+ wndclass.lpszMenuName = NULL;
+ wndclass.lpszClassName = szBookTabName;
+
+ RegisterClass ( &wndclass );
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// BookTab_WndProc()
+//
+// Distributes messages coming in to the BookTab control
+//
+// Input:
+// hwnd - Our handle
+// message - the ordinal of the incoming message
+// wParam - half of the incoming data
+// lParam - the other half of the incoming data
+//
+// Returns:
+// True if we handled the message, else False
+//
+// History
+// Arthur Brooking 8/06/93 created
+//////////////////////////////////////////////////////////////////////////////
+LRESULT CALLBACK BookTab_WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch (message)
+ {
+ // standard windows messages
+ HANDLE_MSG( hwnd, WM_CREATE, BookTab_OnCreate);
+ HANDLE_MSG( hwnd, WM_DESTROY, BookTab_OnDestroy);
+ HANDLE_MSG( hwnd, WM_LBUTTONDOWN, BookTab_OnLButtonDown);
+ HANDLE_MSG( hwnd, WM_PAINT, BookTab_OnPaint);
+ HANDLE_MSG( hwnd, WM_SIZE, BookTab_OnSize);
+ HANDLE_MSG( hwnd, WM_SETFOCUS, BookTab_OnSetFocus);
+ HANDLE_MSG( hwnd, WM_KILLFOCUS, BookTab_OnKillFocus);
+ HANDLE_MSG( hwnd, WM_KEYDOWN, BookTab_OnKey);
+ HANDLE_MSG( hwnd, WM_KEYUP, BookTab_OnKey);
+
+ // messages specific to all custom controls
+ HANDLE_MSG( hwnd, WM_GETDLGCODE, BookTab_OnGetDlgCode);
+ HANDLE_MSG( hwnd, WM_ENABLE, BookTab_OnEnable);
+
+ // messages specific to THIS custom control
+ case BT_ADDITEM:
+ return( BookTab_OnAddItem( hwnd, (LPTSTR)lParam ));
+ case BT_INSERTITEM:
+ return( BookTab_OnInsertItem( hwnd, (UINT)wParam, (LPTSTR)lParam ));
+ case BT_DELETEITEM:
+ return( BookTab_OnDeleteItem( hwnd, (UINT)wParam ));
+ case BT_DELETEALLITEMS:
+ return( BookTab_OnDeleteAllItems( hwnd ));
+ case BT_SETITEM:
+ return( BookTab_OnSetItem( hwnd, (UINT)wParam, (LPTSTR)lParam ));
+ case BT_GETITEM:
+ return( BookTab_OnGetItem( hwnd, (UINT)wParam, (LPTSTR)lParam ));
+ case BT_SETCURSEL:
+ return( BookTab_OnSetCurSel( hwnd, (UINT)wParam ));
+ case BT_GETCURSEL:
+ return( BookTab_OnGetCurSel( hwnd ));
+ case BT_GETITEMCOUNT:
+ return( BookTab_OnGetItemCount( hwnd ));
+ case BT_SETITEMDATA:
+ return( BookTab_OnSetItemData( hwnd, (UINT)wParam, (DWORD)lParam ));
+ case BT_GETITEMDATA:
+ return( BookTab_OnGetItemData( hwnd, (UINT)wParam ));
+ case BT_PUTINBACK:
+ BookTab_OnPutInBack( hwnd );
+ return (TRUE);
+ }
+ // pass unprocessed messages to DefWndProc...
+ return DefWindowProc(hwnd, message, wParam, lParam);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// BookTab_OnCreate()
+//
+// Initializes a new instance of our lovely custom control
+//
+// Input:
+// hwnd - our window handle
+// lpcreatestruct - pointer to the data with which to do our thing
+//
+// Returns:
+// True if the instance is created, else false
+//
+// History
+// Arthur Brooking 8/06/93 created
+//////////////////////////////////////////////////////////////////////////////
+BOOL BookTab_OnCreate(HWND hwnd, CREATESTRUCT FAR* lpCreateStruct)
+{
+ LPTABDATA pData;
+ UINT i;
+
+ // allocate the instance data for this control
+ pData = LocalAlloc( LPTR, sizeof( TABDATA ));
+ if( pData == NULL )
+ return FALSE;
+ SetInstanceDataPtr( hwnd, pData );
+
+ // initialize values in the control
+ pData->total_tabs = 0;
+ pData->selected_tab = 0;
+
+ pData->hwndParent = lpCreateStruct->hwndParent;
+
+ // fill the prospective tab slots with data
+ for( i = 0; i < MAX_TABS; i++ )
+ {
+ pData->tab[i].label[0] = TEXT('\0');
+ pData->tab[i].data = (DWORD)0;
+ }
+
+#ifdef JAPAN // BookTab_OnCreate()
+ // create the proper fonts:
+ // 8 pt MS Gothic bold for selected and
+ // 8 pt MS Gothic regular for not selected
+ {
+ #ifdef UNICODE
+ WCHAR szGothic[] = {0xff2d, 0xff33, 0x20, 0x30b4, 0x30b7, 0x30c3, 0x30af, 0 };
+ #else
+ #error Ansi string of szGothic is nessesory.
+ #endif
+
+ pData->hfSelected = CreateFont( -8, 0, 0, 0,
+ FW_BOLD, FALSE, FALSE, FALSE,
+ SHIFTJIS_CHARSET, OUT_TT_PRECIS,
+ CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
+ 0x4, szGothic );
+ pData->hfUnselected = CreateFont( -8, 0, 0, 0,
+ FW_NORMAL, FALSE, FALSE, FALSE,
+ SHIFTJIS_CHARSET, OUT_TT_PRECIS,
+ CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
+ 0x4, szGothic );
+ }
+#else
+ // create the proper fonts:
+ // 8 pt sans serif bold for selected and
+ // 8 pt sans serif regular for not selected
+ pData->hfSelected = CreateFont( -8, 0, 0, 0,
+ FW_BOLD, FALSE, FALSE, FALSE,
+ ANSI_CHARSET, OUT_TT_PRECIS,
+ CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
+ 0x4, TEXT("MS Sans Serif") );
+ pData->hfUnselected = CreateFont( -8, 0, 0, 0,
+ FW_NORMAL, FALSE, FALSE, FALSE,
+ ANSI_CHARSET, OUT_TT_PRECIS,
+ CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
+ 0x4, TEXT("MS Sans Serif") );
+#endif // JAPAN
+
+ // fill the rest of the sizing info
+ BookTab_OnSize( hwnd, 0, lpCreateStruct->cx, lpCreateStruct->cy );
+
+ // make sure that we are on the bottom
+ SetWindowPos( hwnd, HWND_BOTTOM, 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE | SWP_NOREDRAW );
+
+ // make sure we update
+ pData->fUpdate = TRUE;
+
+ // put us last
+ BookTab_PutInBack( hwnd );
+
+ return TRUE;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// BookTab_OnDestroy()
+//
+// Cleans up as our control goes away
+//
+// Input:
+// hwnd - our window handle
+//
+// Returns:
+// nothing
+//
+// History
+// Arthur Brooking 8/06/93 created
+//////////////////////////////////////////////////////////////////////////////
+void BookTab_OnDestroy(HWND hwnd)
+{
+ LPTABDATA pData;
+
+ // get the instance data
+ pData = GetInstanceDataPtr( hwnd );
+
+ // delete our fonts
+ DeleteObject( pData->hfSelected );
+ DeleteObject( pData->hfUnselected );
+
+ // free up our instance data
+ LocalFree( pData );
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// BookTab_OnLButtonDown()
+//
+// Handles the event where a user has the left mouse button down
+//
+// Input:
+// hwnd - our window handle
+// fDblClk - an indication on the second message of a double click
+// x - the mouses x coordinate at the time of the message
+// y - the mouses y coordinate at the time of the message
+// keyFlags - an indication of which keys were pressed at the time
+//
+// Returns:
+// nuthin'
+//
+// History
+// Arthur Brooking 8/06/93 created
+//////////////////////////////////////////////////////////////////////////////
+void BookTab_OnLButtonDown(HWND hwnd, BOOL fDblClk, int x, int y, UINT keyFlags)
+{
+ LPTABDATA pData;
+ UINT i;
+
+ // get the instance data
+ pData = GetInstanceDataPtr( hwnd );
+
+ // where did they click the mouse...
+ // loop thru the tabs to find the one struck
+ for( i = 0; i < pData->total_tabs; i++ )
+ {
+ if( IsPointInRect( x, y, &(pData->tab[i].tab_rect) ) )
+ {
+ // this is the correct spot
+ BookTab_OnSetCurSel( hwnd, i );
+
+ // notify our parent that the selection has changed
+ SendMessage( pData->hwndParent, BTN_SELCHANGE,
+ pData->selected_tab, (DWORD)hwnd);
+
+ SetFocus( hwnd );
+ return;
+ }
+ }
+
+ // the mouse was clicked outside any of the button areas
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// BookTab_OnPaint()
+//
+// Handles requests from windows that the control repaint itself
+//
+// Input:
+// hwnd - our window handle
+//
+// Returns:
+// hopefully :)
+//
+// History
+// Arthur Brooking 8/06/93 created
+//////////////////////////////////////////////////////////////////////////////
+void BookTab_OnPaint(HWND hwnd)
+{
+ LPTABDATA pData;
+ PAINTSTRUCT ps;
+ HDC hdc;
+ TEXTMETRIC tm;
+ UINT i;
+ HWND hwndFocus;
+
+ HPEN hOldPen;
+ HPEN hShadowPen;
+ HPEN hHighlightPen;
+ HPEN hFramePen;
+ HPEN hBackPen;
+ HBRUSH hBackBrush;
+ HFONT hfOldFont;
+
+ WORD cyChar;
+ WORD yWidth;
+ WORD xWidth;
+ RECT total;
+ RECT temp;
+ LPRECT pTab;
+ LPRECT pText;
+
+ // get the instance data
+ pData = GetInstanceDataPtr( hwnd );
+
+ // right before drawing, make sure that the button sizes are accurate
+ BookTab_UpdateButtons( hwnd );
+
+ // get the instance data
+ pData = GetInstanceDataPtr( hwnd );
+
+ // get the handle to the window with the current focus
+ hwndFocus = GetFocus();
+
+ // prepare for painting...
+ BeginPaint( hwnd, &ps );
+ hdc = GetDC( hwnd );
+
+ // set text stuff
+ SetBkColor(hdc, GetSysColor(COLOR_BTNFACE));
+ SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT));
+ SetTextAlign( hdc, TA_TOP | TA_LEFT );
+
+ // determine proper sizes
+ GetTextMetrics(hdc, &tm);
+ cyChar = (WORD)tm.tmHeight;
+ xWidth = GetSystemMetrics(SM_CXBORDER);
+ yWidth = GetSystemMetrics(SM_CYBORDER);
+ GetClientRect( hwnd, &total );
+ //BUGBUG fudge the rectangle so that the bottom and left do not get cut off
+ total.bottom -= yWidth;
+ total.right -= xWidth;
+
+ // set up the pens that we will need
+ hHighlightPen = CreatePen(PS_SOLID, yWidth, GetSysColor(COLOR_BTNHIGHLIGHT));
+ hShadowPen = CreatePen(PS_SOLID, yWidth, GetSysColor(COLOR_BTNSHADOW));
+ hFramePen = CreatePen(PS_SOLID, yWidth, GetSysColor(COLOR_WINDOWFRAME));
+ hBackPen = CreatePen(PS_SOLID, yWidth, GetSysColor(COLOR_BTNFACE));
+ hBackBrush = CreateSolidBrush( GetSysColor(COLOR_BTNFACE));
+
+ // get the old pen by setting a new one
+ hOldPen = SelectPen( hdc, hHighlightPen );
+
+ // clear out behind the tabs if we need to
+ if( pData->fUpdate == TRUE )
+ {
+ FillRect( hdc, &(pData->tabs_rect), hBackBrush );
+ pData->fUpdate = FALSE;
+ }
+
+ // draw the box...
+ // left side dark border
+ SelectPen( hdc, hFramePen );
+ MoveToEx( hdc, total.left, pData->tab[0].tab_rect.bottom+yWidth, NULL );
+ LineTo ( hdc, total.left, total.bottom );
+ // bottom dark border
+ LineTo ( hdc, total.right, total.bottom );
+ // right side dark border
+ LineTo ( hdc, total.right, pData->tab[0].tab_rect.bottom+yWidth);
+ // top dark border, right half (over to selection)
+ LineTo ( hdc, pData->tab[pData->selected_tab].tab_rect.right-yWidth,
+ pData->tab[0].tab_rect.bottom+yWidth);
+ // skip area under the selected tab
+ MoveToEx( hdc, pData->tab[pData->selected_tab].tab_rect.left,
+ pData->tab[0].tab_rect.bottom+yWidth, NULL);
+ // top dark border, left half (from selection to left border)
+ LineTo ( hdc, total.left, pData->tab[0].tab_rect.bottom+yWidth);
+
+ // left side highlight #1
+ SelectPen( hdc, hHighlightPen );
+ MoveToEx( hdc, total.left+xWidth, pData->tab[0].tab_rect.bottom+2*yWidth, NULL );
+ LineTo( hdc, total.left+xWidth, total.bottom-yWidth );
+
+ // bottom shadow #1
+ SelectPen( hdc, hShadowPen );
+ LineTo( hdc, total.right-xWidth, total.bottom-yWidth );
+ // right side shadow #1
+ LineTo( hdc, total.right-xWidth, pData->tab[0].tab_rect.bottom+2*yWidth );
+
+ // top hilite #1
+ SelectPen( hdc, hHighlightPen );
+ // top hilite, right half (over to selection)
+ LineTo ( hdc, pData->tab[pData->selected_tab].tab_rect.right-yWidth,
+ pData->tab[0].tab_rect.bottom+2*yWidth);
+ // skip area under the selected tab
+ MoveToEx( hdc, pData->tab[pData->selected_tab].tab_rect.left,
+ pData->tab[0].tab_rect.bottom+2*yWidth, NULL);
+ // top hilite, left half (from selection to left border)
+ if( pData->selected_tab != 0 )
+ LineTo ( hdc, total.left+2*xWidth,
+ pData->tab[0].tab_rect.bottom+2*yWidth);
+
+ // left side highlight #2
+ SelectPen( hdc, hHighlightPen );
+ MoveToEx( hdc, total.left+2*xWidth, pData->tab[0].tab_rect.bottom+3*yWidth, NULL );
+ LineTo( hdc, total.left+2*xWidth, total.bottom-2*yWidth );
+
+ // bottom shadow #2
+ SelectPen( hdc, hShadowPen );
+ LineTo( hdc, total.right-2*xWidth, total.bottom-2*yWidth );
+ // right side shadow #2
+ LineTo( hdc, total.right-2*xWidth, pData->tab[0].tab_rect.bottom+3*yWidth );
+
+ // top hilite #2
+ SelectPen( hdc, hHighlightPen );
+ // top hilite, right half (over to selection)
+ LineTo ( hdc, pData->tab[pData->selected_tab].tab_rect.right-2*yWidth,
+ pData->tab[0].tab_rect.bottom+3*yWidth);
+ // skip area under the selected tab
+ MoveToEx( hdc, pData->tab[pData->selected_tab].tab_rect.left,
+ pData->tab[0].tab_rect.bottom+3*yWidth, NULL);
+ // top hilite, left half (from selection to left border)
+ if( pData->selected_tab != 0 )
+ LineTo ( hdc, total.left+2*xWidth,
+ pData->tab[0].tab_rect.bottom+3*yWidth);
+
+ // Draw the tabs...
+ // loop thru the tabs
+ for( i = 0; i < pData->total_tabs; i++ )
+ {
+ // point our local variables at the current rects
+ pTab = &(pData->tab[i].tab_rect);
+ pText = &(pData->tab[i].text_rect);
+
+ if( i == pData->selected_tab )
+ {
+ // this is the selection, it should not be pushed down...
+ // left side dark border
+ SelectPen( hdc, hFramePen );
+ MoveToEx(hdc, pTab->left, pTab->bottom, NULL);
+ LineTo(hdc, pTab->left, pTab->top + ANGLE_Y*yWidth);
+ // left side angle dark border
+ LineTo(hdc, pTab->left + ANGLE_X*xWidth, pTab->top);
+ // top dark border
+ LineTo(hdc, pTab->right - ANGLE_X*xWidth, pTab->top);
+ // right side angle dark border
+ LineTo(hdc, pTab->right, pTab->top + ANGLE_Y*yWidth);
+ // right side dark border (overshoot by one)
+ LineTo(hdc, pTab->right, pTab->bottom+yWidth);
+
+ // left side hilite #1 (extends down 3 below the box to handle
+ // melding the hilites with the box below)
+ SelectPen( hdc, hHighlightPen);
+ MoveToEx(hdc, pTab->left+xWidth, pTab->bottom+3*yWidth, NULL);
+ LineTo(hdc, pTab->left+xWidth, pTab->top+ANGLE_Y*yWidth );
+ // left side angle hilight #1
+ LineTo(hdc, pTab->left+ANGLE_X*xWidth, pTab->top+yWidth );
+ // top hilite #1
+ LineTo(hdc, pTab->right-ANGLE_X*xWidth, pTab->top+yWidth );
+ // right side angle shadow #1
+ SelectPen( hdc, hShadowPen);
+ LineTo(hdc, pTab->right-xWidth, pTab->top+ANGLE_Y*yWidth );
+ // right side shadow #1 (overshoot by one) (see above)
+ LineTo(hdc, pTab->right-xWidth, pTab->bottom+3*yWidth);
+
+ // left side hilite #2 (the 2* are becaus we are the 2nd hilite)
+ SelectPen( hdc, hHighlightPen);
+ MoveToEx(hdc, pTab->left+2*xWidth, pTab->bottom+3*yWidth, NULL);
+ LineTo(hdc, pTab->left+2*xWidth, pTab->top+ANGLE_Y*yWidth );
+ // left side angle hilight #2
+ LineTo(hdc, pTab->left+ANGLE_X*xWidth, pTab->top+2*yWidth );
+ // top hilite #2
+ LineTo(hdc, pTab->right-ANGLE_X*xWidth, pTab->top+2*yWidth );
+ // right side angle shadow #2
+ SelectPen( hdc, hShadowPen);
+ LineTo(hdc, pTab->right-2*xWidth, pTab->top+ANGLE_Y*yWidth );
+ // right side shadow #2 (overshoot by one)
+ LineTo(hdc, pTab->right-2*xWidth, pTab->bottom+4*yWidth );
+
+ // clear out the chunk below the active tab
+ SelectPen(hdc, hBackPen );
+ MoveToEx(hdc, pTab->left+3*xWidth, pTab->bottom+yWidth, NULL);
+ LineTo(hdc, pTab->right-2*xWidth, pTab->bottom+yWidth);
+ MoveToEx(hdc, pTab->left+3*xWidth, pTab->bottom+2*yWidth, NULL);
+ LineTo(hdc, pTab->right-2*xWidth, pTab->bottom+2*yWidth);
+ MoveToEx(hdc, pTab->left+3*xWidth, pTab->bottom+3*yWidth, NULL);
+ LineTo(hdc, pTab->right-2*xWidth, pTab->bottom+3*yWidth);
+
+ // clear out the old label...
+ FillRect( hdc, pText, hBackBrush );
+
+ // now print in the label ...
+ hfOldFont = SelectObject( hdc, pData->hfSelected );
+ ExtTextOut( hdc,
+ pText->left+ CARAT_X*xWidth + FLUFF_X*xWidth,
+ pText->top + CARAT_Y*yWidth + FLUFF_Y*yWidth,
+ 0, NULL, pData->tab[i].label,
+ lstrlen(pData->tab[i].label), NULL );
+ SelectFont( hdc, hfOldFont );
+
+ // if we have the focus, print the caret
+ if( hwnd == hwndFocus )
+ {
+ // we have the focus
+ temp.top = pText->top + FLUFF_X*xWidth;
+ temp.left = pText->left + FLUFF_Y*yWidth;
+ temp.bottom = pText->bottom - FLUFF_X*xWidth;
+ temp.right = pText->right - FLUFF_Y*yWidth;
+ DrawFocusRect( hdc, &temp );
+ }
+
+ }
+ else
+ {
+ // push this tab down one border width...
+ // this will mean an extra +1 on all ANGLE_Ys...
+ // left side dark border
+ SelectPen( hdc, hFramePen );
+ MoveToEx(hdc, pTab->left, pTab->bottom, NULL);
+ LineTo(hdc, pTab->left, pTab->top + (ANGLE_Y+1)*yWidth);
+ // left side angle dark border
+ LineTo(hdc, pTab->left + ANGLE_X*xWidth, pTab->top+yWidth);
+ // top dark border
+ LineTo(hdc, pTab->right - ANGLE_X*yWidth, pTab->top+yWidth);
+ // right side angle dark border
+ LineTo(hdc, pTab->right, pTab->top + (ANGLE_Y+1)*yWidth);
+ // right side dark border (overshoot by one)
+ LineTo(hdc, pTab->right, pTab->bottom+yWidth);
+
+ // left side hilite
+ SelectPen( hdc, hHighlightPen);
+ MoveToEx(hdc, pTab->left+xWidth, pTab->bottom, NULL);
+ LineTo(hdc, pTab->left+xWidth, pTab->top+(ANGLE_Y+1)*yWidth);
+ // left side angle hilight
+ LineTo(hdc, pTab->left+ANGLE_X*xWidth, pTab->top+2*yWidth);
+ // top hilite
+ LineTo(hdc, pTab->right-ANGLE_X*xWidth, pTab->top+2*yWidth);
+
+ // right side angle shadow
+ SelectPen( hdc, hShadowPen);
+ LineTo(hdc, pTab->right-xWidth, pTab->top+(ANGLE_Y+1)*yWidth);
+ // right side shadow (overshoot by one)
+ LineTo(hdc, pTab->right-xWidth, pTab->bottom+yWidth);
+
+ // clean above left angle
+ SelectPen( hdc, hBackPen );
+ MoveToEx(hdc, pTab->left, pTab->top+ANGLE_Y*yWidth, NULL );
+ LineTo(hdc, pTab->left+ANGLE_X*xWidth, pTab->top );
+ // clean above top
+ LineTo(hdc, pTab->right-ANGLE_X*xWidth, pTab->top);
+ // clean above right angle
+ LineTo(hdc, pTab->right, pTab->top+ANGLE_Y*yWidth );
+ // clean last corner
+ LineTo(hdc, pTab->right, pTab->top+(ANGLE_Y+1)*yWidth );
+
+ // clean up inside left hilite
+ MoveToEx(hdc, pTab->left+2*xWidth, pTab->bottom, NULL );
+ LineTo(hdc, pTab->left+2*xWidth, pTab->top+(ANGLE_Y+1)*yWidth);
+ // clean up inside left angle hilite
+ LineTo(hdc, pTab->left+ANGLE_X*xWidth, pTab->top+3*yWidth);
+ // clean up inside top hilite (noop)
+ LineTo(hdc, pTab->right-ANGLE_X*xWidth, pTab->top+3*yWidth);
+ // clean up inside right angle shadow (noop)
+ LineTo(hdc, pTab->right-2*xWidth, pTab->top+(ANGLE_Y+1)*yWidth);
+ // clean up inside left hilite (overshoot by one)
+ LineTo(hdc, pTab->right-2*xWidth, pTab->bottom+yWidth);
+
+ // clear out the old label...
+ FillRect( hdc, pText, hBackBrush );
+
+ // now print in the label ...
+ hfOldFont = SelectObject( hdc, pData->hfUnselected );
+ ExtTextOut( hdc,
+ pText->left+ CARAT_X*xWidth + FLUFF_X*xWidth,
+ pText->top + CARAT_Y*yWidth + FLUFF_Y*yWidth + yWidth,
+ 0, NULL, pData->tab[i].label,
+ lstrlen(pData->tab[i].label), NULL );
+ SelectFont( hdc, hfOldFont );
+ }
+ }
+
+ SelectPen( hdc, hOldPen);
+
+ // put the DC we used back into circulation
+ ReleaseDC( hwnd, hdc );
+
+ // tell windows that we're done
+ EndPaint( hwnd, &ps );
+
+ // clean up
+ DeleteObject( hHighlightPen );
+ DeleteObject( hShadowPen );
+ DeleteObject( hFramePen );
+ DeleteObject( hBackPen );
+ DeleteObject( hBackBrush );
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// BookTab_OnSize()
+//
+// Handles requests from windows that we should resize ourselves
+//
+// Input:
+// hwnd - our window handle
+// state - an indication of Minimized, maximized, iconic, blah blah blah
+// cx - our new width
+// cy - our new height
+//
+// Returns:
+// hopefully :)
+//
+// History
+// Arthur Brooking 8/06/93 created
+//////////////////////////////////////////////////////////////////////////////
+void BookTab_OnSize(HWND hwnd, UINT state, int cx, int cy)
+{
+ // need to update the button size stuff, just for hit testing
+ BookTab_UpdateButtons(hwnd);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// BookTab_OnSetFocus()
+//
+// Handles windows telling us that we just got the focus
+//
+// Input:
+// hwnd - our window handle
+// hwndOld - the guy who used to have the focus (i don't use)
+//
+// Returns:
+// nyaytay
+//
+// History
+// Arthur Brooking 8/06/93 created
+//////////////////////////////////////////////////////////////////////////////
+void BookTab_OnSetFocus(HWND hwnd, HWND hwndOldFocus)
+{
+ LPTABDATA pData;
+
+ // get the instance data
+ pData = GetInstanceDataPtr( hwnd );
+
+ // make sure that we are on the bottom
+ SetWindowPos( hwnd, HWND_BOTTOM, 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE | SWP_NOREDRAW );
+
+ // we gotta repaint just the rect for the active tab
+ InvalidateRect( hwnd, &(pData->tab[pData->selected_tab].tab_rect), FALSE );
+ UpdateWindow( hwnd );
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// BookTab_OnKillFocus()
+//
+// Handles windows telling us that we are just about to lose the focus
+//
+// Input:
+// hwnd - our window handle
+// hwndNew - the lucky guy who is about to have the focus (i don't use)
+//
+// Returns:
+// nyaytay
+//
+// History
+// Arthur Brooking 8/06/93 created
+//////////////////////////////////////////////////////////////////////////////
+void BookTab_OnKillFocus(HWND hwnd, HWND hwndNewFocus)
+{
+ LPTABDATA pData;
+
+ // get the instance data
+ pData = GetInstanceDataPtr( hwnd );
+
+ // we gotta repaint just the rect for the active tab
+ InvalidateRect( hwnd, &(pData->tab[pData->selected_tab].tab_rect), FALSE );
+ UpdateWindow( hwnd );
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// BookTab_OnKey()
+//
+// Handes key messages sent to the control
+//
+// Input:
+// hwnd - our window handle
+// vk - the virtual key code
+// fDown - is the key down?
+// cRepeat - how many times it was pressed
+// flags - i don't use
+//
+// Returns:
+// nada
+//
+// History
+// Arthur Brooking 8/06/93 created
+//////////////////////////////////////////////////////////////////////////////
+void BookTab_OnKey(HWND hwnd, UINT vk, BOOL fDown, int cRepeat, UINT flags)
+{
+ LPTABDATA pData;
+
+ // get the instance data
+ pData = GetInstanceDataPtr( hwnd );
+
+ // don't want key up messages
+ if( fDown == FALSE )
+ return;
+
+ // we only handle left and right cursor
+ switch( vk )
+ {
+ case VK_LEFT:
+ // move to the tab to the left (wrap if needed)
+ BookTab_OnSetCurSel( hwnd, (pData->selected_tab == 0)?
+ (pData->total_tabs-1):(pData->selected_tab-1));
+
+ // notify our parent that the selection has changed
+ SendMessage( pData->hwndParent, BTN_SELCHANGE,
+ pData->selected_tab, (DWORD)hwnd);
+ break;
+
+
+
+ case VK_RIGHT:
+ BookTab_OnSetCurSel( hwnd,
+ (pData->selected_tab+1) % (pData->total_tabs));
+
+ // notify our parent that the selection has changed
+ SendMessage( pData->hwndParent, BTN_SELCHANGE,
+ pData->selected_tab, (DWORD)hwnd);
+ break;
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+// BookTab_OnGetDlgCode()
+//
+// The windows dialog manager is asking us what type of user inputs we want
+//
+// Input:
+// hwnd - our window handle
+// lpmsg - who knows, I don't use it, it's not in the paper docs...
+//
+// Returns:
+// a word which is a bitmap of input types
+//
+// History
+// Arthur Brooking 8/06/93 created
+//////////////////////////////////////////////////////////////////////////////
+UINT BookTab_OnGetDlgCode(HWND hwnd, MSG FAR* lpmsg)
+{
+ // We just want cursor keys and character keys
+ return( DLGC_WANTARROWS | DLGC_WANTCHARS );
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// BookTab_OnEnable()
+//
+// Windows is telling us that we are being enabled/disabled
+//
+// Input:
+// hwnd - our window handle
+// fEnable - Are we being enabled?
+//
+// Returns:
+// nada
+//
+// History
+// Arthur Brooking 8/06/93 created
+//////////////////////////////////////////////////////////////////////////////
+void BookTab_OnEnable(HWND hwnd, BOOL fEnable)
+{
+ // BUGBUG - we look no different in either state
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// BookTab_OnAddItem()
+//
+// Adds an item to the end of the tab list
+//
+// Input:
+// hwnd - our window handle
+// text - the label of the tab to add
+//
+// Returns:
+// the index of the item as added
+//
+// History
+// Arthur Brooking 8/06/93 created
+//////////////////////////////////////////////////////////////////////////////
+UINT BookTab_OnAddItem( HWND hwnd, LPTSTR text )
+{
+ LPTABDATA pData;
+
+ // get the instance data
+ pData = GetInstanceDataPtr( hwnd );
+
+ // call the worker for insert with the current end of the tab lizst
+ return( BookTab_OnInsertItem( hwnd, pData->total_tabs, text) );
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+// BookTab_OnInsertItem()
+//
+// Inserts the given item at the spot indicated and shoves the others down
+//
+// Input:
+// hwnd - our window handle
+// index - the proposed index of the new item
+// text - the label to add to the new tab
+//
+// Returns:
+// the ACTUAL new index of the item (we could sort or have to reduce
+// the initial request if it would leave a gap)
+//
+// History
+// Arthur Brooking 8/06/93 created
+//////////////////////////////////////////////////////////////////////////////
+UINT BookTab_OnInsertItem( HWND hwnd, UINT index, LPTSTR text)
+{
+ LPTABDATA pData;
+ int i;
+
+ // get the instance data
+ pData = GetInstanceDataPtr( hwnd );
+
+ // make sure that the text will fit
+ if( lstrlen( text ) > MAX_TAB_LABEL_LEN-1 )
+ return (UINT)-1;
+
+ // are we full
+ // BUGBUG, silly limit in the future
+ if( pData->total_tabs >= MAX_TABS )
+ // we can not add at this time
+ return (UINT)-1;
+
+ // make sure that the requested index is within or adjacent to currently
+ // used spots
+ if( index > pData->total_tabs )
+ // change it so that index now points at the next open slot
+ index = pData->total_tabs;
+
+ // slide over all tabs above
+ for( i = (int)pData->total_tabs; i > (int)index; i-- )
+ {
+ memcpy( &(pData->tab[i]), &(pData->tab[i-1]), sizeof( ONETAB) );
+ }
+
+ // your room is ready sir
+ lstrcpy( pData->tab[index].label, text );
+ pData->total_tabs++;
+
+ // should clear the background
+ pData->fUpdate = TRUE;
+
+ return index;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// BookTab_OnDeleteItem()
+//
+// Deletes the item at the index given and closes up the gaps
+//
+// Input:
+// hwnd - our window handle
+// index - item to be deleted
+//
+// Returns:
+// nuthin'
+//
+// History
+// Arthur Brooking 8/06/93 created
+//////////////////////////////////////////////////////////////////////////////
+BOOL BookTab_OnDeleteItem( HWND hwnd, UINT index )
+{
+ LPTABDATA pData;
+ UINT i;
+
+ // get the instance data
+ pData = GetInstanceDataPtr( hwnd );
+
+ // make sure that we even have an element like this
+ if( index >= pData->total_tabs )
+ return FALSE;
+
+ // slide all of the deceased successors over
+ for( i = index+1; i < pData->total_tabs; i++ )
+ {
+ memcpy( &(pData->tab[i-1]), &(pData->tab[i]), sizeof( ONETAB) );
+ }
+
+ // reduce the count to account for the deletion
+ pData->total_tabs--;
+
+ // should clear the background
+ pData->fUpdate = TRUE;
+
+ return TRUE;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// BookTab_OnDeleteAllItems()
+//
+// Genocide on tabs
+//
+// Input:
+// hwnd - our window handle
+//
+// Returns:
+// nothing
+//
+// History
+// Arthur Brooking 8/06/93 created
+//////////////////////////////////////////////////////////////////////////////
+BOOL BookTab_OnDeleteAllItems( HWND hwnd)
+{
+ LPTABDATA pData;
+
+ // get the instance data
+ pData = GetInstanceDataPtr( hwnd );
+
+ // BUGBUG just set our count to zero
+ pData->total_tabs = 0;
+
+ return TRUE;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// BookTab_OnSetItem()
+//
+// Sets the title of the booktab given
+//
+// Input:
+// hwnd - our window handle
+// index - the tab to label
+// text - the words to put on the tab
+//
+// Returns:
+// TRUE if successful, else False
+//
+// History
+// Arthur Brooking 8/06/93 created
+//////////////////////////////////////////////////////////////////////////////
+BOOL BookTab_OnSetItem( HWND hwnd, UINT index, LPTSTR text )
+{
+ LPTABDATA pData;
+
+ // get the instance data
+ pData = GetInstanceDataPtr( hwnd );
+
+ // make sure that the text will fit
+ if( lstrlen( text ) > MAX_TAB_LABEL_LEN-1 )
+ return FALSE;
+
+ // make sure that the index is legal
+ if( index >= pData->total_tabs )
+ return FALSE;
+
+ // set the title
+ lstrcpy( pData->tab[index].label, text );
+
+ // we are changing the size of the tab, we will need to clean out behind
+ pData->fUpdate = TRUE;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// BookTab_OnGetItem()
+//
+// Retrieves a booktab title
+//
+// Input:
+// hwnd - our window handle
+// index - the tab to label
+// text - the buffer to fill with the tab title
+//
+// Returns:
+// a pointer to the filled buffer if successful, else NULL
+//
+// History
+// Arthur Brooking 8/06/93 created
+//////////////////////////////////////////////////////////////////////////////
+BOOL BookTab_OnGetItem( HWND hwnd, UINT index, LPTSTR text )
+{
+ LPTABDATA pData;
+
+ // get the instance data
+ pData = GetInstanceDataPtr( hwnd );
+
+ // make sure that the index is legal
+ if( index >= pData->total_tabs )
+ return FALSE;
+
+ // get the title
+ lstrcpy( text, pData->tab[index].label );
+ return( TRUE );
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// BookTab_OnSetCurSel()
+//
+// Sets the current selection
+//
+// Input:
+// hwnd - our window handle
+// newsel - the requested selection
+//
+// Returns:
+// the new current selection
+//
+// History
+// Arthur Brooking 8/06/93 created
+//////////////////////////////////////////////////////////////////////////////
+UINT BookTab_OnSetCurSel( HWND hwnd, UINT newsel )
+{
+ LPTABDATA pData;
+
+ // get the instance data
+ pData = GetInstanceDataPtr( hwnd );
+
+ // make sure that the requested selection is within the proper bounds
+ if( newsel >= pData->total_tabs )
+ return( pData->selected_tab );
+
+ // make sure that the selection actually changed
+ if( newsel != pData->selected_tab )
+ {
+ // set selection
+ pData->selected_tab = newsel;
+
+ // make us redraw
+ InvalidateRect( hwnd, NULL, FALSE );
+ UpdateWindow( hwnd );
+ }
+
+ return( pData->selected_tab );
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// BookTab_GetCurSel()
+//
+// Retrieves the current selection
+//
+// Input:
+// hwnd - our window handle
+//
+// Returns:
+// the current selection
+//
+// History
+// Arthur Brooking 8/06/93 created
+//////////////////////////////////////////////////////////////////////////////
+UINT BookTab_OnGetCurSel( HWND hwnd )
+{
+ LPTABDATA pData;
+
+ // get the instance data
+ pData = GetInstanceDataPtr( hwnd );
+
+ // get selection
+ return( pData->selected_tab );
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// BookTab_OnGetItemCount()
+//
+// Retrieves the number of tabs currently in use
+//
+// Input:
+// hwnd - our window handle
+//
+// Returns:
+// the number of tabs in use
+//
+// History
+// Arthur Brooking 8/06/93 created
+//////////////////////////////////////////////////////////////////////////////
+UINT BookTab_OnGetItemCount( HWND hwnd )
+{
+ LPTABDATA pData;
+
+ // get the instance data
+ pData = GetInstanceDataPtr( hwnd );
+
+ // get the number of tabs
+ return( pData->total_tabs );
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// BookTab_OnSetItemData()
+//
+// Adds a DWORD of data to the data structure for the given tab
+//
+// Input:
+// hwnd - our window handle
+// index - which tab to add data to
+// data - what to add
+//
+// Returns:
+// TRUE if succcessful, else FALSE
+//
+// History
+// Arthur Brooking 8/06/93 created
+//////////////////////////////////////////////////////////////////////////////
+BOOL BookTab_OnSetItemData( HWND hwnd, UINT index, DWORD data )
+{
+ LPTABDATA pData;
+
+ // get the instance data
+ pData = GetInstanceDataPtr( hwnd );
+
+ // set the instance data
+ pData->tab[index].data = data;
+
+ return TRUE;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// BookTab_OnPutInBack()
+//
+// Sets the focus to the booktab and then back to whoever had it first,
+// this seemes to put this control in the very back.
+//
+// Input:
+// hwnd - our window handle
+//
+// Returns:
+// <nothing>
+//
+// History
+// Arthur Brooking 1/21/93 created
+//////////////////////////////////////////////////////////////////////////////
+void BookTab_OnPutInBack( HWND hwnd )
+{
+ HWND hwndOldFocus;
+
+ // set the focus to us
+ hwndOldFocus = SetFocus( hwnd );
+
+ // if there was an old focus, set it back to that.
+ if( hwndOldFocus )
+ SetFocus( hwndOldFocus );
+
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// BookTab_OnGetItemData()
+//
+// Gets the DWORD of data stored in the data structure for the given tab
+//
+// Input:
+// hwnd - our window handle
+// index - which tab to get data from
+//
+// Returns:
+// the stored DWORD
+//
+// History
+// Arthur Brooking 8/06/93 created
+//////////////////////////////////////////////////////////////////////////////
+DWORD BookTab_OnGetItemData( HWND hwnd, UINT index)
+{
+ LPTABDATA pData;
+
+ // get the instance data
+ pData = GetInstanceDataPtr( hwnd );
+
+ // get the instance data
+ return( (DWORD)pData->tab[index].data );
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// IsPointInRect()
+//
+// determines if the point specifier is in the rectangle specified
+//
+// Input:
+// given_x - x coordinate of the point to be tested
+// given_y - y coordinate of the point to be tested
+// pTab - a pointer to the rectangle to test against
+//
+// Returns:
+// True if the point is within the rectangle, False if not
+//
+// History
+// Arthur Brooking 8/06/93 created
+//////////////////////////////////////////////////////////////////////////////
+BOOL IsPointInRect( int given_x, int given_y, LPRECT pRect )
+{
+ // is it above
+ if( given_y < pRect->top )
+ return FALSE;
+
+ // is it below
+ if( given_y > pRect->bottom )
+ return FALSE;
+
+ // is it to the left
+ if( given_x < pRect->left )
+ return FALSE;
+
+ // is it to the right
+ if( given_x > pRect->right )
+ return FALSE;
+
+ // well, it must be inside
+ return TRUE;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// BookTab_UpdateButtons()
+//
+// Takes the current data and updates the sizes of the tabs
+//
+// Input:
+// hwnd - our window handle
+//
+// Returns:
+// nuthin
+//
+// History
+// Arthur Brooking 8/06/93 created
+//////////////////////////////////////////////////////////////////////////////
+void BookTab_UpdateButtons( HWND hwnd )
+{
+ LPTABDATA pData;
+ HDC hdc;
+ SIZE cur_size;
+ RECT total_rect;
+ WORD yWidth;
+ WORD xWidth;
+ UINT left;
+ UINT i;
+ HFONT hfOldFont;
+
+
+ // get the instance data
+ pData = GetInstanceDataPtr( hwnd );
+
+ // preset this so that the MAXes later will work
+ pData->tabs_rect.bottom = 0;
+
+ xWidth = GetSystemMetrics(SM_CXBORDER);
+ yWidth = GetSystemMetrics(SM_CYBORDER);
+ GetClientRect( hwnd, &total_rect);
+ // BUGBUG cheat to see the whole thing
+ total_rect.bottom -= yWidth;
+ total_rect.right -= xWidth;
+
+ hdc = GetDC( hwnd );
+
+ // use the selected font (BOLD) to size the tabs
+ hfOldFont = SelectObject( hdc, pData->hfSelected );
+
+ // loop thru the tabs
+ left = total_rect.left;
+ for( i = 0; i < pData->total_tabs; i++ )
+ {
+ // get the size of the data for this tab
+ GetTextExtentPoint( hdc, pData->tab[i].label,
+ lstrlen( pData->tab[i].label), &cur_size);
+
+ // calculate the text rectatangle first ...
+ // the text top is down the size of the angle
+ pData->tab[i].text_rect.top = total_rect.top + ANGLE_Y*yWidth;
+
+ // the text left is over the size of the angle
+ pData->tab[i].text_rect.left = left + ANGLE_X*xWidth;
+
+ // the text bottom is down from the top the size of the text +
+ // 2x the fluff(top and bottom) + 2x the carat space
+ pData->tab[i].text_rect.bottom = pData->tab[i].text_rect.top +
+ cur_size.cy + 2*FLUFF_Y*yWidth + 2*CARAT_Y*yWidth;
+
+ // the text right is over from the left the size of the text +
+ // 2x the fluff(left and right) + 2x the carat space
+ pData->tab[i].text_rect.right = pData->tab[i].text_rect.left +
+ cur_size.cx + 2*FLUFF_X*xWidth + 2*CARAT_X*xWidth;
+
+
+ // then calculate the full tab rectangle
+ // the tab top is the top of the control
+ pData->tab[i].tab_rect.top = total_rect.top;
+
+ // the left side of the tab is next to the previous right
+ pData->tab[i].tab_rect.left = left;
+
+ // the tab bottom is down the footroom from the text bottom
+ pData->tab[i].tab_rect.bottom = pData->tab[i].text_rect.bottom +
+ FOOTROOM_Y*yWidth;
+
+ // the tab right is over the size of the angle from the text right
+ pData->tab[i].tab_rect.right = pData->tab[i].text_rect.right +
+ ANGLE_Y*yWidth;
+
+ // set the left for the next guy to be our right
+ left = pData->tab[i].tab_rect.right;
+
+ // set the bottom of the all tabs rectangle
+ pData->tabs_rect.bottom = max( pData->tabs_rect.bottom,
+ pData->tab[i].tab_rect.bottom);
+
+ // BUGBUG check for run off the side
+ }
+
+ // set the rest of the cumulative tabs rect
+ pData->tabs_rect.top = total_rect.top;
+ pData->tabs_rect.right = total_rect.right;
+ pData->tabs_rect.left = total_rect.left;
+ // BUGBUG why
+ pData->tabs_rect.bottom++;
+
+ // reset the font
+ SelectObject( hdc, hfOldFont );
+
+ // free up the resources used
+ ReleaseDC( hwnd, hdc );
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+// BookTab_()
+//
+//
+//
+// Input:
+// hwnd - our window handle
+//
+// Returns:
+//
+//
+// History
+// Arthur Brooking 8/06/93 created
+//////////////////////////////////////////////////////////////////////////////
+
diff --git a/private/nw/convert/nwconv/tab.h b/private/nw/convert/nwconv/tab.h
new file mode 100644
index 000000000..413c32623
--- /dev/null
+++ b/private/nw/convert/nwconv/tab.h
@@ -0,0 +1,71 @@
+//////////////////////////////////////////////////////////////////////////////
+// Book Tab Control Name
+//////////////////////////////////////////////////////////////////////////////
+#define BOOK_TAB_CONTROL TEXT("BOOKTAB")
+
+//////////////////////////////////////////////////////////////////////////////
+// Book Tab specific input messages
+//////////////////////////////////////////////////////////////////////////////
+//
+// message wParam lParam return action
+// ------- ------ ------ ------ ------
+// BT_ADDITEM -- pItem index adds tab at next avail. spot
+// BT_INSERTITEM index pItem index adds a new item at index
+// BT_DELETEITEM index -- <nothing> deletes the tab at index
+// BT_DELETEALLITEMS -- -- <nothing> deletes all tabs
+// BT_SETITEM index pItem succ/fail replaces text on tab
+// BT_GETITEM index pItem succ/fail retrieves text on tab
+// BT_SETCURSEL index -- succ/fail sets the current selection
+// BT_GETCURSEL -- -- index
+// BT_GETITEMCOUNT -- -- item count
+// BT_SETITEMDATA index data succ/fail store a DWORD with a tab
+// BT_GETITEMDATA index -- data retrieve the DWORD from a tab
+// BT_PUTINBACK -- -- <nothing> solves focus problem
+//
+//
+#define BT_BASE WM_USER + 777
+#define BT_ADDITEM BT_BASE + 0
+#define BT_INSERTITEM BT_BASE + 1
+#define BT_DELETEITEM BT_BASE + 2
+#define BT_DELETEALLITEMS BT_BASE + 3
+#define BT_SETITEM BT_BASE + 4
+#define BT_GETITEM BT_BASE + 5
+#define BT_SETCURSEL BT_BASE + 6
+#define BT_GETCURSEL BT_BASE + 7
+#define BT_GETITEMCOUNT BT_BASE + 8
+#define BT_SETITEMDATA BT_BASE + 9
+#define BT_GETITEMDATA BT_BASE + 10
+#define BT_PUTINBACK BT_BASE + 11
+
+//////////////////////////////////////////////////////////////////////////////
+// Book Tab specific notification messages
+//////////////////////////////////////////////////////////////////////////////
+// message wParam lParam meaning
+// ------- ------ ------ -------
+// BTN_SELCHANGE index my hwnd the current selection has changed
+// (please note that this notfification WILL NOT BE
+// SENT if the selection is changed by the
+// BT_SETCURSEL message).
+//
+#define BTN_SELCHANGE BT_BASE + 100
+
+//////////////////////////////////////////////////////////////////////////////
+// Book Tab functions for all to see
+//////////////////////////////////////////////////////////////////////////////
+void BookTab_Initialize(HINSTANCE hInstance);
+
+//////////////////////////////////////////////////////////////////////////////
+// Book Tab macros to make messages look like functions
+//////////////////////////////////////////////////////////////////////////////
+#define BookTab_AddItem(hwndCtl, text) ((UINT)(DWORD)SendMessage((hwndCtl), BT_ADDITEM, (WPARAM)(0), (LPARAM)(LPCTSTR)(text) ))
+#define BookTab_InsertItem(hwndCtl, index, text) ((UINT)(DWORD)SendMessage((hwndCtl), BT_INSERTITEM, (WPARAM)(UINT)(index), (LPARAM)(LPCTSTR)(text) ))
+#define BookTab_DeleteItem(hwndCtl, index) ((void)(DWORD)SendMessage((hwndCtl), BT_DELETEITEM, (WPARAM)(UINT)(index), (LPARAM)(0) ))
+#define BookTab_DeleteAllItems(hwndCtl) ((void)(DWORD)SendMessage((hwndCtl), BT_DELETEALLITEMS, (WPARAM)(0), (LPARAM)(0) ))
+#define BookTab_SetItem(hwndCtl, index, text) ((BOOL)(DWORD)SendMessage((hwndCtl), BT_SETITEM, (WPARAM)(UINT)(index), (LPARAM)(LPCTSTR)(text) ))
+#define BookTab_GetItem(hwndCtl, index, text) ((BOOL)(DWORD)SendMessage((hwndCtl), BT_GETITEM, (WPARAM)(UINT)(index), (LPARAM)(LPCTSTR)(text) ))
+#define BookTab_SetCurSel(hwndCtl, index) ((BOOL)(DWORD)SendMessage((hwndCtl), BT_SETCURSEL, (WPARAM)(UINT)(index), (LPARAM)(0) ))
+#define BookTab_GetCurSel(hwndCtl) ((UINT)(DWORD)SendMessage((hwndCtl), BT_GETCURSEL, (WPARAM)(0), (LPARAM)(0) ))
+#define BookTab_GetItemCount(hwndCtl) ((UINT)(DWORD)SendMessage((hwndCtl), BT_GETITEMCOUNT, (WPARAM)(0), (LPARAM)(0) ))
+#define BookTab_SetItemData(hwndCtl, index, data) ((BOOL)(DWORD)SendMessage((hwndCtl), BT_SETITEMDATA, (WPARAM)(UINT)(index), (LPARAM)(DWORD)(data) ))
+#define BookTab_GetItemData(hwndCtl, index) ((DWORD)(DWORD)SendMessage((hwndCtl), BT_GETITEMDATA, (WPARAM)(UINT)(index), (LPARAM)(0) ))
+#define BookTab_PutInBack(hwndCtl) ((DWORD)(DWORD)PostMessage((hwndCtl), BT_PUTINBACK, (WPARAM)(0), (LPARAM)(0) ))
diff --git a/private/nw/convert/nwconv/transfer.c b/private/nw/convert/nwconv/transfer.c
new file mode 100644
index 000000000..94f5d4350
--- /dev/null
+++ b/private/nw/convert/nwconv/transfer.c
@@ -0,0 +1,2381 @@
+/*
+ +-------------------------------------------------------------------------+
+ | Netware to Windows NT Transfer Loop |
+ +-------------------------------------------------------------------------+
+ | (c) Copyright 1993-1994 |
+ | Microsoft Corp. |
+ | All rights reserved |
+ | |
+ | Program : [Transfer.c] |
+ | Programmer : Arthur Hanson |
+ | Original Program Date : [Jul 27, 1993] |
+ | Last Update : [Jun 16, 1994] |
+ | |
+ | Version: 1.00 |
+ | |
+ | Description: |
+ | |
+ | History: |
+ | arth June 16, 1994 1.00 Original Version. |
+ | |
+ +-------------------------------------------------------------------------+
+*/
+
+
+#include "globals.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <time.h>
+
+#include "nwconv.h"
+#include "convapi.h"
+#include "ntnetapi.h"
+#include "nwnetapi.h"
+#include "userdlg.h"
+#include "statbox.h"
+#include "filedlg.h"
+#include "map.h"
+
+#define NWC_ERR_IGNORE 1
+#define NWC_ERR_NAMELONG 2
+#define NWC_ERR_DUPLICATE 3
+#define NWC_ERR_NAMEINVALID 4
+
+#define ILLEGAL_CHARS TEXT("\"\\/[]:;=,+*?<>")
+
+// define for routines in fcopy.c
+void ConvertFiles(HWND hDlg, BOOL TConversion, USER_LIST *Users, GROUP_LIST *Groups);
+void ConvertFilesInit(HWND hDlg);
+void VSharesCreate(DEST_SERVER_BUFFER *DServ, BOOL TConversion);
+
+// Cache of user and group lists
+typedef struct _LIST_CACHE {
+ struct _LIST_CACHE *next;
+ ULONG Count;
+ void *ul;
+} LIST_CACHE;
+
+static LIST_CACHE *UserCacheHead = NULL;
+static LIST_CACHE *GroupCacheHead = NULL;
+
+// Misc string holders
+static LPTSTR LocalName = NULL;
+static LPTSTR SourceServer, DestServer;
+TCHAR UserServerName[MAX_SERVER_NAME_LEN + 3];
+static TCHAR tmpStr[80];
+static TCHAR tmpStr2[60];
+static TCHAR ErrorText[256];
+static TCHAR NewName[256];
+static TCHAR pLine[256];
+
+static CONVERT_OPTIONS *ConvOpt = NULL;
+static FILE_OPTIONS *FileOptions = NULL;
+static BOOL TConversion;
+static BOOL WarningDlgForNTFS;
+
+// Totals for stat box
+static UINT TotErrors;
+static UINT TotGroups;
+static UINT TotUsers;
+static UINT TotConversions;
+UINT TotFiles;
+ULONG StartTime;
+ULONG CurrTime;
+
+// User and Group list pointers
+static USER_LIST *Users = NULL;
+static USER_LIST *NTUsers = NULL;
+static GROUP_LIST *Groups = NULL;
+static GROUP_LIST *NTGroups = NULL;
+static DWORD UserCount, NTUserCount;
+static DWORD GroupCount, NTGroupCount;
+static BOOL TransferCancel = FALSE;
+
+static MAP_FILE *hMap;
+
+// All of this is used for transfer lists in the conversion
+#define USER_SERVER 0
+#define USER_SERVER_PDC 1
+#define USER_SERVER_TRUSTED 2
+
+typedef struct _TRANSFER_BUFFER {
+ LPTSTR ServerName;
+ UINT UserServerType;
+ CONVERT_LIST *ConvertList;
+} TRANSFER_BUFFER;
+
+typedef struct _TRANSFER_LIST {
+ ULONG Count;
+ TRANSFER_BUFFER TList[];
+} TRANSFER_LIST;
+
+
+/*+-------------------------------------------------------------------------+
+ | ErrorIt()
+ |
+ +-------------------------------------------------------------------------+*/
+void ErrorIt(LPTSTR szFormat, ...) {
+ static TCHAR tmpStr[1024];
+ va_list marker;
+
+ va_start(marker, szFormat);
+
+ wvsprintf(tmpStr, szFormat, marker);
+#ifdef DEBUG
+ dprintf(TEXT("Errorit: %s\n"), tmpStr);
+#endif
+
+ TotErrors++;
+ Status_TotErrors(TotErrors);
+ LogWriteErr(TEXT("%s"),tmpStr);
+ va_end(marker);
+
+} // ErrorIt
+
+
+/*+-------------------------------------------------------------------------+
+ | NTFS Check Routines |
+ +-------------------------------------------------------------------------+*/
+
+/*+-------------------------------------------------------------------------+
+ | ShareListNTFSCheck()
+ |
+ +-------------------------------------------------------------------------+*/
+BOOL ShareListNTFSCheck() {
+ CONVERT_LIST *ConvList;
+ DEST_SERVER_BUFFER *DServ;
+ SOURCE_SERVER_BUFFER *SServ;
+ SHARE_LIST *ShareList;
+ SHARE_BUFFER *SList;
+ VIRTUAL_SHARE_BUFFER *VShare;
+ DRIVE_BUFFER *Drive;
+ ULONG i;
+ FILE_OPTIONS *FileOptions;
+
+ // Go through the convert list checking for any shares to non NTFS drives
+ ConvList = ConvertListStart;
+
+ while (ConvList != NULL) {
+ DServ = ConvList->FileServ;
+ SServ = ConvList->SourceServ;
+
+ FileOptions = (FILE_OPTIONS *) ConvList->FileOptions;
+ if (FileOptions->TransferFileInfo) {
+ ShareList = SServ->ShareList;
+
+ if (ShareList != NULL) {
+
+ SList = ShareList->SList;
+ for (i = 0; i < ShareList->Count; i++) {
+
+ // if not flagged as okay for going to fat, then must check
+ if (SList[i].Convert && !SList[i].ToFat)
+ if (SList[i].DestShare != NULL)
+ if (SList[i].Virtual) {
+ VShare = (VIRTUAL_SHARE_BUFFER *) SList[i].DestShare;
+ Drive = VShare->Drive;
+
+ if ((Drive == NULL) || (Drive->Type != DRIVE_TYPE_NTFS))
+ return FALSE;
+ } else {
+ Drive = SList[i].DestShare->Drive;
+
+ if ((Drive == NULL) || (Drive->Type != DRIVE_TYPE_NTFS))
+ return FALSE;
+ }
+ } // for loop through shares
+
+ }
+
+ } // if FileOptions
+
+ ConvList = ConvList->next;
+ }
+
+ return TRUE;
+
+} // ShareListNTFSCheck
+
+
+/*+-------------------------------------------------------------------------+
+ | ShareListNTFSListboxFill()
+ |
+ +-------------------------------------------------------------------------+*/
+void ShareListNTFSListboxFill(HWND hDlg) {
+ TCHAR AddLine[256];
+ CONVERT_LIST *ConvList;
+ DEST_SERVER_BUFFER *DServ;
+ SOURCE_SERVER_BUFFER *SServ;
+ SHARE_LIST *ShareList;
+ SHARE_BUFFER *SList;
+ VIRTUAL_SHARE_BUFFER *VShare;
+ DRIVE_BUFFER *Drive;
+ ULONG i;
+ HWND hCtrl;
+ FILE_OPTIONS *FileOptions;
+
+ // Go through the convert list checking for any shares to non NTFS drives
+ ConvList = ConvertListStart;
+ hCtrl = GetDlgItem(hDlg, IDC_LIST1);
+
+ while (ConvList != NULL) {
+ DServ = ConvList->FileServ;
+ SServ = ConvList->SourceServ;
+
+ FileOptions = (FILE_OPTIONS *) ConvList->FileOptions;
+ if (FileOptions->TransferFileInfo) {
+ ShareList = SServ->ShareList;
+
+ if (ShareList != NULL) {
+
+ SList = ShareList->SList;
+ for (i = 0; i < ShareList->Count; i++) {
+
+ // if not flagged as okay for going to fat, then must check
+ if (SList[i].Convert && !SList[i].ToFat)
+ if (SList[i].DestShare != NULL)
+ if (SList[i].Virtual) {
+ VShare = (VIRTUAL_SHARE_BUFFER *) SList[i].DestShare;
+ Drive = VShare->Drive;
+
+ if ((Drive == NULL) || (Drive->Type != DRIVE_TYPE_NTFS)) {
+ wsprintf(AddLine, TEXT("%s\\%s: -> \\\\%s\\%s"), SServ->Name, SList[i].Name, DServ->Name, VShare->Name);
+ SendMessage(hCtrl, LB_ADDSTRING, (WPARAM) 0, (LPARAM) AddLine);
+ }
+ } else {
+ Drive = SList[i].DestShare->Drive;
+
+ if ((Drive == NULL) || (Drive->Type != DRIVE_TYPE_NTFS)) {
+ wsprintf(AddLine, TEXT("%s\\%s: -> \\\\%s\\%s"), SServ->Name, SList[i].Name, DServ->Name, SList[i].DestShare->Name);
+ SendMessage(hCtrl, LB_ADDSTRING, (WPARAM) 0, (LPARAM) AddLine);
+ }
+ }
+ } // for loop through shares
+
+ }
+ } // if FileOptions
+
+ ConvList = ConvList->next;
+ }
+
+} // ShareListNTFSListboxFill
+
+
+/*+-------------------------------------------------------------------------+
+ | ShareListFATOK()
+ |
+ +-------------------------------------------------------------------------+*/
+void ShareListFATOK() {
+ CONVERT_LIST *ConvList;
+ DEST_SERVER_BUFFER *DServ;
+ SOURCE_SERVER_BUFFER *SServ;
+ SHARE_LIST *ShareList;
+ SHARE_BUFFER *SList;
+ ULONG i;
+ FILE_OPTIONS *FileOptions;
+
+ // Go through the convert list checking for any shares to non NTFS drives
+ ConvList = ConvertListStart;
+
+ while (ConvList != NULL) {
+ DServ = ConvList->FileServ;
+ SServ = ConvList->SourceServ;
+
+ FileOptions = (FILE_OPTIONS *) ConvList->FileOptions;
+ if (FileOptions->TransferFileInfo) {
+ ShareList = SServ->ShareList;
+
+ if (ShareList != NULL) {
+
+ SList = ShareList->SList;
+ for (i = 0; i < ShareList->Count; i++) {
+ if (SList[i].Convert)
+ SList[i].ToFat = TRUE;
+ }
+
+ }
+ } // if FileOptions
+
+ ConvList = ConvList->next;
+ }
+
+} // ShareListFATOK
+
+
+/*+-------------------------------------------------------------------------+
+ | Space Check Routines |
+ +-------------------------------------------------------------------------+*/
+
+/*+-------------------------------------------------------------------------+
+ | ShareListSpaceCheck()
+ |
+ +-------------------------------------------------------------------------+*/
+BOOL ShareListSpaceCheck() {
+ CONVERT_LIST *ConvList;
+ DEST_SERVER_BUFFER *DServ;
+ SOURCE_SERVER_BUFFER *SServ;
+ SHARE_LIST *ShareList;
+ SHARE_BUFFER *SList;
+ VIRTUAL_SHARE_BUFFER *VShare;
+ DRIVE_BUFFER *Drive;
+ ULONG i;
+ FILE_OPTIONS *FileOptions;
+
+ // Go through the convert list checking for any shares to non NTFS drives
+ ConvList = ConvertListStart;
+
+ while (ConvList != NULL) {
+ DServ = ConvList->FileServ;
+ SServ = ConvList->SourceServ;
+
+ FileOptions = (FILE_OPTIONS *) ConvList->FileOptions;
+ if (FileOptions->TransferFileInfo) {
+ ShareList = SServ->ShareList;
+
+ if (ShareList != NULL) {
+
+ SList = ShareList->SList;
+ for (i = 0; i < ShareList->Count; i++) {
+
+ if (SList[i].Convert && (SList[i].DestShare != NULL))
+ if (SList[i].Virtual) {
+ VShare = (VIRTUAL_SHARE_BUFFER *) SList[i].DestShare;
+ Drive = VShare->Drive;
+
+ if ((Drive == NULL) || (Drive->AllocSpace > Drive->FreeSpace))
+ return FALSE;
+ } else {
+ Drive = SList[i].DestShare->Drive;
+
+ if ((Drive == NULL) || (Drive->AllocSpace > Drive->FreeSpace))
+ return FALSE;
+ }
+ } // for loop through shares
+
+ }
+ } // if FileOptions
+
+ ConvList = ConvList->next;
+ }
+
+ return TRUE;
+
+} // ShareListSpaceCheck
+
+
+/*+-------------------------------------------------------------------------+
+ | ShareListSpaceListboxFill()
+ |
+ +-------------------------------------------------------------------------+*/
+void ShareListSpaceListboxFill(HWND hDlg) {
+ TCHAR AddLine[256];
+ TCHAR Free[25];
+ CONVERT_LIST *ConvList;
+ DEST_SERVER_BUFFER *DServ;
+ SOURCE_SERVER_BUFFER *SServ;
+ SHARE_LIST *ShareList;
+ SHARE_BUFFER *SList;
+ VIRTUAL_SHARE_BUFFER *VShare;
+ DRIVE_BUFFER *Drive;
+ ULONG i;
+ HWND hCtrl;
+ FILE_OPTIONS *FileOptions;
+
+ // Go through the convert list checking for any shares to non NTFS drives
+ ConvList = ConvertListStart;
+ hCtrl = GetDlgItem(hDlg, IDC_LIST1);
+
+ while (ConvList != NULL) {
+ DServ = ConvList->FileServ;
+ SServ = ConvList->SourceServ;
+
+ FileOptions = (FILE_OPTIONS *) ConvList->FileOptions;
+ if (FileOptions->TransferFileInfo) {
+ ShareList = SServ->ShareList;
+
+ if (ShareList != NULL) {
+
+ SList = ShareList->SList;
+ for (i = 0; i < ShareList->Count; i++) {
+
+ if (SList[i].Convert && (SList[i].DestShare != NULL))
+ if (SList[i].Virtual) {
+ VShare = (VIRTUAL_SHARE_BUFFER *) SList[i].DestShare;
+ Drive = VShare->Drive;
+
+ if (Drive != NULL) {
+ if (Drive->AllocSpace > Drive->FreeSpace) {
+ // List shares then space
+ wsprintf(AddLine, TEXT("%s\\%s: -> \\\\%s\\%s"), SServ->Name, SList[i].Name, DServ->Name, VShare->Name);
+ SendMessage(hCtrl, LB_ADDSTRING, (WPARAM) 0, (LPARAM) AddLine);
+
+ lstrcpy(Free, lToStr(Drive->FreeSpace));
+ wsprintf(AddLine, Lids(IDS_D_13), Free, lToStr(Drive->AllocSpace));
+ SendMessage(hCtrl, LB_ADDSTRING, (WPARAM) 0, (LPARAM) AddLine);
+ }
+ } else {
+ wsprintf(AddLine, TEXT("%s\\%s: -> \\\\%s\\%s"), SServ->Name, SList[i].Name, DServ->Name, VShare->Name);
+ SendMessage(hCtrl, LB_ADDSTRING, (WPARAM) 0, (LPARAM) AddLine);
+
+ wsprintf(AddLine, Lids(IDS_D_14));
+ SendMessage(hCtrl, LB_ADDSTRING, (WPARAM) 0, (LPARAM) AddLine);
+ }
+ } else {
+ Drive = SList[i].DestShare->Drive;
+
+ if (Drive != NULL) {
+ if (Drive->AllocSpace > Drive->FreeSpace) {
+ // List shares then space
+ wsprintf(AddLine, TEXT("%s\\%s: -> \\\\%s\\%s"), SServ->Name, SList[i].Name, DServ->Name, SList[i].DestShare->Name);
+ SendMessage(hCtrl, LB_ADDSTRING, (WPARAM) 0, (LPARAM) AddLine);
+
+ lstrcpy(Free, lToStr(Drive->FreeSpace));
+ wsprintf(AddLine, Lids(IDS_D_13), Free, lToStr(Drive->AllocSpace));
+ SendMessage(hCtrl, LB_ADDSTRING, (WPARAM) 0, (LPARAM) AddLine);
+ }
+ } else {
+ wsprintf(AddLine, TEXT("%s\\%s: -> \\\\%s\\%s"), SServ->Name, SList[i].Name, DServ->Name, SList[i].DestShare->Name);
+ SendMessage(hCtrl, LB_ADDSTRING, (WPARAM) 0, (LPARAM) AddLine);
+
+ wsprintf(AddLine, Lids(IDS_D_14));
+ SendMessage(hCtrl, LB_ADDSTRING, (WPARAM) 0, (LPARAM) AddLine);
+ }
+ } // if Virtual
+
+ } // for loop through shares
+
+ }
+ } // if FileOptions
+
+ ConvList = ConvList->next;
+ }
+
+} // ShareListSpaceListboxFill
+
+
+/*+-------------------------------------------------------------------------+
+ | Conversion Warning Dialog |
+ +-------------------------------------------------------------------------+*/
+
+/*+-------------------------------------------------------------------------+
+ | DlgConversionWarning()
+ |
+ +-------------------------------------------------------------------------+*/
+LRESULT CALLBACK DlgConversionWarning(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
+ int wmId, wmEvent;
+
+ switch (message) {
+ case WM_INITDIALOG:
+ // Center the dialog over the application window
+ CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
+
+ if (WarningDlgForNTFS) {
+ SendDlgItemMessage(hDlg, IDC_PANEL1, WM_SETTEXT, 0, (LPARAM) Lids(IDS_D_15));
+ ShareListNTFSListboxFill(hDlg);
+ } else {
+ SendDlgItemMessage(hDlg, IDC_PANEL1, WM_SETTEXT, 0, (LPARAM) Lids(IDS_D_16));
+ ShareListSpaceListboxFill(hDlg);
+ }
+
+ return (TRUE);
+
+ case WM_COMMAND:
+ wmId = LOWORD(wParam);
+ wmEvent = HIWORD(wParam);
+
+ switch (wmId) {
+ case IDYES:
+ if (WarningDlgForNTFS)
+ ShareListFATOK();
+
+ EndDialog(hDlg, 0);
+ return (TRUE);
+ break;
+
+ case IDNO:
+ TransferCancel = TRUE;
+ EndDialog(hDlg, 0);
+ return (TRUE);
+ break;
+
+ }
+
+ break;
+ }
+
+ return (FALSE); // Didn't process the message
+
+ lParam;
+} // DlgConversionWarning
+
+
+/*+-------------------------------------------------------------------------+
+ | ConversionWarningDlg_Do()
+ |
+ +-------------------------------------------------------------------------+*/
+void ConversionWarningDlg_Do(HWND hDlg) {
+ DLGPROC lpfnDlg;
+
+ lpfnDlg = MakeProcInstance((DLGPROC) DlgConversionWarning, hInst);
+ DialogBox(hInst, TEXT("AlertSel"), hDlg, lpfnDlg) ;
+ FreeProcInstance(lpfnDlg);
+
+} // ConversionWarningDlg_Do
+
+
+/*+-------------------------------------------------------------------------+
+ | NTFSCheck()
+ |
+ +-------------------------------------------------------------------------+*/
+void NTFSCheck(HWND hDlg) {
+ WarningDlgForNTFS = TRUE;
+
+ if (!ShareListNTFSCheck())
+ ConversionWarningDlg_Do(hDlg);
+
+} // NTFSCheck
+
+
+/*+-------------------------------------------------------------------------+
+ | SpaceCheck()
+ |
+ +-------------------------------------------------------------------------+*/
+void SpaceCheck(HWND hDlg) {
+ WarningDlgForNTFS = FALSE;
+
+ if (!ShareListSpaceCheck())
+ ConversionWarningDlg_Do(hDlg);
+
+} // SpaceCheck
+
+
+
+/*+-------------------------------------------------------------------------+
+ | End of Conversion Dialog |
+ +-------------------------------------------------------------------------+*/
+
+/*+-------------------------------------------------------------------------+
+ | DlgConversionEnd()
+ |
+ +-------------------------------------------------------------------------+*/
+LRESULT CALLBACK DlgConversionEnd(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
+ HANDLE hFile;
+ int wmId, wmEvent;
+ static char CmdLine[256];
+ HWND hCtrl;
+
+ switch (message) {
+ case WM_INITDIALOG:
+ // Center the dialog over the application window
+ CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
+
+ hCtrl = GetDlgItem(hDlg, IDC_VIEWLOG);
+
+ // check if logfile exists, if it does allow log file viewing...
+ hFile = CreateFileA( "LogFile.LOG", GENERIC_READ, 0, NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (hFile != (HANDLE) INVALID_HANDLE_VALUE)
+ CloseHandle( hFile );
+ else
+ EnableWindow(hCtrl, FALSE);
+
+ SendDlgItemMessage(hDlg, IDC_S_TOT_COMP, WM_SETTEXT, 0, (LPARAM) lToStr(TotConversions));
+ SendDlgItemMessage(hDlg, IDC_S_TOT_GROUPS, WM_SETTEXT, 0, (LPARAM) lToStr(TotGroups));
+ SendDlgItemMessage(hDlg, IDC_S_TOT_USERS, WM_SETTEXT, 0, (LPARAM) lToStr(TotUsers));
+ SendDlgItemMessage(hDlg, IDC_S_TOT_FILES, WM_SETTEXT, 0, (LPARAM) lToStr(TotFiles));
+ SendDlgItemMessage(hDlg, IDC_S_TOT_ERRORS, WM_SETTEXT, 0, (LPARAM) lToStr(TotErrors));
+
+ if (TransferCancel)
+ SendMessage(hDlg, WM_SETTEXT, (WPARAM) 0, (LPARAM) Lids(IDS_D_17));
+
+ return (TRUE);
+
+ case WM_COMMAND:
+ wmId = LOWORD(wParam);
+ wmEvent = HIWORD(wParam);
+
+ switch (wmId) {
+ case IDOK:
+ case IDCANCEL:
+ EndDialog(hDlg, 0);
+ return (TRUE);
+ break;
+
+ case IDC_VIEWLOG:
+ lstrcpyA(CmdLine, "LogView ");
+ lstrcatA(CmdLine, "Error.LOG Summary.LOG LogFile.LOG");
+ WinExec(CmdLine, SW_SHOW);
+ return (TRUE);
+ break;
+
+ }
+
+ break;
+ }
+
+ return (FALSE); // Didn't process the message
+
+ lParam;
+} // DlgConversionEnd
+
+
+/*+-------------------------------------------------------------------------+
+ | ConversionEndDlg_Do()
+ |
+ +-------------------------------------------------------------------------+*/
+void ConversionEndDlg_Do(HWND hDlg) {
+ DLGPROC lpfnDlg;
+
+ lpfnDlg = MakeProcInstance((DLGPROC) DlgConversionEnd, hInst);
+ DialogBox(hInst, TEXT("ConversionEnd"), hDlg, lpfnDlg) ;
+ FreeProcInstance(lpfnDlg);
+
+} // ConversionEndDlg_Do
+
+
+
+/*+-------------------------------------------------------------------------+
+ | User / Group lists and Cache routines |
+ +-------------------------------------------------------------------------+*/
+
+/*+-------------------------------------------------------------------------+
+ | FindUserMatch()
+ |
+ | Searches through the user list using a binary search. Returns a
+ | pointer to the found user record or NULL if no match.
+ |
+ +-------------------------------------------------------------------------+*/
+USER_BUFFER *FindUserMatch(LPTSTR Name, USER_LIST *UserList, BOOL NewName) {
+ LONG begin = 0;
+ LONG end;
+ LONG cur;
+ int match;
+ USER_BUFFER *UserBuffer;
+
+ if (UserList == NULL)
+ return NULL;
+
+ UserBuffer = UserList->UserBuffer;
+ end = UserList->Count - 1;
+
+ while (end >= begin) {
+ // go halfway in-between
+ cur = (begin + end) / 2;
+
+ // compare the two result into match
+ if (NewName)
+ match = lstrcmpi(Name, UserBuffer[cur].NewName);
+ else
+ match = lstrcmpi(Name, UserBuffer[cur].Name);
+
+ if (match < 0)
+ // move new begin
+ end = cur - 1;
+ else
+ begin = cur + 1;
+
+ if (match == 0)
+ return &UserBuffer[cur];
+ }
+
+ return NULL;
+
+} // FindUserMatch
+
+
+/*+-------------------------------------------------------------------------+
+ | FindGroupMatch()
+ |
+ | Searches through the group list using a binary search. Returns a
+ | pointer to the found group record or NULL if no match.
+ |
+ +-------------------------------------------------------------------------+*/
+GROUP_BUFFER *FindGroupMatch(LPTSTR Name, GROUP_LIST *GroupList, BOOL NewName) {
+ LONG begin = 0;
+ LONG end;
+ LONG cur;
+ int match;
+ GROUP_BUFFER *GroupBuffer;
+
+ if (GroupList == NULL)
+ return NULL;
+
+ GroupBuffer = GroupList->GroupBuffer;
+ end = GroupList->Count - 1;
+
+ while (end >= begin) {
+ // go halfway in-between
+ cur = (begin + end) / 2;
+
+ // compare the two result into match
+ if (NewName)
+ match = lstrcmpi(Name, GroupBuffer[cur].NewName);
+ else
+ match = lstrcmpi(Name, GroupBuffer[cur].Name);
+
+ if (match < 0)
+ // move new begin
+ end = cur - 1;
+ else
+ begin = cur + 1;
+
+ if (match == 0)
+ return &GroupBuffer[cur];
+ }
+
+ return NULL;
+
+} // FindGroupMatch
+
+
+/*+-------------------------------------------------------------------------+
+ | The List Cache's are a linked list of previously converted user and
+ | group lists. This is mostly for when running trial conversions you can
+ | check if a previously transferred name conflicts with a new name (since
+ | the name won't actually be out on the destination server).
+ |
+ | The Cache is kept around while working with the same destination server
+ | or domain. Once a new domain/server is selected the cache and all lists
+ | should be freed.
+ +-------------------------------------------------------------------------+*/
+
+/*+-------------------------------------------------------------------------+
+ | ListCachePut()
+ |
+ +-------------------------------------------------------------------------+*/
+BOOL ListCachePut(LIST_CACHE **CacheHead, void *ul, ULONG Count) {
+ LIST_CACHE *cc;
+ LIST_CACHE *pc;
+
+ cc = (LIST_CACHE *) AllocMemory(sizeof(LIST_CACHE));
+
+ if (cc == NULL)
+ return FALSE;
+
+ // Init the cache entry
+ cc->next = NULL;
+ cc->Count = Count;
+ cc->ul = ul;
+
+ // Now put it at the end of the chain
+ if (*CacheHead == NULL) {
+ *CacheHead = cc;
+ return TRUE;
+ }
+
+ pc = *CacheHead;
+ while (pc->next)
+ pc = pc->next;
+
+ pc->next = cc;
+ return TRUE;
+
+} // ListCachePut
+
+
+/*+-------------------------------------------------------------------------+
+ | ListCacheFree()
+ |
+ +-------------------------------------------------------------------------+*/
+void ListCacheFree(LIST_CACHE **CacheHead) {
+ LIST_CACHE *cc;
+ LIST_CACHE *pc;
+
+ cc = *CacheHead;
+
+ while (cc) {
+ // Free the user list attached to this cache entry
+ FreeMemory(cc->ul);
+
+ // Save next cache entry
+ pc = cc->next;
+
+ // Free up the cache entry and loop
+ FreeMemory(cc);
+ cc = pc;
+ }
+
+ *CacheHead = NULL;
+
+} // ListCacheFree
+
+
+/*+-------------------------------------------------------------------------+
+ | UserCacheMatch()
+ |
+ +-------------------------------------------------------------------------+*/
+BOOL UserCacheMatch(LPTSTR UserName) {
+ LIST_CACHE *cc;
+ BOOL match = FALSE;
+
+ cc = UserCacheHead;
+
+ // loop through the cache entries and try to match with each user list
+ while (cc && !match) {
+ if (FindUserMatch(UserName, (USER_LIST *) cc->ul, TRUE))
+ match = TRUE;
+ else
+ cc = cc->next;
+ }
+
+ return match;
+
+} // UserCacheMatch
+
+
+/*+-------------------------------------------------------------------------+
+ | GroupCacheMatch()
+ |
+ +-------------------------------------------------------------------------+*/
+BOOL GroupCacheMatch(LPTSTR GroupName) {
+ LIST_CACHE *cc;
+ BOOL match = FALSE;
+
+ cc = GroupCacheHead;
+
+ // loop through the cache entries and try to match with each user list
+ while (cc && !match) {
+ if (FindGroupMatch(GroupName, (GROUP_LIST *) cc->ul, TRUE))
+ match = TRUE;
+ else
+ cc = cc->next;
+ }
+
+ return match;
+
+} // GroupCacheMatch
+
+
+/*+-------------------------------------------------------------------------+
+ | Logging Garbage |
+ +-------------------------------------------------------------------------+*/
+
+/*+-------------------------------------------------------------------------+
+ | ConvertOptionsLog()
+ |
+ | Writes all the admin selected conversion options to the log file.
+ |
+ +-------------------------------------------------------------------------+*/
+void ConvertOptionsLog() {
+
+ LogWriteLog(0, Lids(IDS_L_136));
+
+ if (ConvOpt->TransferUserInfo)
+ LogWriteLog(1, Lids(IDS_L_137), Lids(IDS_YES));
+ else
+ LogWriteLog(1, Lids(IDS_L_137), Lids(IDS_NO));
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+
+ if (ConvOpt->TransferUserInfo) {
+ LogWriteLog(1, Lids(IDS_L_140));
+
+ if (ConvOpt->UseMappingFile) {
+ LogWriteLog(2, Lids(IDS_L_138), Lids(IDS_YES));
+ LogWriteLog(3, Lids(IDS_L_139), ConvOpt->MappingFile);
+ } else {
+ LogWriteLog(2, Lids(IDS_L_138), Lids(IDS_NO));
+
+ // Password Options
+ LogWriteLog(2, Lids(IDS_L_141));
+ switch (ConvOpt->PasswordOption) {
+ case 0: // Use NULL
+ LogWriteLog(0, Lids(IDS_L_142));
+ break;
+
+ case 1: // Password is username
+ LogWriteLog(0, Lids(IDS_L_143));
+ break;
+
+ case 2: // Use constant
+ LogWriteLog(0, Lids(IDS_L_144), ConvOpt->PasswordConstant);
+ break;
+ }
+ }
+
+ if (ConvOpt->ForcePasswordChange)
+ LogWriteLog(3, Lids(IDS_L_145), Lids(IDS_YES));
+ else
+ LogWriteLog(3, Lids(IDS_L_145), Lids(IDS_NO));
+
+ if (!ConvOpt->UseMappingFile) {
+ // User Names Options
+ LogWriteLog(2, Lids(IDS_L_146));
+
+ switch (ConvOpt->UserNameOption) {
+ case 0: // Don't transfer - log failures
+ LogWriteLog(0, Lids(IDS_L_148));
+ break;
+
+ case 1: // Ignore
+ LogWriteLog(0, Lids(IDS_L_149));
+ break;
+
+ case 2: // Overwrite with new info
+ LogWriteLog(0, Lids(IDS_L_151));
+ break;
+
+ case 3: // Pre-Pend constant
+ LogWriteLog(0, Lids(IDS_L_150), ConvOpt->UserConstant);
+ break;
+ }
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+
+ // Group Names Options
+ LogWriteLog(2, Lids(IDS_L_147));
+
+ switch (ConvOpt->GroupNameOption) {
+ case 0: // Don't transfer - log failures
+ LogWriteLog(0, Lids(IDS_L_148));
+ break;
+
+ case 1: // Overwrite with new info
+ LogWriteLog(0, Lids(IDS_L_149));
+ break;
+
+ case 2: // Pre-Pend constant
+ LogWriteLog(0, Lids(IDS_L_150), ConvOpt->GroupConstant);
+ break;
+ }
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+ }
+
+ if (ConvOpt->SupervisorDefaults)
+ LogWriteLog(2, Lids(IDS_L_152), Lids(IDS_YES));
+ else
+ LogWriteLog(2, Lids(IDS_L_152), Lids(IDS_NO));
+
+ if (ConvOpt->AdminAccounts)
+ LogWriteLog(2, Lids(IDS_L_153), Lids(IDS_YES));
+ else
+ LogWriteLog(2, Lids(IDS_L_153), Lids(IDS_NO));
+
+ if (ConvOpt->UseTrustedDomain && (ConvOpt->TrustedDomain != NULL))
+ LogWriteLog(2, Lids(IDS_L_154), ConvOpt->TrustedDomain->Name);
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+ }
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+
+} // ConvertOptionsLog
+
+
+/*+-------------------------------------------------------------------------+
+ | OptionsFileLog()
+ |
+ +-------------------------------------------------------------------------+*/
+void OptionsFileLog() {
+ LogWriteLog(0, Lids(IDS_L_155));
+
+ if (FileOptions->TransferFileInfo)
+ LogWriteLog(1, Lids(IDS_L_156), Lids(IDS_YES));
+ else
+ LogWriteLog(1, Lids(IDS_L_156), Lids(IDS_NO));
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+} // OptionsFileLog
+
+
+/*+-------------------------------------------------------------------------+
+ | ConvertPairLog()
+ |
+ +-------------------------------------------------------------------------+*/
+void ConvertPairLog() {
+ LogWriteLog(0, Lids(IDS_LINE));
+ wsprintf(tmpStr, Lids(IDS_L_157), SourceServer);
+ LogWriteLog(0, Lids(IDS_BRACE), tmpStr);
+
+ wsprintf(tmpStr, Lids(IDS_L_158), DestServer);
+ LogWriteLog(0, Lids(IDS_BRACE), tmpStr);
+
+ LogWriteLog(0, Lids(IDS_LINE));
+ GetTime(tmpStr2);
+ wsprintf(tmpStr, Lids(IDS_L_159), tmpStr2);
+ LogWriteLog(0, Lids(IDS_BRACE), tmpStr);
+ LogWriteLog(0, Lids(IDS_LINE));
+ LogWriteLog(0, Lids(IDS_CRLF));
+
+ LogWriteSummary(0, Lids(IDS_CRLF));
+ LogWriteSummary(0, TEXT("[%s -> %s]\r\n"), SourceServer, DestServer);
+ ErrorContextSet(TEXT("[%s -> %s]\r\n"), SourceServer, DestServer);
+
+} // ConvertPairLog
+
+
+/*+-------------------------------------------------------------------------+
+ | UsersLogNames()
+ |
+ +-------------------------------------------------------------------------+*/
+void UsersLogNames(USER_LIST *Users) {
+ ULONG i;
+ DWORD UserCount;
+ USER_BUFFER *UserBuffer;
+
+ if (Users == NULL)
+ return;
+
+ UserCount = Users->Count;
+ UserBuffer = Users->UserBuffer;
+
+ if (UserCount) {
+ LogWriteLog(1, Lids(IDS_L_160));
+ LogWriteLog(1, Lids(IDS_L_161));
+
+ // Check Mapping File
+ for (i = 0; i < UserCount; i++) {
+ if (UserBuffer[i].IsNewName)
+ wsprintf(pLine, TEXT("%s -> %s"), UserBuffer[i].Name, UserBuffer[i].NewName);
+ else
+ wsprintf(pLine, TEXT("%s"), UserBuffer[i].NewName);
+
+ LogWriteLog(1, TEXT(" %-50s"), pLine);
+
+ if (UserBuffer[i].err) {
+ if (UserBuffer[i].err == NWC_ERR_NAMELONG)
+ LogWriteLog(0, Lids(IDS_L_162));
+
+ if (UserBuffer[i].err == NWC_ERR_DUPLICATE)
+ LogWriteLog(0, Lids(IDS_L_163));
+
+ if (UserBuffer[i].err == NWC_ERR_NAMEINVALID)
+ LogWriteLog(0, Lids(IDS_L_164));
+
+ }
+
+ // Need to check this seperatly - as it's not an error
+ if (UserBuffer[i].Overwrite)
+ LogWriteLog(0, Lids(IDS_L_163));
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+
+ }
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+ }
+
+} // UsersLogNames
+
+
+/*+-------------------------------------------------------------------------+
+ | UserNewName_Check()
+ |
+ +-------------------------------------------------------------------------+*/
+void UserNewName_Check(USER_BUFFER *Users) {
+ // We have done any mappings that need to be done, now check for
+ // name validity if there is a new name...
+ if (Users->IsNewName)
+ if (UserCacheMatch(Users->NewName)) {
+ Users->err = NWC_ERR_DUPLICATE;
+ }
+
+ if (lstrlen(Users->NewName) > MAX_NT_USER_NAME_LEN) {
+ // Name is too long
+ Users->err = NWC_ERR_NAMELONG;
+ }
+
+ // Check if a valid name (no illegal characters)...
+ if ((int) wcscspn(Users->NewName, ILLEGAL_CHARS) < (int) lstrlen(Users->NewName))
+ Users->err = NWC_ERR_NAMEINVALID;
+
+} // UserNewName_Check
+
+
+/*+-------------------------------------------------------------------------+
+ | UserNames_Resolve()
+ |
+ +-------------------------------------------------------------------------+*/
+void UserNames_Resolve(USER_BUFFER *Users) {
+ LPTSTR TheName;
+ LPTSTR ErrorText;
+ ULONG RetType;
+
+ // Figure out which name to use
+ if (Users->IsNewName)
+ TheName = Users->Name;
+ else
+ TheName = Users->NewName;
+
+ // If using mapping file then map the name appropriatly
+ if (ConvOpt->UseMappingFile) {
+ if (UserCacheMatch(TheName))
+ Users->err = NWC_ERR_DUPLICATE;
+ } else {
+ // check if the user name is in the destination list (duplicate)
+ if (UserCacheMatch(TheName)) {
+ // There was - so figure out based on conversion options what
+ // to do with it...
+ switch (ConvOpt->UserNameOption) {
+ case 0: // Log Errors
+ Users->err = NWC_ERR_DUPLICATE;
+ break;
+
+ case 1: // ignore
+ Users->err = NWC_ERR_IGNORE;
+ break;
+
+ case 2: // Overwrite
+ Users->Overwrite = TRUE;
+ break;
+
+ case 3: // Pre-Pend constant
+ lstrcpy(NewName, ConvOpt->UserConstant);
+ lstrcat(NewName, Users->Name);
+ lstrcpy(Users->NewName, NewName);
+ Users->IsNewName = TRUE;
+ break;
+ } // switch
+ }
+ }
+
+ do {
+ RetType = IDIGNORE;
+ UserNewName_Check(Users);
+
+ if (Users->err && (Users->err != NWC_ERR_IGNORE) && PopupOnError()) {
+ switch(Users->err) {
+ case NWC_ERR_NAMELONG:
+ ErrorText = Lids(IDS_L_165);
+ break;
+
+ case NWC_ERR_DUPLICATE:
+ ErrorText = Lids(IDS_L_166);
+ break;
+
+ case NWC_ERR_NAMEINVALID:
+ ErrorText = Lids(IDS_L_167);
+ break;
+
+ }
+
+ RetType = UserNameErrorDlg_Do(Lids(IDS_L_168), ErrorText, Users);
+ }
+ } while (RetType == IDRETRY);
+
+ if (RetType == IDABORT)
+ TransferCancel = TRUE;
+
+} // UserNames_Resolve
+
+
+/*+-------------------------------------------------------------------------+
+ | UserSave()
+ |
+ | Given a user-name, moves their account information from the source
+ | to the destination server.
+ |
+ +-------------------------------------------------------------------------+*/
+BOOL UserSave(BOOL *NWInfo, LPTSTR origUserName, LPTSTR newUserName, NT_USER_INFO *NTInfo, void **OldInfo, BOOL Replace) {
+ NET_API_STATUS ret = 0;
+ void *NWUInfo;
+ FPNW_INFO fpnw;
+ static TCHAR ServerName[MAX_SERVER_NAME_LEN+3]; // +3 for leading slashes and ending NULL
+
+ *NWInfo = FALSE;
+
+ // Overlay NetWare info into record
+ NWUserInfoGet(origUserName, OldInfo);
+ NWUInfo = *OldInfo;
+
+ if (NWUInfo == NULL)
+ return FALSE;
+
+ *NWInfo = TRUE;
+ NWNetUserMapInfo(origUserName, NWUInfo, NTInfo);
+
+ // Force user to change password if required
+ if (ConvOpt->ForcePasswordChange)
+ NTInfo->password_expired = (DWORD) (-1L);
+
+ // Now get/map special FPNW info
+ if (ConvOpt->NetWareInfo) {
+ NWFPNWMapInfo(NWUInfo, &fpnw);
+ }
+
+ if (!TConversion)
+ if (Replace)
+ if (ConvOpt->NetWareInfo)
+ ret = NTUserInfoSet(NTInfo, &fpnw);
+ else
+ ret = NTUserInfoSet(NTInfo, NULL);
+ else
+ if (ConvOpt->NetWareInfo)
+ ret = NTUserInfoSave(NTInfo, &fpnw);
+ else
+ ret = NTUserInfoSave(NTInfo, NULL);
+
+ if (ret)
+ return FALSE;
+ else {
+ // now save out FPNW Info
+ if (ConvOpt->NetWareInfo)
+ NTSAMParmsSet(newUserName, fpnw, NTInfo->password, ConvOpt->ForcePasswordChange);
+
+ return TRUE;
+ }
+
+} // UserSave
+
+
+/*+-------------------------------------------------------------------------+
+ | UsersConvert()
+ |
+ +-------------------------------------------------------------------------+*/
+void UsersConvert() {
+ static TCHAR Password[MAX_PW_LEN + 1];
+ USER_BUFFER *UserBuffer = NULL;
+ BOOL NWInfo;
+ DWORD status = 0;
+ DWORD i;
+ void *NW_UInfo;
+ static NT_USER_INFO NT_UInfo;
+ ULONG TotConv = 0;
+
+ Status_ConvTxt(Lids(IDS_D_18));
+ Status_ItemLabel(Lids(IDS_D_19));
+
+ Status_CurTot((UINT) UserCount);
+
+ LogWriteLog(0, Lids(IDS_L_169));
+ LogWriteLog(1, Lids(IDS_L_170), UserCount);
+ ErrorCategorySet(Lids(IDS_L_171));
+
+ if (Users == NULL)
+ return;
+
+ UserBuffer = Users->UserBuffer;
+
+ // This will update the status pane - but we will reset it afterwards
+ for (i = 0; i < UserCount; i++) {
+ // Don't update totals yet, but update item ref incase this takes
+ // awhile
+ Status_CurNum((UINT) i + 1);
+ Status_Item(UserBuffer[i].Name);
+
+ UserNames_Resolve(&UserBuffer[i]);
+ if (TransferCancel)
+ return;
+ }
+
+ UsersLogNames(Users);
+
+ i = 0;
+ while (i < UserCount) {
+ Status_CurNum((UINT) i + 1);
+ Status_Item(UserBuffer[i].Name);
+ lstrcpy(pLine, UserBuffer[i].Name);
+
+ if (UserBuffer[i].IsNewName)
+ wsprintf(pLine, TEXT("[%s -> %s]"), UserBuffer[i].Name, UserBuffer[i].NewName);
+ else
+ wsprintf(pLine, TEXT("[%s]"), UserBuffer[i].NewName);
+
+ LogWriteLog(1, TEXT("%-50s"), pLine);
+ ErrorItemSet(TEXT("%s\r\n"), pLine);
+
+ // If duplicate or other type error just do logging - don't try to save
+ if (UserBuffer[i].err) {
+ if (UserBuffer[i].err == NWC_ERR_DUPLICATE) {
+ LogWriteLog(0, Lids(IDS_L_172));
+ ErrorIt(Lids(IDS_L_173));
+ }
+
+ if (UserBuffer[i].err == NWC_ERR_NAMELONG) {
+ LogWriteLog(0, Lids(IDS_L_174));
+ ErrorIt(Lids(IDS_L_175));
+ }
+
+ if (UserBuffer[i].err == NWC_ERR_NAMEINVALID) {
+ LogWriteLog(0, Lids(IDS_L_176));
+ ErrorIt(Lids(IDS_L_177));
+ }
+
+ } else {
+ // Init the user record
+ NTUserRecInit(UserBuffer[i].NewName, &NT_UInfo);
+
+ // +-------------------------------------------------------------+
+ // | User name figured out - now map password |
+ // +-------------------------------------------------------------+
+ memset(Password, 0, sizeof(Password));
+ if (ConvOpt->UseMappingFile)
+ // If using map file, password is already set
+ lstrcpy(Password, UserBuffer[i].Password);
+ else
+ if (lstrlen(Password) == 0) {
+ // Didn't map password - so find what to use
+ switch (ConvOpt->PasswordOption) {
+ case 0: // No Password
+ // Don't need to do anything
+ break;
+
+ case 1: // Username
+ // BUGBUG: Name can be longer then password!!!
+ lstrcpy(Password, UserBuffer[i].NewName);
+ break;
+
+ case 2: // Constant
+ lstrcpy(Password, ConvOpt->PasswordConstant);
+ break;
+
+ } // switch
+ }
+
+ NT_UInfo.password = Password;
+
+#ifdef DEBUG
+dprintf(TEXT("User: %s\n"), UserBuffer[i].Name);
+#endif
+
+ if (!UserSave(&NWInfo, UserBuffer[i].Name, UserBuffer[i].NewName, &NT_UInfo, &NW_UInfo, UserBuffer[i].Overwrite )) {
+ LogWriteLog(0, Lids(IDS_L_178));
+ ErrorIt(Lids(IDS_L_179));
+ } else {
+ // only increment total if actually converted...
+ TotUsers++;
+ TotConv++;
+ Status_TotUsers(TotUsers);
+
+ LogWriteLog(0, Lids(IDS_L_180));
+ LogWriteLog(0, Lids(IDS_CRLF));
+
+ // Converted - now need to save info to logs...
+ if (NWInfo) {
+ if (VerboseUserLogging())
+ NWUserInfoLog(UserBuffer[i].Name, NW_UInfo);
+
+ if (VerboseUserLogging())
+ NTUserRecLog(NT_UInfo);
+ }
+
+ }
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+
+ }
+
+ i++;
+ }
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+ LogWriteSummary(1, Lids(IDS_L_181), lToStr(TotConv));
+
+} // UsersConvert
+
+
+/*+-------------------------------------------------------------------------+
+ | GroupSave()
+ |
+ +-------------------------------------------------------------------------+*/
+BOOL GroupSave(LPTSTR Name, DWORD *Status) {
+
+ *Status = 0;
+
+ if (!TConversion)
+ if ((*Status = NTGroupSave(Name)))
+ return FALSE;
+
+ return TRUE;
+
+} // GroupSave
+
+
+/*+-------------------------------------------------------------------------+
+ | GroupNewName_Check()
+ |
+ +-------------------------------------------------------------------------+*/
+void GroupNewName_Check(GROUP_BUFFER *Groups) {
+ // We have done any mappings that need to be done, now check for
+ // name validity if there is a new name...
+ if (Groups->IsNewName)
+ if (GroupCacheMatch(Groups->NewName)) {
+ Groups->err = NWC_ERR_DUPLICATE;
+ }
+
+ // make sure not too long
+ if (lstrlen(Groups->NewName) > MAX_NT_GROUP_NAME_LEN) {
+ // Name is too long
+ Groups->err = NWC_ERR_NAMELONG;
+ }
+
+ // Check if a valid name (no illegal characters)...
+ if ((int) wcscspn(Groups->NewName, ILLEGAL_CHARS) < (int) lstrlen(Groups->NewName))
+ Groups->err = NWC_ERR_NAMEINVALID;
+
+} // GroupNewName_Check
+
+
+/*+-------------------------------------------------------------------------+
+ | GroupNames_Resolve()
+ |
+ +-------------------------------------------------------------------------+*/
+void GroupNames_Resolve(GROUP_BUFFER *Groups) {
+ LPTSTR TheName;
+ LPTSTR ErrorText;
+ ULONG RetType;
+
+ // Figure out which name to use
+ if (Groups->IsNewName)
+ TheName = Groups->Name;
+ else
+ TheName = Groups->NewName;
+
+ // If using mapping file then map the name appropriatly
+ if (ConvOpt->UseMappingFile) {
+ if (GroupCacheMatch(TheName))
+ Groups->err = NWC_ERR_DUPLICATE;
+ } else {
+ // check if the user name is in the destination list (duplicate)
+ if (GroupCacheMatch(TheName)) {
+ // There was - so figure out based on conversion options what
+ // to do with it...
+ switch (ConvOpt->GroupNameOption) {
+ case 0: // Log Errors
+ Groups->err = NWC_ERR_DUPLICATE;
+ break;
+
+ case 1: // ignore
+ Groups->err = NWC_ERR_IGNORE;
+ break;
+
+ case 2: // Pre-Pend constant
+ lstrcpy(NewName, ConvOpt->GroupConstant);
+ lstrcat(NewName, Groups->Name);
+ lstrcpy(Groups->NewName, NewName);
+ Groups->IsNewName = TRUE;
+ break;
+ } // switch
+ }
+ }
+
+ do {
+ RetType = IDIGNORE;
+ GroupNewName_Check(Groups);
+
+ if (Groups->err && (Groups->err != NWC_ERR_IGNORE) && PopupOnError()) {
+ switch(Groups->err) {
+ case NWC_ERR_NAMELONG:
+ ErrorText = Lids(IDS_L_182);
+ break;
+
+ case NWC_ERR_DUPLICATE:
+ ErrorText = Lids(IDS_L_183);
+ break;
+
+ case NWC_ERR_NAMEINVALID:
+ ErrorText = Lids(IDS_L_184);
+ break;
+
+ }
+
+ RetType = GroupNameErrorDlg_Do(Lids(IDS_L_185), ErrorText, Groups);
+ }
+ } while (RetType == IDRETRY);
+
+ if (RetType == IDABORT)
+ TransferCancel = TRUE;
+
+} // GroupNames_Resolve
+
+
+/*+-------------------------------------------------------------------------+
+ | GroupsConvert()
+ |
+ +-------------------------------------------------------------------------+*/
+void GroupsConvert() {
+ USER_LIST *GUsers = NULL;
+ USER_BUFFER *GUserBuffer;
+ USER_BUFFER *pUser;
+ GROUP_BUFFER *pGroup;
+ GROUP_BUFFER *GroupBuffer = NULL;
+ DWORD GUserCount;
+ DWORD status = 0;
+ ULONG Count, i;
+ ULONG TotConv = 0;
+ LPTSTR NewName;
+ BOOL SecEquivTitle = FALSE;
+ BOOL SecEquivUser = FALSE;
+ TCHAR GroupTitle[TMP_STR_LEN_256];
+
+ // update status pane
+ Status_ConvTxt(Lids(IDS_D_20));
+ Status_ItemLabel(Lids(IDS_D_21));
+ ErrorCategorySet(Lids(IDS_L_186));
+
+ Status_CurTot((UINT) GroupCount);
+ LogWriteLog(0, Lids(IDS_L_187));
+ LogWriteLog(1, Lids(IDS_L_188), GroupCount);
+
+ if (Groups == NULL)
+ return;
+
+ GroupBuffer = Groups->GroupBuffer;
+
+ for (i = 0; i < GroupCount; i++) {
+ // Don't update totals yet, but update item ref incase this takes
+ // awhile
+ Status_CurNum((UINT) i + 1);
+ Status_Item(GroupBuffer[i].Name);
+
+ GroupNames_Resolve(&GroupBuffer[i]);
+
+ if (TransferCancel)
+ return;
+ }
+
+ i = 0;
+ while (i < GroupCount) {
+ // update status pane for this group
+ Status_CurNum((UINT) i + 1);
+ Status_Item(GroupBuffer[i].Name);
+ lstrcpy(pLine, GroupBuffer[i].Name);
+
+#ifdef DEBUG
+dprintf(TEXT("Working on Group: %s\r\n"), GroupBuffer[i].Name);
+#endif
+
+ if (GroupBuffer[i].IsNewName)
+ wsprintf(pLine, TEXT("%s -> %s"), GroupBuffer[i].Name, GroupBuffer[i].NewName);
+ else
+ wsprintf(pLine, TEXT("%s"), GroupBuffer[i].NewName);
+
+ LogWriteLog(1, TEXT("%-50s"), pLine);
+ ErrorItemSet(TEXT("[%s]\r\n"), pLine);
+
+ // If duplicate or other type error just do logging - don't try
+ // to save...
+ if (GroupBuffer[i].err) {
+ if (GroupBuffer[i].err == NWC_ERR_DUPLICATE) {
+ LogWriteLog(0, Lids(IDS_L_163));
+ ErrorIt(Lids(IDS_L_189));
+ }
+
+ if (GroupBuffer[i].err == NWC_ERR_NAMELONG) {
+ LogWriteLog(0, Lids(IDS_L_162));
+ ErrorIt(Lids(IDS_L_190));
+ }
+
+ if (GroupBuffer[i].err == NWC_ERR_NAMEINVALID) {
+ LogWriteLog(0, Lids(IDS_L_164));
+ ErrorIt(Lids(IDS_L_191));
+ }
+
+ } else {
+ // Try to save it and get any errors...
+ if (!GroupSave(GroupBuffer[i].NewName, &status)) {
+ LogWriteLog(0, Lids(IDS_L_192));
+ ErrorIt(Lids(IDS_L_193));
+ } else {
+ // only increment total if actually converted...
+ TotGroups++;
+ TotConv++;
+ Status_TotGroups(TotGroups);
+
+ LogWriteLog(0, Lids(IDS_L_180));
+ }
+ }
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+
+ i++;
+ }
+ LogWriteLog(0, Lids(IDS_CRLF));
+
+ ErrorCategorySet(Lids(IDS_L_194));
+ // +-------------------------------------------------------------+
+ // | Go through and add users to the groups |
+ // +-------------------------------------------------------------+
+ for (Count = 0; Count < GroupCount; Count++) {
+ GUserCount = 0;
+
+ if (!(status = NWGroupUsersEnum(GroupBuffer[Count].Name, &GUsers)) && (GUsers != NULL)) {
+ GUserCount = GUsers->Count;
+ GUserBuffer = GUsers->UserBuffer;
+
+ if (GUserCount > 0) {
+ wsprintf(GroupTitle, Lids(IDS_S_46), GroupBuffer[Count].NewName);
+ EscapeFormattingChars(GroupTitle,
+ sizeof(GroupTitle)/sizeof(GroupTitle[0])) ;
+ Status_ItemLabel(GroupTitle);
+ LogWriteLog(1, TEXT("[%s]\r\n"), GroupBuffer[Count].NewName);
+ }
+
+ for (i = 0; i < GUserCount; i++) {
+ pUser = FindUserMatch(GUserBuffer[i].Name, Users, FALSE);
+
+ if (pUser == NULL)
+ NewName = NWSpecialNamesMap(GUserBuffer[i].Name);
+ else
+ NewName = pUser->NewName;
+
+ LogWriteLog(2, TEXT("%-20s"), NewName);
+ Status_Item(NewName);
+
+#ifdef DEBUG
+dprintf(TEXT("Adding User [%s] to Group: %s\n"), NewName, GroupBuffer[Count].NewName );
+#endif
+ if (!TConversion)
+ if (NTGroupUserAdd(GroupBuffer[Count].NewName, NewName, FALSE)) {
+ LogWriteLog(0, Lids(IDS_L_196));
+ ErrorIt(Lids(IDS_L_195), NewName, GroupBuffer[Count].NewName);
+ }
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+ }
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+ FreeMemory((LPBYTE) GUsers);
+ } else {
+ LogWriteLog(1, Lids(IDS_L_197), GroupBuffer[Count].Name);
+ ErrorIt(Lids(IDS_L_197), GroupBuffer[Count].Name);
+ }
+
+ } // loop adding users to groups
+
+ ErrorCategorySet(Lids(IDS_L_198));
+ // +-------------------------------------------------------------+
+ // | Convert Security Equivalences to Group Names |
+ // +-------------------------------------------------------------+
+ SecEquivTitle = FALSE;
+ for (Count = 0; Count < UserCount; Count++) {
+ GUserCount = 0;
+ SecEquivUser = FALSE;
+
+ if (!(status = NWUserEquivalenceEnum(Users->UserBuffer[Count].Name, &GUsers)) && (GUsers != NULL)) {
+ GUserCount = GUsers->Count;
+ GUserBuffer = GUsers->UserBuffer;
+
+ if (GUserCount > 0) {
+ for (i = 0; i < GUserCount; i++) {
+ pGroup = FindGroupMatch(GUserBuffer[i].Name, Groups, FALSE);
+
+ if (pGroup != NULL) {
+ if ((pGroup->err != NWC_ERR_NAMELONG) && (pGroup->err != NWC_ERR_NAMEINVALID))
+ if (!SecEquivTitle) {
+ SecEquivTitle = TRUE;
+ LogWriteLog(0, Lids(IDS_CRLF));
+ LogWriteLog(0, Lids(IDS_L_199));
+ }
+
+ if (!SecEquivUser) {
+ SecEquivUser = TRUE;
+ wsprintf(GroupTitle, Lids(IDS_S_47), Users->UserBuffer[Count].NewName);
+ EscapeFormattingChars(GroupTitle,
+ sizeof(GroupTitle)/sizeof(GroupTitle[0])) ;
+ Status_ItemLabel(GroupTitle);
+ LogWriteLog(1, TEXT("[%s]\r\n"), Users->UserBuffer[Count].NewName);
+ }
+
+ LogWriteLog(2, TEXT("%-20s"), pGroup->NewName);
+ Status_Item(pGroup->NewName);
+#ifdef DEBUG
+dprintf(TEXT("User [%s] Security Equivalence: %s\n"), Users->UserBuffer[Count].NewName, pGroup->NewName );
+#endif
+ if (!TConversion)
+ if (NTGroupUserAdd(pGroup->NewName, Users->UserBuffer[Count].NewName, FALSE)) {
+ LogWriteLog(0, Lids(IDS_L_196));
+ ErrorIt(Lids(IDS_L_195), Users->UserBuffer[Count].NewName, pGroup->NewName);
+ }
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+ } else {
+ // There was not a group match - check if this is supervisor
+ // equivalence
+ if (!lstrcmpi(GUserBuffer[i].Name, Lids(IDS_S_28))) {
+ // Check if we should add them
+ if (ConvOpt->AdminAccounts) {
+ if (!SecEquivTitle) {
+ SecEquivTitle = TRUE;
+ LogWriteLog(0, Lids(IDS_CRLF));
+ LogWriteLog(0, Lids(IDS_L_199));
+ }
+
+ if (!SecEquivUser) {
+ SecEquivUser = TRUE;
+ LogWriteLog(1, TEXT("[%s]\r\n"), Users->UserBuffer[Count].NewName);
+ }
+
+ LogWriteLog(2, TEXT("%-20s"), Lids(IDS_S_42));
+
+ if (!TConversion)
+ if (NTGroupUserAdd(Lids(IDS_S_42), Users->UserBuffer[Count].NewName, FALSE)) {
+ LogWriteLog(0, Lids(IDS_L_196));
+ ErrorIt(Lids(IDS_L_195), Users->UserBuffer[Count].NewName, Lids(IDS_S_42));
+ }
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+ }
+ }
+ }
+ }
+
+ // Only put blank line if we logged this user
+ if (SecEquivUser)
+ LogWriteLog(0, Lids(IDS_CRLF));
+
+ }
+
+ FreeMemory((LPBYTE) GUsers);
+ }
+
+ } // Loop converting security equivalences
+
+ // Synchronize the domain - we need to synch as Print Operators are a
+ // local group
+ NTDomainSynch(CurrentConvertList->FileServ);
+
+ // Now set server to appropriate dest server (local group - so must
+ // be on dest server and not PDC or trusted domain)...
+ if ((status = NTServerSet(CurrentConvertList->FileServ->Name))) {
+ // Failed to set server so log it and loop to next server
+ LogWriteLog(0, Lids(IDS_L_209), CurrentConvertList->FileServ->Name);
+ ErrorIt(Lids(IDS_L_209), CurrentConvertList->FileServ->Name);
+ return;
+ }
+
+ ErrorCategorySet(Lids(IDS_L_200));
+ // +-------------------------------------------------------------+
+ // | Do Print Operators |
+ // +-------------------------------------------------------------+
+ SecEquivTitle = FALSE;
+ if (!(status = NWPrintOpsEnum(&GUsers)) && (GUsers != NULL)) {
+ GUserCount = GUsers->Count;
+ GUserBuffer = GUsers->UserBuffer;
+
+ if (GUserCount > 0) {
+ for (i = 0; i < GUserCount; i++) {
+
+ if (!SecEquivTitle) {
+ SecEquivTitle = TRUE;
+ LogWriteLog(0, Lids(IDS_CRLF));
+ LogWriteLog(0, Lids(IDS_L_201));
+ }
+
+ pUser = FindUserMatch(GUserBuffer[i].Name, Users, FALSE);
+
+ if ((pUser == NULL) || ((pUser->err != NWC_ERR_NAMELONG) && (pUser->err != NWC_ERR_NAMEINVALID))) {
+ if (pUser == NULL)
+ NewName = NWSpecialNamesMap(GUserBuffer[i].Name);
+ else
+ NewName = pUser->NewName;
+
+ LogWriteLog(2, TEXT("%-20s"), NewName);
+#ifdef DEBUG
+dprintf(TEXT("Adding User [%s] to Group: %s\n"), NewName, Lids(IDS_S_43) );
+#endif
+ if (!TConversion)
+ if (NTGroupUserAdd(Lids(IDS_S_43), NewName, TRUE)) {
+ LogWriteLog(0, Lids(IDS_L_196));
+ ErrorIt(Lids(IDS_L_195), NewName, Lids(IDS_S_43));
+ }
+
+ LogWriteLog(0, Lids(IDS_CRLF));
+ }
+ }
+ }
+ }
+
+ LogWriteSummary(1, Lids(IDS_L_202), lToStr(TotConv));
+
+} // GroupsConvert
+
+
+/*+-------------------------------------------------------------------------+
+ | SupervisorDefaultsConvert()
+ |
+ +-------------------------------------------------------------------------+*/
+void SupervisorDefaultsConvert(TRANSFER_LIST *tl) {
+ ULONG i;
+ void *Defaults;
+ BOOL ConvertDefaults = FALSE;
+ NT_DEFAULTS *NTDefaults = NULL;
+ NT_DEFAULTS CDefaults;
+ DEST_SERVER_BUFFER *oDServ = NULL;
+ TRANSFER_BUFFER *TList;
+ CONVERT_OPTIONS *ConvOpt;
+
+ if (tl == NULL)
+ return;
+
+ TList = tl->TList;
+
+ memset(&CDefaults, 0, sizeof(CDefaults));
+ LogWriteLog(0, Lids(IDS_LINE));
+ LogWriteLog(0, Lids(IDS_BRACE), Lids(IDS_L_203));
+ LogWriteLog(0, Lids(IDS_LINE));
+
+ // Loop through the server pairs for conversion - this is sorted in order of
+ // destination users servers.
+ for (i = 0; i < tl->Count; i++) {
+ CurrentConvertList = TList[i].ConvertList;
+ ConvOpt = (CONVERT_OPTIONS *) CurrentConvertList->ConvertOptions;
+
+ if (CurrentConvertList->FileServ != oDServ) {
+ // if this is not the first time through the loop, then we need to save
+ // off the converted defaults
+ if (ConvertDefaults && (oDServ != NULL)) {
+ ConvertDefaults = FALSE;
+ LogWriteLog(0, Lids(IDS_L_204), oDServ->Name);
+
+ if (NTDefaults != NULL) {
+ NTUserDefaultsLog(*NTDefaults);
+
+ if (!TConversion)
+ NTUserDefaultsSet(*NTDefaults);
+ }
+
+ }
+
+ oDServ = CurrentConvertList->FileServ;
+
+ // Point to dest server and get defaults
+ NTServerSet(CurrentConvertList->FileServ->Name);
+ NTUserDefaultsGet(&NTDefaults);
+ memset(&CDefaults, 0, sizeof(CDefaults));
+
+ if (NTDefaults != NULL)
+ memcpy(&CDefaults, NTDefaults, sizeof(CDefaults));
+
+ }
+
+ // Supervisor defaults
+ if (ConvOpt->SupervisorDefaults) {
+
+ // if not flagged for this dest server, then flag and write out original
+ // values
+ if (!ConvertDefaults) {
+ ConvertDefaults = TRUE;
+
+ if (NTDefaults != NULL) {
+ LogWriteLog(0, Lids(IDS_L_205), CurrentConvertList->FileServ->Name);
+ NTUserDefaultsLog(*NTDefaults);
+ }
+ }
+
+ NWServerSet(CurrentConvertList->SourceServ->Name);
+ NWUserDefaultsGet(&Defaults);
+
+ if (Defaults != NULL) {
+ LogWriteLog(0, Lids(IDS_L_206), CurrentConvertList->SourceServ->Name);
+ NWUserDefaultsLog(Defaults);
+ NWUserDefaultsMap(Defaults, &CDefaults);
+
+ // Now map in least restrictive values to the NT one
+ if (NTDefaults != NULL) {
+ if (CDefaults.min_passwd_len < NTDefaults->min_passwd_len)
+ NTDefaults->min_passwd_len = CDefaults.min_passwd_len;
+
+ if (CDefaults.max_passwd_age < NTDefaults->max_passwd_age)
+ NTDefaults->max_passwd_age = CDefaults.max_passwd_age;
+
+ if (CDefaults.force_logoff < NTDefaults->force_logoff)
+ NTDefaults->force_logoff = CDefaults.force_logoff;
+
+ }
+
+ FreeMemory(Defaults);
+ Defaults = NULL;
+ }
+ }
+
+ }
+
+ // Need to catch the last one through the loop
+ if (ConvertDefaults && (oDServ != NULL)) {
+ ConvertDefaults = FALSE;
+ LogWriteLog(0, Lids(IDS_L_204), oDServ->Name);
+
+ if (NTDefaults != NULL) {
+ NTUserDefaultsLog(*NTDefaults);
+
+ if (!TConversion)
+ NTUserDefaultsSet(*NTDefaults);
+ }
+
+ }
+
+
+} // SupervisorDefaultsConvert
+
+
+/*+-------------------------------------------------------------------------+
+ | TransferListCompare()
+ |
+ +-------------------------------------------------------------------------+*/
+int __cdecl TransferListCompare(const void *arg1, const void *arg2) {
+ TRANSFER_BUFFER *TBarg1, *TBarg2;
+
+ TBarg1 = (TRANSFER_BUFFER *) arg1;
+ TBarg2 = (TRANSFER_BUFFER *) arg2;
+
+ return lstrcmpi( TBarg1->ServerName, TBarg2->ServerName);
+
+} // TransferListCompare
+
+
+/*+-------------------------------------------------------------------------+
+ | TransferListCreate()
+ |
+ +-------------------------------------------------------------------------+*/
+TRANSFER_LIST *TransferListCreate() {
+ CONVERT_OPTIONS *ConvOpt;
+ static TRANSFER_LIST *tl;
+ TRANSFER_BUFFER *TList;
+ CONVERT_LIST *CList;
+ ULONG Count = 0;
+
+ tl = NULL;
+ CList = ConvertListStart;
+ while (CList != NULL) {
+ Count++;
+ CList = CList->next;
+ }
+
+ if (Count == 0)
+ return NULL;
+
+ tl = AllocMemory(sizeof(TRANSFER_LIST) + (sizeof(TRANSFER_BUFFER) * Count));
+ if (tl == NULL)
+ return NULL;
+
+ tl->Count = Count;
+ TList = tl->TList;
+
+ // init it all to NULL
+ memset(TList, 0, sizeof(TRANSFER_BUFFER) * Count);
+
+ Count = 0;
+ CList = ConvertListStart;
+ while (CList != NULL) {
+ TList[Count].ConvertList = CList;
+
+ // If going to a trusted domain then point to it's PDC for user transfers
+ ConvOpt = (CONVERT_OPTIONS *) CList->ConvertOptions;
+ if (ConvOpt->UseTrustedDomain && (ConvOpt->TrustedDomain != NULL) && (ConvOpt->TrustedDomain->PDCName != NULL)) {
+ TList[Count].UserServerType = USER_SERVER_TRUSTED;
+ TList[Count].ServerName = ConvOpt->TrustedDomain->PDCName;
+ } else
+ // If in a domain then point to the PDC for user transfers
+ if (CList->FileServ->InDomain && CList->FileServ->Domain) {
+ TList[Count].UserServerType = USER_SERVER_PDC;
+ TList[Count].ServerName = CList->FileServ->Domain->PDCName;
+ } else {
+ TList[Count].UserServerType = USER_SERVER;
+ TList[Count].ServerName = CList->FileServ->Name;
+ }
+
+ Count++;
+ CList = CList->next;
+ }
+
+ // Have setup the main transfer list - now need to sort it in order of the
+ // server names that users are being transfered to.
+ qsort((void *) TList, (size_t) tl->Count, sizeof(TRANSFER_BUFFER), TransferListCompare);
+
+#ifdef DEBUG
+dprintf(TEXT("\nTransfer List:\n"));
+for (Count = 0; Count < tl->Count; Count++) {
+ dprintf(TEXT(" Name: %s "), TList[Count].ServerName);
+ switch (TList[Count].UserServerType) {
+ case USER_SERVER:
+ dprintf(TEXT("(Normal)\n"));
+ break;
+
+ case USER_SERVER_PDC:
+ dprintf(TEXT("(PDC)\n"));
+ break;
+
+ case USER_SERVER_TRUSTED:
+ dprintf(TEXT("(TRUSTED)\n"));
+ break;
+ }
+}
+
+dprintf(TEXT("\n"));
+#endif
+ return tl;
+
+} // TransferListCreate
+
+
+/*+-------------------------------------------------------------------------+
+ | DoConversion()
+ |
+ | Main program that does the actuall conversion. Loops through the
+ | convert list and transfer the information.
+ |
+ +-------------------------------------------------------------------------+*/
+void DoConversion(HWND hDlg, BOOL TrialConversion) {
+ TRANSFER_LIST *tl = NULL;
+ TRANSFER_BUFFER *TList;
+ LPTSTR oDServ = NULL;
+ DWORD status = 0;
+ UINT i;
+ BOOL GotUserList;
+ TCHAR sztime[40];
+ LPTSTR DomainName;
+
+ time(&StartTime);
+ TransferCancel = FALSE;
+ TConversion = TrialConversion;
+
+ // Check if going to non NTFS drives - if so, let user abort
+ NTFSCheck(hDlg);
+ if (TransferCancel)
+ return;
+
+ CursorHourGlass();
+
+ PanelDlg_Do(hDlg, Lids(IDS_D_22));
+ ConvertFilesInit(hDlg);
+
+ if (Panel_Cancel()) {
+ PanelDlgKill();
+ TransferCancel = TRUE;
+ CursorNormal();
+ return;
+ }
+
+ PanelDlgKill();
+
+ // Check if enough space on destination drives, if not allow user to abort
+ CursorNormal();
+ SpaceCheck(hDlg);
+ if (TransferCancel)
+ return;
+
+ CursorHourGlass();
+ tl = TransferListCreate();
+ TList = tl->TList;
+
+ DoStatusDlg(hDlg);
+
+ Status_TotConv((UINT) NumServerPairs);
+
+ Users = NULL;
+ NTUsers = NULL;
+ Groups = NULL;
+ NTGroups = NULL;
+ UserCount = 0;
+ NTUserCount = 0;
+ GroupCount = 0;
+ NTGroupCount= 0;
+
+ // Initialize global statistics
+ TotErrors = 0;
+ TotGroups = 0;
+ TotUsers = 0;
+ TotFiles = 0;
+
+ // Update statistics window
+ Status_TotComplete(0);
+ Status_TotGroups(TotGroups);
+ Status_TotUsers(TotUsers);
+ Status_TotFiles(TotFiles);
+ Status_TotErrors(TotErrors);
+
+ // Set up logs and do all the header stuff
+ LogInit();
+
+ if (TrialConversion) {
+ LogWriteLog(0, Lids(IDS_L_207));
+ } else {
+ LogWriteLog(0, Lids(IDS_L_208));
+ }
+
+ LogWriteSummary(0, Lids(IDS_CRLF));
+ LogWriteErr(Lids(IDS_CRLF));
+ LogWriteLog(0, Lids(IDS_CRLF));
+
+ // Log the list of servers to be converted
+ ErrorResetAll();
+ ConvertListLog();
+
+ // Loop through source servers and conglomerate defaults into dest servers
+ // and log the results
+ SupervisorDefaultsConvert(tl);
+
+
+ // +---------------------------------------------------------------------+
+ // | Done with init - loop through server pairs and do conversion |
+ // +---------------------------------------------------------------------+
+
+ // Get Local computer name
+ GetLocalName(&LocalName);
+
+ // Loop through the server pairs for conversion
+ for (i = 0; ((i < tl->Count) && !TransferCancel); i++) {
+ CurrentConvertList = TList[i].ConvertList;
+
+ // Get source and destination server - update logs and status window
+ Status_CurConv(i + 1);
+
+ SourceServer = CurrentConvertList->SourceServ->Name;
+ DestServer = CurrentConvertList->FileServ->Name;
+
+ Status_SrcServ(SourceServer);
+ Status_DestServ(DestServer);
+
+ // Log this server pair - section heading
+ ConvertPairLog();
+
+ // SetConvert options and log them out.
+ ConvOpt = (CONVERT_OPTIONS *) CurrentConvertList->ConvertOptions;
+ FileOptions = (FILE_OPTIONS *) CurrentConvertList->FileOptions;
+ ConvertOptionsLog();
+ OptionsFileLog();
+
+ // If our destination server has changed then update the caches
+ if (TList[i].ServerName != oDServ) {
+ oDServ = TList[i].ServerName;
+ GotUserList = TRUE;
+
+ ListCacheFree(&UserCacheHead);
+ ListCacheFree(&GroupCacheHead);
+ if ((status = NTServerSet(DestServer))) {
+ // Failed to set server so log it and loop to next server
+ LogWriteLog(0, Lids(IDS_L_209), DestServer);
+ ErrorIt(Lids(IDS_L_209), DestServer);
+ goto ConvDo_Loop;
+ }
+
+ // Put VShares here so it doesn't get lost in user info
+ if (FileOptions->TransferFileInfo)
+ VSharesCreate(CurrentConvertList->FileServ, TConversion);
+
+ // Get users on NT server and put in cache
+ if (status = NTUsersEnum(&NTUsers)) {
+ // Failed - make sure we don't try to convert users and log err
+ NTUsers = NULL;
+ NTUserCount = 0;
+ LogWriteLog(0, Lids(IDS_L_210), DestServer);
+ ErrorIt(Lids(IDS_L_210), DestServer);
+ GotUserList = FALSE;
+ } else
+ NTUserCount = NTUsers->Count;
+
+ if (!ListCachePut(&UserCacheHead, (void *) NTUsers, NTUserCount)) {
+ // Failed - but clean up NT List first
+ GotUserList = FALSE;
+ FreeMemory(NTUsers);
+ } else {
+ // Now get Groups (if users succeded) and put in group cache
+ if (status = NTGroupsEnum(&NTGroups)) {
+ // Failed - make sure we don't try to convert users and log err
+ NTGroupCount = 0;
+ NTGroups = NULL;
+ LogWriteLog(0, Lids(IDS_L_211), DestServer);
+ ErrorIt(Lids(IDS_L_211), DestServer);
+ FreeMemory(NTUsers);
+ GotUserList = FALSE;
+ } else
+ NTGroupCount = NTGroups->Count;
+
+ if (!ListCachePut(&GroupCacheHead, (void *) NTGroups, NTGroupCount)) {
+ // Failed - but clean up NT List first
+ GotUserList = FALSE;
+ FreeMemory(NTUsers);
+ FreeMemory(NTGroups);
+ }
+ }
+
+ }
+
+ wsprintf(UserServerName, TEXT("\\\\%s"), TList[i].ServerName);
+ if ((status = NTServerSet(TList[i].ServerName))) {
+ // Failed to set server so log it and loop to next server
+ LogWriteLog(0, Lids(IDS_L_209), TList[i].ServerName);
+ ErrorIt(Lids(IDS_L_209), TList[i].ServerName);
+ goto ConvDo_Loop;
+ }
+
+ if (ConvOpt->NetWareInfo) {
+ NTSAMClose();
+
+ if (ConvOpt->UseTrustedDomain && (ConvOpt->TrustedDomain != NULL))
+ DomainName = ConvOpt->TrustedDomain->Name;
+ else
+ if ((CurrentConvertList->FileServ->InDomain) && (CurrentConvertList->FileServ->Domain != NULL))
+ DomainName = CurrentConvertList->FileServ->Domain->Name;
+ else
+ DomainName = TEXT("");
+
+ if ((status = NTSAMConnect(TList[i].ServerName, DomainName))) {
+ // Failed to set server so log it and loop to next server
+ LogWriteLog(0, Lids(IDS_L_209), TList[i].ServerName);
+ ErrorIt(Lids(IDS_L_209), TList[i].ServerName);
+ goto ConvDo_Loop;
+ }
+ }
+
+ if ((status = NWServerSet(SourceServer))) {
+ // Failed to set server so log it and loop to next server
+ LogWriteLog(0, Lids(IDS_L_209), SourceServer);
+ ErrorIt(Lids(IDS_L_209), SourceServer);
+ goto ConvDo_Loop;
+ }
+
+ //
+ // If we are using mapping file then don't enum users and groups off
+ // the server. Get them from the mapping file instead.
+ //
+ hMap = NULL;
+ if (ConvOpt->UseMappingFile) {
+ //
+ // This is mapping file stuff
+ //
+ hMap = map_Open(ConvOpt->MappingFile);
+ if (hMap == NULL) {
+ ErrorIt(Lids(IDS_L_217), ConvOpt->MappingFile);
+ goto ConvDo_Loop;
+ }
+
+ if ((status = map_GroupsEnum(hMap, &Groups))) {
+ // Failed - make sure we don't try to convert users and log err
+ Groups = NULL;
+ GroupCount = 0;
+ LogWriteLog(0, Lids(IDS_L_219), ConvOpt->MappingFile);
+ ErrorIt(Lids(IDS_L_219), ConvOpt->MappingFile);
+ GotUserList = FALSE;
+ } else
+ GroupCount = Groups->Count;
+
+ if ((status = map_UsersEnum(hMap, &Users))) {
+ // Failed - make sure we don't try to convert users and log err
+ Users = NULL;
+ UserCount = 0;
+ LogWriteLog(0, Lids(IDS_L_218), ConvOpt->MappingFile);
+ ErrorIt(Lids(IDS_L_218), ConvOpt->MappingFile);
+ GotUserList = FALSE;
+ } else
+ UserCount = Users->Count;
+
+ } else {
+ //
+ // Enuming users and groups from NetWare Server instead of map file
+ //
+ if ((status = NWGroupsEnum(&Groups, TRUE))) {
+ // Failed - make sure we don't try to convert users and log err
+ Groups = NULL;
+ GroupCount = 0;
+ LogWriteLog(0, Lids(IDS_L_211), SourceServer);
+ ErrorIt(Lids(IDS_L_211), SourceServer);
+ GotUserList = FALSE;
+ } else
+ GroupCount = Groups->Count;
+
+ if ((status = NWUsersEnum(&Users, TRUE))) {
+ // Failed - make sure we don't try to convert users and log err
+ Users = NULL;
+ UserCount = 0;
+ LogWriteLog(0, Lids(IDS_L_210), SourceServer);
+ ErrorIt(Lids(IDS_L_210), SourceServer);
+ GotUserList = FALSE;
+ } else
+ UserCount = Users->Count;
+ }
+
+ if (GotUserList) {
+ // User and Groups
+ if (ConvOpt->TransferUserInfo) {
+ UsersConvert();
+
+ if (!TransferCancel)
+ GroupsConvert();
+ }
+ }
+
+ // Note GroupsConvert switches servers for Print Operators to the
+ // destination server (and not the PDC).
+
+ // Files
+ if (!(TransferCancel) && FileOptions->TransferFileInfo) {
+ ErrorCategorySet(Lids(IDS_L_212));
+
+ // Now set server to appropriate file dest server
+ if ((status = NTServerSet(CurrentConvertList->FileServ->Name))) {
+ // Failed to set server so log it and loop to next server
+ LogWriteLog(0, Lids(IDS_L_209), CurrentConvertList->FileServ->Name);
+ ErrorIt(Lids(IDS_L_209), CurrentConvertList->FileServ->Name);
+ goto ConvDo_Loop;
+ }
+
+ Status_BytesTxt(Lids(IDS_L_213));
+ Status_Bytes(TEXT("0"));
+ Status_TotBytes(TEXT("0"));
+ Status_BytesSep(Lids(IDS_L_214));
+ ConvertFiles(hDlg, TConversion, Users, Groups);
+ Status_BytesTxt(TEXT(""));
+ Status_Bytes(TEXT(""));
+ Status_TotBytes(TEXT(""));
+ Status_BytesSep(TEXT(""));
+ }
+
+ NWServerFree();
+
+ConvDo_Loop:
+ Status_TotComplete(i);
+
+ if (Users) {
+ FreeMemory(Users);
+ UserCount = 0;
+ }
+
+ if (Groups) {
+ FreeMemory(Groups);
+ GroupCount = 0;
+ }
+
+ if (hMap != NULL)
+ map_Close(hMap);
+
+ } // for loop through transfer list
+
+ // Free up our caches
+ ListCacheFree(&UserCacheHead);
+ ListCacheFree(&GroupCacheHead);
+
+ // Log out the finish time
+ LogWriteSummary(0, Lids(IDS_CRLF));
+ LogWriteSummary(0, Lids(IDS_CRLF));
+ LogWriteLog(0, Lids(IDS_CRLF));
+ LogWriteLog(0, Lids(IDS_CRLF));
+ GetTime(sztime);
+
+ if (TransferCancel) {
+ LogWriteLog(0, Lids(IDS_L_215), sztime);
+ LogWriteSummary(0, Lids(IDS_L_215), sztime);
+ } else {
+ LogWriteLog(0, Lids(IDS_L_216), sztime);
+ LogWriteSummary(0, Lids(IDS_L_216), sztime);
+ }
+
+ if (tl != NULL)
+ FreeMemory(tl);
+
+ NTSAMClose();
+ StatusDlgKill();
+ CursorNormal();
+
+ TotConversions = i;
+ ConversionEndDlg_Do(hDlg);
+
+} // DoConversion
+
+
+/*+-------------------------------------------------------------------------+
+ | ConversionSuccessful()
+ |
+ +-------------------------------------------------------------------------+*/
+BOOL ConversionSuccessful() {
+ if (TotErrors || TransferCancel)
+ return FALSE;
+ else
+ return TRUE;
+
+} // ConversionSuccesful
diff --git a/private/nw/convert/nwconv/transfer.h b/private/nw/convert/nwconv/transfer.h
new file mode 100644
index 000000000..0a766548a
--- /dev/null
+++ b/private/nw/convert/nwconv/transfer.h
@@ -0,0 +1,19 @@
+/*+-------------------------------------------------------------------------+
+ | Copyright 1993-1994 (C) Microsoft Corporation - All rights reserved. |
+ +-------------------------------------------------------------------------+*/
+
+#ifndef _TRANSFER_
+#define _TRANSFER_
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+void DoConversion(HWND hDlg, BOOL TrialConversion);
+BOOL ConversionSuccessful();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/nw/convert/nwconv/userdlg.c b/private/nw/convert/nwconv/userdlg.c
new file mode 100644
index 000000000..5ceb86465
--- /dev/null
+++ b/private/nw/convert/nwconv/userdlg.c
@@ -0,0 +1,1266 @@
+/*
+ +-------------------------------------------------------------------------+
+ | User Options Dialog |
+ +-------------------------------------------------------------------------+
+ | (c) Copyright 1993-1994 |
+ | Microsoft Corp. |
+ | All rights reserved |
+ | |
+ | Program : [UserDlg.c] |
+ | Programmer : Arthur Hanson |
+ | Original Program Date : [Feb 15, 1993] |
+ | Last Update : [Jun 16, 1994] |
+ | |
+ | Version: 1.00 |
+ | |
+ | Description: |
+ | |
+ | History: |
+ | arth Jun 16, 1994 1.00 Original Version. |
+ | |
+ +-------------------------------------------------------------------------+
+*/
+
+
+#include "globals.h"
+
+#include <limits.h>
+
+#include "nwconv.h"
+#include "convapi.h"
+#include "userdlg.h"
+#include "transfer.h"
+#include "ntnetapi.h"
+#include "map.h"
+
+#define togmap 1
+
+// Utility Macros for Advanced >> button
+#define SetStyleOn(hWnd, Style) SetWindowLong(hWnd, GWL_STYLE, Style | GetWindowLong(hWnd, GWL_STYLE));
+
+#define SetStyleOff(hWnd, Style) SetWindowLong(hWnd, GWL_STYLE, ~Style & GetWindowLong(hWnd, GWL_STYLE));
+
+
+static CONVERT_OPTIONS cvoDefault;
+static CONVERT_OPTIONS *CurrentConvertOptions;
+static LPTSTR SourceServ;
+static LPTSTR DestServ;
+static SOURCE_SERVER_BUFFER *SServ;
+static DEST_SERVER_BUFFER *DServ;
+static BOOL FPNWChk;
+static short TabSelection;
+
+/*+-------------------------------------------------------------------------+
+ | UserOptionsDefaultsSet()
+ |
+ +-------------------------------------------------------------------------+*/
+void UserOptionsDefaultsSet(void *cvto) {
+ memcpy((void *) &cvoDefault, cvto, sizeof(CONVERT_OPTIONS));
+
+} // UserOptionsDefaultsSet
+
+
+/*+-------------------------------------------------------------------------+
+ | UserOptionsDefaultsReset()
+ |
+ +-------------------------------------------------------------------------+*/
+void UserOptionsDefaultsReset() {
+ memset(&cvoDefault, 0, sizeof(CONVERT_OPTIONS));
+
+ cvoDefault.TransferUserInfo = TRUE;
+ cvoDefault.ForcePasswordChange = TRUE;
+ cvoDefault.SupervisorDefaults = TRUE;
+ cvoDefault.AdminAccounts = FALSE;
+ cvoDefault.NetWareInfo = FALSE;
+ cvoDefault.GroupNameOption = 1;
+
+} // UserOptionsDefaultsReset
+
+
+/*+-------------------------------------------------------------------------+
+ | UserOptionsInit()
+ |
+ +-------------------------------------------------------------------------+*/
+void UserOptionsInit(void **lpcvto) {
+ CONVERT_OPTIONS *cvto;
+
+ cvto = (CONVERT_OPTIONS *) *lpcvto;
+
+ // if we need to allocate space, do so
+ if (cvto == NULL)
+ cvto = AllocMemory(sizeof(CONVERT_OPTIONS));
+
+ // make sure it was allocated
+ if (cvto == NULL)
+ return;
+
+ memcpy(cvto, (void *) &cvoDefault, sizeof(CONVERT_OPTIONS));
+ *lpcvto = (void *) cvto;
+
+} // UserOptionsInit
+
+
+/*+-------------------------------------------------------------------------+
+ | UserOptionsLoad()
+ |
+ +-------------------------------------------------------------------------+*/
+void UserOptionsLoad(HANDLE hFile, void **lpcvto) {
+ CONVERT_OPTIONS *cvto;
+ DWORD wrote;
+
+ cvto = (CONVERT_OPTIONS *) *lpcvto;
+
+ // if we need to allocate space, do so
+ if (cvto == NULL)
+ cvto = AllocMemory(sizeof(CONVERT_OPTIONS));
+
+ // make sure it was allocated
+ if (cvto == NULL)
+ return;
+
+ ReadFile(hFile, cvto, sizeof(CONVERT_OPTIONS), &wrote, NULL);
+ *lpcvto = (void *) cvto;
+
+} // UserOptionsLoad
+
+
+/*+-------------------------------------------------------------------------+
+ | UserOptionsSave()
+ |
+ +-------------------------------------------------------------------------+*/
+void UserOptionsSave(HANDLE hFile, void *pcvto) {
+ CONVERT_OPTIONS *cvto;
+ DWORD wrote;
+ DOMAIN_BUFFER *Trusted;
+
+ cvto = (CONVERT_OPTIONS *) pcvto;
+
+ // if trusted domain then index the domain list and save off the old
+ // domain pointer so that we save the index instead.
+ if (cvto->UseTrustedDomain && (cvto->TrustedDomain != NULL)) {
+ DomainListIndex();
+ Trusted = cvto->TrustedDomain;
+ cvto->TrustedDomain = (DOMAIN_BUFFER *) cvto->TrustedDomain->Index;
+ } else
+ cvto->UseTrustedDomain = FALSE;
+
+ WriteFile(hFile, pcvto, sizeof(CONVERT_OPTIONS), &wrote, NULL);
+
+ // if we replaced the domain pointer, then restore it
+ if (cvto->UseTrustedDomain)
+ cvto->TrustedDomain = Trusted;
+
+} // UserOptionsSave
+
+
+/*+-------------------------------------------------------------------------+
+ | Passwords_Toggle()
+ |
+ +-------------------------------------------------------------------------+*/
+void Passwords_Toggle(HWND hDlg, BOOL Toggle) {
+ HWND hCtrl;
+ BOOL MainToggle = Toggle;
+
+ hCtrl = GetDlgItem(hDlg, IDC_CHKUSERS);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 0)
+ MainToggle = FALSE;
+
+ hCtrl = GetDlgItem(hDlg, IDC_CHKPWFORCE);
+ ShowWindow(hCtrl, Toggle);
+ EnableWindow(hCtrl, MainToggle);
+
+#ifdef togmap
+ hCtrl = GetDlgItem(hDlg, IDC_CHKMAPPING);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 1)
+ MainToggle = FALSE;
+#endif
+
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO1);
+ ShowWindow(hCtrl, Toggle);
+ EnableWindow(hCtrl, MainToggle);
+
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO2);
+ ShowWindow(hCtrl, Toggle);
+ EnableWindow(hCtrl, MainToggle);
+
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO3);
+ ShowWindow(hCtrl, Toggle);
+ EnableWindow(hCtrl, MainToggle);
+
+ hCtrl = GetDlgItem(hDlg, IDC_PWCONST);
+ ShowWindow(hCtrl, Toggle);
+ EnableWindow(hCtrl, MainToggle);
+
+} // Passwords_Toggle
+
+
+/*+-------------------------------------------------------------------------+
+ | DuplicateUsers_Toggle()
+ |
+ +-------------------------------------------------------------------------+*/
+void DuplicateUsers_Toggle(HWND hDlg, BOOL Toggle) {
+ HWND hCtrl;
+ BOOL MainToggle = Toggle;
+
+ hCtrl = GetDlgItem(hDlg, IDC_CHKUSERS);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 0)
+ MainToggle = FALSE;
+
+#ifdef togmap
+ hCtrl = GetDlgItem(hDlg, IDC_CHKMAPPING);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 1)
+ MainToggle = FALSE;
+#endif
+
+ hCtrl = GetDlgItem(hDlg, IDC_STATDUP);
+ ShowWindow(hCtrl, Toggle);
+
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO4);
+ ShowWindow(hCtrl, Toggle);
+ EnableWindow(hCtrl, MainToggle);
+
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO5);
+ ShowWindow(hCtrl, Toggle);
+ EnableWindow(hCtrl, MainToggle);
+
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO6);
+ ShowWindow(hCtrl, Toggle);
+ EnableWindow(hCtrl, MainToggle);
+
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO7);
+ ShowWindow(hCtrl, Toggle);
+ EnableWindow(hCtrl, MainToggle);
+
+ hCtrl = GetDlgItem(hDlg, IDC_USERCONST);
+ ShowWindow(hCtrl, Toggle);
+ EnableWindow(hCtrl, MainToggle);
+
+} // DuplicateUsers_Toggle
+
+
+/*+-------------------------------------------------------------------------+
+ | DuplicateGroups_Toggle()
+ |
+ +-------------------------------------------------------------------------+*/
+void DuplicateGroups_Toggle(HWND hDlg, BOOL Toggle) {
+ HWND hCtrl;
+ BOOL MainToggle = Toggle;
+
+ hCtrl = GetDlgItem(hDlg, IDC_CHKUSERS);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 0)
+ MainToggle = FALSE;
+
+#ifdef togmap
+ hCtrl = GetDlgItem(hDlg, IDC_CHKMAPPING);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 1)
+ MainToggle = FALSE;
+#endif
+
+ hCtrl = GetDlgItem(hDlg, IDC_STATDUP);
+ ShowWindow(hCtrl, Toggle);
+
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO8);
+ ShowWindow(hCtrl, Toggle);
+ EnableWindow(hCtrl, MainToggle);
+
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO9);
+ ShowWindow(hCtrl, Toggle);
+ EnableWindow(hCtrl, MainToggle);
+
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO10);
+ ShowWindow(hCtrl, Toggle);
+ EnableWindow(hCtrl, MainToggle);
+
+ hCtrl = GetDlgItem(hDlg, IDC_GROUPCONST);
+ ShowWindow(hCtrl, Toggle);
+ EnableWindow(hCtrl, MainToggle);
+
+} // DuplicateGroups_Toggle
+
+
+/*+-------------------------------------------------------------------------+
+ | Defaults_Toggle()
+ |
+ +-------------------------------------------------------------------------+*/
+void Defaults_Toggle(HWND hDlg, BOOL Toggle) {
+ HWND hCtrl;
+ BOOL MainToggle = Toggle;
+
+ hCtrl = GetDlgItem(hDlg, IDC_CHKUSERS);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 0)
+ MainToggle = FALSE;
+
+ hCtrl = GetDlgItem(hDlg, IDC_CHKSUPER);
+ EnableWindow(hCtrl, MainToggle);
+ ShowWindow(hCtrl, Toggle);
+
+ hCtrl = GetDlgItem(hDlg, IDC_CHKADMIN);
+ EnableWindow(hCtrl, MainToggle);
+ ShowWindow(hCtrl, Toggle);
+
+ hCtrl = GetDlgItem(hDlg, IDC_CHKFPNW);
+ if (FPNWChk)
+ EnableWindow(hCtrl, MainToggle);
+ else
+ EnableWindow(hCtrl, FALSE);
+
+ ShowWindow(hCtrl, Toggle);
+} // Defaults_Toggle
+
+
+/*+-------------------------------------------------------------------------+
+ | Mapping_Toggle()
+ |
+ +-------------------------------------------------------------------------+*/
+void Mapping_Toggle(HWND hDlg, BOOL Toggle) {
+ HWND hCtrl;
+
+ // These two are the reverse of the others...
+ hCtrl = GetDlgItem(hDlg, IDC_MAPPINGFILE);
+ EnableWindow(hCtrl, !Toggle);
+ hCtrl = GetDlgItem(hDlg, IDC_BTNMAPPINGFILE);
+ EnableWindow(hCtrl, !Toggle);
+ hCtrl = GetDlgItem(hDlg, IDC_BTNMAPPINGEDIT);
+ EnableWindow(hCtrl, !Toggle);
+
+#ifdef togmap
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO1);
+ EnableWindow(hCtrl, Toggle);
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO2);
+ EnableWindow(hCtrl, Toggle);
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO3);
+ EnableWindow(hCtrl, Toggle);
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO4);
+ EnableWindow(hCtrl, Toggle);
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO5);
+ EnableWindow(hCtrl, Toggle);
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO6);
+ EnableWindow(hCtrl, Toggle);
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO7);
+ EnableWindow(hCtrl, Toggle);
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO8);
+ EnableWindow(hCtrl, Toggle);
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO9);
+ EnableWindow(hCtrl, Toggle);
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO10);
+ EnableWindow(hCtrl, Toggle);
+ hCtrl = GetDlgItem(hDlg, IDC_CHKPWFORCE);
+ EnableWindow(hCtrl, Toggle);
+ hCtrl = GetDlgItem(hDlg, IDC_PWCONST);
+ EnableWindow(hCtrl, Toggle);
+ hCtrl = GetDlgItem(hDlg, IDC_USERCONST);
+ EnableWindow(hCtrl, Toggle);
+ hCtrl = GetDlgItem(hDlg, IDC_GROUPCONST);
+ EnableWindow(hCtrl, Toggle);
+#endif
+
+} // Mapping_Toggle
+
+
+/*+-------------------------------------------------------------------------+
+ | UserDialogToggle()
+ |
+ +-------------------------------------------------------------------------+*/
+void UserDialogToggle(HWND hDlg, BOOL Toggle) {
+ HWND hCtrl;
+
+ hCtrl = GetDlgItem(hDlg, IDC_CHKMAPPING);
+ EnableWindow(hCtrl, Toggle);
+
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 1)
+ Mapping_Toggle(hDlg, FALSE);
+ else
+ Mapping_Toggle(hDlg, Toggle);
+
+ if (!Toggle) {
+ hCtrl = GetDlgItem(hDlg, IDC_MAPPINGFILE);
+ EnableWindow(hCtrl, Toggle);
+ hCtrl = GetDlgItem(hDlg, IDC_BTNMAPPINGFILE);
+ EnableWindow(hCtrl, Toggle);
+ hCtrl = GetDlgItem(hDlg, IDC_BTNMAPPINGEDIT);
+ EnableWindow(hCtrl, Toggle);
+ }
+
+ hCtrl = GetDlgItem(hDlg, IDC_CHKSUPER);
+ EnableWindow(hCtrl, Toggle);
+ hCtrl = GetDlgItem(hDlg, IDC_CHKADMIN);
+ EnableWindow(hCtrl, Toggle);
+
+ hCtrl = GetDlgItem(hDlg, IDC_CHKFPNW);
+ if (FPNWChk)
+ EnableWindow(hCtrl, Toggle);
+ else
+ EnableWindow(hCtrl, FALSE);
+
+ // Check the Advanced Trusted domain check and toggle controls appropriatly
+ hCtrl = GetDlgItem(hDlg, IDC_CHKTRUSTED);
+
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 1) {
+ hCtrl = GetDlgItem(hDlg, IDC_TRUSTED);
+ EnableWindow(hCtrl, Toggle);
+
+ } else {
+ hCtrl = GetDlgItem(hDlg, IDC_TRUSTED);
+ EnableWindow(hCtrl, FALSE);
+
+ }
+
+ // Now toggle the checkbox itself
+ hCtrl = GetDlgItem(hDlg, IDC_CHKTRUSTED);
+ EnableWindow(hCtrl, Toggle);
+
+} // UserDialogToggle
+
+
+/*+-------------------------------------------------------------------------+
+ | UserDialogSave()
+ |
+ +-------------------------------------------------------------------------+*/
+void UserDialogSave(HWND hDlg) {
+ HWND hCtrl;
+ DWORD dwIndex;
+ TCHAR TrustedDomain[MAX_PATH + 1];
+
+ hCtrl = GetDlgItem(hDlg, IDC_CHKUSERS);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 1)
+ CurrentConvertOptions->TransferUserInfo = TRUE;
+ else
+ CurrentConvertOptions->TransferUserInfo = FALSE;
+
+ hCtrl = GetDlgItem(hDlg, IDC_CHKMAPPING);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 1)
+ CurrentConvertOptions->UseMappingFile = TRUE;
+ else
+ CurrentConvertOptions->UseMappingFile = FALSE;
+
+ // Mapping file is handled in Verify
+
+ // Password stuff--------------------------------------------------------
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO1);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 1)
+ CurrentConvertOptions->PasswordOption = 0;
+
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO2);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 1)
+ CurrentConvertOptions->PasswordOption = 1;
+
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO3);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 1)
+ CurrentConvertOptions->PasswordOption = 2;
+
+ hCtrl = GetDlgItem(hDlg, IDC_PWCONST);
+ * (WORD *)CurrentConvertOptions->PasswordConstant = sizeof(CurrentConvertOptions->PasswordConstant);
+ SendMessage(hCtrl, EM_GETLINE, 0, (LPARAM) CurrentConvertOptions->PasswordConstant);
+
+ hCtrl = GetDlgItem(hDlg, IDC_CHKPWFORCE);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 1)
+ CurrentConvertOptions->ForcePasswordChange = TRUE;
+ else
+ CurrentConvertOptions->ForcePasswordChange = FALSE;
+
+ // Username stuff--------------------------------------------------------
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO4);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 1)
+ CurrentConvertOptions->UserNameOption = 0;
+
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO5);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 1)
+ CurrentConvertOptions->UserNameOption = 1;
+
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO6);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 1)
+ CurrentConvertOptions->UserNameOption = 2;
+
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO7);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 1)
+ CurrentConvertOptions->UserNameOption = 3;
+
+ hCtrl = GetDlgItem(hDlg, IDC_USERCONST);
+ * (WORD *)CurrentConvertOptions->UserConstant = sizeof(CurrentConvertOptions->UserConstant);
+ SendMessage(hCtrl, EM_GETLINE, 0, (LPARAM) CurrentConvertOptions->UserConstant);
+
+ // Group-name stuff--------------------------------------------------------
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO8);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 1)
+ CurrentConvertOptions->GroupNameOption = 0;
+
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO9);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 1)
+ CurrentConvertOptions->GroupNameOption = 1;
+
+ hCtrl = GetDlgItem(hDlg, IDC_RADIO10);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 1)
+ CurrentConvertOptions->GroupNameOption = 2;
+
+ hCtrl = GetDlgItem(hDlg, IDC_GROUPCONST);
+ * (WORD *)CurrentConvertOptions->GroupConstant = sizeof(CurrentConvertOptions->GroupConstant);
+ SendMessage(hCtrl, EM_GETLINE, 0, (LPARAM) CurrentConvertOptions->GroupConstant);
+
+
+ // Defaults page stuff------------------------------------------------------
+ hCtrl = GetDlgItem(hDlg, IDC_CHKSUPER);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 1)
+ CurrentConvertOptions->SupervisorDefaults = TRUE;
+ else
+ CurrentConvertOptions->SupervisorDefaults = FALSE;
+
+ hCtrl = GetDlgItem(hDlg, IDC_CHKADMIN);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 1)
+ CurrentConvertOptions->AdminAccounts = TRUE;
+ else
+ CurrentConvertOptions->AdminAccounts = FALSE;
+
+ if (FPNWChk) {
+ hCtrl = GetDlgItem(hDlg, IDC_CHKFPNW);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 1)
+ CurrentConvertOptions->NetWareInfo = TRUE;
+ else
+ CurrentConvertOptions->NetWareInfo = FALSE;
+ } else
+ CurrentConvertOptions->NetWareInfo = FALSE;
+
+
+ // Advanced >> button stuff-------------------------------------------------
+ hCtrl = GetDlgItem(hDlg, IDC_CHKTRUSTED);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 1) {
+ CurrentConvertOptions->UseTrustedDomain = TRUE;
+
+ // if there is already a trusted domain selected - clear it out
+ if (CurrentConvertOptions->TrustedDomain != NULL) {
+ DomainListDelete(CurrentConvertOptions->TrustedDomain);
+ CurrentConvertOptions->TrustedDomain = NULL;
+ }
+
+ hCtrl = GetDlgItem(hDlg, IDC_TRUSTED);
+ dwIndex = SendMessage(hCtrl, CB_GETCURSEL, 0, 0);
+
+ if (dwIndex != CB_ERR) {
+ // Get the domain name and then try to add it to our lists
+ SendMessage(hCtrl, CB_GETLBTEXT, (WPARAM) dwIndex, (LPARAM) TrustedDomain);
+ CurrentConvertOptions->TrustedDomain = NTTrustedDomainSet(hDlg, DestServ, TrustedDomain);
+ }
+
+ } else
+ CurrentConvertOptions->UseTrustedDomain = FALSE;
+
+ // Set default values to new selections
+ UserOptionsDefaultsSet(CurrentConvertOptions);
+
+} // UserDialogSave
+
+
+/*+-------------------------------------------------------------------------+
+ | UserDialogVerify()
+ |
+ +-------------------------------------------------------------------------+*/
+BOOL UserDialogVerify(HWND hDlg) {
+ HWND hCtrl;
+ static TCHAR MappingFile[MAX_PATH + 1];
+ static char FileNameA[MAX_PATH + 1];
+ static char CmdLine[MAX_PATH + 1 + 12]; // Editor + file
+ TCHAR drive[MAX_DRIVE + 1];
+ TCHAR dir[MAX_PATH + 1];
+ TCHAR fname[MAX_PATH + 1];
+ TCHAR ext[_MAX_EXT + 1];
+ HANDLE hFile;
+
+ // Check trusted domain...
+
+ // Check mapping file
+ hCtrl = GetDlgItem(hDlg, IDC_CHKUSERS);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 1) {
+ hCtrl = GetDlgItem(hDlg, IDC_CHKMAPPING);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 1) {
+ hCtrl = GetDlgItem(hDlg, IDC_MAPPINGFILE);
+ * (WORD *)MappingFile = sizeof(MappingFile);
+ SendMessage(hCtrl, EM_GETLINE, 0, (LPARAM) MappingFile);
+ lsplitpath(MappingFile, drive, dir, fname, ext);
+
+ // Make sure a file name is specified
+ if (lstrlen(fname) == 0) {
+ MessageBox(hDlg, Lids(IDS_NOREADMAP), Lids(IDS_TXTWARNING), MB_OK | MB_ICONEXCLAMATION);
+ hCtrl = GetDlgItem(hDlg, IDC_MAPPINGFILE);
+ SetFocus(hCtrl);
+ return FALSE;
+ }
+
+ if ((drive[0] == TEXT('\0')) && (dir[0] == TEXT('\0')))
+ lstrcpy(dir, ProgPath);
+
+ if (ext[0] == TEXT('\0'))
+ lstrcpy(ext, Lids(IDS_S_36));
+
+ lmakepath(MappingFile, drive, dir, fname, ext);
+
+ lstrcpy(CurrentConvertOptions->MappingFile, MappingFile);
+
+ WideCharToMultiByte(CP_ACP, 0, MappingFile, -1, FileNameA, sizeof(FileNameA), NULL, NULL);
+ hFile = CreateFileA( FileNameA, GENERIC_READ, 0,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
+
+ if (hFile == INVALID_HANDLE_VALUE) {
+ MessageBox(hDlg, Lids(IDS_NOREADMAP), Lids(IDS_TXTWARNING), MB_OK | MB_ICONEXCLAMATION);
+ hCtrl = GetDlgItem(hDlg, IDC_MAPPINGFILE);
+ SetFocus(hCtrl);
+ return FALSE;
+ } else
+ CloseHandle(hFile);
+
+ }
+ } // mapping file
+
+ return TRUE;
+
+} // UserDialogVerify
+
+
+/*+-------------------------------------------------------------------------+
+ | UserDialogSetup()
+ |
+ +-------------------------------------------------------------------------+*/
+void UserDialogSetup(HWND hDlg) {
+ HWND hCtrl;
+
+ // Main TransferUserCheckbox
+ hCtrl = GetDlgItem(hDlg, IDC_CHKUSERS);
+ if (CurrentConvertOptions->TransferUserInfo)
+ SendMessage(hCtrl, BM_SETCHECK, 1, 0);
+ else
+ SendMessage(hCtrl, BM_SETCHECK, 0, 0);
+
+ // Mapping file checkbox
+ hCtrl = GetDlgItem(hDlg, IDC_CHKMAPPING);
+ if (CurrentConvertOptions->UseMappingFile)
+ SendMessage(hCtrl, BM_SETCHECK, 1, 0);
+ else
+ SendMessage(hCtrl, BM_SETCHECK, 0, 0);
+
+
+ hCtrl = GetDlgItem(hDlg, IDC_MAPPINGFILE);
+ PostMessage(hCtrl, EM_LIMITTEXT, (WPARAM) MAX_PATH, 0);
+ SendMessage(hCtrl, WM_SETTEXT, 0, (LPARAM) CurrentConvertOptions->MappingFile);
+
+ // Force Password checkbox
+ hCtrl = GetDlgItem(hDlg, IDC_CHKPWFORCE);
+ if (CurrentConvertOptions->ForcePasswordChange)
+ SendMessage(hCtrl, BM_SETCHECK, 1, 0);
+ else
+ SendMessage(hCtrl, BM_SETCHECK, 0, 0);
+
+ // Set the text and limit the lengths of the edit fields...
+ hCtrl = GetDlgItem(hDlg, IDC_PWCONST);
+ PostMessage(hCtrl, EM_LIMITTEXT, (WPARAM) MAX_PW_LEN, 0);
+ SendMessage(hCtrl, WM_SETTEXT, 0, (LPARAM) CurrentConvertOptions->PasswordConstant);
+
+ hCtrl = GetDlgItem(hDlg, IDC_USERCONST);
+ PostMessage(hCtrl, EM_LIMITTEXT, (WPARAM) MAX_UCONST_LEN, 0);
+ SendMessage(hCtrl, WM_SETTEXT, 0, (LPARAM) CurrentConvertOptions->UserConstant);
+
+ hCtrl = GetDlgItem(hDlg, IDC_GROUPCONST);
+ PostMessage(hCtrl, EM_LIMITTEXT, (WPARAM) MAX_UCONST_LEN, 0);
+ SendMessage(hCtrl, WM_SETTEXT, 0, (LPARAM) CurrentConvertOptions->GroupConstant);
+
+ // Now init the radio buttons correctly
+ CheckRadioButton(hDlg, IDC_RADIO1, IDC_RADIO3, IDC_RADIO1 + CurrentConvertOptions->PasswordOption);
+ CheckRadioButton(hDlg, IDC_RADIO4, IDC_RADIO7, IDC_RADIO4 + CurrentConvertOptions->UserNameOption);
+ CheckRadioButton(hDlg, IDC_RADIO8, IDC_RADIO10, IDC_RADIO8 + CurrentConvertOptions->GroupNameOption);
+
+ // Do the controls in the defaults section
+ hCtrl = GetDlgItem(hDlg, IDC_CHKSUPER);
+ if (CurrentConvertOptions->SupervisorDefaults)
+ SendMessage(hCtrl, BM_SETCHECK, 1, 0);
+ else
+ SendMessage(hCtrl, BM_SETCHECK, 0, 0);
+
+ hCtrl = GetDlgItem(hDlg, IDC_CHKADMIN);
+ if (CurrentConvertOptions->AdminAccounts)
+ SendMessage(hCtrl, BM_SETCHECK, 1, 0);
+ else
+ SendMessage(hCtrl, BM_SETCHECK, 0, 0);
+
+ if (FPNWChk) {
+ hCtrl = GetDlgItem(hDlg, IDC_CHKFPNW);
+ if (CurrentConvertOptions->NetWareInfo)
+ SendMessage(hCtrl, BM_SETCHECK, 1, 0);
+ else
+ SendMessage(hCtrl, BM_SETCHECK, 0, 0);
+ } else
+ SendMessage(hCtrl, BM_SETCHECK, 0, 0);
+
+ // Now for the Advanced >> area..
+ hCtrl = GetDlgItem(hDlg, IDC_CHKTRUSTED);
+ if (CurrentConvertOptions->UseTrustedDomain)
+ SendMessage(hCtrl, BM_SETCHECK, 1, 0);
+ else
+ SendMessage(hCtrl, BM_SETCHECK, 0, 0);
+
+} // UserDialogSetup
+
+
+
+/*+-------------------------------------------------------------------------+
+ | ShowArea()
+ |
+ +-------------------------------------------------------------------------+*/
+void ShowArea(BOOL fShowDefAreaOnly, HWND hDlg, HWND hWndDefArea) {
+ RECT rcDlg, rcDefArea;
+ TCHAR szDlgDims[25];
+ char szaDlgDims[25];
+ HWND hWndChild;
+ RECT rc;
+
+ // Save original width and height of dialog box
+ GetWindowRect(hDlg, &rcDlg);
+
+ // Retrieve coordinates for default area window.
+ GetWindowRect(hWndDefArea, &rcDefArea);
+
+ hWndChild = GetFirstChild(hDlg);
+
+ for (; hWndChild != NULL; hWndChild = GetNextSibling(hWndChild)) {
+ // Calculate rectangle occupied by child window in sreen coordinates
+ GetWindowRect(hWndChild, &rc);
+
+ // Enable/Disable child if its:
+ // right edge is >= the right edge of hWndDefArea.
+ // bottom edge is >= the bottom edge of hWndDefArea.
+ if ((rc.right >= rcDefArea.right) || (rc.bottom >= rcDefArea.bottom))
+ EnableWindow(hWndChild, !fShowDefAreaOnly);
+ }
+
+ if (fShowDefAreaOnly) {
+ wsprintf(szDlgDims, TEXT("%05u %05u"), rcDlg.right - rcDlg.left, rcDlg.bottom - rcDlg.top);
+
+ SetStyleOff(hWndDefArea, SS_BLACKRECT);
+ SetStyleOn(hWndDefArea, SS_LEFT);
+ SetWindowText(hWndDefArea, szDlgDims);
+
+ // Resize dialog box to fit only default area.
+ SetWindowPos(hDlg, NULL, 0, 0, rcDefArea.right - rcDlg.left,
+ rcDefArea.bottom - rcDlg.top, SWP_NOZORDER | SWP_NOMOVE);
+
+ // Make sure that the Default area box is hidden.
+ ShowWindow(hWndDefArea, SW_HIDE);
+ } else {
+ GetWindowText(hWndDefArea, szDlgDims, sizeof(szDlgDims));
+
+ WideCharToMultiByte( CP_ACP, 0, szDlgDims, -1, szaDlgDims, sizeof(szDlgDims), NULL, NULL );
+
+ // Restore dialog box to its original size.
+ SetWindowPos(hDlg, NULL, 0, 0, atoi(szaDlgDims), atoi(szaDlgDims + 6),
+ SWP_NOZORDER | SWP_NOMOVE);
+ }
+
+
+} // ShowArea
+
+
+/*+-------------------------------------------------------------------------+
+ | UserFileGet()
+ |
+ +-------------------------------------------------------------------------+*/
+DWORD UserFileGet(HWND hwnd, LPTSTR FilePath) {
+ OPENFILENAME ofn;
+ TCHAR szDirName[MAX_PATH + 1];
+ TCHAR szFile[MAX_PATH + 1], szFileTitle[MAX_PATH + 1];
+ UINT i, cbString;
+ TCHAR chReplace;
+ TCHAR szFilter[256];
+ BOOL Found = FALSE;
+ LPTSTR szExt;
+
+ szExt = Lids(IDS_S_37);
+
+ lstrcpy(szDirName, ProgPath);
+ lstrcpy(szFile, TEXT(""));
+
+ if ((cbString = LoadString(hInst, IDS_USERFILTERSTRING, szFilter, sizeof(szFilter))) == 0) {
+ // Error occured
+ return 1L;
+ }
+
+ chReplace = szFilter[cbString - 1]; // Retrieve wild character
+
+ for (i = 0; szFilter[i] != TEXT('\0'); i++) {
+ if (szFilter[i] == chReplace)
+ szFilter[i] = TEXT('\0');
+ }
+
+ // Set all structure members to zero
+ memset(&ofn, 0, sizeof(OPENFILENAME));
+
+ ofn.lStructSize = sizeof(OPENFILENAME);
+ ofn.hwndOwner = hwnd;
+ ofn.lpstrFilter = szFilter;
+ ofn.nFilterIndex = 1;
+ ofn.lpstrFile = szFile;
+ ofn.nMaxFile = sizeof(szFile);
+ ofn.lpstrFileTitle = szFileTitle;
+ ofn.nMaxFileTitle = sizeof(szFileTitle);
+ ofn.lpstrInitialDir = szDirName;
+ ofn.lpstrDefExt = szExt;
+ ofn.Flags = OFN_HIDEREADONLY | OFN_NOCHANGEDIR | OFN_PATHMUSTEXIST;
+
+ if (GetOpenFileName(&ofn)) {
+ // Don't really need to open it yet - just copy the data
+
+ // If no path then append .
+ i = 0;
+ while (!Found && (ofn.lpstrFile[i] != TEXT('\0'))) {
+ if ((ofn.lpstrFile[i] == TEXT(':')) || (ofn.lpstrFile[i] == TEXT('\\')))
+ Found = TRUE;
+ i++;
+ }
+
+ lstrcpy(FilePath, TEXT(""));
+
+ if (!Found)
+ lstrcpy(FilePath, TEXT(".\\"));
+
+ lstrcat(FilePath, ofn.lpstrFile);
+ return 0L;
+ } else {
+ // Couldn't open the dang file
+ return 1L;
+ }
+
+} // UserFileGet
+
+
+/*+-------------------------------------------------------------------------+
+ | DlgUsers_TrustedSetup()
+ |
+ +-------------------------------------------------------------------------+*/
+void DlgUsers_TrustedSetup(HWND hDlg) {
+ HWND hCtrl;
+ ULONG i;
+ static TRUSTED_DOMAIN_LIST *TList = NULL;
+
+ NTTrustedDomainsEnum(DestServ, &TList);
+
+ if (TList != NULL) {
+ hCtrl = GetDlgItem(hDlg, IDC_TRUSTED);
+
+ for (i = 0; i < TList->Count; i++)
+ SendMessage(hCtrl, CB_ADDSTRING, (WPARAM) 0, (LPARAM) TList->Name[i]);
+
+ }
+
+ EnableWindow(GetDlgItem(hDlg, IDC_ADVANCED), FALSE);
+ ShowArea(FALSE, hDlg, GetDlgItem(hDlg, IDC_DEFAULTBOX));
+
+ // Toggle the advanced controls based on the main user toggle
+ hCtrl = GetDlgItem(hDlg, IDC_CHKUSERS);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 1) {
+ hCtrl = GetDlgItem(hDlg, IDC_CHKTRUSTED);
+ EnableWindow(hCtrl, TRUE);
+
+ hCtrl = GetDlgItem(hDlg, IDC_TRUSTED);
+ EnableWindow(hCtrl, TRUE);
+ SetFocus(GetDlgItem(hDlg, IDC_CHKTRUSTED));
+ } else {
+ hCtrl = GetDlgItem(hDlg, IDC_CHKTRUSTED);
+ EnableWindow(hCtrl, FALSE);
+
+ hCtrl = GetDlgItem(hDlg, IDC_TRUSTED);
+ EnableWindow(hCtrl, FALSE);
+ SetFocus(GetDlgItem(hDlg, IDHELP));
+ }
+
+
+ if ((TList == NULL) || (TList->Count = 0)) {
+ EnableWindow(GetDlgItem(hDlg, IDC_CHKTRUSTED), FALSE);
+ EnableWindow(GetDlgItem(hDlg, IDC_TRUSTED), FALSE);
+ SetFocus(GetDlgItem(hDlg, IDHELP));
+ } else {
+ // Select the trusted domain (or first one if none currently selected)
+ if (CurrentConvertOptions->TrustedDomain != NULL)
+ SendMessage(GetDlgItem(hDlg, IDC_TRUSTED), CB_SELECTSTRING, (WPARAM) -1, (LPARAM) CurrentConvertOptions->TrustedDomain->Name);
+ else
+ SendMessage(GetDlgItem(hDlg, IDC_TRUSTED), CB_SETCURSEL, 0, 0);
+
+ // if the checkbox is set then enable the edit field
+ if (!CurrentConvertOptions->UseTrustedDomain)
+ EnableWindow(GetDlgItem(hDlg, IDC_TRUSTED), FALSE);
+
+ }
+
+ // Free up the list as we don't need it anymore (it is in the combo-box)
+ if (TList) {
+ FreeMemory(TList);
+ TList = NULL;
+ }
+
+} // DlgUsers_TrustedSetup
+
+
+/*+-------------------------------------------------------------------------+
+ | MappingFileEdit()
+ |
+ +-------------------------------------------------------------------------+*/
+BOOL MappingFileEdit(HWND hDlg) {
+ HWND hCtrl;
+ static TCHAR MappingFile[MAX_PATH + 1];
+ static char FileNameA[MAX_PATH + 1];
+ static char CmdLine[MAX_PATH + 1 + 12]; // Editor + file
+ TCHAR drive[MAX_DRIVE + 1];
+ TCHAR dir[MAX_PATH + 1];
+ TCHAR fname[MAX_PATH + 1];
+ TCHAR ext[_MAX_EXT + 1];
+ HANDLE hFile;
+ UINT uReturn;
+ BOOL ret = TRUE;
+ BOOL Browse = FALSE;
+
+ do {
+ // First check filename
+ hCtrl = GetDlgItem(hDlg, IDC_MAPPINGFILE);
+ * (WORD *)MappingFile = sizeof(MappingFile);
+ SendMessage(hCtrl, EM_GETLINE, 0, (LPARAM) MappingFile);
+ lsplitpath(MappingFile, drive, dir, fname, ext);
+
+ // Make sure a file name is specified
+ if (Browse || (lstrlen(fname) == 0)) {
+ // No name was specified, so let the user browse for a file
+ if (!UserFileGet(hDlg, MappingFile)) {
+ hCtrl = GetDlgItem(hDlg, IDC_MAPPINGFILE);
+ SendMessage(hCtrl, WM_SETTEXT, 0, (LPARAM) MappingFile);
+ SetFocus(hCtrl);
+ lsplitpath(MappingFile, drive, dir, fname, ext);
+ } else
+ return FALSE;
+ }
+
+ Browse = FALSE;
+
+ // remake path so it is fully qualified
+ if ((drive[0] == TEXT('\0')) && (dir[0] == TEXT('\0')))
+ lstrcpy(dir, ProgPath);
+
+ if (ext[0] == TEXT('\0'))
+ lstrcpy(ext, Lids(IDS_S_36));
+
+ lmakepath(MappingFile, drive, dir, fname, ext);
+
+ // Now make sure the file exists and is accessible
+
+ WideCharToMultiByte(CP_ACP, 0, MappingFile, -1, FileNameA, sizeof(FileNameA), NULL, NULL);
+
+ hFile = CreateFileA( FileNameA, GENERIC_READ, 0,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
+
+ if (hFile == INVALID_HANDLE_VALUE) {
+ // Couldn't open the file, report to user
+ if (MessageBox(hDlg, Lids(IDS_NOEDITMAP), Lids(IDS_TXTWARNING), MB_YESNO | MB_ICONQUESTION) == IDYES)
+ Browse = TRUE;
+ else {
+ // get out of loop as nothing more to do
+ hCtrl = GetDlgItem(hDlg, IDC_MAPPINGFILE);
+ SetFocus(hCtrl);
+ ret = FALSE;
+ }
+ } else {
+ // Could create a new file so make the mapping file...
+ CloseHandle(hFile);
+ CursorHourGlass();
+ wsprintfA(CmdLine, "Notepad %s", FileNameA);
+ uReturn = WinExec(CmdLine, SW_SHOW);
+ CursorNormal();
+ }
+
+ } while (Browse);
+
+ // Check mapping file check-box, if it's not check'd then do so
+ if (ret) {
+ hCtrl = GetDlgItem(hDlg, IDC_CHKMAPPING);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) != 1)
+ SendMessage(hCtrl, BM_SETCHECK, 1, 0);
+ }
+
+ return ret;
+
+} // MappingFileEdit
+
+
+/*+-------------------------------------------------------------------------+
+ | DlgUsers()
+ |
+ +-------------------------------------------------------------------------+*/
+LRESULT CALLBACK DlgUsers(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
+ HWND hCtrl;
+ int wmId, wmEvent;
+ static short UserNameTab, GroupNameTab, PasswordsTab, DefaultsTab;
+ static BOOL TrustedEnabled;
+ TCHAR TrustedDomain[MAX_PATH + 1];
+ static TCHAR MappingFile[MAX_PATH + 1];
+ DWORD dwIndex;
+
+ switch (message) {
+
+ case WM_INITDIALOG:
+ // Center the dialog over the application window
+ CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
+
+ // Setup for Advanced >> button
+ ShowArea(TRUE, hDlg, GetDlgItem(hDlg, IDC_DEFAULTBOX));
+ TrustedEnabled = FALSE;
+
+ // Need to set the check and radio buttons to correct state...
+ UserDialogSetup(hDlg);
+
+ hCtrl = GetDlgItem( hDlg, IDC_TABUSERS );
+
+ PasswordsTab = BookTab_AddItem( hCtrl, Lids(IDS_S_38) );
+ BookTab_SetItemData( hCtrl, PasswordsTab, 1001 );
+
+ UserNameTab = BookTab_AddItem( hCtrl, Lids(IDS_S_39) );
+ BookTab_SetItemData( hCtrl, UserNameTab, 1002 );
+
+ GroupNameTab = BookTab_AddItem( hCtrl, Lids(IDS_S_40) );
+ BookTab_SetItemData( hCtrl, GroupNameTab, 1003 );
+
+ DefaultsTab = BookTab_AddItem( hCtrl, Lids(IDS_S_41) );
+ BookTab_SetItemData( hCtrl, DefaultsTab, 1004 );
+
+ BookTab_SetCurSel(hCtrl, PasswordsTab);
+
+ // now need to reset the Advanced button to the correct state and also the
+ // Advanced display area...
+ if (CurrentConvertOptions->UseTrustedDomain) {
+ TrustedEnabled = TRUE;
+ DlgUsers_TrustedSetup(hDlg);
+ }
+
+ // Weirdness to initially enable controls on the tab control -
+ // looks like a bug in the tab control.
+ SetFocus(hCtrl);
+ hCtrl = GetDlgItem( hDlg, IDOK );
+ SetFocus(hCtrl);
+ PostMessage(hDlg, BTN_SELCHANGE, 0, 0);
+
+ return (TRUE);
+
+ case BTN_SELCHANGE:
+
+ hCtrl = GetDlgItem( hDlg, IDC_TABUSERS );
+ TabSelection = BookTab_GetCurSel(hCtrl);
+
+ if (TabSelection == UserNameTab) {
+ Passwords_Toggle(hDlg, FALSE);
+ Defaults_Toggle(hDlg, FALSE);
+ DuplicateGroups_Toggle(hDlg, FALSE);
+ DuplicateUsers_Toggle(hDlg, TRUE);
+ }
+
+ if (TabSelection == GroupNameTab) {
+ Passwords_Toggle(hDlg, FALSE);
+ Defaults_Toggle(hDlg, FALSE);
+ DuplicateUsers_Toggle(hDlg, FALSE);
+ DuplicateGroups_Toggle(hDlg, TRUE);
+ }
+
+ if (TabSelection == PasswordsTab) {
+ DuplicateUsers_Toggle(hDlg, FALSE);
+ Defaults_Toggle(hDlg, FALSE);
+ DuplicateGroups_Toggle(hDlg, FALSE);
+ Passwords_Toggle(hDlg, TRUE);
+ }
+
+ if (TabSelection == DefaultsTab) {
+ Passwords_Toggle(hDlg, FALSE);
+ DuplicateUsers_Toggle(hDlg, FALSE);
+ DuplicateGroups_Toggle(hDlg, FALSE);
+ Defaults_Toggle(hDlg, TRUE);
+ }
+
+ return (TRUE);
+
+ case WM_COMMAND:
+ wmId = LOWORD(wParam);
+ wmEvent = HIWORD(wParam);
+
+ switch (wmId) {
+ case IDOK:
+ if (UserDialogVerify(hDlg)) {
+ UserDialogSave(hDlg);
+ EndDialog(hDlg, 0);
+ }
+
+ return (TRUE);
+ break;
+
+ case IDCANCEL:
+ EndDialog(hDlg, 0);
+ return (TRUE);
+ break;
+
+ case IDHELP:
+ if (TrustedEnabled)
+ WinHelp(hDlg, HELP_FILE, HELP_CONTEXT, (DWORD) IDC_HELP_USERADV);
+ else
+ WinHelp(hDlg, HELP_FILE, HELP_CONTEXT, (DWORD) IDC_HELP_USER);
+
+ return (TRUE);
+ break;
+
+ case IDC_ADVANCED:
+ TrustedEnabled = TRUE;
+ DlgUsers_TrustedSetup(hDlg);
+ return (TRUE);
+ break;
+
+ case IDC_CHKUSERS:
+ hCtrl = GetDlgItem(hDlg, IDC_CHKUSERS);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 1)
+ UserDialogToggle(hDlg, TRUE);
+ else
+ UserDialogToggle(hDlg, FALSE);
+
+ PostMessage(hDlg, BTN_SELCHANGE, 0, 0);
+ return (TRUE);
+ break;
+
+ case IDC_CHKMAPPING:
+ hCtrl = GetDlgItem(hDlg, IDC_CHKMAPPING);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 1)
+ Mapping_Toggle(hDlg, FALSE);
+ else
+ Mapping_Toggle(hDlg, TRUE);
+
+ return (TRUE);
+ break;
+
+ case IDC_CHKTRUSTED:
+ hCtrl = GetDlgItem(hDlg, IDC_CHKTRUSTED);
+ if (SendMessage(hCtrl, BM_GETCHECK, 0, 0) == 1) {
+ hCtrl = GetDlgItem(hDlg, IDC_TRUSTED);
+ EnableWindow(hCtrl, TRUE);
+
+ // Get trusted domain
+ dwIndex = SendMessage(hCtrl, CB_GETCURSEL, 0, 0);
+ if (dwIndex != CB_ERR) {
+ // Get the domain name and then try to add it to our lists
+ SendMessage(hCtrl, CB_GETLBTEXT, (WPARAM) dwIndex, (LPARAM) TrustedDomain);
+ CurrentConvertOptions->TrustedDomain = NTTrustedDomainSet(hDlg, DestServ, TrustedDomain);
+ CurrentConvertOptions->UseTrustedDomain = TRUE;
+ } else
+ CurrentConvertOptions->UseTrustedDomain = FALSE;
+
+ } else {
+ hCtrl = GetDlgItem(hDlg, IDC_TRUSTED);
+ EnableWindow(hCtrl, FALSE);
+ CurrentConvertOptions->UseTrustedDomain = FALSE;
+ }
+
+ // Toggled trust relationship so setup FPNW info accordingly
+ FPNWChk = IsFPNW(UserServerNameGet(DServ, CurrentConvertOptions));
+ if (TabSelection == DefaultsTab)
+ Defaults_Toggle(hDlg, TRUE);
+
+ return (TRUE);
+ break;
+
+ case IDC_TRUSTED:
+ if (wmEvent == CBN_SELCHANGE) {
+ // A new trusted domain was selected, update FPNW flag
+ hCtrl = GetDlgItem(hDlg, IDC_TRUSTED);
+ EnableWindow(hCtrl, TRUE);
+
+ // Get trusted domain
+ dwIndex = SendMessage(hCtrl, CB_GETCURSEL, 0, 0);
+ if (dwIndex != CB_ERR) {
+ // Get the domain name and then try to add it to our lists
+ SendMessage(hCtrl, CB_GETLBTEXT, (WPARAM) dwIndex, (LPARAM) TrustedDomain);
+ CurrentConvertOptions->TrustedDomain = NTTrustedDomainSet(hDlg, DestServ, TrustedDomain);
+ CurrentConvertOptions->UseTrustedDomain = TRUE;
+ } else
+ CurrentConvertOptions->UseTrustedDomain = FALSE;
+
+ FPNWChk = IsFPNW(UserServerNameGet(DServ, CurrentConvertOptions));
+ CurrentConvertOptions->NetWareInfo = FPNWChk;
+
+ if (TabSelection == DefaultsTab)
+ Defaults_Toggle(hDlg, TRUE);
+
+ }
+ break;
+
+ case IDC_BTNMAPPINGFILE:
+ // Get anything in the text box
+ hCtrl = GetDlgItem(hDlg, IDC_MAPPINGFILE);
+ * (WORD *)MappingFile = sizeof(MappingFile);
+ SendMessage(hCtrl, EM_GETLINE, 0, (LPARAM) MappingFile);
+
+ // invoke the creation routine
+ MapFileCreate(hDlg, MappingFile, SourceServ);
+
+ // reset edit-box to whatever the returned filename is
+ SendMessage(hCtrl, WM_SETTEXT, 0, (LPARAM) MappingFile);
+
+ if (SendMessage(hCtrl, EM_LINELENGTH, 0, 0)) {
+ hCtrl = GetDlgItem(hDlg, IDC_CHKMAPPING);
+ SendMessage(hCtrl, BM_SETCHECK, 1, 0);
+ } else {
+ hCtrl = GetDlgItem(hDlg, IDC_CHKMAPPING);
+ SendMessage(hCtrl, BM_SETCHECK, 0, 0);
+ }
+
+ return (TRUE);
+ break;
+
+ case IDC_BTNMAPPINGEDIT:
+ MappingFileEdit(hDlg);
+ return (TRUE);
+ break;
+
+ case IDC_PWCONST:
+
+ if (wmEvent == EN_CHANGE)
+ CheckRadioButton(hDlg, IDC_RADIO1, IDC_RADIO3, IDC_RADIO3);
+
+ break;
+
+ case IDC_USERCONST:
+
+ if (wmEvent == EN_CHANGE)
+ CheckRadioButton(hDlg, IDC_RADIO4, IDC_RADIO7, IDC_RADIO7);
+
+ break;
+
+ case IDC_GROUPCONST:
+
+ if (wmEvent == EN_CHANGE)
+ CheckRadioButton(hDlg, IDC_RADIO8, IDC_RADIO10, IDC_RADIO10);
+
+ break;
+
+ }
+ break;
+ }
+
+ return (FALSE); // Didn't process the message
+
+ lParam;
+} // DlgUsers
+
+
+
+/*+-------------------------------------------------------------------------+
+ | UserOptions_Do()
+ |
+ +-------------------------------------------------------------------------+*/
+void UserOptions_Do(HWND hDlg, void *ConvOptions, SOURCE_SERVER_BUFFER *SourceServer, DEST_SERVER_BUFFER *DestServer) {
+ DLGPROC lpfnDlg;
+
+ SServ = SourceServer;
+ DServ = DestServer;
+ SourceServ = SourceServer->Name;
+ DestServ = DestServer->Name;
+
+ FPNWChk = DServ->IsFPNW;
+ CurrentConvertOptions = (CONVERT_OPTIONS *) ConvOptions;
+ lpfnDlg = MakeProcInstance((DLGPROC)DlgUsers, hInst);
+ DialogBox(hInst, TEXT("DlgNewUsers"), hDlg, lpfnDlg) ;
+ FreeProcInstance(lpfnDlg);
+} // UserOptions_Do
+
diff --git a/private/nw/convert/nwconv/userdlg.h b/private/nw/convert/nwconv/userdlg.h
new file mode 100644
index 000000000..dcb91d6a0
--- /dev/null
+++ b/private/nw/convert/nwconv/userdlg.h
@@ -0,0 +1,49 @@
+/*+-------------------------------------------------------------------------+
+ | Copyright 1993-1994 (C) Microsoft Corporation - All rights reserved. |
+ +-------------------------------------------------------------------------+*/
+
+#ifndef _USERDLG_
+#define _USERDLG_
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+
+typedef struct _CONVERT_OPTIONS {
+ BOOL TransferUserInfo;
+
+ BOOL UseMappingFile;
+ TCHAR MappingFile[MAX_PATH + 1];
+
+ int PasswordOption;
+ TCHAR PasswordConstant[MAX_PW_LEN + 1];
+ BOOL ForcePasswordChange;
+
+ int UserNameOption;
+ TCHAR UserConstant[MAX_UCONST_LEN + 1];
+
+ int GroupNameOption;
+ TCHAR GroupConstant[MAX_UCONST_LEN + 1];
+
+ BOOL SupervisorDefaults;
+ BOOL AdminAccounts;
+ BOOL NetWareInfo;
+
+ BOOL UseTrustedDomain;
+ DOMAIN_BUFFER *TrustedDomain;
+} CONVERT_OPTIONS;
+
+void UserOptionsDefaultsSet(void *cvto);
+void UserOptionsDefaultsReset();
+void UserOptionsInit(void **cvto);
+void UserOptionsLoad(HANDLE hFile, void **lpcvto);
+void UserOptionsSave(HANDLE hFile, void *cvto);
+
+void UserOptions_Do(HWND hDlg, void *ConvOptions, SOURCE_SERVER_BUFFER *SourceServer, DEST_SERVER_BUFFER *DestServer);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/nw/convert/nwconv/usrprop.c b/private/nw/convert/nwconv/usrprop.c
new file mode 100644
index 000000000..c52603ee0
--- /dev/null
+++ b/private/nw/convert/nwconv/usrprop.c
@@ -0,0 +1,592 @@
+/*++
+
+Copyright (c) 1993-1993 Microsoft Corporation
+
+Module Name:
+
+ usrprop.c
+
+Abstract:
+
+ This module implements QueryUserProperty() and SetUserProperty()
+ which read and write NetWare Properties to the UserParms field.
+
+Author:
+
+ Andy Herron (andyhe) 24-May-1993
+ Congpa You (CongpaY) 28-Oct-1993 Seperated SetUserProperty() and
+ QueryUserProperty() out from usrprop.c
+ in ncpsrv\svcdlls\ncpsvc\libbind,
+ modified the code and fixed a few
+ existing problems.
+
+Revision History:
+
+--*/
+
+#include "nt.h"
+#include "ntrtl.h"
+#include "nturtl.h"
+#include "ntioapi.h"
+#include "windef.h"
+#include "winbase.h"
+#include "stdio.h"
+#include "stdlib.h"
+#include "winuser.h"
+
+#include "usrprop.h"
+
+#define NCP_SET 0x02 /* Series of Object ID numbers, each 4
+ bytes long */
+
+//
+// All internal (opaque) structures are listed here since no one else
+// needs to reference them.
+//
+
+//
+// The user's Parameter field is mapped out to a structure that contains
+// the backlevel 48 WCHARs for Mac/Ras compatibility plus a new structure
+// that is basically an array of chars that make up a property name plus
+// a property value.
+//
+
+//
+// This is the structure for an individual property. Note that there are
+// no null terminators in this.
+//
+typedef struct _USER_PROPERTY {
+ WCHAR PropertyLength; // length of property name
+ WCHAR ValueLength; // length of property value
+ WCHAR PropertyFlag; // type of property (1 = set, 2 = item)
+ WCHAR Property[1]; // start of property name, followed by value
+} USER_PROPERTY, *PUSER_PROPERTY;
+
+//
+// This is the structure that maps the beginning of the user's Parameters
+// field. It is only separate so that we can do a sizeof() without including
+// the first property, which may or may not be there.
+//
+
+typedef struct _USER_PROPERTIES_HDR {
+ WCHAR BacklevelParms[48]; // RAS & Mac data stored here.
+ WCHAR PropertySignature; // signature that we can look for.
+ WCHAR PropertyCount; // number of properties present.
+} USER_PROPERTIES_HDR, *PUSER_PROPERTIES_HDR;
+
+//
+// This structure maps out the whole of the user's Parameters field when
+// the user properties structure is present and at least one property is
+// defined.
+//
+
+typedef struct _USER_PROPERTIES {
+ USER_PROPERTIES_HDR Header;
+ USER_PROPERTY FirstProperty;
+} USER_PROPERTIES, *PUSER_PROPERTIES;
+
+//
+// forward references
+//
+
+NTSTATUS
+UserPropertyAllocBlock (
+ IN PUNICODE_STRING Existing,
+ IN ULONG DesiredLength,
+ IN OUT PUNICODE_STRING New
+ );
+
+BOOL
+FindUserProperty (
+ PUSER_PROPERTIES UserProperties,
+ LPWSTR Property,
+ PUSER_PROPERTY *pUserProperty,
+ USHORT *pCount
+ );
+
+VOID
+RemoveUserProperty (
+ UNICODE_STRING *puniUserParms,
+ PUSER_PROPERTY UserProperty,
+ USHORT Count,
+ BOOL *Update
+ );
+
+NTSTATUS
+SetUserProperty (
+ IN LPWSTR UserParms,
+ IN LPWSTR Property,
+ IN UNICODE_STRING PropertyValue,
+ IN WCHAR PropertyFlag,
+ OUT LPWSTR *pNewUserParms, // memory has to be freed afer use.
+ OUT BOOL *Update
+ )
+/*
+ This function sets a property field in the user's Parameters field.
+*/
+{
+ NTSTATUS status;
+ UNICODE_STRING uniUserParms;
+ UNICODE_STRING uniNewUserParms;
+ USHORT Count = 0;
+ USHORT PropertyLength;
+ USHORT ValueLength;
+ PUSER_PROPERTIES UserProperties;
+ PUSER_PROPERTY UserProperty;
+ LPWSTR PropertyValueString = NULL;
+ INT i;
+ UCHAR *pchValue = NULL;
+
+ // Check if parameters are correct.
+ if (Property == NULL)
+ {
+ return( STATUS_INVALID_PARAMETER );
+ }
+
+ // Initialize output variables.
+ *Update = FALSE;
+ *pNewUserParms = NULL;
+
+ // Converty UserParms to unicode string.
+ uniUserParms.Buffer = UserParms;
+ uniUserParms.Length = UserParms? (lstrlenW(UserParms) + 1)* sizeof (WCHAR)
+ : 0;
+ uniUserParms.MaximumLength = uniUserParms.Length;
+
+ /** Get the length of the property name **/
+
+ PropertyLength = lstrlenW( Property ) * sizeof(WCHAR);
+
+ /** Get the length of the property value **/
+ ValueLength = PropertyValue.Length;
+
+ if (ValueLength != 0)
+ {
+ // Converty property value to asci string so that
+ // if property value is 0, it can be stored correctly.
+
+ PropertyValueString = (LPWSTR) LocalAlloc (LPTR, (ValueLength+1)*sizeof (WCHAR));
+
+ pchValue = (UCHAR *) PropertyValue.Buffer;
+
+ // BUGBUG. Since wsprint converts 0x00 to 20 30 (20 is
+ // space and 30 is 0), sscanf converts 20 30 to 0. If the
+ // value is uncode string, this convertsion would not
+ // convert back to original value. So if we want to store
+ // some value in the UserParms, we have to pass in ansi
+ // string.
+
+ for (i = 0; i < ValueLength; i++)
+ {
+ wsprintfA ((PCHAR)(PropertyValueString+i), "%02x", *(pchValue+i));
+ }
+
+ *(PropertyValueString+ValueLength) = 0;
+ ValueLength = ValueLength * sizeof (WCHAR);
+ }
+
+ //
+ // check that user has valid property structure , if not, create one
+ //
+
+ if (UserParms != NULL)
+ {
+ Count = (lstrlenW (UserParms) + 1)* sizeof(WCHAR);
+ }
+
+ if (Count < sizeof( USER_PROPERTIES))
+ {
+ Count = sizeof( USER_PROPERTIES_HDR ) + sizeof(WCHAR);
+ }
+
+ if (ValueLength > 0)
+ {
+ Count += sizeof( USER_PROPERTY ) + PropertyLength + ValueLength;
+ }
+
+ if (Count > 0x7FFF)
+ {
+ // can't be bigger than 32K of user parms.
+ return (STATUS_BUFFER_OVERFLOW);
+ }
+
+ status = UserPropertyAllocBlock( &uniUserParms,
+ Count,
+ &uniNewUserParms );
+
+ if ( !NT_SUCCESS(status) )
+ {
+ return status;
+ }
+
+ // Make the output pNewUserParms point to uniNewUserPams's buffer
+ // which is the new UserParms string.
+
+ *pNewUserParms = uniNewUserParms.Buffer;
+
+ UserProperties = (PUSER_PROPERTIES) uniNewUserParms.Buffer;
+
+ if (FindUserProperty (UserProperties,
+ Property,
+ &UserProperty,
+ &Count))
+ {
+ RemoveUserProperty ( &uniNewUserParms,
+ UserProperty,
+ Count,
+ Update);
+ }
+
+ //
+ // If the new value of the property is not null, add it.
+ //
+
+ if (ValueLength > 0) {
+
+ // find the end of the parameters list
+
+ UserProperty = &(UserProperties->FirstProperty);
+
+ for (Count = 1; Count <= UserProperties->Header.PropertyCount; Count++)
+ {
+ UserProperty = (PUSER_PROPERTY)
+ ((LPSTR)((LPSTR) UserProperty +
+ sizeof(USER_PROPERTY) + // length of entry
+ UserProperty->PropertyLength +
+ UserProperty->ValueLength -
+ sizeof(WCHAR))); // for Property[0]
+ }
+
+ //
+ // append it to the end and update length of string
+ //
+
+ UserProperty->PropertyFlag = (PropertyFlag & NCP_SET) ?
+ USER_PROPERTY_TYPE_SET :
+ USER_PROPERTY_TYPE_ITEM;
+
+ UserProperty->PropertyLength = PropertyLength;
+ UserProperty->ValueLength = ValueLength;
+
+ RtlCopyMemory( &(UserProperty->Property[0]),
+ Property,
+ PropertyLength );
+
+ RtlCopyMemory( &(UserProperty->Property[PropertyLength / sizeof(WCHAR)]),
+ PropertyValueString,
+ ValueLength );
+
+ uniNewUserParms.Length +=
+ sizeof(USER_PROPERTY) + // length of entry
+ PropertyLength + // length of property name string
+ ValueLength - // length of value string
+ sizeof(WCHAR); // account for WCHAR Property[1]
+
+ UserProperties->Header.PropertyCount++;
+
+ *Update = TRUE;
+ }
+
+ // UserParms is already null terminated. We don't need to set the
+ // end of UserParms to be NULL since we zero init the buffer already.
+
+ return( status );
+} // SetUserProperty
+
+#define MAPHEXTODIGIT(x) ( x >= '0' && x <= '9' ? (x-'0') : \
+ x >= 'A' && x <= 'F' ? (x-'A'+10) : \
+ x >= 'a' && x <= 'f' ? (x-'a'+10) : 0 )
+
+
+NTSTATUS
+QueryUserProperty (
+ IN LPWSTR UserParms,
+ IN LPWSTR PropertyName,
+ OUT PWCHAR PropertyFlag,
+ OUT PUNICODE_STRING PropertyValue
+ )
+/*
+ This routine returns a user definable property value as it is stored
+ in the user's Parameters field. Note that the RAS/MAC fields are
+ stripped before we start processing user properties.
+*/
+{
+ USHORT PropertyNameLength;
+ USHORT Count;
+ PUSER_PROPERTY UserProperty;
+ WCHAR *Value;
+ UINT i;
+ CHAR *PropertyValueString;
+ CHAR *pchValue;
+
+ // Set PropertyValue->Length to 0 initially. If the property is not found
+ // it will still be 0 on exit.
+
+ PropertyValue->Length = 0;
+ PropertyValue->Buffer = NULL;
+
+ PropertyNameLength = lstrlenW(PropertyName) * sizeof(WCHAR);
+
+ // Check if UserParms have the right structure.
+
+ if (FindUserProperty ((PUSER_PROPERTIES) UserParms,
+ PropertyName,
+ &UserProperty,
+ &Count) )
+ {
+
+ Value = (LPWSTR)(LPSTR)((LPSTR) &(UserProperty->Property[0]) +
+ PropertyNameLength);
+
+ //
+ // Found the requested property
+ //
+
+ //
+ // Copy the property flag.
+ //
+
+ if (PropertyFlag)
+ *PropertyFlag = UserProperty->PropertyFlag;
+
+ // Allocate memory for PropertyValue->Buffer
+
+ PropertyValueString = LocalAlloc ( LPTR, UserProperty->ValueLength+1);
+ PropertyValue->Buffer = LocalAlloc ( LPTR, UserProperty->ValueLength/sizeof(WCHAR));
+
+ //
+ // Make sure the property value length is valid.
+ //
+ if ((PropertyValue->Buffer == NULL) || (PropertyValueString == NULL))
+ {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // Copy the property value to the buffer.
+ //
+
+ RtlCopyMemory( PropertyValueString,
+ Value,
+ UserProperty->ValueLength );
+
+ pchValue = (CHAR *) PropertyValue->Buffer;
+
+ // Convert from value unicode string to value.
+ for (i = 0; i < UserProperty->ValueLength/sizeof(WCHAR) ; i++)
+ {
+ // sscanf will trash memory.
+ // sscanf( PropertyValueString+2*i, "%2x", pchValue+i);
+
+ pchValue[i] = MAPHEXTODIGIT( PropertyValueString[2*i]) * 16 +
+ MAPHEXTODIGIT( PropertyValueString[2*i+1]);
+ }
+
+ PropertyValue->Length = UserProperty->ValueLength/sizeof(WCHAR);
+ PropertyValue->MaximumLength = UserProperty->ValueLength/sizeof(WCHAR);
+
+ LocalFree( PropertyValueString);
+ }
+
+ return STATUS_SUCCESS;
+} // QueryUserProperty
+
+
+// Common routine used by QueryUserProperty() and SetUserProperty().
+
+BOOL
+FindUserProperty (
+ PUSER_PROPERTIES UserProperties,
+ LPWSTR Property,
+ PUSER_PROPERTY *pUserProperty,
+ USHORT *pCount
+ )
+{
+ BOOL fFound = FALSE;
+ USHORT PropertyLength;
+
+ //
+ // Check if user has valid property structure attached,
+ // pointed to by UserProperties.
+ //
+
+ if ( ( UserProperties != NULL )
+ && ( lstrlenW( (LPWSTR) UserProperties) * sizeof(WCHAR) >
+ sizeof(UserProperties->Header.BacklevelParms))
+ && ( UserProperties->Header.PropertySignature == USER_PROPERTY_SIGNATURE)
+ )
+ {
+ //
+ // user has valid property structure.
+ //
+
+ *pUserProperty = &(UserProperties->FirstProperty);
+
+ PropertyLength = lstrlenW( Property ) * sizeof(WCHAR);
+
+ for ( *pCount = 1; *pCount <= UserProperties->Header.PropertyCount;
+ (*pCount)++ )
+ {
+ if ( ( PropertyLength == (*pUserProperty)->PropertyLength )
+ && ( RtlCompareMemory( &((*pUserProperty)->Property[0]),
+ Property,
+ PropertyLength ) == PropertyLength )
+ )
+ {
+ fFound = TRUE;
+ break;
+ }
+
+ *pUserProperty = (PUSER_PROPERTY)
+ ((LPSTR) (*pUserProperty)
+ + sizeof( USER_PROPERTY )
+ + (*pUserProperty)->PropertyLength
+ + (*pUserProperty)->ValueLength
+ - sizeof(WCHAR)); // for Property[0]
+ }
+ }
+
+ return( fFound );
+} // FindUserProperty
+
+
+// Remove a property field from the User Parms.
+
+VOID
+RemoveUserProperty (
+ UNICODE_STRING *puniUserParms,
+ PUSER_PROPERTY UserProperty,
+ USHORT Count,
+ BOOL *Update
+ )
+{
+ PUSER_PROPERTIES UserProperties;
+ PUSER_PROPERTY NextProperty;
+ USHORT OldParmLength;
+
+ UserProperties = (PUSER_PROPERTIES) puniUserParms->Buffer;
+
+ OldParmLength = sizeof( USER_PROPERTY ) +
+ UserProperty->PropertyLength +
+ UserProperty->ValueLength -
+ sizeof(WCHAR); // for Property[0]
+
+
+ NextProperty = (PUSER_PROPERTY)(LPSTR)((LPSTR) UserProperty + OldParmLength);
+
+ //
+ // if we're not on the last one, copy the remaining buffer up
+ //
+
+ if (Count < UserProperties->Header.PropertyCount) {
+
+ RtlMoveMemory( UserProperty,
+ NextProperty,
+ puniUserParms->Length -
+ ((LPSTR) NextProperty -
+ (LPSTR) puniUserParms->Buffer ));
+ }
+
+ //
+ // Now reduce the length of the buffer by the amount we pulled out
+ //
+
+ puniUserParms->Length -= OldParmLength;
+
+ UserProperties->Header.PropertyCount--;
+
+ *Update = TRUE;
+} // RemoveUserProperty
+
+
+NTSTATUS
+UserPropertyAllocBlock (
+ IN PUNICODE_STRING Existing,
+ IN ULONG DesiredLength,
+ IN OUT PUNICODE_STRING New
+ )
+/*
+ This allocates a larger block for user's parameters and copies the old
+ block in.
+*/
+{
+ PUSER_PROPERTIES UserProperties;
+ CLONG Count;
+ WCHAR *pNewBuff;
+
+
+ //
+ // We will allocate a new buffer to store the new parameters
+ // and copy the existing parameters into it.
+ //
+
+ New->Buffer = LocalAlloc (LPTR, DesiredLength);
+
+ if ( New->Buffer == NULL)
+ {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ New->MaximumLength = (USHORT) DesiredLength;
+
+ if (Existing != NULL)
+ {
+
+ New->Length = Existing->Length;
+
+ RtlCopyMemory( New->Buffer,
+ Existing->Buffer,
+ Existing->Length );
+ }
+ else
+ {
+ New->Length = 0;
+ }
+
+ //
+ // Ensure that we don't have any nulls in our string.
+ //
+
+ for ( Count = 0;
+ Count < New->Length / sizeof(WCHAR);
+ Count++ )
+ {
+ if (*(New->Buffer + Count) == L'\0')
+ {
+ New->Length = (USHORT) Count * sizeof(WCHAR);
+ break;
+ }
+ }
+
+ //
+ // now pad it out with spaces until reached Mac+Ras reserved length
+ //
+
+ pNewBuff = (WCHAR *) New->Buffer + ( New->Length / sizeof(WCHAR) );
+
+ while ( New->Length < sizeof(UserProperties->Header.BacklevelParms))
+ {
+ *( pNewBuff++ ) = L' ';
+ New->Length += sizeof(WCHAR);
+ }
+
+ //
+ // If the signature isn't there, stick it in and set prop count to 0
+ //
+
+ UserProperties = (PUSER_PROPERTIES) New->Buffer;
+
+ if (New->Length < sizeof(USER_PROPERTIES_HDR) ||
+ UserProperties->Header.PropertySignature != USER_PROPERTY_SIGNATURE)
+ {
+
+ UserProperties->Header.PropertySignature = USER_PROPERTY_SIGNATURE;
+ UserProperties->Header.PropertyCount = 0;
+
+ New->Length = sizeof(USER_PROPERTIES_HDR);
+ }
+
+ return STATUS_SUCCESS;
+} // UserPropertyAllocBlock
+
+// usrprop.c eof.
diff --git a/private/nw/convert/nwconv/usrprop.h b/private/nw/convert/nwconv/usrprop.h
new file mode 100644
index 000000000..6c7dbb6e8
--- /dev/null
+++ b/private/nw/convert/nwconv/usrprop.h
@@ -0,0 +1,77 @@
+/*++
+
+Copyright (c) 1993 Micro Computer Systems, Inc.
+
+Module Name:
+
+ usrprop.h
+
+Abstract:
+
+ This is the public include file for some of the functions used by
+ User Manager and Server Manager.
+
+Author:
+
+ Congpa You 02-Dec-1993 Created.
+
+Revision History:
+
+--*/
+
+#ifndef _USRPROP_H_
+#define _USRPROP_H_
+
+#define USER_PROPERTY_SIGNATURE L'P'
+
+#define NWPASSWORD L"NWPassword"
+#define OLDNWPASSWORD L"OldNWPassword"
+#define MAXCONNECTIONS L"MaxConnections"
+#define NWTIMEPASSWORDSET L"NWPasswordSet"
+#define SZTRUE L"TRUE"
+#define GRACELOGINALLOWED L"GraceLoginAllowed"
+#define GRACELOGINREMAINING L"GraceLoginRemaining"
+#define NWLOGONFROM L"NWLogonFrom"
+#define NWHOMEDIR L"NWHomeDir"
+#define NW_PRINT_SERVER_REF_COUNT L"PSRefCount"
+
+#define NWENCRYPTEDPASSWORDLENGTH 8
+
+#define NO_LIMIT 0xffff
+
+#define DEFAULT_MAXCONNECTIONS NO_LIMIT
+#define DEFAULT_NWPASSWORDEXPIRED FALSE
+#define DEFAULT_GRACELOGINALLOWED 6
+#define DEFAULT_GRACELOGINREMAINING 6
+#define DEFAULT_NWLOGONFROM NULL
+#define DEFAULT_NWHOMEDIR NULL
+
+#define USER_PROPERTY_TYPE_ITEM 1
+#define USER_PROPERTY_TYPE_SET 2
+
+//Encryption function
+NTSTATUS ReturnNetwareForm (const char * pszSecretValue,
+ DWORD dwUserId,
+ const WCHAR * pchNWPassword,
+ UCHAR * pchEncryptedNWPassword);
+
+NTSTATUS
+SetUserProperty(
+ IN LPWSTR UserParms,
+ IN LPWSTR Property,
+ IN UNICODE_STRING PropertyValue,
+ IN WCHAR PropertyFlag,
+ OUT LPWSTR * pNewUserParms, // memory has to be freed after use.
+ OUT BOOL * Update
+ );
+
+NTSTATUS
+QueryUserProperty (
+ IN LPWSTR UserParms,
+ IN LPWSTR Property,
+ OUT PWCHAR PropertyFlag,
+ OUT PUNICODE_STRING PropertyValue
+ );
+
+#endif // _USRPROP_H_
+
diff --git a/private/nw/convert/nwconv/utils.c b/private/nw/convert/nwconv/utils.c
new file mode 100644
index 000000000..5f1f03691
--- /dev/null
+++ b/private/nw/convert/nwconv/utils.c
@@ -0,0 +1,830 @@
+/*++
+
+Copyright (c) 1993-1995 Microsoft Corporation
+
+Module Name:
+
+ nwconv.c
+
+Abstract:
+
+
+Author:
+
+ Arthur Hanson (arth) 27-Jul-1994
+
+Revision History:
+
+--*/
+
+
+#include "globals.h"
+
+LPTSTR alpsz[TOTAL_STRINGS]; // String resource array cache.
+static UINT cswitch = 0;
+static HCURSOR hCursor;
+
+
+/////////////////////////////////////////////////////////////////////////
+LPTSTR
+Lids(
+ WORD idsStr
+ )
+
+/*++
+
+Routine Description:
+
+ Returns the requested string from the string table. Caches the
+ strings in an internal buffer. Will return a NULL string if the
+ string can't be loaded.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ WORD idsString;
+ static TCHAR szEmpty[] = TEXT("");
+ TCHAR Buffer[MAX_STRING_SIZE];
+
+ WORD nLen;
+ LPTSTR lpsz;
+
+ idsString = idsStr - IDS_STRINGBASE;
+ if ((idsString == 0) ||( idsString > TOTAL_STRINGS))
+ return(szEmpty);
+
+ // -1 index as table is 0 based and 0 is not a valid string ID.
+ if (alpsz[idsString-1])
+ return((LPTSTR)alpsz[idsString-1]);
+
+ if (!(nLen = LoadString(hInst, idsStr, (LPTSTR) Buffer, MAX_STRING_SIZE)))
+ return(szEmpty);
+
+ if (!(lpsz = AllocMemory((nLen+1) * sizeof(TCHAR))))
+ return(szEmpty);
+
+ lstrcpy((LPTSTR)lpsz, (LPTSTR) Buffer);
+
+ return (alpsz[idsString-1] = lpsz);
+} // Lids
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+StringTableDestroy()
+
+/*++
+
+Routine Description:
+
+ Frees up all the memory allocated in the string table.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ int i;
+
+ for (i=0; i < TOTAL_STRINGS ; i++ ) {
+ if (alpsz[i]) {
+ FreeMemory(alpsz[i]);
+ alpsz[i]=NULL;
+ }
+ }
+} // StringTableDestroy
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+CursorHourGlass()
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ if (!cswitch) {
+ hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
+ ShowCursor(TRUE);
+ }
+
+ cswitch++;
+
+} // CursorHourGlass
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+CursorNormal()
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+
+ if (cswitch == 0)
+ return;
+
+ cswitch--;
+
+ if (!cswitch) {
+ ShowCursor(FALSE);
+ SetCursor(hCursor);
+ }
+
+} // Cursor Normal
+
+
+/////////////////////////////////////////////////////////////////////////
+BOOL
+BitTest(
+ int Bit,
+ BYTE *Bits
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ int i, j;
+
+ i = Bit / 8;
+ j = Bit % 8;
+
+ if ((Bits[i] >> j) & 0x01)
+ return TRUE;
+ else
+ return FALSE;
+
+} // BitTest
+
+
+/////////////////////////////////////////////////////////////////////////
+BOOL
+CenterWindow(
+ HWND hwndChild,
+ HWND hwndParent
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ RECT rChild, rParent;
+ int wChild, hChild, wParent, hParent;
+ int wScreen, hScreen, xNew, yNew;
+ HDC hdc;
+
+ // Get the Height and Width of the child window
+ GetWindowRect (hwndChild, &rChild);
+ wChild = rChild.right - rChild.left;
+ hChild = rChild.bottom - rChild.top;
+
+ // Get the Height and Width of the parent window
+ GetWindowRect (hwndParent, &rParent);
+ wParent = rParent.right - rParent.left;
+ hParent = rParent.bottom - rParent.top;
+
+ // Get the display limits
+ hdc = GetDC (hwndChild);
+ wScreen = GetDeviceCaps (hdc, HORZRES);
+ hScreen = GetDeviceCaps (hdc, VERTRES);
+ ReleaseDC (hwndChild, hdc);
+
+ // Calculate new X position, then adjust for screen
+ xNew = rParent.left + ((wParent - wChild) /2);
+ if (xNew < 0)
+ xNew = 0;
+ else if ((xNew+wChild) > wScreen)
+ xNew = wScreen - wChild;
+
+ // Calculate new Y position, then adjust for screen
+ yNew = rParent.top + ((hParent - hChild) /2);
+ if (yNew < 0)
+ yNew = 0;
+ else if ((yNew+hChild) > hScreen)
+ yNew = hScreen - hChild;
+
+ // Set it, and return
+ return SetWindowPos (hwndChild, NULL, xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
+
+} // CenterWindow
+
+
+/////////////////////////////////////////////////////////////////////////
+TCHAR *
+lstrchr(
+ LPTSTR String,
+ TCHAR c
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ TCHAR *ptrChar = String;
+ BOOL Found = FALSE;
+
+ while(*ptrChar && !Found) {
+ if (*ptrChar == c)
+ Found = TRUE;
+ else
+ ptrChar++;
+ }
+
+ if (Found)
+ return ptrChar;
+ else
+ return NULL;
+
+} // lstrchr
+
+
+/////////////////////////////////////////////////////////////////////////
+BOOL
+IsPath(
+ LPTSTR Path
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG len;
+ LPTSTR ptr;
+
+ len = lstrlen(Path);
+ if (len < 2) // must have a slash and character
+ return FALSE;
+
+ // now know path is at least 2 characters long
+ ptr = Path;
+
+ // if slash anywhere then it has to be a path
+ while (*ptr)
+ if (*ptr == TEXT('\\'))
+ return TRUE;
+ else
+ ptr++;
+
+ // no slash - unless this is a drive then it aint no path.
+ if (Path[1] == TEXT(':'))
+ return TRUE;
+
+ return FALSE;
+
+} // IsPath
+
+
+/////////////////////////////////////////////////////////////////////////
+LPTSTR
+lToStr(
+ ULONG Number
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ static TCHAR String[15];
+ TCHAR tString[15];
+ LPTSTR sptr, dptr;
+ ULONG Count;
+
+ sptr = String;
+ dptr = tString;
+ wsprintf(tString, TEXT("%lu"), Number);
+ Count = lstrlen(tString);
+
+ *sptr++ = *dptr++;
+ Count--;
+
+ while (*dptr) {
+ if (!(Count % 3))
+ *sptr++ = TEXT(',');
+
+ *sptr++ = *dptr++;
+ Count--;
+ }
+ *sptr = TEXT('\0');
+
+ return String;
+} // lToStr;
+
+
+/////////////////////////////////////////////////////////////////////////
+LPTSTR
+TimeToStr(
+ ULONG TotTime
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ static TCHAR String[10];
+ ULONG Hours, Minutes, Seconds;
+
+ Hours = TotTime / 3600;
+ TotTime -= (Hours * 3600);
+ Minutes = TotTime / 60;
+ Seconds = TotTime - (Minutes * 60);
+
+ wsprintf(String, TEXT("%3lu:%2lu:%2lu"), Hours, Minutes, Seconds);
+
+ return String;
+} // TimeToStr;
+
+
+/////////////////////////////////////////////////////////////////////////
+LPTSTR
+DateToStr(
+ ULONG TotTime
+ )
+
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ static TCHAR String[10];
+ ULONG Hours, Minutes, Seconds;
+
+ Hours = TotTime / 3600;
+ TotTime -= (Hours * 3600);
+ Minutes = TotTime / 60;
+ Seconds = TotTime - (Minutes * 60);
+
+ wsprintf(String, TEXT("%3lu:%2lu:%2lu"), Hours, Minutes, Seconds);
+
+ return String;
+} // DateToStr;
+
+
+/*+-----------------------------------------------------------------------+
+ | Took the _splitpath and _makepath routines and converted them to |
+ | be NT (long file name) and Unicode friendly. |
+ +-----------------------------------------------------------------------+*/
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+lsplitpath(
+ const TCHAR *path,
+ TCHAR *drive,
+ TCHAR *dir,
+ TCHAR *fname,
+ TCHAR *ext
+ )
+
+/*++
+
+Routine Description:
+
+ Splits a path name into its individual components
+
+Arguments:
+
+ path - pointer to path name to be parsed
+ drive - pointer to buffer for drive component, if any
+ dir - pointer to buffer for subdirectory component, if any
+ fname - pointer to buffer for file base name component, if any
+ ext - pointer to buffer for file name extension component, if any
+
+Return Value:
+
+ drive - pointer to drive string. Includes ':' if a drive was given.
+ dir - pointer to subdirectory string. Includes leading and
+ trailing '/' or '\', if any.
+ fname - pointer to file base name
+ ext - pointer to file extension, if any. Includes leading '.'.
+
+--*/
+
+{
+ TCHAR *p;
+ TCHAR *last_slash = NULL, *dot = NULL;
+ unsigned len;
+
+ // init these so we don't exit with bogus values
+ drive[0] = TEXT('\0');
+ dir[0] = TEXT('\0');
+ fname[0] = TEXT('\0');
+ ext[0] = TEXT('\0');
+
+ if (path[0] == TEXT('\0'))
+ return;
+
+ /*+---------------------------------------------------------------------+
+ | Assume that the path argument has the following form, where any or |
+ | all of the components may be missing. |
+ | |
+ | <drive><dir><fname><ext> |
+ | |
+ | drive: |
+ | 0 to MAX_DRIVE-1 characters, the last of which, if any, is a |
+ | ':' or a '\' in the case of a UNC path. |
+ | dir: |
+ | 0 to _MAX_DIR-1 characters in the form of an absolute path |
+ | (leading '/' or '\') or relative path, the last of which, if |
+ | any, must be a '/' or '\'. E.g - |
+ | |
+ | absolute path: |
+ | \top\next\last\ ; or |
+ | /top/next/last/ |
+ | relative path: |
+ | top\next\last\ ; or |
+ | top/next/last/ |
+ | Mixed use of '/' and '\' within a path is also tolerated |
+ | fname: |
+ | 0 to _MAX_FNAME-1 characters not including the '.' character |
+ | ext: |
+ | 0 to _MAX_EXT-1 characters where, if any, the first must be a |
+ | '.' |
+ +---------------------------------------------------------------------+*/
+
+ // extract drive letter and :, if any
+ if ( path[0] && (path[1] == TEXT(':')) ) {
+ if (drive) {
+ drive[0] = path[0];
+ drive[1] = path[1];
+ drive[2] = TEXT('\0');
+ }
+ path += 2;
+ }
+
+ // if no drive then check for UNC pathname
+ if (drive[0] == TEXT('\0'))
+ if ((path[0] == TEXT('\\')) && (path[1] == TEXT('\\'))) {
+ // got a UNC path so put server-sharename into drive
+ drive[0] = path[0];
+ drive[1] = path[1];
+ path += 2;
+
+ p = &drive[2];
+ while ((*path != TEXT('\0')) && (*path != TEXT('\\')))
+ *p++ = *path++;
+
+ if (*path == TEXT('\0'))
+ return;
+
+ // now sitting at the share - copy this as well (copy slash first)
+ *p++ = *path++;
+ while ((*path != TEXT('\0')) && (*path != TEXT('\\')))
+ *p++ = *path++;
+
+ // tack on terminating NULL
+ *p = TEXT('\0');
+ }
+
+ /*+---------------------------------------------------------------------+
+ | extract path string, if any. Path now points to the first character|
+ | of the path, if any, or the filename or extension, if no path was |
+ | specified. Scan ahead for the last occurence, if any, of a '/' or |
+ | '\' path separator character. If none is found, there is no path. |
+ | We will also note the last '.' character found, if any, to aid in |
+ | handling the extension. |
+ +---------------------------------------------------------------------+*/
+
+ for (last_slash = NULL, p = (TCHAR *)path; *p; p++) {
+ if (*p == TEXT('/') || *p == TEXT('\\'))
+ // point to one beyond for later copy
+ last_slash = p + 1;
+ else if (*p == TEXT('.'))
+ dot = p;
+ }
+
+ if (last_slash) {
+
+ // found a path - copy up through last_slash or max. characters allowed,
+ // whichever is smaller
+ if (dir) {
+ len = __min((last_slash - path), (_MAX_DIR - 1));
+ lstrcpyn(dir, path, len + 1);
+ dir[len] = TEXT('\0');
+ }
+ path = last_slash;
+ }
+
+ /*+---------------------------------------------------------------------+
+ | extract file name and extension, if any. Path now points to the |
+ | first character of the file name, if any, or the extension if no |
+ | file name was given. Dot points to the '.' beginning the extension,|
+ | if any. |
+ +---------------------------------------------------------------------+*/
+
+ if (dot && (dot >= path)) {
+ // found the marker for an extension - copy the file name up to the
+ // '.'.
+ if (fname) {
+ len = __min((dot - path), (_MAX_FNAME - 1));
+ lstrcpyn(fname, path, len + 1);
+ *(fname + len) = TEXT('\0');
+ }
+
+ // now we can get the extension - remember that p still points to the
+ // terminating nul character of path.
+ if (ext) {
+ len = __min((p - dot), (_MAX_EXT - 1));
+ lstrcpyn(ext, dot, len + 1);
+ ext[len] = TEXT('\0');
+ }
+ }
+ else {
+ // found no extension, give empty extension and copy rest of string
+ // into fname.
+ if (fname) {
+ len = __min((p - path), (_MAX_FNAME - 1));
+ lstrcpyn(fname, path, len + 1);
+ fname[len] = TEXT('\0');
+ }
+ if (ext) {
+ *ext = TEXT('\0');
+ }
+ }
+
+} // lsplitpath
+
+
+/////////////////////////////////////////////////////////////////////////
+VOID
+lmakepath(
+ TCHAR *path,
+ const TCHAR *drive,
+ const TCHAR *dir,
+ const TCHAR *fname,
+ const TCHAR *ext
+ )
+
+/*++
+
+Routine Description:
+
+ create a path name from its individual components.
+
+Arguments:
+
+ char *path - pointer to buffer for constructed path
+ char *drive - pointer to drive component, may or may not contain
+ trailing ':'
+ char *dir - pointer to subdirectory component, may or may not include
+ leading and/or trailing '/' or '\' characters
+ char *fname - pointer to file base name component
+ char *ext - pointer to extension component, may or may not contain
+ a leading '.'.
+
+Return Value:
+
+ path - pointer to constructed path name
+
+--*/
+
+{
+ const TCHAR *p;
+
+ /*+---------------------------------------------------------------------+
+ | we assume that the arguments are in the following form (although we |
+ | do not diagnose invalid arguments or illegal filenames (such as |
+ | names longer than 8.3 or with illegal characters in them) |
+ | |
+ | drive: |
+ | A or A: |
+ | dir: |
+ | \top\next\last\ ; or |
+ | /top/next/last/ ; or |
+ | |
+ | either of the above forms with either/both the leading and |
+ | trailing / or \ removed. Mixed use of '/' and '\' is also |
+ | tolerated |
+ | fname: |
+ | any valid file name |
+ | ext: |
+ | any valid extension (none if empty or null ) |
+ +---------------------------------------------------------------------+*/
+
+ // copy drive
+ if (drive && *drive)
+ while (*drive)
+ *path++ = *drive++;
+
+ // copy dir
+ if ((p = dir) && *p) {
+ do {
+ *path++ = *p++;
+ }
+ while (*p);
+ if ((*(p-1) != TEXT('/')) && (*(p-1) != TEXT('\\'))) {
+ *path++ = TEXT('\\');
+ }
+ }
+
+ // copy fname
+ if (p = fname) {
+ while (*p) {
+ *path++ = *p++;
+ }
+ }
+
+ // copy ext, including 0-terminator - check to see if a '.' needs to be
+ // inserted.
+ if (p = ext) {
+ if (*p && *p != TEXT('.')) {
+ *path++ = TEXT('.');
+ }
+ while (*path++ = *p++)
+ ;
+ }
+ else {
+ // better add the 0-terminator
+ *path = TEXT('\0');
+ }
+
+} // lmakepath
+
+
+#ifndef _UNICODE
+#error "Function below not DBCS safe"
+#endif
+
+VOID
+EscapeFormattingChars(
+ LPTSTR String,
+ ULONG BufferLength
+ )
+
+/*++
+
+Routine Description:
+
+ Escapes any formatting chars (ie. % chars) in the string so
+ if you sprintf it, you dont trap out as a result of trying to
+ access bogus stack data.
+
+Arguments:
+
+ String - String to fix up. Escaping is done IN PLACE.
+ BufferLength - Size of the buffer the string is in. We need to know
+ this since we are inserting characters. BufferLength is in
+ characters, not bytes.
+
+Return Value:
+
+ None
+
+--*/
+{
+
+
+ ULONG Length; LONG Avail ;
+ LPTSTR End, Tmp = String ;
+
+ if (!Tmp)
+ return ;
+
+ Length = lstrlen(String) ;
+
+ //
+ // Point past end of string. We use this to figure out
+ // via pointer substraction how much needs to be shifted
+ // down as we insert chars.
+ //
+ End = Tmp + Length + 1 ;
+
+ //
+ // How much is avail for escape chars
+ //
+ Avail = BufferLength - (Length+1) ;
+
+ while (*Tmp) {
+
+ if (*Tmp == TEXT('%')) {
+
+ //
+ // If no more space, just change to '_'.
+ //
+ if (Avail <= 0) {
+
+ *Tmp == TEXT('_') ;
+ }
+ else {
+
+ //
+ // Move string over and add escape character.
+ // This is not very efficient but we assume
+ // that this is not common.
+ //
+ --Avail ;
+ memmove(Tmp+1,
+ Tmp,
+ ((LPBYTE)End - (LPBYTE)Tmp)) ;
+ *Tmp = TEXT('%') ;
+ Tmp++ ;
+ }
+
+
+ }
+
+ ++Tmp ;
+ }
+}
+
+
diff --git a/private/nw/convert/nwconv/utils.h b/private/nw/convert/nwconv/utils.h
new file mode 100644
index 000000000..bc2944862
--- /dev/null
+++ b/private/nw/convert/nwconv/utils.h
@@ -0,0 +1,33 @@
+/*+-------------------------------------------------------------------------+
+ | Copyright 1993-1994 (C) Microsoft Corporation - All rights reserved. |
+ +-------------------------------------------------------------------------+*/
+
+#ifndef _UTILS_
+#define _UTILS_
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+#define MAX_DRIVE 30
+
+LPTSTR Lids(WORD idsStr);
+void StringTableDestroy();
+void CursorHourGlass();
+void CursorNormal();
+BOOL BitTest(int Bit, BYTE *Bits);
+BOOL CenterWindow( HWND hwndChild, HWND hwndParent );
+TCHAR *lstrchr(LPTSTR String, TCHAR c);
+BOOL IsPath(LPTSTR Path);
+LPTSTR lToStr(ULONG Number);
+LPTSTR TimeToStr(ULONG TotTime);
+void EscapeFormattingChars(LPTSTR String, ULONG BufferLength) ;
+
+void lsplitpath(const TCHAR *path, TCHAR *drive, TCHAR *dir, TCHAR *fname, TCHAR *ext);
+void lmakepath(TCHAR *path, const TCHAR *drive, const TCHAR *dir, const TCHAR *fname, const TCHAR *ext);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/private/nw/convert/nwconv/version.h b/private/nw/convert/nwconv/version.h
new file mode 100644
index 000000000..2d4116ea1
--- /dev/null
+++ b/private/nw/convert/nwconv/version.h
@@ -0,0 +1,52 @@
+/*
+** Template for version resources. Place this in your .rc file,
+** editing the values for VER_FILETYPE, VER_FILESUBTYPE,
+** VER_FILEDESCRIPTION_STR and VER_INTERNALNAME_STR as needed.
+** See winver.h for possible values.
+**
+** Ntverp.h defines several global values that don't need to be
+** changed except for official releases such as betas, sdk updates, etc.
+**
+** Common.ver has the actual version resource structure that all these
+** #defines eventually initialize.
+*/
+
+/* #include <windows.h> needed if this will be the .rc file */
+
+#include <ntverp.h>
+
+/*-----------------------------------------------*/
+/* the following lines are specific to this file */
+/*-----------------------------------------------*/
+
+/* VER_FILETYPE, VER_FILESUBTYPE, VER_FILEDESCRIPTION_STR
+ * and VER_INTERNALNAME_STR must be defined before including COMMON.VER
+ * The strings don't need a '\0', since common.ver has them.
+ */
+#define VER_FILETYPE VFT_APP
+/* possible values: VFT_UNKNOWN
+ VFT_APP
+ VFT_DLL
+ VFT_DRV
+ VFT_FONT
+ VFT_VXD
+ VFT_STATIC_LIB
+*/
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+/* possible values VFT2_UNKNOWN
+ VFT2_DRV_PRINTER
+ VFT2_DRV_KEYBOARD
+ VFT2_DRV_LANGUAGE
+ VFT2_DRV_DISPLAY
+ VFT2_DRV_MOUSE
+ VFT2_DRV_NETWORK
+ VFT2_DRV_SYSTEM
+ VFT2_DRV_INSTALLABLE
+ VFT2_DRV_SOUND
+ VFT2_DRV_COMM
+*/
+#define VER_FILEDESCRIPTION_STR "Migration Tool for NetWare"
+#define VER_INTERNALNAME_STR "NWConv"
+#define VER_ORIGINALFILENAME_STR "NWCONV.EXE"
+
+#include "common.ver"
diff --git a/private/nw/convert/nwconv/vlist.h b/private/nw/convert/nwconv/vlist.h
new file mode 100644
index 000000000..8fffae07f
--- /dev/null
+++ b/private/nw/convert/nwconv/vlist.h
@@ -0,0 +1,95 @@
+#define VLB_OK 0
+#define VLB_ERR -1
+#define VLB_ENDOFFILE -1
+
+#define VLBS_USEDATAVALUES 0x8000L
+#define VLBS_3DFRAME 0x4000L
+#define VLBS_NOTIFY 0x0001L
+#define VLBS_NOREDRAW 0x0004L
+#define VLBS_OWNERDRAWFIXED 0x0010L
+#define VLBS_HASSTRINGS 0x0040L
+#define VLBS_USETABSTOPS 0x0080L
+#define VLBS_NOINTEGRALHEIGHT 0x0100L
+#define VLBS_WANTKEYBOARDINPUT 0x0400L
+#define VLBS_DISABLENOSCROLL 0x1000L
+
+// Application->VLIST messages
+// Corresponding to LB_ messages
+#define VLB_RESETCONTENT (WM_USER+500)
+#define VLB_SETCURSEL (WM_USER+501)
+#define VLB_GETCURSEL (WM_USER+502)
+#define VLB_GETTEXT (WM_USER+503)
+#define VLB_GETTEXTLEN (WM_USER+504)
+#define VLB_GETCOUNT (WM_USER+505)
+#define VLB_SELECTSTRING (WM_USER+506)
+#define VLB_FINDSTRING (WM_USER+507)
+#define VLB_GETITEMRECT (WM_USER+508)
+#define VLB_GETITEMDATA (WM_USER+509)
+#define VLB_SETITEMDATA (WM_USER+510)
+#define VLB_SETITEMHEIGHT (WM_USER+511)
+#define VLB_GETITEMHEIGHT (WM_USER+512)
+#define VLB_FINDSTRINGEXACT (WM_USER+513)
+#define VLB_INITIALIZE (WM_USER+514)
+#define VLB_SETTABSTOPS (WM_USER+515)
+#define VLB_GETTOPINDEX (WM_USER+516)
+#define VLB_SETTOPINDEX (WM_USER+517)
+#define VLB_GETHORIZONTALEXTENT (WM_USER+518)
+#define VLB_SETHORIZONTALEXTENT (WM_USER+519)
+
+// Unique to VLIST
+#define VLB_UPDATEPAGE (WM_USER+520)
+#define VLB_GETLINES (WM_USER+521)
+#define VLB_GETSCROLLPOS (WM_USER+522)
+#define VLB_HSCROLL (WM_USER+523)
+#define VLB_PAGEDOWN (WM_USER+524)
+#define VLB_PAGEUP (WM_USER+525)
+#define VLB_GETLISTBOXSTYLE (WM_USER+526)
+#define VLB_GETFOCUSHWND (WM_USER+527)
+#define VLB_GETVLISTSTYLE (WM_USER+528)
+
+#define VLB_TOVLIST_MSGMIN VLB_RESETCONTENT
+#define VLB_TOVLIST_MSGMAX VLB_GETVLISTSTYLE
+
+// VLIST->Application messages
+// Conflicts with VLB_
+#define VLBR_FINDSTRING (WM_USER+600)
+#define VLBR_FINDSTRINGEXACT (WM_USER+601)
+#define VLBR_SELECTSTRING (WM_USER+602)
+#define VLBR_GETITEMDATA (WM_USER+603)
+#define VLBR_GETTEXT (WM_USER+604)
+#define VLBR_GETTEXTLEN (WM_USER+605)
+
+// Unique Messages
+//
+#define VLB_FIRST (WM_USER+606)
+#define VLB_PREV (WM_USER+607)
+#define VLB_NEXT (WM_USER+608)
+#define VLB_LAST (WM_USER+609)
+#define VLB_FINDITEM (WM_USER+610)
+#define VLB_RANGE (WM_USER+611)
+#define VLB_FINDPOS (WM_USER+612)
+#define VLB_DONE (WM_USER+613)
+
+// VLIST->Application Notifications
+#define VLBN_FREEITEM (WM_USER+700)
+#define VLBN_FREEALL (WM_USER+701)
+
+#define VLB_TOAPP_MSGMIN VLB_FINDSTRING
+#define VLB_TOAPP_MSGMAX VLBN_FREEALL
+
+#define IDS_VLBOXNAME 1
+
+typedef struct _VLBStruct {
+ int nCtlID;
+ int nStatus;
+ LONG lData; // current data value
+ LONG lIndex; // current index
+ LONG lSelItem; // current selection (if data value)
+ LPTSTR lpTextPointer;
+ LPTSTR lpFindString;
+} VLBSTRUCT;
+
+typedef VLBSTRUCT FAR* LPVLBSTRUCT;
+
+#define VLIST_CLASSNAME "VList"
+extern BOOL WINAPI RegisterVListBox(HINSTANCE);
diff --git a/private/nw/convert/nwconv/vlistint.h b/private/nw/convert/nwconv/vlistint.h
new file mode 100644
index 000000000..bd037a175
--- /dev/null
+++ b/private/nw/convert/nwconv/vlistint.h
@@ -0,0 +1,89 @@
+#include "windows.h"
+#include "windowsx.h"
+#include "vlist.h"
+
+typedef struct tagVLISTBox
+ {
+ HWND hwnd; // hwnd of this VLIST box
+ int nId; // Id of Control
+ HINSTANCE hInstance; // Instance of parent
+ HWND hwndParent; // hwnd of parent of VLIST box
+ HWND hwndList; // hwnd of List box
+ WNDPROC lpfnLBWndProc; // Window procedure of list box
+ int nchHeight; // Height of text line
+ int nLines; // Number of lines in listbox
+ LONG styleSave; // Save the Style Bits
+ WORD VLBoxStyle; // List Box Style
+ HANDLE hFont; // Font for List box
+ LONG lToplIndex; // Top logical record number;
+ int nCountInBox; // Number of Items in box.
+ LONG lNumLogicalRecs; // Number of logical records
+ VLBSTRUCT vlbStruct; // Buffer to communicate to app
+ WORD wFlags; // Various flags fot the VLB
+ //
+ // 0x01 - HasStrings
+ // 0x02 - Use Data Values
+ // 0x04 - Multiple Selections
+ // 0x08 - Ok for parent to have focus
+ // 0x10 - Control has focus
+
+ LONG lSelItem; // List of selected items
+ int nvlbRedrawState; // Redraw State
+ BOOL bHScrollBar; // Does it have a H Scroll
+} VLBOX;
+
+typedef VLBOX NEAR *PVLBOX;
+typedef VLBOX FAR *LPVLBOX;
+
+
+#define IDS_VLBOXNAME 1
+#define VLBLBOXID 100
+#define VLBEDITID 101
+
+#define HASSTRINGS 0x01 // List box stores strings
+#define USEDATAVALUES 0x02 // Use Data Values to talk to parent
+#define MULTIPLESEL 0x04 // VLB has extended or multiple selection
+#define PARENTFOCUS 0x08 // Ok for parent to have focus
+#define HASFOCUS 0x10 // 0x10 - Control has focus
+
+LRESULT CALLBACK VListBoxWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
+LRESULT CALLBACK LBSubclassProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
+
+LONG VLBMessageItemHandler( PVLBOX pVLBox, UINT message, LPTSTR lpfoo);
+LONG VLBParentMessageHandler( PVLBOX pVLBox, UINT message, WPARAM wParam, LPARAM lParam);
+LONG VLBNcCreateHandler( HWND hwnd, LPCREATESTRUCT lpcreateStruct);
+LONG VLBCreateHandler( PVLBOX pVListBox, HWND hwnd, LPCREATESTRUCT lpcreateStruct);
+void VLBNcDestroyHandler(HWND hwnd, PVLBOX pVListBox, WPARAM wParam, LPARAM lParam);
+void VLBDestroyHandler(HWND hwnd, PVLBOX pVLBox, WPARAM wParam, LPARAM lParam);
+void VLBSetFontHandler( PVLBOX pVListBox, HANDLE hFont, BOOL fRedraw);
+int VLBScrollDownLine( PVLBOX pVLBox);
+int VLBScrollUpLine( PVLBOX pVLBox);
+int VLBScrollDownPage( PVLBOX pVLBox, int nAdjustment);
+int VLBScrollUpPage( PVLBOX pVLBox, int nAdjustment);
+void UpdateVLBWindow( PVLBOX pVLBox, LPRECT lpRect);
+int VLBFindPos( PVLBOX pVLBox, int nPos);
+int VLBFindPage( PVLBOX pVLBox, LONG lFindRecNum, BOOL bUpdateTop);
+int VLBFirstPage( PVLBOX pVLBox);
+int VLBLastPage( PVLBOX pVLBox);
+LONG vlbSetCurSel( PVLBOX pVLBox, int nOption, LONG lParam);
+int vlbFindData( PVLBOX pVLBox, LONG lData);
+void VLBSizeHandler( PVLBOX pVLBox, int nItemHeight);
+int vlbInVLB( PVLBOX pVLBox, LONG lData);
+void VLBCountLines( PVLBOX pVLBox);
+
+void vlbRedrawOff(PVLBOX pVLBox);
+void vlbRedrawOn(PVLBOX pVLBox);
+
+BOOL TestSelectedItem(PVLBOX pVLBox, VLBSTRUCT vlbStruct);
+void SetSelectedItem(PVLBOX pVLBox);
+
+void vlbPGDN(PVLBOX pVLBox);
+void vlbPGUP(PVLBOX pVLBox);
+
+void vlbLineDn(PVLBOX pVLBox);
+void vlbLineUp(PVLBOX pVLBox);
+
+void VLBFreeItem(PVLBOX pVLBox, long lFreeItem);
+void VLBFreePage(PVLBOX pVLBox);
+
+extern HANDLE hInstance; // Global instance handle for DLL
diff --git a/private/nw/dirs b/private/nw/dirs
new file mode 100644
index 000000000..1a920cdd4
--- /dev/null
+++ b/private/nw/dirs
@@ -0,0 +1,36 @@
+!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=event \
+ nwlib \
+ rdr \
+ perf \
+ vwipxspx \
+ install \
+ nwapi32 \
+ svcdlls \
+ nw16 \
+ nwscript \
+ convert
+
+OPTIONAL_DIRS= \
+ test
diff --git a/private/nw/event/dummy.c b/private/nw/event/dummy.c
new file mode 100644
index 000000000..537f11ef3
--- /dev/null
+++ b/private/nw/event/dummy.c
@@ -0,0 +1,4 @@
+void
+DummyEntryPoint(void)
+{
+}
diff --git a/private/nw/event/events.rc b/private/nw/event/events.rc
new file mode 100644
index 000000000..b4724c4c1
--- /dev/null
+++ b/private/nw/event/events.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "Event Messages for Client Service for NetWare"
+#define VER_INTERNALNAME_STR "nwevent.dll"
+#define VER_ORIGINALFILENAME_STR "nwevent.dll"
+
+#include "common.ver"
+
+1 11 MSG00001.bin
diff --git a/private/nw/event/makefile b/private/nw/event/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/nw/event/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/event/nwevent.def b/private/nw/event/nwevent.def
new file mode 100644
index 000000000..e67efff20
--- /dev/null
+++ b/private/nw/event/nwevent.def
@@ -0,0 +1,7 @@
+LIBRARY NWEVENT
+
+DESCRIPTION 'Client Service for NetWare event messages'
+
+EXPORTS
+
+ DummyEntryPoint
diff --git a/private/nw/event/nwevent.mc b/private/nw/event/nwevent.mc
new file mode 100644
index 000000000..803c2e823
--- /dev/null
+++ b/private/nw/event/nwevent.mc
@@ -0,0 +1,178 @@
+;/*++ BUILD Version: 0001 // Increment this if a change has global effects
+;
+;Copyright (c) 1992 Microsoft Corporation
+;
+;Module Name:
+;
+; nwevent.h
+;
+;Abstract:
+;
+; Definitions for NetWare network events.
+;
+;Author:
+;
+; Portable Systems Group 12/22/1992
+;
+;Revision History:
+;
+;Notes:
+;
+; This file is generated by the MC tool from the netevent.mc file.
+;
+;--*/
+;
+;#ifndef _NWEVENT_
+;#define _NWEVENT_
+;
+
+SeverityNames=(Success=0x0:STATUS_SEVERITY_SUCCESS
+ Informational=0x1:STATUS_SEVERITY_INFORMATIONAL
+ Warning=0x2:STATUS_SEVERITY_WARNING
+ Error=0x3:STATUS_SEVERITY_ERROR
+ )
+
+
+
+;
+;/////////////////////////////////////////////////////////////////////////
+;//
+;// NetWare Redirector Events
+;//
+;//
+;/////////////////////////////////////////////////////////////////////////
+;
+;
+;
+;// Issued from kernel mode. Don't use %1 for
+;// server-supplied insertion strings -- the I/O system provides the
+;// first string.
+;
+
+MessageId=8001 Severity=Error SymbolicName=EVENT_NWRDR_RESOURCE_SHORTAGE
+Language=English
+The Microsoft Client Service for NetWare redirector was unable to allocate memory.
+.
+
+MessageId=8002 Severity=Error SymbolicName=EVENT_NWRDR_CANT_CREATE_DEVICE
+Language=English
+The Microsoft Client Service for NetWare redirector could not create its device. The redirector could not be started.
+.
+
+MessageId=8003 Severity=Error SymbolicName=EVENT_NWRDR_INVALID_REPLY
+Language=English
+The Microsoft Client Service for NetWare redirector received an incorrectly formatted response from %2.
+.
+
+MessageId=8004 Severity=Error SymbolicName=EVENT_NWRDR_FAILED_UNLOCK
+Language=English
+The Microsoft Client Service for NetWare redirector failed to unlock part of a file on server %2.
+.
+
+MessageId=8005 Severity=Error SymbolicName=EVENT_NWRDR_NETWORK_ERROR
+Language=English
+The Microsoft Client Service for NetWare redirector has encountered a network error.
+.
+
+MessageId=8006 Severity=Error SymbolicName=EVENT_NWRDR_UNEXPECTED_ERROR
+Language=English
+An unexpected network error has occurred on the connection to %2.
+.
+
+MessageId=8007 Severity=Warning SymbolicName=EVENT_NWRDR_TIMEOUT
+Language=English
+The Microsoft Client Service for NetWare redirector has timed out one or more requests to %2.
+.
+
+MessageId=8008 Severity=Error SymbolicName=EVENT_NWRDR_NO_SERVER_ON_NETWORK
+Language=English
+No NetWare or compatible server exists on this network.
+.
+
+
+;/////////////////////////////////////////////////////////////////////////
+;//
+;// NetWare Workstation Events
+;//
+;//
+;/////////////////////////////////////////////////////////////////////////
+
+MessageId=9001 Severity=Error SymbolicName=EVENT_NWWKSTA_NO_TRANSPORTS
+Language=English
+The Microsoft Client Service for NetWare could not start because it did not bind to any transports.
+.
+
+MessageId=9002 Severity=Error SymbolicName=EVENT_NWWKSTA_CANT_BIND_TO_TRANSPORT
+Language=English
+The Microsoft Client Service for NetWare could not bind to the transport %1.
+.
+
+MessageId=9003 Severity=Warning SymbolicName=EVENT_NWWKSTA_INVALID_REGISTRY_VALUE
+Language=English
+The value named %1 in the Microsoft Client Service for NetWare registry key %2 was invalid. The value was
+ignored, and processing continued.
+.
+
+MessageId=9004 Severity=Error SymbolicName=EVENT_NWWKSTA_CANT_CREATE_REDIRECTOR
+Language=English
+The Microsoft Client Service for NetWare redirector (%1) could not be started.
+.
+
+MessageId=9005 Severity=Error SymbolicName=EVENT_NWWKSTA_GATEWAY_LOGON_FAILED
+Language=English
+The Gateway Account could not be logged on. The error %1 occurred.
+.
+
+MessageId=9006 Severity=Error SymbolicName=EVENT_NWWKSTA_CANNOT_REDIRECT_DEVICES
+Language=English
+Device %1 could not be reconnected to %2. Error %3 occurred.
+.
+
+MessageId=9007 Severity=Error SymbolicName=EVENT_NWWKSTA_WRONG_NWLINK_VERSION
+Language=English
+The version of the NWLINK transport present does not support all the capabilities required to use the Microsoft Client Service for NetWare properly.
+.
+
+
+
+;/////////////////////////////////////////////////////////////////////////
+;//
+;// NetWare Credential Manager Events
+;//
+;//
+;/////////////////////////////////////////////////////////////////////////
+
+MessageId=10001 Severity=Informational SymbolicName=NW_MESSAGE_TITLE
+Language=English
+Client Service for NetWare%0
+.
+
+MessageId=10002 Severity=Informational SymbolicName=NW_MESSAGE_FROM_SERVER
+Language=English
+Message from server %1. %n
+.
+
+MessageId=10003 Severity=Warning SymbolicName=NW_PASSWORD_HAS_EXPIRED
+Language=English
+The password for %1 on %2 has expired, with %3 grace logins left.%n
+.
+
+MessageId=10004 Severity=Error SymbolicName=NW_LOGIN_DISABLED
+Language=English
+Logins to the server has been disabled.%n
+.
+
+MessageId=10005 Severity=Error SymbolicName=NW_PASSWORD_HAS_EXPIRED1
+Language=English
+The password for %1 on %2 has expired.%n
+.
+
+MessageId=10006 Severity=Informational SymbolicName=NW_MESSAGE_TITLE_NTAS
+Language=English
+Gateway Service for NetWare%0
+.
+
+
+;
+;#endif // _NWEVENT
+;
diff --git a/private/nw/event/reg.ini b/private/nw/event/reg.ini
new file mode 100644
index 000000000..da0a38d58
--- /dev/null
+++ b/private/nw/event/reg.ini
@@ -0,0 +1,7 @@
+\registry\machine\system\currentcontrolset\services\eventlog\system\nwrdr
+ EventMessageFile = REG_EXPAND_SZ %SystemRoot%\System32\nwevent.dll
+ TypesSupported = REG_DWORD 0x00000007
+\registry\machine\system\currentcontrolset\services\eventlog\system\netwareworkstation
+ EventMessageFile = REG_EXPAND_SZ %SystemRoot%\System32\nwevent.dll
+ TypesSupported = REG_DWORD 0x00000007
+ \ No newline at end of file
diff --git a/private/nw/event/sources b/private/nw/event/sources
new file mode 100644
index 000000000..f67702c1b
--- /dev/null
+++ b/private/nw/event/sources
@@ -0,0 +1,21 @@
+MAJORCOMP=eventlog
+MINORCOMP=nwevent
+
+TARGETNAME=nwevent
+TARGETPATH=\nt\public\sdk\lib
+TARGETTYPE=DYNLINK
+DLLBASE=0x09900000
+
+INCLUDES=.
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SOURCES=dummy.c \
+ events.rc
+
+UMRES=obj\*\nwevent.res
+
+
diff --git a/private/nw/inc/ncp.h b/private/nw/inc/ncp.h
new file mode 100644
index 000000000..886fecba5
--- /dev/null
+++ b/private/nw/inc/ncp.h
@@ -0,0 +1,488 @@
+/*++ BUILD Version: 0001 // Increment this if a change has global effects
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ ncp.h
+
+Abstract:
+
+ This module defines NCP fields and constants.
+
+Author:
+
+ Manny Weiser (mannyw) 10-Aug-1993
+
+Revision History:
+
+--*/
+
+#ifndef _NCP_
+#define _NCP_
+
+///////////////////////////////// IPX ///////////////////////////////////////
+
+#define RIP_SOCKET 0x5304
+#define SAP_SOCKET 0x5204
+#define NCP_SOCKET 0x5104
+
+//
+// SAP request types
+//
+
+#define SAP_GENERAL_REQUEST 1
+#define SAP_FIND_NEAREST 3
+
+//
+// SAP services
+//
+
+#define SAP_SERVICE_TYPE_SERVER 4
+#define SAP_SERVICE_TYPE_DIR_SERVER 278
+
+///// IPX driver services:
+
+#ifndef NWDOS_INCLUDED
+
+typedef UCHAR byte;
+typedef USHORT word;
+
+#endif
+
+typedef ULONG NetAddress;
+typedef byte NodeAddress [6];
+
+typedef struct
+{
+ NetAddress Net;
+ NodeAddress Node;
+ word Socket;
+} IPXaddress;
+
+NTSTATUS IPX_Get_Local_Target ( IPXaddress*, NodeAddress*, word* );
+void IPX_Get_Internetwork_Address ( IPXaddress* );
+word IPX_Get_Interval_Marker ( void );
+
+struct _IRP_CONTEXT;
+struct _NW_TDI_STRUCT;
+
+NTSTATUS
+IPX_Open_Socket(
+ IN struct _IRP_CONTEXT* pIrpC,
+ IN struct _NW_TDI_STRUCT* pTdiStruc
+ );
+
+VOID
+IPX_Close_Socket(
+ IN struct _NW_TDI_STRUCT* pTdiStruc
+ );
+
+///// IPX support routines to hide V86/VxD differences:
+
+//byte* IPX_Allocate_Low_Memory ( word );
+//ESR* IPX_Wrap_Callback ( ESR* );
+
+///////////////////////////////// SAP ///////////////////////////////////////
+
+typedef struct
+{
+ word Command;
+ word ServerType;
+ char Name [48];
+ IPXaddress Address;
+ word Hops;
+} SAPpacket;
+
+///////////////////////////////// RIP ///////////////////////////////////////
+
+typedef struct
+{
+ word Command;
+ NetAddress Net;
+ word Hops;
+ word Ticks;
+} RIPpacket;
+
+///////////////////////////////// PEP ///////////////////////////////////////
+
+#define PEP_COMMAND_CONNECT 0x1111
+#define PEP_COMMAND_REQUEST 0x2222
+#define PEP_COMMAND_RESPONSE 0x3333
+#define PEP_COMMAND_DISCONNECT 0x5555
+#define PEP_COMMAND_BURST 0x7777
+#define PEP_COMMAND_ACKNOWLEDGE 0x9999
+
+typedef struct
+{
+ word command;
+ byte sequence;
+ byte connection;
+ byte x;
+} PEPrequest;
+
+typedef struct
+{
+ byte error;
+ byte status;
+} PEPresponse;
+
+//
+// Bindery object types
+//
+
+#define OT_WILD ~0
+#define OT_USER 1
+#define OT_GROUP 2
+#define OT_PRINT_QUEUE 3
+#define OT_FILESERVER 4
+#define OT_DIRSERVER 0x278
+#define OT_JOBQUEUE 0xA
+
+#define ENCRYPTION_KEY_SIZE 8
+#define OBJECT_ID_SIZE 4
+
+#define BROADCAST_MESSAGE_WAITING 0x21
+
+//
+// NCP function codes
+//
+
+#define NCP_MESSAGE_FUNCTION 0x15
+#define NCP_DIR_FUNCTION 0x16
+#define NCP_ADMIN_FUNCTION 0x17
+#define NCP_END_OF_JOB 0x18
+#define NCP_LOGOUT 0x19
+#define NCP_LOCK_RANGE 0x1A
+#define NCP_UNLOCK_RANGE 0x1E
+#define NCP_NEGOTIATE_BUFFER_SIZE 0x21
+#define NCP_GET_SHORT_NAME 0x30
+#define NCP_FLUSH_FILE 0x3D
+#define NCP_SEARCH_INITIATE 0x3E
+#define NCP_SEARCH_CONTINUE 0x3F
+#define NCP_SEARCH_FILE 0x40
+#define NCP_CLOSE 0x42
+#define NCP_CREATE_FILE 0x43
+#define NCP_DELETE_FILE 0x44
+#define NCP_RENAME_FILE 0x45
+#define NCP_SET_FILE_ATTRIBUTES 0x46
+#define NCP_GET_FILE_SIZE 0x47
+#define NCP_READ_FILE 0x48
+#define NCP_WRITE_FILE 0x49
+#define NCP_SET_FILE_TIME 0x4B
+#define NCP_OPEN_FILE 0x4C
+#define NCP_CREATE_NEW_FILE 0x4D
+#define NCP_LFN_FUNCTION 0x57
+#define NCP_NEGOTIATE_LIP_CONNECTION 0x61
+#define NCP_NEGOTIATE_BURST_CONNECTION 0x65
+
+//
+// Subfunctions of NCP_MESSAGE_FUNCTION
+//
+
+#define NCP_GET_MESSAGE 0x01
+#define NCP_GET_ENTIRE_MESSAGE 0x0B
+
+//
+// Subfunctions of NCP_DIR_FUNCTION
+//
+
+#define NCP_GET_DIRECTORY_PATH 0x01
+#define NCP_GET_VOLUME_NUMBER 0x05
+#define NCP_CREATE_DIRECTORY 0x0A
+#define NCP_DELETE_DIRECTORY 0x0B
+#define NCP_RENAME_DIRECTORY 0x0F
+#define NCP_ALLOCATE_DIR_HANDLE 0x12
+#define NCP_ALLOCATE_TEMP_DIR_HANDLE 0x13
+#define NCP_DEALLOCATE_DIR_HANDLE 0x14
+#define NCP_GET_VOLUME_STATS 0x15
+#define NCP_GET_VOLUME_INFO 0x2C
+#define NCP_GET_NAME_SPACE_INFO 0x2F
+
+//
+// Subfunctions of NCP_ADMIN_FUNCTION
+//
+
+#define NCP_GET_SERVER_INFO 0x11
+#define NCP_PLAIN_TEXT_LOGIN 0x14
+#define NCP_GET_LOGIN_KEY 0x17
+#define NCP_ENCRYPTED_LOGIN 0x18
+#define NCP_CHANGE_CONN_AUTH_STATUS 0x1D
+#define NCP_QUERY_OBJECT_ID 0x35
+#define NCP_SCAN_BINDERY_OBJECT 0x37
+#define NCP_QUERY_PROPERTY_VALUE 0x3D
+#define NCP_PLAIN_TEXT_VERIFY_PASSWORD 0x3F
+#define NCP_IS_OBJECT_IN_SET 0x43
+#define NCP_ENCRYPTED_VERIFY_PASSWORD 0x4A
+#define NCP_CREATE_QUEUE_JOB 0x68
+#define NCP_CLOSE_FILE_AND_START_JOB 0x69
+#define NCP_CLOSE_FILE_AND_CANCEL_JOB 0x6A
+#define NCP_SUBFUNC_79 0x79 // BUGBUG
+#define NCP_SUBFUNC_7F 0x7F // BUGBUG
+
+//
+// Values for NCP_CHANGE_CONN_AUTH_STATUS
+//
+
+#define NCP_CONN_NOT_LICENSED 0
+#define NCP_CONN_LICENSED 1
+
+//
+// Subfunctions of NCP_NAME_SPACE_FUNCTION
+//
+
+#define NCP_LFN_OPEN_CREATE 0x01
+#define NCP_LFN_SEARCH_INITIATE 0x02
+#define NCP_LFN_SEARCH_CONTINUE 0x03
+#define NCP_LFN_RENAME_FILE 0x04
+#define NCP_LFN_GET_INFO 0x06
+#define NCP_LFN_SET_INFO 0x07
+#define NCP_LFN_DELETE_FILE 0x08
+#define NCP_LFN_ALLOCATE_DIR_HANDLE 0x0C
+
+//
+// Packet types
+//
+
+#define PACKET_TYPE_SAP 0x00
+#define PACKET_TYPE_NCP 0x11
+
+//
+// Special character used to indicate that the next char in the
+// search mask is a wild card character.
+//
+
+#define LFN_META_CHARACTER (UCHAR)0xFF
+
+//
+// Properties we query and set
+//
+
+#define NET_ADDRESS_PROPERTY "NET_ADDRESS"
+
+//
+// Search attributes
+//
+
+#define SEARCH_ALL_DIRECTORIES 0x16
+#define SEARCH_ALL_FILES 0x06
+#define SEARCH_EXEC_ONLY_FILES 0x4E
+
+// File Attributes
+
+#define NW_ATTRIBUTE_SHARABLE 0x80
+#define NW_ATTRIBUTE_ARCHIVE 0x20
+#define NW_ATTRIBUTE_DIRECTORY 0x10
+#define NW_ATTRIBUTE_EXECUTE_ONLY 0x08
+#define NW_ATTRIBUTE_SYSTEM 0x04
+#define NW_ATTRIBUTE_HIDDEN 0x02
+#define NW_ATTRIBUTE_READ_ONLY 0x01
+#define NW_ATTRIBUTE_EXEC_ONLY 0x4E
+
+// Open Flags
+
+#define NW_OPEN_EXCLUSIVE 0x10
+#define NW_DENY_WRITE 0x08
+#define NW_DENY_READ 0x04
+#define NW_OPEN_FOR_WRITE 0x02
+#define NW_OPEN_FOR_READ 0x01
+
+//
+// Connection status flags
+//
+
+#define NCP_STATUS_BAD_CONNECTION 0x01
+#define NCP_STATUS_NO_CONNECTIONS 0x02
+#define NCP_STATUS_SERVER_DOWN 0x04
+#define NCP_STATUS_MSG_PENDING 0x08
+#define NCP_STATUS_SHUTDOWN 0x10
+
+//
+// Extended name space (long file name) query information flags
+//
+
+#define LFN_FLAG_INFO_NAME 0x0001
+#define LFN_FLAG_INFO_BLOCK_SIZE 0x0002
+#define LFN_FLAG_INFO_ATTRIBUTES 0x0004
+#define LFN_FLAG_INFO_FILE_SIZE 0x0008
+#define LFN_FLAG_INFO_STREAMS 0x0010
+#define LFN_FLAG_INFO_EA_INFO 0x0020
+#define LFN_FLAG_INFO_ARCHIVE_TIME 0x0040
+#define LFN_FLAG_INFO_MODIFY_TIME 0x0080
+#define LFN_FLAG_INFO_CREATION_TIME 0x0100
+#define LFN_FLAG_INFO_CREATOR 0x0200
+#define LFN_FLAG_INFO_DIR_INFO 0x0400
+#define LFN_FLAG_INFO_RIGHTS 0x0800
+
+//
+// Extended name space (long file name) set information flags
+//
+
+#define LFN_FLAG_SET_NAME 0x0001 // Never used
+#define LFN_FLAG_SET_INFO_ATTRIBUTES 0x0002
+#define LFN_FLAG_SET_INFO_CREATE_DATE 0x0004
+#define LFN_FLAG_SET_INFO_CREATE_TIME 0x0008
+#define LFN_FLAG_SET_INFO_CREATOR_ID 0x0010
+#define LFN_FLAG_SET_INFO_ARCHIVE_DATE 0x0020
+#define LFN_FLAG_SET_INFO_ARCHIVE_TIME 0x0040
+#define LFN_FLAG_SET_INFO_ARCHIVE_ID 0x0080
+#define LFN_FLAG_SET_INFO_MODIFY_DATE 0x0100
+#define LFN_FLAG_SET_INFO_MODIFY_TIME 0x0200
+#define LFN_FLAG_SET_INFO_MODIFY_ID 0x0400
+#define LFN_FLAG_SET_INFO_LASTACCESS_DATE 0x0800
+#define LFN_FLAG_SET_INFO_INHERITANCE 0x1000
+#define LFN_FLAG_SET_INFO_MAXIMUM_SPACE 0x2000
+
+//
+// Extended name space (long file name) open mode flags
+//
+
+#define LFN_FLAG_OM_OPEN 0x01
+#define LFN_FLAG_OM_OVERWRITE 0x02
+#define LFN_FLAG_OM_CREATE 0x08
+
+//
+// Long name directory flags
+//
+
+#define LFN_FLAG_SHORT_DIRECTORY 0x00
+
+//
+// Burst request
+//
+
+#define BURST_REQUEST_READ 0x01
+#define BURST_REQUEST_WRITE 0x02
+
+//
+// Burst flags
+//
+
+#define BURST_FLAG_END_OF_BURST 0x10
+#define BURST_FLAG_SYSTEM_PACKET 0x80
+
+/////////////////////////////////////////////////////////////////////////////
+
+#define MAX_SERVER_NAME_LENGTH 48
+
+#include <packon.h>
+
+typedef struct _SAP_FIND_NEAREST_RESPONSE {
+ USHORT SapType; // == 4
+ USHORT SapServiceType;
+ UCHAR ServerName[MAX_SERVER_NAME_LENGTH];
+ ULONG Network;
+ UCHAR NodeNumber[6];
+ USHORT HopCount; // Hi-lo order
+} SAP_FIND_NEAREST_RESPONSE, *PSAP_FIND_NEAREST_RESPONSE;
+
+typedef struct _NCP_HEADER {
+ USHORT Command;
+ UCHAR SequenceNumber;
+ UCHAR ConnectionIdLow;
+ UCHAR TaskId;
+ UCHAR ConnectionIdHigh;
+} NCP_HEADER, *PNCP_HEADER;
+
+//
+// Header format for NCP request with no subfunction.
+//
+
+typedef struct _NCP_REQUEST {
+ NCP_HEADER NcpHeader;
+ UCHAR FunctionCode;
+
+ //
+ // Function specific stuff follows.
+ //
+} NCP_REQUEST, *PNCP_REQUEST;
+
+//
+// Header format for NCP request with a subfunction.
+//
+
+typedef struct _NCP_REQUEST_WITH_SUB {
+ NCP_HEADER NcpHeader;
+ UCHAR FunctionCode;
+ USHORT SubfunctionLength;
+ UCHAR SubfunctionCode;
+
+ //
+ // Function specific stuff follows.
+ //
+} NCP_REQUEST_WITH_SUB, *PNCP_REQUEST_WITH_SUB;
+
+typedef struct _NCP_RESPONSE {
+ NCP_HEADER NcpHeader;
+
+ UCHAR Error;
+ UCHAR Status;
+
+ //
+ // Function specific stuff follows.
+ //
+
+} NCP_RESPONSE, *PNCP_RESPONSE;
+
+typedef struct _NCP_BURST_HEADER {
+ USHORT Command; // 0x7777
+ UCHAR Flags;
+ UCHAR StreamType; // 0x02
+ ULONG SourceConnection;
+ ULONG DestinationConnection;
+ ULONG PacketSequenceNo;
+ ULONG SendDelayTime;
+ USHORT BurstSequenceNo;
+ USHORT AckSequenceNo;
+ ULONG DataSize;
+ ULONG BurstOffset;
+ USHORT BurstLength;
+ USHORT MissingFragmentCount;
+} NCP_BURST_HEADER, *PNCP_BURST_HEADER;
+
+typedef struct _NCP_BURST_WRITE_REQUEST {
+ NCP_BURST_HEADER BurstHeader;
+ ULONG Function; // 0x02 = Write
+ ULONG Handle;
+ ULONG TotalWriteOffset;
+ ULONG TotalWriteLength;
+ ULONG Offset;
+ ULONG Length;
+ //UCHAR Data[x];
+} NCP_BURST_WRITE_REQUEST, *PNCP_BURST_WRITE_REQUEST;
+
+typedef struct _NCP_BURST_READ_RESPONSE {
+ NCP_BURST_HEADER BurstHeader;
+ ULONG Result;
+ ULONG BytesRead;
+ //UCHAR Data[x];
+} NCP_BURST_READ_RESPONSE, *PNCP_BURST_READ_RESPONSE;
+
+typedef struct _NCP_BURST_READ_REQUEST {
+ NCP_BURST_HEADER BurstHeader;
+ ULONG Function; // 0x02 = Write
+ ULONG Handle;
+ ULONG TotalReadOffset;
+ ULONG TotalReadLength;
+ ULONG Offset;
+ ULONG Length;
+} NCP_BURST_READ_REQUEST, *PNCP_BURST_READ_REQUEST;
+
+typedef struct _NCP_READ_REQUEST {
+ NCP_REQUEST RequestHeader;
+ UCHAR Unused;
+ UCHAR Handle[6];
+ ULONG FileOffset;
+ USHORT Length;
+} NCP_READ_REQUEST, *PNCP_READ_REQUEST;
+
+typedef struct _NCP_READ_RESPONSE {
+ NCP_RESPONSE ResponseHeader;
+ USHORT Length;
+ //UCHAR Data[x];
+} NCP_READ_RESPONSE, *PNCP_READ_RESPONSE;
+
+#include <packoff.h>
+
+#endif // _NCP_
+
diff --git a/private/nw/inc/nds.h b/private/nw/inc/nds.h
new file mode 100644
index 000000000..76728bcfb
--- /dev/null
+++ b/private/nw/inc/nds.h
@@ -0,0 +1,226 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ Nds.h
+
+Abstract:
+
+ This defines the necessary NDS data structures and
+ symbolic constants for both kernel and user mode
+ components.
+
+Author:
+
+ Cory West [CoryWest] 08-Jan-1996
+
+Revision History:
+
+--*/
+
+//
+// NDS Actions.
+//
+
+#define NDS_REQUEST 104 // NCP Function Number.
+#define NDS_PING 1 // Subfunction code for ping.
+#define NDS_ACTION 2 // Subfunction code for action.
+
+//
+// NDS Verb Numbers.
+//
+
+#define NDSV_RESOLVE_NAME 1
+#define NDSV_READ_ENTRY_INFO 2
+#define NDSV_READ 3
+#define NDSV_LIST 5
+#define NDSV_OPEN_STREAM 27
+#define NDSV_GET_SERVER_ADDRESS 53
+#define NDSV_CHANGE_PASSWORD 55
+#define NDSV_BEGIN_LOGIN 57
+#define NDSV_FINISH_LOGIN 58
+#define NDSV_BEGIN_AUTHENTICATE 59
+#define NDSV_FINISH_AUTHENTICATE 60
+#define NDSV_LOGOUT 61
+
+//
+// Rounding Macros.
+//
+
+#define ROUNDUP4(x) ( ( (x) + 3 ) & ( ~3 ) )
+#define ROUNDUP2(x) ( ( (x) + 1 ) & ( ~1 ) )
+
+//
+// Context Flags.
+//
+
+#define FLAGS_DEREF_ALIASES 0x1
+#define FLAGS_XLATE_STRINGS 0x2
+#define FLAGS_TYPELESS_NAMES 0x4
+#define FLAGS_ASYNC_MODE 0x8 // Not supported.
+#define FLAGS_CANONICALIZE_NAMES 0x10
+#define FLAGS_ALL_PUBLIC 0x1f
+
+//
+// values for RESOLVE_NAME request flags
+//
+
+#define RSLV_DEREF_ALIASES 0x40
+#define RSLV_READABLE 0x02
+#define RSLV_WRITABLE 0x04
+#define RSLV_WALK_TREE 0x20
+#define RSLV_CREATE_ID 0x10
+#define RSLV_ENTRY_ID 0x1
+
+#define RESOLVE_NAME_ACCEPT_REMOTE 1
+#define RESOLVE_NAME_REFER_REMOTE 2
+
+//
+// Confidence Levels.
+//
+
+#define LOW_CONF 0
+#define MED_CONF 1
+#define HIGH_CONF 2
+
+//
+// Referral Scopes.
+//
+
+#define ANY_SCOPE 0
+#define COUNTRY_SCOPE 1
+#define ORGANIZATION_SCOPE 2
+#define LOCAL_SCOPE 3
+
+//
+// Max name sizes.
+//
+
+#define MAX_NDS_SCHEMA_NAME_CHARS 32
+
+#define MAX_NDS_NAME_CHARS 256
+#define MAX_NDS_NAME_SIZE ( MAX_NDS_NAME_CHARS * 2 )
+#define MAX_NDS_TREE_NAME_LEN 32
+
+//
+// For an NDS exchange, we use buffers of this size to hold the send
+// and receive data. These sizes come from the Win95 implementation.
+//
+
+#define NDS_BUFFER_SIZE 2048
+#define DUMMY_ITER_HANDLE ( ( unsigned long ) 0xffffffff )
+#define INITIAL_ITERATION ( ( unsigned long ) 0xffffffff )
+#define ENTRY_INFO_NAME_VALUE 1
+
+//
+// Various server responses.
+//
+
+typedef struct {
+
+ DWORD CompletionCode;
+ DWORD RemoteEntry;
+ DWORD EntryId;
+ DWORD ServerNameLength;
+ WCHAR ReferredServer[1];
+
+ //
+ // If RemoteEntry is set to RESOLVE_NAME_REFER_REMOTE,
+ // Then the tree server doesn't know the information
+ // about the object in question and has referred us to
+ // the server named in ReferredServer.
+ //
+
+} NDS_RESPONSE_RESOLVE_NAME, *PNDS_RESPONSE_RESOLVE_NAME;
+
+typedef struct {
+
+ DWORD CompletionCode;
+ DWORD EntryFlags;
+ DWORD SubordinateCount;
+ DWORD ModificationTime;
+
+ //
+ // Two UNICODE strings follow in standard NDS format:
+ //
+ // DWORD BaseClassLen;
+ // WCHAR BaseClass[BaseClassLen];
+ // DWORD EntryNameLen;
+ // WCHAR EntryName[EntryNameLen];
+ //
+
+} NDS_RESPONSE_GET_OBJECT_INFO, *PNDS_RESPONSE_GET_OBJECT_INFO;
+
+typedef struct {
+
+ DWORD EntryId;
+ DWORD Flags;
+ DWORD SubordinateCount;
+ DWORD ModificationTime;
+
+ //
+ // Two UNICODE strings follow in standard NDS format:
+ //
+ // DWORD BaseClassLen;
+ // WCHAR BaseClass[BaseClassLen];
+ // DWORD EntryNameLen;
+ // WCHAR EntryName[EntryNameLen];
+ //
+
+} NDS_RESPONSE_SUBORDINATE_ENTRY, *PNDS_RESPONSE_SUBORDINATE_ENTRY;
+
+typedef struct {
+
+ DWORD CompletionCode;
+ DWORD IterationHandle;
+ DWORD SubordinateEntries;
+
+ //
+ // Followed by an array of NDS_SUBORDINATE_ENTRY
+ // structures that is SubordinateEntries long.
+ //
+
+} NDS_RESPONSE_SUBORDINATE_LIST, *PNDS_RESPONSE_SUBORDINATE_LIST;
+
+typedef struct {
+
+ DWORD SyntaxID;
+ DWORD AttribNameLength;
+ WCHAR AttribName[1];
+
+ //
+ // AttribName is of length
+ // AttribNameLength, of course.
+ //
+
+ DWORD NumValues;
+
+ //
+ // Followed by an array of NumValues
+ // Attrib structures.
+ //
+
+} NDS_ATTRIBUTE, *PNDS_ATTRIBUTE;
+
+typedef struct {
+
+ DWORD CompletionCode;
+ DWORD IterationHandle;
+ DWORD InfoType;
+ DWORD NumAttributes;
+
+ //
+ // Followed by an array of
+ // NDS_ATTRIBUTE structures.
+ //
+
+} NDS_RESPONSE_READ_ATTRIBUTE, *PNDS_RESPONSE_READ_ATTRIBUTE;
+
+typedef struct {
+ DWORD dwLength;
+ WCHAR Buffer[1];
+} NDS_STRING, *PNDS_STRING;
+
+
diff --git a/private/nw/inc/ndsapi32.h b/private/nw/inc/ndsapi32.h
new file mode 100644
index 000000000..57b55f890
--- /dev/null
+++ b/private/nw/inc/ndsapi32.h
@@ -0,0 +1,330 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ NdsLib32.h
+
+Abstract:
+
+ This module exposes the minimal win32 API to Netware directory
+ services support in the Netware redirector.
+
+Author:
+
+ Cory West [CoryWest] 23-Feb-1995
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <ntdef.h>
+
+#include <stdio.h>
+#include <ntddnwfs.h>
+
+NTSTATUS
+NwNdsOpenTreeHandle(
+ IN PUNICODE_STRING puNdsTree,
+ OUT PHANDLE phNwRdrHandle
+);
+
+// NwNdsOpenTreeHandle( PUNICODE_STRING, PHANDLE )
+//
+// Given an NDS tree name, this opens a handle the the redirector
+// for accessing that tree. The handle should closed using the
+// standard NT CloseHandle() call. This function is only a
+// simple wrapper around NT OpenFile().
+
+//
+// Administrativa.
+//
+
+#define HANDLE_TYPE_NCP_SERVER 1
+#define HANDLE_TYPE_NDS_TREE 2
+
+NTSTATUS
+NwNdsOpenGenericHandle(
+ IN PUNICODE_STRING puNdsTree,
+ OUT LPDWORD lpdwHandleType,
+ OUT PHANDLE phNwRdrHandle
+);
+
+// NwNdsOpenGenericHandle( PUNICODE_STRING, LPDWORD, PHANDLE )
+//
+// Given a name, this opens a handle the the redirector for accessing that
+// named tree or server. lpdwHandleType is set to either HANDLE_TYPE_NCP_SERVER
+// or HANDLE_TYPE_NDS_TREE accordingly. The handle should be closed using
+// the standard NT CloseHandle() call. This function is only a simple
+// wrapper around NT OpenFile().
+
+NTSTATUS
+NwOpenHandleWithSupplementalCredentials(
+ IN PUNICODE_STRING puResourceName,
+ IN PUNICODE_STRING puUserName,
+ IN PUNICODE_STRING puPassword,
+ OUT LPDWORD lpdwHandleType,
+ OUT PHANDLE phNwHandle
+);
+
+// NwOpenHandleWithSupplementalCredentials
+//
+// Given a resource name (either a server name or a tree name),
+// open a handle to that resource with the provided username and
+// password. As with the open generic handle routine, lpdsHandleType
+// will be set to either HANDLE_TYPE_NCP_SERVER or
+// HANDLE_TYPE_NDS_TREE based on the result of the open.
+
+//
+// Administrativa.
+//
+
+NTSTATUS
+NwNdsSetTreeContext (
+ IN HANDLE hNdsRdr,
+ IN PUNICODE_STRING puTree,
+ IN PUNICODE_STRING puContext
+);
+
+// NwNdsSetTreeContext(HANDLE, PUNICODE_STRING, PUNICODE_STRING)
+//
+// Set the current context for the specified tree.
+//
+// Arguments:
+//
+// HANDLE hNdsRdr - A handle to the redirector.
+// PUNICODE_STRING puTree - The tree name.
+// PUNICODE_STRING puContext - The context in that tree.
+
+NTSTATUS
+NwNdsGetTreeContext (
+ IN HANDLE hNdsRdr,
+ IN PUNICODE_STRING puTree,
+ OUT PUNICODE_STRING puContext
+);
+
+// NwNdsGetTreeContext(HANDLE, PUNICODE_STRING, PUNICODE_STRING)
+//
+// Get the current context for the specified tree.
+//
+// Arguments:
+//
+// HANDLE hNdsRdr - A handle to the redirector.
+// PUNICODE_STRING puTree - The tree name.
+// PUNICODE_STRING puContext - The context in that tree.
+
+//
+// Browsing and Navigating support.
+//
+
+NTSTATUS
+NwNdsResolveName (
+ IN HANDLE hNdsTree,
+ IN PUNICODE_STRING puObjectName,
+ OUT DWORD *dwObjectId,
+ OUT PUNICODE_STRING puReferredServer,
+ OUT PBYTE pbRawResponse,
+ IN DWORD dwResponseBufferLen
+);
+
+// NwNdsResolveName(HANDLE, PUNICODE_STRING, PDWORD)
+//
+// Resolve the given name to an NDS object id. This utilizes
+// NDS verb 1.
+//
+// There is currently no interface for canonicalizing names.
+// This call will use the default context if one has been set
+// for this NDS tree.
+//
+// puReferredServer must point to a UNICODE_STRING with enough
+// space to hold a server name (MAX_SERVER_NAME_LENGTH) *
+// sizeof( WCHAR ).
+//
+// If dwResponseBufferLen is not 0, and pbRawResponse points
+// to a writable buffer of length dwResponseBufferLen, then
+// this routine will also return the entire NDS response in
+// the raw response buffer. The NDS response is described
+// by NDS_RESPONSE_RESOLVE_NAME.
+//
+// Arguments:
+//
+// HANDLE hNdsTree - The name of the NDS tree that we are interested in looking into.
+// PUNICODE_STRING puObjectName - The name that we want resolved into an object id.
+// DWORD *dwObjectId - The place where we will place the object id.
+// BYTE *pbRawResponse - The raw response buffer, if desired.
+// DWORD dwResponseBufferLen - The length of the raw response buffer.
+
+NTSTATUS
+NwNdsList (
+ IN HANDLE hNdsTree,
+ IN DWORD dwObjectId,
+ OUT DWORD *dwIterHandle,
+ OUT BYTE *pbReplyBuf,
+ IN DWORD dwReplyBufLen
+);
+
+// NwNdsList(HANDLE, DWORD, PDWORD, PBYTE, DWORD, PDWORD)
+//
+// List the immediate subordinates of an object. This utilizes
+// NDS verb 5.
+//
+// Arguments:
+//
+// HANDLE hNdsTree - The handle to the tree that we are interested in.
+// DWORD dwObjectId - The object that we want to list.
+// DWORD *dwIterHandle - The iteration handle to be used in continuing
+// the request if the buffer is not large enough for the entire
+// list of subordinates.
+// BYTE *pbReplyBuf - The buffer where the raw reply will be placed.
+// DWORD dwReplyBufLen - The length of the raw reply buffer.
+
+NTSTATUS
+NwNdsReadObjectInfo(
+ IN HANDLE hNdsTree,
+ IN DWORD dwObjectId,
+ OUT PBYTE pbReplyBuf,
+ IN DWORD dwReplyBufLen
+);
+
+// NwNdsReadObjectInfo(PUNICODE_STRING, DWORD, PBYTE, DWORD)
+//
+// Given an object id, this gets the basic info for the object. This
+// utilizes NDS verb 2. The reply buffer should be large enough to
+// hold a DS_OBJ_INFO struct and the text of the two unicode strings.
+//
+// Arguments:
+//
+// HANDLE hNdsTree - The tree that we want to look in.
+// DWORD dwObjectId - The object id that we want to learn about.
+// BYTE *pbReplyBuf - The space for the reply.
+// DWORD dwReplyBufLen - The length of the reply buffer.
+
+NTSTATUS
+NwNdsReadAttribute (
+ IN HANDLE hNdsTree,
+ IN DWORD dwObjectId,
+ IN DWORD *dwIterHandle,
+ IN PUNICODE_STRING puAttrName,
+ OUT BYTE *pbReplyBuf,
+ IN DWORD dwReplyBufLen
+);
+
+// NwNdsReadAttribute(HANDLE, DWORD, PDWORD, PUNICODE_STRING, PBYTE, DWORD)
+//
+// Read the requested attribute from the listed object.
+// This utilizes NDS verb 3.
+//
+// Arguments:
+//
+// HANDLE hNdsTree - The tree that we want to read from.
+// DWORD dwObjectId - The object that we want to read from.
+// DWORD *dwIterHandle - The iteration handle.
+// PUNICODE_STRING puAttrName - The name of the attribute.
+// BYTE *pbReplyBuf - The buffer to hold the response.
+// DWORD deReplyBufLen - The length of the reply buffer.
+
+NTSTATUS
+NwNdsOpenStream (
+ IN HANDLE hNdsTree,
+ IN DWORD dwObjectId,
+ IN PUNICODE_STRING puStreamName,
+ IN DWORD dwOpenFlags,
+ OUT DWORD *pdwFileLength
+);
+
+// NwNdsOpenStream(HANDLE, DWORD, PBYTE, DWORD)
+//
+// Open a file handle to the stream listed.
+// This utilizes NDS verb 27.
+//
+// Arguments:
+//
+// HANDLE hNdsTree - The handle to the NDS tree that we are interested in.
+// DWORD dwObjectId - The object id that we want to query.
+// PUNICODE_STRING puStreamName - The name of the stream that we want to open.
+// DWORD dwOpenFlags - 1 for read, 2 for write, 3 for read/write.
+// DWORD *pdwFileLength - The length of the file stream.
+
+NTSTATUS
+NwNdsGetQueueInformation(
+ IN HANDLE hNdsTree,
+ IN PUNICODE_STRING puQueueName,
+ OUT PUNICODE_STRING puHostServer,
+ OUT PDWORD pdwQueueId
+);
+
+// NwNdsGetQueueInformation(HANDLE, PUNICODE_STRING, PUNICODE_STRING, PDWORD)
+//
+// Arguments:
+//
+// HANDLE hNdsTree - The handle to the NDS tree that knows about the queue.
+// PUNICODE_STRING puQueueName - The ds path to the queue that we want.
+// PUNICODE_STRING puHostServer - The host server for this queue.
+// PDWORD pdwQueueId - The queue id for this queue on this server.
+
+NTSTATUS
+NwNdsGetVolumeInformation(
+ IN HANDLE hNdsTree,
+ IN PUNICODE_STRING puVolumeName,
+ OUT PUNICODE_STRING puHostServer,
+ OUT PUNICODE_STRING puHostVolume
+);
+
+// NwNdsGetVoluemInformation(HANDLE, PUNICODE_STRING, PUNICODE_STRING, PUNICODE_STRING)
+//
+// Arguments:
+//
+// HANDLE hNdsTree - The handle to the NDS tree that knows about the volume.
+// PUNICODE_STRING puVolumeName - The ds path to the volume that we want.
+// PUNICODE_STRING puHostServer - The host server for this nds volume.
+// PUNICODE_STRING puHostVolume - The host volume for this nds volume.
+
+//
+// User mode fragment exchange.
+//
+
+NTSTATUS
+_cdecl
+FragExWithWait(
+ IN HANDLE hNdsServer,
+ IN DWORD NdsVerb,
+ IN BYTE *pReplyBuffer,
+ IN DWORD pReplyBufferLen,
+ IN OUT DWORD *pdwReplyLen,
+ IN BYTE *NdsRequestStr,
+ ...
+);
+
+NTSTATUS
+_cdecl
+ParseResponse(
+ PUCHAR Response,
+ ULONG ResponseLength,
+ char* FormatString,
+ ...
+);
+
+int
+_cdecl
+FormatBuf(
+ char *buf,
+ int bufLen,
+ const char *format,
+ va_list args
+);
+
+//
+// Change password support.
+//
+
+NTSTATUS
+NwNdsChangePassword(
+ IN HANDLE hNwRdr,
+ IN PUNICODE_STRING puTreeName,
+ IN PUNICODE_STRING puUserName,
+ IN PUNICODE_STRING puCurrentPassword,
+ IN PUNICODE_STRING puNewPassword
+);
diff --git a/private/nw/inc/ntddnwfs.h b/private/nw/inc/ntddnwfs.h
new file mode 100644
index 000000000..28fe63515
--- /dev/null
+++ b/private/nw/inc/ntddnwfs.h
@@ -0,0 +1,625 @@
+/*++ BUILD Version: 0009 // Increment this if a change has global effects
+
+Copyright (c) 1987-1993 Microsoft Corporation
+
+Module Name:
+
+ ntddnwfs.h
+
+Abstract:
+
+ This is the include file that defines all constants and types for
+ accessing the NetWare redirector file system device.
+
+Author:
+
+ Colin Watson (ColinW) 23-Dec-1992
+
+Revision History:
+
+
+--*/
+
+#ifndef _NTDDNWFS_
+#define _NTDDNWFS_
+
+#include <windef.h>
+#include <winnetwk.h> // NETRESOURCE structure
+
+typedef CHAR SERVERNAME[48];
+typedef SERVERNAME* PSERVERNAME;
+
+//
+// Device Name - this string is the name of the device. It is the name
+// that should be passed to NtOpenFile when accessing the device.
+//
+// Note: For devices that support multiple units, it should be suffixed
+// with the Ascii representation of the unit number.
+//
+
+#define DD_NWFS_DEVICE_NAME "\\Device\\NwRdr"
+#define DD_NWFS_DEVICE_NAME_U L"\\Device\\NwRdr"
+
+//
+// The file system name as returned by
+// NtQueryInformationVolume(FileFsAttributeInformation)
+//
+#define DD_NWFS_FILESYS_NAME "NWRDR"
+#define DD_NWFS_FILESYS_NAME_U L"NWRDR"
+
+//
+// Connection type bit mask
+//
+#define CONNTYPE_DISK 0x00000001
+#define CONNTYPE_PRINT 0x00000002
+#define CONNTYPE_ANY ( CONNTYPE_DISK | CONNTYPE_PRINT )
+#define CONNTYPE_IMPLICIT 0x80000000
+#define CONNTYPE_SYMBOLIC 0x40000000
+
+//
+// EA Names for creating a connection
+//
+#define EA_NAME_USERNAME "UserName"
+#define EA_NAME_PASSWORD "Password"
+#define EA_NAME_TYPE "Type"
+#define EA_NAME_CREDENTIAL_EX "ExCredentials"
+
+#define TRANSACTION_REQUEST 0x00000003
+
+
+//
+// NtDeviceIoControlFile/NtFsControlFile IoControlCode values for this device.
+//
+// Warning: Remember that the low two bits of the code specify how the
+// buffers are passed to the driver!
+//
+//
+// Method = 00 - Buffer both input and output buffers for the request
+// Method = 01 - Buffer input, map output buffer to an MDL as an IN buff
+// Method = 10 - Buffer input, map output buffer to an MDL as an OUT buff
+// Method = 11 - Do not buffer either the input or output
+//
+
+#define IOCTL_NWRDR_BASE FILE_DEVICE_NETWORK_FILE_SYSTEM
+
+#define _NWRDR_CONTROL_CODE(request, method, access) \
+ CTL_CODE(IOCTL_NWRDR_BASE, request, method, access)
+
+#define FSCTL_NWR_START _NWRDR_CONTROL_CODE(200, METHOD_IN_DIRECT, FILE_ANY_ACCESS)
+#define FSCTL_NWR_STOP _NWRDR_CONTROL_CODE(201, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define FSCTL_NWR_LOGON _NWRDR_CONTROL_CODE(202, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define FSCTL_NWR_LOGOFF _NWRDR_CONTROL_CODE(203, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define FSCTL_NWR_GET_CONNECTION _NWRDR_CONTROL_CODE(204, METHOD_NEITHER, FILE_ANY_ACCESS)
+#define FSCTL_NWR_ENUMERATE_CONNECTIONS _NWRDR_CONTROL_CODE(205, METHOD_NEITHER, FILE_ANY_ACCESS)
+#define FSCTL_NWR_DELETE_CONNECTION _NWRDR_CONTROL_CODE(207, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define FSCTL_NWR_BIND_TO_TRANSPORT _NWRDR_CONTROL_CODE(208, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define FSCTL_NWR_CHANGE_PASS _NWRDR_CONTROL_CODE(209, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define FSCTL_NWR_SET_INFO _NWRDR_CONTROL_CODE(211, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define FSCTL_NWR_GET_USERNAME _NWRDR_CONTROL_CODE(215, METHOD_NEITHER, FILE_ANY_ACCESS)
+#define FSCTL_NWR_CHALLENGE _NWRDR_CONTROL_CODE(216, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define FSCTL_NWR_GET_CONN_DETAILS _NWRDR_CONTROL_CODE(217, METHOD_NEITHER, FILE_ANY_ACCESS)
+#define FSCTL_NWR_GET_MESSAGE _NWRDR_CONTROL_CODE(218, METHOD_NEITHER, FILE_ANY_ACCESS)
+#define FSCTL_NWR_GET_STATISTICS _NWRDR_CONTROL_CODE(219, METHOD_NEITHER, FILE_ANY_ACCESS)
+#define FSCTL_NWR_GET_CONN_STATUS _NWRDR_CONTROL_CODE(220, METHOD_NEITHER, FILE_ANY_ACCESS)
+#define FSCTL_NWR_GET_CONN_INFO _NWRDR_CONTROL_CODE(221, METHOD_NEITHER, FILE_ANY_ACCESS)
+#define FSCTL_NWR_GET_PREFERRED_SERVER _NWRDR_CONTROL_CODE(222, METHOD_NEITHER, FILE_ANY_ACCESS)
+#define FSCTL_NWR_GET_CONN_PERFORMANCE _NWRDR_CONTROL_CODE(223, METHOD_NEITHER, FILE_ANY_ACCESS)
+#define FSCTL_NWR_SET_SHAREBIT _NWRDR_CONTROL_CODE(224, METHOD_NEITHER, FILE_ANY_ACCESS)
+
+#define FSCTL_NWR_NDS_SETCONTEXT NWR_ANY_NDS(1)
+#define FSCTL_NWR_NDS_GETCONTEXT NWR_ANY_NDS(2)
+#define FSCTL_NWR_NDS_VERIFY_TREE NWR_ANY_NDS(3)
+#define FSCTL_NWR_NDS_RESOLVE_NAME NWR_ANY_NDS(4)
+#define FSCTL_NWR_NDS_LIST_SUBS NWR_ANY_NDS(5)
+#define FSCTL_NWR_NDS_READ_INFO NWR_ANY_NDS(6)
+#define FSCTL_NWR_NDS_READ_ATTR NWR_ANY_NDS(7)
+#define FSCTL_NWR_NDS_OPEN_STREAM NWR_ANY_NDS(8)
+#define FSCTL_NWR_NDS_GET_QUEUE_INFO NWR_ANY_NDS(9)
+#define FSCTL_NWR_NDS_GET_VOLUME_INFO NWR_ANY_NDS(10)
+#define FSCTL_NWR_NDS_RAW_FRAGEX NWR_ANY_NDS(11)
+#define FSCTL_NWR_NDS_CHANGE_PASS NWR_ANY_NDS(12)
+#define FSCTL_NWR_NDS_LIST_TREES NWR_ANY_NDS(13)
+
+#define IOCTL_NWR_RAW_HANDLE _NWRDR_CONTROL_CODE(1002,METHOD_NEITHER, FILE_ANY_ACCESS)
+
+//
+// UserNcp control code definitions. The parameter (X) to NWR_ANY_NCP
+// is the function code to be placed in the NCP.
+//
+
+#define NWR_ANY_NCP(X) _NWRDR_CONTROL_CODE(0x400 | (X), METHOD_NEITHER, FILE_ANY_ACCESS)
+#define NWR_ANY_F2_NCP(X) _NWRDR_CONTROL_CODE(0x500 | (X), METHOD_NEITHER, FILE_ANY_ACCESS)
+#define NWR_ANY_HANDLE_NCP(X) _NWRDR_CONTROL_CODE(0x600 | (X), METHOD_NEITHER, FILE_ANY_ACCESS)
+#define NWR_ANY_NDS(X) _NWRDR_CONTROL_CODE(0x700 | (X), METHOD_NEITHER, FILE_ANY_ACCESS)
+
+#define FSCTL_NWR_NCP_E3H NWR_ANY_NCP(0x17)
+#define FSCTL_NWR_NCP_E2H NWR_ANY_NCP(0x16)
+#define FSCTL_NWR_NCP_E1H NWR_ANY_NCP(0x15)
+#define FSCTL_NWR_NCP_E0H NWR_ANY_NCP(0x14)
+
+//
+// Macro for obtaining the parameter given to NWR_ANY_XXX when creating
+// a control code to send a UserNcp to the redirector.
+//
+
+#define ANY_NCP_OPCODE(X) ((UCHAR)(((X) >> 2) & 0x00ff))
+
+//
+// Macro to give the command type
+//
+
+#define IS_IT_NWR_ANY_NCP(X) ((X & 0x1C00) == (0x400 << 2))
+#define IS_IT_NWR_ANY_F2_NCP(X) ((X & 0x1C00) == (0x500 << 2))
+#define IS_IT_NWR_ANY_HANDLE_NCP(X) ((X & 0x1C00) == (0x600 << 2))
+
+//
+// Redirector Request Packet used by the Workstation service
+// to pass parameters to the Redirector through Buffer 1 of
+// NtFsControlFile.
+//
+// Additional output of each FSCtl is found in Buffer 2.
+//
+
+#define REQUEST_PACKET_VERSION 0x00000001L // Structure version.
+
+typedef struct _NWR_REQUEST_PACKET {
+
+ ULONG Version; // Version of structure in Buffer 2
+
+ union {
+
+
+ //
+ // For FSCTL_NWR_BIND_TO_TRANSPORT
+ //
+ struct {
+ ULONG QualityOfService; // Quality of service indicator IN
+ ULONG TransportNameLength; // Not including terminator IN
+ WCHAR TransportName[1]; // Name of transport provider IN
+ } Bind;
+
+
+ //
+ // For FSCTL_NWR_LOGON
+ //
+ struct {
+ LUID LogonId; // User logon session identifier IN
+ ULONG UserNameLength; // Byte count not including NULL IN
+ ULONG PasswordLength; // Byte count not including NULL IN
+ ULONG ServerNameLength; // Byte count not including NULL IN
+ ULONG ReplicaAddrLength; // IPX address of the nearest dir server
+ // replica (for NDS login only).
+ // It's either sizeof(TDI_ADDRESS_IPX)
+ // or 0. IN
+
+ WCHAR UserName[1]; // User name not NULL terminated. IN
+
+ // Password string // Default password for connection,
+ // not NULL terminated, packed
+ // in buffer immediately after
+ // UserName. IN
+
+ // ServerName // Preferred server name packed in
+ // buffer immediately after
+ // Password. IN
+
+ // IpxAddress // Address copied from the SAP response
+ // packet, packed immediately after
+ // the servername. IN
+ } Logon;
+
+ //
+ // For FSCTL_NWR_CHANGE_PASS
+ //
+ struct {
+
+ ULONG UserNameLength;
+ ULONG PasswordLength;
+ ULONG ServerNameLength;
+ WCHAR UserName[1];
+
+ // Password string // New password. IN
+
+ // ServerName // Server with the new password IN
+
+ } ChangePass;
+
+ //
+ // For FSCTL_NWR_LOGOFF
+ //
+ struct {
+ LUID LogonId; // User logon session identifier IN
+ } Logoff;
+
+ //
+ // For FSCTL_NWR_DELETE_CONNECTION
+ //
+ struct {
+ BOOLEAN UseForce; // Force flag IN
+ } DeleteConn;
+
+ //
+ // For FSCTL_NWR_GET_CONNECTION
+ //
+ struct {
+ ULONG BytesNeeded; // Size (byte count) required of
+ // output buffer including
+ // terminator OUT
+ ULONG DeviceNameLength; // Not including terminator IN
+ WCHAR DeviceName[4]; // Name of DOS device IN
+ } GetConn;
+
+ //
+ // FSCTL_NWR_ENUMERATE_CONNECTIONS
+ //
+ struct {
+ ULONG EntriesRequested; // Number of entries to get IN
+ ULONG EntriesReturned; // Entries returned in respose buf OUT
+ ULONG ResumeKey; // Handle to next entry to get IN OUT
+ ULONG BytesNeeded; // Size (byte count) of next entry OUT
+ ULONG ConnectionType; // Resource type requested IN
+ } EnumConn;
+
+ //
+ // FSCTL_NWR_SET_INFO
+ //
+ struct {
+ ULONG PrintOption;
+ ULONG MaximumBurstSize;
+
+ ULONG PreferredServerLength; // Byte count not including NULL IN
+ ULONG ProviderNameLength; // Byte count not including NULL IN
+ WCHAR PreferredServer[1]; // Preferred server name not NULL
+ // terminated.
+ // ProviderName string // Provider name not NULL terminated.
+ // Packed in buffer immediately
+ // after PreferredServer
+
+ } SetInfo;
+
+ //
+ // FSCTL_NWR_GET_CONN_STATUS
+ //
+ struct {
+ ULONG ConnectionNameLength; // IN: Length of the connection name we want.
+ ULONG ResumeKey; // IN: Resume key for a continued request.
+ ULONG EntriesReturned; // OUT: Entries returned in respose buffer.
+ ULONG BytesNeeded; // OUT: Size (byte count) of next entry.
+ WCHAR ConnectionName[1]; // IN: Connection name described above.
+ } GetConnStatus;
+
+ //
+ // FSCTL_NWR_GET_CONN_INFO
+ //
+ struct {
+ ULONG ConnectionNameLength; // IN: Length of the connection name we want.
+ WCHAR ConnectionName[1]; // IN: Connection name described above.
+ } GetConnInfo;
+
+ //
+ // FSCTL_NWR_GET_CONN_PERFORMANCE
+ //
+ struct {
+
+ //
+ // These are the fields for the NETCONNECTINFOSTRUCT.
+ //
+
+ DWORD dwFlags;
+ DWORD dwSpeed;
+ DWORD dwDelay;
+ DWORD dwOptDataSize;
+
+ //
+ // This is the remote name in question.
+ //
+
+ ULONG RemoteNameLength;
+ WCHAR RemoteName[1];
+ } GetConnPerformance;
+
+ struct {
+ ULONG DebugFlags; // Value for NwDebug
+ } DebugValue;
+
+ } Parameters;
+
+} NWR_REQUEST_PACKET, *PNWR_REQUEST_PACKET;
+
+typedef struct _NWR_NDS_REQUEST_PACKET {
+
+ //
+ // Version of structure in Buffer 2.
+ //
+
+ ULONG Version;
+
+ union {
+
+ //
+ // For FSCTL_NWR_NDS_RESOLVE_NAME
+ //
+
+ struct {
+ ULONG ObjectNameLength; // IN
+ DWORD ResolverFlags; // IN
+ DWORD BytesWritten; // OUT
+ WCHAR ObjectName[1]; // IN
+ } ResolveName;
+
+ //
+ // For FSCTL_NWR_NDS_READ_INFO
+ //
+
+ struct {
+ DWORD ObjectId; // IN
+ DWORD BytesWritten; // OUT
+ } GetObjectInfo;
+
+ //
+ // For FSCTL_NWR_NDS_LIST_SUBS
+ //
+
+ struct {
+ DWORD ObjectId; // IN
+ DWORD IterHandle; // IN
+ DWORD BytesWritten; // OUT
+ } ListSubordinates;
+
+ //
+ // For FSCTL_NWR_NDS_READ_ATTR
+ //
+
+ struct {
+ DWORD ObjectId; // IN
+ DWORD IterHandle; // IN
+ DWORD BytesWritten; // OUT
+ DWORD AttributeNameLength; // IN
+ WCHAR AttributeName[1]; // IN
+ } ReadAttribute;
+
+ //
+ // For FSCTL_NWR_NDS_OPEN_STREAM
+ //
+
+ struct {
+ DWORD FileLength; // OUT
+ DWORD StreamAccess; // IN
+ DWORD ObjectOid; // IN
+ UNICODE_STRING StreamName; // IN
+ WCHAR StreamNameString[1]; // IN
+ } OpenStream;
+
+ //
+ // For FSCTL_NWR_NDS_SET_CONTEXT
+ //
+
+ struct {
+ DWORD TreeNameLen ; // IN
+ DWORD ContextLen; // IN
+ WCHAR TreeAndContextString[1]; // IN
+ } SetContext;
+
+ //
+ // For FSCTL_NWR_NDS_GET_CONTEXT
+ //
+
+ struct {
+ UNICODE_STRING Context; // OUT
+ DWORD TreeNameLen ; // IN
+ WCHAR TreeNameString[1]; // IN
+ } GetContext;
+
+ //
+ // For FSCTL_NWR_NDS_VERIFY_TREE
+ //
+
+ struct {
+ UNICODE_STRING TreeName; // IN
+ WCHAR NameString[1]; // IN
+ } VerifyTree;
+
+ //
+ // For FSCTL_NWR_NDS_GET_QUEUE_INFO
+ //
+
+ struct {
+ UNICODE_STRING QueueName; // IN
+ UNICODE_STRING HostServer; // OUT
+ DWORD QueueId; // OUT
+ } GetQueueInfo;
+
+ //
+ // For FSCTL_NWR_NDS_GET_VOLUME_INFO
+ //
+
+ struct {
+ DWORD ServerNameLen; // OUT
+ DWORD TargetVolNameLen; // OUT
+ DWORD VolumeNameLen; // IN
+ WCHAR VolumeName[1]; // IN
+ } GetVolumeInfo;
+
+ //
+ // For FSCTL_NWR_NDS_RAW_FRAGEX
+ //
+
+ struct {
+ DWORD NdsVerb; // IN
+ DWORD RequestLength; // IN
+ DWORD ReplyLength; // OUT
+ BYTE Request[1]; // IN
+ } RawRequest;
+
+ //
+ // For FSCTL_NWR_NDS_CHANGE_PASS
+ //
+
+ struct {
+
+ DWORD NdsTreeNameLength;
+ DWORD UserNameLength;
+ DWORD CurrentPasswordLength;
+ DWORD NewPasswordLength;
+
+ //
+ // The above strings should be end to
+ // end starting at StringBuffer.
+ //
+
+ WCHAR StringBuffer[1];
+ } ChangePass;
+
+ //
+ // For FSCTL_NWR_NDS_LIST_TREES
+ //
+
+ struct {
+
+ DWORD NtUserNameLength; // IN
+ LARGE_INTEGER UserLuid; // OUT
+ DWORD TreesReturned; // OUT
+ WCHAR NtUserName[1]; // IN
+ } ListTrees;
+
+ } Parameters;
+
+} NWR_NDS_REQUEST_PACKET, *PNWR_NDS_REQUEST_PACKET;
+
+//
+// Structure of buffer 2 for FSCTL_NWR_GET_CONNECTION
+//
+typedef struct _NWR_SERVER_RESOURCE {
+ WCHAR UncName[1]; // Server resource name DOS device
+ // is connected to; NULL terminated
+} NWR_SERVER_RESOURCE, *PNWR_SERVER_RESOURCE;
+
+//
+// Structure of buffer for FSCTL_NWR_GET_MESSAGE
+//
+
+typedef struct _NWR_SERVER_MESSAGE {
+ ULONG MessageOffset; // Offset from start of buffer to message
+ WCHAR Server[1]; // Source of message, NUL terminated OUT
+ //WCHAR Message[]; // The message text, NUL terminated OUT
+} NWR_SERVER_MESSAGE, *PNWR_SERVER_MESSAGE;
+
+#define TRANSACTION_VERSION 0x00000001L // Structure version.
+typedef struct _NWR_TRANSACTION {
+ ULONG Type; // Type of structure
+ ULONG Size; // Size of fixed portion of structure
+ ULONG Version; // Structure version.
+ ULONG NameLength; // Number of bytes in name (in path
+ // format, e.g., \server\pipe\netapi\4)
+ ULONG NameOffset; // Offset of name in buffer.
+ BOOLEAN ResponseExpected; // Should remote system respond?
+ ULONG Timeout; // Timeout time in milliseconds.
+ ULONG SetupWords; // Number of trans setup words (may be
+ // 0). (setup words are input/output.)
+ ULONG SetupOffset; // Offset of setup (may be 0 for none).
+ ULONG MaxSetup; // Size of setup word array (may be 0).
+ ULONG ParmLength; // Input param area length (may be 0).
+ PVOID ParmPtr; // Input parameter area (may be NULL).
+ ULONG MaxRetParmLength; // Output param. area length (may be 0).
+ ULONG DataLength; // Input data area length (may be 0).
+ PVOID DataPtr; // Input data area (may be NULL).
+ ULONG MaxRetDataLength; // Output data area length (may be 0).
+ PVOID RetDataPtr; // Output data area (may be NULL).
+} NWR_TRANSACTION, *PNWR_TRANSACTION;
+
+typedef struct _NWR_GET_CONNECTION_DETAILS {
+ SERVERNAME ServerName;
+ UCHAR OrderNumber; // Position in the Scb chain starting at 1
+ UCHAR ServerAddress[12];
+ UCHAR ConnectionNumberLo;
+ UCHAR ConnectionNumberHi;
+ UCHAR MajorVersion;
+ UCHAR MinorVersion;
+ BOOLEAN Preferred;
+} NWR_GET_CONNECTION_DETAILS, *PNWR_GET_CONNECTION_DETAILS;
+
+typedef struct _NWR_GET_USERNAME {
+ WCHAR UserName[1];
+} NWR_GET_USERNAME, *PNWR_GET_USERNAME;
+
+typedef struct _NWR_GET_CHALLENGE_REQUEST {
+ ULONG Flags;
+ ULONG ObjectId;
+ UCHAR Challenge[8];
+ ULONG ServerNameorPasswordLength;
+ WCHAR ServerNameorPassword[1]; // No NULL
+} NWR_GET_CHALLENGE_REQUEST, *PNWR_GET_CHALLENGE_REQUEST;
+
+#define CHALLENGE_FLAGS_SERVERNAME 0
+#define CHALLENGE_FLAGS_PASSWORD 1
+
+typedef struct _NWR_GET_CHALLENGE_REPLY {
+ UCHAR Challenge[8];
+} NWR_GET_CHALLENGE_REPLY, *PNWR_GET_CHALLENGE_REPLY;
+
+//
+// Fields marked FIXFIX are not updated. Remove or record...
+//
+typedef struct _NW_REDIR_STATISTICS {
+ LARGE_INTEGER StatisticsStartTime;
+
+ LARGE_INTEGER BytesReceived;
+ LARGE_INTEGER NcpsReceived;
+
+ LARGE_INTEGER BytesTransmitted;
+ LARGE_INTEGER NcpsTransmitted;
+
+ ULONG ReadOperations;
+ ULONG RandomReadOperations; //FIXFIX
+ ULONG ReadNcps;
+ ULONG PacketBurstReadNcps;
+ ULONG PacketBurstReadTimeouts;
+
+ ULONG WriteOperations;
+ ULONG RandomWriteOperations; //FIXFIX
+ ULONG WriteNcps;
+ ULONG PacketBurstWriteNcps;
+ ULONG PacketBurstWriteTimeouts;
+
+ // Connection/Session counts
+ ULONG Sessions;
+ ULONG FailedSessions;
+ ULONG Reconnects;
+ ULONG NW2xConnects; //FIXFIX
+ ULONG NW3xConnects; //FIXFIX
+ ULONG NW4xConnects; //FIXFIX
+ ULONG ServerDisconnects;
+
+ ULONG CurrentCommands;
+} NW_REDIR_STATISTICS, *PNW_REDIR_STATISTICS;
+
+//
+// CONN_STATUS structures for the new shell.
+//
+
+typedef struct _CONN_STATUS {
+ DWORD dwTotalLength; // The total length including packed strings.
+ LPWSTR pszServerName; // The server name.
+ LPWSTR pszUserName; // The user name.
+ LPWSTR pszTreeName; // The tree name or NULL for a 2.x or 3.x server.
+ DWORD nConnNum; // The connection number used on nw srv.
+ BOOL fNds; // TRUE if NDS, False for Bindery servers
+ BOOL fPreferred; // TRUE if the connection is a preferred server with no explicit uses.
+ DWORD dwConnType; // Authentication status of the connection.
+} CONN_STATUS, *PCONN_STATUS;
+
+#define NW_CONN_NOT_AUTHENTICATED 0x00000000
+#define NW_CONN_BINDERY_LOGIN 0x00000001
+#define NW_CONN_NDS_AUTHENTICATED_NO_LICENSE 0x00000002
+#define NW_CONN_NDS_AUTHENTICATED_LICENSED 0x00000003
+#define NW_CONN_DISCONNECTED 0x00000004
+
+typedef struct _CONN_INFORMATION {
+ DWORD HostServerLength;
+ LPWSTR HostServer;
+ DWORD UserNameLength;
+ LPWSTR UserName;
+} CONN_INFORMATION, *PCONN_INFORMATION;
+
+#endif // ifndef _NTDDNWFS_
diff --git a/private/nw/inc/nwapi.h b/private/nw/inc/nwapi.h
new file mode 100644
index 000000000..2b34f27b7
--- /dev/null
+++ b/private/nw/inc/nwapi.h
@@ -0,0 +1,197 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ api.h
+
+Abstract:
+
+ This module contains exposed APIs that is used by the
+ NetWare Control Panel Applet.
+
+Author:
+
+ Yi-Hsin Sung 15-Jul-1993
+
+Revision History:
+
+--*/
+
+#ifndef _NWAPI_INCLUDED_
+#define _NWAPI_INCLUDED_
+
+#include <nwcons.h>
+
+//
+// Bitmask for print options
+//
+
+#define NW_PRINT_SUPPRESS_FORMFEED 0x08
+#define NW_PRINT_PRINT_BANNER 0x80
+#define NW_PRINT_PRINT_NOTIFY 0x10
+
+//
+// Flags for logon script support.
+//
+
+#define NW_LOGONSCRIPT_DISABLED 0x00000000
+#define NW_LOGONSCRIPT_ENABLED 0x00000001
+#define NW_LOGONSCRIPT_4X_ENABLED 0x00000002
+#define NW_LOGONSCRIPT_DEFAULT NW_LOGONSCRIPT_DISABLED
+#define NW_LOGONSCRIPT_DEBUG 0x00000800
+
+//
+// Values for turning on Sync login script flags.
+//
+
+#define SYNC_LOGONSCRIPT 0x1
+#define RESET_SYNC_LOGONSCRIPT 0x2
+
+//
+// Bitmask for gateway redirections
+//
+#define NW_GW_UPDATE_REGISTRY 0x01
+#define NW_GW_CLEANUP_DELETED 0x02
+
+
+DWORD
+NwQueryInfo(
+ OUT PDWORD pnPrintOption,
+ OUT LPWSTR *ppszPreferredSrv
+ );
+
+DWORD
+NwSetInfoInRegistry(
+ IN DWORD nPrintOption,
+ IN LPWSTR pszPreferredSrv
+ );
+
+DWORD
+NwSetLogonOptionsInRegistry(
+ IN DWORD nLogonScriptOptions
+ );
+
+DWORD
+NwQueryLogonOptions(
+ OUT PDWORD pnLogonScriptOptions
+ );
+
+DWORD
+NwSetInfoInWksta(
+ IN DWORD nPrintOption,
+ IN LPWSTR pszPreferredSrv
+ );
+
+DWORD
+NwSetLogonScript(
+ IN DWORD ScriptOptions
+ );
+
+DWORD
+NwValidateUser(
+ IN LPWSTR pszPreferredSrv
+);
+
+DWORD
+NwEnumGWDevices(
+ LPDWORD Index,
+ LPBYTE Buffer,
+ DWORD BufferSize,
+ LPDWORD BytesNeeded,
+ LPDWORD EntriesRead
+ ) ;
+
+DWORD
+NwAddGWDevice(
+ LPWSTR DeviceName,
+ LPWSTR RemoteName,
+ LPWSTR AccountName,
+ LPWSTR Password,
+ DWORD Flags
+ ) ;
+
+DWORD
+NwDeleteGWDevice(
+ LPWSTR DeviceName,
+ DWORD Flags
+ ) ;
+
+DWORD
+NwEnumConnections(
+ HANDLE hEnum,
+ LPDWORD lpcCount,
+ LPVOID lpBuffer,
+ LPDWORD lpBufferSize,
+ BOOL fImplicitConnections
+ );
+
+DWORD
+NwLibSetEverybodyPermission(
+ HKEY hKey,
+ DWORD dwAccessPermission
+ );
+
+DWORD
+NwQueryGatewayAccount(
+ LPWSTR AccountName,
+ DWORD AccountNameLen,
+ LPDWORD AccountCharsNeeded,
+ LPWSTR Password,
+ DWORD PasswordLen,
+ LPDWORD PasswordCharsNeeded
+ );
+
+DWORD
+NwSetGatewayAccount(
+ LPWSTR AccountName,
+ LPWSTR Password
+ );
+
+DWORD
+NwLogonGatewayAccount(
+ LPWSTR AccountName,
+ LPWSTR Password,
+ LPWSTR Server
+ );
+
+DWORD
+NwRegisterGatewayShare(
+ IN LPWSTR ShareName,
+ IN LPWSTR DriveName
+ );
+
+DWORD
+NwClearGatewayShare(
+ IN LPWSTR ShareName
+ );
+
+DWORD
+NwCleanupGatewayShares(
+ VOID
+ );
+
+
+VOID
+MapSpecialJapaneseChars(
+ LPSTR lpszA,
+ WORD length
+ );
+
+VOID
+UnmapSpecialJapaneseChars(
+ LPSTR lpszA,
+ WORD length
+ );
+
+LPSTR
+NwDupStringA(
+ const LPSTR lpszA,
+ WORD length
+ );
+
+#define NwFreeStringA(lp) if((lp) != NULL) { (void)LocalFree((lp)); }
+
+
+#endif
diff --git a/private/nw/inc/nwapi32.h b/private/nw/inc/nwapi32.h
new file mode 100644
index 000000000..d31dbfc81
--- /dev/null
+++ b/private/nw/inc/nwapi32.h
@@ -0,0 +1,810 @@
+//////////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 1993 Microsoft Corporation
+//
+// Module Name:
+//
+// nwapi32.h
+//
+// Abstract:
+//
+// This module contains the support for calls into CSNW.
+//
+// Author:
+//
+// Chris Sandys (a-chrisa) 09-Sep-1993
+//
+// Revision History:
+// Chuck Y Chan Feb 3, 1994 Make it NT like
+// Chuck Y Chan Feb 4, 1996 Merged in calls used by DSMN (from
+// nwcapi32.h)
+//
+//////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef _NWAPI32_H_
+#define _NWAPI32_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+//
+// Order of Funtions
+//
+// NWAddTrusteeToDirectory
+// NWAllocTemporaryDirectoryHandle
+// NWAllocPermanentDirectoryHandle
+// NWAttachToFileServer
+// NWCheckConsolePrivileges
+// NWDeallocateDirectoryHandle
+// NWDetachFromFileServer
+// NWGetFileServerVersionInfo
+// NWGetInternetAddress
+// NWGetObjectName
+// NWGetVolumeInfoWithHandle
+// NWGetVolumeInfoWithNumber
+// NWGetVolumeName
+// NWIsObjectInSet
+// NWLoginToFileServer
+// NWLogoutFromFileServer
+// NWReadPropertyValue
+// NWScanObject
+// NWScanProperty
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Manifests and structures //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+//
+// Version Structure
+//
+#include <packon.h>
+
+typedef struct _VERSION_INFO {
+ BYTE szName[48];
+ BYTE Version;
+ BYTE SubVersion;
+ WORD ConnsSupported;
+ WORD connsInUse;
+ WORD maxVolumes;
+ BYTE OSRev;
+ BYTE SFTLevel;
+ BYTE TTSLevel;
+ WORD PeakConns;
+ BYTE AcctVer;
+ BYTE VAPVer;
+ BYTE QueueVer;
+ BYTE PrintVer;
+ BYTE VirtualConsoleVer;
+ BYTE SecurityResLevel;
+ BYTE InternetworkBVer;
+ BYTE Reserved[60];
+} VERSION_INFO;
+
+#include <packoff.h>
+
+//
+// DLL Defination
+//
+#define DLLEXPORT
+
+//
+// Misc type definitions
+//
+#define NWCCODE USHORT
+#define NWLOCAL_SCOPE USHORT
+#define NWCONN_HANDLE HANDLE
+#define NWFAR
+#define NWAPI WINAPI
+#define NWOBJ_TYPE USHORT
+#define NWOBJ_ID DWORD
+#define NWFLAGS UCHAR
+#define NWVOL_NUM UCHAR
+#define NWDIR_HANDLE UCHAR
+#define NWACCESS_RIGHTS BYTE
+#define NWCONN_NUM USHORT
+#define NWNET_ADDR UCHAR
+#define NWNUMBER WORD
+#define NWVOL_FLAGS WORD
+#define NWSEGMENT_NUM UCHAR
+#define NWSEGMENT_DATA BYTE
+#define NWRIGHTS_MASK WORD
+#define NWSEQUENCE BYTE
+#define NWINDEX_TYPE USHORT
+#define NWDATE_TIME DWORD
+#define NWDIR_TRUSTEE_RIGHTS WORD
+#define NWSEQUENCE BYTE
+
+typedef struct {
+ NWOBJ_ID objectID;
+ NWDIR_TRUSTEE_RIGHTS objectRights;
+} TRUSTEE_INFO;
+
+//
+// Object Types (already in HI-LO format)
+//
+#define OT_WILD 0xFFFF
+#define OT_UNKNOWN 0x0000
+#define OT_USER 0x0100
+#define OT_USER_GROUP 0x0200
+#define OT_PRINT_QUEUE 0x0300
+#define OT_FILE_SERVER 0x0400
+#define OT_JOB_SERVER 0x0500
+#define OT_GATEWAY 0x0600
+#define OT_PRINT_SERVER 0x0700
+#define OT_ARCHIVE_QUEUE 0x0800
+#define OT_ARCHIVE_SERVER 0x0900
+#define OT_JOB_QUEUE 0x0A00
+#define OT_ADMINISTRATION 0x0B00
+#define OT_NAS_SNA_GATEWAY 0x2100
+#define OT_REMOTE_BRIDGE_SERVER 0x2600
+#define OT_TCPIP_GATEWAY 0x2700
+#define OT_DIRSERVER 0x7802
+
+//
+// Bindery object property flag
+//
+#define BF_STATIC 0x00
+#define BF_DYNAMIC 0x01
+#define BF_ITEM 0x00
+#define BF_SET 0x02
+
+//
+// Bindery object security flag
+//
+#define BS_ANY_READ 0x00
+#define BS_LOGGED_READ 0x01
+#define BS_OBJECT_READ 0x02
+#define BS_SUPER_READ 0x03
+#define BS_BINDERY_READ 0x04
+#define BS_ANY_WRITE 0x00
+#define BS_LOGGED_WRITE 0x10
+#define BS_OBJECT_WRITE 0x20
+#define BS_SUPER_WRITE 0x30
+#define BS_BINDERY_WRITE 0x40
+
+//
+// Size Of Things
+//
+#define OBJ_NAME_SIZE 48 // ScanObject name size
+#define VOL_NAME_SIZE 16 // Get Volume Name Size
+#define NW_USER_SIZE 50
+#define NW_GROUP_SIZE 50
+#define NW_PROP_SIZE 50
+#define NW_DATA_SIZE 128
+#define NW_PROP_SET 0x02
+
+//
+// Return Codes
+//
+#define UNSUCCESSFUL -1
+#define SUCCESSFUL 0x0000
+#define REQUESTER_ERROR 0x8800
+
+#define ALREADY_ATTACHED 0x8800
+#define INVALID_CONNECTION 0x8801
+#define NO_CONSOLE_RIGHTS 0x89C6
+#define SERVER_OUT_OF_MEMORY 0x8996
+#define VOLUME_DOES_NOT_EXIST 0x8998
+#define BAD_DIRECTORY_HANDLE 0x899B
+#define INVALID_PATH 0x899C
+#define OBJECT_ALREADY_EXISTS 0x89EE
+#define NO_OBJECT_READ_PRIVILEGE 0x89FB
+#define NO_SUCH_PROPERTY 0x89FB
+#define UNKNOWN_FILE_SERVER 0x89FC
+#define NO_SUCH_OBJECT 0x89FC
+#define NO_FILES_FOUND_ERROR 0x89FF
+
+//
+// Swap MACROS
+//
+#define wSWAP(x) (USHORT)(((((USHORT)x)<<8)&0xFF00) | ((((USHORT)x)>>8)&0x00FF))
+#define dwSWAP(x) (DWORD)( ((((DWORD)x)<<24)&0xFF000000) | ((((DWORD)x)<<8)&0x00FF0000) | ((((DWORD)x)>>8)&0x0000FF00) | ((((DWORD)x)>>24)&0x000000FF) )
+
+#define DW_SIZE 4 // used for placing RAW bytes
+#define W_SIZE 2
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Original functions from Chris Sandys. Keep for compatibility. //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+NWCCODE NWAPI DLLEXPORT
+NWAddTrusteeToDirectory(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE dirHandle,
+ const char NWFAR *pszPath,
+ NWOBJ_ID dwTrusteeID,
+ NWACCESS_RIGHTS rightsMask
+ );
+
+NWCCODE NWAPI DLLEXPORT
+NWAllocPermanentDirectoryHandle(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE dirHandle,
+ char NWFAR *pszDirPath,
+ NWDIR_HANDLE NWFAR *pbNewDirHandle,
+ NWACCESS_RIGHTS NWFAR *pbRightsMask
+ );
+
+NWCCODE NWAPI DLLEXPORT
+NWAllocTemporaryDirectoryHandle(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE dirHandle,
+ char NWFAR *pszDirPath,
+ NWDIR_HANDLE NWFAR *pbNewDirHandle,
+ NWACCESS_RIGHTS NWFAR *pbRightsMask
+ );
+
+NWCCODE NWAPI DLLEXPORT
+NWAttachToFileServer(
+ const char NWFAR *pszServerName,
+ NWLOCAL_SCOPE ScopeFlag,
+ NWCONN_HANDLE NWFAR *phNewConn
+ );
+
+NWCCODE NWAPI DLLEXPORT
+NWAttachToFileServerW(
+ const WCHAR NWFAR *pszServerName,
+ NWLOCAL_SCOPE ScopeFlag,
+ NWCONN_HANDLE NWFAR *phNewConn
+ );
+
+NWCCODE NWAPI DLLEXPORT
+NWCheckConsolePrivileges(
+ NWCONN_HANDLE hConn
+ );
+
+NWCCODE NWAPI DLLEXPORT
+NWDeallocateDirectoryHandle(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE dirHandle
+ );
+
+NWCCODE NWAPI DLLEXPORT
+NWDetachFromFileServer(
+ NWCONN_HANDLE hConn
+ );
+
+NWCCODE NWAPI DLLEXPORT
+NWGetFileServerVersionInfo(
+ NWCONN_HANDLE hConn,
+ VERSION_INFO NWFAR *lpVerInfo
+ );
+
+NWCCODE NWAPI DLLEXPORT
+NWGetInternetAddress(
+ NWCONN_HANDLE hConn,
+ NWCONN_NUM nConnNum,
+ NWNET_ADDR NWFAR *pIntAddr
+ );
+
+NWCCODE NWAPI DLLEXPORT
+NWGetObjectName(
+ NWCONN_HANDLE hConn,
+ NWOBJ_ID dwObjectID,
+ char NWFAR *pszObjName,
+ NWOBJ_TYPE NWFAR *pwObjType
+ );
+
+NWCCODE NWAPI DLLEXPORT
+NWGetVolumeInfoWithHandle(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE nDirHand,
+ char NWFAR *pszVolName,
+ NWNUMBER NWFAR *pwTotalBlocks,
+ NWNUMBER NWFAR *pwSectors,
+ NWNUMBER NWFAR *pwAvailBlocks,
+ NWNUMBER NWFAR *pwTotalDir,
+ NWNUMBER NWFAR *pwAvailDir,
+ NWVOL_FLAGS NWFAR *pfVolRemovable
+ );
+
+NWCCODE NWAPI DLLEXPORT
+NWGetVolumeInfoWithNumber(
+ NWCONN_HANDLE hConn,
+ NWVOL_NUM nVolNum,
+ char NWFAR *pszVolName,
+ NWNUMBER NWFAR *pwTotalBlocks,
+ NWNUMBER NWFAR *pwSectors,
+ NWNUMBER NWFAR *pwAvailBlocks,
+ NWNUMBER NWFAR *pwTotalDir,
+ NWNUMBER NWFAR *pwAvailDir,
+ NWVOL_FLAGS NWFAR *pfVolRemovable
+ );
+
+NWCCODE NWAPI DLLEXPORT
+NWGetVolumeName(
+ NWCONN_HANDLE hConn,
+ NWVOL_NUM bVolNum,
+ char NWFAR *pszVolName
+ );
+
+NWCCODE NWAPI DLLEXPORT /* ??? */
+NWLoginToFileServer(
+ NWCONN_HANDLE hConn,
+ const char NWFAR *pszUserName,
+ NWOBJ_TYPE wObType,
+ const char NWFAR *pszPassword
+ );
+
+NWCCODE NWAPI DLLEXPORT /* ??? */
+NWLogoutFromFileServer(
+ NWCONN_HANDLE hConn
+ );
+
+NWCCODE NWAPI DLLEXPORT /* ??? */
+NWReadPropertyValue(
+ NWCONN_HANDLE hConn,
+ const char NWFAR *pszObjName,
+ NWOBJ_TYPE wObjType,
+ char NWFAR *pszPropName,
+ unsigned char ucSegment,
+ char NWFAR *pValue,
+ NWFLAGS NWFAR *pucMoreFlag,
+ NWFLAGS NWFAR *pucPropFlag
+ );
+
+
+NWCCODE NWAPI DLLEXPORT
+NWScanObject(
+ NWCONN_HANDLE hConn,
+ const char NWFAR *pszSearchName,
+ NWOBJ_TYPE wObjSearchType,
+ NWOBJ_ID NWFAR *pdwObjectID,
+ char NWFAR *pszObjectName,
+ NWOBJ_TYPE NWFAR *pwObjType,
+ NWFLAGS NWFAR *pucHasProperties,
+ NWFLAGS NWFAR *pucObjectFlags,
+ NWFLAGS NWFAR *pucObjSecurity
+ );
+
+NWCCODE NWAPI DLLEXPORT
+NWScanProperty(
+ NWCONN_HANDLE hConn,
+ const char NWFAR *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ char NWFAR *pszSearchName,
+ NWOBJ_ID NWFAR *pdwSequence,
+ char NWFAR *pszPropName,
+ NWFLAGS NWFAR *pucPropFlags,
+ NWFLAGS NWFAR *pucPropSecurity,
+ NWFLAGS NWFAR *pucHasValue,
+ NWFLAGS NWFAR *pucMore
+ );
+
+NWCCODE NWAPI DLLEXPORT
+NWIsObjectInSet(
+ NWCONN_HANDLE hConn,
+ const char NWFAR *lpszObjectName,
+ NWOBJ_TYPE wObjType,
+ const char NWFAR *lpszPropertyName,
+ const char NWFAR *lpszMemberName,
+ NWOBJ_TYPE wMemberType
+ );
+
+NWCCODE NWAPI DLLEXPORT
+NWGetFileServerDateAndTime(
+ NWCONN_HANDLE hConn,
+ BYTE NWFAR *year,
+ BYTE NWFAR *month,
+ BYTE NWFAR *day,
+ BYTE NWFAR *hour,
+ BYTE NWFAR *minute,
+ BYTE NWFAR *second,
+ BYTE NWFAR *dayofweek
+ );
+
+NWCCODE NWAPI DLLEXPORT
+NWCreateQueue(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE dirHandle,
+ const char NWFAR *pszQueueName,
+ NWOBJ_TYPE wQueueType,
+ const char NWFAR *pszPathName,
+ NWOBJ_ID NWFAR *pdwQueueId
+ );
+
+NWCCODE NWAPI DLLEXPORT
+NWChangePropertySecurity(
+ NWCONN_HANDLE hConn,
+ const char NWFAR *pszObjName,
+ NWOBJ_TYPE wObjType,
+ const char NWFAR *pszPropertyName,
+ NWFLAGS ucObjSecurity
+ );
+
+NWCCODE NWAPI DLLEXPORT
+NWDestroyQueue(
+ NWCONN_HANDLE hConn,
+ NWOBJ_ID dwQueueId
+ );
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Newer and more complete NWC functions. //
+// //
+// These functions return NetWare compatible error codes. Win32 error may //
+// be obtained by calling GetLastError(). //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+
+NWCCODE
+NWCAddTrusteeToDirectory(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE dirHandle,
+ const char *pszPath,
+ NWOBJ_ID dwTrusteeID,
+ NWACCESS_RIGHTS rightsMask
+ );
+
+NWCCODE
+NWCAllocPermanentDirectoryHandle(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE dirHandle,
+ char *pszDirPath,
+ NWDIR_HANDLE *pbNewDirHandle,
+ NWACCESS_RIGHTS *pbRightsMask
+ );
+
+NWCCODE
+NWCAllocTemporaryDirectoryHandle(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE dirHandle,
+ char *pszDirPath,
+ NWDIR_HANDLE *pbNewDirHandle,
+ NWACCESS_RIGHTS *pbRightsMask
+ );
+
+NWCCODE
+NWCAttachToFileServer(
+ const char *pszServerName,
+ NWLOCAL_SCOPE ScopeFlag,
+ NWCONN_HANDLE *phNewConn
+ );
+
+NWCCODE
+NWCAttachToFileServerW(
+ const WCHAR *pszServerName,
+ NWLOCAL_SCOPE ScopeFlag,
+ NWCONN_HANDLE *phNewConn
+ );
+
+NWCCODE
+NWCCheckConsolePrivileges(
+ NWCONN_HANDLE hConn
+ );
+
+NWCCODE
+NWCDeallocateDirectoryHandle(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE dirHandle
+ );
+
+NWCCODE
+NWCDetachFromFileServer(
+ NWCONN_HANDLE hConn
+ );
+
+NWCCODE
+NWCGetFileServerVersionInfo(
+ NWCONN_HANDLE hConn,
+ VERSION_INFO *lpVerInfo
+ );
+
+NWCCODE
+NWCGetInternetAddress(
+ NWCONN_HANDLE hConn,
+ NWCONN_NUM nConnNum,
+ NWNET_ADDR *pIntAddr
+ );
+
+NWCCODE
+NWCGetObjectName(
+ NWCONN_HANDLE hConn,
+ NWOBJ_ID dwObjectID,
+ char *pszObjName,
+ NWOBJ_TYPE *pwObjType
+ );
+
+NWCCODE
+NWCGetVolumeInfoWithHandle(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE nDirHand,
+ char *pszVolName,
+ NWNUMBER *pwTotalBlocks,
+ NWNUMBER *pwSectors,
+ NWNUMBER *pwAvailBlocks,
+ NWNUMBER *pwTotalDir,
+ NWNUMBER *pwAvailDir,
+ NWVOL_FLAGS *pfVolRemovable
+ );
+
+NWCCODE
+NWCGetVolumeInfoWithNumber(
+ NWCONN_HANDLE hConn,
+ NWVOL_NUM nVolNum,
+ char *pszVolName,
+ NWNUMBER *pwTotalBlocks,
+ NWNUMBER *pwSectors,
+ NWNUMBER *pwAvailBlocks,
+ NWNUMBER *pwTotalDir,
+ NWNUMBER *pwAvailDir,
+ NWVOL_FLAGS *pfVolRemovable
+ );
+
+NWCCODE
+NWCGetVolumeName(
+ NWCONN_HANDLE hConn,
+ NWVOL_NUM bVolNum,
+ char *pszVolName
+ );
+
+NWCCODE
+NWCLoginToFileServer(
+ NWCONN_HANDLE hConn,
+ const char *pszUserName,
+ NWOBJ_TYPE wObType,
+ const char *pszPassword
+ );
+
+NWCCODE
+NWCLogoutFromFileServer(
+ NWCONN_HANDLE hConn
+ );
+
+NWCCODE
+NWCReadPropertyValue(
+ NWCONN_HANDLE hConn,
+ const char *pszObjName,
+ NWOBJ_TYPE wObjType,
+ char *pszPropName,
+ unsigned char ucSegment,
+ char *pValue,
+ NWFLAGS *pucMoreFlag,
+ NWFLAGS *pucPropFlag
+ );
+
+
+NWCCODE
+NWCScanObject(
+ NWCONN_HANDLE hConn,
+ const char *pszSearchName,
+ NWOBJ_TYPE wObjSearchType,
+ NWOBJ_ID *pdwObjectID,
+ char *pszObjectName,
+ NWOBJ_TYPE *pwObjType,
+ NWFLAGS *pucHasProperties,
+ NWFLAGS *pucObjectFlags,
+ NWFLAGS *pucObjSecurity
+ );
+
+NWCCODE
+NWCScanProperty(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ char *pszSearchName,
+ NWOBJ_ID *pdwSequence,
+ char *pszPropName,
+ NWFLAGS *pucPropFlags,
+ NWFLAGS *pucPropSecurity,
+ NWFLAGS *pucHasValue,
+ NWFLAGS *pucMore
+ );
+
+NWCCODE
+NWCIsObjectInSet(
+ NWCONN_HANDLE hConn,
+ const char *lpszObjectName,
+ NWOBJ_TYPE wObjType,
+ const char *lpszPropertyName,
+ const char *lpszMemberName,
+ NWOBJ_TYPE wMemberType
+ );
+
+NWCCODE
+NWCGetFileServerDateAndTime(
+ NWCONN_HANDLE hConn,
+ BYTE *year,
+ BYTE *month,
+ BYTE *day,
+ BYTE *hour,
+ BYTE *minute,
+ BYTE *second,
+ BYTE *dayofweek
+ );
+
+NWCCODE
+NWCAddTrustee(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE dirHandle,
+ const char *pszPath,
+ NWOBJ_ID dwTrusteeID,
+ NWRIGHTS_MASK rightsMask
+ );
+
+NWCCODE
+NWCDeleteObject(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType
+ );
+
+NWCCODE
+NWCCreateObject(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ NWFLAGS ucObjectFlags,
+ NWFLAGS ucObjSecurity
+ );
+
+NWCCODE
+NWCCreateProperty(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ const char *pszPropertyName,
+ NWFLAGS ucObjectFlags,
+ NWFLAGS ucObjSecurity
+ );
+
+NWCCODE
+NWCDeleteProperty(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ const char *pszPropertyName
+ );
+
+NWCCODE
+NWCWritePropertyValue(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ const char *pszPropertyName,
+ NWSEGMENT_NUM segmentNumber,
+ NWSEGMENT_DATA *segmentData,
+ NWFLAGS moreSegments
+ );
+
+NWCCODE
+NWCGetObjectID(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ NWOBJ_ID *objectID
+ );
+
+NWCCODE
+NWCRenameBinderyObject(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ const char *pszNewObjectName,
+ NWOBJ_TYPE wObjType
+ );
+
+NWCCODE
+NWCAddObjectToSet(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ const char *pszPropertyName,
+ const char *pszMemberName,
+ NWOBJ_TYPE memberType
+ );
+
+NWCCODE
+NWCDeleteObjectFromSet(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ const char *pszPropertyName,
+ const char *pszMemberName,
+ NWOBJ_TYPE memberType
+ );
+
+NWCCODE
+NWCCreateDirectory(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE dirHandle,
+ const char *pszPath,
+ NWACCESS_RIGHTS accessMask
+ );
+
+NWCCODE
+NWCScanForTrustees(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE dirHandle,
+ char *pszsearchDirPath,
+ NWSEQUENCE *pucsequenceNumber,
+ BYTE *numberOfEntries,
+ TRUSTEE_INFO *tl
+ );
+
+NWCCODE
+NWCScanDirectoryForTrustees2(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE dirHandle,
+ char *pszsearchDirPath,
+ NWSEQUENCE *pucsequenceNumber,
+ char *pszdirName,
+ NWDATE_TIME *dirDateTime,
+ NWOBJ_ID *ownerID,
+ TRUSTEE_INFO *trusteeList
+ );
+
+NWCCODE
+NWCGetBinderyAccessLevel(
+ NWCONN_HANDLE hConn,
+ NWFLAGS *accessLevel,
+ NWOBJ_ID *objectID
+ );
+
+NWCCODE
+NWCGetFileServerDescription(
+ NWCONN_HANDLE hConn,
+ char *pszCompany,
+ char *pszVersion,
+ char *pszRevision
+);
+
+NWCCODE
+NWCGetVolumeNumber(
+ NWCONN_HANDLE hConn,
+ char *pszVolume,
+ NWVOL_NUM *VolumeNumber
+);
+
+NWCCODE
+NWCGetVolumeUsage(
+ NWCONN_HANDLE hConn,
+ NWVOL_NUM VolumeNumber,
+ DWORD *TotalBlocks,
+ DWORD *FreeBlocks,
+ DWORD *PurgeableBlocks,
+ DWORD *NotYetPurgeableBlocks,
+ DWORD *TotalDirectoryEntries,
+ DWORD *AvailableDirectoryEntries,
+ BYTE *SectorsPerBlock
+);
+
+NWCCODE
+NWCCreateQueue(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE dirHandle,
+ const char NWFAR *pszQueueName,
+ NWOBJ_TYPE wQueueType,
+ const char NWFAR *pszPathName,
+ NWOBJ_ID NWFAR *pdwQueueId
+ );
+
+NWCCODE
+NWCChangePropertySecurity(
+ NWCONN_HANDLE hConn,
+ const char NWFAR *pszObjName,
+ NWOBJ_TYPE wObjType,
+ const char NWFAR *pszPropertyName,
+ NWFLAGS ucObjSecurity
+ );
+
+NWCCODE
+NWCDestroyQueue(
+ NWCONN_HANDLE hConn,
+ NWOBJ_ID dwQueueId
+ );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif //_NWAPI32_H_
diff --git a/private/nw/inc/nwcanon.h b/private/nw/inc/nwcanon.h
new file mode 100644
index 000000000..7dc514467
--- /dev/null
+++ b/private/nw/inc/nwcanon.h
@@ -0,0 +1,55 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ nwcanon.h
+
+Abstract:
+
+ Header for NetWare names canonicalization library routines.
+
+Author:
+
+ Rita Wong (ritaw) 19-Feb-1993
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+#ifndef _NW_CANON_INCLUDED_
+#define _NW_CANON_INCLUDED_
+
+DWORD
+NwLibValidateLocalName(
+ IN LPWSTR LocalName
+ );
+
+DWORD
+NwLibCanonLocalName(
+ IN LPWSTR LocalName,
+ OUT LPWSTR *OutputBuffer,
+ OUT LPDWORD OutputBufferLength OPTIONAL
+ );
+
+DWORD
+NwLibCanonRemoteName(
+ IN LPWSTR LocalName OPTIONAL,
+ IN LPWSTR RemoteName,
+ OUT LPWSTR *OutputBuffer,
+ OUT LPDWORD OutputBufferLength OPTIONAL
+ );
+
+DWORD
+NwLibCanonUserName(
+ IN LPWSTR UserName,
+ OUT LPWSTR *OutputBuffer,
+ OUT LPDWORD OutputBufferLength OPTIONAL
+ );
+
+#endif // _NW_CANON_INCLUDED_
diff --git a/private/nw/inc/nwcons.h b/private/nw/inc/nwcons.h
new file mode 100644
index 000000000..64455e325
--- /dev/null
+++ b/private/nw/inc/nwcons.h
@@ -0,0 +1,39 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ nwcons.h
+
+Abstract:
+
+ This module contains various Netware constants
+
+Author:
+
+ Chuck Y Chan. Split out from NWAPI.H
+
+Revision History:
+
+--*/
+
+#ifndef _NWCONS_INCLUDED_
+#define _NWCONS_INCLUDED_
+
+//
+// Maximum length of server, password, username
+//
+
+#define NW_MAX_TREE_LEN 32
+#define NW_MAX_SERVER_LEN 48
+#define NW_MAX_PASSWORD_LEN 256
+#define NW_MAX_USERNAME_LEN 256
+
+//
+// special char to distinguish nds context: eg. *tree\ou.o (as opposed to
+// server\volume.
+//
+#define TREE_CHAR L'*'
+
+#endif
diff --git a/private/nw/inc/nwpapi32.h b/private/nw/inc/nwpapi32.h
new file mode 100644
index 000000000..415125e71
--- /dev/null
+++ b/private/nw/inc/nwpapi32.h
@@ -0,0 +1,297 @@
+//////////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 1993 Microsoft Corporation
+//
+// Module Name:
+//
+// nwapi32.h
+//
+// Abstract:
+//
+// This module contains the support to routines
+// into the CSNW that use NTSTATUS.
+//
+// Author:
+//
+// Chris Sandys (a-chrisa) 09-Sep-1993
+//
+// Revision History:
+// Chuck Y Chan Feb 7, 1996 Spilt of NTSTATUS type calls
+// from nwapi32.h
+//
+//////////////////////////////////////////////////////////////////////////////
+
+
+#ifndef _NWPAPI32_H_
+#define _NWPAPI32_H_
+
+#include <nwapi32.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// //
+// Versions of functions above, but return NTSTATUS. Keep around because //
+// existing DSMN code calls them. Do not extenf this set. The NWC* functions //
+// are the ones to use. //
+// //
+///////////////////////////////////////////////////////////////////////////////
+
+NTSTATUS
+NWPAttachToFileServerW(
+ const WCHAR *pszServerName,
+ NWLOCAL_SCOPE ScopeFlag,
+ NWCONN_HANDLE *phNewConn
+ );
+
+NTSTATUS
+NWPDetachFromFileServer(
+ NWCONN_HANDLE hConn
+ );
+
+NTSTATUS
+NWPGetFileServerVersionInfo(
+ NWCONN_HANDLE hConn,
+ VERSION_INFO *lpVerInfo
+ );
+
+DWORD
+NWPLoginToFileServerW(
+ NWCONN_HANDLE hConn,
+ LPWSTR pszUserName,
+ NWOBJ_TYPE wObType,
+ LPWSTR pszPassword
+ );
+
+DWORD
+NWPLogoutFromFileServer(
+ NWCONN_HANDLE hConn
+ );
+
+NTSTATUS
+NWPDeleteObject(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType
+ );
+
+NTSTATUS
+NWPCreateObject(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ NWFLAGS ucObjectFlags,
+ NWFLAGS ucObjSecurity
+ );
+
+NTSTATUS
+NWPWritePropertyValue(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ const char *pszPropertyName,
+ NWSEGMENT_NUM segmentNumber,
+ NWSEGMENT_DATA *segmentData,
+ NWFLAGS moreSegments
+ );
+
+NTSTATUS
+NWPChangeObjectPasswordEncrypted(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ BYTE *validationKey,
+ BYTE *newKeyedPassword
+ );
+
+NTSTATUS
+NWPGetObjectID(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ NWOBJ_ID *objectID
+ );
+
+NTSTATUS
+NWPAddObjectToSet(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ const char *pszPropertyName,
+ const char *pszMemberName,
+ NWOBJ_TYPE memberType
+ );
+
+NTSTATUS
+NWPDeleteObjectFromSet(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ const char *pszPropertyName,
+ const char *pszMemberName,
+ NWOBJ_TYPE memberType
+ );
+
+NTSTATUS
+NWPCreateProperty(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ const char *pszPropertyName,
+ NWFLAGS ucObjectFlags,
+ NWFLAGS ucObjSecurity
+ );
+
+NTSTATUS
+NWPDeleteProperty(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ const char *pszPropertyName
+ );
+
+NTSTATUS
+NWPGetChallengeKey(
+ NWCONN_HANDLE hConn,
+ UCHAR *challengeKey
+ );
+
+NTSTATUS
+NWPReadPropertyValue(
+ NWCONN_HANDLE hConn,
+ const char *pszObjName,
+ NWOBJ_TYPE wObjType,
+ char *pszPropName,
+ unsigned char ucSegment,
+ char *pValue,
+ NWFLAGS *pucMoreFlag,
+ NWFLAGS *pucPropFlag
+ );
+
+
+NTSTATUS
+NWPCreateDirectory(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE dirHandle,
+ const char *pszPath,
+ NWACCESS_RIGHTS accessMask
+ );
+
+NTSTATUS
+NWPAddTrustee(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE dirHandle,
+ const char *pszPath,
+ NWOBJ_ID dwTrusteeID,
+ NWRIGHTS_MASK rightsMask
+ );
+
+NTSTATUS
+NWPRenameBinderyObject(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ const char *pszNewObjectName,
+ NWOBJ_TYPE wObjType
+ );
+
+NTSTATUS
+NWPGetObjectName(
+ NWCONN_HANDLE hConn,
+ NWOBJ_ID dwObjectID,
+ char *pszObjName,
+ NWOBJ_TYPE *pwObjType
+ );
+
+NTSTATUS
+NWPScanObject(
+ NWCONN_HANDLE hConn,
+ const char *pszSearchName,
+ NWOBJ_TYPE wObjSearchType,
+ NWOBJ_ID *pdwObjectID,
+ char *pszObjectName,
+ NWOBJ_TYPE *pwObjType,
+ NWFLAGS *pucHasProperties,
+ NWFLAGS *pucObjectFlags,
+ NWFLAGS *pucObjSecurity
+ );
+
+NTSTATUS
+NWPScanProperty(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ char *pszSearchName,
+ NWOBJ_ID *pdwSequence,
+ char *pszPropName,
+ NWFLAGS *pucPropFlags,
+ NWFLAGS *pucPropSecurity,
+ NWFLAGS *pucHasValue,
+ NWFLAGS *pucMore
+ );
+
+NTSTATUS
+NWPScanForTrustees(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE dirHandle,
+ char *pszsearchDirPath,
+ NWSEQUENCE *pucsequenceNumber,
+ BYTE *numberOfEntries,
+ TRUSTEE_INFO *tl
+ );
+
+NTSTATUS
+NWPScanDirectoryForTrustees2(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE dirHandle,
+ char *pszsearchDirPath,
+ NWSEQUENCE *pucsequenceNumber,
+ char *pszdirName,
+ NWDATE_TIME *dirDateTime,
+ NWOBJ_ID *ownerID,
+ TRUSTEE_INFO *trusteeList
+ );
+
+NTSTATUS
+NWPGetBinderyAccessLevel(
+ NWCONN_HANDLE hConn,
+ NWFLAGS *accessLevel,
+ NWOBJ_ID *objectID
+ );
+
+NTSTATUS
+NWPGetFileServerDescription(
+ NWCONN_HANDLE hConn,
+ char *pszCompany,
+ char *pszVersion,
+ char *pszRevision
+);
+
+NTSTATUS
+NWPGetVolumeNumber(
+ NWCONN_HANDLE hConn,
+ char *pszVolume,
+ NWVOL_NUM *VolumeNumber
+);
+
+NTSTATUS
+NWPGetVolumeUsage(
+ NWCONN_HANDLE hConn,
+ NWVOL_NUM VolumeNumber,
+ DWORD *TotalBlocks,
+ DWORD *FreeBlocks,
+ DWORD *PurgeableBlocks,
+ DWORD *NotYetPurgeableBlocks,
+ DWORD *TotalDirectoryEntries,
+ DWORD *AvailableDirectoryEntries,
+ BYTE *SectorsPerBlock
+);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif //_NWPAPI32_H_
diff --git a/private/nw/inc/nwpkstr.h b/private/nw/inc/nwpkstr.h
new file mode 100644
index 000000000..ae7f7e96e
--- /dev/null
+++ b/private/nw/inc/nwpkstr.h
@@ -0,0 +1,37 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ nwpkstr.h
+
+Abstract:
+
+ Header for NetWare string packing library routines.
+
+Author:
+
+ Rita Wong (ritaw) 2-Mar-1993
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+#ifndef _NW_PKSTR_INCLUDED_
+#define _NW_PKSTR_INCLUDED_
+
+BOOL
+NwlibCopyStringToBuffer(
+ IN LPCWSTR SourceString OPTIONAL,
+ IN DWORD CharacterCount,
+ IN LPCWSTR FixedDataEnd,
+ IN OUT LPWSTR *EndOfVariableData,
+ OUT LPWSTR *VariableDataPointer
+ );
+
+#endif // _NW_PKSTR_INCLUDED_
diff --git a/private/nw/inc/nwrnames.h b/private/nw/inc/nwrnames.h
new file mode 100644
index 000000000..46acd22f3
--- /dev/null
+++ b/private/nw/inc/nwrnames.h
@@ -0,0 +1,55 @@
+/*++
+
+Copyright (c) 1992-1993 Microsoft Corporation
+
+Module Name:
+
+ nwreg.h
+
+Abstract:
+
+ Header which specifies the registry location and value names
+ of the logon credential information written by the provider and
+ read by the workstation service.
+
+ Also contains helper routine prototypes.
+
+Author:
+
+ Rita Wong (ritaw) 22-Mar-1993
+
+Revision History:
+
+--*/
+
+#ifndef _NWRNAMES_INCLUDED_
+#define _NWRNAMES_INCLUDED_
+
+
+#define NW_WORKSTATION_REGKEY L"System\\CurrentControlSet\\Services\\NWCWorkstation\\Parameters"
+#define NW_WORKSTATION_OPTION_REGKEY L"System\\CurrentControlSet\\Services\\NWCWorkstation\\Parameters\\Option"
+#define NW_WORKSTATION_LOGON_REGKEY L"System\\CurrentControlSet\\Services\\NWCWorkstation\\Parameters\\Logon"
+#define NW_SERVICE_LOGON_REGKEY L"System\\CurrentControlSet\\Services\\NWCWorkstation\\Parameters\\ServiceLogon"
+#define NW_WORKSTATION_GATEWAY_DRIVES L"System\\CurrentControlSet\\Services\\NWCWorkstation\\Drives"
+#define NW_WORKSTATION_GATEWAY_SHARES L"System\\CurrentControlSet\\Services\\NWCWorkstation\\Shares"
+#define NW_WORKSTATION_PROVIDER_PATH L"System\\CurrentControlSet\\Services\\NWCWorkstation\\networkprovider"
+
+#define NW_PROVIDER_VALUENAME L"Name"
+
+#define NW_CURRENTUSER_VALUENAME L"CurrentUser"
+#define NW_GATEWAYACCOUNT_VALUENAME L"GatewayAccount"
+#define NW_GATEWAY_ENABLE L"GatewayEnabled"
+
+#define NW_SERVER_VALUENAME L"PreferredServer"
+#define NW_LOGONSCRIPT_VALUENAME L"LogonScript"
+#define NW_LOGONID_VALUENAME L"LogonID"
+#define NW_PRINTOPTION_VALUENAME L"PrintOption"
+#define NW_SYNCLOGONSCRIPT_VALUENAME L"ResetScriptFlag"
+#define NW_DEFAULTSERVER_VALUENAME L"DefaultLocation"
+#define NW_DEFAULTSCRIPTOPTIONS_VALUENAME L"DefaultScriptOptions"
+
+#define WINLOGON_REGKEY L"Software\\Microsoft\\Windows NT\\CurrentVersion\\WinLogon"
+#define SYNCLOGONSCRIPT_VALUENAME L"RunLogonScriptSync"
+
+
+#endif // _NWRNAMES_INCLUDED_
diff --git a/private/nw/inc/nwrpcp.h b/private/nw/inc/nwrpcp.h
new file mode 100644
index 000000000..51dd70572
--- /dev/null
+++ b/private/nw/inc/nwrpcp.h
@@ -0,0 +1,138 @@
+/*++
+
+Copyright (c) 1990-1993 Microsoft Corporation
+
+Module Name:
+
+ nwrpcp.h
+
+Abstract:
+
+ This file contains prototypes for commonly used RPC functionality.
+ This includes: bind/unbind functions, MIDL user alloc/free functions,
+ and server start/stop functions.
+
+Author:
+
+ Dan Lafferty danl 06-Feb-1991
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 06-Feb-1991 danl
+ Created
+
+ 26-Apr-1991 JohnRo
+ Added IN and OUT keywords to MIDL functions. Commented-out
+ (nonstandard) identifier on endif. Deleted tabs.
+
+ 03-July-1991 JimK
+ Commonly used aspects copied from LM specific file.
+
+ 10-Feb-1993 RitaW
+ Copied to the NetWare tree so that the LPC transport can used for
+ the local case.
+
+--*/
+#ifndef _NWRPCP_
+#define _NWRPCP_
+
+#include <nt.h>
+#include <ntrtl.h> // needed for nturtl.h
+#include <nturtl.h>
+#include <windows.h> // win32 typedefs
+#include <rpc.h>
+
+//
+// DEFINES
+//
+
+
+
+//
+// Function Prototypes - routines called by MIDL-generated code:
+//
+
+void *
+MIDL_user_allocate(
+ IN unsigned int NumBytes
+ );
+
+void
+MIDL_user_free(
+ IN void *MemPointer
+ );
+
+//
+// Function Prototypes - routines to go along with the above, but aren't
+// needed by MIDL or any other non-network software.
+//
+
+void *
+MIDL_user_reallocate(
+ IN void * OldPointer OPTIONAL,
+ IN unsigned long NewByteCount
+ );
+
+unsigned long
+MIDL_user_size(
+ IN void * Pointer
+ );
+
+//
+// client side functions
+//
+
+
+RPC_STATUS
+RpcpBindRpc(
+#if 0
+ IN LPWSTR servername,
+#endif
+ IN LPWSTR servicename,
+ IN LPWSTR networkoptions,
+ OUT RPC_BINDING_HANDLE * pBindingHandle
+ );
+
+VOID
+RpcpUnbindRpc(
+ IN RPC_BINDING_HANDLE BindingHandle
+ );
+
+
+
+//
+// server side functions
+//
+
+VOID
+RpcpInitRpcServer(
+ VOID
+ );
+
+RPC_STATUS
+RpcpAddInterface(
+ IN LPWSTR InterfaceName,
+ IN RPC_IF_HANDLE InterfaceSpecification
+ );
+
+RPC_STATUS
+RpcpStartRpcServer(
+ IN LPWSTR InterfaceName,
+ IN RPC_IF_HANDLE InterfaceSpecification
+ );
+
+RPC_STATUS
+RpcpDeleteInterface(
+ IN RPC_IF_HANDLE InterfaceSpecification
+ );
+
+RPC_STATUS
+RpcpStopRpcServer(
+ IN RPC_IF_HANDLE InterfaceSpecification
+ );
+
+#endif // _NWRPCP_
diff --git a/private/nw/inc/nwsnames.h b/private/nw/inc/nwsnames.h
new file mode 100644
index 000000000..df57005d9
--- /dev/null
+++ b/private/nw/inc/nwsnames.h
@@ -0,0 +1,27 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ nwsnames.h
+
+Abstract:
+
+ Header for NetWare service names.
+
+Author:
+
+ Rita Wong (ritaw) 26-Feb-1993
+
+Revision History:
+
+--*/
+
+#ifndef _NW_SNAMES_INCLUDED_
+#define _NW_SNAMES_INCLUDED_
+
+
+#define NW_WORKSTATION_SERVICE L"NWCWorkstation"
+
+#endif // _NW_SNAMES_INCLUDED_
diff --git a/private/nw/inc/nwstatus.h b/private/nw/inc/nwstatus.h
new file mode 100644
index 000000000..98d6b6c05
--- /dev/null
+++ b/private/nw/inc/nwstatus.h
@@ -0,0 +1,27 @@
+/*++ BUILD Version: 0001 // Increment this if a change has global effects
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ nwstatus.h
+
+Abstract:
+
+ This module defines NWRDR specific NTSTATUS
+
+Author:
+
+Revision History:
+
+--*/
+
+#ifndef _NWSTATUS_
+#define _NWSTATUS_
+
+#define FACILITY_NWRDR 0x11
+
+#define NWRDR_PASSWORD_HAS_EXPIRED 0x40110001
+
+#endif // _NWSTATUS_
+
diff --git a/private/nw/inc/nwsvc.h b/private/nw/inc/nwsvc.h
new file mode 100644
index 000000000..0205e0ea1
--- /dev/null
+++ b/private/nw/inc/nwsvc.h
@@ -0,0 +1,88 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ nwsvc.h
+
+Abstract:
+
+ Header file included by services which share the same nwsvc.exe.
+
+Author:
+
+ Rita Wong (ritaw) 26-Feb-1993
+
+Revision History:
+
+
+--*/
+
+#ifndef _NW_SVC_INCLUDED_
+#define _NW_SVC_INCLUDED_
+
+#ifndef RPC_NO_WINDOWS_H // Don't let rpc.h include windows.h
+#define RPC_NO_WINDOWS_H
+#endif // RPC_NO_WINDOWS_H
+
+#include <rpc.h> // RPC_IF_HANDLE
+
+#include <nwsnames.h>
+
+//
+// Service DLLs loaded into nwsvcs.exe all export the same main
+// entry point. NWSVC_ENTRY_POINT defines that name.
+//
+// Note that NWSVC_ENTRY_POINT_STRING is always ANSI, because that's
+// what GetProcAddress takes.
+//
+
+#define NWSVC_ENTRY_POINT ServiceEntry
+#define NWSVC_ENTRY_POINT_STRING "ServiceEntry"
+
+//
+// Start and stop RPC server entry point prototype.
+//
+
+typedef
+RPC_STATUS
+(*PNWSVC_START_RPC_SERVER) (
+ IN LPWSTR InterfaceName,
+ IN RPC_IF_HANDLE InterfaceSpecification
+ );
+
+typedef
+RPC_STATUS
+(*PNWSVC_STOP_RPC_SERVER) (
+ IN RPC_IF_HANDLE InterfaceSpecification
+ );
+
+//
+// Structure containing global data for the various DLLs.
+//
+
+typedef struct _NWSVC_GLOBAL_DATA {
+
+ //
+ // RPC utilities called by service DLLs which alters global data
+ // in nwsvc.exe.
+ //
+ PNWSVC_START_RPC_SERVER StartRpcServer;
+ PNWSVC_STOP_RPC_SERVER StopRpcServer;
+
+} NWSVC_GLOBAL_DATA, *PNWSVC_GLOBAL_DATA;
+
+//
+// Service DLL entry point prototype.
+//
+
+typedef
+VOID
+(*PNWSVC_SERVICE_DLL_ENTRY) (
+ IN DWORD argc,
+ IN LPWSTR argv[],
+ IN PNWSVC_GLOBAL_DATA pGlobalData
+ );
+
+#endif // ndef _NW_SVC_INCLUDED_
diff --git a/private/nw/inc/nwxchg.h b/private/nw/inc/nwxchg.h
new file mode 100644
index 000000000..fdfc388d9
--- /dev/null
+++ b/private/nw/inc/nwxchg.h
@@ -0,0 +1,45 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ nwxchg.h
+
+Abstract:
+
+ Header for generic NCP calling routine.
+
+Author:
+
+ Rita Wong (ritaw) 11-Mar-1993
+
+Environment:
+
+
+Revision History:
+
+--*/
+
+#ifndef _NW_XCHG_INCLUDED_
+#define _NW_XCHG_INCLUDED_
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+NTSTATUS
+NwlibMakeNcp(
+ IN HANDLE DeviceHandle,
+ IN ULONG FsControlCode,
+ IN ULONG RequestBufferSize,
+ IN ULONG ResponseBufferSize,
+ IN PCHAR FormatString,
+ ... // Arguments to FormatString
+ );
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif // _NW_XCHG_INCLUDED_
diff --git a/private/nw/inc/usa/messages.inc b/private/nw/inc/usa/messages.inc
new file mode 100644
index 000000000..19920f559
--- /dev/null
+++ b/private/nw/inc/usa/messages.inc
@@ -0,0 +1,41 @@
+page ,132
+if 0
+
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ messages.asm
+
+Abstract:
+
+ Contains internationalisable strings and lengths
+
+Author:
+
+ Richard L Firth (rfirth) 5-Oct-1993
+
+Environment:
+
+ DOS real mode only
+
+[Notes:]
+
+ optional-notes
+
+Revision History:
+
+ 5-Oct-1993 rfirth
+ Created
+
+--*/
+
+endif
+
+NLS_MSG_001 equ "Incorrect DOS Version"
+NLS_MSG_002 equ "VDM IPX/SPX support is already loaded"
+NLS_MSG_003 equ "Cannot load VDM IPX/SPX support"
+NLS_MSG_004 equ "The Vdm NetWare Redirector is already loaded"
+NLS_MSG_005 equ "The Vdm NetWare Redirector cannot be loaded"
diff --git a/private/nw/inc/validc.h b/private/nw/inc/validc.h
new file mode 100644
index 000000000..58ea46e0f
--- /dev/null
+++ b/private/nw/inc/validc.h
@@ -0,0 +1,87 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ validc.h
+
+Abstract:
+
+ Strings of valid/invalid characters for canonicalization
+
+Author:
+
+ Richard Firth (rfirth) 15-May-1991
+
+Revision History:
+
+ 03-Jan-1992 rfirth
+ Added ILLEGAL_FAT_CHARS and ILLEGAL_HPFS_CHARS (from fsrtl\name.c)
+
+ 27-Sep-1991 JohnRo
+ Changed TEXT macro usage to allow UNICODE.
+
+ 19-Feb-1993 RitaW
+ Ported for NetWare use.
+
+--*/
+
+//
+// Disallowed control characters (not including \0)
+//
+
+#define CTRL_CHARS_0 L"\001\002\003\004\005\006\007"
+#define CTRL_CHARS_1 L"\010\011\012\013\014\015\016\017"
+#define CTRL_CHARS_2 L"\020\021\022\023\024\025\026\027"
+#define CTRL_CHARS_3 L"\030\031\032\033\034\035\036\037"
+
+#define CTRL_CHARS_STR CTRL_CHARS_0 CTRL_CHARS_1 CTRL_CHARS_2 CTRL_CHARS_3
+
+//
+// Character subsets
+//
+
+#define NON_COMPONENT_CHARS L"\\/:"
+#define ILLEGAL_CHARS_STR L"\"<>|"
+#define SPACE_STR L" "
+#define PATH_SEPARATORS L"\\/"
+
+//
+// Combinations of the above
+//
+
+#define ILLEGAL_CHARS CTRL_CHARS_STR ILLEGAL_CHARS_STR
+#define ILLEGAL_NAME_CHARS_STR L"\"/\\[]:|<>+;,?" CTRL_CHARS_STR // "=" removed for NDS
+
+#define STANDARD_ILLEGAL_CHARS ILLEGAL_NAME_CHARS_STR L"*"
+#define SERVER_ILLEGAL_CHARS STANDARD_ILLEGAL_CHARS SPACE_STR
+
+//
+// Characters which may not appear in a canonicalized FAT filename are:
+//
+// 0x00 - 0x1f " * + , / : ; < = > ? [ \ ] |
+//
+
+#define ILLEGAL_FAT_CHARS CTRL_CHARS_STR L"\"*+,/:;<=>?[\\]|"
+
+//
+// Characters which may not appear in a canonicalized HPFS filename are:
+//
+// 0x00 - 0x1f " * / : < > ? \ |
+//
+
+#define ILLEGAL_HPFS_CHARS CTRL_CHARS_STR L"\"*/:<>?\\|"
+
+
+//
+// Checks if the token contains all valid characters
+//
+#define IS_VALID_TOKEN(_Str, _StrLen) \
+ ((BOOL) (wcscspn((_Str), STANDARD_ILLEGAL_CHARS) == (_StrLen)))
+
+//
+// Checks if the server name contains all valid characters for the server name
+//
+#define IS_VALID_SERVER_TOKEN(_Str, _StrLen) \
+ ((BOOL) (wcscspn((_Str), SERVER_ILLEGAL_CHARS) == (_StrLen)))
diff --git a/private/nw/install/dirs b/private/nw/install/dirs
new file mode 100644
index 000000000..b77c0323f
--- /dev/null
+++ b/private/nw/install/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=setupdll
+
+OPTIONAL_DIRS=
diff --git a/private/nw/install/setupdll/common.h b/private/nw/install/setupdll/common.h
new file mode 100644
index 000000000..c04595aa3
--- /dev/null
+++ b/private/nw/install/setupdll/common.h
@@ -0,0 +1,122 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+common.h
+
+ constants and globals that are common to LODCTR and UNLODCTR
+
+Author:
+
+ Bob Watson (a-robw) 10 Feb 93
+
+Revision History:
+
+--*/
+#ifndef _LODCTR_COMMON_H_
+#define _LODCTR_COMMON_H_
+//
+// Local constants
+//
+#define RESERVED 0L
+#define LARGE_BUFFER_SIZE 0x10000 // 64K
+#define MEDIUM_BUFFER_SIZE 0x8000 // 32K
+#define SMALL_BUFFER_SIZE 0x1000 // 4K
+#define FILE_NAME_BUFFER_SIZE MAX_PATH
+#define DISP_BUFF_SIZE 256L
+#define SIZE_OF_OFFSET_STRING 15
+//
+// Data structure and type definitions
+//
+typedef struct _NAME_ENTRY {
+ struct _NAME_ENTRY *pNext;
+ DWORD dwOffset;
+ DWORD dwType;
+ LPTSTR lpText;
+} NAME_ENTRY, *PNAME_ENTRY;
+
+typedef struct _LANGUAGE_LIST_ELEMENT {
+ struct _LANGUAGE_LIST_ELEMENT *pNextLang; // next lang. list
+ LPTSTR LangId; // lang ID string for this elem
+ PNAME_ENTRY pFirstName; // head of name list
+ PNAME_ENTRY pThisName; // pointer to current entry
+ DWORD dwNumElements; // number of elements in array
+ DWORD dwNameBuffSize;
+ DWORD dwHelpBuffSize;
+ PBYTE NameBuffer; // buffer to store strings
+ PBYTE HelpBuffer; // buffer to store help strings
+} LANGUAGE_LIST_ELEMENT, *PLANGUAGE_LIST_ELEMENT;
+
+typedef struct _SYMBOL_TABLE_ENTRY {
+ struct _SYMBOL_TABLE_ENTRY *pNext;
+ LPTSTR SymbolName;
+ DWORD Value;
+} SYMBOL_TABLE_ENTRY, *PSYMBOL_TABLE_ENTRY;
+//
+// Utility Routine prototypes for routines in common.c
+//
+#define StringToInt(in,out) \
+ (((_stscanf ((in), TEXT(" %d"), (out))) == 1) ? TRUE : FALSE)
+
+
+#if _INITIALIZE_GLOBALS_
+//
+//
+// Text string Constant definitions
+//
+const LPTSTR NamesKey = TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib");
+const LPTSTR DefaultLangId = TEXT("009");
+const LPTSTR Counters = TEXT("Counters");
+const LPTSTR Help = TEXT("Help");
+const LPTSTR VersionStr = TEXT("Version");
+const LPTSTR LastHelp = TEXT("Last Help");
+const LPTSTR LastCounter = TEXT("Last Counter");
+const LPTSTR FirstHelp = TEXT("First Help");
+const LPTSTR FirstCounter = TEXT("First Counter");
+const LPTSTR Busy = TEXT("Updating");
+const LPTSTR Slash = TEXT("\\");
+const LPTSTR BlankString = TEXT(" ");
+const LPSTR BlankAnsiString = " ";
+const LPTSTR DriverPathRoot = TEXT("SYSTEM\\CurrentControlSet\\Services");
+const LPTSTR Performance = TEXT("Performance");
+const LPTSTR CounterNameStr = TEXT("Counter ");
+const LPTSTR HelpNameStr = TEXT("Explain ");
+const LPTSTR AddCounterNameStr = TEXT("Addcounter ");
+const LPTSTR AddHelpNameStr = TEXT("Addexplain ");
+
+//
+// Global Buffers
+//
+TCHAR DisplayStringBuffer[DISP_BUFF_SIZE];
+CHAR TextFormat[DISP_BUFF_SIZE];
+HANDLE hMod = NULL; // process handle
+DWORD dwLastError = ERROR_SUCCESS;
+
+#else // just declare the globals
+
+extern const LPTSTR NamesKey;
+extern const LPTSTR VersionStr;
+extern const LPTSTR DefaultLangId;
+extern const LPTSTR Counters;
+extern const LPTSTR Help;
+extern const LPTSTR LastHelp;
+extern const LPTSTR LastCounter;
+extern const LPTSTR FirstHelp;
+extern const LPTSTR FirstCounter;
+extern const LPTSTR Busy;
+extern const LPTSTR Slash;
+extern const LPTSTR BlankString;
+extern const LPSTR BlankAnsiString;
+extern const LPTSTR DriverPathRoot;
+extern const LPTSTR Performance;
+//
+// Global Buffers
+//
+extern TCHAR DisplayStringBuffer[DISP_BUFF_SIZE];
+extern CHAR TextFormat[DISP_BUFF_SIZE];
+extern HANDLE hMod;
+extern DWORD dwLastError;
+
+#endif // _INITIALIZE_GLOBALS_
+
+#endif // _LODCTR_COMMON_H_
diff --git a/private/nw/install/setupdll/dllinit.c b/private/nw/install/setupdll/dllinit.c
new file mode 100644
index 000000000..ea326b220
--- /dev/null
+++ b/private/nw/install/setupdll/dllinit.c
@@ -0,0 +1,65 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ dllinit.c
+
+Abstract:
+
+ This module contians the DLL attach/detach event entry point for
+ a Setup support DLL.
+
+Author:
+
+ Ted Miller (tedm) July-1990
+
+Revision History:
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+HINSTANCE ThisDLLHandle;
+
+BOOL
+DLLInit(
+ IN HINSTANCE DLLHandle,
+ IN DWORD Reason,
+ IN LPVOID ReservedAndUnused
+ )
+{
+ ReservedAndUnused;
+
+ switch(Reason) {
+
+ case DLL_PROCESS_ATTACH:
+
+ ThisDLLHandle = DLLHandle;
+ break;
+
+ case DLL_PROCESS_DETACH:
+
+ // Delete all automatically established connections
+ // See UNC handling in netcon.c.
+ //
+ // BUGBUG: This doesn't work, because the unload sequence
+ // is different for "lazy" load DLLs than for load-time DLLs.
+ // INFs must be responsible for calling DeleteAllConnections().
+ //
+ // DeleteAllConnectionsWorker() ;
+ //
+ break ;
+
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+
+ break;
+ }
+
+ return(TRUE);
+}
diff --git a/private/nw/install/setupdll/lodctr.c b/private/nw/install/setupdll/lodctr.c
new file mode 100644
index 000000000..bfe95538d
--- /dev/null
+++ b/private/nw/install/setupdll/lodctr.c
@@ -0,0 +1,1531 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ lodctr.c
+
+Abstract:
+
+ Program to read the contents of the file specified in the command line
+ and update the registry accordingly
+
+Author:
+
+ Bob Watson (a-robw) 10 Feb 93
+
+Revision History:
+
+ a-robw 25-Feb-93 revised calls to make it compile as a UNICODE or
+ an ANSI app.
+
+--*/
+#define UNICODE 1
+#define _UNICODE 1
+//
+// "C" Include files
+//
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <malloc.h>
+//
+// Windows Include files
+//
+#include <windows.h>
+#include <winperf.h>
+#include <tchar.h>
+//
+#define _INITIALIZE_GLOBALS_ 1
+#include "common.h"
+#undef _INITIALIZE_GLOBALS_
+
+#define TYPE_HELP 1
+#define TYPE_NAME 2
+
+#include "nwcfg.hxx"
+
+#define OLD_VERSION 0x010000
+DWORD dwSystemVersion;
+
+
+BOOL
+GetDriverName (
+ IN LPTSTR lpIniFile,
+ OUT LPTSTR *lpDevName
+)
+/*++
+GetDriverName
+
+ looks up driver name in the .ini file and returns it in lpDevName
+
+Arguments
+
+ lpIniFile
+
+ Filename of ini file
+
+ lpDevName
+
+ pointer to pointer to reciev buffer w/dev name in it
+
+Return Value
+
+ TRUE if found
+ FALSE if not found in .ini file
+
+--*/
+{
+ DWORD dwRetSize;
+
+ if (lpDevName) {
+ dwRetSize = GetPrivateProfileString (
+ TEXT("info"), // info section
+ TEXT("drivername"), // driver name value
+ TEXT("drivernameNotFound"), // default value
+ *lpDevName,
+ DISP_BUFF_SIZE,
+ lpIniFile);
+
+ if ((lstrcmpi(*lpDevName, TEXT("drivernameNotFound"))) != 0) {
+ // name found
+ return TRUE;
+ } else {
+ // name not found, default returned so return NULL string
+ *lpDevName = TEXT("\0");
+ return FALSE;
+ }
+ } else {
+ SetLastError (ERROR_OUTOFMEMORY);
+ return FALSE;
+ }
+}
+
+BOOL
+BuildLanguageTables (
+ IN LPTSTR lpIniFile,
+ IN OUT PLANGUAGE_LIST_ELEMENT pFirstElem
+)
+/*++
+
+BuildLanguageTables
+
+ Creates a list of structures that will hold the text for
+ each supported language
+
+Arguments
+
+ lpIniFile
+
+ Filename with data
+
+ pFirstElem
+
+ pointer to first list entry
+
+ReturnValue
+
+ TRUE if all OK
+ FALSE if not
+
+--*/
+{
+
+ LPTSTR lpEnumeratedLangs;
+ LPTSTR lpThisLang;
+
+ PLANGUAGE_LIST_ELEMENT pThisElem;
+
+ DWORD dwSize;
+
+ lpEnumeratedLangs = malloc(SMALL_BUFFER_SIZE);
+
+ if (!lpEnumeratedLangs) {
+ SetLastError (ERROR_OUTOFMEMORY);
+ return FALSE;
+ }
+
+ dwSize = GetPrivateProfileString (
+ TEXT("languages"),
+ NULL, // return all values in multi-sz string
+ TEXT("009"), // english as the default
+ lpEnumeratedLangs,
+ SMALL_BUFFER_SIZE,
+ lpIniFile);
+
+ // do first language
+
+ lpThisLang = lpEnumeratedLangs;
+ pThisElem = pFirstElem;
+
+ while (*lpThisLang) {
+ pThisElem->pNextLang = NULL;
+ pThisElem->LangId = (LPTSTR) malloc ((lstrlen(lpThisLang) + 1) * sizeof(TCHAR));
+ lstrcpy (pThisElem->LangId, lpThisLang);
+ pThisElem->pFirstName = NULL;
+ pThisElem->pThisName = NULL;
+ pThisElem->dwNumElements=0;
+ pThisElem->NameBuffer = NULL;
+ pThisElem->HelpBuffer = NULL;
+
+ // go to next string
+
+ lpThisLang += lstrlen(lpThisLang) + 1;
+
+ if (*lpThisLang) { // there's another so allocate a new element
+ pThisElem->pNextLang = malloc (sizeof(LANGUAGE_LIST_ELEMENT));
+ if (!pThisElem) {
+ SetLastError (ERROR_OUTOFMEMORY);
+ return FALSE;
+ }
+ pThisElem = pThisElem->pNextLang; // point to new one
+ }
+ }
+
+ return TRUE;
+}
+
+BOOL
+LoadIncludeFile (
+ IN LPTSTR lpIniFile,
+ OUT PSYMBOL_TABLE_ENTRY *pTable
+)
+/*++
+
+LoadIncludeFile
+
+ Reads the include file that contains symbolic name definitions and
+ loads a table with the values defined
+
+Arguments
+
+ lpIniFile
+
+ Ini file with include file name
+
+ pTable
+
+ address of pointer to table structure created
+Return Value
+
+ TRUE if table read or if no table defined
+ FALSE if error encountere reading table
+
+--*/
+{
+ INT iNumArgs;
+
+ DWORD dwSize;
+
+ BOOL bReUse;
+
+ PSYMBOL_TABLE_ENTRY pThisSymbol;
+
+ LPTSTR lpIncludeFileName;
+ LPSTR lpIncludeFile;
+ LPSTR lpLineBuffer;
+ LPSTR lpAnsiSymbol;
+
+ FILE *fIncludeFile;
+ HFILE hIncludeFile;
+ OFSTRUCT ofIncludeFile;
+
+ lpIncludeFileName = malloc (MAX_PATH * sizeof (TCHAR));
+ lpIncludeFile = malloc (MAX_PATH);
+ lpLineBuffer = malloc (DISP_BUFF_SIZE);
+ lpAnsiSymbol = malloc (DISP_BUFF_SIZE);
+
+ if (!lpIncludeFileName || !lpLineBuffer || !lpAnsiSymbol) {
+ SetLastError (ERROR_OUTOFMEMORY);
+ return FALSE;
+ }
+
+ // get name of include file (if present)
+
+ dwSize = GetPrivateProfileString (
+ TEXT("info"),
+ TEXT("symbolfile"),
+ TEXT("SymbolFileNotFound"),
+ lpIncludeFileName,
+ _msize(lpIncludeFileName),
+ lpIniFile);
+
+ if ((lstrcmpi(lpIncludeFileName, TEXT("SymbolFileNotFound"))) == 0) {
+ // no symbol file defined
+ *pTable = NULL;
+ return TRUE;
+ }
+
+ // if here, then a symbol file was defined and is now stored in
+ // lpIncludeFileName
+
+ CharToOem (lpIncludeFileName, lpIncludeFile);
+
+ hIncludeFile = OpenFile (
+ lpIncludeFile,
+ &ofIncludeFile,
+ OF_PARSE);
+
+ if (hIncludeFile == HFILE_ERROR) { // unable to read include filename
+ // error is already in GetLastError
+ *pTable = NULL;
+ return FALSE;
+ } else {
+ // open a stream
+ fIncludeFile = fopen (ofIncludeFile.szPathName, "rt");
+
+ if (!fIncludeFile) {
+ *pTable = NULL;
+ return FALSE;
+ }
+ }
+
+ //
+ // read ANSI Characters from include file
+ //
+
+ bReUse = FALSE;
+
+ while (fgets(lpLineBuffer, DISP_BUFF_SIZE, fIncludeFile) != NULL) {
+ if (strlen(lpLineBuffer) > 8) {
+ if (!bReUse) {
+ if (*pTable) {
+ // then add to list
+ pThisSymbol->pNext = malloc (sizeof (SYMBOL_TABLE_ENTRY));
+ pThisSymbol = pThisSymbol->pNext;
+ } else { // allocate first element
+ *pTable = malloc (sizeof (SYMBOL_TABLE_ENTRY));
+ pThisSymbol = *pTable;
+ }
+
+ if (!pThisSymbol) {
+ SetLastError (ERROR_OUTOFMEMORY);
+ return FALSE;
+ }
+
+ // allocate room for the symbol name by using the line length
+ // - the size of "#define "
+
+// pThisSymbol->SymbolName = malloc ((strlen(lpLineBuffer) - 8) * sizeof (TCHAR));
+ pThisSymbol->SymbolName = malloc (DISP_BUFF_SIZE * sizeof (TCHAR));
+
+ if (!pThisSymbol->SymbolName) {
+ SetLastError (ERROR_OUTOFMEMORY);
+ return FALSE;
+ }
+
+ }
+
+ // all the memory is allocated so load the fields
+
+ pThisSymbol->pNext = NULL;
+
+ iNumArgs = sscanf (lpLineBuffer, "#define %s %d",
+ lpAnsiSymbol, &pThisSymbol->Value);
+
+ if (iNumArgs != 2) {
+ *(pThisSymbol->SymbolName) = TEXT('\0');
+ pThisSymbol->Value = (DWORD)-1L;
+ bReUse = TRUE;
+ } else {
+ OemToChar (lpAnsiSymbol, pThisSymbol->SymbolName);
+ bReUse = FALSE;
+ }
+ }
+ }
+
+ if (lpIncludeFileName) free (lpIncludeFileName);
+ if (lpIncludeFile) free (lpIncludeFile);
+ if (lpLineBuffer) free (lpLineBuffer);
+
+ fclose (fIncludeFile);
+
+ return TRUE;
+
+}
+
+BOOL
+ParseTextId (
+ IN LPTSTR lpTextId,
+ IN PSYMBOL_TABLE_ENTRY pFirstSymbol,
+ OUT PDWORD pdwOffset,
+ OUT LPTSTR *lpLangId,
+ OUT PDWORD pdwType
+)
+/*++
+
+ParseTextId
+
+ decodes Text Id key from .INI file
+
+ syntax for this process is:
+
+ {<DecimalNumber>} {"NAME"}
+ {<SymbolInTable>}_<LangIdString>_{"HELP"}
+
+ e.g. 0_009_NAME
+ OBJECT_1_009_HELP
+
+Arguments
+
+ lpTextId
+
+ string to decode
+
+ pFirstSymbol
+
+ pointer to first entry in symbol table (NULL if no table)
+
+ pdwOffset
+
+ address of DWORD to recive offest value
+
+ lpLangId
+
+ address of pointer to Language Id string
+ (NOTE: this will point into the string lpTextID which will be
+ modified by this routine)
+
+ pdwType
+
+ pointer to dword that will recieve the type of string i.e.
+ HELP or NAME
+
+Return Value
+
+ TRUE text Id decoded successfully
+ FALSE unable to decode string
+
+ NOTE: the string in lpTextID will be modified by this procedure
+
+--*/
+{
+ LPTSTR lpThisChar;
+ PSYMBOL_TABLE_ENTRY pThisSymbol;
+
+ // check for valid return arguments
+
+ if (!(pdwOffset) ||
+ !(lpLangId) ||
+ !(pdwType)) {
+ SetLastError (ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ // search string from right to left in order to identify the
+ // components of the string.
+
+ lpThisChar = lpTextId + lstrlen(lpTextId); // point to end of string
+
+ while (*lpThisChar != TEXT('_')) {
+ lpThisChar--;
+ if (lpThisChar <= lpTextId) {
+ // underscore not found in string
+ SetLastError (ERROR_INVALID_DATA);
+ return FALSE;
+ }
+ }
+
+ // first underscore found
+
+ if ((lstrcmpi(lpThisChar, TEXT("_NAME"))) == 0) {
+ // name found, so set type
+ *pdwType = TYPE_NAME;
+ } else if ((lstrcmpi(lpThisChar, TEXT("_HELP"))) == 0) {
+ // help text found, so set type
+ *pdwType = TYPE_HELP;
+ } else {
+ // bad format
+ SetLastError (ERROR_INVALID_DATA);
+ return FALSE;
+ }
+
+ // set the current underscore to \0 and look for language ID
+
+ *lpThisChar-- = TEXT('\0');
+
+ while (*lpThisChar != TEXT('_')) {
+ lpThisChar--;
+ if (lpThisChar <= lpTextId) {
+ // underscore not found in string
+ SetLastError (ERROR_INVALID_DATA);
+ return FALSE;
+ }
+ }
+
+ // set lang ID string pointer to current char ('_') + 1
+
+ *lpLangId = lpThisChar + 1;
+
+ // set this underscore to a NULL and try to decode the remaining text
+
+ *lpThisChar = TEXT('\0');
+
+ // see if the first part of the string is a decimal digit
+
+ if ((_stscanf (lpTextId, TEXT(" %d"), pdwOffset)) != 1) {
+ // it's not a digit, so try to decode it as a symbol in the
+ // loaded symbol table
+
+ for (pThisSymbol=pFirstSymbol;
+ pThisSymbol && *(pThisSymbol->SymbolName);
+ pThisSymbol = pThisSymbol->pNext) {
+
+ if ((lstrcmpi(lpTextId, pThisSymbol->SymbolName)) == 0) {
+ // a matching symbol was found, so insert it's value
+ // and return (that's all that needs to be done
+ *pdwOffset = pThisSymbol->Value;
+ return TRUE;
+ }
+ }
+ // if here, then no matching symbol was found, and it's not
+ // a number, so return an error
+
+ SetLastError (ERROR_BAD_TOKEN_TYPE);
+ return FALSE;
+ } else {
+ // symbol was prefixed with a decimal number
+ return TRUE;
+ }
+}
+
+PLANGUAGE_LIST_ELEMENT
+FindLanguage (
+ IN PLANGUAGE_LIST_ELEMENT pFirstLang,
+ IN LPTSTR pLangId
+)
+/*++
+
+FindLanguage
+
+ searchs the list of languages and returns a pointer to the language
+ list entry that matches the pLangId string argument
+
+Arguments
+
+ pFirstLang
+
+ pointer to first language list element
+
+ pLangId
+
+ pointer to text string with language ID to look up
+
+Return Value
+
+ Pointer to matching language list entry
+ or NULL if no match
+
+--*/
+{
+ PLANGUAGE_LIST_ELEMENT pThisLang;
+
+ for (pThisLang = pFirstLang;
+ pThisLang;
+ pThisLang = pThisLang->pNextLang) {
+ if ((lstrcmpi(pLangId, pThisLang->LangId)) == 0) {
+ // match found so return pointer
+ return pThisLang;
+ }
+ }
+ return NULL; // no match found
+}
+
+BOOL
+AddEntryToLanguage (
+ PLANGUAGE_LIST_ELEMENT pLang,
+ LPTSTR lpValueKey,
+ DWORD dwType,
+ DWORD dwOffset,
+ LPTSTR lpIniFile
+)
+/*++
+
+AddEntryToLanguage
+
+ Add a text entry to the list of text entries for the specified language
+
+Arguments
+
+ pLang
+
+ pointer to language structure to update
+
+ lpValueKey
+
+ value key to look up in .ini file
+
+ dwOffset
+
+ numeric offset of name in registry
+
+ lpIniFile
+
+ ini file
+
+Return Value
+
+ TRUE if added successfully
+ FALSE if error
+ (see GetLastError for status)
+
+--*/
+{
+ LPTSTR lpLocalStringBuff;
+ DWORD dwSize;
+
+ lpLocalStringBuff = malloc (SMALL_BUFFER_SIZE);
+
+ if (!lpLocalStringBuff) {
+ SetLastError (ERROR_OUTOFMEMORY);
+ return FALSE;
+ }
+
+ dwSize = GetPrivateProfileString (
+ TEXT("text"), // section
+ lpValueKey, // key
+ TEXT("DefaultValue"), // default value
+ lpLocalStringBuff,
+ SMALL_BUFFER_SIZE,
+ lpIniFile);
+
+ if ((lstrcmpi(lpLocalStringBuff, TEXT("DefaultValue")))== 0) {
+ SetLastError (ERROR_BADKEY);
+ if (lpLocalStringBuff) free (lpLocalStringBuff);
+ return FALSE;
+ }
+
+ // key found, so load structure
+
+ if (!pLang->pThisName) {
+ // this is the first
+ pLang->pThisName =
+ malloc (sizeof (NAME_ENTRY) +
+ (lstrlen(lpLocalStringBuff) + 1) * sizeof (TCHAR));
+ if (!pLang->pThisName) {
+ SetLastError (ERROR_OUTOFMEMORY);
+ if (lpLocalStringBuff) free (lpLocalStringBuff);
+ return FALSE;
+ } else {
+ pLang->pFirstName = pLang->pThisName;
+ }
+ } else {
+ pLang->pThisName->pNext =
+ malloc (sizeof (NAME_ENTRY) +
+ (lstrlen(lpLocalStringBuff) + 1) * sizeof (TCHAR));
+ if (!pLang->pThisName->pNext) {
+ SetLastError (ERROR_OUTOFMEMORY);
+ if (lpLocalStringBuff) free (lpLocalStringBuff);
+ return FALSE;
+ } else {
+ pLang->pThisName = pLang->pThisName->pNext;
+ }
+ }
+
+ // pLang->pThisName now points to an uninitialized structre
+
+ pLang->pThisName->pNext = NULL;
+ pLang->pThisName->dwOffset = dwOffset;
+ pLang->pThisName->dwType = dwType;
+ pLang->pThisName->lpText = (LPTSTR)&(pLang->pThisName[1]); // string follows
+
+ lstrcpy (pLang->pThisName->lpText, lpLocalStringBuff);
+
+ if (lpLocalStringBuff) free (lpLocalStringBuff);
+
+ SetLastError (ERROR_SUCCESS);
+
+ return (TRUE);
+}
+
+BOOL
+LoadLanguageLists (
+ IN LPTSTR lpIniFile,
+ IN DWORD dwFirstCounter,
+ IN DWORD dwFirstHelp,
+ IN PSYMBOL_TABLE_ENTRY pFirstSymbol,
+ IN PLANGUAGE_LIST_ELEMENT pFirstLang
+)
+/*++
+
+LoadLanguageLists
+
+ Reads in the name and explain text definitions from the ini file and
+ builds a list of these items for each of the supported languages and
+ then combines all the entries into a sorted MULTI_SZ string buffer.
+
+Arguments
+
+ lpIniFile
+
+ file containing the definitions to add to the registry
+
+ dwFirstCounter
+
+ starting counter name index number
+
+ dwFirstHelp
+
+ starting help text index number
+
+ pFirstLang
+
+ pointer to first element in list of language elements
+
+Return Value
+
+ TRUE if all is well
+ FALSE if not
+ error is returned in GetLastError
+
+--*/
+{
+ LPTSTR lpTextIdArray;
+ LPTSTR lpLocalKey;
+ LPTSTR lpThisKey;
+ DWORD dwSize;
+ LPTSTR lpLang;
+ DWORD dwOffset;
+ DWORD dwType;
+ PLANGUAGE_LIST_ELEMENT pThisLang;
+
+ if (!(lpTextIdArray = malloc (SMALL_BUFFER_SIZE))) {
+ SetLastError (ERROR_OUTOFMEMORY);
+ return FALSE;
+ }
+
+ if (!(lpLocalKey = malloc (MAX_PATH))) {
+ SetLastError (ERROR_OUTOFMEMORY);
+ if (lpTextIdArray) free (lpTextIdArray);
+ return FALSE;
+ }
+
+ // get list of text keys to look up
+
+ dwSize = GetPrivateProfileString (
+ TEXT("text"), // [text] section of .INI file
+ NULL, // return all keys
+ TEXT("DefaultKeyValue"), // default
+ lpTextIdArray, // return buffer
+ SMALL_BUFFER_SIZE, // buffer size
+ lpIniFile); // .INI file name
+
+ if ((lstrcmpi(lpTextIdArray, TEXT("DefaultKeyValue"))) == 0) {
+ // key not found, default returned
+ SetLastError (ERROR_NO_SUCH_GROUP);
+ if (lpTextIdArray) free (lpTextIdArray);
+ if (lpLocalKey) free (lpLocalKey);
+ return FALSE;
+ }
+
+ // do each key returned
+
+ for (lpThisKey=lpTextIdArray;
+ *lpThisKey;
+ lpThisKey += (lstrlen(lpThisKey) + 1)) {
+
+ lstrcpy (lpLocalKey, lpThisKey); // make a copy of the key
+
+ // parse key to see if it's in the correct format
+
+ if (ParseTextId(lpLocalKey, pFirstSymbol, &dwOffset, &lpLang, &dwType)) {
+ // so get pointer to language entry structure
+ pThisLang = FindLanguage (pFirstLang, lpLang);
+ if (pThisLang) {
+ if (!AddEntryToLanguage(pThisLang,
+ lpThisKey, dwType,
+ (dwOffset + ((dwType == TYPE_NAME) ? dwFirstCounter : dwFirstHelp)),
+ lpIniFile)) {
+ }
+ } else { // language not in list
+ }
+ } else { // unable to parse ID string
+ }
+ }
+
+ if (lpTextIdArray) free (lpTextIdArray);
+ if (lpLocalKey) free (lpLocalKey);
+ return TRUE;
+
+}
+
+BOOL
+SortLanguageTables (
+ PLANGUAGE_LIST_ELEMENT pFirstLang,
+ PDWORD pdwLastName,
+ PDWORD pdwLastHelp
+)
+/*++
+
+SortLangageTables
+
+ walks list of languages loaded, allocates and loads a sorted multi_SZ
+ buffer containing new entries to be added to current names/help text
+
+Arguments
+
+ pFirstLang
+
+ pointer to first element in list of languages
+
+ReturnValue
+
+ TRUE everything done as expected
+ FALSE error occurred, status in GetLastError
+
+--*/
+{
+ PLANGUAGE_LIST_ELEMENT pThisLang;
+
+ BOOL bSorted;
+
+ LPTSTR pNameBufPos, pHelpBufPos;
+
+ PNAME_ENTRY pThisName, pPrevName;
+
+ DWORD dwHelpSize, dwNameSize, dwSize;
+
+ if (!pdwLastName || !pdwLastHelp) {
+ SetLastError (ERROR_BAD_ARGUMENTS);
+ return FALSE;
+ }
+
+ for (pThisLang = pFirstLang;
+ pThisLang;
+ pThisLang = pThisLang->pNextLang) {
+ // do each language in list
+
+ // sort elements in list by value (offset) so that lowest is first
+
+ bSorted = FALSE;
+ while (!bSorted ) {
+ // point to start of list
+
+ pPrevName = pThisLang->pFirstName;
+ if (pPrevName) {
+ pThisName = pPrevName->pNext;
+ } else {
+ break; // no elements in this list
+ }
+
+ if (!pThisName) {
+ break; // only one element in the list
+ }
+ bSorted = TRUE; // assume that it's sorted
+
+ // go until end of list
+
+ while (pThisName->pNext) {
+ if (pThisName->dwOffset > pThisName->pNext->dwOffset) {
+ // switch 'em
+ pPrevName->pNext = pThisName->pNext;
+ pThisName->pNext = pThisName->pNext->pNext;
+ pThisName->pNext->pNext = pThisName;
+ bSorted = FALSE;
+ }
+ //move to next entry
+ pPrevName = pThisName;
+ pThisName = pThisName->pNext;
+ }
+ // if bSorted = TRUE , then we walked all the way down
+ // the list without changing anything so that's the end.
+ }
+
+ // with the list sorted, build the MULTI_SZ strings for the
+ // help and name text strings
+
+ // compute buffer size
+
+ dwNameSize = dwHelpSize = 0;
+ *pdwLastName = *pdwLastHelp = 0;
+
+ for (pThisName = pThisLang->pFirstName;
+ pThisName;
+ pThisName = pThisName->pNext) {
+ // compute buffer requirements for this entry
+ dwSize = SIZE_OF_OFFSET_STRING;
+ dwSize += lstrlen (pThisName->lpText);
+ dwSize += 1; // null
+ dwSize *= sizeof (TCHAR); // adjust for character size
+ // add to appropriate size register
+ if (pThisName->dwType == TYPE_NAME) {
+ dwNameSize += dwSize;
+ if (pThisName->dwOffset > *pdwLastName) {
+ *pdwLastName = pThisName->dwOffset;
+ }
+ } else if (pThisName->dwType == TYPE_HELP) {
+ dwHelpSize += dwSize;
+ if (pThisName->dwOffset > *pdwLastHelp) {
+ *pdwLastHelp = pThisName->dwOffset;
+ }
+ }
+ }
+
+ // allocate buffers for the Multi_SZ strings
+
+ pThisLang->NameBuffer = malloc (dwNameSize);
+ pThisLang->HelpBuffer = malloc (dwHelpSize);
+
+ if (!pThisLang->NameBuffer || !pThisLang->HelpBuffer) {
+ SetLastError (ERROR_OUTOFMEMORY);
+ return FALSE;
+ }
+
+ // fill in buffers with sorted strings
+
+ pNameBufPos = (LPTSTR)pThisLang->NameBuffer;
+ pHelpBufPos = (LPTSTR)pThisLang->HelpBuffer;
+
+ for (pThisName = pThisLang->pFirstName;
+ pThisName;
+ pThisName = pThisName->pNext) {
+ if (pThisName->dwType == TYPE_NAME) {
+ // load number as first 0-term. string
+ dwSize = _stprintf (pNameBufPos, TEXT("%d"), pThisName->dwOffset);
+ pNameBufPos += dwSize + 1; // save NULL term.
+ // load the text to match
+ lstrcpy (pNameBufPos, pThisName->lpText);
+ pNameBufPos += lstrlen(pNameBufPos) + 1;
+ } else if (pThisName->dwType == TYPE_HELP) {
+ // load number as first 0-term. string
+ dwSize = _stprintf (pHelpBufPos, TEXT("%d"), pThisName->dwOffset);
+ pHelpBufPos += dwSize + 1; // save NULL term.
+ // load the text to match
+ lstrcpy (pHelpBufPos, pThisName->lpText);
+ pHelpBufPos += lstrlen(pHelpBufPos) + 1;
+ }
+ }
+
+ // add additional NULL at end of string to terminate MULTI_SZ
+
+ *pHelpBufPos = TEXT('\0');
+ *pNameBufPos = TEXT('\0');
+
+ // compute size of MULTI_SZ strings
+
+ pThisLang->dwNameBuffSize = (DWORD)((PBYTE)pNameBufPos -
+ (PBYTE)pThisLang->NameBuffer) +
+ sizeof(TCHAR);
+ pThisLang->dwHelpBuffSize = (DWORD)((PBYTE)pHelpBufPos -
+ (PBYTE)pThisLang->HelpBuffer) +
+ sizeof(TCHAR);
+ }
+ return TRUE;
+}
+
+BOOL
+UpdateEachLanguage (
+ HKEY hPerflibRoot,
+ PLANGUAGE_LIST_ELEMENT pFirstLang
+)
+/*++
+
+UpdateEachLanguage
+
+ Goes through list of languages and adds the sorted MULTI_SZ strings
+ to the existing counter and explain text in the registry.
+ Also updates the "Last Counter and Last Help" values
+
+Arguments
+
+ hPerflibRoot
+
+ handle to Perflib key in the registry
+
+ pFirstLanguage
+
+ pointer to first language entry
+
+Return Value
+
+ TRUE all went as planned
+ FALSE an error occured, use GetLastError to find out what it was.
+
+--*/
+{
+
+ PLANGUAGE_LIST_ELEMENT pThisLang;
+
+ LPTSTR pHelpBuffer;
+ LPTSTR pNameBuffer;
+ LPTSTR pNewName;
+ LPTSTR pNewHelp;
+
+ DWORD dwBufferSize;
+ DWORD dwValueType;
+ DWORD dwCounterSize;
+ DWORD dwHelpSize;
+
+ HKEY hKeyThisLang;
+
+ LONG lStatus;
+
+ for (pThisLang = pFirstLang;
+ pThisLang;
+ pThisLang = pThisLang->pNextLang) {
+
+ lStatus = RegOpenKeyEx(
+ hPerflibRoot,
+ pThisLang->LangId,
+ RESERVED,
+ KEY_READ | KEY_WRITE,
+ &hKeyThisLang);
+
+ if (lStatus == ERROR_SUCCESS) {
+
+ // get size of counter names
+
+ dwBufferSize = 0;
+ lStatus = RegQueryValueEx (
+ hKeyThisLang,
+ Counters,
+ RESERVED,
+ &dwValueType,
+ NULL,
+ &dwBufferSize);
+
+ if (lStatus != ERROR_SUCCESS) {
+ SetLastError (lStatus);
+ return FALSE;
+ }
+
+ dwCounterSize = dwBufferSize;
+
+ // get size of help text
+
+ dwBufferSize = 0;
+ lStatus = RegQueryValueEx (
+ hKeyThisLang,
+ Help,
+ RESERVED,
+ &dwValueType,
+ NULL,
+ &dwBufferSize);
+
+ if (lStatus != ERROR_SUCCESS) {
+ SetLastError (lStatus);
+ return FALSE;
+ }
+
+ dwHelpSize = dwBufferSize;
+
+ // allocate new buffers
+
+ dwCounterSize += pThisLang->dwNameBuffSize;
+ pNameBuffer = malloc (dwCounterSize);
+
+ dwHelpSize += pThisLang->dwHelpBuffSize;
+ pHelpBuffer = malloc (dwHelpSize);
+
+ if (!pNameBuffer || !pHelpBuffer) {
+ SetLastError (ERROR_OUTOFMEMORY);
+ return (FALSE);
+ }
+
+ // load current buffers into memory
+
+ // read counter names into buffer. Counter names will be stored as
+ // a MULTI_SZ string in the format of "###" "Name"
+
+ dwBufferSize = dwCounterSize;
+ lStatus = RegQueryValueEx (
+ hKeyThisLang,
+ Counters,
+ RESERVED,
+ &dwValueType,
+ (LPVOID)pNameBuffer,
+ &dwBufferSize);
+
+ if (lStatus != ERROR_SUCCESS) {
+ SetLastError (lStatus);
+ return FALSE;
+ }
+
+ // set pointer to location in buffer where new string should be
+ // appended: end of buffer - 1 (second null at end of MULTI_SZ
+
+ pNewName = (LPTSTR)((PBYTE)pNameBuffer + dwBufferSize - sizeof(TCHAR));
+
+ // adjust buffer length to take into account 2nd null from 1st
+ // buffer that has been overwritten
+
+ dwCounterSize -= sizeof(TCHAR);
+
+ // read explain text into buffer. Counter names will be stored as
+ // a MULTI_SZ string in the format of "###" "Text..."
+
+ dwBufferSize = dwHelpSize;
+ lStatus = RegQueryValueEx (
+ hKeyThisLang,
+ Help,
+ RESERVED,
+ &dwValueType,
+ (LPVOID)pHelpBuffer,
+ &dwBufferSize);
+
+ if (lStatus != ERROR_SUCCESS) {
+ SetLastError (lStatus);
+ return FALSE;
+ }
+
+ // set pointer to location in buffer where new string should be
+ // appended: end of buffer - 1 (second null at end of MULTI_SZ
+
+ pNewHelp = (LPTSTR)((PBYTE)pHelpBuffer + dwBufferSize - sizeof(TCHAR));
+
+ // adjust buffer length to take into account 2nd null from 1st
+ // buffer that has been overwritten
+
+ dwHelpSize -= sizeof(TCHAR);
+
+ // append new strings to end of current strings
+
+ memcpy (pNewHelp, pThisLang->HelpBuffer, pThisLang->dwHelpBuffSize);
+ memcpy (pNewName, pThisLang->NameBuffer, pThisLang->dwNameBuffSize);
+
+ lStatus = RegSetValueEx (
+ hKeyThisLang,
+ Counters,
+ RESERVED,
+ REG_MULTI_SZ,
+ (LPBYTE)pNameBuffer,
+ dwCounterSize);
+
+ if (lStatus != ERROR_SUCCESS) {
+ SetLastError (lStatus);
+ return FALSE;
+ }
+
+ lStatus = RegSetValueEx (
+ hKeyThisLang,
+ Help,
+ RESERVED,
+ REG_MULTI_SZ,
+ (LPBYTE)pHelpBuffer,
+ dwHelpSize);
+
+ if (lStatus != ERROR_SUCCESS) {
+ SetLastError (lStatus);
+ return FALSE;
+ }
+ free (pNameBuffer);
+ free (pHelpBuffer);
+ CloseHandle (hKeyThisLang);
+ } else {
+ }
+ }
+
+ return TRUE;
+}
+
+BOOL
+UpdateRegistry (
+ LPTSTR lpIniFile,
+ HKEY hKeyMachine,
+ LPTSTR lpDriverName,
+ PLANGUAGE_LIST_ELEMENT pFirstLang,
+ PSYMBOL_TABLE_ENTRY pFirstSymbol
+)
+/*++
+
+UpdateRegistry
+
+ - checks, and if not busy, sets the "busy" key in the registry
+ - Reads in the text and help definitions from the .ini file
+ - Reads in the current contents of the HELP and COUNTER names
+ - Builds a sorted MULTI_SZ struct containing the new definitions
+ - Appends the new MULTI_SZ to the current as read from the registry
+ - loads the new MULTI_SZ string into the registry
+ - updates the keys in the driver's entry and Perflib's entry in the
+ registry (e.g. first, last, etc)
+ - clears the "busy" key
+
+Arguments
+
+ lpIniFile
+ pathname to .ini file conatining definitions
+
+ hKeyMachine
+ handle to HKEY_LOCAL_MACHINE in registry on system to
+ update counters for.
+
+ lpDriverName
+ Name of device driver to load counters for
+
+ pFirstLang
+ pointer to first element in language structure list
+
+ pFirstSymbol
+ pointer to first element in symbol definition list
+
+
+Return Value
+
+ TRUE if registry updated successfully
+ FALSE if registry not updated
+ (This routine will print an error message to stdout if an error
+ is encountered).
+
+--*/
+{
+
+ HKEY hDriverPerf = NULL;
+ HKEY hPerflib = NULL;
+
+ LPTSTR lpDriverKeyPath;
+
+ DWORD dwType;
+ DWORD dwSize;
+
+ DWORD dwFirstDriverCounter;
+ DWORD dwFirstDriverHelp;
+ DWORD dwLastDriverCounter;
+ DWORD dwLastPerflibCounter;
+ DWORD dwLastPerflibHelp;
+
+ BOOL bStatus;
+ LONG lStatus;
+
+ bStatus = FALSE;
+
+ // allocate temporary buffers
+ lpDriverKeyPath = malloc (MAX_PATH * sizeof(TCHAR));
+
+ if (!lpDriverKeyPath) {
+ SetLastError (ERROR_OUTOFMEMORY);
+ goto UpdateRegExit;
+ }
+
+ // build driver key path string
+
+ lstrcpy (lpDriverKeyPath, DriverPathRoot);
+ lstrcat (lpDriverKeyPath, Slash);
+ lstrcat (lpDriverKeyPath, lpDriverName);
+ lstrcat (lpDriverKeyPath, Slash);
+ lstrcat (lpDriverKeyPath, Performance);
+
+ // open keys to registry
+ // open key to driver's performance key
+
+ lStatus = RegOpenKeyEx (
+ hKeyMachine,
+ lpDriverKeyPath,
+ RESERVED,
+ KEY_WRITE | KEY_READ,
+ &hDriverPerf);
+
+ if (lStatus != ERROR_SUCCESS) {
+ SetLastError (lStatus);
+ goto UpdateRegExit;
+ }
+
+ // open key to perflib's "root" key
+
+ lStatus = RegOpenKeyEx (
+ hKeyMachine,
+ NamesKey,
+ RESERVED,
+ KEY_WRITE | KEY_READ,
+ &hPerflib);
+
+ if (lStatus != ERROR_SUCCESS) {
+ SetLastError (lStatus);
+ goto UpdateRegExit;
+ }
+
+ // get "last" values from PERFLIB
+
+ dwType = 0;
+ dwLastPerflibCounter = 0;
+ dwSize = sizeof (dwLastPerflibCounter);
+ lStatus = RegQueryValueEx (
+ hPerflib,
+ LastCounter,
+ RESERVED,
+ &dwType,
+ (LPBYTE)&dwLastPerflibCounter,
+ &dwSize);
+
+ if (lStatus != ERROR_SUCCESS) {
+ // this request should always succeed, if not then worse things
+ // will happen later on, so quit now and avoid the trouble.
+ SetLastError (lStatus);
+ goto UpdateRegExit;
+ }
+
+ // get last help value now
+
+ dwType = 0;
+ dwLastPerflibHelp = 0;
+ dwSize = sizeof (dwLastPerflibHelp);
+ lStatus = RegQueryValueEx (
+ hPerflib,
+ LastHelp,
+ RESERVED,
+ &dwType,
+ (LPBYTE)&dwLastPerflibHelp,
+ &dwSize);
+
+ if (lStatus != ERROR_SUCCESS) {
+ // this request should always succeed, if not then worse things
+ // will happen later on, so quit now and avoid the trouble.
+ SetLastError (lStatus);
+ goto UpdateRegExit;
+ }
+
+ // get last help value now
+
+ dwType = 0;
+ dwSize = sizeof (dwSystemVersion);
+ lStatus = RegQueryValueEx (
+ hPerflib,
+ VersionStr,
+ RESERVED,
+ &dwType,
+ (LPBYTE)&dwSystemVersion,
+ &dwSize);
+
+ if (lStatus != ERROR_SUCCESS) {
+ dwSystemVersion = OLD_VERSION;
+ }
+
+ if ( dwSystemVersion != OLD_VERSION )
+ {
+ // ERROR. The caller does not check the version. It is the caller
+ // fault
+ goto UpdateRegExit;
+ }
+
+ // see if this driver's counter names have already been installed
+ // by checking to see if LastCounter's value is less than Perflib's
+ // Last Counter
+
+ dwType = 0;
+ dwLastDriverCounter = 0;
+ dwSize = sizeof (dwLastDriverCounter);
+ lStatus = RegQueryValueEx (
+ hDriverPerf,
+ LastCounter,
+ RESERVED,
+ &dwType,
+ (LPBYTE)&dwLastDriverCounter,
+ &dwSize);
+
+ if (lStatus == ERROR_SUCCESS) {
+ // if key found, then compare with perflib value and exit this
+ // procedure if the driver's last counter is <= to perflib's last
+ //
+ // if key not found, then continue with installation
+ // on the assumption that the counters have not been installed
+
+ if (dwLastDriverCounter <= dwLastPerflibCounter) {
+ SetLastError (ERROR_SUCCESS);
+ goto UpdateRegExit;
+ }
+ }
+
+ // everything looks like it's ready to go so first check the
+ // busy indicator
+
+ lStatus = RegQueryValueEx (
+ hPerflib,
+ Busy,
+ RESERVED,
+ &dwType,
+ NULL,
+ &dwSize);
+
+ if (lStatus == ERROR_SUCCESS) { // perflib is in use at the moment
+ return ERROR_BUSY;
+ }
+
+ // set the "busy" indicator under the PERFLIB key
+
+ dwSize = lstrlen(lpDriverName) * sizeof (TCHAR);
+ lStatus = RegSetValueEx (
+ hPerflib,
+ Busy,
+ RESERVED,
+ REG_SZ,
+ (LPBYTE)lpDriverName,
+ dwSize);
+
+ if (lStatus != ERROR_SUCCESS) {
+ SetLastError (lStatus);
+ goto UpdateRegExit;
+ }
+
+ // increment (by 2) the last counters so they point to the first
+ // unused index after the existing names and then
+ // set the first driver counters
+
+ dwFirstDriverCounter = dwLastPerflibCounter += 2;
+ dwFirstDriverHelp = dwLastPerflibHelp += 2;
+
+ // load .INI file definitions into language tables
+
+ if (!LoadLanguageLists (lpIniFile, dwLastPerflibCounter, dwLastPerflibHelp,
+ pFirstSymbol, pFirstLang)) {
+ // error message is displayed by LoadLanguageLists so just abort
+ // error is in GetLastError already
+ goto UpdateRegExit;
+ }
+
+ // all the symbols and definitions have been loaded into internal
+ // tables. so now they need to be sorted and merged into a multiSZ string
+ // this routine also updates the "last" counters
+
+ if (!SortLanguageTables (pFirstLang, &dwLastPerflibCounter, &dwLastPerflibHelp)) {
+ goto UpdateRegExit;
+ }
+
+ if (!UpdateEachLanguage (hPerflib, pFirstLang)) {
+ goto UpdateRegExit;
+ }
+
+ // update last counters for driver and perflib
+
+ // perflib...
+
+ lStatus = RegSetValueEx(
+ hPerflib,
+ LastCounter,
+ RESERVED,
+ REG_DWORD,
+ (LPBYTE)&dwLastPerflibCounter,
+ sizeof(DWORD));
+
+ lStatus = RegSetValueEx(
+ hPerflib,
+ LastHelp,
+ RESERVED,
+ REG_DWORD,
+ (LPBYTE)&dwLastPerflibHelp,
+ sizeof(DWORD));
+
+ // and the driver
+
+ lStatus = RegSetValueEx(
+ hDriverPerf,
+ LastCounter,
+ RESERVED,
+ REG_DWORD,
+ (LPBYTE)&dwLastPerflibCounter,
+ sizeof(DWORD));
+
+ lStatus = RegSetValueEx(
+ hDriverPerf,
+ LastHelp,
+ RESERVED,
+ REG_DWORD,
+ (LPBYTE)&dwLastPerflibHelp,
+ sizeof(DWORD));
+
+ lStatus = RegSetValueEx(
+ hDriverPerf,
+ FirstCounter,
+ RESERVED,
+ REG_DWORD,
+ (LPBYTE)&dwFirstDriverCounter,
+ sizeof(DWORD));
+
+ lStatus = RegSetValueEx(
+ hDriverPerf,
+ FirstHelp,
+ RESERVED,
+ REG_DWORD,
+ (LPBYTE)&dwFirstDriverHelp,
+ sizeof(DWORD));
+
+ bStatus = TRUE;
+
+ // free temporary buffers
+UpdateRegExit:
+ // clear busy flag
+
+ lStatus = RegDeleteValue (
+ hPerflib,
+ Busy);
+
+ // free temporary buffers
+
+ if (lpDriverKeyPath) free (lpDriverKeyPath);
+ if (hDriverPerf) CloseHandle (hDriverPerf);
+ if (hPerflib) CloseHandle (hPerflib);
+
+ return bStatus;
+}
+
+BOOL FAR PASCAL lodctr(DWORD argc,LPSTR argv[], LPSTR *ppszResult )
+/*++
+
+main
+
+
+
+Arguments
+
+
+ReturnValue
+
+ 0 (ERROR_SUCCESS) if command was processed
+ Non-Zero if command error was detected.
+
+--*/
+{
+ LPTSTR lpIniFile;
+ LPTSTR lpDriverName;
+
+ LANGUAGE_LIST_ELEMENT LangList;
+ PSYMBOL_TABLE_ENTRY SymbolTable = NULL;
+ PSYMBOL_TABLE_ENTRY pThisSymbol = NULL;
+
+ BOOL fReturn = TRUE;
+
+ lpIniFile = malloc( MAX_PATH * sizeof(TCHAR));
+ lpDriverName = malloc (MAX_PATH * sizeof (TCHAR));
+
+ wsprintfA( achBuff, "{\"NO_ERROR\"}");
+
+ if ( argc == 1) {
+ MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, argv[0], -1, lpIniFile, MAX_PATH);
+
+ if (!GetDriverName (lpIniFile, &lpDriverName)) {
+ wsprintfA(achBuff,"{\"ERROR\"}");
+ fReturn = FALSE;
+ goto EndOfMain;
+ }
+
+ if (!BuildLanguageTables(lpIniFile, &LangList)) {
+ wsprintfA (achBuff, "{\"ERROR\"}");
+ fReturn = FALSE;
+ goto EndOfMain;
+ }
+
+ if (!LoadIncludeFile(lpIniFile, &SymbolTable)) {
+ // open errors displayed in routine
+ fReturn = FALSE;
+ goto EndOfMain;
+ }
+
+ if (!UpdateRegistry(lpIniFile,
+ HKEY_LOCAL_MACHINE,
+ lpDriverName,
+ &LangList,
+ SymbolTable)) {
+ wsprintfA (achBuff, "{\"ERROR\"}");
+ fReturn = FALSE;
+ }
+
+ }
+
+EndOfMain:
+
+ if (lpIniFile) free (lpDriverName);
+ if (lpDriverName) free (lpDriverName);
+
+ *ppszResult = achBuff;
+
+ return (fReturn); // success
+}
diff --git a/private/nw/install/setupdll/makefile b/private/nw/install/setupdll/makefile
new file mode 100644
index 000000000..7686b2955
--- /dev/null
+++ b/private/nw/install/setupdll/makefile
@@ -0,0 +1,10 @@
+#
+# 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
+#
+
+USE_CRTDLL=1
+
+!INCLUDE $(NTMAKEENV)\makefile.def
+!INCLUDE rules.mk
diff --git a/private/nw/install/setupdll/nwcfg.cxx b/private/nw/install/setupdll/nwcfg.cxx
new file mode 100644
index 000000000..1d31aca91
--- /dev/null
+++ b/private/nw/install/setupdll/nwcfg.cxx
@@ -0,0 +1,318 @@
+/**********************************************************************/
+/** Microsoft Windows NT **/
+/** Copyright(c) Microsoft Corp., 1991 **/
+/**********************************************************************/
+
+/*
+
+ nwcfg.cxx
+ netware configuration source code.
+
+ history:
+ terryk 05/07/93 Created
+*/
+
+
+#if defined(DEBUG)
+static const char szFileName[] = __FILE__;
+#define _FILENAME_DEFINED_ONCE szFileName
+#endif
+
+#define APIERR LONG
+
+extern "C"
+{
+
+#include <windows.h>
+#include <port1632.h>
+
+
+#include <winspool.h>
+
+// exported functions
+
+BOOL FAR PASCAL AddNetwarePrinterProvidor( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult );
+BOOL FAR PASCAL DeleteNetwarePrinterProvidor( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult );
+BOOL FAR PASCAL AppendSzToFile( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult );
+BOOL FAR PASCAL GetKernelVersion( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult );
+
+extern HINSTANCE ThisDLLHandle;
+}
+
+#define UNREFERENCED(x) ((void)(x))
+#include <nwcfg.hxx>
+#include <nwcfg.h>
+
+
+/*******************************************************************
+
+ NAME: AddNetwarePrinterProvidor
+
+ SYNOPSIS: This is a wrapper routine for called AddPrintProvidor. It
+ should be called from inf file if the user installs netware.
+
+ ENTRY: NONE from inf file.
+
+ RETURN: BOOL - TRUE for success.
+
+ HISTORY:
+ terryk 07-May-1993 Created
+
+********************************************************************/
+
+#define PROVIDER_DLL_NAME "nwprovau.dll"
+#define MAX_PROVIDER_NAME_LEN 512
+typedef BOOL (WINAPI *T_AddPrintProvidor)(LPSTR pName,DWORD Level,LPBYTE pMonitors);
+typedef BOOL (WINAPI *T_DeletePrintProvidor)(LPSTR pName,LPSTR pEnv, LPSTR pMon);
+
+
+BOOL FAR PASCAL AddNetwarePrinterProvidor( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult )
+{
+ UNREFERENCED( nArgs );
+
+ PROVIDOR_INFO_1 ProvidorInfo1;
+
+ ProvidorInfo1.pEnvironment = (LPSTR) NULL;
+ ProvidorInfo1.pDLLName = PROVIDER_DLL_NAME;
+
+ APIERR err = 0;
+ do {
+ CHAR buf[MAX_PROVIDER_NAME_LEN];
+ LPSTR lpProviderName = (LPSTR)buf;
+ if ( lpProviderName == NULL )
+ {
+ err = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+ }
+ if ( !LoadString( ThisDLLHandle,
+ PROVIDER_NAME,
+ lpProviderName,
+ MAX_PROVIDER_NAME_LEN ) )
+ {
+ err = ::GetLastError();
+ break;
+ }
+
+ ProvidorInfo1.pName = lpProviderName;
+
+
+
+ HINSTANCE hDll = ::LoadLibraryA( "winspool.drv" );
+ if ( hDll == NULL )
+ {
+ err = ::GetLastError();
+ break;
+ }
+
+ FARPROC pAddPrintProvidor = ::GetProcAddress( hDll, "AddPrintProvidorA" );
+
+ if ( pAddPrintProvidor == NULL )
+ {
+ err = ::GetLastError();
+ } else if ( !(*(T_AddPrintProvidor)pAddPrintProvidor)((LPSTR) NULL,1,(LPBYTE)&ProvidorInfo1))
+ {
+ err = ::GetLastError();
+ }
+
+ if ( hDll )
+ ::FreeLibrary( hDll );
+
+ } while (FALSE);
+ wsprintfA( achBuff, "{\"%d\"}", err );
+ *ppszResult = achBuff;
+
+ return TRUE;
+}
+
+BOOL FAR PASCAL DeleteNetwarePrinterProvidor( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult )
+{
+ UNREFERENCED( nArgs );
+ UNREFERENCED( apszArgs );
+
+ APIERR err = 0;
+
+ do {
+ HINSTANCE hDll = ::LoadLibraryA( "winspool.drv" );
+ if ( hDll == NULL )
+ {
+ err = ::GetLastError();
+ break;
+ }
+
+ FARPROC pDeletePrintProvidor = ::GetProcAddress( hDll, "DeletePrintProvidorA" );
+
+ if ( pDeletePrintProvidor == NULL )
+ {
+ err = ::GetLastError();
+ }
+ else
+ {
+ CHAR buf[MAX_PROVIDER_NAME_LEN];
+ LPSTR lpProviderName = (LPSTR)buf;
+ if ( lpProviderName == NULL )
+ {
+ err = ERROR_NOT_ENOUGH_MEMORY;
+ break;
+ }
+ if ( nArgs == 1 )
+ {
+ lpProviderName = apszArgs[0];
+ } else
+ {
+ if ( !LoadString( ThisDLLHandle,
+ PROVIDER_NAME,
+ lpProviderName,
+ MAX_PROVIDER_NAME_LEN ) )
+ {
+ err = ::GetLastError();
+ }
+ }
+ if ( !(*(T_DeletePrintProvidor)pDeletePrintProvidor)( (LPSTR) NULL,
+ (LPSTR) NULL,
+ lpProviderName))
+ {
+ err = ::GetLastError();
+ }
+ }
+
+ if ( hDll )
+ ::FreeLibrary ( hDll );
+
+ } while (FALSE);
+ wsprintfA( achBuff, "{\"%d\"}", err );
+ *ppszResult = achBuff;
+
+ return TRUE;
+}
+
+/*******************************************************************
+
+ NAME: AppendSzToFile
+
+ SYNOPSIS: Append a string to a file.
+
+ ENTRY: Args[0] - FileName string
+ Args[1] - String to be added to the file
+
+ RETURN: BOOL - TRUE for success.
+
+ HISTORY:
+ terryk 07-May-1993 Created
+
+********************************************************************/
+
+BOOL FAR PASCAL
+AppendSzToFile( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult )
+{
+ UNREFERENCED( nArgs );
+
+ DWORD BytesWritten;
+ HANDLE hfile;
+ LPSTR szFileName = apszArgs[0];
+ LPSTR szAddOnSz = apszArgs[1];
+
+ //
+ // Open the file
+ //
+
+ hfile = CreateFile(
+ szFileName,
+ GENERIC_WRITE | GENERIC_READ,
+ FILE_SHARE_READ,
+ (struct _SECURITY_ATTRIBUTES *) NULL,
+ OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL
+ );
+
+ if (hfile == (HANDLE)0xffffffff) {
+ wsprintfA( achBuff, "{ Cannot Open File: \"%s\"}", szFileName );
+ *ppszResult = achBuff;
+ return FALSE;
+ }
+
+ //
+ // Go to end of file
+ //
+
+ SetFilePointer (
+ hfile,
+ 0,
+ (PLONG) NULL,
+ FILE_END
+ );
+
+ //
+ // Append string passed in at the end of the file
+ //
+
+ WriteFile (
+ hfile,
+ szAddOnSz,
+ lstrlen( szAddOnSz ),
+ &BytesWritten,
+ (struct _OVERLAPPED *) NULL
+ );
+
+ CloseHandle (hfile);
+ wsprintfA( achBuff, "{\"%d\"}", 0 );
+ *ppszResult = achBuff;
+ return TRUE;
+}
+
+/*******************************************************************
+
+ NAME: GetKernelVersion
+
+ SYNOPSIS: Get the current kernel version number
+
+ ENTRY: NONE from inf file.
+
+ RETURN: BOOL - TRUE for success.
+ The return number is the kernel build number.
+ {"MajorVerion","MinorVersion","BuildNumber","PatchNumber"}
+
+ HISTORY:
+ terryk 24-Sept-1993 Created
+
+********************************************************************/
+
+BOOL FAR PASCAL
+GetKernelVersion( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult )
+{
+ UNREFERENCED( nArgs );
+
+ DWORD wVer;
+ LONG nSubVersion;
+ LONG nVersion;
+
+ LPCSTR lpszRegName = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion";
+ HKEY hsubkey ;
+ DWORD dwZero = 0;
+ DWORD dwRegValueType;
+ DWORD dwRegValue;
+ DWORD cbRegValue;
+
+ wVer = GetVersion();
+ nSubVersion = GETMINORVERSION(wVer);
+ nVersion = GETMAJORVERSION(wVer);
+
+ cbRegValue = sizeof(dwRegValue);
+
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ lpszRegName, dwZero, KEY_QUERY_VALUE, &hsubkey) ||
+ RegQueryValueEx(hsubkey, "CSDVersion", (LPDWORD)NULL,
+ &dwRegValueType, (LPBYTE)&dwRegValue, &cbRegValue) ||
+ dwRegValueType != REG_DWORD
+ ) {
+ wsprintf(achBuff,"{\"%d\",\"%d\",\"%d\",\"%d\"}", nVersion, nSubVersion, wVer >> 16, 0);
+ } else {
+ wsprintf(achBuff,"{\"%d\",\"%d\",\"%d\",\"%d\"}", nVersion, nSubVersion, wVer >> 16, dwRegValue);
+ }
+ if (hsubkey != NULL) {
+ RegCloseKey (hsubkey);
+ }
+ *ppszResult = achBuff;
+ return TRUE;
+}
+
diff --git a/private/nw/install/setupdll/nwcfg.def b/private/nw/install/setupdll/nwcfg.def
new file mode 100644
index 000000000..3fa95f62b
--- /dev/null
+++ b/private/nw/install/setupdll/nwcfg.def
@@ -0,0 +1,19 @@
+LIBRARY NWCFG
+
+DESCRIPTION 'Client Service for Netware configuration dll $V'
+
+EXPORTS
+ AddNetwarePrinterProvidor @1
+ DeleteNetwarePrinterProvidor @2
+ AppendSzToFile @3
+ RemoveSzFromFile @4
+ GetKernelVersion @5
+ SetEverybodyPermission @6
+ lodctr @7
+ unlodctr @8
+ DeleteGatewayPassword @9
+ SetFileSysChangeValue @10
+
+ CleanupRegistryForNWCS @11
+ SetupRegistryForNWCS @12
+
diff --git a/private/nw/install/setupdll/nwcfg.h b/private/nw/install/setupdll/nwcfg.h
new file mode 100644
index 000000000..6aa91281c
--- /dev/null
+++ b/private/nw/install/setupdll/nwcfg.h
@@ -0,0 +1,22 @@
+/**********************************************************************/
+/** Microsoft Windows NT **/
+/** Copyright(c) Microsoft Corp., 1991 **/
+/**********************************************************************/
+
+/*
+
+ nwcfg.h
+
+ history:
+ thomaspa 1/24/94 Created
+*/
+
+
+#ifndef _NWCFG_H_
+#define _NWCFG_H_
+
+#define PROVIDER_NAME 101
+
+#endif // _NWCFG_H_
+
+
diff --git a/private/nw/install/setupdll/nwcfg.hxx b/private/nw/install/setupdll/nwcfg.hxx
new file mode 100644
index 000000000..db856a517
--- /dev/null
+++ b/private/nw/install/setupdll/nwcfg.hxx
@@ -0,0 +1,28 @@
+/**********************************************************************/
+/** Microsoft Windows NT **/
+/** Copyright(c) Microsoft Corp., 1991 **/
+/**********************************************************************/
+
+/*
+
+ nwcfg.hxx
+ Header for output buffer.
+
+ history:
+ terryk 09/30/93 Created
+*/
+
+
+#ifndef _NWCFG_HXX_
+#define _NWCFG_HXX_
+
+/* Global return buffer */
+char achBuff[2000];
+
+#else
+
+extern char achBuff[2000];
+
+#endif // _NWCFG_HXX_
+
+
diff --git a/private/nw/install/setupdll/nwcfg.prf b/private/nw/install/setupdll/nwcfg.prf
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/private/nw/install/setupdll/nwcfg.prf
@@ -0,0 +1 @@
+
diff --git a/private/nw/install/setupdll/nwcfg.rc b/private/nw/install/setupdll/nwcfg.rc
new file mode 100644
index 000000000..ba1ddfa99
--- /dev/null
+++ b/private/nw/install/setupdll/nwcfg.rc
@@ -0,0 +1,20 @@
+#include "windows.h"
+#include "nwcfg.h"
+
+
+stringtable
+begin
+ PROVIDER_NAME, "NetWare or Compatible Network"
+end
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "NWC Configuration DLL"
+#define VER_INTERNALNAME_STR "nwcfg.dll"
+#define VER_ORIGINALFILENAME_STR "nwcfg.dll"
+
+#include "common.ver"
+
+
diff --git a/private/nw/install/setupdll/removesz.c b/private/nw/install/setupdll/removesz.c
new file mode 100644
index 000000000..00757ae6f
--- /dev/null
+++ b/private/nw/install/setupdll/removesz.c
@@ -0,0 +1,58 @@
+/*++
+Copyright (c) 1990 Microsoft Corporation
+
+RemoveSzFromFile() - remove a specified string from the file
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <nwcfg.hxx>
+
+
+BOOL
+RemoveSzFromFile( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult )
+{
+ FILE * hsrcfile;
+ FILE * hdesfile;
+ char * pszTempname;
+ char szInput[1000];
+
+ pszTempname = tmpnam(NULL);
+ wsprintf(achBuff,"{1}");
+ *ppszResult = achBuff;
+ if ( nArgs != 2 )
+ {
+ return(FALSE);
+ }
+ hsrcfile = fopen(apszArgs[0],"r");
+ hdesfile = fopen(pszTempname,"w");
+ if (( hsrcfile != NULL ) && ( hdesfile != NULL ))
+ {
+ while (fgets(szInput,1000,hsrcfile))
+ {
+ if (_stricmp(szInput,apszArgs[1])!=0)
+ {
+ fputs(szInput,hdesfile);
+ }
+ }
+ }
+ if ( hsrcfile != NULL )
+ fclose(hsrcfile);
+ if ( hdesfile != NULL )
+ fclose(hdesfile);
+ if (( hsrcfile != NULL ) && ( hdesfile != NULL ))
+ {
+ CopyFileA(pszTempname,apszArgs[0], FALSE);
+ DeleteFileA(pszTempname);
+ }
+
+ wsprintf(achBuff,"{0}");
+ *ppszResult = achBuff;
+ return(TRUE);
+}
diff --git a/private/nw/install/setupdll/rules.mk b/private/nw/install/setupdll/rules.mk
new file mode 100644
index 000000000..232ff4c33
--- /dev/null
+++ b/private/nw/install/setupdll/rules.mk
@@ -0,0 +1,4 @@
+# @@ COPY_RIGHT_HERE
+# @@ ROADMAP :: The Rules.mk for the Network Control Panel Applet
+#
+
diff --git a/private/nw/install/setupdll/setvalue.c b/private/nw/install/setupdll/setvalue.c
new file mode 100644
index 000000000..bfaf073cf
--- /dev/null
+++ b/private/nw/install/setupdll/setvalue.c
@@ -0,0 +1,696 @@
+/**********************************************************************/
+/** Microsoft Windows NT **/
+/** Copyright(c) Microsoft Corp., 1991 **/
+/**********************************************************************/
+
+/*
+
+ setvalue.c
+ Code to enable SetValue for everyone.
+
+ history:
+ terryk 09/30/93 Created
+*/
+
+
+#if defined(DEBUG)
+static const char szFileName[] = __FILE__;
+#define _FILENAME_DEFINED_ONCE szFileName
+#endif
+
+#include <string.h>
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <nwlsa.h>
+#include <nwapi.h>
+#include <nwcfg.h>
+#include <nwcfg.hxx>
+
+extern char achBuff[];
+
+// exported functions
+
+BOOL FAR PASCAL SetFileSysChangeValue( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult );
+BOOL FAR PASCAL SetEverybodyPermission( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult );
+BOOL FAR PASCAL SetupRegistryForNWCS( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult );
+BOOL FAR PASCAL SetupRegistryWorker( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult );
+BOOL FAR PASCAL DeleteGatewayPassword( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult );
+BOOL FAR PASCAL CleanupRegistryForNWCS( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult );
+
+//
+// structure for registry munging
+//
+
+typedef struct REG_ENTRY_ {
+ DWORD Operation ;
+ LONG Level ;
+ LPWSTR s1 ;
+ LPWSTR s2 ;
+} REG_ENTRY ;
+
+//
+// local routines
+//
+
+DWORD SetupShellExtensions(REG_ENTRY RegEntries[], DWORD dwNumEntries) ;
+
+typedef DWORD (*LPNWCLEANUPGATEWAYSHARES)(VOID) ;
+
+// Values & Tables that define registry data
+
+#define MAX_REG_LEVEL 10
+
+#define CREATE_ABS 1 // create/open a key with absolute path
+#define CREATE_REL 2 // create/open a key with relative path
+#define VALUE_STR 3 // write a string value
+#define DELETE_ABS 4 // delete key with absolute path
+#define DELETE_REL 5 // delete key with relative path
+#define DELETE_VAL 6 // delete a value
+#define DROP_STACK 7 // drop stack by one
+
+REG_ENTRY RegCreateEntries[] =
+{
+ {CREATE_ABS,0,L"SOFTWARE\\Classes\\NetWare_or_Compatible_Network", NULL},
+ {DELETE_REL,0,L"shellex\\ContextMenuHandlers\\NetWareMenus", NULL},
+ {DELETE_REL,0,L"shellex\\ContextMenuHandlers", NULL},
+ {DELETE_REL,0,L"shellex\\PropertySheetHandlers\\NetWarePage", NULL},
+ {DELETE_REL,0,L"shellex\\PropertySheetHandlers", NULL},
+ {DELETE_REL,0,L"shellex", NULL},
+ {DROP_STACK,0,NULL,NULL},
+ {DELETE_ABS,0,L"SOFTWARE\\Classes\\NetWare_or_Compatible_Network", NULL},
+
+ {CREATE_ABS, 0,L"SOFTWARE\\Classes\\Network\\Type", NULL},
+ {CREATE_REL,+1, L"3", NULL},
+ {CREATE_REL,+1, L"shellex", NULL},
+ {CREATE_REL,+1, L"ContextMenuHandlers", NULL},
+ {CREATE_REL,+1, L"NetWareMenus", NULL},
+ {VALUE_STR,0, L"", L"{8e9d6600-f84a-11ce-8daa-00aa004a5691}"},
+ {CREATE_REL,-1, L"PropertySheetHandlers", NULL},
+ {CREATE_REL,+1, L"NetWarePage", NULL},
+ {VALUE_STR,0, L"", L"{8e9d6600-f84a-11ce-8daa-00aa004a5691}"},
+ {CREATE_ABS, 0,L"SOFTWARE\\Classes\\CLSID", NULL},
+ {CREATE_REL,+1, L"{8e9d6600-f84a-11ce-8daa-00aa004a5691}", NULL},
+ {VALUE_STR,0, L"", L"NetWare Objects"},
+ {CREATE_REL,+1, L"InProcServer32", NULL},
+ {VALUE_STR,0, L"", L"nwprovau.dll"},
+ {VALUE_STR,0, L"ThreadingModel", L"Apartment"},
+ {CREATE_REL,-1, L"{e3f2bac0-099f-11cf-8daa-00aa004a5691}", NULL},
+ {VALUE_STR,0, L"", L"NetWare UNC Folder Menu"},
+ {CREATE_REL,+1, L"InProcServer32", NULL},
+ {VALUE_STR,0, L"", L"nwprovau.dll"},
+ {VALUE_STR,0, L"ThreadingModel", L"Apartment"},
+ {CREATE_REL,-1, L"{52c68510-09a0-11cf-8daa-00aa004a5691}", NULL},
+ {VALUE_STR,0, L"", L"NetWare Hood Verbs"},
+ {CREATE_REL,+1, L"InProcServer32", NULL},
+ {VALUE_STR,0, L"", L"nwprovau.dll"},
+ {VALUE_STR,0, L"ThreadingModel", L"Apartment"},
+ {CREATE_REL,-1, L"{208D2C60-3AEA-1069-A2D7-08002B30309D}", NULL},
+ {CREATE_REL,+1, L"shellex", NULL},
+ {CREATE_REL,+1, L"ContextMenuHandlers", NULL},
+ {CREATE_REL,+1, L"NetWareMenus", NULL},
+ {VALUE_STR,0, L"", L"{52c68510-09a0-11cf-8daa-00aa004a5691}"},
+ {CREATE_ABS, 0,L"SOFTWARE\\Classes\\Folder", NULL},
+ {CREATE_REL,+1, L"shellex", NULL},
+ {CREATE_REL,+1, L"ContextMenuHandlers", NULL},
+ {CREATE_REL,+1, L"NetWareUNCMenu", NULL},
+ {VALUE_STR,0, L"", L"{e3f2bac0-099f-11cf-8daa-00aa004a5691}"},
+ {CREATE_ABS, 0,L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion", NULL},
+ {CREATE_REL,+1, L"Shell Extensions", NULL},
+ {CREATE_REL,+1, L"Approved", NULL},
+ {VALUE_STR,0, L"{8e9d6600-f84a-11ce-8daa-00aa004a5691}", L"Shell extensions for NetWare"},
+ {VALUE_STR,0, L"{e3f2bac0-099f-11cf-8daa-00aa004a5691}", L"Shell extensions for NetWare"},
+ {VALUE_STR,0, L"{52c68510-09a0-11cf-8daa-00aa004a5691}", L"Shell extensions for NetWare"}
+} ;
+
+REG_ENTRY RegDeleteEntries[] =
+{
+ {CREATE_ABS,0,L"SOFTWARE\\Classes\\Network\\Type\\3", NULL},
+ {DELETE_REL,0,L"shellex\\ContextMenuHandlers\\NetWareMenus", NULL},
+ {DELETE_REL,0,L"shellex\\ContextMenuHandlers", NULL},
+ {DELETE_REL,0,L"shellex\\PropertySheetHandlers\\NetWarePage", NULL},
+ {DELETE_REL,0,L"shellex\\PropertySheetHandlers", NULL},
+ {DELETE_REL,0,L"shellex", NULL},
+ {DROP_STACK,0,NULL,NULL},
+ {DELETE_ABS,0,L"SOFTWARE\\Classes\\Network\\Type\\3", NULL},
+
+ {DELETE_ABS,0,L"SOFTWARE\\Classes\\CLSID\\{8e9d6600-f84a-11ce-8daa-00aa004a5691}\\InProcServer32", NULL},
+ {DELETE_ABS,0,L"SOFTWARE\\Classes\\CLSID\\{8e9d6600-f84a-11ce-8daa-00aa004a5691}", NULL},
+ {DELETE_ABS,0,L"SOFTWARE\\Classes\\CLSID\\{e3f2bac0-099f-11cf-8daa-00aa004a5691}\\InProcServer32", NULL},
+ {DELETE_ABS,0,L"SOFTWARE\\Classes\\CLSID\\{e3f2bac0-099f-11cf-8daa-00aa004a5691}", NULL},
+ {DELETE_ABS,0,L"SOFTWARE\\Classes\\CLSID\\{52c68510-09a0-11cf-8daa-00aa004a5691}\\InProcServer32", NULL},
+ {DELETE_ABS,0,L"SOFTWARE\\Classes\\CLSID\\{52c68510-09a0-11cf-8daa-00aa004a5691}", NULL},
+ {DELETE_ABS,0,L"SOFTWARE\\Classes\\CLSID\\{208D2C60-3AEA-1069-A2D7-08002B30309D}\\shellex\\ContextMenuHandlers\\NetWareMenus", NULL},
+
+ {DELETE_ABS,0,L"SOFTWARE\\Classes\\Folder\\shellex\\ContextMenuHandlers\\NetWareUNCMenu", NULL},
+ {CREATE_ABS,0,L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved", NULL},
+ {DELETE_VAL,0,L"{8e9d6600-f84a-11ce-8daa-00aa004a5691}", NULL},
+ {DELETE_VAL,0,L"{e3f2bac0-099f-11cf-8daa-00aa004a5691}", NULL},
+ {DELETE_VAL,0,L"{52c68510-09a0-11cf-8daa-00aa004a5691}", NULL}
+} ;
+
+
+/*******************************************************************
+
+ NAME: SetEverybodyPermission
+
+ SYNOPSIS: Set the registry key to everybody "Set Value" (or whatever
+ the caller want.) This is called from the inf file
+
+ ENTRY: Registry key as the first parameter
+ Permisstion type as the second parameter
+
+ RETURN: BOOL - TRUE for success.
+
+ HISTORY:
+ terryk 07-May-1993 Created
+
+********************************************************************/
+
+typedef DWORD (*T_SetPermission)(HKEY hKey, DWORD dwPermission);
+
+BOOL FAR PASCAL SetEverybodyPermission( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult )
+{
+ HKEY hKey = (HKEY)atol( &(apszArgs[0][1]) ); // registry key
+ DWORD dwPermission = atol( apszArgs[1] ); // permission value
+ DWORD err = ERROR_SUCCESS;
+
+ do {
+ HINSTANCE hDll = LoadLibraryA( "nwapi32.dll" );
+ FARPROC pSetPermission = NULL;
+
+ if ( hDll == NULL )
+ {
+ err = GetLastError();
+ break;
+ }
+
+ pSetPermission = GetProcAddress( hDll, "NwLibSetEverybodyPermission" );
+
+ if ( pSetPermission == NULL )
+ {
+ err = GetLastError();
+ break;
+ }
+ err = (*(T_SetPermission)pSetPermission)( hKey, dwPermission );
+ } while ( FALSE );
+
+ wsprintfA( achBuff, "{\"%d\"}", err );
+ *ppszResult = achBuff;
+
+ return( err == ERROR_SUCCESS );
+}
+
+/*******************************************************************
+
+ NAME: SetFileSysChangeValue
+
+ SYNOPSIS: calls common setup routine. this old entry point is
+ is left here to handle any DLL/INF mismatch.
+
+ ENTRY: NONE from inf file.
+
+ RETURN: BOOL - TRUE for success.
+ (always return TRUE)
+
+ HISTORY:
+ chuckc 29-Oct-1993 Created
+
+********************************************************************/
+
+BOOL FAR PASCAL SetFileSysChangeValue( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult )
+{
+ return SetupRegistryWorker( nArgs, apszArgs, ppszResult );
+}
+
+/*******************************************************************
+
+ NAME: SetupRegistryForNWCS
+
+ SYNOPSIS: calls common worker routine to setup registry.
+
+ ENTRY: NONE from inf file.
+
+ RETURN: BOOL - TRUE for success.
+ (always return TRUE)
+
+ HISTORY:
+ chuckc 29-Oct-1993 Created
+
+********************************************************************/
+
+BOOL FAR PASCAL SetupRegistryForNWCS( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult )
+{
+ return SetupRegistryWorker( nArgs, apszArgs, ppszResult );
+}
+
+/*******************************************************************
+
+ NAME: SetupRegistryWorker
+
+ SYNOPSIS: set the FileSysChangeValue to please NETWARE.DRV.
+ also set win.ini parameter so wfwnet.drv knows we are there.
+
+ ENTRY: NONE from inf file.
+
+ RETURN: BOOL - TRUE for success.
+ (always return TRUE)
+
+ HISTORY:
+ chuckc 29-Oct-1993 Created
+
+********************************************************************/
+
+BOOL FAR PASCAL SetupRegistryWorker( DWORD nArgs, LPSTR apszArgs[], LPSTR * ppszResult )
+{
+ DWORD err = 0, err1 = 0 ;
+
+ (void) nArgs ; // quiet the compiler
+ (void) apszArgs ; // quiet the compiler
+
+ if (!WriteProfileStringA("NWCS",
+ "NwcsInstalled",
+ "1"))
+ {
+ err = GetLastError() ;
+ }
+
+ if (!WritePrivateProfileStringA("386Enh",
+ "FileSysChange",
+ "off",
+ "system.ini"))
+ {
+ err1 = GetLastError() ;
+ }
+
+ if (err1 == NO_ERROR)
+ {
+ err1 = SetupShellExtensions(
+ RegCreateEntries,
+ sizeof(RegCreateEntries)/sizeof(RegCreateEntries[0])) ;
+ }
+
+ wsprintfA( achBuff, "{\"%d\"}", err ? err : err1 );
+ *ppszResult = achBuff;
+
+ return TRUE;
+}
+
+#define NWCLEANUPGATEWAYSHARES_NAME "NwCleanupGatewayShares"
+#define NWPROVAU_DLL_NAME L"NWPROVAU"
+
+/*******************************************************************
+
+ NAME: DeleteGatewayPassword
+
+ SYNOPSIS: delete the LSA secret used for gateway password.
+ also clears the NWCS installed bit. INF will be
+ changed to call CleanupRegistryForNWCS, but this entry
+ point is left here to handle DLL/INF mismatch.
+
+ ENTRY: NONE from inf file.
+
+ RETURN: BOOL - TRUE for success.
+ (always return TRUE)
+
+ HISTORY:
+ chuckc 29-Oct-1993 Created
+
+********************************************************************/
+
+BOOL FAR PASCAL
+DeleteGatewayPassword(
+ DWORD nArgs,
+ LPSTR apszArgs[],
+ LPSTR * ppszResult
+ )
+{
+ return TRUE ; // work is done in cleanup below which does everything.
+}
+
+/*******************************************************************
+
+ NAME: CleanupRegistryForNWCS
+
+ SYNOPSIS: delete the LSA secret used for gateway password.
+ also set flag that NWCS has been removed. this flag
+ is used by wfwnet.drv.
+
+ ENTRY: NONE from inf file.
+
+ RETURN: BOOL - TRUE for success.
+ (always return TRUE)
+
+ HISTORY:
+ chuckc 29-Oct-1993 Created
+
+********************************************************************/
+
+BOOL FAR PASCAL
+CleanupRegistryForNWCS(
+ DWORD nArgs,
+ LPSTR apszArgs[],
+ LPSTR * ppszResult
+ )
+{
+ HANDLE hDll ;
+ DWORD err = 0, err1 = 0 ;
+ LPNWCLEANUPGATEWAYSHARES lpfnNwCleanupGatewayShares = NULL ;
+
+ (void) nArgs ; // quiet the compiler
+ (void) apszArgs ; // quiet the compiler
+
+ if (!WriteProfileStringA("NWCS",
+ "NwcsInstalled",
+ "0"))
+ {
+ err = GetLastError() ;
+ }
+
+ err1 = NwDeletePassword(GATEWAY_USER) ;
+
+ if (!err)
+ err = err1 ;
+
+ if ((hDll = LoadLibraryW(NWPROVAU_DLL_NAME)) &&
+ (lpfnNwCleanupGatewayShares = (LPNWCLEANUPGATEWAYSHARES)
+ GetProcAddress(hDll, NWCLEANUPGATEWAYSHARES_NAME)))
+ {
+ err1 = (*lpfnNwCleanupGatewayShares)() ;
+ (void) FreeLibrary(hDll) ;
+ }
+
+ //
+ // ignore errors for this.
+ //
+ (void) SetupShellExtensions(
+ RegDeleteEntries,
+ sizeof(RegDeleteEntries)/sizeof(RegDeleteEntries[0])) ;
+
+ if (!err)
+ err = err1 ;
+
+ wsprintfA( achBuff, "{\"%d\"}", err );
+ *ppszResult = achBuff;
+
+ return TRUE;
+}
+
+/*******************************************************************
+
+ NAME: SetupShellExtensions
+
+ SYNOPSIS: setup the registry for shell extensions. function is driven
+ by a table of entries (RegEntries). for each entry there is a
+ Operation code that tells us what we are doing. key entries can
+ be created absolute or relative to previous positions, so we
+ maintain a stack of registry handles for the latter case. every
+ key that is created is initially put on the stack. values
+ are always written based on the 'current stack' position.
+
+ ENTRY: NONE
+
+ RETURN: Win32 error code
+
+ HISTORY:
+ chuckc 29-Nov-1995 Created
+
+********************************************************************/
+DWORD SetupShellExtensions(REG_ENTRY RegEntries[], DWORD dwNumEntries)
+{
+ DWORD err, errClose, dwDisposition, i ;
+ HKEY hKey, RegHandleStack[MAX_REG_LEVEL] ;
+ LONG StackIndex = -1 ;
+
+ //
+ // Loop thru and for each entry. Then switch & do the appropriate
+ // operation in the registry.
+ //
+
+ for (i = 0; i < dwNumEntries; i++)
+ {
+ err = NO_ERROR ;
+
+ switch (RegEntries[i].Operation)
+ {
+ case CREATE_ABS:
+
+ //
+ // create/open a reg key with an absolute path. since this
+ // is absolute, we drop everything on the stack, and start
+ // all over again.
+ //
+
+ while (StackIndex >= 0)
+ {
+ errClose = RegCloseKey(RegHandleStack[StackIndex--]) ;
+ ASSERT(errClose == NO_ERROR) ;
+ }
+
+ err = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
+ RegEntries[i].s1, // subkey
+ 0, // reserved
+ NULL, // class
+ REG_OPTION_NON_VOLATILE,
+ KEY_ALL_ACCESS,
+ NULL, // default security
+ &hKey,
+ &dwDisposition) ; // not used
+ if (err != NO_ERROR)
+ {
+ break ;
+ }
+
+ //
+ // by default we advance the stack. no need check for overflow
+ // as the stack is empty.
+ //
+
+ RegHandleStack[++StackIndex] = hKey ;
+ break ;
+
+ case CREATE_REL:
+
+ //
+ // create/open a reg key relative to current stack. make sure
+ // there is something on the stack (check StackIndex >= 0).
+ // then see if we are advancing (+1), staying the same (0) or
+ // dropping back (-ve).
+ //
+
+ if (StackIndex < 0)
+ {
+ err = ERROR_INVALID_FUNCTION ;
+ break ;
+ }
+
+ if (RegEntries[i].Level == +1)
+ {
+ //
+ // opening next level down. continue as is and use
+ // most recently opened key as the starting point.
+ //
+ }
+ else if (RegEntries[i].Level == 0)
+ {
+ //
+ // opening at same level as last time. so we are done
+ // with the last key. what we want to do is close it
+ // and use the parent.
+ //
+ errClose = RegCloseKey(RegHandleStack[StackIndex--]) ;
+
+ ASSERT(errClose == NO_ERROR) ;
+
+ if (StackIndex < 0)
+ {
+ err = ERROR_INVALID_FUNCTION ;
+ break ;
+ }
+ }
+ else if (RegEntries[i].Level < 0)
+ {
+ //
+ // dropping back & opening at a higher level. cleanup
+ // handle for each level we pop.
+ //
+
+ LONG Count = RegEntries[i].Level ;
+
+ while (Count++ < 1)
+ {
+ errClose = RegCloseKey(RegHandleStack[StackIndex--]) ;
+
+ ASSERT(errClose == NO_ERROR) ;
+
+ if (StackIndex < -1)
+ {
+ err = ERROR_INVALID_FUNCTION ;
+ break ;
+ }
+ }
+ }
+ else
+ {
+ //
+ // only -ve numbers, 0 and 1 are valid
+ //
+
+ err = ERROR_INVALID_FUNCTION ;
+ break ;
+ }
+
+ //
+ // create key relative to current point
+ //
+ err = RegCreateKeyExW(RegHandleStack[StackIndex], // current key
+ RegEntries[i].s1, // subkey
+ 0, // reserved
+ NULL, // class
+ REG_OPTION_NON_VOLATILE,
+ KEY_ALL_ACCESS,
+ NULL, // default security
+ &hKey,
+ &dwDisposition) ; // not used
+ if (err != NO_ERROR)
+ {
+ break ;
+ }
+
+ //
+ // by default we advance the stack
+ //
+
+ RegHandleStack[++StackIndex] = hKey ;
+
+ if (StackIndex >= MAX_REG_LEVEL)
+ {
+ err = ERROR_INVALID_FUNCTION ;
+ break ;
+ }
+
+ break ;
+
+ case VALUE_STR:
+
+ //
+ // create a REG_SZ value at current point. check we have
+ // handle on stack.
+ //
+
+ if (StackIndex < 0)
+ {
+ err = ERROR_INVALID_FUNCTION ;
+ break ;
+ }
+
+ err = RegSetValueExW(
+ RegHandleStack[StackIndex], // current key
+ RegEntries[i].s1, // value name
+ 0, // reserved
+ REG_SZ,
+ (BYTE *)RegEntries[i].s2, // value data
+ (wcslen(RegEntries[i].s2)+1)*sizeof(WCHAR)) ;
+ break ;
+
+ case DELETE_ABS:
+
+ //
+ // delete a key (absolute). no change to stack.
+ //
+
+ err = RegDeleteKeyW(HKEY_LOCAL_MACHINE,
+ RegEntries[i].s1) ; // subkey
+
+ if ( err == ERROR_FILE_NOT_FOUND )
+ err = NO_ERROR;
+
+ break ;
+
+ case DELETE_REL:
+
+ //
+ // delete a key (relative). no change to stack.
+ //
+
+ if (StackIndex < 0)
+ {
+ err = ERROR_INVALID_FUNCTION ;
+ break ;
+ }
+
+ err = RegDeleteKeyW(RegHandleStack[StackIndex], // current key
+ RegEntries[i].s1) ; // subkey
+
+ if ( err == ERROR_FILE_NOT_FOUND )
+ err = NO_ERROR;
+
+ break ;
+
+ case DELETE_VAL:
+
+ //
+ // delete value at current point. check we have handle on stack.
+ //
+
+ if (StackIndex < 0)
+ {
+ err = ERROR_INVALID_FUNCTION ;
+ break ;
+ }
+
+ err = RegDeleteValueW(RegHandleStack[StackIndex], // current key
+ RegEntries[i].s1) ; // value name
+ break ;
+
+ case DROP_STACK:
+
+ //
+ // drop current stack by one (closing the handle).
+ //
+
+ if (StackIndex < 0)
+ {
+ err = ERROR_INVALID_FUNCTION ;
+ break ;
+ }
+
+ errClose = RegCloseKey(RegHandleStack[StackIndex--]) ;
+
+ ASSERT(errClose == NO_ERROR) ;
+
+ break ;
+
+ default:
+
+ //
+ // error out if unknown operation
+ //
+
+ err = ERROR_INVALID_FUNCTION ;
+ break ;
+ }
+
+ if (err != NO_ERROR)
+ {
+ break ;
+ }
+ }
+
+ //
+ // cleanup open handles on the stack
+ //
+
+ while (StackIndex >= 0)
+ {
+ errClose = RegCloseKey(RegHandleStack[StackIndex--]) ;
+ ASSERT(errClose == NO_ERROR) ;
+ }
+
+ return err ;
+}
diff --git a/private/nw/install/setupdll/sources b/private/nw/install/setupdll/sources
new file mode 100644
index 000000000..81943eec1
--- /dev/null
+++ b/private/nw/install/setupdll/sources
@@ -0,0 +1,48 @@
+!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:
+
+ Steve Wood (stevewo) 12-Apr-1989
+
+!ENDIF
+
+MAJORCOMP=shell
+MINORCOMP=library
+
+INCLUDES=.;..\..\svcdlls\nwwks\inc;..\..\inc
+MSC_WARNING_LEVEL=/W3 /WX
+
+TARGETNAME=nwcfg
+TARGETPATH=obj
+TARGETTYPE=DYNLINK
+UMTYPE=windows
+
+TARGETLIBS= $(BASEDIR)\public\sdk\lib\*\user32.lib \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib \
+ $(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ ..\..\svcdlls\nwwks\lib\obj\*\nwwlib.lib
+
+DLLBASE=0x67800000
+DLLENTRY=DLLInit
+SOURCES=nwcfg.cxx \
+ removesz.c \
+ setvalue.c \
+ lodctr.c \
+ unlodctr.c \
+ dllinit.c \
+ nwcfg.rc
+
diff --git a/private/nw/install/setupdll/unlodctr.c b/private/nw/install/setupdll/unlodctr.c
new file mode 100644
index 000000000..0a9dc1c13
--- /dev/null
+++ b/private/nw/install/setupdll/unlodctr.c
@@ -0,0 +1,1006 @@
+/*++
+
+Copyright (c) 1991-1993 Microsoft Corporation
+
+Module Name:
+
+ unlodctr.c
+
+Abstract:
+
+ Program to remove the counter names belonging to the driver specified
+ in the command line and update the registry accordingly
+
+Author:
+
+ Bob Watson (a-robw) 12 Feb 93
+
+Revision History:
+
+--*/
+#define UNICODE 1
+#define _UNICODE 1
+//
+// "C" Include files
+//
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <malloc.h>
+//
+// Windows Include files
+//
+#include <windows.h>
+#include <winperf.h>
+#include <tchar.h>
+//
+// local include files
+//
+//#define _INITIALIZE_GLOBALS_ 1 // to define & init global buffers
+#include "common.h"
+//#undef _INITIALIZE_GLOBALS_
+#include "nwcfg.hxx"
+
+// version number for NT 1.0
+#define OLD_VERSION 0x010000
+DWORD dwSystemVersion; // PerfLib version number
+DWORD dwHelpItems; // number of explain text items
+DWORD dwCounterItems; // number of counter text items
+DWORD dwLastCounter;
+DWORD dwLastHelp;
+
+
+LPTSTR
+*BuildNameTable(
+ IN HKEY hKeyPerflib, // handle to perflib key with counter names
+ IN LPTSTR lpszLangId, // unicode value of Language subkey
+ OUT PDWORD pdwLastItem, // size of array in elements
+ OUT HKEY *hKeyNames,
+ OUT LPTSTR CounterNameBuffer, // New version counter name key
+ OUT LPTSTR HelpNameBuffer // New version help name key
+)
+/*++
+
+BuildNameTable
+
+ Caches the counter names and explain text to accelerate name lookups
+ for display.
+
+Arguments:
+
+ hKeyPerflib
+ Handle to an open registry (this can be local or remote.) and
+ is the value returned by RegConnectRegistry or a default key.
+
+ lpszLangId
+ The unicode id of the language to look up. (default is 009)
+
+ pdwLastItem
+ The last array element
+
+Return Value:
+
+ pointer to an allocated table. (the caller must free it when finished!)
+ the table is an array of pointers to zero terminated TEXT strings.
+
+ A NULL pointer is returned if an error occured. (error value is
+ available using the GetLastError function).
+
+ The structure of the buffer returned is:
+
+ Array of pointers to zero terminated strings consisting of
+ pdwLastItem elements
+
+ MULTI_SZ string containing counter id's and names returned from
+ registry for the specified language
+
+ MULTI_SZ string containing explain text id's and explain text strings
+ as returned by the registry for the specified language
+
+ The structures listed above are contiguous so that they may be freed
+ by a single "free" call when finished with them, however only the
+ array elements are intended to be used.
+
+--*/
+{
+
+ LPTSTR *lpReturnValue; // returned pointer to buffer
+
+ LPTSTR *lpCounterId; //
+ LPTSTR lpCounterNames; // pointer to Names buffer returned by reg.
+ LPTSTR lpHelpText ; // pointet to exlpain buffer returned by reg.
+
+ LPTSTR lpThisName; // working pointer
+
+
+ BOOL bStatus; // return status from TRUE/FALSE fn. calls
+ LONG lWin32Status; // return status from fn. calls
+
+ DWORD dwValueType; // value type of buffer returned by reg.
+ DWORD dwArraySize; // size of pointer array in bytes
+ DWORD dwBufferSize; // size of total buffer in bytes
+ DWORD dwCounterSize; // size of counter text buffer in bytes
+ DWORD dwHelpSize; // size of help text buffer in bytes
+ DWORD dwThisCounter; // working counter
+
+ DWORD dwLastId; // largest ID value used by explain/counter text
+
+ LPTSTR lpValueNameString; // pointer to buffer conatining subkey name
+
+ //initialize pointers to NULL
+
+ lpValueNameString = NULL;
+ lpReturnValue = NULL;
+
+ // check for null arguments and insert defaults if necessary
+
+ if (!lpszLangId) {
+ lpszLangId = DefaultLangId;
+ }
+
+ if (hKeyNames) {
+ *hKeyNames = NULL;
+ } else {
+ SetLastError (ERROR_BAD_ARGUMENTS);
+ return NULL;
+ }
+
+ // use the greater of Help items or Counter Items to size array
+
+ if (dwHelpItems >= dwCounterItems) {
+ dwLastId = dwHelpItems;
+ } else {
+ dwLastId = dwCounterItems;
+ }
+
+ // array size is # of elements (+ 1, since names are "1" based)
+ // times the size of a pointer
+
+ dwArraySize = (dwLastId + 1) * sizeof(LPTSTR);
+
+ // allocate string buffer for language ID key string
+
+ lpValueNameString = malloc (
+ lstrlen(NamesKey) * sizeof (TCHAR) +
+ lstrlen(Slash) * sizeof (TCHAR) +
+ lstrlen(lpszLangId) * sizeof (TCHAR) +
+ sizeof (TCHAR));
+
+ if (!lpValueNameString) {
+ lWin32Status = ERROR_OUTOFMEMORY;
+ goto BNT_BAILOUT;
+ }
+
+ lWin32Status = RegOpenKeyEx ( // get handle to this key in the
+ hKeyPerflib, // registry
+ lpszLangId,
+ RESERVED,
+ KEY_READ | KEY_WRITE,
+ hKeyNames);
+
+ if (lWin32Status != ERROR_SUCCESS) goto BNT_BAILOUT;
+
+ // get size of counter names
+
+ dwBufferSize = 0;
+ lWin32Status = RegQueryValueEx (
+ *hKeyNames,
+ Counters,
+ RESERVED,
+ &dwValueType,
+ NULL,
+ &dwBufferSize);
+
+ if (lWin32Status != ERROR_SUCCESS) goto BNT_BAILOUT;
+
+ dwCounterSize = dwBufferSize;
+
+ // get size of help text
+
+ dwBufferSize = 0;
+ lWin32Status = RegQueryValueEx (
+ *hKeyNames,
+ Help,
+ RESERVED,
+ &dwValueType,
+ NULL,
+ &dwBufferSize);
+
+ if (lWin32Status != ERROR_SUCCESS) goto BNT_BAILOUT;
+
+ dwHelpSize = dwBufferSize;
+
+ // allocate buffer with room for pointer array, counter name
+ // strings and help name strings
+
+ lpReturnValue = malloc (dwArraySize + dwCounterSize + dwHelpSize);
+
+ if (!lpReturnValue) {
+ lWin32Status = ERROR_OUTOFMEMORY;
+ goto BNT_BAILOUT;
+ }
+
+ // initialize buffer
+
+ memset (lpReturnValue, 0, _msize(lpReturnValue));
+
+ // initialize pointers into buffer
+
+ lpCounterId = lpReturnValue;
+ lpCounterNames = (LPTSTR)((LPBYTE)lpCounterId + dwArraySize);
+ lpHelpText = (LPTSTR)((LPBYTE)lpCounterNames + dwCounterSize);
+
+ // read counter names into buffer. Counter names will be stored as
+ // a MULTI_SZ string in the format of "###" "Name"
+
+ dwBufferSize = dwCounterSize;
+ lWin32Status = RegQueryValueEx (
+ *hKeyNames,
+ Counters,
+ RESERVED,
+ &dwValueType,
+ (LPVOID)lpCounterNames,
+ &dwBufferSize);
+
+ if (lWin32Status != ERROR_SUCCESS) goto BNT_BAILOUT;
+
+ // read explain text into buffer. Counter names will be stored as
+ // a MULTI_SZ string in the format of "###" "Text..."
+
+ dwBufferSize = dwHelpSize;
+ lWin32Status = RegQueryValueEx (
+ *hKeyNames,
+ Help,
+ RESERVED,
+ &dwValueType,
+ (LPVOID)lpHelpText,
+ &dwBufferSize);
+
+ if (lWin32Status != ERROR_SUCCESS) goto BNT_BAILOUT;
+
+ // load counter array items, by locating each text string
+ // in the returned buffer and loading the
+ // address of it in the corresponding pointer array element.
+
+ for (lpThisName = lpCounterNames;
+ *lpThisName;
+ lpThisName += (lstrlen(lpThisName)+1) ) {
+
+ // first string should be an integer (in decimal digit characters)
+ // so translate to an integer for use in array element identification
+
+ bStatus = StringToInt (lpThisName, &dwThisCounter);
+
+ if (!bStatus) {
+ // error is in GetLastError
+ goto BNT_BAILOUT; // bad entry
+ }
+
+ // point to corresponding counter name which follows the id number
+ // string.
+
+ lpThisName += (lstrlen(lpThisName)+1);
+
+ // and load array element with pointer to string
+
+ lpCounterId[dwThisCounter] = lpThisName;
+
+ }
+
+ // repeat the above for the explain text strings
+
+ for (lpThisName = lpHelpText;
+ *lpThisName;
+ lpThisName += (lstrlen(lpThisName)+1) ) {
+
+ // first string should be an integer (in decimal unicode digits)
+
+ bStatus = StringToInt (lpThisName, &dwThisCounter);
+
+ if (!bStatus) {
+ // error is in GetLastError
+ goto BNT_BAILOUT; // bad entry
+ }
+
+ // point to corresponding counter name
+
+ lpThisName += (lstrlen(lpThisName)+1);
+
+ // and load array element;
+
+ lpCounterId[dwThisCounter] = lpThisName;
+
+ }
+
+ // if the last item arugment was used, then load the last ID value in it
+
+ if (pdwLastItem) *pdwLastItem = dwLastId;
+
+ // free the temporary buffer used
+
+ if (lpValueNameString) {
+ free ((LPVOID)lpValueNameString);
+ }
+
+ // exit returning the pointer to the buffer
+
+ return lpReturnValue;
+
+BNT_BAILOUT:
+ if (lWin32Status != ERROR_SUCCESS) {
+ // if lWin32Status has error, then set last error value to it,
+ // otherwise assume that last error already has value in it
+ SetLastError (lWin32Status);
+ }
+
+ // free buffers used by this routine
+
+ if (lpValueNameString) {
+ free ((LPVOID)lpValueNameString);
+ }
+
+ if (lpReturnValue) {
+ free ((LPVOID)lpReturnValue);
+ }
+
+ return NULL;
+} // BuildNameTable
+
+
+BOOL
+GetDriverFromCommandLine (
+ HKEY hKeyMachine,
+ LPTSTR *lpDriverName,
+ HKEY *hDriverPerf,
+ LPSTR argv[]
+)
+/*++
+
+GetDriverFromCommandLine
+
+ locates the first argument in the command line string (after the
+ image name) and checks to see if
+
+ a) it's there
+
+ b) it's the name of a device driver listed in the
+ Registry\Machine\System\CurrentControlSet\Services key
+ in the registry and it has a "Performance" subkey
+
+ c) that the "First Counter" value under the Performance subkey
+ is defined.
+
+ if all these criteria are true, then the routine returns TRUE and
+ passes the pointer to the driver name back in the argument. If any
+ one of them fail, then NULL is returned in the DriverName arg and
+ the routine returns FALSE
+
+Arguments
+
+ lpDriverName
+
+ the address of a LPTSTR to recive the pointer to the driver name
+
+ hDriverPerf
+
+ the key to the driver's performance subkey
+
+Return Value
+
+ TRUE if a valid driver was found in the command line
+
+ FALSE if not (see above)
+
+--*/
+{
+ LPTSTR lpDriverKey; // buffer to build driver key name in
+ LPTSTR lpThisChar;
+
+ LONG lStatus;
+ DWORD dwFirstCounter;
+ DWORD dwSize;
+ DWORD dwType;
+
+ if (!lpDriverName || !hDriverPerf) {
+ SetLastError (ERROR_BAD_ARGUMENTS);
+ return FALSE;
+ }
+
+ *lpDriverName = NULL; // initialize to NULL
+ *hDriverPerf = NULL;
+
+ lpThisChar = malloc( MAX_PATH * sizeof(TCHAR));
+ MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, argv[0], -1, lpThisChar, MAX_PATH);
+
+ if (*lpThisChar) {
+ // an argument was found so see if it's a driver
+ lpDriverKey = malloc (MAX_PATH * sizeof (TCHAR));
+ if (!lpDriverKey) {
+ SetLastError (ERROR_OUTOFMEMORY);
+ if ( lpThisChar ) free (lpThisChar);
+ return FALSE;
+ }
+
+ lstrcpy (lpDriverKey, DriverPathRoot);
+ lstrcat (lpDriverKey, Slash);
+ lstrcat (lpDriverKey, lpThisChar);
+ lstrcat (lpDriverKey, Slash);
+ lstrcat (lpDriverKey, Performance);
+
+ lStatus = RegOpenKeyEx (
+ hKeyMachine,
+ lpDriverKey,
+ RESERVED,
+ KEY_READ | KEY_WRITE,
+ hDriverPerf);
+
+ if (lStatus == ERROR_SUCCESS) {
+ //
+ // this driver has a performance section so see if its
+ // counters are installed by checking the First Counter
+ // value key for a valid return. If it returns a value
+ // then chances are, it has some counters installed, if
+ // not, then display a message and quit.
+ //
+ free (lpDriverKey); // don't need this any more
+
+ dwType = 0;
+ dwSize = sizeof (dwFirstCounter);
+
+ lStatus = RegQueryValueEx (
+ *hDriverPerf,
+ FirstCounter,
+ RESERVED,
+ &dwType,
+ (LPBYTE)&dwFirstCounter,
+ &dwSize);
+
+ if (lStatus == ERROR_SUCCESS) {
+ // counter names are installed so return success
+ *lpDriverName = lpThisChar;
+ SetLastError (ERROR_SUCCESS);
+ if ( lpThisChar ) free (lpThisChar);
+ return TRUE;
+ } else {
+ SetLastError (ERROR_BADKEY);
+ if ( lpThisChar ) free (lpThisChar);
+ return FALSE;
+ }
+ } else { // key not found
+ SetLastError (lStatus);
+ free (lpDriverKey);
+ if ( lpThisChar ) free (lpThisChar);
+ return FALSE;
+ }
+ } else {
+ SetLastError (ERROR_INVALID_PARAMETER);
+ if ( lpThisChar ) free (lpThisChar);
+ return FALSE;
+ }
+}
+
+
+LONG
+FixNames (
+ HANDLE hKeyLang,
+ LPTSTR *lpOldNameTable,
+ IN LPTSTR lpszLangId, // unicode value of Language subkey
+ DWORD dwLastItem,
+ DWORD dwFirstNameToRemove,
+ DWORD dwLastNameToRemove
+ )
+{
+ LONG lStatus;
+ LPTSTR lpNameBuffer = NULL;
+ LPTSTR lpHelpBuffer = NULL;
+ DWORD dwTextIndex, dwSize;
+ LPTSTR lpNextHelpText;
+ LPTSTR lpNextNameText;
+
+ // allocate space for the array of new text it will point
+ // into the text buffer returned in the lpOldNameTable buffer)
+
+ lpNameBuffer = malloc (_msize(lpOldNameTable));
+ lpHelpBuffer = malloc (_msize(lpOldNameTable));
+
+ if (!lpNameBuffer || !lpHelpBuffer) {
+ lStatus = ERROR_OUTOFMEMORY;
+ return lStatus;
+ }
+
+ // remove this driver's counters from array
+
+ for (dwTextIndex = dwFirstNameToRemove;
+ dwTextIndex <= dwLastNameToRemove;
+ dwTextIndex++) {
+
+ if (dwTextIndex > dwLastItem)
+ break;
+
+ lpOldNameTable[dwTextIndex] = NULL;
+ }
+
+ lpNextHelpText = lpHelpBuffer;
+ lpNextNameText = lpNameBuffer;
+
+ // build new Multi_SZ strings from New Table
+
+ for (dwTextIndex = 0; dwTextIndex <= dwLastItem; dwTextIndex++){
+ if (lpOldNameTable[dwTextIndex]) {
+ // if there's a text string at that index, then ...
+ if (dwTextIndex & 0x1) { // ODD number == Help Text
+ lpNextHelpText +=
+ _stprintf (lpNextHelpText, TEXT("%d"), dwTextIndex) + 1;
+ lpNextHelpText +=
+ _stprintf (lpNextHelpText, TEXT("%s"),
+ lpOldNameTable[dwTextIndex]) + 1;
+ if (dwTextIndex > dwLastHelp){
+ dwLastHelp = dwTextIndex;
+ }
+ } else { // EVEN number == counter name text
+ lpNextNameText +=
+ _stprintf (lpNextNameText, TEXT("%d"), dwTextIndex) + 1;
+ lpNextNameText +=
+ _stprintf (lpNextNameText, TEXT("%s"),
+ lpOldNameTable[dwTextIndex]) + 1;
+ if (dwTextIndex > dwLastCounter){
+ dwLastCounter = dwTextIndex;
+ }
+ }
+ }
+ } // for dwTextIndex
+
+ // add MULTI_SZ terminating NULL
+ *lpNextNameText++ = TEXT ('\0');
+ *lpNextHelpText++ = TEXT ('\0');
+
+ // update counter name text buffer
+
+ dwSize = (DWORD)((LPBYTE)lpNextNameText - (LPBYTE)lpNameBuffer);
+ lStatus = RegSetValueEx (
+ hKeyLang,
+ Counters,
+ RESERVED,
+ REG_MULTI_SZ,
+ (LPBYTE)lpNameBuffer,
+ dwSize);
+
+ if (lStatus != ERROR_SUCCESS) {
+// printf (GetFormatResource(UC_UNABLELOADLANG),
+// Counters, lpLangName, lStatus);
+ goto UCN_FinishLang;
+ }
+
+ dwSize = (DWORD)((LPBYTE)lpNextHelpText - (LPBYTE)lpHelpBuffer);
+ lStatus = RegSetValueEx (
+ hKeyLang,
+ Help,
+ RESERVED,
+ REG_MULTI_SZ,
+ (LPBYTE)lpHelpBuffer,
+ dwSize);
+
+ if (lStatus != ERROR_SUCCESS) {
+// printf (GetFormatResource(UC_UNABLELOADLANG),
+// Help, lpLangName, lStatus);
+ goto UCN_FinishLang;
+ }
+
+
+UCN_FinishLang:
+
+ free (lpNameBuffer);
+ free (lpHelpBuffer);
+ free (lpOldNameTable);
+
+ RegCloseKey (hKeyLang);
+
+ return lStatus;
+}
+
+LONG
+UnloadCounterNames (
+ HKEY hKeyMachine,
+ HKEY hDriverPerf,
+ LPTSTR lpDriverName
+)
+/*++
+
+UnloadCounterNames
+
+ removes the names and explain text for the driver referenced by
+ hDriverPerf and updates the first and last counter values accordingly
+
+ update process:
+
+ - set "updating" flag under Perflib to name of driver being modified
+ - FOR each language under perflib key
+ -- load current counter names and explain text into array of
+ pointers
+ -- look at all drivers and copy their names and text into a new
+ buffer adjusting for the removed counter's entries keeping
+ track of the lowest entry copied. (the names for the driver
+ to be removed will not be copied, of course)
+ -- update each driver's "first" and "last" index values
+ -- copy all other entries from 0 to the lowest copied (i.e. the
+ system counters)
+ -- build a new MULIT_SZ string of help text and counter names
+ -- load new strings into registry
+ - update perflibl "last" counters
+ - delete updating flag
+
+ ******************************************************
+ * *
+ * NOTE: FUNDAMENTAL ASSUMPTION..... *
+ * *
+ * this routine assumes that: *
+ * *
+ * ALL COUNTER NAMES are even numbered and *
+ * ALL HELP TEXT STRINGS are odd numbered *
+ * *
+ ******************************************************
+
+Arguments
+
+ hKeyMachine
+
+ handle to HKEY_LOCAL_MACHINE node of registry on system to
+ remove counters from
+
+ hDrivefPerf
+ handle to registry key of driver to be de-installed
+
+ lpDriverName
+ name of driver being de-installed
+
+Return Value
+
+ DOS Error code.
+
+ ERROR_SUCCESS if all went OK
+ error value if not.
+
+--*/
+{
+
+ HKEY hPerflib;
+ HKEY hServices;
+ HKEY hKeyLang;
+
+ LONG lStatus;
+
+ DWORD dwLangIndex;
+ DWORD dwSize;
+ DWORD dwType;
+ DWORD dwLastItem;
+
+
+ DWORD dwRemLastDriverCounter;
+ DWORD dwRemFirstDriverCounter;
+ DWORD dwRemLastDriverHelp;
+ DWORD dwRemFirstDriverHelp;
+
+ DWORD dwFirstNameToRemove;
+ DWORD dwLastNameToRemove;
+
+ LPTSTR *lpOldNameTable;
+
+ LPTSTR lpLangName = NULL;
+ LPTSTR lpThisDriver = NULL;
+
+ BOOL bPerflibUpdated = FALSE;
+ BOOL bDriversShuffled = FALSE;
+
+ DWORD dwBufferSize; // size of total buffer in bytes
+
+ TCHAR CounterNameBuffer [40];
+ TCHAR HelpNameBuffer [40];
+
+ lStatus = RegOpenKeyEx (
+ hKeyMachine,
+ DriverPathRoot,
+ RESERVED,
+ KEY_READ | KEY_WRITE,
+ &hServices);
+
+ if (lStatus != ERROR_SUCCESS) {
+ return lStatus;
+ }
+
+ // open registry handle to perflib key
+
+ lStatus = RegOpenKeyEx (
+ hKeyMachine,
+ NamesKey,
+ RESERVED,
+ KEY_READ | KEY_WRITE,
+ &hPerflib);
+
+ if (lStatus != ERROR_SUCCESS) {
+ return lStatus;
+ }
+
+ // check & set Busy flag...
+
+ lStatus = RegQueryValueEx (
+ hPerflib,
+ Busy,
+ RESERVED,
+ &dwType,
+ NULL,
+ &dwSize);
+
+ if (lStatus == ERROR_SUCCESS) { // perflib is in use at the moment
+ return ERROR_BUSY;
+ }
+
+
+ lStatus = RegSetValueEx (
+ hPerflib,
+ Busy,
+ RESERVED,
+ REG_SZ,
+ (LPBYTE)lpDriverName,
+ lstrlen(lpDriverName) * sizeof(TCHAR));
+
+ if (lStatus != ERROR_SUCCESS) {
+ RegCloseKey (hPerflib);
+ return lStatus;
+ }
+
+ // query registry to get number of Explain text items
+
+ dwBufferSize = sizeof (dwHelpItems);
+ lStatus = RegQueryValueEx (
+ hPerflib,
+ LastHelp,
+ RESERVED,
+ &dwType,
+ (LPBYTE)&dwHelpItems,
+ &dwBufferSize);
+
+ if ((lStatus != ERROR_SUCCESS) || (dwType != REG_DWORD)) {
+ RegCloseKey (hPerflib);
+ return lStatus;
+ }
+
+ // query registry to get number of counter and object name items
+
+ dwBufferSize = sizeof (dwCounterItems);
+ lStatus = RegQueryValueEx (
+ hPerflib,
+ LastCounter,
+ RESERVED,
+ &dwType,
+ (LPBYTE)&dwCounterItems,
+ &dwBufferSize);
+
+ if ((lStatus != ERROR_SUCCESS) || (dwType != REG_DWORD)) {
+ RegCloseKey (hPerflib);
+ return lStatus;
+ }
+
+ // query registry to get PerfLib system version
+
+ dwBufferSize = sizeof (dwSystemVersion);
+ lStatus = RegQueryValueEx (
+ hPerflib,
+ VersionStr,
+ RESERVED,
+ &dwType,
+ (LPBYTE)&dwSystemVersion,
+ &dwBufferSize);
+
+ if ((lStatus != ERROR_SUCCESS) || (dwType != REG_DWORD)) {
+ // Key not there, must be NT 1.0 version
+ dwSystemVersion = OLD_VERSION;
+ }
+
+ if ( dwSystemVersion != OLD_VERSION )
+ {
+ // ERROR. The caller should check the version before calling me.
+ // let return busy for now...
+ return(ERROR_BUSY);
+ }
+
+
+ // allocate temporary String buffer
+
+ lpLangName = malloc (MAX_PATH * sizeof(TCHAR));
+ lpThisDriver = malloc (MAX_PATH * sizeof(TCHAR));
+
+ if (!lpLangName || !lpThisDriver) {
+ lStatus = ERROR_OUTOFMEMORY;
+ goto UCN_ExitPoint;
+ }
+
+ // Get the values that are in use by the driver to be removed
+
+ dwSize = sizeof (dwRemLastDriverCounter);
+ lStatus = RegQueryValueEx (
+ hDriverPerf,
+ LastCounter,
+ RESERVED,
+ &dwType,
+ (LPBYTE)&dwRemLastDriverCounter,
+ &dwSize);
+
+ if (lStatus != ERROR_SUCCESS) {
+ goto UCN_ExitPoint;
+ }
+
+ dwSize = sizeof (dwRemFirstDriverCounter);
+ lStatus = RegQueryValueEx (
+ hDriverPerf,
+ FirstCounter,
+ RESERVED,
+ &dwType,
+ (LPBYTE)&dwRemFirstDriverCounter,
+ &dwSize);
+
+ if (lStatus != ERROR_SUCCESS) {
+ goto UCN_ExitPoint;
+ }
+
+ dwSize = sizeof (dwRemLastDriverHelp);
+ lStatus = RegQueryValueEx (
+ hDriverPerf,
+ LastHelp,
+ RESERVED,
+ &dwType,
+ (LPBYTE)&dwRemLastDriverHelp,
+ &dwSize);
+
+ if (lStatus != ERROR_SUCCESS) {
+ goto UCN_ExitPoint;
+ }
+
+ dwSize = sizeof (dwRemFirstDriverHelp);
+ lStatus = RegQueryValueEx (
+ hDriverPerf,
+ FirstHelp,
+ RESERVED,
+ &dwType,
+ (LPBYTE)&dwRemFirstDriverHelp,
+ &dwSize);
+
+ if (lStatus != ERROR_SUCCESS) {
+ goto UCN_ExitPoint;
+ }
+
+ // get the first and last counters to define block of names used
+ // by this device
+
+ dwFirstNameToRemove = (dwRemFirstDriverCounter <= dwRemFirstDriverHelp ?
+ dwRemFirstDriverCounter : dwRemFirstDriverHelp);
+
+ dwLastNameToRemove = (dwRemLastDriverCounter >= dwRemLastDriverHelp ?
+ dwRemLastDriverCounter : dwRemLastDriverHelp);
+
+ dwLastCounter = dwLastHelp = 0;
+
+ // do each language under perflib
+ for (dwLangIndex = 0, dwSize = _msize(lpLangName);
+ (RegEnumKey(hPerflib, dwLangIndex, lpLangName, dwSize)) == ERROR_SUCCESS;
+ dwLangIndex++, dwSize = _msize(lpLangName)) {
+
+ lpOldNameTable = BuildNameTable (hPerflib, lpLangName,
+ &dwLastItem, &hKeyLang, CounterNameBuffer, HelpNameBuffer);
+
+ if (lpOldNameTable) {
+ if (!FixNames (
+ hKeyLang,
+ lpOldNameTable,
+ lpLangName,
+ dwLastItem,
+ dwFirstNameToRemove,
+ dwLastNameToRemove)) {
+ bPerflibUpdated = TRUE;
+ }
+ } else { // unable to unload names for this language
+ // display error message
+ }
+ } // end for (more languages)
+
+ if (bPerflibUpdated) {
+ // update perflib's "last" values
+
+ dwSize = sizeof (dwLastCounter);
+ lStatus = RegSetValueEx (
+ hPerflib,
+ LastCounter,
+ RESERVED,
+ REG_DWORD,
+ (LPBYTE)&dwLastCounter,
+ dwSize);
+
+ dwSize = sizeof (dwLastHelp);
+ lStatus = RegSetValueEx (
+ hPerflib,
+ LastHelp,
+ RESERVED,
+ REG_DWORD,
+ (LPBYTE)&dwLastHelp,
+ dwSize);
+
+ // update "driver"s values (i.e. remove them)
+
+ RegDeleteValue (hDriverPerf, FirstCounter);
+ RegDeleteValue (hDriverPerf, LastCounter);
+ RegDeleteValue (hDriverPerf, FirstHelp);
+ RegDeleteValue (hDriverPerf, LastHelp);
+
+ }
+
+UCN_ExitPoint:
+ RegDeleteValue (hPerflib, Busy);
+ RegCloseKey (hPerflib);
+ RegCloseKey (hServices);
+ if (lpLangName) free (lpLangName);
+ if (lpThisDriver) free (lpThisDriver);
+
+ return lStatus;
+
+
+}
+
+BOOL FAR PASCAL unlodctr(DWORD argc,LPSTR argv[], LPSTR *ppszResult )
+/*++
+
+main
+
+ entry point to Counter Name Unloader
+
+
+
+Arguments
+
+ argc
+ # of command line arguments present
+
+ argv
+ array of pointers to command line strings
+
+ (note that these are obtained from the GetCommandLine function in
+ order to work with both UNICODE and ANSI strings.)
+
+ReturnValue
+
+ 0 (ERROR_SUCCESS) if command was processed
+ Non-Zero if command error was detected.
+
+--*/
+{
+ LPTSTR lpDriverName; // name of driver to delete from perflib
+ HKEY hDriverPerf; // handle to performance sub-key of driver
+
+
+ DWORD dwStatus; // return status of fn. calls
+
+ *ppszResult = achBuff;
+
+ wsprintfA( achBuff, "{\"NO_ERROR\"}");
+
+ if (!GetDriverFromCommandLine (
+ HKEY_LOCAL_MACHINE, &lpDriverName, &hDriverPerf, argv)) {
+ // error message was printed in routine if there was an error
+ wsprintfA( achBuff,"{\"ERROR\"}");
+ return (FALSE);
+ }
+
+ // removes names and explain text for driver in lpDriverName
+ // displays error messages for errors encountered
+
+ dwStatus = (DWORD)UnloadCounterNames (HKEY_LOCAL_MACHINE,
+ hDriverPerf, lpDriverName);
+
+ RegCloseKey (hDriverPerf);
+
+ if ( dwStatus != ERROR_SUCCESS )
+ {
+ wsprintfA( achBuff,"{\"ERROR\"}");
+ }
+
+ return (dwStatus==ERROR_SUCCESS);
+
+}
diff --git a/private/nw/install/wksta/alpha.txt b/private/nw/install/wksta/alpha.txt
new file mode 100644
index 000000000..a2e5c37d9
--- /dev/null
+++ b/private/nw/install/wksta/alpha.txt
@@ -0,0 +1,3 @@
+[ProductType]
+STF_PRODUCT = Winnt
+STF_PLATFORM = ALPHA
diff --git a/private/nw/install/wksta/eng.txt b/private/nw/install/wksta/eng.txt
new file mode 100644
index 000000000..0171debb2
--- /dev/null
+++ b/private/nw/install/wksta/eng.txt
@@ -0,0 +1,55 @@
+;
+; Language depended section
+;
+[OptionsWINNTTextENG]
+ NWWKSTA = "Microsoft Client Service for NetWare"
+[OptionsLANMANNTTextENG]
+ NWWKSTA = "Microsoft Gateway Service for NetWare"
+
+[WorkstationENG]
+ NWType = "Client Service for NetWare"
+[GatewayENG]
+ NWType = "Gateway Service for NetWare"
+
+[FileConstantsENG]
+NotEnoughSpace = "Out of disk space. Please remove some files before install "$(NWType)"."
+InstallNWLINKFirst = "Please install the NWLINK IPX/SPX Compatible Transport before you install the "$(NWType)"."
+InstallNTWKSTAFirst = "Please install the Windows NT Workstation service before you install the "$(NWType)"."
+RemoveAndReboot = "Please remove any existing NetWare Workstation component and reboot your machine before installing the latest version of "$(NWType)"."
+UpgradeFirst = "Please install the Windows NT patch before installing the "$(NWType)"."
+NotDaytona = "This version of "$(NWType)" does not support Windows NT 3.5."
+ProCaption = "Windows NT Setup"
+ProCancel = "Cancel"
+ProCancelMsg = "Windows NT Networking is not correctly installed. "+
+ "Are you sure you want to cancel copying files?"
+ProCancelCap = "Network Setup Message"
+ProText1 = "Copying:"
+ProText2 = "To:"
+FunctionTitle = "Workstation Services Setup"
+ProductNWWKSTADescription = $(NWType)
+ProductNWRDRDescription = "Redirector for NetWare"
+ProviderRDRName = "Redirector for NetWare"
+ProductNWWKSTATitle = $(NWType)
+ProductNWWKSTADisplayName = $(NWType)
+ProductNWRDRDisplayName = "Rdr for NetWare"
+ProductNWRDRTitle = "Redirector for NetWare"
+ProductProviderName = "NetWare Network"
+OldPrintProviderName = "NetWare(R) Network"
+
+[DialogConstantsENG]
+Help = "&Help"
+Exit = "Cancel"
+OK = "OK"
+HelpContext = ""
+Continue = "Continue"
+Cancel = "Cancel"
+ShellCodeErrorText = "Shell Code Error"
+
+[FileDependentDlgENG]
+; nothing
+
+[Source Media Descriptions]
+ 1 = "Client/Gateway Service for NetWare Disk #1" , TAGFILE = nwc.2a
+ 2 = "Client/Gateway Service for NetWare Disk #2" , TAGFILE = nwc.2b
+ 3 = ""
+
diff --git a/private/nw/install/wksta/files10.txt b/private/nw/install/wksta/files10.txt
new file mode 100644
index 000000000..1ce384d20
--- /dev/null
+++ b/private/nw/install/wksta/files10.txt
@@ -0,0 +1,94 @@
+[RemoveBackupFiles]
+ LibraryProcedure Status1, $(!LIBHANDLE), DelFile, $(!STF_WINDOWSSYSPATH)"\nethlp.nwc"
+ LibraryProcedure Status1, $(!LIBHANDLE), DelFile, $(!STF_WINDOWSSYSPATH)"\net.nwc"
+ LibraryProcedure Status1, $(!LIBHANDLE), DelFile, $(!STF_WINDOWSSYSPATH)"\netmsg.nwc"
+ LibraryProcedure Status1, $(!LIBHANDLE), DelFile, $(!STF_WINDOWSSYSPATH)"\srvsvc.nwc"
+ LibraryProcedure Status1, $(!LIBHANDLE), DelFile, $(!STF_WINDOWSSYSPATH)"\ncpa.nwc"
+ LibraryProcedure Status1, $(!LIBHANDLE), DelFile, $(!STF_WINDOWSSYSPATH)"\wfwnet.nwc"
+ LibraryProcedure Status1, $(!LIBHANDLE), DelFile, $(!STF_WINDOWSSYSPATH)"\vdmredir.nwc"
+ LibraryProcedure Status1, $(!LIBHANDLE), DelFile, $(!STF_WINDOWSSYSPATH)"\ipxroute.nwc"
+ LibraryProcedure Status1, $(!LIBHANDLE), DelFile, $(!STF_WINDOWSSYSPATH)"\nwlnkmsg.nwc"
+ LibraryProcedure Status1, $(!LIBHANDLE), DelFile, $(!STF_WINDOWSSYSPATH)"\nwnblink.nwc"
+ LibraryProcedure Status1, $(!LIBHANDLE), DelFile, $(!STF_WINDOWSSYSPATH)"\wshnwlnk.nwc"
+ LibraryProcedure Status1, $(!LIBHANDLE), DelFile, $(!STF_WINDOWSSYSPATH)"\nwlnkcfg.nwc"
+ LibraryProcedure Status1, $(!LIBHANDLE), DelFile, $(!STF_WINDOWSSYSPATH)"\nwlnksvc.nwc"
+ LibraryProcedure Status1, $(!LIBHANDLE), DelFile, $(!STF_WINDOWSSYSPATH)"\nwnbexe.nwc"
+ LibraryProcedure Status1, $(!LIBHANDLE), DelFile, $(!STF_WINDOWSSYSPATH)"\drivers\srv.nwc"
+ LibraryProcedure Status1, $(!LIBHANDLE), DelFile, $(!STF_WINDOWSSYSPATH)"\drivers\nwlink.nwc"
+ LibraryProcedure Status1, $(!LIBHANDLE), DelFile, $(!STF_WINDOWSSYSPATH)"\drivers\nwnblink.nwc"
+ return
+
+[Restore-Oldsys]
+3,srv.nwc , SIZE=0, RENAME=srv.sys
+3,nwlink.nwc , SIZE=0, RENAME=nwlink.sys
+3,nwnblink.nwc, SIZE=0, RENAME=nwnblink.sys
+
+[Restore-Oldexe]
+3,nethlp.nwc , SIZE=0, RENAME=net.hlp
+3,net.nwc , SIZE=0, RENAME=net.exe
+3,netmsg.nwc , SIZE=0, RENAME=netmsg.dll
+3,srvsvc.nwc , SIZE=0, RENAME=srvsvc.dll
+3,ncpa.nwc , SIZE=0, RENAME=ncpa.cpl
+3,wfwnet.nwc , SIZE=0, RENAME=wfwnet.drv
+3,vdmredir.nwc, SIZE=0, RENAME=vdmredir.dll
+
+3,ipxroute.nwc, SIZE=0, RENAME=ipxroute.exe
+3,nwlnkmsg.nwc, SIZE=0, RENAME=nwlnkmsg.dll
+3,nwnblink.nwc, SIZE=0, RENAME=nwnblink.dll
+3,wshnwlnk.nwc, SIZE=0, RENAME=wshnwlnk.dll
+3,nwlnkcfg.nwc, SIZE=0, RENAME=nwlnkcfg.dll
+3,nwlnksvc.nwc, SIZE=0, RENAME=nwlnksvc.exe
+3,nwnbexe.nwc , SIZE=0, RENAME=nwnblink.exe
+
+[Files-Drivers]
+1,nwrdr.sys, SIZE=999
+1,srv.sys, SIZE=999, BACKUP=srv.nwc
+1,nwlink.sys, SIZE=999, BACKUP=nwlink.nwc
+1,nwnblink.sys, SIZE=999, BACKUP=nwnblink.nwc
+
+[Files-nwcfgdll]
+1,nwcfg.dll, SIZE=999
+
+[Files-NWWKSTA]
+2,nwsvc.exe, SIZE=999
+2,nwevent.dll , SIZE=999
+2,nwwks.dll , SIZE=999
+2,nwprovau.dll, SIZE=999
+2,nwapi32.dll, SIZE=999
+2,nw16.exe, SIZE=999
+2,netware.drv, SIZE=999
+2,nwc.cpl, SIZE=999
+2,vwipxspx.dll, SIZE=999
+2,vwipxspx.exe, SIZE=999
+2,nwapi16.dll, SIZE=999
+;
+2,net.hlp, SIZE=999, BACKUP=nethlp.nwc
+2,net.exe, SIZE=999, BACKUP=net.nwc
+2,net1.exe, SIZE=999
+2,netmsg.dll, SIZE=999, BACKUP=netmsg.nwc
+2,srvsvc.dll, SIZE=999, BACKUP=srvsvc.nwc
+2,ncpa.cpl, SIZE=999, BACKUP=ncpa.nwc
+2,wfwnet.drv, SIZE=999, BACKUP=wfwnet.nwc
+2,vdmredir.dll, SIZE=999, BACKUP=vdmredir.nwc
+;
+2,ipxroute.exe, SIZE=999, BACKUP=ipxroute.nwc
+2,nwlnkmsg.dll, SIZE=999, BACKUP=nwlnkmsg.nwc
+2,nwnblink.dll, SIZE=999, BACKUP=nwnblink.nwc
+2,wshnwlnk.dll, SIZE=999, BACKUP=wshnwlnk.nwc
+2,nwlnkcfg.dll, SIZE=999, BACKUP=nwlnkcfg.nwc
+2,nwlnksvc.exe, SIZE=999, BACKUP=nwlnksvc.nwc
+2,nwnblink.exe, SIZE=999, BACKUP=nwnbexe.nwc
+;
+; Performance Monitor
+;
+2,nwperf.ini, SIZE=999
+2,perfnw.dll, SIZE=999
+2,nwperfm.h, SIZE=999
+
+[Files-NTASHelp]
+1,nwdocgw.hlp, SIZE=999, RENAME=nwdoc.hlp
+
+[Files-WINNTHelp]
+1,nwdoc.hlp, SIZE=999
+
+
diff --git a/private/nw/install/wksta/files10a.txt b/private/nw/install/wksta/files10a.txt
new file mode 100644
index 000000000..137bf041d
--- /dev/null
+++ b/private/nw/install/wksta/files10a.txt
@@ -0,0 +1,37 @@
+[RemoveBackupFiles]
+ return
+
+[Restore-Oldexe]
+
+[Restore-Oldsys]
+
+
+[Files-Drivers]
+1,nwrdr.sys, SIZE=999
+
+[Files-nwcfgdll]
+1,nwcfg.dll, SIZE=999
+
+[Files-NWWKSTA]
+1,nwsvc.exe, SIZE=999
+1,nwevent.dll , SIZE=999
+1,nwwks.dll , SIZE=999
+1,nwprovau.dll, SIZE=999
+1,nwapi32.dll, SIZE=999
+1,nw16.exe, SIZE=999
+1,nwc.cpl, SIZE=999
+1,vwipxspx.dll, SIZE=999
+1,vwipxspx.exe, SIZE=999
+1,nwapi16.dll, SIZE=999
+;
+; Performance Monitor
+;
+1,nwperf.ini, SIZE=999
+1,nwperfm.h, SIZE=999
+
+[Files-NTASHelp]
+1,nwdocgw.hlp, SIZE=999, RENAME=nwdoc.hlp
+
+[Files-WINNTHelp]
+1,nwdoc.hlp, SIZE=999
+
diff --git a/private/nw/install/wksta/i386.txt b/private/nw/install/wksta/i386.txt
new file mode 100644
index 000000000..6b9fce89b
--- /dev/null
+++ b/private/nw/install/wksta/i386.txt
@@ -0,0 +1,3 @@
+[ProductType]
+STF_PRODUCT = Winnt
+STF_PLATFORM = I386
diff --git a/private/nw/install/wksta/makefile b/private/nw/install/wksta/makefile
new file mode 100644
index 000000000..14f79b701
--- /dev/null
+++ b/private/nw/install/wksta/makefile
@@ -0,0 +1 @@
+!include $(NTMAKEENV)\makefile.def
diff --git a/private/nw/install/wksta/makefile.inc b/private/nw/install/wksta/makefile.inc
new file mode 100644
index 000000000..37d0d9c77
--- /dev/null
+++ b/private/nw/install/wksta/makefile.inc
@@ -0,0 +1,29 @@
+INFHEADER=$(TARGET_DIRECTORY).txt
+
+!IFNDEF INFLANGUAGE
+INFLANGUAGE=ENG
+!ENDIF
+
+all: oemnsvnw.inf
+
+make_inf: oemnsvnw.inf
+
+clean: cleansrc oemnsvnw.inf
+
+cleansrc:
+ del oemnsvnw.inf
+
+
+!IF "$(QFE_BUILD)" != "1"
+
+oemnsvnw.inf: $(INFHEADER) nw.inf $(INFLANGUAGE).txt files10a.txt
+ copy $(INFHEADER)+$(INFLANGUAGE).txt+nw.inf+files10a.txt oemnsvnw.inf
+ binplace oemnsvnw.inf
+
+!ELSE
+
+oemnsvnw.inf: $(INFHEADER) nw.inf $(INFLANGUAGE).txt files10.txt
+ copy $(INFHEADER)+$(INFLANGUAGE).txt+nw.inf+files10.txt oemnsvnw.inf
+ binplace oemnsvnw.inf
+
+!ENDIF
diff --git a/private/nw/install/wksta/mips.txt b/private/nw/install/wksta/mips.txt
new file mode 100644
index 000000000..8101c7cb2
--- /dev/null
+++ b/private/nw/install/wksta/mips.txt
@@ -0,0 +1,3 @@
+[ProductType]
+STF_PRODUCT = Winnt
+STF_PLATFORM = MIPS
diff --git a/private/nw/install/wksta/nw.inf b/private/nw/install/wksta/nw.inf
new file mode 100644
index 000000000..49491ced5
--- /dev/null
+++ b/private/nw/install/wksta/nw.inf
@@ -0,0 +1,1224 @@
+[Identification]
+ OptionType = NetService
+[LanguagesSupported]
+ ENG
+[Options]
+ NWWKSTA
+[FileConstants]
+UtilityInf = "UTILITY.INF"
+subroutineinf = "SUBROUTN.INF"
+SoftwareType = "service"
+Exit_Code = 0
+NetwareEventDLL = "%SystemRoot%\System32\nwevent.dll"
+Manufacturer = "Microsoft"
+ProductMajorVersion = "3"
+ProductMinorVersion = "1"
+ProductVersion = $(ProductMajorVersion)"."$(ProductMinorVersion)
+ProductNWWKSTAName = "NWCWorkstation"
+ProductNWWKSTAImagePath = "%SystemRoot%\System32\nwsvc.exe"
+NetRuleNWWKSTAType = "nwWsta nwcWorkstation"
+NetRuleNWWKSTAClass = {"nwcWorkstation basic"}
+NetRuleNWWKSTABindable = {"nwcWorkstation nwlinkipxTransport non non 100"}
+NetRuleNWWKSTAUse = $(SoftwareType)" no no"
+NetRuleNWWKSTABindForm = """NwcWorkstation"" yes yes container"
+ProductNWRDRName = "NwRdr"
+ProductNWRDRImagePath = "\SystemRoot\System32\drivers\nwrdr.sys"
+ProductProviderImagePath = "%SystemRoot%\System32\nwprovau.dll"
+ProviderName = $(ProductNWWKSTAName)
+ProductKeyName = $(!NTN_SoftwareBase)"\"$(Manufacturer)"\"$(Product$(Option)Name)"\CurrentVersion"
+ParamKeyName = $(!NTN_ServiceBase)"\"$(Product$(Option)Name)"\Parameters"
+LSAKeyName = "System\CurrentControlSet\Control\LSA"
+[GeneralConstants]
+from = ""
+to = ""
+ExitCodeOk = 0
+ExitCodeCancel = 1
+ExitCodeFatal = 2
+KeyNull = ""
+MAXIMUM_ALLOWED = 33554432
+RegistryErrorIndex = NO_ERROR
+KeyProduct = ""
+KeyParameters = ""
+TRUE = 1
+FALSE = 0
+NoTitle = 0
+ExitState = "Active"
+OldVersionExisted = $(FALSE)
+DriverPath = $(!STF_NTPATH)\drivers
+[date]
+ Now = {} ? $(!LIBHANDLE) GetSystemDate
+
+[DetectDiskSpace]
+ VolumeList = {} ? $(!LIBHANDLE) GetHardDriveLetters
+ VolumeFreeList = {} ? $(!LIBHANDLE) GetHardDriveFreeSpace
+ VolumeFSList = {} ? $(!LIBHANDLE) GetHardDriveFileSystems
+
+[DetectSystemMemory]
+ SystemMemory = "" ? $(!LIBHANDLE) GetMemorySize
+
+[Identify]
+ read-syms Identification
+ set Status = STATUS_SUCCESSFUL
+ set Identifier = $(OptionType)
+ set Media = #("Source Media Descriptions", 1, 1)
+ Return $(Status) $(Identifier) $(Media)
+[ReturnOptions]
+ set Status = STATUS_FAILED
+ set OptionList = {}
+ set OptionTextList = {}
+ set LanguageList = ^(LanguagesSupported, 1)
+ Ifcontains(i) $($0) in $(LanguageList)
+ goto returnoptions
+ else
+ set Status = STATUS_NOLANGUAGE
+ goto finish_ReturnOptions
+ endif
+returnoptions = +
+ set OptionList = ^(Options, 1)
+ ifstr(i) $(!STF_PRODUCT) == "WINNT"
+ set OptionTextList = ^(OptionsWINNTText$($0), 1)
+ else
+ set OptionTextList = ^(OptionsLANMANNTText$($0), 1)
+ endif
+ set Status = STATUS_SUCCESSFUL
+finish_ReturnOptions = +
+ Return $(Status) $(OptionList) $(OptionTextList)
+[InstallOption]
+ set Option = $($1)
+ set SrcDir = $($2)
+ set AddCopy = $($3)
+ set DoCopy = $($4)
+ set DoConfig = $($5)
+ set LanguageList = ^(LanguagesSupported, 1)
+ Ifcontains(i) $($0) NOT-IN $(LanguageList)
+ Return STATUS_NOLANGUAGE
+ endif
+ Debug-Output "OEMNSVNW.INF: STF_CWDDIR is: "$(!STF_CWDDIR)
+ Debug-Output "OEMNSVNW.INF: STF_LANGUAGE is: "$(!STF_LANGUAGE)
+ set-subst LF = "\n"
+ set-subst CR = "\r"
+ ifstr(i) $(!STF_PRODUCT) == "WINNT"
+ read-syms Workstation$(!STF_LANGUAGE)
+ else
+ read-syms Gateway$(!STF_LANGUAGE)
+ endif
+ read-syms GeneralConstants
+ read-syms FileConstants
+ read-syms DialogConstants$(!STF_LANGUAGE)
+ ifstr(i) $(!NTN_Origination) == "NCPA"
+ set Continue = $(OK)
+ endif
+ read-syms FileConstants$(!STF_LANGUAGE)
+ detect date
+ detect DetectSystemMemory
+ detect DetectDiskSpace
+ set-title $(FunctionTitle)
+ set to = Begin
+ set from = Begin
+ set CommonStatus = STATUS_SUCCESSFUL
+ EndWait
+Begin = +
+ Ifstr(i) $(!NTN_InstallMode) == deinstall
+ set OEM_ABANDON_OPTIONS = { $(ProductNWWKSTAName), +
+ $(ProductNWRDRName)}
+ set StartLabel = removeadapter
+ else-Ifstr(i) $(!NTN_InstallMode) == Update
+ set StartLabel = UpgradeSoftware
+ else-Ifstr(i) $(!NTN_InstallMode) == bind
+ set StartLabel = bindingadapter
+ else-Ifstr(i) $(!NTN_InstallMode) == configure
+ Shell $(UtilityInf),RegistryErrorString,CANNOT_CONFIGURE_SOFTWARE
+ ifint $($ShellCode) != $(!SHELL_CODE_OK)
+ Debug-Output "OEMNSVNW.INF: ShellCode error: cannot get an error string."
+ goto ShellCodeError
+ endif
+ set Error = $($R0)
+ set from = end
+ set to = end
+ goto nonfatalinfo
+ else
+ set StartLabel = installadapter
+ set OEM_ABANDON_OPTIONS = {}
+ endif
+ set DoWKSTA = FALSE
+ set DoRDR = FALSE
+ set DoNWLINK = FALSE
+ set DoMUP = FALSE
+ Ifstr(i) $(Option) == NWWKSTA
+ set DoWKSTA = TRUE
+ set DoRDR = TRUE
+ set DoNWLINK = TRUE
+ set DoMUP = TRUE
+ Else-ifstr(i) $(Option) == RDR
+ Set DoRDR = TRUE
+ Else
+ Debug-Output "OEMNSVWK.INF: Unrecognized option"
+ Endif
+ set from = $(fatal)
+ set to = $(fatal)
+ goto $(StartLabel)
+installadapter = +
+ GetDriveInPath NtDrive $(!STF_WINDOWSSYSPATH)
+ Shell "" GetFilesSize
+ set RequiredSize = $($R0)
+ ForListDo $(VolumeList)
+ set CurrentVolume = *($(VolumeList), $(#))
+ set CurrentVolumeFree = *($(VolumeFreeList), $(#))
+ Ifstr(i) $(CurrentVolume) == $(NtDrive)
+ set-mul CurrentVolumeFree = $(CurrentVolumeFree) 1024
+ Ifint $(CurrentVolumeFree) < $(RequiredSize)
+ Shell $(subroutineinf) SetupMessage, $(!STF_LANGUAGE), "NONFATAL", $(NotEnoughSpace)
+ ifint $($ShellCode) != $(!SHELL_CODE_OK)
+ goto ShellCodeError
+ endif
+ goto end
+ else
+ goto AfterCheckSpace
+ endif
+ EndIf
+ EndForListDo
+AfterCheckSpace = +
+
+ OpenRegKey $(!REG_H_LOCAL) "" $(!NTN_ServiceBase)"\NWLINKIPX" $(MAXIMUM_ALLOWED) BS_KeyServices
+
+ Ifstr $(BS_KeyServices) == $(KeyNull)
+ Shell $(subroutineinf) SetupMessage, $(!STF_LANGUAGE), "NONFATAL", $(InstallNWLINKFirst)
+ ifint $($ShellCode) != $(!SHELL_CODE_OK)
+ goto ShellCodeError
+ endif
+ goto end
+ endif
+
+ OpenRegKey $(!REG_H_LOCAL) "" $(ProductKeyName) $(MAXIMUM_ALLOWED) KeyProduct
+ Ifstr $(KeyProduct) != $(KeyNull)
+ CloseRegKey $(KeyProduct)
+ Shell $(UtilityInf), VerExistedDlg, $(Product$(Option)Title),+
+ $(ProductVersion)
+ ifint $($ShellCode) != $(!SHELL_CODE_OK)
+ Debug-Output "OEMNSVNW.INF: ShellCode error: cannot get an error string."
+ goto ShellCodeError
+ endif
+ goto end
+ endif
+ CloseRegKey $(KeyProduct)
+
+ ;
+ ; Make sure Mup exists
+ ;
+ ifstr(i) $(DoMUP) == TRUE
+ ;
+ ; Check whether MUP exists or not.
+ ; If exists, continue.
+ ; Otherwise, popup a dialog and end
+ ;
+ Debug-Output "OEMNSVNW.INF: check MUP registry"
+
+ OpenRegKey $(!REG_H_LOCAL) "" $(!NTN_ServiceBase)"\Mup" $(MAXIMUM_ALLOWED) MupKey
+
+ Ifstr $(MupKey) == $(KeyNull)
+ ;
+ ; Mup does not exist. So, end NWCS setup.
+ ;
+ Shell $(subroutineinf) SetupMessage, $(!STF_LANGUAGE), "NONFATAL", $(InstallNTWKSTAFirst)
+ ifint $($ShellCode) != $(!SHELL_CODE_OK)
+ goto ShellCodeError
+ endif
+ goto end
+ endif
+
+ ;
+ ; It exists. So, continue.
+ ;
+ CloseRegKey $(MupKey)
+ endif
+
+ ;
+ ; Check to make sure that NETWAREWORKSTATION is not there
+ ;
+ OpenRegKey $(!REG_H_LOCAL) "" $(!NTN_ServiceBase)"\NetwareWorkstation" $(MAXIMUM_ALLOWED) BS_KeyServices
+
+ Ifstr $(BS_KeyServices) == $(KeyNull)
+ ;
+ ; Good. Let check the "Mark for deletion" service controller problem.
+ ;
+ Shell $(UtilityInf), CreateService, +
+ "NetwareWorkstation", +
+ $(ProductNWWKSTADisplayName), +
+ $(ProductNWWKSTAImagePath), "autoserviceshare", "NetworkProvider", {}, "",+
+ $(NetwareEventDLL)
+ ifint $($ShellCode) != $(!SHELL_CODE_OK)
+ Debug-Output "OEMNSVNW.INF: ShellCode error"
+ goto ShellCodeError
+ endif
+ debug-output $($R0)
+ set RegistryErrorIndex = $($R0)
+ ifstr(i) $(RegistryErrorIndex) != NO_ERROR
+ ;
+ ; it is still there
+ ;
+ Shell $(subroutineinf) SetupMessage, $(!STF_LANGUAGE), "NONFATAL", $(RemoveAndReboot)
+ ifint $($ShellCode) != $(!SHELL_CODE_OK)
+ goto ShellCodeError
+ endif
+ goto end
+ else
+ Shell $(UtilityInf) RemoveService "NetwareWorkstation" "YES"
+ endif
+ else
+ Shell $(subroutineinf) SetupMessage, $(!STF_LANGUAGE), "NONFATAL", $(RemoveAndReboot)
+ ifint $($ShellCode) != $(!SHELL_CODE_OK)
+ goto ShellCodeError
+ endif
+ CloseRegKey $(BS_KeyServices)
+ goto end
+ endif
+
+
+ goto nextstep
+nextstep = +
+ StartWait
+ Ifstr(i) $(!NTN_InstallMode) == install
+ Ifint $(OldVersionExisted) == $(FALSE)
+ goto installproduct
+ endif
+ endif
+ goto writeparameters
+installproduct = +
+ CloseRegKey $(ParamKeyName)
+ ifstr(i) $(!NTN_InstallMode) == "install"
+ Ifstr(i) $(DoCopy) == "YES"
+ Shell $(UtilityInf), DoAskSource, $(!STF_CWDDIR), $(SrcDir) YES
+ Ifint $($ShellCode) != $(!SHELL_CODE_OK)
+ Goto ShellCodeError
+ Else-Ifstr(i) $($R0) == STATUS_FAILED
+ Shell $(UtilityInf) RegistryErrorString "ASK_SOURCE_FAIL"
+ ifint $($ShellCode) != $(!SHELL_CODE_OK)
+ goto ShellCodeError
+ endif
+ set Error = $($R0)
+ Goto fatal
+ Else-Ifstr(i) $($R0) == STATUS_USERCANCEL
+ Goto successful
+ Endif
+ Set SrcDir = $($R1)
+ Endif
+ install "Install-nwcfgdll"
+ ifstr(i) $(STF_INSTALL_OUTCOME) != STF_SUCCESS
+ Shell $(UtilityInf) RegistryErrorString "UNABLE_COPY_FILE"
+ ifint $($ShellCode) != $(!SHELL_CODE_OK)
+ goto ShellCodeError
+ endif
+ set Error = $($R0)
+ goto fatal
+ endif
+ endif
+ set OEM_ABANDON_ON = TRUE
+ LoadLibrary "nw" $(!STF_CWDDIR)\nwcfg.dll !NWCFG_HANDLE
+ Set FLibraryErrCtl = 1
+ ;
+ ; Check the version number
+ ;
+ LibraryProcedure Result $(!NWCFG_HANDLE), GetKernelVersion
+ ifint *($(Result),3) < 528
+ Shell $(subroutineinf) SetupMessage, $(!STF_LANGUAGE), "NONFATAL", $(UpgradeFirst)
+ ifint $($ShellCode) != $(!SHELL_CODE_OK)
+ goto ShellCodeError
+ endif
+ goto end
+ FreeLibrary $(!NWCFG_HANDLE)
+ else-ifint *($(Result),3) > 529
+ Shell $(subroutineinf) SetupMessage, $(!STF_LANGUAGE), "NONFATAL", $(NotDaytona)
+ ifint $($ShellCode) != $(!SHELL_CODE_OK)
+ goto ShellCodeError
+ endif
+ goto end
+ FreeLibrary $(!NWCFG_HANDLE)
+ endif
+
+ ifstr(i) $(!NTN_InstallMode) == "install"
+ install "Install-Option"
+ ifstr(i) $(STF_INSTALL_OUTCOME) != STF_SUCCESS
+ Shell $(UtilityInf) RegistryErrorString "UNABLE_COPY_FILE"
+ ifint $($ShellCode) != $(!SHELL_CODE_OK)
+ goto ShellCodeError
+ endif
+ set Error = $($R0)
+ goto fatal
+ endif
+ endif
+
+ LibraryProcedure Result $(!NWCFG_HANDLE), SetFileSysChangeValue
+ LibraryProcedure Result $(!NWCFG_HANDLE), AddNetwarePrinterProvidor
+ Set FLibraryErrCtl = 0
+ ;
+ ; Change netware to the top provider
+ ;
+ OpenRegKey $(!REG_H_LOCAL) "" "System\CurrentControlSet\Control\Print\Providers" $(MAXIMUM_ALLOWED) PrintProviderKey
+ Ifstr $(PrintProviderKey) != $(KeyNull)
+ GetRegValue $(PrintProviderKey),"Order",OrderInfo
+ set OrderList = *($(OrderInfo),4)
+ set NewOrderList = {$(ProductProviderName)}
+ ForListDo $(OrderList)
+ ifstr(i) $($) != $(ProductProviderName)
+ set NewOrderList = >($(NewOrderList),$($))
+ endif
+ EndForListDo
+ SetRegValue $(PrintProviderKey) {"Order",$(NoTitle),$(!REG_VT_MULTI_SZ),$(NewOrderList)}
+ CloseRegKey $(PrintProviderKey)
+ endif
+
+ ;
+ ; Set Authentication packages
+ ;
+
+ OpenRegKey $(!REG_H_LOCAL) "" $(LSAKeyName) $(MAXIMUM_ALLOWED) LSAKey
+ Ifstr $(LSAKey) != $(KeyNull)
+ GetRegValue $(LSAKey),"Authentication Packages", PackagesInfo
+ set Packages = *($(PackagesInfo), 4)
+ ifcontains(i) "nwprovau" in $(Packages)
+ ; don't do it
+ debug-output "Authentication Packages already exist."
+ else
+ ifstr(i) $(Packages) == ""
+ set Packages = {"nwprovau"}
+ else
+ set Packages = >($(Packages),"nwprovau")
+ endif
+ SetRegValue $(LSAKey) {"Authentication Packages",$(NoTitle),$(!REG_VT_MULTI_SZ),$(Packages)}
+ endif
+ CloseRegKey $(LSAKey)
+ endif
+
+ ifstr(i) $(DoWKSTA) == TRUE
+ Debug-Output "OEMNSVNW.INF: Install Workstation in registry."
+ Set OEM_ABANDON_OPTIONS = >($(OEM_ABANDON_OPTIONS), $(ProductNWWKSTAName))
+ Shell $(UtilityInf), AddSoftwareComponent, $(Manufacturer), +
+ $(ProductNWWKSTAName), +
+ $(ProductNWWKSTAName), +
+ $(ProductNWWKSTADisplayName), $(STF_CONTEXTINFNAME), +
+ $(ProductNWWKSTAImagePath), "autoserviceshare", "NetworkProvider", {}, "",+
+ $(NetwareEventDLL)
+ ifint $($ShellCode) != $(!SHELL_CODE_OK)
+ Debug-Output "OEMNSVNW.INF: ShellCode error"
+ goto ShellCodeError
+ endif
+ set RegistryErrorIndex = $($R0)
+ Ifstr(i) $(RegistryErrorIndex) != NO_ERROR
+ EndWait
+ Debug-Output "OEMNSVNW.INF: Registry error: add software components"
+ CloseRegKey $($R1)
+ CloseRegKey $($R2)
+ CloseRegKey $($R3)
+ CloseRegKey $($R4)
+ CloseRegKey $($R5)
+ goto fatalregistry
+ endif
+ Set SoftProductKey = $($R1)
+ Set SoftNetRuleKey = $($R2)
+ Set SoftServiceKey = $($R3)
+ Set SoftParameterKey = $($R4)
+ Set SoftLinkageKey = $($R5)
+ set NewValueList = {{SoftwareType,$(NoTitle),$(!REG_VT_SZ),$(SoftwareType)},+
+ {MajorVersion,$(NoTitle),$(!REG_VT_DWORD),$(ProductMajorVersion)},+
+ {MinorVersion,$(NoTitle),$(!REG_VT_DWORD),$(ProductMinorVersion)},+
+ {Title,$(NoTitle),$(!REG_VT_SZ),$(ProductNWWKSTATitle)},+
+ {Description,$(NoTitle),$(!REG_VT_SZ),$(ProductNWWKSTADescription)},+
+ {ServiceName,$(NoTitle),$(!REG_VT_SZ),$(ProductNWWKSTAName)},+
+ {InstallDate,$(NoTitle),$(!REG_VT_DWORD),*($(Now),1)}}
+ Shell $(UtilityInf), AddValueList, $(SoftProductKey), $(NewValueList)
+ ifint $($ShellCode) != $(!SHELL_CODE_OK)
+ Debug-Output "OEMNSVNW.INF: ShellCode error."
+ goto ShellCodeError
+ endif
+ set RegistryErrorIndex = $($R0)
+ Ifstr(i) $(RegistryErrorIndex) != NO_ERROR
+ EndWait
+ Debug-Output "OEMNSVNW.INF: registry error: add value list."
+ CloseRegKey $(SoftProductKey)
+ CloseRegKey $(SoftNetRuleKey)
+ CloseRegKey $(SoftServiceKey)
+ CloseRegKey $(SoftLinkageKey)
+ CloseRegKey $(SoftParameterKey)
+ goto fatalregistry
+ endif
+ set NewValueList = {{type,$(NoTitle),$(!REG_VT_SZ),$(NetRuleNWWKSTAType)},+
+ {class,$(NoTitle),$(!REG_VT_MULTI_SZ),$(NetRuleNWWKSTAClass)}, +
+ {use,$(NoTitle),$(!REG_VT_SZ),$(NetRuleNWWKSTAUse)}, +
+ {bindform,$(NoTitle),$(!REG_VT_SZ),$(NetRuleNWWKSTABindForm)}, +
+ {bindable,$(NoTitle),$(!REG_VT_MULTI_SZ),$(NetRuleNWWKSTABindable)}, +
+ {InfOption,$(NoTitle),$(!REG_VT_SZ),NWWKSTA}}
+ Shell $(UtilityInf), AddValueList, $(SoftNetRuleKey), $(NewValueList)
+ ifint $($ShellCode) != $(!SHELL_CODE_OK)
+ Debug-Output "OEMNSVNW.INF: ShellCode error."
+ goto ShellCodeError
+ endif
+ set RegistryErrorIndex = $($R0)
+ CloseRegKey $(SoftProductKey)
+ CloseRegKey $(SoftNetRuleKey)
+ CloseRegKey $(SoftServiceKey)
+
+ Shell "" AddParameterKey $(SoftParameterKey)
+ CloseRegKey $(SoftParameterKey)
+
+ Ifstr(i) $(RegistryErrorIndex) != NO_ERROR
+ CloseRegKey $(SoftLinkageKey)
+ EndWait
+ Debug-Output "OEMNSVNW.INF: Registry error: add value list."
+ goto fatalregistry
+ endif
+ set NewValueList = {{OtherDependencies,$(NoTitle),$(!REG_VT_MULTI_SZ),{"Mup"}}}
+ Shell $(UtilityInf), AddValueList, $(SoftLinkageKey), $(NewValueList)
+ CloseRegKey $(SoftLinkageKey)
+ ifint $($ShellCode) != $(!SHELL_CODE_OK)
+ Debug-Output "OEMNSVNW.INF: ShellCode error."
+ goto ShellCodeError
+ endif
+ set RegistryErrorIndex = $($R0)
+ Ifstr(i) $(RegistryErrorIndex) != NO_ERROR
+ EndWait
+ Debug-Output "OEMNSVNW.INF: registry error: add value list."
+ goto fatalregistry
+ endif
+
+ ; Remove autoexec.nt statements
+ ;
+ Set FLibraryErrCtl = 1
+ LibraryProcedure Result $(!NWCFG_HANDLE), RemoveSzFromFile, $(!STF_WINDOWSSYSPATH)"\autoexec.nt", "REM Install network redirector"$(LF)
+ LibraryProcedure Result $(!NWCFG_HANDLE), RemoveSzFromFile, $(!STF_WINDOWSSYSPATH)"\autoexec.nt", "lh %SystemRoot%\system32\nw16"$(LF)
+ LibraryProcedure Result $(!NWCFG_HANDLE), RemoveSzFromFile, $(!STF_WINDOWSSYSPATH)"\autoexec.nt", "lh %SystemRoot%\system32\vwipxspx"$(LF)
+ ;
+ ; Setup string in autoexec.nt
+ ;
+ LibraryProcedure Result $(!NWCFG_HANDLE), AppendSzToFile, $(!STF_WINDOWSSYSPATH)"\autoexec.nt", "REM Install network redirector"$(CR)$(LF)
+ LibraryProcedure Result $(!NWCFG_HANDLE), AppendSzToFile, $(!STF_WINDOWSSYSPATH)"\autoexec.nt", "lh %SystemRoot%\system32\nw16"$(CR)$(LF)
+ LibraryProcedure Result $(!NWCFG_HANDLE), AppendSzToFile, $(!STF_WINDOWSSYSPATH)"\autoexec.nt", "lh %SystemRoot%\system32\vwipxspx"$(CR)$(LF)
+ Set FLibraryErrCtl = 0
+ endif
+
+ ifstr(i) $(DoNWLINK) == TRUE
+ ;
+ ; Increase the reference counter
+ ;
+ Shell "utility.inf", IncrementRefCount, "Software\Microsoft\NWLINKIPX\CurrentVersion"
+ endif
+
+ ifstr(i) $(DoRDR) == TRUE
+ Debug-Output "OEMNSVNW.INF: Install Rdr registry"
+ Set OEM_ABANDON_OPTIONS = >($(OEM_ABANDON_OPTIONS), $(ProductNWRDRName))
+ Shell $(UtilityInf), CreateService, $(ProductNWRDRName), +
+ $(ProductNWRDRDisplayName), $(ProductNWRDRImagePath), +
+ "system", "Network", {}, "", +
+ $(NetwareEventDLL)
+ ifint $($ShellCode) != $(!SHELL_CODE_OK)
+ Debug-Output "OEMNSVNW.INF: ShellCode error"
+ goto ShellCodeError
+ endif
+ set RegistryErrorIndex = $($R0)
+ set NWRdrServiceKey = $($R1)
+ CloseRegKey $($R2)
+ CloseRegKey $($R3)
+ Ifstr(i) $(RegistryErrorIndex) != NO_ERROR
+ EndWait
+ Debug-Output "OEMNSVNW.INF: Registry error: add software components"
+ goto fatalregistry
+ endif
+
+ ;
+ ; Add performance monitoring info
+ ;
+ CreateRegKey $(NWRdrServiceKey) {"Performance",$(NoTitle),GenericClass} "" +
+ $(MAXIMUM_ALLOWED) "" KeyPerformance
+
+ set NewValueList = {{Library,$(NoTitle),$(!REG_VT_SZ),"Perfnw.dll"},+
+ {Open,$(NoTitle),$(!REG_VT_SZ),"OpenNetWarePerformanceData"},+
+ {Collect,$(NoTitle),$(!REG_VT_SZ),"CollectNetWarePerformanceData"},+
+ {Close,$(NoTitle),$(!REG_VT_SZ),"CloseNetWarePerformanceData"}}
+
+ Shell $(UtilityInf), AddValueList, $(KeyPerformance), $(NewValueList)
+
+ set RegistryErrorIndex = $($R0)
+
+ CloseRegKey $(KeyPerformance)
+ CloseRegKey $(NWRdrServiceKey)
+
+ ;
+ ; Create performance help information
+ ;
+
+ LibraryProcedure Result $(!NWCFG_HANDLE), GetKernelVersion
+ ifint *($(Result),3) < 529
+ Set FLibraryErrCtl = 1
+ LibraryProcedure STATUS $(!NWCFG_HANDLE), lodctr $(!STF_WINDOWSSYSPATH)\nwperf.ini
+ ; Ignore the error code for now
+ Set FLibraryErrCtl = 0
+ else
+ StartDetachedProcess STATUS "" "" $(!STF_WINDOWSSYSPATH)"\lodctr.exe" "nwperf.ini"
+ ifstr(i) $(STATUS) == "ERROR"
+ debug-output "Cannot detached process"
+ endif
+ endif
+
+ Ifstr(i) $(RegistryErrorIndex) != NO_ERROR
+ EndWait
+ Debug-Output "OEMNSVNW.INF: Registry error: add performance information"
+ goto fatalregistry
+ endif
+
+ Debug-Output "OEMNSVNW.INF: Add NetworkProvider"
+ Shell $(UtilityInf), AddNetworkProvider, $(ProviderName), +
+ $(ProductProviderImagePath), $(ProductProviderName), "nwrdr"
+ set RegistryErrorIndex = $($R0)
+ Ifstr(i) $(RegistryErrorIndex) != NO_ERROR
+ EndWait
+ Debug-Output "OEMNSVNW.INF: Registry error: add software components"
+ goto fatalregistry
+ endif
+
+ ;
+ ; Put netware provider to the top of the order list
+ ;
+ OpenRegKey $(!REG_H_LOCAL) "" "System\CurrentControlSet\Control\NetworkProvider\Order" $(MAXIMUM_ALLOWED) NetProviderKey
+ Ifstr $(NetProviderKey) != $(KeyNull)
+ GetRegValue $(NetProviderKey),"ProviderOrder",OrderInfo
+ set OrderList = *($(OrderInfo),4)
+ Split-String $(OrderList), ",", ProviderList
+ set NewOrderList = $(ProductNWWKSTAName)
+ ForListDo $(ProviderList)
+ ifstr(i) $($) != ","
+ ifstr(i) $($) != $(ProductNWWKSTAName)
+ set NewOrderList = $(NewOrderList)","$($)
+ endif
+ endif
+ EndForListDo
+ SetRegValue $(NetProviderKey) {"ProviderOrder",$(NoTitle),$(!REG_VT_SZ),$(NewOrderList)}
+ CloseRegKey $(NetProviderKey)
+ endif
+
+ Shell "" AddShortName
+
+ OpenRegKey $(!REG_H_LOCAL) "" $(!NTN_ServiceBase)"\"$(ProviderName)"\networkprovider" $(MAXIMUM_ALLOWED) ProviderKey
+ ifstr(i) $(ProviderKey) != ""
+ set NewValueList = {{Class,$(NoTitle),$(!REG_VT_DWORD),3}}
+ Shell $(UtilityInf) AddValueList $(ProviderKey) $(NewValueList)
+ endif
+ CloseRegKey $(ProviderKey)
+
+ LibraryProcedure Status1, $(!LIBHANDLE), CheckFileExistance, $(!STF_WINDOWSSYSPATH)"\novell.cpl"
+ ifstr(i) $(Status1) == "YES"
+ LibraryProcedure Status2, $(!LIBHANDLE), RenFile, $(!STF_WINDOWSSYSPATH)"\novell.cpl", $(!STF_WINDOWSSYSPATH)"\novell.bak"
+ endif
+
+ OpenRegKey $(!REG_H_LOCAL) "" "System\CurrentControlSet\Control\WOW" $(MAXIMUM_ALLOWED) WowKey
+ ifstr(i) $(WowKey) != ""
+ GetRegValue $(WowKey),"KnownDLLS", KnownDLLsInfo
+ Split-String *($(KnownDLLsInfo),4), " ", KnownDLLsList
+ Ifcontains(i) "netware.drv" not-in $(KnownDLLsList)
+ ifstr(i) *($(KnownDLLsInfo),4) == ""
+ set KnownDLLs = "netware.drv"
+ else
+ set KnownDLLs = *($(KnownDLLsInfo),4)" netware.drv"
+ endif
+ SetRegValue $(WowKey) {"KnownDLLS",$(NoTitle),$(!REG_VT_SZ),$(KnownDLLs)}
+ endif
+ CloseRegKey $(WowKey)
+ endif
+
+ OpenRegKey $(!REG_H_LOCAL) "" "System\CurrentControlSet\Services\LanmanServer\Parameters" $(MAXIMUM_ALLOWED) ServerParmKey
+ ifstr(i) $(ServerParmKey) != ""
+ SetRegValue $(ServerParmKey) {"EnableSharedNetDrives",$(NoTitle),$(!REG_VT_DWORD),1}
+ CloseRegKey $(ServerParmKey)
+ endif
+ endif
+writeparameters = +
+
+ FreeLibrary $(!NWCFG_HANDLE)
+
+ EndWait
+ goto successful
+ConfigureBrowser = +
+ goto successful
+bindingadapter =+
+ set Error = "Binding: Sorry, not yet implemented."
+ goto fatal
+removeadapter = +
+ OpenRegKey $(!REG_H_LOCAL) "" "System\CurrentControlSet\Control\WOW" $(MAXIMUM_ALLOWED) WowKey
+ ifstr(i) $(WowKey) != ""
+ GetRegValue $(WowKey),"KnownDLLS", KnownDLLsInfo
+ Split-String *($(KnownDLLsInfo),4), " ", KnownDLLsList
+ set KnownDLLs = ""
+ Ifcontains(i) "netware.drv" in $(KnownDLLsList)
+ ForListDo $(KnownDLLsList)
+ ifstr(i) $($) != " "
+ ifstr(i) $($) != "netware.drv"
+ ifstr(i) $(KnownDLLs) == ""
+ set KnownDLLs = $($)
+ else
+ set KnownDLLs = $(KnownDLLs)" "$($)
+ endif
+ endif
+ endif
+ EndForListDo
+ else
+ set KnownDLLs = *($(KnownDLLsInfo),4)
+ endif
+ SetRegValue $(WowKey) {"KnownDLLS",$(NoTitle),$(!REG_VT_SZ),$(KnownDLLs)}
+ CloseRegKey $(WowKey)
+ endif
+ OpenRegKey $(!REG_H_LOCAL) "" "System\CurrentControlSet\Services\LanmanServer\Parameters" $(MAXIMUM_ALLOWED) ServerParmKey
+ ifstr(i) $(ServerParmKey) != ""
+ SetRegValue $(ServerParmKey) {"EnableSharedNetDrives",$(NoTitle),$(!REG_VT_DWORD),0}
+ CloseRegKey $(ServerParmKey)
+ endif
+
+ LoadLibrary "nw" $(!STF_CWDDIR)\nwcfg.dll !NWCFG_HANDLE
+ Set FLibraryErrCtl = 1
+ LibraryProcedure Result $(!NWCFG_HANDLE), DeleteNetwarePrinterProvidor
+ LibraryProcedure Result $(!NWCFG_HANDLE), DeleteGatewayPassword
+ Set FLibraryErrCtl = 0
+
+ LibraryProcedure Result $(!NWCFG_HANDLE), GetKernelVersion
+ ifint *($(Result),3) < 529
+ Set FLibraryErrCtl = 1
+ LibraryProcedure STATUS $(!NWCFG_HANDLE), unlodctr nwrdr
+ ; Ignore the error code for now
+ Set FLibraryErrCtl = 0
+ else
+ StartDetachedProcess STATUS "" "" $(!STF_WINDOWSSYSPATH)"\unlodctr.exe" "nwrdr"
+ ifstr(i) $(STATUS) == "ERROR"
+ debug-output "Cannot detached process"
+ endif
+ endif
+
+ ;
+ ; remove Authentication Packages
+ ;
+ OpenRegKey $(!REG_H_LOCAL) "" $(LSAKeyName) $(MAXIMUM_ALLOWED) LSAKey
+ Ifstr $(LSAKey) != $(KeyNull)
+ GetRegValue $(LSAKey),"Authentication Packages", PackagesInfo
+ set Packages = *($(PackagesInfo), 4)
+ ifcontains(i) "nwprovau" not-in $(Packages)
+ ; don't do it
+ ; something wrong....
+ debug-output "Authentication Packages does not exist.."
+ else
+ set NewPackages = {}
+ ForListDo $(Packages)
+ ifstr(i) $($) != "nwprovau"
+ ifstr(i) $(NewPackages) == {}
+ set NewPackages = {$($)}
+ else
+ set NewPackages = >($(NewPackages),$($))
+ endif
+ endif
+ EndForListDo
+ SetRegValue $(LSAKey) {"Authentication Packages",$(NoTitle),$(!REG_VT_MULTI_SZ),$(NewPackages)}
+ endif
+ CloseRegKey $(LSAKey)
+ endif
+
+ ifcontains(i) $(ProductNWWKSTAName) in $(OEM_ABANDON_OPTIONS)
+ Shell $(UtilityInf), RemoveSoftwareComponent, $(Manufacturer), +
+ $(ProductNWWKSTAName)
+ ifint $($ShellCode) != $(!SHELL_CODE_OK)
+ Debug-Output "OEMNSVNW.INF: ShellCode error"
+ goto ShellCodeError
+ endif
+ set RegistryErrorIndex = $($R0)
+ Ifstr(i) $(RegistryErrorIndex) != NO_ERROR
+ goto fatalregistry
+ endif
+ endif
+ ForListDo {$(ProductNWRDRName)}
+ ifcontains(i) $($) in $(OEM_ABANDON_OPTIONS)
+ Shell $(UtilityInf), RemoveService, $($), "YES"
+ ifint $($ShellCode) != $(!SHELL_CODE_OK)
+ Debug-Output "OEMNSVNW.INF: ShellCode error"
+ goto ShellCodeError
+ endif
+ set RegistryErrorIndex = $($R0)
+ Ifstr(i) $(RegistryErrorIndex) != NO_ERROR
+ goto fatalregistry
+ endif
+ endif
+ EndForListDo
+ Shell "UTILITY.INF", DecrementRefCount, "Software\Microsoft\NWLINKIPX\CurrentVersion"
+ ;
+ ; Remove autoexec.nt statements
+ ;
+ Set FLibraryErrCtl = 1
+ LibraryProcedure Result $(!NWCFG_HANDLE), RemoveSzFromFile, $(!STF_WINDOWSSYSPATH)"\autoexec.nt", "REM Install network redirector"$(LF)
+ LibraryProcedure Result $(!NWCFG_HANDLE), RemoveSzFromFile, $(!STF_WINDOWSSYSPATH)"\autoexec.nt", "lh %SystemRoot%\system32\nw16"$(LF)
+ LibraryProcedure Result $(!NWCFG_HANDLE), RemoveSzFromFile, $(!STF_WINDOWSSYSPATH)"\autoexec.nt", "lh %SystemRoot%\system32\vwipxspx"$(LF)
+ Set FLibraryErrCtl = 0
+
+ Shell $(UtilityInf) RemoveNetworkProvider $(ProviderName)
+ ifint $($ShellCode) != $(!SHELL_CODE_OK)
+ Debug-Output "OEMNSVNW.INF: ShellCode error"
+ goto ShellCodeError
+ endif
+ set RegistryErrorIndex = $($R0)
+ Ifstr(i) $(RegistryErrorIndex) != NO_ERROR
+ goto fatalregistry
+ endif
+
+ ;
+ ; Remove ShrotName
+ ;
+ OpenRegKey $(!REG_H_LOCAL) "" "System\CurrentControlSet\Control\NetworkProvider\ShortName" $(MAXIMUM_ALLOWED) ShortNameKey
+ ifstr $(ShortNameKey) != $(KeyNull)
+ DeleteRegValue $(ShortNameKey) "NW"
+ else
+ ;
+ ; we must be able to open the provider key
+ ;
+ debug-output "something wrong..."
+ endif
+
+ FreeLibrary $(!NWCFG_HANDLE)
+
+ Install "RestoreFiles"
+
+ Set FLibraryErrCtl = 1
+ LibraryProcedure Status1, $(!LIBHANDLE), DelFile, $(!STF_WINDOWSSYSPATH)"\nwc.bak"
+ LibraryProcedure Status1, $(!LIBHANDLE), DelFile, $(!STF_WINDOWSSYSPATH)"\nwapi32.bak"
+ LibraryProcedure Status1, $(!LIBHANDLE), RenFile, $(!STF_WINDOWSSYSPATH)"\nwc.cpl" $(!STF_WINDOWSSYSPATH)"\nwc.bak"
+ LibraryProcedure Status1, $(!LIBHANDLE), RenFile, $(!STF_WINDOWSSYSPATH)"\nwapi32.dll" $(!STF_WINDOWSSYSPATH)"\nwapi32.bak"
+ Set FLibraryErrCtl = 0
+
+ Shell "" RemoveBackupFiles
+
+ goto end
+UpgradeSoftware = +
+ LoadLibrary "nw" $(!STF_CWDDIR)\nwcfg.dll !NWCFG_HANDLE
+ LibraryProcedure Result $(!NWCFG_HANDLE), GetKernelVersion
+ ifint *($(Result),3) > 528
+ Shell $(subroutineinf) SetupMessage, $(!STF_LANGUAGE), "NONFATAL", $(NotDaytona)
+ ifint $($ShellCode) != $(!SHELL_CODE_OK)
+ goto ShellCodeError
+ endif
+ goto end
+ endif
+ FreeLibrary $(!NWCFG_HANDLE)
+
+
+ OpenRegKey $(!REG_H_LOCAL) "" $(ProductKeyName)"\NetRules" $(MAXIMUM_ALLOWED) KeyNetRules
+ Ifstr $(KeyNetRules) != $(KeyNull)
+ GetRegValue $(KeyNetRules),"InfName", InfNameInfo
+ set !UG_Filename = *($(InfNameInfo), 4)
+ CloseRegKey $(KeyNetRules)
+ else
+ Split-String $(!NTN_Infname), "\", FilenameList
+ QueryListSize ListSize $(FilenameList)
+ set !UG_Filename = *($(FilenameList), $(ListSize))
+ endif
+
+ Install "Install-Update"
+ ifstr(i) $(STF_INSTALL_OUTCOME) != STF_SUCCESS
+ goto fatal
+ endif
+
+ set AddCopy = "YES"
+ set DoCopy = "YES"
+ install "Install-nwcfgdll"
+ ifstr(i) $(STF_INSTALL_OUTCOME) != STF_SUCCESS
+ goto fatal
+ endif
+
+ LoadLibrary "nw" $(!STF_CWDDIR)\nwcfg.dll !NWCFG_HANDLE
+
+ OpenRegKey $(!REG_H_LOCAL) "" $(ProductKeyName) $(MAXIMUM_ALLOWED) KeyProduct
+
+ Ifstr $(KeyProduct) != $(KeyNull)
+ CloseRegKey $(KeyProduct)
+
+ ;
+ ; Check whether we are update beta 1 or beta 2.
+ ; Beta 2 will have a Performance key under \nwcworkstation\parameters
+ ;
+ OpenRegKey $(!REG_H_LOCAL) "" $(!NTN_ServiceBase)"\Nwrdr\Performance" $(MAXIMUM_ALLOWED) KeyPerformance
+
+ Ifstr $(KeyPerformance) != $(KeyNull)
+ ;
+ ; We are upgrading beta 2. Do nothing
+ ;
+ CloseRegKey $(KeyPerformance)
+
+ Set FLibraryErrCtl = 1
+ LibraryProcedure Result $(!NWCFG_HANDLE), DeleteGatewayPassword
+ Set FLibraryErrCtl = 0
+
+ LibraryProcedure Result $(!NWCFG_HANDLE), GetKernelVersion
+ ifint *($(Result),3) < 529
+ Set FLibraryErrCtl = 1
+ LibraryProcedure STATUS $(!NWCFG_HANDLE), unlodctr nwrdr
+ LibraryProcedure STATUS $(!NWCFG_HANDLE), lodctr $(!STF_WINDOWSSYSPATH)\nwperf.ini
+ Set FLibraryErrCtl = 0
+ endif
+
+ else
+ ;
+ ; We are upgrading beta 1. Do the followings:
+ ; 1. Add SetFileSysChangeValue call out.
+ ; 2. Add "vwipxspx" to autoexec.nt
+ ; 3. Add Performance stuff
+ ; 4. Add lodctr
+ ; 5. Change the provider device name
+ ; 6. rename novell.cpl
+ ; 7. Add WOW's known DLL
+ ; 8. Add EnableShareNetDrivers
+ ; 9. Change name
+ ; 10. Change class type
+ ;
+
+ ;
+ ; Do 1. Add SetFileSysChangeValue
+ ;
+ LibraryProcedure Result $(!NWCFG_HANDLE), SetFileSysChangeValue
+
+ ; Do 2. Remove autoexec.nt statements
+ ;
+ Set FLibraryErrCtl = 1
+ LibraryProcedure Result $(!NWCFG_HANDLE), RemoveSzFromFile, $(!STF_WINDOWSSYSPATH)"\autoexec.nt", "REM Install network redirector"$(LF)
+ LibraryProcedure Result $(!NWCFG_HANDLE), RemoveSzFromFile, $(!STF_WINDOWSSYSPATH)"\autoexec.nt", "lh %SystemRoot%\system32\nw16"$(LF)
+ LibraryProcedure Result $(!NWCFG_HANDLE), RemoveSzFromFile, $(!STF_WINDOWSSYSPATH)"\autoexec.nt", "lh %SystemRoot%\system32\vwipxspx"$(LF)
+ ;
+ ; Setup string in autoexec.nt
+ ;
+ LibraryProcedure Result $(!NWCFG_HANDLE), AppendSzToFile, $(!STF_WINDOWSSYSPATH)"\autoexec.nt", "REM Install network redirector"$(CR)$(LF)
+ LibraryProcedure Result $(!NWCFG_HANDLE), AppendSzToFile, $(!STF_WINDOWSSYSPATH)"\autoexec.nt", "lh %SystemRoot%\system32\nw16"$(CR)$(LF)
+ LibraryProcedure Result $(!NWCFG_HANDLE), AppendSzToFile, $(!STF_WINDOWSSYSPATH)"\autoexec.nt", "lh %SystemRoot%\system32\vwipxspx"$(CR)$(LF)
+ Set FLibraryErrCtl = 0
+
+ ;
+ ; Do 3. Add performance stuff
+ ;
+ OpenRegKey $(!REG_H_LOCAL) "" $(!NTN_ServiceBase)"\nwrdr" $(MAXIMUM_ALLOWED) KeyNwrdr
+ CreateRegKey $(KeyNwrdr) {"Performance",$(NoTitle),GenericClass} "" +
+ $(MAXIMUM_ALLOWED) "" KeyPerformance
+
+ set NewValueList = {{Library,$(NoTitle),$(!REG_VT_SZ),"Perfnw.dll"},+
+ {Open,$(NoTitle),$(!REG_VT_SZ),"OpenNetWarePerformanceData"},+
+ {Collect,$(NoTitle),$(!REG_VT_SZ),"CollectNetWarePerformanceData"},+
+ {Close,$(NoTitle),$(!REG_VT_SZ),"CloseNetWarePerformanceData"}}
+
+ Shell $(UtilityInf), AddValueList, $(KeyPerformance), $(NewValueList)
+
+ set RegistryErrorIndex = $($R0)
+
+ CloseRegKey $(KeyPerformance)
+ CloseRegKey $(KeyNwrdr)
+
+ ;
+ ; Do 4. Add lodctr
+ ;
+
+ LibraryProcedure Result $(!NWCFG_HANDLE), GetKernelVersion
+ ifint *($(Result),3) < 529
+ Set FLibraryErrCtl = 1
+ LibraryProcedure STATUS $(!NWCFG_HANDLE), lodctr $(!STF_WINDOWSSYSPATH)\nwperf.ini
+ ; Ignore the error code for now
+ Set FLibraryErrCtl = 0
+ endif
+
+ Ifstr(i) $(RegistryErrorIndex) != NO_ERROR
+ EndWait
+ Debug-Output "OEMNSVNW.INF: Registry error: add performance information"
+ goto fatalregistry
+ endif
+
+ ;
+ ; Do 6. rename novell.cpl
+ ;
+ LibraryProcedure Status1, $(!LIBHANDLE), CheckFileExistance, $(!STF_WINDOWSSYSPATH)"\novell.cpl"
+ ifstr(i) $(Status1) == "YES"
+ LibraryProcedure Status2, $(!LIBHANDLE), RenFile, $(!STF_WINDOWSSYSPATH)"\novell.cpl", $(!STF_WINDOWSSYSPATH)"\novell.bak"
+ endif
+
+ ;
+ ; Do 7. Add WOW's known DLL
+ ;
+
+ OpenRegKey $(!REG_H_LOCAL) "" "System\CurrentControlSet\Control\WOW" $(MAXIMUM_ALLOWED) WowKey
+ ifstr(i) $(WowKey) != ""
+ GetRegValue $(WowKey),"KnownDLLS", KnownDLLsInfo
+ Split-String *($(KnownDLLsInfo),4), " ", KnownDLLsList
+ Ifcontains(i) "netware.drv" not-in $(KnownDLLsList)
+ ifstr(i) *($(KnownDLLsInfo),4) == ""
+ set KnownDLLs = "netware.drv"
+ else
+ set KnownDLLs = *($(KnownDLLsInfo),4)" netware.drv"
+ endif
+ SetRegValue $(WowKey) {"KnownDLLS",$(NoTitle),$(!REG_VT_SZ),$(KnownDLLs)}
+ endif
+ CloseRegKey $(WowKey)
+ endif
+ endif
+ endif
+
+ ;
+ ; Do 5. Change the provider device name
+ ;
+ OpenRegKey $(!REG_H_LOCAL) "" "System\CurrentControlSet\Services\NWCWorkstation\networkprovider" $(MAXIMUM_ALLOWED) ProviderKey
+ ifstr(i) $(ProviderKey) != ""
+ SetRegValue $(ProviderKey) {Devicename,$(NoTitle),$(!REG_VT_SZ),"\device\nwrdr"}
+ SetRegValue $(ProviderKey) {Name,$(NoTitle),$(!REG_VT_SZ),$(ProductProviderName)}
+ CloseRegKey $(ProviderKey)
+ endif
+
+ ;
+ ; Do 8. Add EnableShareNetDrivers
+ ;
+ OpenRegKey $(!REG_H_LOCAL) "" "System\CurrentControlSet\Services\LanmanServer\Parameters" $(MAXIMUM_ALLOWED) ServerParmKey
+ ifstr(i) $(ServerParmKey) != ""
+ SetRegValue $(ServerParmKey) {"EnableSharedNetDrives",$(NoTitle),$(!REG_VT_DWORD),1}
+ CloseRegKey $(ServerParmKey)
+ endif
+
+ ;
+ ; do 9. Change name
+ ;
+ OpenRegKey $(!REG_H_LOCAL) "" "Software\Microsoft\NWCWorkstation\CurrentVersion" $(MAXIMUM_ALLOWED) CurrentVersionKey
+ ifstr(i) $(CurrentVersionKey) != ""
+ SetRegValue $(CurrentVersionKey) {"Description",$(NoTitle),$(!REG_VT_SZ),$(NWType)}
+ SetRegValue $(CurrentVersionKey) {"Title",$(NoTitle),$(!REG_VT_SZ),$(NWType)}
+ CloseRegKey $(CurrentVersionKey)
+ endif
+
+ OpenRegKey $(!REG_H_LOCAL) "" "System\CurrentControlSet\Services\NWCWorkstation" $(MAXIMUM_ALLOWED) nwcKey
+ ifstr(i) $(nwcKey) != ""
+ SetRegValue $(nwcKey) {"DisplayName",$(NoTitle),$(!REG_VT_SZ),$(NWType)}
+ CloseRegKey $(nwcKey)
+ endif
+
+ Set FLibraryErrCtl = 1
+ OpenRegKey $(!REG_H_LOCAL) "" "System\CurrentControlSet\Control\Print\Providers" $(MAXIMUM_ALLOWED) PrintProviderKey
+ ifstr(i) $(PrintProviderKey) != ""
+ GetRegValue $(PrintProviderKey) "Order" OrderInfo
+ ifcontains(i) $(OldPrintProviderName) in *($(OrderInfo),4)
+ ; spool is not started
+
+ DeleteRegKey $(PrintProviderKey) $(OldPrintProviderName)
+ OpenRegKey $(PrintProviderKey) "" $(ProductProviderName) $(MAXIMUM_ALLOWED) ProductProviderKey
+ ifstr $(ProductProviderKey) == $(KeyNull)
+ CreateRegKey $(PrintProviderKey) {$(ProductProviderName),$(NoTitle),GenericClass} "" $(MAXIMUM_ALLOWED) "" ProductProviderKey
+ endif
+ ifstr $(ProductProviderKey) != $(KeyNull)
+ SetRegValue $(ProductProviderKey) {"Name",$(NoTitle),$(!REG_VT_SZ),"nwprovau.dll"}
+ endif
+
+ ;
+ ; Change it to top provider
+ ;
+ OpenRegKey $(!REG_H_LOCAL) "" "System\CurrentControlSet\Control\Print\Providers" $(MAXIMUM_ALLOWED) PrintProviderKey
+ Ifstr $(PrintProviderKey) != $(KeyNull)
+ GetRegValue $(PrintProviderKey),"Order",OrderInfo
+ set OrderList = *($(OrderInfo),4)
+ set NewOrderList = {$(ProductProviderName)}
+ ForListDo $(OrderList)
+ ifstr(i) $($) != $(OldPrintProviderName)
+ ifstr(i) $($) != $(ProductProviderName)
+ set NewOrderList = >($(NewOrderList),$($))
+ endif
+ endif
+ EndForListDo
+ SetRegValue $(PrintProviderKey) {"Order",$(NoTitle),$(!REG_VT_MULTI_SZ),$(NewOrderList)}
+ CloseRegKey $(PrintProviderKey)
+ endif
+ endif
+ endif
+ Set FLibraryErrCtl = 0
+
+ Shell "" AddShortName
+
+ FreeLibrary $(!NWCFG_HANDLE)
+
+ goto end
+successful = +
+ goto end
+warning = +
+ Shell $(subroutineinf) SetupMessage, $(!STF_LANGUAGE), "WARNING", $(Error)
+ ifint $($ShellCode) != $(!SHELL_CODE_OK)
+ goto ShellCodeError
+ endif
+ ifstr(i) $($R1) == "OK"
+ goto $(to)
+ else-ifstr(i) $($R1) == "CANCEL"
+ goto $(from)
+ else
+ goto end
+ endif
+nonfatalinfo = +
+ Set CommonStatus = STATUS_USERCANCEL
+ Set Severity = STATUS
+ goto nonfatalmsg
+nonfatal = +
+ Set Severity = NONFATAL
+ goto nonfatalmsg
+nonfatalmsg = +
+ ifstr(i) $(Error) == ""
+ Set Severity = NONFATAL
+ Shell $(UtilityInf) RegistryErrorString "SETUP_FAIL"
+ ifint $($ShellCode) != $(!SHELL_CODE_OK)
+ goto ShellCodeError
+ endif
+ set Error = $($R0)
+ endif
+ Shell $(subroutineinf) SetupMessage, $(!STF_LANGUAGE), $(Severity), $(Error)
+ ifint $($ShellCode) != $(!SHELL_CODE_OK)
+ goto ShellCodeError
+ endif
+ ifstr(i) $($R1) == "OK"
+ goto $(from)
+ else
+ goto "end"
+ endif
+fatalregistry = +
+ Shell $(UtilityInf) RegistryErrorString $(RegistryErrorIndex)
+ ifint $($ShellCode) != $(!SHELL_CODE_OK)
+ goto ShellCodeError
+ endif
+ set Error = $($R0)
+ goto fatal
+fatal = +
+ ifstr(i) $(Error) == ""
+ Shell $(UtilityInf) RegistryErrorString "SETUP_FAIL"
+ ifint $($ShellCode) != $(!SHELL_CODE_OK)
+ goto ShellCodeError
+ endif
+ set Error = $($R0)
+ endif
+ Shell $(subroutineinf) SetupMessage, $(!STF_LANGUAGE), "FATAL", $(Error)
+ ifint $($ShellCode) != $(!SHELL_CODE_OK)
+ goto ShellCodeError
+ endif
+ goto setfailed
+ShellCodeError = +
+ set DlgType = "MessageBox"
+ set STF_MB_TITLE = "Error: "$(FunctionTitle)
+ set STF_MB_TEXT = $(ShellCodeErrorText)
+ set STF_MB_TYPE = 1
+ set STF_MB_ICON = 3
+ set STF_MB_DEF = 1
+ ui start "Error Message"
+ goto setfailed
+setfailed = +
+ set CommonStatus = STATUS_FAILED
+ ifstr(i) $(OEM_ABANDON_ON) == TRUE
+ set OEM_ABANDON_ON = FALSE
+ goto removeadapter
+ endif
+ goto end
+end = +
+ goto term
+term = +
+ Return $(CommonStatus)
+[RestoreFiles]
+ AddSectionFilesToCopyList Restore-Oldexe $(!STF_WINDOWSSYSPATH) $(!STF_WINDOWSSYSPATH)
+ AddSectionFilesToCopyList Restore-Oldsys $(!STF_WINDOWSSYSPATH)\drivers $(!STF_WINDOWSSYSPATH)\drivers
+ set !STF_NCPA_FLUSH_COPYLIST = TRUE
+ CopyFilesInCopyList
+ Exit
+[Install-nwcfgdll]
+ set STF_VITAL = ""
+ ifstr(i) $(AddCopy) == "YES"
+ AddSectionFilesToCopyList Files-nwcfgdll $(SrcDir) $(!STF_WINDOWSSYSPATH)
+ endif
+ ifstr(i) $(DoCopy) == "YES"
+ set !STF_NCPA_FLUSH_COPYLIST = TRUE
+ CopyFilesInCopyList
+ endif
+ Exit
+[Install-Option]
+ set STF_VITAL = ""
+ ifstr(i) $(AddCopy) == "YES"
+ AddSectionFilesToCopyList Files-$(Option) $(SrcDir) $(!STF_WINDOWSSYSPATH)
+ AddSectionFilesToCopyList Files-Drivers $(SrcDir) $(!STF_WINDOWSSYSPATH)\drivers
+ ifstr(i) $(!STF_PRODUCT) == "WINNT"
+ AddSectionFilesToCopyList Files-WINNTHelp $(SrcDir) $(!STF_WINDOWSSYSPATH)
+ else
+ AddSectionFilesToCopyList Files-NTASHelp $(SrcDir) $(!STF_WINDOWSSYSPATH)
+ endif
+ endif
+ ifstr(i) $(DoCopy) == "YES"
+ set !STF_NCPA_FLUSH_COPYLIST = TRUE
+ CopyFilesInCopyList
+ endif
+ ifstr(i) $(DoConfig) == "YES"
+ endif
+ Exit
+[Install-Update]
+ set STF_VITAL = ""
+ AddSectionFilesToCopyList Files-$(Option) $(SrcDir) $(!STF_WINDOWSSYSPATH)
+ AddSectionFilesToCopyList Files-Drivers $(SrcDir) $(!STF_WINDOWSSYSPATH)\drivers
+ AddSectionFilesToCopyList Files-Inf $(SrcDir) $(!STF_WINDOWSSYSPATH)
+ ifstr(i) $(!STF_PRODUCT) == "WINNT"
+ AddSectionFilesToCopyList Files-WINNTHelp $(SrcDir) $(!STF_WINDOWSSYSPATH)
+ else
+ AddSectionFilesToCopyList Files-NTASHelp $(SrcDir) $(!STF_WINDOWSSYSPATH)
+ endif
+ set !STF_NCPA_FLUSH_COPYLIST = TRUE
+ CopyFilesInCopyList
+ Exit
+
+[AddShortName]
+ read-syms GeneralConstants
+ read-syms FileConstants
+ read-syms FileConstants$(!STF_LANGUAGE)
+ ;
+ ; Create Short name
+ ;
+ OpenRegKey $(!REG_H_LOCAL) "" "System\CurrentControlSet\Control\NetworkProvider" $(MAXIMUM_ALLOWED) NetProviderKey
+ ifstr $(NetProviderKey) != $(KeyNull)
+ OpenRegKey $(NetProviderKey) "" "ShortName" $(MAXIMUM_ALLOWED) ShortNameKey
+ ifstr $(ShortNameKey) == $(KeyNull)
+ ;
+ ; Create short key if we cannot open it
+ ;
+ CreateRegKey $(NetProviderKey) {"ShortName",$(NoTitle),GenericClass} "" $(MAXIMUM_ALLOWED) "" ShortNameKey
+ endif
+ ifstr $(ShortNameKey) != $(KeyNull)
+ SetRegValue $(ShortNameKey) {"NW",$(NoTitle),$(!REG_VT_SZ),$(ProductProviderName)}
+ endif
+ else
+ ;
+ ; we must be able to open the provider key
+ ;
+ debug-output "something wrong..."
+ endif
+
+ return
+
+[AddParameterKey]
+ read-syms GeneralConstants
+ set SoftParameterKey = $($0)
+ CreateRegKey $(SoftParameterKey) {"Logon",$(NoTitle),GenericClass} "" $(MAXIMUM_ALLOWED) "" LogonKey
+ CreateRegKey $(SoftParameterKey) {"Option",$(NoTitle),GenericClass} "" $(MAXIMUM_ALLOWED) "" OptionKey
+
+ Set FLibraryErrCtl = 1
+ ;
+ ; 2 is the equal to SetValue
+ ;
+ LibraryProcedure Result $(!NWCFG_HANDLE), SetEverybodyPermission, $(OptionKey), 2
+ Set FLibraryErrCtl = 0
+
+ CloseRegKey $(LogonKey)
+ CloseRegKey $(OptionKey)
+
+ Return
+
+[GetFilesSize]
+ set FileSizeList = >(^(Files-Drivers,3),^(Files-NWWKSTA,3))
+ set TotalSize = 0
+ ForListDo $(FileSizeList)
+ ForListDo $($)
+ Debug-Output $($)
+ Split-String $($) "=" SplitString
+ set Size = *($(SplitString),3)
+ set-add TotalSize = $(TotalSize) $(Size)
+ EndForListDo
+ EndForListDo
+ set-div SizeInK = $(TotalSize) 1024
+ return $(SizeInK)
+
+[Files-Inf]
+1, oemsetup.inf, SIZE=1000, RENAME=$(!UG_Filename)
diff --git a/private/nw/install/wksta/nwperf.ini b/private/nw/install/wksta/nwperf.ini
new file mode 100644
index 000000000..09ad2d364
--- /dev/null
+++ b/private/nw/install/wksta/nwperf.ini
@@ -0,0 +1,37 @@
+[info]
+drivername=NWrdr
+symbolfile=nwperfm.h
+
+[languages]
+009=English
+
+[text]
+NWOBJ_009_NAME=Client Service for NetWare
+NWOBJ_009_HELP=Client Service for NetWare object type.
+
+PACKET_BURST_READ_ID_009_NAME=Packet Burst Read NCP Count/sec
+PACKET_BURST_READ_ID_009_HELP=Packet Burst Read NCP Count/sec is the rate of Netware Core Protocol requests for Packet Burst Read. Packet Burst is a windowing protocol that improves performance.
+
+PACKET_BURST_READ_TO_ID_009_NAME=Packet Burst Read Timeouts/sec
+PACKET_BURST_READ_TO_ID_009_HELP=Packet Burst Read Timeouts/sec is the rate the Netware(R) Workstation Compatible Service needs to retransmit a Burst Read Request because the Netware server took too long to respond.
+
+PACKET_BURST_WRITE_ID_009_NAME=Packet Burst Write NCP Count/sec
+PACKET_BURST_WRITE_ID_009_HELP=Packet Burst Write NCP Count/sec is the rate of Netware Core Protocol requests for Packet Burst Write. Packet Burst is a windowing protocol that improves performance.
+
+PACKET_BURST_WRITE_TO_ID_009_NAME=Packet Burst Write Timeouts/sec
+PACKET_BURST_WRITE_TO_ID_009_HELP=Packet Burst Write Timeouts/sec is the rate the Netware(R) Workstation Compatible Service need to retransmit a Burst Write Request because the Netware server took too long to respond.
+
+PACKET_BURST_IO_ID_009_NAME=Packet Burst IO/sec
+PACKET_BURST_IO_ID_009_HELP=Packet Burst IO/sec is the sum of Packet Burst Read NCPs/sec and Packet Burst Write NCPs/sec.
+
+CONNECT_2X_ID_009_NAME=Connect NetWare 2.x
+CONNECT_2X_ID_009_HELP=Connect NetWare 2.x counts connections to NetWare 2.x servers.
+
+CONNECT_3X_ID_009_NAME=Connect NetWare 3.x
+CONNECT_3X_ID_009_HELP=Connect NetWare 3.x counts connections to NetWare 3.x servers.
+
+CONNECT_4X_ID_009_NAME=Connect NetWare 4.x
+CONNECT_4X_ID_009_HELP=Connect NetWare 4.x counts connections to NetWare 4.x servers.
+
+
+
diff --git a/private/nw/install/wksta/nwperfm.h b/private/nw/install/wksta/nwperfm.h
new file mode 100644
index 000000000..f4934e675
--- /dev/null
+++ b/private/nw/install/wksta/nwperfm.h
@@ -0,0 +1,24 @@
+//
+// NWPerfM.h
+//
+// Offset definition file for exensible counter objects and counters
+//
+// These "relative" offsets must start at 0 and be multiples of 2 (i.e.
+// even numbers). In the Open Procedure, they will be added to the
+// "First Counter" and "First Help" values of the device they belong to,
+// in order to determine the absolute location of the counter and
+// object names and corresponding help text in the registry.
+//
+// this file is used by the extensible counter DLL code as well as the
+// counter name and help text definition file (.INI) file that is used
+// by LODCTR to load the names into the registry.
+//
+#define NWOBJ 0
+#define PACKET_BURST_READ_ID 2
+#define PACKET_BURST_READ_TO_ID 4
+#define PACKET_BURST_WRITE_ID 6
+#define PACKET_BURST_WRITE_TO_ID 8
+#define PACKET_BURST_IO_ID 10
+#define CONNECT_2X_ID 12
+#define CONNECT_3X_ID 14
+#define CONNECT_4X_ID 16
diff --git a/private/nw/install/wksta/ppc.txt b/private/nw/install/wksta/ppc.txt
new file mode 100644
index 000000000..4c0794041
--- /dev/null
+++ b/private/nw/install/wksta/ppc.txt
@@ -0,0 +1,4 @@
+[ProductType]
+STF_PRODUCT = Winnt
+STF_PLATFORM = PPC
+ \ No newline at end of file
diff --git a/private/nw/install/wksta/sources b/private/nw/install/wksta/sources
new file mode 100644
index 000000000..5f820ad93
--- /dev/null
+++ b/private/nw/install/wksta/sources
@@ -0,0 +1,42 @@
+!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:
+
+ Steve Wood (stevewo) 12-Apr-1989
+
+
+Revision History:
+
+ Terence Kwan (terryk) 13-Aug-1992
+ add make_inf
+
+!ENDIF
+
+MAJORCOMP=inf
+MINORCOMP=inf
+
+INCLUDES=.
+
+TARGETNAME=inf
+TARGETPATH=obj
+TARGETEXT = cpl
+TARGETTYPE= macro
+
+
+SOURCES=
+
+NTTARGETFILES=make_inf
diff --git a/private/nw/makefil0 b/private/nw/makefil0
new file mode 100644
index 000000000..391ba6b87
--- /dev/null
+++ b/private/nw/makefil0
@@ -0,0 +1,63 @@
+!IFNDEF BUILDMSG
+BUILDMSG=
+!ENDIF
+
+all: inc\nwevent.h nw16\inc\nwdos.inc nw16\tsr\obj\nw16.exe nw16\drv\netware.drv vwipxspx\tsr\obj\vwipxspx.exe
+!IF "$(BUILDMSG)" != ""
+ @ech ; $(BUILDMSG) ;
+!ENDIF
+
+clean: cleansrc all
+
+cleansrc:
+!IFNDEF NTVDM_BASED_BUILD
+ @echo skip make of dos mode binaries.
+!ELSE
+ -erase inc\nwevent.h event\msg00001.bin
+ -erase /Q nw16\tsr\obj\*
+ -erase /Q nw16\drv\*.drv
+ -erase /Q nw16\drv\*.obj
+ -erase /Q vwipxspx\tsr\obj\*
+ -erase /Q nw16\inc\nwdos.inc
+!ENDIF
+
+inc\nwevent.h: event\nwevent.mc
+ mc -v -r event -h inc\ event\nwevent.mc
+
+nw16\inc\nwdos.inc: nw16\inc\nwdos.h
+!IFNDEF NTVDM_BASED_BUILD
+ @echo skip make of dos mode binaries.
+!ELSE
+ cd nw16\inc
+ nmake nwdos.inc
+ cd ..\..
+!ENDIF
+
+nw16\tsr\obj\nw16.exe:
+!IFNDEF NTVDM_BASED_BUILD
+ @echo skip make of dos mode binaries.
+!ELSE
+ cd nw16\tsr
+ nmake
+ cd ..\..
+!ENDIF
+
+nw16\drv\netware.drv:
+!IFNDEF NTVDM_BASED_BUILD
+ @echo skip make of dos mode binaries.
+!ELSE
+ cd nw16\drv
+ nmake
+ cd ..\..
+!ENDIF
+
+vwipxspx\tsr\obj\vwipxspx.exe:
+!IFNDEF NTVDM_BASED_BUILD
+ @echo skip make of dos mode binaries.
+!ELSE
+ cd vwipxspx\tsr
+ nmake
+ binplace obj\vwipxspx.exe
+ cd ..\..
+!ENDIF
+
diff --git a/private/nw/ndsutils/browser.c b/private/nw/ndsutils/browser.c
new file mode 100644
index 000000000..0c1195aba
--- /dev/null
+++ b/private/nw/ndsutils/browser.c
@@ -0,0 +1,273 @@
+//
+// NDS Browser Test App
+//
+// Cory West
+//
+
+#include "ndsapi32.h"
+#include "nds.h"
+
+VOID
+ConsoleDumpSubordinates(
+ PNDS_RESPONSE_SUBORDINATE_LIST pSubList
+);
+
+int
+_cdecl main(
+ int argc,
+ char **argv
+) {
+
+ NTSTATUS Status;
+
+ //
+ // For NwNdsOpenTreeHandle
+ //
+
+ HANDLE hRdr;
+ OEM_STRING oemStr;
+ UNICODE_STRING ObjectName;
+ WCHAR NdsStr[256];
+
+ //
+ // For NwNdsResolveName
+ //
+
+ PNDS_RESPONSE_RESOLVE_NAME psResolveName;
+ DWORD dwOid;
+ UNICODE_STRING ReferredServer;
+ WCHAR ServerName[48];
+ HANDLE hReferredServer;
+ DWORD dwHandleType;
+
+ //
+ // For NwNdsReadObjectInfo
+ //
+
+ BYTE RawResponse[1024];
+ PNDS_RESPONSE_GET_OBJECT_INFO psGetInfo;
+ PBYTE pbRawGetInfo;
+ DWORD dwStrLen;
+
+ //
+ // For NwNdsList
+ //
+
+ DWORD dwIterHandle;
+
+ /**************************************************/
+
+ //
+ // Examine the argument count and hope for the best.
+ //
+
+ if ( argc != 3 ) {
+ printf( "Usage: browser <tree name> <ds object path>\n" );
+ printf( "For example, browser marsdev dev.mars\n");
+ return -1;
+ }
+
+ //
+ // Convert the tree name string to unicode.
+ //
+
+ oemStr.Length = strlen( argv[1] );
+ oemStr.MaximumLength = oemStr.Length;
+ oemStr.Buffer = argv[1];
+
+ ObjectName.Length = 0;
+ ObjectName.MaximumLength = sizeof( NdsStr );
+ ObjectName.Buffer = NdsStr;
+
+ RtlOemStringToUnicodeString( &ObjectName, &oemStr, FALSE );
+
+ //
+ // Get a handle to the redirector.
+ //
+
+ Status = NwNdsOpenTreeHandle( &ObjectName,
+ &hRdr );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ printf( "*** Open Handle to Nds Tree: Status = %08lx\n", Status );
+ return -1;
+ }
+
+ //
+ // Resolve the name that we have to an object id.
+ //
+
+ oemStr.Length = strlen(argv[2]);
+ oemStr.MaximumLength = oemStr.Length;
+ oemStr.Buffer = argv[2];
+
+ ObjectName.Length = 0;
+ ObjectName.MaximumLength = sizeof(NdsStr);
+ ObjectName.Buffer = NdsStr;
+
+ RtlOemStringToUnicodeString( &ObjectName, &oemStr, FALSE );
+
+ ReferredServer.Buffer = ServerName;
+ ReferredServer.MaximumLength = sizeof( ServerName );
+ ReferredServer.Length = 0;
+
+ Status = NwNdsResolveName ( hRdr,
+ &ObjectName,
+ &dwOid,
+ &ReferredServer,
+ NULL,
+ 0 );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ printf( "*** Resolve Name: Status = %08lx\n", Status );
+ goto Exit;
+ }
+
+ if ( ReferredServer.Length != 0 ) {
+
+ //
+ // We have to jump servers.
+ //
+
+ Status = NwNdsOpenGenericHandle( &ReferredServer,
+ &dwHandleType,
+ &hReferredServer );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ printf( "*** Couldn't open referred server: Status = %08lx\n", Status );
+ goto Exit;
+ }
+
+ CloseHandle( hRdr );
+ hRdr = hReferredServer;
+ }
+
+ printf( "=========================== NDS Object Info ===========================\n" );
+ printf( "Object ID = 0x%08lx\n", dwOid );
+
+ //
+ // Go for the object information.
+ //
+
+ Status = NwNdsReadObjectInfo( hRdr,
+ dwOid,
+ RawResponse,
+ sizeof( RawResponse ) );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ printf( "*** Get Object Info: Status = %08lx\n", Status );
+ goto Exit;
+ }
+
+ psGetInfo = ( PNDS_RESPONSE_GET_OBJECT_INFO ) RawResponse;
+
+ printf( "Flags = 0x%08lx\n", psGetInfo->EntryFlags );
+ printf( "Subordinate Count = 0x%08lx\n", psGetInfo->SubordinateCount );
+ printf( "Last Modified Time = 0x%08lx\n", psGetInfo->ModificationTime );
+
+ //
+ // Dig out the two unicode strings for class name and object name.
+ //
+
+ pbRawGetInfo = RawResponse;
+
+ pbRawGetInfo += sizeof ( NDS_RESPONSE_GET_OBJECT_INFO );
+
+ dwStrLen = * ( DWORD * ) pbRawGetInfo;
+ pbRawGetInfo += sizeof( DWORD );
+ printf( "Class Name: %S\n", pbRawGetInfo );
+
+ pbRawGetInfo += ROUNDUP4( dwStrLen );
+ dwStrLen = * ( DWORD * ) pbRawGetInfo;
+ pbRawGetInfo += sizeof( DWORD );
+ printf( "Object Name: %S\n", pbRawGetInfo );
+
+ //
+ // Get the subordinate list.
+ //
+
+ if ( psGetInfo->SubordinateCount ) {
+
+ dwIterHandle = INITIAL_ITERATION;
+
+ do {
+
+ Status = NwNdsList( hRdr,
+ dwOid,
+ &dwIterHandle,
+ RawResponse,
+ sizeof( RawResponse ) );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ printf( "*** List Subordinates: Status = %08lx\n", Status );
+ goto Exit;
+ }
+
+ ConsoleDumpSubordinates( (PNDS_RESPONSE_SUBORDINATE_LIST) RawResponse );
+
+ } while ( dwIterHandle != INITIAL_ITERATION );
+
+ }
+
+
+Exit:
+
+ CloseHandle( hRdr );
+
+ if ( NT_SUCCESS( Status ) ) {
+ return 0;
+ } else {
+ return -1;
+ }
+
+}
+
+
+VOID
+ConsoleDumpSubordinates(
+ PNDS_RESPONSE_SUBORDINATE_LIST pSubList
+) {
+
+ NTSTATUS Status;
+ PNDS_RESPONSE_SUBORDINATE_ENTRY pSubEntry;
+
+ PBYTE pbRaw;
+ DWORD dwStrLen, dwEntries;
+
+ dwEntries = pSubList->SubordinateEntries;
+
+ printf( "======================== Subordinate List (%d) ========================\n", dwEntries );
+
+ pSubEntry = ( PNDS_RESPONSE_SUBORDINATE_ENTRY )
+ ( ( (BYTE *)pSubList ) + sizeof( NDS_RESPONSE_SUBORDINATE_LIST ) );
+
+ while ( dwEntries ) {
+
+ printf( "EntryID (0x%08lx),\tFlags (0x%08lx)\n",
+ pSubEntry->EntryId, pSubEntry->Flags );
+
+ printf( "Subordinate Count (%d),\tMod Time (0x%08lx)\n",
+ pSubEntry->SubordinateCount, pSubEntry->ModificationTime );
+
+ pbRaw = (BYTE *) pSubEntry;
+ pbRaw += sizeof( NDS_RESPONSE_SUBORDINATE_ENTRY );
+
+ dwStrLen = * ( DWORD * ) pbRaw;
+ pbRaw += sizeof( DWORD );
+ printf( "Class Name: %S\t", pbRaw );
+
+ pbRaw += ROUNDUP4( dwStrLen );
+ dwStrLen = * ( DWORD * ) pbRaw;
+ pbRaw += sizeof( DWORD );
+ printf( "Object Name: %S\n", pbRaw );
+
+ pSubEntry = ( PNDS_RESPONSE_SUBORDINATE_ENTRY ) ( pbRaw + ROUNDUP4( dwStrLen ) );
+ dwEntries--;
+
+ printf( "-----------------------------------------------------------------------\n" );
+
+ }
+
+ return;
+
+}
diff --git a/private/nw/ndsutils/conninfo.c b/private/nw/ndsutils/conninfo.c
new file mode 100644
index 000000000..f2a9a878c
--- /dev/null
+++ b/private/nw/ndsutils/conninfo.c
@@ -0,0 +1,156 @@
+/***
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ ConnInfo.c
+
+Abstract:
+
+ Command line test for getting the connection information
+ for various connections.
+
+Author:
+
+ Cory West [corywest] 14-Nov-95
+
+***/
+
+#include "ndsapi32.h"
+#include "ntddnwfs.h"
+
+int
+_cdecl main(
+ int argc,
+ char **argv
+) {
+
+ 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;
+
+ 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;
+
+ //
+ // Check the arguments.
+ //
+
+ if ( argc != 2 ) {
+ printf( "Usage: conninfo [connection name]\n" );
+ printf( "For Example: conninfo x:, conninfo lpt1:, or conninfo \\\\server\\share\n" );
+ return -1;
+ }
+
+ //
+ // Allocate buffer space.
+ //
+
+ Request = (PNWR_REQUEST_PACKET) LocalAlloc( LMEM_ZEROINIT, BufferSize );
+
+ if ( !Request ) {
+ printf( "Insufficient memory to complete the request.\n" );
+ return -1;
+ }
+
+ //
+ // Convert the connect name to unicode.
+ //
+
+ ConnectionName.Length = 0;
+ ConnectionName.MaximumLength = sizeof( ConnectionBuffer );
+ ConnectionName.Buffer = ConnectionBuffer;
+
+ OemArg.Length = strlen( argv[1] );
+ OemArg.MaximumLength = OemArg.Length;
+ OemArg.Buffer = argv[1];
+
+ RtlOemStringToUnicodeString( &ConnectionName, &OemArg, FALSE );
+
+ //
+ // 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 ( !NT_SUCCESS(ntstatus) )
+ return ntstatus;
+
+ //
+ // 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 ( !NT_SUCCESS( ntstatus ) ) {
+ goto ExitWithClose;
+ }
+
+ //
+ // Print out the CONN_INFO that is in the reply buffer.
+ //
+
+ pConnInfo = (PCONN_INFORMATION) Reply;
+
+ Name.Length = Name.MaximumLength = (USHORT) pConnInfo->HostServerLength;
+ Name.Buffer = pConnInfo->HostServer;
+ printf( "Host Server: %wZ\n", &Name );
+
+ Name.Length = Name.MaximumLength = (USHORT) pConnInfo->UserNameLength;
+ Name.Buffer = pConnInfo->UserName;
+ printf( "User Name: %wZ\n", &Name );
+
+
+ExitWithClose:
+
+ LocalFree( Request );
+ NtClose( hRdr );
+ return ntstatus;
+
+}
diff --git a/private/nw/ndsutils/cx.c b/private/nw/ndsutils/cx.c
new file mode 100644
index 000000000..b1147b2cc
--- /dev/null
+++ b/private/nw/ndsutils/cx.c
@@ -0,0 +1,133 @@
+/***
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ Cx.c
+
+Abstract:
+
+ This is the command line NDS utility for setting contexts.
+
+Author:
+
+ Cory West [corywest] 25-Oct-95
+
+***/
+
+#include "ndsapi32.h"
+
+int
+_cdecl main(
+ int argc,
+ char **argv
+) {
+
+ NTSTATUS Status;
+ HANDLE hNdsTree;
+ OEM_STRING OemArg;
+
+ UNICODE_STRING NdsTree;
+ WCHAR TreeBuffer[1024];
+
+ UNICODE_STRING Context;
+ WCHAR ContextBuffer[1024];
+
+ //
+ // Who do we want to monkey with?
+ //
+
+ if ( argc < 2 ) {
+ printf( "Usage: cx [tree name] [optional context]\n" );
+ return -1;
+ }
+
+ //
+ // Get the tree.
+ //
+
+ OemArg.Length = strlen( argv[1] );
+ OemArg.MaximumLength = OemArg.Length;
+ OemArg.Buffer = argv[1];
+
+ NdsTree.Length = 0;
+ NdsTree.MaximumLength = sizeof( TreeBuffer );
+ NdsTree.Buffer = TreeBuffer;
+
+ RtlOemStringToUnicodeString( &NdsTree, &OemArg, FALSE );
+
+ //
+ // Open up a handle to the tree.
+ //
+
+ Status = NwNdsOpenTreeHandle( &NdsTree,
+ &hNdsTree );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ printf( "The supplied tree name is invalid or the tree is unavailable.\n" );
+ return -1;
+ }
+
+ //
+ // Get or set the context, depending.
+ //
+
+ Context.Length = 0;
+ Context.MaximumLength = sizeof( ContextBuffer );
+ Context.Buffer = ContextBuffer;
+
+ Status = STATUS_UNSUCCESSFUL;
+
+ if ( argc == 2 ) {
+
+ //
+ // Get the context.
+ //
+
+ Status = NwNdsGetTreeContext ( hNdsTree,
+ &NdsTree,
+ &Context );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ printf( "You are not logged into the specified tree.\n" );
+ goto Exit;
+ }
+
+ ContextBuffer[Context.Length/sizeof(WCHAR)] = L'\0';
+ printf( "%S", ContextBuffer );
+
+ } else {
+
+ //
+ // Set the context.
+ //
+
+ OemArg.Length = strlen( argv[2] );
+ OemArg.MaximumLength = OemArg.Length;
+ OemArg.Buffer = argv[2];
+
+ RtlOemStringToUnicodeString( &Context, &OemArg, FALSE );
+
+ Status = NwNdsSetTreeContext ( hNdsTree,
+ &NdsTree,
+ &Context );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ printf( "*** Set context: Status = %08lx\n", Status );
+ }
+
+ }
+
+
+Exit:
+
+ CloseHandle( hNdsTree );
+
+ if ( !NT_SUCCESS( Status )) {
+ return -1;
+ }
+
+ return 0;
+
+}
diff --git a/private/nw/ndsutils/getps.c b/private/nw/ndsutils/getps.c
new file mode 100644
index 000000000..a0634bad6
--- /dev/null
+++ b/private/nw/ndsutils/getps.c
@@ -0,0 +1,102 @@
+/***
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ GetPs.c
+
+Abstract:
+
+ Command line test for getting the preferred server.
+
+Author:
+
+ Cory West [corywest] 14-Nov-95
+
+***/
+
+#include "ndsapi32.h"
+
+int
+_cdecl main(
+ int argc,
+ char **argv
+) {
+
+ 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;
+
+ BYTE Reply[64];
+
+ //
+ // Check the arguments.
+ //
+
+ if ( argc != 1 ) {
+ printf( "Usage: getps\n" );
+ printf( "Retrieves the current preferred server.\n" );
+ return -1;
+ }
+
+ //
+ // 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 ( !NT_SUCCESS(ntstatus) )
+ return ntstatus;
+
+ //
+ // Call the nwrdr.
+ //
+
+ ntstatus = NtFsControlFile( hRdr,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_GET_PREFERRED_SERVER,
+ NULL,
+ 0,
+ (PVOID) Reply,
+ sizeof( Reply ) );
+
+ if ( !NT_SUCCESS( ntstatus ) ) {
+ printf( "No preferred server is currently set.\n" );
+ goto ExitWithClose;
+ }
+
+ //
+ // On success the output buffer contains a UNICODE_STRING
+ // with the string packed in afterwards.
+ //
+
+ printf( "Preferred Server: %wZ\n", (PUNICODE_STRING) Reply );
+
+ExitWithClose:
+
+ NtClose( hRdr );
+ return ntstatus;
+
+}
diff --git a/private/nw/ndsutils/getuser.c b/private/nw/ndsutils/getuser.c
new file mode 100644
index 000000000..833a0e2f7
--- /dev/null
+++ b/private/nw/ndsutils/getuser.c
@@ -0,0 +1,133 @@
+/***
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ GetUser.c
+
+Abstract:
+
+ This is the command line NDS utility for getting the
+ user name used to log into a specified tree or server.
+
+Author:
+
+ Cory West [corywest] 25-Oct-95
+
+***/
+
+#include "ndsapi32.h"
+
+int
+_cdecl main(
+ int argc,
+ char **argv
+) {
+
+ NTSTATUS ntstatus;
+ IO_STATUS_BLOCK IoStatusBlock;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ ACCESS_MASK DesiredAccess = SYNCHRONIZE | FILE_LIST_DIRECTORY;
+ HANDLE hRdr;
+
+ WCHAR DevicePreamble[] = L"\\Device\\Nwrdr\\";
+ UINT PreambleLength = 14;
+
+ WCHAR NameStr[64];
+ UNICODE_STRING OpenName;
+ UINT i;
+
+ OEM_STRING OemArg;
+ UNICODE_STRING NdsTree;
+ WCHAR TreeBuffer[64];
+
+ WCHAR Reply[64];
+
+ //
+ // Check the arguments.
+ //
+
+ if ( argc != 2 ) {
+ printf( "Usage: getuser [tree | server]\n" );
+ return -1;
+ }
+
+ //
+ // Copy over the preamble.
+ //
+
+ OpenName.MaximumLength = sizeof( NameStr );
+
+ for ( i = 0; i < PreambleLength ; i++ )
+ NameStr[i] = DevicePreamble[i];
+
+ //
+ // Convert the argument name to unicode.
+ //
+
+ OemArg.Length = strlen( argv[1] );
+ OemArg.MaximumLength = OemArg.Length;
+ OemArg.Buffer = argv[1];
+
+ NdsTree.Length = 0;
+ NdsTree.MaximumLength = sizeof( TreeBuffer );
+ NdsTree.Buffer = TreeBuffer;
+
+ RtlOemStringToUnicodeString( &NdsTree, &OemArg, FALSE );
+
+ //
+ // Copy the server or tree name.
+ //
+
+ for ( i = 0 ; i < ( NdsTree.Length / sizeof( WCHAR ) ) ; i++ ) {
+ NameStr[i + PreambleLength] = NdsTree.Buffer[i];
+ }
+
+ OpenName.Length = ( i * sizeof( WCHAR ) ) +
+ ( PreambleLength * sizeof( WCHAR ) );
+ OpenName.Buffer = NameStr;
+
+ //
+ // Set up the object attributes.
+ //
+
+ 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) )
+ return ntstatus;
+
+ ntstatus = NtFsControlFile( hRdr,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_GET_USERNAME,
+ (PVOID) TreeBuffer,
+ NdsTree.Length,
+ (PVOID) Reply,
+ sizeof( Reply ) );
+
+ if ( NT_SUCCESS( ntstatus )) {
+
+ NdsTree.Length = (USHORT)IoStatusBlock.Information;
+ NdsTree.MaximumLength = NdsTree.Length;
+ NdsTree.Buffer = Reply;
+ printf( "%wZ", &NdsTree );
+ }
+
+ NtClose( hRdr );
+ return ntstatus;
+
+}
diff --git a/private/nw/ndsutils/listconn.c b/private/nw/ndsutils/listconn.c
new file mode 100644
index 000000000..75a9e3c8b
--- /dev/null
+++ b/private/nw/ndsutils/listconn.c
@@ -0,0 +1,235 @@
+/***
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ ListConn.c
+
+Abstract:
+
+ Command line test for getting the CONN_STATUS structures
+ for various connections.
+
+Author:
+
+ Cory West [corywest] 14-Nov-95
+
+***/
+
+#include "ndsapi32.h"
+#include "ntddnwfs.h"
+
+ULONG DumpConnStatus(
+ PCONN_STATUS pStatus
+);
+
+int
+_cdecl main(
+ int argc,
+ char **argv
+) {
+
+ 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;
+
+ OEM_STRING OemArg;
+ UNICODE_STRING NdsTree;
+ WCHAR TreeBuffer[64];
+
+ ULONG BufferSize = 256;
+ ULONG RequestSize, ReplyLen;
+ PNWR_REQUEST_PACKET Request;
+ BYTE *Reply, *LocalBlock;
+ DWORD Entries, BlockLen;
+
+ //
+ // Check the arguments.
+ //
+
+ if ( argc > 2 ) {
+ printf( "Usage: listconn [tree | server]\n" );
+ return -1;
+ }
+
+ //
+ // Allocate buffer space.
+ //
+
+ Request = (PNWR_REQUEST_PACKET) LocalAlloc( LMEM_ZEROINIT, BufferSize );
+
+ if ( !Request ) {
+ printf( "Insufficient memory to complete the request.\n" );
+ return -1;
+ }
+
+ //
+ // Convert the argument name to unicode.
+ //
+
+ NdsTree.Length = 0;
+ NdsTree.MaximumLength = sizeof( TreeBuffer );
+ NdsTree.Buffer = TreeBuffer;
+
+ if ( argc == 2 ) {
+
+ OemArg.Length = strlen( argv[1] );
+ OemArg.MaximumLength = OemArg.Length;
+ OemArg.Buffer = argv[1];
+
+ NdsTree.Length = 0;
+ NdsTree.MaximumLength = sizeof( TreeBuffer );
+ NdsTree.Buffer = TreeBuffer;
+
+ RtlOemStringToUnicodeString( &NdsTree, &OemArg, FALSE );
+
+ }
+
+ //
+ // 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 ( !NT_SUCCESS(ntstatus) )
+ return ntstatus;
+
+ //
+ // Fill out the request packet for FSCTL_NWR_GET_CONN_STATUS.
+ //
+
+ Request->Parameters.GetConnStatus.ConnectionNameLength = NdsTree.Length;
+ RtlCopyMemory( &(Request->Parameters.GetConnStatus.ConnectionName[0]),
+ TreeBuffer,
+ NdsTree.Length );
+
+ RequestSize = sizeof( NWR_REQUEST_PACKET ) + NdsTree.Length;
+ Reply = ((PBYTE)Request) + RequestSize;
+ ReplyLen = BufferSize - RequestSize;
+
+ do {
+
+ ntstatus = NtFsControlFile( hRdr,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_GET_CONN_STATUS,
+ (PVOID) Request,
+ RequestSize,
+ (PVOID) Reply,
+ ReplyLen );
+
+ if ( !NT_SUCCESS( ntstatus ) ) {
+ goto ExitWithClose;
+ }
+
+ //
+ // Print out this batch and see if we need to continue.
+ //
+ // ATTN!!! The only time that the caller needs to resize
+ // the buffer is when there are no entries returned and the
+ // status is STATUS_BUFFER_TOO_SMALL. When this happens,
+ // the BytesNeeded field contains the smallest usable
+ // buffer size.
+ //
+
+ Entries = Request->Parameters.GetConnStatus.EntriesReturned;
+ printf( "%d entries returned for this call.\n", Entries );
+
+ LocalBlock = Reply;
+
+ while ( Entries-- ) {
+
+ BlockLen = DumpConnStatus( (PCONN_STATUS) LocalBlock );
+ LocalBlock += BlockLen;
+ }
+
+ } while ( Request->Parameters.GetConnStatus.ResumeKey != 0 );
+
+
+ExitWithClose:
+
+ LocalFree( Request );
+ NtClose( hRdr );
+ return ntstatus;
+
+}
+
+ULONG DumpConnStatus(
+ PCONN_STATUS pStatus
+) {
+
+ printf( "--------------------------------------------\n" );
+
+ printf( "Server: %S\n", pStatus->pszServerName );
+ printf( "User: %S\n", pStatus->pszUserName );
+ printf( "NDS Tree: %S\n", pStatus->pszTreeName );
+
+ printf( "Connection Number: %d\n", pStatus->nConnNum );
+
+ if ( pStatus->fNds ) {
+ printf( "Nds Connection: TRUE\n" );
+ } else {
+ printf( "Nds Connection: FALSE\n" );
+ }
+
+ if ( pStatus->fPreferred ) {
+ printf( "Preferred Server: TRUE\n" );
+ } else {
+ printf( "Preferred Server: FALSE\n" );
+ }
+
+ switch ( pStatus->dwConnType ) {
+
+ case NW_CONN_NOT_AUTHENTICATED:
+
+ printf( "Authentication: NOT AUTHENTICATED\n" );
+ break;
+
+ case NW_CONN_BINDERY_LOGIN:
+
+ printf( "Authentication: BINDERY LOGIN\n" );
+ break;
+
+ case NW_CONN_NDS_AUTHENTICATED_NO_LICENSE:
+
+ printf( "Authentication: NDS AUTHENTICATED, NOT LICENSED\n" );
+ break;
+
+ case NW_CONN_NDS_AUTHENTICATED_LICENSED:
+
+ printf( "Authentication: NDS AUTHENTICATED, LICENSED\n" );
+ break;
+
+ case NW_CONN_DISCONNECTED:
+
+ printf( "Authentication: DISCONNECTED\n" );
+ break;
+
+ }
+
+ printf( "--------------------------------------------\n" );
+
+ return pStatus->dwTotalLength;
+}
+
diff --git a/private/nw/ndsutils/makefile b/private/nw/ndsutils/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/nw/ndsutils/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/ndsutils/ndschpw.c b/private/nw/ndsutils/ndschpw.c
new file mode 100644
index 000000000..25eedc513
--- /dev/null
+++ b/private/nw/ndsutils/ndschpw.c
@@ -0,0 +1,173 @@
+/***
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ NdsChPw.c
+
+Abstract:
+
+ This is the command line NDS utility for changing a
+ user's NDS password.
+
+Author:
+
+ Cory West [corywest] 12-Jan-96
+
+***/
+
+#include <ndsapi32.h>
+#include <nds.h>
+
+int
+_cdecl main(
+ int argc,
+ char **argv
+) {
+
+ NTSTATUS ntstatus;
+ IO_STATUS_BLOCK IoStatusBlock;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ ACCESS_MASK DesiredAccess = SYNCHRONIZE | FILE_LIST_DIRECTORY;
+ HANDLE hRdr;
+
+ WCHAR DevicePreamble[] = L"\\Device\\Nwrdr\\";
+ UINT PreambleLength = 14;
+
+ UNICODE_STRING OpenName;
+ WCHAR NameStr[64];
+ UINT i;
+
+ OEM_STRING OemArg;
+
+ UNICODE_STRING NdsTree;
+ WCHAR TreeBuffer[MAX_NDS_TREE_NAME_LEN];
+
+ UNICODE_STRING UserName;
+ WCHAR UserBuffer[MAX_NDS_NAME_CHARS];
+
+ UNICODE_STRING CurrPass;
+ WCHAR CurrPassBuffer[64];
+
+ UNICODE_STRING NewPass;
+ WCHAR NewPassBuffer[64];
+
+ //
+ // Check the arguments.
+ //
+
+ if ( argc != 5 ) {
+ printf( "Usage: ndschpw tree user current_pw new_pw\n" );
+ return -1;
+ }
+
+ //
+ // Copy over the preamble.
+ //
+
+ OpenName.MaximumLength = sizeof( NameStr );
+
+ for ( i = 0; i < PreambleLength ; i++ )
+ NameStr[i] = DevicePreamble[i];
+
+ //
+ // Convert the argument name to unicode.
+ //
+
+ OemArg.Length = strlen( argv[1] );
+ OemArg.MaximumLength = OemArg.Length;
+ OemArg.Buffer = argv[1];
+
+ NdsTree.Length = 0;
+ NdsTree.MaximumLength = sizeof( TreeBuffer );
+ NdsTree.Buffer = TreeBuffer;
+
+ RtlOemStringToUnicodeString( &NdsTree, &OemArg, FALSE );
+
+ //
+ // Copy the server or tree name.
+ //
+
+ for ( i = 0 ; i < ( NdsTree.Length / sizeof( WCHAR ) ) ; i++ ) {
+ NameStr[i + PreambleLength] = NdsTree.Buffer[i];
+ }
+
+ OpenName.Length = ( i * sizeof( WCHAR ) ) +
+ ( PreambleLength * sizeof( WCHAR ) );
+ OpenName.Buffer = NameStr;
+
+ //
+ // Set up the object attributes.
+ //
+
+ 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) )
+ return ntstatus;
+
+ //
+ // Convert the other args to unicode.
+ //
+
+ OemArg.Length = strlen( argv[2] );
+ OemArg.MaximumLength = OemArg.Length;
+ OemArg.Buffer = argv[2];
+
+ UserName.Length = 0;
+ UserName.MaximumLength = sizeof( UserBuffer );
+ UserName.Buffer = UserBuffer;
+
+ RtlOemStringToUnicodeString( &UserName, &OemArg, FALSE );
+
+ OemArg.Length = strlen( argv[3] );
+ OemArg.MaximumLength = OemArg.Length;
+ OemArg.Buffer = argv[3];
+
+ CurrPass.Length = 0;
+ CurrPass.MaximumLength = sizeof( CurrPassBuffer );
+ CurrPass.Buffer = CurrPassBuffer;
+
+ RtlOemStringToUnicodeString( &CurrPass, &OemArg, FALSE );
+
+ OemArg.Length = strlen( argv[4] );
+ OemArg.MaximumLength = OemArg.Length;
+ OemArg.Buffer = argv[4];
+
+ NewPass.Length = 0;
+ NewPass.MaximumLength = sizeof( NewPassBuffer );
+ NewPass.Buffer = NewPassBuffer;
+
+ RtlOemStringToUnicodeString( &NewPass, &OemArg, FALSE );
+
+ //
+ // Submit the request.
+ //
+
+ ntstatus = NwNdsChangePassword( hRdr,
+ &NdsTree,
+ &UserName,
+ &CurrPass,
+ &NewPass );
+
+ if ( NT_SUCCESS( ntstatus )) {
+ printf( "Password changed.\n" );
+ } else {
+ printf( "Password change failed!\n" );
+ }
+
+ NtClose( hRdr );
+ return ntstatus;
+
+}
diff --git a/private/nw/ndsutils/netperf.c b/private/nw/ndsutils/netperf.c
new file mode 100644
index 000000000..e509f3fe7
--- /dev/null
+++ b/private/nw/ndsutils/netperf.c
@@ -0,0 +1,138 @@
+/***
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ netperf.c
+
+Abstract:
+
+ Command line test for getting the connection performance.
+
+Author:
+
+ Cory West [corywest] 17-April-96
+
+***/
+
+#include "ndsapi32.h"
+#include "ntddnwfs.h"
+
+int
+_cdecl main(
+ int argc,
+ char **argv
+) {
+
+ 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;
+
+ OEM_STRING OemArg;
+ UNICODE_STRING ConnectionName;
+
+ PNWR_REQUEST_PACKET Request;
+ ULONG BufferSize = 512;
+ ULONG RequestSize;
+
+ //
+ // Check the arguments.
+ //
+
+ if ( argc != 2 ) {
+ printf( "Usage: netperf [remote name]\n" );
+ return -1;
+ }
+
+ //
+ // Allocate buffer space.
+ //
+
+ Request = (PNWR_REQUEST_PACKET) LocalAlloc( LMEM_ZEROINIT, BufferSize );
+
+ if ( !Request ) {
+ printf( "Insufficient memory to complete the request.\n" );
+ return -1;
+ }
+
+ //
+ // Convert the connect name to unicode.
+ //
+
+ ConnectionName.Length = 0;
+ ConnectionName.MaximumLength = (USHORT) ( BufferSize - sizeof( NWR_REQUEST_PACKET ) );
+ ConnectionName.Buffer = &(Request->Parameters.GetConnPerformance.RemoteName[0]);
+
+ OemArg.Length = strlen( argv[1] );
+ OemArg.MaximumLength = OemArg.Length;
+ OemArg.Buffer = argv[1];
+
+ RtlOemStringToUnicodeString( &ConnectionName, &OemArg, FALSE );
+
+ //
+ // 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 ( !NT_SUCCESS(ntstatus) )
+ return ntstatus;
+
+ //
+ // Fill out the request packet for FSCTL_NWR_GET_CONN_PERFORMANCE.
+ //
+
+ Request->Parameters.GetConnPerformance.RemoteNameLength = 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 ) ) {
+ goto ExitWithClose;
+ }
+
+ //
+ // Print out the speed and packet size.
+ //
+
+ printf( "Speed: %d\n", Request->Parameters.GetConnPerformance.dwSpeed );
+ printf( "Flags: %d\n", Request->Parameters.GetConnPerformance.dwFlags );
+ printf( "Delay: %d\n", Request->Parameters.GetConnPerformance.dwDelay );
+ printf( "Packet Size: %d\n", Request->Parameters.GetConnPerformance.dwOptDataSize );
+
+
+ExitWithClose:
+
+ LocalFree( Request );
+ NtClose( hRdr );
+ return ntstatus;
+
+}
diff --git a/private/nw/ndsutils/rdstrm.c b/private/nw/ndsutils/rdstrm.c
new file mode 100644
index 000000000..f7e8b1491
--- /dev/null
+++ b/private/nw/ndsutils/rdstrm.c
@@ -0,0 +1,195 @@
+//
+// NDS File Stream Cat
+// Cory West
+//
+
+#include "ndsapi32.h"
+#include <nds.h>
+
+int
+_cdecl main(
+ int argc,
+ char **argv
+) {
+
+ NTSTATUS Status;
+
+ //
+ // For NwNdsOpenTreeHandle
+ //
+
+ HANDLE hRdr;
+ OEM_STRING oemStr;
+ UNICODE_STRING ObjectName;
+ WCHAR NdsStr[1024];
+
+ //
+ // For NwNdsResolveName
+ //
+
+ PNDS_RESPONSE_RESOLVE_NAME psResolveName;
+ DWORD dwOid;
+ HANDLE hReferredServer;
+ DWORD dwHandleType;
+ UNICODE_STRING ReferredServer;
+ WCHAR ServerName[48];
+
+ //
+ // For ReadFile of an open stream.
+ //
+
+ DWORD dwBytesRead, dwFileLength, dwBytesShown;
+ BOOL bRead;
+ BYTE RawResponse[1024];
+
+ /**************************************************/
+
+ //
+ // Examine the argument count and hope for the best.
+ //
+
+ if ( argc < 3 ) {
+ printf( "Usage: rdstrm <tree name> <ds object path> <file stream>\n" );
+ printf( "For example, rdstrm tree user.orgunit.org \"Login Script\"\n");
+ return -1;
+ }
+
+ //
+ // Convert the tree name string to unicode.
+ //
+
+ oemStr.Length = strlen( argv[1] );
+ oemStr.MaximumLength = oemStr.Length;
+ oemStr.Buffer = argv[1];
+
+ ObjectName.Length = 0;
+ ObjectName.MaximumLength = sizeof( NdsStr );
+ ObjectName.Buffer = NdsStr;
+
+ RtlOemStringToUnicodeString( &ObjectName, &oemStr, FALSE );
+
+ //
+ // Get a handle to the redirector.
+ //
+
+ Status = NwNdsOpenTreeHandle( &ObjectName, &hRdr );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ printf( "The tree is not available. Status was %08lx.\n", Status );
+ return -1;
+ }
+
+ //
+ // Resolve the name that we have to an object id.
+ //
+
+ oemStr.Length = strlen(argv[2]);
+ oemStr.MaximumLength = oemStr.Length;
+ oemStr.Buffer = argv[2];
+
+ ObjectName.Length = 0;
+ ObjectName.MaximumLength = sizeof(NdsStr);
+ ObjectName.Buffer = NdsStr;
+
+ RtlOemStringToUnicodeString( &ObjectName, &oemStr, FALSE );
+
+ ReferredServer.Buffer = ServerName;
+ ReferredServer.MaximumLength = sizeof( ServerName );
+ ReferredServer.Length = 0;
+
+ Status = NwNdsResolveName ( hRdr,
+ &ObjectName,
+ &dwOid,
+ &ReferredServer,
+ NULL,
+ 0 );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ printf( "The object is not available. Status = %08lx.\n", Status );
+ goto Exit;
+ }
+
+ if ( ReferredServer.Length != 0 ) {
+
+ Status = NwNdsOpenGenericHandle( &ReferredServer,
+ &dwHandleType,
+ &hReferredServer );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ printf( "The object's referred server is not available. Status = %08lx.\n", Status );
+ goto Exit;
+ }
+
+ CloseHandle( hRdr );
+ hRdr = hReferredServer;
+ }
+
+ //
+ // Try to open a file stream for read access.
+ //
+
+ oemStr.Length = strlen(argv[3]);
+ oemStr.MaximumLength = oemStr.Length;
+ oemStr.Buffer = argv[3];
+
+ ObjectName.Length = 0;
+ ObjectName.MaximumLength = sizeof(NdsStr);
+ ObjectName.Buffer = NdsStr;
+
+ RtlOemStringToUnicodeString( &ObjectName, &oemStr, FALSE );
+
+ Status = NwNdsOpenStream( hRdr,
+ dwOid,
+ &ObjectName,
+ 1,
+ &dwFileLength );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ printf( "The file stream is not available. Status = %08lx.\n", Status );
+ goto Exit;
+ }
+
+ //
+ // Dump the file stream.
+ //
+
+ printf( "---------- There are %d bytes in file stream %s ----------\n", dwFileLength, argv[3] );
+
+ while ( dwFileLength ) {
+
+ bRead = ReadFile( hRdr,
+ RawResponse,
+ sizeof( RawResponse ),
+ &dwBytesRead,
+ NULL );
+
+ if ( !bRead ) {
+
+ printf( "*** Couldn't read data from file stream.\n" );
+ goto Exit;
+ }
+
+ dwFileLength -= dwBytesRead;
+ dwBytesShown = 0;
+
+ while ( dwBytesRead-- ) {
+ printf( "%c", RawResponse[dwBytesShown++] );
+ }
+
+
+ }
+
+ printf( "\n-----------------------------------------------------------------------\n" );
+
+Exit:
+
+ CloseHandle( hRdr );
+
+ if ( !NT_SUCCESS( Status )) {
+ return -1;
+ } else {
+ return 0;
+ }
+
+}
+
diff --git a/private/nw/ndsutils/setshare.c b/private/nw/ndsutils/setshare.c
new file mode 100644
index 000000000..92caf2ee8
--- /dev/null
+++ b/private/nw/ndsutils/setshare.c
@@ -0,0 +1,88 @@
+/***
+
+Copyright (c) 1996 Microsoft Corporation
+
+Module Name:
+
+ SetShare.c
+
+Abstract:
+
+ This is a command line test utility for setting the
+ shareable bit on a file on a Netware server.
+
+Author:
+
+ Cory West [corywest] 25-April-96
+
+***/
+
+#include "ndsapi32.h"
+
+int
+_cdecl main(
+ int argc,
+ char **argv
+) {
+
+ NTSTATUS Status;
+ int ReturnCode = 0;
+ IO_STATUS_BLOCK IoStatusBlock;
+ HANDLE hFile;
+
+ //
+ // Check the command line arguments for a file.
+ //
+
+ if ( argc < 1 ) {
+ printf( "Usage: setshare [path to file]\n" );
+ return -1;
+ }
+
+ //
+ // Open the file.
+ //
+
+ hFile = CreateFile( argv[1], // file name
+ GENERIC_READ, // read access
+ 0, // exclusive
+ NULL, // no security specifications
+ OPEN_EXISTING, // do not create
+ 0, // no attributes that we care about
+ NULL ); // no template
+
+ if ( hFile == INVALID_HANDLE_VALUE ) {
+ printf( "Couldn't open the request file. Error = %08lx\n", Status );
+ return -1;
+ }
+
+ //
+ // Tell the redirector to set the shareable bit.
+ //
+
+ Status = NtFsControlFile( hFile,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_SET_SHAREBIT,
+ NULL,
+ 0,
+ NULL,
+ 0 );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ printf( "A parameter was not correct. This only works for a file\n" );
+ printf( "on a Netware volume. Status = %08lx\n", Status );
+ ReturnCode = -1;
+ }
+
+ CloseHandle( hFile );
+
+ //
+ // The bit actually gets set when you close the file.
+ //
+
+ return ReturnCode;
+
+}
diff --git a/private/nw/ndsutils/sources b/private/nw/ndsutils/sources
new file mode 100644
index 000000000..595e412ff
--- /dev/null
+++ b/private/nw/ndsutils/sources
@@ -0,0 +1,46 @@
+!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:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=ntos
+MINORCOMP=nwrdr
+
+TARGETNAME=cx
+TARGETPATH=obj
+TARGETTYPE=library
+
+UMTYPE=console
+UMAPPL=cx*rdstrm*getuser*listconn*volinfo*conninfo*getps*browser*userfrag*ndschpw*treebn*netperf*setshare
+UMLIBS=
+UNICODE=1
+NTDEBUGTYPE=both
+NTDEBUG=ntsd
+386_OPTIMIZATION=/Od
+
+INCLUDES=..\inc;$(_NTROOT)\private\ntos\inc;$(_NTROOT)\private\inc;..\rdr
+
+SOURCES=
+
+UMLIBS=$(BASEDIR)\public\sdk\lib\*\ntdll.lib \
+ $(BASEDIR)\public\sdk\lib\*\nwapi32.lib
+
diff --git a/private/nw/ndsutils/treebn.c b/private/nw/ndsutils/treebn.c
new file mode 100644
index 000000000..77a735e37
--- /dev/null
+++ b/private/nw/ndsutils/treebn.c
@@ -0,0 +1,182 @@
+/***
+
+Copyright (c) 1996 Microsoft Corporation
+
+Module Name:
+
+ Treebn.c
+
+Abstract:
+
+ Command line test for getting the connection information
+ for a particular NT user name.
+
+Author:
+
+ Cory West [corywest] 1-March-1996
+
+***/
+
+#include "ndsapi32.h"
+#include "ntddnwfs.h"
+
+int
+_cdecl main(
+ int argc,
+ char **argv
+) {
+
+ 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;
+
+ OEM_STRING OemArg;
+ UNICODE_STRING NtUserName;
+ WCHAR NtUserBuffer[512];
+
+ ULONG BufferSize = 4096;
+ BYTE *Reply;
+
+ PNWR_NDS_REQUEST_PACKET Request;
+ BYTE RequestBuffer[2048];
+ ULONG RequestSize;
+
+ PCONN_INFORMATION pConnInfo;
+ UNICODE_STRING Name;
+ DWORD dwTrees, dwSize;
+
+ //
+ // Check the arguments.
+ //
+
+ if ( argc != 2 ) {
+ printf( "Usage: treebn [nt user name]\n" );
+ return -1;
+ }
+
+ //
+ // Allocate buffer space.
+ //
+
+ Reply = LocalAlloc( LMEM_ZEROINIT, BufferSize );
+
+ if ( !Reply ) {
+ printf( "Insufficient memory to complete the request.\n" );
+ return -1;
+ }
+
+ //
+ // Convert the user name to unicode.
+ //
+
+ NtUserName.Length = 0;
+ NtUserName.MaximumLength = sizeof( NtUserBuffer );
+ NtUserName.Buffer = NtUserBuffer;
+
+ OemArg.Length = strlen( argv[1] );
+ OemArg.MaximumLength = OemArg.Length;
+ OemArg.Buffer = argv[1];
+
+ RtlOemStringToUnicodeString( &NtUserName, &OemArg, FALSE );
+
+ //
+ // 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 ( !NT_SUCCESS(ntstatus) )
+ return ntstatus;
+
+ //
+ // Fill out the request packet for FSCTL_NWR_NDS_LIST_TREES;
+ //
+
+ Request = ( PNWR_NDS_REQUEST_PACKET ) RequestBuffer;
+
+ Request->Parameters.ListTrees.NtUserNameLength = NtUserName.Length;
+
+ RtlCopyMemory( &(Request->Parameters.ListTrees.NtUserName[0]),
+ NtUserBuffer,
+ NtUserName.Length );
+
+ RequestSize = sizeof( Request->Parameters.ListTrees ) +
+ NtUserName.Length +
+ sizeof( DWORD );
+
+ ntstatus = NtFsControlFile( hRdr,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_NDS_LIST_TREES,
+ (PVOID) Request,
+ RequestSize,
+ (PVOID) Reply,
+ BufferSize );
+
+ if ( !NT_SUCCESS( ntstatus ) ) {
+ goto ExitWithClose;
+ }
+
+ //
+ // Print out the CONN_INFO array that is in the reply buffer.
+ //
+
+ dwTrees = Request->Parameters.ListTrees.TreesReturned;
+ printf( "Returned %d trees.\n", dwTrees );
+ printf( "Luid was %08lx\n", Request->Parameters.ListTrees.UserLuid.LowPart );
+
+ printf( "------------------------\n" );
+
+ pConnInfo = (PCONN_INFORMATION) Reply;
+
+ while ( dwTrees > 0 ) {
+
+ dwSize = sizeof( CONN_INFORMATION );
+
+ Name.Length = Name.MaximumLength = (USHORT) pConnInfo->HostServerLength;
+ Name.Buffer = pConnInfo->HostServer;
+ printf( "Tree: %wZ\n", &Name );
+
+ dwSize += Name.Length;
+
+ Name.Length = Name.MaximumLength = (USHORT) pConnInfo->UserNameLength;
+ Name.Buffer = pConnInfo->UserName;
+ printf( "User Name: %wZ\n", &Name );
+
+ dwSize += Name.Length;
+
+ pConnInfo = (PCONN_INFORMATION) ( ((BYTE *)pConnInfo) + dwSize );
+ dwTrees--;
+
+ printf( "------------------------\n" );
+ }
+
+ ntstatus = STATUS_SUCCESS;
+
+ExitWithClose:
+
+ LocalFree( Request );
+ NtClose( hRdr );
+ return ntstatus;
+
+}
diff --git a/private/nw/ndsutils/userfrag.c b/private/nw/ndsutils/userfrag.c
new file mode 100644
index 000000000..7b8d7efef
--- /dev/null
+++ b/private/nw/ndsutils/userfrag.c
@@ -0,0 +1,149 @@
+/***
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ UserFrag.c
+
+Abstract:
+
+ User mode fragment exchange test.
+
+Author:
+
+ Cory West [corywest] 05-Jan-1996
+
+***/
+
+#include "ndsapi32.h"
+#include <nds.h>
+
+int
+_cdecl main(
+ int argc,
+ char **argv
+) {
+
+ NTSTATUS Status;
+ HANDLE hNdsTree;
+ OEM_STRING OemArg;
+
+ UNICODE_STRING NdsTree;
+ WCHAR TreeBuffer[48]; // Max nds tree name length.
+
+ UNICODE_STRING Object;
+ WCHAR ObjectBuffer[256]; // Max nds name length.
+
+ DWORD dwResolverFlags;
+ DWORD dwOid;
+ DWORD dwReplyLength;
+ BYTE NdsReply[NDS_BUFFER_SIZE];
+
+ //
+ // Who do we want to monkey with?
+ //
+
+ if ( argc != 3 ) {
+ printf( "Usage: userfrag [server name] [nds object]\n" );
+ return -1;
+ }
+
+ //
+ // Get the server.
+ //
+
+ OemArg.Length = strlen( argv[1] );
+ OemArg.MaximumLength = OemArg.Length;
+ OemArg.Buffer = argv[1];
+
+ NdsTree.Length = 0;
+ NdsTree.MaximumLength = sizeof( TreeBuffer );
+ NdsTree.Buffer = TreeBuffer;
+
+ RtlOemStringToUnicodeString( &NdsTree, &OemArg, FALSE );
+
+ //
+ // Open up a handle to the tree.
+ //
+
+ Status = NwNdsOpenTreeHandle( &NdsTree,
+ &hNdsTree );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ printf( "The supplied tree name is invalid or the tree is unavailable.\n" );
+ return -1;
+ }
+
+ //
+ // Get the object information.
+ //
+
+ OemArg.Length = strlen( argv[2] );
+ OemArg.MaximumLength = OemArg.Length;
+ OemArg.Buffer = argv[2];
+
+ Object.Length = 0;
+ Object.MaximumLength = sizeof( ObjectBuffer );
+ Object.Buffer = ObjectBuffer;
+
+ RtlOemStringToUnicodeString( &Object, &OemArg, FALSE );
+
+ dwResolverFlags = RSLV_DEREF_ALIASES | RSLV_WALK_TREE | RSLV_WRITABLE;
+
+ Status = FragExWithWait( hNdsTree,
+ NDSV_RESOLVE_NAME,
+ NdsReply,
+ NDS_BUFFER_SIZE,
+ &dwReplyLength,
+ "DDDSDDDD",
+ 0, // version
+ dwResolverFlags, // flags
+ 0, // scope
+ &Object, // distinguished name
+ 1,0, // transport type
+ 1,0 ); // treeWalker type
+
+
+ if ( !NT_SUCCESS( Status ) ) {
+ printf( "The resolve name failed.\n" );
+ goto ExitWithClose;
+ }
+
+ Status = ParseResponse( NdsReply,
+ dwReplyLength,
+ "G_D",
+ 2 * sizeof( DWORD ), // Skip the first two DWORDs
+ &dwOid );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ printf( "The resolve name response was undecipherable.\n" );
+ goto ExitWithClose;
+ }
+
+ Status = FragExWithWait( hNdsTree,
+ NDSV_READ_ENTRY_INFO,
+ NdsReply,
+ NDS_BUFFER_SIZE,
+ &dwReplyLength,
+ "DD",
+ 0,
+ dwOid );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ printf( "The get object info failed.\n" );
+ goto ExitWithClose;
+ }
+
+ExitWithClose:
+
+ CloseHandle( hNdsTree );
+
+ if ( NT_SUCCESS( Status ) ) {
+ return 0;
+ }
+
+ printf( "Unable to complete the requested operation: 0x%08lx\n", Status );
+ return -1;
+
+}
diff --git a/private/nw/ndsutils/volinfo.c b/private/nw/ndsutils/volinfo.c
new file mode 100644
index 000000000..1e2d838a3
--- /dev/null
+++ b/private/nw/ndsutils/volinfo.c
@@ -0,0 +1,119 @@
+/***
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ VolInfo.c
+
+Abstract:
+
+ A command line NDS utility for resolving volume objects.
+
+Author:
+
+ Cory West [corywest] 25-Oct-95
+
+***/
+
+#include "ndsapi32.h"
+
+int
+_cdecl main(
+ int argc,
+ char **argv
+) {
+
+ NTSTATUS Status;
+ HANDLE hNdsTree;
+ OEM_STRING OemArg;
+
+ UNICODE_STRING NdsTree;
+ WCHAR TreeBuffer[48]; // Max nds tree name length.
+
+ UNICODE_STRING Volume;
+ WCHAR VolumeBuffer[256]; // Max nds name length.
+
+ UNICODE_STRING HostServer, HostVolume;
+ WCHAR HostServerBuffer[48];
+ WCHAR HostVolumeBuffer[256];
+
+ //
+ // Who do we want to monkey with?
+ //
+
+ if ( argc != 3 ) {
+ printf( "Usage: volinfo [tree name] [volume object]\n" );
+ return -1;
+ }
+
+ //
+ // Get the tree.
+ //
+
+ OemArg.Length = strlen( argv[1] );
+ OemArg.MaximumLength = OemArg.Length;
+ OemArg.Buffer = argv[1];
+
+ NdsTree.Length = 0;
+ NdsTree.MaximumLength = sizeof( TreeBuffer );
+ NdsTree.Buffer = TreeBuffer;
+
+ RtlOemStringToUnicodeString( &NdsTree, &OemArg, FALSE );
+
+ //
+ // Open up a handle to the tree.
+ //
+
+ Status = NwNdsOpenTreeHandle( &NdsTree,
+ &hNdsTree );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ printf( "The supplied tree name is invalid or the tree is unavailable.\n" );
+ return -1;
+ }
+
+ //
+ // Get the volume information.
+ //
+
+ OemArg.Length = strlen( argv[2] );
+ OemArg.MaximumLength = OemArg.Length;
+ OemArg.Buffer = argv[2];
+
+ Volume.Length = 0;
+ Volume.MaximumLength = sizeof( VolumeBuffer );
+ Volume.Buffer = VolumeBuffer;
+
+ RtlOemStringToUnicodeString( &Volume, &OemArg, FALSE );
+
+ //
+ // Set up the reply strings.
+ //
+
+ HostServer.Length = 0;
+ HostServer.MaximumLength = sizeof( HostServerBuffer );
+ HostServer.Buffer = HostServerBuffer;
+
+ HostVolume.Length = 0;
+ HostVolume.MaximumLength = sizeof( HostVolumeBuffer );
+ HostVolume.Buffer = HostVolumeBuffer;
+
+ Status = NwNdsGetVolumeInformation( hNdsTree,
+ &Volume,
+ &HostServer,
+ &HostVolume );
+
+
+ CloseHandle( hNdsTree );
+
+ if ( NT_SUCCESS( Status )) {
+ printf( "Host Server: %wZ\n", &HostServer );
+ printf( "Host Volume: %wZ\n", &HostVolume );
+ return 0;
+ }
+
+ printf( "Unable to complete the requested operation: 0x%08lx\n", Status );
+ return -1;
+
+}
diff --git a/private/nw/nw16/dirs b/private/nw/nw16/dirs
new file mode 100644
index 000000000..5c948bcc1
--- /dev/null
+++ b/private/nw/nw16/dirs
@@ -0,0 +1,23 @@
+!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= dll
diff --git a/private/nw/nw16/dll/debug.c b/private/nw/nw16/dll/debug.c
new file mode 100644
index 000000000..886acd75d
--- /dev/null
+++ b/private/nw/nw16/dll/debug.c
@@ -0,0 +1,584 @@
+/*++
+
+Copyright (c) 1991-3 Microsoft Corporation
+
+Module Name:
+
+ debug.c
+
+Abstract:
+
+ This component of netbios runs in the user process and can ( when
+ built in a debug kernel) will log to either the console or through the
+ kernel debugger.
+
+Author:
+
+ Colin Watson (ColinW) 24-Jun-91
+
+Revision History:
+
+--*/
+
+#include "procs.h"
+
+#if NWDBG
+
+//
+// Set DebugControl to 1 to open the logfile on the first NW call and close it
+// on process exit.
+//
+
+int DebugCtrl = 0;
+
+BOOL UseConsole = FALSE;
+BOOL UseLogFile = FALSE;
+BOOL Verbose = FALSE;
+
+HANDLE LogFile = INVALID_HANDLE_VALUE;
+#define LOGNAME (LPTSTR) TEXT("c:\\nwapi16.log")
+
+LONG NwMaxDump = SERVERNAME_LENGTH * MC; //128;
+
+#define ERR_BUF_SIZE 260
+#define NAME_BUF_SIZE 260
+
+extern UCHAR CpuInProtectMode;
+
+LPSTR
+ConvertFlagsToString(
+ IN WORD FlagsRegister,
+ OUT LPSTR Buffer
+ );
+
+WORD
+GetFlags(
+ VOID
+ );
+
+VOID
+HexDumpLine(
+ PCHAR pch,
+ ULONG len,
+ PCHAR s,
+ PCHAR t
+ );
+
+VOID
+DebugControl(
+ int Command
+ )
+/*++
+
+Routine Description:
+
+ This routine controls what we output as debug information and where.
+
+Arguments:
+
+ IN int Command
+
+Return Value:
+
+ none.
+
+--*/
+{
+
+ switch (Command) {
+ case 0:
+ UseLogFile = TRUE;
+ break;
+
+ case 1:
+ UseConsole = TRUE;
+ break;
+
+ case 2:
+ if (LogFile != INVALID_HANDLE_VALUE) {
+ CloseHandle(LogFile);
+ LogFile = INVALID_HANDLE_VALUE;
+ }
+ UseLogFile = FALSE;
+ UseConsole = FALSE;
+ break;
+
+ case 8:
+ Verbose = TRUE; // Same as 4 only chatty
+ DebugCtrl = 4;
+
+ case 4:
+ UseLogFile = TRUE;
+ break;
+
+ }
+ NwPrint(("DebugControl %x\n", Command ));
+}
+
+VOID
+NwPrintf(
+ char *Format,
+ ...
+ )
+/*++
+
+Routine Description:
+
+ This routine is equivalent to printf with the output being directed to
+ stdout.
+
+Arguments:
+
+ IN char *Format - Supplies string to be output and describes following
+ (optional) parameters.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ va_list arglist;
+ char OutputBuffer[200];
+ int length;
+
+ if (( UseConsole == FALSE ) &&
+ ( UseLogFile == FALSE )) {
+ return;
+ }
+
+
+ va_start( arglist, Format );
+
+ length = _vsnprintf( OutputBuffer, sizeof(OutputBuffer)-1, Format, arglist );
+ if (length < 0) {
+ return;
+ }
+
+ OutputBuffer[sizeof(OutputBuffer)-1] = '\0'; // in-case length= 199;
+
+ va_end( arglist );
+
+ if ( UseConsole ) {
+ DbgPrint( "%s", OutputBuffer );
+ } else {
+
+ if ( LogFile == INVALID_HANDLE_VALUE ) {
+ if ( UseLogFile ) {
+ LogFile = CreateFile( LOGNAME,
+ GENERIC_WRITE,
+ FILE_SHARE_WRITE | FILE_SHARE_READ,
+ NULL,
+ TRUNCATE_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL );
+
+ if (LogFile == INVALID_HANDLE_VALUE) {
+ LogFile = CreateFile( LOGNAME,
+ GENERIC_WRITE,
+ FILE_SHARE_WRITE | FILE_SHARE_READ,
+ NULL,
+ OPEN_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL );
+ }
+
+ if ( LogFile == INVALID_HANDLE_VALUE ) {
+ UseLogFile = FALSE;
+ return;
+ }
+ }
+ }
+
+ WriteFile( LogFile , (LPVOID )OutputBuffer, length, &length, NULL );
+ }
+
+} // NwPrintf
+
+void
+FormattedDump(
+ PCHAR far_p,
+ LONG len
+ )
+/*++
+
+Routine Description:
+
+ This routine outputs a buffer in lines of text containing hex and
+ printable characters.
+
+Arguments:
+
+ IN far_p - Supplies buffer to be displayed.
+ IN len - Supplies the length of the buffer in bytes.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ ULONG l;
+ char s[80], t[80];
+
+ if (( UseConsole == FALSE ) &&
+ ( UseLogFile == FALSE )) {
+ return;
+ }
+
+ if (( len > NwMaxDump ) ||
+ ( len < 0 )) {
+ len = NwMaxDump;
+ }
+
+ while (len) {
+ l = len < 16 ? len : 16;
+
+ NwPrint(("%lx ", far_p));
+ HexDumpLine (far_p, l, s, t);
+ NwPrint(("%s%.*s%s\n", s, 1 + ((16 - l) * 3), "", t));
+
+ len -= l;
+ far_p += l;
+ }
+}
+
+VOID
+HexDumpLine(
+ PCHAR pch,
+ ULONG len,
+ PCHAR s,
+ PCHAR t
+ )
+/*++
+
+Routine Description:
+
+ This routine builds a line of text containing hex and printable characters.
+
+Arguments:
+
+ IN pch - Supplies buffer to be displayed.
+ IN len - Supplies the length of the buffer in bytes.
+ IN s - Supplies the start of the buffer to be loaded with the string
+ of hex characters.
+ IN t - Supplies the start of the buffer to be loaded with the string
+ of printable ascii characters.
+
+
+Return Value:
+
+ none.
+
+--*/
+{
+ static UCHAR rghex[] = "0123456789ABCDEF";
+
+ UCHAR c;
+ UCHAR *hex, *asc;
+
+
+ hex = s;
+ asc = t;
+
+ *(asc++) = '*';
+ while (len--) {
+ c = *(pch++);
+ *(hex++) = rghex [c >> 4] ;
+ *(hex++) = rghex [c & 0x0F];
+ *(hex++) = ' ';
+ *(asc++) = (c < ' ' || c > '~') ? (CHAR )'.' : c;
+ }
+ *(asc++) = '*';
+ *asc = 0;
+ *hex = 0;
+
+}
+
+
+VOID
+DisplayExtendedError(VOID)
+{
+ TCHAR errorBuf[ERR_BUF_SIZE];
+ TCHAR nameBuf[NAME_BUF_SIZE];
+ DWORD errorCode;
+ DWORD status;
+
+ status = WNetGetLastError(
+ &errorCode,
+ errorBuf,
+ ERR_BUF_SIZE,
+ nameBuf,
+ NAME_BUF_SIZE);
+
+ if(status != WN_SUCCESS) {
+ NwPrint(("nwapi32: WNetGetLastError failed %d\n",status));
+ return;
+ }
+ NwPrint(("nwapi32: EXTENDED ERROR INFORMATION: (from GetLastError)\n"));
+ NwPrint(("nwapi32: Code: %d\n",errorCode));
+ NwPrint(("nwapi32: Description: "FORMAT_LPSTR"\n",errorBuf));
+ NwPrint(("nwapi32: Provider: "FORMAT_LPSTR"\n\n",nameBuf));
+ return;
+}
+
+VOID
+VrDumpRealMode16BitRegisters(
+ IN BOOL DebugStyle
+ )
+
+/*++
+
+Routine Description:
+
+ Displays dump of 16-bit
+ real-mode 80286 registers - gp registers (8), segment registers (4), flags
+ register (1) instruction pointer register (1)
+
+Arguments:
+
+ DebugStyle - determines look of output:
+
+DebugStyle == TRUE:
+
+ax=1111 bx=2222 cx=3333 dx=4444 sp=5555 bp=6666 si=7777 di=8888
+ds=aaaa es=bbbb ss=cccc cs=dddd ip=iiii fl fl fl fl fl fl fl fl
+
+DebugStyle == FALSE:
+
+cs:ip=cccc:iiii ss:sp=ssss:pppp bp=bbbb ax=1111 bx=2222 cx=3333 dx=4444
+ds:si=dddd:ssss es:di=eeee:dddd flags[ODIxSZxAxPxC]=fl fl fl fl fl fl fl fl
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ char flags_string[25];
+
+ if (( UseConsole == FALSE ) &&
+ ( UseLogFile == FALSE )) {
+ return;
+ }
+
+ if (CpuInProtectMode) {
+ NwPrint(( "Protect Mode:\n"));
+ }
+
+ if (DebugStyle) {
+ NwPrint((
+ "ax=%04x bx=%04x cx=%04x dx=%04x sp=%04x bp=%04x si=%04x di=%04x\n"
+ "ds=%04x es=%04x ss=%04x cs=%04x ip=%04x %s\n\n",
+
+ pNwDosTable->SavedAx, //getAX(),
+ getBX(),
+ getCX(),
+ getDX(),
+ getSP(),
+ getBP(),
+ getSI(),
+ getDI(),
+ getDS(),
+ getES(),
+ getSS(),
+ getCS(),
+ getIP(),
+ ConvertFlagsToString(GetFlags(), flags_string)
+ ));
+ } else {
+ NwPrint((
+ "cs:ip=%04x:%04x ss:sp=%04x:%04x bp=%04x ax=%04x bx=%04x cx=%04x dx=%04x\n"
+ "ds:si=%04x:%04x es:di=%04x:%04x flags[ODITSZxAxPxC]=%s\n\n",
+ getCS(),
+ getIP(),
+ getSS(),
+ getSP(),
+ getBP(),
+ pNwDosTable->SavedAx, //getAX(),
+ getBX(),
+ getCX(),
+ getDX(),
+ getDS(),
+ getSI(),
+ getES(),
+ getDI(),
+ ConvertFlagsToString(GetFlags(), flags_string)
+ ));
+ }
+}
+
+LPSTR
+ConvertFlagsToString(
+ IN WORD FlagsRegister,
+ OUT LPSTR Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ Given a 16-bit word, interpret bit positions as for x86 Flags register
+ and produce descriptive string of flags state (as per debug) eg:
+
+ NV UP DI PL NZ NA PO NC ODItSZxAxPxC = 000000000000b
+ OV DN EI NG ZR AC PE CY ODItSZxAxPxC = 111111111111b
+
+ Trap Flag (t) is not dumped since this has no interest for programs which
+ are not debuggers or don't examine program execution (ie virtually none)
+
+Arguments:
+
+ FlagsRegister - 16-bit flags
+ Buffer - place to store string. Requires 25 bytes inc \0
+
+Return Value:
+
+ Address of <Buffer>
+
+--*/
+
+{
+ static char* flags_states[16][2] = {
+ //0 1
+ "NC", "CY", // CF (0x0001) - Carry
+ "", "", // x (0x0002)
+ "PO", "PE", // PF (0x0004) - Parity
+ "", "", // x (0x0008)
+ "NA", "AC", // AF (0x0010) - Aux (half) carry
+ "", "", // x (0x0020)
+ "NZ", "ZR", // ZF (0x0040) - Zero
+ "PL", "NG", // SF (0x0080) - Sign
+ "", "", // TF (0x0100) - Trap (not dumped)
+ "DI", "EI", // IF (0x0200) - Interrupt
+ "UP", "DN", // DF (0x0400) - Direction
+ "NV", "OV", // OF (0x0800) - Overflow
+ "", "", // x (0x1000) - (I/O Privilege Level) (not dumped)
+ "", "", // x (0x2000) - (I/O Privilege Level) (not dumped)
+ "", "", // x (0x4000) - (Nested Task) (not dumped)
+ "", "" // x (0x8000)
+ };
+ int i;
+ WORD bit;
+ BOOL on;
+
+ *Buffer = 0;
+ for (bit=0x0800, i=11; bit; bit >>= 1, --i) {
+ on = (BOOL)((FlagsRegister & bit) == bit);
+ if (flags_states[i][on][0]) {
+ strcat(Buffer, flags_states[i][on]);
+ strcat(Buffer, " ");
+ }
+ }
+ return Buffer;
+}
+
+WORD
+GetFlags(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Supplies the missing softpc function
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Conglomerates softpc flags into x86 flags word
+
+--*/
+
+{
+ WORD flags;
+
+ flags = (WORD)getCF();
+ flags |= (WORD)getPF() << 2;
+ flags |= (WORD)getAF() << 4;
+ flags |= (WORD)getZF() << 6;
+ flags |= (WORD)getSF() << 7;
+ flags |= (WORD)getIF() << 9;
+ flags |= (WORD)getDF() << 10;
+ flags |= (WORD)getOF() << 11;
+
+ return flags;
+}
+
+VOID
+VrDumpNwData(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Dumps out the state of the 16 bit datastructures.
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ int index;
+ int Drive;
+
+ if (Verbose == FALSE) {
+ return;
+ }
+
+ NwPrint(( "Preferred = %x, Primary = %x\n",
+ pNwDosTable->PreferredServer,
+ pNwDosTable->PrimaryServer));
+
+ for (index = 0; index < MC; index++) {
+
+
+ if ((PUCHAR)pNwDosTable->ServerNameTable[index][0] != 0 ) {
+
+ if (pNwDosTable->ConnectionIdTable[index].ci_InUse != IN_USE) {
+ NwPrint(("Warning Connection not in use %x: %x\n",
+ index,
+ pNwDosTable->ConnectionIdTable[index].ci_InUse));
+ }
+
+ NwPrint((" Server %d = %s, Connection = %d\n",
+ index,
+ (PUCHAR)pNwDosTable-> ServerNameTable[index],
+ (((pNwDosTable->ConnectionIdTable[index]).ci_ConnectionHi * 256) +
+ ( pNwDosTable-> ConnectionIdTable[index]).ci_ConnectionLo )));
+ } else {
+ if (pNwDosTable->ConnectionIdTable[index].ci_InUse != FREE) {
+ NwPrint(("Warning Connection in use but name is null %x: %x\n",
+ index,
+ pNwDosTable->ConnectionIdTable[index]));
+ }
+ }
+ }
+
+ for (Drive = 0; Drive < MD; Drive++ ) {
+
+
+ if (pNwDosTable->DriveFlagTable[Drive] & 3) {
+ NwPrint(("%c=%x on server %d,",'A' + Drive,
+ pNwDosTable->DriveFlagTable[Drive],
+ pNwDosTable->DriveIdTable[ Drive ] ));
+ }
+
+ }
+ NwPrint(("\n"));
+}
+#endif
+
diff --git a/private/nw/nw16/dll/locks.c b/private/nw/nw16/dll/locks.c
new file mode 100644
index 000000000..f3d9ff362
--- /dev/null
+++ b/private/nw/nw16/dll/locks.c
@@ -0,0 +1,677 @@
+
+/*++
+
+Copyright (c) 1993/4 Microsoft Corporation
+
+Module Name:
+
+ Locks.c
+
+Abstract:
+
+ This module implements the routines for the NetWare
+ 16 bit support to perform the synchonization api's
+
+Author:
+
+ Colin Watson [ColinW] 07-Dec-1993
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+UCHAR LockMode = 0;
+
+BOOLEAN Tickle[MC];
+
+NTSTATUS
+Sem(
+ UCHAR Function,
+ UCHAR Connection
+ );
+
+VOID
+Locks(
+ USHORT Command
+ )
+/*++
+
+Routine Description:
+
+ Implements all the locking operations
+
+Arguments:
+
+ Command - supplies Applications AX.
+
+Return Value:
+
+ Return status.
+
+--*/
+{
+ UCHAR Function = Command & 0x00ff;
+ USHORT Operation = Command & 0xff00;
+ CONN_INDEX Connection;
+ NTSTATUS status = STATUS_SUCCESS;
+ PUCHAR Request;
+ ULONG RequestLength;
+ WORD Timeout;
+
+ if ( Operation != 0xCF00) {
+
+ //
+ // Connection does not need to be initialised for CF00 because
+ // we have to loop through all connections. Its harmful because
+ // a CF00 is created during ProcessExit(). If we call selectconnection
+ // and there is no server available this will make process exit
+ // really slow.
+ //
+
+ Connection = SelectConnection();
+ if (Connection == 0xff) {
+ setAL(0xff);
+ return;
+ }
+
+ if ( ServerHandles[Connection] == NULL ) {
+
+ status = OpenConnection( Connection );
+
+ if (!NT_SUCCESS(status)) {
+ setAL((UCHAR)RtlNtStatusToDosError(status));
+ return;
+ }
+ }
+ }
+
+ switch ( Operation ) {
+
+ case 0xBC00: // Log physical record
+
+ status = NwlibMakeNcp(
+ GET_NT_HANDLE(),
+ NWR_ANY_HANDLE_NCP(0x1A),
+ 17, // RequestSize
+ 0, // ResponseSize
+ "b_wwwww",
+ Function,
+ 6, // Leave space for NetWare handle
+ getCX(),getDX(),
+ getSI(),getDI(),
+ getBP());
+ break;
+
+ case 0xBD00: // Physical Unlock
+ status = NwlibMakeNcp(
+ GET_NT_HANDLE(),
+ NWR_ANY_HANDLE_NCP(0x1C),
+ 15, // RequestSize
+ 0, // ResponseSize
+ "b_wwww",
+ Function,
+ 6, // Leave space for NetWare handle
+ getCX(),getDX(),
+ getSI(),getDI());
+
+ break;
+
+ case 0xBE00: // Clear physical record
+
+ status = NwlibMakeNcp(
+ GET_NT_HANDLE(),
+ NWR_ANY_HANDLE_NCP(0x1E),
+ 15, // RequestSize
+ 0, // ResponseSize
+ "b_wwww",
+ Function,
+ 6, // Leave space for NetWare handle
+ getCX(),getDX(),
+ getSI(),getDI());
+
+ break;
+
+ case 0xC200: // Physical Lock set
+ status = NwlibMakeNcp(
+ ServerHandles[Connection],
+ NWR_ANY_F2_NCP(0x1B),
+ 3, // RequestSize
+ 0, // ResponseSize
+ "bw",
+ Function,
+ getBP());
+ break;
+
+ case 0xC300: // Release Physical Record Set
+ status = NwlibMakeNcp(
+ ServerHandles[Connection],
+ NWR_ANY_F2_NCP(0x1D),
+ 0, // RequestSize
+ 0, // ResponseSize
+ "");
+ break;
+
+ case 0xC400: // Clear Physical Record Set
+ status = NwlibMakeNcp(
+ ServerHandles[Connection],
+ NWR_ANY_F2_NCP(0x1F), // Clear Physical Record Set
+ 0, // RequestSize
+ 0, // ResponseSize
+ "");
+ break;
+
+ case 0xC500: // All Semaphore operations
+ status = Sem(Function, Connection);
+ break;
+
+ case 0xC600: // Set/Get Lock mode
+
+ if (Function != 2) {
+ LockMode = Function;
+ }
+
+ setAL(LockMode);
+ return; // avoid setting AL to status at the end of this routine
+ break;
+
+ case 0xCB00: // Lock File Set
+
+ if (LockMode == 0) {
+ if (getDL()) {
+ Timeout = 0xffff;
+ } else {
+ Timeout = 0;
+ }
+ } else {
+ Timeout = getBP();
+ }
+
+ for (Connection = 0; Connection < MC; Connection++) {
+ if (Tickle[Connection]) {
+ status = NwlibMakeNcp(
+ ServerHandles[Connection],
+ NWR_ANY_F2_NCP(0x04),
+ 2, // RequestSize
+ 0, // ResponseSize
+ "w",
+ Timeout);
+ if (!NT_SUCCESS(status)) {
+ break;
+ }
+ }
+ }
+ break;
+
+ case 0xCD00: // Release File Set
+ case 0xCF00: // Clear File Set
+ for (Connection = 0; Connection < MC; Connection++) {
+ if (Tickle[Connection]) {
+ status = NwlibMakeNcp(
+ ServerHandles[Connection],
+ (Operation == 0xCD00) ? NWR_ANY_F2_NCP(0x06): NWR_ANY_F2_NCP(0x08),
+ 0, // RequestSize
+ 0, // ResponseSize
+ "");
+ if (!NT_SUCCESS(status)) {
+ break;
+ }
+
+ if (Operation == 0xCF00) {
+ Tickle[Connection] = FALSE;
+ }
+ }
+ }
+
+ break;
+
+ case 0xD000: // Log Logical Record
+
+ Request = GetVDMPointer (
+ (ULONG)((getDS() << 16)|getDX()),
+ sizeof(UCHAR),
+ IS_PROTECT_MODE());
+
+ RequestLength = Request[0] + 1;
+
+ Request = GetVDMPointer (
+ (ULONG)((getDS() << 16)|getDX()),
+ RequestLength,
+ IS_PROTECT_MODE());
+
+ status = NwlibMakeNcp(
+ ServerHandles[Connection],
+ NWR_ANY_F2_NCP(0x09),
+ RequestLength + 5, // RequestSize
+ 0, // ResponseSize
+ "bwbr",
+ (LockMode) ? Function : 0,
+ (LockMode) ? getBP() : 0,
+ RequestLength,
+ Request, RequestLength );
+ break;
+
+ case 0xD100: // Lock Logical Record Set
+
+ if (LockMode == 0) {
+ if (getDL()) {
+ Timeout = 0xffff;
+ } else {
+ Timeout = 0;
+ }
+ } else {
+ Timeout = getBP();
+ }
+
+ status = NwlibMakeNcp(
+ ServerHandles[Connection],
+ NWR_ANY_F2_NCP(0x0A),
+ 3, // RequestSize
+ 0, // ResponseSize
+ "bw",
+ (LockMode) ? Function : 0,
+ Timeout);
+ break;
+
+ case 0xD200: // Release File
+ case 0xD400: // Clear Logical Record
+ Request = GetVDMPointer (
+ (ULONG)((getDS() << 16)|getDX()),
+ sizeof(UCHAR),
+ IS_PROTECT_MODE());
+
+ RequestLength = Request[0]+1;
+
+ Request = GetVDMPointer (
+ (ULONG)((getDS() << 16)|getDX()),
+ RequestLength,
+ IS_PROTECT_MODE());
+
+ status = NwlibMakeNcp(
+ ServerHandles[Connection],
+ (Operation == 0xD200) ? NWR_ANY_F2_NCP(0x0C) :
+ NWR_ANY_F2_NCP(0x0B),
+ RequestLength+1,
+ 0, // ResponseSize
+ "br",
+ RequestLength,
+ Request, RequestLength );
+ break;
+
+ case 0xD300:
+ status = NwlibMakeNcp(
+ ServerHandles[Connection],
+ NWR_ANY_F2_NCP(0x13),
+ 0, // RequestSize
+ 0, // ResponseSize
+ "");
+ break;
+
+
+ case 0xD500: // Clear Logical Record Set
+ status = NwlibMakeNcp(
+ ServerHandles[Connection],
+ NWR_ANY_F2_NCP(0x0E),
+ 0, // RequestSize
+ 0, // ResponseSize
+ "");
+ break;
+
+ case 0xEB00: // Log File
+ case 0xEC00: // Release File
+ case 0xED00: // Clear File
+ {
+ UCHAR DirHandle;
+ HANDLE Win32DirectoryHandle = 0;
+ PUCHAR ptr;
+
+ Request = GetVDMPointer (
+ (ULONG)((getDS() << 16)|getDX()),
+ 256 * sizeof(UCHAR),
+ IS_PROTECT_MODE());
+
+ RequestLength = strlen(Request);
+
+ // Find DirHandle
+ ptr = Request;
+ while ( (*ptr != 0) &&
+ (!IS_ASCII_PATH_SEPARATOR(*ptr)) &&
+ (*ptr != ':' )) {
+ ptr++;
+ }
+
+ if (IS_ASCII_PATH_SEPARATOR(*ptr)) {
+ int ServerNameLength = ptr - Request;
+ PUCHAR scanptr = ptr;
+
+ //
+ // Make sure there is a ":" further up the name otherwise
+ // we could confuse foo\bar.txt with a server called foo
+ //
+
+ while ( (*scanptr != 0) &&
+ (*scanptr != ':' )) {
+ scanptr++;
+ }
+
+ if (*scanptr) {
+ //
+ // Name is of the form server\sys:foo\bar.txt
+ // set connection appropriately.
+ //
+
+ for (Connection = 0; Connection < MC ; Connection++ ) {
+
+ //
+ // Look for server foo avoiding foobar.
+ //
+
+ if ((pNwDosTable->ConnectionIdTable[Connection].ci_InUse ==
+ IN_USE) &&
+ (!memcmp( pNwDosTable->ServerNameTable[Connection],
+ Request,
+ ServerNameLength)) &&
+ (pNwDosTable->ServerNameTable[Connection][ServerNameLength] ==
+ '\0')) {
+ break; // Connection is the correct server
+ }
+ }
+
+ //
+ // Move Request to after the seperator and ptr to the ":"
+ //
+
+ RequestLength -= ptr + sizeof(UCHAR) - Request;
+ Request = ptr + sizeof(UCHAR);
+ ptr = scanptr;
+ }
+ }
+
+ if (*ptr) {
+
+ //
+ // Name of form "sys:foo\bar.txt" this gives the server
+ // all the information required.
+ //
+
+ DirHandle = 0;
+
+ if (Request[1] == ':') {
+
+ UCHAR Drive = tolower(Request[0])-'a';
+
+ //
+ // Its a normal (redirected) drive k:foo\bar.txt.
+ // Use the drive tables to give the connection and handle.
+ //
+
+ Connection = pNwDosTable->DriveIdTable[ Drive ] - 1;
+ DirHandle = pNwDosTable->DriveHandleTable[Drive];
+
+ if (DirHandle == 0) {
+ DirHandle = (UCHAR)GetDirectoryHandle2(Drive);
+ }
+ Request += 2; // skip "k:"
+ RequestLength -= 2;
+ }
+
+ } else {
+
+ WCHAR Curdir[256];
+
+ //
+ // Name of form "foo\bar.txt"
+ //
+
+ GetCurrentDirectory(sizeof(Curdir), Curdir);
+
+ Win32DirectoryHandle =
+ CreateFileW( Curdir,
+ 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS,
+ 0);
+
+ if (Win32DirectoryHandle != INVALID_HANDLE_VALUE) {
+ DWORD BytesReturned;
+
+ if ( DeviceIoControl(
+ Win32DirectoryHandle,
+ IOCTL_NWR_RAW_HANDLE,
+ NULL,
+ 0,
+ (PUCHAR)&DirHandle,
+ sizeof(DirHandle),
+ &BytesReturned,
+ NULL ) == FALSE ) {
+
+ CloseHandle( Win32DirectoryHandle );
+ setAL(0xff);
+ return;
+
+ }
+
+ } else {
+
+ setAL(0xff);
+ return;
+ }
+ }
+
+ if (Operation == 0xEB00) {
+ status = NwlibMakeNcp(
+ ServerHandles[Connection],
+ NWR_ANY_F2_NCP(0x03),
+ RequestLength + 5,
+ 0, // ResponseSize
+ "bbwbr",
+ DirHandle,
+ (LockMode) ? Function : 0,
+ (LockMode) ? getBP() : 0,
+ RequestLength,
+ Request, RequestLength );
+
+ Tickle[Connection] = TRUE;
+
+ } else {
+ status = NwlibMakeNcp(
+ ServerHandles[Connection],
+ (Operation == 0xEC00 ) ?
+ NWR_ANY_F2_NCP(0x07) :
+ NWR_ANY_F2_NCP(0x05),
+ RequestLength + 2,
+ 0, // ResponseSize
+ "bbr",
+ DirHandle,
+ RequestLength,
+ Request, RequestLength );
+ }
+
+ if (Win32DirectoryHandle) {
+ CloseHandle( Win32DirectoryHandle );
+ }
+ }
+ break;
+
+ }
+
+ if (!NT_SUCCESS(status)) {
+ setAL((UCHAR)RtlNtStatusToDosError(status));
+ return;
+ } else {
+ setAL(0);
+ }
+}
+
+VOID
+InitLocks(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Reset the Tickle internal variables
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ ZeroMemory( Tickle, sizeof(Tickle));
+}
+
+VOID
+ResetLocks(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Reset the Locks for the current VDM. Called during process exit.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ Locks(0xCF00); // Clear all File sets.
+
+}
+
+NTSTATUS
+Sem(
+ UCHAR Function,
+ UCHAR Connection
+ )
+/*++
+
+Routine Description:
+
+ Build all NCPs for Semaphore support
+
+Arguments:
+
+ Function - Supplies the subfunction from AL
+
+ Connection - Supplies the server for the request
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PUCHAR Request;
+ NTSTATUS status;
+
+ switch (Function) {
+
+ UCHAR Value;
+ UCHAR OpenCount;
+ WORD HandleHigh, HandleLow;
+
+ case 0: //OpenSemaphore
+
+ Request = GetVDMPointer (
+ (ULONG)((getDS() << 16)|getDX()),
+ 256 * sizeof(UCHAR),
+ IS_PROTECT_MODE());
+
+ NwPrint(("Nw16: OpenSemaphore\n"));
+
+ status = NwlibMakeNcp(
+ ServerHandles[Connection],
+ NWR_ANY_F2_NCP(0x20),
+ Request[0] + 3, // RequestSize
+ 5, // ResponseSize
+ "bbr|wwb",
+ 0,
+ getCL(), // Semaphore Value
+ Request, Request[0] + 1,
+
+ &HandleHigh, &HandleLow,
+ &OpenCount);
+
+
+ if (NT_SUCCESS(status)) {
+ setBL(OpenCount);
+ setCX(HandleHigh);
+ setDX(HandleLow);
+ }
+
+ break;
+
+ case 1: // ExamineSemaphore
+
+ NwPrint(("Nw16: ExamineSemaphore\n"));
+ status = NwlibMakeNcp(
+ ServerHandles[Connection],
+ NWR_ANY_F2_NCP(0x20),
+ 5, // RequestSize
+ 2, // ResponseSize
+ "bww|bb",
+ 1,
+ getCX(),getDX(),
+
+ &Value,
+ &OpenCount);
+
+ if (NT_SUCCESS(status)) {
+ setCX(Value);
+ setDL(OpenCount);
+ }
+ break;
+
+ case 2: // WaitOnSemaphore
+ NwPrint(("Nw16: WaitOnSemaphore\n"));
+ status = NwlibMakeNcp(
+ ServerHandles[Connection],
+ NWR_ANY_F2_NCP(0x20),
+ 7, // RequestSize
+ 0, // ResponseSize
+ "bwww",
+ 2,
+ getCX(),getDX(),
+ getBP());
+ break;
+
+ case 3: // SignalSemaphore
+ NwPrint(("Nw16: SignalSemaphore\n"));
+ case 4: // CloseSemaphore
+
+ if (Function == 4) {
+ NwPrint(("Nw16: CloseSemaphore\n"));
+ }
+
+ status = NwlibMakeNcp( // Close and Signal
+ ServerHandles[Connection],
+ NWR_ANY_F2_NCP(0x20),
+ 5, // RequestSize
+ 0, // ResponseSize
+ "bww",
+ Function,
+ getCX(),getDX());
+ break;
+
+ default:
+ NwPrint(("Nw16: Unknown Semaphore operation %d\n", Function));
+ break;
+ }
+ return status;
+}
diff --git a/private/nw/nw16/dll/makefile b/private/nw/nw16/dll/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/nw/nw16/dll/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/nw16/dll/ncp.c b/private/nw/nw16/dll/ncp.c
new file mode 100644
index 000000000..c82cca43b
--- /dev/null
+++ b/private/nw/nw16/dll/ncp.c
@@ -0,0 +1,3383 @@
+/*++
+
+Copyright (c) 1993/4 Microsoft Corporation
+
+Module Name:
+
+ ncp.c
+
+Abstract:
+
+ Contains routine which accepts the bop from a 16 bit
+ application and processes the request appropriately.
+ Normally it performes an NCP exchange on behalf of the
+ application.
+
+Author:
+
+ Colin Watson (colinw) 07-Jul-1993
+
+Environment:
+
+
+Revision History:
+
+
+--*/
+
+#include "procs.h"
+
+#define BASE_DOS_ERROR ((NTSTATUS )0xC0010000L)
+#define CLIENT_ID_STRING "MSDOS\0V5.00\0IBM_PC\0IBM"
+
+#include <packon.h>
+typedef struct _TTSOUTPACKET {
+ UCHAR SubFunction;
+ USHORT cx;
+ USHORT dx;
+} TTSOUTPACKET, *PTTSOUTPACKET ;
+
+typedef struct _TTSINPACKET{
+ USHORT cx;
+ USHORT dx;
+} TTSINPACKET, *PTTSINPACKET;
+
+#include <packoff.h>
+
+VOID
+InitDosTable(
+ PNWDOSTABLE pdt
+ );
+
+VOID
+LoadPreferredServerName(
+ VOID
+ );
+
+VOID
+ProcessResourceArray(
+ LPNETRESOURCE NetResource,
+ DWORD NumElements
+ );
+
+VOID
+ProcessResource(
+ LPNETRESOURCE NetResource
+ );
+
+VOID
+SendNCP(
+ ULONG Command
+ );
+
+VOID
+SendF2NCP(
+ ULONG Command
+ );
+
+UCHAR
+AttachmentControl(
+ ULONG Command
+ );
+
+VOID
+SendNCP2(
+ ULONG Command,
+ PUCHAR Request,
+ ULONG RequestLength,
+ PUCHAR Reply,
+ ULONG ReplyLength
+ );
+
+VOID
+CloseConnection(
+ CONN_INDEX Connection
+ );
+
+NTSTATUS
+InitConnection(
+ CONN_INDEX Connection
+ );
+
+VOID
+GetDirectoryHandle(
+ VOID
+ );
+
+VOID
+LoadDriveHandleTable(
+ VOID
+ );
+
+VOID
+AllocateDirectoryHandle(
+ VOID
+ );
+
+VOID
+ResetDrive(
+ UCHAR Drive
+ );
+
+VOID
+AllocateDirectoryHandle2(
+ VOID
+ );
+
+PWCHAR
+BuildUNC(
+ IN PUCHAR aName,
+ IN ULONG aLength
+ );
+
+VOID
+GetServerDateAndTime(
+ VOID
+ );
+
+VOID
+GetShellVersion(
+ IN USHORT Command
+ );
+
+VOID
+TTS(
+ VOID
+ );
+
+VOID
+OpenCreateFile(
+ VOID
+ );
+
+BOOL
+IsItNetWare(
+ PUCHAR Name
+ );
+
+VOID
+SetCompatibility(
+ VOID
+ );
+
+VOID
+OpenQueueFile(
+ VOID
+ );
+
+VOID
+AttachHandle(
+ VOID
+ );
+
+VOID
+ProcessExit(
+ VOID
+ );
+
+VOID
+SystemLogout(
+ VOID
+ );
+
+VOID
+ServerFileCopy(
+ VOID
+ );
+
+VOID
+SetStatus(
+ NTSTATUS Status
+ );
+
+//
+// The following pointer contains the 32 bit virtual address of where
+// the nw16.exe tsr holds the workstation structures.
+//
+
+PNWDOSTABLE pNwDosTable;
+
+//
+// Global variables used to hold the state for this process
+//
+
+UCHAR OriginalPrimary = 0;
+HANDLE ServerHandles[MC];
+
+HANDLE Win32DirectoryHandleTable[MD];
+PWCHAR Drives[MD]; // Strings such as R: or a unc name
+
+UCHAR SearchDriveTable[16];
+
+
+BOOLEAN Initialized = FALSE;
+BOOLEAN TablesValid = FALSE; // Reload each time a process starts
+BOOLEAN DriveHandleTableValid = FALSE; // Reload first time process does NW API
+
+WORD DosTableSegment;
+WORD DosTableOffset;
+
+extern UCHAR LockMode;
+
+#if NWDBG
+BOOL GotDebugState = FALSE;
+extern int DebugCtrl;
+#endif
+
+
+VOID
+Nw16Register(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function is called by wow when nw16.sys is loaded.
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+{
+ DWORD status;
+ HANDLE enumHandle;
+ LPNETRESOURCE netResource;
+ DWORD numElements;
+ DWORD bufferSize;
+ DWORD dwScope = RESOURCE_CONNECTED;
+
+ NwPrint(("Nw16Register\n"));
+
+ if ( !Initialized) {
+ UCHAR CurDir[256];
+ DosTableSegment = getAX();
+ DosTableOffset = getDX();
+
+ //
+ // this call always made from Real Mode (hence FALSE for last param)
+ //
+
+ pNwDosTable = (PNWDOSTABLE) GetVDMPointer (
+ (ULONG)((DosTableSegment << 16)|DosTableOffset),
+ sizeof(NWDOSTABLE),
+ FALSE
+ );
+
+ InitDosTable( pNwDosTable );
+
+ if ((GetCurrentDirectoryA(sizeof(CurDir)-1, CurDir) >= 2) &&
+ (CurDir[1] = ':')) {
+ pNwDosTable->CurrentDrive = tolower(CurDir[0]) - 'a';
+ }
+
+ InitLocks();
+ }
+
+
+#if NWDBG
+ {
+ WCHAR Value[80];
+
+ if (GetEnvironmentVariableW(L"NWDEBUG",
+ Value,
+ sizeof(Value)/sizeof(Value[0]) - 1)) {
+
+ DebugCtrl = Value[0] - '0';
+
+ // 0 Use logfile
+ // 1 Use debugger
+ // 2/undefined No debug output
+ // 4 Use logfile, close on process exit
+ // 8 Use logfile, verbose, close on process exit
+
+ DebugControl( DebugCtrl );
+
+ GotDebugState = TRUE; // Don't look again until process exits vdm
+ }
+ }
+#endif
+
+ LoadPreferredServerName();
+
+ //
+ // Attempt to allow for MD drives
+ //
+
+ bufferSize = (MD*sizeof(NETRESOURCE))+1024;
+
+ netResource = (LPNETRESOURCE) LocalAlloc(LPTR, bufferSize);
+
+ if (netResource == NULL) {
+
+ NwPrint(("Nw16Register: LocalAlloc Failed %d\n",GetLastError));
+ setCF(1);
+ return;
+ }
+
+ //-----------------------------------//
+ // Get a handle for a top level enum //
+ //-----------------------------------//
+ status = NPOpenEnum(
+ dwScope,
+ RESOURCETYPE_DISK,
+ 0,
+ NULL,
+ &enumHandle);
+
+ if ( status != WN_SUCCESS) {
+ NwPrint(("Nw16Register:WNetOpenEnum failed %d\n",status));
+
+ //
+ // If there is an extended error, display it.
+ //
+ if (status == WN_EXTENDED_ERROR) {
+ DisplayExtendedError();
+ }
+ goto LoadLocal;
+ }
+
+ //-----------------------------//
+ // Enumerate the disk devices. //
+ //-----------------------------//
+
+ numElements = 0xffffffff;
+
+ status = NwEnumConnections(
+ enumHandle,
+ &numElements,
+ netResource,
+ &bufferSize,
+ TRUE); // Include implicit connections
+
+ if ( status != WN_SUCCESS) {
+ NwPrint(("Nw16Register:NwEnumResource failed %d\n",status));
+
+ //
+ // If there is an extended error, display it.
+ //
+ if (status == WN_EXTENDED_ERROR) {
+ DisplayExtendedError();
+ }
+ WNetCloseEnum(enumHandle);
+ goto LoadLocal;
+ }
+
+ //------------------------------------------//
+ // Close the EnumHandle & print the results //
+ //------------------------------------------//
+
+ status = NPCloseEnum(enumHandle);
+ if (status != WN_SUCCESS) {
+ NwPrint(("Nw16Register:WNetCloseEnum failed %d\n",status));
+ //
+ // If there is an extended error, display it.
+ //
+ if (status == WN_EXTENDED_ERROR) {
+ DisplayExtendedError();
+ }
+ goto LoadLocal;
+
+ }
+
+ //----------------------------------------//
+ // Insert the results in the Nw Dos Table //
+ //----------------------------------------//
+
+ ProcessResourceArray( netResource, numElements);
+
+LoadLocal:
+
+ //
+ // Add the local devices so that NetWare apps don't try to map them
+ // to remote servers.
+ //
+
+ {
+ USHORT Drive;
+ WCHAR DriveString[4];
+ UINT Type;
+
+ DriveString[1] = L':';
+ DriveString[2] = L'\\';
+ DriveString[3] = L'\0';
+
+ //
+ // Hardwire A: and B: because hitting the floppy drive with
+ // GetDriveType takes too long.
+ //
+
+ pNwDosTable->DriveFlagTable[0] = LOCAL_DRIVE;
+ pNwDosTable->DriveFlagTable[1] = LOCAL_DRIVE;
+
+
+ for (Drive = 2; Drive <= 'Z' - 'A'; Drive++ ) {
+
+ if (pNwDosTable->DriveFlagTable[Drive] == 0) {
+ DriveString[0] = L'A' + Drive;
+ Type = GetDriveTypeW( DriveString );
+
+ //
+ // 0 means drive type cannot be determined, all others are
+ // provided by other filesystems.
+ //
+
+ if (Type != 1) {
+ pNwDosTable->DriveFlagTable[Drive] = LOCAL_DRIVE;
+ }
+ }
+ }
+
+#ifdef NWDBG
+ for (Drive = 0; Drive < MD; Drive++ ) {
+
+ DriveString[0] = L'A' + Drive;
+
+ NwPrint(("%c(%d)=%x,",'A' + Drive,
+ GetDriveTypeW( DriveString ),
+ pNwDosTable->DriveFlagTable[Drive] ));
+
+ if (!((Drive + 1) % 8)) {
+ NwPrint(("\n",0));
+ }
+ }
+
+ NwPrint(("\n"));
+#endif
+
+ }
+
+ if ( !Initialized ) {
+ Initialized = TRUE;
+ pNwDosTable->PrimaryServer = OriginalPrimary;
+ }
+
+ TablesValid = TRUE;
+
+ LocalFree(netResource);
+ setCF(0);
+
+ NwPrint(("Nw16Register: End\n"));
+}
+
+VOID
+LoadPreferredServerName(
+ VOID
+ )
+{
+
+ //
+ // If we already have a connection to somewhere then we already have a
+ // preferred servername of some sort.
+ //
+
+ if (pNwDosTable->ConnectionIdTable[0].ci_InUse == IN_USE) {
+ return;
+ }
+
+ //
+ // Load the server name table with the preferred/nearest server.
+ //
+
+ CopyMemory( pNwDosTable->ServerNameTable[0], "*", sizeof("*"));
+
+ if (NT_SUCCESS(OpenConnection( 0 ))) {
+
+ if( NT_SUCCESS(InitConnection(0)) ) {
+
+ //
+ // Close the handle so that the rdr can be stopped if
+ // user is not running a netware aware application.
+ //
+
+ CloseConnection(0);
+
+ pNwDosTable->PrimaryServer = 1;
+
+ return;
+
+ }
+
+ }
+
+ pNwDosTable->PrimaryServer = 0;
+
+}
+
+VOID
+ProcessResourceArray(
+ LPNETRESOURCE NetResource,
+ DWORD NumElements
+ )
+{
+ DWORD i;
+
+ for (i=0; i<NumElements ;i++ ) {
+ ProcessResource(&(NetResource[i]));
+ }
+ return;
+}
+
+VOID
+ProcessResource(
+ LPNETRESOURCE NetResource
+ )
+{
+ SERVERNAME ServerName;
+ int ServerNameLength;
+ int i;
+ int Connection;
+ BOOLEAN Found = FALSE;
+
+ //
+ // Extract Server Name from RemoteName, skipping first 2 chars that
+ // contain backslashes and taking care to handle entries that only
+ // contain a servername.
+ //
+
+ ServerNameLength = wcslen( NetResource->lpRemoteName );
+
+ ASSERT(NetResource->lpRemoteName[0] == '\\');
+ ASSERT(NetResource->lpRemoteName[1] == '\\');
+
+ for (i = 2; i <= ServerNameLength; i++) {
+
+ if ((NetResource->lpRemoteName[i] == '\\') ||
+ (i == ServerNameLength )){
+
+ ServerNameLength = i - 2;
+
+ WideCharToMultiByte(
+ CP_OEMCP,
+ 0,
+ &NetResource->lpRemoteName[2],
+ ServerNameLength,
+ ServerName,
+ sizeof( ServerName ),
+ NULL,
+ NULL );
+
+ CharUpperBuffA( ServerName, ServerNameLength );
+
+ ZeroMemory( &ServerName[ServerNameLength],
+ SERVERNAME_LENGTH - ServerNameLength );
+
+ break;
+ }
+
+ }
+
+ //
+ // Now try to find ServerName in the connection table. If there are
+ // more than MC servers in the table already then skip this one.
+ //
+
+ for (Connection = 0; Connection < MC ; Connection++ ) {
+ if ((pNwDosTable->ConnectionIdTable[Connection].ci_InUse == IN_USE) &&
+ (!memcmp( pNwDosTable->ServerNameTable[Connection], ServerName, SERVERNAME_LENGTH))) {
+ Found = TRUE;
+ break;
+ }
+ }
+
+
+ NwPrint(("Nw16ProcessResource Server: %s\n",ServerName));
+
+ if ( Found == FALSE ) {
+ for (Connection = 0; Connection < MC ; Connection++ ) {
+ if (pNwDosTable->ConnectionIdTable[Connection].ci_InUse == FREE) {
+
+ CopyMemory( pNwDosTable->ServerNameTable[Connection],
+ ServerName,
+ SERVERNAME_LENGTH);
+
+ if ((NT_SUCCESS(OpenConnection( (CONN_INDEX)Connection ))) &&
+ ( NT_SUCCESS(InitConnection( (CONN_INDEX)Connection ) ))) {
+
+ Found = TRUE;
+
+ } else {
+ // Couldn't talk to the server so ignore it.
+ ZeroMemory( pNwDosTable->ServerNameTable[Connection], SERVERNAME_LENGTH );
+
+ }
+
+ break; // Escape from for (Connection =...
+ }
+ }
+ }
+
+ //
+ // Build the drive id and drive flag tables. Entries 0 - 25
+ // are reserved for drives redirected to letters. We use drives
+ // 26 - 31 for UNC drives.
+ //
+
+ if ( Found == TRUE ) {
+ DRIVE Drive;
+ DRIVE NextUncDrive = 26;
+
+ if ( NetResource->dwType != RESOURCETYPE_DISK ) {
+ return;
+ }
+
+ if ( NetResource->lpLocalName != NULL) {
+ Drive = NetResource->lpLocalName[0] - L'A';
+ } else {
+ if ( NextUncDrive < MD ) {
+ Drive = NextUncDrive++;
+ } else {
+
+ //
+ // No room in the table for this UNC drive.
+ //
+
+ return;
+ }
+ }
+
+ //
+ // We have a drive and a connection. Complete the table
+ // mappings.
+ //
+
+ pNwDosTable->DriveIdTable[ Drive ] = Connection + 1;
+ pNwDosTable->DriveFlagTable[ Drive ] = PERMANENT_NETWORK_DRIVE;
+
+ }
+
+}
+
+
+VOID
+InitDosTable(
+ PNWDOSTABLE pdt
+ )
+
+/*++
+
+Routine Description:
+
+ This routine Initializes the NetWare Dos Table to its empty values.
+
+Arguments:
+
+ pdt - Supplies the table to be initialized.
+
+Return Value:
+
+ None
+
+--*/
+{
+ ZeroMemory( ServerHandles, sizeof(ServerHandles) );
+ ZeroMemory( Drives, sizeof(Drives) );
+ ZeroMemory( (PVOID) pdt, sizeof(NWDOSTABLE) );
+ ZeroMemory( Win32DirectoryHandleTable, sizeof(Win32DirectoryHandleTable) );
+ FillMemory( SearchDriveTable, sizeof(SearchDriveTable), 0xff );
+}
+
+UCHAR CpuInProtectMode;
+
+
+VOID
+Nw16Handler(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This function is called by wow when nw16.sys traps an Int and
+ bop's into 32 bit mode.
+
+Arguments:
+
+
+Return Value:
+
+ None,
+
+--*/
+{
+ USHORT Command;
+ WORD offset;
+
+ //
+ // get the CPU mode once: the memory references it's required for won't
+ // change during this call. Cuts down number of calls to getMSW()
+ //
+
+ CpuInProtectMode = IS_PROTECT_MODE();
+
+ setCF(0);
+ if ( TablesValid == FALSE ) {
+
+ //
+ // Load the tables unless the process is exiting.
+ //
+
+ if ((pNwDosTable->SavedAx & 0xff00) != 0x4c00) {
+ Nw16Register();
+ }
+
+#if NWDBG
+ if (GotDebugState == FALSE) {
+
+ WCHAR Value[80];
+
+ if (GetEnvironmentVariableW(L"NWDEBUG",
+ Value,
+ sizeof(Value)/sizeof(Value[0]) - 1)) {
+
+ DebugCtrl = Value[0] - '0';
+
+ // 0 Use logfile
+ // 1 Use debugger
+ // 2/undefined No debug output
+ // 4 Use logfile, close on process exit
+ // 8 Use logfile, verbose, close on process exit
+
+ DebugControl( DebugCtrl );
+
+ }
+
+ GotDebugState = TRUE; // Don't look again until process exits vdm
+ }
+#endif
+ }
+
+ //
+ // Normal AX register is used to get into 32 bit code so get applications
+ // AX from the shared datastructure.
+ //
+
+ Command = pNwDosTable->SavedAx;
+
+ //
+ // set AX register so that AH gets preserved
+ //
+
+ setAX( Command );
+
+ NwPrint(("Nw16Handler process command %x\n", Command ));
+ VrDumpRealMode16BitRegisters( FALSE );
+ VrDumpNwData();
+
+ switch (Command & 0xff00) {
+
+ case 0x3C00:
+ case 0x3D00:
+ OpenCreateFile();
+ break;
+
+ case 0x4C00:
+ ProcessExit(); // Close all handles
+ goto default_dos_handler; // Let Dos handle rest of processing
+ break;
+
+ case 0x9f00:
+ OpenQueueFile();
+ break;
+
+ case 0xB300: // Packet Signing
+ setAL(0); // not supported
+ break;
+
+ case 0xB400:
+ AttachHandle();
+ break;
+
+ case 0xB500:
+ switch (Command & 0x00ff) {
+ case 03:
+ setAX((WORD)pNwDosTable->TaskModeByte);
+ break;
+
+ case 04:
+ setES((WORD)(CpuInProtectMode ? pNwDosTable->PmSelector : DosTableSegment));
+ setBX((WORD)(DosTableOffset + &((PNWDOSTABLE)0)->TaskModeByte));
+ break;
+
+ case 06:
+ setAX(2);
+ break;
+
+ default:
+ goto default_dos_handler;
+ }
+ break;
+
+ case 0xB800: // Capture - Not supported
+ setAL(0xff);
+ setCF(1);
+ break;
+
+ case 0xBB00: // Set EOJ status
+ {
+ static UCHAR EOJstatus = 1;
+ setAL(EOJstatus);
+ EOJstatus = pNwDosTable->SavedAx & 0x00ff;
+ }
+ break;
+
+ case 0xBC00:
+ case 0xBD00:
+ case 0xBE00:
+
+ case 0xC200:
+ case 0xC300:
+ case 0xC400:
+ case 0xC500:
+ case 0xC600:
+ Locks(Command);
+ break;
+
+ case 0xC700:
+ TTS();
+ break;
+
+ case 0xCB00:
+ case 0xCD00:
+ case 0xCF00:
+
+ case 0xD000:
+ case 0xD100:
+ case 0xD200:
+ case 0xD300:
+ case 0xD400:
+ case 0xD500:
+ Locks(Command);
+ break;
+
+ case 0xD700:
+ SystemLogout();
+ break;
+
+ case 0xDB00:
+ {
+ UCHAR Drive;
+ UCHAR Count = 0;
+ for (Drive = 0; Drive < MD; Drive++) {
+ if (pNwDosTable->DriveFlagTable[Drive] == LOCAL_DRIVE ) {
+ Count++;
+ }
+ }
+ setAL(Count);
+ }
+ break;
+
+ case 0xDC00: // Get station number
+ {
+ CONN_INDEX Connection = SelectConnection();
+ if (Connection == 0xff) {
+ setAL(0xff);
+ setCF(1);
+ } else {
+
+ PCONNECTIONID pConnection =
+ &pNwDosTable->ConnectionIdTable[Connection];
+
+ setAL(pConnection->ci_ConnectionLo);
+ setAH(pConnection->ci_ConnectionHi);
+ setCH( (UCHAR)((pConnection->ci_ConnectionHi == 0) ?
+ pConnection->ci_ConnectionLo / 10 + '0':
+ 'X'));
+ setCL((UCHAR)(pConnection->ci_ConnectionLo % 10 + '0'));
+ }
+ }
+ break;
+
+ case 0xDD00: // Set NetWare Error mode
+ {
+ static UCHAR ErrorMode = 0;
+ setAL( ErrorMode );
+ ErrorMode = getDL();
+ }
+ break;
+
+ case 0xDE00:
+ {
+ static UCHAR BroadCastMode = 0;
+ UCHAR OpCode = getDL();
+ if ( OpCode < 4) {
+ BroadCastMode = OpCode;
+ }
+ setAL(BroadCastMode);
+ }
+ break;
+
+ case 0xDF00: // Capture - Not supported
+ setAL(0xff);
+ setCF(1);
+ break;
+
+ case 0xE000:
+ case 0xE100:
+ case 0xE300:
+ SendNCP(Command);
+ break;
+
+ case 0xE200:
+
+ AllocateDirectoryHandle();
+ break;
+
+ case 0xE700:
+ GetServerDateAndTime();
+ break;
+
+ case 0xE900:
+
+ switch (Command & 0x00ff) {
+ PUCHAR ptr;
+ case 0:
+ GetDirectoryHandle();
+ break;
+
+ case 1:
+ ptr = GetVDMPointer (
+ (ULONG)((getDS() << 16)|getDX()),
+ sizeof(SearchDriveTable),
+ CpuInProtectMode
+ );
+
+ RtlMoveMemory( ptr, SearchDriveTable, sizeof(SearchDriveTable) );
+ break;
+
+ case 2:
+ ptr = GetVDMPointer (
+ (ULONG)((getDS() << 16)|getDX()),
+ sizeof(SearchDriveTable),
+ CpuInProtectMode
+ );
+
+ RtlMoveMemory( SearchDriveTable, ptr, sizeof(SearchDriveTable) );
+ break;
+
+ case 5:
+ AllocateDirectoryHandle2();
+ break;
+
+ case 7:
+ setAL(0xff); // Drive depth not yet implemented
+ break;
+
+#ifdef NWDBG
+ // Debug control
+ case 0xf0: // Use logfile
+ case 0xf1: // Use debugger
+ case 0xf2: // No debug output
+ DebugControl(Command & 0x000f);
+ break;
+#endif
+ default:
+ NwPrint(("Nw16Handler unprocessed interrupt %x\n", pNwDosTable->SavedAx ));
+ }
+ break;
+
+ case 0xEA00:
+ GetShellVersion(Command);
+ break;
+
+ case 0xEB00:
+ case 0xEC00:
+ case 0xED00:
+ Locks(Command);
+ break;
+
+
+ case 0xEF00:
+ NwPrint(("Nw32: %x\n", pNwDosTable->SavedAx ));
+
+ switch (Command & 0xff) {
+ case 00:
+ if (DriveHandleTableValid == FALSE) {
+ LoadDriveHandleTable();
+ }
+
+ offset = (WORD)&((PNWDOSTABLE)0)->DriveHandleTable;
+ break;
+
+ case 01:
+ offset = (WORD)&((PNWDOSTABLE)0)->DriveFlagTable;
+ break;
+
+ case 02:
+ offset = (WORD)&((PNWDOSTABLE)0)->DriveIdTable;
+ break;
+
+ case 03:
+ offset = (WORD)&((PNWDOSTABLE)0)->ConnectionIdTable;
+ break;
+
+ case 04:
+ offset = (WORD)&((PNWDOSTABLE)0)->ServerNameTable;
+ break;
+
+ default:
+ goto default_dos_handler;
+ }
+ setSI((WORD)(DosTableOffset + offset));
+ setES((WORD)(CpuInProtectMode ? pNwDosTable->PmSelector : DosTableSegment));
+ setAL(0);
+ break;
+
+ case 0xF100:
+ setAL(AttachmentControl(Command));
+ break;
+
+ case 0xF200:
+ SendF2NCP(Command);
+ break;
+
+ case 0xF300:
+ ServerFileCopy();
+ break;
+
+ default:
+
+default_dos_handler:
+
+ NwPrint(("Nw16Handler unprocessed interrupt %x\n", pNwDosTable->SavedAx ));
+
+ //
+ // if we don't handle this call, we modify the return ip to point to
+ // code that will restore the stack and jump far into dos
+ //
+
+ setIP((WORD)(getIP() + 3));
+
+ }
+
+#if NWDBG
+ pNwDosTable->SavedAx = getAX();
+#endif
+ VrDumpRealMode16BitRegisters( FALSE );
+}
+
+
+CONN_INDEX
+SelectConnection(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Pick target connection for current transaction
+
+Arguments:
+
+ None
+
+Return Value:
+
+ Index into ConnectionIdTable or 0xff,
+
+--*/
+{
+
+ UCHAR IndexConnection;
+
+ if ( pNwDosTable->PreferredServer != 0 ) {
+ return(pNwDosTable->PreferredServer - 1);
+ }
+
+ // BUGBUG: select default server if current drive is mapped by us?
+
+ if ( pNwDosTable->PrimaryServer != 0 ) {
+ return(pNwDosTable->PrimaryServer - 1);
+ }
+
+ // Need to pick another
+
+ for (IndexConnection = 0; IndexConnection < MC ; IndexConnection++ ) {
+
+ if (pNwDosTable->ConnectionIdTable[IndexConnection].ci_InUse == IN_USE) {
+
+ pNwDosTable->PrimaryServer = IndexConnection + 1;
+
+ return(pNwDosTable->PrimaryServer - 1);
+
+ }
+ }
+
+ // No servers in the table so find the nearest/preferred.
+
+ LoadPreferredServerName();
+
+ return(pNwDosTable->PrimaryServer - 1);
+
+}
+
+
+
+VOID
+SendNCP(
+ ULONG Command
+ )
+/*++
+
+Routine Description:
+
+ Implement generic Send NCP function.
+
+ ASSUMES called from Nw16Handler
+
+Arguments:
+
+ Command - Supply the opcode 0xexxx
+ DS:SI - Supply Request buffer & length
+ ES:DI - Supply Reply buffer & length
+
+ On return AL = Status of operation.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PUCHAR Request, Reply;
+ ULONG RequestLength, ReplyLength;
+ UCHAR OpCode;
+
+ OpCode = (UCHAR)((Command >> 8) - 0xcc);
+
+ Request = GetVDMPointer (
+ (ULONG)((getDS() << 16)|getSI()),
+ sizeof(WORD),
+ CpuInProtectMode
+ );
+
+ Reply = GetVDMPointer (
+ (ULONG)((getES() << 16)|getDI()),
+ sizeof(WORD),
+ CpuInProtectMode
+ );
+
+ RequestLength = *(WORD UNALIGNED*)Request;
+ ReplyLength = *(WORD UNALIGNED*)Reply;
+
+ Request = GetVDMPointer (
+ (ULONG)((getDS() << 16)|getSI() + sizeof(WORD)),
+ (USHORT)RequestLength,
+ CpuInProtectMode
+ );
+ Reply = GetVDMPointer (
+ (ULONG)((getES() << 16)|getDI()) + sizeof(WORD),
+ (USHORT)ReplyLength,
+ CpuInProtectMode
+ );
+
+ NwPrint(("SubRequest %x, RequestLength %x\n", Request[0], RequestLength ));
+
+ SendNCP2( NWR_ANY_NCP(OpCode ),
+ Request,
+ RequestLength,
+ Reply,
+ ReplyLength);
+}
+
+
+VOID
+SendF2NCP(
+ ULONG Command
+ )
+/*++
+
+Routine Description:
+
+ Implement generic Send NCP function. No length to be inseted by
+ the redirector in the request buffer.
+
+ ASSUMES called from Nw16Handler
+
+Arguments:
+
+ Command - Supply the opcode 0xf2xx
+ DS:SI CX - Supply Request buffer & length
+ ES:DI DX - Supply Reply buffer & length
+
+ On return AL = Status of operation.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PUCHAR Request, Reply;
+ ULONG RequestLength, ReplyLength;
+ UCHAR OpCode;
+
+
+ OpCode = (UCHAR)(Command & 0x00ff);
+
+ RequestLength = getCX();
+ ReplyLength = getDX();
+
+ Request = GetVDMPointer (
+ (ULONG)((getDS() << 16)|getSI()),
+ (USHORT)RequestLength,
+ CpuInProtectMode
+ );
+ Reply = GetVDMPointer (
+ (ULONG)((getES() << 16)|getDI()),
+ (USHORT)ReplyLength,
+ CpuInProtectMode
+ );
+
+ NwPrint(("F2SubRequest %x, RequestLength %x\n", Request[2], RequestLength ));
+
+#if 0
+ if ((RequestLength != 0) &&
+ (OpCode == 0x17)) {
+
+ if ((Request[2] == 0x17) ||
+ (Request[2] == 0x18)) {
+ //
+ // The request was for an encryption key. Tell the
+ // application that encryption is not supported.
+ //
+
+ setAL(0xfb);
+ return;
+
+ } else if ((Request[2] == 0x14 ) ||
+ (Request[2] == 0x3f )) {
+
+ //
+ // Plaintext login or Verify Bindery Object Password.
+ // Convert to its WNET equivalent version.
+ //
+
+ UCHAR Name[256];
+ UCHAR Password[256];
+ UCHAR ServerName[sizeof(SERVERNAME)+3];
+ PUCHAR tmp;
+ CONN_INDEX Connection;
+ NETRESOURCEA Nr;
+
+ Connection = SelectConnection();
+ if ( Connection == 0xff ) {
+ setAL(0xff);
+ setCF(1);
+ return;
+ }
+
+ ZeroMemory( &Nr, sizeof(NETRESOURCE));
+ ServerName[0] = '\\';
+ ServerName[1] = '\\';
+ RtlCopyMemory( ServerName+2, pNwDosTable->ServerNameTable[Connection], sizeof(SERVERNAME) );
+ ServerName[sizeof(ServerName)-1] = '\0';
+ Nr.lpRemoteName = ServerName;
+ Nr.dwType = RESOURCETYPE_DISK;
+
+ // point to password length.
+ tmp = &Request[6] + Request[5];
+
+ Name[Request[5]] = '\0';
+ RtlMoveMemory( Name, &Request[6], Request[5]);
+
+ Password[tmp[0]] = '\0';
+ RtlMoveMemory( Password, tmp+1, tmp[0]);
+
+ NwPrint(("Connect to %s as %s password %s\n", ServerName, Name, Password ));
+
+ if (NO_ERROR == WNetAddConnection2A( &Nr, Password, Name, 0)) {
+ setAL(0);
+ } else {
+ setAL(255);
+ }
+ return;
+ }
+ }
+
+#endif
+
+ SendNCP2( NWR_ANY_F2_NCP(OpCode ),
+ Request,
+ RequestLength,
+ Reply,
+ ReplyLength);
+}
+
+
+VOID
+SendNCP2(
+ ULONG Command,
+ PUCHAR Request,
+ ULONG RequestLength,
+ PUCHAR Reply,
+ ULONG ReplyLength
+ )
+/*++
+
+Routine Description:
+
+ Pick target connection for current transaction
+
+ This routine effectively opens a handle for each NCP sent. This means that
+ we don't keep handles open to servers unnecessarily which would cause
+ problems if a user tries to delete the connection or stop the workstation.
+
+ If this causes to much of a load then the fallback is to spin off a thread
+ that waits on an event with a timeout and periodically sweeps the
+ server handle table removing stale handles. Setting the event would cause
+ the thread to exit. Critical sections would need to be added to protect
+ handles. Dll Init/exit routine to kill the thread and close the handles
+ would also be needed.
+
+Arguments:
+
+ Command - Supply the opcode
+ Request, RequestLength - Supply Request buffer & length
+ Reply, ReplyLength - Supply Reply buffer & length
+
+ On return AL = Status of operation.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ CONN_INDEX Connection = SelectConnection();
+ NTSTATUS status;
+ IO_STATUS_BLOCK IoStatusBlock;
+ HANDLE Handle;
+
+ NwPrint(("Send NCP %x to %d:%s\n", Command, Connection, pNwDosTable->ServerNameTable[Connection] ));
+ NwPrint(("RequestLength %x\n", RequestLength ));
+ NwPrint(("Reply %x, ReplyLength %x\n", Reply, ReplyLength ));
+
+ if (Connection == 0xff) {
+ setAL(0xff);
+ setCF(1);
+ return;
+ };
+
+ if ( ServerHandles[Connection] == NULL ) {
+
+ status = OpenConnection( Connection );
+
+ if (!NT_SUCCESS(status)) {
+ SetStatus(status);
+ return;
+ } else {
+ InitConnection( Connection );
+ }
+ }
+
+ Handle = ServerHandles[Connection];
+
+ //
+ // If its a CreateJobandFile NCP then we need to use the handle
+ // created through Dos so that the writes go into the spoolfile created
+ // by this NCP.
+ //
+
+ if (Command == NWR_ANY_F2_NCP(0x17)) {
+
+ if ((Request[2] == 0x68) ||
+ (Request[2] == 0x79)) {
+
+ Handle = GET_NT_HANDLE();
+ }
+ } else if (Command == NWR_ANY_NCP(0x17)) {
+ if ((Request[0] == 0x68) ||
+ (Request[0] == 0x79)) {
+
+ Handle = GET_NT_HANDLE();
+ }
+ }
+
+ FormattedDump( Request, RequestLength );
+
+ //
+ // Make the NCP request on the appropriate handle
+ //
+
+ status = NtFsControlFile(
+ Handle,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ Command,
+ (PVOID) (Request),
+ RequestLength,
+ (PVOID) Reply,
+ ReplyLength);
+
+ if (NT_SUCCESS(status)) {
+ status = IoStatusBlock.Status;
+ FormattedDump( Reply, ReplyLength );
+ }
+
+ if (!NT_SUCCESS(status)) {
+ SetStatus(status);
+ setCF(1);
+ NwPrint(("NtStatus %x, DosError %x\n", status, getAL() ));
+ } else {
+ setAL(0);
+ }
+}
+
+
+NTSTATUS
+OpenConnection(
+ CONN_INDEX Connection
+ )
+/*++
+
+Routine Description:
+
+ Open the handle to the redirector to access the specified server.
+
+Arguments:
+
+ Connection - Supplies the index to use for the handle
+
+Return Value:
+
+ Status of the operation
+
+--*/
+{
+ NTSTATUS Status;
+ IO_STATUS_BLOCK IoStatusBlock;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+
+ LPWSTR FullName;
+
+ UCHAR AnsiName[SERVERNAME_LENGTH+sizeof(UCHAR)];
+
+ UNICODE_STRING UServerName;
+ OEM_STRING AServerName;
+
+ if ( Connection >= MC) {
+ return( BASE_DOS_ERROR + 249 ); // No free connection slots
+ }
+
+ if (ServerHandles[Connection] != NULL ) {
+
+ CloseConnection(Connection);
+
+ }
+
+ FullName = (LPWSTR) LocalAlloc( LMEM_ZEROINIT,
+ sizeof( DD_NWFS_DEVICE_NAME_U ) +
+ (SERVERNAME_LENGTH + 1) * sizeof(WCHAR)
+ );
+
+ if ( FullName == NULL ) {
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ CopyMemory(AnsiName,
+ pNwDosTable->ServerNameTable[Connection],
+ SERVERNAME_LENGTH);
+ AnsiName[SERVERNAME_LENGTH+1] = '\0';
+
+ RtlInitAnsiString( &AServerName, AnsiName );
+ Status = RtlOemStringToUnicodeString( &UServerName,
+ &AServerName,
+ TRUE);
+
+ if (!NT_SUCCESS(Status)) {
+ LocalFree( FullName );
+ return(Status);
+ }
+
+ wcscpy( FullName, DD_NWFS_DEVICE_NAME_U );
+ wcscat( FullName, L"\\");
+ wcscat( FullName, UServerName.Buffer );
+
+ RtlFreeUnicodeString(&UServerName);
+
+ RtlInitUnicodeString( &UServerName, FullName );
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &UServerName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ //
+ // Open a handle to the server.
+ //
+
+ //
+ // Try to login to the nearest server. This is necessary for
+ // the real preferred server if there are no redirections to
+ // it. The rdr can logout and disconnect. SYSCON doesn't like
+ // running from such a server.
+ //
+ Status = NtOpenFile(
+ &ServerHandles[Connection],
+ SYNCHRONIZE | FILE_READ_ATTRIBUTES,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_VALID_FLAGS,
+ FILE_SYNCHRONOUS_IO_NONALERT
+ );
+
+ if ( NT_SUCCESS(Status)) {
+ Status = IoStatusBlock.Status;
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ //
+ // Failed to login. Use the non-login method. This allows the
+ // app to do a bindery login or query the bindery.
+ //
+
+ Status = NtOpenFile(
+ &ServerHandles[Connection],
+ SYNCHRONIZE,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ FILE_SHARE_VALID_FLAGS,
+ FILE_SYNCHRONOUS_IO_NONALERT
+ );
+
+ if ( NT_SUCCESS(Status)) {
+ Status = IoStatusBlock.Status;
+ }
+ }
+
+ NwPrint(("Nw16:OpenConnection %d: %wZ status = %08lx\n", Connection, &UServerName, Status));
+
+ LocalFree( FullName );
+
+ if (!NT_SUCCESS(Status)) {
+ SetStatus(Status);
+ return Status;
+ }
+
+ return Status;
+}
+
+
+VOID
+CloseConnection(
+ CONN_INDEX Connection
+ )
+/*++
+
+Routine Description:
+
+ Close the connection handle
+
+Arguments:
+
+ Connection - Supplies the index to use for the handle
+
+Return Value:
+
+ None.
+
+--*/
+{
+ if (ServerHandles[Connection]) {
+
+ NwPrint(("CloseConnection: %d\n",Connection));
+
+ NtClose(ServerHandles[Connection]);
+
+ ServerHandles[Connection] = NULL;
+ }
+}
+
+
+NTSTATUS
+InitConnection(
+ CONN_INDEX Connection
+ )
+/*++
+
+Routine Description:
+
+ Get the connection status from the redirector.
+
+Arguments:
+
+ Connection - Supplies the index to use for the handle
+
+Return Value:
+
+ Status of the operation
+
+--*/
+{
+ NTSTATUS Status;
+ IO_STATUS_BLOCK IoStatusBlock;
+ NWR_GET_CONNECTION_DETAILS Details;
+
+ Status = NtFsControlFile(
+ ServerHandles[Connection],
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_GET_CONN_DETAILS,
+ NULL,
+ 0,
+ (PVOID) &Details,
+ sizeof(Details));
+
+ if (Status == STATUS_SUCCESS) {
+ Status = IoStatusBlock.Status;
+ }
+
+ NwPrint(("Nw16:InitConnection: %d status = %08lx\n",Connection, Status));
+
+ if (!NT_SUCCESS(Status)) {
+
+ SetStatus(Status);
+
+ CloseConnection(Connection);
+
+ } else {
+ PCONNECTIONID pConnection =
+ &pNwDosTable->ConnectionIdTable[Connection];
+
+ pConnection->ci_OrderNo= Details.OrderNumber;
+
+ CopyMemory(pNwDosTable->ServerNameTable[Connection],
+ Details.ServerName,
+ sizeof(SERVERNAME));
+
+ CopyMemory(pConnection->ci_ServerAddress,
+ Details.ServerAddress,
+ sizeof(pConnection->ci_ServerAddress));
+
+ pConnection->ci_ConnectionNo= Details.ConnectionNumberLo;
+ pConnection->ci_ConnectionLo= Details.ConnectionNumberLo;
+ pConnection->ci_ConnectionHi= Details.ConnectionNumberHi;
+ pConnection->ci_MajorVersion= Details.MajorVersion;
+ pConnection->ci_MinorVersion= Details.MinorVersion;
+ pConnection->ci_InUse = IN_USE;
+ pConnection->ci_1 = 0;
+ pConnection->ci_ConnectionStatus = 2;
+
+ //
+ // If this is the preferred conection then record it as special.
+ // If this is the first drive then also record it. Usually it gets
+ // overwritten by the preferred.
+ //
+
+ if (( Details.Preferred ) ||
+ ( OriginalPrimary == 0 )) {
+
+ NwPrint(("Nw16InitConnection: Primary Connection is %d\n", Connection+1));
+
+ pNwDosTable->PrimaryServer = OriginalPrimary = (UCHAR)Connection + 1;
+ }
+
+ setAL(0);
+ }
+
+ return Status;
+}
+
+VOID
+GetDirectoryHandle(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Obtain a NetWare handle for the current directory.
+
+ If a NetWare handle is assigned then the Win32 handle created for
+ the directory handle is kept open. When the process exits, the Win32
+ handle will close. When all the Win32 handles from this process are
+ closed an endjob NCP will be sent freeing the directory handle on the
+ server.
+
+Arguments:
+
+ DX supplies the drive.
+
+ AL returns the handle.
+ AH returns the status flags.
+
+
+Return Value:
+
+ None.
+
+--*/
+{
+ USHORT Drive = getDX();
+
+ NwPrint(("Nw32:GetDirectoryHandle %c: ", 'A' + Drive));
+
+ GetDirectoryHandle2( Drive );
+
+ setAL(pNwDosTable->DriveHandleTable[Drive]);
+ setAH(pNwDosTable->DriveFlagTable[Drive]);
+
+ NwPrint(("Handle = %x, Flags =%x\n", pNwDosTable->DriveHandleTable[Drive],
+ pNwDosTable->DriveFlagTable[Drive] ));
+}
+
+ULONG
+GetDirectoryHandle2(
+ DWORD Drive
+ )
+/*++
+
+Routine Description:
+
+ Obtain a NetWare handle for the current directory.
+
+ If a NetWare handle is assigned then the Win32 handle created for
+ the directory handle is kept open. When the process exits, the Win32
+ handle will close. When all the Win32 handles from this process are
+ closed an endjob NCP will be sent freeing the directory handle on the
+ server.
+
+ Note: Updates DriveHandleTable.
+
+Arguments:
+
+ Drive supplies the drive index (0 = a:).
+
+Return Value:
+
+ returns the handle.
+
+--*/
+{
+ DWORD BytesReturned;
+
+ if (Drive >= MD) {
+ setAL( 0x98 ); // Volume does not exist
+ return 0xffffffff;
+ }
+
+ NwPrint(("Nw32:GetDirectoryHandle2 %c:\n", 'A' + Drive));
+
+ //
+ // If we don't have a handle and its either a temporary or
+ // permanent drive then create it.
+ //
+
+ if (( Win32DirectoryHandleTable[Drive] == 0 ) &&
+ ( (pNwDosTable->DriveFlagTable[Drive] & 3) != 0 )){
+ WCHAR DriveString[4];
+ PWCHAR Name;
+
+ //
+ // We don't have a handle for this drive.
+ // Open an NT handle to the current directory and
+ // ask the redirector for a NetWare directory handle.
+ //
+
+ if (Drive <= ('Z' - 'A')) {
+
+ DriveString[0] = L'A' + (WCHAR)Drive;
+ DriveString[1] = L':';
+ DriveString[2] = L'.';
+ DriveString[3] = L'\0';
+
+ Name = DriveString;
+
+ } else {
+
+ Name = Drives[Drive];
+
+ if( Name == NULL ) {
+ NwPrint(("\nNw32:GetDirectoryHandle2 Drive not mapped\n",0));
+ return 0xffffffff;
+ }
+ }
+
+ Win32DirectoryHandleTable[Drive] =
+ CreateFileW( Name,
+ 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS,
+ 0);
+
+ if (Win32DirectoryHandleTable[Drive] != INVALID_HANDLE_VALUE) {
+
+ if ( DeviceIoControl(
+ Win32DirectoryHandleTable[Drive],
+ IOCTL_NWR_RAW_HANDLE,
+ NULL,
+ 0,
+ (PUCHAR)&pNwDosTable->DriveHandleTable[Drive],
+ sizeof(pNwDosTable->DriveHandleTable[Drive]),
+ &BytesReturned,
+ NULL ) == FALSE ) {
+
+ NwPrint(("\nNw32:GetDirectoryHandle2 DeviceIoControl %x\n", GetLastError()));
+ CloseHandle( Win32DirectoryHandleTable[Drive] );
+ Win32DirectoryHandleTable[Drive] = 0;
+ return 0xffffffff;
+
+ } else {
+ ASSERT( BytesReturned == sizeof(pNwDosTable->DriveHandleTable[Drive]));
+
+ NwPrint(("\nNw32:GetDirectoryHandle2 Success %x\n", pNwDosTable->DriveHandleTable[Drive]));
+ }
+
+ } else {
+ NwPrint(("\nNw32:GetDirectoryHandle2 CreateFile %x\n", GetLastError()));
+
+ Win32DirectoryHandleTable[Drive] = 0;
+
+ return 0xffffffff;
+ }
+
+ }
+
+ return (ULONG)pNwDosTable->DriveHandleTable[Drive];
+}
+
+VOID
+LoadDriveHandleTable(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Open handles to all the NetWare drives
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ none.
+
+--*/
+{
+
+ USHORT Drive;
+ for (Drive = 0; Drive < MD; Drive++ ) {
+ GetDirectoryHandle2(Drive);
+ }
+
+ DriveHandleTableValid = TRUE;
+
+}
+
+VOID
+AllocateDirectoryHandle(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Allocate permanent or temporary handle for drive.
+
+ For a permanent handle, we map this to a "net use".
+
+ ASSUMES called from Nw16Handler
+
+
+Arguments:
+
+ DS:SI supplies the request.
+ ES:DI supplies the response.
+
+ AL returns the completion code.
+
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ PUCHAR Request=GetVDMPointer (
+ (ULONG)((getDS() << 16)|getSI()),
+ 2,
+ CpuInProtectMode
+ );
+
+ PUCHAR Reply = GetVDMPointer (
+ (ULONG)((getES() << 16)|getDI()),
+ 4,
+ CpuInProtectMode
+ );
+
+ USHORT RequestLength = *(USHORT UNALIGNED *)( Request );
+
+ Request=GetVDMPointer (
+ (ULONG)((getDS() << 16)|getSI()),
+ RequestLength+2,
+ CpuInProtectMode
+ );
+
+ FormattedDump( Request, RequestLength+2 );
+
+
+ if (( Request[2] == 0x12) ||
+ ( Request[2] == 0x13)) {
+ // BUGBUG do temp drives need different handling?
+
+ UCHAR Drive = Request[4] - 'A';
+
+ if (Drive >= MD) {
+ setAL( 0x98 ); // Volume does not exist
+ return;
+ }
+
+ if (pNwDosTable->DriveHandleTable[Drive] != 0) {
+
+ NwPrint(("Nw32: Move directory handle %d\n", Drive));
+
+ //
+ // We already have a directory handle assigned for this
+ // process. Ask the server to point the handle at the requested
+ // position.
+ //
+
+ SendNCP2(FSCTL_NWR_NCP_E2H, Request+2, RequestLength, Reply+2, 2);
+
+ if (getAL() == 0) {
+ // Record the new handle.
+
+ pNwDosTable->DriveIdTable[ Drive ] = SelectConnection()+1;
+
+ if (Request[2] == 0x12) {
+ pNwDosTable->DriveFlagTable[ Drive ] =
+ PERMANENT_NETWORK_DRIVE;
+ } else {
+ pNwDosTable->DriveFlagTable[ Drive ] =
+ TEMPORARY_NETWORK_DRIVE;
+ }
+
+ pNwDosTable->DriveHandleTable[Drive] = Reply[2];
+ NwPrint(("Nw32: Move directory handle -> %x\n", Reply[2]));
+ }
+
+ } else {
+ NETRESOURCE Nr;
+ WCHAR DriveString[3];
+ ULONG Handle;
+
+ if (Request[2] == 0x12) {
+ NwPrint(("Nw32: Allocate permanent directory handle %d\n", Drive));
+ } else {
+ NwPrint(("Nw32: Allocate temporary directory handle %d\n", Drive));
+ }
+
+ if (Drives[Drive] != NULL) {
+
+ // Tidy up the old name for this drive.
+
+ LocalFree( Drives[Drive] );
+ Drives[Drive] = NULL;
+ }
+
+ DriveString[0] = L'A' + Drive; // A through Z
+ DriveString[1] = L':';
+ DriveString[2] = L'\0';
+
+ //
+ // This is effectively a net use!
+ //
+
+ ZeroMemory( &Nr, sizeof(NETRESOURCE));
+
+ Nr.lpRemoteName = BuildUNC(&Request[6], Request[5]);
+ Nr.dwType = RESOURCETYPE_DISK;
+
+ // Save where this drive points.
+ Drives[Drive] = Nr.lpRemoteName;
+
+ if (DriveString[0] <= L'Z') {
+ Nr.lpLocalName = DriveString;
+
+ if (NO_ERROR != WNetAddConnection2W( &Nr, NULL, NULL, 0)) {
+
+ NwPrint(("Nw32: Allocate ->%d\n", GetLastError()));
+ setAL(0x98); // Volume does not exist
+ return;
+ }
+ }
+
+
+ if (Request[2] == 0x12) {
+ pNwDosTable->DriveFlagTable[ Drive ] =
+ PERMANENT_NETWORK_DRIVE;
+ } else {
+ pNwDosTable->DriveFlagTable[ Drive ] =
+ TEMPORARY_NETWORK_DRIVE;
+ }
+
+ Handle = GetDirectoryHandle2( Drive );
+
+ if (Handle == 0xffffffff) {
+
+ if (DriveString[0] <= L'Z') {
+
+ WNetCancelConnection2W( DriveString, 0, TRUE);
+
+ }
+
+ ResetDrive( Drive );
+
+ setAL(0x9c); // Invalid path
+
+ } else {
+
+ //
+ // We have a drive and a connection. Complete the table
+ // mappings.
+ //
+
+ pNwDosTable->DriveIdTable[ Drive ] = SelectConnection()+1;
+
+ Reply[2] = (UCHAR)(Handle & 0xff);
+ Reply[3] = (UCHAR)(0xff); //BUGBUG should be effective rights
+ setAL(0); // Successful
+ }
+ }
+
+ } else if ( Request[2] == 0x14 ) {
+
+ UCHAR DirHandle = Request[3];
+ UCHAR Drive;
+ CONN_INDEX Connection = SelectConnection();
+
+ NwPrint(("Nw32: Deallocate directory handle %d on Connection %d\n", DirHandle, Connection));
+
+ for (Drive = 0; Drive < MD; Drive++) {
+
+
+ NwPrint(("Nw32: Drive %c: is DirHandle %d, Connection %d\n",
+ 'A' + Drive,
+ pNwDosTable->DriveHandleTable[Drive],
+ pNwDosTable->DriveIdTable[ Drive ]-1 ));
+
+ if ((pNwDosTable->DriveHandleTable[Drive] == DirHandle) &&
+ (pNwDosTable->DriveIdTable[ Drive ] == Connection+1)) {
+
+ //
+ // This is effectively a net use del!
+ //
+
+ NwPrint(("Nw32: Deallocate directory handle %c\n", 'A' + Drive));
+
+ ResetDrive(Drive);
+
+ setAL(0);
+
+ return;
+ }
+ }
+
+ setAL(0x9b); // Bad directory handle
+ return;
+
+ } else {
+
+ SendNCP(pNwDosTable->SavedAx);
+ }
+
+ FormattedDump( Reply, Reply[0] );
+}
+
+VOID
+ResetDrive(
+ UCHAR Drive
+ )
+/*++
+
+Routine Description:
+
+ Do a net use del
+
+Arguments:
+
+ Drive - Supplies the target drive.
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ NwPrint(("Nw32: Reset Drive %c:\n", 'A' + Drive ));
+
+ if ((pNwDosTable->DriveFlagTable[ Drive ] &
+ ( PERMANENT_NETWORK_DRIVE | TEMPORARY_NETWORK_DRIVE )) == 0) {
+
+ return;
+
+ }
+
+ if (Win32DirectoryHandleTable[Drive] != 0) {
+
+ CloseHandle( Win32DirectoryHandleTable[Drive] );
+ Win32DirectoryHandleTable[Drive] = 0;
+
+ }
+
+ if (Drive <= (L'Z' - L'A')) {
+
+ DWORD WStatus;
+ WCHAR DriveString[3];
+
+ DriveString[0] = L'A' + Drive;
+ DriveString[1] = L':';
+ DriveString[2] = L'\0';
+
+ WStatus = WNetCancelConnection2W( DriveString, 0, TRUE);
+
+ if( WStatus != NO_ERROR ) {
+ NwPrint(("Nw32: WNetCancelConnection2W failed %d\n", WStatus ));
+ }
+
+ }
+
+ // Turn off flags that show this drive as redirected
+
+ pNwDosTable->DriveFlagTable[ Drive ] &=
+ ~( PERMANENT_NETWORK_DRIVE | TEMPORARY_NETWORK_DRIVE );
+
+ pNwDosTable->DriveHandleTable[Drive] = 0;
+}
+
+VOID
+AllocateDirectoryHandle2(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Allocate root drive
+
+ ASSUMES called from Nw16Handler
+
+Arguments:
+
+ BL supplies drive to map.
+ DS:DX supplies the pathname
+
+ AL returns the completion code.
+
+
+Return Value:
+
+ None.
+
+--*/
+{
+ UCHAR Drive = getBL()-1;
+
+ PUCHAR Name=GetVDMPointer (
+ (ULONG)((getDS() << 16)|getDX()),
+ 256, // longest valid path
+ CpuInProtectMode
+ );
+
+ NETRESOURCE Nr;
+ WCHAR DriveString[3];
+ ULONG Handle;
+
+ NwPrint(("Nw32: e905 map drive %c to %s\n", Drive + 'A', Name ));
+
+ if (Drive >= MD) {
+ setAL( 0x98 ); // Volume does not exist
+ setCF(1);
+ return;
+ }
+
+ if (pNwDosTable->DriveHandleTable[Drive] != 0) {
+
+ NwPrint(("Nw32: Drive already redirected\n"));
+ ResetDrive(Drive);
+
+ }
+
+
+ NwPrint(("Nw32: Allocate permanent directory handle\n"));
+
+ if (Drives[Drive] != NULL) {
+
+ // Tidy up the old name for this drive.
+
+ LocalFree( Drives[Drive] );
+ Drives[Drive] = NULL;
+ }
+
+ //
+ // This is effectively a net use!
+ //
+
+ ZeroMemory( &Nr, sizeof(NETRESOURCE));
+
+ Nr.lpRemoteName = BuildUNC( Name, strlen(Name));
+ // Save where this drive points.
+ Drives[Drive] = Nr.lpRemoteName;
+
+ if (Drive <= (L'Z' - L'A')) {
+ DriveString[0] = L'A' + Drive; // A through Z
+ DriveString[1] = L':';
+ DriveString[2] = L'\0';
+ Nr.lpLocalName = DriveString;
+ Nr.dwType = RESOURCETYPE_DISK;
+
+ if (NO_ERROR != WNetAddConnection2W( &Nr, NULL, NULL, 0)) {
+
+ NwPrint(("Nw32: Allocate0 ->%d\n", GetLastError()));
+
+ if (GetLastError() == ERROR_ALREADY_ASSIGNED) {
+
+ WNetCancelConnection2W( DriveString, 0, TRUE);
+
+ if (NO_ERROR != WNetAddConnection2W( &Nr, NULL, NULL, 0)) {
+
+ NwPrint(("Nw32: Allocate1 ->%d\n", GetLastError()));
+ ResetDrive( Drive );
+ setAL(0x03); // Volume does not exist
+ setCF(1);
+ return;
+ }
+
+ } else {
+
+ NwPrint(("Nw32: Allocate2 ->%d\n", GetLastError()));
+ ResetDrive( Drive );
+ setAL(0x03); // Volume does not exist
+ setCF(1);
+ return;
+ }
+ }
+ }
+
+ //
+ // Set flags so that GetDirectory2 will open handle
+ //
+ pNwDosTable->DriveIdTable[ Drive ] = SelectConnection()+1;
+ pNwDosTable->DriveFlagTable[ Drive ] = PERMANENT_NETWORK_DRIVE;
+
+ Handle = GetDirectoryHandle2( Drive );
+
+ if (Handle == 0xffffffff) {
+
+ ResetDrive( Drive );
+ setAL(0x03); // Invalid path
+ setCF(1);
+
+ } else {
+
+ setAL(0); // Successful
+
+ }
+
+ NwPrint(("Nw32: Returning %x\n",getAL()));
+}
+
+PWCHAR
+BuildUNC(
+ IN PUCHAR aName,
+ IN ULONG aLength
+ )
+/*++
+
+Routine Description:
+
+ This routine takes the ansi name, prepends the appropriate server name
+ (if appropriate) and converts to Unicode.
+
+Arguments:
+
+ IN aName - Supplies the ansi name.
+ IN aLength - Supplies the ansi name length in bytes.
+
+Return Value:
+
+ Unicode string
+
+--*/
+{
+ UNICODE_STRING Name;
+ UCHAR ServerName[sizeof(SERVERNAME)+1];
+
+ CONN_INDEX Connection;
+ ANSI_STRING TempAnsi;
+ UNICODE_STRING TempUnicode;
+ USHORT x;
+
+ // conversion rules for aName to Name are:
+
+ // foo: "\\server\foo\"
+ // foo:bar\baz "\\server\foo\bar\baz"
+ // foo:\bar\baz "\\server\foo\bar\baz"
+
+
+#ifdef NWDBG
+ TempAnsi.Buffer = aName;
+ TempAnsi.Length = (USHORT)aLength;
+ TempAnsi.MaximumLength = (USHORT)aLength;
+ NwPrint(("Nw32: BuildUNC %Z\n", &TempAnsi));
+#endif
+
+ Connection = SelectConnection();
+ if ( Connection == 0xff ) {
+ return NULL;
+ }
+
+ Name.MaximumLength = (USHORT)(aLength + sizeof(SERVERNAME) + 5) * sizeof(WCHAR);
+ Name.Buffer = (PWSTR)LocalAlloc( LMEM_FIXED, (ULONG)Name.MaximumLength);
+
+ if (Name.Buffer == NULL) {
+ return NULL;
+ }
+
+ Name.Length = 4;
+ Name.Buffer[0] = L'\\';
+ Name.Buffer[1] = L'\\';
+
+ //
+ // Be careful because ServerName might be 48 bytes long and therefore
+ // not null terminated.
+ //
+
+ RtlCopyMemory( ServerName, pNwDosTable->ServerNameTable[Connection], sizeof(SERVERNAME) );
+ ServerName[sizeof(ServerName)-1] = '\0';
+
+ RtlInitAnsiString( &TempAnsi, ServerName );
+ RtlAnsiStringToUnicodeString( &TempUnicode, &TempAnsi, TRUE);
+ RtlAppendUnicodeStringToString( &Name, &TempUnicode );
+ RtlFreeUnicodeString( &TempUnicode );
+
+ // Now pack servername to volume seperator if necessary.
+
+ if ((aLength != 0) &&
+ (aName[0] != '\\')) {
+ RtlAppendUnicodeToString( &Name, L"\\");
+ }
+
+ // aName might not be null terminated so be careful creating TempAnsi
+ TempAnsi.Buffer = aName;
+ TempAnsi.Length = (USHORT)aLength;
+ TempAnsi.MaximumLength = (USHORT)aLength;
+
+ if (!NT_SUCCESS(RtlAnsiStringToUnicodeString( &TempUnicode, &TempAnsi, TRUE))) {
+ LocalFree( Name.Buffer );
+ return NULL;
+ }
+
+ RtlAppendUnicodeStringToString( &Name, &TempUnicode );
+
+ // If the name already has a volume seperator then don't add another.
+ for (x=0; x < (Name.Length/sizeof(WCHAR)) ; x++ ) {
+
+ if (Name.Buffer[x] == L':') {
+
+ // Strip the colon if it is immediately followed by a backslash
+
+ if (((Name.Length/sizeof(WCHAR))-1 > x) &&
+ (Name.Buffer[x+1] == L'\\')) {
+
+ RtlMoveMemory( &Name.Buffer[x],
+ &Name.Buffer[x+1],
+ Name.Length - ((x + 1) * sizeof(WCHAR)));
+ Name.Length -= sizeof(WCHAR);
+
+ } else {
+
+ // Replace the colon with a backslash
+ Name.Buffer[x] = L'\\';
+
+ }
+ goto skip;
+ }
+ }
+
+
+skip:
+
+ RtlFreeUnicodeString( &TempUnicode );
+
+ // Strip trailing backslash if present.
+
+ if ((Name.Length >= sizeof(WCHAR) ) &&
+ (Name.Buffer[(Name.Length/sizeof(WCHAR)) - 1 ] == L'\\')) {
+
+ Name.Length -= sizeof(WCHAR);
+ }
+
+ // Return pointer to a null terminated wide char string.
+
+ Name.Buffer[Name.Length/sizeof(WCHAR)] = L'\0';
+ NwPrint(("Nw32: BuildUNC %ws\n", Name.Buffer));
+
+ return Name.Buffer;
+}
+
+
+VOID
+GetServerDateAndTime(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Implement Funtion E7h
+
+ ASSUMES called from Nw16Handler
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ none.
+
+--*/
+{
+
+ PUCHAR Reply = GetVDMPointer (
+ (ULONG)((getDS() << 16)|getDX()),
+ 7,
+ CpuInProtectMode
+ );
+
+ SendNCP2( NWR_ANY_NCP(0x14), NULL, 0, Reply, 7 );
+
+}
+
+VOID
+GetShellVersion(
+ IN USHORT Command
+ )
+/*++
+
+Routine Description:
+
+ Get the environment variables. Needs to be configurable for
+ Japanese machines.
+
+Arguments:
+
+ Command supplies the callers AX.
+
+Return Value:
+
+ none.
+
+--*/
+{
+
+ setAX(0); // MSDOS, PC
+ setBX(0x031a); // Shell version
+ setCX(0);
+
+ if ( (Command & 0x00ff) != 0) {
+
+ LONG tmp;
+ HKEY Key = NULL;
+ PUCHAR Reply = GetVDMPointer (
+ (ULONG)((getES() << 16)|getDI()),
+ 40,
+ CpuInProtectMode
+ );
+
+ ASSERT( sizeof(CLIENT_ID_STRING) <= 40 );
+
+ RtlMoveMemory( Reply, CLIENT_ID_STRING, sizeof(CLIENT_ID_STRING) );
+
+
+ //
+ // Open HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services
+ // \NWCWorkstation\Parameters
+ //
+ tmp = RegOpenKeyExW(
+ HKEY_LOCAL_MACHINE,
+ NW_WORKSTATION_REGKEY,
+ REG_OPTION_NON_VOLATILE, // options
+ KEY_READ, // desired access
+ &Key
+ );
+
+ if (tmp != ERROR_SUCCESS) {
+ return;
+ }
+
+ tmp = 40; // Max size for the string.
+
+ RegQueryValueExA(
+ Key,
+ "ShellVersion",
+ NULL,
+ NULL,
+ Reply,
+ &tmp);
+
+ ASSERT( tmp <= 40 );
+
+ RegCloseKey( Key );
+
+ }
+}
+
+#include <packon.h>
+
+typedef struct _TTSOUTPACKETTYPE {
+ UCHAR SubFunction;
+ USHORT cx;
+ USHORT dx;
+} TTSOUTPACKETTYPE;
+
+typedef struct _TTSINPACKETTYPE {
+ USHORT cx;
+ USHORT dx;
+} TTSINPACKETTYPE;
+
+#include <packoff.h>
+
+VOID
+TTS(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Transaction Tracking System
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ UCHAR bOutput;
+ UCHAR bInput[2];
+
+ TTSINPACKET TTsInPacket;
+ TTSOUTPACKET TTsOutPacket;
+
+
+ switch ( pNwDosTable->SavedAx & 0x00ff )
+ {
+ case 2:
+ // NCP Tts Available
+ bOutput = 0;
+ SendNCP2( NWR_ANY_F2_NCP(0x22), &bOutput, sizeof(UCHAR), NULL, 0);
+
+ if (getAL() == 0xFF) {
+ setAL(01);
+ }
+ break;
+
+ case 0:
+ // NCP Tts Begin/Abort
+ bOutput = 1;
+ SendNCP2( NWR_ANY_F2_NCP(0x22), &bOutput, sizeof(UCHAR), NULL, 0);
+ break;
+
+ case 3:
+ // NCP Tts Begin/Abort
+ bOutput = 3;
+ SendNCP2( NWR_ANY_F2_NCP(0x22), &bOutput, sizeof(UCHAR), NULL, 0);
+ break;
+
+ case 1:
+ // NCP Tts End
+ bOutput = 2;
+ SendNCP2( NWR_ANY_F2_NCP(0x22),
+ &bOutput, sizeof(UCHAR),
+ (PUCHAR)&TTsInPacket, sizeof(TTsInPacket));
+
+ setCX(TTsInPacket.cx);
+ setDX(TTsInPacket.dx);
+ break;
+
+ case 4:
+ // NCP Tts Status
+ TTsOutPacket.SubFunction = 4;
+ TTsOutPacket.cx = getCX();
+ TTsOutPacket.dx = getDX();
+
+ SendNCP2( NWR_ANY_F2_NCP(0x22),
+ (PUCHAR)&TTsOutPacket, sizeof(TTsOutPacket),
+ NULL, 0);
+
+ break;
+
+ case 5:
+ case 7:
+ // NCP Tts Get App/Station Thresholds
+ bOutput = (pNwDosTable->SavedAx & 0x00ff);
+
+ SendNCP2( NWR_ANY_F2_NCP(0x22),
+ &bOutput, sizeof(UCHAR),
+ bInput, sizeof(bInput));
+
+ setCX( (USHORT)((bInput[0] << 8 ) || bInput[1]) );
+ break;
+
+ case 6:
+ case 8:
+ // NCP Tts Set App/Station Thresholds
+ TTsOutPacket.SubFunction = (pNwDosTable->SavedAx & 0x00ff);
+ TTsOutPacket.cx = getCX();
+ SendNCP2( NWR_ANY_F2_NCP(0x22),
+ (PUCHAR)&TTsOutPacket, sizeof(UCHAR) + sizeof(USHORT),
+ NULL, 0);
+ break;
+
+ default:
+ pNwDosTable->SavedAx = 0xc7FF;
+ break;
+ }
+ return;
+}
+
+VOID
+OpenCreateFile(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Look at the file being opened to determine if it is
+ a compatibility mode open to a file on a NetWare drive.
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ WORD Command = pNwDosTable->SavedAx;
+
+ PUCHAR Name;
+
+
+ if ((Command & OF_SHARE_MASK ) != OF_SHARE_COMPAT) {
+ return;
+ }
+
+ Name = GetVDMPointer (
+ (ULONG)((getDS() << 16)|getDX()),
+ 256,
+ CpuInProtectMode
+ );
+
+
+ NwPrint(("Nw16Handler Compatibility Open of %s\n", Name ));
+
+ //
+ // We already know its a Create or Open with sharing options
+ // set to compatibility mode or the tsr wouldn't have bopped to us.
+ //
+
+
+ if (IsItNetWare(Name)) {
+
+ SetCompatibility();
+
+ }
+}
+
+BOOL
+IsItNetWare(
+ PUCHAR Name
+ )
+/*++
+
+Routine Description:
+
+ Look at the filename being opened to determine if it is on a NetWare drive.
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ UCHAR Drive;
+
+ Drive = tolower(Name[0])-'a';
+
+ NwPrint(("Nw16Handler IsItNetWare %s\n", Name ));
+
+ if (Name[1] == ':') {
+
+ if (pNwDosTable->DriveFlagTable[Drive] == LOCAL_DRIVE) {
+
+ // Definitely not a netware drive.
+ return FALSE;
+ }
+
+ } else if ((IS_ASCII_PATH_SEPARATOR(Name[0])) &&
+ (IS_ASCII_PATH_SEPARATOR(Name[0]))) {
+
+ // Assume only UNC names that the tsr built are NetWare
+
+ if ((getDS() == DosTableSegment ) &&
+ (getDX() == (WORD)(DosTableOffset + FIELD_OFFSET(NWDOSTABLE, DeNovellBuffer[0] )))) {
+
+ return TRUE;
+ }
+
+ return FALSE;
+
+ } else {
+
+ Drive = pNwDosTable->CurrentDrive;
+
+ }
+
+ //
+ // If this is a drive we don't know about, refresh our tables.
+ //
+
+ if (pNwDosTable->DriveFlagTable[Drive] == 0 ) {
+
+ Nw16Register();
+
+ }
+
+ if (pNwDosTable->DriveFlagTable[Drive] &
+ (TEMPORARY_NETWORK_DRIVE | PERMANENT_NETWORK_DRIVE )) {
+
+ return TRUE;
+
+ }
+
+ return FALSE;
+
+}
+
+VOID
+SetCompatibility(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Take the Create/Open file request in AX and modify appropriately
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ WORD Command = getAX();
+
+ if (( Command & OF_READ_WRITE_MASK) == OF_READ ) {
+
+ setAX((WORD)(Command | OF_SHARE_DENY_WRITE));
+
+ } else {
+
+ setAX((WORD)(Command | OF_SHARE_EXCLUSIVE));
+
+ }
+
+}
+
+VOID
+OpenQueueFile(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Build the UNC filename \\server\queue using the contents of the shared
+ datastructures and the CreateJobandFile NCP.
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ none.
+
+--*/
+{
+
+ CONN_INDEX Connection = SelectConnection();
+ PUCHAR Request;
+ PUCHAR Buffer = pNwDosTable->DeNovellBuffer;
+ int index;
+
+ if ( Connection == 0xff ) {
+ //
+ // No need to return an errorcode. The NCP exchange
+ // will fail and give an appropriate call to the application.
+ //
+
+ return;
+ }
+
+ if ( ServerHandles[Connection] == NULL ) {
+
+ NTSTATUS status;
+
+ status = OpenConnection( Connection );
+
+ if (!NT_SUCCESS(status)) {
+ SetStatus(status);
+ return;
+ }
+ }
+
+ //
+ // CreateJobandQueue open in progress. The purpose of this
+ // open being processed is to translate the information in
+ // the CreateJob NCP into a pathname to be opened by the 16
+ // bit code.
+ //
+
+
+ //
+ // Users DS:SI points at a CreateJob NCB. Inside the request is
+ // the objectid of the queue. Ask the server for the queue name.
+ //
+
+ Request = GetVDMPointer (
+ (ULONG)((getDS() << 16)|getSI()),
+ 8,
+ CpuInProtectMode);
+
+ NwlibMakeNcp(
+ ServerHandles[Connection],
+ FSCTL_NWR_NCP_E3H,
+ 7, // RequestSize
+ 61, // ResponseSize
+ "br|_r",
+ 0x36, // Get Bindery Object Name
+ Request+3, 4,
+ 6, // Skip ObjectId and Type
+ pNwDosTable->DeNovellBuffer2, 48 );
+
+
+ pNwDosTable->DeNovellBuffer2[54] = '\0';
+
+ Buffer[0] = '\\';
+ Buffer[1] = '\\';
+ Buffer += 2; // Point to after backslashes
+
+ // Copy the servername
+ for (index = 0; index < sizeof(SERVERNAME); index++) {
+ Buffer[index] = pNwDosTable->ServerNameTable[Connection][index];
+ if (Buffer[index] == '\0') {
+ break;
+ }
+ }
+
+ Buffer[index] = '\\';
+
+ RtlCopyMemory( &Buffer[index+1], &pNwDosTable->DeNovellBuffer2[0], 48 );
+
+ NwPrint(("Nw32: CreateQueue Job and File %s\n", pNwDosTable->DeNovellBuffer));
+
+ //
+ // Set up 16 bit registers to do the DOS OpenFile for \\server\queue
+ //
+
+ setDS((WORD)(CpuInProtectMode ? pNwDosTable->PmSelector : DosTableSegment));
+ setDX( (WORD)(DosTableOffset + FIELD_OFFSET(NWDOSTABLE, DeNovellBuffer[0] )) );
+ setAX(0x3d02); // Set to OpenFile
+
+}
+
+VOID
+AttachHandle(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine implements Int 21 B4. Which is supposed to create a
+ Dos Handle that corresponds to a specified 6 byte NetWare handle.
+
+ This is used as a replacement for doing a DosOpen on "NETQ" and usin the
+ handle returned from there.
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ none.
+
+--*/
+{
+
+ if ( pNwDosTable->CreatedJob ) {
+
+ NwPrint(("Nw32: AttachHandle %x\n", pNwDosTable->JobHandle));
+ setAX( pNwDosTable->JobHandle );
+ pNwDosTable->CreatedJob = 0; // Only return it once.
+
+ } else {
+
+ NwPrint(("Nw32: AttachHandle failed, no job\n"));
+ setAX(ERROR_FILE_NOT_FOUND);
+ setCF(1);
+
+ }
+}
+
+VOID
+ProcessExit(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Cleanup all cached handles. Unmap all temporary drives.
+
+ Cleanup the server name table so that if another dos app
+ is started we reload all the useful information such as
+ the servers connection number.
+
+ Note: Dos always completes processing after we complete.
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ UCHAR Connection;
+ UCHAR Drive;
+ USHORT Command = pNwDosTable->SavedAx;
+
+ ResetLocks();
+
+ for (Drive = 0; Drive < MD; Drive++) {
+
+ NwPrint(("Nw32: Deallocate directory handle %c\n", 'A' + Drive));
+
+ if (Win32DirectoryHandleTable[Drive] != 0) {
+
+ CloseHandle( Win32DirectoryHandleTable[Drive] );
+ Win32DirectoryHandleTable[Drive] = 0;
+ pNwDosTable->DriveHandleTable[Drive] = 0;
+
+ }
+ }
+
+ for (Connection = 0; Connection < MC ; Connection++ ) {
+ if (pNwDosTable->ConnectionIdTable[Connection].ci_InUse == IN_USE) {
+
+ CloseConnection(Connection);
+
+ pNwDosTable->ConnectionIdTable[Connection].ci_InUse = FREE;
+
+ ZeroMemory( pNwDosTable->ServerNameTable[Connection], SERVERNAME_LENGTH );
+ }
+ }
+
+ pNwDosTable->PreferredServer = 0;
+
+ LockMode = 0;
+ TablesValid = FALSE;
+ DriveHandleTableValid = FALSE;
+
+#if NWDBG
+ if (DebugCtrl & ~3 ) {
+ DebugControl( 2 ); // Close logfile
+ }
+ GotDebugState = FALSE;
+#endif
+
+ //
+ // set AX register so that AH gets preserved
+ //
+
+ setAX( Command );
+}
+
+VOID
+SystemLogout(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This api is called by the NetWare login.
+
+ Remove all NetWare redirected drives and logout connections
+ that don't have open handles on them. Don't detach the connections.
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ none.
+
+--*/
+{
+
+ UCHAR Connection;
+ UCHAR Drive;
+ USHORT Command = pNwDosTable->SavedAx;
+
+ ResetLocks();
+
+ for (Drive = 0; Drive < MD; Drive++) {
+ ResetDrive(Drive);
+ }
+
+ for (Connection = 0; Connection < MC ; Connection++ ) {
+ if (pNwDosTable->ConnectionIdTable[Connection].ci_InUse == IN_USE) {
+
+ if ( ServerHandles[Connection] == NULL ) {
+ OpenConnection( Connection );
+ }
+
+ if (ServerHandles[Connection] != NULL ) {
+
+ NwlibMakeNcp(
+ ServerHandles[Connection],
+ NWR_ANY_F2_NCP(NCP_LOGOUT),
+ 0, // RequestSize
+ 0, // ResponseSize
+ "");
+
+ CloseConnection(Connection);
+ }
+
+ //pNwDosTable->ConnectionIdTable[Connection].ci_InUse = FREE;
+
+ //ZeroMemory( pNwDosTable->ServerNameTable[Connection], SERVERNAME_LENGTH );
+ }
+ }
+
+ pNwDosTable->PreferredServer = 0;
+ pNwDosTable->PrimaryServer = 0;
+
+ // No servers in the table so find the nearest/preferred.
+
+ LoadPreferredServerName();
+
+ //
+ // set AX register so that AH gets preserved
+ // and AL says success.
+ //
+
+ setAX( (USHORT)(Command & 0xff00) );
+}
+
+UCHAR
+AttachmentControl(
+ ULONG Command
+ )
+/*++
+
+Routine Description:
+
+ Implement Funtion F1h
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ Return status.
+
+--*/
+{
+ UCHAR Connection = getDL();
+
+ if ((Connection < 1) ||
+ (Connection > MC)) {
+ return 0xf7;
+ }
+
+ Connection -= 1;
+
+ switch (Command & 0x00ff) {
+
+ case 0: // Attach
+
+ NwPrint(("Nw16AttachmentControl: Attach connection %d\n", Connection));
+
+ pNwDosTable->ConnectionIdTable[Connection].ci_InUse = IN_USE;
+
+ if ( ServerHandles[Connection] == NULL ) {
+
+ NTSTATUS status = OpenConnection( Connection );
+
+ if (!NT_SUCCESS(status)) {
+ pNwDosTable->ConnectionIdTable[Connection].ci_InUse = FREE;
+ ZeroMemory( pNwDosTable->ServerNameTable[Connection], SERVERNAME_LENGTH );
+ return (UCHAR)RtlNtStatusToDosError(status);
+ } else {
+ InitConnection(Connection);
+ }
+ }
+
+ return 0;
+ break;
+
+ case 1: // Detach
+
+ NwPrint(("Nw16AttachmentControl: Detach connection %d\n", Connection));
+
+ if (pNwDosTable->ConnectionIdTable[Connection].ci_InUse != IN_USE) {
+ return 0xff;
+ } else {
+
+ pNwDosTable->ConnectionIdTable[Connection].ci_InUse = FREE;
+
+ if (ServerHandles[Connection] != NULL ) {
+ CloseConnection(Connection);
+ }
+
+ ZeroMemory( pNwDosTable->ServerNameTable[Connection], SERVERNAME_LENGTH );
+
+ if (pNwDosTable->PrimaryServer == (UCHAR)Connection + 1 ) {
+
+ // Need to pick another
+ UCHAR IndexConnection;
+
+ pNwDosTable->PrimaryServer = 0;
+
+ for (IndexConnection = 0; IndexConnection < MC ; IndexConnection++ ) {
+
+ if (pNwDosTable->ConnectionIdTable[IndexConnection].ci_InUse == IN_USE) {
+
+ pNwDosTable->PrimaryServer = IndexConnection + 1;
+
+ }
+ }
+
+ }
+
+ if (pNwDosTable->PreferredServer == (UCHAR)Connection + 1 ) {
+ pNwDosTable->PreferredServer = 0;
+ }
+
+ return 0;
+ }
+
+ case 2: // Logout
+
+ NwPrint(("Nw16AttachmentControl: Logout connection %d\n", Connection));
+
+ if (pNwDosTable->ConnectionIdTable[Connection].ci_InUse != IN_USE) {
+ return 0xff;
+ } else {
+
+ UCHAR Drive;
+
+ if ( ServerHandles[Connection] == NULL ) {
+ OpenConnection( Connection );
+ }
+
+ for (Drive = 0; Drive < MD; Drive++ ) {
+ if (pNwDosTable->DriveIdTable[ Drive ] == (Connection + 1)) {
+ ResetDrive(Drive);
+ }
+ }
+
+ if (ServerHandles[Connection] != NULL ) {
+ NwlibMakeNcp(
+ ServerHandles[Connection],
+ NWR_ANY_F2_NCP(NCP_LOGOUT),
+ 0, // RequestSize
+ 0, // ResponseSize
+ "");
+ CloseConnection(Connection);
+ }
+
+ return 0;
+ }
+
+ }
+ return 0xff;
+}
+
+VOID
+ServerFileCopy(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Build the NCP that tells the server to move a file on the server.
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ none.
+
+--*/
+{
+
+ DWORD BytesReturned;
+ UCHAR SrcHandle[6];
+ UCHAR DestHandle[6];
+ NTSTATUS status;
+ PUCHAR Buffer;
+
+ Buffer = GetVDMPointer (
+ (ULONG)((getES() << 16)|getDI()),
+ 16,
+ CpuInProtectMode
+ );
+
+ if ( DeviceIoControl(
+ GET_NT_SRCHANDLE(),
+ IOCTL_NWR_RAW_HANDLE,
+ NULL,
+ 0,
+ (PUCHAR)&SrcHandle,
+ sizeof(SrcHandle),
+ &BytesReturned,
+ NULL ) == FALSE ) {
+
+ setAL(0xff);
+ return;
+
+ }
+
+ if ( DeviceIoControl(
+ GET_NT_HANDLE(),
+ IOCTL_NWR_RAW_HANDLE,
+ NULL,
+ 0,
+ (PUCHAR)&DestHandle,
+ sizeof(DestHandle),
+ &BytesReturned,
+ NULL ) == FALSE ) {
+
+ setAL(0xff);
+ return;
+
+ }
+
+ status = NwlibMakeNcp(
+ GET_NT_SRCHANDLE(),
+ NWR_ANY_F2_NCP(0x4A),
+ 25, // RequestSize
+ 4, // ResponseSize
+ "brrddd|d",
+ 0,
+ SrcHandle, 6,
+ DestHandle, 6,
+ *(DWORD UNALIGNED*)&Buffer[4],
+ *(DWORD UNALIGNED*)&Buffer[8],
+ *(DWORD UNALIGNED*)&Buffer[12],
+ &BytesReturned
+ );
+
+ setDX((WORD)(BytesReturned >> 16));
+ setCX((WORD)BytesReturned);
+
+ if (!NT_SUCCESS(status)) {
+ SetStatus(status);
+ return;
+ } else {
+ setAL(0);
+ }
+}
+
+VOID
+SetStatus(
+ NTSTATUS Status
+ )
+/*++
+
+Routine Description:
+
+ Convert an NTSTATUS into the appropriate register settings and updates
+ to the dos tables.
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ UCHAR DosStatus = (UCHAR)RtlNtStatusToDosError(Status);
+
+ if ((!DosStatus) &&
+ (Status != 0)) {
+
+ //
+ // We have a connection bit set
+ //
+
+ if ( Status & (NCP_STATUS_BAD_CONNECTION << 8)) {
+ DosStatus = 0xfc;
+ } else {
+ DosStatus = 0xff;
+ }
+ }
+
+ if (DosStatus) {
+ setCF(1);
+ }
+
+ setAL(DosStatus);
+}
diff --git a/private/nw/nw16/dll/nwapi16.rc b/private/nw/nw16/dll/nwapi16.rc
new file mode 100644
index 000000000..18206f077
--- /dev/null
+++ b/private/nw/nw16/dll/nwapi16.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "NW Windows/Dos API DLL"
+#define VER_INTERNALNAME_STR "NwApi16.DLL"
+#define VER_ORIGINALFILENAME_STR "NwApi16.DLL"
+
+#include "common.ver"
+
diff --git a/private/nw/nw16/dll/nwapi16.src b/private/nw/nw16/dll/nwapi16.src
new file mode 100644
index 000000000..6036c090d
--- /dev/null
+++ b/private/nw/nw16/dll/nwapi16.src
@@ -0,0 +1,10 @@
+LIBRARY NWAPI16
+
+DESCRIPTION 'NWAPI16'
+
+EXPORTS
+
+ Nw16Register
+ Nw16Handler
+
+DATA SINGLE SHARED
diff --git a/private/nw/nw16/dll/procs.h b/private/nw/nw16/dll/procs.h
new file mode 100644
index 000000000..cf51577cc
--- /dev/null
+++ b/private/nw/nw16/dll/procs.h
@@ -0,0 +1,159 @@
+
+/*++
+
+Copyright (c) 1993/4 Microsoft Corporation
+
+Module Name:
+
+ procs.c
+
+Abstract:
+
+ Common header file for routines which support 16 bit
+ applications.
+
+Author:
+
+ Colin Watson (colinw) 21-Nov-1993
+
+Environment:
+
+
+Revision History:
+
+
+--*/
+
+#ifndef DBG
+#define DBG 0
+#endif
+
+#if !DBG
+#undef NWDBG
+#endif
+
+#define UNICODE
+
+#include <stdlib.h>
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include <string.h> // strcmp
+#include <stdio.h>
+#include <stdarg.h>
+#include <debugfmt.h> // FORMAT_LPSTR
+
+#include <nwapi.h>
+#include <nwxchg.h>
+#include <ntddnwfs.h>
+#include <npapi.h>
+#include <nwrnames.h>
+
+#include <vddsvc.h>
+#include <nwdos.h>
+#include <ncp.h>
+
+// Locks.c
+
+VOID
+Locks(
+ USHORT Command
+ );
+
+VOID
+InitLocks(
+ VOID
+ );
+
+VOID
+ResetLocks(
+ VOID
+ );
+
+
+// Ncp.c
+
+extern PNWDOSTABLE pNwDosTable;
+extern HANDLE ServerHandles[MC];
+
+CONN_INDEX
+SelectConnection(
+ VOID
+ );
+
+NTSTATUS
+OpenConnection(
+ CONN_INDEX Connection
+ );
+
+ULONG
+GetDirectoryHandle2(
+ DWORD Drive
+ );
+
+
+#define GET_NT_HANDLE() (HANDLE)(pNwDosTable->NtHandleHi << 16 | pNwDosTable->NtHandleLow)
+#define GET_NT_SRCHANDLE() (HANDLE)(pNwDosTable->NtHandleSrcHi << 16 | pNwDosTable->NtHandleSrcLow)
+
+
+//
+// MSW_PE: Machine Status Word Protect-mode enable bit
+//
+
+#ifndef MSW_PE
+#define MSW_PE 0x0001
+#endif
+
+#undef getMSW // BUGBUG: there's no c_getMSW in the lib!!!
+extern WORD getMSW(VOID);
+
+#define IS_PROTECT_MODE() (UCHAR)((getMSW() & MSW_PE)? TRUE : FALSE)
+
+#if NWDBG
+
+#define NwPrint(String) NwPrintf String;
+
+VOID
+DebugControl(
+ int Command
+ );
+
+VOID
+NwPrintf(
+ char *Format,
+ ...
+ );
+
+VOID
+VrDumpRealMode16BitRegisters(
+ IN BOOL DebugStyle
+ );
+
+VOID
+VrDumpNwData(
+ VOID
+ );
+
+VOID
+DisplayExtendedError(
+ VOID
+ );
+
+VOID
+FormattedDump(
+ PCHAR far_p,
+ LONG len
+ );
+
+#else
+
+#define NwPrint(_x_)
+#define VrDumpRealMode16BitRegisters(_x_)
+#define VrDumpNwData( )
+#define DisplayExtendedError( )
+#define FormattedDump(_x_,_y_)
+
+#endif
diff --git a/private/nw/nw16/dll/sources b/private/nw/nw16/dll/sources
new file mode 100644
index 000000000..fe9eb5d4d
--- /dev/null
+++ b/private/nw/nw16/dll/sources
@@ -0,0 +1,71 @@
+!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=nwapi16
+
+TARGETNAME=nwapi16
+TARGETPATH=$(BASEDIR)\public\sdk\lib
+TARGETTYPE=DYNLINK
+DLLDEF=obj\*\nwapi16.def
+#DLLENTRY=NwApiInitialize
+DLLBASE=0x6950000
+MSC_WARNING_LEVEL=/W3 /WX
+
+INCLUDES=..\..\inc;$(_NTROOT)\private\inc;$(_NTROOT)\private\mvdm\vdd\h;..\inc
+
+SOURCES= \
+ debug.c \
+ ncp.c \
+ locks.c \
+ nwapi16.rc
+
+TARGETLIBS= \
+ $(BASEDIR)\Public\Sdk\Lib\*\kernel32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\advapi32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\ntvdm.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\user32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\mpr.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\nwapi32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\nwprovau.lib
+
+UNICODE=1
+
+USE_NTDLL=1
+
+NET_C_DEFINES=-DRPC_NO_WINDOWS_H -DNWDBG=1
+
+UMTYPE=console
+
+UMTEST=
+
+UMLIBS= \
+ $(BASEDIR)\Public\Sdk\Lib\*\nwapi32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\mpr.lib
+
+OPTIONAL_UMTEST=
+
+!IFDEF MARS_PCH
+PRECOMPILED_INCLUDE=procs.h
+PRECOMPILED_PCH=procs.pch
+PRECOMPILED_OBJ=procs.obj
+!ENDIF
diff --git a/private/nw/nw16/drv/dllentry.asm b/private/nw/nw16/drv/dllentry.asm
new file mode 100644
index 000000000..fce965935
--- /dev/null
+++ b/private/nw/nw16/drv/dllentry.asm
@@ -0,0 +1,81 @@
+PAGE,132
+;***************************************************************************
+;*
+;* DLLENTRY.ASM
+;*
+;* VER.DLL Entry code
+;*
+;* This module generates a code segment called INIT_TEXT.
+;* It initializes the local heap if one exists and then calls
+;* the C routine LibMain() which should have the form:
+;* BOOL FAR PASCAL LibMain(HANDLE hInstance,
+;* WORD wDataSeg,
+;* WORD cbHeap,
+;* LPSTR lpszCmdLine);
+;*
+;* The result of the call to LibMain is returned to Windows.
+;* The C routine should return TRUE if it completes initialization
+;* successfully, FALSE if some error occurs.
+;*
+;**************************************************************************
+
+ INCLUDE CMACROS.INC
+
+externFP <LIBMAIN> ;The C routine to be called
+
+ifndef SEGNAME
+ SEGNAME equ <_TEXT> ; default seg name
+endif
+
+createSeg %SEGNAME, CodeSeg, word, public, CODE
+
+
+sBegin CodeSeg ; this defines what seg this goes in
+assumes cs,CodeSeg
+
+?PLM=0 ;'C'naming
+externA <_acrtused> ;Ensures that Win DLL startup code is linked
+
+?PLM=1 ;'PASCAL' naming
+externFP <LOCALINIT> ;Windows heap init routine
+
+cProc LibEntry, <PUBLIC,FAR> ;Entry point into DLL
+
+cBegin
+ push di ;Handle of the module instance
+ push ds ;Library data segment
+ push cx ;Heap size
+ push es ;Command line segment
+ push si ;Command line offset
+
+ ;** If we have some heap then initialize it
+ jcxz callc ;Jump if no heap specified
+
+ ;** Call the Windows function LocalInit() to set up the heap
+ ;** LocalInit((LPSTR)start, WORD cbHeap);
+
+ xor ax,ax
+ cCall LOCALINIT <ds, ax, cx>
+ or ax,ax ;Did it do it ok ?
+ jz error ;Quit if it failed
+
+ ;** Invoke the C routine to do any special initialization
+
+callc:
+ call LIBMAIN ;Invoke the 'C' routine (result in AX)
+ jmp short exit ;LibMain is responsible for stack clean up
+
+error:
+ pop si ;Clean up stack on a LocalInit error
+ pop es
+ pop cx
+ pop ds
+ pop di
+exit:
+
+cEnd
+
+sEnd _thisseg
+
+ END LibEntry
+
diff --git a/private/nw/nw16/drv/ints.asm b/private/nw/nw16/drv/ints.asm
new file mode 100644
index 000000000..1dfe878ca
--- /dev/null
+++ b/private/nw/nw16/drv/ints.asm
@@ -0,0 +1,368 @@
+page ,132
+
+if 0
+
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ ints.asm
+
+Abstract:
+
+ Contains handler for Windows protect-mode NetwareRequest function, exported
+ by NETWARE.DRV. Code in this file access real mode memory via an LDT descriptor
+ created especially for this purpose. This selector gives us access to all
+ code and data contained in the Nw16 TSR
+
+Author:
+
+ Richard L Firth 22-Jan-1994
+
+Environment:
+
+ Windows protect mode only
+
+Revision History:
+
+ 22-Jan-1994 rfirth
+ Created
+
+--*/
+
+endif
+
+include nwdos.inc ; NWDOSTABLE_ASM structure
+include isvbop.inc ; DispatchCall
+
+.286
+.model medium,pascal
+
+_DATA segment word public 'DATA'
+
+OldInt21Handler dd ?
+RMSegment dw ?
+RMBase dw ? ; MUST be in this order - loaded
+RMSelector dw ? ; via lds dx,word ptr RMBase
+
+.errnz (RMSelector - (RMBase + 2))
+
+_DATA ends
+
+;
+; code segment ordering
+;
+
+INIT_TEXT segment byte public 'CODE'
+INIT_TEXT ends
+
+_TEXT segment byte public 'CODE'
+_TEXT ends
+
+;
+; macros
+;
+
+LOAD_DS macro
+ push _DATA
+ pop ds
+ assume ds:_DATA
+ endm
+
+SET_DS macro
+ push ds
+ LOAD_DS
+ endm
+
+RESTORE_DS macro
+ pop ds
+ assume ds:nothing
+ endm
+
+LOAD_RM_DS_BX macro
+ LOAD_DS
+ lds bx,dword ptr RMBase
+ assume ds:nothing
+ endm
+
+RESTORE_DS_BX macro
+ RESTORE_DS
+ pop bx
+ endm
+
+INIT_TEXT segment byte public 'CODE'
+
+ assume cs:INIT_TEXT
+
+ public GetLowRedirInfo
+GetLowRedirInfo proc far
+ mov ax,9f00h
+ int 21h ; get the RM data segment in BX
+ jc @f
+ SET_DS
+ mov RMSegment,bx
+ mov RMBase,dx
+ mov ax,2
+ int 31h
+ jc @f ; can't create selector
+ mov RMSelector,ax
+
+;
+; now that we have the selector, we write the selector value into the low
+; memory area. The 32-bit DLL will use this value when setting output DS or ES
+; register values if the call originated in Protect Mode
+;
+
+ lds bx,dword ptr RMBase
+ mov [bx]._PmSelector,ax
+
+;
+; we now hook int 21
+;
+
+ LOAD_DS
+ push es
+ mov ax,3521h
+ int 21h
+ mov word ptr OldInt21Handler,bx
+ mov word ptr OldInt21Handler[2],es
+ mov cx,_TEXT
+ mov dx,offset _TEXT:NewInt21Handler
+ mov ax,205h
+ mov bl,21h
+ int 31h
+ pop es
+ RESTORE_DS
+ xor ax,ax ; success: return TRUE
+ inc ax
+ ret
+@@: xor ax,ax ; failure: return FALSE
+ ret
+GetLowRedirInfo endp
+
+INIT_TEXT ends
+
+_TEXT segment byte public 'CODE'
+
+ assume cs:_TEXT
+
+ public NewInt21Handler
+NewInt21Handler proc far
+ sti
+ cmp ah,0e3h
+ jb @f
+ call far ptr NetwareRequest
+ retf 2
+@@: sub sp,4
+ push bp
+ mov bp,sp
+ push es
+ push bx
+ SET_DS
+ les bx,OldInt21Handler
+ mov [bp+2],bx
+ mov [bp+4],es
+ RESTORE_DS
+ pop bx
+ pop es
+ pop bp
+ retf
+NewInt21Handler endp
+
+ public NetwareRequest
+NetwareRequest proc far
+ push bx
+ push ds
+ LOAD_RM_DS_BX
+ cmp ah,0f0h
+ jne for_dll
+
+;
+; these are the 0xF000, 0xF001, 0xF002, 0xF004, 0xF005 calls that we can handle
+; here without having to BOP. All we need do is access the table in the shared
+; real-mode/protect-mode (low) memory
+;
+
+.errnz (_PrimaryServer - (_PreferredServer + 1))
+
+;
+; point bx at PreferredServer in the low memory area. If the request is a
+; PrimaryServer request (0xF004, 0xF005) then point bx at PrimaryServer
+;
+
+ lea bx,[bx]._PreferredServer; bx = offset of PreferredServer
+ cmp al,3
+ cmc
+ adc bx,0 ; bx = &PrimaryServer if F004 or F005
+ or al,al ; f000 = set preferred server
+ jz set_server
+ cmp al,4 ; f004 = set primary server
+ jnz try_01
+
+;
+; 0xF000 or 0xF004: set Preferred or Primary Server to value contained in DL.
+; If DL > 8, set respective server index to 0
+;
+
+set_server:
+ xor al,al
+ cmp dl,8
+ ja @f
+ mov al,dl
+@@: mov [bx],al
+ jmp short exit_f0
+
+;
+; 0xF001 or 0xF005: get Preferred or Primary Server
+;
+
+try_01: cmp al,1 ; f001 = get preferred server
+ jz get_server
+ cmp al,5
+ jnz try_02
+
+get_server:
+ mov al,[bx]
+ jmp short exit_f0
+
+try_02: cmp al,2 ; f002 = get default server
+ jnz for_dll ; try to handle on 32-bit side
+ mov al,[bx] ; al = PreferredServer
+ or al,al
+ jnz exit_f0
+ mov al,[bx+1] ; al = PrimaryServer
+
+exit_f0:RESTORE_DS_BX
+ ret
+
+;
+; if we're here then the call must go through to the 32-bit DLL. Save any relevant
+; info in the low memory area, load the handle and BOP (DispatchCall)
+;
+
+for_dll:mov [bx]._SavedAx,ax ; save AX value for DLL
+ push word ptr [bx]._hVdd ; put VDD handle on top of stack
+
+ cmp ah,0BCh ; bc, bd, be need handle mapping
+ jb @f
+ cmp ah,0BEh
+ ja @f
+ pop ax ; ax = hVdd
+ RESTORE_DS_BX ; ds, bx = user ds, bx
+ call MapNtHandle
+ jmp dispatchola
+
+@@: push bp
+ cmp ah, 0E3h ; Is it new or old Create Job request?
+ je lookupcode
+ cmp ax, 0F217h
+ jne check_f3
+
+lookupcode:
+ mov bp,sp
+ mov ds,[bp+4]
+ cmp byte ptr [si+2],68h
+ je createjob
+ cmp byte ptr [si+2],79h
+ je createjob
+ jmp short outtahere
+
+createjob:
+ LOAD_RM_DS_BX
+ mov [bx]._SavedAx,9f02h
+ push ax ; Open \\Server\queue for NCP
+ mov ax,[bp+2] ; ax = hVdd
+ mov ds,[bp+4] ; ds = users ds
+ push ds
+ push dx ; users dx
+ DispatchCall ; Set DeNovellBuffer to \\Server\queue
+ ; and registers ready for DOS OpenFile
+ int 21h ; Open \\server\queue
+ LOAD_RM_DS_BX
+ jc openfailed
+ mov [bx]._JobHandle, al
+ mov [bx]._CreatedJob, 1 ; Flag JobHandle is valid
+ push bx
+ mov bx, ax ; JobHandle
+ call MapNtHandle ; take bx and find the Nt handle
+ pop bx
+
+openfailed:
+ pop dx
+ pop ds ; Proceed and send the NCP
+ pop ax
+
+ push ds
+ push bx
+ LOAD_RM_DS_BX
+ mov [bx]._SavedAx, ax
+ pop bx
+ pop ds ; users DS
+ jmp short outtahere
+
+check_f3:
+ cmp ah, 0F3h
+ jne outtahere
+ ; FileServerCopy, change both
+ ; handles in the structure es:di
+ push bx
+
+ mov bx,word ptr es:[di] ; Map Source Handle
+ call MapNtHandle
+
+ pop bx
+ mov ax,[bx]._NtHandleHi
+ mov [bx]._NtHandleSrcHi,ax
+ mov ax,[bx]._NtHandleLow
+ mov [bx]._NtHandleSrcLow,ax
+
+ mov bx,word ptr es:[di+2] ; Map Destination Handle
+ call MapNtHandle
+
+outtahere:
+ pop bp
+ pop ax ; ax = hVdd
+ RESTORE_DS_BX ; ds, bx = user ds, bx
+dispatchola:
+ DispatchCall ; BOP: DLL performs action
+ ret ; return to the application
+
+;
+; if the request was not recognized by the DLL, it modifies IP so that control
+; will resume at the next int 21. We just fill the intervening space with NOPs
+; (space that makes up a retf <n> instruction in the RM TSR)
+;
+
+ nop
+ nop
+ int 21h
+ ret
+NetwareRequest endp
+
+; *** MapNtHandle
+; *
+; * Given a handle in BX, map it to a 32-bit Nt handle in NtHandle[Hi|Low]
+; *
+; * ENTRY bx = handle to map
+; *
+; * EXIT Success - NtHandle set to 32-bit Nt handle from SFT
+; *
+; * USES ax, bx, flags
+; *
+; * ASSUMES nothing
+; *
+; ***
+
+MapNtHandle proc near
+ push ax
+ mov ax,9f01h ; call MapNtHandle on (BX) in RM
+ int 21h ; update NtHandleHi, NtHandleLow
+ pop ax
+@@: ret
+MapNtHandle endp
+
+_TEXT ends
+
+end
diff --git a/private/nw/nw16/drv/makefile b/private/nw/nw16/drv/makefile
new file mode 100644
index 000000000..7b44d0246
--- /dev/null
+++ b/private/nw/nw16/drv/makefile
@@ -0,0 +1,127 @@
+# netware.drv makefile
+#
+# Copyright (c) 1991-1993 Microsoft Corporation
+#
+# History:
+# Created 25-Mar-1993 Chuck Y. Chan (ChuckC)
+#
+
+!IFDEF USEBUILD
+
+# If using BUILD.EXE, 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
+
+!ELSE
+
+.SUFFIXES:
+.SUFFIXES: .c .asm .h .inc .obj .lst .sys .exe .com .map .sym .def .lib .dll
+
+WOW16 =..\..\..\mvdm\wow16
+
+! ifdef INCLUDE
+WBIN =
+INCS =
+! else
+WBIN = $(WOW16)\bin^\
+CINCS = -I. -I$(WOW16)\inc
+ASMINCS = $(CINCS) -I..\inc -I\nt\public\sdk\inc
+! endif
+
+# DEFINES = -DWOW -DDEBUG $(MVDMFLAGS)
+DEFINES = -DWOW $(MVDMFLAGS) -DBUILDDLL
+
+AOBJ = -Ml -t $(DEFINES) $(ASMINCS)
+
+CW16 = -AS -G2sw -Os -W3 -Zp $(DEFINES) $(CINCS)
+CW16B = $(CW16) -B1 c1l.exe -B2 c2l.exe -B3 c3l.exe
+
+LPATH = ..\..\tools.os2
+LINKFLAG= /map
+
+! ifdef LIB
+W16LIBS = sdllcew
+! else
+W16LIBS = $(WOW16)\lib\sdllcew.lib
+! endif
+
+
+! IF "$(QFE_BUILD)" != "1"
+CL16=cl16
+! ELSE
+CL16=cl
+! ENDIF
+
+PATH=..\..\..\mvdm\tools16;$(PATH)
+
+.asm.obj:
+ masm $(AOBJ) $*;
+
+.asm.lst:
+ masm $(AOBJ) -l $*,nul,$*.lst;
+
+
+.c.obj:
+ $(CL16) -c -nologo $(CW16) $*.c
+
+.c.lst:
+ $(CL16) -c -nologo $(CW16) -Fonul -Fc$*.lst $*.c
+
+
+.def.lib:
+ implib $*.lib $*.def
+
+.map.sym:
+ $(WBIN)mapsym $*
+
+
+all: netware.drv netware.sym
+ binplace netware.drv
+ binplace netware.sym
+
+clean:
+ if exist *.lrf del *.lrf
+ if exist *.obj del *.obj
+ if exist *.exe del *.exe
+ if exist *.dll del *.dll
+ if exist *.map del *.map
+ if exist *.sym del *.sym
+ if exist *.drv del *.drv
+
+
+nwinit.obj: nwinit.c .\netware.h .\nwerror.h
+ $(CL16) -c -nologo $(CW16) $*.c
+
+! ifdef NTVDM_BASED_BUILD
+LINK16 = link16
+RC16 = rc16
+! else
+LINK16 = $(LPATH)\link
+RC16 = $(LPATH)\rc
+! endif
+
+! if exist ($(WOW16)\lib\libw.lib) && exist ($(WOW16)\lib\sdllcew.lib)
+
+netware.drv: nwinit.obj dllentry.obj nwasmutl.obj netware.def ints.obj
+ $(LINK16) @<<netware.lrf
+nwinit.obj+
+dllentry.obj+
+nwasmutl.obj+
+ints.obj
+netware.drv
+netware $(LINKFLAG)
+$(WOW16)\lib\libw.lib+
+$(WOW16)\lib\sdllcew.lib /nod
+netware;
+<<KEEP
+ $(RC16) netware.drv
+
+! else
+
+netware.drv: nwinit.obj dllentry.obj nwasmutl.obj netware.def ints.obj
+ @echo Nothing to build yet... No libraries
+! endif
+
+!endif
diff --git a/private/nw/nw16/drv/netware.def b/private/nw/nw16/drv/netware.def
new file mode 100644
index 000000000..fa251cdc5
--- /dev/null
+++ b/private/nw/nw16/drv/netware.def
@@ -0,0 +1,21 @@
+LIBRARY NETWARE
+DESCRIPTION 'NETWARE '
+EXETYPE WINDOWS
+CODE MOVEABLE DISCARDABLE
+DATA PRELOAD MOVEABLE SINGLE
+HEAPSIZE 512
+
+EXPORTS
+ WEP @1 RESIDENTNAME ;Internal
+
+ WNETADDCONNECTION @17
+ WNETGETCONNECTION @12
+ WNETCANCELCONNECTION @18
+
+ NETWAREREQUEST @1000
+ PNETWAREREQUEST @1001
+
+
+SEGMENTS
+ _TEXT PRELOAD MOVEABLE DISCARDABLE
+
diff --git a/private/nw/nw16/drv/netware.h b/private/nw/nw16/drv/netware.h
new file mode 100644
index 000000000..c96086ab8
--- /dev/null
+++ b/private/nw/nw16/drv/netware.h
@@ -0,0 +1,61 @@
+/*****************************************************************/
+/** Microsoft Windows 4.0 **/
+/** Copyright (C) Microsoft Corp., 1991-1993 **/
+/*****************************************************************/
+
+
+/*
+ * History:
+ * 08/08/93 vlads Created
+ * 10/16/93 gregj Removed #pragma pack() because of #include nesting
+ *
+ */
+
+#ifndef _INC_NETWARE
+#define _INC_NETWARE
+
+#include <windows.h>
+
+// #include <npdefs.h>
+
+// #include <base.h>
+
+// #include <npassert.h>
+// #include <buffer.h>
+
+// #include <..\..\dev\ddk\inc16\error.h>
+// #include <bseerr.h>
+#include "nwerror.h"
+// #include "..\nwnp\nwsysdos.h"
+
+#ifdef __cplusplus
+extern "C" { /* Assume C declarations for C++ */
+#endif /* __cplusplus */
+
+// #include <netcons.h>
+// #include <netlib.h>
+
+WINAPI NETWAREREQUEST (LPVOID);
+WINAPI PNETWAREREQUEST(LPVOID);
+WINAPI DOSREQUESTER(LPVOID);
+
+//UINT WINAPI WNetAddConnection(LPSTR, LPSTR, LPSTR);
+//UINT WINAPI WNetGetConnection(LPSTR, LPSTR, UINT FAR*);
+//UINT WINAPI WNetCancelConnection(LPSTR, BOOL);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#ifdef DEBUG
+#define TRACE(s) OutputDebugString(s)
+#else
+#define TRACE(s)
+#endif
+
+extern HINSTANCE hInstance;
+
+#endif /* !_INC_NETWARE */
+
diff --git a/private/nw/nw16/drv/nwasmutl.asm b/private/nw/nw16/drv/nwasmutl.asm
new file mode 100644
index 000000000..aa03a37b5
--- /dev/null
+++ b/private/nw/nw16/drv/nwasmutl.asm
@@ -0,0 +1,70 @@
+PAGE,132
+;*****************************************************************;
+;** Microsoft Windows for Workgroups **;
+;** Copyright (C) Microsoft Corp., 1991-1993 **;
+;*****************************************************************;
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; ;;
+;; COMPONENT: Windows NetWare DLL. ;;
+;; ;;
+;; FILE: NWASMUTL.ASM ;;
+;; ;;
+;; PURPOSE: General routines used that cannot be done in C. ;;
+;; ;;
+;; REVISION HISTORY: ;;
+;; vlads 09/20/93 First cut ;;
+;; ;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+ INCLUDE CMACROS.INC
+
+?PLM = 1
+?WIN=0
+
+ifndef SEGNAME
+ SEGNAME equ <_TEXT> ; default seg name
+endif
+
+createSeg %SEGNAME, CodeSeg, word, public, CODE
+
+sBegin CodeSeg ; this defines what seg this goes in
+assumes cs,CodeSeg
+
+;;
+;; Swapping bytes in a word
+;;
+
+cProc WordSwap, <PUBLIC,FAR>
+ parmW inWord
+
+cBegin
+ mov ax, word ptr (inWord)
+ xchg al, ah
+cEnd
+
+
+;;
+;; Swapping words in a long word
+;;
+cProc LongSwap, <FAR,PUBLIC>, <dx>
+ parmD inLong
+
+cBegin
+ mov dx, word ptr (inLong + 2)
+ xchg dl, dh
+ mov ax, word ptr (inLong)
+ xchg al, ah
+cEnd
+
+;public NETWAREREQUEST
+;
+;NETWAREREQUEST proc far
+; int 21h
+; retf
+;NETWAREREQUEST endp
+
+sEnd _thisseg
+
+ END
+
diff --git a/private/nw/nw16/drv/nwerror.h b/private/nw/nw16/drv/nwerror.h
new file mode 100644
index 000000000..f369855ce
--- /dev/null
+++ b/private/nw/nw16/drv/nwerror.h
@@ -0,0 +1,46 @@
+/*****************************************************************/
+/** Microsoft Windows for Workgroups **/
+/** Copyright (C) Microsoft Corp., 1991-1992 **/
+/*****************************************************************/
+
+/* NWERROR.H -- Novell defined error return codes from Netware API
+ *
+ * History:
+ * 03/16/93 vlads Created
+ *
+ */
+
+#ifndef _nwerror_h_
+#define _nwerror_h_
+
+
+#define NWSC_SUCCESS 0x00
+#define NWSC_SERVEROUTOFMEMORY 0x96
+#define NWSC_NOSUCHVOLUME 0x98 // Volume does not exist
+#define NWSC_BADDIRECTORYHANDLE 0x9b
+#define NWSC_NOSUCHPATH 0x9c
+#define NWSC_NOJOBRIGHTS 0xd6
+#define NWSC_EXPIREDPASSWORD 0xdf
+#define NWSC_NOSUCHSEGMENT 0xec // Segment does not exist
+#define NWSC_INVALIDNAME 0xef
+#define NWSC_NOWILDCARD 0xf0 // Wildcard not allowed
+#define NWSC_NOPERMBIND 0xf1 // Invalid bindery security
+
+#define NWSC_ALREADYATTACHED 0xf8 // Already attached to file server
+#define NWSC_NOPERMREADPROP 0xf9 // No property read privelege
+#define NWSC_NOFREESLOTS 0xf9 // No free connection slots at server
+#define NWSC_NOMORESLOTS 0xfa // No more server slots
+#define NWSC_NOSUCHPROPERTY 0xfb // Property does not exist
+#define NWSC_UNKNOWN_REQUEST 0xfb // Invalid NCP number
+#define NWSC_NOSUCHOBJECT 0xfc // End of Scan Bindery Object service
+ // No such object
+#define NWSC_UNKNOWNSERVER 0xfc // Unknown file server
+#define NWSC_SERVERBINDERYLOCKED 0xfe // Server bindery locked
+#define NWSC_BINDERYFAILURE 0xff // Bindery failure
+#define NWSC_ILLEGALSERVERADDRESS 0xff // No response from server (illegal server address)
+#define NWSC_NOSUCHCONNECTION 0xff // Connection ID does not exist
+
+
+typedef WORD NW_STATUS;
+
+#endif
diff --git a/private/nw/nw16/drv/nwinit.c b/private/nw/nw16/drv/nwinit.c
new file mode 100644
index 000000000..c61a4fb40
--- /dev/null
+++ b/private/nw/nw16/drv/nwinit.c
@@ -0,0 +1,81 @@
+/*****************************************************************/
+/** Microsoft Windows 4.0 **/
+/** Copyright (C) Microsoft Corp., 1992-1993 **/
+/*****************************************************************/
+
+/* INIT.C -- General code for MS/Netware network driver emulator.
+ *
+ * History:
+ * 09/22/93 vlads Created
+ *
+ */
+
+#include "netware.h"
+
+#define Reference(x) ((void)(x))
+
+extern BOOL far pascal GetLowRedirInfo(void);
+
+int FAR PASCAL LibMain(
+ HANDLE hInst,
+ WORD wDataSeg,
+ WORD wcbHeapSize,
+ LPSTR lpstrCmdLine)
+{
+
+ //
+ // get shared data segment address. Fail initialization if an error is
+ // returned
+ //
+
+ if (!GetLowRedirInfo()) {
+ return 0;
+ }
+
+ //
+ // return success
+ //
+
+ return 1;
+}
+
+/* WEP
+ * Windows Exit Procedure
+ */
+
+int FAR PASCAL _loadds WEP(int nParameter)
+{
+ Reference(nParameter);
+ return 1;
+}
+
+
+WINAPI PNETWAREREQUEST(LPVOID x)
+{
+ return(1);
+}
+
+//
+// removed because nwcalls makes use of this function; removing it causes
+// NWCALLS to use real INT 21
+//
+
+//WINAPI DOSREQUESTER(LPVOID x)
+//{
+// return(1);
+//}
+
+UINT WINAPI WNetAddConnection(LPSTR p1, LPSTR p2, LPSTR p3)
+{
+ return(1);
+}
+
+UINT WINAPI WNetGetConnection(LPSTR p1, LPSTR p2, UINT FAR *p3)
+{
+ return(1);
+}
+
+UINT WINAPI WNetCancelConnection(LPSTR p1, BOOL p2)
+{
+ return(1);
+}
diff --git a/private/nw/nw16/inc/makefile b/private/nw/nw16/inc/makefile
new file mode 100644
index 000000000..ad117a137
--- /dev/null
+++ b/private/nw/nw16/inc/makefile
@@ -0,0 +1,34 @@
+!IF 0
+
+Copyright (c) 1991 & 1993 Microsoft Corporation
+
+Module Name:
+
+ makefile
+
+Abstract:
+
+ makefile for Vdm NetWare Redir program
+
+Author:
+
+ Richard L Firth (rfirth) 13-Sep-1991
+
+Revision History:
+
+ 13-Sep-1991 rfirth
+ Created
+
+!ENDIF
+
+.SUFFIXES:
+.SUFFIXES: .asm .h
+
+.h.inc:
+ h2inc $< -o $*.inc
+
+nwdos.inc: nwdos.h
+
+clean:
+ del *.inc
+ $(MAKE)
diff --git a/private/nw/nw16/inc/nwdos.h b/private/nw/nw16/inc/nwdos.h
new file mode 100644
index 000000000..c1a03891e
--- /dev/null
+++ b/private/nw/nw16/inc/nwdos.h
@@ -0,0 +1,220 @@
+/*--
+
+ Copyright (c) 1993 Microsoft Corporation
+
+ Module Name:
+
+ NWDOS.h
+
+ Abstract:
+
+ This is the include file that defines all constants and types for
+ 16 bit applications accessing the redirector.
+
+ Author:
+
+ Colin Watson (ColinW) 08-Jul-1993
+
+ Revision History:
+
+--*/
+
+#define NWDOS_INCLUDED
+
+#define MC 8 // maximum number of connections
+
+#define NC 8 // number of novell connections
+#define MP 3 // maximum number of printers
+#define MD 32 // maximum number of drives
+#define PZ 64 // print buffer size
+
+#define SERVERNAME_LENGTH 48
+#define USERNAME_LENGTH 16
+#define PASSWORD_LENGTH 16
+#define IPXADDRESS_LENGTH 12
+#define NODEADDRESS_LENGTH 6
+
+typedef UCHAR byte;
+typedef USHORT word;
+
+typedef byte CONN_INDEX; // index into ConnectionIdTable, range 0..MC-1
+typedef byte DRIVE; // index into DriveXxxTable, range 0..MD-1
+
+/* OpenFile() Flags */
+
+#define OF_READ_WRITE_MASK 0x0003
+/*
+#define OF_READ 0x0000
+#define OF_WRITE 0x0001
+#define OF_READWRITE 0x0002
+*/
+#define OF_SHARE_MASK 0x0070
+/*
+#define OF_SHARE_COMPAT 0x0000
+#define OF_SHARE_EXCLUSIVE 0x0010
+#define OF_SHARE_DENY_WRITE 0x0020
+#define OF_SHARE_DENY_READ 0x0030
+#define OF_SHARE_DENY_NONE 0x0040
+#define OF_PARSE 0x0100
+#define OF_DELETE 0x0200
+#define OF_VERIFY 0x0400 */ /* Used with OF_REOPEN */
+/*
+#define OF_SEARCH 0x0400 */ /* Used without OF_REOPEN */
+/*
+#define OF_CANCEL 0x0800
+#define OF_CREATE 0x1000
+#define OF_PROMPT 0x2000
+#define OF_EXIST 0x4000
+#define OF_REOPEN 0x8000
+*/
+
+//
+// Force misalignment of the following structures
+//
+
+/* XLATOFF */
+#include <packon.h>
+/* XLATON */
+
+typedef struct CID { /* */
+ byte ci_InUse;
+ byte ci_OrderNo;
+ byte ci_ServerAddress[IPXADDRESS_LENGTH];
+ word ci_TimeOut;
+ byte ci_LocalNode[NODEADDRESS_LENGTH];
+ byte ci_SequenceNo;
+ byte ci_ConnectionNo;
+ byte ci_ConnectionStatus;
+ word ci_MaxTimeOut;
+ byte ci_ConnectionLo;
+ byte ci_ConnectionHi;
+ byte ci_MajorVersion;
+ byte ci_1;
+ byte ci_MinorVersion;
+} CONNECTIONID;
+typedef CONNECTIONID UNALIGNED *PCONNECTIONID;
+
+#if 0 /* Already declared in nw\inc\ntddnwfs.h */
+typedef char SERVERNAME[SERVERNAME_LENGTH];
+#endif
+
+typedef char USERNAME[USERNAME_LENGTH];
+typedef char PASSWORD[PASSWORD_LENGTH];
+typedef char IPXADDRESS[IPXADDRESS_LENGTH];
+typedef char NODEADDRESS[NODEADDRESS_LENGTH];
+
+//
+// The following type collects all the structures used between the TSR
+// and the 32 bit dll into one packed structure.
+//
+// *** ANY CHANGES TO THIS STRUCTURE MUST ALSO BE MADE TO THE ASM NWDOSTABLE_ASM
+// *** STRUCTURE (below)
+//
+
+/* XLATOFF */
+typedef struct {
+ CONNECTIONID ConnectionIdTable[MC];
+ SERVERNAME ServerNameTable[MC];
+ CONN_INDEX DriveIdTable[MD]; // Corresponding ConnectionId
+ UCHAR DriveFlagTable[MD];
+ UCHAR DriveHandleTable[MD];
+ UCHAR PreferredServer;
+ UCHAR PrimaryServer;
+ UCHAR TaskModeByte;
+ UCHAR CurrentDrive;
+ USHORT SavedAx;
+ USHORT NtHandleHi;
+ USHORT NtHandleLow;
+ USHORT NtHandleSrcHi;
+ USHORT NtHandleSrcLow;
+ USHORT hVdd;
+ USHORT PmSelector;
+ UCHAR CreatedJob;
+ UCHAR JobHandle;
+ UCHAR DeNovellBuffer[256];
+ UCHAR DeNovellBuffer2[256];
+} NWDOSTABLE;
+typedef NWDOSTABLE *PNWDOSTABLE;
+/* XLATON */
+
+//
+// Turn structure packing back off
+//
+
+/* XLATOFF */
+#include <packoff.h>
+/* XLATON */
+
+//
+// CONNECTIONID Constants
+//
+
+#define FREE 0
+#define IN_USE 0xff
+
+//
+// Values for DriveFlags
+//
+
+#define NOT_MAPPED 0
+#define PERMANENT_NETWORK_DRIVE 1
+#define TEMPORARY_NETWORK_DRIVE 2
+#define LOCAL_DRIVE 0x80
+
+
+///// Client state tables:
+
+extern CONNECTIONID* ConnectionIdTable; // MC entries
+extern SERVERNAME* ServerNameTable; // MC entries
+
+extern byte* DriveFlagTable; // MD entries
+extern byte* DriveIdTable; // MD entries
+
+//
+// this next egregious grossness is extant because MASM cannot handle anything
+// other than a basic type inside a structure declaration
+//
+// *** ANY CHANGES TO THIS STRUCTURE MUST ALSO BE MADE TO THE C NWDOSTABLE
+// *** STRUCTURE (above)
+//
+// NB. The leading underscores are there because we already have globals with
+// the same names
+//
+
+/* ASM
+
+NWDOSTABLE_ASM struc
+
+_ConnectionIdTable db ((size CID) * MC) dup (?)
+_ServerNameTable db (MC * SERVERNAME_LENGTH) dup (?)
+_DriveIdTable db MD dup (?)
+_DriveFlagTable db MD dup (?)
+_DriveHandleTable db MD dup (?)
+_PreferredServer db ?
+_PrimaryServer db ?
+_TaskModeByte db ?
+_CurrentDrive db ?
+_SavedAx dw ?
+_NtHandleHi dw ?
+_NtHandleLow dw ?
+_NtHandleSrcHi dw ?
+_NtHandleSrcLow dw ?
+_hVdd dw ?
+_PmSelector dw ?
+_CreatedJob db ?
+_JobHandle db ?
+_DeNovellBuffer db 256 dup (?)
+_DeNovellBuffer2 db 256 dup (?)
+
+NWDOSTABLE_ASM ends
+
+*/
+
+/* XLATOFF */
+//
+// IS_ASCII_PATH_SEPARATOR - returns TRUE if ch is / or \. ch is a single
+// byte (ASCII) character
+//
+#define IS_ASCII_PATH_SEPARATOR(ch) (((ch) == '/') || ((ch) == '\\'))
+/* XLATON */
+
diff --git a/private/nw/nw16/tsr/asmmacro.inc b/private/nw/nw16/tsr/asmmacro.inc
new file mode 100644
index 000000000..0b7b2db16
--- /dev/null
+++ b/private/nw/nw16/tsr/asmmacro.inc
@@ -0,0 +1,343 @@
+;++
+;
+;Copyright (c) 1991 Microsoft Corporation
+;
+;Module Name:
+;
+; asmmacro.inc
+;
+;Abstract:
+;
+; Contains macros to extend masm functionality:
+;
+; jmpc
+; jmpnc
+; jmpne
+; jmps
+; _mkjmp
+;
+;
+;Author:
+;
+; Richard L Firth (rfirth) 24-Sep-1991
+;
+;Environment:
+;
+; DOS application mode only
+;
+;Revision History:
+;
+; 24-Sep-1991 rfirth
+; Created
+;
+;--
+
+
+
+DEFINED_BIT=020h
+;ISDEFINED equ %(.type <thing> and DEFINED_BIT)
+LABEL_DEFINED equ <(.type &label and DEFINED_BIT)>
+
+DEBUG_MACROS = 0
+;DEBUG_MACROS = 1
+
+
+;*** jmpa
+;*
+;* jump to label if above. Label can be short (+129, -126 from
+;* the first byte of the current jump instruction, if it is a short - ie
+;* byte - jump) or near
+;*
+;* ENTRY label - to jump to
+;*
+;* EXIT nothing
+;*
+;* USES nothing
+;*
+;* ASSUMES 286+
+;*
+;***
+
+jmpa macro label
+ _mkjmp ja,jna,&label
+endm
+
+;*** jmpc
+;*
+;* jump to label if below. Label can be short (+129, -126 from
+;* the first byte of the current jump instruction, if it is a short - ie
+;* byte - jump) or near
+;*
+;* ENTRY label - to jump to
+;*
+;* EXIT nothing
+;*
+;* USES nothing
+;*
+;* ASSUMES 286+
+;*
+;***
+
+jmpb macro label
+ _mkjmp jb,jnb,&label
+endm
+
+;*** jmpc
+;*
+;* jump to label if carry flag set. Label can be short (+129, -126 from
+;* the first byte of the current jump instruction, if it is a short - ie
+;* byte - jump) or near
+;*
+;* ENTRY label - to jump to
+;*
+;* EXIT nothing
+;*
+;* USES nothing
+;*
+;* ASSUMES 286+
+;*
+;***
+
+jmpc macro label
+ _mkjmp jc,jnc,&label
+endm
+
+
+
+;*** jmpnc
+;*
+;* jump to label if carry flag NOT set. Label can be short (+129, -126 from
+;* the first byte of the current jump instruction, if it is a short - ie
+;* byte - jump) or near
+;*
+;* ENTRY label - to jump to
+;*
+;* EXIT nothing
+;*
+;* USES nothing
+;*
+;* ASSUMES 286+
+;*
+;***
+
+jmpnc macro label
+ _mkjmp jnc,jc,&label
+endm
+
+
+
+;*** jmpne
+;*
+;* jump to label if zero flag NOT set. Label can be short (+129, -126 from
+;* the first byte of the current jump instruction, if it is a short - ie
+;* byte - jump) or near
+;*
+;* ENTRY label - to jump to
+;*
+;* EXIT nothing
+;*
+;* USES nothing
+;*
+;* ASSUMES 286+
+;*
+;***
+
+jmpne macro label
+ _mkjmp jne,je,&label
+endm
+
+
+
+;*** jmpe
+;*
+;* jump to label if zero flag set. Label can be short (+129, -126 from
+;* the first byte of the current jump instruction, if it is a short - ie
+;* byte - jump) or near
+;*
+;* ENTRY label - to jump to
+;*
+;* EXIT nothing
+;*
+;* USES nothing
+;*
+;* ASSUMES 286+
+;*
+;***
+
+jmpe macro label
+ _mkjmp je,jne,&label
+endm
+
+
+
+;*** jmps
+;*
+;* jump to label. Label can be short (+129, -126 from
+;* the first byte of the current jump instruction, if it is a short - ie
+;* byte - jump) or near
+;*
+;* ENTRY label - to jump to
+;*
+;* EXIT nothing
+;*
+;* USES nothing
+;*
+;* ASSUMES 286+
+;*
+;***
+
+jmps macro label
+ local l,dist
+dist=&label-$
+if1
+if (.type label and DEFINED_BIT)
+if ((dist gt 129) or (dist lt -126))
+if DEBUG_MACROS
+ %out pass1: &label defined and near
+endif
+ jmp &label
+else
+if DEBUG_MACROS
+ %out pass1: &label defined and short
+endif
+ jmp short &label
+endif
+else
+if DEBUG_MACROS
+ %out pass1: &label not defined
+endif
+ org $+3
+endif
+else
+if ((dist gt 129) or (dist lt -126))
+if DEBUG_MACROS
+ %out pass2: &label defined and near
+endif
+ jmp &label
+else
+if DEBUG_MACROS
+ %out pass2: &label defined and short
+endif
+ jmp short &label
+ org $+1
+endif
+endif
+l:
+endm
+
+
+
+;*** _mkjmp
+;*
+;* Make a jmp<?> macro. Generate instruction sequence for jump with or
+;* without conditional test. Jump may be short (+127/-128 bytes) or near
+;* (+32767/-32768 bytes)
+;*
+;* ENTRY is - short jump instruction
+;* in - near jump instruction
+;* label - to jump to
+;*
+;* EXIT nothing
+;*
+;* USES nothing
+;*
+;* ASSUMES 286+
+;*
+;***
+
+_put macro s,v
+if2
+if DEBUG_MACROS
+%out s = v
+endif
+endif
+endm
+
+_mkjmp macro is, in, label
+ local l
+
+;;
+;; if pass 1 and label is already known, generate correct instruction
+;;
+
+if1
+if (.type &label and DEFINED_BIT)
+
+;;
+;; if label is too far away for short jump instruction, make jump <condition>
+;; into jump <NOT condition> round jump to label followed by a near jump to
+;; label
+;;
+
+if (((&label - $) gt 129) or ((&label - $) lt -126))
+ &in l ;; short jump, NOT condition
+ jmp &label ;; jump to where we want to go
+else
+ &is &label ;; short jump
+endif
+
+;;
+;; if pass 1 and we don't know about the label yet, adjust the program
+;; counter by the max. number of bytes taken up by this macro (5 - 2 for
+;; short jump, 3 for near jump)
+;;
+
+else
+ nop
+ nop
+ nop
+ nop
+ nop
+endif
+
+;;
+;; pass 2 - do same stuff as for pass 1
+;;
+
+else
+if (((&label - $) gt 129) or ((&label - $) lt -126))
+ if ((&label-$) gt 129)
+ _put <label distance>, %(&label-$)
+ else
+ _put <label distance>, %($-&label)
+ endif
+ &in l
+ jmp &label
+else
+
+;;
+;; label is within +127/-128 bytes of current instruction - generate short
+;; jump instruction and put the program counter forward past the space
+;; reserved during pass 1
+;;
+
+ _put <label distance>, %(&label-$)
+ &is &label
+ nop
+ nop
+ nop
+endif
+endif
+l:
+endm
+
+
+
+oldjmps macro label
+if2
+if (((&label - $) gt 127) or (($ - &label) lt -128))
+ jmp short l
+ jmp &label
+else
+ jmp short &label
+ org $+3
+endif
+else
+;;
+;; if this is pass 1 just take up max amount of space so phases don't get
+;; screwed
+;;
+ org $+5
+endif
+l:
+endm
diff --git a/private/nw/nw16/tsr/debugmac.inc b/private/nw/nw16/tsr/debugmac.inc
new file mode 100644
index 000000000..45fe236e5
--- /dev/null
+++ b/private/nw/nw16/tsr/debugmac.inc
@@ -0,0 +1,356 @@
+;++
+;
+;Copyright (c) 1991 Microsoft Corporation
+;
+;Module Name:
+;
+; debugmac.inc
+;
+;Abstract:
+;
+; Contains debugging macros:
+;
+; DbgBreakPoint
+; DbgUnsupported
+; DbgDEBUG
+; DbgPrint
+; DbgPrintTty
+; DbgPrintString
+; DbgPrintHexDword
+; DbgPrintHexWord
+; DbgPrintHexByte
+; DbgPrintNearPointer
+; DbgPrintFarPointer
+;
+;Author:
+;
+; Richard L Firth (rfirth) 13-Sep-1991
+;
+;Environment:
+;
+; DOS application mode only
+;
+;[Notes:]
+;
+; optional-notes
+;
+;Revision History:
+;
+; 13-Sep-1991 rfirth
+; Created
+;
+;--
+
+
+;*** DbgBreakPoint
+;*
+;* Same as NT routine of same name. No-op in non-DEBUG version
+;*
+;* ENTRY
+;*
+;* EXIT
+;*
+;* RETURNS
+;*
+;* ASSUMES
+;*
+;***
+
+DbgBreakPoint macro
+if DEBUG
+ int 3
+endif
+endm
+
+;*** DbgUnsupported
+;*
+;* Causes the 32-bit support code to display a message about an unsupported
+;* service code, and dumps the 16-bit registers. Used to discover when an
+;* unsupported int 2f/11 call or int 21/5f call is being made
+;*
+;* ENTRY
+;*
+;* EXIT
+;*
+;* RETURNS
+;*
+;* ASSUMES
+;*
+;***
+
+DbgUnsupported macro
+if DEBUG
+ SVC -1
+endif
+endm
+
+;*** DbgDEBUG
+;*
+;* Prints the string "DEBUG: " to console using Bios Int 10h/ah=0eh
+;*
+;* ENTRY nothing
+;*
+;* EXIT nothing
+;*
+;* USES ax
+;*
+;* ASSUMES 286+
+;*
+;***
+
+DbgDEBUG macro
+ mov ax,(14 shl 8) + 'D'
+ int 10h
+ mov al,'E'
+ int 10h
+ mov al,'B'
+ int 10h
+ mov al,'U'
+ int 10h
+ mov al,'G'
+ int 10h
+ mov al,':'
+ int 10h
+ mov al,' '
+ int 10h
+endm
+
+
+
+;*** DbgCrLf
+;*
+;* Prints CR,LF to console using Bios Int 10h/ah=0eh
+;*
+;* ENTRY nothing
+;*
+;* EXIT nothing
+;*
+;* USES nothing
+;*
+;* ASSUMES 286+
+;*
+;***
+
+DbgCrLf macro
+ push ax
+ mov ax,(14 shl 8) + 13
+ int 10h
+ mov al,10
+ int 10h
+ pop ax
+endm
+
+
+
+;*** DbgPrint
+;*
+;* Prints an ASCIZ string to console using Bios Int 10h
+;*
+;* ENTRY string - address of ASCIZ string to print
+;*
+;* EXIT nothing
+;*
+;* USES nothing
+;*
+;* ASSUMES 286+
+;*
+;***
+
+DbgPrint macro string
+if DEBUG ;; no macro if not debug version
+ pushf ;; save regs used by DbgPrintTty
+ push ax
+ push bx
+ push si
+ push ds
+ mov ax,seg string
+ mov ds,ax
+ mov si,offset string;; ds:si = address of string
+ DbgPrintTty ;; display it on console
+ pop ds
+ pop si
+ pop bx
+ pop ax
+ popf
+endif
+endm
+
+
+
+;*** DbgPrintTty
+;*
+;* Prints an ASCIZ string in ds:si to console using Bios Int 10h
+;*
+;* ENTRY page - if present defines which Bios video page to use
+;* Defaults to 0
+;* ds:si - address of ASCIZ string to print
+;*
+;* EXIT nothing
+;*
+;* USES al, bh, si, flags
+;*
+;* ASSUMES 286+
+;*
+;***
+
+DbgPrintTty macro page
+ local l1,l2
+
+if DEBUG ;; no macro if not debug version
+ mov ah,14 ;; Bios Int write character as TTY function
+ifb <page>
+ sub bh,bh
+else
+ mov bh,page
+endif
+ cld ;; autoincrement lodsb
+l1: lodsb ;; al := next character; si := next character addr
+ or al,al ;; eof string?
+ jz l2 ;; yes
+ int 10h ;; display it to console
+ jmp short l1 ;; go round again
+l2:
+endif
+endm
+
+
+
+;*** DbgPrintString
+;*
+;* Prints a string to console using Bios Int 10h. Note that this macro
+;* does not do printf style substitutions. The string "DEBUG: " will be
+;* displayed if the banner parm is not blank
+;*
+;* ENTRY string - character string. Needn't be zero-terminated
+;* banner - the "DEBUG: " banner will be printed if not blank
+;*
+;* EXIT nothing
+;*
+;* USES nothing
+;*
+;* ASSUMES 286+
+;*
+;***
+
+DbgPrintString macro string, banner
+ local s1
+ local l1
+
+if DEBUG ;; no macro if not debug version
+ jmp short l1
+s1 db &string,0
+l1: pushf ;; don't destroy direction flag
+ pusha ;; save gp regs
+ifb <banner>
+ DbgDEBUG ;; Display "DEBUG: "
+endif
+ push ds ;; save user's data seg
+ push cs
+ pop ds ;; ds == cs
+ mov si,offset cs:s1 ;; si := string offset
+ DbgPrintTty ;; display ds:si to console
+ pop ds ;; restore user's data seg
+ popa ;; restore gp regs
+ popf ;; restore direction flag+
+endif
+endm
+
+
+
+;*** DbgPrintHexDword
+;*
+;* Prints a dword to console in hex notation using Bios Int 10h
+;*
+;* ENTRY dword - dword to print
+;*
+;* EXIT nothing
+;*
+;* USES nothing
+;*
+;* ASSUMES 286+
+;*
+;***
+
+DbgPrintHexDword macro dword
+if DEBUG ;; no macro if not debug version
+ DbgPrint <"DbgPrintHexDword not implemented yet",13,10>
+endif
+endm
+
+
+
+;*** DbgPrintHexWord
+;*
+;* Prints a word to console in hex notation using Bios Int 10h
+;*
+;* ENTRY word - to print. Can be memory or register
+;*
+;* EXIT nothing
+;*
+;* USES nothing
+;*
+;* ASSUMES 286+
+;*
+;***
+
+DbgPrintHexWord macro word
+ local l1, l2
+if DEBUG ;; no macro if not debug version
+ pushf ;; don't use any registers
+ push ax
+ push cx
+ push dx
+ifdifi <word>,<ax>
+ mov ax,word
+endif
+ mov cx,4
+l1: rol ax,4
+ mov dx,ax
+ and al,0fh
+ cmp al,9
+ jle l2
+ add al,'a'-('9'+1)
+l2: add al,'0'
+ mov ah,14
+ int 10h
+ mov ax,dx
+ loop l1
+ pop dx
+ pop cx
+ pop ax
+ popf
+endif
+endm
+
+
+
+;*** DbgPrintHexByte
+;*
+;* Prints a string to console using Bios Int 10h. Note that this macro
+;* does not do printf style substitutions
+;*
+;* ENTRY string - character string. Needn't be zero-terminated
+;*
+;* EXIT
+;*
+;* USES nothing
+;*
+;* ASSUMES 286+
+;*
+;***
+
+DbgPrintHexByte macro byte
+if DEBUG ;; no macro if not debug version
+ DbgPrint <"DbgPrintHexByte not implemented yet",13,10>
+endif
+endm
+
+
+
+DbgPrintNearPointer macro nearptr
+endm
+
+
+
+DbgPrintFarPointer macro farptr
+endm
diff --git a/private/nw/nw16/tsr/makefile b/private/nw/nw16/tsr/makefile
new file mode 100644
index 000000000..61ae19b12
--- /dev/null
+++ b/private/nw/nw16/tsr/makefile
@@ -0,0 +1,192 @@
+!IF 0
+
+Copyright (c) 1991 & 1993 Microsoft Corporation
+
+Module Name:
+
+ makefile
+
+Abstract:
+
+ makefile for Vdm NetWare Redir program
+
+Author:
+
+ Richard L Firth (rfirth) 13-Sep-1991
+
+Revision History:
+
+ 13-Sep-1991 rfirth
+ Created
+
+!ENDIF
+
+
+
+.SUFFIXES:
+.SUFFIXES: .asm .h
+
+#
+# nmake doesn't work properly if we try to stick the objects in obj, so put
+# them in current dir for now. WHEN CHANGE OBJPATH TO BE OBJ, CHANGE clean TOO
+#
+
+#OBJPATH = .
+OBJPATH = obj
+
+ASM = masm
+!IFDEF NTVDM_BASED_BUILD
+LINK = link16
+!ELSE
+LINK = link
+!ENDIF
+
+#
+# set the country info
+#
+
+!if "$(LANGUAGE)" == "JPN"
+COUNTRY=jpn
+!elseif "$(LANGUAGE)" == "CHT"
+COUNTRY=cht
+!elseif "$(LANGUAGE)" == "CHS"
+COUNTRY=chs
+!elseif "$(LANGUAGE)" == "KOR"
+COUNTRY=kor
+!ENDIF
+
+!IFNDEF COUNTRY
+COUNTRY=usa
+!ENDIF
+
+#
+# convert NTDEBUG into DEBUG flag. NTDEBUG can be not present or retail, either
+# of which mean no debugging; or ntsd, cvp or sym, which means debugging support
+# required
+#
+
+!IFDEF NTDEBUG
+!IF "$(NTDEBUG)" == "retail"
+DEBUGGING=0
+!ELSE
+DEBUGGING=1
+!ENDIF
+!ELSE
+DEBUGGING=0
+!ENDIF
+
+#
+# assembler and linker debugging options
+#
+
+!IF $(DEBUGGING)
+ASMDEBUG = /DDEBUG=1 /Zi
+LINKDEBUG = /CO
+!ELSE
+ASMDEBUG = /DDEBUG=0
+LINKDEBUG =
+!ENDIF
+ASMINC = /I$(_NTROOT)\public\sdk\inc /I$(_NTROOT)\private\mvdm\dos\v86\inc /I..\..\inc /I..\inc
+ASMFLAGS = /Mx
+LINKFLAGS = /MAP /CP:1
+
+#
+# any other non-debug related options (for assembler) go in USERDEFS
+#
+
+#USERDEFS = /DCALL_DOS /DVERBOSE
+#USERDEFS = /DCALL_DOS
+
+
+
+#
+# Inference rules - asm to obj, h to inc
+#
+
+.asm{$(OBJPATH)\}.obj:
+ $(ASM) $(ASMINC) $(ASMDEBUG) $(USERDEFS) $<,$@;
+
+.asm.lst:
+ $(ASM) $(ASMINC) $(ASMDEBUG) $(USERDEFS) /L $<;
+
+
+
+#
+# what it is we're building
+#
+
+TARGET = $(OBJPATH)\nw16.exe
+MAPFILE = $(TARGET:.exe=.map)
+DEFFILE = ;
+
+OBJS = $(OBJPATH)\nw16.obj \
+ $(OBJPATH)\resident.obj
+
+LIBS =
+
+
+
+#
+# how to build it
+#
+
+all: makedir $(TARGET)
+
+$(TARGET): $(OBJS)
+ $(LINK) @<<
+$(OBJS)
+$(TARGET) $(LINKFLAGS) $(LINKDEBUG)
+$(MAPFILE)
+$(LIBS)
+$(DEFFILE)
+<<
+
+
+#
+# where to put it
+#
+
+ binplace $(TARGET)
+
+
+#
+# clean build - delete all objs
+#
+
+#clean: makedir clean2
+clean: clean2
+
+clean2:
+ if exist $(OBJPATH)\*.obj del $(OBJPATH)\*.obj
+ $(MAKE)
+
+#
+# makedir - ensure the subdirectory for the object files exists
+#
+
+makedir:
+ @-if not exist $(OBJPATH) md $(OBJPATH)
+
+
+
+#
+# file dependencies
+#
+
+$(OBJPATH)\nw16.obj: \
+ nw16.asm \
+ debugmac.inc \
+ asmmacro.inc \
+ segorder.inc \
+ messages.inc \
+ ..\inc\nwdos.inc
+
+$(OBJPATH)\resident.obj: \
+ resident.asm \
+ segorder.inc \
+ debugmac.inc \
+ asmmacro.inc \
+ ..\inc\nwdos.inc
+
+messages.inc: ..\..\inc\$(COUNTRY)\messages.inc
+ copy ..\..\inc\$(COUNTRY)\messages.inc .
diff --git a/private/nw/nw16/tsr/nw16.asm b/private/nw/nw16/tsr/nw16.asm
new file mode 100644
index 000000000..ac19beed8
--- /dev/null
+++ b/private/nw/nw16/tsr/nw16.asm
@@ -0,0 +1,517 @@
+page ,132
+
+if 0
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ nw16.asm
+
+Abstract:
+
+ This module contains the stub redir TSR code for NT VDM net support
+
+Author:
+
+ Richard L Firth (rfirth) 05-Sep-1991
+ Colin Watson (colinw) 30-Jun-1993
+
+Environment:
+
+ Dos mode only
+
+Revision History:
+
+ 05-Sep-1991 rfirth
+ Created
+
+ 30-Jun-1993 colinw
+ ported to NetWare
+
+--*/
+endif
+
+
+
+;
+; DOS include files
+;
+
+.xlist
+.xcref
+include isvbop.inc ; NTVDM BOP mechanism
+include dossym.inc ; includes MS-DOS version etc
+include pdb.inc ; PSP defines
+include syscall.inc ; AssignOper
+include segorder.inc ; load order of 'redir' segments
+include debugmac.inc ; debug display macros
+include asmmacro.inc ; jumps which may be short or near
+include messages.inc
+include nwdos.inc ; NetWare structures and nwapi32 interface
+.cref
+.list
+
+;
+; Define externals in resident code and data
+;
+
+ResidentCodeStart
+
+ extrn Old21Handler:dword
+ extrn NwInt21:near
+ extrn hVDD:dword
+ extrn quick_jump_to_dos:byte
+ extrn for_dos_proper:byte
+ extrn chain_previous_int21:byte
+ extrn ConnectionIdTable:byte
+ extrn not_exclusive:byte
+
+ResidentCodeEnd
+
+
+InitStack segment stack para 'stack'
+
+ dw 256 dup (?)
+
+InitStack ends
+
+InitDataStart
+
+
+bad_ver_msg db NLS_MSG_001,c_CR,c_LF
+BAD_VER_MSG_LEN equ $-bad_ver_msg
+ db '$' ; for INT 21/09 display string
+
+already_loaded_msg db NLS_MSG_004,c_CR,c_LF
+ALREADY_LOADED_MSG_LEN equ $-already_loaded_msg
+
+cannot_load_msg db NLS_MSG_005,c_CR, c_LF
+CANNOT_LOAD_MSG_LEN equ $-cannot_load_msg
+
+InitDataEnd
+
+
+InitCodeStart
+
+ assume cs:InitCode
+ assume ds:nothing
+ assume es:nothing
+ assume ss:nothing
+
+ public DllName
+DllName db "NWAPI16.DLL",0
+
+ public InitFunc
+InitFunc db "Nw16Register",0
+
+ public DispFunc
+DispFunc db "Nw16Handler",0
+
+ public start
+start proc near
+
+;
+; when we start up we could be on any old PC - even an original, so don't
+; assume anything other than a model-T processor
+;
+
+ .8086
+
+;
+; Set the data segment while we're at it - all paths set it sooner
+; or later. NOTE: es will point to the PSP until we change it!
+;
+
+ mov dx,InitData
+ mov ds,dx
+ assume ds:InitData
+
+;
+; first off, get the DOS version. If we're not running on NT (VDM) then this
+; TSR's not going to do much, so exit. Exit using various methods, depending
+; on the DOS version (don't you hate compatibility?)
+;
+
+ mov ah,30h
+ int 21h
+ jc ancient_version ; version not even supported
+
+;
+; version is 2.0 or higher. Check it out. al = major#, ah = minor#
+;
+
+ cmp al,major_version
+ jne invalid_version
+
+;
+; what do you know? We're actually running on NT (unless some evil programmer
+; has pinched int 21h/30h and broken it!). Enable minimum instruction set
+; for NTVDM (286 on RISC).
+;
+
+ .286c
+
+;
+; perform an installation check by calling one of our entry points
+; (GetFileServerNameTable). If this returns a table pointer in ES:DI then we
+; know this TSR is already active, in which case we bail out now
+;
+
+ push es
+ push di
+ xor di,di
+ mov es,di
+ mov ax,0ef03h
+ int 21h
+ mov ax,es
+ or ax,di
+ pop di
+ pop es
+ jnz already_here
+
+;
+; OK, the NetWare redir is not already loaded - we're in business.
+; Find entrypoints to nwapi16.dll Get and set the various interrupt
+; vectors, Calculate the amount of space we want to keep,
+; free up any unused space (like the environment segment), display a message
+; in the DEBUG version, then terminate and stay resident. Remember: at this
+; point we expect ES to point at the PSP
+;
+
+ call PullInDll
+ jc already_here ; failed to load
+
+ call InstallInterruptHandlers
+
+ assume es:nothing
+
+ push es
+ pop ds
+ call is_c_on_command_line
+ jz @f
+
+ mov dx,ResidentCode
+ mov ds,dx
+
+ assume ds:ResidentCode
+ mov not_exclusive, 1
+
+ assume ds:nothing
+@@:
+
+;
+; free the environment segment
+;
+
+ mov es,es:[PDB_environ]
+ mov ah,49h
+ int 21h ; free environment segment
+
+;if DEBUG
+;ifdef VERBOSE
+; DbgPrintString <"NetWare Redir successfully loaded",13,10>
+;endif
+;endif
+
+;
+; finally terminate and stay resident
+;
+
+ mov dx,ResidentEnd
+ sub dx,ResidentStart ; number of paragraphs in resident code
+ add dx,10h ; additional for PSP (PDB)
+
+
+;if DEBUG
+;ifdef VERBOSE
+; DbgPrintString "Staying resident with "
+; DbgPrintHexWord dx
+; DbgPrintString " paragraphs. Load seg is ",NOBANNER
+; mov ah,62h
+; int 21h
+; DbgPrintHexWord bx
+; DbgPrintString " current seg is ",NOBANNER
+; DbgPrintHexWord cs
+; DbgCrLf
+;endif
+;endif
+
+ mov ax,3100h
+ int 21h ; terminate and stay resident
+
+;
+; here if the MS-DOS version check (Ah=30h) call is not supported
+;
+
+ancient_version:
+ mov dx,InitData
+ mov ds,dx
+
+ assume ds:InitData
+
+ mov dx,offset bad_ver_msg
+ mov ah,9 ; cp/m-style write to output
+ int 21h
+
+;
+; safe exit: what we really want to do here is INT 20H, but when you do this,
+; CS must be the segment of the PSP of this program. Knowing that CD 20 is
+; embedded at the start of the PSP, the most foolproof way of doing this is
+; to jump (using far return) to the start of the PSP
+;
+
+ push es
+ xor ax,ax
+ push ax
+ retf ; terminate
+
+;
+; we are running on a version of DOS >= 2.00, but its not NT, so we still can't
+; help. Display the familiar message and exit, but using a less programmer-
+; hostile mechanism
+;
+
+invalid_version:
+ mov dx,offset bad_ver_msg
+ mov cx,BAD_VER_MSG_LEN
+ jmps print_error_message_and_exit
+
+;
+; if we cannot initialize 32-bit support (because we can't find/load the DLL)
+; then put back the hooked interrupt vectors as they were when this TSR started,
+; display a message and fail to load the redir TSR
+;
+
+initialization_error:
+ call RestoreInterruptHandlers
+ mov dx,offset cannot_load_msg
+ mov cx,CANNOT_LOAD_MSG_LEN
+ jmps print_error_message_and_exit
+
+;
+; The DOS version's OK, but this TSR is already loaded
+;
+
+already_here:
+ mov dx,offset already_loaded_msg
+ mov cx,ALREADY_LOADED_MSG_LEN
+
+print_error_message_and_exit:
+ mov bx,1 ; bx = stdout handle
+ mov ah,40h ; write to handle
+ int 21h ; write (cx) bytes @ (ds:dx) to stdout
+ mov ax,4c01h ; terminate program
+ int 21h ; au revoir, cruel environment
+
+start endp
+
+;*******************************************************************************
+;*
+;* InstallInterruptHandlers
+;*
+;* Sets the interrupt handlers for all the ints we use - 21
+;*
+;* ENTRY es = PSP segment
+;* ds =
+;*
+;* EXIT Old21Handler contains the original interrupt 21 vector
+;*
+;* RETURNS nothing
+;*
+;* ASSUMES
+;*
+;*******************************************************************************
+
+InstallInterruptHandlers proc
+ push es ; PSP segment - destroyed by INT 21/35h
+ push ds
+
+;
+; note: if we use ResidentCode here, explicitly, instead of seg OldMultHandler,
+; then we can leave out an extraneous load of ds for the ISR address
+;
+
+ mov dx,ResidentCode
+ mov ds,dx
+
+ assume ds:ResidentCode
+
+;
+; Add ourselves to the int 21 chain
+;
+
+ mov ax,3521h
+ int 21h
+ mov word ptr Old21Handler,bx
+ mov word ptr Old21Handler+2,es
+ mov word ptr quick_jump_to_dos+1,bx
+ mov word ptr quick_jump_to_dos+3,es
+ mov word ptr for_dos_proper+1,bx
+ mov word ptr for_dos_proper+3,es
+ mov word ptr chain_previous_int21+1,bx
+ mov word ptr chain_previous_int21+3,es
+ mov dx,offset ResidentCode:NwInt21
+ mov ax,2521h
+ int 21h
+
+ pop ds ; restore segment registers
+ pop es
+ ret
+InstallInterruptHandlers endp
+
+;*******************************************************************************
+;*
+;* RestoreInterruptHandlers
+;*
+;* Resets the interrupt handlers for all the ints we use - 21
+;*
+;* ENTRY Old21Handler
+;* contain the interrupt vectors from before nw16.sys was loaded
+;*
+;* EXIT Original interrupt vectors are restored
+;*
+;* RETURNS nothing
+;*
+;* ASSUMES
+;*
+;*******************************************************************************
+
+RestoreInterruptHandlers proc
+ push ds
+
+ assume ds:nothing
+
+ push es
+ mov dx,ResidentCode
+ mov es,dx
+
+ assume es:ResidentCode
+
+ lds dx,Old21Handler
+ mov ax,2521h
+ int 21h
+
+ pop es
+ pop ds
+ ret
+RestoreInterruptHandlers endp
+
+;*******************************************************************************
+;*
+;* PullInDll
+;*
+;* Does a RegisterModule to load NWAPI32.DLL into our NTVDM.EXE
+;*
+;* ENTRY nothing
+;*
+;* EXIT nothing
+;*
+;* RETURNS cf if fails.
+;*
+;* ASSUMES Earth moves round Sun
+;*
+;******************************************************************************/
+
+PullInDll proc near
+
+ pusha ; dispatch code
+ push dx ; save callers dx,ds,es,ax
+ push ds
+ push es
+ push ax
+
+ mov dx,InitCode
+ mov ds,dx
+
+ assume ds:InitCode
+
+ push ds
+ pop es
+
+ assume es:InitCode
+
+ mov si,offset DllName ; ds:si = nwapi32.dll
+ mov di,offset InitFunc ; es:di = init routine
+ mov bx,offset DispFunc ; ds:bx = dispatch routine
+ mov ax,ResidentCode
+ mov dx,offset ConnectionIdTable
+ ; ax:dx = shared datastructure
+
+ RegisterModule
+
+ jc @f
+
+ mov dx,ResidentCode
+ mov ds,dx
+ assume ds:ResidentCode
+ mov word ptr hVDD,ax
+
+@@: pop ax ; callers ax
+ pop es ; callers es
+ pop ds ; callers ds
+ pop dx ; callers dx
+
+ assume ds:nothing
+ assume es:nothing
+
+ popa ; dispatch code
+ ret
+PullInDll endp
+
+;*******************************************************************************
+;*
+;* is_c_on_command_line
+;*
+;* -C or /C means we should open compatiblity mode createfiles as shared
+;* instead of exclusive
+;*
+;* ENTRY ds points to PDB
+;*
+;* EXIT nothing
+;*
+;* RETURNS zero if not found.
+;*
+;* ASSUMES ds points at PSP
+;*
+;******************************************************************************/
+
+is_c_on_command_line proc near
+ mov si,80h
+ lodsb
+ cbw
+ mov cx,ax
+next: jcxz quit
+ dec cx
+ lodsb
+check_next:
+ cmp al,'-'
+ je check_c
+ cmp al,'/'
+ je check_c
+ cmp al,' '
+ je next
+ cmp al,9
+ je next
+find_ws:jcxz quit
+ dec cx
+ lodsb
+ cmp al,' '
+ je next
+ cmp al,9
+ je next
+ jmp short find_ws
+check_c:jcxz quit
+ dec cx
+ lodsb
+ or al,20h
+ cmp al,'c'
+ jne find_ws
+ or cx,ax
+quit: or cx,cx
+ ret
+is_c_on_command_line endp
+
+InitCodeEnd
+end start
diff --git a/private/nw/nw16/tsr/resident.asm b/private/nw/nw16/tsr/resident.asm
new file mode 100644
index 000000000..5c95168da
--- /dev/null
+++ b/private/nw/nw16/tsr/resident.asm
@@ -0,0 +1,1039 @@
+page ,132
+if 0
+
+/*++
+
+Copyright (c) 1993-4 Microsoft Corporation
+
+Module Name:
+
+ resident.asm
+
+Abstract:
+
+ This module contains the resident code part of the stub redir TSR for NT
+ VDM NetWare support.
+
+Author:
+
+ Colin Watson (colinw) 08-Jul-1993
+
+Environment:
+
+ Dos mode only
+
+Revision History:
+
+ 08-Jul-1993 colinw
+ Created
+
+--*/
+
+endif
+
+
+
+.xlist ; don't list these include files
+.xcref ; turn off cross-reference listing
+include isvbop.inc ; NTVDM BOP mechanism
+include dosmac.inc ; Break macro etc (for following include files only)
+include dossym.inc ; User_<Reg> defines
+include segorder.inc ; segments
+include mult.inc ; MultNET
+include sf.inc ; SFT definitions/structure
+include pdb.inc ; program header/process data block structure
+
+include debugmac.inc ; DbgPrint macro
+include asmmacro.inc ; language extensions
+
+include nwdos.inc ; NetWare structures and nwapi32 interface
+
+.cref ; switch cross-reference back on
+.list ; switch listing back on
+subttl ; kill subtitling started in include file
+
+
+.286 ; all code in this module 286 compatible
+
+far_segment segment
+far_label label far
+far_segment ends
+
+ResidentCodeStart
+
+ assume cs:ResidentCode
+ assume ds:nothing
+ assume es:nothing
+ assume ss:nothing
+
+ public Old21Handler
+Old21Handler dd ?
+
+;
+; IMPORTANT: the following up to the comment <END NWDOSTABLE> must
+; be kept in the same order as for the NWDOSTABLE structure in NWDOS.H/.INC.
+; Align on 32 bits to make it convenient for nwapi32.dll
+;
+ align 4
+
+ public ConnectionIdTable
+ConnectionIdTable CID MC dup (<>)
+
+ public ServerNameTable
+ServerNameTable db MC * SERVERNAME_LENGTH dup (0)
+
+ public DriveIdTable
+DriveIdTable db MD dup (0)
+
+ public DriveFlagTable
+DriveFlagTable db MD dup (0)
+
+ public DriveHandleTable
+DriveHandleTable db MD dup (0)
+
+ public PreferredServer
+PreferredServer db 0
+
+ public PrimaryServer
+PrimaryServer db 0
+
+ public TaskModeByte
+TaskModeByte db 0
+
+CurrentDrive db 0
+
+ public SavedAx;
+SavedAx dw 0
+
+ public NtHandleHi;
+NtHandleHi dw 0
+ public NtHandleLow;
+NtHandleLow dw 0
+
+ public NtHandleSrcHi; // Used in FileServerCopy
+NtHandleSrcHi dw 0
+ public NtHandleSrcLow;
+NtHandleSrcLow dw 0
+
+ public hVDD
+hVDD dw -1
+
+ public PmSelector
+PmSelector dw 0
+
+ public CreatedJob
+CreatedJob db 0
+ public JobHandle
+JobHandle db 0
+
+NOV_BUFFER_LENGTH equ 256
+
+ public DenovellBuffer
+DenovellBuffer db NOV_BUFFER_LENGTH dup (?)
+
+ public DenovellBuffer2
+DenovellBuffer2 db NOV_BUFFER_LENGTH dup (?)
+
+
+.errnz (size DeNovellBuffer2 - size DenovellBuffer)
+
+Comspec db "COMSPEC="
+COMSPEC_LENGTH equ ($ - Comspec)
+
+;
+; this is the <END NWDOSTABLE> structure.
+;
+
+;
+; data passed from nw16.asm
+;
+ public not_exclusive
+not_exclusive db 0
+
+ page
+
+ public NwInt21
+NwInt21 proc far
+ assume cs:ResidentCode
+ assume ds:nothing
+ assume es:nothing
+ assume ss:nothing
+
+ sti ; make sure ints are still enabled
+
+;
+; check whether we filter this vector; if not, pass it through to previous INT 21
+; handler (DOS or some other TSR)
+;
+; If this is a name based operation, and the caller is passing through a novell
+; format name - SYS:FOO or SERVER\SYS:FOO - then munge the name to be a UNC name
+;
+
+ cmp ah,0eh
+ jne @f
+ jmp select_default_drive
+@@: cmp ah,39h ; create directory
+ je check_name
+ ja @f
+
+;
+; ah less than 39h (mkdir) is definitely for DOS
+;
+
+ public quick_jump_to_dos
+quick_jump_to_dos:
+ jmp far_label
+
+;
+; run any of the following name-based calls through the name check:
+;
+; 3ah remove directory
+; 3bh change directory
+; 3ch create file
+; 3dh open file
+; 41h delete file
+; 43h get/set attributes
+; 4bh exec program
+; 4eh find first file
+; 56h rename
+;
+
+@@: cmp ah,3dh
+ jbe check_name
+ cmp ah,41h ; delete file
+ je check_name
+ cmp ah,43h ; get/set attributes
+ je check_name
+ cmp ah,4bh ; exec program
+ je check_name
+ cmp ah,4eh ; find first file
+ je check_name
+ cmp ah,56h ; rename
+ je rename
+ jmp dispatch_check
+
+
+;
+; Rename function. This has 2 path names: source in ds:dx and
+; destination in es:di. Check the destination first then fall through
+; and check the source.
+;
+rename:
+ push ds
+ push dx
+ push es
+ push di ; user registers saved for after Int21
+
+ push ds ; save ds:dx 'cause we will corrupt them
+ push dx
+
+ mov dx,es
+ mov ds,dx
+ mov dx,di ; ds:dx = destination buffer
+ call IsDosPath
+ je @f ; DOS path, no modification
+ cld
+ push di
+ call DenovellizeName
+ pop di
+ cmp dx,offset DenovellBuffer
+ je swap_buffers
+
+@@:
+ pop dx ; ds:dx points at source again
+ pop ds
+
+ pop di
+ pop es
+ pop dx
+ pop ds
+ jmp check_name
+
+;
+; Destination name was normalized and stored in DeNovellBuffer. put the data
+; in Denovellbuffer2 in-case we need to put the Source name in Denovellbuffer
+;
+
+swap_buffers:
+ push cx
+ push si
+ push ds ; will become es during Dos call
+
+ mov si,dx
+ mov di,cs
+ mov es,di
+ mov di,offset DenovellBuffer2
+ mov cx,NOV_BUFFER_LENGTH / 2
+.errnz (NOV_BUFFER_LENGTH and 1)
+
+ rep movsw
+
+ mov di,offset DenovellBuffer2
+ pop es ; es:di is now Denovellbuffer2
+ pop si
+ pop cx
+
+ pop dx ; make ds:dx source again
+ pop ds
+
+ ; stack has users di,es,dx,ds pushed
+ ; parameters are same as callers except for es:di
+ jmp check_src
+
+check_name: ; ds:dx points at name to examine
+ push ds
+ push dx
+ push es
+ push di
+ ; fall through
+
+check_src: ; only jumped to in rename
+
+ cld
+ call IsDosPath
+ je for_dos_properR ; x: or UNC filename. No more processing
+
+ cmp ah,3dh
+ jne notNETQ ; special NETQ open only applies for create
+ cmp CreatedJob,0
+ jz notNETQ ; don't look at name if no job handle available
+
+ push ax
+ push si
+ mov si,dx
+ cld
+ lodsw
+ cmp ax,"EN"
+ jne @f
+ lodsw
+ cmp ax,"QT"
+ jne @f
+ lodsb
+ or al,al
+ jnz @f
+
+ pop si ; Opening NETQ. Return Dos handle from CreateJob and File
+ pop ax
+ mov CreatedJob,0 ; Only return handle once
+ mov al, JobHandle
+ xor ah, ah
+ pop di
+ pop es
+ pop dx
+ pop ds
+ clc
+ retf 2
+
+@@: pop si
+ pop ax
+ jmp for_dos_properR
+
+notNETQ:push di
+ call DenovellizeName ; munge the name if required
+ pop di ; restore caller DI
+
+;
+; Look for compatibility mode opens that need to change to exlusive mode
+; opens so that they get properly cached. Criteria for opening exclusive
+; is that the application did not specify any sharing modes and the drive
+; being opened is on a netware drive.
+;
+
+ cmp ah, 3ch
+ je @f
+ cmp ah, 3dh
+ jne not_compat
+@@: test al,OF_SHARE_MASK
+ jne not_compat
+
+ cmp not_exclusive, 1 ; open shared mode anyway
+ je not_compat
+
+ mov SavedAx,ax
+ mov ax,hVdd
+ DispatchCall ; 32 bit code decides if compat mode
+
+not_compat:
+ pushf
+ call Old21Handler ; fake int 21 to get to DOS
+
+ pop di
+ pop es
+ pop dx
+ pop ds
+ retf 2 ; return to app (with flags from DOS)
+
+for_dos_properR: ; restore regs and call dos
+ pop di
+ pop es
+ pop dx
+ pop ds
+ cmp ah, 3ch
+ je @f
+ cmp ah, 3dh
+ jne for_dos_proper
+@@: test al,OF_SHARE_MASK
+ jne for_dos_proper
+ cmp not_exclusive, 1 ; open shared mode anyway
+ je for_dos_proper
+ mov SavedAx,ax
+ mov ax,hVdd
+@@: DispatchCall ; 32 bit code decides if compat mode
+ public for_dos_proper
+for_dos_proper:
+ jmp far_label
+
+dispatch_check:
+ cmp ah,04ch
+ jne check_9f
+ jmp process_exit
+
+;
+; 'special' entry point to return the data segment info to the protect-mode code
+; so it can generate an LDT descriptor which refers to this memory.
+;
+
+check_9f:
+ cmp ah,9fh
+ jne check_nw_ep ; is it a Netware call?
+ or al,al
+ jnz check_handle_mapper
+ mov bx,seg ConnectionIdTable; 9f00: return segment info
+ mov dx,offset ConnectionIdTable
+ clc ; if we loaded then it can't fail
+ retf 2
+
+;
+; if the call is 9f01 then we call MapNtHandle for the value in BX. This will
+; update NtHandleHi and NtHandleLow, which we assume will be accessed from the
+; code segment register
+;
+
+check_handle_mapper:
+ cmp al,1
+ jne check_nw_ep ; still not one of ours?
+ call MapNtHandle ; 9f01: call MapNtHandle
+ retf 2
+
+check_nw_ep:
+ cmp ah,0b4h
+ jb for_dos_proper
+ cmp ah,0f3h
+ ja for_dos_proper
+ jne @f
+ jmp file_server_copy
+@@: cmp ah,0BAh
+ jne check_f0
+
+ push bx ; get environment. used by map.exe
+ push ax
+ mov ah,051h ; load current apps PDB into ax
+ int 021h
+
+@@: mov es, bx
+ cmp bx, es:PDB_Parent_PID
+ je @f
+ mov bx, es:PDB_Parent_PID
+ jmp @b
+@@:
+ mov dx, es:PDB_environ ; set DX to environment segment
+ mov es, dx ; set es:di to value of COMSPEC
+
+ push si
+ push ds
+ mov ds, dx
+ xor si, si
+
+; future code to save space
+; es <- env seg
+; di <- env off
+; ds <- cs
+; si <- offset Comspec
+; cx <- .size Comspec / 2
+; cld
+; repz cmpsw
+; jnz no match
+
+; al <- 0
+; cx <- remaining size of env seg
+; rep scasb
+
+ cld
+next_var:
+ lodsb
+ cmp al, "C"
+ jne @f
+ lodsb
+ cmp al, "O"
+ jne @f
+ lodsb
+ cmp al, "M"
+ jne @f
+ lodsb
+ cmp al, "S"
+ lodsb
+ jne @f
+ cmp al, "P"
+ jne @f
+ lodsb
+ cmp al, "E"
+ jne @f
+ lodsb
+ cmp al, "C"
+ jne @f
+ lodsb
+ cmp al, "="
+ je got_comspec
+
+@@: ; Search for null terminating environment
+ or al,al
+ je next_var
+ lodsb
+ jmp @b
+
+got_comspec:
+ pop ds
+ mov di,si
+ pop si
+
+ pop ax
+ pop bx
+ iret
+
+check_f0:
+ cmp ah,0f0h
+ jne for_me
+
+;
+; if we're here then we're doing simple stuff that we don't need to bop fer
+; currently stuff here is ah=f0, al = 00, 01, 04, 05
+;
+; caveat emptor dept #312: However, it came to pass that we needed to bop when
+; the f00x calls were made without any preceding calls that would cause nwapi32
+; to be loaded
+;
+
+dispatch_f0:
+
+.errnz ((offset PrimaryServer - offset PreferredServer) - 1)
+
+ or al,al ; f000 = set preferred server
+ jnz try_01
+ cmp dl,8
+ ja zap_preferred
+ mov PreferredServer,dl
+ iret
+
+zap_preferred:
+ mov PreferredServer,al ; al contains 0 remember
+ iret
+
+try_01: cmp al,1 ; f001 = get preferred server
+ jnz try_02
+ mov al,PreferredServer
+ iret
+
+try_02: cmp al,2 ; f002 = get default server
+ jnz try_04
+ mov al,PreferredServer
+ or al,al
+ jnz @f
+ mov al,PrimaryServer
+@@: iret
+
+try_04: cmp al,4 ; f004 = set primary server
+ jne try_05
+ cmp dl,8
+ ja zap_primary
+ mov PrimaryServer,dl
+ iret
+
+zap_primary:
+ mov PrimaryServer,0
+ iret
+
+try_05: cmp al,5 ; f005 = get primary server
+ jne for_me
+ mov al,PrimaryServer
+ iret
+
+file_server_copy:
+ call FileServerCopy ; f3 - Used by ncopy.exe
+ ;jmp for_me
+
+;
+; if the process exits and the dll is loaded then call the 32 bit code to
+; close any cached handles.
+;
+
+process_exit:
+ ;jmp for_me
+
+;
+; if we're here then the dispatch code is for a NetWare client API. First we
+; check if we have already loaded the 32-bit code. If not, then load it. If we
+; get an error, we will fall through to DOS
+;
+
+for_me:
+ cmp ah,0BCh ; bc,bd,be need handle mapping
+ jb no_mapping
+ cmp ah,0BEh
+ ja no_mapping
+
+;do_mapping_call:
+ call MapNtHandle ; take bx and find the Nt handle
+
+no_mapping:
+ mov SavedAx,ax
+
+ cmp ah,0e3h ; Look for CreateJob NCP
+ jne @f ; try f2 alternative
+
+ mov al,[si+2] ; si is NCP subfunction
+ jmp lookupcode
+
+@@: cmp ax,0f217h
+ jne do_dispatch ; Not CreateJob
+ mov al,[si+2] ; si is NCP subfunction
+
+lookupcode:
+ cmp al,68h
+ je createjob
+ cmp al,79h
+ jne do_dispatch
+
+
+createjob: ; It is a CreateJob and File
+
+ ; Always return the errorcode from the NCP exchange
+ ; regardless of any earlier failures in the NT plumbing.
+ mov ax, SavedAx
+ push ax ; Open \\Server\queue for NCP
+ push ds
+ push dx
+ mov ax, 9f02h
+ mov SavedAx,ax
+
+ mov ax,hVdd
+ DispatchCall ; Set DeNovellBuffer to \\Server\queue
+ ; and registers ready for DOS OpenFile
+
+ pushf
+ call Old21Handler ; Open \\server\queue
+ jc @f
+ mov JobHandle, al
+ mov CreatedJob, 1 ; Flag JobHandle is valid
+ push bx
+ xor ah, ah
+ mov bx, ax ; JobHandle
+ call MapNtHandle ; take bx and find the Nt handle
+ pop bx
+
+@@:
+ pop dx
+ pop ds ; Proceed and send the NCP
+ pop ax
+ mov SavedAx, ax
+
+do_dispatch:
+ mov ax,hVdd
+ DispatchCall
+ retf 2 ; return to the application
+
+ public chain_previous_int21
+chain_previous_int21:
+ jmp far_label
+
+
+;
+; Save new drive so we can conveniently handle compatibility mode opens.
+; also need to return 32 as the number of available drives.
+;
+
+select_default_drive:
+ pushf
+ call Old21Handler ; fake int 21 to get to DOS
+
+ mov ah,19h ; get current drive
+ pushf
+ call Old21Handler ; fake int 21 to get to DOS
+ mov CurrentDrive,al ; current drive
+
+ mov al,32 ; # of drives supported by NetWare
+ retf 2 ; return to app (with flags from DOS)
+
+
+NwInt21 endp
+
+;*******************************************************************************
+;*
+;* FileServerCopy
+;*
+;* Implement preperation for calling
+;* \\...)
+;*
+;* ENTRY applications registers
+;*
+;* EXIT nothing
+;*
+;* RETURNS nothing
+;*
+;* ASSUMES no registers (except flags) can be destroyed
+;*
+;******************************************************************************/
+
+FileServerCopy proc near
+
+ push ax
+ push bx
+
+ mov bx,word ptr es:[di] ; Map Source Handle
+ call MapNtHandle
+
+ mov bx,NtHandleHi
+ mov NtHandleSrcHi,bx
+ mov bx,NtHandleLow
+ mov NtHandleSrcLow,bx
+
+ mov bx,word ptr es:[di+2] ; Map Destination Handle
+ call MapNtHandle
+
+@@: pop bx
+ pop ax
+
+ ret
+FileServerCopy endp
+
+;*******************************************************************************
+;*
+;* IsDosPath
+;*
+;* Checks to see if a path name looks like a Microsoft path (<drive>:... or
+;* \\...)
+;*
+;* ENTRY ds:dx = path name
+;*
+;* EXIT nothing
+;*
+;* RETURNS ZF = 1: path is for MS-DOS
+;*
+;* ASSUMES no registers (except flags) can be destroyed
+;*
+;******************************************************************************/
+
+IsDosPath proc near
+ push ax
+ xchg si,dx ; si = offset of filename; dx = ????
+ mov al,[si+1] ; al = second character of filename
+ cmp al,':'
+ je @f ; looks like a DOS filename
+ cmp al,'\' ; (X\... or \\...)
+ jne tryFirstbyte
+ cmp al,'/' ; (X/... or //...)
+ jne @f ; second char is not "\" or "/"
+
+tryFirstbyte:
+ mov al,[si] ; al = first character of filename
+ cmp al,'\' ; (\\... or \/...)
+ je @f
+ cmp al,'/' ; (\/... or //...)
+
+@@: xchg si,dx ; dx = offset of filename; si = ????
+ pop ax
+ ret
+IsDosPath endp
+
+;*******************************************************************************
+;*
+;* DenovellizeName
+;*
+;* Converts a name from Novell format (SERVER\SHARE:filename or
+;* SHARE:filename) to DOS UNC name. Server name is found by:
+;*
+;* if PreferredServer != 0 then Index = PreferredServer
+;* else if PrimaryServer != 0 then Index = PrimaryServer
+;* else Index = 0
+;* servername = ServerNameTable[Index * sizeof(SERVER_NAME)]
+;*
+;* ENTRY ds:dx = name
+;*
+;* EXIT ds:dx = offset of DenovellBuffer
+;*
+;* RETURNS if success, DI points to last byte+1 in DenovellBuffer, else
+;* DI is garbage
+;*
+;* ASSUMES 1. filename does not wrap in buffer segment
+;* 2. DI register can be trashed
+;* 3. DF = 0
+;*
+;******************************************************************************/
+
+DenovellizeName proc near
+ assume ds:nothing
+ assume es:nothing
+
+ push ax
+ push bx
+ push cx
+ push bp
+ push si
+ push es
+ mov bp,ds
+
+;
+; get the length of the input filename
+;
+
+ mov cx,ds
+ mov es,cx
+ mov di,dx ; es:di = filename
+ xor cx,cx
+ dec cx ; cx = ffff
+ xor al,al
+ repnz scasb
+ not cx
+ dec cx ; cx = strlen(filename)
+ cmp cx,length DenovellBuffer
+ jb @f
+ jmp dnn_ret ; filename too long: give it to DOS
+
+;
+; find the offset of ':' in the filename
+;
+
+@@: mov bx,cx ; remember length
+ mov di,dx ; es:di = filename
+ mov al,':'
+ repnz scasb ; di = strchr(filename, ':')+1
+ jz @f
+go_home:jmp dnn_ret ; no ':' - not novell format name?
+@@: cmp byte ptr [di],0
+ je go_home ; device name? (eg "LPT1:") - to DOS
+ mov si,di ; si = offset of ':' in name, +1
+
+;
+; find the offset of the first '/' or '\'
+;
+
+ mov cx,bx ; cx = length of filename
+ mov di,dx ; di = offset of filename
+ mov al,'\'
+ repnz scasb
+ sub bx,cx
+ mov cx,bx
+ mov bx,di
+ mov di,dx
+ mov al,'/'
+ repnz scasb
+ jnz @f
+ mov bx,di
+
+;
+; if ':' before '\' or '/' then name is SYS:FOO... else SERVER\SYS:FOO...
+;
+
+@@: mov di,cs
+ mov es,di
+ mov di,offset DenovellBuffer
+ mov ax,('\' shl 8) + '\'
+ stosw
+ cmp bx,si
+ jb copy_share_name
+ xor bx,bx
+ mov cl,PreferredServer
+ or cl,cl
+ jnz got_index
+ mov cl,PrimaryServer
+ jcxz get_server_name
+
+got_index:
+ dec cl
+ jz get_server_name
+ mov bx,cx
+
+.errnz SERVERNAME_LENGTH - 48
+
+ shl cx,5
+ shl bx,4
+
+get_server_name:
+ add bx,cx
+ mov cx,ds
+ mov si,es
+ mov ds,si
+ lea si,ServerNameTable[bx]
+ cmp byte ptr [si],0
+ je dnn_ret
+ mov ah,SERVERNAME_LENGTH
+
+copy_server_name:
+ lodsb
+ or al,al
+ jz done_server_name
+ stosb
+ dec ah
+ jnz copy_server_name
+
+done_server_name:
+ mov al,'\'
+ stosb
+ mov ds,cx
+
+copy_share_name:
+ mov si,dx
+
+next_char:
+ lodsb
+ cmp al,':'
+ je @f
+ stosb
+ jmp short next_char
+@@: mov al,'\'
+ stosb
+
+copy_rest:
+ lodsb
+ stosb
+ or al,al
+ jnz copy_rest
+ cmp byte ptr [si-2],':'
+ jne @f
+ mov byte ptr [si-2],0
+@@: mov dx,offset DenovellBuffer
+ mov bp,es
+
+dnn_ret:mov ds,bp
+ pop es
+ pop si
+ pop bp
+ pop cx
+ pop bx
+ pop ax
+ ret
+DenovellizeName endp
+
+
+
+;*** DosCallBack
+;*
+;* Call back into DOS via the int 2f/ah=12 back door. If CALL_DOS defined,
+;* use a call, else s/w interrupt. Using a call means no other TSRs etc.
+;* which load AFTER the redir can hook it, but we DON'T HAVE TO MAKE A
+;* PRIVILEGE TRANSITION ON x86 which speeds things up. This should be safe,
+;* because no other s/w should really be hooking INT 2F/AH=12
+;*
+;* ENTRY FunctionNumber - dispatch code goes in al
+;* DosAddr - if present, variable containing address of
+;* DOS int 2f entry point
+;* OldMultHandler - this variable contains the address of DOSs
+;* int 2f back door. Specific to redir code
+;*
+;* EXIT nothing
+;*
+;* USES ax, OldMultHandler
+;*
+;* ASSUMES nothing
+;*
+;***
+
+DosCallBack macro FunctionNumber, DosAddr
+ mov ax,(MultDOS shl 8) + FunctionNumber
+ifdef CALL_DOS
+ pushf
+ifb <DosAddr>
+if (((.type OldMultHandler) and 32) eq 0) ;; OldMultHandler not defined
+ extrn OldMultHandler:dword
+endif
+ call OldMultHandler
+else
+ call DosAddr
+endif
+else
+ int 2fh
+endif
+endm
+
+;
+; defines for DosCallBack FunctionNumbers
+;
+
+SF_FROM_SFN = 22
+PJFN_FROM_HANDLE= 32
+
+; *** MapNtHandle
+; *
+; * Given a handle in BX, map it to a 32-bit Nt handle store result
+; * in NtHandle[Hi|Low]
+; *
+; *
+; * ENTRY bx = handle to map
+; *
+; * EXIT Success - NtHandle set to 32-bit Nt handle from SFT
+; *
+; * RETURNS Success - CF = 0
+; * Failure - CF = 1, ax = ERROR_INVALID_HANDLE
+; *
+; * USES ax, bx, flags
+; *
+; * ASSUMES nothing
+; *
+; ***
+
+MapNtHandle proc near
+ pusha ; save regs used by Dos call back
+ push ds
+ push es
+
+;
+; call back to Dos to get the pointer to the JFN in our caller's JFT. Remember
+; the handle (BX) is an index into the JFT. The byte at this offset in the JFT
+; contains the index of the SFT structure we want in the system file table
+;
+
+ DosCallBack PJFN_FROM_HANDLE ; pJfnFromHamdle
+ jc @f ; bad handle
+
+;
+; we retrieved a pointer to the required byte in the JFT. The byte at this
+; pointer is the SFT index which describes our 'file' (file to (un)lock in
+; this case). We use this as an argument to the next call back function -
+; get Sft from System File Number.
+;
+
+ mov bl,es:[di]
+ xor bh,bh
+ DosCallBack SF_FROM_SFN ; SfFromSfn
+ jc @f ; oops - bad handle
+
+;
+; Ok. We have a pointer to the SFT which describes this named pipe. Get the
+; 32-bit Nt handle and store it in the shared datastructure.
+;
+
+ mov bx,word ptr es:[di].sf_NtHandle[2]
+ mov NtHandleHi,bx
+ mov bx,word ptr es:[di].sf_NtHandle
+ mov NtHandleLow,bx
+
+;
+; restore all registers used by Dos call back.
+; Carry flag is set appropriately
+;
+
+@@: pop es
+ pop ds
+ popa
+ jnc @f
+
+;
+; finally, if there was an error then return a bad handle indication in ax
+;
+
+ mov ax,ERROR_INVALID_HANDLE
+@@: ret
+MapNtHandle endp
+
+ResidentCodeEnd
+
+end
diff --git a/private/nw/nw16/tsr/segorder.inc b/private/nw/nw16/tsr/segorder.inc
new file mode 100644
index 000000000..689c62fa2
--- /dev/null
+++ b/private/nw/nw16/tsr/segorder.inc
@@ -0,0 +1,113 @@
+;/*++
+;
+;Copyright (c) 1991 Microsoft Corporation
+;
+;Module Name:
+;
+; segorder.inc
+;
+;Abstract:
+;
+; This module contains the segment order and segment macros
+;
+;Author:
+;
+; Richard Firth (rfirth) 05-Sep-1991
+;
+;Environment:
+;
+; Dos mode only
+;
+;Notes:
+;
+; When initially loaded, the NT VDM redir has the following order:
+;
+; +----------------------+
+; | |
+; | Resident Code |
+; | |
+; +----------------------+
+; | |
+; | Resident Data |
+; | |
+; +----------------------+ ----------------+
+; | | |
+; | Initialisation Code | <- entry point v
+; | |
+; +----------------------+
+; | | all the stuff between these
+; | Initialisation Data | arrows is discarded if we stay
+; | | resident. Note that the redir
+; +----------------------+ does not uninstall
+; | |
+; | Initialisation Stack | ^
+; | | |
+; +----------------------+ ----------------+
+;
+;Revision History:
+;
+; 05-Sep-1991 rfirth
+; Created
+;
+;--*/
+
+
+
+ResidentStart segment public para 'code'
+ResidentStart ends
+
+ResidentCode segment public word 'code'
+ResidentCode ends
+
+ResidentData segment public word 'data'
+ResidentData ends
+
+ResidentEnd segment public para 'data'
+ResidentEnd ends
+
+ResidentGroup group ResidentStart, ResidentCode, ResidentData, ResidentEnd
+
+InitCode segment public para 'init'
+InitCode ends
+
+InitData segment public word 'init'
+InitData ends
+
+InitStack segment stack para 'stack'
+InitStack ends
+
+;
+; macros to avoid having to type in/possibly alter segment header guff
+;
+
+ResidentCodeStart macro
+ResidentCode segment public word 'code'
+endm
+
+ResidentCodeEnd macro
+ResidentCode ends
+endm
+
+ResidentDataStart macro
+ResidentData segment public word 'data'
+endm
+
+ResidentDataEnd macro
+ResidentData ends
+endm
+
+InitCodeStart macro
+InitCode segment public para 'init'
+endm
+
+InitCodeEnd macro
+InitCode ends
+endm
+
+InitDataStart macro
+InitData segment public word 'init'
+endm
+
+InitDataEnd macro
+InitData ends
+endm
diff --git a/private/nw/nwapi32/makefile b/private/nw/nwapi32/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/nw/nwapi32/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/nwapi32/nwapi32.c b/private/nw/nwapi32/nwapi32.c
new file mode 100644
index 000000000..ed544a5a8
--- /dev/null
+++ b/private/nw/nwapi32/nwapi32.c
@@ -0,0 +1,3 @@
+//
+// dummy file to make build happy
+//
diff --git a/private/nw/nwapi32/nwapi32.rc b/private/nw/nwapi32/nwapi32.rc
new file mode 100644
index 000000000..9843de022
--- /dev/null
+++ b/private/nw/nwapi32/nwapi32.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "NW Win32 API DLL"
+#define VER_INTERNALNAME_STR "NwApi32.DLL"
+#define VER_ORIGINALFILENAME_STR "NwApi32.DLL"
+
+#include "common.ver"
+
diff --git a/private/nw/nwapi32/nwapi32.src b/private/nw/nwapi32/nwapi32.src
new file mode 100644
index 000000000..c55c05579
--- /dev/null
+++ b/private/nw/nwapi32/nwapi32.src
@@ -0,0 +1,118 @@
+LIBRARY NWAPI32
+
+DESCRIPTION 'NWAPI32'
+
+EXPORTS
+
+ NwlibMakeNcp
+ NwLibCanonUserName
+ NwLibCanonLocalName
+ NwLibValidateLocalName
+ NwLibCanonRemoteName
+ NwlibCopyStringToBuffer
+ NwLibSetEverybodyPermission
+ NwDupStringA
+ MapSpecialJapaneseChars
+ UnmapSpecialJapaneseChars
+
+ NWAttachToFileServer
+ NWAttachToFileServerW
+ NWDetachFromFileServer
+ NWGetFileServerVersionInfo
+ NWGetObjectName
+ NWGetVolumeName
+ NWLoginToFileServer
+ NWLogoutFromFileServer
+ NWAddTrusteeToDirectory
+ NWAllocPermanentDirectoryHandle
+ NWAllocTemporaryDirectoryHandle
+ NWCheckConsolePrivileges
+ NWDeallocateDirectoryHandle
+ NWGetInternetAddress
+ NWGetVolumeInfoWithNumber
+ NWGetVolumeInfoWithHandle
+ NWReadPropertyValue
+ NWScanObject
+ NWScanProperty
+ NWIsObjectInSet
+ NWGetFileServerDateAndTime
+
+ NWPAttachToFileServerW
+ NWPDetachFromFileServer
+ NWPGetFileServerVersionInfo
+ NWPGetObjectName
+ NWPLoginToFileServerW
+ NWPLogoutFromFileServer
+ NWPReadPropertyValue
+ NWPScanObject
+ NWPScanProperty
+
+ NWPDeleteObject
+ NWPCreateObject
+ NWPAddTrustee
+ NWPWritePropertyValue
+ NWPGetObjectID
+ NWPRenameBinderyObject
+ NWPAddObjectToSet
+ NWPDeleteObjectFromSet
+ NWPCreateProperty
+ NWPDeleteProperty
+ NWPCreateDirectory
+ NWPGetChallengeKey
+ NWPChangeObjectPasswordEncrypted
+
+ NWCAttachToFileServer
+ NWCAttachToFileServerW
+ NWCDetachFromFileServer
+ NWCGetFileServerVersionInfo
+ NWCGetObjectName
+ NWCGetVolumeName
+ NWCLoginToFileServer
+ NWCLogoutFromFileServer
+ NWCAddTrusteeToDirectory
+ NWCAllocPermanentDirectoryHandle
+ NWCAllocTemporaryDirectoryHandle
+ NWCCheckConsolePrivileges
+ NWCDeallocateDirectoryHandle
+ NWCGetInternetAddress
+ NWCGetVolumeInfoWithNumber
+ NWCGetVolumeInfoWithHandle
+ NWCReadPropertyValue
+ NWCScanObject
+ NWCScanProperty
+ NWCIsObjectInSet
+ NWCGetFileServerDateAndTime
+
+ NWCDeleteObject
+ NWCCreateObject
+ NWCAddTrustee
+ NWCWritePropertyValue
+ NWCGetObjectID
+ NWCRenameBinderyObject
+ NWCAddObjectToSet
+ NWCDeleteObjectFromSet
+ NWCCreateProperty
+ NWCDeleteProperty
+ NWCCreateDirectory
+ NWCScanForTrustees
+ NWCScanDirectoryForTrustees2
+ NWCGetBinderyAccessLevel
+
+ FragExWithWait
+ ParseResponse
+ FormatBuf
+ NwNdsOpenTreeHandle
+ NwNdsOpenGenericHandle
+ NwNdsSetTreeContext
+ NwNdsGetTreeContext
+ NwNdsResolveName
+ NwNdsList
+ NwNdsReadObjectInfo
+ NwNdsReadAttribute
+ NwNdsOpenStream
+ NwNdsGetQueueInformation
+ NwNdsGetVolumeInformation
+ NwNdsChangePassword
+ NwOpenHandleWithSupplementalCredentials
+
+DATA SINGLE SHARED
diff --git a/private/nw/nwapi32/sources b/private/nw/nwapi32/sources
new file mode 100644
index 000000000..8555e194f
--- /dev/null
+++ b/private/nw/nwapi32/sources
@@ -0,0 +1,62 @@
+!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=nwapi
+
+TARGETNAME=nwapi32
+TARGETPATH=$(BASEDIR)\public\sdk\lib
+TARGETTYPE=DYNLINK
+DLLDEF=obj\*\nwapi32.def
+#DLLENTRY=NwApiInitialize
+DLLBASE=0x6970000
+
+INCLUDES=..\inc;$(_NTROOT)\private\inc;
+
+SOURCES= \
+ nwapi32.c \
+ nwapi32.rc
+
+LINKLIBS= \
+ ..\nwlib\obj\*\nwlib.lib \
+
+TARGETLIBS= \
+ $(BASEDIR)\Public\Sdk\Lib\*\kernel32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\user32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\advapi32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\nwprovau.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\kernel32.lib \
+ ..\nwlib\obj\*\nwlib.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\ntvdm.lib
+
+UNICODE=1
+
+NET_C_DEFINES=-DRPC_NO_WINDOWS_H -DNWDBG
+
+UMTYPE=console
+
+UMTEST=
+
+UMLIBS=
+
+OPTIONAL_UMTEST=
+
diff --git a/private/nw/nwlib/canon.c b/private/nw/nwlib/canon.c
new file mode 100644
index 000000000..c33846a66
--- /dev/null
+++ b/private/nw/nwlib/canon.c
@@ -0,0 +1,480 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ canon.c
+
+Abstract:
+
+ Contains canonicalization routines for NetWare names.
+
+Author:
+
+ Rita Wong (ritaw) 19-Feb-1993
+
+Environment:
+
+
+Revision History:
+
+
+--*/
+
+
+#include <procs.h>
+
+
+DWORD
+NwLibValidateLocalName(
+ IN LPWSTR LocalName
+ )
+/*++
+
+Routine Description:
+
+ This routine checks to see if the supplied name is a valid
+ DOS device name.
+
+Arguments:
+
+ LocalName - Supplies a local device name. It can be any of
+ the following:
+
+ X:
+ LPTn or LPTn:
+ COMn or COMn:
+ PRN or PRN:
+ AUX or AUX:
+
+
+Return Value:
+
+ NO_ERROR - LocalName is valid.
+
+ WN_BAD_NETNAME - LocalName is invalid.
+
+--*/
+{
+ DWORD LocalNameLength;
+
+
+ //
+ // Cannot be a NULL or empty string
+ //
+ if (LocalName == NULL || *LocalName == 0) {
+ return WN_BAD_NETNAME;
+ }
+
+ LocalNameLength = wcslen(LocalName);
+
+ if (LocalNameLength == 1) {
+ return WN_BAD_NETNAME;
+ }
+
+ if (LocalName[LocalNameLength - 1] == L':') {
+ if (! IS_VALID_TOKEN(LocalName, LocalNameLength - 1)) {
+ return WN_BAD_NETNAME;
+ }
+ }
+ else {
+ if (! IS_VALID_TOKEN(LocalName, LocalNameLength)) {
+ return WN_BAD_NETNAME;
+ }
+ }
+
+ if (LocalNameLength == 2) {
+ //
+ // Must be in the form of X:
+ //
+ if (! iswalpha(*LocalName)) {
+ return WN_BAD_NETNAME;
+ }
+
+ if (LocalName[1] != L':') {
+ return WN_BAD_NETNAME;
+ }
+
+ return NO_ERROR;
+ }
+
+ if (RtlIsDosDeviceName_U(LocalName) == 0) {
+ return WN_BAD_NETNAME;
+ }
+
+ //
+ // Valid DOS device name but invalid redirection name
+ //
+ if (_wcsnicmp(LocalName, L"NUL", 3) == 0) {
+ return WN_BAD_NETNAME;
+
+ }
+ return NO_ERROR;
+}
+
+
+DWORD
+NwLibCanonLocalName(
+ IN LPWSTR LocalName,
+ OUT LPWSTR *OutputBuffer,
+ OUT LPDWORD OutputBufferLength OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This routine canonicalizes the local name by uppercasing the string
+ and converting the following:
+
+ x: -> X:
+ LPTn: -> LPTn
+ COMn: -> COMn
+ PRN or PRN: -> LPT1
+ AUX or AUX: -> COM1
+
+Arguments:
+
+ LocalName - Supplies a local device name.
+
+ OutputBuffer - Receives a pointer to the canonicalized LocalName.
+
+ OutputBufferLength - Receives the length of the canonicalized name
+ in number of characters, if specified.
+
+Return Value:
+
+ NO_ERROR - Successfully canonicalized the local name.
+
+ WN_BAD_NETNAME - LocalName is invalid.
+
+ ERROR_NOT_ENOUGH_MEMORY - Could not allocate output buffer.
+
+--*/
+{
+ DWORD status;
+ DWORD LocalNameLength;
+
+
+ status = NwLibValidateLocalName(LocalName);
+
+ if (status != NO_ERROR) {
+ return status;
+ }
+
+ LocalNameLength = wcslen(LocalName);
+
+ //
+ // Allocate output buffer. Should be the size of the LocalName
+ // plus 1 for the special case of PRN -> LPT1 or AUX -> COM1.
+ //
+ *OutputBuffer = (PVOID) LocalAlloc(
+ LMEM_ZEROINIT,
+ (LocalNameLength + 2) * sizeof(WCHAR)
+ );
+
+ if (*OutputBuffer == NULL) {
+ KdPrint(("NWLIB: NwLibCanonLocalName LocalAlloc failed %lu\n",
+ GetLastError()));
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ wcscpy(*OutputBuffer, LocalName);
+
+ if (LocalNameLength > 2) {
+
+ if (_wcsnicmp(*OutputBuffer, L"PRN", 3) == 0) {
+
+ //
+ // Convert PRN or PRN: to LPT1
+ //
+ wcscpy(*OutputBuffer, L"LPT1");
+ LocalNameLength = 4;
+
+ }
+ else if (_wcsnicmp(*OutputBuffer, L"AUX", 3) == 0) {
+
+ //
+ // Convert AUX or AUX: to COM1
+ //
+ wcscpy(*OutputBuffer, L"COM1");
+ LocalNameLength = 4;
+ }
+
+ //
+ // Remove trailing colon, if there is one, and decrement the length
+ // of DOS device name.
+ //
+ if ((*OutputBuffer)[LocalNameLength - 1] == L':') {
+ (*OutputBuffer)[--LocalNameLength] = 0;
+ }
+ }
+
+ //
+ // LocalName is always in uppercase.
+ //
+ _wcsupr(*OutputBuffer);
+
+ if (ARGUMENT_PRESENT(OutputBufferLength)) {
+ *OutputBufferLength = LocalNameLength;
+ }
+
+ return NO_ERROR;
+}
+
+
+DWORD
+NwLibCanonRemoteName(
+ IN LPWSTR LocalName OPTIONAL,
+ IN LPWSTR RemoteName,
+ OUT LPWSTR *OutputBuffer,
+ OUT LPDWORD OutputBufferLength OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This routine validates and canonicalizes the supplied
+ NetWare UNC name. It can be of any length in the form of:
+
+ \\Server\Volume\Directory\Subdirectory
+
+Arguments:
+
+ LocalName - Supplies the local device name. If it is NULL, then
+ \\Server is an acceptable format for the UNC name.
+
+ RemoteName - Supplies the UNC name.
+
+ OutputBuffer - Receives a pointer to the canonicalized RemoteName.
+
+ OutputBufferLength - Receives the length of the canonicalized name
+ in number of characters, if specified.
+
+Return Value:
+
+ NO_ERROR - RemoteName is valid.
+
+ WN_BAD_NETNAME - RemoteName is invalid.
+
+--*/
+{
+ DWORD RemoteNameLength;
+ DWORD i;
+ DWORD TokenLength;
+ LPWSTR TokenPtr;
+ BOOL fFirstToken = TRUE;
+
+
+ //
+ // Cannot be a NULL or empty string
+ //
+ if (RemoteName == NULL || *RemoteName == 0) {
+ return WN_BAD_NETNAME;
+ }
+
+ RemoteNameLength = wcslen(RemoteName);
+
+ //
+ // Must be at least \\x\y if local device name is specified.
+ // Otherwise it must be at least \\x.
+ //
+ if ((RemoteNameLength < 5 && ARGUMENT_PRESENT(LocalName)) ||
+ (RemoteNameLength < 3)) {
+ return WN_BAD_NETNAME;
+ }
+
+ //
+ // First two characters must be "\\"
+ //
+ if (*RemoteName != L'\\' || RemoteName[1] != L'\\') {
+ return WN_BAD_NETNAME;
+ }
+
+ if (! ARGUMENT_PRESENT(LocalName) &&
+ (IS_VALID_TOKEN(&RemoteName[2], RemoteNameLength - 2))) {
+
+ //
+ // Return success for \\Server case.
+ //
+
+ *OutputBuffer = (PVOID) LocalAlloc(
+ LMEM_ZEROINIT,
+ (RemoteNameLength + 1) * sizeof(WCHAR)
+ );
+
+ if (*OutputBuffer == NULL) {
+ KdPrint(("NWLIB: NwLibCanonRemoteName LocalAlloc failed %lu\n",
+ GetLastError()));
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ wcscpy(*OutputBuffer, RemoteName);
+
+ return NO_ERROR;
+ }
+
+ //
+ // Must have at least one more backslash after the third character
+ //
+ if (wcschr(&RemoteName[3], L'\\') == NULL) {
+ return WN_BAD_NETNAME;
+ }
+
+ //
+ // Last character cannot a backward slash
+ //
+ if (RemoteName[RemoteNameLength - 1] == L'\\') {
+ return WN_BAD_NETNAME;
+ }
+
+ //
+ // Allocate output buffer. Should be the size of the RemoteName
+ // and space for an extra character to simplify parsing code below.
+ //
+ *OutputBuffer = (PVOID) LocalAlloc(
+ LMEM_ZEROINIT,
+ (RemoteNameLength + 2) * sizeof(WCHAR)
+ );
+
+
+ if (*OutputBuffer == NULL) {
+ KdPrint(("NWLIB: NwLibCanonRemoteName LocalAlloc failed %lu\n",
+ GetLastError()));
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ wcscpy(*OutputBuffer, RemoteName);
+
+ //
+ // Convert all backslashes to NULL terminator, skipping first 2 chars.
+ //
+ for (i = 2; i < RemoteNameLength; i++) {
+ if ((*OutputBuffer)[i] == L'\\') {
+
+ (*OutputBuffer)[i] = 0;
+
+ //
+ // Two consecutive forward or backslashes is bad.
+ //
+ if ((i + 1 < RemoteNameLength) &&
+ ((*OutputBuffer)[i + 1] == L'\\')) {
+
+ (void) LocalFree((HLOCAL) *OutputBuffer);
+ *OutputBuffer = NULL;
+ return WN_BAD_NETNAME;
+ }
+ }
+ }
+
+ //
+ // Validate each token of the RemoteName, separated by NULL terminator.
+ //
+ TokenPtr = *OutputBuffer + 2; // Skip first 2 chars
+
+ while (*TokenPtr != 0) {
+
+ TokenLength = wcslen(TokenPtr);
+
+ if ( ( fFirstToken && !IS_VALID_SERVER_TOKEN(TokenPtr, TokenLength))
+ || ( !fFirstToken && !IS_VALID_TOKEN(TokenPtr, TokenLength))
+ )
+ {
+ (void) LocalFree((HLOCAL) *OutputBuffer);
+ *OutputBuffer = NULL;
+ return WN_BAD_NETNAME;
+ }
+
+ fFirstToken = FALSE;
+ TokenPtr += TokenLength + 1;
+ }
+
+ //
+ // Convert NULL separators to backslashes
+ //
+ for (i = 0; i < RemoteNameLength; i++) {
+ if ((*OutputBuffer)[i] == 0) {
+ (*OutputBuffer)[i] = L'\\';
+ }
+ }
+
+ if (ARGUMENT_PRESENT(OutputBufferLength)) {
+ *OutputBufferLength = RemoteNameLength;
+ }
+
+ return NO_ERROR;
+}
+
+
+DWORD
+NwLibCanonUserName(
+ IN LPWSTR UserName,
+ OUT LPWSTR *OutputBuffer,
+ OUT LPDWORD OutputBufferLength OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This routine canonicalizes the user name by checking to see
+ if the name contains any illegal characters.
+
+
+Arguments:
+
+ UserName - Supplies a username.
+
+ OutputBuffer - Receives a pointer to the canonicalized UserName.
+
+ OutputBufferLength - Receives the length of the canonicalized name
+ in number of characters, if specified.
+
+Return Value:
+
+ NO_ERROR - Successfully canonicalized the username.
+
+ WN_BAD_NETNAME - UserName is invalid.
+
+ ERROR_NOT_ENOUGH_MEMORY - Could not allocate output buffer.
+
+--*/
+{
+ DWORD UserNameLength;
+
+
+ //
+ // Cannot be a NULL or empty string
+ //
+ if (UserName == NULL) {
+ return WN_BAD_NETNAME;
+ }
+
+ UserNameLength = wcslen(UserName);
+
+ if (! IS_VALID_TOKEN(UserName, UserNameLength)) {
+ return WN_BAD_NETNAME;
+ }
+
+ //
+ // Allocate output buffer. Should be the size of the UserName
+ // plus 1 for the special case of PRN -> LPT1 or AUX -> COM1.
+ //
+ *OutputBuffer = (PVOID) LocalAlloc(
+ LMEM_ZEROINIT,
+ (UserNameLength + 1) * sizeof(WCHAR)
+ );
+
+ if (*OutputBuffer == NULL) {
+ KdPrint(("NWLIB: NwLibCanonUserName LocalAlloc failed %lu\n",
+ GetLastError()));
+ return ERROR_NOT_ENOUGH_MEMORY;
+ }
+
+ wcscpy(*OutputBuffer, UserName);
+
+ if (ARGUMENT_PRESENT(OutputBufferLength)) {
+ *OutputBufferLength = UserNameLength;
+ }
+
+ return NO_ERROR;
+}
diff --git a/private/nw/nwlib/exchange.c b/private/nw/nwlib/exchange.c
new file mode 100644
index 000000000..52f34ad38
--- /dev/null
+++ b/private/nw/nwlib/exchange.c
@@ -0,0 +1,571 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ exchange.c
+
+Abstract:
+
+ Contains routine which packages the request buffer, makes
+ the NCP request, and unpackages the response buffer.
+
+Author:
+
+ Hans Hurvig (hanshu) Aug-1992 Created
+ Colin Watson (colinw) 19-Dec-1992
+ Rita Wong (ritaw) 11-Mar-1993 FSCtl version
+
+Environment:
+
+
+Revision History:
+
+
+--*/
+
+
+#include <procs.h>
+
+NTSTATUS
+NwlibMakeNcp(
+ IN HANDLE DeviceHandle,
+ IN ULONG FsControlCode,
+ IN ULONG RequestBufferSize,
+ IN ULONG ResponseBufferSize,
+ IN PCHAR FormatString,
+ ... // Arguments to FormatString
+ )
+/*++
+
+Routine Description:
+
+ This function converts the input arguments into an NCP request buffer
+ based on the format specified in FormatString (e.g. takes a word
+ and writes it in hi-lo format in the request buffer as required by
+ an NCP).
+
+ It then makes the NCP call via the NtFsControlFile API.
+
+ The FormatString also specifies how to convert the fields in the
+ response buffer from the completed NCP call into the output
+ arguments.
+
+ The FormatString takes the form of "xxxx|yyyy" where each 'x'
+ indicates an input argument to convert from, and each 'y' indicates
+ an output argument to convert into. The '|' character separates
+ the input format from the output format specifications.
+
+Arguments:
+
+ DeviceHandle - Supplies a handle to the network file system driver
+ which will be making the network request. This function
+ assumes that the handle was opened for synchronouse I/O access.
+
+ FsControlCode - Supplies the control code which determines the
+ NCP.
+
+ RequestBufferSize - Supplies the size of the request buffer in
+ bytes to be allocated by this routine.
+
+ ResponseBufferSize - Supplies the size of the response buffer in
+ bytes to be allocated by this routine.
+
+ FormatString - Supplies an ANSI string which describes how to
+ convert from the input arguments into NCP request fields, and
+ from the NCP response fields into the output arguments.
+
+ Field types, request/response:
+
+ 'b' byte ( byte / byte* )
+ 'w' hi-lo word ( word / word* )
+ 'd' hi-lo dword ( dword / dword* )
+ '-' zero/skip byte ( void )
+ '=' zero/skip word ( void )
+ ._. zero/skip string ( word )
+ 'p' pstring ( char* )
+ 'P' DBCS pstring ( char* )
+ 'c' cstring ( char* )
+ 'C' cstring followed skip word ( char*, word )
+ 'r' raw bytes ( byte*, word )
+ 'R' DBCS raw bytes ( byte*, word )
+ 'u' p unicode string ( UNICODE_STRING * )
+ 'U' p uppercase string( UNICODE_STRING * )
+ 'W' word n followed by an array of word[n] ( word, word* )
+
+Return Value:
+
+ Return code from the NCP call.
+
+--*/
+{
+ NTSTATUS status;
+
+ va_list Arguments;
+ PCHAR z;
+ WORD t = 1;
+ ULONG data_size;
+
+ LPBYTE RequestBuffer = NULL;
+ LPBYTE ResponseBuffer;
+
+ IO_STATUS_BLOCK IoStatusBlock;
+ DWORD ReturnedDataSize;
+
+
+ //
+ // Allocate memory for request and response buffers.
+ //
+ RequestBuffer = LocalAlloc(
+ LMEM_ZEROINIT,
+ RequestBufferSize + ResponseBufferSize
+ );
+
+ if (RequestBuffer == NULL) {
+ KdPrint(("NWLIB: NwlibMakeNcp LocalAlloc failed %lu\n", GetLastError()));
+ return STATUS_NO_MEMORY;
+ }
+
+ ResponseBuffer = (LPBYTE) ((ULONG) RequestBuffer + RequestBufferSize);
+
+
+ va_start( Arguments, FormatString );
+
+ //
+ // Convert the input arguments into request packet.
+ //
+ z = FormatString;
+
+ data_size = 0;
+
+ while ( *z && *z != '|')
+ {
+ switch ( *z )
+ {
+ case '=':
+ RequestBuffer[data_size++] = 0;
+ case '-':
+ RequestBuffer[data_size++] = 0;
+ break;
+
+ case '_':
+ {
+ WORD l = va_arg ( Arguments, WORD );
+ if (data_size + l > RequestBufferSize)
+ {
+ KdPrint(("NWLIB: NwlibMakeNcp case '_' request buffer too small\n"));
+ status = STATUS_BUFFER_TOO_SMALL;
+ goto CleanExit;
+ }
+ while ( l-- )
+ RequestBuffer[data_size++] = 0;
+ break;
+ }
+
+ case 'b':
+ RequestBuffer[data_size++] = va_arg ( Arguments, BYTE );
+ break;
+
+ case 'w':
+ {
+ WORD w = va_arg ( Arguments, WORD );
+ RequestBuffer[data_size++] = (BYTE) (w >> 8);
+ RequestBuffer[data_size++] = (BYTE) (w >> 0);
+ break;
+ }
+
+ case 'd':
+ {
+ DWORD d = va_arg ( Arguments, DWORD );
+ RequestBuffer[data_size++] = (BYTE) (d >> 24);
+ RequestBuffer[data_size++] = (BYTE) (d >> 16);
+ RequestBuffer[data_size++] = (BYTE) (d >> 8);
+ RequestBuffer[data_size++] = (BYTE) (d >> 0);
+ break;
+ }
+
+ case 'c':
+ {
+ char* c = va_arg ( Arguments, char* );
+ WORD l = strlen( c );
+ if (data_size + l > RequestBufferSize)
+ {
+ KdPrint(("NWLIB: NwlibMakeNcp case 'c' request buffer too small\n"));
+ status = STATUS_BUFFER_TOO_SMALL;
+ goto CleanExit;
+ }
+ RtlCopyMemory( &RequestBuffer[data_size], c, l+1 );
+ data_size += l + 1;
+ break;
+ }
+
+ case 'C':
+ {
+ char* c = va_arg ( Arguments, char* );
+ WORD l = va_arg ( Arguments, WORD );
+ WORD len = strlen( c ) + 1;
+ if (data_size + l > RequestBufferSize)
+ {
+ KdPrint(("NWLIB: NwlibMakeNcp case 'C' request buffer too small\n"));
+ status = STATUS_BUFFER_TOO_SMALL;
+ goto CleanExit;
+ }
+
+ RtlCopyMemory( &RequestBuffer[data_size], c, len > l? l : len);
+ data_size += l;
+ RequestBuffer[data_size-1] = 0;
+ break;
+ }
+
+ case 'P':
+ case 'p':
+ {
+ char* c = va_arg ( Arguments, char* );
+ BYTE l = strlen( c );
+ char* p;
+
+ if (data_size + l > RequestBufferSize)
+ {
+ KdPrint(("NWLIB: NwlibMakeNcp case 'p' request buffer too small\n"));
+ status = STATUS_BUFFER_TOO_SMALL;
+ goto CleanExit;
+ }
+ RequestBuffer[data_size++] = l;
+ RtlCopyMemory( (p=(char*)&RequestBuffer[data_size]), c, l );
+ data_size += l;
+
+ //
+ // Map Japanese special chars
+ //
+ if (*z == 'P') {
+ MapSpecialJapaneseChars(p, (WORD)l);
+ }
+
+ break;
+ }
+
+ case 'u':
+ {
+ PUNICODE_STRING pUString = va_arg ( Arguments, PUNICODE_STRING );
+ OEM_STRING OemString;
+ ULONG Length;
+
+ //
+ // Calculate required string length, excluding trailing NUL.
+ //
+
+ Length = RtlUnicodeStringToOemSize( pUString ) - 1;
+ ASSERT( Length < 0x100 );
+
+ if ( data_size + Length > RequestBufferSize ) {
+ KdPrint(("NWLIB: NwlibMakeNcp case 'u' request buffer too small\n"));
+ status = STATUS_BUFFER_TOO_SMALL;
+ goto CleanExit;
+ }
+
+ RequestBuffer[data_size++] = (UCHAR)Length;
+ OemString.Buffer = &RequestBuffer[data_size];
+ OemString.MaximumLength = (USHORT)Length + 1;
+
+ //
+ // BUGBUG: The following should call RtlUnicodeStringToCountedOemString
+ // but this routine is not exported by ntdll.dll so we can't get to it.
+ //
+ status = RtlUnicodeStringToOemString( &OemString, pUString, FALSE );
+ ASSERT( NT_SUCCESS( status ));
+ data_size += (USHORT)Length;
+ break;
+ }
+
+ case 'U':
+ {
+ //
+ // UPPERCASE the string, copy it from unicode to the packet
+ //
+
+ PUNICODE_STRING pUString = va_arg ( Arguments, PUNICODE_STRING );
+ UNICODE_STRING UUppercaseString;
+ OEM_STRING OemString;
+ ULONG Length;
+
+ status = RtlUpcaseUnicodeString(&UUppercaseString, pUString, TRUE);
+ if ( status )
+ {
+ goto CleanExit;
+ }
+
+ pUString = &UUppercaseString;
+
+ //
+ // Calculate required string length, excluding trailing NUL.
+ //
+
+ Length = RtlUnicodeStringToOemSize( pUString ) - 1;
+ ASSERT( Length < 0x100 );
+
+ if ( data_size + Length > RequestBufferSize ) {
+ KdPrint(("NWLIB: NwlibMakeNcp case 'U' request buffer too small\n"));
+ status = STATUS_BUFFER_TOO_SMALL;
+ goto CleanExit;
+ }
+
+ RequestBuffer[data_size++] = (UCHAR)Length;
+ OemString.Buffer = &RequestBuffer[data_size];
+ OemString.MaximumLength = (USHORT)Length + 1;
+
+ //
+ // BUGBUG: The following should call RtlUnicodeStringToCountedOemString
+ // but this routine is not exported by ntdll.dll so we can't get to it.
+ //
+ status = RtlUnicodeStringToOemString( &OemString, pUString, FALSE );
+ ASSERT( NT_SUCCESS( status ));
+
+ RtlFreeUnicodeString( &UUppercaseString );
+
+ data_size += (USHORT)Length;
+ break;
+ }
+
+ case 'R':
+ case 'r':
+ {
+ BYTE* b = va_arg ( Arguments, BYTE* );
+ WORD l = va_arg ( Arguments, WORD );
+ char* c;
+
+ if ( data_size + l > RequestBufferSize )
+ {
+ KdPrint(("NWLIB: NwlibMakeNcp case 'r' request buffer too small\n"));
+ status = STATUS_BUFFER_TOO_SMALL;
+ goto CleanExit;
+ }
+
+ RtlCopyMemory( (c=(char*)&RequestBuffer[data_size]), b, l );
+ data_size += l;
+
+ //
+ // Map Japanese special chars
+ //
+ if (*z == 'R') {
+ MapSpecialJapaneseChars(c, (WORD)l);
+ }
+
+ break;
+ }
+
+ default:
+ KdPrint(("NWLIB: NwlibMakeNcp invalid request field, %x\n", *z));
+ ASSERT(FALSE);
+ }
+
+ if ( data_size > RequestBufferSize )
+ {
+ KdPrint(("NWLIB: NwlibMakeNcp too much request data\n"));
+ status = STATUS_BUFFER_TOO_SMALL;
+ goto CleanExit;
+ }
+
+
+ z++;
+ }
+
+
+ //
+ // Make the NCP request
+ //
+ status = NtFsControlFile(
+ DeviceHandle,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FsControlCode,
+ (PVOID) RequestBuffer,
+ RequestBufferSize,
+ (PVOID) ResponseBuffer,
+ ResponseBufferSize
+ );
+
+ if (status == STATUS_SUCCESS) {
+ status = IoStatusBlock.Status;
+ }
+
+ if (status != STATUS_SUCCESS) {
+
+#if 0
+ if (! NT_SUCCESS(status)) {
+ KdPrint(("NWLIB: NwlibMakeNcp: NtFsControlFile returns x%08lx\n", status));
+ }
+#endif
+
+ goto CleanExit;
+ }
+
+
+ ReturnedDataSize = IoStatusBlock.Information; // Number of bytes returned
+ // in ResponseBuffer
+
+
+ //
+ // Convert the response packet into output arguments.
+ //
+
+ data_size = 0;
+
+ if (*z && *z == '|') {
+ z++;
+ }
+
+ while ( *z )
+ {
+ switch ( *z )
+ {
+ case '-':
+ data_size += 1;
+ break;
+
+ case '=':
+ data_size += 2;
+ break;
+
+ case '_':
+ {
+ WORD l = va_arg ( Arguments, WORD );
+ data_size += l;
+ break;
+ }
+
+ case 'b':
+ {
+ BYTE* b = va_arg ( Arguments, BYTE* );
+ *b = ResponseBuffer[data_size++];
+ break;
+ }
+
+ case 'w':
+ {
+ BYTE* b = va_arg ( Arguments, BYTE* );
+ b[1] = ResponseBuffer[data_size++];
+ b[0] = ResponseBuffer[data_size++];
+ break;
+ }
+
+ case 'd':
+ {
+ BYTE* b = va_arg ( Arguments, BYTE* );
+ b[3] = ResponseBuffer[data_size++];
+ b[2] = ResponseBuffer[data_size++];
+ b[1] = ResponseBuffer[data_size++];
+ b[0] = ResponseBuffer[data_size++];
+ break;
+ }
+
+ case 'c':
+ {
+ char* c = va_arg ( Arguments, char* );
+ WORD l = strlen( &ResponseBuffer[data_size] );
+ if ( data_size+l+1 < ReturnedDataSize ) {
+ RtlCopyMemory( c, &ResponseBuffer[data_size], l+1 );
+ }
+ data_size += l+1;
+ break;
+ }
+
+ case 'C':
+ {
+ char* c = va_arg ( Arguments, char* );
+ WORD l = va_arg ( Arguments, WORD );
+ WORD len = strlen( &ResponseBuffer[data_size] ) + 1;
+
+ if ( data_size + l < ReturnedDataSize ) {
+ RtlCopyMemory( c, &ResponseBuffer[data_size], len > l ? l :len);
+ }
+ c[l-1] = 0;
+ data_size += l;
+ break;
+
+ }
+
+ case 'P':
+ case 'p':
+ {
+ char* c = va_arg ( Arguments, char* );
+ BYTE l = ResponseBuffer[data_size++];
+ if ( data_size+l <= ReturnedDataSize ) {
+ RtlCopyMemory( c, &ResponseBuffer[data_size], l );
+ c[l] = 0;
+ }
+ data_size += l;
+
+ //
+ // Unmap Japanese special chars
+ //
+ if (*z == 'P') {
+ UnmapSpecialJapaneseChars(c, l);
+ }
+
+ break;
+ }
+
+ case 'R':
+ case 'r':
+ {
+ BYTE* b = va_arg ( Arguments, BYTE* );
+ WORD l = va_arg ( Arguments, WORD );
+ RtlCopyMemory( b, &ResponseBuffer[data_size], l );
+ data_size += l;
+
+ //
+ // Unmap Japanese special chars
+ //
+ if (*z == 'R') {
+ UnmapSpecialJapaneseChars(b, l);
+ }
+ break;
+ }
+
+ case 'W':
+ {
+ BYTE* b = va_arg ( Arguments, BYTE* );
+ BYTE* w = va_arg ( Arguments, BYTE* );
+ WORD i;
+
+ b[1] = ResponseBuffer[data_size++];
+ b[0] = ResponseBuffer[data_size++];
+
+ for ( i = 0; i < ((WORD) *b); i++, w += sizeof(WORD) )
+ {
+ w[1] = ResponseBuffer[data_size++];
+ w[0] = ResponseBuffer[data_size++];
+ }
+ break;
+ }
+
+ default:
+ KdPrint(("NWLIB: NwlibMakeNcp invalid response field, %x\n", *z));
+ ASSERT(FALSE);
+ }
+
+ if ( data_size > ReturnedDataSize )
+ {
+ KdPrint(("NWLIB: NwlibMakeNcp not enough response data\n"));
+ status = STATUS_UNSUCCESSFUL;
+ goto CleanExit;
+ }
+
+ z++;
+ }
+
+ status = STATUS_SUCCESS;
+
+CleanExit:
+ if (RequestBuffer != NULL) {
+ (void) LocalFree((HLOCAL) RequestBuffer);
+ }
+
+ va_end( Arguments );
+
+ return status;
+}
+
diff --git a/private/nw/nwlib/makefile b/private/nw/nwlib/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/nw/nwlib/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/nwlib/ndsapi32.c b/private/nw/nwlib/ndsapi32.c
new file mode 100644
index 000000000..9efe22830
--- /dev/null
+++ b/private/nw/nwlib/ndsapi32.c
@@ -0,0 +1,2144 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ NdsLib32.c
+
+Abstract:
+
+ This module implements the exposed user-mode link to
+ Netware NDS support in the Netware redirector. For
+ more comments, see ndslib32.h.
+
+Author:
+
+ Cory West [CoryWest] 23-Feb-1995
+
+--*/
+
+#include <procs.h>
+
+NTSTATUS
+NwNdsOpenTreeHandle(
+ IN PUNICODE_STRING puNdsTree,
+ OUT PHANDLE phNwRdrHandle
+) {
+
+ NTSTATUS ntstatus, OpenStatus;
+ IO_STATUS_BLOCK IoStatusBlock;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ ACCESS_MASK DesiredAccess = SYNCHRONIZE | FILE_LIST_DIRECTORY;
+
+ WCHAR DevicePreamble[] = L"\\Device\\Nwrdr\\";
+ UINT PreambleLength = 14;
+
+ WCHAR NameStr[128];
+ UNICODE_STRING uOpenName;
+ UINT i;
+
+ PNWR_NDS_REQUEST_PACKET Rrp;
+ BYTE RrpData[1024];
+
+ //
+ // Prepare the open name.
+ //
+
+ uOpenName.MaximumLength = sizeof( NameStr );
+
+ for ( i = 0; i < PreambleLength ; i++ )
+ NameStr[i] = DevicePreamble[i];
+
+ try {
+
+ for ( i = 0 ; i < ( puNdsTree->Length / sizeof( WCHAR ) ) ; i++ ) {
+ NameStr[i + PreambleLength] = puNdsTree->Buffer[i];
+ }
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ return STATUS_INVALID_PARAMETER;
+
+ }
+
+ uOpenName.Length = ( i * sizeof( WCHAR ) ) +
+ ( PreambleLength * sizeof( WCHAR ) );
+ uOpenName.Buffer = 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_SUCCESS(ntstatus) )
+ return ntstatus;
+
+ OpenStatus = IoStatusBlock.Status;
+
+ //
+ // Verify that this is a tree handle, not a server handle.
+ //
+
+ Rrp = (PNWR_NDS_REQUEST_PACKET)RrpData;
+
+ Rrp->Version = 0;
+
+ RtlCopyMemory( &(Rrp->Parameters).VerifyTree,
+ puNdsTree,
+ sizeof( UNICODE_STRING ) );
+
+ RtlCopyMemory( (BYTE *)(&(Rrp->Parameters).VerifyTree) + sizeof( UNICODE_STRING ),
+ puNdsTree->Buffer,
+ puNdsTree->Length );
+
+ try {
+
+ ntstatus = NtFsControlFile( *phNwRdrHandle,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_NDS_VERIFY_TREE,
+ (PVOID) Rrp,
+ sizeof( NWR_NDS_REQUEST_PACKET ),
+ NULL,
+ 0 );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+ ntstatus = GetExceptionCode();
+ goto CloseAndExit;
+ }
+
+ if ( !NT_SUCCESS( ntstatus )) {
+ goto CloseAndExit;
+ }
+
+ //
+ // Otherwise, all is well!
+ //
+
+ return OpenStatus;
+
+CloseAndExit:
+
+ NtClose( *phNwRdrHandle );
+ *phNwRdrHandle = NULL;
+
+ return ntstatus;
+}
+
+NTSTATUS
+NwNdsOpenGenericHandle(
+ IN PUNICODE_STRING puNdsTree,
+ OUT LPDWORD lpdwHandleType,
+ OUT PHANDLE phNwRdrHandle
+) {
+
+ NTSTATUS ntstatus, OpenStatus;
+ IO_STATUS_BLOCK IoStatusBlock;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ ACCESS_MASK DesiredAccess = SYNCHRONIZE | FILE_LIST_DIRECTORY;
+
+ WCHAR DevicePreamble[] = L"\\Device\\Nwrdr\\";
+ UINT PreambleLength = 14;
+
+ WCHAR NameStr[128];
+ UNICODE_STRING uOpenName;
+ UINT i;
+
+ PNWR_NDS_REQUEST_PACKET Rrp;
+ BYTE RrpData[1024];
+
+ //
+ // Prepare the open name.
+ //
+
+ uOpenName.MaximumLength = sizeof( NameStr );
+
+ for ( i = 0; i < PreambleLength ; i++ )
+ NameStr[i] = DevicePreamble[i];
+
+ try {
+
+ for ( i = 0 ; i < ( puNdsTree->Length / sizeof( WCHAR ) ) ; i++ ) {
+ NameStr[i + PreambleLength] = puNdsTree->Buffer[i];
+ }
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ return STATUS_INVALID_PARAMETER;
+
+ }
+
+ uOpenName.Length = ( i * sizeof( WCHAR ) ) +
+ ( PreambleLength * sizeof( WCHAR ) );
+ uOpenName.Buffer = 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_SUCCESS(ntstatus) )
+ return ntstatus;
+
+ OpenStatus = IoStatusBlock.Status;
+
+ //
+ // Verify that this is a tree handle, not a server handle.
+ //
+
+ Rrp = (PNWR_NDS_REQUEST_PACKET)RrpData;
+
+ Rrp->Version = 0;
+
+ RtlCopyMemory( &(Rrp->Parameters).VerifyTree,
+ puNdsTree,
+ sizeof( UNICODE_STRING ) );
+
+ RtlCopyMemory( (BYTE *)(&(Rrp->Parameters).VerifyTree) + sizeof( UNICODE_STRING ),
+ puNdsTree->Buffer,
+ puNdsTree->Length );
+
+ try {
+
+ ntstatus = NtFsControlFile( *phNwRdrHandle,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_NDS_VERIFY_TREE,
+ (PVOID) Rrp,
+ sizeof( NWR_NDS_REQUEST_PACKET ),
+ NULL,
+ 0 );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+ ntstatus = GetExceptionCode();
+ goto CloseAndExit2;
+ }
+
+ if ( !NT_SUCCESS( ntstatus ))
+ {
+ *lpdwHandleType = HANDLE_TYPE_NCP_SERVER;
+ }
+ else
+ {
+ *lpdwHandleType = HANDLE_TYPE_NDS_TREE;
+ }
+
+ return OpenStatus;
+
+CloseAndExit2:
+
+ NtClose( *phNwRdrHandle );
+ *phNwRdrHandle = NULL;
+
+ return ntstatus;
+}
+
+NTSTATUS
+NwOpenHandleWithSupplementalCredentials(
+ IN PUNICODE_STRING puResourceName,
+ IN PUNICODE_STRING puUserName,
+ IN PUNICODE_STRING puPassword,
+ OUT LPDWORD lpdwHandleType,
+ OUT PHANDLE phNwHandle
+) {
+
+ NTSTATUS ntstatus, OpenStatus;
+ IO_STATUS_BLOCK IoStatusBlock;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ ACCESS_MASK DesiredAccess = FILE_GENERIC_READ | FILE_GENERIC_WRITE;
+
+ WCHAR DevicePreamble[] = L"\\Device\\Nwrdr\\";
+ UINT PreambleLength = 14;
+
+ WCHAR NameStr[128];
+ UNICODE_STRING uOpenName;
+ UINT i;
+
+ PFILE_FULL_EA_INFORMATION pEaEntry;
+ PBYTE EaBuffer;
+ ULONG EaLength, EaNameLength, EaTotalLength;
+ ULONG UserNameLen, PasswordLen, TypeLen, CredLen;
+
+ PNWR_NDS_REQUEST_PACKET Rrp;
+ BYTE RrpData[1024];
+
+ //
+ // Prepare the open name.
+ //
+
+ uOpenName.MaximumLength = sizeof( NameStr );
+
+ for ( i = 0; i < PreambleLength ; i++ )
+ NameStr[i] = DevicePreamble[i];
+
+ try {
+
+ for ( i = 0 ; i < ( puResourceName->Length / sizeof( WCHAR ) ) ; i++ ) {
+ NameStr[i + PreambleLength] = puResourceName->Buffer[i];
+ }
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ return STATUS_INVALID_PARAMETER;
+
+ }
+
+ uOpenName.Length = ( i * sizeof( WCHAR ) ) +
+ ( PreambleLength * sizeof( WCHAR ) );
+ uOpenName.Buffer = NameStr;
+
+ //
+ // Set up the object attributes.
+ //
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &uOpenName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL );
+
+ //
+ // Allocate the EA buffer - be a little generous.
+ //
+
+ UserNameLen = strlen( EA_NAME_USERNAME );
+ PasswordLen = strlen( EA_NAME_PASSWORD );
+ TypeLen = strlen( EA_NAME_TYPE );
+ CredLen = strlen( EA_NAME_CREDENTIAL_EX );
+
+ EaLength = 4 * sizeof( FILE_FULL_EA_INFORMATION );
+ EaLength += 4 * sizeof( ULONG );
+ EaLength += ROUNDUP4( UserNameLen );
+ EaLength += ROUNDUP4( PasswordLen );
+ EaLength += ROUNDUP4( TypeLen );
+ EaLength += ROUNDUP4( CredLen );
+ EaLength += ROUNDUP4( puUserName->Length );
+ EaLength += ROUNDUP4( puPassword->Length );
+
+ EaBuffer = LocalAlloc( LMEM_ZEROINIT, EaLength );
+
+ if ( !EaBuffer ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // Pack in the first EA: UserName.
+ //
+
+ pEaEntry = (PFILE_FULL_EA_INFORMATION) EaBuffer;
+
+ EaNameLength = UserNameLen + sizeof( CHAR );
+
+ pEaEntry->EaNameLength = (UCHAR) EaNameLength;
+ pEaEntry->EaValueLength = puUserName->Length;
+
+ RtlCopyMemory( &(pEaEntry->EaName[0]),
+ EA_NAME_USERNAME,
+ EaNameLength );
+
+ EaNameLength = ROUNDUP2( EaNameLength + sizeof( CHAR ) );
+
+ RtlCopyMemory( &(pEaEntry->EaName[EaNameLength]),
+ puUserName->Buffer,
+ puUserName->Length );
+
+ EaLength = ( 2 * sizeof( DWORD ) ) +
+ EaNameLength +
+ puUserName->Length;
+
+ EaLength = ROUNDUP4( EaLength );
+
+ EaTotalLength = EaLength;
+
+ pEaEntry->NextEntryOffset = EaLength;
+
+ //
+ // Pack in the second EA: Password.
+ //
+
+ pEaEntry = (PFILE_FULL_EA_INFORMATION)
+ ( ( (PBYTE)pEaEntry ) + pEaEntry->NextEntryOffset );
+
+ EaNameLength = PasswordLen + sizeof( CHAR );
+
+ pEaEntry->EaNameLength = (UCHAR) EaNameLength;
+ pEaEntry->EaValueLength = puPassword->Length;
+
+ RtlCopyMemory( &(pEaEntry->EaName[0]),
+ EA_NAME_PASSWORD,
+ EaNameLength );
+
+ EaNameLength = ROUNDUP2( EaNameLength + sizeof( CHAR ) );
+
+ RtlCopyMemory( &(pEaEntry->EaName[EaNameLength]),
+ puPassword->Buffer,
+ puPassword->Length );
+
+ EaLength = ( 2 * sizeof( DWORD ) ) +
+ EaNameLength +
+ puPassword->Length;
+
+ EaLength = ROUNDUP4( EaLength );
+
+ EaTotalLength += EaLength;
+
+ pEaEntry->NextEntryOffset = EaLength;
+
+ //
+ // Pack in the third EA: Type.
+ //
+
+ pEaEntry = (PFILE_FULL_EA_INFORMATION)
+ ( ( (PBYTE)pEaEntry ) + pEaEntry->NextEntryOffset );
+
+ EaNameLength = TypeLen + sizeof( CHAR );
+
+ pEaEntry->EaNameLength = (UCHAR) EaNameLength;
+ pEaEntry->EaValueLength = sizeof( ULONG );
+
+ RtlCopyMemory( &(pEaEntry->EaName[0]),
+ EA_NAME_TYPE,
+ EaNameLength );
+
+ EaNameLength = ROUNDUP2( EaNameLength + sizeof( CHAR ) );
+
+ EaLength = ( 2 * sizeof( DWORD ) ) +
+ EaNameLength +
+ sizeof( ULONG );
+
+ EaLength = ROUNDUP4( EaLength );
+
+ EaTotalLength += EaLength;
+
+ pEaEntry->NextEntryOffset = EaLength;
+
+ //
+ // Pack in the fourth EA: the CredentialEx flag.
+ //
+
+ pEaEntry = (PFILE_FULL_EA_INFORMATION)
+ ( ( (PBYTE)pEaEntry ) + pEaEntry->NextEntryOffset );
+
+ EaNameLength = CredLen + sizeof( CHAR );
+
+ pEaEntry->EaNameLength = (UCHAR) EaNameLength;
+ pEaEntry->EaValueLength = sizeof( ULONG );
+
+ RtlCopyMemory( &(pEaEntry->EaName[0]),
+ EA_NAME_CREDENTIAL_EX,
+ EaNameLength );
+
+ EaNameLength = ROUNDUP2( EaNameLength + sizeof( CHAR ) );
+
+ EaLength = ( 2 * sizeof( DWORD ) ) +
+ EaNameLength +
+ sizeof( ULONG );
+
+ EaLength = ROUNDUP4( EaLength );
+ EaTotalLength += EaLength;
+
+ pEaEntry->NextEntryOffset = 0;
+
+ //
+ // Do the open.
+ //
+
+ ntstatus = NtCreateFile( phNwHandle, // File handle (OUT)
+ DesiredAccess, // Access mask
+ &ObjectAttributes, // Object attributes
+ &IoStatusBlock, // Io status
+ NULL, // Optional Allocation size
+ FILE_ATTRIBUTE_NORMAL, // File attributes
+ FILE_SHARE_VALID_FLAGS, // File share access
+ FILE_OPEN, // Create disposition
+ 0, // Create options
+ (PVOID) EaBuffer, // Our EA buffer
+ EaTotalLength ); // Ea buffer length
+
+ LocalFree( EaBuffer );
+
+ if ( !NT_SUCCESS(ntstatus) )
+ return ntstatus;
+
+ OpenStatus = IoStatusBlock.Status;
+
+ //
+ // Check the handle type.
+ //
+
+ Rrp = (PNWR_NDS_REQUEST_PACKET)RrpData;
+
+ Rrp->Version = 0;
+
+ RtlCopyMemory( &(Rrp->Parameters).VerifyTree,
+ puResourceName,
+ sizeof( UNICODE_STRING ) );
+
+ RtlCopyMemory( (BYTE *)(&(Rrp->Parameters).VerifyTree) + sizeof( UNICODE_STRING ),
+ puResourceName->Buffer,
+ puResourceName->Length );
+
+ try {
+
+ ntstatus = NtFsControlFile( *phNwHandle,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_NDS_VERIFY_TREE,
+ (PVOID) Rrp,
+ sizeof( NWR_NDS_REQUEST_PACKET ),
+ NULL,
+ 0 );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+ ntstatus = GetExceptionCode();
+ goto CloseAndExit2;
+ }
+
+ if ( !NT_SUCCESS( ntstatus ))
+ {
+ *lpdwHandleType = HANDLE_TYPE_NCP_SERVER;
+ }
+ else
+ {
+ *lpdwHandleType = HANDLE_TYPE_NDS_TREE;
+ }
+
+ return OpenStatus;
+
+CloseAndExit2:
+
+ NtClose( *phNwHandle );
+ *phNwHandle = NULL;
+
+ return ntstatus;
+}
+
+NTSTATUS
+NwNdsSetTreeContext (
+ IN HANDLE hNdsRdr,
+ IN PUNICODE_STRING puTree,
+ IN PUNICODE_STRING puContext
+)
+/*+++
+
+ This sets the current context in the requested tree.
+
+---*/
+{
+
+ NTSTATUS ntstatus;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ PNWR_NDS_REQUEST_PACKET Rrp;
+ DWORD RrpSize;
+ BYTE *CurrentString;
+
+ //
+ // Set up the request.
+ //
+
+ RrpSize = sizeof( NWR_NDS_REQUEST_PACKET ) +
+ puTree->Length +
+ puContext->Length;
+
+ Rrp = LocalAlloc( LMEM_ZEROINIT, RrpSize );
+
+ if ( !Rrp ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ try {
+
+ (Rrp->Parameters).SetContext.TreeNameLen = puTree->Length;
+ (Rrp->Parameters).SetContext.ContextLen = puContext->Length;
+
+ CurrentString = (BYTE *)(Rrp->Parameters).SetContext.TreeAndContextString;
+
+ RtlCopyMemory( CurrentString, puTree->Buffer, puTree->Length );
+ CurrentString += puTree->Length;
+ RtlCopyMemory( CurrentString, puContext->Buffer, puContext->Length );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ ntstatus = STATUS_INVALID_PARAMETER;
+ goto ExitWithCleanup;
+ }
+
+ try {
+
+ ntstatus = NtFsControlFile( hNdsRdr,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_NDS_SETCONTEXT,
+ (PVOID) Rrp,
+ RrpSize,
+ NULL,
+ 0 );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ ntstatus = GetExceptionCode();
+ goto ExitWithCleanup;
+ }
+
+ExitWithCleanup:
+
+ LocalFree( Rrp );
+ return ntstatus;
+}
+
+NTSTATUS
+NwNdsGetTreeContext (
+ IN HANDLE hNdsRdr,
+ IN PUNICODE_STRING puTree,
+ OUT PUNICODE_STRING puContext
+)
+/*+++
+
+ This gets the current context of the requested tree.
+
+---*/
+{
+
+ NTSTATUS ntstatus;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ PNWR_NDS_REQUEST_PACKET Rrp;
+ DWORD RrpSize;
+
+ //
+ // Set up the request.
+ //
+
+ RrpSize = sizeof( NWR_NDS_REQUEST_PACKET ) + puTree->Length;
+
+ Rrp = LocalAlloc( LMEM_ZEROINIT, RrpSize );
+
+ if ( !Rrp ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ try {
+
+ (Rrp->Parameters).GetContext.TreeNameLen = puTree->Length;
+
+ RtlCopyMemory( (BYTE *)(Rrp->Parameters).GetContext.TreeNameString,
+ puTree->Buffer,
+ puTree->Length );
+
+ (Rrp->Parameters).GetContext.Context.MaximumLength = puContext->MaximumLength;
+ (Rrp->Parameters).GetContext.Context.Length = 0;
+ (Rrp->Parameters).GetContext.Context.Buffer = puContext->Buffer;
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ ntstatus = STATUS_INVALID_PARAMETER;
+ goto ExitWithCleanup;
+ }
+
+ try {
+
+ ntstatus = NtFsControlFile( hNdsRdr,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_NDS_GETCONTEXT,
+ (PVOID) Rrp,
+ RrpSize,
+ NULL,
+ 0 );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ ntstatus = GetExceptionCode();
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Copy out the length; the buffer has already been written.
+ //
+
+ puContext->Length = (Rrp->Parameters).GetContext.Context.Length;
+
+ExitWithCleanup:
+
+ LocalFree( Rrp );
+ return ntstatus;
+}
+
+NTSTATUS
+NwNdsResolveName (
+ IN HANDLE hNdsTree,
+ IN PUNICODE_STRING puObjectName,
+ OUT DWORD *dwObjectId,
+ OUT PUNICODE_STRING puReferredServer,
+ OUT PBYTE pbRawResponse,
+ IN DWORD dwResponseBufferLen
+) {
+
+ //
+ // BUGBUG: Do I want or need to expose the flags parameter?
+ //
+
+ NTSTATUS ntstatus;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ PNWR_NDS_REQUEST_PACKET Rrp;
+ PNDS_RESPONSE_RESOLVE_NAME Rsp;
+ DWORD dwRspBufferLen, dwNameLen, dwPadding;
+
+ BYTE RrpData[1024];
+ BYTE RspData[256];
+
+ Rrp = (PNWR_NDS_REQUEST_PACKET) RrpData;
+
+ RtlZeroMemory( Rrp, 1024 );
+
+ //
+ // NW NDS strings are null terminated, so we make sure we
+ // report the correct length...
+ //
+
+ dwNameLen = puObjectName->Length + sizeof( WCHAR );
+
+ Rrp->Version = 0;
+ Rrp->Parameters.ResolveName.ObjectNameLength = ROUNDUP4( dwNameLen );
+ Rrp->Parameters.ResolveName.ResolverFlags = RSLV_DEREF_ALIASES |
+ RSLV_WALK_TREE |
+ RSLV_WRITABLE;
+
+ try {
+
+ //
+ // But don't try to copy more than the user gave us.
+ //
+
+ memcpy( Rrp->Parameters.ResolveName.ObjectName,
+ puObjectName->Buffer,
+ puObjectName->Length );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Send the request to the Redirector FSD.
+ //
+
+ if ( dwResponseBufferLen != 0 &&
+ pbRawResponse != NULL ) {
+
+ Rsp = ( PNDS_RESPONSE_RESOLVE_NAME ) pbRawResponse;
+ dwRspBufferLen = dwResponseBufferLen;
+
+ } else {
+
+ Rsp = ( PNDS_RESPONSE_RESOLVE_NAME ) RspData;
+ dwRspBufferLen = 256;
+
+ }
+
+ try {
+
+ ntstatus = NtFsControlFile( hNdsTree,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_NDS_RESOLVE_NAME,
+ (PVOID) Rrp,
+ sizeof( NWR_NDS_REQUEST_PACKET ),
+ (PVOID) Rsp,
+ dwRspBufferLen );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ return GetExceptionCode();
+ }
+
+ //
+ // Dig out the object id and referred server.
+ //
+
+ if ( NT_SUCCESS( ntstatus ) ) {
+
+ try {
+
+ *dwObjectId = Rsp->EntryId;
+
+ if ( Rsp->ServerNameLength > puReferredServer->MaximumLength ) {
+
+ ntstatus = STATUS_BUFFER_TOO_SMALL;
+
+ } else {
+
+ RtlCopyMemory( puReferredServer->Buffer,
+ Rsp->ReferredServer,
+ Rsp->ServerNameLength );
+
+ puReferredServer->Length = (USHORT)Rsp->ServerNameLength;
+
+ }
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ return ntstatus;
+
+ }
+
+ }
+
+ return ntstatus;
+
+}
+
+NTSTATUS
+NwNdsList (
+ IN HANDLE hNdsTree,
+ IN DWORD dwObjectId,
+ OUT DWORD *dwIterHandle,
+ OUT BYTE *pbReplyBuf,
+ IN DWORD dwReplyBufLen
+) {
+
+ NTSTATUS Status;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ PNWR_NDS_REQUEST_PACKET Rrp;
+
+ PNDS_RESPONSE_SUBORDINATE_LIST Rsp;
+ DWORD dwRspBufferLen;
+
+ BYTE RrpData[256];
+ BYTE RspData[1024];
+
+ //
+ // BUGBUG: I should make the reply buffer small and start testing
+ // fragment assembly stuff.
+ //
+
+ Rrp = (PNWR_NDS_REQUEST_PACKET) RrpData;
+
+ Rrp->Parameters.ListSubordinates.ObjectId = dwObjectId;
+ Rrp->Parameters.ListSubordinates.IterHandle = *dwIterHandle;
+
+ if ( dwReplyBufLen != 0 &&
+ pbReplyBuf != NULL ) {
+
+ Rsp = ( PNDS_RESPONSE_SUBORDINATE_LIST ) pbReplyBuf;
+ dwRspBufferLen = dwReplyBufLen;
+
+ } else {
+
+ Rsp = ( PNDS_RESPONSE_SUBORDINATE_LIST ) RspData;
+ dwRspBufferLen = 1024;
+
+ }
+
+ try {
+
+ Status = NtFsControlFile( hNdsTree,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_NDS_LIST_SUBS,
+ (PVOID) Rrp,
+ sizeof( NWR_NDS_REQUEST_PACKET ),
+ (PVOID) Rsp,
+ dwRspBufferLen );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ return GetExceptionCode();
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+NwNdsReadObjectInfo(
+ IN HANDLE hNdsTree,
+ IN DWORD dwObjectId,
+ OUT BYTE *pbRawReply,
+ IN DWORD dwReplyBufLen
+)
+{
+
+ NTSTATUS Status;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ PNWR_NDS_REQUEST_PACKET Rrp;
+
+ PNDS_RESPONSE_GET_OBJECT_INFO Rsp;
+ DWORD dwRspBufferLen;
+
+ BYTE RrpData[256];
+ BYTE RspData[1024];
+
+ Rrp = (PNWR_NDS_REQUEST_PACKET) RrpData;
+
+ Rrp->Parameters.GetObjectInfo.ObjectId = dwObjectId;
+
+ if ( dwReplyBufLen != 0 &&
+ pbRawReply != NULL ) {
+
+ Rsp = ( PNDS_RESPONSE_GET_OBJECT_INFO ) pbRawReply;
+ dwRspBufferLen = dwReplyBufLen;
+
+ } else {
+
+ Rsp = ( PNDS_RESPONSE_GET_OBJECT_INFO ) RspData;
+ dwRspBufferLen = 1024;
+
+ }
+
+ try {
+
+ Status = NtFsControlFile( hNdsTree,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_NDS_READ_INFO,
+ (PVOID) Rrp,
+ sizeof( NWR_NDS_REQUEST_PACKET ),
+ (PVOID) Rsp,
+ dwRspBufferLen );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ return GetExceptionCode();
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+NwNdsReadAttribute (
+ IN HANDLE hNdsTree,
+ IN DWORD dwObjectId,
+ IN DWORD *dwIterHandle,
+ IN PUNICODE_STRING puAttrName,
+ OUT BYTE *pbReplyBuf,
+ IN DWORD dwReplyBufLen
+) {
+
+ NTSTATUS ntstatus;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ PNWR_NDS_REQUEST_PACKET Rrp;
+ PNDS_RESPONSE_READ_ATTRIBUTE Rsp;
+
+ DWORD dwAttributeNameLen;
+
+ BYTE RrpData[1024];
+
+ //
+ // Check the incoming buffer.
+ //
+ if ( !dwReplyBufLen ||
+ !pbReplyBuf ) {
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Set up the request.
+ //
+
+ Rrp = (PNWR_NDS_REQUEST_PACKET) RrpData;
+ RtlZeroMemory( Rrp, 1024 );
+
+ (Rrp->Parameters).ReadAttribute.ObjectId = dwObjectId;
+ (Rrp->Parameters).ReadAttribute.IterHandle = *dwIterHandle;
+
+ //
+ // Nds strings are NULL terminated; watch the size.
+ //
+
+ dwAttributeNameLen = puAttrName->Length + sizeof( WCHAR );
+
+ (Rrp->Parameters).ReadAttribute.AttributeNameLength = dwAttributeNameLen;
+
+ try {
+
+ //
+ // But don't try to copy more than the user gave us.
+ //
+
+ memcpy( (Rrp->Parameters).ReadAttribute.AttributeName,
+ puAttrName->Buffer,
+ puAttrName->Length );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Send the request to the Redirector FSD.
+ //
+
+ try {
+
+ ntstatus = NtFsControlFile( hNdsTree,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_NDS_READ_ATTR,
+ (PVOID) Rrp,
+ sizeof( NWR_NDS_REQUEST_PACKET ),
+ (PVOID) pbReplyBuf,
+ dwReplyBufLen );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ return GetExceptionCode();
+ }
+
+ //
+ // There's no buffer post processing on this one.
+ //
+
+ return ntstatus;
+
+}
+
+NTSTATUS
+NwNdsOpenStream (
+ IN HANDLE hNdsTree,
+ IN DWORD dwObjectId,
+ IN PUNICODE_STRING puStreamName,
+ IN DWORD dwOpenFlags,
+ OUT DWORD *pdwFileLength
+) {
+
+ NTSTATUS ntstatus;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ PNWR_NDS_REQUEST_PACKET Rrp;
+ BYTE RrpData[1024];
+
+ //
+ // Set up the request.
+ //
+
+ Rrp = (PNWR_NDS_REQUEST_PACKET) RrpData;
+ RtlZeroMemory( Rrp, 1024 );
+
+ (Rrp->Parameters).OpenStream.StreamAccess = dwOpenFlags;
+ (Rrp->Parameters).OpenStream.ObjectOid = dwObjectId;
+
+ (Rrp->Parameters).OpenStream.StreamName.Length = puStreamName->Length;
+ (Rrp->Parameters).OpenStream.StreamName.MaximumLength =
+ sizeof( RrpData ) - sizeof( (Rrp->Parameters).OpenStream );
+ (Rrp->Parameters).OpenStream.StreamName.Buffer =
+ (Rrp->Parameters).OpenStream.StreamNameString;
+
+ //
+ // Make sure we're not trashing memory.
+ //
+
+ if ( (Rrp->Parameters).OpenStream.StreamName.Length >
+ (Rrp->Parameters).OpenStream.StreamName.MaximumLength ) {
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ try {
+
+ //
+ // But don't try to copy more than the user gave us.
+ //
+
+ memcpy( (Rrp->Parameters).OpenStream.StreamNameString,
+ puStreamName->Buffer,
+ puStreamName->Length );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Send the request to the Redirector FSD.
+ //
+
+ try {
+
+ ntstatus = NtFsControlFile( hNdsTree,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_NDS_OPEN_STREAM,
+ (PVOID) Rrp,
+ sizeof( NWR_NDS_REQUEST_PACKET ),
+ NULL,
+ 0 );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ return GetExceptionCode();
+ }
+
+ if ( pdwFileLength ) {
+ *pdwFileLength = (Rrp->Parameters).OpenStream.FileLength;
+ }
+
+ return ntstatus;
+}
+
+NTSTATUS
+NwNdsGetQueueInformation(
+ IN HANDLE hNdsTree,
+ IN PUNICODE_STRING puQueueName,
+ OUT PUNICODE_STRING puHostServer,
+ OUT PDWORD pdwQueueId
+) {
+
+ NTSTATUS ntstatus;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ PNWR_NDS_REQUEST_PACKET Rrp;
+ BYTE RrpData[1024];
+
+ //
+ // Set up the request.
+ //
+
+ Rrp = (PNWR_NDS_REQUEST_PACKET) RrpData;
+ RtlZeroMemory( Rrp, sizeof( RrpData ) );
+
+ if ( puQueueName ) {
+ (Rrp->Parameters).GetQueueInfo.QueueName.Length = puQueueName->Length;
+ (Rrp->Parameters).GetQueueInfo.QueueName.MaximumLength = puQueueName->MaximumLength;
+ (Rrp->Parameters).GetQueueInfo.QueueName.Buffer = puQueueName->Buffer;
+ }
+
+ if ( puHostServer ) {
+ (Rrp->Parameters).GetQueueInfo.HostServer.Length = 0;
+ (Rrp->Parameters).GetQueueInfo.HostServer.MaximumLength = puHostServer->MaximumLength;
+ (Rrp->Parameters).GetQueueInfo.HostServer.Buffer = puHostServer->Buffer;
+ }
+
+ //
+ // Send the request to the Redirector FSD.
+ //
+
+ try {
+
+ ntstatus = NtFsControlFile( hNdsTree,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_NDS_GET_QUEUE_INFO,
+ (PVOID) Rrp,
+ sizeof( NWR_NDS_REQUEST_PACKET ),
+ NULL,
+ 0 );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ return GetExceptionCode();
+ }
+
+ if ( NT_SUCCESS( ntstatus ) ) {
+
+ if ( pdwQueueId ) {
+ *pdwQueueId = (Rrp->Parameters).GetQueueInfo.QueueId;
+ }
+
+ puHostServer->Length = (Rrp->Parameters).GetQueueInfo.HostServer.Length;
+
+ }
+
+ return ntstatus;
+}
+
+NTSTATUS
+NwNdsGetVolumeInformation(
+ IN HANDLE hNdsTree,
+ IN PUNICODE_STRING puVolumeName,
+ OUT PUNICODE_STRING puHostServer,
+ OUT PUNICODE_STRING puHostVolume
+) {
+
+ NTSTATUS ntstatus;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ PNWR_NDS_REQUEST_PACKET Rrp;
+ DWORD RequestSize;
+ BYTE RrpData[1024];
+ BYTE ReplyData[1024];
+ PBYTE NameStr;
+
+ //
+ // Set up the request.
+ //
+
+ Rrp = (PNWR_NDS_REQUEST_PACKET) RrpData;
+ RtlZeroMemory( Rrp, sizeof( RrpData ) );
+
+ if ( !puVolumeName ||
+ puVolumeName->Length > MAX_NDS_NAME_SIZE ||
+ !puHostServer ||
+ !puHostVolume ) {
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ try {
+
+ (Rrp->Parameters).GetVolumeInfo.VolumeNameLen = puVolumeName->Length;
+ RtlCopyMemory( &((Rrp->Parameters).GetVolumeInfo.VolumeName[0]),
+ puVolumeName->Buffer,
+ puVolumeName->Length );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Send the request to the Redirector FSD.
+ //
+
+ RequestSize = sizeof( NWR_NDS_REQUEST_PACKET ) +
+ (Rrp->Parameters).GetVolumeInfo.VolumeNameLen;
+
+ try {
+
+ ntstatus = NtFsControlFile( hNdsTree,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_NDS_GET_VOLUME_INFO,
+ (PVOID) Rrp,
+ RequestSize,
+ ReplyData,
+ sizeof( ReplyData ) );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ return GetExceptionCode();
+ }
+
+ if ( NT_SUCCESS( ntstatus ) ) {
+
+ try {
+
+ if ( ( puHostServer->MaximumLength < (Rrp->Parameters).GetVolumeInfo.ServerNameLen ) ||
+ ( puHostVolume->MaximumLength < (Rrp->Parameters).GetVolumeInfo.TargetVolNameLen ) ) {
+
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ puHostServer->Length = (USHORT)(Rrp->Parameters).GetVolumeInfo.ServerNameLen;
+ puHostVolume->Length = (USHORT)(Rrp->Parameters).GetVolumeInfo.TargetVolNameLen;
+
+ NameStr = &ReplyData[0];
+
+ RtlCopyMemory( puHostServer->Buffer, NameStr, puHostServer->Length );
+ NameStr += puHostServer->Length;
+ RtlCopyMemory( puHostVolume->Buffer, NameStr, puHostVolume->Length );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ }
+
+ return ntstatus;
+
+}
+
+//
+// User mode fragment exchange.
+//
+
+int
+_cdecl
+FormatBuf(
+ char *buf,
+ int bufLen,
+ const char *format,
+ va_list args
+);
+
+NTSTATUS
+_cdecl
+FragExWithWait(
+ IN HANDLE hNdsServer,
+ IN DWORD NdsVerb,
+ IN BYTE *pReplyBuffer,
+ IN DWORD pReplyBufferLen,
+ IN OUT DWORD *pdwReplyLen,
+ IN BYTE *NdsRequestStr,
+ ...
+)
+/*
+
+Routine Description:
+
+ Exchanges an NDS request in fragments and collects the fragments
+ of the response and writes them to the reply buffer.
+
+Routine Arguments:
+
+ hNdsServer - A handle to the server you want to talk to.
+ NdsVerb - The verb for that indicates the request.
+
+ pReplyBuffer - The reply buffer.
+ pReplyBufferLen - The length of the reply buffer.
+
+ NdsReqestStr - The format string for the arguments to this NDS request.
+ Arguments - The arguments that satisfy the NDS format string.
+
+Return Value:
+
+ NTSTATUS - Status of the exchange, but not the result code in the packet.
+
+*/
+{
+
+ NTSTATUS Status;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ PNWR_NDS_REQUEST_PACKET RawRequest;
+
+ BYTE *NdsRequestBuf;
+ DWORD NdsRequestLen;
+
+ va_list Arguments;
+
+ //
+ // Allocate a request buffer.
+ //
+
+ RawRequest = LocalAlloc( LMEM_ZEROINIT, NDS_BUFFER_SIZE );
+
+ if ( !RawRequest ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // Build the request in our local buffer. The first DWORD
+ // is the verb and the rest is the formatted request.
+ //
+
+ RawRequest->Parameters.RawRequest.NdsVerb = NdsVerb;
+ NdsRequestBuf = &RawRequest->Parameters.RawRequest.Request[0];
+
+ if ( NdsRequestStr != NULL ) {
+
+ va_start( Arguments, NdsRequestStr );
+
+ NdsRequestLen = FormatBuf( NdsRequestBuf,
+ NDS_BUFFER_SIZE - sizeof( NWR_NDS_REQUEST_PACKET ),
+ NdsRequestStr,
+ Arguments );
+
+ if ( !NdsRequestLen ) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ goto ExitWithCleanup;
+
+ }
+
+ va_end( Arguments );
+
+ } else {
+
+ NdsRequestLen = 0;
+ }
+
+ RawRequest->Parameters.RawRequest.RequestLength = NdsRequestLen;
+
+ //
+ // Pass this buffer to kernel mode via FSCTL.
+ //
+
+ try {
+
+ Status = NtFsControlFile( hNdsServer,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_NDS_RAW_FRAGEX,
+ (PVOID) RawRequest,
+ NdsRequestLen + sizeof( NWR_NDS_REQUEST_PACKET ),
+ (PVOID) pReplyBuffer,
+ pReplyBufferLen );
+
+ if ( NT_SUCCESS( Status ) ) {
+ *pdwReplyLen = RawRequest->Parameters.RawRequest.ReplyLength;
+ }
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = GetExceptionCode();
+ }
+
+ExitWithCleanup:
+
+ if ( RawRequest ) {
+ LocalFree( RawRequest );
+ }
+
+ return Status;
+
+}
+
+int
+_cdecl
+FormatBuf(
+ char *buf,
+ int bufLen,
+ const char *format,
+ va_list args
+)
+/*
+
+Routine Description:
+
+ Formats a buffer according to supplied the format string.
+
+ FormatString - Supplies an ANSI string which describes how to
+ convert from the input arguments into NCP request fields, and
+ from the NCP response fields into the output arguments.
+
+ Field types, request/response:
+
+ 'b' byte ( byte / byte* )
+ 'w' hi-lo word ( word / word* )
+ 'd' hi-lo dword ( dword / dword* )
+ 'W' lo-hi word ( word / word*)
+ 'D' lo-hi dword ( dword / dword*)
+ '-' zero/skip byte ( void )
+ '=' zero/skip word ( void )
+ ._. zero/skip string ( word )
+ 'p' pstring ( char* )
+ 'c' cstring ( char* )
+ 'C' cstring followed skip word ( char*, word )
+ 'V' sized NDS value ( byte *, dword / byte **, dword *)
+ 'S' p unicode string copy as NDS_STRING (UNICODE_STRING *)
+ 's' cstring copy as NDS_STRING (char* / char *, word)
+ 'r' raw bytes ( byte*, word )
+ 'u' p unicode string ( UNICODE_STRING * )
+ 'U' p uppercase string( UNICODE_STRING * )
+
+Routine Arguments:
+
+ char *buf - destination buffer.
+ int buflen - length of the destination buffer.
+ char *format - format string.
+ args - args to the format string.
+
+Implementation Notes:
+
+ This comes verbatim from kernel mode.
+
+*/
+{
+ ULONG ix;
+
+ NTSTATUS status;
+ const char *z = format;
+
+ //
+ // Convert the input arguments into request packet.
+ //
+
+ ix = 0;
+
+ while ( *z )
+ {
+ switch ( *z )
+ {
+ case '=':
+ buf[ix++] = 0;
+ case '-':
+ buf[ix++] = 0;
+ break;
+
+ case '_':
+ {
+ WORD l = va_arg ( args, WORD );
+ if (ix + (ULONG)l > (ULONG)bufLen)
+ {
+ goto ErrorExit;
+ }
+ while ( l-- )
+ buf[ix++] = 0;
+ break;
+ }
+
+ case 'b':
+ buf[ix++] = va_arg ( args, BYTE );
+ break;
+
+ case 'w':
+ {
+ WORD w = va_arg ( args, WORD );
+ buf[ix++] = (BYTE) (w >> 8);
+ buf[ix++] = (BYTE) (w >> 0);
+ break;
+ }
+
+ case 'd':
+ {
+ DWORD d = va_arg ( args, DWORD );
+ buf[ix++] = (BYTE) (d >> 24);
+ buf[ix++] = (BYTE) (d >> 16);
+ buf[ix++] = (BYTE) (d >> 8);
+ buf[ix++] = (BYTE) (d >> 0);
+ break;
+ }
+
+ case 'W':
+ {
+ WORD w = va_arg(args, WORD);
+ (* (WORD *)&buf[ix]) = w;
+ ix += 2;
+ break;
+ }
+
+ case 'D':
+ {
+ DWORD d = va_arg (args, DWORD);
+ (* (DWORD *)&buf[ix]) = d;
+ ix += 4;
+ break;
+ }
+
+ case 'c':
+ {
+ char* c = va_arg ( args, char* );
+ WORD l = strlen( c );
+ if (ix + (ULONG)l > (ULONG)bufLen)
+ {
+ goto ErrorExit;
+ }
+ RtlCopyMemory( &buf[ix], c, l+1 );
+ ix += l + 1;
+ break;
+ }
+
+ case 'C':
+ {
+ char* c = va_arg ( args, char* );
+ WORD l = va_arg ( args, WORD );
+ WORD len = strlen( c ) + 1;
+ if (ix + (ULONG)l > (ULONG)bufLen)
+ {
+ goto ErrorExit;
+ }
+
+ RtlCopyMemory( &buf[ix], c, len > l? l : len);
+ ix += l;
+ buf[ix-1] = 0;
+ break;
+ }
+
+ case 'p':
+ {
+ char* c = va_arg ( args, char* );
+ BYTE l = strlen( c );
+ if (ix + (ULONG)l +1 > (ULONG)bufLen)
+ {
+ goto ErrorExit;
+ }
+ buf[ix++] = l;
+ RtlCopyMemory( &buf[ix], c, l );
+ ix += l;
+ break;
+ }
+
+ case 'u':
+ {
+ PUNICODE_STRING pUString = va_arg ( args, PUNICODE_STRING );
+ OEM_STRING OemString;
+ ULONG Length;
+
+ //
+ // Calculate required string length, excluding trailing NUL.
+ //
+
+ Length = RtlUnicodeStringToOemSize( pUString ) - 1;
+ ASSERT( Length < 0x100 );
+
+ if ( ix + Length > (ULONG)bufLen ) {
+ goto ErrorExit;
+ }
+
+ buf[ix++] = (UCHAR)Length;
+ OemString.Buffer = &buf[ix];
+ OemString.MaximumLength = (USHORT)Length + 1;
+
+ status = RtlUnicodeStringToOemString( &OemString, pUString, FALSE );
+ ASSERT( NT_SUCCESS( status ));
+ ix += (USHORT)Length;
+ break;
+ }
+
+ case 'S':
+ {
+ PUNICODE_STRING pUString = va_arg (args, PUNICODE_STRING);
+ ULONG Length, rLength;
+
+ Length = pUString->Length;
+ if (ix + Length + sizeof(Length) + sizeof( WCHAR ) > (ULONG)bufLen) {
+ goto ErrorExit;
+ }
+
+ //
+ // The VLM client uses the rounded up length and it seems to
+ // make a difference! Also, don't forget that NDS strings have
+ // to be NULL terminated.
+ //
+
+ rLength = ROUNDUP4(Length + sizeof( WCHAR ));
+ *((DWORD *)&buf[ix]) = rLength;
+ ix += 4;
+ RtlCopyMemory(&buf[ix], pUString->Buffer, Length);
+ ix += Length;
+ rLength -= Length;
+ RtlFillMemory( &buf[ix], rLength, '\0' );
+ ix += rLength;
+ break;
+
+ }
+
+ case 's':
+ {
+ PUNICODE_STRING pUString = va_arg (args, PUNICODE_STRING);
+ ULONG Length, rLength;
+
+ Length = pUString->Length;
+ if (ix + Length + sizeof(Length) + sizeof( WCHAR ) > (ULONG)bufLen) {
+ // DebugTrace( 0, Dbg, "FormatBuf: case 's' request buffer too small.\n", 0 );
+ goto ErrorExit;
+ }
+
+ //
+ // Don't use the padded size here, only the NDS null terminator.
+ //
+
+ rLength = Length + sizeof( WCHAR );
+ *((DWORD *)&buf[ix]) = rLength;
+ ix += 4;
+ RtlCopyMemory(&buf[ix], pUString->Buffer, Length);
+ ix += Length;
+ rLength -= Length;
+ RtlFillMemory( &buf[ix], rLength, '\0' );
+ ix += rLength;
+ break;
+
+
+ }
+
+ case 'V':
+ {
+ // too similar to 'S' - should be combined
+ BYTE* b = va_arg ( args, BYTE* );
+ DWORD l = va_arg ( args, DWORD );
+ if ( ix + l + sizeof(DWORD) > (ULONG)
+ bufLen )
+ {
+ goto ErrorExit;
+ }
+ *((DWORD *)&buf[ix]) = l;
+ ix += sizeof(DWORD);
+ RtlCopyMemory( &buf[ix], b, l );
+ l = ROUNDUP4(l);
+ ix += l;
+ break;
+ }
+
+ case 'r':
+ {
+ BYTE* b = va_arg ( args, BYTE* );
+ WORD l = va_arg ( args, WORD );
+ if ( b == NULL || l == 0 )
+ {
+ break;
+ }
+ if ( ix + l > (ULONG)bufLen )
+ {
+ goto ErrorExit;
+ }
+ RtlCopyMemory( &buf[ix], b, l );
+ ix += l;
+ break;
+ }
+
+ default:
+
+ ;
+
+ }
+
+ if ( ix > (ULONG)bufLen )
+ {
+ goto ErrorExit;
+ }
+
+
+ z++;
+ }
+
+ return(ix);
+
+ErrorExit:
+ return 0;
+}
+
+NTSTATUS
+_cdecl
+ParseResponse(
+ PUCHAR Response,
+ ULONG ResponseLength,
+ char* FormatString,
+ ... // format specific parameters
+ )
+/*++
+
+Routine Description:
+
+ This routine parse an NCP response.
+
+ Packet types:
+
+ 'G' Generic packet ( )
+
+ Field types, request/response:
+
+ 'b' byte ( byte* )
+ 'w' hi-lo word ( word* )
+ 'x' ordered word ( word* )
+ 'd' hi-lo dword ( dword* )
+ 'e' ordered dword ( dword* )
+ '-' zero/skip byte ( void )
+ '=' zero/skip word ( void )
+ ._. zero/skip string ( word )
+ 'p' pstring ( char* )
+ 'c' cstring ( char* )
+ 'r' raw bytes ( byte*, word )
+
+ Added 3/29/95 by CoryWest:
+
+ 'W' lo-hi word ( word / word*)
+ 'D' lo-hi dword ( dword / dword*)
+ 'S' unicode string copy as NDS_STRING (UNICODE_STRING *)
+ 'T' terminal unicode string copy as NDS_STRING (UNICODE_STRING *)
+
+ 't' terminal unicode string with the nds null copied
+ as NDS_STRING (UNICODE_STRING *) (for GetUseName)
+
+Return Value:
+
+ STATUS - Success or failure, depending on the response.
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ PCHAR FormatByte;
+ va_list Arguments;
+ ULONG Length = 0;
+
+ va_start( Arguments, FormatString );
+
+ //
+ // User mode parse response handles only generic packets.
+ //
+
+ if ( *FormatString != 'G' ) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ FormatByte = FormatString + 1;
+ while ( *FormatByte ) {
+
+ switch ( *FormatByte ) {
+
+ case '-':
+ Length += 1;
+ break;
+
+ case '=':
+ Length += 2;
+ break;
+
+ case '_':
+ {
+ WORD l = va_arg ( Arguments, WORD );
+ Length += l;
+ break;
+ }
+
+ case 'b':
+ {
+ BYTE* b = va_arg ( Arguments, BYTE* );
+ *b = Response[Length++];
+ break;
+ }
+
+ case 'w':
+ {
+ BYTE* b = va_arg ( Arguments, BYTE* );
+ b[1] = Response[Length++];
+ b[0] = Response[Length++];
+ break;
+ }
+
+ case 'x':
+ {
+ WORD* w = va_arg ( Arguments, WORD* );
+ *w = *(WORD UNALIGNED *)&Response[Length];
+ Length += 2;
+ break;
+ }
+
+ case 'd':
+ {
+ BYTE* b = va_arg ( Arguments, BYTE* );
+ b[3] = Response[Length++];
+ b[2] = Response[Length++];
+ b[1] = Response[Length++];
+ b[0] = Response[Length++];
+ break;
+ }
+
+ case 'e':
+ {
+ DWORD UNALIGNED * d = va_arg ( Arguments, DWORD* );
+ *d = *(DWORD UNALIGNED *)&Response[Length];
+ Length += 4;
+ break;
+ }
+
+ case 'c':
+ {
+ char* c = va_arg ( Arguments, char* );
+ WORD l = strlen( &Response[Length] );
+ memcpy ( c, &Response[Length], l+1 );
+ Length += l+1;
+ break;
+ }
+
+ case 'p':
+ {
+ char* c = va_arg ( Arguments, char* );
+ BYTE l = Response[Length++];
+ memcpy ( c, &Response[Length], l );
+ c[l+1] = 0;
+ break;
+ }
+
+ case 'r':
+ {
+ BYTE* b = va_arg ( Arguments, BYTE* );
+ WORD l = va_arg ( Arguments, WORD );
+ RtlCopyMemory( b, &Response[Length], l );
+ Length += l;
+ break;
+ }
+
+ case 'W':
+ {
+
+ WORD *w = va_arg ( Arguments, WORD* );
+ *w = (* (WORD *)&Response[Length]);
+ Length += 2;
+ break;
+
+ }
+
+ case 'D':
+ {
+
+ DWORD *d = va_arg ( Arguments, DWORD* );
+ *d = (* (DWORD *)&Response[Length]);
+ Length += 4;
+ break;
+
+ }
+
+ case 'S':
+ {
+
+ PUNICODE_STRING pU = va_arg( Arguments, PUNICODE_STRING );
+ USHORT strl;
+
+ if (pU) {
+
+ strl = (USHORT)(* (DWORD *)&Response[Length]);
+
+ //
+ // Don't count the null terminator that is part of
+ // Novell's counted unicode string.
+ //
+
+ pU->Length = strl - sizeof( WCHAR );
+ Length += 4;
+ RtlCopyMemory( pU->Buffer, &Response[Length], pU->Length );
+ Length += ROUNDUP4(strl);
+
+ } else {
+
+ //
+ // Skip over the string since we don't want it.
+ //
+
+ Length += ROUNDUP4((* (DWORD *)&Response[Length] ));
+ Length += 4;
+ }
+
+
+ break;
+
+ }
+
+ case 's':
+ {
+
+ PUNICODE_STRING pU = va_arg( Arguments, PUNICODE_STRING );
+ USHORT strl;
+
+ if (pU) {
+
+ strl = (USHORT)(* (DWORD *)&Response[Length]);
+ pU->Length = strl;
+ Length += 4;
+ RtlCopyMemory( pU->Buffer, &Response[Length], pU->Length );
+ Length += ROUNDUP4(strl);
+
+ } else {
+
+ //
+ // Skip over the string since we don't want it.
+ //
+
+ Length += ROUNDUP4((* (DWORD *)&Response[Length] ));
+ Length += 4;
+ }
+
+
+ break;
+
+ }
+
+ case 'T':
+ {
+
+ PUNICODE_STRING pU = va_arg( Arguments, PUNICODE_STRING );
+ USHORT strl;
+
+ if (pU) {
+
+ strl = (USHORT)(* (DWORD *)&Response[Length] );
+ strl -= sizeof( WCHAR ); // Don't count the NULL from NDS.
+
+ if ( strl <= pU->MaximumLength ) {
+
+ pU->Length = strl;
+ Length += 4;
+ RtlCopyMemory( pU->Buffer, &Response[Length], pU->Length );
+
+ //
+ // No need to advance the pointers since this is
+ // specifically a termination case!
+ //
+
+ } else {
+
+ pU->Length = 0;
+ }
+
+ }
+
+ break;
+
+ }
+
+ case 't':
+ {
+
+ PUNICODE_STRING pU = va_arg( Arguments, PUNICODE_STRING );
+ USHORT strl;
+
+ if (pU) {
+
+ strl = (USHORT)(* (DWORD *)&Response[Length] );
+
+ if ( strl <= pU->MaximumLength ) {
+
+ pU->Length = strl;
+ Length += 4;
+ RtlCopyMemory( pU->Buffer, &Response[Length], pU->Length );
+
+ //
+ // No need to advance the pointers since this is
+ // specifically a termination case!
+ //
+
+ } else {
+
+ pU->Length = 0;
+
+ }
+
+ }
+
+ break;
+
+ }
+
+ }
+
+ if ( Length > ResponseLength ) {
+ return( STATUS_INVALID_PARAMETER );
+ }
+
+ FormatByte++;
+
+ }
+
+ va_end( Arguments );
+ return( Status );
+
+}
+
+NTSTATUS
+NwNdsChangePassword(
+ IN HANDLE hNwRdr,
+ IN PUNICODE_STRING puTreeName,
+ IN PUNICODE_STRING puUserName,
+ IN PUNICODE_STRING puCurrentPassword,
+ IN PUNICODE_STRING puNewPassword
+) {
+
+ NTSTATUS Status;
+ PNWR_NDS_REQUEST_PACKET pNdsRequest;
+ DWORD dwRequestLength;
+ PBYTE CurrentString;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ //
+ // Allocate the request.
+ //
+
+ dwRequestLength = sizeof( NWR_NDS_REQUEST_PACKET ) +
+ puTreeName->Length +
+ puUserName->Length +
+ puCurrentPassword->Length +
+ puNewPassword->Length;
+
+ pNdsRequest = LocalAlloc( LMEM_ZEROINIT, dwRequestLength );
+
+ if ( !pNdsRequest) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // Copy the parameters into the request buffer.
+ //
+
+ try {
+
+ (pNdsRequest->Parameters).ChangePass.NdsTreeNameLength =
+ puTreeName->Length;
+ (pNdsRequest->Parameters).ChangePass.UserNameLength =
+ puUserName->Length;
+ (pNdsRequest->Parameters).ChangePass.CurrentPasswordLength =
+ puCurrentPassword->Length;
+ (pNdsRequest->Parameters).ChangePass.NewPasswordLength =
+ puNewPassword->Length;
+
+ CurrentString = ( PBYTE ) &((pNdsRequest->Parameters).ChangePass.StringBuffer[0]);
+ RtlCopyMemory( CurrentString, puTreeName->Buffer, puTreeName->Length );
+
+ CurrentString += puTreeName->Length;
+ RtlCopyMemory( CurrentString, puUserName->Buffer, puUserName->Length );
+
+ CurrentString += puUserName->Length;
+ RtlCopyMemory( CurrentString, puCurrentPassword->Buffer, puCurrentPassword->Length );
+
+ CurrentString += puCurrentPassword->Length;
+ RtlCopyMemory( CurrentString, puNewPassword->Buffer, puNewPassword->Length );
+
+ Status = NtFsControlFile( hNwRdr,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_NDS_CHANGE_PASS,
+ (PVOID) pNdsRequest,
+ dwRequestLength,
+ NULL,
+ 0 );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ }
+
+ LocalFree( pNdsRequest );
+ return Status;
+
+}
+
+
diff --git a/private/nw/nwlib/nwapi32.c b/private/nw/nwlib/nwapi32.c
new file mode 100644
index 000000000..cab4c7d45
--- /dev/null
+++ b/private/nw/nwlib/nwapi32.c
@@ -0,0 +1,1661 @@
+/*++
+
+Copyright (C) 1993 Microsoft Corporation
+
+Module Name:
+
+ NWAPI32.C
+
+Abstract:
+
+ This module contains the NetWare(R) SDK support to routines
+ into the NetWare redirector
+
+Author:
+
+ Chris Sandys (a-chrisa) 09-Sep-1993
+
+Revision History:
+
+ Chuck Y. Chan (chuckc) 02/06/94 Moved to NWCS. Make it more NT like.
+ Chuck Y. Chan (chuckc) 02/27/94 Clear out old code.
+ Make logout work.
+ Check for error in many places.
+ Dont hard code strings.
+ Remove non compatible parameters.
+ Lotsa other cleanup.
+
+--*/
+
+
+#include "procs.h"
+#include "nwapi32.h"
+#include <stdio.h>
+
+//
+// Define structure for internal use. Our handle passed back from attach to
+// file server will be pointer to this. We keep server string around for
+// discnnecting from the server on logout. The structure is freed on detach.
+// Callers should not use this structure but treat pointer as opaque handle.
+//
+typedef struct _NWC_SERVER_INFO {
+ HANDLE hConn ;
+ UNICODE_STRING ServerString ;
+} NWC_SERVER_INFO, *PNWC_SERVER_INFO ;
+
+//
+// define define categories of errors
+//
+typedef enum _NCP_CLASS {
+ NcpClassConnect,
+ NcpClassBindery,
+ NcpClassDir
+} NCP_CLASS ;
+
+//
+// define error mapping structure
+//
+typedef struct _NTSTATUS_TO_NCP {
+ NTSTATUS NtStatus ;
+ NWCCODE NcpCode ;
+} NTSTATUS_TO_NCP, *LPNTSTATUS_TO_NCP ;
+
+//
+// Error mappings for directory errors
+//
+NTSTATUS_TO_NCP MapNcpDirErrors[] =
+{
+ {STATUS_NO_SUCH_DEVICE, VOLUME_DOES_NOT_EXIST},
+ {STATUS_INVALID_HANDLE, BAD_DIRECTORY_HANDLE},
+ {STATUS_OBJECT_PATH_NOT_FOUND, INVALID_PATH},
+ {STATUS_UNSUCCESSFUL, INVALID_PATH},
+ {STATUS_NO_MORE_ENTRIES, NO_SUCH_OBJECT},
+ {STATUS_ACCESS_DENIED, NO_OBJECT_READ_PRIVILEGE},
+ {STATUS_INSUFF_SERVER_RESOURCES, SERVER_OUT_OF_MEMORY},
+ { 0, 0 }
+} ;
+
+//
+// Error mappings for connect errors
+//
+NTSTATUS_TO_NCP MapNcpConnectErrors[] =
+{
+ {STATUS_UNSUCCESSFUL, INVALID_CONNECTION},
+ {STATUS_ACCESS_DENIED, NO_OBJECT_READ_PRIVILEGE},
+ {STATUS_NO_MORE_ENTRIES, UNKNOWN_FILE_SERVER},
+ {STATUS_INSUFF_SERVER_RESOURCES, SERVER_OUT_OF_MEMORY},
+ { 0, 0 }
+} ;
+
+//
+// Error mappings for bindery errors
+//
+NTSTATUS_TO_NCP MapNcpBinderyErrors[] =
+{
+ {STATUS_ACCESS_DENIED, NO_OBJECT_READ_PRIVILEGE},
+ {STATUS_NO_MORE_ENTRIES, NO_SUCH_OBJECT},
+ {STATUS_INVALID_PARAMETER, NO_SUCH_PROPERTY},
+ {STATUS_UNSUCCESSFUL, INVALID_CONNECTION},
+ {STATUS_INSUFF_SERVER_RESOURCES, SERVER_OUT_OF_MEMORY},
+ { 0, 0 }
+} ;
+
+
+//
+// Forwards
+//
+NTSTATUS
+NwAttachToServer(
+ LPWSTR ServerName,
+ LPHANDLE phandleServer
+ );
+
+NTSTATUS
+NwDetachFromServer(
+ HANDLE handleServer
+);
+
+DWORD
+CancelAllConnections(
+ LPWSTR pszServer
+);
+
+
+NWCCODE
+MapNtStatus(
+ const NTSTATUS ntstatus,
+ const NCP_CLASS ncpclass
+);
+
+DWORD
+SetWin32ErrorFromNtStatus(
+ NTSTATUS NtStatus
+) ;
+
+DWORD
+szToWide(
+ LPWSTR lpszW,
+ LPCSTR lpszC,
+ INT nSize
+);
+
+//
+// Static functions used internally
+//
+
+LPSTR
+NwDupStringA(
+ const LPSTR lpszA,
+ WORD length
+)
+{
+ LPSTR lpRet;
+
+ //
+ // Allocate memory
+ //
+ lpRet = LocalAlloc( LMEM_FIXED|LMEM_ZEROINIT , length );
+
+ if(lpRet == NULL) return(NULL);
+
+ //
+ // Dupulicate string
+ //
+ memcpy( (LPVOID)lpRet, (LPVOID)lpszA, length );
+
+ return(lpRet);
+}
+
+
+VOID
+MapSpecialJapaneseChars(
+ LPSTR lpszA,
+ WORD length
+)
+{
+ LCID lcid;
+//
+// Netware Japanese version The following character is replaced with another one
+// if the string is for File Name only when sendding from Client to Server.
+//
+// any char, even DBCS trailByte.
+//
+// SJIS+0xBF -> 0x10
+// SJIS+0xAE -> 0x11
+// SJIS+0xAA -> 0x12
+//
+// DBCS TrailByte only.
+//
+// SJIS+0x5C -> 0x13
+//
+
+// Get system locale and language ID in Kernel mode in order to
+// distinguish the currently running system.
+
+ NtQueryDefaultLocale( TRUE, &lcid );
+
+ if (! (PRIMARYLANGID(lcid) == LANG_JAPANESE ||
+ PRIMARYLANGID(lcid) == LANG_KOREAN ||
+ PRIMARYLANGID(lcid) == LANG_CHINESE) ) {
+
+ return;
+ }
+
+ if(lpszA == NULL)
+ return;
+
+ if( PRIMARYLANGID(lcid) == LANG_JAPANESE ) {
+
+ while( length ) {
+
+ if( IsDBCSLeadByte(*lpszA) ) {
+
+ //
+ // This is a DBCS character, check trailbyte is 0x5C or not.
+ //
+
+ lpszA++;
+ length--;
+ if( *lpszA == 0x5C ) {
+ *lpszA = (UCHAR)0x13;
+ }
+
+ }
+
+ switch( (UCHAR) *lpszA ) {
+ case 0xBF :
+ *lpszA = (UCHAR)0x10;
+ break;
+ case 0xAE :
+ *lpszA = (UCHAR)0x11;
+ break;
+ case 0xAA :
+ *lpszA = (UCHAR)0x12;
+ break;
+ }
+
+ //
+ // next char
+ //
+ lpszA++;
+ length--;
+ }
+ }
+ else if (PRIMARYLANGID(lcid) == LANG_CHINESE ||
+ PRIMARYLANGID(lcid) == LANG_KOREAN) {
+
+ while( length ) {
+ if( IsDBCSLeadByte(*lpszA) && *(lpszA+1) == 0x5C ) {
+ *(lpszA+1) = (UCHAR)0x13;
+ }
+
+ switch( (UCHAR) *lpszA ) {
+
+ case 0xBF :
+ *lpszA = (UCHAR)0x10;
+ break;
+
+ case 0xAE :
+ *lpszA = (UCHAR)0x11;
+ break;
+
+ case 0xAA :
+ *lpszA = (UCHAR)0x12;
+ break;
+ }
+
+ //
+ // next char
+ //
+ lpszA++;
+ length--;
+ }
+ }
+}
+
+VOID
+UnmapSpecialJapaneseChars(
+ LPSTR lpszA,
+ WORD length
+)
+{
+ LCID lcid;
+
+ //
+ // Get system locale and language ID in Kernel mode in order to
+ // distinguish the currently running system.
+ //
+
+ NtQueryDefaultLocale( TRUE, &lcid );
+
+ if (! (PRIMARYLANGID(lcid) == LANG_JAPANESE ||
+ PRIMARYLANGID(lcid) == LANG_KOREAN ||
+ PRIMARYLANGID(lcid) == LANG_CHINESE) ) {
+
+ return;
+ }
+
+ if (lpszA == NULL)
+ return;
+
+ if( PRIMARYLANGID(lcid) == LANG_JAPANESE ) {
+ while( length ) {
+ if( IsDBCSLeadByte(*lpszA) ) {
+ //
+ // This is a DBCS character, check trailbyte is 0x5C or not.
+ //
+ lpszA++;
+ length--;
+ if( *lpszA == 0x13 ) {
+ *lpszA = (UCHAR)0x5C;
+ }
+ }
+
+ switch( (UCHAR) *lpszA ) {
+ case 0x10 :
+ *lpszA = (UCHAR)0xBF;
+ break;
+ case 0x11 :
+ *lpszA = (UCHAR)0xAE;
+ break;
+ case 0x12 :
+ *lpszA = (UCHAR)0xAA;
+ break;
+ }
+ //
+ // next char
+ //
+ lpszA++;
+ length--;
+ }
+ }
+ else if (PRIMARYLANGID(lcid) == LANG_CHINESE ||
+ PRIMARYLANGID(lcid) == LANG_KOREAN) {
+
+ while( length ) {
+ switch( (UCHAR) *lpszA ) {
+ case 0x10 :
+ *lpszA = (UCHAR)0xBF;
+ break;
+ case 0x11 :
+ *lpszA = (UCHAR)0xAE;
+ break;
+ case 0x12 :
+ *lpszA = (UCHAR)0xAA;
+ break;
+ }
+ // have to check after restoring leadbyte values
+ if( IsDBCSLeadByte(*lpszA) && *(lpszA+1) == 0x13 ) {
+ *(lpszA+1) = (UCHAR)0x5C;
+ }
+ //
+ // next char
+ //
+ lpszA++;
+ length--;
+ }
+ }
+}
+
+DWORD
+szToWide(
+ LPWSTR lpszW,
+ LPCSTR lpszC,
+ INT nSize
+ )
+{
+ if (!MultiByteToWideChar(CP_ACP,
+ MB_PRECOMPOSED,
+ lpszC,
+ -1,
+ lpszW,
+ nSize))
+ {
+ return (GetLastError()) ;
+ }
+
+ return NO_ERROR ;
+}
+
+
+NWCCODE
+MapNtStatus(
+ const NTSTATUS ntstatus,
+ const NCP_CLASS ncpclass
+ )
+{
+ LPNTSTATUS_TO_NCP pErrorMap ;
+
+ if (ntstatus == STATUS_SUCCESS)
+ return SUCCESSFUL ;
+
+ switch ( ncpclass ) {
+ case NcpClassBindery:
+ pErrorMap = MapNcpBinderyErrors ;
+ break ;
+ case NcpClassDir:
+ pErrorMap = MapNcpDirErrors ;
+ break ;
+ case NcpClassConnect:
+ pErrorMap = MapNcpConnectErrors ;
+ break ;
+ default:
+ return 0xFFFF ;
+ }
+
+ while (pErrorMap->NtStatus)
+ {
+ if (pErrorMap->NtStatus == ntstatus)
+ return (pErrorMap->NcpCode) ;
+
+ pErrorMap++ ;
+ }
+
+ return 0xFFFF ;
+}
+
+DWORD
+SetWin32ErrorFromNtStatus(
+ NTSTATUS NtStatus
+)
+{
+ DWORD Status ;
+
+ if (NtStatus & 0xC0010000) { // netware specific
+
+ Status = ERROR_EXTENDED_ERROR ;
+
+ } else if (NtStatus == NWRDR_PASSWORD_HAS_EXPIRED) {
+
+ Status = 0 ; // note this is not an error (the operation suceeded!)
+
+ } else {
+
+ Status = RtlNtStatusToDosError(NtStatus) ;
+
+ }
+
+ SetLastError(Status) ;
+
+ return Status ;
+}
+
+//
+// FormatString - Supplies an ANSI string which describes how to
+// convert from the input arguments into NCP request fields, and
+// from the NCP response fields into the output arguments.
+//
+// Field types, request/response:
+//
+// 'b' byte ( byte / byte* )
+// 'w' hi-lo word ( word / word* )
+// 'd' hi-lo dword ( dword / dword* )
+// '-' zero/skip byte ( void )
+// '=' zero/skip word ( void )
+// ._. zero/skip string ( word )
+// 'p' pstring ( char* )
+// 'P' DBCS pstring ( char* )
+// 'c' cstring ( char* )
+// 'C' cstring followed skip word ( char*, word )
+// 'r' raw bytes ( byte*, word )
+// 'R' DBCS raw bytes ( byte*, word )
+// 'u' p unicode string ( UNICODE_STRING * )
+// 'U' p uppercase string( UNICODE_STRING * )
+// 'W' word n followed by an array of word[n] ( word, word* )
+//
+//
+//
+//
+// Standard NCP Function Block
+//
+//
+// NWCCODE NWAPI DLLEXPORT
+// NW***(
+// NWCONN_HANDLE hConn,
+// )
+// {
+// NWCCODE NcpCode;
+// NTSTATUS NtStatus;
+//
+// NtStatus = NwlibMakeNcp(
+// hConn, // Connection Handle
+// FSCTL_NWR_NCP_E3H, // Bindery function
+// , // Max request packet size
+// , // Max response packet size
+// "b|", // Format string
+// // === REQUEST ================================
+// 0x, // b Function
+// // === REPLY ==================================
+// );
+//
+// return MapNtStatus( NtStatus, NcpClassXXX );
+// }
+//
+//
+
+NWCCODE NWAPI DLLEXPORT
+NWAddTrusteeToDirectory(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE dirHandle,
+ const char NWFAR *pszPath,
+ NWOBJ_ID dwTrusteeID,
+ NWACCESS_RIGHTS rightsMask
+ )
+{
+ unsigned short reply;
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E2H, // Directory function
+ 265, // Max request packet size
+ 2, // Max response packet size
+ "bbrbP|", // Format string
+ // === REQUEST ================================
+ 0x0d, // b Add trustee to directory
+ dirHandle, // b 0xffffffff to start or last returned ID when enumerating HI-LO
+ &dwTrusteeID,DW_SIZE, // r Object ID to assigned to directory
+ rightsMask, // b User rights for directory
+ pszPath, // P Directory (if dirHandle = 0 then vol:directory)
+ // === REPLY ==================================
+ &reply // Not used
+ );
+
+ (void) SetWin32ErrorFromNtStatus( NtStatus );
+ return MapNtStatus( NtStatus, NcpClassDir );
+
+}
+NWCCODE NWAPI DLLEXPORT
+NWAllocPermanentDirectoryHandle(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE dirHandle,
+ char NWFAR *pszDirPath,
+ NWDIR_HANDLE NWFAR *pbNewDirHandle,
+ NWACCESS_RIGHTS NWFAR *pbRightsMask
+ )
+{
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E2H, // E2 Function function
+ 261, // Max request packet size
+ 4, // Max response packet size
+ "bbbP|bb", // Format string
+ // === REQUEST ================================
+ 0x12, // b Function Alloc Perm Dir
+ dirHandle, // b 0 for new
+ 0, // b Drive Letter
+ pszDirPath, // P Volume Name (SYS: or SYS:\PUBLIC)
+ // === REPLY ==================================
+ pbNewDirHandle, // b Dir Handle
+ pbRightsMask // b Rights
+ );
+
+ (void) SetWin32ErrorFromNtStatus( NtStatus );
+ return MapNtStatus( NtStatus, NcpClassDir );
+}
+
+NWCCODE NWAPI DLLEXPORT
+NWAllocTemporaryDirectoryHandle(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE dirHandle,
+ char NWFAR *pszDirPath,
+ NWDIR_HANDLE NWFAR *pbNewDirHandle,
+ NWACCESS_RIGHTS NWFAR *pbRightsMask
+ )
+{
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E2H, // E2 Function function
+ 261, // Max request packet size
+ 4, // Max response packet size
+ "bbbP|bb", // Format string
+ // === REQUEST ================================
+ 0x13, // b Function Alloc Temp Dir
+ dirHandle, // b 0 for new
+ 0, // b Drive Letter
+ pszDirPath, // P Volume Name (SYS: or SYS:\PUBLIC)
+ // === REPLY ==================================
+ pbNewDirHandle, // b Dir Handle
+ pbRightsMask // b Rights
+ );
+
+ (void) SetWin32ErrorFromNtStatus( NtStatus );
+ return MapNtStatus( NtStatus, NcpClassDir );
+}
+
+NWCCODE NWAPI DLLEXPORT
+NWAttachToFileServer(
+ const char NWFAR *pszServerName,
+ NWLOCAL_SCOPE ScopeFlag,
+ NWCONN_HANDLE NWFAR *phNewConn
+ )
+{
+ DWORD dwRes;
+ NWCCODE nwRes;
+ LPWSTR lpwszServerName; // Pointer to buffer for WIDE servername
+ int nSize;
+ PNWC_SERVER_INFO pServerInfo ;
+
+
+ //
+ // check parameters and init return result to be null.
+ //
+ if (!pszServerName || !phNewConn)
+ return INVALID_CONNECTION ;
+
+ *phNewConn = NULL ;
+
+ //
+ // Allocate a buffer for wide server name
+ //
+ nSize = strlen(pszServerName)+1 ;
+ if(!(lpwszServerName = (LPWSTR) LocalAlloc(
+ LPTR,
+ nSize * sizeof(WCHAR) )))
+ {
+ nwRes = REQUESTER_ERROR ;
+ goto ExitPoint ;
+ }
+ if (szToWide( lpwszServerName, pszServerName, nSize ) != NO_ERROR)
+ {
+ nwRes = REQUESTER_ERROR ;
+ goto ExitPoint ;
+ }
+
+ //
+ // Call createfile to get a handle for the redirector calls
+ //
+ nwRes = NWAttachToFileServerW( lpwszServerName,
+ ScopeFlag,
+ phNewConn );
+
+
+ExitPoint:
+
+ //
+ // Free the memory allocated above before exiting
+ //
+ if (lpwszServerName)
+ (void) LocalFree( (HLOCAL) lpwszServerName );
+
+ //
+ // Return with NWCCODE
+ //
+ return( nwRes );
+}
+
+
+NWCCODE NWAPI DLLEXPORT
+NWAttachToFileServerW(
+ const WCHAR NWFAR *pszServerName,
+ NWLOCAL_SCOPE ScopeFlag,
+ NWCONN_HANDLE NWFAR *phNewConn
+ )
+{
+ DWORD NtStatus;
+ NWCCODE nwRes;
+ LPWSTR lpwszServerName; // Pointer to buffer for WIDE servername
+ int nSize;
+ PNWC_SERVER_INFO pServerInfo ;
+
+ UNREFERENCED_PARAMETER(ScopeFlag) ;
+
+ //
+ // check parameters and init return result to be null.
+ //
+ if (!pszServerName || !phNewConn)
+ return INVALID_CONNECTION ;
+
+ *phNewConn = NULL ;
+
+ //
+ // Allocate a buffer to store the file server name
+ //
+ nSize = wcslen(pszServerName)+3 ;
+ if(!(lpwszServerName = (LPWSTR) LocalAlloc(
+ LPTR,
+ nSize * sizeof(WCHAR) )))
+ {
+ nwRes = REQUESTER_ERROR ;
+ goto ExitPoint ;
+ }
+ wcscpy( lpwszServerName, L"\\\\" );
+ wcscat( lpwszServerName, pszServerName );
+
+ //
+ // Allocate a buffer for the server info (handle + name pointer). Also
+ // init the unicode string.
+ //
+ if( !(pServerInfo = (PNWC_SERVER_INFO) LocalAlloc(
+ LPTR,
+ sizeof(NWC_SERVER_INFO))) )
+ {
+ nwRes = REQUESTER_ERROR ;
+ goto ExitPoint ;
+ }
+ RtlInitUnicodeString(&pServerInfo->ServerString, lpwszServerName) ;
+
+ //
+ // Call createfile to get a handle for the redirector calls
+ //
+ NtStatus = NwAttachToServer( lpwszServerName, &pServerInfo->hConn );
+
+ if(NT_SUCCESS(NtStatus))
+ {
+ nwRes = SUCCESSFUL;
+ }
+ else
+ {
+ (void) SetWin32ErrorFromNtStatus( NtStatus );
+ nwRes = MapNtStatus( NtStatus, NcpClassConnect );
+ }
+
+ExitPoint:
+
+ //
+ // Free the memory allocated above before exiting
+ //
+ if (nwRes != SUCCESSFUL)
+ {
+ if (lpwszServerName)
+ (void) LocalFree( (HLOCAL) lpwszServerName );
+ if (pServerInfo)
+ (void) LocalFree( (HLOCAL) pServerInfo );
+ }
+ else
+ *phNewConn = (HANDLE) pServerInfo ;
+
+ //
+ // Return with NWCCODE
+ //
+ return( nwRes );
+}
+
+NWCCODE NWAPI DLLEXPORT
+NWCheckConsolePrivileges(
+ NWCONN_HANDLE hConn
+ )
+{
+ WORD wDummy;
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 3, // Max request packet size
+ 2, // Max response packet size
+ "b|r", // Format string
+ // === REQUEST ================================
+ 0xC8, // b Get Console Privilges
+ // === REPLY ==================================
+ &wDummy,W_SIZE // r Dummy Response
+ );
+
+ (void) SetWin32ErrorFromNtStatus( NtStatus );
+ return MapNtStatus( NtStatus, NcpClassBindery );
+}
+
+NWCCODE NWAPI DLLEXPORT
+NWDeallocateDirectoryHandle(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE dirHandle
+ )
+{
+ WORD wDummy;
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E2H, // E2 Function function
+ 4, // Max request packet size
+ 2, // Max response packet size
+ "bb|w", // Format string
+ // === REQUEST ================================
+ 0x14, // b Function Dealloc Dir Hand
+ dirHandle, // b 0 for new
+ // === REPLY ==================================
+ &wDummy
+ );
+
+ (void) SetWin32ErrorFromNtStatus( NtStatus );
+ return MapNtStatus( NtStatus, NcpClassDir );
+}
+
+NWCCODE NWAPI DLLEXPORT
+NWDetachFromFileServer(
+ NWCONN_HANDLE hConn
+ )
+{
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ (void) NwDetachFromServer( pServerInfo->hConn );
+
+ (void) LocalFree (pServerInfo->ServerString.Buffer) ;
+
+ //
+ // catch any body that still trirs to use this puppy...
+ //
+ pServerInfo->ServerString.Buffer = NULL ;
+ pServerInfo->hConn = NULL ;
+
+ (void) LocalFree (pServerInfo) ;
+
+ return SUCCESSFUL;
+}
+
+NWCCODE NWAPI DLLEXPORT
+NWGetFileServerVersionInfo(
+ NWCONN_HANDLE hConn,
+ VERSION_INFO NWFAR *lpVerInfo
+ )
+{
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 3, // Max request packet size
+ 130, // Max response packet size
+ "b|r", // Format string
+ // === REQUEST ================================
+ 0x11, // b Get File Server Information
+ // === REPLY ==================================
+ lpVerInfo, // r File Version Structure
+ sizeof(VERSION_INFO)
+ );
+
+ // Convert HI-LO words to LO-HI
+ // ===========================================================
+ lpVerInfo->ConnsSupported = wSWAP( lpVerInfo->ConnsSupported );
+ lpVerInfo->connsInUse = wSWAP( lpVerInfo->connsInUse );
+ lpVerInfo->maxVolumes = wSWAP( lpVerInfo->maxVolumes );
+ lpVerInfo->PeakConns = wSWAP( lpVerInfo->PeakConns );
+
+ (void) SetWin32ErrorFromNtStatus( NtStatus );
+ return MapNtStatus( NtStatus, NcpClassBindery );
+}
+
+NWCCODE NWAPI DLLEXPORT
+NWGetInternetAddress(
+ NWCONN_HANDLE hConn,
+ NWCONN_NUM nConnNum,
+ NWNET_ADDR NWFAR *pIntAddr
+ )
+{
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 4, // Max request packet size
+ 14, // Max response packet size
+ "bb|r", // Format string
+ // === REQUEST ================================
+ 0x13, // b Get Internet Address
+ nConnNum, // b Connection Number
+ // === REPLY ==================================
+ pIntAddr,12 // r File Version Structure
+ );
+
+ (void) SetWin32ErrorFromNtStatus( NtStatus );
+ return MapNtStatus( NtStatus, NcpClassBindery );
+}
+
+
+NWCCODE NWAPI DLLEXPORT
+NWGetObjectName(
+ NWCONN_HANDLE hConn,
+ NWOBJ_ID dwObjectID,
+ char NWFAR *pszObjName,
+ NWOBJ_TYPE NWFAR *pwObjType )
+{
+ NWOBJ_ID dwRetID;
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 7, // Max request packet size
+ 56, // Max response packet size
+ "br|rrR", // Format string
+ // === REQUEST ================================
+ 0x36, // b Get Bindery Object Name
+ &dwObjectID,DW_SIZE, // r Object ID HI-LO
+ // === REPLY ==================================
+ &dwRetID,DW_SIZE, // r Object ID HI-LO
+ pwObjType,W_SIZE, // r Object Type
+ pszObjName,48 // R Object Name
+ );
+
+ (void) SetWin32ErrorFromNtStatus( NtStatus );
+ return MapNtStatus( NtStatus, NcpClassBindery );
+}
+
+// This function not supported (E3 E9)
+NWCCODE NWAPI DLLEXPORT
+NWGetVolumeInfoWithNumber(
+ NWCONN_HANDLE hConn,
+ NWVOL_NUM nVolNum,
+ char NWFAR *pszVolName,
+ NWNUMBER NWFAR *pwTotalBlocks,
+ NWNUMBER NWFAR *pwSectors,
+ NWNUMBER NWFAR *pwAvailBlocks,
+ NWNUMBER NWFAR *pwTotalDir,
+ NWNUMBER NWFAR *pwAvailDir,
+ NWVOL_FLAGS NWFAR *pfVolRemovable
+ )
+{
+ WORD wTime; // w Elapsed Time
+ BYTE bVoln; // b Vol Num
+ BYTE bDriven; // b Drive Num
+ WORD wStartBlock; // w Starting Block
+ WORD wMaxUsedDir; // w Actual Max Used Directory Entries
+ BYTE bVolHashed; // b Volume is hashed
+ BYTE bVolCached; // b Volume is Cached
+ BYTE bVolMounted; // b Volume is mounted
+
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 4, // Max request packet size
+ 42, // Max response packet size
+ "bb|wbbwwwwwwwbbbbr", // Format string
+ // === REQUEST ================================
+ 0xe9, // b Get Volume Information
+ nVolNum, // b Volume Number (0 to Max Vol)
+ // === REPLY ==================================
+ &wTime, // w Elapsed Time
+ &bVoln, // b Vol Num
+ &bDriven, // b Drive Num
+ pwSectors, // w Sectors per block
+ &wStartBlock, // w Starting Block
+ pwTotalBlocks, // w Total Blocks
+ pwAvailBlocks, // w Available Blocks (free)
+ pwTotalDir, // w Total Dir Slots
+ pwAvailDir, // w Available Directory Slots
+ &wMaxUsedDir, // w Actual Max Used Directory Entries
+ &bVolHashed, // b Volume is hashed
+ &bVolCached, // b Volume is Cached
+ pfVolRemovable, // b Volume is removable
+ &bVolMounted, // b Volume is mounted
+ pszVolName,16 // r Volume Name
+ );
+
+ (void) SetWin32ErrorFromNtStatus( NtStatus );
+ return MapNtStatus( NtStatus, NcpClassBindery );
+}
+
+NWCCODE NWAPI DLLEXPORT
+NWGetVolumeInfoWithHandle(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE nDirHand,
+ char NWFAR *pszVolName,
+ NWNUMBER NWFAR *pwTotalBlocks,
+ NWNUMBER NWFAR *pwSectors,
+ NWNUMBER NWFAR *pwAvailBlocks,
+ NWNUMBER NWFAR *pwTotalDir,
+ NWNUMBER NWFAR *pwAvailDir,
+ NWVOL_FLAGS NWFAR *pfVolRemovable
+ )
+{
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E2H, // Bindery function
+ 4, // Max request packet size
+ 30, // Max response packet size
+ "bb|wwwwwrb", // Format string
+ // === REQUEST ================================
+ 0x15, // b Get Volume Information
+ nDirHand, // b Dir Handle
+ // === REPLY ==================================
+ pwSectors, // w Sectors per block
+ pwTotalBlocks, // w Total Blocks
+ pwAvailBlocks, // w Available Blocks (free)
+ pwTotalDir, // w Total Dir Slots
+ pwAvailDir, // w Available Directory Slots
+ pszVolName,16, // r Volume Name
+ pfVolRemovable // b Volume is removable
+ );
+
+ (void) SetWin32ErrorFromNtStatus( NtStatus );
+ return MapNtStatus( NtStatus, NcpClassDir );
+}
+
+NWCCODE NWAPI DLLEXPORT
+NWGetVolumeName(
+ NWCONN_HANDLE hConn,
+ NWVOL_NUM bVolNum,
+ char NWFAR *pszVolName
+ )
+{
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E2H, // Directory Services
+ 4, // Max request packet size
+ 19, // Max response packet size
+ "bb|p", // Format string
+ // === REQUEST ================================
+ 0x06, // Get Volume Name
+ bVolNum, // Volume Number
+ // === REPLY ==================================
+ pszVolName // Return Volume name
+ );
+
+ (void) SetWin32ErrorFromNtStatus( NtStatus );
+ return MapNtStatus( NtStatus, NcpClassDir );
+}
+
+NWCCODE NWAPI DLLEXPORT
+NWIsObjectInSet(
+ NWCONN_HANDLE hConn,
+ const char NWFAR *lpszObjectName,
+ NWOBJ_TYPE wObjType,
+ const char NWFAR *lpszPropertyName,
+ const char NWFAR *lpszMemberName,
+ NWOBJ_TYPE wMemberType
+ )
+{
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+ WORD Dummy;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 122, // Max request packet size
+ 2, // Max response packet size
+ "brPPrP|", // Format string
+ // === REQUEST ================================
+ 0x43, // b Read Property Value
+ &wObjType,W_SIZE, // r OT_??? HI-LO
+ lpszObjectName, // P Object Name
+ lpszPropertyName, // P Prop Name
+ &wMemberType,W_SIZE, // r Member Type
+ lpszMemberName, // P Member Name
+ // === REPLY ==================================
+ &Dummy,W_SIZE
+ );
+
+ (void) SetWin32ErrorFromNtStatus( NtStatus );
+ return MapNtStatus( NtStatus, NcpClassBindery );
+
+} // NWIsObjectInSet
+
+NWCCODE NWAPI DLLEXPORT
+NWLoginToFileServer(
+ NWCONN_HANDLE hConn,
+ const char NWFAR *pszUserName,
+ NWOBJ_TYPE wObType,
+ const char NWFAR *pszPassword
+ )
+{
+ NETRESOURCEW NetResource;
+ DWORD dwRes, dwSize;
+ NWCCODE nwRes;
+ LPWSTR pszUserNameW = NULL,
+ pszPasswordW = NULL;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ //
+ // validate parameters
+ //
+ if (!hConn || !pszUserName || !pszPassword)
+ return INVALID_CONNECTION ;
+
+ //
+ // allocate memory for unicode strings and convert ANSI input
+ // to Unicode.
+ //
+ dwSize = strlen(pszUserName)+1 ;
+ if (!(pszUserNameW = (LPWSTR)LocalAlloc(
+ LPTR,
+ dwSize * sizeof(WCHAR))))
+ {
+ nwRes = REQUESTER_ERROR ;
+ goto ExitPoint ;
+ }
+ if (szToWide( pszUserNameW, pszUserName, dwSize ) != NO_ERROR)
+ {
+ nwRes = REQUESTER_ERROR ;
+ goto ExitPoint ;
+ }
+
+ dwSize = strlen(pszPassword)+1 ;
+ if (!(pszPasswordW = (LPWSTR)LocalAlloc(
+ LPTR,
+ dwSize * sizeof(WCHAR))))
+ {
+ nwRes = REQUESTER_ERROR ;
+ goto ExitPoint ;
+ }
+
+ if (szToWide( pszPasswordW, pszPassword, dwSize ) != NO_ERROR)
+ {
+ nwRes = REQUESTER_ERROR ;
+ goto ExitPoint ;
+ }
+
+ NetResource.dwScope = 0 ;
+ NetResource.dwUsage = 0 ;
+ NetResource.dwType = RESOURCETYPE_ANY;
+ NetResource.lpLocalName = NULL;
+ NetResource.lpRemoteName = (LPWSTR) pServerInfo->ServerString.Buffer;
+ NetResource.lpComment = NULL;
+ NetResource.lpProvider = NULL ;
+
+ //
+ // make the connection
+ //
+ dwRes=NPAddConnection ( &NetResource,
+ pszPasswordW,
+ pszUserNameW );
+
+ if( NO_ERROR != dwRes ) {
+ dwRes = GetLastError();
+ switch( dwRes ) {
+ case ERROR_SESSION_CREDENTIAL_CONFLICT:
+ nwRes = SUCCESSFUL;
+ break;
+ case ERROR_ALREADY_ASSIGNED:
+ nwRes = ALREADY_ATTACHED;
+ break;
+ case ERROR_ACCESS_DENIED:
+ case ERROR_BAD_DEV_TYPE:
+ case ERROR_BAD_DEVICE:
+ case ERROR_BAD_NET_NAME:
+ case ERROR_BAD_PROFILE:
+ case ERROR_CANNOT_OPEN_PROFILE:
+ case ERROR_DEVICE_ALREADY_REMEMBERED:
+ case ERROR_EXTENDED_ERROR:
+ case ERROR_INVALID_PASSWORD:
+ case ERROR_NO_NET_OR_BAD_PATH:
+ case ERROR_NO_NETWORK:
+ nwRes = INVALID_CONNECTION;
+ break;
+ default:
+ nwRes = INVALID_CONNECTION;
+ break;
+ }
+ } else {
+ nwRes = SUCCESSFUL;
+ }
+
+ExitPoint:
+
+ if (pszUserNameW)
+ (void) LocalFree((HLOCAL) pszUserNameW) ;
+ if (pszPasswordW)
+ (void) LocalFree((HLOCAL) pszPasswordW) ;
+
+ return( nwRes );
+}
+
+NWCCODE NWAPI DLLEXPORT
+NWLogoutFromFileServer(
+ NWCONN_HANDLE hConn
+ )
+{
+ DWORD dwRes;
+ NWCCODE nwRes;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ //
+ // cancel all explicit connections to that server
+ //
+ (void) CancelAllConnections ( pServerInfo->ServerString.Buffer );
+
+ //
+ // now cancel the any connection to \\servername.
+ //
+ dwRes=NPCancelConnection( pServerInfo->ServerString.Buffer, TRUE );
+
+ if( NO_ERROR != dwRes ) {
+ dwRes = GetLastError();
+ switch( dwRes )
+ {
+ case ERROR_NOT_CONNECTED:
+ case ERROR_INVALID_HANDLE:
+ nwRes = SUCCESSFUL;
+ break;
+
+ case ERROR_BAD_PROFILE:
+ case ERROR_CANNOT_OPEN_PROFILE:
+ case ERROR_DEVICE_IN_USE:
+ case ERROR_EXTENDED_ERROR:
+ nwRes = INVALID_CONNECTION;
+ break;
+ default:
+ nwRes = INVALID_CONNECTION;
+ break;
+ }
+ } else {
+ nwRes = SUCCESSFUL;
+ }
+
+ return( nwRes );
+}
+
+NWCCODE NWAPI DLLEXPORT
+NWReadPropertyValue(
+ NWCONN_HANDLE hConn,
+ const char NWFAR *pszObjName,
+ NWOBJ_TYPE wObjType,
+ char NWFAR *pszPropName,
+ unsigned char ucSegment,
+ char NWFAR *pValue,
+ NWFLAGS NWFAR *pucMoreFlag,
+ NWFLAGS NWFAR *pucPropFlag
+ )
+{
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 70, // Max request packet size
+ 132, // Max response packet size
+ "brPbP|rbb", // Format string
+ // === REQUEST ================================
+ 0x3D, // b Read Property Value
+ &wObjType,W_SIZE, // r Object Type HI-LO
+ pszObjName, // P Object Name
+ ucSegment, // b Segment Number
+ pszPropName, // P Property Name
+ // === REPLY ==================================
+ pValue,128, // r Property value
+ pucMoreFlag, // b More Flag
+ pucPropFlag // b Prop Flag
+ );
+
+ (void) SetWin32ErrorFromNtStatus( NtStatus );
+ return MapNtStatus( NtStatus, NcpClassBindery );
+}
+
+NWCCODE NWAPI DLLEXPORT
+NWScanObject(
+ NWCONN_HANDLE hConn,
+ const char NWFAR *pszSearchName,
+ NWOBJ_TYPE wObjSearchType,
+ NWOBJ_ID NWFAR *pdwObjectID,
+ char NWFAR *pszObjectName,
+ NWOBJ_TYPE NWFAR *pwObjType,
+ NWFLAGS NWFAR *pucHasProperties,
+ NWFLAGS NWFAR *pucObjectFlags,
+ NWFLAGS NWFAR *pucObjSecurity
+ )
+{
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 57, // Max request packet size
+ 59, // Max response packet size
+ "brrP|rrRbbb", // Format string
+ // === REQUEST ================================
+ 0x37, // b Scan bindery object
+ pdwObjectID,DW_SIZE, // r 0xffffffff to start or last returned ID when enumerating HI-LO
+ &wObjSearchType,W_SIZE, // r Use OT_??? Defines HI-LO
+ pszSearchName, // P Search Name. (use "*") for all
+ // === REPLY ==================================
+ pdwObjectID,DW_SIZE, // r Returned ID HI-LO
+ pwObjType,W_SIZE, // r rObject Type HI-LO
+ pszObjectName,48, // R Found Name
+ pucObjectFlags, // b Object Flag
+ pucObjSecurity, // b Object Security
+ pucHasProperties // b Has Properties
+ );
+
+ (void) SetWin32ErrorFromNtStatus( NtStatus );
+ return MapNtStatus( NtStatus, NcpClassBindery );
+}
+
+NWCCODE NWAPI DLLEXPORT
+NWScanProperty(
+ NWCONN_HANDLE hConn,
+ const char NWFAR *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ char NWFAR *pszSearchName,
+ NWOBJ_ID NWFAR *pdwSequence,
+ char NWFAR *pszPropName,
+ NWFLAGS NWFAR *pucPropFlags,
+ NWFLAGS NWFAR *pucPropSecurity,
+ NWFLAGS NWFAR *pucHasValue,
+ NWFLAGS NWFAR *pucMore
+ )
+{
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 73, // Max request packet size
+ 26, // Max response packet size
+ "brPrP|Rbbrbb", // Format string
+ // === REQUEST ================================
+ 0x3C, // b Scan Prop function
+ &wObjType,W_SIZE, // r Type of Object
+ pszObjectName, // P Object Name
+ pdwSequence,DW_SIZE, // r Sequence HI-LO
+ pszSearchName, // P Property Name to Search for
+ // === REPLY ==================================
+ pszPropName,16, // R Returned Property Name
+ pucPropFlags, // b Property Flags
+ pucPropSecurity, // b Property Security
+ pdwSequence,DW_SIZE, // r Sequence HI-LO
+ pucHasValue, // b Property Has value
+ pucMore // b More Properties
+ );
+
+ (void) SetWin32ErrorFromNtStatus( NtStatus );
+ return MapNtStatus( NtStatus, NcpClassBindery );
+}
+
+
+
+
+NWCCODE NWAPI DLLEXPORT
+NWGetFileServerDateAndTime(
+ NWCONN_HANDLE hConn,
+ BYTE NWFAR *year,
+ BYTE NWFAR *month,
+ BYTE NWFAR *day,
+ BYTE NWFAR *hour,
+ BYTE NWFAR *minute,
+ BYTE NWFAR *second,
+ BYTE NWFAR *dayofweek
+ )
+{
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E0H, // Server function
+ 0, // Max request packet size
+ 9, // Max response packet size
+ "|bbbbbbb", // Format string
+ // === REQUEST ================================
+ // === REPLY ==================================
+ year,
+ month,
+ day,
+ hour,
+ minute,
+ second,
+ dayofweek
+ );
+
+
+ (void) SetWin32ErrorFromNtStatus( NtStatus );
+ return MapNtStatus( NtStatus, NcpClassConnect );
+
+} // NWGetFileServerDateAndTime
+
+
+//
+// worker routines
+//
+
+#define NW_RDR_SERVER_PREFIX L"\\Device\\Nwrdr\\"
+
+NTSTATUS
+NwAttachToServer(
+ 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:
+
+ 0 or reason for failure.
+
+--*/
+{
+ NTSTATUS ntstatus = STATUS_SUCCESS;
+ 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 STATUS_INSUFFICIENT_RESOURCES ;
+ }
+
+ 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 | FILE_LIST_DIRECTORY,
+ &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 (ntstatus);
+}
+
+
+NTSTATUS
+NwDetachFromServer(
+ IN HANDLE handleServer
+ )
+/*++
+
+Routine Description:
+
+ This routine closes a handle to the given server.
+
+Arguments:
+
+ handleServer - Supplies a open handle to be closed.
+
+Return Value:
+
+ NO_ERROR or reason for failure.
+
+--*/
+{
+ NTSTATUS ntstatus = NtClose( handleServer );
+ return (ntstatus);
+}
+
+
+DWORD
+CancelAllConnections(
+ LPWSTR pszServer
+)
+/*++
+
+Routine Description:
+
+ This routine cancels all connections to a server
+
+Arguments:
+
+ pszServer - the server we are disconnecting from
+
+Return Value:
+
+ NO_ERROR or win32 error for failure.
+
+--*/
+{
+ DWORD status = ERROR_NO_NETWORK;
+ HANDLE EnumHandle = (HANDLE) NULL;
+
+ LPNETRESOURCE NetR = NULL;
+
+ DWORD BytesNeeded = 4096;
+ DWORD EntriesRead;
+ DWORD i;
+
+ //
+ // Retrieve the list of connections
+ //
+ status = NPOpenEnum(
+ RESOURCE_CONNECTED,
+ 0,
+ 0,
+ NULL,
+ &EnumHandle
+ );
+
+ if (status != NO_ERROR) {
+ EnumHandle = (HANDLE) NULL;
+ goto CleanExit;
+ }
+
+ //
+ // Allocate buffer to get connection list.
+ //
+ if ((NetR = (LPNETRESOURCE) LocalAlloc(
+ LPTR,
+ (UINT) 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)
+ {
+ LPNETRESOURCE TmpPtr = NetR;
+
+ for (i = 0; i < EntriesRead; i++, TmpPtr++)
+ {
+ LPWSTR pszTmp ;
+
+ //
+ // If it contains the server we are logging off from, we want
+ // to cancel it. First, lets extract the server name part.
+ //
+
+ pszTmp = TmpPtr->lpRemoteName ;
+
+ if (!pszTmp || !*pszTmp)
+ continue ;
+
+ if ((*pszTmp == L'\\') && (*(pszTmp+1) == L'\\'))
+ pszTmp += 2 ;
+
+ if (pszTmp = wcschr(pszTmp, L'\\'))
+ *pszTmp = 0 ;
+
+ if (_wcsicmp(TmpPtr->lpRemoteName, pszServer) == 0)
+ {
+ //
+ // Aha, it matches. Restore the '\' and nuke it with force.
+ // Ignore errors here.
+ //
+ if (pszTmp)
+ *pszTmp = L'\\' ;
+
+ if (TmpPtr->lpLocalName && *(TmpPtr->lpLocalName))
+ {
+ //
+ // if local name present, its a redirection.
+ //
+ (void) NPCancelConnection( TmpPtr->lpLocalName,TRUE );
+ }
+ else
+ {
+ //
+ // else cancel the deviceless use
+ //
+ (void) NPCancelConnection( TmpPtr->lpRemoteName,TRUE );
+ }
+ }
+ }
+
+ }
+ 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);
+
+ if ((NetR = (LPNETRESOURCE) LocalAlloc(
+ LPTR,
+ (UINT) BytesNeeded
+ )) == NULL) {
+
+ status = ERROR_NOT_ENOUGH_MEMORY;
+ goto CleanExit;
+ }
+ }
+ else
+ {
+ //
+ // cant handle other errors. bag out.
+ //
+ 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);
+ }
+
+ return status;
+}
diff --git a/private/nw/nwlib/nwcapi32.c b/private/nw/nwlib/nwcapi32.c
new file mode 100644
index 000000000..0fd0b4bb1
--- /dev/null
+++ b/private/nw/nwlib/nwcapi32.c
@@ -0,0 +1,798 @@
+/*++
+
+Copyright (C) 1993 Microsoft Corporation
+
+Module Name:
+
+ NWAPI32.C
+
+Abstract:
+
+ This module contains NetWare compatible APIs. The NWC* functions
+ are implemented as wrappers around NWP* or NW* functions.
+
+Author:
+
+ Chuck Y. Chan (ChuckC) 06-Mar-1995
+
+Revision History:
+
+ ChuckC Moved over from DSM to consolidate.
+
+--*/
+
+#include "procs.h"
+
+//
+// define define categories of errors
+//
+typedef enum _NCP_CLASS {
+ NcpClassConnect,
+ NcpClassBindery,
+ NcpClassDir
+} NCP_CLASS ;
+
+extern NWCCODE
+MapNtStatus(
+ const NTSTATUS ntstatus,
+ const NCP_CLASS ncpclass
+);
+
+extern DWORD
+SetWin32ErrorFromNtStatus(
+ NTSTATUS NtStatus
+) ;
+
+
+//
+// Function bodies
+//
+
+
+NWCCODE
+NWCAddTrusteeToDirectory(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE dirHandle,
+ const char *pszPath,
+ NWOBJ_ID dwTrusteeID,
+ NWACCESS_RIGHTS rightsMask
+ )
+{
+ return (NWAddTrusteeToDirectory(
+ hConn,
+ dirHandle,
+ pszPath,
+ dwTrusteeID,
+ rightsMask)) ;
+}
+
+NWCCODE
+NWCAllocPermanentDirectoryHandle(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE dirHandle,
+ char *pszDirPath,
+ NWDIR_HANDLE *pbNewDirHandle,
+ NWACCESS_RIGHTS *pbRightsMask
+ )
+{
+ return (NWAllocPermanentDirectoryHandle(
+ hConn,
+ dirHandle,
+ pszDirPath,
+ pbNewDirHandle,
+ pbRightsMask)) ;
+}
+
+NWCCODE
+NWCAllocTemporaryDirectoryHandle(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE dirHandle,
+ char *pszDirPath,
+ NWDIR_HANDLE *pbNewDirHandle,
+ NWACCESS_RIGHTS *pbRightsMask
+ )
+{
+ return (NWAllocTemporaryDirectoryHandle(
+ hConn,
+ dirHandle,
+ pszDirPath,
+ pbNewDirHandle,
+ pbRightsMask)) ;
+}
+
+NWCCODE
+NWCAttachToFileServer(
+ const char *pszServerName,
+ NWLOCAL_SCOPE ScopeFlag,
+ NWCONN_HANDLE *phNewConn
+ )
+{
+ return (NWAttachToFileServer(
+ pszServerName,
+ ScopeFlag,
+ phNewConn)) ;
+}
+
+NWCCODE
+NWCAttachToFileServerW(
+ const WCHAR *pszServerName,
+ NWLOCAL_SCOPE ScopeFlag,
+ NWCONN_HANDLE *phNewConn
+ )
+{
+ return (NWAttachToFileServerW(
+ pszServerName,
+ ScopeFlag,
+ phNewConn)) ;
+}
+
+NWCCODE
+NWCCheckConsolePrivileges(
+ NWCONN_HANDLE hConn
+ )
+{
+ return(NWCheckConsolePrivileges(hConn));
+}
+
+NWCCODE
+NWCDeallocateDirectoryHandle(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE dirHandle
+ )
+{
+ return(NWDeallocateDirectoryHandle(
+ hConn,
+ dirHandle)) ;
+}
+
+NWCCODE
+NWCDetachFromFileServer(
+ NWCONN_HANDLE hConn
+ )
+{
+ return(NWDetachFromFileServer(hConn)) ;
+}
+
+NWCCODE
+NWCGetFileServerVersionInfo(
+ NWCONN_HANDLE hConn,
+ VERSION_INFO *lpVerInfo
+ )
+{
+ return(NWGetFileServerVersionInfo(
+ hConn,
+ lpVerInfo)) ;
+}
+
+NWCCODE
+NWCGetInternetAddress(
+ NWCONN_HANDLE hConn,
+ NWCONN_NUM nConnNum,
+ NWNET_ADDR *pIntAddr
+ )
+{
+ return (NWGetInternetAddress(
+ hConn,
+ nConnNum,
+ pIntAddr)) ;
+}
+
+NWCCODE
+NWCGetObjectName(
+ NWCONN_HANDLE hConn,
+ NWOBJ_ID dwObjectID,
+ char *pszObjName,
+ NWOBJ_TYPE *pwObjType )
+{
+ return(NWGetObjectName(
+ hConn,
+ dwObjectID,
+ pszObjName,
+ pwObjType )) ;
+}
+
+
+NWCCODE
+NWCGetVolumeInfoWithNumber(
+ NWCONN_HANDLE hConn,
+ NWVOL_NUM nVolNum,
+ char *pszVolName,
+ NWNUMBER *pwTotalBlocks,
+ NWNUMBER *pwSectors,
+ NWNUMBER *pwAvailBlocks,
+ NWNUMBER *pwTotalDir,
+ NWNUMBER *pwAvailDir,
+ NWVOL_FLAGS *pfVolRemovable
+ )
+{
+ return(NWGetVolumeInfoWithNumber(
+ hConn,
+ nVolNum,
+ pszVolName,
+ pwTotalBlocks,
+ pwSectors,
+ pwAvailBlocks,
+ pwTotalDir,
+ pwAvailDir,
+ pfVolRemovable)) ;
+}
+
+NWCCODE
+NWCGetVolumeInfoWithHandle(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE nDirHand,
+ char *pszVolName,
+ NWNUMBER *pwTotalBlocks,
+ NWNUMBER *pwSectors,
+ NWNUMBER *pwAvailBlocks,
+ NWNUMBER *pwTotalDir,
+ NWNUMBER *pwAvailDir,
+ NWVOL_FLAGS *pfVolRemovable
+ )
+{
+ return(NWGetVolumeInfoWithHandle(
+ hConn,
+ nDirHand,
+ pszVolName,
+ pwTotalBlocks,
+ pwSectors,
+ pwAvailBlocks,
+ pwTotalDir,
+ pwAvailDir,
+ pfVolRemovable)) ;
+}
+
+NWCCODE
+NWCGetVolumeName(
+ NWCONN_HANDLE hConn,
+ NWVOL_NUM bVolNum,
+ char *pszVolName
+ )
+{
+ return(NWGetVolumeName(
+ hConn,
+ bVolNum,
+ pszVolName)) ;
+}
+
+NWCCODE
+NWCIsObjectInSet(
+ NWCONN_HANDLE hConn,
+ const char *lpszObjectName,
+ NWOBJ_TYPE wObjType,
+ const char *lpszPropertyName,
+ const char *lpszMemberName,
+ NWOBJ_TYPE wMemberType
+ )
+{
+ return(NWIsObjectInSet(
+ hConn,
+ lpszObjectName,
+ wObjType,
+ lpszPropertyName,
+ lpszMemberName,
+ wMemberType)) ;
+}
+
+
+NWCCODE
+NWCLoginToFileServer(
+ NWCONN_HANDLE hConn,
+ const char *pszUserName,
+ NWOBJ_TYPE wObjType,
+ const char *pszPassword
+ )
+{
+ return(NWLoginToFileServer(
+ hConn,
+ pszUserName,
+ wObjType,
+ pszPassword)) ;
+}
+
+NWCCODE
+NWCLogoutFromFileServer(
+ NWCONN_HANDLE hConn
+ )
+{
+ return(NWLogoutFromFileServer( hConn )) ;
+}
+
+NWCCODE
+NWCReadPropertyValue(
+ NWCONN_HANDLE hConn,
+ const char *pszObjName,
+ NWOBJ_TYPE wObjType,
+ char *pszPropName,
+ unsigned char ucSegment,
+ char *pValue,
+ NWFLAGS *pucMoreFlag,
+ NWFLAGS *pucPropFlag
+ )
+{
+ return(NWReadPropertyValue(
+ hConn,
+ pszObjName,
+ wObjType,
+ pszPropName,
+ ucSegment,
+ pValue,
+ pucMoreFlag,
+ pucPropFlag)) ;
+}
+
+NWCCODE
+NWCScanObject(
+ NWCONN_HANDLE hConn,
+ const char *pszSearchName,
+ NWOBJ_TYPE wObjSearchType,
+ NWOBJ_ID *pdwObjectID,
+ char *pszObjectName,
+ NWOBJ_TYPE *pwObjType,
+ NWFLAGS *pucHasProperties,
+ NWFLAGS *pucObjectFlags,
+ NWFLAGS *pucObjSecurity
+ )
+{
+ return(NWScanObject(
+ hConn,
+ pszSearchName,
+ wObjSearchType,
+ pdwObjectID,
+ pszObjectName,
+ pwObjType,
+ pucHasProperties,
+ pucObjectFlags,
+ pucObjSecurity)) ;
+}
+
+NWCCODE
+NWCScanProperty(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ char *pszSearchName,
+ NWOBJ_ID *pdwSequence,
+ char *pszPropName,
+ NWFLAGS *pucPropFlags,
+ NWFLAGS *pucPropSecurity,
+ NWFLAGS *pucHasValue,
+ NWFLAGS *pucMore
+ )
+{
+ return( NWScanProperty(
+ hConn,
+ pszObjectName,
+ wObjType,
+ pszSearchName,
+ pdwSequence,
+ pszPropName,
+ pucPropFlags,
+ pucPropSecurity,
+ pucHasValue,
+ pucMore)) ;
+}
+
+NWCCODE
+NWCGetFileServerDateAndTime(
+ NWCONN_HANDLE hConn,
+ BYTE *year,
+ BYTE *month,
+ BYTE *day,
+ BYTE *hour,
+ BYTE *minute,
+ BYTE *second,
+ BYTE *dayofweek
+ )
+{
+ return( NWGetFileServerDateAndTime(
+ hConn,
+ year,
+ month,
+ day,
+ hour,
+ minute,
+ second,
+ dayofweek ));
+}
+
+
+NWCCODE
+NWCAddTrustee(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE dirHandle,
+ const char *pszPath,
+ NWOBJ_ID dwTrusteeID,
+ NWRIGHTS_MASK rightsMask
+ )
+{
+ NTSTATUS NtStatus;
+
+ NtStatus = NWPAddTrustee(
+ hConn,
+ dirHandle,
+ pszPath,
+ dwTrusteeID,
+ rightsMask
+ );
+
+ (void) SetWin32ErrorFromNtStatus(NtStatus) ;
+
+ return MapNtStatus( NtStatus, NcpClassDir );
+}
+
+NWCCODE
+NWCDeleteObject(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType
+ )
+{
+ NTSTATUS NtStatus;
+
+ NtStatus = NWPDeleteObject( hConn,
+ pszObjectName,
+ wObjType );
+
+ (void) SetWin32ErrorFromNtStatus(NtStatus) ;
+
+ return MapNtStatus( NtStatus, NcpClassBindery );
+}
+
+NWCCODE
+NWCCreateObject(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ NWFLAGS ucObjectFlags,
+ NWFLAGS ucObjSecurity
+ )
+{
+ NTSTATUS NtStatus ;
+
+ NtStatus = NWPCreateObject( hConn,
+ pszObjectName,
+ wObjType,
+ ucObjectFlags,
+ ucObjSecurity );
+
+ (void) SetWin32ErrorFromNtStatus(NtStatus) ;
+
+ return MapNtStatus( NtStatus, NcpClassBindery );
+}
+
+NWCCODE
+NWCCreateProperty(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ const char *pszPropertyName,
+ NWFLAGS ucObjectFlags,
+ NWFLAGS ucObjSecurity
+ )
+{
+ NTSTATUS NtStatus ;
+
+ NtStatus = NWPCreateProperty( hConn,
+ pszObjectName,
+ wObjType,
+ pszPropertyName,
+ ucObjectFlags,
+ ucObjSecurity );
+
+ (void) SetWin32ErrorFromNtStatus(NtStatus) ;
+
+ return MapNtStatus( NtStatus, NcpClassBindery );
+}
+
+NWCCODE
+NWCDeleteProperty(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ const char *pszPropertyName
+ )
+{
+ NTSTATUS NtStatus ;
+
+ NtStatus = NWPDeleteProperty( hConn,
+ pszObjectName,
+ wObjType,
+ pszPropertyName );
+
+ (void) SetWin32ErrorFromNtStatus(NtStatus) ;
+
+ return MapNtStatus( NtStatus, NcpClassBindery );
+}
+
+NWCCODE
+NWCWritePropertyValue(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ const char *pszPropertyName,
+ NWSEGMENT_NUM segmentNumber,
+ NWSEGMENT_DATA *segmentData,
+ NWFLAGS moreSegments
+ )
+{
+ NTSTATUS NtStatus ;
+
+ NtStatus = NWPWritePropertyValue( hConn,
+ pszObjectName,
+ wObjType,
+ pszPropertyName,
+ segmentNumber,
+ segmentData,
+ moreSegments );
+
+ (void) SetWin32ErrorFromNtStatus(NtStatus) ;
+
+ return MapNtStatus( NtStatus, NcpClassBindery );
+}
+
+NWCCODE
+NWCGetObjectID(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ NWOBJ_ID *objectID
+ )
+{
+ NTSTATUS NtStatus ;
+
+ NtStatus = NWPGetObjectID( hConn,
+ pszObjectName,
+ wObjType,
+ objectID );
+
+
+ (void) SetWin32ErrorFromNtStatus(NtStatus) ;
+
+ return MapNtStatus( NtStatus, NcpClassBindery );
+}
+
+NWCCODE
+NWCRenameBinderyObject(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ const char *pszNewObjectName,
+ NWOBJ_TYPE wObjType
+ )
+{
+ NTSTATUS NtStatus;
+
+ NtStatus = NWPRenameBinderyObject(
+ hConn,
+ pszObjectName,
+ pszNewObjectName,
+ wObjType
+ );
+
+ (void) SetWin32ErrorFromNtStatus(NtStatus) ;
+
+ return MapNtStatus( NtStatus, NcpClassBindery );
+}
+
+NWCCODE
+NWCAddObjectToSet(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ const char *pszPropertyName,
+ const char *pszMemberName,
+ NWOBJ_TYPE memberType
+ )
+{
+ NTSTATUS NtStatus ;
+
+ NtStatus = NWPAddObjectToSet( hConn,
+ pszObjectName,
+ wObjType,
+ pszPropertyName,
+ pszMemberName,
+ memberType );
+
+ (void) SetWin32ErrorFromNtStatus(NtStatus) ;
+
+ return MapNtStatus( NtStatus, NcpClassBindery );
+}
+
+NWCCODE
+NWCDeleteObjectFromSet(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ const char *pszPropertyName,
+ const char *pszMemberName,
+ NWOBJ_TYPE memberType
+ )
+{
+ NTSTATUS NtStatus ;
+
+ NtStatus = NWPDeleteObjectFromSet( hConn,
+ pszObjectName,
+ wObjType,
+ pszPropertyName,
+ pszMemberName,
+ memberType );
+
+ (void) SetWin32ErrorFromNtStatus(NtStatus) ;
+
+ return MapNtStatus( NtStatus, NcpClassBindery );
+}
+
+NWCCODE
+NWCCreateDirectory(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE dirHandle,
+ const char *pszPath,
+ NWACCESS_RIGHTS accessMask
+ )
+{
+ NTSTATUS NtStatus;
+
+ NtStatus = NWPCreateDirectory(
+ hConn,
+ dirHandle,
+ pszPath,
+ accessMask
+ );
+
+ (void) SetWin32ErrorFromNtStatus(NtStatus) ;
+
+ return MapNtStatus( NtStatus, NcpClassBindery );
+}
+
+
+NWCCODE
+NWCScanForTrustees(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE dirHandle,
+ char *pszsearchDirPath,
+ NWSEQUENCE *pucsequenceNumber,
+ BYTE *numberOfEntries,
+ TRUSTEE_INFO *ti
+ )
+{
+ NTSTATUS NtStatus;
+
+ NtStatus = NWPScanForTrustees(
+ hConn,
+ dirHandle,
+ pszsearchDirPath,
+ pucsequenceNumber,
+ numberOfEntries,
+ ti
+ ) ;
+
+ (void) SetWin32ErrorFromNtStatus(NtStatus) ;
+
+ return MapNtStatus( NtStatus, NcpClassBindery );
+}
+
+
+NWCCODE
+NWCScanDirectoryForTrustees2(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE dirHandle,
+ char *pszsearchDirPath,
+ NWSEQUENCE *pucsequenceNumber,
+ char *pszdirName,
+ NWDATE_TIME *dirDateTime,
+ NWOBJ_ID *ownerID,
+ TRUSTEE_INFO *ti
+ )
+{
+ NTSTATUS NtStatus;
+
+ NtStatus = NWPScanDirectoryForTrustees2(
+ hConn,
+ dirHandle,
+ pszsearchDirPath,
+ pucsequenceNumber,
+ pszdirName,
+ dirDateTime,
+ ownerID,
+ ti
+ );
+
+ (void) SetWin32ErrorFromNtStatus(NtStatus) ;
+
+ return MapNtStatus( NtStatus, NcpClassBindery );
+}
+
+
+NWCCODE
+NWCGetBinderyAccessLevel(
+ NWCONN_HANDLE hConn,
+ NWFLAGS *accessLevel,
+ NWOBJ_ID *objectID
+ )
+{
+ NTSTATUS NtStatus;
+
+ NtStatus = NWPGetBinderyAccessLevel(
+ hConn,
+ accessLevel,
+ objectID
+ );
+
+ (void) SetWin32ErrorFromNtStatus(NtStatus) ;
+
+ return MapNtStatus( NtStatus, NcpClassBindery );
+}
+
+NWCCODE
+NWCGetFileServerDescription(
+ NWCONN_HANDLE hConn,
+ char *pszCompany,
+ char *pszVersion,
+ char *pszRevision
+ )
+{
+ NTSTATUS NtStatus ;
+
+ NtStatus = NWPGetFileServerDescription(
+ hConn,
+ pszCompany,
+ pszVersion,
+ pszRevision
+ );
+
+ (void) SetWin32ErrorFromNtStatus(NtStatus) ;
+
+ return MapNtStatus( NtStatus, NcpClassBindery );
+}
+
+NWCCODE
+NWCGetVolumeNumber(
+ NWCONN_HANDLE hConn,
+ char *pszVolume,
+ NWVOL_NUM *VolumeNumber
+ )
+{
+ NTSTATUS NtStatus ;
+
+ NtStatus = NWPGetVolumeNumber(
+ hConn,
+ pszVolume,
+ VolumeNumber
+ );
+
+ (void) SetWin32ErrorFromNtStatus(NtStatus) ;
+
+ return MapNtStatus( NtStatus, NcpClassBindery );
+}
+
+NWCCODE
+NWCGetVolumeUsage(
+ NWCONN_HANDLE hConn,
+ NWVOL_NUM VolumeNumber,
+ DWORD *TotalBlocks,
+ DWORD *FreeBlocks,
+ DWORD *PurgeableBlocks,
+ DWORD *NotYetPurgeableBlocks,
+ DWORD *TotalDirectoryEntries,
+ DWORD *AvailableDirectoryEntries,
+ BYTE *SectorsPerBlock
+ )
+{
+ NTSTATUS NtStatus ;
+
+ NtStatus = NWPGetVolumeUsage(
+ hConn,
+ VolumeNumber,
+ TotalBlocks,
+ FreeBlocks,
+ PurgeableBlocks,
+ NotYetPurgeableBlocks,
+ TotalDirectoryEntries,
+ AvailableDirectoryEntries,
+ SectorsPerBlock
+ );
+
+ (void) SetWin32ErrorFromNtStatus(NtStatus) ;
+
+ return MapNtStatus( NtStatus, NcpClassBindery );
+}
diff --git a/private/nw/nwlib/nwpapi32.c b/private/nw/nwlib/nwpapi32.c
new file mode 100644
index 000000000..030593a71
--- /dev/null
+++ b/private/nw/nwlib/nwpapi32.c
@@ -0,0 +1,1011 @@
+/*++
+
+Copyright (C) 1993 Microsoft Corporation
+
+Module Name:
+
+ NWAPI32.C
+
+Abstract:
+
+ This module contains several useful functions. Mostly wrappers.
+
+Author:
+
+ Chuck Y. Chan (ChuckC) 06-Mar-1995
+
+Revision History:
+
+--*/
+
+
+#include "procs.h"
+
+//
+// Define structure for internal use. Our handle passed back from attach to
+// file server will be pointer to this. We keep server string around for
+// discnnecting from the server on logout. The structure is freed on detach.
+// Callers should not use this structure but treat pointer as opaque handle.
+//
+typedef struct _NWC_SERVER_INFO {
+ HANDLE hConn ;
+ UNICODE_STRING ServerString ;
+} NWC_SERVER_INFO, *PNWC_SERVER_INFO ;
+
+
+//
+// forward declare
+//
+
+extern NTSTATUS
+NwAttachToServer(
+ IN LPWSTR ServerName,
+ OUT LPHANDLE phandleServer
+ ) ;
+
+extern NTSTATUS
+NwDetachFromServer(
+ IN HANDLE handleServer
+ ) ;
+
+extern DWORD
+CancelAllConnections(
+ LPWSTR pszServer
+ ) ;
+
+extern DWORD
+szToWide(
+ LPWSTR lpszW,
+ LPCSTR lpszC,
+ INT nSize
+);
+
+
+
+NTSTATUS
+NWPAttachToFileServerW(
+ const WCHAR *pszServerName,
+ NWLOCAL_SCOPE ScopeFlag,
+ NWCONN_HANDLE *phNewConn
+ )
+{
+ NTSTATUS NtStatus;
+ LPWSTR lpwszServerName; // Pointer to buffer for WIDE servername
+ int nSize;
+ PNWC_SERVER_INFO pServerInfo ;
+
+ UNREFERENCED_PARAMETER(ScopeFlag) ;
+
+ //
+ // check parameters and init return result to be null.
+ //
+ if (!pszServerName || !phNewConn)
+ return STATUS_INVALID_PARAMETER;
+
+ *phNewConn = NULL ;
+
+ //
+ // Allocate a buffer to store the file server name
+ //
+ nSize = wcslen(pszServerName)+3 ;
+ if(!(lpwszServerName = (LPWSTR) LocalAlloc(
+ LPTR,
+ nSize * sizeof(WCHAR) )))
+ {
+ NtStatus = STATUS_NO_MEMORY;
+ goto ExitPoint ;
+ }
+ wcscpy( lpwszServerName, L"\\\\" );
+ wcscat( lpwszServerName, pszServerName );
+
+ //
+ // Allocate a buffer for the server info (handle + name pointer). Also
+ // init the unicode string.
+ //
+ if( !(pServerInfo = (PNWC_SERVER_INFO) LocalAlloc(
+ LPTR,
+ sizeof(NWC_SERVER_INFO))) )
+ {
+ NtStatus = STATUS_NO_MEMORY;
+ goto ExitPoint ;
+ }
+ RtlInitUnicodeString(&pServerInfo->ServerString, lpwszServerName) ;
+
+ //
+ // Call createfile to get a handle for the redirector calls
+ //
+ NtStatus = NwAttachToServer( lpwszServerName, &pServerInfo->hConn );
+
+ExitPoint:
+
+ //
+ // Free the memory allocated above before exiting
+ //
+ if ( !NT_SUCCESS( NtStatus))
+ {
+ if (lpwszServerName)
+ (void) LocalFree( (HLOCAL) lpwszServerName );
+ if (pServerInfo)
+ (void) LocalFree( (HLOCAL) pServerInfo );
+ }
+ else
+ *phNewConn = (HANDLE) pServerInfo ;
+
+ return( NtStatus );
+}
+
+
+NTSTATUS
+NWPDetachFromFileServer(
+ NWCONN_HANDLE hConn
+ )
+{
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ (void) NwDetachFromServer( pServerInfo->hConn );
+
+ (void) LocalFree (pServerInfo->ServerString.Buffer) ;
+
+ //
+ // catch any body that still trirs to use this puppy...
+ //
+ pServerInfo->ServerString.Buffer = NULL ;
+ pServerInfo->hConn = NULL ;
+
+ (void) LocalFree (pServerInfo) ;
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+NWPGetFileServerVersionInfo(
+ NWCONN_HANDLE hConn,
+ VERSION_INFO *lpVerInfo
+ )
+{
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 3, // Max request packet size
+ 130, // Max response packet size
+ "b|r", // Format string
+ // === REQUEST ================================
+ 0x11, // b Get File Server Information
+ // === REPLY ==================================
+ lpVerInfo, // r File Version Structure
+ sizeof(VERSION_INFO)
+ );
+
+ // Convert HI-LO words to LO-HI
+ // ===========================================================
+ lpVerInfo->ConnsSupported = wSWAP( lpVerInfo->ConnsSupported );
+ lpVerInfo->connsInUse = wSWAP( lpVerInfo->connsInUse );
+ lpVerInfo->maxVolumes = wSWAP( lpVerInfo->maxVolumes );
+ lpVerInfo->PeakConns = wSWAP( lpVerInfo->PeakConns );
+ return NtStatus;
+}
+
+NTSTATUS
+NWPGetObjectName(
+ NWCONN_HANDLE hConn,
+ NWOBJ_ID dwObjectID,
+ char *pszObjName,
+ NWOBJ_TYPE *pwObjType )
+{
+ NWOBJ_ID dwRetID;
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 7, // Max request packet size
+ 56, // Max response packet size
+ "br|rrr", // Format string
+ // === REQUEST ================================
+ 0x36, // b Get Bindery Object Name
+ &dwObjectID,DW_SIZE, // r Object ID HI-LO
+ // === REPLY ==================================
+ &dwRetID,DW_SIZE, // r Object ID HI-LO
+ pwObjType,W_SIZE, // r Object Type
+ pszObjName,48 // r Object Name
+ );
+
+ return NtStatus;
+}
+
+DWORD
+NWPLoginToFileServerW(
+ NWCONN_HANDLE hConn,
+ LPWSTR pszUserNameW,
+ NWOBJ_TYPE wObjType,
+ LPWSTR pszPasswordW
+ )
+{
+ NETRESOURCEW NetResource;
+ DWORD dwRes;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ //
+ // validate parameters
+ //
+ if (!hConn || !pszUserNameW || !pszPasswordW)
+ return ERROR_INVALID_PARAMETER;
+
+ NetResource.dwScope = 0 ;
+ NetResource.dwUsage = 0 ;
+ NetResource.dwType = RESOURCETYPE_ANY;
+ NetResource.lpLocalName = NULL;
+ NetResource.lpRemoteName = (LPWSTR) pServerInfo->ServerString.Buffer;
+ NetResource.lpComment = NULL;
+ NetResource.lpProvider = NULL ;
+
+ //
+ // make the connection
+ //
+ dwRes=NPAddConnection ( &NetResource,
+ pszPasswordW,
+ pszUserNameW );
+
+ if( NO_ERROR != dwRes )
+ dwRes = GetLastError();
+
+ return( dwRes );
+}
+
+
+DWORD
+NWPLogoutFromFileServer(
+ NWCONN_HANDLE hConn
+ )
+{
+ DWORD dwRes;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ //
+ // now cancel the any connection to \\servername.
+ //
+ dwRes = NPCancelConnection( pServerInfo->ServerString.Buffer, TRUE );
+
+ if ( NO_ERROR != dwRes )
+ dwRes = GetLastError();
+
+ return dwRes;
+}
+
+
+NTSTATUS
+NWPReadPropertyValue(
+ NWCONN_HANDLE hConn,
+ const char *pszObjName,
+ NWOBJ_TYPE wObjType,
+ char *pszPropName,
+ unsigned char ucSegment,
+ char *pValue,
+ NWFLAGS *pucMoreFlag,
+ NWFLAGS *pucPropFlag
+ )
+{
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 70, // Max request packet size
+ 132, // Max response packet size
+ "brpbp|rbb", // Format string
+ // === REQUEST ================================
+ 0x3D, // b Read Property Value
+ &wObjType,W_SIZE, // r Object Type HI-LO
+ pszObjName, // p Object Name
+ ucSegment, // b Segment Number
+ pszPropName, // p Property Name
+ // === REPLY ==================================
+ pValue,128, // r Property value
+ pucMoreFlag, // b More Flag
+ pucPropFlag // b Prop Flag
+ );
+
+ return NtStatus;
+}
+
+NTSTATUS
+NWPScanObject(
+ NWCONN_HANDLE hConn,
+ const char *pszSearchName,
+ NWOBJ_TYPE wObjSearchType,
+ NWOBJ_ID *pdwObjectID,
+ char *pszObjectName,
+ NWOBJ_TYPE *pwObjType,
+ NWFLAGS *pucHasProperties,
+ NWFLAGS *pucObjectFlags,
+ NWFLAGS *pucObjSecurity
+ )
+{
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 57, // Max request packet size
+ 59, // Max response packet size
+ "brrp|rrrbbb", // Format string
+ // === REQUEST ================================
+ 0x37, // b Scan bindery object
+ pdwObjectID,DW_SIZE, // r 0xffffffff to start or last returned ID when enumerating HI-LO
+ &wObjSearchType,W_SIZE, // r Use OT_??? Defines HI-LO
+ pszSearchName, // p Search Name. (use "*") for all
+ // === REPLY ==================================
+ pdwObjectID,DW_SIZE, // r Returned ID HI-LO
+ pwObjType,W_SIZE, // r rObject Type HI-LO
+ pszObjectName,48, // r Found Name
+ pucObjectFlags, // b Object Flag
+ pucObjSecurity, // b Object Security
+ pucHasProperties // b Has Properties
+ );
+
+ return NtStatus;
+}
+
+NTSTATUS
+NWPScanProperty(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ char *pszSearchName,
+ NWOBJ_ID *pdwSequence,
+ char *pszPropName,
+ NWFLAGS *pucPropFlags,
+ NWFLAGS *pucPropSecurity,
+ NWFLAGS *pucHasValue,
+ NWFLAGS *pucMore
+ )
+{
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 73, // Max request packet size
+ 26, // Max response packet size
+ "brprp|rbbrbb", // Format string
+ // === REQUEST ================================
+ 0x3C, // b Scan Prop function
+ &wObjType,W_SIZE, // r Type of Object
+ pszObjectName, // p Object Name
+ pdwSequence,DW_SIZE, // r Sequence HI-LO
+ pszSearchName, // p Property Name to Search for
+ // === REPLY ==================================
+ pszPropName,16, // r Returned Property Name
+ pucPropFlags, // b Property Flags
+ pucPropSecurity, // b Property Security
+ pdwSequence,DW_SIZE, // r Sequence HI-LO
+ pucHasValue, // b Property Has value
+ pucMore // b More Properties
+ );
+
+ return NtStatus;
+}
+
+NTSTATUS
+NWPDeleteObject(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType
+ )
+{
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 54, // Max request packet size
+ 2, // Max response packet size
+ "brp|", // Format string
+ // === REQUEST ================================
+ 0x33, // b Scan Prop function
+ &wObjType,W_SIZE, // r Type of Object
+ pszObjectName // p Object Name
+ // === REPLY ==================================
+ );
+
+ return NtStatus;
+}
+
+NTSTATUS
+NWPCreateObject(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ NWFLAGS ucObjectFlags,
+ NWFLAGS ucObjSecurity
+ )
+{
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 56, // Max request packet size
+ 2, // Max response packet size
+ "bbbrp|", // Format string
+ // === REQUEST ================================
+ 0x32, // b Scan Prop function
+ ucObjectFlags, // b Object flags
+ ucObjSecurity, // b Object security
+ &wObjType,W_SIZE, // r Type of Object
+ pszObjectName // p Object Name
+ // === REPLY ==================================
+ );
+
+ return NtStatus;
+}
+
+NTSTATUS
+NWPCreateProperty(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ const char *pszPropertyName,
+ NWFLAGS ucObjectFlags,
+ NWFLAGS ucObjSecurity
+ )
+{
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 73, // Max request packet size
+ 2, // Max response packet size
+ "brpbbp|", // Format string
+ // === REQUEST ================================
+ 0x39, // b Create Prop function
+ &wObjType,W_SIZE, // r Type of Object
+ pszObjectName, // p Object Name
+ ucObjectFlags, // b Object flags
+ ucObjSecurity, // b Object security
+ pszPropertyName // p Property Name
+ // === REPLY ==================================
+ );
+
+ return NtStatus;
+}
+
+
+NTSTATUS
+NWPDeleteProperty(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ const char *pszPropertyName
+ )
+{
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 73, // Max request packet size
+ 2, // Max response packet size
+ "brpp|", // Format string
+ // === REQUEST ================================
+ 0x3A, // b Delete Prop function
+ &wObjType,W_SIZE, // r Type of Object
+ pszObjectName, // p Object Name
+ pszPropertyName // p Property Name
+ // === REPLY ==================================
+ );
+
+ return NtStatus;
+}
+
+
+NTSTATUS
+NWPWritePropertyValue(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ const char *pszPropertyName,
+ NWSEGMENT_NUM segmentNumber,
+ NWSEGMENT_DATA *segmentData,
+ NWFLAGS moreSegments
+ )
+{
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 201, // Max request packet size
+ 2, // Max response packet size
+ "brpbbpr|", // Format string
+ // === REQUEST ================================
+ 0x3E, // b Write Prop function
+ &wObjType,W_SIZE, // r Type of Object
+ pszObjectName, // p Object Name
+ segmentNumber, // b Segment Number
+ moreSegments, // b Segment remaining
+ pszPropertyName, // p Property Name
+ segmentData, 128 // r Property Value Data
+ // === REPLY ==================================
+ );
+
+ return NtStatus;
+}
+
+NTSTATUS
+NWPChangeObjectPasswordEncrypted(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ BYTE *validationKey,
+ BYTE *newKeyedPassword
+ )
+{
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ strlen( pszObjectName) + 32, // Max request packet size
+ 2, // Max response packet size
+ "brrpr|", // Format string
+ // === REQUEST ================================
+ 0x4B, // b Write Prop function
+ validationKey, 8, // r Key
+ &wObjType,W_SIZE, // r Type of Object
+ pszObjectName, // p Object Name
+ newKeyedPassword, 17 // r New Keyed Password
+ // === REPLY ==================================
+ );
+
+ return NtStatus;
+}
+
+NTSTATUS
+NWPGetObjectID(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ NWOBJ_ID *objectID
+ )
+{
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 54, // Max request packet size
+ 56, // Max response packet size
+ "brp|d", // Format string
+ // === REQUEST ================================
+ 0x35, // b Get Obj ID
+ &wObjType,W_SIZE, // r Type of Object
+ pszObjectName, // p Object Name
+ // === REPLY ==================================
+ objectID // d Object ID
+ );
+
+ *objectID = dwSWAP( *objectID );
+
+ return NtStatus;
+}
+
+
+NTSTATUS
+NWPRenameBinderyObject(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ const char *pszNewObjectName,
+ NWOBJ_TYPE wObjType
+ )
+{
+ NTSTATUS NtStatus;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 105, // Max request packet size
+ 2, // Max response packet size
+ "brpp", // Format string
+ // === REQUEST ================================
+ 0x34, // b Rename bindery object
+ &wObjType,W_SIZE, // r Type of Object
+ pszObjectName, // p Object Name
+ pszNewObjectName // p New Object Name
+ // === REPLY ==================================
+ );
+
+ return NtStatus;
+}
+
+NTSTATUS
+NWPAddObjectToSet(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ const char *pszPropertyName,
+ const char *pszMemberName,
+ NWOBJ_TYPE memberType
+ )
+{
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 122, // Max request packet size
+ 2, // Max response packet size
+ "brpprp|", // Format string
+ // === REQUEST ================================
+ 0x41, // b Add obj to set
+ &wObjType,W_SIZE, // r Type of Object
+ pszObjectName, // p Object Name
+ pszPropertyName, // p Property Name
+ &memberType, W_SIZE, // r Member type
+ pszMemberName // p Member Name
+ // === REPLY ==================================
+ );
+
+ return NtStatus;
+}
+
+
+NTSTATUS
+NWPDeleteObjectFromSet(
+ NWCONN_HANDLE hConn,
+ const char *pszObjectName,
+ NWOBJ_TYPE wObjType,
+ const char *pszPropertyName,
+ const char *pszMemberName,
+ NWOBJ_TYPE memberType
+ )
+{
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 122, // Max request packet size
+ 2, // Max response packet size
+ "brpprp|", // Format string
+ // === REQUEST ================================
+ 0x42, // b Del object from set
+ &wObjType,W_SIZE, // r Type of Object
+ pszObjectName, // p Object Name
+ pszPropertyName, // p Property Name
+ &memberType, W_SIZE, // r Member type
+ pszMemberName // p Member Name
+ // === REPLY ==================================
+ );
+
+ return NtStatus;
+}
+
+NTSTATUS
+NWPGetChallengeKey(
+ NWCONN_HANDLE hConn,
+ UCHAR *challengeKey
+ )
+{
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 3, // Max request packet size
+ 10, // Max response packet size
+ "b|r", // Format string
+ // === REQUEST ================================
+ 0x17, // b Get Challenge
+ // === REPLY ==================================
+ challengeKey, 8
+ );
+
+ return NtStatus;
+}
+
+NTSTATUS
+NWPCreateDirectory(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE dirHandle,
+ const char *pszPath,
+ NWACCESS_RIGHTS accessMask
+ )
+{
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E2H, // Bindery function
+ 261, // Max request packet size
+ 2, // Max response packet size
+ "bbbp|", // Format string
+ // === REQUEST ================================
+ 0xA, // b Create Directory
+ dirHandle, // b Directory Handle
+ accessMask, // b Access Mask
+ pszPath // p Property Name
+ // === REPLY ==================================
+ );
+
+ return NtStatus;
+}
+
+NTSTATUS
+NWPAddTrustee(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE dirHandle,
+ const char *pszPath,
+ NWOBJ_ID dwTrusteeID,
+ NWRIGHTS_MASK rightsMask
+ )
+{
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E2H, // Directory function
+ 266, // Max request packet size
+ 2, // Max response packet size
+ "bbrrp|", // Format string
+ // === REQUEST ================================
+ 0x27, // b Add trustee to directory
+ dirHandle, // b Directory handle
+ &dwTrusteeID,DW_SIZE, // r Object ID to assigned to directory
+ &rightsMask,W_SIZE, // r User rights for directory
+ pszPath // p Directory (if dirHandle = 0 then vol:directory)
+ );
+
+ return NtStatus;
+
+}
+
+
+NTSTATUS
+NWPScanForTrustees(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE dirHandle,
+ char *pszsearchDirPath,
+ NWSEQUENCE *pucsequenceNumber,
+ BYTE *numberOfEntries,
+ TRUSTEE_INFO *ti
+ )
+{
+ ULONG i;
+ DWORD oid[20];
+ WORD or[20];
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E2H, // Bindery function
+ 261, // Max request packet size
+ 121, // Max response packet size
+ "bbbp|brr", // Format string
+ // === REQUEST ================================
+ 0x26, // b Scan For Trustees
+ dirHandle, // b Directory Handle
+ *pucsequenceNumber, // b Sequence Number
+ pszsearchDirPath, // p Search Dir Path
+ // === REPLY ==================================
+ numberOfEntries,
+ &oid[0],DW_SIZE*20, // r trustee object ID
+ &or[0], W_SIZE*20 // b Trustee rights mask
+ );
+
+
+ for(i = 0; i < 20; i++) {
+ ti[i].objectID = oid[i];
+ ti[i].objectRights = or[i];
+ }
+
+ (*pucsequenceNumber)++;
+
+ return NtStatus ;
+
+} // NWScanForTrustees
+
+
+NTSTATUS
+NWPScanDirectoryForTrustees2(
+ NWCONN_HANDLE hConn,
+ NWDIR_HANDLE dirHandle,
+ char *pszsearchDirPath,
+ NWSEQUENCE *pucsequenceNumber,
+ char *pszdirName,
+ NWDATE_TIME *dirDateTime,
+ NWOBJ_ID *ownerID,
+ TRUSTEE_INFO *ti
+ )
+{
+ ULONG i;
+ DWORD oid[5];
+ BYTE or[5];
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ memset(oid, 0, sizeof(oid));
+ memset(or, 0, sizeof(or));
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E2H, // Bindery function
+ 261, // Max request packet size
+ 49, // Max response packet size
+ "bbbp|rrrrr", // Format string
+ // === REQUEST ================================
+ 0x0C, // b Scan Directory function
+ dirHandle, // b Directory Handle
+ *pucsequenceNumber, // b Sequence Number
+ pszsearchDirPath, // p Search Dir Path
+ // === REPLY ==================================
+ pszdirName,16, // r Returned Directory Name
+ dirDateTime,DW_SIZE, // r Date and Time
+ ownerID,DW_SIZE, // r Owner ID
+ &oid[0],DW_SIZE*5, // r trustee object ID
+ &or[0], 5 // b Trustee rights mask
+ );
+
+
+ for(i = 0; i < 5; i++) {
+ ti[i].objectID = oid[i];
+ ti[i].objectRights = (WORD) or[i];
+ }
+
+ (*pucsequenceNumber)++;
+
+ return NtStatus ;
+
+} // NWScanDirectoryForTrustees2
+
+
+NTSTATUS
+NWPGetBinderyAccessLevel(
+ NWCONN_HANDLE hConn,
+ NWFLAGS *accessLevel,
+ NWOBJ_ID *objectID
+ )
+{
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 3, // Max request packet size
+ 7, // Max response packet size
+ "b|br", // Format string
+ // === REQUEST ================================
+ 0x46, // b Get Bindery Access Level
+ // === REPLY ==================================
+ accessLevel,
+ objectID,DW_SIZE
+ );
+
+
+
+ return NtStatus ;
+
+} // NWGetBinderyAccessLevel
+
+NTSTATUS
+NWPGetFileServerDescription(
+ NWCONN_HANDLE hConn,
+ char *pszCompany,
+ char *pszVersion,
+ char *pszRevision
+ )
+{
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 3, // Max request packet size
+ 514, // Max response packet size
+ "b|ccc", // Format string
+ // === REQUEST ================================
+ 0xC9, // b Get File Server Information
+ // === REPLY ==================================
+ pszCompany, // c Company
+ pszVersion, // c Version
+ pszRevision // c Description
+ );
+
+ return NtStatus;
+}
+
+NTSTATUS
+NWPGetVolumeNumber(
+ NWCONN_HANDLE hConn,
+ char *pszVolume,
+ NWVOL_NUM *VolumeNumber
+ )
+{
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E2H, // Bindery function
+ 20, // Max request packet size
+ 3, // Max response packet size
+ "bp|b", // Format string
+ // === REQUEST ================================
+ 0x05, // b Get Volume Number
+ pszVolume, // p volume name
+ // === REPLY ==================================
+ VolumeNumber // b Description
+ );
+
+ return NtStatus;
+}
+
+NTSTATUS
+NWPGetVolumeUsage(
+ NWCONN_HANDLE hConn,
+ NWVOL_NUM VolumeNumber,
+ DWORD *TotalBlocks,
+ DWORD *FreeBlocks,
+ DWORD *PurgeableBlocks,
+ DWORD *NotYetPurgeableBlocks,
+ DWORD *TotalDirectoryEntries,
+ DWORD *AvailableDirectoryEntries,
+ BYTE *SectorsPerBlock
+ )
+{
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)hConn ;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E2H, // Bindery function
+ 4, // Max request packet size
+ 46, // Max response packet size
+ "bb|dddddd==b", // Format string
+ // === REQUEST ================================
+ 0x2C, // b Get Volume Number
+ VolumeNumber, // p volume number
+ // === REPLY ==================================
+ TotalBlocks,
+ FreeBlocks,
+ PurgeableBlocks,
+ NotYetPurgeableBlocks,
+ TotalDirectoryEntries,
+ AvailableDirectoryEntries,
+ SectorsPerBlock
+ );
+
+ *TotalBlocks = dwSWAP( *TotalBlocks );
+ *FreeBlocks = dwSWAP( *FreeBlocks );
+ *PurgeableBlocks = dwSWAP( *PurgeableBlocks );
+ *NotYetPurgeableBlocks = dwSWAP( *NotYetPurgeableBlocks );
+ *TotalDirectoryEntries = dwSWAP( *TotalDirectoryEntries );
+ *AvailableDirectoryEntries = dwSWAP( *AvailableDirectoryEntries );
+
+ return NtStatus;
+}
diff --git a/private/nw/nwlib/packstr.c b/private/nw/nwlib/packstr.c
new file mode 100644
index 000000000..d66cef0e3
--- /dev/null
+++ b/private/nw/nwlib/packstr.c
@@ -0,0 +1,125 @@
+/*++
+
+Copyright (c) 1992, 1993 Microsoft Corporation
+
+Module Name:
+
+ packstr.c
+
+Abstract:
+
+ Contains functions for packing strings into buffers that also contain
+ structures.
+
+Author:
+
+ From LAN Manager netlib.
+ Rita Wong (ritaw) 2-Mar-1993
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+#include <procs.h>
+
+
+BOOL
+NwlibCopyStringToBuffer(
+ IN LPCWSTR SourceString OPTIONAL,
+ IN DWORD CharacterCount,
+ IN LPCWSTR FixedDataEnd,
+ IN OUT LPWSTR *EndOfVariableData,
+ OUT LPWSTR *VariableDataPointer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine puts a single variable-length string into an output buffer.
+ The string is not written if it would overwrite the last fixed structure
+ in the buffer.
+
+Arguments:
+
+ SourceString - Supplies a pointer to the source string to copy into the
+ output buffer. If SourceString is null then a pointer to a zero terminator
+ is inserted into output buffer.
+
+ CharacterCount - Supplies the length of SourceString, not including zero
+ terminator. (This in units of characters - not bytes).
+
+ FixedDataEnd - Supplies a pointer to just after the end of the last
+ fixed structure in the buffer.
+
+ EndOfVariableData - Supplies an address to a pointer to just after the
+ last position in the output buffer that variable data can occupy.
+ Returns a pointer to the string written in the output buffer.
+
+ VariableDataPointer - Supplies a pointer to the place in the fixed
+ portion of the output buffer where a pointer to the variable data
+ should be written.
+
+Return Value:
+
+ Returns TRUE if string fits into output buffer, FALSE otherwise.
+
+--*/
+{
+ DWORD CharsNeeded = (CharacterCount + 1);
+
+
+ //
+ // Determine if source string will fit, allowing for a zero terminator.
+ // If not, just set the pointer to NULL.
+ //
+
+ if ((*EndOfVariableData - CharsNeeded) >= FixedDataEnd) {
+
+ //
+ // It fits. Move EndOfVariableData pointer up to the location where
+ // we will write the string.
+ //
+
+ *EndOfVariableData -= CharsNeeded;
+
+ //
+ // Copy the string to the buffer if it is not null.
+ //
+
+ if (CharacterCount > 0 && SourceString != NULL) {
+
+ (VOID) wcsncpy(*EndOfVariableData, SourceString, CharacterCount);
+ }
+
+ //
+ // Set the zero terminator.
+ //
+
+ *(*EndOfVariableData + CharacterCount) = L'\0';
+
+ //
+ // Set up the pointer in the fixed data portion to point to where the
+ // string is written.
+ //
+
+ *VariableDataPointer = *EndOfVariableData;
+
+ return TRUE;
+
+ }
+ else {
+
+ //
+ // It doesn't fit. Set the offset to NULL.
+ //
+
+ *VariableDataPointer = NULL;
+
+ return FALSE;
+ }
+}
diff --git a/private/nw/nwlib/procs.h b/private/nw/nwlib/procs.h
new file mode 100644
index 000000000..2cf897262
--- /dev/null
+++ b/private/nw/nwlib/procs.h
@@ -0,0 +1,55 @@
+
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ procs.c
+
+Abstract:
+
+ Common header file for routines which support 16 bit
+ applications.
+
+Author:
+
+ Colin Watson (colinw) 21-Nov-1993
+
+Environment:
+
+
+Revision History:
+
+
+--*/
+
+#define UNICODE
+
+
+#include <stdlib.h>
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <ctype.h>
+
+#include <validc.h>
+#include <nwstatus.h>
+#include <nwcanon.h>
+#include <ntddnwfs.h>
+#include <npapi.h>
+
+#include <nwxchg.h>
+#include <nwapi.h>
+#include <nwapi32.h>
+#include <nwpapi32.h>
+#include <ndsapi32.h>
+#include <nds.h>
+
+#include <debugfmt.h> // FORMAT_LPSTR
+#include <mpr.h>
+
+#include <lmcons.h>
+#include <ntsam.h>
+#include <nwpkstr.h>
diff --git a/private/nw/nwlib/regacl.c b/private/nw/nwlib/regacl.c
new file mode 100644
index 000000000..8258beae9
--- /dev/null
+++ b/private/nw/nwlib/regacl.c
@@ -0,0 +1,161 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ regacl.c
+
+Abstract:
+
+ This module contains the code for adding access permission ACL in a registry
+ key.
+
+Author:
+
+ Terrence Kwan (terryk) 25-Sept-1993
+
+Revision History:
+
+--*/
+
+#include <procs.h>
+
+DWORD
+NwLibSetEverybodyPermission(
+ IN HKEY hKey,
+ IN DWORD dwPermission
+ )
+/*++
+
+Routine Description:
+
+ Set the registry key to everybody "Set Value" (or whatever
+ the caller want.)
+
+Arguments:
+
+ hKey - The handle of the registry key to set security on
+
+ dwPermission - The permission to add to "everybody"
+
+Return Value:
+
+ The win32 error.
+
+--*/
+{
+ LONG err; // error code
+ PSECURITY_DESCRIPTOR psd = NULL; // related SD
+ PACL pDacl = NULL; // Absolute DACL
+ PACL pSacl = NULL; // Absolute SACL
+ PSID pOSid = NULL; // Absolute Owner SID
+ PSID pPSid = NULL; // Absolute Primary SID
+
+ do { // Not a loop, just for breaking out of error
+ //
+ // Initialize all the variables...
+ //
+ // world sid authority
+ SID_IDENTIFIER_AUTHORITY SidAuth= SECURITY_WORLD_SID_AUTHORITY;
+ DWORD cbSize=0; // Security key size
+ PACL pAcl; // original ACL
+ BOOL fDaclPresent;
+ BOOL fDaclDefault;
+ PSID pSid; // original SID
+ SECURITY_DESCRIPTOR absSD; // Absolute SD
+ DWORD AbsSize = sizeof(SECURITY_DESCRIPTOR); // Absolute SD size
+ DWORD DaclSize; // Absolute DACL size
+ DWORD SaclSize; // Absolute SACL size
+ DWORD OSidSize; // Absolute OSID size
+ DWORD PSidSize; // Absolute PSID size
+
+
+ // Get the original DACL list
+
+ RegGetKeySecurity( hKey, DACL_SECURITY_INFORMATION, NULL, &cbSize);
+
+ psd = (PSECURITY_DESCRIPTOR *)LocalAlloc(LMEM_ZEROINIT, cbSize+sizeof(ACCESS_ALLOWED_ACE)+sizeof(ACCESS_MASK)+sizeof(SID));
+ pDacl = (PACL)LocalAlloc(LMEM_ZEROINIT, cbSize+sizeof(ACCESS_ALLOWED_ACE)+sizeof(ACCESS_MASK)+sizeof(SID));
+ pSacl = (PACL)LocalAlloc(LMEM_ZEROINIT, cbSize);
+ pOSid = (PSID)LocalAlloc(LMEM_ZEROINIT, cbSize);
+ pPSid = (PSID)LocalAlloc(LMEM_ZEROINIT, cbSize);
+ DaclSize = cbSize+sizeof(ACCESS_ALLOWED_ACE)+sizeof(ACCESS_MASK)+sizeof(SID);
+ SaclSize = cbSize;
+ OSidSize = cbSize;
+ PSidSize = cbSize;
+
+ if (( NULL == psd) ||
+ ( NULL == pDacl) ||
+ ( NULL == pSacl) ||
+ ( NULL == pOSid) ||
+ ( NULL == pPSid))
+ {
+ err = ERROR_INSUFFICIENT_BUFFER;
+ break;
+ }
+
+ if ( (err = RegGetKeySecurity( hKey, DACL_SECURITY_INFORMATION, psd, &cbSize )) != ERROR_SUCCESS )
+ {
+ break;
+ }
+ if ( !GetSecurityDescriptorDacl( psd, &fDaclPresent, &pAcl, &fDaclDefault ))
+ {
+ err = GetLastError();
+ break;
+ }
+
+ // Increase the size for an extra ACE
+
+ pAcl->AclSize += sizeof(ACCESS_ALLOWED_ACE)+sizeof(ACCESS_MASK)+sizeof(SID);
+
+ // Get World SID
+
+ if ( (err = RtlAllocateAndInitializeSid( &SidAuth, 1,
+ SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pSid)) != ERROR_SUCCESS)
+ {
+ break;
+ }
+
+ // Add Permission ACE
+
+ if ( !AddAccessAllowedAce(pAcl, ACL_REVISION, dwPermission ,pSid))
+ {
+ err = GetLastError();
+ break;
+ }
+
+ // Convert from relate format to absolute format
+
+ if ( !MakeAbsoluteSD( psd, &absSD, &AbsSize, pDacl, &DaclSize, pSacl, &SaclSize,
+ pOSid, &OSidSize, pPSid, &PSidSize ))
+ {
+ err = GetLastError();
+ break;
+ }
+
+ // Set SD
+
+ if ( !SetSecurityDescriptorDacl( &absSD, TRUE, pAcl, FALSE ))
+ {
+ err = GetLastError();
+ break;
+ }
+ if ( (err = RegSetKeySecurity( hKey, DACL_SECURITY_INFORMATION, psd ))
+ != ERROR_SUCCESS )
+ {
+ break;
+ }
+
+ } while (FALSE);
+
+ // Clean up the memory
+
+ LocalFree( psd );
+ LocalFree( pDacl );
+ LocalFree( pSacl );
+ LocalFree( pOSid );
+ LocalFree( pPSid );
+
+ return err;
+}
diff --git a/private/nw/nwlib/sources b/private/nw/nwlib/sources
new file mode 100644
index 000000000..5690351a2
--- /dev/null
+++ b/private/nw/nwlib/sources
@@ -0,0 +1,66 @@
+!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=nwlib
+
+TARGETNAME=nwlib
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+INCLUDES=..\inc;$(_NTROOT)\private\inc;
+MSC_WARNING_LEVEL=/W3 /WX
+
+SOURCES= \
+ canon.c \
+ packstr.c \
+ exchange.c \
+ regacl.c \
+ nwapi32.c \
+ nwcapi32.c \
+ nwpapi32.c \
+ ndsapi32.c
+
+TARGETLIBS= \
+ $(BASEDIR)\Public\Sdk\Lib\*\kernel32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\user32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\advapi32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\nwprovau.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\ntvdm.lib
+
+UNICODE=1
+
+NET_C_DEFINES=-DRPC_NO_WINDOWS_H -DNWDBG
+
+UMTYPE=console
+
+UMTEST=
+
+UMLIBS=
+
+OPTIONAL_UMTEST=
+
+!IFDEF MARS_PCH
+PRECOMPILED_INCLUDE=procs.h
+PRECOMPILED_PCH=procs.pch
+PRECOMPILED_OBJ=procs.obj
+!ENDIF
diff --git a/private/nw/nwlib/tpath.c b/private/nw/nwlib/tpath.c
new file mode 100644
index 000000000..831b4d5f1
--- /dev/null
+++ b/private/nw/nwlib/tpath.c
@@ -0,0 +1,238 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ tpath.c
+
+Abstract:
+
+ Test for canonicalization helpers.
+
+Author:
+
+ Rita Wong (ritaw) 22-Feb-1993
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windef.h>
+#include <winbase.h>
+
+#include <nwcanon.h>
+
+#ifndef UNICODE
+#define UNICODE
+#endif
+
+DWORD
+TestCanonLocalName(
+ IN LPWSTR LocalName,
+ IN DWORD ExpectedError
+ );
+
+DWORD
+TestCanonRemoteName(
+ IN LPWSTR RemoteName,
+ IN DWORD ExpectedError
+ );
+
+// BYTE WorkBuffer[1024];
+
+void _CRTAPI1
+main(
+ void
+ )
+{
+
+
+ TestCanonLocalName(
+ L"x:",
+ NO_ERROR
+ );
+
+ TestCanonLocalName(
+ L"B:",
+ NO_ERROR
+ );
+
+ TestCanonLocalName(
+ L"prn",
+ NO_ERROR
+ );
+
+ TestCanonLocalName(
+ L"lpt1:",
+ NO_ERROR
+ );
+
+ TestCanonLocalName(
+ L"*:",
+ ERROR_INVALID_NAME
+ );
+
+ TestCanonLocalName(
+ L"B",
+ ERROR_INVALID_NAME
+ );
+
+ TestCanonLocalName(
+ L"abc",
+ ERROR_INVALID_NAME
+ );
+
+ TestCanonLocalName(
+ L"\\:",
+ ERROR_INVALID_NAME
+ );
+
+ TestCanonRemoteName(
+ L"\\:",
+ ERROR_INVALID_NAME
+ );
+
+ TestCanonRemoteName(
+ L"\\\\Ser:ver",
+ ERROR_INVALID_NAME
+ );
+
+ TestCanonRemoteName(
+ L"\\\\*",
+ ERROR_INVALID_NAME
+ );
+
+ TestCanonRemoteName(
+ L"\\\\:",
+ ERROR_INVALID_NAME
+ );
+
+ TestCanonRemoteName(
+ L"\\\\Server\\Volume",
+ NO_ERROR
+ );
+
+ TestCanonRemoteName(
+ L"\\\\Server\\Volume\\Dir1\\Directory2\\ALongDirectory3",
+ NO_ERROR
+ );
+
+ TestCanonRemoteName(
+ L"\\\\Server\\Volume\\",
+ ERROR_INVALID_NAME
+ );
+
+ TestCanonRemoteName(
+ L"Server\\Volume\\",
+ ERROR_INVALID_NAME
+ );
+
+ TestCanonRemoteName(
+ L"\\\\Server\\Volu:me",
+ ERROR_INVALID_NAME
+ );
+
+ TestCanonRemoteName(
+ L"\\\\Server\\Volume\\\\Dir",
+ ERROR_INVALID_NAME
+ );
+
+ TestCanonRemoteName(
+ L"\\\\Server/Volume\\Dir",
+ ERROR_INVALID_NAME
+ );
+
+ TestCanonRemoteName(
+ L"\\\\Server\\Volume:",
+ ERROR_INVALID_NAME
+ );
+
+ TestCanonRemoteName(
+ L"\\\\Server",
+ ERROR_INVALID_NAME
+ );
+
+}
+
+
+DWORD
+TestCanonLocalName(
+ IN LPWSTR LocalName,
+ IN DWORD ExpectedError
+ )
+{
+ DWORD status;
+ DWORD OutputBufferLength;
+ LPWSTR OutputBuffer;
+
+
+ printf("\nCanon local name %ws\n", LocalName);
+
+ status = NwLibCanonLocalName(
+ LocalName,
+ &OutputBuffer,
+ &OutputBufferLength
+ );
+
+ if (status == NO_ERROR) {
+
+ printf(" got %ws, length %lu\n", OutputBuffer, OutputBufferLength);
+
+ (void) LocalFree((HLOCAL) OutputBuffer);
+ }
+
+ if (status == ExpectedError) {
+ printf(" SUCCESS: Got %lu as expected\n", ExpectedError);
+
+ }
+ else {
+ printf(" FAILED: Got %lu, expected %lu\n", status, ExpectedError);
+ }
+}
+
+
+DWORD
+TestCanonRemoteName(
+ IN LPWSTR RemoteName,
+ IN DWORD ExpectedError
+ )
+{
+ DWORD status;
+ DWORD OutputBufferLength;
+ LPWSTR OutputBuffer;
+
+
+ printf("\nCanon remote name %ws\n", RemoteName);
+
+ status = NwLibCanonRemoteName(
+ RemoteName,
+ &OutputBuffer,
+ &OutputBufferLength
+ );
+
+ if (status == NO_ERROR) {
+
+ printf(" got %ws, length %lu\n", OutputBuffer, OutputBufferLength);
+
+ (void) LocalFree((HLOCAL) OutputBuffer);
+ }
+
+ if (status == ExpectedError) {
+ printf(" SUCCESS: Got %lu as expected\n", ExpectedError);
+
+ }
+ else {
+ printf(" FAILED: Got %lu, expected %lu\n", status, ExpectedError);
+ }
+}
diff --git a/private/nw/nwscript/attach.c b/private/nw/nwscript/attach.c
new file mode 100644
index 000000000..fa6cacc75
--- /dev/null
+++ b/private/nw/nwscript/attach.c
@@ -0,0 +1,235 @@
+/*************************************************************************
+*
+* ATTACH.C
+*
+* NT Attach routines
+*
+* Copyright (c) 1995 Microsoft Corporation
+*
+* $Log: N:\NT\PRIVATE\NW4\NWSCRIPT\VCS\ATTACH.C $
+*
+* Rev 1.2 10 Apr 1996 14:21:30 terryt
+* Hotfix for 21181hq
+*
+* Rev 1.2 12 Mar 1996 19:52:08 terryt
+* Relative NDS names and merge
+*
+* Rev 1.1 22 Dec 1995 14:23:32 terryt
+* Add Microsoft headers
+*
+* Rev 1.0 15 Nov 1995 18:06:26 terryt
+* Initial revision.
+*
+* Rev 1.1 23 May 1995 19:36:30 terryt
+* Spruce up source
+*
+* Rev 1.0 15 May 1995 19:10:10 terryt
+* Initial revision.
+*
+*************************************************************************/
+
+#include <stdio.h>
+#include <direct.h>
+#include <time.h>
+#include <stdlib.h>
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include <nwapi32.h>
+#include <ntddnwfs.h>
+#include <nwapi.h>
+#include <npapi.h>
+
+#include "inc/common.h"
+#include "ntnw.h"
+
+/********************************************************************
+
+ GetDefaultConnectionID
+
+Routine Description:
+
+ Return the default connection ID ( the "preferred server" )
+
+Arguments:
+
+ phNewConn - pointer to connection number
+
+Return Value:
+ 0 = success
+ else NetWare error number
+
+ *******************************************************************/
+unsigned int
+GetDefaultConnectionID(
+ unsigned int *phNewConn
+ )
+{
+ VERSION_INFO VerInfo;
+ unsigned int Result;
+
+ if ( fNDS )
+ {
+ Result = NTAttachToFileServer( NDSTREE, phNewConn );
+ }
+ else
+ {
+ //
+ // "*" is the name for the preferred server
+ //
+ Result = NTAttachToFileServer( "*", phNewConn );
+ if ( Result )
+ return Result;
+
+ Result = NWGetFileServerVersionInfo( (NWCONN_HANDLE)*phNewConn,
+ &VerInfo );
+ if ( Result )
+ return Result;
+
+ NWDetachFromFileServer( (NWCONN_HANDLE)*phNewConn );
+
+ Result = NTAttachToFileServer( VerInfo.szName, phNewConn );
+ }
+ return Result;
+
+}
+
+/********************************************************************
+
+ NTAttachToFileServer
+
+Routine Description:
+
+ Given a server name, return a connection handle.
+ We need our own because NWAPI32 does it's own mapping
+ of errors.
+
+Arguments:
+
+ pszServerName - Ascii server name
+ phNewConn - pointer to connection handle
+
+Return Value:
+ 0 = success
+ else NetWare error number
+
+ *******************************************************************/
+unsigned int
+NTAttachToFileServer(
+ unsigned char *pszServerName,
+ unsigned int *phNewConn
+ )
+{
+ return ( NWAttachToFileServer( pszServerName, 0,
+ (NWCONN_HANDLE *)phNewConn ) );
+}
+
+
+/********************************************************************
+
+ NTIsConnected
+
+Routine Description:
+
+ Given a server name, is there already a connection to it?
+
+Arguments:
+
+ pszServerName - ascii server name
+
+Return Value:
+ TRUE - a connection to the server exists
+ FALSE - a connection to the server does not exist
+
+ *******************************************************************/
+unsigned int
+NTIsConnected( unsigned char * pszServerName )
+{
+ LPBYTE Buffer ;
+ DWORD dwErr ;
+ HANDLE EnumHandle ;
+ DWORD Count ;
+ LPWSTR pszServerNameW;
+ INT nSize;
+ DWORD BufferSize = 4096;
+
+ nSize = (strlen( pszServerName ) + 1 + 2) * sizeof( WCHAR );
+
+ //
+ // allocate memory and open the enumeration
+ //
+ if (!(pszServerNameW = LocalAlloc( LPTR, nSize ))) {
+ DisplayMessage(IDR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+ wcscpy( pszServerNameW, L"\\\\" );
+ szToWide( pszServerNameW + 2, pszServerName, nSize );
+
+ //
+ // allocate memory and open the enumeration
+ //
+ if (!(Buffer = LocalAlloc( LPTR, BufferSize ))) {
+ (void) LocalFree((HLOCAL) pszServerNameW) ;
+ DisplayMessage(IDR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+ memset( Buffer, 0, BufferSize );
+
+ dwErr = NPOpenEnum(RESOURCE_CONNECTED, 0, 0, NULL, &EnumHandle) ;
+ if (dwErr != WN_SUCCESS) {
+ (void) LocalFree((HLOCAL) pszServerNameW) ;
+ (void) LocalFree((HLOCAL) Buffer) ;
+ return FALSE;
+ }
+
+ do {
+
+ Count = 0xFFFFFFFF ;
+ BufferSize = 4096;
+ dwErr = NwEnumConnections(EnumHandle, &Count, Buffer, &BufferSize, TRUE) ;
+
+ if ((dwErr == WN_SUCCESS || dwErr == WN_NO_MORE_ENTRIES)
+ && ( Count != 0xFFFFFFFF) )
+ {
+ LPNETRESOURCE lpNetResource ;
+ DWORD i ;
+ DWORD ServerLen;
+
+ ServerLen = wcslen( pszServerNameW );
+ lpNetResource = (LPNETRESOURCE) Buffer ;
+ //
+ // search for our server
+ //
+ for ( i = 0; i < Count; lpNetResource++, i++ )
+ {
+ if ( lpNetResource->lpProvider )
+ if ( _wcsicmp( lpNetResource->lpProvider, NW_PROVIDER ) ) {
+ continue;
+ }
+ if ( lpNetResource->lpRemoteName ) {
+ if ( wcslen(lpNetResource->lpRemoteName) > ServerLen ) {
+ if ( lpNetResource->lpRemoteName[ServerLen] == L'\\' )
+ lpNetResource->lpRemoteName[ServerLen] = L'\0';
+ }
+ if ( !_wcsicmp(lpNetResource->lpRemoteName, pszServerNameW )) {
+ (void) WNetCloseEnum(EnumHandle) ;
+ (void) LocalFree((HLOCAL) pszServerNameW) ;
+ (void) LocalFree((HLOCAL) Buffer) ;
+ return TRUE;
+ }
+ }
+ }
+
+ }
+
+ } while (dwErr == WN_SUCCESS) ;
+
+ (void ) WNetCloseEnum(EnumHandle) ;
+ (void) LocalFree((HLOCAL) pszServerNameW) ;
+ (void) LocalFree((HLOCAL) Buffer) ;
+
+ return FALSE;
+}
diff --git a/private/nw/nwscript/break.c b/private/nw/nwscript/break.c
new file mode 100644
index 000000000..30af584db
--- /dev/null
+++ b/private/nw/nwscript/break.c
@@ -0,0 +1,85 @@
+/*************************************************************************
+*
+* BREAK.C
+*
+* Control-C and Control-Break routines
+*
+* Copyright (c) 1995 Microsoft Corporation
+*
+* $Log: N:\NT\PRIVATE\NW4\NWSCRIPT\VCS\BREAK.C $
+*
+* Rev 1.2 10 Apr 1996 14:21:38 terryt
+* Hotfix for 21181hq
+*
+* Rev 1.2 12 Mar 1996 19:52:16 terryt
+* Relative NDS names and merge
+*
+* Rev 1.1 22 Dec 1995 14:23:38 terryt
+* Add Microsoft headers
+*
+* Rev 1.0 15 Nov 1995 18:06:28 terryt
+* Initial revision.
+*
+* Rev 1.0 15 May 1995 19:10:14 terryt
+* Initial revision.
+*
+*************************************************************************/
+#include <stdio.h>
+#include <direct.h>
+#include <time.h>
+#include <stdlib.h>
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include "nwscript.h"
+#include "ntnw.h"
+
+
+/*
+ * Handler for console events
+ */
+BOOL WINAPI
+Handler( DWORD CtrlType )
+{
+ if ( CtrlType & ( CTRL_C_EVENT | CTRL_BREAK_EVENT ) )
+ return TRUE; /* don't execute default handler */
+}
+
+/*
+ * NTBreakOn
+ *
+ * Routine Description:
+ *
+ * Allow Ctrl+C and Ctrl+Break during logon script
+ *
+ * Arguments:
+ * none
+ *
+ * Return Value:
+ * none
+ */
+void NTBreakOn( void )
+{
+ (void) SetConsoleCtrlHandler( &Handler, FALSE );
+}
+
+/*
+ * NTBreakOff
+ *
+ * Routine Description:
+ *
+ * Prevent Ctrl+C and Ctrl+Break during logon script
+ *
+ * Arguments:
+ * none
+ *
+ * Return Value:
+ * none
+ */
+void NTBreakOff( void )
+{
+ (void) SetConsoleCtrlHandler( &Handler, TRUE );
+}
diff --git a/private/nw/nwscript/capture.c b/private/nw/nwscript/capture.c
new file mode 100644
index 000000000..b89df1480
--- /dev/null
+++ b/private/nw/nwscript/capture.c
@@ -0,0 +1,1067 @@
+/*
+ * Module Name:
+ * capture.c
+ * Copyright (c) 1995 Microsoft Corporation
+ *
+ * Abstract:
+ *
+ * SYNTAX (Command line)
+ * - Usage: Capture [/Options]
+ *
+ * Author : Congpa You (Congpay)
+ *
+ * Revision history :
+ * - 05/24/94 congpay Created
+ */
+
+#include "common.h"
+
+extern char *RemoveSpaces (char * buffer);
+
+typedef struct _CAPTURE_PARAMS {
+ UCHAR nLPT;
+ char serverName[MAX_NAME_LEN];
+ char queueName[MAX_QUEUE_NAME_LEN];
+ char bannerUserName[MAX_BANNER_USER_NAME];
+ char filePath[_MAX_PATH];
+ unsigned int NDSCapture;
+ NETWARE_CAPTURE_FLAGS_RW captureFlagsRW;
+}CAPTURE_PARAMS, *PCAPTURE_PARAMS;
+
+/* Local Functions*/
+void UpcaseArg(int argc, char ** argv);
+int IsShowOption (int argc, char ** argv);
+int IsServerSpecified (int argc, char ** argv);
+int IsNoOption (char * option);
+int IsQuestionMark (int argc, char ** argv);
+int IsValidOption (char *input, char *option, char *shortoption);
+void ShowCapture(void);
+
+void GetJobNameFromArg (int argc, char ** argv, char *jobName);
+
+int InitCaptureParams (unsigned int conn,
+ char *jobName,
+ PCAPTURE_PARAMS pCaptureParams);
+
+int ReadArguments (int argc,
+ char ** argv,
+ char *jobName,
+ PCAPTURE_PARAMS pCaptureParams);
+
+int CStartCapture(unsigned int conn,
+ PCAPTURE_PARAMS pCaptureParams);
+
+int GetPrinterDefaultQueue( PCAPTURE_PARAMS, PBYTE );
+
+void
+Capture (char ** argv, unsigned int argc)
+{
+ char jobName[MAX_JOB_NAME_LEN]="";
+ CAPTURE_PARAMS captureParams;
+ unsigned int conn;
+
+ UpcaseArg(argc, argv);
+
+ memset( (PBYTE)&captureParams, 0, sizeof(captureParams) );
+
+ captureParams.nLPT = 0;
+ captureParams.serverName[0] = 0;
+ captureParams.queueName[0] = 0;
+ captureParams.bannerUserName[0] = 0;
+ captureParams.filePath[0] = 0;
+
+ if ( fNDS )
+ {
+ captureParams.NDSCapture = TRUE;
+ }
+ else
+ {
+ captureParams.NDSCapture = FALSE;
+ }
+
+ if ( IsServerSpecified( argc, argv ) )
+ captureParams.NDSCapture = FALSE;
+
+ // If the option is show, show the current capture settings.
+ if (IsShowOption (argc, argv))
+ {
+ ShowCapture();
+ return;
+ }
+
+ // If the option is ?, show the usage.
+ if (IsQuestionMark(argc, argv))
+ {
+ DisplayMessage(IDR_CAPTURE_USAGE);
+ return;
+ }
+
+ // If /Job=jobname is in the parameter, get the jobname.
+ GetJobNameFromArg (argc, argv, jobName);
+
+ if (!CGetDefaultConnectionID (&conn) ||
+ !InitCaptureParams (conn, jobName, &captureParams) ||
+ !ReadArguments (argc,
+ argv,
+ jobName,
+ &captureParams))
+ return;
+
+ // Terminate old capture.
+ EndCapture ((unsigned char) captureParams.nLPT);
+
+ (void) CStartCapture(conn, &captureParams);
+ return;
+}
+
+void UpcaseArg(int argc, char ** argv)
+{
+ int i;
+ for (i = 0; i < argc ; i++)
+ _strupr (argv[i]);
+}
+
+/*
+ Return TRUE if input is /Show option.
+ FALSE otherwise.
+ */
+int IsShowOption (int argc,char ** argv)
+{
+ int bIsShowOption = FALSE;
+ char * p;
+
+ if (argc == 2)
+ {
+ p = argv[1];
+ while ((*p == '/') || (*p == '\\') || (*p == '-'))
+ p++;
+
+ if (!strncmp (p, __SHOW__, max (2, strlen(p))))
+ bIsShowOption = TRUE;
+ }
+
+ return(bIsShowOption);
+}
+
+/*
+ Return TRUE if input is /? option.
+ FALSE otherwise.
+ */
+int IsQuestionMark (int argc, char ** argv)
+{
+ int bIsQuestionMark = FALSE;
+ char * p;
+
+ if (argc == 2)
+ {
+ p = argv[1];
+ while ((*p == '/') || (*p == '\\') || (*p == '-'))
+ p++;
+
+ if (*p == '?')
+ bIsQuestionMark = TRUE;
+ }
+
+ return(bIsQuestionMark);
+}
+
+int IsNoOption (char * option)
+{
+ int bIsNoOption = FALSE;
+ char * p;
+
+ p = option;
+ while ((*p == '/') || (*p == '\\') || (*p == '-'))
+ p++;
+
+ if (!strncmp (p, __OPT_NO__, max (1, strlen(p))))
+ bIsNoOption = TRUE;
+
+ return(bIsNoOption);
+}
+
+/*
+ Return TRUE if the input match option or shortoption.
+ FALSE otherwise.
+ */
+int IsValidOption (char *input, char *option, char *shortoption)
+{
+ int bValideInput = FALSE;
+
+ while ((*input == '/') || (*input == '\\') || (*input == '-'))
+ input++;
+
+ if (!strcmp (input, shortoption))
+ {
+ bValideInput = TRUE;
+ }
+ else if (!strcmp (input, option))
+ {
+ bValideInput = TRUE;
+ }
+
+ return(bValideInput);
+}
+
+void GetJobNameFromArg (int argc, char ** argv, char *jobName)
+{
+ int i;
+ char *pEqual;
+
+ for (i = 0; i < argc; i++)
+ {
+ if (pEqual = strchr (argv[i], '='))
+ {
+ *pEqual = 0;
+
+ if (IsValidOption (argv[i], __JOB__, __SHORT_FOR_JOB__) &&
+ *(pEqual+1) != 0 &&
+ strlen (pEqual+1) < MAX_JOB_NAME_LEN)
+ strcpy (jobName, pEqual+1);
+
+ *pEqual = '=';
+ }
+ }
+
+ return;
+}
+
+int IsServerSpecified (int argc, char ** argv)
+{
+ int i;
+
+ for (i = 0; i < argc; i++)
+ {
+ if (IsValidOption (argv[i], __SERVER__, __SHORT_FOR_SERVER__))
+ return TRUE;
+ }
+
+ return FALSE;
+
+}
+
+/*
+ Initialize the capture flags with job config or default value.
+ */
+int InitCaptureParams (unsigned int conn,
+ char *jobName,
+ PCAPTURE_PARAMS pCaptureParams)
+{
+ PS_JOB_RECORD psJobRecord;
+ unsigned int iRet = 0;
+
+ // Get job configuration.
+ if (jobName[0] == 0)
+ {
+ // Get Default Job Name.
+ if ( Is40Server( conn ) )
+ {
+ iRet = PS40JobGetDefault( pCaptureParams->NDSCapture,
+ 0,
+ NULL,
+ jobName,
+ &psJobRecord );
+ }
+ else
+ {
+ iRet = PSJobGetDefault( conn,
+ 0,
+ NULL,
+ jobName,
+ &psJobRecord );
+ }
+
+ if ( iRet )
+ {
+ if (iRet == PS_ERR_OPENING_DB || iRet == PS_ERR_GETTING_DEFAULT)
+ {
+ pCaptureParams->nLPT = 1;
+ pCaptureParams->bannerUserName[0]=0;
+ pCaptureParams->serverName[0]=0;
+ pCaptureParams->queueName[0]=0;
+ pCaptureParams->filePath[0]=0;
+
+ pCaptureParams->captureFlagsRW.JobControlFlags = 0;
+ pCaptureParams->captureFlagsRW.TabSize = 8;
+ pCaptureParams->captureFlagsRW.NumCopies = 1;
+ pCaptureParams->captureFlagsRW.PrintFlags = DEFAULT_PRINT_FLAGS;
+ pCaptureParams->captureFlagsRW.FormName[0] = 0;
+ pCaptureParams->captureFlagsRW.FormType = 0;
+ pCaptureParams->captureFlagsRW.FlushCaptureTimeout = 0;
+ pCaptureParams->captureFlagsRW.FlushCaptureOnClose = 0;
+
+ strcpy (pCaptureParams->captureFlagsRW.BannerText, DEFAULT_BANNER_TEXT);
+ return(TRUE);
+ }
+ else
+ {
+ DisplayError (iRet, "PSJobGetDefault");
+ return(FALSE);
+ }
+ }
+ }
+ else
+ {
+ if ( Is40Server( conn ) )
+ {
+ iRet = PS40JobRead( pCaptureParams->NDSCapture,
+ NULL,
+ jobName,
+ &psJobRecord);
+ }
+ else
+ {
+ iRet = PSJobRead(conn,
+ NULL,
+ jobName,
+ &psJobRecord);
+ }
+ if ( iRet )
+ {
+ if ( ( iRet == PS_ERR_READING_RECORD) ||
+ ( iRet == PS_ERR_OPENING_DB) )
+ DisplayMessage(IDR_JOB_NOT_FOUND, jobName);
+ else
+ DisplayError (iRet, "PSJobRead");
+ return(FALSE);
+ }
+ }
+
+ pCaptureParams->captureFlagsRW.JobControlFlags = 0;
+ pCaptureParams->captureFlagsRW.TabSize = psJobRecord.TabSize;
+ pCaptureParams->captureFlagsRW.NumCopies = psJobRecord.Copies;
+
+ pCaptureParams->captureFlagsRW.PrintFlags =
+ ((psJobRecord.PrintJobFlag & PS_JOB_EXPAND_TABS)? 0 : CAPTURE_FLAG_EXPAND_TABS)
+ +((psJobRecord.PrintJobFlag & PS_JOB_NO_FORMFEED)? CAPTURE_FLAG_NO_FORMFEED : 0)
+ +((psJobRecord.PrintJobFlag & PS_JOB_NOTIFY)? CAPTURE_FLAG_NOTIFY : 0)
+ +((psJobRecord.PrintJobFlag & PS_JOB_PRINT_BANNER)? CAPTURE_FLAG_PRINT_BANNER : 0);
+
+ pCaptureParams->captureFlagsRW.FormType = 0;
+ pCaptureParams->captureFlagsRW.FlushCaptureTimeout = psJobRecord.TimeOutCount;
+ pCaptureParams->captureFlagsRW.FlushCaptureOnClose = !(psJobRecord.PrintJobFlag & PS_JOB_AUTO_END);
+
+ strcpy (pCaptureParams->captureFlagsRW.FormName, psJobRecord.FormName);
+ strcpy (pCaptureParams->captureFlagsRW.BannerText, (psJobRecord.BannerName[0] == 0)? DEFAULT_BANNER_TEXT : psJobRecord.BannerName);
+
+ pCaptureParams->nLPT = psJobRecord.LocalPrinter;
+ strcpy (pCaptureParams->bannerUserName, psJobRecord.Name);
+ if ( psJobRecord.PrintJobFlag & PS_JOB_ENV_DS ) {
+ strcpy (pCaptureParams->serverName, "");
+ if ( psJobRecord.PrintJobFlag & PS_JOB_DS_PRINTER )
+ GetPrinterDefaultQueue( pCaptureParams, psJobRecord.u.DSObjectName );
+ else
+ strcpy (pCaptureParams->queueName, psJobRecord.u.DSObjectName );
+ }
+ else {
+ strcpy (pCaptureParams->serverName, psJobRecord.u.NonDS.FileServer);
+ strcpy (pCaptureParams->queueName, psJobRecord.u.NonDS.PrintQueue);
+ }
+ pCaptureParams->filePath[0]=0;
+
+ return(TRUE);
+}
+
+int ReadArguments (int argc,
+ char ** argv,
+ char *jobName,
+ PCAPTURE_PARAMS pCaptureParams)
+{
+ int i, fValidOption = TRUE, fValidParam = TRUE;
+ char *pEqual = NULL;
+
+ for (i = 1; i < argc; i++)
+ {
+ if (IsNoOption(argv[i]))
+ {
+ if (i != argc - 1)
+ {
+ i++;
+
+ if (IsValidOption (argv[i], __NOTIFY__, __SHORT_FOR_NOTIFY__))
+ {
+ pCaptureParams->captureFlagsRW.PrintFlags &= (0xFF-CAPTURE_FLAG_NOTIFY);
+ }
+ else if (IsValidOption (argv[i], __AUTOENDCAP__, __SHORT_FOR_AUTOENDCAP__))
+ {
+ pCaptureParams->captureFlagsRW.FlushCaptureOnClose = 1;
+ }
+ else if (IsValidOption (argv[i], __TABS__, __SHORT_FOR_TABS__))
+ {
+ pCaptureParams->captureFlagsRW.PrintFlags &= (0xFF - CAPTURE_FLAG_EXPAND_TABS);
+ }
+ else if (IsValidOption (argv[i], __BANNER__, __SHORT_FOR_BANNER__))
+ {
+ pCaptureParams->captureFlagsRW.PrintFlags &= (0xFF - CAPTURE_FLAG_PRINT_BANNER);
+ }
+ else if (IsValidOption (argv[i], __FORMFEED__, __SHORT_FOR_FORMFEED__))
+ {
+ pCaptureParams->captureFlagsRW.PrintFlags |= CAPTURE_FLAG_NO_FORMFEED;
+ }
+ else
+ {
+ i--;
+ fValidOption = FALSE;
+ break;
+ }
+ }
+ else
+ {
+ fValidOption = FALSE;
+ break;
+ }
+ }
+ else if (IsValidOption (argv[i], __NOTIFY__, __SHORT_FOR_NOTIFY__))
+ {
+ pCaptureParams->captureFlagsRW.PrintFlags |= CAPTURE_FLAG_NOTIFY;
+ }
+ else if (IsValidOption (argv[i], __NONOTIFY__, __SHORT_FOR_NONOTIFY__))
+ {
+ pCaptureParams->captureFlagsRW.PrintFlags &= (0xFF - CAPTURE_FLAG_NOTIFY);
+ }
+ else if (IsValidOption (argv[i], __AUTOENDCAP__, __SHORT_FOR_AUTOENDCAP__))
+ {
+ pCaptureParams->captureFlagsRW.FlushCaptureOnClose = 0;
+ }
+ else if (IsValidOption (argv[i], __NOAUTOENDCAP__, __SHORT_FOR_NOAUTOENDCAP__))
+ {
+ pCaptureParams->captureFlagsRW.FlushCaptureOnClose = 1;
+ }
+ else if (IsValidOption (argv[i], __NOTABS__, __SHORT_FOR_NOTABS__))
+ {
+ pCaptureParams->captureFlagsRW.PrintFlags &= (0xFF - CAPTURE_FLAG_EXPAND_TABS);
+ }
+ else if (IsValidOption (argv[i], __NOBANNER__, __SHORT_FOR_NOBANNER__))
+ {
+ pCaptureParams->captureFlagsRW.PrintFlags &= (0xFF - CAPTURE_FLAG_PRINT_BANNER);
+ }
+ else if (IsValidOption (argv[i], __FORMFEED__, __SHORT_FOR_FORMFEED__))
+ {
+ pCaptureParams->captureFlagsRW.PrintFlags &= (0xFF - CAPTURE_FLAG_NO_FORMFEED);
+ }
+ else if (IsValidOption (argv[i], __NOFORMFEED__, __SHORT_FOR_NOFORMFEED__))
+ {
+ pCaptureParams->captureFlagsRW.PrintFlags |= CAPTURE_FLAG_NO_FORMFEED;
+ }
+ else if (IsValidOption (argv[i], __KEEP__, __SHORT_FOR_KEEP__))
+ {
+ pCaptureParams->captureFlagsRW.PrintFlags |= CAPTURE_FLAG_KEEP;
+ }
+ else
+ {
+ // All other valid options should have '=' sign in it.
+ // Except for LX LPX LPTX
+ //
+ pEqual = strchr (argv[i], '=');
+
+ // Optionally a ':' works too
+ if (pEqual == NULL ) {
+ pEqual = strchr (argv[i], ':');
+ }
+
+ if (pEqual != NULL)
+ *pEqual = 0;
+
+ if (IsValidOption (argv[i], __TIMEOUT__, __SHORT_FOR_TIMEOUT__))
+ {
+ if (pEqual == NULL || *(pEqual+1) == 0)
+ {
+ DisplayMessage(IDR_TIME_OUT_EXPECTED);
+ fValidParam = FALSE;
+ break;
+ }
+
+ pCaptureParams->captureFlagsRW.FlushCaptureTimeout = atoi (pEqual+1);
+
+ if (pCaptureParams->captureFlagsRW.FlushCaptureTimeout > 1000)
+ {
+ DisplayMessage(IDR_TIMEOUT_OUTOF_RANGE);
+ fValidParam = FALSE;
+ break;
+ }
+ }
+ else if (IsValidOption (argv[i], __LOCAL__, __SHORT_FOR_LOCAL__))
+ {
+ if (pEqual == NULL || *(pEqual+1) == 0)
+ {
+ DisplayMessage(IDR_LPT_NUMBER_EXPECTED);
+ fValidParam = FALSE;
+ break;
+ }
+
+ pCaptureParams->nLPT = (unsigned char) atoi (pEqual+1);
+
+ if (pCaptureParams->nLPT < 1 || pCaptureParams->nLPT > 3)
+ {
+ DisplayMessage(IDR_INVALID_LPT_NUMBER);
+ fValidParam = FALSE;
+ break;
+ }
+ }
+ else if (IsValidOption (argv[i], __LOCAL_3__, __LOCAL_2__))
+ {
+ if (pEqual == NULL || *(pEqual+1) == 0)
+ {
+ DisplayMessage(IDR_LPT_NUMBER_EXPECTED);
+ fValidParam = FALSE;
+ break;
+ }
+
+ pCaptureParams->nLPT = (unsigned char) atoi (pEqual+1);
+
+ if (pCaptureParams->nLPT < 1 || pCaptureParams->nLPT > 3)
+ {
+ DisplayMessage(IDR_INVALID_LPT_NUMBER);
+ fValidParam = FALSE;
+ break;
+ }
+ }
+ else if (IsValidOption (argv[i], __JOB__, __SHORT_FOR_JOB__))
+ {
+ if (pEqual == NULL ||
+ *(pEqual+1) == 0 ||
+ strlen (pEqual+1) > MAX_JOB_NAME_LEN - 1)
+ {
+ fValidOption = FALSE;
+ break;
+ }
+ strcpy (jobName, pEqual+1);
+ }
+ else if (IsValidOption (argv[i], __SERVER__, __SHORT_FOR_SERVER__))
+ {
+ if (pEqual == NULL ||
+ *(pEqual+1) == 0 ||
+ strlen (pEqual+1) > MAX_NAME_LEN - 1)
+ {
+ fValidOption = FALSE;
+ break;
+ }
+ pCaptureParams->NDSCapture = FALSE;
+ strcpy (pCaptureParams->serverName, pEqual+1);
+ }
+ else if (IsValidOption (argv[i], __QUEUE__, __SHORT_FOR_QUEUE__))
+ {
+ if (pEqual == NULL ||
+ *(pEqual+1) == 0 ||
+ strlen (pEqual+1) > MAX_QUEUE_NAME_LEN - 1) //compatible.
+ {
+ fValidOption = FALSE;
+ break;
+ }
+ strcpy (pCaptureParams->queueName, pEqual+1);
+ }
+ else if (IsValidOption (argv[i], __PRINTER__, __SHORT_FOR_PRINTER__))
+ {
+ if (pEqual == NULL ||
+ *(pEqual+1) == 0 ||
+ !pCaptureParams->NDSCapture ||
+ strlen (pEqual+1) > MAX_QUEUE_NAME_LEN - 1) //compatible.
+ {
+ fValidOption = FALSE;
+ break;
+ }
+ GetPrinterDefaultQueue( pCaptureParams, pEqual+1 );
+ }
+ else if (IsValidOption (argv[i], __CREATE__, __SHORT_FOR_CREATE__))
+ {
+ if (pEqual != NULL) //compatible.
+ {
+ if (strlen (pEqual+1) > _MAX_PATH - 1)
+ {
+ DisplayMessage(IDR_INVALID_PATH_NAME, pEqual+1);
+ fValidParam = FALSE;
+ break;
+ }
+ strcpy (pCaptureParams->filePath, pEqual+1);
+ }
+ }
+ else if (IsValidOption (argv[i], __FORM__, __SHORT_FOR_FORM__))
+ {
+ int j = 1;
+ int bAllNumbers = TRUE;
+
+ if (pEqual == NULL || *(pEqual+1) == 0)
+ {
+ DisplayMessage(IDR_FORM_EXPECTED);
+ fValidParam = FALSE;
+ break;
+ }
+
+ if (strlen (pEqual+1) > 3) // Only allow 3 digits number.
+ {
+ DisplayMessage(IDR_INVALID_FORM_NAME, pEqual+1);
+ fValidParam = FALSE;
+ break;
+ }
+
+ while (*(pEqual+j) != 0)
+ {
+ if (!isdigit (*(pEqual+j)))
+ {
+ bAllNumbers = FALSE;
+ break;
+ }
+ j++;
+ }
+
+ if (bAllNumbers)
+ {
+ pCaptureParams->captureFlagsRW.FormType = atoi (pEqual+1);
+
+ if (pCaptureParams->captureFlagsRW.FormType > 255)
+ {
+ DisplayMessage(IDR_INVALID_FORM_TYPE);
+ fValidParam = FALSE;
+ break;
+ }
+ }
+ else
+ {
+ DisplayMessage(IDR_INVALID_FORM_NAME, pEqual+1);
+ fValidParam = FALSE;
+ break;
+ }
+ }
+ else if (IsValidOption (argv[i], __COPIES__, __SHORT_FOR_COPIES__))
+ {
+ if (pEqual == NULL || *(pEqual+1) == 0)
+ {
+ DisplayMessage(IDR_COPIES_EXPECTED);
+ fValidParam = FALSE;
+ break;
+ }
+
+ pCaptureParams->captureFlagsRW.NumCopies = atoi (pEqual+1);
+
+ if (pCaptureParams->captureFlagsRW.NumCopies < 1 ||
+ pCaptureParams->captureFlagsRW.NumCopies > 255)
+ {
+ DisplayMessage(IDR_COPIES_OUTOF_RANGE);
+ fValidParam = FALSE;
+ break;
+ }
+ }
+ else if (IsValidOption (argv[i], __TABS__, __SHORT_FOR_TABS__))
+ {
+ if (pEqual == NULL || *(pEqual+1) == 0)
+ {
+ DisplayMessage(IDR_TAB_SIZE_EXPECTED);
+ fValidParam = FALSE;
+ break;
+ }
+
+ pCaptureParams->captureFlagsRW.TabSize = (BYTE) atoi (pEqual+1);
+
+ if (pCaptureParams->captureFlagsRW.TabSize < 1 ||
+ pCaptureParams->captureFlagsRW.TabSize > 18)
+ {
+ DisplayMessage(IDR_TABSIZE_OUTOF_RANGE);
+ fValidParam = FALSE;
+ break;
+ }
+
+ pCaptureParams->captureFlagsRW.PrintFlags |= CAPTURE_FLAG_EXPAND_TABS;
+ }
+ else if (IsValidOption (argv[i], __NAME__, __SHORT_FOR_NAME__))
+ {
+ if (pEqual == NULL ||
+ *(pEqual+1) == 0 ||
+ strlen (pEqual+1) > MAX_BANNER_USER_NAME - 1)
+ {
+ fValidOption = FALSE;
+ break;
+ }
+ strcpy (pCaptureParams->bannerUserName, pEqual+1);
+ }
+ else if (IsValidOption (argv[i], __BANNER__, __SHORT_FOR_BANNER__))
+ {
+ if (pEqual != NULL)
+ {
+ if (strlen (pEqual+1) > MAX_BANNER_USER_NAME - 1)
+ {
+ DisplayMessage(IDR_INVALID_BANNER, pEqual+1);
+ fValidParam = FALSE;
+ break;
+ }
+ strcpy (pCaptureParams->captureFlagsRW.BannerText, pEqual+1);
+ pCaptureParams->captureFlagsRW.PrintFlags |= CAPTURE_FLAG_PRINT_BANNER;
+ }
+ }
+ //
+ // Kludge for LX LPX LPTX parameters
+ // Note that L:X L=X, etc are also valid
+ //
+ else if ( ( pEqual == NULL ) && ( *(argv[i]) == 'L' ) ) {
+ pEqual = argv[i];
+ pEqual++;
+ if ( *pEqual == 'P' ) {
+ pEqual++;
+ if ( *pEqual == 'T' ) {
+ pEqual++;
+ }
+ }
+ pCaptureParams->nLPT = (unsigned char) atoi (pEqual);
+
+ if (pCaptureParams->nLPT < 1 || pCaptureParams->nLPT > 3)
+ {
+ DisplayMessage(IDR_INVALID_LPT_NUMBER);
+ fValidParam = FALSE;
+ break;
+ }
+
+ }
+ else
+ {
+ fValidOption = FALSE;
+ break;
+ }
+ }
+ }
+
+ if (fValidOption && fValidParam)
+ {
+ sprintf (pCaptureParams->captureFlagsRW.JobDescription, __JOB_DESCRIPTION__, pCaptureParams->nLPT);
+ return(TRUE);
+ }
+ else
+ {
+ if (!fValidOption)
+ {
+ if (pEqual)
+ *pEqual = '=';
+ DisplayMessage(IDR_UNKNOW_FLAG, argv[i]);
+ }
+ DisplayMessage(IDR_CAPTURE_USAGE);
+ return(FALSE);
+ }
+}
+
+/*
+ Show the capture setting.
+ */
+void ShowCapture(void)
+{
+ unsigned int iRet = 0;
+ int i;
+ char * queueName;
+
+ for (i = 1; i <= 3; i++ )
+ {
+ NETWARE_CAPTURE_FLAGS_RW captureFlagsRW;
+ NETWARE_CAPTURE_FLAGS_RO captureFlagsRO;
+
+ if (iRet = GetCaptureFlags ((unsigned char)i,
+ &captureFlagsRW,
+ &captureFlagsRO))
+ {
+ DisplayError (iRet, "GetCaptureFlags");
+ }
+ else
+ {
+ char *serverName;
+ WCHAR timeOut[256];
+ WCHAR tabs[256];
+
+ if (captureFlagsRO.LPTCaptureFlag == 0)
+ {
+ DisplayMessage(IDR_NOT_ACTIVE, i);
+ }
+ else
+ {
+ serverName = captureFlagsRO.ServerName;
+
+ if ( !CaptureStringsLoaded ) {
+ (void) LoadString( NULL, IDR_DISABLED, __DISABLED__, 256 );
+ (void) LoadString( NULL, IDR_ENABLED, __ENABLED__, 256 );
+ (void) LoadString( NULL, IDR_YES, __YES__, 256 );
+ (void) LoadString( NULL, IDR_NO, __NO__, 256 );
+ (void) LoadString( NULL, IDR_SECONDS, __SECONDS__, 256 );
+ (void) LoadString( NULL, IDR_CONVERT_TO_SPACE, __CONVERT_TO_SPACE__, 256 );
+ (void) LoadString( NULL, IDR_NO_CONVERSION, __NO_CONVERSION__, 256 );
+ (void) LoadString( NULL, IDR_NOTIFY_USER, __NOTIFY_USER__, 256 );
+ (void) LoadString( NULL, IDR_NOT_NOTIFY_USER, __NOT_NOTIFY_USER__, 256 );
+ (void) LoadString( NULL, IDR_NONE, __NONE__, 256 );
+ }
+
+ if (captureFlagsRW.FlushCaptureTimeout)
+ wsprintf (timeOut, __SECONDS__, captureFlagsRW.FlushCaptureTimeout);
+ else
+ (void) LoadString( NULL, IDR_DISABLED, timeOut, 256 );
+
+ if (captureFlagsRW.PrintFlags & CAPTURE_FLAG_EXPAND_TABS)
+ wsprintf(tabs, __CONVERT_TO_SPACE__, captureFlagsRW.TabSize);
+ else
+ (void) LoadString( NULL, IDR_NO_CONVERSION, tabs, 256 );
+
+
+ queueName = captureFlagsRO.QueueName;
+
+ if ( fNDS )
+ {
+ if ( captureFlagsRW.PrintFlags & CAPTURE_FLAG_PRINT_BANNER )
+ {
+ DisplayMessage(IDR_LPT_STATUS_NDS, i, queueName,
+ captureFlagsRW.PrintFlags & CAPTURE_FLAG_NOTIFY? __NOTIFY_USER__ : __NOT_NOTIFY_USER__, //Notify
+ __DISABLED__, //Capture Defaults
+ captureFlagsRW.FlushCaptureOnClose? __DISABLED__ : __ENABLED__, //AutoEndCap
+ captureFlagsRW.BannerText, //Banner
+ captureFlagsRW.PrintFlags & CAPTURE_FLAG_NO_FORMFEED? __NO__ : __YES__, //Form Feed
+ captureFlagsRW.NumCopies, //Copies
+ tabs, //Tabs
+ captureFlagsRW.FormType, timeOut); //Timeout Counts
+ }
+ else
+ {
+ DisplayMessage(IDR_LPT_STATUS_NO_BANNER_NDS, i, queueName,
+ captureFlagsRW.PrintFlags & CAPTURE_FLAG_NOTIFY? __NOTIFY_USER__ : __NOT_NOTIFY_USER__, //Notify
+ __DISABLED__, //Capture Defaults
+ captureFlagsRW.FlushCaptureOnClose? __DISABLED__ : __ENABLED__, //AutoEndCap
+ __NONE__, //Banner
+ captureFlagsRW.PrintFlags & CAPTURE_FLAG_NO_FORMFEED? __NO__ : __YES__, //Form Feed
+ captureFlagsRW.NumCopies, //Copies
+ tabs, //Tabs
+ captureFlagsRW.FormType, timeOut); //Timeout Counts
+ }
+ }
+ else
+ {
+ if ( captureFlagsRW.PrintFlags & CAPTURE_FLAG_PRINT_BANNER )
+ {
+ DisplayMessage(IDR_LPT_STATUS, i, serverName, queueName,
+ captureFlagsRW.PrintFlags & CAPTURE_FLAG_NOTIFY? __NOTIFY_USER__ : __NOT_NOTIFY_USER__, //Notify
+ __DISABLED__, //Capture Defaults
+ captureFlagsRW.FlushCaptureOnClose? __DISABLED__ : __ENABLED__, //AutoEndCap
+ captureFlagsRW.BannerText, //Banner
+ captureFlagsRW.PrintFlags & CAPTURE_FLAG_NO_FORMFEED? __NO__ : __YES__, //Form Feed
+ captureFlagsRW.NumCopies, //Copies
+ tabs, //Tabs
+ captureFlagsRW.FormType, timeOut); //Timeout Counts
+ }
+ else
+ {
+ DisplayMessage(IDR_LPT_STATUS_NO_BANNER, i, serverName, queueName,
+ captureFlagsRW.PrintFlags & CAPTURE_FLAG_NOTIFY? __NOTIFY_USER__ : __NOT_NOTIFY_USER__, //Notify
+ __DISABLED__, //Capture Defaults
+ captureFlagsRW.FlushCaptureOnClose? __DISABLED__ : __ENABLED__, //AutoEndCap
+ __NONE__, //Banner
+ captureFlagsRW.PrintFlags & CAPTURE_FLAG_NO_FORMFEED? __NO__ : __YES__, //Form Feed
+ captureFlagsRW.NumCopies, //Copies
+ tabs, //Tabs
+ captureFlagsRW.FormType, timeOut); //Timeout Counts
+ }
+ }
+ }
+ }
+ }
+}
+
+int CStartCapture(unsigned int conn,
+ PCAPTURE_PARAMS pCaptureParams)
+{
+ unsigned int iRet = 0;
+ unsigned char FullPath[255 + NCP_VOLUME_LENGTH];
+ unsigned char DirPath[255];
+ unsigned char VolumeName[NCP_VOLUME_LENGTH];
+ WORD status;
+
+ // Get connection handle.
+ if ( !pCaptureParams->NDSCapture )
+ {
+ if ( pCaptureParams->serverName[0] == 0 )
+ {
+ if (iRet = GetFileServerName (conn, pCaptureParams->serverName))
+ {
+ DisplayError (iRet, "GetFileServerName");
+ return (1);
+ }
+ }
+ else
+ {
+ if (iRet = GetConnectionHandle (pCaptureParams->serverName, &conn))
+ {
+ if ( iRet = NTLoginToFileServer( pCaptureParams->serverName, "GUEST", "" ) ) {
+ switch ( iRet ) {
+ case ERROR_INVALID_PASSWORD:
+ case ERROR_NO_SUCH_USER:
+ case ERROR_CONNECTION_COUNT_LIMIT:
+ case ERROR_LOGIN_TIME_RESTRICTION:
+ case ERROR_LOGIN_WKSTA_RESTRICTION:
+ case ERROR_ACCOUNT_DISABLED:
+ case ERROR_PASSWORD_EXPIRED:
+ case ERROR_REMOTE_SESSION_LIMIT_EXCEEDED:
+ DisplayMessage( IDR_CAPTURE_FAILED, pCaptureParams->queueName );
+ DisplayMessage( IDR_ACCESS_DENIED );
+ break;
+ default:
+ DisplayMessage(IDR_SERVER_NOT_FOUND, pCaptureParams->serverName);
+ }
+ return (1);
+ }
+ else {
+ if (iRet = GetConnectionHandle (pCaptureParams->serverName, &conn)) {
+ DisplayMessage(IDR_SERVER_NOT_FOUND, pCaptureParams->serverName);
+ return (1);
+ }
+ }
+ }
+ }
+ }
+
+ if (pCaptureParams->filePath[0] != 0)
+ {
+ DisplayMessage(IDR_FILE_CAPTURE_UNSUPPORTED);
+ return (1);
+ }
+ else
+ {
+ if (pCaptureParams->queueName[0] == 0)
+ {
+ if ( pCaptureParams->NDSCapture )
+ {
+ DisplayMessage(IDR_NO_QUEUE);
+ return (1);
+ }
+ else
+ {
+ // Try to get the default queue ID and name.
+ if (iRet = GetDefaultPrinterQueue (conn, pCaptureParams->serverName, pCaptureParams->queueName))
+ {
+ DisplayMessage(IDR_NO_PRINTERS, pCaptureParams->serverName);
+ return (1);
+ }
+ }
+ }
+ // Start queue capture.
+ if ( pCaptureParams->NDSCapture )
+ {
+ char szCanonName[MAX_QUEUE_NAME_LEN];
+
+ // Get the full name of the printer queue
+ // The redirectory wants root based names for
+ // everything.
+
+ iRet = NDSCanonicalizeName( pCaptureParams->queueName,
+ szCanonName,
+ NDS_NAME_CHARS,
+ TRUE );
+
+ if ( iRet && ( pCaptureParams->queueName[0] != '.' ) )
+ {
+ // If that didn't work, see if it's a root
+ // based name without the leading period.
+
+ strcpy( szCanonName, "." );
+ strcat( szCanonName, pCaptureParams->queueName );
+
+ iRet = NDSCanonicalizeName( szCanonName,
+ szCanonName,
+ MAX_QUEUE_NAME_LEN,
+ TRUE );
+ }
+
+ if ( iRet )
+ iRet = ERROR_BAD_NETPATH;
+ else
+ iRet = StartQueueCapture ( conn,
+ pCaptureParams->nLPT,
+ NDSTREE,
+ szCanonName );
+ }
+ else
+ {
+ iRet = StartQueueCapture (conn,
+ pCaptureParams->nLPT,
+ pCaptureParams->serverName,
+ pCaptureParams->queueName);
+ }
+
+ if ( iRet )
+ {
+ switch ( iRet ) {
+ case ERROR_ACCESS_DENIED:
+ case ERROR_INVALID_PASSWORD:
+ DisplayMessage (IDR_CAPTURE_FAILED, pCaptureParams->queueName);
+ DisplayMessage (IDR_ACCESS_DENIED);
+ break;
+ case ERROR_EXTENDED_ERROR:
+ NTPrintExtendedError();
+ break;
+ case ERROR_BAD_NET_NAME:
+ case ERROR_BAD_NETPATH:
+ if ( pCaptureParams->NDSCapture )
+ DisplayMessage (IDR_NDSQUEUE_NOT_EXIST,
+ pCaptureParams->queueName,
+ pCaptureParams->serverName );
+ else
+ DisplayMessage (IDR_QUEUE_NOT_EXIST,
+ pCaptureParams->queueName,
+ pCaptureParams->serverName );
+ break;
+ default:
+ DisplayError (iRet, "StartQueueCapture");
+ break;
+ }
+ return (1);
+ }
+ }
+
+ if (pCaptureParams->captureFlagsRW.FlushCaptureOnClose == 1)
+ DisplayMessage(IDR_NO_AUTOENDCAP);
+
+ if ( pCaptureParams->NDSCapture )
+ DisplayMessage(IDR_NDSSUCCESS_QUEUE, pCaptureParams->nLPT,
+ pCaptureParams->queueName);
+ else
+ DisplayMessage(IDR_SUCCESS_QUEUE, pCaptureParams->nLPT,
+ pCaptureParams->queueName, pCaptureParams->serverName);
+
+ return(0);
+}
+
+/*
+ * Given an NDS printer name, fill in the default queue name
+ *
+ */
+int
+GetPrinterDefaultQueue( PCAPTURE_PARAMS pCaptureParams,
+ PBYTE PrinterName )
+{
+ BYTE Fixup[ MAX_QUEUE_NAME_LEN];
+ PBYTE ptr;
+ unsigned int iRet;
+
+ iRet = NDSGetProperty ( PrinterName, "Default Queue",
+ pCaptureParams->queueName,
+ MAX_QUEUE_NAME_LEN,
+ NULL );
+ if ( iRet )
+ {
+ /*
+ * Strip off the . in front and add context at end
+ */
+ ptr = RemoveSpaces (PrinterName);
+ if ( *ptr == '.' )
+ {
+ ptr++;
+ strncpy( Fixup, ptr, MAX_QUEUE_NAME_LEN );
+ }
+ else
+ {
+ strncpy( Fixup, ptr, MAX_QUEUE_NAME_LEN );
+ if ( Fixup[strlen(Fixup)-1] != '.' )
+ {
+ strcat( Fixup, "." );
+ }
+ (void) NDSGetContext( Fixup + strlen(Fixup),
+ MAX_QUEUE_NAME_LEN - strlen(Fixup) );
+ }
+ iRet = NDSGetProperty ( Fixup, "Default Queue",
+ pCaptureParams->queueName,
+ MAX_QUEUE_NAME_LEN,
+ NULL );
+ if ( !iRet )
+ ConvertUnicodeToAscii( pCaptureParams->queueName );
+ }
+
+ return iRet;
+}
diff --git a/private/nw/nwscript/common.c b/private/nw/nwscript/common.c
new file mode 100644
index 000000000..019ff87e5
--- /dev/null
+++ b/private/nw/nwscript/common.c
@@ -0,0 +1,400 @@
+
+/*************************************************************************
+*
+* COMMON.C
+*
+* Miscellaneous routines for scripts, ported from DOS
+*
+* Copyright (c) 1995 Microsoft Corporation
+*
+* $Log: N:\NT\PRIVATE\NW4\NWSCRIPT\VCS\COMMON.C $
+*
+* Rev 1.3 10 Apr 1996 14:21:52 terryt
+* Hotfix for 21181hq
+*
+* Rev 1.3 12 Mar 1996 19:52:40 terryt
+* Relative NDS names and merge
+*
+* Rev 1.2 24 Jan 1996 17:14:54 terryt
+* Common read string routine
+*
+* Rev 1.1 22 Dec 1995 14:23:56 terryt
+* Add Microsoft headers
+*
+* Rev 1.0 15 Nov 1995 18:06:36 terryt
+* Initial revision.
+*
+* Rev 1.2 25 Aug 1995 16:22:18 terryt
+* Capture support
+*
+* Rev 1.1 26 Jul 1995 14:17:06 terryt
+* Clean up comments
+*
+* Rev 1.0 15 May 1995 19:10:18 terryt
+* Initial revision.
+*
+*************************************************************************/
+#include "common.h"
+
+/*
+ Used by DisplayMapping() only.
+ Return search number if the drive is a search drive.
+ Return 0 if the drive is not a search drive.
+ */
+int IsSearchDrive(int driveNum)
+{
+ int searchNum = 1;
+ char *path;
+
+ path = NWGetPath();
+
+ while (*path != 0)
+ {
+ if ((*path - 'A' + 1 == driveNum) &&
+ (*(path+1) == ':'))
+ {
+ return searchNum;
+ }
+
+ if (path = strchr (path, ';'))
+ {
+ path++;
+ searchNum++;
+ }
+ else
+ return(0);
+ }
+
+ return(0);
+}
+
+
+/*
+ Get path enviroment variable. This returns the pointer to the
+ path in the parent enviroment segment.
+ */
+char * NWGetPath(void)
+{
+ //
+ // On NT we can't change or get the parent's environment this way
+ //
+ return( getenv("PATH") );
+}
+
+/*
+ Return TRUE if the memory block is large enough for adding new
+ search path. FALSE otherwise.
+ */
+int MemorySegmentLargeEnough (int nInsertByte)
+{
+ return TRUE;
+}
+
+/*
+ Display drive maps info.
+ */
+void DisplayMapping(void)
+{
+ unsigned int iRet = 0;
+ int i;
+ WORD status;
+ char rootPath[MAX_PATH_LEN], relativePath[MAX_PATH_LEN];
+ char *envPath, *tokenPath;
+ char *path;
+ DWORD LocalDrives;
+ DWORD NonSearchDrives;
+ char sLocalDrives[26*2+5];
+ char * sptr;
+
+ // Don't delete this line. This is for fixing bug 1176.
+ DisplayMessage(IDR_NEWLINE);
+
+ LocalDrives = 0;
+ NonSearchDrives = 0;
+
+ // Collect local drives and search drives
+ for (i = 1; i <= 26; i++) {
+ status = NTNetWareDriveStatus( (unsigned short)(i-1) );
+ if ((status & NETWARE_LOCAL_DRIVE) && !(status & NETWARE_NETWORK_DRIVE))
+ LocalDrives |= ( 1 << (i-1) );
+ else if ((status & NETWARE_NETWORK_DRIVE) && (!IsSearchDrive(i)) )
+ {
+ if (status & NETWARE_NETWARE_DRIVE)
+ NonSearchDrives |= ( 1 << (i-1) );
+ else
+ {
+ //For NetWare compatibility
+ LocalDrives |= ( 1 << (i-1) );
+ }
+ }
+ }
+
+ // Print out local drives
+ if ( LocalDrives ) {
+ sptr = &sLocalDrives[0];
+ for (i = 1; i <= 26; i++)
+ {
+ if ( LocalDrives & ( 1 << (i - 1) ) ) {
+ *sptr++ = 'A' + i - 1;
+ *sptr++ = ',';
+ }
+ }
+ sptr--;
+ *sptr = '\0';
+ DisplayMessage(IDR_ALL_LOCAL_DRIVES, sLocalDrives);
+ }
+
+ // Print out non search drives.
+ for (i = 1; i <= 26; i++)
+ {
+ if ( NonSearchDrives & ( 1 << (i - 1) ) ) {
+
+ if (iRet = GetDriveStatus ((unsigned short)i,
+ NETWARE_FORMAT_SERVER_VOLUME,
+ &status,
+ NULL,
+ rootPath,
+ relativePath,
+ NULL))
+ {
+ DisplayError (iRet, "GetDriveStatus");
+ }
+ else
+ {
+ DisplayMessage(IDR_NETWARE_DRIVE, 'A'+i-1, rootPath, relativePath);
+ }
+ }
+ }
+
+ // Print out dashed line as seperator between non search drives
+ // and search drives.
+ DisplayMessage(IDR_DASHED_LINE);
+
+ // Get the PATH environment variable.
+ path = NWGetPath();
+ if ((envPath = malloc (strlen (path) + 1)) == NULL)
+ {
+ DisplayMessage(IDR_NOT_ENOUGH_MEMORY);
+ return;
+ }
+
+ strcpy (envPath, path);
+
+ tokenPath = strtok (envPath, PATH_SEPERATOR);
+
+ // Print out search drvies.
+ for (i = 1; tokenPath != NULL; i++)
+ {
+ if (tokenPath[1] == ':')
+ {
+ if (iRet = GetDriveStatus ((unsigned short)(toupper(tokenPath[0])-'A'+1),
+ NETWARE_FORMAT_SERVER_VOLUME,
+ &status,
+ NULL,
+ rootPath,
+ relativePath,
+ NULL))
+ {
+ DisplayError (iRet, "GetDriveStatus");
+ }
+ else
+ {
+ if (status & NETWARE_NETWARE_DRIVE)
+ DisplayMessage(IDR_NETWARE_SEARCH, i, tokenPath, rootPath, relativePath);
+ else
+ DisplayMessage(IDR_LOCAL_SEARCH, i, tokenPath);
+ }
+ }
+ else
+ {
+ // Path is specified without drive letter.
+ DisplayMessage(IDR_LOCAL_SEARCH, i, tokenPath);
+ }
+
+ tokenPath = strtok (NULL, PATH_SEPERATOR);
+ }
+
+ free (envPath);
+}
+
+/*****************************************************************************
+ * *
+ * GetString *
+ * *
+ * *
+ * entry: pointer to buffer *
+ * length of buffer *
+ * *
+ * exit: length of string *
+ * *
+ *****************************************************************************/
+
+int
+GetString( char * pBuffer, int ByteCount )
+{
+ char * pString = pBuffer;
+ char ch;
+
+ if ( ByteCount > 0 )
+ ByteCount--;
+
+ for( ;; ) {
+
+ switch ( ch = (char) _getch() ) {
+
+ case '\r' :
+ *pString++ = '\0';
+ putchar( '\n' );
+ return( strlen( pBuffer ) );
+
+ case '\b' :
+ if ( pString != pBuffer ) {
+ ByteCount++;
+ pString--;
+ printf( "\b \b" );
+ }
+ break;
+
+ default :
+ if ( ByteCount > 0 && ch >= 0x20 && ch < 0x80 ) {
+ *pString++ = ch;
+ ByteCount--;
+ putchar( ch );
+ }
+ break;
+ }
+
+ }
+ fflush(stdin);
+}
+
+/*
+ Read user or server name from the keyboard input.
+ Return TRUE if user typed in a username
+ FALSE otherwise.
+ */
+int ReadName (char * Name)
+{
+ memset( Name, 0, MAX_NAME_LEN );
+
+ if ( 0 == GetString( Name, MAX_NAME_LEN ) )
+ return FALSE;
+
+ _strupr(Name);
+ return TRUE;
+}
+
+
+
+/*
+ Try to log the user in.
+ Return error code. 0 is success.
+ */
+int Login( char *UserName,
+ char *ServerName,
+ char *Password,
+ int bReadPassword)
+{
+ unsigned int iRet = 0;
+
+ // Try log the user in with no password first.
+ iRet = NTLoginToFileServer( ServerName,
+ UserName,
+ Password);
+
+ if (iRet == ERROR_INVALID_PASSWORD && bReadPassword)
+ {
+ // wrong password. ask for passowrd. and try login with
+ // the input password.
+ DisplayMessage(IDR_PASSWORD, UserName, ServerName);
+
+ ReadPassword (Password);
+
+ iRet = NTLoginToFileServer( ServerName,
+ UserName,
+ Password);
+ }
+
+ switch(iRet)
+ {
+ case NO_ERROR: // ok
+ DisplayMessage(IDR_ATTACHED, ServerName);
+ break;
+
+ case ERROR_INVALID_PASSWORD: // wrong password.
+ case ERROR_NO_SUCH_USER: // no such user.
+ DisplayMessage(IDR_SERVER_USER, ServerName, UserName);
+ DisplayMessage(IDR_ACCESS_DENIED);
+ break;
+
+ case ERROR_CONNECTION_COUNT_LIMIT: // concurrent connection restriction.
+ DisplayMessage(IDR_SERVER_USER, ServerName, UserName);
+ DisplayMessage(IDR_LOGIN_DENIED_NO_CONNECTION);
+ break;
+
+ case ERROR_LOGIN_TIME_RESTRICTION: // time restriction.
+ DisplayMessage(IDR_SERVER_USER, ServerName, UserName);
+ DisplayMessage(IDR_UNAUTHORIZED_LOGIN_TIME);
+ break;
+
+ case ERROR_LOGIN_WKSTA_RESTRICTION: // station restriction.
+ DisplayMessage(IDR_SERVER_USER, ServerName, UserName);
+ DisplayMessage(IDR_UNAUTHORIZED_LOGIN_STATION);
+ break;
+
+ case ERROR_ACCOUNT_DISABLED:
+ DisplayMessage(IDR_SERVER_USER, ServerName, UserName);
+ DisplayMessage(IDR_ACCOUNT_DISABLED);
+ break;
+
+ case ERROR_PASSWORD_EXPIRED: // password expired and no grace login left.
+ DisplayMessage(IDR_SERVER_USER, ServerName, UserName);
+ DisplayMessage(IDR_PASSWORD_EXPRIED_NO_GRACE);
+ break;
+
+ case ERROR_REMOTE_SESSION_LIMIT_EXCEEDED:
+ // Server rejected access
+ DisplayMessage(IDR_CONNECTION_REFUSED);
+ break;
+
+ case ERROR_EXTENDED_ERROR:
+ NTPrintExtendedError();
+ break;
+
+ default :
+ DisplayError(iRet,"LoginToFileServer");
+ break;
+ }
+
+ return(iRet);
+}
+
+int CAttachToFileServer(char *ServerName, unsigned int *pConn, int * pbAlreadyAttached)
+{
+ unsigned int iRet = 0;
+
+ if (pbAlreadyAttached != NULL)
+ *pbAlreadyAttached = FALSE;
+
+ // Validate the server name.
+ iRet = AttachToFileServer(ServerName,pConn);
+
+ switch (iRet)
+ {
+ case 0: // OK
+ break;
+
+ case 0x8800 : // Already atached.
+ if (pbAlreadyAttached != NULL)
+ *pbAlreadyAttached = TRUE;
+
+ iRet = GetConnectionHandle (ServerName, pConn);
+ break;
+
+ default:
+ DisplayMessage(IDR_NO_RESPONSE, ServerName);
+ break;
+ }
+
+ return(iRet);
+}
diff --git a/private/nw/nwscript/date.c b/private/nw/nwscript/date.c
new file mode 100644
index 000000000..1533d1f19
--- /dev/null
+++ b/private/nw/nwscript/date.c
@@ -0,0 +1,78 @@
+/*************************************************************************
+*
+* DATE.C
+*
+* NT date routine
+*
+* Copyright (c) 1995 Microsoft Corporation
+*
+* $Log: N:\NT\PRIVATE\NW4\NWSCRIPT\VCS\DATE.C $
+*
+* Rev 1.2 10 Apr 1996 14:22:00 terryt
+* Hotfix for 21181hq
+*
+* Rev 1.2 12 Mar 1996 19:52:56 terryt
+* Relative NDS names and merge
+*
+* Rev 1.1 22 Dec 1995 14:24:04 terryt
+* Add Microsoft headers
+*
+* Rev 1.0 15 Nov 1995 18:06:40 terryt
+* Initial revision.
+*
+* Rev 1.0 15 May 1995 19:10:22 terryt
+* Initial revision.
+*
+*************************************************************************/
+
+#include <stdio.h>
+#include <direct.h>
+#include <time.h>
+#include <stdlib.h>
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include "nwscript.h"
+
+
+/*
+ *******************************************************************
+
+ NTGetTheDate
+
+Routine Description:
+
+ Return the current date
+
+Arguments:
+
+ yearCurrent pointer to current year
+ 1980-2099
+ monthCurrent pointer to current month
+ 1-12
+ dayCurrent pointer to current day
+ 1-31
+
+Return Value:
+
+
+
+ *******************************************************************
+ */
+void NTGetTheDate( unsigned int * yearCurrent,
+ unsigned char * monthCurrent,
+ unsigned char * dayCurrent )
+{
+ time_t timedat;
+ struct tm * p_tm;
+
+ (void) time( &timedat );
+ p_tm = localtime( &timedat );
+
+ *yearCurrent = p_tm->tm_year + 1900;
+ *monthCurrent = p_tm->tm_mon + 1;
+ *dayCurrent = p_tm->tm_mday;
+}
diff --git a/private/nw/nwscript/dbcs.c b/private/nw/nwscript/dbcs.c
new file mode 100644
index 000000000..f37beacb6
--- /dev/null
+++ b/private/nw/nwscript/dbcs.c
@@ -0,0 +1,98 @@
+
+/*************************************************************************
+*
+* DBCS.C
+*
+* DBCS routines, ported from DOS
+*
+* Copyright (c) 1995 Microsoft Corporation
+*
+* $Log: N:\NT\PRIVATE\NW4\NWSCRIPT\VCS\DBCS.C $
+*
+* Rev 1.1 22 Dec 1995 14:24:10 terryt
+* Add Microsoft headers
+*
+* Rev 1.0 15 Nov 1995 18:06:44 terryt
+* Initial revision.
+*
+* Rev 1.1 25 Aug 1995 16:22:26 terryt
+* Capture support
+*
+* Rev 1.0 15 May 1995 19:10:24 terryt
+* Initial revision.
+*
+*************************************************************************/
+/*
+** dbcs.c - DBCS functions for DOS apps.
+**
+** Written by RokaH and DavidDi.
+*/
+
+
+/* Headers
+**********/
+
+// IsDBCSLeadByte taken out of NT because there is one built in.
+// I left the Next and Prev in because I don't know whether this
+// algorithm is "safer" than the built in code.
+
+#include "common.h"
+
+/*
+** unsigned char *NWAnsiNext(unsigned char *puch);
+**
+** Moves to the next character in a string.
+**
+** Arguments: puch - pointer to current location in string
+**
+** Returns: char * - Pointer to next character in string.
+**
+** Globals: none
+**
+** N.b., if puch points to a null character, NWAnsiNext() will return puch.
+*/
+unsigned char *NWAnsiNext(unsigned char *puch)
+{
+ if (*puch == '\0')
+ return(puch);
+ else if (IsDBCSLeadByte(*puch))
+ puch++;
+
+ puch++;
+
+ return(puch);
+}
+
+
+/*
+** unsigned char *NWAnsiPrev(unsigned char *psz, unsigned char *puch);
+**
+** Moves back one character in a string.
+**
+** Arguments: psz - pointer to start of string
+** puch - pointer to current location in string
+**
+** Returns: char * - Pointer to previous character in string.
+**
+** Globals: none
+**
+** N.b., if puch <= psz, NWAnsiPrev() will return psz.
+**
+** This function is implemented in a very slow fashion because we do not wish
+** to trust that the given string is necessarily DBCS "safe," i.e., contains
+** only single-byte characters and valid DBCS characters. So we start from
+** the beginning of the string and work our way forward.
+*/
+unsigned char *NWAnsiPrev(unsigned char *psz, unsigned char *puch)
+{
+ unsigned char *puchPrevious;
+
+ do
+ {
+ puchPrevious = psz;
+ psz = NWAnsiNext(psz);
+ } while (*psz != '\0' && psz < puch);
+
+ return(puchPrevious);
+}
+
diff --git a/private/nw/nwscript/display.c b/private/nw/nwscript/display.c
new file mode 100644
index 000000000..ddd44e3d7
--- /dev/null
+++ b/private/nw/nwscript/display.c
@@ -0,0 +1,101 @@
+
+/*************************************************************************
+*
+* DISPLAY.C
+*
+* NetWare script routines for displaying information, ported from DOS
+*
+* Copyright (c) 1995 Microsoft Corporation
+*
+* $Log: N:\NT\PRIVATE\NW4\NWSCRIPT\VCS\DISPLAY.C $
+*
+* Rev 1.2 10 Apr 1996 14:22:06 terryt
+* Hotfix for 21181hq
+*
+* Rev 1.2 12 Mar 1996 19:53:04 terryt
+* Relative NDS names and merge
+*
+* Rev 1.1 22 Dec 1995 14:24:18 terryt
+* Add Microsoft headers
+*
+* Rev 1.0 15 Nov 1995 18:06:48 terryt
+* Initial revision.
+*
+* Rev 1.1 25 Aug 1995 16:22:32 terryt
+* Capture support
+*
+* Rev 1.0 15 May 1995 19:10:26 terryt
+* Initial revision.
+*
+*************************************************************************/
+/*
+ File name: display.c
+ Do not add any other functions to this file.
+ Otherwise many exes size will increase.
+ */
+
+
+#include "common.h"
+
+/*
+ Display error report.
+ */
+void DisplayError(int error ,char *functionName)
+{
+ DisplayMessage(IDR_ERROR, error ,functionName);
+}
+
+void xstrupr(char *buffer)
+{
+ for (; *buffer; buffer++)
+ {
+ if (IsDBCSLeadByte(*buffer))
+ buffer++;
+ else if (*buffer == 0xff80)
+ *buffer = (char)0xff87;
+ else if (*buffer == 0xff81)
+ *buffer = (char)0xff9a;
+ else if (*buffer == 0xff82)
+ *buffer = (char)0xff90;
+ else if (*buffer == 0xff84)
+ *buffer = (char)0xff8e;
+ else if (*buffer == 0xff88)
+ *buffer = (char)0xff9f;
+ else if (*buffer == 0xff91)
+ *buffer = (char)0xff92;
+ else if (*buffer == 0xff94)
+ *buffer = (char)0xff99;
+ else if (*buffer == 0xffa4)
+ *buffer = (char)0xffa5;
+ }
+
+ _strupr (buffer);
+}
+
+/*
+ Read password from the keyboard input.
+ */
+void ReadPassword(char * Password)
+{
+ int i = 0;
+ char c;
+
+ do
+ { c=(char)_getch();
+
+ if (c == '\b')
+ {
+ if (i > 0)
+ i--;
+ }
+ else
+ {
+ Password[i]=c;
+ i++;
+ }
+ }while((c!='\r') && i< MAX_PASSWORD_LEN );
+ Password[i-1]='\0';
+ xstrupr(Password);
+ DisplayMessage(IDR_NEWLINE);
+}
+
diff --git a/private/nw/nwscript/drive.c b/private/nw/nwscript/drive.c
new file mode 100644
index 000000000..b6539185d
--- /dev/null
+++ b/private/nw/nwscript/drive.c
@@ -0,0 +1,327 @@
+/*************************************************************************
+*
+* DRIVE.C
+*
+* NT drive routines
+*
+* Copyright (c) 1995 Microsoft Corporation
+*
+* $Log: N:\NT\PRIVATE\NW4\NWSCRIPT\VCS\DRIVE.C $
+*
+* Rev 1.2 10 Apr 1996 14:22:12 terryt
+* Hotfix for 21181hq
+*
+* Rev 1.2 12 Mar 1996 19:53:22 terryt
+* Relative NDS names and merge
+*
+* Rev 1.1 22 Dec 1995 14:24:24 terryt
+* Add Microsoft headers
+*
+* Rev 1.0 15 Nov 1995 18:06:52 terryt
+* Initial revision.
+*
+* Rev 1.2 25 Aug 1995 16:22:38 terryt
+* Capture support
+*
+* Rev 1.1 23 May 1995 19:36:46 terryt
+* Spruce up source
+*
+* Rev 1.0 15 May 1995 19:10:30 terryt
+* Initial revision.
+*
+*************************************************************************/
+#include <stdio.h>
+#include <direct.h>
+#include <time.h>
+#include <stdlib.h>
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include <nwapi32.h>
+#include <nwapi.h>
+#include <npapi.h>
+
+#include "nwscript.h"
+#include "ntnw.h"
+#include "inc/nwlibs.h"
+
+#include "..\..\..\inc\mpr.h"
+
+extern unsigned char NW_PROVIDERA[];
+
+/********************************************************************
+
+ GetFirstDrive
+
+Routine Description:
+
+ Return the first non-local drive
+
+Arguments:
+
+ pFirstDrive = pointer to drive
+ 1-26
+
+Return Value:
+ 0 = success
+ F = failure
+
+ ********************************************************************/
+unsigned int
+GetFirstDrive( unsigned short *pFirstDrive )
+{
+ int i;
+ char DriveName[10];
+ unsigned int drivetype;
+
+ strcpy( DriveName, "A:\\" );
+
+ for ( i = 2; i < 26; i++ ) {
+ DriveName[0] = 'A' + i;
+ drivetype = GetDriveTypeA( DriveName );
+ if ( ( ( drivetype == DRIVE_REMOTE ) &&
+ ( NTIsNetWareDrive( i ) ) ) ||
+ ( drivetype == DRIVE_NO_ROOT_DIR ) ) {
+ *pFirstDrive = i + 1;
+ return 0x0000;
+ }
+ }
+
+ return 0x000F;
+}
+
+/********************************************************************
+
+ IsDriveRemote
+
+Routine Description:
+
+ Is the given drive remote?
+
+Arguments:
+
+ DriveNumber 1-26
+ pRemote 0x1000 = remote, 0x0000 = local
+
+Return Value:
+ 0 = success
+ F = invalid drive
+
+ ********************************************************************/
+unsigned int
+IsDriveRemote(
+ unsigned char DriveNumber,
+ unsigned int *pRemote
+ )
+{
+ char DriveName[10];
+ unsigned int drivetype;
+
+ strcpy( DriveName, "A:\\" );
+ DriveName[0] = 'A' + DriveNumber;
+
+ drivetype = GetDriveTypeA( DriveName );
+
+ if ( drivetype == DRIVE_REMOTE ) {
+ *pRemote = 0x1000;
+ return 0;
+ }
+ else if ( drivetype == DRIVE_NO_ROOT_DIR ) {
+ return 0xF;
+ }
+ else {
+ *pRemote = 0;
+ return 0;
+ }
+}
+
+
+/********************************************************************
+
+ NTNetWareDriveStatus
+
+Routine Description:
+
+ Return the type of drive
+
+Arguments:
+
+ DriveNumber - Number of drive 0-25
+
+Return Value:
+
+ Combination of:
+ NETWARE_NETWORK_DRIVE
+ NETWARE_NETWARE_DRIVE
+ NETWARE_LOCAL_FREE_DRIVE
+ NETWARE_LOCAL_DRIVE
+
+
+
+ *******************************************************************/
+unsigned short
+NTNetWareDriveStatus( unsigned short DriveNumber )
+{
+ char DriveName[10];
+ unsigned int drivetype;
+ unsigned int Status = 0;
+
+ strcpy( DriveName, "A:\\" );
+ DriveName[0] = 'A' + DriveNumber;
+ drivetype = GetDriveTypeA( DriveName );
+
+ if ( drivetype == DRIVE_REMOTE ) {
+ Status |= NETWARE_NETWORK_DRIVE;
+ if ( NTIsNetWareDrive( (unsigned int)DriveNumber ) )
+ Status |= NETWARE_NETWARE_DRIVE;
+ }
+ else if ( drivetype == DRIVE_NO_ROOT_DIR ) {
+ Status = NETWARE_LOCAL_FREE_DRIVE;
+ }
+ else {
+ Status = NETWARE_LOCAL_DRIVE;
+ }
+ return Status;
+}
+
+
+/********************************************************************
+
+ NTGetNWDrivePath
+
+Routine Description:
+
+ Return the server name and path of the specified drive
+
+Arguments:
+ DriveNumber - Number of drive 0-25
+ ServerName - Name of file server
+ Path - Volume:\Path
+
+Return Value:
+ 0 = success
+ else NT error
+
+ *******************************************************************/
+unsigned int NTGetNWDrivePath(
+ unsigned short DriveNumber,
+ unsigned char * ServerName,
+ unsigned char * Path )
+{
+ static char localname[] = "A:";
+ unsigned int Result;
+ char * p;
+ char * volume;
+ char remotename[1024];
+ int length = 1024;
+
+ if ( ServerName != NULL )
+ *ServerName = 0;
+
+ if ( Path != NULL );
+ *Path = 0;
+
+ localname[0] = 'A' + DriveNumber;
+
+ Result = WNetGetConnectionA ( localname, remotename, &length );
+
+ if ( Result != NO_ERROR ) {
+ Result = GetLastError();
+ if ( Result == ERROR_EXTENDED_ERROR )
+ NTPrintExtendedError();
+ return Result;
+ }
+
+ p = strchr (remotename + 2, '\\');
+ if ( !p )
+ return 0xffffffff;
+
+ *p++ = '\0';
+ volume = p;
+
+ if ( ServerName != NULL ) {
+ strcpy( ServerName, remotename + 2 );
+ _strupr( ServerName );
+ }
+
+ if ( Path != NULL ) {
+ p = strchr (volume, '\\');
+ if ( !p ) {
+ strcpy( Path, volume );
+ strcat( Path, ":" );
+ }
+ else {
+ *p = ':';
+ strcpy( Path, volume );
+ }
+ _strupr( Path );
+ }
+
+ return NO_ERROR;
+}
+
+
+/********************************************************************
+
+ NTIsNetWareDrive
+
+Routine Description:
+
+ Returns TRUE if the drive is a netware mapped drive
+
+Arguments:
+
+ DriveNumber - Number of drive 0-25
+
+Return Value:
+ TRUE - drive is NetWare
+ FALSE - drive is not NetWare
+
+ *******************************************************************/
+unsigned int
+NTIsNetWareDrive( unsigned int DriveNumber )
+{
+ LPBYTE Buffer ;
+ DWORD dwErr ;
+ HANDLE EnumHandle ;
+ char DriveName[10];
+ DWORD BufferSize = 4096;
+ LPWNET_CONNECTIONINFOA pConnectionInfo;
+
+ strcpy( DriveName, "A:" );
+
+ DriveName[0] = 'A' + DriveNumber;
+
+ //
+ // allocate memory and open the enumeration
+ //
+ if (!(Buffer = LocalAlloc( LPTR, BufferSize ))) {
+ DisplayMessage(IDR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+
+ dwErr = WNetGetConnection2A( DriveName, Buffer, &BufferSize );
+ if (dwErr != WN_SUCCESS) {
+ dwErr = GetLastError();
+ if ( dwErr == ERROR_EXTENDED_ERROR )
+ NTPrintExtendedError();
+ (void) LocalFree((HLOCAL) Buffer) ;
+ return FALSE;
+ }
+
+ pConnectionInfo = (LPWNET_CONNECTIONINFOA) Buffer;
+
+ if ( !_strcmpi ( pConnectionInfo->lpProvider, NW_PROVIDERA ) ) {
+ (void) LocalFree((HLOCAL) Buffer) ;
+ return TRUE;
+ }
+ else {
+ (void) LocalFree((HLOCAL) Buffer) ;
+ return FALSE;
+ }
+
+ return FALSE;
+}
diff --git a/private/nw/nwscript/drvstat.c b/private/nw/nwscript/drvstat.c
new file mode 100644
index 000000000..908359d79
--- /dev/null
+++ b/private/nw/nwscript/drvstat.c
@@ -0,0 +1,268 @@
+
+/*************************************************************************
+*
+* DRVSTAT.C
+*
+* Drive status routines, ported from DOS
+*
+* Copyright (c) 1995 Microsoft Corporation
+*
+* $Log: N:\NT\PRIVATE\NW4\NWSCRIPT\VCS\DRVSTAT.C $
+*
+* Rev 1.2 10 Apr 1996 14:22:20 terryt
+* Hotfix for 21181hq
+*
+* Rev 1.2 12 Mar 1996 19:53:36 terryt
+* Relative NDS names and merge
+*
+* Rev 1.1 22 Dec 1995 14:24:32 terryt
+* Add Microsoft headers
+*
+* Rev 1.0 15 Nov 1995 18:06:54 terryt
+* Initial revision.
+*
+* Rev 1.1 25 Aug 1995 16:22:44 terryt
+* Capture support
+*
+* Rev 1.0 15 May 1995 19:10:32 terryt
+* Initial revision.
+*
+*************************************************************************/
+
+/*++
+
+Copyright (c) 1994 Micro Computer Systems, Inc.
+
+Module Name:
+
+ nwlibs\drvstat.c
+
+Abstract:
+
+ Directory APIs.
+
+Author:
+
+ Shawn Walker (v-swalk) 10-10-1994
+
+Revision History:
+
+--*/
+#include "common.h"
+
+
+/*++
+*******************************************************************
+
+ GetDriveStatus
+
+Routine Description:
+
+ Get the drive status.
+
+Arguments:
+
+ DriveNumber = The drive to number to use. (1=A,2=B,C=3,...)
+ PathFormat = Format for the return path.
+ NW_FORMAT_NETWARE - volume:path
+ NW_FORMAT_SERVER_VOLUME - server\volume:path
+ NW_FORMAT_DRIVE - G:\path
+ NW_FORMAT_UNC - \\server\volume\path
+ pStatus = A pointer to return the status of the drive.
+ pConnectionHandle = A pointer to return the connection handle
+ for the drive.
+ pRootPath = The pointer to return the base root path. OPTIONAL
+ pRelativePath = The pointer to return the relative to root path.
+ pFullPath = The pointer to return the full path.
+
+Return Value:
+
+ 0x0000 SUCCESSFUL
+ 0x00FF INVALID_DRIVE
+
+*******************************************************************
+--*/
+unsigned int
+GetDriveStatus(
+ unsigned short DriveNumber,
+ unsigned short PathFormat,
+ unsigned short *pStatus,
+ unsigned int *pConnectionHandle,
+ unsigned char *pRootPath,
+ unsigned char *pRelativePath,
+ unsigned char *pFullPath
+ )
+{
+ unsigned char *p;
+ unsigned int Result;
+ unsigned short Status;
+ unsigned char Path[NCP_MAX_PATH_LENGTH + 1];
+ unsigned char WorkPath[NCP_MAX_PATH_LENGTH + 1];
+ unsigned char ServerName[NCP_SERVER_NAME_LENGTH + 1];
+
+ /** Make sure the drive number is valid **/
+
+ if (DriveNumber < 1 || DriveNumber > 32) {
+ return 0x000F; /* INVALID_DRIVE */
+ }
+
+ Status = 0;
+
+ DriveNumber--;
+
+
+ if (pConnectionHandle) {
+ /*
+ * This should never occur.
+ */
+ DisplayError (0xff, "GetDriveStatus");
+ return 0xff;
+ }
+
+ /** Get the directory path from the server **/
+ Result = NTGetNWDrivePath( DriveNumber, ServerName, Path );
+ if ( Result ) {
+ *Path = 0;
+ *ServerName = 0;
+ }
+
+ /** Convert the / in the path to \ **/
+ for (p = Path; *p != 0 ; p++)
+ {
+ if (*p == '/')
+ *p = '\\';
+ }
+
+ /** Get the status of the drive if we need to **/
+ Status = NTNetWareDriveStatus( DriveNumber );
+
+ /** Get the status of the drive if we need to **/
+
+ if (pStatus) {
+ *pStatus = Status;
+ }
+
+ /** Get the full path if we need to **/
+
+ if (pFullPath) {
+
+ if (Status & NETWARE_LOCAL_FREE_DRIVE) {
+ *pFullPath = 0;
+ }
+ else {
+ strcpy(WorkPath, Path);
+
+ /** Build the NetWare path format (volume:path) **/
+
+ if (PathFormat == NETWARE_FORMAT_NETWARE) {
+ strcpy(pFullPath, WorkPath);
+ }
+
+ /** Build the server volume path (server\volume:path) **/
+
+ else if (PathFormat == NETWARE_FORMAT_SERVER_VOLUME) {
+ sprintf(pFullPath, "%s\\%s", ServerName, WorkPath);
+ }
+
+ /** Build the drive path (G:\path) **/
+
+ else if (PathFormat == NETWARE_FORMAT_DRIVE) {
+
+ p = WorkPath;
+ while (*p != ':' && *p) {
+ p++;
+ }
+
+ if (*p == ':') {
+ p++;
+ }
+
+ sprintf(pFullPath, "%c:\\%s", DriveNumber + 'A', p);
+ }
+
+ /** Build the UNC path (\\server\volume\path) **/
+
+ else if (PathFormat == NETWARE_FORMAT_UNC) {
+
+ p = WorkPath;
+ while (*p != ':' && *p) {
+ p++;
+ }
+
+ if (*p == ':') {
+ *p = '\\';
+ }
+
+ sprintf(pFullPath, "\\\\%s\\%s", ServerName, WorkPath);
+ }
+ }
+ }
+
+ strcpy(WorkPath, Path);
+ /*
+ * Path does not have the relative path (current directory) in it.
+ */
+
+ /** Get the root path if we need to **/
+
+ if (pRootPath) {
+
+ if (Status & NETWARE_LOCAL_FREE_DRIVE) {
+ *pRootPath = 0;
+ }
+ else {
+
+ /** Build the NetWare root path format (volume:) **/
+
+ if (PathFormat == NETWARE_FORMAT_NETWARE) {
+ sprintf(pRootPath, strchr(WorkPath, ':')? "%s" : "%s:", WorkPath);
+ }
+
+ /** Build the server volume root path (server\volume:) **/
+
+ else if (PathFormat == NETWARE_FORMAT_SERVER_VOLUME) {
+ if ( fNDS && !_strcmpi( ServerName, NDSTREE) )
+ sprintf(pRootPath, strchr (WorkPath, ':')? "%s" : "%s:", WorkPath);
+ else
+ sprintf(pRootPath, strchr (WorkPath, ':')? "%s\\%s" : "%s\\%s:", ServerName, WorkPath);
+ }
+
+ /** Build the drive root path (G:\) **/
+
+ else if (PathFormat == NETWARE_FORMAT_DRIVE) {
+ sprintf(pRootPath, "%c:\\", DriveNumber + 'A');
+ }
+
+ /** Build the UNC root path (\\server\volume) **/
+
+ else if (PathFormat == NETWARE_FORMAT_UNC) {
+ sprintf(pRootPath, "\\\\%s\\%s", ServerName, WorkPath);
+ }
+ }
+ }
+
+ /** Get the relative path if we need to **/
+
+ if (pRelativePath) {
+
+ if (Status & NETWARE_LOCAL_FREE_DRIVE) {
+ *pRelativePath = 0;
+ }
+ else {
+ int i;
+ NTGetCurrentDirectory( (unsigned char)DriveNumber, pRelativePath );
+ /*
+ * Skip the drive letter
+ */
+ if ( pRelativePath[0] ) {
+ for ( i = 0; ;i++ ) {
+ pRelativePath[i] = pRelativePath[i+3];
+ if ( !pRelativePath[i] )
+ break;
+ }
+ }
+ }
+ }
+
+ return 0x0000;
+}
diff --git a/private/nw/nwscript/env.c b/private/nw/nwscript/env.c
new file mode 100644
index 000000000..dfece205b
--- /dev/null
+++ b/private/nw/nwscript/env.c
@@ -0,0 +1,352 @@
+/*************************************************************************
+*
+* ENV.C
+*
+* Environment export routines
+*
+* Copyright (c) 1995 Microsoft Corporation
+*
+* $Log: N:\NT\PRIVATE\NW4\NWSCRIPT\VCS\ENV.C $
+*
+* Rev 1.2 10 Apr 1996 14:22:28 terryt
+* Hotfix for 21181hq
+*
+* Rev 1.2 12 Mar 1996 19:53:48 terryt
+* Relative NDS names and merge
+*
+* Rev 1.1 22 Dec 1995 14:24:40 terryt
+* Add Microsoft headers
+*
+* Rev 1.0 15 Nov 1995 18:06:58 terryt
+* Initial revision.
+*
+* Rev 1.2 25 Aug 1995 16:22:50 terryt
+* Capture support
+*
+* Rev 1.1 23 May 1995 19:36:54 terryt
+* Spruce up source
+*
+* Rev 1.0 15 May 1995 19:10:34 terryt
+* Initial revision.
+*
+*************************************************************************/
+#include <stdio.h>
+#include <direct.h>
+#include <time.h>
+#include <stdlib.h>
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include "nwscript.h"
+
+#define MAX_PATH_LEN 2048
+#define PATH "Path"
+#define LIBPATH "LibPath"
+#define OS2LIBPATH "Os2LibPath"
+
+unsigned char * Path_Value = NULL;
+unsigned char * LibPath_Value = NULL;
+unsigned char * Os2LibPath_Value = NULL;
+
+
+/********************************************************************
+
+ GetOldPaths
+
+Routine Description:
+
+ Save the orginal paths for
+ Path
+ LibPath
+ Os2LibPath
+
+Arguments:
+ none
+
+Return Value:
+ none
+
+ *******************************************************************/
+void
+GetOldPaths( void )
+{
+ if (!(Path_Value = (unsigned char *)LocalAlloc( LPTR, MAX_PATH_LEN )))
+ {
+ DisplayMessage(IDR_NOT_ENOUGH_MEMORY);
+ return;
+ }
+ GetEnvironmentVariableA( PATH, Path_Value, MAX_PATH_LEN );
+ if (!(LibPath_Value = (unsigned char *)LocalAlloc( LPTR, MAX_PATH_LEN)))
+ {
+ DisplayMessage(IDR_NOT_ENOUGH_MEMORY);
+ return;
+ }
+ GetEnvironmentVariableA( LIBPATH, LibPath_Value, MAX_PATH_LEN );
+ if (!(Os2LibPath_Value = (unsigned char *)LocalAlloc( LPTR, MAX_PATH_LEN)))
+ {
+ DisplayMessage(IDR_NOT_ENOUGH_MEMORY);
+ return;
+ }
+ GetEnvironmentVariableA( OS2LIBPATH, Os2LibPath_Value, MAX_PATH_LEN );
+}
+
+
+/********************************************************************
+
+ AdjustPath
+
+Routine Description:
+
+ Given an old path and a new path, merge the two togther.
+ Basically, the Adjusted path is the old path with the
+ new values at the end, minus any duplicates.
+
+Arguments:
+
+ Value - New path
+ OldPath_Value - Old path
+ AdjustedValue - New value (allocated)
+
+Return Value:
+ none
+
+ *******************************************************************/
+void
+AdjustPath( unsigned char * Value,
+ unsigned char * OldPath_Value,
+ unsigned char ** AdjustedValue )
+{
+ unsigned char * tokenPath;
+ unsigned char * clipStart;
+ unsigned char * clipEnd;
+ unsigned char * tokenSearch;
+ unsigned char * tokenNext;
+
+ if (!(*AdjustedValue = (unsigned char *)LocalAlloc( LPTR, MAX_PATH_LEN)))
+ {
+ DisplayMessage(IDR_NOT_ENOUGH_MEMORY);
+ return;
+ }
+ strncpy( *AdjustedValue, Value, MAX_PATH_LEN );
+
+ if (!(tokenSearch = (unsigned char *)LocalAlloc( LPTR, MAX_PATH_LEN)))
+ {
+ DisplayMessage(IDR_NOT_ENOUGH_MEMORY);
+ (void) LocalFree((HLOCAL) *AdjustedValue) ;
+ return;
+ }
+ strncpy( tokenSearch, OldPath_Value, MAX_PATH_LEN );
+
+ tokenNext = tokenSearch;
+
+ if ( !tokenNext || !tokenNext[0] )
+ tokenPath = NULL;
+ else {
+ tokenPath = tokenNext;
+ tokenNext = strchr( tokenPath, ';' );
+ if ( tokenNext ) {
+ *tokenNext++ = 0;
+ }
+ }
+
+ while ( tokenPath != NULL )
+ {
+ if ( clipStart = strstr( *AdjustedValue, tokenPath ) ) {
+ if ( clipEnd = strchr( clipStart, ';' ) ) {
+ memmove( clipStart, clipEnd + 1, strlen( clipEnd + 1 ) + 1 );
+ }
+ else {
+ clipStart[0] = 0;
+ }
+ }
+
+ if ( !tokenNext || !tokenNext[0] )
+ tokenPath = NULL;
+ else {
+ tokenPath = tokenNext;
+ tokenNext = strchr( tokenPath, ';' );
+ if ( tokenNext ) {
+ *tokenNext++ = 0;
+ }
+ }
+ }
+ (void) LocalFree((HLOCAL) tokenSearch) ;
+
+}
+
+/********************************************************************
+
+ ExportEnv
+
+Routine Description:
+
+ Export environment value to the registry
+
+Arguments:
+
+ EnvString - Environment string
+
+Return Value:
+ none
+
+ *******************************************************************/
+void
+ExportEnv( unsigned char * EnvString )
+{
+ HKEY ScriptEnvironmentKey;
+ NTSTATUS Status;
+ unsigned char * Value;
+ unsigned char * ValueName;
+ unsigned char * AdjustedValue = NULL;
+
+ ValueName = EnvString;
+ Value = strchr( EnvString, '=' );
+
+ if ( Value == NULL ) {
+ wprintf(L"Bad Environment string\n");
+
+ return;
+ }
+ Value++;
+
+ if (!(ValueName = (unsigned char *)LocalAlloc( LPTR, Value-EnvString + 1)))
+ {
+ DisplayMessage(IDR_NOT_ENOUGH_MEMORY);
+ return;
+ }
+ strncpy( ValueName, EnvString, Value-EnvString - 1 );
+
+ if ( !_strcmpi( ValueName, PATH ) ) {
+ AdjustPath( Value, Path_Value, &AdjustedValue );
+ Value = AdjustedValue;
+ }
+ else if ( !_strcmpi( ValueName, LIBPATH ) ) {
+ AdjustPath( Value, LibPath_Value, &AdjustedValue );
+ Value = AdjustedValue;
+ }
+ else if ( !_strcmpi( ValueName, OS2LIBPATH ) ) {
+ AdjustPath( Value, Os2LibPath_Value, &AdjustedValue );
+ Value = AdjustedValue;
+ }
+
+ Status = RegCreateKeyExW( HKEY_CURRENT_USER,
+ SCRIPT_ENVIRONMENT_VALUENAME,
+ 0,
+ WIN31_CLASS,
+ REG_OPTION_VOLATILE,
+ KEY_WRITE,
+ NULL, // security attr
+ &ScriptEnvironmentKey,
+ NULL
+ );
+
+ if ( NT_SUCCESS(Status)) {
+
+ Status = RegSetValueExA( ScriptEnvironmentKey,
+ ValueName,
+ 0,
+ REG_SZ,
+ (LPVOID) Value,
+ strlen( Value ) + 1
+ );
+ }
+ else {
+ wprintf(L"Cannot create registry key\n");
+ }
+
+ (void) LocalFree((HLOCAL) ValueName) ;
+
+ if ( AdjustedValue )
+ (void) LocalFree((HLOCAL) AdjustedValue) ;
+
+ RegCloseKey( ScriptEnvironmentKey );
+}
+
+/********************************************************************
+
+ ExportCurrentDirectory
+
+Routine Description:
+
+ Return the first non-local drive
+
+Arguments:
+
+ DriveNum - Number of drive 1-26
+
+Return Value:
+ none
+
+ *******************************************************************/
+void
+ExportCurrentDirectory( int DriveNum )
+{
+ char DriveName[10];
+ HKEY ScriptEnvironmentKey;
+ NTSTATUS Status;
+ char CurrentPath[MAX_PATH_LEN];
+
+ strcpy( DriveName, "=A:" );
+
+ DriveName[1] += (DriveNum - 1);
+
+ if ( NTGetCurrentDirectory( (unsigned char)(DriveNum - 1), CurrentPath ) )
+ return;
+
+ Status = RegCreateKeyExW( HKEY_CURRENT_USER,
+ SCRIPT_ENVIRONMENT_VALUENAME,
+ 0,
+ WIN31_CLASS,
+ REG_OPTION_VOLATILE,
+ KEY_WRITE,
+ NULL, // security attr
+ &ScriptEnvironmentKey,
+ NULL
+ );
+
+ if ( NT_SUCCESS(Status)) {
+
+ Status = RegSetValueExA( ScriptEnvironmentKey,
+ DriveName,
+ 0,
+ REG_SZ,
+ (LPVOID) CurrentPath,
+ strlen( CurrentPath ) + 1
+ );
+ }
+ else {
+ wprintf(L"Cannot open registry key\n");
+ }
+
+ RegCloseKey( ScriptEnvironmentKey );
+
+}
+
+
+/********************************************************************
+
+ ExportCurrentDrive
+
+Routine Description:
+
+ Export current drive to registry
+ NOT IMPLEMENTED
+
+Arguments:
+
+ DriveNum - drive number
+
+Return Value:
+ none
+
+ *******************************************************************/
+void
+ExportCurrentDrive( int DriveNum )
+{
+ /*
+ * Don't know if we want to do this or how.
+ */
+}
diff --git a/private/nw/nwscript/helpers.c b/private/nw/nwscript/helpers.c
new file mode 100644
index 000000000..9f504490f
--- /dev/null
+++ b/private/nw/nwscript/helpers.c
@@ -0,0 +1,96 @@
+/******************************************************************************
+*
+* HELPERS.C
+*
+* Various helper functions.
+*
+* Copyright (c) 1995 Microsoft Corporation
+*
+* $Log: N:\NT\PRIVATE\NW4\NWSCRIPT\VCS\HELPERS.C $
+*
+* Rev 1.1 22 Dec 1995 14:24:48 terryt
+* Add Microsoft headers
+*
+* Rev 1.0 15 Nov 1995 18:07:02 terryt
+* Initial revision.
+*
+* Rev 1.1 25 Aug 1995 16:22:56 terryt
+* Capture support
+*
+* Rev 1.0 15 May 1995 19:10:38 terryt
+* Initial revision.
+*
+*
+*******************************************************************************/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+#include <windows.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <locale.h>
+
+#include "nwscript.h"
+
+
+/*******************************************************************************
+ *
+ * DisplayMessage
+ * Display a message with variable arguments. Message
+ * format string comes from the application resources.
+ *
+ * ENTRY:
+ * nID (input)
+ * Resource ID of the format string to use in the message.
+ * ... (input)
+ * Optional additional arguments to be used with format string.
+ *
+ * EXIT:
+ *
+ ******************************************************************************/
+
+VOID
+DisplayMessage( unsigned int nID, ... )
+{
+ WCHAR sz1[512];
+
+ va_list args;
+ va_start( args, nID );
+
+ if ( LoadString( NULL, nID, sz1, 512 ) ) {
+
+ setlocale(LC_ALL,".ACP") ;
+ vwprintf( sz1, args );
+ setlocale(LC_ALL,".OCP") ;
+
+ }
+
+ va_end(args);
+
+} /* DisplayMessage() */
+
+
+/*******************************************************************************
+ *
+ * DisplayOemString
+ * Display an OEM string
+ *
+ * ENTRY:
+ * string: string to display
+ *
+ * EXIT:
+ *
+ ******************************************************************************/
+
+VOID
+DisplayOemString( char *string )
+{
+ // this will print % in strings correctly.
+ printf( "%s", string );
+
+} /* DisplayAnsiString() */
+
+
diff --git a/private/nw/nwscript/inc/common.h b/private/nw/nwscript/inc/common.h
new file mode 100644
index 000000000..14764853d
--- /dev/null
+++ b/private/nw/nwscript/inc/common.h
@@ -0,0 +1,195 @@
+
+/*************************************************************************
+*
+* COMMON.H
+*
+* Common header file
+*
+* Copyright (c) 1995 Microsoft Corporation
+*
+* $Log: N:\NT\PRIVATE\NW4\NWSCRIPT\INC\VCS\COMMON.H $
+*
+* Rev 1.3 22 Dec 1995 14:20:06 terryt
+* Add Microsoft headers
+*
+* Rev 1.2 22 Nov 1995 15:44:26 terryt
+* Use proper NetWare user name call
+*
+* Rev 1.1 20 Nov 1995 15:18:46 terryt
+* Context and capture changes
+*
+* Rev 1.0 15 Nov 1995 18:05:30 terryt
+* Initial revision.
+*
+* Rev 1.2 25 Aug 1995 17:03:32 terryt
+* CAPTURE support
+*
+* Rev 1.1 26 Jul 1995 16:01:12 terryt
+* Get rid of unneccessary externs
+*
+* Rev 1.0 15 May 1995 19:09:28 terryt
+* Initial revision.
+*
+*************************************************************************/
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <stdio.h>
+#include <conio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdlib.h>
+#include <windows.h>
+#include <nds.h>
+#include <ndsapi32.h>
+#include <nwapi32.h>
+
+#include "dbcs.h"
+#include "inc\nwlibs.h"
+
+#include "nwscript.h"
+
+
+#define MAX_NAME_LEN 48
+#define MAX_PASSWORD_LEN 128
+#define MAX_PATH_LEN 304 //From NW programmer's guide p268.
+
+/* for map only */
+#define MAX_VOLUME_LEN 256 // 17 for 3X
+#define MAX_DIR_PATH_LEN 256
+
+/* for capture only */
+#define MAX_JOB_NAME_LEN 32
+#define MAX_QUEUE_NAME_LEN 1024
+#define MAX_BANNER_USER_NAME 13
+
+/* for common only */
+#define PATH_SEPERATOR ";"
+
+
+/*
+ Function definations
+ */
+/* used by login.c and script.c */
+void BreakOff(void);
+void BreakOn(void);
+
+/* used by common setpass*/
+void xstrupr(char *buffer);
+void ReadPassword(char * Password);
+
+/* used by map attach login*/
+int CAttachToFileServer(char *ServerName, unsigned int *pConn, int * pbAlreadyAttached);
+int Login(char *UserName, char *ServerName, char *Password, int bReadPassword);
+
+/* used by map attach login*/
+int ReadName (char * Name);
+
+/* used by map login */
+void DisplayError(int error ,char *functionName);
+char * GetDosEnv1(void);
+char * NWGetPath(void);
+int GetRestEnvLen (char *lpRest);
+
+int MemorySegmentLargeEnough (int nInsertByte);
+int IsSearchDrive(int driveNum);
+int GetDriveFromSearchNumber (int searchNumber);
+
+/* used by login logout*/
+void SetLoginDirectory (PBYTE);
+
+/* used by all */
+int Map (char * buffer);
+void DisplayMapping(void);
+int CGetDefaultConnectionID ( unsigned int * pConn );
+int GetConnectionInfo (unsigned int conn,
+ char * serverName,
+ char * userName,
+ unsigned int * pconnNum,
+ unsigned char * loginTime);
+
+extern char * LOGIN_NAME;
+extern char *NDS_FULL_NAME;
+extern char *REQUESTER_CONTEXT;
+extern char *TYPED_USER_NAME;
+extern PWCHAR TYPED_USER_NAME_w;
+extern PBYTE NDSTREE;
+extern PWCHAR NDSTREE_w;
+extern UNICODE_STRING NDSTREE_u;
+extern PBYTE PREFERRED_SERVER;
+
+/*
+ String definitions.
+ */
+extern char *__Day__[7];
+extern char *__Month__[12];
+extern char *__AMPM__[2];
+extern char *__GREETING__[3];
+
+extern char __DEL__[];
+extern char __REM__[];
+extern char __INS__[];
+extern char __ROOT__[];
+extern char __NEXT__[];
+
+extern char __AUTOENDCAP__[];
+extern char __BANNER__[];
+extern char __COPIES__[];
+extern char __CREATE__[];
+extern WCHAR __DISABLED__[];
+extern WCHAR __ENABLED__[];
+extern WCHAR __YES__[];
+extern WCHAR __NO__[];
+extern WCHAR __SECONDS__[];
+extern WCHAR __CONVERT_TO_SPACE__[];
+extern WCHAR __NO_CONVERSION__[];
+extern WCHAR __NOTIFY_USER__[];
+extern WCHAR __NOT_NOTIFY_USER__[];
+extern WCHAR __NONE__[];
+extern char __FORMFEED__[];
+extern char __FORM__[];
+extern char __JOB_DESCRIPTION__[];
+extern char __JOB__[];
+extern char __KEEP__[];
+extern char __LOCAL__[];
+extern char __LOCAL_2__[];
+extern char __LOCAL_3__[];
+extern char __NAME__[];
+extern char __NOAUTOENDCAP__[];
+extern char __NOBANNER__[];
+extern char __NOFORMFEED__[];
+extern char __NONOTIFY__[];
+extern char __NOTABS__[];
+extern char __NOTIFY__[];
+extern char __QUEUE__[];
+extern char __PRINTER__[];
+extern char __OPT_NO__[];
+extern char __SERVER__[];
+extern char __SHORT_FOR_AUTOENDCAP__[];
+extern char __SHORT_FOR_BANNER__[];
+extern char __SHORT_FOR_COPIES__[];
+extern char __SHORT_FOR_CREATE__[];
+extern char __SHORT_FOR_FORMFEED__[];
+extern char __SHORT_FOR_FORM__[];
+extern char __SHORT_FOR_JOB__[];
+extern char __SHORT_FOR_KEEP__[];
+extern char __SHORT_FOR_LOCAL__[];
+extern char __SHORT_FOR_NAME__[];
+extern char __SHORT_FOR_NOAUTOENDCAP__[];
+extern char __SHORT_FOR_NOBANNER__[];
+extern char __SHORT_FOR_NOFORMFEED__[];
+extern char __SHORT_FOR_NONOTIFY__[];
+extern char __SHORT_FOR_NOTABS__[];
+extern char __SHORT_FOR_NOTIFY__[];
+extern char __SHORT_FOR_QUEUE__[];
+extern char __SHORT_FOR_PRINTER__[];
+extern char __SHORT_FOR_SERVER__[];
+extern char __SHORT_FOR_TABS__[];
+extern char __SHORT_FOR_TIMEOUT__[];
+extern char __SHOW__[];
+extern char __TABS__[];
+extern char __TIMEOUT__[];
+
+extern unsigned int CaptureStringsLoaded;
+extern unsigned int fNDS;
diff --git a/private/nw/nwscript/inc/dbcs.h b/private/nw/nwscript/inc/dbcs.h
new file mode 100644
index 000000000..2fb4a5b5b
--- /dev/null
+++ b/private/nw/nwscript/inc/dbcs.h
@@ -0,0 +1,26 @@
+
+/*************************************************************************
+*
+* DBCS.H
+*
+* DBCS header file
+*
+* Copyright (c) 1995 Microsoft Corporation
+*
+* $Log: N:\NT\PRIVATE\NW4\NWSCRIPT\INC\VCS\DBCS.H $
+*
+* Rev 1.1 22 Dec 1995 14:20:14 terryt
+* Add Microsoft headers
+*
+* Rev 1.0 15 Nov 1995 18:05:32 terryt
+* Initial revision.
+*
+* Rev 1.1 25 Aug 1995 17:03:40 terryt
+* CAPTURE support
+*
+* Rev 1.0 15 May 1995 19:09:32 terryt
+* Initial revision.
+*
+*************************************************************************/
+unsigned char *NWAnsiNext(unsigned char *puch);
+unsigned char *NWAnsiPrev(unsigned char *psz, unsigned char *puch);
diff --git a/private/nw/nwscript/inc/ntnw.h b/private/nw/nwscript/inc/ntnw.h
new file mode 100644
index 000000000..a89e15720
--- /dev/null
+++ b/private/nw/nwscript/inc/ntnw.h
@@ -0,0 +1,46 @@
+
+/*************************************************************************
+*
+* NTNW.H
+*
+* NT specific NetWare defines
+*
+* Copyright (c) 1995 Microsoft Corporation
+*
+* $Log: N:\NT\PRIVATE\NW4\NWSCRIPT\INC\VCS\NTNW.H $
+*
+* Rev 1.1 22 Dec 1995 14:20:20 terryt
+* Add Microsoft headers
+*
+* Rev 1.0 15 Nov 1995 18:05:34 terryt
+* Initial revision.
+*
+* Rev 1.0 15 May 1995 19:09:36 terryt
+* Initial revision.
+*
+*************************************************************************/
+
+/*
+ * This must be kept in sync with the NWAPI32 library. This are
+ * internal data structures and routines.
+ */
+typedef struct _NWC_SERVER_INFO {
+ HANDLE hConn ;
+ UNICODE_STRING ServerString ;
+} NWC_SERVER_INFO, *PNWC_SERVER_INFO ;
+
+extern NTSTATUS
+NwlibMakeNcp(
+ IN HANDLE DeviceHandle,
+ IN ULONG FsControlCode,
+ IN ULONG RequestBufferSize,
+ IN ULONG ResponseBufferSize,
+ IN PCHAR FormatString,
+ ... // Arguments to FormatString
+ );
+
+DWORD szToWide( LPWSTR lpszW, LPCSTR lpszC, INT nSize );
+DWORD WideTosz( LPSTR lpszC, LPWSTR lpszW, INT nSize );
+
+extern TCHAR NW_PROVIDER[60];
+
diff --git a/private/nw/nwscript/inc/nwlibs.h b/private/nw/nwscript/inc/nwlibs.h
new file mode 100644
index 000000000..64a187e52
--- /dev/null
+++ b/private/nw/nwscript/inc/nwlibs.h
@@ -0,0 +1,371 @@
+
+/*************************************************************************
+*
+* NWLIBS.H
+*
+* Prototypes
+*
+* Copyright (c) 1995 Microsoft Corporation
+*
+* $Log: N:\NT\PRIVATE\NW4\NWSCRIPT\INC\VCS\NWLIBS.H $
+*
+* Rev 1.1 22 Dec 1995 14:20:28 terryt
+* Add Microsoft headers
+*
+* Rev 1.0 15 Nov 1995 18:05:36 terryt
+* Initial revision.
+*
+* Rev 1.1 25 Aug 1995 17:03:46 terryt
+* CAPTURE support
+*
+* Rev 1.0 15 May 1995 19:09:40 terryt
+* Initial revision.
+*
+*************************************************************************/
+
+/*++
+
+Copyright (c) 1994 Micro Computer Systems, Inc.
+
+Module Name:
+
+ nwlibs\nwlibs.h
+
+Abstract:
+
+ NW Libs prototypes.
+
+Author:
+
+ Shawn Walker (v-swalk) 10-10-1994
+
+Revision History:
+
+--*/
+
+#ifndef _NWLIBS_H_
+#define _NWLIBS_H_
+
+
+/*++
+*******************************************************************
+ NetWare defaults
+*******************************************************************
+--*/
+#define NCP_BINDERY_OBJECT_NAME_LENGTH 48
+#define NCP_SERVER_NAME_LENGTH NCP_BINDERY_OBJECT_NAME_LENGTH
+
+#define NCP_MAX_PATH_LENGTH 255
+#define NCP_VOLUME_LENGTH 256 // 16 in 3X
+
+
+/*++
+*******************************************************************
+ Defines for GetDrive Status
+*******************************************************************
+--*/
+
+#define NETWARE_UNMAPPED_DRIVE 0x0000
+#define NETWARE_FREE_DRIVE 0x0000
+#define NETWARE_LOCAL_FREE_DRIVE 0x0800
+#define NETWARE_LOCAL_DRIVE 0x1000
+#define NETWARE_NETWORK_DRIVE 0x2000
+#define NETWARE_LITE_DRIVE 0x4000
+#define NETWARE_PNW_DRIVE 0x4000
+#define NETWARE_NETWARE_DRIVE 0x8000
+
+#define NETWARE_FORMAT_NETWARE 0
+#define NETWARE_FORMAT_SERVER_VOLUME 1
+#define NETWARE_FORMAT_DRIVE 2
+#define NETWARE_FORMAT_UNC 3
+
+#define NCP_JOB_DESCRIPTION_LENGTH 50
+#define NCP_BANNER_TEXT_LENGTH 13
+#define NCP_FORM_NAME_LENGTH 13
+#define NCP_QUEUE_NAME_LENGTH 65
+
+#define CAPTURE_FLAG_PRINT_BANNER 0x80
+#define CAPTURE_FLAG_EXPAND_TABS 0x40
+#define CAPTURE_FLAG_NOTIFY 0x10
+#define CAPTURE_FLAG_NO_FORMFEED 0x08
+#define CAPTURE_FLAG_KEEP 0x04
+#define DEFAULT_PRINT_FLAGS 0xC0
+#define DEFAULT_BANNER_TEXT "LPT:"
+
+typedef struct _NETWARE_CAPTURE_FLAGS_RW {
+ unsigned char JobDescription[NCP_JOB_DESCRIPTION_LENGTH];
+ unsigned char JobControlFlags;
+ unsigned char TabSize;
+ unsigned short NumCopies;
+ unsigned short PrintFlags;
+ unsigned short MaxLines;
+ unsigned short MaxChars;
+ unsigned char FormName[NCP_FORM_NAME_LENGTH];
+ unsigned char Reserved1[9];
+ unsigned short FormType;
+ unsigned char BannerText[NCP_BANNER_TEXT_LENGTH];
+ unsigned char Reserved2;
+ unsigned short FlushCaptureTimeout;
+ unsigned char FlushCaptureOnClose;
+} NETWARE_CAPTURE_FLAGS_RW, *PNETWARE_CAPTURE_FLAGS_RW, *LPNETWARE_CAPTURE_FLAGS_RW;
+
+typedef struct _NETWARE_CAPTURE_FLAGS_RO {
+ unsigned short ConnectionID;
+ unsigned short SetupStringMaxLen;
+ unsigned short ResetStringMaxLen;
+ unsigned char LPTCaptureFlag;
+ unsigned char FileCaptureFlag;
+ unsigned char TimingOutFlag;
+ unsigned char InProgress;
+ unsigned char PrintQueueFlag;
+ unsigned char PrintJobValid;
+ unsigned char QueueName[NCP_QUEUE_NAME_LENGTH];
+ unsigned char ServerName[NCP_SERVER_NAME_LENGTH];
+} NETWARE_CAPTURE_FLAGS_RO, *PNETWARE_CAPTURE_FLAGS_RO, *LPNETWARE_CAPTURE_FLAGS_RO;
+
+#define NETWARE_CAPTURE_FLAGS_RO_SIZE sizeof(NETWARE_CAPTURE_FLAGS_RO)
+#define NETWARE_CAPTURE_FLAGS_RW_SIZE sizeof(NETWARE_CAPTURE_FLAGS_RW)
+
+#define PS_FORM_NAME_SIZE 12
+#define PS_BANNER_NAME_SIZE 12
+#define PS_BANNER_FILE_SIZE 12
+#define PS_DEVICE_NAME_SIZE 32
+#define PS_MODE_NAME_SIZE 32
+
+#define PS_BIND_NAME_SIZE NCP_BINDERY_OBJECT_NAME_LENGTH
+#define PS_MAX_NAME_SIZE 514
+
+/** Flags for the PS_JOB_REC structure PrintJobFlag field **/
+
+#define PS_JOB_EXPAND_TABS 0x00000001 /* File type:0=Stream 1=Tab */
+#define PS_JOB_NO_FORMFEED 0x00000002 /* Formfeed tail:0=Yes 1=No */
+#define PS_JOB_NOTIFY 0x00000004 /* Notify:0=No 1=Yes */
+#define PS_JOB_PRINT_BANNER 0x00000008 /* Banner:0=No 1=Yes */
+#define PS_JOB_AUTO_END 0x00000010 /* Auto endcap:0=No 1=Yes */
+#define PS_JOB_TIMEOUT 0x00000020 /* Enable T.O.:0=No 1=Yes */
+
+#define PS_JOB_ENV_DS 0x00000040 /* Use D.S. Environment */
+#define PS_JOB_ENV_MASK 0x000001C0 /* Bindery vs. D.S. Mask */
+
+#define PS_JOB_DS_PRINTER 0x00000200 /* D.S. Printer not Queue */
+#define PS_JOB_PRINTER_MASK 0x00000E00 /* D.S. Printer vs. Queue */
+
+/** Default Flags **/
+
+#define PS_JOB_DEFAULT (NWPS_JOB_PRINT_BANNER | NWPS_JOB_AUTO_END)
+#define PS_JOB_DEFAULT_COPIES 1 /* Default Number of Copies */
+#define PS_JOB_DEFAULT_TAB 8 /* Default Tab Expansion */
+
+typedef struct _PS_JOB_RECORD {
+ DWORD PrintJobFlag;
+ SHORT Copies;
+ SHORT TimeOutCount;
+ UCHAR TabSize;
+ UCHAR LocalPrinter;
+ CHAR FormName[PS_FORM_NAME_SIZE + 2];
+ CHAR Name[PS_BANNER_NAME_SIZE + 2];
+ CHAR BannerName[PS_BANNER_FILE_SIZE + 2];
+ CHAR Device[PS_DEVICE_NAME_SIZE + 2];
+ CHAR Mode[PS_MODE_NAME_SIZE + 2];
+ union {
+ struct {
+ /** Pad structures on even boundries **/
+
+ CHAR FileServer[PS_BIND_NAME_SIZE + 2];
+ CHAR PrintQueue[PS_BIND_NAME_SIZE + 2];
+ CHAR PrintServer[PS_BIND_NAME_SIZE + 2];
+ } NonDS;
+ CHAR DSObjectName[PS_MAX_NAME_SIZE];
+ } u;
+ UCHAR Reserved[392];
+} PS_JOB_RECORD, *PPS_JOB_RECORD;
+
+#define PS_JOB_RECORD_SIZE sizeof(PS_JOB_RECORD)
+
+
+/*++
+*******************************************************************
+ FUCNTION PROTOTYPES
+*******************************************************************
+--*/
+
+/** ATTACH.C **/
+
+unsigned int
+AttachToFileServer(
+ unsigned char *pServerName,
+ unsigned int *pNewConnectionId
+ );
+
+unsigned int
+DetachFromFileServer(
+ unsigned int ConnectionId
+ );
+
+/** NCP.C **/
+
+unsigned int
+GetBinderyObjectID(
+ unsigned int ConnectionHandle,
+ char *pObjectName,
+ unsigned short ObjectType,
+ unsigned long *pObjectId
+ );
+
+
+/** CONNECT.C **/
+
+unsigned int
+GetDefaultConnectionID(
+ unsigned int *pConnectionHandle
+ );
+
+unsigned int
+GetConnectionHandle(
+ unsigned char *pServerName,
+ unsigned int *pConnectionHandle
+ );
+
+unsigned int
+GetConnectionNumber(
+ unsigned int ConnectionHandle,
+ unsigned int *pConnectionNumber
+ );
+
+unsigned int
+GetFileServerName(
+ unsigned int ConnectionHandle,
+ char *pServerName
+ );
+
+unsigned int
+GetInternetAddress(
+ unsigned int ConnectionHandle,
+ unsigned int ConnectionNumber,
+ unsigned char *pInternetAddress
+ );
+
+/** DRIVE.C **/
+
+unsigned int
+GetDriveStatus(
+ unsigned short DriveNumber,
+ unsigned short PathFormat,
+ unsigned short *pStatus,
+ unsigned int *pConnectionHandle,
+ unsigned char *pRootPath,
+ unsigned char *pRelativePath,
+ unsigned char *pFullPath
+ );
+
+unsigned int
+GetFirstDrive(
+ unsigned short *pFirstDrive
+ );
+
+unsigned int
+ParsePath(
+ unsigned char *pPath,
+ unsigned char *pServerName, //OPTIONAL
+ unsigned char *pVolumeName, //OPTIONAL
+ unsigned char *pDirPath //OPTIONAL
+ );
+
+unsigned int
+SetDriveBase(
+ unsigned short DriveNumber,
+ unsigned char *ServerName,
+ unsigned int DirHandle,
+ unsigned char *pDirPath
+ );
+
+unsigned int
+DeleteDriveBase(
+ unsigned short DriveNumber
+ );
+
+unsigned int
+GetDirectoryPath(
+ unsigned char ConnectionHandle,
+ unsigned char Handle,
+ unsigned char *pPath
+ );
+
+unsigned int
+IsDriveRemote(
+ unsigned char DriveNumber,
+ unsigned int *pRemote
+ );
+
+/** CAPTURE.C **/
+
+unsigned int
+EndCapture(
+ unsigned char LPTDevice
+ );
+
+#define PS_ERR_BAD_VERSION 0x7770
+#define PS_ERR_GETTING_DEFAULT 0x7773
+#define PS_ERR_OPENING_DB 0x7774
+#define PS_ERR_READING_DB 0x7775
+#define PS_ERR_READING_RECORD 0x7776
+#define PS_ERR_INTERNAL_ERROR 0x7779
+#define PS_ERR_NO_DEFAULT_SPECIFIED 0x777B
+
+unsigned int
+PSJobGetDefault(
+ unsigned int ConnectionHandle,
+ unsigned short SearchFlag,
+ unsigned char *pOwner,
+ unsigned char *pJobName,
+ PPS_JOB_RECORD pJobRecord
+ );
+
+unsigned int
+PSJobRead(
+ unsigned int ConnectionHandle,
+ unsigned char *pOwner,
+ unsigned char *pJobName,
+ PPS_JOB_RECORD pJobRecord
+ );
+
+unsigned int
+PS40JobGetDefault(
+ unsigned int NDSCaptureFlag,
+ unsigned short SearchFlag,
+ unsigned char *pOwner,
+ unsigned char *pJobName,
+ PPS_JOB_RECORD pJobRecord
+ );
+
+unsigned int
+PS40JobRead(
+ unsigned int NDSCaptureFlag,
+ unsigned char *pOwner,
+ unsigned char *pJobName,
+ PPS_JOB_RECORD pJobRecord
+ );
+
+unsigned int
+GetCaptureFlags(
+ unsigned char LPTDevice,
+ PNETWARE_CAPTURE_FLAGS_RW pCaptureFlagsRW,
+ PNETWARE_CAPTURE_FLAGS_RO pCaptureFlagsRO
+ );
+
+unsigned int
+StartQueueCapture(
+ unsigned int ConnectionHandle,
+ unsigned char LPTDevice,
+ unsigned char *pServerName,
+ unsigned char *pQueueName
+ );
+
+unsigned int
+GetDefaultPrinterQueue (
+ unsigned int ConnectionHandle,
+ unsigned char *pServerName,
+ unsigned char *pQueueName
+ );
+
+#endif /* _NWLIBS_H_ */
diff --git a/private/nw/nwscript/inc/nwscript.h b/private/nw/nwscript/inc/nwscript.h
new file mode 100644
index 000000000..d930e7818
--- /dev/null
+++ b/private/nw/nwscript/inc/nwscript.h
@@ -0,0 +1,321 @@
+/******************************************************************************
+*
+* NWSCRIPT.H
+*
+* This module contains typedefs and defines required for the
+* NetWare script utility.
+*
+* Copyright (c) 1995 Microsoft Corporation
+*
+* $Log: N:\NT\PRIVATE\NW4\NWSCRIPT\INC\VCS\NWSCRIPT.H $
+*
+* Rev 1.10 18 Apr 1996 16:53:02 terryt
+* Various enhancements
+*
+* Rev 1.9 10 Apr 1996 14:22:36 terryt
+* Hotfix for 21181hq
+*
+* Rev 1.10 12 Mar 1996 19:42:52 terryt
+* Relative NDS name support
+*
+* Rev 1.9 07 Mar 1996 18:34:46 terryt
+* Misc fixes
+*
+* Rev 1.8 22 Jan 1996 16:44:02 terryt
+* Add automatic map attaches
+*
+* Rev 1.7 08 Jan 1996 13:58:34 terryt
+* Correct NDS Preferred Server
+*
+* Rev 1.6 05 Jan 1996 17:19:08 terryt
+* Ensure context is the correct login default
+*
+* Rev 1.5 04 Jan 1996 18:58:34 terryt
+* Bug fixes reported by MS
+*
+* Rev 1.4 22 Dec 1995 14:20:34 terryt
+* Add Microsoft headers
+*
+* Rev 1.3 28 Nov 1995 17:13:56 terryt
+* Cleanup resource file
+*
+* Rev 1.2 22 Nov 1995 15:44:34 terryt
+* Use proper NetWare user name call
+*
+* Rev 1.1 20 Nov 1995 16:11:34 terryt
+* Context and capture changes
+*
+* Rev 1.0 15 Nov 1995 18:05:38 terryt
+* Initial revision.
+*
+* Rev 1.5 25 Aug 1995 17:03:52 terryt
+* CAPTURE support
+*
+* Rev 1.4 18 Jul 1995 16:07:52 terryt
+* Screen out capture commands
+*
+* Rev 1.3 17 Jul 1995 09:43:02 terryt
+* Use Microsoft name for environment
+*
+* Rev 1.2 23 Jun 1995 09:49:58 terryt
+* Add error message for mapping over MS network drive
+*
+* Rev 1.1 23 May 1995 19:38:14 terryt
+* Spruce up source
+*
+* Rev 1.0 15 May 1995 19:09:42 terryt
+* Initial revision.
+*
+******************************************************************************/
+
+
+#define SCRIPT_ENVIRONMENT_VALUENAME L"Volatile Environment"
+#define REGISTRY_PROVIDER L"System\\CurrentControlSet\\Services\\NWCWorkstation\\networkProvider"
+#define REGISTRY_PROVIDERNAME L"Name"
+
+typedef enum SYNTAX
+{
+ NDSI_UNKNOWN, /* 0 */
+ NDSI_DIST_NAME, /* 1 */
+ NDSI_CE_STRING, /* 2 */
+ NDSI_CI_STRING, /* 3 */
+ NDSI_PR_STRING, /* 4 */
+ NDSI_NU_STRING, /* 5 */
+ NDSI_CI_LIST, /* 6 */
+ NDSI_BOOLEAN, /* 7 */
+ NDSI_INTEGER, /* 8 */
+ NDSI_OCTET_STRING, /* 9 */
+ NDSI_TEL_NUMBER, /* 10 */
+ NDSI_FAX_NUMBER, /* 11 */
+ NDSI_NET_ADDRESS, /* 12 */
+ NDSI_OCTET_LIST, /* 13 */
+ NDSI_EMAIL_ADDRESS, /* 14 */
+ NDSI_PATH, /* 15 */
+ NDSI_REPLICA_POINTER, /* 16 */
+ NDSI_OBJECT_ACL, /* 17 */
+ NDSI_PO_ADDRESS, /* 18 */
+ NDSI_TIMESTAMP, /* 19 */
+ NDSI_CLASS_NAME, /* 20 */
+ NDSI_STREAM, /* 21 */
+ NDSI_COUNTER, /* 22 */
+ NDSI_BACK_LINK, /* 23 */
+ NDSI_TIME, /* 24 */
+ NDSI_TYPED_NAME, /* 25 */
+ NDSI_HOLD, /* 26 */
+ NDSI_INTERVAL, /* 27 */
+ NDSI_TAX_COUNT /* 28 */
+} SYNTAX;
+
+#define DSCL_AFP_SERVER "AFP Server"
+#define DSCL_ALIAS "Alias"
+#define DSCL_BINDERY_OBJECT "Bindery Object"
+#define DSCL_BINDERY_QUEUE "Bindery Queue"
+#define DSCL_COMPUTER "Computer"
+#define DSCL_COUNTRY "Country"
+#define DSCL_DEVICE "Device"
+#define DSCL_DIRECTORY_MAP "Directory Map"
+#define DSCL_EXTERNAL_ENTITY "External Entity"
+#define DSCL_GROUP "Group"
+#define DSCL_LIST "List"
+#define DSCL_LOCALITY "Locality"
+#define DSCL_MESSAGE_ROUTING_GROUP "Message Routing Group"
+#define DSCL_MESSAGING_SERVER "Messaging Server"
+#define DSCL_NCP_SERVER "NCP Server"
+#define DSCL_ORGANIZATION "Organization"
+#define DSCL_ORGANIZATIONAL_PERSON "Organizational Person"
+#define DSCL_ORGANIZATIONAL_ROLE "Organizational Role"
+#define DSCL_ORGANIZATIONAL_UNIT "Organizational Unit"
+#define DSCL_PARTITION "Partition"
+#define DSCL_PERSON "Person"
+#define DSCL_PRINT_SERVER "Print Server"
+#define DSCL_PRINTER "Printer"
+#define DSCL_PROFILE "Profile"
+#define DSCL_QUEUE "Queue"
+#define DSCL_RESOURCE "Resource"
+#define DSCL_SERVER "Server"
+#define DSCL_TOP "Top"
+#define DSCL_UNKNOWN "Unknown"
+#define DSCL_USER "User"
+#define DSCL_VOLUME "Volume"
+
+#define DSAT_HOST_SERVER "Host Server"
+#define DSAT_HOST_RESOURCE_NAME "Host Resource Name"
+#define DSAT_PATH "Path"
+
+void ConvertUnicodeToAscii( PVOID );
+
+void NTGetTheDate( unsigned int *, unsigned char *, unsigned char * );
+void NTGetVersionOfShell( char *, unsigned char *, unsigned char *, unsigned char * );
+void NTBreakOff( void );
+void NTBreakOn( void );
+unsigned short NTNetWareDriveStatus( unsigned short );
+unsigned int NTGetNWDrivePath( unsigned short, unsigned char *, unsigned char * );
+char * NTNWtoUNCFormat( char * );
+unsigned int NTLoginToFileServer( char *, char *, char * );
+unsigned int NTAttachToFileServer( unsigned char *, unsigned int * );
+unsigned int NTIsConnected( unsigned char * );
+unsigned int NTSetDriveBase( unsigned char *, unsigned char *, unsigned char * );
+unsigned int NTGetUserID( unsigned int, unsigned long * );
+unsigned int NTIsNetWareDrive( unsigned int );
+void NTInitProvider( void );
+void DisplayMessage( unsigned int, ... );
+void DisplayOemString( char * );
+void ExportEnv( unsigned char * );
+void ExportCurrentDirectory( int );
+void ExportCurrentDrive( int );
+void GetOldPaths( void );
+void NTPrintExtendedError( void );
+unsigned int NTGetCurrentDirectory( unsigned char, unsigned char * );
+void Capture( char ** argv, unsigned int );
+unsigned int ConverNDSPathToNetWarePathA(char *, char *, char *);
+
+#define CONTEXT_MAX 256
+#define ATTRBUFSIZE 2048
+#define NDS_NAME_CHARS 1024
+
+
+unsigned int NDSInitUserProperty( void );
+unsigned int NDSGetUserProperty( PBYTE, PBYTE Data, unsigned int, SYNTAX *, unsigned int * );
+void NDSGetVar ( PBYTE, PBYTE, unsigned int );
+unsigned int NDSChangeContext( PBYTE );
+unsigned int NDSGetContext( PBYTE, unsigned int );
+unsigned int Is40Server( unsigned int );
+unsigned int NDSfopenStream ( PBYTE, PBYTE, PHANDLE, unsigned int * );
+unsigned int IsMemberOfNDSGroup( PBYTE );
+unsigned int NDSGetProperty ( PBYTE, PBYTE, PBYTE, unsigned int, unsigned int * );
+unsigned int NDSTypeless( LPSTR, LPSTR );
+void CleanupExit( int );
+void NDSCleanup( void );
+int NTGetNWUserName( PWCHAR, PWCHAR, int );
+unsigned int NDSGetClassName( LPSTR, LPSTR );
+
+unsigned int NDSCanonicalizeName( PBYTE, PBYTE, int, int );
+
+#define LIST_3X_SERVER 1
+#define LIST_4X_SERVER 2
+
+BOOL IsServerInAttachList( char *, unsigned int );
+void AddServerToAttachList( char *, unsigned int );
+int DoAttachProcessing( char * );
+
+#define FLAGS_LOCAL_CONTEXT 0x1
+#define FLAGS_NO_CONTEXT 0x2
+#define FLAGS_TYPED_NAMES 0x4
+
+unsigned int NDSAbbreviateName( DWORD, LPSTR, LPSTR );
+
+/*
+ * Resource string IDs
+ */
+#define IDR_ERROR 100
+#define IDR_NO_DEFAULT_CONNECTION 101
+#define IDR_NO_KNOWN_FILE_SERVER 102
+#define IDR_LOCAL_DRIVE 103
+#define IDR_NETWARE_DRIVE 104
+#define IDR_DASHED_LINE 105
+#define IDR_LOCAL_SEARCH 106
+#define IDR_NETWARE_SEARCH 107
+#define IDR_NOT_ENOUGH_MEMORY 108
+#define IDR_PASSWORD 109
+#define IDR_ATTACHED 110
+#define IDR_ACCESS_DENIED 111
+#define IDR_UNAUTHORIZED_LOGIN_TIME 112
+#define IDR_LOGIN_DENIED_NO_CONNECTION 113
+#define IDR_UNAUTHORIZED_LOGIN_STATION 114
+#define IDR_ACCOUNT_DISABLED 115
+#define IDR_PASSWORD_EXPRIED_NO_GRACE 116
+#define IDR_MAP_NOT_ATTACHED_SERVER 117
+#define IDR_MAP_USAGE 118
+#define IDR_UNDEFINED 119
+#define IDR_DIRECTORY_NOT_FOUND 120
+#define IDR_VOLUME_NOT_EXIST 121
+#define IDR_WRONG_DRIVE 122
+#define IDR_DEL_DRIVE 123
+#define IDR_DEL_SEARCH_DRIVE 124
+#define IDR_SEARCH_DRIVE_NOT_EXIST 125
+#define IDR_NOT_NETWORK_DRIVE 126
+#define IDR_NO_DRIVE_AVAIL 127
+#define IDR_INVALID_PATH 128
+#define IDR_CAN_NOT_CHANGE_DRIVE 129
+#define IDR_MAP_INVALID_PATH 130
+#define IDR_MAP_FAILED 131
+#define IDR_NO_SCRIPT_FILE 132
+#define IDR_STRIKE_KEY 133
+#define IDR_CANNOT_EXECUTE 134
+#define IDR_ENOENT 135
+#define IDR_EXIT_NOT_SUPPORTED 136
+#define IDR_IF_TOO_DEEP 137
+#define IDR_SCRIPT_ERROR 138
+#define IDR_ORIGINAL_LINE_WAS 139
+#define IDR_BAD_COMMAND 140
+#define IDR_LABEL_NOT_FOUND 141
+#define IDR_NO_VOLUME 142
+#define IDR_ERROR_DURING 143
+#define IDR_MAP_ERROR 144
+#define IDR_ENTER_SERVER_NAME 145
+#define IDR_ENTER_LOGIN_NAME 146
+#define IDR_ERROR_SET_DEFAULT_DRIVE 147
+#define IDR_ERROR_OPEN_SCRIPT 148
+#define IDR_DIVIDE_BY_ZERO 149
+#define IDR_NEWLINE 150
+#define IDR_SERVER_USER 151
+#define IDR_NON_NETWARE_NETWORK_DRIVE 152
+#define IDR_CAPTURE_USAGE 153
+#define IDR_COPIES_EXPECTED 154
+#define IDR_COPIES_OUTOF_RANGE 155
+#define IDR_FILE_CAPTURE_UNSUPPORTED 156
+#define IDR_FORM_EXPECTED 157
+#define IDR_INVALID_BANNER 158
+#define IDR_INVALID_FORM_NAME 159
+#define IDR_INVALID_FORM_TYPE 160
+#define IDR_INVALID_LPT_NUMBER 161
+#define IDR_INVALID_PATH_NAME 162
+#define IDR_JOB_NOT_FOUND 163
+#define IDR_LPT_NUMBER_EXPECTED 164
+#define IDR_LPT_STATUS 165
+#define IDR_NOT_ACTIVE 166
+#define IDR_NO_AUTOENDCAP 167
+#define IDR_NO_PRINTERS 168
+#define IDR_LPT_STATUS_NO_BANNER 169
+#define IDR_QUEUE_NOT_EXIST 170
+#define IDR_SERVER_NOT_FOUND 171
+#define IDR_SUCCESS_QUEUE 172
+#define IDR_TABSIZE_OUTOF_RANGE 173
+#define IDR_TAB_SIZE_EXPECTED 174
+#define IDR_TIMEOUT_OUTOF_RANGE 175
+#define IDR_TIME_OUT_EXPECTED 176
+#define IDR_UNKNOW_FLAG 177
+#define IDR_DISABLED 178
+#define IDR_ENABLED 179
+#define IDR_YES 180
+#define IDR_NO 181
+#define IDR_SECONDS 182
+#define IDR_CONVERT_TO_SPACE 183
+#define IDR_NO_CONVERSION 184
+#define IDR_NOTIFY_USER 185
+#define IDR_NOT_NOTIFY_USER 186
+#define IDR_NONE 187
+#define IDR_CONNECTION_REFUSED 188
+#define IDR_LASTLOGIN_PM 189
+#define IDR_LASTLOGIN_AM 190
+#define IDR_ALL_LOCAL_DRIVES 191
+#define IDR_CHANGE_CONTEXT_ERROR 192
+#define IDR_GET_CONTEXT_ERROR 193
+#define IDR_DISPLAY_CONTEXT 194
+#define IDR_LPT_STATUS_NDS 195
+#define IDR_LPT_STATUS_NO_BANNER_NDS 196
+#define IDR_NO_QUEUE 197
+#define IDR_LASTLOGIN 198
+#define IDR_TREE_OPEN_FAILED 199
+#define IDR_NDS_CONTEXT_INVALID 200
+#define IDR_NDS_USERNAME_FAILED 201
+#define IDR_QUERY_INFO_FAILED 202
+#define IDR_NO_RESPONSE 203
+#define IDR_NDSQUEUE_NOT_EXIST 204
+#define IDR_NDSSUCCESS_QUEUE 205
+#define IDR_CAPTURE_FAILED 206
+#define IDR_CURRENT_TREE 207
+#define IDR_CURRENT_SERVER 208
+#define IDR_CURRENT_CONTEXT 209
+#define IDR_AUTHENTICATING_SERVER 210
+#define IDR_NO_END_QUOTE 211
diff --git a/private/nw/nwscript/lsparse.c b/private/nw/nwscript/lsparse.c
new file mode 100644
index 000000000..1b0e67fd1
--- /dev/null
+++ b/private/nw/nwscript/lsparse.c
@@ -0,0 +1,315 @@
+/*
+ * LSPARSE.C - NetWare Login Script processing routines for our Win32
+ * NetWare 3.x LOGIN clone.
+ *
+ * Based on code contained in NWPARSE.C, written by Xiao Ying Ding.
+ *
+ * Modified and re-written for Win32 by J. SOUZA, February 1994.
+ *
+ * Modified for NT by Terry Treder
+ *
+ * Copyright (C)1994 Microsoft Corporation.
+ *
+ */
+
+#include <common.h>
+
+/********************************************************************
+
+ ConverNDSPathToNetWarePathA
+
+Routine Description:
+
+ Convert a NDS path to a Netware format path
+
+Arguments:
+ ndspath - origninal NDS path
+ objclass - type of NDS object, NULL if unknown
+ nwpath - Netware format path
+
+Return Value:
+ error
+
+ *******************************************************************/
+unsigned int
+ConverNDSPathToNetWarePathA(char *ndspath, char *objclass, char *nwpath)
+{
+ CHAR szDN[MAX_PATH];
+ CHAR szObjName[MAX_PATH];
+ CHAR cSave;
+ CHAR className[MAX_PATH];
+
+ LPSTR lpDelim = NULL;
+ LPSTR lpFilePath = "";
+ LPSTR lpszValue;
+ LPSTR path;
+ LPSTR volume;
+
+ DWORD dwRet;
+ DWORD length;
+ UINT NWStatus;
+ char bufAttribute[2048];
+
+ // optimize for path beginning with drive letter
+ // This assumes NDS volume and dir map names are at least 2 chars
+
+ if (ndspath[1] == ':')
+ return 1;
+ // strip ':' from path before this call
+ if ( ( lpDelim = strchr(ndspath,':') ) != NULL
+ || ((lpDelim = strchr(ndspath,'\\')) != NULL)) {
+ cSave = *lpDelim;
+ *lpDelim = '\0';
+ lpFilePath = lpDelim+1;
+ }
+
+ if ( objclass == NULL ) {
+
+ NWStatus = NDSCanonicalizeName( ndspath, szObjName, MAX_PATH, TRUE );
+
+ if ( NWStatus != 0 ) {
+#ifdef DEBUG
+ printf("can't canonicalize [%s] (0x%x)\n",
+ ndspath, NWStatus );
+#endif
+
+ if (lpDelim) {
+ *lpDelim = cSave;
+ }
+
+ return 1;
+ }
+
+
+ NWStatus = NDSGetClassName( szObjName, className );
+
+ if ( NWStatus != 0 ||
+ strcmp ( className, DSCL_SERVER ) &&
+ strcmp ( className, DSCL_NCP_SERVER ) &&
+ strcmp ( className, DSCL_VOLUME ) &&
+ strcmp ( className, DSCL_QUEUE ) &&
+ strcmp ( className, DSCL_DIRECTORY_MAP )) {
+
+#ifdef DEBUG
+ printf("no path DSOBJ: %d (%s) (%s)\n",
+ NWStatus, szObjName, className );
+#endif
+
+ if (lpDelim) {
+ *lpDelim = cSave;
+ }
+
+ return 1;
+ }
+
+ objclass = className;
+ }
+ else
+ strcpy ( szObjName, ndspath );
+
+ if (lpDelim) {
+ *lpDelim = cSave;
+ }
+
+#ifdef DEBUG
+ printf("ConvertNDSPath BEFORE [%s]\n", szObjName);
+#endif
+
+ //
+ // Is f this is the server class object , we only need
+ // to extract it's common name and put into netware format
+ //
+ if ((strcmp(objclass,DSCL_SERVER) == 0 ) ||
+ (strcmp(objclass,DSCL_NCP_SERVER) == 0 )) {
+
+ // Abbreaviate first to remove type qualifiers
+ *szDN = '\0';
+ if (0 != NDSAbbreviateName(FLAGS_LOCAL_CONTEXT,(LPSTR)szObjName,szDN)) {
+ return 1;
+ }
+
+ // BUGBUG THis code should be separated as tokenizing function
+ lpDelim = strchr(szDN,'.');
+ if (lpDelim) {
+ *lpDelim = '\0';
+ }
+
+ strcpy(nwpath,szDN);
+
+#ifdef DEBUG
+ printf("Returning Netware path:%s\n",nwpath);
+#endif
+
+ return 0;
+
+ } /* endif server class */
+
+ //
+ // If this is share class object ( volume or queue), we need
+ // to find it's host server name and host resource name
+ //
+ if ((strcmp(objclass,DSCL_VOLUME) == 0 ) ||
+ (strcmp(objclass,DSCL_QUEUE) == 0 )
+ ) {
+
+ //
+ // Read host server name first. It comes back as distinguished
+ // directory name, so we will need to extract server name from it
+ //
+
+ NWStatus = NDSGetProperty ( szObjName,
+ DSAT_HOST_SERVER,
+ bufAttribute,
+ sizeof(bufAttribute),
+ NULL );
+
+ if (NWStatus != 0) {
+#ifdef DEBUG
+ printf("Get host server failed. err=0x%x\n",NWStatus);
+#endif
+ return 1;
+ }
+
+ lpszValue = bufAttribute;
+ ConvertUnicodeToAscii( lpszValue );
+
+ //
+ // Now copy server distinguished name into temporary buffer
+ // and call ourselves to convert it to Netware
+ //
+ strcpy(szDN,lpszValue);
+
+ dwRet = ConverNDSPathToNetWarePathA(szDN, DSCL_SERVER, nwpath);
+ if (dwRet) {
+#ifdef DEBUG
+ printf("Resolving server DN failed\n");
+#endif
+ //Break();
+ return 1;
+ }
+
+ //
+ // Get volume name itself
+ //
+ NWStatus = NDSGetProperty ( szObjName,
+ DSAT_HOST_RESOURCE_NAME,
+ bufAttribute,
+ sizeof(bufAttribute),
+ NULL );
+
+ if (NWStatus != 0) {
+#ifdef DEBUG
+ printf("Get host resource name failed. err=0x%x\n",NWStatus);
+#endif
+ return 1;
+ }
+
+ lpszValue = bufAttribute;
+ ConvertUnicodeToAscii( lpszValue );
+
+ //
+ // Now we already have server name in the user buffer,
+ // append share name to it
+ strcat(nwpath,"/");
+ strcat(nwpath,lpszValue);
+ strcat(nwpath,":");
+ strcat(nwpath, lpFilePath );
+
+#ifdef DEBUG
+ printf("Returning Netware path:%s\n",nwpath);
+#endif
+
+ return 0;
+
+ } /* endif Volume class */
+
+ //
+ // For directory maps we need to find host volume NDS name and
+ // append relative directory path
+ //
+ if (strcmp(objclass,DSCL_DIRECTORY_MAP) == 0 ) {
+
+ //
+ // First get NDS name for host volume object
+ //
+
+ NWStatus = NDSGetProperty ( szObjName,
+ DSAT_PATH,
+ bufAttribute,
+ sizeof(bufAttribute),
+ NULL );
+
+ if (NWStatus != 0) {
+#ifdef DEBUG
+ printf("Get path %s failed. err=0x%x\n", szObjName, NWStatus);
+#endif
+ return 1;
+ }
+
+ volume = bufAttribute;
+ volume += sizeof(DWORD);
+ volume += sizeof(DWORD);
+ ConvertUnicodeToAscii( volume );
+
+ // Path is next
+
+ path = bufAttribute;
+ path += sizeof(DWORD);
+ length = ROUNDUP4(*(DWORD *)path);
+ path += sizeof(DWORD);
+ path += length;
+
+ //
+ // Check for 0 length paths
+ //
+ if ( *(DWORD *)path == 0 ) {
+ path = "";
+ }
+ else {
+ path += sizeof(DWORD);
+ ConvertUnicodeToAscii( path );
+ }
+
+#ifdef DEBUG
+ printf("path is %s\n",path);
+#endif
+
+ //
+ // Now copy volume distinguished name into temporary buffer
+ // and call ourselves to convert it to NetWare
+ //
+ strcpy(szDN,volume);
+
+ dwRet = ConverNDSPathToNetWarePathA(szDN, DSCL_VOLUME, nwpath);
+ if (dwRet) {
+#ifdef DEBUG
+ printf("Resolving volume DN failed\n");
+#endif
+ //Break();
+ return 1;
+ }
+
+ //
+ // Now we already have NetWare server\volume name in the user buffer,
+ // append directory path to it
+ //strcat(nwpath,"\\");
+ // we want only one '\'
+ if (path[0] == '\\' || path[0] == '/') path++;
+ strcat(nwpath,path);
+ // append non-NDS part of path, if any
+ if (*lpFilePath) {
+ strcat(nwpath,"/");
+ strcat(nwpath, lpFilePath );
+ }
+
+#ifdef DEBUG
+ printf("Returning NetWare path:%s\n",nwpath);
+#endif
+
+ return 0;
+
+ } /* endif DirectoryMap class */
+
+ return(1);
+}
+
diff --git a/private/nw/nwscript/makefile b/private/nw/nwscript/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/nw/nwscript/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/nwscript/maplist.c b/private/nw/nwscript/maplist.c
new file mode 100644
index 000000000..5dbe8a132
--- /dev/null
+++ b/private/nw/nwscript/maplist.c
@@ -0,0 +1,252 @@
+
+/*************************************************************************
+*
+* QATTACH.C
+*
+* Do any neccessary attach user queries
+*
+* Copyright (c) 1995 Microsoft Corporation
+*
+* $Log: N:\NT\PRIVATE\NW4\NWSCRIPT\VCS\MAPLIST.C $
+*
+* Rev 1.1 10 Apr 1996 14:22:42 terryt
+* Hotfix for 21181hq
+*
+* Rev 1.1 12 Mar 1996 19:53:58 terryt
+* Relative NDS names and merge
+*
+* Rev 1.0 22 Jan 1996 16:49:24 terryt
+* Initial revision.
+*
+*************************************************************************/
+#include "common.h"
+
+//
+// 4X login script behavior for map commands to servers not in the
+// logged in NDS tree that have not been ATTACH'ed is different from
+// the 3X behavior.
+//
+// The 4X behavior is to always ask for a user name and password for
+// these servers, doing the attach at that point. The user gets
+// two trys.
+//
+// Since NT doesn't have an list of attached servers, and will try
+// to connect to a volume with the default user name and password,
+// a wrapper must be put around the MAP commands. This code
+// must determine that a bindery connection will be made and that
+// this server has not previously been MAP'ed or ATTACH'ed.
+// The user will be always prompted for user name and password.
+// The server will then be logged into with those credentials.
+//
+// One problem with the below is that it's not easy to tell that
+// a connection "will be" made with the bindery, this is done in
+// the redirector. So to simplify things the assumption is that
+// only 3X servers use bindery connections. This means that
+// 4X servers using bindery emulation on a different NDS tree will
+// not always be asked for the user name and password.
+//
+// Already processed servers are kept in a list and marked as 4X or 3X
+// for possible future use.
+//
+// The behavior for a 3X login depends on the LOGIN.EXE version.
+// The old behavior is that you must always ATTACH before mapping.
+// However, if you login to a 3X server with a 4X version LOGIN.EXE
+// it will try to authenticate using your user name (and password)
+// on the first attempt and ask for a password if that fails. The
+// second attempt will ask for your user name. Since this 4X behavior
+// is more forgiving (more scripts "work") that is the one being
+// emulated.
+//
+
+typedef struct _SERVERLIST
+{
+ char * ServerName;
+ unsigned int ServerType;
+ struct _SERVERLIST *pNextServer;
+} SERVERLIST, *PSERVERLIST;
+
+PSERVERLIST pMainList = NULL;
+
+BOOL IsServerInAttachList( char *, unsigned int );
+void AddServerToAttachList( char *, unsigned int );
+int DoAttachProcessing( char * );
+
+/*
+ * Scan the list for the server
+ */
+BOOL
+IsServerInAttachList( char * Server, unsigned int ServerType )
+{
+ PSERVERLIST pServerList = pMainList;
+
+ while ( pServerList != NULL )
+ {
+ if ( !_strcmpi( Server, pServerList->ServerName ) &&
+ ( ServerType & pServerList->ServerType ) )
+ return TRUE;
+ pServerList = pServerList->pNextServer;
+ }
+
+ return FALSE;
+}
+
+/*
+ * Add the server to the list of attached servers
+ *
+ * This is used during MAP's and ATTACH's
+ */
+void
+AddServerToAttachList( char * Server, unsigned int ServerType )
+{
+ PSERVERLIST pServerList;
+
+ pServerList = (PSERVERLIST) malloc( sizeof( SERVERLIST ) );
+
+ if ( pServerList == NULL )
+ {
+ DisplayMessage( IDR_NOT_ENOUGH_MEMORY );
+ return;
+ }
+
+ pServerList->ServerName = _strdup( Server );
+ pServerList->ServerType = ServerType;
+ pServerList->pNextServer = pMainList;
+ pMainList = pServerList;
+}
+
+/*
+ * Do any Attach processing
+ * Return error code. 0 is success.
+ * 880F is the special "attached failed" error
+ */
+
+int
+DoAttachProcessing( char * PossibleServer )
+{
+ unsigned int iRet = 0;
+ unsigned int conn;
+ char userName[MAX_NAME_LEN] = "";
+ char password[MAX_PASSWORD_LEN] = "";
+ BOOL AlreadyConnected = FALSE;
+
+ //
+ // Must have a server to process
+ //
+ if ( !*PossibleServer )
+ return iRet;
+
+ // See if this server has been processed before
+ // No since in doing a 4X server twice, and you only ask
+ // for the user name and password once.
+
+ if ( IsServerInAttachList( PossibleServer,
+ LIST_4X_SERVER | LIST_3X_SERVER ) )
+ return iRet;
+
+ // See if there is already a connection to the server
+
+ if ( NTIsConnected( PossibleServer ) )
+ AlreadyConnected = TRUE;
+ else
+ AlreadyConnected = FALSE;
+
+ // Try and attach to the server
+
+ iRet = NTAttachToFileServer( PossibleServer, &conn );
+
+ // If attach failed, return
+
+ if ( iRet )
+ return iRet;
+
+ // If this is a 4X server then add it to the list of attached
+ // servers. We don't want to do this again. 4X servers must
+ // use the NDS attachment anyway (or at least I don't see a
+ // way of telling that it's going to be a bindery emulation
+ // connection ahead of time).
+
+ if ( fNDS && Is40Server( conn ) )
+ {
+ AddServerToAttachList( PossibleServer, LIST_4X_SERVER );
+ DetachFromFileServer ( conn );
+ return iRet;
+ }
+
+ // Close that first connection
+
+ DetachFromFileServer ( conn );
+
+ // If we are already connected, don't mess with things
+ // The credentials can't be changed anyway
+
+ if ( AlreadyConnected )
+ {
+ AddServerToAttachList( PossibleServer, LIST_3X_SERVER );
+ return iRet;
+ }
+
+ // Ask for user name on an NDS login
+ //
+ // Use the current login name for a 3X login on the first attempt
+
+ if ( fNDS )
+ {
+ DisplayMessage(IDR_ENTER_LOGIN_NAME, PossibleServer);
+ if (!ReadName(userName))
+ return 0x880F;
+ }
+ else
+ {
+ strncpy( userName, LOGIN_NAME, sizeof( userName ) );
+ }
+
+ // Try to log the user in, asking for a password
+
+ iRet = Login( userName,
+ PossibleServer,
+ password,
+ TRUE );
+
+ // Clear out the password
+ // We don't need it again
+
+ memset( password, 0, sizeof( password ) );
+
+ // If failed, give the user one more chance
+
+ if ( iRet )
+ {
+ // Ask for user name
+
+ DisplayMessage(IDR_ENTER_LOGIN_NAME, PossibleServer);
+ if (!ReadName(userName))
+ return 0x880F;
+
+ // Try to log the user in
+
+ iRet = Login( userName,
+ PossibleServer,
+ password,
+ TRUE );
+
+ // Clear out the password
+
+ memset( password, 0, sizeof( password ) );
+
+ }
+
+ // Add servername to list of attached servers, marked as 3X
+
+ if ( !iRet )
+ {
+ AddServerToAttachList( PossibleServer, LIST_3X_SERVER );
+ }
+ else
+ {
+ iRet = 0x880F; // Special I am not attached error
+ }
+
+ return iRet;
+
+}
+
diff --git a/private/nw/nwscript/ncp.c b/private/nw/nwscript/ncp.c
new file mode 100644
index 000000000..72b453350
--- /dev/null
+++ b/private/nw/nwscript/ncp.c
@@ -0,0 +1,356 @@
+
+/*************************************************************************
+*
+* NCP.C
+*
+* All routines doing direct NCPs or filecontrol operations
+*
+* Copyright (c) 1995 Microsoft Corporation
+*
+* $Log: N:\NT\PRIVATE\NW4\NWSCRIPT\VCS\NCP.C $
+*
+* Rev 1.2 10 Apr 1996 14:22:50 terryt
+* Hotfix for 21181hq
+*
+* Rev 1.2 12 Mar 1996 19:54:06 terryt
+* Relative NDS names and merge
+*
+* Rev 1.1 22 Dec 1995 14:24:56 terryt
+* Add Microsoft headers
+*
+* Rev 1.0 15 Nov 1995 18:07:10 terryt
+* Initial revision.
+*
+*************************************************************************/
+#include <stdio.h>
+#include <direct.h>
+#include <time.h>
+#include <stdlib.h>
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include <nwapi32.h>
+#include <ntddnwfs.h>
+
+#include "nwscript.h"
+#include "ntnw.h"
+#include "inc/nwlibs.h"
+
+
+/********************************************************************
+
+ NTGetUserID
+
+Routine Description:
+
+ Given a connection handle, return the user ID
+
+Arguments:
+
+ ConnectionHandle - Connection Handle
+ UserID - returned User ID
+
+Return Value:
+ 0 = success
+ else NT error
+
+ *******************************************************************/
+unsigned int
+NTGetUserID(
+ unsigned int ConnectionHandle,
+ unsigned long *pUserID
+ )
+{
+ NTSTATUS NtStatus ;
+ unsigned int ObjectType;
+ unsigned char LoginTime[7];
+ unsigned char UserName[48];
+ VERSION_INFO VerInfo;
+ unsigned int Version;
+ unsigned int ConnectionNum;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)ConnectionHandle ;
+
+ NtStatus = GetConnectionNumber( ConnectionHandle, &ConnectionNum );
+
+ if (!NT_SUCCESS(NtStatus))
+ return NtStatus;
+
+ NtStatus = NWGetFileServerVersionInfo( (NWCONN_HANDLE)ConnectionHandle,
+ &VerInfo );
+
+ if (!NT_SUCCESS(NtStatus))
+ return NtStatus;
+
+ Version = VerInfo.Version * 1000 + VerInfo.SubVersion * 10;
+
+ if ( ( Version >= 3110 ) || ( Version < 2000 ) ) {
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 8, // Max request packet size
+ 63, // Max response packet size
+ "br|rrrr", // Format string
+ // === REQUEST ================================
+ 0x1c, // b Get Connection Information
+ &ConnectionNum, 4, // r Connection Number
+ // === REPLY ==================================
+ pUserID, 4, // r Object ID
+ &ObjectType, 2, // r Object Type
+ UserName, 48, // r UserName
+ LoginTime, 7 // r Login Time
+ );
+ }
+ else {
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 4, // Max request packet size
+ 63, // Max response packet size
+ "bb|rrrr", // Format string
+ // === REQUEST ================================
+ 0x16, // b Get Connection Information
+ ConnectionNum, // b Connection Number
+ // === REPLY ==================================
+ pUserID, 4, // r Object ID
+ &ObjectType, 2, // r Object Type
+ UserName, 48, // r UserName
+ LoginTime, 7 // r Login Time
+ );
+ }
+
+ return NtStatus;
+}
+
+/********************************************************************
+
+ GetConnectionNumber
+
+Routine Description:
+
+ Given a ConnectionHandle, return the NetWare Connection number
+
+Arguments:
+
+ ConnectionHandle - Connection Handle
+ pConnectionNumber - pointer to returned connection number
+
+Return Value:
+ 0 = success
+ else NT error
+
+ *******************************************************************/
+unsigned int
+GetConnectionNumber(
+ unsigned int ConnectionHandle,
+ unsigned int * pConnectionNumber )
+{
+ NTSTATUS Status;
+ IO_STATUS_BLOCK IoStatusBlock;
+ NWR_GET_CONNECTION_DETAILS Details;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)ConnectionHandle ;
+
+ Status = NtFsControlFile(
+ pServerInfo->hConn, // Connection Handle
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_GET_CONN_DETAILS,
+ NULL,
+ 0,
+ (PVOID) &Details,
+ sizeof(Details));
+
+ if (Status == STATUS_SUCCESS) {
+ Status = IoStatusBlock.Status;
+ }
+
+ if (NT_SUCCESS(Status)) {
+ *pConnectionNumber = 256 * Details.ConnectionNumberHi +
+ Details.ConnectionNumberLo;
+ }
+
+ return Status;
+}
+
+/********************************************************************
+
+ GetInternetAddress
+
+Routine Description:
+
+ Return the address of the current system
+
+Arguments:
+
+ ConnectionHandle - Connection Handle
+ ConnectionNum - Connection Number
+ pAddress - returned address
+
+Return Value:
+ 0 = success
+ else NT error
+
+ *******************************************************************/
+unsigned int
+GetInternetAddress(
+ unsigned int ConnectionHandle,
+ unsigned int ConnectionNum,
+ unsigned char *pAddress
+ )
+{
+ NTSTATUS NtStatus ;
+ VERSION_INFO VerInfo;
+ unsigned int Version;
+ unsigned char Address[12];
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)ConnectionHandle ;
+
+ NtStatus = NWGetFileServerVersionInfo( (NWCONN_HANDLE)ConnectionHandle,
+ &VerInfo );
+
+ if (!NT_SUCCESS(NtStatus))
+ return NtStatus;
+
+ Version = VerInfo.Version * 1000 + VerInfo.SubVersion * 10;
+
+ if ( ( Version >= 3110 ) || ( Version < 2000 ) ) {
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 7, // Max request packet size
+ 14, // Max response packet size
+ "br|r", // Format string
+ // === REQUEST ================================
+ 0x1a, // b Get Connection Information
+ &ConnectionNum, 4, // r Connection Number
+ // === REPLY ==================================
+ Address, 12 // r Login Time
+ );
+ }
+ else {
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E3H, // Bindery function
+ 4, // Max request packet size
+ 14, // Max response packet size
+ "bb|r", // Format string
+ // === REQUEST ================================
+ 0x13, // b Get Connection Information
+ (unsigned char)ConnectionNum, // b Connection Number
+ // === REPLY ==================================
+ Address, 12 // r Login Time
+ );
+ }
+ memcpy( pAddress, Address, 10 );
+
+ return NtStatus;
+}
+
+
+/********************************************************************
+
+ GetBinderyObjectID
+
+Routine Description:
+
+ Get the object ID of a named object in the bindery
+
+Arguments:
+
+ ConnectionHandle - Server connection handle
+ pObjectName - Name of object
+ ObjectType - Object type
+ pObjectId - returned object ID
+
+
+Return Value:
+ 0 = success
+ else NT error
+
+ *******************************************************************/
+unsigned int
+GetBinderyObjectID(
+ unsigned int ConnectionHandle,
+ char *pObjectName,
+ unsigned short ObjectType,
+ unsigned long *pObjectId )
+{
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)ConnectionHandle ;
+ unsigned int reply;
+
+ reply = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ FSCTL_NWR_NCP_E3H, // Directory function
+ 54, // Max request packet size
+ 56, // Max response packet size
+ "brp|r", // Format string
+ // === REQUEST ================================
+ 0x35, // b Get object ID
+ &ObjectType, W_SIZE, // r Object type HI-LO
+ pObjectName, // p UserName
+ // === REPLY ==================================
+ pObjectId, 4 // 4 bytes of raw data
+ );
+ return reply;
+}
+
+/********************************************************************
+
+ GetDefaultPrinterQueue
+
+Routine Description:
+
+ Get the default printer queue.
+
+Arguments:
+ ConnectionHandle - IN
+ Handle to server
+ pServerName - IN
+ File server name
+ pQueueName - OUT
+ Default printer queue name
+
+
+Return Value:
+
+ *******************************************************************/
+unsigned int
+GetDefaultPrinterQueue (
+ unsigned int ConnectionHandle,
+ unsigned char *pServerName,
+ unsigned char *pQueueName
+ )
+{
+ unsigned long ObjectID;
+ NTSTATUS NtStatus ;
+ PNWC_SERVER_INFO pServerInfo = (PNWC_SERVER_INFO)ConnectionHandle ;
+ NWOBJ_TYPE ObjectType;
+ NWCCODE Nwcode;
+
+ NtStatus = NwlibMakeNcp(
+ pServerInfo->hConn, // Connection Handle
+ NWR_ANY_F2_NCP(0x11), // F2 Function function
+ 4, // Max request packet size
+ 4, // Max response packet size
+ "wbb|d", // Format string
+ // === REQUEST ================================
+ 0x2, // w Length
+ 0xA, // b Subfunction
+ 0, // b printer number
+ // === REPLY ==================================
+ &ObjectID // d Object ID of Queue
+ );
+
+ if ( !NT_SUCCESS( NtStatus ) )
+ return ( NtStatus & 0xFF );
+
+ Nwcode = NWGetObjectName( (NWCONN_HANDLE) ConnectionHandle,
+ ObjectID,
+ pQueueName,
+ &ObjectType );
+
+ return Nwcode;
+}
diff --git a/private/nw/nwscript/nds.c b/private/nw/nwscript/nds.c
new file mode 100644
index 000000000..1b73643f5
--- /dev/null
+++ b/private/nw/nwscript/nds.c
@@ -0,0 +1,1505 @@
+/*************************************************************************
+*
+* NDS.C
+*
+* NT NetWare NDS routines
+*
+* Copyright (c) 1995 Microsoft Corporation
+*
+*************************************************************************/
+#include <common.h>
+
+DWORD GUserObjectID;
+HANDLE GhRdr;
+
+
+WCHAR * NDSTREE_w = NULL;
+UNICODE_STRING NDSTREE_u;
+
+/********************************************************************
+
+ ExpandRelativeName
+
+Routine Description:
+
+ If the name is a relative NDS name append the proper context
+ to the end. A relative name has periods on the end. Each
+ period represents one level up the NDS tree.
+
+Arguments:
+
+Return Value:
+
+ *******************************************************************/
+void
+ExpandRelativeName( LPSTR RelativeName, LPSTR AbsoluteName, unsigned int Len,
+ LPSTR Context )
+{
+
+ PBYTE ptr;
+ unsigned int i;
+ unsigned int count = 0;
+
+ strncpy( AbsoluteName, RelativeName, Len );
+
+ if ( ( AbsoluteName[0] == '.' ) &&
+ ( AbsoluteName[ strlen( AbsoluteName ) - 1 ] != '.' ) )
+ return;
+
+ if ( ( strlen( AbsoluteName ) + strlen( Context ) ) > Len )
+ {
+ DisplayMessage( IDR_NOT_ENOUGH_MEMORY );
+ return;
+ }
+
+ if ( AbsoluteName[0] == '\0' )
+ {
+ return;
+ }
+
+ ptr = &AbsoluteName[ strlen( AbsoluteName ) - 1 ];
+
+ // Count the number of periods and back up over them.
+
+ if ( *ptr != '.' )
+ {
+ //
+ // No periods at the end
+ // Assume this is a relative name and append the context
+ //
+ strcat( AbsoluteName, "." );
+ strcat( AbsoluteName + strlen( AbsoluteName ), Context );
+ return;
+ }
+
+ while ( *ptr == '.' )
+ {
+ ptr--;
+ count++;
+ }
+
+ ptr++;
+ *ptr = '\0';
+
+ // ptr now points to where the copy of the rest of the context should start
+ // skip the first "count" entries in the context
+
+ ptr = Context;
+
+ for ( i = 0; i < count; i++ )
+ {
+ ptr = strchr( ptr, '.' );
+ if ( ptr == NULL )
+ {
+ return;
+ }
+ ptr++;
+ }
+ ptr--;
+
+ // Now append
+
+ strcat( AbsoluteName, ptr );
+
+}
+
+
+
+/********************************************************************
+
+ NDSGetNameContext
+
+Routine Description:
+
+ Get the current context
+
+Arguments:
+ none
+
+Return Value:
+ none
+
+ *******************************************************************/
+NTSTATUS
+NDSGetNameContext( LPSTR Context, BOOLEAN flag )
+{
+ //
+ // For NdsResolveName.
+ //
+
+ UNICODE_STRING ReferredServer;
+ WCHAR ServerStr[MAX_NAME_LEN];
+ HANDLE hReferredServer;
+ DWORD dwHandleType;
+
+ NTSTATUS Status;
+
+ OEM_STRING oemStr;
+ UNICODE_STRING defaultcontext;
+ DWORD ThisObjectID;
+ BYTE Buffer[2048];
+ WCHAR NdsStr[1024];
+ PBYTE ptr;
+
+ defaultcontext.Length = 0;
+ defaultcontext.MaximumLength = sizeof( NdsStr );
+ defaultcontext.Buffer = NdsStr;
+
+ Status = NwNdsGetTreeContext( GhRdr, &NDSTREE_u, &defaultcontext );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ ReferredServer.Buffer = ServerStr;
+ ReferredServer.Length = 0;
+ ReferredServer.MaximumLength = sizeof( ServerStr );
+
+ Status = NwNdsResolveName ( GhRdr,
+ &defaultcontext,
+ &ThisObjectID,
+ &ReferredServer,
+ NULL,
+ 0 );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ if ( ReferredServer.Length > 0 ) {
+
+ //
+ // We've been referred to another server, so we
+ // should change the global handle.
+ //
+
+ Status = NwNdsOpenGenericHandle( &ReferredServer,
+ &dwHandleType,
+ &hReferredServer );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ DisplayMessage(IDR_NDS_USERNAME_FAILED);
+ return Status;
+ }
+
+ CloseHandle( GhRdr );
+ GhRdr = hReferredServer;
+ }
+
+ Status = NwNdsReadObjectInfo( GhRdr, ThisObjectID, Buffer, 2048 );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ ptr = Buffer + sizeof( NDS_RESPONSE_GET_OBJECT_INFO );
+ ptr += ROUNDUP4(*(DWORD *)ptr);
+ ptr += sizeof(DWORD);
+ ptr += sizeof(DWORD);
+
+ defaultcontext.Length = wcslen( (WCHAR *)ptr ) * 2;
+ defaultcontext.MaximumLength = defaultcontext.Length;
+ defaultcontext.Buffer = (WCHAR *)ptr;
+
+ oemStr.Length = 0;
+ oemStr.MaximumLength = NDS_NAME_CHARS;
+ oemStr.Buffer = Context;
+
+ RtlUnicodeStringToOemString( &oemStr, &defaultcontext, FALSE );
+
+ return 0;
+}
+
+/********************************************************************
+
+ NDSTypeless
+
+Routine Description:
+
+ Change name to typelese
+
+Arguments:
+ none
+
+Return Value:
+ none
+
+ *******************************************************************/
+unsigned int
+NDSTypeless( LPSTR OrigName , LPSTR TypelessName )
+{
+ int i,j;
+ PBYTE p;
+
+ i = 0;
+ j = 0;
+
+ if ( !_strnicmp( "CN=", OrigName, 3 ) ||
+ !_strnicmp( "OU=", OrigName, 3 ) )
+ {
+ i += 3;
+ }
+ else if ( !_strnicmp( "C=", OrigName, 2 ) ||
+ !_strnicmp( "O=", OrigName, 2 ) )
+ {
+ i += 2;
+ }
+
+ for ( ; (( i < NDS_NAME_CHARS ) && ( OrigName[i] ) ); i++ )
+ {
+ if ( !_strnicmp( ".CN=", &OrigName[i], 4 ) ||
+ !_strnicmp( ".OU=", &OrigName[i], 4 ) )
+ {
+ TypelessName[j++]= '.';
+ i += 3;
+ continue;
+ }
+ if ( !_strnicmp( ".C=", &OrigName[i], 3 ) ||
+ !_strnicmp( ".O=", &OrigName[i], 3 ) )
+ {
+ TypelessName[j++]= '.';
+ i += 2;
+ continue;
+ }
+ /*
+ * Strip out multiple blanks
+ */
+ if ( !_strnicmp( " ", &OrigName[i], 2 ) )
+ {
+ continue;
+ }
+ TypelessName[j++] = OrigName[i];
+ }
+
+ TypelessName[j] = '\0';
+
+ return 0;
+}
+
+/********************************************************************
+
+ NDSAbbreviateName
+
+Routine Description:
+
+ Abbreviate name
+
+Arguments:
+ none
+
+Return Value:
+ none
+
+ *******************************************************************/
+unsigned int
+NDSAbbreviateName( DWORD Flags, LPSTR OrigName , LPSTR AbbrevName )
+{
+ BYTE Buffer[NDS_NAME_CHARS];
+ BYTE CurrentContext[NDS_NAME_CHARS];
+ PBYTE p;
+ PBYTE c;
+ NTSTATUS Status;
+
+ if ( OrigName[0] == '.' )
+ NDSTypeless( OrigName + 1, Buffer );
+ else
+ NDSTypeless( OrigName, Buffer );
+
+ /*
+ * We want a relative name
+ */
+ if ( Flags & FLAGS_LOCAL_CONTEXT )
+ {
+ p = &Buffer[strlen(Buffer)-strlen(REQUESTER_CONTEXT)];
+ if ( !_strcmpi( REQUESTER_CONTEXT, p ) )
+ {
+ // The name is below us
+
+ if ( ( *(p-1) == '.' ) && ( p > Buffer ) )
+ p--;
+ *p = '\0';
+ strcpy( AbbrevName, Buffer );
+ }
+ else
+ {
+ //
+ // Going from back to front for each section of context
+ // in common with AbbrevName
+ // truncate both
+ // Going from back to front for each section of context
+ // left over
+ // concatonate a period to AbbrevName
+ //
+ // Example
+ //
+ // Name: w.x.y.z Context: a.b.z => w.x.y..
+ //
+
+ strcpy( CurrentContext, REQUESTER_CONTEXT );
+ strcpy( AbbrevName, Buffer );
+
+ if ( CurrentContext[0] && AbbrevName[0] )
+ {
+ c = &CurrentContext[ strlen( CurrentContext ) ] - 1;
+ p = &AbbrevName[ strlen( AbbrevName ) ] - 1;
+
+ //
+ // Strip off the matching names from end to front
+ //
+ for ( ;; )
+ {
+ if ( ( c == CurrentContext ) && ( *p == '.' ) )
+ {
+ *c = '\0';
+ *p = '\0';
+ break;
+ }
+
+ if ( *c != *p )
+ break;
+
+ if ( ( *c == '.' ) && ( *p == '.' ) )
+ {
+ *c = '\0';
+ *p = '\0';
+ }
+
+ if ( ( c == CurrentContext ) || ( p == AbbrevName ) )
+ {
+ break;
+ }
+
+ c--; p--;
+ }
+
+ //
+ // Count the remaining sections of the context and
+ // add that number of periods to the end of the buffer.
+ // That is how far we need to back up before getting
+ // to a matching branch of the tree.
+ //
+
+ if ( CurrentContext[0] ) {
+ strcat( AbbrevName, "." );
+ for ( c = CurrentContext; *c; c++ ) {
+ if ( *c == '.' )
+ strcat( AbbrevName, "." );
+ }
+ }
+ }
+
+ }
+ }
+ else
+ strcpy( AbbrevName, Buffer );
+
+ return 0;
+}
+
+
+/********************************************************************
+
+ NDSInitUserProperty
+
+Routine Description:
+
+ none
+
+Arguments:
+ none
+
+Return Value:
+ 0 = no error
+
+ *******************************************************************/
+unsigned int
+NDSInitUserProperty( )
+{
+ NTSTATUS Status;
+ UNICODE_STRING ObjectName;
+ PWCHAR lpT;
+ UNICODE_STRING defaultcontext;
+
+ //
+ // For NdsResolveName.
+ //
+
+ UNICODE_STRING ReferredServer;
+ WCHAR ServerStr[MAX_NAME_LEN];
+ HANDLE hReferredServer;
+ DWORD dwHandleType;
+
+ //
+ // Get a handle to the redirector.
+ //
+
+ Status = NwNdsOpenTreeHandle( &NDSTREE_u, &GhRdr );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ DisplayMessage(IDR_TREE_OPEN_FAILED);
+ return 1;
+ }
+
+ //
+ // Resolve the name that we have to an object id.
+ //
+
+ RtlInitUnicodeString( &ObjectName, TYPED_USER_NAME_w );
+
+ ReferredServer.Buffer = ServerStr;
+ ReferredServer.Length = 0;
+ ReferredServer.MaximumLength = sizeof( ServerStr );
+
+ Status = NwNdsResolveName ( GhRdr,
+ &ObjectName,
+ &GUserObjectID,
+ &ReferredServer,
+ NULL,
+ 0 );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ DisplayMessage(IDR_NDS_USERNAME_FAILED);
+ return 1;
+ }
+
+ if ( ReferredServer.Length > 0 ) {
+
+ //
+ // We've been referred to another server, so we
+ // should change the global handle.
+ //
+
+ Status = NwNdsOpenGenericHandle( &ReferredServer,
+ &dwHandleType,
+ &hReferredServer );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ DisplayMessage(IDR_NDS_USERNAME_FAILED);
+ return 1;
+ }
+
+ CloseHandle( GhRdr );
+ GhRdr = hReferredServer;
+ }
+
+ //
+ // Set the current context to what we think it should be
+ // (At the user's location.)
+ //
+
+ lpT = wcschr( TYPED_USER_NAME_w, L'.' );
+ if ( lpT )
+ {
+ RtlInitUnicodeString( &defaultcontext, lpT+1 );
+ }
+ else
+ {
+ RtlInitUnicodeString( &defaultcontext, L"" );
+ }
+
+ Status = NwNdsSetTreeContext( GhRdr, &NDSTREE_u, &defaultcontext );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ DisplayMessage(IDR_NDS_CONTEXT_INVALID);
+ return 1;
+ }
+
+ return 0;
+
+
+}
+
+/********************************************************************
+
+ NDSCanonicalizeName
+
+Routine Description:
+
+ return a canonicalized version of a name
+
+Arguments:
+ Name - original name
+ CanonName - Canonicalized name
+ Len - length of CanonName
+ fCurrentContext - TRUE => use current contex, FALSE use
+ requester context
+
+Return Value:
+ status error
+
+ *******************************************************************/
+unsigned int
+NDSCanonicalizeName( PBYTE Name, PBYTE CanonName, int Len, int fCurrentContext )
+{
+ NTSTATUS Status;
+ int ccode = -1;
+ DWORD ThisObjectID;
+ OEM_STRING oemStr;
+ UNICODE_STRING ObjectName;
+ BYTE Buffer[2048];
+ BYTE FullName[NDS_NAME_CHARS];
+ PBYTE ptr;
+ UNICODE_STRING ReferredServer;
+ WCHAR ServerStr[MAX_NAME_LEN];
+ DWORD dwHandleType;
+ HANDLE hReferredServer;
+ unsigned char CurrentContext[NDS_NAME_CHARS];
+
+ //
+ // Cope with relative names
+ //
+ if ( fCurrentContext )
+ {
+ Status = NDSGetNameContext( CurrentContext, TRUE );
+ if ( !NT_SUCCESS( Status ) )
+ return Status;
+ ExpandRelativeName( Name, FullName, NDS_NAME_CHARS, CurrentContext );
+ }
+ else
+ ExpandRelativeName( Name, FullName, NDS_NAME_CHARS, REQUESTER_CONTEXT );
+
+ //
+ // Fill it in in case we have an error
+ //
+ strncpy( CanonName, FullName, Len);
+
+ //
+ // Resolve the name that we have to an object id.
+ //
+ // Unfortuneately, the name resolver doesn't understand periods at the
+ // front or end (absolute or relative names)
+ //
+
+ if ( FullName[0] == '.' )
+ {
+ oemStr.Length = strlen( FullName + 1 );
+ oemStr.MaximumLength = oemStr.Length;
+ oemStr.Buffer = FullName + 1;
+ }
+ else
+ {
+ oemStr.Length = strlen( FullName );
+ oemStr.MaximumLength = oemStr.Length;
+ oemStr.Buffer = FullName;
+ }
+
+ ObjectName.Length = 0;
+ ObjectName.MaximumLength = sizeof(Buffer);
+ ObjectName.Buffer = (WCHAR *)Buffer;
+
+ RtlOemStringToUnicodeString( &ObjectName, &oemStr, FALSE );
+
+ ReferredServer.Buffer = ServerStr;
+ ReferredServer.Length = 0;
+ ReferredServer.MaximumLength = sizeof( ServerStr );
+
+ Status = NwNdsResolveName ( GhRdr,
+ &ObjectName,
+ &ThisObjectID,
+ &ReferredServer,
+ NULL,
+ 0 );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ if ( ReferredServer.Length > 0 ) {
+
+ //
+ // We've been referred to another server, so we
+ // should change the global handle.
+ //
+
+ Status = NwNdsOpenGenericHandle( &ReferredServer,
+ &dwHandleType,
+ &hReferredServer );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ CloseHandle( GhRdr );
+ GhRdr = hReferredServer;
+ }
+
+ Status = NwNdsReadObjectInfo( GhRdr, ThisObjectID, Buffer, 2048 );
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ ptr = Buffer + sizeof( NDS_RESPONSE_GET_OBJECT_INFO );
+ ptr += ROUNDUP4(*(DWORD *)ptr);
+ ptr += sizeof(DWORD);
+ ptr += sizeof(DWORD);
+
+ RtlInitUnicodeString( &ObjectName, (PWCHAR)ptr );
+
+ oemStr.Length = 0;
+ oemStr.MaximumLength = Len;
+ oemStr.Buffer = CanonName;
+
+ RtlUnicodeStringToOemString( &oemStr, &ObjectName, FALSE );
+
+ return 0;
+}
+
+/********************************************************************
+
+ NDSGetUserProperty
+
+Routine Description:
+
+ Return the NDS property for the object
+
+Arguments:
+ Property - property name
+ Data - data buffer
+ Size - size of data buffer
+
+Return Value:
+ 0 no error
+
+ *******************************************************************/
+unsigned int
+NDSGetUserProperty( PBYTE Property,
+ PBYTE Data,
+ unsigned int Size,
+ SYNTAX * pSyntaxID,
+ unsigned int * pActualSize )
+{
+ NTSTATUS Status;
+ int ccode = -1;
+
+ OEM_STRING oemStr;
+ UNICODE_STRING PropertyName;
+ WCHAR NdsStr[1024];
+ DWORD iterhandle = INITIAL_ITERATION;
+
+ BYTE Buffer[2048];
+ DWORD BufferSize = 2048;
+ PNDS_RESPONSE_READ_ATTRIBUTE pReadAttribute;
+ PNDS_ATTRIBUTE pAttribute;
+ PBYTE pAttribValue;
+
+ //
+ // Read the User property
+ //
+
+ memset(Buffer, 0, BufferSize);
+
+ oemStr.Length = strlen( Property );
+ oemStr.MaximumLength = oemStr.Length;
+ oemStr.Buffer = Property;
+
+ PropertyName.Length = 0;
+ PropertyName.MaximumLength = sizeof(NdsStr);
+ PropertyName.Buffer = NdsStr;
+
+ RtlOemStringToUnicodeString( &PropertyName, &oemStr, FALSE );
+
+ Status = NwNdsReadAttribute ( GhRdr,
+ GUserObjectID,
+ &iterhandle,
+ &PropertyName,
+ Buffer,
+ BufferSize );
+
+ if ( NT_SUCCESS(Status) )
+ {
+ int i;
+ pReadAttribute = (PNDS_RESPONSE_READ_ATTRIBUTE)Buffer;
+ pAttribute = (PNDS_ATTRIBUTE)(Buffer
+ + sizeof(NDS_RESPONSE_READ_ATTRIBUTE));
+ if ( pSyntaxID )
+ {
+ *pSyntaxID = pAttribute->SyntaxID;
+ }
+
+ pAttribValue = (PBYTE)(pAttribute->AttribName) +
+ ROUNDUP4(pAttribute->AttribNameLength) +
+ sizeof(DWORD);
+
+ if ( pActualSize )
+ {
+ *pActualSize = *(DWORD *)pAttribValue;
+ }
+
+ memcpy( Data, pAttribValue + sizeof(DWORD),
+ min(*(DWORD *)pAttribValue, Size) );
+
+ }
+
+ return Status;
+}
+
+
+/********************************************************************
+
+ NDSGetVar
+
+Routine Description:
+
+ Return value of user property
+
+ Get the syntax type of the property
+ Retrieve the data
+ Do any data conversion
+
+Arguments:
+ Name - of NDS property IN
+ Value - value buffer OUT
+ Size - size of value buffer IN
+
+Return Value:
+ none
+
+ *******************************************************************/
+void
+NDSGetVar ( PBYTE Name, PBYTE Value, unsigned int Size)
+{
+ unsigned int err;
+ SYNTAX Syntax;
+ BYTE Buffer[ATTRBUFSIZE];
+ DWORD ActualSize;
+
+ Value[0] = 0;
+
+ err = NDSGetUserProperty( Name, Buffer, ATTRBUFSIZE, &Syntax, &ActualSize );
+
+ if ( err )
+ {
+ return;
+ }
+
+ switch ( Syntax )
+ {
+ case NDSI_BOOLEAN:
+ if ( *(PBYTE)Buffer )
+ {
+ strcpy( Value, "Y" );
+ }
+ else
+ {
+ strcpy( Value, "N" );
+ }
+ break;
+ case NDSI_DIST_NAME:
+ case NDSI_CE_STRING:
+ case NDSI_CI_STRING:
+ case NDSI_OCTET_STRING:
+ case NDSI_PR_STRING:
+ case NDSI_NU_STRING:
+ case NDSI_TEL_NUMBER:
+ case NDSI_CLASS_NAME:
+ ConvertUnicodeToAscii( Buffer );
+ if ( Syntax == NDSI_DIST_NAME )
+ NDSAbbreviateName(FLAGS_LOCAL_CONTEXT, Buffer, Buffer);
+ strncpy( Value, Buffer, Size );
+ break;
+ case NDSI_CI_LIST:
+ ConvertUnicodeToAscii( Buffer+8 );
+ strncpy( Value, Buffer+8, Size );
+ break;
+ break;
+ case NDSI_INTEGER:
+ case NDSI_COUNTER:
+ case NDSI_TIME:
+ case NDSI_INTERVAL:
+ case NDSI_TIMESTAMP:
+ sprintf( Value, "%d", *(int *)Buffer );
+ break;
+ case NDSI_PO_ADDRESS:
+ {
+ // 6 null terminated lines
+ int line,len;
+ PBYTE ptr = Buffer + 4;
+
+ // Stop if not 6 lines
+ if ( *(int *)Buffer != 6 )
+ break;
+
+ for (line = 0; line <= 5; line++) {
+ len = ROUNDUP4(*(int *)ptr);
+ ptr += 4;
+ if ( !len )
+ break;
+ ConvertUnicodeToAscii( ptr );
+ strcat( Value, ptr );
+ strcat( Value, "\n" );
+ ptr += len;
+ }
+ }
+ break;
+ case NDSI_FAX_NUMBER:
+ if ( *(int *)Buffer == 0 )
+ return;
+ ConvertUnicodeToAscii( Buffer+4 );
+ strncpy( Value, Buffer+4, Size );
+ break;
+ case NDSI_EMAIL_ADDRESS:
+ if ( *(int *)(Buffer+4) == 0 )
+ return;
+ ConvertUnicodeToAscii( Buffer+8 );
+ strncpy( Value, Buffer+8, Size );
+ break;
+ case NDSI_PATH:
+ {
+ int len;
+
+ len = *(int *)(Buffer+4);
+ if ( len == 0 )
+ break;
+ len = ROUNDUP4( len );
+ ConvertUnicodeToAscii( Buffer+8 );
+ strcpy( Value, Buffer+8 );
+ NDSAbbreviateName(FLAGS_LOCAL_CONTEXT, Value, Value);
+ strcat( Value, ":" );
+ if ( *(int *)(Buffer + 8 + len) == 0 )
+ break;
+ ConvertUnicodeToAscii( Buffer+8+len+4 );
+ strcat( Value, Buffer+8+len+4 );
+ break;
+ }
+ case NDSI_NET_ADDRESS:
+ case NDSI_OCTET_LIST:
+ case NDSI_OBJECT_ACL:
+ case NDSI_STREAM:
+ case NDSI_UNKNOWN:
+ case NDSI_REPLICA_POINTER:
+ case NDSI_BACK_LINK:
+ case NDSI_TYPED_NAME:
+ case NDSI_HOLD:
+ case NDSI_TAX_COUNT:
+ default:
+ Value[0] = '\0';
+ Value[1] = '\0';
+ break;
+ }
+
+}
+
+/********************************************************************
+
+ NDSChangeContext
+
+Routine Description:
+
+ Change the current context
+
+Arguments:
+ Context - context string IN
+
+Return Value:
+ error number
+
+ *******************************************************************/
+unsigned int
+NDSChangeContext( PBYTE Context )
+{
+ NTSTATUS Status;
+
+ OEM_STRING oemStr;
+ UNICODE_STRING defaultcontext;
+ WCHAR NdsStr[1024];
+
+ oemStr.Length = strlen( Context );
+ oemStr.MaximumLength = oemStr.Length;
+ oemStr.Buffer = Context;
+
+ defaultcontext.Length = 0;
+ defaultcontext.MaximumLength = sizeof(NdsStr);
+ defaultcontext.Buffer = NdsStr;
+
+ RtlOemStringToUnicodeString( &defaultcontext, &oemStr, FALSE );
+
+ Status = NwNdsSetTreeContext( GhRdr, &NDSTREE_u, &defaultcontext );
+
+ return Status;
+}
+
+/********************************************************************
+
+ NDSGetContext
+
+Routine Description:
+
+ Retrieve the current context
+
+Arguments:
+ Buffer - data buffer for context string OUT
+ len - length of data buffer IN
+
+Return Value:
+ error number
+
+ *******************************************************************/
+unsigned int
+NDSGetContext( PBYTE Buffer,
+ unsigned int len )
+{
+ NTSTATUS Status;
+
+ Status = NDSGetNameContext( Buffer, TRUE );
+ if ( !NT_SUCCESS( Status ) )
+ return Status;
+ NDSAbbreviateName(FLAGS_NO_CONTEXT, Buffer, Buffer);
+ return 0;
+}
+
+/********************************************************************
+
+ NDSfopenStream
+
+Routine Description:
+
+ Open a file handle to an NDS stream property
+
+Arguments:
+ Object - name of object IN
+ Property - name of property IN
+ pStream - pointer to file handle OUT
+ pFileSize - pointer to file size OUT
+
+Return Value:
+ error
+
+ *******************************************************************/
+unsigned int
+NDSfopenStream ( PBYTE Object,
+ PBYTE Property,
+ PHANDLE pStream,
+ unsigned int * pFileSize )
+{
+ //
+ // Status variables.
+ //
+
+ NTSTATUS Status;
+ int ccode = -1;
+
+ //
+ // For NwNdsOpenTreeHandle.
+ //
+
+ HANDLE hRdr;
+ OEM_STRING oemStr;
+ UNICODE_STRING ObjectName;
+ WCHAR NdsStr[1024];
+
+ //
+ // For NwNdsResolveName.
+ //
+
+ DWORD dwOid;
+ UNICODE_STRING ReferredServer;
+ WCHAR ServerStr[MAX_NAME_LEN];
+ DWORD dwHandleType;
+ HANDLE hReferredServer;
+
+ //
+ // Get a handle to the redirector.
+ //
+
+ Status = NwNdsOpenTreeHandle( &NDSTREE_u, &hRdr );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ DisplayMessage(IDR_TREE_OPEN_FAILED);
+ return ccode;
+ }
+
+ //
+ // Resolve the name that we have to an object id.
+ //
+
+ if ( !Object )
+ {
+ return 1;
+ }
+
+ oemStr.Length = strlen( Object );
+ oemStr.MaximumLength = oemStr.Length;
+ oemStr.Buffer = Object;
+
+ ObjectName.Length = 0;
+ ObjectName.MaximumLength = sizeof(NdsStr);
+ ObjectName.Buffer = NdsStr;
+
+ RtlOemStringToUnicodeString( &ObjectName, &oemStr, FALSE );
+
+ ReferredServer.Buffer = ServerStr;
+ ReferredServer.Length = 0;
+ ReferredServer.MaximumLength = sizeof( ServerStr );
+
+ Status = NwNdsResolveName ( hRdr,
+ &ObjectName,
+ &dwOid,
+ &ReferredServer,
+ NULL,
+ 0 );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return 0xffffffff;
+ }
+
+ if ( ReferredServer.Length > 0 ) {
+
+ //
+ // We've been referred to another server, so we
+ // must jump to that server before continuing.
+ //
+
+ Status = NwNdsOpenGenericHandle( &ReferredServer,
+ &dwHandleType,
+ &hReferredServer );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return 0xffffffff;
+ }
+
+ CloseHandle( hRdr );
+ hRdr = hReferredServer;
+ }
+
+ //
+ // Open the file stream.
+ //
+
+ oemStr.Length = strlen( Property );
+ oemStr.MaximumLength = oemStr.Length;
+ oemStr.Buffer = Property;
+
+ ObjectName.Length = 0;
+ ObjectName.MaximumLength = sizeof(NdsStr);
+ ObjectName.Buffer = NdsStr;
+
+ RtlOemStringToUnicodeString( &ObjectName, &oemStr, FALSE );
+
+ //
+ // Try to open a file stream for read access.
+ //
+
+ Status = NwNdsOpenStream( hRdr,
+ dwOid,
+ &ObjectName,
+ 1, // Read access.
+ pFileSize );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return 0xffffffff;
+ }
+
+ *pStream = hRdr;
+
+ return 0;
+}
+
+/*
+ * IsMemberOfNDSGroup
+ * ------------------
+ *
+ * Returns true if currently logged in user object is member of group with given name
+ *
+ */
+unsigned int
+IsMemberOfNDSGroup(
+ PBYTE nwGroup
+ )
+{
+ NTSTATUS Status;
+ UINT nwRet;
+ BYTE szCanonTargetGroupName[NDS_NAME_CHARS+1];
+ UINT syntaxid;
+ UINT actualsize;
+ LPSTR szBuffer;
+ LPSTR pProp;
+ UINT i;
+ DWORD iterhandle = INITIAL_ITERATION;
+ UINT fFoundGroup = FALSE;
+ PNDS_RESPONSE_READ_ATTRIBUTE pReadAttribute;
+ PNDS_ATTRIBUTE pAttribute;
+ PBYTE pAttribValue;
+ UNICODE_STRING PropertyName;
+ UINT numvalues = 0;
+
+
+ szBuffer = (BYTE *)malloc(ATTRBUFSIZE);
+
+ if ( !szBuffer ) {
+ DisplayMessage(IDR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+ memset( szBuffer, 0, ATTRBUFSIZE );
+
+ // Canonicalize name according to current context
+
+ strcpy( szCanonTargetGroupName, nwGroup );
+
+ nwRet = NDSCanonicalizeName( szCanonTargetGroupName,
+ szCanonTargetGroupName,
+ NDS_NAME_CHARS,
+ TRUE );
+ if (nwRet) {
+
+ if ( nwGroup[0] != '.' ) {
+
+ // Try an absolute name
+
+ strcpy( szCanonTargetGroupName, "." );
+ strcat( szCanonTargetGroupName, nwGroup );
+
+ nwRet = NDSCanonicalizeName( szCanonTargetGroupName,
+ szCanonTargetGroupName,
+ NDS_NAME_CHARS,
+ TRUE );
+ }
+
+ if ( nwRet )
+ goto CleanRet;
+ }
+
+ // Should check class name of object
+
+ RtlInitUnicodeString( &PropertyName, L"Group Membership" );
+
+ Status = NwNdsReadAttribute ( GhRdr,
+ GUserObjectID,
+ &iterhandle,
+ &PropertyName,
+ szBuffer,
+ ATTRBUFSIZE );
+
+ if ( !NT_SUCCESS(Status) )
+ {
+ return FALSE;
+ }
+
+ pReadAttribute = (PNDS_RESPONSE_READ_ATTRIBUTE)szBuffer;
+
+ pAttribute = (PNDS_ATTRIBUTE)(szBuffer
+ + sizeof(NDS_RESPONSE_READ_ATTRIBUTE));
+ pAttribute->SyntaxID;
+
+ pAttribValue = (PBYTE)(pAttribute->AttribName) +
+ ROUNDUP4(pAttribute->AttribNameLength) +
+ sizeof(DWORD);
+
+ numvalues = *(PUINT)((PBYTE)(pAttribute->AttribName) +
+ ROUNDUP4(pAttribute->AttribNameLength));
+
+ if ( *(DWORD *)pAttribValue == 0 )
+ {
+ return FALSE;
+ }
+
+ for ( i = 0; i < numvalues; i++ ) {
+ ConvertUnicodeToAscii( pAttribValue+sizeof(DWORD) );
+ if (!_stricmp(pAttribValue+sizeof(DWORD),szCanonTargetGroupName)) {
+ fFoundGroup = TRUE;
+ break;
+ }
+ pAttribValue += ROUNDUP4(*(PUINT)pAttribValue) + sizeof(DWORD);
+ }
+
+
+
+CleanRet:
+ if (szBuffer ) {
+ free (szBuffer);
+ }
+ return fFoundGroup;
+}
+
+/********************************************************************
+
+ NDSGetProperty
+
+Routine Description:
+
+ Return the NDS property for the object
+
+Arguments:
+ Object - name of object IN
+ Property - property name IN
+ Data - data buffer OUT
+ Size - size of data buffer IN
+ pActualSize - real data size OUT
+
+Return Value:
+ error
+
+ *******************************************************************/
+unsigned int
+NDSGetProperty ( PBYTE Object,
+ PBYTE Property,
+ PBYTE Data,
+ unsigned int Size,
+ unsigned int * pActualSize )
+{
+ //
+ // Status variables.
+ //
+
+ NTSTATUS Status;
+ int ccode = -1;
+
+ //
+ // For NwNdsOpenTreeHandle.
+ //
+
+ HANDLE hRdr;
+ OEM_STRING oemStr;
+ UNICODE_STRING ObjectName;
+ WCHAR NdsStr[1024];
+
+ //
+ // For NwNdsResolveName.
+ //
+
+ DWORD dwOid;
+ UNICODE_STRING ReferredServer;
+ WCHAR ServerStr[MAX_NAME_LEN];
+ DWORD dwHandleType;
+ HANDLE hReferredServer;
+
+ //
+ // For NwNdsReadAttribute
+ //
+ BYTE Buffer[2048];
+ DWORD BufferSize = 2048;
+ DWORD iterhandle = INITIAL_ITERATION;
+ PNDS_RESPONSE_READ_ATTRIBUTE pReadAttribute;
+ PNDS_ATTRIBUTE pAttribute;
+ PBYTE pAttribValue;
+
+ //
+ // Get a handle to the redirector.
+ //
+
+ Status = NwNdsOpenTreeHandle( &NDSTREE_u, &hRdr );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ DisplayMessage(IDR_TREE_OPEN_FAILED);
+ return ccode;
+ }
+
+ //
+ // Resolve the name that we have to an object id.
+ //
+
+ if ( !Object )
+ {
+ return 1;
+ }
+
+ oemStr.Length = strlen( Object );
+ oemStr.MaximumLength = oemStr.Length;
+ oemStr.Buffer = Object;
+
+ ObjectName.Length = 0;
+ ObjectName.MaximumLength = sizeof(NdsStr);
+ ObjectName.Buffer = NdsStr;
+
+ RtlOemStringToUnicodeString( &ObjectName, &oemStr, FALSE );
+
+ ReferredServer.Buffer = ServerStr;
+ ReferredServer.Length = 0;
+ ReferredServer.MaximumLength = sizeof( ServerStr );
+
+ Status = NwNdsResolveName ( hRdr,
+ &ObjectName,
+ &dwOid,
+ &ReferredServer,
+ NULL,
+ 0 );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return 0xffffffff;
+ }
+
+ if ( ReferredServer.Length > 0 ) {
+
+ //
+ // We've been referred to another server, so we
+ // must jump to that server before continuing.
+ //
+
+ Status = NwNdsOpenGenericHandle( &ReferredServer,
+ &dwHandleType,
+ &hReferredServer );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return 0xffffffff;
+ }
+
+ CloseHandle( hRdr );
+ hRdr = hReferredServer;
+ }
+
+ //
+ // Get the attribute
+ //
+
+ oemStr.Length = strlen( Property );
+ oemStr.MaximumLength = oemStr.Length;
+ oemStr.Buffer = Property;
+
+ ObjectName.Length = 0;
+ ObjectName.MaximumLength = sizeof(NdsStr);
+ ObjectName.Buffer = NdsStr;
+
+ RtlOemStringToUnicodeString( &ObjectName, &oemStr, FALSE );
+
+ Status = NwNdsReadAttribute ( hRdr,
+ dwOid,
+ &iterhandle,
+ &ObjectName,
+ Buffer,
+ BufferSize );
+
+ if ( NT_SUCCESS(Status) )
+ {
+ int i;
+ pReadAttribute = (PNDS_RESPONSE_READ_ATTRIBUTE)Buffer;
+ pAttribute = (PNDS_ATTRIBUTE)(Buffer
+ + sizeof(NDS_RESPONSE_READ_ATTRIBUTE));
+
+ pAttribValue = (PBYTE)(pAttribute->AttribName) +
+ ROUNDUP4(pAttribute->AttribNameLength) +
+ sizeof(DWORD);
+
+ if ( pActualSize )
+ {
+ *pActualSize = *(DWORD *)pAttribValue;
+ }
+
+ memcpy( Data, pAttribValue + sizeof(DWORD),
+ min(*(DWORD *)pAttribValue, Size) );
+
+ }
+
+ NtClose( hRdr );
+
+ return Status;
+}
+
+
+/********************************************************************
+
+ NDSCleanup
+
+Routine Description:
+
+ Does any NDS cleanup
+
+Arguments:
+ none
+
+Return Value:
+ none
+
+ *******************************************************************/
+void
+NDSCleanup ( void )
+{
+ NtClose( GhRdr );
+}
+
+/********************************************************************
+
+ NDSGetClassName
+
+Routine Description:
+
+ return a class name for an object
+
+Arguments:
+ szObjectName
+ ClassName
+
+Return Value:
+ none
+
+ *******************************************************************/
+unsigned int
+NDSGetClassName( LPSTR szObjectName, LPSTR ClassName )
+{
+ NTSTATUS Status;
+ int ccode = -1;
+ DWORD ThisObjectID;
+ OEM_STRING oemStr;
+ UNICODE_STRING ObjectName;
+ BYTE Buffer[2048];
+ BYTE FullName[NDS_NAME_CHARS];
+ PBYTE ptr;
+ UNICODE_STRING ReferredServer;
+ WCHAR ServerStr[MAX_NAME_LEN];
+ DWORD dwHandleType;
+ HANDLE hReferredServer;
+ DWORD Length;
+
+ //
+ // Resolve the name that we have to an object id.
+ //
+
+ oemStr.Length = strlen( szObjectName );
+ oemStr.MaximumLength = oemStr.Length;
+ oemStr.Buffer = szObjectName;
+
+ ObjectName.Length = 0;
+ ObjectName.MaximumLength = sizeof(Buffer);
+ ObjectName.Buffer = (WCHAR *)Buffer;
+
+ RtlOemStringToUnicodeString( &ObjectName, &oemStr, FALSE );
+
+ ReferredServer.Buffer = ServerStr;
+ ReferredServer.Length = 0;
+ ReferredServer.MaximumLength = sizeof( ServerStr );
+
+ Status = NwNdsResolveName ( GhRdr,
+ &ObjectName,
+ &ThisObjectID,
+ &ReferredServer,
+ NULL,
+ 0 );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ if ( ReferredServer.Length > 0 ) {
+
+ //
+ // We've been referred to another server, so we
+ // should change the global handle.
+ //
+
+ Status = NwNdsOpenGenericHandle( &ReferredServer,
+ &dwHandleType,
+ &hReferredServer );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ CloseHandle( GhRdr );
+ GhRdr = hReferredServer;
+ }
+
+ Status = NwNdsReadObjectInfo( GhRdr, ThisObjectID, Buffer, 2048 );
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ ptr = Buffer + sizeof( NDS_RESPONSE_GET_OBJECT_INFO ) + sizeof( DWORD );
+
+ RtlInitUnicodeString( &ObjectName, (PWCHAR)ptr );
+
+ oemStr.Length = 0;
+ oemStr.MaximumLength = NDS_NAME_CHARS;
+ oemStr.Buffer = ClassName;
+
+ RtlUnicodeStringToOemString( &oemStr, &ObjectName, FALSE );
+
+ return 0;
+}
diff --git a/private/nw/nwscript/nt.c b/private/nw/nwscript/nt.c
new file mode 100644
index 000000000..ebf7d4bb1
--- /dev/null
+++ b/private/nw/nwscript/nt.c
@@ -0,0 +1,617 @@
+/*************************************************************************
+*
+* NT.C
+*
+* NT NetWare routines
+*
+* Copyright (c) 1995 Microsoft Corporation
+*
+* $Log: N:\NT\PRIVATE\NW4\NWSCRIPT\VCS\NT.C $
+*
+* Rev 1.4 22 Dec 1995 14:25:12 terryt
+* Add Microsoft headers
+*
+* Rev 1.3 28 Nov 1995 17:13:28 terryt
+* Cleanup resource file
+*
+* Rev 1.2 22 Nov 1995 15:43:44 terryt
+* Use proper NetWare user name call
+*
+* Rev 1.1 20 Nov 1995 16:10:00 terryt
+* Close open NDS handles
+*
+* Rev 1.0 15 Nov 1995 18:07:18 terryt
+* Initial revision.
+*
+* Rev 1.2 25 Aug 1995 16:23:02 terryt
+* Capture support
+*
+* Rev 1.1 23 May 1995 19:37:02 terryt
+* Spruce up source
+*
+* Rev 1.0 15 May 1995 19:10:40 terryt
+* Initial revision.
+*
+*************************************************************************/
+#include <common.h>
+
+#include <nwapi.h>
+#include <npapi.h>
+
+#include "ntnw.h"
+/*
+ * Name of NetWare provider
+ */
+TCHAR NW_PROVIDER[60];
+unsigned char NW_PROVIDERA[60];
+
+/********************************************************************
+
+ NTPrintExtendedError
+
+Routine Description:
+
+ Print any extended errors from WNet routines
+
+Arguments:
+ None
+
+Return Value:
+ None
+
+ *******************************************************************/
+void
+NTPrintExtendedError( void )
+{
+ DWORD ExError;
+ wchar_t provider[32];
+ wchar_t description[1024];
+
+ if ( !WNetGetLastErrorW( &ExError, description, 1024, provider, 32 ) )
+ wprintf(L"%s\n", description);
+}
+
+
+/********************************************************************
+
+ NTInitProvider
+
+Routine Description:
+
+ Retrieve provider name and save old paths
+
+Arguments:
+ None
+
+Return Value:
+ None
+
+ *******************************************************************/
+void
+NTInitProvider( void )
+{
+ HKEY hKey;
+ DWORD dwType, dwSize;
+ LONG Status;
+ BOOL ret = FALSE;
+
+ dwSize = sizeof(NW_PROVIDER);
+ if ((Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REGISTRY_PROVIDER, 0, KEY_READ, &hKey)) == ERROR_SUCCESS) {
+ (void) RegQueryValueEx(hKey, REGISTRY_PROVIDERNAME, NULL, &dwType, (LPBYTE) NW_PROVIDER, &dwSize);
+ WideTosz( NW_PROVIDERA, NW_PROVIDER, sizeof(NW_PROVIDERA) );
+ }
+
+ RegCloseKey(hKey);
+
+ GetOldPaths();
+}
+
+/********************************************************************
+
+ DeleteDriveBase
+
+Routine Description:
+
+ Disconnect drive from network
+
+Arguments:
+
+ DriveNumber - number of drive 1-26
+
+Return Value:
+ 0 - success
+ else NetWare error
+
+ *******************************************************************/
+unsigned int
+DeleteDriveBase( unsigned short DriveNumber)
+{
+ static char drivename[] = "A:";
+ unsigned int dwRes;
+
+ drivename[0] = 'A' + DriveNumber - 1;
+
+ dwRes = WNetCancelConnection2A( drivename, 0, TRUE );
+
+ if ( dwRes != NO_ERROR )
+ dwRes = GetLastError();
+
+ if ( dwRes == ERROR_EXTENDED_ERROR )
+ NTPrintExtendedError();
+
+ return dwRes;
+}
+
+/********************************************************************
+
+ DetachFromFileServer
+
+Routine Description:
+
+ Break connection from a file server
+
+Arguments:
+
+ ConnectionId - Connection handle
+
+Return Value:
+ 0 = success
+ else NT error
+
+ *******************************************************************/
+unsigned int
+DetachFromFileServer( unsigned int ConnectionId )
+{
+ return ( NWDetachFromFileServer( (NWCONN_HANDLE)ConnectionId ) );
+}
+
+
+/********************************************************************
+
+ NTLoginToFileServer
+
+Routine Description:
+
+ Login to a file server given a user name and password.
+
+ If a NULL password is passed in, the default password should
+ be tried if no password failed.
+
+Arguments:
+
+ pszServerName - Server name
+ pszUserName - User name
+ pszPassword - Password
+
+Return Value:
+ 0 = success
+ else NetWare error
+
+ *******************************************************************/
+unsigned int
+NTLoginToFileServer(
+ char *pszServerName,
+ char *pszUserName,
+ char *pszPassword
+ )
+{
+ NETRESOURCEA NetResource;
+ DWORD dwRes;
+
+ //
+ // validate parameters
+ //
+ if (!pszServerName || !pszUserName || !pszPassword) {
+ DisplayMessage(IDR_ERROR_DURING, "NTLoginToFileServer");
+ return 0xffffffff ;
+ }
+
+ NetResource.dwScope = 0 ;
+ NetResource.dwUsage = 0 ;
+ NetResource.dwType = RESOURCETYPE_ANY;
+ NetResource.lpLocalName = NULL;
+ NetResource.lpRemoteName = pszServerName;
+ NetResource.lpComment = NULL;
+ NetResource.lpProvider = NW_PROVIDERA ;
+
+ //
+ // make the connection
+ //
+ dwRes=WNetAddConnection2A ( &NetResource,
+ pszPassword,
+ pszUserName,
+ 0 );
+ if ( dwRes != NO_ERROR )
+ dwRes = GetLastError();
+
+ //
+ // Try default password if no password was specified
+ //
+ // The error numbers aren't (or weren't) reliable (ERROR_INVALID_PASSWORD)
+ //
+ if ( ( dwRes != NO_ERROR ) && ( pszPassword[0] == '\0' ) ) {
+ dwRes=WNetAddConnection2A ( &NetResource,
+ NULL,
+ pszUserName,
+ 0 );
+ if ( dwRes != NO_ERROR )
+ dwRes = GetLastError();
+ }
+
+ return( dwRes );
+}
+
+/********************************************************************
+
+ GetFileServerName
+
+Routine Description:
+
+ Return the server name associated with the connection ID
+
+Arguments:
+
+ ConnectionId - Connection ID to a server
+ pServerName - Returned server name
+
+Return Value:
+ 0 - success
+ else NT error
+
+ *******************************************************************/
+unsigned int
+GetFileServerName(
+ unsigned int ConnectionId,
+ char * pServerName
+ )
+{
+ unsigned int Result;
+ VERSION_INFO VerInfo;
+
+ *pServerName = '\0';
+
+ Result = NWGetFileServerVersionInfo( (NWCONN_HANDLE) ConnectionId,
+ &VerInfo );
+ if ( !Result )
+ {
+ strcpy( pServerName, VerInfo.szName );
+ }
+
+ return Result;
+}
+
+/********************************************************************
+
+ SetDriveBase
+
+Routine Description:
+
+ Connect a drive to a NetWare volume
+
+Arguments:
+
+ DriveNumber - number of drive 1-26
+ ServerName - server name
+ DirHandle - not used
+ pDirPath - Volume:\Path
+
+Return Value:
+ 0 = success
+ else NetWare error
+
+ *******************************************************************/
+unsigned int
+SetDriveBase(
+ unsigned short DriveNumber,
+ unsigned char *ServerName,
+ unsigned int DirHandle,
+ unsigned char *pDirPath
+ )
+{
+ unsigned int Result = 0;
+ static char driveName[] = "A:" ;
+
+ /*
+ * DirHandle is never used
+ */
+
+ driveName[0]= 'A' + DriveNumber - 1;
+
+ if ( ( ServerName[0] == '\0' ) && fNDS ) {
+
+ /*
+ * Assume its an NDS volume name, if that fails, then
+ * try a default file server volume.
+ */
+ Result = NTSetDriveBase( driveName, NDSTREE, pDirPath );
+
+ if ( !Result )
+ return Result;
+
+ Result = NTSetDriveBase( driveName, PREFERRED_SERVER, pDirPath );
+
+ return Result;
+ }
+
+ Result = NTSetDriveBase( driveName, ServerName, pDirPath );
+
+ return Result;
+}
+
+
+/********************************************************************
+
+ NTSetDriveBase
+
+Routine Description:
+
+ Connect a local name to a NetWare volume and path
+
+Arguments:
+
+ pszLocalName - local name to connect
+ pszServerName - name of file server
+ pszDirPath - Volume:\Path
+
+Return Value:
+ 0 = success
+ else NetWare error
+
+ *******************************************************************/
+unsigned int
+NTSetDriveBase( unsigned char * pszLocalName,
+ unsigned char * pszServerName,
+ unsigned char * pszDirPath )
+{
+ NETRESOURCEA NetResource;
+ DWORD dwRes, dwSize;
+ unsigned char * pszRemoteName = NULL;
+ char * p;
+
+ //
+ // validate parameters
+ //
+ if (!pszLocalName || !pszServerName || !pszDirPath) {
+ DisplayMessage(IDR_ERROR_DURING, "NTSetDriveBase");
+ return 0xffffffff ;
+ }
+
+ //
+ // allocate memory for string
+ //
+ dwSize = strlen(pszDirPath) + strlen(pszServerName) + 5 ;
+ if (!(pszRemoteName = (unsigned char *)LocalAlloc(
+ LPTR,
+ dwSize)))
+ {
+ DisplayMessage(IDR_NOT_ENOUGH_MEMORY);
+ dwRes = 0xffffffff;
+ goto ExitPoint ;
+ }
+
+ //
+ // The requester understands
+ // server\volume:dir
+ // but not
+ // server\volume:\dir
+ //
+ // So just convert it to UNC
+ //
+
+ strcpy( pszRemoteName, "\\\\" );
+ strcat( pszRemoteName, pszServerName );
+ strcat( pszRemoteName, "\\" );
+ strcat( pszRemoteName, pszDirPath );
+
+ p = strchr( pszRemoteName, ':' );
+ if ( !p ) {
+ DisplayMessage(IDR_NO_VOLUME);
+ dwRes = 0xffffffff;
+ goto ExitPoint ;
+ }
+ *p++ = '\\';
+
+ if ( *p == '\\' ) {
+ /* Don't want a double backslash */
+ *p = '\0';
+ p = strchr( pszDirPath, ':' );
+ p++;
+ p++;
+ strcat( pszRemoteName, p );
+ }
+
+ //
+ // strip off trailing backslash
+ //
+ if (pszRemoteName[strlen(pszRemoteName)-1] == '\\')
+ pszRemoteName[strlen(pszRemoteName)-1] = '\0';
+
+ NetResource.dwScope = 0 ;
+ NetResource.dwUsage = 0 ;
+ NetResource.dwType = RESOURCETYPE_DISK;
+ NetResource.lpLocalName = pszLocalName;
+ NetResource.lpRemoteName = pszRemoteName;
+ NetResource.lpComment = NULL;
+ NetResource.lpProvider = NW_PROVIDERA ;
+
+ //
+ // make the connection
+ //
+ dwRes=WNetAddConnection2A ( &NetResource, NULL, NULL, 0 );
+
+ if ( dwRes != NO_ERROR )
+ dwRes = GetLastError();
+
+ExitPoint:
+
+ if (pszRemoteName)
+ (void) LocalFree((HLOCAL) pszRemoteName) ;
+
+ return( dwRes );
+}
+
+
+/********************************************************************
+
+ Is40Server
+
+Routine Description:
+
+ Returns TRUE if 4X server
+
+Arguments:
+
+ ConnectionHandle - Connection Handle
+
+Return Value:
+ TRUE = 4X server
+ FALSE = pre-4X server
+
+ *******************************************************************/
+unsigned int
+Is40Server(
+ unsigned int ConnectionHandle
+ )
+{
+ NTSTATUS NtStatus ;
+ VERSION_INFO VerInfo;
+ unsigned int Version;
+
+ NtStatus = NWGetFileServerVersionInfo( (NWCONN_HANDLE)ConnectionHandle,
+ &VerInfo );
+
+ if (!NT_SUCCESS(NtStatus))
+ FALSE;
+
+ Version = VerInfo.Version * 1000 + VerInfo.SubVersion * 10;
+
+ if ( Version >= 4000 ) {
+ return TRUE;
+ }
+ else {
+ return FALSE;
+ }
+}
+
+/********************************************************************
+
+ CleanupExit
+
+Routine Description:
+
+ Does any cleanup and exits
+
+Arguments:
+
+ ExitCode - exit code for exit()
+
+Return Value:
+ does not return
+
+ *******************************************************************/
+void
+CleanupExit ( int ExitCode )
+{
+ if ( fNDS )
+ NDSCleanup();
+
+ exit( ExitCode );
+}
+
+/********************************************************************
+
+ NTGetNWUserName
+
+Routine Description:
+
+ Get NetWare user name
+
+Arguments:
+
+ TreeBuffer IN - wide string for server or tree
+ UserName OUT - user name
+ Length IN - length of user name
+
+Return Value:
+ error message
+
+ *******************************************************************/
+int
+NTGetNWUserName( PWCHAR TreeBuffer, PWCHAR UserName, int Length )
+{
+
+ NTSTATUS ntstatus;
+ IO_STATUS_BLOCK IoStatusBlock;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ ACCESS_MASK DesiredAccess = SYNCHRONIZE | FILE_LIST_DIRECTORY;
+ HANDLE hRdr;
+
+ WCHAR DevicePreamble[] = L"\\Device\\Nwrdr\\";
+ UINT PreambleLength = 14;
+
+ WCHAR NameStr[64];
+ UNICODE_STRING OpenName;
+ UINT i;
+
+ UNICODE_STRING NdsTree;
+
+ //
+ // Copy over the preamble.
+ //
+
+ OpenName.MaximumLength = sizeof( NameStr );
+
+ for ( i = 0; i < PreambleLength ; i++ )
+ NameStr[i] = DevicePreamble[i];
+
+ RtlInitUnicodeString( &NdsTree, TreeBuffer );
+
+ //
+ // Copy the server or tree name.
+ //
+
+ for ( i = 0 ; i < ( NdsTree.Length / sizeof( WCHAR ) ) ; i++ ) {
+ NameStr[i + PreambleLength] = NdsTree.Buffer[i];
+ }
+
+ OpenName.Length = ( i * sizeof( WCHAR ) ) +
+ ( PreambleLength * sizeof( WCHAR ) );
+ OpenName.Buffer = NameStr;
+
+ //
+ // Set up the object attributes.
+ //
+
+ 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) )
+ return ntstatus;
+
+ ntstatus = NtFsControlFile( hRdr,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_GET_USERNAME,
+ (PVOID) TreeBuffer,
+ NdsTree.Length,
+ (PVOID) UserName,
+ Length );
+
+ UserName[(USHORT)IoStatusBlock.Information/2] = 0;
+
+ NtClose( hRdr );
+ return ntstatus;
+
+}
diff --git a/private/nw/nwscript/ntcap.c b/private/nw/nwscript/ntcap.c
new file mode 100644
index 000000000..64dc75648
--- /dev/null
+++ b/private/nw/nwscript/ntcap.c
@@ -0,0 +1,329 @@
+/*************************************************************************
+*
+* NTCAP.C
+*
+* NT NetWare routines
+*
+* Copyright (c) 1995 Microsoft Corporation
+*
+* $Log: N:\NT\PRIVATE\NW4\NWSCRIPT\VCS\NTCAP.C $
+*
+* Rev 1.2 10 Apr 1996 14:23:04 terryt
+* Hotfix for 21181hq
+*
+* Rev 1.2 12 Mar 1996 19:54:36 terryt
+* Relative NDS names and merge
+*
+* Rev 1.1 22 Dec 1995 14:25:20 terryt
+* Add Microsoft headers
+*
+* Rev 1.0 15 Nov 1995 18:07:20 terryt
+* Initial revision.
+*
+* Rev 1.0 25 Aug 1995 15:41:14 terryt
+* Initial revision.
+*
+*
+*************************************************************************/
+
+#include "common.h"
+#include <ntddnwfs.h>
+#include <nwapi.h>
+#include <npapi.h>
+#include "ntnw.h"
+
+extern unsigned char NW_PROVIDERA[];
+
+/********************************************************************
+
+ EndCapture
+
+Routine Description:
+
+ Remove the local printer redirection
+
+Arguments:
+ LPTDevice - IN
+ 1, 2, or 3 - the local printer #
+
+Return Value:
+ Error
+
+ *******************************************************************/
+unsigned int
+EndCapture(
+ unsigned char LPTDevice
+ )
+{
+ char LPTname[] = "LPT1";
+ unsigned int dwRes;
+
+ LPTname[3] = '1' + LPTDevice - 1;
+
+ /*
+ * Should we check for non-NetWare printers?
+ */
+
+ dwRes = WNetCancelConnection2A( LPTname, 0, TRUE );
+
+ if ( dwRes != NO_ERROR )
+ dwRes = GetLastError();
+
+ if ( dwRes == ERROR_EXTENDED_ERROR )
+ NTPrintExtendedError();
+
+ return dwRes;
+}
+
+
+/********************************************************************
+
+ GetCaptureFlags
+
+Routine Description:
+
+ Return info about the printer capture status. Note that the only
+ options set on NT are on a per-user basis and can be changed with
+ the control panel.
+
+Arguments:
+ LPTDevice - IN
+ LPT device 1, 2 or 3
+ pCaptureFlagsRW - OUT
+ Capture options
+ pCaptureFlagsRO - OUT
+ Capture options
+
+Return Value:
+
+ *******************************************************************/
+unsigned int
+GetCaptureFlags(
+ unsigned char LPTDevice,
+ PNETWARE_CAPTURE_FLAGS_RW pCaptureFlagsRW,
+ PNETWARE_CAPTURE_FLAGS_RO pCaptureFlagsRO
+ )
+{
+ LPBYTE Buffer ;
+ DWORD dwErr ;
+ HANDLE EnumHandle ;
+ DWORD Count ;
+ char LPTName[10];
+ DWORD BufferSize = 4096;
+ char *remotename;
+ char *p;
+ DWORD dwPrintOptions ;
+ LPTSTR pszPreferred ;
+
+ strcpy( LPTName, "LPT1" );
+
+ LPTName[3] = '1' + LPTDevice - 1;
+
+ pCaptureFlagsRO->LPTCaptureFlag = 0;
+
+ //
+ // allocate memory and open the enumeration
+ //
+ if (!(Buffer = LocalAlloc( LPTR, BufferSize ))) {
+ DisplayMessage(IDR_NOT_ENOUGH_MEMORY);
+ return 0xFFFF;
+ }
+
+ dwErr = WNetOpenEnum(RESOURCE_CONNECTED, 0, 0, NULL, &EnumHandle) ;
+ if (dwErr != WN_SUCCESS) {
+ dwErr = GetLastError();
+ if ( dwErr == ERROR_EXTENDED_ERROR )
+ NTPrintExtendedError();
+ (void) LocalFree((HLOCAL) Buffer) ;
+ return 0xFFFF;
+ }
+
+ do {
+
+ Count = 0xFFFFFFFF ;
+ BufferSize = 4096;
+ dwErr = WNetEnumResourceA(EnumHandle, &Count, Buffer, &BufferSize) ;
+
+ if ((dwErr == WN_SUCCESS || dwErr == WN_NO_MORE_ENTRIES)
+ && ( Count != 0xFFFFFFFF) )
+ {
+ LPNETRESOURCEA lpNetResource ;
+ DWORD i ;
+
+ lpNetResource = (LPNETRESOURCEA) Buffer ;
+
+ //
+ // search for our printer
+ //
+ for ( i = 0; i < Count; lpNetResource++, i++ )
+ {
+ if ( lpNetResource->lpLocalName )
+ {
+ if ( !_strcmpi(lpNetResource->lpLocalName, LPTName ))
+ {
+ if ( lpNetResource->lpProvider )
+ {
+ if ( _strcmpi( lpNetResource->lpProvider,
+ NW_PROVIDERA ) )
+ {
+
+ pCaptureFlagsRO->LPTCaptureFlag = 0;
+ }
+ else
+ {
+ remotename = lpNetResource->lpRemoteName;
+ p = strchr (remotename + 2, '\\');
+ if ( !p )
+ return 0xffffffff;
+ *p++ = '\0';
+ _strupr( remotename+2 );
+ _strupr( p );
+ strcpy( pCaptureFlagsRO->ServerName, remotename+2 );
+ strcpy( pCaptureFlagsRO->QueueName, p );
+ pCaptureFlagsRO->LPTCaptureFlag = 1;
+
+ pCaptureFlagsRW->JobControlFlags = 0;
+ pCaptureFlagsRW->TabSize = 8;
+ pCaptureFlagsRW->NumCopies = 1;
+ //
+ // query NW wksta for print options
+ // & preferred server
+ //
+ if ( NwQueryInfo(&dwPrintOptions,
+ &pszPreferred)) {
+ pCaptureFlagsRW->PrintFlags =
+ CAPTURE_FLAG_NOTIFY |
+ CAPTURE_FLAG_PRINT_BANNER ;
+ }
+ else {
+ pCaptureFlagsRW->PrintFlags = 0;
+ if ( dwPrintOptions & NW_PRINT_PRINT_NOTIFY )
+ pCaptureFlagsRW->PrintFlags |=
+ CAPTURE_FLAG_NOTIFY;
+ if ( dwPrintOptions & NW_PRINT_SUPPRESS_FORMFEED)
+ pCaptureFlagsRW->PrintFlags |=
+ CAPTURE_FLAG_NO_FORMFEED;
+ if ( dwPrintOptions & NW_PRINT_PRINT_BANNER )
+ pCaptureFlagsRW->PrintFlags |=
+ CAPTURE_FLAG_PRINT_BANNER;
+ }
+ pCaptureFlagsRW->FormName[0] = 0;
+ pCaptureFlagsRW->FormType = 0;
+ pCaptureFlagsRW->BannerText[0] = 0;
+ pCaptureFlagsRW->FlushCaptureTimeout = 0;
+ pCaptureFlagsRW->FlushCaptureOnClose = 1;
+ }
+ }
+ else
+ {
+ pCaptureFlagsRO->LPTCaptureFlag = 0;
+ }
+
+ (void) WNetCloseEnum(EnumHandle) ;
+ (void) LocalFree((HLOCAL) Buffer) ;
+ return 0;
+ }
+ }
+ }
+ }
+
+ } while (dwErr == WN_SUCCESS) ;
+
+ if ( ( dwErr != WN_SUCCESS ) && ( dwErr != WN_NO_MORE_ENTRIES ) )
+ {
+ dwErr = GetLastError();
+ if ( dwErr == ERROR_EXTENDED_ERROR )
+ NTPrintExtendedError();
+ }
+
+ (void ) WNetCloseEnum(EnumHandle) ;
+ (void) LocalFree((HLOCAL) Buffer) ;
+
+ return 0;
+}
+
+/********************************************************************
+
+ StartQueueCapture
+
+Routine Description:
+
+ Attach local name to the queue.
+
+
+Arguments:
+ ConnectionHandle - IN
+ Handle to file server
+ LPTDevice - IN
+ LPT 1, 2 or 3
+ pServerName - IN
+ Server name
+ pQueueName - IN
+ Printer queue name
+
+Return Value:
+
+ *******************************************************************/
+unsigned int
+StartQueueCapture(
+ unsigned int ConnectionHandle,
+ unsigned char LPTDevice,
+ unsigned char *pServerName,
+ unsigned char *pQueueName
+ )
+{
+ NETRESOURCEA NetResource;
+ DWORD dwRes, dwSize;
+ unsigned char * pszRemoteName = NULL;
+ unsigned char pszLocalName[10];
+ char * p;
+
+ //
+ // validate parameters
+ //
+ if (!pServerName || !pQueueName || !LPTDevice) {
+ DisplayMessage(IDR_ERROR_DURING, "StartQueueCapture");
+ return 0xffffffff ;
+ }
+
+ //
+ // allocate memory for string
+ //
+ dwSize = strlen(pServerName) + strlen(pQueueName) + 5 ;
+ if (!(pszRemoteName = (unsigned char *)LocalAlloc(
+ LPTR,
+ dwSize)))
+ {
+ DisplayMessage(IDR_NOT_ENOUGH_MEMORY);
+ dwRes = 0xffffffff;
+ goto ExitPoint ;
+ }
+
+ sprintf(pszRemoteName, "\\\\%s\\%s", pServerName, pQueueName);
+ sprintf(pszLocalName, "LPT%d", LPTDevice );
+
+ NetResource.dwScope = 0 ;
+ NetResource.dwUsage = 0 ;
+ NetResource.dwType = RESOURCETYPE_PRINT;
+ NetResource.lpLocalName = pszLocalName;
+ NetResource.lpRemoteName = pszRemoteName;
+ NetResource.lpComment = NULL;
+ NetResource.lpProvider = NW_PROVIDERA ;
+
+ //
+ // make the connection
+ //
+ dwRes=WNetAddConnection2A ( &NetResource, NULL, NULL, 0 );
+
+ if ( dwRes != NO_ERROR )
+ dwRes = GetLastError();
+
+ExitPoint:
+
+ if (pszRemoteName)
+ (void) LocalFree((HLOCAL) pszRemoteName) ;
+
+ return( dwRes );
+}
+
+
diff --git a/private/nw/nwscript/ntnw.c b/private/nw/nwscript/ntnw.c
new file mode 100644
index 000000000..00cf54057
--- /dev/null
+++ b/private/nw/nwscript/ntnw.c
@@ -0,0 +1,163 @@
+/*************************************************************************
+*
+* NTNW.C
+*
+* Dos NetWare to NT NetWare translation
+*
+* Copyright (c) 1995 Microsoft Corporation
+*
+* $Log: N:\NT\PRIVATE\NW4\NWSCRIPT\VCS\NTNW.C $
+*
+* Rev 1.1 22 Dec 1995 14:25:28 terryt
+* Add Microsoft headers
+*
+* Rev 1.0 15 Nov 1995 18:07:24 terryt
+* Initial revision.
+*
+* Rev 1.2 25 Aug 1995 16:23:08 terryt
+* Capture support
+*
+* Rev 1.1 23 May 1995 19:37:10 terryt
+* Spruce up source
+*
+* Rev 1.0 15 May 1995 19:10:44 terryt
+* Initial revision.
+*
+*************************************************************************/
+
+#include <stdio.h>
+#include <direct.h>
+#include <time.h>
+#include "common.h"
+
+extern int CONNECTION_ID;
+
+/********************************************************************
+
+ NTGetCurrentDirectory
+
+Routine Description:
+
+ Return the current directory.
+
+Arguments:
+
+ DriveNumber = The drive to get the directory from.
+ (0 = A, 1 = B, 2 = C, etc)
+ pPath = A pointer to a 64 byte buffer to return the
+ current directory.
+
+Return Value:
+
+ 0 Success
+ else Error
+
+ ********************************************************************/
+
+unsigned int
+NTGetCurrentDirectory(
+ unsigned char DriveNumber,
+ unsigned char *pPath
+ )
+{
+ char * CurPath;
+ int currentDrive = _getdrive() ;
+
+ //
+ // Change to the drive and get its current working directory.
+ // Default to root if fail to get cwd. DriveNumber is from 0.
+ //
+
+ _chdrive (DriveNumber+1);
+
+ CurPath = _getcwd(NULL,MAX_PATH) ;
+
+ if ( CurPath != NULL ) {
+
+ strcpy( pPath, CurPath );
+ free(CurPath) ;
+ }
+ else {
+
+ strcpy( pPath, "A:\\" );
+ pPath[0] += DriveNumber;
+ }
+
+ _chdrive (currentDrive);
+
+ return 0;
+}
+
+/********************************************************************
+
+ AttachToFileServer
+
+Routine Description:
+
+ Attach to a named file server
+
+Arguments:
+
+ pServerName - Name of server
+ pNewConnectionId - returned connection handle
+
+Return Value:
+ 0 = success
+ else NetWare error
+
+ *******************************************************************/
+unsigned int
+AttachToFileServer(
+ unsigned char *pServerName,
+ unsigned int *pNewConnectionId
+ )
+{
+ unsigned int Result;
+
+ if ( NTIsConnected( pServerName ) ) {
+ return 0x8800; // Already atached.
+ }
+
+ Result = NTAttachToFileServer( pServerName, pNewConnectionId );
+
+ return Result;
+}
+
+/********************************************************************
+
+ GetConnectionHandle
+
+Routine Description:
+
+ Given a server name, return the connection handle.
+ The server should already be attached
+ Note that this is not called for 4X servers. It's used
+ for attaches and bindery connections.
+
+Arguments:
+
+ pServerName - Name of server
+ pConnectionHandle - pointer to returned connection handle
+
+Return Value:
+ 0 = success
+ else NetWare error
+
+ *******************************************************************/
+unsigned int
+GetConnectionHandle(
+ unsigned char *pServerName,
+ unsigned int *pConnectionHandle
+ )
+{
+ unsigned int Result;
+
+ if ( !NTIsConnected( pServerName ) ) {
+ return 0xFFFF; // not already connected
+ }
+
+ Result = NTAttachToFileServer( pServerName, pConnectionHandle );
+
+ return Result;
+}
+
diff --git a/private/nw/nwscript/ntscript.c b/private/nw/nwscript/ntscript.c
new file mode 100644
index 000000000..b29d9beba
--- /dev/null
+++ b/private/nw/nwscript/ntscript.c
@@ -0,0 +1,304 @@
+/*************************************************************************
+*
+* NTSCRIPT.C
+*
+* Process all login scripts
+*
+* Copyright (c) 1995 Microsoft Corporation
+*
+* $Log: N:\NT\PRIVATE\NW4\NWSCRIPT\VCS\NTSCRIPT.C $
+*
+* Rev 1.8 10 Apr 1996 14:23:12 terryt
+* Hotfix for 21181hq
+*
+* Rev 1.9 12 Mar 1996 19:54:58 terryt
+* Relative NDS names and merge
+*
+* Rev 1.8 07 Mar 1996 18:36:56 terryt
+* Misc fixes
+*
+* Rev 1.7 22 Jan 1996 16:48:26 terryt
+* Add automatic attach query during map
+*
+* Rev 1.6 08 Jan 1996 13:57:58 terryt
+* Correct NDS Preferred Server
+*
+* Rev 1.5 05 Jan 1996 17:18:26 terryt
+* Ensure context is the correct login default
+*
+* Rev 1.4 04 Jan 1996 18:56:48 terryt
+* Bug fixes reported by MS
+*
+* Rev 1.3 22 Dec 1995 11:08:16 terryt
+* Fixes
+*
+* Rev 1.2 22 Nov 1995 15:43:52 terryt
+* Use proper NetWare user name call
+*
+* Rev 1.1 20 Nov 1995 15:09:38 terryt
+* Context and capture changes
+*
+* Rev 1.0 15 Nov 1995 18:07:28 terryt
+* Initial revision.
+*
+* Rev 1.2 25 Aug 1995 16:23:14 terryt
+* Capture support
+*
+* Rev 1.1 26 Jul 1995 16:02:00 terryt
+* Allow deletion of current drive
+*
+* Rev 1.0 15 May 1995 19:10:46 terryt
+* Initial revision.
+*
+*************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <direct.h>
+#include <process.h>
+#include <string.h>
+#include <malloc.h>
+
+#include "common.h"
+
+#include "inc/ntnw.h"
+
+#include <nwapi.h>
+
+void ProcessLoginScripts(unsigned int conn, char * UserName, int argc, char **argv, char *lpScript);
+
+extern int SafeDisk;
+
+extern unsigned int ConvertNDSPathToNetWarePath(char *, char *, char *);
+
+/*************************************************************************
+*
+* NTNetWareLoginScripts
+* Main logon script processor
+*
+* ENTRY:
+*
+* EXIT
+*
+*************************************************************************/
+
+int
+NTNetWareLoginScripts( int argc, char ** argv )
+{
+ unsigned int defConn;
+ char UserName[MAX_NAME_LEN]="";
+ WCHAR UserName_w[MAX_NAME_LEN * sizeof(WCHAR)]=L"";
+ char MessageServer[NDS_NAME_CHARS]="";
+ char *lpScript = NULL;
+ DWORD PrintOptions;
+ LPTSTR pszPreferredSrv;
+ LPTSTR ptreeW;
+ LPTSTR pcontextW;
+ NTSTATUS ntstatus;
+ char * lpC1;
+ char * lpC2;
+ unsigned int NewConn;
+ unsigned int Result;
+
+ if ( NwQueryInfo( &PrintOptions, &pszPreferredSrv ) || !pszPreferredSrv )
+ {
+ DisplayMessage(IDR_QUERY_INFO_FAILED);
+ return( FALSE );
+ }
+
+ //
+ // nwscript /S filename
+ //
+ // can be used to pass a local script file for testing
+ //
+ if ( ( argc >= 3 ) && !_strcmpi(argv[1], "/S") )
+ {
+ lpScript = argv[2];
+ argc -= 2;
+ argv += 2;
+ }
+
+ //
+ // NDS preferred server format is:
+ // *<tree name>\<context>
+
+ fNDS = ( *pszPreferredSrv == L'*' );
+
+ if ( fNDS )
+ {
+
+ // Get the NDS tree name
+
+ ptreeW = pszPreferredSrv + 1;
+
+ pcontextW = wcschr( pszPreferredSrv, L'\\' );
+
+ if ( pcontextW )
+ {
+ *pcontextW++ = L'\0';
+ }
+
+ NDSTREE = malloc ( CONTEXT_MAX );
+ NDSTREE_w = malloc ( CONTEXT_MAX * sizeof(WCHAR) );
+ if ( ptreeW )
+ {
+ wcscpy( NDSTREE_w, ptreeW );
+ RtlInitUnicodeString( &NDSTREE_u, NDSTREE_w );
+ WideTosz( NDSTREE, ptreeW, CONTEXT_MAX );
+ _strupr( NDSTREE );
+ }
+ else
+ {
+ strcpy( NDSTREE, "" );
+ wcscpy( NDSTREE_w, L"" );
+ }
+
+ // Get the fully typed user name
+
+ TYPED_USER_NAME_w = malloc ( sizeof(WCHAR) * NDS_NAME_CHARS );
+ TYPED_USER_NAME = malloc ( NDS_NAME_CHARS );
+
+ ntstatus = NTGetNWUserName( NDSTREE_w, TYPED_USER_NAME_w,
+ sizeof(WCHAR) * NDS_NAME_CHARS );
+ if ( !NT_SUCCESS( ntstatus ) ) {
+ DisplayMessage(IDR_QUERY_INFO_FAILED);
+ return ( FALSE );
+ }
+
+ WideTosz( TYPED_USER_NAME, TYPED_USER_NAME_w, NDS_NAME_CHARS );
+
+ // Get the user name stripped of context and type
+
+ lpC1 = strchr( TYPED_USER_NAME, '=' );
+ if ( lpC1 )
+ lpC1++;
+ else
+ lpC1 = TYPED_USER_NAME;
+
+ lpC2 = strchr( TYPED_USER_NAME, '.' );
+
+ if ( lpC2 )
+ strncpy( UserName, lpC1, lpC2 - lpC1 );
+ else
+ strcpy( UserName, lpC1 );
+
+ // Get the default context
+ // This should be where the user is
+
+ REQUESTER_CONTEXT = malloc( CONTEXT_MAX );
+
+ if ( lpC2 )
+ {
+ strcpy( REQUESTER_CONTEXT, lpC2+1 );
+ }
+ else
+ {
+ strcpy( REQUESTER_CONTEXT, "" );
+ }
+ NDSTypeless( REQUESTER_CONTEXT, REQUESTER_CONTEXT );
+
+ //
+ // This finishes the NDS initialization
+ //
+ if ( NDSInitUserProperty () )
+ return ( FALSE );
+
+ }
+ else
+ {
+ ntstatus = NTGetNWUserName( pszPreferredSrv, UserName_w,
+ MAX_NAME_LEN * sizeof(WCHAR) );
+ if ( !NT_SUCCESS( ntstatus ) ) {
+ DisplayMessage(IDR_QUERY_INFO_FAILED);
+ return ( FALSE );
+ }
+ WideTosz( UserName, UserName_w, MAX_NAME_LEN );
+ _strupr( UserName );
+ }
+
+ //
+ // If we map over a drive, the SafeDisk is used.
+ //
+ SafeDisk = _getdrive();
+
+ NTInitProvider();
+
+ //
+ // Get the default connection handle.
+ //
+ // This is used to get the preferred server!
+
+ if ( !CGetDefaultConnectionID (&defConn) )
+ return( FALSE );
+
+ PREFERRED_SERVER = malloc( NDS_NAME_CHARS );
+
+ GetFileServerName(defConn, PREFERRED_SERVER);
+
+ //
+ // By default we are "attached" to the default server
+ //
+ if ( fNDS )
+ AddServerToAttachList( PREFERRED_SERVER, LIST_4X_SERVER );
+ else
+ AddServerToAttachList( PREFERRED_SERVER, LIST_3X_SERVER );
+
+ //
+ // Print out status
+ //
+ if ( fNDS )
+ {
+ DisplayMessage( IDR_CURRENT_CONTEXT, REQUESTER_CONTEXT );
+ DisplayMessage( IDR_CURRENT_TREE, NDSTREE_w );
+ }
+
+ DisplayMessage( IDR_CURRENT_SERVER, PREFERRED_SERVER );
+
+ //
+ // We may want to change the Preferred Server based on the DS.
+ // "MESSAGE_SERVER" should be the Preferred Server (if possible).
+ //
+ if ( fNDS )
+ {
+ NDSGetVar ( "MESSAGE_SERVER", MessageServer, NDS_NAME_CHARS );
+ if ( strlen( MessageServer ) )
+ {
+ NDSAbbreviateName(FLAGS_NO_CONTEXT, MessageServer, MessageServer);
+ lpC1 = strchr( MessageServer, '.' );
+ if ( lpC1 )
+ *lpC1 = '\0';
+ if ( strcmp( MessageServer, PREFERRED_SERVER) )
+ {
+ DisplayMessage( IDR_AUTHENTICATING_SERVER, MessageServer );
+ Result = NTAttachToFileServer( MessageServer, &NewConn );
+ if ( Result )
+ {
+ DisplayMessage( IDR_SERVER_NOT_FOUND, MessageServer );
+ }
+ else
+ {
+ NWDetachFromFileServer( (NWCONN_HANDLE)NewConn );
+ strncpy( PREFERRED_SERVER, MessageServer, NDS_NAME_CHARS);
+ DisplayMessage( IDR_CURRENT_SERVER, PREFERRED_SERVER );
+
+ // By default we are "attached" to the preferred server
+
+ AddServerToAttachList( PREFERRED_SERVER, LIST_4X_SERVER );
+ }
+ }
+ }
+ }
+
+ //
+ // Just like login we ignore any errors from setting the login
+ // directory.
+ //
+ SetLoginDirectory (PREFERRED_SERVER);
+
+ // Process login scripts.
+
+ ProcessLoginScripts(defConn, UserName, argc, argv, lpScript);
+
+ return( TRUE );
+}
diff --git a/private/nw/nwscript/nwapi1.c b/private/nw/nwscript/nwapi1.c
new file mode 100644
index 000000000..a7a8c30c6
--- /dev/null
+++ b/private/nw/nwscript/nwapi1.c
@@ -0,0 +1,47 @@
+
+/*************************************************************************
+*
+* NWAPI1.C
+*
+* NetWare routines, ported from DOS
+*
+* Copyright (c) 1995 Microsoft Corporation
+*
+* $Log: N:\NT\PRIVATE\NW4\NWSCRIPT\VCS\NWAPI1.C $
+*
+* Rev 1.1 22 Dec 1995 14:25:48 terryt
+* Add Microsoft headers
+*
+* Rev 1.0 15 Nov 1995 18:07:32 terryt
+* Initial revision.
+*
+* Rev 1.0 15 May 1995 19:10:48 terryt
+* Initial revision.
+*
+*************************************************************************/
+#include "common.h"
+
+/*
+ Get default connection handle.
+ return TRUE if succeeded, FALSE otherwise.
+ */
+int CGetDefaultConnectionID ( unsigned int * pConn )
+{
+ unsigned int iRet = GetDefaultConnectionID(pConn);
+
+ switch (iRet)
+ {
+ case 0:
+ break;
+
+ case 0x880f:
+ DisplayMessage(IDR_NO_KNOWN_FILE_SERVER);
+ break;
+ default:
+ DisplayMessage(IDR_NO_DEFAULT_CONNECTION);
+ break;
+ }
+
+ return(iRet == 0);
+}
+
diff --git a/private/nw/nwscript/nwapi2.c b/private/nw/nwscript/nwapi2.c
new file mode 100644
index 000000000..31e0d669b
--- /dev/null
+++ b/private/nw/nwscript/nwapi2.c
@@ -0,0 +1,49 @@
+
+/*************************************************************************
+*
+* NWAPI2.C
+*
+* NetWare routines, ported from DOS
+*
+* Copyright (c) 1995 Microsoft Corporation
+*
+* $Log: N:\NT\PRIVATE\NW4\NWSCRIPT\VCS\NWAPI2.C $
+*
+* Rev 1.1 22 Dec 1995 14:25:54 terryt
+* Add Microsoft headers
+*
+* Rev 1.0 15 Nov 1995 18:07:34 terryt
+* Initial revision.
+*
+* Rev 1.0 15 May 1995 19:10:50 terryt
+* Initial revision.
+*
+*************************************************************************/
+
+#include <direct.h>
+#include "common.h"
+
+/*
+ Set the current drive to the login directory
+ of the default server.
+ */
+void SetLoginDirectory( PBYTE serverName )
+{
+ unsigned int iRet = 0;
+ WORD firstDrive;
+
+ if(iRet = GetFirstDrive (&firstDrive))
+ {
+ DisplayError(iRet,"GetFirstDrive");
+ return;
+ }
+
+ // Nothing we can do if SetDriveBase failed.
+ // Don't report the error.
+
+ if ( !( SetDriveBase (firstDrive, serverName, 0, "SYS:LOGIN") ) )
+ {
+ _chdrive (firstDrive);
+ ExportCurrentDrive( firstDrive );
+ }
+}
diff --git a/private/nw/nwscript/nwapi3.c b/private/nw/nwscript/nwapi3.c
new file mode 100644
index 000000000..5408d286e
--- /dev/null
+++ b/private/nw/nwscript/nwapi3.c
@@ -0,0 +1,1457 @@
+/*************************************************************************
+*
+* NWAPI3.C
+*
+* NetWare routines ported from DOS
+*
+* Copyright (c) 1995 Microsoft Corporation
+*
+* $Log: N:\NT\PRIVATE\NW4\NWSCRIPT\VCS\NWAPI3.C $
+*
+* Rev 1.4 18 Apr 1996 16:52:14 terryt
+* Various enhancements
+*
+* Rev 1.3 10 Apr 1996 14:23:20 terryt
+* Hotfix for 21181hq
+*
+* Rev 1.5 13 Mar 1996 18:49:28 terryt
+* Support directory maps
+*
+* Rev 1.4 12 Mar 1996 19:55:10 terryt
+* Relative NDS names and merge
+*
+* Rev 1.2 22 Dec 1995 14:26:02 terryt
+* Add Microsoft headers
+*
+* Rev 1.1 22 Nov 1995 15:41:56 terryt
+* Fix MAP ROOT of search drives
+*
+* Rev 1.0 15 Nov 1995 18:07:38 terryt
+* Initial revision.
+*
+* Rev 1.3 25 Aug 1995 16:23:22 terryt
+* Capture support
+*
+* Rev 1.2 26 Jul 1995 16:02:08 terryt
+* Allow deletion of current drive
+*
+* Rev 1.1 23 Jun 1995 09:49:22 terryt
+* Add error message for mapping over MS network drive
+*
+* Rev 1.0 15 May 1995 19:10:54 terryt
+* Initial revision.
+*
+*************************************************************************/
+
+/*
+Module Name:
+ nwapi3.c
+
+Abstract:
+ can :
+ - view current mapping
+ - create/change a drive mapping
+ - create/change a search drive mapping
+ - map a drive to a fake root
+ - map the next available drive
+
+ SYNTAX (Command line)
+ View current mapping.
+ MAP [drive:]
+ Create or change network drive mappings
+ MAP path
+ MAP drive:=[drive:|path]
+ MAP [DEL[ete] | REM[ove]] drive:
+ Create or change search dirve mappings
+ MAP [INS[ert]] drive:=[drive:|path]
+ Map a drive to a fake root directory
+ MAP ROOT drive:=[drive:|path]
+ Map the next available dirve
+ MAP N[ext] [drive:|path]
+
+
+
+Author : Thierry TABARD (thierryt)
+
+Revision history :
+ - 03/10/94 thierryt started
+ - 05/13/94 congpay rewrote.
+*/
+
+#include <ctype.h>
+#include <direct.h>
+#include "common.h"
+
+/* Local functions*/
+int IsDrive( char * input);
+int GetSearchNumber( char * input);
+int IsNetwareDrive (int driveNum);
+int IsLocalDrive (int driveNum);
+int IsNonNetwareNetworkDrive (int driveNum);
+int GetDriveFromSearchNumber (int searchNumber);
+
+void DisplayDriveMapping(WORD drive);
+void DisplaySearchDriveMapping(int searchNumber);
+
+int ParseMapPath(char * mapPath, char * volName, char * dirPath, char * serverName, int fMapErrorsOn, char *lpCommand);
+int MapDrive (int driveNum, int searchNum, char * mapPath, int bRoot, int bInsert, int fMapErrorsOn, char *lpCommand);
+int MapNonSearchDrive (int driveNum, char *mapPath, int bRoot, int fMapDisplayOn, int fMapErrorsOn, char *lpCommand);
+int MapSearchDrive (int searchNum, int driveNum, char *mapPath, int bInsert, int bRoot, int fMapDisplayOn, int fMapErrorsOn, char *lpCommand);
+int MapNextAvailableDrive (char *mapPath, int fMapDisplayOn, int fMapErrorsOn, char *lpCommand);
+
+void RemoveDriveFromPath(int searchNumber, int fMapErrorsOn);
+int RemoveDrive (WORD drive, int fMapDisplayOn, int fMapErrorsOn);
+void RemoveSearchDrive (int searchNumber, int fMapDisplayOn, int fMapErrorsOn);
+int InsertSearchDrive(int searchNum, int driveNum, int bInsert, char * insertPath);
+
+#define CM_MAP 0
+#define CM_DEL 1
+#define CM_NEXT 2
+#define CM_HELP 3
+#define MAX_INPUT_PATH_LEN 128
+
+int fMapDisplayOn = TRUE;
+int fMapErrorsOn = TRUE;
+int SafeDisk = 2;
+
+int GetFlag (char *buffer, int *pfInsert, int *pfRoot, char **ppPath)
+{
+ int nFlag, nLen;
+ char *lpSpace, *lpTemp;
+
+ if (((*buffer == '/') || (*buffer == '-') || (*buffer == '\\')) &&
+ (*(buffer+1) == '?'))
+ return CM_HELP;
+
+ lpSpace = strchr (buffer, ' ');
+
+ nFlag = CM_MAP; // A bug!
+
+ if (lpSpace == NULL)
+ {
+ *ppPath = buffer;
+ return CM_MAP;
+ }
+
+ nLen = lpSpace - buffer;
+ lpSpace++;
+
+ if (!strncmp(buffer, __DEL__, max (3, nLen)) ||
+ !strncmp(buffer, __REM__, max (3, nLen)))
+ nFlag = CM_DEL;
+ else if (!strncmp(buffer, __NEXT__, nLen))
+ nFlag = CM_NEXT;
+ else if (!strncmp(buffer, __INS__, max (3, nLen)))
+ {
+ *pfInsert = TRUE;
+ if (lpTemp = strchr (lpSpace, ' '))
+ {
+ nLen = lpTemp - lpSpace;
+ if (!strncmp(lpSpace, __ROOT__, nLen))
+ {
+ *pfRoot = TRUE;
+ lpSpace = lpTemp+1;
+ }
+ }
+ }
+ else if (!strncmp(buffer, __ROOT__, nLen))
+ {
+ *pfRoot = TRUE;
+ if (lpTemp = strchr (lpSpace, ' '))
+ {
+ nLen = lpTemp - lpSpace;
+ if (!strncmp(lpSpace, __INS__, max (3, nLen)))
+ {
+ *pfInsert = TRUE;
+ lpSpace = lpTemp+1;
+ }
+ }
+ }
+ else
+ lpSpace = buffer;
+
+ *ppPath = lpSpace;
+
+ return(nFlag);
+}
+
+int Map (char * buffer)
+{
+ WORD status, driveNum;
+ char *lpCommand, *inputPath, *lpEqual;
+ int fRoot, fInsert, fSpace, fCommandHandled;
+ int nFlag, searchNumber, iRet;
+
+ // Fix for NWCS.
+ // NWGetDriveStatus() always returns 1800 on first call for non-Network
+ // drives on NWCS. So we call with c: first to pass the first call.
+ GetDriveStatus (3,
+ NETWARE_FORMAT_SERVER_VOLUME,
+ &status,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ lpCommand = strtok (buffer, ";");
+
+ if (lpCommand == NULL)
+ {
+ DisplayMapping();
+ return(0);
+ }
+
+ do
+ {
+ fRoot = FALSE;
+ fInsert = FALSE;
+ fSpace = FALSE;
+ fCommandHandled = TRUE;
+
+ // Make sure first and last char of the command are not spaces.
+ if (*lpCommand == ' ')
+ lpCommand++;
+
+ if (*(lpCommand+strlen (lpCommand)-1) == ' ')
+ *(lpCommand+strlen (lpCommand)-1) = 0;
+
+ if (!strcmp (lpCommand, "DISPLAY ON"))
+ {
+ fMapDisplayOn = TRUE;
+ continue;
+ }
+ else if (!strcmp (lpCommand, "DISPLAY OFF"))
+ {
+ fMapDisplayOn = FALSE;
+ continue;
+ }
+ else if (!strcmp (lpCommand, "ERROR ON") || !strcmp (lpCommand, "ERRORS ON"))
+ {
+ fMapErrorsOn = TRUE;
+ continue;
+ }
+ else if (!strcmp (lpCommand, "ERROR OFF") || !strcmp (lpCommand, "ERRORS OFF"))
+ {
+ fMapErrorsOn = FALSE;
+ continue;
+ }
+
+ nFlag = GetFlag (lpCommand, &fInsert, &fRoot, &inputPath);
+
+ /*
+ * The 4X login program is much more forgiving about spaces
+ * in the map command.
+ */
+ {
+ char *lpTemp;
+ char *lpCur;
+ int inquote = FALSE;
+
+ lpTemp = inputPath;
+ lpCur = inputPath;
+
+ /*
+ * Compress blanks unless the string is quoted
+ */
+ while ( *lpTemp )
+ {
+ if ( *lpTemp == '\"' )
+ {
+ if ( inquote )
+ inquote = FALSE;
+ else
+ inquote = TRUE;
+ }
+ else if ( !inquote &&
+ (( *lpTemp == ' ' ) ||
+ ( *lpTemp == '\t' ) ) )
+ {
+ }
+ else
+ {
+ *lpCur++ = *lpTemp;
+ }
+ lpTemp = NWAnsiNext(lpTemp);
+ }
+ *lpCur = '\0';
+ }
+
+
+ if (nFlag == CM_HELP && fMapErrorsOn)
+ DisplayMessage(IDR_MAP_USAGE);
+ else if (nFlag == CM_NEXT)
+ {
+ if (strchr (inputPath, '=') ||
+ strchr (inputPath, ' ') ||
+ strchr (inputPath, '\t'))
+ fCommandHandled = FALSE;
+ else
+ iRet = MapNextAvailableDrive (inputPath, fMapDisplayOn, fMapErrorsOn, lpCommand);
+ }
+ else if (nFlag == CM_DEL)
+ {
+ if (driveNum = IsDrive (inputPath))
+ iRet = RemoveDrive (driveNum, fMapDisplayOn, fMapErrorsOn);
+ else if (searchNumber = GetSearchNumber(inputPath))
+ RemoveSearchDrive (searchNumber, fMapDisplayOn, fMapErrorsOn);
+ else
+ fCommandHandled = FALSE;
+ }
+ else //nFlag = CM_MAP
+ {
+ if (driveNum = IsDrive (inputPath))
+ {
+ if (fInsert)
+ fCommandHandled = FALSE;
+ else if (fRoot)
+ iRet = MapNonSearchDrive (0, inputPath, TRUE, fMapDisplayOn, fMapErrorsOn, lpCommand);
+ else
+ DisplayDriveMapping(driveNum);
+ }
+ else if (searchNumber = GetSearchNumber (inputPath))
+ {
+ if (fInsert || fRoot)
+ fCommandHandled = FALSE;
+ else
+ DisplaySearchDriveMapping(searchNumber);
+ }
+ else if ((lpEqual = strchr (inputPath, '=')) == NULL)
+ {
+ if (fInsert || strchr (inputPath, ' '))
+ fCommandHandled = FALSE;
+ else
+ {
+ /*
+ * We must cope with MAP K:DIR which means change the
+ * directory on K:
+ */
+ driveNum = 0;
+ if (isalpha(inputPath[0]) && (inputPath[1] == ':'))
+ {
+ driveNum = toupper(inputPath[0]) - 'A' + 1;
+ if ( !IsNetwareDrive(driveNum) )
+ {
+ driveNum = 0;
+ }
+ }
+ iRet = MapNonSearchDrive (driveNum, inputPath, fRoot, fMapDisplayOn, fMapErrorsOn, lpCommand);
+ }
+ }
+ else
+ {
+ if ( ( !fNDS && strchr (lpEqual+2, ' ') )
+ || lpEqual == inputPath) {
+ fCommandHandled = FALSE;
+ }
+ else
+ {
+ if (*AnsiPrev(inputPath, lpEqual) == ' ')
+ {
+ fSpace = TRUE;
+ *(lpEqual-1) = 0;
+ }
+ else
+ *lpEqual = 0;
+
+ if (*(lpEqual+1) == ' ')
+ lpEqual++;
+
+ driveNum = IsDrive (inputPath);
+ searchNumber = GetSearchNumber (inputPath);
+ *(inputPath+strlen(inputPath)) = fSpace? ' ' : '=';
+
+ /*
+ * This is to handle the cases:
+ *
+ * map x:=s3:=sys:public
+ * map s3:=x:=sys:public
+ *
+ * Unfortuneatly the underlying parsing routines
+ * want everything null terminated, so we need
+ * to do the following shuffle.
+ *
+ */
+ if ( driveNum || searchNumber )
+ {
+ if ((strchr (lpEqual+1, '=')) != NULL)
+ {
+ char * lpEqual2;
+ char *tmpPath = _strdup( lpEqual+1 );
+
+ lpEqual2 = strchr (tmpPath, '=');
+
+ if (*AnsiPrev(tmpPath, lpEqual2) == ' ')
+ {
+ fSpace = TRUE;
+ *(lpEqual2-1) = 0;
+ }
+ else
+ *lpEqual2 = 0;
+
+ if (*(lpEqual2+1) == ' ')
+ lpEqual2++;
+
+ if ( searchNumber )
+ {
+ driveNum = IsDrive (tmpPath);
+ }
+ else
+ {
+ searchNumber = GetSearchNumber (tmpPath);
+ }
+
+ if ( driveNum && searchNumber )
+ {
+ lpEqual += (lpEqual2 - tmpPath) + 1;
+ }
+
+ free (tmpPath);
+
+ }
+ }
+
+ if (searchNumber)
+ {
+ iRet = MapSearchDrive (searchNumber, driveNum, lpEqual+1, fInsert, fRoot, fMapDisplayOn, fMapErrorsOn, lpCommand);
+ }
+ else if (driveNum)
+ {
+ if (fInsert)
+ fCommandHandled = FALSE;
+ else
+ iRet = MapNonSearchDrive (driveNum, lpEqual+1, fRoot, fMapDisplayOn, fMapErrorsOn, lpCommand);
+ }
+ else
+ fCommandHandled = FALSE;
+ }
+ }
+ }
+
+ if (!fCommandHandled && fMapErrorsOn)
+ {
+ DisplayMessage(IDR_MAP_INVALID_PATH, lpCommand);
+ }
+ }while ((lpCommand = strtok (NULL, ";")) != NULL);
+
+ return(iRet & 0xFF);
+}
+
+/* Return drive number if input is a drive specified as a letter followed
+ by ':' for example if input is "A:", return 1
+ or netware drive could be specified as *1: for example.
+ Otherwise, return 0.
+ */
+int IsDrive( char * input)
+{
+ unsigned short driveNum = 0, n;
+
+ if (isalpha(input[0]) && (input[1] == ':') && (input[2] == 0))
+ driveNum = toupper(input[0]) - 'A' + 1;
+ else if (input[0] == '*' && isdigit (input[1]) && input[1] != '0')
+ {
+ if (isdigit (input[2]) && input[3] == ':' && input[4] == 0)
+ n = (input[1]-'0')*10+(input[2]-'0');
+ else if (input[2] == ':' && input[3] == 0)
+ n = input[1]-'0';
+
+ if (n)
+ {
+ GetFirstDrive (&driveNum);
+ driveNum += (n-1);
+ if (driveNum < 1 || driveNum > 26)
+ driveNum = 0;
+ }
+ }
+
+ return driveNum;
+}
+
+/*
+ If the input is "Sn:", return n, where n > 0 && n <= 16.
+ Otherwise return 0.
+ */
+int GetSearchNumber( char * input)
+{
+ int searchNumber = 0;
+ char *lpTemp;
+
+ if (input[0] != 'S')
+ return(0);
+
+ lpTemp = input+1;
+ while (*lpTemp && isalpha(*lpTemp))
+ lpTemp++;
+
+ if (strncmp (input, "SEARCH", lpTemp-input))
+ return(0);
+
+ if ((lpTemp[0] > '0') &&
+ (lpTemp[0] <= '9'))
+ {
+ if ((lpTemp[1] == ':') &&
+ (lpTemp[2] == 0))
+ {
+ searchNumber = lpTemp[0] - '0';
+ }
+ else if ((lpTemp[0] == '1') &&
+ (lpTemp[1] >= '0') &&
+ (lpTemp[1] <= '6') &&
+ (lpTemp[2] == ':') &&
+ (lpTemp[3] == 0))
+ {
+ searchNumber = 10 + lpTemp[1] - '0';
+ }
+ }
+
+ return(searchNumber);
+}
+
+/*
+ Return TRUE if the drive is a NetWare drive.
+ FALSE otherwise.
+ */
+int IsNetwareDrive (int driveNum)
+{
+ unsigned int iRet=0;
+ WORD status;
+
+ if (iRet = GetDriveStatus ((unsigned short)driveNum,
+ NETWARE_FORMAT_SERVER_VOLUME,
+ &status,
+ NULL,
+ NULL,
+ NULL,
+ NULL))
+ {
+ return FALSE;
+ }
+
+ return (status & NETWARE_NETWARE_DRIVE);
+}
+
+/*
+ Return TRUE if the drive is a local drive.
+ FALSE otherwise.
+ */
+int IsLocalDrive (int driveNum)
+{
+ unsigned int iRet=0;
+ WORD status;
+
+ if (iRet = GetDriveStatus ((unsigned short)driveNum,
+ NETWARE_FORMAT_SERVER_VOLUME,
+ &status,
+ NULL,
+ NULL,
+ NULL,
+ NULL))
+ {
+ return FALSE;
+ }
+
+ return ((status & NETWARE_LOCAL_DRIVE) && !(status & NETWARE_NETWORK_DRIVE));
+}
+
+/*
+ Return TRUE if the drive is a network drive that is not netware
+ FALSE otherwise.
+ */
+int IsNonNetwareNetworkDrive (int driveNum)
+{
+ unsigned int iRet=0;
+ WORD status;
+
+ if (iRet = GetDriveStatus ((unsigned short)driveNum,
+ NETWARE_FORMAT_SERVER_VOLUME,
+ &status,
+ NULL,
+ NULL,
+ NULL,
+ NULL))
+ {
+ return FALSE;
+ }
+
+ return ((status & NETWARE_NETWORK_DRIVE) && !(status & NETWARE_NETWARE_DRIVE));
+}
+
+/*
+ Return the drive number of search drive n.
+ Return 0 if search drive n does not exist.
+ */
+int GetDriveFromSearchNumber (int searchNumber)
+{
+ char *path;
+ int i;
+
+ path = getenv("PATH");
+
+ for (i = 1; i < searchNumber; i++)
+ {
+ path =strchr (path, ';');
+
+ if (path == NULL || *(path+1) == 0)
+ return(0);
+
+ path++;
+ }
+
+ return(toupper(*path) - 'A' + 1);
+}
+
+/*
+ Display a specific drive's mapping.
+ */
+void DisplayDriveMapping(WORD drive)
+{
+ unsigned int iRet = 0;
+ WORD status = 0;
+ char rootPath[MAX_PATH_LEN], relativePath[MAX_PATH_LEN];
+
+ iRet = GetDriveStatus (drive,
+ NETWARE_FORMAT_SERVER_VOLUME,
+ &status,
+ NULL,
+ rootPath,
+ relativePath,
+ NULL);
+ if (iRet)
+ {
+ DisplayError (iRet, "GetDriveStatus");
+ return;
+ }
+
+ if (status & NETWARE_NETWARE_DRIVE)
+ DisplayMessage(IDR_NETWARE_DRIVE, 'A'+drive-1, rootPath, relativePath);
+ else if ((status & NETWARE_NETWORK_DRIVE) || (status & NETWARE_LOCAL_DRIVE))
+ DisplayMessage(IDR_LOCAL_DRIVE, 'A'+drive-1);
+ else
+ DisplayMessage(IDR_UNDEFINED, 'A'+drive-1);
+}
+
+/*
+ Display a specific search drive's mapping.
+ */
+void DisplaySearchDriveMapping(int searchNumber)
+{
+ unsigned int iRet = 0;
+ char *p, *searchPath;
+ int i;
+ WORD status;
+ char path[MAX_PATH_LEN], rootPath[MAX_PATH_LEN], relativePath[MAX_PATH_LEN];
+
+ searchPath = NWGetPath();
+
+ for (i = 0; i < searchNumber-1; i++)
+ {
+ searchPath = strchr (searchPath, ';');
+ if (searchPath != NULL)
+ searchPath++;
+ else
+ return;
+ }
+
+ p = strchr (searchPath, ';');
+ if (p != NULL)
+ {
+ i= p-searchPath;
+ strncpy (path, searchPath, i);
+ path[i] = 0;
+ }
+ else
+ strcpy (path, searchPath);
+
+ if (isalpha(*path) && *(path+1) == ':')
+ {
+ iRet = GetDriveStatus ((unsigned short)(toupper(*path)-'A'+1),
+ NETWARE_FORMAT_SERVER_VOLUME,
+ &status,
+ NULL,
+ rootPath,
+ relativePath,
+ NULL);
+
+ if (iRet)
+ {
+ DisplayError (iRet, "GetDriveStatus");
+ return;
+ }
+ else
+ {
+ if (status & NETWARE_NETWARE_DRIVE)
+ DisplayMessage(IDR_NETWARE_SEARCH, searchNumber, path, rootPath, relativePath);
+ else
+ DisplayMessage(IDR_LOCAL_SEARCH, searchNumber, path);
+ }
+ }
+ else
+ DisplayMessage(IDR_LOCAL_SEARCH, searchNumber, path);
+}
+
+/*
+ Return TRUE if the mapPath is parsed, FALSE otherwise.
+ */
+int ParseMapPath(char * mapPath, char * volName, char * dirPath, char * serverName, int fMapErrorsOn, char * lpCommand)
+{
+ unsigned int iRet=0;
+ char *pColon, inputPath[MAX_PATH_LEN];
+ int drive, nDriveNum;
+
+ // fix g:=:sys:\public case.
+ if (*mapPath == ':')
+ mapPath++;
+
+ if (strlen (mapPath) > MAX_INPUT_PATH_LEN)
+ {
+ if (fMapErrorsOn)
+ DisplayMessage(IDR_INVALID_PATH, mapPath);
+ return FALSE;
+ }
+
+ // Get the drive or volume part if there is one.
+ if (pColon = strchr (mapPath, ':'))
+ {
+ char *directory = pColon+1;
+ int searchNumber;
+
+ // Assing drive: part to input.
+ strncpy (inputPath, mapPath, directory-mapPath);
+ inputPath[directory-mapPath] = 0;
+
+ if (nDriveNum = IsDrive (inputPath))
+ {
+ if (*inputPath == '*')
+ {
+ *inputPath = 'A' + nDriveNum - 1;
+ *(inputPath+1) = ':';
+ *(inputPath+2) = 0;
+ }
+ else if (!IsNetwareDrive(nDriveNum))
+ {
+ if (fMapErrorsOn)
+ DisplayMessage(IDR_NOT_NETWORK_DRIVE);
+ return(FALSE);
+ }
+ }
+ else if (searchNumber = GetSearchNumber(inputPath))
+ {
+ int drive = GetDriveFromSearchNumber (searchNumber);
+
+ if (!drive)
+ {
+ if (fMapErrorsOn)
+ DisplayMessage(IDR_SEARCH_DRIVE_NOT_EXIST, searchNumber);
+ return FALSE;
+ }
+
+ if (!IsNetwareDrive(drive))
+ {
+ if (fMapErrorsOn)
+ DisplayMessage(IDR_NOT_NETWORK_DRIVE);
+ return(FALSE);
+ }
+
+ inputPath[0] = 'A' + drive - 1;
+ inputPath[1] = ':';
+ inputPath[2] = 0;
+ }
+
+ strcat (inputPath, directory);
+ }
+ else
+ {
+ if ( fNDS )
+ {
+ CHAR fullname[MAX_PATH];
+ if ( !NDSCanonicalizeName( mapPath, fullname, MAX_PATH, TRUE ) )
+ if ( !ConverNDSPathToNetWarePathA( fullname, DSCL_DIRECTORY_MAP,
+ inputPath ) )
+ goto ParseThePath;
+ }
+
+ // If drive is not specified, the current drive is used.
+ drive = _getdrive();
+ if (!IsNetwareDrive(drive))
+ {
+ if (fMapErrorsOn)
+ DisplayMessage(IDR_NOT_NETWORK_DRIVE);
+ return(FALSE);
+ }
+
+ inputPath[0] = 'A'+drive-1;
+ inputPath[1] = ':';
+ inputPath[2] = 0;
+
+ strcat (inputPath, mapPath);
+ }
+
+ParseThePath:
+
+ iRet = ParsePath (inputPath,
+ serverName,
+ volName,
+ dirPath);
+ if (iRet)
+ {
+ if (iRet == 0x880F)
+ {
+ DisplayMessage(IDR_MAP_NOT_ATTACHED_SERVER, lpCommand);
+ return(FALSE);
+ }
+ else
+ {
+ if (fMapErrorsOn)
+ DisplayMessage(IDR_INVALID_PATH, inputPath);
+ return(FALSE);
+ }
+ }
+
+ return(TRUE);
+}
+
+/*
+ Map drive to mapPath
+ */
+int MapDrive (int drive, int searchNum, char * mapPath, int bRoot, int bInsert, int fMapErrorsOn, char *lpCommand)
+{
+ unsigned int iRet=0;
+ char volName[MAX_VOLUME_LEN+1]; //+1 for append ':'.
+ char dirPath[MAX_DIR_PATH_LEN];
+ int currentDrive;
+ int OvermapDrive = -1;
+ char serverName[MAX_NAME_LEN];
+
+ if (!ParseMapPath(mapPath, volName, dirPath, serverName, fMapErrorsOn, lpCommand))
+ return(3);
+
+ if (IsNetwareDrive(drive))
+ {
+ if ( drive == _getdrive() ) {
+ OvermapDrive = drive;
+ _chdrive (SafeDisk);
+ }
+ if (iRet = DeleteDriveBase ((unsigned short)drive))
+ {
+ if (fMapErrorsOn) {
+ /* Cannot delete the drive you are on */
+ if (iRet == ERROR_DEVICE_IN_USE)
+ DisplayMessage(IDR_CAN_NOT_CHANGE_DRIVE);
+ else
+ DisplayError (iRet, "DeleteDriveBase");
+ }
+ return iRet;
+ }
+ }
+ else if ( IsNonNetwareNetworkDrive(drive) ) {
+ if (fMapErrorsOn)
+ DisplayMessage(IDR_NON_NETWARE_NETWORK_DRIVE, lpCommand);
+ return 3;
+ }
+
+ if (bRoot)
+ {
+ // +2 is for strcat with ":".
+ char *fullPath = malloc( MAX_VOLUME_LEN + strlen (dirPath) + 2);
+ if (fullPath == NULL)
+ {
+ if (fMapErrorsOn)
+ DisplayMessage(IDR_NOT_ENOUGH_MEMORY);
+ return 8;
+ }
+
+ strcpy (fullPath, volName);
+ strcat (fullPath, ":");
+ strcat (fullPath, dirPath);
+
+ iRet = SetDriveBase ((unsigned short)drive,
+ serverName,
+ 0,
+ fullPath);
+
+ // Relative names need to be expanded for the redirector
+
+ if ( iRet && fNDS && ( volName[strlen(volName) - 1] == '.' ) )
+ {
+ char canonName[MAX_VOLUME_LEN+1];
+ if ( !NDSCanonicalizeName( volName, canonName, MAX_VOLUME_LEN, TRUE ) )
+ {
+ strcpy (fullPath, canonName);
+ strcat (fullPath, ":");
+ strcat (fullPath, dirPath);
+
+ iRet = SetDriveBase ((unsigned short)drive,
+ serverName,
+ 0,
+ fullPath);
+ }
+ }
+
+ if (iRet == 0)
+ {
+ if (searchNum)
+ searchNum = InsertSearchDrive(searchNum, drive, bInsert, NULL);
+
+ currentDrive = _getdrive();
+ _chdrive (drive);
+ _chdir( "\\" );
+ _chdrive (currentDrive);
+ ExportCurrentDirectory( drive );
+
+ if (fMapDisplayOn)
+ {
+ if (searchNum)
+ DisplaySearchDriveMapping (searchNum);
+ else
+ DisplayDriveMapping((unsigned short)drive);
+ }
+ }
+ else
+ {
+ if (fMapErrorsOn)
+ {
+ switch ( iRet )
+ {
+ case ERROR_DEVICE_IN_USE:
+ DisplayMessage(IDR_CAN_NOT_CHANGE_DRIVE);
+ break;
+ case ERROR_BAD_NETPATH:
+ case ERROR_BAD_NET_NAME:
+ DisplayMessage(IDR_VOLUME_NOT_EXIST, volName);
+ iRet = 3;
+ break;
+ case ERROR_EXTENDED_ERROR:
+ NTPrintExtendedError();
+ iRet = 3;
+ break;
+ default:
+ DisplayMessage(IDR_MAP_ERROR, iRet);
+ DisplayMessage(IDR_MAP_FAILED, lpCommand);
+ iRet = 3;
+ break;
+ }
+ }
+ }
+
+ free (fullPath);
+ }
+ else
+ {
+ // NETX requires to end the volName with ':'.
+ strcat (volName, ":");
+
+ iRet = SetDriveBase ((unsigned short)drive,
+ serverName,
+ 0,
+ volName);
+
+ // Relative names need to be expanded for the redirector
+
+ if ( iRet && fNDS && ( volName[strlen(volName) - 2] == '.' ) )
+ {
+ char canonName[MAX_VOLUME_LEN+1];
+
+ volName[strlen(volName)-1] = '\0';
+ if ( !NDSCanonicalizeName( volName, canonName, MAX_VOLUME_LEN, TRUE ) )
+ {
+ strcat (canonName, ":");
+
+ iRet = SetDriveBase ((unsigned short)drive,
+ serverName,
+ 0,
+ canonName);
+ }
+ }
+
+ if (iRet)
+ {
+ if (fMapErrorsOn)
+ {
+ switch ( iRet )
+ {
+ case ERROR_DEVICE_IN_USE:
+ DisplayMessage(IDR_CAN_NOT_CHANGE_DRIVE);
+ break;
+ case ERROR_EXTENDED_ERROR:
+ NTPrintExtendedError();
+ iRet = 3;
+ break;
+ case ERROR_BAD_NETPATH:
+ case ERROR_BAD_NET_NAME:
+ default:
+ DisplayMessage(IDR_MAP_INVALID_PATH, lpCommand);
+ iRet = 3;
+ break;
+ }
+ }
+ }
+ else
+ {
+ // Succeed.
+
+ if (searchNum)
+ searchNum = InsertSearchDrive(searchNum, drive, bInsert, NULL);
+
+ currentDrive = _getdrive();
+ _chdrive (drive);
+ if (!iRet && *dirPath)
+ {
+ iRet = _chdir( "\\" );
+ if ( !iRet )
+ iRet = _chdir (dirPath);
+ if ( iRet ) {
+ if (fMapErrorsOn)
+ {
+ DisplayMessage(IDR_MAP_INVALID_PATH, lpCommand);
+ }
+
+ iRet = 3;
+ }
+
+ }
+ else
+ {
+ _chdir( "\\" );
+ }
+ _chdrive (currentDrive);
+ ExportCurrentDirectory( drive );
+
+ if (iRet == 0 && fMapDisplayOn)
+ {
+ if (searchNum)
+ DisplaySearchDriveMapping (searchNum);
+ else
+ DisplayDriveMapping((unsigned short)drive);
+ }
+
+ }
+ }
+
+ if ( OvermapDrive != -1 )
+ _chdrive (OvermapDrive);
+
+ return(iRet);
+}
+
+/*
+ Map drive secified by driveNum to mapPath.
+ If bRoot is TRUE, use mapPath as the drive base.
+ */
+int MapNonSearchDrive (int driveNum, char *mapPath, int bRoot, int fMapDisplayOn, int fMapErrorsOn, char *lpCommand)
+{
+ int driveLetter, iRet = 0;
+
+ if ((driveNum == 0) && (!strchr(mapPath, ':') && !bRoot))
+ {
+ // map current drive to different directory.
+ if (_chdir(mapPath) && fMapErrorsOn)
+ {
+ DisplayMessage(IDR_DIRECTORY_NOT_FOUND, mapPath);
+ iRet = 3;
+ }
+ else {
+ ExportCurrentDirectory( _getdrive() );
+ if (fMapDisplayOn)
+ DisplayDriveMapping((unsigned short)driveNum);
+ }
+ return(iRet);
+ }
+ else if ( (driveNum) && (isalpha(mapPath[0]) && (mapPath[1] == ':')))
+ {
+ int mapdriveNum = toupper(mapPath[0]) - 'A' + 1;
+
+ if ( driveNum == mapdriveNum )
+ {
+ // map drive to different directory.
+ // map k:=k:\dir
+
+ WORD currentDrive;
+ currentDrive = _getdrive();
+ _chdrive (driveNum);
+ if (_chdir(mapPath) && fMapErrorsOn)
+ {
+ DisplayMessage(IDR_DIRECTORY_NOT_FOUND, mapPath);
+ iRet = 3;
+ }
+ else
+ {
+ ExportCurrentDirectory( _getdrive() );
+ if (fMapDisplayOn)
+ DisplayDriveMapping((unsigned short)driveNum);
+ }
+ _chdrive (currentDrive);
+ return(iRet);
+ }
+ }
+
+ if (driveNum == 0)
+ driveNum = _getdrive();
+
+ driveLetter = 'A' + driveNum -1;
+
+ return MapDrive (driveNum, 0, mapPath, bRoot, 0, fMapErrorsOn, lpCommand);
+}
+
+/*
+ Map the last free drive to mapPath and put it in the search path.
+ If bInsert is TRUE, don't replace search drive n, otherwise,
+ replace.
+ If bRoot is TRUE, use mapPath as the drive base.
+ */
+int MapSearchDrive (int searchNum, int driveNum, char *mapPath, int bInsert, int bRoot, int fMapDisplayOn, int fMapErrorsOn, char *lpCommand)
+{
+ unsigned int iRet=0;
+ int i;
+ WORD status;
+ char * lpEqual;
+
+ /*
+ * Handle syntax map s2:=w:=volume:
+ * Handle syntax map w:=s2:=volume:
+ */
+ if ( driveNum )
+ {
+ return MapDrive (driveNum, searchNum, mapPath, bRoot, bInsert, fMapErrorsOn, lpCommand);
+ }
+
+ // Check if mapPath is local path.
+ if (mapPath[1] == ':' &&
+ IsLocalDrive (toupper(mapPath[0])-'A'+1))
+ {
+ i = 0; // a bug?
+ searchNum = InsertSearchDrive(searchNum, i, bInsert, mapPath);
+ if ((searchNum != 0) && fMapDisplayOn)
+ DisplayMessage(IDR_LOCAL_SEARCH, searchNum, mapPath);
+ return 0;
+ }
+
+ // Try to find the last available drive.
+ for (i = 26; i >= 1; i--)
+ {
+ iRet = GetDriveStatus ((unsigned short)i,
+ NETWARE_FORMAT_SERVER_VOLUME,
+ &status,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ if (iRet)
+ continue;
+
+ if (!(status & NETWARE_LOCAL_DRIVE) &&
+ !(status & NETWARE_NETWORK_DRIVE))
+ {
+ // Found. Map it to the path.
+ return MapDrive (i, searchNum, mapPath, bRoot, bInsert, fMapErrorsOn, lpCommand);
+ }
+ }
+
+ if (fMapErrorsOn)
+ DisplayMessage (IDR_NO_DRIVE_AVAIL);
+ return(0);
+}
+
+/*
+ Map the next available drive to the mapPath.
+ */
+int MapNextAvailableDrive (char *mapPath, int fMapDisplayOn, int fMapErrorsOn, char *lpCommand)
+{
+ unsigned int iRet = 0;
+ int i;
+ WORD status;
+
+ // Find a free drive that is not mapped.
+ // Then map it to the mapPath.
+ for (i = 1; i <= 26; i++)
+ {
+ iRet = GetDriveStatus ((unsigned short)i,
+ NETWARE_FORMAT_SERVER_VOLUME,
+ &status,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ if (iRet)
+ {
+ if (fMapErrorsOn)
+ DisplayError (iRet, "GetDriveStatus");
+ return iRet;
+ }
+
+ if (!(status & NETWARE_LOCAL_DRIVE) &&
+ !(status & NETWARE_NETWORK_DRIVE))
+ {
+ iRet = MapNonSearchDrive (i, mapPath, FALSE, fMapDisplayOn, fMapErrorsOn, lpCommand);
+ return iRet;
+ }
+ }
+
+ if (fMapErrorsOn)
+ DisplayMessage(IDR_NO_DRIVE_AVAIL);
+
+ return(0);
+}
+
+/*
+ Remove a drive mapping.
+ */
+int RemoveDrive (WORD drive, int fMapDisplayOn, int fMapErrorsOn)
+{
+ unsigned int iRet=0;
+ int searchNum;
+
+ if (IsNetwareDrive (drive))
+ {
+ if (searchNum = IsSearchDrive(drive))
+ {
+ RemoveSearchDrive (searchNum, fMapDisplayOn, fMapErrorsOn);
+ }
+ else
+ {
+ /*
+ * Can't delete current drive on NT
+ */
+ if ( drive == _getdrive() ) {
+ _chdrive (SafeDisk);
+ }
+ if (iRet = DeleteDriveBase (drive))
+ {
+ if (fMapErrorsOn)
+ DisplayError (iRet, "DeleteDriveBase");
+ }
+ else
+ {
+ if (fMapDisplayOn)
+ DisplayMessage(IDR_DEL_DRIVE, 'A'+drive-1);
+ }
+ }
+ }
+ else
+ {
+ if (fMapErrorsOn)
+ DisplayMessage(IDR_WRONG_DRIVE, 'A'+drive-1);
+
+ return(50); //error level.
+ }
+
+ return(0);
+}
+
+/*
+ Remove a search drive.
+ */
+void RemoveSearchDrive (int searchNumber, int fMapDisplayOn, int fMapErrorsOn)
+{
+ WORD drive;
+
+ // Get the drive number.
+ drive = GetDriveFromSearchNumber (searchNumber);
+
+ if (!drive)
+ {
+ if (fMapErrorsOn)
+ DisplayMessage(IDR_SEARCH_DRIVE_NOT_EXIST, searchNumber);
+ return;
+ }
+
+ // If the drive is a netware drive, remove the drive mapping.
+ if (IsNetwareDrive (drive))
+ {
+ unsigned int iRet=0;
+ /*
+ * Can't delete current drive on NT
+ */
+ if ( drive == _getdrive() ) {
+ _chdrive (SafeDisk);
+ }
+ if (iRet = DeleteDriveBase (drive))
+ {
+ if (fMapErrorsOn)
+ DisplayError (iRet, "DeleteDriveBase");
+ return;
+ }
+ }
+
+ RemoveDriveFromPath (searchNumber, fMapErrorsOn);
+
+ if (fMapDisplayOn)
+ DisplayMessage(IDR_DEL_SEARCH_DRIVE, 'A'+drive-1);
+
+ // If the drive is not a local drive, remove all reference
+ // to the drive in the path.
+ if (!IsLocalDrive (drive))
+ {
+ while (searchNumber = IsSearchDrive (drive))
+ {
+ RemoveDriveFromPath (searchNumber, fMapErrorsOn);
+ }
+ }
+}
+
+/*
+ Remove a search drive from the path.
+ */
+void RemoveDriveFromPath(int searchNumber, int fMapErrorsOn)
+{
+ char *pOldPath, *pNewPath, *restEnvSeg, *pPath, *Path;
+ int i, n;
+
+ // Move pOldPath to where we want to put the new path string.
+ pOldPath = NWGetPath();
+ pPath = malloc( strlen(pOldPath) + 5 + 1 + 1 );
+ strcpy(pPath, "PATH=");
+ strcat(pPath, pOldPath);
+ pOldPath = pPath + 5;
+
+ for (i = 1; i < searchNumber; i++)
+ {
+ pOldPath=strchr (pOldPath, ';');
+
+ if (pOldPath == NULL)
+ {
+ if (fMapErrorsOn)
+ DisplayMessage(IDR_SEARCH_DRIVE_NOT_EXIST, searchNumber);
+ free( pPath );
+ return;
+ }
+
+ pOldPath++;
+ }
+
+ // Move pNewPath to the beginning of the path string that
+ // needs to be moved.
+ if (pNewPath = strchr (pOldPath, ';'))
+ pNewPath++ ;
+ else
+ pNewPath = pOldPath + strlen (pOldPath);
+
+ // Calculate the number of characters needs to be moved.
+ n = strlen (pNewPath) + 1;
+ restEnvSeg = pNewPath + n;
+
+ n++;
+
+ // Move the path string to overwrite the search drive.
+ memmove (pOldPath, pNewPath, n);
+
+ Path = malloc (strlen (pPath)+1);
+ strcpy (Path, pPath);
+ _putenv (Path);
+ ExportEnv( pPath );
+ free( pPath );
+}
+
+
+/*
+ If bInsert is TRUE, insert dirve specified by driveNum as search
+ drive specified by searchNum. Otherwise replace search drive
+ specified by searchNum with drive specified by driveNum.
+ */
+int InsertSearchDrive(int searchNum, int driveNum, int bInsert, char * insertPath)
+{
+ char *pOldPath, *pNewPath, *restEnvSeg, *pPath, *Path;
+ int i, n = 0, bSemiColon, nInsertChar;
+
+ nInsertChar = (insertPath == NULL)? 3 : strlen (insertPath);
+
+ // Check if memory block is large enough.
+ if (!MemorySegmentLargeEnough (nInsertChar+1))
+ return 0;
+
+ // Move pNewPath to where we put the new drive.
+ pNewPath = NWGetPath();
+ pPath = malloc( strlen(pNewPath) + 5 + 1 + nInsertChar + 1 + 1 );
+ strcpy(pPath, "PATH=");
+ strcat(pPath, pNewPath);
+ pNewPath = pPath + 5;
+
+ for (i = 1; i < searchNum; i++)
+ {
+ if (strchr (pNewPath, ';'))
+ {
+ pNewPath = strchr (pNewPath, ';');
+ }
+ else
+ {
+ pNewPath += strlen (pNewPath);
+ bInsert = TRUE;
+ i++;
+ break;
+ }
+
+ pNewPath++;
+ }
+
+ // Move pOldPath to the begining of the path string that needs
+ // to be moved.
+ if (bInsert)
+ pOldPath = pNewPath;
+ else
+ {
+ if ((pOldPath = strchr (pNewPath, ';')) == NULL)
+ pOldPath = pNewPath + strlen (pNewPath);
+ else
+ pOldPath++;
+ }
+
+ // Figure out the number of characters that need to be moved.
+ n = strlen (pOldPath) + 1;
+ restEnvSeg = pOldPath + strlen (pOldPath) + 1;
+
+ n++;
+
+ // If we insert a new drive to the end of the path which ends with
+ // a ';', or if we replace the last search drive, no ';' is needed.
+ bSemiColon = bInsert ? (*(pNewPath-1) != ';' || *pOldPath != 0)
+ : (*pOldPath != 0);
+
+ // Move the old path so that we will have space for the new search drive.
+ memmove (pNewPath + (bSemiColon? nInsertChar+1:nInsertChar), pOldPath, n);
+
+ if ((*pNewPath == 0)&& bSemiColon)
+ {
+ // Insert as the last one to the path.
+ // Put ';' at the begining.
+ *pNewPath = ';';
+ if (insertPath == NULL)
+ {
+ *(pNewPath+1) = 'A' + driveNum - 1;
+ *(pNewPath+2) = ':';
+ *(pNewPath+3) = '.';
+ }
+ else
+ memcpy (pNewPath+1, insertPath, nInsertChar);
+ }
+ else
+ {
+ if (insertPath == NULL)
+ {
+ *pNewPath = 'A' + driveNum - 1;
+ *(pNewPath+1) = ':';
+ *(pNewPath+2) = '.';
+ }
+ else
+ memcpy (pNewPath, insertPath, nInsertChar);
+ if (bSemiColon)
+ *(pNewPath+nInsertChar) = ';';
+ }
+
+ Path = malloc (strlen (pPath)+1);
+ strcpy (Path, pPath);
+ _putenv (Path);
+ ExportEnv( pPath );
+ free( pPath );
+
+ return (i);
+}
+
+/*
+ * Used by SetEnv().
+ * Return the number of bytes of environment variable pointed by lpRest
+ */
+int GetRestEnvLen (char *lpRest)
+{
+ int nTotal = 1;
+ nTotal += strlen (lpRest);
+
+ return(nTotal);
+}
diff --git a/private/nw/nwscript/nwscript.c b/private/nw/nwscript/nwscript.c
new file mode 100644
index 000000000..f56e23ff0
--- /dev/null
+++ b/private/nw/nwscript/nwscript.c
@@ -0,0 +1,84 @@
+/*************************************************************************
+*
+* NWSCRIPT.C
+*
+* This module is the NetWare Logon Script utility.
+*
+* Copyright (c) 1995 Microsoft Corporation
+*
+* $Log: N:\NT\PRIVATE\NW4\NWSCRIPT\VCS\NWSCRIPT.C $
+*
+* Rev 1.3 22 Jan 1996 16:48:32 terryt
+* Add automatic attach query during map
+*
+* Rev 1.2 22 Dec 1995 14:26:08 terryt
+* Add Microsoft headers
+*
+* Rev 1.1 20 Nov 1995 16:10:38 terryt
+* Close open NDS handles
+*
+* Rev 1.0 15 Nov 1995 18:07:42 terryt
+* Initial revision.
+*
+* Rev 1.1 23 May 1995 19:37:18 terryt
+* Spruce up source
+*
+* Rev 1.0 15 May 1995 19:10:58 terryt
+* Initial revision.
+*
+*************************************************************************/
+
+#include <stdio.h>
+#include <windows.h>
+#include <stdlib.h>
+#include <time.h>
+#include <direct.h>
+#include <process.h>
+#include <string.h>
+#include <malloc.h>
+#include <nwapi.h>
+
+#include "nwscript.h"
+
+int NTNetWareLoginScripts( int argc, char ** argv );
+
+unsigned int fNDS = FALSE;
+
+/*************************************************************************
+*
+* main
+* Main function and entry point
+*
+* ENTRY:
+* argc (input)
+* count of the command line arguments.
+* argv (input)
+* vector of strings containing the command line arguments;
+* (not used due to always being ANSI strings).
+*
+* EXIT
+* (int) exit code: SUCCESS for success; FAILURE for error.
+*
+*************************************************************************/
+
+int _CRTAPI1
+main( int argc,
+ char **argv )
+{
+ //
+ // Call wksta to reset the sync login script flag if it did set it.
+ // This flag is set and reset everytime so that if nw login scripts
+ // are not used, user does not need wait.
+ // Ignore any errors.
+ //
+ (void) NwSetLogonScript(RESET_SYNC_LOGONSCRIPT) ;
+
+ (void)NTNetWareLoginScripts( argc, argv );
+
+ CleanupExit( 0 );
+
+ return 0;
+
+} /* main() */
+
+
diff --git a/private/nw/nwscript/nwscript.rc b/private/nw/nwscript/nwscript.rc
new file mode 100644
index 000000000..96c646176
--- /dev/null
+++ b/private/nw/nwscript/nwscript.rc
@@ -0,0 +1,243 @@
+#ifdef NOMINMAX
+#undef NOMINMAX
+#endif
+
+#include <winver.h>
+#include <ntverp.h>
+#include "nwscript.h"
+
+#define VER_FILETYPE VFT_APP
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "NetWare Logon Script Utility"
+#define VER_INTERNALNAME_STR "nwscript"
+#define VER_ORIGINALFILENAME_STR "nwscript.exe"
+
+// #define VER_LEGALCOPYRIGHT_STR VER_LEGALCOPYRIGHT_STR
+#undef VER_ADDITIONALCOPYRIGHT_STR
+
+#include "common.ver"
+
+STRINGTABLE
+BEGIN
+ IDR_ERROR
+ "Error: unexpected error 0x%x during %S\n"
+ IDR_NO_DEFAULT_CONNECTION
+ "Error: Unable to get default connection.\n\a"
+ IDR_NO_KNOWN_FILE_SERVER
+ "No known file server.\n"
+ IDR_LOCAL_DRIVE
+ "Drive %C: maps to a local disk.\n"
+ IDR_NETWARE_DRIVE
+ "Drive %C: = %S \\%S\n"
+ IDR_DASHED_LINE
+ " ----- Search Drives -----\n"
+ IDR_LOCAL_SEARCH
+ "S%d: = %S\n"
+ IDR_NETWARE_SEARCH
+ "S%d: = %S [%S \\%S]\n"
+ IDR_NOT_ENOUGH_MEMORY
+ "Error: Not enough memory.\n"
+ IDR_NO_RESPONSE
+ "No response from file server %S.\n"
+ IDR_PASSWORD
+ "Enter the password for user %S on server %S: "
+ IDR_ATTACHED
+ "You are attached to server %S.\n\n"
+ IDR_ACCESS_DENIED
+ "Access denied.\n"
+ IDR_UNAUTHORIZED_LOGIN_TIME
+ "Attempt to login during an unauthorized time period.\nThe supervisor has limited the times that you can login to this server.\n"
+ IDR_LOGIN_DENIED_NO_CONNECTION
+ "Attempting to simultaneously login to too many work stations.\nThe supervisor has limited the number of active connections you may have.\n"
+ IDR_UNAUTHORIZED_LOGIN_STATION
+ "Attempting to login from an unapproved station.\nThe supervisor has limited the stations that you are allowed to login on.\n"
+ IDR_ACCOUNT_DISABLED
+ "This account has expired or been disabled by the supervisor.\n"
+ IDR_PASSWORD_EXPRIED_NO_GRACE
+ "Password has expired and all grace logins have been used.\n"
+ IDR_MAP_NOT_ATTACHED_SERVER
+ "Attempt to map drive to server to which you are not currently attached.\nThe map command was ""%S"".\n"
+ IDR_MAP_USAGE
+ "Microsoft (R) MAP Utility\nCopyright (C) Microsoft Corporation 1994. All rights reserved.\n\n View current mapping.\n\tMAP [drive:]\nCreate or change network drive mappings\n\tMAP path\n\tMAP drive:=[drive:|path]\n\tMAP [DEL[ete] | REM[ove]] drive:\n Create or change search drive mappings\n\tMAP [INS[ert]] drive:=[drive:|path]\nMap a drive to a fake root directory\n\tMAP ROOT drive:=[drive:|path]\nMap the next available drive\n\tMAP N[ext] [drive:|path]\n"
+ IDR_UNDEFINED
+ "Drive %C: is not defined.\n"
+ IDR_DIRECTORY_NOT_FOUND
+ "Directory ""%S"" is not locatable.\n"
+ IDR_VOLUME_NOT_EXIST
+ "Volume ""%S"" does not exist.\n"
+ IDR_WRONG_DRIVE
+ "Attempted operation on invalid drive %C:\n"
+ IDR_DEL_DRIVE
+ "Mapping for drive %C: has been deleted.\n"
+ IDR_DEL_SEARCH_DRIVE
+ "The search mapping for drive %C: has been deleted.\n"
+ IDR_SEARCH_DRIVE_NOT_EXIST
+ "Search%d: does not exist.\n"
+ IDR_NOT_NETWORK_DRIVE
+ "Attempt to map network drive to unmapped drive or local drive.\n"
+ IDR_NO_DRIVE_AVAIL
+ "All Drives are in use.\n"
+ IDR_INVALID_PATH
+ """%S"" Invalid path.\n"
+ IDR_CAN_NOT_CHANGE_DRIVE
+ "Can not change the drive mapping.\n"
+ IDR_MAP_INVALID_PATH
+ "Attempt to map drive to invalid path in map command ""%S"".\n"
+ IDR_MAP_FAILED
+ "Following drive mapping operation could not be completed.\r\n ""%S""\r\n"
+ IDR_NO_SCRIPT_FILE
+ "Cannot open this script file: %S\n"
+ IDR_STRIKE_KEY
+ "Strike any key when ready . . ."
+ IDR_CANNOT_EXECUTE
+ "Could not execute external program ""%S"".\n"
+ IDR_ENOENT
+ "Could not execute external program ""%S"", program not found.\n"
+ IDR_EXIT_NOT_SUPPORTED
+ "The EXIT command followed by a string is not supported on this machine.\n"
+ IDR_IF_TOO_DEEP
+ "Script Error: ""IF"" statements nested too deeply. The nesting limit is 9 levels.\n"
+ IDR_SCRIPT_ERROR
+ "Script Error: Could not interpret line.\n"
+ IDR_ORIGINAL_LINE_WAS
+ "The original line was:\r\n%S\n\n"
+ IDR_BAD_COMMAND
+ "Bad command or file name.\n"
+ IDR_LABEL_NOT_FOUND
+ "Could not find label ""%S"".\n"
+ IDR_NO_VOLUME
+ "Error: No volume found.\n"
+ IDR_ERROR_DURING
+ "Error: unexpected error during %S\n"
+ IDR_MAP_ERROR
+ "%x\n"
+ IDR_ENTER_SERVER_NAME
+ "Enter the server name: "
+ IDR_ENTER_LOGIN_NAME
+ "Enter login name for server %S: "
+ IDR_ERROR_SET_DEFAULT_DRIVE
+ "Could not set the default drive to drive %C:.\n\n"
+ IDR_ERROR_OPEN_SCRIPT
+ "Cannot open this script file: %S\n"
+ IDR_DIVIDE_BY_ZERO
+ "Attempted to divide by zero\n"
+ IDR_NEWLINE
+ "\n"
+ IDR_SERVER_USER
+ "%S/%S: "
+ IDR_NON_NETWARE_NETWORK_DRIVE
+ "Following drive mapping operation was attempted on a non-NetWare network drive.\r\n ""%S""\r\n"
+ IDR_CAPTURE_USAGE
+ "Microsoft (R) CAPTURE Utility\nCopyright (C) Microsoft Corporation 1994. All rights reserved.\n\n USAGE: CAPTURE /SHow /Server=fileserver /Queue=queuename /Local=n\n /Form=form or n /CReate=path /Copies=n (1-255) /TImeout=n /Keep /Tabs=n (1-18) /No Tabs /Banner=bannername /NAMe=name /No Banner /FormFeed /No FormFeed\n /AUtoendcap /No Autoend /NOTIfy /No NOTIfy\n"
+
+ IDR_NOT_ACTIVE
+ "\nLPT%d: Capturing Is Not Currently Active.\n"
+ IDR_LPT_STATUS
+ "\n LPT%d: Capturing data to server %S queue %S.\n %s\n Capture Defaults:%-15sAutomatic Endcap:%s\n Banner :%-24SForm Feed :%s\n Copies :%-24dTabs :%s\n Form :%-24dTimeout Count :%s\n"
+ IDR_LPT_STATUS_NO_BANNER
+ "\n LPT%d: Capturing data to server %S queue %S.\n %s\n Capture Defaults:%-15sAutomatic Endcap:%s\n Banner :%-24sForm Feed :%s\n Copies :%-24dTabs :%s\n Form :%-24dTimeout Count :%s\n"
+ IDR_UNKNOW_FLAG
+ "An unknown flag %S was encountered.\n"
+ IDR_INVALID_LPT_NUMBER
+ "Local printer number (1, 2, or 3) expected.\n"
+ IDR_SERVER_NOT_FOUND
+ "Unable to attach to server %S.\n"
+ IDR_TIMEOUT_OUTOF_RANGE
+ "Timeout value (0-1000) expected.\n"
+ IDR_TABSIZE_OUTOF_RANGE
+ "Tab size must be 1 - 18.\n"
+ IDR_COPIES_OUTOF_RANGE
+ "Number of copies (1-255) expected.\n"
+ IDR_INVALID_BANNER
+ "%S is invalid banner.\n"
+ IDR_INVALID_FORM_NAME
+ "%S is invalid form name.\n"
+ IDR_INVALID_FORM_TYPE
+ "Form type (0-255) expected.\n"
+ IDR_SUCCESS_QUEUE
+ "Device LPT%d: re-routed to queue %S on server %S.\n"
+ IDR_NO_PRINTERS
+ "No default queue name can be found on server %S.\n"
+ IDR_QUEUE_NOT_EXIST
+ "Queue %S does not exist on server %S.\n"
+ IDR_TIME_OUT_EXPECTED
+ "Timeout value (0-1000) expected.\n"
+ IDR_LPT_NUMBER_EXPECTED
+ "Local printer number (1, 2, or 3) expected.\n"
+ IDR_FORM_EXPECTED
+ "Form number expected.\n"
+ IDR_COPIES_EXPECTED
+ "Number of copies (1-255) expected.\n"
+ IDR_TAB_SIZE_EXPECTED
+ "Tab size expected.\n"
+ IDR_JOB_NOT_FOUND
+ "%S is not a valid PrintCon job definition.\n"
+ IDR_FILE_CAPTURE_UNSUPPORTED
+ "File Capture is unsupported.\n"
+ IDR_DISABLED
+ "Disabled"
+ IDR_ENABLED
+ "Enabled"
+ IDR_YES
+ "Yes"
+ IDR_NO
+ "No";
+ IDR_SECONDS
+ "%d seconds";
+ IDR_CONVERT_TO_SPACE
+ "Converted to %d spaces";
+ IDR_NO_CONVERSION
+ "No conversion";
+ IDR_NOTIFY_USER
+ "User will be notified after the files are printed.";
+ IDR_NOT_NOTIFY_USER
+ "User will not be notified after the files are printed.";
+ IDR_NONE
+ "(None)";
+ IDR_CONNECTION_REFUSED
+ "Server refused the login; too many sessions.\n";
+ IDR_LASTLOGIN_PM
+ "Last login occurred at: %d-%02d-%02d %d:%02d:%02d pm.\n";
+ IDR_LASTLOGIN_AM
+ "Last login occurred at: %d-%02d-%02d %d:%02d:%02d am.\n";
+ IDR_ALL_LOCAL_DRIVES
+ "Drives %S map to a local disk.\n";
+ IDR_CHANGE_CONTEXT_ERROR
+ "The context you want to change to does not exist.\nYou tried to change to:\n[%S]\nYour context will be left unchanged.\n";
+ IDR_GET_CONTEXT_ERROR
+ "Error accessing the current context.\n";
+ IDR_DISPLAY_CONTEXT
+ "Your context has been changed to: %S\n";
+ IDR_LPT_STATUS_NDS
+ "\n LPT%d: Capturing data to print queue %S.\n %s\n Capture Defaults:%-15sAutomatic Endcap:%s\n Banner :%-24SForm Feed :%s\n Copies :%-24dTabs :%s\n Form :%-24dTimeout Count :%s\n"
+ IDR_LPT_STATUS_NO_BANNER_NDS
+ "\n LPT%d: Capturing data to print queue %S.\n %s\n Capture Defaults:%-15sAutomatic Endcap:%s\n Banner :%-24sForm Feed :%s\n Copies :%-24dTabs :%s\n Form :%-24dTimeout Count :%s\n"
+ IDR_NO_QUEUE
+ "No printer queue was specified\n";
+ IDR_LASTLOGIN
+ "Last login occurred at: %s %s.\n";
+ IDR_TREE_OPEN_FAILED
+ "Cannot open NDS tree\n";
+ IDR_NDS_CONTEXT_INVALID
+ "NDS context is invalid\n";
+ IDR_NDS_USERNAME_FAILED
+ "NDS user name could not be accessed\n";
+ IDR_QUERY_INFO_FAILED
+ "NetWare information query failed\n";
+ IDR_NDSQUEUE_NOT_EXIST
+ "Queue %S does not exist.\n"
+ IDR_NDSSUCCESS_QUEUE
+ "Device LPT%d: re-routed to queue %S.\n"
+ IDR_CAPTURE_FAILED
+ "Capture of queue %S failed.\n"
+ IDR_CURRENT_TREE
+ "Your current tree is: %s\n"
+ IDR_CURRENT_SERVER
+ "You are attached to server %S.\n"
+ IDR_CURRENT_CONTEXT
+ "Your current context is %S\n"
+ IDR_AUTHENTICATING_SERVER
+ "Authenticating to server %S.\n"
+ IDR_NO_END_QUOTE
+ "Script Error: The line contains no end quote.\n"
+END
diff --git a/private/nw/nwscript/parspath.c b/private/nw/nwscript/parspath.c
new file mode 100644
index 000000000..77f3a250c
--- /dev/null
+++ b/private/nw/nwscript/parspath.c
@@ -0,0 +1,341 @@
+
+/*************************************************************************
+*
+* PARSPATH.C
+*
+* NetWare parsing routines, ported from DOS
+*
+* Copyright (c) 1995 Microsoft Corporation
+*
+* $Log: N:\NT\PRIVATE\NW4\NWSCRIPT\VCS\PARSPATH.C $
+*
+* Rev 1.3 22 Jan 1996 16:48:38 terryt
+* Add automatic attach query during map
+*
+* Rev 1.2 22 Dec 1995 14:26:16 terryt
+* Add Microsoft headers
+*
+* Rev 1.1 22 Dec 1995 11:08:50 terryt
+* Fixes
+*
+* Rev 1.0 15 Nov 1995 18:07:48 terryt
+* Initial revision.
+*
+* Rev 1.1 25 Aug 1995 16:23:34 terryt
+* Capture support
+*
+* Rev 1.0 15 May 1995 19:11:00 terryt
+* Initial revision.
+*
+*************************************************************************/
+
+/*++
+
+Copyright (c) 1994 Micro Computer Systems, Inc.
+
+Module Name:
+
+ nwlibs\parspath.c
+
+Abstract:
+
+ Directory APIs.
+
+Author:
+
+ Shawn Walker (v-swalk) 10-10-1994
+
+Revision History:
+
+--*/
+#include "common.h"
+#include <ctype.h>
+#include <direct.h>
+#include "inc\nwlibs.h"
+
+
+/*++
+*******************************************************************
+
+ ParsePath
+
+Routine Description:
+
+ Parse the path string.
+
+Arguments:
+
+ pPath = The pointer to the path to parse.
+ pServerName = The pointer to return the server name.
+ pVolumeName = The pointer to return the volume name.
+ pDirPath = The pointer to return the directory path.
+
+Return Value:
+
+ 0x0000 SUCCESSFUL
+ 0x000F INVALID_DRIVE
+ 0x8800 Unknown error
+
+*******************************************************************
+--*/
+unsigned int
+ParsePath(
+ unsigned char *pPath,
+ unsigned char *pServerName, //OPTIONAL
+ unsigned char *pVolumeName, //OPTIONAL
+ unsigned char *pDirPath //OPTIONAL
+ )
+{
+ unsigned char *p, *p2;
+ unsigned int Result;
+ unsigned int Remote;
+ unsigned int NcpError = 0;
+ unsigned char DriveNumber = (unsigned char)-1;
+ unsigned char CurrentPath[64];
+ unsigned char RootPath[NCP_MAX_PATH_LENGTH];
+ unsigned char ServerName[NCP_MAX_PATH_LENGTH];
+ unsigned char *pRootDir;
+ unsigned char NetWarePath[NCP_MAX_PATH_LENGTH];
+ unsigned char VolumeName[NCP_VOLUME_LENGTH];
+ unsigned int LocalDriveForce = FALSE;
+
+ RootPath[0] = 0;
+ VolumeName[0] = 0;
+ ServerName[0] = 0;
+
+ if ( pServerName )
+ *pServerName = '\0';
+
+ /** See if there is a volume on the path **/
+
+ p = pPath;
+ while (*p != ':' && *p) {
+ p++;
+ }
+
+ if (*p == ':') {
+ *p = 0;
+
+ /**
+ Check to see if this is a drive letter. The volume must
+ be 2 characters or more.
+ **/
+
+ if ((p - pPath) == 1) {
+
+ /** Make sure it is a valid alpha char **/
+
+ if (!isalpha((int) *pPath)) {
+ return 0x000F;
+ }
+
+ *pPath = (unsigned char) toupper((int) *pPath);
+
+ /** Make it a drive number **/
+
+ DriveNumber = (unsigned char) (*pPath - 'A');
+ GetDriveStatus ((unsigned short)(DriveNumber+1),
+ NETWARE_FORMAT_SERVER_VOLUME,
+ NULL,
+ NULL,
+ RootPath,
+ NULL,
+ NULL);
+ pRootDir = strchr (RootPath, ':');
+ if (pRootDir)
+ {
+ /*
+ * Setup the pServerName here
+ */
+
+ pRootDir[0] = '\0';
+ p2 = RootPath;
+ while (*p2)
+ {
+ if (*p2 == '\\' || *p2 == '/')
+ {
+ *p2++ = 0;
+ strcpy(ServerName, RootPath);
+ if (pServerName) {
+ strcpy(pServerName, RootPath);
+ }
+ break;
+ }
+ p2++;
+ }
+ strcpy (RootPath, pRootDir+1);
+ }
+ else
+ RootPath[0] = 0;
+ }
+ else {
+
+ DriveNumber = 0;
+ LocalDriveForce = TRUE;
+
+ /**
+ If there is a server name, save the server name
+ and set the error code to 0x880F but still parse
+ the path. This just means that there is no connection
+ for this server. Even if we do have one.
+ **/
+
+ p2 = pPath;
+ while (*p2) {
+ if (*p2 == '\\' || *p2 == '/') {
+ *p2++ = 0;
+
+ strcpy(ServerName, pPath);
+ if (pServerName) {
+ strcpy(pServerName, pPath);
+ }
+ pPath = p2;
+
+ NcpError = 0x880F;
+ break;
+ }
+ p2++;
+ }
+
+ if (NcpError == 0x880F) {
+ /**
+ Do any attach processing.
+ **/
+
+ NcpError = DoAttachProcessing( ServerName );
+
+ }
+
+ strcpy(VolumeName, pPath);
+ }
+
+ /** Get the directory **/
+
+ p++;
+ pPath = p;
+ }
+
+ /**
+ If we did not get the drive letter of volume name
+ from above, then get the current drive we are on.
+ **/
+
+ if (DriveNumber == (unsigned char) -1) {
+ DriveNumber = _getdrive();
+ }
+
+ /*
+ * Use the PREFERRED_SERVER for 3X logins if no server name
+ * was specified.
+ */
+ if (pServerName && !fNDS && !pServerName[0] ) {
+ strcpy( pServerName, PREFERRED_SERVER );
+ }
+
+ if (pVolumeName) {
+
+ /**
+ Check if the drive is remote, if so, then go get the path
+ from the server.
+ **/
+ if ( LocalDriveForce ) {
+ Result = 0;
+ Remote = 0;
+ }
+ else {
+ Result = IsDriveRemote(DriveNumber, &Remote);
+
+ }
+
+ if (NcpError != 0x880F && !VolumeName[0] && (Result || !Remote)) {
+ pVolumeName[0] = (unsigned char) (DriveNumber + 'A');
+ pVolumeName[1] = 0;
+ }
+ else {
+ if (VolumeName[0]) {
+ strcpy(pVolumeName, VolumeName);
+ }
+ else {
+ Result = NTGetNWDrivePath( DriveNumber, NULL, NetWarePath );
+ if (Result) {
+ return Result;
+ }
+
+ p = NetWarePath;
+ while (*p != ':' && *p) {
+ p++;
+ }
+
+ if (*p == ':') {
+ *p = 0;
+ }
+ strcpy(pVolumeName, NetWarePath);
+ }
+ }
+ }
+
+ if (pDirPath) {
+
+ memset(CurrentPath, 0, sizeof(CurrentPath));
+
+ if (VolumeName[0]) {
+ strcpy(pDirPath, pPath);
+ }
+ else {
+ Result = NTGetCurrentDirectory(DriveNumber, CurrentPath);
+ if (Result) {
+ CurrentPath[0] = 0;
+ }
+ else {
+ /*
+ * Skip the drive letter
+ */
+ if ( CurrentPath[0] ) {
+ int i;
+ for ( i = 0; ;i++ ) {
+ CurrentPath[i] = CurrentPath[i+3];
+ if ( !CurrentPath[i] )
+ break;
+ }
+ }
+ }
+
+ if (CurrentPath[0] == 0) {
+ if ( (*pPath == '\\') || ( *pPath == '/' ) ) {
+ sprintf(pDirPath, "%s%s", RootPath, pPath);
+ }
+ else if ( !(*pPath) ) {
+ sprintf(pDirPath, "%s", RootPath);
+ }
+ else {
+ sprintf(pDirPath, "%s\\%s", RootPath, pPath);
+ }
+ }
+ else {
+ if (*pPath) {
+ if ( (*pPath == '\\') || ( *pPath == '/' ) ) {
+ strcpy (pDirPath, RootPath);
+ if (pPath[1]) {
+ strcat(pDirPath, pPath);
+ }
+ }
+ else {
+ sprintf(pDirPath, "%s\\%s\\%s", RootPath, CurrentPath, pPath);
+ }
+ }
+ else {
+ sprintf(pDirPath, "%s\\%s", RootPath, CurrentPath);
+ }
+ }
+ }
+
+ /** Convert the / in the path to \ **/
+ for (p = pDirPath; ( p && ( *p != 0 ) ) ; p++)
+ {
+ if (*p == '/')
+ *p = '\\';
+ }
+ }
+
+ return NcpError;
+}
diff --git a/private/nw/nwscript/ps40db.c b/private/nw/nwscript/ps40db.c
new file mode 100644
index 000000000..9eae6aa09
--- /dev/null
+++ b/private/nw/nwscript/ps40db.c
@@ -0,0 +1,597 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ psndsdb.c
+
+Abstract:
+
+ Read the Print Configuration Attributes
+
+ $Log: N:\NT\PRIVATE\NW4\NWSCRIPT\VCS\PS40DB.C $
+*
+* Rev 1.4 10 Apr 1996 14:23:28 terryt
+* Hotfix for 21181hq
+*
+* Rev 1.4 12 Mar 1996 19:55:22 terryt
+* Relative NDS names and merge
+*
+* Rev 1.3 04 Jan 1996 18:57:36 terryt
+* Bug fixes reported by MS
+*
+* Rev 1.2 22 Dec 1995 14:26:22 terryt
+* Add Microsoft headers
+*
+* Rev 1.1 20 Nov 1995 15:09:46 terryt
+* Context and capture changes
+*
+* Rev 1.0 15 Nov 1995 18:07:52 terryt
+* Initial revision.
+
+--*/
+#include "common.h"
+
+extern DWORD SwapLong(DWORD number);
+extern char *TYPED_USER_NAME;
+
+unsigned int
+PS40GetJobName(
+ unsigned int NDSCaptureFlag,
+ unsigned short SearchFlag,
+ unsigned char *pOwner,
+ unsigned char *pJobName,
+ PPS_JOB_RECORD pJobRecord,
+ unsigned char GetDefault
+ );
+
+#include <pshpack1.h>
+#define NWPS_JOB_NAME_SIZE 32 /* 31 bytes and a '\0' */
+#define NWPS_FORM_NAME_SIZE 12 /* 12 bytes and a '\0' */
+#define NWPS_BANNER_NAME_SIZE 12 /* 12 bytes and a '\0' */
+#define NWPS_BANNER_FILE_SIZE 12 /* 12 bytes and a '\0' */
+#define NWPS_DEVI_NAME_SIZE 32 /* 32 bytes and a '\0' */
+#define NWPS_MODE_NAME_SIZE 32 /* 32 bytes and a '\0' */
+#define NWPS_BIND_NAME_SIZE 48
+#define NWPS_MAX_NAME_SIZE 514
+/*
+// NWPS_Job_Old_Db_Hdr is the first record in the 4.0 PrnConDB database.
+// It contains the following information about the database:
+// The version number,
+// the number of NWPS_Job_Rec records in PrnConDB,
+// the name of the default print job configuration and
+// the name of the job record owner.
+*/
+typedef struct {
+ char text[ 76 ]; /* Printcon database. Version 4.0 */
+ char DefaultJobName[ 32 ]; /* Name of default Job */
+ char Owner[ 256 ]; /* owner of the job record */
+ WORD NumberOfRecords; /* # of NWPS_Job_Rec's in PrnConDB */
+ WORD NumberOfBlocks; /* # of 50-(NWPS_Job_Name_Rec) blocks */
+ BYTE MajorVersion; /* 4 */
+ BYTE MinorVersion; /* 0 */
+} PRINTCON_40_HEADER;
+
+#define PRINTCON_40_HEADER_SIZE sizeof(PRINTCON_40_HEADER)
+
+/*
+// NWPS_Job_41_Db_Hdr is the first record in the 4.1 PrnConDB database.
+// It contains the following information about the database:
+// The version number,
+// the number of NWPS_Job_Rec records in PrnConDB,
+// the name of the default print job configuration and
+// the name of the job record owner IN UNICODE.
+*/
+typedef struct {
+ char text[ 76 ]; /* Printcon database. Version 4.1 */
+ char DefaultJobName[ 32 ]; /* Name of default Job */
+ char unused[ 256 ]; /* no longer used. */
+ WORD NumberOfRecords; /* # of NWPS_Job_Rec's in PrnConDB */
+ WORD NumberOfBlocks; /* # of 50-(NWPS_Job_Name_Rec) blocks */
+ BYTE MajorVersion; /* 4 */
+ BYTE MinorVersion; /* 1 unicode defaultPJOwner etc. */
+ WORD Owner[ 256 ]; /* owner of the default job record */
+} PRINTCON_41_HEADER;
+
+#define PRINTCON_41_HEADER_SIZE sizeof(PRINTCON_41_HEADER)
+
+/*
+// NWPS_Job_Name_Rec is the type of record found in the
+// second section of the PrnConDB database. Each one of
+// these records contains the name of each NWPS_Job_Rec
+// and a pointer to their location in the third section of
+// the database. There is space set aside in this second
+// section for fifty NWPS_Job_Name_Rec records; if this
+// limit is exceeded then another fifty-record block following
+// the first one is allocated after the third section of the
+// database is moved down to make room for the expansion.
+*/
+typedef struct {
+ char JobName[ NWPS_JOB_NAME_SIZE ]; /* 1 - 31 chars long + 0 */
+ long JobRecordOffset; /* Offset of the record
+ // (from the beginning
+ // of the 3rd section for 4.0
+ // databases and from the start
+ // of the file for pre-4.0)
+ */
+} JOB_NAME_AREA;
+
+#define JOB_NAME_AREA_SIZE sizeof(JOB_NAME_AREA)
+
+typedef struct {
+ union {
+ struct {
+ DWORD DataType : 1; /* 0=Byte stream 1 = Text */
+ DWORD FormFeed : 1; /* 0 = FF; 1 = suppress FF */
+ DWORD NotifyWhenDone : 1; /* 0 = no, 1 = yes */
+ DWORD BannerFlag : 1; /* 0 = no, 1 = yes */
+ DWORD AutoEndCap : 1; /* 0 = no, 1 = yes */
+ DWORD TimeOutFlag: 1; /* 0 = no, 1 = yes */
+ DWORD SystemType : 3; /* 0 = bindery 1 = NDS */
+ DWORD Destination: 3; /* 0 = queue 1 = printer */
+ DWORD unknown : 20;
+ };
+ DWORD PrintJobFlags;
+ };
+
+ WORD NumberOfCopies; /* 1 - 65,000 */
+ WORD TimeoutCount; /* 1 - 1,000 */
+ BYTE TabSize; /* 1 - 18 */
+ BYTE LocalPrinter; /* 0=Lpt1, 1=Lpt2, 2=Lpt3 etc. */
+ char FormName[ NWPS_FORM_NAME_SIZE + 2 ]; /* 1-12 chars */
+ char Name[ NWPS_BANNER_NAME_SIZE + 2 ]; /* 1-12 chars */
+ char BannerName[ NWPS_BANNER_FILE_SIZE + 2 ]; /* 1-12 chars */
+ char Device[ NWPS_DEVI_NAME_SIZE + 2 ]; /* 1-32 chars */
+ char Mode[ NWPS_MODE_NAME_SIZE + 2 ]; /* 1-32 chars */
+ union {
+ struct {
+ /* pad structures on even boundries */
+ char Server[ NWPS_BIND_NAME_SIZE + 2 ]; /* 2-48 chars */
+ char QueueName[ NWPS_BIND_NAME_SIZE + 2 ]; /* 1-48 chars */
+ char PrintServer[ NWPS_BIND_NAME_SIZE + 2 ]; /* 1-48 chars */
+ } NonDS;
+ char DSObjectName[ NWPS_MAX_NAME_SIZE ];
+ } u;
+ BYTE reserved[390]; /* Adds up to 1024 total (was 1026) */
+} JOB_RECORD_AREA;
+
+#define JOB_RECORD_AREA_SIZE sizeof(JOB_RECORD_AREA)
+
+
+#include <poppack.h>
+
+
+
+/*++
+*******************************************************************
+
+ PS40JobGetDefault
+
+Routine Description:
+
+ Get the default print job configuration from 40.
+
+Arguments:
+ NDSCaptureFlag
+ SearchFlag =
+ pOwner =
+ pJobName = A pointer to return the default job configuration name.
+ pJobRecord = A pointer to return the default job configuration.
+
+Return Value:
+
+ SUCCESSFUL 0x0000
+ PS_ERR_BAD_VERSION 0x7770
+ PS_ERR_GETTING_DEFAULT 0x7773
+ PS_ERR_OPENING_DB 0x7774
+ PS_ERR_READING_DB 0x7775
+ PS_ERR_READING_RECORD 0x7776
+ PS_ERR_INTERNAL_ERROR 0x7779
+ PS_ERR_NO_DEFAULT_SPECIFIED 0x777B
+ INVALID_CONNECTION 0x8801
+
+*******************************************************************
+--*/
+unsigned int
+PS40JobGetDefault(
+ unsigned int NDSCaptureFlag,
+ unsigned short SearchFlag,
+ unsigned char *pOwner,
+ unsigned char *pJobName,
+ PPS_JOB_RECORD pJobRecord
+ )
+{
+ return PS40GetJobName(
+ NDSCaptureFlag,
+ SearchFlag,
+ pOwner,
+ pJobName,
+ pJobRecord,
+ TRUE);
+}
+
+
+/*++
+*******************************************************************
+
+ PS40JobRead
+
+Routine Description:
+
+ Get the print job configuration from 40.
+
+Arguments:
+
+ NDSCaptureFlag =
+ pOwner =
+ pJobName = A pointer to return the default job configuration name.
+ pJobRecord = A pointer to return the default job configuration.
+
+Return Value:
+
+ SUCCESSFUL 0x0000
+ PS_ERR_BAD_VERSION 0x7770
+ PS_ERR_GETTING_DEFAULT 0x7773
+ PS_ERR_OPENING_DB 0x7774
+ PS_ERR_READING_DB 0x7775
+ PS_ERR_READING_RECORD 0x7776
+ PS_ERR_INTERNAL_ERROR 0x7779
+ PS_ERR_NO_DEFAULT_SPECIFIED 0x777B
+ INVALID_CONNECTION 0x8801
+
+*******************************************************************
+--*/
+unsigned int
+PS40JobRead(
+ unsigned int NDSCaptureFlag,
+ unsigned char *pOwner,
+ unsigned char *pJobName,
+ PPS_JOB_RECORD pJobRecord
+ )
+{
+ return PS40GetJobName(
+ NDSCaptureFlag,
+ 0,
+ pOwner,
+ pJobName,
+ pJobRecord,
+ FALSE);
+}
+
+
+/*++
+*******************************************************************
+
+ PS40GetJobName
+
+Routine Description:
+
+ Common routine to get the print job configuration from 40.
+
+Arguments:
+ NDSCaptureFlag =
+ SearchFlag =
+ pOwner =
+ pJobName = A pointer to return the default job configuration name.
+ pJobRecord = A pointer to return the default job configuration.
+ GetDefault = TRUE = get the default job name, FALSE = Don't get
+ the default job name.
+
+Return Value:
+
+ SUCCESSFUL 0x0000
+ PS_ERR_BAD_VERSION 0x7770
+ PS_ERR_GETTING_DEFAULT 0x7773
+ PS_ERR_OPENING_DB 0x7774
+ PS_ERR_READING_DB 0x7775
+ PS_ERR_READING_RECORD 0x7776
+ PS_ERR_INTERNAL_ERROR 0x7779
+ PS_ERR_NO_DEFAULT_SPECIFIED 0x777B
+ INVALID_CONNECTION 0x8801
+
+*******************************************************************
+--*/
+unsigned int
+PS40GetJobName(
+ unsigned int NDSCaptureFlag,
+ unsigned short SearchFlag,
+ unsigned char *pOwner,
+ unsigned char *pJobName,
+ PPS_JOB_RECORD pJobRecord,
+ unsigned char GetDefault
+ )
+{
+ unsigned char *pSearchJobName;
+ unsigned long ObjectId;
+ HANDLE stream = NULL;
+ unsigned int Count;
+ unsigned int Bytes;
+ unsigned int RetCode = 0;
+ unsigned int ConnectionNumber;
+ JOB_NAME_AREA JobNameArea;
+ JOB_RECORD_AREA JobRecord;
+ PRINTCON_40_HEADER PrintConHeader;
+ unsigned int Version40 = FALSE;
+ unsigned int ConnectionHandle;
+ unsigned char MailDirPath[NCP_MAX_PATH_LENGTH];
+ unsigned char TempJobName[32];
+ PBYTE JobContext = NULL;
+ unsigned FileSize;
+
+ // BUGBUG Printer names can be used instead of queues
+ // Must lookup "default print queue" if NT doesn't
+
+ if ( NDSCaptureFlag ) {
+
+ if ( !GetDefault ) {
+ JobContext = strchr( pJobName, ':' );
+ if ( JobContext ) {
+ *JobContext = '\0';
+ strncpy( TempJobName, pJobName, 32 );
+ *JobContext++ = ':';
+ pJobName = TempJobName;
+ }
+ }
+
+ if ( JobContext ) {
+ if (NDSfopenStream ( JobContext, "Print Job Configuration", &stream,
+ &FileSize )) {
+ RetCode = PS_ERR_OPENING_DB;
+ goto CommonExit;
+ }
+ }
+ else {
+ if (NDSfopenStream ( TYPED_USER_NAME, "Print Job Configuration",
+ &stream, &FileSize)) {
+ PBYTE p;
+
+ for ( p = TYPED_USER_NAME; p ; p = strchr ( p, '.' ) )
+ {
+ p++;
+
+ if ( *p == 'O' && *(p+1) == 'U' && *(p+2) == '=' )
+ break;
+
+ if ( *p == 'O' && *(p+1) == '=' )
+ break;
+ }
+ if (NDSfopenStream ( p, "Print Job Configuration", &stream,
+ &FileSize)) {
+ RetCode = PS_ERR_OPENING_DB;
+ goto CommonExit;
+ }
+ }
+ }
+ }
+ else {
+
+ if (!CGetDefaultConnectionID (&ConnectionHandle)) {
+ RetCode = PS_ERR_OPENING_DB;
+ goto CommonExit;
+ }
+
+ RetCode = GetConnectionNumber(ConnectionHandle, &ConnectionNumber);
+ if (RetCode) {
+ goto CommonExit;
+ }
+
+ RetCode = GetBinderyObjectID (ConnectionHandle, LOGIN_NAME,
+ OT_USER, &ObjectId);
+ if (RetCode) {
+ goto CommonExit;
+ }
+
+ /** Build the path to open the file **/
+
+ sprintf(MailDirPath, "SYS:MAIL/%lX/PRINTJOB.DAT", SwapLong(ObjectId));
+ stream = CreateFileA( NTNWtoUNCFormat( MailDirPath ),
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL );
+ if (stream == INVALID_HANDLE_VALUE) {
+
+ sprintf(MailDirPath, "SYS:PUBLIC/PRINTJOB.DAT");
+
+ stream = CreateFileA( NTNWtoUNCFormat(MailDirPath),
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL );
+
+ if (stream == INVALID_HANDLE_VALUE) {
+ RetCode = PS_ERR_OPENING_DB;
+ goto CommonExit;
+ }
+ }
+ }
+
+ if ( !ReadFile( stream, (PBYTE) &PrintConHeader, PRINTCON_40_HEADER_SIZE, &Bytes, NULL ) ) {
+ RetCode = PS_ERR_INTERNAL_ERROR;
+ goto CommonExit;
+ }
+
+ if (Bytes < PRINTCON_40_HEADER_SIZE) {
+ if ( !( NDSCaptureFlag && Bytes) ) { // BUGBUG
+ RetCode = PS_ERR_INTERNAL_ERROR;
+ goto CommonExit;
+ }
+ }
+
+ /** Check the version number **/
+
+ if ( PrintConHeader.MajorVersion != 4 ) {
+ RetCode = PS_ERR_BAD_VERSION;
+ goto CommonExit;
+ }
+
+ if ( PrintConHeader.MinorVersion == 0 ) {
+ Version40 = TRUE;
+ }
+
+ /** Get the name we are looking for **/
+
+ if (GetDefault) {
+ if (PrintConHeader.DefaultJobName[0] == 0) {
+ RetCode = PS_ERR_GETTING_DEFAULT;
+ goto CommonExit;
+ }
+ pSearchJobName = PrintConHeader.DefaultJobName;
+ }
+ else {
+ pSearchJobName = pJobName;
+ }
+
+ if ( !Version40 ) {
+ SetFilePointer( stream, PRINTCON_41_HEADER_SIZE, NULL, FILE_BEGIN );
+ }
+
+ Count = 0;
+
+ /** Go through all of the job entry to look for the name **/
+
+ while (Count < PrintConHeader.NumberOfRecords) {
+ if ( !ReadFile( stream, (PBYTE) &JobNameArea, JOB_NAME_AREA_SIZE, &Bytes, NULL) ) {
+ RetCode = PS_ERR_INTERNAL_ERROR;
+ goto CommonExit;
+ }
+
+ if (Bytes < JOB_NAME_AREA_SIZE) {
+ if ( !( NDSCaptureFlag && Bytes) ) { // BUGBUG
+ RetCode = PS_ERR_INTERNAL_ERROR;
+ goto CommonExit;
+ }
+ }
+ Count++;
+
+
+ /** Skip the entry with a null job name **/
+
+ if (JobNameArea.JobName[0] == 0) {
+ continue;
+ }
+
+ /** Is this the job name we are looking for? **/
+
+ if (!_strcmpi(pSearchJobName, JobNameArea.JobName)) {
+ break;
+ }
+ }
+
+ /** See if we found the job name **/
+
+ if (Count > PrintConHeader.NumberOfRecords) {
+ if (GetDefault) {
+ RetCode = PS_ERR_GETTING_DEFAULT;
+ }
+ else {
+ RetCode = PS_ERR_READING_RECORD;
+ }
+ goto CommonExit;
+ }
+
+ /*
+ * The Job offset starts at the beginning of the third section.
+ * The third section starts after the Header and after the
+ * 50 record blocks.
+ */
+ if ( Version40 ) {
+ SetFilePointer( stream,
+ PRINTCON_40_HEADER_SIZE +
+ ( PrintConHeader.NumberOfBlocks * 50) * JOB_NAME_AREA_SIZE +
+ JobNameArea.JobRecordOffset,
+ NULL,
+ FILE_BEGIN );
+ }
+ else {
+ SetFilePointer( stream,
+ PRINTCON_41_HEADER_SIZE +
+ ( PrintConHeader.NumberOfBlocks * 50) * JOB_NAME_AREA_SIZE +
+ JobNameArea.JobRecordOffset,
+ NULL,
+ FILE_BEGIN );
+ }
+
+ memset((PBYTE)&JobRecord, 0, sizeof(JobRecord));
+
+ if ( !ReadFile( stream, (PBYTE) &JobRecord, JOB_RECORD_AREA_SIZE, &Bytes, NULL) ) {
+ RetCode = PS_ERR_READING_RECORD;
+ goto CommonExit;
+ }
+
+ if (Bytes < JOB_RECORD_AREA_SIZE) {
+ if ( !( NDSCaptureFlag && Bytes) ) { // BUGBUG
+ RetCode = PS_ERR_READING_RECORD;
+ goto CommonExit;
+ }
+ }
+
+ memset(pJobRecord, 0, PS_JOB_RECORD_SIZE);
+
+ if (JobRecord.NotifyWhenDone) {
+ pJobRecord->PrintJobFlag |= PS_JOB_NOTIFY;
+ }
+ if (JobRecord.BannerFlag) {
+ pJobRecord->PrintJobFlag |= PS_JOB_PRINT_BANNER;
+ }
+ if (JobRecord.DataType) {
+ pJobRecord->PrintJobFlag |= PS_JOB_EXPAND_TABS;
+ }
+ if (JobRecord.FormFeed) {
+ pJobRecord->PrintJobFlag |= PS_JOB_NO_FORMFEED;
+ }
+ if (JobRecord.AutoEndCap) {
+ pJobRecord->PrintJobFlag |= PS_JOB_AUTO_END;
+ }
+ if (JobRecord.TimeoutCount) {
+ pJobRecord->PrintJobFlag |= PS_JOB_TIMEOUT;
+ }
+ if (JobRecord.Destination) {
+ pJobRecord->PrintJobFlag |= PS_JOB_DS_PRINTER;
+ }
+ if ( JobRecord.SystemType ) {
+ pJobRecord->PrintJobFlag |= PS_JOB_ENV_DS;
+ }
+
+ pJobRecord->Copies = JobRecord.NumberOfCopies;
+ pJobRecord->TabSize = JobRecord.TabSize;
+ pJobRecord->TimeOutCount = JobRecord.TimeoutCount;
+ pJobRecord->LocalPrinter = JobRecord.LocalPrinter;
+
+ strcpy(pJobRecord->Mode, JobRecord.Mode);
+ strcpy(pJobRecord->Device, JobRecord.Device);
+ strcpy(pJobRecord->FormName, JobRecord.FormName);
+ strcpy(pJobRecord->BannerName, JobRecord.BannerName);
+
+ if ( JobRecord.SystemType ) {
+ ConvertUnicodeToAscii( JobRecord.u.DSObjectName );
+ strcpy(pJobRecord->u.DSObjectName, JobRecord.u.DSObjectName);
+ }
+ else {
+ strcpy(pJobRecord->u.NonDS.PrintQueue, JobRecord.u.NonDS.QueueName);
+ strcpy(pJobRecord->u.NonDS.FileServer, JobRecord.u.NonDS.Server);
+ }
+
+ if (GetDefault && pJobName) {
+ strcpy(pJobName, JobNameArea.JobName);
+ }
+
+ if (pOwner) {
+ *pOwner = 0;
+ }
+
+CommonExit:
+ if (stream != NULL) {
+ if ( NDSCaptureFlag )
+ CloseHandle( stream );
+ else
+ fclose( stream );
+ }
+
+ return RetCode;
+}
diff --git a/private/nw/nwscript/psdb.c b/private/nw/nwscript/psdb.c
new file mode 100644
index 000000000..713c1c4c4
--- /dev/null
+++ b/private/nw/nwscript/psdb.c
@@ -0,0 +1,396 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ nwlibs\psdb.c
+
+Abstract:
+
+ Read the Print Con database file APIs.
+
+Author:
+
+ Shawn Walker (v-swalk) 12-12-1994
+
+Revision History:
+
+--*/
+#include "common.h"
+
+extern DWORD SwapLong(DWORD number);
+
+unsigned int
+PSGetJobName(
+ unsigned int ConnectionHandle,
+ unsigned short SearchFlag,
+ unsigned char *pOwner,
+ unsigned char *pJobName,
+ PPS_JOB_RECORD pJobRecord,
+ unsigned char GetDefault
+ );
+
+#define MAX_JOB_NAME_ENTRY 37
+
+#define O_RDONLY 0x0000 /* open for reading only */
+#define O_WRONLY 0x0001 /* open for writing only */
+#define O_RDWR 0x0002 /* open for reading and writing */
+#define O_APPEND 0x0008 /* writes done at eof */
+#define O_CREAT 0x0100 /* create and open file */
+#define O_TRUNC 0x0200 /* open and truncate */
+#define O_EXCL 0x0400 /* open only if file doesn't already exist */
+#define O_TEXT 0x4000 /* file mode is text (translated) */
+#define O_BINARY 0x8000 /* file mode is binary (untranslated) */
+
+#define S_IEXEC 0000100 /* execute/search permission, owner */
+#define S_IWRITE 0000200 /* write permission, owner */
+#define S_IREAD 0000400 /* read permission, owner */
+#define S_IFCHR 0020000 /* character special */
+#define S_IFDIR 0040000 /* directory */
+#define S_IFREG 0100000 /* regular */
+#define S_IFMT 0170000 /* file type mask */
+
+#include <pshpack1.h>
+typedef struct _PRINTCON_HEADER {
+ unsigned char Text[115];
+ unsigned char MajorVersion;
+ unsigned char MinorVersion1;
+ unsigned char MinorVersion2;
+ unsigned char DefaultJobName[32];
+} PRINTCON_HEADER, *PPRINTCON_HEADER;
+
+#define PRINTCON_HEADER_SIZE sizeof(PRINTCON_HEADER)
+
+typedef struct _JOB_NAME_AREA {
+ unsigned char JobName[32];
+ unsigned long JobRecordOffset;
+} JOB_NAME_AREA, *PJOB_NAME_AREA;
+
+#define JOB_NAME_AREA_SIZE sizeof(JOB_NAME_AREA)
+
+typedef struct _JOB_RECORD_AREA {
+ unsigned char ServerName[NCP_BINDERY_OBJECT_NAME_LENGTH];
+ unsigned char QueueName[NCP_BINDERY_OBJECT_NAME_LENGTH];
+ unsigned char TabSize;
+ unsigned short NumberOfCopies;
+ unsigned char FormName[40];
+ unsigned char NotifyWhenDone; //0=No, 1=Yes
+ unsigned long PrintServerID;
+ unsigned char Name[13];
+ unsigned char BannerName[13];
+ unsigned char Device[33];
+ unsigned char Mode[33];
+ unsigned char BannerFlag; //0=No Banner, 1=Banner
+ unsigned char DataType; //1=Byte,0=Stream
+ unsigned char FormFeed; //0=Don't Suppress FF, 1=Suppress FF
+ unsigned short TimeoutCount;
+ unsigned char LocalPrinter; //1=LPT1, 2=LPT2, 3=LPT3
+ unsigned char AutoEndCap; //0=Don't Auto EndCap, 1=Do Auto EndCap
+} JOB_RECORD_AREA, *PJOB_RECORD_AREA;
+#include <poppack.h>
+
+#define JOB_RECORD_AREA_SIZE sizeof(JOB_RECORD_AREA)
+
+
+/*++
+*******************************************************************
+
+ PSJobGetDefault
+
+Routine Description:
+
+ Get the default print job configuration from the printcon.dat
+ file.
+
+Arguments:
+
+ ConnectionHandle = The connection handle to use.
+ SearchFlag =
+ pOwner =
+ pJobName = A pointer to return the default job configuration name.
+ pJobRecord = A pointer to return the default job configuration.
+
+Return Value:
+
+ SUCCESSFUL 0x0000
+ PS_ERR_BAD_VERSION 0x7770
+ PS_ERR_GETTING_DEFAULT 0x7773
+ PS_ERR_OPENING_DB 0x7774
+ PS_ERR_READING_DB 0x7775
+ PS_ERR_READING_RECORD 0x7776
+ PS_ERR_INTERNAL_ERROR 0x7779
+ PS_ERR_NO_DEFAULT_SPECIFIED 0x777B
+ INVALID_CONNECTION 0x8801
+
+*******************************************************************
+--*/
+unsigned int
+PSJobGetDefault(
+ unsigned int ConnectionHandle,
+ unsigned short SearchFlag,
+ unsigned char *pOwner,
+ unsigned char *pJobName,
+ PPS_JOB_RECORD pJobRecord
+ )
+{
+ return PSGetJobName(
+ ConnectionHandle,
+ SearchFlag,
+ pOwner,
+ pJobName,
+ pJobRecord,
+ TRUE);
+}
+
+
+/*++
+*******************************************************************
+
+ PSJobRead
+
+Routine Description:
+
+ Get the print job configuration from the printcon.dat file.
+
+Arguments:
+
+ ConnectionHandle = The connection handle to use.
+ pOwner =
+ pJobName = A pointer to return the default job configuration name.
+ pJobRecord = A pointer to return the default job configuration.
+
+Return Value:
+
+ SUCCESSFUL 0x0000
+ PS_ERR_BAD_VERSION 0x7770
+ PS_ERR_GETTING_DEFAULT 0x7773
+ PS_ERR_OPENING_DB 0x7774
+ PS_ERR_READING_DB 0x7775
+ PS_ERR_READING_RECORD 0x7776
+ PS_ERR_INTERNAL_ERROR 0x7779
+ PS_ERR_NO_DEFAULT_SPECIFIED 0x777B
+ INVALID_CONNECTION 0x8801
+
+*******************************************************************
+--*/
+unsigned int
+PSJobRead(
+ unsigned int ConnectionHandle,
+ unsigned char *pOwner,
+ unsigned char *pJobName,
+ PPS_JOB_RECORD pJobRecord
+ )
+{
+ return PSGetJobName(
+ ConnectionHandle,
+ 0,
+ pOwner,
+ pJobName,
+ pJobRecord,
+ FALSE);
+}
+
+
+/*++
+*******************************************************************
+
+ PSGetJobName
+
+Routine Description:
+
+ Common routine to get the print job configuration from the
+ printcon.dat file.
+
+Arguments:
+
+ ConnectionHandle = The connection handle to use.
+ SearchFlag =
+ pOwner =
+ pJobName = A pointer to return the default job configuration name.
+ pJobRecord = A pointer to return the default job configuration.
+ GetDefault = TRUE = get the default job name, FALSE = Don't get
+ the default job name.
+
+Return Value:
+
+ SUCCESSFUL 0x0000
+ PS_ERR_BAD_VERSION 0x7770
+ PS_ERR_GETTING_DEFAULT 0x7773
+ PS_ERR_OPENING_DB 0x7774
+ PS_ERR_READING_DB 0x7775
+ PS_ERR_READING_RECORD 0x7776
+ PS_ERR_INTERNAL_ERROR 0x7779
+ PS_ERR_NO_DEFAULT_SPECIFIED 0x777B
+ INVALID_CONNECTION 0x8801
+
+*******************************************************************
+--*/
+unsigned int
+PSGetJobName(
+ unsigned int ConnectionHandle,
+ unsigned short SearchFlag,
+ unsigned char *pOwner,
+ unsigned char *pJobName,
+ PPS_JOB_RECORD pJobRecord,
+ unsigned char GetDefault
+ )
+{
+ unsigned char *pSearchJobName;
+ unsigned long ObjectId;
+ FILE *stream = NULL;
+ unsigned int Count;
+ unsigned int Bytes;
+ unsigned int RetCode;
+ unsigned int ConnectionNumber;
+ JOB_NAME_AREA JobNameArea;
+ JOB_RECORD_AREA JobRecord;
+ PRINTCON_HEADER PrintConHeader;
+ unsigned char MailDirPath[NCP_MAX_PATH_LENGTH];
+
+ /** Get the connection number for this connection **/
+
+ RetCode = GetConnectionNumber(ConnectionHandle, &ConnectionNumber);
+ if (RetCode) {
+ goto CommonExit;
+ }
+
+ RetCode = GetBinderyObjectID (ConnectionHandle, LOGIN_NAME,
+ OT_USER, &ObjectId);
+ if (RetCode) {
+ goto CommonExit;
+ }
+
+ /** Build the path to open the file **/
+
+ sprintf(MailDirPath, "SYS:MAIL/%lX/PRINTCON.DAT", SwapLong(ObjectId));
+
+ stream = fopen(NTNWtoUNCFormat( MailDirPath), "rb");
+ if (stream == NULL) {
+ RetCode = PS_ERR_OPENING_DB;
+ goto CommonExit;
+ }
+
+ Bytes = fread( (unsigned char *) &PrintConHeader, sizeof( char), PRINTCON_HEADER_SIZE, stream);
+ if (Bytes < PRINTCON_HEADER_SIZE) {
+ RetCode = PS_ERR_INTERNAL_ERROR;
+ goto CommonExit;
+ }
+
+ /** Check the version number **/
+
+ if ((PrintConHeader.MajorVersion != 3 &&
+ PrintConHeader.MajorVersion != 1) ||
+ PrintConHeader.MinorVersion1 != 1 ||
+ PrintConHeader.MinorVersion2 != 1) {
+
+ RetCode = PS_ERR_BAD_VERSION;
+ goto CommonExit;
+ }
+ /** Get the name we are looking for **/
+
+ if (GetDefault) {
+ if (PrintConHeader.DefaultJobName[0] == 0) {
+ RetCode = PS_ERR_NO_DEFAULT_SPECIFIED;
+ goto CommonExit;
+ }
+ pSearchJobName = PrintConHeader.DefaultJobName;
+ }
+ else {
+ pSearchJobName = pJobName;
+ }
+
+ Count = 0;
+
+ /** Go through all of the job entry to look for the name **/
+
+ while (Count < MAX_JOB_NAME_ENTRY) {
+ Bytes = fread( (unsigned char *) &JobNameArea, sizeof(unsigned char), JOB_NAME_AREA_SIZE, stream);
+ if (Bytes < JOB_NAME_AREA_SIZE) {
+ RetCode = PS_ERR_INTERNAL_ERROR;
+ goto CommonExit;
+ }
+ Count++;
+
+ /** Skip the entry with a null job name **/
+
+ if (JobNameArea.JobName[0] == 0) {
+ continue;
+ }
+
+ /** Is this the job name we are looking for? **/
+
+ if (!_strcmpi(pSearchJobName, JobNameArea.JobName)) {
+ break;
+ }
+ }
+
+ /** See if we found the job name **/
+
+ if (Count > MAX_JOB_NAME_ENTRY) {
+ if (GetDefault) {
+ RetCode = PS_ERR_GETTING_DEFAULT;
+ }
+ else {
+ RetCode = PS_ERR_READING_RECORD;
+ }
+ goto CommonExit;
+ }
+
+ fseek(stream, JobNameArea.JobRecordOffset, SEEK_SET);
+
+ Bytes = fread( (unsigned char *) &JobRecord, sizeof(unsigned char), JOB_RECORD_AREA_SIZE, stream);
+ if (Bytes < JOB_RECORD_AREA_SIZE) {
+ RetCode = PS_ERR_READING_RECORD;
+ goto CommonExit;
+ }
+
+ memset(pJobRecord, 0, PS_JOB_RECORD_SIZE);
+
+ if (JobRecord.NotifyWhenDone) {
+ pJobRecord->PrintJobFlag |= PS_JOB_NOTIFY;
+ }
+ if (JobRecord.BannerFlag) {
+ pJobRecord->PrintJobFlag |= PS_JOB_PRINT_BANNER;
+ }
+ if (JobRecord.DataType) {
+ pJobRecord->PrintJobFlag |= PS_JOB_EXPAND_TABS;
+ }
+ if (JobRecord.FormFeed) {
+ pJobRecord->PrintJobFlag |= PS_JOB_NO_FORMFEED;
+ }
+ if (JobRecord.AutoEndCap) {
+ pJobRecord->PrintJobFlag |= PS_JOB_AUTO_END;
+ }
+ if (JobRecord.TimeoutCount) {
+ pJobRecord->PrintJobFlag |= PS_JOB_TIMEOUT;
+ }
+
+ pJobRecord->Copies = JobRecord.NumberOfCopies;
+ pJobRecord->TabSize = JobRecord.TabSize;
+ pJobRecord->TimeOutCount = JobRecord.TimeoutCount;
+ pJobRecord->LocalPrinter = JobRecord.LocalPrinter;
+
+ strcpy(pJobRecord->Mode, JobRecord.Mode);
+ strcpy(pJobRecord->Device, JobRecord.Device);
+ strcpy(pJobRecord->FormName, JobRecord.FormName);
+ strcpy(pJobRecord->BannerName, JobRecord.BannerName);
+ strcpy(pJobRecord->u.NonDS.PrintQueue, JobRecord.QueueName);
+ strcpy(pJobRecord->u.NonDS.FileServer, JobRecord.ServerName);
+
+ if (GetDefault && pJobName) {
+ strcpy(pJobName, JobNameArea.JobName);
+ }
+
+ if (pOwner) {
+ *pOwner = 0;
+ }
+
+CommonExit:
+
+ if (stream != NULL) {
+ fclose( stream );
+ }
+
+ return RetCode;
+}
diff --git a/private/nw/nwscript/script.c b/private/nw/nwscript/script.c
new file mode 100644
index 000000000..14df55b93
--- /dev/null
+++ b/private/nw/nwscript/script.c
@@ -0,0 +1,3968 @@
+
+/*************************************************************************
+*
+* SCRIPT.C
+*
+* Script routines, ported from DOS
+*
+* Copyright (c) 1995 Microsoft Corporation
+*
+*************************************************************************/
+
+#include "common.h"
+#include <direct.h>
+#include <time.h>
+#include <ctype.h>
+#include <process.h>
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <io.h>
+#include <time.h>
+
+extern VOID nwShowLastLoginTime( VOID );
+
+#define MAXLEN 256
+#define MAX_NUM_IF 10
+
+#define NUMCOMMAND 44
+
+#define CM_IF 20
+#define CM_ELSE 21
+#define CM_END 22
+
+//
+// 3X and 4X variables
+//
+
+#define IDS_DAY_OF_WEEK 0
+#define IDS_DAY 1
+#define IDS_MONTH_NAME 2
+#define IDS_MONTH 3
+#define IDS_NDAY_OF_WEEK 4
+#define IDS_SHORT_YEAR 5
+#define IDS_YEAR 6
+#define IDS_AM_PM 7
+#define IDS_GREETING_TIME 8
+#define IDS_HOUR24 9
+#define IDS_HOUR 10
+#define IDS_MINUTE 11
+#define IDS_SECOND 12
+#define IDS_FULL_NAME 13
+#define IDS_LOGIN_NAME 14
+#define IDS_USER_ID 15
+#define IDS_PASSWORD_EXPIRES 16
+#define IDS_NETWORK_ADDRESS 17
+#define IDS_FILE_SERVER 18
+#define IDS_ACCESS_SERVER 19
+#define IDS_ERROR_LEVEL 20
+#define IDS_ERRORLEVEL 21
+#define IDS_MACHINE 22
+#define IDS_OS_VERSION 23
+#define IDS_OS 24
+#define IDS_SMACHINE 25
+#define IDS_SHELL_TYPE 26
+#define IDS_STATION 27
+#define IDS_P_STATION 28
+#define IDS_SHELL_VERSION 29
+#define NUMVAR_3X IDS_SHELL_VERSION + 1
+
+#define IDS_LAST_NAME 30
+#define IDS_LOGIN_CONTEXT 31
+#define IDS_NETWARE_REQUESTER 32
+#define IDS_REQUESTER_CONTEXT 33
+#define IDS_ACCOUNT_BALANCE 34
+#define IDS_CN 35
+#define IDS_REQUESTER_VERSION 36
+#define IDS_SURNAME 37
+#define IDS_DOS_REQUESTER 38
+#define IDS_REQUESTER 39
+#define IDS_ADMINISTRATIVE_ASSISTANT 40
+#define IDS_ALLOW_UNLIMITED_CREDIT 41
+#define IDS_DESCRIPTION 42
+#define IDS_EMAIL_ADDRESS 43
+#define IDS_EMPLOYEE_ID 44
+#define IDS_FACSIMILE_TELEPHONE_NUMBER 45
+#define IDS_GROUP_MEMBERSHIP 46
+#define IDS_HIGHER_PRIVILEGES 47
+#define IDS_HOME_DIRECTORY 48
+#define IDS_INITIALS 49
+#define IDS_LANGUAGE 50
+#define IDS_LOCKED_BY_INTRUDER 51
+#define IDS_LOGIN_DISABLED 52
+#define IDS_LOGIN_GRACE_LIMIT 53
+#define IDS_LOGIN_GRACE_REMAINING 54
+#define IDS_LOGIN_INTRUDER_ATTEMPTS 55
+#define IDS_LOGIN_MAXIMUM_SIMULTANEOUS 56
+#define IDS_MAILSTOP 57
+#define IDS_MESSAGE_SERVER 58
+#define IDS_MINIMUM_ACCOUNT_BALANCE 59
+#define IDS_NETWORK 60
+#define IDS_OBJECT_CLASS 61
+#define IDS_OU 62
+#define IDS_PASSWORD_ALLOW_CHANGE 63
+#define IDS_PASSWORD_MINIMUM_LENGTH 64
+#define IDS_PASSWORD_REQUIRED 65
+#define IDS_PASSWORD_UNIQUE_REQUIRED 66
+#define IDS_PASSWORDS_USED 67
+#define IDS_PHYSICAL_DELIVERY_OFFICE_NAME 68
+#define IDS_POSTAL_ADDRESS 69
+#define IDS_POSTAL_CODE 70
+#define IDS_POSTAL_OFFICE_BOX 71
+#define IDS_PRIVATE_KEY 72
+#define IDS_PROFILE 73
+#define IDS_REVISION 74
+#define IDS_SECURITY_EQUALS 75
+#define IDS_SECURITY_FLAGS 76
+#define IDS_SEE_ALSO 77
+#define IDS_SERVER_HOLDS 78
+#define IDS_SUPERVISOR 79
+#define IDS_TELEPHONE_NUMBER 80
+#define IDS_TITLE 81
+#define IDS_CERTIFICATE_VALIDITY_INTERVAL 82
+#define IDS_EQUIVALENT_TO_ME 83
+#define IDS_GENERATIONAL_QUALIFIER 84
+#define IDS_GIVEN_NAME 85
+#define IDS_MAILBOX_ID 86
+#define IDS_MAILBOX_LOCATION 87
+#define IDS_PROFILE_MEMBERSHIP 88
+#define IDS_SA 89
+#define IDS_S 90
+#define IDS_L 91
+#define IDS_ACCESS 92
+#define NUMVAR IDS_ACCESS + 1
+
+/*
+ * String constants.
+ */
+
+/*
+ * Text for default Login Script. Don't change.
+ */
+BYTE DefaultLoginScript[] =
+ "WRITE \"Good %GREETING_TIME, %LOGIN_NAME.\\n\"\n"
+ "MAP DISPLAY OFF\n"
+ "MAP ERRORS OFF\n"
+ "Rem: Set 1st drive to most appropriate directory.\n"
+ "MAP *1:=%FILE_SERVER/SYS:;*1:=%FILE_SERVER/SYS:%LOGIN_NAME\n"
+ "If LOGIN_NAME=\"SUPERVISOR\" || LOGIN_NAME=\"ADMIN\" || LOGIN_NAME=\"SUPERVIS\" THEN MAP *1:=%FILE_SERVER/SYS:SYSTEM\n"
+ "Rem: Set search drives (S2 machine-OS dependent).\n"
+ "MAP INS S1:=%FILE_SERVER/SYS:PUBLIC\n"
+ "MAP INS S2:=%FILE_SERVER/SYS:\n"
+ "Rem: Now display all the current drive settings.\n"
+ "MAP DISPLAY ON\n"
+ "MAP\n"
+ "\0";
+
+char *__SPACES__=" \t";
+
+/*
+ * Do not change the order of this array.
+ */
+char * COMPARE_OPERATORS[] =
+{
+ "!=",
+ "<>",
+ "NOT EQUAL TO",
+ "DOES NOT EQUAL",
+ "NOT EQUAL",
+ "IS NOT EQUAL",
+ "#",
+ "IS NOT", // 7
+ ">=", // 8
+ "IS GREATER THAN OR EQUAL TO",
+ "IS GREATER THAN OR EQUAL",
+ "GREATER THAN OR EQUAL TO",
+ "GREATER THAN OR EQUAL",
+ ">", // 13
+ "IS GREATER THAN",
+ "GREATER THAN",
+ "<=", // 16
+ "IS LESS THAN OR EQUAL TO",
+ "IS LESS THAN OR EQUAL",
+ "LESS THAN OR EQUAL TO",
+ "LESS THAN OR EQUAL",
+ "<", // 21
+ "IS LESS THAN",
+ "LESS THAN",
+ "==", // 24
+ "=",
+ "EQUALS",
+ "EQUAL",
+ "IS",
+ "\0"
+};
+
+int IsNotEqual (int nIndex)
+{
+ return(nIndex < 8);
+}
+
+int IsGreaterOrEqual (int nIndex)
+{
+ return(nIndex >= 8 && nIndex < 13);
+}
+
+int IsGreater (int nIndex)
+{
+ return(nIndex >= 13 && nIndex < 16);
+}
+
+int IsLessOrEqual (int nIndex)
+{
+ return(nIndex >= 16 && nIndex < 21);
+}
+
+int IsLess (int nIndex)
+{
+ return(nIndex >= 21 && nIndex < 24);
+}
+
+/*
+ * Type defs.
+ */
+typedef int (*PFCommandHandler) (char *lpParam);
+
+typedef struct tagCOMMANDTABLE
+{
+ char *commandStr0;
+ char *commandStr1;
+ char *commandStr2;
+ PFCommandHandler commandhandler;
+}COMMANDTABLE;
+
+typedef struct tagLABEL_LIST
+{
+ char *pLabel;
+ char *pNextLine;
+ struct tagLABEL_LIST *pNext;
+}LABEL_LIST, *PLABEL_LIST;
+
+/*
+ * Functions that are in command dispatch table.
+ */
+int AttachHandler (char *lpParam);
+int BreakHandler (char *lpParam);
+int ComspecHandler (char *lpParam);
+int DisplayHandler (char *lpParam);
+int DosBreakHandler (char *lpParam);
+int SetHandler (char *lpParam);
+int LocalSetHandler (char *lpParam);
+int DosVerifyHandler (char *lpParam);
+int DriveHandler (char *lpParam);
+int FireHandler (char *lpParam);
+int ExitHandler (char *lpParam);
+int IfHandler (char *lpParam);
+int ElseHandler (char *lpParam);
+int EndHandler (char *lpParam);
+int IncludeHandler (char *lpParam);
+int MapHandler (char *lpParam);
+int PauseHandler (char *lpParam);
+int WriteHandler (char *lpParam);
+int NullHandler (char *lpParam);
+int GotoHandler (char *lpParam);
+int ShiftHandler (char *lpParam);
+int MachineHandler (char *lpParam);
+int CompatibleHandler(char *lpParam);
+int ClearHandler (char *lpParam);
+int LastLoginTimeHandler(char *lpParam);
+int ContextHandler (char *lpParam);
+int ScriptServerHandler(char *lpParam);
+int NoDefaultHandler (char *lpParam);
+
+/*
+ * Command dispatch table. Do not change.
+ *
+ * If you do, you must change CM_IF, CM_ELSE, and CM_END
+ */
+COMMANDTABLE nwCommand[NUMCOMMAND] =
+{
+ "LOCAL", "DOS", "SET", LocalSetHandler,
+ "TEMP", "DOS", "SET", LocalSetHandler,
+ "TEMPORARY", "DOS", "SET", LocalSetHandler,
+ "LOCAL", "SET", NULL, LocalSetHandler,
+ "TEMP", "SET", NULL, LocalSetHandler,
+ "TEMPORARY", "SET", NULL, LocalSetHandler,
+ "DOS", "SET", NULL, SetHandler,
+ "DOS", "VERIFY", NULL, DosVerifyHandler,
+ "DOS", "BREAK", NULL, DosBreakHandler,
+ "FIRE", "PHASERS", NULL, FireHandler,
+ "ATTACH", NULL, NULL, AttachHandler,
+ "BREAK", NULL, NULL, BreakHandler,
+ "COMSPEC", NULL, NULL, NullHandler,
+ "DISPLAY", NULL, NULL, DisplayHandler,
+ "SET_TIME", NULL, NULL, NullHandler,
+ "SET", NULL, NULL, SetHandler,
+ "DRIVE", NULL, NULL, DriveHandler,
+ "FDISPLAY", NULL, NULL, DisplayHandler,
+ "FIRE", NULL, NULL, FireHandler,
+ "EXIT", NULL, NULL, ExitHandler,
+ "IF", NULL, NULL, IfHandler, // CM_IF
+ "ELSE", NULL, NULL, ElseHandler, // CM_ELSE
+ "END", NULL, NULL, EndHandler, // CM_END
+ "INCLUDE", NULL, NULL, IncludeHandler,
+ "MACHINE", NULL, NULL, MachineHandler,
+ "MAP", NULL, NULL, MapHandler,
+ "PAUSE", NULL, NULL, PauseHandler,
+ "COMPATIBLE", NULL, NULL, CompatibleHandler,
+ "PCCOMPATIBLE", NULL, NULL, CompatibleHandler,
+ "REMARK", NULL, NULL, NullHandler,
+ "REM", NULL, NULL, NullHandler,
+ "SHIFT", NULL, NULL, ShiftHandler,
+ "WAIT", NULL, NULL, PauseHandler,
+ "WRITE", NULL, NULL, WriteHandler,
+ "GOTO", NULL, NULL, GotoHandler,
+ "CLS", NULL, NULL, ClearHandler,
+ "CLEAR", NULL, NULL, ClearHandler,
+ "SWAP", NULL, NULL, NullHandler,
+ "LASTLOGIN", NULL, NULL, LastLoginTimeHandler, // 38
+ "CONTEXT", NULL, NULL, ContextHandler, // 39
+ "SCRIPT_SERVER", NULL, NULL, ScriptServerHandler, // 40
+ "NO_DEFAULT", NULL, NULL, NoDefaultHandler, // 41
+ "CX", NULL, NULL, ContextHandler, // 42
+ "PATH", NULL, NULL, MapHandler, // 43
+};
+
+typedef struct tagVARTABLE
+{
+ char *VarName;
+}VARTABLE;
+
+VARTABLE varTable[NUMVAR] =
+{
+ "DAY_OF_WEEK",
+ "DAY",
+ "MONTH_NAME",
+ "MONTH",
+ "NDAY_OF_WEEK",
+ "SHORT_YEAR",
+ "YEAR",
+ "AM_PM",
+ "GREETING_TIME",
+ "HOUR24",
+ "HOUR",
+ "MINUTE",
+ "SECOND",
+ "FULL_NAME",
+ "LOGIN_NAME",
+ "USER_ID",
+ "PASSWORD_EXPIRES",
+ "NETWORK_ADDRESS",
+ "FILE_SERVER",
+ "ACCESS_SERVER",
+ "ERROR_LEVEL",
+ "ERRORLEVEL",
+ "MACHINE",
+ "OS_VERSION",
+ "OS",
+ "SMACHINE",
+ "SHELL_TYPE",
+ "STATION",
+ "P_STATION",
+ "SHELL_VERSION",
+ "LAST_NAME",
+ "LOGIN_CONTEXT",
+ "NETWARE_REQUESTER",
+ "REQUESTER_CONTEXT",
+ "ACCOUNT_BALANCE",
+ "CN",
+ "REQUESTER_VERSION",
+ "SURNAME",
+ "DOS_REQUESTER",
+ "REQUESTER",
+ "ADMINISTRATIVE_ASSISTANT",
+ "ALLOW_UNLIMITED_CREDIT",
+ "DESCRIPTION",
+ "EMAIL_ADDRESS",
+ "EMPLOYEE_ID",
+ "FACSIMILE_TELEPHONE_NUMBER",
+ "GROUP_MEMBERSHIP",
+ "HIGHER_PRIVILEGES",
+ "HOME_DIRECTORY",
+ "INITIALS",
+ "LANGUAGE",
+ "LOCKED_BY_INTRUDER",
+ "LOGIN_DISABLED",
+ "LOGIN_GRACE_LIMIT",
+ "LOGIN_GRACE_REMAINING",
+ "LOGIN_INTRUDER_ATTEMPTS",
+ "LOGIN_MAXIMUM_SIMULTANEOUS",
+ "MAILSTOP",
+ "MESSAGE_SERVER",
+ "MINIMUM_ACCOUNT_BALANCE",
+ "NETWORK",
+ "OBJECT_CLASS",
+ "OU",
+ "PASSWORD_ALLOW_CHANGE",
+ "PASSWORD_MINIMUM_LENGTH",
+ "PASSWORD_REQUIRED",
+ "PASSWORD_UNIQUE_REQUIRED",
+ "PASSWORDS_USED",
+ "PHYSICAL_DELIVERY_OFFICE_NAME",
+ "POSTAL_ADDRESS",
+ "POSTAL_CODE",
+ "POSTAL_OFFICE_BOX",
+ "PRIVATE_KEY",
+ "PROFILE",
+ "REVISION",
+ "SECURITY_EQUALS",
+ "SECURITY_FLAGS",
+ "SEE_ALSO",
+ "SERVER_HOLDS",
+ "SUPERVISOR",
+ "TELEPHONE_NUMBER",
+ "TITLE",
+ "CERTIFICATE_VALIDITY_INTERVAL",
+ "EQUIVALENT_TO_ME",
+ "GENERATIONAL_QUALIFIER",
+ "GIVEN_NAME",
+ "MAILBOX_ID",
+ "MAILBOX_LOCATION",
+ "PROFILE_MEMBERSHIP",
+ "SA",
+ "S",
+ "L",
+ "ACCESS",
+};
+
+/*
+ * Local functions.
+ */
+void SmartCap(char *ptr);
+int NWGetFileSize (char * lpFileName);
+void LoadFile (char *lpFileName, char *lpFileBuffer, int nFileSize);
+int ProcessLoginScriptFile (char *lpLoginScriptFile);
+void ProcessLoginScript (char *lpLoginScript);
+int ProcessLoginScriptProperty (unsigned char *);
+
+int CreateLabelList (PLABEL_LIST *ppLabelList, char *lpLoginScript);
+void FreeLabelList (LABEL_LIST *pLabelList);
+
+void ExternalCmdHandler(char *lpCommand);
+void BadCommandHandler (char *lpCommand);
+
+void CommandDispatch (char *lpCommand);
+int GetTableIndex(char *lpCommand, char ** prestbuffer);
+
+DWORD SwapLong(DWORD number);
+int EndOfLine (char *buffer);
+char *RemoveSpaces (char * buffer);
+int IsOn (char *lpParam);
+int IsOff (char *lpParam);
+int VarTranslate(char *vartext);
+int QuotedStringTranslate (char *buffer);
+void NotQuotedStringTranslate(char *buffer, BOOL remove_dbs);
+void SetLocalEnv(char *buffer);
+int SetEnv (char *lpEnvLine);
+char *ConvertPercent (char *buffer);
+void GetShellVersion(char *buffer, int index);
+
+/*
+ * Global Defines
+ */
+#define IsWhiteSpace(x) ((x==' ')||(x=='\t')||(x=='\n')||(x=='\r')||(x==0))
+
+/*
+ * Global variables.
+ */
+
+//
+// The following globals are used for goto processing... this allows us
+// to manipulate the line we're processing outside ProcessLoginScript.
+//
+
+LABEL_LIST *pGlobalLabelList;
+char *lpGlobalLine;
+char *lpGlobalLineSeparator;
+int fGlobalHaveNulledLineSeparator;
+int fGlobalExitFlag;
+int fGlobalIfTooDeep;
+
+int fBreakOn = TRUE;
+
+int nCondIndex;
+int aCondVal[MAX_NUM_IF];
+
+int ARGC;
+char **ARGV;
+int nGlobalShiftDelta = 0;
+int fGlobalCompatible = FALSE;
+int fNoDefaultLoginScript = FALSE;
+
+char *LOGIN_NAME;
+char *LAST_NAME;
+char *LOGIN_CONTEXT;
+char *REQUESTER_CONTEXT;
+char *COMMON_NAME;
+char *TYPED_USER_NAME;
+PWCHAR TYPED_USER_NAME_w;
+PBYTE NDSTREE;
+PBYTE PREFERRED_SERVER;
+
+HANDLE hconout = INVALID_HANDLE_VALUE;
+
+unsigned int CONNECTION_ID;
+unsigned int CONNECTION_NUMBER;
+unsigned int SCRIPT_ERROR = 0;
+
+#define REQUESTER_VERSION "V1.20"
+
+extern DWORD GUserObjectID;
+
+int IsEmptyFile (char *lpFile)
+{
+ while (*lpFile != 0)
+ {
+ if (*lpFile != ' ' &&
+ *lpFile != '\t'&&
+ *lpFile != '\n'&&
+ *lpFile != '\r')
+ {
+ return(FALSE);
+ }
+ lpFile++;
+ }
+
+ return(TRUE);
+}
+
+/*
+ * Login was successful. Process both Login Scripts: the System Login
+ * Script and the User Login Script. If there is an EXIT command in the
+ * System Login Script, then we do not process the User Login Script.
+ * If there is no User Login Script, we process a default Login Script,
+ * which is hard-coded internally. See the Login Script appendix in
+ * the NetWare Installation guide for more info.
+ */
+void ProcessLoginScripts (unsigned int conn, char *UserName, int argc, char ** argv, char *lpScript)
+{
+ unsigned int iRet = 0;
+ unsigned long userID ;
+ char pchUserLoginScriptFile[24];
+
+ // Initalize LOGIN_NAME, CONNECTION_ID and CONNECTION_NUMBER.
+ ARGC = argc;
+ ARGV = argv;
+ LOGIN_NAME = UserName;
+ CONNECTION_ID = conn;
+
+ // Initialize some 4X variables
+ if ( fNDS )
+ {
+ COMMON_NAME = UserName;
+
+ LOGIN_CONTEXT = malloc ( CONTEXT_MAX );
+ strcpy( LOGIN_CONTEXT, REQUESTER_CONTEXT );
+
+ LAST_NAME = malloc( MAXLEN );
+ NDSGetVar ( "SURNAME", LAST_NAME, MAXLEN );
+ }
+ else {
+ LAST_NAME = UserName;
+ COMMON_NAME = UserName;
+ LOGIN_CONTEXT = "";
+ REQUESTER_CONTEXT = "";
+ }
+
+ if (iRet = GetConnectionNumber (conn, &CONNECTION_NUMBER))
+ {
+ DisplayError (iRet, "GetConnectionNumber");
+ return;
+ }
+
+ if (lpScript)
+ {
+ if (!ProcessLoginScriptFile(lpScript))
+ DisplayMessage(IDR_NO_SCRIPT_FILE, lpScript);
+ }
+ else
+ {
+ if ( fNDS )
+ {
+ unsigned char Object[128];
+ unsigned char ProfileObject[256];
+ PBYTE p;
+ int err;
+
+ // Browse back from user's node to first occurrence
+ // or organizational unit or organization and look for
+ // system script there. If the nearest OU or O doesn't have
+ // a system script, don't run one.
+
+ for ( p = TYPED_USER_NAME; p ; p = strchr ( p, '.' ) )
+ {
+
+ p++;
+
+ if ( *p == 'O' && *(p+1) == 'U' && *(p+2) == '=' )
+ break;
+
+ if ( *p == 'O' && *(p+1) == '=' )
+ break;
+ }
+
+ if ( p != NULL )
+ {
+ ProcessLoginScriptProperty( p );
+ }
+
+ // profile login script.
+
+ if ( !NDSGetUserProperty ( "Profile", ProfileObject, 256, NULL, NULL) )
+ {
+ ConvertUnicodeToAscii( ProfileObject );
+ ProcessLoginScriptProperty( ProfileObject );
+ }
+
+ // user login script
+
+ if ( (!ProcessLoginScriptProperty( TYPED_USER_NAME )) &&
+ (!fNoDefaultLoginScript) )
+ {
+ ProcessLoginScript (DefaultLoginScript);
+ }
+ }
+ else
+ {
+ static char SysLoginScriptFile[] = "SYS:PUBLIC/NET$LOG.DAT" ;
+
+ // Process system login script file.
+ ProcessLoginScriptFile (SysLoginScriptFile);
+
+ // Check if user login script exists.
+ if (iRet = GetBinderyObjectID (conn, UserName, OT_USER,
+ &userID))
+ return;
+
+ sprintf(pchUserLoginScriptFile, "SYS:MAIL/%lx/LOGIN", SwapLong(userID));
+
+ if ( (!ProcessLoginScriptFile (pchUserLoginScriptFile)) &&
+ (!fNoDefaultLoginScript) )
+ {
+ ProcessLoginScript (DefaultLoginScript);
+ }
+ }
+ }
+}
+
+int ProcessLoginScriptFile (char *lpLoginScriptFile)
+{
+ int nFileSize = 0, bEmpty;
+ char *lpLoginScript;
+
+ nFileSize = NWGetFileSize (lpLoginScriptFile);
+
+ if (nFileSize <= 2)
+ return(FALSE);
+
+ // system login script exists.
+ lpLoginScript = malloc (nFileSize);
+ if (lpLoginScript == NULL)
+ {
+ DisplayMessage(IDR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+
+ LoadFile (lpLoginScriptFile, lpLoginScript, nFileSize);
+
+ bEmpty = IsEmptyFile(lpLoginScript);
+
+ if (!bEmpty)
+ ProcessLoginScript (lpLoginScript);
+
+ free (lpLoginScript);
+
+ return(!bEmpty);
+}
+
+
+/*
+ * Retrieve and process the Login Script property
+ */
+int ProcessLoginScriptProperty ( unsigned char * Object )
+{
+ unsigned int nFileSize = 0;
+ unsigned int Actual = 0;
+ unsigned int bEmpty;
+ char *lpLoginScript;
+ HANDLE Stream;
+ int err;
+ unsigned int i,j;
+
+ if ( NDSfopenStream ( Object, "Login Script", &Stream, &nFileSize ) )
+ return(FALSE);
+
+ if ( nFileSize <= 2)
+ return(FALSE);
+
+ // login script exists.
+ lpLoginScript = malloc (nFileSize+2);
+ if (lpLoginScript == NULL)
+ {
+ DisplayMessage(IDR_NOT_ENOUGH_MEMORY);
+ return FALSE;
+ }
+ memset(lpLoginScript, 0, nFileSize+2);
+
+ if ( !ReadFile ( Stream, lpLoginScript, nFileSize, &Actual, NULL ) )
+ {
+ bEmpty = TRUE;
+ }
+#ifdef BUGBUG // ReadFile is returning wrong lengths
+ else if ( Actual != nFileSize )
+ {
+ bEmpty = TRUE;
+ }
+#endif
+ else if ( IsEmptyFile(lpLoginScript) )
+ bEmpty = TRUE;
+ else
+ bEmpty = FALSE;
+
+ for ( i = 0, j = 0; i < nFileSize; i++, j++ )
+ {
+ if (( lpLoginScript[i] == '\r' ) &&
+ ( lpLoginScript[i+1] == '\n' ) )
+ i++;
+
+ lpLoginScript[j] = lpLoginScript[i];
+ }
+
+ while ( j < nFileSize )
+ {
+ lpLoginScript[j++] = 0;
+ }
+
+ CloseHandle( Stream );
+
+ if (!bEmpty)
+ ProcessLoginScript (lpLoginScript);
+
+ free (lpLoginScript);
+
+ return(!bEmpty);
+}
+
+/*
+ * Return the size of the file.
+ */
+int NWGetFileSize (char * lpFileName)
+{
+ int nFileSize = 0;
+ FILE * stream;
+
+ do
+ {
+ if ((stream = fopen (NTNWtoUNCFormat(lpFileName), "r")) == NULL)
+ break;
+
+ while (feof (stream) == 0)
+ {
+ fgetc (stream);
+ nFileSize++;
+ }
+
+ if (fclose (stream))
+ nFileSize = 0;
+ }while (FALSE);
+
+
+ return(nFileSize);
+}
+
+/*
+ * Read the file into memory pointed by lpFileBuffer.
+ */
+void LoadFile (char *lpFileName, char *lpFileBuffer, int nFileSize)
+{
+ FILE * stream;
+
+ if ((stream = fopen (NTNWtoUNCFormat(lpFileName), "r")) != NULL)
+ {
+ fread (lpFileBuffer, sizeof (char), nFileSize, stream);
+ fclose (stream);
+ }
+
+ *(lpFileBuffer+nFileSize-1) = 0;
+
+}
+
+/*
+ * Process Login Script that is in memory pointed by lpLoginScript
+ * line by line.
+ */
+void ProcessLoginScript (char *lpLoginScript)
+{
+ nCondIndex = -1;
+ fGlobalExitFlag = FALSE;
+ fGlobalIfTooDeep = FALSE;
+
+ lpGlobalLine = lpLoginScript; // we start at the top of the login script
+
+ if (!CreateLabelList (&pGlobalLabelList, lpLoginScript))
+ {
+ if (pGlobalLabelList != NULL) {
+
+ FreeLabelList (pGlobalLabelList);
+ pGlobalLabelList = NULL;
+ }
+ return;
+ }
+
+ while (*lpGlobalLine != 0) {
+
+ //
+ // search for the end of the current line and replace with a null
+ //
+
+ if (lpGlobalLineSeparator = strchr(lpGlobalLine, '\n')) {
+
+ //
+ // we may reset this manually in the goto handler, remember so that
+ // we don't trample anything needlessly.
+ //
+
+ *lpGlobalLineSeparator = 0;
+ fGlobalHaveNulledLineSeparator = TRUE;
+
+ } else {
+
+ fGlobalHaveNulledLineSeparator = FALSE;
+ }
+
+ //
+ // Now lpGlobalLine points to one line only.
+ //
+
+ CommandDispatch (lpGlobalLine);
+
+ if (fGlobalExitFlag)
+ {
+ if (fGlobalIfTooDeep)
+ DisplayMessage(IDR_ORIGINAL_LINE_WAS, lpGlobalLine);
+ break;
+ }
+
+ if (lpGlobalLineSeparator) {
+
+ if ( fGlobalHaveNulledLineSeparator ) {
+
+ *lpGlobalLineSeparator = '\n'; // recover the changes made.
+ fGlobalHaveNulledLineSeparator = FALSE;
+ }
+
+ lpGlobalLine = lpGlobalLineSeparator + 1; // next line please
+
+ } else {
+
+ break;
+ }
+ }
+
+ if (pGlobalLabelList != NULL) {
+ FreeLabelList (pGlobalLabelList);
+ pGlobalLabelList = NULL;
+ }
+}
+
+/*
+ * Scan the login script, put labels in a link list and comment out
+ * those label lines.
+ */
+int CreateLabelList (PLABEL_LIST *ppLabelList, char *lpLoginScript)
+{
+ char *lpLine = lpLoginScript, *lpEnd, *lpLabel, *lpTemp;
+ int nLen;
+ PLABEL_LIST *ppNext = ppLabelList;
+
+ while (*lpLine != 0)
+ {
+ if (lpEnd = strchr (lpLine, '\n'))
+ *lpEnd = 0;
+
+ // Now lpLine points to one line only.
+ lpLabel = RemoveSpaces (lpLine);
+ if (isalnum (*lpLabel) || (*lpLabel == '%'))
+ {
+ lpTemp = lpLabel;
+ nLen = 0;
+ while (*lpTemp != 0 && *lpTemp != ' ' && *lpTemp != '\t' && *lpTemp != ':')
+ {
+ if (IsDBCSLeadByte(*lpTemp))
+ {
+ lpTemp++;
+ nLen++;
+ }
+
+ lpTemp++;
+ nLen++;
+ }
+
+ lpTemp = RemoveSpaces (lpTemp);
+ if (*lpTemp == ':' && EndOfLine (lpTemp+1))
+ {
+ // The Line is label line.
+ if ((*ppNext = malloc (sizeof (LABEL_LIST))) == NULL ||
+ ((*ppNext)->pLabel = malloc (nLen+1)) == NULL)
+ {
+ DisplayMessage(IDR_NOT_ENOUGH_MEMORY);
+ return(FALSE);
+ }
+
+ SmartCap(lpLabel);
+ strncpy ((*ppNext)->pLabel, lpLabel, nLen);
+ *((*ppNext)->pLabel+nLen) = 0;
+ (*ppNext)->pNextLine = lpEnd? lpEnd+1 : lpEnd;
+ (*ppNext)->pNext = NULL;
+ ppNext = &((*ppNext)->pNext);
+
+ // Comment out the label line.
+ *(lpLine) = ';';
+ }
+ }
+
+ if (lpEnd)
+ {
+ *lpEnd = '\n'; // recover the changes made.
+ lpLine = lpEnd+1;
+ }
+ else
+ break;
+ }
+
+ return(TRUE);
+}
+
+/*
+ * Free up the memory allocated for the link list.
+ */
+void FreeLabelList (LABEL_LIST *pLabelList)
+{
+ LABEL_LIST *pNext = pLabelList;
+
+ while (pLabelList)
+ {
+ pNext = pLabelList->pNext;
+ free (pLabelList->pLabel);
+ free (pLabelList);
+ pLabelList = pNext;
+ }
+}
+
+/*
+ * Dispatch to command hander according to the command.
+ */
+void CommandDispatch (char *lpCommand)
+{
+ char buffer[MAXLEN];
+ char *restBuffer;
+ int index, fCommandHandled = FALSE;
+ int nTemp = -1;
+
+ // Get rid of leading spaces.
+ lpCommand = RemoveSpaces(lpCommand);
+
+ // Don't do anything if it's a comment line or empty line.
+ if (*lpCommand == ';' || *lpCommand == '*' || *lpCommand == '\0' ||
+ *lpCommand == '\r'|| *lpCommand == '\n')
+ return;
+
+ do // FALSE loop.
+ {
+ // Make sure the command line is not too long to process.
+ if (strlen (lpCommand) > MAXLEN -1) {
+ break;
+ }
+
+ // Make a copy of the command line to buffer.
+ strcpy (buffer, lpCommand);
+
+ // external command line.
+ if (*buffer == '#')
+ {
+ ExternalCmdHandler (buffer);
+ return;
+ }
+
+ // Get the command index in the command table.
+ if ((index = GetTableIndex(buffer, &restBuffer)) == -1)
+ break;
+
+ // Dispatch to the corresponding command handler.
+ if (nCondIndex > -1 &&
+ !aCondVal[nCondIndex] &&
+ index != CM_IF &&
+ index != CM_ELSE &&
+ index != CM_END)
+ fCommandHandled = TRUE;
+ else
+ fCommandHandled = (*nwCommand[index].commandhandler)(restBuffer);
+
+ } while (FALSE);
+
+ if (!fCommandHandled) {
+ BadCommandHandler (lpCommand);
+ }
+}
+
+/*
+ * Used by GetTableIndex().
+ * This function should capitalize the entire command string except
+ * those in quotes.
+ */
+void SmartCap(char *ptr)
+{
+ int inquotes = (*ptr == '\"');
+ char *pNext;
+
+ while (*ptr)
+ {
+ if (!inquotes)
+ {
+ if (IsDBCSLeadByte(*ptr))
+ *((int *)ptr) = toupper ((int)*ptr);
+ else
+ *ptr = (char) toupper((int)*ptr);
+ }
+
+ pNext = NWAnsiNext(ptr);
+
+ if (*pNext == '\"' && *ptr != '\\')
+ inquotes = !inquotes;
+
+ ptr = pNext;
+ }
+}
+
+/*
+ * Return the index of the command in the command dispatch table.
+ * Return -1 if the command is not found in the command dispatch table.
+ */
+int GetTableIndex(char *buffer, char **prestBuffer)
+{
+ int i, nStrLen;
+
+ // Upcase every thing except those in quotes.
+ SmartCap (buffer);
+
+ for (i=0; i<NUMCOMMAND; i++)
+ {
+ if (*(WORD *)nwCommand[i].commandStr0 != *(WORD *)buffer)
+ continue;
+
+ nStrLen = strlen (nwCommand[i].commandStr0);
+
+ if (strncmp(nwCommand[i].commandStr0, buffer, nStrLen))
+ continue;
+
+ *prestBuffer = buffer + nStrLen;
+ *prestBuffer = RemoveSpaces (*prestBuffer);
+
+ if (nwCommand[i].commandStr1)
+ {
+ nStrLen = strlen (nwCommand[i].commandStr1);
+
+ if (strncmp(nwCommand[i].commandStr1, *prestBuffer, nStrLen))
+ continue;
+
+ *prestBuffer += nStrLen;
+ *prestBuffer = RemoveSpaces (*prestBuffer);
+
+ if (nwCommand[i].commandStr2)
+ {
+ nStrLen = strlen (nwCommand[i].commandStr2);
+
+ if (strncmp(nwCommand[i].commandStr2, *prestBuffer, nStrLen))
+ continue;
+
+ *prestBuffer += nStrLen;
+ *prestBuffer = RemoveSpaces (*prestBuffer);
+ }
+ }
+
+ return (i);
+ }
+
+ return(-1);
+}
+
+/*
+ * Goto label... We modify the globals controlling what line we're on.
+ */
+int GotoHandler (char *lpParam)
+{
+ int fLabelFound = FALSE;
+ char *lpLabel, *lpEnd, chEnd;
+ LABEL_LIST *pLabelList = pGlobalLabelList;
+
+ lpLabel = lpParam;
+ lpLabel = RemoveSpaces (lpLabel);
+
+ //
+ // find the end of the label, we'll slam in a null for the search and
+ // restore the char after we're done searching.
+ //
+
+ lpEnd = lpLabel;
+ while (*lpEnd != 0 &&
+ *lpEnd != ' ' &&
+ *lpEnd != '\t' &&
+ *lpEnd != '\r' &&
+ *lpEnd != '\n')
+ {
+ if (*lpEnd == ':')
+ return(FALSE);
+ else
+ lpEnd = NWAnsiNext(lpEnd);
+ }
+
+ chEnd = *lpEnd;
+ *lpEnd = 0;
+
+ while (pLabelList)
+ {
+ if (!_stricmp (pLabelList->pLabel, lpLabel))
+ {
+ if ( fGlobalHaveNulledLineSeparator )
+ {
+
+ *lpGlobalLineSeparator = '\n'; // recover the changes made.
+ fGlobalHaveNulledLineSeparator = FALSE;
+ }
+
+ lpGlobalLine = pLabelList->pNextLine;
+
+ lpGlobalLineSeparator = lpGlobalLine ? (lpGlobalLine - 1) : NULL;
+
+ fLabelFound = TRUE;
+ break;
+ }
+
+ pLabelList = pLabelList->pNext;
+ }
+
+ if (!fLabelFound)
+ {
+ DisplayMessage (IDR_LABEL_NOT_FOUND, lpLabel);
+ fGlobalExitFlag = TRUE;
+ }
+
+ *lpEnd = chEnd;
+ return( TRUE );
+}
+
+/*
+ * Attach [FileServer[/UserName[;Password]]]
+ */
+int AttachHandler (char *lpParam)
+{
+ unsigned int iRet = 0;
+ int fCommandHandled = FALSE;
+ char serverName[MAX_NAME_LEN] = "";
+ char userName[MAX_NAME_LEN] = "";
+ char password[MAX_PASSWORD_LEN] = "";
+ char *lpSlash, *lpSemiColon, *lpServerName, *lpUserName;
+ unsigned int conn;
+ int bAlreadyAttached = FALSE, bReadPassword = TRUE;
+
+ do // FALSE loop.
+ {
+ NotQuotedStringTranslate (lpParam, TRUE);
+
+ // Make sure that there is at most 1 slash.
+ lpSlash = strchr (lpParam, '\\');
+
+ if (lpSlash == NULL)
+ {
+ lpSlash = strchr (lpParam, '/');
+ if (lpSlash != NULL && strchr (lpSlash+1, '/'))
+ break;
+ }
+ else
+ {
+ if (strchr (lpParam, '/') ||
+ strchr (lpSlash+1, '/') ||
+ strchr (lpSlash+1, '\\'))
+ break;
+ }
+
+ // Break the string at slash.
+ if (lpSlash)
+ *lpSlash = 0;
+
+ // Server name should not contain semicolon.
+ if (strchr (lpParam, ';'))
+ break;
+
+ lpServerName = strtok (lpParam, __SPACES__);
+
+ if (lpServerName = NULL)
+ {
+ if (lpSlash)
+ break;
+ }
+ else
+ {
+ // Make sure that there is only one name in front of the slash.
+ if (strtok (NULL, __SPACES__))
+ break;
+
+ // Copy the server name to the buffer.
+ if (strlen (lpParam) > MAX_NAME_LEN-1)
+ break;
+
+ strcpy (serverName, lpParam);
+
+ if (lpSlash)
+ {
+ lpSemiColon = strchr (lpSlash+1, ';');
+ if (lpSemiColon)
+ *lpSemiColon = 0;
+
+ lpUserName = strtok (lpSlash+1, __SPACES__);
+ if (lpUserName)
+ {
+ if ( strtok (NULL, __SPACES__))
+ break;
+
+ if (strlen (lpUserName) > MAX_NAME_LEN-1 )
+ break;
+
+ strcpy (userName, lpUserName);
+ }
+
+ if (lpSemiColon)
+ {
+ if (strlen (lpSemiColon+1) > MAX_PASSWORD_LEN-1)
+ break;
+ strcpy (password, strtok (lpSemiColon+1, __SPACES__));
+ xstrupr (password);
+ bReadPassword = FALSE;
+ }
+ }
+ }
+
+ fCommandHandled = TRUE;
+
+ if (serverName[0] == 0)
+ {
+ DisplayMessage(IDR_ENTER_SERVER_NAME);
+ if (!ReadName(serverName))
+ break;
+
+ DisplayMessage(IDR_ENTER_LOGIN_NAME, serverName);
+ if (!ReadName(userName))
+ break;
+ }
+ else if (userName[0] == 0)
+ strcpy (userName, LOGIN_NAME);
+
+ if (iRet = CAttachToFileServer(serverName, &conn, &bAlreadyAttached))
+ {
+ if (!SCRIPT_ERROR)
+ SCRIPT_ERROR = iRet;
+ break;
+ }
+
+ // Do not need this connection
+ DetachFromFileServer (conn);
+
+#ifdef BUGBUG // not reliable
+ if (bAlreadyAttached)
+ {
+ DisplayMessage(IDR_ATTACHED, serverName);
+ break;
+ }
+#endif
+
+ if (Login(userName, serverName, password, bReadPassword))
+ {
+ // Clear out the password
+ memset( password, 0, sizeof( password ) );
+
+ // Ask for user name
+
+ DisplayMessage(IDR_ENTER_LOGIN_NAME, serverName);
+ if (!ReadName(userName))
+ break;
+
+ if (Login(userName, serverName, password, bReadPassword))
+ {
+ // Clear out the password
+ memset( password, 0, sizeof( password ) );
+ break;
+ }
+ }
+
+ // Clear out the password
+
+ memset( password, 0, sizeof( password ) );
+
+ AddServerToAttachList( serverName, LIST_3X_SERVER );
+
+ } while (FALSE);
+
+ return(fCommandHandled);
+}
+
+/*
+ * BREAK ON, enable ctrl-c, ctrl-break
+ * BREAK OFF, disable ctrl-c, ctrl-break
+ */
+int BreakHandler (char *lpParam)
+{
+ int fCommandHandled = TRUE;
+
+ if (IsOn(lpParam))
+ {
+ if (!fBreakOn)
+ BreakOn();
+ }
+ else if (IsOff(lpParam))
+ {
+ if (fBreakOn)
+ BreakOff();
+ }
+ else
+ fCommandHandled = FALSE;
+
+ return(fCommandHandled);
+}
+
+
+/*
+ * DISPLAY [pathname]file
+ * FDISPLAY [pathname]file
+ */
+int DisplayHandler (char *lpParam)
+{
+ FILE * stream;
+
+ NotQuotedStringTranslate (lpParam, TRUE);
+
+ if ((stream = fopen (lpParam, "r")) != NULL)
+ {
+ while (feof (stream) == 0)
+ _fputchar(fgetc (stream));
+
+ fclose (stream);
+ DisplayMessage(IDR_NEWLINE);
+ }
+
+ return(TRUE);
+}
+
+/*
+ * DOS BREAK ON, enable ctrl-break checking for DOS
+ * DOS BREAK OFF, disable ctrl-break checking for DOS
+ */
+int DosBreakHandler (char *lpParam)
+{
+ int fCommandHandled = TRUE;
+
+ if (IsOn (lpParam))
+ system ("BREAK ON");
+ else if(IsOff (lpParam))
+ system ("BREAK OFF");
+ else
+ fCommandHandled = FALSE;
+
+ return(fCommandHandled);
+}
+
+/*
+ * Used by SetHandler() and LocalSetHandler()
+ * Return TRUE if lpParam points to name = "value", and set
+ * lpParam to "name=value" on return.
+ * Return FALSE otherwise.
+ */
+int VerifySetFormat (char *lpParam)
+{
+ int fCorrect = FALSE;
+ char buffer[MAXLEN];
+ char *lpBuffer = buffer;
+
+ strcpy (buffer, lpParam);
+
+ do
+ {
+ while (*lpBuffer != 0 && *lpBuffer != '=' && *lpBuffer != ' ' && *lpBuffer != '\t')
+ lpBuffer = NWAnsiNext(lpBuffer);
+
+ lpParam[lpBuffer-buffer]=0;
+ strcat (lpParam, "=");
+
+ if (*lpBuffer != '=')
+ lpBuffer = RemoveSpaces (lpBuffer);
+
+ if (*lpBuffer != '=')
+ break;
+
+ lpBuffer = RemoveSpaces (lpBuffer+1);
+
+ if (*lpBuffer)
+ {
+ if (!QuotedStringTranslate (lpBuffer))
+ break;
+
+ strcat (lpParam, lpBuffer);
+ }
+
+ fCorrect = TRUE;
+ }while (FALSE);
+
+ return(fCorrect);
+}
+
+/*
+ * Used by SetHandler() and LocalSetHandler()
+ * Set the local environment variable.
+ * Don't free the memory allocated because the environment variable will
+ * point to free space otherwise.
+ */
+void SetLocalEnv(char *buffer)
+{
+ char *lpEnvString;
+ lpEnvString = malloc(strlen (buffer) + 1);
+
+ if (lpEnvString == NULL)
+ DisplayMessage(IDR_NOT_ENOUGH_MEMORY);
+ else
+ {
+ strcpy (lpEnvString, buffer);
+ _putenv (lpEnvString);
+ }
+}
+
+/*
+ * Set Dos environment variable.
+ * [DOS] SET name = "value"
+ */
+int SetHandler (char *lpParam)
+{
+
+ int fCommandHandled;
+
+ fCommandHandled = VerifySetFormat(lpParam);
+
+ if (fCommandHandled)
+ {
+
+ if ( _strnicmp( "COMSPEC=", lpParam, strlen( "COMSPEC=" ) ) )
+ {
+ SetLocalEnv(lpParam);
+ SetEnv (lpParam);
+ }
+ }
+
+ return(fCommandHandled);
+}
+
+/*
+ * Set local Dos environment variable.
+ * [OPTION] [DOS] SET name = "value"
+ */
+int LocalSetHandler (char *lpParam)
+{
+ int fCommandHandled;
+
+ fCommandHandled = VerifySetFormat(lpParam);
+
+ if (fCommandHandled)
+ if ( _strnicmp( "COMSPEC=", lpParam, strlen( "COMSPEC=" ) ) )
+ {
+ SetLocalEnv (lpParam);
+ }
+
+ return(fCommandHandled);
+}
+
+/*
+ * Used by DosVerifyHandler().
+ * Turn /V option of copy on.
+ */
+void DosVerifyOn(void)
+{
+}
+
+/*
+ * Used by DosVerifyHandler().
+ * Turn /V option of copy off.
+ */
+void DosVerifyOff(void)
+{
+}
+
+/*
+ * DOS VERYFY [ON|OFF], Turn /V option of copy on or off.
+ */
+int DosVerifyHandler (char *lpParam)
+{
+ int fCommandHandled = TRUE;
+
+ if (IsOn(lpParam))
+ DosVerifyOn();
+ else if (IsOff(lpParam))
+ DosVerifyOff();
+ else
+ fCommandHandled = FALSE;
+
+ return(fCommandHandled);
+}
+
+/*
+ * DRIVE [driveletter: | n*:], set the default drive to the one specified.
+ */
+int DriveHandler (char *lpParam)
+{
+ int fCommandHandled = FALSE;
+ WORD driveNum=0, n;
+ char *pColon;
+
+ do // FALSE loop.
+ {
+ if ((pColon = strchr (lpParam, ':')) == NULL ||
+ !EndOfLine (pColon + 1))
+ break;
+
+ if (*lpParam == '*')
+ {
+ *pColon = 0;
+ if ((n = atoi (lpParam+1)) < 1)
+ break;
+
+ GetFirstDrive (&driveNum);
+ driveNum += (n-1);
+ }
+ else if (pColon == lpParam+1 && isupper(*lpParam))
+ driveNum = *lpParam - 'A' + 1;
+ else
+ break;
+
+ if (_chdrive (driveNum))
+ DisplayMessage(IDR_ERROR_SET_DEFAULT_DRIVE, 'A'+driveNum-1);
+ else
+ ExportCurrentDrive( driveNum );
+
+ fCommandHandled = TRUE;
+
+ } while (FALSE);
+
+ return(fCommandHandled);
+}
+
+/*
+ * Used by FireHandler()
+ * Return TRUE if lpTemp points to the legal end of fire statement, ie
+ * [TIMES][COMMENTS]. It also set the *lpTemp to 0 if lpTemp is not NULL.
+ * Return FALSE otherwise.
+ */
+int IsEndOfFireCmd (char *lpTemp)
+{
+ int fEnd = FALSE;
+ do
+ {
+ if (*lpTemp != 0)
+ {
+ if (*lpTemp != ' ' && *lpTemp != '\t' && *lpTemp != '\r')
+ break;
+
+ *lpTemp = 0;
+
+ lpTemp = RemoveSpaces (lpTemp+1);
+
+ if (!strncmp (lpTemp, "TIMES", 5))
+ lpTemp += 5;
+
+ if (!EndOfLine (lpTemp))
+ break;
+ }
+
+ fEnd = TRUE;
+ }while (FALSE);
+
+ return(fEnd);
+}
+
+/*
+ * [FIRE | FIRE PHASERS] n TIMES.
+ */
+int FireHandler (char *lpParam)
+{
+ char *lpTemp, vartext[MAXLEN];
+ int n = 0, nLen;
+ time_t ltimeStart, ltimeEnd;
+
+ if (EndOfLine (lpParam))
+ n = 1;
+ else if (isdigit(*lpParam))
+ {
+ lpTemp = lpParam;
+ while (isdigit(*lpTemp))
+ lpTemp++;
+
+ if (IsEndOfFireCmd (lpTemp))
+ n = atoi (lpParam);
+ }
+ else if (*lpParam == '%')
+ {
+ strcpy (vartext, lpParam+1);
+ if (((nLen = VarTranslate (lpParam)) != 0) &&
+ EndOfLine (lpParam+1+nLen))
+ n = atoi (vartext);
+ }
+
+ if (n < 0)
+ return(FALSE);
+ else if (n == 0) // Compatible with NetWare.
+ n = 1;
+
+ while (n--)
+ {
+ _beep( 610, 100 );
+ _beep( 440, 50 );
+ time(&ltimeStart);
+ do
+ {
+ time(&ltimeEnd);
+ }while (ltimeEnd-ltimeStart == 0);
+ }
+
+ return(TRUE);
+}
+
+/*
+ * EXIT, terminate login script processing.
+ */
+int ExitHandler (char *lpParam)
+{
+ int n;
+ char buffer[16], *argv[10];
+
+ if (EndOfLine (lpParam))
+ CleanupExit(0);
+ else if (QuotedStringTranslate (lpParam))
+ {
+ if (!fGlobalCompatible)
+ {
+ GetShellVersion (buffer, IDS_MACHINE);
+ if (_stricmp (buffer, "IBM_PC"))
+ {
+ DisplayMessage(IDR_EXIT_NOT_SUPPORTED);
+ return(TRUE);
+ }
+ }
+
+ argv[0] = strtok (lpParam, __SPACES__);
+
+ for (n = 1; n < 9; n++)
+ {
+ if ((argv[n] = strtok (NULL, __SPACES__)) == NULL)
+ break;
+ }
+
+ argv[9] = NULL;
+
+ if ((SCRIPT_ERROR = _spawnvp (P_WAIT, argv[0], argv)) == -1)
+ DisplayMessage(IDR_BAD_COMMAND);
+
+ CleanupExit (0);
+ }
+ else
+ return(FALSE);
+}
+
+BOOL nwVarNameCompare(LPCSTR src,LPCSTR target)
+{
+ CHAR szTempName[64];
+ LPSTR pT = szTempName;
+
+ if (!_strnicmp(src,target,strlen(target))) {
+ //
+ // try to reject obvious problems like
+ // %LJUNK where %L would be fine
+ //
+ if ( !isalpha(src[strlen(target)]) )
+ return 0;
+ else
+ return 1;
+ }
+
+ strcpy(szTempName,target);
+
+ while (*pT) {
+ if (!IsDBCSLeadByte(*pT)) {
+ if ('_' == *pT)
+ *pT = ' ';
+ }
+ pT = NWAnsiNext(pT);
+ }
+
+ if (!_strnicmp(src,szTempName,strlen(szTempName))) {
+ //
+ // try to reject obvious problems like
+ // %LJUNK where %L would be fine
+ //
+ if ( !isalpha(src[strlen(target)]) )
+ return 0;
+ else
+ return 1;
+ }
+
+ return 1;
+}
+
+
+/*
+ * Used by the EvalSingleCond() in IfHandler()
+ * Return TRUE if buffer is the right member of condition statement.
+ * *pfCondition is TRUE if the condition meet, FALSE if not.
+ * *ppRest points to the end of the condition statement.
+ * Return FALSE if buffer is not the right member of condition statement.
+ */
+int MemberOf (char *buffer, int *pfCondition, char **ppRest)
+{
+ int i, nChar, fSucceed = FALSE;
+ char *lpTemp;
+ BYTE dataBuffer[128];
+ unsigned char moreFlag;
+ unsigned char propertyType;
+ unsigned long dwObjectId, *pdwGroups;
+ char GroupName[MAXLEN];
+ unsigned char segment;
+
+ *pfCondition = FALSE;
+ do
+ {
+ if ((buffer = strchr (buffer, '\"')) == NULL)
+ break;
+
+ if ((lpTemp = strchr (buffer+1, '\"')) == NULL)
+ break;
+
+ nChar = lpTemp - buffer + 1;
+
+ if (nChar >= MAXLEN)
+ break;
+
+ strncpy (GroupName, buffer, nChar);
+ GroupName[nChar] = 0;
+ if (!QuotedStringTranslate (GroupName))
+ break;
+
+ fSucceed = TRUE;
+ *pfCondition = FALSE;
+ *ppRest = RemoveSpaces (lpTemp+1);
+
+ if (strlen(GroupName) > MAX_NAME_LEN)
+ break;
+
+ if ( fNDS )
+ {
+ if ( IsMemberOfNDSGroup( GroupName ) )
+ {
+ *pfCondition = TRUE;
+ return(TRUE);
+ }
+
+ }
+ else
+ {
+ if (GetBinderyObjectID (CONNECTION_ID,
+ _strupr(GroupName),
+ OT_USER_GROUP,
+ &dwObjectId) )
+ goto done;
+
+ //
+ // For all the group ID's, try and find a match
+ //
+ for ( segment = 1, moreFlag = TRUE; moreFlag && segment; segment++ )
+ {
+ if ( NWReadPropertyValue ((NWCONN_HANDLE)CONNECTION_ID,
+ LOGIN_NAME,
+ OT_USER,
+ "GROUPS_I'M_IN",
+ segment,
+ dataBuffer,
+ &moreFlag,
+ &propertyType))
+ goto done;
+
+ pdwGroups = (unsigned long *) dataBuffer;
+
+ for (i = 0; i < 32 && *(pdwGroups+i); i++)
+ {
+ if (*(pdwGroups+i) == dwObjectId)
+ {
+ *pfCondition = TRUE;
+ return(TRUE);
+ }
+ }
+ }
+ }
+
+ *pfCondition = FALSE;
+ fSucceed = TRUE;
+ } while (FALSE);
+
+done:
+
+ return(fSucceed);
+}
+
+/*
+ * Used by IsCompare() in EvalSingleCond() in IfHandler()
+ * Return the next token.
+ */
+char *GetNextPart (char *lpTemp)
+{
+ INT i;
+
+ if (strncmp (lpTemp, "VALUE", 5) == 0)
+ lpTemp = RemoveSpaces (lpTemp+5);
+
+ if (*lpTemp == '\"')
+ {
+ lpTemp++;
+ while (*lpTemp != 0 && *lpTemp != '\"')
+ lpTemp = NWAnsiNext(lpTemp);
+
+ if (*lpTemp == 0)
+ return(NULL);
+ else
+ lpTemp++;
+ }
+ else if (*lpTemp == '<')
+ {
+ while (*lpTemp != 0 && *lpTemp != '>')
+ lpTemp = NWAnsiNext(lpTemp);
+
+ if (*lpTemp == 0)
+ return(NULL);
+ else
+ lpTemp++;
+ }
+ else
+ {
+ if (*lpTemp == '%')
+ lpTemp++;
+
+ for (i = 0; i < (fNDS ? NUMVAR : NUMVAR_3X); i++)
+ {
+ if (!nwVarNameCompare(lpTemp, varTable[i].VarName))
+ {
+ lpTemp += strlen(varTable[i].VarName);
+ break;
+ }
+ }
+
+ if (i == (fNDS ? NUMVAR : NUMVAR_3X))
+ return(NULL);
+ }
+
+ return(lpTemp);
+}
+
+/*
+ * Used by EvalSingleCond() in IfHandler()
+ * left part of buffer could be "...", <...>, or ... for variables.
+ * Return TRUE if buffer consists of <left> <compare operator> <right part> +
+ * optional rest parts.
+ * Return FALSE otherwise.
+ */
+int IsCompare (char *buffer, char **ppright,
+ int *pnLeftLen, int *pnRightLen,
+ int *pindex, char **ppRest)
+{
+ int i, nLen;
+ char *lpTemp;
+
+ if ((lpTemp = GetNextPart (buffer)) == NULL)
+ return (FALSE);
+
+ *pnLeftLen = lpTemp-buffer;
+ lpTemp = RemoveSpaces (lpTemp);
+
+ for (i = 0; COMPARE_OPERATORS[i][0]; i++)
+ {
+ nLen = strlen (COMPARE_OPERATORS[i]);
+
+ if (!strncmp(lpTemp, COMPARE_OPERATORS[i], nLen))
+ {
+ *lpTemp = 0;
+ lpTemp += nLen;
+ *ppright = RemoveSpaces (lpTemp);
+ *pindex = i;
+ *ppRest = GetNextPart (*ppright);
+ if ( *ppRest == NULL )
+ return (FALSE);
+ *pnRightLen = *ppRest - *ppright;
+ *ppRest = RemoveSpaces (*ppRest);
+ return(TRUE);
+ }
+ }
+
+ return(FALSE);
+}
+
+/*
+ * Used by EvalSingleCond() in IfHandler()
+ * Evaluate lpLeft and lpRight and do the compare operation of index
+ * and put the result in *pfCondition.
+ * Return TRUE if succeed, FALSE otherwise.
+ */
+int Compare (char *lpLeft, char *lpRight,
+ int nLeftLen, int nRightLen,
+ int index, int *pfCondition)
+{
+ char szLeft[MAXLEN], szRight[MAXLEN], *lpTemp;
+ int nCompare, fValue = FALSE;
+
+ if (strncmp (lpLeft, "VALUE", 5) == 0)
+ {
+ fValue = TRUE;
+ lpTemp = RemoveSpaces (lpLeft+5);
+ nLeftLen -= (lpTemp - lpLeft);
+ lpLeft = lpTemp;
+ }
+ if (strncmp (lpRight, "VALUE", 5) == 0)
+ {
+ fValue = TRUE;
+ lpTemp = RemoveSpaces (lpRight+5);
+ nRightLen -= (lpTemp - lpRight);
+ lpRight = lpTemp;
+ }
+
+ strncpy (szLeft, lpLeft, nLeftLen);
+ strncpy (szRight, lpRight, nRightLen);
+
+ szLeft[nLeftLen] = 0;
+ szRight[nRightLen] = 0;
+
+ if (!QuotedStringTranslate (szLeft) ||
+ !QuotedStringTranslate (szRight))
+ return(FALSE);
+
+ if (fValue)
+ nCompare = atoi(szLeft)-atoi(szRight);
+ else
+ nCompare = _stricmp (szLeft, szRight);
+
+ if (IsNotEqual(index))
+ *pfCondition = (nCompare != 0);
+ else if (IsGreaterOrEqual(index))
+ *pfCondition = (nCompare >= 0);
+ else if (IsGreater(index))
+ *pfCondition = (nCompare > 0);
+ else if (IsLessOrEqual(index))
+ *pfCondition = (nCompare <= 0);
+ else if (IsLess(index))
+ *pfCondition = (nCompare < 0);
+ else
+ *pfCondition = (nCompare == 0);
+
+ return(TRUE);
+}
+
+int IsMemberOf (char *buffer)
+{
+ int fIsMemberOf = FALSE;
+
+ if (!strncmp (buffer, "MEMBER", 6))
+ {
+ buffer += 6;
+ if (*buffer == ' ' || *buffer == '\t')
+ {
+ buffer = RemoveSpaces (buffer);
+ if (!strncmp (buffer, "OF", 2))
+ {
+ buffer += 2;
+ if (*buffer == ' ' || *buffer == '\t')
+ buffer = RemoveSpaces (buffer);
+ }
+ }
+
+ fIsMemberOf = (*buffer == '"');
+ }
+
+ return(fIsMemberOf);
+}
+
+int NotMemberOf (char *buffer)
+{
+ int fNotMemberOf = FALSE;
+ if (!strncmp (buffer, "NOT", 3))
+ {
+ buffer += 3;
+ if (*buffer == ' ' || *buffer == '\t')
+ {
+ buffer = RemoveSpaces (buffer);
+ fNotMemberOf = IsMemberOf (buffer);
+ }
+ }
+
+ return(fNotMemberOf);
+}
+
+
+/*
+ * Used by IfHandler()
+ * Evaluate one condition clause and put result in *pfCondition, *ppRest
+ * points to the rest part of buffer.
+ * Return TRUE if succeed, FALSE otherwise.
+ */
+int EvalSingleCond (char *buffer, int *pfCondition)
+{
+ int index, fSuccess = FALSE, nLeftLen, nRightLen;
+ char *pright, *pRest;
+
+ if (IsMemberOf(buffer))
+ fSuccess = MemberOf (buffer, pfCondition, &pRest);
+ else if (NotMemberOf (buffer))
+ {
+ fSuccess = MemberOf (buffer, pfCondition, &pRest);
+ *pfCondition = !(*pfCondition);
+ }
+ else if (IsCompare (buffer, &pright, &nLeftLen, &nRightLen, &index, &pRest))
+ fSuccess = Compare (buffer, pright, nLeftLen, nRightLen, index, pfCondition);
+ else if ( !_strnicmp ("ACCESS_SERVER", buffer, strlen("ACCESS_SERVER")) )
+ {
+ fSuccess = TRUE;
+ *pfCondition = FALSE;
+ pRest = buffer + strlen ("ACCESS_SERVER");
+ }
+
+ if (fSuccess)
+ memmove (buffer, pRest, strlen (pRest)+1);
+
+ return(fSuccess);
+}
+
+int EvaluateCondExpression(char *lpCondExpression, int *pfCondition)
+{
+ int fSuccess = FALSE, fCond;
+ char *lpRight, *lpLeft, *lpOp;
+
+ if (lpRight = strchr (lpCondExpression, ')'))
+ {
+ *lpRight = 0;
+ if ((lpLeft = strrchr (lpCondExpression, '(')) == NULL ||
+ !EvaluateCondExpression(lpLeft+1, pfCondition))
+ return(FALSE);
+
+ *lpLeft = (*pfCondition)? '1' : '0';
+ memmove (lpLeft+1, lpRight+1, strlen (lpRight+1)+1);
+ return(EvaluateCondExpression (lpCondExpression, pfCondition));
+ }
+
+ if (lpOp = strrchr (lpCondExpression, '+'))
+ {
+ *lpOp = 0;
+
+ if (!EvaluateCondExpression (lpCondExpression, pfCondition) ||
+ !EvaluateCondExpression (lpOp+1, &fCond))
+ return(FALSE);
+
+ *pfCondition = (*pfCondition || fCond);
+ return(TRUE);
+ }
+
+ if (lpOp = strrchr (lpCondExpression, '*'))
+ {
+ *lpOp = 0;
+
+ if (!EvaluateCondExpression (lpCondExpression, pfCondition) ||
+ !EvaluateCondExpression (lpOp+1, &fCond))
+ return(FALSE);
+
+ *pfCondition = (*pfCondition && fCond);
+ return(TRUE);
+ }
+
+ if (lpOp = strrchr (lpCondExpression, '^'))
+ {
+ *lpOp = 0;
+
+ if (!EvaluateCondExpression (lpCondExpression, pfCondition) ||
+ !EvaluateCondExpression (lpOp+1, &fCond))
+ return(FALSE);
+
+ *pfCondition = !(*pfCondition && fCond);
+ return(TRUE);
+ }
+
+ if (!strcmp (lpCondExpression, "1"))
+ {
+ *pfCondition = TRUE;
+ return(TRUE);
+ }
+ else if (!strcmp (lpCondExpression, "0"))
+ {
+ *pfCondition = FALSE;
+ return(TRUE);
+ }
+ else
+ return(FALSE);
+}
+
+/*
+ * Used by IfHandler()
+ * Evaluate up to 10 conditions.
+ * Return TRUE if succeed, FALSE otherwise.
+ * On return, buffer stores whatever after conditional expressions
+ * without leading spaces.
+ */
+int EvaluateCond(char *buffer, int *pfCondition)
+{
+ int fCondition = TRUE, fCurrent, fSucceed = FALSE, nCount;
+ char CondExpression[MAXLEN], *lpCond = CondExpression, *lpBuffer = buffer;
+
+ for (nCount = 0; nCount < 10; nCount++)
+ {
+ while (*lpBuffer == '(')
+ {
+ *lpCond = *lpBuffer;
+ lpCond++;
+ lpBuffer++;
+ }
+
+ lpBuffer = RemoveSpaces (lpBuffer);
+
+ if (!EvalSingleCond (lpBuffer, &fCurrent))
+ break;
+
+ *lpCond = fCurrent? '1' : '0';
+ lpCond++;
+
+ while (*lpBuffer == ')')
+ {
+ *lpCond = *lpBuffer;
+ lpCond++;
+ lpBuffer++;
+ }
+
+ lpBuffer = RemoveSpaces (lpBuffer);
+
+ if (*lpBuffer == ',')
+ {
+ *lpCond = '*';
+ lpCond++;
+
+ lpBuffer = RemoveSpaces (lpBuffer+1);
+
+ if (!strncmp (lpBuffer, "AND", 3))
+ lpBuffer = RemoveSpaces (lpBuffer+3);
+ }
+ else if (!strncmp (lpBuffer, "AND", 3))
+ {
+ *lpCond = '*';
+ lpCond++;
+ lpBuffer = RemoveSpaces (lpBuffer+3);
+ }
+ else if (!strncmp (lpBuffer, "&&", 2))
+ {
+ *lpCond = '*';
+ lpCond++;
+ lpBuffer = RemoveSpaces (lpBuffer+2);
+ }
+ else if ( (!strncmp (lpBuffer, "OR", 2)) ||
+ (!strncmp (lpBuffer, "||", 2)) )
+ {
+ *lpCond = '+';
+ lpCond++;
+ lpBuffer = RemoveSpaces (lpBuffer+2);
+ }
+ /*
+ * A NOR expression is documented in some books, but isn't
+ * implemented in the 4X login.exe I have.
+ */
+ else if (!strncmp (lpBuffer, "NOR", 3))
+ {
+ *lpCond = '^';
+ lpCond++;
+ lpBuffer = RemoveSpaces (lpBuffer+3);
+ }
+ else
+ {
+ fSucceed = TRUE;
+ *lpCond = 0;
+ lpBuffer = RemoveSpaces (lpBuffer);
+ memmove (buffer, lpBuffer, strlen (lpBuffer)+1);
+ break;
+ }
+ }
+
+ if (fSucceed)
+ fSucceed = EvaluateCondExpression (CondExpression, pfCondition);
+
+ return(fSucceed);
+}
+
+/*
+ * If statement handler.
+ */
+int IfHandler (char *lpParam)
+{
+ int fCommandHandled = FALSE, fCondition;
+
+ do
+ {
+ if (nCondIndex+1 == MAX_NUM_IF)
+ {
+ DisplayMessage(IDR_IF_TOO_DEEP);
+ fGlobalExitFlag = TRUE;
+ fGlobalIfTooDeep = TRUE;
+ return TRUE;
+ }
+
+ if (EndOfLine (lpParam))
+ break;
+
+ if (!EvaluateCond (lpParam, &fCondition))
+ break;
+
+ if (!strncmp (lpParam, "THEN", 4))
+ {
+ lpParam = RemoveSpaces (lpParam+4);
+
+ if (!strncmp (lpParam, "BEGIN", 5))
+ {
+ lpParam += 5;
+
+ if (!EndOfLine (lpParam))
+ break;
+ }
+ else if((!strncmp (lpParam, "DO", 2)) &&
+ (strncmp (lpParam, "DOS", 3)))
+ {
+ lpParam += 2;
+ if (!EndOfLine (lpParam))
+ break;
+ }
+ }
+ else if (!strncmp (lpParam, "BEGIN", 5))
+ {
+ lpParam += 5;
+
+ if (!EndOfLine (lpParam))
+ break;
+ }
+
+ if (EndOfLine (lpParam))
+ {
+ nCondIndex++;
+ aCondVal[nCondIndex] =
+ (nCondIndex > 0 && !aCondVal[nCondIndex-1])?
+ FALSE : fCondition;
+ }
+ else
+ {
+ if (fCondition && (nCondIndex == -1 || aCondVal[nCondIndex]))
+ CommandDispatch (lpParam);
+ }
+
+ fCommandHandled = TRUE;
+
+ }while (FALSE);
+
+ return(fCommandHandled);
+}
+
+/*
+ * Else statement handler.
+ */
+int ElseHandler (char *lpParam)
+{
+ int fCommandHandled = FALSE;
+
+ if (EndOfLine (lpParam))
+ {
+ if (nCondIndex == 0 ||
+ nCondIndex > 0 && aCondVal[nCondIndex-1])
+ aCondVal[nCondIndex] = !aCondVal[nCondIndex];
+
+ fCommandHandled = TRUE;
+ }
+
+ return(fCommandHandled);
+}
+
+/*
+ * End statement handler.
+ */
+int EndHandler (char *lpParam)
+{
+ int fCommandHandled = FALSE;
+
+ if (EndOfLine (lpParam))
+ {
+ if (nCondIndex > -1)
+ nCondIndex--;
+
+ fCommandHandled = TRUE;
+ }
+
+ return(fCommandHandled);
+}
+
+/*
+ * INCLUDE [pathname]filename
+ */
+int IncludeHandler (char *lpParam)
+{
+ int fCommandHandled = FALSE, nFileSize;
+ char *lpLoginScript, *lpTemp;
+ int i, nCondIndexCopy;
+ int aCondValCopy[MAX_NUM_IF];
+ int iRet;
+
+ //
+ // Save off the old globals that track where we are.
+ //
+
+ LABEL_LIST *pLabelList = pGlobalLabelList;
+ char *lpLine = lpGlobalLine;
+ char *lpLineSeparator = lpGlobalLineSeparator;
+ int fHaveNulledLineSeparator = fGlobalHaveNulledLineSeparator;
+
+ pGlobalLabelList = NULL; // so that we don't free it.
+
+ do
+ {
+ if (strtok (lpParam, __SPACES__) == NULL)
+ break;
+
+ lpTemp = strtok(NULL, __SPACES__);
+ if (lpTemp && !EndOfLine (lpTemp))
+ break;
+
+ fCommandHandled = TRUE;
+ NotQuotedStringTranslate(lpParam, TRUE);
+
+ nCondIndexCopy = nCondIndex;
+ for (i = 0; i < MAX_NUM_IF; i++)
+ aCondValCopy[i] = aCondVal[i];
+
+ /*
+ * First we try a NDS object and then a file
+ */
+ iRet = FALSE;
+ if ( fNDS )
+ {
+ iRet = ProcessLoginScriptProperty( lpParam );
+ if ( !iRet )
+ {
+ char Fixup[MAXLEN];
+ char * ptr;
+ /*
+ * Strip off the . in front and add context at end
+ */
+ ptr = RemoveSpaces (lpParam);
+ if ( *ptr == '.' ) {
+ ptr++;
+ strncpy( Fixup, ptr, MAXLEN );
+ }
+ else {
+ strncpy( Fixup, ptr, MAXLEN );
+ if ( Fixup[strlen(Fixup)-1] != '.' )
+ strcat( Fixup, "." );
+ strcat( Fixup, LOGIN_CONTEXT );
+ }
+ iRet = ProcessLoginScriptProperty( Fixup );
+ }
+ }
+
+ if ( !fNDS || !iRet )
+ {
+ nFileSize = NWGetFileSize (lpParam);
+ if (nFileSize == 0)
+ {
+ DisplayMessage(IDR_ERROR_OPEN_SCRIPT, lpParam);
+ break;
+ }
+
+ // user login script exists.
+ lpLoginScript = malloc (nFileSize);
+ if (lpLoginScript == NULL)
+ {
+ DisplayMessage(IDR_NOT_ENOUGH_MEMORY);
+ break;
+ }
+
+ LoadFile (lpParam, lpLoginScript, nFileSize);
+
+ ProcessLoginScript (lpLoginScript);
+
+ free (lpLoginScript);
+ }
+
+ fGlobalExitFlag = FALSE;
+
+ nCondIndex = nCondIndexCopy;
+ for (i = 0; i < MAX_NUM_IF; i++)
+ aCondVal[i] = aCondValCopy[i];
+
+ }while (FALSE);
+
+ //
+ // restore the globals that track where we are in the file.
+ //
+
+ pGlobalLabelList = pLabelList;
+ lpGlobalLine = lpLine;
+ lpGlobalLineSeparator = lpLineSeparator;
+ fGlobalHaveNulledLineSeparator = fHaveNulledLineSeparator;
+
+
+ return(fCommandHandled);
+}
+
+/*
+ * Map command handler.
+ */
+int MapHandler (char *lpParam)
+{
+ char buffer[MAXLEN]="";
+
+ strcpy( buffer, lpParam );
+
+ NotQuotedStringTranslate( buffer, TRUE );
+
+ Map( buffer );
+
+ return(TRUE);
+}
+
+/*
+ * PAUSE or WAIT.
+ */
+int PauseHandler (char *lpParam)
+{
+ int fCommandHandled = FALSE;
+
+ if (EndOfLine (lpParam))
+ {
+ //Empty kb buffer first.
+ while (_kbhit())
+ _getch();
+
+ DisplayMessage(IDR_STRIKE_KEY);
+ _getch();
+ DisplayMessage(IDR_NEWLINE);
+ fCommandHandled = TRUE;
+ }
+
+ return(fCommandHandled);
+}
+
+/*
+ * Used by WriteHandler().
+ * Return TRUE if buffer ends with ';'. Set it to 0
+ * Return FALSE otherwise.
+ */
+int EndWithSemicolon (char *buffer)
+{
+ char *lpLastSemicolon, *lpRest;
+ lpLastSemicolon = strrchr (buffer, ';');
+ if (lpLastSemicolon)
+ {
+ lpRest = RemoveSpaces (lpLastSemicolon+1);
+ if (*lpRest == 0)
+ {
+ *lpLastSemicolon = 0;
+ return(TRUE);
+ }
+ }
+
+ return(FALSE);
+}
+
+char *ConvertPercent (char *buffer)
+{
+ char *lpPercent, *lpBuffer = buffer;
+ int nPercent = 0;
+
+ while (lpPercent = strchr (lpBuffer, '%'))
+ {
+ nPercent++;
+ lpBuffer = lpPercent+1;
+ }
+
+ if (nPercent == 0)
+ return(NULL);
+
+ lpBuffer = malloc (strlen(buffer)+nPercent+1);
+ if (lpBuffer == NULL)
+ return(NULL);
+
+ strcpy (lpBuffer, buffer);
+
+ lpPercent = strchr (lpBuffer, '%');
+
+ while (lpPercent)
+ {
+ memmove (lpPercent+1, lpPercent, strlen (lpPercent)+1);
+ lpPercent = strchr ( lpPercent+2, '%');
+ }
+
+ return(lpBuffer);
+}
+
+/*
+ * WRITE text, display a text message on the screen.
+ */
+int WriteHandler (char *lpParam)
+{
+ int fNewLine;
+ char *lpBuffer;
+
+ if (*lpParam == 0)
+ {
+ DisplayMessage(IDR_NEWLINE);
+ return(TRUE);
+ }
+
+ fNewLine = !EndWithSemicolon (lpParam);
+
+ if (!QuotedStringTranslate (lpParam))
+ return FALSE;
+
+ lpBuffer = ConvertPercent (lpParam);
+ if (lpBuffer == NULL)
+ {
+ DisplayOemString(lpParam);
+ }
+ else
+ {
+ DisplayOemString(lpBuffer);
+ free (lpBuffer);
+ }
+
+ if (fNewLine)
+ DisplayMessage(IDR_NEWLINE);
+
+ return(TRUE);
+}
+
+/*
+ * Used by ShiftHandler().
+ * Return TURE if the line is all numbers + [comments]
+ * Return FALSE otherwise.
+ */
+int AreAllNumbers(char *buffer)
+{
+ while (isdigit(*buffer))
+ buffer++;
+
+ return(EndOfLine (buffer));
+}
+
+/*
+ * Set the nGlobalShiftDelta variable.
+ */
+int ShiftHandler (char *lpParam)
+{
+ int fCommandHandled = TRUE;
+
+ if (EndOfLine (lpParam))
+ nGlobalShiftDelta++;
+ else if (*lpParam == '-')
+ {
+ lpParam = RemoveSpaces (lpParam+1);
+ if (!AreAllNumbers(lpParam))
+ fCommandHandled = FALSE;
+ else
+ nGlobalShiftDelta -= atoi (lpParam);
+ }
+ else
+ {
+ if (*lpParam == '+')
+ lpParam = RemoveSpaces (lpParam+1);
+
+ if (!AreAllNumbers(lpParam))
+ fCommandHandled = FALSE;
+ else
+ nGlobalShiftDelta += atoi (lpParam);
+ }
+
+ return(fCommandHandled);
+}
+
+/*
+ * Set the machine name.
+ */
+int MachineHandler (char *lpParam)
+{
+ int nLen, i;
+
+ if (*lpParam != '=')
+ return(FALSE);
+
+ lpParam = RemoveSpaces (lpParam+1);
+ if (!QuotedStringTranslate(lpParam))
+ return(FALSE);
+
+ nLen = strlen (lpParam);
+ for (i = nLen; i < 15; i++)
+ *(lpParam+i) = ' ';
+
+ *(lpParam+15) = 0;
+
+ return(TRUE);
+}
+
+/*
+ * Set the fGlobalCompatible variable.
+ */
+int CompatibleHandler(char *lpParam)
+{
+ if (!EndOfLine (lpParam))
+ return(FALSE);
+
+ fGlobalCompatible = TRUE;
+ return(TRUE);
+}
+
+/*
+ * Clear the screen
+ */
+int ClearHandler(char *lpParam)
+{
+ CONSOLE_SCREEN_BUFFER_INFO coninfo;
+ COORD scrolltarget;
+ CHAR_INFO chinfo;
+ SMALL_RECT scrollrect;
+
+ if ( hconout == INVALID_HANDLE_VALUE )
+ {
+ hconout = CreateFile( L"CONOUT$", GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+ OPEN_EXISTING, 0, NULL );
+ }
+
+ if ( hconout == INVALID_HANDLE_VALUE )
+ return TRUE;
+
+ GetConsoleScreenBufferInfo( hconout, &coninfo );
+
+ scrolltarget.Y = (SHORT)(0 - coninfo.dwSize.Y);
+ scrolltarget.X = 0;
+
+ scrollrect.Top = 0;
+ scrollrect.Left = 0;
+ scrollrect.Bottom = coninfo.dwSize.Y;
+ scrollrect.Right = coninfo.dwSize.X;
+ chinfo.Char.AsciiChar = ' ';
+ chinfo.Attributes = coninfo.wAttributes;
+ ScrollConsoleScreenBufferA( hconout, &scrollrect, NULL,
+ scrolltarget, &chinfo);
+
+ coninfo.dwCursorPosition.X = 0;
+ coninfo.dwCursorPosition.Y = 0;
+
+ SetConsoleCursorPosition( hconout, coninfo.dwCursorPosition );
+ return(TRUE);
+}
+
+/*
+ * Display the Last Login Time
+ */
+int LastLoginTimeHandler(char *lpParam)
+{
+ BYTE dataBuffer[128];
+ unsigned char moreFlag;
+ unsigned char propertyType;
+
+ if ( fNDS )
+ {
+ nwShowLastLoginTime();
+ }
+ else
+ {
+ NWReadPropertyValue ((NWCONN_HANDLE)CONNECTION_ID,
+ LOGIN_NAME,
+ OT_USER,
+ "MISC_LOGIN_INFO",
+ 1,
+ dataBuffer,
+ &moreFlag,
+ &propertyType);
+ /*
+ * 0 = year
+ * 1 = month
+ * 2 = day
+ * 3 = hour
+ * 4 = minute
+ * 5 = second
+ */
+
+ if ( dataBuffer[3] >= 12 )
+ {
+ DisplayMessage( IDR_LASTLOGIN_PM,
+ dataBuffer[1],
+ dataBuffer[2],
+ dataBuffer[0],
+ dataBuffer[3] - 12,
+ dataBuffer[4],
+ dataBuffer[5] );
+ }
+ else
+ {
+ DisplayMessage( IDR_LASTLOGIN_AM,
+ dataBuffer[1],
+ dataBuffer[2],
+ dataBuffer[0],
+ dataBuffer[3],
+ dataBuffer[4],
+ dataBuffer[5] );
+ }
+ }
+
+ return(TRUE);
+}
+
+
+
+/*
+ * Change and/or display the current context.
+ */
+int ContextHandler (char *lpParam)
+{
+ unsigned char Buffer[MAXLEN];
+ unsigned char * ptr;
+ unsigned char CurrentContext[MAXLEN];
+
+ if ( *lpParam )
+ {
+ NotQuotedStringTranslate(lpParam, TRUE);
+
+ ptr = RemoveSpaces (lpParam);
+
+ if ( NDSCanonicalizeName( lpParam, Buffer, MAXLEN, TRUE ) )
+ {
+ DisplayMessage(IDR_CHANGE_CONTEXT_ERROR, lpParam);
+ return(TRUE);
+ }
+
+ if ( NDSChangeContext( Buffer ) )
+ {
+ DisplayMessage(IDR_CHANGE_CONTEXT_ERROR, lpParam);
+ return(TRUE);
+ }
+ }
+
+ if ( NDSGetContext( CurrentContext, MAXLEN ) )
+ {
+ DisplayMessage(IDR_GET_CONTEXT_ERROR);
+ }
+ else
+ {
+ DisplayMessage(IDR_DISPLAY_CONTEXT, CurrentContext);
+ }
+ return(TRUE);
+}
+
+/*
+ * Do nothing. Return TRUE so the the command will not
+ * be considered as bad.
+ */
+int ScriptServerHandler (char *lpParam)
+{
+ return(TRUE);
+}
+
+/*
+ * If this is a 4X login, do not execute the default login script.
+ */
+int NoDefaultHandler (char *lpParam)
+{
+ if ( fNDS )
+ fNoDefaultLoginScript = TRUE;
+ return(TRUE);
+}
+
+/*
+ * Do nothing. Return TRUE so the the command will not
+ * be considered as bad.
+ */
+int NullHandler (char *lpParam)
+{
+ return(TRUE);
+}
+
+#define NUMBER_ARGUMENTS 20
+
+/*
+ * External commands start with '#', such as #command /c cls
+ */
+void ExternalCmdHandler (char *lpCommand)
+{
+ int n;
+ int i;
+ unsigned int CommandLength;
+ char *lpCmdName, *argv[NUMBER_ARGUMENTS];
+
+ for ( n = 0; n < NUMBER_ARGUMENTS; n++ )
+ argv[n] = NULL;
+
+ if ((nCondIndex == -1) || aCondVal[nCondIndex])
+ {
+ //Convert variables first.
+ NotQuotedStringTranslate(lpCommand, FALSE);
+
+ lpCommand = RemoveSpaces(lpCommand+1);
+ lpCmdName = strtok (lpCommand, __SPACES__);
+
+ lpCmdName = NTNWtoUNCFormat(lpCmdName);
+
+ argv[0] = lpCmdName;
+
+ for (n = 1; n < NUMBER_ARGUMENTS - 1; n++)
+ {
+ if ((argv[n] = strtok (NULL, __SPACES__)) == NULL)
+ break;
+ }
+
+ argv[9] = NULL;
+
+ /*
+ * Capture command
+ */
+ CommandLength = strlen( lpCommand );
+
+ /*
+ * First see if a COMMAND.COM is invoked
+ */
+ if ( ( ( CommandLength >= strlen("COMMAND.COM") ) &&
+ ( !_stricmp( &lpCommand[CommandLength-strlen("COMMAND.COM")], "COMMAND.COM") ) ) ||
+ ( ( CommandLength >= strlen("COMMAND") ) &&
+ ( !_stricmp( &lpCommand[CommandLength-strlen("COMMAND")], "COMMAND") ) ) )
+ {
+ /*
+ * Search for the CAPTURE argument
+ */
+ for ( i = 1; i < n; i++ )
+ {
+ CommandLength = strlen( argv[i] );
+ if ( ( ( CommandLength >= strlen("CAPTURE.EXE") ) &&
+ ( !_stricmp( &(argv[i])[CommandLength-strlen("CAPTURE.EXE")], "CAPTURE.EXE") ) ) ||
+ ( ( CommandLength >= strlen("CAPTURE") ) &&
+ ( !_stricmp( &(argv[i])[CommandLength-strlen("CAPTURE")], "CAPTURE") ) ) ) {
+ Capture( argv + i, n - i );
+ return;
+ }
+ }
+ }
+ else
+ {
+ /*
+ * Is this a CAPTURE command?
+ */
+ if ( ( ( CommandLength >= strlen("CAPTURE.EXE") ) &&
+ ( !_stricmp( &lpCommand[CommandLength-strlen("CAPTURE.EXE")], "CAPTURE.EXE") ) ) ||
+ ( ( CommandLength >= strlen("CAPTURE") ) &&
+ ( !_stricmp( &lpCommand[CommandLength-strlen("CAPTURE")], "CAPTURE") ) ) ) {
+ Capture( argv, n );
+ return;
+ }
+ }
+
+ if ((SCRIPT_ERROR = _spawnvp (P_WAIT, lpCmdName, argv)) == -1)
+ {
+ if (errno == ENOENT)
+ DisplayMessage(IDR_ENOENT, lpCommand);
+ else
+ DisplayMessage(IDR_CANNOT_EXECUTE, lpCommand);
+ }
+ }
+}
+
+/*
+ * Printe out the bad command line.
+ */
+void BadCommandHandler (char *lpCommand)
+{
+ DisplayMessage(IDR_SCRIPT_ERROR);
+ DisplayMessage(IDR_ORIGINAL_LINE_WAS, lpCommand);
+}
+
+
+/*
+ * Swap the object id.
+ */
+DWORD SwapLong(DWORD number)
+{
+ BYTE *p, tmp[4];
+
+ p = (BYTE *)&number;
+
+ tmp[0] = p[3];
+ tmp[1] = p[2];
+ tmp[2] = p[1];
+ tmp[3] = p[0];
+
+ return(*(DWORD *)tmp);
+}
+
+/*
+ * Remove leading spaces, including tabs.
+ */
+char *RemoveSpaces (char * buffer)
+{
+ while (*buffer == ' ' || *buffer == '\t')
+ buffer++;
+ return(buffer);
+}
+
+/*
+ * Return TRUE if buffer points to the end of the lind, FALSE otherwise.
+ */
+int EndOfLine (char *buffer)
+{
+ int fEndOfLine = FALSE;
+
+ buffer = RemoveSpaces (buffer);
+
+ if (*buffer == '\0' ||
+ *buffer == ';' ||
+ *buffer == '*' ||
+ *buffer == '\r')
+ fEndOfLine = TRUE;
+
+ return(fEndOfLine);
+}
+
+/*
+ * Return TRUE if lpParam points to "ON", FALSE otherwise.
+ */
+int IsOn (char *lpParam)
+{
+ int fOn = FALSE;
+
+ if (!strncmp (lpParam, "ON", 2))
+ {
+ lpParam += 2;
+ fOn = EndOfLine (lpParam);
+ }
+
+ return(fOn);
+}
+
+/*
+ * Return TRUE if lpParam points to "OFF", FALSE otherwise.
+ */
+int IsOff (char *lpParam)
+{
+ int fOff = FALSE;
+
+ if (!strncmp (lpParam, "OFF", 3))
+ {
+ lpParam += 3;
+ fOff = EndOfLine (lpParam);
+ }
+
+ return(fOff);
+}
+
+/*
+ * Used by VarTranslate().
+ * Copy to buffer the value of time variable specified by index.
+ */
+void GetTime (char *buffer, int index)
+{
+ time_t currentTime;
+ struct tm *tmCurrentTime;
+
+ time (&currentTime);
+ tmCurrentTime = localtime(&currentTime);
+
+ switch (index)
+ {
+ case IDS_DAY:
+ sprintf (buffer, "%02d\0", tmCurrentTime->tm_mday);
+ break;
+ case IDS_DAY_OF_WEEK:
+ strcpy (buffer, __Day__[tmCurrentTime->tm_wday]);
+ break;
+ case IDS_MONTH:
+ sprintf (buffer, "%02d\0", tmCurrentTime->tm_mon+1);
+ break;
+ case IDS_MONTH_NAME:
+ strcpy (buffer, __Month__[tmCurrentTime->tm_mon]);
+ break;
+ case IDS_NDAY_OF_WEEK:
+ sprintf (buffer, "%d\0", tmCurrentTime->tm_wday+1);
+ break;
+ case IDS_SHORT_YEAR:
+ sprintf (buffer, "%04d\0", tmCurrentTime->tm_year+1900);
+ strcpy (buffer, buffer+2);
+ break;
+ case IDS_YEAR:
+ sprintf (buffer, "%04d\0", tmCurrentTime->tm_year+1900);
+ break;
+ case IDS_AM_PM:
+ strcpy (buffer, __AMPM__[tmCurrentTime->tm_hour > 12? 1:0]);
+ break;
+ case IDS_GREETING_TIME:
+ if (tmCurrentTime->tm_hour >= 6 && tmCurrentTime->tm_hour < 12)
+ index=0;
+ else if (tmCurrentTime->tm_hour >= 12 && tmCurrentTime->tm_hour < 18)
+ index=1;
+ else
+ index=2;
+
+ strcpy (buffer, __GREETING__[index]);
+ break;
+ case IDS_HOUR:
+ if (tmCurrentTime->tm_hour > 12)
+ tmCurrentTime->tm_hour -= 12;
+ sprintf (buffer, "%d\0", tmCurrentTime->tm_hour);
+ break;
+ case IDS_HOUR24:
+ sprintf (buffer, "%02d\0", tmCurrentTime->tm_hour);
+ break;
+ case IDS_MINUTE:
+ sprintf (buffer, "%02d\0", tmCurrentTime->tm_min);
+ break;
+ case IDS_SECOND:
+ sprintf (buffer, "%02d\0", tmCurrentTime->tm_sec);
+ break;
+ default:
+ *buffer = 0;
+ }
+}
+
+/*
+ * Used by VarTranslate().
+ * Copy to buffer login user's full name.
+ */
+void GetFullName (char *buffer)
+{
+ unsigned int iRet = 0;
+ unsigned char moreFlag;
+ unsigned char propertyType;
+
+ if ( fNDS )
+ {
+ NDSGetVar ( "Full Name", buffer, 128 );
+ if ( buffer[0] == '\0' )
+ strcpy (buffer, "* Unknown *");
+ }
+ else
+ {
+ iRet = NWReadPropertyValue ((NWCONN_HANDLE)CONNECTION_ID,
+ LOGIN_NAME,
+ OT_USER,
+ "IDENTIFICATION",
+ 1,
+ buffer,
+ &moreFlag,
+ &propertyType);
+ if (iRet)
+ strcpy (buffer, "* Unknown *");
+ }
+}
+
+/*
+ * Used by VarTranslate().
+ * Copy to buffer login user's object id.
+ */
+void GetUserID (char *buffer)
+{
+ unsigned long dwObjectID = 0;
+
+ if ( fNDS )
+ dwObjectID = GUserObjectID;
+ else
+ NTGetUserID( CONNECTION_ID, &dwObjectID );
+ sprintf (buffer, "%lx\0", SwapLong(dwObjectID));
+ _strupr (buffer);
+}
+
+unsigned int GetDays (unsigned int year, BYTE month, BYTE date)
+{
+ unsigned int i, days = 0;
+
+ for (i = 1; i < month; i++)
+ {
+ if (i == 2)
+ days += (year%4)? 28 : 29;
+ else if (i == 4 || i == 6 || i == 9 || i == 11)
+ days += 30;
+ else
+ days += 31;
+ }
+
+ days += date;
+ return(days);
+}
+
+/*
+ * Used by VarTranslate().
+ * Copy to buffer the days in which the password expires.
+ */
+void GetPasswordExpires (char *buffer)
+{
+ unsigned int iRet = 0;
+ unsigned int iRet2 = 0;
+ unsigned char moreFlag;
+ unsigned int yearCurrent, yearEnd, days;
+ BYTE monthCurrent, dayCurrent, monthEnd, dayEnd;
+ unsigned int exptime, logintime;
+ unsigned char propertyType;
+
+
+ if ( fNDS )
+ {
+ iRet = NDSGetUserProperty ("Password Expiration Time", (PBYTE)&exptime,
+ 4, NULL, NULL);
+ iRet2 = NDSGetUserProperty ("Login Time", (PBYTE)&logintime,
+ 4, NULL, NULL);
+
+ if ( ( exptime && logintime ) && !iRet && !iRet2 )
+ {
+ if ( exptime <= logintime )
+ strcpy( buffer, "0" );
+ else
+ sprintf( buffer, "%u", ((exptime-logintime)/(60*60*24)) + 1 );
+ }
+ else
+ {
+ sprintf( buffer, "%u", 0x7FFF );
+ }
+ }
+ else
+ {
+ NTGetTheDate( &yearCurrent, &monthCurrent, &dayCurrent );
+ NWReadPropertyValue ((NWCONN_HANDLE)CONNECTION_ID,
+ LOGIN_NAME,
+ OT_USER,
+ "LOGIN_CONTROL",
+ 1,
+ buffer,
+ &moreFlag,
+ &propertyType);
+
+ yearEnd = 1900 + buffer[4];
+ monthEnd = buffer[5];
+ dayEnd = buffer[6];
+
+ if (monthEnd == 0)
+ days = (((yearCurrent%4)? 365 : 366) - GetDays (yearCurrent, monthCurrent, dayCurrent));
+ else if (yearEnd == yearCurrent)
+ {
+ if (monthEnd < monthCurrent ||
+ (monthEnd == monthCurrent && dayEnd <= dayCurrent))
+ days = 0;
+ else
+ days = GetDays (yearEnd, monthEnd, dayEnd) - GetDays (yearCurrent, monthCurrent, dayCurrent) - 1;
+ }
+ else
+ days = ((yearCurrent%4)? 364 : 365) + GetDays (yearEnd, monthEnd, dayEnd) - GetDays (yearCurrent, monthCurrent, dayCurrent);
+
+ sprintf (buffer, "%u", days);
+ }
+}
+
+/*
+ * Used by VarTranslate().
+ * Copy to buffer value of the dos environment variable.
+ * If the variable is not found, buffer is set to be empty string.
+ */
+void GetDosEnv (char *buffer)
+{
+ char *lpTemp;
+
+ // This could be called from "%<x>" where x is not upcase. capitalize
+ // the string first to be sure.
+ _strupr(buffer);
+
+ lpTemp = strchr (buffer, '>');
+ *lpTemp = 0;
+
+ lpTemp = getenv (buffer+1);
+
+ if (lpTemp && strlen (lpTemp) < MAXLEN )
+ strcpy (buffer, lpTemp);
+ else
+ *buffer = 0;
+}
+
+/*
+ * Used by VarTranslate().
+ * Copy to buffer the 8 bytes network address.
+ */
+void GetNetWorkAddr (char *buffer)
+{
+ unsigned char internetAddress[10];
+
+ GetInternetAddress (CONNECTION_ID,
+ CONNECTION_NUMBER,
+ internetAddress);
+
+ sprintf (buffer,
+ "%02X%02X%02X%02X\0",
+ internetAddress[0],
+ internetAddress[1],
+ internetAddress[2],
+ internetAddress[3] );
+}
+
+
+/*
+ * Used by VarTranslate().
+ * Copy to buffer the 12 bytes node address to buffer.
+ */
+void GetPStation (char *buffer)
+{
+ unsigned char internetAddress[10];
+
+ GetInternetAddress (CONNECTION_ID,
+ CONNECTION_NUMBER,
+ internetAddress);
+
+ sprintf (buffer,
+ "%02X%02X%02X%02X%02X%02X\0",
+ internetAddress[4],
+ internetAddress[5],
+ internetAddress[6],
+ internetAddress[7],
+ internetAddress[8],
+ internetAddress[9]);
+}
+
+/*
+ * Used by VarTranslate().
+ * Copy to buffer the decimal string representing the remaining account
+ * balance
+ */
+void GetAccountBalance (char *buffer)
+{
+ DWORD balance;
+ BYTE dataBuffer[128];
+ unsigned char moreFlag;
+ unsigned char propertyType;
+ unsigned int err;
+
+ if ( fNDS )
+ {
+ err = NDSGetUserProperty ("Account Balance", dataBuffer,128, NULL, NULL);
+ }
+ else
+ {
+ err = NWReadPropertyValue ((NWCONN_HANDLE)CONNECTION_ID,
+ LOGIN_NAME,
+ OT_USER,
+ "ACCOUNT_BALANCE",
+ 1,
+ dataBuffer,
+ &moreFlag,
+ &propertyType);
+ }
+
+ if ( err )
+ balance = 0;
+ else
+ balance = *((DWORD *)dataBuffer);
+
+ sprintf (buffer, "%d", balance);
+}
+
+/*
+ * Used by VarTranslate().
+ * Copy to buffer MACHINE, SMACHINE, OS, OS_VERSION or SHELL_TYPE
+ * to buffer according to index.
+ */
+void GetShellVersion(char *buffer, int index)
+{
+ static char szTemp[40];
+ char *lpTemp;
+ BYTE shellmajor, shellminor, shellnum;
+
+ NTGetVersionOfShell( szTemp, &shellmajor, &shellminor, &shellnum );
+
+ lpTemp = szTemp;
+
+ switch (index)
+ {
+ case IDS_OS:
+ strcpy (buffer, lpTemp);
+ break;
+ case IDS_OS_VERSION:
+ lpTemp += (strlen (lpTemp)+1);
+ strcpy (buffer, lpTemp);
+ break;
+ case IDS_MACHINE:
+ lpTemp += (strlen (lpTemp)+1);
+ lpTemp += (strlen (lpTemp)+1);
+ strcpy (buffer, lpTemp);
+ break;
+ case IDS_SMACHINE:
+ lpTemp += (strlen (lpTemp)+1);
+ lpTemp += (strlen (lpTemp)+1);
+ lpTemp += (strlen (lpTemp)+1);
+ strcpy (buffer, lpTemp);
+ break;
+ case IDS_SHELL_TYPE:
+ case IDS_SHELL_VERSION:
+ sprintf (buffer, "V%d.%d%d%c", shellmajor, shellminor/10, shellminor%10, 'A'+shellnum);
+ break;
+ default:
+ *buffer = 0;
+ break;
+ }
+}
+
+void GetArgv(char *buffer)
+{
+ int n;
+
+ n = atoi (buffer)+nGlobalShiftDelta;
+
+ if (n == 0)
+ strcpy (buffer, PREFERRED_SERVER);
+ else if (n == 1)
+ strcpy (buffer, LOGIN_NAME);
+ else if (n > 1 && n < ARGC)
+ strcpy (buffer, ARGV[n]);
+ else
+ *buffer = 0;
+}
+
+/*
+ * vartext is an array of size MAXLEN.
+ * vartext points to a string starts with a variable on enter.
+ * vartext stores the value of the variable on exit.
+ * Return the lenth of the variable.
+ */
+int VarTranslate(char *vartext)
+{
+ int i, nVarLen = 0;
+
+ for (i = 0; i < (fNDS ? NUMVAR : NUMVAR_3X); i++)
+ {
+ if (!nwVarNameCompare(vartext, varTable[i].VarName))
+ {
+ nVarLen = strlen(varTable[i].VarName);
+
+ switch ( i )
+ {
+ case IDS_DAY_OF_WEEK:
+ case IDS_DAY:
+ case IDS_MONTH_NAME:
+ case IDS_MONTH:
+ case IDS_NDAY_OF_WEEK:
+ case IDS_SHORT_YEAR:
+ case IDS_YEAR:
+ case IDS_AM_PM:
+ case IDS_GREETING_TIME:
+ case IDS_HOUR24:
+ case IDS_HOUR:
+ case IDS_MINUTE:
+ case IDS_SECOND:
+ GetTime (vartext, i);
+ break;
+ case IDS_FULL_NAME:
+ GetFullName (vartext);
+ break;
+ case IDS_LOGIN_NAME:
+ strcpy (vartext, LOGIN_NAME);
+ /*
+ * 4X LOGIN.EXE always truncates and replaces spaces
+ * with underscores. There was a report that some
+ * versions of 3X LOGIN.EXE do this also.
+ */
+ if ( fNDS )
+ {
+ int i;
+ vartext[8] = '\0';
+ for ( i = 0; i < 8; i++ )
+ if ( vartext[i] == ' ' )
+ vartext[i] = '_';
+ }
+ break;
+ case IDS_USER_ID:
+ GetUserID (vartext);
+ break;
+ case IDS_PASSWORD_EXPIRES:
+ GetPasswordExpires (vartext);
+ break;
+ case IDS_NETWORK_ADDRESS:
+ case IDS_NETWORK:
+ GetNetWorkAddr (vartext);
+ break;
+ case IDS_FILE_SERVER:
+ strcpy (vartext, PREFERRED_SERVER);
+ break;
+ case IDS_ACCESS_SERVER:
+ case IDS_ACCESS:
+ strcpy (vartext, "0");
+ break;
+ case IDS_ERROR_LEVEL:
+ case IDS_ERRORLEVEL:
+ sprintf (vartext, "%u", SCRIPT_ERROR);
+ break;
+ case IDS_MACHINE:
+ case IDS_OS_VERSION:
+ case IDS_OS:
+ case IDS_SMACHINE:
+ case IDS_SHELL_TYPE:
+ case IDS_SHELL_VERSION:
+ GetShellVersion (vartext, i);
+ break;
+ case IDS_STATION:
+ sprintf (vartext, "%d", CONNECTION_NUMBER);
+ break;
+ case IDS_P_STATION:
+ GetPStation (vartext);
+ break;
+ case IDS_LAST_NAME:
+ case IDS_SURNAME:
+ strcpy (vartext, LAST_NAME);
+ break;
+ case IDS_LOGIN_CONTEXT:
+ strcpy (vartext, LOGIN_CONTEXT);
+ break;
+ case IDS_NETWARE_REQUESTER:
+ case IDS_REQUESTER_VERSION:
+ case IDS_DOS_REQUESTER:
+ case IDS_REQUESTER:
+ strcpy (vartext, REQUESTER_VERSION);
+ break;
+ case IDS_REQUESTER_CONTEXT:
+ strcpy (vartext, REQUESTER_CONTEXT);
+ break;
+ case IDS_ACCOUNT_BALANCE:
+ GetAccountBalance (vartext);
+ break;
+ case IDS_CN:
+ strcpy (vartext, COMMON_NAME);
+ break;
+ case IDS_HOME_DIRECTORY:
+ {
+ char buffer[MAXLEN];
+
+ vartext[0] = '\0';
+ NDSGetVar ( varTable[i].VarName, buffer, MAXLEN );
+ if ( buffer[0] )
+ ConverNDSPathToNetWarePathA( buffer, NULL, vartext );
+ }
+ break;
+ case IDS_ADMINISTRATIVE_ASSISTANT:
+ case IDS_ALLOW_UNLIMITED_CREDIT:
+ case IDS_DESCRIPTION:
+ case IDS_EMAIL_ADDRESS:
+ case IDS_EMPLOYEE_ID:
+ case IDS_FACSIMILE_TELEPHONE_NUMBER:
+ case IDS_GROUP_MEMBERSHIP:
+ case IDS_HIGHER_PRIVILEGES:
+ case IDS_INITIALS:
+ case IDS_LANGUAGE:
+ case IDS_LOCKED_BY_INTRUDER:
+ case IDS_LOGIN_DISABLED:
+ case IDS_LOGIN_GRACE_LIMIT:
+ case IDS_LOGIN_GRACE_REMAINING:
+ case IDS_LOGIN_INTRUDER_ATTEMPTS:
+ case IDS_LOGIN_MAXIMUM_SIMULTANEOUS:
+ case IDS_MAILSTOP:
+ case IDS_MESSAGE_SERVER:
+ case IDS_MINIMUM_ACCOUNT_BALANCE:
+ case IDS_OBJECT_CLASS:
+ case IDS_OU:
+ case IDS_PASSWORD_ALLOW_CHANGE:
+ case IDS_PASSWORD_MINIMUM_LENGTH:
+ case IDS_PASSWORD_REQUIRED:
+ case IDS_PASSWORD_UNIQUE_REQUIRED:
+ case IDS_PASSWORDS_USED:
+ case IDS_PHYSICAL_DELIVERY_OFFICE_NAME:
+ case IDS_POSTAL_ADDRESS:
+ case IDS_POSTAL_CODE:
+ case IDS_POSTAL_OFFICE_BOX:
+ case IDS_PRIVATE_KEY:
+ case IDS_PROFILE:
+ case IDS_REVISION:
+ case IDS_SECURITY_EQUALS:
+ case IDS_SECURITY_FLAGS:
+ case IDS_SEE_ALSO:
+ case IDS_SERVER_HOLDS:
+ case IDS_SUPERVISOR:
+ case IDS_TELEPHONE_NUMBER:
+ case IDS_TITLE:
+ case IDS_CERTIFICATE_VALIDITY_INTERVAL:
+ case IDS_EQUIVALENT_TO_ME:
+ case IDS_GENERATIONAL_QUALIFIER:
+ case IDS_GIVEN_NAME:
+ case IDS_MAILBOX_ID:
+ case IDS_MAILBOX_LOCATION:
+ case IDS_PROFILE_MEMBERSHIP:
+ case IDS_SA:
+ case IDS_S:
+ case IDS_L:
+ NDSGetVar ( varTable[i].VarName, vartext, MAXLEN );
+ break;
+ }
+ return(nVarLen);
+ }
+ }
+
+ if (isdigit(*vartext))
+ {
+ while (isdigit(vartext[nVarLen]))
+ nVarLen++;
+ GetArgv(vartext);
+ }
+ else if (*vartext == '<')
+ {
+ nVarLen = 1;
+ while (vartext[nVarLen] != '>' && vartext[nVarLen] != 0)
+ {
+ if (IsDBCSLeadByte(vartext[nVarLen]))
+ nVarLen++;
+ nVarLen++;
+ }
+
+ if (vartext[nVarLen] == 0)
+ nVarLen = 0;
+ else
+ {
+ nVarLen++;
+ GetDosEnv (vartext);
+ }
+ }
+
+ return(nVarLen);
+}
+
+/*
+ * Parse path string.
+ * If find the %variable value, replace it, otherwise keep as it is.
+ */
+void NotQuotedStringTranslate(char *buf, BOOL Remove_dbs)
+{
+ char *pPercentSign, *pRest, vartext[MAXLEN];
+ int nVarLen, nInsertlen;
+
+ if ( Remove_dbs )
+ {
+ // Convert \\ to \.
+ pRest = buf;
+ for (pRest = buf; *pRest; pRest = NWAnsiNext(pRest))
+ {
+ if (*pRest == '\\' && *(pRest+1) == '\\')
+ memmove (pRest, pRest+1, strlen (pRest));
+ }
+ }
+
+ // Convert variables following '%' sign.
+ pRest = buf;
+ while (pPercentSign = strchr(pRest, '%'))
+ {
+ pRest = pPercentSign+1;
+
+ strcpy (vartext, pRest);
+
+ nVarLen = VarTranslate(vartext);
+
+ if (nVarLen == 0)
+ continue;
+
+ nInsertlen = strlen (vartext);
+ if (strlen (buf) + nInsertlen - nVarLen < MAXLEN)
+ {
+ pRest = pPercentSign+1+nVarLen;
+
+ memmove (pPercentSign+nInsertlen, pRest, strlen (pRest)+1);
+ memmove (pPercentSign, vartext, nInsertlen);
+ pRest = pPercentSign+nInsertlen;
+ }
+ }
+}
+
+/*
+ * Used by QuotedStringTranslate()
+ * On enter, *ppTemp point to a variable, on exit *ppTemp points to the
+ * charecter next to the variable. *ppBuffer points to the end of the
+ * value of the variable.
+ */
+int DoVarTranslate (char **ppTemp, char **ppBuffer, unsigned int nMaxLen, int fInquotes)
+{
+ int nVarLen;
+ char vartext[MAXLEN];
+
+ strcpy (vartext, *ppTemp);
+
+ nVarLen = VarTranslate (vartext);
+
+ if (nVarLen != 0)
+ {
+ if (strlen(vartext) >= nMaxLen)
+ return(FALSE);
+
+ strcpy (*ppBuffer, vartext);
+ (*ppBuffer) = (*ppBuffer) + strlen (vartext);
+ (*ppTemp) += nVarLen;
+ }
+ else if (fInquotes)
+ {
+ strcpy (*ppBuffer, "%");
+ (*ppBuffer) += 1;
+ }
+ else
+ return(FALSE);
+
+ return(TRUE);
+}
+
+/*
+ * Used by QuotedStringTranslate()
+ * On entry, *(*ppTemp -1) is '\', if **ppTemp is one of those special
+ * characters, put the value in **ppBuffer, otherwise copy '\\\ and
+ * whatever is in *ppBuffer to *ppBuffer.
+ */
+void TranslateSpecialChar (char **ppTemp, char **ppBuffer)
+{
+ (*ppTemp)++;
+
+ if (**ppTemp == '\\')
+ **(ppBuffer) = '\\';
+ else if (**ppTemp == 'n')
+ **(ppBuffer) ='\n';
+ else if (**ppTemp == 'r')
+ **(ppBuffer) ='\r';
+ else if (**ppTemp == '\"')
+ **(ppBuffer) ='\"';
+ else if (**ppTemp == '7')
+ **(ppBuffer) ='\7';
+ else
+ {
+ **(ppBuffer) = '\\';
+ (*ppBuffer)++;
+ return;
+ }
+
+ (*ppBuffer)++;
+ (*ppTemp)++;;
+}
+
+/*
+ * Used by QuotedStringTranslate().
+ * Return TRUE if there are more interesting strings and it's seperated by ';'
+ * FALSE otherwise.
+ */
+int GetNextString (char **ppTemp, int *pfEnd)
+{
+ int fMore = FALSE;
+
+ (*ppTemp) = RemoveSpaces (*ppTemp);
+
+ *pfEnd = (**ppTemp == 0);
+
+ if (**ppTemp == ';')
+ {
+ (*ppTemp) = RemoveSpaces (*ppTemp+1);
+ fMore = TRUE;
+ }
+
+ return(fMore);
+}
+
+
+int GetLastShiftOp (char *buffer, char *pchOp, char *lpRest)
+{
+ int i, inquotes = FALSE;
+
+ // NetWare compatibility fix.
+ // for (i = strlen (buffer)-1; i >= 0; i--)
+
+ for (i = 0; buffer[i]; i++)
+ {
+ if (buffer[i] == '\"' && buffer [i-1] != '\\')
+ inquotes = !inquotes;
+ if (!inquotes &&
+ ( (buffer[i] == '>' && buffer[i+1] == '>')
+ ||(buffer[i] == '<' && buffer[i+1] == '<')))
+ {
+ *pchOp = buffer[i];
+ buffer[i] = 0;
+ strcpy (lpRest, RemoveSpaces(buffer+i+2));
+ return(TRUE);
+ }
+ }
+
+ return(FALSE);
+}
+int GetLastAddOp (char *buffer, char *pchOp, char *lpRest)
+{
+ int i, inquotes = FALSE;
+
+ // NetWare compatibility fix.
+ // for (i = strlen (buffer)-1; i >= 0; i--)
+
+ for (i = 0; buffer[i]; i++)
+ {
+ if (buffer[i] == '\"' && buffer [i-1] != '\\')
+ inquotes = !inquotes;
+ if (!inquotes &&
+ (buffer[i] == '+' || buffer[i] == '-') )
+ {
+ *pchOp = buffer[i];
+ buffer[i] = 0;
+ strcpy (lpRest, RemoveSpaces(buffer+i+1));
+ return(TRUE);
+ }
+ }
+
+ return(FALSE);
+}
+
+int GetLastMultiplyOp (char *buffer, char *pchOp, char *lpRest)
+{
+ int i, inquotes = FALSE;
+
+ // NetWare compatibility fix.
+ // for (i = strlen (buffer)-1; i >= 0; i--)
+ for (i = 0; buffer[i]; i++)
+ {
+ if (buffer[i] == '\"' && buffer [i-1] != '\\')
+ inquotes = !inquotes;
+ if (!inquotes &&
+ (buffer[i] == '*' || buffer[i] == '/' || buffer[i] == '%') )
+ {
+
+ *pchOp = buffer[i];
+ buffer[i] = 0;
+ strcpy (lpRest, RemoveSpaces(buffer+i+1));
+ return(TRUE);
+ }
+ }
+
+ return(FALSE);
+}
+
+/*
+ * Used by QuotedStringTranslate.
+ * Return TRUE if input buffer is right format, FALSE otherwise.
+ */
+int SingleStringTranslate (char *buffer)
+{
+ int inquotes = FALSE, fEnd = FALSE, nShift, nLen;
+ char szRest[MAXLEN], chOp;
+ char *lpTemp = szRest, *lpBuffer=buffer;
+
+ buffer = RemoveSpaces (buffer);
+
+ if (GetLastShiftOp (buffer, &chOp, szRest))
+ {
+ if (!QuotedStringTranslate (buffer))
+ return(FALSE);
+
+ while (isdigit (*lpTemp))
+ lpTemp++;
+
+ if (!EndOfLine(lpTemp))
+ return(FALSE);
+
+ *lpTemp = 0;
+
+ nShift = atoi (szRest);
+ nLen = strlen (buffer);
+
+ if (nShift >= nLen)
+ *buffer = 0;
+ else
+ {
+ if (chOp == '<')
+ memmove (buffer, buffer+nShift, nLen-nShift);
+
+ *(buffer+nLen-nShift) = 0;
+ }
+ }
+ else if (GetLastAddOp (buffer, &chOp, szRest))
+ {
+ if (!QuotedStringTranslate (buffer) ||
+ !QuotedStringTranslate (szRest))
+ return(FALSE);
+
+ sprintf (buffer, "%d", (chOp == '+')? (atoi (buffer) + atoi (szRest))
+ : (atoi (buffer) - atoi (szRest)));
+ }
+ else if (GetLastMultiplyOp (buffer, &chOp, szRest))
+ {
+ if (!QuotedStringTranslate (buffer) ||
+ !QuotedStringTranslate (szRest))
+ return(FALSE);
+
+ if (chOp == '*')
+ sprintf (buffer, "%d", atoi (buffer) * atoi (szRest));
+ else
+ {
+ if (atoi (szRest) == 0)
+ {
+ DisplayMessage(IDR_DIVIDE_BY_ZERO);
+ strcpy (buffer, "0");
+ }
+ else
+ {
+ sprintf (buffer, "%d",(chOp == '/')? (atoi (buffer) / atoi (szRest))
+ : (atoi (buffer) % atoi (szRest)));
+ }
+ }
+ }
+ else
+ {
+ strcpy (szRest, buffer);
+ *buffer = 0;
+
+ while (*lpTemp)
+ {
+ if (inquotes)
+ {
+ if (*lpTemp == '\\')
+ TranslateSpecialChar (&lpTemp, &buffer);
+ else if (*lpTemp == '\"')
+ {
+ inquotes = !inquotes;
+ lpTemp++;
+ if (!GetNextString (&lpTemp, &fEnd))
+ break;
+ }
+ else if (*lpTemp == '%')
+ {
+ lpTemp++;
+ DoVarTranslate (&lpTemp, &buffer, MAXLEN-(buffer-lpBuffer), TRUE);
+ }
+ else
+ {
+ *buffer = *lpTemp;
+ if (IsDBCSLeadByte(*buffer))
+ {
+ buffer++;
+ lpTemp++;
+ *buffer = *lpTemp;
+ }
+ buffer++;
+ lpTemp++;
+ }
+ }
+ else
+ {
+ if (*lpTemp == '\"')
+ {
+ inquotes = !inquotes;
+ lpTemp++;
+ }
+ else
+ {
+ if (!DoVarTranslate (&lpTemp, &buffer, MAXLEN-(buffer-lpBuffer), FALSE) ||
+ !GetNextString (&lpTemp, &fEnd))
+ break;
+ }
+ }
+ }
+ if (!fEnd)
+ {
+ if ( inquotes )
+ DisplayMessage( IDR_NO_END_QUOTE );
+ return(FALSE);
+ }
+ *buffer = 0;
+ }
+
+ return(TRUE);
+}
+
+/*
+ * Replace the variables in the string with their value.
+ * Use this function when the input string is quoted format.
+ * Return TRUE if input buffer is right format, FALSE otherwise.
+ */
+int QuotedStringTranslate (char *buffer)
+{
+ char szTemp[MAXLEN], *lpLeft, *lpRight, *ptr = buffer, *pNext;
+ int inquotes;
+
+ lpLeft = *buffer == '('? buffer : NULL;
+ lpRight = *buffer == ')'? buffer : NULL;
+ inquotes = (*ptr == '"');
+
+ while (*ptr)
+ {
+ pNext = NWAnsiNext (ptr);
+
+ if (*pNext == '"' && *(ptr) != '\\')
+ {
+ pNext++;
+ inquotes = !inquotes;
+ }
+
+ ptr = pNext;
+
+ if (!inquotes)
+ {
+ if (*ptr == '(')
+ lpLeft = ptr;
+ else if (*ptr == ')')
+ {
+ lpRight = ptr;
+
+ *lpRight = 0;
+
+ if (lpLeft == NULL)
+ return(FALSE);
+
+ if (lpRight - lpLeft <= 1) //There should be something in the backets.
+ return(FALSE);
+
+ *lpLeft = 0;
+
+ strncpy (szTemp, lpLeft+1, lpRight-lpLeft);
+
+ if (!SingleStringTranslate (szTemp))
+ return(FALSE);
+
+ if (strlen (buffer) + strlen(szTemp) + strlen (lpRight+1) + 2 >= MAXLEN)
+ return(FALSE);
+
+ *lpLeft = '"';
+ *(lpLeft+1+strlen(szTemp)) = '"';
+ memmove (lpLeft+2+strlen(szTemp), lpRight+1, strlen (lpRight+1)+1);
+ memmove (lpLeft+1, szTemp, strlen(szTemp));
+
+ lpLeft = *buffer == '('? buffer : NULL;
+ lpRight = *buffer == ')'? buffer : NULL;
+ ptr = buffer;
+ inquotes = (*ptr == '"');
+ }
+ }
+ }
+
+ if (lpLeft != NULL || lpRight != NULL)
+ return(FALSE);
+ return(SingleStringTranslate (buffer));
+}
+
+
+void BreakOff(void)
+{
+ fBreakOn = FALSE;
+
+ NTBreakOff();
+}
+
+void BreakOn(void)
+{
+ fBreakOn = TRUE;
+
+ NTBreakOn();
+}
+
+/*
+ * Used by ComspecHandler() and SetHandler()
+ * Set dos environment variable.
+ */
+int SetEnv (char *lpEnvLine)
+{
+ ExportEnv( lpEnvLine );
+ return(TRUE);
+}
diff --git a/private/nw/nwscript/sources b/private/nw/nwscript/sources
new file mode 100644
index 000000000..3425d5c96
--- /dev/null
+++ b/private/nw/nwscript/sources
@@ -0,0 +1,85 @@
+!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:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+BLDCRT=1
+
+MAJORCOMP=utils
+MINORCOMP=nwscript
+
+C_DEFINES = -DNT=1 -DUNICODE=1
+MSC_WARNING_LEVEL=/W3 /WX
+
+TARGETNAME=nwscript
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+INCLUDES=inc;..\inc;..\..\inc;..\..\..\inc;$(_NTROOT)\private\inc;
+#GPSIZE=32
+
+SOURCES=capture.c \
+ psdb.c \
+ ntcap.c \
+ helpers.c \
+ script.c \
+ display.c \
+ nwapi1.c \
+ nwapi2.c \
+ nwapi3.c \
+ common.c \
+ strings.c \
+ ntscript.c \
+ dbcs.c \
+ parspath.c \
+ nt.c \
+ ntnw.c \
+ drvstat.c \
+ env.c \
+ break.c \
+ attach.c \
+ ncp.c \
+ drive.c \
+ version.c \
+ date.c \
+ wide.c \
+ unc.c \
+ nds.c \
+ ps40db.c \
+ time.c \
+ maplist.c \
+ lsparse.c \
+ nwscript.rc
+
+
+UMTYPE=console
+UMAPPL=nwscript
+UMLIBS=$(BASEDIR)\Public\Sdk\Lib\*\kernel32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\user32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\mpr.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\ntdll.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\nwapi32.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\nwprovau.lib \
+ obj\*\nwscript.lib
+
+UMRES=obj\*\nwscript.res
+
diff --git a/private/nw/nwscript/strings.c b/private/nw/nwscript/strings.c
new file mode 100644
index 000000000..57fb68fab
--- /dev/null
+++ b/private/nw/nwscript/strings.c
@@ -0,0 +1,132 @@
+
+/*************************************************************************
+*
+* STRINGS.C
+*
+* Various strings
+*
+* Copyright (c) 1995 Microsoft Corporation
+*
+* $Log: N:\NT\PRIVATE\NW4\NWSCRIPT\VCS\STRINGS.C $
+*
+* Rev 1.1 22 Dec 1995 14:26:50 terryt
+* Add Microsoft headers
+*
+* Rev 1.0 15 Nov 1995 18:08:06 terryt
+* Initial revision.
+*
+* Rev 1.1 25 Aug 1995 16:23:56 terryt
+* Capture support
+*
+* Rev 1.0 15 May 1995 19:11:06 terryt
+* Initial revision.
+*
+*************************************************************************/
+
+#include "common.h"
+
+/*
+ * These haven't been put into resource files, because they aren't user
+ * messages. Most are control information or variables. To do this
+ * right, all output and string processing would have to be changed to
+ * unicode. This can't be done until NetWare and International are
+ * understood.
+ */
+
+
+char *__GREETING__[3] = {"morning", "afternoon", "evening"};
+char *__AMPM__[2] = {"am", "pm"};
+char *__Day__[7] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
+char *__Month__[12] = {"January", "Feburary", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};
+char __DEL__[] ="DELETE";
+char __REM__[] ="REMOVE";
+char __INS__[] ="INSERT";
+char __ROOT__[] ="ROOT";
+char __NEXT__[] ="NEXT";
+
+/*
+ * Capture strings
+ */
+unsigned int CaptureStringsLoaded = FALSE;
+WCHAR __DISABLED__[256];
+WCHAR __ENABLED__[256];
+WCHAR __YES__[256];
+WCHAR __NO__[256];
+WCHAR __SECONDS__[256];
+WCHAR __CONVERT_TO_SPACE__[256];
+WCHAR __NO_CONVERSION__[256];
+WCHAR __NOTIFY_USER__[256];
+WCHAR __NOT_NOTIFY_USER__[256];
+WCHAR __NONE__[256];
+
+char __JOB_DESCRIPTION__[] ="LPT%d Catch";
+
+char __OPT_NO__[] ="No";
+
+char __SHOW__[] ="SHOW";
+
+char __NOTIFY__[] ="NOTIFY";
+char __SHORT_FOR_NOTIFY__[] ="NOTI";
+
+char __NONOTIFY__[] ="NONOTIFY";
+char __SHORT_FOR_NONOTIFY__[]="NNOTI";
+
+char __AUTOENDCAP__[] ="AUTOENDCAP";
+char __SHORT_FOR_AUTOENDCAP__[] ="AU";
+
+char __NOAUTOENDCAP__[] ="NOAUTOENDCAP";
+char __SHORT_FOR_NOAUTOENDCAP__[] ="NA";
+
+char __NOTABS__[] ="NOTABS";
+char __SHORT_FOR_NOTABS__[] ="NT";
+
+char __NOBANNER__[] ="NOBANNER";
+char __SHORT_FOR_NOBANNER__[] ="NB";
+
+char __FORMFEED__[] ="FORMFEED";
+char __SHORT_FOR_FORMFEED__[] ="FF";
+
+char __NOFORMFEED__[] ="NOFORMFEED";
+char __SHORT_FOR_NOFORMFEED__[] ="NFF";
+
+char __KEEP__[] ="KEEP";
+char __SHORT_FOR_KEEP__[] ="K";
+
+char __TIMEOUT__[] ="TIMEOUT";
+char __SHORT_FOR_TIMEOUT__[] ="TI";
+
+char __LOCAL__[] ="LOCAL";
+char __SHORT_FOR_LOCAL__[] ="L";
+
+char __LOCAL_3__[] ="LPT";
+char __LOCAL_2__[] ="LP";
+
+char __JOB__[] ="JOB";
+char __SHORT_FOR_JOB__[] ="J";
+
+char __SERVER__[] ="SERVER";
+char __SHORT_FOR_SERVER__[] ="S";
+
+char __QUEUE__[] ="QUEUE";
+char __SHORT_FOR_QUEUE__[] ="Q";
+
+char __PRINTER__[] ="PRINTER";
+char __SHORT_FOR_PRINTER__[] ="P";
+
+char __CREATE__[] ="CREATE";
+char __SHORT_FOR_CREATE__[] ="CR";
+
+char __FORM__[] ="FORM";
+char __SHORT_FOR_FORM__[] ="F";
+
+char __COPIES__[] ="COPIES";
+char __SHORT_FOR_COPIES__[] ="C";
+
+char __TABS__[] ="TABS";
+char __SHORT_FOR_TABS__[] ="T";
+
+char __NAME__[] ="NAME";
+char __SHORT_FOR_NAME__[] ="NAM";
+
+char __BANNER__[] ="BANNER";
+char __SHORT_FOR_BANNER__[] ="B";
diff --git a/private/nw/nwscript/time.c b/private/nw/nwscript/time.c
new file mode 100644
index 000000000..26639359a
--- /dev/null
+++ b/private/nw/nwscript/time.c
@@ -0,0 +1,184 @@
+/*
+ * TIME.C - Various time subroutines needed by NetWare Login Script
+ *
+ * Copyright (c) 1995 Microsoft Corporation
+ */
+
+#include "common.h"
+
+// Needed to convert netware net date to DOS date
+#define _70_to_80_bias 0x012CEA600L
+#define SECS_IN_DAY (60L*60L*24L)
+#define SEC2S_IN_DAY (30L*60L*24L)
+#define FOURYEARS (3*365+366)
+
+WORD MonTotal[] = { 0, // dummy entry for month 0
+ 0, // days before Jan 1
+ 31, // days before Feb 1
+ 31+28, // days before Mar 1
+ 31+28+31, // days before Apr 1
+ 31+28+31+30, // days before May 1
+ 31+28+31+30+31, // days before Jun 1
+ 31+28+31+30+31+30, // days before Jul 1
+ 31+28+31+30+31+30+31, // days before Aug 1
+ 31+28+31+30+31+30+31+31, // days before Sep 1
+ 31+28+31+30+31+30+31+31+30, // days before Oct 1
+ 31+28+31+30+31+30+31+31+30+31, // days before Nov 1
+ 31+28+31+30+31+30+31+31+30+31+30, // days before Dec 1
+ 31+28+31+30+31+30+31+31+30+31+30+31 // days before end of year
+};
+
+#define YR_MASK 0xFE00
+#define LEAPYR_MASK 0x0600
+#define YR_BITS 7
+#define MON_MASK 0x01E0
+#define MON_BITS 4
+#define DAY_MASK 0x001F
+#define DAY_BITS 5
+
+#define HOUR_MASK 0xF800
+#define HOUR_BITS 5
+#define MIN_MASK 0x07E0
+#define MIN_BITS 6
+#define SEC2_MASK 0x001F
+#define SEC2_BITS 5
+
+static void NetToDosDate( DWORD time, WORD * dosdate, WORD * dostime )
+{
+ DWORD secs, days;
+ WORD r;
+
+ time = (time - _70_to_80_bias) / 2; // # of 2 second periods since 1980
+ secs = time % SEC2S_IN_DAY; // 2 second period into day
+ days = time / SEC2S_IN_DAY; // days since Jan 1 1980
+
+ r = (WORD) ( secs % 30 ); // # of 2 second steps
+ secs /= 30;
+ r |= (secs % 60) << SEC2_BITS; // # of minutes
+ r |= (secs / 60) << SEC2_BITS+MIN_BITS; // # of hours
+ *dostime = r;
+
+ r = (WORD) ( days / FOURYEARS );// (r) = four year period past 1980
+ days %= FOURYEARS; // (days) = days into four year period
+ r *= 4; // (r) = years since 1980 (within 3)
+
+ if (days == 31+28) {
+ //* Special case for FEB 29th
+ r = (r<<(MON_BITS+DAY_BITS)) + (2<<DAY_BITS) + 29;
+ } else {
+ if (days > 31+28)
+ --days; // compensate for leap year
+ while (days >= 365) {
+ ++r;
+ days -= 365;
+ }
+
+ for (secs = 1; days >= MonTotal[secs+1] ; ++secs)
+ ;
+ days -= MonTotal[secs];
+ r <<= MON_BITS;
+ r += (WORD)secs;
+ r <<= DAY_BITS;
+ r += (WORD)days+1;
+ }
+ *dosdate = r;
+}
+
+
+#define TIMEDATE_SIZE 64
+
+void nwShowLastLoginTime(VOID)
+{
+ LONG lTime = 0L;
+ SYSTEMTIME st;
+ FILETIME ft;
+ TIME_ZONE_INFORMATION tz;
+ WCHAR szTimeBuf[TIMEDATE_SIZE];
+ WCHAR szDateBuf[TIMEDATE_SIZE];
+ int ret;
+ WORD dostime, dosdate;
+ DWORD tzStat;
+
+ if ( ret = NDSGetUserProperty ("Last Login Time", (PBYTE)&lTime,
+ 4, NULL, NULL) )
+ {
+ #ifdef DEBUG
+ OutputDebugString("NWLSPROC: error getting LOGIN TIME\n\r");
+ #endif
+ return;
+ }
+
+ // From NetWare we get seconds from 1970, need to go through
+ // several conversions to get system time for NLS
+
+ // First deduct bias from UTC time to correct for local time
+ tzStat = GetTimeZoneInformation(&tz);
+ if ( tzStat != (DWORD)-1 ) {
+ if (tzStat == TIME_ZONE_ID_STANDARD)
+ tz.Bias += tz.StandardBias;
+ else if (tzStat == TIME_ZONE_ID_DAYLIGHT)
+ tz.Bias += tz.DaylightBias;
+ lTime -= tz.Bias*60;
+ }
+#ifdef DEBUG
+ else {
+ OutputDebugString("NWLSPROC: GetTimeZoneInformation failed\n\r");
+ }
+#endif // DEBUG
+
+ NetToDosDate( lTime, &dosdate, &dostime );
+ DosDateTimeToFileTime ( dosdate, dostime, &ft );
+ FileTimeToSystemTime ( &ft, &st );
+
+#ifdef notdef
+ // I don't understand this comment, this code doesn't seem to be
+ // needed for NT. - terry
+ //
+ // This code will work on NT, but not on Win95.
+ // Convert the resulting system (UTC) time to local time
+ if ( GetTimeZoneInformation(&tz) != (DWORD)-1 ) {
+ SYSTEMTIME utcTime = st;
+ SystemTimeToTzSpecificLocalTime ( &tz, &utcTime, &st );
+ }
+#ifdef DEBUG
+ else {
+ OutputDebugString("NWLSPROC: GetTimeZoneInformation failed\n\r");
+ }
+#endif // DEBUG
+#endif
+
+ wcscpy(szTimeBuf, L"");
+ ret = GetTimeFormat ( GetSystemDefaultLCID(),
+ TIME_FORCE24HOURFORMAT|TIME_NOTIMEMARKER,
+ &st,
+ NULL,
+ szTimeBuf,
+ TIMEDATE_SIZE );
+#ifdef DEBUG
+ if ( !ret ) {
+ char buf[80];
+ wsprintf(buf,"NWLSPROC: GetTimeFormatA failure: %d sec:%ld\n\r",
+ GetLastError(), lTime );
+ OutputDebugString(buf);
+ }
+#endif
+ ret = GetDateFormat(LOCALE_USER_DEFAULT,
+ DATE_LONGDATE,
+ &st,
+ NULL,
+ szDateBuf,
+ TIMEDATE_SIZE );
+#ifdef DEBUG
+ if ( !ret ) {
+ char buf[80];
+ wsprintf(buf,"NWLSPROC: GetDateFormatA failure: %d sec:%ld\n\r",
+ GetLastError(), lTime );
+ OutputDebugString(buf);
+ }
+#endif
+
+ DisplayMessage( IDR_LASTLOGIN, szDateBuf, szTimeBuf );
+}
+
+
+
diff --git a/private/nw/nwscript/unc.c b/private/nw/nwscript/unc.c
new file mode 100644
index 000000000..8f99320b9
--- /dev/null
+++ b/private/nw/nwscript/unc.c
@@ -0,0 +1,142 @@
+/*************************************************************************
+*
+* UNC.C
+*
+* NetWare format to UNC format
+*
+* Copyright (c) 1995 Microsoft Corporation
+*
+* $Log: N:\NT\PRIVATE\NW4\NWSCRIPT\VCS\UNC.C $
+*
+* Rev 1.4 10 Apr 1996 14:24:00 terryt
+* Hotfix for 21181hq
+*
+* Rev 1.4 12 Mar 1996 19:56:18 terryt
+* Relative NDS names and merge
+*
+* Rev 1.3 04 Jan 1996 18:57:26 terryt
+* Bug fixes reported by MS
+*
+* Rev 1.2 22 Dec 1995 14:27:04 terryt
+* Add Microsoft headers
+*
+* Rev 1.1 22 Dec 1995 11:09:18 terryt
+* Fixes
+*
+* Rev 1.0 15 Nov 1995 18:08:14 terryt
+* Initial revision.
+*
+* Rev 1.1 23 May 1995 19:37:24 terryt
+* Spruce up source
+*
+* Rev 1.0 15 May 1995 19:11:10 terryt
+* Initial revision.
+*
+*************************************************************************/
+#include <stdio.h>
+#include <direct.h>
+#include <time.h>
+#include <stdlib.h>
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include "inc/common.h"
+
+/********************************************************************
+
+ NTNWtoUNCFormat
+
+Routine Description:
+
+ Given a connection handle and a path, change it to UNC format
+ if it's in NetWare format. I.E.
+
+ SYS:\usr\terryt ==> \\HELIUM\SYS\usr\terryt
+
+ Don't do the conversion if it's not in NetWare format.
+
+Arguments:
+
+ ConnectionHandle - Connection Handle
+ NetWarePath - Input original path
+
+Return Value:
+ UNC string
+
+ *******************************************************************/
+char *
+NTNWtoUNCFormat( char * NetWarePath )
+{
+ static char UNCPath[1024];
+ unsigned int Result;
+ char ServerName[48];
+ char * p;
+ char * q;
+
+ /*
+ * If it's UNC already, leave it alone
+ */
+ if ( ( NetWarePath[0] == '\\' ) && ( NetWarePath[1] == '\\' ) )
+ return NetWarePath;
+ if ( ( NetWarePath[0] == '/' ) && ( NetWarePath[1] == '/' ) )
+ return NetWarePath;
+
+ /*
+ * if it's drive:dir, leave it alone
+ */
+ if ( NetWarePath[0] && ( NetWarePath[1] == ':' ) )
+ return NetWarePath;
+
+ /*
+ * if it's not volume:dir, leave it alone
+ */
+ p = strchr( NetWarePath, ':' );
+ if ( !p )
+ return NetWarePath;
+
+ /*
+ * if slashes before :, it must be a file server
+ */
+ q = strchr( NetWarePath, '\\' );
+ if ( q && ( q < p ) )
+ {
+ strcpy( UNCPath, "\\\\" );
+ *p = '\0';
+ strcat( UNCPath, NetWarePath );
+ if (( *(p + 1) != '\\' ) && ( *(p + 1) != '/' ) )
+ strcat( UNCPath, "\\" );
+ strcat( UNCPath, p + 1 );
+ *p = ':';
+ return UNCPath;
+ }
+
+ q = strchr( NetWarePath, '/' );
+ if ( q && ( q < p ) )
+ {
+ strcpy( UNCPath, "\\\\" );
+ *q = '\\';
+ *p = '\0';
+ strcat( UNCPath, NetWarePath );
+ if (( *(p + 1) != '\\' ) && ( *(p + 1) != '/' ) )
+ strcat( UNCPath, "\\" );
+ strcat( UNCPath, p + 1 );
+ *q = '/';
+ *p = ':';
+ return UNCPath;
+ }
+
+ strcpy( UNCPath, "\\\\" );
+ strcat( UNCPath, PREFERRED_SERVER );
+ strcat( UNCPath, "\\" );
+ *p = '\0';
+ strcat( UNCPath, NetWarePath );
+ if (( *(p + 1) != '\\' ) && ( *(p + 1) != '/' ) )
+ strcat( UNCPath, "\\" );
+ strcat( UNCPath, p + 1 );
+ *p = ':';
+
+ return UNCPath;
+}
diff --git a/private/nw/nwscript/version.c b/private/nw/nwscript/version.c
new file mode 100644
index 000000000..b4ce000d6
--- /dev/null
+++ b/private/nw/nwscript/version.c
@@ -0,0 +1,68 @@
+/*************************************************************************
+*
+* VERSION.C
+*
+* Shell version information
+*
+* Copyright (c) 1995 Microsoft Corporation
+*
+* $Log: N:\NT\PRIVATE\NW4\NWSCRIPT\VCS\VERSION.C $
+*
+* Rev 1.2 10 Apr 1996 14:24:08 terryt
+* Hotfix for 21181hq
+*
+* Rev 1.2 12 Mar 1996 19:56:28 terryt
+* Relative NDS names and merge
+*
+* Rev 1.1 22 Dec 1995 14:27:10 terryt
+* Add Microsoft headers
+*
+* Rev 1.0 15 Nov 1995 18:08:18 terryt
+* Initial revision.
+*
+* Rev 1.1 26 Jul 1995 14:17:24 terryt
+* Clean up comments
+*
+* Rev 1.0 15 May 1995 19:11:12 terryt
+* Initial revision.
+*
+*************************************************************************/
+
+#include <stdio.h>
+#include <direct.h>
+#include <time.h>
+#include <stdlib.h>
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include "nwscript.h"
+
+
+/*
+ * MSDOS is not neccessarily the best thing to put out,
+ * maybe Windows_NT, NT or NTDOS. The OS_VERSION is also a problem.
+ * The script variables don't neccessarily have to match the DOS variables.
+ *
+ * The shell version numbers may change with 4.X support.
+ */
+
+#define CLIENT_ID_STRING "MSDOS\0V5.00\0IBM_PC\0IBM"
+#define CLIENT_SHELL_MAJOR 0x03
+#define CLIENT_SHELL_MINOR 0x1a
+#define CLIENT_SHELL_NUMBER 0x00
+
+
+void
+NTGetVersionOfShell( char * buffer,
+ unsigned char * shellmajor,
+ unsigned char * shellminor,
+ unsigned char * shellnum )
+{
+ *shellmajor = CLIENT_SHELL_MAJOR;
+ *shellminor = CLIENT_SHELL_MINOR;
+ *shellnum = CLIENT_SHELL_NUMBER;
+ memcpy( buffer, CLIENT_ID_STRING, 40 );
+}
diff --git a/private/nw/nwscript/wide.c b/private/nw/nwscript/wide.c
new file mode 100644
index 000000000..09578c7bd
--- /dev/null
+++ b/private/nw/nwscript/wide.c
@@ -0,0 +1,149 @@
+/*************************************************************************
+*
+* WIDE.C
+*
+* Wide character translation routines
+*
+* Copyright (c) 1995 Microsoft Corporation
+*
+* $Log: N:\NT\PRIVATE\NW4\NWSCRIPT\VCS\WIDE.C $
+*
+* Rev 1.2 10 Apr 1996 14:24:14 terryt
+* Hotfix for 21181hq
+*
+* Rev 1.2 12 Mar 1996 19:56:36 terryt
+* Relative NDS names and merge
+*
+* Rev 1.1 22 Dec 1995 14:27:18 terryt
+* Add Microsoft headers
+*
+* Rev 1.0 15 Nov 1995 18:08:20 terryt
+* Initial revision.
+*
+* Rev 1.1 23 May 1995 19:37:32 terryt
+* Spruce up source
+*
+* Rev 1.0 15 May 1995 19:11:14 terryt
+* Initial revision.
+*
+*************************************************************************/
+
+#include <stdio.h>
+#include <direct.h>
+#include <time.h>
+#include <stdlib.h>
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include "nwscript.h"
+
+
+/********************************************************************
+
+ szToWide
+
+Routine Description:
+
+ Given a single byte character string, convert to wide
+
+Arguments:
+
+ lpszW - Wide character string returned
+ lpszC - Single character string input
+ nSize - length of Wide character buffer
+
+Return Value:
+ 0 = success
+ else NT error
+
+ *******************************************************************/
+DWORD
+szToWide(
+ LPWSTR lpszW,
+ LPCSTR lpszC,
+ INT nSize
+ )
+{
+ if (!MultiByteToWideChar(CP_OEMCP,
+ MB_PRECOMPOSED,
+ lpszC,
+ -1,
+ lpszW,
+ nSize))
+ {
+ return (GetLastError()) ;
+ }
+
+ return NO_ERROR ;
+}
+
+/********************************************************************
+
+ WideTosz
+
+Routine Description:
+
+ Given a wide character string, convert to single
+
+Arguments:
+
+ lpszC - Single character string returned
+ lpszW - Wide character string input
+ nSize - length of single character buffer
+
+Return Value:
+ 0 = success
+ else NT error
+
+ *******************************************************************/
+DWORD
+WideTosz(
+ LPSTR lpszC,
+ LPWSTR lpszW,
+ INT nSize
+ )
+{
+ if (!WideCharToMultiByte(CP_OEMCP,
+ 0,
+ (LPCWSTR) lpszW,
+ -1,
+ lpszC,
+ nSize,
+ NULL,
+ NULL))
+ {
+ return (GetLastError()) ;
+ }
+
+ return NO_ERROR ;
+}
+
+/********************************************************************
+
+ ConvertUnicodeToAscii
+
+Routine Description:
+
+ Given a wide character string, convert to single
+
+Arguments:
+
+ Buffer - buffer to be converted
+
+Return Value:
+ none
+
+ *******************************************************************/
+void
+ConvertUnicodeToAscii( PVOID Buffer )
+{
+ LPCWSTR lpszW = Buffer;
+ BYTE Destination[1024];
+
+ WideTosz( (LPSTR)Destination, (LPWSTR)Buffer, 1024 );
+
+ strcpy( Buffer, Destination );
+}
diff --git a/private/nw/perf/makefile b/private/nw/perf/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/nw/perf/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/perf/nwdata.c b/private/nw/perf/nwdata.c
new file mode 100644
index 000000000..af2b1b8a3
--- /dev/null
+++ b/private/nw/perf/nwdata.c
@@ -0,0 +1,296 @@
+//
+// NWData.c
+//
+// This file contains the initalized Object and counter definition for NetWare
+// redirector performance extensible DLL.
+// This would be the place to add more counters and the change the definitions
+// of the current ones.
+//
+#include "windows.h"
+#include <winperf.h>
+#include "NWPerf.h"
+
+NW_DATA_DEFINITION NWDataDefinition = {
+ { sizeof(NW_DATA_DEFINITION)+
+ SIZE_OF_COUNTER_BLOCK, // Total Bytes ( Size of this header, the counter definitions
+ // and the size of the actual counter data )
+ sizeof(NW_DATA_DEFINITION), // Definition length ( This header and the counter definitions )
+ sizeof(PERF_OBJECT_TYPE), // Header Length ( This header )
+ NWOBJ, // Object Name Title Index
+ 0, // Object Name Title
+ NWOBJ, // Object Help Title Index
+ 0, // Object Help Title
+ PERF_DETAIL_NOVICE, // Detail Level
+ (sizeof(NW_DATA_DEFINITION)-sizeof(PERF_OBJECT_TYPE))/
+ sizeof(PERF_COUNTER_DEFINITION), // Number of Counters
+ 0, // Default Counters
+ 0, // Num Instances
+ 0, // Code Page
+ {0,0}, // Perf Time
+ {0,0} // Perf Freq
+ },
+ { sizeof(PERF_COUNTER_DEFINITION),
+ 388,
+ 0,
+ 389,
+ 0,
+ -4,
+ PERF_DETAIL_NOVICE,
+ PERF_COUNTER_BULK_COUNT,
+ sizeof(LARGE_INTEGER),
+ BYTES_OFFSET
+ },
+ { sizeof(PERF_COUNTER_DEFINITION),
+ 406,
+ 0,
+ 391,
+ 0,
+ 0,
+ PERF_DETAIL_NOVICE,
+ PERF_COUNTER_COUNTER,
+ sizeof(DWORD),
+ IO_OPERATIONS_OFFSET
+ },
+ { sizeof(PERF_COUNTER_DEFINITION),
+ 400,
+ 0,
+ 401,
+ 0,
+ -1,
+ PERF_DETAIL_NOVICE,
+ PERF_COUNTER_BULK_COUNT,
+ sizeof(LARGE_INTEGER),
+ PACKETS_OFFSET
+ },
+ { sizeof(PERF_COUNTER_DEFINITION),
+ 264,
+ 0,
+ 265,
+ 0,
+ -4,
+ PERF_DETAIL_ADVANCED,
+ PERF_COUNTER_BULK_COUNT,
+ sizeof(LARGE_INTEGER),
+ BYTES_RECEIVED_OFFSET
+ },
+ { sizeof(PERF_COUNTER_DEFINITION),
+ 266,
+ 0,
+ 267,
+ 0,
+ -1,
+ PERF_DETAIL_ADVANCED,
+ PERF_COUNTER_BULK_COUNT,
+ sizeof(LARGE_INTEGER),
+ NCPS_RECEIVED_OFFSET
+ },
+ { sizeof(PERF_COUNTER_DEFINITION),
+ 276,
+ 0,
+ 277,
+ 0,
+ -4,
+ PERF_DETAIL_ADVANCED,
+ PERF_COUNTER_BULK_COUNT,
+ sizeof(LARGE_INTEGER),
+ BYTES_TRANSMITTED_OFFSET
+ },
+ { sizeof(PERF_COUNTER_DEFINITION),
+ 278,
+ 0,
+ 279,
+ 0,
+ -1,
+ PERF_DETAIL_ADVANCED,
+ PERF_COUNTER_BULK_COUNT,
+ sizeof(LARGE_INTEGER),
+ NCPS_TRANSMITTED_OFFSET
+ },
+ { sizeof(PERF_COUNTER_DEFINITION),
+ 10,
+ 0,
+ 289,
+ 0,
+ 0,
+ PERF_DETAIL_NOVICE,
+ PERF_COUNTER_COUNTER,
+ sizeof(DWORD),
+ RDR_READ_OPERATIONS_OFFSET
+ },
+ { sizeof(PERF_COUNTER_DEFINITION),
+ 290,
+ 0,
+ 291,
+ 0,
+ -1,
+ PERF_DETAIL_ADVANCED,
+ PERF_COUNTER_COUNTER,
+ sizeof(DWORD),
+ RANDOM_READ_OPERATIONS_OFFSET
+ },
+ { sizeof(PERF_COUNTER_DEFINITION),
+ 292,
+ 0,
+ 293,
+ 0,
+ -1,
+ PERF_DETAIL_ADVANCED,
+ PERF_COUNTER_COUNTER,
+ sizeof(DWORD),
+ READ_NCPS_OFFSET
+ },
+ { sizeof(PERF_COUNTER_DEFINITION),
+ 12,
+ 0,
+ 299,
+ 0,
+ 0,
+ PERF_DETAIL_NOVICE,
+ PERF_COUNTER_COUNTER,
+ sizeof(DWORD),
+ RDR_WRITE_OPERATIONS_OFFSET
+ },
+ { sizeof(PERF_COUNTER_DEFINITION),
+ 300,
+ 0,
+ 301,
+ 0,
+ -1,
+ PERF_DETAIL_ADVANCED,
+ PERF_COUNTER_COUNTER,
+ sizeof(DWORD),
+ RANDOM_WRITE_OPERATIONS_OFFSET
+ },
+ { sizeof(PERF_COUNTER_DEFINITION),
+ 302,
+ 0,
+ 303,
+ 0,
+ -1,
+ PERF_DETAIL_ADVANCED,
+ PERF_COUNTER_COUNTER,
+ sizeof(DWORD),
+ WRITE_NCPS_OFFSET
+ },
+ { sizeof(PERF_COUNTER_DEFINITION),
+ 314,
+ 0,
+ 315,
+ 0,
+ 0,
+ PERF_DETAIL_NOVICE,
+ PERF_COUNTER_RAWCOUNT,
+ sizeof(DWORD),
+ SESSIONS_OFFSET
+ },
+ { sizeof(PERF_COUNTER_DEFINITION),
+ 316,
+ 0,
+ 317,
+ 0,
+ 0,
+ PERF_DETAIL_ADVANCED,
+ PERF_COUNTER_RAWCOUNT,
+ sizeof(DWORD),
+ RECONNECTS_OFFSET
+ },
+ { sizeof(PERF_COUNTER_DEFINITION),
+ CONNECT_2X_ID,
+ 0,
+ CONNECT_2X_ID,
+ 0,
+ 0,
+ PERF_DETAIL_ADVANCED,
+ PERF_COUNTER_RAWCOUNT,
+ sizeof(DWORD),
+ NETWARE_2X_CONNECTS_OFFSET
+ },
+ { sizeof(PERF_COUNTER_DEFINITION),
+ CONNECT_3X_ID,
+ 0,
+ CONNECT_3X_ID,
+ 0,
+ 0,
+ PERF_DETAIL_ADVANCED,
+ PERF_COUNTER_RAWCOUNT,
+ sizeof(DWORD),
+ NETWARE_3X_CONNECTS_OFFSET
+ },
+ { sizeof(PERF_COUNTER_DEFINITION),
+ CONNECT_4X_ID,
+ 0,
+ CONNECT_4X_ID,
+ 0,
+ 0,
+ PERF_DETAIL_ADVANCED,
+ PERF_COUNTER_RAWCOUNT,
+ sizeof(DWORD),
+ NETWARE_4X_CONNECTS_OFFSET
+ },
+ { sizeof(PERF_COUNTER_DEFINITION),
+ 326,
+ 0,
+ 327,
+ 0,
+ 0,
+ PERF_DETAIL_ADVANCED,
+ PERF_COUNTER_RAWCOUNT,
+ sizeof(DWORD),
+ SERVER_DISCONNECTS_OFFSET
+ },
+ { sizeof(PERF_COUNTER_DEFINITION),
+ PACKET_BURST_READ_ID,
+ 0,
+ PACKET_BURST_READ_ID,
+ 0,
+ 0,
+ PERF_DETAIL_ADVANCED,
+ PERF_COUNTER_COUNTER,
+ sizeof(DWORD),
+ PACKET_BURST_READ_OFFSET
+ },
+ { sizeof(PERF_COUNTER_DEFINITION),
+ PACKET_BURST_READ_TO_ID,
+ 0,
+ PACKET_BURST_READ_TO_ID,
+ 0,
+ 0,
+ PERF_DETAIL_ADVANCED,
+ PERF_COUNTER_COUNTER,
+ sizeof(DWORD),
+ PACKET_BURST_READ_TO_OFFSET
+ },
+ { sizeof(PERF_COUNTER_DEFINITION),
+ PACKET_BURST_WRITE_ID,
+ 0,
+ PACKET_BURST_WRITE_ID,
+ 0,
+ 0,
+ PERF_DETAIL_ADVANCED,
+ PERF_COUNTER_COUNTER,
+ sizeof(DWORD),
+ PACKET_BURST_WRITE_OFFSET
+ },
+ { sizeof(PERF_COUNTER_DEFINITION),
+ PACKET_BURST_WRITE_TO_ID,
+ 0,
+ PACKET_BURST_WRITE_TO_ID,
+ 0,
+ 0,
+ PERF_DETAIL_ADVANCED,
+ PERF_COUNTER_COUNTER,
+ sizeof(DWORD),
+ PACKET_BURST_WRITE_TO_OFFSET
+ },
+ { sizeof(PERF_COUNTER_DEFINITION),
+ PACKET_BURST_IO_ID,
+ 0,
+ PACKET_BURST_IO_ID,
+ 0,
+ 0,
+ PERF_DETAIL_ADVANCED,
+ PERF_COUNTER_COUNTER,
+ sizeof(DWORD),
+ PACKET_BURST_IO_OFFSET
+ }
+};
diff --git a/private/nw/perf/nwperf.c b/private/nw/perf/nwperf.c
new file mode 100644
index 000000000..28d711e54
--- /dev/null
+++ b/private/nw/perf/nwperf.c
@@ -0,0 +1,398 @@
+/****************************************************************************
+
+ PROGRAM: NWPerf.c
+
+ PURPOSE: Contains library routines for providing perfmon with data
+
+ FUNCTIONS:
+*******************************************************************************/
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#include <winperf.h>
+#include <ntddnwfs.h>
+#include "NWPerf.h"
+#include "prfutil.h"
+
+#ifndef QFE_BUILD
+#include "ntprfctr.h"
+#endif
+
+
+
+BOOL gbInitOK = FALSE;
+
+HANDLE hNetWareRdr ;
+extern NW_DATA_DEFINITION NWDataDefinition;
+
+#ifdef QFE_BUILD
+TCHAR PerformanceKeyName [] =
+ TEXT("SYSTEM\\CurrentControlSet\\Services\\NWrdr\\Performance");
+TCHAR FirstCounterKeyName [] = TEXT("First Counter");
+TCHAR FirstHelpKeyName [] = TEXT("First Help");
+#endif
+
+/****************************************************************************
+ FUNCTION: OpenNetWarePerformanceData
+
+ Purpose: This routine also initializes the data structures used to pass
+ data back to the registry
+
+ Return: None.
+r****************************************************************************/
+DWORD APIENTRY
+OpenNetWarePerformanceData(
+ LPWSTR pInstances )
+{
+
+ LONG status;
+#ifdef QFE_BUILD
+ HKEY hKeyPerf = 0;
+ DWORD size;
+ DWORD type;
+ DWORD dwFirstCounter;
+ DWORD dwFirstHelp;
+#else
+ NT_PRODUCT_TYPE ProductType;
+ DWORD dwFirstCounter = NWCS_CLIENT_COUNTER_INDEX ;
+ DWORD dwFirstHelp = NWCS_CLIENT_HELP_INDEX ;
+#endif
+
+ IO_STATUS_BLOCK IoStatusBlock;
+ RTL_RELATIVE_NAME RelativeName;
+ UNICODE_STRING DeviceNameU;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+
+#ifdef QFE_BUILD
+ status = RegOpenKeyEx ( HKEY_LOCAL_MACHINE,
+ PerformanceKeyName,
+ 0L, KEY_ALL_ACCESS, &hKeyPerf );
+
+ if (status != ERROR_SUCCESS) {
+ goto OpenExitPoint;
+ }
+
+ size = sizeof (DWORD);
+ status = RegQueryValueEx( hKeyPerf, FirstCounterKeyName, 0L, &type,
+ (LPBYTE)&dwFirstCounter, &size);
+
+ if (status != ERROR_SUCCESS) {
+ goto OpenExitPoint;
+ }
+
+ size = sizeof (DWORD);
+ status = RegQueryValueEx( hKeyPerf, FirstHelpKeyName,
+ 0L, &type, (LPBYTE)&dwFirstHelp, &size );
+
+ if (status != ERROR_SUCCESS) {
+ goto OpenExitPoint;
+ }
+#endif
+
+ //
+ // NOTE: the initialization program could also retrieve
+ // LastCounter and LastHelp if they wanted to do
+ // bounds checking on the new number. e.g.
+ //
+ // counter->CounterNameTitleIndex += dwFirstCounter;
+ // if (counter->CounterNameTitleIndex > dwLastCounter) {
+ // LogErrorToEventLog (INDEX_OUT_OF_BOUNDS);
+ // }
+
+ NWDataDefinition.NWObjectType.ObjectNameTitleIndex += dwFirstCounter;
+ NWDataDefinition.NWObjectType.ObjectHelpTitleIndex += dwFirstHelp;
+
+ // Counters not defined in Redirector, setup the correct IDs
+ NWDataDefinition.PacketBurstRead.CounterNameTitleIndex += dwFirstCounter;
+ NWDataDefinition.PacketBurstRead.CounterHelpTitleIndex += dwFirstHelp;
+ NWDataDefinition.PacketBurstReadTimeouts.CounterNameTitleIndex += dwFirstCounter;
+ NWDataDefinition.PacketBurstReadTimeouts.CounterHelpTitleIndex += dwFirstHelp;
+ NWDataDefinition.PacketBurstWrite.CounterNameTitleIndex += dwFirstCounter;
+ NWDataDefinition.PacketBurstWrite.CounterHelpTitleIndex += dwFirstHelp;
+ NWDataDefinition.PacketBurstWriteTimeouts.CounterNameTitleIndex += dwFirstCounter;
+ NWDataDefinition.PacketBurstWriteTimeouts.CounterHelpTitleIndex += dwFirstHelp;
+ NWDataDefinition.PacketBurstIO.CounterNameTitleIndex += dwFirstCounter;
+ NWDataDefinition.PacketBurstIO.CounterHelpTitleIndex += dwFirstHelp;
+ NWDataDefinition.NetWare2XConnects.CounterNameTitleIndex += dwFirstCounter;
+ NWDataDefinition.NetWare2XConnects.CounterHelpTitleIndex += dwFirstHelp;
+ NWDataDefinition.NetWare3XConnects.CounterNameTitleIndex += dwFirstCounter;
+ NWDataDefinition.NetWare3XConnects.CounterHelpTitleIndex += dwFirstHelp;
+ NWDataDefinition.NetWare4XConnects.CounterNameTitleIndex += dwFirstCounter;
+ NWDataDefinition.NetWare4XConnects.CounterHelpTitleIndex += dwFirstHelp;
+
+
+#ifndef QFE_BUILD
+ // Check for WorkStation or Server and use the gateway indexes if
+ // currently running on Server.
+ // If RtlGetNtProductType is not successful or ProductType is
+ // WinNt machine, ObjectNameTitleIndex and ObjectHelpTitleIndex are set
+ // to the correct values already.
+ if ( RtlGetNtProductType( &ProductType))
+ {
+ if ( ProductType != NtProductWinNt )
+ {
+ NWDataDefinition.NWObjectType.ObjectNameTitleIndex = NWCS_GATEWAY_COUNTER_INDEX;
+ NWDataDefinition.NWObjectType.ObjectHelpTitleIndex = NWCS_GATEWAY_HELP_INDEX;
+ }
+ }
+#endif
+
+ hNetWareRdr = NULL;
+
+ RtlInitUnicodeString(&DeviceNameU, DD_NWFS_DEVICE_NAME_U);
+ RelativeName.ContainingDirectory = NULL;
+
+ InitializeObjectAttributes(&ObjectAttributes,
+ &DeviceNameU,
+ OBJ_CASE_INSENSITIVE,
+ RelativeName.ContainingDirectory,
+ NULL
+ );
+
+ status = NtCreateFile(&hNetWareRdr,
+ SYNCHRONIZE,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ NULL,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ FILE_OPEN_IF,
+ FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL,
+ 0
+ );
+
+ gbInitOK = TRUE; // ok to use this function
+
+ status = ERROR_SUCCESS; // for successful exit
+
+#ifdef QFE_BUILD
+OpenExitPoint:
+ if (hKeyPerf)
+ RegCloseKey (hKeyPerf); // close key to registry
+#endif
+
+ return ((DWORD) status);
+}
+
+
+/****************************************************************************
+ FUNCTION: CollectNetWarePerformanceData
+
+ Purpose: This routine will return the data for the NetWare counters.
+
+ Arguments:IN LPWSTR lpValueName
+ pointer to a wide character string passed by registry.
+
+ IN OUT LPVOID *lppData
+ IN: pointer to the address of the buffer to receive the
+ completed PerfDataBlock and subordinate structures. This
+ routine will append its data to the buffer starting at
+ the point referenced by *lppData.
+
+ OUT: points to the first byte after the data structure
+ added by this routine. This routine updated the value at
+ lppdata after appending its data.
+
+ IN OUT LPDWORD lpcbTotalBytes
+ IN: the address of the DWORD that tells the size in bytes
+ of the buffer referenced by the lppData argument
+
+ OUT: the number of bytes added by this routine is written
+ to the DWORD pointed to by this argument
+
+ IN OUT LPDWORD NumObjectTypes
+ IN: the address of the DWORD to receive the number of
+ objects added by this routine
+
+ OUT: the number of objects added by this routine is written
+ to the DWORD pointed to by this argument
+
+ Return: ERROR_MORE_DATA if buffer passed is too small to hold data
+ any error conditions encountered are reported
+ to the event log if event logging is enabled.
+
+ ERROR_SUCCESS if success or any other error. Errors, however
+ are also reported to the event log.
+
+****************************************************************************/
+DWORD APIENTRY
+CollectNetWarePerformanceData(
+ IN LPWSTR lpValueName,
+ IN OUT LPVOID *lppData,
+ IN OUT LPDWORD lpcbTotalBytes,
+ IN OUT LPDWORD lpNumObjectTypes)
+{
+ ULONG SpaceNeeded;
+ PDWORD pdwCounter;
+ DWORD dwQueryType;
+ PERF_COUNTER_BLOCK *pPerfCounterBlock;
+ NW_DATA_DEFINITION *pNWDataDefinition;
+ LONG status;
+ NW_REDIR_STATISTICS NWRdrStatistics;
+ LARGE_INTEGER UNALIGNED *pliCounter;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ //
+ // before doing anything else, see if Open went OK
+ //
+ if (!gbInitOK) {
+ *lpcbTotalBytes = (DWORD) 0;
+ *lpNumObjectTypes = (DWORD) 0;
+ return ERROR_SUCCESS; // yes, this is a successful exit
+ }
+
+ // see if this is a foreign (i.e. non-NT) computer data request
+ //
+ dwQueryType = GetQueryType (lpValueName);
+
+ if (dwQueryType == QUERY_FOREIGN) {
+ // this routine does not service requests for data from
+ // Non-NT computers
+ *lpcbTotalBytes = (DWORD) 0;
+ *lpNumObjectTypes = (DWORD) 0;
+ return ERROR_SUCCESS;
+ }
+
+ // If the caller only wanted some counter, check if we have 'em
+ if (dwQueryType == QUERY_ITEMS){
+ if ( !(IsNumberInUnicodeList (
+ NWDataDefinition.NWObjectType.ObjectNameTitleIndex,
+ lpValueName))) {
+ // request received for data object not provided by this routine
+ *lpcbTotalBytes = (DWORD) 0;
+ *lpNumObjectTypes = (DWORD) 0;
+ return ERROR_SUCCESS;
+ }
+ }
+
+ pNWDataDefinition = (NW_DATA_DEFINITION *) *lppData;
+
+ SpaceNeeded = sizeof(NW_DATA_DEFINITION) + SIZE_OF_COUNTER_BLOCK;
+
+ if ( *lpcbTotalBytes < SpaceNeeded ) {
+ *lpcbTotalBytes = (DWORD) 0;
+ *lpNumObjectTypes = (DWORD) 0;
+ return ((DWORD) ERROR_MORE_DATA);
+ }
+
+ //
+ // Copy the (constant, initialized) Object Type and counter definitions
+ // to the caller's data buffer
+ //
+ memmove( pNWDataDefinition, &NWDataDefinition,
+ sizeof(NW_DATA_DEFINITION) );
+
+ // Point at the byte right after all the definitions
+ pPerfCounterBlock = (PERF_COUNTER_BLOCK *) &pNWDataDefinition[1];
+
+ // The first DWORD should specify the size of actual data block
+ pPerfCounterBlock->ByteLength = SIZE_OF_COUNTER_BLOCK;
+
+ // Move the pointer up
+ pdwCounter = (PDWORD) (&pPerfCounterBlock[1]);
+
+
+ // Open the NetWare data
+ if ( hNetWareRdr != NULL) {
+ status = NtFsControlFile(hNetWareRdr,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_GET_STATISTICS,
+ NULL,
+ 0,
+ &NWRdrStatistics,
+ sizeof(NWRdrStatistics)
+ );
+ }
+ if ( hNetWareRdr != NULL && NT_SUCCESS(status) ) {
+
+ pliCounter = (LARGE_INTEGER UNALIGNED * ) (&pPerfCounterBlock[1]);
+
+ pliCounter->QuadPart = NWRdrStatistics.BytesReceived.QuadPart +
+ NWRdrStatistics.BytesTransmitted.QuadPart;
+
+ pdwCounter = (PDWORD) ++pliCounter;
+ *pdwCounter = NWRdrStatistics.ReadOperations +
+ NWRdrStatistics.WriteOperations;
+ pliCounter = (LARGE_INTEGER UNALIGNED * ) ++pdwCounter;
+ pliCounter->QuadPart = NWRdrStatistics.NcpsReceived.QuadPart +
+ NWRdrStatistics.NcpsTransmitted.QuadPart;
+ *++pliCounter = NWRdrStatistics.BytesReceived;
+ *++pliCounter = NWRdrStatistics.NcpsReceived;
+ *++pliCounter = NWRdrStatistics.BytesTransmitted;
+ *++pliCounter = NWRdrStatistics.NcpsTransmitted;
+ pdwCounter = (PDWORD) ++pliCounter;
+ *pdwCounter = NWRdrStatistics.ReadOperations;
+ *++pdwCounter = NWRdrStatistics.RandomReadOperations;
+ *++pdwCounter = NWRdrStatistics.ReadNcps;
+ *++pdwCounter = NWRdrStatistics.WriteOperations;
+ *++pdwCounter = NWRdrStatistics.RandomWriteOperations;
+ *++pdwCounter = NWRdrStatistics.WriteNcps;
+ *++pdwCounter = NWRdrStatistics.Sessions;
+ *++pdwCounter = NWRdrStatistics.Reconnects;
+ *++pdwCounter = NWRdrStatistics.NW2xConnects;
+ *++pdwCounter = NWRdrStatistics.NW3xConnects;
+ *++pdwCounter = NWRdrStatistics.NW4xConnects;
+ *++pdwCounter = NWRdrStatistics.ServerDisconnects;
+
+ *++pdwCounter = NWRdrStatistics.PacketBurstReadNcps;
+ *++pdwCounter = NWRdrStatistics.PacketBurstReadTimeouts;
+ *++pdwCounter = NWRdrStatistics.PacketBurstWriteNcps;
+ *++pdwCounter = NWRdrStatistics.PacketBurstWriteTimeouts;
+ *++pdwCounter = NWRdrStatistics.PacketBurstReadNcps +
+ NWRdrStatistics.PacketBurstWriteNcps;
+
+ *lppData = (LPVOID) ++pdwCounter;
+
+ } else {
+
+ //
+ // Failure to access Redirector: clear counters to 0
+ //
+
+ memset(&pPerfCounterBlock[1],
+ 0,
+ SIZE_OF_COUNTER_BLOCK - sizeof(pPerfCounterBlock));
+
+ pdwCounter = (PDWORD) ((PBYTE) pPerfCounterBlock + SIZE_OF_COUNTER_BLOCK);
+ *lppData = (LPVOID) pdwCounter;
+
+ }
+
+
+ // We sent data for only one Object. (Remember not to confuse this
+ // with counters. Even if more counters are added, the number of object
+ // is still only one. However, this does not mean more objects cannot
+ // be added
+ *lpNumObjectTypes = 1;
+
+ // Fill in the number of bytes we copied - incl. the definitions and the
+ // counter data.
+ *lpcbTotalBytes = ((PBYTE) pdwCounter - (PBYTE) pNWDataDefinition);
+
+ return ERROR_SUCCESS;
+}
+
+/****************************************************************************
+ FUNCTION: CloseNetWarePerformanceData
+
+ Purpose: This routine closes the open handles to NetWare performance counters
+
+
+ Return: ERROR_SUCCESS
+
+****************************************************************************/
+DWORD APIENTRY
+CloseNetWarePerformanceData(
+)
+{
+ NtClose( hNetWareRdr );
+
+ return ERROR_SUCCESS;
+
+}
+
diff --git a/private/nw/perf/nwperf.h b/private/nw/perf/nwperf.h
new file mode 100644
index 000000000..f6af9af41
--- /dev/null
+++ b/private/nw/perf/nwperf.h
@@ -0,0 +1,144 @@
+
+//
+// MODULE: NWPerf.H
+//
+// This file contains all the defines and prototypes for the performnce
+// monitoring DLL for NetWare redirector
+//
+// Date: Sept, 28 1993
+
+
+//
+// The routines that load these structures assume that all fields
+// are packed and aligned on DWORD boundries. Alpha support may
+// change this assumption so the pack pragma is used here to insure
+// the DWORD packing assumption remains valid.
+//
+#pragma pack (4)
+
+//
+// All these definitions will have to be updated when new counters are added.
+// if a new counter called COUNTX is added then the Help and Title indicies
+// defines should include a new entry - "#define COUNTXOBJ 4". This increases
+// in increments of 2 because each counter has a title and help index.
+//
+// The Offset of the counters should have another entry - with the size of
+// the data for COUNT -
+// "#define COUNTER_OFFSET_COUNTX COUNTER_OFFSET_USERS+sizeof(COUNTX_TYPE)"
+//
+// The SIZE_OF_COUNTER_BLOCK will be updated to:
+// "#define SIZE_OF_COUNTER_BLOCK COUNTER_OFFSET_COUNTX + sizeof(DWORD)"
+//
+// Finally the NW_DATA_DEFINITION will have a new PERF_COUNTER_DEFINTIION
+// entry
+
+// Title and Help index defines. These are used for looking up the Registry
+// to get at the counter indicies for the title and help strings.
+
+#define NW_NUM_OBJECTS 1
+#define NWOBJ 0
+#define PACKET_BURST_READ_ID 2
+#define PACKET_BURST_READ_TO_ID 4
+#define PACKET_BURST_WRITE_ID 6
+#define PACKET_BURST_WRITE_TO_ID 8
+#define PACKET_BURST_IO_ID 10
+#define CONNECT_2X_ID 12
+#define CONNECT_3X_ID 14
+#define CONNECT_4X_ID 16
+
+//
+// NetWare Redirector data object definitions.
+// The offsets of the counters. The first DWORD is the size of the counter
+// data block. In WinPerf, you will see this as PERF_COUNTER_BLOCK.ByteLength
+//
+#define BYTES_OFFSET sizeof(DWORD)
+#define IO_OPERATIONS_OFFSET BYTES_OFFSET + sizeof(LARGE_INTEGER)
+#define PACKETS_OFFSET IO_OPERATIONS_OFFSET + sizeof(DWORD)
+#define BYTES_RECEIVED_OFFSET PACKETS_OFFSET + \
+ sizeof(LARGE_INTEGER)
+#define NCPS_RECEIVED_OFFSET BYTES_RECEIVED_OFFSET + \
+ sizeof(LARGE_INTEGER)
+#define BYTES_TRANSMITTED_OFFSET \
+ NCPS_RECEIVED_OFFSET + \
+ sizeof(LARGE_INTEGER)
+#define NCPS_TRANSMITTED_OFFSET \
+ BYTES_TRANSMITTED_OFFSET + \
+ sizeof(LARGE_INTEGER)
+#define RDR_READ_OPERATIONS_OFFSET \
+ NCPS_TRANSMITTED_OFFSET + \
+ sizeof(LARGE_INTEGER)
+#define RANDOM_READ_OPERATIONS_OFFSET RDR_READ_OPERATIONS_OFFSET + \
+ sizeof(DWORD)
+#define READ_NCPS_OFFSET RANDOM_READ_OPERATIONS_OFFSET + \
+ sizeof(DWORD)
+#define RDR_WRITE_OPERATIONS_OFFSET READ_NCPS_OFFSET + \
+ sizeof(DWORD)
+#define RANDOM_WRITE_OPERATIONS_OFFSET RDR_WRITE_OPERATIONS_OFFSET + \
+ sizeof(DWORD)
+#define WRITE_NCPS_OFFSET RANDOM_WRITE_OPERATIONS_OFFSET + \
+ sizeof(DWORD)
+#define SESSIONS_OFFSET WRITE_NCPS_OFFSET + \
+ sizeof(DWORD)
+#define RECONNECTS_OFFSET SESSIONS_OFFSET + \
+ sizeof(DWORD)
+#define NETWARE_2X_CONNECTS_OFFSET RECONNECTS_OFFSET + \
+ sizeof(DWORD)
+#define NETWARE_3X_CONNECTS_OFFSET NETWARE_2X_CONNECTS_OFFSET + \
+ sizeof(DWORD)
+#define NETWARE_4X_CONNECTS_OFFSET NETWARE_3X_CONNECTS_OFFSET + \
+ sizeof(DWORD)
+#define SERVER_DISCONNECTS_OFFSET NETWARE_4X_CONNECTS_OFFSET + \
+ sizeof(DWORD)
+#define PACKET_BURST_READ_OFFSET SERVER_DISCONNECTS_OFFSET + \
+ sizeof(DWORD)
+#define PACKET_BURST_READ_TO_OFFSET PACKET_BURST_READ_OFFSET + \
+ sizeof(DWORD)
+#define PACKET_BURST_WRITE_OFFSET PACKET_BURST_READ_TO_OFFSET + \
+ sizeof(DWORD)
+#define PACKET_BURST_WRITE_TO_OFFSET PACKET_BURST_WRITE_OFFSET + \
+ sizeof(DWORD)
+#define PACKET_BURST_IO_OFFSET PACKET_BURST_WRITE_TO_OFFSET + \
+ sizeof(DWORD)
+#define SIZE_OF_COUNTER_BLOCK PACKET_BURST_IO_OFFSET + \
+ sizeof(DWORD)
+
+
+
+
+// The definition of the NetWare Data definition. This structure holds the
+// definition for actual NetWare object and the definition for each of the
+// counters.
+typedef struct _NW_DATA_DEFINITION {
+ PERF_OBJECT_TYPE NWObjectType;
+ PERF_COUNTER_DEFINITION Bytes;
+ PERF_COUNTER_DEFINITION IoOperations;
+ PERF_COUNTER_DEFINITION Ncps;
+ PERF_COUNTER_DEFINITION BytesReceived;
+ PERF_COUNTER_DEFINITION NcpsReceived;
+ PERF_COUNTER_DEFINITION BytesTransmitted;
+ PERF_COUNTER_DEFINITION NcpsTransmitted;
+ PERF_COUNTER_DEFINITION ReadOperations;
+ PERF_COUNTER_DEFINITION RandomReadOperations;
+ PERF_COUNTER_DEFINITION ReadNcps;
+ PERF_COUNTER_DEFINITION WriteOperations;
+ PERF_COUNTER_DEFINITION RandomWriteOperations;
+ PERF_COUNTER_DEFINITION WriteNcps;
+ PERF_COUNTER_DEFINITION Sessions;
+ PERF_COUNTER_DEFINITION Reconnects;
+ PERF_COUNTER_DEFINITION NetWare2XConnects;
+ PERF_COUNTER_DEFINITION NetWare3XConnects;
+ PERF_COUNTER_DEFINITION NetWare4XConnects;
+ PERF_COUNTER_DEFINITION ServerDisconnects;
+ PERF_COUNTER_DEFINITION PacketBurstRead;
+ PERF_COUNTER_DEFINITION PacketBurstReadTimeouts;
+ PERF_COUNTER_DEFINITION PacketBurstWrite;
+ PERF_COUNTER_DEFINITION PacketBurstWriteTimeouts;
+ PERF_COUNTER_DEFINITION PacketBurstIO;
+} NW_DATA_DEFINITION;
+
+#pragma pack ()
+
+PM_OPEN_PROC OpenNetWarePerformanceData;
+PM_COLLECT_PROC CollectNetWarePerformanceData;
+PM_CLOSE_PROC CloseNetWarePerformanceData;
+
diff --git a/private/nw/perf/nwperf.rc b/private/nw/perf/nwperf.rc
new file mode 100644
index 000000000..d9d1eee7e
--- /dev/null
+++ b/private/nw/perf/nwperf.rc
@@ -0,0 +1,11 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_DRV_NETWORK
+#define VER_FILEDESCRIPTION_STR "Client/Gateway Service for Netware Counters"
+#define VER_INTERNALNAME_STR "PERFNW.DLL"
+#define VER_ORIGINALFILENAME_STR "PERFNW.DLL"
+
+#include "common.ver"
diff --git a/private/nw/perf/perfnw.def b/private/nw/perf/perfnw.def
new file mode 100644
index 000000000..b9884595d
--- /dev/null
+++ b/private/nw/perf/perfnw.def
@@ -0,0 +1,8 @@
+LIBRARY PerfNW
+
+DESCRIPTION 'NetWare(R) Workstation Compatible Service Performance Monitor Support'
+
+EXPORTS
+ OpenNetWarePerformanceData @2
+ CollectNetWarePerformanceData @3
+ CloseNetWarePerformanceData @4
diff --git a/private/nw/perf/prfutil.c b/private/nw/perf/prfutil.c
new file mode 100644
index 000000000..f13286d93
--- /dev/null
+++ b/private/nw/perf/prfutil.c
@@ -0,0 +1,203 @@
+//
+// Prfutil.h
+//
+// Utility procedures from the VGACTRS code in the DDK
+//
+#include "windows.h"
+#include <winperf.h>
+
+#define DEFINE_STRING
+#include "prfutil.h"
+
+DWORD
+GetQueryType (
+ IN LPWSTR lpValue
+)
+/*++
+
+GetQueryType
+
+ returns the type of query described in the lpValue string so that
+ the appropriate processing method may be used
+
+Arguments
+
+ IN lpValue
+ string passed to PerfRegQuery Value for processing
+
+Return Value
+
+ QUERY_GLOBAL
+ if lpValue == 0 (null pointer)
+ lpValue == pointer to Null string
+ lpValue == pointer to "Global" string
+
+ QUERY_FOREIGN
+ if lpValue == pointer to "Foriegn" string
+
+ QUERY_COSTLY
+ if lpValue == pointer to "Costly" string
+
+ otherwise:
+
+ QUERY_ITEMS
+
+--*/
+{
+ TCHAR *pwcArgChar, *pwcTypeChar;
+ BOOL bFound;
+
+ if (lpValue == 0) {
+ return QUERY_GLOBAL;
+ } else if (*lpValue == 0) {
+ return QUERY_GLOBAL;
+ }
+
+ // check for "Global" request
+
+ pwcArgChar = lpValue;
+ pwcTypeChar = GLOBAL_STRING;
+ bFound = TRUE; // assume found until contradicted
+
+ // check to the length of the shortest string
+
+ while ((*pwcArgChar != 0) && (*pwcTypeChar != 0)) {
+ if (*pwcArgChar++ != *pwcTypeChar++) {
+ bFound = FALSE; // no match
+ break; // bail out now
+ }
+ }
+
+ if (bFound) return QUERY_GLOBAL;
+
+ // check for "Foreign" request
+
+ pwcArgChar = lpValue;
+ pwcTypeChar = FOREIGN_STRING;
+ bFound = TRUE; // assume found until contradicted
+
+ // check to the length of the shortest string
+
+ while ((*pwcArgChar != 0) && (*pwcTypeChar != 0)) {
+ if (*pwcArgChar++ != *pwcTypeChar++) {
+ bFound = FALSE; // no match
+ break; // bail out now
+ }
+ }
+
+ if (bFound) return QUERY_FOREIGN;
+
+ // check for "Costly" request
+
+ pwcArgChar = lpValue;
+ pwcTypeChar = COSTLY_STRING;
+ bFound = TRUE; // assume found until contradicted
+
+ // check to the length of the shortest string
+
+ while ((*pwcArgChar != 0) && (*pwcTypeChar != 0)) {
+ if (*pwcArgChar++ != *pwcTypeChar++) {
+ bFound = FALSE; // no match
+ break; // bail out now
+ }
+ }
+
+ if (bFound) return QUERY_COSTLY;
+
+ // if not Global and not Foreign and not Costly,
+ // then it must be an item list
+
+ return QUERY_ITEMS;
+
+}
+
+BOOL
+IsNumberInUnicodeList (
+ IN DWORD dwNumber,
+ IN LPWSTR lpwszUnicodeList
+)
+/*++
+
+IsNumberInUnicodeList
+
+Arguments:
+
+ IN dwNumber
+ DWORD number to find in list
+
+ IN lpwszUnicodeList
+ Null terminated, Space delimited list of decimal numbers
+
+Return Value:
+
+ TRUE:
+ dwNumber was found in the list of unicode number strings
+
+ FALSE:
+ dwNumber was not found in the list.
+
+--*/
+{
+ DWORD dwThisNumber;
+ TCHAR *pwcThisChar;
+ BOOL bValidNumber;
+ BOOL bNewItem;
+ TCHAR wcDelimiter; // could be an argument to be more flexible
+
+ if (lpwszUnicodeList == 0) return FALSE; // null pointer, # not founde
+
+ pwcThisChar = lpwszUnicodeList;
+ dwThisNumber = 0;
+ wcDelimiter = TEXT(' ');
+ bValidNumber = FALSE;
+ bNewItem = TRUE;
+
+ while (TRUE) {
+ switch (EvalThisChar (*pwcThisChar, wcDelimiter)) {
+ case DIGIT:
+ // if this is the first digit after a delimiter, then
+ // set flags to start computing the new number
+ if (bNewItem) {
+ bNewItem = FALSE;
+ bValidNumber = TRUE;
+ }
+ if (bValidNumber) {
+ dwThisNumber *= 10;
+ dwThisNumber += (*pwcThisChar - TEXT('0'));
+ }
+ break;
+
+ case DELIMITER:
+ // a delimter is either the delimiter character or the
+ // end of the string ('\0') if when the delimiter has been
+ // reached a valid number was found, then compare it to the
+ // number from the argument list. if this is the end of the
+ // string and no match was found, then return.
+ //
+ if (bValidNumber) {
+ if (dwThisNumber == dwNumber) return TRUE;
+ bValidNumber = FALSE;
+ }
+ if (*pwcThisChar == 0) {
+ return FALSE;
+ } else {
+ bNewItem = TRUE;
+ dwThisNumber = 0;
+ }
+ break;
+
+ case INVALID:
+ // if an invalid character was encountered, ignore all
+ // characters up to the next delimiter and then start fresh.
+ // the invalid number is not compared.
+ bValidNumber = FALSE;
+ break;
+
+ default:
+ break;
+
+ }
+ pwcThisChar++;
+ }
+
+} // IsNumberInUnicodeList
diff --git a/private/nw/perf/prfutil.h b/private/nw/perf/prfutil.h
new file mode 100644
index 000000000..58eb21abd
--- /dev/null
+++ b/private/nw/perf/prfutil.h
@@ -0,0 +1,42 @@
+//
+// Prfutil.h
+//
+// Utility procedures from the VGACTRS code in the DDK
+//
+#ifndef _PRFUTIL_
+#define _PRF_UTIL_
+
+#define QUERY_GLOBAL 1
+#define QUERY_ITEMS 2
+#define QUERY_FOREIGN 3
+#define QUERY_COSTLY 4
+
+#define DIGIT 1
+#define DELIMITER 2
+#define INVALID 3
+
+#define EvalThisChar(c,d) ( \
+ (c == d) ? DELIMITER : \
+ (c == 0) ? DELIMITER : \
+ (c < (WCHAR)'0') ? INVALID : \
+ (c > (WCHAR)'9') ? INVALID : \
+ DIGIT)
+
+BOOL IsNumberInUnicodeList ( IN DWORD dwNumber, IN LPWSTR lpwszUnicodeList );
+DWORD GetQueryType ( IN LPWSTR lpValue );
+
+// only prfutil.c will define GLOBAL_STRING
+#ifdef DEFINE_STRING
+TCHAR GLOBAL_STRING[] = TEXT("Global");
+TCHAR FOREIGN_STRING[] = TEXT("Foreign");
+TCHAR COSTLY_STRING[] = TEXT("Costly");
+TCHAR NULL_STRING[] = TEXT("\0"); // pointer to null string
+#else
+extern TCHAR GLOBAL_STRING[];
+extern TCHAR FOREIGN_STRING[];
+extern TCHAR COSTLY_STRING[];
+extern TCHAR NULL_STRING[];
+#endif
+
+
+#endif // _PRFUTIL_
diff --git a/private/nw/perf/sources b/private/nw/perf/sources
new file mode 100644
index 000000000..4f18ffc64
--- /dev/null
+++ b/private/nw/perf/sources
@@ -0,0 +1,46 @@
+!IF 0
+**************************************************************
+Sources file for building an Win32 dll.
+
+Module Name : Sources for perfnw.dll
+
+Authors: HonWah Chan
+
+Revisions: 09/28/93
+
+INCLUDES= ..\..\..\inc;..\inc
+**************************************************************
+!ENDIF
+
+DLLBASE=0x7500000
+
+MAJORCOMP=sdktools
+MINORCOMP=perfnw
+
+USE_CRTDLL=1
+
+TARGETNAME=perfnw
+TARGETPATH=\nt\public\sdk\lib
+TARGETTYPE=DYNLINK
+TARGETLIBS= \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib
+
+
+INCLUDES= ..\inc
+
+SOURCES= \
+ nwperf.c \
+ prfutil.c \
+ nwperf.rc \
+ nwdata.c
+
+UMLIBS= \
+ $(BASEDIR)\public\sdk\lib\*\advapi32.lib
+
+
+
+!IF "$(QFE_BUILD)" != "1"
+NET_C_DEFINES= -DSECURITY -DWIN32 -DSTRICT -DUNICODE -D_UNICODE
+!ELSE
+NET_C_DEFINES= -DSECURITY -DWIN32 -DSTRICT -DUNICODE -D_UNICODE -DQFE_BUILD=1
+!ENDIF
diff --git a/private/nw/rdr/attach.c b/private/nw/rdr/attach.c
new file mode 100644
index 000000000..099cfc3d1
--- /dev/null
+++ b/private/nw/rdr/attach.c
@@ -0,0 +1,5169 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ Attach.c
+
+Abstract:
+
+ This module implements the routines for the NetWare
+ redirector to connect and disconnect from a server.
+
+Author:
+
+ Colin Watson [ColinW] 10-Jan-1992
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+#include <stdlib.h> // rand
+
+//
+// The number of bytes in the ipx host address, not
+// including the socket.
+//
+
+#define IPX_HOST_ADDR_LEN 10
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_CREATE)
+
+VOID
+ExtractNextComponentName (
+ OUT PUNICODE_STRING Name,
+ IN PUNICODE_STRING Path,
+ IN BOOLEAN ColonSeparator
+ );
+
+NTSTATUS
+ExtractPathAndFileName(
+ IN PUNICODE_STRING EntryPath,
+ OUT PUNICODE_STRING PathString,
+ OUT PUNICODE_STRING FileName
+ );
+
+NTSTATUS
+DoBinderyLogon(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING UserName,
+ IN PUNICODE_STRING Password
+ );
+
+NTSTATUS
+ConnectToServer(
+ IN PIRP_CONTEXT pIrpContext,
+ OUT PSCB *pScbCollision
+ );
+
+BOOLEAN
+ProcessFindNearestEntry(
+ PIRP_CONTEXT IrpContext,
+ PSAP_FIND_NEAREST_RESPONSE FindNearestResponse
+ );
+
+NTSTATUS
+GetMaxPacketSize(
+ PIRP_CONTEXT pIrpContext,
+ PNONPAGED_SCB pNpScb
+ );
+
+PNONPAGED_SCB
+FindServer(
+ PIRP_CONTEXT pIrpContext,
+ PNONPAGED_SCB pNpScb,
+ PUNICODE_STRING ServerName
+ );
+
+NTSTATUS
+NwAllocateAndInitScb(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING UidServerName OPTIONAL,
+ IN PUNICODE_STRING ServerName OPTIONAL,
+ OUT PSCB *ppScb
+);
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, ExtractNextComponentName )
+#pragma alloc_text( PAGE, ExtractPathAndFileName )
+#pragma alloc_text( PAGE, CrackPath )
+#pragma alloc_text( PAGE, CreateScb )
+#pragma alloc_text( PAGE, FindServer )
+#pragma alloc_text( PAGE, ProcessFindNearestEntry )
+#pragma alloc_text( PAGE, NegotiateBurstMode )
+#pragma alloc_text( PAGE, GetMaxPacketSize )
+#pragma alloc_text( PAGE, NwDeleteScb )
+#pragma alloc_text( PAGE, NwLogoffAndDisconnect )
+#pragma alloc_text( PAGE, InitializeAttach )
+#pragma alloc_text( PAGE, OpenScbSockets )
+#pragma alloc_text( PAGE, DoBinderyLogon )
+#pragma alloc_text( PAGE, QueryServersAddress )
+#pragma alloc_text( PAGE, TreeConnectScb )
+#pragma alloc_text( PAGE, TreeDisconnectScb )
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, ProcessFindNearest )
+#pragma alloc_text( PAGE1, NwLogoffAllServers )
+#pragma alloc_text( PAGE1, DestroyAllScb )
+#pragma alloc_text( PAGE1, SelectConnection )
+#pragma alloc_text( PAGE1, NwFindScb )
+#pragma alloc_text( PAGE1, ConnectToServer )
+#endif
+
+#endif
+
+#if 0 // Not pageable
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+
+VOID
+ExtractNextComponentName (
+ OUT PUNICODE_STRING Name,
+ IN PUNICODE_STRING Path,
+ IN BOOLEAN ColonSeparator
+ )
+
+/*++
+
+Routine Description:
+
+ This routine extracts a the "next" component from a path string.
+
+ It assumes that
+
+Arguments:
+
+ Name - Returns a pointer to the component.
+
+ Path - Supplies a pointer to the backslash seperated pathname.
+
+ ColonSeparator - A colon can be used to terminate this component
+ name.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ register USHORT i; // Index into Name string.
+
+ PAGED_CODE();
+
+ if (Path->Length == 0) {
+ RtlInitUnicodeString(Name, NULL);
+ return;
+ }
+
+ //
+ // Initialize the extracted name to the name passed in skipping the
+ // leading backslash.
+ //
+
+ // DebugTrace(+0, Dbg, "NwExtractNextComponentName = %wZ\n", Path );
+
+ Name->Buffer = Path->Buffer + 1;
+ Name->Length = Path->Length - sizeof(WCHAR);
+ Name->MaximumLength = Path->MaximumLength - sizeof(WCHAR);
+
+ //
+ // Scan forward finding the terminal "\" in the server name.
+ //
+
+ for (i=0;i<(USHORT)(Name->Length/sizeof(WCHAR));i++) {
+
+ if ( Name->Buffer[i] == OBJ_NAME_PATH_SEPARATOR ||
+ ( ColonSeparator && Name->Buffer[i] == L':' ) ) {
+ break;
+ }
+ }
+
+ //
+ // Update the length and maximum length of the structure
+ // to match the new length.
+ //
+
+ Name->Length = Name->MaximumLength = (USHORT)(i*sizeof(WCHAR));
+}
+
+
+NTSTATUS
+ExtractPathAndFileName (
+ IN PUNICODE_STRING EntryPath,
+ OUT PUNICODE_STRING PathString,
+ OUT PUNICODE_STRING FileName
+ )
+/*++
+
+Routine Description:
+
+ This routine cracks the entry path into two pieces, the path and the file
+name component at the start of the name.
+
+
+Arguments:
+
+ IN PUNICODE_STRING EntryPath - Supplies the path to disect.
+ OUT PUNICODE_STRING PathString - Returns the directory containing the file.
+ OUT PUNICODE_STRING FileName - Returns the file name specified.
+
+Return Value:
+
+ NTSTATUS - SUCCESS
+
+
+--*/
+
+{
+ UNICODE_STRING Component;
+ UNICODE_STRING FilePath = *EntryPath;
+
+ PAGED_CODE();
+
+ // Strip trailing separators
+ while ( (FilePath.Length != 0) &&
+ FilePath.Buffer[(FilePath.Length-1)/sizeof(WCHAR)] ==
+ OBJ_NAME_PATH_SEPARATOR ) {
+
+ FilePath.Length -= sizeof(WCHAR);
+ FilePath.MaximumLength -= sizeof(WCHAR);
+ }
+
+ // PathString will become EntryPath minus FileName and trailing separators
+ *PathString = FilePath;
+
+ // Initialize FileName just incase there are no components at all.
+ RtlInitUnicodeString( FileName, NULL );
+
+ //
+ // Scan through the current file name to find the entire path
+ // up to (but not including) the last component in the path.
+ //
+
+ do {
+
+ //
+ // Extract the next component from the name.
+ //
+
+ ExtractNextComponentName(&Component, &FilePath, FALSE);
+
+ //
+ // Bump the "remaining name" pointer by the length of this
+ // component
+ //
+
+ if (Component.Length != 0) {
+
+ FilePath.Length -= Component.Length+sizeof(WCHAR);
+ FilePath.MaximumLength -= Component.MaximumLength+sizeof(WCHAR);
+ FilePath.Buffer += (Component.Length/sizeof(WCHAR))+1;
+
+ *FileName = Component;
+ }
+
+
+ } while (Component.Length != 0);
+
+ //
+ // Take the name, subtract the last component of the name
+ // and concatenate the current path with the new path.
+ //
+
+ if ( FileName->Length != 0 ) {
+
+ //
+ // Set the path's name based on the original name, subtracting
+ // the length of the name portion (including the "\")
+ //
+
+ PathString->Length -= (FileName->Length + sizeof(WCHAR));
+ if ( PathString->Length != 0 ) {
+ PathString->MaximumLength -= (FileName->MaximumLength + sizeof(WCHAR));
+ } else{
+ RtlInitUnicodeString( PathString, NULL );
+ }
+ } else {
+
+ // There was no path or filename
+
+ RtlInitUnicodeString( PathString, NULL );
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+CrackPath (
+ IN PUNICODE_STRING BaseName,
+ OUT PUNICODE_STRING DriveName,
+ OUT PWCHAR DriveLetter,
+ OUT PUNICODE_STRING ServerName,
+ OUT PUNICODE_STRING VolumeName,
+ OUT PUNICODE_STRING PathName,
+ OUT PUNICODE_STRING FileName,
+ OUT PUNICODE_STRING FullName OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This routine extracts the relevant portions from BaseName to extract
+ the components of the user's string.
+
+
+Arguments:
+
+ BaseName - Supplies the base user's path.
+
+ DriveName - Supplies a string to hold the drive specifier.
+
+ DriveLetter - Returns the drive letter. 0 for none, 'A'-'Z' for
+ disk drives, '1'-'9' for LPT connections.
+
+ ServerName - Supplies a string to hold the remote server name.
+
+ VolumeName - Supplies a string to hold the volume name.
+
+ PathName - Supplies a string to hold the remaining part of the path.
+
+ FileName - Supplies a string to hold the final component of the path.
+
+ FullName - Supplies a string to put the Path followed by FileName
+
+Return Value:
+
+ NTSTATUS - Status of operation
+
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ UNICODE_STRING BaseCopy = *BaseName;
+ UNICODE_STRING ShareName;
+
+ PAGED_CODE();
+
+ RtlInitUnicodeString( DriveName, NULL);
+ RtlInitUnicodeString( ServerName, NULL);
+ RtlInitUnicodeString( VolumeName, NULL);
+ RtlInitUnicodeString( PathName, NULL);
+ RtlInitUnicodeString( FileName, NULL);
+ *DriveLetter = 0;
+
+ if (ARGUMENT_PRESENT(FullName)) {
+ RtlInitUnicodeString( FullName, NULL);
+ }
+
+ //
+ // If the name is "\", or empty, there is nothing to do.
+ //
+
+ if ( BaseName->Length <= sizeof( WCHAR ) ) {
+ return STATUS_SUCCESS;
+ }
+
+ ExtractNextComponentName(ServerName, &BaseCopy, FALSE);
+
+ //
+ // Skip over the server name.
+ //
+
+ BaseCopy.Buffer += (ServerName->Length / sizeof(WCHAR)) + 1;
+ BaseCopy.Length -= ServerName->Length + sizeof(WCHAR);
+ BaseCopy.MaximumLength -= ServerName->MaximumLength + sizeof(WCHAR);
+
+ if ((ServerName->Length == sizeof(L"X:") - sizeof(WCHAR) ) &&
+ (ServerName->Buffer[(ServerName->Length / sizeof(WCHAR)) - 1] == L':'))
+ {
+
+ //
+ // The file name is of the form x:\server\volume\foo\bar
+ //
+
+ *DriveName = *ServerName;
+ *DriveLetter = DriveName->Buffer[0];
+
+ RtlInitUnicodeString( ServerName, NULL );
+ ExtractNextComponentName(ServerName, &BaseCopy, FALSE);
+
+ if ( ServerName->Length != 0 ) {
+
+ //
+ // Skip over the server name.
+ //
+
+ BaseCopy.Buffer += (ServerName->Length / sizeof(WCHAR)) + 1;
+ BaseCopy.Length -= ServerName->Length + sizeof(WCHAR);
+ BaseCopy.MaximumLength -= ServerName->MaximumLength + sizeof(WCHAR);
+ }
+ }
+ else if ( ( ServerName->Length == sizeof(L"LPTx") - sizeof(WCHAR) ) &&
+ ( _wcsnicmp( ServerName->Buffer, L"LPT", 3 ) == 0) &&
+ ( ServerName->Buffer[3] >= '0' && ServerName->Buffer[3] <= '9' ) )
+ {
+
+ //
+ // The file name is of the form LPTx\server\printq
+ //
+
+ *DriveName = *ServerName;
+ *DriveLetter = DriveName->Buffer[3];
+
+ RtlInitUnicodeString( ServerName, NULL );
+ ExtractNextComponentName(ServerName, &BaseCopy, FALSE);
+
+ if ( ServerName->Length != 0 ) {
+
+ //
+ // Skip over the server name.
+ //
+
+ BaseCopy.Buffer += (ServerName->Length / sizeof(WCHAR)) + 1;
+ BaseCopy.Length -= ServerName->Length + sizeof(WCHAR);
+ BaseCopy.MaximumLength -= ServerName->MaximumLength + sizeof(WCHAR);
+ }
+ }
+
+ if ( ServerName->Length != 0 ) {
+
+ //
+ // The file name is of the form \\server\volume\foo\bar
+ // Set volume name to server\volume.
+ //
+
+ ExtractNextComponentName( &ShareName, &BaseCopy, TRUE );
+
+ //
+ // Set volume name = \drive:\server\share or \server\share if the
+ // path is UNC.
+ //
+
+ VolumeName->Buffer = ServerName->Buffer - 1;
+
+ if ( ShareName.Length != 0 ) {
+
+ VolumeName->Length = ServerName->Length + ShareName.Length + 2 * sizeof( WCHAR );
+
+ if ( DriveName->Buffer != NULL ) {
+ VolumeName->Buffer = DriveName->Buffer - 1;
+ VolumeName->Length += DriveName->Length + sizeof(WCHAR);
+ }
+
+ BaseCopy.Buffer += ShareName.Length / sizeof(WCHAR) + 1;
+ BaseCopy.Length -= ShareName.Length + sizeof(WCHAR);
+ BaseCopy.MaximumLength -= ShareName.MaximumLength + sizeof(WCHAR);
+
+ } else {
+
+ VolumeName->Length = ServerName->Length + sizeof( WCHAR );
+ return( STATUS_SUCCESS );
+
+ }
+
+ VolumeName->MaximumLength = VolumeName->Length;
+ }
+ else
+ {
+ //
+ // server name is empty. this should only happen if we are
+ // opening the redirector itself. if there is volume or other
+ // components left, fail it.
+ //
+
+ if (BaseCopy.Length > sizeof(WCHAR))
+ {
+ return STATUS_BAD_NETWORK_PATH ;
+ }
+ }
+
+ Status = ExtractPathAndFileName ( &BaseCopy, PathName, FileName );
+
+ if (NT_SUCCESS(Status) &&
+ ARGUMENT_PRESENT(FullName)) {
+
+ //
+ // Use the feature that PathName and FileName are in the same buffer
+ // to return <pathname>\<filename>
+ //
+
+ if ( PathName->Buffer == NULL ) {
+
+ // return just <filename> or NULL
+
+ *FullName = *FileName;
+
+ } else {
+ // Set FullFileName to <PathName>'\'<FileName>
+
+ FullName->Buffer = PathName->Buffer;
+
+ FullName->Length = PathName->Length +
+ FileName->Length +
+ sizeof(WCHAR);
+
+ FullName->MaximumLength = PathName->MaximumLength +
+ FileName->MaximumLength +
+ sizeof(WCHAR);
+ }
+ }
+
+ return( Status );
+}
+
+NTSTATUS
+GetServerByAddress(
+ IN PIRP_CONTEXT pIrpContext,
+ OUT PSCB *Scb,
+ IN IPXaddress *pServerAddress
+)
+/*+++
+
+Description:
+
+ This routine looks up a server by address. If it finds a server that
+ has been connected, it returns it referenced. Otherwise, it returns no
+ server.
+
+---*/
+{
+
+ NTSTATUS Status;
+ PLIST_ENTRY ScbQueueEntry;
+ KIRQL OldIrql;
+ PNONPAGED_SCB pFirstNpScb, pNextNpScb;
+ PNONPAGED_SCB pFoundNpScb = NULL;
+ UNICODE_STRING CredentialName;
+
+ //
+ // Start at the head of the SCB list.
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ if ( ScbQueue.Flink == &ScbQueue ) {
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql);
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ ScbQueueEntry = ScbQueue.Flink;
+ pFirstNpScb = CONTAINING_RECORD( ScbQueueEntry,
+ NONPAGED_SCB,
+ ScbLinks );
+ pNextNpScb = pFirstNpScb;
+
+ //
+ // Leave the first SCB referenced since we need it to
+ // be there for when we walk all the way around the list.
+ //
+
+ NwReferenceScb( pFirstNpScb );
+ NwReferenceScb( pNextNpScb );
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql);
+
+ while ( TRUE ) {
+
+ //
+ // Check to see if the SCB address matches the address we have
+ // and if the user uid matches the uid for this request. Skip
+ // matches that are abandoned anonymous creates.
+ //
+
+ if ( pNextNpScb->pScb ) {
+
+ if ( ( RtlCompareMemory( (BYTE *) pServerAddress,
+ (BYTE *) &pNextNpScb->ServerAddress,
+ IPX_HOST_ADDR_LEN ) == IPX_HOST_ADDR_LEN ) &&
+ ( pIrpContext->Specific.Create.UserUid.QuadPart ==
+ pNextNpScb->pScb->UserUid.QuadPart ) &&
+ ( pNextNpScb->State != SCB_STATE_FLAG_SHUTDOWN ) ) {
+
+ if ( pIrpContext->Specific.Create.fExCredentialCreate ) {
+
+ //
+ // On a credential create, the credential supplied has
+ // to match the extended credential for the server.
+ //
+
+ Status = GetCredentialFromServerName( &pNextNpScb->ServerName,
+ &CredentialName );
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ContinueLoop;
+ }
+
+ if ( RtlCompareUnicodeString( &CredentialName,
+ pIrpContext->Specific.Create.puCredentialName,
+ TRUE ) ) {
+ goto ContinueLoop;
+ }
+
+ }
+
+ pFoundNpScb = pNextNpScb;
+ DebugTrace( 0, Dbg, "GetServerByAddress: %wZ\n", &pFoundNpScb->ServerName );
+ break;
+
+ }
+ }
+
+ContinueLoop:
+
+ //
+ // Otherwise, get the next one in the list. Don't
+ // forget to skip the list head.
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ ScbQueueEntry = pNextNpScb->ScbLinks.Flink;
+
+ if ( ScbQueueEntry == &ScbQueue ) {
+ ScbQueueEntry = ScbQueue.Flink;
+ }
+
+ NwDereferenceScb( pNextNpScb );
+ pNextNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
+
+ if ( pNextNpScb == pFirstNpScb ) {
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+ break;
+ }
+
+ //
+ // Otherwise, reference this SCB and continue.
+ //
+
+ NwReferenceScb( pNextNpScb );
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+ }
+
+ NwDereferenceScb( pFirstNpScb );
+
+ if ( pFoundNpScb ) {
+ *Scb = pFoundNpScb->pScb;
+ return STATUS_SUCCESS;
+ }
+
+ return STATUS_UNSUCCESSFUL;
+
+}
+
+NTSTATUS
+CheckScbSecurity(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PSCB pScb,
+ IN PUNICODE_STRING puUserName,
+ IN PUNICODE_STRING puPassword,
+ IN BOOLEAN fDeferLogon
+)
+/*+++
+
+ You must be at the head of the queue to call this function.
+ This function makes sure that the Scb is valid for the user
+ that requested it.
+
+---*/
+{
+
+ NTSTATUS Status;
+ BOOLEAN SecurityConflict = FALSE;
+
+ ASSERT( pScb->pNpScb->State == SCB_STATE_IN_USE );
+
+ //
+ // If there's no user name or password, there's no conflict.
+ //
+
+ if ( ( puUserName == NULL ) &&
+ ( puPassword == NULL ) ) {
+
+ return STATUS_SUCCESS;
+ }
+
+ if ( pScb->UserName.Length &&
+ pScb->UserName.Buffer ) {
+
+ //
+ // Do a bindery security check if we were bindery
+ // authenticated to this server.
+ //
+
+ if ( !fDeferLogon &&
+ puUserName != NULL &&
+ puUserName->Buffer != NULL ) {
+
+ ASSERT( pScb->Password.Buffer != NULL );
+
+ if ( !RtlEqualUnicodeString( &pScb->UserName, puUserName, TRUE ) ||
+ ( puPassword &&
+ puPassword->Buffer &&
+ puPassword->Length &&
+ !RtlEqualUnicodeString( &pScb->Password, puPassword, TRUE ) )) {
+
+ SecurityConflict = TRUE;
+
+ }
+ }
+
+ } else {
+
+ //
+ // Do an nds security check.
+ //
+
+ Status = NdsCheckCredentials( pIrpContext,
+ puUserName,
+ puPassword );
+
+ if ( !NT_SUCCESS( Status )) {
+
+ SecurityConflict = TRUE;
+ }
+
+ }
+
+ //
+ // If there was a security conflict, see if we can just
+ // take this connection over (i.e. there are no open
+ // files or open handles to the server).
+ //
+
+ if ( SecurityConflict ) {
+
+ if ( ( pScb->OpenFileCount == 0 ) &&
+ ( pScb->IcbCount == 0 ) ) {
+
+ if ( pScb->UserName.Buffer ) {
+ FREE_POOL( pScb->UserName.Buffer );
+ }
+
+ RtlInitUnicodeString( &pScb->UserName, NULL );
+ RtlInitUnicodeString( &pScb->Password, NULL );
+ pScb->pNpScb->State = SCB_STATE_LOGIN_REQUIRED;
+
+ } else {
+
+ DebugTrace( 0, Dbg, "SCB security conflict.\n", 0 );
+ return STATUS_NETWORK_CREDENTIAL_CONFLICT;
+
+ }
+
+ }
+
+ DebugTrace( 0, Dbg, "SCB security check succeeded.\n", 0 );
+ return STATUS_SUCCESS;
+
+}
+
+NTSTATUS
+GetScb(
+ OUT PSCB *Scb,
+ IN PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING Server,
+ IN IPXaddress *pServerAddress,
+ IN PUNICODE_STRING UserName,
+ IN PUNICODE_STRING Password,
+ IN BOOLEAN DeferLogon,
+ OUT PBOOLEAN Existing
+)
+/*+++
+
+Description:
+
+ This routine locates an existing SCB or creates a new SCB.
+ This is the first half of the original CreateScb routine.
+
+Locks:
+
+ See the anonymous create information in CreateScb().
+
+---*/
+{
+
+ NTSTATUS Status;
+ PSCB pScb = NULL;
+ PNONPAGED_SCB pNpScb = NULL;
+ BOOLEAN ExistingScb = TRUE;
+ UNICODE_STRING UidServer;
+ UNICODE_STRING ExCredName;
+ PUNICODE_STRING puConnectName;
+ KIRQL OldIrql;
+
+ DebugTrace( 0, Dbg, "GetScb... %wZ\n", Server );
+
+ if ( pServerAddress != NULL ) {
+ DebugTrace( 0, Dbg, " ->Server Address = (provided)\n", 0 );
+ } else {
+ DebugTrace( 0, Dbg, " ->Server Address = NULL\n", 0 );
+ }
+
+ RtlInitUnicodeString( &UidServer, NULL );
+
+ if ( ( Server == NULL ) ||
+ ( Server->Length == 0 ) ) {
+
+ //
+ // No server name was provided. Either this is a connect by address,
+ // or a connect to a nearby bindery server (defaulting to the preferred
+ // server).
+ //
+
+ if ( pServerAddress == NULL ) {
+
+ //
+ // No server address was provided, so this is an attempt to open
+ // a nearby bindery server.
+ //
+
+ while (TRUE) {
+
+ //
+ // The loop checks that after we get to the front, the SCB
+ // is still in the state we wanted. If not, we need to
+ // reselect another.
+ //
+
+ pNpScb = SelectConnection( NULL );
+
+ //
+ // Note: We'd like to call SelectConnection with the pNpScb
+ // that we last tried, but if the scavenger runs before
+ // this loop gets back to the select connection, we could
+ // pass a bum pointer to SelectConnection, which is bad.
+ //
+
+ if ( pNpScb != NULL) {
+
+ pScb = pNpScb->pScb;
+
+ //
+ // Queue ourselves to the SCB, wait to get to the front to
+ // protect access to server State.
+ //
+
+ pIrpContext->pNpScb = pNpScb;
+ pIrpContext->pScb = pScb;
+
+ NwAppendToQueueAndWait( pIrpContext );
+
+ //
+ // These states have to match the conditions of the
+ // SelectConnection to prevent an infinite loop.
+ //
+
+ if (!((pNpScb->State == SCB_STATE_RECONNECT_REQUIRED ) ||
+ (pNpScb->State == SCB_STATE_LOGIN_REQUIRED ) ||
+ (pNpScb->State == SCB_STATE_IN_USE ))) {
+
+ //
+ // No good any more as default server, select another.
+ //
+
+ pScb = NULL ;
+ NwDereferenceScb( pNpScb );
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ continue ;
+
+ }
+ }
+
+ //
+ // otherwise, we're done
+ //
+
+ break ;
+
+ }
+
+ } else {
+
+ //
+ // An address was provided, so we are attempting to do a lookup
+ // based on address. The server that we are looking for might
+ // exist but not yet have its address recorded, so if we do an
+ // anonymous create, we have to check at the end whether or not
+ // someone else came in and successfully created while we were
+ // looking up the name.
+ //
+ // We don't have to hold the RCB anymore since colliding creates
+ // have to be handled gracefully anyway.
+ //
+
+ Status = GetServerByAddress( pIrpContext, &pScb, pServerAddress );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ //
+ // No anonymous creates are allowed if we are not allowed
+ // to send packets to the net (because it's not possible for
+ // us to resolve the address to a name).
+ //
+
+ if ( BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_NOCONNECT ) ) {
+ return STATUS_BAD_NETWORK_PATH;
+ }
+
+ //
+ // There's no connection to this server, so we'll
+ // have to create one. Let's start with an anonymous
+ // Scb.
+ //
+
+ Status = NwAllocateAndInitScb( pIrpContext,
+ NULL,
+ NULL,
+ &pScb );
+
+ if ( !NT_SUCCESS( Status )) {
+ return Status;
+ }
+
+ //
+ // We've made the anonymous create, so put it on the scb
+ // list and get to the head of the queue.
+ //
+
+ SetFlag( pIrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE );
+
+ pIrpContext->pScb = pScb;
+ pIrpContext->pNpScb = pScb->pNpScb;
+
+ ExInterlockedInsertHeadList( &pScb->pNpScb->Requests,
+ &pIrpContext->NextRequest,
+ &pScb->pNpScb->NpScbSpinLock );
+
+ KeAcquireSpinLock(&ScbSpinLock, &OldIrql);
+ InsertTailList(&ScbQueue, &pScb->pNpScb->ScbLinks);
+ KeReleaseSpinLock(&ScbSpinLock, OldIrql);
+
+ DebugTrace( 0, Dbg, "GetScb started an anonymous create.\n", 0 );
+ ExistingScb = FALSE;
+
+ } else {
+
+ //
+ // Get to the head of the queue and see if this was
+ // an abandoned anonymous create. If so, get the
+ // right server and continue.
+ //
+
+ pIrpContext->pScb = pScb;
+ pIrpContext->pNpScb = pScb->pNpScb;
+ NwAppendToQueueAndWait( pIrpContext );
+
+ if ( pScb->pNpScb->State == SCB_STATE_FLAG_SHUTDOWN ) {
+
+ //
+ // The create abandoned this scb, redoing a
+ // GetServerByAddress() is guaranteed to get
+ // us a good server if there is a server out
+ // there.
+ //
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ NwDereferenceScb( pScb->pNpScb );
+
+ Status = GetServerByAddress( pIrpContext, &pScb, pServerAddress );
+
+ if ( NT_SUCCESS( Status ) ) {
+ ASSERT( pScb != NULL );
+ ASSERT( !IS_ANONYMOUS_SCB( pScb ) );
+ }
+
+ } else {
+
+ ASSERT( !IS_ANONYMOUS_SCB( pScb ) );
+ }
+ }
+
+ ASSERT( pScb != NULL );
+ }
+
+ } else {
+
+ //
+ // A server name was provided, so we are doing a straight
+ // lookup or create by name. Do we need to munge the name
+ // for a supplemental credential connect?
+ //
+
+ RtlInitUnicodeString( &ExCredName, NULL );
+
+ if ( ( pIrpContext->Specific.Create.fExCredentialCreate ) &&
+ ( !IsCredentialName( Server ) ) ) {
+
+ Status = BuildExCredentialServerName( Server,
+ pIrpContext->Specific.Create.puCredentialName,
+ &ExCredName );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ puConnectName = &ExCredName;
+
+ } else {
+
+ puConnectName = Server;
+ }
+
+ Status = MakeUidServer( &UidServer,
+ &pIrpContext->Specific.Create.UserUid,
+ puConnectName );
+
+
+ if ( ExCredName.Buffer ) {
+ FREE_POOL( ExCredName.Buffer );
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+ DebugTrace( 0, Dbg, " ->UidServer = %wZ\n", &UidServer );
+
+ ExistingScb = NwFindScb( &pScb, pIrpContext, &UidServer, Server );
+
+ ASSERT( pScb != NULL );
+ pNpScb = pScb->pNpScb;
+
+ pIrpContext->pNpScb = pNpScb;
+ pIrpContext->pScb = pScb;
+ NwAppendToQueueAndWait(pIrpContext);
+
+ if ( ExistingScb ) {
+
+ //
+ // We found an existing SCB. If we are logged into this
+ // server make sure the supplied username and password, match
+ // the username and password that we logged in with.
+ //
+
+ if ( pNpScb->State == SCB_STATE_IN_USE ) {
+
+ Status = CheckScbSecurity( pIrpContext,
+ pScb,
+ UserName,
+ Password,
+ DeferLogon );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ FREE_POOL( UidServer.Buffer );
+ UidServer.Buffer = NULL;
+ NwDereferenceScb( pNpScb );
+ return Status;
+ }
+ }
+ }
+
+ }
+
+ //
+ // 1) We may or may not have a server (evidenced by pScb).
+ //
+ // 2) If we have a server and ExistingScb is TRUE, we have
+ // an existing server, possibly already connected.
+ // Otherwise, we have a newly created server that
+ // may or may not be anonymous.
+ //
+
+ *Scb = pScb;
+ *Existing = ExistingScb;
+
+#ifdef NWDBG
+
+ if ( pScb != NULL ) {
+
+ //
+ // If we have a server, the SCB is referenced and we will
+ // be at the head of the queue.
+ //
+
+ ASSERT( pIrpContext->pNpScb->Requests.Flink == &pIrpContext->NextRequest );
+
+ }
+
+#endif
+
+ if ( UidServer.Buffer != NULL ) {
+ FREE_POOL( UidServer.Buffer );
+ }
+
+ DebugTrace( 0, Dbg, "GetScb returned %08lx\n", pScb );
+ return STATUS_SUCCESS;
+
+}
+
+NTSTATUS
+ConnectScb(
+ IN PSCB *Scb,
+ IN PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING Server,
+ IN IPXaddress *pServerAddress,
+ IN PUNICODE_STRING UserName,
+ IN PUNICODE_STRING Password,
+ IN BOOLEAN DeferLogon,
+ IN BOOLEAN DeleteConnection,
+ IN BOOLEAN ExistingScb
+)
+/*+++
+
+Description:
+
+ This routine puts the provided scb in the connected state.
+ This is the second half of the original CreateScb routine.
+
+Arguments:
+
+ Scb - The scb for the server we want to connect.
+ pIrpContext - The context for this request.
+ Server - The name of the server, or NULL.
+ pServerAddress - The address of the server, or NULL,
+ UserName - The name of the user to connect as, or NULL.
+ Password - The password for the user, or NULL.
+ DeferLogon - Should we defer the logon?
+ DeleteConnection - Should we succeed even without the net so that
+ the delete request will succeed?
+ ExistingScb - Is this an existing SCB?
+
+ If the SCB is anonymous, we need to safely check for colliding
+ creates when we find out who the server is.
+
+ If this is a reconnect attempt, this routine will not dequeue the
+ irp context, which could cause a deadlock in the reconnect logic.
+
+---*/
+{
+
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ PSCB pScb = *Scb;
+ PNONPAGED_SCB pNpScb = NULL;
+
+ BOOLEAN AnonymousScb = FALSE;
+ PSCB pCollisionScb = NULL;
+
+ NTSTATUS LoginStatus;
+ BOOLEAN TriedNdsLogin;
+
+ PLOGON pLogon;
+ BOOLEAN DeferredLogon = DeferLogon;
+ PNDS_SECURITY_CONTEXT pNdsContext;
+ NTSTATUS CredStatus;
+
+ DebugTrace( 0, Dbg, "ConnectScb... %08lx\n", pScb );
+
+ //
+ // If we already have an SCB, find out where in the
+ // connect chain we need to start off.
+ //
+
+ if ( pScb ) {
+
+ pNpScb = pScb->pNpScb;
+ AnonymousScb = IS_ANONYMOUS_SCB( pScb );
+
+ if ( ExistingScb ) {
+
+ ASSERT( !AnonymousScb );
+
+ //
+ // If this SCB is in STATE_ATTACHING, we need to check
+ // the address in the SCB to make sure that it was at one
+ // point a valid server. If it wasn't, then we shouldn't
+ // honor this create because it's probably a tree create.
+ //
+
+ if ( DeleteConnection ) {
+
+ ASSERT( !BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT ) );
+
+ if ( ( pNpScb->State == SCB_STATE_ATTACHING ) &&
+ ( (pNpScb->ServerAddress).Socket == 0 ) ) {
+
+ Status = STATUS_BAD_NETWORK_PATH;
+ goto CleanupAndExit;
+
+ } else {
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ return STATUS_SUCCESS;
+ }
+ }
+
+RedoConnect:
+
+ if ( pNpScb->State == SCB_STATE_ATTACHING ) {
+ goto GetAddress;
+ } else if ( pNpScb->State == SCB_STATE_RECONNECT_REQUIRED ) {
+ goto Connect;
+ } else if ( pNpScb->State == SCB_STATE_LOGIN_REQUIRED ) {
+ goto Login;
+ } else if ( pNpScb->State == SCB_STATE_IN_USE ) {
+ goto InUse;
+ } else {
+
+ DebugTrace( 0, Dbg, "ConnectScb: Unknown Scb State %08lx\n", pNpScb->State );
+ Status = STATUS_INVALID_PARAMETER;
+ goto CleanupAndExit;
+ }
+
+ } else {
+
+ //
+ // This is a new SCB, we have to run through the whole routine.
+ //
+
+ pNpScb->State = SCB_STATE_ATTACHING;
+ }
+
+ }
+
+GetAddress:
+
+ //
+ // Set the reroute attempted bit so that we don't try
+ // to reconnect during the connect.
+ //
+
+ SetFlag( pIrpContext->Flags, IRP_FLAG_REROUTE_ATTEMPTED );
+
+ if ( !pServerAddress ) {
+
+ //
+ // If we don't have an address, this SCB cannot be anonymous!!
+ //
+
+ ASSERT( !AnonymousScb );
+
+ //
+ // We have to cast an exception frame for this legacy routine
+ // that still uses structured exceptions.
+ //
+
+ try {
+
+ pNpScb = FindServer( pIrpContext, pNpScb, Server );
+
+ ASSERT( pNpScb != NULL );
+
+ //
+ // This is redundant unless the starting server was NULL.
+ // FindServer returns the same SCB we provided to it
+ // unless we called it with NULL.
+ //
+
+ pScb = pNpScb->pScb;
+ pIrpContext->pNpScb = pNpScb;
+ pIrpContext->pScb = pScb;
+ NwAppendToQueueAndWait( pIrpContext );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = GetExceptionCode();
+ goto CleanupAndExit;
+ }
+
+ } else {
+
+ //
+ // Build the address into the NpScb since we already know it.
+ //
+
+ RtlCopyMemory( &pNpScb->ServerAddress,
+ pServerAddress,
+ sizeof( TDI_ADDRESS_IPX ) );
+
+ BuildIpxAddress( pNpScb->ServerAddress.Net,
+ pNpScb->ServerAddress.Node,
+ NCP_SOCKET,
+ &pNpScb->RemoteAddress );
+
+ pNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
+
+ }
+
+Connect:
+
+ //
+ // FindServer may have connected us to the server already,
+ // so we may be able to skip the reconnect here.
+ //
+
+ if ( pNpScb->State == SCB_STATE_RECONNECT_REQUIRED ) {
+
+ //
+ // If this is an anonymous scb, we have to be prepared
+ // for ConnectToServer() to find that we've already connected
+ // this server by name. In this case, we cancel the
+ // anonymous create and use the server that was created
+ // while we were looking up the name.
+ //
+
+ Status = ConnectToServer( pIrpContext, &pCollisionScb );
+
+ if (!NT_SUCCESS(Status)) {
+ goto CleanupAndExit;
+ }
+
+ //
+ // We succeeded. If there's a collision scb, then we need to
+ // abandon the anonymous scb and use the scb that we collided
+ // with. Otherwise, we successfully completed an anonymous
+ // connect and can go on with the create normally.
+ //
+
+ if ( pCollisionScb ) {
+
+ ASSERT( AnonymousScb );
+
+ //
+ // Deref and dequeue from the abandoned server.
+ //
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ NwDereferenceScb( pIrpContext->pNpScb );
+
+ //
+ // Queue to the appropriate server.
+ //
+
+ pIrpContext->pScb = pCollisionScb;
+ pIrpContext->pNpScb = pCollisionScb->pNpScb;
+ NwAppendToQueueAndWait( pIrpContext );
+
+ pScb = pCollisionScb;
+ pNpScb = pCollisionScb->pNpScb;
+ *Scb = pCollisionScb;
+
+ //
+ // Re-start connecting the scb.
+ //
+
+ AnonymousScb = FALSE;
+ ExistingScb = TRUE;
+
+ pCollisionScb = NULL;
+
+ DebugTrace( 0, Dbg, "Re-doing connect on anonymous collision.\n", 0 );
+ goto RedoConnect;
+
+ }
+
+ DebugTrace( +0, Dbg, " Logout from server - just in case\n", 0);
+
+ Status = ExchangeWithWait (
+ pIrpContext,
+ SynchronousResponseCallback,
+ "F",
+ NCP_LOGOUT );
+
+ DebugTrace( +0, Dbg, " %X\n", Status);
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto CleanupAndExit;
+ }
+
+ DebugTrace( +0, Dbg, " Connect to real server = %X\n", Status);
+
+ pNpScb->State = SCB_STATE_LOGIN_REQUIRED;
+ }
+
+Login:
+
+ //
+ // If we have credentials for the tree and this server was named
+ // explicitly, we shouldn't defer the login or else the browse
+ // view of the tree may be wrong. For this reason, NdsServerAuthenticate
+ // has to be a straight shot call and can't remove us from the head
+ // of the queue.
+ //
+
+ if ( ( ( Server != NULL ) || ( pServerAddress != NULL ) ) &&
+ ( DeferredLogon ) &&
+ ( pScb->MajorVersion > 3 ) &&
+ ( pScb->UserName.Length == 0 ) ) {
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUser( &pScb->UserUid, FALSE );
+ NwReleaseRcb( &NwRcb );
+
+ if ( pLogon ) {
+
+ CredStatus = NdsLookupCredentials( &pScb->NdsTreeName,
+ pLogon,
+ &pNdsContext,
+ CREDENTIAL_READ,
+ FALSE );
+
+ if ( NT_SUCCESS( CredStatus ) ) {
+
+ if ( ( pNdsContext->Credential != NULL ) &&
+ ( pNdsContext->CredentialLocked == FALSE ) ) {
+
+ DebugTrace( 0, Dbg, "Forcing authentication to %wZ.\n",
+ &pScb->UidServerName );
+ DeferredLogon = FALSE;
+ }
+
+ NwReleaseCredList( pLogon );
+ }
+ }
+ }
+
+ if (pNpScb->State == SCB_STATE_LOGIN_REQUIRED && !DeferredLogon ) {
+
+ //
+ // NOTE: DoBinderyLogon() and DoNdsLogon() may return a
+ // warning status. If they do, we must return the
+ // warning status to the caller.
+ //
+
+ Status = STATUS_UNSUCCESSFUL;
+ TriedNdsLogin = FALSE;
+
+ //
+ // We force a bindery login for a non 4.x server. Otherwise, we
+ // allow a fall-back from NDS style authentication to bindery style
+ // authentication.
+ //
+
+ if ( pScb->MajorVersion >= 4 ) {
+
+ ASSERT( pScb->NdsTreeName.Length != 0 );
+
+ Status = DoNdsLogon( pIrpContext, UserName, Password );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // Do we need to re-license the connection?
+ //
+
+ if ( ( pScb->VcbCount > 0 ) || ( pScb->OpenNdsStreams > 0 ) ) {
+
+ Status = NdsLicenseConnection( pIrpContext );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ Status = STATUS_REMOTE_SESSION_LIMIT;
+ }
+ }
+
+ }
+
+ TriedNdsLogin = TRUE;
+ LoginStatus = Status;
+
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ Status = DoBinderyLogon( pIrpContext, UserName, Password );
+
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ if ( TriedNdsLogin ) {
+
+ //
+ // Both login attempts have failed. We usually prefer
+ // the NDS status, but not always.
+ //
+
+ if ( ( Status != STATUS_WRONG_PASSWORD ) &&
+ ( Status != STATUS_ACCOUNT_DISABLED ) ) {
+ Status = LoginStatus;
+ }
+ }
+
+ //
+ // Couldn't log on, be good boys and disconnect.
+ //
+
+ ExchangeWithWait (
+ pIrpContext,
+ SynchronousResponseCallback,
+ "D-" ); // Disconnect
+
+ Stats.Sessions--;
+
+ if ( pScb->MajorVersion == 2 ) {
+ Stats.NW2xConnects--;
+ } else if ( pScb->MajorVersion == 3 ) {
+ Stats.NW3xConnects--;
+ } else if ( pScb->MajorVersion == 4 ) {
+ Stats.NW4xConnects--;
+ }
+
+ //
+ // Demote this scb to reconnect required and exit.
+ //
+
+ pNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
+ goto CleanupAndExit;
+ }
+
+ pNpScb->State = SCB_STATE_IN_USE;
+ }
+
+ //
+ // We have to be at the head of the queue to do the reconnect.
+ //
+
+ if ( BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT ) ) {
+ ASSERT( pIrpContext->pNpScb->Requests.Flink == &pIrpContext->NextRequest );
+ } else {
+ NwAppendToQueueAndWait( pIrpContext );
+ }
+
+ ReconnectScb( pIrpContext, pScb );
+
+InUse:
+
+ //
+ // Ok, we've completed the connect routine. Return this good server.
+ //
+
+ *Scb = pScb;
+
+CleanupAndExit:
+
+ //
+ // The reconnect path must not do anything to remove the irp context from
+ // the head of the queue since it also owns the irp context in the second
+ // position on the queue and that irp context is running.
+ //
+
+ if ( !BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT ) ) {
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ }
+
+ DebugTrace( 0, Dbg, "ConnectScb: Connected %08lx\n", pScb );
+ DebugTrace( 0, Dbg, "ConnectScb: Status was %08lx\n", Status );
+ return Status;
+
+}
+
+NTSTATUS
+CreateScb(
+ OUT PSCB *Scb,
+ IN PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING Server,
+ IN IPXaddress *pServerAddress,
+ IN PUNICODE_STRING UserName,
+ IN PUNICODE_STRING Password,
+ IN BOOLEAN DeferLogon,
+ IN BOOLEAN DeleteConnection
+)
+/*++
+
+Routine Description:
+
+ This routine connects to the requested server.
+
+ The following mix of parameters are valid:
+
+ Server Name, No Net Address - The routine will look up
+ up the SCB or create a new one if necessary, getting
+ the server address from a nearby bindery.
+
+ No Server Name, Valid Net Address - The routine will
+ look up the SCB by address or create a new one if
+ necessary. The name of the server will be set in
+ the SCB upon return.
+
+ Server Name, Valid Net Address - The routine will look
+ up the SCB by name or will create a new one if
+ necessary. The supplied server address will be used,
+ sparing a bindery query.
+
+ No Server Name, No Net Address - A connection to the
+ preferred server or a nearby server will be returned.
+
+Arguments:
+
+ Scb - The pointer to the scb in question.
+ pIrpContext - The information for this request.
+ Server - The name of the server, or NULL.
+ pServerAddress - The address of the server, or NULL.
+ UserName - The username for the connect, or NULL.
+ Password - The password for the connect, or NULL.
+ DeferLogon - Should we defer the logon until later?
+ DeleteConnection - Should we allow this even when there's no
+ net response so that the connection can
+ be deleted?
+
+Return Value:
+
+ NTSTATUS - Status of operation. If the return status is STATUS_SUCCESS,
+ then Scb must point to a valid Scb. The irp context pointers will also
+ be set, but the irp context will not be on the scb queue.
+
+--*/
+{
+
+ NTSTATUS Status = STATUS_UNSUCCESSFUL;
+ PSCB pScb = NULL;
+ PNONPAGED_SCB pOriginalNpScb = pIrpContext->pNpScb;
+ PSCB pOriginalScb = pIrpContext->pScb;
+ BOOLEAN ExistingScb = FALSE;
+ BOOLEAN AnonymousScb = FALSE;
+ PLOGON pLogon;
+ PNDS_SECURITY_CONTEXT pNdsContext;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "CreateScb....\n", 0);
+
+ //
+ // Do not allow any SCB opens unless the redirector is running
+ // unless they are no connect creates and we are waiting to bind.
+ //
+
+ if ( NwRcb.State != RCB_STATE_RUNNING ) {
+
+ if ( ( !BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_NOCONNECT ) ||
+ ( NwRcb.State != RCB_STATE_NEED_BIND ) ) ) {
+
+ *Scb = NULL;
+ DebugTrace( -1, Dbg, "CreateScb -> %08lx\n", STATUS_REDIRECTOR_NOT_STARTED );
+ return STATUS_REDIRECTOR_NOT_STARTED;
+ }
+ }
+
+ if ( UserName != NULL ) {
+ DebugTrace( 0, Dbg, " ->UserName = %wZ\n", UserName );
+ } else {
+ DebugTrace( 0, Dbg, " ->UserName = NULL\n", 0 );
+ }
+
+ if ( Password != NULL ) {
+ DebugTrace( 0, Dbg, " ->Password = %wZ\n", Password );
+ } else {
+ DebugTrace( 0, Dbg, " ->Password = NULL\n", 0 );
+ }
+
+ //
+ // Get the SCB for this server.
+ //
+
+ Status = GetScb( &pScb,
+ pIrpContext,
+ Server,
+ pServerAddress,
+ UserName,
+ Password,
+ DeferLogon,
+ &ExistingScb );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ //
+ // At this point, we may or may not have an SCB.
+ //
+ // If we have an SCB, we know:
+ //
+ // 1. The scb is referenced.
+ // 2. We are at the head of the queue.
+ //
+ // IMPORTANT POINT: The SCB may be anonymous. If it is,
+ // we do not hold the RCB, but rather we have to re-check
+ // whether or not the server has shown up via a different
+ // create when we find out who the anonymous server is.
+ // We do this because there is a window where we have a
+ // servers name but not its address and so our lookup by
+ // address might be inaccurate.
+ //
+
+ if ( ( pScb ) && IS_ANONYMOUS_SCB( pScb ) ) {
+ AnonymousScb = TRUE;
+ }
+
+ //
+ // If we have a fully connected SCB, we need to go no further.
+ //
+
+ if ( ( pScb ) && ( pScb->pNpScb->State == SCB_STATE_IN_USE ) ) {
+
+ ASSERT( !AnonymousScb );
+
+ if ( ( pScb->MajorVersion >= 4 ) &&
+ ( pScb->UserName.Buffer == NULL ) ) {
+
+ //
+ // This is an NDS authenticated server and we have
+ // to make sure the credentials aren't locked for
+ // logout.
+ //
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUser( &pScb->UserUid, FALSE );
+ NwReleaseRcb( &NwRcb );
+
+ if ( pLogon ) {
+
+ Status = NdsLookupCredentials( &pScb->NdsTreeName,
+ pLogon,
+ &pNdsContext,
+ CREDENTIAL_READ,
+ FALSE );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ if ( ( pNdsContext->Credential != NULL ) &&
+ ( pNdsContext->CredentialLocked == TRUE ) ) {
+
+ DebugTrace( 0, Dbg, "Denying create... we're logging out.\n", 0 );
+ Status = STATUS_DEVICE_BUSY;
+ }
+
+ NwReleaseCredList( pLogon );
+ }
+ }
+ }
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ //
+ // We must not change the irp context pointers if we're going
+ // to fail this call or we may screw up ref counts and what not.
+ //
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ *Scb = pScb;
+
+ } else {
+
+ *Scb = NULL;
+ NwDereferenceScb( pScb->pNpScb );
+
+ pIrpContext->pNpScb = pOriginalNpScb;
+ pIrpContext->pScb = pOriginalScb;
+
+ }
+
+
+ DebugTrace( -1, Dbg, "CreateScb: pScb = %08lx\n", pScb );
+ return Status;
+ }
+
+ //
+ // Run through the connect routines for this scb. The scb may
+ // be NULL if we're still looking for a nearby server.
+ //
+
+ Status = ConnectScb( &pScb,
+ pIrpContext,
+ Server,
+ pServerAddress,
+ UserName,
+ Password,
+ DeferLogon,
+ DeleteConnection,
+ ExistingScb );
+
+ //
+ // If ConnectScb fails, remove the extra ref count so
+ // the scavenger will clean it up. Anonymous failures
+ // are also cleaned up by the scavenger.
+ //
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ if ( pScb ) {
+ NwDereferenceScb( pScb->pNpScb );
+ }
+
+ //
+ // We must not change the irp context pointers if we're going
+ // to fail this call or we may screw up ref counts and what not.
+ //
+
+ pIrpContext->pNpScb = pOriginalNpScb;
+ pIrpContext->pScb = pOriginalScb;
+ *Scb = NULL;
+
+ DebugTrace( -1, Dbg, "CreateScb: Status = %08lx\n", Status );
+ return Status;
+ }
+
+ //
+ // If ConnectScb succeeds, then we must have an scb, the scb must
+ // be in the IN_USE state (or LOGIN_REQUIRED if DeferLogon was
+ // specified), it must be referenced, and we should not be on the
+ // queue.
+ //
+
+ ASSERT( pScb );
+ ASSERT( !BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE ) );
+ ASSERT( pIrpContext->pNpScb == pScb->pNpScb );
+ ASSERT( pIrpContext->pScb == pScb );
+ ASSERT( pScb->pNpScb->Reference > 0 );
+
+ *Scb = pScb;
+ DebugTrace(-1, Dbg, "CreateScb -> pScb = %08lx\n", pScb );
+ ASSERT( NT_SUCCESS( Status ) );
+
+ return Status;
+}
+
+PNONPAGED_SCB
+FindServer(
+ PIRP_CONTEXT pIrpContext,
+ PNONPAGED_SCB pNpScb,
+ PUNICODE_STRING ServerName
+ )
+/*++
+
+Routine Description:
+
+ This routine attempts to get the network address of a server. If no
+ servers are known, it first sends a find nearest SAP.
+
+Arguments:
+
+ pIrpContext - A pointer to the request parameters.
+
+ pNpScb - A pointer to the non paged SCB for the server to get the
+ address of.
+
+Return Value:
+
+ NONPAGED_SCB - A pointer the nonpaged SCB. This is the same as the
+ input value, unless the input SCB was NULL. Then this is a
+ pointer to the nearest server SCB.
+
+ This routine raises status if it fails to get the server's address.
+
+--*/
+{
+ NTSTATUS Status;
+ ULONG Attempts;
+ BOOLEAN FoundServer = FALSE;
+ PNONPAGED_SCB pNearestNpScb = NULL;
+ PNONPAGED_SCB pLastNpScb = NULL;
+
+ BOOLEAN SentFindNearest = FALSE;
+ BOOLEAN SentGeneral = FALSE;
+ PMDL ReceiveMdl = NULL;
+ PUCHAR ReceiveBuffer = NULL;
+ IPXaddress ServerAddress;
+
+ BOOLEAN ConnectedToNearest = FALSE;
+ BOOLEAN AllocatedIrpContext = FALSE;
+ PIRP_CONTEXT pNewIrpContext;
+ int ResponseCount;
+ int NewServers;
+
+ static LARGE_INTEGER TimeoutWait = {0,0};
+ LARGE_INTEGER Now;
+
+ PAGED_CODE();
+
+ //
+ // If we had a SAP timeout less than 10 seconds ago, just fail this
+ // request immediately. This allows dumb apps to exit a lot faster.
+ //
+
+ KeQuerySystemTime( &Now );
+ if ( Now.QuadPart < TimeoutWait.QuadPart ) {
+ ExRaiseStatus( STATUS_BAD_NETWORK_PATH );
+ }
+
+ try {
+ for ( Attempts = 0; Attempts < MAX_SAP_RETRIES && !FoundServer ; Attempts++ ) {
+
+ //
+ // If this SCB is now marked RECONNECT_REQUIRED, then
+ // it responded to the find nearest and we can immediately
+ // try to connect to it.
+ //
+
+ if ( pNpScb != NULL &&
+ pNpScb->State == SCB_STATE_RECONNECT_REQUIRED ) {
+
+ return pNpScb;
+ }
+
+ //
+ // Pick a server to use to find the address of the server that
+ // we are really interested in.
+ //
+
+ if (pLastNpScb) {
+
+ //
+ // For some reason we couldn't use pNearestScb. Scan from this
+ // server onwards.
+ //
+
+ pNearestNpScb = SelectConnection( pLastNpScb );
+
+ // Allow pLastNpScb to be deleted.
+
+ NwDereferenceScb( pLastNpScb );
+
+ pLastNpScb = NULL;
+
+ } else {
+
+ pNearestNpScb = SelectConnection( NULL );
+
+ }
+
+ if ( pNearestNpScb == NULL ) {
+
+ int i;
+
+ //
+ // If we sent a find nearest, and still don't have a single
+ // entry in the server list, it's time to give up.
+ //
+
+ if (( SentFindNearest) &&
+ ( SentGeneral )) {
+
+ Error(
+ EVENT_NWRDR_NO_SERVER_ON_NETWORK,
+ STATUS_OBJECT_NAME_NOT_FOUND,
+ NULL,
+ 0,
+ 0 );
+
+ ExRaiseStatus( STATUS_BAD_NETWORK_PATH );
+ return NULL;
+ }
+
+ //
+ // We don't have any active servers in the list. Queue our
+ // IrpContext to the NwPermanentNpScb. This insures that
+ // only one thread in the system in doing a find nearest at
+ // any one time.
+ //
+
+ DebugTrace( +0, Dbg, " Nearest Server\n", 0);
+
+ if ( !AllocatedIrpContext ) {
+ AllocatedIrpContext = NwAllocateExtraIrpContext(
+ &pNewIrpContext,
+ &NwPermanentNpScb );
+
+ if ( !AllocatedIrpContext ) {
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+ }
+
+ pNewIrpContext->pNpScb = &NwPermanentNpScb;
+
+ //
+ // Allocate an extra buffer large enough for 4
+ // find nearest responses, or a general SAP response.
+ //
+
+ pNewIrpContext->Specific.Create.FindNearestResponseCount = 0;
+ NewServers = 0;
+
+
+ ReceiveBuffer = ALLOCATE_POOL_EX(
+ NonPagedPool,
+ MAX_SAP_RESPONSE_SIZE );
+
+ pNewIrpContext->Specific.Create.FindNearestResponse[0] = ReceiveBuffer;
+
+ for ( i = 1; i < MAX_SAP_RESPONSES ; i++ ) {
+ pNewIrpContext->Specific.Create.FindNearestResponse[i] =
+ ReceiveBuffer + i * SAP_RECORD_SIZE;
+ }
+
+ //
+ // Get the tick count for this net, so that we know how
+ // long to wait for SAP responses.
+ //
+
+ (VOID)GetTickCount( pNewIrpContext, &NwPermanentNpScb.TickCount );
+ NwPermanentNpScb.SendTimeout = NwPermanentNpScb.TickCount + 10;
+
+ if (!SentFindNearest) {
+
+ //
+ // Send a find nearest SAP, and wait for up to several
+ // responses. This allows us to handle a busy server
+ // that responds quickly to SAPs but will not accept
+ // connections.
+ //
+
+ Status = ExchangeWithWait (
+ pNewIrpContext,
+ ProcessFindNearest,
+ "Aww",
+ SAP_FIND_NEAREST,
+ SAP_SERVICE_TYPE_SERVER );
+
+ if ( Status == STATUS_NETWORK_UNREACHABLE ) {
+
+ //
+ // IPX is not bound to anything that is currently
+ // up (which means it's probably bound only to the
+ // RAS WAN wrapper). Don't waste 20 seconds trying
+ // to find a server.
+ //
+
+ DebugTrace( 0, Dbg, "Aborting FindNearest. No Net.\n", 0 );
+ NwDequeueIrpContext( pNewIrpContext, FALSE );
+ ExRaiseStatus( STATUS_NETWORK_UNREACHABLE );
+ }
+
+ //
+ // Process the set of find nearest responses.
+ //
+
+ for (i = 0; i < (int)pNewIrpContext->Specific.Create.FindNearestResponseCount; i++ ) {
+ if (ProcessFindNearestEntry(
+ pNewIrpContext,
+ (PSAP_FIND_NEAREST_RESPONSE)pNewIrpContext->Specific.Create.FindNearestResponse[i] )
+ ) {
+
+ //
+ // We found a server that was previously unknown.
+ //
+
+ NewServers++;
+ }
+ }
+ }
+
+ if (( !NewServers ) &&
+ ( !SentGeneral)){
+
+ SentGeneral = TRUE;
+
+ //
+ // Either no SAP responses or can't connect to nearest servers.
+ // Try a general SAP.
+ //
+
+ ReceiveMdl = ALLOCATE_MDL(
+ ReceiveBuffer,
+ MAX_SAP_RESPONSE_SIZE,
+ TRUE,
+ FALSE,
+ NULL );
+
+ if ( ReceiveMdl == NULL ) {
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ MmBuildMdlForNonPagedPool( ReceiveMdl );
+ pNewIrpContext->RxMdl->Next = ReceiveMdl;
+
+ Status = ExchangeWithWait (
+ pNewIrpContext,
+ SynchronousResponseCallback,
+ "Aww",
+ SAP_GENERAL_REQUEST,
+ SAP_SERVICE_TYPE_SERVER );
+
+ if ( NT_SUCCESS( Status ) ) {
+ DebugTrace( 0, Dbg, "Received %d bytes\n", pNewIrpContext->ResponseLength );
+ ResponseCount = ( pNewIrpContext->ResponseLength - 2 ) / SAP_RECORD_SIZE;
+
+ //
+ // Process at most MAX_SAP_RESPONSES servers.
+ //
+
+ if ( ResponseCount > MAX_SAP_RESPONSES ) {
+ ResponseCount = MAX_SAP_RESPONSES;
+ }
+
+ for ( i = 0; i < ResponseCount; i++ ) {
+ ProcessFindNearestEntry(
+ pNewIrpContext,
+ (PSAP_FIND_NEAREST_RESPONSE)(pNewIrpContext->rsp + SAP_RECORD_SIZE * i) );
+ }
+ }
+
+ pNewIrpContext->RxMdl->Next = NULL;
+ FREE_MDL( ReceiveMdl );
+ ReceiveMdl = NULL;
+ }
+
+ //
+ // We're done with the find nearest. Free the buffer and
+ // dequeue from the permanent SCB.
+ //
+
+ FREE_POOL( ReceiveBuffer );
+ ReceiveBuffer = NULL;
+ NwDequeueIrpContext( pNewIrpContext, FALSE );
+
+ if ( !NT_SUCCESS( Status ) &&
+ pNewIrpContext->Specific.Create.FindNearestResponseCount == 0 ) {
+
+ //
+ // If the SAP timed out, map the error for MPR.
+ //
+
+ if ( Status == STATUS_REMOTE_NOT_LISTENING ) {
+ Status = STATUS_BAD_NETWORK_PATH;
+ }
+
+ //
+ // Setup the WaitTimeout, and fail this request.
+ //
+
+ KeQuerySystemTime( &TimeoutWait );
+ TimeoutWait.QuadPart += NwOneSecond * 10;
+
+ ExRaiseStatus( Status );
+ return NULL;
+ }
+
+ SentFindNearest = TRUE;
+
+ } else {
+
+ if ( !AllocatedIrpContext ) {
+ AllocatedIrpContext = NwAllocateExtraIrpContext(
+ &pNewIrpContext,
+ pNearestNpScb );
+
+ if ( !AllocatedIrpContext ) {
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+ }
+
+ //
+ // Point the IRP context at the nearest server.
+ //
+
+ pNewIrpContext->pNpScb = pNearestNpScb;
+ NwAppendToQueueAndWait( pNewIrpContext );
+
+ if ( pNearestNpScb->State == SCB_STATE_RECONNECT_REQUIRED ) {
+
+ //
+ // We have no connection to this server, try to
+ // connect now. This is not a valid path for an
+ // anonymous create, so there's no chance that
+ // there will be a name collision.
+ //
+
+ Status = ConnectToServer( pNewIrpContext, NULL );
+ if ( !NT_SUCCESS( Status ) ) {
+
+ //
+ // Failed to connect to the server. Give up.
+ // We'll try another server.
+ //
+
+ NwDequeueIrpContext( pNewIrpContext, FALSE );
+
+ // Keep pNearestScb referenced
+ // so it doesn't disappear.
+
+ pLastNpScb = pNearestNpScb;
+
+ continue;
+
+ } else {
+
+ pNearestNpScb->State = SCB_STATE_LOGIN_REQUIRED;
+ ConnectedToNearest = TRUE;
+
+ }
+ }
+
+ //
+ // update the last used time for this SCB.
+ //
+
+ KeQuerySystemTime( &pNearestNpScb->LastUsedTime );
+
+ if (( pNpScb == NULL ) ||
+ ( ServerName == NULL )) {
+
+ //
+ // We're looking for any server so use this one.
+ //
+ // We'll exit the for loop on the SCB queue,
+ // and with this SCB referenced.
+ //
+
+ pNpScb = pNearestNpScb;
+ Status = STATUS_SUCCESS;
+ FoundServer = TRUE;
+ NwDequeueIrpContext( pNewIrpContext, FALSE );
+
+ } else {
+
+ Status = QueryServersAddress(
+ pNewIrpContext,
+ pNearestNpScb,
+ ServerName,
+ &ServerAddress );
+
+ //
+ // If we connect to this server just to query it's
+ // bindery, disconnect now.
+ //
+
+ if ( ConnectedToNearest && NT_SUCCESS(Status) ) {
+ ExchangeWithWait (
+ pNewIrpContext,
+ SynchronousResponseCallback,
+ "D-" ); // Disconnect
+
+ pNearestNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
+ }
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // Success!
+ //
+ // Point the SCB at the real server address and connect to it,
+ // then logout. (We logout for no apparent reason except
+ // because this is what a netware redir does.)
+ //
+
+ RtlCopyMemory(
+ &pNpScb->ServerAddress,
+ &ServerAddress,
+ sizeof( TDI_ADDRESS_IPX ) );
+
+ BuildIpxAddress(
+ ServerAddress.Net,
+ ServerAddress.Node,
+ NCP_SOCKET,
+ &pNpScb->RemoteAddress );
+
+ FoundServer = TRUE;
+
+ NwDequeueIrpContext( pNewIrpContext, FALSE );
+ NwDereferenceScb( pNearestNpScb );
+
+ pNewIrpContext->pNpScb = pNpScb;
+ pNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
+
+ } else {
+
+ NwDequeueIrpContext( pNewIrpContext, FALSE );
+
+ if ( Status == STATUS_REMOTE_NOT_LISTENING ) {
+
+ //
+ // This server is no longer talking to us.
+ // Try again. Keep pNearestScb referenced
+ // so it doesn't disappear.
+ //
+
+ pLastNpScb = pNearestNpScb;
+
+ continue;
+
+ } else {
+
+ NwDereferenceScb( pNearestNpScb );
+
+ //
+ // This nearest server doesn't know about
+ // the server we are looking for. Give up
+ // and let another rdr try.
+ //
+
+ ExRaiseStatus( STATUS_BAD_NETWORK_PATH );
+ return NULL;
+ }
+ }
+ }
+
+ } // else
+ } // for
+
+ } finally {
+
+ if ( ReceiveBuffer != NULL ) {
+ FREE_POOL( ReceiveBuffer );
+ }
+
+ if ( ReceiveMdl != NULL ) {
+ FREE_MDL( ReceiveMdl );
+ }
+
+ if ( AllocatedIrpContext ) {
+ NwFreeExtraIrpContext( pNewIrpContext );
+ }
+
+ if (pLastNpScb) {
+ NwDereferenceScb( pLastNpScb );
+ }
+
+ }
+
+ if ( !FoundServer ) {
+ ExRaiseStatus( STATUS_BAD_NETWORK_PATH );
+ }
+
+ return pNpScb;
+}
+
+
+NTSTATUS
+ProcessFindNearest(
+ IN struct _IRP_CONTEXT* pIrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ )
+/*++
+
+Routine Description:
+
+ This routine takes the full address of the remote server and builds
+ the corresponding TA_IPX_ADDRESS.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ ULONG ResponseCount;
+ KIRQL OldIrql;
+
+ DebugTrace(+1, Dbg, "ProcessFindNearest...\n", 0);
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ if ( BytesAvailable == 0) {
+
+ //
+ // Timeout.
+ //
+
+ pIrpContext->ResponseParameters.Error = 0;
+ pIrpContext->pNpScb->OkToReceive = FALSE;
+
+ ASSERT( pIrpContext->Event.Header.SignalState == 0 );
+#if NWDBG
+ pIrpContext->DebugValue = 0x101;
+#endif
+ NwSetIrpContextEvent( pIrpContext );
+ DebugTrace(-1, Dbg, "ProcessFindNearest -> %08lx\n", STATUS_REMOTE_NOT_LISTENING);
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+ return STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ if ( BytesAvailable >= FIND_NEAREST_RESP_SIZE &&
+ Response[0] == 0 &&
+ Response[1] == SAP_SERVICE_TYPE_SERVER ) {
+
+ //
+ // This is a valid find nearest response. Process the packet.
+ //
+
+ ResponseCount = pIrpContext->Specific.Create.FindNearestResponseCount++;
+ ASSERT( ResponseCount < MAX_SAP_RESPONSES );
+
+ //
+ // Copy the Find Nearest server response to the receive buffer.
+ //
+
+ RtlCopyMemory(
+ pIrpContext->Specific.Create.FindNearestResponse[ResponseCount],
+ Response,
+ FIND_NEAREST_RESP_SIZE );
+
+ //
+ // If we have reached critical mass on the number of find
+ // nearest responses, set the event to indicate that we
+ // are done.
+ //
+
+ if ( ResponseCount == MAX_SAP_RESPONSES - 1 ) {
+
+ ASSERT( pIrpContext->Event.Header.SignalState == 0 );
+#ifdef NWDBG
+ pIrpContext->DebugValue = 0x102;
+#endif
+ pIrpContext->ResponseParameters.Error = 0;
+ NwSetIrpContextEvent( pIrpContext );
+
+ } else {
+ pIrpContext->pNpScb->OkToReceive = TRUE;
+ }
+
+ } else {
+
+ //
+ // Discard the invalid find nearest response.
+ //
+
+ pIrpContext->pNpScb->OkToReceive = TRUE;
+ }
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+ DebugTrace(-1, Dbg, "ProcessFindNearest -> %08lx\n", STATUS_SUCCESS );
+ return( STATUS_SUCCESS );
+}
+
+BOOLEAN
+ProcessFindNearestEntry(
+ PIRP_CONTEXT IrpContext,
+ PSAP_FIND_NEAREST_RESPONSE FindNearestResponse
+ )
+{
+ OEM_STRING OemServerName;
+ UNICODE_STRING UidServerName;
+ UNICODE_STRING ServerName;
+ NTSTATUS Status;
+ PSCB pScb;
+ PNONPAGED_SCB pNpScb = NULL;
+ BOOLEAN ExistingScb = TRUE;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "ProcessFindNearestEntry\n", 0);
+
+ ServerName.Buffer = NULL;
+ UidServerName.Buffer = NULL;
+
+ try {
+
+ RtlInitString( &OemServerName, FindNearestResponse->ServerName );
+ ASSERT( OemServerName.Length < MAX_SERVER_NAME_LENGTH * sizeof( WCHAR ) );
+
+ Status = RtlOemStringToCountedUnicodeString(
+ &ServerName,
+ &OemServerName,
+ TRUE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ try_return( NOTHING );
+ }
+
+ //
+ // Lookup of the SCB by name. If it is not found, an SCB
+ // will be created.
+ //
+
+ Status = MakeUidServer(
+ &UidServerName,
+ &IrpContext->Specific.Create.UserUid,
+ &ServerName );
+
+ if (!NT_SUCCESS(Status)) {
+ try_return( NOTHING );
+ }
+
+ ExistingScb = NwFindScb( &pScb, IrpContext, &UidServerName, &ServerName );
+ ASSERT( pScb != NULL );
+ pNpScb = pScb->pNpScb;
+
+ //
+ // Copy the network address to the SCB, and calculate the
+ // IPX address.
+ //
+
+ RtlCopyMemory(
+ &pNpScb->ServerAddress,
+ &FindNearestResponse->Network,
+ sizeof( TDI_ADDRESS_IPX ) );
+
+ BuildIpxAddress(
+ pNpScb->ServerAddress.Net,
+ pNpScb->ServerAddress.Node,
+ NCP_SOCKET,
+ &pNpScb->RemoteAddress );
+
+ if ( pNpScb->State == SCB_STATE_ATTACHING ) {
+
+ //
+ // We are in the process of trying to connect to this
+ // server so mark it reconnect required so that
+ // CreateScb will know that we've found it address.
+ //
+
+ pNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
+ }
+
+try_exit: NOTHING;
+
+ } finally {
+
+ if ( pNpScb != NULL ) {
+ NwDereferenceScb( pNpScb );
+ }
+
+ if (UidServerName.Buffer != NULL) {
+ FREE_POOL(UidServerName.Buffer);
+ }
+
+ RtlFreeUnicodeString( &ServerName );
+ }
+
+ //
+ // Tell the caller if we created a new Scb
+ //
+
+
+ if (ExistingScb) {
+ DebugTrace(-1, Dbg, "ProcessFindNearestEntry ->%08lx\n", FALSE );
+ return FALSE;
+ } else {
+ DebugTrace(-1, Dbg, "ProcessFindNearestEntry ->%08lx\n", TRUE );
+ return TRUE;
+ }
+}
+
+
+NTSTATUS
+ConnectToServer(
+ IN struct _IRP_CONTEXT* pIrpContext,
+ OUT PSCB *pScbCollision
+ )
+/*++
+
+Routine Description:
+
+ This routine transfers connect and negotiate buffer NCPs to the server.
+
+ This routine may be called upon to connect an anonymous scb. Upon
+ learning the name of the anonymous scb, it will determine if another
+ create has completed while the name lookup was in progress. If it has,
+ then the routine will refer the called to that new scb. Otherwise, the
+ scb will be entered onto the scb list and used normally. The RCB
+ protects the scb list by name only. For more info, see the comment
+ in CreateScb().
+
+Arguments:
+
+ pIrpContext - supplies context and server information
+
+Return Value:
+
+ Status of operation
+
+--*/
+{
+ NTSTATUS Status, BurstStatus;
+ PNONPAGED_SCB pNpScb = pIrpContext->pNpScb;
+ PSCB pScb = pNpScb->pScb;
+ BOOLEAN AnonymousScb = IS_ANONYMOUS_SCB( pScb );
+ ULONG MaxSafeSize ;
+ BOOLEAN LIPNegotiated ;
+ PLOGON Logon;
+
+ OEM_STRING OemServerName;
+ UNICODE_STRING ServerName;
+ UNICODE_STRING CredentialName;
+ PUNICODE_STRING puConnectName;
+ BYTE OemName[MAX_SERVER_NAME_LENGTH];
+ WCHAR Server[MAX_SERVER_NAME_LENGTH];
+ KIRQL OldIrql;
+ UNICODE_STRING UidServerName;
+ BOOLEAN Success;
+ PLIST_ENTRY ScbQueueEntry;
+ PUNICODE_PREFIX_TABLE_ENTRY PrefixEntry;
+
+ PAGED_CODE();
+
+ DebugTrace( +0, Dbg, " Connect\n", 0);
+
+ //
+ // Get the tick count for our connection to this server
+ //
+
+ Status = GetTickCount( pIrpContext, &pNpScb->TickCount );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ pNpScb->TickCount = DEFAULT_TICK_COUNT;
+ }
+
+ pNpScb->SendTimeout = pNpScb->TickCount + 10;
+
+ //
+ // Initialize timers for a server that supports burst but not LIP
+ //
+
+ pNpScb->NwLoopTime = pNpScb->NwSingleBurstPacketTime = pNpScb->SendTimeout;
+ pNpScb->NwReceiveDelay = pNpScb->NwSendDelay = 0;
+
+ pNpScb->NtSendDelay.QuadPart = 0;
+
+ //
+ // Request connection
+ //
+
+ Status = ExchangeWithWait (
+ pIrpContext,
+ SynchronousResponseCallback,
+ "C-");
+
+ DebugTrace( +0, Dbg, " %X\n", Status);
+
+ if (!NT_SUCCESS(Status)) {
+ if ( Status == STATUS_UNSUCCESSFUL ) {
+#ifdef QFE_BUILD
+ Status = STATUS_TOO_MANY_SESSIONS;
+#else
+ Status = STATUS_REMOTE_SESSION_LIMIT;
+#endif
+ pNpScb->State = SCB_STATE_ATTACHING;
+
+ } else if ( Status == STATUS_REMOTE_NOT_LISTENING ) {
+
+ //
+ // The connect timed out, suspect that the server is down
+ // and put it back in the attaching state.
+ //
+
+ pNpScb->State = SCB_STATE_ATTACHING;
+ }
+
+ return( Status );
+ }
+
+ pNpScb->SequenceNo++;
+
+ Stats.Sessions++;
+
+ //
+ // Get server information
+ //
+
+ DebugTrace( +0, Dbg, "Get file server information\n", 0);
+
+ Status = ExchangeWithWait ( pIrpContext,
+ SynchronousResponseCallback,
+ "S",
+ NCP_ADMIN_FUNCTION, NCP_GET_SERVER_INFO );
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse( pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "Nrbb",
+ OemName,
+ MAX_SERVER_NAME_LENGTH,
+ &pScb->MajorVersion,
+ &pScb->MinorVersion );
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ return(Status);
+ }
+
+ //
+ // If this was an anonymous SCB, we need to check the name
+ // for a create collision before we do anything else.
+ //
+
+ if ( AnonymousScb ) {
+
+ //
+ // Grab the RCB to protect the server prefix table. We've
+ // spent the time sending the packet to look up the server
+ // name so we are a little greedy with the RCB to help
+ // minimize the chance of a collision.
+ //
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ //
+ // Make the uid server name.
+ //
+
+ OemServerName.Buffer = OemName;
+ OemServerName.Length = 0;
+ OemServerName.MaximumLength = sizeof( OemName );
+
+ while ( ( OemServerName.Length < MAX_SERVER_NAME_LENGTH ) &&
+ ( OemName[OemServerName.Length] != '\0' ) ) {
+ OemServerName.Length++;
+ }
+
+ ServerName.Buffer = Server;
+ ServerName.MaximumLength = sizeof( Server );
+ ServerName.Length = 0;
+
+ RtlOemStringToUnicodeString( &ServerName,
+ &OemServerName,
+ FALSE );
+
+ //
+ // If this is an extended credential create, munge the server name.
+ //
+
+ RtlInitUnicodeString( &CredentialName, NULL );
+
+ if ( pIrpContext->Specific.Create.fExCredentialCreate ) {
+
+ Status = BuildExCredentialServerName( &ServerName,
+ pIrpContext->Specific.Create.puCredentialName,
+ &CredentialName );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NwReleaseRcb( &NwRcb );
+ return Status;
+ }
+
+ puConnectName = &CredentialName;
+
+ } else {
+
+ puConnectName = &ServerName;
+ }
+
+ //
+ // Tack on the uid.
+ //
+
+ Status = MakeUidServer( &UidServerName,
+ &pScb->UserUid,
+ puConnectName );
+
+ if ( CredentialName.Buffer ) {
+ FREE_POOL( CredentialName.Buffer );
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NwReleaseRcb( &NwRcb );
+ return Status;
+ }
+
+ //
+ // Actually do the look up in the prefix table.
+ //
+
+ PrefixEntry = RtlFindUnicodePrefix( &NwRcb.ServerNameTable, &UidServerName, 0 );
+
+ if ( PrefixEntry != NULL ) {
+
+ //
+ // There was a collision with this anonymous create. Dump
+ // the anonymous scb and pick up the new one.
+ //
+
+ NwReleaseRcb( &NwRcb );
+ DebugTrace( 0, DEBUG_TRACE_ALWAYS, "Anonymous create collided for %wZ.\n", &UidServerName );
+
+ //
+ // Disconnect this connection so we don't clutter the server.
+ //
+
+ ExchangeWithWait ( pIrpContext,
+ SynchronousResponseCallback,
+ "D-" );
+
+ FREE_POOL( UidServerName.Buffer );
+
+ //
+ // Since there was a collision, we know for a fact that there's another
+ // good SCB for this server somewhere. We set the state on this anonymous
+ // SCB to SCB_STATE_FLAG_SHUTDOWN so that no one ever plays with the
+ // anonymous SCB again. The scavenger will clean it up soon.
+ //
+
+ pNpScb->State = SCB_STATE_FLAG_SHUTDOWN;
+
+ if ( pScbCollision ) {
+ *pScbCollision = CONTAINING_RECORD( PrefixEntry, SCB, PrefixEntry );
+ NwReferenceScb( (*pScbCollision)->pNpScb );
+ return STATUS_SUCCESS;
+ } else {
+ DebugTrace( 0, Dbg, "Invalid path for an anonymous create.\n", 0 );
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ }
+
+ //
+ // This anonymous create didn't collide - cool! Fill in the server
+ // name, check the preferred, server setting, and put the SCB on the
+ // SCB queue in the correct location. This code is similar to pieces
+ // of code in NwAllocateAndInitScb() and NwFindScb().
+ //
+
+ DebugTrace( 0, Dbg, "Completing anonymous create for %wZ!\n", &UidServerName );
+
+ RtlCopyUnicodeString ( &pScb->UidServerName, &UidServerName );
+ pScb->UidServerName.Buffer[ UidServerName.Length / sizeof( WCHAR ) ] = L'\0';
+
+ pScb->UnicodeUid = pScb->UidServerName;
+ pScb->UnicodeUid.Length = UidServerName.Length -
+ puConnectName->Length -
+ sizeof(WCHAR);
+
+ //
+ // Make ServerName point partway down the buffer for UidServerName
+ //
+
+ pNpScb->ServerName.Buffer = (PWSTR)((PUCHAR)pScb->UidServerName.Buffer +
+ UidServerName.Length - puConnectName->Length);
+
+ pNpScb->ServerName.MaximumLength = puConnectName->Length;
+ pNpScb->ServerName.Length = puConnectName->Length;
+
+ //
+ // Determine if this is our preferred server.
+ //
+
+ Logon = FindUser( &pScb->UserUid, FALSE );
+
+ if (( Logon != NULL) &&
+ (RtlCompareUnicodeString( puConnectName, &Logon->ServerName, TRUE ) == 0 )) {
+ pScb->PreferredServer = TRUE;
+ NwReferenceScb( pNpScb );
+ }
+
+ FREE_POOL( UidServerName.Buffer );
+
+ //
+ // Insert the name of this server into the prefix table.
+ //
+
+ Success = RtlInsertUnicodePrefix( &NwRcb.ServerNameTable,
+ &pScb->UidServerName,
+ &pScb->PrefixEntry );
+
+#ifdef NWDBG
+ if ( !Success ) {
+ DebugTrace( 0, DEBUG_TRACE_ALWAYS, "Entering duplicate SCB %wZ.\n", &pScb->UidServerName );
+ DbgBreakPoint();
+ }
+#endif
+
+ //
+ // This create is complete, release the RCB.
+ //
+
+ NwReleaseRcb( &NwRcb );
+
+ //
+ // If this is our preferred server, we have to move this guy
+ // to the head of the scb list. We do this after the create
+ // because we can't acquire the ScbSpinLock while holding the
+ // RCB.
+ //
+
+ if ( pScb->PreferredServer ) {
+
+ KeAcquireSpinLock(&ScbSpinLock, &OldIrql);
+ RemoveEntryList( &pNpScb->ScbLinks );
+ InsertHeadList( &ScbQueue, &pNpScb->ScbLinks );
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+ }
+
+ }
+
+ if ( pScb->MajorVersion == 2 ) {
+
+ Stats.NW2xConnects++;
+ pNpScb->PageAlign = TRUE;
+
+ } else if ( pScb->MajorVersion == 3 ) {
+
+ Stats.NW3xConnects++;
+
+ if (pScb->MinorVersion > 0xb) {
+ pNpScb->PageAlign = FALSE;
+ } else {
+ pNpScb->PageAlign = TRUE;
+ }
+
+ } else if ( pScb->MajorVersion == 4 ) {
+
+ Stats.NW4xConnects++;
+ pNpScb->PageAlign = FALSE;
+
+ NdsPing( pIrpContext, pScb );
+
+ }
+
+ //
+ // Get the local net max packet size. This is the max frame size
+ // does not include space for IPX or lower level headers.
+ //
+
+ Status = GetMaximumPacketSize( pIrpContext, &pNpScb->Server, &pNpScb->MaxPacketSize );
+
+ //
+ // If the transport won't tell us, pick the largest size that
+ // is guaranteed to work.
+ //
+ if ( !NT_SUCCESS( Status ) ) {
+ pNpScb->BufferSize = DEFAULT_PACKET_SIZE;
+ pNpScb->MaxPacketSize = DEFAULT_PACKET_SIZE;
+ } else {
+ pNpScb->BufferSize = (USHORT)pNpScb->MaxPacketSize;
+ }
+ MaxSafeSize = pNpScb->MaxPacketSize ;
+
+ //
+ // Negotiate a burst mode connection. Keep track of that status.
+ //
+
+ Status = NegotiateBurstMode( pIrpContext, pNpScb, &LIPNegotiated );
+ BurstStatus = Status ;
+
+ if (!NT_SUCCESS(Status) || !LIPNegotiated) {
+
+ //
+ // Negotiate buffer size with server if we didnt do burst
+ // sucessfully or if burst succeeded but we didnt do LIP.
+ //
+
+ DebugTrace( +0, Dbg, "Negotiate Buffer Size\n", 0);
+
+ Status = ExchangeWithWait ( pIrpContext,
+ SynchronousResponseCallback,
+ "Fw",
+ NCP_NEGOTIATE_BUFFER_SIZE,
+ pNpScb->BufferSize );
+
+ DebugTrace( +0, Dbg, " %X\n", Status);
+ DebugTrace( +0, Dbg, " Parse response\n", 0);
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse( pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "Nw",
+ &pNpScb->BufferSize );
+
+ //
+ // Dont allow the server to fool us into using a
+ // packet size bigger than what the media can support.
+ // We have at least one case of server returning 4K while
+ // on ethernet.
+ //
+ // Use PacketThreshold so that the PacketAdjustment can be
+ // avoided on small packet sizes such as those on ethernet.
+ //
+
+ if (MaxSafeSize > (ULONG)PacketThreshold) {
+ MaxSafeSize -= (ULONG)LargePacketAdjustment;
+ }
+
+ //
+ // If larger than number we got from transport, taking in account
+ // IPX header (30) & NCP header (BURST_RESPONSE is a good worst
+ // case), we adjust accordingly.
+ //
+ if (pNpScb->BufferSize >
+ (MaxSafeSize - (30 + sizeof(NCP_BURST_READ_RESPONSE))))
+ {
+ pNpScb->BufferSize = (USHORT)
+ (MaxSafeSize - (30 + sizeof(NCP_BURST_READ_RESPONSE))) ;
+ }
+
+ //
+ // An SFT III server responded with a BufferSize of 0!
+ //
+
+ pNpScb->BufferSize = MAX(pNpScb->BufferSize,DEFAULT_PACKET_SIZE);
+
+ //
+ // If an explicit registry default was set, we honour that.
+ // Note that this only applies in the 'default' case, ie. we
+ // didnt negotiate LIP successfully. Typically, we dont
+ // expect to use this, because the server will drop to 512 if
+ // it finds routers in between. But if for some reason the server
+ // came back with a number that was higher than what some router
+ // in between can take, we have this as manual override.
+ //
+
+ if (DefaultMaxPacketSize != 0)
+ {
+ pNpScb->BufferSize = MIN (pNpScb->BufferSize,
+ (USHORT)DefaultMaxPacketSize) ;
+ }
+ }
+
+ if (NT_SUCCESS(BurstStatus)) {
+ //
+ // We negotiated burst but not LIP. Save the packet size we
+ // have from above and renegotiate the burst so that the
+ // server knows how much it can send to us. And then take
+ // the minimum of the two to make sure we are safe.
+ //
+ USHORT SavedPacketSize = pNpScb->BufferSize ;
+
+ Status = NegotiateBurstMode( pIrpContext, pNpScb, &LIPNegotiated );
+
+ pNpScb->BufferSize = MIN(pNpScb->BufferSize,SavedPacketSize) ;
+ }
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NegotiateBurstMode(
+ PIRP_CONTEXT pIrpContext,
+ PNONPAGED_SCB pNpScb,
+ BOOLEAN *LIPNegotiated
+ )
+/*++
+
+Routine Description:
+
+ This routine negotiates a burst mode connection with the specified
+ server.
+
+Arguments:
+
+ pIrpContext - Supplies context and server information.
+
+ pNpScb - A pointer to the NONPAGED_SCB for the server we are
+ negotiating with.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ *LIPNegotiated = FALSE ;
+
+ if (pNpScb->MaxPacketSize == DEFAULT_PACKET_SIZE) {
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ if ( NwBurstModeEnabled ) {
+
+ pNpScb->BurstRenegotiateReqd = TRUE;
+
+ pNpScb->SourceConnectionId = rand();
+ pNpScb->MaxSendSize = NwMaxSendSize;
+ pNpScb->MaxReceiveSize = NwMaxReceiveSize;
+ pNpScb->BurstSequenceNo = 0;
+ pNpScb->BurstRequestNo = 0;
+
+ Status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "FDdWdd",
+ NCP_NEGOTIATE_BURST_CONNECTION,
+ pNpScb->SourceConnectionId,
+ pNpScb->BufferSize,
+ pNpScb->Burst.Socket,
+ pNpScb->MaxSendSize,
+ pNpScb->MaxReceiveSize );
+
+ if ( NT_SUCCESS( Status )) {
+ Status = ParseResponse(
+ pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "Ned",
+ &pNpScb->DestinationConnectionId,
+ &pNpScb->MaxPacketSize );
+
+ if (pNpScb->MaxPacketSize <= DEFAULT_PACKET_SIZE) {
+ pNpScb->MaxPacketSize = DEFAULT_PACKET_SIZE;
+ }
+ }
+
+ if ( NT_SUCCESS( Status )) {
+
+ if (NT_SUCCESS(GetMaxPacketSize( pIrpContext, pNpScb ))) {
+ *LIPNegotiated = TRUE ;
+ }
+
+ pNpScb->SendBurstModeEnabled = TRUE;
+ pNpScb->ReceiveBurstModeEnabled = TRUE;
+
+ //
+ // Use this size as the max read and write size instead of
+ // negotiating. This is what the VLM client does and is
+ // important because the negotiate will give a smaller value.
+ //
+
+ pNpScb->BufferSize = (USHORT)pNpScb->MaxPacketSize;
+
+ return STATUS_SUCCESS;
+ }
+ }
+
+ return STATUS_NOT_SUPPORTED;
+}
+
+
+
+VOID
+RenegotiateBurstMode(
+ PIRP_CONTEXT pIrpContext,
+ PNONPAGED_SCB pNpScb
+ )
+/*++
+
+Routine Description:
+
+ This routine renegotiates a burst mode connection with the specified
+ server. I don't know why we need this but it seems to be required
+ by Netware latest burst implementation.
+
+Arguments:
+
+ pIrpContext - Supplies context and server information.
+
+ pNpScb - A pointer to the NONPAGED_SCB for the server we are
+ negotiating with.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "Re-negotiating burst mode.\n", 0);
+
+ pNpScb->SourceConnectionId = rand();
+ pNpScb->MaxSendSize = NwMaxSendSize;
+ pNpScb->MaxReceiveSize = NwMaxReceiveSize;
+ pNpScb->BurstSequenceNo = 0;
+ pNpScb->BurstRequestNo = 0;
+
+ Status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "FDdWdd",
+ NCP_NEGOTIATE_BURST_CONNECTION,
+ pNpScb->SourceConnectionId,
+ pNpScb->MaxPacketSize,
+ pNpScb->Burst.Socket,
+ pNpScb->MaxSendSize,
+ pNpScb->MaxReceiveSize );
+
+ if ( NT_SUCCESS( Status )) {
+ Status = ParseResponse(
+ pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "Ned",
+ &pNpScb->DestinationConnectionId,
+ &pNpScb->MaxPacketSize );
+
+ //
+ // Randomly downgrade the max burst size, because that is what
+ // the netware server does, and the new burst NLM requires.
+ //
+
+ pNpScb->MaxPacketSize -= 66;
+
+ }
+
+ if ( !NT_SUCCESS( Status ) ||
+ (pNpScb->MaxPacketSize <= DEFAULT_PACKET_SIZE)) {
+
+ pNpScb->MaxPacketSize = DEFAULT_PACKET_SIZE;
+ pNpScb->SendBurstModeEnabled = FALSE;
+ pNpScb->ReceiveBurstModeEnabled = FALSE;
+
+ } else {
+
+ //
+ // Use this size as the max read and write size instead of
+ // negotiating. This is what the VLM client does and is
+ // important because the negotiate will give a smaller value.
+ //
+
+ pNpScb->BufferSize = (USHORT)pNpScb->MaxPacketSize;
+
+ }
+}
+
+
+NTSTATUS
+GetMaxPacketSize(
+ PIRP_CONTEXT pIrpContext,
+ PNONPAGED_SCB pNpScb
+ )
+/*++
+
+Routine Description:
+
+ This routine attempts to use the LIP protocol to find the true MTU of
+ the network.
+
+Arguments:
+
+ pIrpContext - Supplies context and server information.
+
+ pNpScb - A pointer to the NONPAGED_SCB for the server we are '
+ negotiating with.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PUSHORT Buffer = NULL;
+ int index, value;
+ PMDL PartialMdl = NULL, FullMdl = NULL;
+ PMDL ReceiveMdl;
+ NTSTATUS Status;
+ USHORT EchoSocket, LipPacketSize = 0;
+ int MinPacketSize, MaxPacketSize, CurrentPacketSize;
+ ULONG RxMdlLength = MdlLength(pIrpContext->RxMdl); // Save so we can restore it on exit.
+
+ BOOLEAN SecondTime = FALSE;
+ LARGE_INTEGER StartTime, Now, FirstPing, SecondPing, temp;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, DEBUG_TRACE_LIP, "GetMaxPacketSize...\n", 0);
+
+ //
+ // Negotiate LIP, attempt to negotiate a buffer of full network
+ // size.
+ //
+
+ Status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "Fwb",
+ NCP_NEGOTIATE_LIP_CONNECTION,
+ pNpScb->BufferSize,
+ 0 ); // Flags
+
+ if ( NT_SUCCESS( Status )) {
+ Status = ParseResponse(
+ pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "Nwx",
+ &LipPacketSize,
+ &EchoSocket );
+ }
+
+ //
+ // Speedup RAS
+ //
+
+ MaxPacketSize = (int) LipPacketSize - LipPacketAdjustment ;
+
+ if (( !NT_SUCCESS( Status )) ||
+ ( MaxPacketSize <= DEFAULT_PACKET_SIZE ) ||
+ ( EchoSocket == 0 )) {
+
+ //
+ // The server does not support LIP.
+ // Portable NW gives no error but socket 0.
+ // We have a report of a 3.11 server returning MaxPacketSize 0
+ //
+
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ //
+ // Account for the IPX header, which is not counted in
+ // the reported packet size. This causes problems for
+ // servers with poorly written net card drivers that
+ // abend when they get an oversize packet.
+ //
+ // This was reported by Richard Florance (richfl).
+ //
+
+ MaxPacketSize -= 30;
+
+ pNpScb->EchoCounter = MaxPacketSize;
+
+ //
+ // We will use the Echo address for the LIP protocol.
+ //
+
+ BuildIpxAddress(
+ pNpScb->ServerAddress.Net,
+ pNpScb->ServerAddress.Node,
+ EchoSocket,
+ &pNpScb->EchoAddress );
+
+ try {
+
+ Buffer = ALLOCATE_POOL_EX( NonPagedPool, MaxPacketSize );
+
+ //
+ // Avoid RAS compression algorithm from making the large and small
+ // buffers the same length since we want to see the difference in
+ // transmission times.
+ //
+
+ for (index = 0, value = 0; index < MaxPacketSize/2; index++, value++) {
+ Buffer[index] = value;
+ }
+
+ FullMdl = ALLOCATE_MDL( Buffer, MaxPacketSize, TRUE, FALSE, NULL );
+ if ( FullMdl == NULL ) {
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ PartialMdl = ALLOCATE_MDL( Buffer, MaxPacketSize, TRUE, FALSE, NULL );
+ if ( PartialMdl == NULL ) {
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ ReceiveMdl = ALLOCATE_MDL( Buffer, MaxPacketSize, TRUE, FALSE, NULL );
+ if ( ReceiveMdl == NULL ) {
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ } except( NwExceptionFilter( pIrpContext->pOriginalIrp, GetExceptionInformation() )) {
+
+ if ( Buffer != NULL ) {
+ FREE_POOL( Buffer );
+ }
+
+ if ( FullMdl != NULL ) {
+ FREE_MDL( FullMdl );
+ }
+
+ if ( PartialMdl != NULL ) {
+ FREE_MDL( FullMdl );
+ }
+
+ return STATUS_NOT_SUPPORTED;
+ }
+
+ MmBuildMdlForNonPagedPool( FullMdl );
+
+ //
+ // Allocate a receive MDL and chain in to the IrpContext receive MDL.
+ //
+
+ pIrpContext->RxMdl->ByteCount = sizeof( NCP_RESPONSE ) + sizeof(ULONG);
+ MmBuildMdlForNonPagedPool( ReceiveMdl );
+ pIrpContext->RxMdl->Next = ReceiveMdl;
+
+ CurrentPacketSize = MaxPacketSize;
+ MinPacketSize = DEFAULT_PACKET_SIZE;
+
+ // Log values before we update them.
+ DebugTrace( 0, DEBUG_TRACE_LIP, "Using TickCount = %08lx\n", pNpScb->TickCount * pNpScb->MaxPacketSize);
+ DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NwSendDelay = %08lx\n", pNpScb->NwSendDelay );
+ DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NtSendDelay H = %08lx\n", pNpScb->NtSendDelay.HighPart );
+ DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NtSendDelay L = %08lx\n", pNpScb->NtSendDelay.LowPart );
+
+ //
+ // Loop using the bisection method to find the maximum packet size. Feel free to
+ // use shortcuts to avoid unnecessary timeouts.
+ //
+
+ while (TRUE) {
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "Sending %d byte echo\n", CurrentPacketSize );
+
+ IoBuildPartialMdl(
+ FullMdl,
+ PartialMdl,
+ Buffer,
+ CurrentPacketSize - sizeof(NCP_RESPONSE) - sizeof(ULONG) );
+
+ //
+ // Send an echo packet. If we get a response, then we know that
+ // the minimum packet size we can use is at least as big as the
+ // echo packet size.
+ //
+
+ pIrpContext->pTdiStruct = &pIrpContext->pNpScb->Echo;
+
+ //
+ // Short-circuit the even better RAS compression.
+ //
+
+ for ( index = 0; index < MaxPacketSize/2; index++, value++) {
+ Buffer[index] = value;
+ }
+
+ KeQuerySystemTime( &StartTime );
+
+ Status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "E_Df",
+ sizeof(NCP_RESPONSE ),
+ pNpScb->EchoCounter,
+ PartialMdl );
+
+ if (( Status != STATUS_REMOTE_NOT_LISTENING ) ||
+ ( SecondTime )) {
+
+ KeQuerySystemTime( &Now );
+ DebugTrace( 0, DEBUG_TRACE_LIP, "Response received %08lx\n", Status);
+
+ if (!SecondTime) {
+
+ MinPacketSize = CurrentPacketSize;
+ FirstPing.QuadPart = Now.QuadPart - StartTime.QuadPart;
+ }
+
+ } else {
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "No response\n", 0);
+ MaxPacketSize = CurrentPacketSize;
+ }
+
+ pNpScb->EchoCounter++;
+ MmPrepareMdlForReuse( PartialMdl );
+
+
+ if (( MaxPacketSize - MinPacketSize <= LipAccuracy ) ||
+ ( SecondTime )) {
+
+ //
+ // We have the maximum packet size.
+ // Now - StartTime is how long it takes for the round-trip. Now we'll
+ // try the same thing with a small packet and see how long it takes. From
+ // this we'll derive a throughput rating.
+ //
+
+
+ if ( SecondTime) {
+
+ SecondPing.QuadPart = Now.QuadPart - StartTime.QuadPart;
+ break;
+
+ } else {
+ SecondTime = TRUE;
+ // Use a small packet size to verify that the server is still up.
+ CurrentPacketSize = sizeof(NCP_RESPONSE) + sizeof(ULONG) * 2;
+ }
+
+ } else {
+
+ //
+ // Calculate the next packet size guess.
+ //
+
+ if (( Status == STATUS_REMOTE_NOT_LISTENING ) &&
+ ( MaxPacketSize == 1463 )) {
+
+ CurrentPacketSize = 1458;
+
+ } else if (( Status == STATUS_REMOTE_NOT_LISTENING ) &&
+ ( MaxPacketSize == 1458 )) {
+
+ CurrentPacketSize = 1436;
+
+ } else {
+
+ //
+ // We didn't try one of our standard sizes so use the chop search
+ // to get to the next value.
+ //
+
+ CurrentPacketSize = ( MaxPacketSize + MinPacketSize ) / 2;
+
+ }
+ }
+ }
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "Set maximum burst packet size to %d\n", MinPacketSize );
+ DebugTrace( 0, DEBUG_TRACE_LIP, "FirstPing H = %08lx\n", FirstPing.HighPart );
+ DebugTrace( 0, DEBUG_TRACE_LIP, "FirstPing L = %08lx\n", FirstPing.LowPart );
+ DebugTrace( 0, DEBUG_TRACE_LIP, "SecondPing H = %08lx\n", SecondPing.HighPart );
+ DebugTrace( 0, DEBUG_TRACE_LIP, "SecondPing L = %08lx\n", SecondPing.LowPart );
+
+ //
+ // Avoid a divide by zero error if something bad happened.
+ //
+
+ if ( FirstPing.QuadPart != 0 ) {
+ pNpScb->LipDataSpeed = (ULONG) ( ( (LONGLONG)MinPacketSize * (LONGLONG)1600000 )
+ / FirstPing.QuadPart );
+ } else {
+ pNpScb->LipDataSpeed = 0;
+ }
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "LipDataSpeed: %d\n", pNpScb->LipDataSpeed );
+
+ if ((NT_SUCCESS(Status)) &&
+ ( MinPacketSize > DEFAULT_PACKET_SIZE )) {
+
+ temp.QuadPart = FirstPing.QuadPart - SecondPing.QuadPart;
+
+ if (temp.QuadPart > 0) {
+
+ //
+ // Convert to single trip instead of both ways.
+ //
+
+ temp.QuadPart = temp.QuadPart / (2 * 1000);
+
+ } else {
+
+ //
+ // Small packet ping is slower or the same speed as the big ping.
+ // We can't time a small enough interval so go for no delay at all.
+ //
+
+ temp.QuadPart = 0;
+
+ }
+
+
+ ASSERT(temp.HighPart == 0);
+
+ pNpScb->NwGoodSendDelay = pNpScb->NwBadSendDelay = pNpScb->NwSendDelay =
+ MAX(temp.LowPart, (ULONG)MinSendDelay);
+
+ pNpScb->NwGoodReceiveDelay = pNpScb->NwBadReceiveDelay = pNpScb->NwReceiveDelay =
+ MAX(temp.LowPart, (ULONG)MinReceiveDelay);
+
+ //
+ // Time for a big packet to go one way.
+ //
+
+ pNpScb->NwSingleBurstPacketTime = pNpScb->NwReceiveDelay;
+
+ pNpScb->NtSendDelay.QuadPart = pNpScb->NwReceiveDelay * -1000;
+
+
+ //
+ // Maximum that SendDelay is allowed to reach
+ //
+
+ pNpScb->NwMaxSendDelay = MAX( 52, MIN( pNpScb->NwSendDelay, MaxSendDelay ));
+ pNpScb->NwMaxReceiveDelay = MAX( 52, MIN( pNpScb->NwReceiveDelay, MaxReceiveDelay ));
+
+ //
+ // Time for a small packet to get to the server and back.
+ //
+
+ temp.QuadPart = SecondPing.QuadPart / 1000;
+ pNpScb->NwLoopTime = temp.LowPart;
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "Using TickCount = %08lx\n", pNpScb->TickCount * pNpScb->MaxPacketSize);
+ DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NwSendDelay = %08lx\n", pNpScb->NwSendDelay );
+ DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NwMaxSendDelay = %08lx\n", pNpScb->NwMaxSendDelay );
+ DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NwMaxReceiveDelay = %08lx\n", pNpScb->NwMaxReceiveDelay );
+ DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NwLoopTime = %08lx\n", pNpScb->NwLoopTime );
+ DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NtSendDelay H = %08lx\n", pNpScb->NtSendDelay.HighPart );
+ DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->NtSendDelay L = %08lx\n", pNpScb->NtSendDelay.LowPart );
+
+ //
+ // Reset Tdi struct so that we send future NCPs from the server socket.
+ //
+
+ pIrpContext->pTdiStruct = NULL;
+
+ //
+ // Now decouple the MDL
+ //
+
+ pIrpContext->TxMdl->Next = NULL;
+ pIrpContext->RxMdl->Next = NULL;
+ pIrpContext->RxMdl->ByteCount = RxMdlLength;
+
+ //
+ // Calculate the maximum amount of data we can send in a burst write
+ // packet after all the header info is stripped.
+ //
+ // BUGBUG - This is what Novell does, but real header isn't that big
+ // can we do better?
+ //
+
+ pNpScb->MaxPacketSize = MinPacketSize - sizeof( NCP_BURST_WRITE_REQUEST );
+
+ FREE_MDL( PartialMdl );
+ FREE_MDL( ReceiveMdl );
+ FREE_MDL( FullMdl );
+ FREE_POOL( Buffer );
+
+
+ DebugTrace( -1, DEBUG_TRACE_LIP, "GetMaxPacketSize -> VOID\n", 0);
+ return STATUS_SUCCESS;
+
+ } else {
+
+ //
+ // If the small packet couldn't echo then assume the worst.
+ //
+
+ //
+ // Reset Tdi struct so that we send future NCPs from the server socket.
+ //
+
+ pIrpContext->pTdiStruct = NULL;
+
+ //
+ // Now decouple the MDL
+ //
+
+ pIrpContext->TxMdl->Next = NULL;
+ pIrpContext->RxMdl->Next = NULL;
+ pIrpContext->RxMdl->ByteCount = RxMdlLength;
+
+ FREE_MDL( PartialMdl );
+ FREE_MDL( ReceiveMdl );
+ FREE_MDL( FullMdl );
+ FREE_POOL( Buffer );
+
+
+ DebugTrace( -1, DEBUG_TRACE_LIP, "GetMaxPacketSize -> VOID\n", 0);
+ return STATUS_NOT_SUPPORTED;
+ }
+
+}
+
+
+VOID
+DestroyAllScb(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This routine destroys all server control blocks.
+
+Arguments:
+
+
+Return Value:
+
+
+--*/
+
+{
+ KIRQL OldIrql;
+ PLIST_ENTRY ScbQueueEntry;
+ PNONPAGED_SCB pNpScb;
+
+ DebugTrace(+1, Dbg, "DestroyAllScbs....\n", 0);
+
+ KeAcquireSpinLock(&ScbSpinLock, &OldIrql);
+
+ //
+ // Walk the list of SCBs and kill them all.
+ //
+
+ while (!IsListEmpty(&ScbQueue)) {
+
+ ScbQueueEntry = RemoveHeadList( &ScbQueue );
+ pNpScb = CONTAINING_RECORD(ScbQueueEntry, NONPAGED_SCB, ScbLinks);
+
+ //
+ // We can't hold the spin lock while deleting an SCB, so release
+ // it now.
+ //
+
+ KeReleaseSpinLock(&ScbSpinLock, OldIrql);
+
+ NwDeleteScb( pNpScb->pScb );
+
+ KeAcquireSpinLock(&ScbSpinLock, &OldIrql);
+ }
+
+ KeReleaseSpinLock(&ScbSpinLock, OldIrql);
+
+ DebugTrace(-1, Dbg, "DestroyAllScb\n", 0 );
+}
+
+
+VOID
+NwDeleteScb(
+ PSCB pScb
+ )
+/*++
+
+Routine Description:
+
+ This routine deletes an SCB. The SCB must not be in use.
+
+ *** The caller must own the RCB exclusive.
+
+Arguments:
+
+ Scb - The SCB to delete
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PNONPAGED_SCB pNpScb;
+ BOOLEAN AnonymousScb = IS_ANONYMOUS_SCB( pScb );
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwDeleteScb...\n", 0);
+
+ pNpScb = pScb->pNpScb;
+
+ //
+ // Make sure we are not deleting a logged in connection
+ // or we will hang up the license until the server times
+ // it out.
+ //
+
+ ASSERT( pNpScb->State != SCB_STATE_IN_USE );
+ ASSERT( pNpScb->Reference == 0 );
+ ASSERT( !pNpScb->Sending );
+ ASSERT( !pNpScb->Receiving );
+ ASSERT( !pNpScb->OkToReceive );
+ ASSERT( IsListEmpty( &pNpScb->Requests ) );
+ ASSERT( IsListEmpty( &pScb->IcbList ) );
+ ASSERT( pScb->IcbCount == 0 );
+ ASSERT( pScb->VcbCount == 0 );
+
+
+ DebugTrace(0, Dbg, "Cleaning up SCB %08lx\n", pScb);
+
+ if ( AnonymousScb ) {
+ DebugTrace(0, Dbg, "SCB is anonymous\n", &pNpScb->ServerName );
+ } else {
+ ASSERT( IsListEmpty( &pScb->ScbSpecificVcbQueue ) );
+ DebugTrace(0, Dbg, "SCB is %wZ\n", &pNpScb->ServerName );
+ }
+
+ DebugTrace(0, Dbg, "SCB state is %d\n", &pNpScb->State );
+
+ if ( !AnonymousScb ) {
+ RtlRemoveUnicodePrefix ( &NwRcb.ServerNameTable, &pScb->PrefixEntry );
+ }
+
+ IPX_Close_Socket( &pNpScb->Server );
+ IPX_Close_Socket( &pNpScb->WatchDog );
+ IPX_Close_Socket( &pNpScb->Send );
+ IPX_Close_Socket( &pNpScb->Echo);
+ IPX_Close_Socket( &pNpScb->Burst);
+
+ FREE_POOL( pNpScb );
+
+ if ( pScb->UserName.Buffer != NULL ) {
+ FREE_POOL( pScb->UserName.Buffer );
+ }
+
+ FREE_POOL( pScb );
+
+ DebugTrace(-1, Dbg, "NwDeleteScb -> VOID\n", 0);
+}
+
+
+PNONPAGED_SCB
+SelectConnection(
+ PNONPAGED_SCB NpScb OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ Find a default server (which is also the nearest server).
+ If NpScb is not supplied, simply return the first server in
+ the list. If it is supplied return the next server in the
+ list after the given server.
+
+Arguments:
+
+ NpScb - The starting point for the server search.
+
+Return Value:
+
+ Scb to be used or NULL.
+
+--*/
+{
+ PLIST_ENTRY ScbQueueEntry;
+ KIRQL OldIrql;
+ PNONPAGED_SCB pNextNpScb;
+
+ DebugTrace(+1, Dbg, "SelectConnection....\n", 0);
+ KeAcquireSpinLock(&ScbSpinLock, &OldIrql);
+
+ if ( NpScb == NULL ) {
+ ScbQueueEntry = ScbQueue.Flink ;
+ } else {
+ ScbQueueEntry = NpScb->ScbLinks.Flink;
+ }
+
+ for ( ;
+ ScbQueueEntry != &ScbQueue ;
+ ScbQueueEntry = ScbQueueEntry->Flink ) {
+
+ pNextNpScb = CONTAINING_RECORD(
+ ScbQueueEntry,
+ NONPAGED_SCB,
+ ScbLinks );
+
+ //
+ // Check to make sure that this SCB is usable.
+ //
+
+ if (( pNextNpScb->State == SCB_STATE_RECONNECT_REQUIRED ) ||
+ ( pNextNpScb->State == SCB_STATE_LOGIN_REQUIRED ) ||
+ ( pNextNpScb->State == SCB_STATE_IN_USE )) {
+
+ NwReferenceScb( pNextNpScb );
+
+ KeReleaseSpinLock(&ScbSpinLock, OldIrql);
+ DebugTrace(+0, Dbg, " NpScb = %lx\n", pNextNpScb );
+ DebugTrace(-1, Dbg, " NpScb->State = %x\n", pNextNpScb->State );
+ return pNextNpScb;
+ }
+ }
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql);
+ DebugTrace(-1, Dbg, " NULL\n", 0);
+ return NULL;
+}
+
+
+VOID
+NwLogoffAllServers(
+ PIRP_CONTEXT pIrpContext,
+ PLARGE_INTEGER Uid
+ )
+/*++
+
+Routine Description:
+
+ This routine sends a logoff to all connected servers created by the Logon
+ user or all servers if Logon is NULL.
+
+Arguments:
+
+ Uid - Supplies the servers to disconnect from.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ KIRQL OldIrql;
+ PLIST_ENTRY ScbQueueEntry;
+ PLIST_ENTRY NextScbQueueEntry;
+ PNONPAGED_SCB pNpScb;
+
+ DebugTrace( 0, Dbg, "NwLogoffAllServers\n", 0 );
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ for (ScbQueueEntry = ScbQueue.Flink ;
+ ScbQueueEntry != &ScbQueue ;
+ ScbQueueEntry = NextScbQueueEntry ) {
+
+ pNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
+
+ //
+ // Reference the SCB so that it doesn't disappear while we
+ // are disconnecting.
+ //
+
+ NwReferenceScb( pNpScb );
+
+ //
+ // Release the SCB spin lock so that we can send a logoff
+ // NCP.
+ //
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+ //
+ // Destroy this Scb if its not the permanent Scb and either we
+ // are destroying all Scb's or it was created for this user.
+ //
+
+ if (( pNpScb->pScb != NULL ) &&
+ (( Uid == NULL ) ||
+ ( pNpScb->pScb->UserUid.QuadPart == (*Uid).QuadPart))) {
+
+ NwLogoffAndDisconnect( pIrpContext, pNpScb );
+ }
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ //
+ // Release the temporary reference.
+ //
+
+ NextScbQueueEntry = pNpScb->ScbLinks.Flink;
+ NwDereferenceScb( pNpScb );
+ }
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+}
+
+
+VOID
+NwLogoffAndDisconnect(
+ PIRP_CONTEXT pIrpContext,
+ PNONPAGED_SCB pNpScb
+ )
+/*++
+
+Routine Description:
+
+ This routine sends a logoff and disconnects from the name server.
+
+Arguments:
+
+ pIrpContext - A pointer to the current IRP context.
+ pNpScb - A pointer to the server to logoff and disconnect.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PSCB pScb = pNpScb->pScb;
+
+ PAGED_CODE();
+
+ pIrpContext->pNpScb = pNpScb;
+ pIrpContext->pScb = pScb;
+
+ //
+ // Queue ourselves to the SCB, and wait to get to the front to
+ // protect access to server State.
+ //
+
+ NwAppendToQueueAndWait( pIrpContext );
+
+ //
+ // If we are logging out from the preferred server, free the preferred
+ // server reference.
+ //
+
+ if ( pScb != NULL &&
+ pScb->PreferredServer ) {
+ pScb->PreferredServer = FALSE;
+ NwDereferenceScb( pNpScb );
+ }
+
+ //
+ // Nothing to do if we are not connected.
+ //
+
+ if ( pNpScb->State != SCB_STATE_IN_USE &&
+ pNpScb->State != SCB_STATE_LOGIN_REQUIRED ) {
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ return;
+ }
+
+ //
+ // If we timeout then we don't want to go to the bother of
+ // reconnecting.
+ //
+
+ ClearFlag( pIrpContext->Flags, IRP_FLAG_RECONNECTABLE );
+
+ //
+ // Logout and disconnect.
+ //
+
+ if ( pNpScb->State == SCB_STATE_IN_USE ) {
+
+ ExchangeWithWait (
+ pIrpContext,
+ SynchronousResponseCallback,
+ "F",
+ NCP_LOGOUT );
+ }
+
+ ExchangeWithWait (
+ pIrpContext,
+ SynchronousResponseCallback,
+ "D-" ); // Disconnect
+
+ Stats.Sessions--;
+
+ if ( pScb->MajorVersion == 2 ) {
+ Stats.NW2xConnects--;
+ } else if ( pScb->MajorVersion == 3 ) {
+ Stats.NW3xConnects--;
+ } else if ( pScb->MajorVersion == 4 ) {
+ Stats.NW4xConnects--;
+ }
+
+ //
+ // Free the remembered username and password.
+ //
+
+ if ( pScb != NULL && pScb->UserName.Buffer != NULL ) {
+ FREE_POOL( pScb->UserName.Buffer );
+ RtlInitUnicodeString( &pScb->UserName, NULL );
+ RtlInitUnicodeString( &pScb->Password, NULL );
+ }
+
+ pNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ return;
+}
+
+
+VOID
+InitializeAttach (
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Initialize global structures for attaching to servers.
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ PAGED_CODE();
+
+ KeInitializeSpinLock( &ScbSpinLock );
+ InitializeListHead(&ScbQueue);
+}
+
+
+NTSTATUS
+OpenScbSockets(
+ PIRP_CONTEXT pIrpContext,
+ PNONPAGED_SCB pNpScb
+ )
+/*++
+
+Routine Description:
+
+ Open the communications sockets for an SCB.
+
+Arguments:
+
+ pIrpContext - The IRP context pointers for the request in progress.
+
+ pNpScb - The SCB to connect to the network.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+{
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ //
+ // Auto allocate to the server socket.
+ //
+
+ pNpScb->Server.Socket = 0;
+
+ Status = IPX_Open_Socket (pIrpContext, &pNpScb->Server);
+
+ if ( !NT_SUCCESS(Status) ) {
+ return( Status );
+ }
+
+ //
+ // Watchdog Socket is Server.Socket+1
+ //
+
+ pNpScb->WatchDog.Socket = NextSocket( pNpScb->Server.Socket );
+ Status = IPX_Open_Socket ( pIrpContext, &pNpScb->WatchDog );
+
+ if ( !NT_SUCCESS(Status) ) {
+ return( Status );
+ }
+
+ //
+ // Send Socket is WatchDog.Socket+1
+ //
+
+ pNpScb->Send.Socket = NextSocket( pNpScb->WatchDog.Socket );
+ Status = IPX_Open_Socket ( pIrpContext, &pNpScb->Send );
+
+ if ( !NT_SUCCESS(Status) ) {
+ return( Status );
+ }
+
+ //
+ // Echo socket
+ //
+
+ pNpScb->Echo.Socket = NextSocket( pNpScb->Send.Socket );
+ Status = IPX_Open_Socket ( pIrpContext, &pNpScb->Echo );
+
+ if ( !NT_SUCCESS(Status) ) {
+ return( Status );
+ }
+
+ //
+ // Burst socket
+ //
+
+ pNpScb->Burst.Socket = NextSocket( pNpScb->Echo.Socket );
+ Status = IPX_Open_Socket ( pIrpContext, &pNpScb->Burst );
+
+ if ( !NT_SUCCESS(Status) ) {
+ return( Status );
+ }
+
+ return( STATUS_SUCCESS );
+}
+
+NTSTATUS
+DoBinderyLogon(
+ IN PIRP_CONTEXT IrpContext,
+ IN PUNICODE_STRING UserName,
+ IN PUNICODE_STRING Password
+ )
+/*++
+
+Routine Description:
+
+ Performs a bindery based encrypted logon.
+
+ Note: Rcb is held exclusively so that we can access the Logon queue
+ safely.
+
+Arguments:
+
+ pIrpContext - The IRP context pointers for the request in progress.
+
+ UserName - The user name to use to login.
+
+ Password - The password to use to login.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+{
+ PNONPAGED_SCB pNpScb;
+ PSCB pScb;
+ UNICODE_STRING Name;
+ UNICODE_STRING PWord;
+ UCHAR EncryptKey[ENCRYPTION_KEY_SIZE ];
+ NTSTATUS Status;
+ PVOID Buffer;
+ PLOGON Logon = NULL;
+ PWCH OldBuffer;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "DoBinderyLogon...\n", 0);
+
+ //
+ // First get an encryption key.
+ //
+
+ DebugTrace( +0, Dbg, " Get Login key\n", 0);
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "S",
+ NCP_ADMIN_FUNCTION, NCP_GET_LOGIN_KEY );
+
+ pNpScb = IrpContext->pNpScb;
+ pScb = pNpScb->pScb;
+
+ DebugTrace( +0, Dbg, " %X\n", Status);
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nr",
+ EncryptKey, sizeof(EncryptKey) );
+ }
+
+ DebugTrace( +0, Dbg, " %X\n", Status);
+
+ //
+ // Choose a name and password to use to connect to the server. Use
+ // the user supplied if the exist. Otherwise if the server already
+ // has a remembered username use the remembered name. If nothing
+ // else is available use the defaults from logon. Finally, if the
+ // user didn't even logon, use GUEST no password.
+ //
+
+
+ if ( UserName != NULL && UserName->Buffer != NULL ) {
+
+ Name = *UserName;
+
+ } else if ( pScb->UserName.Buffer != NULL ) {
+
+ Name = pScb->UserName;
+
+ } else {
+
+ Logon = FindUser( &pScb->UserUid, FALSE );
+
+ if (Logon != NULL ) {
+ Name = Logon->UserName;
+ } else {
+ ASSERT( FALSE && "No logon record found" );
+ return( STATUS_ACCESS_DENIED );
+ }
+ }
+
+ if ( Password != NULL && Password->Buffer != NULL ) {
+
+ PWord = *Password;
+
+ } else if ( pScb->Password.Buffer != NULL ) {
+
+ PWord = pScb->Password;
+
+ } else {
+
+ if ( Logon == NULL ) {
+ Logon = FindUser( &pScb->UserUid, FALSE );
+ }
+
+ if ( Logon != NULL ) {
+ PWord = Logon->PassWord;
+ } else {
+ ASSERT( FALSE && "No logon record found" );
+ return( STATUS_ACCESS_DENIED );
+ }
+ }
+
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ //
+ // Failed to get an encryption key. Login to server, plain text
+ //
+
+ DebugTrace( +0, Dbg, " Plain Text Login\n", 0);
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "SwUU",
+ NCP_ADMIN_FUNCTION, NCP_PLAIN_TEXT_LOGIN,
+ OT_USER,
+ &Name,
+ &PWord);
+
+ DebugTrace( +0, Dbg, " %X\n", Status);
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N" );
+ }
+
+ DebugTrace( +0, Dbg, " %X\n", Status);
+
+ if ( !NT_SUCCESS( Status )) {
+ return( STATUS_WRONG_PASSWORD);
+ }
+
+ } else if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // We have an encryption key. Get the ObjectId
+ //
+
+ UCHAR Response[ENCRYPTION_KEY_SIZE];
+ UCHAR ObjectId[OBJECT_ID_SIZE];
+ OEM_STRING UOPassword;
+
+ DebugTrace( +0, Dbg, " Query users objectid\n", 0);
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "SwU",
+ NCP_ADMIN_FUNCTION, NCP_QUERY_OBJECT_ID,
+ OT_USER,
+ &Name);
+
+ DebugTrace( +0, Dbg, " %X\n", Status);
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // Save the new address in a local copy so that we can logout.
+ //
+
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nr",
+ ObjectId, OBJECT_ID_SIZE );
+ }
+
+ DebugTrace( +0, Dbg, " %X\n", Status);
+
+ if (!NT_SUCCESS(Status)) {
+ return( STATUS_NO_SUCH_USER );
+ }
+
+ //
+ // Convert the unicode password to uppercase and then the oem
+ // character set.
+ //
+
+ if ( PWord.Length > 0 ) {
+
+ Status = RtlUpcaseUnicodeStringToOemString( &UOPassword, &PWord, TRUE );
+ if (!NT_SUCCESS(Status)) {
+ return( Status );
+ }
+
+ } else {
+ UOPassword.Buffer = "";
+ UOPassword.Length = UOPassword.MaximumLength = 0;
+ }
+
+ RespondToChallenge( ObjectId, &UOPassword, EncryptKey, Response);
+
+ if ( PWord.Length > 0) {
+ RtlFreeAnsiString( &UOPassword );
+ }
+
+ DebugTrace( +0, Dbg, " Encrypted Login\n", 0);
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "SrwU",
+ NCP_ADMIN_FUNCTION, NCP_ENCRYPTED_LOGIN,
+ Response, sizeof(Response),
+ OT_USER,
+ &Name);
+
+ DebugTrace( +0, Dbg, " %X\n", Status);
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // Save the new address in a local copy so that we can logout
+ //
+
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N" );
+ }
+
+ DebugTrace( +0, Dbg, " %X\n", Status);
+
+ if ( !NT_SUCCESS( Status )) {
+
+ //
+ // Special case error mappings.
+ //
+
+ if (( Status == STATUS_UNSUCCESSFUL ) ||
+ ( Status == STATUS_UNEXPECTED_NETWORK_ERROR /* 2.2 servers */ )) {
+ Status = STATUS_WRONG_PASSWORD;
+ }
+
+ if ( Status == STATUS_LOCK_NOT_GRANTED ) {
+ Status = STATUS_ACCOUNT_RESTRICTION; // Bindery locked
+ }
+
+ if ( Status == STATUS_DISK_FULL ) {
+#ifdef QFE_BUILD
+ Status = STATUS_TOO_MANY_SESSIONS;
+#else
+ Status = STATUS_REMOTE_SESSION_LIMIT;
+#endif
+ }
+
+ if ( Status == STATUS_FILE_LOCK_CONFLICT ) {
+ Status = STATUS_SHARING_PAUSED;
+ }
+
+ if ( Status == STATUS_NO_MORE_ENTRIES ) {
+ Status = STATUS_NO_SUCH_USER; // No such object on "Login Object Encrypted" NCP.
+ }
+
+ //
+ // Stupid Netware 4.x servers return a different NCP error for
+ // a disabled account (from intruder lockout) on bindery login,
+ // and nwconvert maps this to a dos error. In this special case,
+ // we'll catch it and map it back.
+ //
+
+ if ( ( IrpContext->pNpScb->pScb->MajorVersion >= 4 ) &&
+ ( Status == 0xC001003B ) ) {
+ Status = STATUS_ACCOUNT_DISABLED;
+ }
+
+ return( Status );
+ }
+
+ } else {
+
+ return( Status );
+
+ }
+
+ //
+ // If the Uid is for the system process then the username must be
+ // in the NtGateway group on the server.
+ //
+
+ if ( IrpContext->Specific.Create.UserUid.QuadPart == DefaultLuid.QuadPart) {
+
+ NTSTATUS Status1 ;
+
+ // IsBinderyObjectInSet?
+ Status1 = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "SwppwU",
+ NCP_ADMIN_FUNCTION, NCP_IS_OBJECT_IN_SET,
+ OT_GROUP,
+ "NTGATEWAY",
+ "GROUP_MEMBERS",
+ OT_USER,
+ &Name);
+
+ if ( !NT_SUCCESS( Status1 ) ) {
+ return STATUS_ACCESS_DENIED;
+ }
+
+ }
+
+ //
+ // Success. Save the username & password for reconnect.
+ //
+
+ //
+ // Setup to free the old user name and password buffer.
+ //
+
+ if ( pScb->UserName.Buffer != NULL ) {
+ OldBuffer = pScb->UserName.Buffer;
+ } else {
+ OldBuffer = NULL;
+ }
+
+ Buffer = ALLOCATE_POOL( NonPagedPool, Name.Length + PWord.Length );
+ if ( Buffer == NULL ) {
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ pScb->UserName.Buffer = Buffer;
+ pScb->UserName.Length = pScb->UserName.MaximumLength = Name.Length;
+ RtlMoveMemory( pScb->UserName.Buffer, Name.Buffer, Name.Length );
+
+ pScb->Password.Buffer = (PWCHAR)((PCHAR)Buffer + Name.Length);
+ pScb->Password.Length = pScb->Password.MaximumLength = PWord.Length;
+ RtlMoveMemory( pScb->Password.Buffer, PWord.Buffer, PWord.Length );
+
+ if ( OldBuffer != NULL ) {
+ FREE_POOL( OldBuffer );
+ }
+ return( Status );
+}
+
+NTSTATUS
+NwAllocateAndInitScb(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING UidServerName OPTIONAL,
+ IN PUNICODE_STRING ServerName OPTIONAL,
+ OUT PSCB *ppScb
+)
+/*++
+
+Routine Description:
+
+ This routine returns a pointer to a newly created SCB. If
+ the UidServerName and ServerName are supplied, the SCB name
+ fields are initialized to this name. Otherwise, the name
+ fields are left blank to be filled in later.
+
+ If UidServerName is provided, ServerName MUST also be provided!!
+
+ The returned SCB is NOT filed in the server prefix table since
+ it might not yet have a name.
+
+Return Value:
+
+ The created SCB or NULL.
+
+--*/
+{
+
+ NTSTATUS Status;
+ PSCB pScb = NULL;
+ PNONPAGED_SCB pNpScb = NULL;
+ USHORT ServerNameLength;
+ PLOGON Logon;
+
+ //
+ // Allocate enough space for a credential munged tree name.
+ //
+
+ pScb = ALLOCATE_POOL ( PagedPool,
+ sizeof( SCB ) +
+ ( ( NDS_TREE_NAME_LEN + MAX_NDS_NAME_CHARS + 2 ) * sizeof( WCHAR ) ) );
+
+ if ( !pScb ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory( pScb, sizeof( SCB ) );
+ RtlInitializeBitMap( &pScb->DriveMapHeader, pScb->DriveMap, MAX_DRIVES );
+
+ //
+ // Initialize pointers to ensure cleanup on error case operates
+ // correctly.
+ //
+
+ if ( UidServerName &&
+ UidServerName->Length ) {
+
+ ServerNameLength = UidServerName->Length + sizeof( WCHAR );
+
+ } else {
+
+ ServerNameLength = ( MAX_SERVER_NAME_LENGTH * sizeof( WCHAR ) ) +
+ ( MAX_UNICODE_UID_LENGTH * sizeof( WCHAR ) ) +
+ ( 2 * sizeof( WCHAR ) );
+
+ }
+
+ pScb->pNpScb = ALLOCATE_POOL ( NonPagedPool,
+ sizeof( NONPAGED_SCB ) + ServerNameLength );
+
+ if ( !pScb->pNpScb ) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ExitWithCleanup;
+ }
+
+ RtlZeroMemory( pScb->pNpScb, sizeof( NONPAGED_SCB ) );
+
+ pNpScb = pScb->pNpScb;
+ pNpScb->pScb = pScb;
+
+ //
+ // If we know the server name, copy it to the allocated buffer.
+ // Append a NULL so that we can use the name a nul-terminated string.
+ //
+
+ pScb->UidServerName.Buffer = (PWCHAR)( pScb->pNpScb + 1 );
+ pScb->UidServerName.MaximumLength = ServerNameLength;
+ pScb->UidServerName.Length = 0;
+
+ RtlInitUnicodeString( &(pNpScb->ServerName), NULL );
+
+ if ( UidServerName &&
+ UidServerName->Length ) {
+
+ RtlCopyUnicodeString ( &pScb->UidServerName, UidServerName );
+ pScb->UidServerName.Buffer[ UidServerName->Length / sizeof( WCHAR ) ] = L'\0';
+
+ pScb->UnicodeUid = pScb->UidServerName;
+ pScb->UnicodeUid.Length = UidServerName->Length -
+ ServerName->Length -
+ sizeof(WCHAR);
+
+ //
+ // Make ServerName point partway down the buffer for UidServerName
+ //
+
+ pNpScb->ServerName.Buffer = (PWSTR)((PUCHAR)pScb->UidServerName.Buffer +
+ UidServerName->Length - ServerName->Length);
+
+ pNpScb->ServerName.MaximumLength = ServerName->Length;
+ pNpScb->ServerName.Length = ServerName->Length;
+
+ }
+
+ pScb->NdsTreeName.MaximumLength = NDS_TREE_NAME_LEN * sizeof( WCHAR );
+ pScb->NdsTreeName.Buffer = (PWCHAR)(pScb + 1);
+
+ pScb->NodeTypeCode = NW_NTC_SCB;
+ pScb->NodeByteSize = sizeof(SCB);
+ InitializeListHead( &pScb->ScbSpecificVcbQueue );
+ InitializeListHead( &pScb->IcbList );
+
+ //
+ // Remember UID of the file creator so we can find the username and
+ // password to use for this Scb when we need it.
+ //
+
+ pScb->UserUid = pIrpContext->Specific.Create.UserUid;
+
+ //
+ // Initialize the non-paged part of the SCB.
+ //
+
+ pNpScb->NodeTypeCode = NW_NTC_SCBNP;
+ pNpScb->NodeByteSize = sizeof(NONPAGED_SCB);
+
+ //
+ // Set the initial SCB reference count.
+ //
+
+ if ( UidServerName &&
+ UidServerName->Length ) {
+
+ Logon = FindUser( &pScb->UserUid, FALSE );
+
+ if (( Logon != NULL) &&
+ (RtlCompareUnicodeString( ServerName, &Logon->ServerName, TRUE ) == 0 )) {
+ pScb->PreferredServer = TRUE;
+ }
+ }
+
+ if ( pScb->PreferredServer ) {
+ pNpScb->Reference = 2;
+ } else {
+ pNpScb->Reference = 1;
+ }
+
+ //
+ // Finish linking the two parts of the Scb together.
+ //
+
+ pNpScb->pScb = pScb;
+
+ KeInitializeSpinLock( &pNpScb->NpScbSpinLock );
+ KeInitializeSpinLock( &pNpScb->NpScbInterLock );
+ InitializeListHead( &pNpScb->Requests );
+
+ RtlFillMemory( &pNpScb->LocalAddress, sizeof(IPXaddress), 0xff);
+
+ pNpScb->State = SCB_STATE_ATTACHING;
+ pNpScb->SequenceNo = 1;
+
+ Status = OpenScbSockets( pIrpContext, pNpScb );
+ if ( !NT_SUCCESS(Status) ) {
+ goto ExitWithCleanup;
+ }
+
+ Status = SetEventHandler (
+ pIrpContext,
+ &pNpScb->Server,
+ TDI_EVENT_RECEIVE_DATAGRAM,
+ &ServerDatagramHandler,
+ pNpScb );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto ExitWithCleanup;
+ }
+
+ Status = SetEventHandler (
+ pIrpContext,
+ &pNpScb->WatchDog,
+ TDI_EVENT_RECEIVE_DATAGRAM,
+ &WatchDogDatagramHandler,
+ pNpScb );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ Status = SetEventHandler (
+ pIrpContext,
+ &pNpScb->Send,
+ TDI_EVENT_RECEIVE_DATAGRAM,
+ &SendDatagramHandler,
+ pNpScb );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ Status = SetEventHandler (
+ pIrpContext,
+ &pNpScb->Echo,
+ TDI_EVENT_RECEIVE_DATAGRAM,
+ &ServerDatagramHandler,
+ pNpScb );
+
+ pNpScb->EchoCounter = 2;
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ Status = SetEventHandler (
+ pIrpContext,
+ &pNpScb->Burst,
+ TDI_EVENT_RECEIVE_DATAGRAM,
+ &ServerDatagramHandler,
+ pNpScb );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ KeQuerySystemTime( &pNpScb->LastUsedTime );
+
+ //
+ // Set burst mode data.
+ //
+
+ pNpScb->BurstRequestNo = 0;
+ pNpScb->BurstSequenceNo = 0;
+
+ if ( ppScb ) {
+ *ppScb = pScb;
+ }
+
+ return STATUS_SUCCESS;
+
+ExitWithCleanup:
+
+ if ( pNpScb != NULL ) {
+
+ IPX_Close_Socket( &pNpScb->Server );
+ IPX_Close_Socket( &pNpScb->WatchDog );
+ IPX_Close_Socket( &pNpScb->Send );
+ IPX_Close_Socket( &pNpScb->Echo );
+ IPX_Close_Socket( &pNpScb->Burst );
+
+ FREE_POOL( pNpScb );
+ }
+
+ FREE_POOL(pScb);
+ return Status;
+
+}
+
+
+BOOLEAN
+NwFindScb(
+ OUT PSCB *Scb,
+ PIRP_CONTEXT IrpContext,
+ PUNICODE_STRING UidServerName,
+ PUNICODE_STRING ServerName
+ )
+/*++
+
+Routine Description:
+
+ This routine returns a pointer to the SCB for the named server.
+ The name is looked up in the SCB table. If it is found, a
+ pointer to the SCB is returned. If none is found an SCB is
+ created and initialized.
+
+ This routine returns with the SCB referenced and the SCB
+ resources held.
+
+Arguments:
+
+ Scb - Returns a pointer to the found / created SCB.
+
+ IrpContext - The IRP context pointers for the request in progress.
+
+ ServerName - The name of the server to find / create.
+
+Return Value:
+
+ TRUE - An old SCB was found.
+
+ FALSE - A new SCB was created, or an attempt to create the SCB failed.
+
+--*/
+{
+ BOOLEAN RcbHeld;
+ PUNICODE_PREFIX_TABLE_ENTRY PrefixEntry;
+ NTSTATUS Status;
+ PSCB pScb = NULL;
+ PNONPAGED_SCB pNpScb = NULL;
+ KIRQL OldIrql;
+ BOOLEAN Success;
+
+ //
+ // Acquire the RCB exclusive to protect the prefix table.
+ // Then lookup the name of this server.
+ //
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ RcbHeld = TRUE;
+ PrefixEntry = RtlFindUnicodePrefix( &NwRcb.ServerNameTable, UidServerName, 0 );
+
+ if ( PrefixEntry != NULL ) {
+
+ PSCB pScb = NULL;
+ PNONPAGED_SCB pNpScb = NULL;
+
+ //
+ // We found the SCB, increment the reference count and return
+ // success.
+ //
+
+ pScb = CONTAINING_RECORD( PrefixEntry, SCB, PrefixEntry );
+ pNpScb = pScb->pNpScb;
+
+ NwReferenceScb( pNpScb );
+
+ //
+ // Release the RCB.
+ //
+
+ NwReleaseRcb( &NwRcb );
+
+ DebugTrace(-1, Dbg, "NwFindScb -> %08lx\n", pScb );
+ *Scb = pScb;
+ return( TRUE );
+ }
+
+ //
+ // We do not have a connection to this server so create the new Scb if requested.
+ //
+
+ if ( BooleanFlagOn( IrpContext->Flags, IRP_FLAG_NOCONNECT ) ) {
+ NwReleaseRcb( &NwRcb );
+ *Scb = NULL;
+ return(FALSE);
+ }
+
+ try {
+
+ Status = NwAllocateAndInitScb( IrpContext,
+ UidServerName,
+ ServerName,
+ &pScb );
+
+ if ( !NT_SUCCESS( Status )) {
+ ExRaiseStatus( Status );
+ }
+
+ ASSERT( pScb != NULL );
+
+ pNpScb = pScb->pNpScb;
+
+ //
+ //*******************************************************************
+ //
+ // From this point on we must not fail to create the Scb because the
+ // another thread will be able to reference the Scb causing severe
+ // problems in the finaly clause or in the other thread.
+ //
+ //*******************************************************************
+ //
+
+ //
+ // Insert this SCB in the global list if SCBs.
+ // If it is the default (i.e. preferred) server, stick it at the
+ // front of the queue so that SelectConnection() will select it
+ // for bindery queries.
+ //
+
+ KeAcquireSpinLock(&ScbSpinLock, &OldIrql);
+
+ if ( pScb->PreferredServer ) {
+ InsertHeadList(&ScbQueue, &pNpScb->ScbLinks);
+ } else {
+ InsertTailList(&ScbQueue, &pNpScb->ScbLinks);
+ }
+
+ KeReleaseSpinLock(&ScbSpinLock, OldIrql);
+
+ //
+ // Insert the name of this server into the prefix table.
+ //
+
+ Success = RtlInsertUnicodePrefix(
+ &NwRcb.ServerNameTable,
+ &pScb->UidServerName,
+ &pScb->PrefixEntry );
+
+#ifdef NWDBG
+ if ( !Success ) {
+ DebugTrace( 0, DEBUG_TRACE_ALWAYS, "Entering duplicate SCB %wZ.\n", &pScb->UidServerName );
+ DbgBreakPoint();
+ }
+#endif
+
+ //
+ // The Scb is now in the prefix table. Any new requests for this
+ // connection can be added to the Scb->Requests queue while we
+ // attach to the server.
+ //
+
+ NwReleaseRcb( &NwRcb );
+ RcbHeld = FALSE;
+
+ //
+ // If we got an error we should have raised an exception.
+ //
+
+ ASSERT( NT_SUCCESS( Status ) );
+
+ } finally {
+
+ if ( !NT_SUCCESS( Status ) || AbnormalTermination() ) {
+ *Scb = NULL;
+ } else {
+ *Scb = pScb;
+ }
+
+ if (RcbHeld) {
+ NwReleaseRcb( &NwRcb );
+ }
+
+ }
+
+ return( FALSE );
+}
+
+NTSTATUS
+QueryServersAddress(
+ PIRP_CONTEXT pIrpContext,
+ PNONPAGED_SCB pNearestScb,
+ PUNICODE_STRING pServerName,
+ IPXaddress *pServerAddress
+ )
+{
+ NTSTATUS Status;
+ UNICODE_STRING NewServer;
+ USHORT CurrChar = 0;
+
+ PAGED_CODE();
+
+ //
+ // Unmunge the server name in case this is a
+ // supplemental credential connect.
+ //
+
+ UnmungeCredentialName( pServerName, &NewServer );
+
+ //
+ // Strip server name trailer, if it exists. If there
+ // was no trailer, the length will end up being exactly
+ // the same as when we started.
+ //
+
+ if (EnableMultipleConnects) {
+
+ while ( (CurrChar < (NewServer.Length / sizeof(WCHAR))) &&
+ NewServer.Buffer[CurrChar] != ((WCHAR)L'#') ) {
+ CurrChar++;
+ }
+ NewServer.Length = CurrChar * sizeof(WCHAR);
+
+ }
+
+ //
+ // Query the bindery of the nearest server looking for
+ // the network address of the target server.
+ //
+
+ DebugTrace( +0, Dbg, "Query servers address\n", 0);
+
+ Status = ExchangeWithWait (
+ pIrpContext,
+ SynchronousResponseCallback,
+ "SwUbp",
+ NCP_ADMIN_FUNCTION, NCP_QUERY_PROPERTY_VALUE,
+ OT_FILESERVER,
+ &NewServer,
+ 1, // Segment number
+ NET_ADDRESS_PROPERTY );
+
+ DebugTrace( +0, Dbg, " %X\n", Status);
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // Save the new address.
+ //
+
+ Status = ParseResponse(
+ pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "Nr",
+ pServerAddress, sizeof(TDI_ADDRESS_IPX) );
+ }
+
+ DebugTrace( +0, Dbg, " %X\n", Status);
+
+ //
+ // Map the server not found error to something sensible.
+ //
+
+ if (( Status == STATUS_NO_MORE_ENTRIES ) ||
+ ( Status == STATUS_VIRTUAL_CIRCUIT_CLOSED ) ||
+ ( Status == NwErrorToNtStatus(ERROR_UNEXP_NET_ERR))) {
+ Status = STATUS_BAD_NETWORK_PATH;
+ }
+
+ return( Status );
+}
+
+
+
+VOID
+TreeConnectScb(
+ IN PSCB Scb
+ )
+/*++
+
+Routine Description:
+
+ This routine increments the tree connect count for a SCB.
+
+Arguments:
+
+ Scb - A pointer to the SCB to connect to.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ Scb->AttachCount++;
+ Scb->OpenFileCount++;
+ NwReferenceScb( Scb->pNpScb );
+ NwReleaseRcb( &NwRcb );
+}
+
+
+NTSTATUS
+TreeDisconnectScb(
+ IN PIRP_CONTEXT IrpContext,
+ IN PSCB Scb
+ )
+/*++
+
+Routine Description:
+
+ This routine decrements the tree connect count for a SCB.
+
+ *** This routine must be called with the RCB resource held.
+
+Arguments:
+
+ Scb - A pointer to the SCB to disconnect.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NTSTATUS Status;
+
+ if ( Scb->AttachCount > 0 ) {
+
+ Scb->AttachCount--;
+ Scb->OpenFileCount--;
+ NwDereferenceScb( Scb->pNpScb );
+
+ Status = STATUS_SUCCESS;
+
+ if ( Scb->OpenFileCount == 0 ) {
+
+ //
+ // Logoff and disconnect from the server now.
+ // Hold on to the SCB lock.
+ // This prevents another thread from trying to access
+ // SCB will this thread is logging off.
+ //
+
+ NwLogoffAndDisconnect( IrpContext, Scb->pNpScb );
+ }
+ } else {
+ // FIXFIX just return success since we are disconnected?
+ Status = STATUS_INVALID_HANDLE;
+ }
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ return( Status );
+}
+
+
+VOID
+ReconnectScb(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PSCB pScb
+ )
+/*++
+
+Routine Description:
+
+ This routine reconnects all the dir handles to a server
+ when reconnecting an SCB.
+
+
+Arguments:
+
+ pScb - A pointer to the SCB that has just been reconnected.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ //
+ // If this is a reconnect, kill all old ICB and VCB handles
+ //
+
+ if ( pScb->VcbCount != 0 ) {
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ //
+ // Invalid all ICBs
+ //
+
+ NwInvalidateAllHandlesForScb( pScb );
+ NwReleaseRcb( &NwRcb );
+
+ //
+ // Acquire new VCB handles for all VCBs.
+ //
+
+ NwReopenVcbHandlesForScb( pIrpContext, pScb );
+ }
+}
diff --git a/private/nw/rdr/cache.c b/private/nw/rdr/cache.c
new file mode 100644
index 000000000..0729b9321
--- /dev/null
+++ b/private/nw/rdr/cache.c
@@ -0,0 +1,698 @@
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ Cache.c
+
+Abstract:
+
+ This module implements internal caching support routines. It does
+ not interact with the cache manager.
+
+Author:
+
+ Manny Weiser [MannyW] 05-Jan-1994
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+
+//
+// The local debug trace level
+//
+
+BOOLEAN
+SpaceForWriteBehind(
+ PNONPAGED_FCB NpFcb,
+ ULONG FileOffset,
+ ULONG BytesToWrite
+ );
+
+BOOLEAN
+OkToReadAhead(
+ PFCB Fcb,
+ IN ULONG FileOffset,
+ IN UCHAR IoType
+ );
+
+#define Dbg (DEBUG_TRACE_CACHE)
+
+//
+// Local procedure prototypes
+//
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, CacheRead )
+#pragma alloc_text( PAGE, SpaceForWriteBehind )
+#pragma alloc_text( PAGE, CacheWrite )
+#pragma alloc_text( PAGE, OkToReadAhead )
+#pragma alloc_text( PAGE, CalculateReadAheadSize )
+#pragma alloc_text( PAGE, FlushCache )
+#pragma alloc_text( PAGE, AcquireFcbAndFlushCache )
+#endif
+
+
+ULONG
+CacheRead(
+ IN PNONPAGED_FCB NpFcb,
+ IN ULONG FileOffset,
+ IN ULONG BytesToRead,
+ IN PVOID UserBuffer
+ , IN BOOLEAN WholeBufferOnly
+ )
+/*++
+
+Routine Description:
+
+ This routine attempts to satisfy a user read from cache. It returns
+ the number of bytes actually copied from cache.
+
+Arguments:
+
+ NpFcb - A pointer the the nonpaged FCB of the file being read.
+
+ FileOffset - The file offset to read.
+
+ BytesToRead - The number of bytes to read.
+
+ UserBuffer - A pointer to the users target buffer.
+
+ WholeBufferOnly - Do a cache read only if we can satisfy the entire
+ read request.
+
+Return Value:
+
+ The number of bytes copied to the user buffer.
+
+--*/
+{
+ ULONG BytesToCopy;
+
+ PAGED_CODE();
+
+ if (DisableReadCache) return 0 ;
+
+ DebugTrace(0, Dbg, "CacheRead...\n", 0 );
+ DebugTrace( 0, Dbg, "FileOffset = %d\n", FileOffset );
+ DebugTrace( 0, Dbg, "ByteCount = %d\n", BytesToRead );
+
+ NwAcquireSharedFcb( NpFcb, TRUE );
+
+ //
+ // If this is a read ahead and it contains some data that the user
+ // could be interested in, copy the interesting data.
+ //
+
+ if ( NpFcb->CacheType == ReadAhead &&
+ NpFcb->CacheDataSize != 0 &&
+ FileOffset >= NpFcb->CacheFileOffset &&
+ FileOffset <= NpFcb->CacheFileOffset + NpFcb->CacheDataSize ) {
+
+ if ( NpFcb->CacheBuffer ) {
+
+ //
+ // Make sure we have a CacheBuffer.
+ //
+
+ BytesToCopy =
+ MIN ( BytesToRead,
+ NpFcb->CacheFileOffset +
+ NpFcb->CacheDataSize - FileOffset );
+
+ if ( WholeBufferOnly && BytesToCopy != BytesToRead ) {
+ NwReleaseFcb( NpFcb );
+ return( 0 );
+ }
+
+ RtlCopyMemory(
+ UserBuffer,
+ NpFcb->CacheBuffer + ( FileOffset - NpFcb->CacheFileOffset ),
+ BytesToCopy );
+
+ DebugTrace(0, Dbg, "CacheRead -> %d\n", BytesToCopy );
+
+ } else {
+
+ ASSERT(FALSE); // we should never get here
+ DebugTrace(0, Dbg, "CacheRead -> %08lx\n", 0 );
+ BytesToCopy = 0;
+ }
+
+
+ } else {
+
+ DebugTrace(0, Dbg, "CacheRead -> %08lx\n", 0 );
+ BytesToCopy = 0;
+ }
+
+ NwReleaseFcb( NpFcb );
+ return( BytesToCopy );
+}
+
+
+BOOLEAN
+SpaceForWriteBehind(
+ PNONPAGED_FCB NpFcb,
+ ULONG FileOffset,
+ ULONG BytesToWrite
+ )
+/*++
+
+Routine Description:
+
+ This routine determines if it is ok to write behind this data to
+ this FCB.
+
+Arguments:
+
+ NpFcb - A pointer the the NONPAGED_FCB of the file being written.
+
+ FileOffset - The file offset to write.
+
+ BytesToWrite - The number of bytes to write.
+
+Return Value:
+
+ The number of bytes copied to the user buffer.
+
+--*/
+{
+ PAGED_CODE();
+
+
+ if ( NpFcb->CacheDataSize == 0 ) {
+ NpFcb->CacheFileOffset = FileOffset;
+ }
+
+ if ( NpFcb->CacheDataSize == 0 && BytesToWrite >= NpFcb->CacheSize ) {
+ return( FALSE );
+ }
+
+ if ( FileOffset - NpFcb->CacheFileOffset + BytesToWrite >
+ NpFcb->CacheSize ) {
+
+ return( FALSE );
+
+ }
+
+ return( TRUE );
+}
+
+
+BOOLEAN
+CacheWrite(
+ IN PIRP_CONTEXT IrpContext OPTIONAL,
+ IN PNONPAGED_FCB NpFcb,
+ IN ULONG FileOffset,
+ IN ULONG BytesToWrite,
+ IN PVOID UserBuffer
+ )
+/*++
+
+Routine Description:
+
+ This routine attempts to satisfy a user write to cache. The write
+ succeeds if it is sequential and fits in the cache buffer.
+
+Arguments:
+
+ IrpContext - A pointer to request parameters.
+
+ NpFcb - A pointer the the NONPAGED_FCB of the file being read.
+
+ FileOffset - The file offset to write.
+
+ BytesToWrite - The number of bytes to write.
+
+ UserBuffer - A pointer to the users source buffer.
+
+Return Value:
+
+ The number of bytes copied to the user buffer.
+
+--*/
+{
+ ULONG CacheSize;
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ if (DisableWriteCache) return FALSE ;
+
+ DebugTrace( +1, Dbg, "CacheWrite...\n", 0 );
+ DebugTrace( 0, Dbg, "FileOffset = %d\n", FileOffset );
+ DebugTrace( 0, Dbg, "ByteCount = %d\n", BytesToWrite );
+
+ if ( NpFcb->Fcb->ShareAccess.SharedWrite ||
+ NpFcb->Fcb->ShareAccess.SharedRead ) {
+
+ DebugTrace( 0, Dbg, "File is not open in exclusive mode\n", 0 );
+ DebugTrace( -1, Dbg, "CacheWrite -> FALSE\n", 0 );
+ return( FALSE );
+ }
+
+ //
+ // Note, If we decide to send data to the server we must be at the front
+ // of the queue before we grab the Fcb exclusive.
+ //
+
+TryAgain:
+
+ NwAcquireExclusiveFcb( NpFcb, TRUE );
+
+ //
+ // Allocate a cache buffer if we don't already have one.
+ //
+
+ if ( NpFcb->CacheBuffer == NULL ) {
+
+ if ( IrpContext == NULL ) {
+ DebugTrace( 0, Dbg, "No cache buffer\n", 0 );
+ DebugTrace( -1, Dbg, "CacheWrite -> FALSE\n", 0 );
+ NwReleaseFcb( NpFcb );
+ return( FALSE );
+ }
+
+ NpFcb->CacheType = WriteBehind;
+
+ if (( IrpContext->pNpScb->SendBurstModeEnabled ) ||
+ ( IrpContext->pNpScb->ReceiveBurstModeEnabled )) {
+
+ CacheSize = IrpContext->pNpScb->MaxReceiveSize;
+
+ } else {
+
+ CacheSize = IrpContext->pNpScb->BufferSize;
+
+ }
+
+ try {
+
+ NpFcb->CacheBuffer = ALLOCATE_POOL_EX( NonPagedPool, CacheSize );
+ NpFcb->CacheSize = CacheSize;
+
+ NpFcb->CacheMdl = ALLOCATE_MDL( NpFcb->CacheBuffer, CacheSize, FALSE, FALSE, NULL );
+
+ if ( NpFcb->CacheMdl == NULL ) {
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ MmBuildMdlForNonPagedPool( NpFcb->CacheMdl );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ if ( NpFcb->CacheBuffer != NULL) {
+ FREE_POOL( NpFcb->CacheBuffer );
+
+ NpFcb->CacheBuffer = NULL;
+ NpFcb->CacheSize = 0;
+
+ }
+
+ DebugTrace( 0, Dbg, "Allocate failed\n", 0 );
+ DebugTrace( -1, Dbg, "CacheWrite -> FALSE\n", 0 );
+
+ NpFcb->CacheDataSize = 0;
+ NwReleaseFcb( NpFcb );
+ return( FALSE );
+ }
+
+ NpFcb->CacheFileOffset = 0;
+ NpFcb->CacheDataSize = 0;
+
+ } else if ( NpFcb->CacheType != WriteBehind ) {
+
+ DebugTrace( -1, Dbg, "CacheWrite not writebehind -> FALSE\n", 0 );
+ NwReleaseFcb( NpFcb );
+ return( FALSE );
+
+ }
+
+ //
+ // If the data is non sequential and non overlapping, flush the
+ // existing cache.
+ //
+
+ if ( NpFcb->CacheDataSize != 0 &&
+ ( FileOffset < NpFcb->CacheFileOffset ||
+ FileOffset > NpFcb->CacheFileOffset + NpFcb->CacheDataSize ) ) {
+
+ //
+ // Release and then AcquireFcbAndFlush() will get us to the front
+ // of the queue before re-acquiring. This avoids potential deadlocks.
+ //
+
+ NwReleaseFcb( NpFcb );
+
+ if ( IrpContext != NULL ) {
+ DebugTrace( 0, Dbg, "Data is not sequential, flushing data\n", 0 );
+
+ status = AcquireFcbAndFlushCache( IrpContext, NpFcb );
+
+ if ( !NT_SUCCESS( status ) ) {
+ ExRaiseStatus( status );
+ }
+
+ }
+
+ DebugTrace( -1, Dbg, "CacheWrite -> FALSE\n", 0 );
+ return( FALSE );
+
+ }
+
+ //
+ // The data is sequential, see if it fits.
+ //
+
+ if ( SpaceForWriteBehind( NpFcb, FileOffset, BytesToWrite ) ) {
+
+ try {
+
+ RtlCopyMemory(
+ NpFcb->CacheBuffer + ( FileOffset - NpFcb->CacheFileOffset ),
+ UserBuffer,
+ BytesToWrite );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Bad user mode buffer in CacheWrite.\n", 0 );
+ DebugTrace(-1, Dbg, "CacheWrite -> FALSE\n", 0 );
+ NwReleaseFcb( NpFcb );
+ return ( FALSE );
+ }
+
+ if ( NpFcb->CacheDataSize <
+ (FileOffset - NpFcb->CacheFileOffset + BytesToWrite) ) {
+
+ NpFcb->CacheDataSize =
+ FileOffset - NpFcb->CacheFileOffset + BytesToWrite;
+
+ }
+
+ DebugTrace(-1, Dbg, "CacheWrite -> TRUE\n", 0 );
+ NwReleaseFcb( NpFcb );
+ return( TRUE );
+
+ } else if ( IrpContext != NULL ) {
+
+ //
+ // The data didn't fit in the cache. If the cache is empty
+ // then its time to return because it never will fit and we
+ // have no stale data. This can happen if this request or
+ // another being processed in parallel flush the cache and
+ // TryAgain.
+ //
+
+ if ( NpFcb->CacheDataSize == 0 ) {
+ DebugTrace(-1, Dbg, "CacheWrite -> FALSE\n", 0 );
+ NwReleaseFcb( NpFcb );
+ return( FALSE );
+ }
+
+ //
+ // The data didn't fit in the cache, flush the cache
+ //
+
+ DebugTrace( 0, Dbg, "Cache is full, flushing data\n", 0 );
+
+ //
+ // We must be at the front of the Queue before writing.
+ //
+
+ NwReleaseFcb( NpFcb );
+
+ status = AcquireFcbAndFlushCache( IrpContext, NpFcb );
+
+ if ( !NT_SUCCESS( status ) ) {
+ ExRaiseStatus( status );
+ }
+
+ //
+ // Now see if it fits in the cache. We need to repeat all
+ // the tests again because two requests can flush the cache at the
+ // same time and the other one of them could have nearly filled it again.
+ //
+
+ goto TryAgain;
+
+ } else {
+ DebugTrace(-1, Dbg, "CacheWrite full -> FALSE\n", 0 );
+ NwReleaseFcb( NpFcb );
+ return( FALSE );
+ }
+}
+
+
+BOOLEAN
+OkToReadAhead(
+ PFCB Fcb,
+ IN ULONG FileOffset,
+ IN UCHAR IoType
+ )
+/*++
+
+Routine Description:
+
+ This routine determines whether the attempted i/o is sequential (so that
+ we can use the cache).
+
+Arguments:
+
+ Fcb - A pointer the the Fcb of the file being read.
+
+ FileOffset - The file offset to read.
+
+Return Value:
+
+ TRUE - The operation is sequential.
+ FALSE - The operation is not sequential.
+
+--*/
+{
+ PAGED_CODE();
+
+ if ( Fcb->NonPagedFcb->CacheType == IoType &&
+ !Fcb->ShareAccess.SharedWrite &&
+ FileOffset == Fcb->LastReadOffset + Fcb->LastReadSize ) {
+
+ DebugTrace(0, Dbg, "Io is sequential\n", 0 );
+ return( TRUE );
+
+ } else {
+
+ DebugTrace(0, Dbg, "Io is not sequential\n", 0 );
+ return( FALSE );
+
+ }
+}
+
+
+ULONG
+CalculateReadAheadSize(
+ IN PIRP_CONTEXT IrpContext,
+ IN PNONPAGED_FCB NpFcb,
+ IN ULONG CacheReadSize,
+ IN ULONG FileOffset,
+ IN ULONG ByteCount
+ )
+/*++
+
+Routine Description:
+
+ This routine determines the amount of data that can be read ahead,
+ and sets up for the read.
+
+ Note: Fcb must be acquired exclusive before calling.
+
+Arguments:
+
+ NpFcb - A pointer the the nonpaged FCB of the file being read.
+
+ FileOffset - The file offset to read.
+
+Return Value:
+
+ The amount of data to read.
+
+--*/
+{
+ ULONG ReadSize;
+ ULONG CacheSize;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "CalculateReadAheadSize\n", 0 );
+
+ if (( IrpContext->pNpScb->SendBurstModeEnabled ) ||
+ ( IrpContext->pNpScb->ReceiveBurstModeEnabled )) {
+
+ CacheSize = IrpContext->pNpScb->MaxReceiveSize;
+
+ } else {
+
+ CacheSize = IrpContext->pNpScb->BufferSize;
+
+ }
+
+ if ( OkToReadAhead( NpFcb->Fcb, FileOffset - CacheReadSize, ReadAhead ) &&
+ ByteCount < CacheSize ) {
+
+ ReadSize = CacheSize;
+
+ } else {
+
+ //
+ // Do not read ahead.
+ //
+
+ DebugTrace( 0, Dbg, "No read ahead\n", 0 );
+ DebugTrace(-1, Dbg, "CalculateReadAheadSize -> %d\n", ByteCount );
+ return ( ByteCount );
+
+ }
+
+ //
+ // Allocate pool for the segment of the read
+ //
+
+ if ( NpFcb->CacheBuffer == NULL ) {
+
+ try {
+
+ NpFcb->CacheBuffer = ALLOCATE_POOL_EX( NonPagedPool, ReadSize );
+ NpFcb->CacheSize = ReadSize;
+
+ NpFcb->CacheMdl = ALLOCATE_MDL( NpFcb->CacheBuffer, ReadSize, FALSE, FALSE, NULL );
+ if ( NpFcb->CacheMdl == NULL ) {
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ MmBuildMdlForNonPagedPool( NpFcb->CacheMdl );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ if ( NpFcb->CacheBuffer != NULL) {
+ FREE_POOL( NpFcb->CacheBuffer );
+
+ NpFcb->CacheBuffer = NULL;
+ }
+
+ NpFcb->CacheSize = 0;
+ NpFcb->CacheDataSize = 0;
+
+ DebugTrace( 0, Dbg, "Failed to allocated buffer\n", 0 );
+ DebugTrace(-1, Dbg, "CalculateReadAheadSize -> %d\n", ByteCount );
+ return( ByteCount );
+ }
+
+ } else {
+ ReadSize = MIN ( NpFcb->CacheSize, ReadSize );
+ }
+
+ DebugTrace(-1, Dbg, "CalculateReadAheadSize -> %d\n", ReadSize );
+ return( ReadSize );
+}
+
+NTSTATUS
+FlushCache(
+ PIRP_CONTEXT IrpContext,
+ PNONPAGED_FCB NpFcb
+ )
+/*++
+
+Routine Description:
+
+ This routine flushes the cache buffer for the NpFcb. The caller must
+ have acquired the FCB exclusive prior to making this call!
+
+Arguments:
+
+ IrpContext - A pointer to request parameters.
+
+ NpFcb - A pointer the the nonpaged FCB of the file to flush.
+
+Return Value:
+
+ The amount of data to read.
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+ PAGED_CODE();
+
+ if ( NpFcb->CacheDataSize != 0 && NpFcb->CacheType == WriteBehind ) {
+
+ LARGE_INTEGER ByteOffset;
+
+ ByteOffset.QuadPart = NpFcb->CacheFileOffset;
+
+ status = DoWrite(
+ IrpContext,
+ ByteOffset,
+ NpFcb->CacheDataSize,
+ NpFcb->CacheBuffer,
+ NpFcb->CacheMdl );
+
+ //
+ // DoWrite leaves us at the head of the queue. The caller
+ // is responsible for dequeueing the irp context appropriately.
+ //
+
+ if ( NT_SUCCESS( status ) ) {
+ NpFcb->CacheDataSize = 0;
+ }
+ }
+
+ return( status );
+}
+
+NTSTATUS
+AcquireFcbAndFlushCache(
+ PIRP_CONTEXT IrpContext,
+ PNONPAGED_FCB NpFcb
+ )
+/*++
+
+Routine Description:
+
+ This routine acquires the FCB exclusive and flushes the cache
+ buffer for the acquired NpFcb.
+
+Arguments:
+
+ IrpContext - A pointer to request parameters.
+
+ NpFcb - A pointer the the nonpaged FCB of the file to flush.
+
+Return Value:
+
+ The amount of data to read.
+
+--*/
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+ PAGED_CODE();
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ NwAcquireExclusiveFcb( NpFcb, TRUE );
+
+ status = FlushCache( IrpContext, NpFcb );
+
+ //
+ // Release the FCB and remove ourselves from the queue.
+ // Frequently the caller will want to grab a resource so
+ // we need to be off the queue then.
+ //
+
+ NwReleaseFcb( NpFcb );
+ NwDequeueIrpContext( IrpContext, FALSE );
+
+ return( status );
+}
diff --git a/private/nw/rdr/callback.c b/private/nw/rdr/callback.c
new file mode 100644
index 000000000..7228813ea
--- /dev/null
+++ b/private/nw/rdr/callback.c
@@ -0,0 +1,190 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ callback.c
+
+Abstract:
+
+ This module implements NCP Response callback routines.
+
+Author:
+
+ Manny Weiser [MannyW] 3-Mar-1993
+
+Revision History:
+
+--*/
+
+#include "procs.h"
+
+#define Dbg (DEBUG_TRACE_EXCHANGE)
+
+#ifdef ALLOC_PRAGMA
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, SynchronousResponseCallback )
+#pragma alloc_text( PAGE1, AsynchResponseCallback )
+#endif
+#endif
+
+#if 0 // Not pageable
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+
+NTSTATUS
+SynchronousResponseCallback (
+ IN PIRP_CONTEXT pIrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR RspData
+ )
+/*++
+
+Routine Description:
+
+ This routine is the callback routine for an NCP which has no
+ return parameters and the caller blocks waiting for a response.
+
+Arguments:
+
+ pIrpContext - A pointer to the context information for this IRP.
+
+ BytesAvailable - Actual number of bytes in the received message.
+
+ RspData - Points to the receive buffer.
+
+Return Value:
+
+ NTSTATUS - Status of the operation.
+
+--*/
+
+{
+ PEPrequest *pNcpHeader;
+ PEPresponse *pNcpResponse;
+
+ DebugTrace( 0, Dbg, "SynchronousResponseCallback\n", 0 );
+ ASSERT( pIrpContext->pNpScb->Requests.Flink == &pIrpContext->NextRequest );
+
+ if ( BytesAvailable == 0) {
+
+ //
+ // No response from server. Status is in pIrpContext->
+ // ResponseParameters.Error
+ //
+
+#ifdef MSWDBG
+ ASSERT( pIrpContext->Event.Header.SignalState == 0 );
+ pIrpContext->DebugValue = 0x103;
+#endif
+ pIrpContext->pOriginalIrp->IoStatus.Status = STATUS_REMOTE_NOT_LISTENING;
+ NwSetIrpContextEvent( pIrpContext );
+
+ return STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ pIrpContext->ResponseLength = BytesAvailable;
+
+ //
+ // Simply copy the data into the response buffer, if it is not
+ // already there (because we used an IRP to receive the data).
+ //
+
+ if ( RspData != pIrpContext->rsp ) {
+ CopyBufferToMdl( pIrpContext->RxMdl, 0, RspData, pIrpContext->ResponseLength );
+ }
+
+ //
+ // Remember the returned error code.
+ //
+
+ pNcpHeader = (PEPrequest *)pIrpContext->rsp;
+ pNcpResponse = (PEPresponse *)(pNcpHeader + 1);
+
+ pIrpContext->ResponseParameters.Error = pNcpResponse->error;
+
+ //
+ // Tell the caller that the response has been received.
+ //
+
+#ifdef MSWDBG
+ ASSERT( pIrpContext->Event.Header.SignalState == 0 );
+ pIrpContext->DebugValue = 0x104;
+#endif
+
+ pIrpContext->pOriginalIrp->IoStatus.Status = STATUS_SUCCESS;
+ pIrpContext->pOriginalIrp->IoStatus.Information = BytesAvailable;
+
+ NwSetIrpContextEvent( pIrpContext );
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+AsynchResponseCallback (
+ IN PIRP_CONTEXT pIrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR RspData
+ )
+/*++
+
+Routine Description:
+
+ This routine is the callback routine for an NCP which has no
+ return parameters and the caller DOES NOT BLOCK waiting for a
+ response.
+
+Arguments:
+
+ pIrpContext - A pointer to the context information for this IRP.
+
+ BytesAvailable - Actual number of bytes in the received message.
+
+ RspData - Points to the receive buffer.
+
+Return Value:
+
+ NTSTATUS - Status of the operation.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ if ( BytesAvailable == 0) {
+
+ //
+ // No response from server. Status is in pIrpContext->
+ // ResponseParameters.Error
+ //
+
+ Status = STATUS_REMOTE_NOT_LISTENING;
+
+ } else {
+
+ if ( ((PNCP_RESPONSE)RspData)->Status != 0 ) {
+
+ Status = STATUS_LINK_FAILED;
+
+ } else {
+
+ Status = NwErrorToNtStatus( ((PNCP_RESPONSE)RspData)->Error );
+
+ }
+ }
+
+ //
+ // We're done with this request. Dequeue the IRP context from
+ // SCB and complete the request.
+ //
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ NwCompleteRequest( pIrpContext, Status );
+
+ return STATUS_SUCCESS;
+}
+
+
diff --git a/private/nw/rdr/cleanup.c b/private/nw/rdr/cleanup.c
new file mode 100644
index 000000000..2eb22afcd
--- /dev/null
+++ b/private/nw/rdr/cleanup.c
@@ -0,0 +1,591 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ cleanup.c
+
+Abstract:
+
+ This module implements the file cleanup routine for Netware Redirector.
+
+Author:
+
+ Manny Weiser (mannyw) 9-Feb-1993
+
+Revision History:
+
+--*/
+
+#include "procs.h"
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_CLEANUP)
+
+//
+// local procedure prototypes
+//
+
+NTSTATUS
+NwCommonCleanup (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+
+NTSTATUS
+NwCleanupRcb (
+ IN PIRP Irp,
+ IN PRCB Rcb
+ );
+
+NTSTATUS
+NwCleanupScb (
+ IN PIRP Irp,
+ IN PSCB Scb
+ );
+
+NTSTATUS
+NwCleanupIcb (
+ IN PIRP_CONTEXT IrpContext,
+ IN PIRP Irp,
+ IN PICB Icb
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwFsdCleanup )
+#pragma alloc_text( PAGE, NwCommonCleanup )
+#pragma alloc_text( PAGE, NwCleanupScb )
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, NwCleanupIcb )
+#endif
+
+#endif
+
+#if 0 // Not pageable
+
+NwCleanupRcb
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+
+NTSTATUS
+NwFsdCleanup (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine implements the FSD part of the NtCleanupFile API calls.
+
+Arguments:
+
+ DeviceObject - Supplies the device object to use.
+
+ Irp - Supplies the Irp being processed
+
+Return Value:
+
+ NTSTATUS - The Fsd status for the Irp
+
+--*/
+
+{
+ NTSTATUS status;
+ PIRP_CONTEXT IrpContext = NULL;
+ BOOLEAN TopLevel;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwFsdCleanup\n", 0);
+
+ //
+ // Call the common cleanup routine.
+ //
+
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ FsRtlEnterFileSystem();
+
+ try {
+
+ IrpContext = AllocateIrpContext( Irp );
+ status = NwCommonCleanup( IrpContext );
+
+ } except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
+
+ if ( IrpContext == NULL ) {
+
+ //
+ // If we couldn't allocate an irp context, just complete
+ // irp without any fanfare.
+ //
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
+
+ } else {
+
+ //
+ // We had some trouble trying to perform the requested
+ // operation, so we'll abort the I/O request with
+ // the error status that we get back from the
+ // execption code.
+ //
+
+ status = NwProcessException( IrpContext, GetExceptionCode() );
+ }
+ }
+
+ if ( IrpContext ) {
+ NwCompleteRequest( IrpContext, status );
+ }
+
+ if ( TopLevel ) {
+ NwSetTopLevelIrp( NULL );
+ }
+ FsRtlExitFileSystem();
+
+ //
+ // Return to our caller.
+ //
+
+ DebugTrace(-1, Dbg, "NwFsdCleanup -> %08lx\n", status );
+ return status;
+}
+
+
+NTSTATUS
+NwCommonCleanup (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This is the common routine for cleaning up a file.
+
+Arguments:
+
+ IrpContext - Supplies the Irp to process
+
+Return Value:
+
+ NTSTATUS - the return status for the operation
+
+--*/
+
+{
+ PIRP Irp;
+ PIO_STACK_LOCATION irpSp;
+ NTSTATUS status;
+ NODE_TYPE_CODE nodeTypeCode;
+ PVOID fsContext, fsContext2;
+
+ PAGED_CODE();
+
+ Irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ DebugTrace(+1, Dbg, "NwCommonCleanup\n", 0);
+ DebugTrace( 0, Dbg, "IrpContext = %08lx\n", (ULONG)IrpContext);
+ DebugTrace( 0, Dbg, "Irp = %08lx\n", (ULONG)Irp);
+ DebugTrace( 0, Dbg, "FileObject = %08lx\n", (ULONG)irpSp->FileObject);
+
+ try {
+
+ //
+ // Get the a referenced pointer to the node and make sure it is
+ // not being closed.
+ //
+
+ if ((nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
+ &fsContext,
+ &fsContext2 )) == NTC_UNDEFINED) {
+
+ DebugTrace(0, Dbg, "The file is disconnected\n", 0);
+
+ status = STATUS_INVALID_HANDLE;
+
+ DebugTrace(-1, Dbg, "NwCommonCleanup -> %08lx\n", status );
+ try_return( NOTHING );
+ }
+
+ //
+ // Decide how to handle this IRP.
+ //
+
+ switch (nodeTypeCode) {
+
+ case NW_NTC_RCB: // Cleanup the file system
+
+ status = NwCleanupRcb( Irp, (PRCB)fsContext2 );
+ break;
+
+ case NW_NTC_SCB: // Cleanup the server control block
+
+ status = NwCleanupScb( Irp, (PSCB)fsContext2 );
+ break;
+
+ case NW_NTC_ICB: // Cleanup the remote file
+ case NW_NTC_ICB_SCB: // Cleanup the server
+
+ status = NwCleanupIcb( IrpContext, Irp, (PICB)fsContext2 );
+ break;
+
+#ifdef NWDBG
+ default:
+
+ //
+ // This is not one of ours.
+ //
+
+ KeBugCheck( RDR_FILE_SYSTEM );
+ break;
+#endif
+
+ }
+
+ try_exit: NOTHING;
+
+ } finally {
+
+ DebugTrace(-1, Dbg, "NwCommonCleanup -> %08lx\n", status);
+
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+NwCleanupRcb (
+ IN PIRP Irp,
+ IN PRCB Rcb
+ )
+
+/*++
+
+Routine Description:
+
+ The routine cleans up a RCB.
+
+ This routine grabs a spinlock so must not be paged out while running.
+
+ Do not reference the code section since this will start the timer and
+ we don't stop it in the rcb close path.
+
+Arguments:
+
+ Irp - Supplies the IRP associated with the cleanup.
+
+ Rcb - Supplies the RCB for MSFS.
+
+Return Value:
+
+ NTSTATUS - An appropriate completion status
+
+--*/
+
+{
+ NTSTATUS status;
+ PIO_STACK_LOCATION irpSp;
+ PFILE_OBJECT closingFileObject;
+ BOOLEAN OwnRcb;
+ BOOLEAN OwnMessageLock = FALSE;
+ KIRQL OldIrql;
+ PLIST_ENTRY listEntry, nextListEntry;
+ PIRP_CONTEXT pTestIrpContext;
+ PIO_STACK_LOCATION pTestIrpSp;
+ PIRP pTestIrp;
+
+ DebugTrace(+1, Dbg, "NwCleanupRcb...\n", 0);
+
+ //
+ // Now acquire exclusive access to the Rcb
+ //
+
+ NwAcquireExclusiveRcb( Rcb, TRUE );
+ OwnRcb = TRUE;
+
+ status = STATUS_SUCCESS;
+
+ try {
+
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ IoRemoveShareAccess( irpSp->FileObject,
+ &Rcb->ShareAccess );
+
+ NwReleaseRcb( Rcb );
+ OwnRcb = FALSE;
+
+ closingFileObject = irpSp->FileObject;
+
+
+ //
+ // Walk the message queue and complete any outstanding Get Message IRPs
+ //
+
+ KeAcquireSpinLock( &NwMessageSpinLock, &OldIrql );
+ OwnMessageLock = TRUE;
+
+ for ( listEntry = NwGetMessageList.Flink;
+ listEntry != &NwGetMessageList;
+ listEntry = nextListEntry ) {
+
+ nextListEntry = listEntry->Flink;
+
+ //
+ // If the file object of the queued request, matches the file object
+ // that is being closed, remove the IRP from the queue, and
+ // complete it with an error.
+ //
+
+ pTestIrpContext = CONTAINING_RECORD( listEntry, IRP_CONTEXT, NextRequest );
+ pTestIrp = pTestIrpContext->pOriginalIrp;
+ pTestIrpSp = IoGetCurrentIrpStackLocation( pTestIrp );
+
+ if ( pTestIrpSp->FileObject == closingFileObject ) {
+ RemoveEntryList( listEntry );
+
+ IoAcquireCancelSpinLock( &pTestIrp->CancelIrql );
+ IoSetCancelRoutine( pTestIrp, NULL );
+ IoReleaseCancelSpinLock( pTestIrp->CancelIrql );
+
+ NwCompleteRequest( pTestIrpContext, STATUS_INVALID_HANDLE );
+ }
+
+ }
+
+ KeReleaseSpinLock( &NwMessageSpinLock, OldIrql );
+ OwnMessageLock = FALSE;
+
+ } finally {
+
+ if ( OwnRcb ) {
+ NwReleaseRcb( Rcb );
+ }
+
+ if ( OwnMessageLock ) {
+ KeReleaseSpinLock( &NwMessageSpinLock, OldIrql );
+ }
+
+ DebugTrace(-1, Dbg, "NwCleanupRcb -> %08lx\n", status);
+ }
+
+ //
+ // And return to our caller
+ //
+
+ return status;
+}
+
+
+NTSTATUS
+NwCleanupScb (
+ IN PIRP Irp,
+ IN PSCB Scb
+ )
+
+/*++
+
+Routine Description:
+
+ The routine cleans up an ICB.
+
+Arguments:
+
+ Irp - Supplies the IRP associated with the cleanup.
+
+ Scb - Supplies the SCB to cleanup.
+
+Return Value:
+
+ NTSTATUS - An appropriate completion status
+
+--*/
+{
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwCleanupScb...\n", 0);
+
+ Status = STATUS_SUCCESS;
+
+ try {
+
+ //
+ // Ensure that this SCB is still active.
+ //
+
+ NwVerifyScb( Scb );
+
+ //
+ // Cancel any IO on this SCB.
+ //
+
+ } finally {
+
+ DebugTrace(-1, Dbg, "NwCleanupScb -> %08lx\n", Status);
+ }
+
+ //
+ // And return to our caller
+ //
+
+ return Status;
+}
+
+
+NTSTATUS
+NwCleanupIcb (
+ IN PIRP_CONTEXT pIrpContext,
+ IN PIRP Irp,
+ IN PICB Icb
+ )
+
+/*++
+
+Routine Description:
+
+ The routine cleans up an ICB.
+
+Arguments:
+
+ Irp - Supplies the IRP associated with the cleanup.
+
+ Rcb - Supplies the RCB for MSFS.
+
+Return Value:
+
+ NTSTATUS - An appropriate completion status
+
+--*/
+{
+ NTSTATUS Status;
+ PNONPAGED_FCB NpFcb;
+
+ DebugTrace(+1, Dbg, "NwCleanupIcb...\n", 0);
+
+ Status = STATUS_SUCCESS;
+
+ try {
+
+ Icb->State = ICB_STATE_CLEANED_UP;
+
+ //
+ // Cancel any IO on this ICB.
+ //
+
+#if 0
+ // HACKHACK
+
+ if ( Icb->SuperType.Fcb->NodeTypeCode == NW_NTC_DCB ) {
+
+ PLIST_ENTRY listEntry;
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ for ( listEntry = FnList.Flink; listEntry != &FnList ; listEntry = listEntry->Flink ) {
+
+ PIRP_CONTEXT IrpContext;
+
+ IrpContext = CONTAINING_RECORD( listEntry, IRP_CONTEXT, NextRequest );
+
+ if ( IrpContext->Icb == Icb ) {
+
+ PIRP irp = pIrpContext->pOriginalIrp;
+
+ IoAcquireCancelSpinLock( &irp->CancelIrql );
+ IoSetCancelRoutine( irp, NULL );
+ IoReleaseCancelSpinLock( irp->CancelIrql );
+
+ RemoveEntryList( &IrpContext->NextRequest );
+ NwCompleteRequest( IrpContext, STATUS_NOT_SUPPORTED );
+ break;
+ }
+ }
+
+ NwReleaseRcb( &NwRcb );
+ }
+#endif
+
+ //
+ // If this is a remote file clear all the cache garbage.
+ //
+
+ if ( Icb->NodeTypeCode == NW_NTC_ICB ) {
+
+ if ( Icb->HasRemoteHandle ) {
+
+ //
+ // Free all of file lock structures that are still hanging around.
+ //
+
+ pIrpContext->pScb = Icb->SuperType.Fcb->Scb;
+ pIrpContext->pNpScb = Icb->SuperType.Fcb->Scb->pNpScb;
+
+ NwFreeLocksForIcb( pIrpContext, Icb );
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ //
+ //
+ //
+ // If this is an executable opened over the net, then
+ // its possible that the executables image section
+ // might still be kept open.
+ //
+ // Ask MM to flush the section closed. This will fail
+ // if the executable in question is still running.
+ //
+
+ NpFcb = Icb->SuperType.Fcb->NonPagedFcb;
+ MmFlushImageSection(&NpFcb->SegmentObject, MmFlushForWrite);
+
+ //
+ // There is also a possiblity that there is a user section
+ // open on this file, in which case we need to force the
+ // section closed to make sure that they are cleaned up.
+ //
+
+ MmForceSectionClosed(&NpFcb->SegmentObject, TRUE);
+
+ }
+
+ //
+ // Remove shared access.
+ //
+
+ IoRemoveShareAccess(
+ Icb->FileObject,
+ &Icb->SuperType.Fcb->ShareAccess );
+ }
+
+
+ } finally {
+
+ DebugTrace(-1, Dbg, "NwCleanupIcb -> %08lx\n", Status);
+ }
+
+ //
+ // And return to our caller
+ //
+
+ return Status;
+}
+
diff --git a/private/nw/rdr/close.c b/private/nw/rdr/close.c
new file mode 100644
index 000000000..7caddbd04
--- /dev/null
+++ b/private/nw/rdr/close.c
@@ -0,0 +1,571 @@
+/*++
+
+Copyright (c) 1992-4 Microsoft Corporation
+
+Module Name:
+
+ Close.c
+
+Abstract:
+
+ This module implements the File Close routine for the NetWare
+ redirector called by the dispatch driver.
+
+Author:
+
+ Colin Watson [ColinW] 19-Dec-1992
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+
+//
+// The local debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_CLOSE)
+
+//
+// Local procedure prototypes
+//
+
+NTSTATUS
+NwCommonClose (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+NwCloseRcb (
+ IN PIRP_CONTEXT IrpContext,
+ IN PRCB Rcb
+ );
+
+NTSTATUS
+NwCloseIcb (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwFsdClose )
+#pragma alloc_text( PAGE, NwCommonClose )
+#pragma alloc_text( PAGE, NwCloseRcb )
+#pragma alloc_text( PAGE, NwCloseIcb )
+#endif
+
+
+NTSTATUS
+NwFsdClose (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine implements the FSD part of Close.
+
+Arguments:
+
+ DeviceObject - Supplies the redirector device object.
+
+ Irp - Supplies the Irp being processed
+
+Return Value:
+
+ NTSTATUS - The FSD status for the IRP
+
+--*/
+
+{
+ NTSTATUS Status;
+ PIRP_CONTEXT IrpContext = NULL;
+ BOOLEAN TopLevel;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwFsdClose\n", 0);
+
+ //
+ // Call the common Close routine
+ //
+
+ FsRtlEnterFileSystem();
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ try {
+
+ IrpContext = AllocateIrpContext( Irp );
+ Status = NwCommonClose( IrpContext );
+
+ } except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
+
+ if ( IrpContext == NULL ) {
+
+ //
+ // If we couldn't allocate an irp context, just complete
+ // irp without any fanfare.
+ //
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
+
+ } else {
+
+ //
+ // We had some trouble trying to perform the requested
+ // operation, so we'll abort the I/O request with
+ // the error status that we get back from the
+ // execption code.
+ //
+
+ Status = NwProcessException( IrpContext, GetExceptionCode() );
+ }
+
+ }
+
+ if ( IrpContext ) {
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwCompleteRequest( IrpContext, Status );
+ }
+
+ if ( TopLevel ) {
+ NwSetTopLevelIrp( NULL );
+ }
+ FsRtlExitFileSystem();
+
+ //
+ // And return to our caller
+ //
+
+ DebugTrace(-1, Dbg, "NwFsdClose -> %08lx\n", Status);
+
+ UNREFERENCED_PARAMETER( DeviceObject );
+
+ return Status;
+}
+
+
+NTSTATUS
+NwCommonClose (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This is the common routine for closing a file.
+
+Arguments:
+
+ IrpContext - Supplies the Irp to process
+
+Return Value:
+
+ NTSTATUS - the return status for the operation
+
+--*/
+
+{
+ PIRP Irp;
+ PIO_STACK_LOCATION irpSp;
+ NTSTATUS status;
+ NODE_TYPE_CODE nodeTypeCode;
+ PVOID fsContext, fsContext2;
+
+ PAGED_CODE();
+
+ Irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ DebugTrace(+1, Dbg, "NwCommonClose\n", 0);
+ DebugTrace( 0, Dbg, "IrpContext = %08lx\n", (ULONG)IrpContext);
+ DebugTrace( 0, Dbg, "Irp = %08lx\n", (ULONG)Irp);
+ DebugTrace( 0, Dbg, "FileObject = %08lx\n", (ULONG)irpSp->FileObject);
+ try {
+
+ //
+ // Get the a referenced pointer to the node and make sure it is
+ // not being closed.
+ //
+
+ if ((nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
+ &fsContext,
+ &fsContext2 )) == NTC_UNDEFINED) {
+
+ DebugTrace(0, Dbg, "The file is disconnected\n", 0);
+
+ status = STATUS_INVALID_HANDLE;
+
+ DebugTrace(-1, Dbg, "NwCommonClose -> %08lx\n", status );
+ try_return( NOTHING );
+ }
+
+ //
+ // Decide how to handle this IRP.
+ //
+
+ switch (nodeTypeCode) {
+
+
+ case NW_NTC_RCB: // Close the file system
+
+ status = NwCloseRcb( IrpContext, (PRCB)fsContext2 );
+ status = STATUS_SUCCESS;
+ break;
+
+ case NW_NTC_ICB: // Close the remote file
+ case NW_NTC_ICB_SCB: // Close the SCB
+
+ status = NwCloseIcb( IrpContext, (PICB)fsContext2 );
+ NwDereferenceUnlockableCodeSection ();
+ break;
+
+#ifdef NWDBG
+ default:
+
+ //
+ // This is not one of ours.
+ //
+
+ KeBugCheck( RDR_FILE_SYSTEM );
+ break;
+#endif
+
+ }
+
+ try_exit: NOTHING;
+
+ } finally {
+
+ //
+ // Just in-case this handle was the last one before we unload.
+ //
+
+ NwUnlockCodeSections(TRUE);
+
+ DebugTrace(-1, Dbg, "NwCommonClose -> %08lx\n", status);
+
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+NwCloseRcb (
+ IN PIRP_CONTEXT IrpContext,
+ IN PRCB Rcb
+ )
+
+/*++
+
+Routine Description:
+
+ The routine cleans up a RCB.
+
+Arguments:
+
+ IrpContext - Supplies the IRP context pointers for this close.
+
+ Rcb - Supplies the RCB for MSFS.
+
+Return Value:
+
+ NTSTATUS - An appropriate completion status
+
+--*/
+
+{
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwCloseRcb...\n", 0);
+
+ //
+ // Now acquire exclusive access to the Rcb
+ //
+
+ NwAcquireExclusiveRcb( Rcb, TRUE );
+
+ status = STATUS_SUCCESS;
+ --Rcb->OpenCount;
+
+ NwReleaseRcb( Rcb );
+
+ DebugTrace(-1, Dbg, "MsCloseRcb -> %08lx\n", status);
+
+ //
+ // And return to our caller
+ //
+
+ return status;
+}
+
+
+NTSTATUS
+NwCloseIcb (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb
+ )
+
+/*++
+
+Routine Description:
+
+ The routine cleans up an ICB.
+
+Arguments:
+
+ IrpContext - Supplies the IRP context pointers for this close.
+
+ Rcb - Supplies the RCB for MSFS.
+
+Return Value:
+
+ NTSTATUS - An appropriate completion status
+
+--*/
+{
+ NTSTATUS Status;
+ PNONPAGED_SCB pNpScb;
+ PVCB Vcb;
+ PFCB Fcb;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwCloseIcb...\n", 0);
+
+ ASSERT( Icb->State == ICB_STATE_CLEANED_UP ||
+ Icb->State == ICB_STATE_CLOSE_PENDING );
+
+ //
+ // If this is a remote file close the remote handle.
+ //
+
+ Status = STATUS_SUCCESS;
+ IrpContext->Icb = Icb;
+ Fcb = Icb->SuperType.Fcb;
+
+ if (( Icb->NodeTypeCode == NW_NTC_ICB ) ||
+ ( Icb->NodeTypeCode == NW_NTC_DCB )) {
+
+ pNpScb = Fcb->Scb->pNpScb;
+ IrpContext->pNpScb = pNpScb;
+
+ if ( Icb->HasRemoteHandle ) {
+
+ Vcb = Fcb->Vcb;
+
+ //
+ // Dump the write behind cache.
+ //
+
+ Status = AcquireFcbAndFlushCache( IrpContext, Fcb->NonPagedFcb );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ IoRaiseInformationalHardError(
+ STATUS_LOST_WRITEBEHIND_DATA,
+ &Fcb->FullFileName,
+ (PKTHREAD)IrpContext->pOriginalIrp->Tail.Overlay.Thread );
+ }
+
+ //
+ // Is this a print job?
+ // Icb->IsPrintJob will be false if a 16 bit app is
+ // responsible for sending the job.
+ //
+
+ if ( FlagOn( Vcb->Flags, VCB_FLAG_PRINT_QUEUE ) &&
+ Icb->IsPrintJob ) {
+
+ //
+ // Yes, did we print?
+ //
+
+ if ( Icb->ActuallyPrinted ) {
+
+ //
+ // Yes. Send a close file and start queue job NCP
+ //
+
+ Status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "Sdw",
+ NCP_ADMIN_FUNCTION, NCP_CLOSE_FILE_AND_START_JOB,
+ Vcb->Specific.Print.QueueId,
+ Icb->JobId );
+ } else {
+
+ //
+ // No. Cancel the job.
+ //
+
+ Status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "Sdw",
+ NCP_ADMIN_FUNCTION, NCP_CLOSE_FILE_AND_CANCEL_JOB,
+ Vcb->Specific.Print.QueueId,
+ Icb->JobId );
+ }
+
+ } else {
+
+ if ( Icb->SuperType.Fcb->NodeTypeCode != NW_NTC_DCB ) {
+
+ //
+ // No, send a close file NCP.
+ //
+
+ ASSERT( IrpContext->pTdiStruct == NULL );
+
+ Status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "F-r",
+ NCP_CLOSE,
+ Icb->Handle, sizeof( Icb->Handle ) );
+
+ // If this is in the long file name space and
+ // the last access flag has been set, we have to
+ // reset the last access time _after_ closing the file.
+
+ if ( Icb->UserSetLastAccessTime &&
+ BooleanFlagOn( Fcb->Flags, FCB_FLAGS_LONG_NAME ) ) {
+
+ Status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "LbbWD_W_bDbC",
+ NCP_LFN_SET_INFO,
+ Fcb->Vcb->Specific.Disk.LongNameSpace,
+ Fcb->Vcb->Specific.Disk.LongNameSpace,
+ SEARCH_ALL_FILES,
+ LFN_FLAG_SET_INFO_LASTACCESS_DATE,
+ 28,
+ Fcb->LastAccessDate,
+ 8,
+ Fcb->Vcb->Specific.Disk.VolumeNumber,
+ Fcb->Vcb->Specific.Disk.Handle,
+ 0,
+ &Fcb->RelativeFileName );
+ }
+
+ //
+ // If someone set the shareable bit, then
+ // see if we can send the NCP over the wire (all
+ // instances of the file need to be closed).
+ //
+
+ if ( BooleanFlagOn( Fcb->Flags, FCB_FLAGS_LAZY_SET_SHAREABLE ) ) {
+ LazySetShareable( IrpContext, Icb, Fcb );
+ }
+
+ } else {
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "Sb",
+ NCP_DIR_FUNCTION, NCP_DEALLOCATE_DIR_HANDLE,
+ Icb->Handle[0]);
+ }
+
+ }
+
+ Icb->HasRemoteHandle = FALSE;
+ }
+
+ } else {
+
+ pNpScb = Icb->SuperType.Scb->pNpScb;
+ IrpContext->pNpScb = pNpScb;
+ IrpContext->pScb = pNpScb->pScb;
+
+ if ( Icb->HasRemoteHandle ) {
+
+ //
+ // If we have a remote handle this is a file stream ICB. We
+ // need to close the remote handle. The exchange will get us
+ // to the head of the queue to protect the SCB state.
+ //
+
+ Status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "F-r",
+ NCP_CLOSE,
+ Icb->Handle, sizeof( Icb->Handle ) );
+
+ Icb->HasRemoteHandle = FALSE;
+
+ pNpScb->pScb->OpenNdsStreams--;
+
+ ASSERT( pNpScb->pScb->MajorVersion > 3 );
+
+ //
+ // Do we need to unlicense this connection?
+ //
+
+ if ( ( pNpScb->pScb->UserName.Length == 0 ) &&
+ ( pNpScb->pScb->VcbCount == 0 ) &&
+ ( pNpScb->pScb->OpenNdsStreams == 0 ) ) {
+ NdsUnlicenseConnection( IrpContext );
+ }
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ }
+
+ }
+
+ if ( Icb->Pid != INVALID_PID ) {
+
+ //
+ // This ICB was involved in a search, send the end job,
+ // then free the PID.
+ //
+
+ NwUnmapPid( Icb->Pid, IrpContext );
+ }
+
+ //
+ // Update the time the SCB was last used.
+ //
+
+ KeQuerySystemTime( &pNpScb->LastUsedTime );
+
+ //
+ // Wait the SCB queue. We do this now since NwDeleteIcb may cause
+ // a packet to be sent by this thread (from NwCleanupVcb()) while
+ // holding the RCB. To eliminate this potential source of deadlock,
+ // queue this IrpContext to the SCB queue before acquiring the RCB.
+ //
+ // Also, we mark this IRP context not reconnectable, since the
+ // reconnect logic, will try to acquire the RCB.
+ //
+
+ NwAppendToQueueAndWait( IrpContext );
+ ClearFlag( IrpContext->Flags, IRP_FLAG_RECONNECTABLE );
+
+ //
+ // Delete the ICB.
+ //
+
+ NwDeleteIcb( IrpContext, Icb );
+
+ //
+ // And return to our caller
+ //
+
+ DebugTrace(-1, Dbg, "NwCloseIcb -> %08lx\n", Status);
+ return Status;
+}
diff --git a/private/nw/rdr/const.h b/private/nw/rdr/const.h
new file mode 100644
index 000000000..45e837360
--- /dev/null
+++ b/private/nw/rdr/const.h
@@ -0,0 +1,166 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ Const.h
+
+Abstract:
+
+ This module declares the obal data used by the NetWare redirector
+ file system.
+
+Author:
+
+ Colin Watson [ColinW] 14-Jan-1993
+
+Revision History:
+
+--*/
+
+#ifndef _NWCONST_
+#define _NWCONST_
+
+// Number of spare stack locations required in Irp's submitted to this
+// filesystem
+
+#define NWRDR_IO_STACKSIZE 2
+
+//
+// NT uses a system time measured in 100 nanosecnd intervals. define
+// convenient constants for setting the timer.
+//
+
+#define MICROSECONDS 10
+#define MILLISECONDS MICROSECONDS*1000
+#define SECONDS MILLISECONDS*1000
+
+#define NwOneSecond 10000000
+
+//
+// Default number of times to retranmit a packet before giving up
+// on waiting for a response.
+//
+
+#define DEFAULT_RETRY_COUNT 10
+
+//
+// Amount of time, in seconds, an idle SCB or VCB should be kept around before
+// being cleaned up.
+//
+
+#define DORMANT_SCB_KEEP_TIME 120
+#define DORMANT_VCB_KEEP_TIME 120
+
+//
+// Largest netware file name
+//
+
+#define NW_MAX_FILENAME_LENGTH 255
+#define NW_MAX_FILENAME_SIZE ( NW_MAX_FILENAME_LENGTH * sizeof(WCHAR) )
+
+//
+// Default frequency for running the scavenger (in 1/18th second ticks)
+// Approx once per minute.
+//
+
+#define DEFAULT_SCAVENGER_TICK_RUN_COUNT 1100
+
+//
+// Size of the drive map table. With room for 26 letter connections,
+// and 10 LPT connections.
+//
+
+#define MAX_DISK_REDIRECTIONS 26
+#define MAX_LPT_REDIRECTIONS 10
+#define DRIVE_MAP_TABLE_SIZE (MAX_DISK_REDIRECTIONS + MAX_LPT_REDIRECTIONS)
+
+//
+// The size of the largest packet we can generate, rounded up to DWORD
+// size. This longest packet is a long name query.
+//
+
+#define MAX_SEND_DATA 256+32
+//
+// The size of the largest non READ packet we can receive, rounded up to DWORD
+// size. This longest packet is read queue job list of 250 jobs
+//
+
+#define MAX_RECV_DATA 544+32
+
+//
+// Best guess at max packet size, if the transport can't tell us.
+// Pick the largest value that will work on any net.
+//
+
+#define DEFAULT_PACKET_SIZE 512
+
+//
+// How close we want to get to true MTU of a connection
+//
+
+#define BURST_PACKET_SIZE_TOLERANCE 8
+
+//
+// Default tick count, in case the transport won't fess up.
+//
+
+#define DEFAULT_TICK_COUNT 2
+
+//
+// Maximum number of times to retry SAP broadcast if we get no response
+//
+
+#define MAX_SAP_RETRIES 2
+
+//
+// The maximum number of SAP response to process if we get many.
+//
+
+#define MAX_SAP_RESPONSES 4
+
+
+#define LFN_NO_OS2_NAME_SPACE -1
+
+//
+// The ordinal for the long namespace in the namespace packet.
+//
+
+#define LONG_NAME_SPACE_ORDINAL 4
+
+//
+// Largest possible SAP Response size and size of a SAP record
+//
+
+#define MAX_SAP_RESPONSE_SIZE 512
+#define SAP_RECORD_SIZE (2 + 48 + 12 + 2)
+#define FIND_NEAREST_RESP_SIZE (2 + SAP_RECORD_SIZE)
+
+//
+// Netware limits
+//
+
+#define MAX_SERVER_NAME_LENGTH 48
+#define MAX_UNICODE_UID_LENGTH 8
+#define MAX_USER_NAME_LENGTH 100 // BUGBUG - What is correct value?
+#define MAX_VOLUME_NAME_LENGTH 16 // BUGBUG - What is correct value?
+
+//
+// Maximum number of unique drive letters we will send to a server.
+// Only seems to matter to portable netWare servers.
+//
+#define MAX_DRIVES 64
+
+
+//
+// Default Timeout Event interval. We do not want to fill up the
+// event-log with timeout events. If a timeout event has been logged
+// in the last timeout event interval, we will ignore further timeout
+// events.
+//
+
+#define DEFAULT_TIMEOUT_EVENT_INTERVAL 5
+
+
+#endif // _NWCONST_
diff --git a/private/nw/rdr/convert.c b/private/nw/rdr/convert.c
new file mode 100644
index 000000000..4caf5372a
--- /dev/null
+++ b/private/nw/rdr/convert.c
@@ -0,0 +1,732 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ Convert.c
+
+Abstract:
+
+ This module implements conversion routine to map NT formats to
+ Netware and vice versa.
+
+Author:
+
+ Manny Weiser [MannyW] 3-Mar-1993
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+
+typedef union _NCP_DATE {
+ USHORT Ushort;
+ struct {
+ USHORT Day : 5;
+ USHORT Month : 4;
+ USHORT Year : 7;
+ } Struct;
+} NCP_DATE;
+
+typedef union _NCP_TIME {
+ USHORT Ushort;
+ struct {
+ USHORT TwoSeconds : 5;
+ USHORT Minutes : 6;
+ USHORT Hours : 5;
+ } Struct;
+} NCP_TIME;
+
+#define BASE_DOS_ERROR ((NTSTATUS )0xC0010000L)
+
+
+struct {
+ UCHAR NetError;
+ NTSTATUS ResultingStatus;
+} Error_Map[] = {
+ // NetWare specific error mappings
+ { 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_OBJECT_NAME_COLLISION },
+ {134, STATUS_OBJECT_NAME_COLLISION },
+ {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 },
+ {149, 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_FILE_LOCK_CONFLICT },
+ {165, STATUS_OBJECT_NAME_NOT_FOUND },
+ {191, STATUS_OBJECT_NAME_INVALID }, // Name space not loaded
+ {192, STATUS_ACCESS_DENIED},
+ {193, STATUS_ACCOUNT_RESTRICTION },
+ {194, STATUS_ACCOUNT_RESTRICTION },
+ {195, STATUS_ACCOUNT_DISABLED},
+ {197, STATUS_ACCOUNT_DISABLED },
+ {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 },
+#ifdef QFE_BUILD
+ {217, STATUS_ACCOUNT_RESTRICTION },
+ {218, STATUS_ACCOUNT_RESTRICTION },
+ {219, STATUS_ACCOUNT_RESTRICTION },
+#else
+ {217, STATUS_CONNECTION_COUNT_LIMIT },
+ {218, STATUS_LOGIN_TIME_RESTRICTION },
+ {219, STATUS_LOGIN_WKSTA_RESTRICTION },
+#endif
+ {220, STATUS_ACCOUNT_DISABLED },
+ {222, STATUS_PASSWORD_EXPIRED },
+ {223, NWRDR_PASSWORD_HAS_EXPIRED },
+ {231, STATUS_REMOTE_SESSION_LIMIT },
+ {236, STATUS_UNEXPECTED_NETWORK_ERROR },
+ {251, STATUS_INVALID_PARAMETER },
+ {252, STATUS_NO_MORE_ENTRIES },
+ {253, STATUS_FILE_LOCK_CONFLICT },
+ {254, STATUS_FILE_LOCK_CONFLICT },
+ {255, STATUS_UNSUCCESSFUL},
+
+ // DOS error mappings
+ //{ ERROR_INVALID_FUNCTION, STATUS_NOT_IMPLEMENTED },
+ { ERROR_FILE_NOT_FOUND, STATUS_NO_SUCH_FILE },
+ { ERROR_PATH_NOT_FOUND, STATUS_OBJECT_PATH_NOT_FOUND },
+ { ERROR_TOO_MANY_OPEN_FILES, STATUS_TOO_MANY_OPENED_FILES },
+ { ERROR_ACCESS_DENIED, STATUS_ACCESS_DENIED },
+ { ERROR_INVALID_HANDLE, STATUS_INVALID_HANDLE },
+ { ERROR_NOT_ENOUGH_MEMORY, STATUS_INSUFFICIENT_RESOURCES },
+ { ERROR_INVALID_ACCESS, STATUS_ACCESS_DENIED },
+ { ERROR_INVALID_DATA, STATUS_DATA_ERROR },
+
+ { ERROR_CURRENT_DIRECTORY, STATUS_DIRECTORY_NOT_EMPTY },
+ { ERROR_NOT_SAME_DEVICE, STATUS_NOT_SAME_DEVICE },
+ { ERROR_NO_MORE_FILES, STATUS_NO_MORE_FILES },
+/* */
+/* These are the universal int 24 mappings for the old INT 24 set of errors */
+/* */
+ { ERROR_WRITE_PROTECT, STATUS_MEDIA_WRITE_PROTECTED},
+ { ERROR_BAD_UNIT, STATUS_UNSUCCESSFUL}, // ***
+ { ERROR_NOT_READY, STATUS_DEVICE_NOT_READY },
+ { ERROR_BAD_COMMAND, STATUS_UNSUCCESSFUL}, // ***
+ { ERROR_CRC, STATUS_CRC_ERROR },
+ { ERROR_BAD_LENGTH, STATUS_DATA_ERROR },
+ { ERROR_SEEK, STATUS_UNSUCCESSFUL },// ***
+ { ERROR_NOT_DOS_DISK, STATUS_DISK_CORRUPT_ERROR }, //***
+ { ERROR_SECTOR_NOT_FOUND, STATUS_NONEXISTENT_SECTOR },
+ { ERROR_OUT_OF_PAPER, STATUS_DEVICE_PAPER_EMPTY},
+ { ERROR_WRITE_FAULT, STATUS_UNSUCCESSFUL}, // ***
+ { ERROR_READ_FAULT, STATUS_UNSUCCESSFUL}, // ***
+ { ERROR_GEN_FAILURE, STATUS_UNSUCCESSFUL }, // ***
+/* */
+/* These are the new 3.0 error codes reported through INT 24 */
+/* */
+ { ERROR_SHARING_VIOLATION, STATUS_SHARING_VIOLATION },
+ { ERROR_LOCK_VIOLATION, STATUS_FILE_LOCK_CONFLICT },
+ { ERROR_WRONG_DISK, STATUS_WRONG_VOLUME },
+// { ERROR_FCB_UNAVAILABLE, },
+// { ERROR_SHARING_BUFFER_EXCEEDED, },
+/* */
+/* New OEM network-related errors are 50-79 */
+/* */
+ { ERROR_NOT_SUPPORTED, STATUS_NOT_SUPPORTED },
+ { ERROR_REM_NOT_LIST, STATUS_REMOTE_NOT_LISTENING },
+ { ERROR_DUP_NAME, STATUS_DUPLICATE_NAME },
+ { ERROR_BAD_NETPATH, STATUS_BAD_NETWORK_PATH },
+ { ERROR_NETWORK_BUSY, STATUS_NETWORK_BUSY },
+ { ERROR_DEV_NOT_EXIST, STATUS_DEVICE_DOES_NOT_EXIST },
+ { ERROR_TOO_MANY_CMDS, STATUS_TOO_MANY_COMMANDS },
+ { ERROR_ADAP_HDW_ERR, STATUS_ADAPTER_HARDWARE_ERROR },
+ { ERROR_BAD_NET_RESP, STATUS_INVALID_NETWORK_RESPONSE },
+ { ERROR_UNEXP_NET_ERR, STATUS_UNEXPECTED_NETWORK_ERROR },
+ { ERROR_BAD_REM_ADAP, STATUS_BAD_REMOTE_ADAPTER },
+ { ERROR_PRINTQ_FULL, STATUS_PRINT_QUEUE_FULL },
+ { ERROR_NO_SPOOL_SPACE, STATUS_NO_SPOOL_SPACE },
+ { ERROR_PRINT_CANCELLED, STATUS_PRINT_CANCELLED },
+ { ERROR_NETNAME_DELETED, STATUS_NETWORK_NAME_DELETED },
+ { ERROR_NETWORK_ACCESS_DENIED, STATUS_NETWORK_ACCESS_DENIED },
+ { ERROR_BAD_DEV_TYPE, STATUS_BAD_DEVICE_TYPE },
+ { ERROR_BAD_NET_NAME, STATUS_BAD_NETWORK_NAME },
+ { ERROR_TOO_MANY_NAMES, STATUS_TOO_MANY_NAMES },
+ { ERROR_TOO_MANY_SESS, STATUS_REMOTE_SESSION_LIMIT },
+ { ERROR_SHARING_PAUSED, STATUS_SHARING_PAUSED },
+ { ERROR_REQ_NOT_ACCEP, STATUS_REQUEST_NOT_ACCEPTED },
+ { ERROR_REDIR_PAUSED, STATUS_REDIRECTOR_PAUSED },
+/* */
+/* End of INT 24 reportable errors */
+/* */
+ { ERROR_FILE_EXISTS, STATUS_OBJECT_NAME_COLLISION },
+// { ERROR_DUP_FCB, },
+// { ERROR_CANNOT_MAKE, },
+// { ERROR_FAIL_I24, },
+/* */
+/* New 3.0 network related error codes */
+/* */
+// { ERROR_OUT_OF_STRUCTURES, },
+// { ERROR_ALREADY_ASSIGNED, },
+ { ERROR_INVALID_PASSWORD, STATUS_WRONG_PASSWORD },
+ { ERROR_INVALID_PARAMETER, STATUS_INVALID_PARAMETER },
+ { ERROR_NET_WRITE_FAULT, STATUS_NET_WRITE_FAULT },
+/* */
+/* New error codes for 4.0 */
+/* */
+// { ERROR_NO_PROC_SLOTS, },
+// { ERROR_NOT_FROZEN, },
+// { ERR_TSTOVFL, },
+// { ERR_TSTDUP, },
+// { ERROR_NO_ITEMS, },
+// { ERROR_INTERRUPT, },
+
+// { ERROR_TOO_MANY_SEMAPHORES, },
+// { ERROR_EXCL_SEM_ALREADY_OWNED, },
+// { ERROR_SEM_IS_SET, },
+// { ERROR_TOO_MANY_SEM_REQUESTS, },
+// { ERROR_INVALID_AT_INTERRUPT_TIME, },
+
+// { ERROR_SEM_OWNER_DIED, },
+// { ERROR_SEM_USER_LIMIT, },
+// { ERROR_DISK_CHANGE, },
+// { ERROR_DRIVE_LOCKED, },
+ { ERROR_BROKEN_PIPE, STATUS_PIPE_BROKEN },
+/* */
+/* New error codes for 5.0 */
+/* */
+ //
+ // NOTE: ERROR_OPEN_FAILED is handled specially.
+ //
+
+ //
+ // The mapping of ERROR_OPEN_FAILED is context sensitive. If the
+ // disposition requested in the Open_AndX SMB is FILE_CREATE, this
+ // error means that the file already existed. If the disposition
+ // is FILE_OPEN, it means that the file does NOT exist!
+ //
+
+ { ERROR_OPEN_FAILED, STATUS_OPEN_FAILED },
+// { ERROR_BUFFER_OVERFLOW, },
+ { ERROR_DISK_FULL, STATUS_DISK_FULL },
+// { ERROR_NO_MORE_SEARCH_HANDLES, },
+// { ERROR_INVALID_TARGET_HANDLE, },
+// { ERROR_PROTECTION_VIOLATION, STATUS_ACCESS_VIOLATION },
+// { ERROR_VIOKBD_REQUEST, },
+// { ERROR_INVALID_CATEGORY, },
+// { ERROR_INVALID_VERIFY_SWITCH, },
+// { ERROR_BAD_DRIVER_LEVEL, },
+// { ERROR_CALL_NOT_IMPLEMENTED, },
+ { ERROR_SEM_TIMEOUT, STATUS_IO_TIMEOUT },
+ { ERROR_INSUFFICIENT_BUFFER, STATUS_BUFFER_TOO_SMALL },
+ { ERROR_INVALID_NAME, STATUS_OBJECT_NAME_INVALID },
+ { ERROR_INVALID_LEVEL, STATUS_INVALID_LEVEL },
+// { ERROR_NO_VOLUME_LABEL, },
+
+/* NOTE: DosQFSInfo no longer returns the above error; it is still here for */
+/* api\d_qfsinf.asm. */
+
+// { ERROR_MOD_NOT_FOUND, },
+// { ERROR_PROC_NOT_FOUND, },
+
+// { ERROR_WAIT_NO_CHILDREN, },
+
+// { ERROR_CHILD_NOT_COMPLETE, },
+
+// { ERROR_DIRECT_ACCESS_HANDLE, },
+ /* for direct disk access */
+ /* handles */
+// { ERROR_NEGATIVE_SEEK, },
+ /* with negitive offset */
+// { ERROR_SEEK_ON_DEVICE, },
+ /* on device or pipe */
+ { ERROR_BAD_PATHNAME, STATUS_OBJECT_PATH_INVALID }, //*
+
+/*
+ * Error codes 230 - 249 are reserved for MS Networks
+ */
+ { ERROR_BAD_PIPE, STATUS_INVALID_PARAMETER },
+ { ERROR_PIPE_BUSY, STATUS_PIPE_NOT_AVAILABLE },
+ { ERROR_NO_DATA, STATUS_PIPE_EMPTY },
+ { ERROR_PIPE_NOT_CONNECTED, STATUS_PIPE_DISCONNECTED },
+ { ERROR_MORE_DATA, STATUS_BUFFER_OVERFLOW },
+
+ { ERROR_VC_DISCONNECTED, STATUS_VIRTUAL_CIRCUIT_CLOSED },
+};
+
+#define NUM_ERRORS sizeof(Error_Map) / sizeof(Error_Map[0])
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_CONVERT)
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NtToNwShareFlags )
+#pragma alloc_text( PAGE, NtAttributesToNwAttributes )
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, pNwErrorToNtStatus )
+#pragma alloc_text( PAGE1, NwBurstResultToNtStatus )
+#pragma alloc_text( PAGE1, NwConnectionStatusToNtStatus )
+#pragma alloc_text( PAGE1, NwDateTimeToNtTime )
+#pragma alloc_text( PAGE1, NwNtTimeToNwDateTime )
+#endif
+
+#endif
+
+#if 0 // Not pageable
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+UCHAR
+NtToNwShareFlags(
+ ULONG DesiredAccess,
+ ULONG NtShareFlags
+ )
+/*++
+
+Routine Description:
+
+ This routine maps a NT desired/share access to Netware share flag bits.
+
+Arguments:
+
+ DesiredAccess - Desired access for open as specified in the read IRP.
+ NtShareFlags - The NT share flags from the create IRP.
+
+Return Value:
+
+ Netware share mode.
+
+--*/
+{
+ UCHAR NwShareFlags = 0;
+ ULONG lDesiredAccess;
+
+ PAGED_CODE();
+
+ //
+ // Ignore share delete, since we can't do anything with it.
+ //
+
+ switch ( NtShareFlags & (FILE_SHARE_READ | FILE_SHARE_WRITE) ) {
+
+ case 0:
+ NwShareFlags = NW_OPEN_EXCLUSIVE;
+ break;
+
+ case FILE_SHARE_READ:
+ NwShareFlags = NW_DENY_WRITE;
+ break;
+
+ case FILE_SHARE_WRITE:
+ NwShareFlags = NW_DENY_READ;
+ break;
+
+ case FILE_SHARE_WRITE | FILE_SHARE_READ:
+ NwShareFlags = 0;
+
+ }
+
+ //
+ // Treat append the same as write.
+ //
+
+ if ( DesiredAccess & FILE_APPEND_DATA) {
+
+ lDesiredAccess = DesiredAccess | FILE_WRITE_DATA;
+
+ } else {
+
+ lDesiredAccess = DesiredAccess;
+
+ }
+
+ switch ( lDesiredAccess & (FILE_EXECUTE | FILE_WRITE_DATA | FILE_READ_DATA) ) {
+
+ case (FILE_EXECUTE | FILE_WRITE_DATA | FILE_READ_DATA):
+ case (FILE_EXECUTE | FILE_WRITE_DATA):
+ NwShareFlags |= NW_OPEN_EXCLUSIVE | NW_OPEN_FOR_WRITE | NW_OPEN_FOR_READ;
+ break;
+
+ case (FILE_EXECUTE | FILE_READ_DATA):
+ case (FILE_EXECUTE):
+ NwShareFlags |= NW_OPEN_EXCLUSIVE | NW_OPEN_FOR_READ;
+ break;
+
+ case (FILE_WRITE_DATA | FILE_READ_DATA):
+ NwShareFlags |= NW_OPEN_FOR_WRITE | NW_OPEN_FOR_READ;
+ break;
+
+ case (FILE_WRITE_DATA):
+ NwShareFlags |= NW_OPEN_FOR_WRITE;
+ break;
+
+ default:
+ NwShareFlags |= NW_OPEN_FOR_READ;
+ break;
+ }
+
+ if (NwShareFlags & NW_OPEN_EXCLUSIVE) {
+
+ //
+ // Remove the NW_DENY_* flags if exclusive is already specified since
+ // this interferes with the shareable flag.
+ //
+
+ return( NwShareFlags & ~(NW_DENY_READ | NW_DENY_WRITE) );
+ }
+
+ return( NwShareFlags );
+}
+
+
+UCHAR
+NtAttributesToNwAttributes(
+ ULONG FileAttributes
+ )
+/*++
+
+Routine Description:
+
+ This routine maps a NT attributes mask to a Netware mask.
+
+Arguments:
+
+ DesiredAccess - Desired access for open as specified in the read IRP.
+
+Return Value:
+
+ Netware share mode.
+
+--*/
+{
+ return( (UCHAR)FileAttributes & 0x3F );
+}
+
+NTSTATUS
+pNwErrorToNtStatus(
+ UCHAR NwError
+ )
+/*++
+
+Routine Description:
+
+ This routine converts a Netware error code to an NT status code.
+
+Arguments:
+
+ NwError - The netware error.
+
+Return Value:
+
+ NTSTATUS - The converted status.
+
+--*/
+
+{
+ int i;
+
+ ASSERT(NwError != 0);
+
+ //
+ // Errors 2 through 127 are mapped as DOS errors.
+ //
+
+ if ( NwError > 1 && NwError < 128 ) {
+ return( BASE_DOS_ERROR + NwError );
+ }
+
+ //
+ // For other errors, search the table for the matching error number.
+ //
+
+ for ( i = 0; i < NUM_ERRORS; i++ ) {
+ if ( Error_Map[i].NetError == NwError ) {
+ return( Error_Map[i].ResultingStatus );
+ }
+ }
+
+ DebugTrace( 0, 0, "No error mapping for error %d\n", NwError );
+
+#ifdef NWDBG
+ Error( EVENT_NWRDR_NETWORK_ERROR, (NTSTATUS)0xC0010000 | NwError, NULL, 0, 0 );
+#endif
+
+ return( (NTSTATUS)0xC0010000 | NwError );
+}
+
+NTSTATUS
+NwBurstResultToNtStatus(
+ ULONG Result
+ )
+/*++
+
+Routine Description:
+
+ This routine converts a Netware burst result code to an NT status code.
+
+Arguments:
+
+ Result - The netware burst result.
+
+Return Value:
+
+ NTSTATUS - The converted status.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ //
+ // the 3 high order bits should not be set. but if they are,
+ // we return an error.
+ //
+ if (Result & 0xFFFFFF00)
+ return( STATUS_UNEXPECTED_NETWORK_ERROR );
+
+ switch ( Result ) {
+
+ case 0:
+ case 3: // No data
+ Status = STATUS_SUCCESS;
+ break;
+
+ case 1:
+ Status = STATUS_DISK_FULL;
+ break;
+
+ case 2: // I/O error
+ Status = STATUS_UNEXPECTED_IO_ERROR;
+ break;
+
+ default:
+ Status = NwErrorToNtStatus( (UCHAR)Result );
+ break;
+ }
+
+ return( Status );
+}
+
+NTSTATUS
+NwConnectionStatusToNtStatus(
+ UCHAR NwStatus
+ )
+/*++
+
+Routine Description:
+
+ This routine converts a Netware connection status code to an NT
+ status code.
+
+Arguments:
+
+ NwStatus - The netware connection status.
+
+Return Value:
+
+ NTSTATUS - The converted status.
+
+--*/
+
+{
+ if ( (NwStatus & 1) == 0 ) {
+ return STATUS_SUCCESS;
+ } else {
+ return STATUS_REMOTE_DISCONNECT;
+ }
+}
+
+LARGE_INTEGER
+NwDateTimeToNtTime (
+ IN USHORT UDate,
+ IN USHORT UTime
+ )
+
+/*++
+
+Routine Description:
+
+ This routine converts an NCP time to an NT time structure.
+
+Arguments:
+
+ Time - Supplies the time of day to convert
+ Date - Supplies the day of the year to convert
+
+Return Value:
+
+ LARGE_INTEGER - Time structure describing input time.
+
+--*/
+
+{
+ TIME_FIELDS TimeFields;
+ LARGE_INTEGER OutputTime;
+ NCP_DATE Date = *(NCP_DATE *)&UDate;
+ NCP_TIME Time = *(NCP_TIME *)&UTime;
+
+ if ( Date.Ushort == 0 && Time.Ushort == 0 ) {
+
+ //
+ // The file time stamp is zero. Do not return a file time of
+ // zero, since this will be biased to a negative time (due to
+ // time zone fixup), and no one will be able to display it
+ // correctly. Instead, we "randomly" pick Jan 01, 1980 @ 12:00am
+ // as the file time.
+ //
+ // We assume that the netware server is in our time zone.
+
+ RtlSecondsSince1980ToTime(0, &OutputTime);
+
+ } else {
+
+ TimeFields.Year = Date.Struct.Year + (USHORT )1980;
+ TimeFields.Month = Date.Struct.Month;
+ TimeFields.Day = Date.Struct.Day;
+
+ TimeFields.Hour = Time.Struct.Hours;
+ TimeFields.Minute = Time.Struct.Minutes;
+ TimeFields.Second = Time.Struct.TwoSeconds*(USHORT )2;
+ TimeFields.Milliseconds = 0;
+
+ //
+ // Make sure that the times specified in the packet are reasonable
+ // before converting them.
+ //
+
+ if (TimeFields.Year < 1601) {
+ TimeFields.Year = 1601;
+ }
+
+ if (TimeFields.Month > 12) {
+ TimeFields.Month = 12;
+ }
+
+ if (TimeFields.Hour >= 24) {
+ TimeFields.Hour = 23;
+ }
+
+ if (TimeFields.Minute >= 60) {
+ TimeFields.Minute = 59;
+ }
+
+ if (TimeFields.Second >= 60) {
+ TimeFields.Second = 59;
+ }
+
+ if (!RtlTimeFieldsToTime(&TimeFields, &OutputTime)) {
+
+ OutputTime.QuadPart = 0;
+ return OutputTime;
+ }
+
+ }
+
+ // Convert to UTC for the system.
+ ExLocalTimeToSystemTime(&OutputTime, &OutputTime);
+ return OutputTime;
+
+}
+
+NTSTATUS
+NwNtTimeToNwDateTime (
+ IN LARGE_INTEGER NtTime,
+ IN PUSHORT NwDate,
+ IN PUSHORT NwTime
+ )
+
+/*++
+
+Routine Description:
+
+ This routine converts an NT time structure to an NCP time.
+
+Arguments:
+
+ NtTime - Supplies to NT Time to convert.
+
+ NwDate - Returns the Netware format date.
+
+ NwTime - Returns the Netware format time.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+
+{
+ TIME_FIELDS TimeFields;
+ NCP_DATE Date;
+ NCP_TIME Time;
+
+ if (NtTime.QuadPart == 0) {
+
+ Time.Ushort = Date.Ushort = 0;
+
+ } else {
+
+ LARGE_INTEGER LocalTime;
+
+ // We assume that the netware server is in our time zone.
+
+ ExSystemTimeToLocalTime( &NtTime, &LocalTime );
+ RtlTimeToTimeFields( &LocalTime, &TimeFields );
+
+ if (TimeFields.Year < 1980 || TimeFields.Year > (1980 + 127) ) {
+ return( STATUS_INVALID_PARAMETER );
+ }
+
+ Date.Struct.Year = (USHORT )(TimeFields.Year - 1980);
+ Date.Struct.Month = TimeFields.Month;
+ Date.Struct.Day = TimeFields.Day;
+
+ Time.Struct.Hours = TimeFields.Hour;
+ Time.Struct.Minutes = TimeFields.Minute;
+
+ //
+ // When converting from a higher granularity time to a lesser
+ // granularity time (seconds to 2 seconds), always round up
+ // the time, don't round down.
+ //
+
+ Time.Struct.TwoSeconds = TimeFields.Second / 2;
+
+ }
+
+ *NwDate = *( USHORT *)&Date;
+ *NwTime = *( USHORT *)&Time;
+ return( STATUS_SUCCESS );
+}
+
diff --git a/private/nw/rdr/convert.h b/private/nw/rdr/convert.h
new file mode 100644
index 000000000..e093e4d2c
--- /dev/null
+++ b/private/nw/rdr/convert.h
@@ -0,0 +1,33 @@
+
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ Convert.h
+
+Abstract:
+
+ This module declares the types used to permit exchange.c to be used
+ with minimal change in the NetWare file system.
+
+Author:
+
+ Colin Watson [ColinW] 23-Dec-1992
+
+Revision History:
+
+--*/
+
+#ifndef _CONVERT_
+#define _CONVERT_
+
+#define byte UCHAR
+#define word USHORT
+#define dword ULONG
+
+#define offsetof(r,f) ((size_t)&(((r*)0)->f))
+#define byteswap(x) ((x>>8)+((x&0xFF)<<8))
+
+#endif //_CONVERT_
diff --git a/private/nw/rdr/create.c b/private/nw/rdr/create.c
new file mode 100644
index 000000000..e50c9ad68
--- /dev/null
+++ b/private/nw/rdr/create.c
@@ -0,0 +1,3398 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ Create.c
+
+Abstract:
+
+ This module implements the File Create routine for the NetWare
+ redirector called by the dispatch driver.
+
+Author:
+
+ Colin Watson [ColinW] 19-Dec-1992
+ Manny Weiser [MannyW] 15-Feb-1993
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+
+NTSTATUS
+NwCommonCreate (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+IO_STATUS_BLOCK
+OpenRedirector(
+ IN PIRP_CONTEXT IrpContext,
+ ULONG DesiredAccess,
+ ULONG ShareAccess,
+ PFILE_OBJECT FileObject
+ );
+
+IO_STATUS_BLOCK
+CreateRemoteFile(
+ IN PIRP_CONTEXT IrpContext,
+ IN PUNICODE_STRING DriveName
+ );
+
+IO_STATUS_BLOCK
+ChangeDirectory(
+ PIRP_CONTEXT IrpContext,
+ PVCB Vcb,
+ PICB Icb
+ );
+
+IO_STATUS_BLOCK
+CreateDir(
+ PIRP_CONTEXT IrpContext,
+ PVCB Vcb,
+ PICB Icb
+ );
+
+NTSTATUS
+FileOrDirectoryExists(
+ IN PIRP_CONTEXT IrpContext,
+ IN PVCB Vcb,
+ IN PICB Icb,
+ PUNICODE_STRING Name,
+ OUT PBOOLEAN IsAFile
+ );
+
+IO_STATUS_BLOCK
+OpenFile(
+ IN PIRP_CONTEXT IrpContext,
+ IN PVCB Vcb,
+ IN PICB Icb,
+ IN BYTE SearchFlags,
+ IN BYTE ShareFlags
+ );
+
+IO_STATUS_BLOCK
+CreateNewFile(
+ IN PIRP_CONTEXT IrpContext,
+ IN PVCB Vcb,
+ IN PICB Icb,
+ IN BYTE SearchFlags,
+ IN BYTE ShareFlags
+ );
+
+IO_STATUS_BLOCK
+CreateOrOverwriteFile(
+ IN PIRP_CONTEXT IrpContext,
+ IN PVCB Vcb,
+ IN PICB Icb,
+ IN BYTE CreateAttributes,
+ IN BYTE OpenFlags,
+ IN BOOLEAN CreateOperation
+ );
+
+IO_STATUS_BLOCK
+OpenRenameTarget(
+ IN PIRP_CONTEXT IrpContext,
+ IN PVCB Vcb,
+ IN PDCB Dcb,
+ IN PICB* Icb
+ );
+
+IO_STATUS_BLOCK
+CreatePrintJob(
+ PIRP_CONTEXT IrpContext,
+ PVCB Vcb,
+ PICB Icb,
+ PUNICODE_STRING DriveName
+ );
+
+VOID
+CloseFile(
+ PIRP_CONTEXT pIrpContext,
+ PICB pIcb
+ );
+
+
+// BUGBUG HACK HACK
+
+BOOLEAN
+MmDisableModifiedWriteOfSection (
+ IN PSECTION_OBJECT_POINTERS SectionObjectPointer
+ );
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_CREATE)
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwFsdCreate )
+#pragma alloc_text( PAGE, NwCommonCreate )
+#pragma alloc_text( PAGE, ReadAttachEas )
+#pragma alloc_text( PAGE, OpenRedirector )
+#pragma alloc_text( PAGE, CreateRemoteFile )
+#pragma alloc_text( PAGE, ChangeDirectory )
+#pragma alloc_text( PAGE, CreateDir )
+#pragma alloc_text( PAGE, FileOrDirectoryExists )
+#pragma alloc_text( PAGE, OpenFile )
+#pragma alloc_text( PAGE, CreateNewFile )
+#pragma alloc_text( PAGE, CreateOrOverwriteFile )
+#pragma alloc_text( PAGE, OpenRenameTarget )
+#pragma alloc_text( PAGE, CreatePrintJob )
+#pragma alloc_text( PAGE, CloseFile )
+#endif
+
+
+NTSTATUS
+NwFsdCreate (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine implements the FSD part of the NtCreateFile and NtOpenFile
+ API calls.
+
+Arguments:
+
+ DeviceObject - Supplies the device object for the redirector.
+
+ Irp - Supplies the Irp being processed
+
+Return Value:
+
+ NTSTATUS - The Fsd status for the Irp
+
+--*/
+
+{
+ NTSTATUS Status;
+ PIRP_CONTEXT IrpContext = NULL;
+ BOOLEAN TopLevel;
+
+ PAGED_CODE();
+
+ TimerStart(Dbg);
+ DebugTrace(+1, Dbg, "NwFsdCreate\n", 0);
+
+ //
+ // Call the common create routine, with block allowed if the operation
+ // is synchronous.
+ //
+
+ FsRtlEnterFileSystem();
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ try {
+
+ IrpContext = AllocateIrpContext( Irp );
+ Status = NwCommonCreate( IrpContext );
+
+ } except( NwExceptionFilter( Irp, GetExceptionInformation() )) {
+
+ if ( IrpContext == NULL ) {
+
+ //
+ // If we couldn't allocate an irp context, just complete
+ // irp without any fanfare.
+ //
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
+
+ } else {
+
+ //
+ // We had some trouble trying to perform the requested
+ // operation, so we'll abort the I/O request with
+ // the error Status that we get back from the
+ // execption code
+ //
+
+ Status = NwProcessException( IrpContext, GetExceptionCode() );
+ }
+ }
+
+ if ( IrpContext ) {
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwCompleteRequest( IrpContext, Status );
+ }
+
+ if ( TopLevel ) {
+ NwSetTopLevelIrp( NULL );
+ }
+ FsRtlExitFileSystem();
+
+ //
+ // And return to our caller
+ //
+
+ DebugTrace(-1, Dbg, "NwFsdCreate -> %08lx\n", Status );
+
+ TimerStop(Dbg,"NwFsdCreate");
+
+ return Status;
+
+ UNREFERENCED_PARAMETER(DeviceObject);
+}
+
+
+NTSTATUS
+NwCommonCreate (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This is the common routine for creating/opening a file called by
+ both the fsd and fsp threads.
+
+Arguments:
+
+ IrpContext - Supplies the context information for the IRP to process
+
+Return Value:
+
+ NTSTATUS - the return status for the operation
+
+--*/
+
+{
+ IO_STATUS_BLOCK Iosb;
+ PIRP Irp;
+ PIO_STACK_LOCATION IrpSp;
+
+ PFILE_OBJECT FileObject;
+ ACCESS_MASK DesiredAccess;
+ USHORT ShareAccess;
+ ULONG Options;
+ BOOLEAN CreateTreeConnection;
+ BOOLEAN DeleteOnClose;
+ BOOLEAN DeferredLogon;
+ BOOLEAN DereferenceCodeSection = FALSE;
+ BOOLEAN OpenedTreeHandle = FALSE;
+
+ UNICODE_STRING CreateFileName;
+ UNICODE_STRING Drive;
+ UNICODE_STRING Server;
+ UNICODE_STRING Volume;
+ UNICODE_STRING Path;
+ UNICODE_STRING FileName;
+ UNICODE_STRING UserName, Password;
+ ULONG ShareType;
+ WCHAR DriveLetter;
+ DWORD dwExtendedCreate = FALSE;
+
+ PSCB Scb = NULL;
+ PICB Icb;
+ UNICODE_STRING DefaultServer;
+
+ PAGED_CODE();
+
+ //
+ // Get the current IRP stack location
+ //
+
+ Irp = IrpContext->pOriginalIrp;
+
+ IrpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ DebugTrace(+1, Dbg, "NwCommonCreate\n", 0 );
+ DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp );
+ DebugTrace( 0, Dbg, "->Flags = %08lx\n", Irp->Flags );
+ DebugTrace( 0, Dbg, "->FileObject = %08lx\n", IrpSp->FileObject );
+ DebugTrace( 0, Dbg, " ->RelatedFileObject = %08lx\n", IrpSp->FileObject->RelatedFileObject );
+ DebugTrace( 0, Dbg, " ->FileName = \"%wZ\"\n", &IrpSp->FileObject->FileName );
+ DebugTrace( 0, Dbg, "->AllocationSize.LowPart = %08lx\n", Irp->Overlay.AllocationSize.LowPart );
+ DebugTrace( 0, Dbg, "->AllocationSize.HighPart = %08lx\n", Irp->Overlay.AllocationSize.HighPart );
+ DebugTrace( 0, Dbg, "->SystemBuffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer );
+ DebugTrace( 0, Dbg, "->IrpSp->Flags = %08lx\n", IrpSp->Flags );
+ DebugTrace( 0, Dbg, "->DesiredAccess = %08lx\n", IrpSp->Parameters.Create.SecurityContext->DesiredAccess );
+ DebugTrace( 0, Dbg, "->Options = %08lx\n", IrpSp->Parameters.Create.Options );
+ DebugTrace( 0, Dbg, "->Disposition = %08lx\n", (IrpSp->Parameters.Create.Options >> 24) & 0x000000ff);
+ DebugTrace( 0, Dbg, "->FileAttributes = %04x\n", IrpSp->Parameters.Create.FileAttributes );
+ DebugTrace( 0, Dbg, "->ShareAccess = %04x\n", IrpSp->Parameters.Create.ShareAccess );
+ DebugTrace( 0, Dbg, "->EaLength = %08lx\n", IrpSp->Parameters.Create.EaLength );
+
+ CreateFileName = IrpSp->FileObject->FileName;
+ Options = IrpSp->Parameters.Create.Options;
+ DesiredAccess = IrpSp->Parameters.Create.SecurityContext->DesiredAccess;
+ ShareAccess = IrpSp->Parameters.Create.ShareAccess;
+
+ CreateTreeConnection = BooleanFlagOn( Options, FILE_CREATE_TREE_CONNECTION );
+ DeleteOnClose = BooleanFlagOn( Options, FILE_DELETE_ON_CLOSE );
+
+ DefaultServer.Buffer = NULL;
+
+ //
+ // Make sure the input large integer is valid
+ //
+
+ if (Irp->Overlay.AllocationSize.HighPart != 0) {
+
+ DebugTrace(-1, Dbg, "NwCommonCreate -> STATUS_INVALID_PARAMETER\n", 0);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Fail requests that don't have the proper impersonation level.
+ //
+
+ if ( IrpSp->Parameters.Create.SecurityContext ) {
+
+ if ( IrpSp->Parameters.Create.SecurityContext->SecurityQos ) {
+
+ if ( IrpSp->Parameters.Create.SecurityContext->SecurityQos->ImpersonationLevel <
+ SecurityImpersonation ) {
+
+ DebugTrace(-1, Dbg, "NwCommonCreate -> Insufficient impersation level.\n", 0);
+ return STATUS_ACCESS_DENIED;
+ }
+ }
+ }
+
+ Iosb.Status = STATUS_SUCCESS;
+
+ FileObject = IrpSp->FileObject;
+ IrpContext->pNpScb = NULL;
+ IrpContext->Specific.Create.UserUid =
+ GetUid(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext);
+
+ try {
+
+ if ( IrpSp->FileObject->RelatedFileObject != NULL ) {
+
+ //
+ // If we open a handle then the DereferenceCodeSection flag
+ // will be set to false. The dereference will eventually
+ // happen when the file is closed.
+ //
+
+ NwReferenceUnlockableCodeSection();
+ DereferenceCodeSection = TRUE;
+
+ //
+ // Record the relative file name for this open.
+ //
+
+ IrpContext->Specific.Create.FullPathName = CreateFileName;
+
+ Iosb = CreateRemoteFile( IrpContext, NULL );
+
+ //
+ // If we succeeded, we want to keep the code section
+ // referenced because we have opened a handle.
+ //
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ DereferenceCodeSection = FALSE;
+ }
+
+ try_return( Iosb.Status );
+ }
+
+ Iosb.Status = CrackPath (
+ &CreateFileName,
+ &Drive,
+ &DriveLetter,
+ &Server,
+ &Volume,
+ &Path,
+ &FileName,
+ NULL );
+
+ if ( !NT_SUCCESS(Iosb.Status)) {
+ try_return(Iosb.Status);
+ }
+
+ //
+ // Remember this good info.
+ //
+
+ IrpContext->Specific.Create.VolumeName = Volume;
+ IrpContext->Specific.Create.PathName = Path;
+ IrpContext->Specific.Create.DriveLetter = DriveLetter;
+ IrpContext->Specific.Create.FileName = FileName;
+ IrpContext->Specific.Create.FullPathName = CreateFileName;
+
+ RtlInitUnicodeString( &IrpContext->Specific.Create.UidConnectName, NULL );
+
+ //
+ // For now assume default username and password
+ //
+
+ ShareType = RESOURCETYPE_ANY;
+ RtlInitUnicodeString( &UserName, NULL );
+ RtlInitUnicodeString( &Password, NULL );
+
+ if ( Server.Length == 0) {
+
+ //
+ // Opened the redirector itself
+ //
+
+ Iosb = OpenRedirector(
+ IrpContext,
+ DesiredAccess,
+ ShareAccess,
+ FileObject );
+
+ } else if ( Server.Length == Volume.Length - sizeof( WCHAR ) ) {
+
+ if (IpxHandle == 0 ) {
+
+ //
+ // We're not bound to the transport and the user is not
+ // opening the redirector to tell us to bind so return failed.
+ //
+
+ try_return( Iosb.Status = STATUS_REDIRECTOR_NOT_STARTED );
+ }
+
+ NwReferenceUnlockableCodeSection();
+ DereferenceCodeSection = TRUE;
+
+ //
+ // If the only requested access is FILE_LIST_DIRECTORY,
+ // defer the logon. This will allow all CreateScb to
+ // succeed with when the user or password is invalid, so
+ // that the user can see volumes, or enumerate servers
+ // on the server.
+ //
+
+ if ( (DesiredAccess & ~( FILE_LIST_DIRECTORY | SYNCHRONIZE ) ) == 0 ) {
+ DeferredLogon = TRUE;
+ } else {
+ DeferredLogon = FALSE;
+ }
+
+ //
+ // Server = "Server", Volume = "\Server"
+ //
+
+ if ( Server.Length == sizeof(WCHAR) && Server.Buffer[0] == L'*') {
+
+ //
+ // Attempt to open \\*, open a handle to the preferred
+ // server
+ //
+
+ PLOGON Logon;
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ Logon = FindUser( &IrpContext->Specific.Create.UserUid, FALSE);
+ ASSERT( Logon != NULL );
+
+ //
+ // Capture the name to avoid holding Rcb or referencing
+ // the logon structure.
+ //
+
+ Iosb.Status = DuplicateUnicodeStringWithString (
+ &DefaultServer,
+ &Logon->ServerName,
+ PagedPool);
+
+ NwReleaseRcb( &NwRcb );
+
+ if (!NT_SUCCESS(Iosb.Status)) {
+ try_return( Iosb.Status );
+ }
+
+ //
+ // If the user specified a preferred server and we managed
+ // to capture the name, try and connect to it.
+ //
+
+ if (DefaultServer.Length != 0) {
+
+ Iosb.Status = CreateScb(
+ &Scb,
+ IrpContext,
+ &DefaultServer,
+ NULL,
+ NULL,
+ NULL,
+ DeferredLogon,
+ FALSE );
+
+ } else {
+
+ //
+ // Record that we could not get to the server specified
+ // in the login structure and that we should attempt to
+ // use the nearest server.
+ //
+
+ Iosb.Status = STATUS_BAD_NETWORK_PATH;
+ }
+
+ if ( !NT_SUCCESS(Iosb.Status)) {
+
+ PNONPAGED_SCB NpScb;
+
+ //
+ // First dequeue the IRP context, in case it was left
+ // on an SCB queue.
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+
+ //
+ // Cannot get to the Preferred server so use any
+ // server we have a connection to.
+ //
+
+
+ NpScb = SelectConnection( NULL );
+
+ if (NpScb != NULL ) {
+
+ Scb = NpScb->pScb;
+
+ Iosb.Status = CreateScb(
+ &Scb,
+ IrpContext,
+ &NpScb->ServerName,
+ NULL,
+ NULL,
+ NULL,
+ DeferredLogon,
+ FALSE );
+
+ //
+ // Release the SCB reference we obtained from
+ // SelectConnection().
+ //
+
+ NwDereferenceScb( NpScb );
+ }
+ }
+
+ if ( !NT_SUCCESS(Iosb.Status)) {
+
+ //
+ // First dequeue the IRP context, in case it was left
+ // on an SCB queue.
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+
+ //
+ // Let CreateScb try and find a nearest server to talk
+ // to.
+ //
+
+ Iosb.Status = CreateScb(
+ &Scb,
+ IrpContext,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ DeferredLogon,
+ FALSE );
+ }
+
+ if ( !NT_SUCCESS(Iosb.Status)) {
+ try_return( Iosb.Status );
+ }
+
+ } else {
+
+ //
+ // On handle opens to a server or tree we support the concept
+ // of an open with supplemental credentials. In this case, we return
+ // a handle to the server or a dir server using the provided
+ // credentials regardless of whether or not there are existing
+ // connections to the resource. This is primarily for admin
+ // tools like OleDs.
+ //
+
+ ReadAttachEas( Irp, &UserName, &Password, &ShareType, &dwExtendedCreate );
+
+ if ( dwExtendedCreate ) {
+ ASSERT( UserName.Length > 0 );
+ IrpContext->Specific.Create.fExCredentialCreate = TRUE;
+ IrpContext->Specific.Create.puCredentialName = &UserName;
+ }
+
+ //
+ // Attempt to open \\server
+ //
+
+ Iosb.Status = CreateScb(
+ &Scb,
+ IrpContext,
+ &Server,
+ NULL,
+ &UserName,
+ &Password,
+ DeferredLogon,
+ DeleteOnClose );
+
+ if ( ( Iosb.Status == STATUS_REMOTE_NOT_LISTENING ) ||
+ ( Iosb.Status == STATUS_BAD_NETWORK_PATH ) ||
+ ( Iosb.Status == STATUS_UNSUCCESSFUL ) ) {
+
+ //
+ // If we couldn't find the server or something
+ // inexplicable occurred, attempt to open \\tree.
+ //
+
+ Iosb.Status = NdsCreateTreeScb( IrpContext,
+ &Scb, // dest scb
+ &Server, // tree we want
+ &UserName,
+ &Password,
+ DeferredLogon,
+ DeleteOnClose );
+
+ if ( !NT_SUCCESS( Iosb.Status ) ) {
+ try_return( Iosb.Status );
+ }
+
+ OpenedTreeHandle = TRUE;
+
+ } else if ( !NT_SUCCESS( Iosb.Status ) ) {
+
+ //
+ // If we failed to get the bindery server for
+ // some legitimate reason, bail out now.
+ //
+ try_return( Iosb.Status );
+ }
+
+ //
+ // We must have a connection at this point. We don't tree
+ // connect the dir server since it's virtual.
+ //
+
+ if ( !OpenedTreeHandle && CreateTreeConnection && !DeleteOnClose ) {
+ TreeConnectScb( Scb );
+ }
+
+ }
+
+ //
+ // Now create the ICB.
+ //
+
+ ASSERT( Iosb.Status == STATUS_SUCCESS );
+ ASSERT( Scb != NULL );
+
+ Icb = NwCreateIcb( NW_NTC_ICB_SCB, Scb );
+ Icb->FileObject = FileObject;
+ NwSetFileObject( FileObject, NULL, Icb );
+
+ //
+ // Indicate that the SCB was opened.
+ //
+
+ Icb->State = ICB_STATE_OPENED;
+
+ //
+ // Is this a tree handle?
+ //
+
+ Icb->IsTreeHandle = OpenedTreeHandle;
+
+ } else {
+
+ NwReferenceUnlockableCodeSection();
+ DereferenceCodeSection = TRUE;
+
+ DeferredLogon = FALSE;
+
+ if ( CreateTreeConnection ) {
+
+ //
+ // We ignore the extended create attribute here because
+ // we DO NOT support extended credential creates to random
+ // files and directories!
+ //
+
+ ReadAttachEas( Irp, &UserName, &Password, &ShareType, NULL );
+
+ if ( DeleteOnClose ) {
+
+ //
+ // Opening a directory to delete a volume. Do not
+ // force logon.
+ //
+
+ DeferredLogon = TRUE;
+ }
+ }
+
+ IrpContext->Specific.Create.ShareType = ShareType;
+ IrpContext->Specific.Create.NdsCreate = FALSE;
+
+ Iosb.Status = CreateScb(
+ &Scb,
+ IrpContext,
+ &Server,
+ NULL,
+ &UserName,
+ &Password,
+ DeferredLogon,
+ DeleteOnClose );
+
+ if ( Iosb.Status == STATUS_REMOTE_NOT_LISTENING ||
+ Iosb.Status == STATUS_BAD_NETWORK_PATH ||
+ Iosb.Status == STATUS_UNSUCCESSFUL ) {
+
+ //
+ // If we couldn't find the server or something
+ // inexplicable occurred, attempt to open \\tree.
+ //
+
+ IrpContext->Specific.Create.NdsCreate = TRUE;
+ IrpContext->Specific.Create.NeedNdsData = TRUE;
+
+ Iosb.Status = NdsCreateTreeScb( IrpContext,
+ &Scb,
+ &Server,
+ &UserName,
+ &Password,
+ DeferredLogon,
+ DeleteOnClose );
+ }
+
+ //
+ // If we have success, then there's a volume to connect.
+ //
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+
+ NTSTATUS CreateScbStatus;
+
+ ASSERT( Scb != NULL );
+
+ //
+ // Remember the status from create SCB, since it might
+ // be an interesting warning.
+ //
+
+ CreateScbStatus = Iosb.Status;
+
+ //
+ // We catch this exception in case we have to retry the
+ // create on the NDS path. This sucks, as does the
+ // exception structure in this code right now, but it's
+ // legacy and now is not the time to change it.
+ //
+
+ try {
+
+ Iosb = CreateRemoteFile(
+ IrpContext,
+ &Drive );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Iosb.Status = GetExceptionCode();
+ }
+
+ //
+ // If this is a server whose name is the same as the tree
+ // that it is a member of, and the create was marked as
+ // non-nds and it failed, retry an nds create.
+ //
+
+ if ( ( !NT_SUCCESS( Iosb.Status) ) &&
+ ( !(IrpContext->Specific.Create.NdsCreate) ) &&
+ ( RtlEqualUnicodeString( &(Scb->pNpScb->ServerName),
+ &(Scb->NdsTreeName),
+ TRUE ) ) ) {
+
+ IrpContext->Specific.Create.NdsCreate = TRUE;
+ IrpContext->Specific.Create.NeedNdsData = TRUE;
+
+ Iosb = CreateRemoteFile(
+ IrpContext,
+ &Drive );
+
+ //
+ // If this fails, it will raise status before setting IOSB
+ // and we'll return the status from the original create,
+ // which is the more interesting one.
+ //
+
+ }
+
+ //
+ // If we successfully open the remote file, return the
+ // CreateScb status instead.
+ //
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ Iosb.Status = CreateScbStatus;
+ }
+
+ }
+ }
+
+ //
+ // If we succeeded, we want to keep the code section
+ // referenced because we have opened a handle.
+ //
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ DereferenceCodeSection = FALSE;
+ }
+
+ try_exit: NOTHING;
+ } finally {
+
+
+ //
+ // Track the Scb in the IrpContext, not in the local Scb
+ // variable since we may have been routed to another server
+ // in process.
+ //
+
+ if ( Scb != NULL ) {
+ ASSERT( IrpContext->pNpScb );
+ NwDereferenceScb( IrpContext->pNpScb );
+ }
+
+ if ( DefaultServer.Buffer != NULL ) {
+ FREE_POOL( DefaultServer.Buffer );
+ }
+
+ DebugTrace(-1, Dbg, "NwCommonCreate -> %08lx\n", Iosb.Status);
+
+ if ( DereferenceCodeSection ) {
+ NwDereferenceUnlockableCodeSection ();
+ }
+
+ }
+
+ //
+ // Map a timeout error to server not found, so that MPR will
+ // try to connect on the next network provider instead of giving up,
+ // which is wrong wrong wrong.
+ //
+
+ if ( Iosb.Status == STATUS_REMOTE_NOT_LISTENING ) {
+ Iosb.Status = STATUS_BAD_NETWORK_PATH;
+ }
+
+ //
+ // Map an unbound transport error to server not found, so that MPR
+ // will try to connect on the next provider.
+ //
+
+ if ( Iosb.Status == STATUS_NETWORK_UNREACHABLE ) {
+ Iosb.Status = STATUS_BAD_NETWORK_PATH;
+ }
+
+ return Iosb.Status;
+}
+
+
+NTSTATUS
+ReadAttachEas(
+ IN PIRP Irp,
+ OUT PUNICODE_STRING UserName,
+ OUT PUNICODE_STRING Password,
+ OUT PULONG ShareType,
+ OUT PDWORD CredentialExtension
+ )
+
+/*++
+
+Routine Description:
+
+ This routine processes the EAs provided when the caller attempts
+ to attach to a remote server.
+
+ Note: This routine does not create additional storage for the names.
+ It is the callers responsibility to save them if required.
+
+Arguments:
+
+ Irp - Supplies all the information
+
+ UserName - Returns the value of the User name EA
+
+ Password - Returns the value of the password EA
+
+ ShareType - Returns the value of the share type EA
+
+ CredentialExtension - Returns whether or not this create
+ should use the provided credentials for an credential
+ extended connection. This is primarily for OleDs
+ accessing the ds in multiple security contexts.
+
+Return Value:
+
+ NTSTATUS - Status of operation
+
+--*/
+{
+
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PFILE_FULL_EA_INFORMATION EaBuffer = Irp->AssociatedIrp.SystemBuffer;
+
+ PAGED_CODE();
+
+ RtlInitUnicodeString( UserName, NULL );
+ RtlInitUnicodeString( Password, NULL );
+ *ShareType = RESOURCETYPE_ANY;
+ if ( CredentialExtension ) {
+ *CredentialExtension = FALSE;
+ }
+
+ DebugTrace(+1, Dbg, "ReadAttachEas....\n", 0);
+
+ if ( EaBuffer != NULL) {
+
+ while (TRUE) {
+ ULONG EaNameLength = EaBuffer->EaNameLength;
+
+ if (strcmp(EaBuffer->EaName, EA_NAME_USERNAME) == 0) {
+
+ UserName->Length = EaBuffer->EaValueLength;
+ UserName->MaximumLength = EaBuffer->EaValueLength;
+ UserName->Buffer = (PWSTR)(EaBuffer->EaName+EaNameLength+1);
+
+ } else if (strcmp(EaBuffer->EaName, EA_NAME_PASSWORD) == 0) {
+
+ Password->Length = EaBuffer->EaValueLength;
+ Password->MaximumLength = EaBuffer->EaValueLength;
+ Password->Buffer = (PWSTR)(EaBuffer->EaName+EaNameLength+1);
+
+ } else if (strcmp(EaBuffer->EaName, EA_NAME_TYPE) == 0) {
+
+ *ShareType = *(ULONG UNALIGNED *)(EaBuffer->EaName+EaNameLength+1);
+
+ } else if (strcmp(EaBuffer->EaName, EA_NAME_CREDENTIAL_EX) == 0) {
+
+ if ( CredentialExtension ) {
+ *CredentialExtension = TRUE;
+ DebugTrace(0, Dbg, "ReadAttachEas signals a credential extension.\n", 0 );
+ }
+
+ } else {
+ DebugTrace(0, Dbg, "ReadAttachEas Unknown EA -> %s\n", EaBuffer->EaName);
+ }
+
+ if (EaBuffer->NextEntryOffset == 0) {
+ break;
+ } else {
+ EaBuffer = (PFILE_FULL_EA_INFORMATION) ((PCHAR) EaBuffer+EaBuffer->NextEntryOffset);
+ }
+ }
+ }
+
+ DebugTrace(-1, Dbg, "ReadAttachEas -> %08lx\n", STATUS_SUCCESS);
+
+ return STATUS_SUCCESS;
+
+}
+
+
+IO_STATUS_BLOCK
+OpenRedirector(
+ IN PIRP_CONTEXT IrpContext,
+ ULONG DesiredAccess,
+ ULONG ShareAccess,
+ PFILE_OBJECT FileObject
+ )
+
+/*++
+
+Routine Description:
+
+ This routines opens a handle to the redirector device.
+
+Arguments:
+
+ IrpContext - Supplies all the information
+
+ DesiredAccess - The requested access to the redirector.
+
+ ShareAccess - The requested share access to the redirector.
+
+ FileObject - A pointer to the caller file object.
+
+Return Value:
+
+ IO_STATUS_BLOCK - Status of operation
+
+--*/
+
+{
+ IO_STATUS_BLOCK iosb;
+
+ PAGED_CODE();
+
+ //
+ // Note that the object manager will only allow an administrator
+ // to open the redir itself. This is good.
+ //
+
+ DebugTrace(+1, Dbg, "NwOpenRedirector\n", 0);
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ try {
+
+ //
+ // Set the new share access
+ //
+
+ if (!NT_SUCCESS(iosb.Status = IoCheckShareAccess( DesiredAccess,
+ ShareAccess,
+ FileObject,
+ &NwRcb.ShareAccess,
+ TRUE ))) {
+
+ DebugTrace(0, Dbg, "bad share access\n", 0);
+
+ try_return( NOTHING );
+ }
+
+ NwSetFileObject( FileObject, NULL, &NwRcb );
+ ++NwRcb.OpenCount;
+
+ //
+ // Set the return status.
+ //
+
+ iosb.Status = STATUS_SUCCESS;
+ iosb.Information = FILE_OPENED;
+
+ try_exit: NOTHING;
+ } finally {
+
+ NwReleaseRcb( &NwRcb );
+ DebugTrace(-1, Dbg, "NwOpenRedirector -> Iosb.Status = %08lx\n", iosb.Status);
+
+ }
+
+ //
+ // Return to the caller.
+ //
+
+ return iosb;
+}
+
+
+IO_STATUS_BLOCK
+CreateRemoteFile(
+ IN PIRP_CONTEXT IrpContext,
+ IN PUNICODE_STRING DriveName
+ )
+/*++
+
+Routine Description:
+
+ This routines opens a remote file or directory.
+
+Arguments:
+
+ IrpContext - Supplies all the information
+
+ DriveName - The drive name. One of three forms X:, LPTx, or NULL.
+
+Return Value:
+
+ IO_STATUS_BLOCK - Status of operation
+
+--*/
+{
+ IO_STATUS_BLOCK Iosb;
+ PIRP Irp;
+ PIO_STACK_LOCATION IrpSp;
+
+ ULONG DesiredAccess;
+ ULONG ShareAccess;
+ PFILE_OBJECT FileObject;
+
+ UNICODE_STRING FileName;
+ PFILE_OBJECT RelatedFileObject;
+ ULONG Options;
+ ULONG FileAttributes;
+
+ BOOLEAN CreateDirectory;
+ BOOLEAN OpenDirectory;
+ BOOLEAN DirectoryFile;
+ BOOLEAN NonDirectoryFile;
+ BOOLEAN DeleteOnClose;
+ BOOLEAN OpenTargetDirectory;
+ ULONG AllocationSize;
+
+ // Unhandled open features.
+
+ // PFILE_FULL_EA_INFORMATION EaBuffer;
+ // ULONG EaLength;
+ // BOOLEAN SequentialOnly;
+ // BOOLEAN NoIntermediateBuffering;
+ // BOOLEAN IsPagingFile;
+ // BOOLEAN NoEaKnowledge;
+
+ ULONG CreateDisposition;
+
+ PFCB Fcb = NULL;
+ PICB Icb = NULL;
+ PDCB Dcb;
+ PVCB Vcb = NULL;
+ PSCB Scb;
+
+ BOOLEAN IsAFile;
+ BOOLEAN MayBeADirectory = FALSE;
+ BOOLEAN OwnOpenLock = FALSE;
+ BOOLEAN SetShareAccess = FALSE;
+
+ BYTE SearchFlags;
+ BYTE ShareFlags;
+
+ BOOLEAN CreateTreeConnection;
+ PUNICODE_STRING VolumeName;
+
+ NTSTATUS Status;
+ UNICODE_STRING NdsConnectName;
+ WCHAR ConnectBuffer[MAX_NDS_NAME_CHARS];
+ BOOLEAN MadeUidNdsName = FALSE;
+
+ PAGED_CODE();
+
+ Irp = IrpContext->pOriginalIrp;
+ IrpSp = IoGetCurrentIrpStackLocation( Irp );
+ DesiredAccess = IrpSp->Parameters.Create.SecurityContext->DesiredAccess;
+ ShareAccess = IrpSp->Parameters.Create.ShareAccess;
+ FileObject = IrpSp->FileObject;
+ OpenTargetDirectory = BooleanFlagOn( IrpSp->Flags, SL_OPEN_TARGET_DIRECTORY );
+
+ //
+ // It is ok to attempt a reconnect if this request fails with a
+ // connection error.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_RECONNECTABLE );
+
+
+ try {
+
+ //
+ // Reference our input parameters to make things easier
+ //
+
+ RelatedFileObject = FileObject->RelatedFileObject;
+
+ //
+ // We actually want the parsed file name.
+ // FileName = FileObject->FileName;
+ //
+ FileName = IrpContext->Specific.Create.FullPathName;
+ Options = IrpSp->Parameters.Create.Options;
+ FileAttributes = IrpSp->Parameters.Create.FileAttributes;
+ AllocationSize = Irp->Overlay.AllocationSize.LowPart;
+
+ //
+ // Short circuit an attempt to open a wildcard name.
+ //
+
+ if ( FsRtlDoesNameContainWildCards( &FileName ) ) {
+ try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID );
+ }
+
+ // Decipher Option flags and values
+ //
+
+ DirectoryFile = BooleanFlagOn( Options, FILE_DIRECTORY_FILE );
+ NonDirectoryFile = BooleanFlagOn( Options, FILE_NON_DIRECTORY_FILE );
+ DeleteOnClose = BooleanFlagOn( Options, FILE_DELETE_ON_CLOSE );
+
+ //
+ // Things we currently ignore, because netware servers don't support it.
+ //
+
+ // SequentialOnly = BooleanFlagOn( Options, FILE_SEQUENTIAL_ONLY );
+ // NoIntermediateBuffering = BooleanFlagOn( Options, FILE_NO_INTERMEDIATE_BUFFERING );
+ // NoEaKnowledge = BooleanFlagOn( Options, FILE_NO_EA_KNOWLEDGE );
+ // EaBuffer = Irp->AssociatedIrp.SystemBuffer;
+ // EaLength = IrpSp->Parameters.Create.EaLength;
+ // IsPagingFile = BooleanFlagOn( IrpSp->Flags, SL_OPEN_PAGING_FILE );
+
+ if ( BooleanFlagOn( Options, FILE_CREATE_TREE_CONNECTION ) ) {
+ CreateDisposition = FILE_OPEN;
+ } else {
+ CreateDisposition = (Options >> 24) & 0x000000ff;
+ }
+
+ CreateDirectory = (BOOLEAN)(DirectoryFile &&
+ ((CreateDisposition == FILE_CREATE) ||
+ (CreateDisposition == FILE_OPEN_IF)));
+
+ OpenDirectory = (BOOLEAN)(DirectoryFile &&
+ ((CreateDisposition == FILE_OPEN) ||
+ (CreateDisposition == FILE_OPEN_IF)));
+
+ Dcb = NULL;
+ if ( RelatedFileObject != NULL ) {
+
+ PNONPAGED_DCB NonPagedDcb;
+
+ NonPagedDcb = RelatedFileObject->FsContext;
+ Dcb = NonPagedDcb->Fcb;
+
+ //
+ // If there is a related file object then this is a relative open
+ // and it better be a DCB.
+ //
+
+ if ( NodeType( Dcb ) != NW_NTC_DCB ) {
+
+ DebugTrace(0, Dbg, "Bad file name\n", 0);
+ Iosb.Status = STATUS_OBJECT_NAME_INVALID;
+ try_return( Iosb );
+ }
+
+
+ //
+ // Obtain SCB pointers.
+ //
+
+ IrpContext->pScb = Dcb->Scb;
+ IrpContext->pNpScb = Dcb->Scb->pNpScb;
+ }
+
+ //
+ // We are about ready to send a packet. Append this IRP context
+ // the SCB workqueue, and wait until it gets to the front.
+ //
+
+ NwAppendToQueueAndWait( IrpContext );
+ ASSERT( IrpContext->pNpScb->Requests.Flink == &IrpContext->NextRequest );
+
+ //
+ // Acquire the Global FCB resource to ensure that one thread
+ // can't access the half created FCB of another thread.
+ //
+
+ NwAcquireOpenLock( );
+ OwnOpenLock = TRUE;
+
+ //
+ // Find the volume for this file.
+ //
+
+ CreateTreeConnection = BooleanFlagOn( Options, FILE_CREATE_TREE_CONNECTION );
+
+ if ( CreateTreeConnection ) {
+ VolumeName = &IrpContext->Specific.Create.FullPathName;
+ } else {
+ VolumeName = &IrpContext->Specific.Create.VolumeName;
+ }
+
+ if ( Dcb == NULL ) {
+
+RetryFindVcb:
+
+ Vcb = NwFindVcb(
+ IrpContext,
+ VolumeName,
+ IrpContext->Specific.Create.ShareType,
+ IrpContext->Specific.Create.DriveLetter,
+ CreateTreeConnection,
+ ( BOOLEAN )( CreateTreeConnection && DeleteOnClose ) );
+
+ if ( Vcb == NULL ) {
+
+ //
+ // If this create failed because we need nds data, get
+ // the data from the ds and resubmit the request.
+ //
+
+ if ( IrpContext->Specific.Create.NdsCreate &&
+ IrpContext->Specific.Create.NeedNdsData ) {
+
+ //
+ // Release the open resource so we can move around.
+ //
+
+ NwReleaseOpenLock( );
+ OwnOpenLock = FALSE;
+
+ //
+ // Take the volume name and build the server/share
+ // connect name.
+ //
+
+ NdsConnectName.Buffer = ConnectBuffer;
+ NdsConnectName.MaximumLength = sizeof( ConnectBuffer );
+ NdsConnectName.Length = 0;
+
+ //
+ // Get the ds information. We may jump servers here.
+ //
+
+ Status = NdsMapObjectToServerShare( IrpContext,
+ &Scb,
+ &NdsConnectName,
+ CreateTreeConnection,
+ &(IrpContext->Specific.Create.dwNdsOid) );
+
+ if( !NT_SUCCESS( Status ) ) {
+ ExRaiseStatus( Status );
+ }
+
+ //
+ // Make sure we are on the scb queue after all the
+ // possible server jumping.
+ //
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ NwAcquireOpenLock( );
+ OwnOpenLock = TRUE;
+
+ //
+ // Prepend the Uid to the server/share name.
+ //
+
+ MergeStrings( &IrpContext->Specific.Create.UidConnectName,
+ &Scb->UnicodeUid,
+ &NdsConnectName,
+ PagedPool );
+
+ MadeUidNdsName = TRUE;
+
+ //
+ // We have the data, so re-do the connect.
+ //
+
+ IrpContext->Specific.Create.NeedNdsData = FALSE;
+ goto RetryFindVcb;
+
+ } else {
+
+ //
+ // If this was an open to delete a tree connect, and we failed
+ // to find the VCB, simply return the error.
+ //
+
+ Iosb.Status = STATUS_BAD_NETWORK_PATH;
+ try_return ( Iosb );
+
+ }
+
+ }
+
+ } else {
+
+ Vcb = Dcb->Vcb;
+ NwReferenceVcb( Vcb );
+
+ }
+
+ ASSERT( Vcb->Scb == IrpContext->pScb );
+
+ //
+ // If this is the target name for a rename then we want to find the
+ // DCB for the parent directory.
+ //
+
+ if (OpenTargetDirectory) {
+
+ Iosb = OpenRenameTarget(IrpContext, Vcb, Dcb, &Icb );
+ if (Icb != NULL) {
+ Fcb = Icb->SuperType.Fcb;
+ }
+ try_return ( Iosb );
+
+ }
+
+ //
+ // Find the FCB for this file. If the FCB exists, we get a
+ // referenced pointer. Otherwise a new FCB is created.
+ //
+
+ Fcb = NwFindFcb( IrpContext->pScb, Vcb, &FileName, Dcb );
+
+ //
+ // Check the share access for this file. The share access
+ // is updated if access is granted.
+ //
+
+ if ( Fcb->IcbCount > 0 ) {
+
+ Iosb.Status = IoCheckShareAccess(
+ DesiredAccess,
+ ShareAccess,
+ FileObject,
+ &Fcb->ShareAccess,
+ TRUE );
+
+ if ( !NT_SUCCESS( Iosb.Status ) ) {
+ try_return( Iosb );
+ }
+
+ } else {
+
+ IoSetShareAccess(
+ DesiredAccess,
+ ShareAccess,
+ FileObject,
+ &Fcb->ShareAccess );
+ }
+
+ SetShareAccess = TRUE;
+
+ //
+ // Now create the ICB.
+ //
+
+ Icb = NwCreateIcb( NW_NTC_ICB, Fcb );
+ Icb->FileObject = FileObject;
+ NwSetFileObject( FileObject, Fcb->NonPagedFcb, Icb );
+
+#ifndef QFE_BUILD
+
+ //
+ // Supply a resource for the modified page write to grab when
+ // writing mem mapped files. We do this because it is imposed
+ // on us by the system, we do not require the resource for any
+ // real serialization.
+ //
+
+ Fcb->NonPagedFcb->Header.Flags = 0;
+ Fcb->NonPagedFcb->Header.Resource = NULL;
+
+#endif
+
+#ifdef NWFASTIO
+ //
+ // Initialize private cache map so that the i/o system will call
+ // our fast path.
+ //
+
+ FileObject->PrivateCacheMap = (PVOID)1;
+#endif
+
+ IrpContext->Icb = Icb;
+
+ //
+ // Allocate an 8 bit PID for this ICB. Use different thread so
+ // each Wow program gets its own id. This is because if the same id
+ // has locks using two handles and closes just one of them the locks
+ // on that handle are not discarded.
+ //
+
+ Iosb.Status = NwMapPid( (ULONG)PsGetCurrentThread(), &Icb->Pid );
+
+ if ( !NT_SUCCESS( Iosb.Status ) ) {
+ try_return( Iosb.Status );
+ }
+
+ //
+ // Try to figure out what it is we're expected to open.
+ //
+
+ Iosb.Status = STATUS_SUCCESS;
+
+ if ( FlagOn( Vcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) {
+
+ //
+ // Opening a print queue job.
+ //
+
+ Iosb = CreatePrintJob( IrpContext, Vcb, Icb, DriveName );
+
+ } else if ( DirectoryFile ||
+ ( Fcb->State == FCB_STATE_OPENED &&
+ Fcb->NodeTypeCode == NW_NTC_DCB ) ) {
+
+ //
+ // Opening a directory.
+ //
+
+ MayBeADirectory = TRUE;
+
+ switch ( CreateDisposition ) {
+
+ case FILE_OPEN:
+ Iosb = ChangeDirectory( IrpContext, Vcb, Icb );
+ break;
+
+ case FILE_CREATE:
+ Iosb = CreateDir( IrpContext, Vcb, Icb );
+ break;
+
+ case FILE_OPEN_IF:
+ Iosb.Status = FileOrDirectoryExists( IrpContext,
+ Vcb,
+ Icb,
+ &Icb->SuperType.Fcb->RelativeFileName,
+ &IsAFile );
+
+ //
+ // If the opener specified a directory, fail this request
+ // if the object is a file.
+ //
+
+ if ( NT_SUCCESS( Iosb.Status ) && IsAFile ) {
+ Iosb.Status = STATUS_OBJECT_PATH_NOT_FOUND;
+ } else if ( !NT_SUCCESS( Iosb.Status )) {
+ Iosb = CreateDir( IrpContext, Vcb, Icb );
+ }
+ break;
+
+ case FILE_SUPERSEDE:
+ case FILE_OVERWRITE:
+ case FILE_OVERWRITE_IF:
+ Iosb.Status = STATUS_INVALID_PARAMETER;
+ break;
+
+ default:
+ KeBugCheck( RDR_FILE_SYSTEM );
+
+ }
+
+ } else {
+
+ SearchFlags = NtAttributesToNwAttributes( FileAttributes );
+ ShareFlags = NtToNwShareFlags( DesiredAccess, ShareAccess );
+
+ IsAFile = NonDirectoryFile ||
+ (Fcb->State == FCB_STATE_OPENED &&
+ Fcb->NodeTypeCode == NW_NTC_FCB );
+ //
+ // Assume we are opening a file. If that fails, and it makes
+ // sense try to open a directory.
+ //
+
+ switch ( CreateDisposition ) {
+
+ case FILE_OPEN:
+
+ //
+ // If the disposition is FILE_OPEN try to avoid an unneeded
+ // open, for some desired access types.
+ //
+
+ switch ( DesiredAccess & ~SYNCHRONIZE ) {
+
+ case FILE_WRITE_ATTRIBUTES:
+ case FILE_READ_ATTRIBUTES:
+ case DELETE:
+
+ Iosb.Status = FileOrDirectoryExists(
+ IrpContext,
+ Vcb,
+ Icb,
+ &Icb->SuperType.Fcb->RelativeFileName,
+ &IsAFile );
+
+ if ( !IsAFile) {
+ MayBeADirectory = TRUE;
+ }
+
+ //
+ // Fail open of read only file for delete access,
+ // since the netware server won't fail the delete.
+ //
+
+ if ( NT_SUCCESS( Iosb.Status ) &&
+ CreateDisposition == DELETE &&
+ FlagOn( Icb->NpFcb->Attributes, NW_ATTRIBUTE_READ_ONLY ) ) {
+
+ Iosb.Status = STATUS_ACCESS_DENIED;
+ }
+
+ if ( ( Iosb.Status == STATUS_OBJECT_NAME_NOT_FOUND ) &&
+ ( (DesiredAccess & ~SYNCHRONIZE) == DELETE ) ) {
+ //
+ // we may not have scan rights. fake the return as OK.
+ // NW allows the delete without scan rights.
+ //
+ Iosb.Status = STATUS_SUCCESS;
+ }
+
+ break;
+
+ default:
+
+ Iosb = OpenFile( IrpContext, Vcb, Icb, SearchFlags, ShareFlags );
+
+ if ( ( Iosb.Status == STATUS_OBJECT_NAME_NOT_FOUND ||
+ Iosb.Status == STATUS_FILE_IS_A_DIRECTORY )
+ && !IsAFile) {
+
+ //
+ // Opener didn't specify file or directory, and open
+ // file failed. So try open directory.
+ //
+
+ Iosb = ChangeDirectory( IrpContext, Vcb, Icb );
+ MayBeADirectory = TRUE;
+
+ } else if ( (Iosb.Status == STATUS_SHARING_VIOLATION) &&
+ ((ShareFlags == (NW_OPEN_FOR_READ | NW_DENY_WRITE)) ||
+ (ShareFlags == (NW_OPEN_FOR_READ)))) {
+
+ //
+ // if the file was already open exclusive (eg. GENERIC_EXECUTE)
+ // then a debugger opening it again for read will fail with
+ // sharing violation. In this case, we will try open exclusive
+ // again to see if that passes.
+ //
+
+ ShareFlags |= NW_OPEN_EXCLUSIVE ;
+ ShareFlags &= ~(NW_DENY_WRITE | NW_DENY_READ);
+ Iosb = OpenFile( IrpContext, Vcb, Icb, SearchFlags, ShareFlags );
+ }
+
+ break;
+
+ }
+
+ break;
+
+ case FILE_CREATE:
+ Iosb = CreateNewFile( IrpContext, Vcb, Icb, SearchFlags, ShareFlags );
+ break;
+
+ case FILE_OPEN_IF:
+ Iosb.Status = FileOrDirectoryExists( IrpContext,
+ Vcb,
+ Icb,
+ &Icb->SuperType.Fcb->RelativeFileName,
+ &IsAFile );
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ Iosb = OpenFile( IrpContext, Vcb, Icb, SearchFlags, ShareFlags );
+ } else {
+ Iosb = CreateNewFile( IrpContext, Vcb, Icb, SearchFlags, ShareFlags );
+ }
+
+ if ( !NT_SUCCESS( Iosb.Status ) && !IsAFile) {
+
+ //
+ // Opener didn't specify file or directory, and open
+ // file and create new file both failed. So try open
+ // or create directory.
+ //
+
+ MayBeADirectory = TRUE;
+ Iosb.Status = FileOrDirectoryExists(
+ IrpContext,
+ Vcb,
+ Icb,
+ &Icb->SuperType.Fcb->RelativeFileName,
+ &IsAFile);
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ Iosb.Information = FILE_OPENED;
+ } else {
+ Iosb = CreateDir( IrpContext, Vcb, Icb );
+ }
+ }
+
+ break;
+
+ //
+ // None of the below make sense for directories so if the
+ // file operation fails, just return the failure status
+ // to the user.
+ //
+
+ case FILE_SUPERSEDE:
+ case FILE_OVERWRITE_IF:
+
+ //
+ // Actually, if Overwrite is chosen, we are supposed to
+ // get the attributes for a file and OR them with the
+ // new attributes.
+ //
+
+ Iosb = CreateOrOverwriteFile( IrpContext, Vcb, Icb, SearchFlags, ShareFlags, FALSE );
+ break;
+
+ case FILE_OVERWRITE:
+ Iosb.Status = FileOrDirectoryExists(
+ IrpContext,
+ Vcb,
+ Icb,
+ &Icb->SuperType.Fcb->RelativeFileName,
+ &IsAFile );
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ Iosb = CreateOrOverwriteFile( IrpContext, Vcb, Icb, SearchFlags, ShareFlags, FALSE );
+ }
+
+ break;
+
+ default:
+ KeBugCheck( RDR_FILE_SYSTEM );
+ }
+
+
+ }
+
+try_exit: NOTHING;
+
+ } finally {
+
+ if ( Vcb != NULL ) {
+ NwDereferenceVcb( Vcb, IrpContext, FALSE );
+ }
+
+ if ( MadeUidNdsName ) {
+ FREE_POOL( IrpContext->Specific.Create.UidConnectName.Buffer );
+ }
+
+ if ( AbnormalTermination() || !NT_SUCCESS( Iosb.Status ) ) {
+
+ //
+ // Remove the share access if necessary
+ //
+
+ if ( SetShareAccess ) {
+ IoRemoveShareAccess( FileObject, &Fcb->ShareAccess );
+ }
+
+ //
+ // Failed to create
+ //
+
+ if ( Icb != NULL ) {
+
+ if ( Icb->Pid != 0 ) {
+ NwUnmapPid( Icb->Pid, NULL );
+ }
+
+ NwDeleteIcb( NULL, Icb );
+ }
+
+ //
+ // If this was a tree connect, derefence the extra
+ // reference on the VCB.
+ //
+
+ if ( CreateTreeConnection && !DeleteOnClose ) {
+ if ( Vcb != NULL ) {
+ NwDereferenceVcb( Vcb, IrpContext, FALSE );
+ }
+ }
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+
+ } else {
+
+ Icb->State = ICB_STATE_OPENED;
+ if ( Fcb->State == FCB_STATE_OPEN_PENDING ) {
+ Fcb->State = FCB_STATE_OPENED;
+ }
+
+ if ( DeleteOnClose && !CreateTreeConnection ) {
+ SetFlag( Fcb->Flags, FCB_FLAGS_DELETE_ON_CLOSE );
+ }
+
+ FileObject->SectionObjectPointer = &Fcb->NonPagedFcb->SegmentObject;
+
+ if ( MayBeADirectory ) {
+
+ //
+ // We successfully opened the file as a directory.
+ // If the DCB is newly created, it will be marked
+ // type FCB, update it.
+ //
+
+ Fcb->NodeTypeCode = NW_NTC_DCB;
+ }
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+
+ }
+
+ if ( OwnOpenLock ) {
+ NwReleaseOpenLock( );
+ }
+
+ }
+
+ return( Iosb );
+}
+
+
+IO_STATUS_BLOCK
+ChangeDirectory(
+ PIRP_CONTEXT IrpContext,
+ PVCB Vcb,
+ PICB Icb
+ )
+/*++
+
+Routine Description:
+
+ This routines sets the directory for a remote drive.
+
+Arguments:
+
+ IrpContext - Supplies all the information
+
+ Vcb - A pointer to the VCB for the remote drive.
+
+ Icb - A pointer to the file we are opening.
+
+Return Value:
+
+ IO_STATUS_BLOCK - Status of operation
+
+--*/
+{
+ IO_STATUS_BLOCK Iosb;
+ PFCB Fcb;
+ BYTE Attributes;
+ BOOLEAN FirstTime = TRUE;
+
+ PAGED_CODE();
+
+ //
+ // No need to send a packet if we are opening the root of the volume.
+ //
+
+ if ( Icb->SuperType.Fcb->RelativeFileName.Length == 0 ) {
+
+ Iosb.Status = STATUS_SUCCESS;
+ Iosb.Information = FILE_OPENED;
+
+ return( Iosb );
+ }
+
+Retry:
+
+ if ( !BooleanFlagOn( Icb->SuperType.Fcb->Flags, FCB_FLAGS_LONG_NAME ) ) {
+
+ Iosb.Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "FwbbJ",
+ NCP_SEARCH_FILE,
+ -1,
+ Vcb->Specific.Disk.Handle,
+ SEARCH_ALL_DIRECTORIES,
+ &Icb->SuperType.Fcb->RelativeFileName );
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ Iosb.Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N==_b",
+ 14,
+ &Attributes );
+ }
+
+
+ } else {
+
+ Iosb.Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "LbbWDbDbC",
+ NCP_LFN_GET_INFO,
+ Vcb->Specific.Disk.LongNameSpace,
+ Vcb->Specific.Disk.LongNameSpace,
+ SEARCH_ALL_DIRECTORIES,
+ LFN_FLAG_INFO_ATTRIBUTES |
+ LFN_FLAG_INFO_MODIFY_TIME,
+ Vcb->Specific.Disk.VolumeNumber,
+ Vcb->Specific.Disk.Handle,
+ 0,
+ &Icb->SuperType.Fcb->RelativeFileName );
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ Iosb.Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N_b",
+ 4,
+ &Attributes );
+ }
+
+ //
+ // Unfortunately, this succeeds even if the file in question
+ // is not a directory.
+ //
+
+ if ( NT_SUCCESS( Iosb.Status ) &&
+ ( !FlagOn( Attributes, NW_ATTRIBUTE_DIRECTORY ) ) ) {
+
+ Iosb.Status = STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+ }
+
+ if ((Iosb.Status == STATUS_INVALID_HANDLE) &&
+ (FirstTime)) {
+
+ //
+ // Check to see if Volume handle is invalid. Caused when volume
+ // is unmounted and then remounted.
+ //
+
+ FirstTime = FALSE;
+
+ NwReopenVcbHandle( IrpContext, Vcb );
+
+ goto Retry;
+ }
+
+ Fcb = Icb->SuperType.Fcb;
+
+ Fcb->NonPagedFcb->Attributes = (UCHAR)Attributes;
+ SetFlag( Fcb->Flags, FCB_FLAGS_ATTRIBUTES_ARE_VALID );
+
+ //
+ // Set information field assuming success. It will be ignored
+ // if the NCP failed.
+ //
+
+ Iosb.Information = FILE_OPENED;
+
+ if ( Iosb.Status == STATUS_UNSUCCESSFUL ) {
+ Iosb.Status = STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+
+ return( Iosb );
+}
+
+
+IO_STATUS_BLOCK
+CreateDir(
+ PIRP_CONTEXT IrpContext,
+ PVCB Vcb,
+ PICB Icb
+ )
+/*++
+
+Routine Description:
+
+ This routines create a new directory.
+
+Arguments:
+
+ IrpContext - Supplies all the information
+
+ Vcb - A pointer to the VCB for the remote drive.
+
+Return Value:
+
+ IO_STATUS_BLOCK - Status of operation
+
+--*/
+{
+ IO_STATUS_BLOCK Iosb;
+
+ PAGED_CODE();
+
+ if ( Icb->SuperType.Fcb->RelativeFileName.Length == 0 ) {
+ Iosb.Status = STATUS_ACCESS_DENIED;
+ return( Iosb );
+ }
+
+ if ( !BooleanFlagOn( Icb->SuperType.Fcb->Flags, FCB_FLAGS_LONG_NAME ) ) {
+
+ if (!IsFatNameValid(&Icb->SuperType.Fcb->RelativeFileName)) {
+
+ Iosb.Status = STATUS_OBJECT_PATH_SYNTAX_BAD;
+
+ return( Iosb );
+
+ }
+
+ Iosb.Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "SbbJ",
+ NCP_DIR_FUNCTION, NCP_CREATE_DIRECTORY,
+ Vcb->Specific.Disk.Handle,
+ 0xFF,
+ &Icb->SuperType.Fcb->RelativeFileName );
+
+ } else {
+
+ Iosb.Status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "LbbWDDWbDbC",
+ NCP_LFN_OPEN_CREATE,
+ Vcb->Specific.Disk.LongNameSpace,
+ LFN_FLAG_OM_CREATE,
+ 0, // Search Flags,
+ 0, // Return Info Mask
+ NW_ATTRIBUTE_DIRECTORY,
+ 0x00ff, // Desired access
+ Vcb->Specific.Disk.VolumeNumber,
+ Vcb->Specific.Disk.Handle,
+ 0, // Short directory flag
+ &Icb->SuperType.Fcb->RelativeFileName );
+
+ }
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ Iosb.Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N" );
+ }
+
+ //
+ // Set information field assuming success. It will be ignored
+ // if the NCP failed.
+ //
+
+ Iosb.Information = FILE_CREATED;
+
+ if ( Iosb.Status == STATUS_UNSUCCESSFUL ) {
+ Iosb.Status = STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ return( Iosb );
+}
+
+
+NTSTATUS
+FileOrDirectoryExists(
+ IN PIRP_CONTEXT IrpContext,
+ IN PVCB Vcb,
+ IN PICB Icb OPTIONAL,
+ PUNICODE_STRING Name,
+ OUT PBOOLEAN IsAFile
+ )
+/*++
+
+Routine Description:
+
+ This routines looks to see if a file or directory exists.
+
+Arguments:
+
+ IrpContext - Supplies allx the information
+
+ Vcb - A pointer to the VCB for the remote drive.
+
+ Icb - A pointer to the ICB for the file we are looking for.
+
+ Name - Fully qualified name.
+
+ IsAFile - Returns TRUE is the found file is a file, FALSE if it is
+ a directory. Return nothing if the function returns FALSE.
+
+Return Value:
+
+ IO_STATUS_BLOCK - Status of operation
+
+--*/
+{
+ ULONG Attributes;
+ ULONG FileSize;
+ USHORT LastModifiedDate;
+ USHORT LastModifiedTime;
+ USHORT CreationDate;
+ USHORT CreationTime = DEFAULT_TIME;
+ USHORT LastAccessDate;
+ NTSTATUS Status;
+ PFCB Fcb;
+ BOOLEAN FirstTime = TRUE;
+
+ PAGED_CODE();
+
+ //
+ // No need to send a packet if we are searching for the root of the volume.
+ //
+
+ if ( Name->Length == 0 ) {
+ *IsAFile = FALSE;
+
+ return( STATUS_SUCCESS );
+ }
+
+ //
+ // Decide how to handle this request. If we have an ICB, use the FCB
+ // to determine the file name type, otherwise we have to make the
+ // decision here.
+ //
+
+ if ( Icb != NULL &&
+ !BooleanFlagOn( Icb->SuperType.Fcb->Flags, FCB_FLAGS_LONG_NAME ) ||
+
+ Vcb->Specific.Disk.LongNameSpace == LFN_NO_OS2_NAME_SPACE ||
+
+ IsFatNameValid( Name ) ) {
+Retry:
+ //
+ // First try a file
+ //
+
+ IrpContext->ResponseLength = 0;
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "FwbbJ",
+ NCP_SEARCH_FILE,
+ -1,
+ Vcb->Specific.Disk.Handle,
+ SEARCH_ALL_FILES,
+ Name );
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N==_b-dwwww",
+ 14,
+ &Attributes,
+ &FileSize,
+ &CreationDate,
+ &LastAccessDate,
+ &LastModifiedDate,
+ &LastModifiedTime );
+ }
+
+ if ((Status == STATUS_INVALID_HANDLE) &&
+ (FirstTime)) {
+
+ //
+ // Check to see if Volume handle is invalid. Caused when volume
+ // is unmounted and then remounted.
+ //
+
+ FirstTime = FALSE;
+
+ NwReopenVcbHandle( IrpContext, Vcb );
+
+ goto Retry;
+ }
+
+ if ( Status == STATUS_UNSUCCESSFUL ) {
+
+ //
+ // Not a file, Is it a directory?
+ //
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "FwbbJ",
+ NCP_SEARCH_FILE,
+ -1,
+ Vcb->Specific.Disk.Handle,
+ SEARCH_ALL_DIRECTORIES,
+ Name );
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N==_b",
+ 14,
+ &Attributes );
+ }
+
+ //
+ // If the exchange or ParseResponse fails then exit with not found
+ //
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return( STATUS_OBJECT_NAME_NOT_FOUND );
+ }
+
+ *IsAFile = FALSE;
+ ASSERT( (Attributes & NW_ATTRIBUTE_DIRECTORY) != 0 );
+
+ } else {
+
+ if ( Status == STATUS_UNEXPECTED_NETWORK_ERROR &&
+ IrpContext->ResponseLength >= sizeof( NCP_RESPONSE ) ) {
+
+ //
+ // Work-around for netware bug. If netware returns short
+ // packet, just return success. We exit prematurely
+ // because we have no attributes to record.
+ //
+
+ Icb = NULL;
+ *IsAFile = TRUE;
+ return ( STATUS_SUCCESS );
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return( Status );
+ }
+
+ *IsAFile = TRUE;
+ ASSERT( ( Attributes & NW_ATTRIBUTE_DIRECTORY ) == 0 );
+
+ }
+
+ } else {
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "LbbWDbDbC",
+ NCP_LFN_GET_INFO,
+ Vcb->Specific.Disk.LongNameSpace,
+ Vcb->Specific.Disk.LongNameSpace,
+ SEARCH_ALL_DIRECTORIES,
+ LFN_FLAG_INFO_ATTRIBUTES |
+ LFN_FLAG_INFO_FILE_SIZE |
+ LFN_FLAG_INFO_MODIFY_TIME |
+ LFN_FLAG_INFO_CREATION_TIME,
+ Vcb->Specific.Disk.VolumeNumber,
+ Vcb->Specific.Disk.Handle,
+ 0,
+ Name );
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N_e=e_xx_xx_x",
+ 4,
+ &Attributes,
+ &FileSize,
+ 6,
+ &CreationTime,
+ &CreationDate,
+ 4,
+ &LastModifiedTime,
+ &LastModifiedDate,
+ 4,
+ &LastAccessDate );
+ }
+
+ //
+ // If the exchange or ParseResponse fails then exit with not found
+ //
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return( STATUS_OBJECT_NAME_NOT_FOUND );
+ }
+
+ if ( Attributes & NW_ATTRIBUTE_DIRECTORY) {
+ *IsAFile = FALSE;
+ } else {
+ *IsAFile = TRUE;
+ }
+ }
+
+ //
+ // If the caller supplied an ICB, update the FCB attributes.
+ // We'll use this info if the caller does a query attributes
+ // on the ICB.
+ //
+
+ if ( Icb != NULL && *IsAFile ) {
+
+ Fcb = Icb->SuperType.Fcb;
+ ASSERT( Fcb->NodeTypeCode == NW_NTC_FCB );
+
+ Fcb->NonPagedFcb->Attributes = (UCHAR)Attributes;
+ Fcb->NonPagedFcb->Header.FileSize.QuadPart = FileSize;
+ Fcb->LastModifiedDate = LastModifiedDate;
+ Fcb->LastModifiedTime = LastModifiedTime;
+ Fcb->CreationTime = CreationTime;
+ Fcb->CreationDate = CreationDate;
+ Fcb->LastAccessDate = LastAccessDate;
+
+ DebugTrace( 0, Dbg, "Attributes -> %08lx\n", Fcb->NonPagedFcb->Attributes );
+ DebugTrace( 0, Dbg, "FileSize.Low-> %08lx\n", Fcb->NonPagedFcb->Header.FileSize.LowPart );
+ DebugTrace( 0, Dbg, "ModifiedDate-> %08lx\n", Fcb->LastModifiedDate );
+ DebugTrace( 0, Dbg, "ModifiedTime-> %08lx\n", Fcb->LastModifiedTime );
+ DebugTrace( 0, Dbg, "CreationTime-> %08lx\n", Fcb->CreationTime );
+ DebugTrace( 0, Dbg, "CreationDate-> %08lx\n", Fcb->CreationDate );
+ DebugTrace( 0, Dbg, "LastAccDate -> %08lx\n", Fcb->LastAccessDate );
+
+ SetFlag( Fcb->Flags, FCB_FLAGS_ATTRIBUTES_ARE_VALID );
+ }
+
+ return( STATUS_SUCCESS );
+}
+
+
+IO_STATUS_BLOCK
+OpenFile(
+ IN PIRP_CONTEXT IrpContext,
+ IN PVCB Vcb,
+ IN PICB Icb,
+ IN BYTE Attributes,
+ IN BYTE OpenFlags
+ )
+/*++
+
+Routine Description:
+
+ This routines sets opens a file on a netware server. It fails if
+ the file does not exist.
+
+Arguments:
+
+ IrpContext - Supplies all the information
+
+ Vcb - A pointer to the VCB for the remote drive.
+
+ Icb - A pointer to the ICB we are opening.
+
+ Attributes - Open attributes.
+
+ OpenFlags - Open mode and sharing mode flags.
+
+Return Value:
+
+ IO_STATUS_BLOCK - Status of operation
+
+--*/
+{
+ IO_STATUS_BLOCK Iosb;
+ PFCB Fcb;
+
+ PAGED_CODE();
+
+ //
+ // No need to send a packet if we are trying to open the root of
+ // the volume as a file.
+ //
+
+ if ( Icb->SuperType.Fcb->RelativeFileName.Length == 0 ) {
+ Iosb.Status = STATUS_FILE_IS_A_DIRECTORY;
+ return( Iosb );
+ }
+
+ Fcb = Icb->SuperType.Fcb;
+ ASSERT( NodeType( Fcb ) == NW_NTC_FCB );
+
+ //
+ // Send the open request and wait for the response.
+ //
+
+ if ( !BooleanFlagOn( Fcb->Flags, FCB_FLAGS_LONG_NAME ) ) {
+
+ Iosb.Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "FbbbJ",
+ NCP_OPEN_FILE,
+ Vcb->Specific.Disk.Handle,
+ SEARCH_ALL_FILES,
+ OpenFlags,
+ &Icb->SuperType.Fcb->RelativeFileName );
+
+ if ( ( ReadExecOnlyFiles ) &&
+ ( !NT_SUCCESS( Iosb.Status ) ) ) {
+
+ //
+ // Retry the open with the appropriate flags for
+ // execute only files.
+ //
+
+ Iosb.Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "FbbbJ",
+ NCP_OPEN_FILE,
+ Vcb->Specific.Disk.Handle,
+ SEARCH_EXEC_ONLY_FILES,
+ OpenFlags,
+ &Icb->SuperType.Fcb->RelativeFileName );
+ }
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ Iosb.Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nr=_b-dwwww",
+ Icb->Handle,
+ sizeof( Icb->Handle ),
+ 14,
+ &Fcb->NonPagedFcb->Attributes,
+ &Fcb->NonPagedFcb->Header.FileSize,
+ &Fcb->CreationDate,
+ &Fcb->LastAccessDate,
+ &Fcb->LastModifiedDate,
+ &Fcb->LastModifiedTime );
+
+ Fcb->CreationTime = DEFAULT_TIME;
+
+ }
+
+ } else {
+
+ Iosb.Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "LbbWDDWbDbC",
+ NCP_LFN_OPEN_CREATE,
+ Vcb->Specific.Disk.LongNameSpace,
+ LFN_FLAG_OM_OPEN,
+ NW_ATTRIBUTE_HIDDEN | NW_ATTRIBUTE_SYSTEM, // Search Flags,
+ LFN_FLAG_INFO_ATTRIBUTES |
+ LFN_FLAG_INFO_FILE_SIZE |
+ LFN_FLAG_INFO_MODIFY_TIME |
+ LFN_FLAG_INFO_CREATION_TIME,
+ 0, // Create attributes
+ OpenFlags, // Desired access
+ Vcb->Specific.Disk.VolumeNumber,
+ Vcb->Specific.Disk.Handle,
+ 0, // Short directory flag
+ &Icb->SuperType.Fcb->RelativeFileName );
+
+ if ( ( ReadExecOnlyFiles ) &&
+ ( !NT_SUCCESS( Iosb.Status ) ) ) {
+
+ Iosb.Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "LbbWDDWbDbC",
+ NCP_LFN_OPEN_CREATE,
+ Vcb->Specific.Disk.LongNameSpace,
+ LFN_FLAG_OM_OPEN,
+ NW_ATTRIBUTE_EXEC_ONLY,
+ LFN_FLAG_INFO_ATTRIBUTES |
+ LFN_FLAG_INFO_FILE_SIZE |
+ LFN_FLAG_INFO_MODIFY_TIME |
+ LFN_FLAG_INFO_CREATION_TIME,
+ 0, // Create attributes
+ OpenFlags, // Desired access
+ Vcb->Specific.Disk.VolumeNumber,
+ Vcb->Specific.Disk.Handle,
+ 0, // Short directory flag
+ &Icb->SuperType.Fcb->RelativeFileName );
+ }
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ Iosb.Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Ne_e=e_xx_xx_x",
+ &Icb->Handle[2],
+ 6,
+ &Fcb->NonPagedFcb->Attributes,
+ &Fcb->NonPagedFcb->Header.FileSize,
+ 6,
+ &Fcb->CreationTime,
+ &Fcb->CreationDate,
+ 4,
+ &Fcb->LastModifiedTime,
+ &Fcb->LastModifiedDate,
+ 4,
+ &Fcb->LastAccessDate );
+ }
+ }
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+
+ //
+ // NT does not allow you to open a read only file for write access.
+ // Netware does. To fake NT semantics, check to see if we should
+ // fail the open that the netware server just succeeded.
+ //
+
+ if ( ( Fcb->NonPagedFcb->Attributes & NW_ATTRIBUTE_READ_ONLY ) &&
+ ( OpenFlags & NW_OPEN_FOR_WRITE ) ) {
+
+ CloseFile( IrpContext, Icb );
+ Iosb.Status = STATUS_ACCESS_DENIED;
+ }
+
+ SetFlag( Fcb->Flags, FCB_FLAGS_ATTRIBUTES_ARE_VALID );
+ Icb->HasRemoteHandle = TRUE;
+
+
+ DebugTrace( 0, Dbg, "Attributes -> %08lx\n", Fcb->NonPagedFcb->Attributes );
+ DebugTrace( 0, Dbg, "FileSize.Low-> %08lx\n", Fcb->NonPagedFcb->Header.FileSize.LowPart );
+ DebugTrace( 0, Dbg, "ModifiedDate-> %08lx\n", Fcb->LastModifiedDate );
+ DebugTrace( 0, Dbg, "ModifiedTime-> %08lx\n", Fcb->LastModifiedTime );
+ DebugTrace( 0, Dbg, "CreationDate-> %08lx\n", Fcb->CreationDate );
+ DebugTrace( 0, Dbg, "CreationTime-> %08lx\n", Fcb->CreationTime );
+ DebugTrace( 0, Dbg, "LastAccDate -> %08lx\n", Fcb->LastAccessDate );
+
+ }
+
+ //
+ // Set information field assuming success. It will be ignored
+ // if the NCP failed.
+ //
+
+ Iosb.Information = FILE_OPENED;
+
+ if ( Iosb.Status == STATUS_UNSUCCESSFUL ) {
+ Iosb.Status = STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ return( Iosb );
+}
+
+
+IO_STATUS_BLOCK
+CreateNewFile(
+ IN PIRP_CONTEXT IrpContext,
+ IN PVCB Vcb,
+ IN PICB Icb,
+ IN BYTE CreateAttributes,
+ IN BYTE OpenFlags
+ )
+/*++
+
+Routine Description:
+
+ This routines creates a new file on a netware server. It fails
+ if the file exists.
+
+Arguments:
+
+ IrpContext - Supplies all the information
+
+ Vcb - A pointer to the VCB for the remote drive.
+
+ Icb - A pointer to the ICB we are opening.
+
+ CreateAttributes - Create attributes.
+
+ OpenFlags - Open mode and sharing mode flags.
+
+Return Value:
+
+ IO_STATUS_BLOCK - Status of operation
+
+--*/
+{
+ IO_STATUS_BLOCK Iosb;
+ PFCB Fcb;
+ UCHAR DelayedAttributes;
+ BOOLEAN CloseAndReopen;
+
+ PAGED_CODE();
+
+ //
+ // If the user opens the file for shared access, then we will need to
+ // create the file close, then reopen it (since we have no NCP to say
+ // create with shared access). If the file is being created read-only,
+ // and the creator requests write access then we pull the additional
+ // trick of creating the file without the read-only, and set it later,
+ // so that the second open can succeed.
+ //
+
+ CloseAndReopen = FALSE;
+ DelayedAttributes = 0;
+
+ if ( OpenFlags != NW_OPEN_EXCLUSIVE ) {
+ CloseAndReopen = TRUE;
+
+ if ( ( CreateAttributes & NW_ATTRIBUTE_READ_ONLY ) &&
+ ( OpenFlags & NW_OPEN_FOR_WRITE ) ) {
+
+ DelayedAttributes = CreateAttributes;
+ CreateAttributes = 0;
+ }
+ }
+
+ //
+ // Send the create request and wait for the response.
+ //
+
+ Fcb = Icb->SuperType.Fcb;
+
+ if ( !BooleanFlagOn( Fcb->Flags, FCB_FLAGS_LONG_NAME ) ) {
+
+ if (!IsFatNameValid(&Icb->SuperType.Fcb->RelativeFileName)) {
+
+ Iosb.Status = STATUS_OBJECT_PATH_SYNTAX_BAD;
+
+ return( Iosb );
+
+ }
+
+ Iosb.Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "FbbJ", // NCP Create New File
+ NCP_CREATE_NEW_FILE,
+ Vcb->Specific.Disk.Handle,
+ CreateAttributes,
+ &Icb->SuperType.Fcb->RelativeFileName );
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ Iosb.Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nr=_b-dwwww",
+ Icb->Handle, sizeof( Icb->Handle ),
+ 14,
+ &Fcb->NonPagedFcb->Attributes,
+ &Fcb->NonPagedFcb->Header.FileSize,
+ &Fcb->CreationDate,
+ &Fcb->LastAccessDate,
+ &Fcb->LastModifiedDate,
+ &Fcb->LastModifiedTime );
+
+ Fcb->CreationTime = DEFAULT_TIME;
+
+ }
+
+ } else {
+
+ Iosb.Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "LbbWDDWbDbC",
+ NCP_LFN_OPEN_CREATE,
+ Vcb->Specific.Disk.LongNameSpace,
+ LFN_FLAG_OM_CREATE,
+ 0, // Search Flags
+ LFN_FLAG_INFO_ATTRIBUTES |
+ LFN_FLAG_INFO_FILE_SIZE |
+ LFN_FLAG_INFO_MODIFY_TIME |
+ LFN_FLAG_INFO_CREATION_TIME,
+ CreateAttributes,
+ 0, // Desired access
+ Vcb->Specific.Disk.VolumeNumber,
+ Vcb->Specific.Disk.Handle,
+ 0, // Short directory flag
+ &Icb->SuperType.Fcb->RelativeFileName );
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ Iosb.Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Ne_e=e_xx_xx_x",
+ &Icb->Handle[2],
+ 6,
+ &Fcb->NonPagedFcb->Attributes,
+ &Fcb->NonPagedFcb->Header.FileSize,
+ 6,
+ &Fcb->CreationTime,
+ &Fcb->CreationDate,
+ 4,
+ &Fcb->LastModifiedTime,
+ &Fcb->LastModifiedDate,
+ 4,
+ &Fcb->LastAccessDate );
+ }
+ }
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ SetFlag( Fcb->Flags, FCB_FLAGS_ATTRIBUTES_ARE_VALID );
+ Icb->HasRemoteHandle = TRUE;
+ DebugTrace( 0, Dbg, "Attributes -> %08lx\n", Fcb->NonPagedFcb->Attributes );
+ DebugTrace( 0, Dbg, "FileSize.Low-> %08lx\n", Fcb->NonPagedFcb->Header.FileSize.LowPart );
+ DebugTrace( 0, Dbg, "ModifiedDate-> %08lx\n", Fcb->LastModifiedDate );
+ DebugTrace( 0, Dbg, "ModifiedTime-> %08lx\n", Fcb->LastModifiedTime );
+ DebugTrace( 0, Dbg, "CreationDate-> %08lx\n", Fcb->CreationDate );
+ DebugTrace( 0, Dbg, "CreationTime-> %08lx\n", Fcb->CreationTime );
+ DebugTrace( 0, Dbg, "LastAcceDate-> %08lx\n", Fcb->LastAccessDate );
+ }
+
+ if ( Iosb.Status == STATUS_UNSUCCESSFUL ) {
+ Iosb.Status = STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ if ( !NT_SUCCESS( Iosb.Status ) ) {
+ return( Iosb );
+ }
+
+
+ //
+ // We've created the file, and the users wants shared access to the
+ // file. Close the file and reopen in sharing mode.
+ //
+
+ if ( CloseAndReopen ) {
+ CloseFile( IrpContext, Icb );
+ Iosb = OpenFile( IrpContext, Vcb, Icb, CreateAttributes, OpenFlags );
+ }
+
+ //
+ // If we need to set attributes, do it now. Ignore errors, if any.
+ //
+
+ if ( DelayedAttributes != 0 ) {
+
+ ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "FbbbU",
+ NCP_SET_FILE_ATTRIBUTES,
+ DelayedAttributes,
+ Fcb->Vcb->Specific.Disk.Handle,
+ SEARCH_ALL_FILES,
+ &Fcb->RelativeFileName );
+
+ }
+
+ //
+ // Set information field assuming success. It will be ignored
+ // if the NCP failed.
+ //
+
+ Iosb.Information = FILE_CREATED;
+ return( Iosb );
+}
+
+
+IO_STATUS_BLOCK
+CreateOrOverwriteFile(
+ IN PIRP_CONTEXT IrpContext,
+ IN PVCB Vcb,
+ IN PICB Icb,
+ IN BYTE CreateAttributes,
+ IN BYTE OpenFlags,
+ IN BOOLEAN CreateOperation
+ )
+/*++
+
+Routine Description:
+
+ This routines creates a file on a netware server. If the file
+ exists it is overwritten.
+
+Arguments:
+
+ IrpContext - Supplies all the information
+
+ Vcb - A pointer to the VCB for the remote drive.
+
+ Icb - A pointer to the ICB we are opening.
+
+ Attributes - Open attributes.
+
+ OpenFlags - Open mode and sharing mode flags.
+
+Return Value:
+
+ IO_STATUS_BLOCK - Status of operation
+
+--*/
+{
+ IO_STATUS_BLOCK Iosb;
+ PFCB Fcb;
+ UCHAR DelayedAttributes;
+ BOOLEAN CloseAndReopen;
+
+ PAGED_CODE();
+
+ Fcb = Icb->SuperType.Fcb;
+
+ //
+ // Send the request and wait for the response.
+ //
+
+ if ( !BooleanFlagOn( Fcb->Flags, FCB_FLAGS_LONG_NAME ) ) {
+
+ if (!IsFatNameValid(&Icb->SuperType.Fcb->RelativeFileName)) {
+
+ Iosb.Status = STATUS_OBJECT_PATH_SYNTAX_BAD;
+
+ return( Iosb );
+
+ }
+
+ //
+ // If the user opens the file for shared access, then we will need to
+ // create the file close, then reopen it (since we have no NCP to say
+ // create with shared access). If the file is being created read-only,
+ // and the creator requests write access then we pull the additional
+ // trick of creating the file without the read-only, and set it later,
+ // so that the second open can succeed.
+ //
+
+ if ( ( CreateAttributes & NW_ATTRIBUTE_READ_ONLY ) &&
+ ( OpenFlags & NW_OPEN_FOR_WRITE ) ) {
+
+ DelayedAttributes = CreateAttributes;
+ CreateAttributes = 0;
+ } else {
+ DelayedAttributes = 0;
+ }
+
+ //
+ // Dos namespace create always returns the file exclusive.
+ //
+
+ if (!FlagOn(OpenFlags, NW_OPEN_EXCLUSIVE)) {
+ CloseAndReopen = TRUE;
+ } else {
+ CloseAndReopen = FALSE;
+ }
+
+ Iosb.Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "FbbJ",
+ NCP_CREATE_FILE,
+ Vcb->Specific.Disk.Handle,
+ CreateAttributes,
+ &Icb->SuperType.Fcb->RelativeFileName );
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ Iosb.Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nr=_b-dwwww",
+ Icb->Handle,
+ sizeof( Icb->Handle ),
+ 14,
+ &Fcb->NonPagedFcb->Attributes,
+ &Fcb->NonPagedFcb->Header.FileSize,
+ &Fcb->CreationDate,
+ &Fcb->LastAccessDate,
+ &Fcb->LastModifiedDate,
+ &Fcb->LastModifiedTime );
+
+ Fcb->CreationTime = DEFAULT_TIME;
+
+ }
+
+ //
+ // We've created the file, and the users wants shared access to the
+ // file. Close the file and reopen in sharing mode.
+ //
+
+ if (( NT_SUCCESS( Iosb.Status ) ) &&
+ ( CloseAndReopen )) {
+
+ CloseFile( IrpContext, Icb );
+ Iosb = OpenFile( IrpContext, Vcb, Icb, CreateAttributes, OpenFlags );
+ }
+
+ if ( DelayedAttributes != 0 ) {
+ ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "FbbbU",
+ NCP_SET_FILE_ATTRIBUTES,
+ DelayedAttributes,
+ Fcb->Vcb->Specific.Disk.Handle,
+ SEARCH_ALL_FILES,
+ &Fcb->RelativeFileName );
+ }
+
+ } else {
+
+ Iosb.Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "LbbWDDWbDbC",
+ NCP_LFN_OPEN_CREATE,
+ Vcb->Specific.Disk.LongNameSpace,
+ LFN_FLAG_OM_OVERWRITE,
+ 0, // Search Flags
+ LFN_FLAG_INFO_ATTRIBUTES |
+ LFN_FLAG_INFO_FILE_SIZE |
+ LFN_FLAG_INFO_MODIFY_TIME |
+ LFN_FLAG_INFO_CREATION_TIME,
+ CreateAttributes,
+ OpenFlags, // DesiredAccess
+ Vcb->Specific.Disk.VolumeNumber,
+ Vcb->Specific.Disk.Handle,
+ 0, // Short directory flag
+ &Icb->SuperType.Fcb->RelativeFileName );
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ Iosb.Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Ne_e=e_xx_xx_x",
+ &Icb->Handle[2],
+ 6,
+ &Fcb->NonPagedFcb->Attributes,
+ &Fcb->NonPagedFcb->Header.FileSize,
+ 6,
+ &Fcb->CreationTime,
+ &Fcb->CreationDate,
+ 4,
+ &Fcb->LastModifiedTime,
+ &Fcb->LastModifiedDate,
+ 4,
+ &Fcb->LastAccessDate );
+ }
+ }
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ SetFlag( Fcb->Flags, FCB_FLAGS_ATTRIBUTES_ARE_VALID );
+ Icb->HasRemoteHandle = TRUE;
+ DebugTrace( 0, Dbg, "Attributes -> %08lx\n", Fcb->NonPagedFcb->Attributes );
+ DebugTrace( 0, Dbg, "FileSize.Low-> %08lx\n", Fcb->NonPagedFcb->Header.FileSize.LowPart );
+ DebugTrace( 0, Dbg, "ModifiedDate-> %08lx\n", Fcb->LastModifiedDate );
+ DebugTrace( 0, Dbg, "ModifiedTime-> %08lx\n", Fcb->LastModifiedTime );
+ DebugTrace( 0, Dbg, "CreationDate-> %08lx\n", Fcb->CreationDate );
+ DebugTrace( 0, Dbg, "CreationTime-> %08lx\n", Fcb->CreationTime );
+ DebugTrace( 0, Dbg, "LastAccDate -> %08lx\n", Fcb->LastAccessDate );
+ } else {
+ return( Iosb );
+ }
+
+ //
+ // Set information field assuming success. It will be ignored
+ // if the NCP failed.
+ //
+
+ if ( CreateOperation) {
+ Iosb.Information = FILE_CREATED;
+ } else {
+ Iosb.Information = FILE_OVERWRITTEN;
+ }
+
+ return( Iosb );
+}
+
+
+IO_STATUS_BLOCK
+OpenRenameTarget(
+ IN PIRP_CONTEXT IrpContext,
+ IN PVCB Vcb,
+ IN PDCB Dcb,
+ IN PICB* Icb
+ )
+/*++
+
+Routine Description:
+
+ This routine opens a directory. If the filename provided specifies
+ a directory then the file/directory to be renamed will be put in this
+ directory.
+
+ If the target foo\bar does not exist or is a file then the source of
+ the rename must be a file and will end up in the directory foo with
+ the name bar
+
+Arguments:
+
+ IrpContext - Supplies all the information
+
+ Vcb - A pointer to the VCB for the remote drive.
+
+ Dcb - A pointer to the DCB for relative opens. If NULL the FileName
+ is an full path name. If non NUL the FileName is relative to
+ this directory.
+
+ Icb - A pointer to where the address of the Icb is to be stored.
+
+Return Value:
+
+ NT_STATUS - Status of operation
+
+--*/
+{
+ PIRP Irp;
+ PIO_STACK_LOCATION IrpSp;
+
+ IO_STATUS_BLOCK Iosb;
+ PFCB Fcb;
+ BOOLEAN FullNameIsAFile;
+ BOOLEAN FullNameExists;
+ BOOLEAN PathIsAFile;
+
+#if 0
+ UNICODE_STRING Drive;
+ WCHAR DriveLetter;
+ UNICODE_STRING Server;
+ UNICODE_STRING Volume;
+ UNICODE_STRING FileName;
+#endif
+ UNICODE_STRING Path;
+ UNICODE_STRING FullName;
+ UNICODE_STRING CompleteName;
+ UNICODE_STRING VcbName;
+ PWCH pTrailingSlash;
+
+ USHORT i;
+ USHORT DcbNameLength;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "OpenRenameTarget\n", 0);
+
+ //
+ // Get the current IRP stack location
+ //
+
+ Irp = IrpContext->pOriginalIrp;
+
+ IrpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ //
+ // Build a complete filename of the form \g:\server\volume\dir1\file
+ //
+
+ if ( Dcb != NULL ) {
+
+ //
+ // Strip to UID portion of the DCB name.
+ //
+
+ for ( i = 0 ; i < Dcb->FullFileName.Length / sizeof(WCHAR) ; i++ ) {
+ if ( Dcb->FullFileName.Buffer[i] == OBJ_NAME_PATH_SEPARATOR ) {
+ break;
+ }
+ }
+
+ ASSERT( Dcb->FullFileName.Buffer[i] == OBJ_NAME_PATH_SEPARATOR );
+
+ //
+ // Now build the full name by appending the file name to the DCB name.
+ //
+
+ DcbNameLength = Dcb->FullFileName.Length - ( i * sizeof(WCHAR) );
+ CompleteName.Length = DcbNameLength + IrpSp->FileObject->FileName.Length + sizeof( WCHAR);
+ CompleteName.MaximumLength = CompleteName.Length;
+ CompleteName.Buffer = ALLOCATE_POOL_EX( PagedPool, CompleteName.Length );
+
+ RtlCopyMemory(
+ CompleteName.Buffer,
+ Dcb->FullFileName.Buffer + i,
+ DcbNameLength );
+
+ CompleteName.Buffer[ DcbNameLength / sizeof(WCHAR) ] = L'\\';
+
+ RtlCopyMemory(
+ CompleteName.Buffer + DcbNameLength / sizeof(WCHAR ) + 1,
+ IrpSp->FileObject->FileName.Buffer,
+ IrpSp->FileObject->FileName.Length );
+
+ Dcb = NULL;
+
+ } else {
+
+ CompleteName = IrpSp->FileObject->FileName;
+
+ }
+
+ //
+ // Calculate the VCB name, without the UID prefix.
+ //
+
+ VcbName.Buffer = wcschr( Vcb->Name.Buffer, L'\\' );
+ VcbName.Length = Vcb->Name.Length -
+ ( (PCHAR)VcbName.Buffer - (PCHAR)Vcb->Name.Buffer );
+
+ //
+ // Calculate the target relative name. This is simply the complete
+ // name minus the VcbName and the leading backslash.
+ //
+
+ FullName.Buffer = CompleteName.Buffer + ( VcbName.Length / sizeof(WCHAR) ) + 1;
+ FullName.Length = CompleteName.Length -
+ ( (PCHAR)FullName.Buffer - (PCHAR)CompleteName.Buffer );
+
+ //
+ // Calculate the target directory relative name. This the the target
+ // full name, minus the last component of the name.
+ //
+
+ pTrailingSlash = FullName.Buffer + FullName.Length / sizeof(WCHAR) - 1;
+ for ( i = 0; i < FullName.Length ; i += sizeof(WCHAR) ) {
+ if ( *pTrailingSlash == L'\\' ) {
+ break;
+ }
+ --pTrailingSlash;
+ }
+
+
+ Path.Buffer = FullName.Buffer;
+
+ if ( i == FullName.Length ) {
+
+ //
+ // If no trailing slash was found, the the target path is the
+ // root directory.
+ //
+
+ Path.Length = 0;
+
+ } else {
+
+ Path.Length = (PCHAR)pTrailingSlash - (PCHAR)FullName.Buffer;
+
+ }
+
+#if 0
+ Iosb.Status = CrackPath(
+ &CompleteName,
+ &Drive,
+ &DriveLetter,
+ &Server,
+ &Volume,
+ &Path,
+ &FileName,
+ &FullName );
+#endif
+
+ Iosb.Status = FileOrDirectoryExists( IrpContext,
+ Vcb,
+ NULL,
+ &Path,
+ &PathIsAFile );
+
+ if ( !NT_SUCCESS( Iosb.Status) ) {
+
+ // The directory containing the file does not exist
+
+ return(Iosb);
+ }
+
+ Iosb.Status = FileOrDirectoryExists( IrpContext,
+ Vcb,
+ NULL,
+ &FullName,
+ &FullNameIsAFile );
+
+ if ( !NT_SUCCESS( Iosb.Status ) ) {
+ FullNameExists = FALSE;
+ Iosb.Information = FILE_DOES_NOT_EXIST;
+ } else {
+ FullNameExists = TRUE;
+ Iosb.Information = 0;
+ }
+
+ DebugTrace( 0, Dbg, "FullNameExists = %08lx\n", FullNameExists);
+ DebugTrace( 0, Dbg, "FullNameIsAFile = %08lx\n", FullNameIsAFile);
+
+ try {
+ UNICODE_STRING TargetPath;
+
+ //
+ // Find the FCB for this file. If the FCB exists, we get a
+ // referenced pointer. Otherwise a new FCB is created.
+ // The file is the complete path minus the target filename.
+ //
+
+ TargetPath = CompleteName;
+
+ Fcb = NwFindFcb( IrpContext->pScb, Vcb, &TargetPath, Dcb );
+
+ //
+ // Now create the ICB.
+ //
+
+ *Icb = NwCreateIcb( NW_NTC_ICB, Fcb );
+
+ (*Icb)->FileObject = IrpSp->FileObject;
+ NwSetFileObject( IrpSp->FileObject, Fcb->NonPagedFcb, *Icb );
+ (*Icb)->Exists = FullNameExists;
+ (*Icb)->IsAFile = FullNameIsAFile;
+
+ try_return(Iosb.Status = STATUS_SUCCESS);
+
+try_exit: NOTHING;
+
+ } finally {
+
+
+ if ( AbnormalTermination() || !NT_SUCCESS( Iosb.Status ) ) {
+
+ //
+ // Failed to create
+ //
+
+ if ( *Icb != NULL ) {
+ NwDeleteIcb( NULL, *Icb );
+ *Icb = NULL;
+ }
+ }
+ }
+
+ DebugTrace(-1, Dbg, "OpenRenameTarget\n", Iosb.Status);
+
+ return( Iosb );
+}
+
+
+IO_STATUS_BLOCK
+CreatePrintJob(
+ PIRP_CONTEXT IrpContext,
+ PVCB Vcb,
+ PICB Icb,
+ PUNICODE_STRING DriveName
+ )
+/*++
+
+Routine Description:
+
+ This routines create a new directory.
+
+Arguments:
+
+ IrpContext - Supplies all the information
+
+ Vcb - A pointer to the VCB for the remote print queue.
+
+ Icb - A pointer to the newly created ICB.
+
+ DriveName - LPTx
+
+Return Value:
+
+ IO_STATUS_BLOCK - Status of operation
+
+--*/
+{
+ IO_STATUS_BLOCK Iosb;
+ PFCB Fcb;
+ ANSI_STRING ODriveName;
+ static CHAR LptName[] = "LPT" ;
+
+ PAGED_CODE();
+
+ //
+ // Make sure the print queue name is correct.
+ //
+
+ if ( Icb->SuperType.Fcb->RelativeFileName.Length != 0 ) {
+ Iosb.Status = STATUS_OBJECT_PATH_SYNTAX_BAD;
+ return( Iosb );
+ }
+
+ //
+ // Send a create queue job packet, and wait the response.
+ //
+
+ if ((DriveName->Length == 0 ) ||
+ (!NT_SUCCESS(RtlUnicodeStringToOemString( &ODriveName, DriveName, TRUE )))) {
+ //
+ // if we dont have a name, use the string "LPT". we do this because
+ // some printers insist on a name.
+ //
+
+ RtlInitString(&ODriveName, LptName);
+ }
+
+ Iosb.Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "Sd_ddw_b_r_bbwwww_x-x_", // Format string
+ NCP_ADMIN_FUNCTION, NCP_CREATE_QUEUE_JOB,
+ Vcb->Specific.Print.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
+ ODriveName.Buffer, ODriveName.Length, // Description
+ 50 - ODriveName.Length , // Description pad
+ 0, // Version number
+ 8, // Tab Size
+ 1, // Number of copies
+ NwPrintOptions, // Control Flags
+ 0x3C, // Maximum lines BUGBUG
+ 0x84, // Maximum characters BUGBUG
+ 22, // Skip bytes
+ &IrpContext->pScb->UserName, 12, // Banner Name
+ &Vcb->ShareName, 12, // Header Name
+ 1+14+80 // null last string & skip rest of client area
+ );
+
+ //
+ // free the string if it was allocated
+ //
+ if (ODriveName.Buffer != LptName)
+ RtlFreeAnsiString(&ODriveName);
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+ Iosb.Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N_w_r",
+ 22,
+ &Icb->JobId,
+ 18,
+ Icb->Handle, sizeof(Icb->Handle) );
+ }
+
+ if ( NT_SUCCESS( Iosb.Status ) ) {
+
+ Fcb = Icb->SuperType.Fcb;
+
+ Fcb->NonPagedFcb->Attributes = 0;
+ Fcb->CreationDate = 0;
+ Fcb->LastAccessDate = 0;
+ Fcb->LastModifiedDate = 0;
+ Fcb->LastModifiedTime = 0;
+
+ Icb->HasRemoteHandle = TRUE;
+ Icb->IsPrintJob = TRUE;
+ Icb->ActuallyPrinted = FALSE;
+
+ SetFlag( Fcb->Flags, FCB_FLAGS_ATTRIBUTES_ARE_VALID );
+
+ }
+
+ //
+ // Set information field assuming success. It will be ignored
+ // if the NCP failed.
+ //
+
+ Iosb.Information = FILE_CREATED;
+
+ if ( Iosb.Status == STATUS_UNSUCCESSFUL ) {
+ Iosb.Status = STATUS_OBJECT_NAME_COLLISION;
+ }
+
+
+ return( Iosb );
+}
+
+VOID
+CloseFile(
+ PIRP_CONTEXT pIrpContext,
+ PICB pIcb
+ )
+/*++
+
+Routine Description:
+
+ This routines closes an opened file.
+
+Arguments:
+
+ pIrpContext - Supplies all the information
+
+ pIcb - A pointer to the newly created ICB.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PAGED_CODE();
+
+ ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "F-r",
+ NCP_CLOSE,
+ pIcb->Handle, 6 );
+}
+
diff --git a/private/nw/rdr/create4.c b/private/nw/rdr/create4.c
new file mode 100644
index 000000000..c0e044be0
--- /dev/null
+++ b/private/nw/rdr/create4.c
@@ -0,0 +1,2075 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ create4.c
+
+Abstract:
+
+ This implements the NDS create routines.
+
+Author:
+
+ Cory West [CoryWest] 23-Feb-1995
+
+--*/
+
+#include "Procs.h"
+
+#define Dbg (DEBUG_TRACE_NDS)
+
+//
+// Pageable.
+//
+
+#pragma alloc_text( PAGE, NdsCreateTreeScb )
+#pragma alloc_text( PAGE, ConnectBinderyVolume )
+#pragma alloc_text( PAGE, HandleVolumeAttach )
+#pragma alloc_text( PAGE, NdsGetDsObjectFromPath )
+#pragma alloc_text( PAGE, NdsVerifyObject )
+#pragma alloc_text( PAGE, NdsVerifyContext )
+#pragma alloc_text( PAGE, NdsMapObjectToServerShare )
+
+//
+// Not page-able:
+//
+// NdsSelectConnection (holds a spin lock)
+//
+
+NTSTATUS
+NdsSelectConnection(
+ PIRP_CONTEXT pIrpContext,
+ PUNICODE_STRING puTreeName,
+ PUNICODE_STRING puUserName,
+ PUNICODE_STRING puPassword,
+ BOOL DeferredLogon,
+ BOOL UseBinderyConnections,
+ PNONPAGED_SCB *ppNpScb
+)
+/*++
+
+Routine Description:
+
+ Find a nearby tree connection point for the given tree.
+
+ DeferredLogon tells us whether or not we need to
+ initiate a login/authenticate exchange yet. If we have
+ credentials to a tree, we are NOT allowed to hand off
+ a connection that has not been logged in because the view
+ of the tree may be different from what it is supposed to
+ be.
+
+ UseBinderyConnections tells us whether or not we want
+ to return bindery authenticated connections as valid
+ nds browse points.
+
+Return Value:
+
+ Scb to a server that belongs to the tree we want.
+
+--*/
+{
+
+ NTSTATUS Status = STATUS_BAD_NETWORK_PATH;
+
+ PLOGON pLogon;
+ PLIST_ENTRY ScbQueueEntry;
+ KIRQL OldIrql;
+
+ PNONPAGED_SCB pFirstNpScb, pNextNpScb;
+ PNONPAGED_SCB pFoundNpScb = NULL;
+ PSCB pScb;
+ LARGE_INTEGER Uid;
+
+ PNONPAGED_SCB pOriginalNpScb;
+ PSCB pOriginalScb;
+
+ PNDS_SECURITY_CONTEXT pNdsContext;
+ SECURITY_SUBJECT_CONTEXT SubjectContext;
+ BOOL PasswordExpired = FALSE;
+
+ //
+ // Save the original server pointers.
+ //
+
+ pOriginalNpScb = pIrpContext->pNpScb;
+ pOriginalScb = pIrpContext->pScb;
+
+ Uid = pIrpContext->Specific.Create.UserUid;
+
+ //
+ // Determine if we need a guest browse connection.
+ //
+
+ if ( DeferredLogon ) {
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUser( &Uid, FALSE );
+ NwReleaseRcb( &NwRcb );
+
+ if ( pLogon ) {
+
+ Status = NdsLookupCredentials( puTreeName,
+ pLogon,
+ &pNdsContext,
+ CREDENTIAL_READ,
+ FALSE );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ if ( ( pNdsContext->Credential != NULL ) &&
+ ( pNdsContext->CredentialLocked == FALSE ) ) {
+
+ DebugTrace( 0, Dbg, "Forcing authenticated browse to %wZ.\n", puTreeName );
+ DeferredLogon = FALSE;
+ }
+
+ NwReleaseCredList( pLogon );
+ }
+ }
+ }
+
+ //
+ // Start at the head of the SCB list.
+ //
+
+ KeAcquireSpinLock(&ScbSpinLock, &OldIrql);
+
+ if ( ScbQueue.Flink == &ScbQueue ) {
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql);
+ return STATUS_BAD_NETWORK_PATH;
+ }
+
+ ScbQueueEntry = ScbQueue.Flink;
+ pFirstNpScb = CONTAINING_RECORD( ScbQueueEntry,
+ NONPAGED_SCB,
+ ScbLinks );
+ pNextNpScb = pFirstNpScb;
+
+ //
+ // Leave the first SCB referenced since we need it to
+ // be there for when we walk all the way around the list.
+ //
+
+ NwReferenceScb( pFirstNpScb );
+ NwReferenceScb( pNextNpScb );
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql);
+
+ while ( TRUE ) {
+
+ //
+ // Check to see if the SCB we have is in the correct tree
+ // and is usable. Make sure we skip over the permanent
+ // npscb since it isn't a tree connection. The current
+ // SCB is always referenced while we're in here.
+ //
+
+ if ( pNextNpScb->pScb ) {
+
+ pScb = pNextNpScb->pScb;
+
+ if ( RtlEqualUnicodeString( puTreeName, &pScb->NdsTreeName, TRUE ) &&
+ Uid.QuadPart == pScb->UserUid.QuadPart ) {
+
+ pIrpContext->pNpScb = pNextNpScb;
+ pIrpContext->pScb = pNextNpScb->pScb;
+ NwAppendToQueueAndWait( pIrpContext );
+
+ switch ( pNextNpScb->State ) {
+
+ case SCB_STATE_RECONNECT_REQUIRED:
+
+ //
+ // Reconnect to the server. This is not
+ // a valid path for an anonymous create,
+ // so there's no chance that we'll get
+ // a name collision.
+ //
+
+ Status = ConnectToServer( pIrpContext, NULL );
+
+ if (!NT_SUCCESS(Status)) {
+ break;
+ }
+
+ pNextNpScb->State = SCB_STATE_LOGIN_REQUIRED;
+
+ case SCB_STATE_LOGIN_REQUIRED:
+
+ //
+ // See if we can login if requested.
+ //
+
+ if ( !DeferredLogon ) {
+
+ Status = DoNdsLogon( pIrpContext, puUserName, puPassword );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ break;
+ }
+
+ //
+ // If we get a warning from this, we need to return it!
+ //
+
+ if ( Status == NWRDR_PASSWORD_HAS_EXPIRED ) {
+ PasswordExpired = TRUE;
+ }
+
+ //
+ // Do we have to re-license the connection?
+ //
+
+ if ( ( pScb->VcbCount > 0 ) || ( pScb->OpenNdsStreams > 0 ) ) {
+
+ Status = NdsLicenseConnection( pIrpContext );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ Status = STATUS_REMOTE_SESSION_LIMIT;
+ break;
+ }
+ }
+
+ pNextNpScb->State = SCB_STATE_IN_USE;
+ }
+
+ case SCB_STATE_IN_USE:
+
+ if ( pNextNpScb->State == SCB_STATE_IN_USE ) {
+
+ if ( ( !UseBinderyConnections ) &&
+ ( pNextNpScb->pScb->UserName.Length != 0 ) ) {
+
+ //
+ // We may not want to use a connection that has been
+ // bindery authenticated to read the NDS tree because
+ // we don't have a way to validate that the NDS and
+ // bindery users are the same.
+ //
+
+ Status = STATUS_ACCESS_DENIED;
+ break;
+ }
+
+ //
+ // Verify that we have security rights to this server.
+ //
+
+ Status = CheckScbSecurity( pIrpContext,
+ pNextNpScb->pScb,
+ puUserName,
+ puPassword,
+ ( BOOLEAN )DeferredLogon );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ break;
+ }
+
+ //
+ // Check SCB security might return with state login required.
+ //
+
+ if ( ( pNextNpScb->State == SCB_STATE_LOGIN_REQUIRED ) &&
+ ( !DeferredLogon ) ) {
+
+ Status = DoNdsLogon( pIrpContext, puUserName, puPassword );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ break;
+ }
+
+ pNextNpScb->State = SCB_STATE_IN_USE;
+ }
+
+ } else {
+
+ //
+ // If we picked up an already good SCB and the
+ // login was deferred, set success and continue.
+ //
+
+ ASSERT( DeferredLogon == TRUE );
+ Status = STATUS_SUCCESS;
+ }
+
+ pFoundNpScb = pNextNpScb;
+ DebugTrace( 0, Dbg, "NdsSelectConnection: NpScb = %lx\n", pFoundNpScb );
+ break;
+
+ default:
+
+ break;
+
+ }
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ if ( pFoundNpScb ) {
+ ASSERT( NT_SUCCESS( Status ) );
+ break;
+ }
+
+ if ( Status == STATUS_WRONG_PASSWORD ||
+ Status == STATUS_NO_SUCH_USER ) {
+ NwDereferenceScb( pNextNpScb );
+ break;
+ }
+
+ //
+ // Restore the server pointers.
+ //
+
+ pIrpContext->pNpScb = pOriginalNpScb;
+ pIrpContext->pScb = pOriginalScb;
+
+ }
+ }
+
+ //
+ // Otherwise, get the next one in the list. Don't
+ // forget to skip the list head.
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ ScbQueueEntry = pNextNpScb->ScbLinks.Flink;
+
+ if ( ScbQueueEntry == &ScbQueue ) {
+ ScbQueueEntry = ScbQueue.Flink;
+ }
+
+ NwDereferenceScb( pNextNpScb );
+ pNextNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
+
+ if ( pNextNpScb == pFirstNpScb ) {
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+ Status = STATUS_BAD_NETWORK_PATH;
+ break;
+ }
+
+ //
+ // Otherwise, reference this SCB and continue.
+ //
+
+ NwReferenceScb( pNextNpScb );
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+ }
+
+ NwDereferenceScb( pFirstNpScb );
+ *ppNpScb = pFoundNpScb;
+
+ if ( ( NT_SUCCESS( Status ) ) &&
+ ( PasswordExpired ) ) {
+ Status = NWRDR_PASSWORD_HAS_EXPIRED;
+ }
+
+ return Status;
+}
+
+NTSTATUS
+NdsCreateTreeScb(
+ IN PIRP_CONTEXT pIrpContext,
+ IN OUT PSCB *ppScb,
+ IN PUNICODE_STRING puTree,
+ IN PUNICODE_STRING puUserName,
+ IN PUNICODE_STRING puPassword,
+ IN BOOLEAN DeferredLogon,
+ IN BOOLEAN DeleteOnClose
+)
+/*++
+
+Description:
+
+ Given a tree name, find us a connection point to the tree. This is
+ done by getting the server addresses out of the bindery and looking
+ up the names of the servers for those addresses.
+
+ When we are all done we need to return the preferred connection
+ point in ppScb.
+
+Arguments:
+
+ pIrpContext - irp context for this request
+ ppScb - pointer to a pointer to the scb that we want
+ puTree - tree we want to talk to
+
+--*/
+{
+
+ NTSTATUS Status;
+
+ PLARGE_INTEGER pUid;
+ PNONPAGED_SCB pNpExistingScb;
+
+ UNICODE_STRING UidServerName;
+ PSCB pTreeScb = NULL;
+
+ PSCB pNearestTreeScb = NULL;
+ PNONPAGED_SCB pNpNearestTreeScb = NULL;
+
+ PSCB pNearbyScb = NULL;
+ BOOLEAN fOnNearbyQueue = FALSE;
+ PIRP_CONTEXT pExtraIrpContext = NULL;
+
+ UNICODE_STRING ScanTreeName;
+ WCHAR ScanBuffer[NDS_TREE_NAME_LEN + 2];
+ int i;
+
+ IPXaddress DirServerAddress;
+ CHAR DirServerName[MAX_SERVER_NAME_LENGTH];
+ ULONG dwLastOid = (ULONG)-1;
+
+ UNICODE_STRING CredentialName;
+ PUNICODE_STRING puConnectName;
+
+ PAGED_CODE();
+
+ UidServerName.Buffer = NULL;
+
+ //
+ // Make sure the tree name is reasonable, first.
+ //
+
+ if ( ( !puTree ) ||
+ ( !puTree->Length ) ||
+ ( puTree->Length / sizeof( WCHAR ) ) > NDS_TREE_NAME_LEN ) {
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // If this is an extended credential create, munge the name.
+ //
+
+ RtlInitUnicodeString( &CredentialName, NULL );
+
+ if ( ( pIrpContext->Specific.Create.fExCredentialCreate ) &&
+ ( !IsCredentialName( puTree ) ) ) {
+
+ Status = BuildExCredentialServerName( puTree,
+ puUserName,
+ &CredentialName );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ puConnectName = &CredentialName;
+
+ } else {
+
+ puConnectName = puTree;
+ }
+
+ //
+ // First check to see if we already have a connection
+ // to this tree that we can use... If so, this will
+ // leave the irp context pointed at that server for us.
+ //
+ // This time around, don't use bindery authenticated
+ // connections to browse the tree.
+ //
+
+ Status = NdsSelectConnection( pIrpContext,
+ puConnectName,
+ puUserName,
+ puPassword,
+ DeferredLogon,
+ FALSE,
+ &pNpExistingScb );
+
+ if ( NT_SUCCESS( Status ) && pNpExistingScb ) {
+ *ppScb = pNpExistingScb->pScb;
+ ASSERT( *ppScb != NULL );
+ ASSERT( NT_SUCCESS( Status ) );
+ goto ExitWithCleanup;
+ }
+
+ //
+ // If there was an authentication failure, bail out.
+ //
+
+ if ( Status == STATUS_NO_SUCH_USER ||
+ Status == STATUS_WRONG_PASSWORD ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Otherwise, we need to select a dir server. To do this,
+ // we have to look up dir server names by address. To do
+ // this we create an SCB for synchronization with the name
+ // *tree*, which isn't a valid server name.
+ //
+
+ ScanTreeName.Length = sizeof( WCHAR );
+ ScanTreeName.MaximumLength = sizeof( ScanBuffer );
+ ScanTreeName.Buffer = ScanBuffer;
+
+ ScanBuffer[0] = L'*';
+ RtlAppendUnicodeStringToString( &ScanTreeName, puTree );
+ ScanBuffer[( ScanTreeName.Length / sizeof( WCHAR ) )] = L'*';
+ ScanTreeName.Length += sizeof( WCHAR );
+
+ //
+ // Now make it a uid server name.
+ //
+
+ Status = MakeUidServer( &UidServerName,
+ &pIrpContext->Specific.Create.UserUid,
+ &ScanTreeName );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ NwFindScb( &pTreeScb,
+ pIrpContext,
+ &UidServerName,
+ &ScanTreeName );
+
+ if ( !pTreeScb ) {
+ DebugTrace( 0, Dbg, "Failed to get a tree scb for synchronization.\n", 0 );
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Get a nearby server connection and prepare to
+ // do the bindery scan for tree connection points.
+ // Don't forget to copy the user uid for security.
+ //
+
+ if ( !NwAllocateExtraIrpContext( &pExtraIrpContext,
+ pTreeScb->pNpScb ) ) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ExitWithCleanup;
+
+ }
+
+ pExtraIrpContext->Specific.Create.UserUid.QuadPart =
+ pIrpContext->Specific.Create.UserUid.QuadPart;
+
+ //
+ // Append a wildcard to the tree name for the bindery scan.
+ //
+
+ ScanTreeName.Length = 0;
+ ScanTreeName.MaximumLength = sizeof( ScanBuffer );
+ ScanTreeName.Buffer = ScanBuffer;
+
+ RtlCopyUnicodeString( &ScanTreeName, puTree );
+
+ i = ScanTreeName.Length / sizeof( WCHAR );
+
+ while( i <= NDS_TREE_NAME_LEN ) {
+ ScanBuffer[i++] = L'_';
+ }
+
+ ScanBuffer[NDS_TREE_NAME_LEN] = L'*';
+ ScanTreeName.Length = (NDS_TREE_NAME_LEN + 1) * sizeof( WCHAR );
+
+ DebugTrace( 0, Dbg, "Scanning for NDS tree %wZ.\n", puTree );
+
+ //
+ // Now we lookup the dir server addresses in the bindery and
+ // try to make dir server connections.
+ //
+
+ while ( TRUE ) {
+
+ if ( ( pNearbyScb ) && ( !fOnNearbyQueue ) ) {
+
+ //
+ // Get back to the head of the nearby server so we can continue
+ // looking for dir servers. If the nearby server is no good anymore,
+ // dereference the connection and set the nearby scb pointer to
+ // NULL. This will cause us to get a new nearby server when we
+ // continue.
+ //
+
+ NwAppendToQueueAndWait( pExtraIrpContext );
+
+ if ( !( ( pNearbyScb->pNpScb->State == SCB_STATE_LOGIN_REQUIRED ) ||
+ ( pNearbyScb->pNpScb->State == SCB_STATE_IN_USE ) ) ) {
+
+ NwDequeueIrpContext( pExtraIrpContext, FALSE );
+ NwDereferenceScb( pNearbyScb->pNpScb );
+ pNearbyScb = NULL;
+
+ //
+ // Don't restart the search. If our bindery server went down in
+ // the middle of a connect, the connect will fail and that's ok.
+ // If we restart the search we can end up in this loop forever.
+ //
+
+ } else {
+
+ fOnNearbyQueue = TRUE;
+ }
+
+ }
+
+ //
+ // Get a bindery server to talk to if we don't have one. This may
+ // be our first time through this loop, or our server may have
+ // gone bad (see above).
+ //
+ // Optimization: What if this CreateScb returns a valid dir server
+ // for the tree we are looking for? We should use it!!
+ //
+
+ if ( !pNearbyScb ) {
+
+ Status = CreateScb( &pNearbyScb,
+ pExtraIrpContext,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ TRUE,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ ASSERT( pExtraIrpContext->pNpScb == pNearbyScb->pNpScb );
+ ASSERT( pExtraIrpContext->pScb == pNearbyScb );
+
+ NwAppendToQueueAndWait( pExtraIrpContext );
+ fOnNearbyQueue = TRUE;
+
+ }
+
+ //
+ // Look up the dir server address from our nearby server.
+ //
+
+ Status = ExchangeWithWait( pExtraIrpContext,
+ SynchronousResponseCallback,
+ "SdwU",
+ NCP_ADMIN_FUNCTION, NCP_SCAN_BINDERY_OBJECT,
+ dwLastOid,
+ OT_DIRSERVER,
+ &ScanTreeName );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ //
+ // We're out of options for dir servers.
+ //
+
+ Status = STATUS_BAD_NETWORK_PATH;
+ break;
+ }
+
+ Status = ParseResponse( pExtraIrpContext,
+ pExtraIrpContext->rsp,
+ pExtraIrpContext->ResponseLength,
+ "Nd_r",
+ &dwLastOid,
+ sizeof( WORD ),
+ DirServerName,
+ MAX_SERVER_NAME_LENGTH );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ break;
+ }
+
+ Status = ExchangeWithWait ( pExtraIrpContext,
+ SynchronousResponseCallback,
+ "Swbrbp",
+ NCP_ADMIN_FUNCTION, NCP_QUERY_PROPERTY_VALUE,
+ OT_DIRSERVER,
+ 0x30,
+ DirServerName,
+ MAX_SERVER_NAME_LENGTH,
+ 1, // Segment number
+ NET_ADDRESS_PROPERTY );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ DebugTrace( 0, Dbg, "No net address property for this dir server.\n", 0 );
+ continue;
+ }
+
+ Status = ParseResponse( pExtraIrpContext,
+ pExtraIrpContext->rsp,
+ pExtraIrpContext->ResponseLength,
+ "Nr",
+ &DirServerAddress,
+ sizeof(TDI_ADDRESS_IPX) );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ DebugTrace( 0, Dbg, "Couldn't parse net address property for this dir server.\n", 0 );
+ continue;
+ }
+
+ //
+ // We know the address of the dir server, so do an anonymous
+ // create to it. Use the original irp context so the uid is
+ // correct. Note that we have to dequeue from the nearby scb
+ // in case we are referred to that server!
+ //
+
+ NwDequeueIrpContext( pExtraIrpContext, FALSE );
+ fOnNearbyQueue = FALSE;
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ Status = CreateScb( &pNearestTreeScb,
+ pIrpContext,
+ NULL,
+ &DirServerAddress,
+ puUserName,
+ puPassword,
+ DeferredLogon,
+ DeleteOnClose );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ if ( Status == STATUS_NO_SUCH_USER ||
+ Status == STATUS_WRONG_PASSWORD ||
+ Status == STATUS_ACCESS_DENIED ||
+ Status == STATUS_ACCOUNT_DISABLED ||
+ Status == STATUS_LOGIN_TIME_RESTRICTION ||
+ Status == STATUS_REMOTE_SESSION_LIMIT ||
+ Status == STATUS_CONNECTION_COUNT_LIMIT ||
+ Status == STATUS_NETWORK_CREDENTIAL_CONFLICT ||
+ Status == STATUS_PASSWORD_EXPIRED ) {
+ break;
+ }
+
+ continue;
+ }
+
+ //
+ // If the server we got back was bindery authenticated,
+ // it is NOT a valid dir server for us to use (yet)!!
+ //
+
+ if ( pNearestTreeScb->UserName.Length != 0 ) {
+
+ Status = STATUS_ACCESS_DENIED;
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ NwDereferenceScb( pNearestTreeScb->pNpScb );
+
+ continue;
+ }
+
+ //
+ // Otherwise, we're golden. Break out of here!
+ //
+
+ DebugTrace( 0, Dbg, "Dir server: %wZ\n", &pNearestTreeScb->UidServerName );
+ *ppScb = pNearestTreeScb;
+ ASSERT( NT_SUCCESS( Status ) );
+ break;
+
+ }
+
+ //
+ // We have been wholly unable to get a browse connection
+ // to this tree. Try again but this time allow the use
+ // of connections that are bindery authenticated. We don't
+ // need the nearby server anymore.
+ //
+
+ if ( pNearbyScb ) {
+
+ NwDequeueIrpContext( pExtraIrpContext, FALSE );
+ NwDereferenceScb( pNearbyScb->pNpScb );
+ }
+
+ if ( ( Status != STATUS_SUCCESS ) &&
+ ( Status != STATUS_NO_SUCH_USER ) &&
+ ( Status != STATUS_WRONG_PASSWORD ) &&
+ ( Status != STATUS_ACCESS_DENIED ) &&
+ ( Status != STATUS_ACCOUNT_DISABLED ) &&
+ ( Status != STATUS_LOGIN_TIME_RESTRICTION ) &&
+ ( Status != STATUS_REMOTE_SESSION_LIMIT ) &&
+ ( Status != STATUS_CONNECTION_COUNT_LIMIT ) &&
+ ( Status != STATUS_NETWORK_CREDENTIAL_CONFLICT ) &&
+ ( Status != STATUS_PASSWORD_EXPIRED ) ) {
+
+ Status = NdsSelectConnection( pIrpContext,
+ puConnectName,
+ puUserName,
+ puPassword,
+ DeferredLogon,
+ TRUE,
+ &pNpExistingScb );
+
+ if ( NT_SUCCESS( Status ) && pNpExistingScb ) {
+ *ppScb = pNpExistingScb->pScb;
+ ASSERT( *ppScb != NULL );
+ }
+ }
+
+ExitWithCleanup:
+
+ //
+ // Clean up and bail.
+ //
+
+ if ( pExtraIrpContext ) {
+ NwFreeExtraIrpContext( pExtraIrpContext );
+ }
+
+ if ( UidServerName.Buffer != NULL ) {
+ FREE_POOL( UidServerName.Buffer );
+ }
+
+ if ( pTreeScb ) {
+ NwDereferenceScb( pTreeScb->pNpScb );
+ }
+
+ if ( CredentialName.Buffer ) {
+ FREE_POOL( CredentialName.Buffer );
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+ConnectBinderyVolume(
+ PIRP_CONTEXT pIrpContext,
+ PUNICODE_STRING puServerName,
+ PUNICODE_STRING puVolumeName
+)
+/*++
+
+Description:
+
+ Given a server name and a volume, try to connect the volume.
+ This is used in QueryPath to pre-connect a volume.
+
+--*/
+{
+
+ NTSTATUS Status;
+ PSCB pScb;
+ PVCB pVcb;
+
+ PAGED_CODE();
+
+ //
+ // Try making a server connection with this name.
+ //
+
+ Status = CreateScb( &pScb,
+ pIrpContext,
+ puServerName,
+ NULL,
+ NULL,
+ NULL,
+ FALSE,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ DebugTrace( 0, Dbg, "Bindery volume connect got server %wZ\n", puServerName );
+
+ //
+ // If we succeeded, do a standard bindery volume attach.
+ //
+
+ pVcb = NwFindVcb( pIrpContext,
+ puVolumeName,
+ RESOURCETYPE_ANY,
+ 0,
+ FALSE,
+ FALSE );
+
+ if ( pVcb == NULL ) {
+
+ Status = STATUS_BAD_NETWORK_PATH;
+
+ } else {
+
+ //
+ // We should not have jumped servers since this was explicit.
+ //
+
+ ASSERT( pScb == pIrpContext->pScb );
+
+ //
+ // Remove NwFindVcb reference. Don't supply an IrpContext
+ // so the Vcb doesn't get destroyed immediately after we just
+ // created it because no-one else has it referenced.
+ //
+
+ NwDereferenceVcb( pVcb, NULL, FALSE );
+ DebugTrace( 0, Dbg, "Bindery volume connect got volume %wZ\n", puVolumeName );
+ }
+
+ NwDereferenceScb( pScb->pNpScb );
+ return Status;
+
+}
+
+NTSTATUS
+HandleVolumeAttach(
+ PIRP_CONTEXT pIrpContext,
+ PUNICODE_STRING puServerName,
+ PUNICODE_STRING puVolumeName
+)
+/*++
+
+Description:
+
+ This function is only callable from the QUERY_PATH code path!
+
+ This functions takes a server name and volume name from
+ QueryPath() and resolves it into a server/volume connection.
+ The server/volume name can be plain or can refer to an
+ nds tree and the nds path to a volume object.
+
+ In the nds case, we only verify that the volume object exists.
+
+Arguments:
+
+ pIrpContext - irp context for this request
+ puServerName - server name or nds tree name
+ puVolumeName - volume name or nds path to volume object
+
+--*/
+{
+
+ NTSTATUS Status;
+ PSCB pScb;
+
+ UNICODE_STRING uDsObject;
+ DWORD dwVolumeOid, dwObjectType;
+
+ PAGED_CODE();
+
+ //
+ // Try the bindery server/volume case first.
+ //
+
+ Status = ConnectBinderyVolume( pIrpContext,
+ puServerName,
+ puVolumeName );
+ if ( NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ if ( Status == STATUS_NETWORK_UNREACHABLE ) {
+
+ // IPX is not bound to anything that is currently
+ // up (which means it's probably bound only to the
+ // RAS WAN wrapper). Don't waste time looking for
+ // a ds tree.
+ //
+
+ return STATUS_BAD_NETWORK_PATH;
+ }
+
+ //
+ // See if this is a tree name and get a ds connection.
+ //
+
+ pIrpContext->Specific.Create.NdsCreate = TRUE;
+
+ Status = NdsCreateTreeScb( pIrpContext,
+ &pScb,
+ puServerName,
+ NULL,
+ NULL,
+ TRUE,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ //
+ // If we have a tree, resolve the volume object.
+ // BUGBUG: We should actually check to see if we
+ // already have a connection to this object before
+ // we hit the ds.
+ //
+
+ Status = NdsGetDsObjectFromPath( pIrpContext,
+ &uDsObject );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NwDereferenceScb( pIrpContext->pNpScb );
+ return Status;
+ }
+
+ Status = NdsVerifyObject( pIrpContext, // irp context for the request
+ &uDsObject, // path to volume object
+ TRUE, // allow a server jump
+ DEFAULT_RESOLVE_FLAGS, // resolver flags
+ &dwVolumeOid, // volume oid from the ds
+ &dwObjectType ); // volume or print queue
+
+ //
+ // We may have jumped servers in the VerifyObject code,
+ // so just make sure we dereference the correct server.
+ //
+
+ NwDereferenceScb( pIrpContext->pNpScb );
+ return Status;
+
+}
+
+NTSTATUS
+NdsGetDsObjectFromPath(
+ IN PIRP_CONTEXT pIrpContext,
+ OUT PUNICODE_STRING puDsObject
+)
+/*++
+
+Description:
+
+ Take the full path from the create irp context and
+ extract out the ds path of the desired object.
+
+ The supplied unicode string shouldn't have a buffer;
+ it will be set up to point into the user's buffer
+ referred to by the irp context.
+
+Arguments:
+
+ pIrpContext - an irp context from a create path request
+ puDsObject - unicode string that will refer to the correct ds path
+
+--*/
+{
+
+ DWORD dwPathSeparators;
+ USHORT NewHead;
+
+ PAGED_CODE();
+
+ //
+ // The VolumeName is one of the following:
+ //
+ // \X:\Server\Volume.Object.Path
+ // \Server\Volume.Object.Path
+ //
+
+ *puDsObject = pIrpContext->Specific.Create.VolumeName;
+
+ //
+ // Skip the leading slash.
+ //
+
+ puDsObject->Length -= sizeof( WCHAR );
+ puDsObject->Buffer += 1;
+
+ //
+ // How many more are there to overcome?
+ //
+
+ NewHead = 0;
+ dwPathSeparators = pIrpContext->Specific.Create.DriveLetter ? 2 : 1;
+
+ while ( NewHead < puDsObject->Length &&
+ dwPathSeparators ) {
+
+ if ( puDsObject->Buffer[NewHead/sizeof(WCHAR)] == OBJ_NAME_PATH_SEPARATOR ) {
+ dwPathSeparators--;
+ }
+
+ NewHead += sizeof( WCHAR );
+ }
+
+ if ( dwPathSeparators ||
+ NewHead == puDsObject->Length) {
+
+ //
+ // Something wasn't formed right in the volume name.
+ //
+
+ return STATUS_BAD_NETWORK_PATH;
+ }
+
+ puDsObject->Length -= NewHead;
+ puDsObject->Buffer += NewHead/sizeof(WCHAR);
+
+ //
+ // If there is a leading dot, skip it.
+ //
+
+ if ( puDsObject->Buffer[0] == L'.' ) {
+
+ puDsObject->Length -= sizeof( WCHAR );
+ puDsObject->Buffer += 1;
+ }
+
+ puDsObject->MaximumLength = puDsObject->Length;
+
+ DebugTrace( 0, Dbg, "DS object: %wZ\n", puDsObject );
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NdsVerifyObject(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING puDsObject,
+ IN BOOLEAN fAllowServerJump,
+ IN DWORD dwResolverFlags,
+ OUT PDWORD pdwDsOid,
+ OUT PDWORD pdwObjectType
+)
+/*++
+
+Description:
+
+ This function verifies that a ds path refers to a volume
+ object, print queue, or a dir map. It returns the oid
+ of the object.
+
+ If fAllowServerJump is set to false, this simply looks up
+ the oid on the current server but doesn't verify the object
+ type. This routine checks all appropriate contexts for the
+ object, unlike ResolveNameKm.
+
+Parameters:
+
+ pIrpContext - irp context for this request, pointed to the ds server
+ puDsObject - path to the object in the ds
+ fAllowServerJump - allow a server jump to take place
+ pdwDsOid - destination of the ds oid of the object
+ pdwObjectType - NDS_OBJECTTYPE_VOLUME, NDS_OBJECTTYPE_QUEUE, or NDS_OBJECTTYPE_DIRMAP
+
+--*/
+{
+
+ NTSTATUS Status;
+
+ PNDS_SECURITY_CONTEXT pCredentials = NULL;
+ PUNICODE_STRING puAppendableContext;
+
+ UNICODE_STRING uFdnObject;
+ WCHAR FdnObject[MAX_NDS_NAME_CHARS];
+
+ PLOGON pLogon;
+ PSCB pScb;
+ USHORT i;
+
+ LOCKED_BUFFER NdsRequest;
+ DWORD dwObjectOid, dwObjectType;
+
+ UNICODE_STRING uVolume;
+ UNICODE_STRING uQueue;
+ UNICODE_STRING uDirMap;
+
+ UNICODE_STRING uReplyString;
+ WCHAR ReplyBuffer[32];
+ BOOLEAN fHoldingCredentialList = FALSE;
+ BOOLEAN fPartiallyDistinguished = FALSE;
+
+ PAGED_CODE();
+
+ NdsRequest.pRecvBufferVa = NULL;
+
+ //
+ // Get the user credentials.
+ //
+
+ pScb = pIrpContext->pNpScb->pScb;
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUser( &pScb->UserUid, FALSE );
+ NwReleaseRcb( &NwRcb );
+
+ //
+ // Get the credential. We don't care if it's locked or
+ // not since we're just querying the ds.
+ //
+
+ if ( pLogon ) {
+
+ Status = NdsLookupCredentials( &pScb->NdsTreeName,
+ pLogon,
+ &pCredentials,
+ CREDENTIAL_READ,
+ FALSE );
+
+ if ( NT_SUCCESS( Status ) ) {
+ ASSERT( pCredentials != NULL );
+ fHoldingCredentialList = TRUE;
+ }
+
+ }
+
+ //
+ // Check to see if it's at least partially distinguished already.
+ //
+
+ i = 0;
+ while (i < puDsObject->Length / sizeof( WCHAR ) ) {
+
+ if ( puDsObject->Buffer[i++] == L'.' ) {
+ fPartiallyDistinguished = TRUE;
+ }
+ }
+
+ //
+ // If it's partially distinguished, try it without the context first.
+ //
+
+ if ( fPartiallyDistinguished ) {
+
+ Status = NdsResolveNameKm ( pIrpContext,
+ puDsObject,
+ &dwObjectOid,
+ fAllowServerJump,
+ dwResolverFlags );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ DebugTrace( 0, Dbg, "VerifyObject: %wZ\n", puDsObject );
+ goto GetObjectType;
+ }
+ }
+
+ //
+ // If that failed, or if it wasn't partially distinguished,
+ // see if there's a current context we can append.
+ //
+
+ if ( ( pCredentials ) &&
+ ( pCredentials->CurrentContext.Length ) ) {
+
+ if ( ( puDsObject->Length + pCredentials->CurrentContext.Length ) < sizeof( FdnObject ) ) {
+
+ //
+ // Append the context.
+ //
+
+ uFdnObject.MaximumLength = sizeof( FdnObject );
+ uFdnObject.Buffer = FdnObject;
+
+ RtlCopyMemory( FdnObject, puDsObject->Buffer, puDsObject->Length );
+ uFdnObject.Length = puDsObject->Length;
+
+ if ( uFdnObject.Buffer[( uFdnObject.Length / sizeof( WCHAR ) ) - 1] == L'.' ) {
+ uFdnObject.Length -= sizeof( WCHAR );
+ }
+
+ if ( pCredentials->CurrentContext.Buffer[0] != L'.' ) {
+ uFdnObject.Buffer[uFdnObject.Length / sizeof( WCHAR )] = L'.';
+ uFdnObject.Length += sizeof( WCHAR );
+ }
+
+ RtlCopyMemory( ((BYTE *)FdnObject) + uFdnObject.Length,
+ pCredentials->CurrentContext.Buffer,
+ pCredentials->CurrentContext.Length );
+
+ uFdnObject.Length += pCredentials->CurrentContext.Length;
+
+ //
+ // Resolve this name.
+ //
+
+ Status = NdsResolveNameKm ( pIrpContext,
+ &uFdnObject,
+ &dwObjectOid,
+ fAllowServerJump,
+ dwResolverFlags );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ DebugTrace( 0, Dbg, "VerifyObject: %wZ\n", &uFdnObject );
+ goto GetObjectType;
+ }
+
+ }
+
+ }
+
+ //
+ // This is not a valid name.
+ //
+
+ DebugTrace( 0, Dbg, "VerifyObject: No ds object to resolve.\n", 0 );
+
+ if ( fHoldingCredentialList ) {
+ NwReleaseCredList( pLogon );
+ fHoldingCredentialList = FALSE;
+ }
+
+ return STATUS_BAD_NETWORK_PATH;
+
+
+GetObjectType:
+
+ if ( fHoldingCredentialList ) {
+ NwReleaseCredList( pLogon );
+ fHoldingCredentialList = FALSE;
+ }
+
+ //
+ // If a server jump is not allowed, we don't need to worry
+ // about getting the object type.
+ //
+
+ if ( !fAllowServerJump ) {
+ dwObjectType = 0;
+ goto CompletedObject;
+ }
+
+ //
+ // Resolve the object and get its information.
+ //
+
+ Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_READ_ENTRY_INFO,
+ &NdsRequest,
+ "DD",
+ 0,
+ dwObjectOid );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ Status = NdsCompletionCodetoNtStatus( &NdsRequest );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Verify that it's a volume object.
+ //
+
+ RtlInitUnicodeString( &uVolume, VOLUME_ATTRIBUTE );
+ RtlInitUnicodeString( &uQueue, QUEUE_ATTRIBUTE );
+ RtlInitUnicodeString( &uDirMap, DIR_MAP_ATTRIBUTE );
+
+ uReplyString.Length = 0;
+ uReplyString.MaximumLength = sizeof( ReplyBuffer );
+ uReplyString.Buffer = ReplyBuffer;
+
+ Status = ParseResponse( NULL,
+ NdsRequest.pRecvBufferVa,
+ NdsRequest.dwBytesWritten,
+ "G_T",
+ sizeof( NDS_RESPONSE_GET_OBJECT_INFO ),
+ &uReplyString );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ dwObjectType = 0;
+
+ if ( !RtlCompareUnicodeString( &uVolume, &uReplyString, FALSE ) ) {
+ dwObjectType = NDS_OBJECTTYPE_VOLUME;
+ } else if ( !RtlCompareUnicodeString( &uQueue, &uReplyString, FALSE ) ) {
+ dwObjectType = NDS_OBJECTTYPE_QUEUE;
+ } else if ( !RtlCompareUnicodeString( &uDirMap, &uReplyString, FALSE ) ) {
+ dwObjectType = NDS_OBJECTTYPE_DIRMAP;
+ }
+
+ if ( !dwObjectType ) {
+
+ DebugTrace( 0, Dbg, "DS object is not a connectable type.\n", 0 );
+ Status = STATUS_OBJECT_PATH_SYNTAX_BAD;
+ goto ExitWithCleanup;
+ }
+
+CompletedObject:
+
+ if ( pdwDsOid ) {
+ *pdwDsOid = dwObjectOid;
+ }
+
+ if ( pdwObjectType ) {
+ *pdwObjectType = dwObjectType;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ExitWithCleanup:
+
+ if ( fHoldingCredentialList ) {
+ NwReleaseCredList( pLogon );
+ }
+
+ if ( NdsRequest.pRecvBufferVa ) {
+ NdsFreeLockedBuffer( &NdsRequest );
+ }
+
+ return Status;
+}
+
+NTSTATUS
+NdsVerifyContext(
+ PIRP_CONTEXT pIrpContext,
+ PUNICODE_STRING puTree,
+ PUNICODE_STRING puContext
+)
+/*++
+
+ Given a context and a tree, verify that the context is a
+ valid container in the tree.
+
+ This call may cause the irpcontex to jump servers to an
+ referred dir server. If so, the scb pointers in the irp
+ context will be updated, the old server will be dereferenced,
+ and the new server will hold the reference for this request.
+
+--*/
+{
+
+ NTSTATUS Status;
+ DWORD dwOid, dwSubordinates;
+ LOCKED_BUFFER NdsRequest;
+ PSCB pScb, pTreeScb;
+ PNONPAGED_SCB pNpScb;
+
+ PAGED_CODE();
+
+ //
+ // Establish a browse connection to the tree we want to query.
+ //
+
+ NdsRequest.pRecvBufferVa = NULL;
+
+ pScb = pIrpContext->pScb;
+ pNpScb = pIrpContext->pNpScb;
+
+ Status = NdsCreateTreeScb( pIrpContext,
+ &pTreeScb,
+ puTree,
+ NULL,
+ NULL,
+ TRUE,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ pTreeScb = NULL;
+ goto ExitWithCleanup;
+ }
+
+ Status = NdsResolveNameKm ( pIrpContext,
+ puContext,
+ &dwOid,
+ TRUE,
+ DEFAULT_RESOLVE_FLAGS );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ DebugTrace( 0, Dbg, "NdsVerifyContext: resolve failed.\n", 0 );
+ goto ExitWithCleanup;
+ }
+
+ Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ NdsRequest.pRecvBufferVa = NULL;
+ goto ExitWithCleanup;
+ }
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_READ_ENTRY_INFO,
+ &NdsRequest,
+ "DD",
+ 0,
+ dwOid );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ Status = NdsCompletionCodetoNtStatus( &NdsRequest );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Verify that it's a volume object by checking the
+ // third DWORD, which is the subordinate count.
+ //
+
+ Status = ParseResponse( NULL,
+ NdsRequest.pRecvBufferVa,
+ NdsRequest.dwBytesWritten,
+ "G_D",
+ 2 * sizeof( DWORD ),
+ &dwSubordinates );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ if ( !dwSubordinates ) {
+
+ DebugTrace( 0, Dbg, "No subordinates in VerifyContext.\n", 0 );
+ Status = STATUS_INVALID_PARAMETER;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Success!
+ //
+
+ExitWithCleanup:
+
+ //
+ // We may have jumped servers in the resolve name call,
+ // so make sure we dereference the correct SCB!
+ //
+
+ if ( pTreeScb ) {
+ NwDereferenceScb( pIrpContext->pNpScb );
+ }
+
+ //
+ // Restore the connection to the original server.
+ //
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ pIrpContext->pScb = pScb;
+ pIrpContext->pNpScb = pNpScb;
+
+ if ( NdsRequest.pRecvBufferVa ) {
+ NdsFreeLockedBuffer( &NdsRequest );
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NdsMapObjectToServerShare(
+ PIRP_CONTEXT pIrpContext,
+ PSCB *ppScb,
+ PUNICODE_STRING puServerSharePath,
+ BOOLEAN CreateTreeConnection,
+ PDWORD pdwObjectId
+)
+/*++
+
+Description:
+
+ This function takes a pointer to a tree scb and an irp
+ context for a create request. It looks up the ds object
+ from the create request in the ds and maps it to
+ the appropriate server/share duple.
+
+ The FullPathName and VolumeName strings in the create
+ section of the irp context are updated and a connection
+ to the real host server is established so that the
+ create request can continue as desired.
+
+--*/
+{
+
+ NTSTATUS Status;
+ LOCKED_BUFFER NdsRequest;
+
+ UNICODE_STRING uServerAttribute;
+ UNICODE_STRING uVolumeAttribute;
+ UNICODE_STRING uQueueAttribute;
+ UNICODE_STRING uPathAttribute;
+
+ UNICODE_STRING uHostServer;
+ UNICODE_STRING uRealServerName;
+ UNICODE_STRING uHostVolume;
+ UNICODE_STRING uHostPath;
+ UNICODE_STRING uIntermediateVolume;
+
+ UNICODE_STRING uDsObjectPath;
+ DWORD dwObjectOid, dwObjectType, dwDirMapType;
+
+ DWORD dwTotalPathLen;
+ USHORT usSrv;
+ PSCB pOldScb, pNewServerScb;
+
+ UNICODE_STRING UserName, Password;
+ ULONG ShareType;
+
+ PAGED_CODE();
+
+ //
+ // Set up strings and buffers.
+ //
+
+ RtlInitUnicodeString( &uServerAttribute, HOST_SERVER_ATTRIBUTE );
+ RtlInitUnicodeString( &uVolumeAttribute, HOST_VOLUME_ATTRIBUTE );
+ RtlInitUnicodeString( &uQueueAttribute, HOST_QUEUE_ATTRIBUTE );
+ RtlInitUnicodeString( &uPathAttribute, HOST_PATH_ATTRIBUTE );
+
+ RtlInitUnicodeString( &uHostServer, NULL );
+ RtlInitUnicodeString( &uRealServerName, NULL );
+ RtlInitUnicodeString( &uHostVolume, NULL );
+ RtlInitUnicodeString( &uHostPath, NULL );
+ RtlInitUnicodeString( &uIntermediateVolume, NULL );
+
+ Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ uHostServer.Buffer = ALLOCATE_POOL( PagedPool, 4 * MAX_NDS_NAME_SIZE );
+
+ if ( !uHostServer.Buffer ) {
+
+ NdsFreeLockedBuffer( &NdsRequest );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ uHostServer.MaximumLength = MAX_NDS_NAME_SIZE;
+
+ uHostVolume.Buffer = ( PWCHAR )(((BYTE *)uHostServer.Buffer) + MAX_NDS_NAME_SIZE);
+ uHostVolume.MaximumLength = MAX_NDS_NAME_SIZE;
+
+ uHostPath.Buffer = ( PWCHAR )(((BYTE *)uHostVolume.Buffer) + MAX_NDS_NAME_SIZE);
+ uHostPath.MaximumLength = MAX_NDS_NAME_SIZE;
+
+ uIntermediateVolume.Buffer = ( PWCHAR )(((BYTE *)uHostPath.Buffer) + MAX_NDS_NAME_SIZE);
+ uIntermediateVolume.MaximumLength = MAX_NDS_NAME_SIZE;
+
+ //
+ // First get the object id from the ds.
+ //
+
+ Status = NdsGetDsObjectFromPath( pIrpContext, &uDsObjectPath );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ pOldScb = pIrpContext->pScb;
+
+ Status = NdsVerifyObject( pIrpContext,
+ &uDsObjectPath,
+ TRUE, // allow server jumping
+ DEFAULT_RESOLVE_FLAGS,
+ &dwObjectOid,
+ &dwObjectType );
+
+ //
+ // We may have jumped servers.
+ //
+
+ *ppScb = pIrpContext->pScb;
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // If this is a dir map, grab the target volume and re-verify
+ // the object for connectability.
+ //
+
+ if ( dwObjectType == NDS_OBJECTTYPE_DIRMAP ) {
+
+ //
+ // First get the volume object and path.
+ //
+
+ Status = NdsReadAttributesKm( pIrpContext,
+ dwObjectOid,
+ &uPathAttribute,
+ &NdsRequest );
+
+ if ( !NT_SUCCESS( Status )) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Dig out the volume path and the directory path.
+ //
+
+ Status = ParseResponse( NULL,
+ NdsRequest.pRecvBufferVa,
+ NdsRequest.dwBytesWritten,
+ "G_____S_ST",
+ sizeof( DWORD ), // completion code
+ sizeof( DWORD ), // iter handle
+ sizeof( DWORD ), // info type
+ sizeof( DWORD ), // attribute count
+ sizeof( DWORD ), // syntax id
+ NULL, // attribute name
+ 3 * sizeof( DWORD ), // unknown
+ &uIntermediateVolume, // ds volume
+ &uHostPath ); // dir map path
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Verify the target volume object.
+ //
+
+ Status = NdsVerifyObject( pIrpContext,
+ &uIntermediateVolume,
+ TRUE,
+ DEFAULT_RESOLVE_FLAGS,
+ &dwObjectOid,
+ &dwDirMapType );
+
+ //
+ // We may have jumped servers.
+ //
+
+ *ppScb = pIrpContext->pScb;
+
+ if ( !NT_SUCCESS( Status )) {
+ goto ExitWithCleanup;
+ }
+
+ ASSERT( dwDirMapType == NDS_OBJECTTYPE_VOLUME );
+
+ }
+
+ //
+ // Get the server (for any connectable object).
+ //
+
+ Status = NdsReadStringAttribute( pIrpContext,
+ dwObjectOid,
+ &uServerAttribute,
+ &uHostServer );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Get the host volume or queue.
+ //
+
+ if ( dwObjectType == NDS_OBJECTTYPE_VOLUME ||
+ dwObjectType == NDS_OBJECTTYPE_DIRMAP ) {
+
+ Status = NdsReadStringAttribute( pIrpContext,
+ dwObjectOid,
+ &uVolumeAttribute,
+ &uHostVolume );
+
+ } else if ( dwObjectType == NDS_OBJECTTYPE_QUEUE ) {
+
+ Status = NdsReadStringAttribute( pIrpContext,
+ dwObjectOid,
+ &uQueueAttribute,
+ &uHostVolume );
+
+ } else {
+
+ Status = STATUS_BAD_NETWORK_PATH;
+
+ }
+
+ if ( !NT_SUCCESS( Status )) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Dig out the actual server name from the X.500 name.
+ //
+
+ Status = NdsGetServerBasicName( &uHostServer,
+ &uRealServerName );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Make sure we have enough space in the new buffer to format
+ // the new connect string of \X:\Server\Share\Path,
+ // \LPTX\Server\Share\Path, or \Server\Share\Path.
+ //
+
+ dwTotalPathLen = uRealServerName.Length + uHostVolume.Length;
+ dwTotalPathLen += ( sizeof( L"\\\\" ) - sizeof( L"" ) );
+
+ //
+ // Account for the correct prefix. We count on single character
+ // drive and printer letters here. Again, maybe unwise later on.
+ //
+
+ if ( pIrpContext->Specific.Create.DriveLetter ) {
+
+ if ( dwObjectType == NDS_OBJECTTYPE_VOLUME ||
+ dwObjectType == NDS_OBJECTTYPE_DIRMAP ) {
+
+ dwTotalPathLen += ( sizeof( L"X:\\" ) - sizeof( L"" ) );
+
+ } else if ( dwObjectType == NDS_OBJECTTYPE_QUEUE ) {
+
+ dwTotalPathLen += ( sizeof( L"LPT1\\" ) - sizeof( L"" ) );
+
+ } else {
+
+ Status = STATUS_BAD_NETWORK_PATH;
+ goto ExitWithCleanup;
+ }
+ }
+
+ //
+ // Count space for the path and filename if present.
+ //
+
+ if ( pIrpContext->Specific.Create.PathName.Length ) {
+ dwTotalPathLen += pIrpContext->Specific.Create.PathName.Length;
+ }
+
+ if ( dwObjectType == NDS_OBJECTTYPE_DIRMAP ) {
+ dwTotalPathLen += uHostPath.Length;
+ dwTotalPathLen += ( sizeof( L"\\" ) - sizeof( L"" ) );
+ }
+
+ if ( pIrpContext->Specific.Create.FileName.Length ) {
+ dwTotalPathLen += pIrpContext->Specific.Create.FileName.Length;
+ dwTotalPathLen += ( sizeof( L"\\" ) - sizeof( L"" ) );
+ }
+
+ if ( dwTotalPathLen > puServerSharePath->MaximumLength ) {
+
+ DebugTrace( 0 , Dbg, "NdsMapObjectToServerShare: Buffer too small.\n", 0 );
+ Status = STATUS_BUFFER_TOO_SMALL;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // First dequeue the irp context from the dir server we've been
+ // talking to, then make the connect to the new server. We logged
+ // in earlier so this will get us an authenticated connection.
+ //
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ //
+ // Since it's possible for us to get attaching to a bindery
+ // authenticated resource, we have to dig out the user name
+ // and password for the create call!!
+ //
+
+ ReadAttachEas( pIrpContext->pOriginalIrp,
+ &UserName,
+ &Password,
+ &ShareType,
+ NULL );
+
+ Status = CreateScb( &pNewServerScb,
+ pIrpContext,
+ &uRealServerName,
+ NULL,
+ &UserName,
+ &Password,
+ FALSE,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ ASSERT( pNewServerScb->pNpScb->State == SCB_STATE_IN_USE );
+
+ NwDereferenceScb( (*ppScb)->pNpScb );
+ *ppScb = pNewServerScb;
+
+ //
+ // Re-query the OID of the print queue object on this server
+ // or it could be wrong. Do not permit any sort of a server
+ // jump this time.
+ //
+
+ if ( dwObjectType == NDS_OBJECTTYPE_QUEUE ) {
+
+ Status = NdsVerifyObject( pIrpContext,
+ &uDsObjectPath,
+ FALSE,
+ RSLV_CREATE_ID,
+ &dwObjectOid,
+ NULL );
+
+ if ( !NT_SUCCESS( Status )) {
+ goto ExitWithCleanup;
+ }
+
+ }
+
+ if ( pdwObjectId ) {
+ *pdwObjectId = dwObjectOid;
+ }
+
+ //
+ // Re-format the path strings in the irp context. The nds share
+ // length tells us how much of the NDS share name is interesting
+ // for getting the directory handle.
+ //
+
+ usSrv = 0;
+ pIrpContext->Specific.Create.dwNdsShareLength = 0;
+
+ puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR;
+ puServerSharePath->Length = sizeof( WCHAR );
+ usSrv += sizeof( WCHAR );
+
+ //
+ // Set the proper prefix for this connect type.
+ //
+
+ if ( pIrpContext->Specific.Create.DriveLetter ) {
+
+ if ( dwObjectType == NDS_OBJECTTYPE_QUEUE ) {
+
+ puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = L'L';
+ usSrv += sizeof( WCHAR );
+
+ puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = L'P';
+ usSrv += sizeof( WCHAR );
+
+ puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = L'T';
+ usSrv += sizeof( WCHAR );
+ }
+
+ puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] =
+ pIrpContext->Specific.Create.DriveLetter;
+ usSrv += sizeof( WCHAR );
+
+ if ( dwObjectType != NDS_OBJECTTYPE_QUEUE ) {
+
+ puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = L':';
+ usSrv += sizeof( WCHAR );
+ }
+
+ puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR;
+ usSrv += sizeof( WCHAR );
+
+ puServerSharePath->Length = usSrv;
+ }
+
+ //
+ // Append the server name.
+ //
+
+ RtlAppendUnicodeStringToString( puServerSharePath, &uRealServerName );
+ usSrv += uRealServerName.Length;
+
+ puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR;
+ puServerSharePath->Length += sizeof( WCHAR );
+ usSrv += sizeof( WCHAR );
+
+ //
+ // Append the volume for volumes or the full ds path to
+ // the print queue for queues.
+ //
+
+ if ( dwObjectType == NDS_OBJECTTYPE_VOLUME ||
+ dwObjectType == NDS_OBJECTTYPE_DIRMAP ) {
+
+ RtlAppendUnicodeStringToString( puServerSharePath, &uHostVolume );
+ usSrv += uHostVolume.Length;
+ pIrpContext->Specific.Create.dwNdsShareLength += uHostVolume.Length;
+
+ } else if ( dwObjectType == NDS_OBJECTTYPE_QUEUE ) {
+
+ RtlAppendUnicodeStringToString( puServerSharePath, &uDsObjectPath );
+ usSrv += uDsObjectPath.Length;
+ pIrpContext->Specific.Create.dwNdsShareLength += uDsObjectPath.Length;
+
+ }
+
+ //
+ // Append the dir map path.
+ //
+
+ if ( dwObjectType == NDS_OBJECTTYPE_DIRMAP ) {
+
+ puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR;
+ puServerSharePath->Length += sizeof( WCHAR );
+ usSrv += sizeof( WCHAR );
+ pIrpContext->Specific.Create.dwNdsShareLength += sizeof( WCHAR );
+
+ RtlAppendUnicodeStringToString( puServerSharePath, &uHostPath );
+ usSrv += uHostPath.Length;
+ pIrpContext->Specific.Create.dwNdsShareLength += uHostPath.Length;
+
+ }
+
+ //
+ // Handle the path and file if they exist.
+ //
+
+ if ( pIrpContext->Specific.Create.PathName.Length ) {
+
+ ASSERT( dwObjectType != NDS_OBJECTTYPE_QUEUE );
+ RtlAppendUnicodeStringToString( puServerSharePath,
+ &pIrpContext->Specific.Create.PathName );
+ usSrv += pIrpContext->Specific.Create.PathName.Length;
+
+ //
+ // If this is a tree connection, then include the path in
+ // the share name so that the map point is correct.
+ //
+
+ if ( CreateTreeConnection ) {
+ pIrpContext->Specific.Create.dwNdsShareLength +=
+ pIrpContext->Specific.Create.PathName.Length;
+ }
+ }
+
+ if ( pIrpContext->Specific.Create.FileName.Length ) {
+
+ ASSERT( dwObjectType != NDS_OBJECTTYPE_QUEUE );
+
+ puServerSharePath->Buffer[usSrv/sizeof(WCHAR)] = OBJ_NAME_PATH_SEPARATOR;
+ puServerSharePath->Length += sizeof( WCHAR );
+ usSrv += sizeof( WCHAR );
+
+ RtlAppendUnicodeStringToString( puServerSharePath,
+ &pIrpContext->Specific.Create.FileName );
+ usSrv += pIrpContext->Specific.Create.FileName.Length;
+
+ //
+ // If this is a tree connection, then include the file in
+ // the share name so that the map point is correct.
+ //
+
+ if ( CreateTreeConnection ) {
+ pIrpContext->Specific.Create.dwNdsShareLength += sizeof( WCHAR );
+ pIrpContext->Specific.Create.dwNdsShareLength +=
+ pIrpContext->Specific.Create.FileName.Length;
+ }
+ }
+
+ //
+ // Record the object type in the irp context.
+ //
+
+ pIrpContext->Specific.Create.dwNdsObjectType = dwObjectType;
+
+ DebugTrace( 0, Dbg, "DS Object path is %wZ\n", &pIrpContext->Specific.Create.FullPathName );
+ DebugTrace( 0, Dbg, "Resolved path is %wZ\n", puServerSharePath );
+
+ExitWithCleanup:
+
+
+ NdsFreeLockedBuffer( &NdsRequest );
+ FREE_POOL( uHostServer.Buffer );
+ return Status;
+}
diff --git a/private/nw/rdr/crypto.h b/private/nw/rdr/crypto.h
new file mode 100644
index 000000000..0de8e0865
--- /dev/null
+++ b/private/nw/rdr/crypto.h
@@ -0,0 +1,139 @@
+/* crypto.h
+ *
+ * Prototypes and definitions for services in crypto.c
+ *
+ * ported to win nt from win 95 on 6/95
+ * Cory West
+ */
+
+#include <windef.h>
+
+#define CIPHERBLOCKSIZE 8 // size of RC2 block
+#define MAX_RSA_BITS 512 // actually 420
+#define MAX_RSA_BYTES (MAX_RSA_BITS/8)
+
+#define B_PSIZEBITS 210
+#define B_PSIZEWORDS (1 + B_PSIZEBITS/32)
+
+void __cdecl
+GenRandomBytes(
+ BYTE *output,
+ int len
+);
+
+//
+// Generate an 8 byte key from a seed of the given length.
+//
+
+void __cdecl
+GenKey8(
+ BYTE *keyData,
+ int keyDataLen,
+ BYTE key8[8]
+);
+
+void __cdecl
+MD2(
+ BYTE *input,
+ const int inlen,
+ BYTE *output
+);
+
+//
+// RC2 encrypt and decrypt wrappers.
+//
+
+int __cdecl
+CBCEncrypt(
+ BYTE *key, // secret key
+ BYTE const *ivec, // initialization vector, NULL implies zero vector
+ BYTE *const input, // plain text
+ int inlen, // size of plaintext
+ BYTE *const output, // encrypted text
+ int *outlen, // OUTPUT: size of encrypted text
+ const int checksumlen // size of checksum, if 0 no checksum is used
+);
+
+int __cdecl
+CBCDecrypt(
+ BYTE *key, // secret key
+ BYTE *ivec, // initialization vector, null ptr implies zero vector
+ BYTE *input, // encrypted text
+ int inlen, // size of encrypted text
+ BYTE *output, // plain text
+ int *outlen, // OUTPUT: size of plaintext
+ int checksumlen // size of checksum; 0=> no checksum
+);
+
+//
+// Wrappers to the RSA code.
+//
+
+int __cdecl
+RSAGetInputBlockSize(
+ BYTE *keydata,
+ int keylen
+);
+
+BYTE * __cdecl
+RSAGetModulus(
+ BYTE *keydata,
+ int keylen,
+ int *modSize
+);
+
+BYTE * _cdecl
+RSAGetPublicExponent(
+ BYTE *keydata,
+ int keylen,
+ int *expSize
+);
+
+int __cdecl
+RSAPack(
+ BYTE *input,
+ int inlen,
+ BYTE *output,
+ int blocksize
+);
+
+int __cdecl
+RSAPublic(
+ BYTE *pukeydata, // BSAFE 1 itemized public key data
+ int pukeylen, // length of BSAFE1 keydata (including sign)
+ BYTE *input, // input block
+ int inlen, // size of input (< modulus)
+ BYTE *output // encrypted block (modulus sized)
+);
+
+int __cdecl
+RSAPrivate(
+ BYTE *prkeydata,
+ int prkeylen,
+ BYTE *input,
+ int inlen,
+ BYTE *output
+);
+
+int __cdecl
+RSAModMpy(
+ BYTE *pukeydata, // BSAFE 1 itemized public key data
+ int pukeylen, // length of BSAFE1 keydata (including sign)
+ BYTE *input1, // input block
+ int inlen1, // size of input (< modulus)
+ BYTE *input2, // multiplier
+ int inlen2, // size of multiplier
+ BYTE *output // encrypted block (modulus sized)
+);
+
+int __cdecl
+RSAModExp(
+ BYTE *pukeydata, // BSAFE 1 itemized public key data
+ int pukeylen, // length of BSAFE1 keydata (including sign)
+ BYTE *input1, // input block
+ int inlen1, // size of input (< modulus)
+ BYTE *exponent,
+ int explen,
+ BYTE *output // encrypted block (modulus sized)
+);
+
diff --git a/private/nw/rdr/data.c b/private/nw/rdr/data.c
new file mode 100644
index 000000000..4a0352afe
--- /dev/null
+++ b/private/nw/rdr/data.c
@@ -0,0 +1,373 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ NwData.c
+
+Abstract:
+
+ This module declares the global data used by the Nw file system.
+
+Author:
+
+ Colin Watson [ColinW] 19-Dec-1992
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+#include <stdlib.h>
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_CATCH_EXCEPTIONS)
+
+PEPROCESS FspProcess;
+
+PDEVICE_OBJECT FileSystemDeviceObject = NULL;
+
+//
+// The volume control block for the redirector device.
+//
+
+RCB NwRcb;
+
+//
+// The ScbSpinLock protects the entire ScbQueue and the first part of the
+// Scb entries on the queue. The first part of the Scb includes the name
+// of the server and a reference count
+//
+
+KSPIN_LOCK ScbSpinLock;
+LIST_ENTRY ScbQueue;
+
+//
+// A permanent SCB to synchronize access to the network.
+//
+
+NONPAGED_SCB NwPermanentNpScb;
+
+LARGE_INTEGER NwMaxLarge = {MAXULONG,MAXLONG};
+ULONG NwAbsoluteTotalWaitTime = 0;
+
+TDI_ADDRESS_IPX OurAddress = {0,0,0,0,0,0,0,0};
+UNICODE_STRING IpxTransportName;
+HANDLE IpxHandle = NULL;
+PDEVICE_OBJECT pIpxDeviceObject = NULL;
+PFILE_OBJECT pIpxFileObject = NULL;
+
+LIST_ENTRY LogonList;
+LOGON Guest;
+LARGE_INTEGER DefaultLuid = SYSTEM_LUID;
+
+//
+// A global list of VCBs, and a monotonic increasing VCB entry, used to
+// control connection enumeration.
+//
+
+LIST_ENTRY GlobalVcbList;
+ULONG CurrentVcbEntry;
+
+#if 0
+//
+// HACKHACK - List of outstanding find notify request
+// Protected by NwRcb resource.
+//
+
+LIST_ENTRY FnList;
+#endif
+
+//
+// Drive mapping table of redirected drives. 26 disk drive mappings +
+// 10 LPT mappings.
+//
+// Netware supports 32 disk redirections, but this funkiness is handled
+// by the 16-bit code.
+//
+
+PVCB DriveMapTable[DRIVE_MAP_TABLE_SIZE];
+
+FAST_IO_DISPATCH NwFastIoDispatch;
+
+//
+// Scavenger related data
+//
+
+ULONG NwScavengerTickCount; // The current tick count
+ULONG NwScavengerTickRunCount; // The count at which to run the scavenger routine
+KSPIN_LOCK NwScavengerSpinLock; // Lock to protect access to the above.
+
+//
+// Message queue data
+//
+
+LIST_ENTRY NwGetMessageList; // List of Get Message IRP contexts
+KSPIN_LOCK NwMessageSpinLock; // Protects the list above.
+
+//
+// Pending lock list
+//
+
+LIST_ENTRY NwPendingLockList; // List of pending File lock IRP contexts
+KSPIN_LOCK NwPendingLockSpinLock;// Protects the list above.
+
+//
+// Lock to synchronize all file opens.
+//
+
+ERESOURCE NwOpenResource;
+
+//
+// Configuration data
+//
+
+BOOLEAN NwBurstModeEnabled = FALSE;
+ULONG NwMaxSendSize = 0;
+ULONG NwMaxReceiveSize = 0;
+ULONG NwPrintOptions = 0x98; // BUGBUG
+UNICODE_STRING NwProviderName = { 0, 0, NULL };
+
+LONG MaxSendDelay = 50000;
+LONG MaxReceiveDelay = 50000;
+LONG MinSendDelay = 0;
+LONG MinReceiveDelay = 0;
+LONG BurstSuccessCount = 1;
+LONG BurstSuccessCount2 = 3;
+LONG AllowGrowth = 0;
+LONG DontShrink = 0;
+LONG SendExtraNcp = 1;
+LONG DefaultMaxPacketSize = 0;
+LONG PacketThreshold = 1500; // Size to use Large vs Small PacketAdjustment
+LONG LargePacketAdjustment = 38;
+LONG LipPacketAdjustment = 0;
+LONG LipAccuracy = BURST_PACKET_SIZE_TOLERANCE;
+LONG Japan = 0; // Controls special DBCS translation
+LONG DisableReadCache = 0; // disable file i/o read cache
+LONG DisableWriteCache = 0; // disable file i/o write cache
+LONG FavourLongNames = 0 ; // use LFN where possible
+LARGE_INTEGER TimeOutEventInterval = {0, 0};
+LONG MaxWriteTimeout = 50 ; // tick counts (see write.c)
+LONG MaxReadTimeout = 50 ; // tick counts (see read.c)
+LONG WriteTimeoutMultiplier = 100; // expressed as percentage (see write.c)
+LONG ReadTimeoutMultiplier = 100; // expressed as percentage (see read.c)
+
+ULONG EnableMultipleConnects = 0;
+
+ULONG ReadExecOnlyFiles = 0;
+
+//
+// Static storage area for perfmon statistics
+//
+
+NW_REDIR_STATISTICS Stats;
+ULONG ContextCount = 0;
+
+//
+// Data structure used to track discardable code.
+//
+
+SECTION_DESCRIPTOR NwSectionDescriptor;
+ERESOURCE NwUnlockableCodeResource;
+
+//
+// The lock timeout value.
+//
+
+ULONG LockTimeoutThreshold = 1;
+
+#ifdef _PNP_POWER
+
+//
+// The TDI PNP Bind handle.
+//
+
+HANDLE TdiBindingHandle = NULL;
+UNICODE_STRING TdiIpxDeviceName;
+WCHAR IpxDevice[] = L"\\Device\\NwlnkIpx";
+
+#endif
+
+//
+// We can't have the scavenger and a line change request running
+// at the same time since they both run on worker threads and
+// walk across all the SCBs. Therefore, when either is running,
+// we set the WorkerRunning value used by the scavenger to TRUE.
+// If a scavenger run tries to happen while a line change request
+// is running, it gets skipped. If a line change request comes in
+// while the scavenger is running, we set DelayedProcessLineChange
+// to TRUE and run it when the scavenger finishes.
+//
+// These values are protected by the existing scavenger spin lock.
+//
+
+BOOLEAN DelayedProcessLineChange = FALSE;
+PIRP DelayedLineChangeIrp = NULL;
+
+#ifdef NWDBG
+
+ULONG NwDebug = 0;
+//ULONG NwDebug = 0xffffff;
+ULONG NwMemDebug = 0xffffffff;
+LONG NwDebugTraceIndent = 0;
+
+ULONG NwFsdEntryCount = 0;
+ULONG NwFspEntryCount = 0;
+ULONG NwIoCallDriverCount = 0;
+
+LONG NwPerformanceTimerLevel = 0x00000000;
+
+ULONG NwTotalTicks[32] = { 0 };
+
+//
+// Debug data for tracking pool usage
+//
+
+KSPIN_LOCK NwDebugInterlock;
+ERESOURCE NwDebugResource;
+
+LIST_ENTRY NwPagedPoolList;
+LIST_ENTRY NwNonpagedPoolList;
+
+ULONG MdlCount;
+ULONG IrpCount;
+
+#endif // NWDBG
+
+//
+// Configurable parameters.
+//
+
+SHORT DefaultRetryCount = DEFAULT_RETRY_COUNT;
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwInitializeData )
+#endif
+
+VOID
+NwInitializeData(
+ VOID
+ )
+{
+ LARGE_INTEGER Now;
+
+ PAGED_CODE();
+
+ NwRcb.State = RCB_STATE_STOPPED;
+
+#ifdef NWDBG
+ // Initialize pool before allocating any memory
+ InitializeListHead( &NwPagedPoolList );
+ InitializeListHead( &NwNonpagedPoolList );
+ ExInitializeResource( &NwDebugResource );
+ KeInitializeSpinLock( &NwDebugInterlock );
+
+ MdlCount = 0;
+ IrpCount = 0;
+#endif
+
+ ExInitializeResource( &NwOpenResource );
+
+ //
+ // Initialize the scavenger spin lock and run tick count.
+ //
+
+ KeInitializeSpinLock( &NwScavengerSpinLock );
+ NwScavengerTickRunCount = DEFAULT_SCAVENGER_TICK_RUN_COUNT;
+
+ RtlInitUnicodeString( &IpxTransportName, NULL );
+
+#ifdef _PNP_POWER
+
+ RtlInitUnicodeString( &TdiIpxDeviceName, IpxDevice );
+
+#endif
+
+ //
+ // Allocate a permanent Non-paged SCB. This SCB is used to
+ // synchronize access to finding the nearest server.
+ // This initialization must be done before the first possible call
+ // to UnloadDriver.
+ //
+
+ RtlZeroMemory( &NwPermanentNpScb, sizeof( NONPAGED_SCB ) );
+
+ NwPermanentNpScb.NodeTypeCode = NW_NTC_SCBNP;
+ NwPermanentNpScb.NodeByteSize = sizeof(NONPAGED_SCB);
+ NwPermanentNpScb.Reference = 1;
+
+ InitializeListHead( &NwPermanentNpScb.Requests );
+
+ //
+ // Initialize the logonlist to have a default entry with server NULL,
+ // username "GUEST" and null password. This will always be the last
+ // entry on the logonlist so that the workstation service can supply
+ // an override.
+ //
+
+ InitializeListHead( &LogonList );
+
+ Guest.NodeTypeCode = NW_NTC_LOGON;
+ Guest.NodeByteSize = sizeof(LOGON);
+ RtlInitUnicodeString( &Guest.ServerName, NULL );
+ RtlInitUnicodeString( &Guest.PassWord, NULL );
+ RtlInitUnicodeString( &Guest.UserName, L"GUEST" );
+ Guest.UserUid = DefaultLuid;
+ InitializeListHead( &Guest.NdsCredentialList );
+ InsertTailList( &LogonList, &Guest.Next );
+
+ //
+ // Initialize the global VCB list.
+ //
+
+ InitializeListHead( &GlobalVcbList );
+ CurrentVcbEntry = 1;
+
+ //
+ // Initialize the Get message queue.
+ //
+
+ InitializeListHead( &NwGetMessageList );
+ KeInitializeSpinLock( &NwMessageSpinLock );
+
+ //
+ // Initialize the Pending lock queue.
+ //
+
+ InitializeListHead( &NwPendingLockList );
+ KeInitializeSpinLock( &NwPendingLockSpinLock );
+
+ //
+ // Insert the Permanent SCB in the global list of SCBs.
+ //
+
+ InsertHeadList( &ScbQueue, &NwPermanentNpScb.ScbLinks );
+
+#if 0
+ // HACKHACK
+ InitializeListHead( &FnList );
+#endif
+
+ //
+ // Seed the random number generator.
+ //
+
+ KeQuerySystemTime( &Now );
+ srand( Now.LowPart );
+
+ RtlZeroMemory( &Stats, sizeof( NW_REDIR_STATISTICS ) );
+
+ ExInitializeResource( &NwUnlockableCodeResource );
+
+ NwSectionDescriptor.Base = BurstReadTimeout;
+ NwSectionDescriptor.Handle = 0;
+ NwSectionDescriptor.ReferenceCount = 0;
+
+ return;
+}
+
+
diff --git a/private/nw/rdr/data.h b/private/nw/rdr/data.h
new file mode 100644
index 000000000..d7d9f3cea
--- /dev/null
+++ b/private/nw/rdr/data.h
@@ -0,0 +1,238 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ Data.h
+
+Abstract:
+
+ This module declares the global data used by the NetWare redirector
+ file system.
+
+Author:
+
+ Colin Watson [ColinW] 15-Dec-1992
+
+Revision History:
+
+--*/
+
+#ifndef _NWDATA_
+#define _NWDATA_
+
+extern PEPROCESS FspProcess;
+extern PDEVICE_OBJECT FileSystemDeviceObject;
+extern RCB NwRcb;
+
+extern KSPIN_LOCK ScbSpinLock;
+extern LIST_ENTRY ScbQueue;
+extern NONPAGED_SCB NwPermanentNpScb;
+extern SCB NwPermanentScb;
+
+extern LARGE_INTEGER NwMaxLarge;
+extern ULONG NwAbsoluteTotalWaitTime;
+
+extern TDI_ADDRESS_IPX OurAddress;
+extern UNICODE_STRING IpxTransportName;
+extern HANDLE IpxHandle;
+extern PDEVICE_OBJECT pIpxDeviceObject;
+extern PFILE_OBJECT pIpxFileObject;
+
+extern LIST_ENTRY LogonList;
+extern LOGON Guest;
+extern LARGE_INTEGER DefaultLuid;
+
+extern LIST_ENTRY GlobalVcbList;
+extern ULONG CurrentVcbEntry;
+
+//
+// Drive mapping table of redirected drives.
+//
+
+extern PVCB DriveMapTable[];
+
+//
+// The global structure used to contain our fast I/O callbacks
+//
+
+extern FAST_IO_DISPATCH NwFastIoDispatch;
+
+//
+// Configurable paramaters
+//
+
+extern SHORT DefaultRetryCount;
+
+extern ULONG NwScavengerTickCount;
+extern ULONG NwScavengerTickRunCount;
+extern KSPIN_LOCK NwScavengerSpinLock;
+
+extern LIST_ENTRY NwGetMessageList;
+extern KSPIN_LOCK NwMessageSpinLock;
+
+extern LIST_ENTRY NwPendingLockList;
+extern KSPIN_LOCK NwPendingLockSpinLock;
+
+extern ERESOURCE NwOpenResource;
+
+#if 0
+extern LIST_ENTRY FnList; // HACKHACK
+#endif
+
+extern BOOLEAN NwBurstModeEnabled;
+extern ULONG NwMaxSendSize;
+extern ULONG NwMaxReceiveSize;
+extern ULONG NwPrintOptions;
+extern UNICODE_STRING NwProviderName;
+
+extern LONG MaxSendDelay;
+extern LONG MaxReceiveDelay;
+extern LONG MinSendDelay;
+extern LONG MinReceiveDelay;
+extern LONG BurstSuccessCount;
+extern LONG BurstSuccessCount2;
+extern LONG AllowGrowth;
+extern LONG DontShrink;
+extern LONG SendExtraNcp;
+extern LONG DefaultMaxPacketSize;
+extern LONG PacketThreshold;
+extern LONG LargePacketAdjustment;
+extern LONG LipPacketAdjustment;
+extern LONG LipAccuracy;
+extern LONG MaxWriteTimeout;
+extern LONG MaxReadTimeout;
+extern LONG WriteTimeoutMultiplier;
+extern LONG ReadTimeoutMultiplier;
+
+extern ULONG EnableMultipleConnects;
+
+extern ULONG ReadExecOnlyFiles;
+
+extern LONG Japan; // Controls special DBCS translation
+extern LONG DisableReadCache ;
+extern LONG DisableWriteCache ;
+extern LONG FavourLongNames ; // use LFN where possible
+
+extern LARGE_INTEGER TimeOutEventInterval;
+
+extern NW_REDIR_STATISTICS Stats;
+extern ULONG ContextCount;
+
+extern SECTION_DESCRIPTOR NwSectionDescriptor;
+extern ERESOURCE NwUnlockableCodeResource;
+
+extern ULONG LockTimeoutThreshold;
+
+#ifdef _PNP_POWER
+
+extern HANDLE TdiBindingHandle;
+extern UNICODE_STRING TdiIpxDeviceName;
+
+#endif
+
+extern BOOLEAN DelayedProcessLineChange;
+extern PIRP DelayedLineChangeIrp;
+
+#ifdef NWDBG
+
+#define DEBUG_TRACE_ALWAYS (0x00000000)
+#define DEBUG_TRACE_CLEANUP (0x00000001)
+#define DEBUG_TRACE_CLOSE (0x00000002)
+#define DEBUG_TRACE_CREATE (0x00000004)
+#define DEBUG_TRACE_FSCTRL (0x00000008)
+#define DEBUG_TRACE_IPX (0x00000010)
+#define DEBUG_TRACE_LOAD (0x00000020)
+#define DEBUG_TRACE_EXCHANGE (0x00000040)
+#define DEBUG_TRACE_FILOBSUP (0x00000080)
+#define DEBUG_TRACE_STRUCSUP (0x00000100)
+#define DEBUG_TRACE_FSP_DISPATCHER (0x00000200)
+#define DEBUG_TRACE_FSP_DUMP (0x00000400)
+#define DEBUG_TRACE_WORKQUE (0x00000800)
+#define DEBUG_TRACE_UNWIND (0x00001000)
+#define DEBUG_TRACE_CATCH_EXCEPTIONS (0x00002000)
+#define DEBUG_TRACE_ICBS (0x00004000)
+#define DEBUG_TRACE_FILEINFO (0x00008000)
+#define DEBUG_TRACE_DIRCTRL (0x00010000)
+#define DEBUG_TRACE_CONVERT (0x00020000)
+#define DEBUG_TRACE_WRITE (0x00040000)
+#define DEBUG_TRACE_READ (0x00080000)
+#define DEBUG_TRACE_VOLINFO (0x00100000)
+#define DEBUG_TRACE_LOCKCTRL (0x00200000)
+#define DEBUG_TRACE_USERNCP (0x00400000)
+#define DEBUG_TRACE_SECURITY (0x00800000)
+#define DEBUG_TRACE_CACHE (0x01000000)
+#define DEBUG_TRACE_LIP (0x02000000)
+#define DEBUG_TRACE_MDL (0x04000000)
+
+#define DEBUG_TRACE_NDS (0x10000000)
+#define DEBUG_TRACE_SCAVENGER (0x40000000)
+#define DEBUG_TRACE_TIMER (0x80000000)
+
+extern ULONG NwDebug;
+extern ULONG NwMemDebug;
+extern LONG NwDebugTraceIndent;
+
+#define DebugTrace( I, L, M, P ) RealDebugTrace( I, L, "%08lx: %*s"M, (PVOID)(P) )
+
+#define DebugUnwind(X) { \
+ if (AbnormalTermination()) { \
+ DebugTrace(0, DEBUG_TRACE_UNWIND, #X ", Abnormal termination.\n", 0); \
+ } \
+}
+
+//
+// The following variables are used to keep track of the total amount
+// of requests processed by the file system, and the number of requests
+// that end up being processed by the Fsp thread. The first variable
+// is incremented whenever an Irp context is created (which is always
+// at the start of an Fsd entry point) and the second is incremented
+// by read request.
+//
+
+extern ULONG NwFsdEntryCount;
+extern ULONG NwFspEntryCount;
+extern ULONG NwIoCallDriverCount;
+extern ULONG NwTotalTicks[];
+
+extern KSPIN_LOCK NwDebugInterlock;
+extern ERESOURCE NwDebugResource;
+
+extern LIST_ENTRY NwPagedPoolList;
+extern LIST_ENTRY NwNonpagedPoolList;
+
+extern ULONG MdlCount;
+extern ULONG IrpCount;
+
+#define DebugDoit(X) {X;}
+
+extern LONG NwPerformanceTimerLevel;
+
+#define TimerStart(LEVEL) { \
+ LARGE_INTEGER TStart, TEnd; \
+ LARGE_INTEGER TElapsed; \
+ TStart = KeQueryPerformanceCounter( NULL ); \
+
+#define TimerStop(LEVEL,s) \
+ TEnd = KeQueryPerformanceCounter( NULL ); \
+ TElapsed = RtlLargeIntegerSubtract( TEnd, TStart ); \
+ /* NwTotalTicks[NwLogOf(LEVEL)] += TElapsed.LowPart; */ \
+ if (FlagOn( NwPerformanceTimerLevel, (LEVEL))) { \
+ DbgPrint("Time of %s %ld\n", (s), TElapsed.LowPart ); \
+ } \
+}
+
+#else
+
+#define DebugTrace(INDENT,LEVEL,X,Y) {NOTHING;}
+#define DebugUnwind(X) {NOTHING;}
+#define DebugDoit(X) {NOTHING;}
+
+#define TimerStart(LEVEL)
+#define TimerStop(LEVEL,s)
+
+#endif // NWDBG
+
+#endif // _NWDATA_
+
diff --git a/private/nw/rdr/debug.c b/private/nw/rdr/debug.c
new file mode 100644
index 000000000..52e356300
--- /dev/null
+++ b/private/nw/rdr/debug.c
@@ -0,0 +1,780 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ Debug.c
+
+Abstract:
+
+ This module declares the Debug only code used by the NetWare redirector
+ file system.
+
+Author:
+
+ Colin Watson [ColinW] 05-Jan-1993
+
+Revision History:
+
+--*/
+#include "procs.h"
+#include <stdio.h>
+#include <stdarg.h>
+
+#define LINE_SIZE 511
+#define BUFFER_LINES 50
+
+
+#ifdef NWDBG
+
+#include <stdlib.h> // rand()
+int FailAllocateMdl = 0;
+
+ULONG MaxDump = 256;
+CHAR DBuffer[BUFFER_LINES*LINE_SIZE+1];
+PCHAR DBufferPtr = DBuffer;
+
+//
+// The reference count debug buffer.
+//
+
+CHAR RBuffer[BUFFER_LINES*LINE_SIZE+1];
+PCHAR RBufferPtr = RBuffer;
+
+LIST_ENTRY MdlList;
+
+VOID
+HexDumpLine (
+ PCHAR pch,
+ ULONG len,
+ PCHAR s,
+ PCHAR t,
+ USHORT flag
+ );
+
+ULONG
+NwMemDbg (
+ IN PCH Format,
+ ...
+ )
+
+//++
+//
+// Routine Description:
+//
+// Effectively DbgPrint to the debugging console.
+//
+// Arguments:
+//
+// Same as for DbgPrint
+//
+//--
+
+{
+ va_list arglist;
+ int Length;
+
+ //
+ // Format the output into a buffer and then print it.
+ //
+
+ va_start(arglist, Format);
+
+ Length = _vsnprintf(DBufferPtr, LINE_SIZE, Format, arglist);
+
+ if (Length < 0) {
+ DbgPrint( "NwRdr: Message is too long for NwMemDbg\n");
+ return 0;
+ }
+
+ va_end(arglist);
+
+ ASSERT( Length <= LINE_SIZE );
+ ASSERT( Length != 0 );
+ ASSERT( DBufferPtr < &DBuffer[BUFFER_LINES*LINE_SIZE+1]);
+ ASSERT( DBufferPtr >= DBuffer);
+
+ DBufferPtr += Length;
+ DBufferPtr[0] = '\0';
+
+ // Avoid running off the end of the buffer and exit
+
+ if (DBufferPtr >= (DBuffer+((BUFFER_LINES-1) * LINE_SIZE))) {
+ DBufferPtr = DBuffer;
+
+ }
+
+ return 0;
+}
+
+VOID
+RefDbgTrace (
+ PVOID Resource,
+ DWORD Count,
+ BOOLEAN Reference,
+ PBYTE FileName,
+ UINT Line
+)
+/**
+
+ Routine Description:
+
+ NwRefDebug logs reference count operations to expose
+ reference count errors or leaks in the redirector.
+
+ Arguments:
+
+ Resource - The object we're adjusting the reference count on.
+ Count - The current count on the object.
+ Reference - If TRUE we are doing a REFERENCE.
+ Otherwise, we are doing a DEREFERENCE.
+ FileName - The callers file name.
+ Line - The callers line number.
+
+**/
+{
+ int Length;
+ int NextCount;
+
+ //
+ // Format the output into a buffer and then print it.
+ //
+
+ if ( Reference )
+ NextCount = Count + 1;
+ else
+ NextCount = Count - 1;
+
+ Length = sprintf( RBufferPtr,
+ "%08lx: R=%08lx, %lu -> %lu (%s, line %d)\n",
+ (PVOID)PsGetCurrentThread(),
+ Resource,
+ Count,
+ NextCount,
+ FileName,
+ Line );
+
+ if (Length < 0) {
+ DbgPrint( "NwRdr: Message is too long for NwRefDbg\n");
+ return;
+ }
+
+ ASSERT( Length <= LINE_SIZE );
+ ASSERT( Length != 0 );
+ ASSERT( RBufferPtr < &RBuffer[BUFFER_LINES*LINE_SIZE+1]);
+ ASSERT( RBufferPtr >= RBuffer);
+
+ RBufferPtr += Length;
+ RBufferPtr[0] = '\0';
+
+ // Avoid running off the end of the buffer and exit
+
+ if (RBufferPtr >= (RBuffer+((BUFFER_LINES-1) * LINE_SIZE))) {
+ RBufferPtr = RBuffer;
+ }
+
+ return;
+}
+
+VOID
+RealDebugTrace(
+ LONG Indent,
+ ULONG Level,
+ PCH Message,
+ PVOID Parameter
+ )
+/*++
+
+Routine Description:
+
+
+Arguments:
+
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ if ( (Level == 0) || (NwMemDebug & Level )) {
+ NwMemDbg( Message, PsGetCurrentThread(), 1, "", Parameter );
+ }
+
+ if ( (Level == 0) || (NwDebug & Level )) {
+
+ if ( Indent < 0) {
+ NwDebugTraceIndent += Indent;
+ }
+
+ DbgPrint( Message, PsGetCurrentThread(), NwDebugTraceIndent, "", Parameter );
+
+ if ( Indent > 0) {
+ NwDebugTraceIndent += Indent;
+ }
+
+ if (NwDebugTraceIndent < 0) {
+ NwDebugTraceIndent = 0;
+ }
+ }
+}
+
+VOID
+dump(
+ IN ULONG Level,
+ IN PVOID far_p,
+ IN ULONG len
+ )
+/*++
+
+Routine Description:
+ Dump Min(len, MaxDump) bytes in classic hex dump style if debug
+ output is turned on for this level.
+
+Arguments:
+
+ IN Level - 0 if always display. Otherwise only display if a
+ corresponding bit is set in NwDebug.
+
+ IN far_p - address of buffer to start dumping from.
+
+ IN len - length in bytes of buffer.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ ULONG l;
+ char s[80], t[80];
+ PCHAR far_pchar = (PCHAR)far_p;
+
+ if ( (Level == 0) || (NwDebug & Level )) {
+ if (len > MaxDump)
+ len = MaxDump;
+
+ while (len) {
+ l = len < 16 ? len : 16;
+
+ DbgPrint("\n%lx ", far_pchar);
+ HexDumpLine (far_pchar, l, s, t, 0);
+ DbgPrint("%s%.*s%s", s, 1 + ((16 - l) * 3), "", t);
+ NwMemDbg ( "%lx: %s%.*s%s\n",
+ far_pchar, s, 1 + ((16 - l) * 3), "", t);
+
+ len -= l;
+ far_pchar += l;
+ }
+ DbgPrint("\n");
+
+ }
+}
+
+VOID
+dumpMdl(
+ IN ULONG Level,
+ IN PMDL Mdl
+ )
+/*++
+
+Routine Description:
+ Dump the memory described by each part of a chained Mdl.
+
+Arguments:
+
+ IN Level - 0 if always display. Otherwise only display if a
+ corresponding bit is set in NwDebug.
+
+ Mdl - Supplies the addresses of the memory to be dumped.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PMDL Next;
+ ULONG len;
+
+
+ if ( (Level == 0) || (NwDebug & Level )) {
+ Next = Mdl; len = 0;
+ do {
+
+ dump(Level, MmGetSystemAddressForMdl(Next), MIN(MmGetMdlByteCount(Next), MaxDump-len));
+
+ len += MmGetMdlByteCount(Next);
+ } while ( (Next = Next->Next) != NULL &&
+ len <= MaxDump);
+ }
+}
+
+VOID
+HexDumpLine (
+ PCHAR pch,
+ ULONG len,
+ PCHAR s,
+ PCHAR t,
+ USHORT flag
+ )
+{
+ static UCHAR rghex[] = "0123456789ABCDEF";
+
+ UCHAR c;
+ UCHAR *hex, *asc;
+
+
+ hex = s;
+ asc = t;
+
+ *(asc++) = '*';
+ while (len--) {
+ c = *(pch++);
+ *(hex++) = rghex [c >> 4] ;
+ *(hex++) = rghex [c & 0x0F];
+ *(hex++) = ' ';
+ *(asc++) = (c < ' ' || c > '~') ? (CHAR )'.' : c;
+ }
+ *(asc++) = '*';
+ *asc = 0;
+ *hex = 0;
+
+ flag;
+}
+
+typedef struct _NW_POOL_HEADER {
+ ULONG Signature;
+ ULONG BufferSize;
+ ULONG BufferType;
+ LIST_ENTRY ListEntry;
+ ULONG Pad; // Pad to Q-word align
+} NW_POOL_HEADER, *PNW_POOL_HEADER;
+
+typedef struct _NW_POOL_TRAILER {
+ ULONG Signature;
+} NW_POOL_TRAILER;
+
+typedef NW_POOL_TRAILER UNALIGNED *PNW_POOL_TRAILER;
+
+PVOID
+NwAllocatePool(
+ ULONG Type,
+ ULONG Size,
+ BOOLEAN RaiseStatus
+ )
+{
+ PCHAR Buffer;
+ PNW_POOL_HEADER PoolHeader;
+ PNW_POOL_TRAILER PoolTrailer;
+
+ if ( RaiseStatus ) {
+ Buffer = FsRtlAllocatePool(
+ Type,
+ sizeof( NW_POOL_HEADER ) + sizeof( NW_POOL_TRAILER ) + Size );
+ } else {
+#ifndef QFE_BUILD
+ Buffer = ExAllocatePoolWithTag(
+ Type,
+ sizeof( NW_POOL_HEADER )+sizeof( NW_POOL_TRAILER )+Size,
+ 'scwn' );
+#else
+ Buffer = ExAllocatePool(
+ Type,
+ sizeof( NW_POOL_HEADER )+sizeof( NW_POOL_TRAILER )+Size );
+#endif
+
+ if ( Buffer == NULL ) {
+ return( NULL );
+ }
+ }
+
+ PoolHeader = (PNW_POOL_HEADER)Buffer;
+ PoolTrailer = (PNW_POOL_TRAILER)(Buffer + sizeof( NW_POOL_HEADER ) + Size);
+
+ PoolHeader->Signature = 0x11111111;
+ PoolHeader->BufferSize = Size;
+ PoolHeader->BufferType = Type;
+
+ PoolTrailer->Signature = 0x99999999;
+
+ if ( Type == PagedPool ) {
+ ExAcquireResourceExclusive( &NwDebugResource, TRUE );
+ InsertTailList( &NwPagedPoolList, &PoolHeader->ListEntry );
+ ExReleaseResource( &NwDebugResource );
+ } else if ( Type == NonPagedPool ) {
+ ExInterlockedInsertTailList( &NwNonpagedPoolList, &PoolHeader->ListEntry, &NwDebugInterlock );
+ } else {
+ KeBugCheck( RDR_FILE_SYSTEM );
+ }
+
+ return( Buffer + sizeof( NW_POOL_HEADER ) );
+}
+
+VOID
+NwFreePool(
+ PVOID Buffer
+ )
+{
+ PNW_POOL_HEADER PoolHeader;
+ PNW_POOL_TRAILER PoolTrailer;
+ KIRQL OldIrql;
+
+ PoolHeader = (PNW_POOL_HEADER)((PCHAR)Buffer - sizeof( NW_POOL_HEADER ));
+ ASSERT( PoolHeader->Signature == 0x11111111 );
+ ASSERT( PoolHeader->BufferType == PagedPool ||
+ PoolHeader->BufferType == NonPagedPool );
+
+ PoolTrailer = (PNW_POOL_TRAILER)((PCHAR)Buffer + PoolHeader->BufferSize );
+ ASSERT( PoolTrailer->Signature == 0x99999999 );
+
+ if ( PoolHeader->BufferType == PagedPool ) {
+ ExAcquireResourceExclusive( &NwDebugResource, TRUE );
+ RemoveEntryList( &PoolHeader->ListEntry );
+ ExReleaseResource( &NwDebugResource );
+ } else {
+ KeAcquireSpinLock( &NwDebugInterlock, &OldIrql );
+ RemoveEntryList( &PoolHeader->ListEntry );
+ KeReleaseSpinLock( &NwDebugInterlock, OldIrql );
+ }
+
+ ExFreePool( PoolHeader );
+}
+
+//
+// Debug functions for allocating and deallocating IRPs and MDLs
+//
+
+PIRP
+NwAllocateIrp(
+ CCHAR Size,
+ BOOLEAN ChargeQuota
+ )
+{
+ ExInterlockedIncrementLong( &IrpCount, &NwDebugInterlock );
+ return IoAllocateIrp( Size, ChargeQuota );
+}
+
+VOID
+NwFreeIrp(
+ PIRP Irp
+ )
+{
+ ExInterlockedDecrementLong( &IrpCount, &NwDebugInterlock );
+ IoFreeIrp( Irp );
+}
+
+typedef struct _NW_MDL {
+ LIST_ENTRY Next;
+ PUCHAR File;
+ int Line;
+ PMDL pMdl;
+} NW_MDL, *PNW_MDL;
+
+//int DebugLine = 2461;
+
+PMDL
+NwAllocateMdl(
+ PVOID Va,
+ ULONG Length,
+ BOOLEAN Secondary,
+ BOOLEAN ChargeQuota,
+ PIRP Irp,
+ PUCHAR FileName,
+ int Line
+ )
+{
+ PNW_MDL Buffer;
+
+ static BOOLEAN MdlSetup = FALSE;
+
+ if (MdlSetup == FALSE) {
+
+ InitializeListHead( &MdlList );
+
+ MdlSetup = TRUE;
+ }
+
+ if ( FailAllocateMdl != 0 ) {
+ if ( ( rand() % FailAllocateMdl ) == 0 ) {
+ return(NULL);
+ }
+ }
+
+#ifndef QFE_BUILD
+ Buffer = ExAllocatePoolWithTag(
+ NonPagedPool,
+ sizeof( NW_MDL),
+ 'scwn' );
+#else
+ Buffer = ExAllocatePool(
+ NonPagedPool,
+ sizeof( NW_MDL));
+#endif
+
+ if ( Buffer == NULL ) {
+ return( NULL );
+ }
+
+ ExInterlockedIncrementLong( &MdlCount, &NwDebugInterlock );
+
+ Buffer->File = FileName;
+ Buffer->Line = Line;
+ Buffer->pMdl = IoAllocateMdl( Va, Length, Secondary, ChargeQuota, Irp );
+
+ ExInterlockedInsertTailList( &MdlList, &Buffer->Next, &NwDebugInterlock );
+
+/*
+ if (DebugLine == Line) {
+ DebugTrace( 0, DEBUG_TRACE_MDL, "AllocateMdl -> %08lx\n", Buffer->pMdl );
+ DebugTrace( 0, DEBUG_TRACE_MDL, "AllocateMdl -> %08lx\n", Line );
+ }
+*/
+ return(Buffer->pMdl);
+}
+
+VOID
+NwFreeMdl(
+ PMDL Mdl
+ )
+{
+ PLIST_ENTRY MdlEntry;
+ PNW_MDL Buffer;
+ KIRQL OldIrql;
+
+ ExInterlockedDecrementLong( &MdlCount, &NwDebugInterlock );
+
+ KeAcquireSpinLock( &NwDebugInterlock, &OldIrql );
+ // Find the Mdl in the list and remove it.
+
+ for (MdlEntry = MdlList.Flink ;
+ MdlEntry != &MdlList ;
+ MdlEntry = MdlEntry->Flink ) {
+
+ Buffer = CONTAINING_RECORD( MdlEntry, NW_MDL, Next );
+
+ if (Buffer->pMdl == Mdl) {
+
+ RemoveEntryList( &Buffer->Next );
+
+ KeReleaseSpinLock( &NwDebugInterlock, OldIrql );
+
+ IoFreeMdl( Mdl );
+ DebugTrace( 0, DEBUG_TRACE_MDL, "FreeMDL - %08lx\n", Mdl );
+/*
+ if (DebugLine == Buffer->Line) {
+ DebugTrace( 0, DEBUG_TRACE_MDL, "FreeMdl -> %08lx\n", Mdl );
+ DebugTrace( 0, DEBUG_TRACE_MDL, "FreeMdl -> %08lx\n", Buffer->Line );
+ }
+*/
+ ExFreePool(Buffer);
+
+ return;
+ }
+ }
+ ASSERT( FALSE );
+
+ KeReleaseSpinLock( &NwDebugInterlock, OldIrql );
+}
+
+/*
+VOID
+NwLookForMdl(
+ )
+{
+ PLIST_ENTRY MdlEntry;
+ PNW_MDL Buffer;
+ KIRQL OldIrql;
+
+ KeAcquireSpinLock( &NwDebugInterlock, &OldIrql );
+ // Find the Mdl in the list and remove it.
+
+ for (MdlEntry = MdlList.Flink ;
+ MdlEntry != &MdlList ;
+ MdlEntry = MdlEntry->Flink ) {
+
+ Buffer = CONTAINING_RECORD( MdlEntry, NW_MDL, Next );
+
+ if (Buffer->Line == DebugLine) {
+
+ DebugTrace( 0, DEBUG_TRACE_MDL, "LookForMdl -> %08lx\n", Buffer );
+ DbgBreakPoint();
+
+ }
+ }
+
+ KeReleaseSpinLock( &NwDebugInterlock, OldIrql );
+}
+*/
+
+//
+// Function version of resource macro, to make debugging easier.
+//
+
+VOID
+NwAcquireExclusiveRcb(
+ PRCB Rcb,
+ BOOLEAN Wait )
+{
+ ExAcquireResourceExclusive( &((Rcb)->Resource), Wait );
+}
+
+VOID
+NwAcquireSharedRcb(
+ PRCB Rcb,
+ BOOLEAN Wait )
+{
+ ExAcquireResourceShared( &((Rcb)->Resource), Wait );
+}
+
+VOID
+NwReleaseRcb(
+ PRCB Rcb )
+{
+ ExReleaseResource( &((Rcb)->Resource) );
+}
+
+VOID
+NwAcquireExclusiveFcb(
+ PNONPAGED_FCB pFcb,
+ BOOLEAN Wait )
+{
+ ExAcquireResourceExclusive( &((pFcb)->Resource), Wait );
+}
+
+VOID
+NwAcquireSharedFcb(
+ PNONPAGED_FCB pFcb,
+ BOOLEAN Wait )
+{
+ ExAcquireResourceShared( &((pFcb)->Resource), Wait );
+}
+
+VOID
+NwReleaseFcb(
+ PNONPAGED_FCB pFcb )
+{
+ ExReleaseResource( &((pFcb)->Resource) );
+}
+
+VOID
+NwAcquireOpenLock(
+ VOID
+ )
+{
+ ExAcquireResourceExclusive( &NwOpenResource, TRUE );
+}
+
+VOID
+NwReleaseOpenLock(
+ VOID
+ )
+{
+ ExReleaseResource( &NwOpenResource );
+}
+
+
+//
+// code to dump ICBs
+//
+
+VOID DumpIcbs(VOID)
+{
+ PVCB Vcb;
+ PFCB Fcb;
+ PICB Icb;
+ PLIST_ENTRY VcbListEntry;
+ PLIST_ENTRY FcbListEntry;
+ PLIST_ENTRY IcbListEntry;
+ KIRQL OldIrql;
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ DbgPrint("\nICB Pid State Scb/Fcb Name\n", 0);
+ for ( VcbListEntry = GlobalVcbList.Flink;
+ VcbListEntry != &GlobalVcbList ;
+ VcbListEntry = VcbListEntry->Flink ) {
+
+ Vcb = CONTAINING_RECORD( VcbListEntry, VCB, GlobalVcbListEntry );
+
+ for ( FcbListEntry = Vcb->FcbList.Flink;
+ FcbListEntry != &(Vcb->FcbList) ;
+ FcbListEntry = FcbListEntry->Flink ) {
+
+ Fcb = CONTAINING_RECORD( FcbListEntry, FCB, FcbListEntry );
+
+ for ( IcbListEntry = Fcb->IcbList.Flink;
+ IcbListEntry != &(Fcb->IcbList) ;
+ IcbListEntry = IcbListEntry->Flink ) {
+
+ Icb = CONTAINING_RECORD( IcbListEntry, ICB, ListEntry );
+
+ DbgPrint("%08lx", Icb);
+ DbgPrint(" %08lx",(DWORD)Icb->Pid);
+ DbgPrint(" %08lx",Icb->State);
+ DbgPrint(" %08lx",Icb->SuperType.Scb);
+ DbgPrint(" %wZ\n",
+ &(Icb->FileObject->FileName) );
+ }
+ }
+ }
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+ NwReleaseRcb( &NwRcb );
+}
+
+#endif // ifdef NWDBG
+
+//
+// Ref counting debug routines.
+//
+
+#ifdef NWDBG
+
+VOID
+ChkNwReferenceScb(
+ PNONPAGED_SCB pNpScb,
+ PBYTE FileName,
+ UINT Line,
+ BOOLEAN Silent
+) {
+
+ if ( (pNpScb)->NodeTypeCode != NW_NTC_SCBNP ) {
+ DbgBreakPoint();
+ }
+
+ if ( !Silent) {
+ RefDbgTrace( pNpScb, pNpScb->Reference, TRUE, FileName, Line );
+ }
+
+ ExInterlockedIncrementLong( &(pNpScb)->Reference, &(pNpScb)->NpScbInterLock );
+}
+
+VOID
+ChkNwDereferenceScb(
+ PNONPAGED_SCB pNpScb,
+ PBYTE FileName,
+ UINT Line,
+ BOOLEAN Silent
+) {
+
+ if ( (pNpScb)->Reference == 0 ) {
+ DbgBreakPoint();
+ }
+
+ if ( (pNpScb)->NodeTypeCode != NW_NTC_SCBNP ) {
+ DbgBreakPoint();
+ }
+
+ if ( !Silent ) {
+ RefDbgTrace( pNpScb, pNpScb->Reference, FALSE, FileName, Line );
+ }
+
+ ExInterlockedDecrementLong( &(pNpScb)->Reference, &(pNpScb)->NpScbInterLock );
+}
+
+#endif
+
diff --git a/private/nw/rdr/deviosup.c b/private/nw/rdr/deviosup.c
new file mode 100644
index 000000000..1ff83c50f
--- /dev/null
+++ b/private/nw/rdr/deviosup.c
@@ -0,0 +1,153 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ deviosup.c
+
+Abstract:
+
+ This module implements the memory locking routines for the netware
+ redirector.
+
+Author:
+
+ Manny Weiser (mannyw) 10-Mar-1993
+
+Revision History:
+
+--*/
+
+#include "procs.h"
+
+//
+// Local debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_DEVIOSUP)
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwMapUserBuffer )
+#pragma alloc_text( PAGE, NwLockUserBuffer )
+#endif
+
+
+VOID
+NwMapUserBuffer (
+ IN OUT PIRP Irp,
+ IN KPROCESSOR_MODE AccessMode,
+ OUT PVOID *UserBuffer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine obtains a usable virtual address for the user buffer
+ for the current I/O request in the specified mode.
+
+Arguments:
+
+ Irp - Pointer to the Irp for the request.
+
+ AccessMode - UserMode or KernelMode.
+
+ UserBuffer - Returns pointer to mapped user buffer.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ AccessMode;
+
+ //
+ // If there is no Mdl, then we must be in the Fsd, and we can simply
+ // return the UserBuffer field from the Irp.
+ //
+
+ if (Irp->MdlAddress == NULL) {
+
+ *UserBuffer = Irp->UserBuffer;
+ return;
+ }
+
+ //
+ // Get a system virtual address for the buffer.
+ //
+
+ *UserBuffer = MmGetSystemAddressForMdl( Irp->MdlAddress );
+ return;
+}
+
+
+VOID
+NwLockUserBuffer (
+ IN OUT PIRP Irp,
+ IN LOCK_OPERATION Operation,
+ IN ULONG BufferLength
+ )
+
+/*++
+
+Routine Description:
+
+ This routine locks the specified buffer for the specified type of
+ access. The file system requires this routine since it does not
+ ask the I/O system to lock its buffers for direct I/O. This routine
+ may only be called from the FSD while still in the user context.
+
+Arguments:
+
+ Irp - Pointer to the IRP for which the buffer is to be locked.
+
+ Operation - IoWriteAccess for read operations, or IoReadAccess for
+ write operations.
+
+ BufferLength - Length of user buffer.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ PMDL mdl;
+
+ PAGED_CODE();
+
+ if (Irp->MdlAddress == NULL) {
+
+ //
+ // This read is bound for the current process. Perform the
+ // same functions as above, only do not switch processes.
+ //
+
+ mdl = IoAllocateMdl( Irp->UserBuffer, BufferLength, FALSE, TRUE, Irp );
+
+ if (mdl == NULL) {
+
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ try {
+
+ MmProbeAndLockPages( mdl,
+ Irp->RequestorMode,
+ Operation );
+
+ } except(EXCEPTION_EXECUTE_HANDLER) {
+
+ IoFreeMdl( mdl );
+ Irp->MdlAddress = NULL;
+ ExRaiseStatus( FsRtlNormalizeNtstatus( GetExceptionCode(),
+ STATUS_INVALID_USER_BUFFER ));
+ }
+ }
+}
diff --git a/private/nw/rdr/dir.c b/private/nw/rdr/dir.c
new file mode 100644
index 000000000..64769b9ac
--- /dev/null
+++ b/private/nw/rdr/dir.c
@@ -0,0 +1,1672 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ dir.c
+
+Abstract:
+
+ This module implements the file directory routines for the
+ Netware Redirector.
+
+Author:
+
+ Manny Weiser (mannyw) 4-Mar-1993
+
+Revision History:
+
+--*/
+
+#include "procs.h"
+
+typedef struct _NW_DIRECTORY_INFO {
+ WCHAR FileNameBuffer[NW_MAX_FILENAME_LENGTH];
+ UNICODE_STRING FileName;
+ UCHAR Attributes;
+ USHORT CreationDate;
+ USHORT CreationTime;
+ USHORT LastAccessDate;
+ USHORT LastUpdateDate;
+ USHORT LastUpdateTime;
+ ULONG FileSize;
+ ULONG DosDirectoryEntry;
+} NW_DIRECTORY_INFO, *PNW_DIRECTORY_INFO;
+
+//
+// Local debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_DIRCTRL)
+
+NTSTATUS
+NwCommonDirectoryControl (
+ IN PIRP_CONTEXT pIrpContext
+ );
+
+NTSTATUS
+NwQueryDirectory (
+ IN PIRP_CONTEXT pIrpContext,
+ IN PICB pIcb
+ );
+
+NTSTATUS
+GetNextFile(
+ PIRP_CONTEXT pIrpContext,
+ PICB Icb,
+ PULONG fileIndexLow,
+ PULONG fileIndexHigh,
+ UCHAR SearchAttributes,
+ PNW_DIRECTORY_INFO NwDirInfo
+ );
+
+NTSTATUS
+NtSearchMaskToNw(
+ IN PUNICODE_STRING UcSearchMask,
+ IN OUT POEM_STRING OemSearchMask,
+ IN PICB Icb,
+ IN BOOLEAN ShortNameSearch
+ );
+
+#if 0
+VOID
+NwCancelFindNotify (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+#endif
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwFsdDirectoryControl )
+#pragma alloc_text( PAGE, NwQueryDirectory )
+#pragma alloc_text( PAGE, GetNextFile )
+#pragma alloc_text( PAGE, NtSearchMaskToNw )
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, NwCommonDirectoryControl )
+#endif
+
+#endif
+
+
+#if 0 // Not pageable
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+
+NTSTATUS
+NwFsdDirectoryControl (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+/*++
+
+Routine Description:
+
+ This routine is the FSD routine that handles directory control
+ functions (i.e., query and notify).
+
+Arguments:
+
+ NwfsDeviceObject - Supplies the device object for the directory function.
+
+ Irp - Supplies the IRP to process.
+
+Return Value:
+
+ NTSTATUS - The result status.
+
+--*/
+
+{
+ PIRP_CONTEXT pIrpContext = NULL;
+ NTSTATUS status;
+ BOOLEAN TopLevel;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwFsdDirectoryControl\n", 0);
+
+ //
+ // Call the common directory control routine.
+ //
+
+ FsRtlEnterFileSystem();
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ try {
+
+ pIrpContext = AllocateIrpContext( Irp );
+ status = NwCommonDirectoryControl( pIrpContext );
+
+ } except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
+
+ if ( pIrpContext == NULL ) {
+
+ //
+ // If we couldn't allocate an irp context, just complete
+ // irp without any fanfare.
+ //
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
+
+ } else {
+
+ //
+ // We had some trouble trying to perform the requested
+ // operation, so we'll abort the I/O request with
+ // the error status that we get back from the
+ // execption code.
+ //
+
+ status = NwProcessException( pIrpContext, GetExceptionCode() );
+ }
+
+ }
+
+ if ( pIrpContext ) {
+ NwCompleteRequest( pIrpContext, status );
+ }
+
+ if ( TopLevel ) {
+ NwSetTopLevelIrp( NULL );
+ }
+ FsRtlExitFileSystem();
+
+ //
+ // Return to the caller.
+ //
+
+ DebugTrace(-1, Dbg, "NwFsdDirectoryControl -> %08lx\n", status );
+
+ return status;
+}
+
+
+NTSTATUS
+NwCommonDirectoryControl (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine does the common code for directory control functions.
+
+Arguments:
+
+ IrpContext - Supplies the request being processed.
+
+Return Value:
+
+ NTSTATUS - The return status for the operation
+
+--*/
+
+{
+ NTSTATUS status;
+
+ PIRP Irp;
+ PIO_STACK_LOCATION irpSp;
+
+ NODE_TYPE_CODE nodeTypeCode;
+ PICB icb;
+ PDCB dcb;
+ PVOID fsContext;
+
+ //
+ // Get the current stack location
+ //
+
+ Irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ DebugTrace(+1, Dbg, "CommonDirectoryControl...\n", 0);
+ DebugTrace( 0, Dbg, "Irp = %08lx\n", (ULONG)Irp);
+
+ //
+ // Decode the file object to figure out who we are. If the result
+ // is not an ICB then its an illegal parameter.
+ //
+
+ if ((nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
+ &fsContext,
+ (PVOID *)&icb )) != NW_NTC_ICB) {
+
+ DebugTrace(0, Dbg, "Not a directory\n", 0);
+
+ status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "CommonDirectoryControl -> %08lx\n", status );
+ return status;
+ }
+
+ dcb = (PDCB)icb->SuperType.Fcb;
+ nodeTypeCode = dcb->NodeTypeCode;
+
+ if ( nodeTypeCode != NW_NTC_DCB ) {
+
+ DebugTrace(0, Dbg, "Not a directory\n", 0);
+
+ status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "CommonDirectoryControl -> %08lx\n", status );
+ return status;
+ }
+
+ IrpContext->pScb = icb->SuperType.Fcb->Scb;
+ IrpContext->pNpScb = IrpContext->pScb->pNpScb;
+ IrpContext->Icb = icb;
+
+ //
+ // Acquire exclusive access to the DCB. Get to front of queue
+ // first to avoid deadlock potential.
+ //
+
+ NwAppendToQueueAndWait( IrpContext );
+ NwAcquireExclusiveFcb( dcb->NonPagedFcb, TRUE );
+
+ try {
+
+ NwVerifyIcb( icb );
+
+ //
+ // We know this is a directory control so we'll case on the
+ // minor function, and call the appropriate work routines.
+ //
+
+ switch (irpSp->MinorFunction) {
+
+ case IRP_MN_QUERY_DIRECTORY:
+
+ status = NwQueryDirectory( IrpContext, icb );
+ break;
+
+ case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
+
+#if 0
+ if ( !icb->FailedFindNotify ) {
+ icb->FailedFindNotify = TRUE;
+#endif
+ status = STATUS_NOT_SUPPORTED;
+#if 0
+ } else {
+
+ //
+ // HACKHACK
+ // Cover for moronic process that keeps trying to use
+ // find notify even though we don't support it.
+ //
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ IoAcquireCancelSpinLock( &Irp->CancelIrql );
+
+ if ( Irp->Cancel ) {
+ status = STATUS_CANCELLED;
+ } else {
+ InsertTailList( &FnList, &IrpContext->NextRequest );
+ IoMarkIrpPending( Irp );
+ IoSetCancelRoutine( Irp, NwCancelFindNotify );
+ status = STATUS_PENDING;
+ }
+
+ IoReleaseCancelSpinLock( Irp->CancelIrql );
+ NwReleaseRcb( &NwRcb );
+
+ }
+#endif
+
+ break;
+
+ default:
+
+ //
+ // For all other minor function codes we say they're invalid
+ // and complete the request.
+ //
+
+ DebugTrace(0, Dbg, "Invalid FS Control Minor Function Code %08lx\n", irpSp->MinorFunction);
+
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ break;
+ }
+
+ } finally {
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+
+ NwReleaseFcb( dcb->NonPagedFcb );
+ DebugTrace(-1, Dbg, "CommonDirectoryControl -> %08lx\n", status);
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+NwQueryDirectory (
+ IN PIRP_CONTEXT pIrpContext,
+ IN PICB Icb
+ )
+
+/*++
+
+Routine Description:
+
+ This is the work routine for querying a directory.
+
+Arugments:
+
+ IrpContext - Supplies the Irp context information.
+
+ Icb - Pointer the ICB for the request.
+
+Return Value:
+
+ NTSTATUS - The return status for the operation.
+
+--*/
+
+{
+ NTSTATUS status = STATUS_SUCCESS;
+ PIRP Irp;
+ PIO_STACK_LOCATION irpSp;
+ PUCHAR buffer;
+ CLONG systemBufferLength;
+ UNICODE_STRING searchMask;
+ ULONG fileIndexLow;
+ ULONG fileIndexHigh;
+ FILE_INFORMATION_CLASS fileInformationClass;
+ BOOLEAN restartScan;
+ BOOLEAN returnSingleEntry;
+ BOOLEAN indexSpecified;
+ PVCB vcb;
+
+ BOOLEAN ansiStringAllocated = FALSE;
+ UCHAR SearchAttributes;
+ BOOLEAN searchRetry;
+
+ static WCHAR star[] = L"*";
+
+ BOOLEAN caseInsensitive = TRUE; //*** Make searches case insensitive
+
+ ULONG lastEntry;
+ ULONG nextEntry;
+ ULONG totalBufferLength;
+
+ PFILE_BOTH_DIR_INFORMATION dirInfo;
+ PFILE_NAMES_INFORMATION namesInfo;
+
+ PAGED_CODE();
+
+ //
+ // Get the current stack location.
+ //
+
+ Irp = pIrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+ vcb = Icb->SuperType.Fcb->Vcb;
+
+ DebugTrace(+1, Dbg, "NwQueryDirectory\n", 0 );
+ DebugTrace( 0, Dbg, "Icb = %08lx\n", (ULONG)Icb);
+ DebugTrace( 0, Dbg, "SystemBuffer = %08lx\n", (ULONG)Irp->AssociatedIrp.SystemBuffer);
+ DebugTrace( 0, Dbg, "Length = %08lx\n", irpSp->Parameters.QueryDirectory.Length);
+ DebugTrace( 0, Dbg, "Search Mask = %08lx\n", (ULONG)irpSp->Parameters.QueryDirectory.FileName);
+ DebugTrace( 0, Dbg, "FileIndex = %08lx\n", irpSp->Parameters.QueryDirectory.FileIndex);
+ DebugTrace( 0, Dbg, "FileInformationClass = %08lx\n", irpSp->Parameters.QueryDirectory.FileInformationClass);
+ DebugTrace( 0, Dbg, "RestartScan = %08lx\n", BooleanFlagOn(irpSp->Flags, SL_RESTART_SCAN));
+ DebugTrace( 0, Dbg, "ReturnSingleEntry = %08lx\n", BooleanFlagOn(irpSp->Flags, SL_RETURN_SINGLE_ENTRY));
+ DebugTrace( 0, Dbg, "IndexSpecified = %08lx\n", BooleanFlagOn(irpSp->Flags, SL_INDEX_SPECIFIED));
+
+ //
+ // Make local copies of the input parameters.
+ //
+
+ systemBufferLength = irpSp->Parameters.QueryDirectory.Length;
+
+ if (pIrpContext->pScb->UserUid.QuadPart != DefaultLuid.QuadPart) {
+ fileIndexLow = 0;
+ } else {
+ //
+ // Tell the gateway we do support resume from index so long
+ // as the index returned is the same as the last file we
+ // returned. Otherwise the SMB server does a brute force rewind
+ // on each find next.
+ //
+
+ fileIndexLow = irpSp->Parameters.QueryDirectory.FileIndex;
+ }
+ fileIndexHigh = 0;
+
+ fileInformationClass =
+ irpSp->Parameters.QueryDirectory.FileInformationClass;
+
+ restartScan = BooleanFlagOn(irpSp->Flags, SL_RESTART_SCAN);
+ indexSpecified = BooleanFlagOn(irpSp->Flags, SL_INDEX_SPECIFIED);
+ returnSingleEntry = BooleanFlagOn(irpSp->Flags, SL_RETURN_SINGLE_ENTRY);
+
+ if (irpSp->Parameters.QueryDirectory.FileName != NULL) {
+ searchMask = *(PUNICODE_STRING)irpSp->Parameters.QueryDirectory.FileName;
+ } else {
+ searchMask.Length = 0;
+ searchMask.Buffer = NULL;
+ }
+
+ buffer = Irp->UserBuffer;
+ DebugTrace(0, Dbg, "Users Buffer -> %08lx\n", buffer);
+
+ //
+ // It is ok to attempt a reconnect if this request fails with a
+ // connection error.
+ //
+
+ SetFlag( pIrpContext->Flags, IRP_FLAG_RECONNECTABLE );
+
+ //
+ // Check if the ICB already has a query template attached. If it
+ // does not already have one then we either use the string we are
+ // given or we attach our own containing "*"
+ //
+
+ if ( Icb->NwQueryTemplate.Buffer == NULL ) {
+
+ //
+ // This is our first time calling query directory so we need
+ // to either set the query template to the user specified string
+ // or to "*.*".
+ //
+
+ if ( searchMask.Buffer == NULL ) {
+
+ DebugTrace(0, Dbg, "Set template to *", 0);
+
+ searchMask.Length = sizeof( star ) - sizeof(WCHAR);
+ searchMask.Buffer = star;
+
+ }
+
+ DebugTrace(0, Dbg, "Set query template -> %wZ\n", (ULONG)&searchMask);
+
+ //
+ // Map the NT search names to NCP. Note that this must be
+ // done after the Unicode to OEM translation.
+ //
+
+ searchRetry = FALSE;
+
+ do {
+
+ status = NtSearchMaskToNw(
+ &searchMask,
+ &Icb->NwQueryTemplate,
+ Icb,
+ searchRetry );
+
+ if ( !NT_SUCCESS( status ) ) {
+ DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", status);
+ return( status );
+ }
+
+ Icb->UQueryTemplate.Buffer = ALLOCATE_POOL( PagedPool, searchMask.Length );
+ if (Icb->UQueryTemplate.Buffer == NULL ) {
+ DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", STATUS_INSUFFICIENT_RESOURCES );
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ Icb->UQueryTemplate.MaximumLength = searchMask.Length;
+ RtlCopyUnicodeString( &Icb->UQueryTemplate, &searchMask );
+
+ //
+ // Now send a Search Initialize NCP.
+ //
+ // Do a short search if the server doesn't support long names,
+ // or this is a short-name non-wild card search
+ //
+
+ if ( !Icb->ShortNameSearch ) {
+
+ status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "Lbb-DbC",
+ NCP_LFN_SEARCH_INITIATE,
+ vcb->Specific.Disk.LongNameSpace,
+ vcb->Specific.Disk.VolumeNumber,
+ vcb->Specific.Disk.Handle,
+ LFN_FLAG_SHORT_DIRECTORY,
+ &Icb->SuperType.Fcb->RelativeFileName );
+
+ if ( NT_SUCCESS( status ) ) {
+
+ status = ParseResponse(
+ pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "Nbee",
+ &Icb->SearchVolume,
+ &Icb->SearchIndexHigh,
+ &Icb->SearchIndexLow );
+ }
+
+ } else {
+
+ status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "FbJ",
+ NCP_SEARCH_INITIATE,
+ vcb->Specific.Disk.Handle,
+ &Icb->SuperType.Fcb->RelativeFileName );
+
+ if ( NT_SUCCESS( status ) ) {
+
+ status = ParseResponse(
+ pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "Nbww-",
+ &Icb->SearchVolume,
+ &Icb->SearchHandle,
+ &Icb->SearchIndexLow );
+ }
+
+ }
+
+ //
+ // If we couldn't find the search path, and we did a long
+ // name search initiate, try again with a short name.
+ //
+
+ if ( status == STATUS_OBJECT_PATH_NOT_FOUND &&
+ !Icb->ShortNameSearch ) {
+
+ searchRetry = TRUE;
+
+ if ( Icb->UQueryTemplate.Buffer != NULL ) {
+ FREE_POOL( Icb->UQueryTemplate.Buffer );
+ }
+
+ RtlFreeOemString ( &Icb->NwQueryTemplate );
+
+ } else {
+ searchRetry = FALSE;
+ }
+
+
+ } while ( searchRetry );
+
+ if ( !NT_SUCCESS( status ) ) {
+ if (status == STATUS_UNSUCCESSFUL) {
+ DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", STATUS_NO_SUCH_FILE);
+ return( STATUS_NO_SUCH_FILE );
+ }
+ DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", status);
+ return( status );
+ }
+
+ //
+ // Since we are doing a search we will need to send an End Of Job
+ // for this PID.
+ //
+
+ NwSetEndOfJobRequired( Icb->Pid );
+
+ fileIndexLow = Icb->SearchIndexLow;
+ fileIndexHigh = Icb->SearchIndexHigh;
+
+ //
+ // We can't ask for both files and directories, so first ask for
+ // files, then ask for directories.
+ //
+
+ SearchAttributes = NW_ATTRIBUTE_SYSTEM |
+ NW_ATTRIBUTE_HIDDEN |
+ NW_ATTRIBUTE_READ_ONLY;
+
+ //
+ // If there are no wildcards in the search mask, then setup to
+ // not generate the . and .. entries.
+ //
+
+ if ( !FsRtlDoesNameContainWildCards( &Icb->UQueryTemplate ) ) {
+ Icb->DotReturned = TRUE;
+ Icb->DotDotReturned = TRUE;
+ } else {
+ Icb->DotReturned = FALSE;
+ Icb->DotDotReturned = FALSE;
+ }
+
+
+ } else {
+
+ //
+ // Check if we were given an index to start with or if we need to
+ // restart the scan or if we should use the index that was saved in
+ // the ICB.
+ //
+
+ if (restartScan) {
+ fileIndexLow = (ULONG)-1;
+ fileIndexHigh = Icb->SearchIndexHigh;
+
+ //
+ // Send a Search Initialize NCP. The server often times out search
+ // handles and if this one has been sitting at the end of the
+ // directory then its likely we would get no files at all!
+ //
+ // Do a short search if the server doesn't support long names,
+ // or this is a short-name non-wild card search
+ //
+
+ if ( !Icb->ShortNameSearch ) {
+
+ status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "Lbb-DbC",
+ NCP_LFN_SEARCH_INITIATE,
+ vcb->Specific.Disk.LongNameSpace,
+ vcb->Specific.Disk.VolumeNumber,
+ vcb->Specific.Disk.Handle,
+ LFN_FLAG_SHORT_DIRECTORY,
+ &Icb->SuperType.Fcb->RelativeFileName );
+
+ if ( NT_SUCCESS( status ) ) {
+
+ status = ParseResponse(
+ pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "Nbee",
+ &Icb->SearchVolume,
+ &Icb->SearchIndexHigh,
+ &Icb->SearchIndexLow );
+ }
+
+ } else {
+
+ status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "FbJ",
+ NCP_SEARCH_INITIATE,
+ vcb->Specific.Disk.Handle,
+ &Icb->SuperType.Fcb->RelativeFileName );
+
+ if ( NT_SUCCESS( status ) ) {
+
+ status = ParseResponse(
+ pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "Nbww-",
+ &Icb->SearchVolume,
+ &Icb->SearchHandle,
+ &Icb->SearchIndexLow );
+ }
+
+ }
+
+ Icb->ReturnedSomething = FALSE;
+
+ //
+ // We can't ask for both files and directories, so first ask for
+ // files, then ask for directories.
+ //
+
+ SearchAttributes = NW_ATTRIBUTE_SYSTEM |
+ NW_ATTRIBUTE_HIDDEN |
+ NW_ATTRIBUTE_READ_ONLY;
+ Icb->SearchAttributes = SearchAttributes;
+
+ Icb->DotReturned = FALSE;
+ Icb->DotDotReturned = FALSE;
+
+ } else if ((!indexSpecified) ||
+ ((fileIndexLow == Icb->SearchIndexLow) &&
+ (pIrpContext->pScb->UserUid.QuadPart == DefaultLuid.QuadPart))) {
+ //
+ // Continue from the last filename. If an index is specified then its
+ // only allowed for the gateway (and other system services).
+ //
+
+ fileIndexLow = Icb->SearchIndexLow;
+ fileIndexHigh = Icb->SearchIndexHigh;
+ SearchAttributes = Icb->SearchAttributes;
+
+ if ( SearchAttributes == 0xFF ) {
+
+ //
+ // This is a completed search.
+ //
+
+ DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", STATUS_NO_MORE_FILES);
+ return( STATUS_NO_MORE_FILES );
+ }
+
+ } else {
+
+ //
+ // SVR to avoid rescanning from end of dir all
+ // the way through the directory again.
+ //
+
+ if ((Icb->SearchIndexLow == -1) &&
+ (Icb->LastSearchIndexLow == fileIndexLow) &&
+ (pIrpContext->pScb->UserUid.QuadPart == DefaultLuid.QuadPart) &&
+ (Icb->SearchAttributes == 0xFF )) {
+
+ DebugTrace(-1, Dbg, "NwQueryDirectory SVR exit-> %08lx\n", STATUS_NO_MORE_FILES);
+ return( STATUS_NO_MORE_FILES );
+ }
+ // SVR end
+
+ //
+ // Someone's trying to do a resume from key. The netware
+ // server doesn't support this, so neither do we.
+ //
+
+ DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", STATUS_NOT_IMPLEMENTED);
+ return( STATUS_NOT_IMPLEMENTED );
+ }
+
+ }
+
+ //
+ // Now we are committed to completing the Irp, we do that in
+ // the finally clause of the following try.
+ //
+
+ try {
+
+ ULONG baseLength;
+ ULONG lengthAdded;
+ NW_DIRECTORY_INFO nwDirInfo;
+ ULONG FileNameLength;
+
+ lastEntry = 0;
+ nextEntry = 0;
+
+ switch (fileInformationClass) {
+
+ case FileDirectoryInformation:
+
+ baseLength = FIELD_OFFSET( FILE_DIRECTORY_INFORMATION, FileName[0] );
+ break;
+
+ case FileFullDirectoryInformation:
+
+ baseLength = FIELD_OFFSET( FILE_FULL_DIR_INFORMATION, FileName[0] );
+ break;
+
+ case FileNamesInformation:
+
+ baseLength = FIELD_OFFSET( FILE_NAMES_INFORMATION, FileName[0] );
+ break;
+
+ case FileBothDirectoryInformation:
+
+ baseLength = FIELD_OFFSET( FILE_BOTH_DIR_INFORMATION, FileName[0] );
+ break;
+
+ default:
+
+ try_return( status = STATUS_INVALID_INFO_CLASS );
+ }
+
+ //
+ // It is not ok to attempt a reconnect if this request fails with a
+ // connection error, since our search handle would be invalid.
+ //
+
+ ClearFlag( pIrpContext->Flags, IRP_FLAG_RECONNECTABLE );
+
+ while ( TRUE ) {
+
+ ULONG bytesToCopy;
+ ULONG bytesRemainingInBuffer;
+
+ DebugTrace(0, Dbg, "Top of Loop\n", 0);
+ DebugTrace(0, Dbg, "CurrentIndex = %08lx\n", fileIndexLow);
+ DebugTrace(0, Dbg, "LastEntry = %08lx\n", lastEntry);
+ DebugTrace(0, Dbg, "NextEntry = %08lx\n", nextEntry);
+
+ nwDirInfo.FileName.Buffer = nwDirInfo.FileNameBuffer;
+ nwDirInfo.FileName.MaximumLength = NW_MAX_FILENAME_SIZE;
+
+ status = GetNextFile(
+ pIrpContext,
+ Icb,
+ &fileIndexLow,
+ &fileIndexHigh,
+ SearchAttributes,
+ &nwDirInfo );
+
+ if ( NT_SUCCESS( status ) ) {
+
+ DebugTrace(0, Dbg, "DirFileName = %wZ\n", &nwDirInfo.FileName);
+ DebugTrace(0, Dbg, "FileIndexLow = %08lx\n", fileIndexLow);
+
+ FileNameLength = nwDirInfo.FileName.Length;
+ bytesRemainingInBuffer = systemBufferLength - nextEntry;
+
+ ASSERT( bytesRemainingInBuffer >= baseLength );
+
+ //
+ // See how much of the name we will be able to copy into
+ // the system buffer. This also dictates our return
+ // value.
+ //
+
+ if ( baseLength + FileNameLength <= bytesRemainingInBuffer ) {
+
+ bytesToCopy = FileNameLength;
+ status = STATUS_SUCCESS;
+
+ } else {
+
+ bytesToCopy = bytesRemainingInBuffer - baseLength;
+ status = STATUS_BUFFER_OVERFLOW;
+ }
+
+ //
+ // Note how much of buffer we are consuming and zero
+ // the base part of the structure.
+ //
+
+ lengthAdded = baseLength + bytesToCopy;
+ RtlZeroMemory( &buffer[nextEntry], baseLength );
+
+ switch (fileInformationClass) {
+
+ case FileBothDirectoryInformation:
+
+ //
+ // Fill in the short name, if this is a LFN volume.
+ //
+
+ DebugTrace(0, Dbg, "Getting directory both information\n", 0);
+
+#if 0
+ if ( nwDirInfo.DosDirectoryEntry != 0xFFFF &&
+ !IsFatNameValid( &nwDirInfo.FileName ) ) {
+
+ UNICODE_STRING ShortName;
+
+ status = ExchangeWithWait (
+ pIrpContext,
+ SynchronousResponseCallback,
+ "SbDb",
+ NCP_DIR_FUNCTION, NCP_GET_SHORT_NAME,
+ Icb->SearchVolume,
+ nwDirInfo.DosDirectoryEntry,
+ 0 );
+
+ if ( NT_SUCCESS( status ) ) {
+
+ dirInfo = (PFILE_BOTH_DIR_INFORMATION)&buffer[nextEntry];
+
+ //
+ // Short name is in form 8.3 plus nul terminator.
+ //
+
+ ShortName.MaximumLength = 13 * sizeof(WCHAR) ;
+ ShortName.Buffer = dirInfo->ShortName;
+
+ status = ParseResponse(
+ pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "N_P",
+ 15,
+ &ShortName );
+
+ if ( NT_SUCCESS( status ) ) {
+ dirInfo->ShortNameLength = (CCHAR)ShortName.Length;
+ }
+ }
+ }
+#endif
+
+ case FileFullDirectoryInformation:
+
+ //
+ // We don't use EaLength, so fill in nothing here.
+ //
+
+ DebugTrace(0, Dbg, "Getting directory full information\n", 0);
+
+ case FileDirectoryInformation:
+
+ DebugTrace(0, Dbg, "Getting directory information\n", 0);
+
+ //
+ // The eof indicates the number of instances and
+ // allocation size is the maximum allowed
+ //
+
+ dirInfo = (PFILE_BOTH_DIR_INFORMATION)&buffer[nextEntry];
+
+ dirInfo->FileAttributes = nwDirInfo.Attributes;
+ dirInfo->FileNameLength = bytesToCopy;
+ dirInfo->EndOfFile.LowPart = nwDirInfo.FileSize;
+ dirInfo->EndOfFile.HighPart = 0;
+ dirInfo->AllocationSize = dirInfo->EndOfFile;
+ dirInfo->CreationTime = NwDateTimeToNtTime( nwDirInfo.CreationDate, nwDirInfo.CreationTime );
+ dirInfo->LastAccessTime = NwDateTimeToNtTime( nwDirInfo.LastAccessDate, 0 );
+ dirInfo->LastWriteTime = NwDateTimeToNtTime( nwDirInfo.LastUpdateDate, nwDirInfo.LastUpdateTime );
+ dirInfo->ChangeTime = dirInfo->LastWriteTime;
+ if (pIrpContext->pScb->UserUid.QuadPart != DefaultLuid.QuadPart) {
+ dirInfo->FileIndex = 0;
+ } else {
+ dirInfo->FileIndex = fileIndexLow;
+ }
+ break;
+
+ case FileNamesInformation:
+
+ DebugTrace(0, Dbg, "Getting names information\n", 0);
+
+
+ namesInfo = (PFILE_NAMES_INFORMATION)&buffer[nextEntry];
+
+ namesInfo->FileNameLength = FileNameLength;
+ if (pIrpContext->pScb->UserUid.QuadPart != DefaultLuid.QuadPart) {
+ namesInfo->FileIndex = 0;
+ } else {
+ namesInfo->FileIndex = fileIndexLow;
+ }
+
+ break;
+
+ default:
+
+ KeBugCheck( RDR_FILE_SYSTEM );
+ }
+
+
+ RtlMoveMemory( &buffer[nextEntry + baseLength],
+ nwDirInfo.FileName.Buffer,
+ bytesToCopy );
+
+ dump( Dbg, &buffer[nextEntry], lengthAdded);
+ //
+ // Setup the previous next entry offset.
+ //
+
+ *((PULONG)(&buffer[lastEntry])) = nextEntry - lastEntry;
+ totalBufferLength = nextEntry + lengthAdded;
+
+ //
+ // Set ourselves up for the next iteration
+ //
+
+ lastEntry = nextEntry;
+ nextEntry += (ULONG)QuadAlign( lengthAdded );
+
+ //
+ // Check if the last entry didn't completely fit
+ //
+
+ if ( status == STATUS_BUFFER_OVERFLOW ) {
+
+ try_return( NOTHING );
+ }
+
+ //
+ // Check if we are only to return a single entry
+ //
+
+ if (returnSingleEntry) {
+ try_return( status = STATUS_SUCCESS );
+ }
+
+ } else {
+
+ //
+ // The search response contained an error. If we have
+ // not yet enumerated directories, do them now. Otherwise,
+ // we are done searching for files.
+ //
+
+ if ( status == STATUS_UNSUCCESSFUL &&
+ !FlagOn(SearchAttributes, NW_ATTRIBUTE_DIRECTORY) ) {
+
+ SetFlag( SearchAttributes, NW_ATTRIBUTE_DIRECTORY );
+ fileIndexLow = (ULONG)-1;
+ continue;
+
+ } else {
+
+ //
+ // Remember that this is a completed search and
+ // quit the loop.
+ //
+
+ SearchAttributes = 0xFF;
+ break;
+ }
+ }
+
+ //
+ // Here are the rules concerning filling up the buffer:
+ //
+ // 1. The Io system garentees that there will always be
+ // enough room for at least one base record.
+ //
+ // 2. If the full first record (including file name) cannot
+ // fit, as much of the name as possible is copied and
+ // STATUS_BUFFER_OVERFLOW is returned.
+ //
+ // 3. If a subsequent record cannot completely fit into the
+ // buffer, none of it (as in 0 bytes) is copied, and
+ // STATUS_SUCCESS is returned. A subsequent query will
+ // pick up with this record.
+ //
+ // Since we cannot rewind a search, we'll guess that the
+ // next entry is a full length name. If it mightn't fix,
+ // just bail and re the files we've got.
+ //
+
+ bytesRemainingInBuffer = systemBufferLength - nextEntry;
+
+ if ( baseLength + NW_MAX_FILENAME_SIZE > bytesRemainingInBuffer ) {
+
+ DebugTrace(0, Dbg, "Next entry won't fit\n", 0);
+ try_return( status = STATUS_SUCCESS );
+ }
+
+ } // while ( TRUE )
+
+ try_exit: NOTHING;
+ } finally {
+
+ //
+ // At this point we're finished searching for files.
+ // If the NextEntry is zero then we haven't found anything so we
+ // will return no more files or no such file.
+ //
+
+ if ( status == STATUS_NO_MORE_FILES ||
+ status == STATUS_UNSUCCESSFUL ||
+ status == STATUS_SUCCESS ) {
+ if (nextEntry == 0) {
+ if (Icb->ReturnedSomething) {
+ status = STATUS_NO_MORE_FILES;
+ } else {
+ status = STATUS_NO_SUCH_FILE;
+ }
+ } else {
+ Icb->ReturnedSomething = TRUE;
+ status = STATUS_SUCCESS;
+ }
+
+ }
+
+ //
+ // Indicate how much of the system buffer we have used up.
+ //
+
+ Irp->IoStatus.Information = totalBufferLength;
+
+ //
+ // Remember the last file index, so that we can resume this
+ // search.
+ //
+
+
+ // SVR to avoid rescanning from end of dir all
+
+ if (fileIndexLow != -1) {
+ Icb->LastSearchIndexLow = fileIndexLow;
+ }
+ // SVR end
+
+ Icb->SearchIndexLow = fileIndexLow;
+ Icb->SearchIndexHigh = fileIndexHigh;
+
+ Icb->SearchAttributes = SearchAttributes;
+
+ DebugTrace(-1, Dbg, "NwQueryDirectory -> %08lx\n", status);
+ }
+
+ return status;
+}
+
+NTSTATUS
+GetNextFile(
+ PIRP_CONTEXT pIrpContext,
+ PICB Icb,
+ PULONG FileIndexLow,
+ PULONG FileIndexHigh,
+ UCHAR SearchAttributes,
+ PNW_DIRECTORY_INFO DirInfo
+ )
+/*++
+
+Routine Description:
+
+ Get the next file in the directory being searched.
+
+Arguments:
+
+ pIrpContext - Supplies the request being processed.
+
+ Icb - A pointer to the ICB for the directory to query.
+
+ FileIndexLow, FileIndexHigh - On entry, the the index of the
+ previous directory entry. On exit, the index to the directory
+ entry returned.
+
+ SearchAttributes - Search attributes to use.
+
+ DirInfo - Returns information for the directory entry found.
+
+Return Value:
+
+ NTSTATUS - The result status.
+
+--*/
+{
+ NTSTATUS status;
+ PVCB vcb;
+
+ static UNICODE_STRING DotFile = { 2, 2, L"." };
+ static UNICODE_STRING DotDotFile = { 4, 4, L".." };
+
+ PAGED_CODE();
+
+ DirInfo->DosDirectoryEntry = 0xFFFF;
+
+ if ( !Icb->DotReturned ) {
+
+ Icb->DotReturned = TRUE;
+
+ //
+ // Return '.' only if it we are not searching in the root directory
+ // and it matches the search pattern.
+ //
+
+ if ( Icb->SuperType.Fcb->RelativeFileName.Length != 0 &&
+ FsRtlIsNameInExpression( &Icb->UQueryTemplate, &DotFile, TRUE, NULL ) ) {
+
+ RtlCopyUnicodeString( &DirInfo->FileName, &DotFile );
+ DirInfo->Attributes = FILE_ATTRIBUTE_DIRECTORY;
+ DirInfo->FileSize = 0;
+ DirInfo->CreationDate = DEFAULT_DATE;
+ DirInfo->LastAccessDate = DEFAULT_DATE;
+ DirInfo->LastUpdateDate = DEFAULT_DATE;
+ DirInfo->LastUpdateTime = DEFAULT_TIME;
+ DirInfo->CreationTime = DEFAULT_TIME;
+
+ return( STATUS_SUCCESS );
+ }
+ }
+
+ if ( !Icb->DotDotReturned ) {
+
+ Icb->DotDotReturned = TRUE;
+
+ //
+ // Return '..' only if it we are not searching in the root directory
+ // and it matches the search pattern.
+ //
+
+ if ( Icb->SuperType.Fcb->RelativeFileName.Length != 0 &&
+ FsRtlIsNameInExpression( &Icb->UQueryTemplate, &DotDotFile, TRUE, NULL ) ) {
+
+ RtlCopyUnicodeString( &DirInfo->FileName, &DotDotFile );
+ DirInfo->Attributes = FILE_ATTRIBUTE_DIRECTORY;
+ DirInfo->FileSize = 0;
+ DirInfo->CreationDate = DEFAULT_DATE;
+ DirInfo->LastAccessDate = DEFAULT_DATE;
+ DirInfo->LastUpdateDate = DEFAULT_DATE;
+ DirInfo->LastUpdateTime = DEFAULT_TIME;
+ DirInfo->CreationTime = DEFAULT_TIME;
+
+ return( STATUS_SUCCESS );
+ }
+ }
+
+ vcb = Icb->SuperType.Fcb->Vcb;
+ if ( Icb->ShortNameSearch ) {
+
+ status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "Fbwwbp",
+ NCP_SEARCH_CONTINUE,
+ Icb->SearchVolume,
+ Icb->SearchHandle,
+ *(PUSHORT)FileIndexLow,
+ SearchAttributes,
+ Icb->NwQueryTemplate.Buffer
+ );
+
+ if ( !NT_SUCCESS( status )) {
+ return status;
+ }
+
+ *FileIndexLow = 0;
+ *FileIndexHigh = 0;
+
+ if ( FlagOn(SearchAttributes, NW_ATTRIBUTE_DIRECTORY) ) {
+
+ status = ParseResponse(
+ pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "Nw=Rb-ww",
+ FileIndexLow,
+ &DirInfo->FileName, 14,
+ &DirInfo->Attributes,
+ &DirInfo->CreationDate,
+ &DirInfo->CreationTime
+ );
+
+#if 0
+ if ( DirInfo->CreationDate == 0 && DirInfo->CreationTime == 0 ) {
+ DirInfo->CreationDate = DEFAULT_DATE;
+ DirInfo->CreationTime = DEFAULT_TIME;
+ }
+#endif
+
+ DirInfo->FileSize = 0;
+ DirInfo->LastAccessDate = DirInfo->CreationDate;
+ DirInfo->LastUpdateDate = DirInfo->CreationDate;
+ DirInfo->LastUpdateTime = DirInfo->CreationTime;
+
+ } else {
+
+ status = ParseResponse(
+ pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "Nw=Rb-dwwww",
+ FileIndexLow,
+ &DirInfo->FileName, 14,
+ &DirInfo->Attributes,
+ &DirInfo->FileSize,
+ &DirInfo->CreationDate,
+ &DirInfo->LastAccessDate,
+ &DirInfo->LastUpdateDate,
+ &DirInfo->LastUpdateTime
+ );
+
+ DirInfo->CreationTime = DEFAULT_TIME;
+ }
+
+ } else {
+
+ status = ExchangeWithWait (
+ pIrpContext,
+ SynchronousResponseCallback,
+ "LbbWDbDDp",
+ NCP_LFN_SEARCH_CONTINUE,
+ vcb->Specific.Disk.LongNameSpace,
+ 0, // Data stream
+ SearchAttributes & SEARCH_ALL_DIRECTORIES,
+ LFN_FLAG_INFO_ATTRIBUTES |
+ LFN_FLAG_INFO_FILE_SIZE |
+ LFN_FLAG_INFO_MODIFY_TIME |
+ LFN_FLAG_INFO_CREATION_TIME |
+ LFN_FLAG_INFO_DIR_INFO |
+ LFN_FLAG_INFO_NAME,
+ vcb->Specific.Disk.VolumeNumber,
+ *FileIndexHigh,
+ *FileIndexLow,
+ Icb->NwQueryTemplate.Buffer );
+
+ if ( NT_SUCCESS( status ) ) {
+ status = ParseResponse(
+ pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "N-ee_e_e_xx_xx_x_e_P",
+ FileIndexHigh,
+ FileIndexLow,
+ 5,
+ &DirInfo->Attributes,
+ 2,
+ &DirInfo->FileSize,
+ 6,
+ &DirInfo->CreationTime,
+ &DirInfo->CreationDate,
+ 4,
+ &DirInfo->LastUpdateTime,
+ &DirInfo->LastUpdateDate,
+ 4,
+ &DirInfo->LastAccessDate,
+ 14,
+ &DirInfo->DosDirectoryEntry,
+ 20,
+ &DirInfo->FileName );
+ }
+
+ if ( FlagOn(SearchAttributes, NW_ATTRIBUTE_DIRECTORY) ) {
+ DirInfo->FileSize = 0;
+ }
+
+ }
+
+ if ( DirInfo->Attributes == 0 ) {
+ DirInfo->Attributes = FILE_ATTRIBUTE_NORMAL;
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+NtSearchMaskToNw(
+ IN PUNICODE_STRING UcSearchMask,
+ IN OUT POEM_STRING OemSearchMask,
+ IN PICB Icb,
+ IN BOOLEAN ShortNameSearch
+ )
+/*++
+
+Routine Description:
+
+ This routine maps a netware path name to the correct netware format.
+
+Arguments:
+
+ UcSearchMask - The search mask in NT format.
+
+ OemSearchMask - The search mask in Netware format.
+
+ Icb - The ICB of the directory in which we are searching.
+
+ ShortNameSearch - If TRUE, always do a short name search.
+
+Return Value:
+
+ NTSTATUS - The result status.
+
+--*/
+
+{
+ USHORT i;
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ //
+ // Use a short name search if the volume does not support long names.
+ // or this is a short name ICB, and we are doing a short name, non
+ // wild-card search.
+ //
+
+ if ( Icb->SuperType.Fcb->Vcb->Specific.Disk.LongNameSpace == LFN_NO_OS2_NAME_SPACE ||
+
+ ShortNameSearch ||
+
+ ( !BooleanFlagOn( Icb->SuperType.Fcb->Flags, FCB_FLAGS_LONG_NAME ) &&
+ !FsRtlDoesNameContainWildCards( UcSearchMask ) &&
+ IsFatNameValid( UcSearchMask ) ) ) {
+
+ Icb->ShortNameSearch = TRUE;
+
+ //
+ // Allocate space for and initialize the query templates.
+ //
+
+ status = RtlUpcaseUnicodeStringToOemString(
+ OemSearchMask,
+ UcSearchMask,
+ TRUE );
+
+ if ( !NT_SUCCESS( status ) ) {
+ return( status );
+ }
+
+ //
+ // Special case. Map '*.*' to '*'.
+ //
+
+ if ( OemSearchMask->Length == 3 &&
+ RtlCompareMemory( OemSearchMask->Buffer, "*.*", 3 ) == 3 ) {
+
+ OemSearchMask->Length = 1;
+ OemSearchMask->Buffer[1] = '\0';
+
+ } else {
+
+
+ for ( i = 0; i < OemSearchMask->Length ; i++ ) {
+
+ if( FsRtlIsLeadDbcsCharacter( OemSearchMask->Buffer[i] ) ) {
+
+ i++; // Skip to the trailing byte.
+
+ if (( Japan ) &&
+ ((UCHAR)(OemSearchMask->Buffer[i]) == 0x5C )) {
+
+ //
+ // The trailbyte is 0x5C, replace it with 0x13
+ //
+
+
+ OemSearchMask->Buffer[i] = (UCHAR)( 0x13 );
+ continue;
+
+ }
+
+ } else {
+
+ // Single byte character that may need modification.
+
+ switch ( (UCHAR)(OemSearchMask->Buffer[i]) ) {
+
+ case ANSI_DOS_STAR:
+ OemSearchMask->Buffer[i] = (UCHAR)( 0x80 | '*' );
+ break;
+
+ case ANSI_DOS_QM:
+ OemSearchMask->Buffer[i] = (UCHAR)( 0x80 | '?' );
+ break;
+
+ case ANSI_DOS_DOT:
+ OemSearchMask->Buffer[i] = (UCHAR)( 0x80 | '.' );
+ break;
+
+ //
+ // Netware Japanese version The following character is
+ // replaced with another one if the string is for File
+ // Name only when sendding from Client to Server.
+ //
+ // SO U+0xFF7F SJIS+0xBF -> 0x10
+ // SMALL_YO U+0xFF6E SJIS+0xAE -> 0x11
+ // SMALL_E U+0xFF64 SJIS+0xAA -> 0x12
+ //
+ // The reason is unknown, Should ask Novell Japan.
+ //
+ // See Also exchange.c
+
+ case 0xBF: // ANSI_DOS_KATAKANA_SO:
+ if (Japan) {
+ OemSearchMask->Buffer[i] = (UCHAR)( 0x10 );
+ }
+ break;
+
+ case 0xAE: // ANSI_DOS_KATAKANA_SMALL_YO:
+ if (Japan) {
+ OemSearchMask->Buffer[i] = (UCHAR)( 0x11 );
+ }
+ break;
+
+ case 0xAA: // ANSI_DOS_KATAKANA_SMALL_E:
+ if (Japan) {
+ OemSearchMask->Buffer[i] = (UCHAR)( 0x12 );
+ }
+ break;
+
+ }
+ }
+ }
+ }
+
+ } else {
+
+ USHORT size;
+ PCHAR buffer;
+ UNICODE_STRING src;
+ OEM_STRING dest;
+
+ Icb->ShortNameSearch = FALSE;
+
+ //
+ // Allocate space for and initialize the query templates.
+ //
+
+#ifndef QFE_BUILD
+ buffer = ExAllocatePoolWithTag( PagedPool,
+ UcSearchMask->Length,
+ 'scwn' );
+#else
+ buffer = ExAllocatePool( PagedPool,
+ UcSearchMask->Length );
+#endif
+ if ( buffer == NULL ) {
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ OemSearchMask->Buffer = buffer;
+
+ //
+ // Special case. Map '????????.???' to '*'.
+ //
+
+ if ( UcSearchMask->Length == 24 &&
+ RtlCompareMemory( UcSearchMask->Buffer, L">>>>>>>>\">>>", 24 ) == 24 ) {
+
+ OemSearchMask->Length = 3;
+ OemSearchMask->Buffer[0] = (UCHAR)0xFF;
+ OemSearchMask->Buffer[1] = '*';
+ OemSearchMask->Buffer[2] = '\0';
+
+ return STATUS_SUCCESS;
+ }
+
+ //
+ // Now convert the string, character by character
+ //
+
+ src.Buffer = UcSearchMask->Buffer;
+ src.Length = 2;
+ dest.Buffer = buffer;
+ dest.MaximumLength = UcSearchMask->Length;
+
+ size = UcSearchMask->Length / 2;
+
+ for ( i = 0; i < size ; i++ ) {
+ switch ( *src.Buffer ) {
+
+ case L'*':
+ case L'?':
+ *dest.Buffer++ = LFN_META_CHARACTER;
+ *dest.Buffer++ = (UCHAR)*src.Buffer++;
+ break;
+
+ case L'.':
+ *dest.Buffer++ = (UCHAR)*src.Buffer++;
+ break;
+
+ case DOS_DOT:
+ *dest.Buffer++ = LFN_META_CHARACTER;
+ *dest.Buffer++ = (UCHAR)( 0x80 | '.' );
+ *src.Buffer++;
+ break;
+
+ case DOS_STAR:
+ *dest.Buffer++ = LFN_META_CHARACTER;
+ *dest.Buffer++ = (UCHAR)( 0x80 | '*' );
+ *src.Buffer++;
+ break;
+
+ case DOS_QM:
+ *dest.Buffer++ = LFN_META_CHARACTER;
+ *dest.Buffer++ = (UCHAR)( 0x80 | '?' );
+ *src.Buffer++;
+ break;
+
+ default:
+ RtlUnicodeStringToCountedOemString( &dest, &src, FALSE );
+ dest.Buffer++;
+ src.Buffer++;
+ }
+ }
+
+ *dest.Buffer = '\0';
+ OemSearchMask->Length = (USHORT)( dest.Buffer - buffer );
+ }
+
+ return STATUS_SUCCESS;
+}
+
+#if 0
+VOID
+NwCancelFindNotify (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine implements the cancel function for an find notify IRP.
+
+Arguments:
+
+ DeviceObject - ignored
+
+ Irp - Supplies the Irp being cancelled.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PLIST_ENTRY listEntry;
+
+ UNREFERENCED_PARAMETER( DeviceObject );
+
+ //
+ // We now need to void the cancel routine and release the io cancel
+ // spin-lock.
+ //
+
+ IoSetCancelRoutine( Irp, NULL );
+ IoReleaseCancelSpinLock( Irp->CancelIrql );
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ for ( listEntry = FnList.Flink; listEntry != &FnList ; listEntry = listEntry->Flink ) {
+
+ PIRP_CONTEXT IrpContext;
+
+ IrpContext = CONTAINING_RECORD( listEntry, IRP_CONTEXT, NextRequest );
+
+ if ( IrpContext->pOriginalIrp == Irp ) {
+ RemoveEntryList( &IrpContext->NextRequest );
+ NwCompleteRequest( IrpContext, STATUS_CANCELLED );
+ break;
+ }
+ }
+
+ NwReleaseRcb( &NwRcb );
+}
+#endif
+
+
diff --git a/private/nw/rdr/encrypt.c b/private/nw/rdr/encrypt.c
new file mode 100644
index 000000000..ee7758621
--- /dev/null
+++ b/private/nw/rdr/encrypt.c
@@ -0,0 +1,322 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ encrypt.c
+
+Abstract:
+
+ This module implements the routines for the NetWare
+ redirector to mangle an objectid, challenge key and
+ password such that a NetWare server will accept the
+ password as valid.
+
+ This program uses information published in Byte Magazine.
+
+Author:
+
+ Colin Watson [ColinW] 15-Mar-1993
+
+Revision History:
+
+--*/
+
+#include <procs.h>
+
+
+UCHAR Table[] = {
+ 0x78, 0x08, 0x64, 0xe4, 0x5c, 0x17, 0xbf, 0xa8,
+ 0xf8, 0xcc, 0x94, 0x1e, 0x46, 0x24, 0x0a, 0xb9,
+ 0x2f, 0xb1, 0xd2, 0x19, 0x5e, 0x70, 0x02, 0x66,
+ 0x07, 0x38, 0x29, 0x3f, 0x7f, 0xcf, 0x64, 0xa0,
+ 0x23, 0xab, 0xd8, 0x3a, 0x17, 0xcf, 0x18, 0x9d,
+ 0x91, 0x94, 0xe4, 0xc5, 0x5c, 0x8b, 0x23, 0x9e,
+ 0x77, 0x69, 0xef, 0xc8, 0xd1, 0xa6, 0xed, 0x07,
+ 0x7a, 0x01, 0xf5, 0x4b, 0x7b, 0xec, 0x95, 0xd1,
+ 0xbd, 0x13, 0x5d, 0xe6, 0x30, 0xbb, 0xf3, 0x64,
+ 0x9d, 0xa3, 0x14, 0x94, 0x83, 0xbe, 0x50, 0x52,
+ 0xcb, 0xd5, 0xd5, 0xd2, 0xd9, 0xac, 0xa0, 0xb3,
+ 0x53, 0x69, 0x51, 0xee, 0x0e, 0x82, 0xd2, 0x20,
+ 0x4f, 0x85, 0x96, 0x86, 0xba, 0xbf, 0x07, 0x28,
+ 0xc7, 0x3a, 0x14, 0x25, 0xf7, 0xac, 0xe5, 0x93,
+ 0xe7, 0x12, 0xe1, 0xf4, 0xa6, 0xc6, 0xf4, 0x30,
+ 0xc0, 0x36, 0xf8, 0x7b, 0x2d, 0xc6, 0xaa, 0x8d } ;
+
+
+UCHAR Keys[32] =
+{0x48,0x93,0x46,0x67,0x98,0x3D,0xE6,0x8D,
+ 0xB7,0x10,0x7A,0x26,0x5A,0xB9,0xB1,0x35,
+ 0x6B,0x0F,0xD5,0x70,0xAE,0xFB,0xAD,0x11,
+ 0xF4,0x47,0xDC,0xA7,0xEC,0xCF,0x50,0xC0};
+
+#define XorArray( DEST, SRC ) { \
+ PULONG D = (PULONG)DEST; \
+ PULONG S = (PULONG)SRC; \
+ int i; \
+ for ( i = 0; i <= 7 ; i++ ) { \
+ D[i] ^= S[i]; \
+ } \
+}
+
+VOID
+Shuffle(
+ UCHAR *achObjectId,
+ UCHAR *szUpperPassword,
+ int iPasswordLen,
+ UCHAR *achOutputBuffer
+ );
+
+int
+Scramble(
+ int iSeed,
+ UCHAR achBuffer[32]
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, RespondToChallenge )
+#pragma alloc_text( PAGE, Shuffle )
+#pragma alloc_text( PAGE, Scramble )
+#endif
+
+
+VOID
+RespondToChallenge(
+ IN PUCHAR achObjectId,
+ IN POEM_STRING Password,
+ IN PUCHAR pChallenge,
+ OUT PUCHAR pResponse
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes the ObjectId and Challenge key from the server and
+ encrypts the user supplied password to develop a credential for the
+ server to verify.
+
+Arguments:
+ IN PUCHAR achObjectId - Supplies the 4 byte user's bindery object id
+ IN POEM_STRING Password - Supplies the user's uppercased password
+ IN PUCHAR pChallenge - Supplies the 8 byte challenge key
+ OUT PUCHAR pResponse - Returns the 8 byte response
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ int index;
+ UCHAR achK[32];
+ UCHAR achBuf[32];
+
+ PAGED_CODE();
+
+ Shuffle(achObjectId, Password->Buffer, Password->Length, achBuf);
+ Shuffle( &pChallenge[0], achBuf, 16, &achK[0] );
+ Shuffle( &pChallenge[4], achBuf, 16, &achK[16] );
+
+ for (index = 0; index < 16; index++)
+ achK[index] ^= achK[31-index];
+
+ for (index = 0; index < 8; index++)
+ pResponse[index] = achK[index] ^ achK[15-index];
+}
+
+
+VOID
+Shuffle(
+ UCHAR *achObjectId,
+ UCHAR *szUpperPassword,
+ int iPasswordLen,
+ UCHAR *achOutputBuffer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine shuffles around the object ID with the password
+
+Arguments:
+
+ IN achObjectId - Supplies the 4 byte user's bindery object id
+
+ IN szUpperPassword - Supplies the user's uppercased password on the
+ first call to process the password. On the second and third calls
+ this parameter contains the OutputBuffer from the first call
+
+ IN iPasswordLen - length of uppercased password
+
+ OUT achOutputBuffer - Returns the 8 byte sub-calculation
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ int iTempIndex;
+ int iOutputIndex;
+ UCHAR achTemp[32];
+
+ PAGED_CODE();
+
+ //
+ // Truncate all trailing zeros from the password.
+ //
+
+ while (iPasswordLen > 0 && szUpperPassword[iPasswordLen-1] == 0 ) {
+ iPasswordLen--;
+ }
+
+ //
+ // Initialize the achTemp buffer. Initialization consists of taking
+ // the password and dividing it up into chunks of 32. Any bytes left
+ // over are the remainder and do not go into the initialization.
+ //
+ // achTemp[0] = szUpperPassword[0] ^ szUpperPassword[32] ^ szUpper...
+ // achTemp[1] = szUpperPassword[1] ^ szUpperPassword[33] ^ szUpper...
+ // etc.
+ //
+
+ if ( iPasswordLen > 32) {
+
+ // At least one chunk of 32. Set the buffer to the first chunk.
+
+ RtlCopyMemory( achTemp, szUpperPassword, 32 );
+
+ szUpperPassword +=32; // Remove the first chunk
+ iPasswordLen -=32;
+
+ while ( iPasswordLen >= 32 ) {
+ //
+ // Xor this chunk with the characters already loaded into
+ // achTemp.
+ //
+
+ XorArray( achTemp, szUpperPassword);
+
+ szUpperPassword +=32; // Remove this chunk
+ iPasswordLen -=32;
+ }
+
+ } else {
+
+ // No chunks of 32 so set the buffer to zero's
+
+ RtlZeroMemory( achTemp, sizeof(achTemp));
+
+ }
+
+ //
+ // achTemp is now initialized. Load the remainder into achTemp.
+ // The remainder is repeated to fill achTemp.
+ //
+ // The corresponding character from Keys is taken to seperate
+ // each repitition.
+ //
+ // As an example, take the remainder "ABCDEFG". The remainder is expanded
+ // to "ABCDEFGwABCDEFGxABCDEFGyABCDEFGz" where w is Keys[7],
+ // x is Keys[15], y is Keys[23] and z is Keys[31].
+ //
+ //
+
+ if (iPasswordLen > 0) {
+ int iPasswordOffset = 0;
+ for (iTempIndex = 0; iTempIndex < 32; iTempIndex++) {
+
+ if (iPasswordLen == iPasswordOffset) {
+ iPasswordOffset = 0;
+ achTemp[iTempIndex] ^= Keys[iTempIndex];
+ } else {
+ achTemp[iTempIndex] ^= szUpperPassword[iPasswordOffset++];
+ }
+ }
+ }
+
+ //
+ // achTemp has been loaded with the users password packed into 32
+ // bytes. Now take the objectid that came from the server and use
+ // that to munge every byte in achTemp.
+ //
+
+ for (iTempIndex = 0; iTempIndex < 32; iTempIndex++)
+ achTemp[iTempIndex] ^= achObjectId[ iTempIndex & 3];
+
+ Scramble( Scramble( 0, achTemp ), achTemp );
+
+ //
+ // Finally take pairs of bytes in achTemp and return the two
+ // nibbles obtained from Table. The pairs of bytes used
+ // are achTemp[n] and achTemp[n+16].
+ //
+
+ for (iOutputIndex = 0; iOutputIndex < 16; iOutputIndex++) {
+
+ unsigned int offset = achTemp[iOutputIndex << 1],
+ shift = (offset & 0x1) ? 0 : 4 ;
+
+ achOutputBuffer[iOutputIndex] =
+ (Table[offset >> 1] >> shift) & 0xF ;
+
+ offset = achTemp[(iOutputIndex << 1)+1],
+ shift = (offset & 0x1) ? 4 : 0 ;
+
+ achOutputBuffer[iOutputIndex] |=
+ (Table[offset >> 1] << shift) & 0xF0;
+ }
+
+ return;
+}
+
+int
+Scramble(
+ int iSeed,
+ UCHAR achBuffer[32]
+ )
+
+/*++
+
+Routine Description:
+
+ This routine scrambles around the contents of the buffer. Each buffer
+ position is updated to include the contents of at least two character
+ positions plus an EncryptKey value. The buffer is processed left to right
+ and so if a character position chooses to merge with a buffer position
+ to its left then this buffer position will include bits derived from at
+ least 3 bytes of the original buffer contents.
+
+Arguments:
+
+ IN iSeed
+ IN OUT achBuffer[32]
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ int iBufferIndex;
+
+ PAGED_CODE();
+
+ for (iBufferIndex = 0; iBufferIndex < 32; iBufferIndex++) {
+ achBuffer[iBufferIndex] =
+ (UCHAR)(
+ ((UCHAR)(achBuffer[iBufferIndex] + iSeed)) ^
+ ((UCHAR)( achBuffer[(iBufferIndex+iSeed) & 31] -
+ Keys[iBufferIndex] )));
+
+ iSeed += achBuffer[iBufferIndex];
+ }
+ return iSeed;
+}
+
diff --git a/private/nw/rdr/errorlog.c b/private/nw/rdr/errorlog.c
new file mode 100644
index 000000000..599d25521
--- /dev/null
+++ b/private/nw/rdr/errorlog.c
@@ -0,0 +1,201 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ errorlog.c
+
+Abstract:
+
+ This module implements the error logging in the netware redirector.
+
+Author:
+
+ Manny Weiser (mannyw) 11-Feb-92
+
+Revision History:
+
+--*/
+
+#include <procs.h>
+#include <align.h>
+
+#include <stdarg.h>
+
+ULONG
+SequenceNumber = 0;
+
+#ifdef ALLOC_PRAGMA
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, Error )
+#endif
+#endif
+
+#if 0 // Not pageable
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+ VOID
+_cdecl
+Error(
+ IN ULONG UniqueErrorCode,
+ IN NTSTATUS NtStatusCode,
+ IN PVOID ExtraInformationBuffer,
+ IN USHORT ExtraInformationLength,
+ IN USHORT NumberOfInsertionStrings,
+ ...
+ )
+
+#define LAST_NAMED_ARGUMENT NumberOfInsertionStrings
+
+/*++
+
+Routine Description:
+
+ This function allocates an I/O error log record, fills it in and writes it
+ to the I/O error log.
+
+Arguments:
+
+ UniqueErrorCode - The event code
+
+ NtStatusCode - The NT status of the failure
+
+ ExtraInformationBuffer - Raw data for the event
+
+ ExtraInformationLength - The length of the raw data
+
+ NumberOfInsertionString - The number of insertion strings that follow
+
+ InsertionString - 0 or more insertion strings.
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ PIO_ERROR_LOG_PACKET ErrorLogEntry;
+ int TotalErrorLogEntryLength;
+ ULONG SizeOfStringData = 0;
+ va_list ParmPtr; // Pointer to stack parms.
+
+ if (NumberOfInsertionStrings != 0) {
+ USHORT i;
+
+ va_start(ParmPtr, LAST_NAMED_ARGUMENT);
+
+ for (i = 0; i < NumberOfInsertionStrings; i += 1) {
+ PWSTR String = va_arg(ParmPtr, PWSTR);
+ SizeOfStringData += (wcslen(String) + 1) * sizeof(WCHAR);
+ }
+ }
+
+ //
+ // Ideally we want the packet to hold the servername and ExtraInformation.
+ // Usually the ExtraInformation gets truncated.
+ //
+
+ TotalErrorLogEntryLength =
+ min( ExtraInformationLength + sizeof(IO_ERROR_LOG_MESSAGE) + 1 + SizeOfStringData,
+ ERROR_LOG_MAXIMUM_SIZE );
+
+ ErrorLogEntry = (PIO_ERROR_LOG_PACKET)IoAllocateErrorLogEntry(
+ FileSystemDeviceObject,
+ (UCHAR)TotalErrorLogEntryLength
+ );
+
+ if (ErrorLogEntry != NULL) {
+ PCHAR DumpData;
+ ULONG RemainingSpace = TotalErrorLogEntryLength - sizeof( IO_ERROR_LOG_MESSAGE );
+ USHORT i;
+ ULONG SizeOfRawData;
+
+ if (RemainingSpace > SizeOfStringData) {
+ SizeOfRawData = RemainingSpace - SizeOfStringData;
+ } else {
+ SizeOfStringData = RemainingSpace;
+
+ SizeOfRawData = 0;
+ }
+
+ //
+ // Fill in the error log entry
+ //
+
+ ErrorLogEntry->ErrorCode = UniqueErrorCode;
+ ErrorLogEntry->MajorFunctionCode = 0;
+ ErrorLogEntry->RetryCount = 0;
+ ErrorLogEntry->UniqueErrorValue = 0;
+ ErrorLogEntry->FinalStatus = NtStatusCode;
+ ErrorLogEntry->IoControlCode = 0;
+ ErrorLogEntry->DeviceOffset.LowPart = 0;
+ ErrorLogEntry->DeviceOffset.HighPart = 0;
+ ErrorLogEntry->SequenceNumber = (ULONG)SequenceNumber ++;
+ ErrorLogEntry->StringOffset =
+ (USHORT)ROUND_UP_COUNT(
+ FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData) + SizeOfRawData,
+ ALIGN_WORD);
+
+ DumpData = (PCHAR)ErrorLogEntry->DumpData;
+
+ //
+ // Append the extra information. This information is typically
+ // an SMB header.
+ //
+
+ if (( ARGUMENT_PRESENT( ExtraInformationBuffer )) &&
+ ( SizeOfRawData != 0 )) {
+ ULONG Length;
+
+ Length = min(ExtraInformationLength, (USHORT)SizeOfRawData);
+ RtlCopyMemory(
+ DumpData,
+ ExtraInformationBuffer,
+ Length);
+ ErrorLogEntry->DumpDataSize = (USHORT)Length;
+ } else {
+ ErrorLogEntry->DumpDataSize = 0;
+ }
+
+ ErrorLogEntry->NumberOfStrings = 0;
+
+ if (NumberOfInsertionStrings != 0) {
+ PWSTR StringOffset = (PWSTR)((PCHAR)ErrorLogEntry + ErrorLogEntry->StringOffset);
+ PWSTR InsertionString;
+
+ //
+ // Set up ParmPtr to point to first of the caller's parameters.
+ //
+
+ va_start(ParmPtr, LAST_NAMED_ARGUMENT);
+
+ for (i = 0 ; i < NumberOfInsertionStrings ; i+= 1) {
+ InsertionString = va_arg(ParmPtr, PWSTR);
+
+ if (((wcslen(InsertionString) + 1) * sizeof(WCHAR)) <= SizeOfStringData ) {
+
+ wcscpy(StringOffset, InsertionString);
+
+ StringOffset += wcslen(InsertionString) + 1;
+
+ SizeOfStringData -= (wcslen(InsertionString) + 1) * sizeof(WCHAR);
+
+ ErrorLogEntry->NumberOfStrings += 1;
+
+ }
+
+ }
+
+ }
+
+ IoWriteErrorLogEntry(ErrorLogEntry);
+ }
+
+}
+
+
diff --git a/private/nw/rdr/except.c b/private/nw/rdr/except.c
new file mode 100644
index 000000000..0c82cc863
--- /dev/null
+++ b/private/nw/rdr/except.c
@@ -0,0 +1,160 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ Except.c
+
+Abstract:
+
+ This module implements the exception handling for the NetWare
+ redirector called by the dispatch driver.
+
+Author:
+
+ Colin Watson [ColinW] 19-Dec-1992
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_CATCH_EXCEPTIONS)
+
+#if 0 // Not pageable
+NwExceptionFilter
+NwProcessException
+#endif
+
+LONG
+NwExceptionFilter (
+ IN PIRP Irp,
+ IN PEXCEPTION_POINTERS ExceptionPointer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is used to decide if we should or should not handle
+ an exception status that is being raised. It inserts the status
+ into the IrpContext and either indicates that we should handle
+ the exception or bug check the system.
+
+Arguments:
+
+ ExceptionCode - Supplies the exception code to being checked.
+
+Return Value:
+
+ ULONG - returns EXCEPTION_EXECUTE_HANDLER or bugchecks
+
+--*/
+
+{
+ NTSTATUS ExceptionCode;
+#ifdef NWDBG
+ PVOID ExceptionAddress;
+ ExceptionAddress = ExceptionPointer->ExceptionRecord->ExceptionAddress;
+#endif
+
+ ExceptionCode = ExceptionPointer->ExceptionRecord->ExceptionCode;
+ DebugTrace(0, DEBUG_TRACE_UNWIND, "NwExceptionFilter %X\n", ExceptionCode);
+#ifdef NWDBG
+ DebugTrace(0, DEBUG_TRACE_UNWIND, " %X\n", ExceptionAddress);
+#endif
+
+ //
+ // If the exception is STATUS_IN_PAGE_ERROR, get the I/O error code
+ // from the exception record.
+ //
+
+ if (ExceptionCode == STATUS_IN_PAGE_ERROR) {
+ if (ExceptionPointer->ExceptionRecord->NumberParameters >= 3) {
+ ExceptionCode = ExceptionPointer->ExceptionRecord->ExceptionInformation[2];
+ }
+ }
+
+ if (FsRtlIsNtstatusExpected( ExceptionCode )) {
+
+ DebugTrace(0, DEBUG_TRACE_UNWIND, "Exception expected\n", 0);
+ return EXCEPTION_EXECUTE_HANDLER;
+
+ } else {
+
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+}
+
+NTSTATUS
+NwProcessException (
+ IN PIRP_CONTEXT IrpContext,
+ IN NTSTATUS ExceptionCode
+ )
+
+/*++
+
+Routine Description:
+
+ This routine process an exception. It either completes the request
+ with the saved exception status or it sends it off to IoRaiseHardError()
+
+Arguments:
+
+ IrpContext - Supplies the Irp being processed
+
+ ExceptionCode - Supplies the normalized exception status being handled
+
+Return Value:
+
+ NTSTATUS - Returns the results of either posting the Irp or the
+ saved completion status.
+
+--*/
+
+{
+ NTSTATUS Status;
+ PIRP Irp;
+
+ DebugTrace(0, Dbg, "NwProcessException\n", 0);
+
+ Irp = IrpContext->pOriginalIrp;
+ Irp->IoStatus.Status = ExceptionCode;
+
+ //
+ // If the error is a hard error, or verify required, then we will complete
+ // it if this is a recursive Irp, or with a top-level Irp, either send
+ // it to the Fsp for verification, or send it to IoRaiseHardError, who
+ // will deal with it.
+ //
+
+ if (ExceptionCode == STATUS_CANT_WAIT) {
+
+ Status = NwPostToFsp( IrpContext, TRUE );
+
+ } else {
+
+ //
+ // We got an error, so zero out the information field before
+ // completing the request if this was an input operation.
+ // Otherwise IopCompleteRequest will try to copy to the user's buffer.
+ //
+
+ if ( FlagOn(Irp->Flags, IRP_INPUT_OPERATION) ) {
+
+ Irp->IoStatus.Information = 0;
+ }
+
+ Status = ExceptionCode;
+
+ }
+
+ return Status;
+}
+
diff --git a/private/nw/rdr/exchange.c b/private/nw/rdr/exchange.c
new file mode 100644
index 000000000..92535c02c
--- /dev/null
+++ b/private/nw/rdr/exchange.c
@@ -0,0 +1,4887 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ exchange.c
+
+Abstract:
+
+ This module implements the File Create routine for the NetWare
+ redirector called by the dispatch driver.
+
+Author:
+
+ Hans Hurvig [hanshu] Aug-1992 Created
+ Colin Watson [ColinW] 19-Dec-1992
+
+Revision History:
+
+--*/
+
+#include "procs.h"
+#include "tdikrnl.h"
+#include <STDARG.H>
+
+#define Dbg (DEBUG_TRACE_EXCHANGE)
+
+//
+// Exchange.c Global constants
+//
+
+// broadcast to socket 0x0452
+
+TA_IPX_ADDRESS SapBroadcastAddress =
+ {
+ 1,
+ sizeof(TA_IPX_ADDRESS), TDI_ADDRESS_TYPE_IPX,
+ 0, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, SAP_SOCKET
+ };
+
+UCHAR SapPacketType = PACKET_TYPE_SAP;
+UCHAR NcpPacketType = PACKET_TYPE_NCP;
+
+extern BOOLEAN WorkerRunning; // From timer.c
+
+#ifdef NWDBG
+ULONG DropCount = 0;
+int AlwaysAllocateIrp = 1;
+#endif
+
+NTSTATUS
+CompletionSend(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ );
+
+NTSTATUS
+FspGetMessage(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+CompletionWatchDogSend(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ );
+
+USHORT
+NextSocket(
+ IN USHORT OldValue
+ );
+
+NTSTATUS
+FormatRequest(
+ PIRP_CONTEXT pIrpC,
+ PEX pEx,
+ char* f,
+ va_list a // format specific parameters
+ );
+
+VOID
+ScheduleReconnectRetry(
+ PIRP_CONTEXT pIrpContext
+ );
+
+VOID
+ReconnectRetry(
+ PIRP_CONTEXT pIrpContext
+ );
+
+NTSTATUS
+CopyIndicatedData(
+ PIRP_CONTEXT pIrpContext,
+ PCHAR RspData,
+ ULONG BytesIndicated,
+ PULONG BytesTaken,
+ ULONG ReceiveDatagramFlags
+ );
+
+NTSTATUS
+AllocateReceiveIrp(
+ PIRP_CONTEXT pIrpContext,
+ PVOID ReceiveData,
+ ULONG BytesAvailable,
+ PULONG BytesAccepted,
+ PNW_TDI_STRUCT pTdiStruct
+ );
+
+NTSTATUS
+ReceiveIrpCompletion(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp,
+ PVOID Context
+ );
+
+NTSTATUS
+FspProcessServerDown(
+ PIRP_CONTEXT IrpContext
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NextSocket )
+#pragma alloc_text( PAGE, ExchangeWithWait )
+#pragma alloc_text( PAGE, NewRouteRetry )
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, FspGetMessage )
+#pragma alloc_text( PAGE1, Exchange )
+#pragma alloc_text( PAGE1, BuildRequestPacket )
+#pragma alloc_text( PAGE1, ParseResponse )
+#pragma alloc_text( PAGE1, ParseNcpResponse )
+#pragma alloc_text( PAGE1, FormatRequest )
+#pragma alloc_text( PAGE1, PrepareAndSendPacket )
+#pragma alloc_text( PAGE1, PreparePacket )
+#pragma alloc_text( PAGE1, SendPacket )
+#pragma alloc_text( PAGE1, AppendToScbQueue )
+#pragma alloc_text( PAGE1, KickQueue )
+#pragma alloc_text( PAGE1, SendNow )
+#pragma alloc_text( PAGE1, SetEvent )
+#pragma alloc_text( PAGE1, CompletionSend )
+#pragma alloc_text( PAGE1, CopyIndicatedData )
+#pragma alloc_text( PAGE1, AllocateReceiveIrp )
+#pragma alloc_text( PAGE1, ReceiveIrpCompletion )
+#pragma alloc_text( PAGE1, VerifyResponse )
+#pragma alloc_text( PAGE1, ScheduleReconnectRetry )
+#pragma alloc_text( PAGE1, ReconnectRetry )
+#pragma alloc_text( PAGE1, NewRouteBurstRetry )
+#endif
+
+#endif
+
+#if 0 // Not pageable
+ServerDatagramHandler
+WatchDogDatagramHandler
+SendDatagramHandler
+CompletionWatchDogSend
+MdlLength
+FreeReceiveIrp
+FspProcessServerDown
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+NTSTATUS
+_cdecl
+Exchange(
+ PIRP_CONTEXT pIrpContext,
+ PEX pEx,
+ char* f,
+ ... // format specific parameters
+ )
+/*++
+
+Routine Description:
+
+ This routine is a wrapper for _Exchange. See the comment
+ in _Exchange for routine and argument description.
+
+--*/
+
+{
+ va_list Arguments;
+ NTSTATUS Status;
+
+ va_start( Arguments, f );
+
+ Status = FormatRequest( pIrpContext, pEx, f, Arguments );
+ if ( !NT_SUCCESS( Status ) ) {
+ return( Status );
+ }
+
+ //
+ // We won't be completing this IRP now, so mark it pending.
+ //
+
+ IoMarkIrpPending( pIrpContext->pOriginalIrp );
+
+ //
+ // Start the packet on it's way to the wire.
+ //
+
+ Status = PrepareAndSendPacket( pIrpContext );
+
+ return( Status );
+}
+
+NTSTATUS
+_cdecl
+BuildRequestPacket(
+ PIRP_CONTEXT pIrpContext,
+ PEX pEx,
+ char* f,
+ ... // format specific parameters
+ )
+/*++
+
+Routine Description:
+
+ This routine is a wrapper for FormatRequest. See the comment
+ in FormatRequest for routine and argument description.
+
+--*/
+
+{
+ va_list Arguments;
+ NTSTATUS Status;
+
+ va_start( Arguments, f );
+
+ Status = FormatRequest( pIrpContext, pEx, f, Arguments );
+ if ( !NT_SUCCESS( Status ) ) {
+ return( Status );
+ }
+
+ return( Status );
+}
+
+
+NTSTATUS
+_cdecl
+ParseResponse(
+ PIRP_CONTEXT IrpContext,
+ PUCHAR Response,
+ ULONG ResponseLength,
+ char* FormatString,
+ ... // format specific parameters
+ )
+/*++
+
+Routine Description:
+
+ This routine parse an NCP response.
+
+Arguments:
+
+ pIrpC - Supplies the irp context for the exchange request. This may
+ be NULL for generic packet types.
+
+ f... - supplies the information needed to create the request to the
+ server. The first byte indicates the packet type and the
+ following bytes contain field types.
+
+ Packet types:
+
+ 'B' Burst primary response ( byte * )
+ 'N' NCP response ( void )
+ 'S' Burst secondary response ( byte * )
+ 'G' Generic packet ( )
+
+ Field types, request/response:
+
+ 'b' byte ( byte* )
+ 'w' hi-lo word ( word* )
+ 'x' ordered word ( word* )
+ 'd' hi-lo dword ( dword* )
+ 'e' ordered dword ( dword* )
+ '-' zero/skip byte ( void )
+ '=' zero/skip word ( void )
+ ._. zero/skip string ( word )
+ 'p' pstring ( char* )
+ 'p' pstring to Unicode ( UNICODE_STRING * )
+ 'c' cstring ( char* )
+ 'r' raw bytes ( byte*, word )
+ 'R' ASCIIZ to Unicode ( UNICODE_STRING *, word )
+
+ Added 3/29/95 by CoryWest:
+
+ 'W' lo-hi word ( word / word*)
+ 'D' lo-hi dword ( dword / dword*)
+ 'S' unicode string copy as NDS_STRING (UNICODE_STRING *)
+ 'T' terminal unicode string copy as NDS_STRING (UNICODE_STRING *)
+
+ 't' terminal unicode string with the nds null copied
+ as NDS_STRING (UNICODE_STRING *) (for GetUseName)
+
+ Not in use:
+
+ 's' cstring copy as NDS_STRING (char* / char *, word)
+ 'V' sized NDS value ( byte **, dword *)
+ 'l' what's this?
+
+Return Value:
+
+ STATUS - The converted error code from the NCP response.
+
+--*/
+
+{
+
+ PEPresponse *pResponseParameters;
+ PCHAR FormatByte;
+ va_list Arguments;
+ NTSTATUS Status = STATUS_SUCCESS;
+ NTSTATUS NcpStatus;
+ ULONG Length;
+
+ va_start( Arguments, FormatString );
+
+ //
+ // Make sure that we have an IrpContext unless we are doing
+ // a scan of a generic packet.
+ //
+
+#ifdef NWDBG
+ if ( *FormatString != 'G' ) {
+ ASSERT( IrpContext != NULL );
+ }
+#endif
+
+ switch ( *FormatString ) {
+
+ //
+ // NCP response.
+ //
+
+ case 'N':
+
+ Length = 8; // The data begins 8 bytes into the packet
+
+ pResponseParameters = (PEPresponse *)( ((PEPrequest *)Response) + 1);
+
+ //
+ // If there's a message pending for us on the server and we have
+ // popups disabled, we won't pick it up, but we should continue
+ // processing NCPs correctly!
+ //
+
+ if ( ( pResponseParameters->status == 0 ) ||
+ ( pResponseParameters->status == 0x40 ) ) {
+ Status = NwErrorToNtStatus( pResponseParameters->error );
+ } else {
+ Status = NwConnectionStatusToNtStatus( pResponseParameters->status );
+ if ( Status == STATUS_REMOTE_DISCONNECT ) {
+ Stats.ServerDisconnects++;
+ IrpContext->pNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
+ }
+ }
+
+ break;
+
+ //
+ // Burst response, first packet
+ //
+
+ case 'B': // BUGBUG Not needed, cleanup write.c first.
+ {
+ PNCP_BURST_HEADER BurstResponse = (PNCP_BURST_HEADER)Response;
+
+ byte* b = va_arg ( Arguments, byte* );
+ ULONG Result;
+ ULONG Offset = BurstResponse->BurstOffset;
+ *b = BurstResponse->Flags;
+
+ Length = 28; // The data begins 28 bytes into the packet
+
+ if ( Offset == 0 ) {
+
+ //
+ // This is the first packet in the burst response. Look
+ // at the result code.
+ //
+ // Note that the result DWORD is in lo-hi order.
+ //
+
+ Result = *(ULONG UNALIGNED *)(Response + 36);
+
+ switch ( Result ) {
+
+ case 0:
+ case 3: // No data
+ break;
+
+ case 1:
+ Status = STATUS_DISK_FULL;
+ break;
+
+ case 2: // I/O error
+ Status = STATUS_UNEXPECTED_IO_ERROR;
+ break;
+
+ default:
+ Status = NwErrorToNtStatus( (UCHAR)Result );
+ break;
+
+ }
+ }
+
+ break;
+ }
+
+#if 0
+ //
+ // Burst response, secondary packet
+ //
+
+ case 'S':
+ {
+ byte* b = va_arg ( Arguments, byte* );
+ *b = Response[2];
+
+ Length = 28; // The data begins 28 bytes into the packet
+ break;
+ }
+#endif
+
+ case 'G':
+ Length = 0; // The data begins at the start of the packet
+ break;
+
+ default:
+ ASSERT( FALSE );
+ Status = STATUS_UNSUCCESSFUL;
+ break;
+ }
+
+ //
+ // If this packet contains an error, simply return the error.
+ //
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return( Status );
+ }
+
+ NcpStatus = Status;
+
+ FormatByte = FormatString + 1;
+ while ( *FormatByte ) {
+
+ switch ( *FormatByte ) {
+
+ case '-':
+ Length += 1;
+ break;
+
+ case '=':
+ Length += 2;
+ break;
+
+ case '_':
+ {
+ word l = va_arg ( Arguments, word );
+ Length += l;
+ break;
+ }
+
+ case 'b':
+ {
+ byte* b = va_arg ( Arguments, byte* );
+ *b = Response[Length++];
+ break;
+ }
+
+ case 'w':
+ {
+ byte* b = va_arg ( Arguments, byte* );
+ b[1] = Response[Length++];
+ b[0] = Response[Length++];
+ break;
+ }
+
+ case 'x':
+ {
+ word* w = va_arg ( Arguments, word* );
+ *w = *(word UNALIGNED *)&Response[Length];
+ Length += 2;
+ break;
+ }
+
+ case 'd':
+ {
+ byte* b = va_arg ( Arguments, byte* );
+ b[3] = Response[Length++];
+ b[2] = Response[Length++];
+ b[1] = Response[Length++];
+ b[0] = Response[Length++];
+ break;
+ }
+
+ case 'e':
+ {
+ dword UNALIGNED * d = va_arg ( Arguments, dword* );
+ *d = *(dword UNALIGNED *)&Response[Length];
+ Length += 4;
+ break;
+ }
+
+ case 'c':
+ {
+ char* c = va_arg ( Arguments, char* );
+ word l = strlen( &Response[Length] );
+ memcpy ( c, &Response[Length], l+1 );
+ Length += l+1;
+ break;
+ }
+
+ case 'p':
+ {
+ char* c = va_arg ( Arguments, char* );
+ byte l = Response[Length++];
+ memcpy ( c, &Response[Length], l );
+ c[l+1] = 0;
+ break;
+ }
+
+ case 'P':
+ {
+ PUNICODE_STRING pUString = va_arg ( Arguments, PUNICODE_STRING );
+ OEM_STRING OemString;
+
+ OemString.Length = Response[Length++];
+ OemString.Buffer = &Response[Length];
+
+ //
+ // Note the the Rtl function would set pUString->Buffer = NULL,
+ // if OemString.Length is 0.
+ //
+
+ if ( OemString.Length != 0 ) {
+
+ Status = RtlOemStringToCountedUnicodeString( pUString, &OemString, FALSE );
+
+ if (!NT_SUCCESS( Status )) {
+ pUString->Length = 0;
+ NcpStatus = Status;
+ }
+
+ } else {
+ pUString->Length = 0;
+ }
+
+
+ break;
+ }
+
+ case 'r':
+ {
+ byte* b = va_arg ( Arguments, byte* );
+ word l = va_arg ( Arguments, word );
+ TdiCopyLookaheadData( b, &Response[Length], l, 0);
+ Length += l;
+ break;
+ }
+
+ case 'R':
+ {
+ //
+ // Interpret the buffer as an ASCIIZ string. Convert
+ // it to unicode in the preallocated buffer.
+ //
+
+ PUNICODE_STRING pUString = va_arg ( Arguments, PUNICODE_STRING );
+ OEM_STRING OemString;
+ USHORT len = va_arg ( Arguments, USHORT );
+
+ OemString.Buffer = &Response[Length];
+ OemString.Length = strlen( OemString.Buffer );
+ OemString.MaximumLength = OemString.Length;
+
+ //
+ // Note the the Rtl function would set pUString->Buffer = NULL,
+ // if OemString.Length is 0.
+ //
+
+ if ( OemString.Length != 0) {
+ Status = RtlOemStringToCountedUnicodeString( pUString, &OemString, FALSE );
+
+ if (!NT_SUCCESS( Status )) {
+
+ ASSERT( Status == STATUS_BUFFER_OVERFLOW );
+ pUString->Length = 0;
+ NcpStatus = Status;
+ }
+
+ } else {
+ pUString->Length = 0;
+ }
+
+ Length += len;
+ break;
+ }
+
+ case 'W':
+ {
+
+ WORD *w = va_arg ( Arguments, WORD* );
+ *w = (* (WORD *)&Response[Length]);
+ Length += 2;
+ break;
+
+ }
+
+ case 'D':
+ {
+
+ DWORD *d = va_arg ( Arguments, DWORD* );
+ *d = (* (DWORD *)&Response[Length]);
+ Length += 4;
+ break;
+
+ }
+
+ case 'S':
+ {
+
+ PUNICODE_STRING pU = va_arg( Arguments, PUNICODE_STRING );
+ USHORT strl;
+
+ if (pU) {
+
+ strl = (USHORT)(* (DWORD *)&Response[Length]);
+
+ //
+ // Don't count the null terminator that is part of
+ // Novell's counted unicode string.
+ //
+
+ pU->Length = strl - sizeof( WCHAR );
+ Length += 4;
+ RtlCopyMemory( pU->Buffer, &Response[Length], pU->Length );
+ Length += ROUNDUP4(strl);
+
+ } else {
+
+ //
+ // Skip over the string since we don't want it.
+ //
+
+ Length += ROUNDUP4((* (DWORD *)&Response[Length] ));
+ Length += 4;
+ }
+
+
+ break;
+
+ }
+
+ case 's':
+ {
+
+ PUNICODE_STRING pU = va_arg( Arguments, PUNICODE_STRING );
+ USHORT strl;
+
+ if (pU) {
+
+ strl = (USHORT)(* (DWORD *)&Response[Length]);
+ pU->Length = strl;
+ Length += 4;
+ RtlCopyMemory( pU->Buffer, &Response[Length], pU->Length );
+ Length += ROUNDUP4(strl);
+
+ } else {
+
+ //
+ // Skip over the string since we don't want it.
+ //
+
+ Length += ROUNDUP4((* (DWORD *)&Response[Length] ));
+ Length += 4;
+ }
+
+
+ break;
+
+ }
+
+ case 'T':
+ {
+
+ PUNICODE_STRING pU = va_arg( Arguments, PUNICODE_STRING );
+ USHORT strl;
+
+ if (pU) {
+
+ strl = (USHORT)(* (DWORD *)&Response[Length] );
+ strl -= sizeof( WCHAR ); // Don't count the NULL from NDS.
+
+ if ( strl <= pU->MaximumLength ) {
+
+ pU->Length = strl;
+ Length += 4;
+ RtlCopyMemory( pU->Buffer, &Response[Length], pU->Length );
+
+ //
+ // No need to advance the pointers since this is
+ // specifically a termination case!
+ //
+
+ } else {
+
+ pU->Length = 0;
+ }
+
+ }
+
+ break;
+
+ }
+
+ case 't':
+ {
+
+ PUNICODE_STRING pU = va_arg( Arguments, PUNICODE_STRING );
+ USHORT strl;
+
+ if (pU) {
+
+ strl = (USHORT)(* (DWORD *)&Response[Length] );
+
+ if ( strl <= pU->MaximumLength ) {
+
+ pU->Length = strl;
+ Length += 4;
+ RtlCopyMemory( pU->Buffer, &Response[Length], pU->Length );
+
+ //
+ // No need to advance the pointers since this is
+ // specifically a termination case!
+ //
+
+ } else {
+
+ pU->Length = 0;
+
+ }
+
+ }
+
+ break;
+
+ }
+
+ /*
+ case 's':
+ {
+
+ char *c = va_arg( Arguments, char * );
+ WORD l = va_arg( Arguments, WORD );
+ ULONG len = (* (DWORD *)&Response[Length]);
+ Length += 4;
+
+ // BUGBUG: How to fix this?
+ // l = WideCharToMultiByte(CP_ACP,0,(WCHAR *)&Response[Length],Length/2,c,l,0,0);
+ // if (!l) {
+ // #ifdef NWDBG
+ // DbgPrint( "ParseResponse case s couldnt translate from WCHAR.\n" );
+ // #endif
+ // goto ErrorExit;
+ // }
+
+ len = ROUNDUP4(len);
+ Length += len;
+ break;
+
+ }
+ case 'V':
+ {
+
+ BYTE **b = va_arg( Arguments, BYTE **);
+ DWORD *pLen = va_arg ( Arguments, DWORD *);
+ DWORD len = (* (DWORD *)&Response[Length]);
+ Length += 4;
+ if (b) {
+ *b = (BYTE *)&Response[Length];
+ }
+ if (pLen) {
+ *pLen = len;
+ }
+ Length += ROUNDUP4(len);
+ break;
+
+ }
+
+ case 'l':
+ {
+
+ BYTE* b = va_arg ( Arguments, BYTE* );
+ BYTE* w = va_arg ( Arguments, BYTE* );
+ WORD i;
+
+ b[1] = Response[Length++];
+ b[0] = Response[Length++];
+
+ for ( i = 0; i < ((WORD) *b); i++, w += sizeof(WORD) )
+ {
+ w[1] = Response[Length++];
+ w[0] = Response[Length++];
+ }
+
+ break;
+ }
+ */
+
+#ifdef NWDBG
+ default:
+ DbgPrintf ( "*****exchange: invalid response field, %x\n", *FormatByte );
+ DbgBreakPoint();
+#endif
+ }
+
+ if ( Length > ResponseLength ) {
+#ifdef NWDBG
+ DbgPrintf ( "*****exchange: not enough response data, %d\n", Length );
+
+ if ( IrpContext ) {
+
+ Error( EVENT_NWRDR_INVALID_REPLY,
+ STATUS_UNEXPECTED_NETWORK_ERROR,
+ NULL,
+ 0,
+ 1,
+ IrpContext->pNpScb->ServerName.Buffer );
+
+ }
+#endif
+ return( STATUS_UNEXPECTED_NETWORK_ERROR );
+ }
+
+ FormatByte++;
+ }
+
+ va_end( Arguments );
+
+ return( NcpStatus );
+}
+
+NTSTATUS
+ParseNcpResponse(
+ PIRP_CONTEXT IrpContext,
+ PNCP_RESPONSE Response
+ )
+{
+ NTSTATUS Status;
+
+ if ( Response->Status == 0 ) {
+ Status = NwErrorToNtStatus( Response->Error );
+ } else {
+ Status = NwConnectionStatusToNtStatus( Response->Status );
+ if ( Status == STATUS_REMOTE_DISCONNECT ) {
+ Stats.ServerDisconnects++;
+ IrpContext->pNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
+ }
+ }
+
+ return( Status );
+}
+
+NTSTATUS
+FormatRequest(
+ PIRP_CONTEXT pIrpC,
+ PEX pEx,
+ char* f,
+ va_list a // format specific parameters
+ )
+/*++
+
+Routine Description:
+
+ Send the packet described by f and the additional parameters. When a
+ valid response has been received call pEx with the resonse.
+
+ An exchange is a generic way of assembling a request packet of a
+ given type, containing a set of fields, sending the packet, receiving
+ a response packet, and disassembling the fields of the response packet.
+
+ The packet type and each field is specified by individual
+ characters in a format string.
+
+ The exchange procedure takes such a format string plus additional
+ parameters as necessary for each character in the string as specified
+ below.
+
+Arguments: '']
+
+ pIrpC - supplies the irp context for the exchange request.
+
+ pEx - supplies the routine to process the data.
+
+ f... - supplies the information needed to create the request to the
+ server. The first byte indicates the packet type and the
+ following bytes contain field types.
+
+ Packet types:
+
+ 'A' SAP broadcast ( void )
+ 'B' NCP burst ( dword, dword, byte )
+ 'C' NCP connect ( void )
+ 'F' NCP function ( byte )
+ 'S' NCP subfunction ( byte, byte )
+ 'N' NCP subfunction w/o size ( byte, byte )
+ 'D' NCP disconnect ( void )
+ 'E' Echo data ( void )
+
+ Field types, request/response:
+
+ 'b' byte ( byte / byte* )
+ 'w' hi-lo word ( word / word* )
+ 'd' hi-lo dword ( dword / dword* )
+ 'W' lo-hi word ( word / word* )
+ 'D' lo-hi dword ( dword / dword* )
+ '-' zero/skip byte ( void )
+ '=' zero/skip word ( void )
+ ._. zero/skip string ( word )
+ 'p' pstring ( char* )
+ 'u' p unicode string ( UNICODE_STRING * )
+ 'U' p uppercase string( UNICODE_STRING * )
+ 'J' variant of U ( UNICODE_STRING * )
+ 'c' cstring ( char* )
+ 'v' cstring ( UNICODE_STRING* )
+ 'r' raw bytes ( byte*, word )
+ 'w' fixed length unicode ( UNICODE_STRING*, word )
+ 'C' Component format name, with count ( UNICODE_STRING * )
+ 'N' Component format name, no count ( UNICODE_STRING * )
+ 'f' separate fragment ( PMDL )
+
+ An 'f' field must be last, and in a response it cannot be
+ preceeded by 'p' or 'c' fields.
+
+
+Return Value:
+
+ Normally returns STATUS_SUCCESS.
+
+--*/
+{
+ NTSTATUS status;
+ char* z;
+ word data_size;
+ PNONPAGED_SCB pNpScb = pIrpC->pNpScb;
+ dword dwData;
+
+ ASSERT( pIrpC->NodeTypeCode == NW_NTC_IRP_CONTEXT );
+ ASSERT( pIrpC->pNpScb != NULL );
+
+ status= STATUS_LINK_FAILED;
+
+ pIrpC->pEx = pEx; // Routine to process reply
+ pIrpC->Destination = pNpScb->RemoteAddress;
+ ClearFlag( pIrpC->Flags, IRP_FLAG_SEQUENCE_NO_REQUIRED );
+
+ switch ( *f ) {
+
+ case 'A':
+ // Send to local network (0), a broadcast (-1), socket 0x452
+ pIrpC->Destination = SapBroadcastAddress;
+ pIrpC->PacketType = SAP_BROADCAST;
+
+ data_size = 0;
+ pNpScb->RetryCount = 3;
+ pNpScb->MaxTimeOut = 2 * pNpScb->TickCount + 10;
+ pNpScb->TimeOut = pNpScb->MaxTimeOut;
+ SetFlag( pIrpC->Flags, IRP_FLAG_RETRY_SEND );
+ break;
+
+ case 'E':
+ pIrpC->Destination = pNpScb->EchoAddress;
+ pIrpC->PacketType = NCP_ECHO;
+
+ //
+ // For echo packets use a short timeout and a small retry count.
+ // Set the retry send bit, so that SendNow doesn't reset the
+ // RetryCount to a bigger number.
+ //
+
+ pNpScb->RetryCount = 0;
+ pNpScb->MaxTimeOut = 2 * pNpScb->TickCount + 7;
+ pNpScb->TimeOut = pNpScb->MaxTimeOut;
+ SetFlag( pIrpC->Flags, IRP_FLAG_RETRY_SEND );
+ SetFlag( pIrpC->Flags, IRP_FLAG_REROUTE_ATTEMPTED );
+
+ data_size = 0;
+ break;
+
+ case 'C':
+ pIrpC->PacketType = NCP_CONNECT;
+ *(PUSHORT)&pIrpC->req[0] = PEP_COMMAND_CONNECT;
+ pIrpC->req[2] = 0x00;
+ pIrpC->req[3] = 0xFF;
+ pIrpC->req[4] = 0x00;
+ pIrpC->req[5] = 0xFF;
+ data_size = 6;
+
+ pNpScb->MaxTimeOut = 16 * pNpScb->TickCount + 10;
+ pNpScb->TimeOut = 4 * pNpScb->TickCount + 10;
+ pNpScb->SequenceNo = 0;
+ break;
+
+ case 'F':
+ pIrpC->PacketType = NCP_FUNCTION;
+ goto FallThrough;
+
+ case 'S':
+ case 'N':
+ pIrpC->PacketType = NCP_SUBFUNCTION;
+ goto FallThrough;
+
+ case 'L':
+ pIrpC->PacketType = NCP_SUBFUNCTION;
+ goto FallThrough;
+
+ case 'D':
+ pIrpC->PacketType = NCP_DISCONNECT;
+ FallThrough:
+ if ( *f == 'D' ) {
+ *(PUSHORT)&pIrpC->req[0] = PEP_COMMAND_DISCONNECT;
+ } else {
+ *(PUSHORT)&pIrpC->req[0] = PEP_COMMAND_REQUEST;
+ }
+
+ pNpScb->RetryCount = DefaultRetryCount ;
+ pNpScb->MaxTimeOut = 2 * pNpScb->TickCount + 10;
+ pNpScb->TimeOut = pNpScb->SendTimeout;
+
+ //
+ // Mark this packet as SequenceNumberRequired. We need to guarantee
+ // the packets are sent in sequence number order, so we will
+ // fill in the sequence number when we are ready to send the
+ // packet.
+ //
+
+ SetFlag( pIrpC->Flags, IRP_FLAG_SEQUENCE_NO_REQUIRED );
+ pIrpC->req[3] = pNpScb->ConnectionNo;
+ pIrpC->req[5] = pNpScb->ConnectionNoHigh;
+
+ if ( pIrpC->Icb != NULL && pIrpC->Icb->Pid != INVALID_PID ) {
+ pIrpC->req[4] = (UCHAR)pIrpC->Icb->Pid;
+ } else {
+ pIrpC->req[4] = 0xFF;
+ }
+
+ data_size = 6;
+
+ if ( *f == 'L' ) {
+ pIrpC->req[data_size++] = NCP_LFN_FUNCTION;
+ }
+
+ if ( *f != 'D' ) {
+ pIrpC->req[data_size++] = va_arg( a, byte );
+ }
+
+ if ( *f == 'S' ) {
+ data_size += 2;
+ pIrpC->req[data_size++] = va_arg( a, byte );
+ }
+
+ if ( *f == 'N' ) {
+ pIrpC->req[data_size++] = va_arg( a, byte );
+ }
+
+ break;
+
+ case 'B':
+ pIrpC->PacketType = NCP_BURST;
+ *(PUSHORT)&pIrpC->req[0] = PEP_COMMAND_BURST;
+
+ pNpScb->TimeOut = pNpScb->MaxTimeOut;
+
+ if ( !BooleanFlagOn( pIrpC->Flags, IRP_FLAG_RETRY_SEND ) ) {
+ pNpScb->RetryCount = 20;
+ }
+
+ pIrpC->req[3] = 0x2; // Stream Type = Big Send Burst
+
+ *(PULONG)&pIrpC->req[4] = pNpScb->SourceConnectionId;
+ *(PULONG)&pIrpC->req[8] = pNpScb->DestinationConnectionId;
+
+
+ LongByteSwap( (*(PULONG)&pIrpC->req[16]) , pNpScb->CurrentBurstDelay ); // Send delay time
+ dwData = va_arg( a, dword ); // Size of data
+ LongByteSwap( pIrpC->req[24], dwData );
+ dwData = va_arg( a, dword ); // Offset of data
+ LongByteSwap( pIrpC->req[28], dwData );
+ pIrpC->req[2] = va_arg( a, byte ); // Burst flags
+
+ data_size = 34;
+
+ break;
+
+ default:
+ DbgPrintf ( "*****exchange: invalid packet type, %x\n", *f );
+ DbgBreakPoint();
+ va_end( a );
+ return status;
+ }
+
+ z = f;
+ while ( *++z && *z != 'f' )
+ {
+ switch ( *z )
+ {
+ case '=':
+ pIrpC->req[data_size++] = 0;
+ case '-':
+ pIrpC->req[data_size++] = 0;
+ break;
+
+ case '_':
+ {
+ word l = va_arg ( a, word );
+ ASSERT( data_size + l <= MAX_SEND_DATA );
+
+ while ( l-- )
+ pIrpC->req[data_size++] = 0;
+ break;
+ }
+
+ case 's':
+ {
+ word l = va_arg ( a, word );
+ ASSERT ( data_size + l <= MAX_SEND_DATA );
+ data_size += l;
+ break;
+ }
+
+ case 'i':
+ pIrpC->req[4] = va_arg ( a, byte );
+ break;
+
+ case 'b':
+ pIrpC->req[data_size++] = va_arg ( a, byte );
+ break;
+
+ case 'w':
+ {
+ word w = va_arg ( a, word );
+ pIrpC->req[data_size++] = (byte) (w >> 8);
+ pIrpC->req[data_size++] = (byte) (w >> 0);
+ break;
+ }
+
+
+ case 'd':
+ {
+ dword d = va_arg ( a, dword );
+ pIrpC->req[data_size++] = (byte) (d >> 24);
+ pIrpC->req[data_size++] = (byte) (d >> 16);
+ pIrpC->req[data_size++] = (byte) (d >> 8);
+ pIrpC->req[data_size++] = (byte) (d >> 0);
+ break;
+ }
+
+ case 'W':
+ {
+ word w = va_arg ( a, word );
+ *(word UNALIGNED *)&pIrpC->req[data_size] = w;
+ data_size += 2;
+ break;
+ }
+
+
+ case 'D':
+ {
+ dword d = va_arg ( a, dword );
+ *(dword UNALIGNED *)&pIrpC->req[data_size] = d;
+ data_size += 4;
+ break;
+ }
+
+ case 'c':
+ {
+ char* c = va_arg ( a, char* );
+ word l = strlen( c );
+ ASSERT (data_size + l <= MAX_SEND_DATA );
+
+ RtlCopyMemory( &pIrpC->req[data_size], c, l+1 );
+ data_size += l + 1;
+ break;
+ }
+
+ case 'v':
+ {
+ PUNICODE_STRING pUString = va_arg ( a, PUNICODE_STRING );
+ OEM_STRING OemString;
+ ULONG Length;
+
+ Length = RtlUnicodeStringToOemSize( pUString ) - 1;
+ ASSERT (( data_size + Length <= MAX_SEND_DATA) && ( (Length & 0xffffff00) == 0) );
+
+ OemString.Buffer = &pIrpC->req[data_size];
+ OemString.MaximumLength = (USHORT)Length + 1;
+ status = RtlUnicodeStringToCountedOemString( &OemString, pUString, FALSE );
+ ASSERT( NT_SUCCESS( status ));
+ data_size += (USHORT)Length + 1;
+ break;
+ }
+
+ case 'p':
+ {
+ char* c = va_arg ( a, char* );
+ byte l = strlen( c );
+
+ if ((data_size+l>MAX_SEND_DATA) ||
+ ( (l & 0xffffff00) != 0) ) {
+
+ ASSERT("***exchange: Packet too long!2!\n" && FALSE );
+ return STATUS_OBJECT_PATH_SYNTAX_BAD;
+ }
+
+ pIrpC->req[data_size++] = l;
+ RtlCopyMemory( &pIrpC->req[data_size], c, l );
+ data_size += l;
+ break;
+ }
+
+ case 'J':
+ case 'U':
+ case 'u':
+ {
+ PUNICODE_STRING pUString = va_arg ( a, PUNICODE_STRING );
+ OEM_STRING OemString;
+ PUCHAR pOemString;
+ ULONG Length;
+ ULONG i;
+
+ //
+ // Calculate required string length, excluding trailing NUL.
+ //
+
+ Length = RtlUnicodeStringToOemSize( pUString ) - 1;
+ ASSERT( Length < 0x100 );
+
+ if (( data_size + Length > MAX_SEND_DATA ) ||
+ ( (Length & 0xffffff00) != 0) ) {
+ ASSERT("***exchange:Packet too long or name >255 chars!4!\n" && FALSE);
+ return STATUS_OBJECT_PATH_SYNTAX_BAD;
+ }
+
+ pIrpC->req[data_size++] = (UCHAR)Length;
+ OemString.Buffer = &pIrpC->req[data_size];
+ OemString.MaximumLength = (USHORT)Length + 1;
+
+ if ( *z == 'u' ) {
+ status = RtlUnicodeStringToCountedOemString(
+ &OemString,
+ pUString,
+ FALSE );
+ } else {
+ status = RtlUpcaseUnicodeStringToCountedOemString(
+ &OemString,
+ pUString,
+ FALSE );
+ }
+
+ if ( !NT_SUCCESS( status ) ) {
+ return status;
+ }
+
+ data_size += (USHORT)Length;
+
+ if (( Japan ) &&
+ ( *z == 'J' )) {
+
+ //
+ // Netware Japanese version The following single byte character is replaced with another one
+ // if the string is for File Name only when sending from Client to Server.
+ //
+ // U+0xFF7F SJIS+0xBF -> 0x10
+ // U+0xFF6E SJIS+0xAE -> 0x11
+ // U+0xFF64 SJIS+0xAA -> 0x12
+ //
+
+ for ( i = 0 , pOemString = OemString.Buffer ; i < Length ; i++ , pOemString++ ) {
+
+ if( FsRtlIsLeadDbcsCharacter( *pOemString ) ) {
+
+ // Skip the trailing byte
+
+ i++; pOemString++;
+
+ if (*pOemString == 0x5C ) {
+
+ //
+ // The trailbyte is 0x5C, replace it with 0x13
+ //
+
+ *pOemString = 0x13;
+
+ }
+
+ } else {
+
+ // Single byte character that may need modification.
+
+
+ if ( *pOemString == 0xBF ) {
+
+ *pOemString = 0x10;
+
+ } else if ( *pOemString == 0xAA ) {
+
+ *pOemString = 0x12;
+
+ } else if ( *pOemString == 0xAE ) {
+
+ *pOemString = 0x11;
+ }
+ }
+ }
+ }
+
+ break;
+ }
+
+ case 'r':
+ {
+ byte* b = va_arg ( a, byte* );
+ word l = va_arg ( a, word );
+ if (data_size+l>MAX_SEND_DATA) {
+ ASSERT("***exchange: Packet too long!6!\n"&& FALSE);
+ return STATUS_UNSUCCESSFUL;
+ }
+ RtlCopyMemory( &pIrpC->req[data_size], b, l );
+ data_size += l;
+ break;
+ }
+
+ case 'x':
+ {
+ PUNICODE_STRING pUString = va_arg ( a, PUNICODE_STRING );
+ ULONG RequiredLength = va_arg( a, word );
+ ULONG Length;
+ OEM_STRING OemString;
+
+ //
+ // Convert this string to an OEM string.
+ //
+
+ status = RtlUnicodeStringToCountedOemString( &OemString, pUString, TRUE );
+ ASSERT( NT_SUCCESS( status ));
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ if ( data_size + RequiredLength > MAX_SEND_DATA ) {
+ ASSERT("***exchange: Packet too long!4!\n" && FALSE);
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ //
+ // Copy the oem string to the buffer, padded with 0's if
+ // necessary.
+ //
+
+ Length = MIN( OemString.Length, RequiredLength );
+ RtlMoveMemory( &pIrpC->req[data_size], OemString.Buffer, Length );
+
+ if ( RequiredLength > Length ) {
+ RtlFillMemory(
+ &pIrpC->req[data_size+Length],
+ RequiredLength - Length,
+ 0 );
+ }
+
+ RtlFreeAnsiString(&OemString);
+
+ data_size += (USHORT)RequiredLength;
+ break;
+ }
+
+ case 'C':
+ case 'N':
+ {
+ PUNICODE_STRING pUString = va_arg ( a, PUNICODE_STRING );
+ OEM_STRING OemString;
+ PWCH thisChar, lastChar, firstChar;
+ PCHAR componentCountPtr, pchar;
+ CHAR componentCount;
+ UNICODE_STRING UnicodeString;
+ int i;
+
+ //
+ // Copy the oem string to the buffer, in component format.
+ //
+
+ thisChar = pUString->Buffer;
+ lastChar = &pUString->Buffer[ pUString->Length / sizeof(WCHAR) ];
+
+ //
+ // Skip leading path separators
+ //
+
+ while ( *thisChar == OBJ_NAME_PATH_SEPARATOR &&
+ thisChar < lastChar ) {
+ thisChar++;
+ }
+
+ componentCount = 0;
+ if ( *z == 'C' ) {
+ componentCountPtr = &pIrpC->req[data_size++];
+ }
+
+
+ while ( thisChar < lastChar ) {
+
+ if ( data_size >= MAX_SEND_DATA - 1 ) {
+ ASSERT( ("***exchange: Packet too long or name > 255 chars!5!\n" && FALSE) );
+ return STATUS_OBJECT_PATH_SYNTAX_BAD;
+ }
+
+ firstChar = thisChar;
+
+ while ( thisChar < lastChar &&
+ *thisChar != OBJ_NAME_PATH_SEPARATOR ) {
+
+ thisChar++;
+
+ }
+
+ ++componentCount;
+
+ UnicodeString.Buffer = firstChar;
+ UnicodeString.Length = ( thisChar - firstChar ) * sizeof(WCHAR);
+
+ OemString.Buffer = &pIrpC->req[data_size + 1];
+ OemString.MaximumLength = MAX_SEND_DATA - data_size - 1;
+
+ status = RtlUnicodeStringToCountedOemString( &OemString, &UnicodeString, FALSE );
+
+ pIrpC->req[data_size] = (UCHAR)OemString.Length;
+ data_size += OemString.Length + 1;
+
+ if ( !NT_SUCCESS( status ) || data_size > MAX_SEND_DATA ) {
+ ASSERT("***exchange: Packet too long or name > 255 chars!5!\n" && FALSE );
+ return STATUS_OBJECT_PATH_SYNTAX_BAD;
+ }
+
+ //
+ // Search the result OEM string for the character 0xFF.
+ // If it's there, fail this request. The server doesn't
+ // deal with 0xFF very well.
+ //
+
+ for ( pchar = OemString.Buffer, i = 0;
+ i < OemString.Length;
+ pchar++, i++ ) {
+
+ //
+ // We need to check for dbcs, because 0xff is a
+ // legal trail byte for EUDC characters.
+ //
+ if ( FsRtlIsLeadDbcsCharacter( (UCHAR)*pchar ) ) {
+
+ //
+ // Skip dbcs character.
+ //
+
+ pchar++; i++;
+ continue;
+ }
+
+ if (( (UCHAR)*pchar == LFN_META_CHARACTER ) ||
+ !FsRtlIsAnsiCharacterLegalHpfs(*pchar, FALSE) ) {
+
+ return STATUS_OBJECT_PATH_SYNTAX_BAD;
+ }
+
+ }
+
+ thisChar++; // Skip the path separator
+
+ }
+
+ if ( *z == 'C' ) {
+ *componentCountPtr = componentCount;
+ }
+
+ break;
+ }
+
+ default:
+#ifdef NWDBG
+ DbgPrintf ( "*****exchange: invalid request field, %x\n", *z );
+ DbgBreakPoint();
+#endif
+ ;
+ }
+
+ if ( data_size > MAX_SEND_DATA )
+ {
+ DbgPrintf( "*****exchange: CORRUPT, too much request data\n" );
+ DbgBreakPoint();
+ va_end( a );
+ return STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ pIrpC->TxMdl->ByteCount = data_size;
+
+ if ( *z == 'f' )
+ {
+ PMDL mdl;
+
+ //
+ // Fragment of data following Ipx header. Next parameter is
+ // the address of the mdl describing the fragment.
+ //
+ ++z;
+ mdl = (PMDL) va_arg ( a, byte* );
+ pIrpC->TxMdl->Next = mdl;
+
+ data_size += (USHORT)MdlLength( mdl );
+ }
+
+ if ( *f == 'S' ) {
+
+ pIrpC->req[7] = (data_size-9) >> 8;
+ pIrpC->req[8] = (data_size-9);
+
+ } else if ( *f == 'B' ) {
+
+ //
+ // For burst packets set the number of bytes in this packet to
+ // a real number for burst requests, and to 0 for a missing packet
+ // request.
+ //
+
+ if ( *(PUSHORT)&pIrpC->req[34] == 0 ) {
+ USHORT RealDataSize = data_size - 36;
+ ShortByteSwap( pIrpC->req[32], RealDataSize );
+ } else {
+ *(PUSHORT)&pIrpC->req[32] = 0;
+ }
+ }
+
+ va_end( a );
+ return( STATUS_SUCCESS );
+}
+
+NTSTATUS
+PrepareAndSendPacket(
+ PIRP_CONTEXT pIrpContext
+ )
+{
+ PreparePacket( pIrpContext, pIrpContext->pOriginalIrp, pIrpContext->TxMdl );
+
+ return SendPacket( pIrpContext, pIrpContext->pNpScb );
+}
+
+VOID
+PreparePacket(
+ PIRP_CONTEXT pIrpContext,
+ PIRP pIrp,
+ PMDL pMdl
+ )
+/*++
+
+Routine Description:
+
+ This routine builds the IRP for sending a packet.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for the request
+ being processed.
+
+ Irp - The IRP to be used to submit the request to the transport.
+
+ Mdl - A pointer to the MDL for the data to send.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PIO_COMPLETION_ROUTINE CompletionRoutine;
+ PNW_TDI_STRUCT pTdiStruct;
+
+ DebugTrace(0, Dbg, "PreparePacket...\n", 0);
+
+ pIrpContext->ConnectionInformation.UserDataLength = 0;
+ pIrpContext->ConnectionInformation.OptionsLength = sizeof( UCHAR );
+ pIrpContext->ConnectionInformation.Options =
+ (pIrpContext->PacketType == SAP_BROADCAST) ?
+ &SapPacketType : &NcpPacketType;
+ pIrpContext->ConnectionInformation.RemoteAddressLength = sizeof(TA_IPX_ADDRESS);
+ pIrpContext->ConnectionInformation.RemoteAddress = &pIrpContext->Destination;
+
+#if NWDBG
+ dump( Dbg,
+ &pIrpContext->Destination.Address[0].Address[0],
+ sizeof(TDI_ADDRESS_IPX));
+ dumpMdl( Dbg, pMdl);
+#endif
+
+ //
+ // Set the socket to use for this send. If unspecified in the
+ // IRP context, use the default (server) socket.
+ //
+
+ pTdiStruct = pIrpContext->pTdiStruct == NULL ?
+ &pIrpContext->pNpScb->Server : pIrpContext->pTdiStruct;
+
+ CompletionRoutine = pIrpContext->CompletionSendRoutine == NULL ?
+ CompletionSend : pIrpContext->CompletionSendRoutine;
+
+ TdiBuildSendDatagram(
+ pIrp,
+ pTdiStruct->pDeviceObject,
+ pTdiStruct->pFileObject,
+ CompletionRoutine,
+ pIrpContext,
+ pMdl,
+ MdlLength( pMdl ),
+ &pIrpContext->ConnectionInformation );
+
+ //
+ // Set the run routine to send now, only if this is the main IRP
+ // for this irp context.
+ //
+
+ if ( pIrp == pIrpContext->pOriginalIrp ) {
+ pIrpContext->RunRoutine = SendNow;
+ }
+
+ return;
+}
+
+
+NTSTATUS
+SendPacket(
+ PIRP_CONTEXT pIrpC,
+ PNONPAGED_SCB pNpScb
+ )
+/*++
+
+Routine Description:
+
+ Queue a packet created by exchange and try to send it to the server.
+
+Arguments:
+
+ pIrpC - supplies the irp context for the request creating the socket.
+
+ pNpScb - supplies the server to receive the request.
+
+Return Value:
+
+ STATUS_PENDING
+
+--*/
+{
+ if ( AppendToScbQueue( pIrpC, pNpScb ) ) {
+ KickQueue( pNpScb );
+ }
+
+ return STATUS_PENDING;
+}
+
+
+BOOLEAN
+AppendToScbQueue(
+ PIRP_CONTEXT IrpContext,
+ PNONPAGED_SCB NpScb
+ )
+/*++
+
+Routine Description:
+
+ Queue an IRP context to the SCB, if it is not already there.
+
+Arguments:
+
+ IrpContext - Supplies the IRP context to queue.
+
+ NpScb - Supplies the server to receive the request.
+
+Return Value:
+
+ TRUE - The IRP Context is at the front of the queue.
+ FALSE - The IRP Context is not at the front of the queue.
+
+--*/
+{
+ PLIST_ENTRY ListEntry;
+#ifdef MSWDBG
+ KIRQL OldIrql;
+#endif
+ DebugTrace(0, Dbg, "AppendToScbQueue... %08lx\n", NpScb);
+ DebugTrace(0, Dbg, "IrpContext = %08lx\n", IrpContext );
+
+ //
+ // Look at the IRP Context flags. If the IRP is already on the
+ // queue, then it must be at the front and ready for processing.
+ //
+
+ if ( FlagOn( IrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE ) ) {
+ ASSERT( NpScb->Requests.Flink == &IrpContext->NextRequest );
+ return( TRUE );
+ }
+
+#ifdef MSWDBG
+ NpScb->RequestQueued = TRUE;
+#endif
+
+#if 0 // Resource layout changed on Daytona. Disable for now.
+
+ //
+ // Make sure that this thread isn't holding the RCB while waiting for
+ // the SCB queue.
+ //
+
+ ASSERT ( NwRcb.Resource.InitialOwnerThreads[0] != (ULONG)PsGetCurrentThread() );
+#endif
+
+ //
+ // The IRP Context was not at the front. Queue it, then look to
+ // see if it was appended to an empty queue.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE );
+
+#ifdef MSWDBG
+ ExAcquireSpinLock( &NpScb->NpScbSpinLock, &OldIrql );
+ if ( IsListEmpty( &NpScb->Requests ) ) {
+ ListEntry = NULL;
+ } else {
+ ListEntry = NpScb->Requests.Flink;
+ }
+
+ InsertTailList( &NpScb->Requests, &IrpContext->NextRequest );
+ IrpContext->SequenceNumber = NpScb->SequenceNumber++;
+ ExReleaseSpinLock( &NpScb->NpScbSpinLock, OldIrql );
+
+#else
+ ListEntry = ExInterlockedInsertTailList(
+ &NpScb->Requests,
+ &IrpContext->NextRequest,
+ &NpScb->NpScbSpinLock );
+#endif
+
+ if ( ListEntry == NULL ) {
+ ASSERT( NpScb->Requests.Flink == &IrpContext->NextRequest );
+ DebugTrace(-1, Dbg, "AppendToScbQueue -> TRUE\n", 0);
+ return( TRUE );
+ } else {
+ DebugTrace(-1, Dbg, "AppendToScbQueue -> FALSE\n", 0);
+ return( FALSE );
+ }
+
+}
+
+
+VOID
+KickQueue(
+ PNONPAGED_SCB pNpScb
+ )
+/*++
+
+Routine Description:
+
+ Queue a packet created by exchange and try to send it to the server.
+
+ Note: NpScbSpinLock must be held before calling this routine.
+
+Arguments:
+
+ pNpScb - supplies the server queue to kick into life.
+
+Return Value:
+
+ none.
+
+--*/
+{
+
+ PIRP_CONTEXT pIrpC;
+ PRUN_ROUTINE RunRoutine;
+ KIRQL OldIrql;
+
+
+ DebugTrace( +1, Dbg, "KickQueue...%08lx\n", pNpScb);
+
+ KeAcquireSpinLock( &pNpScb->NpScbSpinLock, &OldIrql );
+ if ( IsListEmpty( &pNpScb->Requests )) {
+ KeReleaseSpinLock( &pNpScb->NpScbSpinLock, OldIrql );
+ DebugTrace( -1, Dbg, " Empty Queue\n", 0);
+ return;
+ }
+
+ pIrpC = CONTAINING_RECORD(pNpScb->Requests.Flink, IRP_CONTEXT, NextRequest);
+
+ ASSERT( pIrpC->pNpScb->Requests.Flink == &pIrpC->NextRequest );
+ ASSERT( pIrpC->NodeTypeCode == NW_NTC_IRP_CONTEXT);
+
+ RunRoutine = pIrpC->RunRoutine;
+
+ // Only call the routine to tell it it is at the front once
+
+ pIrpC->RunRoutine = NULL;
+
+ KeReleaseSpinLock( &pNpScb->NpScbSpinLock, OldIrql );
+
+ //
+ // If the redir is shutting down do not process this request
+ // unless we must.
+ //
+
+ if ( NwRcb.State != RCB_STATE_RUNNING &&
+ !FlagOn( pIrpC->Flags, IRP_FLAG_SEND_ALWAYS ) ) {
+
+ //
+ // Note that it's safe to call the pEx routine without the
+ // spin lock held since this IrpContext just made it to the
+ // front of the queue, and so can't have i/o in progress.
+ //
+
+ if ( pIrpC->pEx != NULL) {
+ pIrpC->pEx( pIrpC, 0, NULL );
+ DebugTrace( -1, Dbg, "KickQueue\n", 0);
+ return;
+ }
+ }
+
+ if ( RunRoutine != NULL ) {
+
+ ASSERT( pNpScb->Receiving == FALSE );
+
+ RunRoutine( pIrpC );
+
+ }
+
+ DebugTrace( -1, Dbg, "KickQueue\n", 0);
+ return;
+}
+
+VOID
+SendNow(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine submits a TDI send request to the tranport layer.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for the request
+ being processed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PNONPAGED_SCB pNpScb;
+ NTSTATUS Status;
+ PIO_STACK_LOCATION IrpSp;
+
+ pNpScb = IrpContext->pNpScb;
+
+ if ( !BooleanFlagOn( IrpContext->Flags, IRP_FLAG_RETRY_SEND ) ) {
+ pNpScb->RetryCount = DefaultRetryCount;
+ }
+
+ //
+ // Ensure that this IRP Context is really at the front of the queue.
+ //
+
+ ASSERT( pNpScb->Requests.Flink == &IrpContext->NextRequest );
+ IrpContext->RunRoutine = NULL;
+
+ //
+ // Make sure that this is a correctly formatted send request.
+ //
+
+ IrpSp = IoGetNextIrpStackLocation( IrpContext->pOriginalIrp );
+ ASSERT( IrpSp->MajorFunction == IRP_MJ_INTERNAL_DEVICE_CONTROL );
+ ASSERT( IrpSp->MinorFunction == TDI_SEND_DATAGRAM );
+
+ //
+ // This IRP context has a packet ready to send. Send it now.
+ //
+
+ pNpScb->Sending = TRUE;
+ if ( !BooleanFlagOn( IrpContext->Flags, IRP_FLAG_NOT_OK_TO_RECEIVE ) ) {
+ pNpScb->OkToReceive = TRUE;
+ }
+ pNpScb->Receiving = FALSE;
+ pNpScb->Received = FALSE;
+
+ //
+ // If this packet requires a sequence number, set it now.
+ // The sequence number is updated when we receive a response.
+ //
+ // We do not need to synchronize access to SequenceNo since
+ // this is the only active packet for this SCB.
+ //
+
+ if ( BooleanFlagOn( IrpContext->Flags, IRP_FLAG_SEQUENCE_NO_REQUIRED ) ) {
+ ClearFlag( IrpContext->Flags, IRP_FLAG_SEQUENCE_NO_REQUIRED );
+ IrpContext->req[2] = pNpScb->SequenceNo;
+ }
+
+ //
+ // If this packet is a burst packet, fill in the burst sequence number
+ // now, and burst request number.
+ //
+
+ if ( BooleanFlagOn( IrpContext->Flags, IRP_FLAG_BURST_PACKET ) ) {
+
+ LongByteSwap( IrpContext->req[12], pNpScb->BurstSequenceNo );
+ pNpScb->BurstSequenceNo++;
+
+ ShortByteSwap( IrpContext->req[20], pNpScb->BurstRequestNo );
+ ShortByteSwap( IrpContext->req[22], pNpScb->BurstRequestNo );
+
+ }
+
+ DebugTrace( +0, Dbg, "Irp %X\n", IrpContext->pOriginalIrp);
+ DebugTrace( +0, Dbg, "pIrpC %X\n", IrpContext);
+ DebugTrace( +0, Dbg, "Mdl %X\n", IrpContext->TxMdl);
+
+#if NWDBG
+ dumpMdl( Dbg, IrpContext->TxMdl);
+#endif
+
+ {
+ ULONG len = 0;
+ PMDL Next = IrpContext->TxMdl;
+
+ do {
+ len += MmGetMdlByteCount(Next);
+ } while (Next = Next->Next);
+
+ Stats.BytesTransmitted.QuadPart += len;
+ }
+
+ Status = IoCallDriver(pNpScb->Server.pDeviceObject, IrpContext->pOriginalIrp);
+ DebugTrace( -1, Dbg, "Transport returned: %08lx\n", Status );
+
+ Stats.NcpsTransmitted.QuadPart++;
+
+ return;
+
+}
+
+
+VOID
+SetEvent(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine set the IrpContext Event to the signalled state.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for the request
+ being processed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ //
+ // Ensure that this IRP Context is really at the front of the queue.
+ //
+
+ ASSERT( IrpContext->pNpScb->Requests.Flink == &IrpContext->NextRequest );
+
+ //
+ // This IRP context has a thread waiting to get to the front of
+ // the queue. Set the event to indicate that it can continue.
+ //
+
+#ifdef MSWDBG
+ ASSERT( IrpContext->Event.Header.SignalState == 0 );
+ IrpContext->DebugValue = 0x105;
+#endif
+
+ DebugTrace( +0, Dbg, "Setting event for IrpContext %X\n", IrpContext );
+ NwSetIrpContextEvent( IrpContext );
+}
+
+
+USHORT
+NextSocket(
+ IN USHORT OldValue
+ )
+/*++
+
+Routine Description:
+
+ This routine returns the byteswapped OldValue++ wrapping from 7fff.
+
+Arguments:
+
+ OldValue - supplies the existing socket number in the range
+ 0x4000 to 0x7fff.
+
+Return Value:
+
+ USHORT OldValue++
+
+--*/
+
+{
+ USHORT TempValue = OldValue + 0x0100;
+
+ if ( TempValue < 0x100 ) {
+ if ( TempValue == 0x007f ) {
+ // Wrap back to 0x4000 from 0xff7f
+ return 0x0040;
+ } else {
+ // Go from something like 0xff40 to 0x0041
+ return TempValue + 1;
+ }
+ }
+ return TempValue;
+}
+
+
+ULONG
+MdlLength (
+ register IN PMDL Mdl
+ )
+/*++
+
+Routine Description:
+
+ This routine returns the number of bytes in an MDL.
+
+Arguments:
+
+ IN PMDL Mdl - Supplies the MDL to determine the length on.
+
+Return Value:
+
+ ULONG - Number of bytes in the MDL
+
+--*/
+
+{
+ register ULONG Size = 0;
+ while (Mdl!=NULL) {
+ Size += MmGetMdlByteCount(Mdl);
+ Mdl = Mdl->Next;
+ }
+ return Size;
+}
+
+
+NTSTATUS
+CompletionSend(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ This routine does not complete the Irp. It is used to signal to a
+ synchronous part of the driver that it can proceed.
+
+Arguments:
+
+ DeviceObject - unused.
+
+ Irp - Supplies Irp that the transport has finished processing.
+
+ Context - Supplies the IrpContext associated with the Irp.
+
+Return Value:
+
+ The STATUS_MORE_PROCESSING_REQUIRED so that the IO system stops
+ processing Irp stack locations at this point.
+
+--*/
+{
+ PNONPAGED_SCB pNpScb;
+ PIRP_CONTEXT pIrpC = (PIRP_CONTEXT) Context;
+ KIRQL OldIrql;
+
+ //
+ // Avoid completing the Irp because the Mdl etc. do not contain
+ // their original values.
+ //
+
+ DebugTrace( +1, Dbg, "CompletionSend\n", 0);
+ DebugTrace( +0, Dbg, "Irp %X\n", Irp);
+ DebugTrace( +0, Dbg, "pIrpC %X\n", pIrpC);
+ DebugTrace( +0, Dbg, "Status %X\n", Irp->IoStatus.Status);
+
+ pNpScb = pIrpC->pNpScb;
+ KeAcquireSpinLock( &pNpScb->NpScbSpinLock, &OldIrql );
+
+ ASSERT( pNpScb->Sending == TRUE );
+ pNpScb->Sending = FALSE;
+
+ //
+ // If we got a receive indication while waiting for send
+ // completion and the data is all valid, call the receive handler routine now.
+ //
+
+ if ( pNpScb->Received ) {
+
+ pNpScb->Receiving = FALSE;
+ pNpScb->Received = FALSE;
+
+ KeReleaseSpinLock( &pNpScb->NpScbSpinLock, OldIrql );
+
+ pIrpC->pEx(
+ pIrpC,
+ pIrpC->ResponseLength,
+ pIrpC->rsp );
+
+ } else if (( Irp->IoStatus.Status == STATUS_DEVICE_DOES_NOT_EXIST ) ||
+ ( Irp->IoStatus.Status == STATUS_BAD_NETWORK_PATH ) ||
+ ( Irp->IoStatus.Status == STATUS_INVALID_BUFFER_SIZE ) ||
+ ( Irp->IoStatus.Status == STATUS_NETWORK_UNREACHABLE )) {
+
+ //
+ // The send failed.
+ //
+
+ //
+ // FIXFIX I would prefer to use !NT_SUCCESS(Irp->IoStatus.Status) to
+ // get into this code but would need more time to check its ok.
+ //
+
+ //
+ // If this SCB is still flagged okay to receive (how could it not?)
+ // simply call the callback routine to indicate failure.
+ //
+ // If the SendCompletion hasn't happened, set up so that send
+ // completion will call the callback routine.
+ //
+
+ if ( pNpScb->OkToReceive ) {
+
+ pNpScb->OkToReceive = FALSE;
+ ClearFlag( pIrpC->Flags, IRP_FLAG_RETRY_SEND );
+
+ KeReleaseSpinLock( &pNpScb->NpScbSpinLock, OldIrql );
+ DebugTrace(+0, Dbg, "Send failed\n", 0 );
+
+ pIrpC->ResponseParameters.Error = ERROR_UNEXP_NET_ERR;
+ pIrpC->pEx( pIrpC, 0, NULL );
+
+ } else {
+ KeReleaseSpinLock( &pNpScb->NpScbSpinLock, OldIrql );
+ }
+
+ } else {
+
+ KeReleaseSpinLock( &pNpScb->NpScbSpinLock, OldIrql );
+ }
+
+ DebugTrace( -1, Dbg, "CompletionSend STATUS_MORE_PROCESSING_REQUIRED\n", 0);
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+ UNREFERENCED_PARAMETER( DeviceObject );
+ UNREFERENCED_PARAMETER( Irp );
+}
+
+#if NWDBG
+BOOLEAN UseIrpReceive = FALSE;
+#endif
+
+
+NTSTATUS
+ServerDatagramHandler(
+ IN PVOID TdiEventContext,
+ IN int SourceAddressLength,
+ IN PVOID SourceAddress,
+ IN int OptionsLength,
+ IN PVOID Options,
+ IN ULONG ReceiveDatagramFlags,
+ IN ULONG BytesIndicated,
+ IN ULONG BytesAvailable,
+ OUT ULONG *BytesTaken,
+ IN PVOID Tsdu,
+ OUT PIRP *IoRequestPacket
+ )
+/*++
+
+Routine Description:
+
+ This routine is the receive datagram event indication handler for the
+ Server socket.
+
+Arguments:
+
+ TdiEventContext - Context provided for this event, a pointer to the
+ non paged SCB.
+
+ SourceAddressLength - Length of the originator of the datagram.
+
+ SourceAddress - String describing the originator of the datagram.
+
+ OptionsLength - Length of the buffer pointed to by Options.
+
+ Options - Options for the receive.
+
+ ReceiveDatagramFlags - Ignored.
+
+ BytesIndicated - Number of bytes this indication.
+
+ BytesAvailable - Number of bytes in complete Tsdu.
+
+ BytesTaken - Returns the number of bytes used.
+
+ Tsdu - Pointer describing this TSDU, typically a lump of bytes.
+
+ IoRequestPacket - TdiReceive IRP if MORE_PROCESSING_REQUIRED.
+
+Return Value:
+
+ NTSTATUS - Status of receive operation
+
+--*/
+{
+ PNONPAGED_SCB pNpScb = (PNONPAGED_SCB)TdiEventContext;
+ NTSTATUS Status = STATUS_DATA_NOT_ACCEPTED;
+ UCHAR PacketType;
+ PUCHAR RspData = (PUCHAR)Tsdu;
+ PIRP_CONTEXT pIrpC;
+ PNW_TDI_STRUCT pTdiStruct;
+ BOOLEAN AcceptPacket = TRUE;
+ PNCP_BURST_READ_RESPONSE pBurstRsp;
+ NTSTATUS BurstStatus;
+
+ *IoRequestPacket = NULL;
+#if DBG
+ pTdiStruct = NULL;
+#endif
+
+ if (pNpScb->NodeTypeCode != NW_NTC_SCBNP ) {
+
+ DebugTrace(+0, 0, "nwrdr: Invalid Server Indication %x\n", pNpScb );
+#if DBG
+ DbgBreakPoint();
+#endif
+ return STATUS_DATA_NOT_ACCEPTED;
+ }
+
+#if NWDBG
+
+ // Debug only trick to test IRP receive.
+
+ if ( UseIrpReceive ) {
+ BytesIndicated = 0;
+ }
+#endif
+
+ DebugTrace(+1, Dbg, "ServerDatagramHandler\n", 0);
+ DebugTrace(+0, Dbg, "Server %x\n", pNpScb);
+ DebugTrace(+0, Dbg, "BytesIndicated %x\n", BytesIndicated);
+ DebugTrace(+0, Dbg, "BytesAvailable %x\n", BytesAvailable);
+
+ //
+ // SourceAddress is the address of the server or the bridge tbat sent
+ // the packet.
+ //
+
+#if NWDBG
+ dump( Dbg, SourceAddress, SourceAddressLength );
+ dump( Dbg, Tsdu, BytesIndicated );
+#endif
+
+ if ( OptionsLength == 1 ) {
+ PacketType = *(PCHAR)Options;
+ DebugTrace(+0, Dbg, "PacketType %x\n", PacketType);
+ } else {
+ DebugTrace(+0, Dbg, "OptionsLength %x\n", OptionsLength);
+#if NWDBG
+ dump( Dbg, Options, OptionsLength );
+#endif
+ }
+
+ KeAcquireSpinLockAtDpcLevel(&pNpScb->NpScbSpinLock );
+
+ if ( !pNpScb->OkToReceive ) {
+
+ //
+ // This SCB is not expecting to receive any data.
+ // Discard this packet.
+ //
+
+#ifdef NWDBG
+ DropCount++;
+#endif
+ DebugTrace(+0, Dbg, "OkToReceive == FALSE - discard packet\n", 0);
+ AcceptPacket = FALSE;
+ goto process_packet;
+ }
+
+ pIrpC = CONTAINING_RECORD(pNpScb->Requests.Flink, IRP_CONTEXT, NextRequest);
+
+ ASSERT( pIrpC->NodeTypeCode == NW_NTC_IRP_CONTEXT);
+
+ //
+ // Verify that this packet came from where we expect it to come from,
+ // and that is has a minimum size.
+ //
+
+ if ( ( pIrpC->PacketType != SAP_BROADCAST &&
+ RtlCompareMemory(
+ &pIrpC->Destination,
+ SourceAddress,
+ SourceAddressLength ) != (ULONG)SourceAddressLength ) ||
+ BytesIndicated < 8 ) {
+
+ AcceptPacket = FALSE;
+#ifdef NWDBG
+ DbgPrintf ( "***exchange: stray response tossed\n", 0 );
+#endif
+ goto process_packet;
+ }
+
+ switch ( pIrpC->PacketType ) {
+
+ case SAP_BROADCAST:
+
+ //
+ // We are expected a SAP Broadcast frame. Ensure that this
+ // is a correctly formatted SAP.
+ //
+
+ if ( pIrpC->req[0] != RspData[0] ||
+ pIrpC->req[2] != RspData[2] ||
+ pIrpC->req[3] != RspData[3] ||
+ SourceAddressLength != sizeof(TA_IPX_ADDRESS) ) {
+
+ DbgPrintf ( "***exchange: bad SAP packet\n" );
+ AcceptPacket = FALSE;
+ }
+
+ pTdiStruct = &pNpScb->Server;
+ break;
+
+ case NCP_BURST:
+
+ if ( *(USHORT UNALIGNED *)&RspData[0] == PEP_COMMAND_BURST ) {
+
+ if ( BytesIndicated < 36 ) {
+
+ AcceptPacket = FALSE;
+
+ } else if ( ( RspData[2] & BURST_FLAG_SYSTEM_PACKET ) &&
+ RspData[34] == 0 &&
+ RspData[35] == 0 ) {
+
+ //
+ // We have burst mode busy reponse.
+ //
+
+ DebugTrace(+0, Dbg, "Burst mode busy\n", 0 );
+ NwProcessPositiveAck( pNpScb );
+
+ AcceptPacket = FALSE;
+
+ } else {
+
+ USHORT Brn;
+
+ //
+ // Check the burst sequence number.
+ //
+
+ ShortByteSwap( Brn, RspData[20] );
+
+ if ( pNpScb->BurstRequestNo == Brn ) {
+ pTdiStruct = &pNpScb->Burst;
+ AcceptPacket = TRUE;
+ } else {
+ AcceptPacket = FALSE;
+ }
+ }
+ } else {
+ AcceptPacket = FALSE;
+ }
+
+ break;
+
+ case NCP_ECHO:
+
+ pTdiStruct = &pNpScb->Echo;
+ AcceptPacket = TRUE;
+ break;
+
+ default:
+
+ pTdiStruct = &pNpScb->Server;
+
+ //
+ // This is the handling for all packets types other than
+ // SAP Broadcasts.
+ //
+
+ ASSERT( (pIrpC->PacketType == NCP_CONNECT) ||
+ (pIrpC->PacketType == NCP_FUNCTION) ||
+ (pIrpC->PacketType == NCP_SUBFUNCTION) ||
+ (pIrpC->PacketType == NCP_DISCONNECT));
+
+ if ( *(USHORT UNALIGNED *)&RspData[0] == PEP_COMMAND_ACKNOWLEDGE ) {
+
+ AcceptPacket = FALSE;
+
+ if ( RspData[2] == pIrpC->req[2] &&
+ RspData[3] == pIrpC->req[3] ) {
+
+ //
+ // We have received an ACK frame.
+ //
+
+ DebugTrace(+0, Dbg, "Received positive acknowledge\n", 0 );
+ NwProcessPositiveAck( pNpScb );
+
+ }
+
+ break;
+
+ } else if ( *(USHORT UNALIGNED *)&RspData[0] == PEP_COMMAND_BURST ) {
+
+ //
+ // This is a stray burst response, ignore it.
+ //
+
+ AcceptPacket = FALSE;
+ break;
+
+ } else if ( *(USHORT UNALIGNED *)&RspData[0] != PEP_COMMAND_RESPONSE ) {
+
+ //
+ // We have received an invalid frame.
+ //
+
+ DbgPrintf ( "***exchange: invalid Response\n" );
+ AcceptPacket = FALSE;
+ break;
+
+ } else if ( pIrpC->PacketType == NCP_CONNECT ) {
+
+ pNpScb->SequenceNo = RspData[2];
+ pNpScb->ConnectionNo = RspData[3];
+ pNpScb->ConnectionNoHigh = RspData[5];
+
+ // We should now continue to process the Connect
+ break;
+ }
+
+ //
+ // Make sure this the response we expect.
+ //
+
+ if ( !VerifyResponse( pIrpC, RspData ) ) {
+
+ //
+ // This is a stray or corrupt response. Ignore it.
+ //
+
+ AcceptPacket = FALSE;
+ break;
+
+ } else {
+
+ //
+ // We have received a valid, in sequence response.
+ // Bump the current sequence number.
+ //
+
+ ++pNpScb->SequenceNo;
+
+ }
+
+ if ( pIrpC->PacketType == NCP_FUNCTION ||
+ pIrpC->PacketType == NCP_SUBFUNCTION ) {
+
+ if ( ( RspData[7] &
+ ( NCP_STATUS_BAD_CONNECTION |
+ NCP_STATUS_NO_CONNECTIONS ) ) != 0 ) {
+ //
+ // We've lost our connection to the server.
+ // Try to reconnect if it is allowed for this request.
+ //
+
+ pNpScb->State = SCB_STATE_RECONNECT_REQUIRED;
+
+ if ( BooleanFlagOn( pIrpC->Flags, IRP_FLAG_RECONNECTABLE ) ) {
+ ClearFlag( pIrpC->Flags, IRP_FLAG_RECONNECTABLE );
+ AcceptPacket = FALSE;
+ if (!pNpScb->Sending) {
+ ScheduleReconnectRetry( pIrpC );
+ pNpScb->OkToReceive = FALSE;
+ } else {
+ //
+ // If we are sending, it is not OK schedule the
+ // retry now, because if we do and the send
+ // completion hasnt been run we could end up
+ // with 2 guys thinking they are at the front
+ // of the queue. We let the send complete and
+ // wait for that to fail instead. We will
+ // eventually reconnect.
+ //
+ }
+ }
+
+ break;
+
+ } else if ( ( RspData[7] & NCP_STATUS_SHUTDOWN ) != 0 ) {
+
+ //
+ // This server's going down. We need to process this
+ // message in the FSP. Copy the indicated data and
+ // process in the FSP.
+ //
+
+ pNpScb->State = SCB_STATE_ATTACHING;
+ AcceptPacket = FALSE;
+ pNpScb->OkToReceive = FALSE;
+ pNpScb->Receiving = TRUE;
+
+ CopyIndicatedData(
+ pIrpC,
+ RspData,
+ BytesIndicated,
+ BytesTaken,
+ ReceiveDatagramFlags );
+
+ pIrpC->PostProcessRoutine = FspProcessServerDown;
+ Status = NwPostToFsp( pIrpC, FALSE );
+
+ break;
+ }
+
+ } else if ( pIrpC->PacketType == NCP_DISCONNECT ) {
+
+ //
+ // We have received a disconnect frame.
+ //
+
+ break;
+ }
+
+ }
+
+process_packet:
+ if ( AcceptPacket ) {
+
+ ASSERT ( !IsListEmpty( &pNpScb->Requests ));
+ ASSERT( pIrpC->pEx != NULL );
+
+
+ //
+ // If we received this packet without a retry, adjust the
+ // send timeout value.
+ //
+
+ if (( !BooleanFlagOn( pIrpC->Flags, IRP_FLAG_RETRY_SEND ) ) &&
+ ( pIrpC->PacketType != NCP_BURST )) {
+
+ SHORT NewTimeout;
+
+ NewTimeout = ( pNpScb->SendTimeout + pNpScb->TickCount ) / 2;
+ pNpScb->SendTimeout = MAX( NewTimeout, pNpScb->TickCount + 1 );
+
+ DebugTrace( 0, Dbg, "Successful exchange, new send timeout = %d\n", pNpScb->SendTimeout );
+ }
+
+ //
+ // If the transport didn't indicate all of the data, we'll need
+ // to post a receive IRP.
+ //
+
+#ifdef NWDBG
+ if (( BytesIndicated < BytesAvailable ) ||
+ ( AlwaysAllocateIrp )){
+#else
+ if ( BytesIndicated < BytesAvailable ) {
+#endif
+
+ if ( ( BooleanFlagOn( pIrpC->Flags, IRP_FLAG_BURST_REQUEST ) ) &&
+ ( IsListEmpty( &pIrpC->Specific.Read.PacketList ) ) ) {
+
+ pBurstRsp = (PNCP_BURST_READ_RESPONSE)RspData;
+ BurstStatus = NwBurstResultToNtStatus( pBurstRsp->Result );
+
+ //
+ // If this entire burst failed with an error, we can't
+ // let the receive data routine signal the caller until
+ // the pEx gets called and we exit on the correct paths.
+ //
+
+ if ( !NT_SUCCESS( BurstStatus ) ) {
+
+ DebugTrace( 0, Dbg, "Special burst termination %08lx.\n", BurstStatus );
+ pIrpC->Specific.Read.Status = BurstStatus;
+
+ if ( pNpScb->Sending ) {
+
+ //
+ // If the send hasn't completed yet, we can't accept
+ // the packet because IPX may not have completed back
+ // to us yet!
+ //
+
+ KeReleaseSpinLockFromDpcLevel(&pNpScb->NpScbSpinLock );
+ DebugTrace(-1, Dbg, "ServerDatagramHandler -> STATUS_DATA_NOT_ACCEPTED (%08lx)\n", BurstStatus );
+ return( STATUS_DATA_NOT_ACCEPTED );
+
+ } else {
+
+ //
+ // Handle this one just like normal, except that we
+ // know it's going to fail in the receive data routine
+ // and we don't want the timeout routine to fire
+ // causing us all sort of grief, so we set OkToReceive
+ // to FALSE.
+ //
+
+ pNpScb->OkToReceive = FALSE;
+ }
+ }
+
+ }
+
+ FreeReceiveIrp( pIrpC ); // Free old Irp if one was allocated
+
+ Status = AllocateReceiveIrp(
+ pIrpC,
+ RspData,
+ BytesAvailable,
+ BytesTaken,
+ pTdiStruct );
+
+ if (Status == STATUS_MORE_PROCESSING_REQUIRED) {
+
+ pNpScb->OkToReceive = FALSE;
+ pNpScb->Receiving = TRUE;
+
+ } else if (!NT_SUCCESS( Status ) ) {
+
+ pIrpC->ReceiveIrp = NULL;
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+
+ }
+
+ KeReleaseSpinLockFromDpcLevel(&pNpScb->NpScbSpinLock );
+
+ *IoRequestPacket = pIrpC->ReceiveIrp;
+
+ } else {
+
+ pNpScb->OkToReceive = FALSE;
+
+ //
+ // The transport has indicated all of the data.
+ // If the send has completed, call the pEx routine,
+ // otherwise copy the data to a buffer and let the
+ // send completion routine call the pEx routine.
+ //
+
+ if ( pNpScb->Sending ) {
+ DebugTrace( 0, Dbg, "Received data before send completion\n", 0 );
+
+ Status = CopyIndicatedData(
+ pIrpC,
+ RspData,
+ BytesIndicated,
+ BytesTaken,
+ ReceiveDatagramFlags );
+
+ if (NT_SUCCESS(Status)) {
+ pNpScb->Received = TRUE;
+ pNpScb->Receiving = TRUE;
+ } else {
+ // Ignore this packet
+ pNpScb->OkToReceive = TRUE;
+ }
+
+ KeReleaseSpinLockFromDpcLevel(&pNpScb->NpScbSpinLock );
+
+ } else {
+ pNpScb->Receiving = FALSE;
+ pNpScb->Received = FALSE;
+
+ KeReleaseSpinLockFromDpcLevel(&pNpScb->NpScbSpinLock );
+
+ DebugTrace(+0, Dbg, "Call pIrpC->pEx %x\n", pIrpC->pEx );
+
+ Status = pIrpC->pEx(pIrpC,
+ BytesAvailable,
+ RspData);
+ }
+
+ *BytesTaken = BytesAvailable;
+
+ }
+
+ } else {
+
+ KeReleaseSpinLockFromDpcLevel(&pNpScb->NpScbSpinLock );
+ Status = STATUS_DATA_NOT_ACCEPTED;
+
+ }
+
+ Stats.NcpsReceived.QuadPart++;
+ Stats.BytesReceived.QuadPart += BytesAvailable;
+
+ DebugTrace(-1, Dbg, "ServerDatagramHandler -> %08lx\n", Status );
+ return( Status );
+
+} // ServerDatagramHandler
+
+NTSTATUS
+CopyIndicatedData(
+ PIRP_CONTEXT pIrpContext,
+ PCHAR ReceiveData,
+ ULONG BytesIndicated,
+ PULONG BytesAccepted,
+ ULONG ReceiveDatagramFlags
+ )
+/*++
+
+Routine Description:
+
+ This routine copies indicated data to a buffer. If the packet is small
+ enough the data is copied to the preallocated receive buffer in the
+ IRP context. If the packet is too long, a new buffer is allocated.
+
+Arguments:
+
+ pIrpContext - A pointer the block of context information for the request
+ in progress.
+
+ ReceiveData - A pointer to the indicated data.
+
+ BytesIndicated - The number of bytes available in the received packet.
+
+ BytesAccepted - Returns the number of bytes accepted by the receive
+ routine.
+
+ ReceiveDatagramFlags - Receive flags given to us by the transport.
+
+Return Value:
+
+ NTSTATUS - Status of receive operation
+
+--*/
+{
+ NTSTATUS Status;
+ PMDL ReceiveMdl;
+ PVOID MappedVa;
+ ULONG BytesToCopy;
+ BOOLEAN DeleteMdl = FALSE;
+
+ pIrpContext->ResponseLength = BytesIndicated;
+
+ //
+ // If there is a receive data routine, use it to generate the receive
+ // MDL, otherwise use the default MDL.
+ //
+
+ if ( pIrpContext->ReceiveDataRoutine != NULL ) {
+
+ Status = pIrpContext->ReceiveDataRoutine(
+ pIrpContext,
+ BytesIndicated,
+ BytesAccepted,
+ ReceiveData,
+ &ReceiveMdl );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return( Status );
+ }
+
+ //
+ // We can accept up to the size of a burst read header, plus
+ // 3 bytes of fluff for the unaligned read case.
+ //
+
+ ASSERT( *BytesAccepted <= sizeof(NCP_BURST_READ_RESPONSE) + 3 );
+
+ BytesIndicated -= *BytesAccepted;
+ ReceiveData += *BytesAccepted;
+
+ DeleteMdl = TRUE;
+
+ } else {
+
+ *BytesAccepted = 0;
+ ReceiveMdl = pIrpContext->RxMdl;
+
+ }
+
+ if ( ReceiveMdl != NULL ) {
+
+ while ( BytesIndicated > 0 && ReceiveMdl != NULL ) {
+
+ MappedVa = MmGetSystemAddressForMdl( ReceiveMdl );
+ BytesToCopy = MIN( MmGetMdlByteCount( ReceiveMdl ), BytesIndicated );
+ TdiCopyLookaheadData( MappedVa, ReceiveData, BytesToCopy, ReceiveDatagramFlags );
+
+ ReceiveMdl = ReceiveMdl->Next;
+ BytesIndicated -= BytesToCopy;
+ ReceiveData += BytesToCopy;
+
+ ASSERT( !( BytesIndicated != 0 && ReceiveMdl == NULL ) );
+ }
+
+ if (DeleteMdl) {
+
+ PMDL Mdl = pIrpContext->Specific.Read.PartialMdl;
+ PMDL NextMdl;
+
+ while ( Mdl != NULL ) {
+ NextMdl = Mdl->Next;
+ DebugTrace( 0, Dbg, "Freeing MDL %x\n", Mdl );
+ FREE_MDL( Mdl );
+ Mdl = NextMdl;
+ }
+
+ pIrpContext->Specific.Read.PartialMdl = NULL;
+ }
+ }
+
+ return( STATUS_SUCCESS );
+}
+
+NTSTATUS
+AllocateReceiveIrp(
+ PIRP_CONTEXT pIrpContext,
+ PVOID ReceiveData,
+ ULONG BytesAvailable,
+ PULONG BytesAccepted,
+ PNW_TDI_STRUCT pTdiStruct
+ )
+/*++
+
+Routine Description:
+
+ This routine allocates an IRP and if necessary a receive buffer. It
+ then builds an MDL for the buffer and formats the IRP to do a TDI
+ receive.
+
+ BUGBUG - Consider preallocating and queueing for efficiency.
+
+Arguments:
+
+ pIrpContext - A pointer the block of context information for the request
+ in progress.
+
+ ReceiveData - The indicated data.
+
+ BytesAvailable - The number of bytes available in the received packet.
+
+ BytesAccepted - Returns the number of bytes accepted from the packet.
+
+ pTdiStruct - A pointer to the TdiStruct which has indicated the receive.
+
+Return Value:
+
+ NTSTATUS - Status of receive operation
+ STATUS_MORE_PROCESSING_REQUIRED means we were successful.
+
+--*/
+{
+ PIRP Irp = NULL;
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ ASSERT( pTdiStruct != NULL );
+
+ Irp = ALLOCATE_IRP( pIrpContext->pNpScb->Server.pDeviceObject->StackSize, FALSE );
+
+ if ( Irp == NULL ) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto CleanExit;
+ }
+
+ //
+ // If there is no receive data routine for this IRP, the
+ // RxMdl must point to a valid place to put the data.
+ //
+ // If there is a ReceiveDataRoutine it will build an MDL
+ //
+
+ if ( pIrpContext->ReceiveDataRoutine == NULL ) {
+
+ ULONG LengthOfMdl;
+
+ LengthOfMdl = MdlLength( pIrpContext->RxMdl );
+
+ //
+ // If the server sent more data than we can receive, simply
+ // ignore the excess. In particular 3.11 pads long name
+ // response with an excess of junk.
+ //
+
+ if ( BytesAvailable > LengthOfMdl ) {
+ BytesAvailable = LengthOfMdl;
+ }
+
+ Irp->MdlAddress = pIrpContext->RxMdl;
+ *BytesAccepted = 0;
+
+ } else {
+
+ Status = pIrpContext->ReceiveDataRoutine(
+ pIrpContext,
+ BytesAvailable,
+ BytesAccepted,
+ ReceiveData,
+ &Irp->MdlAddress );
+
+ if ( !NT_SUCCESS( Status ) ||
+ Irp->MdlAddress == NULL ) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto CleanExit;
+
+ }
+
+ SetFlag( pIrpContext->Flags, IRP_FLAG_FREE_RECEIVE_MDL );
+
+ }
+
+CleanExit:
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ if ( Irp != NULL ) {
+ FREE_IRP( Irp );
+ }
+
+ Irp = NULL;
+ pIrpContext->ReceiveIrp = NULL;
+ Status = STATUS_DATA_NOT_ACCEPTED;
+ return( Status );
+ }
+
+ pIrpContext->ReceiveIrp = Irp;
+ Status = STATUS_MORE_PROCESSING_REQUIRED;
+
+ pIrpContext->ResponseLength = BytesAvailable;
+
+ TdiBuildReceive(
+ Irp,
+ pTdiStruct->pDeviceObject,
+ pTdiStruct->pFileObject,
+ ReceiveIrpCompletion,
+ pIrpContext,
+ Irp->MdlAddress,
+ 0,
+ BytesAvailable - *BytesAccepted );
+
+ IoSetNextIrpStackLocation( Irp );
+
+ return( Status );
+}
+
+NTSTATUS
+ReceiveIrpCompletion(
+ PDEVICE_OBJECT DeviceObject,
+ PIRP Irp,
+ PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ This routine is called when a recieve IRP completes.
+
+Arguments:
+
+ DeviceObject - Unused.
+
+ Irp - The IRP that completed.
+
+ Context - A pointer the block of context information for the request
+ in progress.
+
+
+Return Value:
+
+ NTSTATUS - Status of receive operation
+
+--*/
+{
+ PIRP_CONTEXT IrpContext = (PIRP_CONTEXT)Context;
+ PIO_STACK_LOCATION IrpSp;
+ PNONPAGED_SCB pNpScb;
+ PMDL Mdl, NextMdl;
+ KIRQL OldIrql;
+
+ ASSERT( Irp == IrpContext->ReceiveIrp );
+
+ pNpScb = IrpContext->pNpScb;
+ IrpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ //
+ // Free the IRP MDL if we allocated one specifically for this IRP.
+ //
+
+ if ( BooleanFlagOn( IrpContext->Flags, IRP_FLAG_FREE_RECEIVE_MDL ) ) {
+
+ Mdl = IrpContext->Specific.Read.PartialMdl;
+ IrpContext->Specific.Read.PartialMdl = NULL;
+
+ while ( Mdl != NULL ) {
+ NextMdl = Mdl->Next;
+ DebugTrace( 0, Dbg, "Freeing MDL %x\n", Mdl );
+ FREE_MDL( Mdl );
+ Mdl = NextMdl;
+ }
+
+ }
+
+ if ( !NT_SUCCESS( Irp->IoStatus.Status ) ) {
+
+ //
+ // Failed to receive the data. Wait for more.
+ //
+
+ pNpScb->OkToReceive = TRUE;
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+ }
+
+ //
+ // If the send has completed, call the pEx routine,
+ // otherwise copy the data to a buffer and let the
+ // send completion routine call the pEx routine.
+ //
+
+ KeAcquireSpinLock( &pNpScb->NpScbSpinLock, &OldIrql );
+
+ if ( pNpScb->Sending ) {
+ DebugTrace( 0, Dbg, "Received data before send completion\n", 0 );
+
+ //
+ // Tell send completion to call pEx.
+ //
+
+ pNpScb->Received = TRUE;
+ KeReleaseSpinLock(&pNpScb->NpScbSpinLock, OldIrql );
+
+ } else {
+ pNpScb->Receiving = FALSE;
+ pNpScb->Received = FALSE;
+
+ KeReleaseSpinLock( &pNpScb->NpScbSpinLock, OldIrql );
+ DebugTrace(+0, Dbg, "Call pIrpC->pEx %x\n", IrpContext->pEx );
+ IrpContext->pEx(
+ IrpContext,
+ IrpContext->ResponseLength,
+ IrpContext->rsp );
+
+ }
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+}
+
+VOID
+FreeReceiveIrp(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine frees a IRP that was allocated to do a receive.
+
+Arguments:
+
+ IrpContext - A pointer the block of context information for the request
+ in progress.
+
+
+Return Value:
+
+ NTSTATUS - Status of receive operation
+
+--*/
+{
+ if ( IrpContext->ReceiveIrp == NULL ) {
+ return;
+ }
+
+ FREE_IRP( IrpContext->ReceiveIrp );
+ IrpContext->ReceiveIrp = NULL;
+}
+
+
+NTSTATUS
+WatchDogDatagramHandler(
+ IN PVOID TdiEventContext,
+ IN int SourceAddressLength,
+ IN PVOID SourceAddress,
+ IN int OptionsLength,
+ IN PVOID Options,
+ IN ULONG ReceiveDatagramFlags,
+ IN ULONG BytesIndicated,
+ IN ULONG BytesAvailable,
+ OUT ULONG *BytesTaken,
+ IN PVOID Tsdu,
+ OUT PIRP *IoRequestPacket
+ )
+/*++
+
+Routine Description:
+
+ This routine is the receive datagram event indication handler for the
+ Server socket.
+
+Arguments:
+
+ TdiEventContext - Context provided for this event, a pointer to the
+ non paged SCB.
+
+ SourceAddressLength - Length of the originator of the datagram.
+
+ SourceAddress - String describing the originator of the datagram.
+
+ OptionsLength - Length of the buffer pointed to by Options.
+
+ Options - Options for the receive.
+
+ ReceiveDatagramFlags - Ignored.
+
+ BytesIndicated - Number of bytes this indication.
+
+ BytesAvailable - Number of bytes in complete Tsdu.
+
+ BytesTaken - Returns the number of bytes used.
+
+ Tsdu - Pointer describing this TSDU, typically a lump of bytes.
+
+ IoRequestPacket - TdiReceive IRP if MORE_PROCESSING_REQUIRED.
+
+Return Value:
+
+ NTSTATUS - Status of receive operation
+
+--*/
+{
+ PNONPAGED_SCB pNpScb = (PNONPAGED_SCB)TdiEventContext;
+ PUCHAR RspData = (PUCHAR)Tsdu;
+
+ *IoRequestPacket = NULL;
+
+
+ //
+ // Transport will complete the processing of the request, we don't
+ // want the datagram.
+ //
+
+
+ DebugTrace(+1, Dbg, "WatchDogDatagramHandler\n", 0);
+ DebugTrace(+0, Dbg, "SourceAddressLength %x\n", SourceAddressLength);
+ DebugTrace(+0, Dbg, "BytesIndicated %x\n", BytesIndicated);
+ DebugTrace(+0, Dbg, "BytesAvailable %x\n", BytesAvailable);
+ DebugTrace(+0, Dbg, "BytesTaken %x\n", *BytesTaken);
+ //
+ // SourceAddress is the address of the server or the bridge tbat sent
+ // the packet.
+ //
+
+#if NWDBG
+ dump( Dbg, SourceAddress, SourceAddressLength );
+ dump( Dbg, Tsdu, BytesIndicated );
+#endif
+
+ if (pNpScb->NodeTypeCode != NW_NTC_SCBNP ) {
+ DebugTrace(+0, 0, "nwrdr: Invalid Watchdog Indication %x\n", pNpScb );
+#if DBG
+ DbgBreakPoint();
+#endif
+ return STATUS_DATA_NOT_ACCEPTED;
+ }
+
+ Stats.NcpsReceived.QuadPart++;
+ Stats.BytesReceived.QuadPart += BytesAvailable;
+
+ if ( RspData[1] == NCP_SEARCH_CONTINUE ) {
+ PIRP pIrp;
+ PIRP_CONTEXT pIrpContext;
+
+ pIrp = ALLOCATE_IRP( pNpScb->WatchDog.pDeviceObject->StackSize, FALSE);
+ if (pIrp == NULL) {
+ DebugTrace(-1, Dbg, " %lx\n", STATUS_DATA_NOT_ACCEPTED);
+ return STATUS_DATA_NOT_ACCEPTED;
+ }
+
+ try {
+ pIrpContext = AllocateIrpContext( pIrp );
+ } except( EXCEPTION_EXECUTE_HANDLER ) {
+ FREE_IRP( pIrp );
+ DebugTrace(-1, Dbg, " %lx\n", STATUS_DATA_NOT_ACCEPTED);
+ return STATUS_DATA_NOT_ACCEPTED;
+ }
+
+
+ pIrpContext->req[0] = pNpScb->ConnectionNo;
+
+ //
+ // Response 'Y' or connection is valid and its from the right server,
+ // or 'N' if it is not.
+ //
+
+ if (( RspData[0] == pNpScb->ConnectionNo ) &&
+ ( RtlCompareMemory(
+ ((PTA_IPX_ADDRESS)SourceAddress)->Address[0].Address,
+ &pNpScb->ServerAddress,
+ 8) == 8 ))
+ {
+ LARGE_INTEGER KillTime, Now;
+ BOOL ScbIsOld ;
+
+ //
+ // Check if this is a not-logged-in SCB that has not been used
+ // for while. If it is, answer NO. In attach.c, we dont disconnect
+ // from a nearest server immediately to avoid the re-connect
+ // overheads. This is where we time the sucker out.
+ //
+
+ KeQuerySystemTime( &Now );
+ KillTime.QuadPart = Now.QuadPart - ( NwOneSecond * DORMANT_SCB_KEEP_TIME);
+
+ ScbIsOld = ((pNpScb->State == SCB_STATE_LOGIN_REQUIRED) &&
+ (pNpScb->LastUsedTime.QuadPart < KillTime.QuadPart)) ;
+
+
+ pIrpContext->req[1] = ScbIsOld ? 'N' : 'Y';
+
+ if (ScbIsOld)
+ {
+ pNpScb->State = SCB_STATE_RECONNECT_REQUIRED ;
+ }
+
+ DebugTrace(-1,Dbg,"WatchDog Response: %s\n", ScbIsOld ? "N" : "Y");
+
+ } else {
+
+ pIrpContext->req[1] = 'N';
+ }
+
+ pIrpContext->TxMdl->ByteCount = 2;
+
+ pIrpContext->ConnectionInformation.UserDataLength = 0;
+ pIrpContext->ConnectionInformation.OptionsLength = sizeof( UCHAR );
+ pIrpContext->ConnectionInformation.Options = &SapPacketType;
+ pIrpContext->ConnectionInformation.RemoteAddressLength = sizeof(TA_IPX_ADDRESS);
+ pIrpContext->ConnectionInformation.RemoteAddress = &pIrpContext->Destination;
+
+ BuildIpxAddress(
+ ((PTA_IPX_ADDRESS)SourceAddress)->Address[0].Address[0].NetworkAddress,
+ ((PTA_IPX_ADDRESS)SourceAddress)->Address[0].Address[0].NodeAddress,
+ ((PTA_IPX_ADDRESS)SourceAddress)->Address[0].Address[0].Socket,
+ &pIrpContext->Destination);
+
+ TdiBuildSendDatagram(
+ pIrpContext->pOriginalIrp,
+ pNpScb->WatchDog.pDeviceObject,
+ pNpScb->WatchDog.pFileObject,
+ &CompletionWatchDogSend,
+ pIrpContext,
+ pIrpContext->TxMdl,
+ MdlLength(pIrpContext->TxMdl),
+ &pIrpContext->ConnectionInformation);
+
+ IoCallDriver(
+ pNpScb->WatchDog.pDeviceObject,
+ pIrpContext->pOriginalIrp );
+ }
+
+ DebugTrace(-1, Dbg, " %lx\n", STATUS_DATA_NOT_ACCEPTED);
+ return STATUS_DATA_NOT_ACCEPTED;
+
+ UNREFERENCED_PARAMETER( SourceAddressLength );
+ UNREFERENCED_PARAMETER( BytesIndicated );
+ UNREFERENCED_PARAMETER( BytesAvailable );
+ UNREFERENCED_PARAMETER( BytesTaken );
+ UNREFERENCED_PARAMETER( Tsdu );
+ UNREFERENCED_PARAMETER( OptionsLength );
+ UNREFERENCED_PARAMETER( Options );
+}
+
+
+NTSTATUS
+CompletionWatchDogSend(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ This routine does not complete the Irp. It is used to signal to a
+ synchronous part of the driver that it can proceed.
+
+Arguments:
+
+ DeviceObject - unused.
+
+ Irp - Supplies Irp that the transport has finished processing.
+
+ Context - Supplies the IrpContext associated with the Irp.
+
+Return Value:
+
+ The STATUS_MORE_PROCESSING_REQUIRED so that the IO system stops
+ processing Irp stack locations at this point.
+
+--*/
+{
+
+ PIRP_CONTEXT pIrpC = (PIRP_CONTEXT) Context;
+
+ //
+ // Avoid completing the Irp because the Mdl etc. do not contain
+ // their original values.
+ //
+
+ DebugTrace( +1, Dbg, "CompletionWatchDogSend\n", 0);
+ DebugTrace( +0, Dbg, "Irp %X\n", Irp);
+ DebugTrace( -1, Dbg, "pIrpC %X\n", pIrpC);
+
+ FREE_IRP( pIrpC->pOriginalIrp );
+
+ pIrpC->pOriginalIrp = NULL; // Avoid FreeIrpContext modifying freed Irp.
+
+ FreeIrpContext( pIrpC );
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+ UNREFERENCED_PARAMETER( DeviceObject );
+ UNREFERENCED_PARAMETER( Irp );
+}
+
+
+NTSTATUS
+SendDatagramHandler(
+ IN PVOID TdiEventContext,
+ IN int SourceAddressLength,
+ IN PVOID SourceAddress,
+ IN int OptionsLength,
+ IN PVOID Options,
+ IN ULONG ReceiveDatagramFlags,
+ IN ULONG BytesIndicated,
+ IN ULONG BytesAvailable,
+ OUT ULONG *BytesTaken,
+ IN PVOID Tsdu,
+ OUT PIRP *IoRequestPacket
+ )
+/*++
+
+Routine Description:
+
+ This routine is the receive datagram event indication handler for the
+ Server socket.
+
+Arguments:
+
+ TdiEventContext - Context provided for this event, a pointer to the
+ non paged SCB.
+
+ SourceAddressLength - Length of the originator of the datagram.
+
+ SourceAddress - String describing the originator of the datagram.
+
+ OptionsLength - Length of the buffer pointed to by Options.
+
+ Options - Options for the receive.
+
+ ReceiveDatagramFlags - Ignored.
+
+ BytesIndicated - Number of bytes this indication.
+
+ BytesAvailable - Number of bytes in complete Tsdu.
+
+ BytesTaken - Returns the number of bytes used.
+
+ Tsdu - Pointer describing this TSDU, typically a lump of bytes.
+
+ IoRequestPacket - TdiReceive IRP if MORE_PROCESSING_REQUIRED.
+
+Return Value:
+
+ NTSTATUS - Status of receive operation
+
+--*/
+
+{
+ PNONPAGED_SCB pNpScb = (PNONPAGED_SCB)TdiEventContext;
+ PUCHAR RspData = (PUCHAR)Tsdu;
+ PIRP_CONTEXT pIrpContext;
+ PLIST_ENTRY listEntry;
+ PIRP Irp;
+
+ *IoRequestPacket = NULL;
+
+ DebugTrace(0, Dbg, "SendDatagramHandler\n", 0);
+
+ Stats.NcpsReceived.QuadPart++;
+ Stats.BytesReceived.QuadPart += BytesAvailable;
+
+ //
+ // Transport will complete the processing of the request, we don't
+ // want the datagram.
+ //
+
+ DebugTrace(+1, Dbg, "SendDatagramHandler\n", 0);
+ DebugTrace(+0, Dbg, "SourceAddressLength %x\n", SourceAddressLength);
+ DebugTrace(+0, Dbg, "BytesIndicated %x\n", BytesIndicated);
+ DebugTrace(+0, Dbg, "BytesAvailable %x\n", BytesAvailable);
+ DebugTrace(+0, Dbg, "BytesTaken %x\n", *BytesTaken);
+
+ //
+ // SourceAddress is the address of the server or the bridge tbat sent
+ // the packet.
+ //
+
+#if NWDBG
+ dump( Dbg, SourceAddress, SourceAddressLength );
+ dump( Dbg, Tsdu, BytesIndicated );
+#endif
+
+ if (pNpScb->NodeTypeCode != NW_NTC_SCBNP ) {
+ DebugTrace(+0, Dbg, "nwrdr: Invalid SendDatagram Indication %x\n", pNpScb );
+#if DBG
+ DbgBreakPoint();
+#endif
+ return STATUS_DATA_NOT_ACCEPTED;
+ }
+
+ if (RspData[1] == BROADCAST_MESSAGE_WAITING ) {
+
+ //
+ // Broadcast message waiting. If the scavenger
+ // isn't running, it's safe to go get it.
+ //
+
+ KeAcquireSpinLockAtDpcLevel( &NwScavengerSpinLock );
+
+ if ( WorkerRunning ) {
+
+ //
+ // The scavenger is running, we can't pick up this
+ // message until the scavenger is done!
+ //
+
+ DebugTrace( 0, DEBUG_TRACE_ALWAYS, "Delaying get message for scavenger.\n", 0 );
+ KeReleaseSpinLockFromDpcLevel( &NwScavengerSpinLock );
+
+ } else {
+
+ //
+ // Make sure the scavenger doesn't start.
+ //
+
+ WorkerRunning = TRUE;
+ KeReleaseSpinLockFromDpcLevel( &NwScavengerSpinLock );
+
+ listEntry = ExInterlockedRemoveHeadList(
+ &NwGetMessageList,
+ &NwMessageSpinLock );
+
+ if ( listEntry != NULL ) {
+
+ pIrpContext = CONTAINING_RECORD( listEntry, IRP_CONTEXT, NextRequest );
+
+ //
+ // Clear the cancel routine for this IRP.
+ //
+
+ Irp = pIrpContext->pOriginalIrp;
+
+ IoAcquireCancelSpinLock( &Irp->CancelIrql );
+ IoSetCancelRoutine( Irp, NULL );
+ IoReleaseCancelSpinLock( Irp->CancelIrql );
+
+ pIrpContext->PostProcessRoutine = FspGetMessage;
+ pIrpContext->pNpScb = pNpScb;
+ pIrpContext->pScb = pNpScb->pScb;
+
+ NwPostToFsp( pIrpContext, TRUE );
+
+ } else {
+
+ WorkerRunning = FALSE;
+ }
+ }
+
+ }
+
+ DebugTrace(-1, Dbg, " %lx\n", STATUS_DATA_NOT_ACCEPTED);
+ return STATUS_DATA_NOT_ACCEPTED;
+
+ UNREFERENCED_PARAMETER( SourceAddressLength );
+ UNREFERENCED_PARAMETER( BytesIndicated );
+ UNREFERENCED_PARAMETER( BytesAvailable );
+ UNREFERENCED_PARAMETER( BytesTaken );
+ UNREFERENCED_PARAMETER( Tsdu );
+ UNREFERENCED_PARAMETER( OptionsLength );
+ UNREFERENCED_PARAMETER( Options );
+}
+
+
+NTSTATUS
+FspGetMessage(
+ IN PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine continues process a broadcast message waiting message.
+
+Arguments:
+
+ pIrpContext - A pointer to the IRP context information for the
+ request in progress.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+{
+ KIRQL OldIrql;
+ PLIST_ENTRY ScbQueueEntry;
+ PNONPAGED_SCB pNpScb;
+
+ UNICODE_STRING Message;
+ NTSTATUS Status;
+ PNWR_SERVER_MESSAGE ServerMessage;
+ PUNICODE_STRING ServerName;
+ ULONG MessageLength;
+ int i;
+
+ PAGED_CODE();
+
+ NwReferenceUnlockableCodeSection();
+
+ //
+ // The Scb may be being deleted so carefully walk the list and reference it if
+ // we find it.
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ ScbQueueEntry = ScbQueue.Flink;
+
+ while ( ScbQueueEntry != &ScbQueue ) {
+
+ pNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
+
+ if (pNpScb == IrpContext->pNpScb ) {
+
+ NwReferenceScb( pNpScb );
+
+ break;
+ }
+
+ ScbQueueEntry = ScbQueueEntry->Flink;
+ }
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+ if (pNpScb != IrpContext->pNpScb ) {
+
+ //
+ // Server deleted. Its easiest to continue processing the IrpContext
+ // with an error than try to recover it and return it to the queue.
+ //
+
+ Status = STATUS_UNSUCCESSFUL;
+ NwDereferenceUnlockableCodeSection();
+
+ //
+ // Re-enable the scavenger before we return!
+ //
+
+ WorkerRunning = FALSE;
+
+ return( Status );
+ }
+
+ //
+ // If the message is telling us that the server is going down then don't
+ // work too hard trying to get the message. The server is persistent with
+ // respect to other messages so we'll come through here again when the
+ // problem has been resolved.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_REROUTE_ATTEMPTED );
+
+ if ( UP_LEVEL_SERVER( IrpContext->pScb ) ) {
+ Status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "S",
+ NCP_MESSAGE_FUNCTION, NCP_GET_ENTIRE_MESSAGE );
+ } else {
+ Status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "S",
+ NCP_MESSAGE_FUNCTION, NCP_GET_MESSAGE );
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NwDereferenceScb( pNpScb );
+ NwDereferenceUnlockableCodeSection();
+
+ //
+ // Re-enable the scavenger before we return!
+ //
+
+ WorkerRunning = FALSE;
+
+ return( Status );
+ }
+
+ ServerMessage = (PNWR_SERVER_MESSAGE)IrpContext->Specific.FileSystemControl.Buffer;
+ MessageLength = IrpContext->Specific.FileSystemControl.Length;
+
+ ServerName = &IrpContext->pNpScb->ServerName;
+ if ( ServerName->Length + FIELD_OFFSET( NWR_SERVER_MESSAGE, Server ) + sizeof(WCHAR) > MessageLength ) {
+
+ Status = STATUS_BUFFER_TOO_SMALL;
+ NwDereferenceScb( pNpScb );
+ NwDereferenceUnlockableCodeSection();
+
+ //
+ // Re-enable the scavenger before we return!
+ //
+
+ WorkerRunning = FALSE;
+
+ return( Status );
+
+ } else {
+
+ //
+ // Copy the server name to the output buffer.
+ //
+
+ ServerMessage->MessageOffset =
+ ServerName->Length +
+ FIELD_OFFSET( NWR_SERVER_MESSAGE, Server ) +
+ sizeof(WCHAR);
+
+ RtlMoveMemory(
+ ServerMessage->Server,
+ ServerName->Buffer,
+ ServerName->Length );
+
+ ServerMessage->Server[ ServerName->Length / sizeof(WCHAR) ] = L'\0';
+ }
+
+ //
+ // Copy the message to the user's buffer.
+ //
+
+ Message.Buffer = &ServerMessage->Server[ ServerName->Length / sizeof(WCHAR) ] + 1;
+ Message.MaximumLength = (USHORT)( MessageLength - ( ServerName->Length + FIELD_OFFSET( NWR_SERVER_MESSAGE, Server ) + sizeof(WCHAR) ) );
+
+ if ( NT_SUCCESS( Status) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "NP",
+ &Message );
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NwDereferenceScb( pNpScb );
+ NwDereferenceUnlockableCodeSection();
+
+ //
+ // Re-enable the scavenger before we return!
+ //
+
+ WorkerRunning = FALSE;
+
+ return( Status );
+ }
+
+ //
+ // Strip the trailing spaces and append a NUL terminator to the message.
+ //
+
+ for ( i = Message.Length / sizeof(WCHAR) - 1; i >= 0 ; i-- ) {
+ if ( Message.Buffer[ i ] != L' ') {
+ Message.Length = (i + 1) * sizeof(WCHAR);
+ break;
+ }
+ }
+
+ if ( Message.Length > 0 ) {
+ Message.Buffer[ Message.Length / sizeof(WCHAR) ] = L'\0';
+ }
+
+ IrpContext->pOriginalIrp->IoStatus.Information =
+ ServerName->Length +
+ FIELD_OFFSET( NWR_SERVER_MESSAGE, Server ) + sizeof(WCHAR) +
+ Message.Length + sizeof(WCHAR);
+
+ NwDereferenceScb( pNpScb );
+ NwDereferenceUnlockableCodeSection();
+
+ //
+ // Re-enable the scavenger before we return!
+ //
+
+ WorkerRunning = FALSE;
+
+ return( Status );
+}
+
+
+NTSTATUS
+_cdecl
+ExchangeWithWait(
+ PIRP_CONTEXT pIrpContext,
+ PEX pEx,
+ char* f,
+ ... // format specific parameters
+ )
+/*++
+
+Routine Description:
+
+ This routine sends a NCP packet and waits for the response.
+
+Arguments:
+
+ pIrpContext - A pointer to the context information for this IRP.
+
+ pEX, Context, f - See _Exchange
+
+Return Value:
+
+ NTSTATUS - Status of the operation.
+
+--*/
+
+{
+ NTSTATUS Status;
+ va_list Arguments;
+
+ PAGED_CODE();
+
+ //KeResetEvent( &pIrpContext->Event );
+
+ va_start( Arguments, f );
+
+ Status = FormatRequest( pIrpContext, pEx, f, Arguments );
+ if ( !NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ va_end( Arguments );
+
+ Status = PrepareAndSendPacket( pIrpContext );
+ if ( !NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ Status = KeWaitForSingleObject(
+ &pIrpContext->Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL
+ );
+
+ if ( !NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ Status = pIrpContext->pOriginalIrp->IoStatus.Status;
+
+ if ( NT_SUCCESS( Status ) &&
+ pIrpContext->PacketType != SAP_BROADCAST ) {
+ Status = NwErrorToNtStatus( pIrpContext->ResponseParameters.Error );
+ }
+
+ return( Status );
+}
+
+BOOLEAN
+VerifyResponse(
+ PIRP_CONTEXT pIrpContext,
+ PVOID Response
+ )
+/*++
+
+Routine Description:
+
+ This routine verifies that a received response is the expected
+ response for the current request.
+
+Arguments:
+
+ pIrpContext - A pointer to the context information for this IRP.
+
+ Response - A pointer to the buffer containing the response.
+
+Return Value:
+
+ TRUE - This is a valid response.
+ FALSE - This is an invalid response.
+
+--*/
+
+{
+ PNCP_RESPONSE pNcpResponse;
+ PNONPAGED_SCB pNpScb;
+
+ pNcpResponse = (PNCP_RESPONSE)Response;
+ pNpScb = pIrpContext->pNpScb;
+
+ if ( pNcpResponse->NcpHeader.ConnectionIdLow != pNpScb->ConnectionNo ) {
+ DebugTrace(+0, Dbg, "VerifyResponse, bad connection number\n", 0);
+
+ return( FALSE );
+ }
+
+ if ( pNcpResponse->NcpHeader.SequenceNumber != pNpScb->SequenceNo ) {
+ DebugTrace(+1, Dbg, "VerifyResponse, bad sequence number %x\n", 0);
+ DebugTrace(+0, Dbg, " pNcpResponse->NcpHeader.SequenceNumber %x\n",
+ pNcpResponse->NcpHeader.SequenceNumber);
+ DebugTrace(-1, Dbg, " pNpScb->SequenceNo %x\n", pNpScb->SequenceNo );
+
+ return( FALSE );
+ }
+
+ return( TRUE );
+}
+
+VOID
+ScheduleReconnectRetry(
+ PIRP_CONTEXT pIrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine schedules an a reconnect attempt, and then resubmits
+ our request if the reconnect was successful.
+
+Arguments:
+
+ pIrpContext - A pointer to the context information for this IRP.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PWORK_QUEUE_ITEM WorkItem;
+
+ WorkItem = ALLOCATE_POOL( NonPagedPool, sizeof( WORK_QUEUE_ITEM ) );
+
+ if ( WorkItem == NULL ) {
+ pIrpContext->pEx( pIrpContext, 0, NULL );
+ }
+
+ pIrpContext->pWorkItem = WorkItem;
+ ExInitializeWorkItem( WorkItem, ReconnectRetry, pIrpContext );
+ ExQueueWorkItem( WorkItem, DelayedWorkQueue );
+
+ return;
+}
+
+VOID
+ReconnectRetry(
+ IN PIRP_CONTEXT pIrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine attempts to reconnect to a disconnected server. If it
+ is successful it resubmits an existing request.
+
+Arguments:
+
+ pIrpContext - A pointer to the context information for this IRP.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PIRP_CONTEXT pNewIrpContext;
+ PSCB pScb, pNewScb;
+ PNONPAGED_SCB pNpScb;
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ pNpScb = pIrpContext->pNpScb;
+ pScb = pNpScb->pScb;
+
+ Stats.Reconnects++;
+
+ if ( pScb == NULL ) {
+ pScb = pNpScb->pScb;
+ pIrpContext->pScb = pScb;
+ }
+
+ //
+ // Free the work item
+ //
+
+ FREE_POOL( pIrpContext->pWorkItem );
+
+ //
+ // Allocate a temporary IRP context to use to reconnect to the server
+ //
+
+ if ( !NwAllocateExtraIrpContext( &pNewIrpContext, pNpScb ) ) {
+ pIrpContext->pEx( pIrpContext, 0, NULL );
+ return;
+ }
+
+ pNewIrpContext->Specific.Create.UserUid = pScb->UserUid;
+ pNewIrpContext->pNpScb = pNpScb;
+ pNewIrpContext->pScb = pScb;
+
+ //
+ // Reset the sequence numbers.
+ //
+
+ pNpScb->SequenceNo = 0;
+ pNpScb->BurstSequenceNo = 0;
+ pNpScb->BurstRequestNo = 0;
+
+ //
+ // Now insert this new IrpContext to the head of the SCB queue for
+ // processing. We can get away with this because we own the IRP context
+ // currently at the front of the queue. With the RECONNECT_ATTEMPT
+ // flag set, ConnectScb() will not remove us from the head of the queue.
+ //
+
+ SetFlag( pNewIrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE );
+ SetFlag( pNewIrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT );
+
+ ExInterlockedInsertHeadList(
+ &pNpScb->Requests,
+ &pNewIrpContext->NextRequest,
+ &pNpScb->NpScbSpinLock );
+
+ pNewScb = pNpScb->pScb;
+
+ Status = ConnectScb( &pNewScb,
+ pNewIrpContext,
+ &pNpScb->ServerName,
+ NULL,
+ NULL,
+ NULL,
+ FALSE,
+ FALSE,
+ TRUE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ //
+ // Couldn't reconnect. Free the extra IRP context, complete the
+ // original request with an error.
+ //
+
+ NwDequeueIrpContext( pNewIrpContext, FALSE );
+ NwFreeExtraIrpContext( pNewIrpContext );
+ pIrpContext->pEx( pIrpContext, 0, NULL );
+ return;
+ }
+
+ ASSERT( pNewScb == pScb );
+
+ //
+ // Try to reconnect the VCBs.
+ //
+
+ NwReopenVcbHandlesForScb( pNewIrpContext, pScb );
+
+ //
+ // Dequeue and free the bonus IRP context.
+ //
+
+ NwDequeueIrpContext( pNewIrpContext, FALSE );
+ NwFreeExtraIrpContext( pNewIrpContext );
+
+ //
+ // Resubmit the original request, with a new sequence number. Note that
+ // it's back at the front of the queue, but no longer reconnectable.
+ //
+
+ pIrpContext->req[2] = pNpScb->SequenceNo;
+ pIrpContext->req[3] = pNpScb->ConnectionNo;
+ pIrpContext->req[5] = pNpScb->ConnectionNoHigh;
+
+ PreparePacket( pIrpContext, pIrpContext->pOriginalIrp, pIrpContext->TxMdl );
+ SendNow( pIrpContext );
+
+ return;
+}
+
+
+NTSTATUS
+NewRouteRetry(
+ IN PIRP_CONTEXT pIrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine attempts to establish a new route to a non-responding server.
+ If it is successful it resubmits the request in progress.
+
+Arguments:
+
+ pIrpContext - A pointer to the context information for this IRP.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NTSTATUS Status;
+ PNONPAGED_SCB pNpScb = pIrpContext->pNpScb;
+ LARGE_INTEGER CurrentTime = {0, 0};
+
+ PAGED_CODE();
+
+ //
+ // Don't bother to re-rip if we are shutting down.
+ //
+
+ if ( NwRcb.State != RCB_STATE_SHUTDOWN ) {
+ Status = GetNewRoute( pIrpContext );
+ } else {
+ Status = STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ //
+ // Ask the transport to establish a new route to the server.
+ //
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ //
+ // Attempt to get new route failed, fail the current request.
+ //
+
+ pIrpContext->ResponseParameters.Error = ERROR_UNEXP_NET_ERR;
+ pIrpContext->pEx( pIrpContext, 0, NULL );
+
+ if ( pNpScb != &NwPermanentNpScb ) {
+
+
+ KeQuerySystemTime( &CurrentTime );
+
+ if ( CanLogTimeOutEvent( pNpScb->NwNextEventTime,
+ CurrentTime
+ )) {
+ Error(
+ EVENT_NWRDR_TIMEOUT,
+ STATUS_UNEXPECTED_NETWORK_ERROR,
+ NULL,
+ 0,
+ 1,
+ pNpScb->ServerName.Buffer );
+
+ //
+ // Set the LastEventTime to the CurrentTime
+ //
+
+ UpdateNextEventTime(
+ pNpScb->NwNextEventTime,
+ CurrentTime,
+ TimeOutEventInterval
+ );
+
+ }
+
+
+ pNpScb->State = SCB_STATE_ATTACHING;
+ }
+
+ } else {
+
+ //
+ // Got a new route, resubmit the request. Allow retries
+ // with the new route.
+ //
+
+ pIrpContext->pNpScb->RetryCount = DefaultRetryCount / 2;
+
+ PreparePacket( pIrpContext, pIrpContext->pOriginalIrp, pIrpContext->TxMdl );
+ SendNow( pIrpContext );
+ }
+
+ //
+ // Return STATUS_PENDING so that the FSP dispatcher doesn't complete
+ // this request.
+ //
+
+ return( STATUS_PENDING );
+}
+
+
+NTSTATUS
+NewRouteBurstRetry(
+ IN PIRP_CONTEXT pIrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine attempts to establish a new route to a non-responding server.
+ If it is successful it resubmits the request in progress.
+
+Arguments:
+
+ pIrpContext - A pointer to the context information for this IRP.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NTSTATUS Status;
+ PIRP_CONTEXT pNewIrpContext;
+ PNONPAGED_SCB pNpScb = pIrpContext->pNpScb;
+ BOOLEAN LIPNegotiated ;
+ LARGE_INTEGER CurrentTime = {0, 0};
+
+ PAGED_CODE();
+
+ //
+ // Don't bother to re-rip if we are shutting down.
+ //
+
+ if ( NwRcb.State == RCB_STATE_SHUTDOWN ) {
+ return( STATUS_REMOTE_NOT_LISTENING );
+ }
+
+ //
+ // Ask the transport to establish a new route to the server.
+ //
+
+ Status = GetNewRoute( pIrpContext );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // If this is a burst write, we must first complete the write
+ // request (there is no way to tell the server to abandon the write).
+ //
+ // Set packet size down to 512 to guarantee that the packets will be
+ // forwarded, and resend the burst data. Queue the new IRP context
+ // behind the burst write, so that we can establish a new burst
+ // connection.
+ //
+ // Note that ResubmitBurstWrite may complete the request and
+ // free the IrpContext.
+ //
+
+ pNpScb->RetryCount = DefaultRetryCount / 2;
+
+ if ( BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_BURST_WRITE ) ) {
+
+ Status = ResubmitBurstWrite( pIrpContext );
+
+ } else {
+
+ //
+ // Allocate a temporary IRP context to use to reconnect to the server
+ //
+
+ if ( NT_SUCCESS( Status ) ) {
+ if ( !NwAllocateExtraIrpContext( &pNewIrpContext, pNpScb ) ) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ } else {
+ pNewIrpContext->Specific.Create.UserUid = pIrpContext->Specific.Create.UserUid;
+
+ SetFlag( pNewIrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE );
+ SetFlag( pNewIrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT );
+
+ //
+ // Since we're doing this from a worker thread, we can't
+ // let the dpc timer schedule _another_ worker thread
+ // request if this also times out or we may deadlock
+ // the delayed work queue.
+ //
+
+ SetFlag( pNewIrpContext->Flags, IRP_FLAG_REROUTE_ATTEMPTED );
+
+ pNewIrpContext->pNpScb = pNpScb;
+
+ }
+ }
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // Insert this new IrpContext to the head of
+ // the SCB queue for processing. We can get away with this
+ // because we own the IRP context currently at the front of
+ // the queue.
+ //
+
+ ExInterlockedInsertHeadList(
+ &pNpScb->Requests,
+ &pNewIrpContext->NextRequest,
+ &pNpScb->NpScbSpinLock );
+
+ //
+ // Now prepare to resend the burst read.
+ //
+
+ PreparePacket( pIrpContext, pIrpContext->pOriginalIrp, pIrpContext->TxMdl );
+
+ //
+ // Renegotiate the burst connection, this will automatically re-sync
+ // the burst connection.
+ //
+ // BUGBUG: We lose sizeof( NCP_BURST_WRITE_REQUEST ) each time
+ // we do this right now.
+ //
+
+ NegotiateBurstMode( pNewIrpContext, pNpScb, &LIPNegotiated );
+
+ //
+ // Reset the sequence numbers.
+ //
+
+ pNpScb->BurstSequenceNo = 0;
+ pNpScb->BurstRequestNo = 0;
+
+ //
+ // Dequeue and free the bonus IRP context.
+ //
+
+ ASSERT( pNpScb->Requests.Flink == &pNewIrpContext->NextRequest );
+
+ ExInterlockedRemoveHeadList(
+ &pNpScb->Requests,
+ &pNpScb->NpScbSpinLock );
+
+ ClearFlag( pNewIrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE );
+
+ NwFreeExtraIrpContext( pNewIrpContext );
+
+ //
+ // Got a new route, resubmit the request
+ //
+
+ Status = ResubmitBurstRead( pIrpContext );
+ }
+ }
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ //
+ // Attempt to get new route failed, fail the current request.
+ //
+
+ pIrpContext->ResponseParameters.Error = ERROR_UNEXP_NET_ERR;
+ pIrpContext->pEx( pIrpContext, 0, NULL );
+
+ if ( pNpScb != &NwPermanentNpScb ) {
+
+
+ KeQuerySystemTime( &CurrentTime );
+
+ if ( CanLogTimeOutEvent( pNpScb->NwNextEventTime,
+ CurrentTime
+ )) {
+ Error(
+ EVENT_NWRDR_TIMEOUT,
+ STATUS_UNEXPECTED_NETWORK_ERROR,
+ NULL,
+ 0,
+ 1,
+ pNpScb->ServerName.Buffer );
+
+ //
+ // Set the LastEventTime to the CurrentTime
+ //
+
+ UpdateNextEventTime(
+ pNpScb->NwNextEventTime,
+ CurrentTime,
+ TimeOutEventInterval
+ );
+
+ }
+
+ }
+ }
+
+ //
+ // Return STATUS_PENDING so that the FSP dispatcher doesn't complete
+ // this request.
+ //
+
+ return( STATUS_PENDING );
+}
+
+NTSTATUS
+FspProcessServerDown(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine process a response with the server shutdown bit set.
+ It close all open handles for the server, and puts the server in
+ the attaching state.
+
+Arguments:
+
+ pIrpContext - A pointer to the context information for this IRP.
+
+Return Value:
+
+ STATUS_PENDING.
+
+--*/
+{
+ KIRQL OldIrql;
+
+ PNONPAGED_SCB pNpScb = IrpContext->pNpScb;
+
+ //
+ // Avoid the Scb from disappearing under us.
+ //
+
+ NwReferenceScb( pNpScb );
+
+ //
+ // Move the IrpContext from the front of the queue just in-case it
+ // owns the Rcb.
+ //
+
+ KeAcquireSpinLock( &IrpContext->pNpScb->NpScbSpinLock, &OldIrql );
+
+ if ( IrpContext->pNpScb->Sending ) {
+
+ //
+ // Let send completion call the pEx routine
+ //
+
+ IrpContext->pNpScb->Received = TRUE;
+ KeReleaseSpinLock( &IrpContext->pNpScb->NpScbSpinLock, OldIrql );
+
+ } else {
+
+ IrpContext->pNpScb->Receiving = FALSE;
+ IrpContext->pNpScb->Received = FALSE;
+ KeReleaseSpinLock( &IrpContext->pNpScb->NpScbSpinLock, OldIrql );
+
+ //
+ // Now call the callback routine.
+ //
+
+ IrpContext->pEx(
+ IrpContext,
+ IrpContext->ResponseLength,
+ IrpContext->rsp );
+
+ }
+
+ //
+ // Close all active handles for this server.
+ //
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ NwInvalidateAllHandlesForScb( pNpScb->pScb );
+ NwReleaseRcb( &NwRcb );
+
+ NwDereferenceScb( pNpScb );
+
+ //
+ // Return STATUS_PENDING so that the FSP process doesn't complete
+ // this request.
+ //
+
+ return( STATUS_PENDING );
+}
+
+
+VOID
+NwProcessSendBurstFailure(
+ PNONPAGED_SCB NpScb,
+ USHORT MissingFragmentCount
+ )
+/*++
+
+Routine Description:
+
+ This routine adjust burst parameters after an unsuccessful burst operation.
+
+Arguments:
+
+ NpScb - A pointer to the SCB that has experienced a burst failure.
+
+ MissingFragmentCount - A measure of how many chunks were lost.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ LONG temp;
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "Burst failure, NpScb = %X\n", NpScb );
+
+ if ( NpScb->NwSendDelay != NpScb->CurrentBurstDelay ) {
+
+ //
+ // This burst has already failed
+ //
+
+ return;
+ }
+
+ NpScb->NwBadSendDelay = NpScb->NwSendDelay;
+
+ //
+ // Add to the send delay. Never let it go above 5000ms.
+ //
+
+ temp = NpScb->NwGoodSendDelay - NpScb->NwBadSendDelay;
+
+ if (temp >= 0) {
+ NpScb->NwSendDelay += temp + 2;
+ } else {
+ NpScb->NwSendDelay += -temp + 2;
+ }
+
+ if ( NpScb->NwSendDelay > NpScb->NwMaxSendDelay ) {
+
+ NpScb->NwSendDelay = NpScb->NwMaxSendDelay;
+
+ //
+ // If we have slowed down a lot then it might be that the server or a
+ // bridge only has a small buffer on its NIC. If this is the case then
+ // rather than sending a big burst with long even gaps between the
+ // packets, we should try to send a burst the size of the buffer.
+ //
+
+ if ( !DontShrink ) {
+
+ if (((NpScb->MaxSendSize - 1) / NpScb->MaxPacketSize) > 2 ) {
+
+ // Round down to the next packet
+
+ NpScb->MaxSendSize = ((NpScb->MaxSendSize - 1) / NpScb->MaxPacketSize) * NpScb->MaxPacketSize;
+
+ //
+ // Adjust SendDelay below threshold to see if things improve before
+ // we shrink the size again.
+ //
+
+ NpScb->NwSendDelay = NpScb->NwGoodSendDelay = NpScb->NwBadSendDelay = MinSendDelay;
+
+ } else {
+
+ //
+ // We reached the minimum size with the maximum delay. Give up on burst.
+ //
+
+ NpScb->SendBurstModeEnabled = FALSE;
+
+ }
+
+ }
+ }
+
+ NpScb->NtSendDelay.QuadPart = NpScb->NwSendDelay * -1000 ;
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "New Send Delay = %d\n", NpScb->NwSendDelay );
+
+ NpScb->SendBurstSuccessCount = 0;
+
+}
+
+
+VOID
+NwProcessReceiveBurstFailure(
+ PNONPAGED_SCB NpScb,
+ USHORT MissingFragmentCount
+ )
+/*++
+
+Routine Description:
+
+ This routine adjust burst parameters after an unsuccessful burst operation.
+
+Arguments:
+
+ NpScb - A pointer to the SCB that has experienced a burst failure.
+
+ MissingFragmentCount - A measure of how many chunks were lost.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ LONG temp;
+
+ DebugTrace(+0, DEBUG_TRACE_LIP, "Burst failure, NpScb = %X\n", NpScb );
+
+ if ( NpScb->NwReceiveDelay != NpScb->CurrentBurstDelay ) {
+
+ //
+ // This burst has already failed
+ //
+
+ return;
+ }
+
+ NpScb->NwBadReceiveDelay = NpScb->NwReceiveDelay;
+
+ //
+ // Add to the Receive delay. Never let it go above 5000ms.
+ //
+
+ temp = NpScb->NwGoodReceiveDelay - NpScb->NwBadReceiveDelay;
+
+ if (temp >= 0) {
+ NpScb->NwReceiveDelay += temp + 2;
+ } else {
+ NpScb->NwReceiveDelay += -temp + 2;
+ }
+
+
+ if ( NpScb->NwReceiveDelay > NpScb->NwMaxReceiveDelay ) {
+
+ NpScb->NwReceiveDelay = MaxReceiveDelay;
+
+ //
+ // If we have slowed down a lot then it might be that the server or a
+ // bridge only has a small buffer on its NIC. If this is the case then
+ // rather than Receiveing a big burst with long even gaps between the
+ // packets, we should try to Receive a burst the size of the buffer.
+ //
+
+ if ( !DontShrink ) {
+
+ if (((NpScb->MaxReceiveSize - 1) / NpScb->MaxPacketSize) > 2 ) {
+
+ // Round down to the next packet
+
+ NpScb->MaxReceiveSize = ((NpScb->MaxReceiveSize - 1) / NpScb->MaxPacketSize) * NpScb->MaxPacketSize;
+
+ //
+ // Adjust ReceiveDelay below threshold to see if things improve before
+ // we shrink the size again.
+ //
+
+ NpScb->NwReceiveDelay = NpScb->NwGoodReceiveDelay = NpScb->NwBadReceiveDelay = MinReceiveDelay;
+
+ } else {
+
+ //
+ // We reached the minimum size with the maximum delay. Give up on burst.
+ //
+
+ NpScb->ReceiveBurstModeEnabled = FALSE;
+
+ }
+
+ }
+
+ }
+
+ NpScb->ReceiveBurstSuccessCount = 0;
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "New Receive Delay = %d\n", NpScb->NwReceiveDelay );
+}
+
+
+VOID
+NwProcessSendBurstSuccess(
+ PNONPAGED_SCB NpScb
+ )
+/*++
+
+Routine Description:
+
+ This routine adjust burst parameters after a successful burst operation.
+
+Arguments:
+
+ NpScb - A pointer to the SCB that has completed the burst.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ LONG temp;
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "Successful burst, NpScb = %X\n", NpScb );
+
+ if ( NpScb->NwSendDelay != NpScb->CurrentBurstDelay ) {
+
+ //
+ // This burst has already failed
+ //
+
+ return;
+ }
+
+ if ( NpScb->SendBurstSuccessCount > BurstSuccessCount ) {
+
+ if (NpScb->NwSendDelay != MinSendDelay ) {
+
+ NpScb->NwGoodSendDelay = NpScb->NwSendDelay;
+
+ temp = NpScb->NwGoodSendDelay - NpScb->NwBadSendDelay;
+
+ if (temp >= 0) {
+ NpScb->NwSendDelay -= 1 + temp;
+ } else {
+ NpScb->NwSendDelay -= 1 - temp;
+ }
+
+ if (NpScb->NwSendDelay < MinSendDelay ) {
+
+ NpScb->NwSendDelay = MinSendDelay;
+
+ }
+
+ NpScb->NtSendDelay.QuadPart = NpScb->NwSendDelay * -1000;
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "New Send Delay = %d\n", NpScb->NwSendDelay );
+
+ //
+ // Start monitoring success at the new rate.
+ //
+
+ NpScb->SendBurstSuccessCount = 0;
+
+ } else if ( NpScb->SendBurstSuccessCount > BurstSuccessCount2 ) {
+
+ //
+ // We may have had a really bad patch causing BadSendDelay to be very big.
+ // If we leave it at its current value then at the first sign of trouble
+ // we will make SendDelay very big
+ //
+
+ NpScb->NwGoodSendDelay = NpScb->NwBadSendDelay = NpScb->NwSendDelay;
+
+ //
+ // Is it time to increase the number of packets in the burst?
+ // AllowGrowth == 0 to be the same as the VLM client.
+ //
+
+ if (( AllowGrowth ) &&
+ ( NpScb->NwSendDelay <= MinSendDelay ) &&
+ ( NpScb->MaxSendSize < NwMaxSendSize)) {
+
+ NpScb->MaxSendSize += NpScb->MaxPacketSize;
+
+
+ if ( NpScb->MaxSendSize > NwMaxSendSize) {
+
+ NpScb->MaxSendSize = NwMaxSendSize;
+
+ }
+ }
+
+ NpScb->SendBurstSuccessCount = 0;
+
+ } else {
+
+ NpScb->SendBurstSuccessCount++;
+
+ }
+
+
+ } else {
+
+ NpScb->SendBurstSuccessCount++;
+
+ }
+
+}
+
+
+VOID
+NwProcessReceiveBurstSuccess(
+ PNONPAGED_SCB NpScb
+ )
+/*++
+
+Routine Description:
+
+ This routine adjust burst parameters after a successful burst operation.
+
+Arguments:
+
+ NpScb - A pointer to the SCB that has completed the burst.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ LONG temp;
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "Successful burst, NpScb = %X\n", NpScb );
+
+ if ( NpScb->NwReceiveDelay != NpScb->CurrentBurstDelay ) {
+
+ //
+ // This burst has already failed
+ //
+
+ return;
+ }
+
+ if ( NpScb->ReceiveBurstSuccessCount > BurstSuccessCount ) {
+
+ //
+ // Once the vlm client reaches the Maximum delay it does not
+ // shrink again.
+ //
+
+ if ( NpScb->NwReceiveDelay != MinReceiveDelay ) {
+
+ NpScb->NwGoodReceiveDelay = NpScb->NwReceiveDelay;
+
+ temp = NpScb->NwGoodReceiveDelay - NpScb->NwBadReceiveDelay;
+
+ if (temp >= 0) {
+ NpScb->NwReceiveDelay -= 1 + temp;
+ } else {
+ NpScb->NwReceiveDelay -= 1 - temp;
+ }
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "New Receive Delay = %d\n", NpScb->NwReceiveDelay );
+
+
+ if (NpScb->NwReceiveDelay < MinReceiveDelay ) {
+ NpScb->NwReceiveDelay = MinReceiveDelay;
+
+ }
+
+ //
+ // Start monitoring success at the new rate.
+ //
+
+ NpScb->ReceiveBurstSuccessCount = 0;
+
+ } else if ( NpScb->ReceiveBurstSuccessCount > BurstSuccessCount2 ) {
+
+ //
+ // We may have had a really bad patch causing BadReceiveDelay to be very big.
+ // If we leave it at its current value then at the first sign of trouble
+ // we will make ReceiveDelay very big
+ //
+
+ NpScb->NwGoodReceiveDelay = NpScb->NwBadReceiveDelay = NpScb->NwReceiveDelay;
+
+
+ //
+ // Is it time to increase the number of packets in the burst?
+ //
+
+ if (( AllowGrowth ) &&
+ ( NpScb->NwReceiveDelay <= MinReceiveDelay ) &&
+ ( NpScb->MaxReceiveSize < NwMaxReceiveSize)) {
+
+ NpScb->MaxReceiveSize += NpScb->MaxPacketSize;
+
+
+ if ( NpScb->MaxReceiveSize > NwMaxReceiveSize) {
+
+ NpScb->MaxReceiveSize = NwMaxReceiveSize;
+
+ }
+ }
+
+ NpScb->ReceiveBurstSuccessCount = 0;
+
+ } else {
+
+ NpScb->ReceiveBurstSuccessCount++;
+
+ }
+
+ } else {
+
+ NpScb->ReceiveBurstSuccessCount++;
+
+ }
+
+}
+
+
+VOID
+NwProcessPositiveAck(
+ PNONPAGED_SCB NpScb
+ )
+/*++
+
+Routine Description:
+
+ This routine processes a positive acknowledgement.
+
+Arguments:
+
+ NpScb - A pointer to the SCB that has experienced a burst failure.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ DebugTrace( 0, Dbg, "Positive ACK, NpScb = %X\n", NpScb );
+
+ NpScb->TotalWaitTime += DefaultRetryCount;
+
+ //
+ // If we have not waited longer than the absolute total, keep waiting.
+ // If we have waited too long, let ourselves timeout.
+ //
+ // If NwAbsoluteTotalWaitTime is 0, then we are prepared to wait forever.
+ //
+
+ if ( NpScb->TotalWaitTime < NwAbsoluteTotalWaitTime ||
+ NwAbsoluteTotalWaitTime == 0) {
+
+ NpScb->RetryCount = DefaultRetryCount;
+
+ } else {
+ DebugTrace( 0, Dbg, "Request exceeds absolute total wait time\n", 0 );
+ }
+}
+
+
diff --git a/private/nw/rdr/exchange.h b/private/nw/rdr/exchange.h
new file mode 100644
index 000000000..dcfb70060
--- /dev/null
+++ b/private/nw/rdr/exchange.h
@@ -0,0 +1,114 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ Exchange.h
+
+Abstract:
+
+ This module defines all of the objects exported by exchange.c in the
+ NetWare redirector.
+
+Author:
+
+ Colin Watson [ColinW] 1-Feb-1993
+
+Revision History:
+
+--*/
+
+#ifndef _NWEXCHANGE_
+#define _NWEXCHANGE_
+
+//
+// Define the prototype for post_exchange routines.
+//
+
+struct _IRP_CONTEXT;
+struct _NONPAGED_SCB;
+
+//
+// Prototype for the exchange routine which starts an NCB transmission
+//
+
+NTSTATUS
+_cdecl
+Exchange
+(
+ struct _IRP_CONTEXT* pIrpC,
+ PEX pEx,
+ char* f,
+ ...
+);
+
+//
+// Prototype of routine that can be used to process the response packet
+//
+
+NTSTATUS
+_cdecl
+ExchangeReply(
+ IN PUCHAR RspData,
+ IN ULONG BytesIndicated,
+ char* f,
+ ... // format specific parameters
+ );
+
+USHORT
+NextSocket(
+ IN USHORT OldValue
+ );
+
+VOID
+KickQueue(
+ struct _NONPAGED_SCB* pNpScb
+ );
+
+NTSTATUS
+ServerDatagramHandler(
+ IN PVOID TdiEventContext, // the event context - pNpScb
+ IN int SourceAddressLength, // length of the originator of the datagram
+ IN PVOID SourceAddress, // string describing the originator of the datagram
+ IN int OptionsLength, // options for the receive
+ IN PVOID Options, //
+ IN ULONG ReceiveDatagramFlags, //
+ IN ULONG BytesIndicated, // number of bytes this indication
+ IN ULONG BytesAvailable, // number of bytes in complete Tsdu
+ OUT ULONG *BytesTaken, // number of bytes used
+ IN PVOID Tsdu, // pointer describing this TSDU, typically a lump of bytes
+ OUT PIRP *IoRequestPacket // TdiReceive IRP if MORE_PROCESSING_REQUIRED.
+ );
+
+NTSTATUS
+WatchDogDatagramHandler(
+ IN PVOID TdiEventContext, // the event context - pNpScb
+ IN int SourceAddressLength, // length of the originator of the datagram
+ IN PVOID SourceAddress, // string describing the originator of the datagram
+ IN int OptionsLength, // options for the receive
+ IN PVOID Options, //
+ IN ULONG ReceiveDatagramFlags, //
+ IN ULONG BytesIndicated, // number of bytes this indication
+ IN ULONG BytesAvailable, // number of bytes in complete Tsdu
+ OUT ULONG *BytesTaken, // number of bytes used
+ IN PVOID Tsdu, // pointer describing this TSDU, typically a lump of bytes
+ OUT PIRP *IoRequestPacket // TdiReceive IRP if MORE_PROCESSING_REQUIRED.
+ );
+
+NTSTATUS
+SendDatagramHandler(
+ IN PVOID TdiEventContext, // the event context - pNpScb
+ IN int SourceAddressLength, // length of the originator of the datagram
+ IN PVOID SourceAddress, // string describing the originator of the datagram
+ IN int OptionsLength, // options for the receive
+ IN PVOID Options, //
+ IN ULONG ReceiveDatagramFlags, //
+ IN ULONG BytesIndicated, // number of bytes this indication
+ IN ULONG BytesAvailable, // number of bytes in complete Tsdu
+ OUT ULONG *BytesTaken, // number of bytes used
+ IN PVOID Tsdu, // pointer describing this TSDU, typically a lump of bytes
+ OUT PIRP *IoRequestPacket // TdiReceive IRP if MORE_PROCESSING_REQUIRED.
+ );
+
+#endif // _NWEXCHANGE_
diff --git a/private/nw/rdr/fileinfo.c b/private/nw/rdr/fileinfo.c
new file mode 100644
index 000000000..e9453a317
--- /dev/null
+++ b/private/nw/rdr/fileinfo.c
@@ -0,0 +1,2905 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ fileinfo.c
+
+Abstract:
+
+ This module implements the get / set file information routines for
+ Netware Redirector.
+
+Author:
+
+ Manny Weiser (mannyw) 4-Mar-1993
+
+Revision History:
+
+--*/
+
+#include "procs.h"
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_FILEINFO)
+
+//
+// local procedure prototypes
+//
+
+NTSTATUS
+NwCommonQueryInformation (
+ IN PIRP_CONTEXT pIrpContext
+ );
+
+NTSTATUS
+NwCommonSetInformation (
+ IN PIRP_CONTEXT pIrpContet
+ );
+
+NTSTATUS
+NwQueryBasicInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_BASIC_INFORMATION Buffer
+ );
+
+NTSTATUS
+NwQueryStandardInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_STANDARD_INFORMATION Buffer
+ );
+
+NTSTATUS
+NwQueryInternalInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_INTERNAL_INFORMATION Buffer
+ );
+
+NTSTATUS
+NwQueryEaInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PFILE_EA_INFORMATION Buffer
+ );
+
+NTSTATUS
+NwQueryNameInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_NAME_INFORMATION Buffer,
+ IN OUT PULONG Length
+ );
+
+NTSTATUS
+NwQueryPositionInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_POSITION_INFORMATION Buffer
+ );
+
+NTSTATUS
+NwSetBasicInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_BASIC_INFORMATION Buffer
+ );
+
+NTSTATUS
+NwSetDispositionInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_DISPOSITION_INFORMATION Buffer
+ );
+
+NTSTATUS
+NwSetRenameInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_RENAME_INFORMATION Buffer
+ );
+
+NTSTATUS
+NwSetPositionInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_POSITION_INFORMATION Buffer
+ );
+
+NTSTATUS
+NwSetAllocationInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_ALLOCATION_INFORMATION Buffer
+ );
+
+NTSTATUS
+NwSetEndOfFileInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_END_OF_FILE_INFORMATION Buffer
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwFsdQueryInformation )
+#pragma alloc_text( PAGE, NwFsdSetInformation )
+#pragma alloc_text( PAGE, NwCommonQueryInformation )
+#pragma alloc_text( PAGE, NwCommonSetInformation )
+#pragma alloc_text( PAGE, NwQueryStandardInfo )
+#pragma alloc_text( PAGE, NwQueryInternalInfo )
+#pragma alloc_text( PAGE, NwQueryEaInfo )
+#pragma alloc_text( PAGE, NwQueryNameInfo )
+#pragma alloc_text( PAGE, NwQueryPositionInfo )
+#pragma alloc_text( PAGE, NwSetBasicInfo )
+#pragma alloc_text( PAGE, NwSetDispositionInfo )
+#pragma alloc_text( PAGE, NwDeleteFile )
+#pragma alloc_text( PAGE, NwSetRenameInfo )
+#pragma alloc_text( PAGE, NwSetPositionInfo )
+#pragma alloc_text( PAGE, NwSetAllocationInfo )
+#pragma alloc_text( PAGE, NwSetEndOfFileInfo )
+#pragma alloc_text( PAGE, OccurenceCount )
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, NwQueryBasicInfo )
+#endif
+
+#endif
+
+#if 0 // Not pageable
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+
+NTSTATUS
+NwFsdQueryInformation (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine implements the FSD part of the NtQueryInformationFile API
+ calls.
+
+Arguments:
+
+ DeviceObject - Supplies a pointer to the device object to use.
+
+ Irp - Supplies a pointer to the Irp to process.
+
+Return Value:
+
+ NTSTATUS - The Fsd status for the Irp
+
+--*/
+
+{
+ NTSTATUS status;
+ PIRP_CONTEXT pIrpContext = NULL;
+ BOOLEAN TopLevel;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwFsdQueryInformation\n", 0);
+
+ //
+ // Call the common query information routine.
+ //
+
+ FsRtlEnterFileSystem();
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ try {
+
+ pIrpContext = AllocateIrpContext( Irp );
+ status = NwCommonQueryInformation( pIrpContext );
+
+ } except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
+
+ if ( pIrpContext == NULL ) {
+
+ //
+ // If we couldn't allocate an irp context, just complete
+ // irp without any fanfare.
+ //
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
+
+ } else {
+
+ //
+ // We had some trouble trying to perform the requested
+ // operation, so we'll abort the I/O request with
+ // the error Status that we get back from the
+ // execption code
+ //
+
+ status = NwProcessException( pIrpContext, GetExceptionCode() );
+ }
+ }
+
+ if ( pIrpContext ) {
+
+ if ( status != STATUS_PENDING ) {
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ }
+
+ NwCompleteRequest( pIrpContext, status );
+ }
+
+ if ( TopLevel ) {
+ NwSetTopLevelIrp( NULL );
+ }
+ FsRtlExitFileSystem();
+
+ //
+ // Return to the caller.
+ //
+
+ DebugTrace(-1, Dbg, "NwFsdQueryInformation -> %08lx\n", status );
+
+ return status;
+}
+
+
+NTSTATUS
+NwFsdSetInformation (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+/*++
+
+Routine Description:
+
+ This routine implements the FSD part of the NtSetInformationFile API
+ calls.
+
+Arguments:
+
+ DeviceObject - Supplies the device object to use.
+
+ Irp - Supplies the Irp being processed
+
+Return Value:
+
+ NTSTATUS - The Fsd status for the Irp
+
+--*/
+{
+ NTSTATUS status;
+ PIRP_CONTEXT pIrpContext = NULL;
+ BOOLEAN TopLevel;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwFsdSetInformation\n", 0);
+
+ //
+ // Call the common Set Information routine.
+ //
+
+ FsRtlEnterFileSystem();
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ try {
+
+ pIrpContext = AllocateIrpContext( Irp );
+ status = NwCommonSetInformation( pIrpContext );
+
+ } except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
+
+ if ( pIrpContext == NULL ) {
+
+ //
+ // If we couldn't allocate an irp context, just complete
+ // irp without any fanfare.
+ //
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
+
+ } else {
+
+ //
+ // We had some trouble trying to perform the requested
+ // operation, so we'll abort the I/O request with
+ // the error Status that we get back from the
+ // execption code
+ //
+
+ status = NwProcessException( pIrpContext, GetExceptionCode() );
+ }
+
+ }
+
+ if ( pIrpContext ) {
+
+ if ( status != STATUS_PENDING ) {
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ }
+
+ NwCompleteRequest( pIrpContext, status );
+ }
+
+ if ( TopLevel ) {
+ NwSetTopLevelIrp( NULL );
+ }
+ FsRtlExitFileSystem();
+
+ //
+ // Return to the caller.
+ //
+
+ DebugTrace(-1, Dbg, "NwFsdSetInformation -> %08lx\n", status );
+
+ return status;
+}
+
+
+NTSTATUS
+NwCommonQueryInformation (
+ IN PIRP_CONTEXT pIrpContext
+ )
+/*++
+
+Routine Description:
+
+ This is the common routine for querying information on a file.
+
+Arguments:
+
+ pIrpContext - Supplies Irp context information.
+
+Return Value:
+
+ NTSTATUS - the return status for the operation.
+
+--*/
+{
+ PIRP Irp;
+ PIO_STACK_LOCATION irpSp;
+ NTSTATUS status;
+
+ ULONG length;
+ FILE_INFORMATION_CLASS fileInformationClass;
+ PVOID buffer;
+
+ NODE_TYPE_CODE nodeTypeCode;
+ PICB icb;
+ PFCB fcb;
+
+ PVOID fsContext, fsContext2;
+
+ PFILE_ALL_INFORMATION AllInfo;
+
+ PAGED_CODE();
+
+ //
+ // Get the current stack location.
+ //
+
+ Irp = pIrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ DebugTrace(+1, Dbg, "NwCommonQueryInformation...\n", 0);
+ DebugTrace( 0, Dbg, " Irp = %08lx\n", (ULONG)Irp);
+ DebugTrace( 0, Dbg, " ->Length = %08lx\n", irpSp->Parameters.QueryFile.Length);
+ DebugTrace( 0, Dbg, " ->FileInformationClass = %08lx\n", irpSp->Parameters.QueryFile.FileInformationClass);
+ DebugTrace( 0, Dbg, " ->Buffer = %08lx\n", (ULONG)Irp->AssociatedIrp.SystemBuffer);
+
+ //
+ // Find out who are.
+ //
+
+ if ((nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
+ &fsContext,
+ &fsContext2 )) == NTC_UNDEFINED) {
+
+ status = STATUS_INVALID_HANDLE;
+
+ DebugTrace(-1, Dbg, "NwCommonQueryInformation -> %08lx\n", status );
+ return status;
+ }
+
+ //
+ // Make sure that this the user is querying an ICB.
+ //
+
+ switch (nodeTypeCode) {
+
+ case NW_NTC_ICB:
+
+ icb = (PICB)fsContext2;
+ break;
+
+ default: // This is an illegal file object to query
+
+ DebugTrace(0, Dbg, "Node type code is not incorrect\n", 0);
+
+ DebugTrace(-1, Dbg, "NwCommonQueryInformation -> STATUS_INVALID_PARAMETER\n", 0);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ pIrpContext->Icb = icb;
+
+ //
+ // Make local copies of the input parameters.
+ //
+
+ length = irpSp->Parameters.QueryFile.Length;
+ fileInformationClass = irpSp->Parameters.QueryFile.FileInformationClass;
+ buffer = Irp->AssociatedIrp.SystemBuffer;
+
+ //
+ // Now acquire shared access to the FCB
+ //
+
+ fcb = icb->SuperType.Fcb;
+
+ try {
+
+ NwVerifyIcbSpecial( icb );
+
+ //
+ // Based on the information class we'll do different actions. Each
+ // of the procedure that we're calling fill up as much of the
+ // buffer as possible and return the remaining length, and status
+ // This is done so that we can use them to build up the
+ // FileAllInformation request. These procedures do not complete the
+ // IRP, instead this procedure must complete the IRP.
+ //
+
+ status = STATUS_SUCCESS;
+
+ switch (fileInformationClass) {
+
+ case FileAllInformation:
+
+ AllInfo = buffer;
+
+ //
+ // First call all the Query Info handlers we can call
+ // synchronously.
+ //
+
+ NwQueryInternalInfo( pIrpContext, icb, &AllInfo->InternalInformation );
+ NwQueryEaInfo( pIrpContext, &AllInfo->EaInformation );
+ NwQueryPositionInfo( pIrpContext, icb, &AllInfo->PositionInformation );
+
+ length -= FIELD_OFFSET( FILE_ALL_INFORMATION, NameInformation );
+
+ status = NwQueryNameInfo( pIrpContext, icb, &AllInfo->NameInformation, &length );
+
+ if ( !NT_ERROR( status ) ) {
+ status = NwQueryStandardInfo( pIrpContext, icb, &AllInfo->StandardInformation );
+ }
+
+ if ( !NT_ERROR( status ) ) {
+ status = NwQueryBasicInfo( pIrpContext, icb, &AllInfo->BasicInformation );
+ }
+
+ break;
+
+
+ case FileBasicInformation:
+
+ length -= sizeof( FILE_BASIC_INFORMATION );
+ status = NwQueryBasicInfo( pIrpContext, icb, buffer );
+
+ break;
+
+ case FileStandardInformation:
+
+ //
+ // We will handle this call for information asynchronously.
+ // The callback routine will fill in the missing data, and
+ // complete the IRP.
+ //
+ // Remember the buffer length, and status to return.
+ //
+
+ length -= sizeof( FILE_STANDARD_INFORMATION );
+ status = NwQueryStandardInfo( pIrpContext, icb, buffer );
+ break;
+
+ case FileInternalInformation:
+
+ status = NwQueryInternalInfo( pIrpContext, icb, buffer );
+ length -= sizeof( FILE_INTERNAL_INFORMATION );
+ break;
+
+ case FileEaInformation:
+
+ status = NwQueryEaInfo( pIrpContext, buffer );
+ length -= sizeof( FILE_EA_INFORMATION );
+ break;
+
+ case FilePositionInformation:
+
+ status = NwQueryPositionInfo( pIrpContext, icb, buffer );
+ length -= sizeof( FILE_POSITION_INFORMATION );
+ break;
+
+ case FileNameInformation:
+
+ status = NwQueryNameInfo( pIrpContext, icb, buffer, &length );
+ break;
+
+ default:
+
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ //
+ // Set the information field to the number of bytes actually
+ // filled in and then complete the request. (This is
+ // irrelavent if the Query worker function returned
+ // STATUS_PENDING).
+ //
+
+ if ( status != STATUS_PENDING ) {
+ Irp->IoStatus.Information =
+ irpSp->Parameters.QueryFile.Length - length;
+ }
+
+ } finally {
+
+ DebugTrace(-1, Dbg, "NwCommonQueryInformation -> %08lx\n", status );
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+NwCommonSetInformation (
+ IN PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This is the common routine for setting information on a file.
+
+Arguments:
+
+ IrpContext - Supplies the Irp to process
+
+Return Value:
+
+ NTSTATUS - the return status for the operation
+
+--*/
+{
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ NTSTATUS status;
+
+ ULONG length;
+ FILE_INFORMATION_CLASS fileInformationClass;
+ PVOID buffer;
+
+ NODE_TYPE_CODE nodeTypeCode;
+ PICB icb;
+ PFCB fcb;
+ PVOID fsContext;
+
+ //
+ // Get the current Irp stack location.
+ //
+
+ irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+ DebugTrace(+1, Dbg, "NwCommonSetInformation...\n", 0);
+ DebugTrace( 0, Dbg, " Irp = %08lx\n", (ULONG)irp);
+ DebugTrace( 0, Dbg, " ->Length = %08lx\n", irpSp->Parameters.SetFile.Length);
+ DebugTrace( 0, Dbg, " ->FileInformationClass = %08lx\n", irpSp->Parameters.SetFile.FileInformationClass);
+ DebugTrace( 0, Dbg, " ->Buffer = %08lx\n", (ULONG)irp->AssociatedIrp.SystemBuffer);
+
+ //
+ // Get a pointer to the FCB and ensure that this is a server side
+ // handler to a file.
+ //
+
+ if ((nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
+ &fsContext,
+ (PVOID *)&icb )) == NTC_UNDEFINED ) {
+
+ status = STATUS_INVALID_HANDLE;
+
+ DebugTrace(-1, Dbg, "NwCommonSetInformation -> %08lx\n", status );
+ return status;
+ }
+
+ //
+ // Make sure that this the user is querying an ICB.
+ //
+
+ switch (nodeTypeCode) {
+
+ case NW_NTC_ICB:
+
+ fcb = icb->SuperType.Fcb;
+ break;
+
+ default: // This is an illegal file object to query
+
+ DebugTrace(0, Dbg, "Node type code is not incorrect\n", 0);
+
+ DebugTrace(-1, Dbg, "NwCommonSetInformation -> STATUS_INVALID_PARAMETER\n", 0);
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ IrpContext->Icb = icb;
+
+ //
+ // Make local copies of the input parameters.
+ //
+
+ length = irpSp->Parameters.SetFile.Length;
+ fileInformationClass = irpSp->Parameters.SetFile.FileInformationClass;
+ buffer = irp->AssociatedIrp.SystemBuffer;
+
+ try {
+
+ NwVerifyIcb( icb );
+
+ //
+ // Based on the information class we'll do different actions. Each
+ // procedure that we're calling will complete the request.
+ //
+
+ switch (fileInformationClass) {
+
+ case FileBasicInformation:
+
+ status = NwSetBasicInfo( IrpContext, icb, buffer );
+ break;
+
+ case FileDispositionInformation:
+
+ status = NwSetDispositionInfo( IrpContext, icb, buffer );
+ break;
+
+ case FileRenameInformation:
+
+ status = NwSetRenameInfo( IrpContext, icb, buffer );
+ break;
+
+ case FilePositionInformation:
+
+ status = NwSetPositionInfo( IrpContext, icb, buffer );
+ break;
+
+ case FileLinkInformation:
+
+ status = STATUS_INVALID_DEVICE_REQUEST;
+ break;
+
+ case FileAllocationInformation:
+
+ status = NwSetAllocationInfo( IrpContext, icb, buffer );
+ break;
+
+ case FileEndOfFileInformation:
+
+ status = NwSetEndOfFileInfo( IrpContext, icb, buffer );
+ break;
+
+ default:
+
+ status = STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ } finally {
+
+ DebugTrace(-1, Dbg, "NwCommonSetInformation -> %08lx\n", status);
+ }
+
+
+ return status;
+}
+
+
+NTSTATUS
+NwQueryBasicInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ OUT PFILE_BASIC_INFORMATION Buffer
+ )
+/*++
+
+Routine Description:
+
+ This routine performs the query basic information operation.
+ This routine cannot be paged, it is called from QueryStandardInfoCallback.
+
+Arguments:
+
+ Icb - Supplies a pointer the ICB for the file being querying.
+
+ Buffer - Supplies a pointer to the buffer where the information is
+ to be returned.
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+ PFCB Fcb;
+ NTSTATUS Status;
+ ULONG Attributes;
+ USHORT CreationDate;
+ USHORT CreationTime = DEFAULT_TIME;
+ USHORT LastAccessDate;
+ USHORT LastModifiedDate;
+ USHORT LastModifiedTime;
+ BOOLEAN FirstTime = TRUE;
+
+ DebugTrace(0, Dbg, "QueryBasicInfo...\n", 0);
+
+ //
+ // Zero out the buffer.
+ //
+
+ RtlZeroMemory( Buffer, sizeof(FILE_BASIC_INFORMATION) );
+ Fcb = Icb->SuperType.Fcb;
+
+ //
+ // It is ok to attempt a reconnect if this request fails with a
+ // connection error.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_RECONNECTABLE );
+
+ NwAcquireSharedFcb( Fcb->NonPagedFcb, TRUE );
+
+ //
+ // If we already know the file attributes, simply return them.
+ //
+
+ if ( FlagOn( Fcb->Flags, FCB_FLAGS_ATTRIBUTES_ARE_VALID ) ) {
+
+ //
+ // Set the various fields in the record
+ //
+
+ Buffer->CreationTime = NwDateTimeToNtTime(
+ Fcb->CreationDate,
+ Fcb->CreationTime
+ );
+
+ Buffer->LastAccessTime = NwDateTimeToNtTime(
+ Fcb->LastAccessDate,
+ DEFAULT_TIME
+ );
+
+ Buffer->LastWriteTime = NwDateTimeToNtTime(
+ Fcb->LastModifiedDate,
+ Fcb->LastModifiedTime
+ );
+
+ DebugTrace(0, Dbg, "QueryBasic known %wZ\n", &Fcb->RelativeFileName);
+ DebugTrace(0, Dbg, "LastModifiedDate %x\n", Fcb->LastModifiedDate);
+ DebugTrace(0, Dbg, "LastModifiedTime %x\n", Fcb->LastModifiedTime);
+ DebugTrace(0, Dbg, "CreationDate %x\n", Fcb->CreationDate );
+ DebugTrace(0, Dbg, "CreationTime %x\n", Fcb->CreationTime );
+ DebugTrace(0, Dbg, "LastAccessDate %x\n", Fcb->LastAccessDate );
+
+ Buffer->FileAttributes = Fcb->NonPagedFcb->Attributes;
+
+ if ( Buffer->FileAttributes == 0 ) {
+ Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL;
+ }
+
+ NwReleaseFcb( Fcb->NonPagedFcb );
+ return STATUS_SUCCESS;
+
+ } else if ( Fcb->RelativeFileName.Length == 0 ) {
+
+ //
+ // Allow 'cd \' to work.
+ //
+
+ Buffer->FileAttributes = FILE_ATTRIBUTE_DIRECTORY;
+
+ Buffer->CreationTime = NwDateTimeToNtTime(
+ DEFAULT_DATE,
+ DEFAULT_TIME
+ );
+
+ Buffer->LastAccessTime = Buffer->CreationTime;
+ Buffer->LastWriteTime = Buffer->CreationTime;
+
+ NwReleaseFcb( Fcb->NonPagedFcb );
+ return STATUS_SUCCESS;
+
+ } else {
+
+ NwReleaseFcb( Fcb->NonPagedFcb );
+
+ IrpContext->pNpScb = Fcb->Scb->pNpScb;
+Retry:
+ if ( !BooleanFlagOn( Fcb->Flags, FCB_FLAGS_LONG_NAME ) ) {
+
+ DebugTrace(0, Dbg, "QueryBasic short %wZ\n", &Fcb->RelativeFileName);
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "FwbbJ",
+ NCP_SEARCH_FILE,
+ -1,
+ Fcb->Vcb->Specific.Disk.Handle,
+ Fcb->NodeTypeCode == NW_NTC_FCB ?
+ SEARCH_ALL_FILES : SEARCH_ALL_DIRECTORIES,
+ &Icb->SuperType.Fcb->RelativeFileName );
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N==_b-==wwww",
+ 14,
+ &Attributes,
+ &CreationDate,
+ &LastAccessDate,
+ &LastModifiedDate,
+ &LastModifiedTime);
+ }
+
+ } else {
+
+ DebugTrace(0, Dbg, "QueryBasic long %wZ\n", &Fcb->RelativeFileName);
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "LbbWDbDbC",
+ NCP_LFN_GET_INFO,
+ Fcb->Vcb->Specific.Disk.LongNameSpace,
+ Fcb->Vcb->Specific.Disk.LongNameSpace,
+ Fcb->NodeTypeCode == NW_NTC_FCB ?
+ SEARCH_ALL_FILES : SEARCH_ALL_DIRECTORIES,
+ LFN_FLAG_INFO_ATTRIBUTES |
+ LFN_FLAG_INFO_MODIFY_TIME |
+ LFN_FLAG_INFO_CREATION_TIME,
+ Fcb->Vcb->Specific.Disk.VolumeNumber,
+ Fcb->Vcb->Specific.Disk.Handle,
+ 0,
+ &Icb->SuperType.Fcb->RelativeFileName );
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N_e_xx_xx_x",
+ 4,
+ &Attributes,
+ 12,
+ &CreationTime,
+ &CreationDate,
+ 4,
+ &LastModifiedTime,
+ &LastModifiedDate,
+ 4,
+ &LastAccessDate );
+
+ }
+ }
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // Set the various fields in the record
+ //
+
+ Buffer->CreationTime = NwDateTimeToNtTime(
+ CreationDate,
+ CreationTime
+ );
+
+ Buffer->LastAccessTime = NwDateTimeToNtTime(
+ LastAccessDate,
+ DEFAULT_TIME
+ );
+
+ Buffer->LastWriteTime = NwDateTimeToNtTime(
+ LastModifiedDate,
+ LastModifiedTime
+ );
+
+ DebugTrace(0, Dbg, "CreationDate %x\n", CreationDate );
+ DebugTrace(0, Dbg, "CreationTime %x\n", CreationTime );
+ DebugTrace(0, Dbg, "LastAccessDate %x\n", LastAccessDate );
+ DebugTrace(0, Dbg, "LastModifiedDate %x\n", LastModifiedDate);
+ DebugTrace(0, Dbg, "LastModifiedTime %x\n", LastModifiedTime);
+
+ Buffer->FileAttributes = (UCHAR)Attributes;
+
+ if ( Buffer->FileAttributes == 0 ) {
+ Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL;
+ }
+
+ } else if ((Status == STATUS_INVALID_HANDLE) &&
+ (FirstTime)) {
+
+ //
+ // Check to see if Volume handle is invalid. Caused when volume
+ // is unmounted and then remounted.
+ //
+
+ FirstTime = FALSE;
+
+ NwReopenVcbHandle( IrpContext, Fcb->Vcb );
+
+ goto Retry;
+ }
+
+ return( Status );
+ }
+}
+
+#if NWFASTIO
+
+BOOLEAN
+NwFastQueryBasicInfo (
+ IN PFILE_OBJECT FileObject,
+ IN BOOLEAN Wait,
+ IN OUT PFILE_BASIC_INFORMATION Buffer,
+ OUT PIO_STATUS_BLOCK IoStatus,
+ IN PDEVICE_OBJECT DeviceObject
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is for the fast query call for standard file information.
+
+Arguments:
+
+ FileObject - Supplies the file object used in this operation
+
+ Wait - Indicates if we are allowed to wait for the information
+
+ Buffer - Supplies the output buffer to receive the basic information
+
+ IoStatus - Receives the final status of the operation
+
+Return Value:
+
+ BOOLEAN - TRUE if the operation succeeded and FALSE if the caller
+ needs to take the long route.
+
+--*/
+
+{
+ NODE_TYPE_CODE NodeTypeCode;
+ PICB Icb;
+ PFCB Fcb;
+ PVOID FsContext;
+
+ //
+ // Find out who are.
+ //
+
+ if ((NodeTypeCode = NwDecodeFileObject( FileObject,
+ &FsContext,
+ &Icb )) != NW_NTC_ICB ) {
+
+ DebugTrace(-1, Dbg, "NwFastQueryStandardInfo -> FALSE\n", 0 );
+ return FALSE;
+ }
+
+ Fcb = Icb->SuperType.Fcb;
+
+ NwAcquireExclusiveFcb( Fcb->NonPagedFcb, TRUE );
+
+ //
+ // If we don't have the info handy, we can't use the fast path.
+ //
+
+ if ( !FlagOn( Fcb->Flags, FCB_FLAGS_ATTRIBUTES_ARE_VALID ) ) {
+ NwReleaseFcb( Fcb->NonPagedFcb );
+ return( FALSE );
+ }
+
+ //
+ // Set the various fields in the record
+ //
+
+ Buffer->CreationTime = NwDateTimeToNtTime(
+ Fcb->CreationDate,
+ Fcb->CreationTime
+ );
+
+ Buffer->LastAccessTime = NwDateTimeToNtTime(
+ Fcb->LastAccessDate,
+ DEFAULT_TIME
+ );
+
+ Buffer->LastWriteTime = NwDateTimeToNtTime(
+ Fcb->LastModifiedDate,
+ Fcb->LastModifiedTime
+ );
+
+ DebugTrace(0, Dbg, "QueryBasic known %wZ\n", &Fcb->RelativeFileName);
+ DebugTrace(0, Dbg, "LastModifiedDate %x\n", Fcb->LastModifiedDate);
+ DebugTrace(0, Dbg, "LastModifiedTime %x\n", Fcb->LastModifiedTime);
+ DebugTrace(0, Dbg, "CreationDate %x\n", Fcb->CreationDate );
+ DebugTrace(0, Dbg, "CreationTime %x\n", Fcb->CreationTime );
+ DebugTrace(0, Dbg, "LastAccessDate %x\n", Fcb->LastAccessDate );
+
+ Buffer->FileAttributes = Fcb->NonPagedFcb->Attributes;
+
+ if ( Buffer->FileAttributes == 0 ) {
+ Buffer->FileAttributes = FILE_ATTRIBUTE_NORMAL;
+ }
+
+ IoStatus->Status = STATUS_SUCCESS;
+ IoStatus->Information = sizeof( *Buffer );
+
+ NwReleaseFcb( Fcb->NonPagedFcb );
+ return TRUE;
+}
+#endif
+
+
+NTSTATUS
+NwQueryStandardInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_STANDARD_INFORMATION Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine perforNw the query standard information operation.
+
+Arguments:
+
+ Fcb - Supplies the FCB of the being queried
+
+ Buffer - Supplies a pointer to the buffer where the information is
+ to be returned
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+ NTSTATUS Status;
+ PFCB Fcb;
+ ULONG FileSize;
+ BOOLEAN FirstTime = TRUE;
+
+ PAGED_CODE();
+
+ Fcb = Icb->SuperType.Fcb;
+
+ //
+ // Zero out the buffer.
+ //
+
+ RtlZeroMemory( Buffer, sizeof(FILE_STANDARD_INFORMATION) );
+
+ //
+ // Fill in the answers we already know.
+ //
+
+ Buffer->NumberOfLinks = 1;
+
+ Buffer->DeletePending = (BOOLEAN)FlagOn( Fcb->Flags, FCB_FLAGS_DELETE_ON_CLOSE );
+
+ if ( Fcb->NodeTypeCode == NW_NTC_FCB ) {
+ Buffer->Directory = FALSE;
+ } else {
+ Buffer->Directory = TRUE;
+ }
+
+ if ( !Icb->HasRemoteHandle ) {
+
+ //
+ // It is ok to attempt a reconnect if this request fails with a
+ // connection error.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_RECONNECTABLE );
+
+ if ( Fcb->NodeTypeCode == NW_NTC_DCB ||
+ FlagOn( Fcb->Vcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) {
+
+ //
+ // Allow 'cd \' to work.
+ //
+
+ Buffer->AllocationSize.QuadPart = 0;
+ Buffer->EndOfFile.QuadPart = 0;
+
+ return STATUS_SUCCESS;
+
+ } else {
+
+ //
+ // No open handle for this file. Use a path based NCP
+ // to get the file size.
+ //
+Retry:
+ IrpContext->pNpScb = Fcb->Scb->pNpScb;
+
+ if ( !BooleanFlagOn( Icb->SuperType.Fcb->Flags, FCB_FLAGS_LONG_NAME ) ) {
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "FwbbJ",
+ NCP_SEARCH_FILE,
+ -1,
+ Fcb->Vcb->Specific.Disk.Handle,
+ SEARCH_ALL_FILES,
+ &Fcb->RelativeFileName );
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N_d",
+ 20,
+ &FileSize );
+ }
+
+ } else {
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "LbbWDbDbC",
+ NCP_LFN_GET_INFO,
+ Fcb->Vcb->Specific.Disk.LongNameSpace,
+ Fcb->Vcb->Specific.Disk.LongNameSpace,
+ SEARCH_ALL_FILES,
+ LFN_FLAG_INFO_FILE_SIZE,
+ Fcb->Vcb->Specific.Disk.VolumeNumber,
+ Fcb->Vcb->Specific.Disk.Handle,
+ 0,
+ &Fcb->RelativeFileName );
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N_e",
+ 10,
+ &FileSize );
+ }
+
+ }
+
+ if ((Status == STATUS_INVALID_HANDLE) &&
+ (FirstTime)) {
+
+ //
+ // Check to see if Volume handle is invalid. Caused when volume
+ // is unmounted and then remounted.
+ //
+
+ FirstTime = FALSE;
+
+ NwReopenVcbHandle( IrpContext, Fcb->Vcb );
+
+ goto Retry;
+ }
+
+ Buffer->AllocationSize.QuadPart = FileSize;
+ Buffer->EndOfFile.QuadPart = FileSize;
+
+ }
+
+ } else {
+
+ //
+ // Start a Get file size NCP
+ //
+
+ IrpContext->pNpScb = Fcb->Scb->pNpScb;
+
+ if ( Fcb->NodeTypeCode == NW_NTC_FCB ) {
+ AcquireFcbAndFlushCache( IrpContext, Fcb->NonPagedFcb );
+ }
+
+ Status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "F-r",
+ NCP_GET_FILE_SIZE,
+ &Icb->Handle, sizeof(Icb->Handle ) );
+
+ if ( NT_SUCCESS( Status ) ) {
+ //
+ // Get the data from the response.
+ //
+
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nd",
+ &FileSize );
+
+ }
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // Fill in Allocation size and EOF, based on the response.
+ //
+
+ Buffer->AllocationSize.QuadPart = FileSize;
+ Buffer->EndOfFile.QuadPart = Buffer->AllocationSize.QuadPart;
+
+ }
+ }
+
+ return( Status );
+}
+
+#if NWFASTIO
+
+BOOLEAN
+NwFastQueryStandardInfo (
+ IN PFILE_OBJECT FileObject,
+ IN BOOLEAN Wait,
+ IN OUT PFILE_STANDARD_INFORMATION Buffer,
+ OUT PIO_STATUS_BLOCK IoStatus,
+ IN PDEVICE_OBJECT DeviceObject
+ )
+/*++
+
+Routine Description:
+
+ This routine is for the fast query call for standard file information.
+
+Arguments:
+
+ FileObject - Supplies the file object used in this operation
+
+ Wait - Indicates if we are allowed to wait for the information
+
+ Buffer - Supplies the output buffer to receive the basic information
+
+ IoStatus - Receives the final status of the operation
+
+Return Value:
+
+ BOOLEAN - TRUE if the operation succeeded and FALSE if the caller
+ needs to take the long route.
+
+--*/
+{
+ NODE_TYPE_CODE NodeTypeCode;
+ PICB Icb;
+ PFCB Fcb;
+ PVOID FsContext;
+
+ //
+ // Find out who are.
+ //
+
+ if ((NodeTypeCode = NwDecodeFileObject( FileObject,
+ &FsContext,
+ &Icb )) != NW_NTC_ICB ) {
+
+ DebugTrace(-1, Dbg, "NwFastQueryStandardInfo -> FALSE\n", 0 );
+ return FALSE;
+ }
+
+ Fcb = Icb->SuperType.Fcb;
+
+ //
+ // If we have the info handy, we can use the fast path.
+ //
+
+ if ( Fcb->NodeTypeCode == NW_NTC_DCB ||
+ FlagOn( Fcb->Vcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) {
+
+ Buffer->AllocationSize.QuadPart = 0;
+ Buffer->EndOfFile.QuadPart = 0;
+
+ Buffer->NumberOfLinks = 1;
+ Buffer->DeletePending = (BOOLEAN)FlagOn( Fcb->Flags, FCB_FLAGS_DELETE_ON_CLOSE );
+
+ Buffer->Directory = TRUE;
+
+ IoStatus->Status = STATUS_SUCCESS;
+ IoStatus->Information = sizeof( *Buffer );
+
+ return TRUE;
+
+ } else {
+
+ return FALSE;
+
+ }
+}
+#endif
+
+
+NTSTATUS
+NwQueryInternalInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_INTERNAL_INFORMATION Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine perforNw the query internal information operation.
+
+Arguments:
+
+ Fcb - Supplies the FCB of the being queried.
+
+ Buffer - Supplies a pointer to the buffer where the information is
+ to be returned.
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "QueryInternalInfo...\n", 0);
+
+ //
+ // Zero out the buffer.
+ //
+
+ RtlZeroMemory( Buffer, sizeof(FILE_INTERNAL_INFORMATION) );
+
+ //
+ // Set the internal index number to be the address of the ICB.
+ //
+
+ Buffer->IndexNumber.LowPart = (ULONG)Icb->NpFcb;
+ Buffer->IndexNumber.HighPart = 0;
+
+ return( STATUS_SUCCESS );
+}
+
+
+NTSTATUS
+NwQueryEaInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PFILE_EA_INFORMATION Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine performs the query Ea information operation.
+
+Arguments:
+
+ Buffer - Supplies a pointer to the buffer where the information is
+ to be returned
+
+Return Value:
+
+ VOID - The result of this query
+
+--*/
+
+{
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "QueryEaInfo...\n", 0);
+
+ //
+ // Zero out the buffer.
+ //
+
+ RtlZeroMemory(Buffer, sizeof(FILE_EA_INFORMATION));
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+NwQueryNameInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_NAME_INFORMATION Buffer,
+ IN PULONG Length
+ )
+
+/*++
+
+Routine Description:
+
+ This routine performs the query name information operation.
+
+Arguments:
+
+ Fcb - Supplies the FCB of the file to query.
+
+ Buffer - Supplies a pointer to the buffer where the information is
+ to be returned
+
+ Length - Supplies and receives the length of the buffer in bytes.
+
+Return Value:
+
+ NTSTATUS - The result of this query.
+
+--*/
+
+{
+ ULONG bytesToCopy;
+ ULONG fileNameSize;
+ PFCB Fcb = Icb->SuperType.Fcb;
+
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "QueryNameInfo...\n", 0);
+
+ //
+ // Win32 expects the root directory name to be '\' terminated,
+ // the netware server does not. So if this is a root directory,
+ // (i.e RelativeFileName length is 0) append a '\' to the path name.
+ //
+
+ //
+ // See if the buffer is large enough, and decide how many bytes to copy.
+ //
+
+ *Length -= FIELD_OFFSET( FILE_NAME_INFORMATION, FileName[0] );
+
+ fileNameSize = Fcb->FullFileName.Length;
+ if ( Fcb->RelativeFileName.Length == 0 ) {
+ fileNameSize += sizeof(L'\\');
+ }
+ Buffer->FileNameLength = fileNameSize;
+
+ if ( *Length >= fileNameSize ) {
+
+ status = STATUS_SUCCESS;
+
+ bytesToCopy = fileNameSize;
+
+ } else {
+
+ status = STATUS_BUFFER_OVERFLOW;
+
+ bytesToCopy = *Length;
+ }
+
+ //
+ // Copy over the file name and its length.
+ //
+
+ RtlMoveMemory(
+ Buffer->FileName,
+ Fcb->FullFileName.Buffer,
+ bytesToCopy);
+
+ //
+ // If this is a root directory, and there is space in the buffer
+ // append a '\' to make win32 happy.
+ //
+
+ if ( Fcb->RelativeFileName.Length == 0 && status == STATUS_SUCCESS ) {
+ Buffer->FileName[ fileNameSize/sizeof(WCHAR) - 1 ] = L'\\';
+ }
+
+ *Length -= bytesToCopy;
+
+ return status;
+}
+
+
+NTSTATUS
+NwQueryPositionInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_POSITION_INFORMATION Buffer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine performs the query position information operation.
+
+Arguments:
+
+ Fcb - Supplies the FCB of the file being queried.
+
+ Buffer - Supplies a pointer to the buffer where the information is
+ to be returned.
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "QueryPositionInfo...\n", 0);
+
+ //
+ // Return the current byte offset. This info is totally
+ // bogus for asynchronous files. Also note that we don't
+ // use the FilePosition member of the ICB for anything.
+ //
+
+ if ( Icb->FileObject ) {
+ Buffer->CurrentByteOffset.QuadPart = Icb->FileObject->CurrentByteOffset.QuadPart;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+NwSetBasicInfo (
+ IN PIRP_CONTEXT pIrpContext,
+ IN PICB Icb,
+ IN PFILE_BASIC_INFORMATION Buffer
+ )
+/*++
+
+Routine Description:
+
+ This routine sets the basic information for a file.
+
+Arguments:
+
+ pIrpContext - Supplies Irp context information.
+
+ Icb - Supplies the ICB for the file being modified.
+
+ Buffer - Supplies the buffer containing the data being set.
+
+Return Value:
+
+ NTSTATUS - Returns our completion status.
+
+--*/
+
+{
+ PFCB Fcb;
+ NTSTATUS Status;
+ BOOLEAN SetTime = FALSE;
+ BOOLEAN SetAttributes = FALSE;
+ ULONG LfnFlag = 0;
+
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "SetBasicInfo...\n", 0);
+
+ Fcb = Icb->SuperType.Fcb;
+
+ pIrpContext->pNpScb = Fcb->Scb->pNpScb;
+
+ //
+ // Append this IRP context and wait to get to the front.
+ // then grab from FCB
+ //
+
+ NwAppendToQueueAndWait( pIrpContext );
+ NwAcquireExclusiveFcb( Fcb->NonPagedFcb, TRUE );
+
+ //
+ // It is ok to attempt a reconnect if this request fails with a
+ // connection error.
+ //
+
+ SetFlag( pIrpContext->Flags, IRP_FLAG_RECONNECTABLE );
+
+ if (Buffer->CreationTime.QuadPart != 0) {
+
+ //
+ // Modify the creation time.
+ //
+
+ Status = NwNtTimeToNwDateTime(
+ Buffer->CreationTime,
+ &Fcb->CreationDate,
+ &Fcb->CreationTime );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NwReleaseFcb( Fcb->NonPagedFcb );
+ return( Status );
+ }
+
+ SetTime = TRUE;
+ LfnFlag |= LFN_FLAG_SET_INFO_CREATE_DATE | LFN_FLAG_SET_INFO_CREATE_TIME;
+ }
+
+ if (Buffer->LastAccessTime.QuadPart != 0) {
+
+ USHORT Dummy;
+
+ //
+ // Modify the last access time.
+ //
+
+ Status = NwNtTimeToNwDateTime(
+ Buffer->LastAccessTime,
+ &Fcb->LastAccessDate,
+ &Dummy );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NwReleaseFcb( Fcb->NonPagedFcb );
+ return( Status );
+ }
+
+ SetTime = TRUE;
+ LfnFlag |= LFN_FLAG_SET_INFO_LASTACCESS_DATE;
+
+ // Set the last access flag in the ICB so that we update
+ // last access time for real when we close this handle!
+
+ Icb->UserSetLastAccessTime = TRUE;
+ }
+
+ if (Buffer->LastWriteTime.QuadPart != 0) {
+
+ //
+ // Modify the last write time
+ //
+
+ Status = NwNtTimeToNwDateTime(
+ Buffer->LastWriteTime,
+ &Fcb->LastModifiedDate,
+ &Fcb->LastModifiedTime );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NwReleaseFcb( Fcb->NonPagedFcb );
+ return( Status );
+ }
+
+ LfnFlag |= LFN_FLAG_SET_INFO_MODIFY_DATE | LFN_FLAG_SET_INFO_MODIFY_TIME;
+ }
+
+
+ if (Buffer->FileAttributes != 0) {
+ LfnFlag |= LFN_FLAG_SET_INFO_ATTRIBUTES;
+ }
+
+ if ( LfnFlag == 0 ) {
+
+ //
+ // Nothing to set, simply return success.
+ //
+
+ Status = STATUS_SUCCESS;
+ }
+
+ if ( Fcb->NodeTypeCode == NW_NTC_FCB ) {
+
+ //
+ // Call plain FlushCache - we don't want to acquire and
+ // release the NpFcb. We are already at the front and have the Fcb
+ // exclusive.
+ //
+
+ FlushCache( pIrpContext, Fcb->NonPagedFcb );
+ }
+
+ if ( BooleanFlagOn( Fcb->Flags, FCB_FLAGS_LONG_NAME ) ) {
+
+ Status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "LbbWDW--WW==WW==_W_bDbC",
+ NCP_LFN_SET_INFO,
+ Fcb->Vcb->Specific.Disk.LongNameSpace,
+ Fcb->Vcb->Specific.Disk.LongNameSpace,
+ Fcb->NodeTypeCode == NW_NTC_FCB ?
+ SEARCH_ALL_FILES : SEARCH_ALL_DIRECTORIES,
+ LfnFlag,
+ NtAttributesToNwAttributes( Buffer->FileAttributes ),
+ Fcb->CreationDate,
+ Fcb->CreationTime,
+ Fcb->LastModifiedDate,
+ Fcb->LastModifiedTime,
+ 8,
+ Fcb->LastAccessDate,
+ 8,
+ Fcb->Vcb->Specific.Disk.VolumeNumber,
+ Fcb->Vcb->Specific.Disk.Handle,
+ 0,
+ &Fcb->RelativeFileName );
+
+ } else {
+
+ if ( LfnFlag & LFN_FLAG_SET_INFO_ATTRIBUTES ) {
+ Status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "FbbbU",
+ NCP_SET_FILE_ATTRIBUTES,
+ NtAttributesToNwAttributes( Buffer->FileAttributes ),
+ Fcb->Vcb->Specific.Disk.Handle,
+ Fcb->NodeTypeCode == NW_NTC_FCB ?
+ SEARCH_ALL_FILES : SEARCH_ALL_DIRECTORIES,
+ &Fcb->RelativeFileName );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ NwReleaseFcb( Fcb->NonPagedFcb );
+ return( Status );
+ }
+
+ }
+
+#if 0
+ //
+ // We could conceivably use ScanDir/SetDir to update last access
+ // and create time. Not supported yet.
+ //
+
+ if ( LfnFlag & ( LFN_FLAG_SET_INFO_LASTACCESS_DATE | LFN_FLAG_SET_INFO_CREATE_DATE ) ) {
+
+ ULONG SearchIndex;
+ ULONG Directory;
+
+ Status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "SbbdU",
+ 0x16, 0x1E, // Scan dir entry
+ Fcb->Vcb->Specific.Disk.Handle,
+ 0x06, // Search attributes
+ -1, // Search index
+ &Fcb->RelativeFileName );
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse(
+ pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "Ndd",
+ &SearchIndex,
+ &Directory );
+ }
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "Sbbdddw=----_ww==ww==ww",
+ 0x16, 0x25, // Set dir entry
+ Fcb->Vcb->Specific.Disk.Handle,
+ 0x06, // Search attributes
+ SearchIndex,
+ 0, // Change Bits?
+ Directory,
+ 12,
+ Fcb->CreationDate,
+ 0,
+ Fcb->LastAccessDate,
+ 0,
+ Fcb->LastModifiedDate,
+ Fcb->LastModifiedTime );
+ }
+ }
+#endif
+
+ if ( LfnFlag & LFN_FLAG_SET_INFO_MODIFY_DATE ) {
+ Status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "F-rww-",
+ NCP_SET_FILE_TIME,
+ &Icb->Handle, sizeof( Icb->Handle ),
+ Fcb->LastModifiedTime,
+ Fcb->LastModifiedDate );
+ }
+ }
+
+ NwReleaseFcb( Fcb->NonPagedFcb );
+
+ //
+ // And return to our caller
+ //
+
+ return Status;
+}
+
+
+NTSTATUS
+NwSetDispositionInfo (
+ IN PIRP_CONTEXT pIrpContext,
+ IN PICB Icb,
+ IN PFILE_DISPOSITION_INFORMATION Buffer
+ )
+/*++
+
+Routine Description:
+
+ This routine sets the disposition information for a file.
+
+Arguments:
+
+ pIrpContext - Supplies Irp context information.
+
+ Icb - Supplies the ICB for the file being modified.
+
+ Buffer - Supplies the buffer containing the data being set.
+
+Return Value:
+
+ NTSTATUS - Returns our completion status.
+
+--*/
+{
+ PFCB Fcb;
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "SetDispositionInfo...\n", 0);
+
+ Fcb = Icb->SuperType.Fcb;
+
+ if ( FlagOn( Fcb->Vcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) {
+
+ //
+ // This is a print queue, just pretend this IRP succeeded.
+ //
+
+ Status = STATUS_SUCCESS;
+
+ } else {
+
+ //
+ // This is a real file or directory. Mark it delete pending.
+ //
+
+ SetFlag( Fcb->Flags, FCB_FLAGS_DELETE_ON_CLOSE );
+
+ pIrpContext->pNpScb = Fcb->Scb->pNpScb;
+ pIrpContext->Icb = Icb;
+
+ Icb->State = ICB_STATE_CLOSE_PENDING;
+
+ //
+ // Go ahead, delete the file.
+ //
+
+ Status = NwDeleteFile( pIrpContext );
+ }
+
+ return( Status );
+}
+
+NTSTATUS
+NwDeleteFile(
+ PIRP_CONTEXT pIrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine continues processing of the SetDispositionInfo request.
+ It must run in the redirector FSP.
+
+Arguments:
+
+ pIrpContext - A pointer to the IRP context information for the
+ request in progress.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+{
+ PICB Icb;
+ PFCB Fcb;
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ Icb = pIrpContext->Icb;
+ Fcb = Icb->SuperType.Fcb;
+
+#if 0 // BUGBUG Was I on drugs? Below seems to be false, remove the check
+ ASSERT ( BooleanFlagOn( Fcb->Flags, FCB_FLAGS_ATTRIBUTES_ARE_VALID ) );
+
+ //
+ // Do not allow delete of read-only file, the netware server will
+ // allow it.
+ //
+
+ if ( Icb->NpFcb->Attributes & NW_ATTRIBUTE_READ_ONLY ) {
+ return( STATUS_ACCESS_DENIED );
+ }
+#endif
+
+ ClearFlag( Fcb->Flags, FCB_FLAGS_DELETE_ON_CLOSE );
+
+ //
+ // To a delete a file, first close the remote handle.
+ //
+
+ if ( Icb->HasRemoteHandle ) {
+
+ Icb->HasRemoteHandle = FALSE;
+
+ Status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "F-r",
+ NCP_CLOSE,
+ Icb->Handle, sizeof( Icb->Handle ) );
+ }
+
+ //
+ // Note that this request cannot be reconnectable since, it can
+ // be called via NwCloseIcb(). See comment in that routine for
+ // more info.
+ //
+
+ if ( Fcb->NodeTypeCode == NW_NTC_FCB ) {
+
+ if ( !BooleanFlagOn( Fcb->Flags, FCB_FLAGS_LONG_NAME ) ) {
+
+ Status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "FbbJ",
+ NCP_DELETE_FILE,
+ Fcb->Vcb->Specific.Disk.Handle,
+ SEARCH_ALL_FILES,
+ &Fcb->RelativeFileName );
+
+ } else {
+
+ Status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "LbbW-DbC",
+ NCP_LFN_DELETE_FILE,
+ Fcb->Vcb->Specific.Disk.LongNameSpace,
+ Fcb->Vcb->Specific.Disk.VolumeNumber,
+ NW_ATTRIBUTE_SYSTEM | NW_ATTRIBUTE_HIDDEN,
+ Fcb->Vcb->Specific.Disk.Handle,
+ LFN_FLAG_SHORT_DIRECTORY,
+ &Fcb->RelativeFileName );
+ }
+
+ } else {
+
+ ASSERT( Fcb->NodeTypeCode == NW_NTC_DCB );
+
+ if ( !BooleanFlagOn( Fcb->Flags, FCB_FLAGS_LONG_NAME ) ) {
+
+ Status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "SbbJ",
+ NCP_DIR_FUNCTION, NCP_DELETE_DIRECTORY,
+ Fcb->Vcb->Specific.Disk.Handle,
+ SEARCH_ALL_DIRECTORIES,
+ &Fcb->RelativeFileName );
+ } else {
+
+ Status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "LbbW-DbC",
+ NCP_LFN_DELETE_FILE,
+ Fcb->Vcb->Specific.Disk.LongNameSpace,
+ Fcb->Vcb->Specific.Disk.VolumeNumber,
+ SEARCH_ALL_DIRECTORIES,
+ Fcb->Vcb->Specific.Disk.Handle,
+ LFN_FLAG_SHORT_DIRECTORY,
+ &Fcb->RelativeFileName );
+ }
+
+ }
+
+ if ( NT_SUCCESS( Status )) {
+
+ Status = ParseResponse(
+ pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "N" );
+
+ } else {
+
+ //
+ // We can map all failures to STATUS_NO_SUCH_FILE
+ // except ACCESS_DENIED, which happens with a read
+ // only file.
+ //
+
+ if ( Status != STATUS_ACCESS_DENIED ) {
+ Status = STATUS_NO_SUCH_FILE;
+ }
+
+ }
+
+ return Status;
+}
+
+NTSTATUS
+NwSetRenameInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_RENAME_INFORMATION Buffer
+ )
+/*++
+
+Routine Description:
+
+ This routine set rename information for a file.
+
+Arguments:
+
+ pIrpContext - A pointer to the IRP context information for the
+ request in progress.
+
+ Icb - A pointer to the ICB of the file to set.
+
+ Buffer - The request buffer.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+{
+ PIRP Irp;
+ PIO_STACK_LOCATION irpSp;
+ NTSTATUS Status;
+ NTSTATUS Status2;
+ PFCB Fcb;
+ PFCB TargetFcb;
+ BOOLEAN HandleAllocated = FALSE;
+ BYTE Handle;
+ PICB TargetIcb = NULL;
+
+ UNICODE_STRING OldDrive;
+ UNICODE_STRING OldServer;
+ UNICODE_STRING OldVolume;
+ UNICODE_STRING OldPath;
+ UNICODE_STRING OldFileName;
+ UNICODE_STRING OldFullName;
+ WCHAR OldDriveLetter;
+ UNICODE_STRING OldFcbFullName;
+
+ UNICODE_STRING NewDrive;
+ UNICODE_STRING NewServer;
+ UNICODE_STRING NewVolume;
+ UNICODE_STRING NewPath;
+ UNICODE_STRING NewFileName;
+ UNICODE_STRING NewFullName;
+ WCHAR NewDriveLetter;
+ UNICODE_STRING NewFcbFullName;
+
+ USHORT i;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "SetRenameInfo...\n", 0);
+
+ //
+ // Can't try to set rename info on a print queue.
+ //
+
+ Fcb = Icb->SuperType.Fcb;
+
+ if ( FlagOn( Fcb->Vcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) {
+ return( STATUS_INVALID_PARAMETER );
+ }
+
+ //
+ // It is ok to attempt a reconnect if this request fails with a
+ // connection error.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_RECONNECTABLE );
+
+ //
+ // Get the current stack location.
+ //
+
+ Irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ DebugTrace( 0, Dbg, " ->FullFileName = %wZ\n",
+ &Fcb->FullFileName);
+
+ if (irpSp->Parameters.SetFile.FileObject != NULL) {
+
+ TargetIcb = irpSp->Parameters.SetFile.FileObject->FsContext2;
+
+ DebugTrace( 0, Dbg, " ->FullFileName = %wZ\n",
+ &TargetIcb->SuperType.Fcb->FullFileName);
+
+ if ( TargetIcb->SuperType.Fcb->Scb != Icb->SuperType.Fcb->Scb ) {
+ return STATUS_NOT_SAME_DEVICE;
+ }
+
+ } else {
+
+ DebugTrace( 0, Dbg, " ->FullFileName in users buffer\n", 0);
+ DebugTrace(-1, Dbg, "SetRenameInfo %08lx\n", STATUS_NOT_IMPLEMENTED);
+ return STATUS_NOT_IMPLEMENTED;
+ }
+
+ DebugTrace( 0, Dbg, " ->TargetFileName = %wZ\n",
+ &irpSp->Parameters.SetFile.FileObject->FileName);
+
+ TargetFcb = ((PNONPAGED_FCB)irpSp->Parameters.SetFile.FileObject->FsContext)->Fcb;
+
+
+ IrpContext->pNpScb = Fcb->Scb->pNpScb;
+
+ NwAppendToQueueAndWait( IrpContext );
+ NwAcquireExclusiveFcb( Fcb->NonPagedFcb, TRUE );
+
+ try {
+
+ //
+ // If either source or destination is a long name, use
+ // the long name path.
+ //
+
+ if ( !BooleanFlagOn( Fcb->Flags, FCB_FLAGS_LONG_NAME ) &&
+ IsFatNameValid( &TargetFcb->RelativeFileName ) &&
+ !BooleanFlagOn( Fcb->Vcb->Flags, VCB_FLAG_LONG_NAME ) ) {
+
+ //
+ // Strip to UID portion of the FCB name.
+ //
+
+ for ( i = 0 ; i < Fcb->FullFileName.Length / sizeof(WCHAR) ; i++ ) {
+ if ( Fcb->FullFileName.Buffer[i] == OBJ_NAME_PATH_SEPARATOR ) {
+ break;
+ }
+ }
+
+ ASSERT( Fcb->FullFileName.Buffer[i] == OBJ_NAME_PATH_SEPARATOR );
+
+ OldFcbFullName.Length = Fcb->FullFileName.Length - i*sizeof(WCHAR);
+ OldFcbFullName.Buffer = Fcb->FullFileName.Buffer + i;
+
+ Status = CrackPath (
+ &OldFcbFullName,
+ &OldDrive,
+ &OldDriveLetter,
+ &OldServer,
+ &OldVolume,
+ &OldPath,
+ &OldFileName,
+ &OldFullName );
+
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // Strip to UID portion of the FCB name.
+ //
+
+ TargetFcb = ((PNONPAGED_FCB)(irpSp->Parameters.SetFile.FileObject->FsContext))->Fcb;
+
+ for ( i = 0 ; i < TargetFcb->FullFileName.Length / sizeof(WCHAR) ; i++ ) {
+ if ( TargetFcb->FullFileName.Buffer[i] == OBJ_NAME_PATH_SEPARATOR ) {
+ break;
+ }
+ }
+
+ ASSERT( TargetFcb->FullFileName.Buffer[i] == OBJ_NAME_PATH_SEPARATOR );
+
+ NewFcbFullName.Length = TargetFcb->FullFileName.Length - i*sizeof(WCHAR);
+ NewFcbFullName.Buffer = TargetFcb->FullFileName.Buffer + i;
+
+ Status = CrackPath (
+ &NewFcbFullName,
+ &NewDrive,
+ &NewDriveLetter,
+ &NewServer,
+ &NewVolume,
+ &NewPath,
+ &NewFileName,
+ &NewFullName );
+
+ ASSERT(NT_SUCCESS(Status));
+
+ //
+ // Make sure that this is the same volume.
+ //
+
+ if ( RtlCompareUnicodeString( &NewVolume, &OldVolume, TRUE ) != 0 ) {
+ try_return( Status = STATUS_NOT_SAME_DEVICE );
+ }
+
+ if (Icb->SuperType.Fcb->IcbCount != 1) {
+ try_return( Status = STATUS_ACCESS_DENIED );
+ }
+
+ //
+ // After a rename, the only operation allowed on the handle is an
+ // NtClose.
+ //
+
+ Icb->State = ICB_STATE_CLOSE_PENDING;
+
+ if ((irpSp->Parameters.SetFile.ReplaceIfExists ) &&
+ (TargetIcb->Exists)) {
+
+ // Delete the file
+
+ Status2 = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "Fb-J",
+ NCP_DELETE_FILE,
+ TargetFcb->Vcb->Specific.Disk.Handle,
+ &TargetFcb->RelativeFileName );
+
+#ifdef NWDBG
+ if ( NT_SUCCESS( Status2 ) ) {
+ Status2 = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N" );
+ }
+
+ ASSERT(NT_SUCCESS(Status2));
+#endif
+ }
+
+ //
+ // Need to create a handle to the directory containing the old
+ // file/directory name because directory rename does not contain a
+ // path and there might not be room for two paths in a file rename.
+ //
+ // The way we do this is to allocate a temporary handle on the server.
+ // This request is at the front of the Scb->Requests queue and so can
+ // use the temporary handle and delete it without affecting any other
+ // requests.
+ //
+
+ if ( OldPath.Length == 0 ) {
+
+ // In the root so use the VCB handle.
+
+ Handle = Fcb->Vcb->Specific.Disk.Handle;
+
+ } else {
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "SbbJ", // NCP Allocate temporary directory handle
+ NCP_DIR_FUNCTION, NCP_ALLOCATE_TEMP_DIR_HANDLE,
+ Fcb->Vcb->Specific.Disk.Handle,
+ '[',
+ &OldPath );
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nb",
+ &Handle );
+ }
+
+ if (!NT_SUCCESS(Status)) {
+ try_return(Status);
+ }
+
+ HandleAllocated = TRUE;
+ }
+
+ if ( Fcb->NodeTypeCode == NW_NTC_DCB ) {
+
+ //
+ // We can only rename files in the same directory
+ //
+
+ if ( RtlCompareUnicodeString( &NewPath, &OldPath, TRUE ) != 0 ) {
+ try_return(Status = STATUS_NOT_SUPPORTED);
+
+ } else {
+
+ Status = ExchangeWithWait ( IrpContext,
+ SynchronousResponseCallback,
+ "SbJJ",
+ NCP_DIR_FUNCTION, NCP_RENAME_DIRECTORY,
+ Handle,
+ &OldFileName,
+ &NewFileName);
+ }
+
+ } else {
+
+ //
+ // We have to close the handle associated with the Icb that
+ // is doing the rename. Close that handle or the rename will
+ // fail for sure.
+ //
+
+ if ( Icb->HasRemoteHandle ) {
+
+ Status2 = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "F-r",
+ NCP_CLOSE,
+ Icb->Handle, sizeof( Icb->Handle ) );
+
+ Icb->HasRemoteHandle = FALSE;
+
+#ifdef NWDBG
+ if ( NT_SUCCESS( Status2 ) ) {
+ Status2 = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N" );
+ }
+
+ ASSERT(NT_SUCCESS(Status2));
+#endif
+ }
+
+ //
+ // Do the file rename Ncp.
+ //
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "FbbJbJ",
+ NCP_RENAME_FILE,
+ Handle,
+ SEARCH_ALL_FILES,
+ &OldFileName,
+ Fcb->Vcb->Specific.Disk.Handle,
+ &NewFullName);
+ }
+
+ } else {
+
+ //
+ // We are going through the long name path. Ensure that the
+ // VCB supports long names.
+ //
+
+ if ( Icb->SuperType.Fcb->Vcb->Specific.Disk.LongNameSpace ==
+ LFN_NO_OS2_NAME_SPACE) {
+ try_return( Status = STATUS_OBJECT_PATH_SYNTAX_BAD );
+ }
+
+ if (Icb->SuperType.Fcb->IcbCount != 1) {
+ try_return( Status = STATUS_ACCESS_DENIED);
+ }
+
+ //
+ // After a rename, the only operation allowed on the handle is an
+ // NtClose.
+ //
+
+ Icb->State = ICB_STATE_CLOSE_PENDING;
+
+ if ((irpSp->Parameters.SetFile.ReplaceIfExists ) &&
+ (TargetIcb->Exists)) {
+
+ // Delete the file
+
+ Status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "LbbW-DbC",
+ NCP_LFN_DELETE_FILE,
+ TargetFcb->Vcb->Specific.Disk.LongNameSpace,
+ TargetFcb->Vcb->Specific.Disk.VolumeNumber,
+ SEARCH_ALL_FILES,
+ TargetFcb->Vcb->Specific.Disk.Handle,
+ LFN_FLAG_SHORT_DIRECTORY,
+ &TargetFcb->RelativeFileName );
+
+#ifdef NWDBG
+ if ( NT_SUCCESS( Status ) ) {
+ Status2 = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N" );
+ }
+
+ ASSERT(NT_SUCCESS(Status2));
+#endif
+ }
+
+ if ( Fcb->NodeTypeCode == NW_NTC_DCB ) {
+
+ //
+ // We can only rename files in the same directory
+ //
+
+ if ( Fcb->Vcb != TargetFcb->Vcb ) {
+ try_return(Status = STATUS_NOT_SUPPORTED);
+
+ } else {
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "LbbWbDbbbDbbNN",
+ NCP_LFN_RENAME_FILE,
+ Fcb->Vcb->Specific.Disk.LongNameSpace,
+ 0, // Rename flag
+ SEARCH_ALL_DIRECTORIES,
+ Fcb->Vcb->Specific.Disk.VolumeNumber,
+ Fcb->Vcb->Specific.Disk.Handle,
+ LFN_FLAG_SHORT_DIRECTORY,
+ OccurenceCount( &Fcb->RelativeFileName, OBJ_NAME_PATH_SEPARATOR ) + 1,
+ Fcb->Vcb->Specific.Disk.VolumeNumber,
+ Fcb->Vcb->Specific.Disk.Handle,
+ LFN_FLAG_SHORT_DIRECTORY,
+ OccurenceCount( &TargetFcb->RelativeFileName, OBJ_NAME_PATH_SEPARATOR ) + 1,
+ &Fcb->RelativeFileName,
+ &TargetFcb->RelativeFileName );
+ }
+
+ } else {
+
+ //
+ // We have to close the handle associated with the Icb that
+ // is doing the rename. Close that handle or the rename will
+ // fail for sure.
+ //
+
+ if ( Icb->HasRemoteHandle ) {
+
+ Status2 = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "F-r",
+ NCP_CLOSE,
+ Icb->Handle, sizeof( Icb->Handle ) );
+
+ Icb->HasRemoteHandle = FALSE;
+
+#ifdef NWDBG
+ if ( NT_SUCCESS( Status2 ) ) {
+ Status2 = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N" );
+ }
+
+ ASSERT(NT_SUCCESS(Status2));
+#endif
+ }
+
+ //
+ // Do the file rename Ncp.
+ //
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "LbbWbDbbbDbbNN",
+ NCP_LFN_RENAME_FILE,
+ Fcb->Vcb->Specific.Disk.LongNameSpace,
+ 0, // Rename flag
+ SEARCH_ALL_FILES,
+ Fcb->Vcb->Specific.Disk.VolumeNumber,
+ Fcb->Vcb->Specific.Disk.Handle,
+ LFN_FLAG_SHORT_DIRECTORY,
+ OccurenceCount( &Fcb->RelativeFileName, OBJ_NAME_PATH_SEPARATOR ) + 1,
+ Fcb->Vcb->Specific.Disk.VolumeNumber,
+ Fcb->Vcb->Specific.Disk.Handle,
+ LFN_FLAG_SHORT_DIRECTORY,
+ OccurenceCount( &TargetFcb->RelativeFileName, OBJ_NAME_PATH_SEPARATOR ) + 1,
+ &Fcb->RelativeFileName,
+ &TargetFcb->RelativeFileName );
+ }
+ }
+
+try_exit: NOTHING;
+ } finally {
+
+ if (HandleAllocated) {
+
+ Status2 = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "Sb", // NCP Deallocate directory handle
+ NCP_DIR_FUNCTION, NCP_DEALLOCATE_DIR_HANDLE,
+ Handle);
+#ifdef NWDBG
+ if ( NT_SUCCESS( Status2 ) ) {
+ Status2 = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N" );
+ }
+
+ ASSERT(NT_SUCCESS(Status2));
+#endif
+
+ }
+
+ NwReleaseFcb( Fcb->NonPagedFcb );
+ }
+
+ DebugTrace(-1, Dbg, "SetRenameInfo %08lx\n", Status );
+
+ //
+ // We're done with this request. Dequeue the IRP context from
+ // SCB and complete the request.
+ //
+
+ if ( Status != STATUS_PENDING ) {
+ NwDequeueIrpContext( IrpContext, FALSE );
+ }
+
+ return Status;
+}
+
+NTSTATUS
+NwSetPositionInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_POSITION_INFORMATION Buffer
+ )
+/*++
+
+Routine Description:
+
+ This routine sets position information for a file.
+
+Arguments:
+
+ pIrpContext - A pointer to the IRP context information for the
+ request in progress.
+
+ Icb - A pointer to the ICB of the file to set.
+
+ Buffer - The request buffer.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+{
+ PAGED_CODE();
+
+ ASSERT( Buffer->CurrentByteOffset.HighPart == 0 );
+
+ if ( Icb->FileObject ) {
+ Icb->FileObject->CurrentByteOffset.QuadPart = Buffer->CurrentByteOffset.QuadPart;
+ }
+
+ return( STATUS_SUCCESS );
+}
+
+
+NTSTATUS
+NwSetAllocationInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_ALLOCATION_INFORMATION Buffer
+ )
+/*++
+
+Routine Description:
+
+ This routine sets allocation information for a file.
+
+Arguments:
+
+ pIrpContext - A pointer to the IRP context information for the
+ request in progress.
+
+ Icb - A pointer to the ICB of the file to set.
+
+ Buffer - The request buffer.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+{
+ NTSTATUS Status;
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ PFCB fcb = (PFCB)Icb->SuperType.Fcb;
+ PULONG pFileSize;
+
+ PAGED_CODE();
+
+ ASSERT( Buffer->AllocationSize.HighPart == 0);
+
+ if ( fcb->NodeTypeCode == NW_NTC_FCB ) {
+
+ pFileSize = &Icb->NpFcb->Header.FileSize.LowPart;
+
+ IrpContext->pNpScb = fcb->Scb->pNpScb;
+
+ if (BooleanFlagOn( fcb->Vcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) {
+
+ return STATUS_SUCCESS;
+
+ }
+
+ } else if ( fcb->NodeTypeCode == NW_NTC_SCB ) {
+
+ pFileSize = &Icb->FileSize;
+
+ IrpContext->pNpScb = ((PSCB)fcb)->pNpScb;
+
+ } else {
+
+ DebugTrace(0, Dbg, "Not a file or a server\n", 0);
+
+ DebugTrace( 0, Dbg, "NwSetAllocationInfo -> %08lx\n", STATUS_INVALID_PARAMETER );
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ if ( !Icb->HasRemoteHandle ) {
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ } else if ( Buffer->AllocationSize.LowPart == *pFileSize ) {
+
+ Status = STATUS_SUCCESS;
+
+ } else {
+
+ irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+#ifndef QFE_BUILD
+ if ( Buffer->AllocationSize.LowPart < *pFileSize ) {
+
+ //
+ // Before we actually truncate, check to see if the purge
+ // is going to fail.
+ //
+
+ if (!MmCanFileBeTruncated( irpSp->FileObject->SectionObjectPointer,
+ &Buffer->AllocationSize )) {
+
+ return( STATUS_USER_MAPPED_FILE );
+ }
+ }
+#endif
+
+ if ( fcb->NodeTypeCode == NW_NTC_FCB ) {
+ AcquireFcbAndFlushCache( IrpContext, fcb->NonPagedFcb );
+ }
+
+ Status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "F-rd=",
+ NCP_WRITE_FILE,
+ &Icb->Handle, sizeof( Icb->Handle ),
+ Buffer->AllocationSize.LowPart );
+
+ if ( NT_SUCCESS( Status ) ) {
+ *pFileSize = Buffer->AllocationSize.LowPart;
+ }
+ }
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+
+ return( Status );
+}
+
+NTSTATUS
+NwSetEndOfFileInfo (
+ IN PIRP_CONTEXT IrpContext,
+ IN PICB Icb,
+ IN PFILE_END_OF_FILE_INFORMATION Buffer
+ )
+/*++
+
+Routine Description:
+
+ This routine sets end of file information for a file.
+
+Arguments:
+
+ pIrpContext - A pointer to the IRP context information for the
+ request in progress.
+
+ Icb - A pointer to the ICB of the file to set.
+
+ Buffer - The request buffer.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+{
+ NTSTATUS Status;
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ PFCB fcb = (PFCB)Icb->SuperType.Fcb;
+ PULONG pFileSize;
+
+ PAGED_CODE();
+
+ ASSERT( Buffer->EndOfFile.HighPart == 0);
+
+ if ( fcb->NodeTypeCode == NW_NTC_FCB ) {
+
+ pFileSize = &Icb->NpFcb->Header.FileSize.LowPart;
+
+ IrpContext->pNpScb = fcb->Scb->pNpScb;
+
+ if (BooleanFlagOn( fcb->Vcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) {
+
+ return STATUS_SUCCESS;
+
+ }
+
+ } else if ( fcb->NodeTypeCode == NW_NTC_SCB ) {
+
+ pFileSize = &Icb->FileSize;
+
+ IrpContext->pNpScb = ((PSCB)fcb)->pNpScb;
+
+ } else {
+
+ DebugTrace(0, Dbg, "Not a file or a server\n", 0);
+
+ DebugTrace( 0, Dbg, "NwSetAllocationInfo -> %08lx\n", STATUS_INVALID_PARAMETER );
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ if ( !Icb->HasRemoteHandle ) {
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ } else if ( Buffer->EndOfFile.LowPart == *pFileSize ) {
+
+ Status = STATUS_SUCCESS;
+
+ } else {
+
+ irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+#ifndef QFE_BUILD
+
+ if ( Buffer->EndOfFile.LowPart < *pFileSize ) {
+
+ //
+ // Before we actually truncate, check to see if the purge
+ // is going to fail.
+ //
+
+ if (!MmCanFileBeTruncated( irpSp->FileObject->SectionObjectPointer,
+ &Buffer->EndOfFile )) {
+
+ return( STATUS_USER_MAPPED_FILE );
+ }
+ }
+#endif
+
+ if ( fcb->NodeTypeCode == NW_NTC_FCB ) {
+ AcquireFcbAndFlushCache( IrpContext, fcb->NonPagedFcb );
+ }
+
+ Status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "F-rd=",
+ NCP_WRITE_FILE,
+ &Icb->Handle, sizeof( Icb->Handle ),
+ Buffer->EndOfFile.LowPart );
+
+ if ( NT_SUCCESS( Status ) ) {
+ *pFileSize = Buffer->EndOfFile.LowPart;
+ }
+ }
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+
+ return( Status );
+}
+
+
+ULONG
+OccurenceCount (
+ IN PUNICODE_STRING String,
+ IN WCHAR SearchChar
+ )
+/*++
+
+Routine Description:
+
+ This routine counts the number of occurences of a search character
+ in a string
+
+Arguments:
+
+ String - The string to search
+
+ SearchChar - The character to search for.
+
+Return Value:
+
+ The occurence count.
+
+--*/
+{
+ PWCH currentChar;
+ PWCH endOfString;
+ ULONG count = 0;
+
+ PAGED_CODE();
+
+ currentChar = String->Buffer;
+ endOfString = &String->Buffer[ String->Length / sizeof(WCHAR) ];
+
+ while ( currentChar < endOfString ) {
+ if ( *currentChar == SearchChar ) {
+ count++;
+ }
+ currentChar++;
+ }
+
+ return( count );
+}
diff --git a/private/nw/rdr/filobsup.c b/private/nw/rdr/filobsup.c
new file mode 100644
index 000000000..64a5787ff
--- /dev/null
+++ b/private/nw/rdr/filobsup.c
@@ -0,0 +1,175 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ filobsup.c
+
+Abstract:
+
+ This module implements the Netware Redirector object support routines.
+
+Author:
+
+ Manny Weiser (mannyw) 10-Feb-1993
+
+Revision History:
+
+--*/
+
+#include "procs.h"
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_FILOBSUP)
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwSetFileObject )
+#pragma alloc_text( PAGE, NwDecodeFileObject )
+#endif
+
+
+VOID
+NwSetFileObject (
+ IN PFILE_OBJECT FileObject OPTIONAL,
+ IN PVOID FsContext,
+ IN PVOID FsContext2
+ )
+
+/*++
+
+Routine Description:
+
+ This routine sets the file system pointers within the file object.
+
+Arguments:
+
+ FileObject - Supplies a pointer to the file object being modified, and
+ can optionally be null.
+
+ FsContext - Supplies a pointer to either an icb, fcb, vcb, or dcb
+ structure.
+
+ FsContext2 - Supplies a pointer to a icb, or is null.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwSetFileObject, FileObject = %08lx\n", (ULONG)FileObject );
+
+ //
+ // Set the fscontext fields of the file object.
+ //
+
+ FileObject->FsContext = FsContext;
+ FileObject->FsContext2 = FsContext2;
+
+ DebugTrace(-1, Dbg, "NwSetFileObject -> VOID\n", 0);
+
+ return;
+}
+
+
+NODE_TYPE_CODE
+NwDecodeFileObject (
+ IN PFILE_OBJECT FileObject,
+ OUT PVOID *FsContext,
+ OUT PVOID *FsContext2
+ )
+
+/*++
+
+Routine Description:
+
+ This procedure takes a pointer to a file object, that has already been
+ opened by the mailslot file system and figures out what it really
+ is opened.
+
+Arguments:
+
+ FileObject - Supplies the file object pointer being interrogated
+
+ FsContext - Receives a pointer to the FsContext pointer
+ FsContext2 - Receives a pointer to the FsContext2 pointer
+
+Return Value:
+
+ NODE_TYPE_CODE - Returns the node type code for a Rcb, Scb, Dcb, Icb,
+ or zero.
+
+ Rcb - indicates that file object opens the netware redirector device.
+
+ Scb - indicates that file object is for a server.
+
+ Dcb - indicates that the file object is for a directory.
+
+ Icb - indicates that the file object is for a file.
+
+ Zero - indicates that the file object was for a netware file
+ but has been closed.
+
+--*/
+
+{
+ NODE_TYPE_CODE NodeTypeCode = NTC_UNDEFINED;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwDecodeFileObject, FileObject = %08lx\n", (ULONG)FileObject);
+
+ //
+ // Read the fs FsContext fields of the file object.
+ //
+
+ *FsContext = FileObject->FsContext;
+ *FsContext2 = FileObject->FsContext2;
+
+ ASSERT ( *FsContext2 != NULL );
+ NodeTypeCode = NodeType( *FsContext2 );
+
+ DebugTrace(-1, Dbg, "NwDecodeFileObject -> %08lx\n", NodeTypeCode);
+ return NodeTypeCode;
+}
+
+BOOLEAN
+NwIsIrpTopLevel (
+ IN PIRP Irp
+ )
+/*++
+
+Routine Description:
+
+ This routine detects if an Irp is the Top level requestor, ie. if it is OK
+ to do a verify or pop-up now. If TRUE is returned, then no file system
+ resources are held above us.
+
+Arguments:
+
+ Irp - Supplies the Irp being processed
+
+ Status - Supplies the status to complete the Irp with
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ if ( NwGetTopLevelIrp() == NULL ) {
+ NwSetTopLevelIrp( Irp );
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
diff --git a/private/nw/rdr/fragex.c b/private/nw/rdr/fragex.c
new file mode 100644
index 000000000..948fb09f8
--- /dev/null
+++ b/private/nw/rdr/fragex.c
@@ -0,0 +1,783 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ FragEx.c
+
+Abstract:
+
+ This module implements the fragment exchanger routine for
+ netware directory services access.
+
+Author:
+
+ Cory West [CoryWest] 23-Feb-1995
+
+Revision History:
+
+--*/
+
+#include <stdarg.h>
+#include "Procs.h"
+
+#define Dbg (DEBUG_TRACE_EXCHANGE)
+
+#pragma alloc_text( PAGE, FragExWithWait )
+#pragma alloc_text( PAGE, FormatBuf )
+#pragma alloc_text( PAGE, FormatBufS )
+
+NTSTATUS
+_cdecl
+FragExWithWait(
+ IN PIRP_CONTEXT pIrpContext,
+ IN DWORD NdsVerb,
+ IN PLOCKED_BUFFER pReplyBuffer,
+ IN BYTE *NdsRequestStr,
+ ...
+)
+/*
+
+Routine Description:
+
+ Exchanges an NDS request in fragments and collects the fragments
+ of the response. The buffer passed in much be locked down for
+ the transport.
+
+Routine Arguments:
+
+ pIrpContext - A pointer to the context information for this IRP.
+ NdsVerb - The verb for that indicates the request.
+
+ pReplyBuffer - The locked down reply buffer.
+
+ NdsReqestStr - The format string for the arguments to this NDS request.
+ Arguments - The arguments that satisfy the NDS format string.
+
+Return Value:
+
+ NTSTATUS - Status of the exchange, but not the result code in the packet.
+
+*/
+{
+
+ NTSTATUS Status;
+
+ BYTE *NdsRequestBuf;
+ DWORD NdsRequestLen;
+
+ BYTE *NdsRequestFrag, *NdsReplyFrag;
+ DWORD NdsRequestBytesLeft, NdsReplyBytesLeft, NdsReplyLen;
+
+ va_list Arguments;
+
+ PMDL pMdlSendData = NULL,
+ pTxMdlFrag = NULL,
+ pRxMdlFrag = NULL;
+
+ PMDL pOrigMdl;
+ DWORD OrigRxMdlSize;
+
+ DWORD MaxFragSize, SendFragSize;
+ DWORD ReplyFragSize, ReplyFragHandle;
+
+ DWORD NdsFraggerHandle = DUMMY_ITER_HANDLE;
+
+ PAGED_CODE();
+
+ DebugTrace( 0 , Dbg, "Entering FragExWithWait...\n", 0 );
+
+ //
+ // Allocate conversation buffer for the request.
+ //
+
+ NdsRequestBuf = ALLOCATE_POOL( PagedPool, NDS_BUFFER_SIZE );
+
+ if ( !NdsRequestBuf ) {
+
+ DebugTrace( 0, Dbg, "No memory for request buffer...\n", 0 );
+ return STATUS_INSUFFICIENT_RESOURCES;
+
+ }
+
+ //
+ // Build the request in our local buffer. Reserve the first
+ // five DWORDs for the NDS request header.
+ //
+
+ if ( NdsRequestStr != NULL ) {
+
+ va_start( Arguments, NdsRequestStr );
+
+ NdsRequestFrag = (BYTE *) NdsRequestBuf + sizeof( NDS_REQUEST_HEADER );
+
+ NdsRequestLen = FormatBuf( NdsRequestFrag,
+ NDS_BUFFER_SIZE - sizeof( NDS_REQUEST_HEADER ),
+ NdsRequestStr,
+ Arguments );
+
+ if ( !NdsRequestLen ) {
+
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+
+ }
+
+ va_end( Arguments );
+
+ } else {
+
+ NdsRequestLen = 0;
+ }
+
+ //
+ // Pack in the NDS preamble now that we know the length.
+ //
+ // The second DWORD in the preamble is the size of the NDS
+ // request which includes the three DWORDs immediately
+ // following the size in the preamble.
+ //
+
+ MaxFragSize = pIrpContext->pNpScb->BufferSize -
+ ( sizeof( NCP_REQUEST_WITH_SUB ) +
+ sizeof( NDS_REPLY_HEADER ) );
+
+ FormatBufS( NdsRequestBuf,
+ 5 * sizeof( DWORD ),
+ "DDDDD",
+ MaxFragSize, // max fragment size
+ NdsRequestLen + ( 3 * sizeof( DWORD ) ), // request size
+ 0, // fragment flags
+ NdsVerb, // nds verb
+ pReplyBuffer->dwRecvLen ); // reply buffer size
+
+ NdsRequestLen += sizeof( NDS_REQUEST_HEADER );
+
+ //
+ // Map the entire request to the SendData mdl and lock it down.
+ // we'll build partials into this data chunk as we proceed.
+ //
+
+ pMdlSendData = ALLOCATE_MDL( NdsRequestBuf,
+ NdsRequestLen,
+ FALSE,
+ FALSE,
+ NULL );
+
+ if ( !pMdlSendData ) {
+
+ DebugTrace( 0, Dbg, "Failed to allocate the request mdl...\n", 0 );
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ExitWithCleanup;
+ }
+
+ try {
+
+ MmProbeAndLockPages( pMdlSendData, KernelMode, IoReadAccess );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Failed to lock request data in FragExWithWait!\n", 0 );
+ Status = GetExceptionCode();
+ goto ExitWithCleanup;
+
+ }
+
+ //
+ // Allocate space for send and receive partial mdls.
+ //
+
+ pTxMdlFrag = ALLOCATE_MDL( NdsRequestBuf,
+ NdsRequestLen,
+ FALSE,
+ FALSE,
+ NULL );
+
+ if ( !pTxMdlFrag ) {
+
+ DebugTrace( 0, Dbg, "Failed to allocate a tx mdl for this fragment...\n", 0 );
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ExitWithCleanup;
+
+ }
+
+ pRxMdlFrag = ALLOCATE_MDL( pReplyBuffer->pRecvBufferVa,
+ pReplyBuffer->dwRecvLen,
+ FALSE,
+ FALSE,
+ NULL );
+
+ if ( !pRxMdlFrag ) {
+
+ DebugTrace( 0, Dbg, "Failed to allocate an rx mdl for this fragment...\n", 0 );
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ExitWithCleanup;
+
+ }
+
+ //
+ // Store the original RxMdl parameters and temporarily shorten it to hold
+ // only the response header.
+ //
+
+ pOrigMdl = pIrpContext->RxMdl->Next;
+ OrigRxMdlSize = MmGetMdlByteCount( pIrpContext->RxMdl );
+ pIrpContext->RxMdl->ByteCount = 16;
+
+ //
+ // The request is formatted, so set our internal pointers
+ // and start the exchange loop.
+ //
+
+ NdsReplyFrag = pReplyBuffer->pRecvBufferVa;
+ NdsReplyBytesLeft = pReplyBuffer->dwRecvLen;
+ NdsReplyLen = 0;
+
+ NdsRequestFrag = NdsRequestBuf;
+ NdsRequestBytesLeft = NdsRequestLen;
+
+ while ( TRUE ) {
+
+ //
+ // If there's more data to send in the request, set up the next MDL frag.
+ //
+
+ if ( NdsRequestBytesLeft ) {
+
+ if ( MaxFragSize < NdsRequestBytesLeft )
+ SendFragSize = MaxFragSize;
+ else
+ SendFragSize = NdsRequestBytesLeft;
+
+ IoBuildPartialMdl( pMdlSendData,
+ pTxMdlFrag,
+ NdsRequestFrag,
+ SendFragSize );
+
+ }
+
+ //
+ // Set up the response partial mdl with the buffer space that we have
+ // left. If we are here and there's no space left in the user's buffer,
+ // we're sort of hosed...
+ //
+
+ if ( !NdsReplyBytesLeft ) {
+
+ DebugTrace( 0, Dbg, "No room for fragment reply.\n", 0 );
+ Status = STATUS_BUFFER_OVERFLOW;
+ goto ExitWithCleanup;
+
+ }
+
+ IoBuildPartialMdl( pReplyBuffer->pRecvMdl,
+ pRxMdlFrag,
+ NdsReplyFrag,
+ NdsReplyBytesLeft );
+
+ pIrpContext->RxMdl->Next = pRxMdlFrag;
+ pRxMdlFrag->Next = NULL;
+
+ //
+ // Do this transaction.
+ //
+
+ SetFlag( pIrpContext->Flags, IRP_FLAG_RECONNECTABLE );
+
+ if ( NdsRequestBytesLeft ) {
+
+ Status = ExchangeWithWait( pIrpContext,
+ SynchronousResponseCallback,
+ "NDf",
+ NDS_REQUEST, // NDS Function 104
+ NDS_ACTION, // NDS Subfunction 2
+ NdsFraggerHandle, // frag handle from the last response
+ pTxMdlFrag ); // NDS MDL Fragment
+
+ NdsRequestBytesLeft -= SendFragSize;
+ NdsRequestFrag = (LPBYTE) NdsRequestFrag + SendFragSize;
+ MmPrepareMdlForReuse( pTxMdlFrag );
+
+ //
+ // We may reuse this irp context, so we have to clear the
+ // TxMdl chain (Exchange doesn't do it for us).
+ //
+
+ pIrpContext->TxMdl->Next = NULL;
+
+ } else {
+
+ //
+ // There were no more request bytes to send, so we must have be allowed
+ // to continue to request another response fragment. NdsFraggerHandle
+ // contains the fragger handle from the last response.
+ //
+
+ Status = ExchangeWithWait( pIrpContext,
+ SynchronousResponseCallback,
+ "ND", // We only care about the frag handle
+ NDS_REQUEST, // NDS Function 104
+ NDS_ACTION, // NDS Subfunction 2
+ NdsFraggerHandle ); // the frag handle from last response
+ }
+
+ ClearFlag( pIrpContext->Flags, IRP_FLAG_RECONNECTABLE );
+
+ //
+ // Success? Get the frag size and frag handle and see.
+ //
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ DebugTrace( 0, Dbg, "Failed to exchange the fragment.\n", 0 );
+ goto ExitWithCleanup;
+
+ }
+
+ Status = ParseResponse( pIrpContext,
+ pIrpContext->rsp, // mapped into first rxmdl
+ 16, // only 16 bytes available
+ "NDD",
+ &ReplyFragSize,
+ &ReplyFragHandle );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // We got that fragment and it's already in our buffer. We have to adjust
+ // the index pointers, reset the MDLs, and continue on. Remember, we don't
+ // have to include space for the fragger handle since we've already got it.
+ //
+
+ ReplyFragSize -= sizeof( DWORD );
+
+ NdsReplyBytesLeft -= ReplyFragSize;
+ NdsReplyFrag = (LPBYTE) NdsReplyFrag + ReplyFragSize;
+ NdsReplyLen += ReplyFragSize;
+ MmPrepareMdlForReuse( pRxMdlFrag );
+
+ //
+ // Inspect the fraghandle.
+ //
+
+ if ( ReplyFragHandle == DUMMY_ITER_HANDLE ) {
+
+ // We are done!
+ //
+ // Invariant: There is a valid NDS response in the NdsReply
+ // and Status is NT_SUCCESS.
+
+ pReplyBuffer->dwBytesWritten = NdsReplyLen;
+ goto ExitWithCleanup;
+
+ } else {
+
+ // There's more coming! Remember the fragger handle and continue.
+
+ NdsFraggerHandle = ReplyFragHandle;
+ }
+
+ }
+
+ DebugTrace( 0, Dbg, "Invalid state in FragExWithWait()\n", 0 );
+
+ExitWithCleanup:
+
+ //
+ // Unlock the request buffer and free its mdl.
+ //
+
+ if ( pMdlSendData ) {
+
+ MmUnlockPages( pMdlSendData );
+ FREE_MDL( pMdlSendData );
+ }
+
+ //
+ // Free the partial mdls.
+ //
+
+ if ( pRxMdlFrag )
+ FREE_MDL( pRxMdlFrag );
+
+ if ( pTxMdlFrag )
+ FREE_MDL( pTxMdlFrag );
+
+ //
+ // Free the request buffer.
+ //
+
+ FREE_POOL( NdsRequestBuf );
+
+ //
+ // Restore the original Irp->RxMdl parameters.
+ //
+
+ pIrpContext->RxMdl->Next = pOrigMdl;
+ pIrpContext->RxMdl->ByteCount = OrigRxMdlSize;
+
+ return Status;
+
+}
+
+int
+_cdecl
+FormatBuf(
+ char *buf,
+ int bufLen,
+ const char *format,
+ va_list args
+)
+/*
+
+Routine Description:
+
+ Formats a buffer according to supplied the format string.
+
+ FormatString - Supplies an ANSI string which describes how to
+ convert from the input arguments into NCP request fields, and
+ from the NCP response fields into the output arguments.
+
+ Field types, request/response:
+
+ 'b' byte ( byte / byte* )
+ 'w' hi-lo word ( word / word* )
+ 'd' hi-lo dword ( dword / dword* )
+ 'W' lo-hi word ( word / word*)
+ 'D' lo-hi dword ( dword / dword*)
+ '-' zero/skip byte ( void )
+ '=' zero/skip word ( void )
+ ._. zero/skip string ( word )
+ 'p' pstring ( char* )
+ 'c' cstring ( char* )
+ 'C' cstring followed skip word ( char*, word )
+ 'V' sized NDS value ( byte *, dword / byte **, dword *)
+ 'S' p unicode string copy as NDS_STRING (UNICODE_STRING *)
+ 's' cstring copy as NDS_STRING (char* / char *, word)
+ 'r' raw bytes ( byte*, word )
+ 'u' p unicode string ( UNICODE_STRING * )
+ 'U' p uppercase string( UNICODE_STRING * )
+
+Routine Arguments:
+
+ char *buf - destination buffer.
+ int buflen - length of the destination buffer.
+ char *format - format string.
+ args - args to the format string.
+
+Implementation Notes:
+
+ This comes almost verbatim from the Win95 source code. It duplicates
+ work in FormatRequest(). Eventually, FormatRequest() should be split
+ into two distinct routines: FormatBuffer() and MakeRequest().
+
+*/
+{
+ ULONG ix;
+
+ NTSTATUS status;
+ const char *z = format;
+
+ PAGED_CODE();
+
+ //
+ // Convert the input arguments into request packet.
+ //
+
+ ix = 0;
+
+ while ( *z )
+ {
+ switch ( *z )
+ {
+ case '=':
+ buf[ix++] = 0;
+ case '-':
+ buf[ix++] = 0;
+ break;
+
+ case '_':
+ {
+ WORD l = va_arg ( args, WORD );
+ if (ix + (ULONG)l > (ULONG)bufLen)
+ {
+#ifdef NWDBG
+ DbgPrintf( "FormatBuf case '_' request buffer too small.\n" );
+#endif
+ goto ErrorExit;
+ }
+ while ( l-- )
+ buf[ix++] = 0;
+ break;
+ }
+
+ case 'b':
+ buf[ix++] = va_arg ( args, BYTE );
+ break;
+
+ case 'w':
+ {
+ WORD w = va_arg ( args, WORD );
+ buf[ix++] = (BYTE) (w >> 8);
+ buf[ix++] = (BYTE) (w >> 0);
+ break;
+ }
+
+ case 'd':
+ {
+ DWORD d = va_arg ( args, DWORD );
+ buf[ix++] = (BYTE) (d >> 24);
+ buf[ix++] = (BYTE) (d >> 16);
+ buf[ix++] = (BYTE) (d >> 8);
+ buf[ix++] = (BYTE) (d >> 0);
+ break;
+ }
+
+ case 'W':
+ {
+ WORD w = va_arg(args, WORD);
+ (* (WORD *)&buf[ix]) = w;
+ ix += 2;
+ break;
+ }
+
+ case 'D':
+ {
+ DWORD d = va_arg (args, DWORD);
+ (* (DWORD *)&buf[ix]) = d;
+ ix += 4;
+ break;
+ }
+
+ case 'c':
+ {
+ char* c = va_arg ( args, char* );
+ WORD l = strlen( c );
+ if (ix + (ULONG)l > (ULONG)bufLen)
+ {
+#ifdef NWDBG
+ DbgPrintf( "FormatBuf case 'c' request buffer too small.\n" );
+#endif
+ goto ErrorExit;
+ }
+ RtlCopyMemory( &buf[ix], c, l+1 );
+ ix += l + 1;
+ break;
+ }
+
+ case 'C':
+ {
+ char* c = va_arg ( args, char* );
+ WORD l = va_arg ( args, WORD );
+ WORD len = strlen( c ) + 1;
+ if (ix + (ULONG)l > (ULONG)bufLen)
+ {
+#ifdef NWDBG
+ DbgPrintf( "FormatBuf 'C' request buffer too small.\n" );
+#endif
+ goto ErrorExit;
+ }
+
+ RtlCopyMemory( &buf[ix], c, len > l? l : len);
+ ix += l;
+ buf[ix-1] = 0;
+ break;
+ }
+
+ case 'p':
+ {
+ char* c = va_arg ( args, char* );
+ BYTE l = strlen( c );
+ if (ix + (ULONG)l +1 > (ULONG)bufLen)
+ {
+#ifdef NWDBG
+ DbgPrintf( "FormatBuf case 'p' request buffer too small.\n" );
+#endif
+ goto ErrorExit;
+ }
+ buf[ix++] = l;
+ RtlCopyMemory( &buf[ix], c, l );
+ ix += l;
+ break;
+ }
+
+ case 'u':
+ {
+ PUNICODE_STRING pUString = va_arg ( args, PUNICODE_STRING );
+ OEM_STRING OemString;
+ ULONG Length;
+
+ //
+ // Calculate required string length, excluding trailing NUL.
+ //
+
+ Length = RtlUnicodeStringToOemSize( pUString ) - 1;
+ ASSERT( Length < 0x100 );
+
+ if ( ix + Length > (ULONG)bufLen ) {
+#ifdef NWDBG
+ DbgPrint( "FormatBuf case 'u' request buffer too small.\n" );
+#endif
+ goto ErrorExit;
+ }
+
+ buf[ix++] = (UCHAR)Length;
+ OemString.Buffer = &buf[ix];
+ OemString.MaximumLength = (USHORT)Length + 1;
+
+ status = RtlUnicodeStringToOemString( &OemString, pUString, FALSE );
+ ASSERT( NT_SUCCESS( status ));
+ ix += (USHORT)Length;
+ break;
+ }
+
+ case 'S':
+ {
+ PUNICODE_STRING pUString = va_arg (args, PUNICODE_STRING);
+ ULONG Length, rLength;
+
+ Length = pUString->Length;
+ if (ix + Length + sizeof(Length) + sizeof( WCHAR ) > (ULONG)bufLen) {
+ DebugTrace( 0, Dbg, "FormatBuf: case 'S' request buffer too small.\n", 0 );
+ goto ErrorExit;
+ }
+
+ //
+ // The VLM client uses the rounded up length and it seems to
+ // make a difference! Also, don't forget that NDS strings have
+ // to be NULL terminated.
+ //
+
+ rLength = ROUNDUP4(Length + sizeof( WCHAR ));
+ *((DWORD *)&buf[ix]) = rLength;
+ ix += 4;
+ RtlCopyMemory(&buf[ix], pUString->Buffer, Length);
+ ix += Length;
+ rLength -= Length;
+ RtlFillMemory( &buf[ix], rLength, '\0' );
+ ix += rLength;
+ break;
+
+ }
+
+ case 's':
+ {
+ PUNICODE_STRING pUString = va_arg (args, PUNICODE_STRING);
+ ULONG Length, rLength;
+
+ Length = pUString->Length;
+ if (ix + Length + sizeof(Length) + sizeof( WCHAR ) > (ULONG)bufLen) {
+ DebugTrace( 0, Dbg, "FormatBuf: case 's' request buffer too small.\n", 0 );
+ goto ErrorExit;
+ }
+
+ //
+ // Don't use the padded size here, only the NDS null terminator.
+ //
+
+ rLength = Length + sizeof( WCHAR );
+ *((DWORD *)&buf[ix]) = rLength;
+ ix += 4;
+ RtlCopyMemory(&buf[ix], pUString->Buffer, Length);
+ ix += Length;
+ rLength -= Length;
+ RtlFillMemory( &buf[ix], rLength, '\0' );
+ ix += rLength;
+ break;
+
+
+ }
+
+ case 'V':
+ {
+ // too similar to 'S' - should be combined
+ BYTE* b = va_arg ( args, BYTE* );
+ DWORD l = va_arg ( args, DWORD );
+ if ( ix + l + sizeof(DWORD) > (ULONG)
+ bufLen )
+ {
+#ifdef NWDBG
+ DbgPrint( "FormatBuf case 'V' request buffer too small.\n" );
+#endif
+ goto ErrorExit;
+ }
+ *((DWORD *)&buf[ix]) = l;
+ ix += sizeof(DWORD);
+ RtlCopyMemory( &buf[ix], b, l );
+ l = ROUNDUP4(l);
+ ix += l;
+ break;
+ }
+
+ case 'r':
+ {
+ BYTE* b = va_arg ( args, BYTE* );
+ WORD l = va_arg ( args, WORD );
+ if ( ix + l > (ULONG)bufLen )
+ {
+#ifdef NWDBG
+ DbgPrint( "FormatBuf case 'r' request buffer too small.\n" );
+#endif
+ goto ErrorExit;
+ }
+ RtlCopyMemory( &buf[ix], b, l );
+ ix += l;
+ break;
+ }
+
+ default:
+
+#ifdef NWDBG
+ DbgPrint( "FormatBuf invalid request field, %x.\n", *z );
+#endif
+ ;
+
+ }
+
+ if ( ix > (ULONG)bufLen )
+ {
+#ifdef NWDBG
+ DbgPrint( "FormatBuf: too much request data.\n" );
+#endif
+ goto ErrorExit;
+ }
+
+
+ z++;
+ }
+
+ return(ix);
+
+ErrorExit:
+ return 0;
+}
+
+
+
+int
+_cdecl
+FormatBufS(
+ char *buf,
+ int bufLen,
+ const char *format,
+ ...
+)
+/*++
+ args from the stack
+--*/
+{
+ va_list args;
+ int len;
+
+ PAGED_CODE();
+
+ va_start(args, format);
+ len = FormatBuf(buf, bufLen, format, args);
+ va_end( args );
+
+ return len;
+}
+
diff --git a/private/nw/rdr/fsctl.c b/private/nw/rdr/fsctl.c
new file mode 100644
index 000000000..159cd1385
--- /dev/null
+++ b/private/nw/rdr/fsctl.c
@@ -0,0 +1,5930 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ FsCtrl.c
+
+Abstract:
+
+ This module implements the File System Control routines for the
+ NetWare redirector called by the dispatch driver.
+
+Author:
+
+ Colin Watson [ColinW] 29-Dec-1992
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+#include "ntddrdr.h"
+#include "ntddmup.h"
+
+//
+// The local debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_FSCTRL)
+
+//
+// Local procedure prototypes
+//
+
+NTSTATUS
+NwCommonDeviceIoControl (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+StartRedirector(
+ PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+StopRedirector(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+BindToTransport (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+ChangePassword (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+SetInfo (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+SetDebug (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+GetMessage (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+GetStats (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+GetPrintJobId (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+GetConnectionDetails(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+GetConnectionPerformance(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+RegisterWithMup(
+ VOID
+ );
+
+VOID
+DeregisterWithMup(
+ VOID
+ );
+
+NTSTATUS
+QueryPath (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+UserNcp(
+ ULONG Function,
+ PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+UserNcpCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ );
+
+NTSTATUS
+FspCompleteLogin(
+ PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+GetConnection(
+ PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+EnumConnections(
+ PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+DeleteConnection(
+ PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+WriteNetResourceEntry(
+ IN OUT PCHAR *FixedPortion,
+ IN OUT PWCHAR *EndOfVariableData,
+ IN PUNICODE_STRING ContainerName OPTIONAL,
+ IN PUNICODE_STRING LocalName OPTIONAL,
+ IN PUNICODE_STRING RemoteName,
+ IN ULONG ScopeFlag,
+ IN ULONG DisplayFlag,
+ IN ULONG UsageFlag,
+ IN ULONG ShareType,
+ OUT PULONG EntrySize
+ );
+
+BOOL
+CopyStringToBuffer(
+ IN LPCWSTR SourceString OPTIONAL,
+ IN DWORD CharacterCount,
+ IN LPCWSTR FixedDataEnd,
+ IN OUT LPWSTR *EndOfVariableData,
+ OUT LPWSTR *VariableDataPointer
+ );
+
+NTSTATUS
+GetRemoteHandle(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+GetUserName(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+GetChallenge(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+WriteConnStatusEntry(
+ PSCB pConnectionScb,
+ PBYTE pbUserBuffer,
+ DWORD dwBufferLen,
+ DWORD *pdwBytesWritten,
+ DWORD *pdwBytesNeeded,
+ BOOLEAN fCallerScb
+ );
+
+NTSTATUS
+GetConnStatus(
+ IN PIRP_CONTEXT IrpContext,
+ PFILE_OBJECT FileObject
+ );
+
+NTSTATUS
+GetConnectionInfo(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+GetPreferredServer(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+SetShareBit(
+ IN PIRP_CONTEXT IrpContext,
+ PFILE_OBJECT FileObject
+ );
+
+//
+// Statics
+//
+
+HANDLE MupHandle;
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwFsdFileSystemControl )
+#pragma alloc_text( PAGE, NwCommonFileSystemControl )
+#pragma alloc_text( PAGE, NwFsdDeviceIoControl )
+#pragma alloc_text( PAGE, NwCommonDeviceIoControl )
+#pragma alloc_text( PAGE, BindToTransport )
+#pragma alloc_text( PAGE, ChangePassword )
+#pragma alloc_text( PAGE, SetInfo )
+#pragma alloc_text( PAGE, GetStats )
+#pragma alloc_text( PAGE, GetPrintJobId )
+#pragma alloc_text( PAGE, StartRedirector )
+#pragma alloc_text( PAGE, StopRedirector )
+#pragma alloc_text( PAGE, RegisterWithMup )
+#pragma alloc_text( PAGE, DeregisterWithMup )
+#pragma alloc_text( PAGE, QueryPath )
+#pragma alloc_text( PAGE, UserNcp )
+#pragma alloc_text( PAGE, GetConnection )
+#pragma alloc_text( PAGE, DeleteConnection )
+#pragma alloc_text( PAGE, WriteNetResourceEntry )
+#pragma alloc_text( PAGE, CopyStringToBuffer )
+#pragma alloc_text( PAGE, GetRemoteHandle )
+#pragma alloc_text( PAGE, GetUserName )
+#pragma alloc_text( PAGE, GetChallenge )
+#pragma alloc_text( PAGE, WriteConnStatusEntry )
+#pragma alloc_text( PAGE, GetConnStatus )
+#pragma alloc_text( PAGE, GetConnectionInfo )
+#pragma alloc_text( PAGE, GetPreferredServer )
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, UserNcpCallback )
+#pragma alloc_text( PAGE1, GetConnectionDetails )
+#pragma alloc_text( PAGE1, GetMessage )
+#pragma alloc_text( PAGE1, EnumConnections )
+#endif
+
+#endif
+
+#if 0 // Not pageable
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+
+
+NTSTATUS
+NwFsdFileSystemControl (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine implements the FSD part of FileSystem control operations
+
+Arguments:
+
+ DeviceObject - Supplies the redirector device object.
+
+ Irp - Supplies the Irp being processed
+
+Return Value:
+
+ NTSTATUS - The FSD status for the IRP
+
+--*/
+
+{
+ NTSTATUS Status;
+ PIRP_CONTEXT IrpContext = NULL;
+ BOOLEAN TopLevel;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwFsdFileSystemControl\n", 0);
+
+ FsRtlEnterFileSystem();
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ try {
+
+ IrpContext = AllocateIrpContext( Irp );
+ SetFlag( IrpContext->Flags, IRP_FLAG_IN_FSD );
+ Status = NwCommonFileSystemControl( IrpContext );
+
+ } except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
+
+ if ( IrpContext == NULL ) {
+
+ //
+ // If we couldn't allocate an irp context, just complete
+ // irp without any fanfare.
+ //
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
+
+ } else {
+
+ //
+ // We had some trouble trying to perform the requested
+ // operation, so we'll abort the I/O request with
+ // the error Status that we get back from the
+ // execption code
+ //
+
+ Status = NwProcessException( IrpContext, GetExceptionCode() );
+ }
+
+ }
+
+ if ( IrpContext ) {
+
+ if ( Status != STATUS_PENDING ) {
+ NwDequeueIrpContext( IrpContext, FALSE );
+ }
+
+ NwCompleteRequest( IrpContext, Status );
+ }
+
+ if ( TopLevel ) {
+ NwSetTopLevelIrp( NULL );
+ }
+ FsRtlExitFileSystem();
+
+ //
+ // And return to our caller
+ //
+
+ DebugTrace(-1, Dbg, "NwFsdFileSystemControl -> %08lx\n", Status);
+
+ return Status;
+}
+
+
+NTSTATUS
+NwCommonFileSystemControl (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This is the common routine for doing FileSystem control operations called
+ by both the fsd and fsp threads
+
+Arguments:
+
+ IrpContext - Supplies the Irp to process
+
+Return Value:
+
+ NTSTATUS - The return status for the operation
+
+--*/
+
+{
+ NTSTATUS Status;
+ PIO_STACK_LOCATION IrpSp;
+ PIRP Irp;
+ ULONG Function;
+
+ PAGED_CODE();
+
+ NwReferenceUnlockableCodeSection();
+
+ try {
+
+ //
+ // Get a pointer to the current Irp stack location
+ //
+
+ Irp = IrpContext->pOriginalIrp;
+ IrpSp = IoGetCurrentIrpStackLocation( Irp );
+ Function = IrpSp->Parameters.FileSystemControl.FsControlCode;
+
+ DebugTrace(+1, Dbg, "NwCommonFileSystemControl\n", 0);
+ DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp);
+ DebugTrace( 0, Dbg, "Function = %08lx\n", Function);
+ DebugTrace( 0, Dbg, "Function = %d\n", (Function >> 2) & 0x0fff);
+
+ //
+ // We know this is a file system control so we'll case on the
+ // minor function, and call a internal worker routine to complete
+ // the irp.
+ //
+
+ if (IrpSp->MinorFunction != IRP_MN_USER_FS_REQUEST ) {
+ DebugTrace( 0, Dbg, "Invalid FS Control Minor Function %08lx\n", IrpSp->MinorFunction);
+ return STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+ switch (Function) {
+
+ case FSCTL_NWR_START:
+ Status = StartRedirector( IrpContext );
+ break;
+
+ case FSCTL_NWR_STOP:
+ Status = StopRedirector( IrpContext );
+ break;
+
+ case FSCTL_NWR_LOGON:
+ Status = Logon( IrpContext );
+ break;
+
+ case FSCTL_NWR_LOGOFF:
+ Status = Logoff( IrpContext );
+ break;
+
+ case FSCTL_NWR_GET_CONNECTION:
+ Status = GetConnection( IrpContext );
+ break;
+
+ case FSCTL_NWR_ENUMERATE_CONNECTIONS:
+ Status = EnumConnections( IrpContext );
+ break;
+
+ case FSCTL_NWR_DELETE_CONNECTION:
+ Status = DeleteConnection( IrpContext );
+ break;
+
+ case FSCTL_NWR_BIND_TO_TRANSPORT:
+ Status = BindToTransport( IrpContext );
+ break;
+
+ case FSCTL_NWR_CHANGE_PASS:
+ Status = ChangePassword( IrpContext );
+ break;
+
+ case FSCTL_NWR_SET_INFO:
+ Status = SetInfo( IrpContext );
+ break;
+
+ case FSCTL_NWR_GET_CONN_DETAILS:
+ Status = GetConnectionDetails( IrpContext );
+ break;
+
+ case FSCTL_NWR_GET_MESSAGE:
+ Status = GetMessage( IrpContext );
+ break;
+
+ case FSCTL_NWR_GET_STATISTICS:
+ Status = GetStats( IrpContext );
+ break;
+
+ case FSCTL_NWR_GET_USERNAME:
+ Status = GetUserName( IrpContext );
+ break;
+
+ case FSCTL_NWR_CHALLENGE:
+ Status = GetChallenge( IrpContext );
+ break;
+
+ case FSCTL_GET_PRINT_ID:
+ Status = GetPrintJobId( IrpContext );
+ break;
+
+ case FSCTL_NWR_GET_CONN_STATUS:
+ Status = GetConnStatus( IrpContext, IrpSp->FileObject );
+ break;
+
+ case FSCTL_NWR_GET_CONN_INFO:
+ Status = GetConnectionInfo( IrpContext );
+ break;
+
+ case FSCTL_NWR_GET_PREFERRED_SERVER:
+ Status = GetPreferredServer( IrpContext );
+ break;
+
+ case FSCTL_NWR_GET_CONN_PERFORMANCE:
+ Status = GetConnectionPerformance( IrpContext );
+ break;
+
+ case FSCTL_NWR_SET_SHAREBIT:
+ Status = SetShareBit( IrpContext, IrpSp->FileObject );
+ break;
+
+ default:
+
+ if (( Function >= NWR_ANY_NCP(0)) &&
+ ( Function <= NWR_ANY_HANDLE_NCP(0x00ff))) {
+
+ Status = UserNcp( Function, IrpContext );
+ break;
+
+ }
+
+ if (( Function >= NWR_ANY_NDS(0)) &&
+ ( Function <= NWR_ANY_NDS(0x00ff))) {
+
+ Status = DispatchNds( Function, IrpContext );
+ break;
+ }
+
+ DebugTrace( 0, Dbg, "Invalid FS Control Code %08lx\n",
+ IrpSp->Parameters.FileSystemControl.FsControlCode);
+
+ Status = STATUS_INVALID_DEVICE_REQUEST;
+ break;
+
+ }
+
+ } finally {
+
+ NwDereferenceUnlockableCodeSection ();
+
+ DebugTrace(-1, Dbg, "NwCommonFileSystemControl -> %08lx\n", Status);
+
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+NwFsdDeviceIoControl (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine implements the FSD part of DeviceIoControl file operations
+
+Arguments:
+
+ DeviceObject - Supplies the redirector device object.
+
+ Irp - Supplies the Irp being processed
+
+Return Value:
+
+ NTSTATUS - The FSD status for the IRP
+
+--*/
+
+{
+ NTSTATUS Status;
+ PIRP_CONTEXT IrpContext = NULL;
+ BOOLEAN TopLevel;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwFsdDeviceIoControl\n", 0);
+
+ FsRtlEnterFileSystem();
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ try {
+
+ IrpContext = AllocateIrpContext( Irp );
+ SetFlag( IrpContext->Flags, IRP_FLAG_IN_FSD );
+ Status = NwCommonDeviceIoControl( IrpContext );
+
+ } except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
+
+ if ( IrpContext == NULL ) {
+
+ //
+ // If we couldn't allocate an irp context, just complete
+ // irp without any fanfare.
+ //
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
+
+ } else {
+
+ //
+ // We had some trouble trying to perform the requested
+ // operation, so we'll abort the I/O request with
+ // the error Status that we get back from the
+ // execption code
+ //
+
+ Status = NwProcessException( IrpContext, GetExceptionCode() );
+ }
+
+ }
+
+ if ( IrpContext ) {
+
+ if ( Status != STATUS_PENDING ) {
+ NwDequeueIrpContext( IrpContext, FALSE );
+ }
+
+ NwCompleteRequest(IrpContext, Status);
+ }
+
+ if ( TopLevel ) {
+ NwSetTopLevelIrp( NULL );
+ }
+ FsRtlExitFileSystem();
+
+ //
+ // And return to our caller
+ //
+
+ DebugTrace(-1, Dbg, "NwFsdDeviceIoControl -> %08lx\n", Status);
+
+ return Status;
+}
+
+
+NTSTATUS
+NwCommonDeviceIoControl (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This is the common routine for doing FileSystem control operations called
+ by both the fsd and fsp threads
+
+Arguments:
+
+ IrpContext - Supplies the Irp to process
+
+Return Value:
+
+ NTSTATUS - The return status for the operation
+
+--*/
+
+{
+ NTSTATUS Status;
+ PIO_STACK_LOCATION IrpSp;
+ PIRP Irp;
+
+ PAGED_CODE();
+
+ NwReferenceUnlockableCodeSection();
+
+ try {
+
+ //
+ // Get a pointer to the current Irp stack location
+ //
+
+ Irp = IrpContext->pOriginalIrp;
+ IrpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ DebugTrace(+1, Dbg, "NwCommonDeviceIoControl\n", 0);
+ DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp);
+ DebugTrace( 0, Dbg, "Function = %08lx\n",
+ IrpSp->Parameters.DeviceIoControl.IoControlCode);
+
+ //
+ // We know this is a DeviceIoControl so we'll case on the
+ // minor function, and call a internal worker routine to complete
+ // the irp.
+ //
+
+ switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {
+
+ case IOCTL_REDIR_QUERY_PATH:
+ Status = QueryPath( IrpContext );
+ break;
+
+ case IOCTL_NWR_RAW_HANDLE:
+ Status = GetRemoteHandle( IrpContext );
+ break;
+
+ default:
+
+ DebugTrace( 0, Dbg, "Invalid IO Control Code %08lx\n",
+ IrpSp->Parameters.DeviceIoControl.IoControlCode);
+
+ Status = STATUS_INVALID_DEVICE_REQUEST;
+ break;
+ }
+
+ } finally {
+
+ NwDereferenceUnlockableCodeSection ();
+ DebugTrace(-1, Dbg, "NwCommonDeviceIoControl -> %08lx\n", Status);
+
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+BindToTransport (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine records the name of the transport to be used and
+ initialises the PermanentScb.
+
+Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+ NTSTATUS Status;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PNWR_REQUEST_PACKET InputBuffer = Irp->AssociatedIrp.SystemBuffer;
+ ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+
+ PAGED_CODE();
+
+#ifdef _PNP_POWER
+
+ //
+ // For PnP builds, register the bind handlers.
+ //
+
+ DebugTrace( 0 , Dbg, "Register TDI bind handlers.\n", 0 );
+
+ TdiInitialize();
+
+ return TdiRegisterNotificationHandler( HandleTdiBindMessage,
+ HandleTdiUnbindMessage,
+ &TdiBindingHandle );
+
+#endif
+
+ DebugTrace(+1, Dbg, "Bind to transport\n", 0);
+
+ try {
+
+ if ( FlagOn( IrpContext->Flags, IRP_FLAG_IN_FSD ) ) {
+ Status = NwPostToFsp( IrpContext, TRUE );
+ try_return( Status );
+ }
+
+ if (IpxHandle != NULL) {
+
+ //
+ // Can only bind to one transport at a time in this implementation
+ //
+
+ try_return(Status= STATUS_SHARING_VIOLATION);
+ }
+
+ //
+ // Check some fields in the input buffer.
+ //
+
+ if (InputBufferLength < sizeof(NWR_REQUEST_PACKET)) {
+ try_return(Status = STATUS_BUFFER_TOO_SMALL);
+ }
+
+ if (InputBuffer->Version != REQUEST_PACKET_VERSION) {
+ try_return(Status = STATUS_INVALID_PARAMETER);
+ }
+
+ if (InputBufferLength <
+ (FIELD_OFFSET(NWR_REQUEST_PACKET,Parameters.Bind.TransportName)) +
+ InputBuffer->Parameters.Bind.TransportNameLength) {
+ try_return(Status = STATUS_INVALID_PARAMETER);
+ }
+
+ if ( IpxTransportName.Buffer != NULL ) {
+ FREE_POOL( IpxTransportName.Buffer );
+ }
+
+ Status = SetUnicodeString ( &IpxTransportName,
+ InputBuffer->Parameters.Bind.TransportNameLength,
+ InputBuffer->Parameters.Bind.TransportName);
+
+ DebugTrace(-1, Dbg, "\"%wZ\"\n", &IpxTransportName);
+
+ if ( !NT_SUCCESS(Status) ) {
+ try_return(Status);
+ }
+
+ Status = IpxOpen();
+ if ( !NT_SUCCESS(Status) ) {
+ try_return(Status);
+ }
+
+ //
+ // Verify that have a large enough stack size.
+ //
+
+ if ( pIpxDeviceObject->StackSize >= FileSystemDeviceObject->StackSize) {
+ IpxClose();
+ try_return( Status = STATUS_INVALID_PARAMETER );
+ }
+
+#ifndef QFE_BUILD
+
+ //
+ // Submit a line change request.
+ //
+
+ SubmitLineChangeRequest();
+#endif
+
+ //
+ // Open a handle to IPX.
+ //
+
+ NwPermanentNpScb.Server.Socket = 0;
+ Status = IPX_Open_Socket( IrpContext, &NwPermanentNpScb.Server );
+ ASSERT( NT_SUCCESS( Status ) );
+
+ Status = SetEventHandler (
+ IrpContext,
+ &NwPermanentNpScb.Server,
+ TDI_EVENT_RECEIVE_DATAGRAM,
+ &ServerDatagramHandler,
+ &NwPermanentNpScb );
+
+ ASSERT( NT_SUCCESS( Status ) );
+
+ IrpContext->pNpScb = &NwPermanentNpScb;
+
+ NwRcb.State = RCB_STATE_RUNNING;
+
+try_exit:NOTHING;
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ Status = GetExceptionCode();
+ }
+
+ DebugTrace(-1, Dbg, "Bind to transport\n", 0);
+ return Status;
+
+}
+
+#ifdef _PNP_POWER
+
+VOID
+HandleTdiBindMessage(
+ IN PUNICODE_STRING DeviceName
+)
+/*+++
+
+Description: This function is the bind handler for NetPnP
+ support. This function is registered with TDI and is called
+ whenever a transport starts up or stops. We watch for IPX
+ coming and going and do the appropriate thing.
+
+ See also: HandleTdiUnbindMessage()
+
+---*/
+{
+
+ NTSTATUS Status;
+ PIRP_CONTEXT IrpContext = NULL;
+ PIRP pIrp = NULL;
+
+ PAGED_CODE();
+
+ //
+ // See if this is IPX requesting a bind. We only bind to NwLnkIpx.
+ //
+
+ if ( !RtlEqualUnicodeString( &TdiIpxDeviceName, DeviceName, TRUE ) ) {
+
+ DebugTrace( 0, Dbg, "Ignoring PnP Bind request for %wZ\n", DeviceName );
+ return;
+ }
+
+ //
+ // Make sure we aren't already bound.
+ //
+
+ if ( ( NwRcb.State != RCB_STATE_NEED_BIND ) ||
+ ( IpxHandle != NULL ) ) {
+
+ DebugTrace( 0, Dbg, "Discarding duplicate PnP bind request.\n", 0 );
+ return;
+ }
+
+ ASSERT( IpxTransportName.Buffer == NULL );
+ ASSERT( pIpxDeviceObject == NULL );
+
+ Status = DuplicateUnicodeStringWithString ( &IpxTransportName,
+ DeviceName,
+ PagedPool );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ DebugTrace( 0, Dbg, "Failing IPX bind: Can't set device name.\n", 0 );
+ return;
+ }
+
+ //
+ // Open IPX.
+ //
+
+ Status = IpxOpen();
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Verify that have a large enough stack size.
+ //
+
+ if ( pIpxDeviceObject->StackSize >= FileSystemDeviceObject->StackSize) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Submit a line change request.
+ //
+
+ SubmitLineChangeRequest();
+
+ //
+ // Allocate an irp and irp context. AllocateIrpContext may raise status.
+ //
+
+ pIrp = ALLOCATE_IRP( pIpxDeviceObject->StackSize, FALSE );
+
+ if ( pIrp == NULL ) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ExitWithCleanup;
+ }
+
+ try {
+
+ IrpContext = AllocateIrpContext( pIrp );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ExitWithCleanup;
+ }
+
+ ASSERT( IrpContext != NULL );
+
+ //
+ // Open a handle to IPX for the permanent scb.
+ //
+
+ NwPermanentNpScb.Server.Socket = 0;
+ Status = IPX_Open_Socket( IrpContext, &NwPermanentNpScb.Server );
+ ASSERT( NT_SUCCESS( Status ) );
+
+ Status = SetEventHandler (
+ IrpContext,
+ &NwPermanentNpScb.Server,
+ TDI_EVENT_RECEIVE_DATAGRAM,
+ &ServerDatagramHandler,
+ &NwPermanentNpScb );
+
+ ASSERT( NT_SUCCESS( Status ) );
+
+ IrpContext->pNpScb = &NwPermanentNpScb;
+
+ NwRcb.State = RCB_STATE_RUNNING;
+
+ DebugTrace( 0, Dbg, "Opened IPX for NwRdr.\n", 0 );
+
+ Status = STATUS_SUCCESS;
+
+ExitWithCleanup:
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ //
+ // If we failed, clean up our globals.
+ //
+
+ if ( pIpxDeviceObject != NULL ) {
+ IpxClose();
+ pIpxDeviceObject = NULL;
+ }
+
+ IpxHandle = NULL;
+
+ if ( IpxTransportName.Buffer != NULL ) {
+ FREE_POOL( IpxTransportName.Buffer );
+ IpxTransportName.Buffer = NULL;
+ }
+
+ DebugTrace( 0, Dbg, "Failing IPX bind request.\n", 0 );
+
+ }
+
+ if ( pIrp != NULL ) {
+ FREE_IRP( pIrp );
+ }
+
+ if ( IrpContext != NULL ) {
+ FreeIrpContext( IrpContext );
+ }
+
+ return;
+
+}
+
+VOID
+HandleTdiUnbindMessage(
+ IN PUNICODE_STRING DeviceName
+)
+/*+++
+
+Description: This function is the unbind handler for NetPnP
+ support. This function is registered with TDI and is called
+ whenever a transport stops. We watch for IPX coming and going
+ and do the appropriate thing.
+
+ See also: HandleTdiBindMessage()
+
+---*/
+{
+
+ DebugTrace( 0, Dbg, "TDI unbind request ignored. Not Supported.\n", 0 );
+ return;
+
+}
+
+#endif
+
+
+NTSTATUS
+ChangePassword (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine records a change in the user's cached password.
+
+Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+ NTSTATUS Status;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PNWR_REQUEST_PACKET InputBuffer = Irp->AssociatedIrp.SystemBuffer;
+ ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+
+ UNICODE_STRING UserName;
+ UNICODE_STRING Password;
+ UNICODE_STRING ServerName;
+ LARGE_INTEGER Uid;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "change password\n", 0);
+
+ try {
+
+ //
+ // Check some fields in the input buffer.
+ //
+
+ if (InputBufferLength < sizeof(NWR_REQUEST_PACKET)) {
+ try_return(Status = STATUS_BUFFER_TOO_SMALL);
+ }
+
+ if (InputBuffer->Version != REQUEST_PACKET_VERSION) {
+ try_return(Status = STATUS_INVALID_PARAMETER);
+ }
+
+ if (InputBufferLength <
+ (FIELD_OFFSET(NWR_REQUEST_PACKET,Parameters.ChangePass.UserName)) +
+ InputBuffer->Parameters.ChangePass.UserNameLength +
+ InputBuffer->Parameters.ChangePass.PasswordLength +
+ InputBuffer->Parameters.ChangePass.ServerNameLength ) {
+ try_return(Status = STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // Get local pointer to the fsctl parameters
+ //
+
+ UserName.Buffer = InputBuffer->Parameters.ChangePass.UserName;
+ UserName.Length = (USHORT)InputBuffer->Parameters.ChangePass.UserNameLength;
+
+ Password.Buffer = UserName.Buffer +
+ (InputBuffer->Parameters.ChangePass.UserNameLength / 2);
+ Password.Length = (USHORT)InputBuffer->Parameters.ChangePass.PasswordLength;
+
+ ServerName.Buffer = Password.Buffer +
+ (InputBuffer->Parameters.ChangePass.PasswordLength / 2);
+ ServerName.Length = (USHORT)InputBuffer->Parameters.ChangePass.ServerNameLength;
+
+ //
+ // Update the default password for this user
+ //
+
+ Status = UpdateUsersPassword( &UserName, &Password, &Uid );
+
+ //
+ // Update the default password for this user
+ //
+
+ if ( NT_SUCCESS( Status ) ) {
+ UpdateServerPassword( IrpContext, &ServerName, &UserName, &Password, &Uid );
+ }
+
+ Status = STATUS_SUCCESS;
+
+try_exit:NOTHING;
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ Status = GetExceptionCode();
+ }
+
+ DebugTrace(-1, Dbg, "Change Password\n", 0);
+ return Status;
+}
+
+
+NTSTATUS
+SetInfo (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine set netware redirector parameters.
+
+Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PNWR_REQUEST_PACKET InputBuffer = Irp->AssociatedIrp.SystemBuffer;
+ ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "Set info\n", 0);
+
+ try {
+
+ //
+ // Check some fields in the input buffer.
+ //
+
+ if (InputBufferLength < sizeof(NWR_REQUEST_PACKET)) {
+ try_return(Status = STATUS_BUFFER_TOO_SMALL);
+ }
+
+ if (InputBuffer->Version != REQUEST_PACKET_VERSION) {
+ try_return(Status = STATUS_INVALID_PARAMETER);
+ }
+
+ if (InputBufferLength <
+ (FIELD_OFFSET(NWR_REQUEST_PACKET,Parameters.SetInfo.PreferredServer)) +
+ InputBuffer->Parameters.SetInfo.PreferredServerLength +
+ InputBuffer->Parameters.SetInfo.ProviderNameLength ) {
+ try_return(Status = STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // We don't do anything with a preferred server change, but if we
+ // get a request to change the preferred tree and context, we
+ // validate the context. The rest of the changes happen at the next
+ // login.
+ //
+
+ if ( InputBuffer->Parameters.SetInfo.PreferredServerLength > 0 &&
+ InputBuffer->Parameters.SetInfo.PreferredServer[0] == '*' ) {
+
+ UNICODE_STRING Tree, NewContext;
+ USHORT i = 0;
+
+ //
+ // Dig out the tree name. Skip over the *.
+ //
+
+ Tree.Length = 0;
+ Tree.Buffer = InputBuffer->Parameters.SetInfo.PreferredServer + 1;
+
+ while ( i < InputBuffer->Parameters.SetInfo.PreferredServerLength ) {
+
+ if ( InputBuffer->Parameters.SetInfo.PreferredServer[i] == L'\\' ) {
+
+ i++;
+ Tree.Length -= sizeof( WCHAR );
+ Tree.MaximumLength = Tree.Length;
+ break;
+
+ } else {
+
+ Tree.Length += sizeof( WCHAR );
+ i++;
+
+ }
+ }
+
+ DebugTrace( 0, Dbg, "Tree: %wZ\n", &Tree );
+
+ NewContext.Length = (USHORT)InputBuffer->Parameters.SetInfo.PreferredServerLength -
+ ( Tree.Length + (2 * sizeof( WCHAR ) ) );
+ NewContext.Buffer = &InputBuffer->Parameters.SetInfo.PreferredServer[i];
+ NewContext.MaximumLength = NewContext.Length;
+
+ //
+ // Strip off any leading period.
+ //
+
+ if ( NewContext.Buffer[0] == L'.' ) {
+
+ NewContext.Buffer++;
+ NewContext.Length -= sizeof( WCHAR );
+ NewContext.MaximumLength -= sizeof( WCHAR );
+
+ }
+
+ DebugTrace( 0, Dbg, "Context: %wZ\n", &NewContext );
+
+ Status = NdsVerifyContext( IrpContext, &Tree, &NewContext );
+
+ if ( !NT_SUCCESS( Status )) {
+ try_return( STATUS_INVALID_PARAMETER );
+ }
+ }
+
+ //
+ // Next set the provider name string.
+ //
+
+ if ( InputBuffer->Parameters.SetInfo.ProviderNameLength != 0 ) {
+
+ PWCH TempBuffer;
+
+ TempBuffer = ALLOCATE_POOL_EX( PagedPool, InputBuffer->Parameters.SetInfo.ProviderNameLength );
+
+ if ( NwProviderName.Buffer != NULL ) {
+ FREE_POOL( NwProviderName.Buffer );
+ }
+
+ NwProviderName.Buffer = TempBuffer;
+ NwProviderName.Length = (USHORT)InputBuffer->Parameters.SetInfo.ProviderNameLength;
+
+ RtlCopyMemory(
+ NwProviderName.Buffer,
+ (PUCHAR)InputBuffer->Parameters.SetInfo.PreferredServer +
+ InputBuffer->Parameters.SetInfo.PreferredServerLength,
+ NwProviderName.Length );
+
+ }
+
+ //
+ // Set burst mode parameters
+ //
+
+ if ( InputBuffer->Parameters.SetInfo.MaximumBurstSize == 0 ) {
+ NwBurstModeEnabled = FALSE;
+ } else if ( InputBuffer->Parameters.SetInfo.MaximumBurstSize != -1 ) {
+ NwBurstModeEnabled = TRUE;
+ NwMaxSendSize = InputBuffer->Parameters.SetInfo.MaximumBurstSize;
+ NwMaxReceiveSize = InputBuffer->Parameters.SetInfo.MaximumBurstSize;
+ }
+
+ //
+ // Set print options
+ //
+
+ NwPrintOptions = InputBuffer->Parameters.SetInfo.PrintOption;
+
+try_exit:NOTHING;
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ Status = GetExceptionCode();
+ }
+
+ DebugTrace(-1, Dbg, "Set info\n", 0);
+ return Status;
+}
+
+
+NTSTATUS
+GetMessage (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine queues an IRP to a list of IRP Contexts available for
+ reading server administrative messages.
+
+Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_PENDING;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ PVOID OutputBuffer;
+
+ DebugTrace(+1, Dbg, "GetMessage\n", 0);
+
+ NwLockUserBuffer( Irp, IoWriteAccess, OutputBufferLength );
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer );
+
+ //
+ // Update the original MDL record in the Irp context, since
+ // NwLockUserBuffer may have created a new MDL.
+ //
+
+ IrpContext->pOriginalMdlAddress = Irp->MdlAddress;
+
+ IrpContext->Specific.FileSystemControl.Buffer = OutputBuffer;
+ IrpContext->Specific.FileSystemControl.Length = OutputBufferLength;
+
+ ExInterlockedInsertTailList(
+ &NwGetMessageList,
+ &IrpContext->NextRequest,
+ &NwMessageSpinLock );
+
+ IoMarkIrpPending( Irp );
+
+ //
+ // Set the cancel routine.
+ //
+
+ IoAcquireCancelSpinLock( &Irp->CancelIrql );
+
+ if ( Irp->Cancel ) {
+ NwCancelIrp( NULL, Irp );
+ } else {
+ IoSetCancelRoutine( Irp, NwCancelIrp );
+ IoReleaseCancelSpinLock( Irp->CancelIrql );
+ }
+
+ DebugTrace(-1, Dbg, "Get Message -> %08lx\n", Status );
+ return Status;
+}
+
+
+NTSTATUS
+GetStats (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine copies Stats into the users buffer.
+
+ Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_PENDING;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ PVOID OutputBuffer;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "GetStats\n", 0);
+
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer );
+
+ if (NwRcb.State != RCB_STATE_RUNNING) {
+
+ Status = STATUS_REDIRECTOR_NOT_STARTED;
+
+ } else if (OutputBufferLength < sizeof(NW_REDIR_STATISTICS)) {
+
+ Status = STATUS_BUFFER_TOO_SMALL;
+
+ } else if (OutputBufferLength != sizeof(NW_REDIR_STATISTICS)) {
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ } else {
+
+ Stats.CurrentCommands = ContextCount;
+
+ RtlCopyMemory(OutputBuffer, &Stats, OutputBufferLength);
+ Status = STATUS_SUCCESS;
+ Irp->IoStatus.Information = OutputBufferLength;
+
+ }
+
+ DebugTrace(-1, Dbg, "GetStats -> %08lx\n", Status );
+ return Status;
+}
+
+
+NTSTATUS
+GetPrintJobId (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine gets the Job ID for this job.
+
+Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_PENDING;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ PQUERY_PRINT_JOB_INFO OutputBuffer;
+ PICB Icb;
+ PVOID FsContext;
+ NODE_TYPE_CODE NodeTypeCode;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "GetJobId\n", 0);
+
+ NodeTypeCode = NwDecodeFileObject(
+ IrpSp->FileObject,
+ &FsContext,
+ (PVOID *)&Icb );
+
+ if (NodeTypeCode != NW_NTC_ICB) {
+
+ DebugTrace(0, Dbg, "Not a file\n", 0);
+ Status = STATUS_INVALID_PARAMETER;
+
+ } else if ( OutputBufferLength < sizeof( QUERY_PRINT_JOB_INFO ) ) {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ } else {
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer );
+
+ OutputBuffer->JobId = Icb->JobId;
+ //OutputBuffer->ServerName = BUGBUG
+ //OutputBuffer->QueueName = BUGBUG
+
+ Status = STATUS_SUCCESS;
+ }
+
+ DebugTrace(-1, Dbg, "GetJobId -> %08lx\n", Status );
+ return Status;
+}
+
+
+NTSTATUS
+GetConnectionDetails(
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine gets the details for a connection. This is normally used
+ for support of NetWare aware Dos applications.
+
+Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_PENDING;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ PNWR_GET_CONNECTION_DETAILS OutputBuffer;
+ PSCB pScb;
+ PNONPAGED_SCB pNpScb;
+ PICB Icb;
+ PVOID FsContext;
+ NODE_TYPE_CODE nodeTypeCode;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "GetConnectionDetails\n", 0);
+
+ if ((nodeTypeCode = NwDecodeFileObject( IrpSp->FileObject,
+ &FsContext,
+ (PVOID *)&Icb )) != NW_NTC_ICB_SCB) {
+
+ DebugTrace(0, Dbg, "Incorrect nodeTypeCode %x\n", nodeTypeCode);
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "GetConnectionDetails -> %08lx\n", Status );
+
+ return Status;
+ }
+
+ //
+ // Make sure that this ICB is still active.
+ //
+
+ NwVerifyIcb( Icb );
+
+ pScb = (PSCB)Icb->SuperType.Scb;
+ nodeTypeCode = pScb->NodeTypeCode;
+
+ if (nodeTypeCode != NW_NTC_SCB) {
+ return STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+ pNpScb = pScb->pNpScb;
+
+ if ( OutputBufferLength < sizeof( NWR_GET_CONNECTION_DETAILS ) ) {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ } else {
+ PLIST_ENTRY ScbQueueEntry;
+ KIRQL OldIrql;
+ PNONPAGED_SCB pNextNpScb;
+ UCHAR OrderNumber;
+ OEM_STRING ServerName;
+
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer );
+
+ KeAcquireSpinLock(&ScbSpinLock, &OldIrql);
+
+ for ( ScbQueueEntry = ScbQueue.Flink, OrderNumber = 1;
+ ScbQueueEntry != &ScbQueue ;
+ ScbQueueEntry = ScbQueueEntry->Flink, OrderNumber++ ) {
+
+ pNextNpScb = CONTAINING_RECORD(
+ ScbQueueEntry,
+ NONPAGED_SCB,
+ ScbLinks );
+
+ //
+ // Check to make sure that this SCB is usable.
+ //
+
+ if ( pNextNpScb == pNpScb ) {
+ break;
+ }
+ }
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql);
+
+ OutputBuffer->OrderNumber = OrderNumber;
+
+ RtlZeroMemory( OutputBuffer->ServerName, sizeof(OutputBuffer->ServerName));
+ ServerName.Buffer = OutputBuffer->ServerName;
+ ServerName.Length = sizeof(OutputBuffer->ServerName);
+ ServerName.MaximumLength = sizeof(OutputBuffer->ServerName);
+ RtlUpcaseUnicodeStringToCountedOemString( &ServerName, &pNpScb->ServerName, FALSE);
+
+ RtlCopyMemory( OutputBuffer->ServerAddress,
+ &pNpScb->ServerAddress,
+ sizeof(OutputBuffer->ServerAddress) );
+
+ OutputBuffer->ServerAddress[12];
+ OutputBuffer->ConnectionNumberLo = pNpScb->ConnectionNo;
+ OutputBuffer->ConnectionNumberHi = pNpScb->ConnectionNoHigh;
+ // BUGBUG We need to ask the server during connect!
+ OutputBuffer->MajorVersion = 1;
+ OutputBuffer->MinorVersion = 11;
+ OutputBuffer->Preferred = pScb->PreferredServer;
+
+ Status = STATUS_SUCCESS;
+ }
+
+ DebugTrace(-1, Dbg, "GetConnectionDetails -> %08lx\n", Status );
+ return Status;
+}
+
+#if 0
+
+NTSTATUS
+GetOurAddress(
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine gets the value of OurAddress. This is normally used
+ for support of NetWare aware Dos applications.
+
+Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_PENDING;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ PNWR_GET_OUR_ADDRESS OutputBuffer;
+ PSCB pScb;
+ PNONPAGED_SCB pNpScb;
+ PICB Icb;
+ PVOID FsContext;
+ NODE_TYPE_CODE nodeTypeCode;
+
+ DebugTrace(+1, Dbg, "GetOurAddress\n", 0);
+
+ if ((nodeTypeCode = NwDecodeFileObject( IrpSp->FileObject,
+ &FsContext,
+ (PVOID *)&Icb )) != NW_NTC_ICB_SCB) {
+
+ DebugTrace(0, Dbg, "Incorrect nodeTypeCode %x\n", nodeTypeCode);
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "GetOurAddress -> %08lx\n", Status );
+ }
+
+ //
+ // Make sure that this ICB is still active.
+ //
+
+ NwVerifyIcb( Icb );
+
+ if ( OutputBufferLength < sizeof( NWR_GET_OUR_ADDRESS ) ) {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ } else {
+
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer );
+
+ RtlCopyMemory( OutputBuffer->Address,
+ &OurAddress,
+ sizeof(OurAddress );
+
+ Status = STATUS_SUCCESS;
+ }
+
+ DebugTrace(-1, Dbg, "GetOurAddress -> %08lx\n", Status );
+ return Status;
+}
+#endif
+
+
+NTSTATUS
+StartRedirector(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine starts the redirector.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - The status of the operation.
+
+--*/
+{
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ //
+ // We need to be in the FSP to Register the MUP.
+ //
+
+ if ( FlagOn( IrpContext->Flags, IRP_FLAG_IN_FSD ) ) {
+ Status = NwPostToFsp( IrpContext, TRUE );
+ return( Status );
+ }
+
+ NwRcb.State = RCB_STATE_STARTING;
+
+ FspProcess = PsGetCurrentProcess();
+
+#ifdef QFE_BUILD
+ StartTimer() ;
+#endif
+
+ //
+ // Now connect to the MUP.
+ //
+
+ RegisterWithMup();
+
+ KeQuerySystemTime( &Stats.StatisticsStartTime );
+
+ NwRcb.State = RCB_STATE_NEED_BIND;
+
+ return( STATUS_SUCCESS );
+}
+
+
+NTSTATUS
+StopRedirector(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine shuts down the redirector.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - The status of the operation.
+
+--*/
+{
+ NTSTATUS Status;
+ PLIST_ENTRY LogonListEntry;
+ ULONG ActiveHandles;
+ ULONG RcbOpenCount;
+
+ PAGED_CODE();
+
+ //
+ // We need to be in the FSP to Deregister the MUP.
+ //
+
+ if ( FlagOn( IrpContext->Flags, IRP_FLAG_IN_FSD ) ) {
+ Status = NwPostToFsp( IrpContext, TRUE );
+ return( Status );
+ }
+
+#ifdef _PNP_POWER
+
+ //
+ // Unregister the bind handler with tdi.
+ //
+
+ if ( TdiBindingHandle != NULL ) {
+ TdiDeregisterNotificationHandler( TdiBindingHandle );
+ TdiBindingHandle = NULL;
+ }
+
+#endif
+
+ NwRcb.State = RCB_STATE_SHUTDOWN;
+
+ //
+ // Invalid all ICBs
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_SEND_ALWAYS );
+ ActiveHandles = NwInvalidateAllHandles(NULL, IrpContext);
+
+ //
+ // To expedite shutdown, set retry count down to 2.
+ //
+
+ DefaultRetryCount = 2;
+
+ //
+ // Close all VCBs
+ //
+
+ NwCloseAllVcbs( IrpContext );
+
+ //
+ // Logoff and disconnect from all servers.
+ //
+
+ NwLogoffAllServers( IrpContext, NULL );
+
+ while ( !IsListEmpty( &LogonList ) ) {
+
+ LogonListEntry = RemoveHeadList( &LogonList );
+
+ FreeLogon(CONTAINING_RECORD( LogonListEntry, LOGON, Next ));
+ }
+
+ InsertTailList( &LogonList, &Guest.Next ); // just in-case we don't unload.
+
+ StopTimer();
+
+ IpxClose();
+
+ //
+ // Remember the open count before calling DeristerWithMup since this
+ // will asynchronously cause handle count to get decremented.
+ //
+
+ RcbOpenCount = NwRcb.OpenCount;
+
+ DeregisterWithMup( );
+
+ DebugTrace(0, Dbg, "StopRedirector: Active handle count = %d\n", ActiveHandles );
+
+ //
+ // On shutdown, we need 0 remote handles and 2 open handles to
+ // the redir (one for the service, and one for the MUP) and the timer stopped.
+ //
+
+ if ( ActiveHandles == 0 && RcbOpenCount <= 2 ) {
+ return( STATUS_SUCCESS );
+ } else {
+ return( STATUS_REDIRECTOR_HAS_OPEN_HANDLES );
+ }
+}
+
+
+NTSTATUS
+RegisterWithMup(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine register this redirector as a UNC provider.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - The status of the operation.
+
+--*/
+{
+ NTSTATUS Status;
+ UNICODE_STRING RdrName;
+
+ PAGED_CODE();
+
+ RtlInitUnicodeString( &RdrName, DD_NWFS_DEVICE_NAME_U );
+ Status = FsRtlRegisterUncProvider(
+ &MupHandle,
+ &RdrName,
+ FALSE // Do not support mailslots
+ );
+
+ return( Status );
+}
+
+
+
+VOID
+DeregisterWithMup(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine deregisters this redirector as a UNC provider.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PAGED_CODE();
+
+ FsRtlDeregisterUncProvider( MupHandle );
+}
+
+
+NTSTATUS
+QueryPath(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine verifies whether a path is a netware path.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PIRP Irp;
+ PIO_STACK_LOCATION IrpSp;
+ PQUERY_PATH_REQUEST qpRequest;
+ PQUERY_PATH_RESPONSE qpResponse;
+ UNICODE_STRING FilePathName;
+ ULONG OutputBufferLength;
+ ULONG InputBufferLength;
+ SECURITY_SUBJECT_CONTEXT SubjectContext;
+
+ UNICODE_STRING DriveName;
+ UNICODE_STRING ServerName;
+ UNICODE_STRING VolumeName;
+ UNICODE_STRING PathName;
+ UNICODE_STRING FileName;
+ UNICODE_STRING UnicodeUid;
+ WCHAR DriveLetter;
+
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ ASSERT (( IOCTL_REDIR_QUERY_PATH & 3) == METHOD_NEITHER);
+
+ RtlInitUnicodeString( &UnicodeUid, NULL );
+
+ try {
+
+ Irp = IrpContext->pOriginalIrp;
+ IrpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ InputBufferLength = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
+
+ //
+ // The input buffer is either in Irp->AssociatedIrp.SystemBuffer, or
+ // in the Type3InputBuffer for type 3 IRP's.
+ //
+
+ qpRequest = (PQUERY_PATH_REQUEST)IrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
+ qpResponse = (PQUERY_PATH_RESPONSE)qpRequest;
+
+ ASSERT( qpRequest != NULL );
+
+ FilePathName.Buffer = qpRequest->FilePathName;
+ FilePathName.Length = (USHORT)qpRequest->PathNameLength;
+
+ status = CrackPath( &FilePathName, &DriveName, &DriveLetter, &ServerName, &VolumeName, &PathName, &FileName, NULL );
+
+ if (( !NT_SUCCESS( status ) ) ||
+ ( ServerName.Length == 0 )) {
+
+ try_return( status = STATUS_BAD_NETWORK_PATH );
+ }
+
+ qpResponse->LengthAccepted = VolumeName.Length;
+
+ //
+ // As far as the redirector is concerned, QueryPath is a form
+ // of create. Set up the IrpContext appropriately.
+ //
+
+ IrpContext->Specific.Create.VolumeName = VolumeName;
+ IrpContext->Specific.Create.PathName = PathName;
+ IrpContext->Specific.Create.DriveLetter = DriveLetter;
+ IrpContext->Specific.Create.FullPathName = FilePathName;
+
+ RtlInitUnicodeString( &IrpContext->Specific.Create.UidConnectName, NULL );
+
+ //
+ // The irp context specific data is now zeroed out by AllocateIrpContext,
+ // so we don't have to worry about re-setting the specific data here.
+ //
+
+ SeCaptureSubjectContext(&SubjectContext);
+
+ IrpContext->Specific.Create.UserUid = GetUid( &SubjectContext );
+
+ SeReleaseSubjectContext(&SubjectContext);
+
+ try {
+
+ //
+ // The slightly more complicated approach. This function
+ // handles the resolution of the server/volume duple. It
+ // may use the bindery, cached nds information, or fresh
+ // nds information.
+ //
+
+ status = HandleVolumeAttach( IrpContext,
+ &ServerName,
+ &VolumeName );
+
+ } except( NwExceptionFilter( Irp, GetExceptionInformation() )) {
+ status = STATUS_BAD_NETWORK_PATH;
+ }
+
+try_exit: NOTHING;
+
+ } finally {
+
+ RtlFreeUnicodeString(&UnicodeUid);
+ }
+
+ return( status );
+}
+
+NTSTATUS
+UserNcp(
+ ULONG IoctlCode,
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine exchanges an NCP with the server.
+
+ BUGBUG - We need to filter or security check what the user is
+ doing.
+
+Arguments:
+
+ IoctlCode - Supplies the code to be used for the NCP.
+
+ IrpContext - A pointer to IRP context information for this request.
+
+Return Value:
+
+ Status of transfer.
+
+--*/
+{
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ PVOID OutputBuffer;
+ ULONG OutputBufferLength;
+ PCHAR InputBuffer;
+ ULONG InputBufferLength;
+
+ PICB icb;
+ PSCB pScb;
+ NODE_TYPE_CODE nodeTypeCode;
+ PVOID fsContext;
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+
+ UCHAR Function = ANY_NCP_OPCODE( IoctlCode );
+ UCHAR Subfunction = 0;
+
+ irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+ OutputBufferLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ InputBufferLength = irpSp->Parameters.DeviceIoControl.InputBufferLength;
+
+ DebugTrace(+1, DEBUG_TRACE_USERNCP, "UserNcp...\n", 0);
+ DebugTrace( 0, DEBUG_TRACE_USERNCP, "irp = %08lx\n", (ULONG)irp);
+
+ //
+ // This F2 and ANY NCP must be addressed either to \Device\NwRdr or
+ // \Device\NwRdr\<servername> any additional name is not allowed.
+ // If the handle used for the Irp specifies \Device\NwRdr then the
+ // redirector gets to choose among the connected servers.
+ //
+ // For HANDLE NCP the file must be an FCB.
+ //
+
+ nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
+ &fsContext,
+ (PVOID *)&icb );
+
+ if ((nodeTypeCode == NW_NTC_ICB_SCB) &&
+ (!IS_IT_NWR_ANY_HANDLE_NCP(IoctlCode))) {
+
+ // All ok
+
+ //
+ // Make sure that this ICB is still active.
+ //
+
+ NwVerifyIcb( icb );
+
+ pScb = (PSCB)icb->SuperType.Scb;
+ nodeTypeCode = pScb->NodeTypeCode;
+
+ IrpContext->pScb = pScb;
+ IrpContext->pNpScb = IrpContext->pScb->pNpScb;
+
+ } else if (nodeTypeCode == NW_NTC_ICB) {
+
+ if ((IS_IT_NWR_ANY_HANDLE_NCP(IoctlCode)) &&
+ (InputBufferLength < 7)) {
+
+ // Buffer needs enough space for the handle!
+ DebugTrace(0, DEBUG_TRACE_USERNCP, "Not enough space for handle %x\n", InputBufferLength);
+
+ status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, DEBUG_TRACE_USERNCP, "UserNcp -> %08lx\n", status );
+ return status;
+ }
+
+ //
+ // Make sure that this ICB is still active.
+ // Let through FCB's and DCB's
+ //
+
+ NwVerifyIcb( icb );
+
+ pScb = (PSCB)icb->SuperType.Fcb->Scb;
+ nodeTypeCode = icb->SuperType.Fcb->NodeTypeCode;
+
+ IrpContext->pScb = pScb;
+ IrpContext->pNpScb = IrpContext->pScb->pNpScb;
+
+ //
+ // Set the icb pointer in case the cache gets
+ // flushed because the write routines look at it.
+ //
+
+ IrpContext->Icb = icb;
+ AcquireFcbAndFlushCache( IrpContext, icb->NpFcb );
+
+ } else {
+
+ DebugTrace(0, DEBUG_TRACE_USERNCP, "Incorrect nodeTypeCode %x\n", nodeTypeCode);
+ DebugTrace(0, DEBUG_TRACE_USERNCP, "Incorrect nodeTypeCode %x\n", irpSp->FileObject);
+
+ status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, DEBUG_TRACE_USERNCP, "UserNcp -> %08lx\n", status );
+ return status;
+ }
+
+ if (icb->Pid == INVALID_PID) {
+ status = NwMapPid( (ULONG)PsGetCurrentThread(), &icb->Pid );
+
+ if ( !NT_SUCCESS( status ) ) {
+ return( status );
+ }
+
+ DebugTrace(-1, DEBUG_TRACE_USERNCP, "UserNcp Pid = %02lx\n", icb->Pid );
+ NwSetEndOfJobRequired(icb->Pid);
+
+ }
+
+ //
+ // We now know where to send the NCP. Lock down the users buffers and
+ // build the Mdls required to transfer the data.
+ //
+
+ InputBuffer = irpSp->Parameters.FileSystemControl.Type3InputBuffer;
+
+ if ( OutputBufferLength ) {
+ NwLockUserBuffer( irp, IoWriteAccess, OutputBufferLength );
+ NwMapUserBuffer( irp, KernelMode, (PVOID *)&OutputBuffer );
+ } else {
+ OutputBuffer = NULL;
+ }
+
+ //
+ // Update the original MDL record in the Irp context, since
+ // NwLockUserBuffer may have created a new MDL.
+ //
+
+ IrpContext->pOriginalMdlAddress = irp->MdlAddress;
+
+ if (InputBufferLength != 0) {
+ if (IS_IT_NWR_ANY_NCP(IoctlCode)) {
+ Subfunction = InputBuffer[0];
+ } else if (InputBufferLength >= 3) {
+ Subfunction = InputBuffer[2];
+ }
+ }
+
+
+ DebugTrace( 0, DEBUG_TRACE_USERNCP, "UserNcp function = %x\n", Function );
+ DebugTrace( 0, DEBUG_TRACE_USERNCP, " & Subfunction = %x\n", Subfunction );
+ dump( DEBUG_TRACE_USERNCP, InputBuffer, InputBufferLength );
+ //dump( DEBUG_TRACE_USERNCP, OutputBuffer, OutputBufferLength );
+
+ if ((Function == NCP_ADMIN_FUNCTION ) &&
+ (InputBufferLength >= 4 )) {
+
+ if ( ( (Subfunction == NCP_SUBFUNC_79) ||
+ (Subfunction == NCP_CREATE_QUEUE_JOB ) ) &&
+ icb->HasRemoteHandle) {
+
+ //
+ // Trying to create a job on a queue that already has a job
+ // on it. Cancel the old job.
+ //
+
+ status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "Sdw",
+ NCP_ADMIN_FUNCTION, NCP_CLOSE_FILE_AND_CANCEL_JOB, // Close File And Cancel Queue Job
+ icb->SuperType.Fcb->Vcb->Specific.Print.QueueId,
+ icb->JobId );
+
+ if (!NT_SUCCESS(status)) {
+
+ DebugTrace( 0, DEBUG_TRACE_USERNCP, "DeleteOldJob got status -> %08lx\n", status );
+ // Don't worry if the delete fails, proceed with the create
+ }
+
+ icb->IsPrintJob = FALSE; // App will have to queue or cancel job, not rdr
+
+ } else if ((Subfunction == NCP_PLAIN_TEXT_LOGIN ) ||
+ (Subfunction == NCP_ENCRYPTED_LOGIN )) {
+
+ UNICODE_STRING UserName;
+ OEM_STRING OemUserName;
+ PUCHAR InputBuffer;
+
+ //
+ // Trying to do a login.
+ //
+
+ //
+ // Queue ourselves to the SCB, and wait to get to the front to
+ // protect access to server State.
+ //
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ //
+ // Assume success, store the user name in the SCB.
+ //
+
+ try {
+
+ InputBuffer = irpSp->Parameters.FileSystemControl.Type3InputBuffer;
+
+ OemUserName.Length = InputBuffer[ 13 ];
+ OemUserName.Buffer = &InputBuffer[14];
+
+ UserName.MaximumLength = OemUserName.Length * sizeof(WCHAR);
+ if ( OemUserName.Length == 0 || OemUserName.Length > MAX_USER_NAME_LENGTH ) {
+ try_return( status = STATUS_NO_SUCH_USER );
+ }
+
+ UserName.Buffer = ALLOCATE_POOL_EX( NonPagedPool, UserName.MaximumLength );
+
+ //
+ // Note the the Rtl function would set pUString->Buffer = NULL,
+ // if OemString.Length is 0.
+ //
+
+ if ( OemUserName.Length != 0 ) {
+ status = RtlOemStringToCountedUnicodeString( &UserName, &OemUserName, FALSE );
+ } else {
+ UserName.Length = 0;
+ }
+try_exit: NOTHING;
+ } finally {
+ NOTHING;
+ }
+
+ if ( NT_SUCCESS( status )) {
+
+ if ( pScb->OpenFileCount != 0 &&
+ pScb->pNpScb->State == SCB_STATE_IN_USE ) {
+
+ if (!RtlEqualUnicodeString( &pScb->UserName, &UserName, TRUE )) {
+
+ //
+ // But were already logged in to this server and at
+ // least one other handle is using the connection and
+ // the user is trying to change the username.
+ //
+
+ FREE_POOL( UserName.Buffer );
+ return STATUS_NETWORK_CREDENTIAL_CONFLICT;
+
+ } else {
+
+ PUCHAR VerifyBuffer = ALLOCATE_POOL( PagedPool, InputBufferLength );
+
+ //
+ // Same username. Validate password is correct.
+
+ if (VerifyBuffer == NULL) {
+ FREE_POOL( UserName.Buffer );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlCopyMemory( VerifyBuffer, InputBuffer, InputBufferLength );
+
+ if (IS_IT_NWR_ANY_NCP(IoctlCode)) {
+ VerifyBuffer[0] = (Subfunction == NCP_PLAIN_TEXT_LOGIN ) ?
+ NCP_PLAIN_TEXT_VERIFY_PASSWORD:
+ NCP_ENCRYPTED_VERIFY_PASSWORD;
+
+ } else {
+ VerifyBuffer[2] = (Subfunction == NCP_PLAIN_TEXT_LOGIN ) ?
+ NCP_PLAIN_TEXT_VERIFY_PASSWORD:
+ NCP_ENCRYPTED_VERIFY_PASSWORD;
+ }
+
+ status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ IS_IT_NWR_ANY_NCP(IoctlCode)? "Sr":"Fbr",
+ Function, VerifyBuffer[0],
+ &VerifyBuffer[1], InputBufferLength - 1 );
+
+ FREE_POOL( UserName.Buffer );
+ FREE_POOL( VerifyBuffer );
+ return status;
+
+ }
+ }
+
+ if (pScb->UserName.Buffer) {
+ FREE_POOL( pScb->UserName.Buffer ); // May include space for password too.
+ }
+
+ IrpContext->pNpScb->pScb->UserName = UserName;
+ IrpContext->pNpScb->pScb->Password.Buffer = UserName.Buffer;
+ IrpContext->pNpScb->pScb->Password.Length = 0;
+
+ } else {
+ return( status );
+ }
+ }
+ } else if (Function == NCP_LOGOUT ) {
+
+ //
+ // Queue ourselves to the SCB, and wait to get to the front to
+ // protect access to server State.
+ //
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ if ( pScb->OpenFileCount == 0 &&
+ pScb->pNpScb->State == SCB_STATE_IN_USE &&
+ !pScb->PreferredServer ) {
+
+ NwLogoffAndDisconnect( IrpContext, pScb->pNpScb);
+ return STATUS_SUCCESS;
+
+ } else {
+
+ return(STATUS_CONNECTION_IN_USE);
+
+ }
+ }
+
+ IrpContext->Icb = icb;
+
+ //
+ // Remember where the response goes.
+ //
+
+ IrpContext->Specific.FileSystemControl.Buffer = OutputBuffer;
+ IrpContext->Specific.FileSystemControl.Length = OutputBufferLength;
+
+ IrpContext->Specific.FileSystemControl.Function = Function;
+ IrpContext->Specific.FileSystemControl.Subfunction = Subfunction;
+
+ //
+ // Decide how to send the buffer. If it is small enough, send it
+ // by copying the user buffer to our send buffer. If it is bigger
+ // we will need to build an MDL for the user's buffer, and used a
+ // chained send.
+ //
+
+ if ( InputBufferLength == 0 ) {
+
+ // Simple request such as systime.exe
+
+ IrpContext->Specific.FileSystemControl.InputMdl = NULL;
+
+ status = Exchange(
+ IrpContext,
+ UserNcpCallback,
+ "F", Function);
+
+ } else if ( InputBufferLength < MAX_SEND_DATA - sizeof( NCP_REQUEST ) - 2 ) {
+
+ //
+ // Send the request by copying it to our send buffer.
+ //
+
+ IrpContext->Specific.FileSystemControl.InputMdl = NULL;
+
+ if (!IS_IT_NWR_ANY_HANDLE_NCP(IoctlCode)) {
+
+ //
+ // E0, E1, E2 and E3 get mapped to 14,15,16 and 17. These need
+ // a length word before the buffer.
+ //
+
+ status = Exchange(
+ IrpContext,
+ UserNcpCallback,
+ IS_IT_NWR_ANY_NCP(IoctlCode)? "Sr":"Fbr",
+ Function, InputBuffer[0],
+ &InputBuffer[1], InputBufferLength - 1 );
+ } else {
+
+ //
+ // Replace the 6 bytes of InputBuffer starting at offset 1
+ // with the 6 byte NetWare address for this icb. This request
+ // is used in some of the 16 bit NCP's used for file locking.
+ // These requests are always fairly small.
+ //
+
+ if (!icb->HasRemoteHandle) {
+ return STATUS_INVALID_HANDLE;
+ }
+
+ status = Exchange(
+ IrpContext,
+ UserNcpCallback,
+ "Fbrr",
+ Function,
+ InputBuffer[0],
+ &icb->Handle, sizeof(icb->Handle),
+ &InputBuffer[7], InputBufferLength - 7 );
+ }
+
+ } else {
+
+ PMDL pMdl = NULL;
+
+ if (IS_IT_NWR_ANY_HANDLE_NCP(IoctlCode)) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // We need to chain send the request. Allocate an MDL.
+ //
+
+ try {
+ pMdl = ALLOCATE_MDL(
+ &InputBuffer[1],
+ InputBufferLength - 1,
+ TRUE, // Secondary MDL
+ TRUE, // Charge quota
+ NULL );
+
+ if ( pMdl == NULL ) {
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ MmProbeAndLockPages( pMdl, irp->RequestorMode, IoReadAccess );
+
+ //
+ // Remember the MDL so we can free it.
+ //
+
+ IrpContext->Specific.FileSystemControl.InputMdl = pMdl;
+
+ //
+ // Send the request.
+ //
+
+ status = Exchange(
+ IrpContext,
+ UserNcpCallback,
+ IS_IT_NWR_ANY_NCP(IoctlCode)? "Sf":"Fbf",
+ Function, InputBuffer[0],
+ pMdl );
+
+
+ } finally {
+
+ if ((status != STATUS_PENDING ) &&
+ ( pMdl != NULL)) {
+
+ FREE_MDL( pMdl );
+
+ }
+ }
+ }
+
+ DebugTrace(-1, DEBUG_TRACE_USERNCP, "UserNcp -> %08lx\n", status );
+ return status;
+}
+
+
+
+NTSTATUS
+UserNcpCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ )
+
+/*++
+
+Routine Description:
+
+ This routine receives the response from a user NCP.
+
+Arguments:
+
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PVOID Buffer;
+ ULONG BufferLength;
+ PIRP Irp;
+ ULONG Length;
+ PICB Icb = IrpContext->Icb;
+ PEPresponse *pResponseParameters;
+
+ DebugTrace(0, DEBUG_TRACE_USERNCP, "UserNcpCallback...\n", 0);
+
+ if ( IrpContext->Specific.FileSystemControl.InputMdl != NULL ) {
+ MmUnlockPages( IrpContext->Specific.FileSystemControl.InputMdl );
+ FREE_MDL( IrpContext->Specific.FileSystemControl.InputMdl );
+ }
+
+ if ( BytesAvailable == 0) {
+
+ //
+ // No response from server. Status is in pIrpContext->
+ // ResponseParameters.Error
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwCompleteRequest( IrpContext, STATUS_REMOTE_NOT_LISTENING );
+
+ return STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ dump( DEBUG_TRACE_USERNCP, Response, BytesAvailable );
+
+ Buffer = IrpContext->Specific.FileSystemControl.Buffer;
+ BufferLength = IrpContext->Specific.FileSystemControl.Length;
+
+ //
+ // Get the data from the response.
+ //
+
+ Length = MIN( BufferLength, BytesAvailable - 8 );
+
+ if (IrpContext->Specific.FileSystemControl.Function == NCP_ADMIN_FUNCTION ) {
+
+ if (IrpContext->Specific.FileSystemControl.Subfunction == NCP_SUBFUNC_79) {
+
+ //
+ // Create Queue Job and File Ncp. If the operation was a success
+ // then we need to save the handle. This will allow Write Irps
+ // on this Icb to be sent to the server.
+ //
+
+ Status = ParseResponse(
+ IrpContext,
+ Response,
+ BytesAvailable,
+ "N_r",
+ 0x3E,
+ Icb->Handle+2,4);
+
+ // Pad the handle to its full 6 bytes.
+ Icb->Handle[0] = 0;
+ Icb->Handle[1] = 0;
+
+ if (NT_SUCCESS(Status)) {
+ Icb->HasRemoteHandle = TRUE;
+ }
+
+ //
+ // Reset the file offset.
+ //
+
+ Icb->FileObject->CurrentByteOffset.QuadPart = 0;
+
+ } else if (IrpContext->Specific.FileSystemControl.Subfunction == NCP_CREATE_QUEUE_JOB ) {
+
+ //
+ // Create Queue Job and File Ncp. If the operation was a success
+ // then we need to save the handle. This will allow Write Irps
+ // on this Icb to be sent to the server.
+ //
+
+ Status = ParseResponse(
+ IrpContext,
+ Response,
+ BytesAvailable,
+ "N_r",
+ 0x2A,
+ Icb->Handle,6);
+
+ if (NT_SUCCESS(Status)) {
+ Icb->HasRemoteHandle = TRUE;
+ }
+
+ //
+ // Reset the file offset.
+ //
+
+ Icb->FileObject->CurrentByteOffset.QuadPart = 0;
+
+ } else if ((IrpContext->Specific.FileSystemControl.Subfunction == NCP_SUBFUNC_7F) ||
+ (IrpContext->Specific.FileSystemControl.Subfunction == NCP_CLOSE_FILE_AND_START_JOB )) {
+
+ // End Job request
+
+ Icb->HasRemoteHandle = FALSE;
+
+ } else if ((IrpContext->Specific.FileSystemControl.Subfunction == NCP_PLAIN_TEXT_LOGIN ) ||
+ (IrpContext->Specific.FileSystemControl.Subfunction == NCP_ENCRYPTED_LOGIN )) {
+
+ //
+ // Trying to do a login from a 16 bit application.
+ //
+
+ Status = ParseResponse(
+ IrpContext,
+ Response,
+ BytesAvailable,
+ "N" );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+
+ //
+ // Set the reconnect attempt flag so that we don't try to
+ // run this irp context through the reconnect logic. Doing
+ // this could deadlock the worker thread that's handling this
+ // fsp side request.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT );
+ IrpContext->PostProcessRoutine = FspCompleteLogin;
+ Status = NwPostToFsp( IrpContext, TRUE );
+ return Status;
+
+ } else {
+ if (IrpContext->pNpScb->pScb->UserName.Buffer) {
+ FREE_POOL( IrpContext->pNpScb->pScb->UserName.Buffer );
+ }
+ RtlInitUnicodeString( &IrpContext->pNpScb->pScb->UserName, NULL);
+ RtlInitUnicodeString( &IrpContext->pNpScb->pScb->Password, NULL);
+ }
+ }
+ }
+
+ pResponseParameters = (PEPresponse *)( ((PEPrequest *)Response) + 1);
+
+ ParseResponse( IrpContext, Response, BytesAvailable, "Nr", Buffer, Length );
+
+ Status = ( ( pResponseParameters->status &
+ ( NCP_STATUS_BAD_CONNECTION |
+ NCP_STATUS_NO_CONNECTIONS |
+ NCP_STATUS_SERVER_DOWN ) ) << 8 ) |
+ pResponseParameters->error;
+
+ if ( Status ) {
+ //
+ // Use the special error code that will cause conversion
+ // of the status back to a Dos error code to leave status and
+ // error unchanged. This is necessary because many of the
+ // NetWare error codes have different meanings depending on the
+ // operation being performed.
+ //
+
+ Status |= 0xc0010000;
+ }
+
+ Irp = IrpContext->pOriginalIrp;
+ Irp->IoStatus.Information = Length;
+
+ //
+ // We're done with this request. Dequeue the IRP context from
+ // SCB and complete the request.
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwCompleteRequest( IrpContext, Status );
+
+ return STATUS_SUCCESS;
+
+}
+
+
+NTSTATUS
+FspCompleteLogin(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine reopens any Vcb directory handles.
+ It also sets the Scb as in use. This could have been done
+ in the callback routine too.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - The status of the operation.
+
+--*/
+{
+
+ IrpContext->pNpScb->State = SCB_STATE_IN_USE;
+
+ ReconnectScb( IrpContext, IrpContext->pScb );
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+GetConnection(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine returns the path of a connection.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - The status of the operation.
+
+--*/
+{
+ NTSTATUS Status;
+ PIRP Irp;
+ PIO_STACK_LOCATION IrpSp;
+ PNWR_SERVER_RESOURCE OutputBuffer;
+ PNWR_REQUEST_PACKET InputBuffer;
+ ULONG InputBufferLength;
+ ULONG OutputBufferLength;
+ PVCB Vcb;
+ PWCH DriveName;
+ ULONG DriveNameLength;
+ UNICODE_STRING Path;
+
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "GetConnection...\n", 0);
+ Irp = IrpContext->pOriginalIrp;
+ IrpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ InputBuffer = IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
+ InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+
+ if ( InputBufferLength < (ULONG)FIELD_OFFSET( NWR_REQUEST_PACKET, Parameters.GetConn.DeviceName[1] ) ) {
+ return( STATUS_INVALID_PARAMETER );
+ }
+
+ Status = STATUS_SUCCESS;
+
+ try {
+
+ //
+ // Find the VCB
+ //
+
+ DriveName = InputBuffer->Parameters.GetConn.DeviceName;
+ DriveNameLength = InputBuffer->Parameters.GetConn.DeviceNameLength;
+ Vcb = NULL;
+
+ if ( DriveName[0] >= L'A' && DriveName[0] <= L'Z' &&
+ DriveName[1] == L':' &&
+ DriveNameLength == sizeof( L"X:" ) - sizeof( L'\0' ) ) {
+
+ Vcb = DriveMapTable[DriveName[0] - 'A'];
+
+ } else if ( _wcsnicmp( DriveName, L"LPT", 3 ) == 0 &&
+ DriveName[3] >= '1' && DriveName[3] <= '9' &&
+ DriveNameLength == sizeof( L"LPTX" ) - sizeof( L'\0' ) ) {
+
+ Vcb = DriveMapTable[MAX_DISK_REDIRECTIONS + DriveName[3] - '1'];
+ }
+
+ if ( Vcb == NULL) {
+ try_return( Status = STATUS_NO_SUCH_FILE );
+ }
+
+ OutputBuffer = (PNWR_SERVER_RESOURCE)Irp->UserBuffer;
+ OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
+
+ //
+ // Calculate the VCB path to return.
+ //
+ // BUGBUG. We shouldn't have to recalc all the time. Add a
+ // new string the the VCB to remember this info. Init it
+ // in NwCreateVcb.
+ //
+
+ if ( Vcb->DriveLetter >= L'A' && Vcb->DriveLetter <= L'Z' ) {
+ Path.Buffer = Vcb->Name.Buffer + 3;
+ Path.Length = Vcb->Name.Length - 6;
+ } else if ( Vcb->DriveLetter >= L'1' && Vcb->DriveLetter <= L'9' ) {
+ Path.Buffer = Vcb->Name.Buffer + 5;
+ Path.Length = Vcb->Name.Length - 10;
+ } else {
+ Path = Vcb->Name;
+ }
+
+ // Strip off the unicode prefix
+
+ Path.Buffer += Vcb->Scb->UnicodeUid.Length/sizeof(WCHAR);
+ Path.Length -= Vcb->Scb->UnicodeUid.Length;
+ Path.MaximumLength -= Vcb->Scb->UnicodeUid.Length;
+
+ if (OutputBufferLength < Path.Length + 2 * sizeof(WCHAR)) {
+ InputBuffer->Parameters.GetConn.BytesNeeded =
+ Path.Length + 2 * sizeof(WCHAR);
+ try_return( Status = STATUS_BUFFER_TOO_SMALL );
+ }
+
+ //
+ // Return the Connection name in the form \\server\share<NUL>
+ //
+
+ OutputBuffer->UncName[0] = L'\\';
+
+ RtlMoveMemory(
+ &OutputBuffer->UncName[1],
+ Path.Buffer,
+ Path.Length );
+
+ OutputBuffer->UncName[ (Path.Length + sizeof(WCHAR)) / sizeof(WCHAR) ] = L'\0';
+
+ Irp->IoStatus.Information = Path.Length + 2 * sizeof(WCHAR);
+
+try_exit: NOTHING;
+
+ } finally {
+ NOTHING;
+ }
+
+ return( Status );
+}
+
+
+NTSTATUS
+DeleteConnection(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine returns removes a connection if force is specified or
+ if there are no open handles on this Vcb.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ NTSTATUS - The status of the operation.
+
+--*/
+{
+ NTSTATUS Status;
+ PIRP Irp;
+ PIO_STACK_LOCATION IrpSp;
+ PNWR_REQUEST_PACKET InputBuffer;
+ ULONG InputBufferLength;
+ PICB Icb;
+ PVCB Vcb;
+ PDCB Dcb;
+ PNONPAGED_DCB NonPagedDcb;
+ NODE_TYPE_CODE NodeTypeCode;
+
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "DeleteConnection...\n", 0);
+ Irp = IrpContext->pOriginalIrp;
+ IrpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ InputBuffer = IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
+ InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+
+ if ( InputBufferLength < (ULONG)FIELD_OFFSET( NWR_REQUEST_PACKET, Parameters.GetConn.DeviceName[1] ) ) {
+ return( STATUS_INVALID_PARAMETER );
+ }
+
+ Status = STATUS_SUCCESS;
+
+ //
+ // Wait to get to the head of the SCB queue. We do this in case
+ // we need to disconnect, so that we can send packets with the RCB
+ // resource held.
+ //
+
+ NodeTypeCode = NwDecodeFileObject( IrpSp->FileObject, &NonPagedDcb, &Icb );
+
+ if ( NodeTypeCode == NW_NTC_ICB_SCB ) {
+ IrpContext->pNpScb = Icb->SuperType.Scb->pNpScb;
+ } else {
+ ASSERT( NodeTypeCode == NW_NTC_ICB );
+ IrpContext->pNpScb = Icb->SuperType.Fcb->Scb->pNpScb;
+ Dcb = NonPagedDcb->Fcb;
+ }
+
+ NwAppendToQueueAndWait( IrpContext );
+ ClearFlag( IrpContext->Flags, IRP_FLAG_RECONNECTABLE );
+
+ //
+ // Acquire exclusive access to the RCB.
+ //
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ try {
+
+ //
+ // Get the a referenced pointer to the node and make sure it is
+ // not being closed, and that it is a directory handle.
+ //
+
+ if ( NodeTypeCode == NW_NTC_ICB_SCB ) {
+
+
+ if ( Icb->IsTreeHandle ) {
+
+ //
+ // Do an NDS logoff. This will release the RCB.
+ //
+
+ Status = NdsLogoff( IrpContext );
+ DebugTrace( 0, Dbg, "Nds tree logoff -> %08lx\n", Status );
+
+ } else {
+
+ DebugTrace( 0, Dbg, "Delete connection to SCB %X\n", Icb->SuperType.Scb );
+
+ Status = TreeDisconnectScb( IrpContext, Icb->SuperType.Scb );
+ DebugTrace(-1, Dbg, "DeleteConnection -> %08lx\n", Status );
+
+ }
+
+ try_return( NOTHING );
+
+ } else if ( NodeTypeCode != NW_NTC_ICB ||
+ Dcb == NULL ||
+ ( Dcb->NodeTypeCode != NW_NTC_DCB &&
+ Dcb->NodeTypeCode != NW_NTC_FCB) ) {
+
+ DebugTrace(0, Dbg, "Invalid file handle\n", 0);
+
+ Status = STATUS_INVALID_HANDLE;
+
+ DebugTrace(-1, Dbg, "DeleteConnection -> %08lx\n", Status );
+ try_return( NOTHING );
+ }
+
+ //
+ // Make sure that this ICB is still active.
+ //
+
+ NwVerifyIcb( Icb );
+
+ Vcb = Dcb->Vcb;
+ DebugTrace(0, Dbg, "Attempt to delete VCB = %08lx\n", Vcb);
+
+ //
+ // Vcb->OpenFileCount will be 1, (to account for this DCB), if the
+ // connection can be deleted.
+ //
+
+ if ( !BooleanFlagOn( Vcb->Flags, VCB_FLAG_EXPLICIT_CONNECTION ) ) {
+ DebugTrace(0, Dbg, "Cannot delete unredireced connection\n", 0);
+ try_return( Status = STATUS_INVALID_DEVICE_REQUEST );
+ } else {
+
+ if ( Vcb->OpenFileCount > 1 ) {
+ DebugTrace(0, Dbg, "Cannot delete in use connection\n", 0);
+ Status = STATUS_CONNECTION_IN_USE;
+ } else {
+
+ //
+ // To delete the VCB, simply dereference it.
+ //
+
+ DebugTrace(0, Dbg, "Deleting connection\n", 0);
+
+ ClearFlag( Vcb->Flags, VCB_FLAG_EXPLICIT_CONNECTION );
+ --Vcb->Scb->OpenFileCount;
+
+ NwDereferenceVcb( Vcb, IrpContext, TRUE );
+ }
+ }
+
+ try_exit: NOTHING;
+
+ } finally {
+
+
+ //
+ // An NDS logoff will have already freed the RCB
+ // and dequeued the irp context.
+ //
+
+ if ( ! ( Icb->IsTreeHandle ) ) {
+ NwReleaseRcb( &NwRcb );
+ NwDequeueIrpContext( IrpContext, FALSE );
+ }
+
+
+ }
+
+ Irp->IoStatus.Information = 0;
+
+ return( Status );
+}
+
+
+
+NTSTATUS
+EnumConnections(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine returns the list of redirector connections.
+
+Arguments:
+
+ IrpContext - A pointer to the IRP Context block for this request.
+
+Return Value:
+
+ NTSTATUS - The status of the operation.
+
+--*/
+{
+ NTSTATUS Status;
+ PIRP Irp;
+ PIO_STACK_LOCATION IrpSp;
+ PNWR_SERVER_RESOURCE OutputBuffer;
+ PNWR_REQUEST_PACKET InputBuffer;
+ ULONG InputBufferLength;
+ ULONG OutputBufferLength;
+ PVCB Vcb;
+ PSCB Scb;
+ BOOLEAN OwnRcb;
+
+ UNICODE_STRING LocalName;
+ UNICODE_STRING ContainerName;
+ PCHAR FixedPortion;
+ PWCHAR EndOfVariableData;
+ ULONG EntrySize;
+ ULONG ResumeKey;
+
+ ULONG ShareType;
+ ULONG EntriesRead = 0;
+ ULONG EntriesRequested;
+ DWORD ConnectionType;
+
+ PLIST_ENTRY ListEntry;
+ UNICODE_STRING Path;
+
+ DebugTrace(0, Dbg, "EnumConnections...\n", 0);
+
+ Irp = IrpContext->pOriginalIrp;
+ IrpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ InputBuffer = IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
+ InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+
+ if ( InputBufferLength < (ULONG)FIELD_OFFSET( NWR_REQUEST_PACKET, Parameters.EnumConn.BytesNeeded ) ) {
+ return( STATUS_INVALID_PARAMETER );
+ }
+
+ OutputBuffer = (PNWR_SERVER_RESOURCE)Irp->UserBuffer;
+ OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength;
+
+ Status = STATUS_SUCCESS;
+
+ try {
+
+ //
+ // Acquire shared access to the drive map table.
+ //
+
+ NwAcquireSharedRcb( &NwRcb, TRUE );
+ OwnRcb = TRUE;
+
+ //
+ // Initialize returned strings
+ //
+
+ RtlInitUnicodeString( &ContainerName, L"\\" );
+
+ FixedPortion = (PCHAR) OutputBuffer;
+ EndOfVariableData = (PWCHAR) ((ULONG) FixedPortion + OutputBufferLength);
+ ConnectionType = InputBuffer->Parameters.EnumConn.ConnectionType;
+
+ EntriesRequested = InputBuffer->Parameters.EnumConn.EntriesRequested;
+
+ //
+ // Run through the global VCB list looking for redirections.
+ //
+
+ ResumeKey = InputBuffer->Parameters.EnumConn.ResumeKey;
+
+ DebugTrace(0, Dbg, "Starting resume key is %d\n", InputBuffer->Parameters.EnumConn.ResumeKey );
+
+ for ( ListEntry = GlobalVcbList.Flink;
+ ListEntry != &GlobalVcbList &&
+ EntriesRequested > EntriesRead &&
+ Status == STATUS_SUCCESS ;
+ ListEntry = ListEntry->Flink ) {
+
+ Vcb = CONTAINING_RECORD( ListEntry, VCB, GlobalVcbListEntry );
+
+ //
+ // Skip connections that we've already enumerated.
+ //
+
+ if ( Vcb->SequenceNumber <= InputBuffer->Parameters.EnumConn.ResumeKey ) {
+ continue;
+ }
+
+ //
+ // Skip implicit connections, if they are not requested.
+ //
+
+ if ( !(ConnectionType & CONNTYPE_IMPLICIT) &&
+ !BooleanFlagOn( Vcb->Flags, VCB_FLAG_EXPLICIT_CONNECTION )) {
+
+ continue;
+ }
+
+ //
+ // Skip connections that are not requested.
+ //
+ if (BooleanFlagOn( Vcb->Flags, VCB_FLAG_PRINT_QUEUE )) {
+ if ( !( ConnectionType & CONNTYPE_PRINT ))
+ continue;
+ } else {
+ if ( !( ConnectionType & CONNTYPE_DISK ))
+ continue;
+ }
+
+
+ if ( Vcb->DriveLetter != 0 ) {
+ if (BooleanFlagOn( Vcb->Flags, VCB_FLAG_PRINT_QUEUE )) {
+ RtlInitUnicodeString( &LocalName, L"LPT1" );
+ LocalName.Buffer[3] = Vcb->DriveLetter;
+ ShareType = RESOURCETYPE_PRINT;
+ } else {
+ RtlInitUnicodeString( &LocalName, L"A:" );
+ LocalName.Buffer[0] = Vcb->DriveLetter;
+ ShareType = RESOURCETYPE_DISK;
+ }
+ } else { // No drive letter connection, i.e. UNC Connection
+ if (BooleanFlagOn( Vcb->Flags, VCB_FLAG_PRINT_QUEUE ))
+ ShareType = RESOURCETYPE_PRINT;
+ else
+ ShareType = RESOURCETYPE_DISK;
+ }
+
+ if ( Vcb->DriveLetter >= L'A' && Vcb->DriveLetter <= L'Z' ) {
+ Path.Buffer = Vcb->Name.Buffer + 3;
+ Path.Length = Vcb->Name.Length - 6;
+ } else if ( Vcb->DriveLetter >= L'1' && Vcb->DriveLetter <= L'9' ) {
+ Path.Buffer = Vcb->Name.Buffer + 5;
+ Path.Length = Vcb->Name.Length - 10;
+ } else {
+ Path = Vcb->Name;
+ }
+
+ // Strip off the unicode prefix
+
+ Path.Buffer += Vcb->Scb->UnicodeUid.Length/sizeof(WCHAR);
+ Path.Length -= Vcb->Scb->UnicodeUid.Length;
+ Path.MaximumLength -= Vcb->Scb->UnicodeUid.Length;
+
+ Status = WriteNetResourceEntry(
+ &FixedPortion,
+ &EndOfVariableData,
+ &ContainerName,
+ Vcb->DriveLetter != 0 ? &LocalName : NULL,
+ &Path,
+ RESOURCE_CONNECTED,
+ RESOURCEDISPLAYTYPE_SHARE,
+ RESOURCEUSAGE_CONNECTABLE,
+ ShareType,
+ &EntrySize
+ );
+
+ if ( Status == STATUS_MORE_ENTRIES ) {
+
+ //
+ // Could not write current entry into output buffer.
+ //
+
+ InputBuffer->Parameters.EnumConn.BytesNeeded = EntrySize;
+
+ } else if ( Status == STATUS_SUCCESS ) {
+
+ //
+ // Note that we've returned the current entry.
+ //
+
+ EntriesRead++;
+ ResumeKey = Vcb->SequenceNumber;
+
+ DebugTrace(0, Dbg, "Returning VCB %08lx\n", Vcb );
+ DebugTrace(0, Dbg, "Sequence # is %08lx\n", ResumeKey );
+ }
+ }
+
+ //
+ // Return the Servers we are connected to. This is most important for
+ // support of NetWare aware 16 bit apps.
+ //
+
+ if ((ConnectionType & CONNTYPE_IMPLICIT) &&
+ ( ConnectionType & CONNTYPE_DISK )) {
+
+ KIRQL OldIrql;
+ PNONPAGED_SCB pNpScb;
+ PLIST_ENTRY NextScbQueueEntry;
+ ULONG EnumSequenceNumber = 0x80000000;
+
+ NwReleaseRcb( &NwRcb );
+ OwnRcb = FALSE;
+
+ RtlInitUnicodeString( &ContainerName, L"\\\\" );
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ for ( ListEntry = ScbQueue.Flink;
+ ListEntry != &ScbQueue &&
+ EntriesRequested > EntriesRead &&
+ Status == STATUS_SUCCESS ;
+ ListEntry = NextScbQueueEntry ) {
+
+ pNpScb = CONTAINING_RECORD( ListEntry, NONPAGED_SCB, ScbLinks );
+ Scb = pNpScb->pScb;
+
+ NwReferenceScb( pNpScb );
+
+ KeReleaseSpinLock(&ScbSpinLock, OldIrql);
+
+ //
+ // Skip connections that we've already enumerated.
+ //
+
+ if (( EnumSequenceNumber <= InputBuffer->Parameters.EnumConn.ResumeKey ) ||
+
+ ( pNpScb == &NwPermanentNpScb ) ||
+
+ (( pNpScb->State != SCB_STATE_LOGIN_REQUIRED ) &&
+ ( pNpScb->State != SCB_STATE_IN_USE ))) {
+
+ //
+ // Move to next entry in the list
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+ NextScbQueueEntry = pNpScb->ScbLinks.Flink;
+ NwDereferenceScb( pNpScb );
+ EnumSequenceNumber++;
+ continue;
+ }
+
+ DebugTrace( 0, Dbg, " EnumConnections returning Servername = %wZ\n", &pNpScb->ServerName );
+
+ Status = WriteNetResourceEntry(
+ &FixedPortion,
+ &EndOfVariableData,
+ &ContainerName,
+ NULL,
+ &pNpScb->ServerName,
+ RESOURCE_CONNECTED,
+ RESOURCEDISPLAYTYPE_SHARE,
+ RESOURCEUSAGE_CONNECTABLE,
+ RESOURCETYPE_DISK,
+ &EntrySize
+ );
+
+ if ( Status == STATUS_MORE_ENTRIES ) {
+
+ //
+ // Could not write current entry into output buffer.
+ //
+
+ InputBuffer->Parameters.EnumConn.BytesNeeded = EntrySize;
+
+ } else if ( Status == STATUS_SUCCESS ) {
+
+ //
+ // Note that we've returned the current entry.
+ //
+
+ EntriesRead++;
+ ResumeKey = EnumSequenceNumber;
+
+ DebugTrace(0, Dbg, "Returning SCB %08lx\n", Scb );
+ DebugTrace(0, Dbg, "Sequence # is %08lx\n", ResumeKey );
+ }
+
+ //
+ // Move to next entry in the list
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+ NextScbQueueEntry = pNpScb->ScbLinks.Flink;
+ NwDereferenceScb( pNpScb );
+ EnumSequenceNumber++;
+ }
+
+ KeReleaseSpinLock(&ScbSpinLock, OldIrql);
+ }
+
+ InputBuffer->Parameters.EnumConn.EntriesReturned = EntriesRead;
+ InputBuffer->Parameters.EnumConn.ResumeKey = ResumeKey;
+
+ if ( EntriesRead == 0 ) {
+
+ if (Status == STATUS_SUCCESS) {
+ Status = STATUS_NO_MORE_ENTRIES;
+ }
+
+ Irp->IoStatus.Information = 0;
+ }
+ else {
+ Irp->IoStatus.Information = OutputBufferLength;
+ }
+
+ } finally {
+ if (OwnRcb) {
+ NwReleaseRcb( &NwRcb );
+ }
+ }
+
+ return( Status );
+}
+
+
+
+NTSTATUS
+WriteNetResourceEntry(
+ IN OUT PCHAR *FixedPortion,
+ IN OUT PWCHAR *EndOfVariableData,
+ IN PUNICODE_STRING ContainerName OPTIONAL,
+ IN PUNICODE_STRING LocalName OPTIONAL,
+ IN PUNICODE_STRING RemoteName,
+ IN ULONG ScopeFlag,
+ IN ULONG DisplayFlag,
+ IN ULONG UsageFlag,
+ IN ULONG ShareType,
+ OUT PULONG EntrySize
+ )
+/*++
+
+Routine Description:
+
+ This function packages a NETRESOURCE 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 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.
+
+ ShareType - Type of the share connected to, RESOURCETYPE_PRINT or
+ RESOURCETYPE_DISK
+
+ EntrySize - Receives the size of the NETRESOURCE entry in bytes.
+
+Return Value:
+
+ STATUS_SUCCESS - Successfully wrote entry into user buffer.
+
+ STATUS_NO_MEMORY - Failed to allocate work buffer.
+
+ STATUS_MORE_ENTRIES - Buffer was too small to fit entry.
+
+--*/
+{
+ BOOLEAN FitInBuffer = TRUE;
+ LPNETRESOURCEW NetR = (LPNETRESOURCEW) *FixedPortion;
+ UNICODE_STRING TmpRemote;
+
+ PAGED_CODE();
+
+ *EntrySize = sizeof(NETRESOURCEW) +
+ RemoteName->Length + NwProviderName.Length + 2 * sizeof(WCHAR);
+
+ if (ARGUMENT_PRESENT(LocalName)) {
+ *EntrySize += LocalName->Length + sizeof(WCHAR);
+ }
+
+ if (ARGUMENT_PRESENT(ContainerName)) {
+ *EntrySize += ContainerName->Length;
+ }
+
+ //
+ // See if buffer is large enough to fit the entry.
+ //
+ if (((ULONG) *FixedPortion + *EntrySize) >
+ (ULONG) *EndOfVariableData) {
+
+ return STATUS_MORE_ENTRIES;
+ }
+
+ NetR->dwScope = ScopeFlag;
+ NetR->dwType = ShareType;
+ NetR->dwDisplayType = DisplayFlag;
+ NetR->dwUsage = UsageFlag;
+ NetR->lpComment = NULL;
+
+ //
+ // Update fixed entry pointer to next entry.
+ //
+ (ULONG) (*FixedPortion) += sizeof(NETRESOURCEW);
+
+ //
+ // RemoteName
+ //
+ if (ARGUMENT_PRESENT(ContainerName)) {
+
+ //
+ // Prefix the RemoteName with its container name making the
+ // it a fully-qualified UNC name.
+ //
+
+ TmpRemote.MaximumLength = RemoteName->Length + ContainerName->Length + sizeof(WCHAR);
+ TmpRemote.Buffer = ALLOCATE_POOL(
+ PagedPool,
+ RemoteName->Length + ContainerName->Length + sizeof(WCHAR)
+ );
+
+ if (TmpRemote.Buffer == NULL) {
+ return STATUS_NO_MEMORY;
+ }
+
+ RtlCopyUnicodeString(&TmpRemote, ContainerName);
+ RtlAppendUnicodeStringToString(&TmpRemote, RemoteName);
+ }
+ else {
+ TmpRemote = *RemoteName;
+ }
+
+ FitInBuffer = CopyStringToBuffer(
+ TmpRemote.Buffer,
+ TmpRemote.Length / sizeof(WCHAR),
+ (LPCWSTR) *FixedPortion,
+ EndOfVariableData,
+ &NetR->lpRemoteName
+ );
+
+ if (ARGUMENT_PRESENT(ContainerName)) {
+ FREE_POOL(TmpRemote.Buffer);
+ }
+
+ ASSERT(FitInBuffer);
+
+ //
+ // LocalName
+ //
+ if (ARGUMENT_PRESENT(LocalName)) {
+ FitInBuffer = CopyStringToBuffer(
+ LocalName->Buffer,
+ LocalName->Length / sizeof(WCHAR),
+ (LPCWSTR) *FixedPortion,
+ EndOfVariableData,
+ &NetR->lpLocalName
+ );
+
+ ASSERT(FitInBuffer);
+ }
+ else {
+ NetR->lpLocalName = NULL;
+ }
+
+ //
+ // ProviderName
+ //
+
+ FitInBuffer = CopyStringToBuffer(
+ NwProviderName.Buffer,
+ NwProviderName.Length / sizeof(WCHAR),
+ (LPCWSTR) *FixedPortion,
+ EndOfVariableData,
+ &NetR->lpProvider
+ );
+
+ ASSERT(FitInBuffer);
+
+ if (! FitInBuffer) {
+ return STATUS_MORE_ENTRIES;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+BOOL
+CopyStringToBuffer(
+ IN LPCWSTR SourceString OPTIONAL,
+ IN DWORD CharacterCount,
+ IN LPCWSTR FixedDataEnd,
+ IN OUT LPWSTR *EndOfVariableData,
+ OUT LPWSTR *VariableDataPointer
+ )
+
+/*++
+
+Routine Description:
+
+ This is based on ..\nwlib\NwlibCopyStringToBuffer
+
+ This routine puts a single variable-length string into an output buffer.
+ The string is not written if it would overwrite the last fixed structure
+ in the buffer.
+
+Arguments:
+
+ SourceString - Supplies a pointer to the source string to copy into the
+ output buffer. If SourceString is null then a pointer to a zero terminator
+ is inserted into output buffer.
+
+ CharacterCount - Supplies the length of SourceString, not including zero
+ terminator. (This in units of characters - not bytes).
+
+ FixedDataEnd - Supplies a pointer to just after the end of the last
+ fixed structure in the buffer.
+
+ EndOfVariableData - Supplies an address to a pointer to just after the
+ last position in the output buffer that variable data can occupy.
+ Returns a pointer to the string written in the output buffer.
+
+ VariableDataPointer - Supplies a pointer to the place in the fixed
+ portion of the output buffer where a pointer to the variable data
+ should be written.
+
+Return Value:
+
+ Returns TRUE if string fits into output buffer, FALSE otherwise.
+
+--*/
+{
+ DWORD CharsNeeded = (CharacterCount + 1);
+
+ PAGED_CODE();
+
+ //
+ // Determine if source string will fit, allowing for a zero terminator.
+ // If not, just set the pointer to NULL.
+ //
+
+ if ((*EndOfVariableData - CharsNeeded) >= FixedDataEnd) {
+
+ //
+ // It fits. Move EndOfVariableData pointer up to the location where
+ // we will write the string.
+ //
+
+ *EndOfVariableData -= CharsNeeded;
+
+ //
+ // Copy the string to the buffer if it is not null.
+ //
+
+ if (CharacterCount > 0 && SourceString != NULL) {
+
+ (VOID) wcsncpy(*EndOfVariableData, SourceString, CharacterCount);
+ }
+
+ //
+ // Set the zero terminator.
+ //
+
+ *(*EndOfVariableData + CharacterCount) = L'\0';
+
+ //
+ // Set up the pointer in the fixed data portion to point to where the
+ // string is written.
+ //
+
+ *VariableDataPointer = *EndOfVariableData;
+
+ return TRUE;
+
+ }
+ else {
+
+ //
+ // It doesn't fit. Set the offset to NULL.
+ //
+
+ *VariableDataPointer = NULL;
+
+ return FALSE;
+ }
+}
+
+
+NTSTATUS
+GetRemoteHandle(
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine gets the NetWare handle for a Directory. This is used
+ for support of NetWare aware Dos applications.
+
+Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_PENDING;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ PCHAR OutputBuffer;
+ PICB Icb;
+ PDCB Dcb;
+ PVOID FsContext;
+ NODE_TYPE_CODE nodeTypeCode;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "GetRemoteHandle\n", 0);
+
+ if ((nodeTypeCode = NwDecodeFileObject( IrpSp->FileObject,
+ &FsContext,
+ (PVOID *)&Icb )) != NW_NTC_ICB) {
+
+ DebugTrace(0, Dbg, "Incorrect nodeTypeCode %x\n", nodeTypeCode);
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "GetRemoteHandle -> %08lx\n", Status );
+ }
+
+ Dcb = (PDCB)Icb->SuperType.Fcb;
+ nodeTypeCode = Dcb->NodeTypeCode;
+
+ if ( nodeTypeCode != NW_NTC_DCB ) {
+
+ DebugTrace(0, Dbg, "Not a directory\n", 0);
+
+#if 1
+ if ( nodeTypeCode != NW_NTC_FCB ) {
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "GetRemoteHandle -> %08lx\n", Status );
+ return Status;
+ }
+
+ //
+ // Return the 6 byte NetWare handle for this file.
+ //
+
+ if (!Icb->HasRemoteHandle) {
+
+ Status = STATUS_INVALID_HANDLE;
+
+ DebugTrace(-1, Dbg, "GetRemoteHandle -> %08lx\n", Status );
+ return Status;
+ }
+
+ if ( OutputBufferLength < sizeof( UCHAR ) ) {
+
+ Status = STATUS_BUFFER_TOO_SMALL;
+
+ DebugTrace(-1, Dbg, "GetRemoteHandle -> %08lx\n", Status );
+ return Status;
+ }
+
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer );
+
+ RtlCopyMemory( OutputBuffer, Icb->Handle, 6 * sizeof(CHAR));
+ IrpContext->pOriginalIrp->IoStatus.Information = 6 * sizeof(CHAR);
+
+ Status = STATUS_SUCCESS;
+
+ DebugTrace(-1, Dbg, "GetRemoteHandle -> %08lx\n", Status );
+ return Status;
+#else
+ Status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "GetRemoteHandle -> %08lx\n", Status );
+ return Status;
+#endif
+ }
+
+ //
+ // Make sure that this ICB is still active.
+ //
+
+ NwVerifyIcb( Icb );
+
+ if ( OutputBufferLength < sizeof( UCHAR ) ) {
+
+ Status = STATUS_BUFFER_TOO_SMALL;
+
+ } else if ( Icb->HasRemoteHandle ) {
+
+ // Already been asked for the handle
+
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer );
+
+ *OutputBuffer = Icb->Handle[0];
+
+ IrpContext->pOriginalIrp->IoStatus.Information = sizeof(CHAR);
+ Status = STATUS_SUCCESS;
+
+ } else {
+
+ CHAR Handle;
+
+ IrpContext->pScb = Dcb->Scb;
+ IrpContext->pNpScb = IrpContext->pScb->pNpScb;
+
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer );
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "SbbJ",
+ NCP_DIR_FUNCTION, NCP_ALLOCATE_TEMP_DIR_HANDLE,
+ Dcb->Vcb->Specific.Disk.Handle,
+ 0,
+ &Dcb->RelativeFileName );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nb",
+ &Handle );
+
+ if (NT_SUCCESS(Status)) {
+ *OutputBuffer = Handle;
+ Icb->Handle[0] = Handle;
+ Icb->HasRemoteHandle = TRUE;
+ IrpContext->pOriginalIrp->IoStatus.Information = sizeof(CHAR);
+ }
+ }
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+
+ DebugTrace( 0, Dbg, " -> %02x\n", Handle );
+
+ }
+
+ DebugTrace(-1, Dbg, "GetRemoteHandle -> %08lx\n", Status );
+ return Status;
+}
+
+
+NTSTATUS
+GetUserName(
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine gets the UserName that would be used to connect to a particular
+ server.
+
+ If there are credentials specific to this connection use them
+ otherwise use the logon credentials.
+
+Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+
+ NTSTATUS Status = STATUS_PENDING;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PWSTR InputBuffer = IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
+ ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+ ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ PWSTR OutputBuffer;
+ SECURITY_SUBJECT_CONTEXT SubjectContext;
+ LARGE_INTEGER Uid;
+ UNICODE_STRING UidServer;
+ UNICODE_STRING ServerName;
+ UNICODE_STRING ConvertedName;
+ PUNICODE_STRING pUserName;
+ PSCB pScb;
+ PLOGON pLogon;
+ BOOLEAN CredentialsHeld = FALSE;
+ BOOLEAN FailedTreeLookup = FALSE;
+ PNDS_SECURITY_CONTEXT pNdsCredentials;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "GetUserName\n", 0);
+
+ SeCaptureSubjectContext(&SubjectContext);
+ Uid = GetUid( &SubjectContext );
+ SeReleaseSubjectContext(&SubjectContext);
+
+ ServerName.Buffer = InputBuffer;
+ ServerName.MaximumLength = (USHORT)InputBufferLength;
+ ServerName.Length = (USHORT)InputBufferLength;
+ Status = MakeUidServer( &UidServer, &Uid, &ServerName );
+
+ if (!NT_SUCCESS(Status)) {
+ DebugTrace(-1, Dbg, "GetUserName -> %08lx\n", Status );
+ return(Status);
+ }
+
+ DebugTrace( 0, Dbg, " ->UidServer = \"%wZ\"\n", &UidServer );
+
+ //
+ // Get the login for this user.
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUser( &Uid, FALSE);
+ NwReleaseRcb( &NwRcb );
+
+ //
+ // First try this name as a server. Avoid FindScb creating a
+ // connection to the server if one doesn't exist already.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_NOCONNECT );
+ NwFindScb( &pScb, IrpContext, &UidServer, &ServerName );
+
+ pUserName = NULL;
+
+ //
+ // Look for bindery server name, or tree login name.
+ //
+
+ if ( pScb != NULL ) {
+
+ if ( pScb->UserName.Buffer != NULL ) {
+
+ pUserName = &pScb->UserName;
+
+ } else if ( pScb->NdsTreeName.Buffer != NULL &&
+ pScb->NdsTreeName.Length > 0 ) {
+
+ Status = NdsLookupCredentials( &pScb->NdsTreeName,
+ pLogon,
+ &pNdsCredentials,
+ CREDENTIAL_READ,
+ FALSE );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ CredentialsHeld = TRUE;
+
+ if ( pNdsCredentials->Credential ) {
+
+ //
+ // If we have login data, get the user name.
+ //
+
+ ConvertedName.Length = pNdsCredentials->Credential->userNameLength -
+ sizeof( WCHAR );
+ ConvertedName.MaximumLength = ConvertedName.Length;
+ ConvertedName.Buffer = (USHORT *)
+ ( ((BYTE *) pNdsCredentials->Credential ) +
+ sizeof( NDS_CREDENTIAL ) +
+ pNdsCredentials->Credential->optDataSize );
+
+ pUserName = &ConvertedName;
+
+ } else {
+
+ //
+ // If there's no credential data, we're not logged in.
+ //
+
+ FailedTreeLookup = TRUE;
+ }
+
+ } else {
+
+ FailedTreeLookup = TRUE;
+ }
+
+ }
+
+ }
+
+ //
+ // If it wasn't a server and we haven't already tried a tree, do so now.
+ //
+
+ if ( pUserName == NULL &&
+ !FailedTreeLookup ) {
+
+ Status = NdsLookupCredentials( &ServerName,
+ pLogon,
+ &pNdsCredentials,
+ CREDENTIAL_READ,
+ FALSE );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ CredentialsHeld = TRUE;
+
+ if ( pNdsCredentials->Credential ) {
+
+ //
+ // If we've logged in, get the user name.
+ //
+
+ ConvertedName.Length = pNdsCredentials->Credential->userNameLength -
+ sizeof( WCHAR );
+ ConvertedName.MaximumLength = ConvertedName.Length;
+ ConvertedName.Buffer = (USHORT *)
+ ( ((BYTE *) pNdsCredentials->Credential ) +
+ sizeof( NDS_CREDENTIAL ) +
+ pNdsCredentials->Credential->optDataSize );
+
+ pUserName = &ConvertedName;
+
+ }
+ }
+
+ }
+
+ //
+ // If we still don't know, return the default name.
+ //
+
+ if ( pUserName == NULL &&
+ pLogon != NULL ) {
+
+ pUserName = &pLogon->UserName;
+ }
+
+ FREE_POOL(UidServer.Buffer);
+
+ if ( pUserName ) {
+
+ DebugTrace( 0, Dbg, "Get User Name: %wZ\n", pUserName );
+
+ try {
+
+ if (pUserName->Length > OutputBufferLength) {
+
+ DebugTrace(-1, Dbg, "GetUserName -> %08lx\n", STATUS_BUFFER_TOO_SMALL );
+ Status = STATUS_BUFFER_TOO_SMALL;
+ goto ReleaseAndExit;
+ }
+
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer );
+
+ IrpContext->pOriginalIrp->IoStatus.Information = pUserName->Length;
+ RtlMoveMemory( OutputBuffer, pUserName->Buffer, pUserName->Length);
+
+ Status = STATUS_SUCCESS;
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ }
+ }
+
+ReleaseAndExit:
+
+ if ( pScb ) {
+ NwDereferenceScb( pScb->pNpScb );
+ }
+
+ DebugTrace(-1, Dbg, "GetUserName -> %08lx\n", Status );
+
+ if ( CredentialsHeld ) {
+ NwReleaseCredList( pLogon );
+ }
+
+ return Status;
+}
+
+
+NTSTATUS
+GetChallenge(
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine builds the challenge and session key for rpc using the
+ credentials stored in the redirector. The Rpc client can supply a
+ password. This allows the redirector to keep the algorithm in one
+ place.
+
+ If a password is supplied then use that, if there is a password on this
+ specific connection use that, otherwise use the logon credentials.
+
+Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+
+ NTSTATUS Status = STATUS_PENDING;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PNWR_GET_CHALLENGE_REQUEST InputBuffer = Irp->AssociatedIrp.SystemBuffer;
+ ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+ ULONG OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ PNWR_GET_CHALLENGE_REPLY OutputBuffer = Irp->AssociatedIrp.SystemBuffer;
+ OEM_STRING Password;
+ PSCB pScb;
+ PLOGON pLogon;
+ BOOLEAN RcbHeld = FALSE;
+ SECURITY_SUBJECT_CONTEXT SubjectContext;
+ LARGE_INTEGER ProcessUid;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "GetChallenge\n", 0);
+
+ if ((InputBufferLength <
+ (FIELD_OFFSET(NWR_GET_CHALLENGE_REQUEST,ServerNameorPassword[0])) +
+ InputBuffer->ServerNameorPasswordLength)) {
+
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // Only allow processes running in the system context to call this api to prevent
+ // password attacks.
+ //
+ SeCaptureSubjectContext(&SubjectContext);
+ SeQueryAuthenticationIdToken(&SubjectContext.PrimaryToken, (PLUID)&ProcessUid);
+ SeReleaseSubjectContext(&SubjectContext);
+
+ // FIXFIX surely there's a define for 3e7 somewhere.
+
+ if (ProcessUid.QuadPart != 0x3e7) {
+ return(STATUS_ACCESS_DENIED);
+ }
+
+ Password.Buffer = NULL;
+
+ if ( InputBuffer->Flags == CHALLENGE_FLAGS_SERVERNAME ) {
+
+ PUNICODE_STRING pPassword;
+ UNICODE_STRING ServerName;
+ LARGE_INTEGER Uid;
+ UNICODE_STRING UidServer;
+
+ if (InputBuffer->ServerNameorPasswordLength == 0) {
+ return(STATUS_INVALID_PARAMETER);
+ }
+
+ //
+ // We have to supply the password from the redirector
+ //
+
+ SeCaptureSubjectContext(&SubjectContext);
+ Uid = GetUid( &SubjectContext );
+ SeReleaseSubjectContext(&SubjectContext);
+
+ ServerName.Buffer = (PWSTR)((PUCHAR)InputBuffer +
+ FIELD_OFFSET(NWR_GET_CHALLENGE_REQUEST,ServerNameorPassword[0]));
+ ServerName.MaximumLength = (USHORT)InputBuffer->ServerNameorPasswordLength;
+ ServerName.Length = (USHORT)InputBuffer->ServerNameorPasswordLength;
+
+ Status = MakeUidServer( &UidServer, &Uid, &ServerName );
+
+ if (!NT_SUCCESS(Status)) {
+ DebugTrace(-1, Dbg, "GetChallenge -> %08lx\n", Status );
+ return(Status);
+ }
+
+ DebugTrace( 0, Dbg, " ->UidServer = \"%wZ\"\n", &UidServer );
+
+ //
+ // Avoid FindScb creating a connection to the server if one
+ // doesn't exist already.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_NOCONNECT );
+ NwFindScb( &pScb, IrpContext, &UidServer, &ServerName );
+
+ try {
+
+ if ((pScb != NULL) &&
+ (pScb->Password.Buffer != NULL)) {
+
+ pPassword = &pScb->Password;
+
+ } else {
+
+ //
+ // Use default credentials for this UID
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ RcbHeld = TRUE;
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUser( &Uid, FALSE);
+
+ if (pLogon != NULL ) {
+
+ pPassword = &pLogon->PassWord;
+
+ } else {
+ DebugTrace(-1, Dbg, "GetChallenge -> %08lx\n", STATUS_ACCESS_DENIED );
+ return( STATUS_ACCESS_DENIED );
+ }
+ }
+
+ if (pPassword->Length != 0) {
+ Status = RtlUpcaseUnicodeStringToOemString( &Password, pPassword, TRUE );
+ if (!NT_SUCCESS(Status)) {
+ DebugTrace(-1, Dbg, "GetChallenge -> %08lx\n", Status );
+ return( Status );
+ }
+ } else {
+ Password.Buffer = "";
+ Password.Length = Password.MaximumLength = 0;
+ }
+
+ } finally {
+
+ if (RcbHeld) {
+ NwReleaseRcb( &NwRcb );
+ }
+
+ if (pScb != NULL) {
+ NwDereferenceScb( pScb->pNpScb );
+ }
+
+ FREE_POOL(UidServer.Buffer);
+ }
+
+ } else {
+
+ UNICODE_STRING LocalPassword;
+
+ LocalPassword.Buffer = (PWSTR)((PUCHAR)InputBuffer +
+ FIELD_OFFSET(NWR_GET_CHALLENGE_REQUEST,ServerNameorPassword[0]));
+ LocalPassword.MaximumLength = (USHORT)InputBuffer->ServerNameorPasswordLength;
+ LocalPassword.Length = (USHORT)InputBuffer->ServerNameorPasswordLength;
+
+ if (LocalPassword.Length != 0) {
+ Status = RtlUpcaseUnicodeStringToOemString( &Password, &LocalPassword, TRUE );
+ if (!NT_SUCCESS(Status)) {
+ DebugTrace(-1, Dbg, "GetChallenge -> %08lx\n", Status );
+ return( Status );
+ }
+ } else {
+ Password.Buffer = "";
+ Password.Length = Password.MaximumLength = 0;
+ }
+ }
+
+ DebugTrace( 0, Dbg, " ->Password = \"%Z\"\n", &Password );
+
+ try {
+ RespondToChallenge( (PUCHAR)&InputBuffer->ObjectId, &Password, InputBuffer->Challenge, OutputBuffer->Challenge);
+
+ } finally {
+
+ if ( Password.Length > 0 ) {
+
+ RtlFreeAnsiString( &Password );
+ }
+ }
+
+ Irp->IoStatus.Information = sizeof(NWR_GET_CHALLENGE_REPLY);
+ Status = STATUS_SUCCESS;
+
+ DebugTrace(-1, Dbg, "GetChallenge -> %08lx\n", Status );
+ return Status;
+}
+
+NTSTATUS
+WriteConnStatusEntry(
+ PSCB pConnectionScb,
+ PBYTE pbUserBuffer,
+ DWORD dwBufferLen,
+ DWORD *pdwBytesWritten,
+ DWORD *pdwBytesNeeded,
+ BOOLEAN fCallerScb
+ )
+{
+
+ NTSTATUS Status;
+ PLOGON pLogon;
+ PNDS_SECURITY_CONTEXT pNdsContext;
+ BOOLEAN fHoldingCredentials = FALSE;
+ PUNICODE_STRING puUserName = NULL;
+ UNICODE_STRING CredentialName;
+ UNICODE_STRING ServerName;
+ PCONN_STATUS pStatus;
+ DWORD dwBytesNeeded;
+ PBYTE pbStrPtr;
+ DWORD dwAllowedHandles;
+
+ //
+ // If this is an NDS connection, get the credentials.
+ //
+
+ if ( ( pConnectionScb->MajorVersion > 3 ) &&
+ ( pConnectionScb->UserName.Length == 0 ) ) {
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUser( &(pConnectionScb->UserUid), FALSE );
+ NwReleaseRcb( &NwRcb );
+
+ if ( pLogon ) {
+
+ Status = NdsLookupCredentials( &(pConnectionScb->NdsTreeName),
+ pLogon,
+ &pNdsContext,
+ CREDENTIAL_READ,
+ FALSE );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ fHoldingCredentials = TRUE;
+
+ if ( pNdsContext->Credential != NULL ) {
+
+ CredentialName.Length = pNdsContext->Credential->userNameLength -
+ sizeof( WCHAR );
+ CredentialName.MaximumLength = CredentialName.Length;
+ CredentialName.Buffer = (USHORT *)
+ ( ((BYTE *) pNdsContext->Credential ) +
+ sizeof( NDS_CREDENTIAL ) +
+ pNdsContext->Credential->optDataSize );
+
+ puUserName = &CredentialName;
+ }
+
+ }
+ }
+
+ } else {
+
+ if ( pConnectionScb->UserName.Length != 0 ) {
+ puUserName = &(pConnectionScb->UserName);
+ } else {
+ puUserName = NULL;
+ }
+
+ }
+
+ DebugTrace( 0, Dbg, "WriteConnStatus: UserName %wZ\n", puUserName );
+
+ //
+ // Strip off the uid from the server name.
+ //
+
+ ServerName.Length = (pConnectionScb->UidServerName).Length;
+ ServerName.Buffer = (pConnectionScb->UidServerName).Buffer;
+
+ while ( ServerName.Length ) {
+
+ if ( ServerName.Buffer[0] == L'\\' ) {
+
+ ServerName.Length -= sizeof( WCHAR );
+ ServerName.Buffer += 1;
+ break;
+ }
+
+ ServerName.Length -= sizeof( WCHAR );
+ ServerName.Buffer += 1;
+
+ }
+
+ DebugTrace( 0, Dbg, "WriteConnStatus: ServerName %wZ\n", &ServerName );
+
+ //
+ // Do we have enough space? Don't forget that we have to
+ // NULL terminate the WCHAR strings.
+ //
+
+ dwBytesNeeded = sizeof( CONN_STATUS );
+
+ dwBytesNeeded += ( ServerName.Length + sizeof( WCHAR ) );
+
+ if ( pConnectionScb->NdsTreeName.Length ) {
+ dwBytesNeeded += ( pConnectionScb->NdsTreeName.Length + sizeof( WCHAR ) );
+ }
+
+ if ( puUserName ) {
+ dwBytesNeeded += ( puUserName->Length + sizeof( WCHAR ) );
+ }
+
+ //
+ // Pad the end to make sure all structures are aligned.
+ //
+
+ dwBytesNeeded = ROUNDUP4( dwBytesNeeded );
+
+ if ( dwBytesNeeded > dwBufferLen ) {
+
+ *pdwBytesNeeded = dwBytesNeeded;
+ Status = STATUS_BUFFER_TOO_SMALL;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Fill in the CONN_STATUS structure.
+ //
+
+ try {
+
+ pStatus = (PCONN_STATUS)pbUserBuffer;
+ pbStrPtr = pbUserBuffer + sizeof( CONN_STATUS );
+
+ //
+ // We always have a server name.
+ //
+
+ pStatus->pszServerName = (PWSTR) pbStrPtr;
+ pbStrPtr += ( ServerName.Length + sizeof( WCHAR ) );
+
+ //
+ // Fill in the user name if applicable.
+ //
+
+ if ( puUserName ) {
+
+ pStatus->pszUserName = (PWSTR) pbStrPtr;
+ pbStrPtr += ( puUserName->Length + sizeof( WCHAR ) );
+
+ } else {
+
+ pStatus->pszUserName = NULL;
+ }
+
+ //
+ // Fill in the tree name if applicable.
+ //
+
+ if ( pConnectionScb->NdsTreeName.Length ) {
+
+ pStatus->pszTreeName = (PWSTR) pbStrPtr;
+
+ } else {
+
+ pStatus->pszTreeName = NULL;
+ }
+
+ //
+ // Fill in the connection number if applicable.
+ //
+
+ if ( ( pConnectionScb->pNpScb->State == SCB_STATE_IN_USE ) ||
+ ( pConnectionScb->pNpScb->State == SCB_STATE_LOGIN_REQUIRED ) ) {
+
+ pStatus->nConnNum = (DWORD)(pConnectionScb->pNpScb->ConnectionNo);
+
+ } else {
+
+ pStatus->nConnNum = 0;
+
+ }
+
+ //
+ // Copy the user name over.
+ //
+
+ if ( puUserName ) {
+
+ RtlCopyMemory( (PBYTE)(pStatus->pszUserName),
+ (PBYTE)(puUserName->Buffer),
+ puUserName->Length );
+ *(pStatus->pszUserName + (puUserName->Length / sizeof( WCHAR ))) = L'\0';
+
+ }
+
+ //
+ // Set the NDS flag and authentication fields.
+ //
+
+ if ( ( pConnectionScb->MajorVersion > 3 ) &&
+ ( pConnectionScb->UserName.Length == 0 ) ) {
+
+ pStatus->fNds = TRUE;
+
+ if ( pConnectionScb->pNpScb->State == SCB_STATE_IN_USE ) {
+
+ if ( ( pConnectionScb->VcbCount ) || ( pConnectionScb->OpenNdsStreams ) ) {
+ pStatus->dwConnType = NW_CONN_NDS_AUTHENTICATED_LICENSED;
+ } else {
+ pStatus->dwConnType = NW_CONN_NDS_AUTHENTICATED_NO_LICENSE;
+ }
+
+ } else if ( pConnectionScb->pNpScb->State == SCB_STATE_LOGIN_REQUIRED ) {
+
+ pStatus->dwConnType = NW_CONN_NOT_AUTHENTICATED;
+
+ } else {
+
+ pStatus->dwConnType = NW_CONN_DISCONNECTED;
+
+ }
+
+ } else {
+
+ pStatus->fNds = FALSE;
+
+ if ( pConnectionScb->pNpScb->State == SCB_STATE_IN_USE ) {
+
+ pStatus->dwConnType = NW_CONN_BINDERY_LOGIN;
+
+ } else if ( pConnectionScb->pNpScb->State == SCB_STATE_LOGIN_REQUIRED ) {
+
+ pStatus->dwConnType = NW_CONN_NOT_AUTHENTICATED;
+
+ } else {
+
+ pStatus->dwConnType = NW_CONN_DISCONNECTED;
+
+ }
+
+ }
+
+ //
+ // Copy over the tree name.
+ //
+
+ if ( pConnectionScb->NdsTreeName.Length ) {
+
+ RtlCopyMemory( (PBYTE)(pStatus->pszTreeName),
+ (PBYTE)(pConnectionScb->NdsTreeName.Buffer),
+ pConnectionScb->NdsTreeName.Length );
+ *( pStatus->pszTreeName +
+ ( pConnectionScb->NdsTreeName.Length / sizeof( WCHAR ) ) ) = L'\0';
+
+ } else {
+
+ pStatus->pszTreeName = NULL;
+ }
+
+ //
+ // Copy the server name over.
+ //
+
+ RtlCopyMemory( (PBYTE)(pStatus->pszServerName),
+ (PBYTE)(ServerName.Buffer),
+ ServerName.Length );
+ *(pStatus->pszServerName + (ServerName.Length / sizeof( WCHAR ))) = L'\0';
+
+ //
+ // Set the preferred server field if this is a preferred server
+ // and there are no explicit uses for the connection. If the
+ // fCallerScb parameter is TRUE, then this SCB has a handle from
+ // the caller of the API and we have to make an allowance for
+ // that handle. Yes, this is kind of ugly.
+ //
+
+ if ( fCallerScb ) {
+ dwAllowedHandles = 1;
+ } else {
+ dwAllowedHandles = 0;
+ }
+
+ if ( ( pConnectionScb->PreferredServer ) &&
+ ( pConnectionScb->OpenFileCount == 0 ) &&
+ ( pConnectionScb->IcbCount == dwAllowedHandles ) ) {
+
+ pStatus->fPreferred = TRUE;
+
+ } else {
+
+ pStatus->fPreferred = FALSE;
+ }
+
+ //
+ // Fill out the length.
+ //
+
+ pStatus->dwTotalLength = dwBytesNeeded;
+ *pdwBytesWritten = dwBytesNeeded;
+ Status = STATUS_SUCCESS;
+
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = GetExceptionCode();
+ DebugTrace( 0, Dbg, "Exception %08lx accessing user mode buffer.\n", Status );
+ goto ExitWithCleanup;
+
+ }
+
+ExitWithCleanup:
+
+ if ( fHoldingCredentials ) {
+ NwReleaseCredList( pLogon );
+ }
+
+ return Status;
+}
+
+NTSTATUS
+GetConnStatus(
+ IN PIRP_CONTEXT IrpContext,
+ IN PFILE_OBJECT FileObject
+ )
+/*++
+
+ Get the connection status for the described connection.
+ The following connection requests are valid:
+
+ Server (e.g. "MARS312") - returns a single connection
+ status structure for this server if the user has a
+ connection to the server.
+
+ Tree (e.g. "*MARSDEV") - returns a connection status
+ structure for every server in the tree that the user
+ has a connection to.
+
+ All Connections (e.g. "") - returns a connection status
+ structure for every server that the user has a
+ connection to.
+
+--*/
+{
+
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+ PNWR_REQUEST_PACKET InputBuffer;
+ ULONG InputBufferLength;
+ BYTE *OutputBuffer;
+ ULONG OutputBufferLength;
+
+ SECURITY_SUBJECT_CONTEXT SubjectContext;
+ LARGE_INTEGER Uid;
+
+ PLIST_ENTRY ListEntry;
+ UNICODE_STRING ConnectionName, UidServer;
+ BOOL fTreeConnections = FALSE;
+ BOOL fServerConnection = FALSE;
+ BOOL OwnRcb = FALSE;
+ PUNICODE_PREFIX_TABLE_ENTRY PrefixEntry;
+ DWORD dwBytesWritten, dwBytesNeeded;
+ KIRQL OldIrql;
+ PSCB pScb;
+ PNONPAGED_SCB pNpScb;
+ DWORD dwReturned = 0;
+ ULONG SequenceNumber = 0;
+
+ NODE_TYPE_CODE nodeTypeCode;
+ PICB pIcb;
+ PSCB pCallerScb;
+ PVOID fsContext, fsContext2;
+
+ //
+ // Get the appropriate buffers.
+ //
+
+ InputBuffer = (PNWR_REQUEST_PACKET) IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
+ InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+ OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer );
+
+ //
+ // Figure out who this request applies to.
+ //
+
+ SeCaptureSubjectContext(&SubjectContext);
+ Uid = GetUid( &SubjectContext );
+ SeReleaseSubjectContext(&SubjectContext);
+
+ RtlInitUnicodeString( &ConnectionName, NULL );
+ RtlInitUnicodeString( &UidServer, NULL );
+
+ //
+ // Figure out who the caller of this routine is so we know to
+ // ignore their handle when deciding what to return.
+ //
+
+ nodeTypeCode = NwDecodeFileObject( FileObject, &fsContext, &fsContext2 );
+
+ if ( nodeTypeCode == NW_NTC_ICB_SCB ) {
+
+ pIcb = (PICB) fsContext2;
+ pCallerScb = pIcb->SuperType.Scb;
+ DebugTrace( 0, Dbg, "GetConnStatus called by handle on %08lx\n", pCallerScb );
+
+ } else {
+
+ pCallerScb = NULL;
+ DebugTrace( 0, Dbg, "Couldn't figure out who called us.\n", 0 );
+ }
+
+ //
+ //
+ // Figure out which connections we're looking for.
+ //
+
+ try {
+
+ if ( InputBuffer->Parameters.GetConnStatus.ConnectionNameLength != 0 ) {
+
+ if ( InputBuffer->Parameters.GetConnStatus.ConnectionName[0] == L'*' ) {
+
+ ConnectionName.Buffer = &(InputBuffer->Parameters.GetConnStatus.ConnectionName[1]);
+ ConnectionName.Length = (USHORT)
+ ( InputBuffer->Parameters.GetConnStatus.ConnectionNameLength -
+ sizeof( WCHAR ) );
+ ConnectionName.MaximumLength = ConnectionName.Length;
+
+ fTreeConnections = TRUE;
+
+ DebugTrace( 0, Dbg, "GetConnStatus: Tree is %wZ\n", &ConnectionName );
+
+ } else {
+
+ ConnectionName.Buffer = InputBuffer->Parameters.GetConnStatus.ConnectionName;
+ ConnectionName.Length = (USHORT)
+ (InputBuffer->Parameters.GetConnStatus.ConnectionNameLength);
+ ConnectionName.MaximumLength = ConnectionName.Length;
+
+ fServerConnection = TRUE;
+
+ Status = MakeUidServer( &UidServer, &Uid, &ConnectionName );
+ if ( !NT_SUCCESS( Status )) {
+ return Status;
+ }
+
+ DebugTrace( 0, Dbg, "GetConnStatus: Server is %wZ\n", &UidServer );
+ }
+
+ } else {
+
+ DebugTrace( 0, Dbg, "GetConnectionStatus: Enumerate all connections.\n", 0 );
+
+ }
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Bad input buffer in GetConnStatus.\n" , 0 );
+
+ }
+
+ //
+ // If this is a server connection, find and return it.
+ //
+
+ if ( fServerConnection ) {
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ OwnRcb = TRUE;
+ PrefixEntry = RtlFindUnicodePrefix( &NwRcb.ServerNameTable, &UidServer, 0 );
+
+ if ( !PrefixEntry ) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto ExitWithCleanup;
+ }
+
+ pScb = CONTAINING_RECORD( PrefixEntry, SCB, PrefixEntry );
+
+ if ( ( pScb->PreferredServer ) ||
+ ( pScb->OpenFileCount > 0 ) ) {
+
+ //
+ // If there are open files, we need to return this.
+ // We always write status entries for the preferred
+ // server so that we can give default logon info.
+ //
+
+ goto ProcessServer;
+ }
+
+ //
+ // Are there open handles other than the caller?
+ //
+
+ if ( pScb == pCallerScb ) {
+
+ if ( pScb->IcbCount > 1 ) {
+
+ ASSERT( pScb->pNpScb->Reference > 1 );
+ goto ProcessServer;
+ }
+
+ } else {
+
+ if ( pScb->IcbCount > 0 ) {
+
+ ASSERT( pScb->pNpScb->Reference > 0 );
+ goto ProcessServer;
+ }
+ }
+
+ //
+ // Not an explicit use for this server.
+ //
+ goto ExitWithCleanup;
+
+ProcessServer:
+
+ NwReferenceScb( pScb->pNpScb );
+
+ NwReleaseRcb( &NwRcb );
+ OwnRcb = FALSE;
+
+ Status = WriteConnStatusEntry( pScb,
+ OutputBuffer,
+ OutputBufferLength,
+ &dwBytesWritten,
+ &dwBytesNeeded,
+ (BOOLEAN)( pScb == pCallerScb ) );
+
+ NwDereferenceScb( pScb->pNpScb );
+
+ InputBuffer->Parameters.GetConnStatus.ResumeKey = 0;
+
+ if ( !NT_SUCCESS( Status )) {
+
+ InputBuffer->Parameters.GetConnStatus.EntriesReturned = 0;
+ InputBuffer->Parameters.GetConnStatus.BytesNeeded = dwBytesNeeded;
+ Irp->IoStatus.Information = 0;
+ goto ExitWithCleanup;
+
+ } else {
+
+ InputBuffer->Parameters.GetConnStatus.EntriesReturned = 1;
+ InputBuffer->Parameters.GetConnStatus.BytesNeeded = 0;
+ Irp->IoStatus.Information = dwBytesWritten;
+ goto ExitWithCleanup;
+
+ }
+ }
+
+ //
+ // We want all connections or all tree connections, so
+ // we need to walk the list.
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+ ListEntry = ScbQueue.Flink;
+
+ while ( ListEntry != &ScbQueue ) {
+
+ pNpScb = CONTAINING_RECORD( ListEntry, NONPAGED_SCB, ScbLinks );
+ pScb = pNpScb->pScb;
+
+ NwReferenceScb( pNpScb );
+
+ KeReleaseSpinLock(&ScbSpinLock, OldIrql);
+
+ //
+ // Make sure we pass up the one's we've already returned.
+ //
+
+ if ( ( SequenceNumber >= InputBuffer->Parameters.GetConnStatus.ResumeKey ) &&
+ ( pNpScb != &NwPermanentNpScb ) ) {
+
+ //
+ // If there are open files, we need to return this.
+ // We always write status entries for the preferred
+ // server so that we can give default logon info.
+ //
+
+ if ( ( pScb->PreferredServer ) ||
+ ( pScb->OpenFileCount > 0 ) ) {
+ goto SecondProcessServer;
+ }
+
+ //
+ // Are there any handles other than the caller?
+ //
+
+ if ( pScb == pCallerScb ) {
+
+ if ( pScb->IcbCount > 1 ) {
+
+ ASSERT( pScb->pNpScb->Reference > 2 );
+ goto SecondProcessServer;
+ }
+
+ } else {
+
+ if ( pScb->IcbCount > 0 ) {
+
+ ASSERT( pScb->pNpScb->Reference > 1 );
+ goto SecondProcessServer;
+ }
+ }
+
+ }
+
+ //
+ // Not an interesting server; move to next entry.
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+ ListEntry = pNpScb->ScbLinks.Flink;
+ NwDereferenceScb( pNpScb );
+ SequenceNumber++;
+ continue;
+
+SecondProcessServer:
+
+ //
+ // We have a possible candidate; see if the uid and tree are appropriate.
+ //
+
+ if ( ( (pScb->UserUid).QuadPart != Uid.QuadPart ) ||
+
+ ( fTreeConnections &&
+ !RtlEqualUnicodeString( &(pScb->NdsTreeName),
+ &ConnectionName,
+ TRUE ) ) ) {
+
+ //
+ // No dice. Move onto the next one.
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+ ListEntry = pNpScb->ScbLinks.Flink;
+ NwDereferenceScb( pNpScb );
+ SequenceNumber++;
+ continue;
+
+ }
+
+ //
+ // Ok, we definitely want to report this one.
+ //
+
+ Status = WriteConnStatusEntry( pScb,
+ OutputBuffer,
+ OutputBufferLength,
+ &dwBytesWritten,
+ &dwBytesNeeded,
+ (BOOLEAN)( pScb == pCallerScb ) );
+
+ if ( !NT_SUCCESS( Status )) {
+
+ //
+ // If we couldn't write this entry, then we have to update
+ // the ResumeKey and return. We don't really know how many
+ // more there are going to be so we 'suggest' to the caller
+ // a 2k buffer size.
+ //
+
+ InputBuffer->Parameters.GetConnStatus.ResumeKey = SequenceNumber;
+ InputBuffer->Parameters.GetConnStatus.EntriesReturned = dwReturned;
+ InputBuffer->Parameters.GetConnStatus.BytesNeeded = 2048;
+ NwDereferenceScb( pNpScb );
+ goto ExitWithCleanup;
+
+ } else {
+
+ OutputBuffer = ( OutputBuffer + dwBytesWritten );
+ OutputBufferLength -= dwBytesWritten;
+ dwReturned++;
+ }
+
+ //
+ // Move to next entry in the list.
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+ ListEntry = pNpScb->ScbLinks.Flink;
+ NwDereferenceScb( pNpScb );
+ SequenceNumber++;
+ }
+
+ //
+ // We made it through the list.
+ //
+
+ KeReleaseSpinLock(&ScbSpinLock, OldIrql);
+
+ InputBuffer->Parameters.GetConnStatus.ResumeKey = 0;
+ InputBuffer->Parameters.GetConnStatus.EntriesReturned = dwReturned;
+ InputBuffer->Parameters.GetConnStatus.BytesNeeded = 0;
+
+ Status = STATUS_SUCCESS;
+
+ExitWithCleanup:
+
+ //
+ // If we returned any entries, then set the status to success.
+ //
+
+ if ( dwReturned ) {
+
+ ASSERT( SequenceNumber != 0 );
+ Status = STATUS_SUCCESS;
+ }
+
+ if ( OwnRcb ) {
+ NwReleaseRcb( &NwRcb );
+ }
+
+ if ( UidServer.Buffer != NULL ) {
+ FREE_POOL( UidServer.Buffer );
+ }
+
+ return Status;
+}
+
+NTSTATUS
+GetConnectionInfo(
+ IN PIRP_CONTEXT IrpContext
+ )
+/*+++
+
+GetConnectionInfo:
+
+ Takes a connection name from the new shell and returns
+ some info commonly requested by property sheets and the
+ such.
+
+ The following connection names are supported:
+
+ Drive Letter: "X:"
+ Printer Port: "LPTX:"
+ UNC Name: "\\SERVER\Share\{Path\}
+
+
+---*/
+{
+
+ NTSTATUS Status;
+
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PNWR_REQUEST_PACKET InputBuffer;
+ PCONN_INFORMATION pConnInfo;
+ ULONG InputBufferLength, OutputBufferLength;
+ ULONG BytesNeeded;
+
+ SECURITY_SUBJECT_CONTEXT SubjectContext;
+ LARGE_INTEGER Uid;
+ UNICODE_STRING ConnectionName;
+ UNICODE_STRING UidVolumeName;
+ WCHAR DriveLetter = 0;
+
+ BOOLEAN OwnRcb = FALSE;
+ BOOLEAN ReferenceVcb = FALSE;
+ PVCB Vcb = NULL;
+ PSCB Scb = NULL;
+ PUNICODE_PREFIX_TABLE_ENTRY Prefix;
+
+ PLOGON pLogon;
+ UNICODE_STRING CredentialName;
+ UNICODE_STRING ServerName;
+ PUNICODE_STRING puUserName = NULL;
+ PNDS_SECURITY_CONTEXT pNdsContext;
+ BOOLEAN fHoldingCredentials = FALSE;
+
+ //
+ // Get the input and output buffers.
+ //
+
+ InputBuffer = (PNWR_REQUEST_PACKET) IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
+ InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+ OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+
+ if ( OutputBufferLength ) {
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&pConnInfo );
+ } else {
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ SeCaptureSubjectContext(&SubjectContext);
+ Uid = GetUid( &SubjectContext );
+ SeReleaseSubjectContext(&SubjectContext);
+
+ RtlInitUnicodeString( &UidVolumeName, NULL );
+
+ ConnectionName.Length = (USHORT)(InputBuffer->Parameters).GetConnInfo.ConnectionNameLength;
+ ConnectionName.MaximumLength = ConnectionName.Length;
+ ConnectionName.Buffer = &((InputBuffer->Parameters).GetConnInfo.ConnectionName[0]);
+
+ //
+ // Ok, this gets a little hand-wavey, but we have to try and figure
+ // what this connection name represents.
+ //
+
+ if ( ConnectionName.Length == sizeof( L"X:" ) - sizeof( WCHAR ) ) {
+ DriveLetter = ConnectionName.Buffer[0];
+ } else if ( ConnectionName.Length == sizeof( L"LPT1:" ) - sizeof( WCHAR ) ) {
+ DriveLetter = ConnectionName.Buffer[3];
+ }
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ OwnRcb = TRUE;
+
+ if ( DriveLetter != 0 ) {
+
+ DebugTrace( 0, Dbg, "GetConnectionInfo: Drive %wZ\n", &ConnectionName );
+
+ //
+ // This is a drive relative path. Look up the drive letter.
+ //
+
+ ASSERT( ( DriveLetter >= L'A' && DriveLetter <= L'Z' ) ||
+ ( DriveLetter >= L'1' && DriveLetter <= L'9' ) );
+
+ if ( DriveLetter >= L'A' && DriveLetter <= L'Z' ) {
+ Vcb = DriveMapTable[DriveLetter - L'A'];
+ } else {
+ Vcb = DriveMapTable[MAX_DISK_REDIRECTIONS + DriveLetter - L'1'];
+ }
+
+ //
+ // Was the Vcb created for this user?
+ //
+
+ if ( ( Vcb != NULL ) && ( Uid.QuadPart != Vcb->Scb->UserUid.QuadPart ) ) {
+ Status = STATUS_ACCESS_DENIED;
+ goto ExitWithCleanup;
+ }
+
+ } else {
+
+ //
+ // This is a UNC path. Skip over the backslashes and
+ // prepend the unicode uid.
+ //
+
+ ConnectionName.Length -= (2 * sizeof( WCHAR ) );
+ ConnectionName.Buffer += 2;
+
+ Status = MakeUidServer( &UidVolumeName, &Uid, &ConnectionName );
+
+ if ( !NT_SUCCESS( Status )) {
+ goto ExitWithCleanup;
+ }
+
+ DebugTrace( 0, Dbg, "GetConnectionInfo: %wZ\n", &UidVolumeName );
+
+ Prefix = RtlFindUnicodePrefix( &NwRcb.VolumeNameTable, &UidVolumeName, 0 );
+
+ if ( Prefix != NULL ) {
+ Vcb = CONTAINING_RECORD( Prefix, VCB, PrefixEntry );
+
+ if ( Vcb->Name.Length != UidVolumeName.Length ) {
+ Vcb = NULL;
+ }
+ }
+ }
+
+ if ( !Vcb ) {
+ Status = STATUS_BAD_NETWORK_PATH;
+ goto ExitWithCleanup;
+ }
+
+ DebugTrace( 0, Dbg, "GetConnectionInfo: Vcb is 0x%08lx\n", Vcb );
+
+ NwReferenceVcb( Vcb );
+ ReferenceVcb = TRUE;
+ NwReleaseRcb( &NwRcb );
+ OwnRcb = FALSE;
+
+ //
+ // Get the username. This is the same code block as in
+ // WriteConnStatusEntry; it should be abstracted out.
+ //
+
+ Scb = Vcb->Scb;
+ ASSERT( Scb != NULL );
+
+ if ( ( Scb->MajorVersion > 3 ) &&
+ ( Scb->UserName.Length == 0 ) ) {
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUser( &Uid, FALSE );
+ NwReleaseRcb( &NwRcb );
+
+ if ( pLogon ) {
+
+ Status = NdsLookupCredentials( &(Scb->NdsTreeName),
+ pLogon,
+ &pNdsContext,
+ CREDENTIAL_READ,
+ FALSE );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ fHoldingCredentials = TRUE;
+
+ if ( pNdsContext->Credential != NULL ) {
+
+ CredentialName.Length = pNdsContext->Credential->userNameLength -
+ sizeof( WCHAR );
+ CredentialName.MaximumLength = CredentialName.Length;
+ CredentialName.Buffer = (USHORT *)
+ ( ((BYTE *) pNdsContext->Credential ) +
+ sizeof( NDS_CREDENTIAL ) +
+ pNdsContext->Credential->optDataSize );
+
+ puUserName = &CredentialName;
+ }
+
+ }
+ }
+
+ } else {
+
+ puUserName = &(Scb->UserName);
+
+ }
+
+ DebugTrace( 0, Dbg, "GetConnectionInfo: UserName %wZ\n", puUserName );
+
+ //
+ // Strip off the uid from the server name.
+ //
+
+ ServerName.Length = (Scb->UidServerName).Length;
+ ServerName.Buffer = (Scb->UidServerName).Buffer;
+
+ while ( ServerName.Length ) {
+
+ if ( ServerName.Buffer[0] == L'\\' ) {
+
+ ServerName.Length -= sizeof( WCHAR );
+ ServerName.Buffer += 1;
+ break;
+ }
+
+ ServerName.Length -= sizeof( WCHAR );
+ ServerName.Buffer += 1;
+
+ }
+
+ DebugTrace( 0, Dbg, "GetConnectionInfo: ServerName %wZ\n", &ServerName );
+
+ //
+ // Write a single CONN_INFORMATION structure into the output buffer.
+ //
+
+ if ( puUserName ) {
+
+ BytesNeeded = sizeof( CONN_INFORMATION ) +
+ ServerName.Length +
+ puUserName->Length;
+ } else {
+
+ BytesNeeded = sizeof( CONN_INFORMATION ) +
+ ServerName.Length;
+
+ }
+
+ if ( BytesNeeded > OutputBufferLength ) {
+ Status = STATUS_BUFFER_TOO_SMALL;
+ goto ExitWithCleanup;
+ }
+
+ pConnInfo->HostServerLength = ServerName.Length;
+ pConnInfo->HostServer = (LPWSTR) ( (PBYTE) pConnInfo ) + sizeof( CONN_INFORMATION );
+ RtlCopyMemory( pConnInfo->HostServer, ServerName.Buffer, ServerName.Length );
+
+ pConnInfo->UserName = (LPWSTR) ( ( (PBYTE) pConnInfo->HostServer ) +
+ ServerName.Length );
+
+ if ( puUserName ) {
+
+ pConnInfo->UserNameLength = puUserName->Length;
+ RtlCopyMemory( pConnInfo->UserName, puUserName->Buffer, puUserName->Length );
+
+ } else {
+
+ pConnInfo->UserNameLength = 0;
+ }
+
+ Status = STATUS_SUCCESS;
+
+ExitWithCleanup:
+
+ if ( fHoldingCredentials ) {
+ NwReleaseCredList( pLogon );
+ }
+
+ if ( OwnRcb ) {
+ NwReleaseRcb( &NwRcb );
+ }
+
+ if ( ReferenceVcb ) {
+ NwDereferenceVcb( Vcb, NULL, FALSE );
+ }
+
+ if ( UidVolumeName.Buffer ) {
+ FREE_POOL( UidVolumeName.Buffer );
+ }
+
+ return Status;
+}
+
+NTSTATUS
+GetPreferredServer(
+ IN PIRP_CONTEXT IrpContext
+ )
+/*+++
+
+GetPreferredServer:
+
+ Returns the current preferred server.
+
+---*/
+{
+
+ NTSTATUS Status;
+
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+ BYTE *OutputBuffer;
+ ULONG OutputBufferLength;
+
+ SECURITY_SUBJECT_CONTEXT SubjectContext;
+ LARGE_INTEGER Uid;
+ PLOGON pLogon;
+
+ PUNICODE_STRING PreferredServer;
+
+ //
+ // Get the output buffer.
+ //
+
+ OutputBufferLength = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
+
+ if ( OutputBufferLength ) {
+ NwMapUserBuffer( Irp, KernelMode, (PVOID *)&OutputBuffer );
+ } else {
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // Get the logon structure for the user and return the preferred server.
+ //
+
+ SeCaptureSubjectContext(&SubjectContext);
+ Uid = GetUid( &SubjectContext );
+ SeReleaseSubjectContext(&SubjectContext);
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUser( &Uid, FALSE );
+
+ Status = STATUS_NO_SUCH_LOGON_SESSION;
+
+ if ( ( pLogon ) &&
+ ( pLogon->ServerName.Length ) &&
+ ( ( pLogon->ServerName.Length + sizeof( UNICODE_STRING ) ) <= OutputBufferLength ) ) {
+
+ PreferredServer = (PUNICODE_STRING) OutputBuffer;
+ PreferredServer->Length = pLogon->ServerName.Length;
+ PreferredServer->MaximumLength = pLogon->ServerName.Length;
+ PreferredServer->Buffer = ( PWCHAR ) ( OutputBuffer + sizeof( UNICODE_STRING ) );
+
+ RtlCopyMemory( PreferredServer->Buffer,
+ pLogon->ServerName.Buffer,
+ pLogon->ServerName.Length );
+
+ Status = STATUS_SUCCESS;
+ }
+
+ NwReleaseRcb( &NwRcb );
+
+ return Status;
+}
+
+NTSTATUS
+GetConnectionPerformance(
+ IN PIRP_CONTEXT IrpContext
+ )
+/*+++
+
+GetConnectionPerformance:
+
+ Takes a connection name from the new shell and returns
+ some estimated performance info to the shell so the shell
+ can decide whether or not it wants to download icons, etc.
+
+ The following connection names are supported:
+
+ Drive Letter: "X:"
+ Printer Port: "LPTX:"
+ UNC Name: "\\SERVER\Share\{Path\}
+
+---*/
+{
+
+ NTSTATUS Status;
+
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PNWR_REQUEST_PACKET InputBuffer;
+ ULONG InputBufferLength;
+
+ SECURITY_SUBJECT_CONTEXT SubjectContext;
+ LARGE_INTEGER Uid;
+ UNICODE_STRING RemoteName;
+
+ WCHAR DriveLetter = 0;
+ BOOLEAN OwnRcb = FALSE;
+ BOOLEAN ReferenceScb = FALSE;
+ PVCB Vcb = NULL;
+ PSCB Scb = NULL;
+
+ PLIST_ENTRY ListEntry;
+ UNICODE_STRING OriginalUnc;
+
+ //
+ // Get the input buffer.
+ //
+
+ InputBuffer = (PNWR_REQUEST_PACKET) IrpSp->Parameters.FileSystemControl.Type3InputBuffer;
+ InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+
+ //
+ // Get the UID for the caller.
+ //
+
+ SeCaptureSubjectContext(&SubjectContext);
+ Uid = GetUid( &SubjectContext );
+ SeReleaseSubjectContext(&SubjectContext);
+
+ //
+ // Dig out the remote name.
+ //
+
+ RemoteName.Length = (USHORT)(InputBuffer->Parameters).GetConnPerformance.RemoteNameLength;
+ RemoteName.MaximumLength = RemoteName.Length;
+ RemoteName.Buffer = &((InputBuffer->Parameters).GetConnPerformance.RemoteName[0]);
+
+ //
+ // Ok, this gets a little hand-wavey, but we have to try and figure
+ // what this connection name represents (just like in GetConnectionInfo).
+ //
+
+ if ( RemoteName.Length == sizeof( L"X:" ) - sizeof( WCHAR ) ) {
+ DriveLetter = RemoteName.Buffer[0];
+ } else if ( RemoteName.Length == sizeof( L"LPT1:" ) - sizeof( WCHAR ) ) {
+ DriveLetter = RemoteName.Buffer[3];
+ }
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ OwnRcb = TRUE;
+
+ DebugTrace( 0, Dbg, "GetConnectionPerformance: Remote Name %wZ\n", &RemoteName );
+
+ if ( DriveLetter != 0 ) {
+
+ if ( ! ( ( ( DriveLetter >= L'a' ) && ( DriveLetter <= L'z' ) ) ||
+ ( ( DriveLetter >= L'A' ) && ( DriveLetter <= L'Z' ) ) ||
+ ( ( DriveLetter >= L'0' ) && ( DriveLetter <= L'9' ) ) ) ) {
+
+ Status = STATUS_BAD_NETWORK_PATH;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // This is a drive relative path. Look up the drive letter.
+ //
+
+ if ( DriveLetter >= L'a' && DriveLetter <= L'z' ) {
+ DriveLetter += (WCHAR) ( L'A' - L'a' );
+ }
+
+ if ( DriveLetter >= L'A' && DriveLetter <= L'Z' ) {
+ Vcb = DriveMapTable[DriveLetter - L'A'];
+ } else {
+ Vcb = DriveMapTable[MAX_DISK_REDIRECTIONS + DriveLetter - L'1'];
+ }
+
+ //
+ // Did we get a connection?
+ //
+
+ if ( Vcb == NULL ) {
+ Status = STATUS_BAD_NETWORK_PATH;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Was the Vcb created for this user?
+ //
+
+ if ( Uid.QuadPart != Vcb->Scb->UserUid.QuadPart ) {
+ Status = STATUS_ACCESS_DENIED;
+ goto ExitWithCleanup;
+ }
+
+ Scb = Vcb->Scb;
+
+ } else {
+
+ //
+ // It's valid for the shell to pass us the remote name of a drive
+ // with no reference to the drive at all. Since we file these in
+ // volume prefix table with their drive letter information, we won't
+ // find them if we do a flat munge and lookup. Therefore, we have
+ // to walk the global vcb list and find the match.
+ //
+
+ //
+ // Skip over the first slash of the provided UNC remote name.
+ //
+
+ RemoteName.Length -= sizeof( WCHAR );
+ RemoteName.Buffer += 1;
+
+ for ( ListEntry = GlobalVcbList.Flink;
+ ( ListEntry != &GlobalVcbList ) && ( Scb == NULL );
+ ListEntry = ListEntry->Flink ) {
+
+ Vcb = CONTAINING_RECORD( ListEntry, VCB, GlobalVcbListEntry );
+
+ OriginalUnc.Length = Vcb->Name.Length;
+ OriginalUnc.MaximumLength = Vcb->Name.MaximumLength;
+ OriginalUnc.Buffer = Vcb->Name.Buffer;
+
+ if ( Vcb->DriveLetter ) {
+
+ //
+ // Try it as a drive connection.
+ //
+
+ while ( ( OriginalUnc.Length ) &&
+ ( OriginalUnc.Buffer[0] != L':' ) ) {
+
+ OriginalUnc.Length -= sizeof( WCHAR );
+ OriginalUnc.Buffer += 1;
+ }
+
+ if ( OriginalUnc.Buffer[0] == L':' ) {
+
+ OriginalUnc.Length -= sizeof( WCHAR );
+ OriginalUnc.Buffer += 1;
+
+ if ( RtlEqualUnicodeString( &OriginalUnc,
+ &RemoteName,
+ TRUE ) ) {
+ Scb = Vcb->Scb;
+ }
+ }
+
+ } else {
+
+ //
+ // Try it as a UNC connection; start by skipping
+ // only the leading slash, the walking to the next
+ // slash.
+ //
+
+ OriginalUnc.Length -= sizeof( WCHAR );
+ OriginalUnc.Buffer += 1;
+
+ while ( ( OriginalUnc.Length ) &&
+ ( OriginalUnc.Buffer[0] != L'\\' ) ) {
+
+ OriginalUnc.Length -= sizeof( WCHAR );
+ OriginalUnc.Buffer += 1;
+ }
+
+ if ( OriginalUnc.Length ) {
+
+ if ( RtlEqualUnicodeString( &OriginalUnc,
+ &RemoteName,
+ TRUE ) ) {
+ Scb = Vcb->Scb;
+ }
+ }
+
+ }
+ }
+
+ }
+
+ if ( !Scb ) {
+ Status = STATUS_BAD_NETWORK_PATH;
+ goto ExitWithCleanup;
+ }
+
+ NwReferenceScb( Scb->pNpScb );
+ ReferenceScb = TRUE;
+ NwReleaseRcb( &NwRcb );
+ OwnRcb = FALSE;
+
+ DebugTrace( 0, Dbg, "GetConnectionPerformance: Scb is 0x%08lx\n", Scb );
+
+ //
+ // Now dig out the performance info from the LIP negotiation.
+ //
+ // dwSpeed - The speed of the media to the network resource in units of 100bps (e.g 1,200
+ // baud point to point link returns 12).
+ // dwDelay - The delay introduced by the network when sending information (i.e. the time
+ // between starting sending data and the time that it starts being received) in
+ // units of a millisecond. This is in addition to any latency that was incorporated
+ // into the calculation of dwSpeed, so the value returned will be 0 for accessing
+ // most resources.
+ // dwOptDataSize - A recommendation for the size of data in bytes that is most efficiently
+ // sent through the network when an application makes a single request to
+ // the network resource. For example, for a disk network resource, this
+ // value might be 2048 or 512 when writing a block of data.
+
+ (InputBuffer->Parameters).GetConnPerformance.dwFlags = WNCON_DYNAMIC;
+ (InputBuffer->Parameters).GetConnPerformance.dwDelay = 0;
+ (InputBuffer->Parameters).GetConnPerformance.dwOptDataSize = Scb->pNpScb->BufferSize;
+ (InputBuffer->Parameters).GetConnPerformance.dwSpeed = Scb->pNpScb->LipDataSpeed;
+
+ //
+ // BUGBUG: We don't return any good speed info for servers that have not yet
+ // negotiated lip. We may return out of date information for servers that have
+ // become disconnected unless a RAS line transition occurred. This API is bogus.
+ //
+
+ Status = STATUS_SUCCESS;
+
+ExitWithCleanup:
+
+ if ( OwnRcb ) {
+ NwReleaseRcb( &NwRcb );
+ }
+
+ if ( ReferenceScb ) {
+ NwDereferenceScb( Scb->pNpScb );
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+SetShareBit(
+ IN PIRP_CONTEXT IrpContext,
+ PFILE_OBJECT FileObject
+ )
+/*+++
+
+SetShareBit:
+
+ This function sets the share bit on a file.
+ The bit won't get set until all handles to the
+ file are closed.
+
+---*/
+{
+
+ NTSTATUS Status;
+
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+
+ NODE_TYPE_CODE nodeTypeCode;
+ PICB pIcb;
+ PFCB pFcb;
+ PVOID fsContext, fsContext2;
+
+ DebugTrace( 0, Dbg, "SetShareBit.\n", 0 );
+
+ //
+ // Make sure this is a handle to a file.
+ //
+
+ nodeTypeCode = NwDecodeFileObject( FileObject, &fsContext, &fsContext2 );
+
+ if ( nodeTypeCode != NW_NTC_ICB ) {
+ DebugTrace( 0, Dbg, "You can only set the share bit on a file!\n", 0 );
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ pIcb = (PICB) fsContext2;
+ pFcb = pIcb->SuperType.Fcb;
+
+ if ( pFcb->NodeTypeCode != NW_NTC_FCB ) {
+ DebugTrace( 0, Dbg, "You can't set the share bit on a directory!\n", 0 );
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Acquire this FCB so we can muck with the flags.
+ //
+
+ NwAcquireExclusiveFcb( pFcb->NonPagedFcb, TRUE );
+
+ SetFlag( pFcb->Flags, FCB_FLAGS_LAZY_SET_SHAREABLE );
+
+ NwReleaseFcb( pFcb->NonPagedFcb );
+
+ return STATUS_SUCCESS;
+
+}
+
+VOID
+LazySetShareable(
+ PIRP_CONTEXT IrpContext,
+ PICB pIcb,
+ PFCB pFcb
+)
+/***
+
+Function Description:
+
+ This function gets called everytime an ICB with a remote handle
+ is closed. If we are closing the last ICB to an FCB and the
+ caller has requested that we set the shareable bit on the FCB,
+ then we need to do so now. Otherwise, we simply return.
+
+Caveats:
+
+ If we fail to set the shareable bit, there is no way to notify
+ the requestor of the operation that the operation was not carried
+ out.
+
+***/
+{
+
+ NTSTATUS Status;
+
+ PLIST_ENTRY IcbListEntry;
+ PICB pCurrentIcb;
+ BOOLEAN OtherHandlesExist = FALSE;
+
+ ULONG Attributes;
+ BOOLEAN AttributesAreValid = FALSE;
+
+
+ //
+ // Get to the head of the queue, acquire the RCB,
+ // and acquire this FCB to protect the ICB list
+ // and FCB flags.
+ //
+
+ NwAppendToQueueAndWait( IrpContext );
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ NwAcquireExclusiveFcb( pFcb->NonPagedFcb, TRUE );
+
+ //
+ // Scan the other ICBs on this FCB to see if any of
+ // them have remote handles.
+ //
+
+ for ( IcbListEntry = pFcb->IcbList.Flink;
+ IcbListEntry != &(pFcb->IcbList) ;
+ IcbListEntry = IcbListEntry->Flink ) {
+
+ pCurrentIcb = CONTAINING_RECORD( IcbListEntry, ICB, ListEntry );
+
+ if ( ( pCurrentIcb != pIcb ) &&
+ ( pCurrentIcb->HasRemoteHandle ) ) {
+ OtherHandlesExist = TRUE;
+ }
+ }
+
+ if ( OtherHandlesExist ) {
+
+ //
+ // We'll do it when the last handle is closed.
+ //
+
+ DebugTrace( 0, Dbg, "LazySetShareable: This isn't the last remote handle.\n", 0 );
+ goto ReleaseAllAndExit;
+ }
+
+ //
+ // We're closing the last handle. Make sure we have valid attributes.
+ //
+
+ if ( !FlagOn( pFcb->Flags, FCB_FLAGS_ATTRIBUTES_ARE_VALID ) ) {
+
+ if ( !BooleanFlagOn( pFcb->Flags, FCB_FLAGS_LONG_NAME ) ) {
+
+ Status = ExchangeWithWait ( IrpContext,
+ SynchronousResponseCallback,
+ "FwbbJ",
+ NCP_SEARCH_FILE,
+ -1,
+ pFcb->Vcb->Specific.Disk.Handle,
+ SEARCH_ALL_FILES,
+ &pFcb->RelativeFileName );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ Status = ParseResponse( IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N==_b",
+ 14,
+ &Attributes );
+
+ if ( NT_SUCCESS( Status ) ) {
+ AttributesAreValid = TRUE;
+ }
+ }
+
+ } else {
+
+ Status = ExchangeWithWait ( IrpContext,
+ SynchronousResponseCallback,
+ "LbbWDbDbC",
+ NCP_LFN_GET_INFO,
+ pFcb->Vcb->Specific.Disk.LongNameSpace,
+ pFcb->Vcb->Specific.Disk.LongNameSpace,
+ SEARCH_ALL_FILES,
+ LFN_FLAG_INFO_ATTRIBUTES,
+ pFcb->Vcb->Specific.Disk.VolumeNumber,
+ pFcb->Vcb->Specific.Disk.Handle,
+ 0,
+ &pFcb->RelativeFileName );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ Status = ParseResponse( IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N_e",
+ 4,
+ &Attributes );
+
+ if ( NT_SUCCESS( Status ) ) {
+ AttributesAreValid = TRUE;
+ }
+
+ }
+
+ }
+
+ } else {
+
+ Attributes = pFcb->NonPagedFcb->Attributes;
+ AttributesAreValid = TRUE;
+ }
+
+ if ( !AttributesAreValid ) {
+ DebugTrace( 0, Dbg, "Couldn't get valid attributes for this file.\n", 0 );
+ goto ReleaseAllAndExit;
+ }
+
+ //
+ // Do the set with the shareable bit on!
+ //
+
+ if ( BooleanFlagOn( pFcb->Flags, FCB_FLAGS_LONG_NAME ) ) {
+
+ Status = ExchangeWithWait( IrpContext,
+ SynchronousResponseCallback,
+ "LbbWDW--WW==WW==_W_bDbC",
+ NCP_LFN_SET_INFO,
+ pFcb->Vcb->Specific.Disk.LongNameSpace,
+ pFcb->Vcb->Specific.Disk.LongNameSpace,
+ SEARCH_ALL_FILES,
+ LFN_FLAG_SET_INFO_ATTRIBUTES,
+ Attributes | 0x80,
+ 0,
+ 0,
+ 0,
+ 0,
+ 8,
+ 0,
+ 8,
+ pFcb->Vcb->Specific.Disk.VolumeNumber,
+ pFcb->Vcb->Specific.Disk.Handle,
+ 0,
+ &pFcb->RelativeFileName );
+
+ } else {
+
+ Status = ExchangeWithWait( IrpContext,
+ SynchronousResponseCallback,
+ "FbbbU",
+ NCP_SET_FILE_ATTRIBUTES,
+ Attributes | 0x80,
+ pFcb->Vcb->Specific.Disk.Handle,
+ SEARCH_ALL_FILES,
+ &pFcb->RelativeFileName );
+
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+ DebugTrace( 0, Dbg, "Failed to set the shareable attribute on the file.\n", 0 );
+ ASSERT( FALSE && "File NOT marked as shareable!!" );
+ } else {
+ DebugTrace( 0, Dbg, "Shareable bit successfully set.\n", 0 );
+ ClearFlag( pFcb->Flags, FCB_FLAGS_LAZY_SET_SHAREABLE );
+ }
+
+ReleaseAllAndExit:
+
+ NwReleaseFcb( pFcb->NonPagedFcb );
+ NwReleaseRcb( &NwRcb );
+ NwDequeueIrpContext( IrpContext, FALSE );
+ return;
+}
diff --git a/private/nw/rdr/fspdisp.c b/private/nw/rdr/fspdisp.c
new file mode 100644
index 000000000..bd21d7d2d
--- /dev/null
+++ b/private/nw/rdr/fspdisp.c
@@ -0,0 +1,232 @@
+/*++
+
+Copyright (c) 1989 Microsoft Corporation
+
+Module Name:
+
+ FspDisp.c
+
+Abstract:
+
+ This module implements the main dispatch procedure/thread for the NetWare
+ Fsp
+
+Author:
+
+ Colin Watson [ColinW] 15-Dec-1992
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+
+//
+// Define our local debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_FSP_DISPATCHER)
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwFspDispatch )
+#endif
+
+#if 0 // Not pageable
+NwPostToFsp
+#endif
+
+
+VOID
+NwFspDispatch (
+ IN PVOID Context
+ )
+
+/*++
+
+Routine Description:
+
+ This is the main FSP thread routine that is executed to receive
+ and dispatch IRP requests. Each FSP thread begins its execution here.
+ There is one thread created at system initialization time and subsequent
+ threads created as needed.
+
+Arguments:
+
+
+ Context - Supplies the thread id.
+
+Return Value:
+
+ None - This routine never exits
+
+--*/
+
+{
+ PIRP Irp;
+ PIRP_CONTEXT IrpContext;
+ PIO_STACK_LOCATION IrpSp;
+ NTSTATUS Status;
+ PPOST_PROCESSOR PostProcessRoutine;
+ BOOLEAN TopLevel;
+
+ IrpContext = (PIRP_CONTEXT)Context;
+
+ Irp = IrpContext->pOriginalIrp;
+ ClearFlag( IrpContext->Flags, IRP_FLAG_IN_FSD );
+
+ //
+ // Now case on the function code. For each major function code,
+ // either call the appropriate FSP routine or case on the minor
+ // function and then call the FSP routine. The FSP routine that
+ // we call is responsible for completing the IRP, and not us.
+ // That way the routine can complete the IRP and then continue
+ // post processing as required. For example, a read can be
+ // satisfied right away and then read can be done.
+ //
+ // We'll do all of the work within an exception handler that
+ // will be invoked if ever some underlying operation gets into
+ // trouble (e.g., if NwReadSectorsSync has trouble).
+ //
+
+
+ DebugTrace(0, Dbg, "NwFspDispatch: Irp = 0x%08lx\n", Irp);
+
+ FsRtlEnterFileSystem();
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ try {
+
+ //
+ // If we have a run routine for this IRP context, then run it,
+ // if not fall through to the IRP handler.
+ //
+
+ if ( IrpContext->PostProcessRoutine != NULL ) {
+
+ PostProcessRoutine = IrpContext->PostProcessRoutine;
+
+ //
+ // Clear the run routine so that we don't run it again.
+ //
+
+ IrpContext->PostProcessRoutine = NULL;
+
+ Status = PostProcessRoutine( IrpContext );
+
+ } else {
+
+ IrpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ switch ( IrpSp->MajorFunction ) {
+
+ //
+ // For File System Control operations,
+ //
+
+ case IRP_MJ_FILE_SYSTEM_CONTROL:
+
+ Status = NwCommonFileSystemControl( IrpContext );
+ break;
+
+ //
+ // For any other major operations, return an invalid
+ // request.
+ //
+
+ default:
+
+ Status = STATUS_INVALID_DEVICE_REQUEST;
+ break;
+
+ }
+
+ }
+
+ //
+ // We're done with this request. Dequeue the IRP context from
+ // SCB and complete the request.
+ //
+
+ if ( Status != STATUS_PENDING ) {
+ NwDequeueIrpContext( IrpContext, FALSE );
+ }
+
+ NwCompleteRequest( IrpContext, Status );
+
+ } except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
+
+ //
+ // We had some trouble trying to perform the requested
+ // operation, so we'll abort the I/O request with
+ // the error status that we get back from the
+ // execption code.
+ //
+
+ (VOID) NwProcessException( IrpContext, GetExceptionCode() );
+ }
+
+ if ( TopLevel ) {
+ NwSetTopLevelIrp( NULL );
+ }
+ FsRtlExitFileSystem();
+
+ return;
+}
+
+
+NTSTATUS
+NwPostToFsp (
+ IN PIRP_CONTEXT IrpContext,
+ IN BOOLEAN MarkIrpPending
+ )
+
+/*++
+
+Routine Description:
+
+ This routine post an IRP context to an executive worker thread
+ for FSP level processing.
+
+ *** WARNING: After calling this routine, the caller may no
+ longer access IrpContext. This routine passes
+ the IrpContext to the FSP which may run and free
+ the IrpContext before this routine returns to the
+ caller.
+
+Arguments:
+
+ IrpContext - Supplies the Irp being processed.
+
+ MarkIrpPending - If true, mark the IRP pending.
+
+Return Value:
+
+ STATUS_PENDING.
+
+--*/
+
+{
+ PIRP Irp = IrpContext->pOriginalIrp;
+
+ DebugTrace(0, Dbg, "NwPostToFsp: IrpContext = %X\n", IrpContext );
+ DebugTrace(0, Dbg, "PostProcessRoutine = %X\n", IrpContext->PostProcessRoutine );
+
+ if ( MarkIrpPending ) {
+
+ //
+ // Mark this I/O request as being pending.
+ //
+
+ IoMarkIrpPending( Irp );
+ }
+
+ //
+ // Queue to IRP context to an ex worker thread.
+ //
+
+ ExInitializeWorkItem( &IrpContext->WorkQueueItem, NwFspDispatch, IrpContext );
+ ExQueueWorkItem( &IrpContext->WorkQueueItem, DelayedWorkQueue );
+
+ return( STATUS_PENDING );
+}
+
diff --git a/private/nw/rdr/init.c b/private/nw/rdr/init.c
new file mode 100644
index 000000000..e24479ab7
--- /dev/null
+++ b/private/nw/rdr/init.c
@@ -0,0 +1,460 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ NwInit.c
+
+Abstract:
+
+ This module implements the DRIVER_INITIALIZATION routine for NetWare
+
+Author:
+
+ Colin Watson [ColinW] 15-Dec-1992
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+#define Dbg (DEBUG_TRACE_LOAD)
+
+//
+// Private declaration because ZwQueryDefaultLocale isn't in any header.
+//
+
+NTSYSAPI
+NTSTATUS
+NTAPI
+ZwQueryDefaultLocale(
+ IN BOOLEAN UserProfile,
+ OUT PLCID DefaultLocaleId
+ );
+
+NTSTATUS
+DriverEntry(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PUNICODE_STRING RegistryPath
+ );
+
+VOID
+UnloadDriver(
+ IN PDRIVER_OBJECT DriverObject
+ );
+
+VOID
+GetConfigurationInformation(
+ PUNICODE_STRING RegistryPath
+ );
+
+VOID
+ReadValue(
+ HANDLE ParametersHandle,
+ PLONG pVar,
+ PWCHAR Name
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, DriverEntry )
+#pragma alloc_text( PAGE, GetConfigurationInformation )
+#pragma alloc_text( PAGE, ReadValue )
+#endif
+
+#if 0 // Not pageable
+UnloadDriver
+#endif
+
+static ULONG IrpStackSize;
+
+
+NTSTATUS
+DriverEntry(
+ IN PDRIVER_OBJECT DriverObject,
+ IN PUNICODE_STRING RegistryPath
+ )
+/*++
+
+Routine Description:
+
+ This is the initialization routine for the Nw file system
+ device driver. This routine creates the device object for the FileSystem
+ device and performs all other driver initialization.
+
+Arguments:
+
+ DriverObject - Pointer to driver object created by the system.
+
+Return Value:
+
+ NTSTATUS - The function value is the final status from the initialization
+ operation.
+
+--*/
+
+{
+ NTSTATUS Status;
+ UNICODE_STRING UnicodeString;
+ PAGED_CODE();
+
+ //DbgBreakPoint();
+
+ InitializeAttach( );
+ NwInitializeData();
+ NwInitializePidTable();
+
+ //
+ // Create the device object.
+ //
+
+ RtlInitUnicodeString( &UnicodeString, DD_NWFS_DEVICE_NAME_U );
+ Status = IoCreateDevice( DriverObject,
+ 0,
+ &UnicodeString,
+ FILE_DEVICE_NETWORK_FILE_SYSTEM,
+ FILE_REMOTE_DEVICE,
+ FALSE,
+ &FileSystemDeviceObject );
+
+ if (!NT_SUCCESS( Status )) {
+ Error(EVENT_NWRDR_CANT_CREATE_DEVICE, Status, NULL, 0, 0);
+ return Status;
+ }
+
+ //
+ // Initialize parameters to the defaults.
+ //
+
+ IrpStackSize = NWRDR_IO_STACKSIZE;
+
+ //
+ // Attempt to read config information from the registry
+ //
+
+ GetConfigurationInformation( RegistryPath );
+
+ //
+ // Set the stack size.
+ //
+
+ FileSystemDeviceObject->StackSize = (CCHAR)IrpStackSize;
+
+ //
+ // Initialize the driver object with this driver's entry points.
+ //
+
+ DriverObject->MajorFunction[IRP_MJ_CREATE] = (PDRIVER_DISPATCH)NwFsdCreate;
+ DriverObject->MajorFunction[IRP_MJ_CLEANUP] = (PDRIVER_DISPATCH)NwFsdCleanup;
+ DriverObject->MajorFunction[IRP_MJ_CLOSE] = (PDRIVER_DISPATCH)NwFsdClose;
+ DriverObject->MajorFunction[IRP_MJ_FILE_SYSTEM_CONTROL] = (PDRIVER_DISPATCH)NwFsdFileSystemControl;
+ DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = (PDRIVER_DISPATCH)NwFsdDeviceIoControl;
+ DriverObject->MajorFunction[IRP_MJ_QUERY_INFORMATION] = (PDRIVER_DISPATCH)NwFsdQueryInformation;
+ DriverObject->MajorFunction[IRP_MJ_QUERY_VOLUME_INFORMATION] = (PDRIVER_DISPATCH)NwFsdQueryVolumeInformation;
+ DriverObject->MajorFunction[IRP_MJ_SET_VOLUME_INFORMATION] = (PDRIVER_DISPATCH)NwFsdSetVolumeInformation;
+ DriverObject->MajorFunction[IRP_MJ_DIRECTORY_CONTROL] = (PDRIVER_DISPATCH)NwFsdDirectoryControl;
+ DriverObject->MajorFunction[IRP_MJ_READ] = (PDRIVER_DISPATCH)NwFsdRead;
+ DriverObject->MajorFunction[IRP_MJ_WRITE] = (PDRIVER_DISPATCH)NwFsdWrite;
+ DriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] = (PDRIVER_DISPATCH)NwFsdSetInformation;
+ DriverObject->MajorFunction[IRP_MJ_LOCK_CONTROL] = (PDRIVER_DISPATCH)NwFsdLockControl;
+ DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS] = (PDRIVER_DISPATCH)NwFsdFlushBuffers;
+/*
+ DriverObject->MajorFunction[IRP_MJ_QUERY_EA] = (PDRIVER_DISPATCH)NwFsdQueryEa;
+ DriverObject->MajorFunction[IRP_MJ_SET_EA] = (PDRIVER_DISPATCH)NwFsdSetEa;
+ DriverObject->MajorFunction[IRP_MJ_SHUTDOWN] = (PDRIVER_DISPATCH)NwFsdShutdown;
+*/
+ DriverObject->DriverUnload = UnloadDriver;
+
+#if NWFASTIO
+ DriverObject->FastIoDispatch = &NwFastIoDispatch;
+
+ NwFastIoDispatch.SizeOfFastIoDispatch = sizeof(FAST_IO_DISPATCH);
+ NwFastIoDispatch.FastIoCheckIfPossible = NULL;
+ NwFastIoDispatch.FastIoRead = NwFastRead;
+ NwFastIoDispatch.FastIoWrite = NwFastWrite;
+ NwFastIoDispatch.FastIoQueryBasicInfo = NwFastQueryBasicInfo;
+ NwFastIoDispatch.FastIoQueryStandardInfo = NwFastQueryStandardInfo;
+ NwFastIoDispatch.FastIoLock = NULL;
+ NwFastIoDispatch.FastIoUnlockSingle = NULL;
+ NwFastIoDispatch.FastIoUnlockAll = NULL;
+ NwFastIoDispatch.FastIoUnlockAllByKey = NULL;
+ NwFastIoDispatch.FastIoDeviceControl = NULL;
+#endif
+
+ NwInitializeRcb( &NwRcb );
+
+ InitializeIrpContext( );
+
+ NwPermanentNpScb.State = SCB_STATE_DISCONNECTING;
+
+ //
+ // Do a kludge here so that we get to the "real" global variables.
+ //
+
+ //NlsLeadByteInfo = *(PUSHORT *)NlsLeadByteInfo;
+ //NlsMbCodePageTag = *(*(PBOOLEAN *)&NlsMbCodePageTag);
+
+#ifndef IFS
+ FsRtlLegalAnsiCharacterArray = *(PUCHAR *)FsRtlLegalAnsiCharacterArray;
+#endif
+
+ DebugTrace(0, Dbg, "NetWare redirector loaded\n", 0);
+
+ //
+ // And return to our caller
+ //
+
+ return( STATUS_SUCCESS );
+}
+
+VOID
+UnloadDriver(
+ IN PDRIVER_OBJECT DriverObject
+ )
+/*++
+
+Routine Description:
+
+ This is the unload routine for the NetWare redirector filesystem.
+
+Arguments:
+
+ DriverObject - pointer to the driver object for the redirector
+
+Return Value:
+
+ None
+
+--*/
+{
+ KIRQL OldIrql;
+
+ PAGED_CODE();
+
+ IpxClose();
+
+ IPX_Close_Socket( &NwPermanentNpScb.Server );
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+ RemoveEntryList( &NwPermanentNpScb.ScbLinks );
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+ DestroyAllScb();
+
+ UninitializeIrpContext();
+
+ if (IpxTransportName.Buffer != NULL) {
+
+ FREE_POOL(IpxTransportName.Buffer);
+
+ }
+
+ if ( NwProviderName.Buffer != NULL ) {
+ FREE_POOL( NwProviderName.Buffer );
+ }
+
+ NwUninitializePidTable();
+
+ ASSERT( IsListEmpty( &NwPagedPoolList ) );
+ ASSERT( IsListEmpty( &NwNonpagedPoolList ) );
+
+ ASSERT( MdlCount == 0 );
+ ASSERT( IrpCount == 0 );
+
+ NwDeleteRcb( &NwRcb );
+
+#ifdef NWDBG
+ ExDeleteResource( &NwDebugResource );
+#endif
+
+ ExDeleteResource( &NwOpenResource );
+ ExDeleteResource( &NwUnlockableCodeResource );
+
+ IoDeleteDevice(FileSystemDeviceObject);
+
+ DebugTrace(0, Dbg, "NetWare redirector unloaded\n\n", 0);
+
+}
+
+
+
+VOID
+GetConfigurationInformation(
+ PUNICODE_STRING RegistryPath
+ )
+/*++
+
+Routine Description:
+
+ This routine read redirector configuration information from the registry.
+
+Arguments:
+
+ RegistryPath - A pointer the a path to the
+
+Return Value:
+
+ None
+
+--*/
+{
+ UNICODE_STRING UnicodeString;
+ HANDLE ConfigHandle;
+ HANDLE ParametersHandle;
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ ULONG TimeOutEventinMins = 0L;
+ LCID lcid;
+
+ PAGED_CODE();
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ RegistryPath, // name
+ OBJ_CASE_INSENSITIVE, // attributes
+ NULL, // root
+ NULL // security descriptor
+ );
+
+ Status = ZwOpenKey ( &ConfigHandle, KEY_READ, &ObjectAttributes );
+
+ if (!NT_SUCCESS(Status)) {
+ return;
+ }
+
+ RtlInitUnicodeString( &UnicodeString, L"Parameters" );
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &UnicodeString,
+ OBJ_CASE_INSENSITIVE,
+ ConfigHandle,
+ NULL
+ );
+
+ Status = ZwOpenKey( &ParametersHandle, KEY_READ, &ObjectAttributes );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ ZwClose( ConfigHandle );
+ return;
+ }
+
+ ReadValue( ParametersHandle, &IrpStackSize, L"IrpStackSize" );
+
+ ReadValue( ParametersHandle, &MaxSendDelay, L"MaxSendDelay" );
+ ReadValue( ParametersHandle, &MaxReceiveDelay, L"MaxReceiveDelay" );
+
+ ReadValue( ParametersHandle, &MinSendDelay, L"MinSendDelay" );
+ ReadValue( ParametersHandle, &MinReceiveDelay, L"MinReceiveDelay" );
+
+ ReadValue( ParametersHandle, &BurstSuccessCount, L"BurstSuccessCount" );
+ ReadValue( ParametersHandle, &BurstSuccessCount2, L"BurstSuccessCount2" );
+ ReadValue( ParametersHandle, &MaxReadTimeout, L"MaxReadTimeout" );
+ ReadValue( ParametersHandle, &MaxWriteTimeout, L"MaxWriteTimeout" );
+ ReadValue( ParametersHandle, &ReadTimeoutMultiplier, L"ReadTimeoutMultiplier" );
+ ReadValue( ParametersHandle, &WriteTimeoutMultiplier, L"WriteTimeoutMultiplier" );
+ ReadValue( ParametersHandle, &AllowGrowth, L"AllowGrowth" );
+ ReadValue( ParametersHandle, &DontShrink, L"DontShrink" );
+ ReadValue( ParametersHandle, &SendExtraNcp, L"SendExtraNcp" );
+ ReadValue( ParametersHandle, &DefaultMaxPacketSize, L"DefaultMaxPacketSize" );
+ ReadValue( ParametersHandle, &PacketThreshold, L"PacketThreshold" );
+ ReadValue( ParametersHandle, &LargePacketAdjustment, L"LargePacketAdjustment" );
+ ReadValue( ParametersHandle, &LipPacketAdjustment, L"LipPacketAdjustment" );
+ ReadValue( ParametersHandle, &LipAccuracy, L"LipAccuracy" );
+
+ ReadValue( ParametersHandle, &DisableReadCache, L"DisableReadCache" );
+ ReadValue( ParametersHandle, &DisableWriteCache, L"DisableWriteCache" );
+ ReadValue( ParametersHandle, &FavourLongNames, L"FavourLongNames" );
+
+ ReadValue( ParametersHandle, &LockTimeoutThreshold, L"LockTimeout" );
+
+ ReadValue( ParametersHandle, &TimeOutEventinMins, L"TimeOutEventinMins");
+
+ ReadValue( ParametersHandle, &EnableMultipleConnects, L"EnableMultipleConnects");
+
+ ReadValue( ParametersHandle, &ReadExecOnlyFiles, L"ReadExecOnlyFiles");
+
+ if (!TimeOutEventinMins) {
+ //
+ // If for some reason, the registry has set the TimeOutEventInterval
+ // to zero, reset to the default value to avoid divide-by-zero
+ //
+
+ TimeOutEventinMins = DEFAULT_TIMEOUT_EVENT_INTERVAL;
+ }
+
+ TimeOutEventInterval.QuadPart = TimeOutEventinMins * 60 * SECONDS;
+
+ ZwClose( ParametersHandle );
+ ZwClose( ConfigHandle );
+
+ Japan = FALSE;
+
+
+ ZwQueryDefaultLocale( TRUE, &lcid );
+
+ if (PRIMARYLANGID(lcid) == LANG_JAPANESE ||
+ PRIMARYLANGID(lcid) == LANG_KOREAN ||
+ PRIMARYLANGID(lcid) == LANG_CHINESE) {
+
+ Japan = TRUE;
+ }
+
+}
+
+
+VOID
+ReadValue(
+ HANDLE ParametersHandle,
+ PLONG pVar,
+ PWCHAR Name
+ )
+/*++
+
+Routine Description:
+
+ This routine reads a single redirector configuration value from the registry.
+
+Arguments:
+
+ Parameters - Supplies where to look for values.
+
+ pVar - Address of the variable to receive the new value if the name exists.
+
+ Name - Name whose value is to be loaded.
+
+Return Value:
+
+ None
+
+--*/
+{
+ WCHAR Storage[256];
+ UNICODE_STRING UnicodeString;
+ NTSTATUS Status;
+ ULONG BytesRead;
+ PKEY_VALUE_FULL_INFORMATION Value = (PKEY_VALUE_FULL_INFORMATION)Storage;
+
+ PAGED_CODE();
+
+ UnicodeString.Buffer = Storage;
+
+ RtlInitUnicodeString(&UnicodeString, Name );
+
+ Status = ZwQueryValueKey(
+ ParametersHandle,
+ &UnicodeString,
+ KeyValueFullInformation,
+ Value,
+ sizeof(Storage),
+ &BytesRead );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ if ( Value->DataLength >= sizeof(ULONG) ) {
+
+ *pVar = *(LONG UNALIGNED *)( (PCHAR)Value + Value->DataOffset );
+
+ }
+ }
+}
diff --git a/private/nw/rdr/ipx.c b/private/nw/rdr/ipx.c
new file mode 100644
index 000000000..2341d1cd3
--- /dev/null
+++ b/private/nw/rdr/ipx.c
@@ -0,0 +1,1749 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ Ipx.c
+
+Abstract:
+
+ This module implements the low level Ipx support routines for the NetWare
+ redirector.
+
+Author:
+
+ Colin Watson [ColinW] 28-Dec-1992
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+#include "wsnwlink.h"
+
+//
+// Define IPX interfaces that should be in a public header file but aren't
+// (at least for NT 1.0). For Daytona, include isnkrnl.h.
+//
+
+#define IPX_ID 'M'<<24 | 'I'<<16 | 'P'<<8 | 'X'
+
+#define I_MIPX (('I' << 24) | ('D' << 16) | ('P' << 8))
+#define MIPX_SENDPTYPE I_MIPX | 118 /* Send ptype in options on recv*/
+#define MIPX_RERIPNETNUM I_MIPX | 144 /* ReRip a network */
+#define MIPX_GETNETINFO I_MIPX | 135 /* Get info on a network num */
+#define MIPX_LINECHANGE I_MIPX | 310 /* queued until WAN line goes up/down */
+
+#define Dbg (DEBUG_TRACE_IPX)
+
+extern BOOLEAN WorkerRunning; // From timer.c
+
+extern POBJECT_TYPE *IoFileObjectType;
+
+typedef TA_IPX_ADDRESS UNALIGNED *PUTA_IPX_ADDRESS;
+
+typedef struct _ADDRESS_INFORMATION {
+ ULONG ActivityCount;
+ TA_IPX_ADDRESS NetworkName;
+ ULONG Unused; // Junk needed to work around streams NWLINK bug.
+} ADDRESS_INFORMATION, *PADDRESS_INFORMATION;
+
+//
+// Handle difference between NT1.0 and use of ntifs.h
+//
+#ifdef IFS
+ #define ATTACHPROCESS(_X) KeAttachProcess(_X);
+#else
+ #define ATTACHPROCESS(_X) KeAttachProcess(&(_X)->Pcb);
+#endif
+
+NTSTATUS
+SubmitTdiRequest (
+ IN PDEVICE_OBJECT pDeviceObject,
+ IN PIRP pIrp
+ );
+
+NTSTATUS
+CompletionEvent(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ );
+
+NTSTATUS
+QueryAddressInformation(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PNW_TDI_STRUCT pTdiStruct,
+ OUT PADDRESS_INFORMATION AddressInformation
+ );
+
+NTSTATUS
+QueryProviderInformation(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PNW_TDI_STRUCT pTdiStruct,
+ OUT PTDI_PROVIDER_INFO ProviderInfo
+ );
+
+USHORT
+GetSocketNumber(
+ IN PIRP_CONTEXT pIrpC,
+ IN PNW_TDI_STRUCT pTdiStruc
+ );
+
+NTSTATUS
+SetTransportOption(
+ IN PIRP_CONTEXT pIrpC,
+ IN PNW_TDI_STRUCT pTdiStruc,
+ IN ULONG Option
+ );
+
+#ifndef QFE_BUILD
+
+NTSTATUS
+CompletionLineChange(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ );
+
+#endif
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, IPX_Get_Local_Target )
+#pragma alloc_text( PAGE, IPX_Get_Internetwork_Address )
+#pragma alloc_text( PAGE, IPX_Get_Interval_Marker )
+#pragma alloc_text( PAGE, IPX_Open_Socket )
+#pragma alloc_text( PAGE, IPX_Close_Socket )
+#pragma alloc_text( PAGE, IpxOpen )
+#pragma alloc_text( PAGE, IpxOpenHandle )
+#pragma alloc_text( PAGE, BuildIpxAddressEa )
+#pragma alloc_text( PAGE, IpxClose )
+#pragma alloc_text( PAGE, SetEventHandler )
+#pragma alloc_text( PAGE, SubmitTdiRequest )
+#pragma alloc_text( PAGE, GetSocketNumber )
+#pragma alloc_text( PAGE, GetMaximumPacketSize )
+#pragma alloc_text( PAGE, QueryAddressInformation )
+#pragma alloc_text( PAGE, QueryProviderInformation )
+#pragma alloc_text( PAGE, SetTransportOption )
+#pragma alloc_text( PAGE, GetNewRoute )
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE, SubmitLineChangeRequest )
+#pragma alloc_text( PAGE, FspProcessLineChange )
+#endif
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, CompletionEvent )
+#endif
+
+#endif
+
+#if 0 // Not pageable
+BuildIpxAddress
+CompletionLineChange
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+
+NTSTATUS
+IPX_Get_Local_Target(
+ IN IPXaddress* RemoteAddress,
+ OUT NodeAddress* LocalTarget,
+ OUT word* Ticks
+ )
+/*++
+
+Routine Description:
+
+ Determine the address in the caller's own network to which to transmit
+ in order to reach the specified machine.
+
+ This is not required for NT since the IPX transport handles the
+ issue of determining routing between this machine and the remote
+ address.
+
+Arguments:
+
+ RemoteAddress - Supplies the remote computers address
+ NodeAddress - Where to store the intermediate machine address
+ Ticks - Returns the expected number of ticks to reach the remote address
+
+Return Value:
+
+ status of the operation
+
+--*/
+{
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "IPX_Get_Local_Target\n", 0);
+ return STATUS_NOT_IMPLEMENTED;
+}
+
+
+VOID
+IPX_Get_Internetwork_Address(
+ OUT IPXaddress* LocalAddress
+ )
+/*++
+
+Routine Description:
+
+ Determine the callers full address in a set of interconnected networks.
+ in order to reach the specified machine.
+
+ This is not required for NT since the IPX transport handles the
+ issue of determining routing between this machine and the remote
+ address.
+
+Arguments:
+
+ LocalAddress - Where to store the local address
+
+Return Value:
+
+ none
+
+--*/
+{
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "IPX_Get_Internetwork_Address\n", 0);
+ RtlFillMemory(LocalAddress, sizeof(IPXaddress), 0xff);
+}
+
+
+word
+IPX_Get_Interval_Marker(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Determine the interval marker in clock ticks.
+
+Arguments:
+
+Return Value:
+
+ interval marker
+
+--*/
+{
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "IPX_Get_Interval_Marker\n", 0);
+ return 0xff;
+}
+
+
+NTSTATUS
+IPX_Open_Socket(
+ IN PIRP_CONTEXT pIrpC,
+ IN PNW_TDI_STRUCT pTdiStruc
+ )
+/*++
+
+Routine Description:
+
+ Open a local socket to be used for a conection to a remote server.
+
+Arguments:
+
+ pIrpC - supplies the irp context for the request creating the socket.
+
+ pTdiStruc - supplies where to record the handle and both device and file
+ object pointers
+
+Return Value:
+
+ 0 success
+
+--*/
+{
+ NTSTATUS Status;
+ UCHAR NetworkName[ sizeof( FILE_FULL_EA_INFORMATION )-1 +
+ TDI_TRANSPORT_ADDRESS_LENGTH + 1 +
+ sizeof(TA_IPX_ADDRESS)];
+
+ static UCHAR LocalNodeAddress[6] = {0,0,0,0,0,0};
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "IPX_Open_Socket %X\n", pTdiStruc->Socket);
+
+ //
+ // Let the transport decide the network number and node address
+ // if the caller specified socket 0. This will allow the transport
+ // to use whatever local adapters are available to get to the
+ // remote server.
+ //
+
+ BuildIpxAddressEa( (ULONG)0,
+ LocalNodeAddress,
+ (USHORT)pTdiStruc->Socket,
+ &NetworkName );
+
+ Status = IpxOpenHandle( &pTdiStruc->Handle,
+ &pTdiStruc->pDeviceObject,
+ &pTdiStruc->pFileObject,
+ &NetworkName,
+ FIELD_OFFSET( FILE_FULL_EA_INFORMATION, EaName[0] ) +
+ TDI_TRANSPORT_ADDRESS_LENGTH + 1 +
+ sizeof(TA_IPX_ADDRESS));
+
+ if ( !NT_SUCCESS(Status) ) {
+ return( Status );
+ }
+
+ if ( pTdiStruc->Socket == 0 ) {
+
+ //
+ // Find out the socket number assigned by the transport
+ //
+
+ pTdiStruc->Socket = GetSocketNumber( pIrpC, pTdiStruc );
+ DebugTrace(0, Dbg, "Assigned socket number %X\n", pTdiStruc->Socket );
+ }
+
+ //
+ // Tell transport to accept packet type being set in the connection
+ // information provided with the send datagram. Transport reports
+ // the packet type similarly on receive datagram.
+ // BUGBUG we should get the ioctl codes for ipx into a standard place
+ //
+
+ Status = SetTransportOption(
+ pIrpC,
+ pTdiStruc,
+ MIPX_SENDPTYPE );
+
+ DebugTrace(-1, Dbg, " %X\n", Status );
+ return Status;
+}
+
+
+
+VOID
+IPX_Close_Socket(
+ IN PNW_TDI_STRUCT pTdiStruc
+ )
+/*++
+
+Routine Description:
+
+ Terminate a connection over the network.
+
+Arguments:
+
+ pTdiStruc - supplies where to record the handle and both device and file
+ object pointers
+
+Return Value:
+
+ none
+
+--*/
+{
+ BOOLEAN ProcessAttached = FALSE;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "IPX_Close_Socket %x\n", pTdiStruc->Socket);
+
+ if ( pTdiStruc->Handle == NULL ) {
+ return;
+ }
+
+ ObDereferenceObject( pTdiStruc->pFileObject );
+
+ //
+ // Attach to the redirector's FSP to allow the handle for the
+ // connection to hang around.
+ //
+
+ if (PsGetCurrentProcess() != FspProcess) {
+ ATTACHPROCESS(FspProcess);
+ ProcessAttached = TRUE;
+ }
+
+ ZwClose( pTdiStruc->Handle );
+
+ if (ProcessAttached) {
+ //
+ // Now re-attach back to our original process
+ //
+
+ KeDetachProcess();
+ }
+
+ pTdiStruc->Handle = NULL;
+
+ pTdiStruc->pFileObject = NULL;
+
+ DebugTrace(-1, Dbg, "IPX_Close_Socket\n", 0);
+ return;
+}
+
+
+NTSTATUS
+IpxOpen(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Open handle to the Ipx transport.
+
+Arguments:
+
+ none.
+
+Return Value:
+
+ none
+
+--*/
+{
+ NTSTATUS Status;
+
+ Status = IpxOpenHandle( &IpxHandle,
+ &pIpxDeviceObject,
+ &pIpxFileObject,
+ NULL,
+ 0 );
+
+ DebugTrace(-1, Dbg, "IpxOpen of local node address %X\n", Status);
+ return Status;
+}
+
+
+NTSTATUS
+IpxOpenHandle(
+ OUT PHANDLE pHandle,
+ OUT PDEVICE_OBJECT* ppDeviceObject,
+ OUT PFILE_OBJECT* ppFileObject,
+ IN PVOID EaBuffer OPTIONAL,
+ IN ULONG EaLength
+ )
+/*++
+
+Routine Description:
+
+ Open handle to the Ipx transport.
+
+Arguments:
+
+ OUT Handle - The handle to the transport if return value is NT_SUCCESS
+
+Return Value:
+
+ none
+
+--*/
+{
+ OBJECT_ATTRIBUTES AddressAttributes;
+ IO_STATUS_BLOCK IoStatusBlock;
+ NTSTATUS Status;
+ BOOLEAN ProcessAttached = FALSE;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "IpxOpenHandle\n", 0);
+
+ *pHandle = NULL;
+
+ InitializeObjectAttributes (&AddressAttributes,
+ &IpxTransportName,
+ OBJ_CASE_INSENSITIVE,// Attributes
+ NULL, // RootDirectory
+ NULL); // SecurityDescriptor
+
+ //
+ // Attach to the redirector's FSP to allow the handle for the
+ // connection to hang around. Normally we create 3 handles at once
+ // so the outer code already has done this to avoid the expensive
+ // attach procedure.
+ //
+
+ if (PsGetCurrentProcess() != FspProcess) {
+ ATTACHPROCESS(FspProcess);
+ ProcessAttached = TRUE;
+ }
+
+ Status = ZwCreateFile(pHandle,
+ GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
+ &AddressAttributes, // Object Attributes
+ &IoStatusBlock, // Final I/O status block
+ NULL, // Allocation Size
+ FILE_ATTRIBUTE_NORMAL, // Normal attributes
+ FILE_SHARE_READ,// Sharing attributes
+ FILE_OPEN_IF, // Create disposition
+ 0, // CreateOptions
+ EaBuffer,EaLength);
+
+ if (!NT_SUCCESS(Status) ||
+ !NT_SUCCESS(Status = IoStatusBlock.Status)) {
+
+ goto error_cleanup2;
+
+ }
+
+ //
+ // Obtain a referenced pointer to the file object.
+ //
+ Status = ObReferenceObjectByHandle (
+ *pHandle,
+ 0,
+ NULL,
+ KernelMode,
+ ppFileObject,
+ NULL
+ );
+
+ if (!NT_SUCCESS(Status)) {
+
+ goto error_cleanup;
+
+ }
+
+ if (ProcessAttached) {
+
+ //
+ // Now re-attach back to our original process
+ //
+
+ KeDetachProcess();
+ }
+
+ *ppDeviceObject = IoGetRelatedDeviceObject( *ppFileObject );
+
+ DebugTrace(-1, Dbg, "IpxOpenHandle %X\n", Status);
+ return Status;
+
+error_cleanup2:
+
+ ASSERT( *pHandle != NULL );
+ ZwClose( *pHandle );
+ *pHandle = NULL;
+
+error_cleanup:
+ if (ProcessAttached) {
+
+ //
+ // Now re-attach back to our original process
+ //
+
+ KeDetachProcess();
+ }
+
+ DebugTrace(-1, Dbg, "IpxOpenHandle %X\n", Status);
+ return Status;
+}
+
+
+VOID
+BuildIpxAddress(
+ IN ULONG NetworkAddress,
+ IN PUCHAR NodeAddress,
+ IN USHORT Socket,
+ OUT PTA_IPX_ADDRESS NetworkName
+ )
+
+/*++
+
+Routine Description:
+
+ This routine builds a TA_NETBIOS_ADDRESS structure in the locations pointed
+ to by NetworkName. All fields are filled out.
+
+Arguments:
+ NetworkAddress - Supplies the network number
+ NodeAddress - Supplies the node number
+ Socket - The socket number (in Hi-Lo order)
+ NetworkName - Supplies the structure to place the address
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ // Warn compiler that TAAddressCount may be mis-aligned.
+ PUTA_IPX_ADDRESS UNetworkName = (PUTA_IPX_ADDRESS)NetworkName;
+
+ DebugTrace(+0, Dbg, "BuildIpxAddress\n", 0);
+
+ UNetworkName->TAAddressCount = 1;
+ UNetworkName->Address[0].AddressType = TDI_ADDRESS_TYPE_IPX;
+ UNetworkName->Address[0].AddressLength = TDI_ADDRESS_LENGTH_IPX;
+
+ RtlMoveMemory (
+ UNetworkName->Address[0].Address[0].NodeAddress,
+ NodeAddress,
+ 6);
+ UNetworkName->Address[0].Address[0].NetworkAddress = NetworkAddress;
+ UNetworkName->Address[0].Address[0].Socket = Socket;
+
+} /* TdiBuildIpxAddress */
+
+
+VOID
+BuildIpxAddressEa (
+ IN ULONG NetworkAddress,
+ IN PUCHAR NodeAddress,
+ IN USHORT Socket,
+ OUT PVOID NetworkName
+ )
+
+/*++
+
+Routine Description:
+
+ Builds an EA describing a Netbios address in the buffer supplied by the
+ user.
+
+Arguments:
+
+ NetworkAddress - Supplies the network number
+ NodeAddress - Supplies the node number
+ Socket -
+ NetworkName - The Ea structure that describes the input parameters.
+
+Return Value:
+
+ An informative error code if something goes wrong. STATUS_SUCCESS if the
+ ea is built properly.
+
+--*/
+
+{
+ PFILE_FULL_EA_INFORMATION EaBuffer;
+ PTA_IPX_ADDRESS TAAddress;
+ ULONG Length;
+
+ DebugTrace(+0, Dbg, "BuildIpxAddressEa\n", 0);
+
+ Length = FIELD_OFFSET( FILE_FULL_EA_INFORMATION, EaName[0] ) +
+ TDI_TRANSPORT_ADDRESS_LENGTH + 1 +
+ sizeof (TA_IPX_ADDRESS);
+ EaBuffer = (PFILE_FULL_EA_INFORMATION)NetworkName;
+
+ EaBuffer->NextEntryOffset = 0;
+ EaBuffer->Flags = 0;
+ EaBuffer->EaNameLength = TDI_TRANSPORT_ADDRESS_LENGTH;
+ EaBuffer->EaValueLength = sizeof (TA_IPX_ADDRESS);
+
+ RtlCopyMemory (
+ EaBuffer->EaName,
+ TdiTransportAddress,
+ EaBuffer->EaNameLength + 1);
+
+ TAAddress = (PTA_IPX_ADDRESS)&EaBuffer->EaName[EaBuffer->EaNameLength+1];
+
+ BuildIpxAddress(
+ NetworkAddress,
+ NodeAddress,
+ Socket,
+ TAAddress);
+
+
+ return;
+
+}
+
+
+VOID
+IpxClose(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Open handle to the Ipx transport.
+
+Arguments:
+
+ none
+
+Return Value:
+
+ none
+
+--*/
+{
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "IpxClose...\n", 0);
+ if ( pIpxFileObject ) {
+ ObDereferenceObject( pIpxFileObject );
+ pIpxFileObject = NULL;
+ }
+
+ if (IpxHandle) {
+ //
+ // Attach to the redirector's FSP to allow the handle for the
+ // connection to hang around.
+ //
+
+ if (PsGetCurrentProcess() != FspProcess) {
+ ATTACHPROCESS(FspProcess);
+ ZwClose( IpxHandle );
+ KeDetachProcess();
+ } else {
+ ZwClose( IpxHandle );
+ }
+
+ IpxHandle = NULL;
+ }
+ DebugTrace(-1, Dbg, "IpxClose\n", 0);
+
+}
+
+
+NTSTATUS
+SetEventHandler (
+ IN PIRP_CONTEXT pIrpC,
+ IN PNW_TDI_STRUCT pTdiStruc,
+ IN ULONG EventType,
+ IN PVOID pEventHandler,
+ IN PVOID pContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine registers an event handler with a TDI transport provider.
+
+Arguments:
+
+ pIrpC - supplies an Irp among other things.
+
+ pTdiStruc - supplies the handle and both device and file object pointers
+ to the transport.
+
+ IN ULONG EventType, - Supplies the type of event.
+
+ IN PVOID pEventHandler - Supplies the event handler.
+
+ IN PVOID pContext - Supplies the context to be supplied to the event
+ handler.
+
+Return Value:
+
+ NTSTATUS - Final status of the set event operation
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ TdiBuildSetEventHandler(pIrpC->pOriginalIrp,
+ pTdiStruc->pDeviceObject,
+ pTdiStruc->pFileObject,
+ NULL,
+ NULL,
+ EventType,
+ pEventHandler,
+ pContext);
+
+ Status = SubmitTdiRequest(pTdiStruc->pDeviceObject,
+ pIrpC->pOriginalIrp);
+
+ return Status;
+}
+
+
+NTSTATUS
+SubmitTdiRequest (
+ IN PDEVICE_OBJECT pDeviceObject,
+ IN PIRP pIrp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine submits a request to TDI and waits for it to complete.
+
+Arguments:
+
+ IN PDevice_OBJECT DeviceObject - Connection or Address handle for TDI request
+ IN PIRP Irp - TDI request to submit.
+
+Return Value:
+
+ NTSTATUS - Final status of request.
+
+--*/
+
+{
+ NTSTATUS Status;
+ KEVENT Event;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "SubmitTdiRequest\n", 0);
+
+ KeInitializeEvent (&Event, NotificationEvent, FALSE);
+
+ IoSetCompletionRoutine(pIrp, CompletionEvent, &Event, TRUE, TRUE, TRUE);
+
+ //
+ // Submit the request
+ //
+
+ Status = IoCallDriver(pDeviceObject, pIrp);
+
+ //
+ // If it failed immediately, return now, otherwise wait.
+ //
+
+ if (!NT_SUCCESS(Status)) {
+ DebugTrace(-1, Dbg, "SubmitTdiRequest %X\n", Status);
+ return Status;
+ }
+
+ if (Status == STATUS_PENDING) {
+
+ DebugTrace(+0, Dbg, "Waiting....\n", 0);
+
+ Status = KeWaitForSingleObject(&Event, // Object to wait on.
+ Executive, // Reason for waiting
+ KernelMode, // Processor mode
+ FALSE, // Alertable
+ NULL); // Timeout
+
+ if (!NT_SUCCESS(Status)) {
+ DebugTrace(-1, Dbg, "SubmitTdiRequest could not wait %X\n", Status);
+ return Status;
+ }
+
+ Status = pIrp->IoStatus.Status;
+ }
+
+ DebugTrace(-1, Dbg, "SubmitTdiRequest %X\n", Status);
+
+ return(Status);
+}
+
+
+NTSTATUS
+CompletionEvent(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ This routine does not complete the Irp. It is used to signal to a
+ synchronous part of the driver that it can proceed.
+
+Arguments:
+
+ DeviceObject - unused.
+
+ Irp - Supplies Irp that the transport has finished processing.
+
+ Context - Supplies the event associated with the Irp.
+
+Return Value:
+
+ The STATUS_MORE_PROCESSING_REQUIRED so that the IO system stops
+ processing Irp stack locations at this point.
+
+--*/
+{
+ DebugTrace( 0, Dbg, "CompletionEvent\n", 0 );
+
+ KeSetEvent((PKEVENT )Context, 0, FALSE);
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+ UNREFERENCED_PARAMETER( DeviceObject );
+ UNREFERENCED_PARAMETER( Irp );
+}
+
+
+USHORT
+GetSocketNumber(
+ IN PIRP_CONTEXT pIrpC,
+ IN PNW_TDI_STRUCT pTdiStruc
+ )
+/*++
+
+Routine Description:
+
+ Use a TDI_ACTION to set the Option.
+
+Arguments:
+
+ pIrpC - supplies an Irp among other things.
+
+ pTdiStruc - supplies the handle and both device and file object pointers
+ to the transport.
+
+ Option - supplies the option to set.
+
+Return Value:
+
+ 0 failed otherwise the socket number.
+
+--*/
+{
+ ADDRESS_INFORMATION AddressInfo;
+ NTSTATUS Status;
+ USHORT SocketNumber;
+
+ PAGED_CODE();
+
+ Status = QueryAddressInformation( pIrpC, pTdiStruc, &AddressInfo );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ SocketNumber = 0;
+ } else {
+ SocketNumber = AddressInfo.NetworkName.Address[0].Address[0].Socket;
+
+ RtlCopyMemory( &OurAddress,
+ &AddressInfo.NetworkName.Address[0].Address[0],
+ sizeof(TDI_ADDRESS_IPX));
+
+ }
+
+ return( SocketNumber );
+}
+
+
+NTSTATUS
+GetMaximumPacketSize(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PNW_TDI_STRUCT pTdiStruct,
+ OUT PULONG pMaximumPacketSize
+ )
+/*++
+
+Routine Description:
+
+ Query the maximum packet size for this network.
+
+Arguments:
+
+ pIrpContext - supplies an Irp among other things.
+
+ pTdiStruct - supplies the handle and both device and file object pointers
+ to the transport.
+
+ pMaximumPacketSize - Returns the maximum packet size for the network.
+
+Return Value:
+
+ The status of the query.
+
+--*/
+{
+ TDI_PROVIDER_INFO ProviderInfo;
+
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ Status = QueryProviderInformation( pIrpContext, pTdiStruct, &ProviderInfo );
+
+ if ( NT_SUCCESS( Status ) ) {
+ *pMaximumPacketSize = ProviderInfo.MaximumLookaheadData;
+ }
+
+ return( Status );
+}
+
+NTSTATUS
+QueryAddressInformation(
+ PIRP_CONTEXT pIrpContext,
+ IN PNW_TDI_STRUCT pTdiStruct,
+ PADDRESS_INFORMATION AddressInformation
+ )
+{
+ NTSTATUS Status;
+
+ PMDL MdlSave = pIrpContext->pOriginalIrp->MdlAddress;
+ PMDL Mdl;
+
+ PAGED_CODE();
+
+ Mdl = ALLOCATE_MDL(
+ AddressInformation,
+ sizeof( *AddressInformation ),
+ FALSE, // Secondary Buffer
+ FALSE, // Charge Quota
+ NULL);
+
+ if ( Mdl == NULL ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ try {
+ MmProbeAndLockPages( Mdl, KernelMode, IoReadAccess );
+ } except( EXCEPTION_EXECUTE_HANDLER ) {
+ FREE_MDL( Mdl );
+ return GetExceptionCode();
+ }
+
+ TdiBuildQueryInformation(
+ pIrpContext->pOriginalIrp,
+ pTdiStruct->pDeviceObject,
+ pTdiStruct->pFileObject,
+ CompletionEvent,
+ NULL,
+ TDI_QUERY_ADDRESS_INFO,
+ Mdl);
+
+ Status = SubmitTdiRequest( pTdiStruct->pDeviceObject, pIrpContext->pOriginalIrp);
+
+ pIrpContext->pOriginalIrp->MdlAddress = MdlSave;
+ MmUnlockPages( Mdl );
+ FREE_MDL( Mdl );
+
+ return( Status );
+}
+
+
+NTSTATUS
+QueryProviderInformation(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PNW_TDI_STRUCT pTdiStruct,
+ PTDI_PROVIDER_INFO ProviderInfo
+ )
+{
+ NTSTATUS Status;
+
+ PMDL MdlSave = pIrpContext->pOriginalIrp->MdlAddress;
+ PMDL Mdl;
+
+ PAGED_CODE();
+
+ Mdl = ALLOCATE_MDL(
+ ProviderInfo,
+ sizeof( *ProviderInfo ),
+ FALSE, // Secondary Buffer
+ FALSE, // Charge Quota
+ NULL);
+
+ if ( Mdl == NULL ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ try {
+ MmProbeAndLockPages( Mdl, KernelMode, IoReadAccess );
+ } except( EXCEPTION_EXECUTE_HANDLER ) {
+ FREE_MDL( Mdl );
+ return GetExceptionCode();
+ }
+
+ TdiBuildQueryInformation(
+ pIrpContext->pOriginalIrp,
+ pTdiStruct->pDeviceObject,
+ pTdiStruct->pFileObject,
+ CompletionEvent,
+ NULL,
+ TDI_QUERY_PROVIDER_INFO,
+ Mdl);
+
+ Status = SubmitTdiRequest(pTdiStruct->pDeviceObject, pIrpContext->pOriginalIrp);
+
+ pIrpContext->pOriginalIrp->MdlAddress = MdlSave;
+ MmUnlockPages( Mdl );
+ FREE_MDL( Mdl );
+
+ return( Status );
+}
+
+
+
+NTSTATUS
+SetTransportOption(
+ IN PIRP_CONTEXT pIrpC,
+ IN PNW_TDI_STRUCT pTdiStruc,
+ IN ULONG Option
+ )
+/*++
+
+Routine Description:
+
+ Use a TDI_ACTION to set the Option.
+
+Arguments:
+
+ pIrpC - supplies an Irp among other things.
+
+ pTdiStruc - supplies the handle and both device and file object pointers
+ to the transport.
+
+ Option - supplies the option to set.
+
+Return Value:
+
+ 0 success
+
+--*/
+{
+ static struct {
+ TDI_ACTION_HEADER Header;
+ BOOLEAN DatagramOption;
+ ULONG BufferLength;
+ ULONG Option;
+ } SetPacketType = {
+ IPX_ID,
+ 0, // ActionCode
+ 0, // Reserved
+ TRUE, // DatagramOption
+ sizeof(ULONG) // BufferLength
+ };
+
+ KEVENT Event;
+ NTSTATUS Status;
+
+ PIRP pIrp = pIrpC->pOriginalIrp;
+
+ //
+ // Save the original MDL and System buffer address, to restore
+ // after the IRP completes.
+ //
+ // We use both the MDL and SystemBuffer because NWLINK assumes that
+ // we are using SystemBuffer even though we are supposed to use the
+ // MDL to pass a pointer to the action buffer.
+ //
+
+ PMDL MdlSave = pIrp->MdlAddress;
+ PCHAR SystemBufferSave = pIrp->AssociatedIrp.SystemBuffer;
+
+ PMDL Mdl;
+
+ PAGED_CODE();
+
+ Mdl = ALLOCATE_MDL(
+ &SetPacketType,
+ sizeof( SetPacketType ),
+ FALSE, // Secondary Buffer
+ FALSE, // Charge Quota
+ NULL );
+
+ if ( Mdl == NULL ) {
+ IPX_Close_Socket( pTdiStruc );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ SetPacketType.Option = Option;
+
+ try {
+ MmProbeAndLockPages( Mdl, KernelMode, IoReadAccess );
+ } except( EXCEPTION_EXECUTE_HANDLER ) {
+ FREE_MDL( Mdl );
+ return GetExceptionCode();
+ }
+
+ KeInitializeEvent (
+ &Event,
+ SynchronizationEvent,
+ FALSE);
+
+ TdiBuildAction(
+ pIrp,
+ pTdiStruc->pDeviceObject,
+ pTdiStruc->pFileObject,
+ CompletionEvent,
+ &Event,
+ Mdl );
+
+ //
+ // Set up the system buffer for NWLINK.
+ //
+
+ pIrp->AssociatedIrp.SystemBuffer = &SetPacketType;
+
+ Status = IoCallDriver (pTdiStruc->pDeviceObject, pIrp);
+
+ if ( Status == STATUS_PENDING ) {
+ Status = KeWaitForSingleObject (
+ &Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL );
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = pIrp->IoStatus.Status;
+ }
+ }
+
+ //
+ // Now restore the system buffer and MDL address in the IRP
+ //
+
+ pIrp->AssociatedIrp.SystemBuffer = SystemBufferSave;
+ pIrp->MdlAddress = MdlSave;
+
+ MmUnlockPages( Mdl );
+ FREE_MDL( Mdl );
+
+ return Status;
+}
+
+
+NTSTATUS
+GetNewRoute(
+ IN PIRP_CONTEXT pIrpContext
+ )
+/*++
+
+Routine Description:
+
+ Use a TDI_ACTION to get a new route.
+
+Arguments:
+
+ pIrpContext - Supplies IRP context information.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+{
+ struct {
+ TDI_ACTION_HEADER Header;
+ BOOLEAN DatagramOption;
+ ULONG BufferLength;
+ ULONG Option;
+ ULONG info_netnum;
+ USHORT info_hopcount;
+ USHORT info_netdelay;
+ int info_cardnum;
+ UCHAR info_router[6];
+ } ReRipRequest = {
+ IPX_ID,
+ 0, // ActionCode
+ 0, // Reserved
+ TRUE, // DatagramOption
+ 24 // Buffer length (not including header)
+ };
+
+ KEVENT Event;
+ NTSTATUS Status;
+
+ PIRP pIrp = pIrpContext->pOriginalIrp;
+
+ //
+ // Save the original MDL and System buffer address, to restore
+ // after the IRP completes.
+ //
+ // We use both the MDL and SystemBuffer because NWLINK assumes that
+ // we are using SystemBuffer even though we are supposed to use the
+ // MDL to pass a pointer to the action buffer.
+ //
+
+ PMDL MdlSave = pIrp->MdlAddress;
+ PCHAR SystemBufferSave = pIrp->AssociatedIrp.SystemBuffer;
+
+ PMDL Mdl;
+
+ PAGED_CODE();
+
+ Mdl = ALLOCATE_MDL(
+ &ReRipRequest,
+ sizeof( ReRipRequest ),
+ FALSE, // Secondary Buffer
+ FALSE, // Charge Quota
+ NULL );
+
+ if ( Mdl == NULL ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ ReRipRequest.Option = MIPX_RERIPNETNUM;
+ ReRipRequest.info_netnum = pIrpContext->pNpScb->ServerAddress.Net;
+
+ try {
+ MmProbeAndLockPages( Mdl, KernelMode, IoReadAccess );
+ } except( EXCEPTION_EXECUTE_HANDLER ) {
+ FREE_MDL( Mdl );
+ return GetExceptionCode();
+ }
+
+ KeInitializeEvent (
+ &Event,
+ SynchronizationEvent,
+ FALSE);
+
+ TdiBuildAction(
+ pIrp,
+ pIrpContext->pNpScb->Server.pDeviceObject,
+ pIrpContext->pNpScb->Server.pFileObject,
+ CompletionEvent,
+ &Event,
+ Mdl );
+
+ //
+ // Set up the system buffer for NWLINK.
+ //
+
+ pIrp->AssociatedIrp.SystemBuffer = &ReRipRequest;
+
+ Status = IoCallDriver ( pIrpContext->pNpScb->Server.pDeviceObject, pIrp);
+
+ if ( Status == STATUS_PENDING ) {
+ Status = KeWaitForSingleObject (
+ &Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL );
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = pIrp->IoStatus.Status;
+ }
+ }
+
+ //
+ // Now restore the system buffer and MDL address in the IRP
+ //
+
+ pIrp->AssociatedIrp.SystemBuffer = SystemBufferSave;
+ pIrp->MdlAddress = MdlSave;
+
+ MmUnlockPages( Mdl );
+ FREE_MDL( Mdl );
+
+ return Status;
+}
+
+
+NTSTATUS
+GetTickCount(
+ IN PIRP_CONTEXT pIrpContext,
+ OUT PUSHORT TickCount
+ )
+/*++
+
+Routine Description:
+
+ Use a TDI_ACTION to get a new route.
+
+Arguments:
+
+ pIrpContext - Supplies IRP context information.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+{
+ struct {
+ TDI_ACTION_HEADER Header;
+ BOOLEAN DatagramOption;
+ ULONG BufferLength;
+ ULONG Option;
+ IPX_NETNUM_DATA NetNumData;
+ } GetTickCountInput = {
+ IPX_ID,
+ 0, // ActionCode
+ 0, // Reserved
+ TRUE, // DatagramOption
+ sizeof( IPX_NETNUM_DATA) + 2 * sizeof( ULONG )
+ };
+
+ struct _GET_TICK_COUNT_OUTPUT {
+ ULONG Option;
+ IPX_NETNUM_DATA NetNumData;
+ };
+
+ struct _GET_TICK_COUNT_OUTPUT *GetTickCountOutput;
+
+ KEVENT Event;
+ NTSTATUS Status;
+
+ PIRP pIrp = pIrpContext->pOriginalIrp;
+
+ //
+ // Save the original MDL and System buffer address, to restore
+ // after the IRP completes.
+ //
+ // We use both the MDL and SystemBuffer because NWLINK assumes that
+ // we are using SystemBuffer even though we are supposed to use the
+ // MDL to pass a pointer to the action buffer.
+ //
+
+ PMDL MdlSave = pIrp->MdlAddress;
+ PCHAR SystemBufferSave = pIrp->AssociatedIrp.SystemBuffer;
+
+ PMDL Mdl;
+
+ PAGED_CODE();
+
+ Mdl = ALLOCATE_MDL(
+ &GetTickCountInput,
+ sizeof( GetTickCountInput ),
+ FALSE, // Secondary Buffer
+ FALSE, // Charge Quota
+ NULL );
+
+ if ( Mdl == NULL ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ GetTickCountInput.Option = MIPX_GETNETINFO;
+ *(PULONG)GetTickCountInput.NetNumData.netnum = pIrpContext->pNpScb->ServerAddress.Net;
+
+ try {
+ MmProbeAndLockPages( Mdl, KernelMode, IoReadAccess );
+ } except( EXCEPTION_EXECUTE_HANDLER ) {
+ FREE_MDL( Mdl );
+ return GetExceptionCode();
+ }
+
+ KeInitializeEvent (
+ &Event,
+ SynchronizationEvent,
+ FALSE);
+
+ TdiBuildAction(
+ pIrp,
+ pIrpContext->pNpScb->Server.pDeviceObject,
+ pIrpContext->pNpScb->Server.pFileObject,
+ CompletionEvent,
+ &Event,
+ Mdl );
+
+ //
+ // Set up the system buffer for NWLINK.
+ //
+
+ pIrp->AssociatedIrp.SystemBuffer = &GetTickCountInput;
+
+ Status = IoCallDriver ( pIrpContext->pNpScb->Server.pDeviceObject, pIrp);
+
+ if ( Status == STATUS_PENDING ) {
+ Status = KeWaitForSingleObject (
+ &Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL );
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = pIrp->IoStatus.Status;
+ }
+ }
+
+ DebugTrace( +0, Dbg, "Get Tick Count, net= %x\n", pIrpContext->pNpScb->ServerAddress.Net );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // HACK-o-rama. Streams and non-streams IPX have different output
+ // buffer formats. For now accept both.
+ //
+
+ if ( IpxTransportName.Length == 32 ) {
+
+ // ISNIPX format
+
+ *TickCount = GetTickCountInput.NetNumData.netdelay;
+ } else {
+
+ // NWLINK format
+
+ GetTickCountOutput = (struct _GET_TICK_COUNT_OUTPUT *)&GetTickCountInput;
+ *TickCount = GetTickCountOutput->NetNumData.netdelay;
+ }
+
+ DebugTrace( +0, Dbg, "Tick Count = %d\n", *TickCount );
+ } else {
+ DebugTrace( +0, Dbg, "GetTickCount failed, status = %X\n", Status );
+ }
+
+ //
+ // Now restore the system buffer and MDL address in the IRP
+ //
+
+ pIrp->AssociatedIrp.SystemBuffer = SystemBufferSave;
+ pIrp->MdlAddress = MdlSave;
+
+ MmUnlockPages( Mdl );
+ FREE_MDL( Mdl );
+
+ return Status;
+}
+
+#ifndef QFE_BUILD
+
+static PIRP LineChangeIrp = NULL;
+
+
+NTSTATUS
+SubmitLineChangeRequest(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Use a TDI_ACTION to get a new route.
+
+Arguments:
+
+ pIrpContext - Supplies IRP context information.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+{
+ struct _LINE_CHANGE {
+ TDI_ACTION_HEADER Header;
+ BOOLEAN DatagramOption;
+ ULONG BufferLength;
+ ULONG Option;
+ } *LineChangeInput;
+
+ PIRP pIrp;
+ PMDL Mdl;
+
+ PAGED_CODE();
+
+ LineChangeInput = ALLOCATE_POOL( NonPagedPool, sizeof( struct _LINE_CHANGE ) );
+
+ //
+ // Complete initialization of the request, and allocate and build an
+ // MDL for the request input buffer.
+ //
+
+ LineChangeInput->Header.TransportId = IPX_ID;
+ LineChangeInput->Header.ActionCode = 0;
+ LineChangeInput->Header.Reserved = 0;
+ LineChangeInput->DatagramOption = 2;
+ LineChangeInput->BufferLength = 2 * sizeof( ULONG );
+ LineChangeInput->Option = MIPX_LINECHANGE;
+
+ Mdl = ALLOCATE_MDL(
+ LineChangeInput,
+ sizeof( *LineChangeInput ),
+ FALSE, // Secondary Buffer
+ FALSE, // Charge Quota
+ NULL );
+
+ if ( Mdl == NULL ) {
+ FREE_POOL( LineChangeInput );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ pIrp = ALLOCATE_IRP( pIpxDeviceObject->StackSize, FALSE );
+
+ if ( pIrp == NULL ) {
+ FREE_POOL( LineChangeInput );
+ FREE_MDL( Mdl );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // Remember this IRP so that we can cancel it.
+ //
+
+ LineChangeIrp = pIrp;
+
+ MmBuildMdlForNonPagedPool( Mdl );
+
+ //
+ // Build and submit a TDI request packet.
+ //
+
+ TdiBuildAction(
+ pIrp,
+ pIpxDeviceObject,
+ pIpxFileObject,
+ CompletionLineChange,
+ NULL,
+ Mdl );
+
+ IoCallDriver ( pIpxDeviceObject, pIrp );
+}
+
+
+
+NTSTATUS
+CompletionLineChange(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ This routine is called when the transport completes a line change IRP.
+ This means that we have switched nets, and that we should mark
+ all of our servers disconnected.
+
+Arguments:
+
+ DeviceObject - unused.
+
+ Irp - Supplies Irp that the transport has finished processing.
+
+ Context - unused.
+
+Return Value:
+
+ The STATUS_MORE_PROCESSING_REQUIRED so that the IO system stops
+ processing Irp stack locations at this point.
+
+--*/
+{
+ PMDL Mdl;
+ PWORK_QUEUE_ITEM WorkQueueItem;
+
+ DebugTrace( 0, Dbg, "CompletionLineChange\n", 0 );
+
+ Mdl = Irp->MdlAddress;
+
+ if ( !NT_SUCCESS( Irp->IoStatus.Status ) ) {
+ FREE_POOL( Mdl->MappedSystemVa );
+ FREE_MDL( Mdl );
+ FREE_IRP( Irp );
+ return( STATUS_MORE_PROCESSING_REQUIRED );
+ }
+
+ //
+ // If the scavenger is running, simply make a note that
+ // we need to do this when it is finished.
+ //
+
+ KeAcquireSpinLockAtDpcLevel( &NwScavengerSpinLock );
+
+ if ( WorkerRunning ) {
+
+ if ( ( DelayedProcessLineChange != FALSE ) &&
+ ( DelayedLineChangeIrp != NULL ) ) {
+
+ //
+ // We've already got a line change. Dump this one.
+ //
+
+ KeReleaseSpinLockFromDpcLevel( &NwScavengerSpinLock );
+
+ DebugTrace( 0, Dbg, "Dumping an additional line change request.\n", 0 );
+
+ FREE_POOL( Mdl->MappedSystemVa );
+ FREE_MDL( Mdl );
+ FREE_IRP( Irp );
+ return( STATUS_MORE_PROCESSING_REQUIRED );
+
+ } else {
+
+ DebugTrace( 0, Dbg, "Delaying a line change request.\n", 0 );
+
+ DelayedProcessLineChange = TRUE;
+ DelayedLineChangeIrp = Irp;
+
+ KeReleaseSpinLockFromDpcLevel( &NwScavengerSpinLock );
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+ }
+
+ } else {
+
+ //
+ // Don't let the scavenger start up while we're running.
+ //
+
+ WorkerRunning = TRUE;
+ KeReleaseSpinLockFromDpcLevel( &NwScavengerSpinLock );
+ }
+
+ WorkQueueItem = ALLOCATE_POOL( NonPagedPool, sizeof( *WorkQueueItem ) );
+ if ( WorkQueueItem == NULL ) {
+ FREE_POOL( Mdl->MappedSystemVa );
+ FREE_MDL( Mdl );
+ FREE_IRP( Irp );
+ return( STATUS_MORE_PROCESSING_REQUIRED );
+ }
+
+ //
+ // Use the user buffer field as a convenient place to remember where
+ // the address of the WorkQueueItem. We can get away with this since
+ // we don't let this IRP complete.
+ //
+
+ Irp->UserBuffer = WorkQueueItem;
+
+ //
+ // Process the line change in the FSP.
+ //
+
+ ExInitializeWorkItem( WorkQueueItem, FspProcessLineChange, Irp );
+ ExQueueWorkItem( WorkQueueItem, DelayedWorkQueue );
+
+ return( STATUS_MORE_PROCESSING_REQUIRED );
+}
+
+VOID
+FspProcessLineChange(
+ IN PVOID Context
+ )
+{
+ PIRP Irp;
+ ULONG ActiveHandles;
+
+ NwReferenceUnlockableCodeSection();
+
+ Irp = (PIRP)Context;
+
+ //
+ // Free the work queue item
+ //
+
+ FREE_POOL( Irp->UserBuffer );
+ Irp->UserBuffer = NULL;
+
+ //
+ // Invalid all remote handles
+ //
+
+ ActiveHandles = NwInvalidateAllHandles(NULL, NULL);
+
+ //
+ // Now that we're done walking all the servers, it's safe
+ // to let the scavenger run again.
+ //
+
+ WorkerRunning = FALSE;
+
+ //
+ // Resubmit the IRP
+ //
+
+ TdiBuildAction(
+ Irp,
+ pIpxDeviceObject,
+ pIpxFileObject,
+ CompletionLineChange,
+ NULL,
+ Irp->MdlAddress );
+
+ IoCallDriver ( pIpxDeviceObject, Irp );
+
+ NwDereferenceUnlockableCodeSection ();
+ return;
+}
+#endif
+
diff --git a/private/nw/rdr/kdext/dirs b/private/nw/rdr/kdext/dirs
new file mode 100644
index 000000000..e6ecf7a47
--- /dev/null
+++ b/private/nw/rdr/kdext/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=ntsd \
+ windbg
+
+OPTIONAL_DIRS=
diff --git a/private/nw/rdr/kdext/ntsd/makefile b/private/nw/rdr/kdext/ntsd/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/nw/rdr/kdext/ntsd/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/rdr/kdext/ntsd/nw.def b/private/nw/rdr/kdext/ntsd/nw.def
new file mode 100644
index 000000000..9d9dae6d8
--- /dev/null
+++ b/private/nw/rdr/kdext/ntsd/nw.def
@@ -0,0 +1,15 @@
+DESCRIPTION 'NT Netware Redirector KD extensions'
+
+EXPORTS
+ nwdump
+ logonlist
+ serverlist
+ trace
+ reftrace
+ traceflags
+ help
+ vcblist
+ fcblist
+ icblist
+ irplist
+ credlist
diff --git a/private/nw/rdr/kdext/ntsd/nw.rc b/private/nw/rdr/kdext/ntsd/nw.rc
new file mode 100644
index 000000000..23f42b167
--- /dev/null
+++ b/private/nw/rdr/kdext/ntsd/nw.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "NW i386 KD Debugger Extensions DLL"
+#define VER_INTERNALNAME_STR "Nw.DLL"
+#define VER_ORIGINALFILENAME_STR "Nw.DLL"
+
+#include "common.ver"
+
diff --git a/private/nw/rdr/kdext/ntsd/sources b/private/nw/rdr/kdext/ntsd/sources
new file mode 100644
index 000000000..da56f0039
--- /dev/null
+++ b/private/nw/rdr/kdext/ntsd/sources
@@ -0,0 +1,49 @@
+!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:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=ntos
+MINORCOMP=rdr
+
+TARGETNAME=nw
+TARGETPATH=obj
+TARGETTYPE=DYNLINK
+TARGETLIBS= \
+ \nt\public\sdk\lib\*\kernel32.lib
+
+
+DLLBASE=0x1010000
+
+INCLUDES=..\..;..\..\..\inc;$(NTOS_ROOT)\inc;$(_NTROOT)\private\inc
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+SOURCES=..\nwrdrkd.c \
+ nw.rc
+
+UMTYPE=console
+OPTIONAL_NTTEST=
+
diff --git a/private/nw/rdr/kdext/nwrdrkd.c b/private/nw/rdr/kdext/nwrdrkd.c
new file mode 100644
index 000000000..0b1abeef2
--- /dev/null
+++ b/private/nw/rdr/kdext/nwrdrkd.c
@@ -0,0 +1,2223 @@
+/*++
+
+NwRdr Kernel Debugger Extensions
+Copyright (c) 1995 Microsoft Corporation
+
+Abstract:
+
+ NW Redirector Kernel Debugger extensions.
+
+ This module contains a set of useful kernel debugger
+ extensions for the NT nw redirector.
+
+Author:
+
+ Cory West <corywest>, 09-Jan-1994
+
+--*/
+
+#include "procs.h"
+#include "nodetype.h"
+
+#include <ntkdexts.h>
+#include <string.h>
+#include <stdlib.h>
+
+//
+// Function prototypes.
+//
+
+VOID
+DumpScbNp(
+ DWORD addr,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ BOOL first
+ );
+
+VOID
+DumpFcbNp(
+ DWORD addr,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ BOOL first
+ );
+
+//
+// Define some macros for simplicity.
+//
+
+#define GET_DWORD( pDest, addr ) \
+ (lpExtensionApis->lpReadVirtualMemRoutine)((LPVOID)(addr), pDest, 4, NULL)
+#define GET_WORD( pDest, addr ) \
+ (lpExtensionApis->lpReadVirtualMemRoutine)((LPVOID)(addr), pDest, 2, NULL)
+#define GET_STRING( pDest, string ) \
+ (lpExtensionApis->lpReadVirtualMemRoutine)(string.Buffer, pDest, \
+ string.Length, NULL); pDest[ string.Length/2 ] = L'\0'
+
+#define printf lpExtensionApis->lpOutputRoutine
+#define getmem lpExtensionApis->lpReadVirtualMemRoutine
+#define getexpr lpExtensionApis->lpGetExpressionRoutine
+
+#ifdef WINDBG
+#define getsymaddr( string ) ((lpExtensionApis->lpGetExpressionRoutine))( "&"##string )
+#else
+#define getsymaddr lpExtensionApis->lpGetExpressionRoutine
+#endif
+
+VOID
+help(
+#ifdef WINDBG
+ HANDLE hProcess,
+ HANDLE hThread,
+#endif
+ DWORD dwCurrentPc,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ LPSTR lpArgumentString
+ )
+/*++
+
+ This function prints out usage for the nw debugger extensions.
+
+--*/
+{
+ printf( "---------------------------------------------------------------------------\n");
+ printf( "NwRdr Debugger Extensions:\n\n");
+
+ printf( "Top Level Functions:\n\n");
+
+ printf( "serverlist(void) - List the servers that the redirector knows.\n");
+ printf( "logonlist(void) - List the users that are logged on.\n");
+ printf( "trace(void) - Display the trace buffer.\n");
+ printf( "nwdump(virtual addr) - Display the object at the given virtual address.\n");
+ printf( " (This function knows how to dump all NwRdr data\n");
+ printf( " structures.)\n");
+ printf( "help(void) - Display this message.\n\n");
+
+ printf( "List Management Functions:\n\n");
+
+ printf( "vcblist(scb*, npscb*) - Given a pointer to any of the specified objects,\n");
+ printf( " this function dumps the VCB list for that server.\n");
+ printf( "irplist(scb*, npscb*) - Given a pointer to any of the specified objects,\n");
+ printf( " this function dumps the IRP list for that server.\n");
+ printf( "fcblist(vcb*) - Given a pointer to a VCB, this function dumps\n");
+ printf( " the FCB/DCB list for that VCB.\n");
+ printf( "icblist(scb*, npscb*,\n");
+ printf( " fcb*, dcb*,\n");
+ printf( " npfcb*) - Given a pointer to any of the specified objects,\n");
+ printf( " function dumps the ICB list for that object.\n");
+ printf( "---------------------------------------------------------------------------\n");
+}
+
+VOID
+traceflags(
+#ifdef WINDBG
+ HANDLE hProcess,
+ HANDLE hThread,
+#endif
+ DWORD dwCurrentPc,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ LPSTR lpArgumentString
+ )
+/*++
+
+ This function prints out the trace flag values.
+
+--*/
+{
+ printf( "DEBUG_TRACE_CLEANUP (0x00000001)\n");
+ printf( "DEBUG_TRACE_CLOSE (0x00000002)\n");
+ printf( "DEBUG_TRACE_CLEANUP (0x00000001)\n");
+ printf( "DEBUG_TRACE_CLOSE (0x00000002)\n");
+ printf( "DEBUG_TRACE_CREATE (0x00000004)\n");
+ printf( "DEBUG_TRACE_FSCTRL (0x00000008)\n");
+ printf( "DEBUG_TRACE_IPX (0x00000010)\n");
+ printf( "DEBUG_TRACE_LOAD (0x00000020)\n");
+ printf( "DEBUG_TRACE_EXCHANGE (0x00000040)\n");
+ printf( "DEBUG_TRACE_FILOBSUP (0x00000080)\n");
+ printf( "DEBUG_TRACE_STRUCSUP (0x00000100)\n");
+ printf( "DEBUG_TRACE_FSP_DISPATCHER (0x00000200)\n");
+ printf( "DEBUG_TRACE_FSP_DUMP (0x00000400)\n");
+ printf( "DEBUG_TRACE_WORKQUE (0x00000800)\n");
+ printf( "DEBUG_TRACE_UNWIND (0x00001000)\n");
+ printf( "DEBUG_TRACE_CATCH_EXCEPTIONS (0x00002000)\n");
+ printf( "DEBUG_TRACE_FILEINFO (0x00008000)\n");
+ printf( "DEBUG_TRACE_DIRCTRL (0x00010000)\n");
+ printf( "DEBUG_TRACE_CONVERT (0x00020000)\n");
+ printf( "DEBUG_TRACE_WRITE (0x00040000)\n");
+ printf( "DEBUG_TRACE_READ (0x00080000)\n");
+ printf( "DEBUG_TRACE_VOLINFO (0x00100000)\n");
+ printf( "DEBUG_TRACE_LOCKCTRL (0x00200000)\n");
+ printf( "DEBUG_TRACE_USERNCP (0x00400000)\n");
+ printf( "DEBUG_TRACE_SECURITY (0x00800000)\n");
+ printf( "DEBUG_TRACE_CACHE (0x01000000)\n");
+ printf( "DEBUG_TRACE_LIP (0x02000000)\n");
+ printf( "DEBUG_TRACE_MDL (0x04000000)\n");
+ printf( "DEBUG_TRACE_NDS (0x10000000)\n");
+ printf( "DEBUG_TRACE_SCAVENGER (0x40000000)\n");
+ printf( "DEBUG_TRACE_TIMER (0x80000000)\n");
+}
+
+//
+// Internal helper routines to convert numerical data into symbolic data.
+//
+
+NODE_TYPE_CODE
+GetNodeType(
+ DWORD objAddr,
+ PNTKD_EXTENSION_APIS lpExtensionApis
+ )
+/*++
+
+ Given the address of an object, this function will
+ attempt to get the node type code for that object.
+
+--*/
+{
+
+ NODE_TYPE_CODE ntc;
+ GET_WORD( &ntc, objAddr );
+ return ntc;
+
+}
+
+LPSTR
+RcbStateToString(
+ DWORD State
+ )
+/*++
+
+Routine Description:
+
+ This helper function converts the RCB state from a
+ DWORD to a readable text string.
+
+Arguments:
+
+ DWORD State - The DWORD RCB state.
+
+Return Value:
+
+ LPSTR containing the readable text string.
+
+--*/
+{
+ switch ( State ) {
+
+ case RCB_STATE_STOPPED:
+ return("RCB_STATE_STOPPED");
+
+
+ case RCB_STATE_STARTING:
+ return("RCB_STATE_STARTING");
+
+ case RCB_STATE_NEED_BIND:
+ return("RCB_STATE_NEED_BIND");
+
+ case RCB_STATE_RUNNING:
+ return("RCB_STATE_RUNNING");
+
+ case RCB_STATE_SHUTDOWN:
+ return("RCB_STATE_SHUTDOWN");
+
+ default:
+ return("(state unknown)" );
+ }
+}
+
+LPSTR
+ScbStateToString(
+ DWORD State
+ )
+/*++
+
+Routine Description:
+
+ This helper function converts the SCB state from a
+ DWORD to a readable text string.
+
+Arguments:
+
+ DWORD State - The DWORD SCB state.
+
+Return Value:
+
+ LPSTR containing the readable text string.
+
+--*/
+{
+ switch ( State ) {
+
+ case SCB_STATE_ATTACHING:
+ return("SCB_STATE_ATTACHING" );
+
+ case SCB_STATE_IN_USE:
+ return("SCB_STATE_IN_USE" );
+
+ case SCB_STATE_DISCONNECTING:
+ return("SCB_STATE_DISCONNECTING" );
+
+ case SCB_STATE_FLAG_SHUTDOWN:
+ return("SCB_STATE_FLAG_SHUTDOWN" );
+
+ case SCB_STATE_RECONNECT_REQUIRED:
+ return("SCB_STATE_RECONNECT_REQD" );
+
+ case SCB_STATE_LOGIN_REQUIRED:
+ return("SCB_STATE_LOGIN_REQUIRED" );
+
+ case SCB_STATE_TREE_SCB:
+ return("SCB_STATE_TREE_SCB" );
+
+ default:
+ return("(state unknown)" );
+ }
+}
+
+LPSTR
+IcbStateToString(
+ DWORD State
+ )
+/*++
+
+Routine Description:
+
+ This helper function converts the ICB state from a
+ DWORD to a readable text string.
+
+--*/
+{
+ switch ( State ) {
+
+ case ICB_STATE_OPEN_PENDING:
+ return("ICB_STATE_OPEN_PENDING" );
+
+ case ICB_STATE_OPENED:
+ return("ICB_STATE_OPENED" );
+
+ case ICB_STATE_CLEANED_UP:
+ return("ICB_STATE_CLEANED_UP" );
+
+ case ICB_STATE_CLOSE_PENDING:
+ return("ICB_STATE_CLOSE_PENDING" );
+
+ default:
+ return("(state unknown)" );
+ }
+}
+
+VOID
+PrintIrpContextFlags(
+ ULONG Flags,
+ PNTKD_EXTENSION_APIS lpExtensionApis
+ )
+/*++
+
+ Print out the flags that are set in the IRP_CONTEXT flags.
+
+--*/
+{
+
+ if ( Flags & IRP_FLAG_IN_FSD )
+ printf( "\tIRP_FLAG_IN_FSD\n" );
+
+ if ( Flags & IRP_FLAG_ON_SCB_QUEUE )
+ printf( "\tIRP_FLAG_ON_SCB_QUEUE\n" );
+
+ if ( Flags & IRP_FLAG_SEQUENCE_NO_REQUIRED )
+ printf( "\tIRP_FLAG_SEQUENCE_NO_REQUIRED\n" );
+
+ if ( Flags & IRP_FLAG_SIGNAL_EVENT )
+ printf( "\tIRP_FLAG_SIGNAL_EVENT\n" );
+
+ if ( Flags & IRP_FLAG_RETRY_SEND )
+ printf( "\tIRP_FLAG_RETRY_SEND\n" );
+
+ if ( Flags & IRP_FLAG_RECONNECTABLE )
+ printf( "\tIRP_FLAG_RECONNECTABLE\n" );
+
+ if ( Flags & IRP_FLAG_RECONNECT_ATTEMPT )
+ printf( "\tIRP_FLAG_RECONNECT_ATTEMPT\n" );
+
+ if ( Flags & IRP_FLAG_BURST_REQUEST )
+ printf( "\tIRP_FLAG_BURST_REQUEST\n" );
+
+ if ( Flags & IRP_FLAG_BURST_PACKET )\
+ printf( "\tIRP_FLAG_BURST_PACKET\n" );
+
+ if ( Flags & IRP_FLAG_NOT_OK_TO_RECEIVE )
+ printf( "\tIRP_FLAG_NOT_OK_TO_RECEIVE\n" );
+
+ if ( Flags & IRP_FLAG_REROUTE_ATTEMPTED )
+ printf( "\tIRP_FLAG_REROUTE_ATTEMPTED\n" );
+
+ if ( Flags & IRP_FLAG_BURST_WRITE )
+ printf( "\tIRP_FLAG_BURST_WRITE\n" );
+
+ if ( Flags & IRP_FLAG_SEND_ALWAYS )
+ printf( "\tIRP_FLAG_SEND_ALWAYS\n" );
+
+ if ( Flags & IRP_FLAG_FREE_RECEIVE_MDL )
+ printf( "\tIRP_FLAG_FREE_RECEIVE_MDL\n" );
+
+ if ( Flags & IRP_FLAG_NOT_SYSTEM_PACKET )
+ printf( "\tIRP_FLAG_NOT_SYSTEM_PACKET\n" );
+
+ if ( Flags & IRP_FLAG_NOCONNECT )
+ printf( "\tIRP_FLAG_NOCONNECT\n" );
+
+}
+
+VOID
+PrintNpFcbFlags(
+ ULONG Flags,
+ PNTKD_EXTENSION_APIS lpExtensionApis
+ )
+/*++
+
+ Print out the flags that are set in the IRP_CONTEXT flags.
+
+--*/
+{
+
+ if ( Flags & FCB_FLAGS_DELETE_ON_CLOSE )
+ printf( "\tFCB_FLAGS_DELETE_ON_CLOSE\n" );
+
+ if ( Flags & FCB_FLAGS_TRUNCATE_ON_CLOSE )
+ printf( "\tFCB_FLAGS_TRUNCATE_ON_CLOSE\n" );
+
+ if ( Flags & FCB_FLAGS_PAGING_FILE )
+ printf( "\tFCB_FLAGS_PAGING_FILE\n" );
+
+ if ( Flags & FCB_FLAGS_PREFIX_INSERTED )
+ printf( "\tFCB_FLAGS_PREFIX_INSERTED\n" );
+
+ if ( Flags & FCB_FLAGS_FORCE_MISS_IN_PROGRESS )
+ printf( "\tFCB_FLAGS_FORCE_MISS_IN_PROGRESS\n" );
+
+ if ( Flags & FCB_FLAGS_ATTRIBUTES_ARE_VALID )
+ printf( "\tFCB_FLAGS_ATTRIBUTES_ARE_VALID\n" );
+
+ if ( Flags & FCB_FLAGS_LONG_NAME )
+ printf( "\tFCB_FLAGS_LONG_NAME\n" );
+}
+
+LPSTR
+PacketToString(
+ UINT pt
+ )
+/*++
+
+Routine Description:
+
+ This helper function converts a PACKET_TYPE to
+ a readable text string.
+
+--*/
+{
+
+ switch ( pt ) {
+
+ case SAP_BROADCAST:
+ return "SAP_BROADCAST";
+ case NCP_CONNECT:
+ return "NCP_CONNECT";
+ case NCP_FUNCTION:
+ return "NCP_FUNCTION";
+ case NCP_SUBFUNCTION:
+ return "NCP_SUBFUNCTION";
+ case NCP_DISCONNECT:
+ return "NCP_DISCONNECT";
+ case NCP_BURST:
+ return "NCP_BURST";
+ case NCP_ECHO:
+ return "NCP_ECHO";
+ default:
+ return "(packet type unknown)";
+ }
+
+}
+
+//
+// The internal object functions for the nwdump() routine.
+// These functions must receive good pointers; they are
+// neither smart, nor exported.
+//
+
+VOID
+DumpScb(
+ DWORD addr,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ BOOL first
+ )
+/*++
+
+ This function takes the address of the pageable portion
+ of an SCB and a pointer to a debugger extension interface
+ block. It prints out the information in the SCB and
+ the corresponding non-pageable SCB.
+
+--*/
+{
+ WCHAR Buffer[64];
+ BOOL b;
+ SCB Scb;
+
+ // Read it.
+
+ b = getmem((PVOID)addr, &Scb, sizeof( Scb ), NULL);
+ if ( b == 0 ) {
+ printf("<could not read the pageable scb>\n");
+ return;
+ }
+ printf( "-----------------------------SCB at %08lx-------------------------------\n", addr );
+ printf( "NodeTypeCode : NW_NTC_SCB\n" );
+ printf( "NodeByteSize : %d\n", Scb.NodeByteSize );
+ printf( "pNpScb Addr : %08lx\n", Scb.pNpScb );
+ printf( "Version : %d\\%d\n", Scb.MajorVersion, Scb.MinorVersion );
+ printf( "VcbList : %08lx (LIST_ENTRY, VCB)\n", addr + FIELD_OFFSET( SCB, ScbSpecificVcbQueue ));
+ printf( "VcbCount : %d\n", Scb.VcbCount );
+ printf( "IcbList : %08lx (LIST_ENTRY, ICB)\n", addr + FIELD_OFFSET( SCB, IcbList ));
+ printf( "IcbCount : %d\n", Scb.IcbCount );
+ printf( "OpenNdsStreams : %d\n", Scb.OpenNdsStreams );
+ printf( "UserUid : %08lx %08lx\n", Scb.UserUid.HighPart, Scb.UserUid.LowPart );
+ printf( "OpenFileCount : %d\n", Scb.OpenFileCount );
+
+ b = GET_STRING( Buffer, Scb.UidServerName );
+ if ( b ) {
+ printf( "UidServerName : %ws\n", Buffer );
+ } else {
+ printf( "UidServerName : (unreadable)\n");
+ }
+
+ b = GET_STRING( Buffer, Scb.NdsTreeName );
+ if ( b ) {
+ printf( "NDS Tree Name : %ws\n", Buffer );
+ } else {
+ printf( "Nds Tree Name : (none)\n");
+ }
+
+ b = GET_STRING( Buffer, Scb.UnicodeUid );
+
+ if ( b ) {
+ printf( "UnicodeUid : %ws\n", Buffer );
+ } else {
+ printf( "UnicodeUid : (unreadable)\n");
+ }
+
+
+ b = GET_STRING( Buffer, Scb.UserName );
+
+ if ( b ) {
+ printf( "User name : %ws\n", Buffer );
+ } else {
+ printf( "User name : (unreadable)\n" );
+ }
+
+ b = GET_STRING( Buffer, Scb.Password );
+
+ if ( b ) {
+ printf( "Password : %ws\n", Buffer );
+ } else {
+ printf( "Password : (unreadable)\n" );
+ }
+
+ printf( "PreferredServer : %s\n", Scb.PreferredServer ? "TRUE" : "FALSE" );
+ printf( "MessageWaiting : %s\n", Scb.MessageWaiting ? "TRUE" : "FALSE" );
+ printf( "AttachCount : %d\n", Scb.AttachCount);
+
+ // What about the drive map?
+
+ // Dump both parts.
+ if ( first )
+ DumpScbNp( (DWORD)Scb.pNpScb, lpExtensionApis, FALSE );
+ else
+ printf( "---------------------------------------------------------------------------\n");
+
+ return;
+}
+
+VOID
+DumpScbNp(
+ DWORD addr,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ BOOL first
+ )
+/*++
+
+ This function takes the address of the nonpageable
+ portion of an SCB and a pointer to a debugger extension
+ interface block. It prints out the information in the
+ nonpageable SCB and the corresponding pageable SCB.
+
+--*/
+{
+ WCHAR Buffer[64];
+ BOOL b;
+ NONPAGED_SCB NpScb;
+
+ // Read it.
+
+ b = getmem( (PVOID)addr, &NpScb, sizeof( NpScb ), NULL );
+ if ( b == 0 ) {
+ printf("<could not read the nonpageable scb>\n");
+ return;
+ }
+
+ printf( "------------------------Non-Pageable SCB at %08lx-----------------------\n", addr);
+ printf( "NodeTypeCode : NW_NTC_SCBNP\n" );
+ printf( "NodeByteSize : %d\n", NpScb.NodeByteSize );
+
+ b = GET_STRING( Buffer, NpScb.ServerName );
+ if ( b ) {
+ printf( "ServerName : %ws\n", Buffer );
+ } else {
+ printf( "ServerName : (unreadable)\n" );
+ }
+
+ printf( "pScb Addr : %08lx\n", NpScb.pScb );
+ printf( "Reference Count : %08lx\n", NpScb.Reference );
+ printf( "State : %s\n", ScbStateToString( NpScb.State ));
+ printf( "Last Used Time : %08lx %08lx\n", NpScb.LastUsedTime.HighPart, NpScb.LastUsedTime.LowPart );
+ printf( "Sending : %s\n", NpScb.Sending ? "TRUE" : "FALSE" );
+ printf( "Receiving : %s\n", NpScb.Receiving ? "TRUE" : "FALSE" );
+ printf( "Ok To Receive : %s\n", NpScb.OkToReceive ? "TRUE" : "FALSE" );
+ printf( "PageAlign : %s\n", NpScb.PageAlign ? "TRUE" : "FALSE" );
+ printf( "Scblinks : %08lx (LIST_ENTRY, NPSCB)\n", addr + FIELD_OFFSET( NONPAGED_SCB, ScbLinks ));
+ printf( "Requests : %08lx (LIST_ENTRY, NPSCB)\n", addr + FIELD_OFFSET( NONPAGED_SCB, Requests ));
+ printf( "------------------------------Transport Info-------------------------------\n" );
+ printf( "TickCount : %d\n", NpScb.TickCount );
+ printf( "RetryCount : %d\n", NpScb.RetryCount );
+ printf( "Timeout : %d\n", NpScb.TimeOut );
+ printf( "SequenceNo : %d\n", NpScb.SequenceNo );
+ printf( "ConnectionNo : %d\n", NpScb.ConnectionNo );
+ printf( "ConnectionNoHi : %d\n", NpScb.ConnectionNoHigh );
+ printf( "ConnectionStat : %d\n", NpScb.ConnectionStatus );
+ printf( "MaxTimeOut : %d\n", NpScb.MaxTimeOut );
+ printf( "BufferSize : %d\n", NpScb.BufferSize );
+ printf( "TaskNo : %d\n", NpScb.TaskNo );
+ printf( "Spin lock : %s\n", NpScb.NpScbSpinLock == 0 ? "Released" : "Acquired " );
+ printf( "LIP Data Speed : %d\n", NpScb.LipDataSpeed );
+ printf( "---------------------------Burst Mode Parameters---------------------------\n");
+ printf( "SourceConnId : %08lx\n", NpScb.SourceConnectionId );
+ printf( "DestConnId : %08lx\n", NpScb.DestinationConnectionId );
+ printf( "MaxPacketSize : %d\n", NpScb.MaxPacketSize );
+ printf( "MaxSendSize : %ld\n", NpScb.MaxSendSize );
+ printf( "MaxReceiveSize : %ld\n", NpScb.MaxReceiveSize );
+ printf( "SendBMEnable : %s\n", NpScb.SendBurstModeEnabled ? "TRUE" : "FALSE" );
+ printf( "ReceiveBMEnable : %s\n", NpScb.ReceiveBurstModeEnabled ? "TRUE" : "FALSE" );
+ printf( "BurstSequenceNo : %d\n", NpScb.BurstSequenceNo );
+ printf( "BurstRequestNo : %d\n", NpScb.BurstRequestNo );
+ printf( "BurstSendDelay : Good %d,\tCurrent %d,\tBad %d\n", NpScb.NwGoodSendDelay, NpScb.NwSendDelay, NpScb.NwBadSendDelay );
+ printf( "BurstReceiveDelay : Good %d,\tCurrent %d,\tBad %d\n", NpScb.NwGoodReceiveDelay, NpScb.NwReceiveDelay, NpScb.NwBadReceiveDelay );
+ printf( "BurstSuccessCount : Send %d, Receive %d\n", NpScb.SendBurstSuccessCount, NpScb.ReceiveBurstSuccessCount );
+ printf( "--------------------------Send Delays and Timeouts-------------------------\n" );
+ printf( "SendTimeout : %d\n", NpScb.SendTimeout );
+ printf( "TotalWaitTime : %d\n", NpScb.TotalWaitTime );
+ printf( "NwLoopTime : %d\n", NpScb.NwLoopTime );
+ printf( "NwSingleBurst : %d\n", NpScb.NwSingleBurstPacketTime );
+ printf( "NwMaxSendDelay : %d\n", NpScb.NwMaxSendDelay );
+ printf( "NwGoodSendDelay : %d\n", NpScb.NwGoodSendDelay );
+ printf( "NwBadSendDelay : %d\n", NpScb.NwBadSendDelay );
+ printf( "BurstDataWritten : %d\n", NpScb.BurstDataWritten );
+ printf( "NwMaxReceiveDelay : %d\n", NpScb.NwMaxReceiveDelay );
+ printf( "NwReceiveDelay : %d\n", NpScb.NwReceiveDelay );
+ printf( "NwGoodReceiveDelay : %d\n", NpScb.NwGoodReceiveDelay );
+ printf( "NwBadReceiveDelay : %d\n", NpScb.NwBadReceiveDelay );
+ printf( "CurrentBurstDelay : %d\n", NpScb.CurrentBurstDelay );
+ printf( "NtSendDelay : %08lx %08lx\n", NpScb.NtSendDelay.HighPart, NpScb.NtSendDelay.LowPart );
+ printf( "NwNextEventTime : %08lx %08lx\n", NpScb.NwNextEventTime.HighPart, NpScb.NwNextEventTime.LowPart );
+
+ // Spin locks? Transport and TDI info?
+
+ // Dump Both Parts.
+ if ( first )
+ DumpScb( (DWORD)NpScb.pScb, lpExtensionApis, FALSE );
+ else
+ printf( "---------------------------------------------------------------------------\n" );
+
+ return;
+}
+
+VOID
+DumpFcb(
+ DWORD addr,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ BOOL first
+ )
+/*++
+
+ This function takes the address of an FCB or DCB and a pointer
+ to a debugger extension interface block. It prints out
+ the information in the FCB or DCB.
+
+--*/
+{
+ WCHAR Buffer[64];
+ BOOL b;
+ FCB Fcb;
+
+ b = getmem( (PVOID)addr, &Fcb, sizeof( Fcb ), NULL );
+ if ( b == 0 ) {
+ printf("<could not read the fcb or dcb>\n");
+ return;
+ }
+
+ if (Fcb.NodeTypeCode == NW_NTC_FCB) {
+ printf( "----------------------------FCB at %08lx--------------------------------\n", addr );
+ printf( "NodeTypeCode : NW_NTC_FCB\n" );
+ } else {
+ printf( "----------------------------DCB at %08lx--------------------------------\n", addr );
+ printf( "NodeTypeCode : NW_NTC_DCB\n" );
+ }
+
+ b = GET_STRING( Buffer, Fcb.FullFileName );
+ if ( b ) {
+ printf( "FullFileName : %ws\n", Buffer );
+ } else {
+ printf( "FullFileName : (unreadable)\n" );
+ }
+
+ b = GET_STRING( Buffer, Fcb.RelativeFileName );
+ if ( b ) {
+ printf( "RelativeFileName : %ws\n", Buffer );
+ } else {
+ printf( "RelativeFileName : (unreadable)\n" );
+ }
+ printf( "VCB Addr : %08lx\n", Fcb.Vcb );
+ printf( "SCB Addr : %08lx\n", Fcb.Scb );
+ printf( "NpFcb Addr : %08lx\n", Fcb.NonPagedFcb );
+ printf( "LastModifiedDate : %d\n", Fcb.LastModifiedDate );
+ printf( "LastModifiedTime : %d\n", Fcb.LastModifiedTime );
+ printf( "CreationDate : %d\n", Fcb.CreationDate );
+ printf( "CreationTime : %d\n", Fcb.CreationTime );
+ printf( "LastAccessDate : %d\n", Fcb.LastAccessDate );
+ printf( "State : %d\n", Fcb.State );
+ printf( "Flags : %d\n", Fcb.Flags );
+
+ // SHARE_ACCESS?
+
+ printf( "FcbListEntry : %08lx (LIST_ENTRY, FCB)\n", addr + FIELD_OFFSET( FCB, FcbListEntry ));
+ printf( "IcbListEntry : %08lx (LIST_ENTRY, ICB)\n", addr + FIELD_OFFSET( FCB, IcbList ));
+ printf( "IcbCount : %d\n", Fcb.IcbCount );
+ printf( "LastReadOffset : %d\n", Fcb.LastReadOffset );
+ printf( "LastReadSize : %d\n", Fcb.LastReadSize );
+
+ // Dump both parts.
+ if ( first )
+ DumpFcbNp( (DWORD)Fcb.NonPagedFcb, lpExtensionApis, FALSE );
+ else
+ printf( "---------------------------------------------------------------------------\n" );
+
+}
+
+VOID
+DumpVcb(
+ DWORD addr,
+ PNTKD_EXTENSION_APIS lpExtensionApis
+ )
+/*++
+
+ This function takes the address of a VCB and a pointer
+ to a debugger extension interface block. It prints out
+ the information in the VCB.
+
+--*/
+{
+ WCHAR Buffer[64];
+ BOOL b;
+ VCB Vcb;
+
+ // Read it.
+
+ b = getmem( (PVOID)addr, &Vcb, sizeof( Vcb ), NULL);
+ if ( b == 0 ) {
+ printf("<could not read the vcb>\n");
+ return;
+ }
+
+ printf( "------------------------------VCB at %08lx------------------------------\n", addr);
+ printf( "NodeTypeCode : NW_NTC_VCB\n" );
+ printf( "NodeByteSize : %d\n", Vcb.NodeByteSize );
+ printf( "Reference Count : %08lx\n", Vcb.Reference );
+ printf( "Last Used Time : %08lx %08lx\n", Vcb.LastUsedTime.HighPart, Vcb.LastUsedTime.LowPart );
+ printf( "GlobalVcbListEntry : %08lx (LIST_ENTRY, VCB)\n", addr + FIELD_OFFSET( VCB, GlobalVcbListEntry) );
+ printf( "SequenceNumber : %d\n", Vcb.SequenceNumber );
+
+ b = GET_STRING( Buffer, Vcb.Name );
+ if ( b ) {
+ printf( "VolumeName : %ws\n", Buffer );
+ } else {
+ printf( "VolumeName : (unreadable)\n" );
+ }
+
+ b = GET_STRING( Buffer, Vcb.ConnectName );
+ if ( b ) {
+ printf( "ConnectName : %ws\n", Buffer );
+ } else {
+ printf( "ConnectName : (unreadable)\n" );
+ }
+
+ b = GET_STRING( Buffer, Vcb.ShareName );
+ if ( b ) {
+ printf( "NW ShareName : %ws\n", Buffer );
+ } else {
+ printf( "NW ShareName : (unreadable)\n" );
+ }
+
+ if ( !Vcb.Flags & VCB_FLAG_PRINT_QUEUE ) {
+ printf( "VolumeNumber : %d\n", Vcb.Specific.Disk.VolumeNumber );
+ printf( "LongNameSpace : %d\n", Vcb.Specific.Disk.LongNameSpace );
+ printf( "Handle : %d\n", Vcb.Specific.Disk.Handle );
+ } else {
+ printf( "QueueId : %d\n", Vcb.Specific.Print.QueueId );
+ }
+
+ if ( Vcb.DriveLetter != 0) {
+ printf( "Drive letter : %wc:\n", Vcb.DriveLetter );
+ } else {
+ printf( "Drive letter : UNC\n" );
+ }
+
+ printf( "Scb Addr : %08lx\n", Vcb.Scb );
+ printf( "VcbListEntry : %08lx (LIST_ENTRY, VCB)\n", addr + FIELD_OFFSET( VCB, VcbListEntry) );
+ printf( "FcbListEntry : %08lx (LIST_ENTRY, FCB)\n", addr + FIELD_OFFSET(VCB, FcbList) );
+ printf( "OpenFileCount : %d\n", Vcb.OpenFileCount );
+ printf( "Flags : %08lx\n", Vcb.Flags );
+ printf( "---------------------------------------------------------------------------\n");
+
+}
+
+VOID
+DumpIcb(
+ DWORD addr,
+ PNTKD_EXTENSION_APIS lpExtensionApis
+ )
+/*++
+
+ This function takes the address of an ICB and a pointer
+ to a debugger extension interface block. It prints out
+ the information in the ICB.
+
+--*/
+{
+ WCHAR Buffer[64];
+ BOOL b, icbscb;
+ ICB Icb;
+ UINT hb;
+
+ b = getmem( (PVOID)addr, &Icb, sizeof( Icb ), NULL);
+ if ( b == 0 ) {
+ printf("<could not read the icb>\n");
+ return;
+ }
+
+ icbscb = (Icb.NodeTypeCode == NW_NTC_ICB_SCB);
+
+ if ( icbscb ) {
+ printf( "---------------------------ICB_SCB at %08lx-----------------------------\n", addr );
+ printf( "NodeTypeCode : NW_NTC_ICB_SCB\n" );
+ } else {
+ printf( "-----------------------------ICB at %08lx-------------------------------\n", addr );
+ printf( "NodeTypeCode : NW_NTC_ICB\n" );
+ }
+
+ printf( "NodeByteSize : %d\n", Icb.NodeByteSize );
+ printf( "ListEntry : %08lx\n", Icb.ListEntry );
+
+ if (icbscb ) {
+ printf( "SuperType Addr : %08lx (SCB)\n", Icb.SuperType.Scb );
+ } else {
+ printf( "SuperType Addr : %08lx (FCB)\n", Icb.SuperType.Fcb );
+ printf( "NpFcb Addr : %08lx\n", Icb.NpFcb );
+ }
+
+ printf( "State : %s\n", IcbStateToString(Icb.State) );
+ printf( "HasRemoteHandle : %s\n", Icb.HasRemoteHandle ? "TRUE" : "FALSE" );
+
+ if ( Icb.HasRemoteHandle ) {
+ printf( "Handle : " );
+ for ( hb = 0; hb < 6; hb++ ) {
+ printf( "%c ", (Icb.Handle)[hb]);
+ }
+ printf( "\n");
+ }
+
+ // What abou the PFILE_OBJECT?
+
+ b = GET_STRING( Buffer, Icb.NwQueryTemplate );
+ if ( b ) {
+ printf( "NwQueryTemplate : %s\n", Buffer );
+ } else {
+ printf( "NWQueryTemplate : (unreadable)\n" );
+ }
+
+ b = GET_STRING( Buffer, Icb.UQueryTemplate );
+ if ( b ) {
+ printf( "UQueryTemplate : %ws\n", Buffer );
+ } else {
+ printf( "UQueryTemplate : (unreadable)\n" );
+ }
+
+ printf( "IndexLastIcbRtr : %d\n", Icb.IndexOfLastIcbReturned );
+ printf( "Pid : %d\n", Icb.Pid );
+ printf( "DotReturned : %s\n", Icb.DotReturned ? "TRUE" : "FALSE" );
+ printf( "DotDotReturned : %s\n", Icb.DotDotReturned ? "TRUE" : "FALSE" );
+ printf( "ReturnedSmthng : %s\n", Icb.ReturnedSomething ? "TRUE" : "FALSE" );
+ printf( "ShortNameSearch : %s\n", Icb.ShortNameSearch ? "TRUE" : "FALSE" );
+ printf( "SearchHandle : %d\n", Icb.SearchHandle );
+ printf( "SearchVolume : %d\n", Icb.SearchVolume );
+ printf( "SearchAttribts : %d\n", Icb.SearchAttributes );
+ printf( "SearchIndexHigh : %d\n", Icb.SearchIndexHigh );
+ printf( "SearchIndexLow : %d\n", Icb.SearchIndexLow );
+ printf( "IsPrintJob : %s\n", Icb.IsPrintJob ? "TRUE" : "FALSE" );
+ printf( "JobId : %d\n", Icb.JobId );
+ printf( "ActuallyPrinted : %s\n", Icb.ActuallyPrinted ? "TRUE" : "FALSE" );
+ printf( "USetLastAccessTime : %s\n", Icb.UserSetLastAccessTime ? "TRUE" : "FALSE" );
+ printf( "File Position : %d\n", Icb.FilePosition );
+ printf( "File Size : %d\n", Icb.FileSize );
+
+ printf( "IsTreeHanle : %s\n", Icb.IsTreeHandle ? "TRUE" : "FALSE" );
+
+ // This needs to be cleaned up!
+
+ printf( "---------------------------------------------------------------------------\n" );
+
+}
+
+VOID
+DumpIrpContext(
+ DWORD addr,
+ PNTKD_EXTENSION_APIS lpExtensionApis
+ )
+{
+ BOOL b;
+ IRP_CONTEXT IrpContext;
+
+ b = getmem( (PVOID)addr, &IrpContext, sizeof( IrpContext ), NULL );
+ if ( b == 0 ) {
+ printf( "<could not read the irpcontext>\n" );
+ return;
+ }
+
+ printf( "--------------------------IRP CONTEXT at %08lx--------------------------\n", addr );
+ printf( "NodeTypeCode : NW_NTC_IRP_CONTEXT\n" );
+
+ // WORK_QUEUE_ITEM?
+
+ printf( "PacketType : %s\n", PacketToString(IrpContext.PacketType));
+ printf( "NpScb Addr : %08lx\n", IrpContext.pNpScb );
+ printf( "Scb Addr : %08lx\n", IrpContext.pScb );
+ printf( "TdiStruct : %08lx\n", IrpContext.pTdiStruct );
+
+ // NextRequest?
+
+ printf( "Event : %08lx\n", addr + FIELD_OFFSET( IRP_CONTEXT, Event ) );
+ printf( "Original IRP : %08lx\n", IrpContext.pOriginalIrp );
+ printf( "Original SB : %08lx\n", IrpContext.pOriginalSystemBuffer );
+ printf( "Original UB : %08lx\n", IrpContext.pOriginalUserBuffer );
+ printf( "Original MDL : %08lx\n", IrpContext.pOriginalMdlAddress );
+ printf( "Receive IRP : %08lx\n", IrpContext.ReceiveIrp );
+ printf( "TxMdl : %08lx\n", IrpContext.TxMdl );
+ printf( "RxMdl : %08lx\n", IrpContext.RxMdl );
+ printf( "RunRoutine : %08lx\n", IrpContext.RunRoutine );
+ printf( "pEx : %08lx\n", IrpContext.pEx );
+ printf( "PostProcessRtn : %08lx\n", IrpContext.PostProcessRoutine );
+ printf( "TimeoutRtn : %08lx\n", IrpContext.TimeoutRoutine );
+ printf( "ComplSendRtn : %08lx\n", IrpContext.CompletionSendRoutine );
+ printf( "pWorkItem : %08lx\n", IrpContext.pWorkItem );
+ printf( "Req Data Addr : %08lx\n", addr + FIELD_OFFSET( IRP_CONTEXT, req ) );
+ printf( "ResponseLength : %08lx\n", IrpContext.ResponseLength );
+ printf( "Rsp Data Addr : %08lx\n", addr + FIELD_OFFSET( IRP_CONTEXT, rsp ) );
+ printf( "Icb Addr : %08lx\n", IrpContext.Icb );
+ printf( "Specific Data Addr : %08lx\n", addr + FIELD_OFFSET( IRP_CONTEXT, Specific.Create.FullPathName ) );
+ printf( "------------------------------IRP Context Flags----------------------------\n");
+ PrintIrpContextFlags(IrpContext.Flags, lpExtensionApis);
+ printf( "---------------------------------------------------------------------------\n" );
+
+ return;
+}
+
+VOID
+DumpFcbNp(
+ DWORD addr,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ BOOL first
+ )
+{
+ WCHAR Buffer[64];
+ BOOL b;
+ NONPAGED_FCB NpFcb;
+
+ b = getmem( (PVOID)addr, &NpFcb, sizeof( NONPAGED_FCB ), NULL);
+ if ( !b ) {
+ printf( "<could not read the non-pageable fcb>\n" );
+ return;
+ }
+
+ printf( "--------------------Common NP FCB Header at %08lx-----------------------\n");
+ printf( "NodeTypeCode : NW_NTC_NONPAGED_FCB\n" );
+ printf( "NodeByteSize : %d\n", NpFcb.Header.NodeByteSize );
+ printf( "IsFastIoPossible : %d\n", NpFcb.Header.IsFastIoPossible );
+
+ // Resource? PagingIoResource?
+
+ printf( "AllocationSize : %08lx %08lx\n", NpFcb.Header.AllocationSize.HighPart, NpFcb.Header.AllocationSize.LowPart );
+ printf( "FileSize : %08lx %08lx\n", NpFcb.Header.FileSize.HighPart, NpFcb.Header.FileSize.LowPart );
+ printf( "ValidDataLength : %08lx %08lx\n", NpFcb.Header.ValidDataLength.HighPart, NpFcb.Header.ValidDataLength.LowPart );
+ printf( "pFcb Addr : %08lx\n", NpFcb.Fcb );
+
+ // SegmentObject?
+
+ printf( "FileLockList : %08lx\n", addr + FIELD_OFFSET( NONPAGED_FCB, FileLockList) );
+ printf( "PendLockList : %08lx\n", addr + FIELD_OFFSET( NONPAGED_FCB, PendingLockList) );
+ printf( "Resource : %08lx\n", addr + FIELD_OFFSET( NONPAGED_FCB, Resource ) );
+
+ printf( "Attributes : %d\n", NpFcb.Attributes );
+ printf( "CacheType : %d\n", NpFcb.CacheType );
+ printf( "CacheBuffer : %08lx\n", NpFcb.CacheBuffer );
+ printf( "CacheMdl : %08lx\n", NpFcb.CacheMdl );
+ printf( "CacheSize : %d\n", NpFcb.CacheSize );
+ printf( "CacheFileOffset : %d\n", NpFcb.CacheFileOffset );
+ printf( "CacheDataSize : %d\n", NpFcb.CacheDataSize );
+ printf( "----------------------------------FCB Flags--------------------------------\n" );
+ PrintNpFcbFlags( NpFcb.Header.Flags, lpExtensionApis );
+
+ // Dump both parts.
+ if ( first )
+ DumpFcb( (DWORD)NpFcb.Fcb, lpExtensionApis, FALSE );
+ else
+ printf( "---------------------------------------------------------------------------\n" );
+
+}
+
+VOID
+DumpRcb(
+ DWORD addr,
+ PNTKD_EXTENSION_APIS lpExtensionApis
+ )
+/*++
+
+ This function takes the address of an ICB and a pointer
+ to a debugger extension interface block. It prints out
+ the information in the ICB.
+
+--*/
+{
+ BOOL b;
+ RCB Rcb;
+
+ b = getmem( (PVOID)addr, &Rcb, sizeof( RCB ), NULL);
+ if ( b == 0 ) {
+ printf("<could not read the rcb>\n");
+ return;
+ }
+
+ printf( "------------------------------------------------------------\n");
+ printf( "NodeTypeCode : NW_NTC_RCB\n");
+ printf( "State : %s\n", RcbStateToString(Rcb.State));
+ printf( "OpenCount : %ul\n", Rcb.OpenCount);
+ printf( "ResourceAddr : %08lx\n", addr + FIELD_OFFSET( RCB, Resource ));
+ printf( "ServerListAddr : %08lx\n", addr + FIELD_OFFSET( RCB,
+ ServerNameTable ));
+ printf( "VolumeListAddr : %08lx\n", addr + FIELD_OFFSET( RCB,
+ VolumeNameTable ));
+ printf( "FileListAddr : %08lx\n", addr + FIELD_OFFSET( RCB,
+ FileNameTable ));
+ printf( "------------------------------------------------------------\n");
+
+}
+
+VOID
+DumpPid(
+ DWORD addr,
+ PNTKD_EXTENSION_APIS lpExtensionApis
+ )
+/*++
+
+ This function takes the address of a PID and a pointer
+ to a debugger extension interface block. It prints out
+ the information in the PID.
+
+--*/
+{
+
+ printf( "------------------------------------------------------------\n");
+ printf( "NodeTypeCode : NW_NTC_PID\n" );
+ printf( "...Not yet implemented...");
+ printf( "------------------------------------------------------------\n");
+
+}
+
+VOID
+DumpFileLock(
+ DWORD addr,
+ PNTKD_EXTENSION_APIS lpExtensionApis
+ )
+/*++
+
+ This function takes the address of a file lock and a pointer
+ to a debugger extension interface block. It prints out
+ the information in the file lock.
+
+--*/
+{
+
+ printf( "------------------------------------------------------------\n" );
+ printf( "NodeTypeCode : NW_NTC_FILE_LOCK\n" );
+ printf( "Not yet implemented...\n" );
+ printf( "------------------------------------------------------------\n" );
+
+}
+
+VOID
+DumpLogon(
+ DWORD addr,
+ PNTKD_EXTENSION_APIS lpExtensionApis
+ )
+/*++
+
+ This function takes the address of a logon and a pointer
+ to a debugger extension interface block. It prints out
+ the information in the logon.
+
+--*/
+{
+ BOOL b;
+ LOGON Logon;
+ WCHAR Buffer[64];
+
+ b = getmem( (PVOID)addr, &Logon, sizeof(LOGON), NULL );
+ if (!b ) {
+ printf( "<unable to read logon>" );
+ return;
+ }
+
+ printf( "------------------------------------------------------------\n");
+ printf( "NodeTypeCode : NW_NTC_LOGON\n" );
+ printf( "NodeByteSize : %d\n", Logon.NodeByteSize );
+ printf( "NextLogon : %08lx (LOGON LIST_ENTRY)\n", addr +
+ FIELD_OFFSET( LOGON, Next ));
+
+ b = GET_STRING( Buffer, Logon.UserName );
+ if ( b ) {
+ printf( "UserName : %ws\n", Buffer );
+ } else {
+ printf( "UserName : <unreadable>\n" );
+ }
+
+ b = GET_STRING( Buffer, Logon.PassWord );
+ if ( b ) {
+ printf( "Password : %ws\n", Buffer );
+ } else {
+ printf( "Password : <unreadable>\n" );
+ }
+
+ b = GET_STRING( Buffer, Logon.ServerName );
+ if ( b ) {
+ printf( "Pref Server : %ws\n", Buffer );
+ } else {
+ printf( "Pref Server : <unreadable>\n" );
+ }
+
+ printf( "UserUid : %08lx %08lx\n", Logon.UserUid.HighPart,
+ Logon.UserUid.LowPart);
+
+ printf( "CredListResource: %08lx\n", addr +
+ FIELD_OFFSET( LOGON, CredentialListResource ));
+
+ printf( "CredentialList : %08lx (CREDENTIAL LIST_ENTRY)\n", addr +
+ FIELD_OFFSET( LOGON, NdsCredentialList ));
+
+ printf( "------------------------------------------------------------\n");
+
+}
+
+VOID
+DumpCredential(
+ DWORD addr,
+ PNTKD_EXTENSION_APIS lpExtensionApis
+ )
+/*++
+
+ This function takes the address of an nds credential and a
+ pointer to a debugger extension interface block. It prints
+ out the information in the logon.
+
+--*/
+{
+ BOOL b;
+ NDS_SECURITY_CONTEXT Context;
+ NDS_CREDENTIAL Credential;
+ NDS_SIGNATURE Signature;
+
+ WCHAR Buffer[512];
+
+ CHAR PackBuffer[2048];
+ BYTE *packed;
+ ULONG packedlen;
+
+ b = getmem( (PVOID)addr, &Context, sizeof(NDS_SECURITY_CONTEXT), NULL );
+ if (!b ) {
+ printf( "<unable to read context>\n" );
+ return;
+ }
+
+ printf( "-------- NDS Security Context at 0x%08lx ----------------\n", addr);
+ printf( "NodeTypeCode : NW_NTC_NDS_CREDENTIAL\n" );
+ printf( "NodeByteSize : %d\n", Context.nts );
+
+ printf( "Next : %08lx (NDS_SECURITY_CONTEXT LIST_ENTRY)\n", addr +
+ FIELD_OFFSET( NDS_SECURITY_CONTEXT, Next ));
+
+
+ b = GET_STRING( Buffer, Context.NdsTreeName );
+ if ( b ) {
+ printf( "Nds Tree Name : %ws\n", Buffer );
+ } else {
+ printf( "Nds Tree Name : <unreadable>\n" );
+ }
+
+ b = GET_STRING( Buffer, Context.CurrentContext );
+ if ( b ) {
+ printf( "Current Context : %ws\n", Buffer );
+ } else {
+ printf( "Current Context :<unreadable>\n" );
+ }
+
+ if ( Context.Credential != NULL ) {
+
+ printf( "--------------------- Credential Data ----------------------\n");
+
+ b = getmem( (PVOID)Context.Credential, &Credential, sizeof(NDS_CREDENTIAL), NULL );
+ if (!b ) {
+ printf( "<unable to read credential>\n" );
+ goto DO_SIGNATURE;
+ }
+
+ printf( "Start validity : 0x%08lx\n", Credential.validityBegin );
+ printf( "End validity : 0x%08lx\n", Credential.validityEnd );
+ printf( "Random : 0x%08lx\n", Credential.random );
+ printf( "Opt data Len : %d\n", Credential.optDataSize );
+ printf( "UserName Len : %d\n", Credential.userNameLength );
+
+ //
+ // Optional data is the first packed data after the struct.
+ //
+
+ packedlen = Credential.optDataSize + Credential.userNameLength;
+ packed = ((BYTE *)Context.Credential) + sizeof( NDS_CREDENTIAL );
+
+ if ( Credential.optDataSize ) {
+ printf( "Opt data addr : %08lx\n", packed );
+ }
+
+ packed += Credential.optDataSize;
+
+ b = getmem( (PVOID)packed, Buffer, Credential.userNameLength, NULL );
+ if ( !b ) {
+ printf( "<unable to read user name>\n" );
+ goto DO_SIGNATURE;
+ }
+ printf( "Username : %ws\n", Buffer );
+
+ } else {
+
+ printf( "-------------------- No Credential Data --------------------\n");
+
+ }
+
+DO_SIGNATURE:
+
+ if ( Context.Signature != NULL ) {
+
+ printf( "---------------------- Signature Data ----------------------\n");
+
+ b = getmem( (PVOID)Context.Signature, &Signature, sizeof(NDS_SIGNATURE), NULL );
+ if (!b ) {
+ printf( "<unable to read signature>\n" );
+ goto DO_END;
+ }
+
+ printf( "Signature Len : %d\n", Signature.signDataLength );
+
+ packedlen = Signature.signDataLength;
+ packed = ((BYTE *)Context.Signature) + sizeof( NDS_SIGNATURE );
+
+ printf( "Signature addr : %08lx\n", packed );
+
+ } else {
+
+ printf( "-------------------- No Signature Data ---------------------\n");
+
+ }
+
+DO_END:
+
+ if ( Context.PublicNdsKey != NULL ) {
+
+ printf( "------------------------------------------------------------\n");
+
+ printf( "Public Key Len : %d\n", Context.PublicKeyLen );
+ printf( "Public Key : %08lx\n", Context.PublicNdsKey );
+
+ printf( "------------------------------------------------------------\n");
+
+ } else {
+
+ printf( "-------------------- No Public Key Data --------------------\n");
+
+ }
+
+}
+
+
+VOID
+DumpMiniIrpContext(
+ DWORD addr,
+ PNTKD_EXTENSION_APIS lpExtensionApis
+ )
+/*++
+
+ This function takes the address of a mini irp context
+ and a pointer to a debugger extension interface block.
+ It prints out the information in the mini irp context.
+
+--*/
+{
+ BOOL b;
+ MINI_IRP_CONTEXT mini;
+
+ b = getmem( (PVOID)addr, &mini, sizeof(MINI_IRP_CONTEXT), NULL );
+ if (!b ) {
+ printf( "<unable to read mini irp context>\n");
+ return;
+ }
+
+ printf( "------------------------------------------------------------\n");
+ printf( "NodeTypeCode : NW_NTC_MINI_IRP_CONTEXT\n" );
+ printf( "NodeByteSize : %d\n", mini.NodeByteSize );
+ printf( "ListEntry : %08lx\n", addr + FIELD_OFFSET( MINI_IRP_CONTEXT,
+ Next ));
+ printf( "IrpContext : %08lx\n", mini.IrpContext );
+ printf( "Irp : %08lx\n", mini.Irp );
+ printf( "Buffer : %08lx\n", mini.Buffer );
+ printf( "Mdl1 : %08lx\n", mini.Mdl1 );
+ printf( "Mdl2 : %08lx\n", mini.Mdl2 );
+ printf( "------------------------------------------------------------\n");
+
+}
+
+VOID
+nwdump(
+#ifdef WINDBG
+ HANDLE hProcess,
+ HANDLE hThread,
+#endif
+ DWORD dwCurrentPc,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ LPSTR lpArgumentString
+ )
+/*++
+
+Routine Description:
+
+ This function takes the pointer to a structure,
+ figures out what the structure is, and calls the
+ appropriate dump routine.
+
+Arguments:
+
+ CurrentPc - Supplies the current pc at the time
+ the extension is called.
+
+ lpExtensionApis - Supplies the address of the
+ functions callable by this extension.
+
+ lpArgumentString - Supplies the address of the structure.
+
+Return Value:
+
+ None.
+
+---*/
+{
+
+ DWORD addr;
+
+ //
+ // Determine the node type and dispatch.
+ //
+
+ addr = getexpr( lpArgumentString );
+
+ switch ( GetNodeType( addr, lpExtensionApis ) ) {
+
+ case NW_NTC_SCB:
+
+ DumpScb(addr, lpExtensionApis, TRUE);
+ break;
+
+ case NW_NTC_SCBNP:
+
+ DumpScbNp(addr, lpExtensionApis, TRUE);
+ break;
+
+ case NW_NTC_FCB:
+ case NW_NTC_DCB:
+
+ DumpFcb(addr, lpExtensionApis, TRUE);
+ break;
+
+ case NW_NTC_VCB:
+
+ DumpVcb(addr, lpExtensionApis);
+ break;
+
+ case NW_NTC_ICB:
+ case NW_NTC_ICB_SCB:
+
+ DumpIcb(addr, lpExtensionApis);
+ break;
+
+ case NW_NTC_IRP_CONTEXT:
+
+ DumpIrpContext(addr, lpExtensionApis);
+ break;
+
+ case NW_NTC_NONPAGED_FCB:
+
+ DumpFcbNp(addr, lpExtensionApis, TRUE);
+ break;
+
+ case NW_NTC_RCB:
+
+ DumpRcb(addr, lpExtensionApis);
+ break;
+
+ case NW_NTC_PID:
+
+ DumpPid(addr, lpExtensionApis);
+ break;
+
+ case NW_NTC_FILE_LOCK:
+
+ DumpFileLock(addr, lpExtensionApis);
+ break;
+
+ case NW_NTC_LOGON:
+
+ DumpLogon(addr, lpExtensionApis);
+ break;
+
+ case NW_NTC_MINI_IRP_CONTEXT:
+
+ DumpMiniIrpContext(addr, lpExtensionApis);
+ break;
+
+ case NW_NTC_NDS_CREDENTIAL:
+
+ DumpCredential(addr, lpExtensionApis);
+ break;
+
+ default:
+
+ printf("(this object does not have a vaid node type)\n");
+ break;
+ }
+
+}
+
+//
+// Other debugger routines.
+//
+
+VOID
+serverlist(
+#ifdef WINDBG
+ HANDLE hProcess,
+ HANDLE hThread,
+#endif
+ DWORD dwCurrentPc,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ LPSTR lpArgumentString
+ )
+/*++
+
+Routine Description:
+
+ This function displays a list of servers that the redirector
+ is maintaining connections to. The information is read from
+ the SCB queue, not from the server list in the RCB. The
+ argument to this function is ignored.
+
+--*/
+{
+
+ DWORD addrScbQueue;
+ WCHAR ServerName[64];
+ BOOL b;
+ PLIST_ENTRY ScbQueueList;
+ DWORD addrNpScb, addrScb;
+ NONPAGED_SCB NpScb;
+ SCB Scb;
+ PNTKD_CHECK_CONTROL_C lpCheckControlCRoutine;
+ lpCheckControlCRoutine = lpExtensionApis->lpCheckControlCRoutine;
+
+ //
+ // Get the address of the server list in the rdr.
+ //
+
+ addrScbQueue = getsymaddr("nwrdr!scbqueue");
+
+ if ( addrScbQueue == 0 ) {
+ printf("The server list was not locatable.\n");
+ return;
+ }
+
+ //
+ // Walk the list of servers.
+ //
+
+ printf("pNpScb pScb Ref State Name\n");
+ printf("---------------------------------------------------------------------------\n");
+
+ for ( GET_DWORD( &ScbQueueList, addrScbQueue );
+ ScbQueueList != (PLIST_ENTRY)addrScbQueue;
+ GET_DWORD( &ScbQueueList, ScbQueueList ) ) {
+
+ if ( lpCheckControlCRoutine() ) {
+ printf("<<<User Stop>>>\n");
+ break;
+ }
+
+ addrNpScb = (DWORD)CONTAINING_RECORD( ScbQueueList, NONPAGED_SCB, ScbLinks );
+
+ printf("%08lx ", addrNpScb );
+
+ b = (getmem)((LPVOID)addrNpScb,
+ &NpScb,
+ sizeof( NpScb ),
+ NULL);
+
+ if ( b == 0 ) {
+ printf("<could not continue>\n");
+ return;
+ }
+
+ addrScb = (DWORD)NpScb.pScb;
+ printf("%08lx ", addrScb );
+
+ printf("%8lx ", NpScb.Reference);
+ printf("%-25s", ScbStateToString( NpScb.State ) );
+
+ if ( addrScb != 0 ) {
+ b = (getmem)((LPVOID)addrScb,
+ &Scb,
+ sizeof( Scb ),
+ NULL);
+
+ if ( b == 0 ) {
+ printf("<unreadable>\n");
+ continue;
+ }
+
+ // Get the server name.
+
+ b = GET_STRING( ServerName, Scb.UidServerName );
+
+ if ( b ) {
+ printf( "%ws\n", ServerName );
+ } else {
+ printf( "Unreadable\n" );
+ }
+ } else {
+ printf( "Permanent SCB\n" );
+ }
+
+ }
+
+ printf("---------------------------------------------------------------------------\n");
+
+}
+
+VOID
+trace(
+#ifdef WINDBG
+ HANDLE hProcess,
+ HANDLE hThread,
+#endif
+ DWORD dwCurrentPc,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ LPSTR lpArgumentString
+ )
+/*++
+
+Routine Description:
+
+ This function dumps the nwrdr trace buffer. Arguments to
+ this function are ignored.
+
+To Be Done:
+
+ Read trace buffer size out of nwrdrd and dynamically size.
+
+--*/
+
+{
+ ULONG addrDBuffer, addrDBufferPtr, DBufferPtr;
+ ULONG BufferSize;
+ PCHAR TraceStart, CurrentPtr;
+ char buffer[80 + 1];
+ char *bptr;
+ char *newptr;
+ int i;
+ int readsize;
+ PNTKD_CHECK_CONTROL_C lpCheckControlCRoutine;
+ lpCheckControlCRoutine = lpExtensionApis->lpCheckControlCRoutine;
+
+ addrDBuffer = getsymaddr( "nwrdr!dbuffer" );
+
+ if ( !addrDBuffer ) {
+ printf("(unable to locate the trace buffer address)\n");
+ return;
+ } else {
+ printf("Address of Dbuffer = %08lx\n", addrDBuffer );
+ }
+
+ addrDBufferPtr = getsymaddr( "nwrdr!dbufferptr" );
+
+ if ( !addrDBuffer ) {
+ printf("(unable to locate the trace buffer pointer)\n");
+ return;
+ } else {
+ printf("Address of DbufferPtr = %08lx\n", addrDBufferPtr );
+ }
+
+ GET_DWORD( &DBufferPtr, addrDBufferPtr );
+ printf("DbufferPtr = %08lx\n", DBufferPtr );
+
+ // Set up state variables and loop.
+
+ TraceStart = (char *)addrDBuffer;
+ BufferSize = 100*255+1;
+ CurrentPtr = (char *)DBufferPtr;
+
+ buffer[80] = '\0';
+ newptr = CurrentPtr + 1;
+ while ( 1 ) {
+
+ if ( lpCheckControlCRoutine() ) {
+ printf("<<<User Stop>>>\n");
+ break;
+ }
+
+ if ( newptr + 80 > TraceStart+BufferSize ) {
+ readsize = TraceStart+BufferSize - newptr;
+ } else {
+ readsize = 80;
+ }
+
+ getmem( newptr, buffer, readsize, NULL );
+
+ bptr = buffer;
+ for (i = 0; i<80 ; i++ ) {
+ if ( buffer[i] == '\n') {
+ buffer[i] = 0;
+ printf( "%s\n", bptr );
+ bptr = &buffer[i+1];
+ }
+ }
+ printf( "%s", bptr );
+
+ //
+ // If we're back to where we started, break out of here.
+ //
+
+ if ( (newptr <= CurrentPtr) &&
+ (newptr + readsize) >= CurrentPtr ) {
+ break;
+ }
+
+ //
+ // Advance the running pointer.
+ //
+
+ newptr += readsize;
+ if ( newptr >= TraceStart+BufferSize ) {
+ newptr = TraceStart;
+ }
+ }
+ printf( "\n");
+}
+
+VOID
+reftrace(
+#ifdef WINDBG
+ HANDLE hProcess,
+ HANDLE hThread,
+#endif
+ DWORD dwCurrentPc,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ LPSTR lpArgumentString
+ )
+/*++
+
+Routine Description:
+
+ This function dumps the nwrdr reference trace buffer.
+
+--*/
+{
+ ULONG addrRBuffer, addrRBufferPtr, RBufferPtr;
+ ULONG BufferSize;
+ PCHAR TraceStart, CurrentPtr;
+ char buffer[80 + 1];
+ char *bptr;
+ char *newptr;
+ int i;
+ int readsize;
+ PNTKD_CHECK_CONTROL_C lpCheckControlCRoutine;
+ lpCheckControlCRoutine = lpExtensionApis->lpCheckControlCRoutine;
+
+ addrRBuffer = getsymaddr( "nwrdr!RBuffer" );
+
+ if ( !addrRBuffer ) {
+ printf("(unable to locate the trace buffer address)\n");
+ return;
+ } else {
+ printf("Address of RBuffer = %08lx\n", addrRBuffer );
+ }
+
+ addrRBufferPtr = getsymaddr( "nwrdr!RBufferptr" );
+
+ if ( !addrRBuffer ) {
+ printf("(unable to locate the trace buffer pointer)\n");
+ return;
+ } else {
+ printf("Address of RBufferPtr = %08lx\n", addrRBufferPtr );
+ }
+
+ GET_DWORD( &RBufferPtr, addrRBufferPtr );
+ printf("RBufferPtr = %08lx\n", RBufferPtr );
+
+ // Set up state variables and loop.
+
+ TraceStart = (char *)addrRBuffer;
+ BufferSize = 100*255+1;
+ CurrentPtr = (char *)RBufferPtr;
+
+ buffer[80] = '\0';
+ newptr = CurrentPtr + 1;
+ while ( 1 ) {
+
+ if ( lpCheckControlCRoutine() ) {
+ printf("<<<User Stop>>>\n");
+ break;
+ }
+
+ if ( newptr + 80 > TraceStart+BufferSize ) {
+ readsize = TraceStart+BufferSize - newptr;
+ } else {
+ readsize = 80;
+ }
+
+ getmem( newptr, buffer, readsize, NULL );
+
+ bptr = buffer;
+ for (i = 0; i<80 ; i++ ) {
+ if ( buffer[i] == '\n') {
+ buffer[i] = 0;
+ printf( "%s\n", bptr );
+ bptr = &buffer[i+1];
+ }
+ }
+ printf( "%s", bptr );
+
+ //
+ // If we're back to where we started, break out of here.
+ //
+
+ if ( (newptr <= CurrentPtr) &&
+ (newptr + readsize) >= CurrentPtr ) {
+ break;
+ }
+
+ //
+ // Advance the running pointer.
+ //
+
+ newptr += readsize;
+ if ( newptr >= TraceStart+BufferSize ) {
+ newptr = TraceStart;
+ }
+ }
+ printf( "\n");
+}
+
+VOID
+logonlist(
+#ifdef WINDBG
+ HANDLE hProcess,
+ HANDLE hThread,
+#endif
+ DWORD dwCurrentPc,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ LPSTR lpArgumentString
+ )
+/*++
+
+Routine Description:
+
+ This routine prints out the logon list for the rdr. Arguments
+ to this function are ignored.
+
+--*/
+
+{
+ DWORD addrLogonList;
+ WCHAR Data[64];
+ BOOL b;
+ PLIST_ENTRY LogonList;
+ DWORD addrLogonEntry;
+ LOGON Logon;
+ PNTKD_CHECK_CONTROL_C lpCheckControlCRoutine;
+ lpCheckControlCRoutine = lpExtensionApis->lpCheckControlCRoutine;
+
+ // Get the address of the logon list.
+
+ addrLogonList = getsymaddr( "nwrdr!logonlist" );
+
+ if ( addrLogonList == 0 ) {
+ printf("The logon list could not be located.\n");
+ return;
+ }
+
+ // Walk the list of servers
+
+ printf("pLogon User Name Password Pref Server UID\n" );
+ printf("---------------------------------------------------------------------------\n" );
+
+ for ( GET_DWORD( &LogonList, addrLogonList );
+ LogonList != (PLIST_ENTRY)addrLogonList;
+ GET_DWORD( &LogonList, LogonList ) ) {
+
+ if ( lpCheckControlCRoutine() ) {
+ printf("<<<User Stop>>>\n");
+ break;
+ }
+
+ addrLogonEntry = (DWORD)CONTAINING_RECORD( LogonList, LOGON, Next );
+
+ printf("%08lx ", addrLogonEntry );
+
+ b = (getmem)((LPVOID)addrLogonEntry,
+ &Logon,
+ sizeof( Logon ),
+ NULL);
+
+ if ( b == 0 ) return;
+
+ if ( Logon.NodeTypeCode != NW_NTC_LOGON ) {
+ printf( "<invalid node type>\n" );
+ return;
+ }
+
+ b = GET_STRING( Data, Logon.UserName );
+
+ if ( b ) {
+ printf( "%-15ws", Data );
+ } else {
+ printf( "%-15s", "Unreadable" );
+ }
+
+ /*
+ b = GET_STRING( Data, Logon.PassWord );
+
+ if ( b ) {
+ printf( "%-15ws", Data );
+ } else {
+ printf( "%-15s", "Unreadable" );
+ }
+ */
+ printf( "%-15s", "<secret>" );
+
+ b = GET_STRING( Data, Logon.ServerName );
+
+ if ( b ) {
+ printf( "%-15ws", Data );
+ } else {
+ printf( "%-15s", "Unreadable" );
+ }
+
+ printf( "%08lx:%08x\n", Logon.UserUid.HighPart, Logon.UserUid.LowPart );
+ }
+
+ printf("---------------------------------------------------------------------------\n" );
+
+}
+
+//
+// Functions that help mangle lists of objects.
+//
+
+VOID
+vcblist(
+#ifdef WINDBG
+ HANDLE hProcess,
+ HANDLE hThread,
+#endif
+ DWORD dwCurrentPc,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ LPSTR lpArgumentString
+ )
+/*++
+
+ This function takes a pointer to the pageable portion
+ or non-pageable portion of an SCB and dumps the VCB
+ list for that SCB.
+
+--*/
+{
+ BOOL b;
+ PVOID objAddr;
+
+ PLIST_ENTRY VcbList;
+ DWORD addrVcbList;
+ PVCB addrVcb;
+ PNTKD_CHECK_CONTROL_C lpCheckControlCRoutine;
+ lpCheckControlCRoutine = lpExtensionApis->lpCheckControlCRoutine;
+
+ // Figure out which object we have.
+ objAddr = (PVOID)getexpr( lpArgumentString );
+
+ // Invariant: If we leave the switch, objAddr must point to the
+ // pageable portion of the SCB that we are interested in.
+
+ switch ( GetNodeType( (DWORD)objAddr, lpExtensionApis ) ) {
+
+ case NW_NTC_SCB:
+
+ break;
+
+ case NW_NTC_SCBNP:
+
+ GET_DWORD( &objAddr,
+ ( (PCHAR)objAddr + FIELD_OFFSET( NONPAGED_SCB, pScb ) ) );
+ if ( objAddr == 0 ) return;
+ break;
+
+ default:
+
+ printf( "(invalid node type code: argument must point to an scb or npscb)\n" );
+ return;
+ }
+
+ // Get the head of the vcb list.
+ addrVcbList = (DWORD)((PCHAR)objAddr + FIELD_OFFSET( SCB, ScbSpecificVcbQueue ));
+
+ // Walk the list and print.
+ for ( GET_DWORD( &VcbList, addrVcbList ) ;
+ VcbList != (PLIST_ENTRY)addrVcbList ;
+ GET_DWORD( &VcbList, VcbList ) ) {
+
+ if ( lpCheckControlCRoutine() ) {
+ printf("<<<User Stop>>>\n");
+ break;
+ }
+
+ addrVcb = (PVCB)CONTAINING_RECORD( VcbList, VCB, VcbListEntry );
+ if( GetNodeType( (DWORD)addrVcb, lpExtensionApis ) != NW_NTC_VCB )
+ printf( "(invalid entry in vcb list)\n" );
+ else
+ DumpVcb( (DWORD)addrVcb, lpExtensionApis );
+ }
+}
+
+VOID
+irplist(
+#ifdef WINDBG
+ HANDLE hProcess,
+ HANDLE hThread,
+#endif
+ DWORD dwCurrentPc,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ LPSTR lpArgumentString
+ )
+/*++
+
+ This function takes a pointer to the non-pageable portion
+ of an SCB and dumps the IRP list for that non-pageable SCB.
+
+--*/
+{
+ PLIST_ENTRY IrpList;
+ DWORD addrIrpList;
+ PIRP_CONTEXT addrIrp;
+
+ PVOID objAddr;
+ BOOL b;
+
+ PNTKD_CHECK_CONTROL_C lpCheckControlCRoutine;
+ lpCheckControlCRoutine = lpExtensionApis->lpCheckControlCRoutine;
+
+
+ // Figure out which object we have.
+ objAddr = (PVOID)getexpr( lpArgumentString );
+
+ // Invariant: If we leave the switch, objAddr must point to the
+ // non-pageable portion of the SCB that we are interested in.
+
+ switch ( GetNodeType( (DWORD)objAddr, lpExtensionApis ) ) {
+
+ case NW_NTC_SCB:
+
+ GET_DWORD( &objAddr,
+ ( (PCHAR)objAddr + FIELD_OFFSET( SCB, pNpScb ) ) );
+ if ( objAddr == 0 ) return;
+ break;
+
+ case NW_NTC_SCBNP:
+
+ break;
+
+ default:
+
+ printf( "(invalid node type code: argument must point to an scb or npscb)\n" );
+ return;
+ }
+
+ // Get the head of the request list.
+ addrIrpList = (DWORD)((PCHAR)objAddr + FIELD_OFFSET( NONPAGED_SCB, Requests ));
+
+ // Walk the list and print.
+ for ( GET_DWORD( &IrpList, addrIrpList ) ;
+ IrpList != (PLIST_ENTRY)addrIrpList ;
+ GET_DWORD( &IrpList, IrpList ) ) {
+
+ if ( lpCheckControlCRoutine() ) {
+ printf("<<<User Stop>>>\n");
+ break;
+ }
+
+ addrIrp = (PIRP_CONTEXT)CONTAINING_RECORD( IrpList, IRP_CONTEXT, NextRequest );
+ if( GetNodeType( (DWORD)addrIrp, lpExtensionApis ) != NW_NTC_IRP_CONTEXT )
+ printf( "(invalid entry in the irp context list)\n" );
+ else
+ DumpIrpContext( (DWORD)addrIrp, lpExtensionApis );
+ }
+}
+
+VOID
+fcblist(
+#ifdef WINDBG
+ HANDLE hProcess,
+ HANDLE hThread,
+#endif
+ DWORD dwCurrentPc,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ LPSTR lpArgumentString
+ )
+/*++
+
+ This function takes a pointer to a VCB and dumps
+ the FCB list for that VCB.
+
+--*/
+{
+ PLIST_ENTRY FcbList;
+ DWORD addrFcbList;
+ PFCB addrFcb;
+
+ NODE_TYPE_CODE ntc;
+ PVOID objAddr;
+ BOOL b;
+
+ PNTKD_CHECK_CONTROL_C lpCheckControlCRoutine;
+ lpCheckControlCRoutine = lpExtensionApis->lpCheckControlCRoutine;
+
+ // Figure out which object we have.
+ objAddr = (PVOID)getexpr( lpArgumentString );
+
+ if ( GetNodeType( (DWORD)objAddr, lpExtensionApis ) != NW_NTC_VCB ) {
+
+ printf( "(invalid node type code: argument must point to a vcb)\n" );
+ return;
+ }
+
+ // Get the head of the fcb list.
+ addrFcbList = (DWORD)((PCHAR)objAddr + FIELD_OFFSET( VCB, FcbList ));
+
+ for ( GET_DWORD( &FcbList, addrFcbList ) ;
+ FcbList != (PLIST_ENTRY)addrFcbList ;
+ GET_DWORD( &FcbList, FcbList ) ) {
+
+ if ( lpCheckControlCRoutine() ) {
+ printf("<<<User Stop>>>\n");
+ break;
+ }
+
+ addrFcb = (PFCB)CONTAINING_RECORD( FcbList, FCB, FcbListEntry );
+ ntc = GetNodeType( (DWORD)addrFcb, lpExtensionApis );
+ if( (ntc != NW_NTC_FCB) && (ntc != NW_NTC_DCB) )
+ printf( "(invalid entry in the fcb list)\n" );
+ else
+ DumpFcb( (DWORD)addrFcb, lpExtensionApis, TRUE );
+ }
+
+}
+
+VOID
+icblist(
+#ifdef WINDBG
+ HANDLE hProcess,
+ HANDLE hThread,
+#endif
+ DWORD dwCurrentPc,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ LPSTR lpArgumentString
+ )
+/*++
+
+ This function takes a pointer to the pageable portion
+ of an SCB or FCB and dumps the ICB list for that SCB or FCB.
+
+--*/
+{
+ PVOID objAddr;
+ BOOL b;
+ NODE_TYPE_CODE ntc;
+
+ PICB addrIcb;
+ PLIST_ENTRY IcbList;
+ DWORD addrIcbList, IcbCount;
+
+ PNTKD_CHECK_CONTROL_C lpCheckControlCRoutine;
+ lpCheckControlCRoutine = lpExtensionApis->lpCheckControlCRoutine;
+
+ // Figure out which object we have.
+ objAddr = (PVOID)getexpr( lpArgumentString );
+
+ // Invariant: If we leave the switch, addrIcbList must point
+ // to the head of the ICB list that we are interested in.
+
+ switch ( GetNodeType( (DWORD)objAddr, lpExtensionApis ) ) {
+
+ case NW_NTC_SCB:
+
+ addrIcbList = (DWORD)((PCHAR)objAddr + FIELD_OFFSET( SCB, IcbList ));
+ break;
+
+ case NW_NTC_SCBNP:
+
+ // Look up the pageable portion.
+ GET_DWORD( &objAddr,
+ ( (PCHAR)objAddr + FIELD_OFFSET( NONPAGED_SCB, pScb ) ) );
+ if ( objAddr == 0 ) return;
+ // Now get it.
+ addrIcbList = (DWORD)((PCHAR)objAddr + FIELD_OFFSET( SCB, IcbList));
+ break;
+
+ case NW_NTC_FCB:
+ case NW_NTC_DCB:
+
+ addrIcbList = (DWORD)((PCHAR)objAddr + FIELD_OFFSET( FCB, IcbList ));
+ break;
+
+ case NW_NTC_NONPAGED_FCB:
+
+ // Look up the pageable portion.
+ GET_DWORD( &objAddr,
+ ( (PCHAR)objAddr + FIELD_OFFSET( NONPAGED_FCB, Fcb ) ) );
+ if (objAddr == 0) return;
+ // Now get it.
+ addrIcbList = (DWORD)((PCHAR)objAddr + FIELD_OFFSET( FCB, IcbList ));
+ break;
+
+ default:
+
+ printf( "(invalid node type: argument must be: scb, npscb, fcb, dcb, or npfcb)\n" );
+ return;
+ }
+
+ // Walk the list.
+ for ( GET_DWORD( &IcbList, addrIcbList ) ;
+ IcbList != (PLIST_ENTRY)addrIcbList ;
+ GET_DWORD( &IcbList, IcbList ) ) {
+
+ if ( lpCheckControlCRoutine() ) {
+ printf("<<<User Stop>>>\n");
+ break;
+ }
+
+ addrIcb = (PICB)CONTAINING_RECORD( IcbList, ICB, ListEntry );
+ ntc = GetNodeType( (DWORD)addrIcb, lpExtensionApis );
+ if( (ntc != NW_NTC_ICB) && (ntc != NW_NTC_ICB_SCB) )
+ printf( "(invalid entry in icb list)\n" );
+ else
+ DumpIcb( (DWORD)addrIcb, lpExtensionApis );
+
+ }
+
+}
+
+VOID
+credlist(
+#ifdef WINDBG
+ HANDLE hProcess,
+ HANDLE hThread,
+#endif
+ DWORD dwCurrentPc,
+ PNTKD_EXTENSION_APIS lpExtensionApis,
+ LPSTR lpArgumentString
+ )
+/*++
+
+ This function takes a pointer to a LOGON and dumps
+ the NDS credential list for that user.
+
+--*/
+{
+ PLIST_ENTRY CredList;
+ DWORD addrCredList;
+ PNDS_SECURITY_CONTEXT addrCred;
+
+ NODE_TYPE_CODE ntc;
+ PVOID objAddr;
+ BOOL b;
+
+ PNTKD_CHECK_CONTROL_C lpCheckControlCRoutine;
+ lpCheckControlCRoutine = lpExtensionApis->lpCheckControlCRoutine;
+
+ // Figure out which object we have.
+ objAddr = (PVOID)getexpr( lpArgumentString );
+
+ if ( GetNodeType( (DWORD)objAddr, lpExtensionApis ) != NW_NTC_LOGON ) {
+
+ printf( "(invalid node type code: argument must point to a logon)\n" );
+ return;
+ }
+
+ // Get the head of the fcb list.
+ addrCredList = (DWORD)((PCHAR)objAddr + FIELD_OFFSET( LOGON, NdsCredentialList ));
+
+ for ( GET_DWORD( &CredList, addrCredList ) ;
+ CredList != (PLIST_ENTRY)addrCredList ;
+ GET_DWORD( &CredList, CredList ) ) {
+
+ if ( lpCheckControlCRoutine() ) {
+ printf("<<<User Stop>>>\n");
+ break;
+ }
+
+ addrCred = (PNDS_SECURITY_CONTEXT)
+ CONTAINING_RECORD( CredList,
+ NDS_SECURITY_CONTEXT,
+ Next );
+ ntc = GetNodeType( (DWORD)addrCred, lpExtensionApis );
+ if( (ntc != NW_NTC_NDS_CREDENTIAL ) )
+ printf( "(invalid entry in the credential list)\n" );
+ else
+ DumpCredential( (DWORD)addrCred, lpExtensionApis);
+ printf("\n");
+ }
+
+}
diff --git a/private/nw/rdr/kdext/windbg/makefile b/private/nw/rdr/kdext/windbg/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/nw/rdr/kdext/windbg/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/rdr/kdext/windbg/nwdbg.def b/private/nw/rdr/kdext/windbg/nwdbg.def
new file mode 100644
index 000000000..ca9758039
--- /dev/null
+++ b/private/nw/rdr/kdext/windbg/nwdbg.def
@@ -0,0 +1,15 @@
+DESCRIPTION 'NT Netware Redirector KD extensions'
+
+EXPORTS
+ nwdump
+ logonlist
+ serverlist
+ trace
+ traceflags
+ help
+ vcblist
+ fcblist
+ icblist
+ irplist
+ credlist
+ reftrace
diff --git a/private/nw/rdr/kdext/windbg/nwdbg.rc b/private/nw/rdr/kdext/windbg/nwdbg.rc
new file mode 100644
index 000000000..5d9a5fe78
--- /dev/null
+++ b/private/nw/rdr/kdext/windbg/nwdbg.rc
@@ -0,0 +1,12 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_UNKNOWN
+#define VER_FILEDESCRIPTION_STR "NW WinDbg Debugger Extensions DLL"
+#define VER_INTERNALNAME_STR "NwDbg.DLL"
+#define VER_ORIGINALFILENAME_STR "NwDbg.DLL"
+
+#include "common.ver"
+
diff --git a/private/nw/rdr/kdext/windbg/sources b/private/nw/rdr/kdext/windbg/sources
new file mode 100644
index 000000000..7f3d51824
--- /dev/null
+++ b/private/nw/rdr/kdext/windbg/sources
@@ -0,0 +1,51 @@
+!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:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=ntos
+MINORCOMP=rdr
+
+TARGETNAME=nwdbg
+TARGETPATH=obj
+TARGETTYPE=DYNLINK
+TARGETLIBS= \
+ \nt\public\sdk\lib\*\kernel32.lib
+
+
+DLLBASE=0x1010000
+
+INCLUDES=..\..;..\..\..\inc;$(NTOS_ROOT)\inc;$(_NTROOT)\private\inc
+
+!IFNDEF DISABLE_NET_UNICODE
+UNICODE=1
+NET_C_DEFINES=-DUNICODE
+!ENDIF
+
+C_DEFINES=-DWINDBG
+
+SOURCES=..\nwrdrkd.c \
+ nwdbg.rc
+
+UMTYPE=console
+OPTIONAL_NTTEST=
+
diff --git a/private/nw/rdr/lock.c b/private/nw/rdr/lock.c
new file mode 100644
index 000000000..42d069f5e
--- /dev/null
+++ b/private/nw/rdr/lock.c
@@ -0,0 +1,1357 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ Lock.c
+
+Abstract:
+
+ This module implements the Lock routine for the NetWare redirector.
+
+ Notes on the implementation of locks.
+
+ o Netware servers handle lock conflicts differently than a LAN Man
+ server, or NT file system would. In particular:
+
+ - A lock conflict on a single file handle (i.e. the same app owns
+ the lock, and is trying to obtain a conflicting lock): The
+ netware server will fail the request only if the lock range is
+ identical to a held lock. Also, the lock fails immediately, even
+ if the app requested a blocking lock.
+
+ - A lock conflict generated by 2 app from the same workstation:
+ The server will fail the request if the request lock overlaps an
+ existing lock by even a single byte, but the server will fail the
+ request immediately, even if the app requested a blocking lock.
+
+ - A lock conflict generated by 2 different workstations: This works
+ as expected. The lock fails if it overlaps an existing lock, and
+ the request blocks if requested by the app.
+
+ o The NT workstation needs to impose NT file system behaviour when dealing
+ with a netware server. There are 2 key elements (complications)
+ added to the redirector to handle this.
+
+ - A locally maintained lock database. This is used to test for
+ lock conflicts locally. If a conflict is detected and the
+ requestor asks for a blocking lock, the lock request is queued
+ to a local lock conflict list. This list is processed when real
+ locks are released.
+
+ - A pending lock list. This is used to poll the netware server
+ about remote lock conflicts. We could not let our lock request
+ block indefinitely as this would tie up our one channel of
+ communication to the server.
+
+ o The data structures
+
+ - NonPagedFcb
+ -> FileLockList - The list of existing locks.
+ -> PendingLockList - The list of locks pending due to a
+ local conflict.
+
+ - NwPendingLockList
+ The list of locks pending due to a remote conflict. The
+ locks are retried indefinitely using a polling mechanism.
+
+ A request can be removed from the pending list via (1) a
+ cleanup for the correct ICB (2) the IRP can be cancelled.
+ (3) The server actually grants the lock.
+
+ o Other notes:
+
+ We play some games to allow us to use the FCB resource as the
+ synchronization mechanism, even though much processing happens
+ at raised IRQL. Be careful not to break this.
+
+Author:
+
+ Colin Watson [ColinW] 13-May-1993
+ Manny Weiser [MannyW] 16-May-1993
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_LOCKCTRL)
+
+NTSTATUS
+NwCommonLock(
+ PIRP_CONTEXT pIrpContext
+ );
+
+NTSTATUS
+LockNcp(
+ PIRP_CONTEXT IrpContext,
+ PICB Icb
+ );
+
+NTSTATUS
+LockNcpCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ );
+
+NTSTATUS
+UnlockNcpCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ );
+
+BOOLEAN
+LockIsOverlapping(
+ PNONPAGED_FCB pNpFcb,
+ LONG StartFileOffset,
+ ULONG Length
+ );
+
+VOID
+AddLockToFcb(
+ PNONPAGED_FCB pNpFcb,
+ PNW_FILE_LOCK FileLock
+ );
+
+VOID
+RemoveLockFromFcb(
+ PNONPAGED_FCB pNpFcb,
+ PNW_FILE_LOCK FileLock
+ );
+
+VOID
+ReattemptPendingLocks(
+ PNONPAGED_FCB pNpFcb
+ );
+
+BOOLEAN
+LockExists(
+ PNONPAGED_FCB pNpFcb,
+ LONG StartOffset,
+ ULONG Length,
+ PNW_FILE_LOCK *FileLock
+ );
+
+NTSTATUS
+UnlockIcbLocks(
+ PIRP_CONTEXT pIrpContext
+ );
+
+NTSTATUS
+UnlockIcbLocksCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwFsdLockControl )
+#pragma alloc_text( PAGE, NwCommonLock )
+#pragma alloc_text( PAGE, LockNcp )
+#pragma alloc_text( PAGE, LockIsOverlapping )
+#pragma alloc_text( PAGE, NwFreeLocksForIcb )
+#pragma alloc_text( PAGE, UnlockIcbLocks )
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, LockNcpCallback )
+#pragma alloc_text( PAGE1, UnlockNcpCallback )
+#pragma alloc_text( PAGE1, AddLockToFcb )
+#pragma alloc_text( PAGE1, RemoveLockFromFcb )
+#pragma alloc_text( PAGE1, ReattemptPendingLocks )
+#pragma alloc_text( PAGE1, LockExists )
+#pragma alloc_text( PAGE1, UnlockIcbLocksCallback )
+#endif
+
+#endif
+
+#if 0 // Not pageable
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+
+
+NTSTATUS
+NwFsdLockControl (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+/*++
+
+Routine Description:
+
+ This routine implements the FSD part of the NtCreateFile and NtOpenFile
+ API calls.
+
+Arguments:
+
+ DeviceObject - Supplies the device object for the redirector.
+
+ Irp - Supplies the Irp being processed
+
+Return Value:
+
+ NTSTATUS - The Fsd status for the Irp
+
+--*/
+{
+ NTSTATUS Status;
+ PIRP_CONTEXT IrpContext = NULL;
+ BOOLEAN TopLevel;
+
+ PAGED_CODE();
+
+ TimerStart(Dbg);
+ DebugTrace(+1, Dbg, "NwFsdLockControl\n", 0);
+
+ //
+ // Call the common lock routine, with block allowed if the operation
+ // is synchronous.
+ //
+
+ FsRtlEnterFileSystem();
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ try {
+
+ IrpContext = AllocateIrpContext( Irp );
+ Status = NwCommonLock( IrpContext );
+
+ } except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
+
+ if ( IrpContext == NULL ) {
+
+ //
+ // If we couldn't allocate an irp context, just complete
+ // irp without any fanfare.
+ //
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ Irp->IoStatus.Status = Status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
+
+ } else {
+
+ //
+ // We had some trouble trying to perform the requested
+ // operation, so we'll abort the I/O request with
+ // the error Status that we get back from the
+ // execption code
+ //
+
+ Status = NwProcessException( IrpContext, GetExceptionCode() );
+ }
+
+ }
+
+ if ( IrpContext ) {
+ NwCompleteRequest( IrpContext, Status );
+ }
+
+ if ( TopLevel ) {
+ NwSetTopLevelIrp( NULL );
+ }
+ FsRtlExitFileSystem();
+
+ //
+ // And return to our caller
+ //
+
+ DebugTrace(-1, Dbg, "NwFsdLock -> %08lx\n", Status );
+
+ TimerStop(Dbg,"NwFsdLockControl");
+
+ return Status;
+
+ UNREFERENCED_PARAMETER(DeviceObject);
+}
+
+
+NTSTATUS
+NwCommonLock (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine does the common code for NtLockFile/NtUnlockFile.
+
+Arguments:
+
+ IrpContext - Supplies the request being processed.
+
+Return Value:
+
+ NTSTATUS - The return status for the operation
+
+--*/
+
+{
+ NTSTATUS status;
+
+ PIRP Irp;
+ PIO_STACK_LOCATION irpSp;
+
+ NODE_TYPE_CODE nodeTypeCode;
+ PICB icb;
+ PFCB fcb;
+ PVOID fsContext;
+
+ PAGED_CODE();
+
+ //
+ // Get the current stack location
+ //
+
+ Irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ DebugTrace(+1, Dbg, "CommonLock...\n", 0);
+ DebugTrace( 0, Dbg, "Irp = %08lx\n", (ULONG)Irp);
+
+ //
+ // Decode the file object to figure out who we are. If the result
+ // is not the root DCB then its an illegal parameter.
+ //
+
+ nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
+ &fsContext,
+ (PVOID *)&icb );
+
+ if (nodeTypeCode != NW_NTC_ICB) {
+
+ DebugTrace(0, Dbg, "Not a file\n", 0);
+
+ status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "CommonLock -> %08lx\n", status );
+ return status;
+ }
+
+ //
+ // Make sure that this ICB is still active.
+ //
+
+ NwVerifyIcb( icb );
+
+ fcb = (PFCB)icb->SuperType.Fcb;
+ nodeTypeCode = fcb->NodeTypeCode;
+
+ if (nodeTypeCode == NW_NTC_FCB ) {
+
+ IrpContext->pScb = fcb->Scb;
+ IrpContext->pNpScb = IrpContext->pScb->pNpScb;
+ IrpContext->Icb = icb;
+
+ } else {
+
+ DebugTrace(0, Dbg, "Not a file\n", 0);
+
+ status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "CommonLock -> %08lx\n", status );
+ return status;
+ }
+
+ switch (irpSp->MinorFunction) {
+
+ case IRP_MN_LOCK:
+ case IRP_MN_UNLOCK_SINGLE:
+ case IRP_MN_UNLOCK_ALL:
+ case IRP_MN_UNLOCK_ALL_BY_KEY:
+ status = LockNcp( IrpContext, icb );
+ break;
+
+ default:
+ //
+ // Minor function added to I/O system that this driver does
+ // not understand.
+ //
+
+ status = STATUS_INVALID_PARAMETER;
+ }
+
+ DebugTrace(-1, Dbg, "CommonLock -> %08lx\n", status);
+
+ return status;
+}
+
+NTSTATUS
+LockNcp(
+ PIRP_CONTEXT IrpContext,
+ PICB Icb
+ )
+/*++
+
+Routine Description:
+
+ This routine exchanges a series of Lock NCPs with the server.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+ Icb - Supplies the file specific information.
+
+Return Value:
+
+ Status of transfer.
+
+--*/
+{
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ LARGE_INTEGER ByteOffset;
+ LARGE_INTEGER Length;
+ ULONG Key;
+
+ PSCB pScb;
+ PNONPAGED_FCB pNpFcb;
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+
+ PNW_FILE_LOCK FileLock = NULL;
+ USHORT LockFlags = 3; // BUGBUG
+
+ PAGED_CODE();
+
+ irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+ ByteOffset = irpSp->Parameters.LockControl.ByteOffset;
+
+ if ( irpSp->Parameters.LockControl.Length != NULL ) {
+ Length = *irpSp->Parameters.LockControl.Length;
+ } else {
+ Length.HighPart = 0;
+ Length.LowPart = 0;
+ }
+
+ Key = irpSp->Parameters.LockControl.Key;
+
+ DebugTrace(+1, Dbg, "LockNcp...\n", 0);
+ DebugTrace( 0, Dbg, "irp = %08lx\n", (ULONG)irp);
+ DebugTrace( 0, Dbg, "MinorFun= %08lx\n", (ULONG)irpSp->MinorFunction);
+ DebugTrace( 0, Dbg, "File = %wZ\n", &Icb->SuperType.Fcb->FullFileName);
+ DebugTrace( 0, Dbg, "HOffset = %lx\n", ByteOffset.HighPart);
+ DebugTrace( 0, Dbg, "LOffset = %lx\n", ByteOffset.LowPart);
+ DebugTrace( 0, Dbg, "HLength = %lx\n", Length.HighPart);
+ DebugTrace( 0, Dbg, "LLength = %lx\n", Length.LowPart);
+ DebugTrace( 0, Dbg, "Key = %lx\n", Key);
+
+ pScb = Icb->SuperType.Fcb->Scb;
+
+ ASSERT (pScb->NodeTypeCode == NW_NTC_SCB);
+
+ pNpFcb = Icb->SuperType.Fcb->NonPagedFcb;
+
+ //
+ // Get to the front of the ScbQueue to protect access to the lock list.
+ //
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ try {
+
+ switch ( irpSp->MinorFunction ) {
+
+ case IRP_MN_LOCK:
+
+ //
+ // Since we are doing a lock we will need to send an End Of Job
+ // for this PID.
+ //
+
+ NwSetEndOfJobRequired( Icb->Pid );
+
+ //
+ // Try to allocate a lock structure before we ask the
+ // server to perform the lock.
+ //
+
+ FileLock = ALLOCATE_POOL_EX( NonPagedPool, sizeof( NW_FILE_LOCK ) );
+ IrpContext->Specific.Lock.FileLock = FileLock;
+
+ FileLock->NodeTypeCode = NW_NTC_FILE_LOCK;
+ FileLock->NodeByteSize = sizeof( NW_FILE_LOCK );
+
+ FileLock->StartFileOffset = ByteOffset.LowPart;
+ FileLock->Length = Length.LowPart;
+ FileLock->EndFileOffset = ByteOffset.LowPart + Length.LowPart - 1;
+ FileLock->Key = Key;
+ FileLock->Icb = Icb;
+ FileLock->IrpContext = IrpContext;
+
+ if ( irpSp->Flags & SL_EXCLUSIVE_LOCK ) {
+ LockFlags = 0x00; // BUGBUG
+ } else {
+ LockFlags = 0x02; // BUGBUG
+ }
+
+ FileLock->Flags = LockFlags;
+
+ //
+ // Is this is an overlapping lock
+ //
+
+ if ( irpSp->Flags & SL_FAIL_IMMEDIATELY ) {
+ IrpContext->Specific.Lock.Wait = FALSE;
+ } else {
+ IrpContext->Specific.Lock.Wait = TRUE;
+ }
+
+ if ( LockIsOverlapping( pNpFcb, ByteOffset.LowPart, Length.LowPart ) ) {
+
+ if ( IrpContext->Specific.Lock.Wait ) {
+
+ //
+ // Queue this IRP context to the FCB. We'll process it
+ // when the local conflict is removed.
+ //
+
+ InsertTailList( &pNpFcb->PendingLockList, &FileLock->ListEntry );
+ status = STATUS_PENDING;
+ NwDequeueIrpContext( IrpContext, FALSE );
+
+ } else {
+ status = STATUS_FILE_LOCK_CONFLICT;
+ }
+
+ } else {
+
+ //
+ // Send the lock request.
+ //
+
+ status = Exchange (
+ IrpContext,
+ LockNcpCallback,
+ "Fbrddw",
+ NCP_LOCK_RANGE,
+ LockFlags | 0x01, // BUGBUG
+ Icb->Handle, sizeof( Icb->Handle ),
+ ByteOffset.LowPart,
+ Length.LowPart,
+ LockTimeoutThreshold );
+
+ if ( !NT_SUCCESS( status ) ) {
+ FREE_POOL( FileLock );
+ }
+ }
+
+ break;
+
+ case IRP_MN_UNLOCK_SINGLE:
+
+ if ( !LockExists( pNpFcb, ByteOffset.LowPart, Length.LowPart, &FileLock ) ) {
+
+ status = STATUS_RANGE_NOT_LOCKED;
+
+ } else {
+ IrpContext->Specific.Lock.FileLock = FileLock;
+
+ status = Exchange (
+ IrpContext,
+ UnlockNcpCallback,
+ "F-rddw",
+ NCP_UNLOCK_RANGE,
+ Icb->Handle, sizeof( Icb->Handle ),
+ ByteOffset.LowPart,
+ Length.LowPart,
+ 1 );
+ }
+
+ break;
+
+ case IRP_MN_UNLOCK_ALL:
+ IrpContext->Icb = Icb;
+ IrpContext->Specific.Lock.ByKey = FALSE ;
+ IrpContext->Specific.Lock.LastLock = &pNpFcb->FileLockList;
+
+ status = UnlockIcbLocks( IrpContext );
+ break;
+
+ case IRP_MN_UNLOCK_ALL_BY_KEY:
+ IrpContext->Icb = Icb;
+ IrpContext->Specific.Lock.Key = Key ;
+ IrpContext->Specific.Lock.ByKey = TRUE ;
+ IrpContext->Specific.Lock.LastLock = &pNpFcb->FileLockList;
+
+ status = UnlockIcbLocks( IrpContext );
+ break;
+ }
+
+ } finally {
+ if ( AbnormalTermination() || !NT_SUCCESS( status ) ) {
+ if ( FileLock != NULL ) {
+ FREE_POOL( FileLock );
+ }
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ }
+ }
+
+ DebugTrace(-1, Dbg, "LockNcb -> %08lx\n", status );
+ return status;
+}
+
+
+
+NTSTATUS
+LockNcpCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ )
+
+/*++
+
+Routine Description:
+
+ This routine receives the response from a user NCP.
+
+Arguments:
+
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+ NTSTATUS Status;
+ PIRP Irp;
+ PIO_STACK_LOCATION irpSp;
+
+ DebugTrace(+1, Dbg, "LockNcpCallback...\n", 0);
+
+ if ( BytesAvailable == 0) {
+
+ //
+ // No response from server. Status is in pIrpContext->
+ // ResponseParameters.Error
+ //
+
+ FREE_POOL( IrpContext->Specific.Lock.FileLock );
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwCompleteRequest( IrpContext, STATUS_REMOTE_NOT_LISTENING );
+
+ DebugTrace(-1, Dbg, "LockNcpCallback -> %08lx\n", STATUS_REMOTE_NOT_LISTENING);
+ return STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ Irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ Status = ParseResponse( IrpContext, Response, BytesAvailable, "N" );
+
+ if (NT_SUCCESS(Status) ) {
+
+ DebugTrace(0, Dbg, "Lock successfully applied\n", 0);
+
+ //
+ // Record this lock in the Icb lock chain
+ //
+
+ AddLockToFcb(
+ IrpContext->Icb->NpFcb,
+ IrpContext->Specific.Lock.FileLock );
+
+ } else if ( Status == STATUS_FILE_LOCK_CONFLICT &&
+ IrpContext->Specific.Lock.Wait ) {
+
+ DebugTrace(0, Dbg, "Lock conflict, adding %08lx to Pending Lock list\n", IrpContext );
+
+ //
+ // The lock conflicts with an existing lock, but the app wants
+ // to wait. Queue the request to the pending lock list and
+ // return, pending.
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ IrpContext->Specific.Lock.Key = 5; // BUGBUG Configurable
+
+ ExInterlockedInsertTailList(
+ &NwPendingLockList,
+ &IrpContext->NextRequest,
+ &NwPendingLockSpinLock );
+
+ Status = STATUS_PENDING;
+
+ DebugTrace(-1, Dbg, "LockNcpCallback -> %08lx\n", Status);
+ return( Status );
+
+ } else {
+
+ //
+ // Status unsuccesful is returned when trying to lock 0 bytes.
+ // Map the error.
+ //
+
+ if ( Status == STATUS_UNSUCCESSFUL ) {
+ Status = STATUS_INVALID_PARAMETER;
+ }
+
+ FREE_POOL( IrpContext->Specific.Lock.FileLock );
+ }
+
+ //
+ // If any locks were pending due to a local lock conflict, try
+ // them now.
+ //
+
+ ReattemptPendingLocks(IrpContext->Icb->NpFcb);
+
+ //
+ // We're done with this request. Dequeue the IRP context from
+ // SCB and complete the request.
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwCompleteRequest( IrpContext, Status );
+
+ DebugTrace(-1, Dbg, "LockNcpCallback -> %08lx\n", Status);
+ return Status;
+
+}
+
+
+NTSTATUS
+UnlockNcpCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ )
+
+/*++
+
+Routine Description:
+
+ This routine receives the response from a user NCP.
+
+Arguments:
+
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+ NTSTATUS Status;
+ PIRP Irp;
+ PIO_STACK_LOCATION irpSp;
+
+ DebugTrace(0, Dbg, "UnlockNcpCallback...\n", 0);
+
+ //
+ // Remove this lock in the Fcb lock chain, regardlesss of the status
+ // of the IO.
+ //
+
+ RemoveLockFromFcb(
+ IrpContext->Icb->NpFcb,
+ IrpContext->Specific.Lock.FileLock );
+
+ FREE_POOL( IrpContext->Specific.Lock.FileLock );
+
+ //
+ // If any locks were pending due to a local lock conflict, try
+ // them now.
+ //
+
+ ReattemptPendingLocks(IrpContext->Icb->NpFcb);
+
+ if ( BytesAvailable == 0) {
+
+ //
+ // No response from server. Status is in pIrpContext->
+ // ResponseParameters.Error
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwCompleteRequest( IrpContext, STATUS_REMOTE_NOT_LISTENING );
+
+ return STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ Irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ Status = ParseResponse( IrpContext, Response, BytesAvailable, "N" );
+
+ if (!NT_SUCCESS( Status )) {
+ Error(
+ EVENT_NWRDR_FAILED_UNLOCK,
+ Status,
+ NULL,
+ 0,
+ 1,
+ IrpContext->pNpScb->ServerName.Buffer );
+ }
+
+ //
+ // We're done with this request. Dequeue the IRP context from
+ // SCB and complete the request.
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwCompleteRequest( IrpContext, Status );
+
+ return STATUS_SUCCESS;
+
+}
+
+BOOLEAN
+LockIsOverlapping(
+ PNONPAGED_FCB pNpFcb,
+ LONG StartFileOffset,
+ ULONG Length
+ )
+/*++
+
+Routine Description:
+
+ This routine tests to see if the requested lock would overlap an
+ existing lock.
+
+ *** This routine must be called at the front of the queue.
+
+Arguments:
+
+ pNpFcb - The FCB of the file being locked.
+
+ StartFileOffset - The first byte in the range to lock.
+
+ Length - The number of bytes to lock.
+
+Return Value:
+
+ TRUE - This lock overlaps an existing lock.
+ FALSE - This lock does not overlap an existing lock.
+
+--*/
+{
+ PLIST_ENTRY ListEntry;
+ PNW_FILE_LOCK pFileLock;
+ LONG EndFileOffset = StartFileOffset + Length - 1;
+
+ PAGED_CODE();
+
+ if ( Length == 0 ) {
+ return( FALSE );
+ }
+
+ for ( ListEntry = pNpFcb->FileLockList.Flink;
+ ListEntry != &pNpFcb->FileLockList;
+ ListEntry = ListEntry->Flink ) {
+
+ pFileLock = CONTAINING_RECORD( ListEntry, NW_FILE_LOCK, ListEntry );
+
+ //
+ // Stop the search if the current lock starts before the potential
+ // new lock ends.
+ //
+
+ if ( pFileLock->StartFileOffset > EndFileOffset ) {
+ break;
+ }
+
+ //
+ // The new lock overlaps if it starts of ends in the middle of
+ // an existing lock.
+ //
+
+ if (( StartFileOffset >= pFileLock->StartFileOffset &&
+ StartFileOffset <= pFileLock->EndFileOffset )
+ ||
+ ( EndFileOffset >= pFileLock->StartFileOffset &&
+ EndFileOffset <= pFileLock->EndFileOffset ) ) {
+
+
+ DebugTrace(0, Dbg, "Lock is overlapping\n", 0);
+ return( TRUE );
+ }
+ }
+
+ DebugTrace(0, Dbg, "Lock is NOT overlapping\n", 0);
+ return( FALSE );
+}
+
+VOID
+AddLockToFcb(
+ PNONPAGED_FCB pNpFcb,
+ PNW_FILE_LOCK FileLock
+ )
+/*++
+
+Routine Description:
+
+ This routine inserts a lock structure into the ordered list of locks
+ for this ICB.
+
+ *** This routine must be called when at the front of the ScbQueue.
+
+Arguments:
+
+ NpFcb - The non paged FCB of file that is being locked.
+
+ FileLock - The file lock structure to insert.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PLIST_ENTRY ListEntry;
+ PNW_FILE_LOCK pFileLock;
+
+ LONG StartFileOffset = FileLock->StartFileOffset;
+ LONG EndFileOffset = FileLock->EndFileOffset;
+
+ DebugTrace(0, Dbg, "Adding Lock to FCB %08lx\n", pNpFcb);
+ DebugTrace(0, Dbg, "Lock is %08lx\n", FileLock );
+
+ if ( IsListEmpty( &pNpFcb->FileLockList ) ) {
+ InsertHeadList( &pNpFcb->FileLockList, &FileLock->ListEntry );
+ return;
+ }
+
+ for ( ListEntry = pNpFcb->FileLockList.Flink;
+ ListEntry != &pNpFcb->FileLockList;
+ ListEntry = ListEntry->Flink ) {
+
+ pFileLock = CONTAINING_RECORD( ListEntry, NW_FILE_LOCK, ListEntry );
+
+ //
+ // Stop the search if the current lock starts after the
+ // new lock ends.
+ //
+
+ if ( pFileLock->StartFileOffset > EndFileOffset ) {
+ break;
+ }
+
+ }
+
+ //
+ // Insert the file lock into the ordered list.
+ //
+
+ InsertTailList( ListEntry, &FileLock->ListEntry );
+}
+
+
+VOID
+RemoveLockFromFcb(
+ PNONPAGED_FCB pNpFcb,
+ PNW_FILE_LOCK FileLock
+ )
+/*++
+
+Routine Description:
+
+ This routine removes a lock structure from the ordered list of locks
+ for this FCB.
+
+ *** This routine must be called when at the front of the ScbQueue.
+
+Arguments:
+
+ pNpFcb - The non paged FCB of file that is being unlocked.
+
+ FileLock - The file lock structure to remove.
+
+Return Value:
+
+ None.
+
+--*/
+{
+#if DBG
+ PNW_FILE_LOCK foundFileLock;
+#endif
+
+ DebugTrace(0, Dbg, "Removing Lock from FCB %08lx\n", pNpFcb);
+ DebugTrace(0, Dbg, "Lock is %08lx\n", FileLock );
+
+ ASSERT( LockExists( pNpFcb, FileLock->StartFileOffset, FileLock->Length, &foundFileLock ) );
+ ASSERT( foundFileLock == FileLock );
+
+ RemoveEntryList( &FileLock->ListEntry );
+ return;
+}
+
+
+VOID
+ReattemptPendingLocks(
+ PNONPAGED_FCB pNpFcb
+ )
+/*++
+
+Routine Description:
+
+ This routine reattempts locks that are pending due to a local lock
+ conflict.
+
+ *** This routine must be called when at the front of the ScbQueue.
+
+Arguments:
+
+ pNpFcb - The non paged FCB of file that is being processed.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PLIST_ENTRY listEntry, nextListEntry;
+ PNW_FILE_LOCK fileLock;
+ NTSTATUS status;
+
+ DebugTrace(+1, Dbg, "ReattemptPendingLocks...\n", 0);
+
+ //
+ // Run the list of pending locks.
+ //
+
+ for ( listEntry = pNpFcb->PendingLockList.Flink;
+ listEntry != &pNpFcb->PendingLockList;
+ listEntry = nextListEntry ) {
+
+ nextListEntry = listEntry->Flink;
+
+ fileLock = CONTAINING_RECORD( listEntry, NW_FILE_LOCK, ListEntry );
+
+ if ( !LockIsOverlapping( pNpFcb, fileLock->StartFileOffset, fileLock->Length ) ) {
+
+ //
+ // It is now safe to try this lock.
+ //
+
+ RemoveEntryList( listEntry );
+
+ DebugTrace(0, Dbg, "Reattempt lock %08lx\n", fileLock->IrpContext);
+
+ status = Exchange (
+ fileLock->IrpContext,
+ LockNcpCallback,
+ "Fbrddw",
+ NCP_LOCK_RANGE,
+ fileLock->Flags | 0x01, // BUGBUG
+ fileLock->Icb->Handle, sizeof( fileLock->Icb->Handle ),
+ fileLock->StartFileOffset,
+ fileLock->Length,
+ LockTimeoutThreshold );
+
+ if ( !NT_SUCCESS( status ) ) {
+
+ NwDequeueIrpContext( fileLock->IrpContext, FALSE );
+ NwCompleteRequest( fileLock->IrpContext, status );
+
+ FREE_POOL( fileLock );
+
+ } else if ( status == STATUS_PENDING ) {
+ DebugTrace(-1, Dbg, "ReattemptPendingLocks\n", 0);
+ return;
+ }
+ }
+
+ }
+
+ DebugTrace(-1, Dbg, "ReattemptPendingLocks\n", 0);
+ return;
+}
+
+
+BOOLEAN
+LockExists(
+ PNONPAGED_FCB pNpFcb,
+ LONG StartOffset,
+ ULONG Length,
+ PNW_FILE_LOCK *FileLock
+ )
+/*++
+
+Routine Description:
+
+ This routine test whether or not a lock is owned for this ICB.
+
+ *** This routine must be called when at the front of the ScbQueue.
+
+Arguments:
+
+ pNpFcb - The non paged FCB of file that is being locked.
+
+ StartOffset - The starting file offset of the lock.
+
+ Length - The number of bytes to lock.
+
+ FileLock - Returns a pointer to the FileLock structure if it was found.
+
+Return Value:
+
+ TRUE - This lock is being held for this ICB.
+ FALSE - This lock is NOT being held for this ICB.
+
+--*/
+{
+ PLIST_ENTRY ListEntry;
+ PNW_FILE_LOCK pFileLock;
+ LONG EndOffset = StartOffset + Length - 1;
+
+ for ( ListEntry = pNpFcb->FileLockList.Flink;
+ ListEntry != &pNpFcb->FileLockList;
+ ListEntry = ListEntry->Flink ) {
+
+ pFileLock = CONTAINING_RECORD( ListEntry, NW_FILE_LOCK, ListEntry );
+
+ //
+ // Search for the lock that exactly matches this one.
+ //
+
+ if ( pFileLock->StartFileOffset == StartOffset &&
+ pFileLock->EndFileOffset == EndOffset ) {
+
+ *FileLock = pFileLock;
+ DebugTrace(0, Dbg, "Found lock\n", 0);
+ return( TRUE );
+ }
+
+ }
+
+ *FileLock = NULL;
+
+ DebugTrace(0, Dbg, "Could not find lock\n", 0);
+ return( FALSE );
+}
+
+NTSTATUS
+UnlockIcbLocks(
+ PIRP_CONTEXT pIrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine unlocks the first lock for an ICB.
+
+ *** This routine must be called when at the front of the ScbQueue.
+
+Arguments:
+
+ IrpContext - A pointer to the IRP context pointers for this request.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PICB pIcb;
+ PNW_FILE_LOCK pFileLock;
+ PLIST_ENTRY LastLockEntry;
+ NTSTATUS Status;
+ PNONPAGED_FCB pNpFcb;
+
+ DebugTrace(+1, Dbg, "UnlockIcbLocks...\n", 0);
+
+ pIcb = pIrpContext->Icb;
+ pNpFcb = pIcb->NpFcb;
+
+ LastLockEntry = pIrpContext->Specific.Lock.LastLock;
+
+ if ( LastLockEntry->Flink == &pNpFcb->FileLockList ) {
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ NwCompleteRequest( pIrpContext, STATUS_SUCCESS );
+
+ DebugTrace(-1, Dbg, "UnlockIcbLocks -> %08lx\n", 0);
+ return STATUS_PENDING;
+ }
+
+ pFileLock = CONTAINING_RECORD( LastLockEntry->Flink, NW_FILE_LOCK, ListEntry );
+
+ if ( pIrpContext->Specific.Lock.ByKey ) {
+
+ //
+ // Doing an unlock by key, skip locks that don't have a matching key.
+ //
+
+ while ( pFileLock->Key != pIrpContext->Specific.Lock.Key ) {
+
+ if ( pFileLock->ListEntry.Flink == &pNpFcb->FileLockList ) {
+
+ //
+ // FIXFIX should we return STATUS_RANGE_NOT_LOCKED if there were no matches
+ // at all?
+ //
+
+ DebugTrace(-1, Dbg, "UnlockIcbLocks -> %08lx\n", STATUS_SUCCESS);
+
+ return( STATUS_SUCCESS );
+ }
+
+ pIrpContext->Specific.Lock.LastLock = &pFileLock->ListEntry;
+ pFileLock = CONTAINING_RECORD( &pFileLock->ListEntry, NW_FILE_LOCK, ListEntry );
+ }
+
+ // We have a locked range. Proceed to unlock this one and any others.
+
+ }
+
+ RemoveEntryList( &pFileLock->ListEntry );
+
+ Status = Exchange (
+ pIrpContext,
+ UnlockIcbLocksCallback,
+ "F-rddw",
+ NCP_UNLOCK_RANGE,
+ pIcb->Handle, sizeof( pIcb->Handle ),
+ pFileLock->StartFileOffset,
+ pFileLock->Length,
+ 1 );
+
+ FREE_POOL( pFileLock );
+
+ DebugTrace(-1, Dbg, "UnlockIcbLocks -> %08lx\n", Status);
+ return Status;
+}
+
+
+NTSTATUS
+UnlockIcbLocksCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ )
+
+/*++
+
+Routine Description:
+
+ This routine receives the response from a user NCP.
+
+Arguments:
+
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+ NTSTATUS Status;
+ PIRP Irp;
+ PIO_STACK_LOCATION irpSp;
+
+ DebugTrace(0, Dbg, "LockNcpCallback...\n", 0);
+
+ if ( BytesAvailable == 0) {
+
+ //
+ // No response from server. Status is in pIrpContext->
+ // ResponseParameters.Error
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwCompleteRequest( IrpContext, STATUS_REMOTE_NOT_LISTENING );
+
+ return STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ Irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ //
+ // Ignore the response, plod ahead.
+ //
+
+ Status = UnlockIcbLocks( IrpContext );
+
+ return Status;
+}
+
+
+
+VOID
+NwFreeLocksForIcb(
+ IN PIRP_CONTEXT pIrpContext,
+ PICB Icb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine unlocks all locks held for a specific ICB.
+
+ Because its only called from Cleanup prior to a close we can
+ simply free the internal structures. The server will clear the
+ locks on the handle when it gets the close.
+
+Arguments:
+
+ ICB - The ICB to free the locks for.
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+ PLIST_ENTRY listEntry, nextListEntry;
+ PNW_FILE_LOCK pFileLock;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwFreeLockForIcb...\n", 0);
+
+ NwAppendToQueueAndWait( pIrpContext );
+
+ for ( listEntry = Icb->NpFcb->FileLockList.Flink;
+ listEntry != &Icb->NpFcb->FileLockList;
+ listEntry = nextListEntry ) {
+
+ nextListEntry = listEntry->Flink;
+
+ pFileLock = CONTAINING_RECORD(
+ listEntry,
+ NW_FILE_LOCK,
+ ListEntry );
+
+ if ( pFileLock->Icb == Icb ) {
+
+ RemoveEntryList( listEntry );
+ FREE_POOL( pFileLock );
+
+ DebugTrace( 0, Dbg, "Freed lock %08lx\n", pFileLock );
+ }
+
+ }
+
+ ReattemptPendingLocks( Icb->NpFcb );
+
+ DebugTrace(-1, Dbg, "NwFreeLockForIcb -> VOID\n", 0);
+
+}
+
diff --git a/private/nw/rdr/lockcode.c b/private/nw/rdr/lockcode.c
new file mode 100644
index 000000000..1a2941c2d
--- /dev/null
+++ b/private/nw/rdr/lockcode.c
@@ -0,0 +1,168 @@
+
+/*++
+
+Copyright (c) 1994 Microsoft Corporation
+
+Module Name:
+
+ lockcode.c
+
+Abstract:
+
+Author:
+
+ Chuck Lenzmeier (chuckl) 30-Jan-1994
+ Manny Weiser (mannyw) 17-May-1994
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+
+
+#ifndef QFE_BUILD
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwReferenceUnlockableCodeSection )
+#pragma alloc_text( PAGE, NwDereferenceUnlockableCodeSection )
+#endif
+
+extern BOOLEAN TimerStop; // From Timer.c
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_CREATE)
+
+
+VOID
+NwReferenceUnlockableCodeSection (
+ VOID
+ )
+{
+ ULONG oldCount;
+
+ //
+ // Lock the lockable code database.
+ //
+
+ ExAcquireResourceExclusive( &NwUnlockableCodeResource, TRUE );
+
+ //
+ // Increment the reference count for the section.
+ //
+
+ oldCount = NwSectionDescriptor.ReferenceCount++;
+
+ if ( oldCount == 0 && NwSectionDescriptor.Handle == NULL ) {
+
+ //
+ // This is the first reference to the section. Start the timer.
+ // Lock our code.
+ //
+
+ NwSectionDescriptor.Handle = MmLockPagableCodeSection( NwSectionDescriptor.Base );
+ StartTimer( );
+
+ } else {
+
+ //
+ // This is not the first reference to the section. The section
+ // had better be locked!
+ //
+
+ ASSERT( NwSectionDescriptor.Handle != NULL );
+
+ //
+ // Restart the timer if the rdr was stopped but didn't unload.
+ //
+
+ if (TimerStop == TRUE) {
+ StartTimer();
+ }
+
+ }
+
+ DebugTrace(+0, Dbg, "NwReferenceCodeSection %d\n", NwSectionDescriptor.ReferenceCount );
+
+ ExReleaseResource( &NwUnlockableCodeResource );
+
+ return;
+
+} // NwReferenceUnlockableCodeSection
+
+
+VOID
+NwDereferenceUnlockableCodeSection (
+ VOID
+ )
+{
+ ULONG newCount;
+
+ //
+ // Lock the lockable code database.
+ //
+
+ ExAcquireResourceExclusive( &NwUnlockableCodeResource, TRUE );
+
+ ASSERT( NwSectionDescriptor.Handle != NULL );
+ ASSERT( NwSectionDescriptor.ReferenceCount > 0 &&
+ NwSectionDescriptor.ReferenceCount < 0x7FFF );
+
+ //
+ // Decrement the reference count for the section.
+ //
+
+ newCount = --NwSectionDescriptor.ReferenceCount;
+
+ DebugTrace(+0, Dbg, "NwDereferenceCodeSection %d\n", NwSectionDescriptor.ReferenceCount );
+
+ ExReleaseResource( &NwUnlockableCodeResource );
+
+ return;
+
+} // NwDereferenceUnlockableCodeSection
+
+BOOLEAN
+NwUnlockCodeSections(
+ IN BOOLEAN BlockIndefinitely
+ )
+{
+ //
+ // Lock the lockable code database.
+ //
+
+ if (!ExAcquireResourceExclusive( &NwUnlockableCodeResource, BlockIndefinitely )) {
+ return FALSE; // Avoid potential deadlock in timer.c
+ }
+
+ DebugTrace(+0, Dbg, "NwUnlockCodeSections %d\n", NwSectionDescriptor.ReferenceCount );
+
+ if ( NwSectionDescriptor.ReferenceCount == 0 ) {
+
+ if ( NwSectionDescriptor.Handle != NULL ) {
+
+ //
+ // This is the last reference to the section. Stop the timer and
+ // unlock the code.
+ //
+
+ StopTimer();
+
+ MmUnlockPagableImageSection( NwSectionDescriptor.Handle );
+ NwSectionDescriptor.Handle = NULL;
+
+ }
+
+ ExReleaseResource( &NwUnlockableCodeResource );
+ return TRUE;
+ }
+
+ ExReleaseResource( &NwUnlockableCodeResource );
+ return FALSE;
+
+}
+
+#endif
diff --git a/private/nw/rdr/makefile b/private/nw/rdr/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/nw/rdr/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/rdr/ndsfsctl.c b/private/nw/rdr/ndsfsctl.c
new file mode 100644
index 000000000..b60c98d8f
--- /dev/null
+++ b/private/nw/rdr/ndsfsctl.c
@@ -0,0 +1,2128 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ NdsFsctl.c
+
+Abstract:
+
+ This implements the NDS user mode hooks to the redirector.
+
+Author:
+
+ Cory West [CoryWest] 23-Feb-1995
+
+--*/
+
+#include "Procs.h"
+
+#define Dbg (DEBUG_TRACE_NDS)
+
+#pragma alloc_text( PAGE, DispatchNds )
+#pragma alloc_text( PAGE, PrepareLockedBufferFromFsd )
+#pragma alloc_text( PAGE, DoBrowseFsctl )
+#pragma alloc_text( PAGE, NdsRawFragex )
+#pragma alloc_text( PAGE, NdsResolveName )
+#pragma alloc_text( PAGE, NdsGetObjectInfo )
+#pragma alloc_text( PAGE, NdsListSubordinates )
+#pragma alloc_text( PAGE, NdsReadAttributes )
+#pragma alloc_text( PAGE, NdsGetVolumeInformation )
+#pragma alloc_text( PAGE, NdsOpenStream )
+#pragma alloc_text( PAGE, NdsSetContext )
+#pragma alloc_text( PAGE, NdsGetContext )
+#pragma alloc_text( PAGE, NdsVerifyTreeHandle )
+#pragma alloc_text( PAGE, NdsGetPrintQueueInfo )
+#pragma alloc_text( PAGE, NdsChangePass )
+#pragma alloc_text( PAGE, NdsListTrees )
+
+//
+// The main handler for all NDS FSCTL calls.
+//
+
+NTSTATUS
+DispatchNds(
+ ULONG IoctlCode,
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine instigates an NDS transaction requested from
+ the fsctl interface.
+
+Arguments:
+
+ IoctlCode - Supplies the code to be used for the NDS transaction.
+ IrpContext - A pointer to IRP context information for this request.
+
+Return Value:
+
+ Status of transaction.
+
+--*/
+{
+ NTSTATUS Status = STATUS_NOT_SUPPORTED;
+ SECURITY_SUBJECT_CONTEXT SubjectContext;
+ LARGE_INTEGER Uid;
+
+ PAGED_CODE();
+
+ //
+ // Always set the user uid in the irp context so that
+ // referral creates NEVER go astray.
+ //
+
+ SeCaptureSubjectContext(&SubjectContext);
+ Uid = GetUid( &SubjectContext );
+ SeReleaseSubjectContext(&SubjectContext);
+
+ IrpContext->Specific.Create.UserUid.QuadPart = Uid.QuadPart;
+
+ switch ( IoctlCode ) {
+
+ //
+ // These calls do not require us to lock down
+ // the user's buffer, but they do generate wire
+ // traffic.
+ //
+
+ case FSCTL_NWR_NDS_SETCONTEXT:
+ DebugTrace( 0, Dbg, "DispatchNds: Set Context\n", 0 );
+ return DoBrowseFsctl( IrpContext, IoctlCode, FALSE );
+
+ case FSCTL_NWR_NDS_GETCONTEXT:
+ DebugTrace( 0, Dbg, "DispatchNds: Get Context\n", 0 );
+ return DoBrowseFsctl( IrpContext, IoctlCode, FALSE );
+
+ case FSCTL_NWR_NDS_OPEN_STREAM:
+ DebugTrace( 0, Dbg, "DispatchNds: Open Stream\n", 0 );
+ return DoBrowseFsctl( IrpContext, IoctlCode, FALSE );
+
+ case FSCTL_NWR_NDS_VERIFY_TREE:
+ DebugTrace( 0, Dbg, "DispatchNds: Verify Tree\n", 0 );
+ return DoBrowseFsctl( IrpContext, IoctlCode, FALSE );
+
+ case FSCTL_NWR_NDS_GET_QUEUE_INFO:
+ DebugTrace( 0, Dbg, "DispatchNds: Get Queue Info\n", 0 );
+ return DoBrowseFsctl( IrpContext, IoctlCode, FALSE );
+
+ case FSCTL_NWR_NDS_GET_VOLUME_INFO:
+ DebugTrace( 0, Dbg, "DispatchNds: Get Volume Info\n", 0 );
+ return DoBrowseFsctl( IrpContext, IoctlCode, FALSE );
+
+ //
+ // These four fsctl calls are the basis of browsing. They
+ // all require a request packet and a user buffer that we
+ // lock down.
+ //
+
+ case FSCTL_NWR_NDS_RESOLVE_NAME:
+ DebugTrace( 0, Dbg, "DispatchNds: Resolve Name\n", 0 );
+ return DoBrowseFsctl( IrpContext, IoctlCode, TRUE );
+
+ case FSCTL_NWR_NDS_LIST_SUBS:
+ DebugTrace( 0, Dbg, "DispatchNds: List Subordinates\n", 0 );
+ return DoBrowseFsctl( IrpContext, IoctlCode, TRUE );
+
+ case FSCTL_NWR_NDS_READ_INFO:
+ DebugTrace( 0, Dbg, "DispatchNds: Read Object Info\n", 0 );
+ return DoBrowseFsctl( IrpContext, IoctlCode, TRUE );
+
+ case FSCTL_NWR_NDS_READ_ATTR:
+ DebugTrace( 0, Dbg, "DispatchNds: Read Attribute\n", 0 );
+ return DoBrowseFsctl( IrpContext, IoctlCode, TRUE );
+
+ //
+ // Support for user mode fragment exchange.
+ //
+
+ case FSCTL_NWR_NDS_RAW_FRAGEX:
+ DebugTrace( 0, Dbg, "DispatchNds: Raw Fragex\n", 0 );
+ return NdsRawFragex( IrpContext );
+
+ //
+ // Change an NDS password.
+ //
+
+ case FSCTL_NWR_NDS_CHANGE_PASS:
+ DebugTrace( 0, Dbg, "DispatchNds: Change Password\n", 0 );
+ return NdsChangePass( IrpContext );
+
+ //
+ // Special fsctl to list the trees that a particular nt user
+ // has credentials to since the change pass ui runs under the
+ // system luid. Sigh.
+ //
+
+ case FSCTL_NWR_NDS_LIST_TREES:
+ DebugTrace( 0, Dbg, "DispatchNds: List trees\n", 0 );
+ return NdsListTrees( IrpContext );
+
+ default:
+
+ DebugTrace( 0, Dbg, "DispatchNds: No Such IOCTL\n", 0 );
+ break;
+
+ }
+
+ DebugTrace( 0, Dbg, " -> %08lx\n", Status );
+ return Status;
+
+}
+
+NTSTATUS
+PrepareLockedBufferFromFsd(
+ PIRP_CONTEXT pIrpContext,
+ PLOCKED_BUFFER pLockedBuffer
+)
+/*
+
+Description:
+
+ This routine takes the irp context for an FSD request with
+ a user mode buffer, and locks down the buffer so that it may
+ be sent to the transport. The locked down buffer, in addition
+ to being described in the irp and irp context, is described
+ in the LOCKED_BUFFER structure.
+
+Arguments:
+
+ pIrpContext - irp context for this request
+ pLockedBuffer - the locked response buffer
+
+*/
+{
+
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+
+ PVOID OutputBuffer;
+ ULONG OutputBufferLength;
+
+ PAGED_CODE();
+
+ //
+ // Get the irp and input buffer information and lock the
+ // buffer to the irp.
+ //
+
+ irp = pIrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+ OutputBufferLength = irpSp->Parameters.FileSystemControl.OutputBufferLength;
+
+ if ( !OutputBufferLength ) {
+
+ DebugTrace( 0, Dbg, "No fsd buffer length in PrepareLockedBufferFromFsd...\n", 0 );
+ return STATUS_BUFFER_TOO_SMALL;
+
+ }
+
+ NwLockUserBuffer( irp, IoWriteAccess, OutputBufferLength );
+ NwMapUserBuffer( irp, irp->RequestorMode, (PVOID *)&OutputBuffer );
+
+ if ( !OutputBuffer ) {
+
+ DebugTrace( 0, Dbg, "No fsd buffer in PrepareLockedBufferFromFsd...\n", 0 );
+ return STATUS_BUFFER_TOO_SMALL;
+
+ }
+
+ //
+ // Update the original MDL record in the Irp context, since
+ // NwLockUserBuffer may have created a new MDL.
+ //
+
+ pIrpContext->pOriginalMdlAddress = irp->MdlAddress;
+
+ //
+ // Fill in our locked buffer description.
+ //
+
+ pLockedBuffer->pRecvBufferVa = MmGetMdlVirtualAddress( irp->MdlAddress );
+ pLockedBuffer->dwRecvLen = MdlLength( irp->MdlAddress );
+ pLockedBuffer->pRecvMdl = irp->MdlAddress;
+
+ // DebugTrace( 0, Dbg, "Locked fsd buffer at %08lx\n", pLockedBuffer->pRecvBufferVa );
+ // DebugTrace( 0, Dbg, " len -> %d\n", pLockedBuffer->dwRecvLen );
+ // DebugTrace( 0, Dbg, " recv mdl at %08lx\n", pLockedBuffer->pRecvMdl );
+
+ return STATUS_SUCCESS;
+
+}
+
+NTSTATUS
+DoBrowseFsctl( PIRP_CONTEXT pIrpContext,
+ ULONG IoctlCode,
+ BOOL LockdownBuffer
+)
+/*+++
+
+Description:
+
+ This actually sets up for an NDS operation that requires wire
+ traffic, including locking down the user buffer if necessary.
+
+Arguments:
+
+ pIrpContext - the irp context for this request
+ IoctlCode - the ioctl requested
+ LockdownBuffer - do we need to lock down the user buffer
+
+---*/
+{
+
+ NTSTATUS Status;
+
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+
+ PNWR_NDS_REQUEST_PACKET InputBuffer;
+ ULONG InputBufferLength;
+
+ PVOID fsContext, fsObject;
+ NODE_TYPE_CODE nodeTypeCode;
+ PSCB pScb = NULL;
+ PICB pIcb = NULL;
+
+ PVOID OutputBuffer;
+ ULONG OutputBufferLength;
+
+ LOCKED_BUFFER LockedBuffer;
+
+ PAGED_CODE();
+
+ //
+ // Get the request packet in the input buffer.
+ //
+
+ irp = pIrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+ InputBuffer = (PNWR_NDS_REQUEST_PACKET) irpSp->Parameters.FileSystemControl.Type3InputBuffer;
+ InputBufferLength = irpSp->Parameters.FileSystemControl.InputBufferLength;
+
+ if ( !InputBuffer ||
+ !InputBufferLength ) {
+
+ DebugTrace( 0, Dbg, "BrowseFsctl has no input buffer...\n", 0 );
+ return STATUS_BUFFER_TOO_SMALL;
+ }
+
+ //
+ // Decode the file object and point the irp context the
+ // the appropriate connection... Should this be in an
+ // exception frame?
+ //
+
+ nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
+ &fsContext,
+ &fsObject );
+
+ if ( nodeTypeCode == NW_NTC_ICB_SCB ) {
+
+ pIcb = (PICB) fsObject;
+ pScb = (pIcb->SuperType).Scb;
+
+ pIrpContext->pScb = pScb;
+ pIrpContext->pNpScb = pIrpContext->pScb->pNpScb;
+ pIrpContext->Icb = pIcb;
+
+ }
+
+ //
+ // Lock the users buffer if this destined for the transport.
+ //
+
+ if ( LockdownBuffer &&
+ nodeTypeCode == NW_NTC_ICB_SCB ) {
+
+ Status = PrepareLockedBufferFromFsd( pIrpContext, &LockedBuffer );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ //
+ // Call the appropriate browser.
+ //
+
+ switch ( IoctlCode ) {
+
+ case FSCTL_NWR_NDS_RESOLVE_NAME:
+
+ return NdsResolveName( pIrpContext, InputBuffer, &LockedBuffer );
+
+ case FSCTL_NWR_NDS_LIST_SUBS:
+
+ return NdsListSubordinates( pIrpContext, InputBuffer, &LockedBuffer );
+
+ case FSCTL_NWR_NDS_READ_INFO:
+
+ return NdsGetObjectInfo( pIrpContext, InputBuffer, &LockedBuffer );
+
+ case FSCTL_NWR_NDS_READ_ATTR:
+
+ return NdsReadAttributes( pIrpContext, InputBuffer, &LockedBuffer );
+
+ default:
+
+ DebugTrace( 0, Dbg, "Invalid ioctl for locked BrowseFsctl...\n", 0 );
+ return STATUS_NOT_SUPPORTED;
+
+ }
+
+ }
+
+ //
+ // There's no user reply buffer for these calls, hence there's no lockdown.
+ //
+
+ switch ( IoctlCode ) {
+
+ case FSCTL_NWR_NDS_OPEN_STREAM:
+
+ //
+ // There has to be an ICB for this!
+ //
+
+ if ( nodeTypeCode != NW_NTC_ICB_SCB ) {
+ return STATUS_INVALID_HANDLE;
+ }
+
+ return NdsOpenStream( pIrpContext, InputBuffer );
+
+ case FSCTL_NWR_NDS_SETCONTEXT:
+
+ return NdsSetContext( pIrpContext, InputBuffer );
+
+ case FSCTL_NWR_NDS_GETCONTEXT:
+
+ return NdsGetContext( pIrpContext, InputBuffer );
+
+ case FSCTL_NWR_NDS_VERIFY_TREE:
+
+ //
+ // Verify that this handle is valid for the specified tree.
+ //
+
+ return NdsVerifyTreeHandle( pIrpContext, InputBuffer );
+
+ case FSCTL_NWR_NDS_GET_QUEUE_INFO:
+
+ //
+ // Get the queue info for this print queue.
+ //
+
+ return NdsGetPrintQueueInfo( pIrpContext, InputBuffer );
+
+ case FSCTL_NWR_NDS_GET_VOLUME_INFO:
+
+ //
+ // Get the volume info for this volume object.
+ // For the new shell property sheets.
+ //
+
+ return NdsGetVolumeInformation( pIrpContext, InputBuffer );
+
+ }
+
+ //
+ // All others are not supported.
+ //
+
+ return STATUS_NOT_SUPPORTED;
+}
+
+NTSTATUS
+NdsRawFragex(
+ PIRP_CONTEXT pIrpContext
+)
+/*+++
+
+ Send a raw user requested fragment.
+
+---*/
+{
+
+ NTSTATUS Status;
+
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ NODE_TYPE_CODE nodeTypeCode;
+ PVOID fsContext, fsObject;
+ PSCB pScb = NULL;
+ PICB pIcb = NULL;
+
+ DWORD NdsVerb;
+ LOCKED_BUFFER NdsRequest;
+
+ PNWR_NDS_REQUEST_PACKET Rrp;
+ PBYTE RawRequest;
+ DWORD RawRequestLen;
+
+ PAGED_CODE();
+
+ //
+ // Get the request.
+ //
+
+ irp = pIrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+ Rrp = ( PNWR_NDS_REQUEST_PACKET ) irpSp->Parameters.FileSystemControl.Type3InputBuffer;
+ RawRequestLen = irpSp->Parameters.FileSystemControl.InputBufferLength;
+
+ if ( !Rrp || ( RawRequestLen < sizeof( NWR_NDS_REQUEST_PACKET ) ) ) {
+
+ DebugTrace( 0, Dbg, "No raw request buffer.\n", 0 );
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Decode the file object and point the irp context
+ // to the appropriate connection.
+ //
+
+ nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
+ &fsContext,
+ &fsObject );
+
+ if ( nodeTypeCode != NW_NTC_ICB_SCB ) {
+
+ DebugTrace( 0, Dbg, "A raw fragment request requires a server handle.\n", 0 );
+ return STATUS_INVALID_HANDLE;
+ }
+
+ pIcb = (PICB) fsObject;
+ pScb = (pIcb->SuperType).Scb;
+
+ pIrpContext->pScb = pScb;
+ pIrpContext->pNpScb = pIrpContext->pScb->pNpScb;
+ pIrpContext->Icb = pIcb;
+
+ //
+ // Dig out the parameters.
+ //
+
+ NdsVerb = Rrp->Parameters.RawRequest.NdsVerb;
+ RawRequestLen = Rrp->Parameters.RawRequest.RequestLength;
+ RawRequest = &Rrp->Parameters.RawRequest.Request[0];
+
+ //
+ // Get the reply buffer all locked in for the fragex.
+ //
+
+ Status = PrepareLockedBufferFromFsd( pIrpContext, &NdsRequest );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ try {
+
+ if ( RawRequestLen ) {
+
+ Status = FragExWithWait( pIrpContext,
+ NdsVerb,
+ &NdsRequest,
+ "r",
+ RawRequest,
+ RawRequestLen );
+ } else {
+
+ Status = FragExWithWait( pIrpContext,
+ NdsVerb,
+ &NdsRequest,
+ NULL );
+ }
+
+ if ( NT_SUCCESS( Status ) ) {
+ Rrp->Parameters.RawRequest.ReplyLength = NdsRequest.dwBytesWritten;
+ }
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ Status = GetExceptionCode();
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+NdsResolveName(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest,
+ PLOCKED_BUFFER pLockedBuffer
+)
+/*+++
+
+Description:
+
+ This function decodes the resolve name request and makes the
+ actual wire request.
+
+Parameters:
+
+ pIrpContext - describes the irp for this request
+ pLockedBuffer - describes the locked, user mode buffer that we will
+ write the response into
+ pNdsRequest - the request parameters
+
+Return Value:
+
+ The status of the exchange.
+
+---*/
+{
+ NTSTATUS Status;
+ UNICODE_STRING uObjectName;
+ DWORD dwResolverFlags;
+ WCHAR ObjectName[MAX_NDS_NAME_CHARS];
+
+ PNDS_WIRE_RESPONSE_RESOLVE_NAME pWireResponse;
+ PNDS_WIRE_RESPONSE_RESOLVE_NAME_REFERRAL pReferral;
+ PNDS_RESPONSE_RESOLVE_NAME pUserResponse;
+ IPXaddress *ReferredAddress;
+ PSCB Scb, OldScb;
+
+ PAGED_CODE();
+
+ //
+ // Fill in the resolver flags and the unicode string for the
+ // object name from the request packet.
+ //
+
+ try {
+
+ uObjectName.Length = (USHORT)(pNdsRequest->Parameters).ResolveName.ObjectNameLength;
+ uObjectName.MaximumLength = sizeof( ObjectName );
+
+ if ( uObjectName.Length > sizeof( ObjectName ) ) {
+ ExRaiseStatus( STATUS_INVALID_BUFFER_SIZE );
+ }
+
+ RtlCopyMemory( ObjectName,
+ (pNdsRequest->Parameters).ResolveName.ObjectName,
+ uObjectName.Length );
+
+ uObjectName.Buffer = ObjectName;
+
+ dwResolverFlags = (pNdsRequest->Parameters).ResolveName.ResolverFlags;
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Bad user mode buffer in resolving name.\n", 0 );
+ return GetExceptionCode();
+ }
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_RESOLVE_NAME,
+ pLockedBuffer,
+ "DDDSDDDD",
+ 0, // version
+ dwResolverFlags, // flags
+ 0, // scope
+ &uObjectName, // distinguished name
+ 1,0, // transport type
+ 1,0 ); // treeWalker type
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ Status = NdsCompletionCodetoNtStatus( pLockedBuffer );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ //
+ // We need to convert the NDS_WIRE_RESPONSE_RESOLVE_NAME that
+ // we got from the server into an NDS_RESPONSE_RESOLVE_NAME
+ // for more general consumption. Notice that a referral packet
+ // has an additional DWORD in it - what a pain.
+ //
+
+ pWireResponse = (PNDS_WIRE_RESPONSE_RESOLVE_NAME) pLockedBuffer->pRecvBufferVa;
+ pReferral = (PNDS_WIRE_RESPONSE_RESOLVE_NAME_REFERRAL) pLockedBuffer->pRecvBufferVa;
+ pUserResponse = (PNDS_RESPONSE_RESOLVE_NAME) pLockedBuffer->pRecvBufferVa;
+
+ try {
+
+ if ( pWireResponse->RemoteEntry == RESOLVE_NAME_ACCEPT_REMOTE ) {
+
+ //
+ // This server can handle this request.
+ //
+
+ pUserResponse->ServerNameLength = 0;
+ (pNdsRequest->Parameters).ResolveName.BytesWritten = 4 * sizeof( DWORD );
+
+ } else {
+
+ ASSERT( pWireResponse->RemoteEntry == RESOLVE_NAME_REFER_REMOTE );
+
+ ASSERT( pReferral->ServerAddresses == 1 );
+ ASSERT( pReferral->AddressType == 0 );
+ ASSERT( pReferral->AddressLength == sizeof( IPXaddress ) );
+
+ //
+ // We've been referred to another server. We have to connect
+ // to the referred server to get the name for the caller.
+ //
+
+ ReferredAddress = (IPXaddress *) pReferral->Address;
+
+ OldScb = pIrpContext->pScb;
+
+ //
+ // Dequeue us from our original server. Do not defer the
+ // logon at this point since a referral means we're in the
+ // middle of a browse operation.
+ //
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ Status = CreateScb( &Scb,
+ pIrpContext,
+ NULL,
+ ReferredAddress,
+ NULL,
+ NULL,
+ TRUE,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ RtlCopyMemory( pUserResponse->ReferredServer,
+ Scb->pNpScb->ServerName.Buffer,
+ Scb->pNpScb->ServerName.Length );
+
+ pUserResponse->ServerNameLength = Scb->pNpScb->ServerName.Length;
+ (pNdsRequest->Parameters).ResolveName.BytesWritten =
+ ( 4 * sizeof( DWORD ) ) + Scb->pNpScb->ServerName.Length;
+
+ DebugTrace( 0, Dbg, "Resolve name referral to: %wZ\n",
+ &Scb->pNpScb->ServerName );
+
+ //
+ // Restore the server pointers, we're not ready to jump
+ // servers yet since this might be a request from the fsd.
+ //
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ NwDereferenceScb( Scb->pNpScb );
+ pIrpContext->pScb = OldScb;
+ pIrpContext->pNpScb = OldScb->pNpScb;
+
+ }
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Bad user mode buffer in resolving name.\n", 0 );
+ return GetExceptionCode();
+
+ }
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+NdsGetObjectInfo(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest,
+ PLOCKED_BUFFER pLockedBuffer
+)
+/*++
+
+Routine Description:
+
+ Get the basic object information for the listed object.
+
+Routine Arguments:
+
+ pIrpContext - describes the irp for this request
+ pLockedBuffer - describes the locked, user mode buffer that we will
+ write the response into
+ pNdsRequest - the request parameters
+
+Return Value:
+
+ The Status of the exchange.
+
+--*/
+{
+ NTSTATUS Status;
+ DWORD dwObjId;
+
+ PAGED_CODE();
+
+ //
+ // Get the object id from the users request packet.
+ //
+
+ try {
+ dwObjId = (pNdsRequest->Parameters).GetObjectInfo.ObjectId;
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+ DebugTrace( 0, Dbg, "Bonk! Lost user mode buffer in NdsGetObjectId...\n", 0 );
+ Status = GetExceptionCode();
+ return Status;
+ }
+
+ //
+ // Hit the wire.
+ //
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_READ_ENTRY_INFO,
+ pLockedBuffer,
+ "DD",
+ 0,
+ dwObjId );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ Status = NdsCompletionCodetoNtStatus( pLockedBuffer );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ try {
+
+ (pNdsRequest->Parameters).GetObjectInfo.BytesWritten = pLockedBuffer->dwBytesWritten;
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Bonk! Lost user mode buffer after getting object info...\n", 0 );
+ Status = GetExceptionCode();
+ return Status;
+
+ }
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+NdsListSubordinates(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest,
+ PLOCKED_BUFFER pLockedBuffer
+)
+/*++
+
+Routine Description:
+
+ List the immediate subordinates of an object.
+
+Routine Arguments:
+
+ pIrpContext - describes the irp for this request
+ pLockedBuffer - describes the locked, user mode buffer that we will
+ write the response into
+ pNdsRequest - the request parameters
+
+Return Value:
+
+ The Status of the exchange.
+
+--*/
+{
+ NTSTATUS Status;
+ DWORD dwParent, dwIterHandle;
+
+ PAGED_CODE();
+
+ //
+ // Dig out the request parameters.
+ //
+
+ try {
+
+ dwParent = (pNdsRequest->Parameters).ListSubordinates.ObjectId;
+ dwIterHandle = (pNdsRequest->Parameters).ListSubordinates.IterHandle;
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Bonk! No user mode buffer in ListSubordinates...\n", 0 );
+ Status = GetExceptionCode();
+ return Status;
+
+ }
+
+ //
+ // Make the request.
+ //
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_LIST,
+ pLockedBuffer,
+ "DDDD",
+ 0,
+ 0x40,
+ dwIterHandle,
+ dwParent );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ Status = NdsCompletionCodetoNtStatus( pLockedBuffer );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ try {
+
+ (pNdsRequest->Parameters).ListSubordinates.BytesWritten = pLockedBuffer->dwBytesWritten;
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Bonk! Lost user mode buffer after getting subordinate list...\n", 0 );
+ Status = GetExceptionCode();
+ return Status;
+
+ }
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+NdsReadAttributes(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest,
+ PLOCKED_BUFFER pLockedBuffer
+)
+/*++
+
+Routine Description:
+
+ Retrieve the named attribute of an object.
+
+ BUGBUG: We don't really know the max attribute name size.
+
+Routine Arguments:
+
+ pIrpContext - describes the irp for this request
+ pLockedBuffer - describes the locked, user mode buffer that we will
+ write the response into
+ pNdsRequest - the request parameters
+
+Return Value:
+
+ The Status of the exchange.
+
+--*/
+{
+ NTSTATUS Status;
+
+ DWORD dwIterHandle, dwOid;
+ UNICODE_STRING uAttributeName;
+ WCHAR AttributeName[MAX_NDS_NAME_CHARS];
+
+ PAGED_CODE();
+
+ RtlZeroMemory( AttributeName, sizeof( AttributeName ) );
+
+ try {
+
+ uAttributeName.Length = (USHORT)(pNdsRequest->Parameters).ReadAttribute.AttributeNameLength;
+ uAttributeName.MaximumLength = sizeof( AttributeName );
+
+ if ( uAttributeName.Length > uAttributeName.MaximumLength ) {
+ ExRaiseStatus( STATUS_INVALID_BUFFER_SIZE );
+ }
+
+ RtlCopyMemory( AttributeName,
+ (pNdsRequest->Parameters).ReadAttribute.AttributeName,
+ uAttributeName.Length );
+
+ uAttributeName.Buffer = AttributeName;
+
+ dwIterHandle = (pNdsRequest->Parameters).ReadAttribute.IterHandle;
+ dwOid = (pNdsRequest->Parameters).ReadAttribute.ObjectId;
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0 , Dbg, "Bonk! Exception accessing user mode buffer in read attributes...\n", 0 );
+ return GetExceptionCode();
+ }
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_READ,
+ pLockedBuffer,
+ "DDDDDDS",
+ 0, // version
+ dwIterHandle, // iteration handle
+ dwOid, // object id
+ 1, // info type
+ //
+ // The attribute specifier has been seen at zero and
+ // at 0x4e0000. I don't know why... but zero doesn't
+ // work sometimes...
+ //
+ 0x4e0000, // attrib type
+ 1, // number of attribs
+ &uAttributeName ); // attrib name
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return Status;
+ }
+
+ Status = NdsCompletionCodetoNtStatus( pLockedBuffer );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ try {
+
+ (pNdsRequest->Parameters).ReadAttribute.BytesWritten = pLockedBuffer->dwBytesWritten;
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Bonk! Lost user mode buffer after reading attribute...\n", 0 );
+ return GetExceptionCode();
+
+ }
+
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+NdsGetVolumeInformation(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest
+)
+/*+++
+
+Description:
+
+ This function gets the name of the server that hosts
+ the listed nds volume.
+
+Parameters:
+
+ pIrpContext - describes the irp for this request
+ pNdsRequest - the request parameters
+
+---*/
+{
+
+
+ NTSTATUS Status;
+
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ PSCB pOriginalScb;
+ PBYTE OutputBuffer;
+ ULONG OutputBufferLength;
+
+ UNICODE_STRING VolumeObject;
+ DWORD VolumeOid;
+ UNICODE_STRING HostServerAttr;
+ UNICODE_STRING HostVolumeAttr;
+ UNICODE_STRING Attribute;
+
+ PWCHAR ServerString;
+ ULONG ServerLength;
+
+ PAGED_CODE();
+
+ //
+ // Get the irp and output buffer information.
+ //
+
+ irp = pIrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+ OutputBufferLength = irpSp->Parameters.FileSystemControl.OutputBufferLength;
+
+ if ( OutputBufferLength ) {
+ NwMapUserBuffer( irp, irp->RequestorMode, (PVOID *)&OutputBuffer );
+ }
+
+ //
+ // Prepare the input information.
+ //
+
+ VolumeObject.Length = (USHORT)pNdsRequest->Parameters.GetVolumeInfo.VolumeNameLen;
+ VolumeObject.MaximumLength = VolumeObject.Length;
+ VolumeObject.Buffer = &(pNdsRequest->Parameters.GetVolumeInfo.VolumeName[0]);
+
+ DebugTrace( 0, Dbg, "Retrieving volume info for %wZ\n", &VolumeObject );
+
+ HostServerAttr.Buffer = HOST_SERVER_ATTRIBUTE; // L"Host Server"
+ HostServerAttr.Length = sizeof( HOST_SERVER_ATTRIBUTE ) - sizeof( WCHAR );
+ HostServerAttr.MaximumLength = HostServerAttr.Length;
+
+ HostVolumeAttr.Buffer = HOST_VOLUME_ATTRIBUTE; // L"Host Resource Name"
+ HostVolumeAttr.Length = sizeof( HOST_VOLUME_ATTRIBUTE ) - sizeof( WCHAR );
+ HostVolumeAttr.MaximumLength = HostVolumeAttr.Length;
+
+ try {
+
+ //
+ // NdsResolveNameKm may have to jump servers to service this
+ // request, however it's dangerous for us to derefence the original
+ // scb because that would expose a scavenger race condition. So,
+ // we add an additional ref-count to the original scb and then clean
+ // up appropriately afterwards, depending on whether or not we
+ // jumped servers.
+ //
+
+ pOriginalScb = pIrpContext->pScb;
+
+ NwReferenceScb( pOriginalScb->pNpScb );
+
+ Status = NdsResolveNameKm ( pIrpContext,
+ &VolumeObject,
+ &VolumeOid,
+ TRUE,
+ DEFAULT_RESOLVE_FLAGS );
+
+ if ( !NT_SUCCESS( Status )) {
+ NwDereferenceScb( pOriginalScb->pNpScb );
+ return STATUS_BAD_NETWORK_PATH;
+ }
+
+ if ( pIrpContext->pScb == pOriginalScb ) {
+
+ //
+ // We didn't jump servers.
+ //
+
+ NwDereferenceScb( pOriginalScb->pNpScb );
+ }
+
+ //
+ // We have to read the server into a temporary buffer so
+ // we can strip off the x500 prefix and the context
+ // from the server name. This isn't really what I would
+ // call nice, but it's the way Netware works.
+ //
+
+ Attribute.Length = 0;
+ Attribute.MaximumLength = MAX_NDS_NAME_SIZE;
+ Attribute.Buffer = ALLOCATE_POOL( PagedPool, MAX_NDS_NAME_SIZE );
+
+ Status = NdsReadStringAttribute( pIrpContext,
+ VolumeOid,
+ &HostServerAttr,
+ &Attribute );
+
+ if ( !NT_SUCCESS( Status )) {
+ FREE_POOL( Attribute.Buffer );
+ goto CleanupScbReferences;
+ }
+
+ ServerString = Attribute.Buffer;
+
+ while( Attribute.Length ) {
+
+ if ( *ServerString == L'=' ) {
+ ServerString += 1;
+ Attribute.Length -= sizeof( WCHAR );
+ break;
+ }
+
+ ServerString += 1;
+ Attribute.Length -= sizeof( WCHAR );
+ }
+
+ if ( Attribute.Length == 0 ) {
+ DebugTrace( 0, Dbg, "Malformed server for volume.\n", 0 );
+ FREE_POOL( Attribute.Buffer );
+ Status = STATUS_UNSUCCESSFUL;
+ goto CleanupScbReferences;
+ }
+
+ ServerLength = 0;
+
+ while ( ServerLength < (Attribute.Length / sizeof( WCHAR )) ) {
+
+ if ( ServerString[ServerLength] == L'.' ) {
+ break;
+ }
+
+ ServerLength++;
+ }
+
+ if ( ServerLength == ( Attribute.Length / sizeof( WCHAR ) ) ) {
+ DebugTrace( 0, Dbg, "Malformed server for volume.\n", 0 );
+ FREE_POOL( Attribute.Buffer );
+ Status = STATUS_UNSUCCESSFUL;
+ goto CleanupScbReferences;
+ }
+
+ ServerLength *= sizeof( WCHAR );
+ RtlCopyMemory( OutputBuffer, ServerString, ServerLength );
+
+ pNdsRequest->Parameters.GetVolumeInfo.ServerNameLen = ServerLength;
+
+ FREE_POOL( Attribute.Buffer );
+
+ Attribute.Length = Attribute.MaximumLength = (USHORT)ServerLength;
+ Attribute.Buffer = (PWCHAR)OutputBuffer;
+ DebugTrace( 0, Dbg, "Host server is: %wZ\n", &Attribute );
+
+ //
+ // Now do the volume in place. This is the easy one.
+ //
+
+ Attribute.MaximumLength = (USHORT)( OutputBufferLength - ServerLength );
+ Attribute.Buffer = (PWSTR) ( OutputBuffer + ServerLength );
+ Attribute.Length = 0;
+
+ Status = NdsReadStringAttribute( pIrpContext,
+ VolumeOid,
+ &HostVolumeAttr,
+ &Attribute );
+
+ if ( !NT_SUCCESS( Status )) {
+ goto CleanupScbReferences;
+ }
+
+ pNdsRequest->Parameters.GetVolumeInfo.TargetVolNameLen = Attribute.Length;
+ DebugTrace( 0, Dbg, "Host volume is: %wZ\n", &Attribute );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Exception handling user mode buffer in GetVolumeInfo.\n", 0 );
+ goto CleanupScbReferences;
+
+ }
+
+ Status = STATUS_SUCCESS;
+
+CleanupScbReferences:
+
+ if ( pIrpContext->pScb != pOriginalScb ) {
+
+ //
+ // We jumped servers and have to cleanup.
+ //
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ NwDereferenceScb( pIrpContext->pScb->pNpScb );
+ pIrpContext->pScb = pOriginalScb;
+ pIrpContext->pNpScb = pOriginalScb->pNpScb;
+
+ }
+
+ return Status;
+}
+
+NTSTATUS
+NdsOpenStream(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest
+) {
+
+ NTSTATUS Status;
+
+ UNICODE_STRING uStream;
+ WCHAR StreamName[MAX_NDS_NAME_CHARS];
+
+ LOCKED_BUFFER NdsRequest;
+
+ DWORD dwOid, StreamAccess;
+ DWORD hNwHandle, dwFileLen;
+
+ PICB pIcb;
+ PSCB pScb = pIrpContext->pNpScb->pScb;
+
+ BOOLEAN LicensedConnection = FALSE;
+
+ PAGED_CODE();
+
+ pIcb = pIrpContext->Icb;
+
+ uStream.Length = 0;
+ uStream.MaximumLength = sizeof( StreamName );
+ uStream.Buffer = StreamName;
+
+ DebugTrace( 0 , Dbg, "NDS open stream...\n", 0 );
+
+ try {
+
+ dwOid = (pNdsRequest->Parameters).OpenStream.ObjectOid;
+ StreamAccess = (pNdsRequest->Parameters).OpenStream.StreamAccess;
+ RtlCopyUnicodeString( &uStream, &(pNdsRequest->Parameters).OpenStream.StreamName );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0 , Dbg, "Bonk! Bad user mode buffer in open stream.\n", 0 );
+ return GetExceptionCode();
+ }
+
+ //
+ // We have the oid and stream name; let's get the handle.
+ //
+
+ Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // If we haven't licensed this connection yet, it's time. Get to the
+ // head of the queue to protect the SCB fields and authenticate the
+ // connection (do not defer the login).
+ //
+
+ NwAppendToQueueAndWait( pIrpContext );
+
+ ASSERT( pScb->MajorVersion > 3 );
+
+ if ( ( pScb->UserName.Length == 0 ) &&
+ ( pScb->VcbCount == 0 ) &&
+ ( pScb->OpenNdsStreams == 0 ) ) {
+
+ if ( pScb->pNpScb->State != SCB_STATE_IN_USE ) {
+
+ Status = ConnectScb( &pScb,
+ pIrpContext,
+ &(pScb->pNpScb->ServerName),
+ NULL, // address
+ NULL, // name
+ NULL, // password
+ FALSE, // defer login
+ FALSE, // delete connection
+ TRUE ); // existing scb
+
+ if ( !NT_SUCCESS( Status ) ) {
+ DebugTrace( 0, Dbg, "Couldn't connect server %08lx to open NDS stream.\n", pScb );
+ goto ExitWithCleanup;
+ }
+ }
+
+ ASSERT( pScb->pNpScb->State == SCB_STATE_IN_USE );
+
+ Status = NdsLicenseConnection( pIrpContext );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ Status = STATUS_REMOTE_SESSION_LIMIT;
+ goto ExitWithCleanup;
+ }
+
+ LicensedConnection = TRUE;
+ }
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_OPEN_STREAM,
+ &NdsRequest,
+ "DDDs",
+ 0, // version
+ StreamAccess, // file access
+ dwOid, // object id
+ &uStream ); // attribute name
+
+ if ( !NT_SUCCESS( Status )) {
+ goto ExitWithCleanup;
+ }
+
+ Status = NdsCompletionCodetoNtStatus( &NdsRequest );
+
+ if ( !NT_SUCCESS( Status )) {
+ goto ExitWithCleanup;
+ }
+
+ Status = ParseResponse( NULL,
+ NdsRequest.pRecvBufferVa,
+ NdsRequest.dwBytesWritten,
+ "G_DD",
+ sizeof( DWORD ), // completion code
+ &hNwHandle, // remote handle
+ &dwFileLen ); // file length
+
+ if ( !NT_SUCCESS( Status )) {
+ goto ExitWithCleanup;
+ }
+
+ *(WORD *)(&pIcb->Handle[0]) = (WORD)hNwHandle + 1;
+ *( (UNALIGNED DWORD *) (&pIcb->Handle[2]) ) = hNwHandle;
+
+ pIrpContext->pScb->OpenNdsStreams++;
+
+ DebugTrace( 0, Dbg, "File stream opened. Length = %d\n", dwFileLen );
+
+ (pNdsRequest->Parameters).OpenStream.FileLength = dwFileLen;
+ pIcb->HasRemoteHandle = TRUE;
+
+ pIcb->FileObject->CurrentByteOffset.QuadPart = 0;
+
+ExitWithCleanup:
+
+ NdsFreeLockedBuffer( &NdsRequest );
+
+ if ( ( !NT_SUCCESS( Status ) ) &&
+ ( LicensedConnection ) ) {
+ NdsUnlicenseConnection( pIrpContext );
+ }
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ return Status;
+
+}
+
+NTSTATUS
+NdsSetContext(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest
+) {
+
+ NTSTATUS Status;
+
+ PLOGON pLogon;
+
+ UNICODE_STRING Tree, Context;
+ PNDS_SECURITY_CONTEXT pCredentials;
+
+ PAGED_CODE();
+
+ DebugTrace( 0 , Dbg, "NDS set context.\n", 0 );
+
+ //
+ // Find out who this is.
+ //
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUser( &(pIrpContext->Specific.Create.UserUid), FALSE );
+ NwReleaseRcb( &NwRcb );
+
+ if ( !Logon ) {
+
+ DebugTrace( 0, Dbg, "Couldn't find logon data for this user.\n", 0 );
+ return STATUS_ACCESS_DENIED;
+
+ }
+
+ //
+ // Verify that this context really is a context.
+ //
+
+ Tree.Length = (USHORT)(pNdsRequest->Parameters).SetContext.TreeNameLen;
+ Tree.MaximumLength = Tree.Length;
+ Tree.Buffer = (pNdsRequest->Parameters).SetContext.TreeAndContextString;
+
+ Context.Length = (USHORT)(pNdsRequest->Parameters).SetContext.ContextLen;
+ Context.MaximumLength = Context.Length;
+ Context.Buffer = (WCHAR *) (((BYTE *)Tree.Buffer) + Tree.Length);
+
+ Status = NdsVerifyContext( pIrpContext, &Tree, &Context );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ Status = NdsLookupCredentials( &Tree,
+ pLogon,
+ &pCredentials,
+ CREDENTIAL_READ,
+ TRUE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ DebugTrace( 0, Dbg, "No credentials in set context.\n", 0 );
+ return STATUS_NO_SUCH_LOGON_SESSION;
+ }
+
+ //
+ // ALERT! We are holding the credential list!
+ //
+
+ if ( Context.Length > MAX_NDS_NAME_SIZE ) {
+
+ DebugTrace( 0, Dbg, "Context too long.\n", 0 );
+ Status = STATUS_INVALID_PARAMETER;
+ goto ReleaseAndExit;
+ }
+
+ try {
+
+ RtlCopyUnicodeString( &pCredentials->CurrentContext, &Context );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Bad user buffer in SetContext.\n", 0 );
+ Status = STATUS_INVALID_PARAMETER;
+ goto ReleaseAndExit;
+ }
+
+ NwReleaseCredList( pLogon );
+
+ //
+ // RELAX! The credential list is free.
+ //
+
+ DebugTrace( 0, Dbg, "New context: %wZ\n", &Context );
+ return STATUS_SUCCESS;
+
+ReleaseAndExit:
+
+ NwReleaseCredList( pLogon );
+ return Status;
+}
+
+NTSTATUS
+NdsGetContext(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest
+) {
+
+ NTSTATUS Status;
+
+ PLOGON pLogon;
+
+ UNICODE_STRING Tree;
+ PNDS_SECURITY_CONTEXT pCredentials;
+
+ PAGED_CODE();
+
+ DebugTrace( 0 , Dbg, "NDS get context.\n", 0 );
+
+ //
+ // Find out who this is.
+ //
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUser( &(pIrpContext->Specific.Create.UserUid), FALSE );
+ NwReleaseRcb( &NwRcb );
+
+ if ( !Logon ) {
+
+ DebugTrace( 0, Dbg, "Couldn't find logon data for this user.\n", 0 );
+ return STATUS_ACCESS_DENIED;
+
+ }
+
+ //
+ // We know who it is, so get the context.
+ //
+
+ Tree.Length = (USHORT)(pNdsRequest->Parameters).GetContext.TreeNameLen;
+ Tree.MaximumLength = Tree.Length;
+ Tree.Buffer = (pNdsRequest->Parameters).GetContext.TreeNameString;
+
+ Status = NdsLookupCredentials( &Tree,
+ pLogon,
+ &pCredentials,
+ CREDENTIAL_READ,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ //
+ // No context has been set, so report none.
+ //
+
+ try {
+
+ (pNdsRequest->Parameters).GetContext.Context.Length = 0;
+ return STATUS_SUCCESS;
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Bad user buffer in GetContext.\n", 0 );
+ return STATUS_INVALID_PARAMETER;
+
+ }
+
+ }
+
+ //
+ // Make sure we can report the whole thing.
+ // ALERT! We are holding the credential list!
+ //
+
+ if ( (pNdsRequest->Parameters).GetContext.Context.MaximumLength <
+ pCredentials->CurrentContext.Length ) {
+
+ Status = STATUS_BUFFER_TOO_SMALL;
+ goto ReleaseAndExit;
+ }
+
+ try {
+
+ RtlCopyUnicodeString( &(pNdsRequest->Parameters).GetContext.Context,
+ &pCredentials->CurrentContext );
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "Bad user buffer in GetContext.\n", 0 );
+ Status = STATUS_INVALID_PARAMETER;
+ goto ReleaseAndExit;
+ }
+
+ NwReleaseCredList( pLogon );
+
+ //
+ // RELAX! The credential list is free.
+ //
+
+ DebugTrace( 0, Dbg, "Reported context: %wZ\n", &pCredentials->CurrentContext );
+ return STATUS_SUCCESS;
+
+ReleaseAndExit:
+
+ NwReleaseCredList( pLogon );
+ return Status;
+
+}
+
+NTSTATUS
+NdsVerifyTreeHandle(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest
+) {
+
+ NTSTATUS Status;
+ UNICODE_STRING NdsTree;
+ WCHAR TreeBuffer[NDS_TREE_NAME_LEN];
+
+ PAGED_CODE();
+
+ try {
+
+ //
+ // Check to see if the handle points to a dir server in the
+ // specified tree. Make sure to unmunge the tree name in
+ // the SCB first, just in case.
+ //
+
+ NdsTree.Length = 0;
+ NdsTree.MaximumLength = sizeof( TreeBuffer );
+ NdsTree.Buffer = TreeBuffer;
+
+ UnmungeCredentialName( &pIrpContext->pScb->NdsTreeName,
+ &NdsTree );
+
+ if ( !RtlCompareUnicodeString( &NdsTree,
+ &(pNdsRequest->Parameters).VerifyTree.TreeName,
+ TRUE ) ) {
+
+ DebugTrace( 0 , Dbg, "NdsVerifyTreeHandle: Success\n", 0 );
+ Status = STATUS_SUCCESS;
+ } else {
+
+ DebugTrace( 0 , Dbg, "NdsVerifyTreeHandle: Failure\n", 0 );
+ Status = STATUS_ACCESS_DENIED;
+ }
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0 , Dbg, "NdsVerifyTreeHandle: Invalid parameters.\n", 0 );
+ Status = STATUS_INVALID_PARAMETER;
+
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+NdsGetPrintQueueInfo(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest
+) {
+
+ NTSTATUS Status;
+
+ UNICODE_STRING ServerAttribute;
+ WCHAR Server[] = L"Host Server";
+
+ PSCB pPrintHost = NULL;
+ PNONPAGED_SCB pOriginalNpScb = NULL;
+
+ DWORD dwObjectId, dwObjectType;
+
+ UNICODE_STRING uPrintServer;
+
+ BYTE *pbQueue, *pbRQueue;
+
+ PAGED_CODE();
+
+ RtlInitUnicodeString( &ServerAttribute, Server );
+
+ //
+ // Make sure we have a print queue object. We may
+ // have to jump servers if we get referred to another
+ // replica. If this is the case, we can't lose the
+ // ref count on the original server since that's where
+ // the ICB handle is.
+ //
+
+ pOriginalNpScb = pIrpContext->pNpScb;
+ NwReferenceScb( pOriginalNpScb );
+
+ Status = NdsVerifyObject( pIrpContext,
+ &(pNdsRequest->Parameters).GetQueueInfo.QueueName,
+ TRUE,
+ DEFAULT_RESOLVE_FLAGS,
+ &dwObjectId,
+ &dwObjectType );
+
+ if ( pIrpContext->pNpScb == pOriginalNpScb ) {
+
+ //
+ // If we were not referred, remove the extra ref
+ // count and clear the original pointer.
+ //
+
+ NwDereferenceScb( pOriginalNpScb );
+ pOriginalNpScb = NULL;
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ if ( dwObjectType != NDS_OBJECTTYPE_QUEUE ) {
+ Status = STATUS_INVALID_PARAMETER;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Retrieve the host server name.
+ //
+
+ Status = NdsReadStringAttribute( pIrpContext,
+ dwObjectId,
+ &ServerAttribute,
+ &(pNdsRequest->Parameters).GetQueueInfo.HostServer );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Dig out the actual server name from the X.500 name.
+ //
+
+ Status = NdsGetServerBasicName( &(pNdsRequest->Parameters).GetQueueInfo.HostServer,
+ &uPrintServer );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Connect to the actual host server. If there was a referral, we
+ // can simply dump the referred server since we are holding the ref
+ // count on the original owner of the ICB.
+ //
+
+ if ( pOriginalNpScb ) {
+ NwDereferenceScb( pIrpContext->pNpScb );
+ } else {
+ pOriginalNpScb = pIrpContext->pNpScb;
+ }
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ Status = CreateScb( &pPrintHost,
+ pIrpContext,
+ &uPrintServer,
+ NULL,
+ NULL,
+ NULL,
+ FALSE,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ pIrpContext->pNpScb = NULL;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Re-query the OID of the print queue object on this server.
+ // Don't allow any server jumping this time; we only need the
+ // oid of the queue.
+ //
+
+ Status = NdsVerifyObject( pIrpContext,
+ &(pNdsRequest->Parameters).GetQueueInfo.QueueName,
+ FALSE,
+ RSLV_CREATE_ID,
+ &dwObjectId,
+ NULL );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // Byte swap the queue id.
+ //
+
+ pbRQueue = (BYTE *) &dwObjectId;
+ pbQueue = (BYTE *) &(pNdsRequest->Parameters).GetQueueInfo.QueueId;
+
+ pbQueue[0] = pbRQueue[3];
+ pbQueue[1] = pbRQueue[2];
+ pbQueue[2] = pbRQueue[1];
+ pbQueue[3] = pbRQueue[0];
+ }
+
+ExitWithCleanup:
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ //
+ // Restore the pointers and ref counts as appropriate.
+ //
+
+ if ( pOriginalNpScb ) {
+
+ if ( pIrpContext->pNpScb ) {
+ NwDereferenceScb( pIrpContext->pNpScb );
+ }
+
+ pIrpContext->pNpScb = pOriginalNpScb;
+ pIrpContext->pScb = pOriginalNpScb->pScb;
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+NdsChangePass(
+ PIRP_CONTEXT pIrpContext
+) {
+
+ NTSTATUS Status;
+
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ PNWR_NDS_REQUEST_PACKET Rrp;
+
+ UNICODE_STRING NdsTree;
+ UNICODE_STRING UserName;
+ UNICODE_STRING CurrentPassword;
+ UNICODE_STRING NewPassword;
+ PBYTE CurrentString;
+ BOOLEAN ServerReferenced = FALSE;
+
+ OEM_STRING OemCurrentPassword;
+ BYTE CurrentBuffer[MAX_PW_CHARS];
+
+ OEM_STRING OemNewPassword;
+ BYTE NewBuffer[MAX_PW_CHARS];
+
+ PSCB Scb;
+
+ PAGED_CODE();
+
+ //
+ // Get the request.
+ //
+
+ irp = pIrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+ Rrp = ( PNWR_NDS_REQUEST_PACKET ) irpSp->Parameters.FileSystemControl.Type3InputBuffer;
+
+ if ( !Rrp ) {
+
+ DebugTrace( 0, Dbg, "No raw request buffer.\n", 0 );
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Dig out the parameters.
+ //
+
+ CurrentString = ( PBYTE ) &(Rrp->Parameters.ChangePass.StringBuffer[0]);
+
+ NdsTree.Length = NdsTree.MaximumLength =
+ ( USHORT ) Rrp->Parameters.ChangePass.NdsTreeNameLength;
+ NdsTree.Buffer = ( PWCHAR ) CurrentString;
+
+ CurrentString += NdsTree.Length;
+
+ UserName.Length = UserName.MaximumLength =
+ ( USHORT ) Rrp->Parameters.ChangePass.UserNameLength;
+ UserName.Buffer = ( PWCHAR ) CurrentString;
+
+ CurrentString += UserName.Length;
+
+ CurrentPassword.Length = CurrentPassword.MaximumLength =
+ ( USHORT ) Rrp->Parameters.ChangePass.CurrentPasswordLength;
+ CurrentPassword.Buffer = ( PWCHAR ) CurrentString;
+
+ CurrentString += CurrentPassword.Length;
+
+ NewPassword.Length = NewPassword.MaximumLength =
+ ( USHORT ) Rrp->Parameters.ChangePass.NewPasswordLength;
+ NewPassword.Buffer = ( PWCHAR ) CurrentString;
+
+ //
+ // Get a server to handle this request.
+ //
+
+ try {
+
+ //
+ // Convert the passwords to the appropriate type.
+ //
+
+ OemCurrentPassword.Length = 0;
+ OemCurrentPassword.MaximumLength = sizeof( CurrentBuffer );
+ OemCurrentPassword.Buffer = CurrentBuffer;
+
+ OemNewPassword.Length = 0;
+ OemNewPassword.MaximumLength = sizeof( NewBuffer );
+ OemNewPassword.Buffer = NewBuffer;
+
+ RtlUpcaseUnicodeStringToOemString( &OemCurrentPassword,
+ &CurrentPassword,
+ FALSE );
+
+ RtlUpcaseUnicodeStringToOemString( &OemNewPassword,
+ &NewPassword,
+ FALSE );
+
+ //
+ // Get a dir server to handle the request.
+ //
+
+ Status = NdsCreateTreeScb( pIrpContext,
+ &Scb,
+ &NdsTree,
+ NULL,
+ NULL,
+ TRUE,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ DebugTrace( 0, Dbg, "No dir servers for nds change password.\n", 0 );
+ return STATUS_BAD_NETWORK_PATH;
+ }
+
+ ServerReferenced = TRUE;
+
+ //
+ // Perform the change password.
+ //
+
+ Status = NdsTreeLogin( pIrpContext,
+ &UserName,
+ &OemCurrentPassword,
+ &OemNewPassword,
+ NULL );
+
+ NwDereferenceScb( Scb->pNpScb );
+ ServerReferenced = FALSE;
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ DebugTrace( 0, Dbg, "NdsChangePass: Exception dealing with user request.\n", 0 );
+ Status = STATUS_INVALID_PARAMETER;
+ goto ExitWithCleanup;
+ }
+
+ DebugTrace( 0, Dbg, "NdsChangePassword succeeded for %wZ.\n", &UserName );
+ Status = STATUS_SUCCESS;
+
+ExitWithCleanup:
+
+ if ( ServerReferenced ) {
+ NwDereferenceScb( Scb->pNpScb );
+ }
+
+ //
+ // We get STATUS_PASSWORD_EXPIRED when the user is not allowed
+ // to change their password on the Netware server, so we return
+ // PASSWORD_RESTRICTION instead.
+ //
+
+ if ( Status == STATUS_PASSWORD_EXPIRED ) {
+ Status = STATUS_PASSWORD_RESTRICTION;
+ }
+
+ return Status;
+
+
+}
+
+
+NTSTATUS
+NdsListTrees(
+ PIRP_CONTEXT pIrpContext
+)
+/*+++
+
+Description:
+
+ This odd little routine takes the NTUSER name of the logged in
+ user (on the system) and returns a list of NDS trees that the
+ NTUSER is connected to and the user names for those connections.
+ This is necessary because the change password ui runs in the
+ systems luid and can't access the GET_CONN_STATUS api and because
+ the change password code might happen when no user is logged in.
+
+ The return data in the users buffer is an array of
+ CONN_INFORMATION structures with the strings packed after the
+ structures. There is no continuation of this routine, so pass
+ a decent sized buffer.
+
+---*/
+{
+
+ NTSTATUS Status;
+
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ PNWR_NDS_REQUEST_PACKET Rrp;
+ DWORD OutputBufferLength;
+ PBYTE OutputBuffer;
+
+ UNICODE_STRING NtUserName;
+ PLOGON pLogon;
+ DWORD dwTreesReturned = 0;
+ DWORD dwBytesNeeded;
+
+ PCONN_INFORMATION pConnInfo;
+ PLIST_ENTRY pNdsList;
+ PNDS_SECURITY_CONTEXT pNdsContext;
+
+ PAGED_CODE();
+
+ //
+ // Get the request.
+ //
+
+ irp = pIrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+ Rrp = ( PNWR_NDS_REQUEST_PACKET ) irpSp->Parameters.FileSystemControl.Type3InputBuffer;
+
+ OutputBufferLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength;
+ NwMapUserBuffer( irp, KernelMode, (PVOID *)&OutputBuffer );
+
+ if ( !Rrp || !OutputBufferLength || !OutputBuffer ) {
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Dig out the parameters.
+ //
+
+ NtUserName.Length = NtUserName.MaximumLength = (USHORT) Rrp->Parameters.ListTrees.NtUserNameLength;
+ NtUserName.Buffer = &(Rrp->Parameters.ListTrees.NtUserName[0]);
+
+ DebugTrace( 0, Dbg, "ListTrees: Looking up %wZ\n", &NtUserName );
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUserByName( &NtUserName );
+ NwReleaseRcb( &NwRcb );
+
+ if ( !pLogon ) {
+ DebugTrace( 0, Dbg, "ListTrees: No such NT user.\n", 0 );
+ return STATUS_NO_SUCH_USER;
+ }
+
+ //
+ // Otherwise build the list of trees.
+ //
+
+ Rrp->Parameters.ListTrees.UserLuid = pLogon->UserUid;
+
+ NwAcquireExclusiveCredList( pLogon );
+ pConnInfo = ( PCONN_INFORMATION ) OutputBuffer;
+
+ pNdsList = pLogon->NdsCredentialList.Flink;
+
+ try {
+
+ while ( pNdsList != &(pLogon->NdsCredentialList) ) {
+
+ pNdsContext = CONTAINING_RECORD( pNdsList, NDS_SECURITY_CONTEXT, Next );
+
+ //
+ // Check to make sure there's a credential.
+ //
+
+ if ( pNdsContext->Credential == NULL ) {
+ goto ProcessNextListEntry;
+ }
+
+ //
+ // Check to make sure there's space to report.
+ //
+
+ dwBytesNeeded = ( sizeof( CONN_INFORMATION ) +
+ pNdsContext->Credential->userNameLength +
+ pNdsContext->NdsTreeName.Length -
+ sizeof( WCHAR ) );
+
+ if ( OutputBufferLength < dwBytesNeeded ) {
+ break;
+ }
+
+ //
+ // Report it! Note that the user name in the credential is NULL terminated.
+ //
+
+ pConnInfo->HostServerLength = pNdsContext->NdsTreeName.Length;
+ pConnInfo->UserNameLength = pNdsContext->Credential->userNameLength - sizeof( WCHAR );
+ pConnInfo->HostServer = (LPWSTR) ( ((BYTE *)pConnInfo) + sizeof( CONN_INFORMATION ) );
+ pConnInfo->UserName = (LPWSTR) ( ( (BYTE *)pConnInfo) +
+ sizeof( CONN_INFORMATION ) +
+ pConnInfo->HostServerLength );
+
+ RtlCopyMemory( pConnInfo->HostServer,
+ pNdsContext->NdsTreeName.Buffer,
+ pConnInfo->HostServerLength );
+
+ RtlCopyMemory( pConnInfo->UserName,
+ ( ((BYTE *) pNdsContext->Credential ) +
+ sizeof( NDS_CREDENTIAL ) +
+ pNdsContext->Credential->optDataSize ),
+ pConnInfo->UserNameLength );
+
+ OutputBufferLength -= dwBytesNeeded;
+ dwTreesReturned++;
+ pConnInfo = ( PCONN_INFORMATION ) ( ((BYTE *)pConnInfo) + dwBytesNeeded );
+
+ProcessNextListEntry:
+
+ //
+ // Do the next one.
+ //
+
+ pNdsList = pNdsList->Flink;
+ }
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ //
+ // If we access violate, stop and return what we have.
+ //
+
+ DebugTrace( 0, Dbg, "User mode buffer access problem.\n", 0 );
+ }
+
+ NwReleaseCredList( pLogon );
+
+ DebugTrace( 0, Dbg, "Returning %d tree entries.\n", dwTreesReturned );
+ Rrp->Parameters.ListTrees.TreesReturned = dwTreesReturned;
+ return STATUS_SUCCESS;
+}
diff --git a/private/nw/rdr/ndslogin.c b/private/nw/rdr/ndslogin.c
new file mode 100644
index 000000000..953a4a4d2
--- /dev/null
+++ b/private/nw/rdr/ndslogin.c
@@ -0,0 +1,3365 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ NdsLogin.c
+
+Abstract:
+
+ This file implements the functionality required to
+ perform an NDS login.
+
+Author:
+
+ Cory West [CoryWest] 23-Feb-1995
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+
+#define Dbg (DEBUG_TRACE_NDS)
+
+//
+// Pageable.
+//
+
+#pragma alloc_text( PAGE, NdsCanonUserName )
+#pragma alloc_text( PAGE, NdsCheckCredentials )
+#pragma alloc_text( PAGE, NdsCheckCredentialsEx )
+#pragma alloc_text( PAGE, NdsLookupCredentials )
+#pragma alloc_text( PAGE, NdsGetCredentials )
+#pragma alloc_text( PAGE, DoNdsLogon )
+#pragma alloc_text( PAGE, BeginLogin )
+#pragma alloc_text( PAGE, FinishLogin )
+#pragma alloc_text( PAGE, ChangeNdsPassword )
+#pragma alloc_text( PAGE, NdsServerAuthenticate )
+#pragma alloc_text( PAGE, BeginAuthenticate )
+#pragma alloc_text( PAGE, NdsLicenseConnection )
+#pragma alloc_text( PAGE, NdsUnlicenseConnection )
+#pragma alloc_text( PAGE, NdsGetBsafeKey )
+
+//
+// Note pageable:
+//
+// NdsTreeLogin (holds a spin lock)
+// NdsLogoff (holds a spin lock)
+//
+
+VOID
+Shuffle(
+ UCHAR *achObjectId,
+ UCHAR *szUpperPassword,
+ int iPasswordLen,
+ UCHAR *achOutputBuffer
+);
+
+NTSTATUS
+NdsCanonUserName(
+ IN PNDS_SECURITY_CONTEXT pNdsContext,
+ IN PUNICODE_STRING puUserName,
+ IN OUT PUNICODE_STRING puCanonUserName
+)
+/*+++
+
+ Canonicalize the user name for the given tree and
+ current connection state. Canonicalization includes
+ handling the correct context and cleaning off all
+ the X500 prefixes.
+
+ ALERT! The credential list must be held (shared or
+ exclusive) while this function is called.
+
+---*/
+{
+
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ USHORT CurrentTargetIndex;
+ int PrefixBytes;
+
+ UNICODE_STRING UnstrippedName;
+ PWCHAR CanonBuffer;
+
+ PAGED_CODE();
+
+ CanonBuffer = ALLOCATE_POOL( PagedPool, MAX_NDS_NAME_SIZE );
+ if ( !CanonBuffer ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // If the name starts with a dot, it's referenced from the root
+ // of the tree and we should not append the context. We should,
+ // however, strip off the leading dot so that name resolution
+ // will work.
+ //
+
+ if ( puUserName->Buffer[0] == L'.' ) {
+
+ UnstrippedName.Length = puUserName->Length - sizeof( WCHAR );
+ UnstrippedName.MaximumLength = UnstrippedName.Length;
+ UnstrippedName.Buffer = &(puUserName->Buffer[1]);
+
+ goto StripPrefixes;
+ }
+
+ //
+ // If the name contains any dots, it's qualified and we
+ // should probably just use it as is.
+ //
+
+ CurrentTargetIndex= 0;
+
+ while ( CurrentTargetIndex< ( puUserName->Length / sizeof( WCHAR ) ) ) {
+
+ if ( puUserName->Buffer[CurrentTargetIndex] == L'.' ) {
+
+ UnstrippedName.Length = puUserName->Length;
+ UnstrippedName.MaximumLength = puUserName->Length;
+ UnstrippedName.Buffer = puUserName->Buffer;
+
+ goto StripPrefixes;
+ }
+
+ CurrentTargetIndex++;
+ }
+
+ //
+ // If we have a context for this tree and the name isn't
+ // qualified, we should append the context.
+ //
+
+ if ( pNdsContext->CurrentContext.Length ) {
+
+ if ( ( puUserName->Length +
+ pNdsContext->CurrentContext.Length ) >= MAX_NDS_NAME_SIZE ) {
+
+ DebugTrace( 0, Dbg, "NDS canon name too long.\n", 0 );
+ Status = STATUS_INVALID_PARAMETER;
+ goto ExitWithCleanup;
+ }
+
+ RtlCopyMemory( CanonBuffer, puUserName->Buffer, puUserName->Length );
+ CanonBuffer[puUserName->Length / sizeof( WCHAR )] = L'.';
+
+ RtlCopyMemory( ((BYTE *)CanonBuffer) + puUserName->Length + sizeof( WCHAR ),
+ pNdsContext->CurrentContext.Buffer,
+ pNdsContext->CurrentContext.Length );
+
+ UnstrippedName.Length = puUserName->Length +
+ pNdsContext->CurrentContext.Length +
+ sizeof( WCHAR );
+ UnstrippedName.MaximumLength = MAX_NDS_NAME_SIZE;
+ UnstrippedName.Buffer = CanonBuffer;
+
+ goto StripPrefixes;
+
+ }
+
+ //
+ // It wasn't qualified, nor was there a context to append, so fail it.
+ //
+
+ DebugTrace( 0, Dbg, "The name %wZ is not canonicalizable.\n", puUserName );
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+
+StripPrefixes:
+
+ //
+ // All of these indexes are in BYTES, not WCHARS!
+ //
+
+ CurrentTargetIndex = 0;
+ PrefixBytes = 0;
+ puCanonUserName->Length = 0;
+
+ while ( ( CurrentTargetIndex < UnstrippedName.Length ) &&
+ ( puCanonUserName->Length < puCanonUserName->MaximumLength ) ) {
+
+ //
+ // Strip off the X.500 prefixes.
+ //
+
+ if ( UnstrippedName.Buffer[CurrentTargetIndex / sizeof( WCHAR )] == L'=' ) {
+
+ CurrentTargetIndex += sizeof( WCHAR );
+ puCanonUserName->Length -= PrefixBytes;
+ PrefixBytes = 0;
+
+ continue;
+ }
+
+ puCanonUserName->Buffer[puCanonUserName->Length / sizeof( WCHAR )] =
+ UnstrippedName.Buffer[CurrentTargetIndex / sizeof( WCHAR )];
+
+ puCanonUserName->Length += sizeof( WCHAR );
+ CurrentTargetIndex += sizeof( WCHAR );
+
+ if ( UnstrippedName.Buffer[CurrentTargetIndex / sizeof( WCHAR )] == L'.' ) {
+ PrefixBytes = 0;
+ PrefixBytes -= sizeof( WCHAR );
+ } else {
+ PrefixBytes += sizeof( WCHAR );
+ }
+ }
+
+ DebugTrace( 0, Dbg, "Canonicalized name: %wZ\n", puCanonUserName );
+
+ExitWithCleanup:
+
+ FREE_POOL( CanonBuffer );
+ return Status;
+}
+
+NTSTATUS
+NdsCheckCredentials(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING puUserName,
+ IN PUNICODE_STRING puPassword
+)
+/*++
+
+ Given a set of credentials and a username and password,
+ we need to determine if username and password match those
+ that were used to acquire the credentials.
+
+--*/
+{
+
+ NTSTATUS Status;
+ PLOGON pLogon;
+ PNONPAGED_SCB pNpScb;
+ PSCB pScb;
+ PNDS_SECURITY_CONTEXT pCredentials;
+
+ PAGED_CODE();
+
+ //
+ // Grab the user's LOGON structure and credentials.
+ //
+
+ pNpScb = pIrpContext->pNpScb;
+ pScb = pNpScb->pScb;
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUser( &pScb->UserUid, FALSE );
+ NwReleaseRcb( &NwRcb );
+
+ if ( !pLogon ) {
+ DebugTrace( 0, Dbg, "Invalid client security context in NdsCheckCredentials.\n", 0 );
+ return STATUS_ACCESS_DENIED;
+ }
+
+ Status = NdsLookupCredentials( &pScb->NdsTreeName,
+ pLogon,
+ &pCredentials,
+ CREDENTIAL_READ,
+ FALSE );
+
+ if( NT_SUCCESS( Status ) ) {
+
+ if ( pCredentials->CredentialLocked ) {
+
+ Status = STATUS_DEVICE_BUSY;
+
+ } else {
+
+ Status = NdsCheckCredentialsEx( pIrpContext,
+ pLogon,
+ pCredentials,
+ puUserName,
+ puPassword );
+
+ }
+
+ NwReleaseCredList( pLogon );
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+NdsCheckCredentialsEx(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PLOGON pLogon,
+ IN PNDS_SECURITY_CONTEXT pNdsContext,
+ IN PUNICODE_STRING puUserName,
+ IN PUNICODE_STRING puPassword
+)
+/*++
+
+ Given a set of credentials and a username and password,
+ we need to determine if username and password match those
+ that were used to acquire the credentials.
+
+ ALERT! The credential list must be held (either shared or
+ exclusive) while this function is called.
+
+--*/
+{
+
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ UNICODE_STRING CredentialName;
+
+ UNICODE_STRING CanonCredentialName, CanonUserName;
+ PWCHAR CredNameBuffer;
+ PWCHAR UserNameBuffer;
+
+ UNICODE_STRING StoredPassword;
+ PWCHAR Stored;
+
+ PAGED_CODE();
+
+ //
+ // If we haven't logged into to the tree, there is no security
+ // conflict. Otherwise, run the check.
+ //
+
+ ASSERT ( pNdsContext->Credential != NULL );
+
+ CredNameBuffer = ALLOCATE_POOL( PagedPool,
+ ( 2 * MAX_NDS_NAME_SIZE ) +
+ ( MAX_PW_CHARS * sizeof( WCHAR ) ) );
+ if ( !CredNameBuffer ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ UserNameBuffer = (PWCHAR) (((BYTE *)CredNameBuffer) + MAX_NDS_NAME_SIZE );
+ Stored = (PWCHAR) (((BYTE *)UserNameBuffer) + MAX_NDS_NAME_SIZE );
+
+ if ( puUserName && puUserName->Length ) {
+
+ //
+ // Canon the incoming name and the credential name.
+ //
+
+ CanonUserName.Length = 0;
+ CanonUserName.MaximumLength = MAX_NDS_NAME_SIZE;
+ CanonUserName.Buffer = UserNameBuffer;
+
+ Status = NdsCanonUserName( pNdsContext,
+ puUserName,
+ &CanonUserName );
+
+ if ( !NT_SUCCESS( Status )) {
+ Status = STATUS_NETWORK_CREDENTIAL_CONFLICT;
+ goto ExitWithCleanup;
+ }
+
+ CanonCredentialName.Length = 0;
+ CanonCredentialName.MaximumLength = MAX_NDS_NAME_SIZE;
+ CanonCredentialName.Buffer = CredNameBuffer;
+
+ CredentialName.Length = (USHORT)pNdsContext->Credential->userNameLength - sizeof( WCHAR );
+ CredentialName.MaximumLength = CredentialName.Length;
+ CredentialName.Buffer = (PWCHAR)( (PBYTE)(pNdsContext->Credential) +
+ sizeof( NDS_CREDENTIAL ) );
+
+ Status = NdsCanonUserName( pNdsContext,
+ &CredentialName,
+ &CanonCredentialName );
+
+ if ( !NT_SUCCESS( Status )) {
+ Status = STATUS_NETWORK_CREDENTIAL_CONFLICT;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // See if they match.
+ //
+
+ if ( RtlCompareUnicodeString( &CanonUserName, &CanonCredentialName, TRUE )) {
+ DebugTrace( 0, Dbg, "NdsCheckCredentialsEx: user name conflict.\n", 0 );
+ Status = STATUS_NETWORK_CREDENTIAL_CONFLICT;
+ goto ExitWithCleanup;
+ }
+ }
+
+ if ( puPassword && puPassword->Length ) {
+
+ //
+ // Now check the password.
+ //
+
+ StoredPassword.Length = 0;
+ StoredPassword.MaximumLength = MAX_PW_CHARS * sizeof( WCHAR );
+ StoredPassword.Buffer = Stored;
+
+ RtlOemStringToUnicodeString( &StoredPassword,
+ &pNdsContext->Password,
+ FALSE );
+
+ if ( RtlCompareUnicodeString( puPassword,
+ &StoredPassword,
+ TRUE ) ) {
+ DebugTrace( 0, Dbg, "NdsCheckCredentialsEx: password conflict.\n", 0 );
+ Status = STATUS_WRONG_PASSWORD;
+ goto ExitWithCleanup;
+ }
+ }
+
+ExitWithCleanup:
+
+ FREE_POOL( CredNameBuffer );
+ return Status;
+}
+
+NTSTATUS
+NdsLookupCredentials(
+ IN PUNICODE_STRING puTreeName,
+ IN PLOGON pLogon,
+ OUT PNDS_SECURITY_CONTEXT *ppCredentials,
+ DWORD dwDesiredAccess,
+ BOOLEAN fCreate
+)
+/*+++
+
+ Retrieve the nds credentials for the given tree from the
+ list of valid credentials for the specified user.
+
+ puTreeName - The name of the tree that we want credentials for. If NULL
+ is specified, we return the credentials for the default tree.
+ pLogon - The logon structure for the user we want to access the tree.
+ ppCredentials - Where to put the pointed to the credentials.
+ dwDesiredAccess - CREDENTIAL_READ if we want read/only access, CREDENTIAL_WRITE
+ if we're going to change the credentials.
+ fCreate - If the credentials don't exist, should we create them?
+
+ We return the credentials with the list held in the appropriate mode. The
+ caller is responsible for releasing the list when done with the credentials.
+
+---*/
+{
+
+ NTSTATUS Status;
+
+ PLIST_ENTRY pFirst, pNext;
+ PNDS_SECURITY_CONTEXT pNdsContext;
+
+ PAGED_CODE();
+
+
+ //
+ // Check for existing credentials.
+ //
+
+ if ( dwDesiredAccess == CREDENTIAL_READ ) {
+ NwAcquireSharedCredList( pLogon );
+ } else {
+ NwAcquireExclusiveCredList( pLogon );
+ }
+
+ pFirst = &pLogon->NdsCredentialList;
+ pNext = pLogon->NdsCredentialList.Flink;
+
+ while ( pNext && ( pFirst != pNext ) ) {
+
+ pNdsContext = (PNDS_SECURITY_CONTEXT)
+ CONTAINING_RECORD( pNext,
+ NDS_SECURITY_CONTEXT,
+ Next );
+
+ ASSERT( pNdsContext->ntc == NW_NTC_NDS_CREDENTIAL );
+
+ if ( !puTreeName ||
+ !RtlCompareUnicodeString( puTreeName,
+ &pNdsContext->NdsTreeName,
+ TRUE ) ) {
+
+ //
+ // If the tree name is null, we'll return the first one
+ // on the list. Otherwise this will work as normal.
+ //
+
+ *ppCredentials = pNdsContext;
+ return STATUS_SUCCESS;
+ }
+
+ pNext = pNdsContext->Next.Flink;
+
+ }
+
+ //
+ // We didn't find the credential. Should we create it?
+ //
+
+ NwReleaseCredList( pLogon );
+
+ if ( !fCreate || !puTreeName ) {
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ //
+ // Acquire exclusive since we're mucking with the list.
+ //
+
+ NwAcquireExclusiveCredList( pLogon );
+
+ pNdsContext = ( PNDS_SECURITY_CONTEXT )
+ ALLOCATE_POOL( PagedPool, sizeof( NDS_SECURITY_CONTEXT ) );
+
+ if ( !pNdsContext ) {
+
+ DebugTrace( 0, Dbg, "Out of memory in NdsLookupCredentials.\n", 0 );
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto UnlockAndExit;
+ }
+
+ //
+ // Initialize the structure.
+ //
+
+ RtlZeroMemory( pNdsContext, sizeof( NDS_SECURITY_CONTEXT ) );
+ pNdsContext->ntc = NW_NTC_NDS_CREDENTIAL;
+ pNdsContext->nts = sizeof( NDS_SECURITY_CONTEXT );
+
+ //
+ // Initialize the tree name.
+ //
+
+ pNdsContext->NdsTreeName.MaximumLength = sizeof( pNdsContext->NdsTreeNameBuffer );
+ pNdsContext->NdsTreeName.Buffer = pNdsContext->NdsTreeNameBuffer;
+
+ RtlCopyUnicodeString( &pNdsContext->NdsTreeName, puTreeName );
+
+ //
+ // Initialize the context buffer.
+ //
+
+ pNdsContext->CurrentContext.Length = 0;
+ pNdsContext->CurrentContext.MaximumLength = sizeof( pNdsContext->CurrentContextString );
+ pNdsContext->CurrentContext.Buffer = pNdsContext->CurrentContextString;
+
+ //
+ // Insert the context into the list.
+ //
+
+ InsertHeadList( &pLogon->NdsCredentialList, &pNdsContext->Next );
+ *ppCredentials = pNdsContext;
+
+ //
+ // Release and re-acquire with the correct permissions.
+ //
+
+ NwReleaseCredList( pLogon );
+
+ if ( dwDesiredAccess == CREDENTIAL_READ ) {
+ NwAcquireSharedCredList( pLogon );
+ } else {
+ NwAcquireExclusiveCredList( pLogon );
+ }
+
+ //
+ // There's no chance that someone's going to come in during this
+ // small window and do a logout because there's no login data
+ // in the credentials.
+ //
+
+ return STATUS_SUCCESS;
+
+UnlockAndExit:
+
+ NwReleaseCredList( pLogon );
+ return STATUS_UNSUCCESSFUL;
+
+}
+
+NTSTATUS
+NdsGetCredentials(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PLOGON pLogon,
+ IN PUNICODE_STRING puUserName,
+ IN PUNICODE_STRING puPassword
+)
+/*++
+
+ Do an NDS tree login and aquire a valid set of credentials.
+
+--*/
+{
+ NTSTATUS Status;
+
+ USHORT i;
+ UNICODE_STRING LoginName, LoginPassword;
+ PWCHAR NdsName;
+ PWCHAR NdsPassword;
+
+ OEM_STRING OemPassword;
+ PBYTE OemPassBuffer;
+ PNDS_SECURITY_CONTEXT pNdsContext;
+
+ PAGED_CODE();
+
+ //
+ // Prepare our login name by canonicalizing the supplied user
+ // name or using a default user name if appropriate.
+ //
+
+ NdsName = ALLOCATE_POOL( PagedPool, MAX_NDS_NAME_SIZE +
+ MAX_PW_CHARS * sizeof( WCHAR ) +
+ MAX_PW_CHARS );
+
+ if ( !NdsName ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ NdsPassword = (PWCHAR) (((BYTE *) NdsName) + MAX_NDS_NAME_SIZE );
+ OemPassBuffer = ((BYTE *) NdsPassword ) + ( MAX_PW_CHARS * sizeof( WCHAR ) );
+
+ LoginName.Length = 0;
+ LoginName.MaximumLength = MAX_NDS_NAME_SIZE;
+ LoginName.Buffer = NdsName;
+
+ Status = NdsLookupCredentials( &pIrpContext->pScb->NdsTreeName,
+ pLogon,
+ &pNdsContext,
+ CREDENTIAL_READ,
+ TRUE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // If the credential list is locked, someone is logging
+ // out and we have to fail the request.
+ //
+
+ if ( pNdsContext->CredentialLocked ) {
+
+ Status = STATUS_DEVICE_BUSY;
+ NwReleaseCredList( pLogon );
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Fix up the user name.
+ // ALERT! We are holding the credential list!
+ //
+
+ if ( puUserName && puUserName->Buffer ) {
+
+ Status = NdsCanonUserName( pNdsContext,
+ puUserName,
+ &LoginName );
+
+ if ( !NT_SUCCESS( Status )) {
+ Status = STATUS_NO_SUCH_USER;
+ }
+
+ } else {
+
+ //
+ // There's no name, so try the default name in the
+ // current context.
+ //
+
+ if ( pNdsContext->CurrentContext.Length > 0 ) {
+
+ //
+ // Make sure the lengths fit and all that.
+ //
+
+ if ( ( pLogon->UserName.Length +
+ pNdsContext->CurrentContext.Length ) >= LoginName.MaximumLength ) {
+
+ Status = STATUS_INVALID_PARAMETER;
+ goto NameResolved;
+ }
+
+ RtlCopyMemory( LoginName.Buffer, pLogon->UserName.Buffer, pLogon->UserName.Length );
+ LoginName.Buffer[pLogon->UserName.Length / sizeof( WCHAR )] = L'.';
+
+ RtlCopyMemory( ((BYTE *)LoginName.Buffer) + pLogon->UserName.Length + sizeof( WCHAR ),
+ pNdsContext->CurrentContext.Buffer,
+ pNdsContext->CurrentContext.Length );
+
+ LoginName.Length = pLogon->UserName.Length +
+ pNdsContext->CurrentContext.Length +
+ sizeof( WCHAR );
+
+ DebugTrace( 0, Dbg, "Using default name and context for login: %wZ\n", &LoginName );
+
+ } else {
+ Status = STATUS_NO_SUCH_USER;
+ }
+
+ }
+
+NameResolved:
+
+ NwReleaseCredList( pLogon );
+
+ //
+ // RELAX! The credential list is free.
+ //
+
+ if ( !NT_SUCCESS( Status ) ) {
+ DebugTrace( 0, Dbg, "No name in NdsGetCredentials.\n", 0 );
+ goto ExitWithCleanup;
+ }
+
+ //
+ // If there's a password, use it. Otherwise, use the default password.
+ //
+
+ if ( puPassword && puPassword->Buffer ) {
+
+ LoginPassword.Length = puPassword->Length;
+ LoginPassword.MaximumLength = puPassword->MaximumLength;
+ LoginPassword.Buffer = puPassword->Buffer;
+
+ } else {
+
+ LoginPassword.Length = 0;
+ LoginPassword.MaximumLength = MAX_PW_CHARS * sizeof( WCHAR );
+ LoginPassword.Buffer = NdsPassword;
+
+ RtlCopyUnicodeString( &LoginPassword,
+ &pLogon->PassWord );
+ }
+
+ //
+ // Convert the password to upcase OEM and login.
+ //
+
+ OemPassword.Length = 0;
+ OemPassword.MaximumLength = MAX_PW_CHARS;
+ OemPassword.Buffer = OemPassBuffer;
+
+ Status = RtlUpcaseUnicodeStringToOemString( &OemPassword,
+ &LoginPassword,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status )) {
+ Status = STATUS_WRONG_PASSWORD;
+ goto ExitWithCleanup;
+ }
+
+ Status = NdsTreeLogin( pIrpContext, &LoginName, &OemPassword, NULL, pLogon );
+
+ExitWithCleanup:
+
+ FREE_POOL( NdsName );
+ return Status;
+}
+
+NTSTATUS
+DoNdsLogon(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING UserName,
+ IN PUNICODE_STRING Password
+)
+/*+++
+
+Description:
+
+ This is the lead function for handling login and authentication to
+ Netware Directory Services. This function acquires credentials to
+ the appropriate tree for the server that the irp context points to,
+ logging us into that tree if necessary, and authenticates us to the
+ current server.
+
+ BUGBUG: This routine gets called from reconnect attempts and from
+ normal requests. Since the allowable actions on each of these paths
+ are different, it might make sense to have two routines, each
+ more maintainable than this single routine. For now, watch out for
+ code in the RECONNECT_ATTEMPT cases.
+
+Arguments:
+
+ pIrpContext - irp context; must refer to appropriate server
+ UserName - login username
+ Password - password
+
+--*/
+{
+
+ NTSTATUS Status;
+ PLOGON pLogon;
+ PNDS_SECURITY_CONTEXT pCredentials;
+ PSCB pScb;
+ UNICODE_STRING BinderyName;
+ UNICODE_STRING uUserName;
+ UNICODE_STRING NtGroup;
+ USHORT Length;
+ PSCB pOriginalServer = NULL;
+ DWORD UserOID;
+
+ BOOL AtHeadOfQueue = FALSE;
+ BOOL HoldingCredentialResource = FALSE;
+ BOOL PasswordExpired = FALSE;
+
+ PAGED_CODE();
+
+ //
+ // Get to the head of the queue if we need to.
+ //
+
+ if ( BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT ) ) {
+ ASSERT( pIrpContext->pNpScb->Requests.Flink == &pIrpContext->NextRequest );
+ } else {
+ NwAppendToQueueAndWait( pIrpContext );
+ }
+
+ AtHeadOfQueue = TRUE;
+
+ //
+ // Grab the user's logon structure.
+ //
+
+ pScb = pIrpContext->pScb;
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ pLogon = FindUser( &pScb->UserUid, FALSE );
+ NwReleaseRcb( &NwRcb );
+
+ if ( !pLogon ) {
+
+ DebugTrace( 0, Dbg, "Invalid client security context.\n", 0 );
+ Status = STATUS_ACCESS_DENIED;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Login and then re-get the tree credentials.
+ //
+
+ Status = NdsLookupCredentials( &pScb->NdsTreeName,
+ pLogon,
+ &pCredentials,
+ CREDENTIAL_READ,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ HoldingCredentialResource = FALSE;
+ goto LOGIN;
+ }
+
+ HoldingCredentialResource = TRUE;
+
+ //
+ // Are we logged in? We can't hold the
+ // credential list while logging in!!
+ //
+
+ if ( !pCredentials->Credential ) {
+
+ HoldingCredentialResource = FALSE;
+ NwReleaseCredList( pLogon );
+ goto LOGIN;
+ }
+
+ //
+ // If this credential is locked, we fail!
+ //
+
+ if ( pCredentials->CredentialLocked ) {
+ Status = STATUS_DEVICE_BUSY;
+ goto ExitWithCleanup;
+ }
+
+ Status = NdsCheckCredentialsEx( pIrpContext,
+ pLogon,
+ pCredentials,
+ UserName,
+ Password );
+
+ if( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ goto AUTHENTICATE;
+
+LOGIN:
+
+ ASSERT( HoldingCredentialResource == FALSE );
+
+ //
+ // If this is a reconnect attempt and we don't have credentials
+ // already, we have to give up. We can't acquire credentials
+ // during a reconnect and retry because it could cause a deadlock.
+ //
+
+ if ( BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT ) ) {
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+ }
+
+ Status = NdsGetCredentials( pIrpContext,
+ pLogon,
+ UserName,
+ Password );
+
+ if ( !NT_SUCCESS( Status )) {
+ goto ExitWithCleanup;
+ }
+
+ if ( Status == NWRDR_PASSWORD_HAS_EXPIRED ) {
+ PasswordExpired = TRUE;
+ }
+
+ Status = NdsLookupCredentials( &pScb->NdsTreeName,
+ pLogon,
+ &pCredentials,
+ CREDENTIAL_READ,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // If this credential is locked, someone is
+ // already logging out and so we fail this.
+ //
+
+ if ( pCredentials->CredentialLocked ) {
+ Status = STATUS_DEVICE_BUSY;
+ NwReleaseCredList( pLogon );
+ goto ExitWithCleanup;
+ }
+
+ HoldingCredentialResource = TRUE;
+
+AUTHENTICATE:
+
+ ASSERT( HoldingCredentialResource == TRUE );
+ ASSERT( AtHeadOfQueue == TRUE );
+
+ //
+ // NdsServerAuthenticate will not take us off the
+ // head of the queue since this is not allowed.
+ //
+
+ Status = NdsServerAuthenticate( pIrpContext, pCredentials );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ NwReleaseCredList( pLogon );
+ HoldingCredentialResource = FALSE;
+
+ //
+ // If this is a gateway request and is not a reconnect attempt, we
+ // need to check the group membership.
+ //
+
+ if ( pIrpContext->Specific.Create.UserUid.QuadPart == DefaultLuid.QuadPart) {
+
+ if ( !BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT ) ) {
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ AtHeadOfQueue = FALSE;
+
+ //
+ // Resolve the name, allowing a server jump if necessary.
+ //
+
+ pOriginalServer = pIrpContext->pScb;
+ NwReferenceScb( pOriginalServer->pNpScb );
+
+ uUserName.MaximumLength = pCredentials->Credential->userNameLength;
+ uUserName.Length = uUserName.MaximumLength;
+ uUserName.Buffer = ( WCHAR * ) ( ((BYTE *)pCredentials->Credential) +
+ sizeof( NDS_CREDENTIAL ) +
+ pCredentials->Credential->optDataSize );
+
+ Status = NdsResolveNameKm( pIrpContext,
+ &uUserName,
+ &UserOID,
+ TRUE,
+ DEFAULT_RESOLVE_FLAGS );
+
+ if ( NT_SUCCESS(Status) ) {
+
+ RtlInitUnicodeString( &NtGroup, NT_GATEWAY_GROUP );
+ Status = NdsCheckGroupMembership( pIrpContext, UserOID, &NtGroup );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ DebugTrace( 0, Dbg, "Gateway connection to NDS server not allowed.\n", 0 );
+ Status = STATUS_ACCESS_DENIED;
+ }
+ }
+
+ //
+ // Take us off the head of the queue in case were were left there.
+ //
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ //
+ // Restore us to the server that we authenticated to so that
+ // the irp context refers to the authenticated server.
+ //
+
+ if ( pOriginalServer != NULL ) {
+
+ NwDereferenceScb( pIrpContext->pNpScb );
+
+ if ( pIrpContext->pScb != pOriginalServer ) {
+ pIrpContext->pScb = pOriginalServer;
+ pIrpContext->pNpScb = pOriginalServer->pNpScb;
+ }
+ }
+
+ }
+ }
+
+ExitWithCleanup:
+
+ if ( HoldingCredentialResource ) {
+ NwReleaseCredList( pLogon );
+ }
+
+ if ( AtHeadOfQueue ) {
+
+ //
+ // If we failed and this was a reconnect attempt, don't dequeue the
+ // irp context or we may deadlock when we try to do the bindery logon.
+ // See ReconnectRetry() for more information on this restriction.
+ //
+
+ if ( !BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT ) ) {
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ }
+
+ }
+
+ if ( ( NT_SUCCESS( Status ) ) &&
+ ( PasswordExpired ) ) {
+ Status = NWRDR_PASSWORD_HAS_EXPIRED;
+ }
+
+ DebugTrace( 0, Dbg, "DoNdsLogin: Status = %08lx\n", Status );
+ return Status;
+}
+
+NTSTATUS
+NdsTreeLogin(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING puUser,
+ IN POEM_STRING pOemPassword,
+ IN POEM_STRING pOemNewPassword,
+ IN PLOGON pUserLogon
+)
+/*++
+
+Routine Description:
+
+ Login the specified user to the NDS tree at the server referred
+ to by the given IrpContext using the supplied password.
+
+Arguments:
+
+ pIrpContext - The irp context for this server connection.
+ puUser - The user login name.
+ pOemPassword - The upper-case, plaintext password.
+ pOemNewPassword - The new password for a change pass request.
+ pUserLogon - The LOGON security structure for this user,
+ which may be NULL for a change password
+ request.
+
+Side Effects:
+
+ If successful, the user's credentials, signature, and
+ public key are saved in the nds context for this NDS tree
+ in the credential list in the LOGON structure.
+
+Notes:
+
+ This function may have to jump around a few servers to
+ get all the info needed for login. If restores the irp
+ context to the original server so that when we authenticate,
+ we authenticate to the correct server (as requested by the
+ user).
+
+--*/
+{
+ NTSTATUS Status; // status of the operation
+ int CryptStatus; // crypt status
+
+ DWORD dwChallenge; // four byte server challenge
+ PUNICODE_STRING puServerName; // server's distinguished name
+
+ DWORD dwUserOID, // user oid on the current server
+ dwSrcUserOID, // user oid on the originating server
+ dwServerOID; // server oid
+
+ BYTE *pbServerPublicNdsKey, // server's public key in NDS format
+ *pbServerPublicBsafeKey; // server's public BSAFE key
+
+ int cbServerPublicNdsKeyLen, // length of server public NDS key
+ cbServerPublicBsafeKeyLen; // length of server pubilc BSAFE key
+
+ BYTE *pbUserPrivateNdsKey, // user's private key in NDS format
+ *pbUserPrivateBsafeKey; // user's private BSAFE key
+
+ int cbUserPrivateNdsKeyLen; // length of user private NDS key
+ WORD cbUserPrivateBsafeKeyLen; // length of user private BSAFE key
+
+ BYTE pbNw3PasswdHash[16]; // nw3 passwd hash
+ BYTE pbNewPasswdHash[16]; // new passwd hash for change pass
+ BYTE pbPasswdHashRC2Key[8]; // RC2 secret key generated from hash
+
+ BYTE pbEncryptedChallenge[16]; // RC2 encrypted server challenge
+ int cbEncryptedChallengeLen; // length of the encrypted challenge
+
+ PNDS_SECURITY_CONTEXT psNdsSecurityContext; // user's nds context
+ BYTE *pbSignData; // user's signature data
+
+ UNICODE_STRING uUserDN; // users fully distinguished name
+ PWCHAR UserDnBuffer;
+
+ DWORD dwValidityStart, dwValidityEnd;
+ BOOLEAN AtHeadOfQueue = FALSE;
+ BOOLEAN HoldingCredResource = FALSE;
+ BOOLEAN PasswordExpired = FALSE;
+
+ UNICODE_STRING PlainServerName;
+ USHORT UidLen;
+ KIRQL OldIrql;
+ PSCB pLoginServer = NULL;
+ PSCB pOriginalServer = NULL;
+ DWORD dwLoginFlags = 0;
+
+ DebugTrace( 0, Dbg, "Enter NdsTreeLogin...\n", 0 );
+
+ ASSERT( puUser );
+ ASSERT( pOemPassword );
+
+ //
+ // Allocate space for the server's public key and the user's private key.
+ //
+
+ cbServerPublicNdsKeyLen = MAX_PUBLIC_KEY_LEN + MAX_ENC_PRIV_KEY_LEN + MAX_NDS_NAME_SIZE;
+
+ pbServerPublicNdsKey = ALLOCATE_POOL( PagedPool, cbServerPublicNdsKeyLen );
+
+ if ( !pbServerPublicNdsKey ) {
+
+ DebugTrace( 0, Dbg, "Out of memory in NdsTreeLogin...\n", 0 );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // First, jump to a server where we can get this user object.
+ // Don't forget the server to which we were originally pointed.
+ //
+
+ pOriginalServer = pIrpContext->pScb;
+ NwReferenceScb( pOriginalServer->pNpScb );
+
+ Status = NdsResolveNameKm( pIrpContext,
+ puUser,
+ &dwUserOID,
+ TRUE,
+ DEFAULT_RESOLVE_FLAGS );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ if ( Status == STATUS_BAD_NETWORK_PATH ) {
+ Status = STATUS_NO_SUCH_USER;
+ }
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Now get the user name from the object info.
+ //
+
+ UserDnBuffer = (PWCHAR) ( pbServerPublicNdsKey +
+ MAX_PUBLIC_KEY_LEN +
+ MAX_ENC_PRIV_KEY_LEN );
+
+ uUserDN.Length = 0;
+ uUserDN.MaximumLength = MAX_NDS_NAME_SIZE;
+ uUserDN.Buffer = UserDnBuffer;
+
+ Status = NdsGetUserName( pIrpContext,
+ dwUserOID,
+ &uUserDN );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Get the name of the server we are currently on. We borrow a
+ // little space from our key buffer and overwrite it later.
+ //
+
+ puServerName = ( PUNICODE_STRING ) pbServerPublicNdsKey;
+ puServerName->Buffer = (PWCHAR) pbServerPublicNdsKey + sizeof( UNICODE_STRING );
+ puServerName->MaximumLength = cbServerPublicNdsKeyLen - sizeof( UNICODE_STRING );
+
+ Status = NdsGetServerName( pIrpContext,
+ puServerName );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // If the public key for this server is on a partition that's
+ // on another server, we'll have to jump over there to get the
+ // public key and then return. The key and user object are
+ // only any good on this server! DO NOT CHANGE THE ORDER OF
+ // THIS OR IT WILL BREAK!
+ //
+
+ pLoginServer = pIrpContext->pScb;
+ NwReferenceScb( pLoginServer->pNpScb );
+
+ Status = NdsResolveNameKm( pIrpContext,
+ puServerName,
+ &dwServerOID,
+ TRUE,
+ DEFAULT_RESOLVE_FLAGS );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Get the server's public key and its length.
+ //
+
+ Status = NdsReadPublicKey( pIrpContext,
+ dwServerOID,
+ pbServerPublicNdsKey,
+ &cbServerPublicNdsKeyLen );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Return us to the login server.
+ //
+
+ if ( pLoginServer != pIrpContext->pScb ) {
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ NwDereferenceScb( pIrpContext->pNpScb );
+ pIrpContext->pScb = pLoginServer;
+ pIrpContext->pNpScb = pLoginServer->pNpScb;
+
+ } else {
+
+ NwDereferenceScb( pLoginServer->pNpScb );
+ }
+
+ pLoginServer = NULL;
+
+ //
+ // Locate the BSAFE key in the NDS key.
+ //
+
+ cbServerPublicBsafeKeyLen = NdsGetBsafeKey( pbServerPublicNdsKey,
+ cbServerPublicNdsKeyLen,
+ &pbServerPublicBsafeKey );
+
+ if ( !cbServerPublicBsafeKeyLen ) {
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Send the begin login packet. This returns to us the
+ // 4 byte challenge and the object id of the user's account
+ // on the server on which it was created. It may be the
+ // same as the object id that we provided if the account
+ // was created on this server.
+ //
+
+ Status = BeginLogin( pIrpContext,
+ dwUserOID,
+ &dwSrcUserOID,
+ &dwChallenge );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Compute the 16 byte NW3 hash and generate the
+ // 8 byte secret key from it. The 8 byte secret
+ // key consists of a MAC checksum of the NW3 hash.
+ //
+
+ Shuffle( (UCHAR *)&dwSrcUserOID,
+ pOemPassword->Buffer,
+ pOemPassword->Length,
+ pbNw3PasswdHash );
+
+ GenKey8( pbNw3PasswdHash,
+ sizeof( pbNw3PasswdHash ),
+ pbPasswdHashRC2Key );
+
+ //
+ // RC2 Encrypt the 4 byte challenge using the secret
+ // key generated from the password.
+ //
+
+ CryptStatus = CBCEncrypt( pbPasswdHashRC2Key,
+ NULL,
+ (BYTE *)&dwChallenge,
+ 4,
+ pbEncryptedChallenge,
+ &cbEncryptedChallengeLen,
+ BSAFE_CHECKSUM_LEN );
+
+ if ( CryptStatus ) {
+
+ DebugTrace( 0, Dbg, "CBC encryption failed.\n", 0 );
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+ }
+
+ pbUserPrivateNdsKey = pbServerPublicNdsKey + MAX_PUBLIC_KEY_LEN;
+ cbUserPrivateNdsKeyLen = MAX_ENC_PRIV_KEY_LEN;
+
+ //
+ // Make the finish login packet. If successful, this routine
+ // returns the encrypted user private key and the valid duration
+ // of the user's credentials.
+ //
+
+ if ( pOemNewPassword ) {
+ dwLoginFlags = 1;
+ }
+
+ Status = FinishLogin( pIrpContext,
+ dwUserOID,
+ dwLoginFlags,
+ pbEncryptedChallenge,
+ pbServerPublicBsafeKey,
+ cbServerPublicBsafeKeyLen,
+ pbUserPrivateNdsKey,
+ &cbUserPrivateNdsKeyLen,
+ &dwValidityStart,
+ &dwValidityEnd );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ if ( !pOemNewPassword ) {
+
+ //
+ // If the password is expired, report it to the user.
+ //
+
+ if ( Status == NWRDR_PASSWORD_HAS_EXPIRED ) {
+ PasswordExpired = TRUE;
+ }
+
+ //
+ // Allocate the credential and set up space for the private key.
+ //
+
+ NwAppendToQueueAndWait( pIrpContext );
+ AtHeadOfQueue = TRUE;
+
+ Status = NdsLookupCredentials( &pIrpContext->pScb->NdsTreeName,
+ pUserLogon,
+ &psNdsSecurityContext,
+ CREDENTIAL_WRITE,
+ TRUE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // ALERT! We are holding the credential list.
+ //
+
+ HoldingCredResource = TRUE;
+
+ psNdsSecurityContext->Credential = ALLOCATE_POOL( PagedPool,
+ sizeof( NDS_CREDENTIAL ) +
+ uUserDN.Length );
+
+ if ( !psNdsSecurityContext->Credential ) {
+
+ DebugTrace( 0, Dbg, "Out of memory in NdsTreeLogin (for credential)...\n", 0 );
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ExitWithCleanup;
+
+ }
+
+ *( (UNALIGNED DWORD *) &( psNdsSecurityContext->Credential->validityBegin ) ) = dwValidityStart;
+ *( (UNALIGNED DWORD *) &( psNdsSecurityContext->Credential->validityEnd ) ) = dwValidityEnd;
+
+ DebugTrace( 0, Dbg, "Credential validity start: 0x%08lx\n", dwValidityStart );
+ DebugTrace( 0, Dbg, "Credential validity end: 0x%08lx\n", dwValidityEnd );
+
+ //
+ // RC2 Decrypt the response to extract the BSAFE private
+ // key data in place.
+ //
+
+ CryptStatus = CBCDecrypt( pbPasswdHashRC2Key,
+ NULL,
+ pbUserPrivateNdsKey,
+ cbUserPrivateNdsKeyLen,
+ pbUserPrivateNdsKey,
+ &cbUserPrivateNdsKeyLen,
+ BSAFE_CHECKSUM_LEN );
+
+ if ( CryptStatus ) {
+
+ DebugTrace( 0, Dbg, "CBC decryption failed.\n", 0 );
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Skip over the header.
+ //
+
+ pbUserPrivateBsafeKey = ( pbUserPrivateNdsKey + sizeof( TAG_DATA_HEADER ) );
+ cbUserPrivateBsafeKeyLen = *( ( WORD * ) pbUserPrivateBsafeKey );
+ pbUserPrivateBsafeKey += sizeof( WORD );
+
+ //
+ // Create the credential.
+ //
+
+ psNdsSecurityContext->Credential->tdh.version = 1;
+ psNdsSecurityContext->Credential->tdh.tag = TAG_CREDENTIAL;
+
+ GenRandomBytes( ( BYTE * ) &(psNdsSecurityContext->Credential->random),
+ sizeof( psNdsSecurityContext->Credential->random ) );
+
+ psNdsSecurityContext->Credential->optDataSize = 0;
+ psNdsSecurityContext->Credential->userNameLength = uUserDN.Length;
+
+ RtlCopyMemory( ( (BYTE *)psNdsSecurityContext->Credential) + sizeof( NDS_CREDENTIAL ),
+ UserDnBuffer,
+ uUserDN.Length );
+
+ //
+ // Generate and save the signature.
+ //
+
+ psNdsSecurityContext->Signature = ALLOCATE_POOL( PagedPool, MAX_SIGNATURE_LEN );
+
+ if ( !psNdsSecurityContext->Signature ) {
+
+ DebugTrace( 0, Dbg, "Out of memory in NdsTreeLogin (for signature)...\n", 0 );
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ExitWithCleanup;
+
+ }
+
+ pbSignData = ( ( ( BYTE * ) psNdsSecurityContext->Signature ) +
+ sizeof( NDS_SIGNATURE ) );
+
+ RtlZeroMemory( pbSignData, MAX_RSA_BYTES );
+
+ psNdsSecurityContext->Signature->tdh.version = 1;
+ psNdsSecurityContext->Signature->tdh.tag = TAG_SIGNATURE;
+
+ //
+ // Create the hash for the signature from the credential.
+ //
+
+ MD2( (BYTE *) psNdsSecurityContext->Credential,
+ sizeof( NDS_CREDENTIAL ) + ( uUserDN.Length ),
+ pbSignData );
+
+ //
+ // Compute the 'signature' by RSA-encrypting the
+ // 16-byte signature hash with the private key.
+ //
+
+ psNdsSecurityContext->Signature->signDataLength = RSAPrivate( pbUserPrivateBsafeKey,
+ cbUserPrivateBsafeKeyLen,
+ pbSignData,
+ 16,
+ pbSignData );
+
+ if ( !psNdsSecurityContext->Signature->signDataLength ) {
+
+ DebugTrace( 0, Dbg, "RSA private encryption for signature failed.\n", 0 );
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Round up the signature length, cause that's how VLM stores it.
+ //
+
+ psNdsSecurityContext->Signature->signDataLength =
+ ROUNDUP4( psNdsSecurityContext->Signature->signDataLength );
+
+ DebugTrace( 0, Dbg, "Signature data length: %d\n",
+ psNdsSecurityContext->Signature->signDataLength );
+
+ //
+ // Get the user's public key for storage in the nds context.
+ //
+
+ psNdsSecurityContext->PublicNdsKey = ALLOCATE_POOL( PagedPool, MAX_PUBLIC_KEY_LEN );
+
+ if ( !psNdsSecurityContext->PublicNdsKey ) {
+
+ DebugTrace( 0, Dbg, "Out of memory in NdsTreeLogin (for public key)...\n", 0 );
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ExitWithCleanup;
+
+ }
+
+ psNdsSecurityContext->PublicKeyLen = MAX_PUBLIC_KEY_LEN;
+
+ ASSERT( AtHeadOfQueue );
+ ASSERT( HoldingCredResource );
+
+ Status = NdsReadPublicKey( pIrpContext,
+ dwUserOID,
+ psNdsSecurityContext->PublicNdsKey,
+ &(psNdsSecurityContext->PublicKeyLen) );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Store away the password we used to connect.
+ //
+
+ psNdsSecurityContext->Password.Buffer = ALLOCATE_POOL( PagedPool, pOemPassword->Length );
+
+ if ( !psNdsSecurityContext->Password.Buffer ) {
+
+ DebugTrace( 0, Dbg, "Out of memory in NdsTreeLogin (for password)\n", 0 );
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ExitWithCleanup;
+ }
+
+ psNdsSecurityContext->Password.Length = pOemPassword->Length;
+ psNdsSecurityContext->Password.MaximumLength = pOemPassword->Length;
+ RtlCopyMemory( psNdsSecurityContext->Password.Buffer,
+ pOemPassword->Buffer,
+ pOemPassword->Length );
+
+ //
+ // We are logged in to the NDS tree. Should we zero the private
+ // key, or is NT's protection sufficient?
+ //
+
+ NwReleaseCredList( pUserLogon );
+
+ //
+ // Try to elect this server as the preferred server if necessary.
+ //
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ if ( ( pUserLogon->ServerName.Length == 0 ) &&
+ ( !pIrpContext->Specific.Create.fExCredentialCreate ) ) {
+
+ //
+ // Strip off the unicode uid from the server name.
+ //
+
+ PlainServerName.Length = pIrpContext->pScb->UidServerName.Length;
+ PlainServerName.Buffer = pIrpContext->pScb->UidServerName.Buffer;
+
+ UidLen = 0;
+
+ while ( UidLen < ( PlainServerName.Length / sizeof( WCHAR ) ) ) {
+
+ if ( PlainServerName.Buffer[UidLen++] == L'\\' ) {
+ break;
+ }
+ }
+
+ PlainServerName.Buffer += UidLen;
+ PlainServerName.Length -= ( UidLen * sizeof( WCHAR ) );
+ PlainServerName.MaximumLength = PlainServerName.Length;
+
+ if ( PlainServerName.Length ) {
+
+ Status = SetUnicodeString( &(pUserLogon->ServerName),
+ PlainServerName.Length,
+ PlainServerName.Buffer );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ DebugTrace( 0, Dbg, "Electing preferred server: %wZ\n", &PlainServerName );
+
+ //
+ // Increase the Scb ref count, set the preferred server flag,
+ // and move the scb to the head of the SCB list.
+ //
+
+ NwReferenceScb( pIrpContext->pScb->pNpScb );
+ pIrpContext->pScb->PreferredServer = TRUE;
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ RemoveEntryList( &(pIrpContext->pScb->pNpScb->ScbLinks) );
+ InsertHeadList( &ScbQueue, &(pIrpContext->pScb->pNpScb->ScbLinks) );
+
+ KeReleaseSpinLock(&ScbSpinLock, OldIrql);
+
+ }
+ }
+ }
+
+ } else {
+
+ //
+ // This isn't a login, but a change password request.
+ //
+ // First we have to RC2 Decrypt the response to extract
+ // the BSAFE private key data in place (just like for a
+ // login).
+ //
+
+ CryptStatus = CBCDecrypt( pbPasswdHashRC2Key,
+ NULL,
+ pbUserPrivateNdsKey,
+ cbUserPrivateNdsKeyLen,
+ pbUserPrivateNdsKey,
+ &cbUserPrivateNdsKeyLen,
+ BSAFE_CHECKSUM_LEN );
+
+ if ( CryptStatus ) {
+
+ DebugTrace( 0, Dbg, "CBC decryption failed.\n", 0 );
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Now, compute the hash of the new password.
+ //
+
+ Shuffle( (UCHAR *)&dwSrcUserOID,
+ pOemNewPassword->Buffer,
+ pOemNewPassword->Length,
+ pbNewPasswdHash );
+
+ //
+ // And finally, make the request.
+ //
+
+ Status = ChangeNdsPassword( pIrpContext,
+ dwUserOID,
+ dwChallenge,
+ pbNw3PasswdHash,
+ pbNewPasswdHash,
+ ( PNDS_PRIVATE_KEY ) pbUserPrivateNdsKey,
+ pbServerPublicBsafeKey,
+ cbServerPublicBsafeKeyLen );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ DebugTrace( 0, Dbg, "Change NDS password failed!\n", 0 );
+ goto ExitWithCleanup;
+ }
+
+ }
+
+ //
+ // Return us to our original server if we've jumped around.
+ //
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ if ( pIrpContext->pScb != pOriginalServer ) {
+
+ NwDereferenceScb( pIrpContext->pNpScb );
+ pIrpContext->pScb = pOriginalServer;
+ pIrpContext->pNpScb = pOriginalServer->pNpScb;
+
+ } else {
+
+ NwDereferenceScb( pOriginalServer->pNpScb );
+ }
+
+ pOriginalServer = NULL;
+
+ if ( !pOemNewPassword ) {
+ NwReleaseRcb( &NwRcb );
+ }
+
+ FREE_POOL( pbServerPublicNdsKey );
+
+ if ( PasswordExpired ) {
+ Status = NWRDR_PASSWORD_HAS_EXPIRED;
+ } else {
+ Status = STATUS_SUCCESS;
+ }
+
+ return Status;
+
+ExitWithCleanup:
+
+ DebugTrace( 0, Dbg, "NdsTreeLogin seems to have failed... cleaning up.\n", 0 );
+
+ FREE_POOL( pbServerPublicNdsKey );
+
+ if ( pLoginServer ) {
+ NwDereferenceScb( pLoginServer->pNpScb );
+ }
+
+ //
+ // If we failed after jumping servers, we have to restore
+ // the irp context to the original server.
+ //
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ if ( pOriginalServer ) {
+
+ if ( pIrpContext->pScb != pOriginalServer ) {
+
+ NwDereferenceScb( pIrpContext->pNpScb );
+ pIrpContext->pScb = pOriginalServer;
+ pIrpContext->pNpScb = pOriginalServer->pNpScb;
+
+ } else {
+
+ NwDereferenceScb( pOriginalServer->pNpScb );
+ }
+
+ }
+
+ if ( HoldingCredResource ) {
+
+ if ( psNdsSecurityContext->Credential ) {
+ FREE_POOL( psNdsSecurityContext->Credential );
+ psNdsSecurityContext->Credential = NULL;
+ }
+
+ if ( psNdsSecurityContext->Signature ) {
+ FREE_POOL( psNdsSecurityContext->Signature );
+ psNdsSecurityContext->Signature = NULL;
+ }
+
+ if ( psNdsSecurityContext->PublicNdsKey ) {
+ FREE_POOL( psNdsSecurityContext->PublicNdsKey );
+ psNdsSecurityContext->PublicNdsKey = NULL;
+ psNdsSecurityContext->PublicKeyLen = 0;
+ }
+
+ NwReleaseCredList( pUserLogon );
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+BeginLogin(
+ IN PIRP_CONTEXT pIrpContext,
+ IN DWORD userId,
+ OUT DWORD *loginId,
+ OUT DWORD *challenge
+)
+/*++
+
+Routine Desription:
+
+ Begin the NDS login process. The loginId returned is objectId of the user
+ on the server which created the account (may not be the current server).
+
+Arguments:
+
+ pIrpContext - The IRP context for this connection.
+ userId - The user's NDS object Id.
+ loginId - The objectId used to encrypt password.
+ challenge - The 4 byte random challenge.
+
+Return value:
+
+ NTSTATUS - The result of the operation.
+
+--*/
+{
+
+ NTSTATUS Status;
+ LOCKED_BUFFER NdsRequest;
+
+ PAGED_CODE();
+
+ DebugTrace( 0, Dbg, "Enter BeginLogin...\n", 0 );
+
+ Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // Announce myself.
+ //
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_BEGIN_LOGIN,
+ &NdsRequest,
+ "DD",
+ 0,
+ userId );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ Status = NdsCompletionCodetoNtStatus( &NdsRequest );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ if ( Status == STATUS_BAD_NETWORK_PATH ) {
+ Status = STATUS_NO_SUCH_USER;
+ }
+
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Get the object id and the challenge string.
+ //
+
+ Status = ParseResponse( NULL,
+ NdsRequest.pRecvBufferVa,
+ NdsRequest.dwBytesWritten,
+ "G_DD",
+ sizeof( DWORD ),
+ loginId,
+ challenge );
+
+ if ( NT_SUCCESS( Status ) ) {
+ DebugTrace( 0, Dbg, "Login 4 byte challenge: 0x%08lx\n", *challenge );
+ } else {
+ DebugTrace( 0, Dbg, "Begin login failed...\n", 0 );
+ }
+
+ExitWithCleanup:
+
+ NdsFreeLockedBuffer( &NdsRequest );
+ return Status;
+
+}
+
+NTSTATUS
+FinishLogin(
+ IN PIRP_CONTEXT pIrpContext,
+ IN DWORD dwUserOID,
+ IN DWORD dwLoginFlags,
+ IN BYTE pbEncryptedChallenge[16],
+ IN BYTE *pbServerPublicBsafeKey,
+ IN int cbServerPublicBsafeKeyLen,
+ OUT BYTE *pbUserEncPrivateNdsKey,
+ OUT int *pcbUserEncPrivateNdsKeyLen,
+ OUT DWORD *pdwCredentialStartTime,
+ OUT DWORD *pdwCredentialEndTime
+)
+/*++
+
+Routine Description:
+
+ Constructs and sends the Finish Login request to the server.
+
+Arguments:
+
+ pIrpContext - (IN) IRP context for this request
+ dwUserOID - (IN) user's NDS object Id
+ pbEncryptedChallenge - (IN) RC2 encrypted challenge
+ pbServerPublicBsafeKey - (IN) server public bsafe key
+ cbServerPublicBsafeKeyLen - (IN) length of server public key
+
+ pbUserEncPrivateNdsKey - (OUT) user's encrypted private nds key
+ pcbUserEncPrivateNdsKeyLen - (OUT) length of pbUserEncPrivateNdsKey
+ pdwCredentialStartTime - (OUT) validity start time for credentials
+ pdwCredentialEndTime - (OUT) validity end time for credentials
+
+--*/
+{
+ NTSTATUS Status;
+
+ const int cbEncryptedChallengeLen = 16;
+
+ int LOG_DATA_POOL_SIZE, // pool sizes for our allocation call
+ PACKET_POOL_SIZE,
+ RESP_POOL_SIZE;
+
+ BYTE *pbRandomBytes; // random bytes used in crypto routines
+ BYTE RandRC2SecretKey[RC2_KEY_LEN]; // random RC2 key generated from above
+ BYTE pbEncryptedChallengeKey[RC2_KEY_LEN]; // RC2 key that will decode the response
+
+ NDS_RAND_BYTE_BLOCK *psRandByteBlock;
+
+ ENC_BLOCK_HDR *pbEncRandSeedHead; // header for encrypted random RC2 key seed
+ BYTE *pbEncRandSeed; // encrypted random seed
+ int cbPackedRandSeedLen; // length of the packed rand seed bytes
+
+ ENC_BLOCK_HDR *pbEncChallengeHead; // header for encrypted challenge
+
+ ENC_BLOCK_HDR *pbEncLogDataHead; // header for encrypted login data
+ BYTE *pbEncLogData; // encrypted login data
+ int cbEncLogDataLen; // length of the encrypted login data
+
+ ENC_BLOCK_HDR *pbEncServerRespHead; // header for encrypted response
+ BYTE *pbEncServerResp; // encrypted response
+
+ int CryptStatus, // crypt call status
+ CryptLen, // length of encrypted data
+ RequestPacketLen, // length of the request packet data
+ cbServerRespLen; // server response length after decryption
+
+ BYTE *pbServerResponse; // response from the server
+ DWORD cbEncServerRespLen; // server response length before decryption
+
+ DWORD EncKeyLen; // length of the encrypted private key
+ ENC_BLOCK_HDR *pbPrivKeyHead; // encryption header of the private key
+
+ LOCKED_BUFFER NdsRequest; // fragex locked buffer
+ BOOL PasswordExpired = FALSE;
+
+ PAGED_CODE();
+
+ DebugTrace( 0, Dbg, "Enter FinishLogin...\n", 0 );
+
+ //
+ // Allocate working space. The login data pool starts at
+ // pbRandomBytes. The packet data starts at pbEncRandSeedHead.
+ // The server response pool starts at pbServerResponse.
+ //
+
+ //
+ // BUGBUG: The alignment of these structures may possibly be wrong on
+ // quad aligned machines; check out a hardware independent fix.
+ //
+
+ LOG_DATA_POOL_SIZE = RAND_KEY_DATA_LEN + // 28 bytes for random seed
+ sizeof ( NDS_RAND_BYTE_BLOCK ) + // login data random header
+ sizeof ( ENC_BLOCK_HDR ) + // header for encrypted challenge
+ cbEncryptedChallengeLen + // data for encrypted challenge
+ 8; // padding
+ LOG_DATA_POOL_SIZE = ROUNDUP4( LOG_DATA_POOL_SIZE );
+
+ PACKET_POOL_SIZE = 2048; // packet buffer size
+ RESP_POOL_SIZE = 2048; // packet buffer size
+
+ pbRandomBytes = ALLOCATE_POOL( PagedPool,
+ LOG_DATA_POOL_SIZE +
+ PACKET_POOL_SIZE +
+ RESP_POOL_SIZE );
+
+ if ( !pbRandomBytes ) {
+
+ DebugTrace( 0, Dbg, "Out of memory in FinishLogin (main block)...\n", 0 );
+ return STATUS_INSUFFICIENT_RESOURCES;
+
+ }
+
+ pbEncRandSeedHead = ( PENC_BLOCK_HDR ) ( pbRandomBytes + LOG_DATA_POOL_SIZE );
+ pbServerResponse = ( pbRandomBytes + LOG_DATA_POOL_SIZE + PACKET_POOL_SIZE );
+
+ //
+ // Start working on the login data. As is common in the crypto world, we
+ // generate a random seed and then make a key from it to be used with a
+ // bulk cipher algorithm. In Netware land, we use MAC to make an 8 byte
+ // key from the random seed and use 64bit RC2 as our bulk cipher. We then
+ // RSA encrypt the seed using the server's public RSA key and use the bulk
+ // cipher to encrypt the rest of our login data.
+ //
+ // Since Novell uses 64bit RC2, the security isn't great.
+ //
+
+ GenRandomBytes( pbRandomBytes, RAND_KEY_DATA_LEN );
+ GenKey8( pbRandomBytes, RAND_KEY_DATA_LEN, RandRC2SecretKey );
+
+ //
+ // Now work on the actual packet data. Create the header for the
+ // encrypted random seed and pack the seed into it.
+ //
+
+ pbEncRandSeed = ( ( BYTE * )pbEncRandSeedHead ) + sizeof( ENC_BLOCK_HDR );
+
+ pbEncRandSeedHead->cipherLength = RSAGetInputBlockSize( pbServerPublicBsafeKey,
+ cbServerPublicBsafeKeyLen );
+
+ cbPackedRandSeedLen = RSAPack( pbRandomBytes,
+ RAND_KEY_DATA_LEN,
+ pbEncRandSeed,
+ pbEncRandSeedHead->cipherLength );
+ //
+ // We should have packed exactly one block.
+ //
+
+ if( cbPackedRandSeedLen != pbEncRandSeedHead->cipherLength ) {
+ DebugTrace( 0, Dbg, "RSAPack didn't pack exactly one block!\n", 0 );
+ }
+
+ pbEncRandSeedHead->cipherLength = RSAPublic( pbServerPublicBsafeKey,
+ cbServerPublicBsafeKeyLen,
+ pbEncRandSeed,
+ pbEncRandSeedHead->cipherLength,
+ pbEncRandSeed );
+
+ if ( !pbEncRandSeedHead->cipherLength ) {
+
+ DebugTrace( 0, Dbg, "Failing in FinishLogin... encryption failed.\n", 0 );
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+
+ }
+
+ //
+ // Fill in the rest of the header for the random seed. We don't count
+ // the first DWORD in the EBH; it isn't part of the header as netware
+ // wants it, per se.
+ //
+
+ pbEncRandSeedHead->blockLength = pbEncRandSeedHead->cipherLength +
+ sizeof( ENC_BLOCK_HDR ) -
+ sizeof( DWORD );
+ pbEncRandSeedHead->version = 1;
+ pbEncRandSeedHead->encType = ENC_TYPE_RSA_PUBLIC;
+ pbEncRandSeedHead->dataLength = RAND_KEY_DATA_LEN;
+
+ //
+ // Go back to working on the login data. Fill out the rbb.
+ //
+
+ psRandByteBlock = ( PNDS_RAND_BYTE_BLOCK ) ( pbRandomBytes + RAND_KEY_DATA_LEN );
+
+ GenRandomBytes( (BYTE *) &psRandByteBlock->rand1, 4 );
+ psRandByteBlock->rand2Len = RAND_FL_DATA_LEN;
+ GenRandomBytes( (BYTE *) &psRandByteBlock->rand2[0], RAND_FL_DATA_LEN );
+
+ //
+ // Fill out the header for the encrypted challenge right after the rbb.
+ //
+
+ pbEncChallengeHead = (ENC_BLOCK_HDR *) ( ((BYTE *)psRandByteBlock) +
+ sizeof( NDS_RAND_BYTE_BLOCK ) );
+
+ pbEncChallengeHead->version = 1;
+ pbEncChallengeHead->encType = ENC_TYPE_RC2_CBC;
+ pbEncChallengeHead->dataLength = 4;
+ pbEncChallengeHead->cipherLength = cbEncryptedChallengeLen;
+ pbEncChallengeHead->blockLength = cbEncryptedChallengeLen +
+ sizeof( ENC_BLOCK_HDR ) -
+ sizeof( DWORD );
+
+ //
+ // Place the encrypted challenge immediately after its header.
+ //
+
+ RtlCopyMemory( (BYTE *)( ((BYTE *)pbEncChallengeHead) +
+ sizeof( ENC_BLOCK_HDR )),
+ pbEncryptedChallenge,
+ cbEncryptedChallengeLen );
+
+ //
+ // Prepare the RC2 key to decrypt FinishLogin response.
+ //
+
+ GenKey8( (BYTE *)( &pbEncChallengeHead->version ),
+ pbEncChallengeHead->blockLength,
+ pbEncryptedChallengeKey );
+
+ //
+ // Finish up the packet data by preparing the login data. Start
+ // with the encryption header.
+ //
+
+ pbEncLogDataHead = ( PENC_BLOCK_HDR ) ( pbEncRandSeed +
+ ROUNDUP4( pbEncRandSeedHead->cipherLength ) );
+
+ pbEncLogData = ( ( BYTE * )pbEncLogDataHead ) + sizeof( ENC_BLOCK_HDR );
+
+ pbEncLogDataHead->version = 1;
+ pbEncLogDataHead->encType = ENC_TYPE_RC2_CBC;
+ pbEncLogDataHead->dataLength = sizeof( NDS_RAND_BYTE_BLOCK ) +
+ sizeof( ENC_BLOCK_HDR ) +
+ cbEncryptedChallengeLen;
+
+ //
+ // Sanity check the packet pool for overflow.
+ //
+
+ if ( ( pbEncLogData + pbEncLogDataHead->dataLength + ( 2 * CIPHERBLOCKSIZE ) ) -
+ (BYTE *) pbEncRandSeedHead > PACKET_POOL_SIZE ) {
+
+ DebugTrace( 0, Dbg, "Packet pool overflow... I'd better fix this.\n", 0 );
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Encrypt the login data.
+ //
+
+ CryptStatus = CBCEncrypt( RandRC2SecretKey,
+ NULL,
+ (BYTE *)psRandByteBlock,
+ pbEncLogDataHead->dataLength,
+ pbEncLogData,
+ &CryptLen,
+ BSAFE_CHECKSUM_LEN );
+
+ if ( CryptStatus ) {
+
+ DebugTrace( 0, Dbg, "Encryption failure in FinishLogin...\n", 0 );
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+ }
+
+ pbEncLogDataHead->cipherLength = (WORD)CryptLen;
+ pbEncLogDataHead->blockLength = pbEncLogDataHead->cipherLength +
+ sizeof( ENC_BLOCK_HDR ) -
+ sizeof( DWORD );
+
+ //
+ // We can finally send out the finish login request! Calculate the
+ // send amount and make the request.
+ //
+
+ RequestPacketLen = ( (BYTE *) pbEncLogData + pbEncLogDataHead->cipherLength ) -
+ (BYTE *) pbEncRandSeedHead;
+
+ NdsRequest.pRecvBufferVa = pbServerResponse;
+ NdsRequest.dwRecvLen = RESP_POOL_SIZE;
+ NdsRequest.pRecvMdl = NULL;
+
+ NdsRequest.pRecvMdl = ALLOCATE_MDL( pbServerResponse,
+ RESP_POOL_SIZE,
+ FALSE,
+ FALSE,
+ NULL );
+ if ( !NdsRequest.pRecvMdl ) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ExitWithCleanup;
+ }
+
+ MmProbeAndLockPages( NdsRequest.pRecvMdl,
+ KernelMode,
+ IoWriteAccess );
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_FINISH_LOGIN,
+ &NdsRequest,
+ "DDDDDDDr",
+ 2, // Version
+ dwLoginFlags, // Flags
+ dwUserOID, // Entry ID
+ 0x494, //
+ 1, // Security Version
+ 0x20009, // Envelope ID 1
+ 0x488, // Envelope length
+ pbEncRandSeedHead, // Cipher block
+ RequestPacketLen ); // Cipher block length
+
+ MmUnlockPages( NdsRequest.pRecvMdl );
+ FREE_MDL( NdsRequest.pRecvMdl );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ Status = NdsCompletionCodetoNtStatus( &NdsRequest );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ if ( Status == NWRDR_PASSWORD_HAS_EXPIRED ) {
+ PasswordExpired = TRUE;
+ }
+
+ cbServerRespLen = NdsRequest.dwBytesWritten;
+
+ //
+ // Save the credential validity times.
+ //
+
+ Status = ParseResponse( NULL,
+ pbServerResponse,
+ cbServerRespLen,
+ "G_DD",
+ sizeof( DWORD ),
+ pdwCredentialStartTime,
+ pdwCredentialEndTime );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Grab the encryption block header for the response. This response in
+ // RC2 encrypted with the pbEncryptedChallengeKey.
+ //
+
+ pbEncServerRespHead = (ENC_BLOCK_HDR *) ( pbServerResponse +
+ ( 3 * sizeof( DWORD ) ) );
+
+ if ( pbEncServerRespHead->encType != ENC_TYPE_RC2_CBC ||
+ pbEncServerRespHead->cipherLength >
+ ( RESP_POOL_SIZE + sizeof( ENC_BLOCK_HDR ) + 12 ) ) {
+
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Decrypt the server response in place.
+ //
+
+ pbEncServerResp = ( BYTE * ) ( ( BYTE * ) pbEncServerRespHead +
+ sizeof( ENC_BLOCK_HDR ) );
+
+ CryptStatus = CBCDecrypt( pbEncryptedChallengeKey,
+ NULL,
+ pbEncServerResp,
+ pbEncServerRespHead->cipherLength,
+ pbEncServerResp,
+ &cbServerRespLen,
+ BSAFE_CHECKSUM_LEN );
+
+ if ( CryptStatus ||
+ cbServerRespLen != pbEncServerRespHead->dataLength ) {
+
+ DebugTrace( 0, Dbg, "Encryption failure (2) in FinishLogin...\n", 0 );
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Examine the first random number to make sure the server is authentic.
+ //
+
+ if ( psRandByteBlock->rand1 != * ( DWORD * ) pbEncServerResp ) {
+
+ DebugTrace( 0, Dbg, "Server failed to respond to our challenge correctly...\n", 0 );
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+
+ }
+
+ //
+ // We know things are legit, so we can extract the private key.
+ // Careful, though: don't XOR the size dword.
+ //
+
+ pbEncServerResp += sizeof( DWORD );
+ EncKeyLen = * ( DWORD * ) ( pbEncServerResp );
+
+ pbEncServerResp += sizeof( DWORD );
+ while ( EncKeyLen-- ) {
+
+ pbEncServerResp[EncKeyLen] ^= psRandByteBlock->rand2[EncKeyLen];
+ }
+
+ //
+ // Check the encryption header on the private key. Don't forget to
+ // backup to include the size dword.
+ //
+
+ pbPrivKeyHead = ( ENC_BLOCK_HDR * )( pbEncServerResp - sizeof( DWORD ) ) ;
+
+ if ( pbPrivKeyHead->encType != ENC_TYPE_RC2_CBC ) {
+
+ DebugTrace( 0, Dbg, "Bad encryption header on the private key...\n", 0 );
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+
+ }
+
+ //
+ // Finally, copy out the user's private NDS key.
+ //
+
+ if ( *pcbUserEncPrivateNdsKeyLen >= pbPrivKeyHead->cipherLength ) {
+
+ DebugTrace( 0, Dbg, "Encrypted private key len: %d\n",
+ pbPrivKeyHead->cipherLength );
+
+ RtlCopyMemory( pbUserEncPrivateNdsKey,
+ ((BYTE *)( pbPrivKeyHead )) + sizeof( ENC_BLOCK_HDR ),
+ pbPrivKeyHead->cipherLength );
+
+ *pcbUserEncPrivateNdsKeyLen = pbPrivKeyHead->cipherLength;
+
+ Status = STATUS_SUCCESS;
+
+ } else {
+
+ DebugTrace( 0, Dbg, "Encryption failure on private key in FinishLogin...\n", 0 );
+ Status = STATUS_UNSUCCESSFUL;
+
+ }
+
+ExitWithCleanup:
+
+ FREE_POOL( pbRandomBytes );
+
+ if ( ( NT_SUCCESS( Status ) ) &&
+ ( PasswordExpired ) ) {
+ Status = NWRDR_PASSWORD_HAS_EXPIRED;
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+ChangeNdsPassword(
+ PIRP_CONTEXT pIrpContext,
+ DWORD dwUserOID,
+ DWORD dwChallenge,
+ PBYTE pbOldPwHash,
+ PBYTE pbNewPwHash,
+ PNDS_PRIVATE_KEY pUserPrivKey,
+ PBYTE pServerPublicBsafeKey,
+ UINT ServerPubKeyLen
+)
+/*+++
+
+Description:
+
+ Send a change password packet. Change the users password
+ on the NDS tree that this irp context points to.
+
+Arguments:
+
+ pIrpContext - The irp context for this request. Points to the target server.
+ dwUserOID - The oid for the current user.
+ dwChallenge - The encrypted challenge from begin login.
+ pbOldPwHash - The 16 byte hash of the old password.
+ pbNewPwHash - The 16 byte hash of the new password.
+ pUserPrivKey - The user's private RSA key with NDS header.
+ pServerPublicBsafeKey - The server's public RSA key in BSAFE format.
+ ServerPubKeyLen - The length of the server's public BSAFE key.
+
+--*/
+{
+ NTSTATUS Status;
+ BYTE pbNewPwKey[8];
+ BYTE pbSecretKey[8];
+ PENC_BLOCK_HDR pbEncSecretKey, pbEncChangePassReq;
+ BYTE RandomBytes[RAND_KEY_DATA_LEN];
+ PBYTE pbEncData;
+ PNDS_CHPW_MSG pChangePassMsg;
+ INT CryptStatus, CryptLen;
+ DWORD dwTotalEncDataLen;
+ LOCKED_BUFFER NdsRequest;
+
+ PAGED_CODE();
+
+ //
+ // Create a secret key from the new password.
+ //
+
+ GenKey8( pbNewPwHash, 16, pbNewPwKey );
+
+ pbEncSecretKey = ALLOCATE_POOL( PagedPool,
+ ( ( 2 * sizeof( ENC_BLOCK_HDR ) ) +
+ ( MAX_RSA_BYTES ) +
+ ( sizeof( NDS_CHPW_MSG ) ) +
+ ( sizeof( NDS_PRIVATE_KEY ) ) +
+ ( pUserPrivKey->keyDataLength ) +
+ 16 ) );
+
+ if ( !pbEncSecretKey ) {
+ DebugTrace( 0, Dbg, "ChangeNdsPassword: Out of memory.\n", 0 );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ FREE_POOL( pbEncSecretKey );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // Generate a random key.
+ //
+
+ GenRandomBytes( RandomBytes, RAND_KEY_DATA_LEN );
+ GenKey8( RandomBytes, RAND_KEY_DATA_LEN, pbSecretKey );
+
+ //
+ // Encrypt the secret key data in the space after the EBH.
+ //
+
+ pbEncSecretKey->dataLength = RAND_KEY_DATA_LEN;
+ pbEncSecretKey->cipherLength = RSAGetInputBlockSize( pServerPublicBsafeKey, ServerPubKeyLen);
+
+ pbEncData = ( PBYTE ) ( pbEncSecretKey + 1 );
+
+ pbEncSecretKey->cipherLength = RSAPack( RandomBytes,
+ pbEncSecretKey->dataLength,
+ pbEncData,
+ pbEncSecretKey->cipherLength );
+
+ pbEncSecretKey->cipherLength = RSAPublic( pServerPublicBsafeKey,
+ ServerPubKeyLen,
+ pbEncData,
+ pbEncSecretKey->cipherLength,
+ pbEncData );
+
+ if ( !pbEncSecretKey->cipherLength ) {
+ DebugTrace( 0, Dbg, "ChangeNdsPassword: RSA encryption failed.\n", 0 );
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Finish filling out the EBH for the secret key block.
+ //
+
+ pbEncSecretKey->version = 1;
+ pbEncSecretKey->encType = ENC_TYPE_RSA_PUBLIC;
+ pbEncSecretKey->blockLength = pbEncSecretKey->cipherLength +
+ sizeof( ENC_BLOCK_HDR ) -
+ sizeof( DWORD );
+
+ //
+ // Now form the change password request.
+ //
+
+ pbEncChangePassReq = ( PENC_BLOCK_HDR )
+ ( pbEncData + ROUNDUP4( pbEncSecretKey->cipherLength ) );
+
+ pChangePassMsg = ( PNDS_CHPW_MSG ) ( pbEncChangePassReq + 1 );
+
+ //
+ // Init the Change Password message.
+ //
+
+ pChangePassMsg->challenge = dwChallenge;
+ pChangePassMsg->oldPwLength = pChangePassMsg->newPwLength = 16;
+
+ RtlCopyMemory( pChangePassMsg->oldPwHash, pbOldPwHash, pChangePassMsg->oldPwLength );
+ RtlCopyMemory( pChangePassMsg->newPwHash, pbNewPwHash, pChangePassMsg->newPwLength );
+
+ pChangePassMsg->unknown = 8;
+ pChangePassMsg->encPrivKeyHdr.version = 1;
+ pChangePassMsg->encPrivKeyHdr.encType = ENC_TYPE_RC2_CBC;
+ pChangePassMsg->encPrivKeyHdr.dataLength = pUserPrivKey->keyDataLength + sizeof( NDS_PRIVATE_KEY );
+
+ //
+ // Encrypt the private key with the key derived from the new password.
+ //
+
+ CryptStatus = CBCEncrypt( pbNewPwKey,
+ NULL,
+ ( PBYTE ) pUserPrivKey,
+ pChangePassMsg->encPrivKeyHdr.dataLength,
+ ( PBYTE ) ( pChangePassMsg + 1 ),
+ &CryptLen,
+ BSAFE_CHECKSUM_LEN );
+
+ if ( CryptStatus ) {
+ DebugTrace( 0, Dbg, "ChangeNdsPassword: CBC encrypt failed.\n", 0 );
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Finish filling out the encryption header.
+ //
+
+ pChangePassMsg->encPrivKeyHdr.cipherLength = CryptLen;
+ pChangePassMsg->encPrivKeyHdr.blockLength = CryptLen +
+ sizeof( ENC_BLOCK_HDR ) -
+ sizeof( DWORD );
+ pbEncChangePassReq->version = 1;
+ pbEncChangePassReq->encType = ENC_TYPE_RC2_CBC;
+ pbEncChangePassReq->dataLength = sizeof( NDS_CHPW_MSG ) + CryptLen;
+
+ //
+ // Encrypt the whole Change Password message in-place with the secret key.
+ //
+
+ CryptStatus = CBCEncrypt( pbSecretKey,
+ NULL,
+ ( PBYTE ) pChangePassMsg,
+ pbEncChangePassReq->dataLength,
+ ( PBYTE ) pChangePassMsg,
+ &CryptLen,
+ BSAFE_CHECKSUM_LEN);
+
+ if ( CryptStatus ) {
+ DebugTrace( 0, Dbg, "ChangeNdsPassword: Second CBC encrypt failed.\n", 0 );
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+ }
+
+ pbEncChangePassReq->cipherLength = CryptLen;
+ pbEncChangePassReq->blockLength =
+ CryptLen + sizeof( ENC_BLOCK_HDR ) - sizeof( DWORD );
+
+ //
+ // Calculate the size of the request.
+ //
+
+ dwTotalEncDataLen = sizeof( ENC_BLOCK_HDR ) + // Secret key header.
+ ROUNDUP4( pbEncSecretKey->cipherLength ) + // Secret key data.
+ sizeof( ENC_BLOCK_HDR ) + // Change pass msg header.
+ CryptLen; // Change pass data.
+
+ //
+ // Send this change password message to the server.
+ //
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_CHANGE_PASSWORD,
+ &NdsRequest,
+ "DDDDDDr",
+ 0,
+ dwUserOID,
+ dwTotalEncDataLen + ( 3 * sizeof( DWORD ) ),
+ 1,
+ 0x20009,
+ dwTotalEncDataLen,
+ pbEncSecretKey,
+ dwTotalEncDataLen );
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = NdsCompletionCodetoNtStatus( &NdsRequest );
+ }
+
+ExitWithCleanup:
+
+ FREE_POOL( pbEncSecretKey );
+ NdsFreeLockedBuffer( &NdsRequest );
+ return Status;
+
+}
+
+NTSTATUS
+NdsServerAuthenticate(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PNDS_SECURITY_CONTEXT pNdsContext
+)
+/*++
+
+Routine Description:
+
+ Authenticate an NDS connection.
+ The user must have already logged into the NDS tree.
+
+ If you change this function - know that you cannot
+ at any point try to acquire the nds credential
+ resource exclusive from here since that could cause
+ a dead lock!!!
+
+ You also must not dequeue the irp context!
+
+Arguments:
+
+ pIrpContext - IrpContext for the server that we want to authenticate to.
+
+Return value:
+
+ NTSTATUS
+
+--*/
+{
+ NTSTATUS Status;
+
+ BYTE *pbUserPublicBsafeKey;
+ int cbUserPublicBsafeKeyLen;
+
+ NDS_AUTH_MSG *psAuthMsg;
+ NDS_CREDENTIAL *psLocalCredential;
+ DWORD dwLocalCredentialLen;
+ UNICODE_STRING uUserName;
+ DWORD UserOID;
+
+ BYTE *x, *y, *r;
+ BYTE CredentialHash[16];
+ int i, rsaBlockSize, rsaModSize, totalXLen;
+ DWORD dwServerRand;
+
+ BYTE *pbResponse;
+ DWORD cbResponseLen;
+ LOCKED_BUFFER NdsRequest;
+
+ PAGED_CODE();
+
+ DebugTrace( 0, Dbg, "Entering NdsServerAuthenticate...\n", 0 );
+
+ //
+ // Allocate space for the auth msg, credential, G-Q bytes, and
+ // the response buffer.
+ //
+
+ psAuthMsg = ALLOCATE_POOL( PagedPool,
+ sizeof( NDS_AUTH_MSG ) + // auth message
+ sizeof( NDS_CREDENTIAL ) + // credential
+ MAX_NDS_NAME_SIZE + //
+ ( MAX_RSA_BYTES * 9 ) ); // G-Q rands
+
+ if ( !psAuthMsg ) {
+
+ DebugTrace( 0, Dbg, "Out of memory in NdsServerAuthenticate (0)...\n", 0 );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ pbResponse = ALLOCATE_POOL( PagedPool, NDS_BUFFER_SIZE );
+
+ if ( !pbResponse ) {
+
+ DebugTrace( 0, Dbg, "Out of memory in NdsServerAuthenticate (1)...\n", 0 );
+ FREE_POOL( psAuthMsg );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ psLocalCredential = (PNDS_CREDENTIAL)( ((BYTE *) psAuthMsg) +
+ sizeof( NDS_AUTH_MSG ) );
+
+ //
+ // Locate the public BSAFE key.
+ //
+
+ cbUserPublicBsafeKeyLen = NdsGetBsafeKey ( (BYTE *)(pNdsContext->PublicNdsKey),
+ pNdsContext->PublicKeyLen,
+ &pbUserPublicBsafeKey );
+
+ //
+ // Get the user's object Id but do not jump dir servers. There is never
+ // any optional data, so we don't really need to skip over it.
+ //
+
+ uUserName.MaximumLength = pNdsContext->Credential->userNameLength;
+ uUserName.Length = uUserName.MaximumLength;
+ uUserName.Buffer = ( WCHAR * ) ( ((BYTE *)pNdsContext->Credential) +
+ sizeof( NDS_CREDENTIAL ) +
+ pNdsContext->Credential->optDataSize );
+
+ Status = NdsResolveNameKm( pIrpContext,
+ &uUserName,
+ &UserOID,
+ FALSE,
+ RSLV_DEREF_ALIASES | RSLV_CREATE_ID | RSLV_ENTRY_ID );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Issue the Begin Authenticate request to get the random server nonce.
+ //
+
+ Status = BeginAuthenticate( pIrpContext,
+ UserOID,
+ &dwServerRand );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Figure out the size of the zero-padded RSA Blocks. We use the same
+ // size as the modulus field of the public key (typically 56 bytes).
+ //
+
+ RSAGetModulus( pbUserPublicBsafeKey,
+ cbUserPublicBsafeKeyLen,
+ &rsaBlockSize);
+
+ DebugTrace( 0, Dbg, "RSA block size for authentication: %d\n", rsaBlockSize );
+
+ //
+ // Prepare the credential and the 3 G-Q rands. The credential,
+ // xs, and ys go out in the packet; rs is secret.
+ //
+
+ RtlZeroMemory( ( BYTE * )psLocalCredential,
+ sizeof( NDS_CREDENTIAL ) +
+ MAX_NDS_NAME_SIZE +
+ 9 * rsaBlockSize );
+
+ dwLocalCredentialLen = sizeof( NDS_CREDENTIAL ) +
+ pNdsContext->Credential->optDataSize +
+ pNdsContext->Credential->userNameLength;
+
+ DebugTrace( 0, Dbg, "Credential length is %d.\n", dwLocalCredentialLen );
+
+ RtlCopyMemory( (BYTE *)psLocalCredential,
+ pNdsContext->Credential,
+ dwLocalCredentialLen );
+
+ x = ( BYTE * ) psAuthMsg + sizeof( NDS_AUTH_MSG ) + dwLocalCredentialLen;
+ y = x + ( 3 * rsaBlockSize );
+ r = y + ( 3 * rsaBlockSize );
+
+ rsaModSize = RSAGetInputBlockSize( pbUserPublicBsafeKey,
+ cbUserPublicBsafeKeyLen );
+
+ DebugTrace( 0, Dbg, "RSA modulus size: %d\n", rsaModSize );
+
+ for ( i = 0; i < 3; i++ ) {
+
+ //
+ // Create Random numbers r1, r2 and r3 of modulus size.
+ //
+
+ GenRandomBytes( r + ( rsaBlockSize * i ), rsaModSize );
+
+ //
+ // Compute x = r**e mod N.
+ //
+
+ RSAPublic( pbUserPublicBsafeKey,
+ cbUserPublicBsafeKeyLen,
+ r + ( rsaBlockSize * i ),
+ rsaModSize,
+ x + ( rsaBlockSize * i ) );
+
+ }
+
+ //
+ // Fill in the AuthMsg fields.
+ //
+
+ psAuthMsg->version = 0;
+ psAuthMsg->svrRand = dwServerRand;
+ psAuthMsg->verb = NDSV_FINISH_AUTHENTICATE;
+ psAuthMsg->credentialLength = dwLocalCredentialLen;
+
+ //
+ // MD2 hash the auth message, credential and x's.
+ //
+
+ MD2( (BYTE *)psAuthMsg,
+ sizeof( NDS_AUTH_MSG ) +
+ psAuthMsg->credentialLength +
+ ( 3 * rsaBlockSize ),
+ CredentialHash );
+
+ //
+ // Compute yi = ri*(S**ci) mod N; c1,c2,c3 are the first three
+ // 16 bit numbers in CredentialHash.
+ //
+
+ totalXLen = 3 * rsaBlockSize;
+
+ for ( i = 0; i < 3; i++ ) {
+
+ RSAModExp( pbUserPublicBsafeKey,
+ cbUserPublicBsafeKeyLen,
+ ( (BYTE *)(pNdsContext->Signature) ) + sizeof( NDS_SIGNATURE ),
+ pNdsContext->Signature->signDataLength,
+ &CredentialHash[i * sizeof( WORD )],
+ sizeof( WORD ),
+ y + ( rsaBlockSize * i) );
+
+ RSAModMpy( pbUserPublicBsafeKey,
+ cbUserPublicBsafeKeyLen,
+ y + ( rsaBlockSize * i ), // input1 = S**ci mod N
+ rsaModSize + 1,
+ r + ( rsaBlockSize * i ), // input2 = ri
+ rsaModSize,
+ y + ( rsaBlockSize * i ) ); // output = yi
+ }
+
+ //
+ // Send the auth proof.
+ //
+
+ NdsRequest.pRecvBufferVa = pbResponse;
+ NdsRequest.dwRecvLen = NDS_BUFFER_SIZE;
+ NdsRequest.pRecvMdl = NULL;
+
+ NdsRequest.pRecvMdl = ALLOCATE_MDL( pbResponse,
+ NDS_BUFFER_SIZE,
+ FALSE,
+ FALSE,
+ NULL );
+ if ( !NdsRequest.pRecvMdl ) {
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ goto ExitWithCleanup;
+ }
+
+ MmProbeAndLockPages( NdsRequest.pRecvMdl,
+ KernelMode,
+ IoWriteAccess );
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_FINISH_AUTHENTICATE,
+ &NdsRequest,
+ "DDDrDDWWWWr",
+ 0, // version
+ 0, // sessionKeyLen
+ psAuthMsg->credentialLength, // credential len
+ (BYTE *)psLocalCredential, // actual credential
+ ROUNDUP4( psAuthMsg->credentialLength ),
+ 12 + ( totalXLen * 2 ), // length of remaining
+ 1, // proof version?
+ 8, // tag?
+ 16, // message digest base
+ 3, // proof order
+ totalXLen, // proofOrder*sizeof(x)
+ x, // x1,x2,x3,y1,y2,y3
+ 2 * totalXLen );
+
+ MmUnlockPages( NdsRequest.pRecvMdl );
+ FREE_MDL( NdsRequest.pRecvMdl );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ Status = NdsCompletionCodetoNtStatus( &NdsRequest );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ cbResponseLen = NdsRequest.dwBytesWritten;
+ DebugTrace( 0, Dbg, "Authentication returned ok status.\n", 0 );
+
+ //
+ // We completed NDS authentication, so clear out the name
+ // and password in the SCB so that we use the credentials
+ // from now on.
+ //
+
+ if ( pIrpContext->pScb->UserName.Buffer != NULL ) {
+
+ DebugTrace( 0, Dbg, "Clearing out bindery login data.\n", 0 );
+
+ pIrpContext->pScb->UserName.Length = 0;
+ pIrpContext->pScb->UserName.MaximumLength = 0;
+
+ pIrpContext->pScb->Password.Length = 0;
+ pIrpContext->pScb->Password.MaximumLength = 0;
+
+ FREE_POOL( pIrpContext->pScb->UserName.Buffer );
+ RtlInitUnicodeString( &pIrpContext->pScb->UserName, NULL );
+ RtlInitUnicodeString( &pIrpContext->pScb->Password, NULL );
+
+ }
+
+ExitWithCleanup:
+
+ FREE_POOL( psAuthMsg );
+ FREE_POOL( pbResponse );
+
+ return Status;
+}
+
+NTSTATUS
+BeginAuthenticate(
+ IN PIRP_CONTEXT pIrpContext,
+ IN DWORD dwUserId,
+ OUT DWORD *pdwSvrRandom
+)
+/*++
+
+Routine Description:
+
+ Authenticate an NDS connection.
+ The user must have already logged into the NDS tree.
+
+Arguments:
+
+ pIrpContext - IrpContext for the server that we want to authenticate to.
+ dwUserID - The user OID that we are authenticating ourselves as.
+ pdwSvrRandon - The server random challenge.
+
+Return value:
+
+ NTSTATUS - The result of the operation.
+
+--*/
+{
+ NTSTATUS Status;
+ LOCKED_BUFFER NdsRequest;
+
+ DWORD dwClientRand;
+
+ PAGED_CODE();
+
+ Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ GenRandomBytes( (BYTE *)&dwClientRand, sizeof( dwClientRand ) );
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_BEGIN_AUTHENTICATE,
+ &NdsRequest,
+ "DDD",
+ 0, // Version.
+ dwUserId, // Entry Id.
+ dwClientRand ); // Client's random challenge.
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ Status = NdsCompletionCodetoNtStatus( &NdsRequest );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ if ( Status == STATUS_BAD_NETWORK_PATH ) {
+ Status = STATUS_NO_SUCH_USER;
+ }
+
+ goto ExitWithCleanup;
+ }
+
+ //
+ // The reply actually contains all this, even though we don't look at it?
+ //
+ // typedef struct {
+ // DWORD svrRand;
+ // DWORD totalLength;
+ // TAG_DATA_HEADER tdh;
+ // WORD unknown; // Always 2.
+ // DWORD encClientRandLength;
+ // CIPHER_BLOCK_HEADER keyCipherHdr;
+ // BYTE keyCipher[];
+ // CIPHER_BLOCK_HEADER encClientRandHdr;
+ // BYTE encClientRand[];
+ // } REPLY_BEGIN_AUTHENTICATE;
+ //
+ // Nah, that can't be right.
+ //
+
+ Status = ParseResponse( NULL,
+ NdsRequest.pRecvBufferVa,
+ NdsRequest.dwBytesWritten,
+ "G_D",
+ sizeof( DWORD ),
+ pdwSvrRandom );
+
+ //
+ // We either got it or we didn't.
+ //
+
+ExitWithCleanup:
+
+ NdsFreeLockedBuffer( &NdsRequest );
+ return Status;
+}
+
+NTSTATUS
+NdsLicenseConnection(
+ PIRP_CONTEXT pIrpContext
+)
+/*+++
+
+ Send the license NCP to the server to license this connection.
+
+---*/
+{
+
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ DebugTrace( 0, Dbg, "Licensing connection to %wZ.\n", &(pIrpContext->pNpScb->pScb->UidServerName) );
+
+ //
+ // Change the authentication state of the connection.
+ //
+
+ Status = ExchangeWithWait ( pIrpContext,
+ SynchronousResponseCallback,
+ "SD",
+ NCP_ADMIN_FUNCTION,
+ NCP_CHANGE_CONN_AUTH_STATUS,
+ NCP_CONN_LICENSED );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ DebugTrace( 0, Dbg, "Licensing failed to %wZ.\n", &(pIrpContext->pNpScb->pScb->UidServerName) );
+ }
+
+ return Status;
+
+}
+
+NTSTATUS
+NdsUnlicenseConnection(
+ PIRP_CONTEXT pIrpContext
+)
+/*+++
+
+ Send the license NCP to the server to license this connection.
+
+---*/
+{
+
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ DebugTrace( 0, Dbg, "Unlicensing connection to %wZ.\n", &(pIrpContext->pNpScb->pScb->UidServerName) );
+
+ //
+ // Change the authentication state of the connection.
+ //
+
+ Status = ExchangeWithWait ( pIrpContext,
+ SynchronousResponseCallback,
+ "SD",
+ NCP_ADMIN_FUNCTION,
+ NCP_CHANGE_CONN_AUTH_STATUS,
+ NCP_CONN_NOT_LICENSED );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ DebugTrace( 0, Dbg, "Unlicensing failed to %wZ.\n", &(pIrpContext->pNpScb->pScb->UidServerName) );
+ }
+
+ return Status;
+}
+
+int
+NdsGetBsafeKey(
+ UCHAR *pPubKey,
+ const int pubKeyLen,
+ UCHAR **ppBsafeKey
+)
+/*++
+
+Routine Description:
+
+ Locate the BSAFE key from within the public key. Note that this does
+ not work for private keys in NDS format. For private keys, you just
+ skip the size word.
+
+ This is verbatim from Win95.
+
+Routine Arguments:
+
+ pPubKey - A pointer to the public key.
+ pubKeyLen - The length of the public key.
+ ppBsafeKey - The pointer to the BSAFE key in the public key.
+
+Return Value:
+
+ The length of the BSAFE key.
+
+--*/
+{
+ int bsafePubKeyLen = 0, totalDNLen;
+ char *pRcv;
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ totalDNLen = 0;
+ Status = ParseResponse( NULL,
+ pPubKey,
+ pubKeyLen,
+ "G_W",
+ ( 2 * sizeof( DWORD ) ) + sizeof( WORD ),
+ &totalDNLen );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto Exit;
+ }
+
+ Status = ParseResponse( NULL,
+ pPubKey,
+ pubKeyLen - 12,
+ "G__W",
+ 12,
+ 5 * sizeof( WORD ) +
+ 3 * sizeof( DWORD ) +
+ totalDNLen,
+ &bsafePubKeyLen );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto Exit;
+ }
+
+ *ppBsafeKey = (UCHAR *) pPubKey +
+ 14 +
+ 5 * sizeof( WORD ) +
+ 3 * sizeof( DWORD ) +
+ totalDNLen;
+
+
+Exit:
+
+ return bsafePubKeyLen;
+}
+
+NTSTATUS
+NdsLogoff(
+ IN PIRP_CONTEXT pIrpContext
+)
+/*++
+
+Routine Description:
+
+ Sends a logout to the NDS tree, closes all NDS authenticated
+ connections, and destroys the current set of NDS credentials.
+
+ This routine acquires the credential list exclusive.
+
+Arguments:
+
+ pIrpContext - The IRP context for this request pointed to a
+ valid dir server.
+
+Notes:
+
+ This is only called from DeleteConnection. The caller owns
+ the RCB exclusive and we will free it before returning.
+
+--*/
+{
+ NTSTATUS Status;
+ LOCKED_BUFFER NdsRequest;
+ PNDS_SECURITY_CONTEXT pCredentials;
+ PLOGON pLogon;
+ PSCB pScb;
+ PNONPAGED_SCB pNpScb;
+
+ PLIST_ENTRY ScbQueueEntry;
+ PNONPAGED_SCB pNextNpScb;
+ KIRQL OldIrql;
+
+ //
+ // Grab the user's LOGON structure.
+ //
+
+ pNpScb = pIrpContext->pNpScb;
+ pScb = pNpScb->pScb;
+
+ //
+ // The caller owns the RCB.
+ //
+
+ pLogon = FindUser( &pScb->UserUid, FALSE );
+
+ if ( !pLogon ) {
+ DebugTrace( 0, Dbg, "Invalid security context for NdsLogoff.\n", 0 );
+ NwReleaseRcb( &NwRcb );
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ return STATUS_NO_SUCH_USER;
+ }
+
+ //
+ // Check to make sure that we have something to log off from.
+ //
+
+ Status = NdsLookupCredentials( &pScb->NdsTreeName,
+ pLogon,
+ &pCredentials,
+ CREDENTIAL_WRITE,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status )) {
+ DebugTrace( 0, Dbg, "NdsLogoff: Nothing to log off from.\n", 0 );
+ NwReleaseRcb( &NwRcb );
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ return STATUS_NO_SUCH_LOGON_SESSION;
+ }
+
+ //
+ // If the credentials are locked, then someone is already
+ // doing a logout.
+ //
+
+ if ( pCredentials->CredentialLocked ) {
+ DebugTrace( 0, Dbg, "NdsLogoff: Logoff already in progress.\n", 0 );
+ NwReleaseCredList( pLogon );
+ NwReleaseRcb( &NwRcb );
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ return STATUS_DEVICE_BUSY;
+ }
+
+ //
+ // Mark the credential locked so we can logout without
+ // worrying about others logging in.
+ //
+
+ pCredentials->CredentialLocked = TRUE;
+
+ //
+ // Release all our resoures so we can jump around servers.
+ //
+
+ NwReleaseCredList( pLogon );
+ NwReleaseRcb( &NwRcb );
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ //
+ // Look through the scb list for connections that are in use. If all
+ // existing connections can be closed down, then we can complete the logout.
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ ScbQueueEntry = pNpScb->ScbLinks.Flink;
+
+ if ( ScbQueueEntry == &ScbQueue ) {
+ ScbQueueEntry = ScbQueue.Flink;
+ }
+
+ pNextNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
+
+ NwReferenceScb( pNextNpScb );
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+ while ( pNextNpScb != pNpScb ) {
+
+ if ( pNextNpScb->pScb != NULL ) {
+
+ //
+ // Is this connection in use by us and is it NDS authenticated?
+ //
+
+ if ( RtlEqualUnicodeString( &pScb->NdsTreeName,
+ &pNextNpScb->pScb->NdsTreeName,
+ TRUE ) &&
+ ( pScb->UserUid.QuadPart == pNextNpScb->pScb->UserUid.QuadPart ) &&
+ ( pNextNpScb->State == SCB_STATE_IN_USE ) &&
+ ( pNextNpScb->pScb->UserName.Length == 0 ) ) {
+
+ pIrpContext->pNpScb = pNextNpScb;
+ pIrpContext->pScb = pNextNpScb->pScb;
+ NwAppendToQueueAndWait( pIrpContext );
+
+ if ( pNextNpScb->pScb->OpenFileCount == 0 ) {
+
+ //
+ // Can we close it anyway? Should we check
+ // for open handles and the such here?
+ //
+
+ pNextNpScb->State = SCB_STATE_LOGIN_REQUIRED;
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ } else {
+
+ DebugTrace( 0, Dbg, "NdsLogoff: Other connections in use.\n", 0 );
+
+ NwAcquireExclusiveCredList( pLogon );
+ pCredentials->CredentialLocked = FALSE;
+ NwReleaseCredList( pLogon );
+
+ NwDereferenceScb( pNextNpScb );
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ return STATUS_CONNECTION_IN_USE;
+
+ }
+ }
+
+ }
+
+ //
+ // Select the next scb.
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ ScbQueueEntry = pNextNpScb->ScbLinks.Flink;
+
+ if ( ScbQueueEntry == &ScbQueue ) {
+ ScbQueueEntry = ScbQueue.Flink;
+ }
+
+ NwDereferenceScb( pNextNpScb );
+ pNextNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
+
+ NwReferenceScb( pNextNpScb );
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+ }
+
+ NwDereferenceScb( pNpScb );
+
+ //
+ // The seed scb for the tree logout should be valid.
+ //
+
+ ASSERT( pNpScb->State == SCB_STATE_IN_USE );
+
+ //
+ // Check to make sure we can close the host scb.
+ //
+
+ if ( pScb->OpenFileCount != 0 ) {
+
+ DebugTrace( 0, Dbg, "NdsLogoff: Seed connection in use.\n", 0 );
+
+ NwAcquireExclusiveCredList( pLogon );
+ pCredentials->CredentialLocked = FALSE;
+ NwReleaseCredList( pLogon );
+
+ return STATUS_CONNECTION_IN_USE;
+ }
+
+ //
+ // We can actually do the logout, so remove the credentials from
+ // the resource list, release the resource, and logout.
+ //
+ // If we are deleting the preferred tree credentials,
+ // then we need to clear the preferred server.
+ //
+
+ if ( (pLogon->NdsCredentialList).Flink == &(pCredentials->Next) ) {
+
+ if ( pLogon->ServerName.Buffer != NULL ) {
+
+ DebugTrace( 0, Dbg, "Clearing preferred server at logout time.\n", 0 );
+
+ FREE_POOL( pLogon->ServerName.Buffer );
+ pLogon->ServerName.Length = pLogon->ServerName.MaximumLength = 0;
+ pLogon->ServerName.Buffer = NULL;
+
+ }
+ }
+
+ NwAcquireExclusiveCredList( pLogon );
+ RemoveEntryList( &pCredentials->Next );
+ NwReleaseCredList( pLogon );
+
+ FreeNdsContext( pCredentials );
+
+ pIrpContext->pNpScb = pNpScb;
+ pIrpContext->pScb = pScb;
+
+ Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_LOGOUT,
+ &NdsRequest,
+ NULL );
+
+ NdsFreeLockedBuffer( &NdsRequest );
+
+ }
+
+ NwAppendToQueueAndWait( pIrpContext );
+
+ ASSERT( pScb->UserName.Buffer == NULL );
+ pNpScb->State = SCB_STATE_LOGIN_REQUIRED;
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ return STATUS_SUCCESS;
+
+}
+
diff --git a/private/nw/rdr/ndsprocs.h b/private/nw/rdr/ndsprocs.h
new file mode 100644
index 000000000..87c3765b0
--- /dev/null
+++ b/private/nw/rdr/ndsprocs.h
@@ -0,0 +1,822 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ NdsProcs.h
+
+Abstract:
+
+ This defines the necessary NDS data structures and
+ symbolic constants.
+
+Author:
+
+ Cory West [CoryWest] 23-Feb-1995
+
+Revision History:
+
+--*/
+
+#include "data.h"
+#include "nodetype.h"
+#include "struct.h"
+#include <stdarg.h>
+
+#include "crypto.h"
+
+//
+// Security information.
+//
+
+#define ENC_TYPE_RSA_PUBLIC 0x90001
+#define ENC_TYPE_RC2_CBC 0x60001
+
+#define RAND_KEY_DATA_LEN 28
+#define RAND_FL_DATA_LEN 1024
+#define RC2_KEY_LEN 8
+
+#define MAX_PUBLIC_KEY_LEN 1300
+#define MAX_BSAFE_PUBLIC_KEY_LEN 200 // Typically 179.
+#define MAX_BSAFE_PRIV_KEY_LEN 280 // Typically 273.
+
+#define MAX_PW_CHARS 16
+
+//
+// The max size for various NDS components.
+//
+
+#define MAX_RSA_BITS 512 // Really 420.
+
+#define NDS_TREE_NAME_LEN 32
+#define NDS_BINDERY_TREE_NAME 48
+
+#define MAX_CREDENTIAL_LEN ( sizeof( NDS_CREDENTIAL ) + MAX_NDS_NAME_SIZE )
+#define MAX_SIGNATURE_LEN ( sizeof( NDS_SIGNATURE ) + MAX_RSA_BYTES )
+#define MAX_ENC_PRIV_KEY_LEN ( MAX_BSAFE_PRIV_KEY_LEN + 64 )
+
+#define BSAFE_CHECKSUM_LEN 5
+
+#define DEFAULT_RESOLVE_FLAGS RSLV_DEREF_ALIASES | RSLV_WALK_TREE | RSLV_WRITABLE
+
+#include <packon.h>
+
+typedef struct {
+
+ DWORD syntaxId; // OCTET STRING (9)
+
+ struct {
+ DWORD nameLength;
+ WORD name[11]; // "Public Key"
+ WORD filler;
+ } attribName;
+
+ DWORD entries; // = 1
+ DWORD totalLength; // of attribute value OCTET STRING
+ DWORD unknown1; // = 1
+ DWORD unknown2; // = 4
+ WORD _issuerDNLength;
+ WORD totalDNLength;
+ WORD length2;
+ WORD length3;
+ WORD issuerDNLength;
+ WORD userDNLength;
+ WORD bsafeSectionLength;
+ DWORD length4;
+
+} PUBLIC_KEY_ATTRIB;
+
+#include <packoff.h>
+
+typedef struct {
+
+ DWORD blockLength; // cipherLength + size of following hdr fields
+ DWORD version; // = 1
+ DWORD encType; // 0x060001 for RC2; 0x090001 and 0x0A0001 for RSA
+ WORD cipherLength; // of ciphertext
+ WORD dataLength; // of plaintext
+
+} ENC_BLOCK_HDR, *PENC_BLOCK_HDR;
+
+typedef struct {
+
+ DWORD rand1;
+ DWORD rand2Len;
+ BYTE rand2[RAND_FL_DATA_LEN];
+
+} NDS_RAND_BYTE_BLOCK, *PNDS_RAND_BYTE_BLOCK;
+
+typedef struct {
+
+ DWORD version;
+ DWORD verb;
+ DWORD svrRand;
+ DWORD credentialLength;
+
+} NDS_AUTH_MSG, *PNDS_AUTH_MSG;
+
+//
+// VLM Uses the Tagged Data Store as a sort of registry on the fly.
+// We, of course, don't use it, but still need the headers.
+//
+// We need these to be packed.
+//
+
+#include <packon.h>
+
+typedef struct {
+ DWORD version;
+ WORD tag;
+} TAG_DATA_HEADER;
+
+#define TAG_PRIVATE_KEY 2
+#define TAG_PUBLIC_KEY 4
+#define TAG_CREDENTIAL 6
+#define TAG_SIGNATURE 7
+#define TAG_PROOF 8
+
+typedef struct {
+
+ TAG_DATA_HEADER tdh;
+ DWORD validityBegin;
+ DWORD validityEnd;
+ DWORD random;
+ WORD optDataSize;
+ WORD userNameLength;
+
+ // BYTE optData[optDataSize];
+ // BYTE userName[userNameLength];
+
+} NDS_CREDENTIAL, *PNDS_CREDENTIAL;
+
+typedef struct {
+
+ TAG_DATA_HEADER tdh;
+ WORD signDataLength;
+
+ //BYTE signData[signLength];
+
+} NDS_SIGNATURE, *PNDS_SIGNATURE;
+
+typedef struct {
+
+ TAG_DATA_HEADER tdh;
+ WORD keyDataLength;
+
+ //BYTE BsafeKeyData[keyDataLength];
+
+} NDS_PRIVATE_KEY, *PNDS_PRIVATE_KEY;
+
+typedef struct {
+
+ DWORD dwMaxFragSize;
+ DWORD dwRequestSize;
+ DWORD dwFragmentFlags;
+ DWORD dwNdsVerb;
+ DWORD dwReplyBufferSize;
+
+} NDS_REQUEST_HEADER, *PNDS_REQUEST_HEADER;
+
+typedef struct {
+
+ DWORD dwFragmentSize;
+ DWORD dwFraggerHandle;
+
+} NDS_REPLY_HEADER, *PNDS_REPLY_HEADER;
+
+#include <packoff.h>
+
+typedef struct _NDS_CONTEXT_HEAD {
+
+ //
+ // Node id and list entries.
+ //
+
+ NODE_TYPE_CODE ntc;
+ NODE_BYTE_SIZE nts;
+
+ //
+ // We can set this flag if we need to pause
+ // all tree activity (like, for a logout).
+ //
+
+ BOOLEAN CredentialLocked;
+
+ LIST_ENTRY Next;
+
+ //
+ // NDS tree name. Leave enough room for the munged credential name.
+ //
+
+ UNICODE_STRING NdsTreeName;
+ WCHAR NdsTreeNameBuffer[NDS_TREE_NAME_LEN + MAX_NDS_NAME_CHARS + 2];
+
+ //
+ // User's credentials.
+ //
+
+ PNDS_CREDENTIAL Credential;
+
+ //
+ // User's signature.
+ //
+
+ PNDS_SIGNATURE Signature;
+
+ //
+ // Password for this tree connection.
+ //
+
+ OEM_STRING Password;
+
+ //
+ // User's public key.
+ //
+
+ DWORD PublicKeyLen;
+ BYTE *PublicNdsKey;
+
+ //
+ // The current context for this tree.
+ //
+
+ UNICODE_STRING CurrentContext;
+ WCHAR CurrentContextString[MAX_NDS_NAME_CHARS];
+
+} NDS_SECURITY_CONTEXT, *PNDS_SECURITY_CONTEXT;
+
+typedef struct _NDS_CHPW_MSG {
+
+ DWORD challenge;
+ DWORD oldPwLength;
+ BYTE oldPwHash[16];
+ DWORD unknown;
+ DWORD newPwLength;
+ BYTE newPwHash[16];
+ ENC_BLOCK_HDR encPrivKeyHdr;
+
+ // BYTE encPrivKey[];
+
+} NDS_CHPW_MSG, *PNDS_CHPW_MSG;
+
+//
+// Credential list handling routines.
+//
+
+#define NwAcquireExclusiveCredList( pLogon) \
+ ExAcquireResourceExclusive( &((pLogon)->CredentialListResource), TRUE )
+
+#define NwAcquireSharedCredList( pLogon ) \
+ ExAcquireResourceShared( &((pLogon)->CredentialListResource), TRUE )
+
+#define NwReleaseCredList( pLogon ) \
+ ExReleaseResource( &((pLogon)->CredentialListResource) )
+
+#include <packon.h>
+
+typedef struct {
+
+ DWORD verb;
+ UINT count;
+ char *bufEnd;
+ PVOID nextItem;
+
+} NDS_TAG, *PNDS_TAG;
+
+#include <packoff.h>
+
+typedef struct _nds_list_response {
+
+ DWORD ccode;
+ DWORD iterationHandle;
+ DWORD numEntries;
+
+ //
+ // Followed by an array of these.
+ //
+ // struct {
+ // DWORD entryId;
+ // DWORD flags;
+ // DWORD subCount;
+ // DWORD modTime;
+ // NDS_STRING BaseClass;
+ // NDS_STRING entryName;
+ // } [];
+ //
+
+} NDS_LIST_RESPONSE, *PNDS_LIST_RESPONSE;
+
+typedef struct _locked_buffer {
+
+ //
+ // Describes a writeable response buffer
+ // that we have locked down for the transport.
+ //
+
+ PVOID pRecvBufferVa;
+ DWORD dwRecvLen;
+ PMDL pRecvMdl;
+ DWORD dwBytesWritten;
+
+} LOCKED_BUFFER, *PLOCKED_BUFFER;
+
+//
+// Some of the response packet formats from ndsapi32.h
+//
+
+typedef struct {
+
+ DWORD CompletionCode;
+ DWORD RemoteEntry;
+ DWORD EntryId;
+ DWORD ServerAddresses;
+ DWORD AddressType;
+ DWORD AddressLength;
+
+ //
+ // The address is of length
+ // AddressLength, of course.
+ //
+
+ BYTE Address[1];
+
+} NDS_WIRE_RESPONSE_RESOLVE_NAME, *PNDS_WIRE_RESPONSE_RESOLVE_NAME;
+
+typedef struct {
+
+ DWORD CompletionCode;
+ DWORD RemoteEntry;
+ DWORD EntryId;
+ DWORD Unknown;
+ DWORD ServerAddresses;
+ DWORD AddressType;
+ DWORD AddressLength;
+
+ //
+ // The address is of length
+ // AddressLength, of course.
+ //
+
+ BYTE Address[1];
+
+} NDS_WIRE_RESPONSE_RESOLVE_NAME_REFERRAL,
+*PNDS_WIRE_RESPONSE_RESOLVE_NAME_REFERRAL;
+
+//
+// Strings for searching ds attributes.
+//
+
+#define PUBLIC_KEY_ATTRIBUTE L"Public Key"
+#define VOLUME_ATTRIBUTE L"Volume"
+#define QUEUE_ATTRIBUTE L"Queue"
+#define DIR_MAP_ATTRIBUTE L"Directory Map"
+#define HOST_SERVER_ATTRIBUTE L"Host Server"
+#define HOST_VOLUME_ATTRIBUTE L"Host Resource Name"
+#define HOST_QUEUE_ATTRIBUTE L"CN"
+#define HOST_PATH_ATTRIBUTE L"Path"
+#define NT_GATEWAY_GROUP L"NTGATEWAY"
+#define GROUPS_ATTRIBUTE L"Group Membership"
+
+//
+// Prototypes from ndslogin.c
+//
+
+NTSTATUS
+NdsCanonUserName(
+ IN PNDS_SECURITY_CONTEXT pNdsContext,
+ IN PUNICODE_STRING puUserName,
+ IN OUT PUNICODE_STRING puCanonUserName
+);
+
+NTSTATUS
+NdsCheckCredentials(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING puUserName,
+ IN PUNICODE_STRING puPassword
+);
+
+NTSTATUS
+NdsCheckCredentialsEx(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PLOGON pLogon,
+ IN PNDS_SECURITY_CONTEXT pNdsContext,
+ IN PUNICODE_STRING puUserName,
+ IN PUNICODE_STRING puPassword
+);
+
+#define CREDENTIAL_READ 0
+#define CREDENTIAL_WRITE 1
+
+NTSTATUS
+NdsLookupCredentials(
+ IN PUNICODE_STRING puTreeName,
+ IN PLOGON pLogon,
+ OUT PNDS_SECURITY_CONTEXT *ppCredentials,
+ DWORD dwDesiredAccess,
+ BOOLEAN fCreate
+);
+
+NTSTATUS
+NdsGetCredentials(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PLOGON pLogon,
+ IN PUNICODE_STRING puUserName,
+ IN PUNICODE_STRING puPassword
+);
+
+NTSTATUS
+ChangeNdsPassword(
+ PIRP_CONTEXT pIrpContext,
+ DWORD dwUserOID,
+ DWORD dwChallenge,
+ PBYTE pbOldPwHash,
+ PBYTE pbNewPwHash,
+ PNDS_PRIVATE_KEY pUserPrivKey,
+ PBYTE pServerPublicBsafeKey,
+ UINT ServerPubKeyLen
+);
+
+NTSTATUS
+DoNdsLogon(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING UserName,
+ IN PUNICODE_STRING Password
+);
+
+NTSTATUS
+NdsTreeLogin(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING puUser,
+ IN POEM_STRING pOemPassword,
+ IN POEM_STRING pOemNewPassword,
+ IN PLOGON pUserLogon
+);
+
+NTSTATUS
+BeginLogin(
+ IN PIRP_CONTEXT pIrpContext,
+ IN DWORD userId,
+ OUT DWORD *loginId,
+ OUT DWORD *challenge
+);
+
+NTSTATUS
+FinishLogin(
+ IN PIRP_CONTEXT pIrpContext,
+ IN DWORD dwUserOID,
+ IN DWORD dwLoginFlags,
+ IN BYTE pbEncryptedChallenge[16],
+ IN BYTE *pbServerPublicBsafeKey,
+ IN int cbServerPublicBsafeKeyLen,
+ OUT BYTE *pbUserEncPrivateNdsKey,
+ OUT int *pcbUserEncPrivateNdsKeyLen,
+ OUT DWORD *pdwCredentialStartTime,
+ OUT DWORD *pdwCredentialEndTime
+);
+
+NTSTATUS
+NdsServerAuthenticate(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PNDS_SECURITY_CONTEXT pNdsContext
+);
+
+NTSTATUS BeginAuthenticate(
+ IN PIRP_CONTEXT pIrpContext,
+ IN DWORD dwUserId,
+ OUT DWORD *pdwSvrRandom
+);
+
+NTSTATUS
+NdsLicenseConnection(
+ PIRP_CONTEXT pIrpContext
+);
+
+NTSTATUS
+NdsUnlicenseConnection(
+ PIRP_CONTEXT pIrpContext
+);
+
+NTSTATUS
+NdsLogoff(
+ IN PIRP_CONTEXT pIrpContext
+);
+
+//
+// Prototypes from fragex.c
+//
+
+NTSTATUS
+FragExWithWait(
+ IN PIRP_CONTEXT pIrpContext,
+ IN DWORD NdsVerb,
+ IN PLOCKED_BUFFER pReplyBuffer,
+ IN BYTE *NdsRequestStr,
+ ...
+);
+
+int
+_cdecl
+FormatBuf(
+ char *buf,
+ int bufLen,
+ const char *format,
+ va_list args
+);
+
+int
+_cdecl
+FormatBufS(
+ char *buf,
+ int bufLen,
+ const char *format,
+ ...
+);
+
+//
+// Prototypes from ndsfsctl.c
+//
+
+NTSTATUS
+NdsCreateTreeScb(
+ IN PIRP_CONTEXT pIrpContext,
+ IN OUT PSCB *ppScb,
+ IN PUNICODE_STRING puTree,
+ IN PUNICODE_STRING puUserName,
+ IN PUNICODE_STRING puPassword,
+ IN BOOLEAN DeferredLogon,
+ IN BOOLEAN DeleteOnClose
+);
+
+NTSTATUS
+NdsLookupServerName(
+ PSCB pTreeScb,
+ PIRP_CONTEXT pIrpContext,
+ IPXaddress *pDirServerAddress,
+ POEM_STRING pOemServerServerName
+);
+
+NTSTATUS
+DispatchNds(
+ IN ULONG IoctlCode,
+ IN PIRP_CONTEXT IrpContext
+);
+
+NTSTATUS
+PrepareLockedBufferFromFsd(
+ PIRP_CONTEXT pIrpContext,
+ PLOCKED_BUFFER pLockedBuffer
+);
+
+NTSTATUS
+DoBrowseFsctl( PIRP_CONTEXT pIrpContext,
+ ULONG IoctlCode,
+ BOOL LockdownBuffer
+);
+
+NTSTATUS
+ConnectBinderyVolume(
+ PIRP_CONTEXT pIrpContext,
+ PUNICODE_STRING puServerName,
+ PUNICODE_STRING puVolumeName
+);
+
+NTSTATUS
+HandleVolumeAttach(
+ PIRP_CONTEXT pIrpContext,
+ PUNICODE_STRING puServerName,
+ PUNICODE_STRING puVolumeName
+);
+
+NTSTATUS
+NdsGetDsObjectFromPath(
+ IN PIRP_CONTEXT pIrpContext,
+ OUT PUNICODE_STRING puDsObject
+);
+
+#define NDS_OBJECTTYPE_VOLUME 1
+#define NDS_OBJECTTYPE_QUEUE 2
+#define NDS_OBJECTTYPE_DIRMAP 3
+
+NTSTATUS
+NdsVerifyObject(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING puVolumeObject,
+ IN BOOLEAN fAllowServerJump,
+ IN DWORD dwResolverFlags,
+ OUT PDWORD pdwVolumeOid,
+ OUT PDWORD pdwObjectType
+);
+
+NTSTATUS
+NdsMapObjectToServerShare(
+ PIRP_CONTEXT pIrpContext,
+ PSCB *ppScb,
+ PUNICODE_STRING puServerSharePath,
+ BOOLEAN CreateTreeConnection,
+ PDWORD pdwObjectId
+);
+
+NTSTATUS
+NdsVerifyContext(
+ PIRP_CONTEXT pIrpContext,
+ PUNICODE_STRING puTree,
+ PUNICODE_STRING puContext
+);
+
+NTSTATUS
+NdsRawFragex(
+ PIRP_CONTEXT pIrpContext
+);
+
+NTSTATUS
+NdsChangePass(
+ PIRP_CONTEXT pIrpContext
+);
+
+NTSTATUS
+NdsListTrees(
+ PIRP_CONTEXT pIrpContext
+);
+
+//
+// Browsing prototypes from ndsread.c
+//
+
+NTSTATUS
+NdsGetServerBasicName(
+ IN PUNICODE_STRING pServerX500Name,
+ IN OUT PUNICODE_STRING pServerName
+);
+
+NTSTATUS
+NdsCheckGroupMembership(
+ PIRP_CONTEXT pIrpContext,
+ DWORD dwUserOid,
+ PUNICODE_STRING puGroupName
+);
+
+NTSTATUS
+NdsResolveName(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PNWR_NDS_REQUEST_PACKET pNdsRequest,
+ IN PLOCKED_BUFFER pLockedBuffer
+);
+
+NTSTATUS
+NdsGetObjectInfo(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PNWR_NDS_REQUEST_PACKET pNdsRequest,
+ IN PLOCKED_BUFFER pLockedBuffer
+);
+
+NTSTATUS
+NdsListSubordinates(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PNWR_NDS_REQUEST_PACKET pNdsRequest,
+ IN PLOCKED_BUFFER pLockedBuffer
+);
+
+NTSTATUS
+NdsReadAttributes(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest,
+ PLOCKED_BUFFER pLockedBuffer
+);
+
+NTSTATUS
+NdsReadAttributesKm(
+ PIRP_CONTEXT pIrpContext,
+ DWORD dwObjectId,
+ PUNICODE_STRING puAttribute,
+ PLOCKED_BUFFER pLockedBuffer
+);
+
+NTSTATUS
+NdsOpenStream(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest
+);
+
+NTSTATUS
+NdsSetContext(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest
+);
+
+NTSTATUS
+NdsGetContext(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest
+);
+
+NTSTATUS
+NdsVerifyTreeHandle(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest
+);
+
+NTSTATUS
+NdsGetPrintQueueInfo(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest
+);
+
+NTSTATUS
+NdsGetVolumeInformation(
+ PIRP_CONTEXT pIrpContext,
+ PNWR_NDS_REQUEST_PACKET pNdsRequest
+);
+
+//
+// Kernel mode browsing prototypes from ndsread.c
+//
+
+NTSTATUS
+NdsResolveNameKm (
+ PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING puObjectName,
+ OUT DWORD *dwObjectId,
+ BOOLEAN AllowDsJump,
+ DWORD dwFlags
+);
+
+NTSTATUS
+NdsReadStringAttribute(
+ PIRP_CONTEXT pIrpContext,
+ IN DWORD dwObjectId,
+ IN PUNICODE_STRING puAttributeName,
+ OUT PUNICODE_STRING puAttributeVal
+);
+
+NTSTATUS
+NdsGetServerName(
+ IN PIRP_CONTEXT pIrpContext,
+ OUT PUNICODE_STRING pUnicodeString
+);
+
+NTSTATUS
+NdsGetUserName(
+ IN PIRP_CONTEXT pIrpContext,
+ IN DWORD dwUserOid,
+ OUT PUNICODE_STRING puUserName
+);
+
+//
+// Other helper prototypes from ndsread.c
+//
+
+VOID
+FreeNdsContext(
+ PNDS_SECURITY_CONTEXT pNdsContext
+);
+
+VOID
+NdsPing(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PSCB pScb
+);
+
+NTSTATUS
+NdsSelectConnection(
+ PIRP_CONTEXT pIrpContext,
+ PUNICODE_STRING puTreeName,
+ PUNICODE_STRING puUserName,
+ PUNICODE_STRING puPassword,
+ BOOL DeferredLogon,
+ BOOL UseBinderyConnections,
+ PNONPAGED_SCB *ppNpScb
+);
+
+NTSTATUS
+NdsCompletionCodetoNtStatus(
+ IN PLOCKED_BUFFER pLockedBuffer
+);
+
+NTSTATUS
+NdsReadPublicKey(
+ IN PIRP_CONTEXT pIrpContext,
+ IN DWORD entryId,
+ OUT BYTE *pPubKeyVal,
+ IN DWORD *pPubKeyLen
+);
+
+int
+NdsGetBsafeKey(
+ UCHAR *pPubKey,
+ const int pubKeyLen,
+ UCHAR **ppBsafeKey
+);
+
+NTSTATUS
+NdsAllocateLockedBuffer(
+ PLOCKED_BUFFER NdsRequest,
+ DWORD BufferSize
+);
+
+NTSTATUS
+NdsFreeLockedBuffer(
+ PLOCKED_BUFFER NdsRequest
+);
+
+
diff --git a/private/nw/rdr/ndsread.c b/private/nw/rdr/ndsread.c
new file mode 100644
index 000000000..189fc9f1c
--- /dev/null
+++ b/private/nw/rdr/ndsread.c
@@ -0,0 +1,1190 @@
+/*++
+
+Copyright (c) 1995 Microsoft Corporation
+
+Module Name:
+
+ NdsRead.c
+
+Abstract:
+
+ This module implements the NDS read and request routines called
+ by the redirector natively and the support routines that go with
+ them.
+
+Author:
+
+ Cory West [CoryWest] 23-Feb-1995
+
+--*/
+
+#include "Procs.h"
+
+#define Dbg (DEBUG_TRACE_NDS)
+
+#pragma alloc_text( PAGE, NdsResolveNameKm )
+#pragma alloc_text( PAGE, NdsReadStringAttribute )
+#pragma alloc_text( PAGE, NdsReadAttributesKm )
+#pragma alloc_text( PAGE, NdsCompletionCodetoNtStatus )
+#pragma alloc_text( PAGE, FreeNdsContext )
+#pragma alloc_text( PAGE, NdsPing )
+#pragma alloc_text( PAGE, NdsGetUserName )
+#pragma alloc_text( PAGE, NdsGetServerBasicName )
+#pragma alloc_text( PAGE, NdsGetServerName )
+#pragma alloc_text( PAGE, NdsReadPublicKey )
+#pragma alloc_text( PAGE, NdsCheckGroupMembership )
+#pragma alloc_text( PAGE, NdsAllocateLockedBuffer )
+#pragma alloc_text( PAGE, NdsFreeLockedBuffer )
+
+NTSTATUS
+NdsResolveNameKm (
+ PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING puObjectName,
+ OUT DWORD *dwObjectId,
+ BOOLEAN AllowDsJump,
+ DWORD dwFlags
+)
+/*++
+
+Description:
+
+ This is a wrapper routine to the browser routine NdsResolveName
+ for kernel components that need to resolve NDS names.
+
+Arguments:
+
+ pIrpContext - must point to the dir server that we should query
+ puObjectName - what we want to resolve
+ *dwObjectId - where to report the result
+ AllowDsJump - if we are referred to another dir server, can we jump?
+
+--*/
+{
+
+ NTSTATUS Status;
+
+ PNWR_NDS_REQUEST_PACKET Rrp;
+
+ PNDS_RESPONSE_RESOLVE_NAME Rsp;
+ LOCKED_BUFFER NdsRequestBuffer;
+
+ PSCB Scb, OldScb;
+ UNICODE_STRING ReferredServer;
+
+ PAGED_CODE();
+
+ //
+ // Prepare the request and response buffers.
+ //
+
+ Rrp = ALLOCATE_POOL( PagedPool, NDS_BUFFER_SIZE );
+
+ if ( !Rrp ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = NdsAllocateLockedBuffer( &NdsRequestBuffer, NDS_BUFFER_SIZE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ FREE_POOL( Rrp );
+ return Status;
+ }
+
+ //
+ // Set up the request packet.
+ //
+
+ RtlZeroMemory( Rrp, NDS_BUFFER_SIZE );
+
+ Rrp->Version = 0;
+ Rrp->Parameters.ResolveName.ObjectNameLength = puObjectName->Length;
+ Rrp->Parameters.ResolveName.ResolverFlags = dwFlags;
+
+ RtlCopyMemory( Rrp->Parameters.ResolveName.ObjectName,
+ puObjectName->Buffer,
+ puObjectName->Length );
+
+ //
+ // Do the resolve.
+ //
+
+ Status = NdsResolveName( pIrpContext, Rrp, &NdsRequestBuffer );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ Status = NdsCompletionCodetoNtStatus( &NdsRequestBuffer );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ Rsp = ( PNDS_RESPONSE_RESOLVE_NAME ) NdsRequestBuffer.pRecvBufferVa;
+
+ if ( ( Rsp->RemoteEntry == RESOLVE_NAME_REFER_REMOTE ) &&
+ ( AllowDsJump ) ) {
+
+ //
+ // We need to queue this request to another server
+ // since this server doesn't have any details about
+ // the object.
+ //
+
+ ReferredServer.Length = (USHORT) Rsp->ServerNameLength;
+ ReferredServer.MaximumLength = ReferredServer.Length;
+ ReferredServer.Buffer = Rsp->ReferredServer;
+
+ OldScb = pIrpContext->pScb;
+ ASSERT( OldScb != NULL );
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+
+ Status = CreateScb( &Scb,
+ pIrpContext,
+ &ReferredServer,
+ NULL,
+ NULL,
+ NULL,
+ TRUE,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Since we've jumped servers, dereference the old host
+ // server. The new one was referenced in CreateScb().
+ //
+
+ NwDereferenceScb( OldScb->pNpScb );
+
+ }
+
+ *dwObjectId = Rsp->EntryId;
+
+ExitWithCleanup:
+
+ NdsFreeLockedBuffer( &NdsRequestBuffer );
+ FREE_POOL( Rrp );
+ return Status;
+
+}
+
+NTSTATUS
+NdsReadStringAttribute(
+ PIRP_CONTEXT pIrpContext,
+ IN DWORD dwObjectId,
+ IN PUNICODE_STRING puAttributeName,
+ OUT PUNICODE_STRING puAttributeVal
+)
+/*++
+
+Description:
+
+ This is a wrapper routine to the browser routine NdsReadAttributes
+ for kernel components that need to read NDS string attributes.
+
+Arguments:
+
+ pIrpContext - must point to the dir server that we should query
+ dwObjectId - oid of the object to query
+ puAttributeName - attribute that we want
+ puAttributeVal - value of the attribute
+
+--*/
+{
+
+ NTSTATUS Status;
+ PNWR_NDS_REQUEST_PACKET Rrp;
+ DWORD dwRequestSize, dwAttributeCount;
+ LOCKED_BUFFER NdsRequest;
+
+ PAGED_CODE();
+
+ //
+ // Set up the request and response buffers.
+ //
+
+ dwRequestSize = sizeof( NWR_NDS_REQUEST_PACKET ) + puAttributeName->Length;
+
+ Rrp = ( PNWR_NDS_REQUEST_PACKET ) ALLOCATE_POOL( PagedPool, dwRequestSize );
+
+ if ( !Rrp ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ FREE_POOL( Rrp );
+ return Status;
+ }
+
+ //
+ // Prepare the request packet.
+ //
+
+ RtlZeroMemory( (BYTE *)Rrp, dwRequestSize );
+
+ Rrp->Version = 0;
+ Rrp->Parameters.ReadAttribute.ObjectId = dwObjectId;
+ Rrp->Parameters.ReadAttribute.IterHandle = DUMMY_ITER_HANDLE;
+ Rrp->Parameters.ReadAttribute.AttributeNameLength = puAttributeName->Length;
+
+ RtlCopyMemory( Rrp->Parameters.ReadAttribute.AttributeName,
+ puAttributeName->Buffer,
+ puAttributeName->Length );
+
+ //
+ // Make the request.
+ //
+
+ Status = NdsReadAttributes( pIrpContext, Rrp, &NdsRequest );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Dig out the string attribute and return it.
+ //
+
+ Status = ParseResponse( NULL,
+ NdsRequest.pRecvBufferVa,
+ NdsRequest.dwBytesWritten,
+ "G___D_S_T",
+ sizeof( DWORD ), // completion code
+ sizeof( DWORD ), // iter handle
+ sizeof( DWORD ), // info type
+ &dwAttributeCount, // attribute count
+ sizeof( DWORD ), // syntax id
+ NULL, // attribute name
+ sizeof( DWORD ), // number of values
+ puAttributeVal ); // attribute string
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+
+ExitWithCleanup:
+
+ FREE_POOL( Rrp );
+ NdsFreeLockedBuffer( &NdsRequest );
+ return Status;
+
+}
+
+NTSTATUS
+NdsReadAttributesKm(
+ PIRP_CONTEXT pIrpContext,
+ IN DWORD dwObjectId,
+ IN PUNICODE_STRING puAttributeName,
+ IN OUT PLOCKED_BUFFER pNdsRequest
+)
+/*++
+
+Description:
+
+ This is a wrapper routine to the browser routine NdsReadAttributes
+ for kernel components that need to read NDS string attributes and
+ get back the raw response.
+
+Arguments:
+
+ pIrpContext - must point to the dir server that we should query
+ dwObjectId - oid of the object to query
+ puAttributeName - attribute that we want
+ puAttributeVal - value of the attribute
+
+--*/
+{
+
+ NTSTATUS Status;
+ PNWR_NDS_REQUEST_PACKET Rrp;
+ DWORD dwRequestSize;
+
+ PAGED_CODE();
+
+ //
+ // Set up the request.
+ //
+
+ dwRequestSize = sizeof( NWR_NDS_REQUEST_PACKET ) + puAttributeName->Length;
+
+ Rrp = ( PNWR_NDS_REQUEST_PACKET ) ALLOCATE_POOL( PagedPool, dwRequestSize );
+
+ if ( !Rrp ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ RtlZeroMemory( (BYTE *)Rrp, dwRequestSize );
+
+ Rrp->Version = 0;
+ Rrp->Parameters.ReadAttribute.ObjectId = dwObjectId;
+ Rrp->Parameters.ReadAttribute.IterHandle = DUMMY_ITER_HANDLE;
+ Rrp->Parameters.ReadAttribute.AttributeNameLength = puAttributeName->Length;
+
+ RtlCopyMemory( Rrp->Parameters.ReadAttribute.AttributeName,
+ puAttributeName->Buffer,
+ puAttributeName->Length );
+
+ Status = NdsReadAttributes( pIrpContext, Rrp, pNdsRequest );
+
+ FREE_POOL( Rrp );
+ return Status;
+
+}
+
+//
+// Frosting and other helper wrapper functions.
+//
+
+NTSTATUS
+NdsCompletionCodetoNtStatus(
+ IN PLOCKED_BUFFER pLockedBuffer
+)
+/*+++
+
+Description:
+
+ Translates the completion code of an NDS transaction into
+ an NTSTATUS error code.
+
+Arguments:
+
+ pLockedBuffer - describes the locked reply buffer that contains
+ the response.
+
+---*/
+{
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ //
+ // Try to get the completion code from the user's buffer.
+ //
+
+ try {
+
+ Status = *((DWORD *)pLockedBuffer->pRecvBufferVa);
+
+ } except ( EXCEPTION_EXECUTE_HANDLER ) {
+
+ return STATUS_UNSUCCESSFUL;
+
+ }
+
+ //
+ // Decode it.
+ //
+
+ if ( Status != STATUS_SUCCESS ) {
+
+ DebugTrace( 0, Dbg, "NDS Error Code: %08lx\n", Status );
+
+ switch ( Status ) {
+
+ case -601: // No such entry.
+ case -602: // No such value.
+ case -603: // No such attribute.
+ case -607: // Illegal attribute.
+ case -610: // Illegal ds name.
+
+ Status = STATUS_BAD_NETWORK_PATH;
+ break;
+
+ //
+ // These may only come on a VERIFY_PASSWORD verb, which
+ // we do not support. I'm not sure, though.
+ //
+
+ case -216: // Password too short.
+ case -215: // Duplicate password.
+
+ Status = STATUS_PASSWORD_RESTRICTION;
+ break;
+
+ case -222: // Expired password (and no grace logins left).
+
+ Status = STATUS_PASSWORD_EXPIRED;
+ break;
+
+ case -223: // Expired password; this is a successful grace login.
+
+ Status = NWRDR_PASSWORD_HAS_EXPIRED;
+ break;
+
+ case -639: // Incomplete authentication.
+ case -672: // No access.
+ case -677: // Invalid identity.
+ case -669: // Wrong password.
+
+ Status = STATUS_WRONG_PASSWORD;
+ break;
+
+ case -197: // Intruder lockout active.
+ case -220: // Account expired or disabled.
+
+ Status = STATUS_ACCOUNT_DISABLED;
+ break;
+
+ case -218: // Login time restrictions.
+
+ Status = STATUS_LOGIN_TIME_RESTRICTION;
+ break;
+
+ case -217: // Maximum logins exceeded.
+
+ Status = STATUS_CONNECTION_COUNT_LIMIT;
+ break;
+
+ default:
+
+ Status = STATUS_UNSUCCESSFUL;
+ }
+
+ }
+
+ return Status;
+}
+
+VOID
+FreeNdsContext(
+ IN PNDS_SECURITY_CONTEXT pNdsSecContext
+)
+/*++
+
+Routine Description:
+
+ Free the referenced NDS context.
+
+--*/
+{
+ PAGED_CODE();
+
+ //
+ // Make sure this is a valid thing to be mucking with.
+ //
+
+ if ( !pNdsSecContext ||
+ pNdsSecContext->ntc != NW_NTC_NDS_CREDENTIAL ) {
+
+ DebugTrace( 0, Dbg, "FreeNdsContext didn't get an NDS context.\n", 0 );
+ return;
+ }
+
+ if ( pNdsSecContext->Credential ) {
+ FREE_POOL( pNdsSecContext->Credential );
+ }
+
+ if ( pNdsSecContext->Signature ) {
+ FREE_POOL( pNdsSecContext->Signature );
+ }
+
+ if ( pNdsSecContext->PublicNdsKey ) {
+ FREE_POOL( pNdsSecContext->PublicNdsKey );
+ }
+
+ if ( pNdsSecContext->Password.Buffer ) {
+ FREE_POOL( pNdsSecContext->Password.Buffer );
+ }
+
+ DebugTrace( 0, Dbg, "Freeing NDS security context at 0x%08lx\n", pNdsSecContext );
+
+ FREE_POOL( pNdsSecContext );
+
+ return;
+}
+
+VOID
+NdsPing(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PSCB pScb
+)
+/*++
+
+Routine Description:
+
+ Examine the server for NDS support and record the NDS tree
+ name in the SCB for later reference.
+
+Routine Arguments:
+
+ pIrpContext - A pointer to the IRP context for this transaction.
+ pScb - The SCB for the server.
+
+Return Value:
+
+ NTSTATUS - Status of the operation.
+
+--*/
+{
+
+ NTSTATUS Status;
+
+ OEM_STRING OemTreeName;
+ BYTE OemBuffer[NDS_TREE_NAME_LEN];
+
+ UNICODE_STRING TreeName;
+ WCHAR WBuffer[NDS_TREE_NAME_LEN];
+
+ UNICODE_STRING CredentialName;
+
+ PAGED_CODE();
+
+ pScb->NdsTreeName.Length = 0;
+
+ OemTreeName.Length = NDS_TREE_NAME_LEN;
+ OemTreeName.MaximumLength = NDS_TREE_NAME_LEN;
+ OemTreeName.Buffer = OemBuffer;
+
+ Status = ExchangeWithWait( pIrpContext,
+ SynchronousResponseCallback,
+ "N",
+ NDS_REQUEST, // NDS Function 104
+ NDS_PING ); // NDS Subfunction 1
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return;
+ }
+
+ //
+ // Pull out the padded NDS name
+ //
+
+ Status = ParseResponse( pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "N_r",
+ 2 * sizeof( DWORD ),
+ OemBuffer,
+ NDS_TREE_NAME_LEN );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return;
+ }
+
+ //
+ // Strip off the padding and convert to unicode.
+ //
+
+ while ( OemTreeName.Length > 0 &&
+ OemBuffer[OemTreeName.Length - 1] == '_' ) {
+ OemTreeName.Length--;
+ }
+
+ //
+ // Copy or munge the tree name, depending on the create type.
+ //
+
+ if ( pIrpContext->Specific.Create.fExCredentialCreate ) {
+
+ TreeName.Length = 0;
+ TreeName.MaximumLength = sizeof( WBuffer );
+ TreeName.Buffer = WBuffer;
+
+ Status = RtlOemStringToUnicodeString( &TreeName,
+ &OemTreeName,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ pScb->NdsTreeName.Length = 0;
+ return;
+ }
+
+ Status = BuildExCredentialServerName( &TreeName,
+ pIrpContext->Specific.Create.puCredentialName,
+ &CredentialName );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return;
+ }
+
+ RtlCopyUnicodeString( &pScb->NdsTreeName, &CredentialName );
+
+ FREE_POOL( CredentialName.Buffer );
+
+ } else {
+
+ Status = RtlOemStringToUnicodeString( &pScb->NdsTreeName,
+ &OemTreeName,
+ FALSE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ pScb->NdsTreeName.Length = 0;
+ return;
+ }
+
+ }
+
+ DebugTrace( 0, Dbg, "Nds Ping: Tree is ""%wZ""\n", &pScb->NdsTreeName);
+ return;
+
+}
+
+NTSTATUS
+NdsGetUserName(
+ IN PIRP_CONTEXT pIrpContext,
+ IN DWORD dwUserOid,
+ OUT PUNICODE_STRING puUserName
+)
+/*++
+
+Description:
+
+ Get the fully distinguished name of the user referred to
+ by the provided oid.
+
+--*/
+{
+ NTSTATUS Status;
+ LOCKED_BUFFER NdsRequest;
+
+ PAGED_CODE();
+
+ //
+ // Allocate buffer space.
+ //
+
+ Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // Make the request.
+ //
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_READ_ENTRY_INFO,
+ &NdsRequest,
+ "DD",
+ 0,
+ dwUserOid );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto ExitWithCleanup;
+ }
+
+ Status = NdsCompletionCodetoNtStatus( &NdsRequest );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ Status = ParseResponse( NULL,
+ NdsRequest.pRecvBufferVa,
+ NdsRequest.dwBytesWritten,
+ "G_St",
+ sizeof( NDS_RESPONSE_GET_OBJECT_INFO ),
+ NULL,
+ puUserName );
+
+ //
+ // We either got it or we didn't.
+ //
+
+ExitWithCleanup:
+
+ NdsFreeLockedBuffer( &NdsRequest );
+ return Status;
+
+}
+
+NTSTATUS
+NdsGetServerBasicName(
+ IN PUNICODE_STRING pServerX500Name,
+ IN OUT PUNICODE_STRING pServerName
+) {
+
+ //
+ // Dig out the first component of the server's X.500 name.
+ // We count on the X500 prefix for the server object being "CN=",
+ // which might be unwise.
+ //
+
+ USHORT usPrefixSize, usSrv;
+
+ PAGED_CODE();
+
+ usPrefixSize = sizeof( "CN=" ) - sizeof( "" );
+ usSrv = 0;
+
+ if ( ( pServerX500Name->Buffer[0] != L'C' ) ||
+ ( pServerX500Name->Buffer[1] != L'N' ) ||
+ ( pServerX500Name->Buffer[2] != L'=' ) ) {
+
+ DebugTrace( 0, Dbg, "NdsGetServerBasicName: Bad prefix.\n", 0 );
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ if ( pServerX500Name->Length <= usPrefixSize ) {
+
+ DebugTrace( 0, Dbg, "NdsGetServerBasicName: Bad string length.\n", 0 );
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ pServerName->Buffer = pServerX500Name->Buffer + usPrefixSize;
+ pServerName->Length = 0;
+
+ while ( ( usSrv < MAX_SERVER_NAME_LENGTH ) &&
+ ( pServerName->Buffer[usSrv++] != L'.' ) ) {
+
+ pServerName->Length += sizeof( WCHAR );
+ }
+
+ if ( usSrv == MAX_SERVER_NAME_LENGTH ) {
+
+ DebugTrace( 0, Dbg, "NdsGetServerBasicName: Bad server name response.\n", 0 );
+ return STATUS_BAD_NETWORK_PATH;
+ }
+
+ pServerName->MaximumLength = pServerName->Length;
+ return STATUS_SUCCESS;
+
+}
+
+NTSTATUS
+NdsGetServerName(
+ IN PIRP_CONTEXT pIrpContext,
+ OUT PUNICODE_STRING puServerName
+)
+/*++
+
+Description:
+
+ Get the fully distinguished name of the server that we
+ are connected to.
+
+--*/
+{
+
+ NTSTATUS Status;
+ LOCKED_BUFFER NdsRequest;
+
+ PAGED_CODE();
+
+ //
+ // Make the request.
+ //
+
+ Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = FragExWithWait( pIrpContext,
+ NDSV_GET_SERVER_ADDRESS,
+ &NdsRequest,
+ NULL );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto ExitWithCleanup;
+ }
+
+ Status = NdsCompletionCodetoNtStatus( &NdsRequest );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Get the server name from the response.
+ //
+
+ Status = ParseResponse( NULL,
+ NdsRequest.pRecvBufferVa,
+ NdsRequest.dwBytesWritten,
+ "G_T",
+ sizeof( DWORD ),
+ puServerName );
+
+ if ( !NT_SUCCESS(Status) ) {
+ goto ExitWithCleanup;
+ }
+
+ExitWithCleanup:
+
+ NdsFreeLockedBuffer( &NdsRequest );
+ return Status;
+
+}
+
+NTSTATUS
+NdsReadPublicKey(
+ IN PIRP_CONTEXT pIrpContext,
+ IN DWORD dwEntryId,
+ OUT BYTE *pPubKeyVal,
+ IN OUT DWORD *pPubKeyLen
+)
+/*++
+
+Routine Description:
+
+ Read the public key referenced by the given entry id.
+
+Routine Arguments:
+
+ pIrpContext - The IRP context for this connection.
+ dwEntryId - The entry id of the key.
+ pPubKeyVal - The destination buffer for the public key.
+ pPubKeyLen - The length of the public key destination buffer.
+
+Return Value:
+
+ The length of the key.
+
+--*/
+{
+ NTSTATUS Status;
+
+ LOCKED_BUFFER NdsRequest;
+
+ PNWR_NDS_REQUEST_PACKET Rrp;
+
+ DWORD dwAttrNameLen, dwAttrLen, dwRcvLen, dwNumEntries;
+ BYTE *pRcv;
+
+ PAGED_CODE();
+
+ //
+ // Allocate and zero send and receive space.
+ //
+
+ Rrp = ALLOCATE_POOL( PagedPool, NDS_BUFFER_SIZE );
+
+ if ( !Rrp ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ FREE_POOL( Rrp );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // Fill in and prepare the request buffer.
+ //
+
+ RtlZeroMemory( Rrp, NDS_BUFFER_SIZE );
+
+ Rrp->Version = 0;
+ Rrp->Parameters.ReadAttribute.ObjectId = dwEntryId;
+ Rrp->Parameters.ReadAttribute.IterHandle = DUMMY_ITER_HANDLE;
+ Rrp->Parameters.ReadAttribute.AttributeNameLength =
+ sizeof( PUBLIC_KEY_ATTRIBUTE ) - sizeof( WCHAR );
+
+ RtlCopyMemory( Rrp->Parameters.ReadAttribute.AttributeName,
+ PUBLIC_KEY_ATTRIBUTE,
+ sizeof( PUBLIC_KEY_ATTRIBUTE ) - sizeof( WCHAR ) );
+
+ //
+ // Do the exchange.
+ //
+
+ Status = NdsReadAttributes( pIrpContext,
+ Rrp,
+ &NdsRequest );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Skip over the attribute header and name.
+ //
+
+ Status = ParseResponse( NULL,
+ NdsRequest.pRecvBufferVa,
+ NdsRequest.dwBytesWritten,
+ "G_D",
+ 5 * sizeof( DWORD ),
+ &dwAttrNameLen );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Skip over the part we've parsed and pull out the attribute.
+ //
+
+ pRcv = (PBYTE)NdsRequest.pRecvBufferVa +
+ ( 6 * sizeof( DWORD ) ) +
+ ROUNDUP4(dwAttrNameLen);
+
+ dwRcvLen = NdsRequest.dwBytesWritten -
+ ( 6 * sizeof( DWORD ) ) +
+ ROUNDUP4(dwAttrNameLen);
+
+ Status = ParseResponse( NULL,
+ pRcv,
+ dwRcvLen,
+ "GDD",
+ &dwNumEntries,
+ &dwAttrLen );
+
+ if ( !NT_SUCCESS( Status ) ||
+ dwNumEntries != 1 ) {
+
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+ }
+
+ DebugTrace( 0, Dbg, "Public Key Length: %d\n", dwAttrLen );
+ pRcv += ( 2 * sizeof( DWORD ) );
+
+ if ( dwAttrLen <= *pPubKeyLen ) {
+
+ RtlCopyMemory( pPubKeyVal, pRcv, dwAttrLen );
+ *pPubKeyLen = dwAttrLen;
+ Status = STATUS_SUCCESS;
+
+ } else {
+
+ DebugTrace( 0, Dbg, "Public key buffer is too small.\n", 0 );
+ Status = STATUS_BUFFER_TOO_SMALL;
+ }
+
+ExitWithCleanup:
+
+ NdsFreeLockedBuffer( &NdsRequest );
+ FREE_POOL( Rrp );
+ return Status;
+
+}
+
+NTSTATUS
+NdsCheckGroupMembership(
+ PIRP_CONTEXT pIrpContext,
+ DWORD dwUserOid,
+ PUNICODE_STRING puGroupName
+) {
+
+ NTSTATUS Status;
+ UNICODE_STRING GroupListAttribute;
+ LOCKED_BUFFER NdsRequest;
+
+ PNDS_RESPONSE_READ_ATTRIBUTE pAttributeResponse;
+ PNDS_ATTRIBUTE pAttribute;
+ PBYTE pAttribData;
+ DWORD dwAttribLength, dwCurrentLength;
+ DWORD dwNumAttributes, dwCurrentAttribute;
+ UNICODE_STRING Group;
+ USHORT GroupLength;
+
+ PAGED_CODE();
+
+ RtlInitUnicodeString( &GroupListAttribute, GROUPS_ATTRIBUTE );
+
+ Status = NdsAllocateLockedBuffer( &NdsRequest, NDS_BUFFER_SIZE );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = NdsReadAttributesKm( pIrpContext,
+ dwUserOid,
+ &GroupListAttribute,
+ &NdsRequest );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ goto ExitWithCleanup;
+ }
+
+ pAttributeResponse = ( PNDS_RESPONSE_READ_ATTRIBUTE ) NdsRequest.pRecvBufferVa;
+ ASSERT( pAttributeResponse->NumAttributes > 0 );
+
+ //
+ // Skip over the response header and walk down the attribute
+ // until we get to the data. This is a little clunky.
+ //
+
+ pAttribute = ( PNDS_ATTRIBUTE ) ( pAttributeResponse + 1 );
+ dwCurrentLength = sizeof( NDS_RESPONSE_READ_ATTRIBUTE );
+
+ dwAttribLength = ROUNDUP4( pAttribute->AttribNameLength );
+ dwAttribLength += ( 2 * sizeof( DWORD ) );
+
+ //
+ // Make sure we don't walk past the end of the buffer because
+ // of a bad packet from the server.
+ //
+
+ if ( ( dwCurrentLength + dwAttribLength ) > NDS_BUFFER_SIZE ) {
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ pAttribData = ( ( BYTE * )pAttribute ) + dwAttribLength;
+ dwCurrentLength += dwAttribLength;
+
+ //
+ // This is DWORD aligned for four byte DWORDs.
+ //
+
+ if ( ( NDS_BUFFER_SIZE - dwCurrentLength ) < sizeof( DWORD ) ) {
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ dwNumAttributes = * ( ( DWORD * ) pAttribData );
+
+ if ( dwNumAttributes == 0 ) {
+ Status = STATUS_UNSUCCESSFUL;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Each attribute is an NDS string DWORD aligned.
+ //
+
+ Status = STATUS_UNSUCCESSFUL;
+
+ pAttribData += sizeof( DWORD );
+ dwCurrentLength += sizeof( DWORD );
+
+ for ( dwCurrentAttribute = 0;
+ dwCurrentAttribute < dwNumAttributes ;
+ dwCurrentAttribute++ ) {
+
+ Group.Length = Group.MaximumLength =
+ ( USHORT )( * ( ( DWORD * ) pAttribData ) ) - sizeof( WCHAR );
+ Group.Buffer = ( PWCHAR ) ( pAttribData + sizeof( DWORD ) );
+
+ if ( ( Group.Length + dwCurrentLength ) > NDS_BUFFER_SIZE ) {
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ //
+ // Strip off the X500 prefix and the context.
+ //
+
+ GroupLength = 0;
+
+ while ( GroupLength < ( Group.Length / sizeof( WCHAR ) ) ) {
+
+ if ( Group.Buffer[GroupLength++] == L'=' ) {
+
+ Group.Buffer += 1;
+ Group.Length -= sizeof( WCHAR );
+ Group.MaximumLength -= sizeof( WCHAR );
+
+ GroupLength = ( Group.Length / sizeof( WCHAR ) );
+ }
+
+ Group.Buffer += 1;
+ Group.Length -= sizeof( WCHAR );
+ Group.MaximumLength -= sizeof( WCHAR );
+ }
+
+ GroupLength = 0;
+
+ while ( GroupLength < ( Group.Length / sizeof( WCHAR ) ) ) {
+
+ if ( Group.Buffer[GroupLength++] == L'.' ) {
+ Group.Length = ( GroupLength - 1 ) * sizeof( WCHAR );
+ Group.MaximumLength = Group.Length;
+ break;
+ }
+ }
+
+ if ( RtlEqualUnicodeString( puGroupName, &Group, TRUE ) ) {
+
+ DebugTrace( 0, Dbg, "Group check for %wZ succeeded.\n", &Group );
+ Status = STATUS_SUCCESS;
+ goto ExitWithCleanup;
+ }
+
+ //
+ // Dig out the attribute size and process the next entry.
+ //
+
+ dwAttribLength = ROUNDUP4( * ( ( DWORD * ) pAttribData ) );
+ dwAttribLength += sizeof( DWORD );
+ pAttribData += dwAttribLength;
+ dwCurrentLength += dwAttribLength;
+
+ }
+
+ExitWithCleanup:
+
+ NdsFreeLockedBuffer( &NdsRequest );
+ return Status;
+}
+
+
+NTSTATUS
+NdsAllocateLockedBuffer(
+ PLOCKED_BUFFER NdsRequest,
+ DWORD BufferSize
+)
+/*++
+
+Description:
+
+ Allocate a buffer for io. Lock it down and fill in the
+ buffer data structure that we pass around.
+
+--*/
+{
+
+ PAGED_CODE();
+
+ NdsRequest->pRecvBufferVa = ALLOCATE_POOL( PagedPool, BufferSize );
+
+ if ( !NdsRequest->pRecvBufferVa ) {
+ DebugTrace( 0, Dbg, "Couldn't allocate locked io buffer.\n", 0 );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ NdsRequest->dwRecvLen = BufferSize;
+ NdsRequest->pRecvMdl = ALLOCATE_MDL( NdsRequest->pRecvBufferVa,
+ BufferSize,
+ FALSE,
+ FALSE,
+ NULL );
+
+ if ( !NdsRequest->pRecvMdl ) {
+ DebugTrace( 0, Dbg, "Couldn't allocate mdl for locked io buffer.\n", 0 );
+ FREE_POOL( NdsRequest->pRecvBufferVa );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ MmProbeAndLockPages( NdsRequest->pRecvMdl,
+ KernelMode,
+ IoWriteAccess );
+
+ return STATUS_SUCCESS;
+
+}
+
+NTSTATUS
+NdsFreeLockedBuffer(
+ PLOCKED_BUFFER NdsRequest
+)
+/*++
+
+Description:
+
+ Free a buffer allocated for io.
+
+--*/
+{
+
+ PAGED_CODE();
+
+ MmUnlockPages( NdsRequest->pRecvMdl );
+ FREE_MDL( NdsRequest->pRecvMdl );
+ FREE_POOL( NdsRequest->pRecvBufferVa );
+ return STATUS_SUCCESS;
+
+}
diff --git a/private/nw/rdr/nodetype.h b/private/nw/rdr/nodetype.h
new file mode 100644
index 000000000..7abbe1537
--- /dev/null
+++ b/private/nw/rdr/nodetype.h
@@ -0,0 +1,63 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ NodeType.h
+
+Abstract:
+
+ This module defines all of the node type codes used in this development
+ shell. Every major data structure in the file system is assigned a node
+ type code that is. This code is the first CSHORT in the structure and is
+ followed by a CSHORT containing the size, in bytes, of the structure.
+
+Author:
+
+ Colin Watson [ColinW] 18-Dec-1992
+
+Revision History:
+
+--*/
+
+#ifndef _NODETYPE_
+#define _NODETYPE_
+
+typedef CSHORT NODE_TYPE_CODE;
+typedef NODE_TYPE_CODE *PNODE_TYPE_CODE;
+
+#define NTC_UNDEFINED ((NODE_TYPE_CODE)0x0000)
+
+#define NW_NTC_SCB ((NODE_TYPE_CODE)0x0F01)
+#define NW_NTC_SCBNP ((NODE_TYPE_CODE)0x0F02)
+#define NW_NTC_FCB ((NODE_TYPE_CODE)0x0F03)
+#define NW_NTC_DCB ((NODE_TYPE_CODE)0x0F04)
+#define NW_NTC_VCB ((NODE_TYPE_CODE)0x0F05)
+#define NW_NTC_ICB ((NODE_TYPE_CODE)0x0F06)
+#define NW_NTC_IRP_CONTEXT ((NODE_TYPE_CODE)0x0F07)
+#define NW_NTC_NONPAGED_FCB ((NODE_TYPE_CODE)0x0F08)
+#define NW_NTC_RCB ((NODE_TYPE_CODE)0x0F0A)
+#define NW_NTC_ICB_SCB ((NODE_TYPE_CODE)0x0F0B)
+#define NW_NTC_PID ((NODE_TYPE_CODE)0x0F0C)
+#define NW_NTC_FILE_LOCK ((NODE_TYPE_CODE)0x0F0D)
+#define NW_NTC_LOGON ((NODE_TYPE_CODE)0x0F0E)
+#define NW_NTC_MINI_IRP_CONTEXT ((NODE_TYPE_CODE)0x0F0F)
+#define NW_NTC_NDS_CREDENTIAL ((NODE_TYPE_CODE)0x0F10)
+
+typedef CSHORT NODE_BYTE_SIZE;
+
+//
+// So all records start with
+//
+// typedef struct _RECORD_NAME {
+// NODE_TYPE_CODE NodeTypeCode;
+// NODE_BYTE_SIZE NodeByteSize;
+// :
+// } RECORD_NAME;
+// typedef RECORD_NAME *PRECORD_NAME;
+//
+
+#define NodeType(Ptr) (*((PNODE_TYPE_CODE)(Ptr)))
+
+#endif // _NODETYPE_
diff --git a/private/nw/rdr/nwrdr.rc b/private/nw/rdr/nwrdr.rc
new file mode 100644
index 000000000..dff665f61
--- /dev/null
+++ b/private/nw/rdr/nwrdr.rc
@@ -0,0 +1,11 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DRV
+#define VER_FILESUBTYPE VFT2_DRV_NETWORK
+#define VER_FILEDESCRIPTION_STR "NetWare Redirector File System Driver"
+#define VER_INTERNALNAME_STR "nwrdr.sys"
+
+#include "common.ver"
+
diff --git a/private/nw/rdr/pid.c b/private/nw/rdr/pid.c
new file mode 100644
index 000000000..375fba553
--- /dev/null
+++ b/private/nw/rdr/pid.c
@@ -0,0 +1,454 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ Pid.c
+
+Abstract:
+
+ This module implements the routines for the NetWare
+ redirector to map 32 bit NT pid values to unique 8 bit
+ NetWare values.
+
+ The technique used is to maintain a table of up to 256 entries.
+ The index of each entry corresponds directly to the 8 bit pid
+ values. Each table entry contains the 32 bit pid of the process
+ that has obtained exclusive access to the pid and the number of
+ handles opened by that process to this server.
+
+ This architecture limits the number of processes on the NT machine
+ communicating with any one server to 256.
+
+ Note: This package assumes that the size that the PidTable grows is
+ a factor of 256-<initial entries>. This ensures that running out of
+ valid entries in the table will occur when 256 entries have been
+ allocated.
+
+Author:
+
+ Colin Watson [ColinW] 02-Mar-1993
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_CREATE)
+
+#define INITIAL_MAPPID_ENTRIES 8
+#define MAPPID_INCREASE 8
+#define MAX_PIDS 256
+
+#define PID_FLAG_EOJ_REQUIRED 0x00000001 // EOJ required for this PID
+
+typedef struct _NW_PID_TABLE_ENTRY {
+ ULONG Pid32;
+ ULONG ReferenceCount;
+ ULONG Flags;
+} NW_PID_TABLE_ENTRY, *PNW_PID_TABLE_ENTRY;
+
+typedef struct _NW_PID_TABLE {
+
+ //
+ // Type and size of this record (must be NW_NTC_PID)
+ //
+
+ NODE_TYPE_CODE NodeTypeCode;
+ NODE_BYTE_SIZE NodeByteSize;
+
+ int ValidEntries;
+ NW_PID_TABLE_ENTRY PidTable[0];
+} NW_PID_TABLE, *PNW_PID_TABLE;
+
+
+
+PNW_PID_TABLE PidTable;
+ERESOURCE PidResource;
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwInitializePidTable )
+#pragma alloc_text( PAGE, NwUninitializePidTable )
+#pragma alloc_text( PAGE, NwMapPid )
+#pragma alloc_text( PAGE, NwSetEndOfJobRequired )
+#pragma alloc_text( PAGE, NwUnmapPid )
+#endif
+
+
+BOOLEAN
+NwInitializePidTable(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Creates a table for the MapPid package. The initial table has room for
+ INITIAL_MAPPID_ENTRIES entries.
+
+Arguments:
+
+
+Return Value:
+
+ NTSTATUS of result.
+
+--*/
+
+{
+ int i;
+ PNW_PID_TABLE TempPid =
+ ALLOCATE_POOL( PagedPool,
+ FIELD_OFFSET( NW_PID_TABLE, PidTable[0] ) +
+ (sizeof(NW_PID_TABLE_ENTRY) * INITIAL_MAPPID_ENTRIES ));
+
+ PAGED_CODE();
+
+ if (TempPid == NULL) {
+ return( FALSE );
+ }
+
+ TempPid->NodeByteSize = FIELD_OFFSET( NW_PID_TABLE, PidTable[0] ) +
+ (sizeof(NW_PID_TABLE_ENTRY) * INITIAL_MAPPID_ENTRIES );
+
+ TempPid->NodeTypeCode = NW_NTC_PID;
+
+ TempPid->ValidEntries = INITIAL_MAPPID_ENTRIES;
+
+ //
+ // Set the ref count for all PIDs to 0, except for pid 0. We
+ // do this so that we don't allocate PID 0.
+ //
+
+ TempPid->PidTable[0].ReferenceCount = 1;
+ for (i = 1; i < INITIAL_MAPPID_ENTRIES ; i++ ) {
+ TempPid->PidTable[i].ReferenceCount = 0;
+ }
+
+ PidTable = TempPid;
+ ExInitializeResource( &PidResource );
+}
+
+VOID
+NwUninitializePidTable(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Deletes a table created by the MapPid package.
+
+Arguments:
+
+ Pid - Supplies the table to be deleted.
+
+Return Value:
+
+--*/
+
+{
+#ifdef NWDBG
+ int i;
+#endif
+
+ PAGED_CODE();
+
+#ifdef NWDBG
+ ASSERT(PidTable->NodeTypeCode == NW_NTC_PID);
+ ASSERT(PidTable->PidTable[0].ReferenceCount == 1);
+
+ for (i = 1; i < PidTable->ValidEntries; i++ ) {
+ ASSERT(PidTable->PidTable[i].ReferenceCount == 0);
+ }
+#endif
+
+ FREE_POOL( PidTable );
+
+ ExDeleteResource( &PidResource );
+ return;
+
+}
+
+NTSTATUS
+NwMapPid(
+ IN ULONG Pid32,
+ OUT PUCHAR Pid8
+ )
+/*++
+
+Routine Description:
+
+ Obtain an 8 bit unique pid for this process. Either use a previosly
+ assigned pid for this process or assign an unused value.
+
+Arguments:
+
+ Pid - Supplies the datastructure used by MapPid to assign pids for
+ this server.
+
+ Pid32 - Supplies the NT pid to be mapped.
+
+ Pid8 - Returns the 8 bit Pid.
+
+Return Value:
+
+ NTSTATUS of result.
+
+--*/
+{
+ int i;
+ int FirstFree = -1;
+ int NewEntries;
+ PNW_PID_TABLE TempPid;
+
+ PAGED_CODE();
+
+ ExAcquireResourceExclusive( &PidResource, TRUE );
+
+ // DebugTrace(0, Dbg, "NwMapPid for %08lx\n", Pid32);
+
+ for (i=0; i < (PidTable)->ValidEntries ; i++ ) {
+
+ if ((PidTable)->PidTable[i].Pid32 == Pid32) {
+
+ //
+ // This process already has an 8 bit pid value assigned.
+ // Increment the reference and return.
+ //
+
+ (PidTable)->PidTable[i].ReferenceCount++;
+ *Pid8 = i;
+
+ // DebugTrace(0, Dbg, "NwMapPid found %08lx\n", (DWORD)i);
+
+ ExReleaseResource( &PidResource );
+ ASSERT( *Pid8 != 0 );
+ return( STATUS_SUCCESS );
+ }
+
+ if ((FirstFree == -1) &&
+ ((PidTable)->PidTable[i].ReferenceCount == 0)) {
+
+ //
+ // i is the lowest free 8 bit Pid.
+ //
+
+ FirstFree = i;
+ }
+ }
+
+ //
+ // This process does not have a pid assigned.
+ //
+
+ if ( FirstFree != -1 ) {
+
+ //
+ // We had an empty slot so assign it to this process.
+ //
+
+ (PidTable)->PidTable[FirstFree].ReferenceCount++;
+ (PidTable)->PidTable[FirstFree].Pid32 = Pid32;
+ *Pid8 = FirstFree;
+
+ DebugTrace(0, DEBUG_TRACE_ICBS, "NwMapPid maps %08lx\n", (DWORD)FirstFree);
+
+ ExReleaseResource( &PidResource );
+ ASSERT( *Pid8 != 0 );
+ return( STATUS_SUCCESS );
+ }
+
+ if ( (PidTable)->ValidEntries == MAX_PIDS ) {
+
+ //
+ // We've run out of 8 bit pids.
+ //
+
+ ExReleaseResource( &PidResource );
+
+#ifdef NWDBG
+ //
+ // temporary code to find the PID leak. BUGBUG
+ //
+ DumpIcbs() ;
+ ASSERT(FALSE) ;
+#endif
+
+ return(STATUS_TOO_MANY_OPENED_FILES);
+ }
+
+ //
+ // Grow the table by MAPPID_INCREASE entries.
+ //
+
+ NewEntries = (PidTable)->ValidEntries + MAPPID_INCREASE;
+
+ TempPid =
+ ALLOCATE_POOL( PagedPool,
+ FIELD_OFFSET( NW_PID_TABLE, PidTable[0] ) +
+ (sizeof(NW_PID_TABLE_ENTRY) * NewEntries ));
+
+ if (TempPid == NULL) {
+ ExReleaseResource( &PidResource );
+ return( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ RtlMoveMemory(
+ TempPid,
+ (PidTable),
+ FIELD_OFFSET( NW_PID_TABLE, PidTable[0] ) +
+ (sizeof(NW_PID_TABLE_ENTRY) * (PidTable)->ValidEntries ));
+
+ TempPid->NodeByteSize = FIELD_OFFSET( NW_PID_TABLE, PidTable[0] ) +
+ (sizeof(NW_PID_TABLE_ENTRY) * NewEntries );
+
+ for ( i = (PidTable)->ValidEntries; i < NewEntries ; i++ ) {
+ TempPid->PidTable[i].ReferenceCount = 0;
+ }
+
+ TempPid->ValidEntries = NewEntries;
+
+ //
+ // Save the index of the first free entry.
+ //
+
+ i = (PidTable)->ValidEntries;
+
+ //
+ // The new table is initialized. Free up the old table and return
+ // the first of the new entries.
+ //
+
+ FREE_POOL(PidTable);
+ PidTable = TempPid;
+
+ (PidTable)->PidTable[i].ReferenceCount = 1;
+ (PidTable)->PidTable[i].Pid32 = Pid32;
+ *Pid8 = i;
+
+ DebugTrace(0, DEBUG_TRACE_ICBS, "NwMapPid grows & maps %08lx\n", (DWORD)i);
+
+ ExReleaseResource( &PidResource );
+ return( STATUS_SUCCESS );
+}
+
+VOID
+NwSetEndOfJobRequired(
+ IN UCHAR Pid8
+ )
+/*++
+
+Routine Description:
+
+ Mark a PID as must send End Of Job when the pid reference count
+ reaches zero.
+
+Arguments:
+
+ Pid8 - The 8 bit Pid to mark.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PAGED_CODE();
+
+ ASSERT( Pid8 != 0 );
+
+ // DebugTrace(0, Dbg, "NwSetEndofJob for %08lx\n", (DWORD)Pid8);
+ SetFlag( PidTable->PidTable[Pid8].Flags, PID_FLAG_EOJ_REQUIRED );
+ return;
+}
+
+
+VOID
+NwUnmapPid(
+ IN UCHAR Pid8,
+ IN PIRP_CONTEXT IrpContext OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This routine dereference an 8 bit PID. If the reference count reaches
+ zero and this PID is marked End Of Job required, this routine will
+ also send an EOJ NCP for this PID.
+
+Arguments:
+
+ Pid8 - The 8 bit Pid to mark.
+
+ IrpContext - The IrpContext for the IRP in progress.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ BOOLEAN EndOfJob;
+
+ PAGED_CODE();
+
+ ASSERT( Pid8 != 0 );
+
+ // DebugTrace(0, Dbg, "NwUnmapPid %08lx\n", (DWORD)Pid8);
+ if ( BooleanFlagOn( PidTable->PidTable[Pid8].Flags, PID_FLAG_EOJ_REQUIRED ) &&
+ IrpContext != NULL ) {
+
+ //
+ // The End of job flag is set. Obtain a position at the front of
+ // the SCB queue, so that if we need to set an EOJ NCP, we needn't
+ // wait for the SCB queue while holding the PID table lock.
+ //
+
+ EndOfJob = TRUE;
+ NwAppendToQueueAndWait( IrpContext );
+ } else {
+ EndOfJob = FALSE;
+ }
+
+ //
+ // The PidResource lock controls the reference counts.
+ //
+
+ ExAcquireResourceExclusive( &PidResource, TRUE );
+
+ if ( --(PidTable)->PidTable[Pid8].ReferenceCount == 0 ) {
+
+ //
+ // Done with this PID, send an EOJ if necessary.
+ //
+
+ // DebugTrace(0, Dbg, "NwUnmapPid (ref=0) %08lx\n", (DWORD)Pid8);
+ (PidTable)->PidTable[Pid8].Flags = 0;
+ (PidTable)->PidTable[Pid8].Pid32 = 0;
+
+ if ( EndOfJob ) {
+ (VOID) ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "F-",
+ NCP_END_OF_JOB );
+ }
+ }
+
+ if ( EndOfJob ) {
+ NwDequeueIrpContext( IrpContext, FALSE );
+ }
+
+ ASSERT((PidTable)->PidTable[Pid8].ReferenceCount>=0);
+
+ ExReleaseResource( &PidResource );
+}
+
diff --git a/private/nw/rdr/procs.h b/private/nw/rdr/procs.h
new file mode 100644
index 000000000..254db4ddd
--- /dev/null
+++ b/private/nw/rdr/procs.h
@@ -0,0 +1,1830 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ Procs.h
+
+Abstract:
+
+ This module defines all of the globally used procedures in the NetWare
+ redirector.
+
+Author:
+
+ Colin Watson [ColinW] 15-Dec-1992
+
+Revision History:
+
+--*/
+
+#ifndef _NWPROCS_
+#define _NWPROCS_
+
+#ifndef QFE_BUILD
+#define IFS 1
+#define NWFASTIO 1
+#endif
+
+#ifdef IFS
+
+ #include <ntifs.h>
+
+#else
+
+ #include <ntos.h>
+ #include <ntioapi.h>
+ #include <zwapi.h>
+ #include <FsRtl.h>
+
+#endif
+
+#include <string.h>
+#include <Tdi.h>
+#include <TdiKrnl.h>
+#include <Status.h>
+#include <nwstatus.h>
+
+// Netware and Netware redirector specific includes
+
+#ifndef DBG
+#define DBG 0
+#endif
+
+#if !DBG
+#undef NWDBG
+#endif
+
+#if NWDBG
+#define PAGED_DBG 1
+#endif
+#ifdef PAGED_DBG
+#undef PAGED_CODE
+#define PAGED_CODE() \
+ struct { ULONG bogus; } ThisCodeCantBePaged; \
+ ThisCodeCantBePaged; \
+ if (KeGetCurrentIrql() > APC_LEVEL) { \
+ KdPrint(( "EX: Pageable code called at IRQL %d\n", KeGetCurrentIrql() )); \
+ ASSERT(FALSE); \
+ }
+#define PAGED_CODE_CHECK() if (ThisCodeCantBePaged) ;
+extern ULONG ThisCodeCantBePaged;
+#else
+#define PAGED_CODE_CHECK()
+#endif
+
+#include <NtDDNwfs.h>
+#include "Const.h"
+#include "Nodetype.h"
+#include "ncp.h"
+#include "Struct.h"
+#include "Data.h"
+#include "Exchange.h"
+#include <NwEvent.h>
+
+//
+// NDS Additions.
+//
+
+#include <nds.h>
+#include "ndsprocs.h"
+
+// Attach.c
+
+NTSTATUS
+ConnectToServer(
+ IN PIRP_CONTEXT pIrpContext,
+ OUT PSCB *pScbCollision
+);
+
+NTSTATUS
+ProcessFindNearest(
+ IN struct _IRP_CONTEXT* pIrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR RspData
+ );
+
+NTSTATUS
+CrackPath (
+ IN PUNICODE_STRING BaseName,
+ OUT PUNICODE_STRING DriveName,
+ OUT PWCHAR DriveLetter,
+ OUT PUNICODE_STRING ServerName,
+ OUT PUNICODE_STRING VolumeName,
+ OUT PUNICODE_STRING PathName,
+ OUT PUNICODE_STRING FileName,
+ OUT PUNICODE_STRING FullName OPTIONAL
+ );
+
+NTSTATUS
+CheckScbSecurity(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PSCB pScb,
+ IN PUNICODE_STRING puUserName,
+ IN PUNICODE_STRING puPassword,
+ IN BOOLEAN fDeferLogon
+);
+
+NTSTATUS
+ConnectScb(
+ IN PSCB *Scb,
+ IN PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING Server,
+ IN IPXaddress *pServerAddress,
+ IN PUNICODE_STRING UserName,
+ IN PUNICODE_STRING Password,
+ IN BOOLEAN DeferLogon,
+ IN BOOLEAN DeleteConnection,
+ IN BOOLEAN ExistingScb
+);
+
+#define IS_ANONYMOUS_SCB( pScb ) \
+ ( (pScb->UidServerName).Length == 0 )
+
+NTSTATUS
+CreateScb(
+ OUT PSCB *Scb,
+ IN PIRP_CONTEXT pIrpC,
+ IN PUNICODE_STRING Server,
+ IN IPXaddress *pServerAddress,
+ IN PUNICODE_STRING UserName,
+ IN PUNICODE_STRING Password,
+ IN BOOLEAN DeferLogon,
+ IN BOOLEAN DeleteConnection
+ );
+
+VOID
+DestroyAllScb(
+ VOID
+ );
+
+VOID
+InitializeAttach (
+ VOID
+ );
+
+NTSTATUS
+OpenScbSockets(
+ PIRP_CONTEXT pIrpC,
+ PNONPAGED_SCB pNpScb
+ );
+
+PNONPAGED_SCB
+SelectConnection(
+ PNONPAGED_SCB NpScb
+ );
+
+VOID
+NwLogoffAndDisconnect(
+ PIRP_CONTEXT pIrpContext,
+ PNONPAGED_SCB pNpScb
+ );
+
+VOID
+NwLogoffAllServers(
+ PIRP_CONTEXT pIrpContext,
+ PLARGE_INTEGER Uid
+ );
+
+VOID
+NwDeleteScb(
+ PSCB pScb
+ );
+
+NTSTATUS
+NegotiateBurstMode(
+ PIRP_CONTEXT pIrpContext,
+ PNONPAGED_SCB pNpScb,
+ BOOLEAN *LIPNegotiated
+ );
+
+VOID
+RenegotiateBurstMode(
+ PIRP_CONTEXT pIrpContext,
+ PNONPAGED_SCB pNpScb
+ );
+
+BOOLEAN
+NwFindScb(
+ OUT PSCB *ppScb,
+ IN PIRP_CONTEXT pIrpContext,
+ IN PUNICODE_STRING UidServerName,
+ IN PUNICODE_STRING ServerName
+ );
+
+NTSTATUS
+QueryServersAddress(
+ PIRP_CONTEXT pIrpContext,
+ PNONPAGED_SCB pNearestScb,
+ PUNICODE_STRING pServerName,
+ IPXaddress *pServerAddress
+ );
+
+VOID
+TreeConnectScb(
+ IN PSCB Scb
+ );
+
+NTSTATUS
+TreeDisconnectScb(
+ IN PIRP_CONTEXT IrpContext,
+ IN PSCB Scb
+ );
+
+VOID
+ReconnectScb(
+ IN PIRP_CONTEXT IrpContext,
+ IN PSCB pScb
+ );
+
+// Cache.c
+
+ULONG
+CacheRead(
+ IN PNONPAGED_FCB NpFcb,
+ IN ULONG FileOffset,
+ IN ULONG BytesToRead,
+ IN PVOID UserBuffer
+#if NWFASTIO
+ , IN BOOLEAN WholeBufferOnly
+#endif
+ );
+
+BOOLEAN
+CacheWrite(
+ IN PIRP_CONTEXT IrpContext,
+ IN PNONPAGED_FCB NpFcb,
+ IN ULONG FileOffset,
+ IN ULONG BytesToWrite,
+ IN PVOID UserBuffer
+ );
+
+ULONG
+CalculateReadAheadSize(
+ IN PIRP_CONTEXT IrpContext,
+ IN PNONPAGED_FCB NpFcb,
+ IN ULONG CacheReadSize,
+ IN ULONG FileOffset,
+ IN ULONG ByteCount
+ );
+
+NTSTATUS
+FlushCache(
+ PIRP_CONTEXT IrpContext,
+ PNONPAGED_FCB NpFcb
+ );
+
+NTSTATUS
+AcquireFcbAndFlushCache(
+ PIRP_CONTEXT IrpContext,
+ PNONPAGED_FCB NpFcb
+ );
+
+// Callback.c
+
+
+NTSTATUS
+SynchronousResponseCallback (
+ IN PIRP_CONTEXT pIrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR RspData
+ );
+
+NTSTATUS
+AsynchResponseCallback (
+ IN PIRP_CONTEXT pIrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR RspData
+ );
+
+NTSTATUS
+NcpSearchFileCallback (
+ IN PIRP_CONTEXT pIrpContext,
+ IN ULONG BytesIndicated,
+ IN ULONG BytesAvailable,
+ OUT ULONG *BytesTaken,
+ IN PUCHAR RspData
+ );
+
+// Cleanup.c
+
+NTSTATUS
+NwFsdCleanup (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+// Close.c
+
+NTSTATUS
+NwFsdClose (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+// Create.c
+
+NTSTATUS
+NwFsdCreate (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+ReadAttachEas(
+ IN PIRP Irp,
+ OUT PUNICODE_STRING UserName,
+ OUT PUNICODE_STRING Password,
+ OUT PULONG ShareType,
+ OUT PDWORD CredentialExtension
+ );
+
+// Convert.c
+
+NTSTATUS
+pNwErrorToNtStatus(
+ UCHAR Error
+ );
+
+NTSTATUS
+NwBurstResultToNtStatus(
+ ULONG Result
+ );
+
+#define NwErrorToNtStatus( STATUS ) \
+ (STATUS == 0 )? STATUS_SUCCESS : pNwErrorToNtStatus(STATUS)
+
+NTSTATUS
+NwConnectionStatusToNtStatus(
+ UCHAR NwStatus
+ );
+
+UCHAR
+NtAttributesToNwAttributes(
+ ULONG FileAttributes
+ );
+
+UCHAR
+NtToNwShareFlags(
+ ULONG DesiredAccess,
+ ULONG NtShareFlags
+ );
+
+LARGE_INTEGER
+NwDateTimeToNtTime(
+ USHORT Date,
+ USHORT Time
+ );
+
+NTSTATUS
+NwNtTimeToNwDateTime (
+ IN LARGE_INTEGER NtTime,
+ IN PUSHORT NwDate,
+ IN PUSHORT NwTime
+ );
+
+// Data.c
+
+VOID
+NwInitializeData(
+ VOID
+ );
+
+// Debug.c
+
+#ifdef NWDBG
+
+ULONG
+NwMemDbg (
+ IN PCH Format,
+ ...
+ );
+
+VOID
+RealDebugTrace(
+ IN LONG Indent,
+ IN ULONG Level,
+ IN PCH Message,
+ IN PVOID Parameter
+ );
+
+VOID
+dump(
+ IN ULONG Level,
+ IN PVOID far_p,
+ IN ULONG len
+ );
+
+VOID
+dumpMdl(
+ IN ULONG Level,
+ IN PMDL Mdl
+ );
+
+VOID
+DumpIcbs(
+ VOID
+ ) ;
+
+
+PVOID
+NwAllocatePool(
+ ULONG Type,
+ ULONG Size,
+ BOOLEAN RaiseStatus
+ );
+
+VOID
+NwFreePool(
+ PVOID Buffer
+ );
+
+PIRP
+NwAllocateIrp(
+ CCHAR Size,
+ BOOLEAN ChargeQuota
+ );
+
+VOID
+NwFreeIrp(
+ PIRP Irp
+ );
+
+PMDL
+NwAllocateMdl(
+ PVOID Va,
+ ULONG Length,
+ BOOLEAN Secondary,
+ BOOLEAN ChargeQuota,
+ PIRP Irp,
+ PUCHAR FileName,
+ int Line
+ );
+
+VOID
+NwFreeMdl(
+ PMDL Mdl
+ );
+
+#else
+#define dump( level, pointer, length ) { NOTHING;}
+#endif
+
+
+// Deviosup.c
+
+VOID
+NwMapUserBuffer (
+ IN OUT PIRP Irp,
+ IN KPROCESSOR_MODE AccessMode,
+ OUT PVOID *UserBuffer
+ );
+
+VOID
+NwLockUserBuffer (
+ IN OUT PIRP Irp,
+ IN LOCK_OPERATION Operation,
+ IN ULONG BufferLength
+ );
+
+// Dir.c
+
+NTSTATUS
+NwFsdDirectoryControl (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+// Encrypt.c
+
+VOID
+RespondToChallenge(
+ IN PUCHAR achObjectId,
+ IN POEM_STRING Password,
+ IN PUCHAR pChallenge,
+ OUT PUCHAR pResponse
+ );
+
+// Exchange.c
+
+BOOLEAN
+AppendToScbQueue(
+ IN PIRP_CONTEXT IrpContext,
+ IN PNONPAGED_SCB NpScb
+ );
+
+VOID
+PreparePacket(
+ PIRP_CONTEXT pIrpContext,
+ PIRP pOriginalIrp,
+ PMDL pMdl
+ );
+
+NTSTATUS
+PrepareAndSendPacket(
+ PIRP_CONTEXT pIrpContext
+ );
+
+NTSTATUS
+SendPacket(
+ PIRP_CONTEXT pIrpContext,
+ PNONPAGED_SCB pNpScb
+ );
+
+VOID
+SendNow(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+VOID
+SetEvent(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+_cdecl
+ExchangeWithWait(
+ PIRP_CONTEXT pIrpContext,
+ PEX pEx,
+ char* f,
+ ... // format specific parameters
+ );
+
+NTSTATUS
+_cdecl
+BuildRequestPacket(
+ PIRP_CONTEXT pIrpContext,
+ PEX pEx,
+ char* f,
+ ... // format specific parameters
+ );
+
+NTSTATUS
+_cdecl
+ParseResponse(
+ PIRP_CONTEXT IrpContext,
+ PUCHAR RequestHeader,
+ ULONG RequestLength,
+ char* f,
+ ... // format specific parameters
+ );
+
+NTSTATUS
+ParseNcpResponse(
+ PIRP_CONTEXT IrpContext,
+ PNCP_RESPONSE Response
+ );
+
+BOOLEAN
+VerifyResponse(
+ PIRP_CONTEXT pIrpContext,
+ PVOID Response
+ );
+
+VOID
+FreeReceiveIrp(
+ PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+NewRouteRetry(
+ IN PIRP_CONTEXT pIrpContext
+ );
+
+NTSTATUS
+NewRouteBurstRetry(
+ IN PIRP_CONTEXT pIrpContext
+ );
+
+ULONG
+MdlLength (
+ register IN PMDL Mdl
+ );
+
+VOID
+NwProcessSendBurstFailure(
+ PNONPAGED_SCB NpScb,
+ USHORT MissingFragmentCount
+ );
+
+VOID
+NwProcessSendBurstSuccess(
+ PNONPAGED_SCB NpScb
+ );
+
+VOID
+NwProcessReceiveBurstFailure(
+ PNONPAGED_SCB NpScb,
+ USHORT MissingFragmentCount
+ );
+
+VOID
+NwProcessReceiveBurstSuccess(
+ PNONPAGED_SCB NpScb
+ );
+
+VOID
+NwProcessPositiveAck(
+ PNONPAGED_SCB NpScb
+ );
+
+// Errorlog.c
+
+VOID
+_cdecl
+Error(
+ IN ULONG UniqueErrorCode,
+ IN NTSTATUS NtStatusCode,
+ IN PVOID ExtraInformationBuffer,
+ IN USHORT ExtraInformationLength,
+ IN USHORT NumberOfInsertionStrings,
+ ...
+ );
+
+// FileInfo.c
+
+NTSTATUS
+NwFsdQueryInformation (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+NwFsdSetInformation (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+NwDeleteFile(
+ IN PIRP_CONTEXT pIrpContext
+ );
+
+ULONG
+OccurenceCount (
+ IN PUNICODE_STRING String,
+ IN WCHAR SearchChar
+ );
+
+#if NWFASTIO
+BOOLEAN
+NwFastQueryBasicInfo (
+ IN PFILE_OBJECT FileObject,
+ IN BOOLEAN Wait,
+ IN OUT PFILE_BASIC_INFORMATION Buffer,
+ OUT PIO_STATUS_BLOCK IoStatus,
+ IN PDEVICE_OBJECT DeviceObject
+ );
+
+BOOLEAN
+NwFastQueryStandardInfo (
+ IN PFILE_OBJECT FileObject,
+ IN BOOLEAN Wait,
+ IN OUT PFILE_STANDARD_INFORMATION Buffer,
+ OUT PIO_STATUS_BLOCK IoStatus,
+ IN PDEVICE_OBJECT DeviceObject
+ );
+#endif
+
+// Filobsup.c
+
+VOID
+NwSetFileObject (
+ IN PFILE_OBJECT FileObject OPTIONAL,
+ IN PVOID FsContext,
+ IN PVOID FsContext2
+ );
+
+NODE_TYPE_CODE
+NwDecodeFileObject (
+ IN PFILE_OBJECT FileObject,
+ OUT PVOID *FsContext,
+ OUT PVOID *FsContext2
+ );
+
+BOOLEAN
+NwIsIrpTopLevel (
+ IN PIRP Irp
+ );
+
+// Fsctl.c
+
+NTSTATUS
+NwFsdFileSystemControl (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+NwCommonFileSystemControl (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+NwFsdDeviceIoControl (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+#ifdef _PNP_POWER
+
+VOID
+HandleTdiBindMessage(
+ IN PUNICODE_STRING DeviceName
+);
+
+VOID
+HandleTdiUnbindMessage(
+ IN PUNICODE_STRING DeviceName
+);
+
+#endif
+
+PLOGON
+FindUser(
+ IN PLARGE_INTEGER Uid,
+ IN BOOLEAN ExactMatch
+ );
+
+LARGE_INTEGER
+GetUid(
+ IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext
+ );
+
+PLOGON
+FindUserByName(
+ IN PUNICODE_STRING UserName
+);
+
+VOID
+LazySetShareable(
+ PIRP_CONTEXT IrpContext,
+ PICB pIcb,
+ PFCB pFcb
+);
+
+// FspDisp.c
+
+VOID
+NwFspDispatch (
+ IN PVOID Context
+ );
+
+NTSTATUS
+NwPostToFsp (
+ IN PIRP_CONTEXT IrpContext,
+ IN BOOLEAN MarkIrpPending
+ );
+
+// hack.c
+
+NTSTATUS
+_cdecl
+BuildNcpResponse(
+ PIRP_CONTEXT pIrpC,
+ char* f,
+ char Error,
+ char Status,
+ ...
+ );
+
+NTSTATUS
+HackSendMessage(
+ PIRP_CONTEXT pIrpContext
+ );
+
+NTSTATUS
+_cdecl
+HackParseResponse(
+ PUCHAR Response,
+ char* FormatString,
+ ... // format specific parameters
+ );
+
+// Ipx.c
+
+NTSTATUS
+IpxOpenHandle(
+ OUT PHANDLE pHandle,
+ OUT PDEVICE_OBJECT* ppDeviceObject,
+ OUT PFILE_OBJECT* pFileObject,
+ IN PVOID EaBuffer OPTIONAL,
+ IN ULONG EaLength
+ );
+
+NTSTATUS
+IpxOpen(
+ VOID
+ );
+
+VOID
+IpxClose(
+ VOID
+ );
+
+VOID
+BuildIpxAddress(
+ IN ULONG NetworkAddress,
+ IN PUCHAR NodeAddress,
+ IN USHORT Socket,
+ OUT PTA_IPX_ADDRESS NetworkName
+ );
+
+VOID
+BuildIpxAddressEa (
+ IN ULONG NetworkAddress,
+ IN PUCHAR NodeAddress,
+ IN USHORT Socket,
+ OUT PVOID NetworkName
+ );
+
+NTSTATUS
+SetEventHandler (
+ IN PIRP_CONTEXT pIrpC,
+ IN PNW_TDI_STRUCT pTdiStruc,
+ IN ULONG EventType,
+ IN PVOID pEventHandler,
+ IN PVOID pContext
+ );
+
+NTSTATUS
+GetMaximumPacketSize(
+ IN PIRP_CONTEXT pIrpContext,
+ IN PNW_TDI_STRUCT pTdiStruct,
+ OUT PULONG pMaximumPacketSize
+ );
+
+NTSTATUS
+GetNewRoute(
+ IN PIRP_CONTEXT pIrpContext
+ );
+
+NTSTATUS
+GetTickCount(
+ IN PIRP_CONTEXT pIrpContext,
+ OUT PUSHORT HopCount
+ );
+
+#ifndef QFE_BUILD
+
+NTSTATUS
+SubmitLineChangeRequest(
+ VOID
+ );
+
+#endif
+
+VOID
+FspProcessLineChange(
+ IN PVOID Context
+ );
+
+// Lock.c
+
+NTSTATUS
+NwFsdLockControl (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+VOID
+NwFreeLocksForIcb(
+ PIRP_CONTEXT pIrpContext,
+ PICB Icb
+ );
+
+// Lockcode.c
+
+VOID
+NwReferenceUnlockableCodeSection (
+ VOID
+ );
+
+VOID
+NwDereferenceUnlockableCodeSection (
+ VOID
+ );
+
+BOOLEAN
+NwUnlockCodeSections(
+ BOOLEAN BlockIndefinitely
+ );
+
+// Pid.c
+
+BOOLEAN
+NwInitializePidTable(
+ VOID
+ );
+
+NTSTATUS
+NwMapPid(
+ IN ULONG Pid32,
+ OUT PUCHAR Pid8
+ );
+
+VOID
+NwSetEndOfJobRequired(
+ IN UCHAR Pid8
+ );
+
+VOID
+NwUnmapPid(
+ IN UCHAR Pid8,
+ IN PIRP_CONTEXT IrpContext OPTIONAL
+ );
+
+VOID
+NwUninitializePidTable(
+ VOID
+ );
+
+// Read.c
+
+NTSTATUS
+NwFsdRead(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+VOID
+BurstReadTimeout(
+ PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+ResubmitBurstRead (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+#if NWFASTIO
+BOOLEAN
+NwFastRead (
+ IN PFILE_OBJECT FileObject,
+ IN PLARGE_INTEGER FileOffset,
+ IN ULONG Length,
+ IN BOOLEAN Wait,
+ IN ULONG LockKey,
+ OUT PVOID Buffer,
+ OUT PIO_STATUS_BLOCK IoStatus,
+ IN PDEVICE_OBJECT DeviceObject
+ );
+#endif
+
+// Scavenger.c
+
+VOID
+DisconnectTimedOutScbs(
+ LARGE_INTEGER Now
+ );
+
+VOID
+NwScavengerRoutine(
+ IN PWORK_QUEUE_ITEM WorkItem
+ );
+
+BOOLEAN
+NwAllocateExtraIrpContext(
+ OUT PIRP_CONTEXT *ppIrpContext,
+ IN PNONPAGED_SCB pScb
+ );
+
+VOID
+NwFreeExtraIrpContext(
+ IN PIRP_CONTEXT pIrpContext
+ );
+
+VOID
+CleanupScbs(
+ LARGE_INTEGER Now
+ );
+
+// Security.c
+
+VOID
+CreateAnsiUid(
+ OUT PCHAR aUid,
+ IN PLARGE_INTEGER Uid
+ );
+
+NTSTATUS
+MakeUidServer(
+ PUNICODE_STRING UidServer,
+ PLARGE_INTEGER Uid,
+ PUNICODE_STRING Server
+ );
+
+NTSTATUS
+Logon(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+VOID
+FreeLogon(
+ IN PLOGON Logon
+ );
+
+NTSTATUS
+Logoff(
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+UpdateUsersPassword(
+ IN PUNICODE_STRING UserName,
+ IN PUNICODE_STRING Password,
+ OUT PLARGE_INTEGER Uid
+ );
+
+NTSTATUS
+UpdateServerPassword(
+ PIRP_CONTEXT IrpContext,
+ IN PUNICODE_STRING ServerName,
+ IN PUNICODE_STRING UserName,
+ IN PUNICODE_STRING Password,
+ IN PLARGE_INTEGER Uid
+ );
+
+// String.c
+
+NTSTATUS
+DuplicateStringWithString (
+ OUT PSTRING DestinationString,
+ IN PSTRING SourceString,
+ IN POOL_TYPE PoolType
+ );
+
+
+NTSTATUS
+DuplicateUnicodeStringWithString (
+ OUT PUNICODE_STRING DestinationString,
+ IN PUNICODE_STRING SourceString,
+ IN POOL_TYPE PoolType
+ );
+
+NTSTATUS
+SetUnicodeString (
+ IN PUNICODE_STRING Destination,
+ IN ULONG Length,
+ IN PWCHAR Source
+ );
+
+VOID
+MergeStrings(
+ IN PUNICODE_STRING Destination,
+ IN PUNICODE_STRING S1,
+ IN PUNICODE_STRING S2,
+ IN ULONG Type
+ );
+
+// Strucsup.c
+
+VOID
+NwInitializeRcb (
+ IN PRCB Rcb
+ );
+
+VOID
+NwDeleteRcb (
+ IN PRCB Rcb
+ );
+
+PFCB
+NwCreateFcb (
+ IN PUNICODE_STRING FileName,
+ IN PSCB Scb,
+ IN PVCB Vcb
+ );
+
+PFCB
+NwFindFcb (
+ IN PSCB Scb,
+ IN PVCB Vcb,
+ IN PUNICODE_STRING FileName,
+ IN PDCB Dcb OPTIONAL
+ );
+
+VOID
+NwDereferenceFcb (
+ IN PIRP_CONTEXT IrpContext,
+ IN PFCB Fcb
+ );
+
+PICB
+NwCreateIcb (
+ IN USHORT Type,
+ IN PVOID Associate
+ );
+
+VOID
+NwVerifyIcb (
+ IN PICB Icb
+ );
+
+VOID
+NwVerifyIcbSpecial(
+ IN PICB Icb
+ );
+
+VOID
+NwVerifyScb (
+ IN PSCB Scb
+ );
+
+VOID
+NwDeleteIcb (
+ IN PIRP_CONTEXT IrpContext OPTIONAL,
+ IN PICB Icb
+ );
+
+PVCB
+NwFindVcb (
+ IN PIRP_CONTEXT IrpContext,
+ IN PUNICODE_STRING VolumeName,
+ IN ULONG ShareType,
+ IN WCHAR DriveLetter,
+ IN BOOLEAN ExplicitConnection,
+ IN BOOLEAN FindExisting
+ );
+
+PVCB
+NwCreateVcb (
+ IN PIRP_CONTEXT IrpContext,
+ IN PSCB Scb,
+ IN PUNICODE_STRING VolumeName,
+ IN ULONG ShareType,
+ IN WCHAR DriveLetter,
+ IN BOOLEAN ExplicitConnection
+ );
+
+VOID
+NwDereferenceVcb (
+ IN PVCB Vcb,
+ IN PIRP_CONTEXT IrpContext OPTIONAL,
+ IN BOOLEAN OwnRcb
+ );
+
+VOID
+NwCleanupVcb(
+ IN PVCB pVcb,
+ IN PIRP_CONTEXT pIrpContext
+ );
+
+VOID
+NwCloseAllVcbs(
+ PIRP_CONTEXT pIrpContext
+ );
+
+VOID
+NwReopenVcbHandlesForScb (
+ IN PIRP_CONTEXT IrpContext,
+ IN PSCB Scb
+ );
+
+VOID
+NwReopenVcbHandle(
+ IN PIRP_CONTEXT IrpContext,
+ IN PVCB Vcb
+ );
+
+ULONG
+NwInvalidateAllHandles (
+ PLARGE_INTEGER Uid,
+ PIRP_CONTEXT IrpContext
+ );
+
+ULONG
+NwInvalidateAllHandlesForScb (
+ PSCB Scb
+ );
+
+BOOLEAN
+IsFatNameValid (
+ IN PUNICODE_STRING FileName
+ );
+
+// Timer.c
+
+VOID
+StartTimer(
+ );
+
+VOID
+StopTimer(
+ );
+
+// Util.c
+
+VOID
+CopyBufferToMdl(
+ PMDL DestinationMdl,
+ ULONG DataOffset,
+ PVOID SourceData,
+ ULONG SourceByteCount
+ );
+
+NTSTATUS
+GetCredentialFromServerName(
+ IN PUNICODE_STRING puServerName,
+ OUT PUNICODE_STRING puCredentialName
+);
+
+NTSTATUS
+BuildExCredentialServerName(
+ IN PUNICODE_STRING puServerName,
+ IN PUNICODE_STRING puUserName,
+ OUT PUNICODE_STRING puExCredServerName
+);
+
+NTSTATUS
+UnmungeCredentialName(
+ IN PUNICODE_STRING puCredName,
+ OUT PUNICODE_STRING puServerName
+);
+
+BOOLEAN
+IsCredentialName(
+ IN PUNICODE_STRING puObjectName
+);
+
+// VolInfo.c
+
+NTSTATUS
+NwFsdQueryVolumeInformation (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+NwFsdSetVolumeInformation (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+// WorkQue.c
+
+PIRP_CONTEXT
+AllocateIrpContext (
+ PIRP pIrp
+ );
+
+VOID
+FreeIrpContext (
+ PIRP_CONTEXT IrpContext
+ );
+
+VOID
+InitializeIrpContext (
+ VOID
+ );
+
+VOID
+UninitializeIrpContext (
+ VOID
+ );
+
+VOID
+NwCompleteRequest (
+ PIRP_CONTEXT IrpContext,
+ NTSTATUS Status
+ );
+
+VOID
+NwAppendToQueueAndWait(
+ PIRP_CONTEXT IrpContext
+ );
+
+VOID
+NwDequeueIrpContext(
+ IN PIRP_CONTEXT IrpContext,
+ IN BOOLEAN OwnSpinLock
+ );
+
+VOID
+NwCancelIrp (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+PIRP
+NwAllocateSendIrp (
+ PIRP_CONTEXT IrpContext
+ );
+
+PMINI_IRP_CONTEXT
+AllocateMiniIrpContext (
+ PIRP_CONTEXT IrpContext
+ );
+
+VOID
+FreeMiniIrpContext (
+ PMINI_IRP_CONTEXT MiniIrpContext
+ );
+
+// Write.c
+
+NTSTATUS
+NwFsdWrite(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+DoWrite(
+ PIRP_CONTEXT IrpContext,
+ LARGE_INTEGER ByteOffset,
+ ULONG BufferLength,
+ PVOID WriteBuffer,
+ PMDL WriteMdl
+ );
+
+NTSTATUS
+NwFsdFlushBuffers(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ );
+
+NTSTATUS
+ResubmitBurstWrite(
+ PIRP_CONTEXT IrpContext
+ );
+
+#if NWFASTIO
+BOOLEAN
+NwFastWrite (
+ IN PFILE_OBJECT FileObject,
+ IN PLARGE_INTEGER FileOffset,
+ IN ULONG Length,
+ IN BOOLEAN Wait,
+ IN ULONG LockKey,
+ OUT PVOID Buffer,
+ OUT PIO_STATUS_BLOCK IoStatus,
+ IN PDEVICE_OBJECT DeviceObject
+ );
+#endif
+
+//
+// A function that returns finished denotes if it was able to complete the
+// operation (TRUE) or could not complete the operation (FALSE) because the
+// wait value stored in the irp context was false and we would have had
+// to block for a resource or I/O
+//
+
+typedef BOOLEAN FINISHED;
+
+//
+// Miscellaneous support routines
+//
+
+//
+// This macro returns TRUE if a flag in a set of flags is on and FALSE
+// otherwise. It is followed by two macros for setting and clearing
+// flags
+//
+
+#define BooleanFlagOn(Flags,SingleFlag) ((BOOLEAN)((((Flags) & (SingleFlag)) != 0)))
+
+#define SetFlag(Flags,SingleFlag) { \
+ (Flags) |= (SingleFlag); \
+}
+
+
+#define ClearFlag(Flags,SingleFlag) { \
+ (Flags) &= ~(SingleFlag); \
+}
+
+//
+// The following macro is used to determine if an FSD thread can block
+// for I/O or wait for a resource. It returns TRUE if the thread can
+// block and FALSE otherwise. This attribute can then be used to call
+// the FSD & FSP common work routine with the proper wait value.
+//
+
+#define CanFsdWait(IRP) IoIsOperationSynchronous(IRP)
+
+//
+// This macro takes a pointer (or ulong) and returns its rounded up word
+// value
+//
+
+#define WordAlign(Ptr) ( \
+ ((((ULONG)(Ptr)) + 1) & 0xfffffffe) \
+ )
+
+//
+// This macro takes a pointer (or ulong) and returns its rounded up longword
+// value
+//
+
+#define LongAlign(Ptr) ( \
+ ((((ULONG)(Ptr)) + 3) & 0xfffffffc) \
+ )
+
+//
+// This macro takes a pointer (or ulong) and returns its rounded up quadword
+// value
+//
+
+#define QuadAlign(Ptr) ( \
+ ((((ULONG)(Ptr)) + 7) & 0xfffffff8) \
+ )
+
+//
+// The following two macro are used by the Fsd/Fsp exception handlers to
+// process an exception. The first macro is the exception filter used in the
+// Fsd/Fsp to decide if an exception should be handled at this level.
+// The second macro decides if the exception is to be finished off by
+// completing the IRP, and cleaning up the Irp Context, or if we should
+// bugcheck. Exception values such as STATUS_FILE_INVALID (raised by
+// VerfySup.c) cause us to complete the Irp and cleanup, while exceptions
+// such as accvio cause us to bugcheck.
+//
+// The basic structure for fsd/fsp exception handling is as follows:
+//
+// NwFsdXxx(...)
+// {
+// try {
+//
+// ...
+//
+// } except(NwExceptionFilter( IrpContext, GetExceptionCode() )) {
+//
+// Status = NwProcessException( IrpContext, Irp, GetExceptionCode() );
+// }
+//
+// Return Status;
+// }
+//
+// To explicitly raise an exception that we expect, such as
+// STATUS_FILE_INVALID, use the below macro NwRaiseStatus(). To raise a
+// status from an unknown origin (such as CcFlushCache()), use the macro
+// NwNormalizeAndRaiseStatus. This will raise the status if it is expected,
+// or raise STATUS_UNEXPECTED_IO_ERROR if it is not.
+//
+// Note that when using these two macros, the original status is placed in
+// IrpContext->ExceptionStatus, signaling NwExceptionFilter and
+// NwProcessException that the status we actually raise is by definition
+// expected.
+//
+
+LONG
+NwExceptionFilter (
+ IN PIRP Irp,
+ IN PEXCEPTION_POINTERS ExceptionPointer
+ );
+
+NTSTATUS
+NwProcessException (
+ IN PIRP_CONTEXT IrpContext,
+ IN NTSTATUS ExceptionCode
+ );
+
+//
+// VOID
+// NwRaiseStatus (
+// IN NT_STATUS Status
+// );
+//
+//
+
+#define NwRaiseStatus(IRPCONTEXT,STATUS) { \
+ ExRaiseStatus( (STATUS) ); \
+ KeBugCheck( NW_FILE_SYSTEM ); \
+}
+
+//
+// VOID
+// NwNormalAndRaiseStatus (
+// IN NT_STATUS Status
+// );
+//
+
+#define NwNormalizeAndRaiseStatus(IRPCONTEXT,STATUS) { \
+ if ((STATUS) == STATUS_VERIFY_REQUIRED) { ExRaiseStatus((STATUS)); } \
+ ExRaiseStatus(FsRtlNormalizeNtstatus((STATUS),STATUS_UNEXPECTED_IO_ERROR)); \
+ KeBugCheck( NW_FILE_SYSTEM ); \
+}
+
+//
+// The Following routine makes a popup
+//
+
+#define NwRaiseInformationalHardError(STATUS,NAME) { \
+ UNICODE_STRING Name; \
+ if (NT_SUCCESS(RtlOemStringToCountedUnicodeString(&Name, (NAME), TRUE))) { \
+ IoRaiseInformationalHardError(Status, &Name, (Irp == NULL ?\
+ NULL : &(Irp->Tail.Overlay.Thread)->Tcb)); \
+ RtlFreeUnicodeString(&Name); \
+ } \
+}
+
+
+//
+// The following macros are used to establish the semantics needed
+// to do a return from within a try-finally clause. As a rule every
+// try clause must end with a label call try_exit. For example,
+//
+// try {
+// :
+// :
+//
+// try_exit: NOTHING;
+// } finally {
+//
+// :
+// :
+// }
+//
+// Every return statement executed inside of a try clause should use the
+// try_return macro. If the compiler fully supports the try-finally construct
+// then the macro should be
+//
+// #define try_return(S) { return(S); }
+//
+// If the compiler does not support the try-finally construct then the macro
+// should be
+//
+// #define try_return(S) { S; goto try_exit; }
+//
+
+#define try_return(S) { S; goto try_exit; }
+
+
+#if NWDBG
+#define InternalError(String) { \
+ DbgPrint("Internal NetWare Redirector Error "); \
+ DbgPrint String; \
+ DbgPrint("\nFile %s, Line %d\n", __FILE__, __LINE__); \
+ ASSERT(FALSE); \
+}
+#else
+#define InternalError(String) {NOTHING;}
+#endif
+
+#define DbgPrintf DbgPrint
+
+//
+// Reference and dereference Macros.
+//
+
+VOID
+RefDbgTrace (
+ PVOID Resource,
+ DWORD Count,
+ BOOLEAN Reference,
+ PBYTE FileName,
+ UINT Line
+);
+
+#ifdef NWDBG
+
+VOID
+ChkNwReferenceScb(
+ PNONPAGED_SCB pNpScb,
+ PBYTE FileName,
+ UINT Line,
+ BOOLEAN Silent
+);
+
+VOID
+ChkNwDereferenceScb(
+ PNONPAGED_SCB pNpScb,
+ PBYTE FileName,
+ UINT Line,
+ BOOLEAN Silent
+);
+
+#define NwReferenceScb( pNpScb ) \
+ ChkNwReferenceScb( pNpScb, __FILE__, __LINE__, FALSE )
+
+#define NwQuietReferenceScb( pNpScb ) \
+ ChkNwReferenceScb( pNpScb, __FILE__, __LINE__, TRUE )
+
+#define NwDereferenceScb( pNpScb ) \
+ ChkNwDereferenceScb( pNpScb, __FILE__, __LINE__, FALSE )
+
+#define NwQuietDereferenceScb( pNpScb ) \
+ ChkNwDereferenceScb( pNpScb, __FILE__, __LINE__, TRUE )
+
+#else
+
+#define NwReferenceScb( pNpScb ) \
+ ExInterlockedIncrementLong( &(pNpScb)->Reference, &(pNpScb)->NpScbInterLock )
+
+#define NwQuietReferenceScb( pNpScb ) \
+ ExInterlockedIncrementLong( &(pNpScb)->Reference, &(pNpScb)->NpScbInterLock )
+
+#define NwDereferenceScb( pNpScb ) \
+ ExInterlockedDecrementLong( &(pNpScb)->Reference, &(pNpScb)->NpScbInterLock )
+
+#define NwQuietDereferenceScb( pNpScb ) \
+ ExInterlockedDecrementLong( &(pNpScb)->Reference, &(pNpScb)->NpScbInterLock )
+#endif
+
+//
+// Irpcontext event macro.
+//
+
+#define NwSetIrpContextEvent( pIrpContext ) \
+ DebugTrace( 0, DEBUG_TRACE_WORKQUE, "Set event for IrpC = %08lx\n", pIrpContext ); \
+ DebugTrace( 0, DEBUG_TRACE_WORKQUE, "IrpC->pNpScb = %08lx\n", pIrpContext->pNpScb ); \
+ KeSetEvent( &pIrpContext->Event, 0, FALSE )
+
+//
+// VCB macros must be called with the RCB resource held.
+//
+
+
+#if NWDBG
+VOID
+NwReferenceVcb (
+ IN PVCB Vcb
+ );
+#else
+#define NwReferenceVcb( pVcb ) ++(pVcb)->Reference;
+#endif
+
+//
+// Resource acquisition and release macros
+//
+
+#if NWDBG
+
+VOID
+NwAcquireExclusiveRcb(
+ PRCB Rcb,
+ BOOLEAN Wait
+ );
+
+VOID
+NwAcquireSharedRcb(
+ PRCB Rcb,
+ BOOLEAN Wait
+ );
+
+VOID
+NwReleaseRcb(
+ PRCB Rcb
+ );
+
+VOID
+NwAcquireExclusiveFcb(
+ PNONPAGED_FCB pFcb,
+ BOOLEAN Wait
+ );
+
+VOID
+NwAcquireSharedFcb(
+ PNONPAGED_FCB pFcb,
+ BOOLEAN Wait
+ );
+
+VOID
+NwReleaseFcb(
+ PNONPAGED_FCB pFcb
+ );
+
+VOID
+NwAcquireOpenLock(
+ VOID
+ );
+
+VOID
+NwReleaseOpenLock(
+ VOID
+ );
+
+#else
+
+#define NwAcquireExclusiveRcb( Rcb, Wait ) \
+ ExAcquireResourceExclusive( &((Rcb)->Resource), Wait )
+
+#define NwAcquireSharedRcb( Rcb, Wait ) \
+ ExAcquireResourceShared( &((Rcb)->Resource), Wait )
+
+#define NwReleaseRcb( Rcb ) \
+ ExReleaseResource( &((Rcb)->Resource) )
+
+#define NwAcquireExclusiveFcb( pFcb, Wait ) \
+ ExAcquireResourceExclusive( &((pFcb)->Resource), Wait )
+
+#define NwAcquireSharedFcb( pFcb, Wait ) \
+ ExAcquireResourceShared( &((pFcb)->Resource), Wait )
+
+#define NwReleaseFcb( pFcb ) \
+ ExReleaseResource( &((pFcb)->Resource) )
+
+#define NwAcquireOpenLock( ) \
+ ExAcquireResourceExclusive( &NwOpenResource, TRUE )
+
+#define NwReleaseOpenLock( ) \
+ ExReleaseResource( &NwOpenResource )
+
+#endif
+
+#define NwReleaseFcbForThread( pFcb, pThread ) \
+ ExReleaseResourceForThread( &((pFcb)->Resource), pThread )
+
+//
+// Memory allocation and deallocation macros
+//
+
+#ifdef NWDBG
+
+#define ALLOCATE_POOL_EX( Type, Size ) NwAllocatePool( Type, Size, TRUE )
+#define ALLOCATE_POOL( Type, Size ) NwAllocatePool( Type, Size, FALSE )
+#define FREE_POOL( Buffer ) NwFreePool( Buffer )
+
+#define ALLOCATE_IRP( Size, ChargeQuota ) \
+ NwAllocateIrp( Size, ChargeQuota )
+#define FREE_IRP( Irp ) NwFreeIrp( Irp )
+
+#define ALLOCATE_MDL( Va, Length, Secondary, ChargeQuota, Irp ) \
+ NwAllocateMdl(Va, Length, Secondary, ChargeQuota, Irp, __FILE__, __LINE__ )
+#define FREE_MDL( Mdl ) NwFreeMdl( Mdl )
+
+#else
+
+#define ALLOCATE_POOL_EX( Type, Size ) FsRtlAllocatePool( Type, Size )
+#ifndef QFE_BUILD
+#define ALLOCATE_POOL( Type, Size ) ExAllocatePoolWithTag( Type, Size, 'scwn' )
+#else
+#define ALLOCATE_POOL( Type, Size ) ExAllocatePool( Type, Size )
+#endif
+#define FREE_POOL( Buffer ) ExFreePool( Buffer )
+
+#define ALLOCATE_IRP( Size, ChargeQuota ) \
+ IoAllocateIrp( Size, ChargeQuota )
+#define FREE_IRP( Irp ) IoFreeIrp( Irp )
+
+#define ALLOCATE_MDL( Va, Length, Secondary, ChargeQuota, Irp ) \
+ IoAllocateMdl(Va, Length, Secondary, ChargeQuota, Irp )
+#define FREE_MDL( Mdl ) IoFreeMdl( Mdl )
+#endif
+
+//
+// Useful macros
+//
+
+#define MIN(a,b) ((a)<(b) ? (a):(b))
+#define MAX(a,b) ((a)>(b) ? (a):(b))
+
+#define DIFFERENT_PAGES( START, SIZE ) \
+ (((ULONG)START & ~(4096-1)) != (((ULONG)START + SIZE) & ~(4096-1)))
+
+#define UP_LEVEL_SERVER( Scb ) \
+ ( ( Scb->MajorVersion >= 4 ) || \
+ ( Scb->MajorVersion == 3 && Scb->MinorVersion >= 12 ) )
+
+#define LFN_SUPPORTED( Scb ) \
+ ( ( Scb->MajorVersion >= 4 ) || \
+ ( Scb->MajorVersion == 3 && Scb->MinorVersion >= 11 ) )
+
+#define LongByteSwap( l1, l2 ) \
+{ \
+ PUCHAR c1 = (PUCHAR)&l1; \
+ PUCHAR c2 = (PUCHAR)&l2; \
+ c1[0] = c2[3]; \
+ c1[1] = c2[2]; \
+ c1[2] = c2[1]; \
+ c1[3] = c2[0]; \
+}
+
+#define ShortByteSwap( s1, s2 ) \
+{ \
+ PUCHAR c1 = (PUCHAR)&s1; \
+ PUCHAR c2 = (PUCHAR)&s2; \
+ c1[0] = c2[1]; \
+ c1[1] = c2[0]; \
+}
+
+
+
+#define CanLogTimeOutEvent( LastTime, CurrentTime ) \
+ ( ( CurrentTime.QuadPart ) - ( LastTime.QuadPart ) >= 0 )
+
+#define UpdateNextEventTime( LastTime, CurrentTime, TimeOutEventInterval ) \
+ ( LastTime.QuadPart ) = ( CurrentTime.QuadPart ) + \
+ ( TimeOutEventInterval.QuadPart )
+
+
+
+//
+// Macros to isolate NT 3.1 and NT 3.5 differences.
+//
+
+#ifdef QFE_BUILD
+
+#define NwGetTopLevelIrp() (PIRP)(PsGetCurrentThread()->TopLevelIrp)
+#define NwSetTopLevelIrp(Irp) (PIRP)(PsGetCurrentThread())->TopLevelIrp = Irp;
+
+
+#else
+
+#define NwGetTopLevelIrp() IoGetTopLevelIrp()
+#define NwSetTopLevelIrp(Irp) IoSetTopLevelIrp(Irp)
+
+#endif
+
+#endif // _NWPROCS_
diff --git a/private/nw/rdr/read.c b/private/nw/rdr/read.c
new file mode 100644
index 000000000..de5e63f03
--- /dev/null
+++ b/private/nw/rdr/read.c
@@ -0,0 +1,2838 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ Read.c
+
+Abstract:
+
+ This module implements support for NtReadFile for the
+ NetWare redirector called by the dispatch driver.
+
+Author:
+
+ Colin Watson [ColinW] 07-Apr-1993
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+#ifdef NWDBG
+#include <stdlib.h> // rand()
+#endif
+
+//
+// The local debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_READ)
+
+#define SIZE_ADJUST( ic ) \
+ ( sizeof( ULONG ) + sizeof( ULONG ) + ( ic->Specific.Read.FileOffset & 0x03 ) )
+
+//
+// Local procedure prototypes
+//
+
+NTSTATUS
+NwCommonRead (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+ReadNcp(
+ PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+ReadNcpCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ );
+
+VOID
+BuildReadNcp(
+ PIRP_CONTEXT IrpContext,
+ ULONG FileOffset,
+ USHORT Length
+ );
+
+NTSTATUS
+ParseReadResponse(
+ PIRP_CONTEXT IrpContext,
+ PNCP_READ_RESPONSE Response,
+ ULONG BytesAvailable,
+ PUSHORT Length
+ );
+
+NTSTATUS
+BurstRead(
+ PIRP_CONTEXT IrpContext
+ );
+
+VOID
+BuildBurstReadRequest(
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG Handle,
+ IN ULONG FileOffset,
+ IN ULONG Length
+ );
+
+NTSTATUS
+BurstReadCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ );
+
+VOID
+RecordPacketReceipt(
+ IN OUT PIRP_CONTEXT IrpContext,
+ IN PVOID ReadData,
+ IN ULONG DataOffset,
+ IN USHORT BytesCount,
+ IN BOOLEAN CopyData
+ );
+
+BOOLEAN
+VerifyBurstRead(
+ PIRP_CONTEXT IrpContext
+ );
+
+VOID
+FreePacketList(
+ PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+BurstReadReceive(
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PULONG BytesAccepted,
+ IN PUCHAR Response,
+ OUT PMDL *pReceiveMdl
+ );
+
+NTSTATUS
+ReadNcpReceive(
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PULONG BytesAccepted,
+ IN PUCHAR Response,
+ OUT PMDL *pReceiveMdl
+ );
+
+NTSTATUS
+ParseBurstReadResponse(
+ IN PIRP_CONTEXT IrpContext,
+ PVOID Response,
+ ULONG BytesAvailable,
+ PUCHAR Flags,
+ PULONG DataOffset,
+ PUSHORT BytesThisPacket,
+ PUCHAR *ReadData,
+ PULONG TotalBytesRead
+ );
+
+PMDL
+AllocateReceivePartialMdl(
+ PMDL FullMdl,
+ ULONG DataOffset,
+ ULONG BytesThisPacket
+ );
+
+VOID
+SetConnectionTimeout(
+ PNONPAGED_SCB pNpScb,
+ ULONG Length
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwFsdRead )
+#pragma alloc_text( PAGE, NwCommonRead )
+#pragma alloc_text( PAGE, ReadNcp )
+#pragma alloc_text( PAGE, BurstRead )
+#pragma alloc_text( PAGE, BuildBurstReadRequest )
+#pragma alloc_text( PAGE, ResubmitBurstRead )
+#pragma alloc_text( PAGE, SetConnectionTimeout )
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, ReadNcpCallback )
+#pragma alloc_text( PAGE1, ReadNcpReceive )
+#pragma alloc_text( PAGE1, BuildReadNcp )
+#pragma alloc_text( PAGE1, ParseReadResponse )
+#pragma alloc_text( PAGE1, BurstReadCallback )
+#pragma alloc_text( PAGE1, BurstReadTimeout )
+#pragma alloc_text( PAGE1, RecordPacketReceipt )
+#pragma alloc_text( PAGE1, VerifyBurstRead )
+#pragma alloc_text( PAGE1, FreePacketList )
+#pragma alloc_text( PAGE1, BurstReadReceive )
+#pragma alloc_text( PAGE1, ParseBurstReadResponse )
+#pragma alloc_text( PAGE1, AllocateReceivePartialMdl )
+#endif
+
+#endif
+
+#if 0 // Not pageable
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+
+NTSTATUS
+NwFsdRead(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is the FSD routine that handles NtReadFile.
+
+Arguments:
+
+ NwfsDeviceObject - Supplies the device object for the read function.
+
+ Irp - Supplies the IRP to process.
+
+Return Value:
+
+ NTSTATUS - The result status.
+
+--*/
+
+{
+ PIRP_CONTEXT pIrpContext = NULL;
+ NTSTATUS status;
+ BOOLEAN TopLevel;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwFsdRead\n", 0);
+
+ //
+ // Call the common direcotry control routine.
+ //
+
+ FsRtlEnterFileSystem();
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ try {
+
+ pIrpContext = AllocateIrpContext( Irp );
+ status = NwCommonRead( pIrpContext );
+
+ } except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
+
+ if ( pIrpContext == NULL ) {
+
+ //
+ // If we couldn't allocate an irp context, just complete
+ // irp without any fanfare.
+ //
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
+
+ } else {
+
+ //
+ // We had some trouble trying to perform the requested
+ // operation, so we'll abort the I/O request with
+ // the error Status that we get back from the
+ // execption code
+ //
+
+ status = NwProcessException( pIrpContext, GetExceptionCode() );
+ }
+
+ }
+
+ if ( pIrpContext ) {
+
+ if ( status != STATUS_PENDING ) {
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ }
+
+ NwCompleteRequest( pIrpContext, status );
+ }
+
+ if ( TopLevel ) {
+ NwSetTopLevelIrp( NULL );
+ }
+ FsRtlExitFileSystem();
+
+ //
+ // Return to the caller.
+ //
+
+ DebugTrace(-1, Dbg, "NwFsdRead -> %08lx\n", status );
+
+ Stats.ReadOperations++;
+
+ return status;
+}
+
+
+NTSTATUS
+NwCommonRead (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine does the common code for NtReadFile.
+
+Arguments:
+
+ IrpContext - Supplies the request being processed.
+
+Return Value:
+
+ NTSTATUS - The return status for the operation
+
+--*/
+
+{
+ NTSTATUS status;
+
+ PIRP Irp;
+ PIO_STACK_LOCATION irpSp;
+
+ NODE_TYPE_CODE nodeTypeCode;
+ PICB icb;
+ PFCB fcb;
+ PVOID fsContext;
+
+ ULONG BufferLength; // Size application requested to read
+ ULONG ByteOffset;
+ ULONG PreviousByteOffset;
+ ULONG BytesRead;
+ ULONG NewBufferLength;
+ PVOID SystemBuffer;
+
+ //
+ // Get the current stack location
+ //
+
+ Irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ DebugTrace(+1, Dbg, "CommonRead...\n", 0);
+ DebugTrace( 0, Dbg, "Irp = %08lx\n", (ULONG)Irp);
+
+ //
+ // Decode the file object to figure out who we are. If the result
+ // is not the root DCB then its an illegal parameter.
+ //
+
+ nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
+ &fsContext,
+ (PVOID *)&icb );
+
+ fcb = (PFCB)icb->SuperType.Fcb;
+
+ if (((nodeTypeCode != NW_NTC_ICB) &&
+ (nodeTypeCode != NW_NTC_ICB_SCB)) ||
+ (!icb->HasRemoteHandle) ) {
+
+ DebugTrace(0, Dbg, "Not a file\n", 0);
+
+ status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "CommonRead -> %08lx\n", status );
+ return status;
+ }
+
+ //
+ // Make sure that this ICB is still active.
+ //
+
+ NwVerifyIcbSpecial( icb );
+
+ if ( fcb->NodeTypeCode == NW_NTC_FCB ) {
+
+ IrpContext->pScb = fcb->Scb;
+ IrpContext->pNpScb = IrpContext->pScb->pNpScb;
+ IrpContext->Icb = icb;
+
+ } else if ( fcb->NodeTypeCode == NW_NTC_SCB ) {
+
+ IrpContext->pScb = icb->SuperType.Scb;
+ IrpContext->pNpScb = IrpContext->pScb->pNpScb;
+ IrpContext->Icb = icb;
+ fcb = NULL;
+
+ } else {
+
+ DebugTrace(0, Dbg, "Not a file\n", 0);
+
+ status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "CommonRead -> %08lx\n", status );
+ return status;
+ }
+
+ BufferLength = irpSp->Parameters.Read.Length;
+ ByteOffset = irpSp->Parameters.Read.ByteOffset.LowPart;
+
+ //
+ // Fail reads beyond file offset 4GB.
+ //
+
+ if ( irpSp->Parameters.Read.ByteOffset.HighPart != 0 ) {
+ return( STATUS_INVALID_PARAMETER );
+ }
+
+ //
+ // Special case 0 length read.
+ //
+
+ if ( BufferLength == 0 ) {
+ Irp->IoStatus.Information = 0;
+ return( STATUS_SUCCESS );
+ }
+
+ if (FlagOn(irpSp->FileObject->Flags, FO_SYNCHRONOUS_IO) &&
+ !FlagOn( Irp->Flags, IRP_PAGING_IO)) {
+
+ PreviousByteOffset = irpSp->FileObject->CurrentByteOffset.LowPart;
+ irpSp->FileObject->CurrentByteOffset.LowPart = ByteOffset;
+ }
+
+ //
+ // First flush the write behind cache unless this is a
+ // file stream operation.
+ //
+
+ if ( fcb ) {
+
+ status = AcquireFcbAndFlushCache( IrpContext, fcb->NonPagedFcb );
+ if ( !NT_SUCCESS( status ) ) {
+ goto ResetByteOffsetAndExit;
+ }
+
+ //
+ // Read as much as we can from cache.
+ //
+
+ NwMapUserBuffer( Irp, KernelMode, &SystemBuffer );
+
+ BytesRead = CacheRead(
+ fcb->NonPagedFcb,
+ ByteOffset,
+ BufferLength,
+#if NWFASTIO
+ SystemBuffer,
+ FALSE );
+#else
+ SystemBuffer );
+#endif
+
+ //
+ // If all the data was the the cache, we are done.
+ //
+
+ if ( BytesRead == BufferLength ) {
+
+ Irp->IoStatus.Information = BytesRead;
+
+ //
+ // Update the current byte offset in the file if it is a
+ // synchronous file (and this is not paging I/O).
+ //
+
+ if (FlagOn(irpSp->FileObject->Flags, FO_SYNCHRONOUS_IO) &&
+ !FlagOn( Irp->Flags, IRP_PAGING_IO)) {
+
+ irpSp->FileObject->CurrentByteOffset.QuadPart += BytesRead;
+ }
+
+ //
+ // If this is a paging read, we need to flush the MDL
+ // since on some systems the I-cache and D-cache
+ // are not synchronized.
+ //
+
+ if (FlagOn(Irp->Flags, IRP_PAGING_IO)) {
+ KeFlushIoBuffers( Irp->MdlAddress, TRUE, FALSE);
+ }
+
+ //
+ // Record read offset and size to discover a sequential read pattern.
+ //
+
+ fcb->LastReadOffset = irpSp->Parameters.Read.ByteOffset.LowPart;
+ fcb->LastReadSize = irpSp->Parameters.Read.Length;
+
+ DebugTrace(-1, Dbg, "CommonRead -> %08lx\n", STATUS_SUCCESS );
+ return( STATUS_SUCCESS );
+ }
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ // Protect read cache
+ NwAcquireExclusiveFcb( fcb->NonPagedFcb, TRUE );
+
+ IrpContext->Specific.Read.CacheReadSize = BytesRead;
+ fcb->NonPagedFcb->CacheFileOffset = ByteOffset + BufferLength;
+
+ ByteOffset += BytesRead;
+ BufferLength -= BytesRead;
+
+ NewBufferLength = CalculateReadAheadSize(
+ IrpContext,
+ fcb->NonPagedFcb,
+ BytesRead,
+ ByteOffset,
+ BufferLength );
+
+ IrpContext->Specific.Read.ReadAheadSize = NewBufferLength - BufferLength;
+
+ } else {
+
+ //
+ // This is a read from a ds file stream handle. For now,
+ // there's no cache support.
+ //
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ BytesRead = 0;
+
+ IrpContext->Specific.Read.CacheReadSize = BytesRead;
+ IrpContext->Specific.Read.ReadAheadSize = 0;
+ }
+
+ //
+ // If burst mode is enabled, and this read is too big to do in a single
+ // core read NCP, use burst mode.
+ //
+ // BUGBUG: We don't support burst against a ds file stream yet.
+ //
+
+ if ( IrpContext->pNpScb->ReceiveBurstModeEnabled &&
+ NewBufferLength > IrpContext->pNpScb->BufferSize &&
+ fcb ) {
+ status = BurstRead( IrpContext );
+ } else {
+ status = ReadNcp( IrpContext );
+ }
+
+ Irp->MdlAddress = IrpContext->pOriginalMdlAddress;
+
+ if (Irp->MdlAddress != NULL) {
+ // Next might point to the cache mdl.
+ Irp->MdlAddress->Next = NULL;
+ }
+
+ if ( NT_SUCCESS( status ) ) {
+
+ //
+ // Update the current byte offset in the file if it is a
+ // synchronous file (and this is not paging I/O).
+ //
+
+ if (FlagOn(irpSp->FileObject->Flags, FO_SYNCHRONOUS_IO) &&
+ !FlagOn( Irp->Flags, IRP_PAGING_IO)) {
+
+ irpSp->FileObject->CurrentByteOffset.QuadPart += Irp->IoStatus.Information;
+ }
+
+ //
+ // If this is a paging read, we need to flush the MDL
+ // since on some systems the I-cache and D-cache
+ // are not synchronized.
+ //
+
+ if (FlagOn(Irp->Flags, IRP_PAGING_IO)) {
+ KeFlushIoBuffers( Irp->MdlAddress, TRUE, FALSE);
+ }
+
+ //
+ // If we received 0 bytes without an error, we must be beyond
+ // the end of file.
+ //
+
+ if ( Irp->IoStatus.Information == 0 ) {
+ status = STATUS_END_OF_FILE;
+ }
+ }
+
+ //
+ // Record read offset and size to discover a sequential read pattern.
+ //
+
+ if ( fcb ) {
+
+ fcb->LastReadOffset = irpSp->Parameters.Read.ByteOffset.LowPart;
+ fcb->LastReadSize = irpSp->Parameters.Read.Length;
+
+ NwReleaseFcb( fcb->NonPagedFcb );
+
+ }
+
+ DebugTrace(-1, Dbg, "CommonRead -> %08lx\n", status);
+
+ResetByteOffsetAndExit:
+
+ if ( !NT_SUCCESS( status ) ) {
+
+ if (FlagOn(irpSp->FileObject->Flags, FO_SYNCHRONOUS_IO) &&
+ !FlagOn( Irp->Flags, IRP_PAGING_IO)) {
+
+ irpSp->FileObject->CurrentByteOffset.LowPart = PreviousByteOffset;
+
+ }
+ }
+
+ return status;
+}
+
+NTSTATUS
+ReadNcp(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine exchanges a series of read NCPs with the server.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+ Icb - Supplies the file specific information.
+
+Return Value:
+
+ Status of transfer.
+
+--*/
+{
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ ULONG Length; // Size we will send to the server
+ PMDL DataMdl;
+
+ PSCB pScb;
+ PNONPAGED_SCB pNpScb;
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+ PICB Icb;
+ ULONG ByteOffset;
+ ULONG BufferLength;
+ ULONG MdlLength;
+ BOOLEAN Done;
+ PMDL Mdl, NextMdl;
+
+ irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+ Icb = IrpContext->Icb;
+
+ BufferLength = irpSp->Parameters.Read.Length +
+ IrpContext->Specific.Read.ReadAheadSize -
+ IrpContext->Specific.Read.CacheReadSize;
+
+ ByteOffset = irpSp->Parameters.Read.ByteOffset.LowPart +
+ IrpContext->Specific.Read.CacheReadSize;
+
+ IrpContext->Specific.Read.FileOffset = ByteOffset;
+
+ DebugTrace(+1, Dbg, "ReadNcp...\n", 0);
+ DebugTrace( 0, Dbg, "irp = %08lx\n", (ULONG)irp);
+ DebugTrace( 0, Dbg, "File = %wZ\n", &Icb->SuperType.Fcb->FullFileName);
+ DebugTrace( 0, Dbg, "Length = %ld\n", BufferLength);
+ DebugTrace( 0, Dbg, "Offset = %d\n", ByteOffset);
+
+ if ( Icb->SuperType.Fcb->NodeTypeCode == NW_NTC_FCB ) {
+
+ pScb = Icb->SuperType.Fcb->Scb;
+
+ } else if ( Icb->SuperType.Fcb->NodeTypeCode == NW_NTC_SCB ) {
+
+ pScb = Icb->SuperType.Scb;
+
+ }
+
+ ASSERT( pScb );
+
+ //
+ // Update the original MDL record in the Irp context so that we
+ // can restore it on i/o completion.
+ //
+
+ IrpContext->pOriginalMdlAddress = irp->MdlAddress;
+
+ Length = MIN( IrpContext->pNpScb->BufferSize, BufferLength );
+
+ //
+ // The old servers will not accept reads that cross 4k boundaries in the file
+ //
+
+ if ((IrpContext->pNpScb->PageAlign) &&
+ (DIFFERENT_PAGES( ByteOffset, Length ))) {
+
+ Length = 4096 - ((ULONG)ByteOffset & (4096-1));
+
+ }
+
+ IrpContext->Specific.Read.Buffer = irp->UserBuffer;
+ IrpContext->Specific.Read.ReadOffset = IrpContext->Specific.Read.CacheReadSize;
+ IrpContext->Specific.Read.RemainingLength = BufferLength;
+ IrpContext->Specific.Read.PartialMdl = NULL;
+
+ //
+ // Set up to process a read NCP
+ //
+
+ pNpScb = pScb->pNpScb;
+ IrpContext->pEx = ReadNcpCallback;
+ IrpContext->Destination = pNpScb->RemoteAddress;
+ IrpContext->PacketType = NCP_FUNCTION;
+ IrpContext->ReceiveDataRoutine = ReadNcpReceive;
+
+ pNpScb->MaxTimeOut = 2 * pNpScb->TickCount + 10;
+ pNpScb->TimeOut = pNpScb->SendTimeout;
+ pNpScb->RetryCount = DefaultRetryCount;
+
+ Done = FALSE;
+
+ while ( !Done ) {
+
+ //
+ // Setup to do at most 64K of i/o asynchronously, or buffer length.
+ //
+
+ IrpContext->Specific.Read.BurstSize =
+ MIN( 64 * 1024, IrpContext->Specific.Read.RemainingLength );
+
+ IrpContext->Specific.Read.BurstRequestOffset = 0;
+
+ //
+ // Try to allocate an MDL for this i/o.
+ //
+
+ if ( IrpContext->Specific.Read.ReadAheadSize == 0 ) {
+ MdlLength = IrpContext->Specific.Read.BurstSize;
+ } else {
+ MdlLength = IrpContext->Specific.Read.BurstSize - IrpContext->Specific.Read.ReadAheadSize;
+ }
+
+ DataMdl = ALLOCATE_MDL(
+ (PCHAR)IrpContext->Specific.Read.Buffer +
+ IrpContext->Specific.Read.ReadOffset,
+ MdlLength,
+ FALSE, // Secondary Buffer
+ FALSE, // Charge Quota
+ NULL);
+
+ if ( DataMdl == NULL ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ IrpContext->Specific.Read.FullMdl = DataMdl;
+
+ //
+ // If there is no MDL for this read, probe the data MDL to
+ // lock it's pages down. Otherwise, use the data MDL as
+ // a partial MDL.
+ //
+
+ if ( IrpContext->pOriginalMdlAddress == NULL ) {
+
+ try {
+ MmProbeAndLockPages( DataMdl, irp->RequestorMode, IoWriteAccess);
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ FREE_MDL( DataMdl );
+ return GetExceptionCode();
+ }
+
+ } else {
+
+ IoBuildPartialMdl(
+ IrpContext->pOriginalMdlAddress,
+ DataMdl,
+ (PCHAR)IrpContext->Specific.Read.Buffer +
+ IrpContext->Specific.Read.ReadOffset,
+ MdlLength );
+
+ }
+
+ IrpContext->Specific.Read.BurstBuffer = MmGetSystemAddressForMdl( DataMdl );
+
+ if ( IrpContext->Specific.Read.BurstSize ==
+ IrpContext->Specific.Read.RemainingLength ) {
+ Done = TRUE;
+ }
+
+ if ( IrpContext->Specific.Read.ReadAheadSize != 0 ) {
+ DataMdl->Next = Icb->NpFcb->CacheMdl;
+ }
+
+ IrpContext->Specific.Read.LastReadLength = Length;
+
+ //
+ // Build and send the request.
+ //
+
+ BuildReadNcp(
+ IrpContext,
+ IrpContext->Specific.Read.FileOffset,
+ (USHORT) MIN( Length, IrpContext->Specific.Read.RemainingLength ) );
+
+ status = PrepareAndSendPacket( IrpContext );
+ if ( NT_SUCCESS( status )) {
+ KeWaitForSingleObject(
+ &IrpContext->Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL
+ );
+
+ status = IrpContext->Specific.Read.Status;
+
+ }
+
+ //
+ // Stop looping if the read failed, or we read less data than
+ // requested.
+ //
+
+ if ( !NT_SUCCESS( status ) ||
+ IrpContext->Specific.Read.BurstSize != 0 ) {
+
+ Done = TRUE;
+
+ }
+
+ if ( IrpContext->pOriginalMdlAddress == NULL ) {
+ MmUnlockPages( DataMdl );
+ }
+
+ FREE_MDL( DataMdl );
+
+ }
+
+ //
+ // Free the receive MDL if one was allocated.
+ //
+
+ Mdl = IrpContext->Specific.Read.PartialMdl;
+
+ while ( Mdl != NULL ) {
+ NextMdl = Mdl->Next;
+ FREE_MDL( Mdl );
+ Mdl = NextMdl;
+ }
+
+ DebugTrace(-1, Dbg, "ReadNcp -> %08lx\n", status );
+
+ Stats.ReadNcps++;
+
+ return status;
+}
+
+
+NTSTATUS
+ReadNcpCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ )
+
+/*++
+
+Routine Description:
+
+ This routine receives the response from a user NCP.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+ BytesAvailable - Number of bytes in the response.
+
+ Response - The response data.
+
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PIRP Irp;
+ PIO_STACK_LOCATION irpSp;
+ ULONG Length;
+ USHORT USLength;
+ PNONPAGED_FCB NpFcb;
+
+ DebugTrace(0, Dbg, "ReadNcpCallback...\n", 0);
+
+ if ( BytesAvailable == 0) {
+
+ //
+ // No response from server. Status is in pIrpContext->
+ // ResponseParameters.Error
+ //
+
+ IrpContext->Specific.Read.Status = STATUS_REMOTE_NOT_LISTENING;
+
+ NwSetIrpContextEvent( IrpContext );
+ return STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ //
+ // How much data was received?
+ //
+
+ Status = ParseReadResponse(
+ IrpContext,
+ (PNCP_READ_RESPONSE)Response,
+ BytesAvailable,
+ &USLength );
+
+ Length = (ULONG)USLength;
+ DebugTrace(0, Dbg, "Ncp contains %d bytes\n", Length);
+
+ if ((NT_SUCCESS(Status)) &&
+ (Length != 0)) {
+
+ //
+ // If we are receiving the data at indication time, copy the
+ // user's data to the user's buffer.
+ //
+
+ if ( Response != IrpContext->rsp ) {
+
+ //
+ // Read in the data.
+ // Note: If the FileOffset is at an odd byte then the server
+ // will insert an extra pad byte.
+ //
+
+ CopyBufferToMdl(
+ IrpContext->Specific.Read.FullMdl,
+ IrpContext->Specific.Read.BurstRequestOffset,
+ Response + sizeof( NCP_READ_RESPONSE ) + ( IrpContext->Specific.Read.FileOffset & 1),
+ Length );
+
+ DebugTrace( 0, Dbg, "RxLength= %ld\n", Length);
+
+ dump( Dbg,(PUCHAR)IrpContext->Specific.Read.BurstBuffer +
+ IrpContext->Specific.Read.BurstRequestOffset,
+ Length);
+
+ }
+
+ DebugTrace( 0, Dbg, "RxLength= %ld\n", Length);
+ IrpContext->Specific.Read.ReadOffset += Length;
+ IrpContext->Specific.Read.BurstRequestOffset += Length;
+ IrpContext->Specific.Read.FileOffset += Length;
+ IrpContext->Specific.Read.RemainingLength -= Length;
+ IrpContext->Specific.Read.BurstSize -= Length;
+ }
+
+ DebugTrace( 0, Dbg, "RemainingLength = %ld\n",IrpContext->Specific.Read.RemainingLength);
+
+ //
+ // If the previous read was succesful, and we received as much data
+ // as we asked for, and there is more locked data, send the next
+ // read request.
+ //
+
+
+ if ( ( NT_SUCCESS( Status ) ) &&
+ ( IrpContext->Specific.Read.BurstSize != 0 ) &&
+ ( Length == IrpContext->Specific.Read.LastReadLength ) ) {
+
+ //
+ // Read the next packet.
+ //
+
+ Length = MIN( IrpContext->pNpScb->BufferSize,
+ IrpContext->Specific.Read.BurstSize );
+
+ //
+ // The server will not accept reads that cross 4k boundaries
+ // in the file.
+ //
+
+ if ((IrpContext->pNpScb->PageAlign) &&
+ (DIFFERENT_PAGES( IrpContext->Specific.Read.FileOffset, Length ))) {
+ Length = 4096 - ((ULONG)IrpContext->Specific.Read.FileOffset & (4096-1));
+ }
+
+ IrpContext->Specific.Read.LastReadLength = Length;
+ DebugTrace( 0, Dbg, "Length = %ld\n", Length);
+
+ //
+ // Build and send the request.
+ //
+
+ BuildReadNcp(
+ IrpContext,
+ IrpContext->Specific.Read.FileOffset,
+ (USHORT)Length );
+
+ Status = PrepareAndSendPacket( IrpContext );
+
+ Stats.ReadNcps++;
+
+ if ( !NT_SUCCESS(Status) ) {
+ // Abandon this request
+ goto returnstatus;
+ }
+
+ } else {
+returnstatus:
+
+ Irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+ NpFcb = IrpContext->Icb->NpFcb;
+
+ if ( IrpContext->Icb->NodeTypeCode == NW_NTC_ICB_SCB ) {
+ NpFcb = NULL;
+ }
+
+ //
+ // Calculate how much data we read into the cache, and how much data
+ // we read into the users buffer.
+ //
+
+ if ( NpFcb ) {
+
+ if ( IrpContext->Specific.Read.ReadOffset > irpSp->Parameters.Read.Length ) {
+
+ ASSERT(NpFcb->CacheBuffer != NULL ) ; // had better be there..
+
+ NpFcb->CacheDataSize = IrpContext->Specific.Read.ReadOffset -
+ irpSp->Parameters.Read.Length;
+
+ Irp->IoStatus.Information = irpSp->Parameters.Read.Length;
+
+ } else {
+
+ NpFcb->CacheDataSize = 0;
+ Irp->IoStatus.Information = IrpContext->Specific.Read.ReadOffset;
+
+ }
+
+ } else {
+
+ Irp->IoStatus.Information = IrpContext->Specific.Read.ReadOffset;
+
+ }
+
+ //
+ // We're done with this request, signal the reading thread.
+ //
+
+ IrpContext->Specific.Read.Status = Status;
+
+ NwSetIrpContextEvent( IrpContext );
+ }
+
+ DebugTrace( 0, Dbg, "ReadNcpCallback -> %08lx\n", Status );
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+ReadNcpReceive(
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PULONG BytesAccepted,
+ IN PUCHAR Response,
+ OUT PMDL *pReceiveMdl
+ )
+{
+ PMDL ReceiveMdl;
+ PMDL Mdl, NextMdl;
+
+ DebugTrace( 0, Dbg, "ReadNcpReceive\n", 0 );
+
+ Mdl = IrpContext->Specific.Read.PartialMdl;
+ IrpContext->Specific.Read.PartialMdl = NULL;
+
+ while ( Mdl != NULL ) {
+ NextMdl = Mdl->Next;
+ FREE_MDL( Mdl );
+ Mdl = NextMdl;
+ }
+
+ //
+ // Set up receive MDL. Note that we get an extra byte of header
+ // when reading from an odd offset.
+ //
+
+ IrpContext->RxMdl->ByteCount = sizeof( NCP_READ_RESPONSE ) +
+ (IrpContext->Specific.Read.FileOffset & 1);
+
+ ASSERT( IrpContext->Specific.Read.FullMdl != NULL );
+
+ //
+ // If we are reading at EOF, or there was a read error there will
+ // be a small response.
+ //
+
+ if ( BytesAvailable > MmGetMdlByteCount( IrpContext->RxMdl ) ) {
+
+ ReceiveMdl = AllocateReceivePartialMdl(
+ IrpContext->Specific.Read.FullMdl,
+ IrpContext->Specific.Read.BurstRequestOffset,
+ BytesAvailable - MmGetMdlByteCount( IrpContext->RxMdl ) );
+
+ IrpContext->RxMdl->Next = ReceiveMdl;
+
+ // Record Mdl to free when CopyIndicatedData or Irp completed.
+ IrpContext->Specific.Read.PartialMdl = ReceiveMdl;
+
+ } else {
+
+ IrpContext->RxMdl->Next = NULL;
+
+ }
+
+ *pReceiveMdl = IrpContext->RxMdl;
+ return STATUS_SUCCESS;
+}
+
+
+VOID
+BuildReadNcp(
+ PIRP_CONTEXT IrpContext,
+ ULONG FileOffset,
+ USHORT Length
+ )
+{
+ PNCP_READ_REQUEST ReadRequest;
+
+ ReadRequest = (PNCP_READ_REQUEST)IrpContext->req;
+
+ ReadRequest->RequestHeader.NcpHeader.Command = PEP_COMMAND_REQUEST;
+ ReadRequest->RequestHeader.NcpHeader.ConnectionIdLow =
+ IrpContext->pNpScb->ConnectionNo;
+ ReadRequest->RequestHeader.NcpHeader.ConnectionIdHigh =
+ IrpContext->pNpScb->ConnectionNoHigh;
+ ReadRequest->RequestHeader.NcpHeader.TaskId =
+ IrpContext->Icb->Pid;
+
+ ReadRequest->RequestHeader.FunctionCode = NCP_READ_FILE;
+ ReadRequest->Unused = 0;
+ RtlMoveMemory(
+ ReadRequest->Handle,
+ IrpContext->Icb->Handle,
+ sizeof( IrpContext->Icb->Handle ) );
+
+ LongByteSwap( ReadRequest->FileOffset, FileOffset );
+ ShortByteSwap( ReadRequest->Length, Length );
+
+ IrpContext->TxMdl->ByteCount = sizeof( *ReadRequest );
+ SetFlag( IrpContext->Flags, IRP_FLAG_SEQUENCE_NO_REQUIRED );
+ ClearFlag( IrpContext->Flags, IRP_FLAG_RETRY_SEND );
+
+ return;
+}
+
+NTSTATUS
+ParseReadResponse(
+ PIRP_CONTEXT IrpContext,
+ PNCP_READ_RESPONSE Response,
+ ULONG BytesAvailable,
+ PUSHORT Length )
+{
+ NTSTATUS Status;
+
+ Status = ParseNcpResponse( IrpContext, &Response->ResponseHeader );
+
+ if (!NT_SUCCESS(Status)) {
+ return( Status );
+ }
+
+ if ( BytesAvailable < sizeof( NCP_READ_RESPONSE ) ) {
+ return( STATUS_UNEXPECTED_NETWORK_ERROR );
+ }
+
+ ShortByteSwap( *Length, Response->Length );
+
+ return( Status );
+}
+
+NTSTATUS
+BurstRead(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine exchanges a series of burst read NCPs with the server.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+ ByteOffset - The file offset for the read.
+
+ BufferLength - The number of bytes to read.
+
+Return Value:
+
+ Status of transfer.
+
+--*/
+{
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ ULONG Length; // Size we will send to the server
+ PMDL DataMdl;
+ ULONG MdlLength;
+
+ PSCB pScb;
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+ PICB Icb;
+ PNONPAGED_SCB pNpScb;
+ ULONG ByteOffset;
+ ULONG BufferLength;
+
+ BOOLEAN Done;
+
+ PAGED_CODE();
+
+ pNpScb = IrpContext->pNpScb;
+
+ irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+ Icb = IrpContext->Icb;
+
+ BufferLength = irpSp->Parameters.Read.Length +
+ IrpContext->Specific.Read.ReadAheadSize -
+ IrpContext->Specific.Read.CacheReadSize;
+
+ ByteOffset = irpSp->Parameters.Read.ByteOffset.LowPart +
+ IrpContext->Specific.Read.CacheReadSize;
+
+ IrpContext->Specific.Read.FileOffset = ByteOffset;
+ IrpContext->Specific.Read.TotalReadOffset = ByteOffset;
+ IrpContext->Specific.Read.TotalReadLength = BufferLength;
+
+ DebugTrace(+1, Dbg, "BurstRead...\n", 0);
+ DebugTrace( 0, Dbg, "irp = %08lx\n", (ULONG)irp);
+ DebugTrace( 0, Dbg, "File = %wZ\n", &Icb->SuperType.Fcb->FullFileName);
+ DebugTrace( 0, Dbg, "Length = %ld\n", BufferLength);
+ DebugTrace( 0, Dbg, "Offset = %ld\n", ByteOffset);
+ DebugTrace( 0, Dbg, "Org Len = %ld\n", irpSp->Parameters.Read.Length );
+ DebugTrace( 0, Dbg, "Org Off = %ld\n", irpSp->Parameters.Read.ByteOffset.LowPart );
+
+ ASSERT (Icb->SuperType.Fcb->NodeTypeCode == NW_NTC_FCB);
+
+ pScb = Icb->SuperType.Fcb->Scb;
+
+ ASSERT (pScb->NodeTypeCode == NW_NTC_SCB);
+
+ //
+ // Update the original MDL record in the Irp context so that we
+ // can restore it on i/o completion.
+ //
+
+ IrpContext->pOriginalMdlAddress = irp->MdlAddress;
+
+ Length = MIN( pNpScb->MaxReceiveSize, BufferLength );
+
+ if ( pNpScb->BurstRenegotiateReqd ) {
+ pNpScb->BurstRenegotiateReqd = FALSE;
+
+ RenegotiateBurstMode( IrpContext, pNpScb );
+ }
+
+ IrpContext->Specific.Read.ReadOffset = IrpContext->Specific.Read.CacheReadSize;
+ IrpContext->Specific.Read.RemainingLength = BufferLength;
+ IrpContext->Specific.Read.LastReadLength = Length;
+
+ InitializeListHead( &IrpContext->Specific.Read.PacketList );
+ IrpContext->Specific.Read.BurstRequestOffset = 0;
+ IrpContext->Specific.Read.BurstSize = 0;
+ IrpContext->Specific.Read.DataReceived = FALSE;
+
+ IrpContext->pTdiStruct = &pNpScb->Burst;
+ IrpContext->TimeoutRoutine = BurstReadTimeout;
+ IrpContext->ReceiveDataRoutine = BurstReadReceive;
+
+ IrpContext->Specific.Read.Buffer = irp->UserBuffer;
+
+ IrpContext->pEx = BurstReadCallback;
+ IrpContext->Destination = pNpScb->RemoteAddress;
+ IrpContext->PacketType = NCP_BURST;
+
+ //
+ // Tell BurstWrite that it needs to send a dummy Ncp on the next write.
+ //
+
+ pNpScb->BurstDataWritten = 0x00010000;
+
+ //
+ // The server will pause NwReceiveDelay between packets. Make sure we have our timeout
+ // so that we will take that into account.
+ //
+
+ SetConnectionTimeout( IrpContext->pNpScb, Length );
+
+ Done = FALSE;
+
+ while ( !Done ) {
+
+ //
+ // Set burst read timeouts to how long we think the burst should take.
+ //
+
+ pNpScb->RetryCount = 20;
+
+ //
+ // Allocate and build an MDL for the users buffer.
+ //
+
+ if ( IrpContext->Specific.Read.ReadAheadSize == 0 ) {
+ MdlLength = Length;
+ } else {
+ MdlLength = Length - IrpContext->Specific.Read.ReadAheadSize;
+ }
+
+ DataMdl = ALLOCATE_MDL(
+ (PCHAR)IrpContext->Specific.Read.Buffer +
+ IrpContext->Specific.Read.ReadOffset,
+ MdlLength,
+ FALSE, // Secondary Buffer
+ FALSE, // Charge Quota
+ NULL);
+
+ if ( DataMdl == NULL ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // If there is no MDL for this read, probe the data MDL to lock it's
+ // pages down.
+ //
+ // Otherwise, use the data MDL as a partial MDL and lock the pages
+ // accordingly.
+ //
+
+ if ( IrpContext->pOriginalMdlAddress == NULL ) {
+
+ try {
+ MmProbeAndLockPages( DataMdl, irp->RequestorMode, IoWriteAccess);
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ FREE_MDL( DataMdl );
+ return GetExceptionCode();
+ }
+
+ } else {
+
+ IoBuildPartialMdl(
+ IrpContext->pOriginalMdlAddress,
+ DataMdl,
+ (PCHAR)IrpContext->Specific.Read.Buffer +
+ IrpContext->Specific.Read.ReadOffset,
+ MdlLength );
+ }
+
+ IrpContext->Specific.Read.FullMdl = DataMdl;
+ IrpContext->Specific.Read.BurstBuffer = MmGetSystemAddressForMdl( DataMdl );
+
+ if ( IrpContext->Specific.Read.ReadAheadSize != 0 ) {
+ DataMdl->Next = Icb->NpFcb->CacheMdl;
+ }
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_BURST_REQUEST | IRP_FLAG_BURST_PACKET );
+
+ //
+ // Send the request.
+ //
+
+ BuildBurstReadRequest(
+ IrpContext,
+ *(ULONG UNALIGNED *)(&Icb->Handle[2]),
+ IrpContext->Specific.Read.FileOffset,
+ Length );
+
+ status = PrepareAndSendPacket( IrpContext );
+ if ( NT_SUCCESS( status )) {
+ status = KeWaitForSingleObject(
+ &IrpContext->Event,
+ Executive,
+ KernelMode,
+ FALSE,
+ NULL
+ );
+ }
+
+ if ( IrpContext->pOriginalMdlAddress == NULL ) {
+ MmUnlockPages( DataMdl );
+ }
+
+ FREE_MDL( DataMdl );
+ FreePacketList( IrpContext );
+
+ ClearFlag( IrpContext->Flags, IRP_FLAG_BURST_REQUEST );
+
+ status = IrpContext->Specific.Read.Status;
+
+ if ( status != STATUS_REMOTE_NOT_LISTENING ) {
+ IrpContext->pNpScb->BurstRequestNo++;
+ NwProcessReceiveBurstSuccess( IrpContext->pNpScb );
+ }
+
+ if ( !NT_SUCCESS( status ) ) {
+ return( status );
+ }
+
+ //
+ // Update the read status data.
+ //
+
+ IrpContext->Specific.Read.ReadOffset +=
+ IrpContext->Specific.Read.BurstSize;
+ IrpContext->Specific.Read.FileOffset +=
+ IrpContext->Specific.Read.BurstSize;
+ IrpContext->Specific.Read.RemainingLength -=
+ IrpContext->Specific.Read.BurstSize;
+
+ if ( IrpContext->Specific.Read.LastReadLength ==
+ IrpContext->Specific.Read.BurstSize &&
+
+ IrpContext->Specific.Read.RemainingLength > 0 ) {
+
+ //
+ // We've received all the data from the current burst, and we
+ // received as many bytes as we asked for, and we need more data
+ // to satisfy the users read request, start another read burst.
+ //
+
+ Length = MIN( IrpContext->pNpScb->MaxReceiveSize,
+ IrpContext->Specific.Read.RemainingLength );
+
+ DebugTrace( 0, Dbg, "Requesting another burst, length = %ld\n", Length);
+
+ ASSERT( Length != 0 );
+
+ IrpContext->Specific.Read.LastReadLength = Length;
+ (PUCHAR)IrpContext->Specific.Read.BurstBuffer +=
+ IrpContext->Specific.Read.BurstSize;
+ IrpContext->Specific.Read.BurstRequestOffset = 0;
+ IrpContext->Specific.Read.BurstSize = 0;
+ IrpContext->Specific.Read.DataReceived = FALSE;
+
+ } else {
+ Done = TRUE;
+ }
+
+ }
+
+
+ //
+ // Calculate how much data we read into the cache, and how much data
+ // we read into the users buffer.
+ //
+
+ if ( IrpContext->Specific.Read.ReadOffset > irpSp->Parameters.Read.Length ) {
+
+ ASSERT(Icb->NpFcb->CacheBuffer != NULL ) ; // this had better be there
+
+ Icb->NpFcb->CacheDataSize =
+ IrpContext->Specific.Read.ReadOffset -
+ irpSp->Parameters.Read.Length;
+
+ irp->IoStatus.Information = irpSp->Parameters.Read.Length;
+
+ } else {
+
+ Icb->NpFcb->CacheDataSize = 0;
+ irp->IoStatus.Information = IrpContext->Specific.Read.ReadOffset;
+
+ }
+
+ DebugTrace( 0, Dbg, "BytesRead -> %08lx\n", irp->IoStatus.Information );
+ DebugTrace(-1, Dbg, "BurstRead -> %08lx\n", status );
+
+ Stats.PacketBurstReadNcps++;
+ return status;
+}
+
+VOID
+BuildBurstReadRequest(
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG Handle,
+ IN ULONG FileOffset,
+ IN ULONG Length
+ )
+{
+ PNCP_BURST_READ_REQUEST BurstRead;
+ PNONPAGED_SCB pNpScb;
+ ULONG Temp;
+
+ BurstRead = (PNCP_BURST_READ_REQUEST)(IrpContext->req);
+ pNpScb = IrpContext->pNpScb;
+
+ BurstRead->BurstHeader.Command = PEP_COMMAND_BURST;
+ BurstRead->BurstHeader.Flags = BURST_FLAG_END_OF_BURST;
+ BurstRead->BurstHeader.StreamType = 0x02;
+ BurstRead->BurstHeader.SourceConnection = pNpScb->SourceConnectionId;
+ BurstRead->BurstHeader.DestinationConnection = pNpScb->DestinationConnectionId;
+
+ LongByteSwap( BurstRead->BurstHeader.SendDelayTime, pNpScb->NwReceiveDelay );
+
+ pNpScb->CurrentBurstDelay = pNpScb->NwReceiveDelay;
+
+ Temp = sizeof( NCP_BURST_READ_REQUEST ) - sizeof( NCP_BURST_HEADER );
+ LongByteSwap( BurstRead->BurstHeader.DataSize, Temp);
+
+ BurstRead->BurstHeader.BurstOffset = 0;
+
+ ShortByteSwap( BurstRead->BurstHeader.BurstLength, Temp );
+
+ BurstRead->BurstHeader.MissingFragmentCount = 0;
+
+ BurstRead->Function = 1;
+ BurstRead->Handle = Handle;
+
+ LongByteSwap(
+ BurstRead->TotalReadOffset,
+ IrpContext->Specific.Read.TotalReadOffset );
+
+ LongByteSwap(
+ BurstRead->TotalReadLength,
+ IrpContext->Specific.Read.TotalReadLength );
+
+ LongByteSwap( BurstRead->Offset, FileOffset );
+ LongByteSwap( BurstRead->Length, Length );
+
+ IrpContext->TxMdl->ByteCount = sizeof( NCP_BURST_READ_REQUEST );
+}
+
+#ifdef NWDBG
+int DropReadPackets;
+#endif
+
+
+NTSTATUS
+BurstReadCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ )
+/*++
+
+Routine Description:
+
+ This routine receives the response from a user NCP.
+
+Arguments:
+
+ pIrpContext - A pointer to the context information for this IRP.
+
+ BytesAvailable - Actual number of bytes in the received message.
+
+ RspData - Points to the receive buffer.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG DataOffset;
+ ULONG TotalBytesRead;
+ PUCHAR ReadData;
+ USHORT BytesThisPacket = 0;
+ UCHAR Flags;
+ KIRQL OldIrql;
+
+ DebugTrace(+1, Dbg, "BurstReadCallback...\n", 0);
+ DebugTrace( 0, Dbg, "IrpContext = %X\n", IrpContext );
+
+ if ( BytesAvailable == 0) {
+
+ //
+ // No response from server.
+ //
+
+ IrpContext->Specific.Read.Status = STATUS_REMOTE_NOT_LISTENING;
+ NwSetIrpContextEvent( IrpContext );
+
+ DebugTrace( -1, Dbg, "BurstReadCallback -> %X\n", STATUS_REMOTE_NOT_LISTENING );
+ return STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ Stats.PacketBurstReadNcps++;
+
+ if ( Response != IrpContext->rsp ) {
+
+ //
+ // Acquire the SCB spin lock to protect access to the list
+ // of received data for this read.
+ //
+
+ KeAcquireSpinLock( &IrpContext->pNpScb->NpScbSpinLock, &OldIrql );
+
+ Status = ParseBurstReadResponse(
+ IrpContext,
+ Response,
+ BytesAvailable,
+ &Flags,
+ &DataOffset,
+ &BytesThisPacket,
+ &ReadData,
+ &TotalBytesRead );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ IrpContext->Specific.Read.Status = Status;
+ KeReleaseSpinLock( &IrpContext->pNpScb->NpScbSpinLock, OldIrql );
+ return( STATUS_SUCCESS );
+ }
+
+ //
+ // Update the list of data received, and copy the data to the users
+ // buffer.
+ //
+
+ RecordPacketReceipt( IrpContext, ReadData, DataOffset, BytesThisPacket, TRUE );
+ KeReleaseSpinLock( &IrpContext->pNpScb->NpScbSpinLock, OldIrql );
+
+ } else {
+ Flags = IrpContext->Specific.Read.Flags;
+ }
+
+ //
+ // If this isn't the last packet setup for the next burst packet.
+ //
+
+ if ( !FlagOn( Flags, BURST_FLAG_END_OF_BURST ) ) {
+
+ DebugTrace(0, Dbg, "Waiting for another packet\n", 0);
+
+ IrpContext->pNpScb->OkToReceive = TRUE;
+
+ DebugTrace( -1, Dbg, "BurstReadCallback -> %X\n", STATUS_SUCCESS );
+ return( STATUS_SUCCESS );
+ }
+
+ DebugTrace(0, Dbg, "Received final packet\n", 0);
+
+ //
+ // Have we received all of the data? If not, VerifyBurstRead will
+ // send a missing data request.
+ //
+
+ if ( VerifyBurstRead( IrpContext ) ) {
+
+ //
+ // All the data for the current burst has been received, notify
+ // the thread that is sending the data.
+ //
+
+ if (NT_SUCCESS(IrpContext->Specific.Read.Status)) {
+
+ //
+ // If Irp allocation fails then it is possible for the
+ // packet to have been recorded but not copied into the
+ // user buffer. In this case leave the failure status.
+ //
+
+ IrpContext->Specific.Read.Status = STATUS_SUCCESS;
+ }
+
+ NwSetIrpContextEvent( IrpContext );
+
+ }
+
+ DebugTrace( -1, Dbg, "BurstReadCallback -> %X\n", STATUS_SUCCESS );
+ return STATUS_SUCCESS;
+
+}
+
+VOID
+BurstReadTimeout(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine handles a burst read timeout, i.e. no immediate response
+ to the current burst read request. It request to read the packet burst
+ data from the last valid received packet.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+Return Value:
+
+ Status of transfer.
+
+--*/
+{
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+
+ DebugTrace(0, Dbg, "BurstReadTimeout\n", 0 );
+
+ //
+ // Re-request the data we haven't received.
+ //
+
+ if ( !IrpContext->Specific.Read.DataReceived ) {
+
+ DebugTrace( 0, Dbg, "No packets received, retranmit\n", 0 );
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_RETRY_SEND );
+
+ //
+ // We never received any data. Try retransmitting the previous
+ // request.
+ //
+
+ PreparePacket( IrpContext, IrpContext->pOriginalIrp, IrpContext->TxMdl );
+ SendNow( IrpContext );
+
+ } else {
+
+ IrpContext->Specific.Read.DataReceived = FALSE;
+
+ //
+ // Verify burst read will send a missing data request if one we
+ // have not received all of the data.
+ //
+
+ if ( VerifyBurstRead( IrpContext ) ) {
+ NwSetIrpContextEvent( IrpContext );
+ }
+ }
+
+ Stats.PacketBurstReadTimeouts++;
+}
+
+NTSTATUS
+ResubmitBurstRead (
+ IN PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine handles a rerouted burst read. The burst request is
+ resubmitted on a new burst connection.
+
+Arguments:*
+
+ pIrpContext - A pointer to the context information for this IRP.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NTSTATUS Status;
+ ULONG Length, DataMdlBytes = 0 ;
+ PMDL DataMdl ;
+
+ DebugTrace( 0, Dbg, "ResubmitBurstRead\n", 0 );
+
+ //
+ // Recalculate the burst size, as MaxReceiveSize may have changed.
+ //
+
+ Length = MIN( IrpContext->pNpScb->MaxReceiveSize,
+ IrpContext->Specific.Read.RemainingLength );
+
+ //
+ // Make sure we dont ask for more than bytes described by MDL
+ //
+ DataMdl = IrpContext->Specific.Read.FullMdl;
+
+ while (DataMdl) {
+
+ DataMdlBytes += MmGetMdlByteCount( DataMdl );
+ DataMdl = DataMdl->Next;
+ }
+
+ Length = MIN( Length, DataMdlBytes ) ;
+
+ DebugTrace( 0, Dbg, "Requesting another burst, length = %ld\n", Length);
+
+ ASSERT( Length != 0 );
+
+ //
+ // Free the packet list, and reset all of the current burst context
+ // information.
+ //
+
+ FreePacketList( IrpContext );
+
+ IrpContext->Specific.Read.LastReadLength = Length;
+ IrpContext->Specific.Read.BurstRequestOffset = 0;
+ IrpContext->Specific.Read.BurstSize = 0;
+ IrpContext->Specific.Read.DataReceived = FALSE;
+
+ SetConnectionTimeout( IrpContext->pNpScb, Length );
+
+ //
+ // Format and send the request.
+ //
+
+ BuildBurstReadRequest(
+ IrpContext,
+ *(ULONG UNALIGNED *)(&IrpContext->Icb->Handle[2]),
+ IrpContext->Specific.Read.FileOffset,
+ Length );
+
+ // Avoid SendNow setting the RetryCount back to the default
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_RETRY_SEND );
+
+ Status = PrepareAndSendPacket( IrpContext );
+
+ return Status;
+}
+
+VOID
+RecordPacketReceipt(
+ PIRP_CONTEXT IrpContext,
+ PVOID ReadData,
+ ULONG DataOffset,
+ USHORT ByteCount,
+ BOOLEAN CopyData
+ )
+/*++
+
+Routine Description:
+
+ This routine records the reciept of a burst read packet. It allocates
+ a burst read entry to record data start and length, and then inserts
+ the structure in order in the list of packets received for this burst.
+ It then copies the data to the user buffer.
+
+ BUGBUG - This routine could release the spin lock before doing the
+ data copy. Would this be useful?
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+ ReadData - A pointer to the data to copy.
+
+ DataOffset - The start offset of the data in the received packet.
+
+ ByteCount - The amount of data received.
+
+ CopyData - If FALSE, don't copy the data to the user's buffer. The
+ transport will do it.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PBURST_READ_ENTRY BurstReadEntry;
+ PBURST_READ_ENTRY ThisBurstReadEntry, NextBurstReadEntry;
+ PLIST_ENTRY ListEntry;
+#if NWDBG
+ BOOLEAN Insert = FALSE;
+#endif
+ USHORT ExtraBytes;
+
+ DebugTrace(0, Dbg, "RecordPacketReceipt\n", 0 );
+
+ IrpContext->Specific.Read.DataReceived = TRUE;
+
+ //
+ // Allocate and initialize a burst read entry.
+ //
+
+ BurstReadEntry = ALLOCATE_POOL( NonPagedPool, sizeof( BURST_READ_ENTRY ) );
+ if ( BurstReadEntry == NULL ) {
+ DebugTrace(0, Dbg, "Failed to allocate BurstReadEntry\n", 0 );
+ return;
+ }
+
+ //
+ // Insert this element in the ordered list of received packets.
+ //
+
+ if ( IsListEmpty( &IrpContext->Specific.Read.PacketList ) ) {
+
+#if NWDBG
+ Insert = TRUE;
+#endif
+
+ InsertHeadList(
+ &IrpContext->Specific.Read.PacketList,
+ &BurstReadEntry->ListEntry );
+
+ DebugTrace(0, Dbg, "First packet in the list\n", 0 );
+
+ } else {
+
+ //
+ // Walk the list of received packets, looking for the place to
+ // insert this entry. Walk the list backwards, since most of
+ // the time we will be appending to the list.
+ //
+
+ ListEntry = IrpContext->Specific.Read.PacketList.Blink;
+ ThisBurstReadEntry = NULL;
+
+ while ( ListEntry != &IrpContext->Specific.Read.PacketList ) {
+
+ NextBurstReadEntry = ThisBurstReadEntry;
+ ThisBurstReadEntry = CONTAINING_RECORD(
+ ListEntry,
+ BURST_READ_ENTRY,
+ ListEntry );
+
+ if ( ThisBurstReadEntry->DataOffset <= DataOffset ) {
+
+ //
+ // Found the place in the list to insert this entry.
+ //
+
+ if ( ThisBurstReadEntry->DataOffset +
+ ThisBurstReadEntry->ByteCount > DataOffset ) {
+
+ //
+ // The start of this packet contains data which
+ // overlaps data we have received. Chuck the extra
+ // data.
+ //
+
+ ExtraBytes = (USHORT)( ThisBurstReadEntry->DataOffset +
+ ThisBurstReadEntry->ByteCount - DataOffset );
+
+ if ( ExtraBytes < ByteCount ) {
+ DataOffset += ExtraBytes;
+ (PCHAR)ReadData += ExtraBytes;
+ ByteCount -= ExtraBytes;
+ } else {
+ ByteCount = 0;
+ }
+
+ }
+
+ if ( NextBurstReadEntry != NULL &&
+ DataOffset + ByteCount > NextBurstReadEntry->DataOffset ) {
+
+ //
+ // This packet contains some new data, but some of it
+ // overlaps the NextBurstReadEntry. Simply ignore
+ // the overlap by adjusting the byte count.
+ //
+ // If the packet is all overlap, toss it.
+ //
+
+ ByteCount = (USHORT)( NextBurstReadEntry->DataOffset - DataOffset );
+ }
+
+ if ( ByteCount == 0 ) {
+ FREE_POOL( BurstReadEntry );
+ return;
+ }
+#if NWDBG
+ Insert = TRUE;
+#endif
+ InsertHeadList( ListEntry, &BurstReadEntry->ListEntry );
+ break;
+
+ } else {
+
+ ListEntry = ListEntry->Blink;
+ }
+ }
+
+ //
+ // Couldn't find the place to insert
+ //
+
+ ASSERT( Insert );
+ }
+
+ BurstReadEntry->DataOffset = DataOffset;
+ BurstReadEntry->ByteCount = ByteCount;
+
+ //
+ // Copy the data to our read buffer.
+ //
+
+ if ( CopyData ) {
+ CopyBufferToMdl(
+ IrpContext->Specific.Read.FullMdl,
+ DataOffset,
+ ReadData,
+ ByteCount );
+ }
+
+ return;
+}
+
+#include <packon.h>
+
+typedef struct _MISSING_DATA_ENTRY {
+ ULONG DataOffset;
+ USHORT ByteCount;
+} MISSING_DATA_ENTRY, *PMISSING_DATA_ENTRY;
+
+#include <packoff.h>
+
+BOOLEAN
+VerifyBurstRead(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine verifies the set of response to a burst read request.
+ If some data is missing a missing packet request is sent.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+Return Value:
+
+ TRUE - All the data was received.
+
+ FALSE - Some data was missing.
+
+--*/
+{
+ ULONG CurrentOffset = 0;
+ PLIST_ENTRY ListEntry;
+ PBURST_READ_ENTRY BurstReadEntry;
+ USHORT MissingFragmentCount = 0;
+ USHORT ByteCount;
+ ULONG DataOffset;
+ MISSING_DATA_ENTRY UNALIGNED *MissingDataEntry;
+ KIRQL OldIrql;
+
+ DebugTrace(+1, Dbg, "VerifyBurstRead\n", 0 );
+
+ //
+ // Acquire the SCB spin lock to protect access to the list
+ // of received data for this read.
+ //
+
+ KeAcquireSpinLock(&IrpContext->pNpScb->NpScbSpinLock, &OldIrql);
+
+#ifdef NWDBG
+ //
+ // Verify that the list is in order.
+ //
+
+ ListEntry = IrpContext->Specific.Read.PacketList.Flink;
+
+ while ( ListEntry != &IrpContext->Specific.Read.PacketList ) {
+
+ BurstReadEntry = CONTAINING_RECORD( ListEntry, BURST_READ_ENTRY, ListEntry );
+ ASSERT ( BurstReadEntry->DataOffset >= CurrentOffset);
+ CurrentOffset = BurstReadEntry->DataOffset + BurstReadEntry->ByteCount;
+ ListEntry = ListEntry->Flink;
+ }
+
+ CurrentOffset = 0;
+
+#endif
+
+ ListEntry = IrpContext->Specific.Read.PacketList.Flink;
+
+ while ( ListEntry != &IrpContext->Specific.Read.PacketList ) {
+
+ BurstReadEntry = CONTAINING_RECORD( ListEntry, BURST_READ_ENTRY, ListEntry );
+ if ( BurstReadEntry->DataOffset != CurrentOffset) {
+
+ //
+ // There is a hole in the data, fill in a missing packet entry.
+ //
+
+ MissingDataEntry = (MISSING_DATA_ENTRY UNALIGNED *)
+ &IrpContext->req[ sizeof( NCP_BURST_HEADER ) +
+ MissingFragmentCount * sizeof( MISSING_DATA_ENTRY ) ];
+
+ DataOffset = CurrentOffset + SIZE_ADJUST( IrpContext );
+ LongByteSwap( MissingDataEntry->DataOffset, DataOffset );
+
+ ByteCount = (USHORT)( BurstReadEntry->DataOffset - CurrentOffset );
+ ShortByteSwap( MissingDataEntry->ByteCount, ByteCount );
+
+ ASSERT( BurstReadEntry->DataOffset - CurrentOffset <= IrpContext->pNpScb->MaxReceiveSize );
+
+ DebugTrace(0, Dbg, "Missing data at offset %ld\n", DataOffset );
+ DebugTrace(0, Dbg, "Missing %d bytes\n", ByteCount );
+ DebugTrace(0, Dbg, "CurrentOffset: %d\n", CurrentOffset );
+
+ MissingFragmentCount++;
+ }
+
+ CurrentOffset = BurstReadEntry->DataOffset + BurstReadEntry->ByteCount;
+ ListEntry = ListEntry->Flink;
+ }
+
+ //
+ // Any data missing off the end?
+ //
+
+ if ( CurrentOffset <
+ IrpContext->Specific.Read.BurstSize ) {
+
+ //
+ // There is a hole in the data, fill in a missing packet entry.
+ //
+
+ MissingDataEntry = (PMISSING_DATA_ENTRY)
+ &IrpContext->req[ sizeof( NCP_BURST_HEADER ) +
+ MissingFragmentCount * sizeof( MISSING_DATA_ENTRY ) ];
+
+ DataOffset = CurrentOffset + SIZE_ADJUST( IrpContext );
+ LongByteSwap( MissingDataEntry->DataOffset, DataOffset );
+
+ ByteCount = (USHORT)( IrpContext->Specific.Read.BurstSize - CurrentOffset );
+ ShortByteSwap( MissingDataEntry->ByteCount, ByteCount );
+
+ ASSERT( IrpContext->Specific.Read.BurstSize - CurrentOffset < IrpContext->pNpScb->MaxReceiveSize );
+
+ DebugTrace(0, Dbg, "Missing data at offset %ld\n", MissingDataEntry->DataOffset );
+ DebugTrace(0, Dbg, "Missing %d bytes\n", MissingDataEntry->ByteCount );
+
+ MissingFragmentCount++;
+ }
+
+
+ if ( MissingFragmentCount == 0 ) {
+
+ //
+ // This read is now complete. Don't process any more packets until
+ // the next packet is sent.
+ //
+
+ IrpContext->pNpScb->OkToReceive = FALSE;
+
+ KeReleaseSpinLock(&IrpContext->pNpScb->NpScbSpinLock, OldIrql);
+
+ DebugTrace(-1, Dbg, "VerifyBurstRead -> TRUE\n", 0 );
+
+ return( TRUE );
+
+ } else {
+
+ KeReleaseSpinLock(&IrpContext->pNpScb->NpScbSpinLock, OldIrql);
+
+ //
+ // The server dropped a packet, adjust the timers.
+ //
+
+ NwProcessReceiveBurstFailure( IrpContext->pNpScb, MissingFragmentCount );
+
+ //
+ // Request the missing data.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_BURST_PACKET );
+
+ //
+ // Update burst request offset since we are about to request
+ // more data. Note that this will reset the retry count,
+ // thus giving the server full timeout time to return the
+ // missing data.
+ //
+
+ BuildRequestPacket(
+ IrpContext,
+ BurstReadCallback,
+ "Bws",
+ 0, // Frame size for this request is 0
+ 0, // Offset of data
+ BURST_FLAG_SYSTEM_PACKET,
+ MissingFragmentCount,
+ MissingFragmentCount * sizeof( MISSING_DATA_ENTRY )
+ );
+
+ PrepareAndSendPacket( IrpContext );
+
+ Stats.PacketBurstReadTimeouts++;
+
+ DebugTrace(-1, Dbg, "VerifyBurstRead -> FALSE\n", 0 );
+ return( FALSE );
+ }
+}
+
+
+VOID
+FreePacketList(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine frees the received packet list for a burst read.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PLIST_ENTRY ListHead;
+ PBURST_READ_ENTRY BurstReadEntry;
+
+ ListHead = &IrpContext->Specific.Read.PacketList;
+ while ( !IsListEmpty( ListHead ) ) {
+ BurstReadEntry = CONTAINING_RECORD( ListHead->Flink, BURST_READ_ENTRY, ListEntry );
+ RemoveHeadList( ListHead );
+ FREE_POOL( BurstReadEntry );
+ }
+}
+
+NTSTATUS
+BurstReadReceive(
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PULONG BytesAccepted,
+ IN PUCHAR Response,
+ PMDL *pReceiveMdl
+ )
+/*++
+
+Routine Description:
+
+ This routine builds an MDL to receive burst read data. This routine
+ is called at data indication time.
+
+ This routine is called with the non paged SCB spin lock held.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+ BytesAvailable - The number of bytes in the entire packet.
+
+ BytesAccepted - Returns the number of bytes accepted from the packet.
+
+ Response - A pointer to the indication buffer.
+
+Return Value:
+
+ Mdl - An MDL to receive the data.
+ This routine raise an exception if it cannot receive the data.
+
+--*/
+{
+ NTSTATUS Status;
+ ULONG DataOffset;
+ ULONG TotalBytesRead;
+ PUCHAR ReadData;
+ USHORT BytesThisPacket;
+ UCHAR Flags;
+ PMDL PartialMdl;
+
+ DebugTrace(0, Dbg, "Burst read receive\n", 0);
+
+ Status = ParseBurstReadResponse(
+ IrpContext,
+ Response,
+ BytesAvailable,
+ &Flags,
+ &DataOffset,
+ &BytesThisPacket,
+ &ReadData,
+ &TotalBytesRead );
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ DebugTrace(0, Dbg, "Failed to parse burst read response\n", 0);
+ return Status;
+ }
+
+ //
+ // We can accept up to the size of a burst read header, plus
+ // 3 bytes of fluff for the unaligned read case.
+ //
+
+ *BytesAccepted = ReadData - Response;
+ ASSERT( *BytesAccepted <= sizeof(NCP_BURST_READ_RESPONSE) + 3 );
+
+ RecordPacketReceipt( IrpContext, ReadData, DataOffset, BytesThisPacket, FALSE );
+
+ IrpContext->Specific.Read.Flags = Flags;
+
+ //
+ // If we did a read at EOF the netware server will return 0 bytes read,
+ // no error.
+ //
+
+ ASSERT( IrpContext->Specific.Read.FullMdl != NULL );
+
+ if ( BytesThisPacket > 0 ) {
+
+ PartialMdl = AllocateReceivePartialMdl(
+ IrpContext->Specific.Read.FullMdl,
+ DataOffset,
+ BytesThisPacket );
+
+ if ( !PartialMdl ) {
+ IrpContext->Specific.Read.Status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ // Record Mdl to free when CopyIndicatedData or Irp completed.
+ IrpContext->Specific.Read.PartialMdl = PartialMdl;
+
+ } else {
+
+ PartialMdl = NULL;
+
+ }
+
+ *pReceiveMdl = PartialMdl;
+ return( STATUS_SUCCESS );
+}
+
+NTSTATUS
+ParseBurstReadResponse(
+ IN PIRP_CONTEXT IrpContext,
+ PUCHAR Response,
+ ULONG BytesAvailable,
+ PUCHAR Flags,
+ PULONG DataOffset,
+ PUSHORT BytesThisPacket,
+ PUCHAR *ReadData,
+ PULONG TotalBytesRead
+ )
+/*++
+
+Routine Description:
+
+ This routine parses a burst read response.
+
+ This routine must be called the the nonpagd SCB spinlock held.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+ Response - A pointer to the response buffer.
+
+ BytesAvailable - The number of bytes in the packet.
+
+ Flags - Returns the Burst Flags
+
+ DataOffset - Returns the data offset (within the burst) of the
+ data in this packet.
+
+ BytesThisPacket - Returns the number of file data bytes in this packet.
+
+ ReadData - Returns a pointer to the start of the file data in the
+ packet buffer.
+
+ TotalBytesRead - Returns the number of byte of file data in the
+ entire burst.
+
+
+Return Value:
+
+ The status of the read.
+
+--*/
+{
+ NTSTATUS Status;
+ ULONG Result;
+ PNCP_BURST_READ_RESPONSE ReadResponse;
+
+ DebugTrace(+1, Dbg, "ParseBurstReadResponse\n", 0);
+
+ ReadResponse = (PNCP_BURST_READ_RESPONSE)Response;
+ *Flags = ReadResponse->BurstHeader.Flags;
+
+#ifdef NWDBG
+ //
+ // Bad net simulator.
+ //
+
+ if ( DropReadPackets != 0 ) {
+ if ( ( rand() % DropReadPackets ) == 0 ) {
+
+ IrpContext->pNpScb->OkToReceive = TRUE;
+ DebugTrace( 0, Dbg, "Dropping packet\n", 0 );
+ DebugTrace( -1, Dbg, "ParseBurstReadResponse -> %X\n", STATUS_UNSUCCESSFUL );
+ return ( STATUS_UNSUCCESSFUL );
+ }
+ }
+
+#endif
+
+ //
+ // If this isn't the last packet, setup for the next burst packet.
+ //
+
+ if ( !FlagOn( *Flags, BURST_FLAG_END_OF_BURST ) ) {
+
+ DebugTrace(0, Dbg, "Waiting for another packet\n", 0);
+
+ //
+ // Once we receive the first packet in a read response be aggresive
+ // about timing out while waiting for the rest of the burst.
+ //
+
+ IrpContext->pNpScb->TimeOut = IrpContext->pNpScb->SendTimeout ;
+
+ IrpContext->pNpScb->OkToReceive = TRUE;
+ }
+
+
+ LongByteSwap( *DataOffset, ReadResponse->BurstHeader.BurstOffset );
+ ShortByteSwap( *BytesThisPacket, ReadResponse->BurstHeader.BurstLength );
+
+ //
+ // How much data was received?
+ //
+
+ if ( IsListEmpty( &IrpContext->Specific.Read.PacketList ) ) {
+
+ DebugTrace(0, Dbg, "Expecting initial response\n", 0);
+
+ //
+ // This is the initial burst response packet.
+ //
+
+ if ( *DataOffset != 0 ) {
+
+ DebugTrace(0, Dbg, "Invalid initial response tossed\n", 0);
+
+ //
+ // This is actually a subsequent response. Toss it.
+ // BUGBUG - Can we handle it?
+ //
+
+ DebugTrace( -1, Dbg, "ParseBurstReadResponse -> %X\n", STATUS_UNSUCCESSFUL );
+ IrpContext->pNpScb->OkToReceive = TRUE;
+
+ return ( STATUS_UNSUCCESSFUL );
+ }
+
+ Result = ReadResponse->Result;
+ LongByteSwap( *TotalBytesRead, ReadResponse->BytesRead );
+
+ Status = NwBurstResultToNtStatus( Result );
+ IrpContext->Specific.Read.Status = Status;
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ //
+ // Update the burst request number now.
+ //
+
+ DebugTrace(0, Dbg, "Read completed, error = %X\n", Status );
+
+ ClearFlag( IrpContext->Flags, IRP_FLAG_BURST_REQUEST );
+ NwSetIrpContextEvent( IrpContext );
+
+ DebugTrace( -1, Dbg, "ParseBurstReadResponse -> %X\n", Status );
+ return( Status );
+ }
+
+ if ( Result == 3 || *BytesThisPacket < 8 ) { // No data
+ *TotalBytesRead = 0;
+ *BytesThisPacket = 8;
+ }
+
+ *ReadData = Response + sizeof(NCP_BURST_READ_RESPONSE);
+
+ IrpContext->Specific.Read.BurstSize = *TotalBytesRead;
+
+ //
+ // Bytes this packet includes a LONG status and a LONG byte total.
+ // Adjust the count to reflect the number of data bytes actually
+ // shipped.
+ //
+
+ *BytesThisPacket -= sizeof( ULONG ) + sizeof( ULONG );
+
+ //
+ // Adjust this data if the read was not DWORD aligned.
+ //
+
+ if ( (IrpContext->Specific.Read.FileOffset & 0x03) != 0
+ && *BytesThisPacket != 0 ) {
+
+ *ReadData += IrpContext->Specific.Read.FileOffset & 0x03;
+ *BytesThisPacket -= (USHORT)IrpContext->Specific.Read.FileOffset & 0x03;
+ }
+
+ DebugTrace(0, Dbg, "Initial response\n", 0);
+ DebugTrace(0, Dbg, "Result = %ld\n", Result);
+ DebugTrace(0, Dbg, "Total bytes read = %ld\n", *TotalBytesRead );
+
+ } else {
+
+ //
+ // Intermediate response packet.
+ //
+
+ *ReadData = Response + sizeof( NCP_BURST_HEADER );
+ *DataOffset -= SIZE_ADJUST( IrpContext );
+
+ }
+
+ DebugTrace(0, Dbg, "DataOffset = %ld\n", *DataOffset );
+ DebugTrace(0, Dbg, "# bytes received = %d\n", *BytesThisPacket );
+
+ if ( *DataOffset > IrpContext->Specific.Read.BurstSize ||
+ *DataOffset + *BytesThisPacket > IrpContext->Specific.Read.BurstSize ) {
+
+ DebugTrace(0, Dbg, "Invalid response tossed\n", 0);
+
+ DebugTrace( -1, Dbg, "ParseBurstReadResponse -> %X\n", STATUS_SUCCESS );
+ IrpContext->pNpScb->OkToReceive = TRUE;
+ return ( STATUS_UNSUCCESSFUL );
+ }
+
+ DebugTrace( -1, Dbg, "ParseBurstReadResponse -> %X\n", STATUS_SUCCESS );
+ return( STATUS_SUCCESS );
+}
+
+
+PMDL
+AllocateReceivePartialMdl(
+ PMDL FullMdl,
+ ULONG DataOffset,
+ ULONG BytesThisPacket
+ )
+/*++
+
+Routine Description:
+
+ This routine allocates a partial MDL to receive read data. This
+ routine is called at receive indication time.
+
+Arguments:
+
+ FullMdl - The FullMdl for the buffer.
+
+ DataOffset - The offset into the buffer where the data is to be received.
+
+ BytesThisPacket - The number of data bytes to be received into the buffer.
+
+Return Value:
+
+ MDL - A pointer to an MDL to receive the data
+ This routine raises an exception if it cannot allocate an MDL.
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PUCHAR BufferStart, BufferEnd;
+ PMDL InitialMdl, NextMdl;
+ PMDL ReceiveMdl, PreviousReceiveMdl;
+ ULONG BytesThisMdl;
+
+ BufferStart = (PUCHAR)MmGetMdlVirtualAddress( FullMdl ) + DataOffset;
+ BufferEnd = (PUCHAR)MmGetMdlVirtualAddress( FullMdl ) +
+ MmGetMdlByteCount( FullMdl );
+
+ //
+ // Walk the MDL chain look for the MDL for the actual buffer for the
+ // start of this data.
+ //
+
+ while ( BufferStart >= BufferEnd ) {
+ DataOffset -= MmGetMdlByteCount( FullMdl );
+ FullMdl = FullMdl->Next;
+
+ //
+ // if more data than expected, dont dereference NULL! see next loop.
+ //
+ if (!FullMdl) {
+ ASSERT(FALSE) ;
+ break ;
+ }
+
+ BufferStart = (PUCHAR)MmGetMdlVirtualAddress( FullMdl ) + DataOffset;
+ BufferEnd = (PUCHAR)MmGetMdlVirtualAddress( FullMdl ) +
+ MmGetMdlByteCount( FullMdl );
+ }
+
+ PreviousReceiveMdl = NULL;
+ InitialMdl = NULL;
+ BytesThisMdl = (ULONG)(BufferEnd - BufferStart);
+
+ //
+ // Check FullMdl to cover the case where the server returns more data
+ // than requested.
+ //
+
+ while (( BytesThisPacket != 0 ) &&
+ ( FullMdl != NULL )) {
+
+ BytesThisMdl = MIN( BytesThisMdl, BytesThisPacket );
+
+ //
+ // Some of the data fits in the first part of the MDL;
+ //
+
+ ReceiveMdl = ALLOCATE_MDL(
+ BufferStart,
+ BytesThisMdl,
+ FALSE,
+ FALSE,
+ NULL );
+
+ if ( ReceiveMdl == NULL ) {
+
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ break;
+ }
+
+ if ( InitialMdl == NULL ) {
+ InitialMdl = ReceiveMdl;
+ }
+
+ IoBuildPartialMdl(
+ FullMdl,
+ ReceiveMdl,
+ BufferStart,
+ BytesThisMdl );
+
+ if ( PreviousReceiveMdl != NULL ) {
+ PreviousReceiveMdl->Next = ReceiveMdl;
+ }
+
+ PreviousReceiveMdl = ReceiveMdl;
+
+ BytesThisPacket -= BytesThisMdl;
+
+ FullMdl = FullMdl->Next;
+
+ if ( FullMdl != NULL) {
+ BytesThisMdl = MmGetMdlByteCount( FullMdl );
+ BufferStart = MmGetMdlVirtualAddress( FullMdl );
+ }
+
+ }
+
+ if ( Status == STATUS_INSUFFICIENT_RESOURCES ) {
+
+ //
+ // Cleanup allocated MDLs
+ //
+
+ while ( InitialMdl != NULL ) {
+ NextMdl = InitialMdl->Next;
+ FREE_MDL( InitialMdl );
+ InitialMdl = NextMdl;
+ }
+
+ DebugTrace( 0, Dbg, "AllocateReceivePartialMdl Failed\n", 0 );
+ }
+
+ DebugTrace( 0, Dbg, "AllocateReceivePartialMdl -> %08lX\n", InitialMdl );
+ return( InitialMdl );
+}
+
+
+VOID
+SetConnectionTimeout(
+ PNONPAGED_SCB pNpScb,
+ ULONG Length
+ )
+/*++
+
+Routine Description:
+
+
+ The server will pause NwReceiveDelay between packets. Make sure we have our timeout
+ so that we will take that into account.
+
+Arguments:
+
+ pNpScb - Connection
+
+ Length - Length of the burst in bytes
+
+Return Value:
+
+ None.
+
+--*/
+{
+
+ ULONG TimeInNwUnits;
+ LONG SingleTimeInNwUnits;
+
+ SingleTimeInNwUnits = pNpScb->NwSingleBurstPacketTime + pNpScb->NwReceiveDelay;
+
+ TimeInNwUnits = SingleTimeInNwUnits * ((Length / pNpScb->MaxPacketSize) + 1) +
+ pNpScb->NwLoopTime;
+
+ //
+ // Convert to 1/18ths of a second ticks and multiply by a fudge
+ // factor. The fudge factor is expressed as a percentage. 100 will
+ // mean no fudge.
+ //
+
+ pNpScb->MaxTimeOut = (SHORT)( ((TimeInNwUnits / 555) *
+ (ULONG)ReadTimeoutMultiplier) / 100 + 1);
+
+ //
+ // Now make sure we have a meaningful lower and upper limit.
+ //
+ if (pNpScb->MaxTimeOut < 2)
+ {
+ pNpScb->MaxTimeOut = 2 ;
+ }
+
+ if (pNpScb->MaxTimeOut > (SHORT)MaxReadTimeout)
+ {
+ pNpScb->MaxTimeOut = (SHORT)MaxReadTimeout ;
+ }
+
+ pNpScb->TimeOut = pNpScb->SendTimeout = pNpScb->MaxTimeOut;
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->MaxTimeout = %08lx\n", pNpScb->MaxTimeOut );
+}
+
+#if NWFASTIO
+
+BOOLEAN
+NwFastRead (
+ IN PFILE_OBJECT FileObject,
+ IN PLARGE_INTEGER FileOffset,
+ IN ULONG Length,
+ IN BOOLEAN Wait,
+ IN ULONG LockKey,
+ OUT PVOID Buffer,
+ OUT PIO_STATUS_BLOCK IoStatus,
+ IN PDEVICE_OBJECT DeviceObject
+ )
+/*++
+
+Routine Description:
+
+ This routine does a fast cached read bypassing the usual file system
+ entry routine (i.e., without the Irp). It is used to do a copy read
+ of a cached file object. For a complete description of the arguments
+ see CcCopyRead.
+
+Arguments:
+
+ FileObject - Pointer to the file object being read.
+
+ FileOffset - Byte offset in file for desired data.
+
+ Length - Length of desired data in bytes.
+
+ Wait - FALSE if caller may not block, TRUE otherwise
+
+ Buffer - Pointer to output buffer to which data should be copied.
+
+ IoStatus - Pointer to standard I/O status block to receive the status
+ for the transfer.
+
+Return Value:
+
+ FALSE - if Wait was supplied as FALSE and the data was not delivered, or
+ if there is an I/O error.
+
+ TRUE - if the data is being delivered
+
+--*/
+
+{
+ NODE_TYPE_CODE nodeTypeCode;
+ PICB icb;
+ PFCB fcb;
+ PVOID fsContext;
+ ULONG bytesRead;
+ ULONG offset;
+
+ DebugTrace(+1, Dbg, "NwFastRead...\n", 0);
+
+ //
+ // Special case a read of zero length
+ //
+
+ if (Length == 0) {
+
+ //
+ // A zero length transfer was requested.
+ //
+
+ IoStatus->Status = STATUS_SUCCESS;
+ IoStatus->Information = 0;
+
+ DebugTrace(+1, Dbg, "NwFastRead -> TRUE\n", 0);
+ return TRUE;
+ }
+
+ //
+ // Decode the file object to figure out who we are. If the result
+ // is not FCB then its an illegal parameter.
+ //
+
+ if ((nodeTypeCode = NwDecodeFileObject( FileObject,
+ &fsContext,
+ (PVOID *)&icb )) != NW_NTC_ICB) {
+
+ DebugTrace(0, Dbg, "Not a file\n", 0);
+ DebugTrace(-1, Dbg, "NwFastRead -> FALSE\n", 0);
+ return FALSE;
+ }
+
+ fcb = (PFCB)icb->SuperType.Fcb;
+ nodeTypeCode = fcb->NodeTypeCode;
+ offset = FileOffset->LowPart;
+
+ bytesRead = CacheRead(
+ fcb->NonPagedFcb,
+ offset,
+ Length,
+ Buffer,
+ TRUE );
+
+ if ( bytesRead != 0 ) {
+
+ ASSERT( bytesRead == Length );
+ IoStatus->Status = STATUS_SUCCESS;
+ IoStatus->Information = bytesRead;
+#ifndef NT1057
+ FileObject->CurrentByteOffset.QuadPart += Length;
+#endif
+ DebugTrace(-1, Dbg, "NwFastRead -> TRUE\n", 0);
+ return( TRUE );
+
+ } else {
+
+ DebugTrace(-1, Dbg, "NwFastRead -> FALSE\n", 0);
+ return( FALSE );
+
+ }
+}
+#endif
diff --git a/private/nw/rdr/scavengr.c b/private/nw/rdr/scavengr.c
new file mode 100644
index 000000000..ac69a9ab8
--- /dev/null
+++ b/private/nw/rdr/scavengr.c
@@ -0,0 +1,689 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ Scavengr.c
+
+Abstract:
+
+ This module implements the Netware Redirector scavenger thread.
+
+Author:
+
+ Manny Weiser [MannyW] 15-Feb-1993
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_SCAVENGER)
+
+extern BOOLEAN WorkerRunning; // From timer.c
+
+#ifdef NWDBG
+DWORD DumpIcbFlag = 0 ;
+#endif
+
+VOID
+CleanupVcbs(
+ LARGE_INTEGER Now
+ );
+
+#ifdef ALLOC_PRAGMA
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, NwAllocateExtraIrpContext )
+#pragma alloc_text( PAGE1, NwFreeExtraIrpContext )
+#pragma alloc_text( PAGE1, CleanupVcbs )
+#pragma alloc_text( PAGE1, CleanupScbs )
+#pragma alloc_text( PAGE1, DisconnectTimedOutScbs )
+#endif
+
+#endif
+
+//
+// Not pageable:
+//
+// NwScavengerRoutine - Acquires a spin lock.
+//
+
+VOID
+NwScavengerRoutine(
+ IN PWORK_QUEUE_ITEM WorkItem
+ )
+/*++
+
+Routine Description:
+
+ This routine implements the scavenger. The scavenger runs
+ periodically in the context of an executive worker thread to
+ do background cleanup operations on redirector data.
+
+Arguments:
+
+ WorkItem - The work item for this routine.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LARGE_INTEGER Now;
+ PMDL LineChangeMdl;
+ PWORK_QUEUE_ITEM LineChangeWorkItem;
+ KIRQL OldIrql;
+
+ PAGED_CODE();
+
+
+ DebugTrace(+1, Dbg, "NwScavengerRoutine\n", 0);
+
+ KeQuerySystemTime( &Now );
+
+#ifdef NWDBG
+ if (DumpIcbFlag != 0)
+ DumpIcbs();
+#endif
+
+ //
+ // Try to free unused VCBs.
+ //
+
+ CleanupVcbs(Now);
+
+ //
+ // Try disconnect from SCBs that are timed out.
+ //
+
+ DisconnectTimedOutScbs(Now) ;
+
+ //
+ // Try to free unused SCBs.
+ //
+
+ CleanupScbs(Now);
+
+ //
+ // Flag we're finished now to avoid deadlock in stop timer.
+ //
+
+ KeAcquireSpinLock( &NwScavengerSpinLock, &OldIrql );
+
+ if ( DelayedProcessLineChange ) {
+
+ DebugTrace( 0, Dbg, "Scavenger processing a delayed line change notification.\n", 0 );
+
+ LineChangeMdl = DelayedLineChangeIrp->MdlAddress;
+ LineChangeWorkItem = ALLOCATE_POOL( NonPagedPool, sizeof( WORK_QUEUE_ITEM ) );
+
+ if ( LineChangeWorkItem == NULL ) {
+
+ //
+ // If we couldn't get a work queue item, just blow
+ // it all off for now.
+ //
+
+ FREE_POOL( LineChangeMdl->MappedSystemVa );
+ FREE_MDL( LineChangeMdl );
+ FREE_IRP( DelayedLineChangeIrp );
+
+ DelayedLineChangeIrp = NULL;
+ DelayedProcessLineChange = FALSE;
+ WorkerRunning = FALSE;
+
+ KeReleaseSpinLock( &NwScavengerSpinLock, OldIrql );
+
+ } else {
+
+ //
+ // Leave WorkRunning set to TRUE so that the scavenger can't run
+ // while the process line change is running, but clear the line
+ // change flag. The FspProcessLineChange function will clear the
+ // WorkerRunning flag.
+ //
+
+ DelayedProcessLineChange = FALSE;
+ KeReleaseSpinLock( &NwScavengerSpinLock, OldIrql );
+
+ //
+ // Use the user buffer field as a convenient place to remember where
+ // the address of the WorkQueueItem. We can get away with this since
+ // we don't let this IRP complete.
+ //
+
+ DelayedLineChangeIrp->UserBuffer = LineChangeWorkItem;
+
+ //
+ // Process the line change in the FSP.
+ //
+
+ ExInitializeWorkItem( LineChangeWorkItem, FspProcessLineChange, DelayedLineChangeIrp );
+ ExQueueWorkItem( LineChangeWorkItem, DelayedWorkQueue );
+
+ }
+
+ } else {
+
+ //
+ // No line change happened while the scavenger was running.
+ //
+
+ WorkerRunning = FALSE;
+ KeReleaseSpinLock( &NwScavengerSpinLock, OldIrql );
+
+ }
+
+ //
+ // Unlock discardable code, if we are inactive. Don't block
+ // if can't get resource.
+ //
+
+ NwUnlockCodeSections(FALSE);
+
+
+ DebugTrace(-1, Dbg, "NwScavengerRoutine\n", 0);
+ return;
+}
+
+
+VOID
+CleanupScbs(
+ LARGE_INTEGER Now
+ )
+/*++
+
+Routine Description:
+
+ This routine tries to free unused VCB structures.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ KIRQL OldIrql;
+ PLIST_ENTRY ScbQueueEntry;
+ PNONPAGED_SCB pNpScb;
+ PLIST_ENTRY NextScbQueueEntry;
+ PSCB pScb;
+ LIST_ENTRY DyingScbs;
+ LARGE_INTEGER KillTime ;
+
+ DebugTrace(+1, Dbg, "CleanupScbs\n", 0);
+
+ //
+ // Calculate KillTime = Now - 2 minutes.
+ //
+
+ InitializeListHead( &DyingScbs );
+
+ KillTime.QuadPart = Now.QuadPart - ( NwOneSecond * DORMANT_SCB_KEEP_TIME );
+
+ //
+ // Scan through the SCBs holding the RCB.
+ //
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ //
+ // find all SCBs that are no longer usable and put them on the dying list.
+ // we will take a second pass thru to remove timed out ones, based on
+ // what is left.
+ //
+
+ for (ScbQueueEntry = ScbQueue.Flink ;
+ ScbQueueEntry != &ScbQueue ;
+ ScbQueueEntry = NextScbQueueEntry )
+ {
+
+ pNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
+ NextScbQueueEntry = pNpScb->ScbLinks.Flink;
+
+ if ( ( pNpScb->Reference == 0 ) &&
+ ( ( pNpScb->LastUsedTime.QuadPart < KillTime.QuadPart ) ||
+ ( pNpScb->State == SCB_STATE_FLAG_SHUTDOWN ) ) )
+ {
+ DebugTrace( 0, Dbg,
+ "Moving SCB %08lx to dead list\n", pNpScb);
+
+ //
+ // The SCB has no references and is not logged in nor attached.
+ //
+
+ RemoveEntryList( &pNpScb->ScbLinks );
+ InsertHeadList( &DyingScbs, &pNpScb->ScbLinks );
+ }
+
+#ifdef MSWDBG
+ //
+ // Look for blocked connections. If there's something
+ // queued for this server yet nothing was added or removed
+ // since the last time the scavenger ran then stop
+ //
+
+ if ((!IsListEmpty( &pNpScb->Requests ) ) &&
+ (pNpScb->RequestQueued == FALSE) &&
+ (pNpScb->RequestDequeued == FALSE )) {
+
+ DebugTrace( 0, Dbg, "Server %08lx seems to be locked up!\n", pNpScb );
+ ASSERT( FALSE );
+
+ } else {
+
+ pNpScb->RequestQueued = FALSE;
+ pNpScb->RequestDequeued = FALSE;
+
+ }
+#endif
+ }
+
+ //
+ // Now that the dying SCBs are off the ScbQueue we can release
+ // the SCB spin lock.
+ //
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+ //
+ // Walk the list of Dying SCBs and kill them off. Note that we are
+ // still holding the RCB.
+ //
+
+ while ( !IsListEmpty( &DyingScbs ) ) {
+
+ pNpScb = CONTAINING_RECORD( DyingScbs.Flink, NONPAGED_SCB, ScbLinks );
+ pScb = pNpScb->pScb;
+
+ RemoveHeadList( &DyingScbs );
+ NwDeleteScb( pScb );
+ }
+
+ NwReleaseRcb( &NwRcb );
+
+ DebugTrace(-1, Dbg, "CleanupScbs\n", 0);
+
+}
+
+VOID
+CleanupVcbs(
+ LARGE_INTEGER Now
+ )
+/*++
+
+Routine Description:
+
+ This routine tries to free unused VCB structures.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ KIRQL OldIrql;
+ PLIST_ENTRY ScbQueueEntry;
+ PLIST_ENTRY VcbQueueEntry;
+ PLIST_ENTRY NextVcbQueueEntry;
+ PNONPAGED_SCB pNpScb;
+ PSCB pScb;
+ PVCB pVcb;
+ LARGE_INTEGER KillTime;
+
+ NTSTATUS Status;
+ PIRP_CONTEXT IrpContext = NULL;
+ BOOLEAN VcbDeleted;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "CleanupVcbs...\n", 0 );
+
+ //
+ // Calculate KillTime = Now - 5 minutes.
+ //
+
+ KillTime.QuadPart = Now.QuadPart - ( NwOneSecond * DORMANT_VCB_KEEP_TIME );
+
+ //
+ // Scan through the SCBs.
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ ScbQueueEntry = ScbQueue.Flink;
+
+ while ( ScbQueueEntry != &ScbQueue ) {
+
+ pNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
+
+ //
+ // Reference the SCB so that it won't go away when we release
+ // the SCB spin lock.
+ //
+
+ NwReferenceScb( pNpScb );
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+ pScb = pNpScb->pScb;
+
+ if ( pScb == NULL) {
+
+ //
+ // This must be the permanent SCB. Just skip it.
+ //
+
+ ASSERT( pNpScb == &NwPermanentNpScb );
+
+ } else {
+
+ //
+ // Get an irp context and get to the head of the queue.
+ //
+
+ if ( NwAllocateExtraIrpContext( &IrpContext, pNpScb ) ) {
+
+ IrpContext->pNpScb = pNpScb;
+ IrpContext->pScb = pNpScb->pScb;
+ NwAppendToQueueAndWait( IrpContext );
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ VcbDeleted = TRUE;
+
+ //
+ // NwCleanupVcb releases the RCB, but we can't be guaranteed
+ // the state of the Vcb list when we release the RCB.
+ //
+ // If we need to cleanup a VCB, release the lock, and start
+ // processing the list again.
+ //
+
+ while ( VcbDeleted ) {
+
+ VcbDeleted = FALSE;
+
+ for ( VcbQueueEntry = pScb->ScbSpecificVcbQueue.Flink ;
+ VcbQueueEntry != &pScb->ScbSpecificVcbQueue;
+ VcbQueueEntry = NextVcbQueueEntry ) {
+
+ pVcb = CONTAINING_RECORD( VcbQueueEntry, VCB, VcbListEntry );
+ NextVcbQueueEntry = VcbQueueEntry->Flink;
+
+ //
+ // The VCB has no references, and hasn't been used for
+ // a long time. Kill it.
+ //
+
+ if ( pVcb->Reference == 0 ) {
+
+ Status = STATUS_SUCCESS;
+
+ DebugTrace(0, Dbg, "Cleaning up VCB %08lx\n", pVcb );
+ DebugTrace(0, Dbg, "VCB name = %wZ\n", &pVcb->Name );
+
+ // Lock down so that we can send a packet.
+ NwReferenceUnlockableCodeSection();
+
+ NwCleanupVcb( pVcb, IrpContext );
+
+ NwDereferenceUnlockableCodeSection ();
+
+ //
+ // Get back to the head of the queue, re-acquire
+ // the VCB, and restart the processing of this list.
+ //
+
+ NwAppendToQueueAndWait( IrpContext );
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ VcbDeleted = TRUE;
+
+ break;
+ }
+
+ } // for
+
+ } // while
+
+ } else {
+
+ IrpContext = NULL;
+ DebugTrace( 0, Dbg, "Couldn't cleanup SCB: %08lx\n", pNpScb );
+
+ }
+
+ NwReleaseRcb( &NwRcb );
+
+ }
+
+ //
+ // Free the irp context allocated for this SCB.
+ //
+
+ if ( IrpContext != NULL ) {
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwFreeExtraIrpContext( IrpContext );
+ IrpContext = NULL;
+ }
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+ ScbQueueEntry = pNpScb->ScbLinks.Flink;
+ NwDereferenceScb( pNpScb );
+ }
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+ DebugTrace(-1, Dbg, "CleanupVcbs -> VOID\n", 0 );
+}
+
+
+VOID
+DisconnectTimedOutScbs(
+ LARGE_INTEGER Now
+ )
+/*++
+
+Routine Description:
+
+ This routine disconnects any timed out SCBs before they get
+ nuked by CleanupScbs() which does not disconnect.
+
+ NOTE: The SCB's are destroyed on a timeout for a couple of
+ reasons. The first is because if we used a reference count then
+ normal use of UNCs would cause us to be continually reconnecting.
+ Another is in FindNearestServer where its useful to collect the
+ Near servers that are out of connections so we can avoid them when
+ we iterate through the 5 nearest servers and we escalate to General
+ SAP response.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ KIRQL OldIrql;
+ PLIST_ENTRY ScbQueueEntry;
+ PNONPAGED_SCB pNpScb;
+ LARGE_INTEGER KillTime ;
+
+ PIRP_CONTEXT IrpContext = NULL;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "DisconnectTimedOutScbs...\n", 0 );
+
+ //
+ // Calculate KillTime = Now - 5 minutes.
+ //
+
+ KillTime.QuadPart = Now.QuadPart - ( NwOneSecond * DORMANT_SCB_KEEP_TIME );
+
+ //
+ // Scan through the SCBs.
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ ScbQueueEntry = ScbQueue.Flink;
+
+ while ( ScbQueueEntry != &ScbQueue )
+ {
+
+ pNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
+
+
+ if ( (pNpScb != &NwPermanentNpScb) &&
+ (pNpScb->Reference == 0 ) &&
+ (pNpScb->LastUsedTime.QuadPart < KillTime.QuadPart) )
+ {
+ //
+ // Reference the SCB so that it won't go away when we release
+ // the SCB spin lock.
+ //
+
+ NwReferenceScb( pNpScb );
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+ //
+ // Not the permanent SCB and the reference count is the one
+ // we just added, So this is really at zero & has not been used
+ // for a while. Note we only allocate the IrpContext once.
+ //
+ if ( IrpContext ||
+ NwAllocateExtraIrpContext( &IrpContext, pNpScb ) )
+ {
+
+ IrpContext->pNpScb = pNpScb;
+
+ // Lock down so that we can send a packet.
+ NwReferenceUnlockableCodeSection();
+
+ //
+ // get to front of queue and recheck to make sure we are
+ // still with a ref count of 1.
+ //
+ NwAppendToQueueAndWait( IrpContext );
+
+ if (pNpScb->Reference == 1)
+ {
+ //
+ // make sure we do not reconnect.
+ //
+ ClearFlag( IrpContext->Flags, IRP_FLAG_RECONNECTABLE );
+
+ //
+ // This will result in a logoff and/or disconnect as
+ // need.
+ //
+ NwLogoffAndDisconnect(IrpContext, pNpScb) ;
+ }
+
+ NwDequeueIrpContext(IrpContext, FALSE) ;
+
+ NwDereferenceUnlockableCodeSection ();
+
+
+ }
+ else
+ {
+ //
+ // Could not allocate IrpContext. Oh well, we'll just leave
+ // this connection for the watch dog.
+ //
+ }
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+ NwDereferenceScb( pNpScb );
+ }
+ else
+ {
+ //
+ // not timed out or is permanent SCB. dont disconnect.
+ //
+ }
+
+ ScbQueueEntry = pNpScb->ScbLinks.Flink;
+ }
+
+ if ( IrpContext )
+ NwFreeExtraIrpContext( IrpContext );
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+ DebugTrace(-1, Dbg, "DisconnectTimedOutScbs -> VOID\n", 0 );
+}
+
+BOOLEAN
+NwAllocateExtraIrpContext(
+ OUT PIRP_CONTEXT *ppIrpContext,
+ IN PNONPAGED_SCB pNpScb
+ )
+{
+ PIRP Irp;
+ BOOLEAN Success = TRUE;
+
+ try {
+
+ //
+ // Try to allocate an IRP
+ //
+
+ Irp = ALLOCATE_IRP( pNpScb->Server.pDeviceObject->StackSize, FALSE );
+ if ( Irp == NULL ) {
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ //
+ // Try to allocate an IRP Context. This will
+ // raise an excpetion if it fails.
+ //
+
+ *ppIrpContext = AllocateIrpContext( Irp );
+ Irp->Tail.Overlay.Thread = PsGetCurrentThread();
+
+ } except( NwExceptionFilter( Irp, GetExceptionInformation() )) {
+ Success = FALSE;
+ }
+
+ return( Success );
+}
+
+VOID
+NwFreeExtraIrpContext(
+ IN PIRP_CONTEXT pIrpContext
+ )
+{
+ FREE_IRP( pIrpContext->pOriginalIrp );
+
+ pIrpContext->pOriginalIrp = NULL; // Avoid FreeIrpContext modifying freed Irp.
+
+ FreeIrpContext( pIrpContext );
+
+ return;
+}
+
diff --git a/private/nw/rdr/security.c b/private/nw/rdr/security.c
new file mode 100644
index 000000000..e1b2fbfba
--- /dev/null
+++ b/private/nw/rdr/security.c
@@ -0,0 +1,1009 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ Security.c
+
+Abstract:
+
+ This module implements security related tasks in the
+ NetWare redirector.
+
+Author:
+
+ Colin Watson [ColinW] 05-Nov-1993
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+#include <stdio.h>
+
+PLOGON
+FindUserByName(
+ IN PUNICODE_STRING UserName
+ );
+
+//
+// The local debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_SECURITY)
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, CreateAnsiUid )
+#pragma alloc_text( PAGE, MakeUidServer )
+#pragma alloc_text( PAGE, FindUser )
+#pragma alloc_text( PAGE, FindUserByName )
+#pragma alloc_text( PAGE, GetUid )
+#pragma alloc_text( PAGE, FreeLogon )
+#pragma alloc_text( PAGE, Logon )
+#pragma alloc_text( PAGE, Logoff )
+#endif
+
+
+VOID
+CreateAnsiUid(
+ OUT PCHAR aUid,
+ IN PLARGE_INTEGER Uid
+ )
+/*++
+
+Routine Description:
+
+ This routine converts the Uid into an array of ansi characters,
+ preserving the uniqueness and allocating the buffer in the process.
+
+ Note: aUid needs to be 17 bytes long.
+
+Arguments:
+
+ OUT PCHAR aUid,
+ IN PLARGE_INTEGER Uid
+
+Return Value:
+
+ Status
+
+--*/
+{
+ PAGED_CODE();
+
+ if (Uid->HighPart != 0) {
+ sprintf( aUid, "%lx%08lx\\", Uid->HighPart, Uid->LowPart );
+ } else {
+ sprintf( aUid, "%lx\\", Uid->LowPart );
+ }
+ return;
+}
+
+
+NTSTATUS
+MakeUidServer(
+ PUNICODE_STRING UidServer,
+ PLARGE_INTEGER Uid,
+ PUNICODE_STRING Server
+ )
+
+/*++
+
+Routine Description:
+
+ This routine makes a Unicode string of the form 3e7\servername
+
+Arguments:
+
+ OUT PUNICODE_STRING UidServer,
+ IN PLARGE_INTEGER Uid,
+ IN PUNICODE_STRING Server
+
+Return Value:
+
+ Status
+
+--*/
+{
+ //
+ // Translate the servername into the form 3e7\Server where 3e7
+ // is the value of the Uid.
+ //
+ UCHAR aUid[17];
+ ANSI_STRING AnsiString;
+ ULONG UnicodeLength;
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ CreateAnsiUid( aUid, Uid);
+
+ RtlInitAnsiString( &AnsiString, aUid );
+
+ UnicodeLength = RtlAnsiStringToUnicodeSize(&AnsiString);
+
+ UidServer->MaximumLength = (USHORT)UnicodeLength + Server->Length;
+ UidServer->Buffer = ALLOCATE_POOL(PagedPool,UidServer->MaximumLength);
+
+ if (UidServer->Buffer == NULL) {
+ DebugTrace(-1, Dbg, "MakeUidServer -> %08lx\n", STATUS_INSUFFICIENT_RESOURCES);
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ Status = RtlAnsiStringToUnicodeString( UidServer, &AnsiString, FALSE);
+ ASSERT(NT_SUCCESS(Status) && "MakeUidServer failed!");
+
+ Status = RtlAppendStringToString( (PSTRING)UidServer, (PSTRING)Server);
+ ASSERT(NT_SUCCESS(Status) && "MakeUidServer part 2 failed!");
+ return STATUS_SUCCESS;
+}
+
+
+PLOGON
+FindUser(
+ IN PLARGE_INTEGER Uid,
+ IN BOOLEAN ExactMatch
+ )
+
+/*++
+
+Routine Description:
+
+ This routine searches the LogonList for the user entry corresponding
+ to Uid.
+
+ Note: Rcb must be held to prevent LogonList being changed.
+
+Arguments:
+
+ IN PLARGE_INTEGER Uid
+
+ IN BOOLEAN ExactMatch - if TRUE, don't return a default
+
+Return Value:
+
+ None
+
+--*/
+{
+ PLIST_ENTRY LogonQueueEntry = LogonList.Flink;
+ PLOGON DefaultLogon = NULL;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "FindUser...\n", 0);
+ DebugTrace( 0, Dbg, " ->UserUidHigh = %08lx\n", Uid->HighPart);
+ DebugTrace( 0, Dbg, " ->UserUidLow = %08lx\n", Uid->LowPart);
+ while ( LogonQueueEntry != &LogonList ) {
+
+ PLOGON Logon = CONTAINING_RECORD( LogonQueueEntry, LOGON, Next );
+
+ if ( (*Uid).QuadPart == Logon->UserUid.QuadPart ) {
+ DebugTrace(-1, Dbg, " ... %x\n", Logon );
+ return Logon;
+ }
+
+ LogonQueueEntry = Logon->Next.Flink;
+ }
+
+ if (ExactMatch) {
+ DebugTrace(-1, Dbg, " ... DefaultLogon NULL\n", 0 );
+ return NULL;
+ }
+
+ LogonQueueEntry = LogonList.Flink;
+ while ( LogonQueueEntry != &LogonList ) {
+
+ PLOGON Logon = CONTAINING_RECORD( LogonQueueEntry, LOGON, Next );
+
+ if (Logon->UserUid.QuadPart == DefaultLuid.QuadPart) {
+
+ //
+ // This is the first Default Logon entry. If this UID is not
+ // in the table then this is the one to use.
+ //
+
+ DebugTrace(-1, Dbg, " ... DefaultLogon %lx\n", Logon );
+ return Logon;
+ }
+
+ LogonQueueEntry = Logon->Next.Flink;
+ }
+
+ ASSERT( FALSE && "Couldn't find the Id" );
+
+ DebugTrace(-1, Dbg, " ... DefaultLogon NULL\n", 0 );
+ return NULL;
+}
+
+
+PLOGON
+FindUserByName(
+ IN PUNICODE_STRING UserName
+ )
+/*++
+
+Routine Description:
+
+ This routine searches the LogonList for the user entry corresponding
+ to Username.
+
+ Note: Rcb must be held to prevent LogonList being changed.
+
+Arguments:
+
+ UserName - The user name to find.
+
+Return Value:
+
+ If found, a pointer to the logon structure
+ NULL, if no match
+
+--*/
+{
+ PLIST_ENTRY LogonQueueEntry = LogonList.Flink;
+ PLOGON Logon;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "FindUserByName...\n", 0);
+ DebugTrace( 0, Dbg, " ->UserName = %wZ\n", UserName);
+
+ while ( LogonQueueEntry != &LogonList ) {
+
+ Logon = CONTAINING_RECORD( LogonQueueEntry, LOGON, Next );
+
+ if ( RtlEqualUnicodeString( UserName, &Logon->UserName, TRUE ) ) {
+ DebugTrace(-1, Dbg, " ... %x\n", Logon );
+ return Logon;
+ }
+
+ LogonQueueEntry = Logon->Next.Flink;
+ }
+
+ DebugTrace(-1, Dbg, " ... NULL\n", 0 );
+ return NULL;
+}
+
+
+LARGE_INTEGER
+GetUid(
+ IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine gets the effective UID to be used for this create.
+
+Arguments:
+
+ SubjectSecurityContext - Supplies the information from IrpSp.
+
+Return Value:
+
+ None
+
+--*/
+{
+ LARGE_INTEGER LogonId;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "GetUid ... \n", 0);
+
+
+ // Is the thread currently impersonating someone else?
+
+ if (SubjectSecurityContext->ClientToken != NULL) {
+
+ //
+ // If its impersonating someone that is logged in locally then use
+ // the local id.
+ //
+
+ SeQueryAuthenticationIdToken(SubjectSecurityContext->ClientToken, (PLUID)&LogonId);
+
+ if (FindUser(&LogonId, TRUE) == NULL) {
+
+ //
+ // Not logged on locally, use the processes LogonId so that the
+ // gateway will work.
+ //
+
+ SeQueryAuthenticationIdToken(SubjectSecurityContext->PrimaryToken, (PLUID)&LogonId);
+ }
+
+ } else {
+
+ //
+ // Use the processes LogonId
+ //
+
+ SeQueryAuthenticationIdToken(SubjectSecurityContext->PrimaryToken, (PLUID)&LogonId);
+ }
+
+ DebugTrace( 0, Dbg, " ->UserUidHigh = %08lx\n", LogonId.HighPart);
+ DebugTrace(-1, Dbg, " ->UserUidLow = %08lx\n", LogonId.LowPart);
+
+ return LogonId;
+}
+
+
+VOID
+FreeLogon(
+ IN PLOGON Logon
+ )
+
+/*++
+
+Routine Description:
+
+ This routine free's all the strings inside Logon and the structure itself.
+
+Arguments:
+
+ IN PLOGON Logon
+
+Return Value:
+
+ None
+
+--*/
+{
+ PLIST_ENTRY pListEntry;
+ PNDS_SECURITY_CONTEXT pContext;
+
+ PAGED_CODE();
+
+ if ((Logon == NULL) ||
+ (Logon == &Guest)) {
+ return;
+ }
+
+ if ( Logon->UserName.Buffer != NULL ) {
+ FREE_POOL( Logon->UserName.Buffer );
+ }
+
+ if ( Logon->PassWord.Buffer != NULL ) {
+ FREE_POOL( Logon->PassWord.Buffer );
+ }
+
+ if ( Logon->ServerName.Buffer != NULL ) {
+ FREE_POOL( Logon->ServerName.Buffer );
+ }
+
+ while ( !IsListEmpty(&Logon->NdsCredentialList) ) {
+
+ pListEntry = RemoveHeadList( &Logon->NdsCredentialList );
+ pContext = CONTAINING_RECORD(pListEntry, NDS_SECURITY_CONTEXT, Next );
+ FreeNdsContext( pContext );
+
+ }
+
+ ExDeleteResource( &Logon->CredentialListResource );
+ FREE_POOL( Logon );
+}
+
+
+NTSTATUS
+Logon(
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes the username and password supplied and makes
+ them the default to be used for all connections.
+
+Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ PLOGON Logon = NULL;
+
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PNWR_REQUEST_PACKET InputBuffer = Irp->AssociatedIrp.SystemBuffer;
+ ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+
+ UNICODE_STRING ServerName;
+ PNDS_SECURITY_CONTEXT pNdsContext;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "Logon\n", 0);
+
+ try {
+
+ //
+ // Check some fields in the input buffer.
+ //
+
+ if (InputBufferLength < sizeof(NWR_REQUEST_PACKET)) {
+ try_return(Status = STATUS_BUFFER_TOO_SMALL);
+ }
+
+ if (InputBuffer->Version != REQUEST_PACKET_VERSION) {
+ try_return(Status = STATUS_INVALID_PARAMETER);
+ }
+
+ if (InputBufferLength <
+ (FIELD_OFFSET(NWR_REQUEST_PACKET,Parameters.Logon.UserName)) +
+ InputBuffer->Parameters.Logon.UserNameLength +
+ InputBuffer->Parameters.Logon.PasswordLength +
+ InputBuffer->Parameters.Logon.ServerNameLength +
+ InputBuffer->Parameters.Logon.ReplicaAddrLength) {
+ try_return(Status = STATUS_INVALID_PARAMETER);
+ }
+
+ Logon = ALLOCATE_POOL(NonPagedPool,sizeof(LOGON));
+ if (Logon == NULL) {
+ try_return( Status = STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ RtlZeroMemory(Logon, sizeof(LOGON));
+ Logon->NodeTypeCode = NW_NTC_LOGON;
+ Logon->NodeByteSize = sizeof(LOGON);
+ InitializeListHead( &Logon->NdsCredentialList );
+ ExInitializeResource( &Logon->CredentialListResource );
+
+ Status = SetUnicodeString(&Logon->UserName,
+ InputBuffer->Parameters.Logon.UserNameLength,
+ InputBuffer->Parameters.Logon.UserName);
+
+ if (!NT_SUCCESS(Status)) {
+ try_return( Status );
+ }
+
+ Status = SetUnicodeString(&Logon->PassWord,
+ InputBuffer->Parameters.Logon.PasswordLength,
+ (PWCHAR)
+ ((PUCHAR)InputBuffer->Parameters.Logon.UserName +
+ InputBuffer->Parameters.Logon.UserNameLength));
+
+ if (!NT_SUCCESS(Status)) {
+ try_return( Status );
+ }
+
+ ServerName.Buffer =
+ (PWCHAR)
+ ((PUCHAR)InputBuffer->Parameters.Logon.UserName +
+ InputBuffer->Parameters.Logon.UserNameLength +
+ InputBuffer->Parameters.Logon.PasswordLength);
+
+ ServerName.Length =
+ (USHORT)InputBuffer->Parameters.Logon.ServerNameLength;
+
+ ServerName.MaximumLength =
+ (USHORT)InputBuffer->Parameters.Logon.ServerNameLength;
+
+ if ( ServerName.Length &&
+ ServerName.Buffer[0] != L'*' ) {
+
+ //
+ // Only set this as the preferred server if it's not
+ // a default tree. Default tree requests start with a '*'.
+ //
+
+ Status = SetUnicodeString(&Logon->ServerName,
+ ServerName.Length,
+ ServerName.Buffer );
+
+ if (!NT_SUCCESS(Status)) {
+ try_return( Status );
+ }
+ }
+
+ //
+ // Store the unique userid in both unicode and large integer form
+ // the unicode form is used as a prefix to the servername in all
+ // paths so that each userid gets their own connection to the server.
+ //
+
+ *((PLUID)(&Logon->UserUid)) = InputBuffer->Parameters.Logon.LogonId;
+
+ // Save Uid for CreateScb
+
+ *((PLUID)(&IrpContext->Specific.Create.UserUid)) =
+ InputBuffer->Parameters.Logon.LogonId;
+
+try_exit:NOTHING;
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ Status = GetExceptionCode();
+
+ }
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ if (NT_SUCCESS(Status)) {
+
+ DebugTrace( 0, Dbg, " ->UserName = %wZ\n", &Logon->UserName );
+ DebugTrace( 0, Dbg, " ->PassWord = %wZ\n", &Logon->PassWord );
+
+ if ( ServerName.Length && ServerName.Buffer[0] == L'*' ) {
+ DebugTrace( 0, Dbg, " ->DefaultTree = %wZ\n", &ServerName );
+ } else {
+ DebugTrace( 0, Dbg, " ->ServerName = %wZ\n", &Logon->ServerName );
+ }
+
+ DebugTrace( 0, Dbg, " ->UserUidHigh = %08lx\n", Logon->UserUid.HighPart);
+ DebugTrace( 0, Dbg, " ->UserUidLow = %08lx\n", Logon->UserUid.LowPart);
+
+ InsertHeadList( &LogonList, &Logon->Next );
+ NwReleaseRcb( &NwRcb );
+
+ if ( ServerName.Length &&
+ ServerName.Buffer[0] != L'*' ) {
+
+ PSCB Scb;
+
+ // See if we can login as this user.
+
+ Status = CreateScb(
+ &Scb,
+ IrpContext,
+ &ServerName,
+ NULL,
+ NULL,
+ NULL,
+ FALSE,
+ FALSE );
+ if (NT_SUCCESS(Status)) {
+
+ //
+ // CreateScb has already boosted the reference count
+ // because this is a preferred server so it will not go
+ // away. We need to dereference it here because there is
+ // no handle associated with the CreateScb
+ //
+
+ NwDereferenceScb(Scb->pNpScb);
+ }
+
+ }
+
+ if ( ServerName.Length &&
+ ServerName.Buffer[0] == L'*' ) {
+
+ PSCB Scb;
+ BOOL SetContext;
+ UINT ContextLength;
+ UNICODE_STRING DefaultContext;
+ IPXaddress *ReplicaAddr;
+
+ //
+ // Ok, this is a little confusing. On Login, the provider can
+ // specify the address of the replica that we should use to log
+ // in. If this is the case, then we do pre-connect that replica.
+ // Otherwise, we do the standard login to any replica. The
+ // reason for this is that standard replica location uses the
+ // bindery and doesn't always get us the nearest dir server.
+ //
+
+ if ( InputBuffer->Parameters.Logon.ReplicaAddrLength ==
+ sizeof( TDI_ADDRESS_IPX ) ) {
+
+ ReplicaAddr = (IPXaddress*)
+ ((PUCHAR) InputBuffer->Parameters.Logon.UserName +
+ InputBuffer->Parameters.Logon.UserNameLength +
+ InputBuffer->Parameters.Logon.PasswordLength +
+ InputBuffer->Parameters.Logon.ServerNameLength);
+
+ CreateScb( &Scb,
+ IrpContext,
+ NULL, // anonymous create
+ ReplicaAddr, // nearest replica add
+ NULL, // no user name
+ NULL, // no password
+ TRUE, // defer the login
+ FALSE ); // we are not deleting the connection
+
+ }
+
+ //
+ // Set if this includes a default context.
+ //
+
+ ServerName.Buffer += 1;
+ ServerName.Length -= sizeof( WCHAR );
+ ServerName.MaximumLength -= sizeof( WCHAR );
+
+ SetContext = FALSE;
+ ContextLength = 0;
+
+ while ( ContextLength < ServerName.Length / sizeof( WCHAR ) ) {
+
+ if ( ServerName.Buffer[ContextLength] == L'\\' ) {
+
+ SetContext = TRUE;
+
+ ContextLength++;
+
+ //
+ // Skip any leading periods.
+ //
+
+ if ( ServerName.Buffer[ContextLength] == L'.' ) {
+
+ DefaultContext.Buffer = &ServerName.Buffer[ContextLength + 1];
+ ServerName.Length -= sizeof ( WCHAR ) ;
+ ServerName.MaximumLength -= sizeof ( WCHAR );
+
+ } else {
+
+ DefaultContext.Buffer = &ServerName.Buffer[ContextLength];
+
+ }
+
+ ContextLength *= sizeof( WCHAR );
+ DefaultContext.Length = ServerName.Length - ContextLength;
+ DefaultContext.MaximumLength = ServerName.MaximumLength - ContextLength;
+
+ ServerName.Length -= ( DefaultContext.Length + sizeof( WCHAR ) );
+ ServerName.MaximumLength -= ( DefaultContext.Length + sizeof( WCHAR ) );
+
+ }
+
+ ContextLength++;
+ }
+
+ //
+ // Verify that this context is valid before we acquire
+ // the credentials and really set the context.
+ //
+
+ if ( SetContext ) {
+
+ Status = NdsVerifyContext( IrpContext, &ServerName, &DefaultContext );
+
+ if ( !NT_SUCCESS( Status )) {
+ SetContext = FALSE;
+ }
+
+ }
+
+ //
+ // Generate the credential shell for the default tree and
+ // set the context if appropriate.
+ //
+
+ Status = NdsLookupCredentials( &ServerName,
+ Logon,
+ &pNdsContext,
+ CREDENTIAL_WRITE,
+ TRUE );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // Set the context. It doesn't matter if the
+ // credential is locked or not.
+ //
+
+ if ( SetContext ) {
+
+ RtlCopyUnicodeString( &pNdsContext->CurrentContext,
+ &DefaultContext );
+ DebugTrace( 0, Dbg, "Default Context: %wZ\n", &DefaultContext );
+ }
+
+ NwReleaseCredList( Logon );
+
+ //
+ // RELAX! The credential list is free.
+ //
+
+ DebugTrace( 0, Dbg, "Default Tree: %wZ\n", &ServerName );
+
+ Status = NdsCreateTreeScb( IrpContext,
+ &Scb,
+ &ServerName,
+ NULL,
+ NULL,
+ FALSE,
+ FALSE );
+
+ if (NT_SUCCESS(Status)) {
+ NwDereferenceScb(Scb->pNpScb);
+ }
+ }
+ }
+
+ //
+ // No login requested.
+ //
+
+ } else {
+
+ FreeLogon( Logon );
+ NwReleaseRcb( &NwRcb );
+
+ }
+
+
+ DebugTrace(-1, Dbg, "Logon %lx\n", Status);
+ return Status;
+}
+
+
+NTSTATUS
+Logoff(
+ IN PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine sets the username back to guest and removes the password.
+
+Arguments:
+
+ IN PIRP_CONTEXT IrpContext - Io Request Packet for request
+
+Return Value:
+
+NTSTATUS
+
+--*/
+
+{
+ BOOLEAN Locked = FALSE;
+ NTSTATUS Status = STATUS_SUCCESS;
+ PIRP Irp = IrpContext->pOriginalIrp;
+ PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
+ PNWR_REQUEST_PACKET InputBuffer = Irp->AssociatedIrp.SystemBuffer;
+ ULONG InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength;
+ LARGE_INTEGER User;
+ PLOGON Logon;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "Logoff...\n", 0);
+
+ try {
+
+ //
+ // Check some fields in the input buffer.
+ //
+
+ if (InputBufferLength < sizeof(NWR_REQUEST_PACKET)) {
+ try_return(Status = STATUS_BUFFER_TOO_SMALL);
+ }
+
+ if (InputBuffer->Version != REQUEST_PACKET_VERSION) {
+ try_return(Status = STATUS_INVALID_PARAMETER);
+ }
+
+ *((PLUID)(&User)) = InputBuffer->Parameters.Logoff.LogonId;
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ Locked = TRUE;
+
+ Logon = FindUser(&User, TRUE);
+
+ if ( Logon != NULL ) {
+
+ LARGE_INTEGER Uid = Logon->UserUid;
+
+ //
+ // We have found the right user.
+ //
+
+ ASSERT( Logon != &Guest);
+
+ NwReleaseRcb( &NwRcb );
+ Locked = FALSE;
+
+ DebugTrace( 0, Dbg, " ->UserName = %wZ\n", &Logon->UserName );
+ DebugTrace( 0, Dbg, " ->ServerName = %wZ\n", &Logon->ServerName );
+ DebugTrace( 0, Dbg, " ->UserUidHigh = %08lx\n", Logon->UserUid.HighPart);
+ DebugTrace( 0, Dbg, " ->UserUidLow = %08lx\n", Logon->UserUid.LowPart);
+
+
+ //
+ // Invalidating all the handles for this user will also cause logoffs
+ // to all the servers in question.
+ //
+
+ NwInvalidateAllHandles(&Uid, IrpContext);
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ Locked = TRUE;
+
+ Logon = FindUser(&User, TRUE);
+
+ if (Logon != NULL) {
+ RemoveEntryList( &Logon->Next );
+ FreeLogon( Logon );
+ } else {
+ ASSERT( FALSE && "Double logoff!");
+ }
+
+ Status = STATUS_SUCCESS;
+
+ } else {
+
+ Status = STATUS_UNSUCCESSFUL;
+ }
+
+try_exit:NOTHING;
+ } finally {
+ if (Locked == TRUE ) {
+ NwReleaseRcb( &NwRcb );
+ }
+ }
+
+ DebugTrace(-1, Dbg, "Logoff %lx\n", Status);
+
+ return Status;
+}
+
+NTSTATUS
+UpdateUsersPassword(
+ IN PUNICODE_STRING UserName,
+ IN PUNICODE_STRING Password,
+ OUT PLARGE_INTEGER Uid
+ )
+/*++
+
+Routine Description:
+
+ This routine updates the cached password for a given user.
+ If the named user is not logged in, an error is returned.
+
+Arguments:
+
+ UserName - Supplies the name of the user
+
+ Password - Supplies the new password
+
+ Uid - Returns the LUID of the updated user.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ PLOGON Logon;
+ NTSTATUS Status;
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ Logon = FindUserByName( UserName );
+
+ if ( Logon != NULL ) {
+
+ if ( Logon->PassWord.Buffer != NULL ) {
+ FREE_POOL( Logon->PassWord.Buffer );
+ }
+
+ Status = SetUnicodeString(
+ &Logon->PassWord,
+ Password->Length,
+ Password->Buffer );
+
+ *Uid = Logon->UserUid;
+
+ } else {
+
+ Status = STATUS_UNSUCCESSFUL;
+ }
+
+ NwReleaseRcb( &NwRcb );
+ return( Status );
+
+}
+
+NTSTATUS
+UpdateServerPassword(
+ PIRP_CONTEXT IrpContext,
+ IN PUNICODE_STRING ServerName,
+ IN PUNICODE_STRING UserName,
+ IN PUNICODE_STRING Password,
+ IN PLARGE_INTEGER Uid
+ )
+/*++
+
+Routine Description:
+
+ This routine updates the cached password for a named server connection.
+ If the server does not exist in the server table, an error is returned.
+
+Arguments:
+
+ ServerName - Supplies the name of the server
+
+ UserName - Supplies the name of the user
+
+ Password - Supplies the new password
+
+ Uid - The LUID of the user.
+
+Return Value:
+
+ NTSTATUS
+
+--*/
+{
+ UNICODE_STRING UidServer;
+ NTSTATUS Status;
+ PUNICODE_PREFIX_TABLE_ENTRY PrefixEntry;
+ PSCB pScb;
+ PNONPAGED_SCB pNpScb;
+ PVOID Buffer;
+
+ Status = MakeUidServer(
+ &UidServer,
+ Uid,
+ ServerName );
+
+ if ( !NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ DebugTrace( 0, Dbg, " ->UidServer = %wZ\n", &UidServer );
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ PrefixEntry = RtlFindUnicodePrefix( &NwRcb.ServerNameTable, &UidServer, 0 );
+
+ if ( PrefixEntry != NULL ) {
+
+ pScb = CONTAINING_RECORD( PrefixEntry, SCB, PrefixEntry );
+ pNpScb = pScb->pNpScb;
+
+ NwReferenceScb( pNpScb );
+
+ //
+ // Release the RCB.
+ //
+
+ NwReleaseRcb( &NwRcb );
+
+ } else {
+
+ NwReleaseRcb( &NwRcb );
+ FREE_POOL(UidServer.Buffer);
+ return( STATUS_BAD_NETWORK_PATH );
+ }
+
+ IrpContext->pNpScb = pNpScb;
+ NwAppendToQueueAndWait( IrpContext );
+
+ //
+ // Free the old username password, allocate a new one.
+ //
+
+ if ( pScb->UserName.Buffer != NULL ) {
+ FREE_POOL( pScb->UserName.Buffer );
+ }
+
+ Buffer = ALLOCATE_POOL_EX( NonPagedPool, UserName->Length + Password->Length );
+
+ pScb->UserName.Buffer = Buffer;
+ pScb->UserName.Length = pScb->UserName.MaximumLength = UserName->Length;
+ RtlMoveMemory( pScb->UserName.Buffer, UserName->Buffer, UserName->Length );
+
+ pScb->Password.Buffer = (PWCHAR)((PCHAR)Buffer + UserName->Length);
+ pScb->Password.Length = pScb->Password.MaximumLength = Password->Length;
+ RtlMoveMemory( pScb->Password.Buffer, Password->Buffer, Password->Length );
+
+ FREE_POOL(UidServer.Buffer);
+
+ return( STATUS_SUCCESS );
+}
+
diff --git a/private/nw/rdr/sources b/private/nw/rdr/sources
new file mode 100644
index 000000000..b4e3f30e1
--- /dev/null
+++ b/private/nw/rdr/sources
@@ -0,0 +1,90 @@
+!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:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=ntos
+MINORCOMP=nwrdr
+
+TARGETNAME=nwrdr
+TARGETPATH=\nt\public\sdk\lib
+TARGETTYPE=DRIVER
+TARGETLIBS=.\*\nw4crypt.lib \
+ .\*\rsa32.lib \
+ $(_NTROOT)\public\sdk\lib\*\tdi.lib
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+C_DEFINES=$(C_DEFINES) -D_PNP_POWER=1
+
+!IF "$(QFE_BUILD)" != "1"
+NET_C_DEFINES=-DNWDBG=1
+!ELSE
+NET_C_DEFINES=-DNWDBG=1 -DQFE_BUILD=1
+!ENDIF
+
+INCLUDES=..\inc;$(_NTROOT)\private\inc;$(_NTROOT)\private\ntos\inc
+
+SOURCES=Attach.c \
+ Cache.c \
+ Callback.c \
+ Cleanup.c \
+ Close.c \
+ Convert.c \
+ Create.c \
+ Data.c \
+ Debug.c \
+ Deviosup.c \
+ Dir.c \
+ Encrypt.c \
+ Errorlog.c \
+ Except.c \
+ Exchange.c \
+ Filobsup.c \
+ Fileinfo.c \
+ Fsctl.c \
+ FspDisp.c \
+ Init.c \
+ Ipx.c \
+ Lock.c \
+ LockCode.c \
+ NwRdr.rc \
+ Pid.c \
+ Read.c \
+ Scavengr.c \
+ Security.c \
+ String.c \
+ Strucsup.c \
+ Timer.c \
+ Util.c \
+ VolInfo.c \
+ Workque.c \
+ Write.c \
+ Create4.c \
+ Fragex.c \
+ Ndsfsctl.c \
+ Ndslogin.c \
+ Ndsread.c
+
+PRECOMPILED_INCLUDE=procs.h
+PRECOMPILED_PCH=procs.pch
+PRECOMPILED_OBJ=procs.obj
diff --git a/private/nw/rdr/string.c b/private/nw/rdr/string.c
new file mode 100644
index 000000000..c2136c3e0
--- /dev/null
+++ b/private/nw/rdr/string.c
@@ -0,0 +1,378 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ string.c
+
+Abstract:
+
+ This module implements the string routines needed for the NT redirector
+
+Author:
+
+ Colin Watson (ColinW) 02-Apr-1993
+
+Revision History:
+
+ 14-Jun-1990 LarryO
+
+ Created for Lanman Redirector
+
+ 02-Apr-1993 ColinW
+
+ Modified for NwRdr
+
+--*/
+
+#include "Procs.h"
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, DuplicateStringWithString )
+#pragma alloc_text( PAGE, DuplicateUnicodeStringWithString )
+#pragma alloc_text( PAGE, SetUnicodeString )
+#pragma alloc_text( PAGE, MergeStrings )
+#endif
+
+
+NTSTATUS
+DuplicateStringWithString (
+ OUT PSTRING DestinationString,
+ IN PSTRING SourceString,
+ IN POOL_TYPE PoolType
+ )
+
+/*++
+
+Routine Description:
+
+ This routine duplicates a supplied input string, storing the result
+ of the duplication in the supplied string. The maximumlength of the
+ new string is determined by the length of the SourceString.
+
+
+Arguments:
+
+ OUT PSTRING DestinationString - Returns the filled in string.
+ IN PSTRING SourceString - Supplies the string to duplicate
+ IN POOLTYPE PoolType - Supplies the type of pool (PagedPool or
+ NonPagedPool)
+Return Value:
+
+ NTSTATUS - Status of resulting operation
+ If !NT_SUCCESS then DestinationString->Buffer == NULL
+--*/
+
+{
+ PAGED_CODE();
+
+ DestinationString->Buffer = NULL;
+
+ try {
+
+ if (SourceString->Length != 0) {
+ //
+ // Allocate pool to hold the buffer (contents of the string)
+ //
+
+ DestinationString->Buffer = (PSZ )ALLOCATE_POOL(PoolType,
+ SourceString->Length);
+ }
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ return GetExceptionCode();
+
+ }
+
+ if (DestinationString->Buffer == NULL && SourceString->Length != 0) {
+
+ //
+ // The allocation failed, return failure.
+ //
+
+ return STATUS_INSUFFICIENT_RESOURCES;
+
+ }
+
+ DestinationString->MaximumLength = SourceString->Length;
+
+ //
+ // Copy the source string into the newly allocated
+ // destination string
+ //
+
+ RtlCopyString(DestinationString, SourceString);
+
+ return STATUS_SUCCESS;
+
+}
+
+
+NTSTATUS
+DuplicateUnicodeStringWithString (
+ OUT PUNICODE_STRING DestinationString,
+ IN PUNICODE_STRING SourceString,
+ IN POOL_TYPE PoolType
+ )
+
+/*++
+
+Routine Description:
+
+ This routine duplicates a supplied input string, storing the result
+ of the duplication in the supplied string. The maximumlength of the
+ new string is determined by the length of the SourceString.
+
+
+Arguments:
+
+ OUT PSTRING DestinationString - Returns the filled in string.
+ IN PSTRING SourceString - Supplies the string to duplicate
+ IN POOLTYPE PoolType - Supplies the type of pool (PagedPool or
+ NonPagedPool)
+Return Value:
+
+ NTSTATUS - Status of resulting operation
+ If !NT_SUCCESS then DestinationString->Buffer == NULL
+
+--*/
+
+{
+ PAGED_CODE();
+
+ DestinationString->Buffer = NULL;
+
+ try {
+
+ if (SourceString->Length != 0) {
+ //
+ // Allocate pool to hold the buffer (contents of the string)
+ //
+
+ DestinationString->Buffer = (WCHAR *)ALLOCATE_POOL(PoolType,
+ SourceString->Length);
+ }
+
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+
+ return GetExceptionCode();
+
+ }
+
+ if (DestinationString->Buffer == NULL && SourceString->Length != 0) {
+
+ //
+ // The allocation failed, return failure.
+ //
+
+ return STATUS_INSUFFICIENT_RESOURCES;
+
+ }
+
+ DestinationString->MaximumLength = SourceString->Length;
+
+ //
+ // Copy the source string into the newly allocated
+ // destination string
+ //
+
+ RtlCopyUnicodeString(DestinationString, SourceString);
+
+ return STATUS_SUCCESS;
+
+}
+
+#if 0
+
+VOID
+CopyUnicodeStringToUnicode (
+ OUT PVOID *Destination,
+ IN PUNICODE_STRING Source,
+ IN BOOLEAN AdjustPointer
+ )
+
+/*++
+
+Routine Description:
+ This routine copies the specified source string onto the destination
+ asciiz string.
+
+Arguments:
+
+ OUT PUCHAR Destination, - Supplies a pointer to the destination
+ buffer for the string.
+ IN PSTRING String - Supplies the source string.
+ IN BOOLEAN AdjustPointer - If TRUE, increment destination pointer
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ RtlCopyMemory((*Destination), (Source)->Buffer, (Source)->Length);
+ if (AdjustPointer) {
+ ((PCHAR)(*Destination)) += ((Source)->Length);
+ }
+}
+
+
+NTSTATUS
+CopyUnicodeStringToAscii (
+ OUT PUCHAR *Destination,
+ IN PUNICODE_STRING Source,
+ IN BOOLEAN AdjustPointer,
+ IN USHORT MaxLength
+ )
+/*++
+
+Routine Description:
+
+ This routine copies the specified source string onto the destination
+ asciiz string.
+
+Arguments:
+
+ OUT PUCHAR Destination, - Supplies the destination asciiz string.
+ IN PUNICODE_STRING String - Supplies the source string.
+ IN BOOLEAN AdjustPointer - If TRUE, increment destination pointer
+
+Return Value:
+
+ Status of conversion.
+--*/
+{
+ ANSI_STRING DestinationString;
+
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ DestinationString.Buffer = (*Destination);
+
+ DestinationString.MaximumLength = (USHORT)(MaxLength);
+
+ Status = RtlUnicodeStringToOemString(&DestinationString, (Source), FALSE);
+
+ if (!NT_SUCCESS(Status)) {
+ return Status;
+ }
+
+ if (AdjustPointer) {
+ (*Destination) += DestinationString.Length;
+ }
+
+ return STATUS_SUCCESS;
+
+}
+#endif
+
+
+NTSTATUS
+SetUnicodeString (
+ IN PUNICODE_STRING Destination,
+ IN ULONG Length,
+ IN PWCHAR Source
+ )
+/*++
+
+Routine Description:
+
+ This routine copies the specified source string onto the destination
+ UNICODE string allocating the buffer.
+
+Arguments:
+
+
+Return Value:
+
+ Status of conversion.
+--*/
+{
+ UNICODE_STRING Temp;
+
+ PAGED_CODE();
+
+ Destination->Buffer = NULL;
+ Destination->Length = 0;
+ Destination->MaximumLength = 0;
+
+ if (Length == 0) {
+ return STATUS_SUCCESS;
+ }
+
+ Temp.MaximumLength =
+ Temp.Length = (USHORT )Length;
+ Temp.Buffer = Source;
+
+ Destination->Buffer =
+ ALLOCATE_POOL(NonPagedPool,
+ Temp.MaximumLength+sizeof(WCHAR));
+
+ if (Destination->Buffer == NULL) {
+ Error(EVENT_NWRDR_RESOURCE_SHORTAGE, STATUS_INSUFFICIENT_RESOURCES, NULL, 0, 0);
+ return STATUS_INSUFFICIENT_RESOURCES;
+
+ }
+
+ Destination->MaximumLength = (USHORT)Length;
+
+ RtlCopyUnicodeString(Destination, &Temp);
+
+ Destination->Buffer[(Destination->Length/sizeof(WCHAR))] = UNICODE_NULL;
+
+ return STATUS_SUCCESS;
+
+}
+
+
+VOID
+MergeStrings(
+ IN PUNICODE_STRING Destination,
+ IN PUNICODE_STRING S1,
+ IN PUNICODE_STRING S2,
+ IN ULONG Type
+ )
+/*++
+
+Routine Description:
+
+ This routine Allocates space for Destination.Buffer and copies S1 followed
+ by S2 into the buffer.
+
+ Raises status if couldn't allocate buffer
+
+Arguments:
+
+ IN PUNICODE_STRING Destination,
+ IN PUNICODE_STRING S1,
+ IN PUNICODE_STRING S2,
+ IN ULONG Type - PagedPool or NonPagedPool
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PAGED_CODE();
+
+ Destination->MaximumLength = S1->Length + S2->Length;
+ Destination->Length = S1->Length + S2->Length;
+
+ Destination->Buffer = ALLOCATE_POOL_EX( Type, Destination->MaximumLength );
+
+ RtlCopyMemory( Destination->Buffer,
+ S1->Buffer,
+ S1->Length);
+
+ RtlCopyMemory( (PUCHAR)Destination->Buffer + S1->Length,
+ S2->Buffer,
+ S2->Length);
+ return;
+}
diff --git a/private/nw/rdr/strucsup.c b/private/nw/rdr/strucsup.c
new file mode 100644
index 000000000..3595cc709
--- /dev/null
+++ b/private/nw/rdr/strucsup.c
@@ -0,0 +1,3127 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ strucsup.c
+
+Abstract:
+
+ This module implements the Netware Redirector structure support routines.
+
+Author:
+
+ Manny Weiser (mannyw) 10-Feb-1993
+
+Revision History:
+
+--*/
+#include "procs.h"
+
+BOOLEAN
+GetLongNameSpaceForVolume(
+ IN PIRP_CONTEXT IrpContext,
+ IN UNICODE_STRING ShareName,
+ OUT PCHAR VolumeLongNameSpace,
+ OUT PCHAR VolumeNumber
+ );
+
+CHAR
+GetNewDriveNumber (
+ IN PSCB Scb
+ );
+
+VOID
+FreeDriveNumber(
+ IN PSCB Scb,
+ IN CHAR DriveNumber
+ );
+
+#define Dbg (DEBUG_TRACE_STRUCSUP)
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwInitializeRcb )
+#pragma alloc_text( PAGE, NwDeleteRcb )
+#pragma alloc_text( PAGE, NwCreateIcb )
+#pragma alloc_text( PAGE, NwDeleteIcb )
+#pragma alloc_text( PAGE, NwVerifyIcb )
+#pragma alloc_text( PAGE, NwVerifyIcbSpecial )
+#pragma alloc_text( PAGE, NwInvalidateAllHandlesForScb )
+#pragma alloc_text( PAGE, NwVerifyScb )
+#pragma alloc_text( PAGE, NwCreateFcb )
+#pragma alloc_text( PAGE, NwFindFcb )
+#pragma alloc_text( PAGE, NwDereferenceFcb )
+#pragma alloc_text( PAGE, NwFindVcb )
+#pragma alloc_text( PAGE, NwCreateVcb )
+#pragma alloc_text( PAGE, NwReopenVcbHandlesForScb )
+#pragma alloc_text( PAGE, NwReopenVcbHandle )
+#ifdef NWDBG
+#pragma alloc_text( PAGE, NwReferenceVcb )
+#endif
+#pragma alloc_text( PAGE, NwDereferenceVcb )
+#pragma alloc_text( PAGE, NwCleanupVcb )
+#pragma alloc_text( PAGE, GetLongNameSpaceForVolume )
+#pragma alloc_text( PAGE, IsFatNameValid )
+#pragma alloc_text( PAGE, GetNewDriveNumber )
+#pragma alloc_text( PAGE, FreeDriveNumber )
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, NwInvalidateAllHandles )
+#pragma alloc_text( PAGE1, NwCloseAllVcbs )
+#endif
+
+#endif
+
+#if 0 // Not pageable
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+VOID
+NwInitializeRcb (
+ IN PRCB Rcb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine initializes new Rcb record.
+
+Arguments:
+
+ Rcb - Supplies the address of the Rcb record being initialized.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwInitializeRcb, Rcb = %08lx\n", (ULONG)Rcb);
+
+ //
+ // We start by first zeroing out all of the RCB, this will guarantee
+ // that any stale data is wiped clean.
+ //
+
+ RtlZeroMemory( Rcb, sizeof(RCB) );
+
+ //
+ // Set the node type code, node byte size, and reference count.
+ //
+
+ Rcb->NodeTypeCode = NW_NTC_RCB;
+ Rcb->NodeByteSize = sizeof(RCB);
+ Rcb->OpenCount = 0;
+
+ //
+ // Initialize the resource variable for the RCB.
+ //
+
+ ExInitializeResource( &Rcb->Resource );
+
+ //
+ // Initialize the server name and file name tables.
+ //
+
+ RtlInitializeUnicodePrefix( &Rcb->ServerNameTable );
+ RtlInitializeUnicodePrefix( &Rcb->VolumeNameTable );
+ RtlInitializeUnicodePrefix( &Rcb->FileNameTable );
+
+ //
+ // Return to the caller.
+ //
+
+ DebugTrace(-1, Dbg, "NwInitializeRcb -> VOID\n", 0);
+
+ return;
+}
+
+
+VOID
+NwDeleteRcb (
+ IN PRCB Rcb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine removes the RCB record from our in-memory data
+ structures. It also will remove all associated underlings
+ (i.e., FCB records).
+
+Arguments:
+
+ Rcb - Supplies the Rcb to be removed
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwDeleteRcb, Rcb = %08lx\n", (ULONG)Rcb);
+
+ //
+ // Uninitialize the resource variable for the RCB.
+ //
+
+ ExDeleteResource( &Rcb->Resource );
+
+ //
+ // Return to the caller.
+ //
+
+ DebugTrace(-1, Dbg, "NwDeleteRcb -> VOID\n", 0);
+
+ return;
+}
+
+
+PICB
+NwCreateIcb (
+ IN USHORT Type,
+ IN PVOID Associate
+ )
+
+/*++
+
+Routine Description:
+
+ This routine allocates and initialize a new ICB. The ICB is
+ inserted into the FCB's list.
+
+ *** This routine must be called with the RCB held exclusively.
+
+Arguments:
+
+ Type - The type of ICB this will be.
+
+ Associate - A pointer to an associated data structure.
+ It will be a FCB, DCB, or SCB.
+
+Return Value:
+
+ ICB - A pointer to the newly created ICB.
+
+ If memory allocation fails, this routine will raise an exception.
+
+--*/
+
+{
+ PICB Icb;
+ PSCB Scb;
+
+ PAGED_CODE();
+
+ Icb = ALLOCATE_POOL_EX( NonPagedPool, sizeof( ICB ) );
+
+ RtlZeroMemory( Icb, sizeof( ICB ) );
+
+ Icb->NodeTypeCode = Type;
+ Icb->NodeByteSize = sizeof( ICB );
+ Icb->State = ICB_STATE_OPEN_PENDING;
+ Icb->Pid = (UCHAR)INVALID_PID;
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ if ( Type == NW_NTC_ICB ) {
+
+ PFCB Fcb = (PFCB)Associate;
+
+ //
+ // Insert this ICB on the list of ICBs for this FCB.
+ //
+
+ InsertTailList( &Fcb->IcbList, &Icb->ListEntry );
+ ++Fcb->IcbCount;
+ Icb->SuperType.Fcb = Fcb;
+ Icb->NpFcb = Fcb->NonPagedFcb;
+
+ Fcb->Vcb->OpenFileCount++;
+ Scb = Fcb->Scb;
+
+ Scb->OpenFileCount++;
+
+ } else if ( Type == NW_NTC_ICB_SCB ) {
+
+ Scb = (PSCB)Associate;
+
+ //
+ // Insert this ICB on the list of ICBs for this SCB.
+ //
+
+ InsertTailList( &Scb->IcbList, &Icb->ListEntry );
+ ++Scb->IcbCount;
+ Icb->SuperType.Scb = Scb;
+
+ } else {
+
+ KeBugCheck( RDR_FILE_SYSTEM );
+
+ }
+
+ NwReleaseRcb( &NwRcb );
+
+ NwReferenceScb( Scb->pNpScb );
+ return( Icb );
+}
+
+
+VOID
+NwDeleteIcb (
+ IN PIRP_CONTEXT IrpContext OPTIONAL,
+ IN PICB Icb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine deletes an ICB in the OPEN_PENDING state.
+
+ *** The IRP context must be at the head of the SCB queue when
+ this routine is called.
+
+Arguments:
+
+ Icb - A pointer the ICB to delete.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PFCB Fcb;
+ PSCB Scb;
+
+ PAGED_CODE();
+
+ //
+ // Acquire the lock to protect the ICB list.
+ //
+ DebugTrace( 0, DEBUG_TRACE_ICBS, "NwDeleteIcb, Icb = %08lx\n", (ULONG)Icb);
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ RemoveEntryList( &Icb->ListEntry );
+
+ if ( Icb->NodeTypeCode == NW_NTC_ICB ) {
+
+ Fcb = Icb->SuperType.Fcb;
+ Scb = Fcb->Scb;
+
+ //
+ // Decrement the open file count for the VCB. Note that the ICB
+ // only reference the VCB indirectly via the FCB, so that we do
+ // not dereference the VCB here.
+ //
+
+ --Fcb->Vcb->OpenFileCount;
+ --Scb->OpenFileCount;
+
+ //
+ // Dereference the FCB. This frees the FCB if
+ // this was the last ICB for the FCB.
+ //
+
+ NwDereferenceFcb( IrpContext, Fcb );
+
+ } else if ( Icb->NodeTypeCode == NW_NTC_ICB_SCB ) {
+
+ Scb = Icb->SuperType.Scb;
+
+ //
+ // Decrement of OpenIcb count on the SCB.
+ //
+
+ Scb->IcbCount--;
+
+ } else {
+ KeBugCheck( RDR_FILE_SYSTEM );
+ }
+
+ //
+ // Free the query template buffers.
+ //
+
+ RtlFreeOemString( &Icb->NwQueryTemplate );
+
+ if ( Icb->UQueryTemplate.Buffer != NULL ) {
+ FREE_POOL( Icb->UQueryTemplate.Buffer );
+ }
+
+ //
+ // Try and gracefully catch a 16 bit app closing a
+ // handle to the server and wipe the connection as
+ // soon as possible. This only applies to bindery
+ // authenticated connections because in NDS land,
+ // we handle the licensing of the connection
+ // dynamically.
+ //
+
+ if ( ( Scb->pNpScb->Reference == 1 ) &&
+ ( Icb->NodeTypeCode == NW_NTC_ICB_SCB ) &&
+ ( !Icb->IsTreeHandle ) &&
+ ( IrpContext != NULL ) &&
+ ( Scb->UserName.Length != 0 ) )
+ {
+ LARGE_INTEGER Now;
+ KeQuerySystemTime( &Now );
+
+ DebugTrace( 0, Dbg, "Quick disconnecting 16-bit app.\n", 0 );
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ if ( Scb->OpenFileCount == 0 &&
+ Scb->pNpScb->State != SCB_STATE_RECONNECT_REQUIRED &&
+ !Scb->PreferredServer ) {
+
+ NwLogoffAndDisconnect( IrpContext, Scb->pNpScb);
+ }
+
+ Now.QuadPart += ( NwOneSecond * DORMANT_SCB_KEEP_TIME );
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwDereferenceScb( Scb->pNpScb );
+ DisconnectTimedOutScbs(Now) ;
+ CleanupScbs(Now);
+
+ } else {
+
+ NwDereferenceScb( Scb->pNpScb );
+
+ }
+ FREE_POOL( Icb );
+ NwReleaseRcb( &NwRcb );
+}
+
+VOID
+NwVerifyIcb (
+ IN PICB Icb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine verifies that an ICB is in the opened state.
+ If it is not, the routine raises an exception.
+
+Arguments:
+
+ Icb - A pointer the ICB to verify.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ if ( Icb->State != ICB_STATE_OPENED ) {
+ ExRaiseStatus( STATUS_INVALID_HANDLE );
+ }
+}
+
+VOID
+NwVerifyIcbSpecial (
+ IN PICB Icb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine verifies that an ICB is in the opened state.
+ If it is not, the routine raises an exception.
+
+Arguments:
+
+ Icb - A pointer the ICB to verify.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ if ( (Icb->State != ICB_STATE_OPENED &&
+ Icb->State != ICB_STATE_CLEANED_UP) ) {
+ ExRaiseStatus( STATUS_INVALID_HANDLE );
+ }
+}
+
+
+ULONG
+NwInvalidateAllHandles (
+ PLARGE_INTEGER Uid OPTIONAL,
+ PIRP_CONTEXT IrpContext OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This routine finds all of the ICB in the system that were created
+ by the user specified by the Logon credentials and marks them
+ invalid.
+
+Arguments:
+
+ Uid - Supplies the userid of the handles to close or NULL if all
+ handles to be invalidated.
+ IrpContext - The Irpcontext to be used for the NwLogoffAndDisconnect
+ call, if appropriate. If this is NULL, it indicates a RAS
+ transition.
+
+Return Value:
+
+ The number of active handles that were closed.
+
+--*/
+
+{
+ KIRQL OldIrql;
+ PLIST_ENTRY ScbQueueEntry, NextScbQueueEntry;
+ PNONPAGED_SCB pNpScb;
+ PSCB pScb;
+ ULONG FilesClosed = 0;
+
+ PAGED_CODE();
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ for (ScbQueueEntry = ScbQueue.Flink ;
+ ScbQueueEntry != &ScbQueue ;
+ ScbQueueEntry = NextScbQueueEntry ) {
+
+ pNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
+
+ pScb = pNpScb->pScb;
+ if ( pScb != NULL ) {
+
+ NwReferenceScb( pNpScb );
+
+ //
+ // Release the SCB spin lock as we are about to touch nonpaged pool.
+ //
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+ if ((Uid == NULL) ||
+ ( pScb->UserUid.QuadPart == (*Uid).QuadPart)) {
+
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ FilesClosed += NwInvalidateAllHandlesForScb( pScb );
+ NwReleaseRcb( &NwRcb );
+
+ if ( IrpContext ) {
+
+ IrpContext->pNpScb = pNpScb;
+ NwLogoffAndDisconnect( IrpContext , pNpScb);
+ NwDequeueIrpContext( IrpContext, FALSE );
+
+ } else {
+
+ //
+ // No IrpContext means that a RAS transition has occurred.
+ // Let's try to keep our Netware servers happy if the net
+ // is still attached.
+ //
+
+ PIRP_CONTEXT LocalIrpContext;
+ if (NwAllocateExtraIrpContext(&LocalIrpContext, pNpScb)) {
+
+ // Lock down so that we can send a packet.
+ NwReferenceUnlockableCodeSection();
+
+ LocalIrpContext->pNpScb = pNpScb;
+ NwLogoffAndDisconnect( LocalIrpContext, pNpScb);
+
+ NwAppendToQueueAndWait( LocalIrpContext );
+
+ NwDequeueIrpContext( LocalIrpContext, FALSE );
+ NwDereferenceUnlockableCodeSection ();
+ NwFreeExtraIrpContext( LocalIrpContext );
+
+ }
+
+ //
+ // Clear the LIP data speed.
+ //
+
+ pNpScb->LipDataSpeed = 0;
+ pNpScb->State = SCB_STATE_ATTACHING;
+
+ }
+
+
+ }
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ NwDereferenceScb( pNpScb );
+ }
+
+ NextScbQueueEntry = pNpScb->ScbLinks.Flink;
+ }
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+ return( FilesClosed );
+}
+
+ULONG
+NwInvalidateAllHandlesForScb (
+ PSCB Scb
+ )
+/*++
+
+Routine Description:
+
+ This routine finds all of the ICB in for an SCB and marks them
+ invalid.
+
+ *** The caller must own the RCB shared or exclusive.
+
+Arguments:
+
+ SCB - A pointer to the SCB whose files are closed.
+
+Return Value:
+
+ The number of files that were closed.
+
+--*/
+
+{
+ PLIST_ENTRY VcbQueueEntry;
+ PLIST_ENTRY FcbQueueEntry;
+ PLIST_ENTRY IcbQueueEntry;
+ PVCB pVcb;
+ PFCB pFcb;
+ PICB pIcb;
+
+ ULONG FilesClosed = 0;
+
+ PAGED_CODE();
+
+ //
+ // Walk the list of VCBs for this SCB
+ //
+
+ for ( VcbQueueEntry = Scb->ScbSpecificVcbQueue.Flink;
+ VcbQueueEntry != &Scb->ScbSpecificVcbQueue;
+ VcbQueueEntry = VcbQueueEntry->Flink ) {
+
+ pVcb = CONTAINING_RECORD( VcbQueueEntry, VCB, VcbListEntry );
+
+ if ( !BooleanFlagOn( pVcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) {
+ pVcb->Specific.Disk.Handle = (CHAR)-1;
+ }
+
+ //
+ // Walk the list of FCBs and DCSs for this VCB
+ //
+
+ for ( FcbQueueEntry = pVcb->FcbList.Flink;
+ FcbQueueEntry != &pVcb->FcbList;
+ FcbQueueEntry = FcbQueueEntry->Flink ) {
+
+ pFcb = CONTAINING_RECORD( FcbQueueEntry, FCB, FcbListEntry );
+
+ //
+ // Walk the list of ICBs for this FCB or DCB
+ //
+
+ for ( IcbQueueEntry = pFcb->IcbList.Flink;
+ IcbQueueEntry != &pFcb->IcbList;
+ IcbQueueEntry = IcbQueueEntry->Flink ) {
+
+ pIcb = CONTAINING_RECORD( IcbQueueEntry, ICB, ListEntry );
+
+ //
+ // Mark the ICB handle invalid.
+ //
+
+ pIcb->State = ICB_STATE_CLOSE_PENDING;
+ pIcb->HasRemoteHandle = FALSE;
+ FilesClosed++;
+ }
+ }
+ }
+
+ return( FilesClosed );
+}
+
+
+VOID
+NwVerifyScb (
+ IN PSCB Scb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine verifies that an SCB is in the opened state.
+ If it is not, the routine raises an exception.
+
+Arguments:
+
+ Scb - A pointer the SCB to verify.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ if ( Scb->pNpScb->State == SCB_STATE_FLAG_SHUTDOWN ) {
+ ExRaiseStatus( STATUS_INVALID_HANDLE );
+ }
+}
+
+
+PFCB
+NwCreateFcb (
+ IN PUNICODE_STRING FileName,
+ IN PSCB Scb,
+ IN PVCB Vcb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine allocates and initialize a new FCB. The FCB is
+ inserted into the RCB prefix table.
+
+ *** This routine must be called with the RCB held exclusively.
+
+Arguments:
+
+ FileName - The name of the file to create.
+
+ Scb - A pointer to the SCB for this file.
+
+ Vcb - A pointer to the VCB for the file.
+
+Return Value:
+
+ FCB - A pointer to the newly created DCB.
+
+ If memory allocation fails, this routine will raise an exception.
+
+--*/
+
+{
+ PFCB Fcb;
+ PNONPAGED_FCB NpFcb;
+ PWCH FileNameBuffer;
+ SHORT Length;
+
+ PAGED_CODE();
+
+ Fcb = NULL;
+ NpFcb = NULL;
+
+ try {
+
+ //
+ // Allocate and initialize structures.
+ //
+
+ Fcb = ALLOCATE_POOL_EX(
+ PagedPool,
+ sizeof( FCB ) + FileName->Length + sizeof(WCHAR));
+
+ RtlZeroMemory( Fcb, sizeof( FCB ) );
+ Fcb->NodeTypeCode = NW_NTC_FCB;
+ Fcb->NodeByteSize = sizeof( FCB ) + FileName->Length;
+ Fcb->State = FCB_STATE_OPEN_PENDING;
+
+ InitializeListHead( &Fcb->IcbList );
+
+ Fcb->Vcb = Vcb;
+ Fcb->Scb = Scb;
+
+ FileNameBuffer = (PWCH)(Fcb + 1);
+
+ NpFcb = ALLOCATE_POOL_EX( NonPagedPool, sizeof( NONPAGED_FCB ) );
+ RtlZeroMemory( NpFcb, sizeof( NONPAGED_FCB ) );
+
+ NpFcb->Header.NodeTypeCode = NW_NTC_NONPAGED_FCB;
+ NpFcb->Header.NodeByteSize = sizeof( NONPAGED_FCB );
+
+ NpFcb->Fcb = Fcb;
+ Fcb->NonPagedFcb = NpFcb;
+
+ //
+ // Initialize the resource variable for the FCB.
+ //
+
+ ExInitializeResource( &NpFcb->Resource );
+
+ //
+ // Copy the file name
+ //
+
+ RtlCopyMemory( FileNameBuffer, FileName->Buffer, FileName->Length );
+ Fcb->FullFileName.MaximumLength = FileName->Length;
+ Fcb->FullFileName.Length = FileName->Length;
+ Fcb->FullFileName.Buffer = FileNameBuffer;
+
+ //
+ // The Relative name is normally the full name without the
+ // server and volume name. Also strip the leading backslash.
+ //
+
+ Length = FileName->Length - Vcb->Name.Length - sizeof(L'\\');
+ if ( Length < 0 ) {
+ Length = 0;
+ }
+
+ Fcb->RelativeFileName.Buffer = (PWCH)
+ ((PCHAR)FileNameBuffer + Vcb->Name.Length + sizeof(L'\\'));
+
+ Fcb->RelativeFileName.MaximumLength = Length;
+ Fcb->RelativeFileName.Length = Length;
+
+ //
+ // Insert this file in the prefix table.
+ //
+
+ RtlInsertUnicodePrefix(
+ &NwRcb.FileNameTable,
+ &Fcb->FullFileName,
+ &Fcb->PrefixEntry );
+
+ //
+ // Insert this file into the VCB list, and increment the
+ // file open count.
+ //
+
+ NwReferenceVcb( Vcb );
+
+ InsertTailList(
+ &Vcb->FcbList,
+ &Fcb->FcbListEntry );
+
+ //
+ // Initialize the list of file locks for this FCB.
+ //
+
+ InitializeListHead( &NpFcb->FileLockList );
+ InitializeListHead( &NpFcb->PendingLockList );
+
+ //
+ // Set the long name bit if necessary
+ //
+
+ if ( Fcb->Vcb->Specific.Disk.LongNameSpace != LFN_NO_OS2_NAME_SPACE ) {
+
+ //
+ // OBSCURE CODE POINT
+ //
+ // By default FavourLongNames is not set and we use DOS name
+ // space unless we know we have to use LFN. Reason is if we
+ // start using LFN then DOS apps that dont handle longnames
+ // will give us short names and we are hosed because we are
+ // using LFN NCPs that dont see the short names. Eg. without
+ // the check below, the following will fail (assume mv.exe is
+ // DOS app).
+ //
+ // cd public\longnamedir
+ // mv foo bar
+ //
+ // This is because we will get call with public\longname\foo
+ // and the truncated dir name is not accepted. If user values
+ // case sensitivity, they can set this reg value and we will
+ // use LFN even for short names. They sacrifice the scenario
+ // above.
+ //
+ if ( FavourLongNames || !IsFatNameValid( &Fcb->RelativeFileName ) ) {
+
+ SetFlag( Fcb->Flags, FCB_FLAGS_LONG_NAME );
+ }
+ }
+
+ } finally {
+ if ( AbnormalTermination() ) {
+ if ( Fcb != NULL ) FREE_POOL( Fcb );
+ if ( NpFcb != NULL ) FREE_POOL( NpFcb );
+ }
+ }
+
+ return( Fcb );
+}
+
+
+PFCB
+NwFindFcb (
+ IN PSCB Scb,
+ IN PVCB Vcb,
+ IN PUNICODE_STRING FileName,
+ IN PDCB Dcb OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ This routine find an existing FCB by matching the file name.
+ If a match is find the FCB reference count is incremented.
+ If no match is found an FCB is created.
+
+Arguments:
+
+ Scb - A pointer to the server for this open.
+
+ FileName - The name of the file to find.
+
+ Dcb - A pointer to the DCB for relative opens. If NULL the FileName
+ is an full path name. If non NUL the FileName is relative to
+ this directory.
+
+
+Return Value:
+
+ FCB - A pointer to the found or newly created DCB.
+
+ If memory allocation fails, this routine will raise an exception.
+
+--*/
+
+{
+ PFCB Fcb;
+ PUNICODE_PREFIX_TABLE_ENTRY Prefix;
+ UNICODE_STRING FullName;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwFindFcb\n", 0);
+ ASSERT( Scb->NodeTypeCode == NW_NTC_SCB );
+
+ if ( Dcb == NULL ) {
+
+ MergeStrings( &FullName,
+ &Scb->UnicodeUid,
+ FileName,
+ PagedPool );
+
+ } else {
+
+ //
+ // Construct full name.
+ //
+
+ FullName.Length = Dcb->FullFileName.Length + FileName->Length + 2;
+ FullName.MaximumLength = FullName.Length;
+ FullName.Buffer = ALLOCATE_POOL_EX( PagedPool, FullName.Length );
+
+ RtlCopyMemory(
+ FullName.Buffer,
+ Dcb->FullFileName.Buffer,
+ Dcb->FullFileName.Length );
+
+ FullName.Buffer[ Dcb->FullFileName.Length / sizeof(WCHAR) ] = L'\\';
+
+ RtlCopyMemory(
+ FullName.Buffer + Dcb->FullFileName.Length / sizeof(WCHAR) + 1,
+ FileName->Buffer,
+ FileName->Length );
+ }
+
+ DebugTrace( 0, Dbg, " ->FullName = ""%wZ""\n", &FullName);
+
+ //
+ // Strip the trailing '\' if there is one.
+ //
+
+ if ( FullName.Buffer[ FullName.Length/sizeof(WCHAR) - 1] == L'\\' ) {
+ FullName.Length -= sizeof(WCHAR);
+ }
+
+ Fcb = NULL;
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ Prefix = RtlFindUnicodePrefix( &NwRcb.FileNameTable, &FullName, 0 );
+
+ if ( Prefix != NULL ) {
+ Fcb = CONTAINING_RECORD( Prefix, FCB, PrefixEntry );
+
+ if ( Fcb->FullFileName.Length != FullName.Length ) {
+
+ //
+ // This was not an exact match. Ignore it.
+ // or
+ // This Fcb is for a share owned by another LogonId.
+ //
+
+ Fcb = NULL;
+ }
+
+ }
+
+ try {
+ if ( Fcb != NULL ) {
+ DebugTrace(0, Dbg, "Found existing FCB = %08lx\n", Fcb);
+ } else {
+ Fcb = NwCreateFcb( &FullName, Scb, Vcb );
+ DebugTrace(0, Dbg, "Created new FCB = %08lx\n", Fcb);
+ }
+ } finally {
+
+ if ( FullName.Buffer != NULL ) {
+ FREE_POOL( FullName.Buffer );
+ }
+
+ NwReleaseRcb( &NwRcb );
+ }
+
+ ASSERT( Fcb == NULL || Fcb->Scb == Scb );
+
+ DebugTrace(-1, Dbg, "NwFindFcb\n", 0);
+ return( Fcb );
+}
+
+
+VOID
+NwDereferenceFcb(
+ IN PIRP_CONTEXT IrpContext OPTIONAL,
+ IN PFCB Fcb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine decrement the ICB count for an FCB. If the count
+ goes to zero, cleanup the FCB.
+
+ *** This routine must be called with the RCB held exclusively.
+
+Arguments:
+
+ FCB - A pointer to an FCB.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PNONPAGED_FCB NpFcb;
+ PLIST_ENTRY listEntry, nextListEntry;
+ PNW_FILE_LOCK pFileLock;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwDereferenceFcb\n", 0);
+ DebugTrace(0, Dbg, "New ICB count = %d\n", Fcb->IcbCount-1 );
+
+ ASSERT( NodeType( Fcb ) == NW_NTC_FCB ||
+ NodeType( Fcb ) == NW_NTC_DCB );
+
+ if ( --Fcb->IcbCount == 0 ) {
+
+ NpFcb = Fcb->NonPagedFcb;
+
+ ASSERT( IsListEmpty( &Fcb->IcbList ) );
+
+ //
+ // If there are outstanding locks, clean them up. This
+ // happens when something causes a remote handle to get
+ // closed before the cleanup routine is called by the
+ // ios on the regular close path.
+ //
+
+ if ( !IsListEmpty( &NpFcb->FileLockList ) ) {
+
+ DebugTrace( 0, Dbg, "Freeing stray locks on FCB %08lx\n", NpFcb );
+
+ for ( listEntry = NpFcb->FileLockList.Flink;
+ listEntry != &NpFcb->FileLockList;
+ listEntry = nextListEntry ) {
+
+ nextListEntry = listEntry->Flink;
+
+ pFileLock = CONTAINING_RECORD( listEntry,
+ NW_FILE_LOCK,
+ ListEntry );
+
+ RemoveEntryList( listEntry );
+ FREE_POOL( pFileLock );
+ }
+ }
+
+ if ( !IsListEmpty( &NpFcb->PendingLockList ) ) {
+
+ DebugTrace( 0, Dbg, "Freeing stray pending locks on FCB %08lx\n", NpFcb );
+
+ for ( listEntry = NpFcb->PendingLockList.Flink;
+ listEntry != &NpFcb->PendingLockList;
+ listEntry = nextListEntry ) {
+
+ nextListEntry = listEntry->Flink;
+
+ pFileLock = CONTAINING_RECORD( listEntry,
+ NW_FILE_LOCK,
+ ListEntry );
+
+ RemoveEntryList( listEntry );
+ FREE_POOL( pFileLock );
+ }
+ }
+
+ //
+ // Delete the file now, if it is delete pending.
+ //
+
+ if ( BooleanFlagOn( Fcb->Flags, FCB_FLAGS_DELETE_ON_CLOSE ) ) {
+ NwDeleteFile( IrpContext );
+ }
+
+ //
+ // Remove this file in the prefix table.
+ //
+
+ RtlRemoveUnicodePrefix(
+ &NwRcb.FileNameTable,
+ &Fcb->PrefixEntry );
+
+ //
+ // Remove this file from the SCB list, and decrement the
+ // file open count.
+ //
+
+ RemoveEntryList( &Fcb->FcbListEntry );
+ NwDereferenceVcb( Fcb->Vcb, IrpContext, TRUE );
+
+ //
+ // Delete the resource variable for the FCB.
+ //
+
+ ExDeleteResource( &NpFcb->Resource );
+
+ //
+ // Delete the cache buffer and MDL.
+ //
+
+ if ( NpFcb->CacheBuffer != NULL ) {
+ FREE_POOL( NpFcb->CacheBuffer );
+ FREE_MDL( NpFcb->CacheMdl );
+ }
+
+ //
+ // Finally free the paged and non-paged memory
+ //
+
+ FREE_POOL( Fcb );
+ FREE_POOL( NpFcb );
+ }
+
+ DebugTrace(-1, Dbg, "NwDereferenceFcb\n", 0);
+}
+
+
+PVCB
+NwFindVcb (
+ IN PIRP_CONTEXT IrpContext,
+ IN PUNICODE_STRING VolumeName,
+ IN ULONG ShareType,
+ IN WCHAR DriveLetter,
+ IN BOOLEAN ExplicitConnection,
+ IN BOOLEAN FindExisting
+ )
+
+/*++
+
+Routine Description:
+
+ This routine looks for a VCB structure. If one is found, it
+ is referenced and a pointer is returned. If no VCB is found, an
+ attempt is made to connect to the named volume and to create a VCB.
+
+Arguments:
+
+ IrpContext - A pointer to the IRP context block for this request.
+
+ VolumeName - The minimum name of the volume. This will be in one of
+ the following forms:
+
+ \SERVER\SHARE UNC open server volume
+ \TREE\VOLUME UNC open tree volume in current context
+ \TREE\PATH.TO.VOLUME UNC open distinguished tree volume
+
+ \X:\SERVER\SHARE tree connect server volume
+ \X:\TREE\VOLUME tree connect tree volume in current context
+ \X:\TREE\PATH.TO.VOLUME tree connect distinguished tree volume
+
+ ShareType - The type of the share to find.
+
+ DriveLetter - The drive letter to find. A - Z for drive letter, 1 - 9
+ for LPT ports or 0 if none.
+
+ ExplicitConnection - If TRUE, the caller is make an explicit connection
+ to this Volume. If FALSE, this is an implicit connection made by
+ a UNC operation.
+
+Return Value:
+
+ VCB - Pointer to a found or newly created VCB.
+
+--*/
+{
+ PVCB Vcb = NULL;
+ BOOLEAN OwnRcb = TRUE;
+ PUNICODE_PREFIX_TABLE_ENTRY Prefix;
+ UNICODE_STRING UidVolumeName;
+ PNONPAGED_SCB pNpScb = IrpContext->pScb->pNpScb;
+
+ PAGED_CODE();
+
+ UidVolumeName.Buffer = NULL;
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ try {
+
+ MergeStrings( &UidVolumeName,
+ &IrpContext->pScb->UnicodeUid,
+ VolumeName,
+ PagedPool );
+
+ DebugTrace(+1, Dbg, "NwFindVcb %wZ\n", &UidVolumeName );
+
+ if ( DriveLetter != 0 ) {
+
+ //
+ // This is a drive relative path. Look up the drive letter.
+ //
+
+ ASSERT( ( DriveLetter >= L'A' && DriveLetter <= L'Z' ) ||
+ ( DriveLetter >= L'1' && DriveLetter <= L'9' ) );
+
+ if ( DriveLetter >= L'A' && DriveLetter <= L'Z' ) {
+ Vcb = DriveMapTable[DriveLetter - L'A'];
+ } else {
+ Vcb = DriveMapTable[MAX_DISK_REDIRECTIONS + DriveLetter - L'1'];
+ }
+
+ //
+ // Was the Vcb created for this user?
+ //
+
+ if ((Vcb != NULL) &&
+ (IrpContext->Specific.Create.UserUid.QuadPart != Vcb->Scb->UserUid.QuadPart )) {
+
+ ExRaiseStatus( STATUS_ACCESS_DENIED );
+ }
+
+ } else {
+
+ //
+ // This is a UNC path. Look up the path name.
+ //
+
+ Prefix = RtlFindUnicodePrefix( &NwRcb.VolumeNameTable, &UidVolumeName, 0 );
+
+ if ( Prefix != NULL ) {
+ Vcb = CONTAINING_RECORD( Prefix, VCB, PrefixEntry );
+
+ if ( Vcb->Name.Length != UidVolumeName.Length ) {
+
+ //
+ // This was not an exact match. Ignore it.
+ //
+
+ Vcb = NULL;
+ }
+ }
+ }
+
+ if ( Vcb != NULL ) {
+
+ //
+ // If this is an explicit use to a UNC path, we may find an
+ // existing VCB structure. Mark this structure, and reference it.
+ //
+
+ if ( !BooleanFlagOn( Vcb->Flags, VCB_FLAG_EXPLICIT_CONNECTION ) &&
+ ExplicitConnection ) {
+
+ NwReferenceVcb( Vcb );
+ SetFlag( Vcb->Flags, VCB_FLAG_EXPLICIT_CONNECTION );
+ SetFlag( Vcb->Flags, VCB_FLAG_DELETE_IMMEDIATELY );
+
+ //
+ // Count this as an open file on the SCB.
+ //
+
+ ++Vcb->Scb->OpenFileCount;
+ }
+
+ NwReferenceVcb( Vcb );
+ DebugTrace(0, Dbg, "Found existing VCB = %08lx\n", Vcb);
+
+ //
+ // If this VCB is queued to a different SCB as may
+ // happen when we are resolving NDS UNC names, we
+ // need to re-point the irpcontext at the correct SCB.
+ // We can't hold the RCB or the open lock while we do
+ // this!
+ //
+ // It is ok to release the open lock since we know
+ // that we have an already created VCB and that we're
+ // not creating a new vcb.
+ //
+
+ if ( Vcb->Scb != IrpContext->pScb ) {
+
+ NwReferenceScb( Vcb->Scb->pNpScb );
+
+ NwReleaseOpenLock( );
+
+ NwReleaseRcb( &NwRcb );
+ OwnRcb = FALSE;
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwDereferenceScb( IrpContext->pNpScb );
+
+ IrpContext->pScb = Vcb->Scb;
+ IrpContext->pNpScb = Vcb->Scb->pNpScb;
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ NwAcquireOpenLock( );
+
+ }
+
+ } else if ( !FindExisting ) {
+
+ //
+ // Can't hold the RCB while creating a new VCB.
+ //
+
+ NwReleaseRcb( &NwRcb );
+ OwnRcb = FALSE;
+
+ Vcb = NwCreateVcb(
+ IrpContext,
+ IrpContext->pScb,
+ &UidVolumeName,
+ ShareType,
+ DriveLetter,
+ ExplicitConnection );
+
+ if ( Vcb ) {
+ DebugTrace(0, Dbg, "Created new VCB = %08lx\n", Vcb);
+ }
+
+ } else {
+
+ //
+ // If we didn't find anything and don't want
+ // to do a create, make sure the caller doesn't
+ // try to process the nds path.
+ //
+
+ IrpContext->Specific.Create.NeedNdsData = FALSE;
+ }
+
+ } finally {
+
+ if ( OwnRcb ) {
+ NwReleaseRcb( &NwRcb );
+ }
+
+ if (UidVolumeName.Buffer != NULL) {
+ FREE_POOL( UidVolumeName.Buffer );
+ }
+ }
+
+ DebugTrace(-1, Dbg, "NwFindVcb\n", 0);
+ return( Vcb );
+
+}
+
+PVCB
+NwCreateVcb (
+ IN PIRP_CONTEXT IrpContext,
+ IN PSCB Scb,
+ IN PUNICODE_STRING VolumeName,
+ IN ULONG ShareType,
+ IN WCHAR DriveLetter,
+ IN BOOLEAN ExplicitConnection
+ )
+
+/*++
+
+Routine Description:
+
+ This routine allocates and initialize a new VCB. The
+ workstation tries to connect to the Volume. If successful
+ it creates a VCB and it is inserted into the volume
+ prefix table.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information.
+
+ Scb - A pointer to the SCB for this volume.
+
+ VolumeName - The name of the volume to create.
+
+ ShareType - The type of share to create.
+
+ DriveLetter - The drive letter assigned to this volume, or 0 if none.
+
+ ExplicitConnection - TRUE if we are creating this VCB due to an
+ add connection request. FALSE if we are creating the VCB to
+ service a UNC request.
+
+Return Value:
+
+ VCB - A pointer to the newly created DCB.
+ NULL - Could not create a DCB, or failed to connect to the volume.
+
+--*/
+
+{
+ PVCB Vcb;
+ PWCH VolumeNameBuffer;
+ PWCH ShareNameBuffer;
+ PWCH ConnectNameBuffer;
+ UCHAR DirectoryHandle;
+ ULONG QueueId;
+ BYTE *pbQueue, *pbRQueue;
+ BOOLEAN PrintQueue = FALSE;
+ NTSTATUS Status;
+ CHAR LongNameSpace = LFN_NO_OS2_NAME_SPACE;
+ CHAR VolumeNumber = -1;
+ CHAR DriveNumber = 0;
+ USHORT PreludeLength, ConnectNameLength;
+ PNONPAGED_SCB NpScb = Scb->pNpScb;
+
+ UNICODE_STRING ShareName;
+ UNICODE_STRING LongShareName;
+ PWCH p;
+
+ BOOLEAN InsertedColon;
+ BOOLEAN LongName = FALSE;
+ BOOLEAN LicensedConnection = FALSE;
+
+ PUNICODE_STRING puConnectName;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwCreateVcb\n", 0);
+ DebugTrace( 0, Dbg, " ->Server = %wZ\n", &NpScb->ServerName );
+ DebugTrace( 0, Dbg, " ->VolumeName = %wZ\n", VolumeName );
+ DebugTrace( 0, Dbg, " ->DriveLetter = %x\n", DriveLetter );
+
+ Vcb = NULL;
+ ShareName.Buffer = NULL;
+
+ if ( IrpContext != NULL &&
+ IrpContext->Specific.Create.NdsCreate ) {
+
+ //
+ // If we don't have the NDS data for this create, bail out
+ // and have the create thread get the data before re-attempting
+ // the create. This is kind of weird, but we have to do it
+ // so that we handle the open lock correctly and prevent
+ // duplicate creates.
+ //
+
+ if ( IrpContext->Specific.Create.NeedNdsData ) {
+ DebugTrace( -1, Dbg, "NwCreateVcb: Need NDS data to continue.\n", 0 );
+ return NULL;
+ }
+
+ ConnectNameLength = IrpContext->Specific.Create.UidConnectName.Length;
+ puConnectName = &IrpContext->Specific.Create.UidConnectName;
+
+ } else {
+
+ puConnectName = VolumeName;
+ ConnectNameLength = 0;
+ }
+
+ DebugTrace( 0, Dbg, " ->ConnectName = %wZ\n", puConnectName );
+
+ if ( IrpContext != NULL) {
+
+ //
+ // Build the share name from the volume name.
+ //
+ // The share name will either be 'volume:' or 'volume:path\path'
+ //
+
+ //
+ // Allocate space for the share name buffer, and copy the volume
+ // name to the share name buffer, skipping the server name and
+ // the leading backslash.
+ //
+
+ if ( DriveLetter >= L'A' && DriveLetter <= L'Z' ) {
+
+ if ( ShareType == RESOURCETYPE_PRINT ) {
+ ExRaiseStatus( STATUS_BAD_NETWORK_PATH );
+ } else if ( ShareType == RESOURCETYPE_ANY) {
+ ShareType = RESOURCETYPE_DISK;
+ }
+
+ PreludeLength = Scb->UidServerName.Length +
+ sizeof( L"X:") + sizeof(WCHAR);
+
+ } else if ( DriveLetter >= L'1' && DriveLetter <= L'9' ) {
+
+ if ( ShareType == RESOURCETYPE_DISK ) {
+ ExRaiseStatus( STATUS_BAD_NETWORK_PATH );
+ } else if ( ShareType == RESOURCETYPE_ANY) {
+ ShareType = RESOURCETYPE_PRINT;
+ }
+
+ PreludeLength = Scb->UidServerName.Length +
+ sizeof( L"LPTX") + sizeof(WCHAR);
+
+ } else {
+ PreludeLength = Scb->UidServerName.Length + sizeof(WCHAR);
+ }
+
+ //
+ // Quick check for bogus volume name.
+ //
+
+ if ( puConnectName->Length <= PreludeLength ) {
+ ExRaiseStatus( STATUS_BAD_NETWORK_PATH );
+ }
+
+ //
+ // Clip the NDS share name at the appropriate spot.
+ //
+
+ if ( IrpContext->Specific.Create.NdsCreate ) {
+ ShareName.Length = (USHORT)IrpContext->Specific.Create.dwNdsShareLength;
+ } else {
+ ShareName.Length = puConnectName->Length - PreludeLength;
+ }
+
+ ShareName.Buffer = ALLOCATE_POOL_EX( PagedPool, ShareName.Length + sizeof(WCHAR) );
+
+ RtlMoveMemory(
+ ShareName.Buffer,
+ puConnectName->Buffer + PreludeLength / sizeof(WCHAR),
+ ShareName.Length );
+
+ ShareName.MaximumLength = ShareName.Length;
+
+ DebugTrace( 0, Dbg, " ->ServerShare = %wZ\n", &ShareName );
+
+ //
+ // Create a long share name.
+ //
+
+ LongShareName.Length = ShareName.Length;
+ LongShareName.Buffer = puConnectName->Buffer + PreludeLength / sizeof(WCHAR);
+
+ //
+ // Now scan the share name for the 1st slash.
+ //
+
+ InsertedColon = FALSE;
+
+ for ( p = ShareName.Buffer; p < ShareName.Buffer + ShareName.Length/sizeof(WCHAR); p++ ) {
+ if ( *p == L'\\') {
+ *p = L':';
+ InsertedColon = TRUE;
+ break;
+ }
+ }
+
+ if ( !InsertedColon ) {
+
+ //
+ // We need to append a column to generate the share name.
+ // Since we already allocated an extra WCHAR of buffer space,
+ // just append the ':' to the share name.
+ //
+
+ ShareName.Buffer[ShareName.Length / sizeof(WCHAR)] = L':';
+ ShareName.Length += 2;
+ }
+
+ ASSERT( ShareType == RESOURCETYPE_ANY ||
+ ShareType == RESOURCETYPE_DISK ||
+ ShareType == RESOURCETYPE_PRINT );
+
+ //
+ // If there are no vcb's and no nds streams connected to this scb and
+ // this is a Netware 4.x server that is NDS authenticated, then we
+ // haven't yet licensed this connection and we should do so.
+ //
+
+ if ( ( IrpContext->pScb->MajorVersion > 3 ) &&
+ ( IrpContext->pScb->UserName.Length == 0 ) &&
+ ( IrpContext->pScb->VcbCount == 0 ) &&
+ ( IrpContext->pScb->OpenNdsStreams == 0 ) ) {
+
+ Status = NdsLicenseConnection( IrpContext );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ ExRaiseStatus( STATUS_REMOTE_SESSION_LIMIT );
+ }
+
+ LicensedConnection = TRUE;
+ }
+
+ if ( ShareType == RESOURCETYPE_ANY ||
+ ShareType == RESOURCETYPE_DISK ) {
+
+ GetLongNameSpaceForVolume(
+ IrpContext,
+ ShareName,
+ &LongNameSpace,
+ &VolumeNumber );
+
+ //
+ // BUGBUG: If this is the deref of a directory map, the path we have
+ // been provided is the short name space path. We have to get the
+ // long name path to connect up the long name space for the user!
+ //
+
+ if ( ( IrpContext->Specific.Create.NdsCreate ) &&
+ ( IrpContext->Specific.Create.dwNdsObjectType == NDS_OBJECTTYPE_DIRMAP ) ) {
+ LongNameSpace = LFN_NO_OS2_NAME_SPACE;
+ }
+
+ //
+ // Try to get a permanent handle to the volume.
+ //
+
+ if ( LongNameSpace == LFN_NO_OS2_NAME_SPACE ) {
+
+ DriveNumber = GetNewDriveNumber(Scb);
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "SbbJ",
+ NCP_DIR_FUNCTION, NCP_ALLOCATE_DIR_HANDLE,
+ 0,
+ DriveNumber,
+ &ShareName );
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nb",
+ &DirectoryHandle );
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+ FreeDriveNumber( Scb, DriveNumber );
+ }
+
+ } else {
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "LbbWbDbC",
+ NCP_LFN_ALLOCATE_DIR_HANDLE,
+ LongNameSpace,
+ 0,
+ 0, // Mode = permanent
+ VolumeNumber,
+ LFN_FLAG_SHORT_DIRECTORY,
+ 0xFF, // Flag
+ &LongShareName );
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nb",
+ &DirectoryHandle );
+ }
+
+ //
+ // WARNING. See comment towards end of NwCreateFcb() !!!
+ //
+ if ( FavourLongNames || !IsFatNameValid( &LongShareName ) ) {
+ LongName = TRUE;
+ }
+ }
+
+ if ( ( Status == STATUS_NO_SUCH_DEVICE ) &&
+ ( ShareType != RESOURCETYPE_ANY ) ) {
+
+ //
+ // Asked for disk and it failed. If its ANY, then try print.
+ //
+
+ if (DriveNumber) {
+ FreeDriveNumber( Scb, DriveNumber );
+ }
+
+ FREE_POOL( ShareName.Buffer );
+
+ if ( LicensedConnection ) {
+ NdsUnlicenseConnection( IrpContext );
+ }
+
+ ExRaiseStatus( STATUS_BAD_NETWORK_NAME );
+ return( NULL );
+ }
+
+ }
+
+ if ( ShareType == RESOURCETYPE_PRINT ||
+ ( ShareType == RESOURCETYPE_ANY && !NT_SUCCESS( Status ) ) ) {
+
+ //
+ // Try to connect to a print queue. If this is a bindery
+ // server or an nds server with bindery emulation, we scan
+ // the bindery for the QueueId. Otherwise, the QueueId is
+ // simply the ds object id with the byte ordering reversed.
+ //
+
+ ShareName.Length -= sizeof(WCHAR);
+
+ if ( ( Scb->MajorVersion < 4 ) ||
+ ( !( IrpContext->Specific.Create.NdsCreate ) ) ) {
+
+ Status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "SdwJ", // Format string
+ NCP_ADMIN_FUNCTION, NCP_SCAN_BINDERY_OBJECT,
+ -1, // Previous ID
+ OT_PRINT_QUEUE,
+ &ShareName ); // Queue Name
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nd",
+ &QueueId );
+ }
+
+ } else {
+
+ if ( IrpContext->Specific.Create.dwNdsObjectType == NDS_OBJECTTYPE_QUEUE ) {
+
+ DebugTrace( 0, Dbg, "Mapping NDS print queue %08lx\n",
+ IrpContext->Specific.Create.dwNdsOid );
+
+ pbQueue = (BYTE *)&IrpContext->Specific.Create.dwNdsOid;
+ pbRQueue = (BYTE *)&QueueId;
+
+ pbRQueue[0] = pbQueue[3];
+ pbRQueue[1] = pbQueue[2];
+ pbRQueue[2] = pbQueue[1];
+ pbRQueue[3] = pbQueue[0];
+
+ Status = STATUS_SUCCESS;
+
+ } else {
+
+ DebugTrace( 0, Dbg, "Nds object is not a print queue.\n", 0 );
+ Status = STATUS_UNSUCCESSFUL;
+ }
+ }
+
+ PrintQueue = TRUE;
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+
+ if (DriveNumber) {
+ FreeDriveNumber( Scb, DriveNumber );
+ }
+
+ FREE_POOL( ShareName.Buffer );
+
+ if ( LicensedConnection ) {
+ NdsUnlicenseConnection( IrpContext );
+ }
+
+ ExRaiseStatus( STATUS_BAD_NETWORK_PATH );
+ return( NULL );
+ }
+
+ } else {
+ DirectoryHandle = 1;
+ }
+
+ //
+ // Allocate and initialize structures.
+ //
+
+ try {
+
+ Vcb = ALLOCATE_POOL_EX( PagedPool, sizeof( VCB ) + // vcb
+ VolumeName->Length + // volume name
+ ShareName.Length + // share name
+ ConnectNameLength ); // connect name
+
+ RtlZeroMemory( Vcb, sizeof( VCB ) );
+ Vcb->NodeTypeCode = NW_NTC_VCB;
+ Vcb->NodeByteSize = sizeof( VCB ) +
+ VolumeName->Length +
+ ShareName.Length +
+ ConnectNameLength;
+
+ InitializeListHead( &Vcb->FcbList );
+
+ VolumeNameBuffer = (PWCH)(Vcb + 1);
+ ShareNameBuffer = (PWCH)((PCHAR)VolumeNameBuffer + VolumeName->Length);
+ ConnectNameBuffer = (PWCH)((PCHAR)ShareNameBuffer + ShareName.Length);
+
+ Vcb->Reference = 1;
+
+ //
+ // Copy the volume name
+ //
+
+ RtlCopyMemory( VolumeNameBuffer, VolumeName->Buffer, VolumeName->Length );
+ Vcb->Name.MaximumLength = VolumeName->Length;
+ Vcb->Name.Length = VolumeName->Length;
+ Vcb->Name.Buffer = VolumeNameBuffer;
+
+ //
+ // Copy the share name
+ //
+
+ if ( IrpContext != NULL) {
+
+ RtlCopyMemory( ShareNameBuffer, ShareName.Buffer, ShareName.Length );
+ Vcb->ShareName.MaximumLength = ShareName.Length;
+ Vcb->ShareName.Length = ShareName.Length;
+ Vcb->ShareName.Buffer = ShareNameBuffer;
+
+ }
+
+ //
+ // Copy the connect name
+ //
+
+ if ( ConnectNameLength ) {
+
+ RtlCopyMemory( ConnectNameBuffer,
+ IrpContext->Specific.Create.UidConnectName.Buffer,
+ IrpContext->Specific.Create.UidConnectName.Length );
+ Vcb->ConnectName.MaximumLength = IrpContext->Specific.Create.UidConnectName.Length;
+ Vcb->ConnectName.Length = IrpContext->Specific.Create.UidConnectName.Length;
+ Vcb->ConnectName.Buffer = ConnectNameBuffer;
+
+ }
+
+ if ( ExplicitConnection ) {
+
+ //
+ // Bump the reference count to account for this drive being
+ // mapped via an explicit connection.
+ //
+
+ NwReferenceVcb( Vcb );
+ SetFlag( Vcb->Flags, VCB_FLAG_EXPLICIT_CONNECTION );
+ SetFlag( Vcb->Flags, VCB_FLAG_DELETE_IMMEDIATELY );
+
+ }
+
+ if ( LongName ) {
+ SetFlag( Vcb->Flags, VCB_FLAG_LONG_NAME );
+ }
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ if ( DriveLetter != 0) {
+
+ //
+ // Insert this VCB in the drive map table.
+ //
+
+ if ( DriveLetter >= 'A' && DriveLetter <= 'Z' ) {
+ DriveMapTable[DriveLetter - 'A'] = Vcb;
+ } else {
+ DriveMapTable[MAX_DISK_REDIRECTIONS + DriveLetter - '1'] = Vcb;
+ }
+
+ Vcb->DriveLetter = DriveLetter;
+
+ } else {
+
+ //
+ // Insert this VCB in the prefix table.
+ //
+
+ RtlInsertUnicodePrefix(
+ &NwRcb.VolumeNameTable,
+ &Vcb->Name,
+ &Vcb->PrefixEntry );
+ }
+
+ //
+ // Add this VCB to the global list.
+ //
+
+ InsertTailList( &GlobalVcbList, &Vcb->GlobalVcbListEntry );
+ Vcb->SequenceNumber = CurrentVcbEntry++;
+
+ //
+ // Insert this VCB in the per SCB list
+ //
+
+ Vcb->Scb = Scb;
+ InsertTailList( &Scb->ScbSpecificVcbQueue, &Vcb->VcbListEntry );
+ ++Scb->VcbCount;
+ NwReferenceScb( Scb->pNpScb );
+
+ if ( ExplicitConnection ) {
+
+ //
+ // Count this as an open file on the SCB.
+ //
+
+ ++Vcb->Scb->OpenFileCount;
+ }
+
+ if ( !PrintQueue) {
+
+ PLIST_ENTRY VcbQueueEntry;
+ PVCB pVcb;
+
+ Vcb->Specific.Disk.Handle = DirectoryHandle;
+ Vcb->Specific.Disk.LongNameSpace = LongNameSpace;
+ Vcb->Specific.Disk.VolumeNumber = VolumeNumber;
+ Vcb->Specific.Disk.DriveNumber = DriveNumber;
+
+ //
+ // Appears that some servers can reuse the same permanent drive handle.
+ // if this happens we want to make the old handle invalid otherwise
+ // we will keep on using the new volume as if its the old one.
+ //
+
+ for ( VcbQueueEntry = Scb->ScbSpecificVcbQueue.Flink;
+ VcbQueueEntry != &Scb->ScbSpecificVcbQueue;
+ VcbQueueEntry = pVcb->VcbListEntry.Flink ) {
+
+ pVcb = CONTAINING_RECORD( VcbQueueEntry, VCB, VcbListEntry );
+
+ if ( !BooleanFlagOn( pVcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) {
+
+ if (( pVcb->Specific.Disk.Handle == DirectoryHandle ) &&
+ ( pVcb->Specific.Disk.VolumeNumber != VolumeNumber )) {
+ // Invalidate the old handle
+ pVcb->Specific.Disk.Handle = (CHAR)-1;
+
+ // We could assume that the new one is correct but I don't think we will....
+ Vcb->Specific.Disk.Handle = (CHAR)-1;
+ break;
+ }
+ }
+ }
+
+ } else {
+ SetFlag( Vcb->Flags, VCB_FLAG_PRINT_QUEUE );
+ Vcb->Specific.Print.QueueId = QueueId;
+ }
+
+ NwReleaseRcb( &NwRcb );
+
+ } finally {
+
+ if ( AbnormalTermination() ) {
+
+ if ( Vcb != NULL ) FREE_POOL( Vcb );
+
+ if ( LicensedConnection ) {
+ NdsUnlicenseConnection( IrpContext );
+ }
+ }
+
+ if ( ShareName.Buffer != NULL ) {
+ FREE_POOL( ShareName.Buffer );
+ }
+
+ DebugTrace(-1, Dbg, "NwCreateVcb %lx\n", Vcb);
+ }
+
+ return( Vcb );
+}
+
+VOID
+NwReopenVcbHandlesForScb (
+ IN PIRP_CONTEXT IrpContext,
+ IN PSCB Scb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine reopens VCB handles after the autoreconnects to a server.
+
+ *** This IrpContext must already be at the head of the SCB queue.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information.
+
+ Scb - A pointer to the SCB for this volume.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PLIST_ENTRY VcbQueueEntry, NextVcbQueueEntry;
+ PVCB pVcb;
+
+ PLIST_ENTRY FcbQueueEntry;
+ PLIST_ENTRY IcbQueueEntry;
+ PFCB pFcb;
+ PICB pIcb;
+
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ //
+ // Walk the list of VCBs for this SCB
+ //
+
+ for ( VcbQueueEntry = Scb->ScbSpecificVcbQueue.Flink;
+ VcbQueueEntry != &Scb->ScbSpecificVcbQueue;
+ VcbQueueEntry = NextVcbQueueEntry ) {
+
+ pVcb = CONTAINING_RECORD( VcbQueueEntry, VCB, VcbListEntry );
+
+ if ( pVcb->Specific.Disk.Handle != 1 ) {
+
+ //
+ // Skip reconnecting SYS:LOGIN, since we get it for free.
+ //
+
+ //
+ // Reference the VCB so it can't disappear on us, then release
+ // the RCB.
+ //
+
+ NwReferenceVcb( pVcb );
+ NwReleaseRcb( &NwRcb );
+
+ //
+ // Try to get a permanent handle to the volume.
+ //
+
+ if ( BooleanFlagOn( pVcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) {
+
+ Status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "SdwU", // Format string
+ NCP_ADMIN_FUNCTION, NCP_SCAN_BINDERY_OBJECT,
+ -1, // Previous ID
+ OT_PRINT_QUEUE,
+ &pVcb->ShareName ); // Queue Name
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nd",
+ &pVcb->Specific.Print.QueueId );
+ }
+
+ } else {
+
+ NwReopenVcbHandle( IrpContext, pVcb);
+
+ }
+
+
+ //
+ // Setup for the next loop iteration.
+ //
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ //
+ // Walk the list of DCSs for this VCB and make them all valid.
+ //
+
+ for ( FcbQueueEntry = pVcb->FcbList.Flink;
+ FcbQueueEntry != &pVcb->FcbList;
+ FcbQueueEntry = FcbQueueEntry->Flink ) {
+
+ pFcb = CONTAINING_RECORD( FcbQueueEntry, FCB, FcbListEntry );
+
+ if ( pFcb->NodeTypeCode == NW_NTC_DCB ) {
+
+ //
+ // Walk the list of ICBs for this FCB or DCB
+ //
+
+ for ( IcbQueueEntry = pFcb->IcbList.Flink;
+ IcbQueueEntry != &pFcb->IcbList;
+ IcbQueueEntry = IcbQueueEntry->Flink ) {
+
+ pIcb = CONTAINING_RECORD( IcbQueueEntry, ICB, ListEntry );
+
+ //
+ // Mark the ICB handle invalid.
+ //
+
+ pIcb->State = ICB_STATE_OPENED;
+ }
+ }
+ }
+
+ }
+
+ NextVcbQueueEntry = VcbQueueEntry->Flink;
+
+ if ( pVcb->Specific.Disk.Handle != 1 ) {
+ NwDereferenceVcb( pVcb, NULL, TRUE );
+ }
+
+ }
+
+ NwReleaseRcb( &NwRcb );
+}
+
+VOID
+NwReopenVcbHandle(
+ IN PIRP_CONTEXT IrpContext,
+ IN PVCB Vcb
+ )
+
+/*++
+
+Routine Description:
+
+ This routine reopens a VCB handle after it appears that the server
+ may have dismounted and remounted the volume.
+
+ *** This IrpContext must already be at the head of the SCB queue.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information.
+
+ Vcb - A pointer to the VCB for this volume.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ PAGED_CODE();
+
+ ASSERT( Vcb->Scb->pNpScb->Requests.Flink == &IrpContext->NextRequest );
+
+ if ( Vcb->Specific.Disk.LongNameSpace == LFN_NO_OS2_NAME_SPACE ) {
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "SbbJ",
+ NCP_DIR_FUNCTION, NCP_ALLOCATE_DIR_HANDLE,
+ 0,
+ Vcb->Specific.Disk.DriveNumber,
+ &Vcb->ShareName );
+
+ } else {
+ UNICODE_STRING Name;
+
+ PWCH thisChar, lastChar;
+
+ Status = DuplicateUnicodeStringWithString (
+ &Name,
+ &Vcb->ShareName,
+ PagedPool);
+
+ if ( !NT_SUCCESS( Status ) ) {
+ // Not much we can do now.
+ return;
+ }
+
+ thisChar = Name.Buffer;
+ lastChar = &Name.Buffer[ Name.Length / sizeof(WCHAR) ];
+
+ //
+ // Change the : to a backslash so that FormatMessage works
+ //
+
+ while ( thisChar < lastChar ) {
+ if (*thisChar == L':' ) {
+ *thisChar = L'\\';
+ break;
+ }
+ thisChar++;
+ }
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "LbbWbDbC",
+ NCP_LFN_ALLOCATE_DIR_HANDLE,
+ Vcb->Specific.Disk.LongNameSpace,
+ 0,
+ 0, // Mode = permanent
+ Vcb->Specific.Disk.VolumeNumber,
+ LFN_FLAG_SHORT_DIRECTORY,
+ 0xFF, // Flag
+ &Name );
+
+ if ( Name.Buffer != NULL ) {
+ FREE_POOL( Name.Buffer );
+ }
+
+ }
+
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nb",
+ &Vcb->Specific.Disk.Handle );
+ }
+
+ if ( !NT_SUCCESS( Status ) ) {
+ Vcb->Specific.Disk.Handle = (CHAR)-1;
+ } else {
+
+ PLIST_ENTRY VcbQueueEntry;
+ PVCB pVcb;
+
+ //
+ // Appears that some servers can reuse the same permanent drive handle.
+ // if this happens we want to make the old handle invalid otherwise
+ // we will keep on using the new volume as if its the old one.
+ //
+ // Note that we reach the scb pointer from the npscb pointer because
+ // the scb pointer isn't always valid. These few cases where only one
+ // pointer is set should be found and fixed.
+ //
+
+ for ( VcbQueueEntry = IrpContext->pNpScb->pScb->ScbSpecificVcbQueue.Flink;
+ VcbQueueEntry != &IrpContext->pNpScb->pScb->ScbSpecificVcbQueue;
+ VcbQueueEntry = pVcb->VcbListEntry.Flink ) {
+
+ pVcb = CONTAINING_RECORD( VcbQueueEntry, VCB, VcbListEntry );
+
+ if ( !BooleanFlagOn( pVcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) {
+
+ if (( pVcb->Specific.Disk.Handle == Vcb->Specific.Disk.Handle ) &&
+ ( pVcb->Specific.Disk.VolumeNumber != Vcb->Specific.Disk.VolumeNumber )) {
+ // Invalidate the old handle
+ pVcb->Specific.Disk.Handle = (CHAR)-1;
+
+ // We could assume that the new one is correct but I don't think we will....
+ Vcb->Specific.Disk.Handle = (CHAR)-1;
+ break;
+ }
+ }
+ }
+ }
+
+}
+#ifdef NWDBG
+
+VOID
+NwReferenceVcb (
+ IN PVCB Vcb
+ )
+/*++
+
+Routine Description:
+
+ This routine increments the FCB count for a VCB.
+
+Arguments:
+
+ VCB - A pointer to an VCB.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwReferenceVcb %08lx\n", Vcb);
+ DebugTrace(0, Dbg, "Current Reference count = %d\n", Vcb->Reference );
+
+ ASSERT( NodeType( Vcb ) == NW_NTC_VCB );
+
+ ++Vcb->Reference;
+
+}
+#endif
+
+
+VOID
+NwDereferenceVcb (
+ IN PVCB Vcb,
+ IN PIRP_CONTEXT IrpContext OPTIONAL,
+ IN BOOLEAN OwnRcb
+ )
+/*++
+
+Routine Description:
+
+ This routine decrement the FCB count for a VCB.
+ If the count goes to zero, we record the time. The scavenger
+ thread will cleanup delete the VCB if it remains idle.
+
+ This routine may be called with the RCB owned and the irpcontext
+ at the head of the queue. Be careful when dequeueing the irp
+ context or acquiring any resources!
+
+Arguments:
+
+ VCB - A pointer to an VCB.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PSCB Scb = Vcb->Scb;
+ PNONPAGED_SCB pOrigNpScb = NULL;
+
+#ifdef NWDBG
+ BOOLEAN OwnRcbExclusive = FALSE;
+#endif
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwDereferenceVcb %08lx\n", Vcb);
+
+ ASSERT( NodeType( Vcb ) == NW_NTC_VCB );
+
+#ifdef NWDBG
+
+ //
+ // A little extra lock checking.
+ //
+
+ OwnRcbExclusive = ExIsResourceAcquiredExclusiveLite( &(NwRcb.Resource) );
+
+ if ( OwnRcb ) {
+ ASSERT( OwnRcbExclusive );
+ } else {
+ ASSERT( !OwnRcbExclusive );
+ }
+
+#endif
+
+ //
+ // We have to get to the right scb queue before doing this
+ // so that CleanupVcb unlicenses the correct connection.
+ //
+
+ if ( ( IrpContext ) &&
+ ( IrpContext->pNpScb->pScb->MajorVersion > 3 ) &&
+ ( IrpContext->pNpScb != Scb->pNpScb ) ) {
+
+ if ( OwnRcb ) {
+ NwReleaseRcb( &NwRcb );
+ }
+
+ pOrigNpScb = IrpContext->pNpScb;
+ ASSERT( pOrigNpScb != NULL );
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+
+ IrpContext->pScb = Scb;
+ IrpContext->pNpScb = Scb->pNpScb;
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ //
+ // If the caller owned the RCB, we have to make sure
+ // we re-acquire the RCB reference that we freed for
+ // them so that they don't lose access to the resource
+ // too early.
+ //
+
+ if ( OwnRcb ) {
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ }
+
+ }
+
+ //
+ // Acquire the lock to protect the Reference count.
+ //
+
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ DebugTrace(0, Dbg, "Current Reference count = %d\n", Vcb->Reference );
+ --Vcb->Reference;
+
+ if ( Vcb->Reference == 0 ) {
+ if ( !BooleanFlagOn( Vcb->Flags, VCB_FLAG_DELETE_IMMEDIATELY ) ||
+ IrpContext == NULL ) {
+
+ //
+ // Either this is a UNC path, or we don't have an IRP context
+ // to do the VCB cleanup. Simply timestamp the VCB and the
+ // scavenger will cleanup if the VCB remains idle.
+ //
+
+ KeQuerySystemTime( &Vcb->LastUsedTime );
+ NwReleaseRcb( &NwRcb );
+
+ } else {
+
+ //
+ // This VCB is being explicitly deleted by the user.
+ // Make it go away now. This will release the RCB.
+ //
+
+ NwCleanupVcb( Vcb, IrpContext );
+
+ }
+
+ } else {
+
+ NwReleaseRcb( &NwRcb );
+ }
+
+ //
+ // At this point, we've released our acquisition of the RCB, but
+ // the caller may still own the RCB. To prevent a deadlock, we
+ // have to be careful when we put this irpcontext back on the
+ // original server.
+ //
+
+ if ( pOrigNpScb ) {
+
+ if ( OwnRcb ) {
+ NwReleaseRcb( &NwRcb );
+ }
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+
+ IrpContext->pNpScb = pOrigNpScb;
+ IrpContext->pScb = pOrigNpScb->pScb;
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ //
+ // Re-acquire for the caller.
+ //
+
+ if ( OwnRcb ) {
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+ }
+
+ }
+
+ DebugTrace(-1, Dbg, "NwDereferenceVcb\n", 0);
+
+}
+
+
+VOID
+NwCleanupVcb(
+ IN PVCB pVcb,
+ IN PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine cleans up and frees a VCB.
+
+ This routine must be called with the RCB held to
+ protect the drive map tables and unicode prefix
+ tables. The caller must own the IRP context at
+ the head of the SCB queue. This routine will
+ free the RCB and dequeue the irp context.
+
+Arguments:
+
+ pVcb - A pointer to the VCB to free.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ NTSTATUS Status;
+ CHAR Handle;
+ BOOLEAN CallDeleteScb = FALSE;
+ PSCB pScb = pVcb->Scb;
+ PNONPAGED_SCB pNpScb = pScb->pNpScb;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwCleanupVcb...\n", 0);
+
+ ASSERT( pVcb->NodeTypeCode == NW_NTC_VCB );
+ ASSERT( IsListEmpty( &pVcb->FcbList ) );
+ ASSERT( pVcb->OpenFileCount == 0 );
+
+ DebugTrace(0, Dbg, "Cleaning Vcb %08lx\n", pVcb);
+
+ //
+ // Remove the VCB from the drive map table. The RCB is owned, so
+ // the drive map table and vcb lists are protected.
+ //
+
+ if ( pVcb->DriveLetter != 0 ) {
+ if ( pVcb->DriveLetter >= L'A' && pVcb->DriveLetter <= L'Z' ) {
+ DriveMapTable[pVcb->DriveLetter - L'A'] = NULL;
+ } else {
+ DriveMapTable[MAX_DISK_REDIRECTIONS + pVcb->DriveLetter - L'1'] = NULL;
+ }
+
+ if ( !BooleanFlagOn( pVcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) {
+ FreeDriveNumber( pVcb->Scb, pVcb->Specific.Disk.DriveNumber );
+ }
+ }
+
+ //
+ // Remove the VCB from the Volume Name table.
+ //
+
+ RtlRemoveUnicodePrefix ( &NwRcb.VolumeNameTable, &pVcb->PrefixEntry );
+
+ //
+ // Remove the VCB from the global list
+ //
+
+ RemoveEntryList( &pVcb->GlobalVcbListEntry );
+
+ //
+ // Remove the VCB from our SCB's VCB list.
+ //
+
+ RemoveEntryList( &pVcb->VcbListEntry );
+
+ --pScb->VcbCount;
+
+ //
+ // There is no server jumping allowed!! We should have
+ // pre-located the correct server to avoid deadlock problems.
+ //
+
+ ASSERT( IrpContext->pNpScb == pNpScb );
+
+ //
+ // If we are cleaning up the last vcb on an NDS server and
+ // there are no open streams, we can unlicense the connection.
+ //
+
+ if ( ( pScb->MajorVersion > 3 ) &&
+ ( pScb->UserName.Length == 0 ) &&
+ ( pScb->VcbCount == 0 ) &&
+ ( pScb->OpenNdsStreams == 0 ) ) {
+ NdsUnlicenseConnection( IrpContext );
+ }
+
+ //
+ // If this is a VCB for a share, remove the volume handle.
+ //
+
+ if ( !BooleanFlagOn( pVcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) {
+
+ Handle = pVcb->Specific.Disk.Handle;
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "Sb",
+ NCP_DIR_FUNCTION, NCP_DEALLOCATE_DIR_HANDLE,
+ Handle );
+
+ if ( NT_SUCCESS( Status )) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "N" );
+ }
+ }
+
+ //
+ // We can now free the VCB memory.
+ //
+
+ FREE_POOL( pVcb );
+
+ //
+ // If there are no handles open (and hence no explicit connections)
+ // and this is a bindery login, then we should logout and disconnect
+ // from this server. This is most important when a user has a
+ // login count on a server set to 1 and wants to access the server
+ // from another machine.
+ //
+ // Release the RCB in case we get off the head of the queue in
+ // NwLogoffAndDisconnect.
+ //
+
+ NwReleaseRcb( &NwRcb );
+
+ if ( ( pScb->IcbCount == 0 ) &&
+ ( pScb->OpenFileCount == 0 ) &&
+ ( pNpScb->State == SCB_STATE_IN_USE ) &&
+ ( pScb->UserName.Length != 0 ) ) {
+
+ NwLogoffAndDisconnect( IrpContext, pNpScb );
+ }
+
+ //
+ // We might need to restore the server pointers.
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwDereferenceScb( pScb->pNpScb );
+
+ DebugTrace(-1, Dbg, "NwCleanupVcb exit\n", 0);
+ return;
+}
+
+VOID
+NwCloseAllVcbs(
+ PIRP_CONTEXT pIrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine sends closes all open VCB handles.
+
+Arguments:
+
+ pIrpContext - The IRP context for this request.
+
+Return Value:
+
+ none.
+
+--*/
+{
+ KIRQL OldIrql;
+ PLIST_ENTRY ScbQueueEntry, NextScbQueueEntry;
+ PLIST_ENTRY VcbQueueEntry, NextVcbQueueEntry;
+ PNONPAGED_SCB pNpScb;
+ PSCB pScb;
+ PVCB pVcb;
+ BOOLEAN VcbDeleted;
+
+ PAGED_CODE();
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+
+ for (ScbQueueEntry = ScbQueue.Flink ;
+ ScbQueueEntry != &ScbQueue ;
+ ScbQueueEntry = NextScbQueueEntry ) {
+
+ pNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
+ NextScbQueueEntry = pNpScb->ScbLinks.Flink;
+
+ pScb = pNpScb->pScb;
+ if ( pScb == NULL ) {
+ continue;
+ }
+
+ NwReferenceScb( pNpScb );
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+ //
+ // Get to the head of the SCB queue so that we don't deadlock
+ // if we need to send packets in NwCleanupVcb().
+ //
+
+ pIrpContext->pNpScb = pNpScb;
+ pIrpContext->pScb = pNpScb->pScb;
+
+ NwAppendToQueueAndWait( pIrpContext );
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ //
+ // NwCleanupVcb releases the RCB, but we can't be guaranteed
+ // the state of the Vcb list when we release the RCB.
+ //
+ // If we need to cleanup a VCB, release the lock, and start
+ // processing the list again.
+ //
+
+ VcbDeleted = TRUE;
+
+ while ( VcbDeleted ) {
+
+ VcbDeleted = FALSE;
+
+ //
+ // Walk the list of VCBs for this SCB
+ //
+
+ for ( VcbQueueEntry = pScb->ScbSpecificVcbQueue.Flink;
+ VcbQueueEntry != &pScb->ScbSpecificVcbQueue;
+ VcbQueueEntry = NextVcbQueueEntry ) {
+
+ pVcb = CONTAINING_RECORD( VcbQueueEntry, VCB, VcbListEntry );
+ NextVcbQueueEntry = VcbQueueEntry->Flink;
+
+ //
+ // If this VCB is mapped to a drive letter, delete the mapping
+ // now.
+ //
+
+ if ( BooleanFlagOn( pVcb->Flags, VCB_FLAG_EXPLICIT_CONNECTION )) {
+
+ //
+ // Remove the VCB from the global list.
+ //
+
+ ClearFlag( pVcb->Flags, VCB_FLAG_EXPLICIT_CONNECTION );
+ --pVcb->Reference;
+ --pVcb->Scb->OpenFileCount;
+ }
+
+ if ( pVcb->DriveLetter >= L'A' && pVcb->DriveLetter <= L'Z' ) {
+ DriveMapTable[ pVcb->DriveLetter - 'A' ] = NULL;
+ } else if ( pVcb->DriveLetter >= L'1' && pVcb->DriveLetter <= L'9' ) {
+ DriveMapTable[ MAX_DISK_REDIRECTIONS + pVcb->DriveLetter - '1' ] = NULL;
+ } else {
+ ASSERT( pVcb->DriveLetter == 0 );
+ }
+
+ if ( pVcb->Reference == 0 ) {
+
+ NwCleanupVcb( pVcb, pIrpContext );
+
+ //
+ // Get back to the head of the queue.
+ //
+
+ NwAppendToQueueAndWait( pIrpContext );
+ NwAcquireExclusiveRcb( &NwRcb, TRUE );
+
+ VcbDeleted = TRUE;
+ break;
+
+ } else {
+ SetFlag( pVcb->Flags, VCB_FLAG_DELETE_IMMEDIATELY );
+ }
+
+ }
+ }
+
+ //
+ // Get off the head of this SCB and move on.
+ //
+
+ KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
+ NwDequeueIrpContext( pIrpContext, TRUE );
+ NwReleaseRcb( &NwRcb );
+ NwDereferenceScb( pNpScb );
+ }
+
+ KeReleaseSpinLock( &ScbSpinLock, OldIrql );
+
+}
+
+BOOLEAN
+GetLongNameSpaceForVolume(
+ IN PIRP_CONTEXT IrpContext,
+ IN UNICODE_STRING ShareName,
+ OUT PCHAR VolumeLongNameSpace,
+ OUT PCHAR VolumeNumber
+ )
+/*++
+
+Routine Description:
+
+ This routine determines the name space index for long name support.
+ This is accomplished by looking for the OS2 name space.
+
+Arguments:
+
+ pIrpContext - The IRP context for this request.
+
+ ShareName - The name of the interesting volume.
+
+ VolumeLongNameSpace - Returns the name space id of the OS/2 name space.
+
+ VolumeNumber - Returns the volume number.
+
+Return Value:
+
+ TRUE - The volume support long names.
+ FALSE - The volume does not support long names.
+
+--*/
+{
+ NTSTATUS Status;
+ char *ptr;
+ int i;
+ char length;
+ BOOLEAN LongNameSpace;
+ CHAR NumberOfNameSpaces, NumberOfInfoRecords;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "GetLongNameSpaceForVolume...\n", 0);
+
+ *VolumeLongNameSpace = LFN_NO_OS2_NAME_SPACE;
+
+ //
+ // Get the ordinal number of this volume.
+ //
+
+ for ( i = 0; ShareName.Buffer[i] != ':'; i++);
+ ShareName.Length = i * sizeof( WCHAR );
+
+ DebugTrace( 0, Dbg, "Volume name %wZ\n", &ShareName );
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "SU",
+ NCP_DIR_FUNCTION, NCP_GET_VOLUME_NUMBER,
+ &ShareName );
+
+ if ( NT_SUCCESS( Status ) ) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nb",
+ VolumeNumber );
+ }
+
+ if ( !NT_SUCCESS( Status )) {
+ DebugTrace( 0, Dbg, "Couldn't get volume number\n", 0);
+ DebugTrace(-1, Dbg, "GetLongNameSpaceForVolume -> -1\n", 0);
+ return( FALSE );
+ }
+
+ //
+ // Send a get name space info request, and wait for the response.
+ //
+
+ DebugTrace( 0, Dbg, "Querying volume number %d\n", *VolumeNumber );
+
+ Status = ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "Sb",
+ NCP_DIR_FUNCTION, NCP_GET_NAME_SPACE_INFO,
+ *VolumeNumber );
+
+ if ( NT_SUCCESS( Status )) {
+ Status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nb",
+ &NumberOfNameSpaces );
+ }
+
+ if ( !NT_SUCCESS( Status )) {
+ DebugTrace( 0, Dbg, "Couldn't get name space info\n", 0);
+ DebugTrace(-1, Dbg, "GetLongNameSpaceForVolume -> -1\n", 0);
+ return( FALSE );
+ }
+
+ //
+ // Parse the response, it has the following format:
+ //
+ // NCP Header
+ //
+ // Number of Name Space Records (n1, byte)
+ //
+ // n1 Name Space Records
+ // Length (l1, byte)
+ // Value (l1 bytes, non-NUL-terminated ASCII string)
+ //
+ // Number of Name Space Info Records (n2, byte)
+ //
+ // n2 Name Space Info Records
+ // Record number (byte)
+ // Length (l2, byte)
+ // Value (l2 bytes, non-NUL-terminated ASCII string)
+ //
+ // Loaded name spaces (n3, byte)
+ // Loaded name space list (n3 bytes, each byte refers to the ordinal
+ // number of a name space record )
+ //
+ // Volume name spaces (n3, byte)
+ // Volume name space list (n3 bytes, as above)
+ //
+ // Volume Data Streams (n3, byte)
+ // Volume Data Streams (n3 bytes, each byte refers to the ordinal
+ // number of a name space info record )
+ //
+
+ DebugTrace( 0, Dbg, "Number of name spaces = %d\n", NumberOfNameSpaces );
+
+ ptr = &IrpContext->rsp[ 9 ];
+ LongNameSpace = FALSE;
+
+ //
+ // Skip the loaded name space list.
+ //
+
+ for ( i = 0 ; i < NumberOfNameSpaces ; i++ ) {
+ length = *ptr++;
+ ptr += length;
+ }
+
+ //
+ // Skip the supported data streams list.
+ //
+
+ NumberOfInfoRecords = *ptr++;
+
+ for ( i = 0 ; i < NumberOfInfoRecords ; i++ ) {
+ ptr++; // Skip record number
+ length = *ptr;
+ ptr += length + 1;
+ }
+
+ //
+ // Skip the supported data streams ordinal list.
+ //
+
+ length = *ptr;
+ ptr += length + 1;
+
+ //
+ // See if this volume supports long names.
+ //
+
+ length = *ptr++;
+ for ( i = 0; i < length ; i++ ) {
+ if ( *ptr++ == LONG_NAME_SPACE_ORDINAL ) {
+ LongNameSpace = TRUE;
+ *VolumeLongNameSpace = LONG_NAME_SPACE_ORDINAL;
+ }
+ }
+
+ if ( LongNameSpace ) {
+ DebugTrace(-1, Dbg, "GetLongNameSpaceForVolume -> STATUS_SUCCESS\n", 0 );
+ } else {
+ DebugTrace(-1, Dbg, "No long name space for volume.\n", 0 );
+ }
+
+ return( LongNameSpace );
+}
+
+BOOLEAN
+IsFatNameValid (
+ IN PUNICODE_STRING FileName
+ )
+/*++
+
+Routine Description:
+
+ This routine checks if the specified file name is conformant to the
+ Fat 8.3 file naming rules.
+
+ FIXFIX Either get a wide version of FsRtlIsFatDbcsLegal or keep the OemString
+ FIXFIX version around for the packet we'll eventually create.
+
+Arguments:
+
+ FileName - Supplies the name to check.
+
+Return Value:
+
+ BOOLEAN - TRUE if the name is valid, FALSE otherwise.
+
+--*/
+
+{
+ STRING DbcsName;
+ int i;
+
+ PAGED_CODE();
+
+ //
+ // Build up the dbcs string to call the fsrtl routine to check
+ // for legal 8.3 formation
+ //
+
+ if (NT_SUCCESS(RtlUnicodeStringToCountedOemString( &DbcsName, FileName, TRUE))) {
+
+ for ( i = 0; i < DbcsName.Length; i++ ) {
+
+ if ( FsRtlIsLeadDbcsCharacter( DbcsName.Buffer[i] ) ) {
+
+ //
+ // Ignore lead bytes and trailing bytes
+ //
+
+ i++;
+
+ } else {
+
+ //
+ // disallow:
+ // '*' + 0x80 alt-170 (0xAA)
+ // '.' + 0x80 alt-174 (0xAE),
+ // '?' + 0x80 alt-191 (0xBF) the same as Dos clients.
+ //
+ // May need to add 229(0xE5) too.
+ //
+ // We also disallow spaces as valid FAT chars since
+ // NetWare treats them as part of the OS2 name space.
+ //
+
+ if ((DbcsName.Buffer[i] == 0xAA) ||
+ (DbcsName.Buffer[i] == 0xAE) ||
+ (DbcsName.Buffer[i] == 0xBF) ||
+ (DbcsName.Buffer[i] == ' ')) {
+
+ RtlFreeOemString( &DbcsName );
+ return FALSE;
+ }
+ }
+ }
+
+ if (FsRtlIsFatDbcsLegal( DbcsName, FALSE, TRUE, TRUE )) {
+
+ RtlFreeOemString( &DbcsName );
+
+ return TRUE;
+
+ }
+
+ RtlFreeOemString( &DbcsName );
+ }
+
+ //
+ // And return to our caller
+ //
+
+ return FALSE;
+}
+
+CHAR
+GetNewDriveNumber (
+ IN PSCB Scb
+ )
+/*++
+
+Routine Description:
+
+ Portable NetWare needs us to give a different drive letter each time
+ we ask for a permanent handle. If we use the same one then:
+
+ net use s: \\port\sys
+ net use v: \\port\vol1
+ dir s:
+ <get contents of \\port\vol1 !!!!>
+
+
+Arguments:
+
+ Scb
+
+Return Value:
+
+ Letter assigned.
+
+--*/
+
+{
+
+ ULONG result = RtlFindClearBitsAndSet( &Scb->DriveMapHeader, 1, 0 );
+
+ PAGED_CODE();
+
+ if (result == 0xffffffff) {
+ return(0); // All used!
+ } else {
+ return('A' + (CHAR)(result & 0x00ff) );
+ }
+}
+
+VOID
+FreeDriveNumber(
+ IN PSCB Scb,
+ IN CHAR DriveNumber
+ )
+/*++
+
+Routine Description:
+
+ This routine releases the appropriate Drivehandles bit.
+
+Arguments:
+
+ FileName - Supplies the name to check.
+
+Return Value:
+
+ BOOLEAN - TRUE if the name is valid, FALSE otherwise.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ if (DriveNumber) {
+ RtlClearBits( &Scb->DriveMapHeader, (DriveNumber - 'A') & 0x00ff, 1);
+ }
+}
diff --git a/private/nw/rdr/struct.h b/private/nw/rdr/struct.h
new file mode 100644
index 000000000..6f6bdf9eb
--- /dev/null
+++ b/private/nw/rdr/struct.h
@@ -0,0 +1,1357 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ Struct.h
+
+Abstract:
+
+ This module defines the data structures that make up the major internal
+ part of the NetWare file system.
+
+Author:
+
+ Colin Watson [ColinW] 18-Dec-1992
+
+Revision History:
+
+--*/
+
+#ifndef _NWSTRUC_
+#define _NWSTRUC_
+
+#define byte UCHAR
+#define word USHORT
+#define dword ULONG
+
+typedef enum _PACKET_TYPE {
+ SAP_BROADCAST,
+ NCP_CONNECT,
+ NCP_FUNCTION,
+ NCP_SUBFUNCTION,
+ NCP_DISCONNECT,
+ NCP_BURST,
+ NCP_ECHO
+} PACKET_TYPE;
+
+typedef struct _NW_TDI_STRUCT {
+ HANDLE Handle;
+ PDEVICE_OBJECT pDeviceObject;
+ PFILE_OBJECT pFileObject;
+ USHORT Socket;
+} NW_TDI_STRUCT, *PNW_TDI_STRUCT;
+
+typedef
+NTSTATUS
+(*PEX) (
+ IN struct _IRP_CONTEXT* pIrpC,
+ IN ULONG BytesAvailable,
+ IN PUCHAR RspData
+ );
+
+typedef
+VOID
+(*PRUN_ROUTINE) (
+ IN struct _IRP_CONTEXT *IrpContext
+ );
+
+typedef
+NTSTATUS
+(*PPOST_PROCESSOR) (
+ IN struct _IRP_CONTEXT *IrpContext
+ );
+
+typedef
+NTSTATUS
+(*PRECEIVE_ROUTINE) (
+ IN struct _IRP_CONTEXT *IrpContext,
+ IN ULONG BytesAvailable,
+ IN PULONG BytesAccepted,
+ IN PUCHAR Response,
+ OUT PMDL *pReceiveMdl
+ );
+
+//
+// The Scb (Server control Block) record corresponds to every server
+// connected to by the file system.
+// They are ordered in ScbQueue.
+// This structure is allocated from paged pool
+//
+
+typedef struct _SCB {
+
+ //
+ // The type and size of this record (must be NW_NTC_SCB)
+ //
+
+ NODE_TYPE_CODE NodeTypeCode;
+ NODE_BYTE_SIZE NodeByteSize;
+
+ //
+ // Pointer to the non-paged part of the SCB.
+ //
+
+ struct _NONPAGED_SCB *pNpScb;
+
+ //
+ // Prefix table entry.
+ //
+
+ UNICODE_PREFIX_TABLE_ENTRY PrefixEntry;
+
+ //
+ // Server version number
+ //
+
+ UCHAR MajorVersion;
+ UCHAR MinorVersion;
+
+ //
+ // List of VCBs for this server, and a count of the VCB on the list.
+ // These fields are protected by the RCB resource.
+ //
+
+ LIST_ENTRY ScbSpecificVcbQueue;
+ ULONG VcbCount;
+
+ //
+ // A list of ICBs for the SCB.
+ //
+
+ LIST_ENTRY IcbList;
+ ULONG IcbCount;
+ ULONG OpenNdsStreams;
+
+ //
+ // User credentials that this Scb relates to.
+ //
+
+ LARGE_INTEGER UserUid;
+
+ //
+ // A count of the open files for all the VCBs for this server.
+ // Plus the number of VCB that are explicitly connected.
+ //
+
+ ULONG OpenFileCount;
+
+ //
+ // The name of the server for this SCB. Note the pNpScb->ServerName and
+ // UnicodeUid point at subparts of UidServerName->Buffer which must be
+ // non-paged pool.
+ //
+
+ UNICODE_STRING UidServerName; // L"3e7\mars312
+ UNICODE_STRING UnicodeUid; // L"3e7"
+
+ //
+ // The name of nds tree that this server belongs to, if any.
+ //
+
+ UNICODE_STRING NdsTreeName; // L"MARS"
+
+ //
+ // The username / password to use for auto-reconnect.
+ //
+
+ UNICODE_STRING UserName;
+ UNICODE_STRING Password;
+
+ //
+ // Is this the logon (preferred) server?
+ //
+
+ BOOLEAN PreferredServer;
+
+ //
+ // Is this server waiting for us to read a message?
+ //
+
+ BOOLEAN MessageWaiting;
+
+ //
+ // The number of tree connects to the root of the SCB.
+ //
+
+ ULONG AttachCount;
+
+ RTL_BITMAP DriveMapHeader;
+ ULONG DriveMap[ (MAX_DRIVES + 1) / 32 ];
+
+} SCB, *PSCB;
+
+//
+// Values for pNpScb->State
+//
+
+//
+// The SCB is on it's way up
+//
+
+#define SCB_STATE_ATTACHING (0x0001)
+
+//
+// The SCB is connected and logged in.
+//
+
+#define SCB_STATE_IN_USE (0x0003)
+
+//
+// The SCB is being disconnected or shutdown.
+//
+
+#define SCB_STATE_DISCONNECTING (0x0004)
+#define SCB_STATE_FLAG_SHUTDOWN (0x0005)
+
+//
+// The SCB is waiting to be connected.
+//
+
+#define SCB_STATE_RECONNECT_REQUIRED (0x0006)
+
+//
+// The SCB is connected but has not been logged into
+//
+
+#define SCB_STATE_LOGIN_REQUIRED (0x0007)
+
+//
+// The SCB is a fake SCB used to find a dir
+// server for a tree.
+//
+
+#define SCB_STATE_TREE_SCB (0x0008)
+
+//
+// The NONPAGED_SCB (Server control Block) contains all the data required
+// when communicating with a server when a spinlock is held or at raised
+// IRQL such as when being called at indication time by the transport.
+// This structure must be allocated from non-paged pool.
+//
+
+typedef struct _NONPAGED_SCB {
+
+ //
+ // The type and size of this record (must be NW_NTC_SCBNP
+ //
+
+ NODE_TYPE_CODE NodeTypeCode;
+ NODE_BYTE_SIZE NodeByteSize;
+
+ //
+ // Reference count and state information.
+ //
+
+ ULONG Reference;
+ ULONG State;
+
+ //
+ // The time this SCB was last used.
+ //
+
+ LARGE_INTEGER LastUsedTime;
+
+ //
+ // Sending is true between the IoCallDriver to send the datagram and
+ // the completion routine for the send.
+ //
+
+ BOOLEAN Sending;
+
+ //
+ // Receiving is true when the transport has indicated to the driver
+ // that there is data to receive and there is too much data to handle
+ // at indication time or we have received indicated data before
+ // the the send IRP completes.
+ //
+
+ BOOLEAN Receiving;
+
+ //
+ // Received is true when the rx data is valid. If a receive Irp is
+ // put down when Receiving is set to true then Received is set to
+ // true when the receive Irp completes.
+ //
+
+ BOOLEAN Received;
+
+ //
+ // OkToReceive is true iff pEx should be called
+ //
+
+ BOOLEAN OkToReceive;
+
+ //
+ // Older servers insist that reads and writes do not cross 4k offsets
+ // in the file.
+ //
+
+ BOOLEAN PageAlign;
+
+ //
+ // The links on the global list of SCBs.
+ //
+
+ LIST_ENTRY ScbLinks;
+
+ //
+ // Pointer to the paged component of the Scb
+ //
+
+ PSCB pScb;
+
+ //
+ // The list of request in progress for this SCB.
+ //
+
+ LIST_ENTRY Requests;
+
+ //
+ // The name of the server for this SCB.
+ //
+
+ UNICODE_STRING ServerName;
+
+ //
+ // Transport related information.
+ //
+
+ TA_IPX_ADDRESS LocalAddress;
+ TA_IPX_ADDRESS RemoteAddress;
+ TA_IPX_ADDRESS EchoAddress;
+ IPXaddress ServerAddress;
+ ULONG EchoCounter;
+
+ //
+ // Server is an autoassigned a socket in the range 0x4000 to 0x7fff.
+ // The transport assigns the socket number avoiding in-use sockets.
+ // Watchdog is socket+1 and Send is socket+2.
+ //
+
+ NW_TDI_STRUCT Server; // Used by us to contact server
+ NW_TDI_STRUCT WatchDog; // Used by the server to check on us
+ NW_TDI_STRUCT Send; // Used for send messages
+ NW_TDI_STRUCT Echo; // Used to determine max packet size
+ NW_TDI_STRUCT Burst; // Used for burst mode read and write
+
+ USHORT TickCount;
+
+ SHORT RetryCount; // Counts down to zero for current request
+ SHORT TimeOut; // ticks to retransmission of current request
+ UCHAR SequenceNo;
+ UCHAR ConnectionNo;
+ UCHAR ConnectionNoHigh;
+ UCHAR ConnectionStatus;
+ USHORT MaxTimeOut;
+ USHORT BufferSize;
+ UCHAR TaskNo;
+
+ //
+ // Burst mode parameters
+ //
+
+ ULONG SourceConnectionId; // High-low order
+ ULONG DestinationConnectionId; // High-low order
+ ULONG MaxPacketSize;
+ ULONG MaxSendSize;
+ ULONG MaxReceiveSize;
+ BOOLEAN SendBurstModeEnabled;
+ BOOLEAN ReceiveBurstModeEnabled;
+ BOOLEAN BurstRenegotiateReqd;
+ ULONG BurstSequenceNo; // Counts # of burst packets sent
+ USHORT BurstRequestNo; // Counts # of burst requests sent
+ LONG SendBurstSuccessCount; // The number of consecutive successful bursts
+ LONG ReceiveBurstSuccessCount; // The number of consecutive successful bursts
+
+ //
+ // Send delays and timeouts
+ //
+
+ SHORT SendTimeout; // Exchange timeout in ticks (1/18th sec)
+ ULONG TotalWaitTime; // Total time, in ticks, waiting for current response
+
+ LONG NwLoopTime; // Time for a small packet to reach the server and return
+ LONG NwSingleBurstPacketTime; // Time for a burst packet to go to the server
+
+ LONG NwMaxSendDelay; // Burst send delay time, in 100us units
+ LONG NwSendDelay; // Burst send delay time, in 100us units
+ LONG NwGoodSendDelay; // Burst send delay time, in 100us units
+ LONG NwBadSendDelay; // Burst send delay time, in 100us units
+ LONG BurstDataWritten; // Bytes written, used for dummy NCP in write.c
+
+ LONG NwMaxReceiveDelay; // Burst delay time, in 100us units
+ LONG NwReceiveDelay; // Burst delay time, in 100us units
+ LONG NwGoodReceiveDelay; // Burst delay time, in 100us units
+ LONG NwBadReceiveDelay; // Burst delay time, in 100us units
+
+ LONG CurrentBurstDelay; // All requests in the current burst need the same value
+
+ LARGE_INTEGER NtSendDelay; // Burst send delay time, in 100ns units
+
+ //
+ // A spin lock used to protect various fields for this SCB.
+ // NpScbInterLock is used to protect pNpScb->Reference.
+ //
+
+ KSPIN_LOCK NpScbSpinLock;
+ KSPIN_LOCK NpScbInterLock;
+
+ //
+ // This field records the last time a time-out event was written to
+ // the event log for this server.
+ //
+
+ LARGE_INTEGER NwNextEventTime;
+
+ //
+ // LIP estimation of speed in 100bps units.
+ //
+
+ ULONG LipDataSpeed;
+
+#ifdef MSWDBG
+ BOOL RequestQueued;
+ BOOL RequestDequeued;
+
+ ULONG SequenceNumber;
+#endif
+
+} NONPAGED_SCB, *PNONPAGED_SCB;
+
+//
+// Delete this VCB immediately if the reference count reaches zero.
+//
+
+#define VCB_FLAG_DELETE_IMMEDIATELY 0x00000001
+#define VCB_FLAG_EXPLICIT_CONNECTION 0x00000002
+#define VCB_FLAG_PRINT_QUEUE 0x00000004
+#define VCB_FLAG_LONG_NAME 0x00000008
+
+//
+// The VCB corresponds to a netware volume.
+//
+
+typedef struct _VCB {
+
+ //
+ // Type and size of this record (must be NW_NTC_VCB)
+ //
+
+ NODE_TYPE_CODE NodeTypeCode;
+ NODE_BYTE_SIZE NodeByteSize;
+
+ ULONG Reference;
+ LARGE_INTEGER LastUsedTime;
+
+ //
+ // Connection the the global VCB list.
+ //
+
+ LIST_ENTRY GlobalVcbListEntry;
+ ULONG SequenceNumber;
+
+ //
+ // The requested volume name in the following form:
+ //
+ // \{Server | Tree}\{Share | Volume.Object}\Path
+ //
+
+ UNICODE_STRING Name;
+
+ //
+ // If the above name refers to an nds volume, this
+ // contains the resolved server and share name in
+ // the following form:
+ //
+ // \Server\Share\Path
+ //
+
+ UNICODE_STRING ConnectName;
+
+ //
+ // The share name in Netware compatible form.
+ //
+
+ UNICODE_STRING ShareName;
+
+ //
+ // The prefix table entry for this volume.
+ //
+
+ UNICODE_PREFIX_TABLE_ENTRY PrefixEntry; // 7 DWORDs
+
+ union {
+
+ //
+ // Disk VCB specific data.
+ //
+
+ struct {
+
+ //
+ // The volume number
+ //
+
+ CHAR VolumeNumber;
+
+ //
+ // The name space number for long name support. -1 if long name
+ // space is not supported.
+ //
+
+ CHAR LongNameSpace;
+
+ //
+ // The remote handle
+ //
+
+ CHAR Handle;
+
+ //
+ // The Drive Letter we told the server we were mapping. Portable
+ // NetWare needs this to be different for each permanent handle
+ // we create.
+ //
+
+ CHAR DriveNumber;
+
+ } Disk;
+
+ //
+ // Print VCB specific data.
+ //
+
+ struct {
+ ULONG QueueId;
+ } Print;
+
+ } Specific;
+
+ //
+ // The drive letter for this VCB. (0 if this is UNC).
+ //
+
+ WCHAR DriveLetter;
+
+ //
+ // The SCB for this volume, and a link to the VCBs for this SCB
+ //
+
+ PSCB Scb;
+ LIST_ENTRY VcbListEntry;
+
+ //
+ // List of FCBs and DCBs for this server. These fields are protected
+ // by the RCB resource.
+ //
+
+ LIST_ENTRY FcbList;
+
+ //
+ // The count of open ICBs for this VCB.
+ //
+
+ ULONG OpenFileCount;
+
+ //
+ // VCB flags
+ //
+
+ ULONG Flags;
+
+} VCB, *PVCB;
+
+//
+// Use default date / time when netware returns no info, or bogus info.
+//
+
+#define DEFAULT_DATE ( 1 + (1 << 5) + (0 << 9) ) /* Jan 1, 1980 */
+#define DEFAULT_TIME ( 0 + (0 << 5) + (0 << 11) ) /* 12:00am */
+
+//
+// The Fcb/Dcb record corresponds to every open file and directory.
+//
+// The structure is really divided into two parts. FCB can be allocated
+// from paged pool which the NONPAGED_FCB must be allocated from non-paged
+// pool.
+//
+
+typedef struct _FCB {
+
+ //
+ // Type and size of this record (must be NW_NTC_FCB or NW_NTC_DCB)
+ //
+
+ NODE_TYPE_CODE NodeTypeCode;
+ NODE_BYTE_SIZE NodeByteSize;
+
+ //
+ // The VCB for this file.
+ //
+
+ PVCB Vcb;
+
+ //
+ // The following field is the fully qualified file name for this FCB/DCB.
+ // The file name relative to the root of the volume.
+ //
+
+ UNICODE_STRING FullFileName;
+ UNICODE_STRING RelativeFileName;
+
+ //
+ // Netware file information.
+ //
+
+ USHORT LastModifiedDate;
+ USHORT LastModifiedTime;
+ USHORT CreationDate;
+ USHORT CreationTime;
+ USHORT LastAccessDate;
+
+ //
+ // The state of the FCB.
+ //
+
+ ULONG State;
+ ULONG Flags;
+
+ //
+ // A record of accesss currently granted.
+ //
+
+ SHARE_ACCESS ShareAccess;
+
+ //
+ // The prefix table entry for this file.
+ //
+
+ UNICODE_PREFIX_TABLE_ENTRY PrefixEntry;
+
+ //
+ // The SCB for this file, and a link to the FCB for this SCB
+ //
+
+ PSCB Scb;
+ LIST_ENTRY FcbListEntry;
+
+ //
+ // The list of ICB's for this FCB or DCB.
+ //
+
+ LIST_ENTRY IcbList;
+ ULONG IcbCount;
+
+ //
+ // A pointer to the specific non-paged data for the Fcb.
+ //
+
+ struct _NONPAGED_FCB *NonPagedFcb;
+
+ ULONG LastReadOffset;
+ ULONG LastReadSize;
+
+} FCB, DCB;
+typedef FCB *PFCB;
+typedef DCB *PDCB;
+
+typedef enum {
+ ReadAhead,
+ WriteBehind
+} CACHE_TYPE;
+
+typedef struct _NONPAGED_FCB {
+
+ //
+ // The following field is used for fast I/O
+ //
+ // The following comments refer to the use of the AllocationSize field
+ // of the FsRtl-defined header to the nonpaged Fcb.
+ //
+ // For a directory when we create a Dcb we will not immediately
+ // initialize the cache map, instead we will postpone it until our first
+ // call to NwReadDirectoryFile or NwPrepareWriteDirectoryFile.
+ // At that time we will search the Nw to find out the current allocation
+ // size (by calling NwLookupFileAllocationSize) and then initialize the
+ // cache map to this allocation size.
+ //
+ // For a file when we create an Fcb we will not immediately initialize
+ // the cache map, instead we will postpone it until we need it and
+ // then we determine the allocation size from either searching the
+ // fat to determine the real file allocation, or from the allocation
+ // that we've just allocated if we're creating a file.
+ //
+ // A value of -1 indicates that we do not know what the current allocation
+ // size really is, and need to examine the fat to find it. A value
+ // of than -1 is the real file/directory allocation size.
+ //
+ // Whenever we need to extend the allocation size we call
+ // NwAddFileAllocation which (if we're really extending the allocation)
+ // will modify the Nw, Rcb, and update this field. The caller
+ // of NwAddFileAllocation is then responsible for altering the Cache
+ // map size.
+ //
+
+ FSRTL_COMMON_FCB_HEADER Header;
+
+ PFCB Fcb;
+
+ //
+ // The following field contains a record of special pointers used by
+ // MM and Cache to manipluate section objects. Note that the values
+ // are set outside of the file system. However the file system on an
+ // open/create will set the file object's SectionObject field to point
+ // to this field
+ //
+
+ SECTION_OBJECT_POINTERS SegmentObject;
+
+ //
+ // The following field is used to maintain a list of locks owned for
+ // this file. It points to an ordered list of file locks.
+ //
+
+ LIST_ENTRY FileLockList;
+
+ //
+ // The following field is used to maintain a list of pending locks
+ // for this file. All locks in this list conflict with existing
+ // locks on the FileLockList.
+ //
+
+ LIST_ENTRY PendingLockList;
+
+ //
+ // A resource to synchronize access to the FCB and it's ICBs
+ //
+
+ ERESOURCE Resource;
+
+ //
+ // Netware file information.
+ //
+
+ UCHAR Attributes;
+
+ //
+ // File data cache information
+ //
+
+ UCHAR CacheType; // ReadAhead or WriteBehind
+ PUCHAR CacheBuffer; // The cache buffer
+ PMDL CacheMdl; // The full MDL for the cache buffer
+ ULONG CacheSize; // The size of the cache buffer
+ ULONG CacheFileOffset; // The file offset of this data
+ ULONG CacheDataSize; // The amount of file data in the cache
+
+} NONPAGED_FCB, NONPAGED_DCB;
+
+typedef NONPAGED_FCB *PNONPAGED_FCB;
+typedef NONPAGED_DCB *PNONPAGED_DCB;
+
+#define FCB_STATE_OPEN_PENDING 0x00000001
+#define FCB_STATE_OPENED 0x00000002
+#define FCB_STATE_CLOSE_PENDING 0x00000003
+
+#define FCB_FLAGS_DELETE_ON_CLOSE 0x00000001
+#define FCB_FLAGS_TRUNCATE_ON_CLOSE 0x00000002
+#define FCB_FLAGS_PAGING_FILE 0x00000004
+#define FCB_FLAGS_PREFIX_INSERTED 0x00000008
+#define FCB_FLAGS_FORCE_MISS_IN_PROGRESS 0x00000010
+#define FCB_FLAGS_ATTRIBUTES_ARE_VALID 0x00000020
+#define FCB_FLAGS_LONG_NAME 0x00000040
+#define FCB_FLAGS_LAZY_SET_SHAREABLE 0x00000100
+
+//
+// The Icb record is allocated for every file object
+//
+
+typedef struct _ICB {
+
+ //
+ // Type and size of this record (must be NW_NTC_ICB or NW_NTC_ICB_SCB)
+ //
+
+ NODE_TYPE_CODE NodeTypeCode;
+ NODE_BYTE_SIZE NodeByteSize;
+
+ //
+ // A link to the list of ICB's for our FCB, and our FCB.
+ //
+
+ LIST_ENTRY ListEntry;
+
+ union {
+ PFCB Fcb;
+ PSCB Scb;
+ } SuperType;
+
+ PNONPAGED_FCB NpFcb; // Valid only for node type NW_ITC_ICB
+
+ //
+ // The state of this ICB.
+ //
+
+ ULONG State;
+
+ //
+ // The remote handle;
+ //
+
+ UCHAR Handle[6]; // Keep WORD aligned.
+
+ BOOLEAN HasRemoteHandle; // TRUE if we have a remote handle for this ICB
+
+ //
+ // The file object for this ICB.
+ //
+
+ PFILE_OBJECT FileObject;
+
+ //
+ // The query template is used to filter directory query requests.
+ // It originally is set to null and on the first call the NtQueryDirectory
+ // it is set the the input filename or "*" if the name is supplied.
+ // All subsquent queries then use this template
+ //
+
+ OEM_STRING NwQueryTemplate;
+ UNICODE_STRING UQueryTemplate;
+ ULONG IndexOfLastIcbReturned;
+ UCHAR Pid;
+
+ BOOLEAN DotReturned;
+ BOOLEAN DotDotReturned;
+ BOOLEAN ReturnedSomething;
+ BOOLEAN ShortNameSearch;
+
+ //
+ // More search parameters.
+ //
+
+ USHORT SearchHandle;
+ UCHAR SearchVolume;
+ UCHAR SearchAttributes;
+
+ //
+ // Extra search parameters for long name support
+ //
+
+ ULONG SearchIndexLow;
+ ULONG SearchIndexHigh;
+
+ //
+ // SVR to avoid rescanning from end of dir all
+ // the way through the directory again.
+ //
+
+ ULONG LastSearchIndexLow;
+
+ // SVR end
+
+ //
+ // Print parametres;
+ //
+
+ BOOLEAN IsPrintJob;
+ USHORT JobId;
+ BOOLEAN ActuallyPrinted;
+
+ //
+ // This flag prevents cleanup from updating the access time.
+ //
+
+ BOOLEAN UserSetLastAccessTime;
+
+ //
+ // The current file position.
+ //
+
+ ULONG FilePosition;
+
+ //
+ // The size of the file if its ICB_SCB
+ //
+
+ ULONG FileSize;
+
+ //
+ // The Next dirent offset is used by directory enumeration. It is
+ // the offset (within the directory file) of the next dirent to examine.
+ //
+
+ //VBO OffsetToStartSearchFrom;
+
+ //
+ // If this ICB was created with OPEN_RENAME_TARGET then the following
+ // parameters are used
+ //
+
+ BOOLEAN IsAFile;
+ BOOLEAN Exists;
+ BOOLEAN FailedFindNotify;
+
+ //
+ // Is this a tree handle? We need to know for delete.
+ //
+ BOOLEAN IsTreeHandle;
+
+} ICB, *PICB;
+
+#define ICB_STATE_OPEN_PENDING 0x00000001
+#define ICB_STATE_OPENED 0x00000002
+#define ICB_STATE_CLEANED_UP 0x00000003
+#define ICB_STATE_CLOSE_PENDING 0x00000004
+
+#define INVALID_PID 0
+
+//
+// A structure used to maintain a list of file locks.
+//
+
+typedef struct _NW_FILE_LOCK {
+
+ //
+ // Type and size of this record (must be NW_NTC_FILE_LOCK )
+ //
+
+ NODE_TYPE_CODE NodeTypeCode;
+ NODE_BYTE_SIZE NodeByteSize;
+
+ //
+ // A link to the list of locks for this FCB.
+ //
+
+ LIST_ENTRY ListEntry;
+
+ //
+ // The ICB this lock belongs to.
+ //
+
+ PICB Icb;
+
+ //
+ // The IRP Context for this lock request.
+ //
+
+ struct _IRP_CONTEXT *IrpContext;
+
+ //
+ // The lock offset, length, and key.
+ //
+
+ LONG StartFileOffset;
+ ULONG Length;
+ LONG EndFileOffset;
+ ULONG Key;
+ USHORT Flags;
+
+} NW_FILE_LOCK, *PNW_FILE_LOCK;
+
+//
+// The Rcb record controls access to the redirector device
+//
+
+typedef struct _RCB {
+
+ //
+ // Type and size of this record (must be NW_NTC_RCB)
+ //
+
+ NODE_TYPE_CODE NodeTypeCode;
+ NODE_BYTE_SIZE NodeByteSize;
+
+ //
+ // The run state of the redirector
+ //
+
+ ULONG State;
+
+ //
+ // The count of open handles to the RCB.
+ // Access is protected by the RCB Resource.
+ //
+
+ ULONG OpenCount;
+
+ //
+ // A resource to synchronize access to the RCB.
+ //
+
+ ERESOURCE Resource;
+
+ //
+ // A record of accesss currently granted to the RCB.
+ //
+
+ SHARE_ACCESS ShareAccess;
+
+ //
+ // A prefix table of all connected servers.
+ //
+
+ UNICODE_PREFIX_TABLE ServerNameTable;
+
+ //
+ // A prefix table of all open volumes.
+ //
+
+ UNICODE_PREFIX_TABLE VolumeNameTable;
+
+ //
+ // A prefix table of all open files
+ //
+
+ UNICODE_PREFIX_TABLE FileNameTable;
+
+} RCB, *PRCB;
+
+
+#define RCB_STATE_STOPPED 0x00000001
+#define RCB_STATE_STARTING 0x00000002
+#define RCB_STATE_NEED_BIND 0x00000003
+#define RCB_STATE_RUNNING 0x00000004
+#define RCB_STATE_SHUTDOWN 0x00000005
+
+//
+// IRP_CONTEXT Flags bits.
+//
+
+#define IRP_FLAG_IN_FSD 0x00000001 // This IRP is being process in the FSD
+#define IRP_FLAG_ON_SCB_QUEUE 0x00000002 // This IRP is queued to an SCB
+#define IRP_FLAG_SEQUENCE_NO_REQUIRED 0x00000004 // This packet requires a sequence #
+#define IRP_FLAG_SIGNAL_EVENT 0x00000010
+#define IRP_FLAG_RETRY_SEND 0x00000020 // We are resending a timed out request
+#define IRP_FLAG_RECONNECTABLE 0x00000040 // We are allowed to try a reconnect if this request fails due to a bad connection
+#define IRP_FLAG_RECONNECT_ATTEMPT 0x00000080 // This IRP is being used to attempt a reconnect
+#define IRP_FLAG_BURST_REQUEST 0x00000100 // This is a burst request packet
+#define IRP_FLAG_BURST_PACKET 0x00000200 // This is any burst packet
+#define IRP_FLAG_NOT_OK_TO_RECEIVE 0x00000400 // Don't set ok to receive when sending this packet
+#define IRP_FLAG_REROUTE_ATTEMPTED 0x00000800 // A re-route has been attempted for this packet
+#define IRP_FLAG_BURST_WRITE 0x00001000 // We are processsing a burst write request
+#define IRP_FLAG_SEND_ALWAYS 0x00002000 // Okay to send this packet, even if RCB State is shutdown
+#define IRP_FLAG_FREE_RECEIVE_MDL 0x00004000 // Free the receive irp's MDL when the irp completes
+#define IRP_FLAG_NOT_SYSTEM_PACKET 0x00008000 // Used in burst writes to alternate system packet and normal
+#define IRP_FLAG_NOCONNECT 0x00010000 // Used to inspect server list
+
+typedef struct _IRP_CONTEXT {
+
+ //
+ // Type and size of this record (must be NW_NTC_IRP_CONTEXT).
+ //
+
+ NODE_TYPE_CODE NodeTypeCode;
+ NODE_BYTE_SIZE NodeByteSize;
+
+ //
+ // Information about this IRP
+ //
+
+ ULONG Flags;
+
+ //
+ // This structure is used for posting to the Ex worker threads.
+ //
+
+ WORK_QUEUE_ITEM WorkQueueItem; // 4*sizeof(ULONG)
+
+ // Workspace for exchange()
+ PACKET_TYPE PacketType;
+
+ //
+ // Server Control Block to which this request applies.
+ //
+
+ PNONPAGED_SCB pNpScb;
+ PSCB pScb;
+
+ //
+ // The socket structure to use for this request. If NULL, use
+ // pNpScb->Server socket.
+ //
+
+ PNW_TDI_STRUCT pTdiStruct;
+
+ //
+ // List of requests to a particular server. Listed on Scb->Requests.
+ //
+
+ LIST_ENTRY NextRequest;
+
+ //
+ // Used for processing synchronous IRPs.
+ //
+
+ KEVENT Event; // 4 words
+
+ //
+ // A pointer to the originating Irp and its original contents when
+ // the I/O system submitted it to the rdr.
+ //
+
+ PIRP pOriginalIrp;
+ PVOID pOriginalSystemBuffer;
+ PVOID pOriginalUserBuffer;
+ PMDL pOriginalMdlAddress;
+
+ //
+ // Information used if we need to post an IRP to process the receive
+ //
+
+ PIRP ReceiveIrp;
+
+ //
+ // Pointer to the Mdl used to transmit/receive the Ncp header.
+ //
+
+ PMDL TxMdl;
+ PMDL RxMdl;
+
+ //
+ // Routine to run when this IRP context reaches the front of the
+ // SCB queue.
+ //
+
+ PRUN_ROUTINE RunRoutine;
+
+ //
+ // Routine to handle the response Ncp
+ //
+
+ PEX pEx;
+
+ //
+ // Routine to handle packet receipt
+ //
+
+ PRECEIVE_ROUTINE ReceiveDataRoutine;
+
+ //
+ // Routine to handle FSP post processing.
+ //
+
+ PPOST_PROCESSOR PostProcessRoutine;
+
+ //
+ // Routine to run when this IRP context times out while on the SCB
+ // queue.
+ //
+
+ PRUN_ROUTINE TimeoutRoutine;
+
+ //
+ // Routine to run when this IRP has completed a send.
+ //
+
+ PIO_COMPLETION_ROUTINE CompletionSendRoutine;
+
+ //
+ // Work Item used for scheduling reconnect.
+ //
+
+ PWORK_QUEUE_ITEM pWorkItem;
+
+ //
+ // Buffer used to hold the Ncb to be transmitted/received.
+ //
+
+ ULONG Signature1;
+
+ UCHAR req[MAX_SEND_DATA];
+ ULONG Signature2;
+
+ ULONG ResponseLength;
+ UCHAR rsp[MAX_RECV_DATA];
+ ULONG Signature3;
+
+ //
+ // Address to be used in the Send Datagram.
+ //
+
+ TA_IPX_ADDRESS Destination;
+ TDI_CONNECTION_INFORMATION ConnectionInformation; // Remote server
+
+ //
+ // The ICB being processed.
+ //
+
+ PICB Icb;
+
+ //
+ // Per IRP processor information. A handy place to store information
+ // for the IRP in progress.
+ //
+
+ union {
+ struct {
+ UNICODE_STRING FullPathName;
+ UNICODE_STRING VolumeName;
+ UNICODE_STRING PathName;
+ UNICODE_STRING FileName;
+ BOOLEAN NdsCreate;
+ BOOLEAN NeedNdsData;
+ DWORD dwNdsOid;
+ DWORD dwNdsObjectType;
+ DWORD dwNdsShareLength;
+ UNICODE_STRING UidConnectName;
+ WCHAR DriveLetter;
+ ULONG ShareType;
+ BOOLEAN fExCredentialCreate;
+ PUNICODE_STRING puCredentialName;
+ PCHAR FindNearestResponse[4];
+ ULONG FindNearestResponseCount;
+ LARGE_INTEGER UserUid;
+ } Create;
+
+ struct {
+ PVOID Buffer;
+ ULONG Length;
+ PVCB Vcb;
+ CHAR VolumeNumber;
+ } QueryVolumeInformation;
+
+ struct {
+ PVOID Buffer;
+ ULONG Length;
+ PMDL InputMdl;
+ UCHAR Function; // Used for special case post-processing
+ UCHAR Subfunction; // during UserNcpCallback
+
+ } FileSystemControl;
+
+ struct {
+ PVOID Buffer;
+ ULONG WriteOffset;
+ ULONG RemainingLength;
+ PMDL PartialMdl;
+ PMDL FullMdl;
+ ULONG FileOffset;
+ ULONG LastWriteLength;
+
+ ULONG BurstOffset;
+ ULONG BurstLength;
+ NTSTATUS Status;
+
+ ULONG TotalWriteLength;
+ ULONG TotalWriteOffset;
+
+ ULONG PacketCount;
+ } Write;
+
+ struct {
+ ULONG CacheReadSize; // Amount of data read from the cache
+ ULONG ReadAheadSize; // Extra data to read
+
+ PVOID Buffer; // Buffer for the current read
+ PMDL FullMdl;
+ PMDL PartialMdl;
+ ULONG ReadOffset;
+ ULONG RemainingLength;
+ ULONG FileOffset;
+ ULONG LastReadLength;
+
+ LIST_ENTRY PacketList; // List of packets received
+ ULONG BurstRequestOffset; // Offset in burst buffer for last request
+ ULONG BurstSize; // Number of bytes in current burst
+ PVOID BurstBuffer; // Buffer for the current burst
+ BOOLEAN DataReceived;
+ NTSTATUS Status;
+ UCHAR Flags;
+
+ ULONG TotalReadLength;
+ ULONG TotalReadOffset;
+ } Read;
+
+ struct {
+ PNW_FILE_LOCK FileLock;
+ ULONG Key;
+ BOOLEAN Wait;
+ BOOLEAN ByKey;
+ PLIST_ENTRY LastLock;
+ } Lock;
+
+ } Specific;
+
+ struct {
+ UCHAR Error;
+ } ResponseParameters;
+
+#ifdef NWDBG
+ ULONG DebugValue;
+ ULONG SequenceNumber;
+#endif
+} IRP_CONTEXT, *PIRP_CONTEXT;
+
+typedef struct _BURST_READ_ENTRY {
+ LIST_ENTRY ListEntry;
+ ULONG DataOffset;
+ USHORT ByteCount;
+} BURST_READ_ENTRY, *PBURST_READ_ENTRY;
+
+typedef struct _LOGON {
+
+ //
+ // The type and size of this record.
+ //
+
+ NODE_TYPE_CODE NodeTypeCode;
+ NODE_BYTE_SIZE NodeByteSize;
+
+ //
+ // List of Login records.
+ //
+
+ LIST_ENTRY Next;
+
+ UNICODE_STRING UserName;
+ UNICODE_STRING PassWord;
+ UNICODE_STRING ServerName;
+ LARGE_INTEGER UserUid;
+
+ //
+ // The NDS credential list, default tree,
+ // and default context for this user.
+ //
+
+ ERESOURCE CredentialListResource;
+ LIST_ENTRY NdsCredentialList;
+
+} LOGON, *PLOGON;
+
+typedef struct _MINI_IRP_CONTEXT {
+
+ //
+ // Header information
+ //
+
+ NODE_TYPE_CODE NodeTypeCode;
+ NODE_BYTE_SIZE NodeByteSize;
+
+ //
+ // A link to queue IRP contexts
+ //
+
+ LIST_ENTRY Next;
+
+ PIRP_CONTEXT IrpContext;
+ PIRP Irp;
+
+ PVOID Buffer; // The buffer for this request.
+ PMDL Mdl1; // The MDL for the buffer
+ PMDL Mdl2; // The MDL for the data
+} MINI_IRP_CONTEXT, *PMINI_IRP_CONTEXT;
+
+//
+// Definitions for unlockable code sections.
+//
+
+typedef struct _SECTION_DESCRIPTOR {
+ PVOID Base;
+ PVOID Handle;
+ ULONG ReferenceCount;
+} SECTION_DESCRIPTOR, *PSECTION_DESCRIPTOR;
+
+#endif // _NWSTRUC_
+
diff --git a/private/nw/rdr/synch.txt b/private/nw/rdr/synch.txt
new file mode 100644
index 000000000..82d68ee1a
--- /dev/null
+++ b/private/nw/rdr/synch.txt
@@ -0,0 +1,74 @@
+This file describes use of resources and spin locks with the netware
+redirector. There are 2 major sections - global locks, and per structure
+locks. Each subsection names a lock or resources and list all of the
+global variable, and structure fields it protects.
+
+
+1. Global Locks
+
+1.1 ScbSpinLock
+ - ScbQueue
+ - NpScb fields -> ScbLinks
+ - Nearest server table.
+
+1.2 Rcb->Resource
+ - ServerNameTable
+ - FileNameTable, Fcb->PrefixEntry
+ - Rcb fields -> OpenCount
+ - Synchronize access to newly created FCBs
+ - DefaultUserName, DefaultPassword, DefaultServerName.
+ - Fcb->IcbList, Icb->ListEntry
+ - DriveMapTable
+ - Vcb->ReferenceCount, Vcb->FcbList, Vcb->FcbCount, GlobalVcbListEntry
+ - Scb->ScbSpecificVcbQueue, Scb->VcbCount, OpenFileCount, AttachCount
+ - GlobalVcbList, CurrentVcbEntry
+
+1.3 NwScavengerSpinLock
+ - NwScavengerTickCount
+
+1.4 Front of the SCB queue
+ - Icb->State, HasRemoteHandle
+ - NpScb->State
+ - pScb->UserName, pScb->Password
+ - Fcb->FileSize (except during create)
+
+1.5 NwMessageSpinLock
+ - NwGetMessageList
+
+1.6 NwPendingLockSpinLock
+ - NwPendingLockList
+
+1.7 NwFcbTableResource
+ - FCB / ICB creation.
+
+2. Per structure locks
+
+2.1 SCB->NpScbSpinLock
+ - NpScb fields -> Sending, Receiving, OkToReceive, TimeOut,
+ MaxTimeOut, Requests, RetryCount, Reference
+
+2.2 FCB->Resource
+ - Fcb-> Attributes, LastModifiedDate, LastModifiedTime
+ State, FileLockList, PendingLockList,
+
+2.3 Front of the SCB queue
+ - Icb->FileLockList
+
+2.4 LOGON->CredListResource
+ Protects the credentials on the cred list
+ and the list pointers. Acquire shared for
+ read access to the list. Acquire exclusive
+ for write access to the list.
+
+In order to eliminate dead locks, locks should be acquired in the following
+order (if multiple locks are needed).
+
+RCB->Resource
+FCB->Resource
+NwScavengerSpinLock
+LOGON->CredListResource
+ScbSpinLock
+SCB->NpScbSpinLock
+
+A thread cannot wait for the SCB queue while holding any other lock.
+
diff --git a/private/nw/rdr/timer.c b/private/nw/rdr/timer.c
new file mode 100644
index 000000000..e5ca1204c
--- /dev/null
+++ b/private/nw/rdr/timer.c
@@ -0,0 +1,537 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ timer.c
+
+Abstract:
+
+ This module contains code which implements the receive and send timeouts
+ for each connection.
+
+Author:
+
+ Colin Watson (ColinW) 21-Feb-1993
+
+Environment:
+
+ Kernel mode
+
+Revision History:
+
+--*/
+
+#include "procs.h"
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_TIMER)
+
+LARGE_INTEGER DueTime;
+KDPC NwDpc; // DPC object for timeouts.
+KTIMER Timer; // kernel timer for this request.
+ULONG ScavengerTickCount;
+
+BOOLEAN WorkerRunning = FALSE;
+WORK_QUEUE_ITEM WorkItem;
+
+#ifdef NWDBG
+BOOLEAN DisableTimer = FALSE;
+#endif
+
+//
+// When we want to stop the timer, set TimerStop to TRUE. When the timer
+// is stopped TimerStopped will be set to the signalled state.
+//
+
+BOOLEAN TimerStop;
+KEVENT TimerStopped;
+
+VOID
+TimerDPC(
+ IN PKDPC Dpc,
+ IN PVOID Context,
+ IN PVOID SystemArgument1,
+ IN PVOID SystemArgument2
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, StartTimer )
+#pragma alloc_text( PAGE, StopTimer )
+
+#endif
+
+
+VOID
+StartTimer(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine starts the timer ticking.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PAGED_CODE();
+
+ //
+ // We need 18.21 ticks per second
+ //
+
+ DueTime.QuadPart = (( 100000 * MILLISECONDS ) / 1821) * -1;
+
+ //
+ // This is the first connection with timeouts specified.
+ // Set up the timer so that every 500 milliseconds we scan all the
+ // connections for timed out receive and sends.
+ //
+
+ TimerStop = FALSE;
+
+ KeInitializeEvent( &TimerStopped, SynchronizationEvent, FALSE );
+ KeInitializeDpc( &NwDpc, TimerDPC, NULL );
+ KeInitializeTimer( &Timer );
+
+ (VOID)KeSetTimer(&Timer, DueTime, &NwDpc);
+
+ DebugTrace(+0, Dbg, "StartTimer\n", 0);
+}
+
+
+VOID
+StopTimer(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine stops the timer. It blocks until the timer has stopped.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PAGED_CODE();
+
+ if (TimerStop == FALSE) {
+ TimerStop = TRUE;
+
+ DebugTrace(+0, Dbg, "StopTimer\n", 0);
+ KeWaitForSingleObject (&TimerStopped, Executive, KernelMode, FALSE, NULL);
+ }
+}
+
+
+VOID
+TimerDPC(
+ IN PKDPC Dpc,
+ IN PVOID Context,
+ IN PVOID SystemArgument1,
+ IN PVOID SystemArgument2
+ )
+/*++
+
+Routine Description:
+
+ This routine is called to search for timed out send and receive
+ requests. This routine is called at DPC level.
+
+Arguments:
+
+ Dpc - Unused.
+ Context - Unused.
+ SystemArgument1 - Unused.
+ SystemArgument2 - Unused.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PLIST_ENTRY ScbQueueEntry;
+ PLIST_ENTRY NextScbQueueEntry;
+ PLIST_ENTRY IrpContextEntry;
+ PLIST_ENTRY NextIrpContextEntry;
+ SHORT RetryCount;
+ PIRP_CONTEXT pIrpContext;
+ LARGE_INTEGER CurrentTime = {0, 0};
+ WCHAR AnonymousName[] = L"UNKNOWN";
+ PWCHAR ServerLogName;
+
+ if ( TimerStop ) {
+ KeSetEvent( &TimerStopped, 0, FALSE );
+ return;
+ }
+
+ //
+ // For each Server see if there is a timeout to process.
+ //
+
+#ifdef NWDBG
+ if ( DisableTimer ) {
+ //
+ // Reset the timer to run for another tick.
+ //
+
+ (VOID)KeSetTimer ( &Timer, DueTime, &NwDpc);
+
+ return;
+ }
+#endif
+
+ //DebugTrace(+1, Dbg, "TimerDpc....\n", 0);
+
+ //
+ // Scan through the Scb's looking timed out requests.
+ //
+
+ KeAcquireSpinLockAtDpcLevel( &ScbSpinLock );
+
+ ScbQueueEntry = ScbQueue.Flink;
+
+ if (ScbQueueEntry != &ScbQueue) {
+ PNONPAGED_SCB pNpScb = CONTAINING_RECORD(ScbQueueEntry,
+ NONPAGED_SCB,
+ ScbLinks);
+ NwQuietReferenceScb( pNpScb );
+ }
+
+ for (;
+ ScbQueueEntry != &ScbQueue ;
+ ScbQueueEntry = NextScbQueueEntry ) {
+
+ PNONPAGED_SCB pNpScb = CONTAINING_RECORD(ScbQueueEntry,
+ NONPAGED_SCB,
+ ScbLinks);
+
+ // Obtain a pointer to the next SCB in the SCB list before
+ // dereferencing the current one.
+ //
+
+ NextScbQueueEntry = pNpScb->ScbLinks.Flink;
+
+ if (NextScbQueueEntry != &ScbQueue) {
+ PNONPAGED_SCB pNextNpScb = CONTAINING_RECORD(NextScbQueueEntry,
+ NONPAGED_SCB,
+ ScbLinks);
+ //
+ // Reference the next entry in the list to ensure the scavenger
+ // doesn't put it on another list or destroy it.
+ //
+
+ NwQuietReferenceScb( pNextNpScb );
+ }
+
+ KeReleaseSpinLockFromDpcLevel( &ScbSpinLock );
+
+ //
+ // Acquire the Scb specific spin lock to protect access
+ // the the Scb fields.
+ //
+
+ KeAcquireSpinLockAtDpcLevel( &pNpScb->NpScbSpinLock );
+
+ //
+ // Look at the first request on the queue only (since it is
+ // the only active request).
+ //
+
+ if ( ( !IsListEmpty( &pNpScb->Requests )) &&
+ ( !pNpScb->Sending ) &&
+ ( pNpScb->OkToReceive ) &&
+ ( --pNpScb->TimeOut <= 0 ) ) {
+
+ PIRP_CONTEXT pIrpContext;
+
+ //
+ // This request has timed out. Try to retransmit the request.
+ //
+
+ pIrpContext = CONTAINING_RECORD(
+ pNpScb->Requests.Flink,
+ IRP_CONTEXT,
+ NextRequest);
+
+ pNpScb->TimeOut = pNpScb->MaxTimeOut;
+
+ //
+ // Check the retry count while we own the spin lock.
+ //
+
+ RetryCount = --pNpScb->RetryCount;
+ NwQuietDereferenceScb( pNpScb );
+
+ //
+ // Set OkToReceive to FALSE, so that if we receive a response
+ // right now, our receive handler won't handle the response
+ // and cause IRP context to be freed.
+ //
+
+ pNpScb->OkToReceive = FALSE;
+ KeReleaseSpinLockFromDpcLevel( &pNpScb->NpScbSpinLock );
+
+ if ( pIrpContext->pOriginalIrp->Cancel ) {
+
+ //
+ // This IRP has been cancelled. Call the callback routine.
+ //
+ // BUGBUG - This will cause a timeout error.
+ //
+
+ DebugTrace(+0, Dbg, "Timer cancel IRP %X\n", pIrpContext->pOriginalIrp );
+ pIrpContext->pEx( pIrpContext, 0, NULL );
+
+ } else if ( RetryCount >= 0) {
+
+ //
+ // We're not out of retries. Resend the request packet.
+ //
+ // First adjust the send timeout up. Adjust the timeout
+ // more slowly on a close by server.
+ //
+
+ if ( pNpScb->SendTimeout < pNpScb->MaxTimeOut ) {
+ if ( pNpScb->TickCount <= 4 ) {
+ pNpScb->SendTimeout++;
+ } else {
+ pNpScb->SendTimeout = pNpScb->SendTimeout * 3 / 2;
+ if ( pNpScb->SendTimeout > pNpScb->MaxTimeOut ) {
+ pNpScb->SendTimeout = pNpScb->MaxTimeOut;
+ }
+ }
+ }
+
+ pNpScb->TimeOut = pNpScb->SendTimeout;
+ DebugTrace(+0, Dbg, "Adjusting send timeout: %x\n", pIrpContext );
+ DebugTrace(+0, Dbg, "Adjusting send timeout to: %d\n", pNpScb->TimeOut );
+
+ if ( pIrpContext->TimeoutRoutine != NULL ) {
+
+ DebugTrace(+0, Dbg, "Timeout Routine, retry %x\n", RetryCount+1);
+ DebugTrace(+0, Dbg, "Calling TimeoutRoutine, %x\n", pIrpContext->TimeoutRoutine);
+ pIrpContext->TimeoutRoutine( pIrpContext );
+
+ } else {
+
+ DebugTrace(+0, Dbg, "Resending Packet, retry %x\n", RetryCount+1);
+ PreparePacket( pIrpContext, pIrpContext->pOriginalIrp, pIrpContext->TxMdl );
+
+ SetFlag( pIrpContext->Flags, IRP_FLAG_RETRY_SEND );
+ SendNow( pIrpContext );
+ }
+
+ Stats.FailedSessions++;
+
+ } else {
+
+ ASSERT( pIrpContext->pEx != NULL );
+
+ //
+ // We are out of retries.
+ //
+
+ if ( BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_REROUTE_ATTEMPTED ) ||
+ ( NwAbsoluteTotalWaitTime != 0 &&
+ pNpScb->TotalWaitTime >= NwAbsoluteTotalWaitTime ) ) {
+
+
+ ClearFlag( pIrpContext->Flags, IRP_FLAG_RETRY_SEND );
+
+ //
+ // He have already attempted to reroute the request.
+ // Give up.
+ //
+
+ DebugTrace(+0, Dbg, "Abandon Exchange\n", 0 );
+
+ if ( pIrpContext->pNpScb != &NwPermanentNpScb ) {
+
+ //
+ // Reset to the attaching state. If the server
+ // is dead, the next attempt to open a handle will
+ // fail with a better error than unexpected network
+ // error.
+ //
+
+ pIrpContext->pNpScb->State = SCB_STATE_ATTACHING;
+
+ //
+ // Determine the CurrentTime. We need to know if
+ // TimeOutEventInterval minutes have passed before
+ // we log the next time-out event.
+ //
+
+ KeQuerySystemTime( &CurrentTime );
+
+ if ( CanLogTimeOutEvent( pNpScb->NwNextEventTime,
+ CurrentTime
+ )) {
+
+ if ( pNpScb->ServerName.Buffer != NULL ) {
+ ServerLogName = pNpScb->ServerName.Buffer;
+ } else {
+ ServerLogName = &AnonymousName[0];
+ }
+
+ Error(
+ EVENT_NWRDR_TIMEOUT,
+ STATUS_UNEXPECTED_NETWORK_ERROR,
+ NULL,
+ 0,
+ 1,
+ ServerLogName );
+
+ //
+ // Set the LastEventTime to the CurrentTime
+ //
+
+ UpdateNextEventTime(
+ pNpScb->NwNextEventTime,
+ CurrentTime,
+ TimeOutEventInterval
+ );
+ }
+
+ }
+
+ pIrpContext->ResponseParameters.Error = ERROR_UNEXP_NET_ERR;
+ pIrpContext->pEx( pIrpContext, 0, NULL );
+
+ } else {
+
+ //
+ // Attempt to reroute the request.
+ //
+
+ SetFlag( pIrpContext->Flags, IRP_FLAG_REROUTE_ATTEMPTED );
+
+ if ( BooleanFlagOn(
+ pIrpContext->Flags,
+ IRP_FLAG_BURST_PACKET ) ) {
+ pIrpContext->PostProcessRoutine = NewRouteBurstRetry;
+ } else {
+ pIrpContext->PostProcessRoutine = NewRouteRetry;
+ }
+
+ NwPostToFsp( pIrpContext, FALSE );
+ }
+ }
+
+ } else {
+
+ if ( ( !IsListEmpty( &pNpScb->Requests )) &&
+ ( !pNpScb->Sending ) &&
+ ( pNpScb->OkToReceive ) ) {
+
+ DebugTrace( 0, Dbg, "TimeOut %d\n", pNpScb->TimeOut );
+ }
+
+ //
+ // Nothing to do for this SCB. Dereference this SCB and
+ // release the spin lock.
+ //
+
+ KeReleaseSpinLockFromDpcLevel( &pNpScb->NpScbSpinLock );
+ NwQuietDereferenceScb( pNpScb );
+ }
+
+ KeAcquireSpinLockAtDpcLevel( &ScbSpinLock );
+
+ }
+
+ KeReleaseSpinLockFromDpcLevel( &ScbSpinLock );
+
+ //
+ // Now see if the scavenger routine needs to be run.
+ // Only ever queue one workitem.
+ //
+
+ KeAcquireSpinLockAtDpcLevel( &NwScavengerSpinLock );
+
+ NwScavengerTickCount++;
+ if (( !WorkerRunning ) &&
+ ( NwScavengerTickCount > NwScavengerTickRunCount )) {
+
+ ExInitializeWorkItem( &WorkItem, NwScavengerRoutine, &WorkItem );
+ ExQueueWorkItem( &WorkItem, DelayedWorkQueue );
+ NwScavengerTickCount = 0;
+ WorkerRunning = TRUE;
+ }
+
+ KeReleaseSpinLockFromDpcLevel( &NwScavengerSpinLock );
+
+ //
+ // Scan the list of pending locks, looking for locks to retry.
+ //
+
+ KeAcquireSpinLockAtDpcLevel( &NwPendingLockSpinLock );
+
+ for (IrpContextEntry = NwPendingLockList.Flink ;
+ IrpContextEntry != &NwPendingLockList ;
+ IrpContextEntry = NextIrpContextEntry ) {
+
+ NextIrpContextEntry = IrpContextEntry->Flink;
+ pIrpContext = CONTAINING_RECORD( IrpContextEntry, IRP_CONTEXT, NextRequest );
+
+ //
+ // BUGBUG surely we can't use the key like this to control the number
+ // of retries.
+ //
+
+ if ( --pIrpContext->Specific.Lock.Key <= 0 ) {
+
+ //
+ // Remove the IRP Context from the queue and reattempt the lock.
+ // Set the SEQUENCE_NO_REQUIRED flag so that the packet gets
+ // renumbered.
+ //
+
+ RemoveEntryList( &pIrpContext->NextRequest );
+ SetFlag( pIrpContext->Flags, IRP_FLAG_SEQUENCE_NO_REQUIRED );
+ PrepareAndSendPacket( pIrpContext );
+ }
+
+ }
+
+ KeReleaseSpinLockFromDpcLevel( &NwPendingLockSpinLock );
+
+ //
+ // Reset the timer to run for another tick.
+ //
+
+ (VOID)KeSetTimer ( &Timer, DueTime, &NwDpc);
+
+ //DebugTrace(-1, Dbg, "TimerDpc\n", 0);
+ return;
+
+ UNREFERENCED_PARAMETER (Dpc);
+ UNREFERENCED_PARAMETER (Context);
+ UNREFERENCED_PARAMETER (SystemArgument1);
+ UNREFERENCED_PARAMETER (SystemArgument2);
+
+}
+
+
diff --git a/private/nw/rdr/util.c b/private/nw/rdr/util.c
new file mode 100644
index 000000000..54da848dc
--- /dev/null
+++ b/private/nw/rdr/util.c
@@ -0,0 +1,385 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ Util.c
+
+Abstract:
+
+ This module contains utilities function for the netware redirector.
+
+Author:
+
+ Manny Weiser [MannyW] 07-Jan-1994
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+
+//
+// The local debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_CONVERT)
+
+#ifdef ALLOC_PRAGMA
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, CopyBufferToMdl )
+#endif
+#endif
+
+#if 0 // Not pageable
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+
+
+VOID
+CopyBufferToMdl(
+ PMDL DestinationMdl,
+ ULONG DataOffset,
+ PUCHAR SourceData,
+ ULONG SourceByteCount
+ )
+/*++
+
+Routine Description:
+
+ This routine copies data from a buffer described by a pointer to a
+ given offset in a buffer described by an MDL.
+
+Arguments:
+
+ DestinationMdl - The MDL for the destination buffer.
+
+ DataOffset - The offset into the destination buffer to copy the data.
+
+ SourceData - A pointer to the source data buffer.
+
+ SourceByteCount - The number of bytes to copy.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ ULONG BufferOffset;
+ ULONG PreviousBufferOffset;
+ PMDL Mdl;
+ ULONG BytesToCopy;
+ ULONG MdlByteCount;
+ PVOID pSystemVa;
+
+ DebugTrace( +1, Dbg, "MdlMoveMemory...\n", 0 );
+ DebugTrace( 0, Dbg, "Desitination MDL = %X\n", DestinationMdl );
+ DebugTrace( 0, Dbg, "DataOffset = %d\n", DataOffset );
+ DebugTrace( 0, Dbg, "SourceData = %X\n", SourceData );
+ DebugTrace( 0, Dbg, "SourceByteCount = %d\n", SourceByteCount );
+
+ BufferOffset = 0;
+
+ Mdl = DestinationMdl;
+
+ //
+ // Truncate the response if it is too big.
+ //
+
+ MdlByteCount = MdlLength( Mdl );
+ if ( SourceByteCount + DataOffset > MdlByteCount ) {
+ SourceByteCount = MdlByteCount - DataOffset;
+ }
+
+ while ( Mdl != NULL && SourceByteCount != 0 ) {
+
+ PreviousBufferOffset = BufferOffset;
+ BufferOffset += MmGetMdlByteCount( Mdl );
+
+ if ( DataOffset < BufferOffset ) {
+
+ //
+ // Copy the data to this buffer
+ //
+
+ while ( SourceByteCount > 0 ) {
+
+ BytesToCopy = MIN( SourceByteCount,
+ BufferOffset - DataOffset );
+
+ pSystemVa = MmGetSystemAddressForMdl( Mdl );
+
+ DebugTrace( 0, Dbg, "Copy to %X\n", (PUCHAR) pSystemVa +
+ DataOffset -
+ PreviousBufferOffset );
+ DebugTrace( 0, Dbg, "Copy from %X\n", SourceData );
+ DebugTrace( 0, Dbg, "Copy bytes %d\n", BytesToCopy );
+
+ TdiCopyLookaheadData(
+ (PUCHAR)pSystemVa + DataOffset - PreviousBufferOffset,
+ SourceData,
+ BytesToCopy,
+ 0 );
+
+ SourceData += BytesToCopy;
+ DataOffset += BytesToCopy;
+ SourceByteCount -= BytesToCopy;
+
+ Mdl = Mdl->Next;
+ if ( Mdl != NULL ) {
+ PreviousBufferOffset = BufferOffset;
+ BufferOffset += MmGetMdlByteCount( Mdl );
+ } else {
+ ASSERT( SourceByteCount == 0 );
+ }
+ }
+
+ } else {
+
+ Mdl = Mdl->Next;
+
+ }
+ }
+
+ DebugTrace( -1, Dbg, "MdlMoveMemory -> VOID\n", 0 );
+}
+
+//
+// These parsing routines are used to do multiple credential
+// connects to a single server.
+//
+
+NTSTATUS
+GetCredentialFromServerName(
+ IN PUNICODE_STRING puServerName,
+ OUT PUNICODE_STRING puCredentialName
+)
+/*+++
+
+ Description: Given a munged server(credential) name,
+ this routine returns the credential.
+---*/
+{
+
+ DWORD NameLength = 0;
+ BOOLEAN FoundFirstParen = FALSE;
+ BOOLEAN FoundLastParen = FALSE;
+
+ DebugTrace( 0, Dbg, "GetCredentialFromServerName: %wZ\n", puServerName );
+
+ puCredentialName->Length = puServerName->Length;
+ puCredentialName->Buffer = puServerName->Buffer;
+
+ //
+ // Find the first paren.
+ //
+
+ while ( ( puCredentialName->Length ) && !FoundFirstParen ) {
+
+ if ( puCredentialName->Buffer[0] == L'(' ) {
+ FoundFirstParen = TRUE;
+ }
+
+ puCredentialName->Buffer++;
+ puCredentialName->Length -= sizeof( WCHAR );
+ }
+
+ if ( !FoundFirstParen ) {
+ DebugTrace( 0, Dbg, "No opening paren for server(credential) name.\n", 0 );
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ //
+ // Figure out the name length.
+ //
+
+ while ( ( puCredentialName->Length ) && !FoundLastParen ) {
+
+ if ( puCredentialName->Buffer[NameLength] == L')' ) {
+ FoundLastParen = TRUE;
+ }
+
+ NameLength++;
+ puCredentialName->Length -= sizeof( WCHAR );
+ }
+
+ if ( !FoundLastParen ) {
+ DebugTrace( 0, Dbg, "No closing paren for server(credential) name.\n", 0 );
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ //
+ // Format the name and return. Don't count the closing paren.
+ //
+
+ NameLength--;
+
+ if ( !NameLength ) {
+ DebugTrace( 0, Dbg, "Null credential name.\n", 0 );
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ puCredentialName->Length = (USHORT) (NameLength * sizeof( WCHAR ));
+ puCredentialName->MaximumLength = puCredentialName->Length;
+
+ DebugTrace( 0, Dbg, "GetCredentialFromServerName --> %wZ\n", puCredentialName );
+
+ return STATUS_SUCCESS;
+
+}
+
+NTSTATUS
+BuildExCredentialServerName(
+ IN PUNICODE_STRING puServerName,
+ IN PUNICODE_STRING puUserName,
+ OUT PUNICODE_STRING puExCredServerName
+)
+/*+++
+
+Description:
+
+ Takes a server name and a user name and makes an
+ ExCredServerName, which is simply: server(user)
+
+ This routine allocates memory for the credential
+ server name and the caller is responsible for
+ freeing the memory when it is no longer needed.
+
+---*/
+{
+
+ NTSTATUS Status;
+ PBYTE pbCredNameBuffer;
+
+ DebugTrace( 0, Dbg, "BuildExCredentialServerName\n", 0 );
+
+ if ( ( !puExCredServerName ) ||
+ ( !puServerName ) ||
+ ( !puUserName ) ) {
+
+ DebugTrace( 0, DEBUG_TRACE_ALWAYS, "BuildExCredentialServerName -> STATUS_INVALID_PARAMETER\n", 0 );
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ puExCredServerName->MaximumLength = puServerName->Length +
+ puUserName->Length +
+ ( 2 * sizeof( WCHAR ) );
+
+ pbCredNameBuffer = ALLOCATE_POOL( PagedPool,
+ puExCredServerName->MaximumLength );
+
+ if ( pbCredNameBuffer == NULL ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ puExCredServerName->Buffer = (PWCHAR) pbCredNameBuffer;
+ puExCredServerName->Length = puExCredServerName->MaximumLength;
+
+ //
+ // Copy over the server name.
+ //
+
+ RtlCopyMemory( pbCredNameBuffer,
+ puServerName->Buffer,
+ puServerName->Length );
+
+ pbCredNameBuffer += puServerName->Length;
+
+ //
+ // Add the credential name in parenthesis.
+ //
+
+ *( (PWCHAR) pbCredNameBuffer ) = L'(';
+
+ pbCredNameBuffer += sizeof( WCHAR );
+
+ RtlCopyMemory( pbCredNameBuffer,
+ puUserName->Buffer,
+ puUserName->Length );
+
+ pbCredNameBuffer += puUserName->Length;
+
+ *( (PWCHAR) pbCredNameBuffer ) = L')';
+
+ DebugTrace( 0, Dbg, "BuildExCredentialServerName: %wZ\n", puExCredServerName );
+ return STATUS_SUCCESS;
+
+}
+
+NTSTATUS
+UnmungeCredentialName(
+ IN PUNICODE_STRING puCredName,
+ OUT PUNICODE_STRING puServerName
+)
+/*+++
+
+Description:
+
+ Given server(username), return the server
+ name portion.
+
+---*/
+{
+
+ USHORT Length = 0;
+
+ DebugTrace( 0, Dbg, "UnmungeCredentialName: %wZ\n", puCredName );
+
+ puServerName->Buffer = puCredName->Buffer;
+ puServerName->MaximumLength = puCredName->MaximumLength;
+
+ while ( Length < ( puCredName->Length / sizeof( WCHAR ) ) ) {
+
+ //
+ // Look for the opening paren.
+ //
+
+ if ( puCredName->Buffer[Length] == L'(' ) {
+ break;
+ }
+
+ Length++;
+ }
+
+ puServerName->Length = Length * sizeof( WCHAR );
+
+ DebugTrace( 0, Dbg, " -> %wZ\n", puServerName );
+ return STATUS_SUCCESS;
+
+}
+
+BOOLEAN
+IsCredentialName(
+ IN PUNICODE_STRING puObjectName
+)
+/*+++
+
+Description: This returns TRUE if the object is an extended
+ credential munged name.
+
+---*/
+{
+
+ DWORD dwCurrent = 0;
+
+ if ( !puObjectName ) {
+ return FALSE;
+ }
+
+ while ( dwCurrent < ( puObjectName->Length ) / sizeof( WCHAR ) ) {
+
+ if ( puObjectName->Buffer[dwCurrent] == L'(' ) {
+ return TRUE;
+ }
+
+ dwCurrent++;
+ }
+
+ return FALSE;
+}
+
diff --git a/private/nw/rdr/volinfo.c b/private/nw/rdr/volinfo.c
new file mode 100644
index 000000000..9e2477c44
--- /dev/null
+++ b/private/nw/rdr/volinfo.c
@@ -0,0 +1,1278 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ fileinfo.c
+
+Abstract:
+
+ This module implements the get / set volume information routines for
+ netware redirector.
+
+ Setting volume information is currently unimplemented.
+
+Author:
+
+ Manny Weiser (mannyw) 4-Mar-1993
+
+Revision History:
+
+--*/
+
+#include "procs.h"
+
+#define NW_FS_NAME L"NWCompat"
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_VOLINFO)
+
+//
+// Local procedure prototypes.
+//
+
+NTSTATUS
+NwCommonQueryVolumeInformation (
+ IN PIRP_CONTEXT pIrpContext
+ );
+
+NTSTATUS
+NwQueryAttributeInfo (
+ IN PVCB Vcb,
+ IN PFILE_FS_ATTRIBUTE_INFORMATION Buffer,
+ IN ULONG Length,
+ OUT PULONG BytesWritten
+ );
+
+NTSTATUS
+NwQueryVolumeInfo (
+ IN PIRP_CONTEXT pIrpContext,
+ IN PVCB Vcb,
+ IN PFILE_FS_VOLUME_INFORMATION Buffer,
+ IN ULONG Length,
+ OUT PULONG BytesWritten
+ );
+
+NTSTATUS
+NwQueryLabelInfo (
+ IN PIRP_CONTEXT pIrpContext,
+ IN PVCB Vcb,
+ IN PFILE_FS_LABEL_INFORMATION Buffer,
+ IN ULONG Length,
+ OUT PULONG BytesWritten
+ );
+
+NTSTATUS
+NwQuerySizeInfo (
+ IN PIRP_CONTEXT pIrpContext,
+ IN PVCB Vcb,
+ IN PFILE_FS_VOLUME_INFORMATION Buffer,
+ IN ULONG Length
+ );
+
+NTSTATUS
+QueryFsSizeInfoCallback(
+ IN PIRP_CONTEXT pIrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ );
+
+NTSTATUS
+QueryFsSizeInfoCallback2(
+ IN PIRP_CONTEXT pIrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ );
+
+NTSTATUS
+NwQueryDeviceInfo (
+ IN PIRP_CONTEXT pIrpContext,
+ IN PVCB Vcb,
+ IN PFILE_FS_DEVICE_INFORMATION Buffer,
+ IN ULONG Length
+ );
+
+NTSTATUS
+NwCommonSetVolumeInformation (
+ IN PIRP_CONTEXT pIrpContext
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwFsdQueryVolumeInformation )
+#pragma alloc_text( PAGE, NwCommonQueryVolumeInformation )
+#pragma alloc_text( PAGE, NwQueryAttributeInfo )
+#pragma alloc_text( PAGE, NwQueryVolumeInfo )
+#pragma alloc_text( PAGE, NwQueryLabelInfo )
+#pragma alloc_text( PAGE, NwQuerySizeInfo )
+#pragma alloc_text( PAGE, NwQueryDeviceInfo )
+#pragma alloc_text( PAGE, NwFsdSetVolumeInformation )
+#pragma alloc_text( PAGE, NwCommonSetVolumeInformation )
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, QueryFsSizeInfoCallback )
+#pragma alloc_text( PAGE1, QueryFsSizeInfoCallback2 )
+#endif
+
+#endif
+
+#if 0 // Not pageable
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+
+NTSTATUS
+NwFsdQueryVolumeInformation (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine implements the FSD part of the NtQueryVolumeInformationFile
+ API calls.
+
+Arguments:
+
+ NwfsDeviceObject - Supplies a pointer to the device object to use.
+
+ Irp - Supplies a pointer to the Irp to process.
+
+Return Value:
+
+ NTSTATUS - The Fsd status for the Irp
+
+--*/
+
+{
+ NTSTATUS status;
+ PIRP_CONTEXT pIrpContext = NULL;
+ BOOLEAN TopLevel;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwFsdQueryVolumeInformation\n", 0);
+
+ //
+ // Call the common query volume information routine.
+ //
+
+ FsRtlEnterFileSystem();
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ try {
+
+ pIrpContext = AllocateIrpContext( Irp );
+ status = NwCommonQueryVolumeInformation( pIrpContext );
+
+ } except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
+
+ if ( pIrpContext == NULL ) {
+
+ //
+ // If we couldn't allocate an irp context, just complete
+ // irp without any fanfare.
+ //
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
+
+ } else {
+
+ //
+ // We had some trouble trying to perform the requested
+ // operation, so we'll abort the I/O request with
+ // the error Status that we get back from the
+ // execption code
+ //
+
+ status = NwProcessException( pIrpContext, GetExceptionCode() );
+ }
+
+ }
+
+ if ( pIrpContext ) {
+
+ if ( status != STATUS_PENDING ) {
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ }
+
+ NwCompleteRequest( pIrpContext, status );
+ }
+
+ if ( TopLevel ) {
+ NwSetTopLevelIrp( NULL );
+ }
+ FsRtlExitFileSystem();
+
+ //
+ // Return to the caller.
+ //
+
+ DebugTrace(-1, Dbg, "NwFsdQueryVolumeInformation -> %08lx\n", status );
+
+ return status;
+}
+
+
+NTSTATUS
+NwCommonQueryVolumeInformation (
+ IN PIRP_CONTEXT pIrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This is the common routine for querying volume information.
+
+Arguments:
+
+ IrpContext - Supplies the Irp to process
+
+Return Value:
+
+ NTSTATUS - the return status for the operation.
+
+--*/
+
+{
+ PIRP Irp;
+ PIO_STACK_LOCATION irpSp;
+ NTSTATUS status;
+
+ ULONG length;
+ ULONG bytesWritten;
+ FS_INFORMATION_CLASS fsInformationClass;
+ PVOID buffer;
+
+ NODE_TYPE_CODE nodeTypeCode;
+
+ PVOID fsContext, fsContext2;
+ PICB icb = NULL;
+ PVCB vcb = NULL;
+
+ PAGED_CODE();
+
+ //
+ // Get the current stack location.
+ //
+
+ Irp = pIrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ DebugTrace(+1, Dbg, "NwCommonQueryInformation...\n", 0);
+ DebugTrace( 0, Dbg, " Irp = %08lx\n", (ULONG)Irp);
+ DebugTrace( 0, Dbg, " ->Length = %08lx\n", irpSp->Parameters.QueryFile.Length);
+ DebugTrace( 0, Dbg, " ->FsInformationClass = %08lx\n", irpSp->Parameters.QueryVolume.FsInformationClass);
+ DebugTrace( 0, Dbg, " ->Buffer = %08lx\n", (ULONG)Irp->AssociatedIrp.SystemBuffer);
+
+ //
+ // Find out who are.
+ //
+
+ if ((nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
+ &fsContext,
+ &fsContext2 )) == NTC_UNDEFINED) {
+
+ DebugTrace(0, Dbg, "Handle is closing\n", 0);
+
+ NwCompleteRequest( pIrpContext, STATUS_INVALID_HANDLE );
+ status = STATUS_INVALID_HANDLE;
+
+ DebugTrace(-1, Dbg, "NwCommonQueryVolumeInformation -> %08lx\n", status );
+ return status;
+ }
+
+ //
+ // Decide how to handle this request. A user can query information
+ // on a VCB only.
+ //
+
+ switch (nodeTypeCode) {
+
+ case NW_NTC_RCB:
+ break;
+
+ case NW_NTC_ICB:
+ icb = (PICB)fsContext2;
+
+ //
+ // Make sure that this ICB is still active.
+ //
+
+ NwVerifyIcb( icb );
+
+ vcb = icb->SuperType.Fcb->Vcb;
+
+ pIrpContext->pNpScb = icb->SuperType.Fcb->Scb->pNpScb;
+
+ break;
+
+ default: // This is not a nodetype
+
+ DebugTrace(0, Dbg, "Node type code is not incorrect\n", 0);
+ DebugTrace(-1, Dbg, "NwCommonQueryVolumeInformation -> STATUS_INVALID_PARAMETER\n", 0);
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ //
+ // Make local copies of the input parameters.
+ //
+
+ length = irpSp->Parameters.QueryVolume.Length;
+ fsInformationClass = irpSp->Parameters.QueryVolume.FsInformationClass;
+ buffer = Irp->AssociatedIrp.SystemBuffer;
+
+ //
+ // It is ok to attempt a reconnect if this request fails with a
+ // connection error.
+ //
+
+ SetFlag( pIrpContext->Flags, IRP_FLAG_RECONNECTABLE );
+
+ try {
+
+ //
+ // Decide how to handle the request.
+ //
+
+ switch (fsInformationClass) {
+
+ case FileFsVolumeInformation:
+
+ status = NwQueryVolumeInfo( pIrpContext, vcb, buffer, length, &bytesWritten );
+ break;
+
+ case FileFsLabelInformation:
+ status = NwQueryLabelInfo( pIrpContext, vcb, buffer, length, &bytesWritten );
+ break;
+
+ case FileFsSizeInformation:
+ if ( vcb != NULL ) {
+ status = NwQuerySizeInfo( pIrpContext, vcb, buffer, length );
+ } else {
+ status = STATUS_INVALID_PARAMETER;
+ }
+ break;
+
+ case FileFsDeviceInformation:
+ status = NwQueryDeviceInfo( pIrpContext, vcb, buffer, length );
+ bytesWritten = sizeof( FILE_FS_DEVICE_INFORMATION );
+ break;
+
+ case FileFsAttributeInformation:
+
+ if ( vcb != NULL ) {
+ status = NwQueryAttributeInfo( vcb, buffer, length, &bytesWritten );
+ } else {
+ status = STATUS_INVALID_PARAMETER;
+ }
+
+ break;
+
+ default:
+
+ status = STATUS_INVALID_PARAMETER;
+ DebugTrace(0, Dbg, "Unhandled query volume level %d\n", fsInformationClass );
+ break;
+ }
+
+ //
+ // Set the information field to the number of bytes actually
+ // filled in and then complete the request.
+ //
+ // If the worker function returned status pending, it's
+ // callback routine will fill the information field.
+ //
+
+ if ( status != STATUS_PENDING ) {
+ Irp->IoStatus.Information = bytesWritten;
+ }
+
+ } finally {
+
+ DebugTrace(-1, Dbg, "NwCommonQueryVolumeInformation -> %08lx\n", status );
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+NwQueryAttributeInfo (
+ IN PVCB Vcb,
+ IN PFILE_FS_ATTRIBUTE_INFORMATION Buffer,
+ IN ULONG Length,
+ OUT PULONG BytesWritten
+ )
+
+/*++
+
+Routine Description:
+
+ This routine performs the query fs attribute information operation.
+
+Arguments:
+
+ Vcb - Supplies the VCB to query.
+
+ Buffer - Supplies a pointer to the buffer where the information is
+ to be returned.
+
+ Length - Supplies the length of the buffer in bytes.
+
+ BytesWritten - Returns the number of bytes written to the buffer.
+
+Return Value:
+
+ NTSTATUS - The result of this query.
+
+--*/
+
+{
+ NTSTATUS status;
+ ULONG bytesToCopy;
+
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "QueryFsAttributeInfo...\n", 0);
+
+ //
+ // See how many bytes of the file system name we can copy.
+ //
+
+ Length -= FIELD_OFFSET( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName[0] );
+
+ *BytesWritten = FIELD_OFFSET( FILE_FS_ATTRIBUTE_INFORMATION, FileSystemName[0] );
+
+ if ( Length >= sizeof(NW_FS_NAME) - 2 ) {
+
+ status = STATUS_SUCCESS;
+ *BytesWritten += sizeof(NW_FS_NAME - 2);
+ bytesToCopy = sizeof( NW_FS_NAME - 2 );
+
+ } else {
+
+ status = STATUS_BUFFER_OVERFLOW;
+ *BytesWritten += Length;
+ bytesToCopy = Length;
+ }
+
+ //
+ // Fill in the attribute information.
+ //
+
+ Buffer->FileSystemAttributes = 0;
+
+ if ( Vcb->Specific.Disk.LongNameSpace == LFN_NO_OS2_NAME_SPACE ) {
+ Buffer->MaximumComponentNameLength = 12;
+ } else {
+ Buffer->MaximumComponentNameLength = NW_MAX_FILENAME_LENGTH;
+ }
+
+ //
+ // And copy over the file name and its length.
+ //
+
+ RtlMoveMemory( &Buffer->FileSystemName[0],
+ NW_FS_NAME,
+ bytesToCopy );
+
+ Buffer->FileSystemNameLength = bytesToCopy;
+
+ return status;
+}
+
+
+
+NTSTATUS
+NwQueryVolumeInfo (
+ IN PIRP_CONTEXT pIrpContext,
+ IN PVCB Vcb,
+ IN PFILE_FS_VOLUME_INFORMATION Buffer,
+ IN ULONG Length,
+ OUT PULONG BytesWritten
+ )
+
+/*++
+
+Routine Description:
+
+ This routine performs the query fs volume information operation.
+
+Arguments:
+
+ Vcb - The VCB to query.
+
+ Buffer - Supplies a pointer to the buffer where the information is
+ to be returned.
+
+ Length - Supplies the length of the buffer in bytes.
+
+Return Value:
+
+ NTSTATUS - The result of this query.
+
+--*/
+
+{
+ NTSTATUS status;
+ UNICODE_STRING VolumeName;
+
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "QueryVolumeInfo...\n", 0);
+
+ //
+ // Do the volume request synchronously.
+ //
+
+ status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "Sb",
+ NCP_DIR_FUNCTION, NCP_GET_VOLUME_STATS,
+ Vcb->Specific.Disk.Handle );
+
+ if ( !NT_SUCCESS( status ) ) {
+ return status;
+ }
+
+ //
+ // Get the data from the response.
+ //
+
+ VolumeName.MaximumLength =
+ MIN( MAX_VOLUME_NAME_LENGTH * sizeof( WCHAR ),
+ Length - FIELD_OFFSET( FILE_FS_VOLUME_INFORMATION, VolumeLabel ) );
+ VolumeName.Buffer = Buffer->VolumeLabel;
+
+ status = ParseResponse(
+ pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "N=====R",
+ &VolumeName,
+ MAX_VOLUME_NAME_LENGTH );
+
+ //
+ // Fill in the volume information.
+ //
+
+ Buffer->VolumeCreationTime.HighPart = 0;
+ Buffer->VolumeCreationTime.LowPart = 0;
+ Buffer->VolumeSerialNumber = 0;
+ Buffer->VolumeLabelLength = VolumeName.Length;
+ Buffer->SupportsObjects = FALSE;
+
+ pIrpContext->pOriginalIrp->IoStatus.Information =
+ FIELD_OFFSET( FILE_FS_VOLUME_INFORMATION, VolumeLabel[0] ) +
+ VolumeName.Length;
+ *BytesWritten = pIrpContext->pOriginalIrp->IoStatus.Information;
+
+ pIrpContext->pOriginalIrp->IoStatus.Status = status;
+
+ //
+ // If the volume has been unmounted and remounted then we will
+ // fail this dir but the next one will be fine.
+ //
+
+ if (status == STATUS_UNSUCCESSFUL) {
+ NwReopenVcbHandle( pIrpContext, Vcb);
+ }
+
+ return status;
+}
+
+
+NTSTATUS
+NwQueryLabelInfo (
+ IN PIRP_CONTEXT pIrpContext,
+ IN PVCB Vcb,
+ IN PFILE_FS_LABEL_INFORMATION Buffer,
+ IN ULONG Length,
+ OUT PULONG BytesWritten
+ )
+
+/*++
+
+Routine Description:
+
+ This routine performs the query fs label information operation.
+
+Arguments:
+
+ Vcb - The VCB to query.
+
+ Buffer - Supplies a pointer to the buffer where the information is
+ to be returned.
+
+ Length - Supplies the length of the buffer in bytes.
+
+Return Value:
+
+ NTSTATUS - The result of this query.
+
+--*/
+
+{
+ NTSTATUS status;
+ UNICODE_STRING VolumeName;
+
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "QueryLabelInfo...\n", 0);
+
+ //
+ // Do the volume query synchronously.
+ //
+
+ status = ExchangeWithWait(
+ pIrpContext,
+ SynchronousResponseCallback,
+ "Sb",
+ NCP_DIR_FUNCTION, NCP_GET_VOLUME_STATS,
+ Vcb->Specific.Disk.Handle );
+
+ if ( !NT_SUCCESS( status ) ) {
+ return status;
+ }
+
+ VolumeName.MaximumLength =
+ MIN( MAX_VOLUME_NAME_LENGTH * sizeof( WCHAR ),
+ Length - FIELD_OFFSET(FILE_FS_LABEL_INFORMATION, VolumeLabel ) );
+ VolumeName.Buffer = Buffer->VolumeLabel;
+
+ status = ParseResponse(
+ pIrpContext,
+ pIrpContext->rsp,
+ pIrpContext->ResponseLength,
+ "N=====R",
+ &VolumeName, 12 );
+
+ //
+ // Fill in the label information.
+ //
+
+ Buffer->VolumeLabelLength = VolumeName.Length;
+
+ pIrpContext->pOriginalIrp->IoStatus.Information =
+ FIELD_OFFSET( FILE_FS_LABEL_INFORMATION, VolumeLabel[0] ) +
+ VolumeName.Length;
+ *BytesWritten = pIrpContext->pOriginalIrp->IoStatus.Information;
+
+ pIrpContext->pOriginalIrp->IoStatus.Status = status;
+
+ return status;
+
+}
+
+
+NTSTATUS
+NwQuerySizeInfo (
+ IN PIRP_CONTEXT pIrpContext,
+ IN PVCB Vcb,
+ IN PFILE_FS_VOLUME_INFORMATION Buffer,
+ IN ULONG Length
+ )
+
+/*++
+
+Routine Description:
+
+ This routine performs the query fs size information operation.
+
+Arguments:
+
+ Vcb - The VCB to query.
+
+ Buffer - Supplies a pointer to the buffer where the information is
+ to be returned.
+
+ Length - Supplies the length of the buffer in bytes.
+
+Return Value:
+
+ NTSTATUS - The result of this query.
+
+--*/
+
+{
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "QueryFsSizeInfo...\n", 0);
+
+ //
+ // Remember where the response goes.
+ //
+
+ pIrpContext->Specific.QueryVolumeInformation.Buffer = Buffer;
+ pIrpContext->Specific.QueryVolumeInformation.Length = Length;
+ pIrpContext->Specific.QueryVolumeInformation.VolumeNumber = Vcb->Specific.Disk.VolumeNumber;
+
+ //
+ // Start a Get Size Information NCP
+ //
+
+ status = Exchange(
+ pIrpContext,
+ QueryFsSizeInfoCallback,
+ "Sb",
+ NCP_DIR_FUNCTION, NCP_GET_VOLUME_STATS,
+ Vcb->Specific.Disk.Handle );
+
+ return( status );
+}
+
+NTSTATUS
+QueryFsSizeInfoCallback(
+ IN PIRP_CONTEXT pIrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ )
+/*++
+
+Routine Description:
+
+ This routine receives the query volume size response and generates
+ a Query Standard Information response.
+
+Arguments:
+
+
+Return Value:
+
+ VOID
+
+--*/
+{
+ PFILE_FS_SIZE_INFORMATION Buffer;
+ NTSTATUS Status;
+
+ DebugTrace(0, Dbg, "QueryFsSizeInfoCallback...\n", 0);
+
+ if ( BytesAvailable == 0) {
+
+ //
+ // We're done with this request. Dequeue the IRP context from
+ // SCB and complete the request.
+ //
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ NwCompleteRequest( pIrpContext, STATUS_REMOTE_NOT_LISTENING );
+
+ //
+ // No response from server. Status is in pIrpContext->
+ // ResponseParameters.Error
+ //
+
+ DebugTrace( 0, Dbg, "Timeout\n", 0);
+ return STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ //
+ // Get the data from the response.
+ //
+
+ Buffer = pIrpContext->Specific.QueryVolumeInformation.Buffer;
+ RtlZeroMemory( Buffer, sizeof( FILE_FS_SIZE_INFORMATION ) );
+
+ Status = ParseResponse(
+ pIrpContext,
+ Response,
+ BytesAvailable,
+ "Nwww",
+ &Buffer->SectorsPerAllocationUnit,
+ &Buffer->TotalAllocationUnits.LowPart,
+ &Buffer->AvailableAllocationUnits.LowPart );
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ if (Buffer->TotalAllocationUnits.LowPart == 0xffff) {
+
+ //
+ // The next callback will fill in all the appropriate size info.
+ //
+
+ Status = Exchange(
+ pIrpContext,
+ QueryFsSizeInfoCallback2,
+ "Sb",
+ NCP_DIR_FUNCTION, NCP_GET_VOLUME_INFO,
+ pIrpContext->Specific.QueryVolumeInformation.VolumeNumber );
+
+ if (Status == STATUS_PENDING) {
+ return( STATUS_SUCCESS );
+ }
+
+ } else {
+
+ //
+ // Fill in the remaining size information.
+ //
+
+ Buffer->BytesPerSector = 512;
+
+ pIrpContext->pOriginalIrp->IoStatus.Information =
+ sizeof( FILE_FS_SIZE_INFORMATION );
+ }
+ }
+
+ //
+ // We're done with this request. Dequeue the IRP context from
+ // SCB and complete the request.
+ //
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ NwCompleteRequest( pIrpContext, Status );
+
+ return STATUS_SUCCESS;
+}
+
+NTSTATUS
+QueryFsSizeInfoCallback2(
+ IN PIRP_CONTEXT pIrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ )
+/*++
+
+Routine Description:
+
+ This routine receives the query volume size response and generates
+ a Query Standard Information response.
+
+Arguments:
+
+
+Return Value:
+
+ VOID
+
+--*/
+{
+ PFILE_FS_SIZE_INFORMATION Buffer;
+ NTSTATUS Status;
+ ULONG PurgeableAllocationUnits;
+ ULONG OriginalFreeSpace, OriginalSectorsPerAllocUnit, OriginalTotalSpace;
+ ULONG ScaleSectorsPerUnit;
+
+ DebugTrace(0, Dbg, "QueryFsSizeInfoCallback2...\n", 0);
+
+ if ( BytesAvailable == 0) {
+
+ //
+ // We're done with this request. Dequeue the IRP context from
+ // SCB and complete the request.
+ //
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ NwCompleteRequest( pIrpContext, STATUS_REMOTE_NOT_LISTENING );
+
+ //
+ // No response from server. Status is in pIrpContext->
+ // ResponseParameters.Error
+ //
+
+ DebugTrace( 0, Dbg, "Timeout\n", 0);
+ return STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ //
+ // Get the data from the response. Save off the data from
+ // the GET_VOLUME_STATS call to compute the correct sizes.
+ //
+
+ Buffer = pIrpContext->Specific.QueryVolumeInformation.Buffer;
+
+ OriginalTotalSpace = Buffer->TotalAllocationUnits.LowPart;
+ OriginalFreeSpace = Buffer->AvailableAllocationUnits.LowPart;
+ OriginalSectorsPerAllocUnit = Buffer->SectorsPerAllocationUnit;
+
+ RtlZeroMemory( Buffer, sizeof( FILE_FS_SIZE_INFORMATION ) );
+
+ Status = ParseResponse(
+ pIrpContext,
+ Response,
+ BytesAvailable,
+ "Neee_b",
+ &Buffer->TotalAllocationUnits.LowPart,
+ &Buffer->AvailableAllocationUnits.LowPart,
+ &PurgeableAllocationUnits,
+ 16,
+ &Buffer->SectorsPerAllocationUnit);
+
+ if ( NT_SUCCESS( Status ) ) {
+
+ //
+ // If the original free space was maxed out, just add the
+ // additionally indicated units. Otherwise, return the
+ // original free space (which is the correct limit) and
+ // adjust the sectors per allocation units if necessary.
+ //
+
+ if ( OriginalFreeSpace != 0xffff ) {
+
+ Buffer->AvailableAllocationUnits.LowPart = OriginalFreeSpace;
+
+ if ( ( Buffer->SectorsPerAllocationUnit != 0 ) &&
+ ( OriginalSectorsPerAllocUnit != 0 ) ) {
+
+ //
+ // ScaleSectorsPerUnit should always be a whole number.
+ // There's no floating point here!!
+ //
+
+ if ( (ULONG) Buffer->SectorsPerAllocationUnit <= OriginalSectorsPerAllocUnit ) {
+
+ ScaleSectorsPerUnit =
+ OriginalSectorsPerAllocUnit / Buffer->SectorsPerAllocationUnit;
+ Buffer->TotalAllocationUnits.LowPart /= ScaleSectorsPerUnit;
+
+ } else {
+
+ ScaleSectorsPerUnit =
+ Buffer->SectorsPerAllocationUnit / OriginalSectorsPerAllocUnit;
+ Buffer->TotalAllocationUnits.LowPart *= ScaleSectorsPerUnit;
+ }
+
+ Buffer->SectorsPerAllocationUnit = OriginalSectorsPerAllocUnit;
+ }
+
+ } else {
+
+ Buffer->AvailableAllocationUnits.QuadPart += PurgeableAllocationUnits;
+ }
+
+ } else {
+
+ //
+ // If we didn't succeed the second packet, restore the original values.
+ //
+
+ Buffer->TotalAllocationUnits.LowPart = OriginalTotalSpace;
+ Buffer->AvailableAllocationUnits.LowPart = OriginalFreeSpace;
+ Buffer->SectorsPerAllocationUnit = OriginalSectorsPerAllocUnit;
+
+ }
+
+ //
+ // Fill in the remaining size information.
+ //
+
+ Buffer->BytesPerSector = 512;
+
+ pIrpContext->pOriginalIrp->IoStatus.Information =
+ sizeof( FILE_FS_SIZE_INFORMATION );
+
+ //
+ // We're done with this request. Dequeue the IRP context from
+ // SCB and complete the request.
+ //
+
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ NwCompleteRequest( pIrpContext, Status );
+
+ return STATUS_SUCCESS;
+}
+
+
+
+NTSTATUS
+NwQueryDeviceInfo (
+ IN PIRP_CONTEXT pIrpContext,
+ IN PVCB Vcb,
+ IN PFILE_FS_DEVICE_INFORMATION Buffer,
+ IN ULONG Length
+ )
+
+/*++
+
+Routine Description:
+
+ This routine performs the query fs size information operation.
+
+Arguments:
+
+ Vcb - The VCB to query.
+
+ Buffer - Supplies a pointer to the buffer where the information is
+ to be returned.
+
+ Length - Supplies the length of the buffer in bytes.
+
+Return Value:
+
+ NTSTATUS - The result of this query.
+
+--*/
+
+{
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "QueryFsDeviceInfo...\n", 0);
+
+ //
+ // BUGBUG. Is this universally true?
+ //
+
+ Buffer->DeviceType = FILE_DEVICE_DISK;
+ Buffer->Characteristics = FILE_REMOTE_DEVICE;
+
+ return( STATUS_SUCCESS );
+}
+
+
+NTSTATUS
+NwFsdSetVolumeInformation (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+/*++
+
+Routine Description:
+
+ This routine implements the FSD part of the NtSetVolumeInformationFile
+ API calls.
+
+Arguments:
+
+ NwfsDeviceObject - Supplies a pointer to the device object to use.
+
+ Irp - Supplies a pointer to the Irp to process.
+
+Return Value:
+
+ NTSTATUS - The Fsd status for the Irp
+
+--*/
+
+{
+ NTSTATUS status;
+ PIRP_CONTEXT pIrpContext = NULL;
+ BOOLEAN TopLevel;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwFsdSetVolumeInformation\n", 0);
+
+ //
+ // Call the common query volume information routine.
+ //
+
+ FsRtlEnterFileSystem();
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ try {
+
+ pIrpContext = AllocateIrpContext( Irp );
+ status = NwCommonSetVolumeInformation( pIrpContext );
+
+ } except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
+
+ if ( pIrpContext == NULL ) {
+
+ //
+ // If we couldn't allocate an irp context, just complete
+ // irp without any fanfare.
+ //
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
+
+ } else {
+
+ //
+ // We had some trouble trying to perform the requested
+ // operation, so we'll abort the I/O request with
+ // the error Status that we get back from the
+ // execption code
+ //
+
+ status = NwProcessException( pIrpContext, GetExceptionCode() );
+ }
+ }
+
+ if ( pIrpContext ) {
+
+ if ( status != STATUS_PENDING ) {
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ }
+
+ NwCompleteRequest( pIrpContext, status );
+ }
+
+ if ( TopLevel ) {
+ NwSetTopLevelIrp( NULL );
+ }
+ FsRtlExitFileSystem();
+
+ //
+ // Return to the caller.
+ //
+
+ DebugTrace(-1, Dbg, "NwFsdSetVolumeInformation -> %08lx\n", status );
+
+ return status;
+}
+
+
+NTSTATUS
+NwCommonSetVolumeInformation (
+ IN PIRP_CONTEXT pIrpContext
+ )
+/*++
+
+Routine Description:
+
+ This is the common routine for setting volume information.
+
+Arguments:
+
+ IrpContext - Supplies the Irp context to process
+
+Return Value:
+
+ NTSTATUS - the return status for the operation.
+
+--*/
+
+{
+ PIRP Irp;
+ PIO_STACK_LOCATION irpSp;
+ NTSTATUS status;
+
+ FS_INFORMATION_CLASS fsInformationClass;
+
+ NODE_TYPE_CODE nodeTypeCode;
+
+ PVOID fsContext, fsContext2;
+ PICB icb = NULL;
+ PVCB vcb = NULL;
+
+ PAGED_CODE();
+
+ //
+ // Get the current stack location.
+ //
+
+ Irp = pIrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ DebugTrace(+1, Dbg, "NwCommonSetVolumeInformation...\n", 0);
+ DebugTrace( 0, Dbg, " Irp = %08lx\n", (ULONG)Irp);
+ DebugTrace( 0, Dbg, " ->Length = %08lx\n", irpSp->Parameters.QueryFile.Length);
+ DebugTrace( 0, Dbg, " ->FsInformationClass = %08lx\n", irpSp->Parameters.QueryVolume.FsInformationClass);
+ DebugTrace( 0, Dbg, " ->Buffer = %08lx\n", (ULONG)Irp->AssociatedIrp.SystemBuffer);
+
+ //
+ // Find out who are.
+ //
+
+ if ((nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
+ &fsContext,
+ &fsContext2 )) == NTC_UNDEFINED) {
+
+ DebugTrace(0, Dbg, "Handle is closing\n", 0);
+
+ NwCompleteRequest( pIrpContext, STATUS_INVALID_HANDLE );
+ status = STATUS_INVALID_HANDLE;
+
+ DebugTrace(-1, Dbg, "NwCommonSetVolumeInformation -> %08lx\n", status );
+ return status;
+ }
+
+ //
+ // Decide how to handle this request. A user can set information
+ // on a VCB only.
+ //
+
+ switch (nodeTypeCode) {
+
+ case NW_NTC_RCB:
+ break;
+
+ case NW_NTC_ICB:
+ icb = (PICB)fsContext2;
+
+ //
+ // Make sure that this ICB is still active.
+ //
+
+ NwVerifyIcb( icb );
+
+ vcb = icb->SuperType.Fcb->Vcb;
+
+ pIrpContext->pNpScb = icb->SuperType.Fcb->Scb->pNpScb;
+
+ break;
+
+ default: // This is not a nodetype
+
+ DebugTrace(0, Dbg, "Node type code is not incorrect\n", 0);
+ DebugTrace(-1, Dbg, "NwCommonSetVolumeInformation -> STATUS_INVALID_PARAMETER\n", 0);
+
+ return STATUS_INVALID_PARAMETER;
+ }
+
+ fsInformationClass = irpSp->Parameters.SetVolume.FsInformationClass;
+
+ try {
+
+ //
+ // Decide how to handle the request.
+ //
+
+ switch (fsInformationClass) {
+
+ case FileFsLabelInformation:
+
+ //
+ // We're not allowed to set the label on a Netware volume.
+ //
+
+ status = STATUS_ACCESS_DENIED;
+ break;
+
+ default:
+
+ status = STATUS_INVALID_PARAMETER;
+ DebugTrace(0, Dbg, "Unhandled set volume level %d\n", fsInformationClass );
+ break;
+ }
+
+ //
+ // Set the information field to the number of bytes actually
+ // filled in and then complete the request.
+ //
+ // If the worker function returned status pending, it's
+ // callback routine will fill the information field.
+ //
+
+ if ( status != STATUS_PENDING ) {
+ Irp->IoStatus.Information = 0;
+ }
+
+ } finally {
+
+ DebugTrace(-1, Dbg, "NwCommonSetVolumeInformation -> %08lx\n", status );
+ }
+
+ return status;
+}
+
diff --git a/private/nw/rdr/workque.c b/private/nw/rdr/workque.c
new file mode 100644
index 000000000..aba699e32
--- /dev/null
+++ b/private/nw/rdr/workque.c
@@ -0,0 +1,829 @@
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ Workque.c
+
+Abstract:
+
+ This module implements the queue of work from the FSD to the
+ FSP threads (system worker threads) for the NetWare redirector.
+
+Author:
+
+ Colin Watson [ColinW] 19-Dec-1992
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+
+LIST_ENTRY IrpContextList;
+KSPIN_LOCK IrpContextInterlock;
+KSPIN_LOCK ContextInterlock;
+
+LONG FreeContextCount = 4; // Allow up to 4 free contexts
+
+LIST_ENTRY MiniIrpContextList;
+LONG FreeMiniContextCount = 20; // Allow up to 20 free mini contexts
+LONG MiniContextCount = 0; // Allow up to 20 free mini contexts
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_WORKQUE)
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, InitializeIrpContext )
+#pragma alloc_text( PAGE, UninitializeIrpContext )
+#pragma alloc_text( PAGE, NwAppendToQueueAndWait )
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, NwDequeueIrpContext )
+#pragma alloc_text( PAGE1, AllocateMiniIrpContext )
+#pragma alloc_text( PAGE1, FreeMiniIrpContext )
+#endif
+
+#endif
+
+#if 0 // Not pageable
+AllocateIrpContext
+FreeIrpContext
+NwCompleteRequest
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+
+PIRP_CONTEXT
+AllocateIrpContext (
+ PIRP pIrp
+ )
+/*++
+
+Routine Description:
+
+ Initialize a work queue structure, allocating all structures used for it.
+
+Arguments:
+
+ pIrp - Supplies the Irp for the applications request
+
+
+Return Value:
+
+ PIRP_CONTEXT - Newly allocated Irp Context.
+
+--*/
+{
+ PIRP_CONTEXT IrpContext;
+
+ if ((IrpContext = (PIRP_CONTEXT )ExInterlockedRemoveHeadList(&IrpContextList, &IrpContextInterlock)) == NULL) {
+
+ try {
+
+ //
+ // If there are no IRP contexts in the "zone", allocate a new
+ // Irp context from non paged pool.
+ //
+
+ IrpContext = ALLOCATE_POOL_EX(NonPagedPool, sizeof(IRP_CONTEXT));
+
+ RtlFillMemory( IrpContext, sizeof(IRP_CONTEXT), 0 );
+
+ IrpContext->TxMdl = NULL;
+ IrpContext->RxMdl = NULL;
+
+ KeInitializeEvent( &IrpContext->Event, SynchronizationEvent, FALSE );
+
+ IrpContext->NodeTypeCode = NW_NTC_IRP_CONTEXT;
+ IrpContext->NodeByteSize = sizeof(IRP_CONTEXT);
+
+ IrpContext->TxMdl = ALLOCATE_MDL( &IrpContext->req, MAX_SEND_DATA, FALSE, FALSE, NULL );
+ if ( IrpContext->TxMdl == NULL) {
+ InternalError(("Could not allocate TxMdl for IRP context\n"));
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ IrpContext->RxMdl = ALLOCATE_MDL( &IrpContext->rsp, MAX_RECV_DATA, FALSE, FALSE, NULL );
+ if ( IrpContext->RxMdl == NULL) {
+ InternalError(("Could not allocate RxMdl for IRP context\n"));
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ } finally {
+
+ if ( AbnormalTermination() ) {
+
+ if ( IrpContext != NULL ) {
+
+ if (IrpContext->TxMdl != NULL ) {
+ FREE_MDL( IrpContext->TxMdl );
+ }
+
+ FREE_POOL( IrpContext );
+ } else {
+ InternalError(("Could not allocate pool for IRP context\n"));
+ }
+ }
+ }
+
+ MmBuildMdlForNonPagedPool(IrpContext->TxMdl);
+ MmBuildMdlForNonPagedPool(IrpContext->RxMdl);
+
+#ifdef NWDBG
+ // Make it easy to find fields in the context
+ IrpContext->Signature1 = 0xfeedf00d;
+ IrpContext->Signature2 = 0xfeedf00d;
+ IrpContext->Signature3 = 0xfeedf00d;
+#endif
+
+ // IrpContext is allocated. Finish off initialization.
+
+ } else {
+
+ // Record that we have removed an entry from the free list
+ ExInterlockedIncrementLong(&FreeContextCount,&ContextInterlock);
+
+ ASSERT( IrpContext != NULL );
+
+ //
+ // The free list uses the start of the structure for the list entry
+ // so restore corrupted fields.
+ //
+
+ IrpContext->NodeTypeCode = NW_NTC_IRP_CONTEXT;
+ IrpContext->NodeByteSize = sizeof(IRP_CONTEXT);
+
+ // Ensure mdl's are clean
+
+ IrpContext->TxMdl->Next = NULL;
+ IrpContext->RxMdl->Next = NULL;
+ IrpContext->RxMdl->ByteCount = MAX_RECV_DATA;
+
+ //
+ // Clean "used" fields
+ //
+
+ IrpContext->Flags = 0;
+ IrpContext->Icb = NULL;
+ IrpContext->pEx = NULL;
+ IrpContext->TimeoutRoutine = NULL;
+ IrpContext->CompletionSendRoutine = NULL;
+ IrpContext->ReceiveDataRoutine = NULL;
+ IrpContext->pTdiStruct = NULL;
+
+ //
+ // Clean the specific data zone.
+ //
+
+ RtlZeroMemory( &(IrpContext->Specific), sizeof( IrpContext->Specific ) );
+ }
+
+ ExInterlockedIncrementLong(&ContextCount,&ContextInterlock);
+
+ //
+ // Save away the fields in the Irp that might be tromped by
+ // building the Irp for the exchange with the server.
+ //
+
+ IrpContext->pOriginalIrp = pIrp;
+
+ if ( pIrp != NULL) {
+ IrpContext->pOriginalSystemBuffer = pIrp->AssociatedIrp.SystemBuffer;
+ IrpContext->pOriginalUserBuffer = pIrp->UserBuffer;
+ IrpContext->pOriginalMdlAddress = pIrp->MdlAddress;
+ }
+
+#ifdef NWDBG
+ IrpContext->pNpScb = NULL;
+#endif
+
+ ASSERT( !BooleanFlagOn( IrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE ) );
+
+ return IrpContext;
+}
+
+ VOID
+FreeIrpContext (
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ Initialize a work queue structure, allocating all structures used for it.
+
+Arguments:
+
+ PIRP_CONTEXT IrpContext - Irp Context to free.
+ None
+
+
+Return Value:
+
+
+--*/
+{
+
+ ASSERT( IrpContext->NodeTypeCode == NW_NTC_IRP_CONTEXT );
+ ASSERT( !BooleanFlagOn( IrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE ) );
+ ASSERT( IrpContext->PostProcessRoutine == NULL );
+
+ FreeReceiveIrp( IrpContext );
+
+#ifdef NWDBG
+ IrpContext->DebugValue = 0;
+#endif
+ IrpContext->Flags = 0;
+
+ //
+ // Cleanup the Irp needs to be restored to its original settings.
+ //
+
+ if ( IrpContext->pOriginalIrp != NULL ) {
+
+ PIRP pIrp = IrpContext->pOriginalIrp;
+
+ pIrp->AssociatedIrp.SystemBuffer = IrpContext->pOriginalSystemBuffer;
+
+ pIrp->UserBuffer = IrpContext->pOriginalUserBuffer;
+
+ pIrp->MdlAddress = IrpContext->pOriginalMdlAddress;
+
+#ifdef NWDBG
+ IrpContext->pOriginalIrp = NULL;
+#endif
+ }
+
+#ifdef NWDBG
+ RtlZeroMemory( &IrpContext->WorkQueueItem, sizeof( WORK_QUEUE_ITEM ) );
+#endif
+
+ ExInterlockedDecrementLong(&ContextCount, &ContextInterlock);
+
+ if ( ExInterlockedDecrementLong(&FreeContextCount, &ContextInterlock) !=
+ ResultNegative ) {
+
+ //
+ // We use the first two longwords of the IRP context as a list entry
+ // when we free it to the list.
+ //
+
+ ExInterlockedInsertTailList(&IrpContextList,
+ (PLIST_ENTRY )IrpContext,
+ &IrpContextInterlock);
+ } else {
+ //
+ // We already have as many free context as we allow so destroy
+ // this context. Restore FreeContextCount to its original value.
+ //
+
+ ExInterlockedIncrementLong( &FreeContextCount, &ContextInterlock );
+
+ FREE_MDL( IrpContext->TxMdl );
+ FREE_MDL( IrpContext->RxMdl );
+ FREE_POOL(IrpContext);
+#ifdef NWDBG
+ ContextCount --;
+#endif
+ }
+}
+
+
+ VOID
+InitializeIrpContext (
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Initialize the Irp Context system
+
+Arguments:
+
+ None.
+
+
+Return Value:
+ None.
+
+--*/
+{
+ PAGED_CODE();
+
+ KeInitializeSpinLock(&IrpContextInterlock);
+ KeInitializeSpinLock(&ContextInterlock);
+ InitializeListHead(&IrpContextList);
+ InitializeListHead(&MiniIrpContextList);
+}
+
+ VOID
+UninitializeIrpContext (
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Initialize the Irp Context system
+
+Arguments:
+
+ None.
+
+
+Return Value:
+ None.
+
+--*/
+{
+ PIRP_CONTEXT IrpContext;
+ PLIST_ENTRY ListEntry;
+ PMINI_IRP_CONTEXT MiniIrpContext;
+
+ PAGED_CODE();
+
+ //
+ // Free all the IRP contexts.
+ //
+
+ while ( !IsListEmpty( &IrpContextList ) ) {
+ IrpContext = (PIRP_CONTEXT)RemoveHeadList( &IrpContextList );
+
+ FREE_MDL( IrpContext->TxMdl );
+ FREE_MDL( IrpContext->RxMdl );
+ FREE_POOL(IrpContext);
+ }
+
+ while ( !IsListEmpty( &MiniIrpContextList ) ) {
+
+ ListEntry = RemoveHeadList( &MiniIrpContextList );
+ MiniIrpContext = CONTAINING_RECORD( ListEntry, MINI_IRP_CONTEXT, Next );
+
+ FREE_POOL( MiniIrpContext->Buffer );
+ FREE_MDL( MiniIrpContext->Mdl2 );
+ FREE_MDL( MiniIrpContext->Mdl1 );
+ FREE_IRP( MiniIrpContext->Irp );
+ FREE_POOL( MiniIrpContext );
+ }
+}
+
+
+VOID
+NwCompleteRequest (
+ PIRP_CONTEXT IrpContext,
+ NTSTATUS Status
+ )
+/*++
+
+Routine Description:
+
+ The following procedure is used by the FSP and FSD routines to complete
+ an IRP.
+
+Arguments:
+
+ IrpContext - A pointer to the IRP context information.
+
+ Status - The status to use to complete the IRP.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PIRP Irp;
+
+ if ( IrpContext == NULL ) {
+ return;
+ }
+
+ if ( Status == STATUS_PENDING ) {
+ return;
+ }
+
+ if ( Status == STATUS_INSUFFICIENT_RESOURCES ) {
+ Error( EVENT_NWRDR_RESOURCE_SHORTAGE, Status, NULL, 0, 0 );
+ }
+
+ Irp = IrpContext->pOriginalIrp;
+
+ Irp->IoStatus.Status = Status;
+ DebugTrace(0, Dbg, "Completing Irp with status %X\n", Status );
+
+ // Restore the Irp to its original state
+
+ FreeIrpContext( IrpContext );
+
+ IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
+
+ return;
+}
+
+
+VOID
+NwAppendToQueueAndWait(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine appends an IrpContext to the SCB queue, and waits the
+ the queue to be ready to process the Irp.
+
+Arguments:
+
+ IrpContext - A pointer to the IRP context information.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ BOOLEAN AtFront;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwAppendToQueueAndWait\n", 0);
+
+ IrpContext->RunRoutine = SetEvent;
+
+#ifdef MSWDBG
+ ASSERT( IrpContext->Event.Header.SignalState == 0 );
+#endif
+
+ AtFront = AppendToScbQueue( IrpContext, IrpContext->pNpScb );
+
+ if ( AtFront ) {
+ KickQueue( IrpContext->pNpScb );
+ }
+
+ //
+ // Wait until we get to the front of the queue.
+ //
+
+ KeWaitForSingleObject(
+ &IrpContext->Event,
+ UserRequest,
+ KernelMode,
+ FALSE,
+ NULL );
+
+ ASSERT( IrpContext->pNpScb->Requests.Flink == &IrpContext->NextRequest );
+
+ DebugTrace(-1, Dbg, "NwAppendToQueueAndWait\n", 0);
+ return;
+}
+
+
+VOID
+NwDequeueIrpContext(
+ IN PIRP_CONTEXT pIrpContext,
+ IN BOOLEAN OwnSpinLock
+ )
+/*++
+
+Routine Description:
+
+ This routine removes an IRP Context from the front the SCB queue.
+
+Arguments:
+
+ IrpContext - A pointer to the IRP context information.
+
+ OwnSpinLock - If TRUE, the caller owns the SCB spin lock.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PLIST_ENTRY pListEntry;
+ KIRQL OldIrql;
+ PNONPAGED_SCB pNpScb;
+
+ DebugTrace(+1, Dbg, "NwDequeueIrpContext\n", 0);
+
+ if (!BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE ) ) {
+ DebugTrace(-1, Dbg, "NwDequeueIrpContext\n", 0);
+ return;
+ }
+
+ pNpScb = pIrpContext->pNpScb;
+
+ if ( !OwnSpinLock ) {
+ KeAcquireSpinLock( &pNpScb->NpScbSpinLock, &OldIrql );
+ }
+
+ //
+ // Disable timer from looking at this queue.
+ //
+
+ pNpScb->OkToReceive = FALSE;
+
+ pListEntry = RemoveHeadList( &pNpScb->Requests );
+
+ if ( !OwnSpinLock ) {
+ KeReleaseSpinLock( &pNpScb->NpScbSpinLock, OldIrql );
+ }
+
+#ifdef NWDBG
+ ASSERT ( CONTAINING_RECORD( pListEntry, IRP_CONTEXT, NextRequest ) == pIrpContext );
+
+ {
+
+ PIRP_CONTEXT RemovedContext = CONTAINING_RECORD( pListEntry, IRP_CONTEXT, NextRequest );
+ if ( RemovedContext != pIrpContext ) {
+ DbgBreakPoint();
+ }
+
+ }
+
+ DebugTrace(
+ 0,
+ Dbg,
+ "Dequeued IRP Context %08lx\n",
+ CONTAINING_RECORD( pListEntry, IRP_CONTEXT, NextRequest ) );
+
+#ifdef MSWDBG
+ pNpScb->RequestDequeued = TRUE;
+#endif
+
+#endif
+
+ ClearFlag( pIrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE );
+
+ //
+ // Give the next IRP context on the SCB queue a chance to run.
+ //
+
+ KickQueue( pNpScb );
+
+ DebugTrace(-1, Dbg, "NwDequeueIrpContext\n", 0);
+ return;
+}
+
+
+VOID
+NwCancelIrp (
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+/*++
+
+Routine Description:
+
+ This routine implements the cancel function for an IRP being processed
+ by the redirector.
+
+Arguments:
+
+ DeviceObject - ignored
+
+ Irp - Supplies the Irp being cancelled.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PLIST_ENTRY listEntry, nextListEntry;
+ KIRQL OldIrql;
+ PIRP_CONTEXT pTestIrpContext;
+ PIRP pTestIrp;
+
+ UNREFERENCED_PARAMETER( DeviceObject );
+
+ //
+ // We now need to void the cancel routine and release the io cancel
+ // spin-lock.
+ //
+
+ IoSetCancelRoutine( Irp, NULL );
+ IoReleaseCancelSpinLock( Irp->CancelIrql );
+
+ //
+ // Now we have to search for the IRP to cancel everywhere. So just
+ // look for cancelled IRPs and process them all.
+ //
+
+ //
+ // Process the Get Message queue.
+ //
+
+ KeAcquireSpinLock( &NwMessageSpinLock, &OldIrql );
+
+ for ( listEntry = NwGetMessageList.Flink;
+ listEntry != &NwGetMessageList;
+ listEntry = nextListEntry ) {
+
+ nextListEntry = listEntry->Flink;
+
+ //
+ // If the file object of the queued request, matches the file object
+ // that is being closed, remove the IRP from the queue, and
+ // complete it with an error.
+ //
+
+ pTestIrpContext = CONTAINING_RECORD( listEntry, IRP_CONTEXT, NextRequest );
+ pTestIrp = pTestIrpContext->pOriginalIrp;
+
+ if ( pTestIrp->Cancel ) {
+ RemoveEntryList( listEntry );
+ NwCompleteRequest( pTestIrpContext, STATUS_CANCELLED );
+ }
+
+ }
+
+ KeReleaseSpinLock( &NwMessageSpinLock, OldIrql );
+
+ //
+ // Process the set of SCB IRP queues.
+ //
+
+ // BUGBUG. Not done yet. Needed?
+
+ //
+ // And return to our caller
+ //
+
+ return;
+}
+
+PMINI_IRP_CONTEXT
+AllocateMiniIrpContext (
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine allocates an IRP, a buffer, and an MDL for sending
+ a burst write fragment.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ Irp - The allocated and initialized IRP.
+ NULL - The IRP allocation failed.
+
+--*/
+
+{
+ PMINI_IRP_CONTEXT MiniIrpContext;
+ PIRP Irp = NULL;
+ PMDL Mdl1 = NULL, Mdl2 = NULL;
+ PVOID Buffer = NULL;
+ PLIST_ENTRY ListEntry;
+
+ ListEntry = ExInterlockedRemoveHeadList(
+ &MiniIrpContextList,
+ &IrpContextInterlock);
+
+ if ( ListEntry == NULL) {
+
+ try {
+ MiniIrpContext = ALLOCATE_POOL_EX( NonPagedPool, sizeof( *MiniIrpContext ) );
+
+ MiniIrpContext->NodeTypeCode = NW_NTC_MINI_IRP_CONTEXT;
+ MiniIrpContext->NodeByteSize = sizeof( *MiniIrpContext );
+
+ Irp = ALLOCATE_IRP(
+ IrpContext->pNpScb->Server.pDeviceObject->StackSize,
+ FALSE );
+
+ if ( Irp == NULL ) {
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ Buffer = ALLOCATE_POOL_EX( NonPagedPool, sizeof( NCP_BURST_HEADER ) );
+
+ Mdl1 = ALLOCATE_MDL( Buffer, sizeof( NCP_BURST_HEADER ), FALSE, FALSE, NULL );
+ if ( Mdl1 == NULL ) {
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ MmBuildMdlForNonPagedPool( Mdl1 );
+
+ //
+ // Since this MDL can be used to send a packet on any server,
+ // allocate an MDL large enough for any packet size.
+ //
+
+ Mdl2 = ALLOCATE_MDL( 0, 65535 + PAGE_SIZE - 1, FALSE, FALSE, NULL );
+ if ( Mdl2 == NULL ) {
+ ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ Mdl1->Next = Mdl2;
+
+ MiniIrpContext->Irp = Irp;
+ MiniIrpContext->Buffer = Buffer;
+ MiniIrpContext->Mdl1 = Mdl1;
+ MiniIrpContext->Mdl2 = Mdl2;
+
+ ExInterlockedIncrementLong( &MiniContextCount, &ContextInterlock );
+
+ } except( EXCEPTION_EXECUTE_HANDLER ) {
+
+ if ( Buffer != NULL ) {
+ FREE_POOL( Buffer );
+ }
+
+ if ( Irp != NULL ) {
+ FREE_IRP( Irp );
+ }
+
+ if ( Mdl1 != NULL ) {
+ FREE_MDL( Mdl1 );
+ }
+
+ return( NULL );
+ }
+
+ } else {
+
+ //
+ // Record that we have removed an entry from the free list.
+ //
+
+ ExInterlockedIncrementLong( &FreeMiniContextCount, &ContextInterlock );
+ MiniIrpContext = CONTAINING_RECORD( ListEntry, MINI_IRP_CONTEXT, Next );
+
+ }
+
+ MiniIrpContext->IrpContext = IrpContext;
+
+ return( MiniIrpContext );
+}
+
+VOID
+FreeMiniIrpContext (
+ PMINI_IRP_CONTEXT MiniIrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine frees a mini IRP Context.
+
+Arguments:
+
+ MiniIrpContext - The mini IRP context to free.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ ExInterlockedDecrementLong( &MiniContextCount, &ContextInterlock );
+
+ if ( ExInterlockedDecrementLong( &FreeMiniContextCount, &ContextInterlock) !=
+ ResultNegative ) {
+
+ //
+ // Ok to keep this mini irp context. Just queue it to the free list.
+ //
+
+ MmPrepareMdlForReuse( MiniIrpContext->Mdl2 );
+
+ ExInterlockedInsertTailList(
+ &MiniIrpContextList,
+ &MiniIrpContext->Next,
+ &IrpContextInterlock );
+
+ } else {
+
+ //
+ // We already have as many free context as we allow so destroy
+ // this context. Restore FreeContextCount to its original value.
+ //
+
+ ExInterlockedIncrementLong( &FreeContextCount, &ContextInterlock );
+
+ FREE_POOL( MiniIrpContext->Buffer );
+ FREE_MDL( MiniIrpContext->Mdl2 );
+ FREE_MDL( MiniIrpContext->Mdl1 );
+ FREE_IRP( MiniIrpContext->Irp );
+
+ FREE_POOL( MiniIrpContext );
+ }
+}
+
diff --git a/private/nw/rdr/write.c b/private/nw/rdr/write.c
new file mode 100644
index 000000000..60d6a2cd5
--- /dev/null
+++ b/private/nw/rdr/write.c
@@ -0,0 +1,3063 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ Write.c
+
+Abstract:
+
+ This module implements support for NtWriteFile for the
+ NetWare redirector called by the dispatch driver.
+
+Author:
+
+ Colin Watson [ColinW] 07-Apr-1993
+
+Revision History:
+
+--*/
+
+#include "Procs.h"
+#include <stdlib.h>
+
+//
+// The local debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_WRITE)
+
+//
+// The header overhead in the first packet of a burst write.
+//
+
+#define BURST_WRITE_HEADER_SIZE \
+ ( sizeof( NCP_BURST_WRITE_REQUEST ) - sizeof( NCP_BURST_HEADER ) )
+
+//
+// Local procedure prototypes
+//
+
+NTSTATUS
+NwCommonWrite (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+WriteNcp(
+ PIRP_CONTEXT IrpContext,
+ LARGE_INTEGER ByteOffset,
+ ULONG BufferLength,
+ PVOID WriteBuffer,
+ PMDL WriteMdl
+ );
+
+NTSTATUS
+QueryEofForWriteCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ );
+
+NTSTATUS
+WriteNcpCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ );
+
+NTSTATUS
+BurstWrite(
+ PIRP_CONTEXT IrpContext,
+ LARGE_INTEGER ByteOffset,
+ ULONG BufferLength,
+ PVOID WriteBuffer,
+ PMDL WriteMdl
+ );
+
+NTSTATUS
+SendWriteBurst(
+ PIRP_CONTEXT IrpContext,
+ ULONG Offset,
+ USHORT Length,
+ BOOLEAN EndOfBurst,
+ BOOLEAN Retransmission
+ );
+
+VOID
+BuildBurstWriteFirstReq(
+ PIRP_CONTEXT IrpContext,
+ PVOID Buffer,
+ ULONG DataSize,
+ PMDL BurstMdl,
+ UCHAR Flags,
+ ULONG Handle,
+ ULONG FileOffset
+ );
+
+VOID
+BuildBurstWriteNextReq(
+ PIRP_CONTEXT IrpContext,
+ PVOID Buffer,
+ ULONG DataSize,
+ UCHAR BurstFlags,
+ ULONG BurstOffset,
+ PMDL BurstHeaderMdl,
+ PMDL BurstDataMdl
+ );
+
+NTSTATUS
+BurstWriteCompletionSend(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ );
+
+NTSTATUS
+BurstWriteCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ );
+
+VOID
+BurstWriteTimeout(
+ PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+BurstWriteReconnect(
+ PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+NwCommonFlushBuffers (
+ IN PIRP_CONTEXT IrpContext
+ );
+
+NTSTATUS
+FlushBuffersCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ );
+
+NTSTATUS
+SendSecondaryPacket(
+ PIRP_CONTEXT IrpContext,
+ PIRP Irp
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, NwFsdWrite )
+#pragma alloc_text( PAGE, NwCommonWrite )
+#pragma alloc_text( PAGE, DoWrite )
+#pragma alloc_text( PAGE, WriteNcp )
+#pragma alloc_text( PAGE, BurstWrite )
+#pragma alloc_text( PAGE, SendWriteBurst )
+#pragma alloc_text( PAGE, ResubmitBurstWrite )
+#pragma alloc_text( PAGE, NwFsdFlushBuffers )
+#pragma alloc_text( PAGE, NwCommonFlushBuffers )
+#pragma alloc_text( PAGE, BuildBurstWriteFirstReq )
+#pragma alloc_text( PAGE, BuildBurstWriteNextReq )
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, WriteNcpCallback )
+#pragma alloc_text( PAGE1, BurstWriteCompletionSend )
+#pragma alloc_text( PAGE1, BurstWriteCallback )
+#pragma alloc_text( PAGE1, BurstWriteTimeout )
+#pragma alloc_text( PAGE1, FlushBuffersCallback )
+#pragma alloc_text( PAGE1, SendSecondaryPacket )
+#pragma alloc_text( PAGE1, BurstWriteReconnect )
+#endif
+
+#endif
+
+#if 0 // Not pageable
+
+// see ifndef QFE_BUILD above
+
+#endif
+
+
+NTSTATUS
+NwFsdWrite(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is the FSD routine that handles NtWriteFile.
+
+Arguments:
+
+ NwfsDeviceObject - Supplies the device object for the write function.
+
+ Irp - Supplies the IRP to process.
+
+Return Value:
+
+ NTSTATUS - The result status.
+
+--*/
+
+{
+ PIRP_CONTEXT pIrpContext = NULL;
+ NTSTATUS status;
+ BOOLEAN TopLevel;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwFsdWrite\n", 0);
+
+ //
+ // Call the common write routine.
+ //
+
+ FsRtlEnterFileSystem();
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ try {
+
+ pIrpContext = AllocateIrpContext( Irp );
+ status = NwCommonWrite( pIrpContext );
+
+ } except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
+
+ if ( pIrpContext == NULL ) {
+
+ //
+ // If we couldn't allocate an irp context, just complete
+ // irp without any fanfare.
+ //
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
+
+ } else {
+
+ //
+ // We had some trouble trying to perform the requested
+ // operation, so we'll abort the I/O request with
+ // the error Status that we get back from the
+ // execption code
+ //
+
+ status = NwProcessException( pIrpContext, GetExceptionCode() );
+ }
+
+ }
+
+ if ( pIrpContext ) {
+
+ if ( status != STATUS_PENDING ) {
+ NwDequeueIrpContext( pIrpContext, FALSE );
+ }
+
+ NwCompleteRequest( pIrpContext, status );
+ }
+
+ if ( TopLevel ) {
+ NwSetTopLevelIrp( NULL );
+ }
+ FsRtlExitFileSystem();
+
+ //
+ // Return to the caller.
+ //
+
+ DebugTrace(-1, Dbg, "NwFsdWrite -> %08lx\n", status );
+
+ Stats.WriteOperations++;
+
+ return status;
+}
+
+
+NTSTATUS
+NwCommonWrite (
+ IN PIRP_CONTEXT IrpContext
+ )
+
+/*++
+
+Routine Description:
+
+ This routine does the common code for NtWriteFile.
+
+Arguments:
+
+ IrpContext - Supplies the request being processed.
+
+Return Value:
+
+ NTSTATUS - The return status for the operation
+
+--*/
+
+{
+ NTSTATUS status;
+
+ PIRP Irp;
+ PIO_STACK_LOCATION irpSp;
+
+ NODE_TYPE_CODE nodeTypeCode;
+ PICB icb;
+ PFCB fcb;
+ PNONPAGED_FCB pNpFcb;
+ PVOID fsContext;
+
+ BOOLEAN WroteToCache;
+ LARGE_INTEGER ByteOffset;
+ LARGE_INTEGER PreviousByteOffset;
+ ULONG BufferLength;
+
+ PULONG pFileSize;
+
+ // ULONG FileLength;
+
+ PAGED_CODE();
+
+ //
+ // Get the current stack location
+ //
+
+ Irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ DebugTrace(+1, Dbg, "CommonWrite...\n", 0);
+ DebugTrace( 0, Dbg, "Irp = %08lx\n", (ULONG)Irp);
+
+ //
+ // Decode the file object to figure out who we are. If the result
+ // is not the root DCB then its an illegal parameter.
+ //
+
+ nodeTypeCode = NwDecodeFileObject( irpSp->FileObject,
+ &fsContext,
+ (PVOID *)&icb );
+
+ fcb = (PFCB)icb->SuperType.Fcb;
+
+ if (((nodeTypeCode != NW_NTC_ICB) &&
+ (nodeTypeCode != NW_NTC_ICB_SCB)) ||
+ (!icb->HasRemoteHandle) ) {
+
+ DebugTrace(0, Dbg, "Not a file\n", 0);
+
+ status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "CommonWrite -> %08lx\n", status );
+ return status;
+ }
+
+ //
+ // Make sure that this ICB is still active.
+ //
+
+ NwVerifyIcbSpecial( icb );
+
+ if ( fcb->NodeTypeCode == NW_NTC_FCB ) {
+
+ IrpContext->pScb = fcb->Scb;
+ IrpContext->pNpScb = IrpContext->pScb->pNpScb;
+ IrpContext->Icb = icb;
+ pFileSize = &icb->NpFcb->Header.FileSize.LowPart;
+
+ } else if ( fcb->NodeTypeCode == NW_NTC_SCB ) {
+
+ IrpContext->pScb = icb->SuperType.Scb;
+ IrpContext->pNpScb = IrpContext->pScb->pNpScb;
+ IrpContext->Icb = icb;
+ fcb = NULL;
+ pFileSize = &icb->FileSize;
+
+ } else {
+
+ DebugTrace(0, Dbg, "Not a file or a server\n", 0);
+
+ status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "CommonWrite -> %08lx\n", status );
+ return status;
+ }
+
+ ByteOffset = irpSp->Parameters.Write.ByteOffset;
+ BufferLength = irpSp->Parameters.Write.Length;
+
+ //
+ // Can't handle large byte offset, but write to EOF is okay.
+ //
+
+ if ( ByteOffset.HighPart != 0 ) {
+
+ if ( ByteOffset.HighPart != 0xFFFFFFFF ||
+ ByteOffset.LowPart != 0xFFFFFFFF ) {
+
+ return( STATUS_INVALID_PARAMETER );
+ }
+ }
+
+ if (FlagOn(irpSp->FileObject->Flags, FO_SYNCHRONOUS_IO) &&
+ !FlagOn(Irp->Flags, IRP_PAGING_IO)) {
+
+ PreviousByteOffset.QuadPart = irpSp->FileObject->CurrentByteOffset.QuadPart;
+ irpSp->FileObject->CurrentByteOffset.QuadPart = ByteOffset.QuadPart;
+ }
+
+ //
+ // Paging I/O is not allowed to extend the file
+ //
+
+ if ((FlagOn(Irp->Flags, IRP_PAGING_IO)) &&
+ (ByteOffset.LowPart + BufferLength > *pFileSize )) {
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ if ( ByteOffset.LowPart + BufferLength <= *pFileSize ) {
+
+ //
+ // Someone else extended the file. Do nothing.
+ //
+
+ // continue;
+
+ } else if ( ByteOffset.LowPart > *pFileSize ) {
+
+ //
+ // Whole write is off the end of the buffer
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ Irp->IoStatus.Information = 0;
+ return( STATUS_SUCCESS );
+
+ } else {
+
+ //
+ // Truncate request to size of file
+ //
+
+ BufferLength = *pFileSize - ByteOffset.LowPart;
+
+ }
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ }
+
+
+ //
+ // Special case 0 length write.
+ //
+
+ if ( BufferLength == 0 ) {
+ Irp->IoStatus.Information = 0;
+ return( STATUS_SUCCESS );
+ }
+
+ //
+ // Remember the original MDL, so that we can restore it when we are done.
+ //
+
+ IrpContext->pOriginalMdlAddress = Irp->MdlAddress;
+
+ //
+ // Attempt to write this data to our private cache
+ //
+ // BUGBUG - Cheap fix, don't process MDL based writes. Fix up
+ // post Daytona beta.
+ //
+
+ if ( fcb != NULL && Irp->UserBuffer != NULL ) {
+
+ WroteToCache = CacheWrite(
+ IrpContext,
+ fcb->NonPagedFcb,
+ ByteOffset.LowPart,
+ BufferLength,
+ Irp->UserBuffer );
+
+ if ( WroteToCache ) {
+
+ Irp->IoStatus.Information = BufferLength;
+
+ //
+ // Update the current byte offset in the file if it is a
+ // synchronous file (and this is not paging I/O).
+ //
+
+ if (FlagOn(irpSp->FileObject->Flags, FO_SYNCHRONOUS_IO) &&
+ !FlagOn(Irp->Flags, IRP_PAGING_IO)) {
+
+ irpSp->FileObject->CurrentByteOffset.QuadPart += BufferLength;
+ }
+
+ //
+ // Record write offset and size to discover a sequential write pattern.
+ //
+
+ fcb->LastReadOffset = irpSp->Parameters.Write.ByteOffset.LowPart;
+ fcb->LastReadSize = irpSp->Parameters.Write.Length;
+
+ //
+ // If the file was extended, record the new file size.
+ //
+
+ if ( fcb->LastReadOffset + fcb->LastReadSize >
+ fcb->NonPagedFcb->Header.FileSize.LowPart ) {
+
+ fcb->NonPagedFcb->Header.FileSize.LowPart =
+ fcb->LastReadOffset + fcb->LastReadSize;
+ }
+
+ DebugTrace(-1, Dbg, "NwCommonWrite -> %08lx\n", STATUS_SUCCESS );
+ return( STATUS_SUCCESS );
+ }
+
+ }
+
+ status = DoWrite(
+ IrpContext,
+ ByteOffset,
+ BufferLength,
+ Irp->UserBuffer,
+ IrpContext->pOriginalMdlAddress );
+
+ if ( NT_SUCCESS( status ) ) {
+
+ //
+ // We actually wrote something out to the wire. If there was a read
+ // cache and this write overlapped it, invalidate the read cache data
+ // so that we get good data on future reads.
+ //
+
+ if ( fcb != NULL ) {
+
+ pNpFcb = fcb->NonPagedFcb;
+
+ if ( ( pNpFcb->CacheBuffer != NULL ) &&
+ ( pNpFcb->CacheSize != 0 ) &&
+ ( pNpFcb->CacheType == ReadAhead ) ) {
+
+ //
+ // Two cases: (1) offset is less than cache offset
+ // (2) offset is inside cached region
+ //
+
+ if ( ByteOffset.LowPart < pNpFcb->CacheFileOffset ) {
+
+ //
+ // Did we run into the read cache?
+ //
+
+ if ( BufferLength >
+ (pNpFcb->CacheFileOffset - ByteOffset.LowPart) ) {
+
+ DebugTrace( 0, Dbg, "Invalidated read cache for %08lx.\n", pNpFcb );
+ pNpFcb->CacheDataSize = 0;
+
+ }
+
+ } else {
+
+ //
+ // Did we write over any of the cached region.
+ //
+
+ if ( ByteOffset.LowPart <= ( pNpFcb->CacheFileOffset + pNpFcb->CacheDataSize ) ) {
+
+ DebugTrace( 0, Dbg, "Invalidated read cache for %08lx.\n", pNpFcb );
+ pNpFcb->CacheDataSize = 0;
+
+ }
+ }
+ }
+
+ }
+
+ Irp->IoStatus.Information = IrpContext->Specific.Write.WriteOffset;
+
+ //
+ // Update the current byte offset in the file if it is a
+ // synchronous file (and this is not paging I/O).
+ //
+
+ if (FlagOn(irpSp->FileObject->Flags, FO_SYNCHRONOUS_IO) &&
+ !FlagOn(Irp->Flags, IRP_PAGING_IO)) {
+
+ irpSp->FileObject->CurrentByteOffset.QuadPart += BufferLength;
+ }
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ if (ByteOffset.LowPart + BufferLength > *pFileSize ) {
+
+ *pFileSize = ByteOffset.LowPart + BufferLength;
+
+ }
+
+ } else {
+
+ //
+ // The request failed, don't move the file pointer.
+ //
+
+ if (FlagOn(irpSp->FileObject->Flags, FO_SYNCHRONOUS_IO) &&
+ !FlagOn(Irp->Flags, IRP_PAGING_IO)) {
+
+ irpSp->FileObject->CurrentByteOffset.QuadPart = PreviousByteOffset.QuadPart;
+ }
+
+ }
+
+ DebugTrace(-1, Dbg, "CommonWrite -> %08lx\n", status);
+
+ return status;
+}
+
+NTSTATUS
+DoWrite(
+ PIRP_CONTEXT IrpContext,
+ LARGE_INTEGER ByteOffset,
+ ULONG BufferLength,
+ PVOID WriteBuffer,
+ PMDL WriteMdl OPTIONAL
+ )
+/*++
+
+Routine Description:
+
+ This routine does a write to the network via the most efficient
+ available protocol.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+ ByteOffset - The file offset to write.
+
+ BufferLength - The number of bytes to write.
+
+ WriteBuffer - A pointer to the source buffer.
+
+ WriteMdl = An optional MDL for the write buffer.
+
+Return Value:
+
+ Status of transfer.
+
+--*/
+{
+ NTSTATUS status;
+
+ PAGED_CODE();
+
+ if ( IrpContext->pNpScb->SendBurstModeEnabled &&
+ BufferLength > IrpContext->pNpScb->BufferSize ) {
+ status = BurstWrite( IrpContext, ByteOffset, BufferLength, WriteBuffer, WriteMdl );
+ } else {
+ status = WriteNcp( IrpContext, ByteOffset, BufferLength, WriteBuffer, WriteMdl );
+ }
+
+ //
+ // Reset IrpContext parameters
+ //
+
+ IrpContext->TxMdl->Next = NULL;
+ IrpContext->CompletionSendRoutine = NULL;
+ IrpContext->TimeoutRoutine = NULL;
+ IrpContext->Flags &= ~(IRP_FLAG_RETRY_SEND | IRP_FLAG_BURST_REQUEST | IRP_FLAG_BURST_PACKET |
+ IRP_FLAG_BURST_WRITE | IRP_FLAG_NOT_SYSTEM_PACKET );
+ IrpContext->pTdiStruct = NULL;
+
+ IrpContext->pOriginalIrp->MdlAddress = IrpContext->pOriginalMdlAddress;
+ IrpContext->pOriginalIrp->AssociatedIrp.SystemBuffer = IrpContext->pOriginalSystemBuffer;
+
+ return( status );
+}
+
+NTSTATUS
+WriteNcp(
+ PIRP_CONTEXT IrpContext,
+ LARGE_INTEGER ByteOffset,
+ ULONG BufferLength,
+ PVOID WriteBuffer,
+ PMDL WriteMdl
+ )
+/*++
+
+Routine Description:
+
+ This routine exchanges a series of write NCPs with the server.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+ Icb - Supplies the file specific information.
+
+Return Value:
+
+ Status of transfer.
+
+--*/
+{
+ PICB Icb;
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ ULONG Length; // Size we will send to the server
+ ULONG FileLength;
+
+ PSCB pScb;
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+ PMDL DataMdl;
+ BOOLEAN Done;
+
+ PAGED_CODE();
+
+ Icb = IrpContext->Icb;
+ irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+ DebugTrace(+1, Dbg, "WriteNcp...\n", 0);
+ DebugTrace( 0, Dbg, "irp = %08lx\n", (ULONG)irp);
+ DebugTrace( 0, Dbg, "WriteLen= %ld\n", BufferLength);
+ DebugTrace( 0, Dbg, "HOffset = %lx\n", ByteOffset.HighPart);
+ DebugTrace( 0, Dbg, "LOffset = %lx\n", ByteOffset.LowPart);
+
+ if (Icb->SuperType.Fcb->NodeTypeCode == NW_NTC_FCB) {
+ pScb = Icb->SuperType.Fcb->Scb;
+ DebugTrace( 0, Dbg, "File = %wZ\n", &Icb->SuperType.Fcb->FullFileName);
+ } else {
+
+ //
+ // Write to a queue
+ //
+
+ pScb = Icb->SuperType.Scb;
+
+ }
+
+ ASSERT (pScb->NodeTypeCode == NW_NTC_SCB);
+
+ if ( ByteOffset.HighPart == 0xFFFFFFFF &&
+ ByteOffset.LowPart == FILE_WRITE_TO_END_OF_FILE ) {
+
+ //
+ // Write relative to end of file. Find the end of file.
+ //
+
+ status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "F-r",
+ NCP_GET_FILE_SIZE,
+ &Icb->Handle, sizeof( Icb->Handle ) );
+
+ if ( NT_SUCCESS( status ) ) {
+ status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nd",
+ &FileLength );
+
+ if ( !NT_SUCCESS( status ) ) {
+ return status;
+ }
+
+ }
+
+ IrpContext->Specific.Write.FileOffset = FileLength;
+ }
+
+ Length = MIN( (ULONG)IrpContext->pNpScb->BufferSize, BufferLength );
+ DebugTrace( 0, Dbg, "Length = %ld\n", Length);
+
+ //
+ // The server will not accept writes that cross 4k boundaries in the file
+ //
+
+ if ((IrpContext->pNpScb->PageAlign) &&
+ (DIFFERENT_PAGES( ByteOffset.LowPart, Length ))) {
+ Length = 4096 -
+ ((ULONG)ByteOffset.LowPart & (4096-1));
+ }
+
+ IrpContext->Specific.Write.Buffer = WriteBuffer;
+ IrpContext->Specific.Write.WriteOffset = 0;
+ IrpContext->Specific.Write.RemainingLength = BufferLength;
+ IrpContext->Specific.Write.LastWriteLength = Length;
+ IrpContext->Specific.Write.FileOffset = ByteOffset.LowPart;
+ IrpContext->Specific.Write.PartialMdl = NULL;
+
+ Done = FALSE;
+
+ while ( !Done ) {
+
+ //
+ // Setup to do at most 64K of i/o asynchronously, or buffer length.
+ //
+
+ IrpContext->Specific.Write.BurstLength =
+ MIN( 64 * 1024, IrpContext->Specific.Write.RemainingLength );
+ IrpContext->Specific.Write.BurstOffset = 0;
+
+ //
+ // Try to allocate an MDL for this i/o.
+ //
+
+ DataMdl = ALLOCATE_MDL(
+ (PCHAR)IrpContext->Specific.Write.Buffer +
+ IrpContext->Specific.Write.WriteOffset,
+ IrpContext->Specific.Write.BurstLength,
+ FALSE, // Secondary Buffer
+ FALSE, // Charge Quota
+ NULL);
+
+ if ( DataMdl == NULL ) {
+ if ( IrpContext->Specific.Write.PartialMdl != NULL ) {
+ FREE_MDL( IrpContext->Specific.Write.PartialMdl );
+ }
+ DebugTrace(-1, Dbg, "WriteNcp -> %X\n", STATUS_INSUFFICIENT_RESOURCES );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ IrpContext->Specific.Write.FullMdl = DataMdl;
+
+
+ //
+ // If there is no MDL for this write probe the data MDL to
+ // lock it's pages down. Otherwise, use the data MDL as
+ // a partial MDL.
+ //
+
+ if ( WriteMdl == NULL ) {
+
+ //
+ // The Probe may cause us to page in some data. If the data is from
+ // the same server we are writing to then we had better not be at
+ // the front of the queue otherwise it will wait indefinitely behind us.
+ // Its a good idea to Dequeue ourselves after each burst anyway because
+ // its a quick operation and it alow smaller requests to overtake a very
+ // large series of bursts.
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+
+ try {
+ MmProbeAndLockPages( DataMdl, irp->RequestorMode, IoReadAccess);
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ FREE_MDL( DataMdl );
+ DebugTrace(-1, Dbg, "WriteNcp -> %X\n", GetExceptionCode() );
+ return GetExceptionCode();
+ }
+
+ } else {
+ IoBuildPartialMdl(
+ WriteMdl,
+ DataMdl,
+ (PCHAR)IrpContext->Specific.Write.Buffer,
+ IrpContext->Specific.Write.BurstLength );
+ }
+
+ //
+ // Allocate a partial Mdl for the worst possible case of alignment
+ //
+
+ IrpContext->Specific.Write.PartialMdl =
+ ALLOCATE_MDL( 0 , IrpContext->pNpScb->BufferSize + PAGE_SIZE-1, FALSE, FALSE, NULL);
+
+ if ( IrpContext->Specific.Write.PartialMdl == NULL ) {
+
+ if ( WriteMdl == NULL ) {
+ MmUnlockPages( DataMdl );
+ }
+
+ FREE_MDL( DataMdl );
+ DebugTrace(-1, Dbg, "WriteNcp -> %X\n", STATUS_INSUFFICIENT_RESOURCES );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // Build a partial MDL for this write NCP.
+ //
+
+ IoBuildPartialMdl(
+ DataMdl,
+ IrpContext->Specific.Write.PartialMdl,
+ MmGetMdlVirtualAddress( DataMdl ),
+ Length );
+
+ if ( IrpContext->Specific.Write.BurstLength ==
+ IrpContext->Specific.Write.RemainingLength ) {
+ Done = TRUE;
+ }
+
+ //
+ // Send the request.
+ //
+
+ status = ExchangeWithWait(
+ IrpContext,
+ WriteNcpCallback,
+ "F-rdwf",
+ NCP_WRITE_FILE,
+ &Icb->Handle, sizeof( Icb->Handle ),
+ IrpContext->Specific.Write.FileOffset,
+ Length,
+ IrpContext->Specific.Write.PartialMdl );
+
+ Stats.WriteNcps+=2;
+
+ FREE_MDL( IrpContext->Specific.Write.PartialMdl );
+
+ //
+ // Unlock locked pages, and free our MDL.
+ //
+
+ if ( WriteMdl == NULL ) {
+ MmUnlockPages( DataMdl );
+ }
+
+ FREE_MDL( DataMdl );
+
+ //
+ // If we had a failure, we need to terminate this loop.
+ // The only status that is set is the Specific->Write
+ // status. We can not trust what comes back from the
+ // ExchangeWithWait by design.
+ //
+
+ if ( !NT_SUCCESS( IrpContext->Specific.Write.Status ) ) {
+ Done = TRUE;
+ }
+
+ //
+ // Reset the packet length since we may have less than
+ // a packet to send.
+ //
+
+ Length = MIN( (ULONG)IrpContext->pNpScb->BufferSize,
+ IrpContext->Specific.Write.RemainingLength );
+ IrpContext->Specific.Write.LastWriteLength = Length;
+
+ }
+
+ status = IrpContext->Specific.Write.Status;
+
+ DebugTrace(-1, Dbg, "WriteNcp -> %08lx\n", status );
+ return status;
+}
+
+
+NTSTATUS
+WriteNcpCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ )
+/*++
+
+Routine Description:
+
+ This routine receives the response from a user NCP.
+
+Arguments:
+
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+ ULONG Length;
+ ULONG LastLength;
+
+ DebugTrace(0, Dbg, "WriteNcpCallback...\n", 0);
+
+ if ( BytesAvailable == 0) {
+
+ //
+ // No response from server. Status is in pIrpContext->
+ // ResponseParameters.Error
+ //
+
+ IrpContext->Specific.Write.Status = STATUS_REMOTE_NOT_LISTENING;
+
+ NwSetIrpContextEvent( IrpContext );
+ return STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ LastLength = IrpContext->Specific.Write.LastWriteLength;
+ Status = ParseResponse( IrpContext, Response, BytesAvailable, "N" );
+
+ if ( NT_SUCCESS(Status) ) {
+
+ // If the last write worked then move the pointers appropriately
+
+ IrpContext->Specific.Write.RemainingLength -= LastLength;
+ IrpContext->Specific.Write.BurstLength -= LastLength;
+ IrpContext->Specific.Write.WriteOffset += LastLength;
+ IrpContext->Specific.Write.FileOffset += LastLength;
+ IrpContext->Specific.Write.BurstOffset += LastLength;
+
+ // If this is a print job, remember that we actually wrote data
+
+ if ( IrpContext->Icb->IsPrintJob ) {
+ IrpContext->Icb->ActuallyPrinted = TRUE;
+ }
+
+ } else {
+
+ //
+ // Abandon this request
+ //
+
+ IrpContext->Specific.Write.Status = Status;
+ NwSetIrpContextEvent( IrpContext );
+ DebugTrace( 0, Dbg, "WriteNcpCallback -> %08lx\n", Status );
+ return Status;
+ }
+
+
+ if ( IrpContext->Specific.Write.BurstLength != 0 ) {
+
+ // Write the next packet.
+
+ DebugTrace( 0, Dbg, "RemainingLength = %ld\n", IrpContext->Specific.Write.RemainingLength);
+ DebugTrace( 0, Dbg, "FileOffset = %ld\n", IrpContext->Specific.Write.FileOffset);
+ DebugTrace( 0, Dbg, "WriteOffset = %ld\n", IrpContext->Specific.Write.WriteOffset);
+ DebugTrace( 0, Dbg, "BurstOffset = %ld\n", IrpContext->Specific.Write.BurstOffset);
+
+
+ Length = MIN( (ULONG)IrpContext->pNpScb->BufferSize,
+ IrpContext->Specific.Write.BurstLength );
+
+ //
+ // The server will not accept writes that cross 4k boundaries
+ // in the file.
+ //
+
+ if ((IrpContext->pNpScb->PageAlign) &&
+ (DIFFERENT_PAGES( IrpContext->Specific.Write.FileOffset, Length ))) {
+
+ Length = 4096 -
+ ((ULONG)IrpContext->Specific.Write.FileOffset & (4096-1));
+
+ }
+
+ IrpContext->Specific.Write.LastWriteLength = Length;
+
+ DebugTrace( 0, Dbg, "Length = %ld\n", Length);
+
+ MmPrepareMdlForReuse( IrpContext->Specific.Write.PartialMdl );
+
+ IoBuildPartialMdl(
+ IrpContext->Specific.Write.FullMdl,
+ IrpContext->Specific.Write.PartialMdl,
+ (PUCHAR)MmGetMdlVirtualAddress( IrpContext->Specific.Write.FullMdl ) +
+ IrpContext->Specific.Write.BurstOffset,
+ Length );
+
+ //
+ // Send the request.
+ //
+
+ BuildRequestPacket(
+ IrpContext,
+ WriteNcpCallback,
+ "F-rdwf",
+ NCP_WRITE_FILE,
+ &IrpContext->Icb->Handle, sizeof( IrpContext->Icb->Handle ),
+ IrpContext->Specific.Write.FileOffset,
+ Length,
+ IrpContext->Specific.Write.PartialMdl );
+
+ Status = PrepareAndSendPacket( IrpContext );
+
+ Stats.WriteNcps+=2;
+
+ DebugTrace(-1, Dbg, "WriteNcbCallBack -> %08lx\n", Status );
+
+ if ( !NT_SUCCESS(Status) ) {
+
+ //
+ // Abandon this request
+ //
+
+ IrpContext->Specific.Write.Status = Status;
+ NwSetIrpContextEvent( IrpContext );
+ DebugTrace( 0, Dbg, "WriteNcpCallback -> %08lx\n", Status );
+ return Status;
+ }
+
+
+ } else {
+
+ //
+ // We're done with this request, signal the writing thread.
+ //
+
+ IrpContext->Specific.Write.Status = STATUS_SUCCESS;
+ NwSetIrpContextEvent( IrpContext );
+ }
+
+ DebugTrace( 0, Dbg, "WriteNcpCallback -> %08lx\n", Status );
+ return STATUS_SUCCESS;
+
+}
+
+
+NTSTATUS
+BurstWrite(
+ PIRP_CONTEXT IrpContext,
+ LARGE_INTEGER ByteOffset,
+ ULONG BufferLength,
+ PVOID WriteBuffer,
+ PMDL WriteMdl
+ )
+/*++
+
+Routine Description:
+
+ This routine exchanges a series of burst write NCPs with the server.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+Return Value:
+
+ Status of the transfer.
+
+--*/
+{
+ PICB Icb;
+ PIRP irp;
+ PIO_STACK_LOCATION irpSp;
+ ULONG Length; // Size we will send to the server
+
+ PSCB pScb;
+ PNONPAGED_SCB pNpScb;
+ NTSTATUS status = STATUS_UNSUCCESSFUL;
+ PMDL DataMdl;
+ BOOLEAN Done;
+ BOOLEAN MissingData;
+
+ ULONG TimeInNwUnits;
+
+ ULONG LastLength;
+ ULONG Result;
+ UCHAR BurstFlags;
+ USHORT MissingFragmentCount;
+ USHORT i;
+ ULONG FragmentOffset;
+ USHORT FragmentLength;
+
+ Icb = IrpContext->Icb;
+ pNpScb = IrpContext->pNpScb;
+ irp = IrpContext->pOriginalIrp;
+ irpSp = IoGetCurrentIrpStackLocation( irp );
+
+ IrpContext->Specific.Write.WriteOffset = 0;
+ IrpContext->Specific.Write.RemainingLength = BufferLength;
+
+ IrpContext->Specific.Write.TotalWriteLength = BufferLength;
+ IrpContext->Specific.Write.TotalWriteOffset = ByteOffset.LowPart;
+
+ DebugTrace(+1, Dbg, "BurstWrite...\n", 0);
+ DebugTrace( 0, Dbg, "irp = %08lx\n", (ULONG)irp);
+ DebugTrace( 0, Dbg, "WriteLen= %ld\n", BufferLength);
+ DebugTrace( 0, Dbg, "HOffset = %lx\n", ByteOffset.HighPart);
+ DebugTrace( 0, Dbg, "LOffset = %lx\n", ByteOffset.LowPart);
+
+ //
+ // Renegotiate burst mode, if necessary
+ //
+
+ if ( pNpScb->BurstRenegotiateReqd ) {
+ pNpScb->BurstRenegotiateReqd = FALSE;
+
+ RenegotiateBurstMode( IrpContext, pNpScb );
+ }
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_BURST_WRITE );
+
+ if (Icb->SuperType.Fcb->NodeTypeCode == NW_NTC_FCB) {
+
+ pScb = Icb->SuperType.Fcb->Scb;
+ DebugTrace( 0, Dbg, "File = %wZ\n", &Icb->SuperType.Fcb->FullFileName);
+
+ } else {
+
+ //
+ // Write to a queue
+ //
+
+ pScb = Icb->SuperType.Scb;
+
+ }
+
+ ASSERT (pScb->NodeTypeCode == NW_NTC_SCB);
+
+ //
+ // Calculate the length of the burst to send.
+ //
+
+ Length = MIN( (ULONG)pNpScb->MaxSendSize, BufferLength );
+ DebugTrace( 0, Dbg, "Length = %ld\n", Length);
+
+ if ( ByteOffset.HighPart == 0xFFFFFFFF &&
+ ByteOffset.LowPart == FILE_WRITE_TO_END_OF_FILE ) {
+
+ ULONG FileLength;
+
+ //
+ // Write relative to end of file. Find the end of file.
+ //
+
+ status = ExchangeWithWait(
+ IrpContext,
+ SynchronousResponseCallback,
+ "F-r",
+ NCP_GET_FILE_SIZE,
+ &Icb->Handle, sizeof(Icb->Handle) );
+
+ if ( NT_SUCCESS( status ) ) {
+ status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "Nd",
+ &FileLength );
+ }
+
+ if ( !NT_SUCCESS( status ) ) {
+ return( status );
+ }
+
+ IrpContext->Specific.Write.FileOffset = FileLength;
+
+ } else {
+
+ IrpContext->Specific.Write.FileOffset = ByteOffset.LowPart;
+
+ }
+
+ //
+ // Setup context parameters for burst write.
+ //
+
+ IrpContext->Specific.Write.LastWriteLength = Length;
+ IrpContext->Destination = pNpScb->RemoteAddress;
+
+ IrpContext->Specific.Write.Buffer = WriteBuffer;
+
+ //
+ // Set the timeout to be the time for all te burst packets to be sent plus a round
+ // trip delay plus a second.
+ //
+
+ TimeInNwUnits = pNpScb->NwSingleBurstPacketTime * ((Length / IrpContext->pNpScb->MaxPacketSize) + 1) +
+ IrpContext->pNpScb->NwLoopTime;
+
+ IrpContext->pNpScb->SendTimeout =
+ (SHORT)(((TimeInNwUnits / 555) *
+ (ULONG)WriteTimeoutMultiplier) / 100 + 1) ;
+
+ if (IrpContext->pNpScb->SendTimeout < 2)
+ {
+ IrpContext->pNpScb->SendTimeout = 2 ;
+ }
+
+ if (IrpContext->pNpScb->SendTimeout > (SHORT)MaxWriteTimeout)
+ {
+ IrpContext->pNpScb->SendTimeout = (SHORT)MaxWriteTimeout ;
+ }
+
+ IrpContext->pNpScb->TimeOut = IrpContext->pNpScb->SendTimeout;
+
+ pNpScb->RetryCount = 20;
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "pNpScb->SendTimeout = %08lx\n", IrpContext->pNpScb->SendTimeout );
+
+ Done = FALSE;
+
+ do {
+
+ DataMdl = ALLOCATE_MDL(
+ (PCHAR)IrpContext->Specific.Write.Buffer +
+ IrpContext->Specific.Write.WriteOffset,
+ Length,
+ FALSE, // Secondary Buffer
+ FALSE, // Charge Quota
+ NULL);
+
+ if ( DataMdl == NULL ) {
+ return ( STATUS_INSUFFICIENT_RESOURCES );
+ }
+
+ //
+ // If there is no MDL for this write, probe the data MDL to lock it's
+ // pages down.
+ //
+ // Otherwise, use the data MDL as a partial MDL and lock the pages
+ // accordingly.
+ //
+
+ if ( WriteMdl == NULL ) {
+
+ //
+ // The Probe may cause us to page in some data. If the data is from
+ // the same server we are writing to then we had better not be at
+ // the front of the queue otherwise it will wait indefinitely behind us.
+ // Its a good idea to Dequeue ourselves after each burst anyway because
+ // its a quick operation and it alow smaller requests to overtake a very
+ // large series of bursts.
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+
+ try {
+ MmProbeAndLockPages( DataMdl, irp->RequestorMode, IoReadAccess);
+ } except (EXCEPTION_EXECUTE_HANDLER) {
+ FREE_MDL( DataMdl );
+ return GetExceptionCode();
+ }
+
+ } else {
+
+ IoBuildPartialMdl(
+ WriteMdl,
+ DataMdl,
+ (PCHAR)IrpContext->Specific.Write.Buffer +
+ IrpContext->Specific.Write.WriteOffset,
+ Length );
+ }
+
+ pNpScb->BurstDataWritten += Length;
+
+ if (( SendExtraNcp ) &&
+ ( pNpScb->BurstDataWritten >= 0x0000ffff )) {
+
+
+ ULONG Flags;
+
+ //
+ // VLM client sends an NCP when starting a burst mode request
+ // if the last request was not a write. It also does this every
+ // 0xfe00 bytes written
+ //
+ // When going to a queue we will use handle 2. This is what the vlm
+ // client always seems to do.
+ //
+
+ Flags = IrpContext->Flags;
+
+ //
+ // Reset IrpContext parameters
+ //
+
+ IrpContext->TxMdl->Next = NULL;
+ IrpContext->CompletionSendRoutine = NULL;
+ IrpContext->TimeoutRoutine = NULL;
+ IrpContext->Flags &= ~(IRP_FLAG_RETRY_SEND | IRP_FLAG_BURST_REQUEST | IRP_FLAG_BURST_PACKET |
+ IRP_FLAG_BURST_WRITE | IRP_FLAG_NOT_SYSTEM_PACKET );
+ IrpContext->pTdiStruct = NULL;
+
+ ExchangeWithWait (
+ IrpContext,
+ SynchronousResponseCallback,
+ "Sb", // NCP Get Directory Path
+ NCP_DIR_FUNCTION, NCP_GET_DIRECTORY_PATH,
+ (Icb->SuperType.Fcb->NodeTypeCode == NW_NTC_FCB)?
+ Icb->SuperType.Fcb->Vcb->Specific.Disk.Handle : 2 );
+
+ pNpScb->BurstDataWritten = Length;
+
+ IrpContext->Flags = Flags;
+ SetFlag( IrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE );
+ }
+
+ IrpContext->TimeoutRoutine = BurstWriteTimeout;
+ IrpContext->CompletionSendRoutine = BurstWriteCompletionSend;
+ IrpContext->pTdiStruct = &IrpContext->pNpScb->Burst;
+ IrpContext->PacketType = NCP_BURST;
+ IrpContext->pEx = BurstWriteCallback;
+
+ IrpContext->Specific.Write.FullMdl = DataMdl;
+
+ MmGetSystemAddressForMdl( DataMdl );
+
+ //
+ // Allocate a partial Mdl for the worst possible case of alignment
+ //
+
+ IrpContext->Specific.Write.PartialMdl =
+ ALLOCATE_MDL( 0, IrpContext->pNpScb->MaxPacketSize + PAGE_SIZE - 1, FALSE, FALSE, NULL);
+
+ if ( IrpContext->Specific.Write.PartialMdl == NULL ) {
+
+ if ( WriteMdl == NULL ) {
+ MmUnlockPages( DataMdl );
+ }
+
+ FREE_MDL( DataMdl );
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ //
+ // Get to the front of the SCB queue, if we are not already there.
+ // Note that can't append this IrpContext to the SCB until after
+ // the probe and lock, since the probe and lock may cause a paging
+ // read on this SCB.
+ //
+
+ NwAppendToQueueAndWait( IrpContext );
+
+ status = SendWriteBurst(
+ IrpContext,
+ BURST_WRITE_HEADER_SIZE,
+ (USHORT)Length,
+ TRUE,
+ FALSE );
+
+ MissingData = TRUE;
+ while ( MissingData ) {
+
+ KeWaitForSingleObject( &IrpContext->Event, Executive, KernelMode, FALSE, NULL );
+ MmPrepareMdlForReuse( IrpContext->Specific.Write.PartialMdl );
+
+ if ( BooleanFlagOn( IrpContext->Flags, IRP_FLAG_RETRY_SEND ) ) {
+
+ //
+ // This burst has timed out, simply resend the burst.
+ //
+
+ NwProcessSendBurstFailure( pNpScb, 1 );
+
+ status = SendWriteBurst(
+ IrpContext,
+ BURST_WRITE_HEADER_SIZE,
+ (USHORT)Length,
+ TRUE,
+ TRUE );
+ continue;
+ }
+
+ if ( !NT_SUCCESS( IrpContext->Specific.Write.Status ) ) {
+
+ status = IrpContext->Specific.Write.Status;
+ Done = TRUE;
+
+ goto EndOfLoop;
+
+ } else {
+
+ status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "B_d",
+ &BurstFlags,
+ 8,
+ &Result );
+
+ }
+
+ if ( BurstFlags & BURST_FLAG_SYSTEM_PACKET ) {
+
+ //
+ // The server dropped at least one packet.
+ //
+
+ MissingData = TRUE;
+ DebugTrace( 0, Dbg, "Received system packet\n", 0 );
+
+ //
+ // This is a missing fragment request.
+ //
+
+ status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "G_w",
+ 34,
+ &MissingFragmentCount );
+
+ ASSERT( NT_SUCCESS( status ) );
+ ASSERT( MissingFragmentCount != 0 );
+
+ NwProcessSendBurstFailure( pNpScb, MissingFragmentCount );
+
+ DebugTrace( 0, Dbg, "Received request for %d missing fragment\n", MissingFragmentCount );
+ ClearFlag( IrpContext->Flags, IRP_FLAG_RETRY_SEND );
+
+ //
+ // Walk the missing fragment list and send the missing fragments.
+ //
+
+ for ( i = 0; i < MissingFragmentCount && NT_SUCCESS( status ); i++ ) {
+
+ status = ParseResponse(
+ IrpContext,
+ IrpContext->rsp,
+ IrpContext->ResponseLength,
+ "G_dw",
+ 34 + 2 + 6 * i,
+ &FragmentOffset,
+ &FragmentLength
+ );
+
+ ASSERT( NT_SUCCESS( status ) );
+
+ if ( FragmentOffset < Length + BURST_WRITE_HEADER_SIZE &&
+ FragmentOffset + FragmentLength <=
+ Length + BURST_WRITE_HEADER_SIZE ) {
+
+ //
+ // Send a burst with the missing data. Do no set the
+ // end of burst bit until we have sent the last
+ // missing fragment packet.
+ //
+
+ status = SendWriteBurst(
+ IrpContext,
+ FragmentOffset,
+ FragmentLength,
+ (BOOLEAN)( i == (MissingFragmentCount - 1)),
+ FALSE );
+ } else {
+
+ //
+ // Received a bogus missing fragment request.
+ // Ignore the remainder of the request.
+ //
+
+ status = STATUS_INVALID_NETWORK_RESPONSE;
+ Done = TRUE;
+
+ goto EndOfLoop;
+
+ }
+ }
+
+ Stats.PacketBurstWriteTimeouts++;
+
+ } else {
+
+ NwProcessSendBurstSuccess( pNpScb );
+
+ MissingData = FALSE;
+
+ //
+ // This is not a system packets, check the response.
+ //
+
+ if ( Result == 0 ) {
+
+ //
+ // If the last write worked then move the pointers appropriately
+ //
+
+ LastLength = IrpContext->Specific.Write.LastWriteLength;
+
+ IrpContext->Specific.Write.RemainingLength -= LastLength;
+ IrpContext->Specific.Write.WriteOffset += LastLength;
+ IrpContext->Specific.Write.FileOffset += LastLength;
+
+ //
+ // If this is a print job, remember that we actually wrote data
+ //
+
+ if ( IrpContext->Icb->IsPrintJob ) {
+ IrpContext->Icb->ActuallyPrinted = TRUE;
+ }
+
+ } else {
+
+ //
+ // Abandon this request
+ //
+
+ Done = TRUE;
+ }
+
+
+ //
+ // Do we need to send another burst to satisfy the write IRP?
+ //
+
+ if ( IrpContext->Specific.Write.RemainingLength != 0 ) {
+
+ //
+ // Write the next packet.
+ //
+
+ DebugTrace( 0, Dbg, "RemainingLength = %ld\n", IrpContext->Specific.Write.RemainingLength);
+ DebugTrace( 0, Dbg, "FileOffset = %ld\n", IrpContext->Specific.Write.FileOffset);
+ DebugTrace( 0, Dbg, "WriteOffset = %ld\n", IrpContext->Specific.Write.WriteOffset);
+
+ Length = MIN( (ULONG)IrpContext->pNpScb->MaxSendSize,
+ IrpContext->Specific.Write.RemainingLength );
+
+ IrpContext->Specific.Write.LastWriteLength = Length;
+
+ } else {
+ Done = TRUE;
+ }
+
+ } // else ( not a system packet )
+
+ } // while ( missing data )
+
+ //
+ // Update the burst request number now.
+ //
+
+ if ( status != STATUS_REMOTE_NOT_LISTENING ) {
+ IrpContext->pNpScb->BurstRequestNo++;
+ }
+
+ //
+ // If we need to reconnect, do it now.
+ //
+
+ if ( BooleanFlagOn( IrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT ) ) {
+ BurstWriteReconnect( IrpContext );
+ }
+
+ //
+ // Dequeue this Irp context in preparation for the next run
+ // through the loop.
+ //
+
+EndOfLoop:
+ ASSERT( status != STATUS_PENDING );
+
+ FREE_MDL( IrpContext->Specific.Write.PartialMdl );
+
+ //
+ // Unlock locked pages, and free our MDL.
+ //
+
+ if ( WriteMdl == NULL ) {
+ MmUnlockPages( DataMdl );
+ }
+
+ FREE_MDL( DataMdl );
+
+ } while ( !Done );
+
+ DebugTrace(-1, Dbg, "BurstWrite -> %08lx\n", status );
+ return status;
+}
+
+#ifdef NWDBG
+int DropWritePackets;
+#endif
+
+
+NTSTATUS
+BurstWriteCompletionSend(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp,
+ IN PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ This routine handles completion of a burst write send. If the sending
+ thread is waiting for send completion notification, it signals the
+ IrpContext Event.
+
+ Note that this routine can be called from SendWriteBurst (i.e. not
+ at DPC level), if an allocation fails.
+
+Arguments:
+
+ DeviceObject - unused.
+
+ Irp - Supplies Irp that the transport has finished processing.
+
+ Context - Supplies the IrpContext associated with the Irp.
+
+Return Value:
+
+ The STATUS_MORE_PROCESSING_REQUIRED so that the IO system stops
+ processing Irp stack locations at this point.
+
+--*/
+{
+ PIRP_CONTEXT pIrpContext = (PIRP_CONTEXT) Context;
+ INTERLOCKED_RESULT Result;
+ KIRQL OldIrql;
+ NTSTATUS Status;
+
+ //
+ // Avoid completing the Irp because the Mdl etc. do not contain
+ // their original values.
+ //
+
+ DebugTrace( +1, Dbg, "BurstWriteCompletionSend\n", 0);
+ DebugTrace( +0, Dbg, "Irp %X\n", Irp);
+ DebugTrace( +0, Dbg, "pIrpC %X\n", pIrpContext);
+
+ if ( Irp != NULL ) {
+
+ DebugTrace( 0, Dbg, "Burst Write Send = %08lx\n", Irp->IoStatus.Status );
+
+ Status = Irp->IoStatus.Status;
+
+ } else {
+
+ Status = STATUS_SUCCESS;
+
+ }
+
+ //
+ // If this was a secondary IRP, free it now.
+ //
+
+ if ( pIrpContext->NodeTypeCode == NW_NTC_MINI_IRP_CONTEXT ) {
+ PMINI_IRP_CONTEXT MiniIrpContext;
+
+ MiniIrpContext = (PMINI_IRP_CONTEXT)pIrpContext;
+
+ ASSERT( MiniIrpContext->Mdl2->Next == NULL );
+
+ pIrpContext = MiniIrpContext->IrpContext;
+ FreeMiniIrpContext( MiniIrpContext );
+
+ }
+
+ //
+ // Nothing to do unless the last send has completed.
+ //
+
+ Result = ExInterlockedDecrementLong(
+ &pIrpContext->Specific.Write.PacketCount,
+ &pIrpContext->pNpScb->NpScbInterLock );
+
+ if ( Result != RESULT_ZERO ) {
+ DebugTrace( 0, Dbg, "Packets to go = %d\n", pIrpContext->Specific.Write.PacketCount );
+
+ if (Status == STATUS_BAD_NETWORK_PATH) {
+
+ //
+ // IPX has ripped for the destination but failed to find the net. Minimise the
+ // difference between this case and sending a normal burst by completing the
+ // transmission as soon as possible.
+ //
+
+ pIrpContext->pNpScb->NwSendDelay = 0;
+
+ }
+
+ return STATUS_MORE_PROCESSING_REQUIRED;
+ }
+
+ KeAcquireSpinLock( &pIrpContext->pNpScb->NpScbSpinLock, &OldIrql );
+
+ ASSERT( pIrpContext->pNpScb->Sending == TRUE );
+ pIrpContext->pNpScb->Sending = FALSE;
+
+ //
+ // Signal to the writing thread that the send has completed, if it
+ // is waiting.
+ //
+
+ if ( BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_SIGNAL_EVENT ) ) {
+ ClearFlag( pIrpContext->Flags, IRP_FLAG_SIGNAL_EVENT );
+ NwSetIrpContextEvent( pIrpContext );
+ }
+
+ //
+ // If we processed a receive while waiting for send
+ // completion call the receive handler routine now.
+ //
+
+ if ( pIrpContext->pNpScb->Received ) {
+
+ pIrpContext->pNpScb->Receiving = FALSE;
+ pIrpContext->pNpScb->Received = FALSE;
+
+ KeReleaseSpinLock( &pIrpContext->pNpScb->NpScbSpinLock, OldIrql );
+
+ pIrpContext->pEx(
+ pIrpContext,
+ pIrpContext->ResponseLength,
+ pIrpContext->rsp );
+
+ } else {
+ if ((Status == STATUS_BAD_NETWORK_PATH) &&
+ (pIrpContext->pNpScb->Receiving == FALSE)) {
+
+ //
+ // Usually means a ras connection has gone down during the burst.
+ // Go through the timeout logic now because the ras timeouts take
+ // a long time and unless we re rip things won't get better.
+ //
+
+ pIrpContext->Specific.Write.Status = STATUS_REMOTE_NOT_LISTENING;
+ ClearFlag( pIrpContext->Flags, IRP_FLAG_RETRY_SEND );
+
+ NwSetIrpContextEvent( pIrpContext );
+
+ }
+
+ KeReleaseSpinLock( &pIrpContext->pNpScb->NpScbSpinLock, OldIrql );
+ }
+
+ DebugTrace( -1, Dbg, "BurstWriteCompletionSend -> STATUS_MORE_PROCESSING_REQUIRED\n", 0);
+ return STATUS_MORE_PROCESSING_REQUIRED;
+
+ UNREFERENCED_PARAMETER( DeviceObject );
+}
+
+
+NTSTATUS
+BurstWriteCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ )
+/*++
+
+Routine Description:
+
+ This routine receives the response a burst write.
+
+Arguments:
+
+ IrpContext - A pointer to the context information for this IRP.
+
+ BytesAvailable - Actual number of bytes in the received message.
+
+ Response - Points to the receive buffer.
+
+Return Value:
+
+ VOID
+
+--*/
+{
+ NTSTATUS Status = STATUS_SUCCESS;
+
+ DebugTrace(0, Dbg, "BurstWriteCallback...\n", 0);
+
+ if ( BytesAvailable == 0) {
+
+ //
+ // No response from server. Status is in pIrpContext->Write.Status
+ // Clear the retry send bit so we don't keep retrying.
+ //
+
+ IrpContext->Specific.Write.Status = STATUS_REMOTE_NOT_LISTENING;
+ ClearFlag( IrpContext->Flags, IRP_FLAG_RETRY_SEND );
+
+ NwSetIrpContextEvent( IrpContext );
+
+ DebugTrace(-1, Dbg, "BurstWriteCallback -> %X\n", STATUS_REMOTE_NOT_LISTENING );
+ return STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ IrpContext->Specific.Write.Status = STATUS_SUCCESS;
+ ASSERT( BytesAvailable < MAX_RECV_DATA );
+ ++Stats.PacketBurstWriteNcps;
+
+ //
+ // Clear the retry send bit, since we have a response.
+ //
+
+ ClearFlag( IrpContext->Flags, IRP_FLAG_RETRY_SEND );
+
+ //
+ // Copy the burst write response, and signal the users thread
+ // to continue.
+ //
+
+ TdiCopyLookaheadData(
+ IrpContext->rsp,
+ Response,
+ BytesAvailable < MAX_RECV_DATA ? BytesAvailable : MAX_RECV_DATA,
+ 0
+ );
+
+ IrpContext->ResponseLength = BytesAvailable;
+
+ NwSetIrpContextEvent( IrpContext );
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+SendWriteBurst(
+ PIRP_CONTEXT IrpContext,
+ ULONG BurstOffset,
+ USHORT Length,
+ BOOLEAN EndOfBurst,
+ BOOLEAN Retransmission
+ )
+/*++
+
+Routine Description:
+
+ This routine does the actual work of sending a series of burst write
+ NCPs to the server.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+ BurstOffset - The offset in the burst to start sending. If BurstOffset
+ equals BURST_WRITE_HEADER_SIZE, start from the beginning of the burst.
+
+ Length - The length of the burst.
+
+ EndOfBurst - If TRUE set the end of burst bit when sending the last
+ frame. Otherwise there is more (discontiguous) data to come in
+ the current burst.
+
+ Retransmission - If TRUE, this is a burst write timeout retransmission.
+ Send the first packet only.
+
+Return Value:
+
+ Status of transfer.
+
+--*/
+{
+ UCHAR BurstFlags;
+ NTSTATUS Status;
+ BOOLEAN MoreData;
+ PIRP SendIrp;
+ PMINI_IRP_CONTEXT MiniIrpContext;
+
+ PAGED_CODE();
+
+ DebugTrace( +1, Dbg, "SendWriteBurst...\n", 0);
+
+ DebugTrace( 0, Dbg, "Data offset = %d\n", BurstOffset );
+ DebugTrace( 0, Dbg, "Data length = %d\n", Length );
+ DebugTrace( 0, Dbg, "End of burst = %d\n", EndOfBurst );
+
+ //
+ // Send the request.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_BURST_REQUEST | IRP_FLAG_BURST_PACKET );
+
+ //
+ // Set the burst flags
+ //
+
+ IrpContext->Specific.Write.BurstLength =
+ MIN( IrpContext->pNpScb->MaxPacketSize, Length );
+
+ //
+ // Set the end-of-burst bit (and enable receiving the response), if this
+ // is the last packet we expect to send.
+ //
+
+ if ( ( !EndOfBurst || IrpContext->Specific.Write.BurstLength < Length )
+ && !Retransmission ) {
+
+ IrpContext->pNpScb->OkToReceive = FALSE;
+ SetFlag( IrpContext->Flags, IRP_FLAG_NOT_OK_TO_RECEIVE );
+ BurstFlags = 0;
+
+ } else {
+
+ DebugTrace( 0, Dbg, "Last packet in the burst\n", 0);
+ ClearFlag( IrpContext->Flags, IRP_FLAG_NOT_OK_TO_RECEIVE );
+ BurstFlags = BURST_FLAG_END_OF_BURST;
+
+ }
+
+ if ( !EndOfBurst ) {
+ SetFlag( IrpContext->Flags, IRP_FLAG_SIGNAL_EVENT );
+ }
+
+ //
+ // Build the partial MDL for the first packet in the burst.
+ //
+
+ IoBuildPartialMdl(
+ IrpContext->Specific.Write.FullMdl,
+ IrpContext->Specific.Write.PartialMdl,
+ (PUCHAR)MmGetMdlVirtualAddress( IrpContext->Specific.Write.FullMdl ) +
+ BurstOffset - BURST_WRITE_HEADER_SIZE,
+ IrpContext->Specific.Write.BurstLength );
+
+ //
+ // Set the burst flags
+ //
+
+ if ( BurstOffset == BURST_WRITE_HEADER_SIZE ) {
+ SetFlag( IrpContext->Flags, IRP_FLAG_BURST_REQUEST | IRP_FLAG_BURST_PACKET );
+ }
+
+ if ( ( IrpContext->Specific.Write.BurstLength < Length ) &&
+ !Retransmission ) {
+ MoreData = TRUE;
+ } else {
+ MoreData = FALSE;
+ }
+
+ if ( BurstOffset == BURST_WRITE_HEADER_SIZE ) {
+
+ BuildBurstWriteFirstReq(
+ IrpContext,
+ IrpContext->req,
+ Length,
+ IrpContext->Specific.Write.PartialMdl,
+ BurstFlags,
+ *(ULONG UNALIGNED *)(&IrpContext->Icb->Handle[2]),
+ IrpContext->Specific.Write.FileOffset );
+
+ } else {
+
+ BuildBurstWriteNextReq(
+ IrpContext,
+ IrpContext->req,
+ IrpContext->Specific.Write.LastWriteLength + BURST_WRITE_HEADER_SIZE,
+ BurstFlags,
+ BurstOffset,
+ IrpContext->TxMdl,
+ IrpContext->Specific.Write.PartialMdl
+ );
+
+ }
+
+ if ( !Retransmission ) {
+ IrpContext->Specific.Write.PacketCount =
+ ( Length + IrpContext->pNpScb->MaxPacketSize - 1 ) /
+ IrpContext->pNpScb->MaxPacketSize;
+
+ } else {
+ IrpContext->Specific.Write.PacketCount = 1;
+ }
+
+ DebugTrace( 0, Dbg, "Packet count = %d\n", IrpContext->Specific.Write.PacketCount );
+
+ DebugTrace( 0, DEBUG_TRACE_LIP, "Send delay = %d\n", IrpContext->pNpScb->NwSendDelay );
+
+ //
+ // Use the original IRP context to format the first packet.
+ //
+
+ ++Stats.PacketBurstWriteNcps;
+ PreparePacket( IrpContext, IrpContext->pOriginalIrp, IrpContext->TxMdl );
+
+ Status = SendPacket( IrpContext, IrpContext->pNpScb );
+
+ while ( MoreData ) {
+
+ if ( IrpContext->pNpScb->NwSendDelay > 0 ) {
+
+ //
+ // Introduce a send delay between packets.
+ //
+
+ KeDelayExecutionThread(
+ KernelMode,
+ FALSE,
+ &IrpContext->pNpScb->NtSendDelay );
+ }
+
+ MiniIrpContext = AllocateMiniIrpContext( IrpContext );
+
+ DebugTrace( 0, Dbg, "Allocated mini IrpContext = %X\n", MiniIrpContext );
+
+ //
+ // Calculate the total number of bytes to send during this burst. Do this before
+ // checking to see if MiniIrpContext is NULL so that we skip the packet rather
+ // than sitting in a tight loop.
+ //
+
+ BurstOffset += IrpContext->Specific.Write.BurstLength;
+
+ //
+ // Do we need to send another burst write packet?
+ //
+
+ Length -= (USHORT)IrpContext->Specific.Write.BurstLength;
+
+ ASSERT ( Length > 0 );
+
+ IrpContext->Specific.Write.BurstLength =
+ MIN( IrpContext->pNpScb->MaxPacketSize, (ULONG)Length );
+
+ DebugTrace( +0, Dbg, "More data, sending %d bytes\n", IrpContext->Specific.Write.BurstLength );
+
+ //
+ // If we can't allocate a mini irp context to send the packet,
+ // just skip it and wait for the server to ask a retranmit. At
+ // this point performance isn't exactly stellar, so don't worry
+ // about having to wait for a timeout.
+ //
+
+ if ( MiniIrpContext == NULL ) {
+
+ ExInterlockedDecrementLong(
+ &IrpContext->Specific.Write.PacketCount,
+ &IrpContext->pNpScb->NpScbInterLock );
+
+ continue;
+ }
+
+#ifdef NWDBG
+
+ //
+ // If DropWritePackets is enabled, simulate missing packets, by
+ // occasionally dropping 500 bytes of data.
+ //
+
+ if ( DropWritePackets != 0 ) {
+ if ( ( rand() % DropWritePackets ) == 0 &&
+ Length != IrpContext->Specific.Write.BurstLength ) {
+
+ FreeMiniIrpContext( MiniIrpContext );
+
+ ExInterlockedDecrementLong(
+ &IrpContext->Specific.Write.PacketCount,
+ &IrpContext->pNpScb->NpScbInterLock );
+
+ continue;
+ }
+ }
+#endif
+
+ //
+ // Build the MDL for the data to send.
+ //
+
+ IoBuildPartialMdl(
+ IrpContext->Specific.Write.FullMdl,
+ MiniIrpContext->Mdl2,
+ (PUCHAR)MmGetMdlVirtualAddress( IrpContext->Specific.Write.FullMdl ) +
+ BurstOffset - BURST_WRITE_HEADER_SIZE,
+ IrpContext->Specific.Write.BurstLength );
+
+ //
+ // Set the burst flags
+ //
+
+ if ( !EndOfBurst || IrpContext->Specific.Write.BurstLength < Length ) {
+
+ IrpContext->pNpScb->OkToReceive = FALSE;
+ SetFlag( IrpContext->Flags, IRP_FLAG_NOT_OK_TO_RECEIVE );
+ BurstFlags = 0;
+ } else {
+ DebugTrace( 0, Dbg, "Last packet in the burst\n", 0);
+ ClearFlag( IrpContext->Flags, IRP_FLAG_NOT_OK_TO_RECEIVE );
+ BurstFlags = BURST_FLAG_END_OF_BURST;
+ }
+
+ if ( IrpContext->Specific.Write.BurstLength == Length ) {
+ MoreData = FALSE;
+ }
+
+ BuildBurstWriteNextReq(
+ IrpContext,
+ MiniIrpContext->Mdl1->MappedSystemVa,
+ IrpContext->Specific.Write.LastWriteLength +
+ BURST_WRITE_HEADER_SIZE,
+ BurstFlags,
+ BurstOffset,
+ MiniIrpContext->Mdl1,
+ MiniIrpContext->Mdl2
+ );
+
+ ++Stats.PacketBurstWriteNcps;
+
+ SendIrp = MiniIrpContext->Irp;
+
+ PreparePacket( IrpContext, SendIrp, MiniIrpContext->Mdl1 );
+
+ // BUGBUG Clean this up
+ IoSetCompletionRoutine( SendIrp, BurstWriteCompletionSend, MiniIrpContext, TRUE, TRUE, TRUE);
+
+ ASSERT( MiniIrpContext->Mdl2->Next == NULL );
+
+ Status = SendSecondaryPacket( IrpContext, SendIrp );
+ }
+
+ //
+ // If this is not the end-of-burst, wait for send completion here,
+ // since the caller is about to send more data.
+ //
+
+ if ( !EndOfBurst ) {
+ KeWaitForSingleObject( &IrpContext->Event, Executive, KernelMode, FALSE, NULL );
+ }
+
+ DebugTrace( -1, Dbg, "SendWriteBurst -> %X\n", Status );
+ return( Status );
+}
+
+
+VOID
+BurstWriteTimeout(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine handles a burst write timeout.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+Return Value:
+
+ None
+
+--*/
+{
+ NTSTATUS Status = STATUS_UNSUCCESSFUL;
+ PIRP Irp;
+
+ DebugTrace(0, Dbg, "BurstWriteTimeout\n", 0 );
+
+ Irp = IrpContext->pOriginalIrp;
+
+ //
+ // Set the RetrySend flag, so that we know to retransmit the request.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_RETRY_SEND );
+
+ //
+ // Signal the write thread to wakeup and resend the burst.
+ //
+
+ NwSetIrpContextEvent( IrpContext );
+
+ Stats.PacketBurstWriteTimeouts++;
+
+ return;
+}
+
+
+NTSTATUS
+ResubmitBurstWrite(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine resubmits a burst write over a new burst connection.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+Return Value:
+
+ None
+
+--*/
+{
+
+ PNONPAGED_SCB pNpScb = IrpContext->pNpScb;
+
+ PAGED_CODE();
+
+ //
+ // Remember that we need to establish a new burst connection.
+ //
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT );
+
+ //
+ // Set the packet size down the largest packet we can use, that
+ // is guaranteed to be routable.
+ //
+
+ pNpScb->MaxPacketSize = DEFAULT_PACKET_SIZE;
+
+ //
+ // Crank the delay times down so we give the new connection a chance.
+ //
+
+ pNpScb->NwGoodSendDelay = pNpScb->NwBadSendDelay = pNpScb->NwSendDelay = MinSendDelay;
+ pNpScb->NwGoodReceiveDelay = pNpScb->NwBadReceiveDelay = pNpScb->NwReceiveDelay = MinReceiveDelay;
+
+ pNpScb->SendBurstSuccessCount = 0;
+ pNpScb->ReceiveBurstSuccessCount = 0;
+
+ pNpScb->NtSendDelay.QuadPart = MinSendDelay;
+
+ //
+ // Signal the write thread to wakeup and resend the burst.
+ //
+
+ NwSetIrpContextEvent( IrpContext );
+
+ return( STATUS_PENDING );
+}
+
+
+NTSTATUS
+BurstWriteReconnect(
+ PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine allocates a new IRP context and renegotiates burst mode.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for this request.
+
+Return Value:
+
+ None
+
+--*/
+{
+ PIRP_CONTEXT pNewIrpContext;
+ PNONPAGED_SCB pNpScb = IrpContext->pNpScb;
+ BOOLEAN LIPNegotiated ;
+
+ PAGED_CODE();
+
+ //
+ // Attempt to allocate an extra IRP context.
+ //
+
+ if ( !NwAllocateExtraIrpContext( &pNewIrpContext, pNpScb ) ) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ pNewIrpContext->Specific.Create.UserUid = IrpContext->Specific.Create.UserUid;
+
+ SetFlag( pNewIrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT );
+ pNewIrpContext->pNpScb = pNpScb;
+
+ //
+ // Insert this new IrpContext to the head of
+ // the SCB queue for processing. We can get away with this
+ // because we own the IRP context currently at the front of
+ // the queue.
+ //
+
+ ExInterlockedInsertHeadList(
+ &pNpScb->Requests,
+ &pNewIrpContext->NextRequest,
+ &pNpScb->NpScbSpinLock );
+
+ SetFlag( pNewIrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE );
+
+ //
+ // Renegotiate the burst connection, this will automatically re-sync
+ // the burst connection.
+ //
+
+ NegotiateBurstMode( pNewIrpContext, pNpScb, &LIPNegotiated );
+
+ //
+ // Reset the sequence numbers.
+ //
+
+ pNpScb->BurstSequenceNo = 0;
+ pNpScb->BurstRequestNo = 0;
+
+ //
+ // Dequeue and free the bonus IRP context.
+ //
+
+ ExInterlockedRemoveHeadList(
+ &pNpScb->Requests,
+ &pNpScb->NpScbSpinLock );
+
+ ClearFlag( pNewIrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE );
+
+ NwFreeExtraIrpContext( pNewIrpContext );
+
+ ClearFlag( IrpContext->Flags, IRP_FLAG_RECONNECT_ATTEMPT );
+
+ return( STATUS_SUCCESS );
+}
+
+
+NTSTATUS
+NwFsdFlushBuffers(
+ IN PDEVICE_OBJECT DeviceObject,
+ IN PIRP Irp
+ )
+/*++
+
+Routine Description:
+
+ This routine is the FSD routine that handles NtFlushBuffersFile.
+
+Arguments:
+
+ DeviceObject - Supplies the device object for the write function.
+
+ Irp - Supplies the IRP to process.
+
+Return Value:
+
+ NTSTATUS - The result status.
+
+--*/
+
+{
+ PIRP_CONTEXT pIrpContext = NULL;
+ NTSTATUS status;
+ BOOLEAN TopLevel;
+
+ PAGED_CODE();
+
+ DebugTrace(+1, Dbg, "NwFsdFlushBuffers\n", 0);
+
+ //
+ // Call the common write routine.
+ //
+
+ FsRtlEnterFileSystem();
+ TopLevel = NwIsIrpTopLevel( Irp );
+
+ try {
+
+ pIrpContext = AllocateIrpContext( Irp );
+ status = NwCommonFlushBuffers( pIrpContext );
+
+ } except(NwExceptionFilter( Irp, GetExceptionInformation() )) {
+
+ if ( pIrpContext == NULL ) {
+
+ //
+ // If we couldn't allocate an irp context, just complete
+ // irp without any fanfare.
+ //
+
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ Irp->IoStatus.Status = status;
+ Irp->IoStatus.Information = 0;
+ IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
+
+ } else {
+
+ //
+ // We had some trouble trying to perform the requested
+ // operation, so we'll abort the I/O request with
+ // the error Status that we get back from the
+ // execption code
+ //
+
+ status = NwProcessException( pIrpContext, GetExceptionCode() );
+ }
+
+ }
+
+ if ( pIrpContext ) {
+ NwCompleteRequest( pIrpContext, status );
+ }
+
+ if ( TopLevel ) {
+ NwSetTopLevelIrp( NULL );
+ }
+ FsRtlExitFileSystem();
+
+ //
+ // Return to the caller.
+ //
+
+ DebugTrace(-1, Dbg, "NwFsdFlushBuffers -> %08lx\n", status );
+
+ return status;
+}
+
+
+NTSTATUS
+NwCommonFlushBuffers (
+ IN PIRP_CONTEXT IrpContext
+ )
+/*++
+
+Routine Description:
+
+ This routine requests all dirty cache buffers to be flushed for a
+ given file.
+
+Arguments:
+
+ IrpContext - Supplies the request being processed.
+
+Return Value:
+
+ The status of the operation.
+
+--*/
+
+{
+ PIRP Irp;
+ PIO_STACK_LOCATION IrpSp;
+
+ NTSTATUS Status;
+ PFCB Fcb;
+ PICB Icb;
+ NODE_TYPE_CODE NodeTypeCode;
+ PVOID FsContext;
+
+ PAGED_CODE();
+
+ DebugTrace(0, Dbg, "NwCommonFlushBuffers...\n", 0);
+
+ //
+ // Get the current stack location
+ //
+
+ Irp = IrpContext->pOriginalIrp;
+ IrpSp = IoGetCurrentIrpStackLocation( Irp );
+
+ DebugTrace( 0, Dbg, "Irp = %08lx\n", (ULONG)Irp);
+
+ //
+ // Decode the file object to figure out who we are. If the result
+ // is not the a file then its an illegal parameter.
+ //
+
+ if (( NodeTypeCode = NwDecodeFileObject( IrpSp->FileObject,
+ &FsContext,
+ (PVOID *)&Icb )) != NW_NTC_ICB) {
+
+ DebugTrace(0, Dbg, "Not a file\n", 0);
+
+ Status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "NwCommonFlushBuffers -> %08lx\n", Status );
+ return Status;
+ }
+
+ //
+ // Make sure that this ICB is still active.
+ //
+
+ NwVerifyIcbSpecial( Icb );
+
+ Fcb = (PFCB)Icb->SuperType.Fcb;
+ NodeTypeCode = Fcb->NodeTypeCode;
+
+ if ( NodeTypeCode != NW_NTC_FCB ) {
+
+ DebugTrace(0, Dbg, "Not a file\n", 0);
+ Status = STATUS_INVALID_PARAMETER;
+
+ DebugTrace(-1, Dbg, "CommonFlushBuffers -> %08lx\n", Status );
+ return Status;
+ }
+
+ //
+ // Set up the IRP context to do an exchange
+ //
+
+ IrpContext->pScb = Fcb->Scb;
+ IrpContext->pNpScb = IrpContext->pScb->pNpScb;
+ IrpContext->Icb = Icb;
+
+ //
+ // Send any user data to the server. Note we must not be on the
+ // queue when we do this.
+ //
+
+ MmFlushImageSection(&Icb->NpFcb->SegmentObject, MmFlushForWrite);
+
+ //
+ // Flush our dirty data.
+ //
+
+ Status = AcquireFcbAndFlushCache( IrpContext, Fcb->NonPagedFcb );
+ if ( !NT_SUCCESS( Status )) {
+ return( Status );
+ }
+
+ //
+ // Send a flush NCP
+ //
+
+ Status = Exchange (
+ IrpContext,
+ FlushBuffersCallback,
+ "F-r",
+ NCP_FLUSH_FILE,
+ &Icb->Handle, sizeof( Icb->Handle ) );
+
+ return( Status );
+}
+
+
+NTSTATUS
+FlushBuffersCallback (
+ IN PIRP_CONTEXT IrpContext,
+ IN ULONG BytesAvailable,
+ IN PUCHAR Response
+ )
+/*++
+
+Routine Description:
+
+ This routine receives the flush file size response and completes the
+ flush IRP.
+
+Arguments:
+
+
+
+Return Value:
+
+ VOID
+
+--*/
+
+{
+ NTSTATUS Status;
+
+ DebugTrace(0, Dbg, "FlushBuffersCallback...\n", 0);
+
+ if ( BytesAvailable == 0) {
+
+ //
+ // We're done with this request. Dequeue the IRP context from
+ // SCB and complete the request.
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwCompleteRequest( IrpContext, STATUS_REMOTE_NOT_LISTENING );
+
+ //
+ // No response from server. Status is in pIrpContext->
+ // ResponseParameters.Error
+ //
+
+ DebugTrace( 0, Dbg, "Timeout\n", 0);
+ return STATUS_REMOTE_NOT_LISTENING;
+ }
+
+ //
+ // Get the data from the response.
+ //
+
+ Status = ParseResponse(
+ IrpContext,
+ Response,
+ BytesAvailable,
+ "N" );
+
+ //
+ // We're done with this request. Dequeue the IRP context from
+ // SCB and complete the request.
+ //
+
+ NwDequeueIrpContext( IrpContext, FALSE );
+ NwCompleteRequest( IrpContext, Status );
+
+ return Status;
+}
+
+
+VOID
+BuildBurstWriteFirstReq(
+ PIRP_CONTEXT IrpContext,
+ PVOID Buffer,
+ ULONG DataSize,
+ PMDL BurstMdl,
+ UCHAR Flags,
+ ULONG Handle,
+ ULONG FileOffset
+ )
+{
+ PNCP_BURST_WRITE_REQUEST BurstWrite;
+ PNONPAGED_SCB pNpScb;
+ ULONG RealDataLength;
+ USHORT RealBurstLength;
+
+ PAGED_CODE();
+
+ BurstWrite = (PNCP_BURST_WRITE_REQUEST)Buffer;
+ pNpScb = IrpContext->pNpScb;
+
+ RealDataLength = DataSize + sizeof( *BurstWrite ) - sizeof( NCP_BURST_HEADER );
+ RealBurstLength = (USHORT)MdlLength( BurstMdl ) + sizeof( *BurstWrite ) - sizeof( NCP_BURST_HEADER );
+
+ BurstWrite->BurstHeader.Command = PEP_COMMAND_BURST;
+ BurstWrite->BurstHeader.Flags = Flags;
+ BurstWrite->BurstHeader.StreamType = 0x02;
+ BurstWrite->BurstHeader.SourceConnection = pNpScb->SourceConnectionId;
+ BurstWrite->BurstHeader.DestinationConnection = pNpScb->DestinationConnectionId;
+
+
+ if ( !BooleanFlagOn( IrpContext->Flags, IRP_FLAG_RETRY_SEND ) ) {
+
+ //
+ // Use the same delay on all retransmissions of the burst. Save
+ // the current time.
+ //
+
+ pNpScb->CurrentBurstDelay = pNpScb->NwSendDelay;
+
+ //
+ // Send system packet next retransmission.
+ //
+
+ ClearFlag( IrpContext->Flags, IRP_FLAG_NOT_SYSTEM_PACKET );
+
+ } else {
+
+ //
+ // This is a retransmission. Alternate between sending a system
+ // packet and the first write.
+ //
+
+ if ( !BooleanFlagOn( IrpContext->Flags, IRP_FLAG_NOT_SYSTEM_PACKET ) ) {
+
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_NOT_SYSTEM_PACKET );
+
+ BurstWrite->BurstHeader.Flags = BURST_FLAG_SYSTEM_PACKET;
+
+ LongByteSwap( BurstWrite->BurstHeader.SendDelayTime, pNpScb->CurrentBurstDelay );
+
+ BurstWrite->BurstHeader.DataSize = 0;
+ BurstWrite->BurstHeader.BurstOffset = 0;
+ BurstWrite->BurstHeader.BurstLength = 0;
+ BurstWrite->BurstHeader.MissingFragmentCount = 0;
+
+ IrpContext->TxMdl->ByteCount = sizeof( NCP_BURST_HEADER );
+ IrpContext->TxMdl->Next = NULL;
+
+ return;
+
+ }
+
+ //
+ // Send system packet next retransmission.
+ //
+
+ ClearFlag( IrpContext->Flags, IRP_FLAG_NOT_SYSTEM_PACKET );
+
+ }
+
+ LongByteSwap( BurstWrite->BurstHeader.SendDelayTime, pNpScb->CurrentBurstDelay );
+
+ LongByteSwap( BurstWrite->BurstHeader.DataSize, RealDataLength );
+ BurstWrite->BurstHeader.BurstOffset = 0;
+ ShortByteSwap( BurstWrite->BurstHeader.BurstLength, RealBurstLength );
+ BurstWrite->BurstHeader.MissingFragmentCount = 0;
+
+ BurstWrite->Function = BURST_REQUEST_WRITE;
+ BurstWrite->Handle = Handle;
+ LongByteSwap( BurstWrite->TotalWriteOffset, IrpContext->Specific.Write.TotalWriteOffset );
+ LongByteSwap( BurstWrite->TotalWriteLength, IrpContext->Specific.Write.TotalWriteLength );
+ LongByteSwap( BurstWrite->Offset, FileOffset );
+ LongByteSwap( BurstWrite->Length, DataSize );
+
+ IrpContext->TxMdl->ByteCount = sizeof( *BurstWrite );
+ IrpContext->TxMdl->Next = BurstMdl;
+
+ return;
+}
+
+VOID
+BuildBurstWriteNextReq(
+ PIRP_CONTEXT IrpContext,
+ PVOID Buffer,
+ ULONG DataSize,
+ UCHAR BurstFlags,
+ ULONG BurstOffset,
+ PMDL BurstHeaderMdl,
+ PMDL BurstDataMdl
+ )
+{
+ PNCP_BURST_HEADER BurstHeader;
+ PNONPAGED_SCB pNpScb;
+ USHORT BurstLength;
+
+ PAGED_CODE();
+
+ BurstHeader = (PNCP_BURST_HEADER)Buffer;
+ pNpScb = IrpContext->pNpScb;
+
+ BurstLength = (USHORT)MdlLength( BurstDataMdl );
+
+ BurstHeader->Command = PEP_COMMAND_BURST;
+ BurstHeader->Flags = BurstFlags;
+ BurstHeader->StreamType = 0x02;
+ BurstHeader->SourceConnection = pNpScb->SourceConnectionId;
+ BurstHeader->DestinationConnection = pNpScb->DestinationConnectionId;
+
+ LongByteSwap( BurstHeader->SendDelayTime, pNpScb->CurrentBurstDelay );
+
+ if ( BooleanFlagOn( IrpContext->Flags, IRP_FLAG_RETRY_SEND ) ) {
+
+ //
+ // This is a retransmission. Alternate between sending a system
+ // packet and the first write.
+ //
+
+ if ( !BooleanFlagOn( IrpContext->Flags, IRP_FLAG_NOT_SYSTEM_PACKET ) ) {
+
+
+ SetFlag( IrpContext->Flags, IRP_FLAG_NOT_SYSTEM_PACKET );
+
+ BurstHeader->Flags = BURST_FLAG_SYSTEM_PACKET;
+
+ LongByteSwap( BurstHeader->SendDelayTime, pNpScb->CurrentBurstDelay );
+
+ BurstHeader->DataSize = 0;
+ BurstHeader->BurstOffset = 0;
+ BurstHeader->BurstLength = 0;
+ BurstHeader->MissingFragmentCount = 0;
+
+ IrpContext->TxMdl->ByteCount = sizeof( NCP_BURST_HEADER );
+ IrpContext->TxMdl->Next = NULL;
+
+ return;
+
+ }
+
+ //
+ // Send system packet next retransmission.
+ //
+
+ ClearFlag( IrpContext->Flags, IRP_FLAG_NOT_SYSTEM_PACKET );
+
+ } else {
+
+ //
+ // Send system packet next retransmission.
+ //
+
+ ClearFlag( IrpContext->Flags, IRP_FLAG_NOT_SYSTEM_PACKET );
+
+ }
+
+ LongByteSwap( BurstHeader->DataSize, DataSize );
+ LongByteSwap( BurstHeader->BurstOffset, BurstOffset );
+ ShortByteSwap( BurstHeader->BurstLength, BurstLength );
+ BurstHeader->MissingFragmentCount = 0;
+
+ BurstHeaderMdl->ByteCount = sizeof( *BurstHeader );
+ BurstHeaderMdl->Next = BurstDataMdl;
+
+ return;
+}
+
+
+NTSTATUS
+SendSecondaryPacket(
+ PIRP_CONTEXT IrpContext,
+ PIRP Irp
+ )
+/*++
+
+Routine Description:
+
+ This routine submits a TDI send request to the tranport layer.
+
+Arguments:
+
+ IrpContext - A pointer to IRP context information for the request
+ being processed.
+
+ Irp - The IRP for the packet to send.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PNONPAGED_SCB pNpScb;
+ NTSTATUS Status;
+ PNCP_BURST_HEADER BurstHeader;
+ pNpScb = IrpContext->pNpScb;
+
+ DebugTrace( 0, Dbg, "SendSecondaryPacket\n", 0 );
+
+ BurstHeader = (PNCP_BURST_HEADER)( MmGetMdlVirtualAddress( Irp->MdlAddress ) );
+
+ if ( !BooleanFlagOn( IrpContext->Flags, IRP_FLAG_NOT_OK_TO_RECEIVE ) ) {
+ pNpScb->OkToReceive = TRUE;
+ }
+
+ LongByteSwap( BurstHeader->PacketSequenceNo, pNpScb->BurstSequenceNo );
+ pNpScb->BurstSequenceNo++;
+
+ ShortByteSwap( BurstHeader->BurstSequenceNo, pNpScb->BurstRequestNo );
+ ShortByteSwap( BurstHeader->AckSequenceNo, pNpScb->BurstRequestNo );
+
+ DebugTrace( +0, Dbg, "Irp %X\n", Irp );
+ DebugTrace( +0, Dbg, "pIrpC %X\n", IrpContext);
+
+#if NWDBG
+ dumpMdl( Dbg, IrpContext->TxMdl);
+#endif
+
+ Stats.BytesTransmitted.QuadPart += MdlLength( Irp->MdlAddress );
+ Stats.NcpsTransmitted.QuadPart += 1;
+
+ Status = IoCallDriver( pNpScb->Server.pDeviceObject, Irp );
+ DebugTrace( -1, Dbg, " %X\n", Status );
+
+ if ( !NT_SUCCESS( Status ) ) {
+ Error( EVENT_NWRDR_NETWORK_ERROR, Status, NULL, 0, 0 );
+ }
+
+ return Status;
+}
+
+#if NWFASTIO
+
+BOOLEAN
+NwFastWrite (
+ IN PFILE_OBJECT FileObject,
+ IN PLARGE_INTEGER FileOffset,
+ IN ULONG Length,
+ IN BOOLEAN Wait,
+ IN ULONG LockKey,
+ OUT PVOID Buffer,
+ OUT PIO_STATUS_BLOCK IoStatus,
+ IN PDEVICE_OBJECT DeviceObject
+ )
+/*++
+
+Routine Description:
+
+ This routine does a fast cached read bypassing the usual file system
+ entry routine (i.e., without the Irp). It is used to do a copy read
+ of a cached file object. For a complete description of the arguments
+ see CcCopyRead.
+
+Arguments:
+
+ FileObject - Pointer to the file object being read.
+
+ FileOffset - Byte offset in file for desired data.
+
+ Length - Length of desired data in bytes.
+
+ Wait - FALSE if caller may not block, TRUE otherwise
+
+ Buffer - Pointer to output buffer to which data should be copied.
+
+ IoStatus - Pointer to standard I/O status block to receive the status
+ for the transfer.
+
+Return Value:
+
+ FALSE - if Wait was supplied as FALSE and the data was not delivered, or
+ if there is an I/O error.
+
+ TRUE - if the data is being delivered
+
+--*/
+
+{
+ NODE_TYPE_CODE nodeTypeCode;
+ PICB icb;
+ PFCB fcb;
+ PVOID fsContext;
+ ULONG offset;
+ BOOLEAN wroteToCache;
+
+ DebugTrace(+1, Dbg, "NwFastWrite...\n", 0);
+
+ //
+ // Special case a read of zero length
+ //
+
+ if (Length == 0) {
+
+ //
+ // A zero length transfer was requested.
+ //
+
+ IoStatus->Status = STATUS_SUCCESS;
+ IoStatus->Information = 0;
+
+ DebugTrace(+1, Dbg, "NwFastWrite -> TRUE\n", 0);
+ return TRUE;
+ }
+
+ //
+ // Decode the file object to figure out who we are. If the result
+ // is not FCB then its an illegal parameter.
+ //
+
+ if ((nodeTypeCode = NwDecodeFileObject( FileObject,
+ &fsContext,
+ (PVOID *)&icb )) != NW_NTC_ICB) {
+
+ DebugTrace(0, Dbg, "Not a file\n", 0);
+ DebugTrace(-1, Dbg, "NwFastWrite -> FALSE\n", 0);
+ return FALSE;
+ }
+
+ fcb = (PFCB)icb->SuperType.Fcb;
+ nodeTypeCode = fcb->NodeTypeCode;
+ offset = FileOffset->LowPart;
+
+ IoStatus->Status = STATUS_SUCCESS;
+ IoStatus->Information = Length;
+
+ wroteToCache = CacheWrite(
+ NULL,
+ fcb->NonPagedFcb,
+ offset,
+ Length,
+ Buffer );
+
+ DebugTrace(-1, Dbg, "NwFastWrite -> %s\n", wroteToCache ? "TRUE" : "FALSE" );
+
+ if ( wroteToCache ) {
+
+ //
+ // If the file was extended, record the new file size.
+ //
+
+ if ( ( offset + Length ) > fcb->NonPagedFcb->Header.FileSize.LowPart ) {
+ fcb->NonPagedFcb->Header.FileSize.LowPart = ( offset + Length );
+ }
+ }
+
+#ifndef NT1057
+
+ //
+ // Update the file object if we succeeded. We know that this
+ // is synchronous and not paging io because it's coming in through
+ // the cache.
+ //
+
+ if ( wroteToCache ) {
+ FileObject->CurrentByteOffset.QuadPart += Length;
+ }
+
+#endif
+
+ return( wroteToCache );
+
+}
+#endif
diff --git a/private/nw/rpc/client.c b/private/nw/rpc/client.c
new file mode 100644
index 000000000..56dcfbfe1
--- /dev/null
+++ b/private/nw/rpc/client.c
@@ -0,0 +1,184 @@
+/*++
+
+Copyright (c) 1990-93 Microsoft Corporation
+
+Module Name:
+
+ client.c
+
+Abstract:
+
+ This file contains commonly used client-side RPC control functions.
+
+Author:
+
+ Dan Lafferty danl 06-Feb-1991
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 06-Feb-1991 danl
+ Created
+ 26-Apr-1991 JohnRo
+ Split out MIDL user (allocate,free) so linker doesn't get confused.
+ Deleted tabs.
+ 03-July-1991 JimK
+ Copied from LM specific file.
+ 27-Feb-1992 JohnRo
+ Fixed heap trashing bug in RpcpBindRpc().
+
+ 10-Feb-1993 RitaW
+ Copied to the NetWare tree so that the LPC transport can used for
+ the local case.
+
+--*/
+
+#include <nt.h> // needed for NTSTATUS
+#include <ntrtl.h> // needed for nturtl.h
+#include <nturtl.h> // needed for windows.h
+#include <windows.h> // win32 typedefs
+#include <rpc.h> // rpc prototypes
+
+#include <stdlib.h> // for wcscpy wcscat
+#include <tstr.h> // WCSSIZE
+
+#include <nwrpcp.h>
+
+#define NT_PIPE_PREFIX TEXT("\\PIPE\\")
+#define NT_PIPE_PREFIX_W L"\\PIPE\\"
+
+
+
+RPC_STATUS
+RpcpBindRpc(
+#if 0
+ IN LPWSTR ServerName,
+#endif
+ IN LPWSTR ServiceName,
+ IN LPWSTR NetworkOptions,
+ OUT RPC_BINDING_HANDLE *pBindingHandle
+ )
+
+/*++
+
+Routine Description:
+
+ Binds to the RPC server if possible.
+
+Arguments:
+
+ ServerName - Name of server to bind with.
+
+ ServiceName - Name of service to bind with.
+
+ pBindingHandle - Location where binding handle is to be placed
+
+Return Value:
+
+ RPC_S_OK - The binding has been successfully completed.
+
+ RPC error, if failed.
+
+--*/
+
+{
+ RPC_STATUS RpcStatus;
+ LPWSTR StringBinding;
+
+#if 0
+ LPWSTR Endpoint;
+ WCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1];
+ LPWSTR NewServerName = NULL;
+ DWORD bufLen = MAX_COMPUTERNAME_LENGTH + 1;
+
+
+ if (ServerName != NULL) {
+ if (GetComputerNameW(ComputerName,&bufLen)) {
+ if (_wcsicmp(ComputerName,&(ServerName[2]))==0) {
+ NewServerName = NULL;
+ }
+ else {
+ NewServerName = ServerName;
+ }
+ }
+ }
+
+ if ( NewServerName == NULL ) {
+#endif
+
+ RpcStatus = RpcStringBindingComposeW(0, L"ncalrpc", 0, ServiceName,
+ NetworkOptions, &StringBinding);
+
+#if 0
+ }
+ else {
+ // We need to concatenate \pipe\ to the front of the service
+ // name.
+
+ Endpoint = (LPWSTR)LocalAlloc(
+ 0,
+ sizeof(NT_PIPE_PREFIX_W) + WCSSIZE(ServiceName));
+ if (Endpoint == 0) {
+ return(STATUS_NO_MEMORY);
+ }
+ wcscpy(Endpoint,NT_PIPE_PREFIX_W);
+ wcscat(Endpoint,ServiceName);
+
+ RpcStatus = RpcStringBindingComposeW(0, L"ncacn_np", NewServerName,
+ Endpoint, NetworkOptions, &StringBinding);
+ LocalFree(Endpoint);
+ }
+#endif
+
+ if ( RpcStatus != RPC_S_OK ) {
+ KdPrint(("NWRPCUTIL: RpcStringBindingComposeW failed %ld\n", RpcStatus));
+ return RpcStatus;
+ }
+
+ RpcStatus = RpcBindingFromStringBindingW(StringBinding, pBindingHandle);
+
+ RpcStringFreeW(&StringBinding);
+
+ if ( RpcStatus != RPC_S_OK ) {
+ KdPrint(("NWRPCUTIL: RpcBindingFromStringBindingW failed %ld\n", RpcStatus));
+ return RpcStatus;
+ }
+
+ return RPC_S_OK;
+}
+
+
+VOID
+RpcpUnbindRpc(
+ IN RPC_BINDING_HANDLE BindingHandle
+ )
+
+/*++
+
+Routine Description:
+
+ Unbinds from the RPC interface.
+ If we decide to cache bindings, this routine will do something more
+ interesting.
+
+Arguments:
+
+ BindingHandle - This points to the binding handle that is to be closed.
+
+
+Return Value:
+
+ None.
+
+--*/
+{
+ RPC_STATUS RpcStatus;
+
+
+ RpcStatus = RpcBindingFree(&BindingHandle);
+
+ ASSERT(RpcStatus == RPC_S_OK);
+}
diff --git a/private/nw/rpc/makefile b/private/nw/rpc/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/nw/rpc/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/rpc/midluser.c b/private/nw/rpc/midluser.c
new file mode 100644
index 000000000..8d6e53206
--- /dev/null
+++ b/private/nw/rpc/midluser.c
@@ -0,0 +1,211 @@
+/*++
+
+Copyright (c) 1990-1993 Microsoft Corporation
+
+Module Name:
+
+ MidlUser.c
+
+Abstract:
+
+ This file contains common functions and utilities that the API
+ DLLs can use in making remote calls. This includes the
+ MIDL_USER_ALLOCATE functions.
+
+Author:
+
+ Dan Lafferty danl 06-Feb-1991
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 06-Feb-1991 danl
+ Created
+ 25-Apr-1991 JohnRo
+ Split out MIDL user (allocate,free) into seperate source file, so
+ linker doesn't get confused.
+ 03-July-1991 JimK
+ Moved to a common directory so services available to more than just
+ LM code.
+ 03-Dec-1991 JohnRo
+ Added MIDL_user_reallocate and MIDL_user_size APIs. (These are so we
+ create the NetApiBufferAllocate, NetApiBufferReallocate, and
+ NetApiBufferSize APIs.)
+ Also check alignment of allocated data.
+
+ 10-Feb-1993 RitaW
+ Copied to the NetWare tree so that the LPC transport can used for
+ the local case.
+
+--*/
+
+#include <nt.h>
+#include <ntrtl.h> // needed for nturtl.h
+#include <nturtl.h> // needed for windows.h
+#include <windows.h> // win32 typedefs
+#include <rpc.h> // rpc prototypes
+
+#include <align.h> // POINTER_IS_ALIGNED(), ALIGN_WORST.
+#include <winbase.h> // LocalAlloc
+
+#include <nwrpcp.h> // MIDL_user_allocate(), MIDL_user_free().
+
+
+PVOID
+MIDL_user_allocate (
+ IN unsigned int NumBytes
+ )
+
+/*++
+
+Routine Description:
+
+ Allocates storage for RPC transactions. The RPC stubs will either call
+ MIDL_user_allocate when it needs to un-marshall data into a buffer
+ that the user must free. RPC servers will use MIDL_user_allocate to
+ allocate storage that the RPC server stub will free after marshalling
+ the data.
+
+Arguments:
+
+ NumBytes - The number of bytes to allocate.
+
+Return Value:
+
+ none
+
+Note:
+
+
+--*/
+
+{
+ LPVOID NewPointer;
+
+ NewPointer = (LPVOID) LocalAlloc(
+ LMEM_ZEROINIT,
+ NumBytes
+ );
+
+ ASSERT( POINTER_IS_ALIGNED( NewPointer, ALIGN_WORST) );
+
+ return (NewPointer);
+
+} // MIDL_user_allocate
+
+
+
+VOID
+MIDL_user_free (
+ IN void *MemPointer
+ )
+
+/*++
+
+Routine Description:
+
+ Frees storage used in RPC transactions. The RPC client can call this
+ function to free buffer space that was allocated by the RPC client
+ stub when un-marshalling data that is to be returned to the client.
+ The Client calls MIDL_user_free when it is finished with the data and
+ desires to free up the storage.
+ The RPC server stub calls MIDL_user_free when it has completed
+ marshalling server data that is to be passed back to the client.
+
+Arguments:
+
+ MemPointer - This points to the memory block that is to be released.
+
+Return Value:
+
+ none.
+
+Note:
+
+
+--*/
+{
+ ASSERT( POINTER_IS_ALIGNED( MemPointer, ALIGN_WORST) );
+ (void) LocalFree((HLOCAL) MemPointer);
+
+} // MIDL_user_free
+
+void *
+MIDL_user_reallocate(
+ IN void * OldPointer OPTIONAL,
+ IN unsigned long NewByteCount
+ )
+{
+ LPVOID NewPointer; // may be NULL.
+
+
+ ASSERT( POINTER_IS_ALIGNED( OldPointer, ALIGN_WORST) );
+
+
+ // Special cases: something into nothing, or nothing into something.
+ if (OldPointer == NULL) {
+
+ NewPointer = (LPVOID) LocalAlloc(
+ LMEM_ZEROINIT,
+ NewByteCount
+ );
+
+ } else if (NewByteCount == 0) {
+
+ (void) LocalFree((HLOCAL) OldPointer );
+ NewPointer = NULL;
+
+ } else { // must be realloc of something to something else.
+
+ HANDLE hOldMem;
+ HANDLE hNewMem; // handle for new (may = old handle)
+
+ hOldMem = LocalHandle( (LPSTR) OldPointer);
+ ASSERT(hOldMem != NULL);
+
+ hNewMem = (PVOID) LocalReAlloc(
+ hOldMem, // old handle
+ NewByteCount, // new size in bytes
+ LMEM_ZEROINIT | // flags
+ LMEM_MOVEABLE // (motion okay)
+ );
+
+ if (hNewMem == NULL) {
+ return (NULL);
+ }
+
+ NewPointer = (LPVOID) hNewMem;
+
+ } // must be realloc of something to something else
+
+ ASSERT( POINTER_IS_ALIGNED( NewPointer, ALIGN_WORST) );
+
+ return (NewPointer);
+
+} // MIDL_user_reallocate
+
+
+unsigned long
+MIDL_user_size(
+ IN void * Pointer
+ )
+{
+ DWORD ByteCount;
+ HANDLE hMemory;
+
+ ASSERT( Pointer != NULL );
+ ASSERT( POINTER_IS_ALIGNED( Pointer, ALIGN_WORST ) );
+
+ hMemory = LocalHandle( (LPSTR) Pointer );
+ ASSERT( hMemory != NULL );
+
+ ByteCount = LocalSize( hMemory );
+
+ ASSERT( ByteCount > 0 );
+
+ return (ByteCount);
+
+} // MIDL_user_size
diff --git a/private/nw/rpc/server.c b/private/nw/rpc/server.c
new file mode 100644
index 000000000..f9825d3ef
--- /dev/null
+++ b/private/nw/rpc/server.c
@@ -0,0 +1,362 @@
+/*++
+
+Copyright (c) 1990-1993 Microsoft Corporation
+
+Module Name:
+
+ server.c
+
+Abstract:
+
+ This file contains commonly used server-side RPC functions,
+ such as starting and stoping RPC servers.
+
+Author:
+
+ Dan Lafferty danl 09-May-1991
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+ 09-May-1991 Danl
+ Created
+
+ 03-July-1991 JimK
+ Copied from a net-specific file.
+
+ 18-Feb-1992 Danl
+ Added support for multiple endpoints & interfaces per server.
+
+ 10-Feb-1993 RitaW
+ Copied to the NetWare tree so that the LPC transport can used for
+ the local case.
+
+--*/
+
+//
+// INCLUDES
+//
+
+#include <nt.h> // DbgPrint
+#include <ntrtl.h> // DbgPrint
+#include <windef.h> // win32 typedefs
+#include <rpc.h> // rpc prototypes
+#include <nturtl.h> // needed for winbase.h
+#include <winbase.h> // LocalAlloc
+
+#include <stdlib.h> // for wcscpy wcscat
+#include <tstr.h> // WCSSIZE
+
+#include <nwrpcp.h>
+
+
+#define NT_PIPE_PREFIX_W L"\\PIPE\\"
+
+//
+// GLOBALS
+//
+
+static CRITICAL_SECTION RpcpCriticalSection;
+static DWORD RpcpNumInstances;
+
+
+
+VOID
+RpcpInitRpcServer(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This function initializes the critical section used to protect the
+ global server handle and instance count.
+
+Arguments:
+
+ none
+
+Return Value:
+
+ none
+
+--*/
+{
+ InitializeCriticalSection(&RpcpCriticalSection);
+ RpcpNumInstances = 0;
+}
+
+
+RPC_STATUS
+RpcpAddInterface(
+ IN LPWSTR InterfaceName,
+ IN RPC_IF_HANDLE InterfaceSpecification
+ )
+
+/*++
+
+Routine Description:
+
+ Starts an RPC Server, adds the address (or port/pipe), and adds the
+ interface (dispatch table).
+
+Arguments:
+
+ InterfaceName - points to the name of the interface.
+
+ InterfaceSpecification - Supplies the interface handle for the
+ interface which we wish to add.
+
+Return Value:
+
+ RPC_S_OK - Indicates the server was successfully started.
+
+ Other - Status values that may be returned by:
+
+ RpcServerRegisterIf()
+ RpcServerUseProtseqEp()
+
+ , or any RPC error codes, or any windows error codes that
+ can be returned by LocalAlloc.
+
+--*/
+{
+ RPC_STATUS RpcStatus;
+ LPWSTR Endpoint = NULL;
+
+ PSECURITY_DESCRIPTOR SecurityDescriptor = NULL;
+ BOOL Bool;
+
+
+#if 0
+ // We need to concatenate \pipe\ to the front of the interface name.
+
+ Endpoint = LocalAlloc(0, sizeof(NT_PIPE_PREFIX_W) + WCSSIZE(InterfaceName));
+ if (Endpoint == 0) {
+ return(STATUS_NO_MEMORY);
+ }
+ wcscpy(Endpoint, NT_PIPE_PREFIX_W );
+ wcscat(Endpoint,InterfaceName);
+#endif
+
+ //
+ // Croft up a security descriptor that will grant everyone
+ // all access to the object (basically, no security)
+ //
+ // We do this by putting in a NULL Dacl.
+ //
+ // BUGBUG: rpc should copy the security descriptor,
+ // Since it currently doesn't, simply allocate it for now and
+ // leave it around forever.
+ //
+
+ SecurityDescriptor = (PVOID) LocalAlloc(LMEM_FIXED, sizeof( SECURITY_DESCRIPTOR));
+ if (SecurityDescriptor == 0) {
+ RpcStatus = RPC_S_OUT_OF_MEMORY;
+ goto CleanExit;
+ }
+
+ InitializeSecurityDescriptor( SecurityDescriptor, SECURITY_DESCRIPTOR_REVISION );
+
+ Bool = SetSecurityDescriptorDacl (
+ SecurityDescriptor,
+ TRUE, // Dacl present
+ NULL, // NULL Dacl
+ FALSE // Not defaulted
+ );
+
+ //
+ // There's no way the above call can fail. But check anyway.
+ //
+ ASSERT( Bool );
+
+#if 0
+ // Ignore the second argument for now.
+
+ RpcStatus = RpcServerUseProtseqEpW(L"ncacn_np", 10, Endpoint, SecurityDescriptor);
+
+ // If RpcpStartRpcServer and then RpcpStopRpcServer have already
+ // been called, the endpoint will have already been added but not
+ // removed (because there is no way to do it). If the endpoint is
+ // already there, it is ok.
+
+ if ( (RpcStatus != RPC_S_OK)
+ && (RpcStatus != RPC_S_DUPLICATE_ENDPOINT)) {
+
+ KdPrint(("RpcServerUseProtseqW failed! rpcstatus = %u\n",RpcStatus));
+ goto CleanExit;
+ }
+#endif // #if 0
+
+
+ RpcStatus = RpcServerUseProtseqEpW(L"ncalrpc", 10, InterfaceName, SecurityDescriptor);
+
+ // If RpcpStartRpcServer and then RpcpStopRpcServer have already
+ // been called, the endpoint will have already been added but not
+ // removed (because there is no way to do it). If the endpoint is
+ // already there, it is ok.
+
+ if ((RpcStatus != RPC_S_OK)
+ && (RpcStatus != RPC_S_DUPLICATE_ENDPOINT)) {
+
+ KdPrint(("RpcServerUseProtseqW failed! rpcstatus = %u\n",RpcStatus));
+ goto CleanExit;
+ }
+
+ RpcStatus = RpcServerRegisterIf(InterfaceSpecification, 0, 0);
+
+CleanExit:
+ if ( Endpoint != NULL ) {
+ LocalFree(Endpoint);
+ }
+
+ return RpcStatus;
+}
+
+
+RPC_STATUS
+RpcpStartRpcServer(
+ IN LPWSTR InterfaceName,
+ IN RPC_IF_HANDLE InterfaceSpecification
+ )
+
+/*++
+
+Routine Description:
+
+ Starts an RPC Server, adds the address (or port/pipe), and adds the
+ interface (dispatch table).
+
+Arguments:
+
+ InterfaceName - points to the name of the interface.
+
+ InterfaceSpecification - Supplies the interface handle for the
+ interface which we wish to add.
+
+Return Value:
+
+ RPC_S_OK - Indicates the server was successfully started.
+
+ Other - Status values that may be returned by:
+
+ RpcServerRegisterIf()
+ RpcServerUseProtseqEp()
+
+ , or any RPC error codes, or any windows error codes that
+ can be returned by LocalAlloc.
+
+--*/
+{
+ RPC_STATUS RpcStatus;
+
+ EnterCriticalSection(&RpcpCriticalSection);
+
+ RpcStatus = RpcpAddInterface( InterfaceName,
+ InterfaceSpecification );
+
+ if ( RpcStatus != RPC_S_OK ) {
+ LeaveCriticalSection(&RpcpCriticalSection);
+ return RpcStatus;
+ }
+
+ RpcpNumInstances++;
+
+ if (RpcpNumInstances == 1) {
+
+
+ // The first argument specifies the minimum number of threads to
+ // be created to handle calls; the second argument specifies the
+ // maximum number of concurrent calls allowed. The last argument
+ // indicates not to wait.
+
+ RpcStatus = RpcServerListen(1,12345, 1);
+ if ( RpcStatus == RPC_S_ALREADY_LISTENING ) {
+ RpcStatus = RPC_S_OK;
+ }
+ }
+
+ LeaveCriticalSection(&RpcpCriticalSection);
+ return RpcStatus;
+}
+
+
+RPC_STATUS
+RpcpDeleteInterface(
+ IN RPC_IF_HANDLE InterfaceSpecification
+ )
+
+/*++
+
+Routine Description:
+
+ Deletes the interface. This is likely
+ to be caused by an invalid handle. If an attempt to add the same
+ interface or address again, then an error will be generated at that
+ time.
+
+Arguments:
+
+ InterfaceSpecification - A handle for the interface that is to be removed.
+
+Return Value:
+
+ RPC_S_OK, or any RPC error codes that can be returned from
+ RpcServerUnregisterIf.
+
+--*/
+{
+ RPC_STATUS RpcStatus;
+
+
+ RpcStatus = RpcServerUnregisterIf(InterfaceSpecification, 0, 1);
+
+ return RpcStatus;
+}
+
+
+RPC_STATUS
+RpcpStopRpcServer(
+ IN RPC_IF_HANDLE InterfaceSpecification
+ )
+
+/*++
+
+Routine Description:
+
+ Deletes the interface. This is likely
+ to be caused by an invalid handle. If an attempt to add the same
+ interface or address again, then an error will be generated at that
+ time.
+
+Arguments:
+
+ InterfaceSpecification - A handle for the interface that is to be removed.
+
+Return Value:
+
+ RPC_S_OK, or any RPC error codes that can be returned from
+ RpcServerUnregisterIf.
+
+--*/
+{
+ RPC_STATUS RpcStatus;
+
+
+ RpcStatus = RpcServerUnregisterIf(InterfaceSpecification, 0, 1);
+ EnterCriticalSection(&RpcpCriticalSection);
+
+ RpcpNumInstances--;
+ if (RpcpNumInstances == 0) {
+ RpcMgmtStopServerListening(0);
+ RpcMgmtWaitServerListen();
+ }
+
+ LeaveCriticalSection(&RpcpCriticalSection);
+
+ return RpcStatus;
+}
diff --git a/private/nw/rpc/sources b/private/nw/rpc/sources
new file mode 100644
index 000000000..8fcc5555f
--- /dev/null
+++ b/private/nw/rpc/sources
@@ -0,0 +1,41 @@
+!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:
+
+ Jim Kelly (JimK) 3-July-1991
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=nw
+MINORCOMP=rpc
+
+TARGETNAME=nwrpc
+TARGETPATH=obj
+TARGETTYPE=LIBRARY
+
+INCLUDES=..\inc;$(_NTROOT)\private\inc
+
+SOURCES=midluser.c \
+ client.c \
+ server.c
+
+NET_C_DEFINES=-DRPC_NO_WINDOWS_H
+UMTYPE=nt
+UMTEST=
+OPTIONAL_UMTEST=
diff --git a/private/nw/service/dirs b/private/nw/service/dirs
new file mode 100644
index 000000000..b047e73e2
--- /dev/null
+++ b/private/nw/service/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= \
+ nwsvc
diff --git a/private/nw/service/nwsvc/makefile b/private/nw/service/nwsvc/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/nw/service/nwsvc/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/service/nwsvc/nwsvc.c b/private/nw/service/nwsvc/nwsvc.c
new file mode 100644
index 000000000..5486f0983
--- /dev/null
+++ b/private/nw/service/nwsvc/nwsvc.c
@@ -0,0 +1,230 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ nwsvc.c
+
+Abstract:
+
+ This is the main module of the NetWare services process shared by
+ NetWare services.
+
+Author:
+
+ Rita Wong (ritaw) 26-Feb-1993
+
+Environment:
+
+ User Mode - Win32
+
+Revision History:
+
+--*/
+
+#ifndef UNICODE
+#define UNICODE
+#endif
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+
+#include <nwrpcp.h>
+#include <nwsvc.h>
+
+//
+// Service entry points -- thunks to real service entry points.
+//
+
+VOID
+StartWorkstation(
+ IN DWORD argc,
+ IN LPWSTR argv[]
+ );
+
+//
+// Local function used by the above to load and invoke a service DLL.
+//
+VOID
+NwsvcStartService(
+ IN LPWSTR DllName,
+ IN DWORD argc,
+ IN LPWSTR argv[]
+ );
+
+
+//
+// To pass RPC utility function pointers to DLLs so that global data
+// for this process can be updated from within the DLLs.
+//
+NWSVC_GLOBAL_DATA GlobalData;
+
+//
+// Dispatch table for all services.
+// Add new service entries here and in the DLL name list.
+//
+SERVICE_TABLE_ENTRYW NwServiceDispatchTable[] = {
+ { NW_WORKSTATION_SERVICE, StartWorkstation },
+ { NULL, NULL }
+ };
+
+//
+// DLL names for all services.
+//
+#define WORKSTATION_DLL TEXT("nwwks.dll")
+
+
+VOID _CRTAPI1
+main(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This is the main function of the NetWare services process.
+ It does process-wide initialization and relinquishes the main
+ thread to the service controller to become the control
+ dispatch thread.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ //
+ // Initialize process-wide RPC data so that all services in this
+ // process share the same RPC server.
+ //
+ RpcpInitRpcServer();
+
+ //
+ // Each of the following routines alters global data in this process.
+ // This data has to be made accessible from each service DLL.
+ //
+ GlobalData.StartRpcServer = RpcpStartRpcServer;
+ GlobalData.StopRpcServer = RpcpStopRpcServer;
+
+ //
+ // Become the dispatch thread.
+ //
+ (void) StartServiceCtrlDispatcherW(NwServiceDispatchTable);
+}
+
+
+VOID
+StartWorkstation (
+ IN DWORD argc,
+ IN LPWSTR argv[]
+ )
+
+/*++
+
+Routine Description:
+
+ This is the thunk routine for the Workstation service. It loads the
+ DLL that contains the service and calls its main routine.
+
+Arguments:
+
+ argc, argv - Passed through to the service
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ //
+ // Call NwsvcStartService to load and run the service.
+ //
+
+ NwsvcStartService( WORKSTATION_DLL, argc, argv );
+
+} // StartWorkstation
+
+
+
+VOID
+NwsvcStartService(
+ IN LPWSTR DllName,
+ IN DWORD argc,
+ IN LPWSTR argv[]
+ )
+
+/*++
+
+Routine Description:
+
+ This routine loads the DLL that contains a service and calls its
+ main routine.
+
+Arguments:
+
+ DllName - name of the DLL
+
+ argc, argv - Passed through to the service
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ HMODULE dllHandle;
+ PNWSVC_SERVICE_DLL_ENTRY serviceEntry;
+ BOOL ok;
+
+
+ //
+ // Load the DLL that contains the service.
+ //
+ dllHandle = LoadLibraryW(DllName);
+
+ if (dllHandle == NULL) {
+ KdPrint(("NVSVC: Failed to load DLL %ws: %lu\n", DllName, GetLastError()));
+ return;
+ }
+
+ //
+ // Get the address of the service's main entry point. This
+ // entry point has a well-known name.
+ //
+ serviceEntry = (PNWSVC_SERVICE_DLL_ENTRY) GetProcAddress(
+ dllHandle,
+ NWSVC_ENTRY_POINT_STRING
+ );
+ if (serviceEntry == NULL) {
+ KdPrint(("NWSVC: Can't find entry %s in DLL %ws: %lu\n",
+ NWSVC_ENTRY_POINT_STRING, DllName, GetLastError()));
+ return;
+ }
+
+ //
+ // Call the service's main entry point. This call doesn't return
+ // until the service exits.
+ //
+ serviceEntry(argc, argv, &GlobalData);
+
+
+ //
+ // Unload the DLL.
+ //
+ ok = FreeLibrary( dllHandle );
+ if (! ok) {
+ KdPrint(("NWSVC: Can't unload DLL %ws: %lu\n", DllName, GetLastError()));
+ }
+
+ return;
+
+} // NwsvcStartService
diff --git a/private/nw/service/nwsvc/nwsvc.rc b/private/nw/service/nwsvc/nwsvc.rc
new file mode 100644
index 000000000..11120e36b
--- /dev/null
+++ b/private/nw/service/nwsvc/nwsvc.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 "Netware Services"
+#define VER_INTERNALNAME_STR "nwsvc.exe"
+
+#include "common.ver"
diff --git a/private/nw/service/nwsvc/sources b/private/nw/service/nwsvc/sources
new file mode 100644
index 000000000..172f9f6d4
--- /dev/null
+++ b/private/nw/service/nwsvc/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:
+
+Revision History:
+
+!ENDIF
+
+MAJORCOMP=nw
+MINORCOMP=service
+TARGETNAME=nwsvc
+
+TARGETPATH=obj
+
+TARGETTYPE=PROGRAM
+
+TARGETLIBS= \
+ \nt\public\sdk\lib\*\rpcrt4.lib \
+ \nt\public\sdk\lib\*\ntdll.lib \
+ \nt\public\sdk\lib\*\rpcndr.lib \
+ $(BASEDIR)\Public\Sdk\Lib\*\nwapi32.lib \
+ ..\..\rpc\obj\*\nwrpc.lib
+
+INCLUDES=.;..\..\inc;$(NTOS)\inc
+
+SOURCES= \
+ nwsvc.c \
+ nwsvc.rc
+
+UMTYPE=windows
+UMTEST=
+
+UNICODE=1
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);
+}
diff --git a/private/nw/test/attach.c b/private/nw/test/attach.c
new file mode 100644
index 000000000..92fb54e7c
--- /dev/null
+++ b/private/nw/test/attach.c
@@ -0,0 +1,404 @@
+
+/*++
+
+Copyright (c) 1992 Microsoft Corporation
+
+Module Name:
+
+ Attach.c
+
+Abstract:
+
+ This module implements the routines for the NetWare
+ redirector to connect and disconnect from a server.
+
+Author:
+
+ Colin Watson [ColinW] 10-Jan-1992
+
+Revision History:
+
+--*/
+#include <stdio.h>
+#include <nt.h>
+#include <ntrtl.h>
+//#include "Procs.h"
+
+
+#define DebugTrace(INDENT,LEVEL,X,Y) { \
+ printf(X,Y); \
+}
+
+
+
+VOID
+ExtractNextComponentName (
+ OUT PUNICODE_STRING Name,
+ IN PUNICODE_STRING Path
+ )
+
+/*++
+
+Routine Description:
+
+ This routine extracts a the "next" component from a path string.
+
+ It assumes that
+
+Arguments:
+
+ Name - Returns a pointer to the component.
+
+ Path - Supplies a pointer to the backslash seperated pathname.
+
+Return Value:
+
+ None
+
+--*/
+
+{
+ register USHORT i; // Index into Name string.
+
+ if (Path->Length == 0) {
+ RtlInitUnicodeString(Name, NULL);
+ return;
+ }
+
+ //
+ // Initialize the extracted name to the name passed in skipping the
+ // leading backslash.
+ //
+
+ ASSERT(Path->Buffer[0] == OBJ_NAME_PATH_SEPARATOR);
+
+ Name->Buffer = Path->Buffer + 1;
+ Name->Length = Path->Length - sizeof(WCHAR);
+ Name->MaximumLength = Path->MaximumLength - sizeof(WCHAR);
+
+ //
+ // Scan forward finding the terminal "\" in the server name.
+ //
+
+ for (i=0;i<(USHORT)(Name->Length/sizeof(WCHAR));i++) {
+
+ if (Name->Buffer[i] == OBJ_NAME_PATH_SEPARATOR) {
+ break;
+ }
+ }
+
+ //
+ // Update the length and maximum length of the structure
+ // to match the new length.
+ //
+
+ Name->Length = Name->MaximumLength = (USHORT)(i*sizeof(WCHAR));
+}
+
+
+NTSTATUS
+ExtractPathAndFileName (
+ IN PUNICODE_STRING EntryPath,
+ OUT PUNICODE_STRING PathString,
+ OUT PUNICODE_STRING FileName
+ )
+
+/*++
+
+Routine Description:
+
+ This routine cracks the entry path into two pieces, the path and the file
+name component at the start of the name.
+
+
+Arguments:
+
+ IN PUNICODE_STRING EntryPath - Supplies the path to disect.
+ OUT PUNICODE_STRING PathString - Returns the directory containing the file.
+ OUT PUNICODE_STRING FileName - Returns the file name specified.
+
+Return Value:
+
+ NTSTATUS - SUCCESS
+
+
+--*/
+
+{
+ UNICODE_STRING Component;
+ UNICODE_STRING FilePath = *EntryPath;
+
+ // Strip trailing separators
+ while ( (FilePath.Length != 0) &&
+ FilePath.Buffer[(FilePath.Length-1)/sizeof(WCHAR)] ==
+ OBJ_NAME_PATH_SEPARATOR ) {
+
+ FilePath.Length -= sizeof(WCHAR);
+ FilePath.MaximumLength -= sizeof(WCHAR);
+ }
+
+ // PathString will become EntryPath minus FileName and trailing separators
+ *PathString = FilePath;
+
+ // Initialize FileName just incase there are no components at all.
+ RtlInitUnicodeString( FileName, NULL );
+
+ //
+ // Scan through the current file name to find the entire path
+ // up to (but not including) the last component in the path.
+ //
+
+ do {
+
+ //
+ // Extract the next component from the name.
+ //
+
+ ExtractNextComponentName(&Component, &FilePath);
+
+ //
+ // Bump the "remaining name" pointer by the length of this
+ // component
+ //
+
+ if (Component.Length != 0) {
+
+ FilePath.Length -= Component.Length+sizeof(WCHAR);
+ FilePath.MaximumLength -= Component.MaximumLength+sizeof(WCHAR);
+ FilePath.Buffer += (Component.Length/sizeof(WCHAR))+1;
+
+ *FileName = Component;
+ }
+
+
+ } while (Component.Length != 0);
+
+ //
+ // Take the name, subtract the last component of the name
+ // and concatenate the current path with the new path.
+ //
+
+ if ( FileName->Length != 0 ) {
+
+ //
+ // Set the path's name based on the original name, subtracting
+ // the length of the name portion (including the "\")
+ //
+
+ PathString->Length -= (FileName->Length + sizeof(WCHAR));
+ if ( PathString->Length != 0 ) {
+ PathString->MaximumLength -= (FileName->MaximumLength + sizeof(WCHAR));
+ } else{
+ RtlInitUnicodeString( PathString, NULL );
+ }
+ } else {
+
+ // There was no path or filename
+
+ RtlInitUnicodeString( PathString, NULL );
+ }
+
+ return STATUS_SUCCESS;
+}
+
+
+NTSTATUS
+CrackPath (
+ IN PUNICODE_STRING BaseName,
+ OUT PUNICODE_STRING DriveName,
+ OUT PUNICODE_STRING ServerName,
+ OUT PUNICODE_STRING VolumeName,
+ OUT PUNICODE_STRING PathName,
+ OUT PUNICODE_STRING FileName
+ )
+
+/*++
+
+Routine Description:
+
+ This routine extracts the relevant portions from BaseName to extract
+ the components of the user's string.
+
+
+Arguments:
+
+ BaseName - Supplies the base user's path.
+
+ DriveName - Supplies a string to hold the drive specifier.
+
+ ServerName - Supplies a string to hold the remote server name.
+
+ VolumeName - Supplies a string to hold the volume name.
+
+ PathName - Supplies a string to hold the remaining part of the path.
+
+ FileName - Supplies a string to hold the final component of the path.
+
+Return Value:
+
+ NTSTATUS - Status of operation
+
+
+--*/
+
+{
+ UNICODE_STRING BaseCopy = *BaseName;
+ UNICODE_STRING ShareName;
+
+ RtlInitUnicodeString( DriveName, NULL);
+ RtlInitUnicodeString( ServerName, NULL);
+ RtlInitUnicodeString( VolumeName, NULL);
+ RtlInitUnicodeString( PathName, NULL);
+ RtlInitUnicodeString( FileName, NULL);
+
+ //
+ // If the name is "\", or empty, there is nothing to do.
+ //
+
+ if ( BaseName->Length <= sizeof( WCHAR ) ) {
+ return STATUS_SUCCESS;
+ }
+
+ ExtractNextComponentName(ServerName, &BaseCopy);
+
+ //
+ // Skip over the server name.
+ //
+
+ BaseCopy.Buffer += (ServerName->Length / sizeof(WCHAR)) + 1;
+ BaseCopy.Length -= ServerName->Length + sizeof(WCHAR);
+ BaseCopy.MaximumLength -= ServerName->MaximumLength + sizeof(WCHAR);
+
+ if ((ServerName->Length == sizeof(L"X:") - sizeof(WCHAR) ) &&
+ (ServerName->Buffer[(ServerName->Length / sizeof(WCHAR)) - 1] == L':')) {
+
+ //
+ // The file name is of the form x:\server\volume\foo\bar
+ //
+
+ *DriveName = *ServerName;
+
+ RtlInitUnicodeString( ServerName, NULL );
+ ExtractNextComponentName(ServerName, &BaseCopy);
+
+ if ( ServerName->Length != 0 ) {
+
+ //
+ // Skip over the server name.
+ //
+
+ BaseCopy.Buffer += (ServerName->Length / sizeof(WCHAR)) + 1;
+ BaseCopy.Length -= ServerName->Length + sizeof(WCHAR);
+ BaseCopy.MaximumLength -= ServerName->MaximumLength + sizeof(WCHAR);
+ }
+ }
+
+ if ( ServerName->Length != 0 ) {
+
+ //
+ // The file name is of the form \\server\volume\foo\bar
+ // Set volume name to server\volume.
+ //
+
+ ExtractNextComponentName( &ShareName, &BaseCopy);
+
+ //
+ // Set volume name = \server\share
+ //
+
+ VolumeName->Buffer = ServerName->Buffer - 1;
+ ASSERT( VolumeName->Buffer[0] == '\\' );
+
+ if ( ShareName.Length != 0 ) {
+
+ VolumeName->Length = ServerName->Length + ShareName.Length + 2 * sizeof( WCHAR );
+
+ BaseCopy.Buffer += ShareName.Length / sizeof(WCHAR) + 1;
+ BaseCopy.Length -= ShareName.Length + sizeof(WCHAR);
+ BaseCopy.MaximumLength -= ShareName.MaximumLength + sizeof(WCHAR);
+
+ } else {
+
+ VolumeName->Length = ServerName->Length + ShareName.Length + sizeof( WCHAR );
+
+ return( STATUS_SUCCESS );
+ }
+ }
+
+ return ExtractPathAndFileName ( &BaseCopy, PathName, FileName );
+
+}
+
+
+NTSTATUS
+TestCrackPath (
+ IN PUNICODE_STRING BaseName
+ ) {
+ NTSTATUS Status;
+ UNICODE_STRING DriveName;
+ UNICODE_STRING ServerName;
+ UNICODE_STRING VolumeName;
+ UNICODE_STRING PathName;
+ UNICODE_STRING FileName;
+ RtlInitUnicodeString( &DriveName, L"Error" );
+ RtlInitUnicodeString( &ServerName, L"Error" );
+ RtlInitUnicodeString( &PathName, L"Error" );
+ RtlInitUnicodeString( &FileName, L"Error" );
+ DebugTrace( 0, Dbg, "\nName = %Z\n", BaseName );
+ Status = CrackPath( BaseName, &DriveName, &ServerName, &VolumeName, &PathName, &FileName );
+ DebugTrace( 0, Dbg, " DriveName = %Z\n", &DriveName );
+ DebugTrace( 0, Dbg, " ServerName= %Z\n", &ServerName );
+ DebugTrace( 0, Dbg, " VolumeName= %Z\n", &VolumeName );
+ DebugTrace( 0, Dbg, " PathName = %Z\n", &PathName );
+ DebugTrace( 0, Dbg, " FileName = %Z\n", &FileName );
+ return Status;
+
+}
+int
+_cdecl
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ UNICODE_STRING Name;
+ RtlInitUnicodeString( &Name, L"\\" );
+ TestCrackPath( &Name );
+ RtlInitUnicodeString( &Name, L"\\x:" );
+ TestCrackPath( &Name );
+ RtlInitUnicodeString( &Name, L"\\x:\\" );
+ TestCrackPath( &Name );
+ RtlInitUnicodeString( &Name, L"\\x:\\Server" );
+ TestCrackPath( &Name );
+ RtlInitUnicodeString( &Name, L"\\x:\\Server\\" );
+ TestCrackPath( &Name );
+ RtlInitUnicodeString( &Name, L"\\x:\\Server\\FileName" );
+ TestCrackPath( &Name );
+ RtlInitUnicodeString( &Name, L"\\x:\\Server\\FileName\\" );
+ TestCrackPath( &Name );
+ RtlInitUnicodeString( &Name, L"\\x:\\Server\\Path\\FileName" );
+ TestCrackPath( &Name );
+ RtlInitUnicodeString( &Name, L"\\x:\\Server\\Path\\FileName\\" );
+ TestCrackPath( &Name );
+ RtlInitUnicodeString( &Name, L"\\x:\\Server\\Path\\Path2\\FileName" );
+ TestCrackPath( &Name );
+ RtlInitUnicodeString( &Name, L"\\x:\\Server\\Path\\Path2\\FileName\\" );
+ TestCrackPath( &Name );
+ RtlInitUnicodeString( &Name, L"\\Server" );
+ TestCrackPath( &Name );
+ RtlInitUnicodeString( &Name, L"\\Server\\" );
+ TestCrackPath( &Name );
+ RtlInitUnicodeString( &Name, L"\\Server\\FileName" );
+ TestCrackPath( &Name );
+ RtlInitUnicodeString( &Name, L"\\Server\\FileName\\" );
+ TestCrackPath( &Name );
+ RtlInitUnicodeString( &Name, L"\\Server\\Path\\FileName" );
+ TestCrackPath( &Name );
+ RtlInitUnicodeString( &Name, L"\\Server\\Path\\FileName\\" );
+ TestCrackPath( &Name );
+ RtlInitUnicodeString( &Name, L"\\Server\\Path\\Path2\\FileName" );
+ TestCrackPath( &Name );
+ RtlInitUnicodeString( &Name, L"\\Server\\Path\\Path2\\FileName\\" );
+ TestCrackPath( &Name );
+ return 0;
+}
diff --git a/private/nw/test/encrypt.c b/private/nw/test/encrypt.c
new file mode 100644
index 000000000..6884a9277
--- /dev/null
+++ b/private/nw/test/encrypt.c
@@ -0,0 +1,243 @@
+//
+// This file pulled from logon.pas
+//
+
+#include <string.h>
+#define WIN32_CONSOLE_APP
+#include <windows.h>
+
+typedef unsigned short int WORD;
+typedef unsigned char UCHAR;
+typedef unsigned long ULONG;
+typedef unsigned long *PULONG;
+
+
+UCHAR Table[] =
+{0x7,0x8,0x0,0x8,0x6,0x4,0xE,0x4,0x5,0xC,0x1,0x7,0xB,0xF,0xA,0x8,
+ 0xF,0x8,0xC,0xC,0x9,0x4,0x1,0xE,0x4,0x6,0x2,0x4,0x0,0xA,0xB,0x9,
+ 0x2,0xF,0xB,0x1,0xD,0x2,0x1,0x9,0x5,0xE,0x7,0x0,0x0,0x2,0x6,0x6,
+ 0x0,0x7,0x3,0x8,0x2,0x9,0x3,0xF,0x7,0xF,0xC,0xF,0x6,0x4,0xA,0x0,
+ 0x2,0x3,0xA,0xB,0xD,0x8,0x3,0xA,0x1,0x7,0xC,0xF,0x1,0x8,0x9,0xD,
+ 0x9,0x1,0x9,0x4,0xE,0x4,0xC,0x5,0x5,0xC,0x8,0xB,0x2,0x3,0x9,0xE,
+ 0x7,0x7,0x6,0x9,0xE,0xF,0xC,0x8,0xD,0x1,0xA,0x6,0xE,0xD,0x0,0x7,
+ 0x7,0xA,0x0,0x1,0xF,0x5,0x4,0xB,0x7,0xB,0xE,0xC,0x9,0x5,0xD,0x1,
+ 0xB,0xD,0x1,0x3,0x5,0xD,0xE,0x6,0x3,0x0,0xB,0xB,0xF,0x3,0x6,0x4,
+ 0x9,0xD,0xA,0x3,0x1,0x4,0x9,0x4,0x8,0x3,0xB,0xE,0x5,0x0,0x5,0x2,
+ 0xC,0xB,0xD,0x5,0xD,0x5,0xD,0x2,0xD,0x9,0xA,0xC,0xA,0x0,0xB,0x3,
+ 0x5,0x3,0x6,0x9,0x5,0x1,0xE,0xE,0x0,0xE,0x8,0x2,0xD,0x2,0x2,0x0,
+ 0x4,0xF,0x8,0x5,0x9,0x6,0x8,0x6,0xB,0xA,0xB,0xF,0x0,0x7,0x2,0x8,
+ 0xC,0x7,0x3,0xA,0x1,0x4,0x2,0x5,0xF,0x7,0xA,0xC,0xE,0x5,0x9,0x3,
+ 0xE,0x7,0x1,0x2,0xE,0x1,0xF,0x4,0xA,0x6,0xC,0x6,0xF,0x4,0x3,0x0,
+ 0xC,0x0,0x3,0x6,0xF,0x8,0x7,0xB,0x2,0xD,0xC,0x6,0xA,0xA,0x8,0xD};
+
+UCHAR Keys[32] =
+{0x48,0x93,0x46,0x67,0x98,0x3D,0xE6,0x8D,
+ 0xB7,0x10,0x7A,0x26,0x5A,0xB9,0xB1,0x35,
+ 0x6B,0x0F,0xD5,0x70,0xAE,0xFB,0xAD,0x11,
+ 0xF4,0x47,0xDC,0xA7,0xEC,0xCF,0x50,0xC0};
+
+#define XorArray( DEST, SRC ) { \
+ PULONG D = (PULONG)DEST; \
+ PULONG S = (PULONG)SRC; \
+ int i; \
+ for ( i = 0; i <= 7 ; i++ ) { \
+ D[i] ^= S[i]; \
+ } \
+}
+
+int
+Scramble(
+ int iSeed,
+ UCHAR achBuffer[32]
+ );
+
+VOID
+Shuffle(
+ UCHAR *achObjectId,
+ UCHAR *szUpperPassword,
+ int iPasswordLen,
+ UCHAR *achOutputBuffer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine shuffles around the object ID with the password
+
+Arguments:
+
+ IN achObjectId - Supplies the 4 byte user's bindery object id
+
+ IN szUpperPassword - Supplies the user's uppercased password on the
+ first call to process the password. On the second and third calls
+ this parameter contains the OutputBuffer from the first call
+
+ IN iPasswordLen - length of uppercased password
+
+ OUT achOutputBuffer - Returns the 8 byte sub-calculation
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ int iTempIndex;
+ int iOutputIndex;
+ UCHAR achTemp[32];
+
+ //
+ // Initialize the achTemp buffer. Initialization consists of taking
+ // the password and dividing it up into chunks of 32. Any bytes left
+ // over are the remainder and do not go into the initialization.
+ //
+ // achTemp[0] = szUpperPassword[0] ^ szUpperPassword[32] ^ szUpper...
+ // achTemp[1] = szUpperPassword[1] ^ szUpperPassword[33] ^ szUpper...
+ // etc.
+ //
+
+ if ( iPasswordLen > 32) {
+
+ // At least one chunk of 32. Set the buffer to the first chunk.
+
+ RtlCopyMemory( achTemp, szUpperPassword, 32 );
+
+ szUpperPassword +=32; // Remove the first chunk
+ iPasswordLen -=32;
+
+ while ( iPasswordLen >= 32 ) {
+ //
+ // Xor this chunk with the characters already loaded into
+ // achTemp.
+ //
+
+ XorArray( achTemp, szUpperPassword);
+
+ szUpperPassword +=32; // Remove this chunk
+ iPasswordLen -=32;
+ }
+
+ } else {
+
+ // No chunks of 32 so set the buffer to zero's
+
+ RtlZeroMemory( achTemp, sizeof(achTemp));
+
+ }
+
+ //
+ // achTemp is now initialized. Load the remainder into achTemp.
+ // The remainder is repeated to fill achTemp.
+ //
+ // The corresponding character from Keys is taken to seperate
+ // each repitition.
+ //
+ // As an example, take the remainder "ABCDEFG". The remainder is expanded
+ // to "ABCDEFGwABCDEFGxABCDEFGyABCDEFGz" where w is Keys[7],
+ // x is Keys[15], y is Keys[23] and z is Keys[31].
+ //
+ //
+
+ if (iPasswordLen > 0) {
+ int iPasswordOffset = 0;
+ for (iTempIndex = 0; iTempIndex < 32; iTempIndex++) {
+
+ if (iPasswordLen == iPasswordOffset) {
+ iPasswordOffset = 0;
+ achTemp[iTempIndex] ^= Keys[iTempIndex];
+ } else {
+ achTemp[iTempIndex] ^= szUpperPassword[iPasswordOffset++];
+ }
+ }
+ }
+
+ //
+ // achTemp has been loaded with the users password packed into 32
+ // bytes. Now take the objectid that came from the server and use
+ // that to munge every byte in achTemp.
+ //
+
+ for (iTempIndex = 0; iTempIndex < 32; iTempIndex++)
+ achTemp[iTempIndex] ^= achObjectId[ iTempIndex & 3];
+
+ Scramble( Scramble( 0, achTemp ), achTemp );
+
+ //
+ // Finally take pairs of bytes in achTemp and return the two
+ // nibbles obtained from Table. The pairs of bytes used
+ // are achTemp[n] and achTemp[n+16].
+ //
+
+ for (iOutputIndex = 0; iOutputIndex < 16; iOutputIndex++) {
+
+ achOutputBuffer[iOutputIndex] =
+ Table[achTemp[iOutputIndex << 1]] |
+ (Table[achTemp[(iOutputIndex << 1) + 1]] << 4);
+ }
+
+ return;
+}
+
+int
+Scramble(
+ int iSeed,
+ UCHAR achBuffer[32]
+ )
+
+/*++
+
+Routine Description:
+
+ This routine scrambles around the contents of the buffer. Each buffer
+ position is updated to include the contents of at least two character
+ positions plus an EncryptKey value. The buffer is processed left to right
+ and so if a character position chooses to merge with a buffer position
+ to its left then this buffer position will include bits derived from at
+ least 3 bytes of the original buffer contents.
+
+Arguments:
+
+ IN iSeed
+ IN OUT achBuffer[32]
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ int iBufferIndex;
+
+ for (iBufferIndex = 0; iBufferIndex < 32; iBufferIndex++) {
+ achBuffer[iBufferIndex] =
+ (UCHAR)(
+ ((UCHAR)(achBuffer[iBufferIndex] + iSeed)) ^
+ ((UCHAR)( achBuffer[(iBufferIndex+iSeed) & 31] -
+ Keys[iBufferIndex] )));
+
+ iSeed += achBuffer[iBufferIndex];
+ }
+ return iSeed;
+}
+
+
+ULONG Key = 0x95000009;
+UCHAR szUpperPassword[129] = { 0x00 };
+UCHAR achBuf[32] = {0};
+
+int
+_cdecl
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ Shuffle((UCHAR *) &Key, szUpperPassword, 0, achBuf);
+
+ return(1);
+}
+// eof.
+
diff --git a/private/nw/test/fileio.c b/private/nw/test/fileio.c
new file mode 100644
index 000000000..ed535206b
--- /dev/null
+++ b/private/nw/test/fileio.c
@@ -0,0 +1,210 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ fileio.c
+
+Abstract:
+
+ User mode test program for the Microsoft Netware redir file system.
+
+ This test program can be built from the command line using the
+ command 'nmake UMTEST=fileio'.
+
+Author:
+
+ Manny Weiser (mannyw) 17-May-1993
+
+Revision History:
+
+--*/
+
+#include <stdio.h>
+#include <string.h>
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+//
+// Local definitions
+//
+
+VOID
+DisplayUsage(
+ PSZ ProgramName
+ );
+
+
+BOOLEAN
+OpenFile(
+ PSZ Name,
+ PHANDLE Handle
+ );
+
+QueryFileInfo(
+ IN HANDLE Handle
+ );
+
+
+cdecl
+main(
+ int argc,
+ char *argv[],
+ )
+{
+ HANDLE handle;
+ BOOLEAN success;
+ char *fileName;
+
+ if ( argc > 1) {
+ fileName = argv[1];
+ } else {
+ fileName = "testfile.txt";
+ }
+
+ success = OpenFile( fileName, &handle );
+ if ( !success) {
+ return 1;
+ }
+
+ success = QueryFileInfo( handle );
+ if ( !success) {
+ return 1;
+ }
+
+ printf( "%s exiting\n", argv[0]);
+ return 0;
+}
+
+
+VOID
+DisplayUsage(
+ PSZ ProgramName
+ )
+{
+ printf( "Usage: %s [filename]", ProgramName);
+}
+
+
+BOOLEAN
+OpenFile(
+ PSZ Name,
+ PHANDLE Handle
+ )
+{
+ UNICODE_STRING nameString;
+ NTSTATUS status;
+ OBJECT_ATTRIBUTES objectAttributes;
+ IO_STATUS_BLOCK ioStatusBlock;
+ RTL_RELATIVE_NAME RelativeName;
+ STRING AnsiFileName;
+ UNICODE_STRING FileName;
+ BOOLEAN TranslationStatus;
+
+ RtlInitString( &AnsiFileName, Name );
+ RtlOemStringToUnicodeString( &FileName, &AnsiFileName, TRUE );
+
+ TranslationStatus = RtlDosPathNameToNtPathName_U(
+ FileName.Buffer,
+ &FileName,
+ NULL,
+ &RelativeName
+ );
+
+ if ( !TranslationStatus ) {
+ return FALSE;
+ }
+
+ if ( RelativeName.RelativeName.Length ) {
+ FileName = *(PUNICODE_STRING)&RelativeName.RelativeName;
+ } else {
+ RelativeName.ContainingDirectory = NULL;
+ }
+
+ //
+ // Open the file
+ //
+
+ InitializeObjectAttributes(
+ &objectAttributes,
+ &FileName,
+ OBJ_CASE_INSENSITIVE,
+ RelativeName.ContainingDirectory,
+ NULL
+ );
+
+ status = NtOpenFile (
+ Handle,
+ FILE_GENERIC_READ,
+ &objectAttributes,
+ &ioStatusBlock,
+ FILE_SHARE_WRITE | FILE_SHARE_READ,
+ 0L
+ );
+
+ if (!NT_SUCCESS(status) ) {
+ printf( "Open status = %x for file %Z\n", status, &nameString );
+ }
+
+ RtlFreeHeap(RtlProcessHeap(), 0, FileName.Buffer );
+ return ( (BOOLEAN) NT_SUCCESS( status ) );
+}
+
+
+DoSleep(
+ IN ULONG time
+ )
+{
+ ULONG ms;
+ LARGE_INTEGER delayTime;
+
+ ms = time * 100;
+ delayTime = RtlEnlargedIntegerMultiply( ms, -10000 );
+ NtDelayExecution( TRUE, (PLARGE_INTEGER)&delayTime );
+
+ return( 0 );
+}
+
+QueryFileInfo(
+ IN HANDLE Handle
+ )
+{
+ NTSTATUS status;
+ IO_STATUS_BLOCK IoStatusBlock;
+ char buffer[200];
+ PFILE_ALL_INFORMATION allInfo = (PFILE_ALL_INFORMATION)buffer;
+
+ status = NtQueryInformationFile(
+ Handle,
+ &IoStatusBlock,
+ allInfo,
+ 200,
+ FileAllInformation );
+
+ if ( NT_SUCCESS( status ) ) {
+ status = IoStatusBlock.Status;
+ }
+
+ if ( !NT_SUCCESS( status ) ) {
+ printf("NtQueryInformation file returns %08lx\n", status );
+ return( status );
+ }
+
+ printf( "File attributes = %x\n", allInfo->BasicInformation.FileAttributes );
+ printf( "File size = %d\n", allInfo->StandardInformation.AllocationSize.LowPart );
+ printf( "Is a dir = %d\n", allInfo->StandardInformation.Directory );
+ printf( "Index = %x\n", allInfo->InternalInformation.IndexNumber );
+ printf( "Easiz = %d\n", allInfo->EaInformation.EaSize );
+ printf( "Access flags = %x\n", allInfo->AccessInformation.AccessFlags );
+ printf( "Current offset = %d\n", allInfo->PositionInformation.CurrentByteOffset.LowPart );
+ printf( "Mode = %x\n", allInfo->ModeInformation.Mode );
+ printf( "Alignment info = %d\n", allInfo->AlignmentInformation.AlignmentRequirement );
+ allInfo->NameInformation.FileName[ allInfo->NameInformation.FileNameLength / 2 ] = '\0';
+ printf( "Name = %ws\n", allInfo->NameInformation.FileName );
+
+ return( status );
+}
+
+
diff --git a/private/nw/test/getmsg.c b/private/nw/test/getmsg.c
new file mode 100644
index 000000000..35c3146d2
--- /dev/null
+++ b/private/nw/test/getmsg.c
@@ -0,0 +1,178 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ fileio.c
+
+Abstract:
+
+ User mode test program for the Microsoft Netware redir file system.
+
+ This test program can be built from the command line using the
+ command 'nmake UMTEST=fileio'.
+
+Author:
+
+ Manny Weiser (mannyw) 17-May-1993
+
+Revision History:
+
+--*/
+
+#include <stdio.h>
+#include <string.h>
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <ntddnwfs.h>
+
+//
+// Local definitions
+//
+
+VOID
+DisplayUsage(
+ PSZ ProgramName
+ );
+
+
+BOOLEAN
+OpenRedir(
+ PHANDLE Handle
+ );
+
+GetMessage(
+ IN HANDLE Handle
+ );
+
+#define BUFFER_SIZE 200
+
+
+_cdecl
+main(
+ int argc,
+ char *argv[],
+ )
+{
+ HANDLE handle;
+ BOOLEAN success;
+
+ success = OpenRedir( &handle );
+ if ( !success) {
+ return 1;
+ }
+
+ printf("Opened redirector\n" );
+
+ success = GetMessage( handle );
+ if ( !success) {
+ return 1;
+ }
+
+ printf( "%s exiting\n", argv[0]);
+ NtClose( handle );
+ return 0;
+
+
+}
+
+
+BOOLEAN
+OpenRedir(
+ PHANDLE Handle
+ )
+{
+ NTSTATUS status;
+ OBJECT_ATTRIBUTES objectAttributes;
+ IO_STATUS_BLOCK ioStatusBlock;
+ UNICODE_STRING FileName;
+
+ FileName.Buffer = DD_NWFS_DEVICE_NAME_U;
+ FileName.Length = sizeof( DD_NWFS_DEVICE_NAME_U ) - sizeof( WCHAR );
+ FileName.MaximumLength = sizeof( DD_NWFS_DEVICE_NAME_U );
+
+ //
+ // Open the file
+ //
+
+ InitializeObjectAttributes(
+ &objectAttributes,
+ &FileName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ status = NtOpenFile (
+ Handle,
+ FILE_GENERIC_READ | SYNCHRONIZE,
+ &objectAttributes,
+ &ioStatusBlock,
+ FILE_SHARE_WRITE | FILE_SHARE_READ,
+ 0L
+ );
+
+ if (!NT_SUCCESS(status) ) {
+ printf( "Open status = %x for file %Z\n", status, &FileName );
+ }
+
+ return ( (BOOLEAN) NT_SUCCESS( status ) );
+}
+
+
+
+GetMessage(
+ IN HANDLE Handle
+ )
+{
+ NTSTATUS status;
+ IO_STATUS_BLOCK IoStatusBlock;
+ char OutputBuffer[200];
+ PWCHAR ServerName;
+ PWCHAR Message;
+ PNWR_SERVER_MESSAGE ServerMessage;
+
+ printf("Waiting for message\n" );
+
+ status = NtFsControlFile(
+ Handle,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_GET_MESSAGE,
+ NULL,
+ 0,
+ OutputBuffer,
+ sizeof(OutputBuffer)
+ );
+
+#if 0
+ if ( NT_SUCCESS( status ) ) {
+
+ status = NtWaitForSingleObject( Handle, FALSE, NULL );
+ if ( NT_SUCCESS( status )) {
+ status = IoStatusBlock.Status;
+ }
+ }
+
+ if ( !NT_SUCCESS( status ) ) {
+ printf("NtFsControlFile returns %08lx\n", status );
+ return( status );
+ } else {
+ printf("Message received\n" );
+ }
+
+ ServerMessage = (PNWR_SERVER_MESSAGE)OutputBuffer;
+ ServerName = ServerMessage->Server;
+ Message = (PWCHAR)((PCHAR)ServerMessage + ServerMessage->MessageOffset);
+
+ printf("From %S, Message = %S\n", ServerName, Message );
+#endif
+
+ return( status );
+}
+
+
diff --git a/private/nw/test/lock.c b/private/nw/test/lock.c
new file mode 100644
index 000000000..89eb4d4b2
--- /dev/null
+++ b/private/nw/test/lock.c
@@ -0,0 +1,80 @@
+
+
+/* LOCKING.C: This program opens a file with sharing. It locks
+
+ * some bytes before reading them, then unlocks them. Note that the
+ * program works correctly only if the following conditions are met:
+ * - The file exists
+ * - The program is run with MS-DOS version 3.0 or later
+ * with file sharing installed (SHARE.COM or SHARE.EXE), or
+ * if a Microsoft Networks compatible network is running
+
+ */
+
+#include <crt\io.h>
+#include <sys\types.h>
+#include <sys\stat.h>
+#include <sys\locking.h>
+#include <share.h>
+#include <fcntl.h>
+#include <stdio.h>
+
+#include <stdlib.h>
+
+#define STRING "0123456789012345678901234567890123456789012345678"
+
+void
+_cdecl
+main( void )
+{
+ int fh, numread;
+
+ char buffer[60];
+ /* Quit if can't open file or MS-DOS version doesn't support sharing. */
+ fh = _sopen( "test.txt",
+ _O_CREAT | _O_TRUNC | _O_RDWR,
+ _SH_DENYNO,
+ _S_IREAD | _S_IWRITE );
+
+ if( (fh == -1) || (_osmajor < 3) ) {
+ printf( "Cannot create/open file\n" );
+ exit( 1 );
+ }
+
+ _write( fh, STRING, sizeof(STRING));
+ _write( fh, STRING, sizeof(STRING));
+ _write( fh, STRING, sizeof(STRING));
+ _write( fh, STRING, sizeof(STRING));
+ _write( fh, STRING, sizeof(STRING));
+ _write( fh, STRING, sizeof(STRING));
+
+ /* Lock some bytes and read them. Then unlock. */
+ lseek( fh, 0L, SEEK_SET );
+ if( _locking( fh, LK_NBLCK, 50L ) != -1 )
+ {
+ printf( "No one can change these bytes while I'm reading them\n" );
+ numread = _read( fh, buffer, 50 );
+ printf( "%d bytes read: %.50s\n", numread, buffer );
+ lseek( fh, 0L, SEEK_SET );
+ _locking( fh, LK_UNLCK, 50L );
+ printf( "Now I'm done. Do what you will with them\n" );
+ }
+ else
+
+ perror( "Locking failed\n" );
+
+ lseek( fh, 4096L, SEEK_SET );
+ if( _locking( fh, LK_NBLCK, 1024L ) != -1 )
+ {
+ printf( "The lock off the end of the file worked ok\n" );
+ //lseek( fh, 4096L, SEEK_SET );
+ if( _locking( fh, LK_UNLCK, 1024L ) == -1 )
+ printf( "The second unlock failed\n" );
+ }
+ else
+
+ perror( "Locking failed\n" );
+
+ _close( fh );
+}
+
diff --git a/private/nw/test/lock2.c b/private/nw/test/lock2.c
new file mode 100644
index 000000000..d1e0ba45c
--- /dev/null
+++ b/private/nw/test/lock2.c
@@ -0,0 +1,65 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ tfile.c
+
+Abstract:
+
+ Test program for Win32 Base File API calls
+
+Author:
+
+ Mark Lucovsky (markl) 26-Sep-1990
+
+Revision History:
+
+--*/
+
+#include <windows.h>
+#include <assert.h>
+
+DWORD
+_cdecl
+main(
+ int argc,
+ char *argv[],
+ char *envp[]
+ )
+{
+
+ HANDLE iFile;
+
+ iFile = CreateFile(
+ "testfile.txt",
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL
+ );
+ assert(iFile != INVALID_HANDLE_VALUE);
+
+ assert(LockFile(iFile,0,0,10,0));
+ assert(LockFile(iFile,10,0,10,0));
+ assert(!LockFile(iFile,1,0,1,0));
+ assert(!LockFile(iFile,0,0,11,0));
+ assert(!LockFile(iFile,0,0,20,0));
+ assert(!UnlockFile(iFile,1,0,1,0));
+ assert(!UnlockFile(iFile,0,0,11,0));
+ assert(!UnlockFile(iFile,0,0,20,0));
+ assert(UnlockFile(iFile,0,0,10,0));
+ assert(UnlockFile(iFile,10,0,10,0));
+ assert(LockFile(iFile,0,0,10,0));
+ assert(LockFile(iFile,10,0,10,0));
+
+ Sleep(60 * 1000);
+
+ CloseHandle(iFile);
+
+ return 1;
+
+}
diff --git a/private/nw/test/lock3.c b/private/nw/test/lock3.c
new file mode 100644
index 000000000..eff9d0d55
--- /dev/null
+++ b/private/nw/test/lock3.c
@@ -0,0 +1,159 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ tfile.c
+
+Abstract:
+
+ Test program for Win32 Base File API calls
+
+Author:
+
+ Mark Lucovsky (markl) 26-Sep-1990
+
+Revision History:
+
+--*/
+
+#include <windows.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+VOID
+TryLock(
+ int Type,
+ HANDLE Handle,
+ DWORD Offset,
+ DWORD StartOffset,
+ DWORD Range,
+ BOOL ExpectedResult
+ );
+
+#define Lock 1
+#define Unlock 2
+
+DWORD
+_cdecl
+main(
+ int argc,
+ char *argv[],
+ char *envp[]
+ )
+{
+
+ HANDLE iFile;
+ BOOL success;
+ STARTUPINFO startupInfo;
+ PROCESS_INFORMATION processInfo;
+ SECURITY_ATTRIBUTES security;
+ char commandLine[80];
+ int offset;
+
+ printf( "Number of arguments = %d\n", argc );
+
+ if ( argc == 1 ) {
+
+ security.nLength = sizeof( security );
+ security.lpSecurityDescriptor = NULL;
+ security.bInheritHandle = TRUE;
+
+ iFile = CreateFile(
+ "testfile.txt",
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ &security,
+ OPEN_EXISTING,
+ 0,
+ NULL
+ );
+
+ printf("Created file, handle = %d\n", iFile );
+
+ memset( &startupInfo, 0, sizeof( startupInfo ) );
+ sprintf( commandLine, "%s %d\n", argv[0], iFile );
+
+ success = CreateProcess(
+ "D:\\444\\nt\\system32\\lock3.exe",
+ commandLine,
+ NULL, // Process security
+ NULL, // Thread security
+ TRUE, // Inherit Handles
+ 0, // Create flags
+ NULL, // Environment
+ NULL, // Current Directory
+ &startupInfo,
+ &processInfo );
+
+ if ( !success ) {
+ printf("Create process failed, err = %d\n", GetLastError() );
+ }
+ offset = 0;
+ } else {
+
+ iFile = (HANDLE)atoi( argv[1] );
+ printf("Inherited file handle = %d\n", iFile );
+ offset = 100;
+ }
+
+ assert(iFile != INVALID_HANDLE_VALUE);
+
+ TryLock( Lock, iFile, offset, 0, 10, TRUE );
+ TryLock( Lock, iFile, offset, 10, 10, TRUE );
+ TryLock( Lock, iFile, offset, 1, 1, FALSE );
+ TryLock( Lock, iFile, offset, 0, 11, FALSE );
+ TryLock( Lock, iFile, offset, 0, 20, FALSE );
+ TryLock( Unlock, iFile, offset, 1, 1, FALSE );
+ TryLock( Unlock, iFile, offset, 0, 12, FALSE );
+ TryLock( Unlock, iFile, offset, 0, 20, FALSE );
+ TryLock( Unlock, iFile, offset, 0, 10, TRUE );
+ TryLock( Unlock, iFile, offset, 10, 10, TRUE );
+ TryLock( Lock, iFile, offset, 0, 10, TRUE );
+ TryLock( Lock, iFile, offset, 10, 10, TRUE );
+
+ Sleep(5 * argc * 1000);
+
+ CloseHandle(iFile);
+ printf("Done\n");
+ return 1;
+
+}
+
+VOID
+TryLock(
+ int Type,
+ HANDLE Handle,
+ DWORD Offset,
+ DWORD StartOffset,
+ DWORD Range,
+ BOOL ExpectedResult
+ )
+{
+ BOOL result;
+ DWORD err;
+
+ if ( Type == Lock ) {
+ result = LockFile( Handle, StartOffset + Offset, 0, Range, 0 );
+ } else {
+ result = UnlockFile( Handle, StartOffset + Offset, 0, Range, 0 );
+ }
+
+ if ( result == ExpectedResult ) {
+ return;
+ } else {
+ err = GetLastError();
+ printf("Expected %d got %d for %slock at %d L%d\n",
+ ExpectedResult, result,
+ Type == Lock ? "" : "un",
+ StartOffset + Offset, Range );
+ if ( !result ) {
+ printf("Unexpected error = %08lx\n", err );
+ }
+ }
+}
+
+
+
diff --git a/private/nw/test/lock4.c b/private/nw/test/lock4.c
new file mode 100644
index 000000000..7ce0a3544
--- /dev/null
+++ b/private/nw/test/lock4.c
@@ -0,0 +1,157 @@
+/*++
+
+Copyright (c) 1990 Microsoft Corporation
+
+Module Name:
+
+ tfile.c
+
+Abstract:
+
+ Test program for Win32 Base File API calls
+
+Author:
+
+ Mark Lucovsky (markl) 26-Sep-1990
+
+Revision History:
+
+--*/
+
+#include <windows.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+VOID
+TryLock(
+ int Type,
+ HANDLE Handle,
+ DWORD Offset,
+ DWORD StartOffset,
+ DWORD Range,
+ BOOL ExpectedResult
+ );
+
+#define Lock 1
+#define Unlock 2
+
+DWORD
+_cdecl
+main(
+ int argc,
+ char *argv[],
+ char *envp[]
+ )
+{
+
+ HANDLE iFile;
+ int offset;
+ char *fileName;
+
+ printf( "Number of arguments = %d\n", argc );
+
+ if ( argc >= 2 ) {
+ fileName = argv[1];
+ } else {
+ fileName = "testfile.txt";
+ }
+
+
+ iFile = CreateFile(
+ fileName,
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL
+ );
+
+ if ( iFile != INVALID_HANDLE_VALUE ) {
+ printf("Opened file, handle = %d\n", iFile );
+ } else {
+ printf("Failed to open %s, err = %d\n", fileName, GetLastError() );
+ }
+
+ if ( argc == 3 ) {
+ offset = atoi( argv[2] );
+ } else {
+ offset = 0;
+ }
+
+ printf("File offset = %d\n", offset );
+
+ TryLock( Lock, iFile, offset, 10, 10, TRUE ); // 10 - 19
+ TryLock( Lock, iFile, offset, 10, 20, TRUE ); // 10 - 29
+ TryLock( Lock, iFile, offset, 0, 30, TRUE ); // 0 - 29
+// TryLock( Lock, iFile, offset, 1, 1, FALSE );
+// TryLock( Lock, iFile, offset, 0, 11, FALSE );
+// TryLock( Lock, iFile, offset, 0, 20, FALSE );
+// TryLock( Unlock, iFile, offset, 1, 1, FALSE );
+// TryLock( Unlock, iFile, offset, 0, 12, FALSE );
+// TryLock( Unlock, iFile, offset, 0, 20, FALSE );
+// TryLock( Lock, iFile, offset, 0, 10, TRUE );
+// TryLock( Lock, iFile, offset, 10, 10, TRUE );
+
+// Sleep(20 * 1000);
+
+ TryLock( Unlock, iFile, offset, 0, 10, TRUE );
+ TryLock( Unlock, iFile, offset, 10, 10, TRUE );
+
+ CloseHandle(iFile);
+ printf("Done\n");
+ return 1;
+
+}
+
+VOID
+TryLock(
+ int Type,
+ HANDLE Handle,
+ DWORD Offset,
+ DWORD StartOffset,
+ DWORD Range,
+ BOOL ExpectedResult
+ )
+{
+ BOOL result;
+ DWORD err;
+ OVERLAPPED overlapped;
+
+ if ( Type == Lock ) {
+ overlapped.Offset = StartOffset + Offset;
+ overlapped.OffsetHigh = 0;
+ result = LockFileEx( Handle, LOCKFILE_EXCLUSIVE_LOCK, 0, Range, 0, &overlapped );
+ if (!result && GetLastError() == ERROR_IO_PENDING) {
+ err = WaitForSingleObject( Handle, INFINITE );
+ if ( err == WAIT_OBJECT_0 ) {
+ err = overlapped.Internal;
+ }
+
+ if ( err == 0 ) {
+ result = TRUE;
+ } else {
+ result = FALSE;
+ }
+ }
+ } else {
+ result = UnlockFile( Handle, StartOffset + Offset, 0, Range, 0 );
+ }
+
+ if ( result == ExpectedResult ) {
+ return;
+ } else {
+ err = GetLastError();
+ printf("Expected %d got %d for %slock at %d L%d\n",
+ ExpectedResult, result,
+ Type == Lock ? "" : "un",
+ StartOffset + Offset, Range );
+ if ( !result ) {
+ printf("Unexpected error = %08lx\n", err );
+ }
+ }
+}
+
+
+
diff --git a/private/nw/test/makefile b/private/nw/test/makefile
new file mode 100644
index 000000000..6ee4f43fa
--- /dev/null
+++ b/private/nw/test/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/test/open.c b/private/nw/test/open.c
new file mode 100644
index 000000000..fa3984438
--- /dev/null
+++ b/private/nw/test/open.c
@@ -0,0 +1,144 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ fileio.c
+
+Abstract:
+
+ User mode test program for the Microsoft Netware redir file system.
+
+ This test program can be built from the command line using the
+ command 'nmake UMTEST=open'.
+
+Author:
+
+ Manny Weiser (mannyw) 17-May-1993
+
+Revision History:
+
+--*/
+
+#include <stdio.h>
+#include <string.h>
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+
+//
+// Local definitions
+//
+
+VOID
+DisplayUsage(
+ PSZ ProgramName
+ );
+
+
+BOOLEAN
+OpenFile(
+ PSZ Name,
+ PHANDLE Handle
+ );
+
+
+cdecl
+main(
+ int argc,
+ char *argv[],
+ )
+{
+ HANDLE handle;
+ BOOLEAN success;
+ char *fileName;
+
+ if ( argc == 1) {
+ DisplayUsage( argv[0] );
+ return( 1 );
+ }
+
+ fileName = argv[1];
+
+ success = OpenFile( fileName, &handle );
+ if ( !success) {
+ return 1;
+ }
+
+ NtClose( handle );
+
+ printf( "%s exiting\n", argv[0]);
+ return 0;
+}
+
+
+VOID
+DisplayUsage(
+ PSZ ProgramName
+ )
+{
+ printf( "Usage: %s [filename]", ProgramName);
+}
+
+
+BOOLEAN
+OpenFile(
+ PSZ Name,
+ PHANDLE Handle
+ )
+{
+ NTSTATUS status;
+ OBJECT_ATTRIBUTES objectAttributes;
+ IO_STATUS_BLOCK ioStatusBlock;
+ STRING AnsiFileName;
+ UNICODE_STRING FileName;
+
+ RtlInitString( &AnsiFileName, Name );
+ RtlOemStringToUnicodeString( &FileName, &AnsiFileName, TRUE );
+
+ //
+ // Open the file
+ //
+
+ InitializeObjectAttributes(
+ &objectAttributes,
+ &FileName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ status = NtOpenFile (
+ Handle,
+ FILE_GENERIC_READ,
+ &objectAttributes,
+ &ioStatusBlock,
+ FILE_SHARE_WRITE | FILE_SHARE_READ,
+ 0L
+ );
+
+ if (!NT_SUCCESS(status) ) {
+ printf( "Open status = %x for file %Z\n", status, &FileName );
+ }
+
+ RtlFreeHeap(RtlProcessHeap(), 0, FileName.Buffer );
+ return ( (BOOLEAN) NT_SUCCESS( status ) );
+}
+
+
+DoSleep(
+ IN ULONG time
+ )
+{
+ ULONG ms;
+ LARGE_INTEGER delayTime;
+
+ ms = time * 100;
+ delayTime = RtlEnlargedIntegerMultiply( ms, -10000 );
+ NtDelayExecution( TRUE, (PLARGE_INTEGER)&delayTime );
+
+ return( 0 );
+}
+
+
diff --git a/private/nw/test/sources b/private/nw/test/sources
new file mode 100644
index 000000000..f3f344158
--- /dev/null
+++ b/private/nw/test/sources
@@ -0,0 +1,44 @@
+!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:
+
+ Steve Wood (stevewo) 12-Apr-1990
+
+NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=ntos
+MINORCOMP=nwrdr
+
+TARGETNAME=testnw
+TARGETPATH=obj
+TARGETTYPE=library
+
+UMTYPE=console
+UMAPPL=testnw*attach*encrypt*lock2*userncp*lock3*fileio*lock4*getmsg*tex*open
+UMLIBS=
+UNICODE=1
+NTDEBUGTYPE=both
+NTDEBUG=ntsd
+386_OPTIMIZATION=/Od
+
+INCLUDES=..\inc;$(_NTROOT)\private\ntos\inc;$(_NTROOT)\private\inc;..\rdr
+
+SOURCES=
+
+USE_CL860_LARGE_MODEL=1
diff --git a/private/nw/test/testnw.c b/private/nw/test/testnw.c
new file mode 100644
index 000000000..7c95555f1
--- /dev/null
+++ b/private/nw/test/testnw.c
@@ -0,0 +1,124 @@
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#define WIN32_CONSOLE_APP
+#include <windows.h>
+#include <stdio.h>
+#include <ntddnwfs.h>
+
+#define CANCEL
+
+
+BOOLEAN test1();
+
+int
+_cdecl
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ UCHAR error = FALSE;
+ DefineDosDevice(DDD_RAW_TARGET_PATH, "R:", "\\Device\\NwRdr\\R:\\NETWARE311");
+ printf( "Test1...\n" );
+ if (!test1()) {
+ printf("Error: Test 1 failed\n");
+ error = TRUE;
+ }
+ return error;
+}
+
+
+
+BOOLEAN
+test1()
+{
+ HANDLE FileHandle;
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ UNICODE_STRING UnicodeString;
+ IO_STATUS_BLOCK IoStatus;
+
+ NWR_REQUEST_PACKET Input;
+
+ printf( "test 1: opening\n");
+
+ RtlInitUnicodeString( &UnicodeString, L"\\Device\\NwRdr" );
+
+ InitializeObjectAttributes( &ObjectAttributes,
+ &UnicodeString,
+ 0,
+ NULL,
+ NULL
+ );
+/* Status = NtCreateFile( &FileHandle,
+ FILE_LIST_DIRECTORY | SYNCHRONIZE,
+ &ObjectAttributes,
+ &IoStatus,
+ (PLARGE_INTEGER) NULL,
+ 0L,
+ 0L,
+ FILE_CREATE,
+ FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_ALERT,
+ (PVOID) NULL,
+ 0L );
+*/
+ Status = NtOpenFile ( &FileHandle,
+ FILE_LIST_DIRECTORY | SYNCHRONIZE,
+ &ObjectAttributes,
+ &IoStatus,
+ FILE_SHARE_READ,
+ FILE_DIRECTORY_FILE);
+
+ if (Status != STATUS_SUCCESS) {
+ printf( "test 1: Wrong return value %X - open \n",Status);
+ return FALSE;
+ }
+
+ if (IoStatus.Status != STATUS_SUCCESS) {
+ printf( "test 2: Wrong I/O Status value %X - open \n",IoStatus.Status);
+ return FALSE;
+ }
+
+ printf( "test 1: opened device successfully\n");
+
+ Input.Version = REQUEST_PACKET_VERSION;
+ Input.Parameters.DebugValue.DebugFlags = 0xffffffff;
+
+ NtFsControlFile( FileHandle,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatus,
+ FSCTL_NWR_DEBUG,
+ &Input,
+ sizeof(Input),
+ NULL,
+ 0);
+
+ if (Status != STATUS_SUCCESS) {
+ printf( "test 1: Wrong return value %X - SetDebug \n",Status);
+ return FALSE;
+ }
+
+ if (IoStatus.Status != STATUS_SUCCESS) {
+ printf( "test 2: Wrong I/O Status value %X - SetDebug \n",IoStatus.Status);
+ return FALSE;
+ }
+
+ printf( "test 1: set debug trace level successfully\n");
+
+ //
+ // Now close the device
+ //
+
+ printf("test 1: closing device\n");
+
+ Status = NtClose( FileHandle );
+ if (Status != STATUS_SUCCESS) {
+ printf( "test 1: Wrong return value %lX - close \n",Status);
+ return FALSE;
+ }
+ return TRUE;
+}
+
diff --git a/private/nw/test/tex.c b/private/nw/test/tex.c
new file mode 100644
index 000000000..b251aa063
--- /dev/null
+++ b/private/nw/test/tex.c
@@ -0,0 +1,740 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ tex.c
+
+Abstract:
+
+ User mode test program for the Microsoft Netware redir file system.
+
+ This test program can be built from the command line using the
+ command 'nmake UMTEST=tex'.
+
+Author:
+
+ Manny Weiser (mannyw) 7-Jun-1993
+
+Revision History:
+
+--*/
+
+#include <stdio.h>
+#include <string.h>
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <ntddnwfs.h>
+#include <ntddnwp.h>
+#include <STDARG.H>
+
+//
+// Local definitions
+//
+
+
+VOID
+DisplayUsage(
+ PSZ ProgramName
+ );
+
+SendMessage(
+ IN char* Format,
+ ...
+ );
+
+VOID
+SetLock(
+ PCHAR FileHandle,
+ BOOLEAN Exclusive,
+ ULONG ByteOffset,
+ ULONG Length,
+ ULONG Timeout
+ );
+
+NTSTATUS
+FormatRequest(
+ PCHAR SendBuffer,
+ PULONG SendBufferLength,
+ char* Format,
+ va_list a
+ );
+
+BOOLEAN
+OpenServer(
+ PHANDLE Handle,
+ PWCH ServerName
+ );
+
+NTSTATUS
+_cdecl
+ParseResponse(
+ char* FormatString,
+ ... // format specific parameters
+ );
+
+#define BUFFER_SIZE 200
+
+WCHAR *ServerName = L"NETWARE311";
+UCHAR Pid = 255;
+
+HANDLE ServerHandle;
+_cdecl
+main(
+ int argc,
+ char *argv[],
+ )
+{
+ BOOLEAN success;
+ UCHAR DirectoryHandle;
+ UCHAR FileHandle1[6];
+ UCHAR FileHandle2[6];
+
+ success = OpenServer( &ServerHandle, ServerName );
+ if ( !success) {
+ return 1;
+ }
+
+ printf("Opened server, %S\n", ServerName );
+
+ // Get directory handle for SYS:
+
+ SendMessage( "Sbbp", 0x16, 0x12, 0, 0, "SYS:" );
+ ParseResponse( "b", &DirectoryHandle );
+
+ // Open FILE1
+
+ Pid = 100;
+ SendMessage( "Fbbbp", 0x4C, DirectoryHandle, 0x07, 01, "File1" );
+ ParseResponse( "r", FileHandle1, 6 );
+
+ // Open FILE1
+
+ Pid = 101;
+ SendMessage( "Fbbbp", 0x4C, DirectoryHandle, 0x07, 01, "File1" );
+ ParseResponse( "r", FileHandle2, 6 );
+
+ // Lock test
+
+ Pid = 100;
+ SetLock( FileHandle1, TRUE, 0, 10, 0xFFFF );
+ SetLock( FileHandle1, TRUE, 10, 10, 0xFFFF );
+ SetLock( FileHandle1, TRUE, 5, 10, 0xFFFF );
+ SetLock( FileHandle1, TRUE, 10, 10, 0xFFFF );
+
+ Pid = 101;
+ SetLock( FileHandle2, TRUE, 6, 10, 0xFFFF );
+ SetLock( FileHandle2, TRUE, 7, 10, 0xFFFF );
+ SetLock( FileHandle2, TRUE, 0, 10, 0xFFFF );
+
+ Sleep( 10 * 1000 );
+
+ // Close FILE1
+
+ SendMessage( "F-r", 0x42, FileHandle1, 6 );
+ ParseResponse( "" );
+
+ // Close FILE1
+
+ SendMessage( "F-r", 0x42, FileHandle2, 6 );
+ ParseResponse( "" );
+
+ // End Job
+
+ Pid = 100;
+ SendMessage( "F-", 0x18 );
+ Pid = 101;
+ SendMessage( "F-", 0x18 );
+
+ // Close the directory
+
+ SendMessage( "Sb", 0x16, 0x14, DirectoryHandle );
+ ParseResponse( "" );
+
+ printf( "%s exiting\n", argv[0]);
+ return 0;
+}
+
+VOID
+SetLock(
+ PCHAR FileHandle,
+ BOOLEAN Exclusive,
+ ULONG ByteOffset,
+ ULONG Length,
+ ULONG Timeout
+ )
+{
+ USHORT Flags;
+
+ if ( Exclusive ) {
+ Flags = 1;
+ } else {
+ Flags = 3;
+ }
+
+ SendMessage( "Fbrddw", 0x1A, Flags, FileHandle, 6, ByteOffset, Length, Timeout );
+ ParseResponse( "" );
+}
+
+BOOLEAN
+OpenServer(
+ PHANDLE Handle,
+ PWCH ServerName
+ )
+{
+ NTSTATUS status;
+ OBJECT_ATTRIBUTES objectAttributes;
+ IO_STATUS_BLOCK ioStatusBlock;
+ WCHAR FileNameBuffer[100];
+ UNICODE_STRING FileName;
+
+ FileName.Buffer = FileNameBuffer;
+ FileName.Length = 0;
+ FileName.MaximumLength = 100;
+
+ status = RtlAppendUnicodeToString( &FileName, DD_NWFS_DEVICE_NAME_U );
+ ASSERT( status == STATUS_SUCCESS );
+ status = RtlAppendUnicodeToString( &FileName, L"\\" );
+ ASSERT( status == STATUS_SUCCESS );
+ status = RtlAppendUnicodeToString( &FileName, ServerName );
+ ASSERT( status == STATUS_SUCCESS );
+
+ //
+ // Open the file
+ //
+
+ InitializeObjectAttributes(
+ &objectAttributes,
+ &FileName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ status = NtOpenFile (
+ Handle,
+ FILE_GENERIC_READ | SYNCHRONIZE,
+ &objectAttributes,
+ &ioStatusBlock,
+ FILE_SHARE_WRITE | FILE_SHARE_READ,
+ 0L
+ );
+
+ if (!NT_SUCCESS(status) ) {
+ printf( "Open status = %x for file %Z\n", status, &FileName );
+ }
+
+ return ( (BOOLEAN) NT_SUCCESS( status ) );
+}
+
+
+CHAR Buffer1[100];
+CHAR Buffer2[100];
+
+SendMessage(
+ IN char* Format,
+ ...
+ )
+{
+ NTSTATUS status;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ va_list Arguments;
+ NTSTATUS Status;
+ ULONG ReceiveBufferSize = 100;
+ ULONG SendBufferSize = 100;
+
+ va_start( Arguments, Format );
+
+ Buffer1[0] = Pid;
+
+ Status = FormatRequest( &Buffer1[1], &SendBufferSize, Format, Arguments );
+ if ( !NT_SUCCESS( Status ) ) {
+ return( Status );
+ }
+
+ printf("Sending message\n" );
+
+ status = NtFsControlFile(
+ ServerHandle,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_ANY_NCP,
+ Buffer1,
+ SendBufferSize + 1,
+ Buffer2,
+ ReceiveBufferSize
+ );
+
+ if ( NT_SUCCESS( status ) ) {
+
+ status = NtWaitForSingleObject( ServerHandle, FALSE, NULL );
+ if ( NT_SUCCESS( status )) {
+ status = IoStatusBlock.Status;
+ }
+ }
+
+ if ( !NT_SUCCESS( status ) ) {
+ printf("NtFsControlFile returns %08lx\n", status );
+ return( status );
+ } else {
+ printf("Message received\n" );
+ }
+
+
+ return( status );
+}
+
+
+NTSTATUS
+FormatRequest(
+ PCHAR SendBuffer,
+ PULONG SendBufferLength,
+ char* Format,
+ va_list a
+ )
+/*++
+
+Routine Description:
+
+ Send the packet described by Format and the additional parameters.
+
+Arguments:
+
+ Format - the information needed to create the request to the
+ server. The first byte indicates the packet type and the
+ following bytes contain field types.
+
+ Packet types:
+
+ 'A' SAP broadcast ( void )
+ 'C' NCP connect ( void )
+ 'F' NCP function ( byte )
+ 'S' NCP subfunction ( byte, byte )
+ 'D' NCP disconnect ( void )
+
+ Field types, request/response:
+
+ 'b' byte ( byte / byte* )
+ 'w' hi-lo word ( word / word* )
+ 'd' hi-lo dword ( dword / dword* )
+ '-' zero/skip byte ( void )
+ '=' zero/skip word ( void )
+ ._. zero/skip string ( word )
+ 'p' pstring ( char* )
+ 'u' p unicode string ( UNICODE_STRING * )
+ 'c' cstring ( char* )
+ 'r' raw bytes ( byte*, word )
+ 'u' p unicode string ( UNICODE_STRING * )
+ 'U' p uppercase string( UNICODE_STRING * )
+ 'f' separate fragment ( PMDL )
+
+ An 'f' field must be last, and in a response it cannot be
+ preceeded by 'p' or 'c' fields.
+
+
+Return Value:
+
+ Normally returns STATUS_PENDING.
+
+--*/
+{
+ NTSTATUS status;
+ char* pFormatCharacter;
+ USHORT t = 1;
+ ULONG data_size;
+ ULONG length;
+
+ data_size = 1;
+
+ SendBuffer[ 0 ] = va_arg( a, UCHAR );
+
+ if ( *Format == 'S' ) {
+ data_size += 2;
+ SendBuffer[data_size++] = va_arg( a, UCHAR );
+ }
+
+ pFormatCharacter = Format;
+
+ while ( *++pFormatCharacter && *pFormatCharacter != 'f' )
+ {
+ switch ( *pFormatCharacter ) {
+
+ case '=':
+ SendBuffer[data_size++] = 0;
+ case '-':
+ SendBuffer[data_size++] = 0;
+ break;
+
+ case '_':
+ length = va_arg ( a, USHORT );
+
+ if ( data_size + length > *SendBufferLength ) {
+ printf("***exch:!0!\n");
+ return( FALSE );
+ }
+
+ while ( length-- ) {
+ SendBuffer[data_size++] = 0;
+ }
+
+ break;
+
+ case 'b':
+ SendBuffer[data_size++] = va_arg ( a, UCHAR );
+ break;
+
+ case 'w':
+ {
+ USHORT w = va_arg ( a, USHORT );
+
+ SendBuffer[data_size++] = (UCHAR) (w >> 8);
+ SendBuffer[data_size++] = (UCHAR) (w >> 0);
+ break;
+ }
+
+ case 'd':
+ {
+ ULONG d = va_arg ( a, ULONG );
+
+ SendBuffer[data_size++] = (UCHAR) (d >> 24);
+ SendBuffer[data_size++] = (UCHAR) (d >> 16);
+ SendBuffer[data_size++] = (UCHAR) (d >> 8);
+ SendBuffer[data_size++] = (UCHAR) (d >> 0);
+ break;
+ }
+
+ case 'c':
+ {
+ char* c = va_arg ( a, char* );
+
+ length = strlen( c );
+
+ if ( data_size + length > *SendBufferLength ) {
+ printf("***exch:!1!\n");
+ return( FALSE );
+ }
+
+ RtlCopyMemory( &SendBuffer[data_size], c, length + 1 );
+ data_size += length + 1;
+ break;
+ }
+
+ case 'p':
+ {
+ char* c = va_arg ( a, char* );
+
+ length = strlen( c );
+
+ if ( data_size + length > *SendBufferLength ) {
+ printf("***exch:!2!\n");
+ return FALSE;
+ }
+
+ SendBuffer[data_size++] = (UCHAR)length;
+ RtlCopyMemory( &SendBuffer[data_size], c, length );
+ data_size += length;
+ break;
+ }
+
+ case 'u':
+ {
+ PUNICODE_STRING pUString = va_arg ( a, PUNICODE_STRING );
+ OEM_STRING OemString;
+
+ //
+ // Calculate required string length, excluding trailing NUL.
+ //
+
+ length = (USHORT)RtlUnicodeStringToOemSize( pUString ) - 1;
+ ASSERT( length < 0x100 );
+
+ if ( data_size + length > *SendBufferLength ) {
+ printf("***exch:!4!\n");
+ return( FALSE );
+ }
+
+ SendBuffer[data_size++] = (UCHAR)length;
+ OemString.Buffer = &SendBuffer[data_size];
+ OemString.MaximumLength = (USHORT)length + 1;
+ status = RtlUnicodeStringToCountedOemString( &OemString, pUString, FALSE );
+ ASSERT( NT_SUCCESS( status ));
+ data_size += (USHORT)length;
+ break;
+ }
+
+ case 'U':
+ {
+ USHORT i;
+
+ //
+ // UPPERCASE the string, copy it from unicode to the packet
+ //
+
+ PUNICODE_STRING pUString = va_arg ( a, PUNICODE_STRING );
+ UNICODE_STRING UUppercaseString;
+ OEM_STRING OemString;
+
+ if ( pUString->Length > 0 ) {
+
+ RtlUpcaseUnicodeString( &UUppercaseString, pUString, TRUE );
+
+ //
+ // Change all '\' to '/'
+ //
+
+ for ( i = 0 ; i < UUppercaseString.Length ; i++ ) {
+ if ( UUppercaseString.Buffer[i] == L'\\' ) {
+ UUppercaseString.Buffer[i] = L'/';
+ }
+ }
+
+ //
+ // Calculate required string length, excluding trailing NUL.
+ //
+
+ length = (USHORT)RtlUnicodeStringToOemSize( &UUppercaseString ) - 1;
+ ASSERT( length < 0x100 );
+
+ } else {
+ UUppercaseString = *pUString;
+ length = 0;
+ }
+
+ if ( data_size + length > *SendBufferLength ) {
+ printf("***exch:!5!\n");
+ return( FALSE );
+ }
+
+ SendBuffer[data_size++] = (UCHAR)length;
+ OemString.Buffer = &SendBuffer[data_size];
+ OemString.MaximumLength = (USHORT)length + 1;
+ status = RtlUnicodeStringToCountedOemString( &OemString,
+ &UUppercaseString,
+ FALSE );
+ ASSERT( NT_SUCCESS( status ));
+
+ if ( pUString->Length > 0 ) {
+ RtlFreeUnicodeString( &UUppercaseString );
+ }
+
+ data_size += (USHORT)length;
+ break;
+ }
+
+ case 'r':
+ {
+ UCHAR* b = va_arg ( a, UCHAR* );
+ length = va_arg ( a, USHORT );
+
+ if ( data_size + length > *SendBufferLength ) {
+ printf("***exch:!6!\n");
+ return( FALSE );
+ }
+
+ RtlCopyMemory( &SendBuffer[data_size], b, length );
+ data_size += length;
+ break;
+ }
+
+ default:
+ printf ( "*****exchange: invalid request field, %x\n", *pFormatCharacter);
+ return( FALSE );
+ }
+
+ if ( data_size > *SendBufferLength ) {
+ printf( "*****exchange: CORRUPT, too much request data\n" );
+ va_end( a );
+ return FALSE;
+ }
+ }
+
+ if ( *Format == 'S' )
+ {
+ SendBuffer[1] = (UCHAR)((data_size-3) >> 8);
+ SendBuffer[2] = (UCHAR)(data_size-3);
+ }
+
+ va_end( a );
+
+ *SendBufferLength = data_size;
+ return TRUE;
+}
+
+NTSTATUS
+_cdecl
+ParseResponse(
+ char* FormatString,
+ ... // format specific parameters
+ )
+/*++
+
+Routine Description:
+
+ This routine parse an NCP response.
+
+Arguments:
+
+ FormatString - supplies the information needed to create the request to the
+ server. The first byte indicates the packet type and the
+ following bytes contain field types.
+
+ Field types, request/response:
+
+ 'b' byte ( byte* )
+ 'w' hi-lo word ( word* )
+ 'd' hi-lo dword ( dword* )
+ '-' zero/skip byte ( void )
+ '=' zero/skip word ( void )
+ ._. zero/skip string ( word )
+ 'p' pstring ( char* )
+ 'c' cstring ( char* )
+ 'r' raw bytes ( byte*, word )
+ 'R' ASCIIZ to Unicode ( UNICODE_STRING *, word )
+
+Return Value:
+
+ STATUS - The converted error code from the NCP response.
+
+--*/
+
+{
+ PCHAR FormatByte;
+ va_list Arguments;
+ int Length = 0;
+
+ va_start( Arguments, FormatString );
+
+ FormatByte = FormatString;
+ while ( *FormatByte ) {
+
+ switch ( *FormatByte ) {
+
+ case '-':
+ Length += 1;
+ break;
+
+ case '=':
+ Length += 2;
+ break;
+
+ case '_':
+ {
+ USHORT l = va_arg ( Arguments, USHORT );
+ Length += l;
+ break;
+ }
+
+ case 'b':
+ {
+ UCHAR* b = va_arg ( Arguments, UCHAR* );
+ *b = Buffer2[Length++];
+ break;
+ }
+
+ case 'w':
+ {
+ UCHAR* b = va_arg ( Arguments, UCHAR* );
+ b[1] = Buffer2[Length++];
+ b[0] = Buffer2[Length++];
+ break;
+ }
+
+ case 'd':
+ {
+ UCHAR* b = va_arg ( Arguments, UCHAR* );
+ b[3] = Buffer2[Length++];
+ b[2] = Buffer2[Length++];
+ b[1] = Buffer2[Length++];
+ b[0] = Buffer2[Length++];
+ break;
+ }
+
+ case 'c':
+ {
+ char* c = va_arg ( Arguments, char* );
+ USHORT l = strlen( &Buffer2[Length] );
+ memcpy ( c, &Buffer2[Length], l+1 );
+ Length += l+1;
+ break;
+ }
+
+ case 'p':
+ {
+ char* c = va_arg ( Arguments, char* );
+ UCHAR l = Buffer2[Length++];
+ memcpy ( c, &Buffer2[Length], l );
+ c[l+1] = 0;
+ break;
+ }
+
+#if 0
+ case 'P':
+ {
+ PUNICODE_STRING pUString = va_arg ( Arguments, PUNICODE_STRING );
+ OEM_STRING OemString;
+
+ OemString.Length = Buffer2[Length++];
+ OemString.Buffer = &Buffer2[Length];
+
+ Status = RtlOemStringToCountedUnicodeString( pUString, &OemString, FALSE );
+ ASSERT( NT_SUCCESS( Status ));
+
+ break;
+ }
+#endif
+
+ case 'r':
+ {
+ UCHAR* b = va_arg ( Arguments, UCHAR* );
+ USHORT l = va_arg ( Arguments, USHORT );
+ RtlCopyMemory( b, &Buffer2[Length], l );
+ Length += l;
+ break;
+ }
+
+#if 0
+ case 'R':
+ {
+ //
+ // Interpret the buffer as an ASCIIZ string. Convert
+ // it to unicode in the preallocated buffer.
+ //
+
+ PUNICODE_STRING pUString = va_arg ( Arguments, PUNICODE_STRING );
+ OEM_STRING OemString;
+ USHORT len = va_arg ( Arguments, USHORT );
+
+ OemString.Buffer = &Buffer2[Length];
+ OemString.Length = strlen( OemString.Buffer );
+ OemString.MaximumLength = OemString.Length;
+
+ Status = RtlOemStringToCountedUnicodeString( pUString, &OemString, FALSE );
+ ASSERT( NT_SUCCESS( Status ));
+ Length += len;
+ break;
+ }
+#endif
+
+ default:
+ printf ( "*****exchange: invalid response field, %x\n", *FormatByte );
+ return( FALSE );
+ }
+
+#if 0
+ if ( Length > PacketLength )
+ {
+ printf ( "*****exchange: not enough response data, %d\n", i );
+ }
+#endif
+ FormatByte++;
+ }
+
+ va_end( Arguments );
+}
+
+
diff --git a/private/nw/test/tp1.c b/private/nw/test/tp1.c
new file mode 100644
index 000000000..5ce2ebc57
--- /dev/null
+++ b/private/nw/test/tp1.c
@@ -0,0 +1,1637 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ tex.c
+
+Abstract:
+
+ User mode test program for the Microsoft Netware redir file system.
+
+ This test program can be built from the command line using the
+ command 'nmake UMTEST=tex'.
+
+Author:
+
+ Manny Weiser (mannyw) 7-Jun-1993
+
+Revision History:
+
+--*/
+
+#include <conio.h>
+#include <stdio.h>
+#include <string.h>
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <ntddnwfs.h>
+#include <STDARG.H>
+
+#define OT_USER 1
+
+
+// Hex dump
+ULONG MaxDump = 256;
+
+VOID
+dump(
+ IN PVOID far_p,
+ IN ULONG len
+ );
+
+VOID
+HexDumpLine (
+ PCHAR pch,
+ ULONG len,
+ PCHAR s,
+ PCHAR t,
+ USHORT flag
+ );
+
+
+// End Hex dump
+
+VOID
+ResetPassword(
+ PUCHAR Vnew
+ );
+
+UCHAR
+LookUp(
+ UCHAR Value,
+ UCHAR Mask,
+ int index
+ );
+
+SendMessage(
+ IN char* Format,
+ ...
+ );
+
+NTSTATUS
+FormatRequest(
+ PCHAR SendBuffer,
+ PULONG SendBufferLength,
+ char* Format,
+ va_list a
+ );
+
+BOOLEAN
+OpenServer(
+ PHANDLE Handle,
+ PWCH ServerName
+ );
+
+NTSTATUS
+_cdecl
+ParseResponse(
+ char* FormatString,
+ ... // format specific parameters
+ );
+
+VOID
+Shuffle(
+ UCHAR *achObjectId,
+ UCHAR *szUpperPassword,
+ int iPasswordLen,
+ UCHAR *achOutputBuffer
+ );
+
+int
+Scramble(
+ int iSeed,
+ UCHAR achBuffer[32]
+ );
+
+VOID
+RespondToChallenge(
+ IN PUCHAR achObjectId,
+ IN POEM_STRING Password,
+ IN PUCHAR pChallenge,
+ OUT PUCHAR pResponse
+ );
+
+VOID
+RespondToChallengePart1(
+ IN PUCHAR achObjectId,
+ IN POEM_STRING Password,
+ OUT PUCHAR pResponse
+ );
+
+VOID
+RespondToChallengePart2(
+ IN PUCHAR pResponsePart1,
+ IN PUCHAR pChallenge,
+ OUT PUCHAR pResponse
+ );
+
+NTSTATUS
+GetCurrentPasswordValue (
+ IN OUT PCHAR OutputValue
+ );
+
+#define BUFFER_SIZE 200
+
+WCHAR *ServerName = L"YIHSINS3";
+UCHAR Pid = 255;
+UCHAR Object[4];
+
+WCHAR FileNameBuffer[100];
+UNICODE_STRING FileName;
+
+#define NW_USER "ANDYHE"
+
+
+HANDLE ServerHandle;
+_cdecl
+main(
+ int argc,
+ char *argv[],
+ )
+{
+ BOOLEAN success;
+ UCHAR Challenge[8];
+ UCHAR fileValue[17];
+ UCHAR ResponseP2[16];
+ int x, y;
+ NTSTATUS status;
+
+
+#if 0
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+ 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
+ 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
+ 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+
+#endif
+
+#define START_POSITION 0x00
+
+ UCHAR Vold[] = {
+0x00,0x23,0x45,0x67,0x89,0xAB,0xCD,0x3F,0x00,0x24,0x45,0x67,0x89,0xAB,0xCD,0x4F,0x11,
+0x00,0x23,0x45,0x67,0x29,0x2B,0xCD,0xEF,0x00,0x23,0x45,0x67,0x79,0x7B,0xCD,0xEF,0x11
+ };
+
+ UCHAR Vc[] = {
+// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22
+#if 0
+ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+ 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
+ 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
+ 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+#endif
+ };
+
+ success = OpenServer( &ServerHandle, ServerName );
+ if ( !success) {
+ return 1;
+ }
+
+ printf("Opened server, %wS\n", ServerName );
+
+ //
+ // Setup the file name that we'll be opening every time.
+ //
+
+ FileName.Buffer = FileNameBuffer;
+ FileName.Length = 0;
+ FileName.MaximumLength = 100;
+
+ status = RtlAppendUnicodeToString( &FileName, DD_NWFS_DEVICE_NAME_U );
+ ASSERT( status == STATUS_SUCCESS );
+ status = RtlAppendUnicodeToString( &FileName, L"\\" );
+ status = RtlAppendUnicodeToString( &FileName, ServerName );
+ status = RtlAppendUnicodeToString( &FileName, L"\\SYS:\\SYSTEM\\NET$VAL.SYS" );
+
+ // Get objectid
+ SendMessage(
+ "Swp",
+ 0x17, 0x35,
+ OT_USER,
+ NW_USER);
+
+ ParseResponse("r", Object, 4 );
+
+ // jump into the middle of a run...
+
+ x = 0;
+ y = START_POSITION * 17;
+ goto start;
+
+ for ( x = 0 ; x < sizeof(Vold) ; x+= 17 ) {
+
+ for ( y = 0 ; y < sizeof(Vc) ; y+= 17 ) {
+
+start:
+ //
+ // Set the password on the server to be the value of Vold required for
+ // the second part of the test.
+ //
+
+ ResetPassword( Vold + x );
+
+ //
+ // We now know that Vold on the server is the same as the Vold vector.
+ // We can now set the password to any Vnew.
+ // We can do this without the real password because the
+ // server will believe we know the real password if we give it
+ // the result of taking Vold and the Challenge key and passing
+ // it through RespondToChallengePart2
+ //
+
+ printf( "Getting a challenge key.... " );
+
+ SendMessage(
+ "S",
+ 0x17, 0x17);
+
+ ParseResponse("r", Challenge, sizeof(Challenge) );
+
+ // Fabricate the number the server will use to validate the password change
+ RespondToChallengePart2( Vold + x, Challenge, ResponseP2 );
+
+ printf( "Vold= " ); dump( Vold + x, 17);
+
+ // Put out results to console...
+
+ status = GetCurrentPasswordValue( &fileValue[0] );
+
+ if ( NT_SUCCESS( status ) ) {
+
+ printf( "File= " );
+ dump( fileValue, 17);
+ }
+ printf( " Vc= "); dump( Vc + y, 17);
+
+ printf( "Setting the new password... " );
+
+ // Send the set password
+ SendMessage("Srwpr", 0x17, 0x4b,
+ ResponseP2, 8,
+ OT_USER,
+ NW_USER,
+ Vc + y, 17 );
+
+ status = GetCurrentPasswordValue( &fileValue[0] );
+
+ if ( NT_SUCCESS( status ) ) {
+
+ printf( "Vnew= ");
+ dump( fileValue, 17);
+ }
+
+// printf( "Press a key...\n" ); getch();
+
+// printf( "\n" );
+ printf( "\n\n" );
+ }
+ }
+
+ printf( "%s exiting\n", argv[0]);
+ return 0;
+}
+
+NTSTATUS
+GetCurrentPasswordValue (
+ IN OUT PCHAR OutputValue
+ )
+//
+// This routine reads the current password from the file.
+//
+{
+ HANDLE fileHandle;
+ LARGE_INTEGER filePosition;
+ OBJECT_ATTRIBUTES objectAttributes;
+ IO_STATUS_BLOCK ioStatusBlock;
+ int i;
+ PCHAR ch;
+ NTSTATUS status;
+
+ ch = OutputValue;
+ for (i = 0; i < 17; i++) {
+
+ *(ch++) = '0';
+ }
+
+ // Send a Close Bindery to the server
+
+ printf( "Closing the bindery... " );
+
+ SendMessage(
+ "S",
+ 0x17, 0x44);
+
+ //
+ // Open the server\sys:system\net$val.sys file which the server just
+ // closed.
+ //
+
+ InitializeObjectAttributes(
+ &objectAttributes,
+ &FileName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ status = NtOpenFile (
+ &fileHandle,
+ FILE_GENERIC_READ | SYNCHRONIZE,
+ &objectAttributes,
+ &ioStatusBlock,
+ FILE_SHARE_WRITE | FILE_SHARE_READ,
+ 0L
+ );
+
+ if (!NT_SUCCESS(status) ) {
+
+ printf( " Open failed. Status = 0x%x\n", status );
+
+ } else {
+
+ //
+ // Seek to the spot in the file that has the supervisor's password.
+ // This will be different for different servers. Here's a table :
+ // ObjectID = Supervisor AndyHe
+ // Netware311 : 0x097C
+ // Mars312 : 0x16CC
+ // YihsinS3 : 0x59EA 0x050C
+ //
+
+ filePosition.HighPart = 0L;
+ filePosition.LowPart = 0x050C;
+
+ // Read the 17 bytes at that offset and close the file
+
+ status = NtReadFile (
+ fileHandle,
+ NULL,
+ NULL,
+ NULL,
+ &ioStatusBlock,
+ OutputValue,
+ 17,
+ &filePosition,
+ NULL
+ );
+
+ if (!NT_SUCCESS(status) ) {
+
+ printf( " Read failed. Status = 0x%x\n", status );
+ }
+
+ NtClose( fileHandle );
+ }
+
+ // Send an Open Bindery to the server
+
+ printf( "Opening the bindery... " );
+ SendMessage(
+ "S",
+ 0x17, 0x45);
+
+ return(status);
+}
+
+VOID
+ResetPassword(
+ PUCHAR Vnew
+ )
+{
+//
+// Note Vc[0] specifies the length. To derive new values of Vnew use the following
+// table. For each nibble desired in Vnew, scan the appropriate column and
+// read off the corresponding Vc nibble value on the left.
+//
+
+ UCHAR Vc[17];
+
+ UCHAR Challenge[8];
+ OEM_STRING Password = {0,0,""};
+ UCHAR EncryptKey[8];
+
+ int x;
+
+ //printf( "Get Login key for set password using NULL password\n");
+
+ printf( "Getting a challenge key.... " );
+
+ SendMessage(
+ "S",
+ 0x17, 0x17);
+
+ ParseResponse("r", Challenge, sizeof(Challenge) );
+
+ RespondToChallenge( Object, &Password, Challenge, EncryptKey );
+ // dump( EncryptKey, 8);
+
+ //
+ // Build Vc so that the server will get Vnew stored in the bindery.
+ // Note Vols/Vnew have the length at V[17] whereas Vc has it at Vc[0].
+ //
+
+ for ( x = 0 ; x < 16; x++ ) {
+ Vc[x+1] = LookUp( Vnew[x], 0xf0, x) | LookUp( Vnew[x], 0x0f, x);
+ }
+
+ Vc[0] = LookUp( Vnew[16], 0xf0, 16) | LookUp( Vnew[16], 0x0f, 16);
+
+ //printf( "Vnew "); dump( Vnew, 17);
+ //printf( "Vc "); dump( Vc, 17);
+
+ //
+ // This uses the null password, objectid=1 and our supervisor privilege
+ // to set it, regardless of the and our ability to figure out what
+ // Vnew will be created for a particular Vc.
+ //
+
+ printf( "Setting the old password... " );
+
+ SendMessage("Srwpr", 0x17, 0x4b,
+ EncryptKey, sizeof(EncryptKey),
+ OT_USER,
+ NW_USER,
+ Vc, 17 );
+
+ ParseResponse( "" );
+}
+
+UCHAR
+LookUp(
+ UCHAR Value,
+ UCHAR Mask,
+ int index
+ )
+{
+
+#if 0
+
+UCHAR Vtab[] = {
+
+ // This table is for the supervisor
+
+0x97, 0xE3, 0x73, 0xD8, 0x70, 0x5B, 0x85, 0xD3, 0xF5, 0xCD, 0xDB, 0xFA, 0xDC, 0xEB, 0x98, 0x64, 0x18, //0//
+0x31, 0x6B, 0x88, 0x1E, 0x88, 0x09, 0x10, 0x29, 0x0B, 0x84, 0x61, 0x03, 0x43, 0xA1, 0xD5, 0x5A, 0x09, //1//
+0xCB, 0xFF, 0x67, 0x70, 0xB6, 0xE3, 0x4E, 0x9A, 0x63, 0x27, 0x1A, 0x3C, 0x5A, 0x9A, 0x3B, 0xFC, 0x3A, //2//
+0xEF, 0xA8, 0x44, 0x42, 0xDB, 0x1E, 0x59, 0x31, 0x17, 0xEB, 0xF5, 0x99, 0x18, 0x8E, 0x63, 0xAE, 0x2B, //3//
+0x23, 0x07, 0xEB, 0xC9, 0xCD, 0xD6, 0xC7, 0xCF, 0xC8, 0xBF, 0x0F, 0xE2, 0x01, 0x16, 0x42, 0xD3, 0x1C, //4//
+0x8A, 0xC4, 0x06, 0xB6, 0x51, 0xCA, 0x6F, 0xA7, 0x2E, 0x33, 0x82, 0xBD, 0x65, 0x58, 0xC0, 0xCB, 0x0D, //5//
+0x0D, 0xBD, 0x5D, 0x65, 0x92, 0x77, 0x0D, 0x50, 0xAC, 0x79, 0x40, 0x86, 0x34, 0x45, 0x8F, 0x42, 0x3E, //6//
+0x12, 0x8C, 0xA0, 0xAF, 0x07, 0xA1, 0xD3, 0x7D, 0x86, 0x5C, 0xA3, 0x2B, 0x80, 0x3F, 0x1D, 0x29, 0x2F, //7//
+0x68, 0x15, 0xD1, 0x34, 0xE4, 0x2F, 0xF6, 0xFC, 0xEF, 0xDA, 0x5C, 0x57, 0xCF, 0xB4, 0x5A, 0x90, 0x10, //8//
+0x7C, 0x49, 0xF2, 0x5A, 0xF3, 0x34, 0xBC, 0x88, 0x3A, 0x91, 0xB8, 0xC8, 0xAD, 0x03, 0x01, 0x77, 0x01, //9//
+0xB0, 0x91, 0xBA, 0x23, 0x1E, 0x42, 0x72, 0xEB, 0xD2, 0xF8, 0x7E, 0x1E, 0xF7, 0x69, 0xE9, 0x0F, 0x32, //a//
+0xA6, 0x32, 0x9C, 0x9D, 0x49, 0xB5, 0x98, 0x4E, 0x49, 0x00, 0xC9, 0x7F, 0x92, 0xC0, 0x2E, 0x3D, 0x23, //b//
+0x54, 0x2A, 0x25, 0x07, 0x6C, 0x6C, 0xAA, 0xB2, 0xB1, 0x45, 0x27, 0xD4, 0x76, 0x27, 0xBC, 0xE8, 0x14, //c//
+0xD9, 0xD0, 0xCE, 0x8C, 0x25, 0x98, 0xEB, 0x66, 0x74, 0x62, 0xE6, 0xA0, 0xEE, 0xFD, 0xF4, 0x86, 0x05, //d//
+0xF5, 0x7E, 0x1F, 0xEB, 0x3F, 0xFD, 0x34, 0x04, 0x50, 0x16, 0x3D, 0x61, 0x29, 0xDC, 0xA7, 0xB5, 0x36, //e//
+0x4E, 0x56, 0x39, 0xF1, 0xAA, 0x80, 0x21, 0x15, 0x9D, 0xAE, 0x94, 0x45, 0xBB, 0x72, 0x76, 0x11, 0x27 //f//
+};
+
+#endif
+
+
+UCHAR Vtab[] = {
+
+ // This table is for user AndyHe on Yihsins3, object id = 9000095
+
+0xDC, 0x9D, 0xA3, 0x3C, 0x61, 0xCA, 0x98, 0xF2, 0xBB, 0xB5, 0xAE, 0x34, 0x04, 0x43, 0xB0, 0xC9, 0x1B,
+0x3F, 0xF0, 0xCF, 0x12, 0x36, 0x49, 0xC1, 0x29, 0x93, 0x4E, 0x9A, 0x58, 0x52, 0xC8, 0x3D, 0x13, 0x0A,
+0xC9, 0xBC, 0x9A, 0x26, 0x58, 0x7F, 0x1F, 0x4D, 0xD0, 0xA4, 0x77, 0xB7, 0xB8, 0x39, 0x72, 0xFB, 0x39,
+0x7E, 0xDB, 0x66, 0x08, 0xDC, 0x51, 0x3A, 0x81, 0xFD, 0x8B, 0x2B, 0xC5, 0x8F, 0x8C, 0x49, 0x9D, 0x28,
+0x4D, 0xA9, 0x25, 0xB4, 0xBB, 0x18, 0xF3, 0x3A, 0x86, 0x3D, 0x06, 0x23, 0x77, 0xAA, 0x1E, 0xD8, 0x1F,
+0xA5, 0x43, 0xBC, 0x91, 0xC9, 0x0B, 0x79, 0xBF, 0xCF, 0xCC, 0x58, 0x00, 0xE5, 0x1F, 0xC1, 0x81, 0x0E,
+0x83, 0x51, 0x01, 0x73, 0xEF, 0x27, 0x66, 0x13, 0x6C, 0x6F, 0xE4, 0x7F, 0xC9, 0x2D, 0x04, 0xBE, 0x3D,
+0x21, 0xCF, 0x82, 0xF7, 0x93, 0xA6, 0x47, 0x76, 0x19, 0x06, 0x85, 0x4D, 0x4D, 0xFB, 0x23, 0x4F, 0x2C,
+0x54, 0x3A, 0xFE, 0xED, 0x72, 0x9D, 0xBB, 0x65, 0x45, 0xE3, 0x3D, 0x61, 0x1B, 0x04, 0x6A, 0x37, 0x13,
+0x18, 0x08, 0x47, 0x5E, 0x4A, 0xF0, 0xD0, 0x9E, 0x77, 0x9A, 0x69, 0x92, 0x31, 0x55, 0xD5, 0x0C, 0x02,
+0xE0, 0x87, 0x3B, 0x85, 0x04, 0xB4, 0x55, 0xAB, 0x58, 0x22, 0xFF, 0xAB, 0xF0, 0x76, 0xF6, 0x74, 0x31,
+0xF2, 0x15, 0xD9, 0xD9, 0xA0, 0xD3, 0x02, 0x0C, 0x01, 0xF0, 0xB1, 0xFE, 0x63, 0xD2, 0xEF, 0xE6, 0x20,
+0x9B, 0x76, 0x70, 0x4B, 0x87, 0x65, 0xE4, 0xC8, 0x22, 0x59, 0xD0, 0x19, 0x9A, 0xB7, 0x88, 0x60, 0x17,
+0xB7, 0xE4, 0xE4, 0xA0, 0x25, 0x3C, 0x8D, 0x50, 0xAE, 0x17, 0xC2, 0xEC, 0xAE, 0x90, 0xA7, 0x52, 0x06,
+0x06, 0x2E, 0x18, 0x6F, 0xFD, 0x82, 0x2C, 0xE7, 0xE4, 0x78, 0x4C, 0xDA, 0xDC, 0xE1, 0x9C, 0xA5, 0x35,
+0x6A, 0x62, 0x5D, 0xCA, 0x1E, 0xEE, 0xAE, 0xD4, 0x3A, 0xD1, 0x13, 0x86, 0x26, 0x6E, 0x5B, 0x2A, 0x24
+};
+
+UCHAR Ch = Value & Mask;
+int x;
+
+//
+// Scan down the column of Vtab looking for a nibble that matches Ch
+// return the index in the nibble indicated by Mask.
+//
+
+for ( x = 0 ; x < 16 ; x++) {
+
+ if ( (Vtab[ (x*17) + index] & Mask) == Ch ) {
+
+ x |= x << 4; // Copy into both nibbles
+ return( x & Mask );
+
+ }
+}
+
+printf(" Not found Value %x, Mask %x, Index %x\n", Value, Mask, index);
+return( 0 );
+
+}
+
+BOOLEAN
+OpenServer(
+ PHANDLE Handle,
+ PWCH ServerName
+ )
+{
+ NTSTATUS status;
+ OBJECT_ATTRIBUTES objectAttributes;
+ IO_STATUS_BLOCK ioStatusBlock;
+ WCHAR ServerNameBuffer[100];
+ UNICODE_STRING ServerNameString;
+
+ ServerNameString.Buffer = ServerNameBuffer;
+ ServerNameString.Length = 0;
+ ServerNameString.MaximumLength = 100;
+
+ status = RtlAppendUnicodeToString( &ServerNameString, DD_NWFS_DEVICE_NAME_U );
+ ASSERT( status == STATUS_SUCCESS );
+ status = RtlAppendUnicodeToString( &ServerNameString, L"\\" );
+ ASSERT( status == STATUS_SUCCESS );
+ status = RtlAppendUnicodeToString( &ServerNameString, ServerName );
+ ASSERT( status == STATUS_SUCCESS );
+
+ //
+ // Open the file
+ //
+
+ InitializeObjectAttributes(
+ &objectAttributes,
+ &ServerNameString,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ status = NtOpenFile (
+ Handle,
+ FILE_GENERIC_READ | SYNCHRONIZE,
+ &objectAttributes,
+ &ioStatusBlock,
+ FILE_SHARE_WRITE | FILE_SHARE_READ,
+ 0L
+ );
+
+ if (!NT_SUCCESS(status) ) {
+ printf( "Open status = %x for file %Z\n", status, &ServerName );
+ }
+
+ return ( (BOOLEAN) NT_SUCCESS( status ) );
+}
+
+
+CHAR Buffer1[100];
+CHAR Buffer2[100];
+
+SendMessage(
+ IN char* Format,
+ ...
+ )
+{
+ NTSTATUS status = -1;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ va_list Arguments;
+ NTSTATUS Status;
+ ULONG ReceiveBufferSize = 100;
+ ULONG SendBufferSize = 100;
+
+ va_start( Arguments, Format );
+
+ Buffer1[0] = Pid;
+
+ Status = FormatRequest( &Buffer1[1], &SendBufferSize, Format, Arguments );
+ if ( !NT_SUCCESS( Status ) ) {
+ return( Status );
+ }
+
+ //printf("Sending message\n" );
+ //dump( Buffer1, SendBufferSize + 1);
+
+ status = NtFsControlFile(
+ ServerHandle,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ NWR_ANY_NCP(0x17), // packet already contains the code necessary
+ Buffer1 + 1,
+ SendBufferSize,
+ Buffer2,
+ ReceiveBufferSize
+ );
+ if ( NT_SUCCESS( status ) ) {
+
+ status = NtWaitForSingleObject( ServerHandle, FALSE, NULL );
+ if ( NT_SUCCESS( status )) {
+ status = IoStatusBlock.Status;
+ }
+ }
+
+
+ if ( !NT_SUCCESS( status ) ) {
+ printf("SendPacket returns %08lx\n", status );
+ } else {
+ printf("Send succeeded.\n" );
+ //dump( Buffer2, IoStatusBlock.Information );
+ }
+ return( status );
+}
+
+
+NTSTATUS
+FormatRequest(
+ PCHAR SendBuffer,
+ PULONG SendBufferLength,
+ char* Format,
+ va_list a
+ )
+/*++
+
+Routine Description:
+
+ Send the packet described by Format and the additional parameters.
+
+Arguments:
+
+ Format - the information needed to create the request to the
+ server. The first byte indicates the packet type and the
+ following bytes contain field types.
+
+ Packet types:
+
+ 'A' SAP broadcast ( void )
+ 'C' NCP connect ( void )
+ 'F' NCP function ( byte )
+ 'S' NCP subfunction ( byte, byte )
+ 'D' NCP disconnect ( void )
+
+ Field types, request/response:
+
+ 'b' byte ( byte / byte* )
+ 'w' hi-lo word ( word / word* )
+ 'd' hi-lo dword ( dword / dword* )
+ '-' zero/skip byte ( void )
+ '=' zero/skip word ( void )
+ ._. zero/skip string ( word )
+ 'p' pstring ( char* )
+ 'u' p unicode string ( UNICODE_STRING * )
+ 'c' cstring ( char* )
+ 'r' raw bytes ( byte*, word )
+ 'u' p unicode string ( UNICODE_STRING * )
+ 'U' p uppercase string( UNICODE_STRING * )
+ 'f' separate fragment ( PMDL )
+
+ An 'f' field must be last, and in a response it cannot be
+ preceeded by 'p' or 'c' fields.
+
+
+Return Value:
+
+ Normally returns STATUS_PENDING.
+
+--*/
+{
+ NTSTATUS status;
+ char* pFormatCharacter;
+ USHORT t = 1;
+ ULONG data_size;
+ ULONG length;
+
+ data_size = 1;
+
+ SendBuffer[ 0 ] = va_arg( a, UCHAR );
+
+ if ( *Format == 'S' ) {
+ data_size += 2;
+ SendBuffer[data_size++] = va_arg( a, UCHAR );
+ }
+
+ pFormatCharacter = Format;
+
+ while ( *++pFormatCharacter && *pFormatCharacter != 'f' )
+ {
+ switch ( *pFormatCharacter ) {
+
+ case '=':
+ SendBuffer[data_size++] = 0;
+ case '-':
+ SendBuffer[data_size++] = 0;
+ break;
+
+ case '_':
+ length = va_arg ( a, USHORT );
+
+ if ( data_size + length > *SendBufferLength ) {
+ printf("***exch:!0!\n");
+ return( FALSE );
+ }
+
+ while ( length-- ) {
+ SendBuffer[data_size++] = 0;
+ }
+
+ break;
+
+ case 'b':
+ SendBuffer[data_size++] = va_arg ( a, UCHAR );
+ break;
+
+ case 'w':
+ {
+ USHORT w = va_arg ( a, USHORT );
+
+ SendBuffer[data_size++] = (UCHAR) (w >> 8);
+ SendBuffer[data_size++] = (UCHAR) (w >> 0);
+ break;
+ }
+
+ case 'd':
+ {
+ ULONG d = va_arg ( a, ULONG );
+
+ SendBuffer[data_size++] = (UCHAR) (d >> 24);
+ SendBuffer[data_size++] = (UCHAR) (d >> 16);
+ SendBuffer[data_size++] = (UCHAR) (d >> 8);
+ SendBuffer[data_size++] = (UCHAR) (d >> 0);
+ break;
+ }
+
+ case 'c':
+ {
+ char* c = va_arg ( a, char* );
+
+ length = strlen( c );
+
+ if ( data_size + length > *SendBufferLength ) {
+ printf("***exch:!1!\n");
+ return( FALSE );
+ }
+
+ RtlCopyMemory( &SendBuffer[data_size], c, length + 1 );
+ data_size += length + 1;
+ break;
+ }
+
+ case 'p':
+ {
+ char* c = va_arg ( a, char* );
+
+ length = strlen( c );
+
+ if ( data_size + length > *SendBufferLength ) {
+ printf("***exch:!2!\n");
+ return FALSE;
+ }
+
+ SendBuffer[data_size++] = (UCHAR)length;
+ RtlCopyMemory( &SendBuffer[data_size], c, length );
+ data_size += length;
+ break;
+ }
+
+ case 'u':
+ {
+ PUNICODE_STRING pUString = va_arg ( a, PUNICODE_STRING );
+ OEM_STRING OemString;
+
+ //
+ // Calculate required string length, excluding trailing NUL.
+ //
+
+ length = (USHORT)RtlUnicodeStringToOemSize( pUString ) - 1;
+ ASSERT( length < 0x100 );
+
+ if ( data_size + length > *SendBufferLength ) {
+ printf("***exch:!4!\n");
+ return( FALSE );
+ }
+
+ SendBuffer[data_size++] = (UCHAR)length;
+ OemString.Buffer = &SendBuffer[data_size];
+ OemString.MaximumLength = (USHORT)length + 1;
+ status = RtlUnicodeStringToCountedOemString( &OemString, pUString, FALSE );
+ ASSERT( NT_SUCCESS( status ));
+ data_size += (USHORT)length;
+ break;
+ }
+
+ case 'U':
+ {
+ USHORT i;
+
+ //
+ // UPPERCASE the string, copy it from unicode to the packet
+ //
+
+ PUNICODE_STRING pUString = va_arg ( a, PUNICODE_STRING );
+ UNICODE_STRING UUppercaseString;
+ OEM_STRING OemString;
+
+ if ( pUString->Length > 0 ) {
+
+ RtlUpcaseUnicodeString( &UUppercaseString, pUString, TRUE );
+
+ //
+ // Change all '\' to '/'
+ //
+
+ for ( i = 0 ; i < UUppercaseString.Length ; i++ ) {
+ if ( UUppercaseString.Buffer[i] == L'\\' ) {
+ UUppercaseString.Buffer[i] = L'/';
+ }
+ }
+
+ //
+ // Calculate required string length, excluding trailing NUL.
+ //
+
+ length = (USHORT)RtlUnicodeStringToOemSize( &UUppercaseString ) - 1;
+ ASSERT( length < 0x100 );
+
+ } else {
+ UUppercaseString = *pUString;
+ length = 0;
+ }
+
+ if ( data_size + length > *SendBufferLength ) {
+ printf("***exch:!5!\n");
+ return( FALSE );
+ }
+
+ SendBuffer[data_size++] = (UCHAR)length;
+ OemString.Buffer = &SendBuffer[data_size];
+ OemString.MaximumLength = (USHORT)length + 1;
+ status = RtlUnicodeStringToCountedOemString( &OemString,
+ &UUppercaseString,
+ FALSE );
+ ASSERT( NT_SUCCESS( status ));
+
+ if ( pUString->Length > 0 ) {
+ RtlFreeUnicodeString( &UUppercaseString );
+ }
+
+ data_size += (USHORT)length;
+ break;
+ }
+
+ case 'r':
+ {
+ UCHAR* b = va_arg ( a, UCHAR* );
+ length = va_arg ( a, USHORT );
+
+ if ( data_size + length > *SendBufferLength ) {
+ printf("***exch:!6!\n");
+ return( FALSE );
+ }
+
+ RtlCopyMemory( &SendBuffer[data_size], b, length );
+ data_size += length;
+ break;
+ }
+
+ default:
+ printf ( "*****exchange: invalid request field, %x\n", *pFormatCharacter);
+ return( FALSE );
+ }
+
+ if ( data_size > *SendBufferLength ) {
+ printf( "*****exchange: CORRUPT, too much request data\n" );
+ va_end( a );
+ return FALSE;
+ }
+ }
+
+ if ( *Format == 'S' )
+ {
+ SendBuffer[1] = (UCHAR)((data_size-3) >> 8);
+ SendBuffer[2] = (UCHAR)(data_size-3);
+ }
+
+ va_end( a );
+
+ *SendBufferLength = data_size;
+ return TRUE;
+}
+
+NTSTATUS
+_cdecl
+ParseResponse(
+ char* FormatString,
+ ... // format specific parameters
+ )
+/*++
+
+Routine Description:
+
+ This routine parse an NCP response.
+
+Arguments:
+
+ FormatString - supplies the information needed to create the request to the
+ server. The first byte indicates the packet type and the
+ following bytes contain field types.
+
+ Field types, request/response:
+
+ 'b' byte ( byte* )
+ 'w' hi-lo word ( word* )
+ 'd' hi-lo dword ( dword* )
+ '-' zero/skip byte ( void )
+ '=' zero/skip word ( void )
+ ._. zero/skip string ( word )
+ 'p' pstring ( char* )
+ 'c' cstring ( char* )
+ 'r' raw bytes ( byte*, word )
+ 'R' ASCIIZ to Unicode ( UNICODE_STRING *, word )
+
+Return Value:
+
+ STATUS - The converted error code from the NCP response.
+
+--*/
+
+{
+ PCHAR FormatByte;
+ va_list Arguments;
+ int Length = 0;
+
+ va_start( Arguments, FormatString );
+
+ FormatByte = FormatString;
+ while ( *FormatByte ) {
+
+ switch ( *FormatByte ) {
+
+ case '-':
+ Length += 1;
+ break;
+
+ case '=':
+ Length += 2;
+ break;
+
+ case '_':
+ {
+ USHORT l = va_arg ( Arguments, USHORT );
+ Length += l;
+ break;
+ }
+
+ case 'b':
+ {
+ UCHAR* b = va_arg ( Arguments, UCHAR* );
+ *b = Buffer2[Length++];
+ break;
+ }
+
+ case 'w':
+ {
+ UCHAR* b = va_arg ( Arguments, UCHAR* );
+ b[1] = Buffer2[Length++];
+ b[0] = Buffer2[Length++];
+ break;
+ }
+
+ case 'd':
+ {
+ UCHAR* b = va_arg ( Arguments, UCHAR* );
+ b[3] = Buffer2[Length++];
+ b[2] = Buffer2[Length++];
+ b[1] = Buffer2[Length++];
+ b[0] = Buffer2[Length++];
+ break;
+ }
+
+ case 'c':
+ {
+ char* c = va_arg ( Arguments, char* );
+ USHORT l = strlen( &Buffer2[Length] );
+ memcpy ( c, &Buffer2[Length], l+1 );
+ Length += l+1;
+ break;
+ }
+
+ case 'p':
+ {
+ char* c = va_arg ( Arguments, char* );
+ UCHAR l = Buffer2[Length++];
+ memcpy ( c, &Buffer2[Length], l );
+ c[l+1] = 0;
+ break;
+ }
+
+#if 0
+ case 'P':
+ {
+ PUNICODE_STRING pUString = va_arg ( Arguments, PUNICODE_STRING );
+ OEM_STRING OemString;
+
+ OemString.Length = Buffer2[Length++];
+ OemString.Buffer = &Buffer2[Length];
+
+ Status = RtlOemStringToCountedUnicodeString( pUString, &OemString, FALSE );
+ ASSERT( NT_SUCCESS( Status ));
+
+ break;
+ }
+#endif
+
+ case 'r':
+ {
+ UCHAR* b = va_arg ( Arguments, UCHAR* );
+ USHORT l = va_arg ( Arguments, USHORT );
+ RtlCopyMemory( b, &Buffer2[Length], l );
+ Length += l;
+ break;
+ }
+
+#if 0
+ case 'R':
+ {
+ //
+ // Interpret the buffer as an ASCIIZ string. Convert
+ // it to unicode in the preallocated buffer.
+ //
+
+ PUNICODE_STRING pUString = va_arg ( Arguments, PUNICODE_STRING );
+ OEM_STRING OemString;
+ USHORT len = va_arg ( Arguments, USHORT );
+
+ OemString.Buffer = &Buffer2[Length];
+ OemString.Length = strlen( OemString.Buffer );
+ OemString.MaximumLength = OemString.Length;
+
+ Status = RtlOemStringToCountedUnicodeString( pUString, &OemString, FALSE );
+ ASSERT( NT_SUCCESS( Status ));
+ Length += len;
+ break;
+ }
+#endif
+
+ default:
+ printf ( "*****exchange: invalid response field, %x\n", *FormatByte );
+ return( FALSE );
+ }
+
+#if 0
+ if ( Length > PacketLength )
+ {
+ printf ( "*****exchange: not enough response data, %d\n", i );
+ }
+#endif
+ FormatByte++;
+ }
+
+ va_end( Arguments );
+}
+
+
+// Hex dump
+
+VOID
+dump(
+ IN PVOID far_p,
+ IN ULONG len
+ )
+/*++
+
+Routine Description:
+ Dump Min(len, MaxDump) bytes in classic hex dump style.
+
+Arguments:
+
+ IN far_p - address of buffer to start dumping from.
+
+ IN len - length in bytes of buffer.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ ULONG l;
+ char s[80], t[80];
+ PCHAR far_pchar = (PCHAR)far_p;
+
+ if (len > MaxDump)
+ len = MaxDump;
+
+ while (len) {
+ l = len < 17 ? len : 17;
+
+ // printf("\n%lx ", far_pchar);
+ HexDumpLine (far_pchar, l, s, t, 0);
+ // printf("%s%.*s%s", s, 1 + ((16 - l) * 3), "", t);
+ printf("%s", s);
+
+ len -= l;
+ far_pchar += l;
+ }
+ printf("\n");
+}
+
+VOID
+HexDumpLine (
+ PCHAR pch,
+ ULONG len,
+ PCHAR s,
+ PCHAR t,
+ USHORT flag
+ )
+{
+ static UCHAR rghex[] = "0123456789ABCDEF";
+
+ UCHAR c;
+ UCHAR *hex, *asc;
+
+
+ hex = s;
+ asc = t;
+
+ *(asc++) = '*';
+ while (len--) {
+ c = *(pch++);
+ *(hex++) = rghex [c >> 4] ;
+ *(hex++) = rghex [c & 0x0F];
+ *(hex++) = ' ';
+ *(asc++) = (c < ' ' || c > '~') ? (CHAR )'.' : c;
+ }
+ *(asc++) = '*';
+ *asc = 0;
+ *hex = 0;
+
+ flag;
+}
+
+// End Hex dump
+
+UCHAR Table[] =
+{0x7,0x8,0x0,0x8,0x6,0x4,0xE,0x4,0x5,0xC,0x1,0x7,0xB,0xF,0xA,0x8,
+ 0xF,0x8,0xC,0xC,0x9,0x4,0x1,0xE,0x4,0x6,0x2,0x4,0x0,0xA,0xB,0x9,
+ 0x2,0xF,0xB,0x1,0xD,0x2,0x1,0x9,0x5,0xE,0x7,0x0,0x0,0x2,0x6,0x6,
+ 0x0,0x7,0x3,0x8,0x2,0x9,0x3,0xF,0x7,0xF,0xC,0xF,0x6,0x4,0xA,0x0,
+ 0x2,0x3,0xA,0xB,0xD,0x8,0x3,0xA,0x1,0x7,0xC,0xF,0x1,0x8,0x9,0xD,
+ 0x9,0x1,0x9,0x4,0xE,0x4,0xC,0x5,0x5,0xC,0x8,0xB,0x2,0x3,0x9,0xE,
+ 0x7,0x7,0x6,0x9,0xE,0xF,0xC,0x8,0xD,0x1,0xA,0x6,0xE,0xD,0x0,0x7,
+ 0x7,0xA,0x0,0x1,0xF,0x5,0x4,0xB,0x7,0xB,0xE,0xC,0x9,0x5,0xD,0x1,
+ 0xB,0xD,0x1,0x3,0x5,0xD,0xE,0x6,0x3,0x0,0xB,0xB,0xF,0x3,0x6,0x4,
+ 0x9,0xD,0xA,0x3,0x1,0x4,0x9,0x4,0x8,0x3,0xB,0xE,0x5,0x0,0x5,0x2,
+ 0xC,0xB,0xD,0x5,0xD,0x5,0xD,0x2,0xD,0x9,0xA,0xC,0xA,0x0,0xB,0x3,
+ 0x5,0x3,0x6,0x9,0x5,0x1,0xE,0xE,0x0,0xE,0x8,0x2,0xD,0x2,0x2,0x0,
+ 0x4,0xF,0x8,0x5,0x9,0x6,0x8,0x6,0xB,0xA,0xB,0xF,0x0,0x7,0x2,0x8,
+ 0xC,0x7,0x3,0xA,0x1,0x4,0x2,0x5,0xF,0x7,0xA,0xC,0xE,0x5,0x9,0x3,
+ 0xE,0x7,0x1,0x2,0xE,0x1,0xF,0x4,0xA,0x6,0xC,0x6,0xF,0x4,0x3,0x0,
+ 0xC,0x0,0x3,0x6,0xF,0x8,0x7,0xB,0x2,0xD,0xC,0x6,0xA,0xA,0x8,0xD};
+
+UCHAR Keys[32] =
+{0x48,0x93,0x46,0x67,0x98,0x3D,0xE6,0x8D,
+ 0xB7,0x10,0x7A,0x26,0x5A,0xB9,0xB1,0x35,
+ 0x6B,0x0F,0xD5,0x70,0xAE,0xFB,0xAD,0x11,
+ 0xF4,0x47,0xDC,0xA7,0xEC,0xCF,0x50,0xC0};
+
+#define XorArray( DEST, SRC ) { \
+ PULONG D = (PULONG)DEST; \
+ PULONG S = (PULONG)SRC; \
+ int i; \
+ for ( i = 0; i <= 7 ; i++ ) { \
+ D[i] ^= S[i]; \
+ } \
+}
+
+VOID
+RespondToChallenge(
+ IN PUCHAR achObjectId,
+ IN POEM_STRING Password,
+ IN PUCHAR pChallenge,
+ OUT PUCHAR pResponse
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes the ObjectId and Challenge key from the server and
+ encrypts the user supplied password to develop a credential for the
+ server to verify.
+
+Arguments:
+ IN PUCHAR achObjectId - Supplies the 4 byte user's bindery object id
+ IN POEM_STRING Password - Supplies the user's uppercased password
+ IN PUCHAR pChallenge - Supplies the 8 byte challenge key
+ OUT PUCHAR pResponse - Returns the 8 byte response
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ int index;
+ UCHAR achK[32];
+ UCHAR achBuf[32];
+
+ Shuffle(achObjectId, Password->Buffer, Password->Length, achBuf);
+ Shuffle( &pChallenge[0], achBuf, 16, &achK[0] );
+ Shuffle( &pChallenge[4], achBuf, 16, &achK[16] );
+
+ for (index = 0; index < 16; index++)
+ achK[index] ^= achK[31-index];
+
+ for (index = 0; index < 8; index++)
+ pResponse[index] = achK[index] ^ achK[15-index];
+}
+
+VOID
+RespondToChallengePart1(
+ IN PUCHAR achObjectId,
+ IN POEM_STRING Password,
+ OUT PUCHAR pResponse
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes the ObjectId and Challenge key from the server and
+ encrypts the user supplied password to develop a credential for the
+ server to verify.
+
+Arguments:
+ IN PUCHAR achObjectId - Supplies the 4 byte user's bindery object id
+ IN POEM_STRING Password - Supplies the user's uppercased password
+ IN PUCHAR pChallenge - Supplies the 8 byte challenge key
+ OUT PUCHAR pResponse - Returns the 16 byte response held by the server
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ UCHAR achBuf[32];
+
+ Shuffle(achObjectId, Password->Buffer, Password->Length, achBuf);
+ memmove(pResponse, achBuf, 16);
+}
+
+VOID
+RespondToChallengePart2(
+ IN PUCHAR pResponsePart1,
+ IN PUCHAR pChallenge,
+ OUT PUCHAR pResponse
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes the result of Shuffling the ObjectId and the Password
+ and processes it with a challenge key.
+
+Arguments:
+ IN PUCHAR pResponsePart1 - Supplies the 16 byte output of
+ RespondToChallengePart1.
+ IN PUCHAR pChallenge - Supplies the 8 byte challenge key
+ OUT PUCHAR pResponse - Returns the 8 byte response
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ int index;
+ UCHAR achK[32];
+
+ Shuffle( &pChallenge[0], pResponsePart1, 16, &achK[0] );
+ Shuffle( &pChallenge[4], pResponsePart1, 16, &achK[16] );
+
+ for (index = 0; index < 16; index++)
+ achK[index] ^= achK[31-index];
+
+ for (index = 0; index < 8; index++)
+ pResponse[index] = achK[index] ^ achK[15-index];
+}
+
+VOID
+Shuffle(
+ UCHAR *achObjectId,
+ UCHAR *szUpperPassword,
+ int iPasswordLen,
+ UCHAR *achOutputBuffer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine shuffles around the object ID with the password
+
+Arguments:
+
+ IN achObjectId - Supplies the 4 byte user's bindery object id
+
+ IN szUpperPassword - Supplies the user's uppercased password on the
+ first call to process the password. On the second and third calls
+ this parameter contains the OutputBuffer from the first call
+
+ IN iPasswordLen - length of uppercased password
+
+ OUT achOutputBuffer - Returns the 8 byte sub-calculation
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ int iTempIndex;
+ int iOutputIndex;
+ UCHAR achTemp[32];
+
+ //
+ // Initialize the achTemp buffer. Initialization consists of taking
+ // the password and dividing it up into chunks of 32. Any bytes left
+ // over are the remainder and do not go into the initialization.
+ //
+ // achTemp[0] = szUpperPassword[0] ^ szUpperPassword[32] ^ szUpper...
+ // achTemp[1] = szUpperPassword[1] ^ szUpperPassword[33] ^ szUpper...
+ // etc.
+ //
+
+ if ( iPasswordLen > 32) {
+
+ // At least one chunk of 32. Set the buffer to the first chunk.
+
+ RtlCopyMemory( achTemp, szUpperPassword, 32 );
+
+ szUpperPassword +=32; // Remove the first chunk
+ iPasswordLen -=32;
+
+ while ( iPasswordLen >= 32 ) {
+ //
+ // Xor this chunk with the characters already loaded into
+ // achTemp.
+ //
+
+ XorArray( achTemp, szUpperPassword);
+
+ szUpperPassword +=32; // Remove this chunk
+ iPasswordLen -=32;
+ }
+
+ } else {
+
+ // No chunks of 32 so set the buffer to zero's
+
+ RtlZeroMemory( achTemp, sizeof(achTemp));
+
+ }
+
+ //
+ // achTemp is now initialized. Load the remainder into achTemp.
+ // The remainder is repeated to fill achTemp.
+ //
+ // The corresponding character from Keys is taken to seperate
+ // each repitition.
+ //
+ // As an example, take the remainder "ABCDEFG". The remainder is expanded
+ // to "ABCDEFGwABCDEFGxABCDEFGyABCDEFGz" where w is Keys[7],
+ // x is Keys[15], y is Keys[23] and z is Keys[31].
+ //
+ //
+
+ if (iPasswordLen > 0) {
+ int iPasswordOffset = 0;
+ for (iTempIndex = 0; iTempIndex < 32; iTempIndex++) {
+
+ if (iPasswordLen == iPasswordOffset) {
+ iPasswordOffset = 0;
+ achTemp[iTempIndex] ^= Keys[iTempIndex];
+ } else {
+ achTemp[iTempIndex] ^= szUpperPassword[iPasswordOffset++];
+ }
+ }
+ }
+
+ //
+ // achTemp has been loaded with the users password packed into 32
+ // bytes. Now take the objectid that came from the server and use
+ // that to munge every byte in achTemp.
+ //
+
+ for (iTempIndex = 0; iTempIndex < 32; iTempIndex++)
+ achTemp[iTempIndex] ^= achObjectId[ iTempIndex & 3];
+
+ Scramble( Scramble( 0, achTemp ), achTemp );
+
+ //
+ // Finally take pairs of bytes in achTemp and return the two
+ // nibbles obtained from Table. The pairs of bytes used
+ // are achTemp[n] and achTemp[n+16].
+ //
+
+ for (iOutputIndex = 0; iOutputIndex < 16; iOutputIndex++) {
+
+ achOutputBuffer[iOutputIndex] =
+ Table[achTemp[iOutputIndex << 1]] |
+ (Table[achTemp[(iOutputIndex << 1) + 1]] << 4);
+ }
+
+ return;
+}
+
+int
+Scramble(
+ int iSeed,
+ UCHAR achBuffer[32]
+ )
+
+/*++
+
+Routine Description:
+
+ This routine scrambles around the contents of the buffer. Each buffer
+ position is updated to include the contents of at least two character
+ positions plus an EncryptKey value. The buffer is processed left to right
+ and so if a character position chooses to merge with a buffer position
+ to its left then this buffer position will include bits derived from at
+ least 3 bytes of the original buffer contents.
+
+Arguments:
+
+ IN iSeed
+ IN OUT achBuffer[32]
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ int iBufferIndex;
+
+ for (iBufferIndex = 0; iBufferIndex < 32; iBufferIndex++) {
+ achBuffer[iBufferIndex] =
+ (UCHAR)(
+ ((UCHAR)(achBuffer[iBufferIndex] + iSeed)) ^
+ ((UCHAR)( achBuffer[(iBufferIndex+iSeed) & 31] -
+ Keys[iBufferIndex] )));
+
+ iSeed += achBuffer[iBufferIndex];
+ }
+ return iSeed;
+}
+
+#if 0
+
+// this data was gathered for object id = 9000095 from yihsins3 with
+// this program by reseting the password to null manually and then
+// sending over the specified Vc
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+Vnew= DC 9D A3 3C 61 CA 98 F2 BB B5 AE 34 04 43 B0 C9 1B
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11
+Vnew= 3F F0 CF 12 36 49 C1 29 93 4E 9A 58 52 C8 3D 13 0A
+
+File= 3F F0 CF 12 36 49 C1 29 93 4E 9A 58 52 C8 3D 13 0A
+ Vc= 22 22 22 22 22 22 22 22 22 22 22 22 22 22 22 22 22
+Vnew= C9 BC 9A 26 58 7F 1F 4D D0 A4 77 B7 B8 39 72 FB 39
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33
+Vnew= 7E DB 66 08 DC 51 3A 81 FD 8B 2B C5 8F 8C 49 9D 28
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44
+Vnew= 4D A9 25 B4 BB 18 F3 3A 86 3D 06 23 77 AA 1E D8 1F
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55
+Vnew= A5 43 BC 91 C9 0B 79 BF CF CC 58 00 E5 1F C1 81 0E
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= 66 66 66 66 66 66 66 66 66 66 66 66 66 66 66 66 66
+Vnew= 83 51 01 73 EF 27 66 13 6C 6F E4 7F C9 2D 04 BE 3D
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77
+Vnew= 21 CF 82 F7 93 A6 47 76 19 06 85 4D 4D FB 23 4F 2C
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88
+Vnew= 54 3A FE ED 72 9D BB 65 45 E3 3D 61 1B 04 6A 37 13
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99
+Vnew= 18 08 47 5E 4A F0 D0 9E 77 9A 69 92 31 04 6A 37 02
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA
+Vnew= E0 87 3B 85 04 B4 55 AB 58 22 FF AB F0 76 F6 74 31
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= BB BB BB BB BB BB BB BB BB BB BB BB BB BB BB BB BB
+Vnew= F2 15 D9 D9 A0 D3 02 0C 01 F0 B1 FE 63 D2 EF E6 20
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
+Vnew= 9B 76 70 4B 87 65 E4 C8 22 59 D0 19 9A B7 88 60 17
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD
+Vnew= B7 E4 E4 A0 25 3C 8D 50 AE 17 C2 EC AE 90 A7 52 06
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE
+Vnew= 06 2E 18 6F FD 82 2C E7 E4 78 4C DA DC E1 9C A5 35
+
+File= 06 2E 18 6F FD 82 2C E7 E4 78 4C DA DC E1 9C A5 35
+ Vc= FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
+Vnew= 6A 62 5D CA 1E EE AE D4 3A D1 13 86 26 6E 5B 2A 24
+
+
+0xEA, 0x91, 0x6C, 0x3D, 0xAB, 0x59, 0xB9, 0xBD, 0xB2, 0x7B, 0x4C, 0x , 0x , 0x , 0x , 0x , 0x , //0//
+0x97, 0xB6, 0xE6, 0x15, 0xF0, 0x43, 0x21, 0x63, 0x7B, 0xDF, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //1//
+0x7B, 0xEF, 0x47, 0x21, 0xD8, 0x6E, 0xEB, 0x10, 0xCC, 0xAA, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //2//
+0x16, 0x85, 0xA0, 0x06, 0x17, 0xDB, 0x34, 0x46, 0xF1, 0x48, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //3//
+0x48, 0x5D, 0x9D, 0xC4, 0x9A, 0x1A, 0x7C, 0x2F, 0x8E, 0x12, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //4//
+0x85, 0x6B, 0xF4, 0x9A, 0x2D, 0x3C, 0xAA, 0xD8, 0xA8, 0xC0, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //5//
+0xFE, 0xFC, 0x33, 0xE2, 0x01, 0xC7, 0x66, 0x87, 0x64, 0x67, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //6//
+0x3D, 0xCA, 0xC9, 0x67, 0x8C, 0x26, 0x57, 0x7E, 0x99, 0xED, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //7//
+0x69, 0xA9, 0x7E, 0xA3, 0xC2, 0xE4, 0xD0, 0x3C, 0x4A, 0x3E, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //8//
+0xC2, 0x04, 0x2B, 0x5B, 0x75, 0x81, 0x05, 0x91, 0x17, 0x9C, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //9//
+0x5F, 0x48, 0x02, 0xDF, 0xB9, 0x70, 0xF3, 0xA4, 0xDF, 0x29, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //A//
+0xDC, 0x23, 0x5A, 0x4C, 0x44, 0xA5, 0x88, 0x5A, 0x00, 0x03, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //B//
+0x20, 0x72, 0x15, 0xF0, 0x53, 0x0D, 0x1E, 0xCB, 0x56, 0x55, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //C//
+0x04, 0x30, 0xBF, 0xB8, 0x3E, 0xB8, 0x9D, 0xF2, 0x23, 0xF4, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //D//
+0xA3, 0xDE, 0xD8, 0x89, 0x6F, 0xFF, 0xF3, 0xE9, 0xED, 0x81, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //E//
+0xB1, 0x17, 0x81, 0x7E, 0xE6, 0x92, 0x42, 0x05, 0x35, 0xB6, 0x , 0x , 0x , 0x , 0x , 0x , 0x //F//
+ };
+ 00 = AE 34 04 43 B0 C9 1B
+ 11 = 9A 58 52 C8 3D 13 0A
+ 22 = 77 B7 B8 39 72 FB 39
+ 33 = 2B C5 8F 8C 49 9D 28
+ 44 = 06 23 77 AA 1E D8 1F
+ 55 = 58 00 E5 1F C1 81 0E
+ 66 = E4 7F C9 2D 04 BE 3D
+ 77 = 85 4D 4D FB 23 4F 2C
+ 88 = 3D 61 1B 04 6A 37 13
+ 99 = 69 92 31 55 D5 0C 02
+ AA = FF AB F0 76 F6 74 31
+ BB = B1 FE 63 D2 EF E6 20
+ CC = D0 19 9A B7 88 60 17
+ DD = C2 EC AE 90 A7 52 06
+ EE = 4C DA DC E1 9C A5 35
+ FF = 13 86 26 6E 5B 2A 24
+
+
+
+
+#endif
+
diff --git a/private/nw/test/tp2.c b/private/nw/test/tp2.c
new file mode 100644
index 000000000..004161df8
--- /dev/null
+++ b/private/nw/test/tp2.c
@@ -0,0 +1,1560 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ tex.c
+
+Abstract:
+
+ User mode test program for the Microsoft Netware redir file system.
+
+ This test program can be built from the command line using the
+ command 'nmake UMTEST=tex'.
+
+Author:
+
+ Manny Weiser (mannyw) 7-Jun-1993
+
+Revision History:
+
+--*/
+
+#include <conio.h>
+#include <stdio.h>
+#include <string.h>
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <ntddnwfs.h>
+#include <ntddnwp.h>
+#include <STDARG.H>
+
+#define OT_USER 1
+
+
+// Hex dump
+ULONG MaxDump = 256;
+
+VOID
+dump(
+ IN PVOID far_p,
+ IN ULONG len
+ );
+
+VOID
+HexDumpLine (
+ PCHAR pch,
+ ULONG len,
+ PCHAR s,
+ PCHAR t,
+ USHORT flag
+ );
+
+
+// End Hex dump
+
+VOID
+ResetPassword(
+ PUCHAR Vnew
+ );
+
+UCHAR
+LookUp(
+ UCHAR Value,
+ UCHAR Mask,
+ int index
+ );
+
+SendMessage(
+ IN char* Format,
+ ...
+ );
+
+NTSTATUS
+FormatRequest(
+ PCHAR SendBuffer,
+ PULONG SendBufferLength,
+ char* Format,
+ va_list a
+ );
+
+BOOLEAN
+OpenServer(
+ PHANDLE Handle,
+ PWCH ServerName
+ );
+
+NTSTATUS
+_cdecl
+ParseResponse(
+ char* FormatString,
+ ... // format specific parameters
+ );
+
+VOID
+Shuffle(
+ UCHAR *achObjectId,
+ UCHAR *szUpperPassword,
+ int iPasswordLen,
+ UCHAR *achOutputBuffer
+ );
+
+int
+Scramble(
+ int iSeed,
+ UCHAR achBuffer[32]
+ );
+
+VOID
+RespondToChallenge(
+ IN PUCHAR achObjectId,
+ IN POEM_STRING Password,
+ IN PUCHAR pChallenge,
+ OUT PUCHAR pResponse
+ );
+
+VOID
+RespondToChallengePart1(
+ IN PUCHAR achObjectId,
+ IN POEM_STRING Password,
+ OUT PUCHAR pResponse
+ );
+
+VOID
+RespondToChallengePart2(
+ IN PUCHAR pResponsePart1,
+ IN PUCHAR pChallenge,
+ OUT PUCHAR pResponse
+ );
+
+NTSTATUS
+GetCurrentPasswordValue (
+ IN OUT PCHAR OutputValue
+ );
+
+#define BUFFER_SIZE 200
+
+WCHAR *ServerName = L"NETWARE311";
+UCHAR Pid = 255;
+UCHAR Object[4];
+
+WCHAR FileNameBuffer[100];
+UNICODE_STRING FileName;
+
+
+HANDLE ServerHandle;
+_cdecl
+main(
+ int argc,
+ char *argv[],
+ )
+{
+ BOOLEAN success;
+ UCHAR Challenge[8];
+ UCHAR fileValue[17];
+ UCHAR ResponseP2[16];
+ int x, y;
+ NTSTATUS status;
+
+
+#if 0
+ UCHAR Vold[] = {
+ 0x31, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x31, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x22,
+ 0x32, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x11,
+ 0x32, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x23, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x11,
+ 0x23, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x22,
+ 0x54, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x11,
+ 0x54, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x22,
+ 0x75, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x11,
+ 0x75, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x22,
+ 0x76, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x11,
+ 0x76, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x22,
+ 0xF7, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x11,
+ 0xF7, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x22,
+ 0x98, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x11,
+ 0x98, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x22,
+ 0x89, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x88, 0x88, 0x11,
+ 0x89, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x88, 0x88, 0x22,
+ 0xba, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x11,
+ 0xba, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x22,
+ 0xab, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x11,
+ 0xab, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0x22,
+ 0xdc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x11,
+ 0xdc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x22,
+ 0xcd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0x11,
+ 0xcd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0x22,
+ 0xfe, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0x11,
+ 0xfe, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0x22,
+ 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x11,
+ 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x22,
+ 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11,
+ 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22 };
+
+
+ UCHAR Vc[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+ 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
+ 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
+ 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x88, 0x88, 0x88,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+#endif
+
+ UCHAR Vold[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x11,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x22,
+ 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x11,
+ 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x22,
+ 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x11,
+ 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x22,
+ 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x11,
+ 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x22,
+ 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x11,
+ 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x22,
+ 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x11,
+ 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x22,
+ 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x11,
+ 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x22,
+ 0xff, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x11,
+ 0xff, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x22,
+ 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x11,
+ 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x22,
+ 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0x11,
+ 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0x22,
+ 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0x11,
+ 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0x22,
+ 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0x11,
+ 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xff, 0xff, 0xff, 0x22,
+ 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x11,
+ 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x22,
+ 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xff, 0x11,
+ 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xff, 0x22,
+ 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0x11,
+ 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0x22,
+ 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xff, 0xff, 0x11,
+ 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0xff, 0xff, 0x22,
+ 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x11,
+ 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x22,
+ 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, 0x11,
+ 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, 0x22,
+ 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0x11,
+ 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0x22,
+ 0xff, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xfe, 0xff, 0xff, 0x11,
+ 0xff, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xfe, 0xff, 0xff, 0x22,
+ 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xff, 0x11,
+ 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xff, 0x22,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xff, 0x11,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0xff, 0x22,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0x11,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xff, 0xff, 0x22,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x11,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0x22,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xff, 0x11,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xff, 0x22,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0x11,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff, 0x22,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xff, 0x11,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0xff, 0x22,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x11,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x22,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0x11,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xfd, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0x22,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x11,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0x22,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0x11,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0x22,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0x11,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbf, 0x22 };
+
+ UCHAR Vc[] = {
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11 };
+
+
+ success = OpenServer( &ServerHandle, ServerName );
+ if ( !success) {
+ return 1;
+ }
+
+ printf("Opened server, %wS\n", ServerName );
+
+ //
+ // Setup the file name that we'll be opening every time.
+ //
+
+ FileName.Buffer = FileNameBuffer;
+ FileName.Length = 0;
+ FileName.MaximumLength = 100;
+
+ status = RtlAppendUnicodeToString( &FileName, DD_NWFS_DEVICE_NAME_U );
+ ASSERT( status == STATUS_SUCCESS );
+ status = RtlAppendUnicodeToString( &FileName, L"\\" );
+ status = RtlAppendUnicodeToString( &FileName, ServerName );
+ status = RtlAppendUnicodeToString( &FileName, L"\\SYS:\\SYSTEM\\NET$VAL.SYS" );
+
+ // Get objectid
+ SendMessage(
+ "Swp",
+ 0x17, 0x35,
+ OT_USER,
+ "SUPERVISOR");
+
+ ParseResponse("r", Object, 4 );
+
+ for ( x = 0 ; x < sizeof(Vold) ; x+= 17 ) {
+
+ for ( y = 0 ; y < sizeof(Vc) ; y+= 17 ) {
+
+ //
+ // Set the password on the server to be the value of Vold required for
+ // the second part of the test.
+ //
+
+ ResetPassword( Vold + x );
+
+ //
+ // We now know that Vold on the server is the same as the Vold vector.
+ // We can now set the password to any Vnew.
+ // We can do this without the real password because the
+ // server will believe we know the real password if we give it
+ // the result of taking Vold and the Challenge key and passing
+ // it through RespondToChallengePart2
+ //
+
+ printf( "Getting a challenge key.... " );
+
+ SendMessage(
+ "S",
+ 0x17, 0x17);
+
+ ParseResponse("r", Challenge, sizeof(Challenge) );
+
+ // Fabricate the number the server will use to validate the password change
+ RespondToChallengePart2( Vold + x, Challenge, ResponseP2 );
+
+ printf( "Vold= " ); dump( Vold + x, 17);
+
+ // Put out results to console...
+
+ status = GetCurrentPasswordValue( &fileValue[0] );
+
+ if ( NT_SUCCESS( status ) ) {
+
+ printf( "File= " );
+ dump( fileValue, 17);
+ }
+ printf( " Vc= "); dump( Vc + y, 17);
+
+ printf( "Setting the new password... " );
+
+ // Send the set password
+ SendMessage("Srwpr", 0x17, 0x4b,
+ ResponseP2, 8,
+ OT_USER,
+ "SUPERVISOR",
+ Vc + y, 17 );
+
+ status = GetCurrentPasswordValue( &fileValue[0] );
+
+ if ( NT_SUCCESS( status ) ) {
+
+ printf( "Vnew= ");
+ dump( fileValue, 17);
+ }
+
+ printf( "\n\n" );
+ }
+ }
+
+ printf( "%s exiting\n", argv[0]);
+ return 0;
+}
+
+NTSTATUS
+GetCurrentPasswordValue (
+ IN OUT PCHAR OutputValue
+ )
+//
+// This routine reads the current password from the file.
+//
+{
+ HANDLE fileHandle;
+ LARGE_INTEGER filePosition;
+ OBJECT_ATTRIBUTES objectAttributes;
+ IO_STATUS_BLOCK ioStatusBlock;
+ int i;
+ PCHAR ch;
+ NTSTATUS status;
+
+ ch = OutputValue;
+ for (i = 0; i < 17; i++) {
+
+ *(ch++) = '0';
+ }
+
+ // Send a Close Bindery to the server
+
+ printf( "Closing the bindery... " );
+
+ SendMessage(
+ "S",
+ 0x17, 0x44);
+
+ //
+ // Open the server\sys:system\net$val.sys file which the server just
+ // closed.
+ //
+
+ InitializeObjectAttributes(
+ &objectAttributes,
+ &FileName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ status = NtOpenFile (
+ &fileHandle,
+ FILE_GENERIC_READ | SYNCHRONIZE,
+ &objectAttributes,
+ &ioStatusBlock,
+ FILE_SHARE_WRITE | FILE_SHARE_READ,
+ 0L
+ );
+
+ if (!NT_SUCCESS(status) ) {
+
+ printf( " Open failed. Status = 0x%x\n", status );
+
+ } else {
+
+ //
+ // Seek to the spot in the file that has the supervisor's password.
+ // This will be different for different servers. Here's a table :
+ //
+ // Netware311 : 0x097C
+ //
+
+ filePosition.HighPart = 0L;
+ filePosition.LowPart = 0x097C;
+
+ // Read the 17 bytes at that offset and close the file
+
+ status = NtReadFile (
+ fileHandle,
+ NULL,
+ NULL,
+ NULL,
+ &ioStatusBlock,
+ OutputValue,
+ 17,
+ &filePosition,
+ NULL
+ );
+
+ if (!NT_SUCCESS(status) ) {
+
+ printf( " Read failed. Status = 0x%x\n", status );
+ }
+
+ NtClose( fileHandle );
+ }
+
+ // Send an Open Bindery to the server
+
+ printf( "Opening the bindery... " );
+ SendMessage(
+ "S",
+ 0x17, 0x45);
+
+ return(status);
+}
+
+VOID
+ResetPassword(
+ PUCHAR Vnew
+ )
+{
+//
+// Note Vc[0] specifies the length. To derive new values of Vnew use the following
+// table. For each nibble desired in Vnew, scan the appropriate column and
+// read off the corresponding Vc nibble value on the left.
+//
+
+ UCHAR Vc[17];
+
+ UCHAR Challenge[8];
+ OEM_STRING Password = {0,0,""};
+ UCHAR EncryptKey[8];
+
+ int x;
+
+ //printf( "Get Login key for set password using NULL password\n");
+
+ printf( "Getting a challenge key.... " );
+
+ SendMessage(
+ "S",
+ 0x17, 0x17);
+
+ ParseResponse("r", Challenge, sizeof(Challenge) );
+
+ RespondToChallenge( Object, &Password, Challenge, EncryptKey );
+ // dump( EncryptKey, 8);
+
+ //
+ // Build Vc so that the server will get Vnew stored in the bindery.
+ // Note Vols/Vnew have the length at V[17] whereas Vc has it at Vc[0].
+ //
+
+ for ( x = 0 ; x < 16; x++ ) {
+ Vc[x+1] = LookUp( Vnew[x], 0xf0, x) | LookUp( Vnew[x], 0x0f, x);
+ }
+
+ Vc[0] = LookUp( Vnew[16], 0xf0, 16) | LookUp( Vnew[16], 0x0f, 16);
+
+ //printf( "Vnew "); dump( Vnew, 17);
+ //printf( "Vc "); dump( Vc, 17);
+
+ //
+ // This uses the null password, objectid=1 and our supervisor privilege
+ // to set it, regardless of the and our ability to figure out what
+ // Vnew will be created for a particular Vc.
+ //
+
+ printf( "Setting the old password... " );
+
+ SendMessage("Srwpr", 0x17, 0x4b,
+ EncryptKey, sizeof(EncryptKey),
+ OT_USER,
+ "SUPERVISOR",
+ Vc, 17 );
+
+ ParseResponse( "" );
+}
+
+UCHAR
+LookUp(
+ UCHAR Value,
+ UCHAR Mask,
+ int index
+ )
+{
+UCHAR Vtab[] = {
+
+0x97, 0xE3, 0x73, 0xD8, 0x70, 0x5B, 0x85, 0xD3, 0xF5, 0xCD, 0xDB, 0xFA, 0xDC, 0xEB, 0x98, 0x64, 0x18, //0//
+0x31, 0x6B, 0x88, 0x1E, 0x88, 0x09, 0x10, 0x29, 0x0B, 0x84, 0x61, 0x03, 0x43, 0xA1, 0xD5, 0x5A, 0x09, //1//
+0xCB, 0xFF, 0x67, 0x70, 0xB6, 0xE3, 0x4E, 0x9A, 0x63, 0x27, 0x1A, 0x3C, 0x5A, 0x9A, 0x3B, 0xFC, 0x3A, //2//
+0xEF, 0xA8, 0x44, 0x42, 0xDB, 0x1E, 0x59, 0x31, 0x17, 0xEB, 0xF5, 0x99, 0x18, 0x8E, 0x63, 0xAE, 0x2B, //3//
+0x23, 0x07, 0xEB, 0xC9, 0xCD, 0xD6, 0xC7, 0xCF, 0xC8, 0xBF, 0x0F, 0xE2, 0x01, 0x16, 0x42, 0xD3, 0x1C, //4//
+0x8A, 0xC4, 0x06, 0xB6, 0x51, 0xCA, 0x6F, 0xA7, 0x2E, 0x33, 0x82, 0xBD, 0x65, 0x58, 0xC0, 0xCB, 0x0D, //5//
+0x0D, 0xBD, 0x5D, 0x65, 0x92, 0x77, 0x0D, 0x50, 0xAC, 0x79, 0x40, 0x86, 0x34, 0x45, 0x8F, 0x42, 0x3E, //6//
+0x12, 0x8C, 0xA0, 0xAF, 0x07, 0xA1, 0xD3, 0x7D, 0x86, 0x5C, 0xA3, 0x2B, 0x80, 0x3F, 0x1D, 0x29, 0x2F, //7//
+0x68, 0x15, 0xD1, 0x34, 0xE4, 0x2F, 0xF6, 0xFC, 0xEF, 0xDA, 0x5C, 0x57, 0xCF, 0xB4, 0x5A, 0x90, 0x10, //8//
+0x7C, 0x49, 0xF2, 0x5A, 0xF3, 0x34, 0xBC, 0x88, 0x3A, 0x91, 0xB8, 0xC8, 0xAD, 0x03, 0x01, 0x77, 0x01, //9//
+0xB0, 0x91, 0xBA, 0x23, 0x1E, 0x42, 0x72, 0xEB, 0xD2, 0xF8, 0x7E, 0x1E, 0xF7, 0x69, 0xE9, 0x0F, 0x32, //a//
+0xA6, 0x32, 0x9C, 0x9D, 0x49, 0xB5, 0x98, 0x4E, 0x49, 0x00, 0xC9, 0x7F, 0x92, 0xC0, 0x2E, 0x3D, 0x23, //b//
+0x54, 0x2A, 0x25, 0x07, 0x6C, 0x6C, 0xAA, 0xB2, 0xB1, 0x45, 0x27, 0xD4, 0x76, 0x27, 0xBC, 0xE8, 0x14, //c//
+0xD9, 0xD0, 0xCE, 0x8C, 0x25, 0x98, 0xEB, 0x66, 0x74, 0x62, 0xE6, 0xA0, 0xEE, 0xFD, 0xF4, 0x86, 0x05, //d//
+0xF5, 0x7E, 0x1F, 0xEB, 0x3F, 0xFD, 0x34, 0x04, 0x50, 0x16, 0x3D, 0x61, 0x29, 0xDC, 0xA7, 0xB5, 0x36, //e//
+0x4E, 0x56, 0x39, 0xF1, 0xAA, 0x80, 0x21, 0x15, 0x9D, 0xAE, 0x94, 0x45, 0xBB, 0x72, 0x76, 0x11, 0x27 //f//
+};
+
+UCHAR Ch = Value & Mask;
+int x;
+
+//
+// Scan down the column of Vtab looking for a nibble that matches Ch
+// return the index in the nibble indicated by Mask.
+//
+
+for ( x = 0 ; x < 16 ; x++) {
+
+ if ( (Vtab[ (x*17) + index] & Mask) == Ch ) {
+
+ x |= x << 4; // Copy into both nibbles
+ return( x & Mask );
+
+ }
+}
+
+printf(" Not found Value %x, Mask %x, Index %x\n", Value, Mask, index);
+return( 0 );
+
+}
+
+BOOLEAN
+OpenServer(
+ PHANDLE Handle,
+ PWCH ServerName
+ )
+{
+ NTSTATUS status;
+ OBJECT_ATTRIBUTES objectAttributes;
+ IO_STATUS_BLOCK ioStatusBlock;
+ WCHAR ServerNameBuffer[100];
+ UNICODE_STRING ServerNameString;
+
+ ServerNameString.Buffer = ServerNameBuffer;
+ ServerNameString.Length = 0;
+ ServerNameString.MaximumLength = 100;
+
+ status = RtlAppendUnicodeToString( &ServerNameString, DD_NWFS_DEVICE_NAME_U );
+ ASSERT( status == STATUS_SUCCESS );
+ status = RtlAppendUnicodeToString( &ServerNameString, L"\\" );
+ ASSERT( status == STATUS_SUCCESS );
+ status = RtlAppendUnicodeToString( &ServerNameString, ServerName );
+ ASSERT( status == STATUS_SUCCESS );
+
+ //
+ // Open the file
+ //
+
+ InitializeObjectAttributes(
+ &objectAttributes,
+ &ServerNameString,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ status = NtOpenFile (
+ Handle,
+ FILE_GENERIC_READ | SYNCHRONIZE,
+ &objectAttributes,
+ &ioStatusBlock,
+ FILE_SHARE_WRITE | FILE_SHARE_READ,
+ 0L
+ );
+
+ if (!NT_SUCCESS(status) ) {
+ printf( "Open status = %x for file %Z\n", status, &ServerName );
+ }
+
+ return ( (BOOLEAN) NT_SUCCESS( status ) );
+}
+
+
+CHAR Buffer1[100];
+CHAR Buffer2[100];
+
+SendMessage(
+ IN char* Format,
+ ...
+ )
+{
+ NTSTATUS status = -1;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ va_list Arguments;
+ NTSTATUS Status;
+ ULONG ReceiveBufferSize = 100;
+ ULONG SendBufferSize = 100;
+
+ va_start( Arguments, Format );
+
+ Buffer1[0] = Pid;
+
+ Status = FormatRequest( &Buffer1[1], &SendBufferSize, Format, Arguments );
+ if ( !NT_SUCCESS( Status ) ) {
+ return( Status );
+ }
+
+ //printf("Sending message\n" );
+ //dump( Buffer1, SendBufferSize + 1);
+
+ status = NtFsControlFile(
+ ServerHandle,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ FSCTL_NWR_ANY_NCP,
+ Buffer1,
+ SendBufferSize + 1,
+ Buffer2,
+ ReceiveBufferSize
+ );
+ if ( NT_SUCCESS( status ) ) {
+
+ status = NtWaitForSingleObject( ServerHandle, FALSE, NULL );
+ if ( NT_SUCCESS( status )) {
+ status = IoStatusBlock.Status;
+ }
+ }
+
+
+ if ( !NT_SUCCESS( status ) ) {
+ printf("SendPacket returns %08lx\n", status );
+ } else {
+ printf("Send succeeded.\n" );
+ //dump( Buffer2, IoStatusBlock.Information );
+ }
+ return( status );
+}
+
+
+NTSTATUS
+FormatRequest(
+ PCHAR SendBuffer,
+ PULONG SendBufferLength,
+ char* Format,
+ va_list a
+ )
+/*++
+
+Routine Description:
+
+ Send the packet described by Format and the additional parameters.
+
+Arguments:
+
+ Format - the information needed to create the request to the
+ server. The first byte indicates the packet type and the
+ following bytes contain field types.
+
+ Packet types:
+
+ 'A' SAP broadcast ( void )
+ 'C' NCP connect ( void )
+ 'F' NCP function ( byte )
+ 'S' NCP subfunction ( byte, byte )
+ 'D' NCP disconnect ( void )
+
+ Field types, request/response:
+
+ 'b' byte ( byte / byte* )
+ 'w' hi-lo word ( word / word* )
+ 'd' hi-lo dword ( dword / dword* )
+ '-' zero/skip byte ( void )
+ '=' zero/skip word ( void )
+ ._. zero/skip string ( word )
+ 'p' pstring ( char* )
+ 'u' p unicode string ( UNICODE_STRING * )
+ 'c' cstring ( char* )
+ 'r' raw bytes ( byte*, word )
+ 'u' p unicode string ( UNICODE_STRING * )
+ 'U' p uppercase string( UNICODE_STRING * )
+ 'f' separate fragment ( PMDL )
+
+ An 'f' field must be last, and in a response it cannot be
+ preceeded by 'p' or 'c' fields.
+
+
+Return Value:
+
+ Normally returns STATUS_PENDING.
+
+--*/
+{
+ NTSTATUS status;
+ char* pFormatCharacter;
+ USHORT t = 1;
+ ULONG data_size;
+ ULONG length;
+
+ data_size = 1;
+
+ SendBuffer[ 0 ] = va_arg( a, UCHAR );
+
+ if ( *Format == 'S' ) {
+ data_size += 2;
+ SendBuffer[data_size++] = va_arg( a, UCHAR );
+ }
+
+ pFormatCharacter = Format;
+
+ while ( *++pFormatCharacter && *pFormatCharacter != 'f' )
+ {
+ switch ( *pFormatCharacter ) {
+
+ case '=':
+ SendBuffer[data_size++] = 0;
+ case '-':
+ SendBuffer[data_size++] = 0;
+ break;
+
+ case '_':
+ length = va_arg ( a, USHORT );
+
+ if ( data_size + length > *SendBufferLength ) {
+ printf("***exch:!0!\n");
+ return( FALSE );
+ }
+
+ while ( length-- ) {
+ SendBuffer[data_size++] = 0;
+ }
+
+ break;
+
+ case 'b':
+ SendBuffer[data_size++] = va_arg ( a, UCHAR );
+ break;
+
+ case 'w':
+ {
+ USHORT w = va_arg ( a, USHORT );
+
+ SendBuffer[data_size++] = (UCHAR) (w >> 8);
+ SendBuffer[data_size++] = (UCHAR) (w >> 0);
+ break;
+ }
+
+ case 'd':
+ {
+ ULONG d = va_arg ( a, ULONG );
+
+ SendBuffer[data_size++] = (UCHAR) (d >> 24);
+ SendBuffer[data_size++] = (UCHAR) (d >> 16);
+ SendBuffer[data_size++] = (UCHAR) (d >> 8);
+ SendBuffer[data_size++] = (UCHAR) (d >> 0);
+ break;
+ }
+
+ case 'c':
+ {
+ char* c = va_arg ( a, char* );
+
+ length = strlen( c );
+
+ if ( data_size + length > *SendBufferLength ) {
+ printf("***exch:!1!\n");
+ return( FALSE );
+ }
+
+ RtlCopyMemory( &SendBuffer[data_size], c, length + 1 );
+ data_size += length + 1;
+ break;
+ }
+
+ case 'p':
+ {
+ char* c = va_arg ( a, char* );
+
+ length = strlen( c );
+
+ if ( data_size + length > *SendBufferLength ) {
+ printf("***exch:!2!\n");
+ return FALSE;
+ }
+
+ SendBuffer[data_size++] = (UCHAR)length;
+ RtlCopyMemory( &SendBuffer[data_size], c, length );
+ data_size += length;
+ break;
+ }
+
+ case 'u':
+ {
+ PUNICODE_STRING pUString = va_arg ( a, PUNICODE_STRING );
+ OEM_STRING OemString;
+
+ //
+ // Calculate required string length, excluding trailing NUL.
+ //
+
+ length = (USHORT)RtlUnicodeStringToOemSize( pUString ) - 1;
+ ASSERT( length < 0x100 );
+
+ if ( data_size + length > *SendBufferLength ) {
+ printf("***exch:!4!\n");
+ return( FALSE );
+ }
+
+ SendBuffer[data_size++] = (UCHAR)length;
+ OemString.Buffer = &SendBuffer[data_size];
+ OemString.MaximumLength = (USHORT)length + 1;
+ status = RtlUnicodeStringToCountedOemString( &OemString, pUString, FALSE );
+ ASSERT( NT_SUCCESS( status ));
+ data_size += (USHORT)length;
+ break;
+ }
+
+ case 'U':
+ {
+ USHORT i;
+
+ //
+ // UPPERCASE the string, copy it from unicode to the packet
+ //
+
+ PUNICODE_STRING pUString = va_arg ( a, PUNICODE_STRING );
+ UNICODE_STRING UUppercaseString;
+ OEM_STRING OemString;
+
+ if ( pUString->Length > 0 ) {
+
+ RtlUpcaseUnicodeString( &UUppercaseString, pUString, TRUE );
+
+ //
+ // Change all '\' to '/'
+ //
+
+ for ( i = 0 ; i < UUppercaseString.Length ; i++ ) {
+ if ( UUppercaseString.Buffer[i] == L'\\' ) {
+ UUppercaseString.Buffer[i] = L'/';
+ }
+ }
+
+ //
+ // Calculate required string length, excluding trailing NUL.
+ //
+
+ length = (USHORT)RtlUnicodeStringToOemSize( &UUppercaseString ) - 1;
+ ASSERT( length < 0x100 );
+
+ } else {
+ UUppercaseString = *pUString;
+ length = 0;
+ }
+
+ if ( data_size + length > *SendBufferLength ) {
+ printf("***exch:!5!\n");
+ return( FALSE );
+ }
+
+ SendBuffer[data_size++] = (UCHAR)length;
+ OemString.Buffer = &SendBuffer[data_size];
+ OemString.MaximumLength = (USHORT)length + 1;
+ status = RtlUnicodeStringToCountedOemString( &OemString,
+ &UUppercaseString,
+ FALSE );
+ ASSERT( NT_SUCCESS( status ));
+
+ if ( pUString->Length > 0 ) {
+ RtlFreeUnicodeString( &UUppercaseString );
+ }
+
+ data_size += (USHORT)length;
+ break;
+ }
+
+ case 'r':
+ {
+ UCHAR* b = va_arg ( a, UCHAR* );
+ length = va_arg ( a, USHORT );
+
+ if ( data_size + length > *SendBufferLength ) {
+ printf("***exch:!6!\n");
+ return( FALSE );
+ }
+
+ RtlCopyMemory( &SendBuffer[data_size], b, length );
+ data_size += length;
+ break;
+ }
+
+ default:
+ printf ( "*****exchange: invalid request field, %x\n", *pFormatCharacter);
+ return( FALSE );
+ }
+
+ if ( data_size > *SendBufferLength ) {
+ printf( "*****exchange: CORRUPT, too much request data\n" );
+ va_end( a );
+ return FALSE;
+ }
+ }
+
+ if ( *Format == 'S' )
+ {
+ SendBuffer[1] = (UCHAR)((data_size-3) >> 8);
+ SendBuffer[2] = (UCHAR)(data_size-3);
+ }
+
+ va_end( a );
+
+ *SendBufferLength = data_size;
+ return TRUE;
+}
+
+NTSTATUS
+_cdecl
+ParseResponse(
+ char* FormatString,
+ ... // format specific parameters
+ )
+/*++
+
+Routine Description:
+
+ This routine parse an NCP response.
+
+Arguments:
+
+ FormatString - supplies the information needed to create the request to the
+ server. The first byte indicates the packet type and the
+ following bytes contain field types.
+
+ Field types, request/response:
+
+ 'b' byte ( byte* )
+ 'w' hi-lo word ( word* )
+ 'd' hi-lo dword ( dword* )
+ '-' zero/skip byte ( void )
+ '=' zero/skip word ( void )
+ ._. zero/skip string ( word )
+ 'p' pstring ( char* )
+ 'c' cstring ( char* )
+ 'r' raw bytes ( byte*, word )
+ 'R' ASCIIZ to Unicode ( UNICODE_STRING *, word )
+
+Return Value:
+
+ STATUS - The converted error code from the NCP response.
+
+--*/
+
+{
+ PCHAR FormatByte;
+ va_list Arguments;
+ int Length = 0;
+
+ va_start( Arguments, FormatString );
+
+ FormatByte = FormatString;
+ while ( *FormatByte ) {
+
+ switch ( *FormatByte ) {
+
+ case '-':
+ Length += 1;
+ break;
+
+ case '=':
+ Length += 2;
+ break;
+
+ case '_':
+ {
+ USHORT l = va_arg ( Arguments, USHORT );
+ Length += l;
+ break;
+ }
+
+ case 'b':
+ {
+ UCHAR* b = va_arg ( Arguments, UCHAR* );
+ *b = Buffer2[Length++];
+ break;
+ }
+
+ case 'w':
+ {
+ UCHAR* b = va_arg ( Arguments, UCHAR* );
+ b[1] = Buffer2[Length++];
+ b[0] = Buffer2[Length++];
+ break;
+ }
+
+ case 'd':
+ {
+ UCHAR* b = va_arg ( Arguments, UCHAR* );
+ b[3] = Buffer2[Length++];
+ b[2] = Buffer2[Length++];
+ b[1] = Buffer2[Length++];
+ b[0] = Buffer2[Length++];
+ break;
+ }
+
+ case 'c':
+ {
+ char* c = va_arg ( Arguments, char* );
+ USHORT l = strlen( &Buffer2[Length] );
+ memcpy ( c, &Buffer2[Length], l+1 );
+ Length += l+1;
+ break;
+ }
+
+ case 'p':
+ {
+ char* c = va_arg ( Arguments, char* );
+ UCHAR l = Buffer2[Length++];
+ memcpy ( c, &Buffer2[Length], l );
+ c[l+1] = 0;
+ break;
+ }
+
+#if 0
+ case 'P':
+ {
+ PUNICODE_STRING pUString = va_arg ( Arguments, PUNICODE_STRING );
+ OEM_STRING OemString;
+
+ OemString.Length = Buffer2[Length++];
+ OemString.Buffer = &Buffer2[Length];
+
+ Status = RtlOemStringToCountedUnicodeString( pUString, &OemString, FALSE );
+ ASSERT( NT_SUCCESS( Status ));
+
+ break;
+ }
+#endif
+
+ case 'r':
+ {
+ UCHAR* b = va_arg ( Arguments, UCHAR* );
+ USHORT l = va_arg ( Arguments, USHORT );
+ RtlCopyMemory( b, &Buffer2[Length], l );
+ Length += l;
+ break;
+ }
+
+#if 0
+ case 'R':
+ {
+ //
+ // Interpret the buffer as an ASCIIZ string. Convert
+ // it to unicode in the preallocated buffer.
+ //
+
+ PUNICODE_STRING pUString = va_arg ( Arguments, PUNICODE_STRING );
+ OEM_STRING OemString;
+ USHORT len = va_arg ( Arguments, USHORT );
+
+ OemString.Buffer = &Buffer2[Length];
+ OemString.Length = strlen( OemString.Buffer );
+ OemString.MaximumLength = OemString.Length;
+
+ Status = RtlOemStringToCountedUnicodeString( pUString, &OemString, FALSE );
+ ASSERT( NT_SUCCESS( Status ));
+ Length += len;
+ break;
+ }
+#endif
+
+ default:
+ printf ( "*****exchange: invalid response field, %x\n", *FormatByte );
+ return( FALSE );
+ }
+
+#if 0
+ if ( Length > PacketLength )
+ {
+ printf ( "*****exchange: not enough response data, %d\n", i );
+ }
+#endif
+ FormatByte++;
+ }
+
+ va_end( Arguments );
+}
+
+
+// Hex dump
+
+VOID
+dump(
+ IN PVOID far_p,
+ IN ULONG len
+ )
+/*++
+
+Routine Description:
+ Dump Min(len, MaxDump) bytes in classic hex dump style.
+
+Arguments:
+
+ IN far_p - address of buffer to start dumping from.
+
+ IN len - length in bytes of buffer.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ ULONG l;
+ char s[80], t[80];
+ PCHAR far_pchar = (PCHAR)far_p;
+
+ if (len > MaxDump)
+ len = MaxDump;
+
+ while (len) {
+ l = len < 17 ? len : 17;
+
+ // printf("\n%lx ", far_pchar);
+ HexDumpLine (far_pchar, l, s, t, 0);
+ // printf("%s%.*s%s", s, 1 + ((16 - l) * 3), "", t);
+ printf("%s", s);
+
+ len -= l;
+ far_pchar += l;
+ }
+ printf("\n");
+}
+
+VOID
+HexDumpLine (
+ PCHAR pch,
+ ULONG len,
+ PCHAR s,
+ PCHAR t,
+ USHORT flag
+ )
+{
+ static UCHAR rghex[] = "0123456789ABCDEF";
+
+ UCHAR c;
+ UCHAR *hex, *asc;
+
+
+ hex = s;
+ asc = t;
+
+ *(asc++) = '*';
+ while (len--) {
+ c = *(pch++);
+ *(hex++) = rghex [c >> 4] ;
+ *(hex++) = rghex [c & 0x0F];
+ *(hex++) = ' ';
+ *(asc++) = (c < ' ' || c > '~') ? (CHAR )'.' : c;
+ }
+ *(asc++) = '*';
+ *asc = 0;
+ *hex = 0;
+
+ flag;
+}
+
+// End Hex dump
+
+UCHAR Table[] =
+{0x7,0x8,0x0,0x8,0x6,0x4,0xE,0x4,0x5,0xC,0x1,0x7,0xB,0xF,0xA,0x8,
+ 0xF,0x8,0xC,0xC,0x9,0x4,0x1,0xE,0x4,0x6,0x2,0x4,0x0,0xA,0xB,0x9,
+ 0x2,0xF,0xB,0x1,0xD,0x2,0x1,0x9,0x5,0xE,0x7,0x0,0x0,0x2,0x6,0x6,
+ 0x0,0x7,0x3,0x8,0x2,0x9,0x3,0xF,0x7,0xF,0xC,0xF,0x6,0x4,0xA,0x0,
+ 0x2,0x3,0xA,0xB,0xD,0x8,0x3,0xA,0x1,0x7,0xC,0xF,0x1,0x8,0x9,0xD,
+ 0x9,0x1,0x9,0x4,0xE,0x4,0xC,0x5,0x5,0xC,0x8,0xB,0x2,0x3,0x9,0xE,
+ 0x7,0x7,0x6,0x9,0xE,0xF,0xC,0x8,0xD,0x1,0xA,0x6,0xE,0xD,0x0,0x7,
+ 0x7,0xA,0x0,0x1,0xF,0x5,0x4,0xB,0x7,0xB,0xE,0xC,0x9,0x5,0xD,0x1,
+ 0xB,0xD,0x1,0x3,0x5,0xD,0xE,0x6,0x3,0x0,0xB,0xB,0xF,0x3,0x6,0x4,
+ 0x9,0xD,0xA,0x3,0x1,0x4,0x9,0x4,0x8,0x3,0xB,0xE,0x5,0x0,0x5,0x2,
+ 0xC,0xB,0xD,0x5,0xD,0x5,0xD,0x2,0xD,0x9,0xA,0xC,0xA,0x0,0xB,0x3,
+ 0x5,0x3,0x6,0x9,0x5,0x1,0xE,0xE,0x0,0xE,0x8,0x2,0xD,0x2,0x2,0x0,
+ 0x4,0xF,0x8,0x5,0x9,0x6,0x8,0x6,0xB,0xA,0xB,0xF,0x0,0x7,0x2,0x8,
+ 0xC,0x7,0x3,0xA,0x1,0x4,0x2,0x5,0xF,0x7,0xA,0xC,0xE,0x5,0x9,0x3,
+ 0xE,0x7,0x1,0x2,0xE,0x1,0xF,0x4,0xA,0x6,0xC,0x6,0xF,0x4,0x3,0x0,
+ 0xC,0x0,0x3,0x6,0xF,0x8,0x7,0xB,0x2,0xD,0xC,0x6,0xA,0xA,0x8,0xD};
+
+UCHAR Keys[32] =
+{0x48,0x93,0x46,0x67,0x98,0x3D,0xE6,0x8D,
+ 0xB7,0x10,0x7A,0x26,0x5A,0xB9,0xB1,0x35,
+ 0x6B,0x0F,0xD5,0x70,0xAE,0xFB,0xAD,0x11,
+ 0xF4,0x47,0xDC,0xA7,0xEC,0xCF,0x50,0xC0};
+
+#define XorArray( DEST, SRC ) { \
+ PULONG D = (PULONG)DEST; \
+ PULONG S = (PULONG)SRC; \
+ int i; \
+ for ( i = 0; i <= 7 ; i++ ) { \
+ D[i] ^= S[i]; \
+ } \
+}
+
+VOID
+RespondToChallenge(
+ IN PUCHAR achObjectId,
+ IN POEM_STRING Password,
+ IN PUCHAR pChallenge,
+ OUT PUCHAR pResponse
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes the ObjectId and Challenge key from the server and
+ encrypts the user supplied password to develop a credential for the
+ server to verify.
+
+Arguments:
+ IN PUCHAR achObjectId - Supplies the 4 byte user's bindery object id
+ IN POEM_STRING Password - Supplies the user's uppercased password
+ IN PUCHAR pChallenge - Supplies the 8 byte challenge key
+ OUT PUCHAR pResponse - Returns the 8 byte response
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ int index;
+ UCHAR achK[32];
+ UCHAR achBuf[32];
+
+ Shuffle(achObjectId, Password->Buffer, Password->Length, achBuf);
+ Shuffle( &pChallenge[0], achBuf, 16, &achK[0] );
+ Shuffle( &pChallenge[4], achBuf, 16, &achK[16] );
+
+ for (index = 0; index < 16; index++)
+ achK[index] ^= achK[31-index];
+
+ for (index = 0; index < 8; index++)
+ pResponse[index] = achK[index] ^ achK[15-index];
+}
+
+VOID
+RespondToChallengePart1(
+ IN PUCHAR achObjectId,
+ IN POEM_STRING Password,
+ OUT PUCHAR pResponse
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes the ObjectId and Challenge key from the server and
+ encrypts the user supplied password to develop a credential for the
+ server to verify.
+
+Arguments:
+ IN PUCHAR achObjectId - Supplies the 4 byte user's bindery object id
+ IN POEM_STRING Password - Supplies the user's uppercased password
+ IN PUCHAR pChallenge - Supplies the 8 byte challenge key
+ OUT PUCHAR pResponse - Returns the 16 byte response held by the server
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ UCHAR achBuf[32];
+
+ Shuffle(achObjectId, Password->Buffer, Password->Length, achBuf);
+ memmove(pResponse, achBuf, 16);
+}
+
+VOID
+RespondToChallengePart2(
+ IN PUCHAR pResponsePart1,
+ IN PUCHAR pChallenge,
+ OUT PUCHAR pResponse
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes the result of Shuffling the ObjectId and the Password
+ and processes it with a challenge key.
+
+Arguments:
+ IN PUCHAR pResponsePart1 - Supplies the 16 byte output of
+ RespondToChallengePart1.
+ IN PUCHAR pChallenge - Supplies the 8 byte challenge key
+ OUT PUCHAR pResponse - Returns the 8 byte response
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ int index;
+ UCHAR achK[32];
+
+ Shuffle( &pChallenge[0], pResponsePart1, 16, &achK[0] );
+ Shuffle( &pChallenge[4], pResponsePart1, 16, &achK[16] );
+
+ for (index = 0; index < 16; index++)
+ achK[index] ^= achK[31-index];
+
+ for (index = 0; index < 8; index++)
+ pResponse[index] = achK[index] ^ achK[15-index];
+}
+
+VOID
+Shuffle(
+ UCHAR *achObjectId,
+ UCHAR *szUpperPassword,
+ int iPasswordLen,
+ UCHAR *achOutputBuffer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine shuffles around the object ID with the password
+
+Arguments:
+
+ IN achObjectId - Supplies the 4 byte user's bindery object id
+
+ IN szUpperPassword - Supplies the user's uppercased password on the
+ first call to process the password. On the second and third calls
+ this parameter contains the OutputBuffer from the first call
+
+ IN iPasswordLen - length of uppercased password
+
+ OUT achOutputBuffer - Returns the 8 byte sub-calculation
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ int iTempIndex;
+ int iOutputIndex;
+ UCHAR achTemp[32];
+
+ //
+ // Initialize the achTemp buffer. Initialization consists of taking
+ // the password and dividing it up into chunks of 32. Any bytes left
+ // over are the remainder and do not go into the initialization.
+ //
+ // achTemp[0] = szUpperPassword[0] ^ szUpperPassword[32] ^ szUpper...
+ // achTemp[1] = szUpperPassword[1] ^ szUpperPassword[33] ^ szUpper...
+ // etc.
+ //
+
+ if ( iPasswordLen > 32) {
+
+ // At least one chunk of 32. Set the buffer to the first chunk.
+
+ RtlCopyMemory( achTemp, szUpperPassword, 32 );
+
+ szUpperPassword +=32; // Remove the first chunk
+ iPasswordLen -=32;
+
+ while ( iPasswordLen >= 32 ) {
+ //
+ // Xor this chunk with the characters already loaded into
+ // achTemp.
+ //
+
+ XorArray( achTemp, szUpperPassword);
+
+ szUpperPassword +=32; // Remove this chunk
+ iPasswordLen -=32;
+ }
+
+ } else {
+
+ // No chunks of 32 so set the buffer to zero's
+
+ RtlZeroMemory( achTemp, sizeof(achTemp));
+
+ }
+
+ //
+ // achTemp is now initialized. Load the remainder into achTemp.
+ // The remainder is repeated to fill achTemp.
+ //
+ // The corresponding character from Keys is taken to seperate
+ // each repitition.
+ //
+ // As an example, take the remainder "ABCDEFG". The remainder is expanded
+ // to "ABCDEFGwABCDEFGxABCDEFGyABCDEFGz" where w is Keys[7],
+ // x is Keys[15], y is Keys[23] and z is Keys[31].
+ //
+ //
+
+ if (iPasswordLen > 0) {
+ int iPasswordOffset = 0;
+ for (iTempIndex = 0; iTempIndex < 32; iTempIndex++) {
+
+ if (iPasswordLen == iPasswordOffset) {
+ iPasswordOffset = 0;
+ achTemp[iTempIndex] ^= Keys[iTempIndex];
+ } else {
+ achTemp[iTempIndex] ^= szUpperPassword[iPasswordOffset++];
+ }
+ }
+ }
+
+ //
+ // achTemp has been loaded with the users password packed into 32
+ // bytes. Now take the objectid that came from the server and use
+ // that to munge every byte in achTemp.
+ //
+
+ for (iTempIndex = 0; iTempIndex < 32; iTempIndex++)
+ achTemp[iTempIndex] ^= achObjectId[ iTempIndex & 3];
+
+ Scramble( Scramble( 0, achTemp ), achTemp );
+
+ //
+ // Finally take pairs of bytes in achTemp and return the two
+ // nibbles obtained from Table. The pairs of bytes used
+ // are achTemp[n] and achTemp[n+16].
+ //
+
+ for (iOutputIndex = 0; iOutputIndex < 16; iOutputIndex++) {
+
+ achOutputBuffer[iOutputIndex] =
+ Table[achTemp[iOutputIndex << 1]] |
+ (Table[achTemp[(iOutputIndex << 1) + 1]] << 4);
+ }
+
+ return;
+}
+
+int
+Scramble(
+ int iSeed,
+ UCHAR achBuffer[32]
+ )
+
+/*++
+
+Routine Description:
+
+ This routine scrambles around the contents of the buffer. Each buffer
+ position is updated to include the contents of at least two character
+ positions plus an EncryptKey value. The buffer is processed left to right
+ and so if a character position chooses to merge with a buffer position
+ to its left then this buffer position will include bits derived from at
+ least 3 bytes of the original buffer contents.
+
+Arguments:
+
+ IN iSeed
+ IN OUT achBuffer[32]
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ int iBufferIndex;
+
+ for (iBufferIndex = 0; iBufferIndex < 32; iBufferIndex++) {
+ achBuffer[iBufferIndex] =
+ (UCHAR)(
+ ((UCHAR)(achBuffer[iBufferIndex] + iSeed)) ^
+ ((UCHAR)( achBuffer[(iBufferIndex+iSeed) & 31] -
+ Keys[iBufferIndex] )));
+
+ iSeed += achBuffer[iBufferIndex];
+ }
+ return iSeed;
+}
+
diff --git a/private/nw/test/tp3.c b/private/nw/test/tp3.c
new file mode 100644
index 000000000..f33ffc123
--- /dev/null
+++ b/private/nw/test/tp3.c
@@ -0,0 +1,1715 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ tex.c
+
+Abstract:
+
+ User mode test program for the Microsoft Netware redir file system.
+
+ This test program can be built from the command line using the
+ command 'nmake UMTEST=tex'.
+
+Author:
+
+ Manny Weiser (mannyw) 7-Jun-1993
+
+Revision History:
+
+--*/
+
+#include <conio.h>
+#include <stdio.h>
+#include <string.h>
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <ntddnwfs.h>
+#include <STDARG.H>
+
+#define OT_USER 1
+
+
+// Hex dump
+ULONG MaxDump = 256;
+
+VOID
+dump(
+ IN PVOID far_p,
+ IN ULONG len
+ );
+
+VOID
+HexDumpLine (
+ PCHAR pch,
+ ULONG len,
+ PCHAR s,
+ PCHAR t,
+ USHORT flag
+ );
+
+
+// End Hex dump
+
+VOID
+ResetPassword(
+ PUCHAR Vnew
+ );
+
+UCHAR
+LookUp(
+ UCHAR Value,
+ UCHAR Mask,
+ int index
+ );
+
+SendMessage(
+ IN char* Format,
+ ...
+ );
+
+NTSTATUS
+FormatRequest(
+ PCHAR SendBuffer,
+ PULONG SendBufferLength,
+ char* Format,
+ va_list a
+ );
+
+BOOLEAN
+OpenServer(
+ PHANDLE Handle,
+ PWCH ServerName
+ );
+
+NTSTATUS
+_cdecl
+ParseResponse(
+ char* FormatString,
+ ... // format specific parameters
+ );
+
+VOID
+Shuffle(
+ UCHAR *achObjectId,
+ UCHAR *szUpperPassword,
+ int iPasswordLen,
+ UCHAR *achOutputBuffer
+ );
+
+int
+Scramble(
+ int iSeed,
+ UCHAR achBuffer[32]
+ );
+
+VOID
+RespondToChallenge(
+ IN PUCHAR achObjectId,
+ IN POEM_STRING Password,
+ IN PUCHAR pChallenge,
+ OUT PUCHAR pResponse
+ );
+
+VOID
+RespondToChallengePart1(
+ IN PUCHAR achObjectId,
+ IN POEM_STRING Password,
+ OUT PUCHAR pResponse
+ );
+
+VOID
+RespondToChallengePart2(
+ IN PUCHAR pResponsePart1,
+ IN PUCHAR pChallenge,
+ OUT PUCHAR pResponse
+ );
+
+NTSTATUS
+GetCurrentPasswordValue (
+ IN OUT PCHAR OutputValue
+ );
+
+#define BUFFER_SIZE 200
+
+WCHAR *ServerName = L"YIHSINS3";
+UCHAR Pid = 255;
+UCHAR Object[4];
+
+WCHAR FileNameBuffer[100];
+UNICODE_STRING FileName;
+
+#define NW_USER "ANDYHE"
+
+
+HANDLE ServerHandle;
+_cdecl
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ BOOLEAN success;
+ UCHAR Challenge[8];
+ UCHAR fileValue[17];
+ UCHAR ResponseP2[16];
+ int x, y;
+ NTSTATUS status;
+
+
+#if 0
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+ 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
+ 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
+ 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+
+#endif
+
+#define START_POSITION 0x00
+
+ UCHAR Vold[] = {
+0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x11,
+0x00, 0x01, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x10, 0xe0, 0x00, 0x00, 0x11,
+0x00, 0x02, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x20, 0xd0, 0x00, 0x00, 0x11,
+0x00, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x30, 0xc0, 0x00, 0x00, 0x11,
+0x00, 0x04, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x00, 0x00, 0x11,
+0x00, 0x05, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x50, 0xa0, 0x00, 0x00, 0x11,
+0x00, 0x06, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x60, 0x90, 0x00, 0x00, 0x11,
+0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x70, 0x80, 0x00, 0x00, 0x11,
+0x00, 0x08, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x80, 0x70, 0x00, 0x00, 0x11,
+0x00, 0x09, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x90, 0x60, 0x00, 0x00, 0x11,
+0x00, 0x0a, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0xa0, 0x50, 0x00, 0x00, 0x11,
+0x00, 0x0b, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0xb0, 0x40, 0x00, 0x00, 0x11,
+0x00, 0x0c, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0xc0, 0x30, 0x00, 0x00, 0x11,
+0x00, 0x0d, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0xd0, 0x20, 0x00, 0x00, 0x11,
+0x00, 0x0e, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0xe0, 0x10, 0x00, 0x00, 0x11,
+0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x11,
+0x50, 0x60, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x00,0x50, 0x60, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x00, 0x11,
+0x50, 0x61, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x00,0x50, 0x60, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x10, 0x11,
+0x50, 0x62, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x00,0x50, 0x60, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x20, 0x11,
+0x50, 0x63, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x00,0x50, 0x60, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x30, 0x11,
+0x50, 0x64, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x00,0x50, 0x60, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x40, 0x11,
+0x50, 0x65, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x00,0x50, 0x60, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x50, 0x11,
+0x50, 0x66, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x00,0x50, 0x60, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x60, 0x11,
+0x50, 0x67, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x00,0x50, 0x60, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x70, 0x11,
+0x50, 0x68, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x00,0x50, 0x60, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x80, 0x11,
+0x50, 0x69, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x00,0x50, 0x60, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x90, 0x11,
+0x50, 0x6a, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x00,0x50, 0x60, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0xa0, 0x11,
+0x50, 0x6b, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x00,0x50, 0x60, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0xb0, 0x11,
+0x50, 0x6c, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x00,0x50, 0x60, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0xc0, 0x11,
+0x50, 0x6d, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x00,0x50, 0x60, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0xd0, 0x11,
+0x50, 0x6e, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x00,0x50, 0x60, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0xe0, 0x11,
+0x50, 0x6f, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x00,0x50, 0x60, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0xf0, 0x11
+ };
+
+ UCHAR Vc[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+#if 0
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+ 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
+ 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
+ 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+#endif
+ };
+
+ success = OpenServer( &ServerHandle, ServerName );
+ if ( !success) {
+ return 1;
+ }
+
+ printf("Opened server, %wS\n", ServerName );
+
+ //
+ // Setup the file name that we'll be opening every time.
+ //
+
+ FileName.Buffer = FileNameBuffer;
+ FileName.Length = 0;
+ FileName.MaximumLength = 100;
+
+ status = RtlAppendUnicodeToString( &FileName, DD_NWFS_DEVICE_NAME_U );
+ ASSERT( status == STATUS_SUCCESS );
+ status = RtlAppendUnicodeToString( &FileName, L"\\" );
+ status = RtlAppendUnicodeToString( &FileName, ServerName );
+ status = RtlAppendUnicodeToString( &FileName, L"\\SYS:\\SYSTEM\\NET$VAL.SYS" );
+
+ // Get objectid
+// SendMessage(
+// "Swp",
+// 0x17, 0x35,
+// OT_USER,
+// NW_USER);
+ SendMessage(
+ "Swp",
+ 0x35,
+ OT_USER,
+ NW_USER);
+
+ ParseResponse("r", Object, 4 );
+
+ // jump into the middle of a run...
+
+ x = 0;
+ y = START_POSITION * 17;
+ goto start;
+
+ for ( x = 0 ; x < sizeof(Vold) ; x+= 17 ) {
+
+ for ( y = 0 ; y < sizeof(Vc) ; y+= 17 ) {
+
+start:
+ //
+ // Set the password on the server to be the value of Vold required for
+ // the second part of the test.
+ //
+
+ ResetPassword( Vold + x );
+
+ //
+ // We now know that Vold on the server is the same as the Vold vector.
+ // We can now set the password to any Vnew.
+ // We can do this without the real password because the
+ // server will believe we know the real password if we give it
+ // the result of taking Vold and the Challenge key and passing
+ // it through RespondToChallengePart2
+ //
+
+ printf( "Getting a challenge key.... " );
+
+// SendMessage(
+// "S",
+// 0x17, 0x17);
+ SendMessage( "S",
+ 0x17);
+
+ ParseResponse("r", Challenge, sizeof(Challenge) );
+
+ // Fabricate the number the server will use to validate the password change
+ RespondToChallengePart2( Vold + x, Challenge, ResponseP2 );
+
+ printf( "Vold= " ); dump( Vold + x, 17);
+
+ // Put out results to console...
+
+ status = GetCurrentPasswordValue( &fileValue[0] );
+
+ if ( NT_SUCCESS( status ) ) {
+
+ printf( "File= " );
+ dump( fileValue, 17);
+ }
+ printf( " Vc= "); dump( Vc + y, 17);
+
+ printf( "Setting the new password... " );
+
+ // Send the set password
+// SendMessage("Srwpr", 0x17, 0x4b,
+// ResponseP2, 8,
+/// OT_USER,
+// NW_USER,
+// Vc + y, 17 );
+ SendMessage("Srwpr",
+ 0x4b,
+ ResponseP2, 8,
+ OT_USER,
+ NW_USER,
+ Vc + y, 17 );
+
+ status = GetCurrentPasswordValue( &fileValue[0] );
+
+ if ( NT_SUCCESS( status ) ) {
+
+ printf( "Vnew= ");
+ dump( fileValue, 17);
+ }
+
+// printf( "Press a key...\n" ); getch();
+
+// printf( "\n" );
+ printf( "\n\n" );
+ }
+ }
+
+ printf( "%s exiting\n", argv[0]);
+ return 0;
+}
+
+NTSTATUS
+GetCurrentPasswordValue (
+ IN OUT PCHAR OutputValue
+ )
+//
+// This routine reads the current password from the file.
+//
+{
+ HANDLE fileHandle;
+ LARGE_INTEGER filePosition;
+ OBJECT_ATTRIBUTES objectAttributes;
+ IO_STATUS_BLOCK ioStatusBlock;
+ int i;
+ PCHAR ch;
+ NTSTATUS status;
+
+ ch = OutputValue;
+ for (i = 0; i < 17; i++) {
+
+ *(ch++) = '0';
+ }
+
+ // Send a Close Bindery to the server
+
+ printf( "Closing the bindery... " );
+
+// SendMessage(
+// "S",
+// 0x17, 0x44);
+ SendMessage(
+ "S",
+ 0x44);
+
+ //
+ // Open the server\sys:system\net$val.sys file which the server just
+ // closed.
+ //
+
+ InitializeObjectAttributes(
+ &objectAttributes,
+ &FileName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ status = NtOpenFile (
+ &fileHandle,
+ FILE_GENERIC_READ | SYNCHRONIZE,
+ &objectAttributes,
+ &ioStatusBlock,
+ FILE_SHARE_WRITE | FILE_SHARE_READ,
+ 0L
+ );
+
+ if (!NT_SUCCESS(status) ) {
+
+ printf( " Open failed. Status = 0x%x\n", status );
+
+ } else {
+
+ //
+ // Seek to the spot in the file that has the supervisor's password.
+ // This will be different for different servers. Here's a table :
+ // ObjectID = Supervisor AndyHe
+ // Netware311 : 0x097C
+ // Mars312 : 0x16CC
+ // YihsinS3 : 0x59EA 0x050C
+ //
+
+ filePosition.HighPart = 0L;
+ filePosition.LowPart = 0x050C;
+
+ // Read the 17 bytes at that offset and close the file
+
+ status = NtReadFile (
+ fileHandle,
+ NULL,
+ NULL,
+ NULL,
+ &ioStatusBlock,
+ OutputValue,
+ 17,
+ &filePosition,
+ NULL
+ );
+
+ if (!NT_SUCCESS(status) ) {
+
+ printf( " Read failed. Status = 0x%x\n", status );
+ }
+
+ NtClose( fileHandle );
+ }
+
+ // Send an Open Bindery to the server
+
+ printf( "Opening the bindery... " );
+// SendMessage(
+// "S",
+// 0x17, 0x45);
+ SendMessage(
+ "S",
+ 0x45);
+
+ return(status);
+}
+
+VOID
+ResetPassword(
+ PUCHAR Vnew
+ )
+{
+//
+// Note Vc[0] specifies the length. To derive new values of Vnew use the following
+// table. For each nibble desired in Vnew, scan the appropriate column and
+// read off the corresponding Vc nibble value on the left.
+//
+
+ UCHAR Vc[17];
+
+ UCHAR Challenge[8];
+ OEM_STRING Password = {0,0,""};
+ UCHAR EncryptKey[8];
+
+ int x;
+
+ //printf( "Get Login key for set password using NULL password\n");
+
+ printf( "Getting a challenge key.... " );
+
+// SendMessage(
+// "S",
+// 0x17, 0x17);
+ SendMessage(
+ "S",
+ 0x17);
+
+ ParseResponse("r", Challenge, sizeof(Challenge) );
+
+ RespondToChallenge( Object, &Password, Challenge, EncryptKey );
+ // dump( EncryptKey, 8);
+
+ //
+ // Build Vc so that the server will get Vnew stored in the bindery.
+ // Note Vols/Vnew have the length at V[17] whereas Vc has it at Vc[0].
+ //
+
+ for ( x = 0 ; x < 16; x++ ) {
+ Vc[x+1] = LookUp( Vnew[x], 0xf0, x) | LookUp( Vnew[x], 0x0f, x);
+ }
+
+ Vc[0] = LookUp( Vnew[16], 0xf0, 16) | LookUp( Vnew[16], 0x0f, 16);
+
+ //printf( "Vnew "); dump( Vnew, 17);
+ //printf( "Vc "); dump( Vc, 17);
+
+ //
+ // This uses the null password, objectid=1 and our supervisor privilege
+ // to set it, regardless of the and our ability to figure out what
+ // Vnew will be created for a particular Vc.
+ //
+
+ printf( "Setting the old password... " );
+
+// SendMessage("Srwpr", 0x17, 0x4b,
+// EncryptKey, sizeof(EncryptKey),
+// OT_USER,
+// NW_USER,
+// Vc, 17 );
+ SendMessage("Srwpr",
+ 0x4b,
+ EncryptKey, sizeof(EncryptKey),
+ OT_USER,
+ NW_USER,
+ Vc, 17 );
+
+ ParseResponse( "" );
+}
+
+UCHAR
+LookUp(
+ UCHAR Value,
+ UCHAR Mask,
+ int index
+ )
+{
+
+#if 0
+
+UCHAR Vtab[] = {
+
+ // This table is for the supervisor
+
+0x97, 0xE3, 0x73, 0xD8, 0x70, 0x5B, 0x85, 0xD3, 0xF5, 0xCD, 0xDB, 0xFA, 0xDC, 0xEB, 0x98, 0x64, 0x18, //0//
+0x31, 0x6B, 0x88, 0x1E, 0x88, 0x09, 0x10, 0x29, 0x0B, 0x84, 0x61, 0x03, 0x43, 0xA1, 0xD5, 0x5A, 0x09, //1//
+0xCB, 0xFF, 0x67, 0x70, 0xB6, 0xE3, 0x4E, 0x9A, 0x63, 0x27, 0x1A, 0x3C, 0x5A, 0x9A, 0x3B, 0xFC, 0x3A, //2//
+0xEF, 0xA8, 0x44, 0x42, 0xDB, 0x1E, 0x59, 0x31, 0x17, 0xEB, 0xF5, 0x99, 0x18, 0x8E, 0x63, 0xAE, 0x2B, //3//
+0x23, 0x07, 0xEB, 0xC9, 0xCD, 0xD6, 0xC7, 0xCF, 0xC8, 0xBF, 0x0F, 0xE2, 0x01, 0x16, 0x42, 0xD3, 0x1C, //4//
+0x8A, 0xC4, 0x06, 0xB6, 0x51, 0xCA, 0x6F, 0xA7, 0x2E, 0x33, 0x82, 0xBD, 0x65, 0x58, 0xC0, 0xCB, 0x0D, //5//
+0x0D, 0xBD, 0x5D, 0x65, 0x92, 0x77, 0x0D, 0x50, 0xAC, 0x79, 0x40, 0x86, 0x34, 0x45, 0x8F, 0x42, 0x3E, //6//
+0x12, 0x8C, 0xA0, 0xAF, 0x07, 0xA1, 0xD3, 0x7D, 0x86, 0x5C, 0xA3, 0x2B, 0x80, 0x3F, 0x1D, 0x29, 0x2F, //7//
+0x68, 0x15, 0xD1, 0x34, 0xE4, 0x2F, 0xF6, 0xFC, 0xEF, 0xDA, 0x5C, 0x57, 0xCF, 0xB4, 0x5A, 0x90, 0x10, //8//
+0x7C, 0x49, 0xF2, 0x5A, 0xF3, 0x34, 0xBC, 0x88, 0x3A, 0x91, 0xB8, 0xC8, 0xAD, 0x03, 0x01, 0x77, 0x01, //9//
+0xB0, 0x91, 0xBA, 0x23, 0x1E, 0x42, 0x72, 0xEB, 0xD2, 0xF8, 0x7E, 0x1E, 0xF7, 0x69, 0xE9, 0x0F, 0x32, //a//
+0xA6, 0x32, 0x9C, 0x9D, 0x49, 0xB5, 0x98, 0x4E, 0x49, 0x00, 0xC9, 0x7F, 0x92, 0xC0, 0x2E, 0x3D, 0x23, //b//
+0x54, 0x2A, 0x25, 0x07, 0x6C, 0x6C, 0xAA, 0xB2, 0xB1, 0x45, 0x27, 0xD4, 0x76, 0x27, 0xBC, 0xE8, 0x14, //c//
+0xD9, 0xD0, 0xCE, 0x8C, 0x25, 0x98, 0xEB, 0x66, 0x74, 0x62, 0xE6, 0xA0, 0xEE, 0xFD, 0xF4, 0x86, 0x05, //d//
+0xF5, 0x7E, 0x1F, 0xEB, 0x3F, 0xFD, 0x34, 0x04, 0x50, 0x16, 0x3D, 0x61, 0x29, 0xDC, 0xA7, 0xB5, 0x36, //e//
+0x4E, 0x56, 0x39, 0xF1, 0xAA, 0x80, 0x21, 0x15, 0x9D, 0xAE, 0x94, 0x45, 0xBB, 0x72, 0x76, 0x11, 0x27 //f//
+};
+
+#endif
+
+
+UCHAR Vtab[] = {
+
+ // This table is for user AndyHe on Yihsins3, object id = 9000095
+
+0xDC, 0x9D, 0xA3, 0x3C, 0x61, 0xCA, 0x98, 0xF2, 0xBB, 0xB5, 0xAE, 0x34, 0x04, 0x43, 0xB0, 0xC9, 0x1B,
+0x3F, 0xF0, 0xCF, 0x12, 0x36, 0x49, 0xC1, 0x29, 0x93, 0x4E, 0x9A, 0x58, 0x52, 0xC8, 0x3D, 0x13, 0x0A,
+0xC9, 0xBC, 0x9A, 0x26, 0x58, 0x7F, 0x1F, 0x4D, 0xD0, 0xA4, 0x77, 0xB7, 0xB8, 0x39, 0x72, 0xFB, 0x39,
+0x7E, 0xDB, 0x66, 0x08, 0xDC, 0x51, 0x3A, 0x81, 0xFD, 0x8B, 0x2B, 0xC5, 0x8F, 0x8C, 0x49, 0x9D, 0x28,
+0x4D, 0xA9, 0x25, 0xB4, 0xBB, 0x18, 0xF3, 0x3A, 0x86, 0x3D, 0x06, 0x23, 0x77, 0xAA, 0x1E, 0xD8, 0x1F,
+0xA5, 0x43, 0xBC, 0x91, 0xC9, 0x0B, 0x79, 0xBF, 0xCF, 0xCC, 0x58, 0x00, 0xE5, 0x1F, 0xC1, 0x81, 0x0E,
+0x83, 0x51, 0x01, 0x73, 0xEF, 0x27, 0x66, 0x13, 0x6C, 0x6F, 0xE4, 0x7F, 0xC9, 0x2D, 0x04, 0xBE, 0x3D,
+0x21, 0xCF, 0x82, 0xF7, 0x93, 0xA6, 0x47, 0x76, 0x19, 0x06, 0x85, 0x4D, 0x4D, 0xFB, 0x23, 0x4F, 0x2C,
+0x54, 0x3A, 0xFE, 0xED, 0x72, 0x9D, 0xBB, 0x65, 0x45, 0xE3, 0x3D, 0x61, 0x1B, 0x04, 0x6A, 0x37, 0x13,
+0x18, 0x08, 0x47, 0x5E, 0x4A, 0xF0, 0xD0, 0x9E, 0x77, 0x9A, 0x69, 0x92, 0x31, 0x55, 0xD5, 0x0C, 0x02,
+0xE0, 0x87, 0x3B, 0x85, 0x04, 0xB4, 0x55, 0xAB, 0x58, 0x22, 0xFF, 0xAB, 0xF0, 0x76, 0xF6, 0x74, 0x31,
+0xF2, 0x15, 0xD9, 0xD9, 0xA0, 0xD3, 0x02, 0x0C, 0x01, 0xF0, 0xB1, 0xFE, 0x63, 0xD2, 0xEF, 0xE6, 0x20,
+0x9B, 0x76, 0x70, 0x4B, 0x87, 0x65, 0xE4, 0xC8, 0x22, 0x59, 0xD0, 0x19, 0x9A, 0xB7, 0x88, 0x60, 0x17,
+0xB7, 0xE4, 0xE4, 0xA0, 0x25, 0x3C, 0x8D, 0x50, 0xAE, 0x17, 0xC2, 0xEC, 0xAE, 0x90, 0xA7, 0x52, 0x06,
+0x06, 0x2E, 0x18, 0x6F, 0xFD, 0x82, 0x2C, 0xE7, 0xE4, 0x78, 0x4C, 0xDA, 0xDC, 0xE1, 0x9C, 0xA5, 0x35,
+0x6A, 0x62, 0x5D, 0xCA, 0x1E, 0xEE, 0xAE, 0xD4, 0x3A, 0xD1, 0x13, 0x86, 0x26, 0x6E, 0x5B, 0x2A, 0x24
+};
+
+UCHAR Ch = Value & Mask;
+int x;
+
+//
+// Scan down the column of Vtab looking for a nibble that matches Ch
+// return the index in the nibble indicated by Mask.
+//
+
+for ( x = 0 ; x < 16 ; x++) {
+
+ if ( (Vtab[ (x*17) + index] & Mask) == Ch ) {
+
+ x |= x << 4; // Copy into both nibbles
+ return( x & Mask );
+
+ }
+}
+
+printf(" Not found Value %x, Mask %x, Index %x\n", Value, Mask, index);
+return( 0 );
+
+}
+
+BOOLEAN
+OpenServer(
+ PHANDLE Handle,
+ PWCH ServerName
+ )
+{
+ NTSTATUS status;
+ OBJECT_ATTRIBUTES objectAttributes;
+ IO_STATUS_BLOCK ioStatusBlock;
+ WCHAR ServerNameBuffer[100];
+ UNICODE_STRING ServerNameString;
+
+ ServerNameString.Buffer = ServerNameBuffer;
+ ServerNameString.Length = 0;
+ ServerNameString.MaximumLength = 100;
+
+ status = RtlAppendUnicodeToString( &ServerNameString, DD_NWFS_DEVICE_NAME_U );
+ ASSERT( status == STATUS_SUCCESS );
+ status = RtlAppendUnicodeToString( &ServerNameString, L"\\" );
+ ASSERT( status == STATUS_SUCCESS );
+ status = RtlAppendUnicodeToString( &ServerNameString, ServerName );
+ ASSERT( status == STATUS_SUCCESS );
+
+ //
+ // Open the file
+ //
+
+ InitializeObjectAttributes(
+ &objectAttributes,
+ &ServerNameString,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ status = NtOpenFile (
+ Handle,
+ SYNCHRONIZE,
+ &objectAttributes,
+ &ioStatusBlock,
+ FILE_SHARE_VALID_FLAGS,
+ FILE_SYNCHRONOUS_IO_NONALERT
+ );
+
+ if (!NT_SUCCESS(status) ) {
+ printf( "Open status = %x for file %Z\n", status, &ServerName );
+ }
+
+ return ( (BOOLEAN) NT_SUCCESS( status ) );
+}
+
+
+CHAR Buffer1[100];
+CHAR Buffer2[100];
+
+SendMessage(
+ IN char* Format,
+ ...
+ )
+{
+ NTSTATUS status = -1;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ va_list Arguments;
+ NTSTATUS Status;
+ ULONG ReceiveBufferSize = 100;
+ ULONG SendBufferSize = 100;
+
+ va_start( Arguments, Format );
+
+ Status = FormatRequest( &Buffer1[0], &SendBufferSize, Format, Arguments );
+ if ( !NT_SUCCESS( Status ) ) {
+ return( Status );
+ }
+
+ Buffer1[0] = 0;
+ Buffer1[0] = SendBufferSize - 2;
+
+ printf("Sending message\n" );
+ dump( Buffer1, SendBufferSize + 1);
+
+ status = NtFsControlFile(
+ ServerHandle,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ NWR_ANY_NCP(0x17),
+ Buffer1,
+ SendBufferSize,
+ Buffer2,
+ ReceiveBufferSize
+ );
+
+ if (NT_SUCCESS(status)) {
+ status = IoStatusBlock.Status;
+// FormattedDump( Reply, ReplyLength );
+ }
+
+#if 0
+ status = NtFsControlFile(
+ ServerHandle,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ NWR_ANY_NCP(0x17), // packet already contains the code necessary
+ Buffer1,
+ SendBufferSize +1,
+ Buffer2,
+ ReceiveBufferSize
+ );
+ if ( NT_SUCCESS( status ) ) {
+
+ status = NtWaitForSingleObject( ServerHandle, FALSE, NULL );
+ if ( NT_SUCCESS( status )) {
+ status = IoStatusBlock.Status;
+ }
+ }
+#endif
+
+ if ( !NT_SUCCESS( status ) ) {
+ printf("SendPacket returns %08lx\n", status );
+ } else {
+ printf("Send succeeded.\n" );
+ //dump( Buffer2, IoStatusBlock.Information );
+ }
+ return( status );
+}
+
+
+NTSTATUS
+FormatRequest(
+ PCHAR SendBuffer,
+ PULONG SendBufferLength,
+ char* Format,
+ va_list a
+ )
+/*++
+
+Routine Description:
+
+ Send the packet described by Format and the additional parameters.
+
+Arguments:
+
+ Format - the information needed to create the request to the
+ server. The first byte indicates the packet type and the
+ following bytes contain field types.
+
+ Packet types:
+
+ 'A' SAP broadcast ( void )
+ 'C' NCP connect ( void )
+ 'F' NCP function ( byte )
+ 'S' NCP subfunction ( byte, byte )
+ 'D' NCP disconnect ( void )
+
+ Field types, request/response:
+
+ 'b' byte ( byte / byte* )
+ 'w' hi-lo word ( word / word* )
+ 'd' hi-lo dword ( dword / dword* )
+ '-' zero/skip byte ( void )
+ '=' zero/skip word ( void )
+ ._. zero/skip string ( word )
+ 'p' pstring ( char* )
+ 'u' p unicode string ( UNICODE_STRING * )
+ 'c' cstring ( char* )
+ 'r' raw bytes ( byte*, word )
+ 'u' p unicode string ( UNICODE_STRING * )
+ 'U' p uppercase string( UNICODE_STRING * )
+ 'f' separate fragment ( PMDL )
+
+ An 'f' field must be last, and in a response it cannot be
+ preceeded by 'p' or 'c' fields.
+
+
+Return Value:
+
+ Normally returns STATUS_PENDING.
+
+--*/
+{
+ NTSTATUS status;
+ char* pFormatCharacter;
+ USHORT t = 1;
+ ULONG data_size;
+ ULONG length;
+
+ data_size = 1;
+
+ SendBuffer[ 0 ] = va_arg( a, UCHAR );
+
+ if ( *Format == 'S' ) {
+ data_size += 2;
+ SendBuffer[data_size++] = va_arg( a, UCHAR );
+ }
+
+ pFormatCharacter = Format;
+
+ while ( *++pFormatCharacter && *pFormatCharacter != 'f' )
+ {
+ switch ( *pFormatCharacter ) {
+
+ case '=':
+ SendBuffer[data_size++] = 0;
+ case '-':
+ SendBuffer[data_size++] = 0;
+ break;
+
+ case '_':
+ length = va_arg ( a, USHORT );
+
+ if ( data_size + length > *SendBufferLength ) {
+ printf("***exch:!0!\n");
+ return( FALSE );
+ }
+
+ while ( length-- ) {
+ SendBuffer[data_size++] = 0;
+ }
+
+ break;
+
+ case 'b':
+ SendBuffer[data_size++] = va_arg ( a, UCHAR );
+ break;
+
+ case 'w':
+ {
+ USHORT w = va_arg ( a, USHORT );
+
+ SendBuffer[data_size++] = (UCHAR) (w >> 8);
+ SendBuffer[data_size++] = (UCHAR) (w >> 0);
+ break;
+ }
+
+ case 'd':
+ {
+ ULONG d = va_arg ( a, ULONG );
+
+ SendBuffer[data_size++] = (UCHAR) (d >> 24);
+ SendBuffer[data_size++] = (UCHAR) (d >> 16);
+ SendBuffer[data_size++] = (UCHAR) (d >> 8);
+ SendBuffer[data_size++] = (UCHAR) (d >> 0);
+ break;
+ }
+
+ case 'c':
+ {
+ char* c = va_arg ( a, char* );
+
+ length = strlen( c );
+
+ if ( data_size + length > *SendBufferLength ) {
+ printf("***exch:!1!\n");
+ return( FALSE );
+ }
+
+ RtlCopyMemory( &SendBuffer[data_size], c, length + 1 );
+ data_size += length + 1;
+ break;
+ }
+
+ case 'p':
+ {
+ char* c = va_arg ( a, char* );
+
+ length = strlen( c );
+
+ if ( data_size + length > *SendBufferLength ) {
+ printf("***exch:!2!\n");
+ return FALSE;
+ }
+
+ SendBuffer[data_size++] = (UCHAR)length;
+ RtlCopyMemory( &SendBuffer[data_size], c, length );
+ data_size += length;
+ break;
+ }
+
+ case 'u':
+ {
+ PUNICODE_STRING pUString = va_arg ( a, PUNICODE_STRING );
+ OEM_STRING OemString;
+
+ //
+ // Calculate required string length, excluding trailing NUL.
+ //
+
+ length = (USHORT)RtlUnicodeStringToOemSize( pUString ) - 1;
+ ASSERT( length < 0x100 );
+
+ if ( data_size + length > *SendBufferLength ) {
+ printf("***exch:!4!\n");
+ return( FALSE );
+ }
+
+ SendBuffer[data_size++] = (UCHAR)length;
+ OemString.Buffer = &SendBuffer[data_size];
+ OemString.MaximumLength = (USHORT)length + 1;
+ status = RtlUnicodeStringToCountedOemString( &OemString, pUString, FALSE );
+ ASSERT( NT_SUCCESS( status ));
+ data_size += (USHORT)length;
+ break;
+ }
+
+ case 'U':
+ {
+ USHORT i;
+
+ //
+ // UPPERCASE the string, copy it from unicode to the packet
+ //
+
+ PUNICODE_STRING pUString = va_arg ( a, PUNICODE_STRING );
+ UNICODE_STRING UUppercaseString;
+ OEM_STRING OemString;
+
+ if ( pUString->Length > 0 ) {
+
+ RtlUpcaseUnicodeString( &UUppercaseString, pUString, TRUE );
+
+ //
+ // Change all '\' to '/'
+ //
+
+ for ( i = 0 ; i < UUppercaseString.Length ; i++ ) {
+ if ( UUppercaseString.Buffer[i] == L'\\' ) {
+ UUppercaseString.Buffer[i] = L'/';
+ }
+ }
+
+ //
+ // Calculate required string length, excluding trailing NUL.
+ //
+
+ length = (USHORT)RtlUnicodeStringToOemSize( &UUppercaseString ) - 1;
+ ASSERT( length < 0x100 );
+
+ } else {
+ UUppercaseString = *pUString;
+ length = 0;
+ }
+
+ if ( data_size + length > *SendBufferLength ) {
+ printf("***exch:!5!\n");
+ return( FALSE );
+ }
+
+ SendBuffer[data_size++] = (UCHAR)length;
+ OemString.Buffer = &SendBuffer[data_size];
+ OemString.MaximumLength = (USHORT)length + 1;
+ status = RtlUnicodeStringToCountedOemString( &OemString,
+ &UUppercaseString,
+ FALSE );
+ ASSERT( NT_SUCCESS( status ));
+
+ if ( pUString->Length > 0 ) {
+ RtlFreeUnicodeString( &UUppercaseString );
+ }
+
+ data_size += (USHORT)length;
+ break;
+ }
+
+ case 'r':
+ {
+ UCHAR* b = va_arg ( a, UCHAR* );
+ length = va_arg ( a, USHORT );
+
+ if ( data_size + length > *SendBufferLength ) {
+ printf("***exch:!6!\n");
+ return( FALSE );
+ }
+
+ RtlCopyMemory( &SendBuffer[data_size], b, length );
+ data_size += length;
+ break;
+ }
+
+ default:
+ printf ( "*****exchange: invalid request field, %x\n", *pFormatCharacter);
+ return( FALSE );
+ }
+
+ if ( data_size > *SendBufferLength ) {
+ printf( "*****exchange: CORRUPT, too much request data\n" );
+ va_end( a );
+ return FALSE;
+ }
+ }
+
+ if ( *Format == 'S' )
+ {
+ SendBuffer[1] = (UCHAR)((data_size-3) >> 8);
+ SendBuffer[2] = (UCHAR)(data_size-3);
+ }
+
+ va_end( a );
+
+ *SendBufferLength = data_size;
+ return TRUE;
+}
+
+NTSTATUS
+_cdecl
+ParseResponse(
+ char* FormatString,
+ ... // format specific parameters
+ )
+/*++
+
+Routine Description:
+
+ This routine parse an NCP response.
+
+Arguments:
+
+ FormatString - supplies the information needed to create the request to the
+ server. The first byte indicates the packet type and the
+ following bytes contain field types.
+
+ Field types, request/response:
+
+ 'b' byte ( byte* )
+ 'w' hi-lo word ( word* )
+ 'd' hi-lo dword ( dword* )
+ '-' zero/skip byte ( void )
+ '=' zero/skip word ( void )
+ ._. zero/skip string ( word )
+ 'p' pstring ( char* )
+ 'c' cstring ( char* )
+ 'r' raw bytes ( byte*, word )
+ 'R' ASCIIZ to Unicode ( UNICODE_STRING *, word )
+
+Return Value:
+
+ STATUS - The converted error code from the NCP response.
+
+--*/
+
+{
+ PCHAR FormatByte;
+ va_list Arguments;
+ int Length = 0;
+
+ va_start( Arguments, FormatString );
+
+ FormatByte = FormatString;
+ while ( *FormatByte ) {
+
+ switch ( *FormatByte ) {
+
+ case '-':
+ Length += 1;
+ break;
+
+ case '=':
+ Length += 2;
+ break;
+
+ case '_':
+ {
+ USHORT l = va_arg ( Arguments, USHORT );
+ Length += l;
+ break;
+ }
+
+ case 'b':
+ {
+ UCHAR* b = va_arg ( Arguments, UCHAR* );
+ *b = Buffer2[Length++];
+ break;
+ }
+
+ case 'w':
+ {
+ UCHAR* b = va_arg ( Arguments, UCHAR* );
+ b[1] = Buffer2[Length++];
+ b[0] = Buffer2[Length++];
+ break;
+ }
+
+ case 'd':
+ {
+ UCHAR* b = va_arg ( Arguments, UCHAR* );
+ b[3] = Buffer2[Length++];
+ b[2] = Buffer2[Length++];
+ b[1] = Buffer2[Length++];
+ b[0] = Buffer2[Length++];
+ break;
+ }
+
+ case 'c':
+ {
+ char* c = va_arg ( Arguments, char* );
+ USHORT l = strlen( &Buffer2[Length] );
+ memcpy ( c, &Buffer2[Length], l+1 );
+ Length += l+1;
+ break;
+ }
+
+ case 'p':
+ {
+ char* c = va_arg ( Arguments, char* );
+ UCHAR l = Buffer2[Length++];
+ memcpy ( c, &Buffer2[Length], l );
+ c[l+1] = 0;
+ break;
+ }
+
+#if 0
+ case 'P':
+ {
+ PUNICODE_STRING pUString = va_arg ( Arguments, PUNICODE_STRING );
+ OEM_STRING OemString;
+
+ OemString.Length = Buffer2[Length++];
+ OemString.Buffer = &Buffer2[Length];
+
+ Status = RtlOemStringToCountedUnicodeString( pUString, &OemString, FALSE );
+ ASSERT( NT_SUCCESS( Status ));
+
+ break;
+ }
+#endif
+
+ case 'r':
+ {
+ UCHAR* b = va_arg ( Arguments, UCHAR* );
+ USHORT l = va_arg ( Arguments, USHORT );
+ RtlCopyMemory( b, &Buffer2[Length], l );
+ Length += l;
+ break;
+ }
+
+#if 0
+ case 'R':
+ {
+ //
+ // Interpret the buffer as an ASCIIZ string. Convert
+ // it to unicode in the preallocated buffer.
+ //
+
+ PUNICODE_STRING pUString = va_arg ( Arguments, PUNICODE_STRING );
+ OEM_STRING OemString;
+ USHORT len = va_arg ( Arguments, USHORT );
+
+ OemString.Buffer = &Buffer2[Length];
+ OemString.Length = strlen( OemString.Buffer );
+ OemString.MaximumLength = OemString.Length;
+
+ Status = RtlOemStringToCountedUnicodeString( pUString, &OemString, FALSE );
+ ASSERT( NT_SUCCESS( Status ));
+ Length += len;
+ break;
+ }
+#endif
+
+ default:
+ printf ( "*****exchange: invalid response field, %x\n", *FormatByte );
+ return( FALSE );
+ }
+
+#if 0
+ if ( Length > PacketLength )
+ {
+ printf ( "*****exchange: not enough response data, %d\n", i );
+ }
+#endif
+ FormatByte++;
+ }
+
+ va_end( Arguments );
+}
+
+
+// Hex dump
+
+VOID
+dump(
+ IN PVOID far_p,
+ IN ULONG len
+ )
+/*++
+
+Routine Description:
+ Dump Min(len, MaxDump) bytes in classic hex dump style.
+
+Arguments:
+
+ IN far_p - address of buffer to start dumping from.
+
+ IN len - length in bytes of buffer.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ ULONG l;
+ char s[80], t[80];
+ PCHAR far_pchar = (PCHAR)far_p;
+
+ if (len > MaxDump)
+ len = MaxDump;
+
+ while (len) {
+ l = len < 17 ? len : 17;
+
+ // printf("\n%lx ", far_pchar);
+ HexDumpLine (far_pchar, l, s, t, 0);
+ // printf("%s%.*s%s", s, 1 + ((16 - l) * 3), "", t);
+ printf("%s", s);
+
+ len -= l;
+ far_pchar += l;
+ }
+ printf("\n");
+}
+
+VOID
+HexDumpLine (
+ PCHAR pch,
+ ULONG len,
+ PCHAR s,
+ PCHAR t,
+ USHORT flag
+ )
+{
+ static UCHAR rghex[] = "0123456789ABCDEF";
+
+ UCHAR c;
+ UCHAR *hex, *asc;
+
+
+ hex = s;
+ asc = t;
+
+ *(asc++) = '*';
+ while (len--) {
+ c = *(pch++);
+ *(hex++) = rghex [c >> 4] ;
+ *(hex++) = rghex [c & 0x0F];
+ *(hex++) = ' ';
+ *(asc++) = (c < ' ' || c > '~') ? (CHAR )'.' : c;
+ }
+ *(asc++) = '*';
+ *asc = 0;
+ *hex = 0;
+
+ flag;
+}
+
+// End Hex dump
+
+UCHAR Table[] =
+{0x7,0x8,0x0,0x8,0x6,0x4,0xE,0x4,0x5,0xC,0x1,0x7,0xB,0xF,0xA,0x8,
+ 0xF,0x8,0xC,0xC,0x9,0x4,0x1,0xE,0x4,0x6,0x2,0x4,0x0,0xA,0xB,0x9,
+ 0x2,0xF,0xB,0x1,0xD,0x2,0x1,0x9,0x5,0xE,0x7,0x0,0x0,0x2,0x6,0x6,
+ 0x0,0x7,0x3,0x8,0x2,0x9,0x3,0xF,0x7,0xF,0xC,0xF,0x6,0x4,0xA,0x0,
+ 0x2,0x3,0xA,0xB,0xD,0x8,0x3,0xA,0x1,0x7,0xC,0xF,0x1,0x8,0x9,0xD,
+ 0x9,0x1,0x9,0x4,0xE,0x4,0xC,0x5,0x5,0xC,0x8,0xB,0x2,0x3,0x9,0xE,
+ 0x7,0x7,0x6,0x9,0xE,0xF,0xC,0x8,0xD,0x1,0xA,0x6,0xE,0xD,0x0,0x7,
+ 0x7,0xA,0x0,0x1,0xF,0x5,0x4,0xB,0x7,0xB,0xE,0xC,0x9,0x5,0xD,0x1,
+ 0xB,0xD,0x1,0x3,0x5,0xD,0xE,0x6,0x3,0x0,0xB,0xB,0xF,0x3,0x6,0x4,
+ 0x9,0xD,0xA,0x3,0x1,0x4,0x9,0x4,0x8,0x3,0xB,0xE,0x5,0x0,0x5,0x2,
+ 0xC,0xB,0xD,0x5,0xD,0x5,0xD,0x2,0xD,0x9,0xA,0xC,0xA,0x0,0xB,0x3,
+ 0x5,0x3,0x6,0x9,0x5,0x1,0xE,0xE,0x0,0xE,0x8,0x2,0xD,0x2,0x2,0x0,
+ 0x4,0xF,0x8,0x5,0x9,0x6,0x8,0x6,0xB,0xA,0xB,0xF,0x0,0x7,0x2,0x8,
+ 0xC,0x7,0x3,0xA,0x1,0x4,0x2,0x5,0xF,0x7,0xA,0xC,0xE,0x5,0x9,0x3,
+ 0xE,0x7,0x1,0x2,0xE,0x1,0xF,0x4,0xA,0x6,0xC,0x6,0xF,0x4,0x3,0x0,
+ 0xC,0x0,0x3,0x6,0xF,0x8,0x7,0xB,0x2,0xD,0xC,0x6,0xA,0xA,0x8,0xD};
+
+UCHAR Keys[32] =
+{0x48,0x93,0x46,0x67,0x98,0x3D,0xE6,0x8D,
+ 0xB7,0x10,0x7A,0x26,0x5A,0xB9,0xB1,0x35,
+ 0x6B,0x0F,0xD5,0x70,0xAE,0xFB,0xAD,0x11,
+ 0xF4,0x47,0xDC,0xA7,0xEC,0xCF,0x50,0xC0};
+
+#define XorArray( DEST, SRC ) { \
+ PULONG D = (PULONG)DEST; \
+ PULONG S = (PULONG)SRC; \
+ int i; \
+ for ( i = 0; i <= 7 ; i++ ) { \
+ D[i] ^= S[i]; \
+ } \
+}
+
+VOID
+RespondToChallenge(
+ IN PUCHAR achObjectId,
+ IN POEM_STRING Password,
+ IN PUCHAR pChallenge,
+ OUT PUCHAR pResponse
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes the ObjectId and Challenge key from the server and
+ encrypts the user supplied password to develop a credential for the
+ server to verify.
+
+Arguments:
+ IN PUCHAR achObjectId - Supplies the 4 byte user's bindery object id
+ IN POEM_STRING Password - Supplies the user's uppercased password
+ IN PUCHAR pChallenge - Supplies the 8 byte challenge key
+ OUT PUCHAR pResponse - Returns the 8 byte response
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ int index;
+ UCHAR achK[32];
+ UCHAR achBuf[32];
+
+ Shuffle(achObjectId, Password->Buffer, Password->Length, achBuf);
+ Shuffle( &pChallenge[0], achBuf, 16, &achK[0] );
+ Shuffle( &pChallenge[4], achBuf, 16, &achK[16] );
+
+ for (index = 0; index < 16; index++)
+ achK[index] ^= achK[31-index];
+
+ for (index = 0; index < 8; index++)
+ pResponse[index] = achK[index] ^ achK[15-index];
+}
+
+VOID
+RespondToChallengePart1(
+ IN PUCHAR achObjectId,
+ IN POEM_STRING Password,
+ OUT PUCHAR pResponse
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes the ObjectId and Challenge key from the server and
+ encrypts the user supplied password to develop a credential for the
+ server to verify.
+
+Arguments:
+ IN PUCHAR achObjectId - Supplies the 4 byte user's bindery object id
+ IN POEM_STRING Password - Supplies the user's uppercased password
+ IN PUCHAR pChallenge - Supplies the 8 byte challenge key
+ OUT PUCHAR pResponse - Returns the 16 byte response held by the server
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ UCHAR achBuf[32];
+
+ Shuffle(achObjectId, Password->Buffer, Password->Length, achBuf);
+ memmove(pResponse, achBuf, 16);
+}
+
+VOID
+RespondToChallengePart2(
+ IN PUCHAR pResponsePart1,
+ IN PUCHAR pChallenge,
+ OUT PUCHAR pResponse
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes the result of Shuffling the ObjectId and the Password
+ and processes it with a challenge key.
+
+Arguments:
+ IN PUCHAR pResponsePart1 - Supplies the 16 byte output of
+ RespondToChallengePart1.
+ IN PUCHAR pChallenge - Supplies the 8 byte challenge key
+ OUT PUCHAR pResponse - Returns the 8 byte response
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ int index;
+ UCHAR achK[32];
+
+ Shuffle( &pChallenge[0], pResponsePart1, 16, &achK[0] );
+ Shuffle( &pChallenge[4], pResponsePart1, 16, &achK[16] );
+
+ for (index = 0; index < 16; index++)
+ achK[index] ^= achK[31-index];
+
+ for (index = 0; index < 8; index++)
+ pResponse[index] = achK[index] ^ achK[15-index];
+}
+
+VOID
+Shuffle(
+ UCHAR *achObjectId,
+ UCHAR *szUpperPassword,
+ int iPasswordLen,
+ UCHAR *achOutputBuffer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine shuffles around the object ID with the password
+
+Arguments:
+
+ IN achObjectId - Supplies the 4 byte user's bindery object id
+
+ IN szUpperPassword - Supplies the user's uppercased password on the
+ first call to process the password. On the second and third calls
+ this parameter contains the OutputBuffer from the first call
+
+ IN iPasswordLen - length of uppercased password
+
+ OUT achOutputBuffer - Returns the 8 byte sub-calculation
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ int iTempIndex;
+ int iOutputIndex;
+ UCHAR achTemp[32];
+
+ //
+ // Initialize the achTemp buffer. Initialization consists of taking
+ // the password and dividing it up into chunks of 32. Any bytes left
+ // over are the remainder and do not go into the initialization.
+ //
+ // achTemp[0] = szUpperPassword[0] ^ szUpperPassword[32] ^ szUpper...
+ // achTemp[1] = szUpperPassword[1] ^ szUpperPassword[33] ^ szUpper...
+ // etc.
+ //
+
+ if ( iPasswordLen > 32) {
+
+ // At least one chunk of 32. Set the buffer to the first chunk.
+
+ RtlCopyMemory( achTemp, szUpperPassword, 32 );
+
+ szUpperPassword +=32; // Remove the first chunk
+ iPasswordLen -=32;
+
+ while ( iPasswordLen >= 32 ) {
+ //
+ // Xor this chunk with the characters already loaded into
+ // achTemp.
+ //
+
+ XorArray( achTemp, szUpperPassword);
+
+ szUpperPassword +=32; // Remove this chunk
+ iPasswordLen -=32;
+ }
+
+ } else {
+
+ // No chunks of 32 so set the buffer to zero's
+
+ RtlZeroMemory( achTemp, sizeof(achTemp));
+
+ }
+
+ //
+ // achTemp is now initialized. Load the remainder into achTemp.
+ // The remainder is repeated to fill achTemp.
+ //
+ // The corresponding character from Keys is taken to seperate
+ // each repitition.
+ //
+ // As an example, take the remainder "ABCDEFG". The remainder is expanded
+ // to "ABCDEFGwABCDEFGxABCDEFGyABCDEFGz" where w is Keys[7],
+ // x is Keys[15], y is Keys[23] and z is Keys[31].
+ //
+ //
+
+ if (iPasswordLen > 0) {
+ int iPasswordOffset = 0;
+ for (iTempIndex = 0; iTempIndex < 32; iTempIndex++) {
+
+ if (iPasswordLen == iPasswordOffset) {
+ iPasswordOffset = 0;
+ achTemp[iTempIndex] ^= Keys[iTempIndex];
+ } else {
+ achTemp[iTempIndex] ^= szUpperPassword[iPasswordOffset++];
+ }
+ }
+ }
+
+ //
+ // achTemp has been loaded with the users password packed into 32
+ // bytes. Now take the objectid that came from the server and use
+ // that to munge every byte in achTemp.
+ //
+
+ for (iTempIndex = 0; iTempIndex < 32; iTempIndex++)
+ achTemp[iTempIndex] ^= achObjectId[ iTempIndex & 3];
+
+ Scramble( Scramble( 0, achTemp ), achTemp );
+
+ //
+ // Finally take pairs of bytes in achTemp and return the two
+ // nibbles obtained from Table. The pairs of bytes used
+ // are achTemp[n] and achTemp[n+16].
+ //
+
+ for (iOutputIndex = 0; iOutputIndex < 16; iOutputIndex++) {
+
+ achOutputBuffer[iOutputIndex] =
+ Table[achTemp[iOutputIndex << 1]] |
+ (Table[achTemp[(iOutputIndex << 1) + 1]] << 4);
+ }
+
+ return;
+}
+
+int
+Scramble(
+ int iSeed,
+ UCHAR achBuffer[32]
+ )
+
+/*++
+
+Routine Description:
+
+ This routine scrambles around the contents of the buffer. Each buffer
+ position is updated to include the contents of at least two character
+ positions plus an EncryptKey value. The buffer is processed left to right
+ and so if a character position chooses to merge with a buffer position
+ to its left then this buffer position will include bits derived from at
+ least 3 bytes of the original buffer contents.
+
+Arguments:
+
+ IN iSeed
+ IN OUT achBuffer[32]
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ int iBufferIndex;
+
+ for (iBufferIndex = 0; iBufferIndex < 32; iBufferIndex++) {
+ achBuffer[iBufferIndex] =
+ (UCHAR)(
+ ((UCHAR)(achBuffer[iBufferIndex] + iSeed)) ^
+ ((UCHAR)( achBuffer[(iBufferIndex+iSeed) & 31] -
+ Keys[iBufferIndex] )));
+
+ iSeed += achBuffer[iBufferIndex];
+ }
+ return iSeed;
+}
+
+#if 0
+
+// this data was gathered for object id = 9000095 from yihsins3 with
+// this program by reseting the password to null manually and then
+// sending over the specified Vc
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+Vnew= DC 9D A3 3C 61 CA 98 F2 BB B5 AE 34 04 43 B0 C9 1B
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11
+Vnew= 3F F0 CF 12 36 49 C1 29 93 4E 9A 58 52 C8 3D 13 0A
+
+File= 3F F0 CF 12 36 49 C1 29 93 4E 9A 58 52 C8 3D 13 0A
+ Vc= 22 22 22 22 22 22 22 22 22 22 22 22 22 22 22 22 22
+Vnew= C9 BC 9A 26 58 7F 1F 4D D0 A4 77 B7 B8 39 72 FB 39
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33
+Vnew= 7E DB 66 08 DC 51 3A 81 FD 8B 2B C5 8F 8C 49 9D 28
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44
+Vnew= 4D A9 25 B4 BB 18 F3 3A 86 3D 06 23 77 AA 1E D8 1F
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55
+Vnew= A5 43 BC 91 C9 0B 79 BF CF CC 58 00 E5 1F C1 81 0E
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= 66 66 66 66 66 66 66 66 66 66 66 66 66 66 66 66 66
+Vnew= 83 51 01 73 EF 27 66 13 6C 6F E4 7F C9 2D 04 BE 3D
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77
+Vnew= 21 CF 82 F7 93 A6 47 76 19 06 85 4D 4D FB 23 4F 2C
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88
+Vnew= 54 3A FE ED 72 9D BB 65 45 E3 3D 61 1B 04 6A 37 13
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99
+Vnew= 18 08 47 5E 4A F0 D0 9E 77 9A 69 92 31 04 6A 37 02
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA
+Vnew= E0 87 3B 85 04 B4 55 AB 58 22 FF AB F0 76 F6 74 31
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= BB BB BB BB BB BB BB BB BB BB BB BB BB BB BB BB BB
+Vnew= F2 15 D9 D9 A0 D3 02 0C 01 F0 B1 FE 63 D2 EF E6 20
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
+Vnew= 9B 76 70 4B 87 65 E4 C8 22 59 D0 19 9A B7 88 60 17
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD
+Vnew= B7 E4 E4 A0 25 3C 8D 50 AE 17 C2 EC AE 90 A7 52 06
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE
+Vnew= 06 2E 18 6F FD 82 2C E7 E4 78 4C DA DC E1 9C A5 35
+
+File= 06 2E 18 6F FD 82 2C E7 E4 78 4C DA DC E1 9C A5 35
+ Vc= FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
+Vnew= 6A 62 5D CA 1E EE AE D4 3A D1 13 86 26 6E 5B 2A 24
+
+
+0xEA, 0x91, 0x6C, 0x3D, 0xAB, 0x59, 0xB9, 0xBD, 0xB2, 0x7B, 0x4C, 0x , 0x , 0x , 0x , 0x , 0x , //0//
+0x97, 0xB6, 0xE6, 0x15, 0xF0, 0x43, 0x21, 0x63, 0x7B, 0xDF, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //1//
+0x7B, 0xEF, 0x47, 0x21, 0xD8, 0x6E, 0xEB, 0x10, 0xCC, 0xAA, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //2//
+0x16, 0x85, 0xA0, 0x06, 0x17, 0xDB, 0x34, 0x46, 0xF1, 0x48, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //3//
+0x48, 0x5D, 0x9D, 0xC4, 0x9A, 0x1A, 0x7C, 0x2F, 0x8E, 0x12, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //4//
+0x85, 0x6B, 0xF4, 0x9A, 0x2D, 0x3C, 0xAA, 0xD8, 0xA8, 0xC0, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //5//
+0xFE, 0xFC, 0x33, 0xE2, 0x01, 0xC7, 0x66, 0x87, 0x64, 0x67, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //6//
+0x3D, 0xCA, 0xC9, 0x67, 0x8C, 0x26, 0x57, 0x7E, 0x99, 0xED, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //7//
+0x69, 0xA9, 0x7E, 0xA3, 0xC2, 0xE4, 0xD0, 0x3C, 0x4A, 0x3E, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //8//
+0xC2, 0x04, 0x2B, 0x5B, 0x75, 0x81, 0x05, 0x91, 0x17, 0x9C, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //9//
+0x5F, 0x48, 0x02, 0xDF, 0xB9, 0x70, 0xF3, 0xA4, 0xDF, 0x29, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //A//
+0xDC, 0x23, 0x5A, 0x4C, 0x44, 0xA5, 0x88, 0x5A, 0x00, 0x03, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //B//
+0x20, 0x72, 0x15, 0xF0, 0x53, 0x0D, 0x1E, 0xCB, 0x56, 0x55, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //C//
+0x04, 0x30, 0xBF, 0xB8, 0x3E, 0xB8, 0x9D, 0xF2, 0x23, 0xF4, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //D//
+0xA3, 0xDE, 0xD8, 0x89, 0x6F, 0xFF, 0xF3, 0xE9, 0xED, 0x81, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //E//
+0xB1, 0x17, 0x81, 0x7E, 0xE6, 0x92, 0x42, 0x05, 0x35, 0xB6, 0x , 0x , 0x , 0x , 0x , 0x , 0x //F//
+ };
+ 00 = AE 34 04 43 B0 C9 1B
+ 11 = 9A 58 52 C8 3D 13 0A
+ 22 = 77 B7 B8 39 72 FB 39
+ 33 = 2B C5 8F 8C 49 9D 28
+ 44 = 06 23 77 AA 1E D8 1F
+ 55 = 58 00 E5 1F C1 81 0E
+ 66 = E4 7F C9 2D 04 BE 3D
+ 77 = 85 4D 4D FB 23 4F 2C
+ 88 = 3D 61 1B 04 6A 37 13
+ 99 = 69 92 31 55 D5 0C 02
+ AA = FF AB F0 76 F6 74 31
+ BB = B1 FE 63 D2 EF E6 20
+ CC = D0 19 9A B7 88 60 17
+ DD = C2 EC AE 90 A7 52 06
+ EE = 4C DA DC E1 9C A5 35
+ FF = 13 86 26 6E 5B 2A 24
+
+
+
+
+#endif
+
diff --git a/private/nw/test/tp4.c b/private/nw/test/tp4.c
new file mode 100644
index 000000000..6788bbcba
--- /dev/null
+++ b/private/nw/test/tp4.c
@@ -0,0 +1,1668 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ tex.c
+
+Abstract:
+
+ User mode test program for the Microsoft Netware redir file system.
+
+ This test program can be built from the command line using the
+ command 'nmake UMTEST=tex'.
+
+Author:
+
+ Manny Weiser (mannyw) 7-Jun-1993
+
+Revision History:
+
+--*/
+
+#include <conio.h>
+#include <stdio.h>
+#include <string.h>
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <ntddnwfs.h>
+#include <STDARG.H>
+
+#define OT_USER 1
+
+
+// Hex dump
+ULONG MaxDump = 256;
+
+VOID
+dump(
+ IN PVOID far_p,
+ IN ULONG len
+ );
+
+VOID
+HexDumpLine (
+ PCHAR pch,
+ ULONG len,
+ PCHAR s,
+ PCHAR t,
+ USHORT flag
+ );
+
+
+// End Hex dump
+
+VOID
+ResetPassword(
+ PUCHAR Vnew
+ );
+
+UCHAR
+LookUp(
+ UCHAR Value,
+ UCHAR Mask,
+ int index
+ );
+
+SendMessage(
+ IN char* Format,
+ ...
+ );
+
+NTSTATUS
+FormatRequest(
+ PCHAR SendBuffer,
+ PULONG SendBufferLength,
+ char* Format,
+ va_list a
+ );
+
+BOOLEAN
+OpenServer(
+ PHANDLE Handle,
+ PWCH ServerName
+ );
+
+NTSTATUS
+_cdecl
+ParseResponse(
+ char* FormatString,
+ ... // format specific parameters
+ );
+
+VOID
+Shuffle(
+ UCHAR *achObjectId,
+ UCHAR *szUpperPassword,
+ int iPasswordLen,
+ UCHAR *achOutputBuffer
+ );
+
+int
+Scramble(
+ int iSeed,
+ UCHAR achBuffer[32]
+ );
+
+VOID
+RespondToChallenge(
+ IN PUCHAR achObjectId,
+ IN POEM_STRING Password,
+ IN PUCHAR pChallenge,
+ OUT PUCHAR pResponse
+ );
+
+VOID
+RespondToChallengePart1(
+ IN PUCHAR achObjectId,
+ IN POEM_STRING Password,
+ OUT PUCHAR pResponse
+ );
+
+VOID
+RespondToChallengePart2(
+ IN PUCHAR pResponsePart1,
+ IN PUCHAR pChallenge,
+ OUT PUCHAR pResponse
+ );
+
+NTSTATUS
+GetCurrentPasswordValue (
+ IN OUT PCHAR OutputValue
+ );
+
+#define BUFFER_SIZE 200
+
+//WCHAR *ServerName = L"YIHSINS3";
+WCHAR *ServerName = L"MARS312";
+UCHAR Pid = 255;
+UCHAR Object[4];
+
+WCHAR FileNameBuffer[100];
+UNICODE_STRING FileName;
+
+//#define NW_USER "ANDYHE"
+#define NW_USER "COLINW"
+
+
+HANDLE ServerHandle;
+_cdecl
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ BOOLEAN success;
+ UCHAR Challenge[8];
+ UCHAR fileValue[17];
+ UCHAR ResponseP2[16];
+ int x, y;
+ NTSTATUS status;
+
+
+#if 0
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+ 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
+ 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
+ 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+
+#endif
+
+#define START_POSITION 0x00
+
+ UCHAR Vold[] = {
+0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x11,
+0x00, 0x01, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x10, 0xe0, 0x00, 0x00, 0x11,
+0x00, 0x02, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x20, 0xd0, 0x00, 0x00, 0x11,
+0x00, 0x03, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x30, 0xc0, 0x00, 0x00, 0x11,
+0x00, 0x04, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x40, 0xb0, 0x00, 0x00, 0x11,
+0x00, 0x05, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x50, 0xa0, 0x00, 0x00, 0x11,
+0x00, 0x06, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x60, 0x90, 0x00, 0x00, 0x11,
+0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x70, 0x80, 0x00, 0x00, 0x11,
+0x00, 0x08, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x80, 0x70, 0x00, 0x00, 0x11,
+0x00, 0x09, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0x90, 0x60, 0x00, 0x00, 0x11,
+0x00, 0x0a, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0xa0, 0x50, 0x00, 0x00, 0x11,
+0x00, 0x0b, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0xb0, 0x40, 0x00, 0x00, 0x11,
+0x00, 0x0c, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0xc0, 0x30, 0x00, 0x00, 0x11,
+0x00, 0x0d, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0xd0, 0x20, 0x00, 0x00, 0x11,
+0x00, 0x0e, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0xe0, 0x10, 0x00, 0x00, 0x11,
+0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x11,
+0x50, 0x60, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x00,0x50, 0x60, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x00, 0x11,
+0x50, 0x61, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x00,0x50, 0x60, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x10, 0x11,
+0x50, 0x62, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x00,0x50, 0x60, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x20, 0x11,
+0x50, 0x63, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x00,0x50, 0x60, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x30, 0x11,
+0x50, 0x64, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x00,0x50, 0x60, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x40, 0x11,
+0x50, 0x65, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x00,0x50, 0x60, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x50, 0x11,
+0x50, 0x66, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x00,0x50, 0x60, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x60, 0x11,
+0x50, 0x67, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x00,0x50, 0x60, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x70, 0x11,
+0x50, 0x68, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x00,0x50, 0x60, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x80, 0x11,
+0x50, 0x69, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x00,0x50, 0x60, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x90, 0x11,
+0x50, 0x6a, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x00,0x50, 0x60, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0xa0, 0x11,
+0x50, 0x6b, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x00,0x50, 0x60, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0xb0, 0x11,
+0x50, 0x6c, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x00,0x50, 0x60, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0xc0, 0x11,
+0x50, 0x6d, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x00,0x50, 0x60, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0xd0, 0x11,
+0x50, 0x6e, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x00,0x50, 0x60, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0xe0, 0x11,
+0x50, 0x6f, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0x00,0x50, 0x60, 0x00, 0x09, 0x0a, 0x0b, 0x50, 0xf0, 0x11
+ };
+
+ UCHAR Vc[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+#if 0
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22,
+ 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33,
+ 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44, 0x44,
+ 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
+ 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
+ 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
+ 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x99,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb, 0xbb,
+ 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
+ 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
+ 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+#endif
+ };
+
+ success = OpenServer( &ServerHandle, ServerName );
+ if ( !success) {
+ return 1;
+ }
+
+ printf("Opened server, %wS\n", ServerName );
+
+ //
+ // Setup the file name that we'll be opening every time.
+ //
+
+ FileName.Buffer = FileNameBuffer;
+ FileName.Length = 0;
+ FileName.MaximumLength = 100;
+
+ status = RtlAppendUnicodeToString( &FileName, DD_NWFS_DEVICE_NAME_U );
+ ASSERT( status == STATUS_SUCCESS );
+ status = RtlAppendUnicodeToString( &FileName, L"\\" );
+ status = RtlAppendUnicodeToString( &FileName, ServerName );
+ status = RtlAppendUnicodeToString( &FileName, L"\\SYS:\\SYSTEM\\NET$VAL.SYS" );
+
+ // Get objectid
+ SendMessage(
+ "Swp",
+ 0x17, 0x35,
+ OT_USER,
+ NW_USER);
+
+ ParseResponse("r", Object, 4 );
+
+ // jump into the middle of a run...
+
+ x = 0;
+ y = START_POSITION * 17;
+ goto start;
+
+ for ( x = 0 ; x < sizeof(Vold) ; x+= 17 ) {
+
+ for ( y = 0 ; y < sizeof(Vc) ; y+= 17 ) {
+
+start:
+ //
+ // Set the password on the server to be the value of Vold required for
+ // the second part of the test.
+ //
+
+ ResetPassword( Vold + x );
+
+ //
+ // We now know that Vold on the server is the same as the Vold vector.
+ // We can now set the password to any Vnew.
+ // We can do this without the real password because the
+ // server will believe we know the real password if we give it
+ // the result of taking Vold and the Challenge key and passing
+ // it through RespondToChallengePart2
+ //
+
+ printf( "Getting a challenge key.... " );
+
+ SendMessage(
+ "S",
+ 0x17, 0x17);
+
+ ParseResponse("r", Challenge, sizeof(Challenge) );
+
+ // Fabricate the number the server will use to validate the password change
+ RespondToChallengePart2( Vold + x, Challenge, ResponseP2 );
+
+ printf( "Vold= " ); dump( Vold + x, 17);
+
+ // Put out results to console...
+
+ status = GetCurrentPasswordValue( &fileValue[0] );
+
+ if ( NT_SUCCESS( status ) ) {
+
+ printf( "File= " );
+ dump( fileValue, 17);
+ }
+ printf( " Vc= "); dump( Vc + y, 17);
+
+ printf( "Setting the new password... " );
+
+ // Send the set password
+ SendMessage("Srwpr", 0x17, 0x4b,
+ ResponseP2, 8,
+ OT_USER,
+ NW_USER,
+ Vc + y, 17 );
+
+ status = GetCurrentPasswordValue( &fileValue[0] );
+
+ if ( NT_SUCCESS( status ) ) {
+
+ printf( "Vnew= ");
+ dump( fileValue, 17);
+ }
+
+// printf( "Press a key...\n" ); getch();
+
+// printf( "\n" );
+ printf( "\n\n" );
+ }
+ }
+
+ printf( "%s exiting\n", argv[0]);
+ return 0;
+}
+
+NTSTATUS
+GetCurrentPasswordValue (
+ IN OUT PCHAR OutputValue
+ )
+//
+// This routine reads the current password from the file.
+//
+{
+ HANDLE fileHandle;
+ LARGE_INTEGER filePosition;
+ OBJECT_ATTRIBUTES objectAttributes;
+ IO_STATUS_BLOCK ioStatusBlock;
+ int i;
+ PCHAR ch;
+ NTSTATUS status;
+
+ ch = OutputValue;
+ for (i = 0; i < 17; i++) {
+
+ *(ch++) = '0';
+ }
+
+ // Send a Close Bindery to the server
+
+ printf( "Closing the bindery... " );
+
+ SendMessage(
+ "S",
+ 0x17, 0x44);
+
+ //
+ // Open the server\sys:system\net$val.sys file which the server just
+ // closed.
+ //
+
+ InitializeObjectAttributes(
+ &objectAttributes,
+ &FileName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ status = NtOpenFile (
+ &fileHandle,
+ FILE_GENERIC_READ | SYNCHRONIZE,
+ &objectAttributes,
+ &ioStatusBlock,
+ FILE_SHARE_WRITE | FILE_SHARE_READ,
+ 0L
+ );
+
+ if (!NT_SUCCESS(status) ) {
+
+ printf( " Open failed. Status = 0x%x\n", status );
+
+ } else {
+
+ //
+ // Seek to the spot in the file that has the supervisor's password.
+ // This will be different for different servers. Here's a table :
+ // ObjectID = Supervisor AndyHe
+ // Netware311 : 0x097C
+ // Mars312 : 0x16CC
+ // YihsinS3 : 0x59EA 0x050C
+ //
+
+ filePosition.HighPart = 0L;
+ filePosition.LowPart = 0x050C;
+
+ // Read the 17 bytes at that offset and close the file
+
+ status = NtReadFile (
+ fileHandle,
+ NULL,
+ NULL,
+ NULL,
+ &ioStatusBlock,
+ OutputValue,
+ 17,
+ &filePosition,
+ NULL
+ );
+
+ if (!NT_SUCCESS(status) ) {
+
+ printf( " Read failed. Status = 0x%x\n", status );
+ }
+
+ NtClose( fileHandle );
+ }
+
+ // Send an Open Bindery to the server
+
+ printf( "Opening the bindery... " );
+ SendMessage(
+ "S",
+ 0x17, 0x45);
+
+ return(status);
+}
+
+VOID
+ResetPassword(
+ PUCHAR Vnew
+ )
+{
+//
+// Note Vc[0] specifies the length. To derive new values of Vnew use the following
+// table. For each nibble desired in Vnew, scan the appropriate column and
+// read off the corresponding Vc nibble value on the left.
+//
+
+ UCHAR Vc[17];
+
+ UCHAR Challenge[8];
+ OEM_STRING Password = {0,0,""};
+ UCHAR EncryptKey[8];
+
+ int x;
+
+ //printf( "Get Login key for set password using NULL password\n");
+
+ printf( "Getting a challenge key.... " );
+
+ SendMessage(
+ "S",
+ 0x17, 0x17);
+
+ ParseResponse("r", Challenge, sizeof(Challenge) );
+
+ RespondToChallenge( Object, &Password, Challenge, EncryptKey );
+ // dump( EncryptKey, 8);
+
+ //
+ // Build Vc so that the server will get Vnew stored in the bindery.
+ // Note Vols/Vnew have the length at V[17] whereas Vc has it at Vc[0].
+ //
+
+ for ( x = 0 ; x < 16; x++ ) {
+ Vc[x+1] = LookUp( Vnew[x], 0xf0, x) | LookUp( Vnew[x], 0x0f, x);
+ }
+
+ Vc[0] = LookUp( Vnew[16], 0xf0, 16) | LookUp( Vnew[16], 0x0f, 16);
+
+ //printf( "Vnew "); dump( Vnew, 17);
+ //printf( "Vc "); dump( Vc, 17);
+
+ //
+ // This uses the null password, objectid=1 and our supervisor privilege
+ // to set it, regardless of the and our ability to figure out what
+ // Vnew will be created for a particular Vc.
+ //
+
+ printf( "Setting the old password... " );
+
+ SendMessage("Srwpr", 0x17, 0x4b,
+ EncryptKey, sizeof(EncryptKey),
+ OT_USER,
+ NW_USER,
+ Vc, 17 );
+
+ ParseResponse( "" );
+}
+
+UCHAR
+LookUp(
+ UCHAR Value,
+ UCHAR Mask,
+ int index
+ )
+{
+
+#if 0
+
+UCHAR Vtab[] = {
+
+ // This table is for the supervisor
+
+0x97, 0xE3, 0x73, 0xD8, 0x70, 0x5B, 0x85, 0xD3, 0xF5, 0xCD, 0xDB, 0xFA, 0xDC, 0xEB, 0x98, 0x64, 0x18, //0//
+0x31, 0x6B, 0x88, 0x1E, 0x88, 0x09, 0x10, 0x29, 0x0B, 0x84, 0x61, 0x03, 0x43, 0xA1, 0xD5, 0x5A, 0x09, //1//
+0xCB, 0xFF, 0x67, 0x70, 0xB6, 0xE3, 0x4E, 0x9A, 0x63, 0x27, 0x1A, 0x3C, 0x5A, 0x9A, 0x3B, 0xFC, 0x3A, //2//
+0xEF, 0xA8, 0x44, 0x42, 0xDB, 0x1E, 0x59, 0x31, 0x17, 0xEB, 0xF5, 0x99, 0x18, 0x8E, 0x63, 0xAE, 0x2B, //3//
+0x23, 0x07, 0xEB, 0xC9, 0xCD, 0xD6, 0xC7, 0xCF, 0xC8, 0xBF, 0x0F, 0xE2, 0x01, 0x16, 0x42, 0xD3, 0x1C, //4//
+0x8A, 0xC4, 0x06, 0xB6, 0x51, 0xCA, 0x6F, 0xA7, 0x2E, 0x33, 0x82, 0xBD, 0x65, 0x58, 0xC0, 0xCB, 0x0D, //5//
+0x0D, 0xBD, 0x5D, 0x65, 0x92, 0x77, 0x0D, 0x50, 0xAC, 0x79, 0x40, 0x86, 0x34, 0x45, 0x8F, 0x42, 0x3E, //6//
+0x12, 0x8C, 0xA0, 0xAF, 0x07, 0xA1, 0xD3, 0x7D, 0x86, 0x5C, 0xA3, 0x2B, 0x80, 0x3F, 0x1D, 0x29, 0x2F, //7//
+0x68, 0x15, 0xD1, 0x34, 0xE4, 0x2F, 0xF6, 0xFC, 0xEF, 0xDA, 0x5C, 0x57, 0xCF, 0xB4, 0x5A, 0x90, 0x10, //8//
+0x7C, 0x49, 0xF2, 0x5A, 0xF3, 0x34, 0xBC, 0x88, 0x3A, 0x91, 0xB8, 0xC8, 0xAD, 0x03, 0x01, 0x77, 0x01, //9//
+0xB0, 0x91, 0xBA, 0x23, 0x1E, 0x42, 0x72, 0xEB, 0xD2, 0xF8, 0x7E, 0x1E, 0xF7, 0x69, 0xE9, 0x0F, 0x32, //a//
+0xA6, 0x32, 0x9C, 0x9D, 0x49, 0xB5, 0x98, 0x4E, 0x49, 0x00, 0xC9, 0x7F, 0x92, 0xC0, 0x2E, 0x3D, 0x23, //b//
+0x54, 0x2A, 0x25, 0x07, 0x6C, 0x6C, 0xAA, 0xB2, 0xB1, 0x45, 0x27, 0xD4, 0x76, 0x27, 0xBC, 0xE8, 0x14, //c//
+0xD9, 0xD0, 0xCE, 0x8C, 0x25, 0x98, 0xEB, 0x66, 0x74, 0x62, 0xE6, 0xA0, 0xEE, 0xFD, 0xF4, 0x86, 0x05, //d//
+0xF5, 0x7E, 0x1F, 0xEB, 0x3F, 0xFD, 0x34, 0x04, 0x50, 0x16, 0x3D, 0x61, 0x29, 0xDC, 0xA7, 0xB5, 0x36, //e//
+0x4E, 0x56, 0x39, 0xF1, 0xAA, 0x80, 0x21, 0x15, 0x9D, 0xAE, 0x94, 0x45, 0xBB, 0x72, 0x76, 0x11, 0x27 //f//
+};
+
+#endif
+
+
+UCHAR Vtab[] = {
+
+ // This table is for user AndyHe on Yihsins3, object id = 9000095
+
+0xDC, 0x9D, 0xA3, 0x3C, 0x61, 0xCA, 0x98, 0xF2, 0xBB, 0xB5, 0xAE, 0x34, 0x04, 0x43, 0xB0, 0xC9, 0x1B,
+0x3F, 0xF0, 0xCF, 0x12, 0x36, 0x49, 0xC1, 0x29, 0x93, 0x4E, 0x9A, 0x58, 0x52, 0xC8, 0x3D, 0x13, 0x0A,
+0xC9, 0xBC, 0x9A, 0x26, 0x58, 0x7F, 0x1F, 0x4D, 0xD0, 0xA4, 0x77, 0xB7, 0xB8, 0x39, 0x72, 0xFB, 0x39,
+0x7E, 0xDB, 0x66, 0x08, 0xDC, 0x51, 0x3A, 0x81, 0xFD, 0x8B, 0x2B, 0xC5, 0x8F, 0x8C, 0x49, 0x9D, 0x28,
+0x4D, 0xA9, 0x25, 0xB4, 0xBB, 0x18, 0xF3, 0x3A, 0x86, 0x3D, 0x06, 0x23, 0x77, 0xAA, 0x1E, 0xD8, 0x1F,
+0xA5, 0x43, 0xBC, 0x91, 0xC9, 0x0B, 0x79, 0xBF, 0xCF, 0xCC, 0x58, 0x00, 0xE5, 0x1F, 0xC1, 0x81, 0x0E,
+0x83, 0x51, 0x01, 0x73, 0xEF, 0x27, 0x66, 0x13, 0x6C, 0x6F, 0xE4, 0x7F, 0xC9, 0x2D, 0x04, 0xBE, 0x3D,
+0x21, 0xCF, 0x82, 0xF7, 0x93, 0xA6, 0x47, 0x76, 0x19, 0x06, 0x85, 0x4D, 0x4D, 0xFB, 0x23, 0x4F, 0x2C,
+0x54, 0x3A, 0xFE, 0xED, 0x72, 0x9D, 0xBB, 0x65, 0x45, 0xE3, 0x3D, 0x61, 0x1B, 0x04, 0x6A, 0x37, 0x13,
+0x18, 0x08, 0x47, 0x5E, 0x4A, 0xF0, 0xD0, 0x9E, 0x77, 0x9A, 0x69, 0x92, 0x31, 0x55, 0xD5, 0x0C, 0x02,
+0xE0, 0x87, 0x3B, 0x85, 0x04, 0xB4, 0x55, 0xAB, 0x58, 0x22, 0xFF, 0xAB, 0xF0, 0x76, 0xF6, 0x74, 0x31,
+0xF2, 0x15, 0xD9, 0xD9, 0xA0, 0xD3, 0x02, 0x0C, 0x01, 0xF0, 0xB1, 0xFE, 0x63, 0xD2, 0xEF, 0xE6, 0x20,
+0x9B, 0x76, 0x70, 0x4B, 0x87, 0x65, 0xE4, 0xC8, 0x22, 0x59, 0xD0, 0x19, 0x9A, 0xB7, 0x88, 0x60, 0x17,
+0xB7, 0xE4, 0xE4, 0xA0, 0x25, 0x3C, 0x8D, 0x50, 0xAE, 0x17, 0xC2, 0xEC, 0xAE, 0x90, 0xA7, 0x52, 0x06,
+0x06, 0x2E, 0x18, 0x6F, 0xFD, 0x82, 0x2C, 0xE7, 0xE4, 0x78, 0x4C, 0xDA, 0xDC, 0xE1, 0x9C, 0xA5, 0x35,
+0x6A, 0x62, 0x5D, 0xCA, 0x1E, 0xEE, 0xAE, 0xD4, 0x3A, 0xD1, 0x13, 0x86, 0x26, 0x6E, 0x5B, 0x2A, 0x24
+};
+
+UCHAR Ch = Value & Mask;
+int x;
+
+//
+// Scan down the column of Vtab looking for a nibble that matches Ch
+// return the index in the nibble indicated by Mask.
+//
+
+for ( x = 0 ; x < 16 ; x++) {
+
+ if ( (Vtab[ (x*17) + index] & Mask) == Ch ) {
+
+ x |= x << 4; // Copy into both nibbles
+ return( x & Mask );
+
+ }
+}
+
+printf(" Not found Value %x, Mask %x, Index %x\n", Value, Mask, index);
+return( 0 );
+
+}
+
+BOOLEAN
+OpenServer(
+ PHANDLE Handle,
+ PWCH ServerName
+ )
+{
+ NTSTATUS status;
+ OBJECT_ATTRIBUTES objectAttributes;
+ IO_STATUS_BLOCK ioStatusBlock;
+ WCHAR ServerNameBuffer[100];
+ UNICODE_STRING ServerNameString;
+
+ ServerNameString.Buffer = ServerNameBuffer;
+ ServerNameString.Length = 0;
+ ServerNameString.MaximumLength = 100;
+
+ status = RtlAppendUnicodeToString( &ServerNameString, DD_NWFS_DEVICE_NAME_U );
+ ASSERT( status == STATUS_SUCCESS );
+ status = RtlAppendUnicodeToString( &ServerNameString, L"\\" );
+ ASSERT( status == STATUS_SUCCESS );
+ status = RtlAppendUnicodeToString( &ServerNameString, ServerName );
+ ASSERT( status == STATUS_SUCCESS );
+
+ //
+ // Open the file
+ //
+
+ InitializeObjectAttributes(
+ &objectAttributes,
+ &ServerNameString,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL
+ );
+
+ status = NtOpenFile (
+ Handle,
+ FILE_GENERIC_READ | SYNCHRONIZE,
+ &objectAttributes,
+ &ioStatusBlock,
+ FILE_SHARE_WRITE | FILE_SHARE_READ,
+ 0L
+ );
+
+ if (!NT_SUCCESS(status) ) {
+ printf( "Open status = %x for file %Z\n", status, &ServerName );
+ }
+
+ return ( (BOOLEAN) NT_SUCCESS( status ) );
+}
+
+
+CHAR Buffer1[100];
+CHAR Buffer2[100];
+
+SendMessage(
+ IN char* Format,
+ ...
+ )
+{
+ NTSTATUS status = -1;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ va_list Arguments;
+ NTSTATUS Status;
+ ULONG ReceiveBufferSize = 100;
+ ULONG SendBufferSize = 100;
+
+ va_start( Arguments, Format );
+
+
+ Status = FormatRequest( Buffer1, &SendBufferSize, Format, Arguments );
+ if ( !NT_SUCCESS( Status ) ) {
+ return( Status );
+ }
+
+ //printf("Sending message\n" );
+ //dump( Buffer1, SendBufferSize + 1);
+
+ status = NtFsControlFile(
+ ServerHandle,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ NWR_ANY_NCP(Buffer1[0]),
+ Buffer1+3,
+ SendBufferSize - 3,
+ Buffer2,
+ ReceiveBufferSize
+ );
+ if ( NT_SUCCESS( status ) ) {
+
+ status = NtWaitForSingleObject( ServerHandle, FALSE, NULL );
+ if ( NT_SUCCESS( status )) {
+ status = IoStatusBlock.Status;
+ }
+ }
+
+
+ if ( !NT_SUCCESS( status ) ) {
+ printf("SendPacket returns %08lx\n", status );
+ } else {
+ printf("Send succeeded.\n" );
+ //dump( Buffer2, IoStatusBlock.Information );
+ }
+ return( status );
+}
+
+
+NTSTATUS
+FormatRequest(
+ PCHAR SendBuffer,
+ PULONG SendBufferLength,
+ char* Format,
+ va_list a
+ )
+/*++
+
+Routine Description:
+
+ Send the packet described by Format and the additional parameters.
+
+Arguments:
+
+ Format - the information needed to create the request to the
+ server. The first byte indicates the packet type and the
+ following bytes contain field types.
+
+ Packet types:
+
+ 'A' SAP broadcast ( void )
+ 'C' NCP connect ( void )
+ 'F' NCP function ( byte )
+ 'S' NCP subfunction ( byte, byte )
+ 'D' NCP disconnect ( void )
+
+ Field types, request/response:
+
+ 'b' byte ( byte / byte* )
+ 'w' hi-lo word ( word / word* )
+ 'd' hi-lo dword ( dword / dword* )
+ '-' zero/skip byte ( void )
+ '=' zero/skip word ( void )
+ ._. zero/skip string ( word )
+ 'p' pstring ( char* )
+ 'u' p unicode string ( UNICODE_STRING * )
+ 'c' cstring ( char* )
+ 'r' raw bytes ( byte*, word )
+ 'u' p unicode string ( UNICODE_STRING * )
+ 'U' p uppercase string( UNICODE_STRING * )
+ 'f' separate fragment ( PMDL )
+
+ An 'f' field must be last, and in a response it cannot be
+ preceeded by 'p' or 'c' fields.
+
+
+Return Value:
+
+ Normally returns STATUS_PENDING.
+
+--*/
+{
+ NTSTATUS status;
+ char* pFormatCharacter;
+ USHORT t = 1;
+ ULONG data_size;
+ ULONG length;
+
+ data_size = 1;
+
+ SendBuffer[ 0 ] = va_arg( a, UCHAR );
+
+ if ( *Format == 'S' ) {
+ data_size += 2;
+ SendBuffer[data_size++] = va_arg( a, UCHAR );
+ }
+
+ pFormatCharacter = Format;
+
+ while ( *++pFormatCharacter && *pFormatCharacter != 'f' )
+ {
+ switch ( *pFormatCharacter ) {
+
+ case '=':
+ SendBuffer[data_size++] = 0;
+ case '-':
+ SendBuffer[data_size++] = 0;
+ break;
+
+ case '_':
+ length = va_arg ( a, USHORT );
+
+ if ( data_size + length > *SendBufferLength ) {
+ printf("***exch:!0!\n");
+ return( FALSE );
+ }
+
+ while ( length-- ) {
+ SendBuffer[data_size++] = 0;
+ }
+
+ break;
+
+ case 'b':
+ SendBuffer[data_size++] = va_arg ( a, UCHAR );
+ break;
+
+ case 'w':
+ {
+ USHORT w = va_arg ( a, USHORT );
+
+ SendBuffer[data_size++] = (UCHAR) (w >> 8);
+ SendBuffer[data_size++] = (UCHAR) (w >> 0);
+ break;
+ }
+
+ case 'd':
+ {
+ ULONG d = va_arg ( a, ULONG );
+
+ SendBuffer[data_size++] = (UCHAR) (d >> 24);
+ SendBuffer[data_size++] = (UCHAR) (d >> 16);
+ SendBuffer[data_size++] = (UCHAR) (d >> 8);
+ SendBuffer[data_size++] = (UCHAR) (d >> 0);
+ break;
+ }
+
+ case 'c':
+ {
+ char* c = va_arg ( a, char* );
+
+ length = strlen( c );
+
+ if ( data_size + length > *SendBufferLength ) {
+ printf("***exch:!1!\n");
+ return( FALSE );
+ }
+
+ RtlCopyMemory( &SendBuffer[data_size], c, length + 1 );
+ data_size += length + 1;
+ break;
+ }
+
+ case 'p':
+ {
+ char* c = va_arg ( a, char* );
+
+ length = strlen( c );
+
+ if ( data_size + length > *SendBufferLength ) {
+ printf("***exch:!2!\n");
+ return FALSE;
+ }
+
+ SendBuffer[data_size++] = (UCHAR)length;
+ RtlCopyMemory( &SendBuffer[data_size], c, length );
+ data_size += length;
+ break;
+ }
+
+ case 'u':
+ {
+ PUNICODE_STRING pUString = va_arg ( a, PUNICODE_STRING );
+ OEM_STRING OemString;
+
+ //
+ // Calculate required string length, excluding trailing NUL.
+ //
+
+ length = (USHORT)RtlUnicodeStringToOemSize( pUString ) - 1;
+ ASSERT( length < 0x100 );
+
+ if ( data_size + length > *SendBufferLength ) {
+ printf("***exch:!4!\n");
+ return( FALSE );
+ }
+
+ SendBuffer[data_size++] = (UCHAR)length;
+ OemString.Buffer = &SendBuffer[data_size];
+ OemString.MaximumLength = (USHORT)length + 1;
+ status = RtlUnicodeStringToCountedOemString( &OemString, pUString, FALSE );
+ ASSERT( NT_SUCCESS( status ));
+ data_size += (USHORT)length;
+ break;
+ }
+
+ case 'U':
+ {
+ USHORT i;
+
+ //
+ // UPPERCASE the string, copy it from unicode to the packet
+ //
+
+ PUNICODE_STRING pUString = va_arg ( a, PUNICODE_STRING );
+ UNICODE_STRING UUppercaseString;
+ OEM_STRING OemString;
+
+ if ( pUString->Length > 0 ) {
+
+ RtlUpcaseUnicodeString( &UUppercaseString, pUString, TRUE );
+
+ //
+ // Change all '\' to '/'
+ //
+
+ for ( i = 0 ; i < UUppercaseString.Length ; i++ ) {
+ if ( UUppercaseString.Buffer[i] == L'\\' ) {
+ UUppercaseString.Buffer[i] = L'/';
+ }
+ }
+
+ //
+ // Calculate required string length, excluding trailing NUL.
+ //
+
+ length = (USHORT)RtlUnicodeStringToOemSize( &UUppercaseString ) - 1;
+ ASSERT( length < 0x100 );
+
+ } else {
+ UUppercaseString = *pUString;
+ length = 0;
+ }
+
+ if ( data_size + length > *SendBufferLength ) {
+ printf("***exch:!5!\n");
+ return( FALSE );
+ }
+
+ SendBuffer[data_size++] = (UCHAR)length;
+ OemString.Buffer = &SendBuffer[data_size];
+ OemString.MaximumLength = (USHORT)length + 1;
+ status = RtlUnicodeStringToCountedOemString( &OemString,
+ &UUppercaseString,
+ FALSE );
+ ASSERT( NT_SUCCESS( status ));
+
+ if ( pUString->Length > 0 ) {
+ RtlFreeUnicodeString( &UUppercaseString );
+ }
+
+ data_size += (USHORT)length;
+ break;
+ }
+
+ case 'r':
+ {
+ UCHAR* b = va_arg ( a, UCHAR* );
+ length = va_arg ( a, USHORT );
+
+ if ( data_size + length > *SendBufferLength ) {
+ printf("***exch:!6!\n");
+ return( FALSE );
+ }
+
+ RtlCopyMemory( &SendBuffer[data_size], b, length );
+ data_size += length;
+ break;
+ }
+
+ default:
+ printf ( "*****exchange: invalid request field, %x\n", *pFormatCharacter);
+ return( FALSE );
+ }
+
+ if ( data_size > *SendBufferLength ) {
+ printf( "*****exchange: CORRUPT, too much request data\n" );
+ va_end( a );
+ return FALSE;
+ }
+ }
+
+ if ( *Format == 'S' )
+ {
+ SendBuffer[1] = (UCHAR)((data_size-3) >> 8);
+ SendBuffer[2] = (UCHAR)(data_size-3);
+ }
+
+ va_end( a );
+
+ *SendBufferLength = data_size;
+ return TRUE;
+}
+
+NTSTATUS
+_cdecl
+ParseResponse(
+ char* FormatString,
+ ... // format specific parameters
+ )
+/*++
+
+Routine Description:
+
+ This routine parse an NCP response.
+
+Arguments:
+
+ FormatString - supplies the information needed to create the request to the
+ server. The first byte indicates the packet type and the
+ following bytes contain field types.
+
+ Field types, request/response:
+
+ 'b' byte ( byte* )
+ 'w' hi-lo word ( word* )
+ 'd' hi-lo dword ( dword* )
+ '-' zero/skip byte ( void )
+ '=' zero/skip word ( void )
+ ._. zero/skip string ( word )
+ 'p' pstring ( char* )
+ 'c' cstring ( char* )
+ 'r' raw bytes ( byte*, word )
+ 'R' ASCIIZ to Unicode ( UNICODE_STRING *, word )
+
+Return Value:
+
+ STATUS - The converted error code from the NCP response.
+
+--*/
+
+{
+ PCHAR FormatByte;
+ va_list Arguments;
+ int Length = 0;
+
+ va_start( Arguments, FormatString );
+
+ FormatByte = FormatString;
+ while ( *FormatByte ) {
+
+ switch ( *FormatByte ) {
+
+ case '-':
+ Length += 1;
+ break;
+
+ case '=':
+ Length += 2;
+ break;
+
+ case '_':
+ {
+ USHORT l = va_arg ( Arguments, USHORT );
+ Length += l;
+ break;
+ }
+
+ case 'b':
+ {
+ UCHAR* b = va_arg ( Arguments, UCHAR* );
+ *b = Buffer2[Length++];
+ break;
+ }
+
+ case 'w':
+ {
+ UCHAR* b = va_arg ( Arguments, UCHAR* );
+ b[1] = Buffer2[Length++];
+ b[0] = Buffer2[Length++];
+ break;
+ }
+
+ case 'd':
+ {
+ UCHAR* b = va_arg ( Arguments, UCHAR* );
+ b[3] = Buffer2[Length++];
+ b[2] = Buffer2[Length++];
+ b[1] = Buffer2[Length++];
+ b[0] = Buffer2[Length++];
+ break;
+ }
+
+ case 'c':
+ {
+ char* c = va_arg ( Arguments, char* );
+ USHORT l = strlen( &Buffer2[Length] );
+ memcpy ( c, &Buffer2[Length], l+1 );
+ Length += l+1;
+ break;
+ }
+
+ case 'p':
+ {
+ char* c = va_arg ( Arguments, char* );
+ UCHAR l = Buffer2[Length++];
+ memcpy ( c, &Buffer2[Length], l );
+ c[l+1] = 0;
+ break;
+ }
+
+#if 0
+ case 'P':
+ {
+ PUNICODE_STRING pUString = va_arg ( Arguments, PUNICODE_STRING );
+ OEM_STRING OemString;
+
+ OemString.Length = Buffer2[Length++];
+ OemString.Buffer = &Buffer2[Length];
+
+ Status = RtlOemStringToCountedUnicodeString( pUString, &OemString, FALSE );
+ ASSERT( NT_SUCCESS( Status ));
+
+ break;
+ }
+#endif
+
+ case 'r':
+ {
+ UCHAR* b = va_arg ( Arguments, UCHAR* );
+ USHORT l = va_arg ( Arguments, USHORT );
+ RtlCopyMemory( b, &Buffer2[Length], l );
+ Length += l;
+ break;
+ }
+
+#if 0
+ case 'R':
+ {
+ //
+ // Interpret the buffer as an ASCIIZ string. Convert
+ // it to unicode in the preallocated buffer.
+ //
+
+ PUNICODE_STRING pUString = va_arg ( Arguments, PUNICODE_STRING );
+ OEM_STRING OemString;
+ USHORT len = va_arg ( Arguments, USHORT );
+
+ OemString.Buffer = &Buffer2[Length];
+ OemString.Length = strlen( OemString.Buffer );
+ OemString.MaximumLength = OemString.Length;
+
+ Status = RtlOemStringToCountedUnicodeString( pUString, &OemString, FALSE );
+ ASSERT( NT_SUCCESS( Status ));
+ Length += len;
+ break;
+ }
+#endif
+
+ default:
+ printf ( "*****exchange: invalid response field, %x\n", *FormatByte );
+ return( FALSE );
+ }
+
+#if 0
+ if ( Length > PacketLength )
+ {
+ printf ( "*****exchange: not enough response data, %d\n", i );
+ }
+#endif
+ FormatByte++;
+ }
+
+ va_end( Arguments );
+}
+
+
+// Hex dump
+
+VOID
+dump(
+ IN PVOID far_p,
+ IN ULONG len
+ )
+/*++
+
+Routine Description:
+ Dump Min(len, MaxDump) bytes in classic hex dump style.
+
+Arguments:
+
+ IN far_p - address of buffer to start dumping from.
+
+ IN len - length in bytes of buffer.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ ULONG l;
+ char s[80], t[80];
+ PCHAR far_pchar = (PCHAR)far_p;
+
+ if (len > MaxDump)
+ len = MaxDump;
+
+ while (len) {
+ l = len < 17 ? len : 17;
+
+ // printf("\n%lx ", far_pchar);
+ HexDumpLine (far_pchar, l, s, t, 0);
+ // printf("%s%.*s%s", s, 1 + ((16 - l) * 3), "", t);
+ printf("%s", s);
+
+ len -= l;
+ far_pchar += l;
+ }
+ printf("\n");
+}
+
+VOID
+HexDumpLine (
+ PCHAR pch,
+ ULONG len,
+ PCHAR s,
+ PCHAR t,
+ USHORT flag
+ )
+{
+ static UCHAR rghex[] = "0123456789ABCDEF";
+
+ UCHAR c;
+ UCHAR *hex, *asc;
+
+
+ hex = s;
+ asc = t;
+
+ *(asc++) = '*';
+ while (len--) {
+ c = *(pch++);
+ *(hex++) = rghex [c >> 4] ;
+ *(hex++) = rghex [c & 0x0F];
+ *(hex++) = ' ';
+ *(asc++) = (c < ' ' || c > '~') ? (CHAR )'.' : c;
+ }
+ *(asc++) = '*';
+ *asc = 0;
+ *hex = 0;
+
+ flag;
+}
+
+// End Hex dump
+
+UCHAR Table[] =
+{0x7,0x8,0x0,0x8,0x6,0x4,0xE,0x4,0x5,0xC,0x1,0x7,0xB,0xF,0xA,0x8,
+ 0xF,0x8,0xC,0xC,0x9,0x4,0x1,0xE,0x4,0x6,0x2,0x4,0x0,0xA,0xB,0x9,
+ 0x2,0xF,0xB,0x1,0xD,0x2,0x1,0x9,0x5,0xE,0x7,0x0,0x0,0x2,0x6,0x6,
+ 0x0,0x7,0x3,0x8,0x2,0x9,0x3,0xF,0x7,0xF,0xC,0xF,0x6,0x4,0xA,0x0,
+ 0x2,0x3,0xA,0xB,0xD,0x8,0x3,0xA,0x1,0x7,0xC,0xF,0x1,0x8,0x9,0xD,
+ 0x9,0x1,0x9,0x4,0xE,0x4,0xC,0x5,0x5,0xC,0x8,0xB,0x2,0x3,0x9,0xE,
+ 0x7,0x7,0x6,0x9,0xE,0xF,0xC,0x8,0xD,0x1,0xA,0x6,0xE,0xD,0x0,0x7,
+ 0x7,0xA,0x0,0x1,0xF,0x5,0x4,0xB,0x7,0xB,0xE,0xC,0x9,0x5,0xD,0x1,
+ 0xB,0xD,0x1,0x3,0x5,0xD,0xE,0x6,0x3,0x0,0xB,0xB,0xF,0x3,0x6,0x4,
+ 0x9,0xD,0xA,0x3,0x1,0x4,0x9,0x4,0x8,0x3,0xB,0xE,0x5,0x0,0x5,0x2,
+ 0xC,0xB,0xD,0x5,0xD,0x5,0xD,0x2,0xD,0x9,0xA,0xC,0xA,0x0,0xB,0x3,
+ 0x5,0x3,0x6,0x9,0x5,0x1,0xE,0xE,0x0,0xE,0x8,0x2,0xD,0x2,0x2,0x0,
+ 0x4,0xF,0x8,0x5,0x9,0x6,0x8,0x6,0xB,0xA,0xB,0xF,0x0,0x7,0x2,0x8,
+ 0xC,0x7,0x3,0xA,0x1,0x4,0x2,0x5,0xF,0x7,0xA,0xC,0xE,0x5,0x9,0x3,
+ 0xE,0x7,0x1,0x2,0xE,0x1,0xF,0x4,0xA,0x6,0xC,0x6,0xF,0x4,0x3,0x0,
+ 0xC,0x0,0x3,0x6,0xF,0x8,0x7,0xB,0x2,0xD,0xC,0x6,0xA,0xA,0x8,0xD};
+
+UCHAR Keys[32] =
+{0x48,0x93,0x46,0x67,0x98,0x3D,0xE6,0x8D,
+ 0xB7,0x10,0x7A,0x26,0x5A,0xB9,0xB1,0x35,
+ 0x6B,0x0F,0xD5,0x70,0xAE,0xFB,0xAD,0x11,
+ 0xF4,0x47,0xDC,0xA7,0xEC,0xCF,0x50,0xC0};
+
+#define XorArray( DEST, SRC ) { \
+ PULONG D = (PULONG)DEST; \
+ PULONG S = (PULONG)SRC; \
+ int i; \
+ for ( i = 0; i <= 7 ; i++ ) { \
+ D[i] ^= S[i]; \
+ } \
+}
+
+VOID
+RespondToChallenge(
+ IN PUCHAR achObjectId,
+ IN POEM_STRING Password,
+ IN PUCHAR pChallenge,
+ OUT PUCHAR pResponse
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes the ObjectId and Challenge key from the server and
+ encrypts the user supplied password to develop a credential for the
+ server to verify.
+
+Arguments:
+ IN PUCHAR achObjectId - Supplies the 4 byte user's bindery object id
+ IN POEM_STRING Password - Supplies the user's uppercased password
+ IN PUCHAR pChallenge - Supplies the 8 byte challenge key
+ OUT PUCHAR pResponse - Returns the 8 byte response
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ int index;
+ UCHAR achK[32];
+ UCHAR achBuf[32];
+
+ Shuffle(achObjectId, Password->Buffer, Password->Length, achBuf);
+ Shuffle( &pChallenge[0], achBuf, 16, &achK[0] );
+ Shuffle( &pChallenge[4], achBuf, 16, &achK[16] );
+
+ for (index = 0; index < 16; index++)
+ achK[index] ^= achK[31-index];
+
+ for (index = 0; index < 8; index++)
+ pResponse[index] = achK[index] ^ achK[15-index];
+}
+
+VOID
+RespondToChallengePart1(
+ IN PUCHAR achObjectId,
+ IN POEM_STRING Password,
+ OUT PUCHAR pResponse
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes the ObjectId and Challenge key from the server and
+ encrypts the user supplied password to develop a credential for the
+ server to verify.
+
+Arguments:
+ IN PUCHAR achObjectId - Supplies the 4 byte user's bindery object id
+ IN POEM_STRING Password - Supplies the user's uppercased password
+ IN PUCHAR pChallenge - Supplies the 8 byte challenge key
+ OUT PUCHAR pResponse - Returns the 16 byte response held by the server
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ UCHAR achBuf[32];
+
+ Shuffle(achObjectId, Password->Buffer, Password->Length, achBuf);
+ memmove(pResponse, achBuf, 16);
+}
+
+VOID
+RespondToChallengePart2(
+ IN PUCHAR pResponsePart1,
+ IN PUCHAR pChallenge,
+ OUT PUCHAR pResponse
+ )
+
+/*++
+
+Routine Description:
+
+ This routine takes the result of Shuffling the ObjectId and the Password
+ and processes it with a challenge key.
+
+Arguments:
+ IN PUCHAR pResponsePart1 - Supplies the 16 byte output of
+ RespondToChallengePart1.
+ IN PUCHAR pChallenge - Supplies the 8 byte challenge key
+ OUT PUCHAR pResponse - Returns the 8 byte response
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ int index;
+ UCHAR achK[32];
+
+ Shuffle( &pChallenge[0], pResponsePart1, 16, &achK[0] );
+ Shuffle( &pChallenge[4], pResponsePart1, 16, &achK[16] );
+
+ for (index = 0; index < 16; index++)
+ achK[index] ^= achK[31-index];
+
+ for (index = 0; index < 8; index++)
+ pResponse[index] = achK[index] ^ achK[15-index];
+}
+
+VOID
+Shuffle(
+ UCHAR *achObjectId,
+ UCHAR *szUpperPassword,
+ int iPasswordLen,
+ UCHAR *achOutputBuffer
+ )
+
+/*++
+
+Routine Description:
+
+ This routine shuffles around the object ID with the password
+
+Arguments:
+
+ IN achObjectId - Supplies the 4 byte user's bindery object id
+
+ IN szUpperPassword - Supplies the user's uppercased password on the
+ first call to process the password. On the second and third calls
+ this parameter contains the OutputBuffer from the first call
+
+ IN iPasswordLen - length of uppercased password
+
+ OUT achOutputBuffer - Returns the 8 byte sub-calculation
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ int iTempIndex;
+ int iOutputIndex;
+ UCHAR achTemp[32];
+
+ //
+ // Initialize the achTemp buffer. Initialization consists of taking
+ // the password and dividing it up into chunks of 32. Any bytes left
+ // over are the remainder and do not go into the initialization.
+ //
+ // achTemp[0] = szUpperPassword[0] ^ szUpperPassword[32] ^ szUpper...
+ // achTemp[1] = szUpperPassword[1] ^ szUpperPassword[33] ^ szUpper...
+ // etc.
+ //
+
+ if ( iPasswordLen > 32) {
+
+ // At least one chunk of 32. Set the buffer to the first chunk.
+
+ RtlCopyMemory( achTemp, szUpperPassword, 32 );
+
+ szUpperPassword +=32; // Remove the first chunk
+ iPasswordLen -=32;
+
+ while ( iPasswordLen >= 32 ) {
+ //
+ // Xor this chunk with the characters already loaded into
+ // achTemp.
+ //
+
+ XorArray( achTemp, szUpperPassword);
+
+ szUpperPassword +=32; // Remove this chunk
+ iPasswordLen -=32;
+ }
+
+ } else {
+
+ // No chunks of 32 so set the buffer to zero's
+
+ RtlZeroMemory( achTemp, sizeof(achTemp));
+
+ }
+
+ //
+ // achTemp is now initialized. Load the remainder into achTemp.
+ // The remainder is repeated to fill achTemp.
+ //
+ // The corresponding character from Keys is taken to seperate
+ // each repitition.
+ //
+ // As an example, take the remainder "ABCDEFG". The remainder is expanded
+ // to "ABCDEFGwABCDEFGxABCDEFGyABCDEFGz" where w is Keys[7],
+ // x is Keys[15], y is Keys[23] and z is Keys[31].
+ //
+ //
+
+ if (iPasswordLen > 0) {
+ int iPasswordOffset = 0;
+ for (iTempIndex = 0; iTempIndex < 32; iTempIndex++) {
+
+ if (iPasswordLen == iPasswordOffset) {
+ iPasswordOffset = 0;
+ achTemp[iTempIndex] ^= Keys[iTempIndex];
+ } else {
+ achTemp[iTempIndex] ^= szUpperPassword[iPasswordOffset++];
+ }
+ }
+ }
+
+ //
+ // achTemp has been loaded with the users password packed into 32
+ // bytes. Now take the objectid that came from the server and use
+ // that to munge every byte in achTemp.
+ //
+
+ for (iTempIndex = 0; iTempIndex < 32; iTempIndex++)
+ achTemp[iTempIndex] ^= achObjectId[ iTempIndex & 3];
+
+ Scramble( Scramble( 0, achTemp ), achTemp );
+
+ //
+ // Finally take pairs of bytes in achTemp and return the two
+ // nibbles obtained from Table. The pairs of bytes used
+ // are achTemp[n] and achTemp[n+16].
+ //
+
+ for (iOutputIndex = 0; iOutputIndex < 16; iOutputIndex++) {
+
+ achOutputBuffer[iOutputIndex] =
+ Table[achTemp[iOutputIndex << 1]] |
+ (Table[achTemp[(iOutputIndex << 1) + 1]] << 4);
+ }
+
+ return;
+}
+
+int
+Scramble(
+ int iSeed,
+ UCHAR achBuffer[32]
+ )
+
+/*++
+
+Routine Description:
+
+ This routine scrambles around the contents of the buffer. Each buffer
+ position is updated to include the contents of at least two character
+ positions plus an EncryptKey value. The buffer is processed left to right
+ and so if a character position chooses to merge with a buffer position
+ to its left then this buffer position will include bits derived from at
+ least 3 bytes of the original buffer contents.
+
+Arguments:
+
+ IN iSeed
+ IN OUT achBuffer[32]
+
+Return Value:
+
+ none.
+
+--*/
+
+{
+ int iBufferIndex;
+
+ for (iBufferIndex = 0; iBufferIndex < 32; iBufferIndex++) {
+ achBuffer[iBufferIndex] =
+ (UCHAR)(
+ ((UCHAR)(achBuffer[iBufferIndex] + iSeed)) ^
+ ((UCHAR)( achBuffer[(iBufferIndex+iSeed) & 31] -
+ Keys[iBufferIndex] )));
+
+ iSeed += achBuffer[iBufferIndex];
+ }
+ return iSeed;
+}
+
+#if 0
+
+// this data was gathered for object id = 9000095 from yihsins3 with
+// this program by reseting the password to null manually and then
+// sending over the specified Vc
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+Vnew= DC 9D A3 3C 61 CA 98 F2 BB B5 AE 34 04 43 B0 C9 1B
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11 11
+Vnew= 3F F0 CF 12 36 49 C1 29 93 4E 9A 58 52 C8 3D 13 0A
+
+File= 3F F0 CF 12 36 49 C1 29 93 4E 9A 58 52 C8 3D 13 0A
+ Vc= 22 22 22 22 22 22 22 22 22 22 22 22 22 22 22 22 22
+Vnew= C9 BC 9A 26 58 7F 1F 4D D0 A4 77 B7 B8 39 72 FB 39
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33 33
+Vnew= 7E DB 66 08 DC 51 3A 81 FD 8B 2B C5 8F 8C 49 9D 28
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44
+Vnew= 4D A9 25 B4 BB 18 F3 3A 86 3D 06 23 77 AA 1E D8 1F
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55 55
+Vnew= A5 43 BC 91 C9 0B 79 BF CF CC 58 00 E5 1F C1 81 0E
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= 66 66 66 66 66 66 66 66 66 66 66 66 66 66 66 66 66
+Vnew= 83 51 01 73 EF 27 66 13 6C 6F E4 7F C9 2D 04 BE 3D
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77 77
+Vnew= 21 CF 82 F7 93 A6 47 76 19 06 85 4D 4D FB 23 4F 2C
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88 88
+Vnew= 54 3A FE ED 72 9D BB 65 45 E3 3D 61 1B 04 6A 37 13
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99
+Vnew= 18 08 47 5E 4A F0 D0 9E 77 9A 69 92 31 04 6A 37 02
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA AA
+Vnew= E0 87 3B 85 04 B4 55 AB 58 22 FF AB F0 76 F6 74 31
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= BB BB BB BB BB BB BB BB BB BB BB BB BB BB BB BB BB
+Vnew= F2 15 D9 D9 A0 D3 02 0C 01 F0 B1 FE 63 D2 EF E6 20
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
+Vnew= 9B 76 70 4B 87 65 E4 C8 22 59 D0 19 9A B7 88 60 17
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD DD
+Vnew= B7 E4 E4 A0 25 3C 8D 50 AE 17 C2 EC AE 90 A7 52 06
+
+File= 41 1A 23 1F E3 FF 5C 0D 5B DF 40 52 52 DE 9C EC 00
+ Vc= EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE EE
+Vnew= 06 2E 18 6F FD 82 2C E7 E4 78 4C DA DC E1 9C A5 35
+
+File= 06 2E 18 6F FD 82 2C E7 E4 78 4C DA DC E1 9C A5 35
+ Vc= FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF
+Vnew= 6A 62 5D CA 1E EE AE D4 3A D1 13 86 26 6E 5B 2A 24
+
+
+0xEA, 0x91, 0x6C, 0x3D, 0xAB, 0x59, 0xB9, 0xBD, 0xB2, 0x7B, 0x4C, 0x , 0x , 0x , 0x , 0x , 0x , //0//
+0x97, 0xB6, 0xE6, 0x15, 0xF0, 0x43, 0x21, 0x63, 0x7B, 0xDF, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //1//
+0x7B, 0xEF, 0x47, 0x21, 0xD8, 0x6E, 0xEB, 0x10, 0xCC, 0xAA, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //2//
+0x16, 0x85, 0xA0, 0x06, 0x17, 0xDB, 0x34, 0x46, 0xF1, 0x48, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //3//
+0x48, 0x5D, 0x9D, 0xC4, 0x9A, 0x1A, 0x7C, 0x2F, 0x8E, 0x12, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //4//
+0x85, 0x6B, 0xF4, 0x9A, 0x2D, 0x3C, 0xAA, 0xD8, 0xA8, 0xC0, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //5//
+0xFE, 0xFC, 0x33, 0xE2, 0x01, 0xC7, 0x66, 0x87, 0x64, 0x67, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //6//
+0x3D, 0xCA, 0xC9, 0x67, 0x8C, 0x26, 0x57, 0x7E, 0x99, 0xED, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //7//
+0x69, 0xA9, 0x7E, 0xA3, 0xC2, 0xE4, 0xD0, 0x3C, 0x4A, 0x3E, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //8//
+0xC2, 0x04, 0x2B, 0x5B, 0x75, 0x81, 0x05, 0x91, 0x17, 0x9C, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //9//
+0x5F, 0x48, 0x02, 0xDF, 0xB9, 0x70, 0xF3, 0xA4, 0xDF, 0x29, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //A//
+0xDC, 0x23, 0x5A, 0x4C, 0x44, 0xA5, 0x88, 0x5A, 0x00, 0x03, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //B//
+0x20, 0x72, 0x15, 0xF0, 0x53, 0x0D, 0x1E, 0xCB, 0x56, 0x55, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //C//
+0x04, 0x30, 0xBF, 0xB8, 0x3E, 0xB8, 0x9D, 0xF2, 0x23, 0xF4, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //D//
+0xA3, 0xDE, 0xD8, 0x89, 0x6F, 0xFF, 0xF3, 0xE9, 0xED, 0x81, 0x , 0x , 0x , 0x , 0x , 0x , 0x , //E//
+0xB1, 0x17, 0x81, 0x7E, 0xE6, 0x92, 0x42, 0x05, 0x35, 0xB6, 0x , 0x , 0x , 0x , 0x , 0x , 0x //F//
+ };
+ 00 = AE 34 04 43 B0 C9 1B
+ 11 = 9A 58 52 C8 3D 13 0A
+ 22 = 77 B7 B8 39 72 FB 39
+ 33 = 2B C5 8F 8C 49 9D 28
+ 44 = 06 23 77 AA 1E D8 1F
+ 55 = 58 00 E5 1F C1 81 0E
+ 66 = E4 7F C9 2D 04 BE 3D
+ 77 = 85 4D 4D FB 23 4F 2C
+ 88 = 3D 61 1B 04 6A 37 13
+ 99 = 69 92 31 55 D5 0C 02
+ AA = FF AB F0 76 F6 74 31
+ BB = B1 FE 63 D2 EF E6 20
+ CC = D0 19 9A B7 88 60 17
+ DD = C2 EC AE 90 A7 52 06
+ EE = 4C DA DC E1 9C A5 35
+ FF = 13 86 26 6E 5B 2A 24
+
+
+
+
+#endif
+
diff --git a/private/nw/test/userncp.c b/private/nw/test/userncp.c
new file mode 100644
index 000000000..ce3e95195
--- /dev/null
+++ b/private/nw/test/userncp.c
@@ -0,0 +1,246 @@
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#define WIN32_CONSOLE_APP
+#include <windows.h>
+#include <stdio.h>
+#include <ntddnwfs.h>
+
+#define CANCEL
+
+VOID
+dump(
+ IN ULONG Level,
+ IN PVOID far_p,
+ IN ULONG len
+ );
+
+VOID
+HexDumpLine (
+ PCHAR pch,
+ ULONG len,
+ PCHAR s,
+ PCHAR t,
+ USHORT flag
+ );
+
+BOOLEAN test1();
+
+int
+_cdecl
+main(
+ int argc,
+ char *argv[]
+ )
+{
+ UCHAR error = FALSE;
+
+ printf( "Test1...\n" );
+ if (!test1()) {
+ printf("Error: Test 1 failed\n");
+ error = TRUE;
+ }
+ return error;
+}
+
+
+
+BOOLEAN
+test1()
+{
+ HANDLE FileHandle;
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ UNICODE_STRING UnicodeString;
+ IO_STATUS_BLOCK IoStatus;
+
+ CHAR Input1[] = { 0, 1, 1, 1, '\\' };
+ CHAR Input2[] = { 1, 1 };
+ CHAR Output[20];
+
+ printf( "test 1: opening\n");
+
+ RtlInitUnicodeString( &UnicodeString, L"\\Device\\NwRdr\\Netware311" );
+
+ InitializeObjectAttributes(
+ &ObjectAttributes,
+ &UnicodeString,
+ 0,
+ NULL,
+ NULL
+ );
+
+ Status = NtOpenFile (
+ &FileHandle,
+ FILE_GENERIC_READ | FILE_GENERIC_WRITE | SYNCHRONIZE,
+ &ObjectAttributes,
+ &IoStatus,
+ FILE_SHARE_READ,
+ 0 );
+
+ if (Status != STATUS_SUCCESS) {
+ printf( "test 1: NtOpenFile failed, error = %08lx\n",Status);
+ return FALSE;
+ }
+
+ if (IoStatus.Status != STATUS_SUCCESS) {
+ printf( "test 1: NtOpenFile failed, error = %08lx\n",IoStatus.Status);
+ return FALSE;
+ }
+
+ printf( "test 1: opened server successfully\n");
+
+ //
+ // Send set path
+ //
+
+ NtFsControlFile( FileHandle,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatus,
+ FSCTL_NWR_NCP_E2H,
+ &Input1,
+ sizeof(Input1),
+ &Output,
+ sizeof(Output) );
+
+ if (Status != STATUS_SUCCESS) {
+ printf( "test 1: NtFsControlFile failed, error = %0x08lx\n",Status);
+ return FALSE;
+ }
+
+ printf( "test 1: Nt fs control file succeeded\n");
+
+ if ( IoStatus.Information != 0) {
+ dump( 0, &Output, IoStatus.Information );
+ } else {
+ printf("Received empty buffer\n");
+ }
+
+ //
+ // Send get path
+ //
+
+ NtFsControlFile( FileHandle,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatus,
+ FSCTL_NWR_NCP_E2H,
+ &Input2,
+ sizeof(Input2),
+ &Output,
+ sizeof(Output) );
+
+ if (Status != STATUS_SUCCESS) {
+ printf( "test 1: NtFsControlFile failed, error = %0x08lx\n",Status);
+ return FALSE;
+ }
+
+ printf( "test 1: Nt fs control file succeeded\n");
+
+ if ( IoStatus.Information != 0) {
+ dump( 0, &Output, IoStatus.Information );
+ } else {
+ printf("Received empty buffer\n");
+ }
+
+
+ //
+ // Now close the device
+ //
+
+ printf("test 1: closing device\n");
+
+ Status = NtClose( FileHandle );
+ if (Status != STATUS_SUCCESS) {
+ printf( "test 1: Wrong return value %lX - close \n",Status);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+VOID
+dump(
+ IN ULONG Level,
+ IN PVOID far_p,
+ IN ULONG len
+ )
+/*++
+
+Routine Description:
+ Dump Min(len, MaxDump) bytes in classic hex dump style if debug
+ output is turned on for this level.
+
+Arguments:
+
+ IN Level - 0 if always display. Otherwise only display if a
+ corresponding bit is set in NwDebug.
+
+ IN far_p - address of buffer to start dumping from.
+
+ IN len - length in bytes of buffer.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ ULONG l;
+ char s[80], t[80];
+ PCHAR far_pchar = (PCHAR)far_p;
+ ULONG MaxDump = 256;
+
+ if (Level == 0) {
+ if (len > MaxDump)
+ len = MaxDump;
+
+ while (len) {
+ l = len < 16 ? len : 16;
+
+ printf("\n%lx ", far_pchar);
+ HexDumpLine (far_pchar, l, s, t, 0);
+ printf("%s%.*s%s", s, 1 + ((16 - l) * 3), "", t);
+
+ len -= l;
+ far_pchar += l;
+ }
+ printf("\n");
+ }
+}
+
+VOID
+HexDumpLine (
+ PCHAR pch,
+ ULONG len,
+ PCHAR s,
+ PCHAR t,
+ USHORT flag
+ )
+{
+ static UCHAR rghex[] = "0123456789ABCDEF";
+
+ UCHAR c;
+ UCHAR *hex, *asc;
+
+
+ hex = s;
+ asc = t;
+
+ *(asc++) = '*';
+ while (len--) {
+ c = *(pch++);
+ *(hex++) = rghex [c >> 4] ;
+ *(hex++) = rghex [c & 0x0F];
+ *(hex++) = ' ';
+ *(asc++) = (c < ' ' || c > '~') ? (CHAR )'.' : c;
+ }
+ *(asc++) = '*';
+ *asc = 0;
+ *hex = 0;
+
+ flag;
+}
+
diff --git a/private/nw/vwipxspx/dirs b/private/nw/vwipxspx/dirs
new file mode 100644
index 000000000..3f63b087e
--- /dev/null
+++ b/private/nw/vwipxspx/dirs
@@ -0,0 +1,23 @@
+!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= dll
diff --git a/private/nw/vwipxspx/dll/makefile b/private/nw/vwipxspx/dll/makefile
new file mode 100644
index 000000000..f0db8e4a7
--- /dev/null
+++ b/private/nw/vwipxspx/dll/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/vwipxspx/dll/socket.c b/private/nw/vwipxspx/dll/socket.c
new file mode 100644
index 000000000..874d4691f
--- /dev/null
+++ b/private/nw/vwipxspx/dll/socket.c
@@ -0,0 +1,2309 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ socket.c
+
+Abstract:
+
+ Contains functions to create, delete and manipulate IPX sockets and SPX
+ connections
+
+ Contents:
+ CreateSocket
+ AllocateTemporarySocket
+ QueueSocket
+ DequeueSocket
+ FindSocket
+ FindActiveSocket
+ ReopenSocket
+ KillSocket
+ KillShortLivedSockets
+ AllocateConnection
+ DeallocateConnection
+ FindConnection
+ QueueConnection
+ DequeueConnection
+ KillConnection
+ AbortOrTerminateConnection
+ CheckPendingSpxRequests
+ (CheckSocketState)
+ (CheckSelectRead)
+ (CheckSelectWrite)
+ (AsyncReadAction)
+ (AsyncWriteAction)
+ (CompleteAccept)
+ (CompleteReceive)
+ (CompleteConnect)
+ (CompleteSend)
+
+Author:
+
+ Richard L Firth (rfirth) 25-Oct-1993
+
+Environment:
+
+ User-mode Win32
+
+Revision History:
+
+ 25-Oct-1993 rfirth
+ Created
+
+--*/
+
+#include "vw.h"
+#pragma hdrstop
+
+//
+// miscellaneous manifests
+//
+
+#define ARBITRARY_CONNECTION_INCREMENT 2
+
+//
+// macros
+//
+
+#define ALLOCATE_CONNECTION_NUMBER() (ConnectionNumber += ARBITRARY_CONNECTION_INCREMENT)
+
+//
+// private data
+//
+
+PRIVATE LPSOCKET_INFO SocketList = NULL;
+PRIVATE LPCONNECTION_INFO ConnectionList = NULL;
+PRIVATE WORD ConnectionNumber = ARBITRARY_CONNECTION_NUMBER;
+
+//
+// private functions
+//
+
+PRIVATE
+BOOL
+CheckSocketState(
+ IN SOCKET Socket,
+ OUT LPBOOL Readable,
+ OUT LPBOOL Writeable,
+ OUT LPBOOL Error
+ );
+
+
+PRIVATE
+VOID
+CheckSelectRead(
+ IN LPSOCKET_INFO pSocketInfo,
+ IN LPCONNECTION_INFO pConnectionInfo,
+ OUT BOOL *CheckRead
+ );
+
+PRIVATE
+VOID
+CheckSelectWrite(
+ IN LPSOCKET_INFO pSocketInfo,
+ IN LPCONNECTION_INFO pConnectionInfo,
+ OUT BOOL *CheckWrite
+ );
+
+PRIVATE
+VOID
+AsyncReadAction(
+ IN LPSOCKET_INFO pSocketInfo,
+ IN LPCONNECTION_INFO pConnectionInfo,
+ OUT BOOL *ReadPerformed
+ );
+
+PRIVATE
+VOID
+AsyncWriteAction(
+ IN LPSOCKET_INFO pSocketInfo,
+ IN LPCONNECTION_INFO pConnectionInfo,
+ OUT BOOL *WritePerformed
+ );
+
+PRIVATE
+VOID
+CompleteAccept(
+ IN LPSOCKET_INFO pSocketInfo,
+ IN LPCONNECTION_INFO pConnectionInfo
+ );
+
+PRIVATE
+VOID
+CompleteReceive(
+ IN LPSOCKET_INFO pSocketInfo,
+ IN LPCONNECTION_INFO pConnectionInfo
+ );
+
+PRIVATE
+VOID
+CompleteConnect(
+ IN LPSOCKET_INFO pSocketInfo,
+ IN LPCONNECTION_INFO pConnectionInfo
+ );
+
+PRIVATE
+VOID
+CompleteSend(
+ IN LPSOCKET_INFO pSocketInfo,
+ IN LPCONNECTION_INFO pConnectionInfo
+ );
+
+#if SPX_HACK
+PRIVATE VOID ModifyFirstReceive(LPBYTE, LPDWORD, WORD, SOCKET);
+#endif
+
+//
+// public functions
+//
+
+
+int
+CreateSocket(
+ IN SOCKET_TYPE SocketType,
+ IN OUT ULPWORD pSocketNumber,
+ OUT SOCKET* pSocket
+ )
+
+/*++
+
+Routine Description:
+
+ Creates a socket for IPX or SPX (a connection). Once the socket is created
+ we have to bind it to the IPX/SPX 'socket' - i.e. port. We also need to
+ change a few things about the standard socket:
+
+ * if this is an SPX request then we must set the REUSEADDR socket option
+ since there may typically be several connect requests over the same
+ WinSock socket: we need to be able to bind multiple connections to the
+ same socket number
+
+ * all sockets opened by this function are put into non-blocking mode
+ * all sockets opened by this function will return the packet header in
+ any received data (IPX_RECVHDR)
+
+ The requested socket number can be 0 in which case we bind to a dynamic
+ socket number. We always return the number of the socket bound to: if not 0
+ on input, this should always be the same value as that requested in
+ pSocketNumber
+
+ If any WinSock call fails (and the socket was created) then we close the
+ socket before returning
+
+Arguments:
+
+ SocketType - SOCKET_TYPE_IPX or SOCKET_TYPE_SPX
+ pSocketNumber - input: socket number to bind (can be 0)
+ output: socket number bound
+ pSocket - pointer to address of socket identifier to return
+
+Return Value:
+
+ int
+ Success - IPX_SUCCESS/SPX_SUCCESS (0)
+
+ Failure - IPX_SOCKET_TABLE_FULL
+ WinSock cannot create the socket
+
+ IPX_SOCKET_ALREADY_OPEN
+ Assume the request was for an IPX socket: we do not allow
+ multiple IPX sockets to be bound to the same socket number,
+ only SPX
+
+
+--*/
+
+{
+ SOCKET s;
+ SOCKADDR_IPX socketAddress;
+ BOOL true = TRUE;
+ int rc;
+ int status = IPX_SOCKET_TABLE_FULL; // default error
+
+ s = socket(AF_IPX,
+ (SocketType == SOCKET_TYPE_SPX) ? SOCK_SEQPACKET : SOCK_DGRAM,
+ (SocketType == SOCKET_TYPE_SPX) ? NSPROTO_SPX : NSPROTO_IPX
+ );
+
+ if (s != INVALID_SOCKET) {
+
+ //
+ // for stream (SPX) sockets, we need multiple sockets bound to the
+ // same socket number if we are to have multiple connections on the
+ // same SPX socket
+ //
+
+ if (SocketType == SOCKET_TYPE_SPX) {
+ rc = setsockopt(s,
+ SOL_SOCKET,
+ SO_REUSEADDR,
+ (char FAR*)&true,
+ sizeof(true)
+ );
+ if (rc == SOCKET_ERROR) {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "CreateSocket: setsockopt(SO_REUSEADDR) returns %d\n",
+ WSAGetLastError()
+ ));
+
+ } else {
+ rc = setsockopt(s,
+ SOL_SOCKET,
+ SO_OOBINLINE,
+ (char FAR*)&true,
+ sizeof(true)
+ );
+
+ if (rc == SOCKET_ERROR) {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "CreateSocket: setsockopt(SO_OOBINLINE) returns %d\n",
+ WSAGetLastError()
+ ));
+
+ }
+ }
+ } else {
+
+ //
+ // allow broadcasts to be transmitted on IPX sockets
+ //
+
+ rc = setsockopt(s,
+ SOL_SOCKET,
+ SO_BROADCAST,
+ (char FAR*)&true,
+ sizeof(true)
+ );
+ }
+ if (!rc) {
+
+ //
+ // bind the socket to the local socket number (port)
+ //
+
+ ZeroMemory(&socketAddress, sizeof(socketAddress));
+ socketAddress.sa_family = AF_IPX;
+ socketAddress.sa_socket = *pSocketNumber;
+ rc = bind(s, (LPSOCKADDR)&socketAddress, sizeof(socketAddress));
+ if (rc != SOCKET_ERROR) {
+
+ int length = sizeof(socketAddress);
+
+ ZeroMemory(&socketAddress, sizeof(socketAddress));
+ socketAddress.sa_family = AF_IPX;
+
+ //
+ // use getsockname() to find the (big-endian) socket value that
+ // was actually assigned: should only be different from
+ // *pSocketNumber if the latter was 0 on input
+ //
+
+ rc = getsockname(s, (LPSOCKADDR)&socketAddress, &length);
+ if (rc != SOCKET_ERROR) {
+
+ u_long arg = !0;
+
+ //
+ // put the socket into non-blocking mode. Neither IPX nor
+ // SPX sockets are blocking: the app starts an I/O request
+ // and if it doesn't complete immediately, will be completed
+ // by AES which periodically polls the outstanding I/O
+ // requests
+ //
+
+ rc = ioctlsocket(s, FIONBIO, &arg);
+ if (rc != SOCKET_ERROR) {
+
+ //
+ // return protocol header on receive frames
+ //
+
+ rc = setsockopt(s,
+ NSPROTO_IPX,
+ IPX_RECVHDR,
+ (char FAR*)&true,
+ sizeof(true)
+ );
+ if (rc != SOCKET_ERROR) {
+ *pSocketNumber = socketAddress.sa_socket;
+ *pSocket = s;
+ status = IPX_SUCCESS;
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "CreateSocket: setsockopt(RECVHDR) returns %d\n",
+ WSAGetLastError()
+ ));
+
+ }
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "CreateSocket: ioctlsocket(FIONBIO) returns %d\n",
+ WSAGetLastError()
+ ));
+
+ }
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "CreateSocket: getsockname() returns %d\n",
+ WSAGetLastError()
+ ));
+
+ }
+ } else {
+
+ //
+ // bind() failed - either an expected error (the requested socket
+ // is already in use), or (horror) an unexpected error, in which
+ // case report table full (?)
+ //
+
+ switch (WSAGetLastError()) {
+ case WSAEADDRINUSE:
+
+ ASSERT(*pSocketNumber != 0);
+ ASSERT(SocketType == SOCKET_TYPE_IPX);
+
+ status = IPX_SOCKET_ALREADY_OPEN;
+ break;
+
+ default:
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "CreateSocket: bind() on socket %#x returns %d\n",
+ s,
+ WSAGetLastError()
+ ));
+
+ }
+ }
+ }
+ } else {
+
+ //
+ // the socket() call failed - treat as table full
+ //
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "CreateSocket: socket() returns %d\n",
+ WSAGetLastError()
+ ));
+
+ }
+ if (status != IPX_SUCCESS) {
+ if (s != INVALID_SOCKET) {
+ closesocket(s);
+ }
+ }
+ return status;
+}
+
+
+LPSOCKET_INFO
+AllocateTemporarySocket(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Allocates a temporary socket. Creates an IPX socket having a dynamically
+ allocated socket number
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ LPSOCKET_INFO
+ Success - pointer to SOCKET_INFO structure
+ Failure - NULL
+
+--*/
+
+{
+ LPSOCKET_INFO pSocketInfo;
+ int rc;
+
+ pSocketInfo = AllocateSocket();
+ if (pSocketInfo) {
+
+ //
+ // assumption: the SOCKET_INFO structure was zeroed by LocalAlloc(LPTR,..
+ // hence the SocketNumber fields is 0. This causes CreateSocket to
+ // generate a dynamic socket number
+ //
+
+ rc = CreateSocket(SOCKET_TYPE_IPX,
+ &pSocketInfo->SocketNumber,
+ &pSocketInfo->Socket
+ );
+ if (rc == IPX_SUCCESS) {
+ pSocketInfo->Flags |= SOCKET_FLAG_TEMPORARY;
+ } else {
+ DeallocateSocket(pSocketInfo);
+ pSocketInfo = NULL;
+ }
+ }
+ return pSocketInfo;
+}
+
+
+VOID
+QueueSocket(
+ IN LPSOCKET_INFO pSocketInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Add a SOCKET_INFO structure to the list (LIFO) of (opened) sockets
+
+Arguments:
+
+ pSocketInfo - pointer to filled-in SOCKET_INFO structure
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RequestMutex();
+ pSocketInfo->Next = SocketList;
+ SocketList = pSocketInfo;
+ ReleaseMutex();
+}
+
+
+LPSOCKET_INFO
+DequeueSocket(
+ IN LPSOCKET_INFO pSocketInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Remove a SOCKET_INFO structure from the list
+
+Arguments:
+
+ pSocketInfo - pointer to SOCKET_INFO structure to remove
+
+Return Value:
+
+ LPSOCKET_INFO
+ pSocketInfo - should be this value
+ NULL - couldn't find pSocketInfo (should not get this!)
+
+--*/
+
+{
+ LPSOCKET_INFO prev, p;
+
+ ASSERT(SocketList);
+
+ RequestMutex();
+ prev = (LPSOCKET_INFO)&SocketList;
+ p = SocketList;
+ while (p) {
+ if (p == pSocketInfo) {
+ prev->Next = p->Next;
+ p->Next = NULL;
+ break;
+ } else {
+ prev = p;
+ p = p->Next;
+ }
+ }
+
+ if (!p) {
+
+ //
+ // should never reach here
+ //
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_FATAL,
+ "DequeueSocket: can't find socket structure %08x on queue\n",
+ pSocketInfo
+ ));
+
+ }
+
+ ReleaseMutex();
+ return p;
+}
+
+
+LPSOCKET_INFO
+FindSocket(
+ IN WORD SocketNumber
+ )
+
+/*++
+
+Routine Description:
+
+ Locate a SOCKET_INFO structure in the list, by (big-endian) socket number
+
+ Assumes: 1. There is 1 and only 1 SOCKET_INFO structure that contains
+ SocketNumber
+
+Arguments:
+
+ SocketNumber - big-endian socket number to find
+
+Return Value:
+
+ LPSOCKET_INFO
+ NULL - couldn't find requested socket
+ !NULL - pointer to discovered SOCKET_INFO structure
+
+--*/
+
+{
+ LPSOCKET_INFO p;
+
+ RequestMutex();
+ p = SocketList;
+ while (p) {
+ if (p->SocketNumber == SocketNumber) {
+ break;
+ } else {
+ p = p->Next;
+ }
+ }
+ ReleaseMutex();
+ return p;
+}
+
+
+LPSOCKET_INFO
+FindActiveSocket(
+ IN LPSOCKET_INFO pSocketInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Find a SOCKET_INFO structure with pending send or receive. Called as FindFirst,
+ FindNext - first call made with pSocketInfo == NULL: enters critical section
+ if an active socket is found, returns pointer
+
+ Subsequent calls are made with pSocketInfo pointing to last returned
+ SOCKET_INFO. This continues the search. When search exhausted, critical
+ section is released
+
+Arguments:
+
+ pSocketInfo - pointer to SOCKET_INFO structure: first time must be NULL
+
+Return Value:
+
+ LPSOCKET_INFO - next active SOCKET_INFO structure or NULL
+
+--*/
+
+{
+ if (!pSocketInfo) {
+ RequestMutex();
+ pSocketInfo = SocketList;
+ } else {
+ pSocketInfo = pSocketInfo->Next;
+ }
+ for (; pSocketInfo; pSocketInfo = pSocketInfo->Next) {
+ if (pSocketInfo->Flags & (SOCKET_FLAG_SENDING | SOCKET_FLAG_LISTENING)) {
+ return pSocketInfo;
+ }
+ }
+ ReleaseMutex();
+ return NULL;
+}
+
+
+int
+ReopenSocket(
+ LPSOCKET_INFO pSocketInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Called expressly to close an IPX socket and reassign the descriptor to SPX.
+ Note that after this function completes, IPXSendPacket and IPXListenForPacket
+ cannot be made agains the IPX socket
+
+Arguments:
+
+ pSocketInfo - pointer to SOCKET_INFO which currently describes an IPX socket
+
+Return Value:
+
+ int - return code from CreateSocket
+
+--*/
+
+{
+ int rc;
+
+ rc = closesocket(pSocketInfo->Socket);
+ if (rc == SOCKET_ERROR) {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "ReopenSocket: closesocket() returns %d\n",
+ WSAGetLastError()
+ ));
+
+ }
+
+ //
+ // mark this socket as connection-based (SPX) socket
+ //
+
+ pSocketInfo->SpxSocket = TRUE;
+
+ //
+ // BUGBUG: need the DOS/Windows task ID: owner may have changed!
+ //
+
+ //
+ // re-open the socket for SPX use
+ //
+
+ return CreateSocket(SOCKET_TYPE_SPX,
+ &pSocketInfo->SocketNumber,
+ &pSocketInfo->Socket
+ );
+}
+
+
+VOID
+KillSocket(
+ IN LPSOCKET_INFO pSocketInfo
+ )
+
+/*++
+
+Routine Description:
+
+ closes a socket, removes the SOCKET_INFO structure from the list and cancels
+ any pending send, listen or timed events associated with the socket
+
+Arguments:
+
+ pSocketInfo - identifying socket to kill
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ int rc;
+
+ //
+ // remove the SOCKET_INFO structure from the list of sockets. Cancel
+ // any pending ECB requests and any IPX timed events that have the
+ // same socket number
+ //
+
+ DequeueSocket(pSocketInfo);
+ rc = closesocket(pSocketInfo->Socket);
+ if (rc == SOCKET_ERROR) {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "KillSocket: closesocket() returns %d\n",
+ WSAGetLastError()
+ ));
+
+ }
+
+ //
+ // the socket has been removed from SocketList: no need to grab mutex to
+ // perform the following
+ //
+
+ CancelTimedEvents(pSocketInfo->SocketNumber, 0, 0);
+ CancelSocketQueue(&pSocketInfo->ListenQueue);
+ CancelSocketQueue(&pSocketInfo->HeaderQueue);
+ CancelSocketQueue(&pSocketInfo->SendQueue);
+ if (pSocketInfo->SpxSocket) {
+
+ LPCONNECTION_INFO pConnectionInfo;
+
+ while (pConnectionInfo = pSocketInfo->Connections) {
+ DequeueConnection(pSocketInfo, pConnectionInfo);
+ KillConnection(pConnectionInfo);
+ }
+ }
+ DeallocateSocket(pSocketInfo);
+}
+
+
+VOID
+KillShortLivedSockets(
+ IN WORD Owner
+ )
+
+/*++
+
+Routine Description:
+
+ For all those sockets created by a DOS process as SHORT_LIVED, terminate
+ the sockets, cancelling any outstanding ECBs
+
+Arguments:
+
+ Owner - DOS PDB which opened sockets
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPSOCKET_INFO pSocketInfo;
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "KillShortLivedSockets(%04x)\n",
+ Owner
+ ));
+
+ RequestMutex();
+
+ //
+ // kill any non-socket (AES) timed events owned by this DOS process
+ //
+
+ CancelTimedEvents(0, Owner, 0);
+
+ //
+ // kill all sockets owned by this PDB
+ //
+
+ pSocketInfo = SocketList;
+ while (pSocketInfo) {
+
+ LPSOCKET_INFO next;
+
+ next = pSocketInfo->Next;
+ if (!pSocketInfo->LongLived && (pSocketInfo->Owner == Owner)) {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "KillShortLivedSockets: Socket %04x owned by %04x\n",
+ B2LW(pSocketInfo->SocketNumber),
+ pSocketInfo->Owner
+ ));
+
+ KillSocket(pSocketInfo);
+ }
+ pSocketInfo = next;
+ }
+ ReleaseMutex();
+}
+
+
+LPCONNECTION_INFO
+AllocateConnection(
+ LPSOCKET_INFO pSocketInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Allocates a CONNECTION_INFO structure. If successful, links it at the head
+ of ConnectionList
+
+Arguments:
+
+ pSocketInfo - pointer to owner SOCKET_INFO
+
+Return Value:
+
+ LPCONNECTION_INFO
+ Success - !NULL
+ Failure - NULL
+
+--*/
+
+{
+ LPCONNECTION_INFO pConnectionInfo;
+
+ pConnectionInfo = (LPCONNECTION_INFO)LocalAlloc(LPTR, sizeof(*pConnectionInfo));
+ if (pConnectionInfo) {
+ RequestMutex();
+ pConnectionInfo->ConnectionId = ALLOCATE_CONNECTION_NUMBER();
+ pConnectionInfo->List = ConnectionList;
+ ConnectionList = pConnectionInfo;
+ ReleaseMutex();
+ }
+
+#if SPX_HACK
+ pConnectionInfo->Flags = CF_1ST_RECEIVE;
+#endif
+
+ return pConnectionInfo;
+}
+
+
+VOID
+DeallocateConnection(
+ IN LPCONNECTION_INFO pConnectionInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Undoes the work of AllocateConnection - removes pConnectionInfo from
+ ConnectionList and deallocates the structure
+
+Arguments:
+
+ pConnectionInfo - pointer to CONNECTION_INFO to deallocate
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPCONNECTION_INFO p;
+ LPCONNECTION_INFO prev = (LPCONNECTION_INFO)&ConnectionList;
+
+ RequestMutex();
+ for (p = ConnectionList; p != pConnectionInfo; ) {
+ prev = p;
+ p = p->List;
+ }
+
+ //
+ // if p is NULL or differs from pConnectionInfo then there's a problem
+ //
+
+ ASSERT(p);
+
+ //
+ // special case if pConnectionInfo is first on list: can't say
+ // &ConnectionList->List - accesses one pointer beyond ConnectionList
+ // which is WRONG
+ //
+
+ if (prev == (LPCONNECTION_INFO)&ConnectionList) {
+ ConnectionList = p->List;
+ } else {
+ prev->List = p->List;
+ }
+ FREE_OBJECT(pConnectionInfo);
+ ReleaseMutex();
+}
+
+
+LPCONNECTION_INFO
+FindConnection(
+ IN WORD ConnectionId
+ )
+
+/*++
+
+Routine Description:
+
+ Returns a pointer to CONNECTION_INFO given a unique connection ID
+
+Arguments:
+
+ ConnectionId - value to find
+
+Return Value:
+
+ LPCONNECTION_INFO
+ Success - !NULL
+ Failure - NULL
+
+--*/
+
+{
+ LPCONNECTION_INFO pConnectionInfo;
+
+ RequestMutex();
+ for (pConnectionInfo = ConnectionList; pConnectionInfo; ) {
+ if (pConnectionInfo->ConnectionId == ConnectionId) {
+ break;
+ } else {
+ pConnectionInfo = pConnectionInfo->List;
+ }
+ }
+ ReleaseMutex();
+ return pConnectionInfo;
+}
+
+
+VOID
+QueueConnection(
+ IN LPSOCKET_INFO pSocketInfo,
+ IN LPCONNECTION_INFO pConnectionInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Adds a CONNECTION_INFO to the list of connections owned by a SOCKET_INFO.
+ Points the CONNECTION_INFO back to the SOCKET_INFO
+
+Arguments:
+
+ pSocketInfo - owning SOCKET_INFO
+ pConnectionInfo - CONNECTION_INFO to add
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ pConnectionInfo->Next = pSocketInfo->Connections;
+ pSocketInfo->Connections = pConnectionInfo;
+ pConnectionInfo->OwningSocket = pSocketInfo;
+}
+
+
+LPCONNECTION_INFO
+DequeueConnection(
+ IN LPSOCKET_INFO pSocketInfo,
+ IN LPCONNECTION_INFO pConnectionInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Removes a CONNECTION_INFO from the list of connections owned by a SOCKET_INFO
+
+Arguments:
+
+ pSocketInfo - owning SOCKET_INFO
+ pConnectionInfo - CONNECTION_INFO to remove
+
+Return Value:
+
+ LPCONNECTION_INFO
+ Success - pointer to removed CONNECTION_INFO (should be same as
+ pConnectionInfo)
+ Failure - NULL (not expected)
+
+--*/
+
+{
+ LPCONNECTION_INFO prev = (LPCONNECTION_INFO)&pSocketInfo->Connections;
+ LPCONNECTION_INFO p = prev->Next;
+
+ while (p && p != pConnectionInfo) {
+ prev = p;
+ p = p->Next;
+ }
+
+ ASSERT(p == pConnectionInfo);
+
+ prev->Next = p->Next;
+ p->OwningSocket = NULL;
+ return p;
+}
+
+
+VOID
+KillConnection(
+ IN LPCONNECTION_INFO pConnectionInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Closes a socket belonging to a connection and cancels all outstanding
+ requests. The CONNECTION_INFO is deallocated
+
+Arguments:
+
+ pConnectionInfo - pointer to CONNECTION_INFO to kill
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ if (pConnectionInfo->Socket) {
+ closesocket(pConnectionInfo->Socket);
+ }
+ CancelConnectionQueue(&pConnectionInfo->ConnectQueue);
+ CancelConnectionQueue(&pConnectionInfo->AcceptQueue);
+ CancelConnectionQueue(&pConnectionInfo->ListenQueue);
+ CancelConnectionQueue(&pConnectionInfo->SendQueue);
+ DeallocateConnection(pConnectionInfo);
+}
+
+
+VOID
+AbortOrTerminateConnection(
+ IN LPCONNECTION_INFO pConnectionInfo,
+ IN BYTE CompletionCode
+ )
+
+/*++
+
+Routine Description:
+
+ Aborts or terminates a connection: closes the socket, dequeues and completes
+ all outstanding ECBs with relevant code and deallocates the CONNECTION_INFO
+ structure
+
+ The CONNECTION_INFO must NOT be queued on a SOCKET_INFO when this routine
+ is called
+
+Arguments:
+
+ pConnectionInfo - pointer to CONNECTION_INFO to kill
+ CompletionCode - completion code to put in pending ECBs
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ if (pConnectionInfo->Socket) {
+ closesocket(pConnectionInfo->Socket);
+ }
+ AbortQueue(&pConnectionInfo->ConnectQueue, CompletionCode);
+ AbortQueue(&pConnectionInfo->AcceptQueue, CompletionCode);
+ AbortQueue(&pConnectionInfo->ListenQueue, CompletionCode);
+ AbortQueue(&pConnectionInfo->SendQueue, CompletionCode);
+ DeallocateConnection(pConnectionInfo);
+}
+
+
+VOID
+CheckPendingSpxRequests(
+ BOOL *pfOperationPerformed
+ )
+
+/*++
+
+Routine Description:
+
+ Checks the open non-blocking SPX sockets for:
+
+ errors
+ outgoing established connections (connect)
+ incoming established connections (listen/accept)
+ data to receive (recv)
+ send completions (send)
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPSOCKET_INFO pSocketInfo;
+
+ *pfOperationPerformed = FALSE ;
+
+ RequestMutex();
+ pSocketInfo = SocketList;
+ while (pSocketInfo) {
+ if (pSocketInfo->SpxSocket) {
+
+ LPCONNECTION_INFO pConnectionInfo;
+
+ pConnectionInfo = pSocketInfo->Connections;
+ while (pConnectionInfo) {
+
+ LPCONNECTION_INFO next;
+
+ //
+ // pluck out the Next field now, in case this CONNECTION_INFO
+ // is destroyed as the result of an error
+ //
+
+ next = pConnectionInfo->Next;
+
+ //
+ // if this connection has an active socket or we have issued
+ // SPXListenForConnection against the socket then check the
+ // state
+ //
+
+ if (pConnectionInfo->Socket
+ || (pConnectionInfo->State == CI_WAITING)) {
+
+ SOCKET sock;
+ BOOL readable;
+ BOOL writeable;
+ BOOL sockError;
+
+ CheckSelectRead(pSocketInfo,
+ pConnectionInfo,
+ &readable);
+
+ CheckSelectWrite(pSocketInfo,
+ pConnectionInfo,
+ &writeable);
+
+ sock = pConnectionInfo->Socket
+ ? pConnectionInfo->Socket
+ : pSocketInfo->Socket
+ ;
+
+ if (CheckSocketState(sock, &readable, &writeable, &sockError)) {
+ if (!sockError) {
+
+ if (readable) {
+ AsyncReadAction(pSocketInfo,
+ pConnectionInfo,
+ pfOperationPerformed);
+ }
+ if (writeable) {
+ AsyncWriteAction(pSocketInfo,
+ pConnectionInfo,
+ pfOperationPerformed);
+ }
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "CheckPendingSpxRequests: socket %x has error. Connection %08x state %d\n",
+ sock,
+ pConnectionInfo,
+ pConnectionInfo->State
+ ));
+
+ //
+ // irrespective of the error, we just abort any
+ // connection that gets an error
+ //
+
+ DequeueConnection(pConnectionInfo->OwningSocket,
+ pConnectionInfo
+ );
+ AbortOrTerminateConnection(pConnectionInfo,
+ ECB_CC_CONNECTION_ABORTED
+ );
+ }
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "CheckPendingSpxRequests: CheckSocketState returns %d\n",
+ WSAGetLastError()
+ ));
+
+ }
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "CheckPendingSpxRequests: connection %04x (%08x) in weird state?\n",
+ pConnectionInfo->ConnectionId,
+ pConnectionInfo
+ ));
+
+ }
+ pConnectionInfo = next;
+ }
+ }
+ pSocketInfo = pSocketInfo->Next;
+ }
+ ReleaseMutex();
+}
+
+
+PRIVATE
+BOOL
+CheckSocketState(
+ IN SOCKET Socket,
+ OUT LPBOOL Readable,
+ OUT LPBOOL Writeable,
+ OUT LPBOOL Error
+ )
+
+/*++
+
+Routine Description:
+
+ Given a socket descriptor, checks to see if it is in one of the following
+ states:
+
+ readable - if waiting for a connection, connection has been made
+ else if established, data is ready to be received
+
+ writeable - if waiting to make a connection, connection has been
+ made, else if established, we can send data on this
+ socket
+
+ error - some error has occurred on the socket
+
+Arguments:
+
+ Socket - socket descriptor to check
+ Readable - returned TRUE if readable
+ Writeable - returned TRUE if writeable
+ Error - returned TRUE if error on socket
+
+Return Value:
+
+ BOOL
+ TRUE - contents of Readable, Writeable and Error are valid
+ FALSE - an error occurred performing the select
+
+--*/
+
+{
+ fd_set errors;
+ fd_set reads;
+ fd_set writes;
+ int n;
+ static struct timeval timeout = {0, 0};
+
+ FD_ZERO(&errors);
+ FD_ZERO(&reads);
+ FD_ZERO(&writes);
+
+ if (*Readable)
+ FD_SET(Socket, &reads);
+ if (*Writeable)
+ FD_SET(Socket, &writes);
+ FD_SET(Socket, &errors);
+
+ n = select(0, &reads, &writes, &errors, &timeout);
+
+ if (n != SOCKET_ERROR) {
+ *Readable = (BOOL)(reads.fd_count == 1);
+ *Writeable = (BOOL)(writes.fd_count == 1);
+ *Error = (BOOL)(errors.fd_count == 1);
+ return TRUE;
+ } else if (n) {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "CheckSocketState: select returns %d\n",
+ WSAGetLastError()
+ ));
+
+ }
+ return FALSE;
+}
+
+
+PRIVATE
+VOID
+AsyncReadAction(
+ IN LPSOCKET_INFO pSocketInfo,
+ IN LPCONNECTION_INFO pConnectionInfo,
+ OUT BOOL *ReadPerformed
+ )
+
+/*++
+
+Routine Description:
+
+ A connection has some read action to complete - complete a pending
+ SPXListenForConnection or SPXListenForSequencedPacket
+
+Arguments:
+
+ pSocketInfo - pointer to SOCKET_INFO
+ pConnectionInfo - pointer to CONNECTION_INFO
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ *ReadPerformed = FALSE ;
+
+ switch (pConnectionInfo->State) {
+ case CI_STARTING:
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "AsyncReadAction: STARTING connection %04x (%08x) readable\n",
+ pConnectionInfo->ConnectionId,
+ pConnectionInfo
+ ));
+
+ break;
+
+ case CI_WAITING:
+ if (pConnectionInfo->AcceptQueue.Head) {
+ CompleteAccept(pSocketInfo, pConnectionInfo);
+ *ReadPerformed = TRUE ;
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "AsyncReadAction: connection %04x (%08x): no AcceptQueue\n",
+ pConnectionInfo->ConnectionId,
+ pConnectionInfo
+ ));
+
+ }
+ break;
+
+ case CI_ESTABLISHED:
+ if (pSocketInfo->ListenQueue.Head) {
+ CompleteReceive(pSocketInfo, pConnectionInfo);
+ *ReadPerformed = TRUE ;
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_WARNING,
+ "AsyncReadAction: connection %04x (%08x): no ListenQueue\n",
+ pConnectionInfo->ConnectionId,
+ pConnectionInfo
+ ));
+
+ }
+ break;
+
+ case CI_TERMINATING:
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "AsyncReadAction: TERMINATING connection %04x (%08x) readable\n",
+ pConnectionInfo->ConnectionId,
+ pConnectionInfo
+ ));
+
+ break;
+ }
+}
+
+
+PRIVATE
+VOID
+AsyncWriteAction(
+ IN LPSOCKET_INFO pSocketInfo,
+ IN LPCONNECTION_INFO pConnectionInfo,
+ OUT BOOL *WritePerformed
+ )
+
+/*++
+
+Routine Description:
+
+ A connection has some write action to complete - complete a pending
+ SPXEstablishConnection or SPXSendSequencedPacket
+
+Arguments:
+
+ pSocketInfo - pointer to SOCKET_INFO
+ pConnectionInfo - pointer to CONNECTION_INFO
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ *WritePerformed = FALSE ;
+
+ switch (pConnectionInfo->State) {
+ case CI_STARTING:
+ if (pConnectionInfo->ConnectQueue.Head) {
+ CompleteConnect(pSocketInfo, pConnectionInfo);
+ *WritePerformed = TRUE ;
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "AsyncWriteAction: connection %04x (%08x): no ConnectQueue\n",
+ pConnectionInfo->ConnectionId,
+ pConnectionInfo
+ ));
+
+ }
+ break;
+
+ case CI_WAITING:
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "AsyncWriteAction: WAITING connection %04x (%08x) is writeable\n",
+ pConnectionInfo->ConnectionId,
+ pConnectionInfo
+ ));
+
+ break;
+
+ case CI_ESTABLISHED:
+ if (pConnectionInfo->SendQueue.Head) {
+ CompleteSend(pSocketInfo, pConnectionInfo);
+ *WritePerformed = TRUE ;
+ } else {
+/*
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_WARNING,
+ "AsyncWriteAction: connection %04x (%08x): no SendQueue\n",
+ pConnectionInfo->ConnectionId,
+ pConnectionInfo
+ ));
+*/
+ }
+ break;
+
+ case CI_TERMINATING:
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "AsyncWriteAction: TERMINATING connection %04x (%08x) writeable\n",
+ pConnectionInfo->ConnectionId,
+ pConnectionInfo
+ ));
+
+ break;
+ }
+}
+
+PRIVATE
+VOID
+CheckSelectRead(
+ IN LPSOCKET_INFO pSocketInfo,
+ IN LPCONNECTION_INFO pConnectionInfo,
+ OUT BOOL *CheckRead
+ )
+
+/*++
+
+Routine Description:
+
+ See if want to check for Read readiness in select statement.
+
+Arguments:
+
+ pSocketInfo - pointer to SOCKET_INFO
+ pConnectionInfo - pointer to CONNECTION_INFO
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ *CheckRead = FALSE ;
+
+ switch (pConnectionInfo->State)
+ {
+ case CI_WAITING:
+
+ if (pConnectionInfo->AcceptQueue.Head)
+ *CheckRead = TRUE ;
+ break;
+
+ case CI_ESTABLISHED:
+
+ if (pSocketInfo->ListenQueue.Head)
+ *CheckRead = TRUE ;
+ break;
+
+ default:
+
+ break;
+ }
+}
+
+
+PRIVATE
+VOID
+CheckSelectWrite(
+ IN LPSOCKET_INFO pSocketInfo,
+ IN LPCONNECTION_INFO pConnectionInfo,
+ OUT BOOL *CheckWrite
+ )
+
+/*++
+
+Routine Description:
+
+ See if want to check for Write readiness in select statement.
+
+Arguments:
+
+ pSocketInfo - pointer to SOCKET_INFO
+ pConnectionInfo - pointer to CONNECTION_INFO
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ *CheckWrite = FALSE ;
+
+ switch (pConnectionInfo->State)
+ {
+
+ case CI_STARTING:
+
+ if (pConnectionInfo->ConnectQueue.Head)
+ *CheckWrite = TRUE ;
+ break;
+
+ case CI_ESTABLISHED:
+
+ if (pConnectionInfo->SendQueue.Head)
+ *CheckWrite = TRUE ;
+ break;
+
+ default:
+
+ break;
+ }
+}
+
+
+
+PRIVATE
+VOID
+CompleteAccept(
+ IN LPSOCKET_INFO pSocketInfo,
+ IN LPCONNECTION_INFO pConnectionInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Complete a SPXListenForConnection
+
+Arguments:
+
+ pSocketInfo - pointer to SOCKET_INFO
+ pConnectionInfo - pointer to CONNECTION_INFO
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ SOCKET conn;
+ SOCKADDR_IPX remoteAddress;
+ int addressLength = sizeof(remoteAddress);
+ LPXECB pXecb = pConnectionInfo->AcceptQueue.Head;
+ BOOL true = TRUE;
+ int rc;
+
+ conn = accept(pSocketInfo->Socket, (LPSOCKADDR)&remoteAddress, &addressLength);
+ if (conn != SOCKET_ERROR) {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "CompleteAccept: connection %04x (%08x) socket=%x\n",
+ pConnectionInfo->ConnectionId,
+ pConnectionInfo,
+ conn
+ ));
+
+ //
+ // we want to receive the frame headers from this socket
+ //
+
+ rc = setsockopt(conn,
+ NSPROTO_IPX,
+ IPX_RECVHDR,
+ (char FAR*)&true,
+ sizeof(true)
+ );
+ rc = !SOCKET_ERROR;
+ if (rc != SOCKET_ERROR) {
+
+ //
+ // update the CONNECTION_INFO structure with the actual socket
+ // identifier and set the connection state to established
+ //
+
+ pConnectionInfo->Socket = conn;
+ pConnectionInfo->State = CI_ESTABLISHED;
+
+ //
+ // update the app's ECB with the connection ID
+ //
+
+ SPX_ECB_CONNECTION_ID(pXecb->Ecb) = pConnectionInfo->ConnectionId;
+
+ //
+ // and with the partner address info
+ //
+
+ CopyMemory(&pXecb->Ecb->DriverWorkspace,
+ &remoteAddress.sa_netnum,
+ sizeof(pXecb->Ecb->DriverWorkspace)
+ );
+
+ //
+ // fill in the immediate address field
+ //
+
+ CopyMemory(&pXecb->Ecb->ImmediateAddress,
+ &remoteAddress.sa_nodenum,
+ sizeof(pXecb->Ecb->ImmediateAddress)
+ );
+
+ //
+ // remove the XECB from AcceptQueue and complete the SPXListenForConnection ECB
+ //
+
+ DequeueEcb(pXecb, &pConnectionInfo->AcceptQueue);
+
+ IPXDUMPECB((pXecb->Ecb,
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress),
+ ECB_TYPE_SPX,
+ FALSE,
+ FALSE,
+ IS_PROT_MODE(pXecb)
+ ));
+
+ CompleteOrQueueEcb(pXecb, ECB_CC_SUCCESS);
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "CompleteAccept: setsockopt(IPX_RECVHDR) returns %d\n",
+ WSAGetLastError()
+ ));
+
+ //
+ // BUGBUG: value?
+ //
+
+ closesocket(conn);
+ DequeueEcb(pXecb, &pConnectionInfo->AcceptQueue);
+ DequeueConnection(pSocketInfo, pConnectionInfo);
+ DeallocateConnection(pConnectionInfo);
+ CompleteOrQueueEcb(pXecb, ECB_CC_CONNECTION_ABORTED);
+ }
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "CompleteAccept: accept() returns %d\n",
+ WSAGetLastError()
+ ));
+
+ }
+}
+
+
+PRIVATE
+VOID
+CompleteReceive(
+ IN LPSOCKET_INFO pSocketInfo,
+ IN LPCONNECTION_INFO pConnectionInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Complete a SPXListenForSequencedPacket
+
+Arguments:
+
+ pSocketInfo - pointer to SOCKET_INFO
+ pConnectionInfo - pointer to CONNECTION_INFO
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPXECB pXecb;
+ int rc;
+ BOOL conn_q;
+ LPXECB_QUEUE pQueue;
+ int len;
+ BOOL completeRequest;
+ BYTE status;
+
+ //
+ // receive packets while there are listen ECBs and data waiting
+ //
+
+ while (1) {
+ if (pConnectionInfo->ListenQueue.Head) {
+ pQueue = &pConnectionInfo->ListenQueue;
+ pXecb = pConnectionInfo->ListenQueue.Head;
+ conn_q = TRUE;
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "CompleteReceive: XECB %08x from CONNECTION_INFO %08x\n",
+ pXecb,
+ pConnectionInfo
+ ));
+
+
+ } else if (pSocketInfo->ListenQueue.Head) {
+ pQueue = &pSocketInfo->ListenQueue;
+ pXecb = pSocketInfo->ListenQueue.Head;
+ conn_q = FALSE;
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "CompleteReceive: XECB %08x from SOCKET_INFO %08x\n",
+ pXecb,
+ pSocketInfo
+ ));
+
+ } else {
+ break;
+ }
+
+ rc = recv(pConnectionInfo->Socket, pXecb->Data, pXecb->Length, 0);
+
+ if (rc != SOCKET_ERROR) {
+
+ len = rc;
+ status = ECB_CC_SUCCESS;
+ completeRequest = TRUE;
+
+ } else {
+ rc = WSAGetLastError();
+ if (rc == WSAEMSGSIZE) {
+ len = pXecb->Length;
+ status = ECB_CC_PACKET_OVERFLOW;
+ completeRequest = TRUE;
+ } else {
+ completeRequest = FALSE;
+
+ //
+ // if no data to receive, quit the loop (don't go down error path)
+ //
+
+ if (rc == WSAEWOULDBLOCK) {
+ break;
+ }
+ }
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "CompleteReceive: error %d on socket %08x (CID %04x)\n",
+ rc,
+ pConnectionInfo->Socket,
+ pConnectionInfo->ConnectionId
+ ));
+
+ DUMPXECB(pXecb);
+
+ }
+
+ if( rc == WSAEDISCON ) {
+
+ //
+ // handle the disconnect case - we still need to complete the
+ // ECB.
+ //
+
+ LPSPX_PACKET pPacket = (LPSPX_PACKET)pXecb->Buffer;
+
+ status = ECB_CC_SUCCESS;
+
+
+ pPacket->DestinationConnectId = pConnectionInfo->ConnectionId;
+ pPacket->SourceConnectId = pConnectionInfo->RemoteConnectionId;
+ pPacket->DataStreamType = SPX_DS_TERMINATE ;
+ pPacket->Checksum = 0xffff;
+ pPacket->Length = L2BW(SPX_HEADER_LENGTH);
+ pPacket->TransportControl = 0;
+ pPacket->PacketType = 5;
+
+ pXecb->Length = SPX_HEADER_LENGTH ;
+ ScatterData(pXecb);
+
+ DequeueEcb(pXecb, pQueue);
+
+ //
+ // Put the remote node address in the ECB's immediate address
+ // field
+ //
+
+ CopyMemory(pXecb->Ecb->ImmediateAddress,
+ pConnectionInfo->RemoteNode,
+ sizeof(pXecb->Ecb->ImmediateAddress)
+ );
+
+ CompleteOrQueueIo(pXecb, status);
+
+ DequeueConnection(pConnectionInfo->OwningSocket, pConnectionInfo);
+ AbortOrTerminateConnection(pConnectionInfo, ECB_CC_CONNECTION_ABORTED);
+ break ;
+
+ }
+ else if (completeRequest) {
+
+#if SPX_HACK
+ if (pConnectionInfo->Flags & CF_1ST_RECEIVE) {
+ pConnectionInfo->Flags &= ~CF_1ST_RECEIVE;
+ ModifyFirstReceive(pXecb->Data, &len, pSocketInfo->SocketNumber, pConnectionInfo->Socket);
+ }
+#endif
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "CompleteReceive: recv() on socket %#x returns %d bytes (Addr=%08x)\n",
+ pConnectionInfo->Socket,
+ len,
+ pXecb->Data
+ ));
+
+ IPXDUMPDATA((pXecb->Data, 0, 0, FALSE, (WORD)len));
+
+ pXecb->Length -= len;
+ pXecb->ActualLength += len;
+ pXecb->Data += len;
+ if (pXecb->ActualLength >= SPX_HEADER_LENGTH) {
+ if (pXecb->Flags & XECB_FLAG_FIRST_RECEIVE) {
+
+ LPSPX_PACKET pPacket = (LPSPX_PACKET)pXecb->Buffer;
+
+ //
+ // record in the SPX header the local connection id we invented
+ //
+
+ pPacket->DestinationConnectId = pConnectionInfo->ConnectionId;
+
+ //
+ // record the actual frame length from the header
+ //
+
+ pXecb->FrameLength = B2LW(((LPSPX_PACKET)pXecb->Buffer)->Length);
+ pXecb->Flags &= ~XECB_FLAG_FIRST_RECEIVE;
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "CompleteReceive: FrameLength=%x (%d)\n",
+ pXecb->FrameLength,
+ pXecb->FrameLength
+ ));
+
+ }
+
+ //
+ // if we received all the data in the packet (according to length
+ // field in the SPX header) OR we ran out of buffer space, remove
+ // the ECB from its queue and complete it
+ //
+
+ if (!pXecb->Length || (pXecb->ActualLength == pXecb->FrameLength)) {
+ if (pXecb->Flags & XECB_FLAG_BUFFER_ALLOCATED) {
+
+ //
+ // update the XECB.Length field to reflect the amount of
+ // data received and copy it to the fragmented buffers
+ // in VDM. do not overflow buffer if FrameLength turns
+ // out to be larger than we expect.
+ //
+
+ pXecb->Length = min(pXecb->FrameLength,
+ pXecb->ActualLength);
+ ScatterData(pXecb);
+ }
+ DequeueEcb(pXecb, pQueue);
+
+ // DUMPXECB(pXecb);
+
+
+ IPXDUMPECB((pXecb->Ecb,
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress),
+ ECB_TYPE_SPX,
+ TRUE,
+ TRUE,
+ IS_PROT_MODE(pXecb)
+ ));
+
+ //
+ // Put the remote node address in the ECB's immediate address
+ // field
+ //
+
+ CopyMemory(pXecb->Ecb->ImmediateAddress,
+ pConnectionInfo->RemoteNode,
+ sizeof(pXecb->Ecb->ImmediateAddress)
+ );
+ CompleteOrQueueIo(pXecb, status);
+ } else {
+
+ //
+ // partial receive. If the listen ECB came off the socket
+ // queue then put it on the connection queue: this is the
+ // ECB that will be used for this connection until all data
+ // received or we get an error
+ //
+
+ if (!conn_q) {
+ DequeueEcb(pXecb, &pSocketInfo->ListenQueue);
+ QueueEcb(pXecb,
+ &pConnectionInfo->ListenQueue,
+ CONNECTION_LISTEN_QUEUE
+ );
+ }
+
+ //
+ // not enough data to satisfy read: don't continue yet
+ //
+
+ break;
+ }
+ }
+ } else {
+
+ //
+ // error occurred - abort the connection
+ //
+
+ if (!conn_q) {
+ DequeueEcb(pXecb, &pSocketInfo->ListenQueue);
+ QueueEcb(pXecb,
+ &pConnectionInfo->ListenQueue,
+ CONNECTION_LISTEN_QUEUE
+ );
+ }
+ DequeueConnection(pConnectionInfo->OwningSocket, pConnectionInfo);
+ AbortOrTerminateConnection(pConnectionInfo, ECB_CC_CONNECTION_ABORTED);
+
+ //
+ // don't continue in this case
+ //
+
+ break;
+ }
+ }
+}
+
+
+PRIVATE
+VOID
+CompleteConnect(
+ IN LPSOCKET_INFO pSocketInfo,
+ IN LPCONNECTION_INFO pConnectionInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Complete a SPXEstablishConnection
+
+Arguments:
+
+ pSocketInfo - pointer to SOCKET_INFO
+ pConnectionInfo - pointer to CONNECTION_INFO
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPXECB pXecb = pConnectionInfo->ConnectQueue.Head;
+/*
+ LPSPX_PACKET pPacket;
+
+ //
+ // the connection ID also appears in the first segment of the establish
+ // ECB
+ //
+
+ pPacket = (LPSPX_PACKET)GET_FAR_POINTER(&ECB_FRAGMENT(pXecb->Ecb, 0)->Address,
+ IS_PROT_MODE(pXecb)
+ );
+ pPacket->Checksum = 0xffff;
+ pPacket->Length = L2BW(SPX_HEADER_LENGTH);
+ pPacket->TransportControl = 0;
+ pPacket->PacketType = 5;
+ pPacket->Source.Socket = pSocketInfo->SocketNumber;
+ pPacket->ConnectionControl = 0xc0;
+ pPacket->DataStreamType = 0;
+ pPacket->SourceConnectId = pConnectionInfo->ConnectionId;
+ pPacket->DestinationConnectId = 0xffff;
+ pPacket->SequenceNumber = 0;
+ pPacket->AckNumber = 0;
+ pPacket->AllocationNumber = 0;
+*/
+
+ pConnectionInfo->State = CI_ESTABLISHED;
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "CompleteConnect: connection %04x (%08x) completed\n",
+ pConnectionInfo->ConnectionId,
+ pConnectionInfo
+ ));
+
+ DUMPCONN(pConnectionInfo);
+
+ DequeueEcb(pXecb, &pConnectionInfo->ConnectQueue);
+
+ IPXDUMPECB((pXecb->Ecb,
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress),
+ ECB_TYPE_SPX,
+ TRUE,
+ TRUE,
+ IS_PROT_MODE(pXecb)
+ ));
+
+ CompleteOrQueueEcb(pXecb, ECB_CC_SUCCESS);
+}
+
+
+PRIVATE
+VOID
+CompleteSend(
+ IN LPSOCKET_INFO pSocketInfo,
+ IN LPCONNECTION_INFO pConnectionInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Complete a SPXSendSequencedPacket
+
+Arguments:
+
+ pSocketInfo - pointer to SOCKET_INFO
+ pConnectionInfo - pointer to CONNECTION_INFO
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPXECB pXecb = pConnectionInfo->SendQueue.Head;
+ int rc;
+ BYTE status;
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "CompleteSend: sending %d (0x%x) bytes from %08x\n",
+ pXecb->Length,
+ pXecb->Length,
+ pXecb->Data
+ ));
+
+ IPXDUMPECB((pXecb->Ecb,
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress),
+ ECB_TYPE_SPX,
+ TRUE,
+ TRUE,
+ IS_PROT_MODE(pXecb)
+ ));
+
+ rc = send(pConnectionInfo->Socket, pXecb->Data, pXecb->Length, 0);
+ if (rc == pXecb->Length) {
+
+ //
+ // all data sent
+ //
+
+ status = ECB_CC_SUCCESS;
+ } else if (rc == SOCKET_ERROR) {
+ rc = WSAGetLastError();
+ if (rc == WSAEWOULDBLOCK) {
+
+ //
+ // huh???
+ //
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "CompleteSend: send() returns WSAEWOODBLOCK??\n"
+ ));
+
+ //
+ // leave ECB on queue
+ //
+
+ return;
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "CompleteSend: send() returns %d\n",
+ rc
+ ));
+
+ //
+ // BUGBUG: abort connection?
+ //
+
+ status = ECB_CC_CONNECTION_ABORTED;
+ }
+ } else {
+
+ //
+ // partial data sent. Update the buffer pointer and length fields
+ // and leave this ECB at the head of the send queue
+ //
+
+ pXecb->Data += rc;
+ pXecb->Length -= (WORD)rc;
+ return;
+ }
+ DequeueEcb(pXecb, &pConnectionInfo->SendQueue);
+ CompleteOrQueueIo(pXecb, status);
+}
+
+#if SPX_HACK
+
+PRIVATE
+VOID
+ModifyFirstReceive(
+ LPBYTE Buffer,
+ LPDWORD pLength,
+ WORD SocketNumber,
+ SOCKET Socket
+ )
+{
+ WORD len = *(LPWORD)pLength;
+
+ if ((*(ULPWORD)Buffer != 0xffff) && (*(ULPWORD)(Buffer+2) != L2BW(len))) {
+
+ LPSPX_PACKET packet;
+ SOCKADDR_IPX remote;
+ int rc;
+ int remlen;
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "ModifyFirstReceive: Modifying: Buffer=%08x Length=%04x SocketNumber=%04x Socket=%08x\n",
+ Buffer,
+ len,
+ B2LW(SocketNumber),
+ Socket
+ ));
+
+ MoveMemory(Buffer+42, Buffer, len);
+ packet = (LPSPX_PACKET)Buffer;
+ packet->Checksum = 0xffff;
+ packet->Length = L2BW(42+len);
+ packet->TransportControl = 0;
+ packet->PacketType = 5;
+ CopyMemory((LPVOID)&packet->Destination,
+ (LPVOID)&MyInternetAddress.sa_netnum,
+ sizeof(INTERNET_ADDRESS)
+ );
+ packet->Destination.Socket = SocketNumber;
+ rc = getpeername(Socket, (LPSOCKADDR)&remote, &remlen);
+ if (rc != SOCKET_ERROR) {
+ CopyMemory((LPVOID)&packet->Source,
+ (LPVOID)&remote.sa_netnum,
+ sizeof(NETWARE_ADDRESS)
+ );
+ } else {
+ ZeroMemory((LPVOID)&packet->Source, sizeof(NETWARE_ADDRESS));
+ }
+ packet->ConnectionControl = 0x40;
+ packet->DataStreamType = 0;
+ packet->SourceConnectId = 0;
+ packet->DestinationConnectId = 0;
+ packet->SequenceNumber = 0;
+ packet->AckNumber = 0;
+ packet->AllocationNumber = 0;
+ *pLength += 42;
+ }
+}
+
+#endif
diff --git a/private/nw/vwipxspx/dll/socket.h b/private/nw/vwipxspx/dll/socket.h
new file mode 100644
index 000000000..facf7c6f7
--- /dev/null
+++ b/private/nw/vwipxspx/dll/socket.h
@@ -0,0 +1,318 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ socket.h
+
+Abstract:
+
+ Contains macros, prototypes and structures for socket.c
+
+Author:
+
+ Richard L Firth (rfirth) 25-Oct-1993
+
+Revision History:
+
+ 25-Oct-1993 rfirth
+ Created
+
+--*/
+
+#define ARBITRARY_CONNECTION_NUMBER 0x6c8e
+
+//
+// forward declarations
+//
+
+typedef struct _FIFO *LPFIFO;
+typedef struct _XECB *LPXECB;
+typedef struct _XECB_QUEUE *LPXECB_QUEUE;
+typedef struct _SOCKET_INFO* LPSOCKET_INFO;
+typedef struct _CONNECTION_INFO *LPCONNECTION_INFO;
+
+//
+// FIFO - standard single-linked FIFO queue structure
+//
+
+typedef struct _FIFO {
+ LPVOID Head;
+ LPVOID Tail;
+} FIFO;
+
+//
+// function type for cancelling XECB/ECB
+//
+
+typedef BYTE (*ECB_CANCEL_ROUTINE)(LPXECB);
+
+//
+// QUEUE_ID - indicator of which queue an ECB is on
+//
+
+typedef enum {
+ NO_QUEUE = 0x10cadd1e,
+ ASYNC_COMPLETION_QUEUE = 0xCC5055C0, // arbitrary numbers make life interesting
+ TIMER_QUEUE,
+ SOCKET_LISTEN_QUEUE,
+ SOCKET_SEND_QUEUE,
+ SOCKET_HEADER_QUEUE, // special queue for small ECBs that cannot hold data
+ CONNECTION_CONNECT_QUEUE,
+ CONNECTION_ACCEPT_QUEUE,
+ CONNECTION_SEND_QUEUE,
+ CONNECTION_LISTEN_QUEUE
+} QUEUE_ID;
+
+//
+// XECB - our copy of the ECB (IPX or AES)
+//
+
+typedef struct _XECB {
+ LPXECB Next;
+ LPECB Ecb; // points to ECB in DOS memory
+ ECB_ADDRESS EcbAddress; // segmented address of ECB in DOS memory
+ ESR_ADDRESS EsrAddress; // Event Service Routine in DOS memory
+ LPBYTE Buffer; // address of 32-bit buffer
+ LPBYTE Data; // moveable data pointer
+ WORD FrameLength; // actual size of frame (from IPX/SPX header)
+ WORD ActualLength; // same as FrameLength. Not decremented
+ WORD Length; // length of 32-bit buffer
+ WORD Ticks; // for AES
+ WORD SocketNumber; // number of owning socket
+ WORD Owner; // owning DOS Task ID
+ DWORD TaskId; // owning Windows Task ID
+ DWORD Flags; // see below
+ QUEUE_ID QueueId; // identifies the queue for quick location
+ LPVOID OwningObject; // which SOCKET_INFO or CONNECTION_INFO the queue is on
+ DWORD RefCount; // the dreaded reference count
+} XECB;
+
+//
+// XECB flags
+//
+
+#define XECB_FLAG_AES 0x00000000
+#define XECB_FLAG_IPX 0x00000001
+#define XECB_FLAG_TEMPORARY_SOCKET 0x00000002
+#define XECB_FLAG_BUFFER_ALLOCATED 0x00000004
+#define XECB_FLAG_LISTEN 0x00000008
+#define XECB_FLAG_SEND 0x00000010
+#define XECB_FLAG_TIMER 0x00000020
+#define XECB_FLAG_ASYNC 0x00000040
+#define XECB_FLAG_FIRST_RECEIVE 0x00000080
+#define XECB_FLAG_SPX 0x00000100
+#define XECB_FLAG_PROTMODE 0x00000200
+
+#define IS_PROT_MODE(p) (((p)->Flags & XECB_FLAG_PROTMODE) ? TRUE : FALSE)
+
+//
+// XECB_QUEUE - queue of XECBs
+//
+
+typedef struct _XECB_QUEUE {
+ LPXECB Head;
+ LPXECB Tail;
+} XECB_QUEUE;
+
+//
+// SOCKET_INFO - maintains info about IPX sockets
+//
+
+typedef struct _SOCKET_INFO {
+ LPSOCKET_INFO Next;
+ WORD SocketNumber; // big-endian socket (bound port)
+ WORD Owner; // DOS PDB
+ DWORD TaskId; // Windows owner
+ SOCKET Socket; // the WinSock socket handle
+ DWORD Flags;
+
+ //
+ // BUGBUG: these next 2 fields can be flags
+ //
+
+ BOOL LongLived; // TRUE if keep-alive when app dies
+ BOOL SpxSocket; // TRUE if socket opened for SPX
+
+ //
+ // BUGBUG: these next 2 fields can we WORD or even BYTE (do we even need
+ // them - only used to tell AES if pending requests - it can look at the
+ // queue heads?
+ //
+
+ DWORD PendingSends; // used by cancel
+ DWORD PendingListens; // used by cancel
+
+ //
+ // ListenQueue is used for IPXListenForPacket and SPXListenForSequencedPacket
+ //
+
+ XECB_QUEUE ListenQueue; // pool of listening ECBs against this socket
+
+ //
+ // SendQueue is used by IPX for IPXSendPacket
+ //
+
+ XECB_QUEUE SendQueue; // queue of pending send ECBs against this socket
+
+ //
+ // HeaderQueue is used to hold small ECBs that can only take header info.
+ // We have this separate queue to make sure that we do not put ECBs that
+ // really cant accept any data into the Listen Queue.
+ //
+
+ XECB_QUEUE HeaderQueue; // pool of header ECBs against this socket
+
+ LPCONNECTION_INFO Connections;
+} SOCKET_INFO;
+
+#define SOCKET_FLAG_LISTENING 0x00000001
+#define SOCKET_FLAG_SENDING 0x00000002
+#define SOCKET_FLAG_TEMPORARY 0x80000000
+
+//
+// CONNECTION_INFO - maintains info about SPX sockets
+//
+
+typedef struct _CONNECTION_INFO {
+ LPCONNECTION_INFO Next; // next CONNECTION_INFO by OwningSocket
+ LPCONNECTION_INFO List; // all CONNECTION_INFO are linked together
+ LPSOCKET_INFO OwningSocket; // back-pointer to SOCKET_INFO
+ SOCKET Socket; // handle to socket
+ DWORD TaskId; // identifies windows task/owner
+ WORD ConnectionId; // analogous to SocketNumber
+ BYTE Flags;
+ BYTE State;
+ XECB_QUEUE ConnectQueue; // outgoing connections being made
+ XECB_QUEUE AcceptQueue; // waiting for incoming connections
+ XECB_QUEUE SendQueue; // packet sends on this connection
+ XECB_QUEUE ListenQueue; // partially complete receive
+ BYTE RemoteNode[6];
+ WORD RemoteConnectionId;
+} CONNECTION_INFO;
+
+//
+// CONNECTION_INFO Flag field values
+//
+
+#define CF_1ST_RECEIVE 0x80 // hack-o-rama till NWLink timing problem fixed
+
+//
+// CONNECTION_INFO State field values
+//
+
+#define CI_WAITING 0x01
+#define CI_STARTING 0x02
+#define CI_ESTABLISHED 0x03
+#define CI_TERMINATING 0x04
+
+//
+// one-line function macros
+//
+
+#define AllocateSocket() (LPSOCKET_INFO)LocalAlloc(LPTR, sizeof(SOCKET_INFO))
+#define DeallocateSocket(p) FREE_OBJECT(p)
+
+//
+// SocketType parameter for CreateSocket
+//
+
+typedef enum {
+ SOCKET_TYPE_IPX,
+ SOCKET_TYPE_SPX
+} SOCKET_TYPE;
+
+//
+// function prototypes
+//
+
+int
+CreateSocket(
+ IN SOCKET_TYPE SocketType,
+ IN OUT ULPWORD pSocketNumber,
+ OUT SOCKET* pSocket
+ );
+
+LPSOCKET_INFO
+AllocateTemporarySocket(
+ VOID
+ );
+
+VOID
+QueueSocket(
+ IN LPSOCKET_INFO pSocketInfo
+ );
+
+LPSOCKET_INFO
+DequeueSocket(
+ IN LPSOCKET_INFO pSocketInfo
+ );
+
+LPSOCKET_INFO
+FindSocket(
+ IN WORD SocketNumber
+ );
+
+LPSOCKET_INFO
+FindActiveSocket(
+ IN LPSOCKET_INFO pSocketInfo
+ );
+
+int
+ReopenSocket(
+ LPSOCKET_INFO pSocketInfo
+ );
+
+VOID
+KillSocket(
+ IN LPSOCKET_INFO pSocketInfo
+ );
+
+VOID
+KillShortLivedSockets(
+ IN WORD Owner
+ );
+
+LPCONNECTION_INFO
+AllocateConnection(
+ LPSOCKET_INFO pSocketInfo
+ );
+
+VOID
+DeallocateConnection(
+ IN LPCONNECTION_INFO pConnectionInfo
+ );
+
+LPCONNECTION_INFO
+FindConnection(
+ IN WORD ConnectionId
+ );
+
+VOID
+QueueConnection(
+ IN LPSOCKET_INFO pSocketInfo,
+ IN LPCONNECTION_INFO pConnectionInfo
+ );
+
+LPCONNECTION_INFO
+DequeueConnection(
+ IN LPSOCKET_INFO pSocketInfo,
+ IN LPCONNECTION_INFO pConnectionInfo
+ );
+
+VOID
+KillConnection(
+ IN LPCONNECTION_INFO pConnectionInfo
+ );
+
+VOID
+AbortOrTerminateConnection(
+ IN LPCONNECTION_INFO pConnectionInfo,
+ IN BYTE CompletionCode
+ );
+
+VOID
+CheckPendingSpxRequests(
+ BOOL *pfOperationPerformed
+ );
diff --git a/private/nw/vwipxspx/dll/sources b/private/nw/vwipxspx/dll/sources
new file mode 100644
index 000000000..2cb76d030
--- /dev/null
+++ b/private/nw/vwipxspx/dll/sources
@@ -0,0 +1,64 @@
+!IF 0
+
+Copyright (c) 1989-1991 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.
+
+
+History:
+
+ Created 12-Sep-1991 by Richard L Firth (rfirth)
+ from template created 12-Apr-1990 by Steve Wood (stevewo)
+
+
+NOTE: Commented description of this file is in \nt\public\oak\bin\sources.tpl
+
+!ENDIF
+
+MAJORCOMP=nw
+MINORCOMP=vwipxspx
+
+TARGETNAME=vwipxspx
+TARGETPATH=$(BASEDIR)\public\sdk\lib
+TARGETTYPE=DYNLINK
+TARGETLIBS=$(BASEDIR)\public\sdk\lib\*\kernel32.lib \
+ $(BASEDIR)\public\sdk\lib\*\ntvdm.lib \
+ $(BASEDIR)\public\sdk\lib\*\wsock32.lib
+
+DLLENTRY=VwDllEntryPoint
+DLLBASE=0x03200000
+
+USE_CRTDLL=1
+
+MSC_WARNING_LEVEL=/W3 /WX
+
+INCLUDES=.;..\..\inc;$(BASEDIR)\private\inc;$(BASEDIR)\private\mvdm\vdd\h
+
+SOURCES=vwdll.c \
+ vwipx.c \
+ vwspx.c \
+ vwasync.c \
+ vwmisc.c \
+ vwdebug.c \
+ socket.c \
+ vwinapi.c \
+ vwdos.c \
+ vwipxspx.rc \
+ util.c
+
+C_DEFINES=-DMAX_OPEN_SOCKETS=150 -DSPX_HACK=1 -DREUSEADDR=1
+
+!ifdef MARS_PCH
+PRECOMPILED_INCLUDE=vw.h
+PRECOMPILED_PCH=vw.pch
+PRECOMPILED_OBJ=vw.obj
+!endif
diff --git a/private/nw/vwipxspx/dll/util.c b/private/nw/vwipxspx/dll/util.c
new file mode 100644
index 000000000..5e4aac78f
--- /dev/null
+++ b/private/nw/vwipxspx/dll/util.c
@@ -0,0 +1,2966 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ util.c
+
+Abstract:
+
+ ntVdm netWare (Vw) IPX/SPX Functions
+
+ Vw: The peoples' network
+
+ Contains various utility routines
+
+ Contents:
+ GetInternetAddress
+ GetMaxPacketSize
+ RetrieveEcb
+ RetrieveXEcb
+ (AllocateXecb)
+ (DeallocateXecb)
+ ScheduleEvent
+ ScanTimerList
+ CancelTimerEvent
+ CancelTimedEvents
+ CancelAsyncEvent
+ CancelSocketEvent
+ CancelConnectionEvent
+ QueueEcb
+ DequeueEcb
+ CancelSocketQueue
+ CancelConnectionQueue
+ AbortQueue
+ AbortConnectionEvent
+ StartIpxSend
+ GetIoBuffer
+ (ReleaseIoBuffer)
+ GatherData
+ ScatterData
+ IpxReceiveFirst
+ IpxReceiveNext
+ (IpxSendFirst)
+ IpxSendNext
+ (QueueReceiveRequest)
+ (DequeueReceiveRequest)
+ (QueueSendRequest)
+ (DequeueSendRequest)
+ CompleteOrQueueIo
+ CompleteIo
+ CompleteOrQueueEcb
+ CompleteEcb
+ (QueueAsyncCompletion)
+ EsrCallback
+ VWinEsrCallback
+ FifoAddHead
+ FifoAdd
+ FifoRemove
+ FifoNext
+
+Author:
+
+ Richard L Firth (rfirth) 30-Sep-1993
+
+Environment:
+
+ User-mode Win32
+
+Revision History:
+
+ 30-Sep-1993 rfirth
+ Created
+
+--*/
+
+#include "vw.h"
+#pragma hdrstop
+
+//
+// private routine prototypes
+//
+
+PRIVATE
+LPXECB
+AllocateXecb(
+ VOID
+ );
+
+PRIVATE
+VOID
+DeallocateXecb(
+ IN LPXECB pXecb
+ );
+
+PRIVATE
+VOID
+ReleaseIoBuffer(
+ IN LPXECB pXecb
+ );
+
+PRIVATE
+VOID
+IpxSendFirst(
+ IN LPXECB pXecb,
+ IN LPSOCKET_INFO pSocketInfo
+ );
+
+PRIVATE
+VOID
+QueueReceiveRequest(
+ IN LPXECB pXecb,
+ IN LPSOCKET_INFO pSocketInfo
+ );
+
+PRIVATE
+LPXECB
+DequeueReceiveRequest(
+ IN LPXECB pXecb,
+ IN LPSOCKET_INFO pSocketInfo
+ );
+
+PRIVATE
+VOID
+QueueSendRequest(
+ IN LPXECB pXecb,
+ IN LPSOCKET_INFO pSocketInfo
+ );
+
+PRIVATE
+LPXECB
+DequeueSendRequest(
+ IN LPXECB pXecb,
+ IN LPSOCKET_INFO pSocketInfo
+ );
+
+PRIVATE
+VOID
+QueueAsyncCompletion(
+ IN LPXECB pXecb,
+ IN BYTE CompletionCode
+ );
+
+//
+// private data
+//
+
+//
+// TimerList - singly-linked list of timed events, in order of duration
+//
+
+PRIVATE LPXECB TimerList = NULL;
+
+//
+// AsyncCompletionQueue - keeps list of completed ECBs awaiting removal via
+// ESR callback
+//
+
+PRIVATE FIFO AsyncCompletionQueue = {NULL, NULL};
+
+//
+// sort-of-private data (matches not-really-global data in other modules)
+//
+
+//
+// SerializationCritSec - grab this when manipulating SOCKET_INFO list
+//
+
+CRITICAL_SECTION SerializationCritSec;
+
+//
+// AsyncCritSec - grab this when manipulating AsyncCompletionQueue
+//
+
+CRITICAL_SECTION AsyncCritSec;
+
+//
+// functions
+//
+
+
+int
+GetInternetAddress(
+ IN OUT LPSOCKADDR_IPX InternetAddress
+ )
+
+/*++
+
+Routine Description:
+
+ Gets the node and net numbers for this station
+
+Arguments:
+
+ InternetAddress - pointer to SOCKADDR_IPX structure to fill with internetwork
+ address for this station
+
+Return Value:
+
+ int
+ Success - 0
+ Failure - SOCKET_ERROR
+
+--*/
+
+{
+ SOCKET s;
+ int rc;
+ int structureLength = sizeof(*InternetAddress);
+
+ s = socket(AF_IPX, SOCK_DGRAM, NSPROTO_IPX);
+ if (s != INVALID_SOCKET) {
+
+ //
+ // make dynamic binding (socket number = 0)
+ //
+
+ ZeroMemory(InternetAddress, structureLength);
+ InternetAddress->sa_family = AF_IPX;
+ rc = bind(s, (LPSOCKADDR)InternetAddress, structureLength);
+ if (rc != SOCKET_ERROR) {
+ rc = getsockname(s, (LPSOCKADDR)InternetAddress, &structureLength);
+ if (rc) {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "GetInternetAddress: getsockname() returns %d\n",
+ WSAGetLastError()
+ ));
+
+ }
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "GetInternetAddress: bind() returns %d\n",
+ WSAGetLastError()
+ ));
+
+ }
+ closesocket(s);
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "GetInternetAddress: socket() returns %d\n",
+ WSAGetLastError()
+ ));
+
+ rc = SOCKET_ERROR;
+ }
+ return rc;
+}
+
+
+int
+GetMaxPacketSize(
+ OUT LPWORD MaxPacketSize
+ )
+
+/*++
+
+Routine Description:
+
+ Returns the maximum packet allowed by the underlying transport
+
+Arguments:
+
+ MaxPacketSize - pointer to returned maximum packet size
+
+Return Value:
+
+ int
+ Success - 0
+ Failure - SOCKET_ERROR
+
+--*/
+
+{
+ SOCKET s;
+ int maxLen, maxLenSize = sizeof(maxLen);
+ int rc;
+ SOCKADDR_IPX ipxAddr;
+
+ s = socket(AF_IPX, SOCK_DGRAM, NSPROTO_IPX);
+ if (s != SOCKET_ERROR) {
+
+ //
+ // set socket to 0 - causes any applicable address to be bound
+ //
+
+ ZeroMemory(&ipxAddr, sizeof(ipxAddr));
+ ipxAddr.sa_family = AF_IPX;
+ rc = bind(s, (LPSOCKADDR)&ipxAddr, sizeof(ipxAddr));
+ if (rc != SOCKET_ERROR) {
+
+ rc = getsockopt(s,
+ NSPROTO_IPX,
+ IPX_MAXSIZE,
+ (char FAR*)&maxLen,
+ &maxLenSize
+ );
+ if (rc != SOCKET_ERROR) {
+
+ //
+ // IPX_MAXSIZE always returns the amount of data that can be
+ // transmitted in a single frame. 16-bit IPX/SPX requires that
+ // the IPX header length be included in the data size
+ //
+
+ maxLen += IPX_HEADER_LENGTH;
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "GetMaxPacketSize: getsockopt() returns %d\n",
+ WSAGetLastError()
+ ));
+
+ }
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "GetMaxPacketSize: bind() returns %d\n",
+ WSAGetLastError()
+ ));
+
+ }
+ closesocket(s);
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "GetMaxPacketSize: socket() returns %d\n",
+ WSAGetLastError()
+ ));
+
+ rc = SOCKET_ERROR;
+ }
+
+ //
+ // BUGBUG: since IPX and SPX can only deal in packets of max size 576 bytes,
+ // should we just return 576 if the indicated size is larger???
+ //
+
+ *MaxPacketSize = (rc != SOCKET_ERROR) ? maxLen : MAXIMUM_IPX_PACKET_LENGTH;
+
+ return rc;
+}
+
+
+LPXECB
+RetrieveEcb(
+ IN BYTE EcbType
+ )
+
+/*++
+
+Routine Description:
+
+ Returns pointer to 32-bit extended ECB structure which contains flat pointer
+ to IPX or AES ECB in VDM memory
+
+ We allocate the extended ECB for 3 reasons:
+
+ 1. Avoids 16-bit app scribbling over our control fields
+ 2. Don't have to make unaligned references to all fields (still need some)
+ 3. Don't have enough space in AES ECB to remember all the stuff we need
+
+ However, we do update the 16-bit ECB's LinkAddress field. We use this as a
+ pointer to the 32-bit XECB we allocate in this routine. This just saves us
+ having to traverse all the lists looking for the address of the 16-bit ECB
+ (which we could still do as a fall-back)
+
+Arguments:
+
+ EcbType - type of ECB - AES, IPX or SPX
+
+Return Value:
+
+ LPXECB - 32-bit pointer to extended ECB structure
+
+--*/
+
+{
+ WORD segment;
+ WORD offset;
+ LPECB pEcb;
+
+ segment = IPX_GET_ECB_SEGMENT();
+ offset = IPX_GET_ECB_OFFSET();
+ pEcb = (LPIPX_ECB)POINTER_FROM_WORDS(segment, offset, sizeof(IPX_ECB));
+
+ return RetrieveXEcb(EcbType, pEcb, (ECB_ADDRESS)MAKELONG(offset,segment));
+}
+
+
+LPXECB
+RetrieveXEcb(
+ IN BYTE EcbType,
+ LPECB pEcb,
+ ECB_ADDRESS EcbAddress
+ )
+
+/*++
+
+Routine Description:
+
+ worker for RetrieveEcb, callable from windows functions (ex DOS parms)
+
+Arguments:
+
+ EcbType - type of ECB - AES, IPX or SPX
+ pEcb - pointer to the 16-bit ECB
+ EcbAddress - address (seg:off in DWORD) of 16-bit ECB
+
+Return Value:
+
+ LPXECB
+
+--*/
+
+{
+ LPXECB pXecb;
+
+ if (pEcb) {
+
+ //
+ // allocate and fill-in 32-bit extended ECB structure. If can't allocate
+ // then return NULL
+ //
+
+ pXecb = AllocateXecb();
+ if (pXecb) {
+ pXecb->Ecb = pEcb;
+ pXecb->EcbAddress = EcbAddress;
+ pXecb->EsrAddress = pEcb->EsrAddress;
+
+ //
+ // set flags - IPX/AES, SPX, protect-mode
+ //
+
+ pXecb->Flags |= (((EcbType == ECB_TYPE_IPX) || (EcbType == ECB_TYPE_SPX))
+ ? XECB_FLAG_IPX
+ : XECB_FLAG_AES)
+ | ((EcbType == ECB_TYPE_SPX) ? XECB_FLAG_SPX : 0)
+ | ((getMSW() & MSW_PE) ? XECB_FLAG_PROTMODE : 0);
+
+ //
+ // this XECB is not yet on a queue
+ //
+
+ pXecb->QueueId = NO_QUEUE;
+
+ //
+ // mark the 16-bit ECB as being used. We use an undefined value to
+ // make sure it gets set/reset in the right places
+ //
+
+ pEcb->InUse = ECB_IU_TEMPORARY;
+
+ //
+ // use the LinkAddress field in the 16-bit ECB to point to the XECB.
+ // We use this when cancelling the ECB
+ //
+
+ pEcb->LinkAddress = pXecb;
+
+ //
+ // AES and IPX ECBs have different sizes and different layouts
+ //
+
+ if ((EcbType == ECB_TYPE_IPX) || (EcbType == ECB_TYPE_SPX)) {
+ pXecb->SocketNumber = pEcb->SocketNumber;
+ }
+ }
+ } else {
+ pXecb = NULL;
+ }
+ return pXecb;
+}
+
+
+PRIVATE
+LPXECB
+AllocateXecb(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Allocate an XECB; zero it; set the reference count to 1
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ LPXECB
+
+--*/
+
+{
+ LPXECB pXecb;
+
+ pXecb = (LPXECB)LocalAlloc(LPTR, sizeof(*pXecb));
+ if (pXecb) {
+ pXecb->RefCount = 1;
+ }
+ return pXecb;
+}
+
+
+PRIVATE
+VOID
+DeallocateXecb(
+ IN LPXECB pXecb
+ )
+
+/*++
+
+Routine Description:
+
+ decrement the XECB reference count (while holding SerializationCritSec). If
+ goes to 0 then free the structure (else other thread is also holding pointer
+ to XECB)
+
+Arguments:
+
+ pXecb - XECB to deallocate
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RequestMutex();
+ --pXecb->RefCount;
+ if (!pXecb->RefCount) {
+
+#if DBG
+ FillMemory(pXecb, sizeof(*pXecb), 0xFF);
+#endif
+
+ FREE_OBJECT(pXecb);
+ }
+ ReleaseMutex();
+}
+
+
+VOID
+ScheduleEvent(
+ IN LPXECB pXecb,
+ IN WORD Ticks
+ )
+
+/*++
+
+Routine Description:
+
+ Adds an ECB to the TimerList, ordered by Ticks. The value of Ticks cannot
+ be zero
+
+ Assumes 1. Ticks != 0
+ 2. pXecb->Next is already NULL (as result of LocalAlloc(LPTR,...)
+
+Arguments:
+
+ pXecb - pointer to XECB describing IPX or AES ECB to queue
+ Ticks - number of ticks to elapse before ECB is cooked
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ ASSERT(Ticks);
+ ASSERT(pXecb->Next == NULL);
+
+ RequestMutex();
+ if (!TimerList) {
+ TimerList = pXecb;
+ } else {
+ if (TimerList->Ticks > Ticks) {
+ TimerList->Ticks -= Ticks;
+ pXecb->Next = TimerList;
+ TimerList = pXecb;
+ } else {
+
+ LPXECB previous = (LPXECB)TimerList;
+ LPXECB this = previous->Next;
+
+ Ticks -= TimerList->Ticks;
+ while (this && Ticks > this->Ticks) {
+ Ticks -= this->Ticks;
+ previous = this;
+ this = this->Next;
+ }
+ previous->Next = pXecb;
+ pXecb->Next = this;
+ }
+ }
+ pXecb->Ticks = Ticks;
+ pXecb->QueueId = TIMER_QUEUE;
+ ReleaseMutex();
+}
+
+
+VOID
+ScanTimerList(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Called once per tick. Decrements the tick count of the ECB at the head of
+ the list. If it goes to zero, completes the ECB and any subsequent ECBs
+ which whose tick count would go to zero
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPXECB pXecb;
+
+ RequestMutex();
+ pXecb = TimerList;
+ if (pXecb) {
+
+ //
+ // Decrement if not already zero. Can be zero because the ECB at the
+ // front of the list could have been Cancelled. This makes sure we
+ // do not wrap around to 0xFFFF !!!
+ //
+
+ if (pXecb->Ticks != 0)
+ --pXecb->Ticks;
+
+ if (!pXecb->Ticks) {
+
+ //
+ // complete all ECBs that would go to 0 on this tick
+ //
+
+ while (pXecb->Ticks <= 1) {
+ TimerList = pXecb->Next;
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "ScanTimerList: ECB %04x:%04x is done\n",
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress)
+ ));
+
+ CompleteOrQueueEcb(pXecb, ECB_CC_SUCCESS);
+ pXecb = TimerList;
+ if (!pXecb) {
+ break;
+ }
+ }
+ }
+ }
+
+ ReleaseMutex();
+}
+
+
+BYTE
+CancelTimerEvent(
+ IN LPXECB pXecb
+ )
+
+/*++
+
+Routine Description:
+
+ Cancels a pending event on the timer list
+
+Arguments:
+
+ pXecb - pointer to XECB to cancel
+
+Return Value:
+
+ BYTE
+ Success - IPX_SUCCESS
+ Failure - IPX_ECB_NOT_IN_USE
+
+--*/
+
+{
+ LPXECB listptr;
+ LPXECB previous = (LPXECB)&TimerList;
+ BYTE status;
+
+ RequestMutex();
+ listptr = TimerList;
+ while (listptr && listptr != pXecb) {
+ previous = listptr;
+ listptr = listptr->Next;
+ }
+ if (listptr) {
+
+ //
+ // take the XECB out of the list and complete the ECB (in VDM memory).
+ // Does not generate a call-back to the ESR. When CompleteEcb returns,
+ // the XECB has been deallocated
+ //
+
+ previous->Next = listptr->Next;
+
+ ASSERT(pXecb->RefCount == 2);
+
+ --pXecb->RefCount;
+ CompleteEcb(pXecb, ECB_CC_CANCELLED);
+ status = IPX_SUCCESS;
+ } else {
+ status = IPX_ECB_NOT_IN_USE;
+ }
+ ReleaseMutex();
+ return status;
+}
+
+
+VOID
+CancelTimedEvents(
+ IN WORD SocketNumber,
+ IN WORD Owner,
+ IN DWORD TaskId
+ )
+
+/*++
+
+Routine Description:
+
+ traverses the TimerList cancelling any IPX or AES events owned by any of
+ SocketNumber, Owner or TaskId
+
+ Assumes valid SocketNumber, Owner or TaskId cannot be 0
+
+Arguments:
+
+ SocketNumber - owning socket of IPX events to cancel
+ Owner - owning DOS PDB
+ TaskID - owning Windows Task ID
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPXECB pXecb;
+ LPXECB prev = (LPXECB)&TimerList;
+ LPXECB next;
+
+ RequestMutex();
+ pXecb = TimerList;
+ while (pXecb) {
+
+ next = pXecb->Next;
+
+ if ((SocketNumber && (pXecb->SocketNumber == SocketNumber))
+ || (Owner && !(pXecb->Flags & XECB_FLAG_IPX) && (pXecb->Owner == Owner))
+ || (TaskId && (pXecb->TaskId == TaskId))) {
+
+ prev->Next = next;
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "CancelTimedEvents: cancelling ECB %08x (%04x:%04x)\n",
+ pXecb,
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress)
+ ));
+
+ CompleteEcb(pXecb, ECB_CC_CANCELLED);
+ }
+ else
+ {
+ prev = pXecb ;
+ }
+ pXecb = next;
+ }
+ ReleaseMutex();
+}
+
+
+BYTE
+CancelAsyncEvent(
+ IN LPXECB pXecb
+ )
+
+/*++
+
+Routine Description:
+
+ Called to cancel an event currently on the async completion list. We don't
+ cancel these events - just return 0xF9 (ECB cannot be cancelled). It is a
+ race to see who gets there first - us with the cancel, or the ESR callback.
+ In this case it is fairly immaterial
+
+Arguments:
+
+ pXecb - pointer to XECB to cancel (ignored)
+
+Return Value:
+
+ BYTE - IPX_CANNOT_CANCEL
+
+--*/
+
+{
+ //
+ // we call DeallocateXecb to reduce the reference count. If the other thread
+ // really tried to deallocate it in the short time we've been looking at it
+ // on the cancel path, the call will finish up what the other thread started
+ //
+
+ DeallocateXecb(pXecb);
+ return IPX_CANNOT_CANCEL;
+}
+
+
+BYTE
+CancelSocketEvent(
+ IN LPXECB pXecb
+ )
+
+/*++
+
+Routine Description:
+
+ Called to cancel a pending send or listen from a socket queue. Request can
+ be IPX or SPX. If IPX event, then the ECB is on either the SendQueue or
+ ListenQueue. If SPX, it may be on a CONNECTION_INFO ConnectQueue,
+ AcceptQueue, SendQueue or ListenQueue, or if it is an
+ SPXListenForSequencedPacket request that is still in the pool then it may
+ be on the owning SOCKET_INFO ListenQueue
+
+Arguments:
+
+ pXecb - pointer to XECB describing ECB to cancel
+
+Return Value:
+
+ BYTE - IPX_SUCCESS
+
+--*/
+
+{
+ LPXECB ptr;
+ LPVOID pObject;
+
+ RequestMutex();
+ pObject = pXecb->OwningObject;
+ switch (pXecb->QueueId) {
+ case SOCKET_LISTEN_QUEUE:
+ if (pXecb->Flags & XECB_FLAG_SPX) {
+ ptr = DequeueEcb(pXecb, &((LPSOCKET_INFO)pObject)->ListenQueue);
+ } else {
+ ptr = DequeueReceiveRequest(pXecb, (LPSOCKET_INFO)pObject);
+ }
+ break;
+
+ case SOCKET_SEND_QUEUE:
+ if (pXecb->Flags & XECB_FLAG_SPX) {
+ ptr = DequeueEcb(pXecb, &((LPSOCKET_INFO)pObject)->SendQueue);
+ } else {
+ ptr = DequeueSendRequest(pXecb, (LPSOCKET_INFO)pObject);
+ }
+ break;
+
+ case SOCKET_HEADER_QUEUE: // SPX only
+ if (pXecb->Flags & XECB_FLAG_SPX) {
+ ptr = DequeueEcb(pXecb, &((LPSOCKET_INFO)pObject)->HeaderQueue);
+ } else {
+ ASSERT(FALSE);
+ }
+ break;
+ }
+ ReleaseMutex();
+ if (ptr) {
+ CompleteIo(ptr, ECB_CC_CANCELLED);
+ }
+ return IPX_SUCCESS;
+}
+
+
+BYTE
+CancelConnectionEvent(
+ IN LPXECB pXecb
+ )
+
+/*++
+
+Routine Description:
+
+ Cancels a pending SPXListenForConnection or SPXListenForSequencedPacket, the
+ only cancellable SPX requests
+
+Arguments:
+
+ pXecb - pointer to SPX XECB to cancel
+
+Return Value:
+
+ BYTE - IPX_SUCCESS
+
+--*/
+
+{
+ LPXECB ptr;
+ LPVOID pObject;
+ LPXECB_QUEUE pQueue;
+
+ RequestMutex();
+ pObject = pXecb->OwningObject;
+ switch (pXecb->QueueId) {
+ case CONNECTION_ACCEPT_QUEUE:
+ pQueue = &((LPCONNECTION_INFO)pObject)->AcceptQueue;
+ break;
+
+ case CONNECTION_LISTEN_QUEUE:
+ pQueue = &((LPCONNECTION_INFO)pObject)->ListenQueue;
+ break;
+ }
+ ptr = DequeueEcb(pXecb, pQueue);
+ ReleaseMutex();
+ if (ptr) {
+ CompleteIo(ptr, ECB_CC_CANCELLED);
+ }
+ return IPX_SUCCESS;
+}
+
+
+VOID
+QueueEcb(
+ IN LPXECB pXecb,
+ IN LPXECB_QUEUE Queue,
+ IN QUEUE_ID QueueId
+ )
+
+/*++
+
+Routine Description:
+
+ Adds an XECB to a queue and sets the queue identifier in the XECB.
+
+Arguments:
+
+ pXecb - pointer to XECB to queue
+ Queue - pointer to queue to add XECB to (at tail)
+ QueueId - identifies Queue
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPVOID owningObject = NULL;
+
+#define CONTAINER_STRUCTURE(p, t, f) (LPVOID)(((LPBYTE)(p)) - (DWORD)(&((t)0)->f))
+
+ pXecb->QueueId = QueueId;
+ switch (QueueId) {
+ case SOCKET_LISTEN_QUEUE:
+ if (Queue->Tail && (Queue->Tail->Length < pXecb->Length)) {
+ FifoAddHead((LPFIFO)Queue, (LPFIFO)pXecb);
+ } else {
+ FifoAdd((LPFIFO)Queue, (LPFIFO)pXecb);
+ }
+ owningObject = CONTAINER_STRUCTURE(Queue, LPSOCKET_INFO, ListenQueue);
+ break;
+
+ case SOCKET_SEND_QUEUE:
+ FifoAdd((LPFIFO)Queue, (LPFIFO)pXecb);
+ owningObject = CONTAINER_STRUCTURE(Queue, LPSOCKET_INFO, SendQueue);
+ break;
+
+ case SOCKET_HEADER_QUEUE:
+ FifoAdd((LPFIFO)Queue, (LPFIFO)pXecb);
+ owningObject = CONTAINER_STRUCTURE(Queue, LPSOCKET_INFO, HeaderQueue);
+ break;
+
+ case CONNECTION_CONNECT_QUEUE:
+ FifoAdd((LPFIFO)Queue, (LPFIFO)pXecb);
+ owningObject = CONTAINER_STRUCTURE(Queue, LPCONNECTION_INFO, ConnectQueue);
+ break;
+
+ case CONNECTION_ACCEPT_QUEUE:
+ FifoAdd((LPFIFO)Queue, (LPFIFO)pXecb);
+ owningObject = CONTAINER_STRUCTURE(Queue, LPCONNECTION_INFO, AcceptQueue);
+ break;
+
+ case CONNECTION_SEND_QUEUE:
+ FifoAdd((LPFIFO)Queue, (LPFIFO)pXecb);
+ owningObject = CONTAINER_STRUCTURE(Queue, LPCONNECTION_INFO, SendQueue);
+ break;
+
+ case CONNECTION_LISTEN_QUEUE:
+ FifoAdd((LPFIFO)Queue, (LPFIFO)pXecb);
+ owningObject = CONTAINER_STRUCTURE(Queue, LPCONNECTION_INFO, ListenQueue);
+ break;
+ }
+ pXecb->OwningObject = owningObject;
+}
+
+
+LPXECB
+DequeueEcb(
+ IN LPXECB pXecb,
+ IN LPXECB_QUEUE Queue
+ )
+
+/*++
+
+Routine Description:
+
+ Removes pXecb from Queue and resets the XECB queue identifier (to NO_QUEUE)
+
+Arguments:
+
+ pXecb - pointer to XECB to remove
+ Queue - queue from which to remove pXecb
+
+Return Value:
+
+ LPXECB
+ pointer to removed XECB
+
+--*/
+
+{
+ LPXECB p;
+
+ p = (LPXECB)FifoRemove((LPFIFO)Queue, (LPFIFO)pXecb);
+ pXecb->QueueId = NO_QUEUE;
+ pXecb->OwningObject = NULL;
+ return pXecb;
+}
+
+
+VOID
+CancelSocketQueue(
+ IN LPXECB_QUEUE pXecbQueue
+ )
+
+/*++
+
+Routine Description:
+
+ Cancels all pending ECBs on a SOCKET_INFO queue
+
+Arguments:
+
+ pXecbQueue - pointer to (socket/connection) queue
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPXECB ptr;
+
+ while (ptr = pXecbQueue->Head) {
+ CancelSocketEvent(ptr);
+ }
+}
+
+
+VOID
+CancelConnectionQueue(
+ IN LPXECB_QUEUE pXecbQueue
+ )
+
+/*++
+
+Routine Description:
+
+ Cancels all pending ECBs on a CONNECTION_INFO queue
+
+Arguments:
+
+ pXecbQueue - pointer to XECB queue on CONNECTION_INFO
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPXECB ptr;
+
+ while (ptr = pXecbQueue->Head) {
+ CancelConnectionEvent(ptr);
+ }
+}
+
+
+VOID
+AbortQueue(
+ IN LPXECB_QUEUE pXecbQueue,
+ IN BYTE CompletionCode
+ )
+
+/*++
+
+Routine Description:
+
+ Aborts or terminates an ECB queue from a CONNECTION_INFO structure
+
+Arguments:
+
+ pXecbQueue - pointer to queue
+ CompletionCode - to put in aborted/terminated ECBs
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPXECB ptr;
+
+ while (ptr = pXecbQueue->Head) {
+ AbortConnectionEvent(ptr, CompletionCode);
+ }
+}
+
+
+VOID
+AbortConnectionEvent(
+ IN LPXECB pXecb,
+ IN BYTE CompletionCode
+ )
+
+/*++
+
+Routine Description:
+
+ Aborts a connection ECB
+
+Arguments:
+
+ pXecb - pointer to SPX XECB to cancel
+ CompletionCode - value to put in ECB
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPXECB ptr;
+ LPCONNECTION_INFO pConnectionInfo;
+ LPXECB_QUEUE pQueue;
+
+ pConnectionInfo = (LPCONNECTION_INFO)pXecb->OwningObject;
+ switch (pXecb->QueueId) {
+ case CONNECTION_CONNECT_QUEUE:
+ pQueue = &pConnectionInfo->ConnectQueue;
+ break;
+
+ case CONNECTION_ACCEPT_QUEUE:
+ pQueue = &pConnectionInfo->AcceptQueue;
+ break;
+
+ case CONNECTION_SEND_QUEUE:
+ pQueue = &pConnectionInfo->SendQueue;
+ break;
+
+ case CONNECTION_LISTEN_QUEUE:
+ pQueue = &pConnectionInfo->ListenQueue;
+ break;
+ }
+ ptr = DequeueEcb(pXecb, pQueue);
+ if (ptr) {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "AbortConnectionEvent: Aborting ECB %04x:%04x\n",
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress)
+ ));
+
+ SPX_ECB_CONNECTION_ID(ptr->Ecb) = pConnectionInfo->ConnectionId;
+ CompleteOrQueueIo(ptr, CompletionCode);
+ }
+}
+
+
+VOID
+StartIpxSend(
+ IN LPXECB pXecb,
+ IN LPSOCKET_INFO pSocketInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Starts a send operation for IPXSendPacket(). Allocates a send buffer if
+ the ECB has >1 fragment else uses a pointer to the single fragment buffer
+ in 16-bit address space
+
+ Fills in various fields in the ECB and IPX header
+
+ Assumes: 1. By the time this function is called, we already know we have
+ a valid non-zero fragment count and the first fragment is
+ big enough to hold an IPX packet header
+
+ When this function terminates, the ECB is either completed or queued
+
+Arguments:
+
+ pXecb - pointer to XECB describing ECB to use for sending
+ pSocketInfo - pointer to SOCKET_INFO structure
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ BOOL success;
+ int packetLength = 0;
+ LPFRAGMENT pFragment;
+ int fragmentCount;
+ LPIPX_ECB pEcb = (LPIPX_ECB)pXecb->Ecb;
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "StartIpxSend: %d frag(s), 1: address=%x (%04x:%04x), len=%04x\n",
+ READ_WORD(&pEcb->FragmentCount),
+ GET_FAR_POINTER(&(ECB_FRAGMENT(pEcb, 0)->Address), IS_PROT_MODE(pXecb)),
+ GET_SELECTOR(&(ECB_FRAGMENT(pEcb, 0)->Address)),
+ GET_OFFSET(&(ECB_FRAGMENT(pEcb, 0)->Address)),
+ READ_WORD(&(ECB_FRAGMENT(pEcb, 0)->Length))
+ ));
+
+ //
+ // mark the ECB as being used by IPX (for send)
+ //
+
+ pEcb->InUse = ECB_IU_SENDING;
+
+ //
+ // the total send buffer size cannot exceed the maximum packet size
+ //
+
+ fragmentCount = (int)pEcb->FragmentCount;
+
+ ASSERT(fragmentCount);
+
+ pFragment = (LPFRAGMENT)&(ECB_FRAGMENT(pEcb, 0)->Address);
+ while (fragmentCount--) {
+ packetLength += pFragment->Length;
+ ++pFragment;
+ }
+ if (packetLength <= MyMaxPacketSize) {
+ success = GetIoBuffer(pXecb, TRUE, IPX_HEADER_LENGTH);
+ if (success) {
+
+ LPIPX_PACKET pPacket = (LPIPX_PACKET)GET_FAR_POINTER(
+ &(ECB_FRAGMENT(pEcb, 0)->Address),
+ IS_PROT_MODE(pXecb)
+ );
+
+ //
+ // fill in the following fields in the IPX header:
+ //
+ // Checksum
+ // Length
+ // TransportControl
+ // Source (network, node, socket)
+ //
+ // BUGBUG: Does real IPX modify these fields in app memory?
+ // If so, does the app expect modified fields?
+ // If not, we need to always copy then modify memory,
+ // even if only 1 fragment
+ //
+
+ pPacket->Checksum = 0xFFFF;
+ pPacket->Length = L2BW((WORD)packetLength);
+ pPacket->TransportControl = 0;
+ CopyMemory((LPBYTE)&pPacket->Source,
+ &MyInternetAddress.sa_netnum,
+ sizeof(MyInternetAddress.sa_netnum)
+ + sizeof(MyInternetAddress.sa_nodenum)
+ );
+ pPacket->Source.Socket = pSocketInfo->SocketNumber;
+
+ //
+ // if we allocated a buffer then there is >1 fragment. Collect them
+ //
+
+ if (pXecb->Flags & XECB_FLAG_BUFFER_ALLOCATED) {
+ GatherData(pXecb, IPX_HEADER_LENGTH);
+ }
+
+ //
+ // initiate the send. IPX_ECB_BUFFER32(pEcb) points to the data to send,
+ // IPX_ECB_LENGTH32(pEcb) is the size of data to send
+ //
+
+ IpxSendFirst(pXecb, pSocketInfo);
+ } else {
+
+ //
+ // couldn't allocate a buffer? Comes under the heading of
+ // hardware error?
+ //
+
+ CompleteEcb(pXecb, ECB_CC_HARDWARE_ERROR);
+ if (pSocketInfo->Flags & SOCKET_FLAG_TEMPORARY) {
+ KillSocket(pSocketInfo);
+ }
+ }
+ } else {
+
+ //
+ // packet larger than MyMaxPacketSize
+ //
+
+ CompleteOrQueueEcb(pXecb, ECB_CC_BAD_REQUEST);
+ if (pSocketInfo->Flags & SOCKET_FLAG_TEMPORARY) {
+ KillSocket(pSocketInfo);
+ }
+ }
+}
+
+
+BOOL
+GetIoBuffer(
+ IN OUT LPXECB pXecb,
+ IN BOOL Send,
+ IN WORD HeaderLength
+ )
+
+/*++
+
+Routine Description:
+
+ Allocate a buffer based on the ECB fragment list. If there is only 1 fragment
+ we use the address of the buffer in the VDM. If >1 fragment, we allocate a
+ 32-bit buffer large enough to hold all the 16-bit fragments
+
+ We trim the buffer requirement for a send buffer: we do not send the IPX/SPX
+ header with the data: it will be provided by the transport
+
+ Assumes: 1. If called for a send buffer, the first fragment has already
+ been verified as >= HeaderLength
+
+Arguments:
+
+ pXecb - pointer to XECB which points to IPX_ECB containing fragment
+ list to allocate buffer for
+ Send - TRUE if this request is to get a send buffer
+ HeaderLength - length of the (untransmitted) header portion
+
+Return Value:
+
+ BOOL
+ TRUE - Buffer allocated, XECB updated with address, length and flags
+ FALSE - either ECB contains bad fragment descriptor list or we
+ couldn't allocate a buffer
+
+--*/
+
+{
+ WORD fragmentCount;
+ WORD bufferLength = 0;
+ LPBYTE bufferPointer = NULL;
+ WORD flags = 0;
+ int i;
+ int fragIndex = 0; // index of fragment address to use if no allocation required
+ LPIPX_ECB pEcb = (LPIPX_ECB)pXecb->Ecb;
+
+ fragmentCount = READ_WORD(&pEcb->FragmentCount);
+
+ for (i = 0; i < (int)fragmentCount; ++i) {
+ bufferLength += ECB_FRAGMENT(pEcb, i)->Length;
+ }
+ if (bufferLength) {
+
+ //
+ // exclude the IPX header from send buffer. If the first send fragment
+ // contains only the IPX header, reduce the fragment count by 1
+ //
+
+ if (Send) {
+ bufferLength -= HeaderLength;
+ if (ECB_FRAGMENT(pEcb, 0)->Length == HeaderLength) {
+ --fragmentCount;
+ fragIndex = 1;
+ }
+ }
+ if (bufferLength) {
+ if (fragmentCount > 1) {
+ bufferPointer = AllocateBuffer(bufferLength);
+ if (bufferPointer) {
+ flags = XECB_FLAG_BUFFER_ALLOCATED;
+ } else {
+
+ //
+ // need a buffer; failed to allocate it
+ //
+
+ return FALSE;
+ }
+ } else {
+
+ //
+ // fragmentCount must be 1 (else bufferLength would be 0)
+ //
+
+ bufferPointer = GET_FAR_POINTER(
+ &ECB_FRAGMENT(pEcb, fragIndex)->Address,
+ IS_PROT_MODE(pXecb)
+ );
+ if (Send && !fragIndex) {
+
+ //
+ // if we are allocating a send buffer AND there is only 1
+ // fragment AND it is the first fragment then the one and
+ // only fragment must contain the IPX header and the data.
+ // Advance the data pointer past the IPX header
+ //
+
+ bufferPointer += HeaderLength;
+ }
+ }
+ } else {
+
+ //
+ // sending 0 bytes!!!
+ //
+
+ }
+ } else {
+
+ //
+ // fragments but no buffer length? Sounds like a malformed packet
+ //
+
+ return FALSE;
+ }
+
+ //
+ // bufferPointer is either the address of a buffer in 32-bit memory which
+ // must be gather/scattered when the I/O operation completes, or it is the
+ // address of a single fragment buffer in 16-bit memory. In the former case
+ // flags is ECB_ALLOCATE_32 and the latter 0
+ //
+
+ pXecb->Buffer = pXecb->Data = bufferPointer;
+ pXecb->Length = bufferLength;
+ pXecb->Flags |= flags;
+ return TRUE;
+}
+
+
+PRIVATE
+VOID
+ReleaseIoBuffer(
+ IN LPXECB pXecb
+ )
+
+/*++
+
+Routine Description:
+
+ Deallocates I/O buffer attached to XECB and zaps associated XECB fields
+
+Arguments:
+
+ pXecb - pointer to XECB owning buffer to be released
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ if (pXecb->Flags & XECB_FLAG_BUFFER_ALLOCATED) {
+ DeallocateBuffer(pXecb->Buffer);
+ pXecb->Buffer = pXecb->Data = NULL;
+ pXecb->Flags &= ~XECB_FLAG_BUFFER_ALLOCATED;
+ }
+}
+
+
+VOID
+GatherData(
+ IN LPXECB pXecb,
+ IN WORD HeaderLength
+ )
+
+/*++
+
+Routine Description:
+
+ Copies data from fragmented 16-bit memory into single 32-bit memory buffer.
+ Used to send data. We exclude the IPX header: this information is supplied
+ by the transport
+
+ Assumes: 1. The fragment descriptor list has been verified: we know that
+ the first fragment contains at least the IPX header
+
+Arguments:
+
+ pXecb - pointer to XECB structure. The following IPX_ECB and XECB
+ fields must contain coherent values:
+
+ IPX_ECB.FragmentCount
+ XECB.Buffer
+
+ HeaderLength - length of the (untransmitted) header portion
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ int fragmentCount;
+ WORD length;
+ ULPBYTE pData16;
+ ULPBYTE pData32;
+ LPFRAGMENT pFragment;
+ LPIPX_ECB pEcb = (LPIPX_ECB)pXecb->Ecb;
+
+ fragmentCount = (int)pEcb->FragmentCount;
+ pFragment = (LPFRAGMENT)&(ECB_FRAGMENT(pEcb, 0)->Address);
+ pData32 = pXecb->Buffer;
+
+ //
+ // if the 1st fragment contains more than the IPX/SPX header, copy the data
+ // after the header
+ //
+
+ if (pFragment->Length > HeaderLength) {
+
+ LPBYTE fragAddr = GET_FAR_POINTER(&pFragment->Address,
+ IS_PROT_MODE(pXecb)
+ );
+
+ length = pFragment->Length - HeaderLength;
+ CopyMemory((LPVOID)pData32,
+ fragAddr + HeaderLength,
+ length
+ );
+ pData32 += length;
+ }
+
+ //
+ // copy subsequent fragments
+ //
+
+ ++pFragment;
+ while (--fragmentCount) {
+ pData16 = GET_FAR_POINTER(&pFragment->Address, IS_PROT_MODE(pXecb));
+ length = pFragment->Length;
+ CopyMemory((PVOID)pData32, (CONST VOID*)pData16, (ULONG)length);
+ pData32 += length;
+ ++pFragment;
+ }
+}
+
+
+VOID
+ScatterData(
+ IN LPXECB pXecb
+ )
+
+/*++
+
+Routine Description:
+
+ Copies data from 32-bit memory to 16-bit. The data must be fragmented if
+ this function has been called (i.e. we determined there were >1 fragments
+ and allocated a single 32-bit buffer to cover them)
+
+Arguments:
+
+ pXecb - pointer to XECB containing 32-bit buffer info
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ int fragmentCount;
+ int length;
+ WORD length16;
+ WORD length32;
+ ULPBYTE pData16;
+ ULPBYTE pData32;
+ LPFRAGMENT pFragment;
+ LPIPX_ECB pEcb = (LPIPX_ECB)pXecb->Ecb;
+
+ fragmentCount = (int)pEcb->FragmentCount;
+ pFragment = (LPFRAGMENT)&(ECB_FRAGMENT(pEcb, 0)->Address);
+ pData32 = pXecb->Buffer;
+ length32 = pXecb->Length;
+ while (length32) {
+ pData16 = GET_FAR_POINTER(&pFragment->Address, IS_PROT_MODE(pXecb));
+ length16 = pFragment->Length;
+ length = min(length16, length32);
+ CopyMemory((PVOID)pData16, (CONST VOID*)pData32, (ULONG)length);
+ pData32 += length;
+ length32 -= length;
+ ++pFragment;
+ --fragmentCount;
+
+ ASSERT(fragmentCount >= 0);
+
+ }
+}
+
+
+VOID
+IpxReceiveFirst(
+ IN LPXECB pXecb,
+ IN LPSOCKET_INFO pSocketInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Performs a receive against a non-blocking socket. This is the first
+ receive call for this ECB. If the receive completes immediately with data
+ or an error that isn't WSAEWOULDBLOCK then the ECB is completed. If the
+ receives completes with a WSAEWOULDBLOCK error then the request is queued
+ for deferred processing by the AES thread
+
+ Unlike send, receives are not serialized. If there are already receives
+ pending against the socket there could be a clash between this function
+ and IpxReceiveNext(), called from the AES thread. In this case, we expect
+ Winsock to do the right thing and serialize the callers
+
+Arguments:
+
+ pXecb - pointer to XECB describing receive ECB
+ pSocketInfo - pointer to socket structure
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ SOCKADDR_IPX from;
+ int fromLen = sizeof(from);
+ int rc;
+ BYTE status;
+ BOOL error;
+
+ rc = recvfrom(pSocketInfo->Socket,
+ (char FAR*)pXecb->Buffer,
+ (int)pXecb->Length,
+ 0, // flags
+ (LPSOCKADDR)&from,
+ &fromLen
+ );
+ if (rc != SOCKET_ERROR) {
+ error = FALSE;
+ status = ECB_CC_SUCCESS;
+ } else {
+ error = TRUE;
+ rc = WSAGetLastError();
+ if (rc == WSAEWOULDBLOCK) {
+ RequestMutex();
+ QueueReceiveRequest(pXecb, pSocketInfo);
+ ReleaseMutex();
+ } else if (rc == WSAEMSGSIZE) {
+ error = FALSE;
+ status = ECB_CC_BAD_REQUEST;
+ rc = pXecb->Length;
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "IpxReceiveFirst: recvfrom() returns %d (buflen=%d)\n",
+ rc,
+ pXecb->Length
+ ));
+
+ //
+ // BUGBUG: map error
+ //
+
+ CompleteOrQueueIo(pXecb, ECB_CC_BAD_REQUEST);
+ }
+ }
+ if (!error) {
+
+ //
+ // rc = bytes received, or 0 = connection terminated (even for DGRAM?)
+ //
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "IpxReceiveFirst: bytes received = %d (%x)\n",
+ rc,
+ rc
+ ));
+/*
+ VwDumpEcb(pXecb->Ecb,
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress),
+ FALSE,
+ TRUE,
+ TRUE,
+ IS_PROT_MODE(pXecb)
+ );
+*/
+
+ IPXDUMPDATA((pXecb->Buffer, 0, 0, FALSE, (WORD)rc));
+
+ //
+ // if the receive buffers are fragmented, copy the data to 16-bit memory
+ // (else single buffer: its already there (dude))
+ //
+
+ if (pXecb->Flags & XECB_FLAG_BUFFER_ALLOCATED) {
+
+ //
+ // update the ECB_LENGTH32 field to reflect the amount of data received
+ //
+
+ pXecb->Length = (WORD)rc;
+ ScatterData(pXecb);
+
+ //
+ // we have finished with the 32-bit buffer: deallocate it
+ //
+
+ ReleaseIoBuffer(pXecb);
+ }
+
+ //
+ // update the ImmediateAddress field in the ECB with the node address
+ // of the sender
+ //
+
+ CopyMemory(pXecb->Ecb->ImmediateAddress, from.sa_nodenum, sizeof(from.sa_nodenum));
+
+ //
+ // if this ECB has a non-NULL ESR then queue for asynchronous completion
+ // else complete immediately (app must poll InUse field)
+ //
+
+ CompleteOrQueueEcb(pXecb, status);
+ }
+}
+
+
+VOID
+IpxReceiveNext(
+ IN LPSOCKET_INFO pSocketInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Attempts to complete an IPXListenForPacket request that has been deferred due
+ to the fact the socket was blocked.
+
+ The ECB containing all the receive information is at the head of the
+ ListenQueue on pSocketInfo
+
+ We can use any queued listen ECB, but it just so happens we use the one at
+ the head of the FIFO
+
+ Note: SerializationCritSec is held when this function is called.
+
+Arguments:
+
+ pSocketInfo - pointer to SOCKET_INFO structure with pending IPX send request
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPXECB pXecb;
+ SOCKADDR_IPX from;
+ int fromLen = sizeof(from);
+ int rc;
+ BYTE status;
+ BOOL error;
+
+ ASSERT(pSocketInfo);
+
+ pXecb = (LPXECB)pSocketInfo->ListenQueue.Head;
+
+ ASSERT(pXecb);
+
+ rc = recvfrom(pSocketInfo->Socket,
+ (char FAR*)pXecb->Buffer,
+ (int)pXecb->Length,
+ 0, // flags
+ (LPSOCKADDR)&from,
+ &fromLen
+ );
+ if (rc != SOCKET_ERROR) {
+ error = FALSE;
+ status = ECB_CC_SUCCESS;
+ } else {
+ error = TRUE;
+ rc = WSAGetLastError();
+ if (rc == WSAEMSGSIZE) {
+ error = FALSE;
+ status = ECB_CC_BAD_REQUEST;
+ rc = pXecb->Length;
+ } else if (rc != WSAEWOULDBLOCK) {
+ DequeueReceiveRequest(pXecb, pSocketInfo);
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "IpxReceiveNext: recvfrom() returns %d\n",
+ rc
+ ));
+
+ //
+ // BUGBUG: completion code?
+ //
+
+ CompleteOrQueueIo(pXecb, ECB_CC_CANCELLED);
+ }
+ }
+ if (!error) {
+/*
+ VwDumpEcb(pXecb->Ecb,
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress),
+ FALSE,
+ TRUE,
+ TRUE,
+ IS_PROT_MODE(pXecb)
+ );
+*/
+ //
+ // data received. Remove ECB from queue
+ //
+
+ DequeueReceiveRequest(pXecb, pSocketInfo);
+
+ //
+ // rc = bytes received, or 0 = connection terminated (even for DGRAM?)
+ //
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "IpxReceiveNext: ECB %04x:%04x bytes received = %d (%x)\n",
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress),
+ rc,
+ rc
+ ));
+
+ IPXDUMPDATA((pXecb->Buffer, 0, 0, FALSE, (WORD)rc));
+
+ //
+ // if the receive buffers are fragmented, copy the data to 16-bit memory
+ // (else single buffer: its already there (dude))
+ //
+
+ if (pXecb->Flags & XECB_FLAG_BUFFER_ALLOCATED) {
+
+ //
+ // update the IPX_ECB_LENGTH32 field to reflect the amount of data received
+ //
+
+ pXecb->Length = (WORD)rc;
+ ScatterData(pXecb);
+ ReleaseIoBuffer(pXecb);
+ }
+
+ //
+ // update the ImmediateAddress field in the ECB with the node address
+ // of the sender
+ //
+
+ CopyMemory(pXecb->Ecb->ImmediateAddress,
+ from.sa_nodenum,
+ sizeof(from.sa_nodenum)
+ );
+
+ //
+ // if this ECB has a non-NULL ESR then queue for asynchronous completion
+ // else complete immediately (app must poll InUse field)
+ //
+
+ CompleteOrQueueEcb(pXecb, ECB_CC_SUCCESS);
+ }
+}
+
+
+PRIVATE
+VOID
+IpxSendFirst(
+ IN LPXECB pXecb,
+ IN LPSOCKET_INFO pSocketInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Tries to send an IPX packet. This is the first attempt to send the packet
+ described in the ECB. If the send succeeds or fails with an error other
+ than WSAEWOULDBLOCK we complete the ECB. If the send attempt fails because
+ the transport can't accept the request at this time, we queue it for later
+ when the AES thread will attempt to send it.
+
+ If there is already a send being attempted then we just queue this request
+ and let AES handle it in IpxSendNext()
+
+Arguments:
+
+ pXecb - pointer to XECB
+ pSocketInfo - pointer to SOCKET_INFO structure
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ RequestMutex();
+ if (pSocketInfo->Flags & SOCKET_FLAG_SENDING) {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "IpxSendFirst: queueing ECB %04x:%04x\n",
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress)
+ ));
+
+ QueueSendRequest(pXecb, pSocketInfo);
+ } else {
+
+ SOCKADDR_IPX to;
+ LPIPX_PACKET pPacket;
+ int length;
+ int rc;
+ LPIPX_ECB pEcb = (LPIPX_ECB)pXecb->Ecb;
+ int type;
+/*
+ VwDumpEcb(pXecb->Ecb,
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress),
+ FALSE,
+ TRUE,
+ TRUE,
+ IS_PROT_MODE(pXecb)
+ );
+*/
+ length = (int)pXecb->Length;
+
+ //
+ // the first fragment holds the destination address info
+ //
+
+ pPacket = (LPIPX_PACKET)GET_FAR_POINTER(&ECB_FRAGMENT(pEcb, 0)->Address,
+ IS_PROT_MODE(pXecb)
+ );
+ to.sa_family = AF_IPX;
+
+ //
+ // copy the destination net number as a DWORD (4 bytes) from the
+ // destination network address structure in the IPX packet header
+ //
+
+ *(ULPDWORD)&to.sa_netnum[0] = *(ULPDWORD)&pPacket->Destination.Net[0];
+ //
+ // copy the immediate (destination) node number as a DWORD (4 bytes) and
+ // a WORD (2 bytes) from the Destination network address structure in
+ // the IPX packet header. pPacket is an unaligned pointer, so we are
+ // safe
+ //
+
+ *(ULPDWORD)&to.sa_nodenum[0] = *(ULPDWORD)&pPacket->Destination.Node[0];
+
+ *(LPWORD)&to.sa_nodenum[4] = *(ULPWORD)&pPacket->Destination.Node[4];
+
+ //
+ // copy the destination socket number from the IPX packet header as a
+ // WORD (2 bytes). Again, the aligned pointer will save us
+ //
+
+ to.sa_socket = pPacket->Destination.Socket;
+
+ type = (int)pPacket->PacketType;
+ rc = setsockopt(pSocketInfo->Socket,
+ NSPROTO_IPX,
+ IPX_PTYPE,
+ (char FAR*)&type,
+ sizeof(type)
+ );
+ if (rc) {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "IpxSendFirst: setsockopt(IPX_PTYPE) returns %d\n",
+ WSAGetLastError()
+ ));
+
+ }
+ rc = sendto(pSocketInfo->Socket,
+ (char FAR*)pXecb->Buffer,
+ length,
+ 0, // flags
+ (LPSOCKADDR)&to,
+ sizeof(to)
+ );
+ if (rc == length) {
+
+ //
+ // all data sent
+ //
+
+ IPXDUMPDATA((pXecb->Buffer, 0, 0, FALSE, (WORD)rc));
+
+ CompleteOrQueueIo(pXecb, ECB_CC_SUCCESS);
+ if (pSocketInfo->Flags & SOCKET_FLAG_TEMPORARY) {
+ KillSocket(pSocketInfo);
+ }
+ } else if (rc == SOCKET_ERROR) {
+ rc = WSAGetLastError();
+ if (rc == WSAEWOULDBLOCK) {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "IpxSendFirst: queueing ECB %04x:%04x (after sendto)\n",
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress)
+ ));
+
+ QueueSendRequest(pXecb, pSocketInfo);
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "IpxSendFirst: sendto() returns %d\n",
+ rc
+ ));
+
+ //
+ // BUGBUG: couple of possible completion codes here
+ //
+
+ CompleteIo(pXecb, ECB_CC_UNDELIVERABLE);
+ if (pSocketInfo->Flags & SOCKET_FLAG_TEMPORARY) {
+ KillSocket(pSocketInfo);
+ }
+ }
+ } else {
+
+ //
+ // BUGBUG: Is 0 a valid value for rc?
+ //
+
+ //
+ // send should send all the data or return an error
+ //
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_FATAL,
+ "IpxSendFirst: sendto() returns unexpected %d (length = %d)\n",
+ rc,
+ length
+ ));
+ }
+ }
+ ReleaseMutex();
+}
+
+
+VOID
+IpxSendNext(
+ IN LPSOCKET_INFO pSocketInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Attempts to complete an IPXSendPacket request that has been deferred due
+ to the fact the socket was blocked.
+
+ The ECB containing all the send information is at the head of the SendQueue
+ on pSocketInfo
+
+ The SendQueue is serialized in FIFO order
+
+ Note: SerializationCritSec is held when this function is called.
+
+Arguments:
+
+ pSocketInfo - pointer to SOCKET_INFO structure with pending IPX send request
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ SOCKADDR_IPX to;
+ LPIPX_PACKET pPacket;
+ int length;
+ int rc;
+ LPXECB pXecb;
+ LPIPX_ECB pEcb;
+ int type;
+
+ pXecb = (LPXECB)pSocketInfo->SendQueue.Head;
+ pEcb = (LPIPX_ECB)pXecb->Ecb;
+
+ ASSERT(pXecb);
+ ASSERT(pEcb);
+/*
+ VwDumpEcb(pXecb->Ecb,
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress),
+ FALSE,
+ TRUE,
+ TRUE,
+ IS_PROT_MODE(pXecb)
+ );
+*/
+ length = (int)pXecb->Length;
+
+ //
+ // even though we have a 32-bit pointer to the IPX packet buffer which
+ // may be in 16- or 32-bit memory, we still need unaligned access
+ //
+
+ pPacket = (LPIPX_PACKET)pXecb->Buffer;
+ to.sa_family = AF_IPX;
+
+ //
+ // copy the destination net number as a DWORD (4 bytes) from the
+ // destination network address structure in the IPX packet header
+ //
+
+ *(ULPDWORD)&to.sa_netnum[0] = *(ULPDWORD)&pPacket->Destination.Net[0];
+ //
+ // copy the immediate (destination) node number as a DWORD (4 bytes) and
+ // a WORD (2 bytes) from the Destination network address structure in
+ // the IPX packet header. pPacket is an unaligned pointer, so we are
+ // safe
+ //
+
+ *(ULPDWORD)&to.sa_nodenum[0] = *(ULPDWORD)&pPacket->Destination.Node[0];
+ *(LPWORD)&to.sa_nodenum[4] = *(ULPWORD)&pPacket->Destination.Node[4];
+
+ //
+ // copy the destination socket number from the IPX packet header as a
+ // WORD (2 bytes). Again, the aligned pointer will save us
+ //
+
+ to.sa_socket = pPacket->Destination.Socket;
+
+ type = (int)pPacket->PacketType;
+ rc = setsockopt(pSocketInfo->Socket,
+ NSPROTO_IPX,
+ IPX_PTYPE,
+ (char FAR*)&type,
+ sizeof(type)
+ );
+ if (rc) {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "IpxSendNext: setsockopt(IPX_PTYPE) returns %d\n",
+ WSAGetLastError()
+ ));
+
+ }
+ rc = sendto(pSocketInfo->Socket,
+ (char FAR*)pPacket,
+ length,
+ 0, // flags
+ (LPSOCKADDR)&to,
+ sizeof(to)
+ );
+ if (rc == length) {
+
+ //
+ // all data sent - dequeue it
+ //
+
+
+ IPXDUMPDATA((pXecb->Buffer, 0, 0, FALSE, (WORD)rc));
+
+ DequeueEcb(pXecb, &pSocketInfo->SendQueue);
+ if (pXecb->EsrAddress) {
+ if (pXecb->Flags & XECB_FLAG_BUFFER_ALLOCATED) {
+ ReleaseIoBuffer(pXecb);
+ }
+ QueueAsyncCompletion(pXecb, ECB_CC_SUCCESS);
+ } else {
+ CompleteIo(pXecb, ECB_CC_SUCCESS);
+ }
+ if (pSocketInfo->Flags & SOCKET_FLAG_TEMPORARY) {
+ KillSocket(pSocketInfo);
+ }
+ } else if (rc == SOCKET_ERROR) {
+
+ //
+ // if the socket is still blocked, there's nothing to do - just leave
+ // the request hanging around till next time
+ //
+
+ rc = WSAGetLastError();
+ if (rc != WSAEWOULDBLOCK) {
+ DequeueSendRequest(pXecb, pSocketInfo);
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "IpxSendNext: sendto() returns %d\n",
+ rc
+ ));
+
+ //
+ // BUGBUG: couple of possible completion codes here
+ //
+
+ CompleteIo(pXecb, ECB_CC_UNDELIVERABLE);
+ if (pSocketInfo->Flags & SOCKET_FLAG_TEMPORARY) {
+ KillSocket(pSocketInfo);
+ }
+ }
+ } else {
+
+ //
+ // BUGBUG: Is 0 a valid value for rc?
+ //
+
+ //
+ // send should send all the data or return an error
+ //
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_FATAL,
+ "IpxSendNext: sendto() returns unexpected %d (length = %d)\n",
+ rc,
+ length
+ ));
+ }
+}
+
+
+PRIVATE
+VOID
+QueueReceiveRequest(
+ IN LPXECB pXecb,
+ IN LPSOCKET_INFO pSocketInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Add a listen XECB to queue of listen XECBs on a SOCKET_INFO structure
+
+Arguments:
+
+ pXecb - pointer to listen XECB to queue
+ pSocketInfo - pointer to SOCKET_INFO structure
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ QueueEcb(pXecb, &pSocketInfo->ListenQueue, SOCKET_LISTEN_QUEUE);
+ ++pSocketInfo->PendingListens;
+ pSocketInfo->Flags |= SOCKET_FLAG_LISTENING;
+}
+
+
+PRIVATE
+LPXECB
+DequeueReceiveRequest(
+ IN LPXECB pXecb,
+ IN LPSOCKET_INFO pSocketInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Remove a listen XECB from queue of listen XECBs on a SOCKET_INFO structure
+
+Arguments:
+
+ pXecb - pointer to listen XECB to dequeue
+ pSocketInfo - pointer to SOCKET_INFO structure
+
+Return Value:
+
+ LPXECB
+
+--*/
+
+{
+ LPXECB ptr;
+
+ ptr = (LPXECB)DequeueEcb(pXecb, &pSocketInfo->ListenQueue);
+ if (ptr) {
+
+ ASSERT(ptr == pXecb);
+
+ --pSocketInfo->PendingListens;
+ if (!pSocketInfo->PendingListens) {
+ pSocketInfo->Flags &= ~SOCKET_FLAG_LISTENING;
+ }
+
+ //
+ // BUGBUG: Is this correct value?
+ //
+
+ pXecb->Ecb->InUse = ECB_IU_AWAITING_PROCESSING;
+ }
+ return ptr;
+}
+
+
+PRIVATE
+VOID
+QueueSendRequest(
+ IN LPXECB pXecb,
+ IN LPSOCKET_INFO pSocketInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Add a send XECB to queue of send XECBs on a SOCKET_INFO structure
+
+Arguments:
+
+ pXecb - pointer to send XECB to queue
+ pSocketInfo - pointer to SOCKET_INFO structure
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ QueueEcb(pXecb, &pSocketInfo->SendQueue, SOCKET_SEND_QUEUE);
+ ++pSocketInfo->PendingSends;
+ pSocketInfo->Flags |= SOCKET_FLAG_SENDING;
+ pXecb->Ecb->InUse = ECB_IU_SEND_QUEUED;
+}
+
+
+PRIVATE
+LPXECB
+DequeueSendRequest(
+ IN LPXECB pXecb,
+ IN LPSOCKET_INFO pSocketInfo
+ )
+
+/*++
+
+Routine Description:
+
+ Remove a send XECB from queue of send XECBs on a SOCKET_INFO structure
+
+Arguments:
+
+ pXecb - pointer to send XECB to dequeue
+ pSocketInfo - pointer to SOCKET_INFO structure
+
+Return Value:
+
+ LPXECB
+
+--*/
+
+{
+ LPXECB ptr;
+
+ ptr = (LPXECB)DequeueEcb(pXecb, &pSocketInfo->SendQueue);
+ if (ptr) {
+
+ ASSERT(ptr == pXecb);
+
+ --pSocketInfo->PendingSends;
+ if (!pSocketInfo->PendingSends) {
+ pSocketInfo->Flags &= ~SOCKET_FLAG_SENDING;
+ }
+ pXecb->Ecb->InUse = ECB_IU_AWAITING_PROCESSING;
+ }
+ return ptr;
+}
+
+
+VOID
+CompleteOrQueueIo(
+ IN LPXECB pXecb,
+ IN BYTE CompletionCode
+ )
+
+/*++
+
+Routine Description:
+
+ Returns any allocated buffer resource then completes or queues the ECB
+
+Arguments:
+
+ pXecb - pointer to XECB structure
+ CompletionCode - value to put in CompletionCode field
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ //
+ // if we allocated a buffer, free it
+ //
+
+ if (pXecb->Flags & XECB_FLAG_BUFFER_ALLOCATED) {
+ ReleaseIoBuffer(pXecb);
+ }
+ CompleteOrQueueEcb(pXecb, CompletionCode);
+}
+
+
+VOID
+CompleteIo(
+ IN LPXECB pXecb,
+ IN BYTE CompletionCode
+ )
+
+/*++
+
+Routine Description:
+
+ Completes a send/receive request by returning any allocated buffer resource
+ and setting the ECB InUse and CompletionCode fields
+
+Arguments:
+
+ pXecb - pointer to XECB structure
+ CompletionCode - value to put in CompletionCode field
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ //
+ // if we allocated a buffer, free it
+ //
+
+ if (pXecb->Flags & XECB_FLAG_BUFFER_ALLOCATED) {
+ ReleaseIoBuffer(pXecb);
+ }
+ CompleteEcb(pXecb, CompletionCode);
+}
+
+
+VOID
+CompleteOrQueueEcb(
+ IN LPXECB pXecb,
+ IN BYTE CompletionCode
+ )
+
+/*++
+
+Routine Description:
+
+ Queues an XECB for completion by ESR or completes it now
+
+Arguments:
+
+ pXecb - pointer to XECB describing ECB to complete
+ CompletionCode - value to put in ECB CompletionCode field
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ if (pXecb->EsrAddress) {
+ QueueAsyncCompletion(pXecb, CompletionCode);
+ } else {
+ CompleteIo(pXecb, CompletionCode);
+ }
+}
+
+
+VOID
+CompleteEcb(
+ IN LPXECB pXecb,
+ IN BYTE CompletionCode
+ )
+
+/*++
+
+Routine Description:
+
+ Sets the CompletionCode field in the ECB and sets the InUse field to 0.
+ Deallocates the XECB structure
+
+Arguments:
+
+ pXecb - pointer to XECB describing ECB in 16-bit memory to update
+ CompletionCode - value to put in CompletionCode field
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPIPX_ECB pEcb = (LPIPX_ECB)pXecb->Ecb;
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "CompleteEcb: completing ECB @%04x:%04x w/ %02x\n",
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress),
+ CompletionCode
+ ));
+
+ //
+ // if this is really an AES ECB then CompletionCode is actually the first
+ // byte of the AES workspace. It shouldn't matter that we write into this
+ // field - we are supposed to own it
+ //
+
+ pEcb->CompletionCode = CompletionCode;
+ pEcb->InUse = ECB_IU_NOT_IN_USE;
+
+ //
+ // reset the LinkAddress field. This means we have completed the ECB
+ //
+
+ pEcb->LinkAddress = NULL;
+
+ //
+ // finally, deallocate the XECB. This mustn't have any allocated resources
+ // (like a buffer)
+ //
+
+ DeallocateXecb(pXecb);
+}
+
+
+PRIVATE
+VOID
+QueueAsyncCompletion(
+ IN LPXECB pXecb,
+ IN BYTE CompletionCode
+ )
+
+/*++
+
+Routine Description:
+
+ Add an XECB to the (serialized) async completion queue and raise a simulated
+ hardware interrupt in the VDM.
+
+ The interrupt will cause the VDM to start executing at the ISR in the TSR
+ which will call-back to find the address for the ESR, then execute it
+
+Arguments:
+
+ pXecb - pointer to XECB describing IPX or AES ECB to add to async
+ completion list
+ CompletionCode - the ECB in VDM memory will be updated with this completion
+ code
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "QueueAsyncCompletion: completing ECB @%04x:%04x w/ %02x\n",
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress),
+ CompletionCode
+ ));
+
+ pXecb->Ecb->CompletionCode = CompletionCode;
+ pXecb->QueueId = ASYNC_COMPLETION_QUEUE;
+ EnterCriticalSection(&AsyncCritSec);
+ FifoAdd(&AsyncCompletionQueue, (LPFIFO)pXecb);
+ LeaveCriticalSection(&AsyncCritSec);
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "QueueAsyncCompletion: ECB @ %04x:%04x ESR @ %04x:%04x\n",
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress),
+ HIWORD(pXecb->EsrAddress),
+ LOWORD(pXecb->EsrAddress)
+ ));
+
+ VDDSimulateInterrupt(Ica, IcaLine, 1);
+}
+
+
+VOID
+EsrCallback(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Callback function from within 16-bit TSR ESR function. Returns the address
+ of the next completed ECB in ES:SI
+
+ Any allocated resources (e.g. 32-bit buffer) must have been freed by the
+ time the ESR callback happens
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ WORD segment = 0;
+ WORD offset = 0;
+ BYTE flags;
+
+ VWinEsrCallback( &segment, &offset, &flags );
+
+ setES(segment);
+ setSI(offset);
+ setAL(flags);
+}
+
+
+VOID
+VWinEsrCallback(
+ WORD *pSegment,
+ WORD *pOffset,
+ BYTE *pFlags
+ )
+
+/*++
+
+Routine Description:
+
+ Callback function from within 16-bit function. Returns the address
+ of the next completed ECB
+
+ Any allocated resources (e.g. 32-bit buffer) must have been freed by the
+ time the ESR callback happens
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPXECB pXecb;
+
+ EnterCriticalSection(&AsyncCritSec);
+ pXecb = AsyncCompletionQueue.Head;
+ if (pXecb) {
+
+ WORD msw = getMSW();
+
+ if ((msw & MSW_PE) ^ IS_PROT_MODE(pXecb)) {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "EsrCallback: ECB @ %04x:%04x NOT for this proc mode (%d)\n",
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress),
+ msw & MSW_PE
+ ));
+
+ pXecb = NULL;
+ } else {
+ pXecb = (LPXECB)FifoNext(&AsyncCompletionQueue);
+ }
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_FATAL,
+ "EsrCallback: no ECBs on AsyncCompletionQueue!\n"
+ ));
+
+ }
+ LeaveCriticalSection(&AsyncCritSec);
+
+ if (pXecb) {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "EsrCallback: ECB @ %04x:%04x ESR @ %04x:%04x\n",
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress),
+ HIWORD(pXecb->EsrAddress),
+ LOWORD(pXecb->EsrAddress)
+ ));
+
+ *pSegment = HIWORD(pXecb->EcbAddress);
+ *pOffset = LOWORD(pXecb->EcbAddress);
+ pXecb->Ecb->LinkAddress = NULL;
+ pXecb->Ecb->InUse = ECB_IU_NOT_IN_USE;
+ *pFlags = (BYTE)((pXecb->Flags & XECB_FLAG_IPX) ? ECB_TYPE_IPX : ECB_TYPE_AES);
+ DeallocateXecb(pXecb);
+ setCF(0);
+ } else {
+ setCF(1);
+ }
+}
+
+
+VOID
+FifoAddHead(
+ IN LPFIFO pFifo,
+ IN LPFIFO pElement
+ )
+
+/*++
+
+Routine Description:
+
+ Adds an element to the head of a (single-linked) FIFO list
+
+Arguments:
+
+ pFifo - pointer to FIFO structure
+ pElement - pointer to (FIFO) element to add to list
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ if (!pFifo->Head) {
+ pFifo->Head = pFifo->Tail = pElement;
+ pElement->Head = NULL;
+ } else {
+ pElement->Head = pFifo->Head;
+ pFifo->Head = pElement;
+ }
+}
+
+VOID
+FifoAdd(
+ IN LPFIFO pFifo,
+ IN LPFIFO pElement
+ )
+
+/*++
+
+Routine Description:
+
+ Adds an element to the tail of a (single-linked) FIFO list
+
+Arguments:
+
+ pFifo - pointer to FIFO structure
+ pElement - pointer to (FIFO) element to add to list
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ if (!pFifo->Head) {
+ pFifo->Head = pFifo->Tail = pElement;
+ } else {
+ ((LPFIFO)pFifo->Tail)->Head = pElement;
+ }
+ pFifo->Tail = pElement;
+ pElement->Head = NULL;
+}
+
+
+LPFIFO
+FifoRemove(
+ IN LPFIFO pFifo,
+ IN LPFIFO pElement
+ )
+
+/*++
+
+Routine Description:
+
+ Removes an element from a (single-linked) FIFO list
+
+Arguments:
+
+ pFifo - pointer to FIFO structure
+ pElement - pointer to (FIFO) element to remove (single-linked)
+
+Return Value:
+
+ PFIFO
+ NULL - pElement not on list
+ !NULL - pElement removed from list
+
+--*/
+
+{
+ LPFIFO p;
+ LPFIFO prev = (LPFIFO)pFifo;
+
+ p = (LPFIFO)pFifo->Head;
+ while (p && (p != pElement)) {
+ prev = p;
+ p = p->Head;
+ }
+ if (p) {
+ prev->Head = p->Head;
+ if (pFifo->Head == NULL) {
+ pFifo->Tail = NULL;
+ } else if (pFifo->Tail == p) {
+ pFifo->Tail = prev;
+ }
+ }
+ return p;
+}
+
+
+LPFIFO
+FifoNext(
+ IN LPFIFO pFifo
+ )
+
+/*++
+
+Routine Description:
+
+ Remove element at head of FIFO queue
+
+Arguments:
+
+ pFifo - pointer to FIFO
+
+Return Value:
+
+ LPFIFO
+ NULL - nothing on queue
+ !NULL - removed element
+
+--*/
+
+{
+ LPFIFO p;
+ LPFIFO prev = (LPFIFO)pFifo;
+
+ p = (LPFIFO)pFifo->Head;
+ if (p) {
+ pFifo->Head = p->Head;
+ if (!pFifo->Head) {
+ pFifo->Tail = NULL;
+ }
+ }
+ return p;
+}
diff --git a/private/nw/vwipxspx/dll/util.h b/private/nw/vwipxspx/dll/util.h
new file mode 100644
index 000000000..d75781952
--- /dev/null
+++ b/private/nw/vwipxspx/dll/util.h
@@ -0,0 +1,233 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ util.h
+
+Abstract:
+
+ Contains macros, prototypes and structures for util.c
+
+Author:
+
+ Richard L Firth (rfirth) 25-Oct-1993
+
+Revision History:
+
+ 25-Oct-1993 rfirth
+ Created
+
+--*/
+
+//
+// external data
+//
+
+extern CRITICAL_SECTION SerializationCritSec;
+
+//
+// one-line function macros
+//
+
+#define RequestMutex() EnterCriticalSection(&SerializationCritSec)
+#define ReleaseMutex() LeaveCriticalSection(&SerializationCritSec)
+
+//
+// function prototypes
+//
+
+int
+GetInternetAddress(
+ IN OUT LPSOCKADDR_IPX InternetAddress
+ );
+
+int
+GetMaxPacketSize(
+ OUT LPWORD MaxPacketSize
+ );
+
+LPXECB
+RetrieveEcb(
+ IN BYTE Type
+ );
+
+LPXECB
+RetrieveXEcb(
+ IN BYTE Type,
+ IN LPECB pEcb,
+ IN ECB_ADDRESS EcbAddress
+ );
+
+VOID
+ScheduleEvent(
+ IN LPXECB pXecb,
+ IN WORD Ticks
+ );
+
+VOID
+ScanTimerList(
+ VOID
+ );
+
+BYTE
+CancelTimerEvent(
+ IN LPXECB pXecb
+ );
+
+VOID
+CancelTimedEvents(
+ IN WORD SocketNumber,
+ IN WORD Owner,
+ IN DWORD TaskId
+ );
+
+BYTE
+CancelAsyncEvent(
+ IN LPXECB pXecb
+ );
+
+BYTE
+CancelSocketEvent(
+ IN LPXECB pXecb
+ );
+
+BYTE
+CancelConnectionEvent(
+ IN LPXECB pXecb
+ );
+
+VOID
+QueueEcb(
+ IN LPXECB pXecb,
+ IN LPXECB_QUEUE Queue,
+ IN QUEUE_ID QueueId
+ );
+
+LPXECB
+DequeueEcb(
+ IN LPXECB pXecb,
+ IN LPXECB_QUEUE Queue
+ );
+
+VOID
+CancelSocketQueue(
+ IN LPXECB_QUEUE pXecbQueue
+ );
+
+VOID
+CancelConnectionQueue(
+ IN LPXECB_QUEUE pXecbQueue
+ );
+
+VOID
+AbortQueue(
+ IN LPXECB_QUEUE pXecbQueue,
+ IN BYTE CompletionCode
+ );
+
+VOID
+AbortConnectionEvent(
+ IN LPXECB pXecb,
+ IN BYTE CompletionCode
+ );
+
+VOID
+StartIpxSend(
+ IN LPXECB pEcb,
+ IN LPSOCKET_INFO pSocketInfo
+ );
+
+BOOL
+GetIoBuffer(
+ IN OUT LPXECB pXecb,
+ IN BOOL Send,
+ IN WORD HeaderLength
+ );
+
+VOID
+GatherData(
+ IN LPXECB pXecb,
+ IN WORD HeaderLength
+ );
+
+VOID
+ScatterData(
+ IN LPXECB pXecb
+ );
+
+VOID
+IpxReceiveFirst(
+ IN LPXECB pXecb,
+ IN LPSOCKET_INFO pSocketInfo
+ );
+
+VOID
+IpxReceiveNext(
+ IN LPSOCKET_INFO pSocketInfo
+ );
+
+VOID
+IpxSendNext(
+ IN LPSOCKET_INFO pSocketInfo
+ );
+
+VOID
+CompleteOrQueueIo(
+ IN LPXECB pXecb,
+ IN BYTE CompletionCode
+ );
+
+VOID
+CompleteIo(
+ IN LPXECB pXecb,
+ IN BYTE CompletionCode
+ );
+
+VOID
+CompleteOrQueueEcb(
+ IN LPXECB pXecb,
+ IN BYTE CompletionCode
+ );
+
+VOID
+CompleteEcb(
+ IN LPXECB pEcb,
+ IN BYTE CompletionCode
+ );
+
+VOID
+EsrCallback(
+ VOID
+ );
+
+VOID
+VWinEsrCallback(
+ WORD *pSegment,
+ WORD *pOffset,
+ BYTE *pFlags
+ );
+
+VOID
+FifoAddHead(
+ IN LPFIFO pFifo,
+ IN LPFIFO pElement
+ );
+
+VOID
+FifoAdd(
+ IN LPFIFO pFifo,
+ IN LPFIFO pElement
+ );
+
+LPFIFO
+FifoRemove(
+ IN LPFIFO pFifo,
+ IN LPFIFO pElement
+ );
+
+LPFIFO
+FifoNext(
+ IN LPFIFO pFifo
+ );
diff --git a/private/nw/vwipxspx/dll/vw.h b/private/nw/vwipxspx/dll/vw.h
new file mode 100644
index 000000000..e14ec83e0
--- /dev/null
+++ b/private/nw/vwipxspx/dll/vw.h
@@ -0,0 +1,57 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ vw.h
+
+Abstract:
+
+ Top-level include file for VWIPXSPX DLL. Pulls in all other required header
+ files
+
+Author:
+
+ Richard L Firth (rfirth) 25-Oct-1993
+
+Revision History:
+
+ 25-Oct-1993 rfirth
+ Created
+
+--*/
+
+//
+// all include files required by VWIPXSPX.DLL
+//
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#include <nt.h>
+#include <ntrtl.h>
+#include <nturtl.h>
+#include <windows.h>
+#define FD_SETSIZE MAX_OPEN_SOCKETS
+#include <winsock.h>
+#include <wsipx.h>
+#include <wsnwlink.h>
+#include <vddsvc.h> // GetVDMAddress, GetVDMPointer
+#undef getMSW // BUGBUG: there's no c_getMSW in the lib!!!
+
+extern WORD getMSW(VOID);
+
+#include "vwvdm.h"
+#include "vwdll.h"
+#include "vwipxspx.h"
+#include "vwasync.h"
+#include "vwmisc.h"
+#include "vwipx.h"
+#include "vwspx.h"
+#include "socket.h"
+#include "util.h"
+#include "vwdebug.h"
+#include "vwinapi.h"
+#include "vwint.h"
diff --git a/private/nw/vwipxspx/dll/vwasync.c b/private/nw/vwipxspx/dll/vwasync.c
new file mode 100644
index 000000000..686d05816
--- /dev/null
+++ b/private/nw/vwipxspx/dll/vwasync.c
@@ -0,0 +1,167 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ vwasync.c
+
+Abstract:
+
+ ntVdm netWare (Vw) IPX/SPX Functions
+
+ Vw: The peoples' network
+
+ Contains Asyncrhonous Event Scheduler (thread)
+
+ Contents:
+ VwAesThread
+ (CheckPendingIpxRequests)
+
+Author:
+
+ Richard L Firth (rfirth) 30-Sep-1993
+
+Environment:
+
+ User-mode Win32
+
+Revision History:
+
+ 30-Sep-1993 rfirth
+ Created
+
+--*/
+
+#include "vw.h"
+#pragma hdrstop
+
+//
+// private routine prototypes
+//
+
+PRIVATE
+VOID
+CheckPendingIpxRequests(
+ VOID
+ );
+
+//
+// global data
+//
+
+WORD AesTickCount;
+
+//
+// functions
+//
+
+
+DWORD
+VwAesThread(
+ IN LPVOID Parameters
+ )
+
+/*++
+
+Routine Description:
+
+ Provides the functionality of the Asynchronous Event Scheduler (AES) in the
+ Netware world:
+
+ - updates the tick count
+ - completes any matured timer events
+ - checks any pending requests and schedules the next action
+
+ This thread wakes up every PC tick (1/18 second)
+
+Arguments:
+
+ Parameters - unused
+
+Return Value:
+
+ DWORD
+ 0
+
+--*/
+
+{
+ BOOL fOperationPerformed = FALSE ;
+ static int n = 1 ;
+
+ UNREFERENCED_PARAMETER(Parameters);
+
+ while (TRUE)
+ {
+ //
+ // we will always yield in this loop to be friendly to others,
+ // but occasionally we will forcefully do a non zero sleep for
+ // lower priority threads to run.
+ //
+ if ((n % 100) == 0)
+ {
+ Sleep(ONE_TICK) ;
+ n = 1 ;
+ }
+ if (!fOperationPerformed && ((n % 4) == 0))
+ {
+ Sleep(10) ;
+ n++ ;
+ }
+ else
+ {
+ Sleep(0) ;
+ n++ ;
+ }
+
+ ++AesTickCount;
+ ScanTimerList();
+ CheckPendingIpxRequests();
+ CheckPendingSpxRequests(&fOperationPerformed);
+ }
+
+ return 0; // compiler-pacifier
+}
+
+
+PRIVATE
+VOID
+CheckPendingIpxRequests(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Polls the opened, active non-blocking IPX sockets to see if there is anything
+ to do (data to receive, availability to send, timeouts)
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPSOCKET_INFO pActiveSocket = NULL;
+
+ //
+ // search SOCKET_INFO structures for something to do. Could do select()
+ // but we have most of the info anyway. We use the BFI filter mechanism
+ //
+
+ while (pActiveSocket = FindActiveSocket(pActiveSocket)) {
+ if (pActiveSocket->Flags & SOCKET_FLAG_SENDING) {
+ IpxSendNext(pActiveSocket);
+ }
+ if (pActiveSocket->Flags & SOCKET_FLAG_LISTENING) {
+ IpxReceiveNext(pActiveSocket);
+ }
+ }
+}
diff --git a/private/nw/vwipxspx/dll/vwasync.h b/private/nw/vwipxspx/dll/vwasync.h
new file mode 100644
index 000000000..4c4bd4779
--- /dev/null
+++ b/private/nw/vwipxspx/dll/vwasync.h
@@ -0,0 +1,27 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ vwipx.h
+
+Abstract:
+
+ Contains function prototypes for VWASYNC.C
+
+Author:
+
+ Richard L Firth (rfirth) 25-Oct-1993
+
+Revision History:
+
+ 25-Oct-1993 rfirth
+ Created
+
+--*/
+
+DWORD
+VwAesThread(
+ IN LPVOID Parameters
+ );
diff --git a/private/nw/vwipxspx/dll/vwdebug.c b/private/nw/vwipxspx/dll/vwdebug.c
new file mode 100644
index 000000000..5aa94c933
--- /dev/null
+++ b/private/nw/vwipxspx/dll/vwdebug.c
@@ -0,0 +1,825 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ vwdebug.c
+
+Abstract:
+
+ Contains debug routines for VWIPXSPX.DLL
+
+ Contents:
+ VwDebugStart
+ VwDebugEnd
+ VwDebugPrint
+ VwDumpData
+ VwDumpEcb
+ VwDumpFragment
+ VwDumpPacketHeader
+ VwDumpXecb
+ VwDumpSocketInfo
+ VwDumpConnectionInfo
+ VwDumpConnectionStats
+ VwLog
+ CheckInterrupts
+
+Author:
+
+ Richard L Firth (rfirth) 5-Oct-1993
+
+Environment:
+
+ User-mode Win32
+
+Revision History:
+
+ 5-Oct-1993 rfirth
+ Created
+
+--*/
+
+#include "vw.h"
+#pragma hdrstop
+
+#if DBG
+
+//
+// private prototypes
+//
+
+int PokeLevelString(LPSTR, DWORD, DWORD, LPSTR);
+LPSTR StripNameFromPath(LPSTR);
+
+//
+// private data
+//
+
+DWORD VwDebugFlags = 0;
+DWORD VwDebugFunctions = 0;
+DWORD VwShow = SHOW_ECBS | SHOW_HEADERS;
+DWORD VwDebugLevel = IPXDBG_MIN_LEVEL;
+DWORD VwDebugDump = 0;
+BOOL VwDebugInitialized = FALSE;
+FILE* hVwDebugLog = NULL;
+DWORD DebugFlagsEx = 0 ;
+
+//
+// functions
+//
+
+VOID VwDebugStart() {
+
+ //
+ // a little run-time diagnostication, madam?
+ //
+
+ LPSTR ptr;
+
+ if (VwDebugInitialized) {
+ return;
+ }
+
+ //
+ // override VwDebugFlags from VWFLAGS environment variable
+ //
+
+ if (ptr = getenv("VWFLAGS")) {
+ VwDebugFlags = (DWORD)strtoul(ptr, NULL, 0);
+ }
+ if (ptr = getenv("VWFUNCS")) {
+ VwDebugFunctions = (DWORD)strtoul(ptr, NULL, 0);
+ }
+ if (ptr = getenv("VWSHOW")) {
+ VwShow = (DWORD)strtoul(ptr, NULL, 0);
+ }
+ if (ptr = getenv("VWLEVEL")) {
+ VwDebugLevel = strtoul(ptr, NULL, 0);
+ if (VwDebugLevel > IPXDBG_MAX_LEVEL) {
+ VwDebugLevel = IPXDBG_MAX_LEVEL;
+ }
+ }
+ if (ptr = getenv("VWDUMP")) {
+ VwDebugDump = strtoul(ptr, NULL, 0);
+ }
+ IF_DEBUG(TO_FILE) {
+ if ((hVwDebugLog = fopen(VWDEBUG_FILE, "w+")) == NULL) {
+ VwDebugFlags &= ~DEBUG_TO_FILE;
+ } else {
+
+#if 0
+
+ char currentDirectory[256];
+ int n;
+
+ currentDirectory[0] = 0;
+ if (n = GetCurrentDirectory(sizeof(currentDirectory), currentDirectory)) {
+ if (currentDirectory[n-1] == '\\') {
+ currentDirectory[n-1] = 0;
+ }
+ }
+
+ DbgPrint("VWIPXSPX: Writing debug output to %s\\" VWDEBUG_FILE "\n", currentDirectory);
+#endif
+
+ }
+ }
+ VwDebugInitialized = TRUE;
+}
+
+VOID VwDebugEnd() {
+ IF_DEBUG(TO_FILE) {
+ fflush(hVwDebugLog);
+ fclose(hVwDebugLog);
+ }
+}
+
+VOID VwDebugPrint(LPSTR Module, DWORD Line, DWORD Function, DWORD Level, LPSTR Format, ...) {
+
+ char buf[1024];
+ va_list p;
+
+ IF_DEBUG(NOTHING) {
+ return;
+ }
+
+ //
+ // only log something if we are tracking this Function and Level is above
+ // (or equal to) the filter cut-off or Level >= minimum alert level (error)
+ //
+
+ if (((Function & VwDebugFunctions) && (Level >= VwDebugLevel)) || (Level >= IPXDBG_LEVEL_ERROR)) {
+ va_start(p, Format);
+ vsprintf(buf+PokeLevelString(Module, Line, Level, buf), Format, p);
+ VwLog(buf);
+ va_end(p);
+ }
+}
+
+int PokeLevelString(LPSTR Module, DWORD Line, DWORD Level, LPSTR Buffer) {
+
+ int length;
+ static char levelString[4] = " : ";
+ char level;
+
+ switch (Level) {
+ case IPXDBG_LEVEL_INFO:
+ level = 'I';
+ break;
+
+ case IPXDBG_LEVEL_WARNING:
+ level = 'W';
+ break;
+
+ case IPXDBG_LEVEL_ERROR:
+ level = 'E';
+ break;
+
+ case IPXDBG_LEVEL_FATAL:
+ level = 'F';
+ break;
+ }
+
+ levelString[0] = level;
+ strcpy(Buffer, levelString);
+ length = strlen(levelString);
+
+ if (Level >= IPXDBG_LEVEL_ERROR) {
+ length += sprintf(Buffer + length, "%s [% 5d]: ", StripNameFromPath(Module), Line);
+ }
+
+ return length;
+}
+
+LPSTR StripNameFromPath(LPSTR Path) {
+
+ LPSTR p;
+
+ p = strrchr(Path, '\\');
+ return p ? p+1 : Path;
+}
+
+VOID VwDumpData(ULPBYTE Address, WORD Seg, WORD Off, BOOL InVdm, WORD Size) {
+
+ char buf[128];
+ int i, len;
+
+ IF_NOT_DEBUG(DATA) {
+ return;
+ }
+
+ while (Size) {
+ len = min(Size, 16);
+ if (InVdm) {
+ sprintf(buf, "%04x:%04x ", Seg, Off);
+ } else {
+ sprintf(buf, "%08x ", Address);
+ }
+ for (i = 0; i < len; ++i) {
+ sprintf(&buf[10+i*3], "%02.2x ", Address[i] & 0xff);
+ }
+ for (i = len; i < 17; ++i) {
+ strcat(buf, " ");
+ }
+ for (i = 0; i < len; ++i) {
+
+ char ch;
+
+ ch = Address[i];
+ buf[61+i] = ((ch < 32) || (ch > 127)) ? '.' : ch;
+ }
+ buf[61+i++] = '\n';
+ buf[61+i] = 0;
+ VwLog(buf);
+ Address += len;
+ Size -= len;
+ Off += len;
+ }
+ VwLog("\n");
+}
+
+VOID VwDumpEcb(LPECB pEcb, WORD Seg, WORD Off, BYTE Type, BOOL Frags, BOOL Data, BOOL Mode) {
+
+ char buf[512];
+ int n;
+ char* bufptr;
+
+ IF_NOT_DEBUG(ECB) {
+ return;
+ }
+
+ IF_NOT_SHOW(ECBS) {
+ VwDumpData((ULPBYTE)pEcb,
+ Seg,
+ Off,
+ TRUE,
+ (WORD)((Type == ECB_TYPE_AES)
+ ? sizeof(AES_ECB)
+ : (sizeof(IPX_ECB) + sizeof(FRAGMENT) * (pEcb->FragmentCount - 1)))
+ );
+ return;
+ }
+
+ n = sprintf(buf,
+ "\n"
+ "%s ECB @ %04x:%04x:\n"
+ "LinkAddress %04x:%04x\n"
+ "EsrAddress %04x:%04x\n"
+ "InUse %02x\n",
+ (Type == ECB_TYPE_AES)
+ ? "AES"
+ : (Type == ECB_TYPE_IPX)
+ ? "IPX"
+ : "SPX",
+ Seg,
+ Off,
+ GET_SEGMENT(&pEcb->LinkAddress),
+ GET_OFFSET(&pEcb->LinkAddress),
+ GET_SEGMENT(&pEcb->EsrAddress),
+ GET_OFFSET(&pEcb->EsrAddress),
+ pEcb->InUse
+ );
+ bufptr = buf + n;
+ if (Type == ECB_TYPE_AES) {
+ sprintf(bufptr,
+ "AesWorkspace %02x-%02x-%02x-%02x-%02x\n",
+ ((LPAES_ECB)pEcb)->AesWorkspace[0] & 0xff,
+ ((LPAES_ECB)pEcb)->AesWorkspace[1] & 0xff,
+ ((LPAES_ECB)pEcb)->AesWorkspace[2] & 0xff,
+ ((LPAES_ECB)pEcb)->AesWorkspace[3] & 0xff,
+ ((LPAES_ECB)pEcb)->AesWorkspace[4] & 0xff
+ );
+ } else {
+ sprintf(bufptr,
+ "CompletionCode %02x\n"
+ "SocketNumber %04x\n"
+ "IpxWorkspace %08x\n"
+ "DriverWorkspace %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n"
+ "ImmediateAddress %02x-%02x-%02x-%02x-%02x-%02x\n"
+ "FragmentCount %04x\n",
+ pEcb->CompletionCode,
+ B2LW(pEcb->SocketNumber),
+ pEcb->IpxWorkspace,
+ pEcb->DriverWorkspace[0] & 0xff,
+ pEcb->DriverWorkspace[1] & 0xff,
+ pEcb->DriverWorkspace[2] & 0xff,
+ pEcb->DriverWorkspace[3] & 0xff,
+ pEcb->DriverWorkspace[4] & 0xff,
+ pEcb->DriverWorkspace[5] & 0xff,
+ pEcb->DriverWorkspace[6] & 0xff,
+ pEcb->DriverWorkspace[7] & 0xff,
+ pEcb->DriverWorkspace[8] & 0xff,
+ pEcb->DriverWorkspace[9] & 0xff,
+ pEcb->DriverWorkspace[10] & 0xff,
+ pEcb->DriverWorkspace[11] & 0xff,
+ pEcb->ImmediateAddress[0] & 0xff,
+ pEcb->ImmediateAddress[1] & 0xff,
+ pEcb->ImmediateAddress[2] & 0xff,
+ pEcb->ImmediateAddress[3] & 0xff,
+ pEcb->ImmediateAddress[4] & 0xff,
+ pEcb->ImmediateAddress[5] & 0xff,
+ pEcb->FragmentCount
+ );
+ }
+
+ VwLog(buf);
+
+ if ((Type != ECB_TYPE_AES) && Frags) {
+
+ ASSERT(pEcb->FragmentCount < 10);
+
+ VwDumpFragment(pEcb->FragmentCount,
+ (LPFRAGMENT)(pEcb + 1),
+ Type,
+ Data,
+ Mode
+ );
+ }
+}
+
+VOID VwDumpFragment(WORD Count, LPFRAGMENT pFrag, BYTE Type, BOOL Data, BOOL Mode) {
+
+ char buf[256];
+ int i;
+
+ IF_NOT_DEBUG(FRAGMENTS) {
+ return;
+ }
+
+ for (i = 0; i < Count; ++i) {
+ sprintf(buf,
+ "Fragment %d:\n"
+ " Address %04x:%04x\n"
+ " Length %04x\n",
+ i + 1,
+ GET_SEGMENT(&pFrag->Address),
+ GET_OFFSET(&pFrag->Address),
+ pFrag->Length
+ );
+ VwLog(buf);
+ if (Data) {
+
+ ULPBYTE ptr;
+ WORD size;
+ WORD offset;
+
+ ptr = GET_FAR_POINTER(&pFrag->Address, Mode);
+ size = pFrag->Length;
+ offset = GET_OFFSET(&pFrag->Address);
+
+ //
+ // this allows us to show headers vs. raw data
+ //
+
+ IF_SHOW(HEADERS) {
+ if (i == 0) {
+ VwDumpPacketHeader(ptr, Type);
+
+ if (Type == ECB_TYPE_IPX) {
+ ptr += IPX_HEADER_LENGTH;
+ size -= IPX_HEADER_LENGTH;
+ offset += IPX_HEADER_LENGTH;
+ } else {
+ ptr += SPX_HEADER_LENGTH;
+ size -= SPX_HEADER_LENGTH;
+ offset += SPX_HEADER_LENGTH;
+ }
+ }
+ }
+
+ VwDumpData(ptr,
+ GET_SEGMENT(&pFrag->Address),
+ offset,
+ TRUE,
+ size
+ );
+ }
+ ++pFrag;
+ }
+}
+
+VOID VwDumpPacketHeader(ULPBYTE pPacket, BYTE Type) {
+
+ char buf[512];
+
+ IF_NOT_DEBUG(HEADERS) {
+ return;
+ }
+
+ sprintf(buf,
+ "Checksum %04x\n"
+ "Length %04x\n"
+ "TransportControl %02x\n"
+ "PacketType %02x\n"
+ "Destination %02x-%02x-%02x-%02x : %02x-%02x-%02x-%02x-%02x-%02x : %04x\n"
+ "Source %02x-%02x-%02x-%02x : %02x-%02x-%02x-%02x-%02x-%02x : %04x\n",
+ ((LPIPX_PACKET)pPacket)->Checksum,
+ B2LW(((LPIPX_PACKET)pPacket)->Length),
+ ((LPIPX_PACKET)pPacket)->TransportControl,
+ ((LPIPX_PACKET)pPacket)->PacketType,
+ ((LPIPX_PACKET)pPacket)->Destination.Net[0] & 0xff,
+ ((LPIPX_PACKET)pPacket)->Destination.Net[1] & 0xff,
+ ((LPIPX_PACKET)pPacket)->Destination.Net[2] & 0xff,
+ ((LPIPX_PACKET)pPacket)->Destination.Net[3] & 0xff,
+ ((LPIPX_PACKET)pPacket)->Destination.Node[0] & 0xff,
+ ((LPIPX_PACKET)pPacket)->Destination.Node[1] & 0xff,
+ ((LPIPX_PACKET)pPacket)->Destination.Node[2] & 0xff,
+ ((LPIPX_PACKET)pPacket)->Destination.Node[3] & 0xff,
+ ((LPIPX_PACKET)pPacket)->Destination.Node[4] & 0xff,
+ ((LPIPX_PACKET)pPacket)->Destination.Node[5] & 0xff,
+ B2LW(((LPIPX_PACKET)pPacket)->Destination.Socket),
+ ((LPIPX_PACKET)pPacket)->Source.Net[0] & 0xff,
+ ((LPIPX_PACKET)pPacket)->Source.Net[1] & 0xff,
+ ((LPIPX_PACKET)pPacket)->Source.Net[2] & 0xff,
+ ((LPIPX_PACKET)pPacket)->Source.Net[3] & 0xff,
+ ((LPIPX_PACKET)pPacket)->Source.Node[0] & 0xff,
+ ((LPIPX_PACKET)pPacket)->Source.Node[1] & 0xff,
+ ((LPIPX_PACKET)pPacket)->Source.Node[2] & 0xff,
+ ((LPIPX_PACKET)pPacket)->Source.Node[3] & 0xff,
+ ((LPIPX_PACKET)pPacket)->Source.Node[4] & 0xff,
+ ((LPIPX_PACKET)pPacket)->Source.Node[5] & 0xff,
+ B2LW(((LPIPX_PACKET)pPacket)->Source.Socket)
+ );
+ VwLog(buf);
+ if (Type == ECB_TYPE_SPX) {
+ sprintf(buf,
+ "ConnectControl %02x\n"
+ "DataStreamType %02x\n"
+ "SourceConnectId %04x\n"
+ "DestConnectId %04x\n"
+ "SequenceNumber %04x\n"
+ "AckNumber %04x\n"
+ "AllocationNumber %04x\n",
+ ((LPSPX_PACKET)pPacket)->ConnectionControl,
+ ((LPSPX_PACKET)pPacket)->DataStreamType,
+ B2LW(((LPSPX_PACKET)pPacket)->SourceConnectId),
+ B2LW(((LPSPX_PACKET)pPacket)->DestinationConnectId),
+ B2LW(((LPSPX_PACKET)pPacket)->SequenceNumber),
+ B2LW(((LPSPX_PACKET)pPacket)->AckNumber),
+ B2LW(((LPSPX_PACKET)pPacket)->AllocationNumber)
+ );
+ VwLog(buf);
+ }
+ VwLog("\n");
+}
+
+VOID VwDumpXecb(LPXECB pXecb) {
+
+ char buf[512];
+
+ IF_NOT_DEBUG(XECB) {
+ return;
+ }
+
+ sprintf(buf,
+ "XECB @ %08x:\n"
+ "Next %08x\n"
+ "Ecb %08x\n"
+ "EcbAddress %08x\n"
+ "EsrAddress %08x\n"
+ "Buffer %08x\n"
+ "Data %08x\n"
+ "FrameLength %04x\n"
+ "ActualLength %04x\n"
+ "Length %04x\n"
+ "Ticks %04x\n"
+ "SocketNumber %04x\n"
+ "Owner %04x\n"
+ "TaskId %08x\n"
+ "Flags %08x\n"
+ "QueueId %08x\n"
+ "OwningObject %08x\n"
+ "RefCount %08x\n",
+ pXecb,
+ pXecb->Next,
+ pXecb->Ecb,
+ pXecb->EcbAddress,
+ pXecb->EsrAddress,
+ pXecb->Buffer,
+ pXecb->Data,
+ pXecb->FrameLength,
+ pXecb->ActualLength,
+ pXecb->Length,
+ pXecb->Ticks,
+ B2LW(pXecb->SocketNumber),
+ pXecb->Owner,
+ pXecb->TaskId,
+ pXecb->Flags,
+ pXecb->QueueId,
+ pXecb->OwningObject,
+ pXecb->RefCount
+ );
+ VwLog(buf);
+}
+
+VOID VwDumpSocketInfo(LPSOCKET_INFO pSocketInfo) {
+
+ char buf[512];
+
+ IF_NOT_DEBUG(SOCKINFO) {
+ return;
+ }
+
+ sprintf(buf,
+ "SOCKET_INFO @ %08x:\n"
+ "Next %08x\n"
+ "SocketNumber %04x\n"
+ "Owner %04x\n"
+ "TaskId %08x\n"
+ "Socket %08x\n"
+ "Flags %08x\n"
+ "LongLived %d\n"
+ "SpxSocket %d\n"
+ "PendingSends %08x\n"
+ "PendingListens %08x\n"
+ "ListenQueue %08x, %08x\n"
+ "SendQueue %08x, %08x\n"
+ "HeaderQueue %08x, %08x\n"
+ "Connections %08x\n",
+ pSocketInfo,
+ pSocketInfo->Next,
+ B2LW(pSocketInfo->SocketNumber),
+ pSocketInfo->Owner,
+ pSocketInfo->TaskId,
+ pSocketInfo->Socket,
+ pSocketInfo->Flags,
+ pSocketInfo->LongLived,
+ pSocketInfo->SpxSocket,
+ pSocketInfo->PendingSends,
+ pSocketInfo->PendingListens,
+ pSocketInfo->ListenQueue.Head,
+ pSocketInfo->ListenQueue.Tail,
+ pSocketInfo->SendQueue.Head,
+ pSocketInfo->SendQueue.Tail,
+ pSocketInfo->HeaderQueue.Head,
+ pSocketInfo->HeaderQueue.Tail,
+ pSocketInfo->Connections
+ );
+ VwLog(buf);
+}
+
+VOID VwDumpConnectionInfo(LPCONNECTION_INFO pConnectionInfo) {
+
+ char buf[512];
+
+ IF_NOT_DEBUG(CONNINFO) {
+ return;
+ }
+
+ sprintf(buf,
+ "CONNECTION_INFO @ %08x:\n"
+ "Next %08x\n"
+ "List %08x\n"
+ "OwningSocket %08x\n"
+ "Socket %08x\n"
+ "TaskId %08x\n"
+ "ConnectionId %04x\n"
+ "Flags %02x\n"
+ "State %02x\n"
+ "ConnectQueue %08x, %08x\n"
+ "AcceptQueue %08x, %08x\n"
+ "SendQueue %08x, %08x\n"
+ "ListenQueue %08x, %08x\n",
+ pConnectionInfo,
+ pConnectionInfo->Next,
+ pConnectionInfo->List,
+ pConnectionInfo->OwningSocket,
+ pConnectionInfo->Socket,
+ pConnectionInfo->TaskId,
+ pConnectionInfo->ConnectionId,
+ pConnectionInfo->Flags,
+ pConnectionInfo->State,
+ pConnectionInfo->ConnectQueue.Head,
+ pConnectionInfo->ConnectQueue.Tail,
+ pConnectionInfo->AcceptQueue.Head,
+ pConnectionInfo->AcceptQueue.Tail,
+ pConnectionInfo->SendQueue.Head,
+ pConnectionInfo->SendQueue.Tail,
+ pConnectionInfo->ListenQueue.Head,
+ pConnectionInfo->ListenQueue.Tail
+ );
+ VwLog(buf);
+}
+
+VOID VwDumpConnectionStats(LPSPX_CONNECTION_STATS pStats) {
+
+ char buf[1024];
+
+ IF_NOT_DEBUG(STATS) {
+ return;
+ }
+
+ sprintf(buf,
+ "State %02x\n"
+ "WatchDog %02x\n"
+ "LocalConnectionId %04x\n"
+ "RemoteConnectionId %04x\n"
+ "LocalSequenceNumber %04x\n"
+ "LocalAckNumber %04x\n"
+ "LocalAllocNumber %04x\n"
+ "RemoteAckNumber %04x\n"
+ "RemoteAllocNumber %04x\n"
+ "LocalSocket %04x\n"
+ "ImmediateAddress %02x-%02x-%02x-%02x-%02x-%02x\n"
+ "RemoteNetwork %02x-%02x-%02x-%02x\n"
+ "RemoteNode %02x-%02x-%02x-%02x-%02x-%02x\n"
+ "RemoteSocket %04x\n"
+ "RetransmissionCount %04x\n"
+ "EstimatedRoundTripDelay %04x\n"
+ "RetransmittedPackets %04x\n",
+ pStats->State,
+ pStats->WatchDog,
+ B2LW(pStats->LocalConnectionId),
+ B2LW(pStats->RemoteConnectionId),
+ B2LW(pStats->LocalSequenceNumber),
+ B2LW(pStats->LocalAckNumber),
+ B2LW(pStats->LocalAllocNumber),
+ B2LW(pStats->RemoteAckNumber),
+ B2LW(pStats->RemoteAllocNumber),
+ B2LW(pStats->LocalSocket),
+ pStats->ImmediateAddress[0] & 0xff,
+ pStats->ImmediateAddress[1] & 0xff,
+ pStats->ImmediateAddress[2] & 0xff,
+ pStats->ImmediateAddress[3] & 0xff,
+ pStats->ImmediateAddress[4] & 0xff,
+ pStats->ImmediateAddress[5] & 0xff,
+ pStats->RemoteNetwork[0] & 0xff,
+ pStats->RemoteNetwork[1] & 0xff,
+ pStats->RemoteNetwork[2] & 0xff,
+ pStats->RemoteNetwork[3] & 0xff,
+ pStats->RemoteNode[0] & 0xff,
+ pStats->RemoteNode[1] & 0xff,
+ pStats->RemoteNode[2] & 0xff,
+ pStats->RemoteNode[3] & 0xff,
+ pStats->RemoteNode[4] & 0xff,
+ pStats->RemoteNode[5] & 0xff,
+ B2LW(pStats->RemoteSocket),
+ B2LW(pStats->RetransmissionCount),
+ B2LW(pStats->EstimatedRoundTripDelay),
+ B2LW(pStats->RetransmittedPackets)
+ );
+
+ VwLog(buf);
+}
+
+VOID VwLog(LPSTR buf) {
+
+ IF_DEBUG(NOTHING) {
+ return;
+ }
+
+ IF_DEBUG(TO_FILE) {
+ fputs(buf, hVwDebugLog);
+ IF_DEBUG(FLUSH) {
+ fflush(hVwDebugLog);
+ }
+ } else IF_DEBUG(TO_DBG) {
+ OutputDebugString(buf);
+ }
+}
+
+VOID CheckInterrupts(LPSTR name) {
+
+ IF_DEBUG(CHECK_INT) {
+
+ LPWORD pDosIntFlag = (LPWORD)GET_POINTER(0x40, 0x314, 2, FALSE);
+
+ if ((getIF() == 0) || !(*pDosIntFlag & 0x200)) {
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "*** CheckInterrupts: ints off in %s (IF=%d, 40:314=%04x)\n",
+ name,
+ getIF(),
+ *pDosIntFlag
+ ));
+ }
+ }
+}
+
+extern LPCONNECTION_INFO ConnectionList ;
+extern LPSOCKET_INFO SocketList ;
+
+VOID VwDumpAll(VOID)
+{
+ char buf[512];
+ LPCONNECTION_INFO pConnectionInfo;
+ LPSOCKET_INFO pSocketInfo;
+
+ if (DebugFlagsEx == 0)
+ return ;
+
+ DebugFlagsEx = 0 ;
+
+ RequestMutex();
+
+
+ pSocketInfo = SocketList;
+ while (pSocketInfo) {
+
+ LPXECB pXecb ;
+
+ if (!(pSocketInfo->SpxSocket)) {
+ pSocketInfo = pSocketInfo->Next;
+ continue ;
+ }
+
+ sprintf(buf,
+ "%sSOCKET_INFO @ %08x:\n"
+ " SocketNumber %04x\n"
+ " Owner %04x\n"
+ " TaskId %08x\n"
+ " Socket %08x\n"
+ " Flags %08x\n"
+ " LongLived %d\n"
+ " PendingSends %08x\n"
+ " PendingListens %08x\n"
+ " ListenQueue %08x, %08x\n"
+ " SendQueue %08x, %08x\n"
+ " HeaderQueue %08x, %08x\n"
+ " Connections %08x\n\n",
+ (pSocketInfo->SpxSocket)?"SPX ":"",
+ pSocketInfo,
+ B2LW(pSocketInfo->SocketNumber),
+ pSocketInfo->Owner,
+ pSocketInfo->TaskId,
+ pSocketInfo->Socket,
+ pSocketInfo->Flags,
+ pSocketInfo->LongLived,
+ pSocketInfo->PendingSends,
+ pSocketInfo->PendingListens,
+ pSocketInfo->ListenQueue.Head,
+ pSocketInfo->ListenQueue.Tail,
+ pSocketInfo->SendQueue.Head,
+ pSocketInfo->SendQueue.Tail,
+ pSocketInfo->HeaderQueue.Head,
+ pSocketInfo->HeaderQueue.Tail,
+ pSocketInfo->Connections
+ );
+ OutputDebugString(buf);
+
+ pConnectionInfo = pSocketInfo->Connections ;
+
+ while(pConnectionInfo) {
+ sprintf(buf,
+ "CONNECTION_INFO @ %08x:\n"
+ " List %08x\n"
+ " OwningSocket %08x\n"
+ " Socket %08x\n"
+ " TaskId %08x\n"
+ " ConnectionId %04x\n"
+ " Flags %02x\n"
+ " State %02x\n"
+ " ConnectQueue %08x, %08x\n"
+ " AcceptQueue %08x, %08x\n"
+ " SendQueue %08x, %08x\n"
+ " ListenQueue %08x, %08x\n\n",
+ pConnectionInfo,
+ pConnectionInfo->List,
+ pConnectionInfo->OwningSocket,
+ pConnectionInfo->Socket,
+ pConnectionInfo->TaskId,
+ pConnectionInfo->ConnectionId,
+ pConnectionInfo->Flags,
+ pConnectionInfo->State,
+ pConnectionInfo->ConnectQueue.Head,
+ pConnectionInfo->ConnectQueue.Tail,
+ pConnectionInfo->AcceptQueue.Head,
+ pConnectionInfo->AcceptQueue.Tail,
+ pConnectionInfo->SendQueue.Head,
+ pConnectionInfo->SendQueue.Tail,
+ pConnectionInfo->ListenQueue.Head,
+ pConnectionInfo->ListenQueue.Tail
+ );
+ OutputDebugString(buf);
+ pConnectionInfo = pConnectionInfo->Next ;
+ }
+
+ pXecb = pSocketInfo->ListenQueue.Head ;
+ while(pXecb) {
+ sprintf(buf,
+ " XECB @ %08x: (Ecb %08x)\n"
+ " EcbAddress/EsrAddress %08x %08x\n"
+ " Flags/RefCount %08x %08x\n"
+ " Buffer/QueueId %08x %08x\n"
+ " OwningObject %08x\n",
+ pXecb,
+ pXecb->Ecb,
+ pXecb->EcbAddress,
+ pXecb->EsrAddress,
+ pXecb->Flags,
+ pXecb->RefCount,
+ pXecb->Buffer,
+ pXecb->QueueId,
+ pXecb->OwningObject
+ );
+ OutputDebugString(buf);
+ pXecb = pXecb->Next ;
+ }
+ pSocketInfo = pSocketInfo->Next;
+ }
+ ReleaseMutex();
+}
+
+#endif // DBG
diff --git a/private/nw/vwipxspx/dll/vwdebug.h b/private/nw/vwipxspx/dll/vwdebug.h
new file mode 100644
index 000000000..142c7e22a
--- /dev/null
+++ b/private/nw/vwipxspx/dll/vwdebug.h
@@ -0,0 +1,184 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ vwdebug.h
+
+Abstract:
+
+ Prototypes, structures, manifests, macros for VWIPXSPX debug routines
+
+Author:
+
+ Richard L Firth (rfirth) 5-Oct-1993
+
+Revision History:
+
+ 5-Oct-1993 rfirth
+ Created
+
+--*/
+
+#ifndef _VWDEBUG_H_
+#define _VWDEBUG_H_
+
+//
+// debug flags
+//
+
+#define DEBUG_ANY 0xFFFFFFFF // any debug flags set
+#define DEBUG_NOTHING 0x00000001 // no debug output
+#define DEBUG_CHECK_INT 0x00080000 // check interrupts (DOS)
+#define DEBUG_STATS 0x00100000 // dump connection stats
+#define DEBUG_DATA 0x00200000 // dump data (send)
+#define DEBUG_FRAGMENTS 0x00400000 // dump fragments
+#define DEBUG_HEADERS 0x00800000 // dump IPX/SPX headers
+#define DEBUG_ECB 0x01000000 // dump 16-bit ECBs
+#define DEBUG_XECB 0x02000000 // dump 32-bit XECBs
+#define DEBUG_SOCKINFO 0x04000000 // dump SOCKET_INFO structs
+#define DEBUG_CONNINFO 0x08000000 // dump CONNECTION_INFO structs
+#define DEBUG_DLL 0x10000000 // include DLL attach/detach info
+#define DEBUG_FLUSH 0x20000000 // flush every write
+#define DEBUG_TO_FILE 0x40000000 // write debug stuff to file
+#define DEBUG_TO_DBG 0x80000000 // debug stuff to debugger
+
+#define VWDEBUG_FILE "VWDEBUG.LOG"
+
+//
+// function designators
+//
+
+#define FUNCTION_ANY 0xFFFFFFFF
+#define FUNCTION_IPXOpenSocket 0x00000001 // 0x00
+#define FUNCTION_IPXCloseSocket 0x00000002 // 0x01
+#define FUNCTION_IPXGetLocalTarget 0x00000004 // 0x02
+#define FUNCTION_IPXSendPacket 0x00000008 // 0x03
+#define FUNCTION_IPXListenForPacket 0x00000010 // 0x04
+#define FUNCTION_IPXScheduleIPXEvent 0x00000020 // 0x05
+#define FUNCTION_IPXCancelEvent 0x00000040 // 0x06
+#define FUNCTION_IPXScheduleAESEvent 0x00000080 // 0x07
+#define FUNCTION_IPXGetIntervalMarker 0x00000100 // 0x08
+#define FUNCTION_IPXGetInternetworkAddress 0x00000200 // 0x09
+#define FUNCTION_IPXRelinquishControl 0x00000400 // 0x0A
+#define FUNCTION_IPXDisconnectFromTarget 0x00000800 // 0x0B
+#define FUNCTION_InvalidFunction_0C 0x00001000 // 0x0C
+#define FUNCTION_InvalidFunction_0D 0x00002000 // 0x0D
+#define FUNCTION_InvalidFunction_0E 0x00004000 // 0x0E
+#define FUNCTION_InvalidFunction_0F 0x00008000 // 0x0F
+#define FUNCTION_SPXInitialize 0x00010000 // 0x10
+#define FUNCTION_SPXEstablishConnection 0x00020000 // 0x11
+#define FUNCTION_SPXListenForConnection 0x00040000 // 0x12
+#define FUNCTION_SPXTerminateConnection 0x00080000 // 0x13
+#define FUNCTION_SPXAbortConnection 0x00100000 // 0x14
+#define FUNCTION_SPXGetConnectionStatus 0x00200000 // 0x15
+#define FUNCTION_SPXSendSequencedPacket 0x00400000 // 0x16
+#define FUNCTION_SPXListenForSequencedPacket 0x00800000 // 0x17
+#define FUNCTION_InvalidFunction_18 0x01000000 // 0x18
+#define FUNCTION_InvalidFunction_19 0x02000000 // 0x19
+#define FUNCTION_IPXGetMaxPacketSize 0x04000000 // 0x1A
+#define FUNCTION_InvalidFunction_1B 0x08000000 // 0x1B
+#define FUNCTION_InvalidFunction_1C 0x10000000 // 0x1C
+#define FUNCTION_InvalidFunction_1D 0x20000000 // 0x1D
+#define FUNCTION_InvalidFunction_1E 0x40000000 // 0x1E
+#define FUNCTION_IPXGetInformation 0x80000000 // 0x1F
+#define FUNCTION_IPXSendWithChecksum 0xFFFFFFFF // 0x20
+#define FUNCTION_IPXGenerateChecksum 0xFFFFFFFF // 0x21
+#define FUNCTION_IPXVerifyChecksum 0xFFFFFFFF // 0x22
+
+//
+// debug levels
+//
+
+#define IPXDBG_LEVEL_ALL 0
+#define IPXDBG_LEVEL_INFO 1
+#define IPXDBG_LEVEL_WARNING 2
+#define IPXDBG_LEVEL_ERROR 3
+#define IPXDBG_LEVEL_FATAL 4
+
+#define IPXDBG_MIN_LEVEL IPXDBG_LEVEL_ALL
+#define IPXDBG_MAX_LEVEL IPXDBG_LEVEL_FATAL
+
+//
+// info dump flags (VWDUMP)
+//
+
+#define DUMP_ECB_IN 0x00000001
+#define DUMP_ECB_OUT 0x00000002
+#define DUMP_SEND_DATA 0x00000004
+#define DUMP_RECEIVE_DATA 0x00000008
+
+//
+// show flags
+//
+
+#define SHOW_ECBS 0x00000001 // show ECBs vs. raw data
+#define SHOW_HEADERS 0x00000002 // show IPX/SPX headers vs. raw data
+
+#if DBG
+
+extern DWORD VwDebugFlags;
+extern DWORD VwDebugFunctions;
+extern DWORD VwShow;
+extern DWORD DebugFlagsEx;
+
+
+#define IF_DEBUG(f) if (VwDebugFlags & DEBUG_ ## f)
+#define IF_NOT_DEBUG(f) if (!(VwDebugFlags & DEBUG_ ## f))
+#define IF_SHOW(f) if (VwShow & SHOW_ ## f)
+#define IF_NOT_SHOW(f) if (!(VwShow & SHOW_ ## f))
+#define PRIVATE
+#define IPXDBGPRINT(x) VwDebugPrint x
+#define IPXDBGSTART() VwDebugStart()
+#define IPXDBGEND() VwDebugEnd
+#define VWASSERT(a, b) ASSERT((a) == (b))
+#define IPXDUMPDATA(x) VwDumpData x
+#define IPXDUMPECB(x) VwDumpEcb x
+#define DUMPXECB(x) VwDumpXecb(x)
+#define DUMPCONN(x) VwDumpConnectionInfo(x)
+#define DUMPSTATS(x) VwDumpConnectionStats(x)
+#define CHECK_INTERRUPTS(s) CheckInterrupts(s)
+#define DUMPALL() VwDumpAll()
+
+#else
+
+#define IF_DEBUG(f) if (0)
+#define IF_NOT_DEBUG(f) if (0)
+#define IF_SHOW(f) if (0)
+#define IF_NOT_SHOW(f) if (0)
+#define PRIVATE static
+#define IPXDBGPRINT(x)
+#define IPXDBGSTART()
+#define IPXDBGEND()
+#define VWASSERT(a, b) a
+#define IPXDUMPDATA(x)
+#define IPXDUMPECB(x)
+#define DUMPXECB(x)
+#define DUMPCONN(x)
+#define DUMPSTATS(x)
+#define CHECK_INTERRUPTS(s)
+#define DUMPALL()
+
+#endif
+
+//
+// debug function prototypes
+//
+
+extern VOID VwDebugStart(VOID);
+extern VOID VwDebugEnd(VOID);
+extern VOID VwDebugPrint(LPSTR, DWORD, DWORD, DWORD, LPSTR, ...);
+extern VOID VwDumpData(ULPBYTE, WORD, WORD, BOOL, WORD);
+extern VOID VwDumpEcb(LPECB, WORD, WORD, BYTE, BOOL, BOOL, BOOL);
+extern VOID VwDumpFragment(WORD, LPFRAGMENT, BYTE, BOOL, BOOL);
+extern VOID VwDumpPacketHeader(ULPBYTE, BYTE);
+extern VOID VwDumpXecb(LPXECB);
+extern VOID VwDumpSocketInfo(LPSOCKET_INFO);
+extern VOID VwDumpConnectionInfo(LPCONNECTION_INFO);
+extern VOID VwDumpConnectionStats(LPSPX_CONNECTION_STATS);
+extern VOID VwLog(LPSTR);
+extern VOID CheckInterrupts(LPSTR);
+extern VOID VwDumpAll(VOID);
+
+#endif // _VWDEBUG_H_
diff --git a/private/nw/vwipxspx/dll/vwdll.c b/private/nw/vwipxspx/dll/vwdll.c
new file mode 100644
index 000000000..0b7b9fcd7
--- /dev/null
+++ b/private/nw/vwipxspx/dll/vwdll.c
@@ -0,0 +1,502 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ vwdll.c
+
+Abstract:
+
+ ntVdm netWare (Vw) IPX/SPX Functions
+
+ Vw: The peoples' network
+
+ VDD functions for DOS/WOW IPX/SPX support
+
+ Contents:
+ VwDllEntryPoint
+ VwInitialize
+ VWinInitialize
+ VwDispatcher
+ VwInvalidFunction
+
+Author:
+
+ Richard L Firth (rfirth) 30-Sep-1993
+
+Environment:
+
+ User-mode Win32
+
+Revision History:
+
+ 30-Sep-1993 rfirth
+ Created
+
+--*/
+
+#include "vw.h"
+#pragma hdrstop
+
+//
+// private prototypes
+//
+
+PRIVATE
+VOID
+VwInvalidFunction(
+ VOID
+ );
+
+//
+// private data
+//
+
+PRIVATE
+VOID
+(*VwDispatchTable[])(VOID) = {
+ VwIPXOpenSocket, // 0x00
+ VwIPXCloseSocket, // 0x01
+ VwIPXGetLocalTarget, // 0x02
+ VwIPXSendPacket, // 0x03
+ VwIPXListenForPacket, // 0x04
+ VwIPXScheduleIPXEvent, // 0x05
+ VwIPXCancelEvent, // 0x06
+ VwIPXScheduleAESEvent, // 0x07
+ VwIPXGetIntervalMarker, // 0x08
+ VwIPXGetInternetworkAddress, // 0x09
+ VwIPXRelinquishControl, // 0x0A
+ VwIPXDisconnectFromTarget, // 0x0B
+ VwInvalidFunction, // 0x0C
+ VwInvalidFunction, // 0x0D old-style GetMaxPacketSize
+ VwInvalidFunction, // 0x0E
+ VwInvalidFunction, // 0x0F internal send packet function
+ VwSPXInitialize, // 0x10
+ VwSPXEstablishConnection, // 0x11
+ VwSPXListenForConnection, // 0x12
+ VwSPXTerminateConnection, // 0x13
+ VwSPXAbortConnection, // 0x14
+ VwSPXGetConnectionStatus, // 0x15
+ VwSPXSendSequencedPacket, // 0x16
+ VwSPXListenForSequencedPacket, // 0x17
+ VwInvalidFunction, // 0x18
+ VwInvalidFunction, // 0x19
+ VwIPXGetMaxPacketSize, // 0x1A
+ VwInvalidFunction, // 0x1B
+ VwInvalidFunction, // 0x1C
+ VwInvalidFunction, // 0x1D
+ VwInvalidFunction, // 0x1E
+ VwIPXGetInformation, // 0x1F
+ VwIPXSendWithChecksum, // 0x20
+ VwIPXGenerateChecksum, // 0x21
+ VwIPXVerifyChecksum // 0x22
+};
+
+#define MAX_IPXSPX_FUNCTION LAST_ELEMENT(VwDispatchTable)
+
+WSADATA WsaData = {0};
+HANDLE hAesThread = NULL;
+
+//
+// global data
+//
+
+SOCKADDR_IPX MyInternetAddress;
+WORD MyMaxPacketSize;
+int Ica;
+BYTE IcaLine;
+
+//
+// not-really-global data
+//
+
+extern CRITICAL_SECTION SerializationCritSec;
+extern CRITICAL_SECTION AsyncCritSec;
+
+//
+// functions
+//
+
+
+BOOL
+WINAPI
+VwDllEntryPoint(
+ IN PVOID DllHandle,
+ IN ULONG Reason,
+ IN PCONTEXT Context OPTIONAL
+ )
+
+/*++
+
+Routine Description:
+
+ Called when the process attaches (LoadLibrary/init) and detaches (FreeLibrary/
+ process termination) from this DLL
+
+ Attach:
+ initialize Winsock DLL
+ get internet address for this station
+ get maximum packet size supported by transport (IPX)
+ create AES thread
+
+ Detach:
+ terminate Winsock DLL
+
+Arguments:
+
+ DllHandle - unused
+ Reason - checked for process attach/detach
+ Context - unused
+
+Return Value:
+
+ BOOLEAN
+
+--*/
+
+{
+ DWORD aesThreadId; // unused outside of this function
+
+ static BOOL CriticalSectionsAreInitialized = FALSE;
+
+ UNREFERENCED_PARAMETER(DllHandle);
+ UNREFERENCED_PARAMETER(Context);
+
+ IPXDBGSTART();
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "VwDllEntryPoint: %s\n",
+ Reason == DLL_PROCESS_ATTACH ? "DLL_PROCESS_ATTACH"
+ : Reason == DLL_PROCESS_DETACH ? "DLL_PROCESS_DETACH"
+ : Reason == DLL_THREAD_ATTACH ? "DLL_THREAD_ATTACH"
+ : Reason == DLL_THREAD_DETACH ? "DLL_THREAD_DETACH"
+ : "?"
+ ));
+
+ if (Reason == DLL_PROCESS_ATTACH) {
+
+ int err;
+
+ //
+ // BUGBUG: get ICA values from new VDD service. Right now we grab
+ // line 4 on the slave (base = 0x70, modifier = 0x03)
+ //
+
+ Ica = ICA_SLAVE;
+ IcaLine = 3;
+
+ err = WSAStartup(MAKEWORD(1, 1), &WsaData);
+ if (err) {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_FATAL,
+ "VwDllEntryPoint: WSAStartup() returns %d\n",
+ err
+ ));
+
+ return FALSE;
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "VwDllEntryPoint: WsaData:\n"
+ "\twVersion : 0x%04x\n"
+ "\twHighVersion : 0x%04x\n"
+ "\tszDescription : \"%s\"\n"
+ "\tszSystemStatus : \"%s\"\n"
+ "\tiMaxSockets : %d\n"
+ "\tiMaxUdpDg : %d\n"
+ "\tlpVendorInfo : 0x%08x\n",
+ WsaData.wVersion,
+ WsaData.wHighVersion,
+ WsaData.szDescription,
+ WsaData.szSystemStatus,
+ WsaData.iMaxSockets,
+ WsaData.iMaxUdpDg,
+ WsaData.lpVendorInfo
+ ));
+
+ }
+
+ //
+ // retrieve the internet address for this station. Used in
+ // IPXGetInternetworkAddress() and IPXSendPacket()
+ //
+
+ err = GetInternetAddress(&MyInternetAddress);
+ if (err) {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_FATAL,
+ "VwDllEntryPoint: GetInternetAddress() returns %d\n",
+ WSAGetLastError()
+ ));
+
+ goto attach_error_exit;
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "VwDllEntryPoint: MyInternetAddress:\n"
+ "\tNet : %02.2x-%02.2x-%02.2x-%02.2x\n"
+ "\tNode : %02.2x-%02.2x-%02.2x-%02.2x-%02.2x-%02.2x\n",
+ MyInternetAddress.sa_netnum[0] & 0xff,
+ MyInternetAddress.sa_netnum[1] & 0xff,
+ MyInternetAddress.sa_netnum[2] & 0xff,
+ MyInternetAddress.sa_netnum[3] & 0xff,
+ MyInternetAddress.sa_nodenum[0] & 0xff,
+ MyInternetAddress.sa_nodenum[1] & 0xff,
+ MyInternetAddress.sa_nodenum[2] & 0xff,
+ MyInternetAddress.sa_nodenum[3] & 0xff,
+ MyInternetAddress.sa_nodenum[4] & 0xff,
+ MyInternetAddress.sa_nodenum[5] & 0xff
+ ));
+
+ }
+
+ //
+ // get the maximum packet size supported by IPX. Used in
+ // IPXGetMaxPacketSize()
+ //
+
+ err = GetMaxPacketSize(&MyMaxPacketSize);
+ if (err) {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_FATAL,
+ "VwDllEntryPoint: GetMaxPacketSize() returns %d\n",
+ WSAGetLastError()
+ ));
+
+ goto attach_error_exit;
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "VwDllEntryPoint: GetMaxPacketSize: %04x (%d)\n",
+ MyMaxPacketSize,
+ MyMaxPacketSize
+ ));
+
+ }
+
+ hAesThread = CreateThread(NULL,
+ 0,
+ (LPTHREAD_START_ROUTINE)VwAesThread,
+ NULL,
+ 0,
+ &aesThreadId
+ );
+ if (hAesThread == NULL) {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_FATAL,
+ "VwDllEntryPoint: CreateThread() returns %d\n",
+ GetLastError()
+ ));
+
+ goto attach_error_exit;
+ }
+
+ //
+ // finally initialize any critical sections
+ //
+
+ InitializeCriticalSection(&SerializationCritSec);
+ InitializeCriticalSection(&AsyncCritSec);
+ CriticalSectionsAreInitialized = TRUE;
+ } else if (Reason == DLL_PROCESS_DETACH) {
+ if (hAesThread != NULL) {
+ WaitForSingleObject(hAesThread, ONE_TICK * 2);
+ CloseHandle(hAesThread);
+ }
+
+ WSACleanup();
+
+ if (CriticalSectionsAreInitialized) {
+ DeleteCriticalSection(&SerializationCritSec);
+ DeleteCriticalSection(&AsyncCritSec);
+ }
+
+ IPXDBGEND();
+ }
+ return TRUE;
+
+attach_error_exit:
+
+ //
+ // here if any fatal errors on process attach after successfully performing
+ // WSAStartup
+ //
+
+ WSACleanup();
+ return FALSE;
+}
+
+BYTE
+VWinInitialize(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ Called by interface when nwipxspx.dll is loaded. We
+ return the IRQ value.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ The IRQ value.
+
+--*/
+
+{
+ return 0x73;
+}
+
+
+
+VOID
+VwInitialize(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Called by VDD interface when DLL loaded via call to RegisterModule. We
+ get the IRQ value and return it as an interrupt vector in BX
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "VwInitialize\n"
+ ));
+
+ //
+ // only lines on slave PIC are available. Currently, lines 3, 4 and 7 are
+ // not used. We'll grab line 3 here, but in the future we expect a function
+ // to return the available IRQ line
+ //
+
+ setBX( VWinInitialize() );
+}
+
+
+VOID
+VwDispatcher(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Branches to relevant IPX/SPX handler for DOS calls, based on contents of
+ VDM BX register.
+
+ Control transfered here from 16-bit entry point, either as result of call
+ to far address returned from INT 2Fh/AH=7A or INT 7Ah
+
+ Special: we use BX = 0xFFFF to indicate that the app is terminating. The
+ TSR hooks INT 0x2F/AX=0x1122 (IFSResetEnvironment)
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ DWORD dispatchIndex;
+
+ dispatchIndex = (DWORD)getBX() & 0x7fff;
+
+ if (dispatchIndex <= MAX_IPXSPX_FUNCTION) {
+ VwDispatchTable[dispatchIndex]();
+ } else if (dispatchIndex == 0x7FFE) {
+ EsrCallback();
+ } else if (dispatchIndex == 0x7FFF) {
+ VwTerminateProgram();
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_ERROR,
+ "ERROR: VwDispatcher: dispatchIndex = %x\n",
+ dispatchIndex
+ ));
+
+ //
+ // BUGBUG - what's the correct error code to return in this situation?
+ // (will it ever arise?)
+ //
+
+ setAX(ERROR_INVALID_FUNCTION);
+ setCF(1);
+ }
+}
+
+
+PRIVATE
+VOID
+VwInvalidFunction(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Just alerts us to the fact that an invalid function request was made.
+ Useful if any app makes a bad call, or we miss out a required function
+ during design/implementation
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "VwInvalidFunction: BX=%04x\n",
+ getBX()
+ ));
+}
diff --git a/private/nw/vwipxspx/dll/vwdll.h b/private/nw/vwipxspx/dll/vwdll.h
new file mode 100644
index 000000000..aa87daddb
--- /dev/null
+++ b/private/nw/vwipxspx/dll/vwdll.h
@@ -0,0 +1,27 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ vwdll.h
+
+Abstract:
+
+ Contains external data declarations for VWDLL.C
+
+Author:
+
+ Richard L Firth (rfirth) 25-Oct-1993
+
+Revision History:
+
+ 25-Oct-1993 rfirth
+ Created
+
+--*/
+
+extern SOCKADDR_IPX MyInternetAddress;
+extern WORD MyMaxPacketSize;
+extern int Ica;
+extern BYTE IcaLine;
diff --git a/private/nw/vwipxspx/dll/vwdos.c b/private/nw/vwipxspx/dll/vwdos.c
new file mode 100644
index 000000000..31a8ee5ba
--- /dev/null
+++ b/private/nw/vwipxspx/dll/vwdos.c
@@ -0,0 +1,1502 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ vwdos.c
+
+Abstract:
+
+ ntVdm netWare (Vw) IPX/SPX Functions
+
+ Vw: The peoples' network
+
+ Contains handlers for DOS IPX/SPX calls (netware functions). The IPX APIs
+ use WinSock to perform the actual operations
+
+ Contents:
+ VwIPXCancelEvent
+ VwIPXCloseSocket
+ VwIPXDisconnectFromTarget
+ VwIPXGenerateChecksum
+ VwIPXGetInformation
+ VwIPXGetInternetworkAddress
+ VwIPXGetIntervalMarker
+ VwIPXGetLocalTarget
+ VwIPXGetLocalTargetAsync
+ VwIPXGetMaxPacketSize
+ VwIPXInitialize
+ VwIPXListenForPacket
+ VwIPXOpenSocket
+ VwIPXRelinquishControl
+ VwIPXScheduleAESEvent
+ VwIPXScheduleIPXEvent
+ VwIPXSendPacket
+ VwIPXSendWithChecksum
+ VwIPXSPXDeinit
+ VwIPXVerifyChecksum
+
+ VwSPXAbortConnection
+ VwSPXEstablishConnection
+ VwSPXGetConnectionStatus
+ VwSPXInitialize
+ VwSPXListenForConnection
+ VwSPXListenForSequencedPacket
+ VwSPXSendSequencedPacket
+ VwSPXTerminateConnection
+
+Author:
+
+ Richard L Firth (rfirth) 30-Sep-1993
+
+Environment:
+
+ User-mode Win32
+
+Revision History:
+
+ 30-Sep-1993 rfirth
+ Created
+
+--*/
+
+#include "vw.h"
+#pragma hdrstop
+
+//
+// functions
+//
+
+
+VOID
+VwIPXCancelEvent(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Cancels event described by an ECB
+
+ This call is Synchronous
+
+Arguments:
+
+ Inputs
+ BX 06h
+ ES:SI ECB
+
+ Outputs
+ AL Completion code:
+ 00h Success
+ F9h Can't cancel ECB
+ FFh ECB not in use
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPECB pEcb;
+ WORD status;
+
+ CHECK_INTERRUPTS("VwIPXCancelEvent");
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXCancelEvent,
+ IPXDBG_LEVEL_INFO,
+ "VwIPXCancelEvent(%04x:%04x)\n",
+ getES(),
+ getSI()
+ ));
+
+ IPX_GET_IPX_ECB(pEcb);
+
+ status = _VwIPXCancelEvent( pEcb );
+
+ IPX_SET_STATUS(status);
+}
+
+
+VOID
+VwIPXCloseSocket(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Closes a socket and cancels any outstanding events on the socket.
+ Closing an unopened socket does not return an error
+ ESRs in cancelled ECBs are not called
+
+ This call is Synchronous
+
+Arguments:
+
+ Inputs
+ BX 01h
+ DX Socket Number
+
+ Outputs
+ Nothing
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ WORD socketNumber;
+
+ CHECK_INTERRUPTS("VwIPXCloseSocket");
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXCloseSocket,
+ IPXDBG_LEVEL_INFO,
+ "VwIPXCloseSocket(%#x)\n",
+ B2LW(IPX_SOCKET_PARM())
+ ));
+
+ IPX_GET_SOCKET(socketNumber);
+
+ _VwIPXCloseSocket( socketNumber );
+
+}
+
+
+VOID
+VwIPXDisconnectFromTarget(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Performs no action for NTVDM IPX
+
+ This call is Synchronous
+
+Arguments:
+
+ Inputs
+ BX 0Bh
+ ES:SI Request buffer:
+ Destination Network DB 4 DUP (?)
+ Destination Node DB 6 DUP (?)
+ Destination Socket DB 2 DUP (?)
+
+ Outputs
+ Nothing
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ CHECK_INTERRUPTS("VwIPXDisconnectFromTarget");
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXDisconnectFromTarget,
+ IPXDBG_LEVEL_INFO,
+ "VwIPXDisconnectFromTarget\n"
+ ));
+}
+
+
+VOID
+VwIPXGenerateChecksum(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Generates checksum for a transmit ECB
+
+ This call is Synchronous
+
+Arguments:
+
+ Inputs
+ BX 21h
+ ES:SI ECB address
+
+ Outputs
+ No registers
+ ECB checksum field is updated
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ CHECK_INTERRUPTS("VwIPXGenerateChecksum");
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXGenerateChecksum,
+ IPXDBG_LEVEL_INFO,
+ "VwIPXGenerateChecksum\n"
+ ));
+}
+
+
+VOID
+VwIPXGetInformation(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Returns a bit-map of supported functions
+
+ This call is Synchronous
+
+Arguments:
+
+ Inputs
+ BX 1Fh
+ DX 0000h
+
+ Outputs
+ DX Bit map:
+ 0001h Set if IPX is IPXODI.COM, not dedicated IPX
+ 0002h Set if checksum functions (20h, 21h, 22h) supported
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ CHECK_INTERRUPTS("VwIPXGetInformation");
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXGetInformation,
+ IPXDBG_LEVEL_INFO,
+ "VwIPXGetInformation\n"
+ ));
+
+ IPX_SET_INFORMATION(IPX_ODI);
+}
+
+
+VOID
+VwIPXGetInternetworkAddress(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Returns a buffer containing the net number and node number for this
+ station.
+
+ This function cannot return an error (!)
+
+ Assumes: 1. GetInternetAddress has been successfully called in the
+ DLL initialization phase
+
+ This call is Synchronous
+
+Arguments:
+
+ Inputs
+ BX 09h
+
+ Outputs
+ ES:SI Buffer
+ Network Address DB 4 DUP (?)
+ Node Address DB 6 DUP (?)
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPINTERNET_ADDRESS pAddr;
+
+ CHECK_INTERRUPTS("VwIPXGetInternetworkAddress");
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXGetInternetworkAddress,
+ IPXDBG_LEVEL_INFO,
+ "VwIPXGetInternetworkAddress(%04x:%04x)\n",
+ getES(),
+ getSI()
+ ));
+
+ pAddr = (LPINTERNET_ADDRESS)IPX_BUFFER_PARM(sizeof(*pAddr));
+
+ _VwIPXGetInternetworkAddress( pAddr );
+}
+
+
+VOID
+VwIPXGetIntervalMarker(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Just returns the tick count maintained by Asynchronous Event Scheduler
+
+ This call is Synchronous
+
+Arguments:
+
+ Inputs
+ BX 08h
+
+ Outputs
+ AX Interval marker
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ CHECK_INTERRUPTS("VwIPXGetIntervalMarker");
+
+ setAX( _VwIPXGetIntervalMarker() );
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXGetIntervalMarker,
+ IPXDBG_LEVEL_INFO,
+ "VwIPXGetIntervalMarker: Returning %04x\n",
+ getAX()
+ ));
+}
+
+
+VOID
+VwIPXGetLocalTarget(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Given a target address of the form (network address {4}, node address {6}),
+ returns the node address of the target if on the same network, or the node
+ address of the router which knows how to get to the next hop in reaching the
+ eventual target
+
+ This call is Synchronous
+
+Arguments:
+
+ Inputs
+ BX 02h
+ ES:SI Request buffer
+ Destination Network DB 4 DUP (?)
+ Destination Node DB 6 DUP (?)
+ Destination Socket DB 2 DUP (?)
+ ES:DI Response buffer
+ Local Target DB 6 DUP (?)
+
+ Outputs
+ AL Completion code
+ 00h Success
+ FAh No path to destination node found
+ AH Number of hops to destination
+ CX Transport time
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPBYTE pImmediateAddress;
+ LPBYTE pNetworkAddress;
+ WORD transportTime;
+ WORD status;
+
+ CHECK_INTERRUPTS("VwIPXGetLocalTarget");
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXGetLocalTarget,
+ IPXDBG_LEVEL_INFO,
+ "VwIPXGetLocalTarget(target buf @ %04x:%04x, local buf @ %04x:%04x)\n",
+ getES(),
+ getSI(),
+ getES(),
+ getDI()
+ ));
+
+
+ pImmediateAddress = POINTER_FROM_WORDS(getES(), getDI(), 6);
+ pNetworkAddress = POINTER_FROM_WORDS(getES(), getSI(), 12);
+
+ status = _VwIPXGetLocalTarget( pNetworkAddress,
+ pImmediateAddress,
+ &transportTime );
+
+
+ setCX( transportTime );
+ setAH(1);
+
+ IPX_SET_STATUS(status);
+}
+
+
+VOID
+VwIPXGetLocalTargetAsync(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ description-of-function.
+
+ This call is Asynchronous
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ CHECK_INTERRUPTS("VwIPXGetLocalTargetAsync");
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "VwIPXGetLocalTargetAsync\n"
+ ));
+}
+
+
+VOID
+VwIPXGetMaxPacketSize(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Returns the maximum packet size the underlying network can handle
+
+ Assumes: 1. A successfull call to GetMaxPacketSize has been made during
+ DLL initialization
+ 2. Maximum packet size is constant
+
+ This call is Synchronous
+
+Arguments:
+
+ Inputs
+ BX 1Ah
+
+ Outputs
+ AX Maximum packet size
+ CX IPX retry count
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ WORD maxPacketSize;
+ WORD retryCount;
+
+ CHECK_INTERRUPTS("VwIPXGetMaxPacketSize");
+
+ maxPacketSize = _VwIPXGetMaxPacketSize( &retryCount );
+
+ setAX(maxPacketSize);
+
+ //
+ // BUGBUG: The DOS Assembly and C manuals differ slightly here: DOS says
+ // we return the IPX retry count in CX. There is no corresponding parameter
+ // in the C interface?
+ //
+
+ setCX(retryCount);
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXGetMaxPacketSize,
+ IPXDBG_LEVEL_INFO,
+ "VwIPXGetMaxPacketSize: PacketSize=%d, RetryCount=%d\n",
+ getAX(),
+ getCX()
+ ));
+}
+
+
+VOID
+VwIPXInitialize(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ description-of-function.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ CHECK_INTERRUPTS("VwIPXInitialize");
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "VwIPXInitialize\n"
+ ));
+}
+
+
+VOID
+VwIPXListenForPacket(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Queue a listen request against a socket. All listen requests will be
+ completed asynchronously, unless cancelled by app
+
+ This call is Asynchronous
+
+Arguments:
+
+ Inputs
+ BX 04h
+ ES:SI ECB address
+
+ Outputs
+ AL Completion code
+ FFh Socket doesn't exist
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPECB pEcb;
+ WORD status;
+
+ CHECK_INTERRUPTS("VwIPXListenForPacket");
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXListenForPacket,
+ IPXDBG_LEVEL_INFO,
+ "VwIPXListenForPacket(%04x:%04x)\n",
+ getES(),
+ getSI()
+ ));
+
+ IPX_GET_IPX_ECB(pEcb);
+
+ status = _VwIPXListenForPacket( pEcb, ECB_PARM_ADDRESS() );
+
+ IPX_SET_STATUS(status);
+}
+
+
+VOID
+VwIPXOpenSocket(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Opens a socket for use by IPX or SPX. Puts the socket into non-blocking mode.
+ The socket will be bound to IPX
+
+ This call is Synchronous
+
+Arguments:
+
+ Inputs
+ AL Socket Longevity flag
+ This parameter is actually in BP - AX has been sequestered
+ by the VDD dispatcher
+ BX 00h
+ DX Requested Socket Number
+
+ CX DOS PDB. This parameter is not part of the IPX API.
+ Added because we need to remember which DOS executable created
+ the socket: we need to clean-up short-lived sockets when the
+ executable terminates
+
+ Outputs
+ AL Completion code:
+ 00h Success
+ FFh Socket already open
+ FEh Socket table full
+ DX Assigned socket number
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ BYTE socketLife;
+ WORD socketNumber;
+ WORD status;
+
+ CHECK_INTERRUPTS("VwIPXOpenSocket");
+
+ IPX_GET_SOCKET_LIFE(socketLife);
+ IPX_GET_SOCKET(socketNumber);
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXOpenSocket,
+ IPXDBG_LEVEL_INFO,
+ "VwIPXOpenSocket(Life=%02x, Socket=%04x, Owner=%04x)\n",
+ socketLife,
+ B2LW(socketNumber),
+ IPX_SOCKET_OWNER_PARM()
+ ));
+
+
+ status = _VwIPXOpenSocket( &socketNumber,
+ socketLife,
+ IPX_SOCKET_OWNER_PARM() );
+
+ if ( status == IPX_SUCCESS )
+ IPX_SET_SOCKET(socketNumber);
+
+ IPX_SET_STATUS(status);
+}
+
+
+VOID
+VwIPXRelinquishControl(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Just sleep for a nominal amount. Netware seems to be dependent on the
+ default setting of the PC clock, so one timer tick (1/18 second) would
+ seem to be a good value
+
+ This call is Synchronous
+
+Arguments:
+
+ Inputs
+ BX 0Ah
+
+ Outputs
+ Nothing
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ CHECK_INTERRUPTS("VwIPXRelinquishControl");
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXRelinquishControl,
+ IPXDBG_LEVEL_INFO,
+ "VwIPXRelinquishControl\n"
+ ));
+
+ _VwIPXRelinquishControl();
+
+}
+
+
+VOID
+VwIPXScheduleAESEvent(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Schedules a an event to occur in some number of ticks. When the tick count
+ reaches 0, the ECB InUse field is cleared and any ESR called
+
+ This call is Asynchronous
+
+Arguments:
+
+ Inputs
+ BX 07h
+ AX Delay time - number of 1/18 second ticks
+ ES:SI ECB address
+
+ Outputs
+ Nothing
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPXECB pXecb = AES_ECB_PARM();
+ WORD ticks = IPX_TICKS_PARM();
+
+ CHECK_INTERRUPTS("VwIPXScheduleAESEvent");
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXScheduleAESEvent,
+ IPXDBG_LEVEL_INFO,
+ "VwIPXScheduleAESEvent(%04x:%04x, %04x)\n",
+ getES(),
+ getSI(),
+ ticks
+ ));
+
+ ScheduleEvent(pXecb, ticks);
+}
+
+
+VOID
+VwIPXScheduleIPXEvent(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Schedules a an event to occur in some number of ticks. When the tick count
+ reaches 0, the ECB InUse field is cleared and any ESR called
+
+ This call is Asynchronous
+
+Arguments:
+
+ Inputs
+ BX 05h
+ AX Delay time - number of 1/18 second ticks
+ ES:SI ECB address
+
+ Outputs
+ Nothing
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPECB pEcb;
+ WORD ticks = IPX_TICKS_PARM();
+
+ CHECK_INTERRUPTS("VwIPXScheduleIPXEvent");
+
+ IPX_GET_IPX_ECB(pEcb);
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXScheduleIPXEvent,
+ IPXDBG_LEVEL_INFO,
+ "VwIPXScheduleIPXEvent(%04x:%04x, %04x)\n",
+ getES(),
+ getSI(),
+ ticks
+ ));
+
+ _VwIPXScheduleIPXEvent( ticks, pEcb, ECB_PARM_ADDRESS() );
+
+}
+
+
+VOID
+VwIPXSendPacket(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Sends a packet to the target machine/router. This call can be made on a
+ socket that is not open
+
+ The app must have filled in the following IPX_ECB fields:
+
+ EsrAddress
+ Socket
+ ImmediateAddress
+ FragmentCount
+ fragment descriptor fields
+
+ and the following IPX_PACKET fields:
+
+ PacketType
+ Destination.Net
+ Destination.Node
+ Destination.Socket
+
+ This call is Asynchronous
+
+Arguments:
+
+ Inputs
+ BX 03h
+ CX DOS PDB. This parameter is not part of the IPX API.
+ Added because we need to remember which DOS executable owns the
+ socket IF WE MUST CREATE A TEMPORTARY SOCKET: we need to clean-up
+ short-lived sockets when the executable terminates
+ ES:SI ECB Address
+
+ Outputs
+ Nothing
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPECB pEcb;
+ WORD owner;
+
+ CHECK_INTERRUPTS("VwIPXSendPacket");
+
+ IPX_GET_IPX_ECB(pEcb);
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXSendPacket,
+ IPXDBG_LEVEL_INFO,
+ "VwIPXSendPacket(%04x:%04x), owner = %04x\n",
+ getES(),
+ getSI(),
+ IPX_SOCKET_OWNER_PARM()
+ ));
+
+ _VwIPXSendPacket(pEcb,
+ ECB_PARM_ADDRESS(),
+ IPX_SOCKET_OWNER_PARM()
+ );
+}
+
+
+VOID
+VwIPXSendWithChecksum(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ description-of-function.
+
+ This call is Asynchronous
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ CHECK_INTERRUPTS("VwIPXSendWithChecksum");
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXSendWithChecksum,
+ IPXDBG_LEVEL_INFO,
+ "VwIPXSendWithChecksum\n"
+ ));
+}
+
+
+VOID
+VwIPXSPXDeinit(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ description-of-function.
+
+ This call is Synchronous
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ CHECK_INTERRUPTS("VwIPXSPXDeinit");
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "VwIPXSPXDeinit\n"
+ ));
+}
+
+
+VOID
+VwIPXVerifyChecksum(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ description-of-function.
+
+ This call is Synchronous
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ CHECK_INTERRUPTS("VwIPXVerifyChecksum");
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXVerifyChecksum,
+ IPXDBG_LEVEL_INFO,
+ "VwIPXVerifyChecksum\n"
+ ));
+}
+
+
+VOID
+VwSPXAbortConnection(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Aborts this end of a connection
+
+ This call is Asynchronous
+
+Arguments:
+
+ Inputs
+ BX 14h
+ DX Connection ID
+
+ Outputs
+ Nothing
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ WORD connectionId = SPX_CONNECTION_PARM();
+
+ CHECK_INTERRUPTS("VwSPXAbortConnection");
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_SPXAbortConnection,
+ IPXDBG_LEVEL_INFO,
+ "VwSPXAbortConnection(%04x)\n",
+ connectionId
+ ));
+
+ _VwSPXAbortConnection(connectionId);
+}
+
+
+VOID
+VwSPXEstablishConnection(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Creates a connection with a remote SPX socket. The remote end can be on
+ this machine (i.e. same app in DOS world)
+
+ This call is Asynchronous
+
+Arguments:
+
+ Inputs
+ BX 11h
+ AL Retry count
+ AH WatchDog flag
+ ES:SI ECB Address
+
+ Outputs
+ AL Completion code:
+ 00h Attempting to talk to remote
+ EFh Local connection table full
+ FDh Fragment count not 1; buffer size not 42
+ FFh Send socket not open
+ DX Connection ID
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ WORD status;
+ BYTE retryCount = SPX_RETRY_COUNT_PARM();
+ BYTE watchDogFlag = SPX_WATCHDOG_FLAG_PARM();
+ WORD connectionId;
+ LPECB pEcb;
+
+ CHECK_INTERRUPTS("VwSPXEstablishConnection");
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_SPXEstablishConnection,
+ IPXDBG_LEVEL_INFO,
+ "VwSPXEstablishConnection(%02x, %02x, %04x:%04x)\n",
+ retryCount,
+ watchDogFlag,
+ ECB_PARM_SEGMENT(),
+ ECB_PARM_OFFSET()
+ ));
+
+ IPX_GET_IPX_ECB( pEcb );
+
+ IPXDUMPECB((pEcb, getES(), getSI(), ECB_TYPE_SPX, TRUE, TRUE, FALSE));
+
+ status = _VwSPXEstablishConnection( retryCount,
+ watchDogFlag,
+ &connectionId,
+ pEcb,
+ ECB_PARM_ADDRESS() );
+
+
+ SPX_SET_CONNECTION_ID( connectionId );
+ SPX_SET_STATUS( status );
+}
+
+
+VOID
+VwSPXGetConnectionStatus(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Returns buffer crammed full of useful statistics or something (hu hu huh)
+
+ This call is Synchronous
+
+Arguments:
+
+ Inputs
+ BX 15h
+ DX Connection ID
+ ES:SI Buffer address
+
+ Outputs
+ AL Completion code:
+ 00h Connection is active
+ EEh No such connection
+
+ on output, buffer in ES:SI contains:
+
+ BYTE ConnectionStatus
+ BYTE WatchDogActive
+ WORD LocalConnectionID
+ WORD RemoteConnectionID
+ WORD SequenceNumber
+ WORD LocalAckNumber
+ WORD LocalAllocationNumber
+ WORD RemoteAckNumber
+ WORD RemoteAllocationNumber
+ WORD LocalSocket
+ BYTE ImmediateAddress[6]
+ BYTE RemoteNetwork[4]
+ WORD RetransmissionCount
+ WORD RetransmittedPackets
+ WORD SuppressedPackets
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ WORD status;
+ WORD connectionId = SPX_CONNECTION_PARM();
+ LPSPX_CONNECTION_STATS pStats = (LPSPX_CONNECTION_STATS)SPX_BUFFER_PARM(sizeof(*pStats));
+
+ CHECK_INTERRUPTS("VwSPXGetConnectionStatus");
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_SPXGetConnectionStatus,
+ IPXDBG_LEVEL_INFO,
+ "VwSPXGetConnectionStatus: connectionId=%04x\n",
+ connectionId
+ ));
+
+ status = _VwSPXGetConnectionStatus( connectionId,
+ pStats );
+
+
+ SPX_SET_STATUS(status);
+}
+
+
+VOID
+VwSPXInitialize(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Informs the app that SPX is present on this station
+
+ This call is Synchronous
+
+Arguments:
+
+ Inputs
+ BX 10h
+ AL 00h
+
+ Outputs
+ AL Installation flag:
+ 00h Not installed
+ FFh Installed
+ BH SPX Major revision number
+ BL SPX Minor revision number
+ CX Maximum SPX connections supported
+ normally from SHELL.CFG
+ DX Available SPX connections
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ WORD status;
+ BYTE majorRevisionNumber;
+ BYTE minorRevisionNumber;
+ WORD maxConnections;
+ WORD availableConnections;
+
+ CHECK_INTERRUPTS("VwSPXInitialize");
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_SPXInitialize,
+ IPXDBG_LEVEL_INFO,
+ "VwSPXInitialize\n"
+ ));
+
+
+ status = _VwSPXInitialize( &majorRevisionNumber,
+ &minorRevisionNumber,
+ &maxConnections,
+ &availableConnections );
+
+
+ setBH( majorRevisionNumber );
+ setBL( minorRevisionNumber );
+ setCX( maxConnections );
+ setDX( availableConnections );
+ SPX_SET_STATUS(status);
+}
+
+
+VOID
+VwSPXListenForConnection(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Listens for an incoming connection request
+
+ This call is Asynchronous
+
+Arguments:
+
+ Inputs
+ BX 12h
+ AL Retry count
+ AH SPX WatchDog flag
+ ES:SI ECB Address
+
+ Outputs
+ Nothing
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ BYTE retryCount = SPX_RETRY_COUNT_PARM();
+ BYTE watchDogFlag = SPX_WATCHDOG_FLAG_PARM();
+ LPECB pEcb;
+
+ CHECK_INTERRUPTS("VwSPXListenForConnection");
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_SPXListenForConnection,
+ IPXDBG_LEVEL_INFO,
+ "VwSPXListenForConnection(%02x, %02x, %04x:%04x)\n",
+ retryCount,
+ watchDogFlag,
+ ECB_PARM_SEGMENT(),
+ ECB_PARM_OFFSET()
+ ));
+
+ IPX_GET_IPX_ECB( pEcb );
+
+ IPXDUMPECB((pEcb, getES(), getSI(), ECB_TYPE_SPX, TRUE, FALSE, FALSE));
+
+ _VwSPXListenForConnection( retryCount,
+ watchDogFlag,
+ pEcb,
+ ECB_PARM_ADDRESS() );
+}
+
+
+VOID
+VwSPXListenForSequencedPacket(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Attempts to receive an SPX packet. This call is made against the top-level
+ socket (the socket in SPX-speak, not the connection). We can receive a
+ packet from any connection assigned to this socket. In this function, we
+ just queue the ECB (since there is no return status, we expect that the
+ app has supplied an ESR) and let AES handle it
+
+ This call is Asynchronous
+
+Arguments:
+
+ Inputs
+ BX 17h
+ ES:SI ECB Address
+
+ Outputs
+ Nothing
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPECB pEcb;
+
+ CHECK_INTERRUPTS("VwSPXListenForSequencedPacket");
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_SPXListenForSequencedPacket,
+ IPXDBG_LEVEL_INFO,
+ "VwSPXListenForSequencedPacket(%04x:%04x)\n",
+ ECB_PARM_SEGMENT(),
+ ECB_PARM_OFFSET()
+ ));
+
+ IPX_GET_IPX_ECB( pEcb );
+
+ IPXDUMPECB((pEcb, getES(), getSI(), ECB_TYPE_SPX, TRUE, FALSE, FALSE));
+
+ _VwSPXListenForSequencedPacket( pEcb,
+ ECB_PARM_ADDRESS());
+
+}
+
+
+VOID
+VwSPXSendSequencedPacket(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Sends a packet on an SPX connection
+
+ This call is Asynchronous
+
+Arguments:
+
+ Inputs
+ BX 16h
+ DX Connection ID
+ ES:SI ECB address
+
+ Outputs
+ Nothing
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ WORD connectionId = SPX_CONNECTION_PARM();
+ LPECB pEcb;
+
+ CHECK_INTERRUPTS("VwSPXSendSequencedPacket""VwSPXSendSequencedPacket");
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_SPXSendSequencedPacket,
+ IPXDBG_LEVEL_INFO,
+ "VwSPXSendSequencedPacket(%04x, %04x:%04x)\n",
+ connectionId,
+ getES(),
+ getSI()
+ ));
+
+ IPX_GET_IPX_ECB( pEcb );
+
+ IPXDUMPECB((pEcb, getES(), getSI(), ECB_TYPE_SPX, TRUE, TRUE, FALSE));
+
+ _VwSPXSendSequencedPacket( connectionId,
+ pEcb,
+ ECB_PARM_ADDRESS() );
+
+}
+
+
+VOID
+VwSPXTerminateConnection(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Terminates a connection
+
+ This call is Asynchronous
+
+Arguments:
+
+ Inputs
+ BX 13h
+ DX Connection ID
+ ES:SI ECB Address
+
+ Outputs
+ Nothing
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ WORD connectionId = SPX_CONNECTION_PARM();
+ LPECB pEcb;
+
+ CHECK_INTERRUPTS("VwSPXTerminateConnection");
+
+ IPX_GET_IPX_ECB( pEcb );
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_SPXTerminateConnection,
+ IPXDBG_LEVEL_INFO,
+ "VwSPXTerminateConnection(%04x, %04x:%04x)\n",
+ connectionId,
+ ECB_PARM_SEGMENT(),
+ ECB_PARM_OFFSET()
+ ));
+
+ _VwSPXTerminateConnection(connectionId, pEcb, ECB_PARM_ADDRESS());
+}
diff --git a/private/nw/vwipxspx/dll/vwinapi.c b/private/nw/vwipxspx/dll/vwinapi.c
new file mode 100644
index 000000000..303be996c
--- /dev/null
+++ b/private/nw/vwipxspx/dll/vwinapi.c
@@ -0,0 +1,1185 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ vwinapi.c
+
+Abstract:
+
+ ntVdm netWare (Vw) IPX/SPX Functions
+
+ Contains handlers for WOW IPX/SPX calls (netware functions). The IPX/SPX
+ APIs use WinSock to perform the actual operations
+
+ Contents:
+ VWinIPXCancelEvent
+ VWinIPXCloseSocket
+ VWinIPXDisconnectFromTarget
+ VWinIPXGetInternetworkAddress
+ VWinIPXGetIntervalMarker
+ VWinIPXGetLocalTarget
+ VWinIPXGetLocalTargetAsync
+ VWinIPXGetMaxPacketSize
+ VWinIPXInitialize
+ VWinIPXListenForPacket
+ VWinIPXOpenSocket
+ VWinIPXRelinquishControl
+ VWinIPXScheduleIPXEvent
+ VWinIPXSendPacket
+ VWinIPXSPXDeinit
+
+ VWinSPXAbortConnection
+ VWinSPXEstablishConnection
+ VWinSPXGetConnectionStatus
+ VWinSPXInitialize
+ VWinSPXListenForConnection
+ VWinSPXListenForSequencedPacket
+ VWinSPXSendSequencedPacket
+ VWinSPXTerminateConnection
+
+Author:
+
+ Yi-Hsin Sung ( yihsins ) 28-Oct-1993
+
+Environment:
+
+ User-mode Win32
+
+Revision History:
+
+ 28-Oct-1993 yihsins
+ Created
+
+--*/
+
+#include "vw.h"
+#pragma hdrstop
+
+//
+// functions
+//
+
+
+WORD
+VWinIPXCancelEvent(
+ IN DWORD IPXTaskID,
+ IN LPECB pEcb
+ )
+
+/*++
+
+Routine Description:
+
+ Cancels event described by an ECB
+
+ This call is Synchronous
+
+Arguments:
+
+ Inputs
+ IPXTaskID
+ pECB
+
+Return Value:
+
+ 00h Success
+ F9h Can't cancel ECB
+ FFh ECB not in use
+
+--*/
+
+{
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXCancelEvent,
+ IPXDBG_LEVEL_INFO,
+ "VWinIPXCancelEvent\n"
+ ));
+
+ // ignore IPXTaskID for now
+ UNREFERENCED_PARAMETER( IPXTaskID );
+
+ return _VwIPXCancelEvent( pEcb );
+}
+
+
+VOID
+VWinIPXCloseSocket(
+ IN DWORD IPXTaskID,
+ IN WORD socketNumber
+ )
+
+/*++
+
+Routine Description:
+
+ Closes a socket and cancels any outstanding events on the socket.
+ Closing an unopened socket does not return an error
+ ESRs in cancelled ECBs are not called
+
+ This call is Synchronous
+
+Arguments:
+
+ Inputs
+ IPXTaskID
+ socketNumber
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXCloseSocket,
+ IPXDBG_LEVEL_INFO,
+ "VWinIPXCloseSocket(%#x)\n",
+ B2LW(socketNumber)
+ ));
+
+ // ignore IPXTaskID for now
+ UNREFERENCED_PARAMETER( IPXTaskID );
+
+ _VwIPXCloseSocket( socketNumber );
+}
+
+
+VOID
+VWinIPXDisconnectFromTarget(
+ IN DWORD IPXTaskID,
+ IN LPBYTE pNetworkAddress
+ )
+
+/*++
+
+Routine Description:
+
+ Performs no action for NTVDM IPX
+
+ This call is Synchronous
+
+Arguments:
+
+ Inputs
+ IPXTaskID
+ pNetworkAddress
+
+ Outputs
+ Nothing
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXDisconnectFromTarget,
+ IPXDBG_LEVEL_INFO,
+ "VWinIPXDisconnectFromTarget\n"
+ ));
+}
+
+
+VOID
+VWinIPXGetInternetworkAddress(
+ IN DWORD IPXTaskID,
+ OUT LPINTERNET_ADDRESS pNetworkAddress
+ )
+
+/*++
+
+Routine Description:
+
+ Returns a buffer containing the net number and node number for this
+ station.
+
+ This function cannot return an error (!)
+
+ Assumes: 1. GetInternetAddress has been successfully called in the
+ DLL initialization phase
+
+ This call is Synchronous
+
+Arguments:
+
+ Inputs
+ IPXTaskID
+
+ Outputs
+ pNetworkAddress
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXGetInternetworkAddress,
+ IPXDBG_LEVEL_INFO,
+ "VWinIPXGetInternetworkAddress\n"
+ ));
+
+ // ignore IPXTaskID for now
+ UNREFERENCED_PARAMETER( IPXTaskID );
+
+ _VwIPXGetInternetworkAddress( pNetworkAddress );
+
+}
+
+
+WORD
+VWinIPXGetIntervalMarker(
+ IN DWORD IPXTaskID
+ )
+
+/*++
+
+Routine Description:
+
+ Just returns the tick count maintained by Asynchronous Event Scheduler
+
+ This call is Synchronous
+
+Arguments:
+
+ Inputs
+ IPXTaskID
+
+ Outputs
+
+Return Value:
+
+ The tick count.
+
+--*/
+
+{
+ WORD intervalMarker = _VwIPXGetIntervalMarker();
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXGetIntervalMarker,
+ IPXDBG_LEVEL_INFO,
+ "VWinIPXGetIntervalMarker: Returning %04x\n",
+ intervalMarker
+ ));
+
+ // ignore IPXTaskID for now
+ UNREFERENCED_PARAMETER( IPXTaskID );
+
+ return intervalMarker;
+}
+
+
+WORD
+VWinIPXGetLocalTarget(
+ IN DWORD IPXTaskID,
+ IN LPBYTE pNetworkAddress,
+ OUT LPBYTE pImmediateAddress,
+ OUT ULPWORD pTransportTime
+ )
+
+/*++
+
+Routine Description:
+
+ Given a target address of the form (network address {4}, node address {6}),
+ returns the node address of the target if on the same network, or the node
+ address of the router which knows how to get to the next hop in reaching the
+ eventual target
+
+ This call is Synchronous
+
+Arguments:
+
+ Inputs
+ IPXTaskID
+ pNetworkAddress
+
+ Outputs
+ pImmediateAddress
+ pTransportTime
+
+
+Return Value:
+
+ 00h Success
+ F1h Ipx/Spx Not Initialized
+ FAh No path to destination node found
+
+--*/
+
+{
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXGetLocalTarget,
+ IPXDBG_LEVEL_INFO,
+ "VWinIPXGetLocalTarget\n"
+ ));
+
+ // ignore IPXTaskID for now
+ UNREFERENCED_PARAMETER( IPXTaskID );
+
+ return _VwIPXGetLocalTarget( pNetworkAddress,
+ pImmediateAddress,
+ pTransportTime );
+}
+
+
+WORD
+VWinIPXGetLocalTargetAsync(
+ IN LPBYTE pSendAGLT,
+ OUT LPBYTE pListenAGLT,
+ IN WORD windowsHandle
+ )
+
+/*++
+
+Routine Description:
+
+ description-of-function.
+
+ This call is Asynchronous
+
+Arguments:
+
+ pSendAGLT
+ pListenAGLT
+ windowsHandle
+
+Return Value:
+
+ 00h Success
+ F1h Ipx/Spx Not Initialized
+ FAh No Local Target Identified
+
+--*/
+
+{
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "VWinIPXGetLocalTargetAsync\n"
+ ));
+
+ return IPX_SUCCESS; // return success for now
+}
+
+
+WORD
+VWinIPXGetMaxPacketSize(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Returns the maximum packet size the underlying network can handle
+
+ Assumes: 1. A successfull call to GetMaxPacketSize has been made during
+ DLL initialization
+ 2. Maximum packet size is constant
+
+ This call is Synchronous
+
+Arguments:
+
+ Inputs
+ None.
+
+Return Value:
+
+ The max packet size.
+
+--*/
+
+{
+ //
+ // this is a WORD function in DOS and Windows: always return MaxPacketSize
+ // in AX
+ //
+
+ WORD maxPacketSize = _VwIPXGetMaxPacketSize( NULL );
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXGetMaxPacketSize,
+ IPXDBG_LEVEL_INFO,
+ "VWinIPXGetMaxPacketSize: PacketSize=%d\n",
+ maxPacketSize
+ ));
+
+ return maxPacketSize;
+}
+
+
+WORD
+VWinIPXInitialize(
+ IN OUT ULPDWORD pIPXTaskID,
+ IN WORD maxECBs,
+ IN WORD maxPacketSize
+ )
+
+/*++
+
+Routine Description:
+
+ Get the entry address for the IPX Interface.
+
+Arguments:
+
+ Inputs
+ maxECBs
+ maxPacketSize
+
+ Output
+ pIPXTaskID
+
+Return Value:
+
+ 00h Success
+ F0h Ipx NotInstalled
+ F1h Ipx/Spx Not Initialized
+ F2h No Dos Memory
+ F3h No Free Ecb
+ F4h Lock Failed
+ F5h Over the maximum limit
+ F6h Ipx/Spx Previously Initialized
+
+--*/
+
+{
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "VWinIPXInitialize (MaxECBs=%04x, MaxPacketSize=%04x)\n",
+ maxECBs,
+ maxPacketSize
+ ));
+
+ UNREFERENCED_PARAMETER( maxECBs ); // ignore for now
+ UNREFERENCED_PARAMETER( maxPacketSize ); // ignore for now
+
+// *pIPXTaskID = 0; // BUGBUG Can we ignore this?
+
+ return IPX_SUCCESS;
+}
+
+
+VOID
+VWinIPXListenForPacket(
+ IN DWORD IPXTaskID,
+ IN LPECB pEcb,
+ IN ECB_ADDRESS EcbAddress
+ )
+
+/*++
+
+Routine Description:
+
+ Queue a listen request against a socket. All listen requests will be
+ completed asynchronously, unless cancelled by app
+
+ This call is Asynchronous
+
+Arguments:
+
+ Inputs
+ IPXTaskID
+ pEcb
+ EcbAddress
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXListenForPacket,
+ IPXDBG_LEVEL_INFO,
+ "VWinIPXListenForPacket(%04x:%04x)\n",
+ HIWORD(EcbAddress),
+ LOWORD(EcbAddress)
+ ));
+
+ // ignore IPXTaskID for now
+ UNREFERENCED_PARAMETER( IPXTaskID );
+
+ (VOID) _VwIPXListenForPacket( pEcb, EcbAddress );
+}
+
+
+WORD
+VWinIPXOpenSocket(
+ IN DWORD IPXTaskID,
+ IN OUT ULPWORD pSocketNumber,
+ IN BYTE socketType
+ )
+
+/*++
+
+Routine Description:
+
+ Opens a socket for use by IPX or SPX.Puts the socket into non-blocking mode.
+ The socket will be bound to IPX.
+
+ This call is Synchronous
+
+Arguments:
+
+ Inputs
+ IPXTaskID
+ *pSocketNumber
+ socketType - Socket Longevity flag
+
+ Outputs
+ pSocketNumber - Assigned socket number
+
+Return Value:
+
+ 00h Success
+ F0h Ipx Not Installed
+ F1h Ipx/Spx Not Initialized
+ FEh Socket table full
+ FFh Socket already open
+
+--*/
+
+{
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXOpenSocket,
+ IPXDBG_LEVEL_INFO,
+ "VwIPXOpenSocket(Life=%02x, Socket=%04x)\n",
+ socketType,
+ B2LW(*pSocketNumber)
+ ));
+
+ // ignore IPXTaskID for now
+ UNREFERENCED_PARAMETER( IPXTaskID );
+
+ return _VwIPXOpenSocket( pSocketNumber,
+ socketType,
+ 0 );
+
+}
+
+
+VOID
+VWinIPXRelinquishControl(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Just sleep for a nominal amount. Netware seems to be dependent on the
+ default setting of the PC clock, so one timer tick (1/18 second) would
+ seem to be a good value
+
+ This call is Synchronous
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXRelinquishControl,
+ IPXDBG_LEVEL_INFO,
+ "VWinIPXRelinquishControl\n"
+ ));
+
+ _VwIPXRelinquishControl();
+}
+
+
+VOID
+VWinIPXScheduleIPXEvent(
+ IN DWORD IPXTaskID,
+ IN WORD time,
+ IN LPECB pEcb,
+ IN ECB_ADDRESS EcbAddress
+ )
+
+/*++
+
+Routine Description:
+
+ Schedules a an event to occur in some number of ticks. When the tick count
+ reaches 0, the ECB InUse field is cleared and any ESR called
+
+ This call is Asynchronous
+
+Arguments:
+
+ Inputs
+ IPXTaskID
+ time
+ pEcb
+ EcbAddress
+
+ Outputs
+ Nothing
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXScheduleIPXEvent,
+ IPXDBG_LEVEL_INFO,
+ "VWinIPXScheduleIPXEvent(%04x:%04x, Time:%04x)\n",
+ HIWORD( EcbAddress ),
+ LOWORD( EcbAddress ),
+ time
+ ));
+
+ // ignore IPXTaskID for now
+ UNREFERENCED_PARAMETER( IPXTaskID );
+
+ _VwIPXScheduleIPXEvent( time, pEcb, EcbAddress );
+}
+
+
+VOID
+VWinIPXSendPacket(
+ IN DWORD IPXTaskID,
+ IN LPECB pEcb,
+ IN ECB_ADDRESS EcbAddress
+ )
+
+/*++
+
+Routine Description:
+
+ Sends a packet to the target machine/router. This call can be made on a
+ socket that is not open
+
+ The app must have filled in the following IPX_ECB fields:
+
+ EsrAddress
+ Socket
+ ImmediateAddress
+ FragmentCount
+ fragment descriptor fields
+
+ and the following IPX_PACKET fields:
+
+ PacketType
+ Destination.Net
+ Destination.Node
+ Destination.Socket
+
+ This call is Asynchronous
+
+Arguments:
+
+ Inputs
+ IPXTaskID
+ pEcb
+ EcbAddress
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXSendPacket,
+ IPXDBG_LEVEL_INFO,
+ "VWinIPXSendPacket(%04x:%04x)\n",
+ HIWORD( EcbAddress ),
+ LOWORD( EcbAddress )
+ ));
+
+ // ignore IPXTaskID for now
+ UNREFERENCED_PARAMETER( IPXTaskID );
+
+ _VwIPXSendPacket( pEcb, EcbAddress, 0);
+}
+
+
+WORD
+VWinIPXSPXDeinit(
+ IN DWORD IPXTaskID
+ )
+
+/*++
+
+Routine Description:
+
+ Release any resources allocated to an application by NWIPXSPX.DLL
+ for use by other applications.
+
+ This call is Synchronous
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ 00h Successful
+ F1h IPX/SPX Not Initialized
+
+--*/
+
+{
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "VwIPXSPXDeinit\n"
+ ));
+
+ // ignore IPXTaskID for now
+ UNREFERENCED_PARAMETER( IPXTaskID );
+ return IPX_SUCCESS;
+}
+
+
+VOID
+VWinSPXAbortConnection(
+ IN WORD SPXConnectionID
+ )
+
+/*++
+
+Routine Description:
+
+ Abort an SPX connection.
+
+ This call is Synchronous
+
+Arguments:
+
+ SPXConnectionID
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_SPXAbortConnection,
+ IPXDBG_LEVEL_INFO,
+ "VWinSPXAbortConnection(%04x)\n",
+ SPXConnectionID
+ ));
+
+ _VwSPXAbortConnection(SPXConnectionID);
+}
+
+
+WORD
+VWinSPXEstablishConnection(
+ IN DWORD IPXTaskID,
+ IN BYTE retryCount,
+ IN BYTE watchDog,
+ OUT ULPWORD pSPXConnectionID,
+ IN LPECB pEcb,
+ IN ECB_ADDRESS EcbAddress
+ )
+
+/*++
+
+Routine Description:
+
+ Establish a connection with a listening socket.
+
+ This call is Synchronous
+
+Arguments:
+
+ Inputs
+ IPXTaskID
+ retryCount
+ watchDog
+ pEcb
+ EcbAddress
+
+ Outputs
+ pSPXConnectionID
+ pEcb
+
+Return Value:
+
+ 00h Success
+ EFh Connection Table Full
+ F1h IPX/SPX Not Initialized
+ FDh Malformed Packet
+ FFh Socket Not Opened
+
+--*/
+
+{
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_SPXEstablishConnection,
+ IPXDBG_LEVEL_INFO,
+ "VWinSPXEstablishConnection(%02x, %02x, %04x:%04x)\n",
+ retryCount,
+ watchDog,
+ HIWORD(EcbAddress),
+ LOWORD(EcbAddress)
+ ));
+
+ // ignore IPXTaskID for now
+ UNREFERENCED_PARAMETER( IPXTaskID );
+
+ return _VwSPXEstablishConnection( retryCount,
+ watchDog,
+ pSPXConnectionID,
+ pEcb,
+ EcbAddress );
+}
+
+
+WORD
+VWinSPXGetConnectionStatus(
+ IN DWORD IPXTaskID,
+ IN WORD SPXConnectionID,
+ IN LPSPX_CONNECTION_STATS pConnectionStats
+ )
+
+/*++
+
+Routine Description:
+
+ Return the status of an SPX connection.
+
+ This call is Synchronous
+
+Arguments:
+
+ Inputs
+ IPXTaskID
+ SPXConnectionID
+
+ Outputs
+ pConnectionStats
+
+Return Value:
+
+ 00h Success
+ EEh Invalid Connection
+ F1h IPX/SPX Not Initialized
+
+--*/
+
+{
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_SPXGetConnectionStatus,
+ IPXDBG_LEVEL_INFO,
+ "VWinSPXGetConnectionStatus\n"
+ ));
+
+ // ignore IPXTaskID for now
+ UNREFERENCED_PARAMETER( IPXTaskID );
+
+ return _VwSPXGetConnectionStatus( SPXConnectionID,
+ pConnectionStats );
+}
+
+
+WORD
+VWinSPXInitialize(
+ IN OUT ULPDWORD pIPXTaskID,
+ IN WORD maxECBs,
+ IN WORD maxPacketSize,
+ OUT LPBYTE pMajorRevisionNumber,
+ OUT LPBYTE pMinorRevisionNumber,
+ OUT ULPWORD pMaxConnections,
+ OUT ULPWORD pAvailableConnections
+ )
+
+/*++
+
+Routine Description:
+
+ Informs the app that SPX is present on this station
+
+ This call is Synchronous
+
+Arguments:
+
+ pIPXTaskID - on input, specifies how resources will be
+ allocated:
+
+ 0x00000000 - directly to calling application
+ 0xFFFFFFFE - directly to calling application,
+ but multiple initializations are
+ allowed
+ 0xFFFFFFFF - resources allocated in a pool for
+ multiple applications
+ maxECBs - maximum number of outstanding ECBs
+ maxPacketSize - maximum packet size to be sent by the app
+ pMajorRevisionNumber - returned SPX major version #
+ pMinorRevisionNumber - returned SPX minor version #
+ pMaxConnections - maximum connections supported by this SPX version
+ pAvailableConnections - number of connections available to this app
+
+Return Value:
+
+ WORD
+ 0x0000 SPX not installed
+ 0x00F1 IPX/SPX not installed
+ 0x00F2 no DOS memory
+ 0x00F3 no free ECBs
+ 0x00F4 lock failed
+ 0x00F5 exceeded maximum limit
+ 0x00F6 IPX/SPX already initialized
+ 0x00FF SPX installed
+
+--*/
+
+{
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_SPXInitialize,
+ IPXDBG_LEVEL_INFO,
+ "VWinSPXInitialize\n"
+ ));
+
+ UNREFERENCED_PARAMETER( maxECBs ); // ignore for now
+ UNREFERENCED_PARAMETER( maxPacketSize ); // ignore for now
+
+ //
+ // do the same thing as 16-bit windows and return the task ID unchanged
+ //
+
+// *pIPXTaskID = 0;
+
+ return _VwSPXInitialize( pMajorRevisionNumber,
+ pMinorRevisionNumber,
+ pMaxConnections,
+ pAvailableConnections );
+}
+
+
+VOID
+VWinSPXListenForConnection(
+ IN DWORD IPXTaskID,
+ IN BYTE retryCount,
+ IN BYTE watchDog,
+ IN LPECB pEcb,
+ IN ECB_ADDRESS EcbAddress
+ )
+
+/*++
+
+Routine Description:
+
+ Listens for an incoming connection request
+
+ This call is Asynchronous
+
+Arguments:
+
+ Inputs
+ IPXTaskID
+ retryCount
+ watchDogFlag
+ pEcb
+ EcbAddress
+
+ Outputs
+ Nothing
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_SPXListenForConnection,
+ IPXDBG_LEVEL_INFO,
+ "VWinSPXListenForConnection(%02x, %02x, %04x:%04x)\n",
+ retryCount,
+ watchDog,
+ HIWORD(EcbAddress),
+ LOWORD(EcbAddress)
+ ));
+
+ // ignore IPXTaskID for now
+ UNREFERENCED_PARAMETER( IPXTaskID );
+
+ _VwSPXListenForConnection( retryCount,
+ watchDog,
+ pEcb,
+ EcbAddress );
+}
+
+
+VOID
+VWinSPXListenForSequencedPacket(
+ IN DWORD IPXTaskID,
+ IN LPECB pEcb,
+ IN ECB_ADDRESS EcbAddress
+ )
+
+/*++
+
+Routine Description:
+
+ Attempts to receive an SPX packet.
+
+ This call is Asynchronous
+
+Arguments:
+
+ Inputs
+ IPXTaskID
+ pEcb
+ EcbAddress
+
+ Outputs
+ Nothing
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_SPXListenForSequencedPacket,
+ IPXDBG_LEVEL_INFO,
+ "VWinSPXListenForSequencedPacket(%04x:%04x)\n",
+ HIWORD(EcbAddress),
+ LOWORD(EcbAddress)
+ ));
+
+ // ignore IPXTaskID for now
+ UNREFERENCED_PARAMETER( IPXTaskID );
+
+ _VwSPXListenForSequencedPacket( pEcb,
+ EcbAddress );
+}
+
+
+VOID
+VWinSPXSendSequencedPacket(
+ IN DWORD IPXTaskID,
+ IN WORD SPXConnectionID,
+ IN LPECB pEcb,
+ IN ECB_ADDRESS EcbAddress
+ )
+
+/*++
+
+Routine Description:
+
+ Sends a packet on an SPX connection
+
+ This call is Asynchronous
+
+Arguments:
+
+ Inputs
+ IPXTaskID
+ SPXConnectionID
+ pEcb
+ EcbAddress
+
+ Outputs
+ Nothing
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_SPXSendSequencedPacket,
+ IPXDBG_LEVEL_INFO,
+ "VWinSPXSendSequencedPacket(%04x, %04x:%04x)\n",
+ SPXConnectionID,
+ HIWORD(EcbAddress),
+ LOWORD(EcbAddress)
+ ));
+
+ // ignore IPXTaskID for now
+ UNREFERENCED_PARAMETER( IPXTaskID );
+
+ _VwSPXSendSequencedPacket( SPXConnectionID,
+ pEcb,
+ EcbAddress );
+}
+
+
+VOID
+VWinSPXTerminateConnection(
+ IN DWORD IPXTaskID,
+ IN WORD SPXConnectionID,
+ IN LPECB pEcb,
+ IN ECB_ADDRESS EcbAddress
+ )
+
+/*++
+
+Routine Description:
+
+ Terminate an SPX connection by passing a connection ID and an
+ ECB address to SPX. Then return control to the calling application.
+
+ This call is Asynchronous
+
+Arguments:
+
+ Inputs
+ IPXTaskID
+ SPXConnectionID
+ pEcb
+ EcbAddress
+
+ Outputs
+ Nothing
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_SPXTerminateConnection,
+ IPXDBG_LEVEL_INFO,
+ "VWinSPXTerminateConnection(%04x, %04x:%04x)\n",
+ SPXConnectionID,
+ HIWORD(EcbAddress),
+ LOWORD(EcbAddress)
+ ));
+
+ // ignore IPXTaskID for now
+
+ UNREFERENCED_PARAMETER( IPXTaskID );
+
+ _VwSPXTerminateConnection(SPXConnectionID, pEcb, EcbAddress);
+}
diff --git a/private/nw/vwipxspx/dll/vwinapi.h b/private/nw/vwipxspx/dll/vwinapi.h
new file mode 100644
index 000000000..d706d7c87
--- /dev/null
+++ b/private/nw/vwipxspx/dll/vwinapi.h
@@ -0,0 +1,186 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ vwinapi.h
+
+Abstract:
+
+ Contains function prototypes for WIN IPX/SPX functions
+
+Author:
+
+
+Environment:
+
+ User-mode Win32
+
+Revision History:
+
+ 28-Oct-1993 yihsins
+ Created
+
+--*/
+
+WORD
+VWinIPXCancelEvent(
+ IN DWORD IPXTaskID,
+ IN LPECB pEcb
+ );
+
+VOID
+VWinIPXCloseSocket(
+ IN DWORD IPXTaskID,
+ IN WORD socketNumber
+ );
+
+VOID
+VWinIPXDisconnectFromTarget(
+ IN DWORD IPXTaskID,
+ OUT LPBYTE pNetworkAddress
+ );
+
+VOID
+VWinIPXGetInternetworkAddress(
+ IN DWORD IPXTaskID,
+ OUT LPINTERNET_ADDRESS pNetworkAddress
+ );
+
+WORD
+VWinIPXGetIntervalMarker(
+ IN DWORD IPXTaskID
+ );
+
+WORD
+VWinIPXGetLocalTarget(
+ IN DWORD IPXTaskID,
+ IN LPBYTE pNetworkAddress,
+ OUT LPBYTE pImmediateAddress,
+ OUT ULPWORD pTransportTime
+ );
+
+WORD
+VWinIPXGetLocalTargetAsync(
+ IN LPBYTE pSendAGLT,
+ OUT LPBYTE pListenAGLT,
+ IN WORD windowsHandle
+ );
+
+WORD
+VWinIPXGetMaxPacketSize(
+ VOID
+ );
+
+WORD
+VWinIPXInitialize(
+ IN OUT ULPDWORD pIPXTaskID,
+ IN WORD maxECBs,
+ IN WORD maxPacketSize
+ );
+
+VOID
+VWinIPXListenForPacket(
+ DWORD IPXTaskID,
+ LPECB pEcb,
+ ECB_ADDRESS EcbAddress
+ );
+
+WORD
+VWinIPXOpenSocket(
+ IN DWORD IPXTaskID,
+ IN OUT ULPWORD pSocketNumber,
+ IN BYTE socketType
+ );
+
+VOID
+VWinIPXRelinquishControl(
+ VOID
+ );
+
+VOID
+VWinIPXScheduleIPXEvent(
+ IN DWORD IPXTaskID,
+ IN WORD time,
+ IN LPECB pEcb,
+ IN ECB_ADDRESS EcbAddress
+ );
+
+VOID
+VWinIPXSendPacket(
+ IN DWORD IPXTaskID,
+ IN LPECB pEcb,
+ IN ECB_ADDRESS EcbAddress
+ );
+
+WORD
+VWinIPXSPXDeinit(
+ IN DWORD IPXTaskID
+ );
+
+VOID
+VWinSPXAbortConnection(
+ IN WORD SPXConnectionID
+ );
+
+WORD
+VWinSPXEstablishConnection(
+ IN DWORD IPXTaskID,
+ IN BYTE retryCount,
+ IN BYTE watchDog,
+ OUT ULPWORD pSPXConnectionID,
+ IN LPECB pEcb,
+ IN ECB_ADDRESS EcbAddress
+ );
+
+WORD
+VWinSPXGetConnectionStatus(
+ IN DWORD IPXTaskID,
+ IN WORD SPXConnectionID,
+ IN LPSPX_CONNECTION_STATS pConnectionStats
+ );
+
+WORD
+VWinSPXInitialize(
+ IN OUT DWORD UNALIGNED* pIPXTaskID,
+ IN WORD maxECBs,
+ IN WORD maxPacketSize,
+ OUT LPBYTE pMajorRevisionNumber,
+ OUT LPBYTE pMinorRevisionNumber,
+ OUT WORD UNALIGNED* pMaxConnections,
+ OUT WORD UNALIGNED* pAvailableConnections
+ );
+
+VOID
+VWinSPXListenForConnection(
+ IN DWORD IPXTaskID,
+ IN BYTE retryCount,
+ IN BYTE watchDog,
+ IN LPECB pEcb,
+ IN ECB_ADDRESS EcbAddress
+ );
+
+
+VOID
+VWinSPXListenForSequencedPacket(
+ IN DWORD IPXTaskID,
+ IN LPECB pEcb,
+ IN ECB_ADDRESS EcbAddress
+ );
+
+VOID
+VWinSPXSendSequencedPacket(
+ IN DWORD IPXTaskID,
+ IN WORD SPXConnectionID,
+ IN LPECB pEcb,
+ IN ECB_ADDRESS EcbAddress
+ );
+
+VOID
+VWinSPXTerminateConnection(
+ IN DWORD IPXTaskID,
+ IN WORD SPXConnectionID,
+ IN LPECB pEcb,
+ IN ECB_ADDRESS EcbAddress
+ );
diff --git a/private/nw/vwipxspx/dll/vwint.h b/private/nw/vwipxspx/dll/vwint.h
new file mode 100644
index 000000000..25ba92631
--- /dev/null
+++ b/private/nw/vwipxspx/dll/vwint.h
@@ -0,0 +1,146 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ vwint.h
+
+Abstract:
+
+ Contains internal function prototypes used by DOS/WIN IPX/SPX functions
+
+Author:
+
+ Yi-Hsin Sung (yihsins) 28-Oct-1993
+
+Environment:
+
+ User-mode Win32
+
+Revision History:
+
+ 28-Oct-1993 yihsins
+ Created
+
+--*/
+
+WORD
+_VwIPXCancelEvent(
+ IN LPECB pEcb
+ );
+
+VOID
+_VwIPXCloseSocket(
+ IN WORD SocketNumber
+ );
+
+VOID
+_VwIPXGetInternetworkAddress(
+ OUT LPINTERNET_ADDRESS pNetworkAddress
+ );
+
+WORD
+_VwIPXGetIntervalMarker(
+ VOID
+ );
+
+WORD
+_VwIPXGetLocalTarget(
+ IN LPBYTE pNetworkAddress,
+ OUT LPBYTE pImmediateAddress,
+ OUT ULPWORD pTransportTime
+ );
+
+WORD
+_VwIPXGetMaxPacketSize(
+ OUT ULPWORD pRetryCount
+ );
+
+WORD
+_VwIPXListenForPacket(
+ IN OUT LPECB pEcb,
+ IN ECB_ADDRESS EcbAddress
+ );
+
+WORD
+_VwIPXOpenSocket(
+ IN OUT ULPWORD pSocketNumber,
+ IN BYTE SocketType,
+ IN WORD DosPDB
+ );
+
+VOID
+_VwIPXRelinquishControl(
+ VOID
+ );
+
+VOID
+_VwIPXScheduleIPXEvent(
+ IN WORD Time,
+ IN LPECB pEcb,
+ IN ECB_ADDRESS EcbAddress
+ );
+
+VOID
+_VwIPXSendPacket(
+ IN LPECB pEcb,
+ IN ECB_ADDRESS EcbAddress,
+ IN WORD DosPDB
+ );
+
+VOID
+_VwSPXAbortConnection(
+ IN WORD SPXConnectionID
+ );
+
+WORD
+_VwSPXEstablishConnection(
+ IN BYTE RetryCount,
+ IN BYTE WatchDog,
+ OUT ULPWORD pSPXConnectionID,
+ IN LPECB pEcb,
+ IN ECB_ADDRESS EcbAddress
+ );
+
+WORD
+_VwSPXGetConnectionStatus(
+ IN WORD SPXConnectionID,
+ OUT LPSPX_CONNECTION_STATS pStats
+ );
+
+WORD
+_VwSPXInitialize(
+ OUT ULPBYTE pMajorRevisionNumber,
+ OUT ULPBYTE pMinorRevisionNumber,
+ OUT ULPWORD pMaxConnections,
+ OUT ULPWORD pAvailableConnections
+ );
+
+VOID
+_VwSPXListenForConnection(
+ IN BYTE RetryCount,
+ IN BYTE WatchDog,
+ IN LPECB pEcb,
+ IN ECB_ADDRESS EcbAddress
+ );
+
+VOID
+_VwSPXListenForSequencedPacket(
+ IN LPECB pEcb,
+ IN ECB_ADDRESS EcbAddress
+ );
+
+VOID
+_VwSPXSendSequencedPacket(
+ IN WORD SPXConnectionID,
+ IN LPECB pEcb,
+ IN ECB_ADDRESS EcbAddress
+ );
+
+VOID
+_VwSPXTerminateConnection(
+ IN WORD SPXConnectionID,
+ IN LPECB pEcb,
+ IN ECB_ADDRESS EcbAddress
+ );
diff --git a/private/nw/vwipxspx/dll/vwipx.c b/private/nw/vwipxspx/dll/vwipx.c
new file mode 100644
index 000000000..64ddcaf34
--- /dev/null
+++ b/private/nw/vwipxspx/dll/vwipx.c
@@ -0,0 +1,839 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ vwipx.c
+
+Abstract:
+
+ ntVdm netWare (Vw) IPX/SPX Functions
+
+ Vw: The peoples' network
+
+ Internal worker routines for DOS/WOW IPX calls (netware functions).
+ The IPX APIs use WinSock to perform the actual operations
+
+ Contents:
+ _VwIPXCancelEvent
+ _VwIPXCloseSocket
+ _VwIPXGetInternetworkAddress
+ _VwIPXGetIntervalMarker
+ _VwIPXGetLocalTarget
+ _VwIPXGetMaxPacketSize
+ _VwIPXListenForPacket
+ _VwIPXOpenSocket
+ _VwIPXRelinquishControl
+ _VwIPXScheduleIPXEvent
+ _VwIPXSendPacket
+
+Author:
+
+ Richard L Firth (rfirth) 30-Sep-1993
+
+Environment:
+
+ User-mode Win32
+
+Revision History:
+
+ 30-Sep-1993 rfirth
+ Created
+
+--*/
+
+#include "vw.h"
+#pragma hdrstop
+
+extern WORD AesTickCount;
+
+//
+// functions
+//
+
+
+WORD
+_VwIPXCancelEvent(
+ IN LPECB pEcb
+ )
+
+/*++
+
+Routine Description:
+
+ Internal routine shared by DOS and WIN that cancels event
+ described by an ECB
+
+ This call is Synchronous
+
+Arguments:
+
+ Inputs
+ pECB
+
+Return Value:
+
+ 00h Success
+ F9h Can't cancel ECB
+ FFh ECB not in use
+
+--*/
+
+{
+ LPXECB pXecb;
+ WORD status;
+
+ //
+ // BUGBUG: find out what real IPX does if given a NULL pointer
+ //
+
+ if (!pEcb) {
+ return IPX_ECB_NOT_IN_USE;
+ }
+
+ //
+ // BUGBUG: if this ECB references an SPX action then return 0xF9
+ //
+
+ //
+ // if the ECB is still in the state we left it then LinkAddress will be the
+ // address of the XECB which subsequently points back to the ECB. If both
+ // these pan out then we have an ECB which we have at least seen before.
+ // Maybe we can cancel it?
+ //
+ // Note: we grab the serialization semaphore here just in case the AES thread
+ // is about to complete the ECB
+ //
+
+ status = IPX_CANNOT_CANCEL;
+ RequestMutex();
+ pXecb = (LPXECB)pEcb->LinkAddress;
+ if (pXecb) {
+ try {
+ if (pXecb->Ecb == pEcb) {
+ status = IPX_SUCCESS;
+
+ //
+ // pXecb ok: increase reference count in case other thread tries
+ // to deallocate it while we're trying to cancel it
+ //
+
+ ++pXecb->RefCount;
+ }
+ } except(1) {
+
+ //
+ // bad pointer: bogus ECB
+ //
+
+ }
+ } else {
+
+ //
+ // NULL pointer: event probably completed already
+ //
+
+ status = IPX_ECB_NOT_IN_USE;
+ }
+ ReleaseMutex();
+ if (status == IPX_SUCCESS) {
+
+ ECB_CANCEL_ROUTINE cancelRoutine;
+
+ //
+ // we have an ECB to cancel. If we still have it, it will be on one of
+ // the socket queues, the timer list or the async completion list. If
+ // the latter we are in a race. Treat such events as already happened.
+ // We will cancel events on the timer list and queued send and receive
+ // events only
+ //
+
+ switch (pXecb->QueueId) {
+ case NO_QUEUE:
+ status = ECB_CC_CANCELLED;
+ goto cancel_exit;
+
+ case ASYNC_COMPLETION_QUEUE:
+ cancelRoutine = CancelAsyncEvent;
+ break;
+
+ case TIMER_QUEUE:
+ cancelRoutine = CancelTimerEvent;
+ break;
+
+ case SOCKET_LISTEN_QUEUE:
+ case SOCKET_SEND_QUEUE:
+ cancelRoutine = CancelSocketEvent;
+ break;
+
+ case CONNECTION_CONNECT_QUEUE:
+ case CONNECTION_SEND_QUEUE:
+
+ //
+ // SPXEstablishConnection and SPXSendSequencedPacket cannot be
+ // cancelled using IPXCancelEvent
+ //
+
+ status = ECB_CC_CANNOT_CANCEL;
+ goto cancel_exit;
+
+ case CONNECTION_ACCEPT_QUEUE:
+ case CONNECTION_LISTEN_QUEUE:
+ cancelRoutine = CancelConnectionEvent;
+ break;
+ }
+ return cancelRoutine(pXecb);
+ }
+
+ //
+ // app tried to sneak us an unknown ECB, -OR- the ECB was stomped on,
+ // destroying the LinkAddress and hence the address of the XECB. We
+ // could search the various lists looking for an XECB whose Ecb field
+ // matches pEcb, but if the app has scribbled over the ECB when we
+ // (make that Novell) told it not to, chances are it would fail real
+ // well on DOS. Probable worst case is that the app is terminating and
+ // the ECB may sometime later call an ESR which won't be there. Crashola
+ //
+
+cancel_exit:
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXCancelEvent,
+ IPXDBG_LEVEL_ERROR,
+ "VwIPXCancelEvent: cannot find/cancel ECB %04x:%04x\n",
+ HIWORD(pEcb),
+ LOWORD(pEcb)
+ ));
+
+ pEcb->CompletionCode = (BYTE)status;
+ pEcb->InUse = ECB_IU_NOT_IN_USE;
+ return status;
+}
+
+
+VOID
+_VwIPXCloseSocket(
+ IN WORD socketNumber
+ )
+
+/*++
+
+Routine Description:
+
+ Closes a socket and cancels any outstanding events on the socket.
+ Closing an unopened socket does not return an error
+ ESRs in cancelled ECBs are not called
+
+ This call is Synchronous
+
+Arguments:
+
+ Inputs
+ socketNumber
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPSOCKET_INFO pSocketInfo;
+
+ pSocketInfo = FindSocket(socketNumber);
+ if (pSocketInfo != NULL) {
+ KillSocket(pSocketInfo);
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXCloseSocket,
+ IPXDBG_LEVEL_WARNING,
+ "_VwIPXCloseSocket: can't locate socket 0x%04x\n",
+ B2LW(socketNumber)
+ ));
+
+ }
+}
+
+
+VOID
+_VwIPXGetInternetworkAddress(
+ IN LPINTERNET_ADDRESS pNetworkAddress
+ )
+
+/*++
+
+Routine Description:
+
+ Returns a buffer containing the net number and node number for this
+ station.
+
+ This function cannot return an error (!)
+
+ Assumes: 1. GetInternetAddress has been successfully called in the
+ DLL initialization phase
+
+ This call is Synchronous
+
+Arguments:
+
+ Inputs
+ Nothing.
+
+ Outputs
+ pNetworkAddress
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ CopyMemory((LPBYTE)pNetworkAddress,
+ (LPBYTE)&MyInternetAddress.sa_netnum,
+ sizeof(*pNetworkAddress)
+ );
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXGetInternetworkAddress,
+ IPXDBG_LEVEL_INFO,
+ "VwIPXGetInternetworkAddress: %02x-%02x-%02x-%02x : %02x-%02x-%02x-%02x-%02x-%02x\n",
+ pNetworkAddress->Net[0] & 0xff,
+ pNetworkAddress->Net[1] & 0xff,
+ pNetworkAddress->Net[2] & 0xff,
+ pNetworkAddress->Net[3] & 0xff,
+ pNetworkAddress->Node[0] & 0xff,
+ pNetworkAddress->Node[1] & 0xff,
+ pNetworkAddress->Node[2] & 0xff,
+ pNetworkAddress->Node[3] & 0xff,
+ pNetworkAddress->Node[4] & 0xff,
+ pNetworkAddress->Node[5] & 0xff
+ ));
+
+}
+
+
+WORD
+_VwIPXGetIntervalMarker(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Just returns the tick count maintained by Asynchronous Event Scheduler
+
+ This call is Synchronous
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ The tick count.
+
+--*/
+
+{
+ Sleep(0);
+ return AesTickCount;
+}
+
+
+WORD
+_VwIPXGetLocalTarget(
+ IN LPBYTE pNetworkAddress,
+ OUT LPBYTE pImmediateAddress,
+ OUT ULPWORD pTransportTime
+ )
+
+/*++
+
+Routine Description:
+
+ Given a target address of the form (network address {4}, node address {6}),
+ returns the node address of the target if on the same network, or the node
+ address of the router which knows how to get to the next hop in reaching the
+ eventual target
+
+ This call is Synchronous
+
+Arguments:
+
+ Inputs
+ pNetworkAddress
+
+ Outputs
+ pImmediateAddress
+ pTransportTime
+
+Return Value:
+
+ 00h Success
+ F1h Ipx/Spx Not Initialized
+ FAh No path to destination node found
+
+--*/
+
+{
+ //
+ // the transport handles real routing, so we always return the immediate
+ // address as the target address. The transport will only look at the
+ // target when routing
+ //
+
+ CopyMemory( pImmediateAddress,
+ pNetworkAddress + 4,
+ 6
+ );
+
+ *pTransportTime = 1; // ticks
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXGetLocalTarget,
+ IPXDBG_LEVEL_INFO,
+ "VwIPXGetLocalTarget: IN: %02x-%02x-%02x-%02x:%02x-%02x-%02x-%02x-%02x-%02x OUT: %02x-%02x-%02x-%02x-%02x-%02x\n",
+ ((LPINTERNET_ADDRESS)pNetworkAddress)->Net[0] & 0xff,
+ ((LPINTERNET_ADDRESS)pNetworkAddress)->Net[1] & 0xff,
+ ((LPINTERNET_ADDRESS)pNetworkAddress)->Net[2] & 0xff,
+ ((LPINTERNET_ADDRESS)pNetworkAddress)->Net[3] & 0xff,
+ ((LPINTERNET_ADDRESS)pNetworkAddress)->Node[0] & 0xff,
+ ((LPINTERNET_ADDRESS)pNetworkAddress)->Node[1] & 0xff,
+ ((LPINTERNET_ADDRESS)pNetworkAddress)->Node[2] & 0xff,
+ ((LPINTERNET_ADDRESS)pNetworkAddress)->Node[3] & 0xff,
+ ((LPINTERNET_ADDRESS)pNetworkAddress)->Node[4] & 0xff,
+ ((LPINTERNET_ADDRESS)pNetworkAddress)->Node[5] & 0xff,
+ pImmediateAddress[0] & 0xff,
+ pImmediateAddress[1] & 0xff,
+ pImmediateAddress[2] & 0xff,
+ pImmediateAddress[3] & 0xff,
+ pImmediateAddress[4] & 0xff,
+ pImmediateAddress[5] & 0xff
+ ));
+
+ return IPX_SUCCESS;
+}
+
+
+
+WORD
+_VwIPXGetMaxPacketSize(
+ OUT ULPWORD pRetryCount
+ )
+
+/*++
+
+Routine Description:
+
+ Returns the maximum packet size the underlying network can handle
+
+ Assumes: 1. A successfull call to GetMaxPacketSize has been made during
+ DLL initialization
+ 2. Maximum packet size is constant
+
+ This call is Synchronous
+
+Arguments:
+
+ Outputs
+ pRetryCount
+
+
+Return Value:
+
+ The maximum packet size.
+
+--*/
+
+{
+ if ( pRetryCount ) {
+ *pRetryCount = 5; // arbitrary?
+ }
+ return MyMaxPacketSize;
+}
+
+
+WORD
+_VwIPXListenForPacket(
+ IN LPECB pEcb,
+ IN ECB_ADDRESS EcbAddress
+ )
+
+/*++
+
+Routine Description:
+
+ Queue a listen request against a socket. All listen requests will be
+ completed asynchronously, unless cancelled by app
+
+ This call is Asynchronous
+
+Arguments:
+
+ Inputs
+ pEcb
+ EcbAddress
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPXECB pXecb = RetrieveXEcb(ECB_TYPE_IPX, pEcb, EcbAddress);
+ LPSOCKET_INFO pSocketInfo;
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXListenForPacket,
+ IPXDBG_LEVEL_INFO,
+ "_VwIPXListenForPacket(%04x:%04x) socket=%04x ESR=%04x:%04x\n",
+ HIWORD(EcbAddress),
+ LOWORD(EcbAddress),
+ B2LW(pXecb->SocketNumber),
+ HIWORD(pXecb->EsrAddress),
+ LOWORD(pXecb->EsrAddress)
+ ));
+
+ //
+ // don't know what real IPX/SPX does if it gets a NULL pointer
+ //
+
+ if (!pXecb) {
+ return IPX_BAD_REQUEST;
+ }
+
+ //
+ // the socket must be open already before we can perform a listen
+ //
+
+ pSocketInfo = FindSocket(pXecb->SocketNumber);
+
+ //
+ // we also return NON_EXISTENT_SOCKET (0xFF) if the socket is in use for SPX
+ //
+
+ //
+ // BUGBUG: There is nothing in the farty netware documentation that explains
+ // what gets returned if this is the case, only a warning about IPX listens
+ // and sends can't be made on a socket open for SPX. Really definitive
+ //
+
+ if (!pSocketInfo || pSocketInfo->SpxSocket) {
+ CompleteEcb(pXecb, ECB_CC_NON_EXISTENT_SOCKET);
+ return IPX_NON_EXISTENT_SOCKET;
+ }
+
+ //
+ // initiate the receive. It may complete if there is data waiting or an
+ // error occurs, otherwise the ECB will be placed in a receive pending queue
+ // for this socket
+ //
+
+ if (GetIoBuffer(pXecb, FALSE, IPX_HEADER_LENGTH)) {
+ pXecb->Ecb->InUse = ECB_IU_LISTENING;
+ IpxReceiveFirst(pXecb, pSocketInfo);
+ } else {
+ CompleteEcb(pXecb, ECB_CC_CANCELLED);
+ }
+
+ //
+ // return success. Any errors will be communicated asynchronously - either
+ // indirectly by relevant values in CompletionCode and InUse fields of the
+ // ECB or directly by an ESR callback
+ //
+
+ return IPX_SUCCESS;
+}
+
+
+WORD
+_VwIPXOpenSocket(
+ IN OUT ULPWORD pSocketNumber,
+ IN BYTE socketType,
+ IN WORD dosPDB
+ )
+
+/*++
+
+Routine Description:
+
+ Opens a socket for use by IPX or SPX. Puts the socket into non-blocking mode.
+ The socket will be bound to IPX
+
+ This call is Synchronous
+
+Arguments:
+ Inputs
+ *pSocketNumber - The requested socket number
+ socketType - Socket Longevity flag
+ dosPDB - DOS PDB. This parameter is not part of the IPX API.
+ Added because we need to remember which DOS executable created
+ the socket: we need to clean-up short-lived sockets when the
+ executable terminates
+
+ Outputs
+ pSocketNumber - Assigned socket number
+
+Return Value:
+
+ 00h Success
+ F0h Ipx Not Installed
+ F1h Ipx/Spx Not Initialized
+ FEh Socket table full
+ FFh Socket already open
+
+--*/
+
+{
+ LPSOCKET_INFO pSocketInfo;
+ WORD status;
+
+ if ((pSocketInfo = AllocateSocket()) == NULL) {
+ return IPX_SOCKET_TABLE_FULL;
+ }
+ status = CreateSocket(SOCKET_TYPE_IPX, pSocketNumber, &pSocketInfo->Socket);
+ if (status == IPX_SUCCESS) {
+
+ //
+ // set up the SOCKET_INFO fields and add it to our list of open sockets
+ //
+
+ pSocketInfo->Owner = dosPDB;
+ pSocketInfo->SocketNumber = *pSocketNumber;
+
+ //
+ // treat socketType == 0 as short-lived, anything else as long-lived.
+ // There doesn't appear to be an error return if the flag is not 0 or 0xFF
+ //
+
+ pSocketInfo->LongLived = (BOOL)(socketType != 0);
+ QueueSocket(pSocketInfo);
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXOpenSocket,
+ IPXDBG_LEVEL_INFO,
+ "_VwIPXOpenSocket: created socket %04x\n",
+ B2LW(*pSocketNumber)
+ ));
+
+ } else {
+ DeallocateSocket(pSocketInfo);
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_IPXOpenSocket,
+ IPXDBG_LEVEL_ERROR,
+ "_VwIPXOpenSocket: Failure: returning %x\n",
+ status
+ ));
+
+ }
+ return status;
+}
+
+
+VOID
+_VwIPXRelinquishControl(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ Just sleep for a nominal amount.
+
+ This call is Synchronous
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ Sleep(0);
+}
+
+
+
+VOID
+_VwIPXScheduleIPXEvent(
+ IN WORD time,
+ IN LPECB pEcb,
+ IN ECB_ADDRESS EcbAddress
+ )
+
+/*++
+
+Routine Description:
+
+ Schedules a an event to occur in some number of ticks. When the tick count
+ reaches 0, the ECB InUse field is cleared and any ESR called
+
+ This call is Asynchronous
+
+Arguments:
+
+ Inputs
+ time - the delay time ( number of 1/18 second ticks )
+ pEcb
+ EcbAddress
+
+ Outputs
+ Nothing
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPXECB pXecb = RetrieveXEcb(ECB_TYPE_IPX, pEcb, EcbAddress);
+
+ ScheduleEvent(pXecb, time);
+}
+
+
+VOID
+_VwIPXSendPacket(
+ IN LPECB pEcb,
+ IN ECB_ADDRESS EcbAddress,
+ IN WORD DosPDB
+ )
+
+/*++
+
+Routine Description:
+
+ Sends a packet to the target machine/router. This call can be made on a
+ socket that is not open
+
+ The app must have filled in the following IPX_ECB fields:
+
+ EsrAddress
+ Socket
+ ImmediateAddress
+ FragmentCount
+ fragment descriptor fields
+
+ and the following IPX_PACKET fields:
+
+ PacketType
+ Destination.Net
+ Destination.Node
+ Destination.Socket
+
+ This call is Asynchronous
+
+Arguments:
+
+ Inputs
+ pEcb
+ EcbAddress
+ DosPDB
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPXECB pXecb = RetrieveXEcb(ECB_TYPE_IPX, pEcb, EcbAddress);
+ LPSOCKET_INFO pSocketInfo;
+
+ //
+ // this function returns no immediate status so we must assume that the
+ // ECB pointer is valid
+ //
+
+ //
+ // check the ECB for correctness
+ //
+
+ if ((pXecb->Ecb->FragmentCount == 0)
+ || (ECB_FRAGMENT(pXecb->Ecb, 0)->Length < IPX_HEADER_LENGTH)) {
+ CompleteEcb(pXecb, ECB_CC_BAD_REQUEST);
+ return;
+ }
+
+ //
+ // IPXSendPacket() can be called on an unopened socket: we must try to
+ // temporarily allocate the socket
+ //
+ // Q: Is the following scenario possible with real IPX:
+ // IPXSendPacket() on unopened socket X
+ // Send fails & gets queued
+ // app makes IPXOpenSocket() call on X; X gets opened
+ //
+ // Currently, we would create the temporary socket and fail IPXOpenSocket()
+ // because it is already open!
+ //
+
+ pSocketInfo = FindSocket(pXecb->SocketNumber);
+ if (!pSocketInfo) {
+
+ //
+ // BUGBUG: when is temporary socket deleted? After send completed?
+ // when app dies? when? Novell documentation is not specific (used
+ // to say something else :-))
+ //
+
+ pSocketInfo = AllocateTemporarySocket();
+ if (pSocketInfo) {
+
+ //
+ // set up the SOCKET_INFO fields and add it to our list of open sockets
+ //
+
+ pSocketInfo->Owner = DosPDB;
+
+ //
+ // temporary sockets are always short-lived
+ //
+
+ pSocketInfo->LongLived = FALSE;
+ QueueSocket(pSocketInfo);
+
+ } else {
+
+ //
+ // BUGBUG: completion code???
+ //
+
+ CompleteEcb(pXecb, ECB_CC_SOCKET_TABLE_FULL);
+ return;
+ }
+ } else if (pSocketInfo->SpxSocket) {
+
+ //
+ // see complaint in IPXListenForPacket
+ //
+ // can't make IPX requests on socket opened for SPX
+ //
+ // BUGBUG: completion code??
+ //
+
+ CompleteEcb(pXecb, ECB_CC_NON_EXISTENT_SOCKET);
+ return;
+ }
+
+ //
+ // start the send: tries to send the data in one go. Either succeeds, fails
+ // with an error, or queues the ECB for subsequent attempts via AES/IPX
+ // deferred processing.
+ //
+ // In the first 2 cases, the ECB has been completed already
+ //
+
+ StartIpxSend(pXecb, pSocketInfo);
+}
diff --git a/private/nw/vwipxspx/dll/vwipx.h b/private/nw/vwipxspx/dll/vwipx.h
new file mode 100644
index 000000000..351351eab
--- /dev/null
+++ b/private/nw/vwipxspx/dll/vwipx.h
@@ -0,0 +1,122 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ vwipx.h
+
+Abstract:
+
+ Contains function prototypes for VWIPX.C
+
+Author:
+
+ Richard L Firth (rfirth) 25-Oct-1993
+
+Revision History:
+
+ 25-Oct-1993 rfirth
+ Created
+
+--*/
+
+VOID
+VwIPXCancelEvent(
+ VOID
+ );
+
+VOID
+VwIPXCloseSocket(
+ VOID
+ );
+
+VOID
+VwIPXDisconnectFromTarget(
+ VOID
+ );
+
+VOID
+VwIPXGenerateChecksum(
+ VOID
+ );
+
+VOID
+VwIPXGetInformation(
+ VOID
+ );
+
+VOID
+VwIPXGetInternetworkAddress(
+ VOID
+ );
+
+VOID
+VwIPXGetIntervalMarker(
+ VOID
+ );
+
+VOID
+VwIPXGetLocalTarget(
+ VOID
+ );
+
+VOID
+VwIPXGetLocalTargetAsync(
+ VOID
+ );
+
+VOID
+VwIPXGetMaxPacketSize(
+ VOID
+ );
+
+VOID
+VwIPXInitialize(
+ VOID
+ );
+
+VOID
+VwIPXListenForPacket(
+ VOID
+ );
+
+VOID
+VwIPXOpenSocket(
+ VOID
+ );
+
+VOID
+VwIPXRelinquishControl(
+ VOID
+ );
+
+VOID
+VwIPXScheduleAESEvent(
+ VOID
+ );
+
+VOID
+VwIPXScheduleIPXEvent(
+ VOID
+ );
+
+VOID
+VwIPXSendPacket(
+ VOID
+ );
+
+VOID
+VwIPXSendWithChecksum(
+ VOID
+ );
+
+VOID
+VwIPXSPXDeinit(
+ VOID
+ );
+
+VOID
+VwIPXVerifyChecksum(
+ VOID
+ );
diff --git a/private/nw/vwipxspx/dll/vwipxspx.def b/private/nw/vwipxspx/dll/vwipxspx.def
new file mode 100644
index 000000000..c62a71f84
--- /dev/null
+++ b/private/nw/vwipxspx/dll/vwipxspx.def
@@ -0,0 +1,64 @@
+LIBRARY VWIPXSPX
+
+DESCRIPTION 'WOW Network Support - IPX/SPX APIs'
+
+EXPORTS
+ VwInitialize
+ VwDispatcher
+
+ VwIPXCancelEvent
+ VwIPXCloseSocket
+ VwIPXDisconnectFromTarget
+ VwIPXGenerateChecksum
+ VwIPXGetInformation
+ VwIPXGetInternetworkAddress
+ VwIPXGetIntervalMarker
+ VwIPXGetLocalTarget
+ VwIPXGetLocalTargetAsync
+ VwIPXGetMaxPacketSize
+ VwIPXInitialize
+ VwIPXListenForPacket
+ VwIPXOpenSocket
+ VwIPXRelinquishControl
+ VwIPXScheduleAESEvent
+ VwIPXScheduleIPXEvent
+ VwIPXSendPacket
+ VwIPXSendWithChecksum
+ VwIPXSPXDeinit
+ VwIPXVerifyChecksum
+ VwSPXAbortConnection
+ VwSPXEstablishConnection
+ VwSPXGetConnectionStatus
+ VwSPXInitialize
+ VwSPXListenForConnection
+ VwSPXListenForSequencedPacket
+ VwSPXSendSequencedPacket
+ VwSPXTerminateConnection
+
+ VWinEsrCallback
+ VWinInitialize
+
+ VWinIPXCancelEvent
+ VWinIPXCloseSocket
+ VWinIPXDisconnectFromTarget
+ VWinIPXGetInternetworkAddress
+ VWinIPXGetIntervalMarker
+ VWinIPXGetLocalTarget
+ VWinIPXGetLocalTargetAsync
+ VWinIPXGetMaxPacketSize
+ VWinIPXInitialize
+ VWinIPXListenForPacket
+ VWinIPXOpenSocket
+ VWinIPXRelinquishControl
+ VWinIPXScheduleIPXEvent
+ VWinIPXSendPacket
+ VWinIPXSPXDeinit
+
+ VWinSPXAbortConnection
+ VWinSPXEstablishConnection
+ VWinSPXGetConnectionStatus
+ VWinSPXInitialize
+ VWinSPXListenForConnection
+ VWinSPXListenForSequencedPacket
+ VWinSPXSendSequencedPacket
+ VWinSPXTerminateConnection
diff --git a/private/nw/vwipxspx/dll/vwipxspx.h b/private/nw/vwipxspx/dll/vwipxspx.h
new file mode 100644
index 000000000..01ad6ec5a
--- /dev/null
+++ b/private/nw/vwipxspx/dll/vwipxspx.h
@@ -0,0 +1,586 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ vwipxspx.h
+
+Abstract:
+
+ Contains manifests, typedefs, structures, macros for NTVDM IPX/SPX support
+
+Author:
+
+ Richard L Firth (rfirth) 30-Sep-1993
+
+Environment:
+
+ Structures are expected to live in segmented VDM address space, but be
+ accessible from flat 32-bit protect mode. The VDM can be in real or protect
+ mode
+
+Revision History:
+
+ 30-Sep-1993 rfirth
+ Created
+
+--*/
+
+#ifndef _VWIPXSPX_H_
+#define _VWIPXSPX_H_
+
+//
+// FREE_OBJECT - in free version, just calls LocalFree. For debug version, fills
+// memory with some arbitrary value, then frees the pointer and checks that what
+// LocalFree thought that the pointer pointed at a valid, freeable object
+//
+
+#if DBG
+
+#define FREE_OBJECT(p) {\
+ FillMemory(p, sizeof(*p), 0xFF);\
+ VWASSERT(LocalFree((HLOCAL)(p)), NULL);\
+ }
+#else
+
+#define FREE_OBJECT(p) VWASSERT(LocalFree((HLOCAL)(p)), NULL)
+
+#endif
+
+//
+// simple function macros
+//
+
+//#define AllocateXecb() (LPXECB)LocalAlloc(LPTR, sizeof(XECB))
+//#define DeallocateXecb(p) FREE_OBJECT(p)
+#define AllocateBuffer(s) (LPVOID)LocalAlloc(LMEM_FIXED, (s))
+#define DeallocateBuffer(p) FREE_OBJECT(p)
+
+//
+// pseudo-types for 16-bit addresses
+//
+
+#define ESR_ADDRESS DWORD
+#define ECB_ADDRESS DWORD
+
+//
+// from Novell documentation, the default maximum open sockets. Max max is 150
+//
+
+#ifndef DEFAULT_MAX_OPEN_SOCKETS
+#define DEFAULT_MAX_OPEN_SOCKETS 20
+#endif
+
+#ifndef MAX_OPEN_SOCKETS
+#define MAX_OPEN_SOCKETS 150
+#endif
+
+#define SPX_INSTALLED 0xFF
+
+#define MAX_LISTEN_QUEUE_SIZE 5 // ?
+
+//
+// misc. macros
+//
+
+//
+// B2LW, L2Bx - big-endian to little-endian macros
+//
+
+#define B2LW(w) (WORD)(((WORD)(w) << 8) | ((WORD)(w) >> 8))
+#define B2LD(d) (DWORD)(B2LW((DWORD)(d) << 16) | B2LW((DWORD)(d) >> 16))
+#define L2BW(w) B2LW(w)
+#define L2BD(d) B2LD(d)
+
+//
+// miscellaneous manifests
+//
+
+#define ONE_TICK (1000/18) // 1/18 sec in milliseconds (55.55 mSec)
+#define SLEEP_TIME ONE_TICK // amount of time we Sleep() during IPXRelinquishControl
+
+//
+// options for IPXGetInformation
+//
+
+#define IPX_ODI 0x0001
+#define IPX_CHECKSUM_FUNCTIONS 0x0002
+
+//
+// IPX/SPX structures. The following structures are in VDM format, and should
+// be packed on a byte-boundary
+//
+// Netware maintains certain structure fields in network (big-endian) format
+//
+
+#include <packon.h>
+
+//
+// INTERNET_ADDRESS - structure returned by IPXGetInternetworkAddress
+//
+
+typedef struct {
+ BYTE Net[4];
+ BYTE Node[6];
+} INTERNET_ADDRESS ;
+
+typedef INTERNET_ADDRESS UNALIGNED *LPINTERNET_ADDRESS;
+
+//
+// NETWARE_ADDRESS - address of an application on the network, as defined by
+// its network segment, node address and socket number
+//
+
+typedef struct {
+ BYTE Net[4]; // hi-lo
+ BYTE Node[6]; // hi-lo
+ WORD Socket; // hi-lo
+} NETWARE_ADDRESS ;
+
+typedef NETWARE_ADDRESS UNALIGNED *LPNETWARE_ADDRESS;
+
+//
+// FRAGMENT - ECB/IPX/SPX buffers are split into 'fragments'
+//
+
+typedef struct {
+ LPVOID Address; // offset-segment
+ WORD Length; // hi-lo
+} FRAGMENT ;
+
+typedef FRAGMENT UNALIGNED *LPFRAGMENT;
+
+//
+// IPX_PACKET - format of packet submitted to IPX for sending. The maximum
+// size of an IPX packet is 576 bytes, 30 bytes header, 546 bytes data
+//
+
+typedef struct {
+ WORD Checksum; // always set to 0xFFFF
+ WORD Length; // set by IPX - header + data
+ BYTE TransportControl; // set by IPX to 0. Used by routers
+
+ //
+ // for IPX, PacketType is 0 (Unknown Packet Type) or 4 (Packet Exchange
+ // Packet)
+ //
+
+ BYTE PacketType;
+ NETWARE_ADDRESS Destination;
+ NETWARE_ADDRESS Source;
+ BYTE Data[]; // 546 bytes max.
+} IPX_PACKET ;
+
+typedef IPX_PACKET UNALIGNED *LPIPX_PACKET;
+
+#define IPX_HEADER_LENGTH sizeof(IPX_PACKET)
+#define MAXIMUM_IPX_PACKET_LENGTH 576
+#define MAXIMUM_IPX_DATA_LENGTH (MAXIMUM_IPX_PACKET_LENGTH - IPX_HEADER_LENGTH)
+
+#define IPX_PACKET_TYPE 4
+
+//
+// SPX_PACKET - format of packet submitted to SPX for sending. The maximum
+// size of an SPX packet is 576 bytes, 42 bytes header, 534 bytes data
+//
+
+typedef struct {
+ WORD Checksum; // always set to 0xFFFF
+ WORD Length; // set by IPX - header + data
+ BYTE TransportControl; // set by IPX to 0. Used by routers
+
+ //
+ // for SPX, PacketType is set to 5 (Sequenced Packet Protocol Packet)
+ //
+
+ BYTE PacketType;
+ NETWARE_ADDRESS Destination;
+ NETWARE_ADDRESS Source;
+
+ //
+ // ConnectionControl is a bitmap which control bi-directional flow over a
+ // link. The bits are defined (by Xerox SPP) as:
+ //
+ // 0-3 undefined
+ // 4 end-of-message
+ // This is the only bit which can be directly manipulated by an
+ // app. The bit is passed through unchanged by SPX
+ // 5 attention
+ // Ignored by SPX, but passed through
+ // 6 acknowledge
+ // Set by SPX if an ack is required
+ // 7 system packet
+ // Set by SPX if the packet is internal control. An app should
+ // never see this bit (i.e. should never see a system packet)
+ //
+
+ BYTE ConnectionControl;
+
+ //
+ // DataStreamType defines the type of data in the packet:
+ //
+ // 0x00 - 0xFD client-defined.
+ // Ignored by SPX
+ // 0xFE end-of-connection.
+ // When active connection is terminated, SPX
+ // generates and sends a packet with this bit set.
+ // This will be the last packet sent on the connection
+ // 0xFF end-of-connection acknowledgement
+ // SPX generates a system packet to acknowledge an
+ // end-of-connection packet
+ //
+
+ BYTE DataStreamType;
+ WORD SourceConnectId; // assigned by SPX
+ WORD DestinationConnectId;
+ WORD SequenceNumber; // managed by SPX
+ WORD AckNumber; // managed by SPX
+ WORD AllocationNumber; // managed by SPX
+ BYTE Data[]; // 534 bytes max.
+
+} SPX_PACKET ;
+
+typedef SPX_PACKET UNALIGNED *LPSPX_PACKET;
+
+#define SPX_HEADER_LENGTH sizeof(SPX_PACKET)
+#define MAXIMUM_SPX_PACKET_LENGTH MAXIMUM_IPX_PACKET_LENGTH
+#define MAXIMUM_SPX_DATA_LENGTH (MAXIMUM_SPX_PACKET_LENGTH - SPX_HEADER_LENGTH)
+
+#define SPX_PACKET_TYPE 5
+
+//
+// ConnectionControl flags
+//
+
+#define SPX_CONNECTION_RESERVED 0x0F
+#define SPX_END_OF_MESSAGE 0x10
+#define SPX_ATTENTION 0x20
+#define SPX_ACK_REQUIRED 0x40
+#define SPX_SYSTEM_PACKET 0x80
+
+//
+// DataStreamType values
+//
+
+#define SPX_DS_ESTABLISH 0x00
+#define SPX_DS_TERMINATE 0xfe
+
+//
+// IPX_ECB - Event Control Block. This structure is used by most IPX/SPX APIs,
+// especially when deferred IPX/AES processing is required. The following
+// structure is a socket-based ECB
+//
+
+typedef struct {
+
+ //
+ // LinkAddress is reserved for use by IPX. We use it to link the ECB onto
+ // a queue. We appropriate the space used for an x86 segmented address
+ // (real or protect mode) as a flat 32-bit pointer
+ //
+
+ ULPVOID LinkAddress; // offset-segment
+
+ //
+ // EsrAddress is non-NULL if an Event Service Routine will be called when
+ // the event described by the ECB completes. This will always be an x86
+ // segmented address (real or protect mode)
+ //
+
+ ESR_ADDRESS EsrAddress; // offset-segment
+
+ //
+ // IPX uses the InUse field to mark the ECB as owned by IPX (!0) or by the
+ // app (0):
+ //
+ // 0xF8 App tried to send a packet while IPX was busy; IPX queued
+ // the ECB
+ // 0xFA IPX is processing the ECB
+ // 0xFB IPX has used the ECB for some event and put it on a queue
+ // for processing
+ // 0xFC the ECB is waiting for an AES event to occur
+ // 0xFD the ECB is waiting for an IPX event to occur
+ // 0xFE IPX is listening on a socket for incoming packets
+ // 0xFF IPX is using the ECB to send a packet
+ //
+
+ BYTE InUse;
+
+ //
+ // CompletionCode is used to return a status from a deferred request. This
+ // field is not valid until InUse has been set to 0
+ //
+ // NOTE: We have to differentiate between AES and IPX ECBs on callbacks: due
+ // to their different sizes, we store the 16-bit segment and offset in
+ // different places. In order to differentiate the ECBs, we use CompletionCode
+ // field (AesWorkspace[0]) as the owner. The real CompletionCode for IPX ECBs
+ // goes in IPX_ECB_COMPLETE (DriverWorkspace[7]). But only for completed ECBs
+ // that have an ESR
+ //
+
+ BYTE CompletionCode;
+ WORD SocketNumber; // hi-lo
+
+ //
+ // the first word of IpxWorkspace is used to return the connection ID of
+ // an SPX connection
+ //
+
+ DWORD IpxWorkspace;
+ BYTE DriverWorkspace[12];
+
+ //
+ // ImmediateAddress is the local network node at the remote end of this
+ // connection. It is either the node address of the remote machine if it
+ // is on this LAN, or it is the node address of the router if the remote
+ // machine is on a different LAN
+ //
+ // This field must be initialized when talking over IPX, but not SPX
+ //
+
+ BYTE ImmediateAddress[6];
+
+ //
+ // FragmentCount - number of FRAGMENT structures that comprise the request.
+ // Must be at least 1
+ //
+
+ WORD FragmentCount;
+
+ //
+ // FragmentCount fragments start here
+ //
+
+} IPX_ECB ;
+
+typedef IPX_ECB UNALIGNED *LPIPX_ECB;
+
+//
+// ECB InUse values
+//
+
+#define ECB_IU_NOT_IN_USE 0x00
+#define ECB_IU_TEMPORARY 0xCC
+#define ECB_IU_LISTENING_SPX 0xF7 // same as win16 (by observation)
+#define ECB_IU_SEND_QUEUED 0xF8
+#define ECB_IU_AWAITING_CONNECTION 0xF9 // same as win16 (by observation)
+#define ECB_IU_BEING_PROCESSED 0xFA
+#define ECB_IU_AWAITING_PROCESSING 0xFB
+#define ECB_IU_AWAITING_AES_EVENT 0xFC
+#define ECB_IU_AWAITING_IPX_EVENT 0xFD
+#define ECB_IU_LISTENING 0xFE
+#define ECB_IU_SENDING 0xFF
+
+//
+// ECB CompletionCode values
+//
+
+#define ECB_CC_SUCCESS 0x00
+#define ECB_CC_CONNECTION_TERMINATED 0xEC
+#define ECB_CC_CONNECTION_ABORTED 0xED
+#define ECB_CC_INVALID_CONNECTION 0xEE
+#define ECB_CC_CONNECTION_TABLE_FULL 0xEF
+#define ECB_CC_CANNOT_CANCEL 0xF9
+#define ECB_CC_CANCELLED 0xFC
+#define ECB_CC_BAD_REQUEST 0xFD
+#define ECB_CC_BAD_SEND_REQUEST 0xFD
+#define ECB_CC_PACKET_OVERFLOW 0xFD
+#define ECB_CC_UNDELIVERABLE 0xFE
+#define ECB_CC_SOCKET_TABLE_FULL 0xFE
+#define ECB_CC_BAD_LISTEN_REQUEST 0xFF
+#define ECB_CC_HARDWARE_ERROR 0xFF
+#define ECB_CC_NON_EXISTENT_SOCKET 0xFF
+
+//
+// we commandeer certain (reserved) fields for our own internal use:
+//
+// LPECB EcbLink LinkAddress
+// PVOID Buffer32 DriverWorkspace[0]
+// WORD Length32 DriverWorkspace[4]
+// WORD Flags32 DriverWorkspace[6]
+// WORD OriginalEs DriverWorkspace[8]
+// WORD OriginalSi DriverWorkspace[10]
+//
+
+#define ECB_TYPE(p) (((LPIPX_ECB)(p))->CompletionCode)
+#define IPX_ECB_SEGMENT(p) (WORD)*((ULPWORD)&(((LPIPX_ECB)(p))->IpxWorkspace)+0)
+#define IPX_ECB_OFFSET(p) (WORD)*((ULPWORD)&(((LPIPX_ECB)(p))->IpxWorkspace)+2)
+#define IPX_ECB_BUFFER32(p) (ULPVOID)*(ULPVOID*)&(((LPIPX_ECB)(p))->DriverWorkspace[0])
+#define IPX_ECB_LENGTH32(p) (WORD)*(ULPWORD)&(((LPIPX_ECB)(p))->DriverWorkspace[4])
+#define IPX_ECB_FLAGS32(p) (((LPIPX_ECB)(p))->DriverWorkspace[6])
+#define IPX_ECB_COMPLETE(p) (((LPIPX_ECB)(p))->DriverWorkspace[7])
+
+#define SPX_ECB_CONNECTION_ID(p) (WORD)*(ULPWORD)&(((LPIPX_ECB)(p))->IpxWorkspace)
+
+//
+// ECB Flags32 flags
+//
+
+#define ECB_FLAG_BUFFER_ALLOCATED 0x01
+
+//
+// ECB types
+//
+
+#define ECB_TYPE_AES 0
+#define ECB_TYPE_IPX 1
+#define ECB_TYPE_SPX 2
+
+//
+// ECB owners
+//
+
+#define ECB_OWNER_IPX 0xFF
+#define ECB_OWNER_AES 0x00
+
+//
+// ECB_FRAGMENT - macro which gives the address of the first fragment structure
+// within a socket-based ECB
+//
+
+#define ECB_FRAGMENT(p, n) ((LPFRAGMENT)(((LPIPX_ECB)(p) + 1)) + (n))
+
+//
+// AES_ECB - used by AES, these socket-less ECBs are used to schedule events
+//
+
+typedef struct {
+ ULPVOID LinkAddress; // offset-segment
+ ESR_ADDRESS EsrAddress; // offset-segment
+ BYTE InUse;
+
+ //
+ // first 3 bytes overlay CompletionCode (1) and SocketNumber (2) fields of
+ // IPX_ECB. Last 2 bytes overlay first 2 bytes of IpxWorkspace (4) field of
+ // IPX_ECB. We use the 1st byte of the common unused fields as the ECB type
+ // (send/receive/timed-event)
+ //
+
+ BYTE AesWorkspace[5];
+} AES_ECB ;
+
+typedef AES_ECB UNALIGNED *LPAES_ECB;
+
+//
+// as with IPX_ECB, we 'borrow' some of the reserved fields for our own use
+//
+
+#define AES_ECB_SEGMENT(p) (WORD)*(ULPWORD)&(((LPAES_ECB)(p))->AesWorkspace[1])
+#define AES_ECB_OFFSET(p) (WORD)*(ULPWORD)&(((LPAES_ECB)(p))->AesWorkspace[3])
+
+//
+// LPECB - points to either IPX_ECB or AES_ECB. Both in VDM workspace
+//
+
+#define LPECB LPIPX_ECB
+
+//
+// SPX_CONNECTION_STATS - returned by SPXGetConnectionStatus. All WORD fields
+// are to be returned HiLo (ie to Hawaii). All fields come back from NT SPX
+// transport in HiLo format also (this was changed recently, used to be in
+// Intel order).
+//
+
+typedef struct {
+ BYTE State;
+ BYTE WatchDog;
+ WORD LocalConnectionId;
+ WORD RemoteConnectionId;
+ WORD LocalSequenceNumber;
+ WORD LocalAckNumber;
+ WORD LocalAllocNumber;
+ WORD RemoteAckNumber;
+ WORD RemoteAllocNumber;
+ WORD LocalSocket;
+ BYTE ImmediateAddress[6];
+ BYTE RemoteNetwork[4];
+ BYTE RemoteNode[6];
+ WORD RemoteSocket;
+ WORD RetransmissionCount;
+ WORD EstimatedRoundTripDelay;
+ WORD RetransmittedPackets;
+ WORD SuppressedPackets;
+} SPX_CONNECTION_STATS ;
+
+typedef SPX_CONNECTION_STATS UNALIGNED* LPSPX_CONNECTION_STATS;
+
+#include <packoff.h>
+
+//
+// 16-bit parameter get/set macros. These may change depending on requirements
+// of real/protect mode parameters (e.g. stack based vs. register based)
+//
+
+#define IPX_GET_AES_ECB(p) (p) = (LPAES_ECB)POINTER_FROM_WORDS(getES(), getSI(), sizeof(AES_ECB))
+#define IPX_GET_IPX_ECB(p) (p) = (LPIPX_ECB)POINTER_FROM_WORDS(getES(), getSI(), sizeof(IPX_ECB))
+#define IPX_GET_SOCKET(s) (s) = (WORD)getDX()
+#define IPX_GET_SOCKET_LIFE(l) (l) = (BYTE)getBP()
+#define IPX_GET_SOCKET_OWNER(o) (o) = (WORD)getCX()
+#define IPX_GET_BUFFER(p, s) (p) = (ULPBYTE)POINTER_FROM_WORDS(getES(), getSI(), (s))
+#define IPX_GET_ECB_SEGMENT() getES()
+#define IPX_GET_ECB_OFFSET() getSI()
+
+#define IPX_SET_STATUS(s) setAL((BYTE)(s))
+#define IPX_SET_SOCKET(s) setDX((WORD)(s))
+#define IPX_SET_INFORMATION(v) setDX((WORD)(v))
+
+#define SPX_SET_STATUS(s) setAL((BYTE)(s))
+#define SPX_SET_CONNECTION_ID(i) setDX((WORD)(i))
+
+//
+// macros returning 16-bit API parameters - may fetch register contents or values
+// from stack/memory
+//
+
+#define ECB_PARM_SEGMENT() getES()
+#define ECB_PARM_OFFSET() getSI()
+#define ECB_PARM_ADDRESS() (ECB_ADDRESS)MAKELONG(getSI(), getES())
+
+#define AES_ECB_PARM() RetrieveEcb(ECB_TYPE_AES)
+
+#define IPX_ECB_PARM() RetrieveEcb(ECB_TYPE_IPX)
+#define IPX_SOCKET_PARM() getDX()
+#define IPX_SOCKET_LIFE_PARM() (BYTE)getBP()
+#define IPX_SOCKET_OWNER_PARM() getCX()
+#define IPX_BUFFER_PARM(s) (ULPBYTE)POINTER_FROM_WORDS(getES(), getSI(), (s))
+#define IPX_TICKS_PARM() getBP()
+
+#define SPX_RETRY_COUNT_PARM() (BYTE)getBP()
+#define SPX_WATCHDOG_FLAG_PARM() ((BYTE)(getBP() >> 8))
+#define SPX_ECB_PARM() RetrieveEcb(ECB_TYPE_IPX)
+#define SPX_CONNECTION_PARM() getDX()
+#define SPX_BUFFER_PARM(s) (ULPBYTE)POINTER_FROM_WORDS(getES(), getSI(), (s))
+
+//
+// IPX error codes - same codes used in different circumstances
+//
+
+#define IPX_SUCCESS 0x00
+#define IPX_CANNOT_CANCEL 0xF9
+#define IPX_NO_PATH_TO_DESTINATION 0xFA
+#define IPX_CANCELLED 0xFC
+#define IPX_BAD_REQUEST 0xFD
+#define IPX_SOCKET_TABLE_FULL 0xFE
+#define IPX_UNDELIVERABLE 0xFE
+#define IPX_SOCKET_ALREADY_OPEN 0xFF
+#define IPX_HARDWARE_ERROR 0xFF
+#define IPX_NON_EXISTENT_SOCKET 0xFF
+#define IPX_ECB_NOT_IN_USE 0xFF
+
+//
+// SPX error codes - same codes used in different circumstances
+//
+
+#define SPX_SUCCESS 0x00
+#define SPX_CONNECTION_TERMINATED 0xEC
+#define SPX_CONNECTION_ABORTED 0xED
+#define SPX_INVALID_CONNECTION 0xEE
+#define SPX_CONNECTION_TABLE_FULL 0xEF
+#define SPX_SOCKET_CLOSED 0xFC
+#define SPX_PACKET_OVERFLOW 0xFD
+#define SPX_BAD_SEND_REQUEST 0xFD // malformed packet
+#define SPX_BAD_LISTEN_REQUEST 0xFF
+#define SPX_NON_EXISTENT_SOCKET 0xFF
+
+#endif // _VWIPXSPX_H_
diff --git a/private/nw/vwipxspx/dll/vwipxspx.rc b/private/nw/vwipxspx/dll/vwipxspx.rc
new file mode 100644
index 000000000..c355cffea
--- /dev/null
+++ b/private/nw/vwipxspx/dll/vwipxspx.rc
@@ -0,0 +1,11 @@
+#include <windows.h>
+
+#include <ntverp.h>
+
+#define VER_FILETYPE VFT_DLL
+#define VER_FILESUBTYPE VFT2_DRV_NETWORK
+#define VER_FILEDESCRIPTION_STR "Virtual Dos Machine IPX/SPX Interface Library"
+#define VER_INTERNALNAME_STR "VWIPXSPX.DLL"
+#define VER_ORIGINALFILENAME_STR "VWIPXSPX.DLL"
+
+#include "common.ver"
diff --git a/private/nw/vwipxspx/dll/vwmisc.c b/private/nw/vwipxspx/dll/vwmisc.c
new file mode 100644
index 000000000..2d7821dee
--- /dev/null
+++ b/private/nw/vwipxspx/dll/vwmisc.c
@@ -0,0 +1,74 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ vwmisc.c
+
+Abstract:
+
+ ntVdm netWare (Vw) IPX/SPX Functions
+
+ Vw: The peoples' network
+
+ Contains miscellaneous (non-IPX/SPX) functions
+
+ Contents:
+ VwTerminateProgram
+
+Author:
+
+ Richard L Firth (rfirth) 30-Sep-1993
+
+Environment:
+
+ User-mode Win32
+
+Revision History:
+
+ 30-Sep-1993 rfirth
+ Created
+
+--*/
+
+#include "vw.h"
+#pragma hdrstop
+
+//
+// functions
+//
+
+
+VOID
+VwTerminateProgram(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ When a DOS program terminates, we must close any open sockets that were
+ specified as SHORT_LIVED
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_ANY,
+ IPXDBG_LEVEL_INFO,
+ "VwTerminateProgram: PDB=%04x\n",
+ getCX()
+ ));
+
+ KillShortLivedSockets(getCX());
+}
diff --git a/private/nw/vwipxspx/dll/vwmisc.h b/private/nw/vwipxspx/dll/vwmisc.h
new file mode 100644
index 000000000..0367bc7d7
--- /dev/null
+++ b/private/nw/vwipxspx/dll/vwmisc.h
@@ -0,0 +1,27 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ vwipx.h
+
+Abstract:
+
+ Contains function prototypes for VWMISC.C
+
+Author:
+
+ Richard L Firth (rfirth) 25-Oct-1993
+
+Revision History:
+
+ 25-Oct-1993 rfirth
+ Created
+
+--*/
+
+VOID
+VwTerminateProgram(
+ VOID
+ );
diff --git a/private/nw/vwipxspx/dll/vwspx.c b/private/nw/vwipxspx/dll/vwspx.c
new file mode 100644
index 000000000..02ef58481
--- /dev/null
+++ b/private/nw/vwipxspx/dll/vwspx.c
@@ -0,0 +1,1339 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ vwspx.c
+
+Abstract:
+
+ ntVdm netWare (Vw) IPX/SPX Functions
+
+ Vw: The peoples' network
+
+ Contains internal routines for DOS/WOW SPX calls (netware functions).
+ The SPX APIs use WinSock to perform the actual operations
+
+ Contents:
+ _VwSPXAbortConnection
+ _VwSPXEstablishConnection
+ _VwSPXGetConnectionStatus
+ _VwSPXInitialize
+ _VwSPXListenForConnection
+ _VwSPXListenForSequencedPacket
+ _VwSPXSendSequencedPacket
+ _VwSPXTerminateConnection
+
+ The SPX functions build on the IPX functions (VWIPX.C). SPX maintains
+ connections, IPX maintains sockets. A socket may have a list of connections
+
+Author:
+
+ Richard L Firth (rfirth) 30-Sep-1993
+
+Environment:
+
+ User-mode Win32
+
+Revision History:
+
+ 30-Sep-1993 rfirth
+ Created
+
+--*/
+
+#include "vw.h"
+#pragma hdrstop
+
+//
+// functions
+//
+
+
+VOID
+_VwSPXAbortConnection(
+ IN WORD SPXConnectionID
+ )
+
+/*++
+
+Routine Description:
+
+ Aborts a connection. Because NWLink doesn't differentiate between abrupt
+ and graceful closes, this function has the same effect as
+ VwSPXTerminateConnection
+
+Arguments:
+
+ SPXConnectionID - connection to abort
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPCONNECTION_INFO pConnectionInfo;
+
+ RequestMutex();
+ pConnectionInfo = FindConnection(SPXConnectionID);
+ if (pConnectionInfo) {
+ DequeueConnection(pConnectionInfo->OwningSocket, pConnectionInfo);
+ AbortOrTerminateConnection(pConnectionInfo, ECB_CC_CONNECTION_ABORTED);
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_SPXAbortConnection,
+ IPXDBG_LEVEL_ERROR,
+ "VwSPXAbortConnection: cannot find connection %04x\n",
+ SPXConnectionID
+ ));
+
+ }
+ ReleaseMutex();
+}
+
+
+WORD
+_VwSPXEstablishConnection(
+ IN BYTE retryCount,
+ IN BYTE watchDogFlag,
+ OUT ULPWORD pSPXConnectionID,
+ IN LPECB pEcb,
+ IN ECB_ADDRESS EcbAddress
+ )
+
+/*++
+
+Routine Description:
+
+ Creates a connection with a remote SPX socket. The remote end can be on
+ this machine (i.e. same app in DOS world)
+
+ This call is Asynchronous
+
+Arguments:
+
+ Inputs
+ retryCount
+ watchDogFlag
+ pEcb
+ EcbAddress
+
+ Outputs
+ pSPXConnectionID
+
+Return Value:
+
+ 00h Attempting to talk to remote
+ EFh Local connection table full
+ FDh Fragment count not 1; buffer size not 42
+ FFh Send socket not open
+
+--*/
+
+{
+ LPXECB pXecb = RetrieveXEcb(ECB_TYPE_SPX, pEcb, EcbAddress);
+ LPSOCKET_INFO pSocketInfo;
+ LPCONNECTION_INFO pConnectionInfo;
+ WORD connectionId;
+ LPSPX_PACKET pPacket;
+ SOCKADDR_IPX destination;
+ int rc;
+ SOCKET s;
+
+ pSocketInfo = FindSocket(pXecb->SocketNumber);
+ if (!pSocketInfo) {
+ CompleteEcb(pXecb, ECB_CC_NON_EXISTENT_SOCKET);
+ return SPX_NON_EXISTENT_SOCKET;
+ }
+
+ //
+ // if no outstanding IPX operations, change socket to SPX
+ //
+
+ if (!pSocketInfo->SpxSocket) {
+ if (!(pSocketInfo->PendingSends && pSocketInfo->PendingListens)) {
+ rc = ReopenSocket(pSocketInfo);
+ } else {
+ rc = ECB_CC_BAD_SEND_REQUEST;
+ }
+ if (rc != SPX_SUCCESS) {
+ CompleteOrQueueEcb(pXecb, (BYTE)rc);
+ return SPX_BAD_SEND_REQUEST;
+ }
+ }
+
+ //
+ // real SPX will use the ECB to send an ESTABLISH CONNECTION packet. This
+ // is handled for us within the SPX transport. Nevertheless we must check
+ // the fragment and dismiss the request if its not sufficient
+ //
+
+ if ((pXecb->Ecb->FragmentCount != 1)
+ || (ECB_FRAGMENT(pXecb->Ecb, 0)->Length < SPX_HEADER_LENGTH)) {
+ CompleteEcb(pXecb, ECB_CC_BAD_SEND_REQUEST);
+ return SPX_BAD_SEND_REQUEST;
+ }
+
+ //
+ // the socket is open for SPX. Allocate a connection/connection ID
+ //
+
+ pConnectionInfo = AllocateConnection(pSocketInfo);
+ if (pConnectionInfo) {
+
+ //
+ // create new socket, bound to the same local address as the parent
+ // socket. This is the 'connection'
+ //
+
+#if REUSEADDR
+
+ connectionId = pSocketInfo->SocketNumber;
+ rc = CreateSocket(SOCKET_TYPE_SPX, &connectionId, &pConnectionInfo->Socket);
+ s = pConnectionInfo->Socket;
+// if (rc == SPX_SUCCESS) {
+
+#else
+
+ s = socket(AF_IPX, SOCK_SEQPACKET, NSPROTO_SPX);
+ if (s != INVALID_SOCKET) {
+
+ u_long arg = !0;
+
+ //
+ // put the socket in non-blocking I/O mode
+ //
+
+ rc = ioctlsocket(s, FIONBIO, &arg);
+ if (rc != SOCKET_ERROR) {
+
+ int true = 1;
+
+ rc = setsockopt(s,
+ NSPROTO_IPX,
+ IPX_RECVHDR,
+ (char FAR*)&true,
+ sizeof(true)
+ );
+ if (rc != SOCKET_ERROR) {
+ pConnectionInfo->Socket = s;
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_SPXEstablishConnection,
+ IPXDBG_LEVEL_ERROR,
+ "VwSPXEstablishConnection: setsockopt(IPX_RECVHDR) returns %d\n",
+ WSAGetLastError()
+ ));
+
+ }
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_SPXEstablishConnection,
+ IPXDBG_LEVEL_ERROR,
+ "VwSPXEstablishConnection: ioctlsocket(FIONBIO) returns %d\n",
+ WSAGetLastError()
+ ));
+
+ }
+ } else {
+ rc = WSAGetLastError();
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_SPXEstablishConnection,
+ IPXDBG_LEVEL_ERROR,
+ "VwSPXEstablishConnection: socket() returns %d\n",
+ rc
+ ));
+
+ }
+
+#endif
+
+ } else {
+ rc = !SPX_SUCCESS;
+ }
+
+ if (rc == SPX_SUCCESS) {
+ pConnectionInfo->State = CI_STARTING;
+ connectionId = pConnectionInfo->ConnectionId;
+
+ //
+ // set the ECB InUse field to 0xF7. Same as snowball by observation (and
+ // probably correct anyway since it looks as though 0xF7 means 'waiting
+ // to receive SPX packet', which is true in this case - normally SPX
+ // creates a connection by sending an establish frame then waits for the
+ // ack frame
+ //
+
+ pXecb->Ecb->InUse = ECB_IU_LISTENING_SPX;
+ } else {
+
+ //
+ // if we failed to get CONNECTION_INFO or create the new socket, return
+ // immediately with an error (socket table full?)
+ //
+
+ if (s != INVALID_SOCKET) {
+ closesocket(s);
+ }
+ if (pConnectionInfo) {
+ DeallocateConnection(pConnectionInfo);
+ }
+ CompleteEcb(pXecb, ECB_CC_CONNECTION_TABLE_FULL);
+ return SPX_CONNECTION_TABLE_FULL;
+ }
+
+ //
+ // get the destination info from the SPX header in VDM memory and set up the
+ // connection. If the connect request would wait then we leave AES to
+ // periodically check the progress of the request
+ //
+
+ pPacket = (LPSPX_PACKET)GET_FAR_POINTER(&ECB_FRAGMENT(pXecb->Ecb, 0)->Address,
+ IS_PROT_MODE(pXecb)
+ );
+ //
+ // fill in the packet details (app shouldn't look at these until the command
+ // completes). In 16-bit SPX, these values are filled in by the transport.
+ // Our transport does not return these values, so we have to 'invent' them,
+ // but since they are fairly static it should be ok (maybe the transport
+ // should return them)
+ //
+
+ pPacket->Checksum = 0xffff;
+ pPacket->Length = L2BW(SPX_HEADER_LENGTH);
+ pPacket->TransportControl = 0;
+ pPacket->PacketType = SPX_PACKET_TYPE;
+ pPacket->Source.Socket = pSocketInfo->SocketNumber;
+ pPacket->ConnectionControl = SPX_SYSTEM_PACKET | SPX_ACK_REQUIRED;
+ pPacket->DataStreamType = SPX_DS_ESTABLISH;
+ pPacket->SourceConnectId = pConnectionInfo->ConnectionId;
+ pPacket->DestinationConnectId = 0xffff;
+ pPacket->SequenceNumber = 0;
+ pPacket->AckNumber = 0;
+ pPacket->AllocationNumber = 0;
+
+ //
+ // get the destination address info
+ //
+
+ CopyMemory(&destination.sa_netnum,
+ (LPBYTE)&pPacket->Destination,
+ sizeof(pPacket->Destination)
+ );
+ destination.sa_family = AF_IPX;
+
+ //
+ // initiate the connection
+ //
+
+ rc = connect(s, (LPSOCKADDR)&destination, sizeof(destination));
+ if (rc != SOCKET_ERROR) {
+
+ //
+ // add the CONNECTION_INFO structure to the list of connections owned
+ // by this socket and set the connection state to ESTABLISHED
+ //
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_SPXEstablishConnection,
+ IPXDBG_LEVEL_INFO,
+ "VwSPXEstablishConnection: socket connected\n"
+ ));
+
+ RequestMutex();
+ QueueConnection(pSocketInfo, pConnectionInfo);
+ pConnectionInfo->State = CI_ESTABLISHED;
+ ReleaseMutex();
+
+ //
+ // the connection ID also appears in the first segment of the establish
+ // ECB
+ //
+
+ pPacket->SourceConnectId = connectionId;
+
+ //
+ // the SPXEstablishConnection ECB is done!
+ //
+
+ CompleteEcb(pXecb, ECB_CC_SUCCESS);
+ } else {
+ rc = WSAGetLastError();
+ if (rc == WSAEWOULDBLOCK) {
+
+ //
+ // the connect request is in progress. Add it to the queue of
+ // pending SPXEstablishConnection requests (SHOULD ONLY BE 1 PER
+ // CONNECTION!!!) and add the CONNECTION_INFO structure to the
+ // owning SOCKET_INFO structure
+ //
+
+ RequestMutex();
+ QueueEcb(pXecb,
+ &pConnectionInfo->ConnectQueue,
+ CONNECTION_CONNECT_QUEUE
+ );
+ QueueConnection(pSocketInfo, pConnectionInfo);
+ ReleaseMutex();
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_SPXEstablishConnection,
+ IPXDBG_LEVEL_INFO,
+ "VwSPXEstablishConnection: connect() queued\n"
+ ));
+
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_SPXEstablishConnection,
+ IPXDBG_LEVEL_ERROR,
+ "VwSPXEstablishConnection: connect(%x) returns %d\n",
+ s,
+ rc
+ ));
+
+ //
+ // the connect request failed. Deallocate all resources (socket,
+ // CONNECTION_INFO, XECB) and return failure
+ //
+
+ closesocket(pConnectionInfo->Socket);
+ DeallocateConnection(pConnectionInfo);
+ CompleteEcb(pXecb, ECB_CC_CONNECTION_ABORTED);
+ return SPX_CONNECTION_ABORTED;
+ }
+ }
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_SPXEstablishConnection,
+ IPXDBG_LEVEL_INFO,
+ "VwSPXEstablishConnection: returning %04x\n",
+ connectionId
+ ));
+
+ *pSPXConnectionID = connectionId;
+ return SPX_SUCCESS;
+}
+
+
+WORD
+_VwSPXGetConnectionStatus(
+ IN WORD connectionId,
+ OUT LPSPX_CONNECTION_STATS pStats
+ )
+
+/*++
+
+Routine Description:
+
+ Returns buffer crammed full of useful statistics or something (hu hu huh)
+
+ This call is Synchronous
+
+Arguments:
+
+ Inputs
+ connectionId
+ pStats
+
+ Outputs
+ on output, buffer in pStats contains:
+
+ BYTE ConnectionStatus
+ BYTE WatchDogActive
+ WORD LocalConnectionID
+ WORD RemoteConnectionID
+ WORD SequenceNumber
+ WORD LocalAckNumber
+ WORD LocalAllocationNumber
+ WORD RemoteAckNumber
+ WORD RemoteAllocationNumber
+ WORD LocalSocket
+ BYTE ImmediateAddress[6]
+ BYTE RemoteNetwork[4]
+ WORD RetransmissionCount
+ WORD RetransmittedPackets
+ WORD SuppressedPackets
+
+Return Value:
+ 00h Connection is active
+ EEh No such connection
+
+--*/
+
+{
+ int rc;
+ IPX_SPXCONNSTATUS_DATA buf;
+ int buflen = sizeof(buf);
+ LPCONNECTION_INFO pConnectionInfo;
+
+ pConnectionInfo = FindConnection(connectionId);
+ if (!pConnectionInfo) {
+ return SPX_INVALID_CONNECTION;
+ }
+
+ //
+ // get the stats
+ //
+
+ rc = getsockopt(pConnectionInfo->Socket,
+ NSPROTO_IPX,
+ IPX_SPXGETCONNECTIONSTATUS,
+ (char FAR*)&buf,
+ &buflen
+ );
+ if (rc == SOCKET_ERROR) {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_SPXGetConnectionStatus,
+ IPXDBG_LEVEL_ERROR,
+ "VwSPXGetConnectionStatus: getsockopt() returns %d\n",
+ WSAGetLastError()
+ ));
+
+ //
+ // the request to get the stats failed - probably because the socket is
+ // not yet connected. Fill in those bits we know about
+ //
+
+ ZeroMemory((LPBYTE)pStats, sizeof(*pStats));
+ } else {
+
+ //
+ // copy the returned fields
+ //
+
+ pStats->RemoteConnectionId = buf.RemoteConnectionId;
+ pStats->LocalSequenceNumber = buf.LocalSequenceNumber;
+ pStats->LocalAckNumber = buf.LocalAckNumber;
+ pStats->LocalAllocNumber = buf.LocalAllocNumber;
+ pStats->RemoteAckNumber = buf.RemoteAckNumber;
+ pStats->RemoteAllocNumber = buf.RemoteAllocNumber;
+ pStats->LocalSocket = buf.LocalSocket;
+ CopyMemory(&pStats->ImmediateAddress,
+ &buf.ImmediateAddress,
+ sizeof(buf.ImmediateAddress)
+ );
+
+ //
+ // copy remote network as a DWORD. Endian format is same for both
+ //
+
+ *(ULPDWORD)&pStats->RemoteNetwork = *(LPDWORD)&buf.RemoteNetwork;
+ CopyMemory(&pStats->RemoteNode,
+ &buf.RemoteNode,
+ sizeof(buf.RemoteNode)
+ );
+ pStats->RemoteSocket = buf.RemoteSocket;
+ pStats->RetransmissionCount = buf.RetransmissionCount;
+ pStats->EstimatedRoundTripDelay = buf.EstimatedRoundTripDelay;
+ pStats->RetransmittedPackets = buf.RetransmittedPackets;
+ pStats->SuppressedPackets = buf.SuppressedPacket;
+ }
+
+ //
+ // fill in common, known fields
+ //
+
+ pStats->State = pConnectionInfo->State; // not returned by NWIPX
+ pStats->WatchDog = 0x02; // see novell dog-umentation
+ pStats->LocalConnectionId = L2BW(pConnectionInfo->ConnectionId);
+ pStats->LocalSocket = pConnectionInfo->OwningSocket->SocketNumber;
+
+ DUMPSTATS(pStats);
+
+ //
+ // we are returning some kind o stats - therefore success
+ //
+
+ return SPX_SUCCESS;
+}
+
+
+WORD
+_VwSPXInitialize(
+ OUT ULPBYTE pMajorRevisionNumber,
+ OUT ULPBYTE pMinorRevisionNumber,
+ OUT ULPWORD pMaxConnections,
+ OUT ULPWORD pAvailableConnections
+ )
+
+/*++
+
+Routine Description:
+
+ Informs the app that SPX is present on this station
+
+ This call is Synchronous
+
+Arguments:
+
+ Inputs
+
+ Outputs
+ pMajorRevisionNumber - SPX Major revision number
+ pminorRevisionNumber - SPX Minor revision number
+ pMaxConnections - Maximum SPX connections supported
+ normally from SHELL.CFG
+ pAvailableConnections - Available SPX connections
+
+Return Value:
+
+ 00h Not installed
+ FFh Installed
+
+--*/
+
+{
+
+ //
+ // The following values are returned same as per Windows For Workgroups
+ // v3.10
+ //
+
+ *pMajorRevisionNumber = 3;
+ *pMinorRevisionNumber = 10;
+ *pMaxConnections = 128;
+ *pAvailableConnections = *pMaxConnections - 1 ;
+
+ return SPX_INSTALLED;
+}
+
+
+VOID
+_VwSPXListenForConnection(
+ IN BYTE retryCount,
+ IN BYTE watchDogFlag,
+ IN LPECB pEcb,
+ IN ECB_ADDRESS EcbAddress
+ )
+
+/*++
+
+Routine Description:
+
+ Listens for an incoming connection request
+
+ This call is Asynchronous
+
+Arguments:
+
+ Inputs
+ retryCount
+ watchDogFlag
+ pEcb
+ EcbAddress
+
+ Outputs
+ Nothing
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPXECB pXecb = RetrieveXEcb(ECB_TYPE_SPX, pEcb, EcbAddress);
+ LPSOCKET_INFO pSocketInfo;
+ LPCONNECTION_INFO pConnectionInfo;
+ SOCKET sock;
+ SOCKET conn;
+ SOCKADDR_IPX remoteAddress;
+ int rc;
+ BYTE completionCode;
+
+ //
+ // it turns out that SPXListenForConnection doesn't need a fragment to
+ // receive connection info - that is handled by SPXListenForSequencedPacket
+ // that the app has dutifully initiated
+ //
+
+ pSocketInfo = FindSocket(pXecb->SocketNumber);
+ if (!pSocketInfo) {
+ completionCode = ECB_CC_NON_EXISTENT_SOCKET;
+ goto lc_completion_exit;
+ }
+
+ //
+ // if no outstanding IPX operations, change socket to SPX
+ //
+
+ if (!pSocketInfo->SpxSocket) {
+ if (!(pSocketInfo->PendingSends && pSocketInfo->PendingListens)) {
+ rc = ReopenSocket(pSocketInfo);
+ } else {
+ rc = ECB_CC_BAD_LISTEN_REQUEST;
+ }
+ if (rc != SPX_SUCCESS) {
+ completionCode = (BYTE)rc;
+ goto lc_completion_exit;
+ }
+ }
+
+ //
+ // the socket is open for SPX. Allocate a connection/connection ID
+ //
+
+ pConnectionInfo = AllocateConnection(pSocketInfo);
+ if (!pConnectionInfo) {
+ completionCode = ECB_CC_CONNECTION_TABLE_FULL;
+ goto lc_completion_exit;
+ }
+
+ //
+ // put the socket into listening state and try to accept a connection
+ //
+
+ sock = pSocketInfo->Socket;
+
+ //
+ // BUGBUG: if the socket is already listening, will probably return an
+ // error: just queue
+ //
+
+ rc = listen(sock, MAX_LISTEN_QUEUE_SIZE);
+ if (rc != SOCKET_ERROR) {
+
+ int addressLength = sizeof(remoteAddress);
+
+ conn = accept(sock, (LPSOCKADDR)&remoteAddress, &addressLength);
+ if (conn != SOCKET_ERROR) {
+
+ //
+ // we want to receive the frame headers from this socket
+ //
+
+ BOOL bval = TRUE;
+
+ rc = setsockopt(conn,
+ NSPROTO_IPX,
+ IPX_RECVHDR,
+ (char FAR*)&bval,
+ sizeof(bval)
+ );
+ if (rc != SOCKET_ERROR) {
+
+ //
+ // update the CONNECTION_INFO structure with the actual socket
+ // identifier and set the connection state to established
+ //
+
+ pConnectionInfo->Socket = conn;
+ pConnectionInfo->State = CI_ESTABLISHED;
+
+ //
+ // add the CONNECTION_INFO structure to the list of connections owned
+ // by this socket
+ //
+
+ RequestMutex();
+ QueueConnection(pSocketInfo, pConnectionInfo);
+ ReleaseMutex();
+
+ //
+ // update the app's ECB with the connection ID
+ //
+
+ SPX_ECB_CONNECTION_ID(pXecb->Ecb) = pConnectionInfo->ConnectionId;
+
+ //
+ // and with the partner address info
+ //
+
+ CopyMemory(&pXecb->Ecb->DriverWorkspace,
+ &remoteAddress.sa_netnum,
+ sizeof(pXecb->Ecb->DriverWorkspace)
+ );
+ completionCode = ECB_CC_SUCCESS;
+ goto lc_completion_exit;
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_SPXListenForConnection,
+ IPXDBG_LEVEL_ERROR,
+ "VwSPXListenForConnection: setsockopt(RECVHDR) returns %d\n",
+ WSAGetLastError()
+ ));
+
+ //
+ // BUGBUG: value?
+ //
+
+ closesocket(conn);
+ completionCode = ECB_CC_CONNECTION_ABORTED;
+ goto lc_deallocate_exit;
+ }
+ } else {
+ rc = WSAGetLastError();
+ if (rc == WSAEWOULDBLOCK) {
+
+ //
+ // the accept request is in progress. Add it to the queue of
+ // pending SPXListenForConnection requests (SHOULD ONLY BE 1 PER
+ // CONNECTION!!!) and add the CONNECTION_INFO structure to the
+ // owning SOCKET_INFO structure
+ //
+
+ pConnectionInfo->State = CI_WAITING; // waiting for incoming connect
+ RequestMutex();
+ QueueEcb(pXecb,
+ &pConnectionInfo->AcceptQueue,
+ CONNECTION_ACCEPT_QUEUE
+ );
+ QueueConnection(pSocketInfo, pConnectionInfo);
+ pXecb->Ecb->InUse = ECB_IU_AWAITING_CONNECTION;
+ ReleaseMutex();
+ } else {
+
+ //
+ // the accept request failed. Deallocate all resources
+ // (CONNECTION_INFO, XECB) and complete the ECB with a failure
+ // indication
+ //
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_SPXListenForConnection,
+ IPXDBG_LEVEL_ERROR,
+ "VwSPXListenForConnection: accept() returns %d\n",
+ rc
+ ));
+
+ //
+ // BUGBUG: completion code?
+ //
+
+ completionCode = ECB_CC_CONNECTION_ABORTED;
+ goto lc_deallocate_exit;
+ }
+ }
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_SPXListenForConnection,
+ IPXDBG_LEVEL_ERROR,
+ "VwSPXListenForConnection: listen() returns %d\n",
+ WSAGetLastError()
+ ));
+
+ //
+ // listen failed? Bogus. Complete the ECB and we're outta here
+ //
+
+ //
+ // BUGBUG: completion code?
+ //
+
+ completionCode = ECB_CC_CONNECTION_ABORTED;
+ goto lc_deallocate_exit;
+ }
+
+ //
+ // here if we queued the listen request
+ //
+
+ return;
+
+lc_deallocate_exit:
+ DeallocateConnection(pConnectionInfo);
+
+lc_completion_exit:
+ CompleteEcb(pXecb, completionCode);
+}
+
+
+VOID
+_VwSPXListenForSequencedPacket(
+ IN LPECB pEcb,
+ IN ECB_ADDRESS EcbAddress
+ )
+
+/*++
+
+Routine Description:
+
+ Attempts to receive an SPX packet. This call is made against the top-level
+ socket (the socket in SPX-speak, not the connection). We can receive a
+ packet from any connection assigned to this socket. In this function, we
+ just queue the ECB (since there is no return status, we expect that the
+ app has supplied an ESR) and let AES handle it
+
+ This call is Asynchronous
+
+Arguments:
+
+ Inputs
+ pEcb
+ EcbAddress
+
+ Outputs
+ Nothing
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPXECB pXecb = RetrieveXEcb(ECB_TYPE_SPX, pEcb, EcbAddress);
+ LPSOCKET_INFO pSocketInfo;
+ int rc;
+ BOOL dummy ;
+ BYTE status;
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_SPXListenForSequencedPacket,
+ IPXDBG_LEVEL_INFO,
+ "VwSPXListenForSequencedPacket(%04x:%04x) socket=%04x ESR=%04x:%04x\n",
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress),
+ B2LW(pXecb->SocketNumber),
+ HIWORD(pXecb->EsrAddress),
+ LOWORD(pXecb->EsrAddress)
+ ));
+
+ pSocketInfo = FindSocket(pXecb->SocketNumber);
+ if (!pSocketInfo) {
+ status = ECB_CC_NON_EXISTENT_SOCKET;
+ goto lp_exit;
+ }
+
+ //
+ // if no outstanding IPX operations, change socket to SPX
+ //
+
+ if (!pSocketInfo->SpxSocket) {
+ if (!(pSocketInfo->PendingSends && pSocketInfo->PendingListens)) {
+ rc = ReopenSocket(pSocketInfo);
+ } else {
+ rc = ECB_CC_BAD_LISTEN_REQUEST;
+ }
+ if (rc != SPX_SUCCESS) {
+ status = (BYTE)rc;
+ goto lp_exit;
+ }
+ }
+
+ //
+ // the first fragment must be large enough to hold an SPX packet header
+ //
+
+ if ((pXecb->Ecb->FragmentCount == 0)
+ || (ECB_FRAGMENT(pXecb->Ecb, 0)->Length < SPX_HEADER_LENGTH)) {
+ status = ECB_CC_BAD_LISTEN_REQUEST;
+ goto lp_exit;
+ }
+
+ //
+ // we have a socket and the receive buffer looks good. Get a buffer for recv()
+ //
+
+ if (!GetIoBuffer(pXecb, FALSE, SPX_HEADER_LENGTH)) {
+ status = ECB_CC_BAD_LISTEN_REQUEST;
+ goto lp_exit;
+ } else {
+
+ //
+ // when recv() is attempted against this request, it will be the first
+ // time we tried to receive anything to this buffer. That means (if we
+ // get anything) that the buffer will contain the length of the entire
+ // frame
+ //
+
+ pXecb->Flags |= XECB_FLAG_FIRST_RECEIVE;
+ }
+
+ //
+ // mark the VDM ECB as in use
+ //
+
+ pXecb->Ecb->InUse = ECB_IU_LISTENING_SPX;
+
+ //
+ // add this ECB to the queue of listens for the top-level socket and quit
+ //
+
+ RequestMutex();
+
+ if ((pXecb->Ecb->FragmentCount == 1) &&
+ (ECB_FRAGMENT(pXecb->Ecb, 0)->Length == SPX_HEADER_LENGTH))
+ {
+ QueueEcb(pXecb, &pSocketInfo->HeaderQueue, SOCKET_HEADER_QUEUE);
+ }
+ else
+ {
+ QueueEcb(pXecb, &pSocketInfo->ListenQueue, SOCKET_LISTEN_QUEUE);
+ }
+
+ ReleaseMutex();
+
+ //
+ // see if we are ready to rock
+ //
+
+ CheckPendingSpxRequests(&dummy);
+ return;
+
+lp_exit:
+ CompleteOrQueueEcb(pXecb, status);
+}
+
+
+VOID
+_VwSPXSendSequencedPacket(
+ IN WORD connectionId,
+ IN LPECB pEcb,
+ IN ECB_ADDRESS EcbAddress
+ )
+
+/*++
+
+Routine Description:
+
+ Sends a packet on an SPX connection
+
+ This call is Asynchronous
+
+Arguments:
+
+ Inputs
+ connectionId
+ pEcb
+ EcbAddress
+
+ Outputs
+ Nothing
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPXECB pXecb = RetrieveXEcb(ECB_TYPE_SPX, pEcb, EcbAddress);
+ LPCONNECTION_INFO pConnectionInfo;
+ int rc;
+ BOOL addToQueue;
+ LPSPX_PACKET pPacket;
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_SPXSendSequencedPacket,
+ IPXDBG_LEVEL_INFO,
+ "VwSPXSendSequencedPacket(%04x:%04x) Connection=%04x ESR=%04x:%04x\n",
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress),
+ connectionId,
+ HIWORD(pXecb->EsrAddress),
+ LOWORD(pXecb->EsrAddress)
+ ));
+
+ IPXDUMPECB((pXecb->Ecb,
+ HIWORD(pXecb->EcbAddress),
+ LOWORD(pXecb->EcbAddress),
+ ECB_TYPE_SPX,
+ TRUE,
+ TRUE,
+ IS_PROT_MODE(pXecb)
+ ));
+
+ //
+ // find the connection. No need to check if this is an SPX socket: if we
+ // can't find the connection, its a bad connection, else the socket must
+ // be open for SPX
+ //
+
+ pConnectionInfo = FindConnection(connectionId);
+ if (!pConnectionInfo || (pConnectionInfo->State != CI_ESTABLISHED)) {
+ CompleteOrQueueEcb(pXecb, ECB_CC_INVALID_CONNECTION);
+ return;
+ }
+
+ //
+ // the first fragment must be large enough to hold an SPX packet header
+ //
+
+ if ((pXecb->Ecb->FragmentCount == 0)
+ || (ECB_FRAGMENT(pXecb->Ecb, 0)->Length < SPX_HEADER_LENGTH)) {
+ CompleteOrQueueEcb(pXecb, ECB_CC_BAD_SEND_REQUEST);
+ return;
+ }
+ if (!GetIoBuffer(pXecb, TRUE, SPX_HEADER_LENGTH)) {
+ CompleteOrQueueEcb(pXecb, ECB_CC_BAD_SEND_REQUEST);
+ return;
+ }
+
+ pPacket = (LPSPX_PACKET)GET_FAR_POINTER(
+ &(ECB_FRAGMENT(pXecb->Ecb, 0)->Address),
+ IS_PROT_MODE(pXecb)
+ );
+
+ //
+ // fill in the following fields in the SPX header:
+ //
+ // Checksum
+ // Length
+ // TransportControl
+ // Source (network, node, socket)
+ //
+ // BUGBUG: Does real IPX modify these fields in app memory?
+ // If so, does the app expect modified fields?
+ // If not, we need to always copy then modify memory,
+ // even if only 1 fragment
+ //
+
+ pPacket->Checksum = 0xFFFF;
+
+ //
+ // since the transport adds the SPX header, we subtracted the length of
+ // the header from our transmit length; add it back when updating the
+ // header in the app's space
+ //
+
+ pPacket->Length = L2BW(pXecb->Length + SPX_HEADER_LENGTH);
+ pPacket->TransportControl = 0;
+ CopyMemory((LPBYTE)&pPacket->Source,
+ &MyInternetAddress.sa_netnum,
+ sizeof(MyInternetAddress.sa_netnum)
+ + sizeof(MyInternetAddress.sa_nodenum)
+ );
+ pPacket->Source.Socket = pConnectionInfo->OwningSocket->SocketNumber;
+
+ //
+ // if we allocated a buffer then there is >1 fragment. Collect them
+ //
+
+ if (pXecb->Flags & XECB_FLAG_BUFFER_ALLOCATED) {
+ GatherData(pXecb, SPX_HEADER_LENGTH);
+ }
+
+ //
+ // BUGBUG: length check: >576 == error??
+ //
+
+ //
+ // mark the VDM ECB as in use
+ //
+
+ pXecb->Ecb->InUse = ECB_IU_SENDING;
+
+ //
+ // if there is a send queued on this connection already, add this request
+ // to the back of the queue and let AES do the rest
+ //
+
+ RequestMutex();
+ if (pConnectionInfo->SendQueue.Head) {
+ addToQueue = TRUE;
+ } else {
+
+ int dataStreamType;
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_SPXSendSequencedPacket,
+ IPXDBG_LEVEL_INFO,
+ "VwSPXSendSequencedPacket: sending %d (0x%x) bytes from %08x\n",
+ pXecb->Length,
+ pXecb->Length,
+ pXecb->Data
+ ));
+
+ //
+ // no outstanding sends queued for this connection. Start sending this
+ // packet
+ //
+
+ dataStreamType = (int)pPacket->DataStreamType;
+ rc = setsockopt(pConnectionInfo->Socket,
+ NSPROTO_IPX,
+ IPX_DSTYPE,
+ (char FAR*)&dataStreamType,
+ sizeof(dataStreamType)
+ );
+ if (rc != SOCKET_ERROR) {
+
+ //
+ // if the app set the END_OF_MESSAGE bit in the ConnectionControl
+ // field then set the flags to 0: NWLink will automatically set the
+ // end-of-message bit in the packet; otherwise set flags to MSG_PARTIAL
+ // to indicate to NWLink that it *shouldn't* set the bit in the packet
+ //
+
+ int flags = (pPacket->ConnectionControl & SPX_END_OF_MESSAGE)
+ ? 0
+ : MSG_PARTIAL
+ ;
+
+ rc = send(pConnectionInfo->Socket, pXecb->Data, pXecb->Length, flags);
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_SPXSendSequencedPacket,
+ IPXDBG_LEVEL_INFO,
+ "VwSPXSendSequencedPacket: send() returns %d\n",
+ rc
+ ));
+
+ if (rc == pXecb->Length) {
+
+ //
+ // all data sent
+ //
+
+ CompleteOrQueueIo(pXecb, ECB_CC_SUCCESS);
+ addToQueue = FALSE;
+ } else if (rc == SOCKET_ERROR) {
+ rc = WSAGetLastError();
+ if (rc == WSAEWOULDBLOCK) {
+
+ //
+ // can't send right now. Queue it for AES
+ //
+
+ addToQueue = TRUE;
+ }
+ } else {
+
+ //
+ // partial data sent. Update the buffer pointer and length fields
+ // and queue this request
+ //
+
+ pXecb->Data += rc;
+ pXecb->Length -= (WORD)rc;
+ addToQueue = TRUE;
+ }
+ } else {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_SPXSendSequencedPacket,
+ IPXDBG_LEVEL_ERROR,
+ "VwSPXSendSequencedPacket: setsockopt(IPX_DSTYPE) returns %d\n",
+ WSAGetLastError()
+ ));
+
+ CompleteOrQueueIo(pXecb, ECB_CC_BAD_REQUEST);
+ }
+ }
+
+ //
+ // if addToQueue set then we can't do anything right now - add this
+ // request to the send queue
+ //
+
+ if (addToQueue) {
+
+ IPXDBGPRINT((__FILE__, __LINE__,
+ FUNCTION_SPXSendSequencedPacket,
+ IPXDBG_LEVEL_WARNING,
+ "VwSPXSendSequencedPacket: adding XECB %08x to send queue\n",
+ pXecb
+ ));
+
+ QueueEcb(pXecb, &pConnectionInfo->SendQueue, CONNECTION_SEND_QUEUE);
+ }
+ ReleaseMutex();
+}
+
+
+VOID
+_VwSPXTerminateConnection(
+ IN WORD SPXConnectionID,
+ IN LPECB pEcb,
+ IN ECB_ADDRESS EcbAddress
+ )
+
+/*++
+
+Routine Description:
+
+ Terminates a connection
+
+Arguments:
+
+ SPXConnectionID - connection to terminate
+ pEcb - pointer to 16-bit ECB to use
+ EcbAddress - address of 16-bit ECB in 16:16 format
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ LPCONNECTION_INFO pConnectionInfo;
+ LPXECB pXecb = RetrieveXEcb(ECB_TYPE_SPX, pEcb, EcbAddress);
+ BYTE status;
+ BYTE completionCode;
+
+ RequestMutex();
+ pConnectionInfo = FindConnection(SPXConnectionID);
+ if (pConnectionInfo) {
+
+ //
+ // once dequeued, pConnectionInfo no longer points to OwningSocket
+ //
+
+ WORD socketNumber = pConnectionInfo->OwningSocket->SocketNumber;
+
+ DequeueConnection(pConnectionInfo->OwningSocket, pConnectionInfo);
+ if ((pXecb->Ecb->FragmentCount >= 1)
+ && (ECB_FRAGMENT(pXecb->Ecb, 0)->Length >= SPX_HEADER_LENGTH)) {
+
+ LPSPX_PACKET pPacket;
+ SOCKADDR_IPX remote;
+ int remoteLen = sizeof(remote);
+
+ completionCode = ECB_CC_CONNECTION_TERMINATED;
+ status = ECB_CC_SUCCESS;
+
+ //
+ // fill in the packet header: this would normally contain the
+ // acknowledgement packet from the remote partner
+ //
+
+ pPacket = (LPSPX_PACKET)GET_FAR_POINTER(
+ &(ECB_FRAGMENT(pXecb->Ecb, 0)->Address),
+ IS_PROT_MODE(pXecb)
+ );
+ pPacket->Checksum = 0xffff;
+ pPacket->Length = L2BW(SPX_HEADER_LENGTH);
+ pPacket->TransportControl = 0;
+ pPacket->PacketType = SPX_PACKET_TYPE;
+ getpeername(pConnectionInfo->Socket, (LPSOCKADDR)&remote, &remoteLen);
+ CopyMemory((LPBYTE)&pPacket->Destination,
+ (LPBYTE)&remote.sa_netnum,
+ sizeof(NETWARE_ADDRESS)
+ );
+ CopyMemory((LPBYTE)&pPacket->Source,
+ (LPBYTE)&MyInternetAddress.sa_netnum,
+ sizeof(INTERNET_ADDRESS)
+ );
+ pPacket->Source.Socket = socketNumber;
+ pPacket->ConnectionControl = SPX_ACK_REQUIRED;
+ pPacket->DataStreamType = SPX_DS_TERMINATE;
+ pPacket->SourceConnectId = pConnectionInfo->ConnectionId;
+ pPacket->DestinationConnectId = 0;
+ pPacket->SequenceNumber = 0;
+ pPacket->AckNumber = 0;
+ pPacket->AllocationNumber = 0;
+
+ } else {
+ completionCode = ECB_CC_CONNECTION_ABORTED;
+ status = ECB_CC_BAD_REQUEST;
+ }
+ AbortOrTerminateConnection(pConnectionInfo, completionCode);
+ } else {
+ status = ECB_CC_INVALID_CONNECTION;
+ }
+ ReleaseMutex();
+ CompleteOrQueueEcb(pXecb, status);
+}
diff --git a/private/nw/vwipxspx/dll/vwspx.h b/private/nw/vwipxspx/dll/vwspx.h
new file mode 100644
index 000000000..68e1b63fa
--- /dev/null
+++ b/private/nw/vwipxspx/dll/vwspx.h
@@ -0,0 +1,62 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ vwspx.h
+
+Abstract:
+
+ Contains function prototypes for VWSPX.C
+
+Author:
+
+ Richard L Firth (rfirth) 25-Oct-1993
+
+Revision History:
+
+ 25-Oct-1993 rfirth
+ Created
+
+--*/
+
+VOID
+VwSPXAbortConnection(
+ VOID
+ );
+
+VOID
+VwSPXEstablishConnection(
+ VOID
+ );
+
+VOID
+VwSPXGetConnectionStatus(
+ VOID
+ );
+
+VOID
+VwSPXInitialize(
+ VOID
+ );
+
+VOID
+VwSPXListenForConnection(
+ VOID
+ );
+
+VOID
+VwSPXListenForSequencedPacket(
+ VOID
+ );
+
+VOID
+VwSPXSendSequencedPacket(
+ VOID
+ );
+
+VOID
+VwSPXTerminateConnection(
+ VOID
+ );
diff --git a/private/nw/vwipxspx/dll/vwvdm.h b/private/nw/vwipxspx/dll/vwvdm.h
new file mode 100644
index 000000000..2fd23ba22
--- /dev/null
+++ b/private/nw/vwipxspx/dll/vwvdm.h
@@ -0,0 +1,151 @@
+/*++
+
+Copyright (c) 1991 Microsoft Corporation
+
+Module Name:
+
+ vwvdm.h
+
+Abstract:
+
+ Contains macros, manifests, includes for dealing with VDM
+
+Author:
+
+ Richard L Firth (rfirth) 25-Oct-1993
+
+Revision History:
+
+ 25-Oct-1993 rfirth
+ Created
+
+--*/
+
+#ifndef _VWVDM_H_
+#define _VWVDM_H_
+
+//
+// unaligned pointers - non-Intel platforms must use UNALIGNED to access data
+// in VDM which can (and most likely will) be aligned on odd-byte and word
+// boundaries
+//
+
+#ifndef ULPBYTE
+#define ULPBYTE BYTE UNALIGNED FAR*
+#endif
+
+#ifndef ULPWORD
+#define ULPWORD WORD UNALIGNED FAR*
+#endif
+
+#ifndef ULPDWORD
+#define ULPDWORD DWORD UNALIGNED FAR*
+#endif
+
+#ifndef ULPVOID
+#define ULPVOID VOID UNALIGNED FAR*
+#endif
+
+//
+// VDM macros
+//
+
+#define MSW_PE 0x0001 // Protect-mode Enable bit in x86 Machine Status Word
+
+//
+// POINTER_FROM_WORDS - returns 32-bit pointer to address in VDM memory described
+// by seg:off. If seg:off = 0:0, returns NULL
+//
+
+#define POINTER_FROM_WORDS(seg, off, size) \
+ _inlinePointerFromWords((WORD)(seg), (WORD)(off), (WORD)(size))
+
+//
+// _inlinePointerFromWords - the POINTER_FROM_WORDS macro is inefficient if the
+// arguments are calls to eg. getES(), getBX() - the calls are made twice if
+// the pointer turns out to be non-zero. Use an inline function to achieve the
+// same results, but only call function arguments once
+//
+
+__inline LPVOID _inlinePointerFromWords(WORD seg, WORD off, WORD size) {
+ return (seg | off)
+ ? (LPVOID)GetVDMPointer((ULONG)(MAKELONG(off, seg)), size, (CHAR)((getMSW() & MSW_PE) ? TRUE : FALSE))
+ : NULL;
+}
+
+//
+// GET_POINTER - does the same thing as POINTER_FROM_WORDS, but we know beforehand
+// which processor mode we are in
+//
+
+#define GET_POINTER(seg, off, size, mode) \
+ _inlineGetPointer((WORD)(seg), (WORD)(off), (WORD)(size), (BOOL)(mode))
+
+__inline LPVOID _inlineGetPointer(WORD seg, WORD off, WORD size, BOOL mode) {
+ return (seg | off)
+ ? (LPVOID)GetVDMPointer(MAKELONG(off, seg), size, (UCHAR)mode)
+ : NULL;
+}
+
+//
+// GET_FAR_POINTER - same as READ_FAR_POINTER with the same proviso as for
+// GET_POINTER
+//
+
+#define GET_FAR_POINTER(addr, mode) ((LPBYTE)(GET_POINTER(GET_SELECTOR(addr), GET_OFFSET(addr), sizeof(LPBYTE), mode)))
+
+//
+// GET_SELECTOR - retrieves the selector word from the intel 32-bit far pointer
+// (DWORD) pointed at by <pointer> (remember: stored as offset, segment)
+//
+
+#define GET_SELECTOR(pointer) READ_WORD((LPWORD)(pointer)+1)
+
+//
+// GET_SEGMENT - same as GET_SELECTOR
+//
+
+#define GET_SEGMENT(pointer) GET_SELECTOR(pointer)
+
+//
+// GET_OFFSET - retrieves the offset word from an intel 32-bit far pointer
+// (DWORD) pointed at by <pointer> (remember: stored as offset, segment)
+//
+
+#define GET_OFFSET(pointer) READ_WORD((LPWORD)(pointer))
+
+//
+// READ_FAR_POINTER - read the pair of words in VDM memory, currently pointed at
+// by a 32-bit flat pointer and convert them to a 32-bit flat pointer
+//
+
+#define READ_FAR_POINTER(addr) ((LPBYTE)(POINTER_FROM_WORDS(GET_SELECTOR(addr), GET_OFFSET(addr), sizeof(LPBYTE))))
+
+//
+// READ_WORD - read a single 16-bit little-endian word from VDM memory. On non
+// Intel platforms, use unaligned pointer to access data
+//
+
+#define READ_WORD(addr) (*((ULPWORD)(addr)))
+
+//
+// READ_DWORD - read a 4-byte little-endian double word from VDM memory. On non
+// Intel platforms, use unaligned pointer to access data
+//
+
+#define READ_DWORD(addr) (*((ULPDWORD)(addr)))
+
+//
+// ARRAY_ELEMENTS - gives the number of elements of a particular type in an
+// array
+//
+
+#define ARRAY_ELEMENTS(a) (sizeof(a)/sizeof((a)[0]))
+
+//
+// LAST_ELEMENT - returns the index of the last element in array
+//
+
+#define LAST_ELEMENT(a) (ARRAY_ELEMENTS(a)-1)
+
+#endif // _VWVDM_H_
diff --git a/private/nw/vwipxspx/tsr/asmmacro.inc b/private/nw/vwipxspx/tsr/asmmacro.inc
new file mode 100644
index 000000000..0b7b2db16
--- /dev/null
+++ b/private/nw/vwipxspx/tsr/asmmacro.inc
@@ -0,0 +1,343 @@
+;++
+;
+;Copyright (c) 1991 Microsoft Corporation
+;
+;Module Name:
+;
+; asmmacro.inc
+;
+;Abstract:
+;
+; Contains macros to extend masm functionality:
+;
+; jmpc
+; jmpnc
+; jmpne
+; jmps
+; _mkjmp
+;
+;
+;Author:
+;
+; Richard L Firth (rfirth) 24-Sep-1991
+;
+;Environment:
+;
+; DOS application mode only
+;
+;Revision History:
+;
+; 24-Sep-1991 rfirth
+; Created
+;
+;--
+
+
+
+DEFINED_BIT=020h
+;ISDEFINED equ %(.type <thing> and DEFINED_BIT)
+LABEL_DEFINED equ <(.type &label and DEFINED_BIT)>
+
+DEBUG_MACROS = 0
+;DEBUG_MACROS = 1
+
+
+;*** jmpa
+;*
+;* jump to label if above. Label can be short (+129, -126 from
+;* the first byte of the current jump instruction, if it is a short - ie
+;* byte - jump) or near
+;*
+;* ENTRY label - to jump to
+;*
+;* EXIT nothing
+;*
+;* USES nothing
+;*
+;* ASSUMES 286+
+;*
+;***
+
+jmpa macro label
+ _mkjmp ja,jna,&label
+endm
+
+;*** jmpc
+;*
+;* jump to label if below. Label can be short (+129, -126 from
+;* the first byte of the current jump instruction, if it is a short - ie
+;* byte - jump) or near
+;*
+;* ENTRY label - to jump to
+;*
+;* EXIT nothing
+;*
+;* USES nothing
+;*
+;* ASSUMES 286+
+;*
+;***
+
+jmpb macro label
+ _mkjmp jb,jnb,&label
+endm
+
+;*** jmpc
+;*
+;* jump to label if carry flag set. Label can be short (+129, -126 from
+;* the first byte of the current jump instruction, if it is a short - ie
+;* byte - jump) or near
+;*
+;* ENTRY label - to jump to
+;*
+;* EXIT nothing
+;*
+;* USES nothing
+;*
+;* ASSUMES 286+
+;*
+;***
+
+jmpc macro label
+ _mkjmp jc,jnc,&label
+endm
+
+
+
+;*** jmpnc
+;*
+;* jump to label if carry flag NOT set. Label can be short (+129, -126 from
+;* the first byte of the current jump instruction, if it is a short - ie
+;* byte - jump) or near
+;*
+;* ENTRY label - to jump to
+;*
+;* EXIT nothing
+;*
+;* USES nothing
+;*
+;* ASSUMES 286+
+;*
+;***
+
+jmpnc macro label
+ _mkjmp jnc,jc,&label
+endm
+
+
+
+;*** jmpne
+;*
+;* jump to label if zero flag NOT set. Label can be short (+129, -126 from
+;* the first byte of the current jump instruction, if it is a short - ie
+;* byte - jump) or near
+;*
+;* ENTRY label - to jump to
+;*
+;* EXIT nothing
+;*
+;* USES nothing
+;*
+;* ASSUMES 286+
+;*
+;***
+
+jmpne macro label
+ _mkjmp jne,je,&label
+endm
+
+
+
+;*** jmpe
+;*
+;* jump to label if zero flag set. Label can be short (+129, -126 from
+;* the first byte of the current jump instruction, if it is a short - ie
+;* byte - jump) or near
+;*
+;* ENTRY label - to jump to
+;*
+;* EXIT nothing
+;*
+;* USES nothing
+;*
+;* ASSUMES 286+
+;*
+;***
+
+jmpe macro label
+ _mkjmp je,jne,&label
+endm
+
+
+
+;*** jmps
+;*
+;* jump to label. Label can be short (+129, -126 from
+;* the first byte of the current jump instruction, if it is a short - ie
+;* byte - jump) or near
+;*
+;* ENTRY label - to jump to
+;*
+;* EXIT nothing
+;*
+;* USES nothing
+;*
+;* ASSUMES 286+
+;*
+;***
+
+jmps macro label
+ local l,dist
+dist=&label-$
+if1
+if (.type label and DEFINED_BIT)
+if ((dist gt 129) or (dist lt -126))
+if DEBUG_MACROS
+ %out pass1: &label defined and near
+endif
+ jmp &label
+else
+if DEBUG_MACROS
+ %out pass1: &label defined and short
+endif
+ jmp short &label
+endif
+else
+if DEBUG_MACROS
+ %out pass1: &label not defined
+endif
+ org $+3
+endif
+else
+if ((dist gt 129) or (dist lt -126))
+if DEBUG_MACROS
+ %out pass2: &label defined and near
+endif
+ jmp &label
+else
+if DEBUG_MACROS
+ %out pass2: &label defined and short
+endif
+ jmp short &label
+ org $+1
+endif
+endif
+l:
+endm
+
+
+
+;*** _mkjmp
+;*
+;* Make a jmp<?> macro. Generate instruction sequence for jump with or
+;* without conditional test. Jump may be short (+127/-128 bytes) or near
+;* (+32767/-32768 bytes)
+;*
+;* ENTRY is - short jump instruction
+;* in - near jump instruction
+;* label - to jump to
+;*
+;* EXIT nothing
+;*
+;* USES nothing
+;*
+;* ASSUMES 286+
+;*
+;***
+
+_put macro s,v
+if2
+if DEBUG_MACROS
+%out s = v
+endif
+endif
+endm
+
+_mkjmp macro is, in, label
+ local l
+
+;;
+;; if pass 1 and label is already known, generate correct instruction
+;;
+
+if1
+if (.type &label and DEFINED_BIT)
+
+;;
+;; if label is too far away for short jump instruction, make jump <condition>
+;; into jump <NOT condition> round jump to label followed by a near jump to
+;; label
+;;
+
+if (((&label - $) gt 129) or ((&label - $) lt -126))
+ &in l ;; short jump, NOT condition
+ jmp &label ;; jump to where we want to go
+else
+ &is &label ;; short jump
+endif
+
+;;
+;; if pass 1 and we don't know about the label yet, adjust the program
+;; counter by the max. number of bytes taken up by this macro (5 - 2 for
+;; short jump, 3 for near jump)
+;;
+
+else
+ nop
+ nop
+ nop
+ nop
+ nop
+endif
+
+;;
+;; pass 2 - do same stuff as for pass 1
+;;
+
+else
+if (((&label - $) gt 129) or ((&label - $) lt -126))
+ if ((&label-$) gt 129)
+ _put <label distance>, %(&label-$)
+ else
+ _put <label distance>, %($-&label)
+ endif
+ &in l
+ jmp &label
+else
+
+;;
+;; label is within +127/-128 bytes of current instruction - generate short
+;; jump instruction and put the program counter forward past the space
+;; reserved during pass 1
+;;
+
+ _put <label distance>, %(&label-$)
+ &is &label
+ nop
+ nop
+ nop
+endif
+endif
+l:
+endm
+
+
+
+oldjmps macro label
+if2
+if (((&label - $) gt 127) or (($ - &label) lt -128))
+ jmp short l
+ jmp &label
+else
+ jmp short &label
+ org $+3
+endif
+else
+;;
+;; if this is pass 1 just take up max amount of space so phases don't get
+;; screwed
+;;
+ org $+5
+endif
+l:
+endm
diff --git a/private/nw/vwipxspx/tsr/debugmac.inc b/private/nw/vwipxspx/tsr/debugmac.inc
new file mode 100644
index 000000000..45fe236e5
--- /dev/null
+++ b/private/nw/vwipxspx/tsr/debugmac.inc
@@ -0,0 +1,356 @@
+;++
+;
+;Copyright (c) 1991 Microsoft Corporation
+;
+;Module Name:
+;
+; debugmac.inc
+;
+;Abstract:
+;
+; Contains debugging macros:
+;
+; DbgBreakPoint
+; DbgUnsupported
+; DbgDEBUG
+; DbgPrint
+; DbgPrintTty
+; DbgPrintString
+; DbgPrintHexDword
+; DbgPrintHexWord
+; DbgPrintHexByte
+; DbgPrintNearPointer
+; DbgPrintFarPointer
+;
+;Author:
+;
+; Richard L Firth (rfirth) 13-Sep-1991
+;
+;Environment:
+;
+; DOS application mode only
+;
+;[Notes:]
+;
+; optional-notes
+;
+;Revision History:
+;
+; 13-Sep-1991 rfirth
+; Created
+;
+;--
+
+
+;*** DbgBreakPoint
+;*
+;* Same as NT routine of same name. No-op in non-DEBUG version
+;*
+;* ENTRY
+;*
+;* EXIT
+;*
+;* RETURNS
+;*
+;* ASSUMES
+;*
+;***
+
+DbgBreakPoint macro
+if DEBUG
+ int 3
+endif
+endm
+
+;*** DbgUnsupported
+;*
+;* Causes the 32-bit support code to display a message about an unsupported
+;* service code, and dumps the 16-bit registers. Used to discover when an
+;* unsupported int 2f/11 call or int 21/5f call is being made
+;*
+;* ENTRY
+;*
+;* EXIT
+;*
+;* RETURNS
+;*
+;* ASSUMES
+;*
+;***
+
+DbgUnsupported macro
+if DEBUG
+ SVC -1
+endif
+endm
+
+;*** DbgDEBUG
+;*
+;* Prints the string "DEBUG: " to console using Bios Int 10h/ah=0eh
+;*
+;* ENTRY nothing
+;*
+;* EXIT nothing
+;*
+;* USES ax
+;*
+;* ASSUMES 286+
+;*
+;***
+
+DbgDEBUG macro
+ mov ax,(14 shl 8) + 'D'
+ int 10h
+ mov al,'E'
+ int 10h
+ mov al,'B'
+ int 10h
+ mov al,'U'
+ int 10h
+ mov al,'G'
+ int 10h
+ mov al,':'
+ int 10h
+ mov al,' '
+ int 10h
+endm
+
+
+
+;*** DbgCrLf
+;*
+;* Prints CR,LF to console using Bios Int 10h/ah=0eh
+;*
+;* ENTRY nothing
+;*
+;* EXIT nothing
+;*
+;* USES nothing
+;*
+;* ASSUMES 286+
+;*
+;***
+
+DbgCrLf macro
+ push ax
+ mov ax,(14 shl 8) + 13
+ int 10h
+ mov al,10
+ int 10h
+ pop ax
+endm
+
+
+
+;*** DbgPrint
+;*
+;* Prints an ASCIZ string to console using Bios Int 10h
+;*
+;* ENTRY string - address of ASCIZ string to print
+;*
+;* EXIT nothing
+;*
+;* USES nothing
+;*
+;* ASSUMES 286+
+;*
+;***
+
+DbgPrint macro string
+if DEBUG ;; no macro if not debug version
+ pushf ;; save regs used by DbgPrintTty
+ push ax
+ push bx
+ push si
+ push ds
+ mov ax,seg string
+ mov ds,ax
+ mov si,offset string;; ds:si = address of string
+ DbgPrintTty ;; display it on console
+ pop ds
+ pop si
+ pop bx
+ pop ax
+ popf
+endif
+endm
+
+
+
+;*** DbgPrintTty
+;*
+;* Prints an ASCIZ string in ds:si to console using Bios Int 10h
+;*
+;* ENTRY page - if present defines which Bios video page to use
+;* Defaults to 0
+;* ds:si - address of ASCIZ string to print
+;*
+;* EXIT nothing
+;*
+;* USES al, bh, si, flags
+;*
+;* ASSUMES 286+
+;*
+;***
+
+DbgPrintTty macro page
+ local l1,l2
+
+if DEBUG ;; no macro if not debug version
+ mov ah,14 ;; Bios Int write character as TTY function
+ifb <page>
+ sub bh,bh
+else
+ mov bh,page
+endif
+ cld ;; autoincrement lodsb
+l1: lodsb ;; al := next character; si := next character addr
+ or al,al ;; eof string?
+ jz l2 ;; yes
+ int 10h ;; display it to console
+ jmp short l1 ;; go round again
+l2:
+endif
+endm
+
+
+
+;*** DbgPrintString
+;*
+;* Prints a string to console using Bios Int 10h. Note that this macro
+;* does not do printf style substitutions. The string "DEBUG: " will be
+;* displayed if the banner parm is not blank
+;*
+;* ENTRY string - character string. Needn't be zero-terminated
+;* banner - the "DEBUG: " banner will be printed if not blank
+;*
+;* EXIT nothing
+;*
+;* USES nothing
+;*
+;* ASSUMES 286+
+;*
+;***
+
+DbgPrintString macro string, banner
+ local s1
+ local l1
+
+if DEBUG ;; no macro if not debug version
+ jmp short l1
+s1 db &string,0
+l1: pushf ;; don't destroy direction flag
+ pusha ;; save gp regs
+ifb <banner>
+ DbgDEBUG ;; Display "DEBUG: "
+endif
+ push ds ;; save user's data seg
+ push cs
+ pop ds ;; ds == cs
+ mov si,offset cs:s1 ;; si := string offset
+ DbgPrintTty ;; display ds:si to console
+ pop ds ;; restore user's data seg
+ popa ;; restore gp regs
+ popf ;; restore direction flag+
+endif
+endm
+
+
+
+;*** DbgPrintHexDword
+;*
+;* Prints a dword to console in hex notation using Bios Int 10h
+;*
+;* ENTRY dword - dword to print
+;*
+;* EXIT nothing
+;*
+;* USES nothing
+;*
+;* ASSUMES 286+
+;*
+;***
+
+DbgPrintHexDword macro dword
+if DEBUG ;; no macro if not debug version
+ DbgPrint <"DbgPrintHexDword not implemented yet",13,10>
+endif
+endm
+
+
+
+;*** DbgPrintHexWord
+;*
+;* Prints a word to console in hex notation using Bios Int 10h
+;*
+;* ENTRY word - to print. Can be memory or register
+;*
+;* EXIT nothing
+;*
+;* USES nothing
+;*
+;* ASSUMES 286+
+;*
+;***
+
+DbgPrintHexWord macro word
+ local l1, l2
+if DEBUG ;; no macro if not debug version
+ pushf ;; don't use any registers
+ push ax
+ push cx
+ push dx
+ifdifi <word>,<ax>
+ mov ax,word
+endif
+ mov cx,4
+l1: rol ax,4
+ mov dx,ax
+ and al,0fh
+ cmp al,9
+ jle l2
+ add al,'a'-('9'+1)
+l2: add al,'0'
+ mov ah,14
+ int 10h
+ mov ax,dx
+ loop l1
+ pop dx
+ pop cx
+ pop ax
+ popf
+endif
+endm
+
+
+
+;*** DbgPrintHexByte
+;*
+;* Prints a string to console using Bios Int 10h. Note that this macro
+;* does not do printf style substitutions
+;*
+;* ENTRY string - character string. Needn't be zero-terminated
+;*
+;* EXIT
+;*
+;* USES nothing
+;*
+;* ASSUMES 286+
+;*
+;***
+
+DbgPrintHexByte macro byte
+if DEBUG ;; no macro if not debug version
+ DbgPrint <"DbgPrintHexByte not implemented yet",13,10>
+endif
+endm
+
+
+
+DbgPrintNearPointer macro nearptr
+endm
+
+
+
+DbgPrintFarPointer macro farptr
+endm
diff --git a/private/nw/vwipxspx/tsr/makefile b/private/nw/vwipxspx/tsr/makefile
new file mode 100644
index 000000000..cd181de7d
--- /dev/null
+++ b/private/nw/vwipxspx/tsr/makefile
@@ -0,0 +1,159 @@
+!IF 0
+
+Copyright (c) 1991 & 1993 Microsoft Corporation
+
+Module Name:
+
+ makefile
+
+Abstract:
+
+ makefile for Vdm NetWare Redir program
+
+Author:
+
+ Richard L Firth (rfirth) 13-Sep-1991
+
+Revision History:
+
+ 13-Sep-1991 rfirth
+ Created
+
+!ENDIF
+
+
+
+.SUFFIXES:
+.SUFFIXES: .asm .h
+
+OBJPATH = obj
+
+ASM = masm
+!IFDEF NTVDM_BASED_BUILD
+LINK = link16
+!ELSE
+LINK = link
+!ENDIF
+
+#
+# set the country info
+#
+
+!IFNDEF COUNTRY
+COUNTRY=usa
+!ENDIF
+
+#
+# convert NTDEBUG into DEBUG flag. NTDEBUG can be not present or retail, either
+# of which mean no debugging; or ntsd, cvp or sym, which means debugging support
+# required
+#
+
+!IFDEF NTDEBUG
+!IF "$(NTDEBUG)" == "retail"
+DEBUGGING=0
+!ELSE
+DEBUGGING=1
+!ENDIF
+!ELSE
+DEBUGGING=0
+!ENDIF
+
+#
+# assembler and linker debugging options
+#
+
+!IF $(DEBUGGING)
+ASMDEBUG = /DDEBUG=1 /Zi
+LINKDEBUG = /CO
+!ELSE
+ASMDEBUG = /DDEBUG=0
+LINKDEBUG =
+!ENDIF
+ASMINC = /I. /I..\..\inc /I..\..\..\mvdm\dos\v86\inc /I\nt\public\sdk\inc
+ASMFLAGS = /Mx
+LINKFLAGS = /MAP /CP:1
+
+#
+# any other non-debug related options (for assembler) go in USERDEFS
+#
+
+USERDEFS = /DCALL_DOS
+
+#
+# Inference rules - asm to obj, h to inc
+#
+
+.asm{$(OBJPATH)\}.obj:
+ $(ASM) $(ASMINC) $(ASMDEBUG) $(USERDEFS) $<,$@;
+
+.h.inc:
+ h2inc $< -o $*.inc
+
+.asm.lst:
+ $(ASM) $(ASMINC) $(ASMDEBUG) $(USERDEFS) /d /L $<;
+
+#
+# what it is we're building
+#
+
+TARGET = $(OBJPATH)\vwipxspx.exe
+MAPFILE = $(TARGET:.exe=.map)
+DEFFILE = ;
+
+OBJS = $(OBJPATH)\vwipxspx.obj
+
+LIBS =
+
+#
+# how to build it
+#
+
+all: makedir $(TARGET)
+
+$(TARGET): $(OBJS)
+ $(LINK) @<<
+$(OBJS)
+$(TARGET) $(LINKFLAGS) $(LINKDEBUG)
+$(MAPFILE)
+$(LIBS)
+$(DEFFILE)
+<<
+
+#
+# where to put it
+#
+
+ binplace $(TARGET)
+
+#
+# clean build - delete all objs
+#
+
+#clean: makedir clean2
+clean: clean2
+
+clean2:
+ if exist $(OBJPATH)\*.obj del $(OBJPATH)\*.obj
+ $(MAKE)
+
+#
+# makedir - ensure the subdirectory for the object files exists
+#
+
+makedir:
+ @-if not exist $(OBJPATH) md $(OBJPATH)
+
+#
+# file dependencies
+#
+
+$(OBJPATH)\vwipxspx.obj:\
+ vwipxspx.asm \
+ debugmac.inc \
+ asmmacro.inc \
+ segorder.inc \
+ messages.inc
+
+messages.inc: ..\..\inc\$(COUNTRY)\messages.inc
+ copy ..\..\inc\$(COUNTRY)\messages.inc .
diff --git a/private/nw/vwipxspx/tsr/segorder.inc b/private/nw/vwipxspx/tsr/segorder.inc
new file mode 100644
index 000000000..689c62fa2
--- /dev/null
+++ b/private/nw/vwipxspx/tsr/segorder.inc
@@ -0,0 +1,113 @@
+;/*++
+;
+;Copyright (c) 1991 Microsoft Corporation
+;
+;Module Name:
+;
+; segorder.inc
+;
+;Abstract:
+;
+; This module contains the segment order and segment macros
+;
+;Author:
+;
+; Richard Firth (rfirth) 05-Sep-1991
+;
+;Environment:
+;
+; Dos mode only
+;
+;Notes:
+;
+; When initially loaded, the NT VDM redir has the following order:
+;
+; +----------------------+
+; | |
+; | Resident Code |
+; | |
+; +----------------------+
+; | |
+; | Resident Data |
+; | |
+; +----------------------+ ----------------+
+; | | |
+; | Initialisation Code | <- entry point v
+; | |
+; +----------------------+
+; | | all the stuff between these
+; | Initialisation Data | arrows is discarded if we stay
+; | | resident. Note that the redir
+; +----------------------+ does not uninstall
+; | |
+; | Initialisation Stack | ^
+; | | |
+; +----------------------+ ----------------+
+;
+;Revision History:
+;
+; 05-Sep-1991 rfirth
+; Created
+;
+;--*/
+
+
+
+ResidentStart segment public para 'code'
+ResidentStart ends
+
+ResidentCode segment public word 'code'
+ResidentCode ends
+
+ResidentData segment public word 'data'
+ResidentData ends
+
+ResidentEnd segment public para 'data'
+ResidentEnd ends
+
+ResidentGroup group ResidentStart, ResidentCode, ResidentData, ResidentEnd
+
+InitCode segment public para 'init'
+InitCode ends
+
+InitData segment public word 'init'
+InitData ends
+
+InitStack segment stack para 'stack'
+InitStack ends
+
+;
+; macros to avoid having to type in/possibly alter segment header guff
+;
+
+ResidentCodeStart macro
+ResidentCode segment public word 'code'
+endm
+
+ResidentCodeEnd macro
+ResidentCode ends
+endm
+
+ResidentDataStart macro
+ResidentData segment public word 'data'
+endm
+
+ResidentDataEnd macro
+ResidentData ends
+endm
+
+InitCodeStart macro
+InitCode segment public para 'init'
+endm
+
+InitCodeEnd macro
+InitCode ends
+endm
+
+InitDataStart macro
+InitData segment public word 'init'
+endm
+
+InitDataEnd macro
+InitData ends
+endm
diff --git a/private/nw/vwipxspx/tsr/vwipxspx.asm b/private/nw/vwipxspx/tsr/vwipxspx.asm
new file mode 100644
index 000000000..601f80c10
--- /dev/null
+++ b/private/nw/vwipxspx/tsr/vwipxspx.asm
@@ -0,0 +1,711 @@
+page ,132
+if 0
+
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ vwipxspx.asm
+
+Abstract:
+
+ Contains handlers for 16-bit (DOS) netware IPX/SPX emulation. Creates TSR
+
+ Contents:
+ start
+ InstallationCheck
+ InstallVdd
+ InstallInterruptHandlers
+ VwIpxEntryPoint
+ VwIpxDispatcher
+ VwIpx7ADispatcher
+ DispatchWithFeeling
+ VwIpxEsrFunction
+
+Author:
+
+ Richard L Firth (rfirth) 30-Sep-1993
+
+Environment:
+
+ DOS Real mode only
+
+Revision History:
+
+ 30-Sep-1993 rfirth
+ Created
+
+--*/
+
+endif
+
+;
+; DOS include files
+;
+
+.xlist
+.xcref
+include isvbop.inc ; NTVDM BOP mechanism
+include dossym.inc ; includes MS-DOS version etc
+include pdb.inc ; PSP defines
+include syscall.inc ; AssignOper
+include segorder.inc ; load order of 'redir' segments
+include debugmac.inc ; debug display macros
+include asmmacro.inc ; jumps which may be short or near
+include messages.inc ; internationalisationable (prestidigitation) messages
+.cref
+.list
+
+InitStack segment stack para 'stack'
+
+ dw 256 dup (?)
+
+InitStack ends
+
+InitDataStart
+
+bad_ver_msg db NLS_MSG_001,c_CR,c_LF
+BAD_VER_MSG_LEN equ $-bad_ver_msg
+ db '$' ; for INT 21/09 display string
+
+already_loaded_msg db NLS_MSG_002,c_CR,c_LF
+ALREADY_LOADED_MSG_LEN equ $-already_loaded_msg
+
+cannot_load_msg db NLS_MSG_003,c_CR, c_LF
+CANNOT_LOAD_MSG_LEN equ $-cannot_load_msg
+
+;
+; strings used to load/dispatch NWIPXSPX.DLL
+;
+
+DllName db "VWIPXSPX.DLL",0
+InitFunc db "VwInitialize",0
+DispFunc db "VwDispatcher",0
+
+InitDataEnd
+
+InitCodeStart
+ assume cs:InitCode
+ assume ds:nothing
+ assume es:nothing
+ assume ss:nothing
+
+ public start
+start proc near
+
+;
+; when we start up we could be on any old PC - even an original, so don't
+; assume anything other than a model-T processor
+;
+
+ .8086
+
+;
+; Set the data segment while we're at it - all paths set it sooner
+; or later. NOTE: es will point to the PSP until we change it!
+;
+
+ mov dx,InitData
+ mov ds,dx
+
+ assume ds:InitData
+
+;
+; first off, get the DOS version. If we're not running on NT (VDM) then this
+; TSR's not going to do much, so exit. Exit using various methods, depending
+; on the DOS version (don't you hate compatibility?)
+;
+
+ mov ah,30h
+ int 21h
+ jc ancient_version ; version not even supported
+
+;
+; version is 2.0 or higher. Check it out. al = major#, ah = minor#
+;
+
+ cmp al,major_version
+ jne invalid_version
+
+;
+; okay, we're at least 5.0. But are we NT?
+;
+
+ mov ax,3306h
+ int 21h
+ jc invalid_version ; ?
+ cmp bl,5
+ jne invalid_version
+ cmp bh,50
+ jne invalid_version
+
+;
+; what do you know? We're actually running on NT (unless some evil programmer
+; has pinched int 21h/30h and broken it!). Enable minimum instruction set
+; for NTVDM (286 on RISC).
+;
+
+ .286c
+
+;
+; perform an installation check. Bail if we're there dude ((C) Beavis & Butthead)
+;
+
+ call InstallationCheck
+ jnz already_here ; nope - IPX/SPX support installed already
+
+;
+; BUGBUG: we should find some way of deferring loading the 32-bit DLL until an
+; IPX/SPX function is called, to speed-up loading. However, if we later find we
+; cannot load the DLL, it may be too late: there is no way of consistently
+; returning an error and we cannot unload the TSR
+;
+
+ call InstallVdd ; returns IRQ in BX
+ jc initialization_error
+ call InstallInterruptHandlers
+
+ assume es:nothing
+
+;
+; free the environment segment
+;
+
+ mov es,es:[PDB_environ]
+ mov ah,49h
+ int 21h ; free environment segment
+
+;
+; finally terminate and stay resident
+;
+
+ mov dx,ResidentEnd
+ sub dx,ResidentStart ; number of paragraphs in resident code
+ add dx,10h ; additional for PSP (PDB)
+ mov ax,3100h
+ int 21h ; terminate and stay resident
+
+;
+; here if the MS-DOS version check (Ah=30h) call is not supported
+;
+
+ancient_version:
+ mov dx,InitData
+ mov ds,dx
+
+ assume ds:InitData
+
+ mov dx,offset bad_ver_msg
+ mov ah,9 ; cp/m-style write to output
+ int 21h
+
+;
+; safe exit: what we really want to do here is INT 20H, but when you do this,
+; CS must be the segment of the PSP of this program. Knowing that CD 20 is
+; embedded at the start of the PSP, the most foolproof way of doing this is
+; to jump (using far return) to the start of the PSP
+;
+
+ push es
+ xor ax,ax
+ push ax
+ retf ; terminate
+
+;
+; we are running on a version of DOS >= 2.00, but its not NT, so we still can't
+; help. Display the familiar message and exit, but using a less programmer-
+; hostile mechanism
+;
+
+invalid_version:
+ mov dx,offset bad_ver_msg
+ mov cx,BAD_VER_MSG_LEN
+ jmp short print_error_message_and_exit
+
+;
+; if we cannot initialize 32-bit support (because we can't find/load the DLL)
+; then put back the hooked interrupt vectors as they were when this TSR started,
+; display a message and fail to load the redir TSR
+;
+
+initialization_error:
+ mov dx,offset cannot_load_msg
+ mov cx,CANNOT_LOAD_MSG_LEN
+ jmp short print_error_message_and_exit
+
+;
+; The DOS version's OK, but this TSR is already loaded
+;
+
+already_here:
+ mov dx,offset already_loaded_msg
+ mov cx,ALREADY_LOADED_MSG_LEN
+
+print_error_message_and_exit:
+ mov bx,1 ; bx = stdout handle
+ mov ah,40h ; write to handle
+ int 21h ; write (cx) bytes @ (ds:dx) to stdout
+ mov ax,4c01h ; terminate program
+ int 21h ; au revoir, cruel environment
+
+start endp
+
+; *** InstallationCheck
+; *
+; * Test to see if this module is already loaded
+; *
+; * ENTRY nothing
+; *
+; * EXIT ZF = 0: loaded
+; *
+; * USES AX
+; *
+; * ASSUMES nothing
+; *
+; ***
+
+InstallationCheck proc
+ mov ax,7a00h
+ int 2fh
+ or al,al
+ ret
+InstallationCheck endp
+
+; *** InstallVdd
+; *
+; * Load VWIPXSPX.DLL into the NTVDM process context
+; *
+; * ENTRY nothing
+; *
+; * EXIT CF = 1: error
+; * CF = 0: VWIPXSPX loaded ok
+; * AX = VDD handle
+; * BX = IRQ used by call-back functions (ESR)
+; * ResidentCode:VddHandle updated
+; * ResidentCode:IrqValue updated
+; *
+; * USES AX, BX, SI, DI
+; *
+; * ASSUMES nothing
+; *
+; ***
+
+InstallVdd proc
+ push ds
+ push es
+ mov ax,InitData
+ mov ds,ax
+
+ assume ds:InitData
+
+ mov es,ax
+ mov si,offset DllName ; ds:si = library name
+ mov di,offset InitFunc ; es:di = init function name
+ mov bx,offset DispFunc ; ds:bx = dispatcher function name
+
+ RegisterModule ; returns carry if problem
+
+ mov si,ResidentCode
+ mov ds,si
+
+ assume ds:ResidentCode
+
+ mov VddHandle,ax
+ mov IrqValue,bx
+ pop es
+
+ assume es:nothing
+
+ pop ds
+
+ assume ds:nothing
+
+ ret
+InstallVdd endp
+
+; *** InstallInterruptHandlers
+; *
+; * Sets the interrupt handlers for all the ints we use - 2F, 7A
+; *
+; * ENTRY BX = IRQ for call-backs
+; * ES = PSP segment
+; *
+; * EXIT Old2FHandler contains the original interrupt 2F vector
+; * Old7AHandler contains the original interrupt 7A vector
+; * OldIrqHandler contains original IRQ vector
+; *
+; * USES AX, BX, CX, DX
+; *
+; * ASSUMES nothing
+; *
+; ***
+
+InstallInterruptHandlers proc
+ push es ; PSP segment - destroyed by INT 21/35h
+ push ds
+ mov dx,ResidentCode
+ mov ds,dx
+
+ assume ds:ResidentCode
+
+;
+; get and set call-back IRQ
+;
+
+ mov ah,35h
+ mov al,bl
+ mov cl,bl ; cl = IRQ number
+ int 21h
+ mov word ptr OldIrqHandler,bx
+ mov word ptr OldIrqHandler+2,es
+ mov al,cl
+ mov ah,25h
+ mov dx,offset ResidentCode:VwIpxEsrFunction
+ int 21h
+
+;
+; get and set 2F handler
+;
+
+ mov ax,352Fh
+ int 21h
+ mov word ptr Old2FHandler,bx
+ mov word ptr Old2FHandler+2,es
+ mov dx,offset ResidentCode:VwIpxEntryPoint
+ mov ax,252Fh
+ int 21h
+
+;
+; get and set 7A handler
+;
+
+ mov ax,357Ah
+ int 21h
+ mov word ptr Old7AHandler,bx
+ mov word ptr Old7AHandler+2,es
+ mov dx,offset ResidentCode:VwIpx7ADispatcher
+ mov ax,257Ah
+ int 21h
+ pop ds ; restore segment registers
+
+ assume ds:nothing
+
+ pop es
+
+ assume es:nothing
+
+ ret
+InstallInterruptHandlers endp
+
+InitCodeEnd
+
+page
+
+;
+; code from here on will be left in memory after initialisation
+;
+
+ResidentCodeStart
+
+ assume cs:ResidentCode
+ assume ds:nothing
+ assume es:nothing
+ assume ss:nothing
+
+Old2FHandler dd ?
+Old7AHandler dd ?
+OldIrqHandler dd ?
+
+IrqValue dw ?
+
+VddHandle dw ?
+
+; *** VwIpxEntryPoint
+; *
+; * The INT 2Fh handler that recognizes the Netware IPX request code (7A).
+; * Also chains INT 2F/AX=1122
+; *
+; * ENTRY AX = 7A00h
+; *
+; * EXIT AL = 0FFh
+; * ES:DI = address of routine to call when submitting IPX/SPX
+; * requests
+; *
+; * USES
+; *
+; * ASSUMES nothing
+; *
+; ***
+
+VwIpxEntryPoint proc
+ cmp ax,7a00h
+ jne @f
+ mov di,cs
+ mov es,di
+ mov di,offset VwIpxDispatcher
+ dec al
+ iret
+
+;
+; not 7A00h. Check for 1122h (IFSResetEnvironment). If yes, then this is DOS
+; calling the IFS chain to notify that the app is terminating. When we have
+; notified the DLL, chain the IFS request
+;
+
+@@: cmp ax,7affh
+ jne try1122
+ mov di,cs
+ mov es,di
+ mov di,offset VwIpxDispatcher
+ or bx,bx
+ jz @f
+ mov cx,8000h
+ mov si,7
+ iret
+@@: mov cx,14h
+ mov si,200h
+ iret
+
+try1122:cmp ax,1122h
+ jne @f
+
+;
+; DOS Calls INT 2F/AX=1122 for every terminating app, including this one. We
+; can't differentiate between a TSR and a non-TSR. Let the DLL handle it
+;
+
+ push ax
+ push bx
+ push cx
+ mov ah,51h
+ int 21h
+ mov cx,bx ; cx = PDB of terminating program/TSR
+ mov bx,-1 ; bx = dispatch code
+ mov ax,VddHandle ; ax = VDD handle
+ DispatchCall
+ pop cx
+ pop bx
+ pop ax
+@@: jmp Old2FHandler ; chain int 2F
+VwIpxEntryPoint endp
+
+; *** VwIpxDispatcher
+; *
+; * All DOS IPX/SPX calls are routed here by the netware libraries. Just
+; * BOP on through to the other side
+; *
+; * This routine just transfers control to 32-bit world, where all work is
+; * done
+; *
+; * ENTRY BX = netware IPX/SPX dispatch code
+; * others - depends on function
+; *
+; * EXIT depends on function
+; *
+; * USES depends on function
+; *
+; * ASSUMES nothing
+; *
+; ***
+
+VwIpxDispatcher proc far
+ pushf ; apparently we don't modify flags
+ call DispatchWithFeeling
+ popf
+ ret
+VwIpxDispatcher endp
+
+; *** VwIpx7ADispatcher
+; *
+; * Older Netware apps make the call to IPX/SPX via INT 7A. Same function
+; * as VwIpxDispatcher
+; *
+; * This routine just transfers control to 32-bit world, where all work is
+; * done
+; *
+; * ENTRY BX = netware IPX/SPX dispatch code
+; * others - depends on function
+; *
+; * EXIT depends on function
+; *
+; * USES depends on function
+; *
+; * ASSUMES nothing
+; *
+; ***
+
+VwIpx7ADispatcher proc
+ call DispatchWithFeeling
+ iret
+VwIpx7ADispatcher endp
+
+; *** DispatchWithFeeling
+; *
+; * Performs the dispatch for VrIpxDispatcher and VrIpx7ADispatcher. Checks
+; * requested function for return code in AX: either returns value in AX
+; * or restores AX to value on input
+; *
+; * This routine just transfers control to 32-bit world, where all work is
+; * done
+; *
+; * ENTRY BX = netware IPX/SPX dispatch code
+; * others - depends on function
+; *
+; * EXIT depends on function
+; *
+; * USES depends on function
+; *
+; * ASSUMES 1. Dispatch codes are in range 0..255 (ie 0 in BH)
+; *
+; ***
+
+DispatchWithFeeling proc
+ push bp
+ push ax ; caller value
+
+;
+; some APIs (IPXOpenSocket, IPXScheduleIPXEvent, SPXEstablishConnection, and
+; others...) pass a parameter in AX. Since AX is being used for the VDD
+; handle, we have to commandeer another register to hold our AX value. BP is
+; always a good candidate
+;
+
+ mov bp,ax ; grumble, mutter, gnash, gnash
+ push cx ; required if IPXOpenSocket
+ push bx ; dispatch code
+ or bx,bx ; IPXOpenSocket?
+ jz @f ; yus ma'am
+ cmp bl,3 ; IPXSendPacket?
+ jz @f ; yus ma'am again
+ jmp short carry_on_dispatching ; ooo-err missus
+
+;
+; IPXOpenSocket et IPXSendPacket: We need an extra piece of info - the PDB of
+; the process making this request. This is so we can clean-up at program
+; termination
+;
+
+@@: push bx
+ mov ah,51h ; get DOS PDB
+ int 21h ; this call can be made any time
+ mov cx,bx
+ pop bx
+
+carry_on_dispatching:
+ mov ax,VddHandle
+ DispatchCall
+ mov bp,sp
+
+;
+; BX and [BP] will be the same value except for SPXInitialize which is the only
+; function that returns something in BX
+;
+
+ xchg bx,[bp] ; bx = dispatch code, [bp] = returned bx
+
+;
+; if this call returns something in AX (or AL) don't pop the AX value we pushed.
+; If not a call which returns something in AX then restore the caller's AX. You
+; can rest assured some assembler programmer has made use of the fact that some
+; calls modify AX and the others leave it alone (presumably...?)
+;
+
+ or bl,bl ; 0x00 = IPXOpenSocket
+ jz @f
+ cmp bl,2 ; 0x02 = IPXGetLocalTarget
+ jz @f
+ cmp bl,4 ; 0x04 = IPXListenForPacket
+ jz @f
+ cmp bl,6 ; 0x06 = IPXCancelEvent
+ jz @f
+ cmp bl,8 ; 0x08 = IPXGetIntervalMarker
+ jz @f
+ cmp bl,10h ; 0x10 = SPXInitialize
+ jz spx_init
+ cmp bl,11h ; 0x11 = SPXEstablishConnection
+ jz @f
+ cmp bl,15h ; 0x15 = SPXGetConnectionStatus
+ jz @f
+ cmp bl,1ah ; 0x1A = IPXGetMaxPacketSize
+ jz @f
+ pop cx ; original dispatch code
+ pop cx ; original cx
+ pop ax ; original ax
+ pop bp ; original bp
+ ret
+
+;
+; here if this call returns something in AX/AL
+;
+
+@@: pop cx ; original dispatch code
+ pop cx ; original cx
+ pop bp ; don't restore AX
+ pop bp
+ ret
+
+;
+; here if the call was SPXInitialize which returns values in AX, BX, CX, DX
+;
+
+spx_init:
+ pop bx ; bx = major/minor SPX version #
+ pop bp ; caller cx - NOT restored
+ pop bp ; caller ax - NOT restored
+ pop bp ; caller bp - restored
+ ret
+DispatchWithFeeling endp
+
+; *** VwIpxEsrFunction
+; *
+; * This routine makes the call to the ESR as defined in the ECB. We must
+; * set up our stack, save the registers (except SS & SP), then call the
+; * ESR.
+; *
+; * Control will not be transferred here for an ECB which has a NULL ESR
+; * field
+; *
+; * ENTRY AL = 0 for AES or 0FFh for IPX
+; * ES:SI = ECB address
+; *
+; * EXIT depends on function
+; *
+; * USES depends on function
+; *
+; * ASSUMES nothing
+; *
+; ***
+
+VwIpxEsrFunction proc
+
+;
+; Novell documentation states all registers except SS and SP are saved before
+; calling ESR and that INTERRUPTS ARE DISABLED
+;
+
+ pusha
+ push ds
+ push es
+ mov ax,VddHandle
+ mov bx,-2
+ DispatchCall ; get ECB
+ jc @f
+ call dword ptr es:[si][4] ; branch to the ESR
+ mov al,20h
+ out 0a0h,al ; clear slave pic
+ out 20h,al ; " master "
+ pop es
+ pop ds
+ popa
+ iret
+@@: pop es
+ pop ds
+ popa
+ jmp OldIrqHandler
+VwIpxEsrFunction endp
+
+ResidentCodeEnd
+
+end start