{$X+,B-,V-,S-} {essential compiler directives} UNIT nwConn; { nwConn unit as of 950301 / NwTP 0.6 API. (c) 1993,1995, R. Spronk } { Includes modifications to Attach/Detach by H. Jelonneck } INTERFACE { Primary Functions: Interrupt: comments: Connection Services ------------------- * AttachToFileServer (F100) * AttachToFileServerWithAddress (F100) * DetachFromFileServer (F101) . EnterLoginArea (F217/0A) * GetConnectionInformation (F217/16) * GetConnectionNumber (DC..) * GetInternetAddress (F217/13) * GetObjectConnectionNumbers (F217/15) * GetWorkstationNodeAddress (EE..) * LoginToFileserver (F217/14) UNencrypted * LoginEncrToFileserver (F217/18) encrypted * Logout (F219) * LogoutFromFileServer (F102) Secondary Functions: * GetUserAtConnection * GetObjectLoginControl * GetObjectNodeControl * ObjectCanLoginAt Workstation Services -------------------- * EndOfJob (D6) to be rewritten to F218 * GetConnectionID (EF04) * GetConnectionIDtable (EF03) (1) * GetDefaultConnectionID (F002) * GetEndOfJobStatus (BB..) * GetFileServerName (EF04) * GetNetwareErrorMode (DD..) * GetNetwareShellVersion (EA00) * GetNumberOfLocalDrives (DB..) * GetPreferredConnectionID (F001) * GetPrimaryConnectionID (F005) * GetShowDots (E908) * GetWorkstationEnvironment (EAxx,xx>00) (2) * SetEndOfJobStatus (BB..) * SetNetwareErrorMode (DD..) * SetPreferredConnectionID (F000) * SetPrimaryConnectionID (F004) * SetShowDots (E908) Secondary Functions: * GetEffectiveConnectionID (F001,F002,F005) * IsConnectionIDinUse (EF03) Not Implemented: ---------------- - GetStationsLoggedInformation (F217/05) (3) - Login (F217/00) (4) - MapUserToStationSet (F217/02) (5) Notes: -Only functions marked with a * have been tested; others might work. -(1): This function returns the complete Connection ID table. The partial function IsConnectionIDInUse has been moved to the secondary function group. -(2): This function is an extension to EA00 GetNetwareShellVersion. A function that returns all returned information from the call EAxx,xx>00 is sometimes referred to as GetWShardwareAndOS. -NOT implemented in this API: (3): Replaced by F217/16 GetConnectionInformation. (4): This function has been replaced by F217/14 LoginToFileServer. (5): Replaced by F217/15 GetObjectConnectionNumbers. -NW 386 can support up to 250 connections, NW 286 Max 100. -Type TconnectionList=array[1..250] of byte (Declared in unit nwMisc) } Uses nwIntr,nwMisc,nwBindry; Const MaxServers=8; Type TServerNameTableEntry = Array [1..48] OF Char; TServerNameTable = Array[1..MaxServers] OF TServerNameTableEntry; TConnectionIDTableEntry= Record SlotInUse : Byte; OrderNumber : Byte; ServerAddress : TinternetworkAddress; ReceiveTimeOut : Word; RouterAddress : TnodeAddress; PacketSeqNbr : Byte; ConnectionNumber : Byte; ConnectionStatus : Byte; MaxTimeOut : Word; WConnectionNumber: Word; MajorNWrev : Byte; ServerFlags : Byte; MinorNWrev : Byte; END; TConnectionIDTable = Array [1..MaxServers] OF TConnectionIDTableEntry; TloginControl=Record AccountDisabled :boolean; AccountExpirationDate :TNovTime; { dmy valid only } MinimumPasswordLength :byte; PasswordControlFlag :byte; DaysBetweenPasswordChanges:word; PasswordExpirationDate :TnovTime; LastLoginTime :TnovTime; {dmy, hms valid only } GraceLoginsRemaining :Byte; MaxGraceLoginsAllowed :byte; BadLoginCount :byte; AccountResetTime :TnovTime; {dmy, hms valid only } LastIntruderAddress :TinterNetworkAddress; MaxConcurrentConnections :byte; LoginTimes :array[1..42] of byte; DiskSpace :Longint; end; TnodeControl=array[1..12] of record net :TnetworkAddress; node:TnodeAddress; end; Var result:word; {BB.. [2.0/2.1/3.x]} Function SetEndOfJobStatus( EndOfJobFlag: Boolean ):Boolean; { When this function is called with EndOfJobFlag=False and control is returned to the root COMMAND.COM, COMMAND.COM will NOT perform an EOJ action. } {BB.. [2.0/2.1/3.x]} Function GetEndOfJobStatus(Var EndOfJobFlag: Boolean ):Boolean; {F218 [2.15c+]} FUNCTION EndOfJob(All : Boolean):boolean; { Forces an end of job } {E908 (shell 3.00+)} Function SetShowDots( Show:Boolean):Boolean; {E908 (shell 3.00+)} Function GetShowDots(Var Shown:Boolean):Boolean; {F219 [2.15c+]} Function Logout:boolean; { Logout from all file servers, remains attached to Server, effective EOJ } {DB.. [2.0/2.1/3.x]} Function GetNumberOfLocalDrives( Var drives:Byte ):Boolean; {DC.. [2.0/2.1/3.x]} Function GetConnectionNumber(Var ConnectionNbr:byte):boolean; { Returns connection number of requesting WS } {DD.. [2.0/2.1/3.x]} Function SetNetwareErrorMode( errMode:Byte):boolean; { Sets the shell's handling mode for dealing with netware errors. } {DD.. [2.0/2.1/3.x]} Function GetNetwareErrorMode(Var errMode:Byte):boolean; {E3../0A [2.0/2.1/3.x]} Function EnterLoginArea( LoginSubDirName:String; numberOfLocalDrives:Byte ):boolean; { Changes the login directory. Used by boot-proms. } {F217/13 [2.15c+]} Function GetInternetAddress( ConnNbr : byte; var IntNetAddress:TinternetworkAddress):boolean; {F217/14 [2.15c+] UNENCRYPTED} Function LoginToFileServer( objName:String; objType:word; password : string ):boolean; {F217/18 [2.15c+] ENCRYPTED} FUNCTION LoginEncrToFileServer(ObjName: String; ObjType: Word; PassWord: String ): Boolean; {F217/15 [2.15c+]} Function GetObjectConnectionNumbers( objName:String; objType:Word; Var numberOfConnections: Byte; Var connections: TconnectionList ):boolean; { returns a list of connectionnumbers where objects of the desired type and name are logged in. } {F217/16 [2.15c+]} Function GetConnectionInforMation (ConnectionNbr:byte; Var objName:String; Var objType:Word; Var objId:LongInt; Var LoginTime:TnovTime ):boolean; {EA00 [2.0/2.1/3.x]} Function GetNetwareShellVersion( Var MajorVersion,MinorVersion, RevisionLevel :Byte ):Boolean; { Returns information about a WS environment. Queries shell. } {EAxx,xx>00 [2.0/2.1/3.x]} Function GetWorkstationEnvironment(Var OStype,OSversion, HardwareType,ShortHWType:String):Boolean; {EE.. [2.0/2.1/3.x]} FUNCTION GetWorkstationNodeAddress( var physicalNodeAddress: TNodeAddress ):boolean; { Get the physical address of the workstation (6 bytes hi-endian) } {EF03 [2.0/2.1/3.x]} Function GetConnectionIDTable( ConnectionID: Byte ; Var TableEntry: TConnectionIDTableEntry ):boolean; { Returns a copy of the entry in the shells' ConnectionID table corresponding with the given ConnectionID. } {EF04 [2.0/2.1/3.x]} Function GetConnectionID( serverName: String; Var ConnectionID: Byte):boolean; {EF04 [2.0/2.1/3.x]} Function GetFileServerName( ConnectionID : byte; var ServerName : string):boolean; { get name of file server. file server number must be in range [1..MaxServers] } {F000 [2.0/2.1/3.x]} Function SetPreferredConnectionID( ConnectionID :byte ):boolean; {F001 [2.0/2.1/3.x]} Function GetPreferredConnectionID(var connID : byte):boolean; {F002 [2.0/2.1/3.x]} FUNCTION GetDefaultConnectionID(var connID :byte):boolean; {F004 [2.0/2.1/3.x]} FUNCTION SetPrimaryConnectionID( primaryConnectionID :Byte ):boolean; {F005 [2.0/2.1/3.x]} FUNCTION GetPrimaryConnectionID(var connID :byte ):boolean; {F100 [2.0+]} Function AttachToFileServerWithAddress(ServerName:string; ServerAddr:TinternetworkAddress; Var ConnectionID:Byte):Boolean; {F100 [2.0/2.1/3.x] (also calls EF03,EF04)} Function AttachToFileServer(ServerName : String; Var ConnectionID:Byte):Boolean; { Create an attachment between a server and a workstation. } {F101 [2.0/2.1/3.x]} Function DetachFromFileServer( ConnectionID:byte ):boolean; { removes server from shell's server table. Relinquishes the fileserver connection number and breaks the connection. } {F102 [2.0/2.1/3.x]} Function LogoutFromFileServer(var ConnectionID: byte):boolean; {logout from one file server} {***** secondary Functions, Result variable is not used *******************} {EF03 [2.0/2.1/3.x] secondary Function } Function IsConnectionIDinUse( ConnectionID: Byte ):boolean; Function GetUserAtConnection( ConnectionNbr:byte; var username: string):boolean; {This function provides a short method of obtaining just the USERID.} Function GetEffectiveConnectionID(Var connId:byte):boolean; {What server are the requests currently sent to? } Function GetObjectLoginControl(ObjName:string; ObjType:word; VAR LoginControlInfo:TloginControl):boolean; Function GetObjectNodeControl( ObjName:string; ObjType:word; {i/o} Var seqNbr:integer; {out} Var NodeControlInfo:TnodeControl):boolean; Function ObjectCanLoginAt(ObjName:String; ObjType:Word; LoginTime:TnovTime ):Boolean; { -If the fields hour,min,sec and dayOfWeek are filled, the time will be checked against the login timerestrictions. -If the fields year,month,day are filled ( >0 ), the date will be checked with the expiration date of the account and with the Account disabled Flag. } IMPLEMENTATION{=============================================================} Type TPConnectionIDTPtr=^TConnectionIDTable; TPServerNTPtr=^TServerNameTable; {F000 [2.0/2.1/3.x]} Function SetPreferredConnectionID( ConnectionID :byte ):boolean; { The preferred server is the default server to which the request packets are sent. Calls are routed to the preferred server. (IF explicitly set!). If the preferred server was not set then the requests are routed to the server that is attached to the current drive. If the current drive is a local drive then the requests will be sent to the primary server (mostly the server the shell initially attached to.) } var regs : TTregisters; begin regs.ax := $F000; regs.dl := ConnectionID; { 1..MaxServers, 0 to clear } RealModeIntr($21,regs); result:=0; SetPreferredConnectionID:=TRUE; end; {F004 [2.0/2.1/3.x]} FUNCTION SetPrimaryConnectionID( primaryConnectionID :Byte ):boolean; var regs : TTregisters; begin regs.ax := $F004; regs.dl := primaryConnectionID; { 1..MaxServers, or 0 to clear } RealModeIntr($21,regs); result:=0; SetPrimaryConnectionID:=TRUE; end; {F005 [2.0/2.1/3.x]} FUNCTION GetPrimaryConnectionID(var connID :byte ):boolean; { returns connection number of the primary file server (1..MaxServers) } var regs : TTregisters; begin regs.ax := $F005; RealModeIntr($21,regs); connID := regs.al; if connId>MaxServers then result:=$FF else result:=$00; GetPrimaryConnectionID:=(result=0); end; {F002 [2.0/2.1/3.x]} FUNCTION GetDefaultConnectionID(var connID :byte):boolean; { Returns the connection ID of the file server to which the packets are currently being sent. } var regs : TTregisters; begin regs.ax := $F002; RealModeIntr($21,regs); connID := regs.al; { 1..MaxServers } if connId>MaxServers then result:=$FF else result:=$00; GetDefaultConnectionID:=(result=0); end; {F001 [2.0/2.1/3.x]} Function GetPreferredConnectionID(var connID : byte):boolean; var regs : TTregisters; begin regs.ax := $F001; RealModeIntr($21,regs); connID := regs.al; { 1..MaxServers, or 0 if the preferred server was not set } { The preferred coneection is reset to 0 by an EOJ. } if connId>MaxServers then result:=$FF else result:=$00; GetPreferredConnectionID:=(result=0); end; {EF04 [2.0/2.1/3.x]} Function GetConnectionID( serverName: String; Var ConnectionID: Byte):boolean; Type ptarr=^arr; arr=Array[0..MaxServers*32] of Byte; Var regs : TTregisters; NameTable : Array [1..MaxServers*48] of Byte; ServerNames: Array [1..MaxServers] of String; t : Byte; begin UpString(ServerName); regs.ax := $EF04; RealModeIntr($21,regs); { get pointer to shell's server name table. } move(nwPtr(regs.es, regs.si)^,NameTable,MaxServers*48); For t := 0 to 7 do ZstrCopy(ServerNames[t+1],NameTable[1+ t*48],48); t:=1; While ((t<9) and (ServerNames[t]<>ServerName)) do inc(t); If t=9 then Result:=$FC { invalid server name } else begin ConnectionID:=t; { ServerName found. Is ConnectionID valid ? } regs.ax:=$EF03; RealModeIntr($21,regs); IF (ptarr(nwPtr(regs.es,regs.si))^[(ConnectionID-1)*32] = $00 ) {= $FF ?? } then begin ConnectionID:=0; result:=$FC { ConnectionID invalid => servername invalid } end else result:=$00; end; GetConnectionID:=(result=0); end; {EF04 [2.0/2.1/3.x]} Function GetFileServerName( ConnectionID : Byte; Var ServerName : String):boolean; { Get the name of file server, associated with the ConnectionID. The File server number must be in the range [1..MaxServers]. The function will fail (result=$FF) if connID falls outside of this range. } Type ptarr=^arr; arr=Array[0..MaxServers*32] of Byte; Var regs : TTregisters; NameTable : Array [1..MaxServers*48] of Byte; ServerNames: Array [1..MaxServers] of String; t : Byte; begin regs.ax := $EF04; RealModeIntr($21,regs); { Get pointer to shell's server name table. } move(nwPtr(regs.es, regs.si)^,NameTable,MaxServers*48); For t := 0 to 7 do ZstrCopy(ServerNames[t+1],NameTable[1+ t*48],48); if ((ConnectionID<1) or (ConnectionID>MaxServers)) then ServerName:='' else ServerName := ServerNames[ConnectionID]; IF ServerName='' then result:=$FF else begin { The name is valid, but is the ConnectionID valid ? } regs.ax:=$EF03; RealModeIntr($21,regs); IF (ptarr(nwPtr(regs.es,regs.si))^[(ConnectionID-1)*32] = $00 ) {= $FF ?? } then begin result:=$FF; { ConnectionID invalid => servername invalid } ServerName:=''; end else result:=$00; end; GetFileServerName:=(result=0); end; Function AttachToFileServerWithAddress(ServerName:string; ServerAddr:TinternetworkAddress; Var ConnectionID:Byte):Boolean; { Create an attachment between a server and a workstation. } { Does not Login the workstation. } { After attaching, and beFore logging in, you must set the preferred server to the ConnectionID of the server. } { Will not report an error if you're already attached to -or even logged on to- the target server. } { Attaches to the server whose address is supplied. The server name will be placed in the server name tables, even if the servername is incorrect or the supplied servername isn't associated with the supplied address. } { Based on the InsertServer Function in LOGON.PAS by Barry Nance, and on Rose, p.262 } Var ConnectionIDTPtr : TPConnectionIDTPtr; ServerNTPtr : TPServerNTPtr; NewServerSlot,i : Byte; OldConnId : Byte; ServIsAttached : Boolean; AccessLevel : Byte; ObjID : Longint; Regs:TTRegisters; NewServer:Boolean; Var cid:byte; BEGIN { If server known, take adress from shells' tables. If server not known, try to read its' adress from a servers' bindery. This will fail if you're not connected to at least one server. Once an adress has been found, AttachToFileServerWithAdress is called. } ServerAddr.socket:=swap($0451); { swapped hi-lo} UpString(ServerName); regs.ax:=$EF03; RealModeIntr($21,regs); ConnectionIDTPtr:=nwPtr(regs.es,regs.si); { Ptr to TConnectionIDTable } { Determine whether the suplied server is already known/attached to } ConnectionID:=0; REPEAT inc(ConnectionID) UNTIL (ConnectionID>MaxServers) or ((ConnectionIDTPtr^[ConnectionID].SlotInUse>0) and IsEqualNetworkAddress(ConnectionIDTPtr^[ConnectionID].ServerAddress,ServerAddr) ); NewServer:=(ConnectionID>MaxServers); { If the server is a new server, put it in the sorted ConnectionIDTable } IF NewServer then begin { Determine free slot to insert new server } NewServerSlot := 1; WHILE (ConnectionIDTPtr^[NewServerSlot].SlotInUse <> $00) AND (NewServerSlot <= MaxServers) do inc(NewServerSlot); IF NewServerSlot > MaxServers then begin Result:=$7C; AttachToFileServerWithAddress := False; exit; end; With ConnectionIDTPtr^[NewServerSlot] do begin ServerAddress:=ServerAddr; OrderNumber := 0; For i := 1 TO MaxServers do begin IF (ConnectionIDTPtr^[i].SlotInUse <> $00) and (ConnectionIDTPtr^[i].OrderNumber>=OrderNumber) then OrderNumber:=ConnectionIDTPtr^[i].OrderNumber+1; end; SlotInUse := $FF; { Must be set to $FF before attaching } end; ConnectionID:=NewServerSlot; end else { NOT a new server.. } IF (ConnectionIDTPtr^[ConnectionID].ConnectionNumber > 0) AND (ConnectionIDTPtr^[ConnectionID].ConnectionNumber < $FF) AND (ConnectionIDTPtr^[ConnectionID].ConnectionStatus = $FF) then Begin { ServerIsKnown } GetPreferredConnectionID (OldConnId); SetPreferredConnectionID (ConnectionID); ServIsAttached := GetBinderyAccessLevel (AccessLevel, ObjID); SetPreferredConnectionID (OldConnID); IF ServIsAttached { ServerIsAlreadyAttached / caller may even be looged on } then begin result:=0; AttachToFileServerWithAddress := True; exit; end; End; { Create an attachment } With Regs do begin AX := $F100; DL := ConnectionID; RealModeIntr($21,Regs); Result := AL; { F8 already attached to server; F9 No Free connection slots at server; FA no more server slots; FE Server Bindery Locked; FF No response from server } end; IF NewServer then begin if Result<>$00 { F9/FA/FE/FF error at server/no response from server } then Begin { Note that the combination of a 'new' server and err. F8 is impossible } ConnectionIDTPtr^[NewServerSlot].SlotInUse:=$00; { Invalid server, Free slot again } end else begin { Valid server, sort ConnectionID table } With ConnectionIDTPtr^[NewServerSlot] do begin SlotInUse:=$00; { temporarily set to 0, For sorting purposes } OrderNumber := 1; For i := 1 TO MaxServers do begin IF ConnectionIDTPtr^[i].SlotInUse <> $00 then begin IF IsLowerNetworkAddress(ConnectionIDTPtr^[i].ServerAddress, ServerAddress) then inc(OrderNumber) else inc(ConnectionIDTPtr^[i].OrderNumber) end; end; SlotInUse:=$FF; end; { Put new servers' name in server Name Table } regs.ax := $EF04; RealModeIntr($21,regs); ServerNTPtr:=nwPtr(regs.es, regs.si); { pointer to shell's server name table. } FillChar(ServerNTPtr^[NewServerSlot],48,#0); If ServerName[0]>#47 then ServerName[0]:=#47; Move(ServerName[1],ServerNTPtr^[NewServerSlot],Length (ServerName)); end; end; AttachToFileServerWithAddress:=(result=0); { Valid completion codes: 7C Maximum number of attached servers exceeded. F8 already attached to server; F9 No Free connection slots at specified server; FA no more server slots; FF No response from server FC No Free slots in shells' ConnectionID table; } end; Function AttachToFileServer(ServerName : String; Var ConnectionID:Byte):Boolean; { Create an attachment between a server and a workstation. } { !! you have to be attached to at least 1 server before calling this function. } { Does not Login the workstation. } { After attaching, and beFore logging in, you must set the preferred server to the ConnectionID of the server. } { Will not report an error if you're already attached to -or even logged on to- the target server. } Var ConnectionIDTPtr : TPConnectionIDTPtr; OldConnId : Byte; ServIsAttached : Boolean; AccessLevel : Byte; ObjID : Longint; PropValue :Tproperty; MoreSegments :boolean; PropFlags :Byte; Regs:TTRegisters; ServAddr:TinternetworkAddress; BEGIN { If server known, take adress from shells' tables. If server not known, try to read its' address from a servers' bindery. This will fail if you're not connected to at least one server. Once an adress has been found, AttachToFileServerWithAdress is called. } UpString(ServerName); regs.ax:=$EF03; RealModeIntr($21,regs); ConnectionIDTPtr:=nwPtr(regs.es,regs.si); { Ptr to TConnectionIDTable } { Determine whether the suplied server is already known/attached to } IF GetConnectionID(ServerName,ConnectionID) then Begin IF (ConnectionIDTPtr^[ConnectionID].ConnectionNumber > 0) AND (ConnectionIDTPtr^[ConnectionID].ConnectionNumber < $FF) AND (ConnectionIDTPtr^[ConnectionID].ConnectionStatus = $FF) then Begin { ServerIsKnown } GetPreferredConnectionID (OldConnId); SetPreferredConnectionID (ConnectionID); ServIsAttached := GetBinderyAccessLevel (AccessLevel, ObjID); SetPreferredConnectionID (OldConnID); result:=0; IF ServIsAttached { ServerIsAlreadyAttached / caller may even be looged on } then begin AttachToFileServer := True; exit; end else ServAddr:=ConnectionIDTPtr^[ConnectionID].ServerAddress; end End Else begin IF ReadPropertyValue(ServerName,OT_FILE_SERVER,'NET_ADDRESS',1,PropValue,moreSegments,propFlags) then begin result:=0; Move(PropValue,ServAddr,SizeOf(TinternetworkAddress)); end else begin Result:=$FC; AttachToFileServer:=False; exit; end; End; if result=0 then AttachToFileServerWithAddress(ServerName,ServAddr,ConnectionID); AttachToFileServer:=(result=0); { Valid completion codes: 7C Maximum number of attached servers exceeded. 7D Bindery read error (The supplied server can't be located/doesn't exist) F8 already attached to server; F9 No Free connection slots at specified server; FA no more server slots; FE Server Bindery Locked; FF No response from server FC No Free slots in shells' ConnectionID table; } END; {F101 [2.0/2.1/3.x]} Function DetachFromFileServer( ConnectionID:Byte ):boolean; { removes server from shell's server table. Relinquishes the fileserver connection number and breaks the connection. The function will fail (result=$FF) if connID falls outside of the range [1..MaxServers].} Type ArrPtr=^Tarr; Tarr=Array[0..MaxServers*48] of Byte; Var regs : TTregisters; begin if (ConnectionID<1) or (ConnectionID>MaxServers) then result:=$FF else begin regs.ax := $F101; regs.dl := ConnectionID; { 1..MaxServers } RealModeIntr($21,regs); result := regs.al; { returncodes: 00 successful; FF Connection Doesn't exist } end; DetachFromFileServer:=(result=0); end; {EF03 [2.0/2.1/3.x]} Function GetConnectionIDTable( ConnectionID: Byte ; Var TableEntry: TConnectionIDTableEntry ):boolean; { Returns a copy of the entry in the shells' ConnectionID table corresponding With the given ConnectionID. All fields are returned lo-hi, except Net and Node addresses. The function will fail (result=$FF) if connID falls outside of the range [1..MaxServers].} Type ptarr=^tarr; tarr=Array[0..MaxServers*32] of Byte; Var regs:TTregisters; begin If ((ConnectionID<1) or (ConnectionID>MaxServers)) then Result:=$FF else begin regs.ax:=$EF03; RealModeIntr($21,regs); move( ptarr(nwPtr(regs.es,regs.si))^[(ConnectionID-1)*32], TableEntry, 32 ); With TableEntry do begin ServerAddress.socket:=swap(ServerAddress.socket); { Force lo-hi } ReceiveTimeOut:=swap(ReceiveTimeOut); { Force lo-hi } MaxTimeOut:=swap(MaxTimeOut); { Force lo-hi } WconnectionNumber:=swap(WconnectionNumber); { force lo-hi } end; Result:=$00; end; GetConnectionIDTable:=(Result=0); end; {DC.. [2.0/2.1/3.x]} Function GetConnectionNumber(Var ConnectionNbr:byte):boolean; { returns connection number of requesting WS (1..100) } var regs:TTRegisters; begin regs.Ah:=$DC; RealModeIntr($21,regs); ConnectionNbr:=Regs.AL; { logical WS connection # } { cl= first digit of logical conn #, ch= second digit of conn# } result:=0; GetConnectionNumber:=true; end; {F217/16 [2.15c+]} Function GetConnectionInformation (ConnectionNbr:byte; Var objName:String; Var objType:Word; Var objId:LongInt; Var LoginTime:TnovTime ):boolean; Type TReq=Record PacketLength : Word; FunctionVal : Byte; _ConnectionNo : Byte; End; Trep=Record _objId :LongInt; { hi-lo } _ObjType : word; { hi-lo } _ObjName : Array [1..48] of Byte; _LoginTime : TnovTime; Reserved:word; End; TPreq=^Treq; TPrep=^Trep; Var i,x: Integer; Begin With TPreq(GlobalReqBuf)^ Do Begin PacketLength := 2; FunctionVal := $16; _ConnectionNo := ConnectionNbr; End; F2SystemCall($17,SizeOf(Treq),SizeOf(TRep),result); If Result = 0 Then Begin With TPrep(GlobalReplyBuf)^ Do Begin ZstrCopy(ObjName,_objName,48); ObjId:=Lswap(_objId); ObjType:=swap(_objType); logintime:=_logintime; End; End; { patch to have a NIL object return an error. } if objName='' then result:=$FD; { no_such_connection } GetConnectionInformation:=(result=0); End { GetConnectInfo }; {F217/14 [2.15c+,unencrypted]} Function LoginToFileServer( objName:String; objType:word; password : string ):boolean; Type Treq=record len :word; subFunc :byte; _objType :Word; { hi-lo } _objName :String[47]; { asciiz? } _objPassw:String[127]; { allowed to be '' } end; TPreq=^Treq; Begin WITH TPreq(GlobalReqBuf)^ do begin len:=SizeOf(Treq)-2; subFunc:=$14; _objType:=swap(objType); PStrCopy(_objName,objName,47); _objName[47]:=#0; UpString(_objName); PStrCopy(_objPassw,password,127); UpString(_objPassw); end; F2SystemCall($17,SizeOf(Treq),0,result); LoginToFileServer:=(result=0) end; {F217/18 [3.x]} FUNCTION LoginEncrToFileServer(ObjName: String; ObjType: Word; PassWord: String): Boolean; { assumes the current effective server = the server to login to. } FUNCTION LoginEncrypted(ObjName : String; ObjType : Word; VAR key : TencryptionKey): Boolean; Type Treq=RECORD BufLen : Word; _func : Byte; _key : TencryptionKey; _ObjType: Word; _ObjName: String[48]; End; TPreq=^Treq; Begin With TPreq(GlobalReqBuf)^ do Begin _func := $18; _key := key; _ObjType := Swap(objType); PstrCopy(_ObjName,ObjName,48); UpString(_ObjName); if ObjName[0]<#48 then _objName[0]:=objName[0] else _objname[0]:=#48; BufLen:=ord(_ObjName[0])+12; End; F2SystemCall($17,SizeOf(Treq),0,result); LoginEncrypted:=(result=0); End; VAR key : TencryptionKey; ObjId:LongInt; Begin UpString(password); if password[0]>#127 Then password[0]:=#127; IF GetEncryptionKey(key) Then Begin IF GetBinderyObjectId(objName,objType,ObjId) Then Begin EncryptPassword(objId,password,key); LoginEncrypted(ObjName, ObjType, key); End; End Else LoginToFileServer(ObjName, ObjType, Password); LoginEncrToFileServer:= (result=0); End; {F219 [2.15c+]} Function Logout:boolean; {logout from all file servers, remains attached to Server, effective EOJ } begin F2SystemCall($19,0,0,result); result:=$00; Logout:=true; end; {F102 [2.0/2.1/3.x]} Function LogoutFromFileServer(var ConnectionID: byte):boolean; {logout from one file server} var regs : TTregisters; begin regs.ah := $F1; regs.al := $02; regs.dl := ConnectionID; RealModeIntr($21,regs); result:=00; LogoutFromFileServer:=True; end; {EE.. [2.0/2.1/3.x]} FUNCTION GetWorkstationNodeAddress( var physicalNodeAddress: TNodeAddress ):boolean; { Get the physical station address (6 bytes hi-endian) } Var Regs :TTRegisters; Begin {Get the physical address from the Network Card} Regs.Ah := $EE; RealModeIntr($21,Regs); result:=Regs.AL; {nw node= CX BX AX hi-endian} physicalNodeAddress[1]:=Regs.CH; physicalNodeAddress[2]:=Regs.CL; physicalNodeAddress[3]:=Regs.bh; physicalNodeAddress[4]:=Regs.bl; physicalNodeAddress[5]:=Regs.ah; physicalNodeAddress[6]:=Regs.al; result := 0; GetWorkstationNodeAddress:=true; End; {F217/13 [2.15c+]} Function GetInternetAddress( ConnNbr : byte; Var IntNetAddress:TinterNetworkAddress ):boolean; Type TReq=record length : word; subfunction : byte; connection : byte; end; TRep=record network : LongInt; { array [1..4] of byte } { hi-lo } node : array [1..6] of byte; { hi-lo } socket : word; { array [1..2] of byte } { hi-lo } end; TPreq=^Treq; TPrep=^Trep; BEGIN With TPreq(GlobalreqBuf)^ do begin length := 2; subfunction := $13; connection := ConnNbr; end; F2SystemCall($17,SizeOf(Treq),SizeOf(TRep),result); if result = 0 then With TPrep(GlobalreplyBuf)^ do begin move(network,IntNetAddress.net,4); {_is_ and stays hi-lo } move(node,IntNetAddress.node,6); { _is_ and stays hi-lo } IntNetAddress.socket:=swap(socket); { force lo-hi } end; GetInternetAddress:=(result=0); end; {D6.. [2.0/2.1/3.x]} FUNCTION EndOfJob(All : Boolean):boolean; { forces an end of job If All is TRUE, then ends all jobs, otherwise ends a single job. Ending a job unlocks and clears all locked or logged files and records. It close all open network and local files and resets error and lock modes. It also resets the workstation environment. } Var NovRegs:TTRegisters; BEGIN with NovRegs do begin AH := $D6; if All then BX := $FFFF else BX := $00; end; RealModeIntr($21,NovRegs); Result:=$00; EndOfJob:=True; end; {$IFDEF NewCalls} {F218 [2.15c+]} FUNCTION EndOfJob(All : Boolean):boolean; { forces an end of job If All is TRUE, then ends all jobs, otherwise ends a single job. Ending a job unlocks and clears all locked or logged files and records. It close all open network and local files and resets error and lock modes. It also resets the workstation environment. } Type Treq=record len:word; _all:word; end; { ??? ERR: unclear how the req buffer should be... } TPreq=^Treq; BEGIN With TPreq(GlobalReqBuf)^ do begin if All then _all := $FFFF else _all := $0000; len:=2; end; F2SystemCall($18,2,0,result); Result:=$00; EndOfJob:=True; end; {$ENDIF} {F217/0A [2.0/2.1/3.x]} Function EnterLoginArea( LoginSubDirName:String; numberOfLocalDrives:Byte ):boolean; { Changes the login directory. Used by boot-proms. LoginSubDirName contains the name of a sub directory under SYS:LOGIN (e.g. 'V330' means login.exe is to be executed in directory SYS:LOGIN\V330)} Type Treq=record len:word; subFunc:byte; _numLocDr:Byte; _subDirName:String[255]; end; TPreq=^Treq; Begin WITH TPreq(GlobalReqBuf)^ do begin len:=SizeOf(Treq)-2; subFunc:=$0A; _numLocDr:=numberOfLocalDrives; PstrCopy(_subDirName,LoginSubDirName,255); UpString(_subDirName); end; F2SystemCall($17,Sizeof(Treq),0,result); EnterLoginArea:=(result=0) end; {F217/15 [2.15c+]} Function GetObjectConnectionNumbers( objName:String; objType:Word; Var numberOfConnections: Byte; Var connections: TconnectionList ):boolean; { returns a list of connectionnumbers where objects of the desired type and name are logged in. Tconnectionlist is defined as an array[1..100] of byte. Max connections for Netware 286 = 100. Netware 386 allows more than 100 connections. If you pass a bad Objectname or the object is not logged in, the errorcode is NOT set to NO_SUCH_OBJECT ($FC), but GetObjectConnectionNumbers returns 0.} Type Treq=record len:word; subFunc:byte; _objType:Word; { hi-lo} _objName:String[47]; end; Trep=record _NbrOfConn:Byte; _connList:TconnectionList end; TPreq=^Treq; TPrep=^Trep; Begin WITH TPreq(GlobalReqBuf)^ do begin len:=SizeOf(Treq)-2; subFunc:=$15; PstrCopy(_objName,objName,47); _objname[47]:=#0; UpString(_objName); _objType:=swap(objType); end; F2SystemCall($17,SizeOf(Treq),SizeOf(Trep),result); With TPrep(GlobalReplyBuf)^ do begin connections:=_connList; NumberOfConnections:=_NbrOfConn; end; getObjectConnectionNumbers:=(result=0) end; {EA00 [2.0/2.1/3.x]} Function GetNetwareShellVersion( Var MajorVersion,MinorVersion, RevisionLevel :Byte ):Boolean; { Returns information about a WS environment. Queries shell. See also: GetWorkstationEnvironment } Var regs:TTRegisters; tmp1,tmp2:word; Begin With regs do begin AX:=$EA00; GetGlobalBufferAddress(tmp1,tmp2,ES,DI); { Set ES:DI to real-mode address of GlobalReplyBuffer } { Returned value NOT used, but registers need a valid value anyway. } RealModeIntr($21,regs); MajorVersion:=BH; MinorVersion:=BL; { shell version>=3.00 : } { CH = Shell Type. 0=conventional, 1= expanded, 2= extended } RevisionLevel:=CL; { 1=A,2=B etc. } end; Result:=$00; GetNetwareShellVersion:=True; end; {EAxx,xx>00 [2.0/2.1/3.x] (shell version >=3.00) } Function GetWorkstationEnvironment(Var OStype,OSversion, HardwareType,ShortHWType:String):Boolean; Type Treply=record stringz4:array[1..4*32] of char; end; TPreply=^Treply; Var regs:TTRegisters; sNo,k:Byte; tmp1,tmp2:word; Begin With regs do begin AX:=$EA01; BX:=$00; GetGlobalBufferAddress(tmp1,tmp2,ES,DI); { set ES:DI to real-mode address of GlobalReplyBuffer } RealModeIntr($21,regs); end; OStype:=''; OSVersion:=''; HardwareType:=''; ShortHWtype:=''; sNo:=0;k:=0; With TPreply(GlobalReplyBuf)^ do begin while sNo<4 do begin inc(k); while ((k<128) and (stringz4[k]<>#0)) do begin Case sNo of 0:OStype:=OStype+stringz4[k]; 1:OSversion:=OSversion+stringz4[k]; 2:HardwareType:=HardwareType+stringz4[k]; 3:ShortHWtype:=ShortHWtype+stringz4[k]; end; {case} inc(k); end; inc(Sno); end; end; Result:=$00; GetWorkstationEnvironment:=True; end; {DD.. [2.0/2.1/3.x]} Function SetNetwareErrorMode( errMode:Byte):boolean; { Sets the shell's handling mode for dealing with netware errors. 0: default, INT 24 handler 'Abort, Retry, Fail'; 1: a netware error number is returned in AL; 2: the netware error number is translated to a DOS error number, this number is returned. An EOJ resets the errormode to 0. } Var regs:TTregisters; begin Regs.AH:=$DD; Regs.DL:=errMode; RealModeIntr($21,Regs); { regs.al now contains previous error mode } Result:=$00; SetNetWareErrorMode:=True; end; {DD.. [2.0/2.1/3.x]} Function GetNetwareErrorMode(Var errMode:Byte):boolean; Var regs:TTregisters; begin Regs.AH:=$DD; Regs.DL:=0; RealModeIntr($21,Regs); { regs.al now contains previous error mode } errMode:=regs.al; regs.ah:=$DD; RealModeIntr($21,regs); { reset old error mode } Result:=$00; GetNetWareErrorMode:=True; end; {BB.. [2.0/2.1/3.x]} Function SetEndOfJobStatus( EndOfJobFlag: Boolean ):Boolean; { When this function is called with EndOfJobFlag=False and control is returned to the root COMMAND.COM, COMMAND.COM will NOT perform an EOJ action. } Var regs:TTRegisters; begin regs.AH:=$BB; If EndOfJobFlag then regs.AL:=$01 else regs.AL:=$00; RealModeIntr($21,Regs); { AL now contains previous EOJ Flag } Result:=$00; SetEndOfJobStatus:=True; end; {BB.. [2.0/2.1/3.x]} Function GetEndOfJobStatus(Var EndOfJobFlag: Boolean ):Boolean; Var regs:TTRegisters; begin regs.AH:=$BB; regs.al:=$00; RealModeIntr($21,Regs); { AL now contains previous EOJ Flag } EndOfJobFlag:=(regs.al<>0); regs.ah:=$BB; RealModeIntr($21,regs); { reset old eoj-status } Result:=$00; GetEndOfJobStatus:=True; end; {E908 (shell 3.00+)} Function SetShowDots( Show:Boolean):Boolean; Var regs:TTregisters; begin regs.ax:=$E908; if Show then regs.bl:=$01 else regs.bl:=$00; RealModeIntr($21,Regs); Result:=$00; SetShowDots:=True; end; {E908 (shell 3.00+)} Function GetShowDots(Var Shown:Boolean):Boolean; Var regs:TTregisters; begin regs.ax:=$E908; RealModeIntr($21,Regs); Shown:=(regs.bl<>0); regs.ax:=$E908; RealModeIntr($21,regs); {reset old 'show dots' parameter} Result:=$00; GetShowDots:=True; end; {DB.. [2.0/2.1/3.x]} Function GetNumberOfLocalDrives( Var drives:Byte ):Boolean; Var regs:TTregisters; begin regs.ah:=$DB; RealModeIntr($21,Regs); drives:=Regs.AL; Result:=$00; GetNumberOfLocalDrives:=TRUE; end; {=======SECONDARY FUNCTIONS===================================================} {EF03 [2.0/2.1/3.x] secondary Function } Function IsConnectionIDinUse( ConnectionID: Byte ):boolean; { This function returns FALSE if connId isn't in the range [1..MaxServers] } Type ptarr=^arr; arr=Array[0..MaxServers*32] of Byte; Var regs:TTregisters; begin If ((ConnectionID<1) or (ConnectionID>MaxServers)) then IsConnectionIDInUse:=FALSE { NWTP04: TRUE } else begin regs.ax:=$EF03; RealModeIntr($21,regs); IsConnectionIDinUse:=(ptarr(nwPtr(regs.es,regs.si))^[(ConnectionID-1)*32] <> $00 ) end; end; Function GetUserAtConnection( ConnectionNbr:byte; var username: string):boolean; {This function provides a shorter method of obtaining just the USERID.} var id:LongInt; typ:word; time:TnovTime; begin getUserAtConnection:=GetConnectionInformation(ConnectionNbr,username,typ,id,time); end; Function GetEffectiveConnectionID(Var connId:byte):boolean; begin if NOT (GetPreferredConnectionID(connId) and (connId<>0)) then if NOT (GetDefaultConnectionID(ConnId) and (connId<>0)) then GetPrimaryConnectionID(ConnId); GetEffectiveConnectionID:=(result=$00); end; Function GetObjectLoginControl(ObjName:string; ObjType:word; VAR LoginControlInfo:TloginControl):boolean; { Caller must have access to the bindery property LOGIN_CONTROL. Default: you need to be supervisor-equivalent or the object the property is associated with. (reading your 'own' information) PasswordcontrolFlag: 00 User is allowed to change PW. 01 User is NOT allowed to change PW. 02 User is allowed to change PW, but the new password must be unique. 03 User is NOT allowed to change PW, and a new password, to be changed by the supervisor, must be unique. } Var LCpropVal:Tproperty; lc:record _AccExpDate :array[1..3] of byte; {yy mm dd} _AccDisabled :boolean; _PWexpDate :array[1..3] of byte; {yy mm dd} _GraceLoginsRemaining:byte; _DaysBetwPWchanges :word; {hi-lo} _MaxGraceLogins :byte; _minPWlen :byte; _unknown1 :byte; {! = hi-byte of maxConcConn } _MaxConcConn :byte; _loginTimes :array[1..42] of byte; _LastLoginTime :array[1..6] of byte; {yy mm dd hh mm ss} _PWcontrol :byte; _unknown2 :byte; { not used } _MaxDiskSpace :Longint; { hi-lo } _unknown3 :Byte; {! = hi-byte of bad login count } _badLoginCount :byte; _AccountResetTime :LongInt; { minutes since 1/1/1985 } _lastIntruderAddress :TinterNetworkAddress; end ABSOLUTE LCpropVal; moreSegments:boolean; propFlags:byte; Procedure Min2NovTime(m:Longint; Var time:TnovTime); Const darr:array[1..12] of word=(0,31,59,90,120,151,181,212,243,273,304,334); Var d,dr:word; i,Lastleap:byte; begin d:=(m div 1440); i:=0; lastLeap:=84; while d>((3+(i*4))*365)+31+28 do begin dec(d); lastLeap:=85+3+(i*4); inc(i); end; WITH time do begin year:=(d DIV 365)+85; dr:=(d MOD 365); month:=1; while (month<12) and (dr>darr[month+1]) do inc(month); day:=(dr-darr[month]); if (day=28) and (month=2) and (lastLeap=year) then inc(day); dr:=(m mod 1440); hour:=(dr div 60); min:=(dr mod 60); sec:=0; end; end; begin IF nwBindry.ReadPropertyValue(ObjName,ObjType,'LOGIN_CONTROL',1, LCpropval,moreSegments,propFlags) then begin FillChar(LoginControlInfo,SizeOf(LoginControlInfo),#0); With LoginControlInfo do begin AccountDisabled :=lc._AccDisabled; move(lc._AccExpDate[1],AccountExpirationDate.year,3); move(lc._PWexpDate[1],PasswordExpirationDate.year,3); MinimumPasswordLength :=lc._minPWlen; PasswordControlFlag :=lc._PWcontrol; DaysBetweenPasswordChanges:=swap(lc._DaysBetwPWchanges); Move(lc._lastLoginTime[1],LastLoginTime.year,6); GraceLoginsRemaining :=lc._GraceLoginsRemaining; MaxGraceLoginsAllowed :=lc._maxGraceLogins; BadLoginCount :=lc._badLoginCount; Min2NovTime(Lswap(lc._AccountResetTime),AccountResetTime); LastIntruderAddress :=lc._LastIntruderAddress; LastIntruderAddress.socket:=swap(LastIntruderAddress.socket); {force lo-hi} MaxConcurrentConnections :=lc._MaxConcConn; Move(lc._LoginTimes[1],LoginTimes[1],42); DiskSpace :=Lswap(lc._MaxDiskSpace); end; result:=$00; end else result:=nwBindry.result; GetObjectLoginControl:=(result=0); end; Function ObjectCanLoginAt(ObjName:String; ObjType:Word; LoginTime:TnovTime ):Boolean; { Caller must have access to the bindery property LOGIN_CONTROL. Default: you need to be supervisor-equivalent or the object the property is associated with. (reading your 'own' information) -If one or more of the fields hour,min,sec,dayOfWeek contain a value >0, the supplied time will be checked against the login timerestrictions. (this means that checking '00:00 on sundays' is impossible) -If one or more of the fields year,month,day contain a value >0 , the date will be checked with the expiration date of the account and with the Account disabled Flag. } Var CanLog:Boolean; Info:Tlogincontrol; half_hrs:word; begin IF GetObjectLoginControl(ObjName,ObjType,Info) then begin if (logintime.month>0) and (loginTime.day>0) then CanLog:=((NOT Info.AccountDisabled) and IsLaterNovTime(Info.AccountExpirationDate,loginTime)) else CanLog:=true; if (logintime.hour>0) or (loginTime.min>0) or (logintime.sec>0) or (logintime.DayOfWeek>0) then begin half_hrs:=(loginTime.DayOfWeek * 48)+(LoginTime.hour *2); if LoginTime.min>=30 then inc(half_hrs); If half_hrs>=336 then result:=$122 else CanLog:=CanLog AND ((Info.LoginTimes[(half_hrs DIV 8)+1] AND (1 SHL (half_hrs MOD 8)) ) >0) end; end else begin CanLog:=(result=$FB); {no such property} result:=0; end; ObjectCanLoginAt:=(result=0) and CanLog; end; Function GetObjectNodeControl( ObjName:string; ObjType:word; {i/o} Var seqNbr:integer; {out} Var NodeControlInfo:TnodeControl):boolean; Var NCpropVal:Tproperty; moreSegments:boolean; propFlags:byte; begin if seqNbr=$FBFB then result:=$EC else begin if seqNbr<1 then seqNbr:=1; IF nwBindry.ReadPropertyValue(ObjName,ObjType,'NODE_CONTROL',seqNbr, NCpropval,moreSegments,propFlags) then begin Move(NCpropVal,NodeControlInfo,120); if moreSegments then inc(seqNbr) else seqNbr:=Integer($FBFB); end else result:=nwBindry.result; end; GetObjectNodeControl:=(result=0); { $EC No more records (no such segment); $FB No restrictions found (No such property) } end; end. { end of unit nwConn }