From 94a5ab5902748f5388b35b7ced2c993e4d7a2b48 Mon Sep 17 00:00:00 2001 From: Mario Fetka Date: Tue, 12 May 2026 20:38:28 +0200 Subject: [PATCH] Import nweadmin 0.1b from upstream zip --- COPYING | 339 +++++ NWADMIN.EXE | Bin 0 -> 237176 bytes NWTP/COPYING | 339 +++++ NWTP/FILE_ID.DIZ | 9 + NWTP/NWACCT.PAS | 561 +++++++ NWTP/NWBINDRY.PAS | 1442 ++++++++++++++++++ NWTP/NWCONN.PAS | 1455 ++++++++++++++++++ NWTP/NWFILE.PAS | 2999 +++++++++++++++++++++++++++++++++++++ NWTP/NWINTR.PAS | 761 ++++++++++ NWTP/NWIPX.PAS | 606 ++++++++ NWTP/NWLOCK.PAS | 663 ++++++++ NWTP/NWMESS.PAS | 308 ++++ NWTP/NWMISC.PAS | 961 ++++++++++++ NWTP/NWQMS.PAS | 1149 ++++++++++++++ NWTP/NWSEMA.PAS | 330 ++++ NWTP/NWSERV.PAS | 748 +++++++++ NWTP/NWSPX.PAS | 315 ++++ NWTP/NWTP.FAQ | 198 +++ NWTP/NWTP.TPH | Bin 0 -> 239613 bytes NWTP/NWTP06.TXT | 9 + NWTP/README.1ST | 22 + NWTP/README.EXE | Bin 0 -> 487800 bytes NWTP/REL.TXT | 96 ++ NWTP/THELP.CFG | 3 + NWTP/THELP.COM | Bin 0 -> 11088 bytes NWTP/XACCT/ACCT.PAS | 63 + NWTP/XACCT/TSTACCT.PAS | 171 +++ NWTP/XBINDRY/BACKBIN.PAS | 143 ++ NWTP/XBINDRY/NEW.TXT | 69 + NWTP/XBINDRY/NEW2.TXT | 53 + NWTP/XBINDRY/NONAME00.EXE | Bin 0 -> 3904 bytes NWTP/XBINDRY/NWPN9401.TXT | 199 +++ NWTP/XBINDRY/OT_XXX | 1212 +++++++++++++++ NWTP/XBINDRY/SCANBIND.BAK | 381 +++++ NWTP/XBINDRY/SCANBIND.EXE | Bin 0 -> 13456 bytes NWTP/XBINDRY/SCANBIND.PAS | 376 +++++ NWTP/XBINDRY/SUPEQ.PAS | 38 + NWTP/XBINDRY/SWAPNAME.PAS | 96 ++ NWTP/XBINDRY/TSTBIND.PAS | 249 +++ NWTP/XCONN/CEXPPW.PAS | 66 + NWTP/XCONN/CHKATT.PAS | 44 + NWTP/XCONN/DETACH.PAS | 31 + NWTP/XCONN/LOGCON.PAS | 81 + NWTP/XCONN/LOGOUT.PAS | 214 +++ NWTP/XCONN/PWEXP.PAS | 63 + NWTP/XCONN/TRCOPY.PAS | 188 +++ NWTP/XCONN/TSTCONN.PAS | 172 +++ NWTP/XCONN/TSTCONN2.PAS | 127 ++ NWTP/XCONN/TSTCONN3.PAS | 53 + NWTP/XCONN/WHO.PAS | 307 ++++ NWTP/XFILE/GETOFIL.PAS | 87 ++ NWTP/XFILE/LDIR.PAS | 243 +++ NWTP/XFILE/TSTDH.PAS | 211 +++ NWTP/XFILE/TSTENT2.PAS | 138 ++ NWTP/XFILE/TSTENTRY.PAS | 91 ++ NWTP/XFILE/TSTTRUST.PAS | 58 + NWTP/XFILE/TSTVOL.PAS | 170 +++ NWTP/XFILE/USPACE.PAS | 100 ++ NWTP/XFILE/VOLSTAT.PAS | 111 ++ NWTP/XIPX/APPN9001.TXT | 1644 ++++++++++++++++++++ NWTP/XIPX/APPN9008.TXT | 288 ++++ NWTP/XIPX/BLTS9401.TXT | 758 ++++++++++ NWTP/XIPX/CHKVEND.PAS | 105 ++ NWTP/XIPX/FGET.PAS | 201 +++ NWTP/XIPX/FSEND.PAS | 255 ++++ NWTP/XIPX/M1_PEP.PAS | 230 +++ NWTP/XIPX/M_PEP.PAS | 176 +++ NWTP/XIPX/NWPEP.PAS | 97 ++ NWTP/XIPX/NWRIP.PAS | 152 ++ NWTP/XIPX/NWSAP.PAS | 50 + NWTP/XIPX/R1_HELLO.PAS | 136 ++ NWTP/XIPX/R2_HELLO.PAS | 107 ++ NWTP/XIPX/R3_HELLO.PAS | 146 ++ NWTP/XIPX/R_HELLO.PAS | 92 ++ NWTP/XIPX/S1_HELLO.PAS | 114 ++ NWTP/XIPX/S1_PEP.PAS | 170 +++ NWTP/XIPX/SHWSAPS.PAS | 189 +++ NWTP/XIPX/SKT_XXX | 1881 +++++++++++++++++++++++ NWTP/XIPX/S_HELLO.PAS | 76 + NWTP/XIPX/S_PEP.PAS | 125 ++ NWTP/XIPX/TSTRIP.PAS | 30 + NWTP/XIPX/VEND_XXX | 246 +++ NWTP/XLOCK/TSTLRL.PAS | 74 + NWTP/XLOCK/TSTPFL.PAS | 65 + NWTP/XMESS/PMAIL.PAS | 409 +++++ NWTP/XMESS/TSTMESS.PAS | 118 ++ NWTP/XMESS/XPMAIL.PAS | 21 + NWTP/XOTHER/PHONE.PAS | 819 ++++++++++ NWTP/XOTHER/TVLM.PAS | 83 + NWTP/XQMS/QAVAIL.PAS | 95 ++ NWTP/XSEMA/SEMATEST.PAS | 91 ++ NWTP/XSEMA/TSTSEMA2.PAS | 86 ++ NWTP/XSERV/CLRCONN.PAS | 59 + NWTP/XSERV/LOGLOCK.PAS | 39 + NWTP/XSERV/TSTSERV.PAS | 122 ++ README | 92 ++ SRC/COPYING | 339 +++++ SRC/DELUSER.DCU | Bin 0 -> 1872 bytes SRC/DELUSER.DFM | Bin 0 -> 842 bytes SRC/DELUSER.PAS | 29 + SRC/EDUSER.DCU | Bin 0 -> 6112 bytes SRC/EDUSER.DFM | Bin 0 -> 1484 bytes SRC/EDUSER.PAS | 116 ++ SRC/MYADMIN.DPR | 35 + SRC/MYADMIN.EXE | Bin 0 -> 237176 bytes SRC/MYADMIN.OPT | 34 + SRC/MYADMIN.RES | Bin 0 -> 794 bytes SRC/README.TXT | 51 + SRC/STARTFRM.DCU | Bin 0 -> 6416 bytes SRC/STARTFRM.DFM | Bin 0 -> 4707 bytes SRC/STARTFRM.PAS | 145 ++ SRC/SVRUTIL.DCU | Bin 0 -> 3168 bytes SRC/SVRUTIL.DFM | Bin 0 -> 951 bytes SRC/SVRUTIL.PAS | 69 + SRC/UNITS/CHGPASS2.DCU | Bin 0 -> 1696 bytes SRC/UNITS/CHGPASS2.DFM | Bin 0 -> 534 bytes SRC/UNITS/CHGPASS2.PAS | 27 + SRC/UNITS/NWACCT.PAS | 561 +++++++ SRC/UNITS/NWBINDRY.DCU | Bin 0 -> 33408 bytes SRC/UNITS/NWBINDRY.PAS | 1442 ++++++++++++++++++ SRC/UNITS/NWCONN.DCU | Bin 0 -> 28768 bytes SRC/UNITS/NWCONN.PAS | 1455 ++++++++++++++++++ SRC/UNITS/NWFILE.PAS | 2999 +++++++++++++++++++++++++++++++++++++ SRC/UNITS/NWINTR.DCU | Bin 0 -> 12784 bytes SRC/UNITS/NWINTR.PAS | 761 ++++++++++ SRC/UNITS/NWIPX.PAS | 606 ++++++++ SRC/UNITS/NWLOCK.PAS | 663 ++++++++ SRC/UNITS/NWMESS.DCU | Bin 0 -> 5280 bytes SRC/UNITS/NWMESS.PAS | 308 ++++ SRC/UNITS/NWMISC.DCU | Bin 0 -> 22368 bytes SRC/UNITS/NWMISC.PAS | 961 ++++++++++++ SRC/UNITS/NWQMS.PAS | 1149 ++++++++++++++ SRC/UNITS/NWSEMA.PAS | 330 ++++ SRC/UNITS/NWSERV.DCU | Bin 0 -> 15632 bytes SRC/UNITS/NWSERV.PAS | 748 +++++++++ SRC/UNITS/NWSPX.PAS | 315 ++++ SRC/UNITS/SENDMSG.DCU | Bin 0 -> 2256 bytes SRC/UNITS/SENDMSG.DFM | Bin 0 -> 966 bytes SRC/UNITS/SENDMSG.PAS | 37 + SRC/USER.DCU | Bin 0 -> 5808 bytes SRC/USER.DFM | Bin 0 -> 2684 bytes SRC/USER.PAS | 118 ++ 142 files changed, 41657 insertions(+) create mode 100644 COPYING create mode 100644 NWADMIN.EXE create mode 100644 NWTP/COPYING create mode 100644 NWTP/FILE_ID.DIZ create mode 100644 NWTP/NWACCT.PAS create mode 100644 NWTP/NWBINDRY.PAS create mode 100644 NWTP/NWCONN.PAS create mode 100644 NWTP/NWFILE.PAS create mode 100644 NWTP/NWINTR.PAS create mode 100644 NWTP/NWIPX.PAS create mode 100644 NWTP/NWLOCK.PAS create mode 100644 NWTP/NWMESS.PAS create mode 100644 NWTP/NWMISC.PAS create mode 100644 NWTP/NWQMS.PAS create mode 100644 NWTP/NWSEMA.PAS create mode 100644 NWTP/NWSERV.PAS create mode 100644 NWTP/NWSPX.PAS create mode 100644 NWTP/NWTP.FAQ create mode 100644 NWTP/NWTP.TPH create mode 100644 NWTP/NWTP06.TXT create mode 100644 NWTP/README.1ST create mode 100644 NWTP/README.EXE create mode 100644 NWTP/REL.TXT create mode 100644 NWTP/THELP.CFG create mode 100644 NWTP/THELP.COM create mode 100644 NWTP/XACCT/ACCT.PAS create mode 100644 NWTP/XACCT/TSTACCT.PAS create mode 100644 NWTP/XBINDRY/BACKBIN.PAS create mode 100644 NWTP/XBINDRY/NEW.TXT create mode 100644 NWTP/XBINDRY/NEW2.TXT create mode 100644 NWTP/XBINDRY/NONAME00.EXE create mode 100644 NWTP/XBINDRY/NWPN9401.TXT create mode 100644 NWTP/XBINDRY/OT_XXX create mode 100644 NWTP/XBINDRY/SCANBIND.BAK create mode 100644 NWTP/XBINDRY/SCANBIND.EXE create mode 100644 NWTP/XBINDRY/SCANBIND.PAS create mode 100644 NWTP/XBINDRY/SUPEQ.PAS create mode 100644 NWTP/XBINDRY/SWAPNAME.PAS create mode 100644 NWTP/XBINDRY/TSTBIND.PAS create mode 100644 NWTP/XCONN/CEXPPW.PAS create mode 100644 NWTP/XCONN/CHKATT.PAS create mode 100644 NWTP/XCONN/DETACH.PAS create mode 100644 NWTP/XCONN/LOGCON.PAS create mode 100644 NWTP/XCONN/LOGOUT.PAS create mode 100644 NWTP/XCONN/PWEXP.PAS create mode 100644 NWTP/XCONN/TRCOPY.PAS create mode 100644 NWTP/XCONN/TSTCONN.PAS create mode 100644 NWTP/XCONN/TSTCONN2.PAS create mode 100644 NWTP/XCONN/TSTCONN3.PAS create mode 100644 NWTP/XCONN/WHO.PAS create mode 100644 NWTP/XFILE/GETOFIL.PAS create mode 100644 NWTP/XFILE/LDIR.PAS create mode 100644 NWTP/XFILE/TSTDH.PAS create mode 100644 NWTP/XFILE/TSTENT2.PAS create mode 100644 NWTP/XFILE/TSTENTRY.PAS create mode 100644 NWTP/XFILE/TSTTRUST.PAS create mode 100644 NWTP/XFILE/TSTVOL.PAS create mode 100644 NWTP/XFILE/USPACE.PAS create mode 100644 NWTP/XFILE/VOLSTAT.PAS create mode 100644 NWTP/XIPX/APPN9001.TXT create mode 100644 NWTP/XIPX/APPN9008.TXT create mode 100644 NWTP/XIPX/BLTS9401.TXT create mode 100644 NWTP/XIPX/CHKVEND.PAS create mode 100644 NWTP/XIPX/FGET.PAS create mode 100644 NWTP/XIPX/FSEND.PAS create mode 100644 NWTP/XIPX/M1_PEP.PAS create mode 100644 NWTP/XIPX/M_PEP.PAS create mode 100644 NWTP/XIPX/NWPEP.PAS create mode 100644 NWTP/XIPX/NWRIP.PAS create mode 100644 NWTP/XIPX/NWSAP.PAS create mode 100644 NWTP/XIPX/R1_HELLO.PAS create mode 100644 NWTP/XIPX/R2_HELLO.PAS create mode 100644 NWTP/XIPX/R3_HELLO.PAS create mode 100644 NWTP/XIPX/R_HELLO.PAS create mode 100644 NWTP/XIPX/S1_HELLO.PAS create mode 100644 NWTP/XIPX/S1_PEP.PAS create mode 100644 NWTP/XIPX/SHWSAPS.PAS create mode 100644 NWTP/XIPX/SKT_XXX create mode 100644 NWTP/XIPX/S_HELLO.PAS create mode 100644 NWTP/XIPX/S_PEP.PAS create mode 100644 NWTP/XIPX/TSTRIP.PAS create mode 100644 NWTP/XIPX/VEND_XXX create mode 100644 NWTP/XLOCK/TSTLRL.PAS create mode 100644 NWTP/XLOCK/TSTPFL.PAS create mode 100644 NWTP/XMESS/PMAIL.PAS create mode 100644 NWTP/XMESS/TSTMESS.PAS create mode 100644 NWTP/XMESS/XPMAIL.PAS create mode 100644 NWTP/XOTHER/PHONE.PAS create mode 100644 NWTP/XOTHER/TVLM.PAS create mode 100644 NWTP/XQMS/QAVAIL.PAS create mode 100644 NWTP/XSEMA/SEMATEST.PAS create mode 100644 NWTP/XSEMA/TSTSEMA2.PAS create mode 100644 NWTP/XSERV/CLRCONN.PAS create mode 100644 NWTP/XSERV/LOGLOCK.PAS create mode 100644 NWTP/XSERV/TSTSERV.PAS create mode 100644 README create mode 100644 SRC/COPYING create mode 100644 SRC/DELUSER.DCU create mode 100644 SRC/DELUSER.DFM create mode 100644 SRC/DELUSER.PAS create mode 100644 SRC/EDUSER.DCU create mode 100644 SRC/EDUSER.DFM create mode 100644 SRC/EDUSER.PAS create mode 100644 SRC/MYADMIN.DPR create mode 100644 SRC/MYADMIN.EXE create mode 100644 SRC/MYADMIN.OPT create mode 100644 SRC/MYADMIN.RES create mode 100644 SRC/README.TXT create mode 100644 SRC/STARTFRM.DCU create mode 100644 SRC/STARTFRM.DFM create mode 100644 SRC/STARTFRM.PAS create mode 100644 SRC/SVRUTIL.DCU create mode 100644 SRC/SVRUTIL.DFM create mode 100644 SRC/SVRUTIL.PAS create mode 100644 SRC/UNITS/CHGPASS2.DCU create mode 100644 SRC/UNITS/CHGPASS2.DFM create mode 100644 SRC/UNITS/CHGPASS2.PAS create mode 100644 SRC/UNITS/NWACCT.PAS create mode 100644 SRC/UNITS/NWBINDRY.DCU create mode 100644 SRC/UNITS/NWBINDRY.PAS create mode 100644 SRC/UNITS/NWCONN.DCU create mode 100644 SRC/UNITS/NWCONN.PAS create mode 100644 SRC/UNITS/NWFILE.PAS create mode 100644 SRC/UNITS/NWINTR.DCU create mode 100644 SRC/UNITS/NWINTR.PAS create mode 100644 SRC/UNITS/NWIPX.PAS create mode 100644 SRC/UNITS/NWLOCK.PAS create mode 100644 SRC/UNITS/NWMESS.DCU create mode 100644 SRC/UNITS/NWMESS.PAS create mode 100644 SRC/UNITS/NWMISC.DCU create mode 100644 SRC/UNITS/NWMISC.PAS create mode 100644 SRC/UNITS/NWQMS.PAS create mode 100644 SRC/UNITS/NWSEMA.PAS create mode 100644 SRC/UNITS/NWSERV.DCU create mode 100644 SRC/UNITS/NWSERV.PAS create mode 100644 SRC/UNITS/NWSPX.PAS create mode 100644 SRC/UNITS/SENDMSG.DCU create mode 100644 SRC/UNITS/SENDMSG.DFM create mode 100644 SRC/UNITS/SENDMSG.PAS create mode 100644 SRC/USER.DCU create mode 100644 SRC/USER.DFM create mode 100644 SRC/USER.PAS diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..76ce5a6 --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/NWADMIN.EXE b/NWADMIN.EXE new file mode 100644 index 0000000000000000000000000000000000000000..5f9ec39f64d72d6985afdbea5776afb99b03a152 GIT binary patch literal 237176 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 AjsO4v literal 0 HcmV?d00001 diff --git a/NWTP/COPYING b/NWTP/COPYING new file mode 100644 index 0000000..e77696a --- /dev/null +++ b/NWTP/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/NWTP/FILE_ID.DIZ b/NWTP/FILE_ID.DIZ new file mode 100644 index 0000000..81dee58 --- /dev/null +++ b/NWTP/FILE_ID.DIZ @@ -0,0 +1,9 @@ +Netware Interface Units for Pascal. +(Novell Netware 3.x and TP/BP 6.x/7.x) +Freeware. Full sources, over 300 kb. of +documentation and lots of examples. The +most complete API available for PASCAL. +KEYWORDS: LAN, Network, Novell, API, +Library, Bindery, IPX, SPX, Queue, +Tool, Reference, Workstation, Client, +Real Mode, Protected Mode, Windows. diff --git a/NWTP/NWACCT.PAS b/NWTP/NWACCT.PAS new file mode 100644 index 0000000..e6ad4ce --- /dev/null +++ b/NWTP/NWACCT.PAS @@ -0,0 +1,561 @@ +{$X+,B-,V-,S-} {essential compiler directives} + +Unit nwAcct; + +{ nwAcct unit as of 950301 / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +INTERFACE + +Uses nwIntr,nwMisc,nwBindry,nwConn; + +{ Primary functions: Interrupt: Comments: + +* GetAccountStatus (F217/96) (1) +* SubmitAccountCharge (F217/97) (2)(3) +* SubmitAccountHold (F217/98) (2) +* SubmitAccountNote (F217/99) (2) + + Secondary functions: + +* AccountingInstalled (4) +* SetAccountStatus (5) +* AddAccountingServer (5) +* DeleteAccountingServer (5) +* DeleteAccountHolds (2) + + Notes: (1) To be called by: + -accounting servers; + -supervisor equivalent users; + -objects querying their own account status. + (2) To be called by accounting servers only. + (3) Can be imitated by supervisor-equivalent users by + calling GetAccountStatus and SetAccountStatus. Atomicity + of such a bindery transaction can not be guaranteed. + (4) Can be called by all logged on users. + (5) Supervisor equivalent users only. + +} + +Var result:word; + +{ Type definitions based on NET$ACCT.FMT by Wolfgang Schreiber } +{ See Acct.pas in the XACCT archive for an example of their use. } + +CONST { Accounting file record types } + RT_SUBMIT_CHARGE=1; + RT_ACCOUNT_NOTE =2; + + { comment types within accounting file } + + CT_CONN_CHARGE = 1; + CT_STORAGE_CHARGE = 2; + CT_LOGIN_NOTE = 3; + CT_LOGOUT_NOTE = 4; + CT_INTRUDER_NOTE = 5; + CT_TIMEMOD_NOTE = 6; + CT_BOOT_NOTE = 8; + CT_DOWN_NOTE = 9; + CT_COMMENT = 99; + +Type TAccDateTime6 = Array [1..6] of Byte; { date and time stamp of entry YMDHMS} + +Type TComment = RECORD { interprete comments according to CmtType } + CASE Integer of + CT_CONN_CHARGE : (ConnectTime : LongInt; + RequestCount : LongInt; + BytesRead : Array[1..6] of BYTE; {hi-lo} + BytesWritten : Array[1..6] of BYTE); {hi-lo} + CT_STORAGE_CHARGE : (BlocksOwned : LongInt; + HalfHours : LongInt); + CT_LOGIN_NOTE, + CT_LOGOUT_NOTE, + CT_INTRUDER_NOTE : (Net :TnetworkAddress; + Node:TnodeAddress); + CT_TIMEMOD_NOTE : (ServerTime : TAccDateTime6); + CT_BOOT_NOTE, + CT_DOWN_NOTE : ();{ NO comment fields } + CT_COMMENT : (Comment : String) + END; + +{ Use either the Type SubmitCharge or SubmitNote to interprete + an entry - decide on typecasting with the aid of the RecType field. } + +Type TChargeRecord = RECORD + Length : Word; + ServerObjId : LongInt; {hi-lo} + TimeStamp : TAccDateTime6; + RecType : BYTE; {Record type Note/Charge} + ccode : BYTE; {completion code} + ServiceType : WORD; {hi-lo} + ClientObjID : LongInt; {hi-lo} + Charge : LongInt; {hi-lo} + CommentType : WORD; {hi-lo} + Comment : Tcomment; {Variable length field} + END; + +Type TNoteRecord = RECORD + Length : Word; + ServerObjId : LongInt; {hi-lo} + TimeStamp : TAccDateTime6; + RecType : BYTE; + ccode : BYTE; + ServiceType : WORD; {hi-lo} + ClientObjID : LongInt; {hi-lo} + CommentType : WORD; {hi-lo} + Comment : TComment; + END; + + +{F217/96 [2.15c+]} +Function GetAccountStatus(objName:string; objType:word; + Var balance,limit,holds:LongInt):boolean; + +{F217/97 [2.15c+]} +Function SubmitAccountCharge(objName:string; objType:word; + charge,cancelHoldAmount:Longint; + serviceType, commentType:word; comment:string):boolean; + +{F217/98 [2.15c+]} +Function SubmitAccountHold(objName:string; objType:word; + reserveAmount:Longint ):boolean; + +{F217/99 [2.15c+]} +Function SubmitAccountNote(objName:string; objType:word; + serviceType,commentType:word; comment:string):boolean; + +{--------Secondary Functions-----------------------------------------------} + +Function AccountingInstalled:boolean; + +Function SetAccountStatus(objName:string; objType:word; balance,limit:LongInt):boolean; +{ need to be supervisor equivalent to use this call } + +Function AddAccountingServer(objName:string;objType:word):boolean; +{ need to be supervisor equivalent to use this call } + +Function DeleteAccountingServer(objName:string;objType:word):boolean; +{ need to be supervisor equivalent to use this call } + +Function DeleteAccountHolds(objName:string; objType:word):boolean; +{ delete all holds the caller (an accounting server) has on the + object with name objName of type objType. } + +Type Tcharge=record + DaysOfCharge:Byte; { bit 0=sunday,.. bit 6=saturday } + TimeOfCharge:Byte; { 0:00=0 ..23:30 =47, half-hour + during which the specified 'new' rate takes effect. } + ChargeRateMultiplier, + ChargeRateDivisor:Word; + end; + TchargeRec=record + NextChargeTime:Longint; { minutes since 1-1-1985 } + charges:array[1..20] of Tcharge; + end; + + +Type TchargeTableEntry=array[0..47] of Real; +Var ChargeTable:Array [0..6] of TchargeTableEntry; + +IMPLEMENTATION {===========================================================} + +Procedure GetBindryAccountStatus(objName:string; objType:word; + Var balance,limit,holds:LongInt); +{ called by GetAccountStatus when the calling object isn't an + accounting server. The F217/96 fails, but a bindery read will + work for supervisor-equivalent users. } +Var accPropVal:Tproperty; + accVal: record + _balance:LongInt; {hi-lo} + _limit:LongInt; {hi-lo} + _Reserved:array[1..120] of byte; { NW internal info } + end ABSOLUTE accPropVal; + holdPropVal:Tproperty; + holdVal: array[1..16] + of record + AccountServerID:Longint; {hi-lo} + HoldAmount :LongInt; {hi-lo} + end ABSOLUTE holdPropVal; + moreSegments:boolean; + t,propFlags:byte; +begin +IF ReadPropertyValue(objName,objType,'ACCOUNT_BALANCE',1, + accPropVal,moreSegments,propFlags) + then begin + balance:=Lswap(accVal._balance); + limit:=Lswap(accVal._limit); + IF ReadPropertyValue(objName,objType,'ACCOUNT_HOLDS',1, + holdPropVal,moreSegments,propFlags) + then begin { holds exist. } + holds:=0; + for t:=1 to 16 + do if holdVal[t].AccountServerID<>0 + then holds:=holds+Lswap(holdVal[t].HoldAmount); + end; + if nwBindry.result=$FB + then begin + result:=0; + holds:=0; + end + else result:=nwBindry.result; + end + else if nwBindry.result=$FB { no such property } + then result:=$C1 + else if nwBindry.result=$F1 { invalid bindery security } + then result:=$C0 + else result:=nwBindry.result; +{ resultcodes: 00 success; C0 No Account Privileges; C1 No Account Balance; + 96 Server Out Of memory; FC No Such Object; FE Server Bindery Locked; + FF Bindery Failure} +end; + + +{F217/96 [2.15c+]} +Function GetAccountStatus(objName:string; objType:word; + Var balance,limit,holds:LongInt):boolean; +{ equivalent to reading the ACCOUNT_BALANCE and ACCOUNT_HOLDS properties + of the object. The properties may not exist. } +{ This function will be successful if: + a) the caller is an accounting server on the current fileserver + OR b) the caller is supervisor-equivalent + OR c) the caller is querying his own account status } +Type Treq=record + len:word; + subF:byte; + _objType:word; {hi-lo} + _objName:string[48]; + end; + Trep=record + _balance: LongInt; {hi-lo} + _limit : Longint; {hi-lo} + reserved: array [1..120] of byte; + _holds : array [1..16] + of record + serverObjId:LongInt; {hi-lo} + HoldAmount :LongInt {hi-lo} + end; + end; + TPreq=^Treq; + TPrep=^Trep; +Var t:byte; +begin +With TPreq(GlobalReqBuf)^ + do begin + len:=sizeOf(Treq)-2; + subf:=$96; + _objType:=swap(objType); { force hi-lo} + PstrCopy(_objName,objName,48); UpString(_objName); + end; +F2SystemCall($17,sizeOf(Treq),sizeOf(Trep),result); +With TPrep(GlobalReplyBuf)^ + do begin + balance:=Lswap(_balance); { force lo-hi again } + limit:=Lswap(_limit); { force lo-hi again } + holds:=0; + for t:=1 to 16 + do if _holds[t].serverObjId<>0 + then holds:=holds+Lswap(_holds[t].holdAmount); { force lo-hi again } + end; +IF result=$C0 { no account privileges } + then GetBindryAccountStatus(objName,objType,balance,limit,holds); + { try to read status not as an accounting server, but as a supervisor } +GetAccountStatus:=(result=0); +{ resultcodes: 00 success; C0 No Account Privileges; C1 No Account Balance } +end; + + +{F217/97 [2.15c+]} +Function SubmitAccountCharge(objName:string; objType:word; + charge,cancelHoldAmount:Longint; + serviceType, commentType:word; comment:string):boolean; +{ -The cancelHold amount should be exactly the same as the amount that + was put on huld with the SubmitAccountHold call. If no + SubmitAccountHold call was made, the cancelHoldAmount should be set to zero. + -'negative charges' are allowed. They will increase the balance of + the object objName of objType. + -Use the objectType of caller for the serviceType parameter. + (audit log purposes) + -Set commentType to 0 and comment to '' if you aren't interested in the + audit log. + -To be called by accounting servers only. + -Can be imitated by supervisor-equivalent users by + calling GetAccountStatus and SetAccountStatus. Atomicity + of such a bindery transcation can not be guaranteed. + + } +Type Treq=record + len :word; + subf:byte; + _serviceType:word; {hi-lo} + _charge :Longint; {hi-lo} + _cancelHold :Longint; {hi-lo} + _objType :word; {hi-lo} + _commentType:word; {hi-lo} + _objNameAndComment:Array[1..305] of char; + end; + TPreq=^Treq; +Var p:byte; +begin +With TPreq(GlobalReqBuf)^ + do begin + subf:=$97; + _serviceType:= swap(serviceType); {force hi-lo} + _charge :=Lswap(charge); {force hi-lo} + _cancelHold :=Lswap(cancelHoldAmount); {force hi-lo} + _objType := swap(objType); {force hi-lo} + _commentType:= swap(commentType); {force hi-lo} + p:=ord(objName[0]);if p>48 then p:=48; + UpString(objName); + Move(objname[0],_objNameandComment[1],p+1); + Move(comment[0],_objNameandComment[p+2],ord(comment[0])+1); + len:=15+p+1+ord(comment[0])+1; + F2SystemCall($17,len+2,0,result); + end; +SubmitAccountCharge:=(result=$00); +{ resultcodes: 00 successful; C0 No Account Privileges; + C1 No Account Balance; C2 Credit Limit Exceeded. } +end; + + +{F217/98 [2.15c+]} +Function SubmitAccountHold(objName:string; objType:word; + reserveAmount:Longint ):boolean; +{ To be called by accounting servers only. } +Type Treq=record + len :word; + subf:byte; + _reserveAmount:Longint; {hi-lo} + _objType:word; {hi-lo} + _objName:string[48]; + end; + TPreq=^Treq; +Var p:byte; +begin +With TPreq(GlobalReqBuf)^ + do begin + subf:=$98; + _reserveAmount:=Lswap(ReserveAmount); { force hi-lo} + _objType:=swap(objType); { force hi-lo } + p:=ord(objName[0]); if p>48 then p:=48; + _objName:=objname;UpString(_objName);_objName[0]:=chr(p); + len:=7+p+1; + F2SystemCall($17,len+2,0,result); + end; +SubmitAccountHold:=(result=$00); +{ resultcodes: 00 successful; C0 No Account Privileges; + C1 No Account Balance; C2 Credit Limit Exceeded. + C3 Account Too Many Holds } +end; + +{F217/99 [2.15c+]} +Function SubmitAccountNote(objName:string; objType:word; + serviceType,commentType:word; comment:string):boolean; +{ To be called by accounting servers only.} +Type Treq=record + len:word; + subf:byte; + _serviceType:word; {hi-lo} + _objType:word; {hi-lo} + _commentType:word; {hi-lo} + _objNameAndComment:array[1..305] of char; + end; + TPreq=^Treq; +Var p:byte; +begin +with TPreq(GlobalReqBuf)^ + do begin + subf:=$99; + _serviceType:= swap(serviceType); {force hi-lo} + _objType := swap(objType); {force hi-lo} + _commentType:= swap(commentType); {force hi-lo} + p:=ord(objName[0]);if p>48 then p:=48; + UpString(objName); + Move(objname[0],_objNameandComment[1],p+1); + Move(comment[0],_objNameandComment[p+2],ord(comment[0])+1); + len:=7+p+1+ord(comment[0])+1; + F2SystemCall($17,len+2,0,result); + end; +SubmitAccountNote:=(result=0); +{resultcodes: 00 Successful; C0 No Account Privileges } +end; + +{---------------- Secondary Functions--------------------------------------} + + +Function AccountingInstalled:boolean; +Var propVal:Tproperty; + connId:byte; + moreSegments:boolean; + propFlags:byte; + currServerName:string; +begin +IF NOT GetEffectiveConnectionID(ConnId) + then result:=nwConn.result + else if NOT GetFileServerName(ConnId,currServerName) + then result:=nwConn.result + else begin + ReadPropertyValue(currServerName,OT_FILE_SERVER,'ACCOUNT_SERVERS',1, + propVal,moreSegments,propFlags); + result:=nwBindry.result; + end; +AccountingInstalled:=(result=0); +end; + + +Function SetAccountStatus(objName:string; objType:word; balance,limit:LongInt):boolean; +{ will change the account status to reflect the given parameters. + any holds will not be changed. + You need to be supervisor-eq. to do this...} +Var accPropVal:Tproperty; + accVal: record + _balance:LongInt; {hi-lo} + _limit:LongInt; {hi-lo} + _Reserved:array[1..120] of byte; { NW internal info } + end ABSOLUTE accPropVal; + OldBalance,OldLimit,OldHolds:LongInt; + moreSegments:boolean; + propFlags:byte; +begin +IF ReadPropertyValue(objName,objType,'ACCOUNT_BALANCE',1, + accPropVal,moreSegments,propFlags) + then begin + accVal._balance:=Lswap(balance); { force hi-lo} + accVal._limit:=Lswap(limit); { force hi-lo} + WritePropertyValue(objName,objType,'ACCOUNT_BALANCE', + 1,accPropVal,FALSE); + if (nwBindry.result=$F1) or (nwBindry.result=$F8) + then result:=$C0 + else result:=nwBindry.result; + end + else if nwBindry.result=$FB { no such property } + then result:=$C1 + else if nwBindry.result=$F1 { invalid bindery security } + then result:=$C0 + else result:=nwBindry.result; +SetAccountStatus:=(result=$00); +{ resultcodes: 00 success; C0 No Account Privileges; C1 No Account Balance; + 96 Server Out Of memory; FC No Such Object; FE Server Bindery Locked; + FF Bindery Failure} +end; + + +Function AddAccountingServer(objName:string;objType:word):boolean; +Var ConnId:byte; + currServerName:string; +begin +IF NOT GetEffectiveConnectionID(ConnId) + then result:=nwConn.result + else if NOT GetFileServerName(ConnId,currServerName) + then result:=nwConn.result + else begin + AddBinderyObjectToSet(currServerName,OT_FILE_SERVER,'ACCOUNT_SERVERS', + objName,objType); + result:=nwBindry.result; + end; +AddAccountingServer:=(result=0); +end; + +Function DeleteAccountingServer(objName:string;objType:word):boolean; +Var ConnId:byte; + currServerName:string; +begin +IF NOT GetEffectiveConnectionID(ConnId) + then result:=nwConn.result + else if NOT GetFileServerName(ConnId,currServerName) + then result:=nwConn.result + else begin + DeleteBinderyObjectFromSet(currServerName,OT_FILE_SERVER,'ACCOUNT_SERVERS', + objName,objType); + result:=nwBindry.result; + end; +DeleteAccountingServer:=(result=0); +end; + +{F217/96 } +Function DeleteAccountHolds(objName:string; objType:word):boolean; +{ delete all holds the caller (an accounting server) has on the + object with name objName of type objType. } +Type Treq=record + len:word; + subF:byte; + _objType:word; {hi-lo} + _objName:string[48]; + end; + Trep=record + _balance: LongInt; {hi-lo} + _limit : Longint; {hi-lo} + reserved: array [1..120] of byte; + _holds : array [1..16] + of record + serverObjId:LongInt; {hi-lo} + HoldAmount :LongInt {hi-lo} + end; + end; + TPreq=^Treq; + TPrep=^Trep; +Var t:byte; + holds:LongInt; + level:byte; + accServerId:LongInt; + accServerType:word; + accServerName:string; +begin +GetBinderyAccessLevel(Level,accServerID); +GetBinderyObjectName(accServerID,accServerName,accServerType); +With TPreq(GlobalReqBuf)^ + do begin + len:=sizeOf(Treq)-2; + subf:=$96; + _objType:=swap(objType); { force hi-lo} + PstrCopy(_objName,objName,48); UpString(_objName); + end; +F2SystemCall($17,sizeOf(Treq),sizeOf(Trep),result); +if result=0 + then With TPrep(GlobalReplyBuf)^ + do begin + holds:=0; + for t:=1 to 16 + do if accServerID=Lswap(_holds[t].serverObjId) + then holds:=holds+Lswap(_holds[t].holdAmount); { force lo-hi again } + if holds<>0 + then SubmitAccountCharge(objName,objType,0,holds, + accServerType,0,'clearing holds'); + end; +DeleteAccountHolds:=(result=0); +{ resultcodes: 00 success; C0 No Account Privileges; C1 No Account Balance } +end; + + +Function GetConnectTimeCharge(Var currentCharge:Real;Var chargeRec:TchargeRec):boolean; +Var propVal:Tproperty; + _chargeRec:TchargeRec ABSOLUTE propVal; + _currcharge:record + fill:LongInt; + currMult,currDiv:word; {hi-lo} + end ABSOLUTE propVal; + connId:byte; + moreSegments:boolean; + propFlags:byte; + currServerName:string; +begin +IF NOT GetEffectiveConnectionID(ConnId) + then result:=nwConn.result + else if NOT GetFileServerName(ConnId,currServerName) + then result:=nwConn.result + else if ReadPropertyValue(currServerName,OT_FILE_SERVER, + 'CONNECT_TIME',1, + propVal,moreSegments,propFlags) + then begin + IF _currCharge.currDiv=0 + then currentCharge:=0 + else currentCharge:=Swap(_currCharge.currMult)/Swap(_currCharge.currDiv); + move(propVal[9],propVal[5],124); + chargeRec:=_chargeRec; + result:=0; + end + else result:=nwBindry.result; +GetConnectTimeCharge:=(result=0); +end; + + + +end. \ No newline at end of file diff --git a/NWTP/NWBINDRY.PAS b/NWTP/NWBINDRY.PAS new file mode 100644 index 0000000..0865434 --- /dev/null +++ b/NWTP/NWBINDRY.PAS @@ -0,0 +1,1442 @@ +{$X+,B-,V-,S-} {essential compiler directives} + +UNIT nwBindry; + +{ nwBindry unit as of 950301 / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +INTERFACE + +USES nwIntr,nwMisc; + +{ Primary Functions: Interrupt: comments: + +* AddBinderyObjectToSet (F217/41) +* ChangeBinderyObjectPassword (F217/40) Unencrypted Passwords. +* ChangeEncrBinderyObjectPassword (F217/4B) Encrypted Passwords. +* ChangeBinderyObjectSecurity (F217/38) +* ChangePropertySecurity (F217/3B) +* CloseBindery (F217/44) +* CreateBinderyObject (F217/32) +* CreateProperty (F217/39) +* DeleteBinderyObject (F217/33) +* DeleteBinderyObjectFromSet (F217/42) +* DeleteProperty (F217/3A) +* GetBinderyAccessLevel (F217/46) +* GetBinderyObjectID (F217/35) +* GetBinderyObjectName (F217/36) +* GetEncryptionKey (F217/17) (1) +* GetRelationOfBinderyObject (F217/4C) +* IsBinderyObjectInSet (F217/43) +* IsStationAManager (F217/49) +* OpenBindery (F217/45) +* ReadPropertyValue (F217/3D) +* RenameBinderyObject (F217/34) +* ScanBinderyObject (F217/37) +* ScanProperty (F217/3C) +* VerifyBinderyObjectPassword (F217/3F) Unencrypted Passwords. +* VerifyEncrBinderyObjectPassword (F217/4A) Encrypted passwords +* WritePropertyValue (F217/3E) + + Secondary Functions: + +* IsShellLoaded +* IsUserLoggedOn +* ExistsUser +* ExistsFileServer +* GetRealUserName +* IsGroupMember +* AddUserToGroup +* DeleteUserFromGroup + +Not implemented: + +- ChangePassword (F217/01) (2) +- GetMemberSetMofGroupG (F217/09) (3) +- GetStationsRootMask (E3../06) (4) +- MapNumberToGroupName (F217/08) (5) +- MapNumberToObject (F217/04) (6) +- MapObjectToNumber (F217/03) (7) + +Notes: -Names of Objects & Properties (and Passwords) are converted to + uppercase by the above functions. + -Functions marked with a '*' are tested (with 3.1x) and found correct. + (See example programs in XBIND.ZIP, e.g. SCANBIND,TSTBIND,BACKBIN). + -(1): Called by other functions, e.g. ChangeEncrBinderObjectPassword, + VerifyEncrBinderyObjectPassword, LoginEncrToFileserver. + (2): This call has been replaced by F217/40 ChangeBinderyObjectPassword. + (3): replaced by F217/37 ScanBinderyObject and F217/3D ReadPropertyValue. + (4): -obsolete call- + (5,6): Replaced by F217/36 GetBinderyObjectName. + (7): Replaced by F217/35 GetBinderyObjectID. +} + +CONST + { known object types: (see the file OT_XXX for a full list)} + OT_WILD = Word(-1); + OT_UNKNOWN = 0; + OT_USER = 1; + OT_USER_GROUP = 2; + OT_PRINT_QUEUE = 3; + OT_FILE_SERVER = 4; + OT_JOB_SERVER = 5; + OT_GATEWAY = 6; + OT_PRINT_SERVER = 7; + OT_ARCHIVE_QUEUE = 8; + OT_ARCHIVE_SERVER = 9; + OT_JOB_QUEUE = $0A; + OT_ADMINISTRATION = $0B; + OT_ADVERTISING_PRINTSERVER = $47; + OT_NETWARE_ACCESS_SERVER = $98; + OT_NAMED_PIPES_SERVER = $9A; + OT_RSPCX_SERVER = $0107; { # Rconsole/FileServer, Sckt. 0451h, 8140h } + + { bindery security: } + BS_ANY_READ = $00; + BS_LOGGED_READ = $01; + BS_OBJECT_READ = $02; + BS_SUPER_READ = $03; + BS_BINDERY_READ = $04; + + BS_ANY_WRITE = $00; + BS_LOGGED_WRITE = $10; + BS_OBJECT_WRITE = $20; + BS_SUPER_WRITE = $30; + BS_BINDERY_WRITE = $40; + +{property & object objFlag/propFlags Constants:} + BF_ITEM = $00; + BF_SET = $02; + BF_DYN_PROP = $10; {1} + BF_STAT_PROP = $00; {1} + { or BF_ITEM/SET with BF_xx_PROP to obtain propFlags } + BF_STAT_OBJ = $00; {1} + BF_DYN_OBJ = $01; {1} + +{ Note 1: not available in the NW interface for C } + + +Type Tproperty=Array[1..128] of Byte; + + TobjIdArray=array[1..$20] of Longint; + +Var result:word; + +{F217/32 [2.15c+] } +Function CreateBinderyObject(objName:string; objType:Word; + objFlaG, objSecurity :Byte ):boolean; +{ Creates an object in the bindery. } + +{F217/33 [2.15c+] } +Function DeleteBinderyObject( objName:String; objType:Word ):boolean; +{ deletes a bindery object and all asociated properties. } + +{F217/34 [2.15c+]} +Function RenameBinderyObject( objName,NewObjName :string; objType :word ):boolean; +{ This function allows the (supervisor-equivalent) user to rename an object, + given its' type and old name. } + +{F217/35 [2.15c+] } +Function GetBinderyObjectID( objName:String; objType:word; + Var objID:Longint ):boolean; +{ returns the object ID of an object, given its type and name. } + +{F217/36 [2.15c+] } +Function GetBinderyObjectName( object_Id:LongInt; + Var objName:String; Var objType:word ):boolean; +{ returns the type and name of an object, given its four BYTE-id. } + +{F217/37 [2.15c+]} +Function ScanBinderyObject( SearchObjName: String; + SearchObjType: Word; + {i/o:} Var lastObjSeen : Longint; + {out:} Var RepName : String; + Var RepType : Word; + Var RepId : LongInt; + Var RepFlag : Byte; + Var RepSecurity : Byte; + Var RepHasProperties: Boolean + ) :boolean; +{ This function scans the bindery and returns complete information about + one or more bindery object(s). It can be called iteratively. } + +{F217/38 [2.15c+]} +Function ChangeBinderyObjectSecurity(objName :String; objType :Word; + NewObjSecurity :Byte ):boolean; +{ Changes the security of a Bindery object. } + +{F217/39 [2.15c+]} +Function CreateProperty( objName:String; objType:Word; + propertyName:String; propFlags,propSecurity:Byte ):boolean; +{ Creates a property to be associated with a bindery object. } + +{F217/3A [2.15c+]} +Function DeleteProperty( objName:String; objType:Word; + propertyName:String ):boolean; +{ Deletes a property from a bindery object. } + +{F217/3B [2.15c+] } +Function ChangePropertySecurity( objName:String; objType:Word; + propName:String; newPropSecurity:Byte ):boolean; +{ The call can't assign a greater access security level for the property + than the security level of the caller. } + +{F217/3C [2.15c+]} +Function ScanProperty( objName:String; objType:Word; searchPropName:String; + {i/o var:} Var SequenceNumber:LongInt; + { output:} Var propName:String; + Var propFlags:Byte; + Var propSecurity:Byte; + Var propHasValue:Boolean; + Var moreProperties:Boolean ):boolean; +{ return information about one or more properties. } + +{F217/3D [2.15c+]} +Function ReadPropertyValue( objName:String; objType:Word; + propName:String; segmentNumber:Word; + Var propValue : Tproperty; + Var moreSegments: Boolean; + Var propFlags : Byte ):boolean; +{ Returns the value of a property associated with a Bindery object. } + +{F217/3E [2.15c+]} +Function WritePropertyValue( objName:String; objType:Word; + propName:String; segmentNbr: Byte; propValue:Tproperty; + moreSegments:Boolean ):boolean; +{ Changes the value of a (NON-SET) property associated with a Bindery object. } + +{F217/3F [2.15c+]} +FUNCTION VerifyBinderyObjectPassword + ( objName:string; objType:Word; password:string):boolean; +{ Verifies the accuracy of a password for a bindery object. (UNencrypted version) } + +{F217/4A [2.15c+]} +FUNCTION VerifyEncrBinderyObjectPassword + ( objName:string; objType:Word; password:string):boolean; +{ Verifies the accuracy of a password for a bindery object. (ENcrypted version) } + +{F217/ [2.15c+] } +Function ChangeEncrBinderyObjectPassword(objName:String; objType:Word; + oldPassWord,newPassWord:String ):boolean; +{ Changes the password of a bindery object. (UNencrypted version) } + +{F217/40 [2.0/2.1/3.x] } +Function ChangeBinderyObjectPassword(objName:String; objType:Word; + oldPassWord,newPassWord:String ):boolean; +{ Changes the password of a bindery object. (UNencrypted version) } + +{F217/41 [2.15c+]} +Function AddBinderyObjectToSet(objName:String; objType:Word;propName, + memberName:String; memberType:Word ):boolean; +{ Adds a bindery object (member) to a property set. } + +{F217/42 [2.15c+]} +Function DeleteBinderyObjectFromSet(objName:String; objType:Word;propName, + memberName:String; memberType:Word ):boolean; +{ Deletes a (member) bindery object from a property set. } + +{F217/43 [2.15c+]} +Function IsBinderyObjectInSet(objName:String; objType:Word;propName, + memberName:String; memberType:Word ):boolean; +{ Allows the programmer to check whether a bindery object is a member of a + set-property. } + +{F217/44 [2.15c+]} +Function CloseBindery:boolean; +{ Closes the bindery files so they can be backed up. (Supervisor only) } + +{F217/45 [2.15c+]} +Function OpenBindery:boolean; +{ This call must be used after the CloseBindery call. No other bindery + call will work while the bindery is closed. } + +{F217/46 [2.15c+] } +Function getBinderyAccessLevel( {out:} Var SecurityAccesslevel:byte; + Var ObjId:Longint ): Boolean; +{ It returns the user's access level to the bindery. } + +{F217/17 [3.x]} +FUNCTION GetEncryptionKey(VAR key : TencryptionKey): Boolean; +{ Used by calls using encrypted passwords to query the target fileserver + for an encryption key. } + + +{F217/49 [2.15c+]} +Function IsStationAManager:boolean; +{ Is station a workgroup manager ? } + + +{F217/4C [2.15c+]} +Function GetRelationOfBinderyObject(ObjName:string;ObjType:word; + relationPropertyName:string; + {i/o} Var sequenceNbr:longint; + {out} Var NbrOfObjects:word; + Var Info:TobjIdArray ):boolean; + +{************************** secondary functions: ****************************} + +Function IsShellLoaded:boolean; +Function IsUserLoggedOn:boolean; +Function ExistsUser(userObjName:string):boolean; +Function GetRealUserName(userObjname:string; Var realname:string):boolean; +Function IsGroupMember(GroupName,UserObjName:String): Boolean; +Function AddUserToGroup(userName,GroupName:String):boolean; +Function DeleteUserFromGroup(userName,GroupName:String):boolean; +Function ExistsFileServer(ServerName:string):Boolean; + +IMPLEMENTATION{=============================================================} + + +{F217/17 [3.x]} +FUNCTION GetEncryptionKey(VAR key : TencryptionKey): Boolean; +Type Treq=RECORD + len : WORD; + func: BYTE; + END; + TPreq=^Treq; +BEGIN +With TPreq(GlobalReqBuf)^ + do begin + len := 1; + func := $17; + end; +F2SystemCall($17,sizeof(Treq),SizeOf(TencryptionKey),result); +Move(GlobalReplyBuf^,key,SizeOf(key)); +GetEncryptionKey:=(Result=0); +END; + + +{F217/3F [2.15c+]} +FUNCTION VerifyBinderyObjectPassword + ( objName:string;objType:Word; password : string):boolean; +{ Verifies the accuracy of a password for a bindery object. } +{ Passwords need to be converted to upper case, NULL if there is no password. } +Type TReq=record + buffer_length : Word; + subfunction : byte; + obj_type : word; { hi-lo } + _ObjectName : string[48]; + _PassWord : string[127]; + end; + TPreq=^Treq; +begin +With TPreq(GlobalReqBuf)^ +do begin + buffer_length := SizeOf(Treq)-2; + subfunction :=$3F; + obj_type:=swap(objType); { force hi-lo } + UpString(objName); + UpString(password); + PStrCopy(_ObjectName,objName,48); _ObjectName[48]:=#0; UpString(_ObjectName); + PStrCopy(_PassWord,password,127); Upstring(_PassWord); + end; +F2SystemCall($17,sizeof(Treq),0,result); +VerifyBinderyObjectPassword:=(result=0); +{ possible resultcodes: +$00 0 verification of object_name/password combination +$96 150 Sever out of memory +$C5 197 account disabled due to intrusion lockout +$D6 214 unencrypted password calls not allowed on this v3+ server +$F0 240 Wildcard not allowed +$FB 251 no such property +$FC 252 no such object_name on this server +$FE 254 Server Bindery Locked +$FF 255 Bindery failure (No such object or bad password) } +end; + + +{F217/4A [3.x]} +FUNCTION VerifyEncrBinderyObjectPassword(ObjName: String; ObjType: Word; PassWord: String): Boolean; + + FUNCTION VerifyEncrypted(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 := $4A; + _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); + VerifyEncrypted:=(result=0); + End; + +VAR + key : TencryptionKey; + ObjId:LongInt; + _pw:string; + +Begin +UpString(password); +_pw:=password; +if _pw[0]>#127 Then _pw[0]:=#127; + +IF GetEncryptionKey(key) + Then Begin + + IF GetBinderyObjectId(objName,objType,ObjId) + Then Begin + EncryptPassword(objId,_pw,key); + VerifyEncrypted(ObjName, ObjType, key); + End; + End + Else VerifyBinderyObjectPassword(ObjName, ObjType, Password); + +VerifyEncrBinderyObjectPassword := (result=0); +End; + + +{F217/37 [2.15c+]} +Function ScanBinderyObject( SearchObjName: String; + SearchObjType: Word; + {i/o:} Var lastObjSeen : Longint; + {out:} Var RepName : String; + Var RepType : Word; + Var RepId : LongInt; + Var RepFlag : Byte; + Var RepSecurity : Byte; + Var RepHasProperties: Boolean + ) :boolean; +{ This function scans the bindery and returns complete information about + a bindery object. } +Type TReq = record + length : word; + subfunction : byte; + last_obj_id : longint; {hi-lo} + search_obj_type : word; {hi-lo} + search_obj_name : string[48]; + end; + TRep= record + object_id : longint; {hi-lo} + object_type : word; {hi-lo} + object_name : array [1..48] of byte; + object_flag : byte; + security : byte; + properties : byte; + end; + TPreq=^Treq; + TPrep=^Trep; + +Var TempStr:string; + count : integer; +begin +with TPreq(GlobalReqBuf)^ +do begin + length := SizeOf(Treq)-2; + subfunction := $37; + last_obj_id := Lswap(lastObjseen); { force hi-lo } + search_obj_type:= swap(Word(SearchObjType)); { force hi-lo } + PstrCopy(Search_obj_name,SearchObjName,48); Search_obj_Name[48]:=#0; UpString(Search_obj_name); + end; +F2SystemCall($17,sizeOf(Treq),sizeOf(Trep),result); +With TPrep(GlobalReplyBuf)^ +do begin + repFlag := object_flag; + repHasProperties := (properties>0); + repSecurity := security; + repType := swap(object_type); { force lo-hi } + repId := Lswap(object_id); { force lo-hi } + lastObjSeen := repId; + ZStrCopy(repName,Object_Name,48); + end; +scanBinderyObject:=(result=0); +{ Possible Resultcodes: + 96h server out of memory; EFh Invalid Name; FCh No Such Object; + FEh Server Bindery Locked; FFh Bindery failure } +end; + + +{F217/3D [2.15c+]} +Function ReadPropertyValue( objName:String; objType:Word; + propName:String; segmentNumber:Word; + Var propValue : Tproperty; + Var moreSegments: Boolean; + Var propFlags : Byte ):boolean; +{ Returns the value of a property associated with a Bindery object. } +Type Treq=record + len : word; + subfunction : byte; + _objType : word; { hi-lo } + _ObjName : string[48]; + _segNbr : byte; + _propName : string[15]; + end; + Trep = record + _propValue : Tproperty; {array [1..128] of byte} + _moreSegments : byte; + _propFlags : byte; + end; + TPreq=^Treq; + TPrep=^Trep; +BEGIN + With TPreq(GlobalReqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subfunction := $3d; + _objType:=swap(objType); { force hi-lo } + _segNbr:=segmentNumber; + PStrCopy(_ObjName,objName,48); _ObjName[48]:=#0; UpString(_ObjName); + PStrCopy(_PropName,propName,15); UpString(_propName); + end; +F2SystemCall($17,sizeof(Treq),sizeof(Trep),result); +if result=0 + then with TPrep(GlobalreplyBuf)^ + do begin + propValue:=_propValue; + moreSegments:=(_moreSegments>0); + propFlags:=_propFlags; + end; +ReadPropertyValue:=(result=0); +{ 96 server out of memory; EC no such segment; F0 wilcard not allowed; + f1 invalid bindery security; f9 no property read privileges; + fb no such property; fc no such object; FE Server Bindery Locked; + FF Bindery Failure. } +end; + + +{F217/36 [2.15c+] } +Function GetBinderyObjectName( object_Id:LongInt; + Var objName:String; Var objType:word ):boolean; +{ returns the type and name of an object, given its four BYTE-id. } +Type TReq =record + len:word; + subF:byte; + _objId:LongInt; { hi-lo } + end; + Trep=record + _objId:LongInt; { hi-lo } + _objType:word; { hi-lo } + _objName:array[1..48] of Byte; + end; + TPreq=^Treq; + TPrep=^Trep; +BEGIN +WITH TPreq(GlobalReqBuf)^ +do begin + len :=SizeOf(TReq)-2; + SubF:=$36; + _objId:=Lswap(object_Id); { force hi-lo } + end; +F2SystemCall($17,sizeOf(Treq),sizeOf(Trep),result); +IF result=0 + then with TPrep(GlobalReplyBuf)^ + do begin + ZstrCopy(objName,_objName,48); + objType:=swap(_objType); { force lo-hi } + end; +GetBinderyObjectName:=(result=0); +end; + + +{F217/35 [2.15c+] } +Function GetBinderyObjectID( objName:String; objType:word; + Var objID:Longint ):boolean; +{ returns the object ID of an object, given its type and name. } +Type Treq=record + len:word; + subF:Byte; + _objType:word; { hi-lo } + _objName:string[48]; + end; + TRep=record + _objId:LongInt; { hi-lo } + _objType:word; { hi-lo } + _objName:array[1..48] of char; + end; + TPreq=^Treq; + TPrep=^Trep; +BEGIN +WITH TPreq(GlobalReqBuf)^ +do begin + len :=SizeOf(TReq)-2; + SubF:=$35; + _objType:=swap(objType); { force hi-lo } + PStrCopy(_objName,objName,48); _objName[48]:=#0; + UpString(_objName); + end; +F2SystemCall($17,sizeOf(Treq),sizeOf(Trep),result); +IF result=0 + then with TPrep(GlobalReplyBuf)^ + do objID:=Lswap(_objId); { force lo-hi } +GetBinderyObjectID:=(result=0); +end; + + +{F217/46 [2.15c+]} +Function getBinderyAccessLevel(Var SecurityAccessLevel:byte; + Var objId:Longint ):boolean; +{ It returns the user's access level to the bindery. } +{ Often used as a quick way of determining the current users' object id } +{ use the BS_xxxx constants to determine the exact rights of the user } +Type Treq=record + len :word; + subF :byte; + end; + Trep=record + accLeveL:byte; + _objId:longInt; + fill:array[1..20] of byte; + end; + TPreq=^Treq; + TPrep=^Trep; +BEGIN +With TPreq(GlobalReqBuf)^ + do begin + subF:=$46; + len:=sizeOf(Treq)-2; + end; +F2SystemCall($17,sizeOf(Treq),sizeOf(Trep),result); +If result=0 + then with TPrep(GlobalReplyBuf)^ + do begin + SecurityAccessLevel:=accLevel; + objId:=Lswap(_objId); + end; +GetBinderyAccessLevel:=(result=0); +end; + + + +{F217/45 [2.15c+]} +Function OpenBindery:boolean; +{ This call must be used after the CloseBindery call. No other bindery + call will work while the bindery is closed. } +Type Treq=record + len:word; + subFunc:byte; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + len:=1; + subFunc:=$45; + end; +F2SystemCall($17,sizeOf(Treq),0,result); +OpenBindery:=(result=0) +end; + + +{F217/44 [2.15c+]} +Function CloseBindery:boolean; +{ Closes the bindery files so they can be backed up. (Supervisor only) } +Type Treq=record + len:word; + subFunc:byte; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + len:=SizeOf(Treq)-2; + subFunc:=$44; + end; +F2SystemCall($17,sizeOf(Treq),0,result); +CloseBindery:=(result=0) +end; + + +{F217/32 [2.15c+] } +Function CreateBinderyObject(objName:string; objType:Word; + objFlaG, objSecurity :Byte ):boolean; +{ Creates an object in the bindery. + objName: name of the new object (47 chars) + objType: object type number (own type number or OT_xxx constant) + objFlag: identifies an object as static (0) or dynamic (1) + (dynamic objects are removed from the bindery when the server goes down) + objSecurity: high nibble: write privileges needed to modify this object + low nibble: read privileges needed to access this object + (default: $31 Supervisor write/Logged read) } +Type Treq=record + len :word; + subFunc :byte; + _objFlag :Byte; + _objSecurity :Byte; + _objType :word; { hi-lo } + _objName :string[48] + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + len:=SizeOf(Treq)-2; + subFunc:=$32; + _objFlag:=objFlag; + _objSecurity:=objSecurity; + _objType:=swap(objType); { force hi-lo } + PStrCopy(_objName,objName,48); _objName[48]:=#0; UpString(_objName); + end; +F2SystemCall($17,sizeof(Treq),0,result); +CreateBinderyObject:=(result=0) +{ 96h server out of memory; EEh Object Already Exists; EFh Invalid Name + F1h invalid Bindery security; F5h no object create privileges + FEh Server Bindery Locked; FFh Bindery Failure } +end; + + + +{F217/33 [2.15c+] } +Function DeleteBinderyObject( objName:String; objType:Word ):boolean; +{ deletes a bindery object and all asociated properties. } +Type Treq=record + len :word; + subFunc :byte; + _objType :Word; { hi-lo } + _objName :string[48]; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalreqBuf)^ +do begin + len:=SizeOf(Treq)-2; + subFunc:=$33; + _objType:=swap(objType); { force hi-lo } + PStrCopy(_objName,objName,48); _objName[48]:=#48; UpString(_objName); + end; +F2SystemCall($17,sizeOf(Treq),0,result); +DeleteBinderyObject:=(result=0) +{ 96h Server out of memory; EFh Invalid name; F0h wildcard not allowed; + F4h No object delete privileges; FCh no such object + FEh Server Bindery Locked; FFh bindery failure } +end; + + +{F217/34 [2.15c+]} +Function RenameBinderyObject( objName,NewObjName :string; objType :word ):boolean; +{ This function allows the (supervisor-equivalent) user to rename an object, + given its' type and old name. } +Type Treq=record + len :word; + subFunc :byte; + _objType :word; { hi-lo } + _objName :string[48]; + _NewObjName :string[48]; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + len:=SizeOf(Treq)-2; + subFunc:=$34; + _objType:=swap(objType); { force hi-lo } + PstrCopy(_objName,objName,48); _objName[48]:=#0; Upstring(_objName); + PstrCopy(_NewObjName,NewObjName,48); _NewObjName[48]:=#0; UpString(_NewObjName); + end; +F2SystemCall($17,sizeOf(Treq),0,result); +RenameBinderyObject:=(result=0) +{ 96h Server out of memory; EFh Invalid name; F0h wildcard not allowed; + F3h No object rename privileges; FCh no such object + FEh Server Bindery Locked; FFh bindery failure } +end; + + + +{F217/43 [2.15c+]} +Function IsBinderyObjectInSet(objName:String; objType:Word; + propName, memberName:String; memberType:Word ):boolean; +{ Allows the programmer to check whether a bindery object is a member of a + set-property. Objectname( of Objecttype) is the object to be searched for, + PropName (attached to the object with name memberName (of memberType)) + is the property containing the set to be searched. + User must have read rights to the object and the property. + Ex: ('SUPERVISOR',OT_USER,'GROUP_MEMBERS','EVERYONE',OT_USER_GROUP) } +Type Treq=record + len :word; + subFunc :byte; + _objType :Word; { hi-lo } + _objName :String[48]; { [48]=#0 } + _propName :String[15]; + _memObjType :Word; { hi-lo } + _memName :String[48]; { [48]=#0 } + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + len:=SizeOf(Treq)-2; + subFunc:=$43; + _objType:=swap(objType); { force hi-lo } + PstrCopy(_objName,objName,48); _objName[48]:=#0; UpString(_objName); + PstrCopy(_propName,propName,15); UpString(_propName); + _memObjType:=swap(memberType); { force hi-lo } + PStrCopy(_memName,memberName,48); _memName[48]:=#0; UpString(_memName); + end; +F2SystemCall($17,sizeOf(Treq),0,result); +IsBinderyObjectInSet:=(result=0) +{ 96h Server out of memory; EA No Such member; EB Not Group Property + F0h wildcard not allowed; F9 No Property read privileges; + FCh no such object; FEh Server Bindery Locked; FFh bindery failure } +end; + + + +{F217/41 [2.15c+]} +Function AddBinderyObjectToSet(objName:String; objType:Word; + propName, memberName:String; memberType:Word ):boolean; +{ Adds a bindery object to a property set. + user must have write access to the set property. } +Type Treq=record + len :word; + subFunc :byte; + _objType :Word; { hi-lo } + _objName :String[48]; { [48]=#0 } + _propName :String[15]; + _memObjType :Word; { hi-lo } + _memName :String[48]; { [48]=#0 } + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + len:=SizeOf(Treq)-2; + subFunc:=$41; + _objType:=swap(objType); { force hi-lo } + PstrCopy(_objName,objName,48); _objName[48]:=#0; UpString(_objName); + PstrCopy(_propName,propName,15); UpString(_propName); + _memObjType:=swap(memberType); { force hi-lo } + PStrCopy(_memName,memberName,48); _memName[48]:=#0; UpString(_memName); + end; +F2SystemCall($17,sizeOf(Treq),0,result); +AddBinderyObjectToSet:=(result=0) +{ 96h Server out of memory; E9 Member already Exists; EB Not Group Property + F0h wildcard not allowed; F8 No Property write privileges; + FCh no such object; FEh Server Bindery Locked; FFh bindery failure } +end; + + +{F217/42 [2.0/2.1/3.x]} +Function DeleteBinderyObjectFromSet(objName:String; objType:Word; + propName, memberName:String; memberType:Word ):boolean; +{ Deltes a bindery object from a property set. + user must have write access to the set property. } +Type Treq=record + len :word; + subFunc :byte; + _objType :Word; { hi-lo } + _objName :String[48]; { [48]=#0 } + _propName :String[15]; + _memObjType :Word; { hi-lo } + _memName :String[48]; { [48]=#0 } + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + len:=SizeOf(Treq)-2; + subFunc:=$42; + _objType:=swap(objType); { force hi-lo } + PstrCopy(_objName,objName,48); _objName[48]:=#0; UpString(_objName); + PstrCopy(_propName,propName,15); UpString(_propName); + _memObjType:=swap(memberType); { force hi-lo } + PStrCopy(_memName,memberName,48); _memName[48]:=#0; UpString(_memName); + end; +F2SystemCall($17,sizeOf(Treq),0,result); +DeleteBinderyObjectFromSet:=(result=0) +{ 96h Server out of memory; EA No Such Member; EB Not Group Property + F0h wildcard not allowed; F8 No Property write privileges; FB No Such property; + FCh no such object; FEh Server Bindery Locked; FFh bindery failure } +end; + + +{F217/38 [2.15c+]} +Function ChangeBinderyObjectSecurity(objName :String; objType :Word; + NewObjSecurity :Byte ):boolean; +{ Changes the security of a Bindery object. This call is made successfully, + if the user is supervisor equivalent and the current security is unequal to + NetWare Read/ NetWare Write. } +Type Treq=record + len :word; + subFunc :byte; + _NobjSec :Byte; + _objType :Word; { hi-lo } + _objName :String[48]; { [48]=#0 } + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + len:=SizeOf(Treq)-2; + subFunc:=$38; + _NobjSec:=NewObjSecurity; + _objType:=swap(objType); { force hi-lo } + PstrCopy(_objName,objName,48); _objName[48]:=#0; UpString(_objName); + end; +F2SystemCall($17,sizeOf(Treq),0,result); +ChangeBinderyObjectSecurity:=(result=0) +{ Completion Codes: + 96 Server out of memory; F0 Wildcard Not Allowed; F1 Invalid Bindery Security; + FC No Such Object; FE Server Bindery Locked; FF Bindery Failure. } +end; + + + + +{F217/4B [3.x]} +FUNCTION ChangeEncrBinderyObjectPassword(ObjName: String; ObjType: Word; +{#d} oldPassWord,newPassword: String): Boolean; +{ Changes the password of a bindery object. + Old Password can be NULL. To log into a file server, an object must have a + PASSWORD property. User must have read and write access to the bindery object. } + FUNCTION ChangeEncrypted(ObjName : String; ObjType : Word; + oldEncrPW:TencryptionKey; + Var newPWdif:TencrPWdifference; + PWdifCheckSum:byte + ):boolean; + Type Treq=RECORD + BufLen : Word; + _func : Byte; + _oldPW : TencryptionKey; + _ObjType: Word; + _ObjNameLen : byte; + _Various: array [1..48+1+16] of byte; { ObjName, difCheksum, PWdif } + End; + TPreq=^Treq; + Begin + With TPreq(GlobalreqBuf)^ + do Begin + _func := $4B; + _oldPW:=oldEncrPW; + _ObjType := Swap(objType); + if objName[0]>#48 + then objName[0]:=#48; + move (objName[0],_objNameLen,ord(objName[0])+1); + _Various[_objNamelen+1]:=PWdifCheckSum; + move(newPWdif,_Various[_objNamelen+2],16); + BufLen:=29+_objNameLen; + F2SystemCall($17,buflen+2,0,result); + end; + ChangeEncrypted:=(result=0); + End; + +VAR + key : TencryptionKey; + ObjId:LongInt; + PWdif:TencrPWdifference; + PWdifChecksum:byte; + +Begin +UpString(oldPassword); +if oldPassword[0]>#127 + Then oldPassword[0]:=#127; +UpString(newPassword); +if newPassword[0]>#127 + Then newPassword[0]:=#127; +UpString(ObjName); + +IF GetEncryptionKey(key) + Then Begin + IF GetBinderyObjectId(objName,objType,ObjId) + Then Begin + EncryptPasswordDifference(objId, + OldPassword,NewPassword, + key, { i/o, out: EncrOldPW } + PWdif, { out, 16 bytes } + PWdifChecksum { out, 1 byte } + ); + ChangeEncrypted(ObjName, ObjType, key, PWdif, PWdifChecksum); + End; + End + Else ChangeBinderyObjectPassword(ObjName, ObjType, OldPassword, NewPassword); + +ChangeEncrBinderyObjectPassword:= (result=0); +{ Completion Codes: + 96 Server Out Of Memory; F0 Wildcard Not Allowed; FB No Such Property; + FC No Such Object; FE Server Bindery Locked; FF No Such Object *OR* + No Password Associated With Object *OR* Old Password Invalid. } +End; + + +{F217/40 [2.0/2.1/3.x] } +Function ChangeBinderyObjectPassword(objName:String; objType:Word; + oldPassWord,newPassWord:String ):boolean; +{ Changes the password of a bindery object. + Allow unencrypted passwords must be ON! + Old Password can be NULL. To log into a file server, an object must have a + PASSWORD property. User must have read and write access to the bindery object. } +Type Treq=record + len :word; + subFunc :byte; + _objType :Word; { hi-lo } + _objName :String[48]; { [48]=#0 } + _oldPW :String[128]; { wow! a password of 128 chars! } + _newPW :String[128]; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + len:=SizeOf(Treq)-2; + subFunc:=$40; + _objType:=swap(objType); { force hi-lo } + PStrCopy(_objName,objName,48); _ObjName[48]:=#0; UpString(_objName); + PStrCopy(_oldPW,oldPassWord,128); UpString(_oldPW); + PStrCopy(_newPW,newPassWord,128); UpString(_newPW); + end; +F2SystemCall($17,sizeOf(Treq),0,result); +ChangeBinderyObjectPassword:=(result=0) +{ Completion Codes: + 96 Server Out Of Memory; F0 Wildcard Not Allowed; FB No Such Property; + FC No Such Object; FE Server Bindery Locked; FF No Such Object *OR* + No Password Associated With Object *OR* Old Password Invalid. } +end; + + + + +{F217/39 [2.15c+]} +Function CreateProperty( objName:String; objType:Word; + propertyName:String; propFlags,propSecurity:Byte ):boolean; +{ Creates a property to be associated with a bindery object. + property flags tell whether a property is dynamic or static and whether + the property is defined as static or dynamic. } +Type Treq=record + len :word; + subFunc :byte; + _objType :Word; { hi-lo } + _objName :String[48]; { [48]=#0 } + _propFlags:Byte; + _propSec :Byte; + _propName :String[15]; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + len:=SizeOf(Treq)-2; + subFunc:=$39; + _objType:=swap(objType); { force hi-lo } + PStrCopy(_objName,objName,48); _objName[48]:=#0; UpString(_objName); + _propFlags:=propFlags; + _propSec:=propSecurity; + PStrCopy(_propName,propertyName,15); UpString(_propName); + end; +F2SystemCall($17,sizeof(Treq),0,result); +CreateProperty:=(result=0) +{ Completion Codes: + 96 Server Out Of Memory; ED Property already exists; EF Invalid Name; + F0 Wildcard Not Allowed; F1 Invalid Bindery Security; F7 No Property Create Privileges; + FC No Such Object; FE Server Bindery Locked; FF Bindery Failure } +end; + + +{F217/3A [2.15c+]} +Function DeleteProperty( objName:String; objType:Word; + propertyName:String ):boolean; +{ Deletes a property from a bindery object. + The property field may contain wildcards. } +Type Treq=record + len :word; + subFunc :byte; + _objType :Word; { hi-lo } + _objName :String[48]; { [48]=#0 } + _propName :String[15]; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + len:=SizeOf(Treq)-2; + subFunc:=$3A; + _objType:=swap(objType); { force hi-lo } + PStrCopy(_objName,objName,48); _objName[48]:=#0; UpString(_objName); + PStrCopy(_propName,propertyName,15); UpString(_propName); + end; +F2SystemCall($17,sizeOf(Treq),0,result); +DeleteProperty:=(result=0) +{ Completion Codes: + 96 Server Out Of Memory; F0 Wildcard Not Allowed; F1 Invalid Bindery Security; + F6 No property delete privileges; FB No Such property; + FC No Such Object; FE Server Bindery Locked; FF Bindery Failure } +end; + + +{F217/3C [2.15c+]} +Function ScanProperty( objName:String; objType:Word; searchPropName:String; + {i/o var:} Var SequenceNumber:LongInt; + { output:} Var propName:String; + Var propFlags:Byte; + Var propSecurity:Byte; + Var propHasValue:Boolean; + Var moreProperties:Boolean ):boolean; +{ Sequence number should be -1 the first time this call is made. + The call can be reiterated (by supplying the returned Seq.#) until + moreProperties=FALSE or nwBindry.Result=NO_SUCH_PROPERTY. + searchPropName may contain wildcards; + If propHasValue=TRUE, the value can be read by calling ReadPropertyValue; + moreProperties=TRUE if more properties exist for this object. } +Type Treq=record + len :word; + subFunc :byte; + _objType :Word; {hi-lo} + _objName :String[48]; + _SeqNbr :LongInt; {hi-lo} + _propName :String[15]; + end; + Trep=record + _propName :array[1..16] of Byte; + _propFlags:Byte; + _propSec :Byte; + _SeqNbr :Longint; {hi-lo} + _propHasValue:Byte; + _moreProp :Byte; + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + len:=SizeOf(Treq)-2; + subFunc:=$3C; + _objType:=swap(objType); { force hi-lo } + PStrCopy(_objName,objName,48); _objName[48]:=#0; UpString(_objName); + _SeqNbr:=Lswap(SequenceNumber); { force hi-lo } + PstrCopy(_propName,searchPropName,15); UpString(_propName); + end; +F2SystemCall($17,sizeof(Treq),sizeof(Trep),result); +With TPrep(GlobalReplyBuf)^ +do begin + SequenceNumber:=Lswap(_SeqNbr); { force lo-hi } + ZStrCopy(propName,_propName,15); + propFlags:=_propFlags; + propSecurity:=_propSec; + propHasValue:=(_propHasValue>0); + moreProperties:=(_moreProp>0); + end; +ScanProperty:=(result=0) +{ Completion Codes: + 96 Server Out Of Memory; F1 Invalid Bindery Security; FB No Such property; + FC No Such Object; FE Server Bindery Locked; FF Bindery Failure } +end; + + +{F217/3E [2.15c+]} +Function WritePropertyValue( objName:String; objType:Word; + propName:String; segmentNbr: Byte; propValue:Tproperty; + moreSegments:Boolean ):boolean; +{ Changes the value of a (NON-SET) property associated with a Bindery object.} +Type Treq=record + len :word; + subFunc :byte; + _objType :Word; { hi-lo } + _objName :String[48]; + _segNbr :Byte; + _EraseRemainingSeg:Byte; { FF=true 00=false } + _propName :String[15]; + _propValSeg :Tproperty; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + len:=SizeOf(Treq)-2; + subFunc:=$3E; + _objType:=swap(objType); { force hi-lo } + PStrCopy(_objName,objName,48); _objName[48]:=#0; UpString(_objName); + _segNbr:=segmentNbr; + if moreSegments + then _EraseRemainingSeg:=$00 + else _EraseRemainingSeg:=$FF; + PstrCopy(_propName,propName,15); UpString(_propName); + _propValSeg:=propValue; + end; +F2SystemCall($17,sizeOf(Treq),0,result); +WritePropertyValue:=(result=0) +{ Completion Codes: + 96 Server Out Of Memory; E8 Not Item Property; EC no such segment; + F0 Wildcard Not Allowed; F1 Invalid Bindery Security; + F8 No property write privileges; FB No Such property; + FC No Such Object; FE Server Bindery Locked; FF Bindery Failure } +end; + +{F217/3B [2.15c+] } +Function ChangePropertySecurity( objName:String; objType:Word; + propName:String; newPropSecurity:Byte ):boolean; +{ The user must have read and write access to the property to make this call. + The call can't assign a greater security level than the security level of + the caller. } +Type Treq=record + len:word; + subFunc:byte; + _objType:Word; { hi-lo } + _objName:String[48]; + _NewPropSec:Byte; + _PropName:String[15]; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + len:=SizeOf(Treq)-2; + subFunc:=$3B; + _objType:=swap(objType); { force hi-lo } + PstrCopy(_objName,objName,48); _objName[48]:=#0; Upstring(_objName); + _NewPropSec:=NewPropSecurity; + PstrCopy(_propName,propName,15); Upstring(_propName); + end; +F2SystemCall($17,sizeOf(Treq),0,result); +ChangePropertySecurity:=(result=0) +{ Completion Codes: + 96 Server Out Of Memory; F0 Wildcard Not Allowed; F1 Invalid Bindery Security; + FB No Such property; FC No Such Object; FE Server Bindery Locked; + FF Bindery Failure } +end; + +{F217/49 [3.0+]} +Function IsStationAManager:boolean; +{ Fast way to detremine if: object ID of caller included in the MANAGERS + set property attached to the SUPERVISOR object. } +Type Treq=record + len:word; + subFunc:byte; + end; + Trep=record + unknown:byte; + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subFunc:=$49; + end; +F2SystemCall($17,SizeOf(Treq),Sizeof(Trep),result); +{With TPrep(GlobalReplyBuf)^ + do begin + + end; } +IsStationAManager:=(result=0) +{ Completion codes: + 00 Successful (WS is a manager); + FF Not a manager } +end; + +{F217/4C [3.0+]} +Function GetRelationOfBinderyObject(ObjName:string;ObjType:word; + relationPropertyName:string; + {i/o} Var sequenceNbr:longint; + {out} Var NbrOfObjects:word; + Var Info:TobjIdArray ):boolean; +{ OBJ_SUPERVISORS GROUPS_I'M_IN SECURITY_EQUALS } +Type Treq=record + len :word; + subFunc :byte; + _SeqObjId :Longint; {hi-lo} + _ObjType :word; {hi-lo} + _ObjAndPropName:string; + end; + Trep=record + _NbrOfObj:Word; + _Info :TobjIdArray; + end; + TPreq=^Treq; + TPrep=^Trep; +Var t:byte; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$4C; + _SeqObjId:=Lswap(SequenceNbr); + _ObjType:=swap(ObjType); + Upstring(ObjName);UpString(RelationPropertyName); + _ObjAndPropName:=ObjName; + move(RelationPropertyName[0], + _ObjAndPropName[ord(ObjName[0])+1], + ord(RelationPropertyName[0])+1); + len:=9+ord(ObjName[0])+ord(RelationPropertyName[0]); + F2SystemCall($17,len+2,Sizeof(Trep),result); + end; +With TPrep(GlobalReplyBuf)^ + do begin + NbrOfObjects:=swap(_NbrOfObj); + for t:= 1 to NbrOfObjects + do Info[t]:=Lswap(_Info[t]); + if NbrOfObjects=$20 + then SequenceNbr:=Info[$20] + else SequenceNbr:=-1; + end; +if result<>0 then SequenceNbr:=-1; +GetRelationOfBinderyObject:=(result=0) +end; + + + +{=======SECONDARY FUNCTIONS===================================================} + + + +Function IsShellLoaded:boolean; +Var mask:byte; + id:LongInt; +begin +{$IFNDEF MSDOS} +FillChar(nwintr.GlobalReplyBuf^,Sizeof(nwintr.TintrBuffer),#$0); +{ Only needed in protected mode, otherwise an invalid value is reported. + Doesn't harm a bit if you use it in other modes, though. } +{$ENDIF} +IsShellLoaded:=(nwBindry.getBinderyAccessLevel(mask,id) and (id<>0)); +end; + + +Function IsUserLoggedOn:boolean; +Var mask:byte; + id:LongInt; + objName:String; + objType:word; +begin +IsUserLoggedOn:=( nwBindry.getBinderyAccessLevel(mask,id) + and (id<>0) + and nwBindry.GetBinderyObjectName(id,objName,objType) + ) +end; + + +Function GetRealUserName(userObjName:string; Var realname:string):boolean; +Var propValue:Tproperty; + moreSeg:Boolean; + w,propFlag:Byte; +begin +If ReadPropertyValue(userObjName,OT_USER,'IDENTIFICATION',1,propValue,moreSeg,propFlag) + then ZstrCopy(RealName,PropValue,128) + else realname:=''; +GetRealUserName:=(result=0); +end; + +Function GetUserObjectID:LongInt; +Var mask:byte; + id:LongInt; +begin +if getBinderyAccessLevel(mask,id) + then GetUserObjectID:=id + else getUserObjectID:=-1; +{ -1 : look at nwBindry.result for error number } +end; + + +Function ExistsUser(userObjName:string):boolean; +Var ObjId:Longint; +begin +ExistsUser:=GetBinderyObjectId(userObjName,OT_USER,ObjId); +end; + +Function IsGroupMember( GroupName,UserObjName:String): Boolean; +begin +IsGroupMember:=IsBinderyObjectInSet(GroupName,OT_USER_GROUP,'GROUP_MEMBERS', + UserObjName,OT_USER); +end; + + + + +Function AddUserToGroup(userName,GroupName:String):boolean; +begin +{ first create the necessary properties. They may already exist. } + +CreateProperty(userName,OT_USER,'GROUPS_I''M_IN', + BF_SET,BS_SUPER_WRITE OR BS_LOGGED_READ); +IF (result<>$00) and (result<>$ED) { property already exists } +then begin AddUserToGroup:=false;exit end; { bindery failure / bad username} + +CreateProperty(userName,OT_USER,'SECURITY_EQUALS', + BF_SET,BS_SUPER_WRITE OR BS_LOGGED_READ); +IF (result<>$00) and (result<>$ED) { property already exists } +then begin AddUserToGroup:=false;exit end; + +{ The following construction seems a bit overdone, but it is needed to keep + the bindery consistent. A user is either fully added to a group OR + nothing happens, this way we ensure that a user is not 'patially added' + to a group. + If the user already is a member of the group, no error is returned. } +IF AddBinderyObjectToSet(Groupname,OT_USER_GROUP,'GROUP_MEMBERS', + userName,OT_USER) + then begin + IF AddBinderyObjectToSet(userName,OT_USER,'GROUPS_I''M_IN', + GroupName,OT_USER_GROUP) + then begin + IF AddBinderyObjectToSet(userName,OT_USER,'SECURITY_EQUALS', + GroupName,OT_USER_GROUP) + then begin + AddUserToGroup:=true; + exit; + end + else begin { attempt to delete partially setup member } + DeleteBinderyObjectFromSet(Groupname,OT_USER_GROUP,'GROUP_MEMBERS', + userName,OT_USER); + DeleteBinderyObjectFromSet(userName,OT_USER,'GROUPS_I''M_IN', + GroupName,OT_USER_GROUP) + end + end + else begin + DeleteBinderyObjectFromSet(Groupname,OT_USER_GROUP,'GROUP_MEMBERS', + userName,OT_USER); + end; + end; +if result=$E9 then result:=$00; { $E9: user already a member of group } +AddUserToGroup:=(result=0); +{ As all these called functions are in this unit, you can check nwBindry.result + for the errorcode. } +{ resultcodes: $FC user OR group object doesn't exist. } +end; + + + + +Function DeleteUserFromGroup(userName,GroupName:String):boolean; +begin +{ The following construction seems a bit overdone, but it is needed to keep + the bindery consistent. A user is either totally deleted from a group OR + nothing happens, this way we ensure that a user is not 'patially deleted' + from a group. + If the user was not a member of the group, no error is returned. } +IF DeleteBinderyObjectFromSet(Groupname,OT_USER_GROUP,'GROUP_MEMBERS', + userName,OT_USER) + then begin + IF DeleteBinderyObjectFromSet(userName,OT_USER,'GROUPS_I''M_IN', + GroupName,OT_USER_GROUP) + then begin + IF DeleteBinderyObjectFromSet(userName,OT_USER,'SECURITY_EQUALS', + GroupName,OT_USER_GROUP) + then begin + DeleteUserFromGroup:=True; + exit; + end + else begin { attempt to repair partially deleted member } + AddBinderyObjectToSet(Groupname,OT_USER_GROUP,'GROUP_MEMBERS', + userName,OT_USER); + AddBinderyObjectToSet(userName,OT_USER,'GROUPS_I''M_IN', + GroupName,OT_USER_GROUP) + end + end + else AddBinderyObjectToSet(Groupname,OT_USER_GROUP,'GROUP_MEMBERS', + userName,OT_USER); + end; +if result=$EA then result:=0; { $EA: user obj NOT a member of group } +DeleteUserFromGroup:=false; +{ As all these called functions are in this unit, you can check nwBindry.result + for the errorcode. } +{ Resultcodes: $FC user OR group object doesn't exist. } +end; + +Function ExistsFileServer(ServerName:string):Boolean; +{ You must be attached to at least one server before using this function. } +Var ObjId:Longint; +begin +UpString(ServerName); +ExistsFileServer:=GetBinderyObjectId(ServerName,OT_FILE_SERVER,ObjId); +end; + + + +end. { end of unit nwBindry } + diff --git a/NWTP/NWCONN.PAS b/NWTP/NWCONN.PAS new file mode 100644 index 0000000..41b9129 --- /dev/null +++ b/NWTP/NWCONN.PAS @@ -0,0 +1,1455 @@ +{$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 } + diff --git a/NWTP/NWFILE.PAS b/NWTP/NWFILE.PAS new file mode 100644 index 0000000..3f9bb4e --- /dev/null +++ b/NWTP/NWFILE.PAS @@ -0,0 +1,2999 @@ +{$X+,B-,V-} {essential compiler directives} + +Unit nwFile; + +{ nwFile unit as of 950301 / NwTP 0.6 API. (c) 1993,1995, R. Spronk } + +INTERFACE +{ Primary Functions Interrupt: comments: + +Volume Management (Volume Tables) +--------------------------------- + +* ClearObjectVolRestriction (F216/22) (3) [aka ClearVolumeRestrictions/RemoveObjectDiskRestrictions] +* GetObjectVolRestriction (F216/29) (3) [aka GetObjDiskRestrictions/GetObjectDiskUsageAndRestrictions] +* GetVolumeName (F216/06) +* GetVolumeNameWithHandle (F216/15) [aka GetVolumeInfoWithHandle] +* GetVolumeNumber (F216/05) +* GetVolumeUsage (F216/2C) (3) [aka GetExtendedVolumeInformation] +* IsVolumeRemovable (F212) [aka GetVolumeInfoWithNumber] +* ScanVolForRestrictions (F216/20) (3) +* SetObjectVolRestriction (F216/21) (3) [aka SetVolumeRestrictions/SetObjectVolSpaceLimit + /AddUserDiskspaceRestriction] + +Directory Handles (Directory Handle Table/Drive tables) +------------------------------------------------------- + +* AllocPermanentDirHandle (F216/12) +* AllocTemporaryDirHandle (F216/13) +* DeallocateDirHandle (F216/14) +* DeleteFakeRootDirectory (E906) +* GetDirectoryHandle (E900) +* GetDriveConnectionId (EF02) +* GetDirectoryPath (F216/01) +* GetDriveFlag (EF01) (6) +* GetDriveHandle (EF00) (6) +* GetRelativeDriveDepth (E907) +* GetSearchDriveVector (E901) +* MapFakeRootDirectory (E905) +* SetDirectoryHandle (F216/00) +* SetDriveConnectionId (EF02) +* SetDriveFlag (EF01) +* SetDriveHandle (EF00) +* SetSearchDriveVector (E902) + + Secondary Functions + +* DeleteConnectionsDriveMappings +* DeleteDriveMapping +* GetEnvPath (BA..) +* IsSearchDrive (BA..) +* IsNetworkDrive (4409) +* MapDrive +* MapPermanentDrive +* MapSearchDrive +* SetEnvPath (BA..) + +Entries (directory/file management) +----------------------------------- + +* ChangeDirectory (3B..) (DOS) +* ConvertPathToDirEntryId (F217/F4) +* CreateDirectory (F216/0A) +* DeleteDirectory (F216/0B) +* EraseFiles (F244) +. FileServerFileCopy (F3..) + GetDirectoryInfo (F216/2D) (3) +* GetDirectoryEntry (F216/1F) (3) +. GetExtendedFileAttributes (B600) =F24E ??? +. GetFileAttributes (4300) (DOS) +* GetTrueEntryName (60..) (DOS) +* MapDirentryIdToPath (F217/F3) + MoveEntry (F216/2E) (3) dir and files +* PurgeSalvagableFile (F216/1D) (3) +* RecoverSalvagebleFile (F216/1C) (3) +* RenameDirectory (F216/0F) +* ScanDirectoryInformation (F216/02) +* ScanDirectoryEntry (F216/1E) (3) +* ScanFileInformation (F217/0F) + ScanFilePhysical (F216/28) (3) +* ScanSalvagableFiles (F216/1B) (3) +* SetEntry (F216/25) (3) dir and files +. SetExtendedFileAttributes (B601) =F24F +. SetFileAttributes (F246) [4301] +* SetFileInformation (F217/10) + +* ScanDirRestrictions (F216/23) (3) +* SetDirRestriction (F216/24) (3) + + Secondary functions: + + DeleteFile + GetFileHandle + IsFileShareable + FlagFileShareable + PurgeFiles (by dirHandle,fileMask) + SalvageFiles (by dirHandle,fileMask) + PurgeAllErasedFiles + + +Trustees/Max. Rights Mask +------------------------- + +* DeleteTrustee (F216/2B) (3) +* GetEffectiveRights (F216/2A) (3) +. ModifyMaximumRightsMask (F216/04) +. ScanBinderyObjectTrusteePaths (F217/47) +* ScanEntryForTrustees (F216/26) (3) +* SetTrustee (F216/27) (3) + + +Not Implemented: +---------------- + +- AddTrusteeToDirectory (F216/0D) (10) +- AllocSpecialDirHandle (F216/16) (2) +- DeleteTrusteeFromDirectory (F216/0E) (10) +- FileServerFileCopy (E6..) (8) +- GetEffectiveDirectoryRights (F216/03) (10) +- GetPathFromDirEntryID (F216/1A) (12) +- GetVolumeInformation (F217/E9) (1) +- GetVolumeInfoWithHandle (F216/15) (5) +- GetVolumeInfoWithNumber (F212) (4) [DA..] +- PurgeErasedFiles (F216/10) (8) +- PurgeAllErasedFiles (F217/CE) (8) +- RestoreDirectoryHandle (F216/18) (2) +- RestoreErasedFile (F216/11) (8) +- SaveDirectoryHandle (F216/17) (2) +- ScanDirectoryForTrustees (F216/0C) (9) +- SetDirectoryInformation (F216/19) (11) +- SetFileAttributes (E4..) (7) +- UpdateFileSize (E5..) (7) + + +Notes: (1) GetVolumeInformation. This call is NOT available in all 3.x versions. + (only with Nw 2.1 & 3.1x and CLIB.NLM dated before 11-11-92 ) + This call is not implemented here. Replaced by GetVolumeUsage. + (2) not available in (all versions of) NW 3.x. + (3) NW 3.x (and upwards) only. + (4) Replaced by GetVolumeUsage and IsVolumeRemovable. + (5) Replaced by GetVolumeUsage and GetVolumeNameWithHandle. + (6) Information can also be obtained by calling GetDirectoryHandle. + (DOS) 'Normal' DOS call, extended by NetWare shell. + (7) Not supported by Adv.NW 3.x. Not implemented here. + These are functions using FCB's. If another function with the same + name is listed here, that function performs the same action. + (8) Not supported by Adv.NW 3.x. Not implemented here. + These functions have been replaced with calls marked (3) + (9) Replaced by a newer version: ScanEntryForTrustees. + (10) Replaced by DeleteTrustee, GetEffectiveRights and SetTrustee. + (11) Replaced by SetEntry + (12) Replaced by MapDirEntryIDtoPath + + } + +Uses nwIntr,nwMisc,nwBindry,nwConn; + +Var Result:Word; + +Type TsearchDriveVector=array [1..17] of byte; + + +CONST + DRIVE_UNUSED = $00; + DRIVE_PERMANENT = $01; { Drive permanently assigned to fileserver directory } + DRIVE_TEMPORARY = $02; { Drive temporary assigned to FS dir. Released by EOJ } + DRIVE_NETWORK = $03; { Normal drive mapping } + DRIVE_LOCAL = $80; { Drive is local. ! By ORing with one of the above bits, + it can be reassigned to a FS directory.} + + {Name Space Type constants} + NS_DOS =0; + NS_MAC =1; + NS_NFS =2; + NS_FTAM =3; + NS_HPFS =4; + + { Attributes / Netware directory & file attributes } + A_NORMAL = $00; {file} + A_READ_ONLY = $01; {file} + A_HIDDEN = $02; {file/dir} + A_SYSTEM = $04; {file/dir} + A_EXECUTE_ONLY = $08; {file} + A_DIRECTORY = $10; {file} + A_NEEDS_ARCHIVED = $20; {file} + A_undocumented = $40; + A_SHAREABLE = $80; {file} + + A_LO_SEARCH = $0100; {file} + A_MID_SEARCH = $0200; {file} + A_HI_SEARCH = $0400; {file} + A_RESERVED = $0800; {file/dir} + A_TRANSACTIONAL = $1000; {file} + A_INDEXED = $2000; {file} + A_READ_AUDIT = $4000; {file} + A_WRITE_AUDIT = $8000; {file} + + A_PURGE = $010000; {file/dir} + A_RENAME_INHIBIT = $020000; {file/dir} + A_DELETE_INHIBIT = $040000; {file/dir} + A_COPY_INHIBIT = $080000; {file} + + { Trustee Attributes / directory access rights } + TA_NONE = $00; + TA_READ = $01; {R open/read} + TA_WRITE = $02; {W open/write} + TA_RESERVED = $04; { reserved, set to 0 } + TA_CREATE = $08; {C create files or dirs} + TA_DELETE = $10; {E delete files/dirs} + TA_ACCESS = $20; {A set /delete trustees} + TA_SEARCH = $40; {F directory can be searched/file is visible} + TA_MODIFY = $80; {M modify dir/file attributes} + TA_SUPERVISOR =$100; {S supervisor rights to file or directory } + + { Entry Modify flags / see SetEntry } + + EM_ENTRYNAME = $00000001; + EM_ATTRIBUTES = $00000002; + EM_CREATIONTIME = $0000000C; { date = $04, time = $08 } + EM_OWNERID = $00000010; + EM_ARCHIVETIME = $00000060; { date = $20, time = $40 } + EM_ARCHIVERID = $00000080; + EM_MODIFYTIME = $00000300; { date = $0100, time =$0200 } + EM_MODIFIERID = $00000400; + EM_LASTACCESSTIME = $00000800; { date = $0800 } + EM_RIGHTSMASK = $00001000; + EM_MAXDISKSPACE = $00002000; + +Type TvolUsage=record + totalBlocks, {static info} + freeBlocks, {dynamic} + purgableBlocks, {dynamic} + notYetPurgableBlocks, {dynamic} + totalDirEntries, {static} + availDirEntries, {dynamic} + Flags :LongInt; {dynamic} + SectorsPerBlock :byte; {static/number of 512 byte sectors per block} + volumeName :string[16];{static} + end; + + { used By ScanVolForRestrictions } + TobjVolRestr=array[1..64] of record + objId :LongInt; + MaxAllowedBlocks:LongInt; + end; + + +Type Tentry=record + EntryName :String[16]; + + NSType :byte; {namespace number} + DataForkSize :Longint; { =FileSize when NStype=0 (dos) } + {ResourceForkSize:Longint; (Mac data) =0 when NStype=0 (dos) } + FileSize :Longint; {FileSize=Resource+Data forksize } + + Attributes :Longint; + RightsMask :word; {(4)} + + CreationTime, + ArchiveTime, + ModifyTime, + LastAccessTime, + DeleteTime :TnovTime; {salvagable file only} + + OwnerId, + ArchiverId, + ModifierId, + DeletorId :Longint; {salvagable file only} + + end; + { Note: (4) When used with ScanDirectoryInfo, this field + contains the MaximumRightsMask. + Otherwise, the InheritedRightsMask } + +Type TdirRestrList=array[1..56] of record + Level:Byte; + MaxBlocks, + AvailableBlocks:Longint; + end; + {when MaxBlocks and Availableblocks are set to to $7FFFFFFF, + no restrictions are enforced -at this level-} + +Type TtrusteeInformation=record + NumberOfTrustees:Byte; + TrusteeID :array[1..20] of Longint; + TrusteeRights:array[1..20] of Word; + end; + +{-------------------- Volumes----------------------- } +{F216/05 [2.15c+]} +Function GetVolumeNumber( volumeName:String; Var volumeNumber:Byte ):boolean; +{ Returns the volume number of a given volume name } + +{F216/06 [2.15c+]} +Function GetVolumeName( volumeNumber:Byte; Var volumeName:String ):boolean; +{ Returns the volume name of a give volume number [0..31]. + If the volume is not mounted at the time of this call, a null-string is returned. } + +{F216/2C [2.15c+]} +Function GetVolumeUsage(volumeNumber:byte; Var VolUsage: TvolUsage):boolean; + +{F212 [2.15c+]} +Function IsVolumeRemovable( volumeNumber:Byte; + Var volIsRemoveable:Boolean):boolean; + +{F216/15 [2.15c+]} +Function GetVolumeNameWithHandle( dirHandle:Byte; + Var volumeName:String ):boolean; +{F216/29 [3.x]} +Function GetObjectVolRestriction(VolumeNumber:byte; objId:LongInt; + Var MaxAllowedBlocks,BlocksInUse:LongInt):boolean; + +{F216/21 [3.x]} +Function SetObjectVolRestriction(VolumeNumber:byte; objId, + MaxAllowedBlocks:LongInt):boolean; +{F216/22 [3.x]} +Function ClearObjectVolRestriction(VolumeNumber:byte; objId:LongInt):boolean; + + +{F216/20 [3.x]} +Function ScanVolForRestrictions(VolumeNumber:byte; + {i/o} Var sequenceNbr:LongInt; + {out} Var NbrOfObjects:byte; + Var ResultBuffer:TobjVolRestr):boolean; +{ 1st call: sequenceNbr=0, + after last call: sequenceNbr=0 again. } + +{-------------------- Directory Handles/ Drives -------------} + +{F216/01} +Function GetDirectoryPath(DirHandle:byte; Var PathName:string):boolean; + +{EF00 [2.0/2.1/3.x]} +Function GetDriveHandle( DriveNumber:Byte; Var DirHandle:Byte ):boolean; +{ The call returns a pointer to the shell's Drive Handle Table. (32 bytes) + (Drives A..Z and temporary drives [\]^_' ) + If a drive has been assigned a directory handle on the file server, + the handle can be found in the DHT at the position corresponding with the drive letter.} + +{EF00 [2.0/2.1/3.x]} +Function SetDriveHandle( DriveNumber:Byte; DirHandle:Byte ):boolean; + +{E900 [2.0/2.1/3.x]} +Function GetDirectoryHandle( DriveNumber:Byte; Var dirHandle,status:byte):Boolean; +{ Returns directory handle and status flags for a drive. } +{ Drivenumber = 0..31 (A..Z = 0..25) and temp drives (26..31) } + +{EF01 [2.0/2.1/3.x]} +Function GetDriveFlag( DriveNumber:Byte; Var DriveStatus:Byte ):Boolean; +{ This call returns a pointer to the shell's Drive Flag Table (32 Bytes) + Each entry indicates a drive's status (permanent,temporary,local,unassigned) + For further explanation see the DRIVE_xxx constants.} + +{EF01 [2.0/2.1/3.x]} +Function SetDriveFlag( DriveNumber:Byte; DriveStatus:Byte ):Boolean; + +{F216/14 [2.15c+]} +function DeallocateDirHandle(DirHandle : Byte) : Boolean; +{ This function deletes a directory handle } + + +{EF02 [2.0/2.1/3.x]} +Function GetDriveConnectionID( DriveNumber:Byte; Var connID:Byte):boolean; +{ returns the servernumber (1..8) associated with a drive. } + +{EF02 [2.0/2.1/3.x]} +Function SetDriveConnectionID( DriveNumber:Byte; connID:Byte):boolean; + +{F216/00 [2.15c+]} +Function SetDirectoryHandle( sourceDirHandle:Byte; sourceDirPath:String; + targetDirHandle:Byte ):boolean; +{ make handle 'targetHandle' point to the directory provided by + sourceHandle and/or sourceDirPath. } + +{F216/12 [2.15c+]} +FUNCTION AllocPermanentDirHandle( DriveNumber:Byte; + DirHandle : byte; DirPath : string ; + var NewDirHandle, EffectiveRights: byte ) :boolean; + +{F216/13 [2.15c+]} +function AllocTemporaryDirHandle( DriveNumber:byte; + DirHandle : Byte; DirPath : String; + var NewDirHandle,EffectiveRights : Byte) : Boolean; +{ Allocates a temporary directory handle, deleted automatically by EOJ. } + +{E901} +Function GetSearchDriveVector(Var vector:TsearchDriveVector):boolean; + +{E902 } +Function SetSearchDriveVector(vector:TsearchDriveVector):boolean; + +{E905 (shell 3.01+)} +Function MapFakeRootDirectory(DriveNumber:byte; DirPath:string):boolean; + +{E906 (shell 3.01+)} +Function DeleteFakeRootDirectory(DriveNumber:byte):boolean; + +{E907 (shell 3.01+)} +Function GetRelativeDriveDepth(DriveNumber:byte; Var depth:byte):boolean; + +{BA.. } +Function GetEnvPath(Var EnvPath:string):boolean; + +{BA.. } +Function SetEnvPath(EnvPath:string):boolean; + + +{secondary } +FUNCTION MapDrive(DriveNumber:byte; DirectoryPath:string; + Root, Permanent:boolean):boolean; + +{secondary } +FUNCTION MapPermanentDrive(DriveNumber:byte; DirectoryPath:string; + Root:boolean):boolean; + +{secondary} +Function MapSearchDrive(DriveNumber:byte; DirPath:string; + PathPosition:byte; + Insert,Root,Permanent:Boolean):boolean; + +{secondary} +Function DeleteDriveMapping(DriveNumber:Byte):boolean; + +{secondary} +Function DeleteConnectionsDriveMappings(ConnId:Byte):Boolean; + +{secondary} +Function IsSearchDrive(DriveNumber:byte):boolean; + +{4409 } +Function IsNetworkDrive(driveNumber:Byte):boolean; +{ isNetworkDrive is set to TRUE if the drive is a) a network drive, and + b) a legal drive letter was used. } + + +{------------------------- entries -----------------------------------------} + +{F217/0F [2.15c+]} +Function ScanFileInformation(DirHandle:Byte; FilePath:string; + SearchAttrib:Byte; + {i/o} VAR SequenceNbr:Integer; + {out} VAR fileInfo:Tentry):Boolean; + +{F217/F4 [3.0+]} +Function ConvertPathToDirEntryId(dirHandle:Byte; dirPath:string; + Var VolNbr :byte; + Var dirEntryID:LongInt):boolean; +{ aka ConvertPathToDirEntry / requires console rights } + +{F216/02} +Function ScanDirectoryInformation(dirHandle:byte; searchDirPath:string; + {i/o} Var sequenceNumber:word; + {out:} Var dirInfo:Tentry ):boolean; + +{F216/1F [2.15c+]} +Function GetDirectoryEntry(DirHandle:byte; + Var dirEntry:Tentry):boolean; + +{F216/1E [2.15c+]} +Function ScanDirectoryEntry(DirHandle:Byte; EntryName:string; SearchFlags:Longint; + {i/o} Var EntryId:Longint; + {out} Var Entry:Tentry ):boolean; + +{F217/F3 [3.0+]} +Function MapDirEntryIdToPath(VolNbr:byte;DirEntryId:Longint; NStype:byte; + Var ExtPath:string):boolean; + +{F216/25 [2.15c+] } +Function SetEntry(DirHandle:Byte;EntryId:Longint;SearchFlags:Byte; + ModFlags:Longint; Entry:Tentry ):boolean; + +{F217/10 [2.15c+]} +Function SetFileInformation(DirHandle:Byte; FilePath:string; + SearchAttrib:Byte; + fileInfo:TEntry):boolean; + +{F216/1B [2.15c+]} +Function ScanSalvagableFiles(DirHandle:Byte; + {i/o} Var EntryId:Longint; + {out} Var Entry:Tentry ):boolean; + +{F216/1D [3.0+]} +Function PurgeSalvagableFile(DirHandle:Byte; + EntryId:Longint; FileName:string):boolean; + +{F216/1C [3.0+] } +Function RecoverSalvagableFile(dirHandle:Byte; EntryId:Longint; + OldName,NewName:string):boolean; + +{F244 [2.1x/3.x]} +Function EraseFiles(dirHandle, searchAttrib:Byte; filePath:string ):boolean; + +{60.. (extended DOS call)} +Function GetTrueEntryName(DirPath:string; Var CanonicalPath:string):boolean; + + +{F216/0F [2.0/2.1/3.x]} +Function RenameDirectory( dirHandle:Byte; dirPath, newDirName :String):Boolean; + +{F216/0B [2.15c+]} +Function DeleteDirectory(DirHandle:Byte; DirPath:string):boolean; + +{F216/0A [2.15+]} +Function CreateDirectory(DirHandle:Byte; DirPath:string; MaxRightsMask:byte):boolean; + +{3B.. } +Function ChangeDirectory(DirPath:string):boolean; + + +{F216/24 [3.0+]} +Function SetDirRestriction(DirHandle:Byte; DiskSpaceLimit:Longint):boolean; + +{F216/23 [3.0+]} +Function ScanDirRestrictions(DirHandle:Byte; + Var NumberOfEntries:Byte; + Var RestrInfo:TdirRestrList):boolean; + +{--------------------------- Rights/trustees ---------------------------} + +{F216/27 [3.0+]} +Function SetTrustee(DirHandle:Byte;DirPath:string; + TrusteeObjectID:Longint; + RightsMask:Word ):boolean; + +{F216/2B [3.0+]} +Function DeleteTrustee(DirHandle:Byte;DirPath:String; + TrusteeObjectId:Longint):boolean; + +{F216/2A [3.0+]} +function GetEffectiveRights(DirHandle:Byte;DirPath:String; + var Rights:Word) : Boolean; + +{F216/04 [2.15c+]} +Function ModifyMaximumRightsMask(DirHandle:Byte;DirPath:string; + RevokeRightsMask,GrantRightsMask:Word):boolean; + + +{F217/47 [2.15c+]} +Function ScanBinderyObjectTrusteePaths(TrusteeObjectId:Longint; + VolumeNumber:Byte; + {i/o} Var SequenceNumber:word; + {out} Var AccessMask:Word; + Var Path:string ):boolean; + +{F216/26 [3.0+]} +Function ScanEntryForTrustees(DirHandle:Byte;DirPath:String; + {i/o} Var SequenceNumber:Byte; + {out} Var TrusteeInfo: TtrusteeInformation):boolean; + +IMPLEMENTATION{============================================================} + +{$IFDEF MSDOS} +uses dos; { file handles / 'normal' file attributes } +{$ENDIF} + +Type TintEntry=record { Unit internal Entry type } + { 0} _res1 :Longint; { low word = Dir Id of parent Dir } + { 4} _attrib :Longint; + { 8} _res2 :word; + { 10} _NStype :Byte; + { 11} _name :string[12]; + { 24} _creationTime :Longint; + { 28} _OwnerId :Longint; { hi-lo} + { 32} _ArchiveTime :Longint; + { 36} _ArchiverId :Longint; { hi-lo} + { 40} _modifyTime :Longint; + + { 44} _ModifierId :Longint; { files only } + { 48} _ForkSize :Longint; { files only } + { 52} _res3 :array[1..44] of byte; { Trustee obj IDs and Tr. rights } + { 96} _FileRightsMask:word; { files only } + { 98} _AccessDate :word; { files only } + + {100} _DirRightsMask :word; { directories only } + {102} _res4 :word; {Unique Dir ID, hi-lo} { directories only } + {104} _DeleteTime :Longint; { salvageable files only } + {108} _DeletorID :LongInt; { salvageable files only } + {112} _res5 :array[1..16] of byte; + {128} end; + +Procedure Convert2ExtEntry(Var Ie:TintEntry;Var Oe:Tentry); +begin +FillChar(Oe,Sizeof(Tentry),#$0); +with Ie,Oe + do begin + Attributes:=_Attrib; + NStype:=_NStype; + Entryname:=_name; + DosTime2NovTime(_CreationTime,CreationTime); + OwnerId:=Lswap(_OwnerId); {force lo-hi} + DosTime2NovTime(_ArchiveTime,ArchiveTime); + ArchiverId:=Lswap(_ArchiverID); {force lo-hi} + DosTime2NovTime(_ModifyTime,ModifyTime); + if (_attrib and $10)>0 { is entry a directory ? } + then begin + RightsMask:=_DirRightsMask; + end + else begin + ModifierId:=LSwap(_ModifierId); {force lo-hi} + DataForksize:=_Forksize; + if _NSType=0 + then FileSize:=_ForkSize; + RightsMask:=_FileRightsMask; + DosTime2NovTime(MakeLong(_accessDate,0),LastAccessTime); + DosTime2NovTime(_DeleteTime,DeleteTime); + DeletorId:=Lswap(_DeletorID); {force lo-hi} + end; + end; +end; + +Procedure Convert2IntEntry(Var Oe:TEntry;Var Ie:TIntEntry); +Var TempTime:Longint; +begin +FillChar(Ie,Sizeof(Tentry),#$0); +with Ie,Oe + do begin + _Attrib:=Attributes; + _NStype:=NStype; + _Name:=EntryName; + NovTime2DosTime(CreationTime,_CreationTime); + _OwnerId:=Lswap(OwnerId); {force hi-lo} + NovTime2DosTime(ArchiveTime,_ArchiveTime); + _ArchiverId:=Lswap(ArchiverId); {force hi-lo} + NovTime2DosTime(ModifyTime,_ModifyTime); + if (Attributes and $10)>0 { is entry a directory ? } + then begin + _DirRightsMask:=RightsMask; + end + else begin + _ModifierId:=Lswap(ModifierId); { force hi-lo } + _ForkSize:=DataForkSize; + _FileRightsMask:=RightsMask; + NovTime2DosTime(LastAccessTime,TempTime); + _AccessDate:=HiLong(TempTime); + NovTime2DosTime(DeleteTime,_DeleteTime); + _DeletorID:=Lswap(DeletorId); { force hi-lo } + end; + end; +end; + +Procedure ConvertPathToVolFormat(Var path:string); +{ reformat \\server\vol\path to VOL:PATH + server/vol:path to VOL:PATH } +Var pcolon,pslash:byte; +begin +if (Path[0]>#1) and (Path[1]='\') and (Path[2]='\') + then begin + delete(Path,1,2); + Path:=Path+'\'; + pslash:=pos('\',Path); + if pslash>0 + then begin + delete(Path,1,pslash); { remove servername from path } + pslash:=pos('\',Path); + if pslash>0 + then Path:=copy(Path,1,pslash-1)+':'+copy(Path,pslash+1,255); + end; + while Path[ord(Path[0])]='\' do dec(Path[0]); + end + else begin + pcolon:=pos(':',path); + if (path[0]>#3) and (pcolon>3) + then begin + pslash:=pos('/',path); + if (pslash=0) or (pslash>pcolon) + then pslash:=pos('\',path); + if (pslash>0) and (pslash#16 + then volumeName[0]:=#16; + volName:=volumeName; + if volname[ord(volName[0])]=':' + then dec(volName[0]); + len:=2+ord(volName[0]); + F2SystemCall($16,len+2,SizeOf(Trep),result); + end; +volumenumber:=TPrep(GlobalReplyBuf)^.volNbr; +getVolumeNumber:=(result=0) +{resultcodes: + 00 success; 98h volume doesn't exist } +end; + + +{F216/15 [2.15c+]} +Function GetVolumeNameWithHandle( dirHandle:Byte; + Var volumeName:String ):boolean; +Type Treq=record + len :word; + subFunc :byte; + _dirHandle :Byte; + end; + Trep=record + _sectPerBlock :Word; {hi-lo} + _TotalBlocks :Word; {hi-lo} + _availBlocks :Word; {hi-lo} { Use GetVolumeUsage for the other fields } + _TotalDirSlots:Word; {hi-lo} + _availDirSlots:Word; {hi-lo} + _volName :array[1..16] of byte; + _volRemoveable:Word; {hi-lo} + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subFunc:=$15; + _dirHandle:=dirHandle; + end; +F2SystemCall($16,Sizeof(Treq),SizeOf(Trep),result); +ZStrCopy(volumeName,TPrep(GlobalReplyBuf)^._volName,16); +if volumeName='' + then result:=$9B; { Invalid directory handle } +getVolumeNameWithHandle:=(result=0) +{ resultcodes: 00 success; $9B invalid directory handle } +end; + + +{F212 [2.15c+]} +Function IsVolumeRemovable( volumeNumber:Byte; + Var volIsRemoveable:Boolean):boolean; +{ stripped down version of the GetVolumeInfoWithNumber call } +Type Treq=Byte; + Trep=record + _sectPerBlock :Word; {hi-lo} + _TotalBlocks :Word; {hi-lo} + _availBlocks :Word; {hi-lo} + _TotalDirSlots :Word; {hi-lo} + _availDirSlots :Word; {hi-lo} + _volName :array[1..16] of byte; + _volRemoveable :Word; {hi-lo} + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +TPreq(GlobalReqBuf)^:=volumeNumber; +F2SystemCall($12,SizeOf(Treq),SizeOf(Trep),result); +With TPrep(GlobalReplyBuf)^ + do begin + volIsRemoveable:=(_volRemoveable>0); + if _volName[1]=0 + then result:=$98; + end; +IsVolumeRemovable:=(result=0); +{ resultcodes: 00 success; 98h Invalid volume number / volume not mounted } +end; + +{F216/22 [3.x]} +Function ClearObjectVolRestriction(VolumeNumber:byte; objId:LongInt):boolean; +{ If the objId doesn't exist, no error is returned. } +Type Treq=record + len:word; + subFunc:byte; + _volNbr:byte; + _objId:LongInt; { hi-lo } + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subFunc:=$22; + _volNbr:=VolumeNumber; + _objId:=Lswap(objId); { force hi-lo } + end; +F2SystemCall($16,SizeOf(Treq),0,result); +ClearObjectVolRestriction:=(result=0) +{ $8C No supervisor rights } +end; + +{F216/29 [3.x]} +Function GetObjectVolRestriction(VolumeNumber:byte; objId:LongInt; + Var MaxAllowedBlocks,BlocksInUse:LongInt):boolean; +{ If MaxAllowedBlocks is equal to $40000000 on return, there are no + disk restrictions for the object on this volume. } +{ You need not be logged in to use this call. } +Type Treq=record + len :word; + subFunc:byte; + _volNbr:byte; + _objId :Longint; {hi-lo} + end; + Trep=record + _MaxAllowedBlocks, + _BlocksInUse :Longint; + end; + TPreq=^Treq; + TPrep=^Trep; +Var objName:string; + objType:word; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len :=SizeOf(Treq)-2; + subFunc:=$29; + _volNbr:=VolumeNumber; + _objId :=Lswap(objId); {force hi-lo} + end; +F2SystemCall($16,SizeOf(Treq),SizeOf(Trep),result); +With TPrep(GlobalReplyBuf)^ + do begin + MaxAllowedBlocks:=_MaxAllowedBlocks; + BlocksInUse:=_BlocksInUse; + If BlocksInUse=0 + then if NOT nwBindry.GetBinderyObjectName(objId,objName,objType) + then result:=$FF; + end; +GetObjectVolRestriction:=(result=0) +{resultcodes: 00 success; $FF Invalid objectId } +end; + +{F216/20 [3.x]} +Function ScanVolForRestrictions(VolumeNumber:byte; + {i/o} Var sequenceNbr:LongInt; + {out} Var NbrOfObjects:byte; + Var ResultBuffer:TobjVolRestr):boolean; +{ 1st call: sequenceNbr=0, + // n-th call: sequenceNbr(n):=sequenceNbr(n-1)+NbrOfObjects + // (addition done by function itself) + + after last call: sequenceNbr=0 again. } +Type Treq=record + len:word; + subFunc:byte; + _volNbr:byte; + _seqNbr:LongInt; { lo-hi !} + end; + Trep=record + _NbrOfObjects:byte; + _buff :TobjVolRestr; + end; + TPreq=^Treq; + TPrep=^Trep; +Var t:byte; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subFunc:=$20; + _volNbr:=VolumeNumber; + _seqNbr:=sequenceNbr; + end; +F2SystemCall($16,SizeOf(Treq),SizeOf(Trep),result); +if result=0 + then begin + With TPrep(GlobalReplyBuf)^ + do begin + NbrOfObjects:=_NbrOfObjects; + ResultBuffer:=_buff; + For t:=1 to NbrOfObjects + do ResultBuffer[t].objId:=Lswap(_buff[t].ObjId); + if _NbrOfObjects=0 + then result:=$FF + else sequenceNbr:=sequenceNbr+_NbrOfObjects; + end + end + else NbrOfObjects:=0; +ScanVolForRestrictions:=(result=0) +{ $98 VolumeNumber doesn't exist; + $FF No New restriction data (end of iteration) } +end; + +{F216/21 [3.x]} +Function SetObjectVolRestriction(VolumeNumber:byte; objId,MaxAllowedBlocks:LongInt):boolean; +{ If the objId doesn't exist, no error is returned. } +Type Treq=record + len :word; + subFunc:byte; + _volNbr:byte; + _objId :Longint; {hi-lo} + _maxBlocks:LongInt; {lo-hi !!} + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalreqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subFunc:=$21; + _volNbr:=VolumeNumber; + _objId:=Lswap(objId); { force hi-lo } + _maxBlocks:=MaxAllowedBlocks; + end; +F2SystemCall($16,SizeOf(Treq),0,result); +SetObjectVolRestriction:=(result=0) +{ $8C No supervisor Rights } +end; + + +{--------------Dir handles/ drive mappings----------------------------------} + + +{BA.. } +Function GetEnvPath(Var EnvPath:string):boolean; {#d} +Type Tarr=array[1..2048] of byte; +Var regs:TTregisters; + penv:^Tarr; + i,envSize:word; + state:byte; +begin +regs.ah:=$BA; +RealModeIntr($21,regs); +envSize:=byte(nwPtr(regs.dx-1,3)^) SHL 4; +penv:=nwPtr(regs.dx,0); +i:=1; +state:=0; +while (i0) + do begin + EnvPath:=EnvPath+chr(penv^[i]); + inc(i); + end; +if i>envSize + then begin + result:=$301; + GetEnvPath:=false; + exit; + end; +result:=0; +GetEnvPath:=true; +{ 00 successful + 300 'Path' not found + 301 Path value could not be read } +end; + +{BA.. } +Function SetEnvPath(EnvPath:string):boolean; {#d} +Type Tarr=array[1..2048] of byte; +Var regs:TTregisters; + penv:^Tarr; + i,t,envSize:word; + state:byte; + pbegin,pend:word; + NewPathSize,OldPathSize:byte; + diff:integer; + sVector:TsearchDriveVector; + Vecind,p:byte; + dn:Byte; +begin +Upstring(EnvPath); +If pos('PATH=',envPath)=1 + then delete(EnvPath,1,5); +regs.ah:=$BA; +RealModeIntr($21,regs); +envSize:=word(nwPtr(regs.dx-1,3)^) SHL 4; +penv:=nwPtr(regs.dx,0); + +i:=1; +state:=0; +while (i0) + do inc(i); +if i>envSize + then begin + result:=$301; + SetEnvPath:=false; + exit; + end; +dec(i); +pend:=i; + +{ determine end of 'active' environment / marked by $00 00} +while (ienvSize + then begin + result:=$302; + SetEnvPath:=false; + exit; + end; + +diff:=NewPathSize-OldPathSize; +if diff>0 + then for t:=i downto pend + do penv^[t+diff]:=penv^[t]; +if diff<0 + then for t:=pend to i + do penv^[t+diff]:=penv^[t]; +Move(EnvPath[1],penv^[pbegin],NewPathSize); + +FillChar(Svector,SizeOf(TsearchDriveVector),#$FF); +VecInd:=1; +REPEAT +p:=pos(':',envPath); +if p>0 + then begin + dn:=ord(ord(envPath[p-1])-ord('A')); + p:=pos(';',envPath); + if p=0 + then envPath:='' + else delete(envPath,1,p); + IF IsNetworkDrive(dn) + then begin + Svector[VecInd]:=dn; + inc(VecInd) + end; + end; +UNTIL (p=0) or (VecInd=17); +SetSearchDriveVector(Svector); + +result:=0; +SetEnvPath:=true; +{ 00 successful + 300 'Path' not found + 301 Environment failure + 302 Environment overflow (new path too large) } +end; + + +{F216/01} +Function GetDirectoryPath(DirHandle:byte; Var PathName:string):boolean; +{ path includes volumename } +Type Treq=record + len :word; + subFunc:byte; + _dh :byte; + end; + Trep=record + DirPath:string[255]; + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subFunc:=$01; + _dh:=DirHandle; + end; +F2SystemCall($16,SizeOf(Treq),SizeOf(Trep),result); +PathName:=TPrep(GlobalReplyBuf)^.DirPath; +GetDirectoryPath:=(result=0) +{ 00 Successful 9B Bad directory handle } +end; + +{EF02 [2.0/2.1/3.x]} +Function GetDriveConnectionID( DriveNumber:Byte; Var connID:Byte):boolean; +{ returns the servernumber (1..8) associated with a drive. } +Type pArr=^arr; + arr=array[0..31] of byte; +Var regs:TTregisters; +begin +regs.ax:=$EF02; +RealModeIntr($21,Regs); +If DriveNumber>31 + then result:=$0105 + else begin + connID:=Parr(nwPtr(Regs.es,regs.si))^[DriveNumber]; + Result:=0; + end; +GetDriveConnectionID:=(Result=0); +end; + +{EF02 [2.0/2.1/3.x]} +Function SetDriveConnectionID( DriveNumber:Byte; connID:Byte):boolean; +Type pArr=^arr; + arr=array[0..31] of byte; +Var regs:TTregisters; +begin +regs.ax:=$EF02; +RealModeIntr($21,Regs); +If DriveNumber>31 + then result:=$0105 + else begin + Parr(nwPtr(Regs.es,regs.si))^[DriveNumber]:=connId; + Result:=0; + end; +SetDriveConnectionID:=(Result=0); +end; + + +{EF00 [2.0/2.1/3.x]} +Function GetDriveHandle( DriveNumber:Byte; Var DirHandle:Byte ):boolean; +{ The call returns a pointer to the shell's Drive Handle Table. (32 bytes) + (Drives A..Z and temporary drives [\]^_' ) + If a drive has been assigned a directory handle on the file server, + the handle can be found in the DHT at the position corresponding with the drive letter.} +Type pArr=^arr; + arr=array[0..31] of byte; +Var regs:TTregisters; +begin +regs.ax:=$EF00; +RealModeIntr($21,regs); +if DriveNumber>31 + then result:=$0105 + else begin + DirHandle:=Parr(nwPtr(Regs.Es,Regs.Si))^[DriveNumber]; + Result:=0; + end; +GetDriveHandle:=(Result=0); +end; + +{EF00 [2.0/2.1/3.x]} +Function SetDriveHandle( DriveNumber:Byte; DirHandle:Byte ):boolean; +Type pArr=^arr; + arr=array[0..31] of byte; +Var regs:TTregisters; +begin +regs.ax:=$EF00; +RealModeIntr($21,regs); +if DriveNumber>31 + then result:=$0105 + else begin + Parr(nwPtr(Regs.Es,Regs.Si))^[DriveNumber]:=DirHandle; + Result:=0; + end; +SetDriveHandle:=(Result=0); +end; + +{EF01 [2.0/2.1/3.x]} +Function GetDriveFlag( DriveNumber:Byte; Var DriveStatus:Byte ):Boolean; +{ This call returns a pointer to the shell's Drive Flag Table (32 Bytes) + Each entry indicates a drive's status (permanent,temporary,local,unassigned) + For further explanation see the DRIVE_xxx constants.} +Type pArr=^arr; + arr=array[0..31] of byte; +Var regs:TTregisters; +begin +regs.ax:=$EF01; +RealModeIntr($21,Regs); +If DriveNumber>31 + then result:=$0105 + else begin + DriveStatus:=Parr(nwPtr(Regs.es,regs.si))^[DriveNumber]; + Result:=0; + end; +GetDriveFlag:=(Result=0); +end; + +{EF01 [2.0/2.1/3.x]} +Function SetDriveFlag( DriveNumber:Byte; DriveStatus:Byte ):Boolean; +Type pArr=^arr; + arr=array[0..31] of byte; +Var regs:TTregisters; +begin +regs.ax:=$EF01; +RealModeIntr($21,Regs); +If DriveNumber>31 + then result:=$0105 + else begin + Parr(nwPtr(Regs.es,regs.si))^[DriveNumber]:=DriveStatus; + Result:=0; + end; +SetDriveFlag:=(Result=0); +end; + + +{E900 [2.0/2.1/3.x]} +Function GetDirectoryHandle( DriveNumber:Byte; Var dirHandle,status:byte):Boolean; +{ Returns directory handle and status flags for a drive. } +{ Drivenumber = 0..31 (A..Z = 0..25) and temp drives (26..31) } +{ Status Byte + 7 6 5 4 3 2 1 0 + | | +-Permenant Directory Handle + | +----Temporary Directory Handle + +----------------------Mapped to a local drive } +{ in case of an invalid driveNumber, handle and status will be set to 0 } +Var Regs:TTRegisters; +begin +With Regs +do begin + AX:=$E900; + DX:=DriveNumber; + RealModeIntr($21,Regs); + { AH = Status Flags; + 01 mapped to a permanent dir handle; + 02 mapped to a temporary dir handle; + 80 local drive. } + dirHandle:=AL; + status:=AH; + If dirHandle=0 + then begin status:=0;Result:=$FF end {INVALID_DRIVE_NUMBER} + else Result:=0; + GetDirectoryHandle:=(Result=0) + end; +{ result: $00 success; $FF Invalid Drive Number } +end; + + +{F216/00 [2.15c+]} +Function SetDirectoryHandle( sourceDirHandle:Byte; sourceDirPath:String; + targetDirHandle:Byte ):boolean; +{ make handle 'targetHandle' point to the directory provided by + sourceHandle and/or sourceDirPath. ( "Volume:dir\subdir" ) } +Type Treq=record + len :word; + subFunc :byte; + _TargetDH :Byte; + _SourceDH :Byte; + _SourceDP :String[255] + end; + TPreq=^Treq; +Var p:Byte; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$00; + _TargetDH:=targetDirHandle; + _SourceDH:=SourceDirHandle; + if SourceDirHandle=0 + then ConvertPathToVolFormat(SourceDirPath); + _sourceDP:=sourceDirPath; + UpString(_sourceDP); + len:=4+ord(_SourceDP[0]); + F2SystemCall($16,len+2,0,result); + end; +SetDirectoryHandle:=(result=0) +{ resultcodes: + 00 Success; 98h Volume does not exist; + 9Bh Bad directory handle; 9Ch Invalid Path. } +end; + + +{F216/12 [2.15c+]} +FUNCTION AllocPermanentDirHandle( DriveNumber:Byte; + DirHandle : byte; DirPath : string ; + var NewDirHandle, EffectiveRights: byte ) :boolean; +{ Effective server must be the server involved, i.e. where the dir is stored } +Type Treq=record + len : word; + subf : byte; + _dirHandle : byte; + _driveLetter : char; + _DirectoryPath: String[255]; + end; + Trep=record + _newDirHandle : byte; + _EffectiveRights : byte; { e.r. mask } + end; + TPreq=^Treq; + TPrep=^Trep; +Var p:Byte; +BEGIN +With TPreq(GlobalReqBuf)^ + do begin + subf := $12; + _dirHandle := dirHandle; + _driveLetter := chr(DriveNumber+ord('A')); + if Dirhandle=0 + then ConvertPathToVolFormat(DirPath); + _DirectoryPath:=DirPath; + UpString(_DirectoryPath); + len:=4+ord(_DirectoryPath[0]); + F2SystemCall($16,len+2,sizeof(Trep),result); + end; +if result = 0 + then with TPrep(GlobalReplyBuf)^ + do begin + effectiveRights := _effectiveRights; + newDirHandle := _newDirHandle; + end; +AllocPermanentDirHandle:=(result=0); +{ $00 Successful $98 Volume doen't exist $9C Invalid path } +end; + + + + + +{F216/13 [2.15c+]} +function AllocTemporaryDirHandle( DriveNumber:byte; + DirHandle : Byte; DirPath : String; + var NewDirHandle,EffectiveRights : Byte) : Boolean; +{ Allocates a temporary directory handle, deleted automatically by EOJ. } +{ Effective server must be the server involved, i.e. where the dir is stored } +Type TReq=record + Len : Word; + SubF : Byte; + Handle : Byte; + Letter : Char; + _DirectoryPath : String; + end; + TRep=record + NewH : Byte; + Mask : Byte; + end; + TPreq=^Treq; + TPrep=^Trep; +Var p:Byte; +begin +with TPReq(GlobalReqBuf)^ + do begin + SubF := $13; + Handle := DirHandle; + Letter := chr(DriveNumber+ord('A')); + { Allocating handles requires paths to be in + the VOL:path format.. NOT canonical } + if handle=0 + then ConvertPathToVolFormat(DirPath); + _DirectoryPath:=DirPath; + UpString(_DirectoryPath); + Len:=4+length(_DirectoryPath); + F2SystemCall($16,len+2,SizeOf(Trep),result); + end; +with TPrep(GlobalReplyBuf)^ + do begin + NewDirHandle := NewH; + EffectiveRights := Mask; + end; +AllocTemporaryDirHandle:=(result=0); +{ result: 00 success; 98h Volume doesn't exist; 9Ch Invalid Path } +end; + + +{F216/14 [2.15c+]} +function DeallocateDirHandle(DirHandle : Byte) : Boolean; +{ This function deletes a directory handle } +Type TReq=record + Len : Word; + SubF : Byte; + Handle : Byte; + end; + TPreq=^Treq; +begin +with TPReq(GlobalReqBuf)^ + do begin + Len := 2; + SubF := $14; + Handle:= DirHandle; + end; +F2SystemCall($16,Sizeof(Treq),0,result); +DeallocateDirHandle:=(result=0); +{ result: + 00h - Success; 9Bh - Bad directory handle } +end; + + +{E901 } +Function GetSearchDriveVector(Var vector:TsearchDriveVector):boolean; +Var regs:TTregisters; + tmp1,tmp2:word; +begin +regs.ax:=$E901; +GetGlobalBufferAddress(tmp1,tmp2,regs.ds,regs.dx); +{ DS:DX real-mode address of GlobalReplyBuffer } +RealModeIntr($21,regs); +result:=0; +Move(GlobalReplyBuf^,vector,sizeof(TsearchDriveVector)); +vector[17]:=$FF; +GetSearchDriveVector:=True; +end; + +{E902 } +Function SetSearchDriveVector(vector:TsearchDriveVector):boolean; +Var regs:TTregisters; + tmp1,tmp2:word; +begin +regs.ax:=$E902; +Move(vector,GlobalReqBuf^,sizeof(TsearchDriveVector)); +GetGlobalBufferAddress(regs.ds,regs.dx,tmp1,tmp2); +{ DS:DX real-mode address of GlobalRequestBuffer } +RealModeIntr($21,regs); +result:=0; +SetSearchDriveVector:=True; +end; + +Function IsSearchDrive(DriveNumber:byte):boolean; +Var pth:string; +begin +IsSearchDrive:=(getEnvPath(pth) + and (pos(chr(DriveNumber+ord('A'))+':',pth)>0)); +end; + + +{E905 (shell 3.00+)} +Function MapFakeRootDirectory(DriveNumber:byte; DirPath:string):boolean; +{ Dirpath may include server and volumename } +Var regs:TTregisters; + tmp1,tmp2:word; + PName:string; +begin +with regs + do begin + ax:=$E905; + bl:=driveNumber+1; { FF default, 0=A, 2= B etc. } + GetGlobalBufferAddress(ds,dx,tmp1,tmp2); + { VLM patch for SERVER/VOL: and VOL: type paths } + if (DirPath[0]>#2) and (pos(':',DirPath)>2) + then GetTrueEntryName(DirPath,PName) + else PName:=DirPath; + Pname:=Pname+#0; + move(PName[1],GlobalReqBuf^,ord(PName[1])); + { DS:DX real-mode address of GlobalRequestBuffer holding new path } + RealModeIntr($21,regs); + if (flags and 1 {carry})>0 + then result:=al + else result:=0; + end; +MapFakeRootDirectory:=(result=0); +{ $00 Successful $03 Invalid path $0F Invalid Drive $11 Not same device } +end; + +{E906 (shell 3.00+)} +Function DeleteFakeRootDirectory(DriveNumber:byte):boolean; +Var regs:TTregisters; +begin +with regs + do begin + ax:=$E906; + bl:=DriveNumber+1; + RealModeIntr($21,regs); + result:=0; + end; +DeleteFakeRootDirectory:=(result=0); +end; + +{E907 (shell 3.00+)} +Function GetRelativeDriveDepth(DriveNumber:byte; Var depth:byte):boolean; +Var regs:TTregisters; +begin +with regs + do begin + ax:=$E907; + bl:=DriveNumber+1; + RealModeIntr($21,regs); + depth:=al; + if al<$FF + then result:=0 + else result:=$FF; { no fake root assigned } + end; +GetRelativeDriveDepth:=(result=0); +{ 00 Succesful $FF No fake root assigned } +end; + +{secondary} +Function DeleteDriveMapping(DriveNumber:Byte):boolean; +Var dirHandle,status:byte; + pth:string; + ch:char; + p:byte; + DDepth,Dflag:byte; +begin +{ if searchdrive, remove drive from searchtable and PATH environment string } +IF GetEnvPath(pth) + then begin + if pth[ord(pth[0])]<>';' + then pth:=pth+';'; + p:=pos(chr(DriveNumber+ord('A'))+':',pth); + if p>0 + then begin { it is a searchdrive, remove from path } + Repeat + ch:=pth[p]; + delete(pth,p,1); + UNTIL ch=';'; + SetEnvPath(pth); { also creates a new searchdriveVector } + end; + end; +IF (result=0) and GetDirectoryHandle(DriveNumber,dirHandle,status) + then begin + IF GetRelativeDriveDepth(DriveNumber,DDepth) { is it a fake root ? } + then DeleteFakeRootDirectory(DriveNumber); + GetDriveFlag(DriveNumber,Dflag); + SetDriveFlag(DriveNumber,(Dflag and $F0) or DRIVE_UNUSED); + SetDriveHandle(DriveNumber,0); + SetDriveConnectionId(DriveNumber,0); + DeallocateDirHandle(dirHandle); + end; +DeleteDriveMapping:=(result=0); +end; + + +{secondary } +FUNCTION MapPermanentDrive(DriveNumber:byte; DirectoryPath:string; + Root:boolean):boolean; +var pth : string; + DriveHandle: Byte; +begin +IF GetTrueEntryName(DirectoryPath,pth) + then begin + while pth[ord(pth[0])] IN ['\','.','*','?'] + do dec(pth[0]); + if pth[1]<>'\' + then result:=$104 { attempt to map network drive to local drive } + else begin + If GetDriveHandle(DriveNumber,DriveHandle) and (DriveHandle<>0) + then DeleteDriveMapping(DriveNumber); + + IF MapFakeRootDirectory(DriveNumber,pth) + then begin + if (not root) + then DeleteFakeRootDirectory(DriveNumber); + { does not delete the mapping itself, + only the fake root. } + end; + end; + end + else result:=$101; { direcory not locatable } +MapPermanentDrive:=(result=0); +end; + +{secondary} +FUNCTION MapDrive(DriveNumber:Byte; DirectoryPath:string; + Root, Permanent:boolean):boolean; +var rights : byte; + newHandle : byte; + HandlePth,pth,srvr,vol: string; + OldConnId,VolConnId:byte; + p:byte; + VolNbr:byte; + Dflag:byte; +begin +IF Permanent + then begin + MapDrive:=MapPermanentDrive(DriveNumber,DirectoryPath,Root); + exit; + end; +{ map temporary drive } +IF GetTrueEntryName(DirectoryPath,pth) + then begin + if pth[ord(pth[0])]<>'\' + then pth:=pth+'\'; + if pth[1]<>'\' + then result:=$104 { attempt to map network drive to local drive } + else begin + delete(pth,1,2); + p:=pos('\',pth); + if p=0 then result:=$106; + srvr:=copy(pth,1,p-1); + delete(pth,1,p); + p:=pos('\',pth); + if p=0 then result:=$105; { volume does not exist } + vol:=copy(pth,1,p-1); + delete(pth,1,p); + IF NOT GetConnectionId(srvr,VolConnId) + then result:=$106; { server does not exist } + end; + end + else result:=$101; { direcory not locatable } +if (result=0) + then begin + while pth[ord(pth[0])] IN ['\','.','*','?'] + do dec(pth[0]); + + { rebuild path: Alloc handle requires VOL:path format } + HandlePth:=vol+':\'+pth; + GetPreferredConnectionId(OldConnId); + SetPreferredConnectionId(VolConnId); + + { IF Permanent + then AllocPermanentDirHandle(DriveNumber,0,HandlePth, + newHandle,rights) + else} + AllocTemporaryDirHandle(DriveNumber,0,HandlePth, + newHandle,rights); + if (result=0) + then begin + GetDriveFlag(DriveNumber,Dflag); + {If Permanent + then SetDriveFlag(DriveNumber,(Dflag and $F0) or DRIVE_PERMANENT) + else} + SetDriveFlag(DriveNumber,(Dflag and $F0) or DRIVE_TEMPORARY); + SetDriveHandle(DriveNumber,newHandle); + SetDriveConnectionId(DriveNumber,VolConnId); + IF root + then MapFakeRootDirectory(DriveNumber,'\\'+srvr+'\'+vol+'\'+pth); + end; + SetPreferredConnectionId(OldConnId); + end; +MapDrive:=(result=0); +end; + + + +Function MapSearchDrive(DriveNumber:byte; DirPath:string; + PathPosition:byte; + Insert,Root,Permanent:Boolean):boolean; +Var pth:string; + p,scCount:byte; + ch:char; +begin +IF MapDrive(DriveNumber,DirPath,Root,Permanent) + then begin + GetEnvPath(pth); + if pth[ord(pth[0])]<>';' + then pth:=pth+';'; + scCount:=1;p:=1; + while (scCount=ord(pth[0])); + pth:=copy(pth,1,p-1)+chr(DriveNumber+ord('A')) + +':.;'+copy(pth,p,255); + end + else pth:=pth+chr(DriveNumber+ord('A'))+':.;'; + SetEnvPath(pth); + end; +MapSearchDrive:=(result=0); +end; + +{secondary} +Function DeleteConnectionsDriveMappings(ConnId:Byte):Boolean; +Var t,connId2,res:Byte; +begin +res:=$FF; +for t:=0 to 31 + do if GetDriveConnectionId(t,connId2) and (connId2=connId) + then begin + DeleteDriveMapping(t); + if result=0 + then res:=0; + end; +result:=res; +DeleteConnectionsDriveMappings:=(result=0); +{00 successful FF No mappings affected OR Invalid connectionId } +end; + + +{4409 / implemented as a secondary function } +Function IsNetworkDrive(driveNumber:Byte):boolean; +{ isNetworkDrive is set to TRUE if the drive is a) a network drive, and + b) a legal drive letter was used. } +Var regs:TTRegisters; +begin +With regs +do begin + AX:=$4409; + BL:=DriveNumber+1; + RealModeIntr($21,Regs); + IsNetworkDrive:=(DX and $1000)<>0 + end; +end; + + +{--======================-- Entries --===============================--} + + +{60.. (extended DOS call)} +Function GetTrueEntryName(DirPath:string; Var CanonicalPath:string):boolean; +{ SERVER/VOL:[\]Path -> \\SERVER\VOL\path + VOL:[\]Path -> \\effective_server_name\VOL\path + D:\ -> D:\. + +{ if a volumename is supplied without a servername, the name of the + effective server will be returned. } + +{ Format of returned string: + a) D:\path\file.ext or + b) \\servername\volumename\path\file.ext } + +LABEL skip; + +Var reply :array[1..128] of byte; + regs :TTregisters; + pcolon, + pslash :byte; + srvr, + volname:string[47]; + connId :Byte; +begin +{ ----- Pre processing } +if DirPath[0]>#2 + then begin + if ((DirPath[1]='\') and (DirPath[2]='\')) + then begin + CanonicalPath:=DirPath; + UpString(Canonicalpath); + goto skip + end; + pcolon:=pos(':',DirPath); + if (pcolon=2) and (DirPath[0]=#3) and (DirPath[3]='\') + then DirPath:=DirPath+'.'; + { fix known problem of netware: D:\. instead of D:\ } + if (pcolon=2) and (DirPath[0]=#2) + then DirPath:=DirPath+'.'; + { fix know problem of -among others- OS/2-dos: D:. instead of D: } + end; +pcolon:=pos(':',DirPath); +if pcolon>2 + then begin { format must be VOL:[\]path or SERVER/VOL:[\]Path } + pslash:=pos('/',DirPath); + if pslash=0 + then pslash:=$FF; + if (pslash#0) and (dirPath[1]='\') + then delete(DirPath,1,1); + DirPath:='\\'+srvr+'\'+volname+'\'+DirPath; + end; +if dirPath='' + then dirPath:='\'; +{ ----- actual call } +dirPath:=dirPath+#0; { zero terminate } +WITH regs + do begin + Move(dirPath[1],GlobalReqBuf^,ord(dirPath[0])); + GetGlobalBufferAddress(ds,si,es,di); + { DS:SI real mode pointer to GlobalRequestBuffer holding asciiz path ; + ES:DI real mode pointer to GlbalReplyBuffer } + ah:=$60; + RealModeIntr($21,regs); + Move(GlobalReplyBuf^,reply[1],128); + if (regs.flags and 1 {carry})>0 + then begin + result:=ax; + reply[1]:=0; + end + else result:=0; + end; +ZstrCopy(CanonicalPath,reply[1],128); +{ ----- post-processing -- strip \ and . } +skip: ; +While CanonicalPath[ord(CanonicalPath[0])] in ['\','.'] + do dec(CanonicalPath[0]); +GetTrueEntryName:=(result=0); +{ $00 successful + $02 Invalid component in directory path OR drive letter only + $03 Malformed path OR invalid drive letter } +end; + + + +{3B.. } +Function ChangeDirectory(DirPath:string):boolean; +{ does not change the default drive } +Var regs:TTregisters; + tmp1,tmp2:word; +begin +if DirPath[0]>#63 + then result:=$110 { length of path too long } + else begin + DirPath:=DirPath+#0; + with regs + do begin + ah:=$3b; + Move(DirPath[1],GlobalReqBuf^,ord(DirPath[0])); + GetGlobalBufferAddress(ds,dx,tmp1,tmp2); + { DS:DX real-mode pointer to GlobalRequestBuffer holding DirPath } + RealModeIntr($21,regs); + If (flags and 1 {carry})>0 + then result:=$111 { invalid pathname } + else result:=0; + end; + end; +ChangeDirectory:=(result=0); +end; + +{F216/0A [2.15+]} +Function CreateDirectory(DirHandle:Byte; DirPath:string; MaxRightsMask:byte):boolean; +Type Treq=record + len :word; + subFunc :byte; + _DirHandle:byte; + _MRM :byte; + _dirPath :string[255]; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalreqBuf)^ +do begin + subFunc:=$0A; + _dirHandle:=DirHandle; + _MRM:=MaxRightsMask; + _DirPath:=DirPath; + len:=4+ord(_dirPath[0]); + F2SystemCall($16,len+2,0,result); + end; +CreateDirectory:=(result=0) +{ 00 successful 84 No create privileges 98 Volume doesn't exist + FF directory already exists } +end; + + +{F216/0B [2.15c+]} +Function DeleteDirectory(DirHandle:Byte; DirPath:string):boolean; +Type Treq=record + len :word; + subFunc :byte; + _DirHandle:byte; + unused :byte; + _DirPath :string[255]; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalreqBuf)^ +do begin + subFunc:=$0B; + _DirHandle:=DirHandle; + _DirPath:=DirPath; + unused:=0; + len:=4+ord(_DirPath[0]); + F2SystemCall($16,len+2,0,result); + end; +DeleteDirectory:=(result=0) +{ 00 successful 8A No delete privileges + 98 Volume doesn't exist 9B Bad directory handle + 9C Invalid path 9F Directory in use + A0 Directory not empty } +end; + + +{F217/F4 [3.0+]} +Function ConvertPathToDirEntryId(dirHandle:Byte; dirPath:string; + Var VolNbr :byte; + Var dirEntryID:LongInt):boolean; +{ aka ConvertPathToDirEntry } +Type Treq=record + len :word; + subFunc :byte; + _dirHandle:byte; + _DirPath :string[255]; + end; + Trep=record + _volNbr:Byte; + _EntryId:Longint; {hi-lo} + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + subFunc:=$F4; + _dirHandle:=DirHandle; + _dirPath:=DirPath; + UpString(_DirPath); + If DirHandle=0 + then ConvertPathToVolFormat(_DirPath); + len:=3+ord(_DirPath[0]); + F2SystemCall($17,len+2,SizeOf(Trep),result); + end; +With TPrep(GlobalReplyBuf)^ +do begin + VolNbr :=_volNbr; + dirEntryId:=_EntryId; + end; +ConvertPathToDirEntryId:=(result=0) +{ 00 Successful 9B Bad directory Handle + 9C Invalid Path C6 No console rights } +end; + +{F217/F3 [3.0+]} +Function MapDirEntryIdToPath(VolNbr:byte;DirEntryId:Longint; NStype:byte; + Var ExtPath:string):boolean; +{aka MapDirectoryNumberToPath } +{ Returns full path/ with nameSpace information; + Doesn't return server or volumename. } +Type Treq=record + len :word; + subFunc :byte; + _VolNbr :byte; + _EntryId:longint; {hi-lo} + _NameSp :byte; + end; + Trep=record + _path:array[1..255] of byte; {!! maximum: 512 bytes in path ! } + end; + TPreq=^Treq; + TPrep=^Trep; +Var TempPath:string; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subFunc:=$F3; + _VolNbr:=VolNbr; + _EntryId:=DirEntryId; + _NameSp:=NStype; + end; +F2SystemCall($17,SizeOf(Treq),SizeOf(Trep),result); +if result=0 + then begin + With TPrep(GlobalReplyBuf)^ + do ZstrCopy(TempPath,_path,255); + { TempPath according to the 'new' Novell format; + translate into a 'DOS' style path } + NovPath2DOSPath(TempPath,ExtPath); { dir\subdir (no server or volume name) } + end; +MapDirentryIdtoPath:=(result=0) +{ 00 Successful C6 No console rights FF ? } +end; + + +{F216/02} +Function ScanDirectoryInformation(dirHandle:byte; searchDirPath:string; + {i/o} Var sequenceNumber:word; + {out:} Var dirInfo:Tentry ):boolean; +{ set sequenceNumber to 0 before the first call. + + If wildcards (* or ?) are included in the searchDirPath: + Iterate until a $9C error is returned. + + If you don't include a wildcard in the searchDirPath, only use + this call once. Do not iterate, the same entry will be returned + eternaly. + + } +Type Treq=record + len :word; + subFunc :byte; + _dirHandle :byte; + _subDirNumber:word; {hi-lo} + _dirPath :string[255] + end; + Trep=record + _subDirName :array[1..16] of byte; + _creationDate :word; + _creationTime :word; + _ownerObjId :LongInt; {hi-lo} + _maxRightsMask:word; + _SubDirNbr :word; {hi-lo} + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$02; + _dirHandle:=dirHandle; + _subDirNumber:=swap(sequenceNumber); { force hi-lo} + _dirPath:=searchDirPath; + UpString(_dirPath); + len:=5+ord(searchDirPath[0]); + F2SystemCall($16,len+2,SizeOf(Trep),result); + end; +With TPrep(GlobalReplyBuf)^ + do begin + FillChar(dirInfo,SizeOf(Tentry),0); + ZstrCopy(dirInfo.EntryName,_SubDirName,16); + + DosTime2NovTime(MakeLong(swap(_CreationDate),swap(_CreationTime)), + dirInfo.creationTime); + dirInfo.ownerId:=Lswap(_ownerObjId); + dirInfo.RightsMask:=_maxRightsMask; + sequenceNumber:=swap(_SubDirNbr)+1; + end; +ScanDirectoryInformation:=(result=0) +{resultcodes: $00 success; $98 Volume does not exist; + $9B Bad directory Handle $9C Invalid Path } +end; + + +{F216/0F [2.0/2.1/3.x]} +Function RenameDirectory( dirHandle:Byte; dirPath, newDirName :String):Boolean; +{ The new directory name must be a regular (legal) directory name, + max 14 chars long. + The user must have Parental and Modify rights in the parent directory of + the directory to be renamed. } +Type Treq=record + len :word; + subFunc :byte; + _dirHandle :Byte; + _dirNames :Array[0..255+1+14] of byte; { _dirpath[0] is allowed to be 0 } + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$0F; + _dirHandle:=dirHandle; + Upstring(dirPath); + UpString(newDirName); + Move(DirPath[0],_DirNames[0],ord(DirPath[0])+1); + Move(newDirName[0],_DirNames[1+_DirNames[0]],ord(newDirName[0])+1); + len:=4+ord(dirPath[0])+ord(newDirName[0]); + F2SystemCall($16,len+2,0,result); + end; +RenameDirectory:=(result=0) +{ Possible ResultCodes: + 8B No Rename Privileges; 9B Bad Directory Handle; + 9C Invalid Path; 9E Invalid (new) Dir Name. } +end; + + +{F216/1F [2.15c+]} +Function GetDirectoryEntry(DirHandle:byte; + Var dirEntry:Tentry):boolean; +Type Treq=record + len:word; + subFunc:byte; + _dirHandle:byte; + end; + Trep=record + _Entry :TintEntry; + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subFunc:=$1F; + _dirHandle:=dirHandle; + end; +F2SystemCall($16,SizeOf(Treq),SizeOf(Trep),result); +With TPrep(GlobalReplyBuf)^ + do begin + Convert2ExtEntry(_entry,dirEntry); + end; +GetDirectoryEntry:=(result=0) +{ 00 successful 98 Volume doesn't exist + 9B Bad directory handle 9C Invalid path } +end; + + +{B601 [2.0+] } +function SetExtendedFileAttributes(FilePath:String; Attr:Byte) : Boolean; +{ See GetExtFAttr for meaning of Attr the Attribute + Function result code: + 00h Success; + FFh File not found; + FEh Access denied } +Var Novregs:TTRegisters; + tmp1,tmp2:word; +begin +with NovRegs +do begin + AX := $B601; + if FilePath[0]=#255 + then FilePath[255]:=#0 + else FilePath:=FilePath+#0; + Move(FilePath[1],GlobalReqBuf^,ord(FilePath[0])); + GetGlobalBufferAddress(ds,dx,tmp1,tmp2); + { DS:DX real mode pointer to GlobalRequestBuffer holding FilePath } + CL := Attr; + RealModeIntr($21,NovRegs); + IF (Flags AND 1 {carry})>0 + then Result:=AL + else Result:=$00; + Result := AL + end; +SetExtendedFileAttributes:=(Result=0); +end; + + + +{B600 [2.0+]} +function GetExtendedFileAttributes(FilePath:String; var Attributes:Byte) : Boolean; +{ Meaning of Attributes: + 7 6 5 4 3 2 1 0 + | | | | | | | + | | | | +---+---+------Search mode + | | | +----------------------transactional bit A_TRANSACTIONAL + | | +--------------------------Indexing bit A_INDEXED + | +------------------------------Read Audit bit A_READ_AUDIT + +----------------------------------Write Audit bit A_WRITE_AUDIT + } +Var NovRegs:TTRegisters; + tmp1,tmp2:word; +begin +with NovRegs +do begin + AX := $B600; + if FilePath[0]=#255 + then FilePath[255]:=#0 + else FilePath:=FilePath+#0; { null terminate string } + Move(FilePath[1],GlobalReqBuf^,ord(FilePath[0])); + GetGlobalBufferAddress(ds,dx,tmp1,tmp2); + { DS:DX real mode pointer to GlobalRequestBuffer hloding FilePath } + RealModeIntr($21,NovRegs); + IF (Flags and 1 {carry})>0 + then Result := AL + else Result:=$00; + Attributes := CL; + end; +GetExtendedFileAttributes:=(Result=0); +{ $8C caller lacks privileges + FEh not permitted to search directory + FFh file not found } +end; + + + + +{F3.. [2.x/3.x]} +Function FileServerFileCopy( sourceFileHandle, destFileHandle:word; + sourceFileOffset, destFileOffset:Longint; + numberOfBytesToCopy :Longint; + VAR numberOfBytesCopied :Longint ):boolean; +{Note: both source and destination must be on the same file server +SeeAlso: 3C..,3F..} +Type Treq=record + _sFH,_dFH :word; {lo-hi} {as returned by GetFileHandle.} + _sFoffs,_dfOffs:Longint; {lo-hi} + _NbrOfBytes :Longint; {lo-hi} + end; + TPreq=^Treq; +Var regs:TTRegisters; + tmp1,tmp2:word; +begin +with TPreq(GlobalReqBuf)^ + do begin + _sFH:=sourceFileHandle; + _dFH:=destFileHandle; + _sFoffs:=sourceFileOffset; + _dFoffs:=destFileOffset; + _NbrOfBytes:=numberOfBytesToCopy; + end; +with regs + do begin + AH:=$F3; + GetGlobalBufferAddress(es,di,tmp1,tmp2); + { ES:DI real mode pointer to GlobalRequestBuffer } + RealModeIntr($21,regs); + result:=AL; + end; +numberOfBytesCopied:=MakeLong(regs.cx,regs.dx); { ? swap those regs for correct byte order ? } +FileServerFileCopy:=(Result=0); +end; + +{level-0 function. See GetFileAttributes and SetFileAttributes } +Function DoFileAttributes(subf:byte;FilePath:string;VAR attr:byte):boolean; +Var regs:TTregisters; + tmp1,tmp2:word; +begin +with regs +do begin + AH:=$43; + AL:=subf; + if subf=$01 then CX:=attr; + if filePath[0]=#255 + then filePath[255]:=#0 + else filePath:=filePath+#0; + Move(FilePath[1],GlobalReqBuf^,ord(FilePath[0])); + GetGlobalBufferAddress(ds,dx,tmp1,tmp2); + { DS:DX real mode pointer to GlobalRequestBuffer holding FilePath } + RealModeIntr($21,regs); + IF ((Flags and 1 {Fcarry})<>0) + then result:=AL + else begin + result:=$00; + if subf=$00 then attr:=CX + end; + end; +DoFileAttributes:=(result=$00); +{ resultcodes: 00 success; 01 invalid function; + 03 path not found; 05 access denied. } +end; + +{4300 [1.x/2.x/3.x]} +Function GetFileAttributes(FilePath:string; Var attr:byte):boolean; +{ A_READ_ONLY,A_HIDDEN,A_SYSTEM and A_SHAREABLE only. } +begin +GetFileAttributes:=DoFileAttributes($00,FilePath,attr); +end; + +{4301 [1.x/2.x/3.x]} +Function SetFileAttributes(FilePath:string; attr:byte):boolean; +{ A_READ_ONLY,A_HIDDEN,A_SYSTEM and A_SHAREABLE only. } +Var _attr:byte; +begin +_attr:=attr; +SetFileAttributes:=DoFileAttributes($01,FilePath,_attr); +end; + + + +{F217/0F [2.15c+]} +Function ScanFileInformation(DirHandle:Byte; FilePath:string; + SearchAttrib:Byte; + {i/o} VAR SequenceNbr:Integer; + {out} VAR fileInfo:Tentry):Boolean; +{ To be called Iteratatively; initial value for seqNbr=-1 } +{ wildcards in filename allowed. + Iterate util an error $FF occurs } +Type Treq=record + len :word; + subFunc :byte; + _seqNbr :word; {hi-lo} + _dirHandle :byte; + _searchAttrib:Byte; + _filePath :string; + end; + Trep=record + _seqNbr :word; {hi-lo} + _fileName :array[1..14] of byte; + _Fattr, + _ExtFattr :Byte; + _Fsize :LongInt; {hi-lo} + _Crdate :word; {hi-lo} + _LastAccDate :word; {hi-lo} + _LastUpdDate, + _LastUpdTime :Word; + _ownerObjId :Longint; {hi-lo} + _LastArchDate, + _lastArchTime:Word; + _reserved :array[1..56] of byte; + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + subFunc:=$0F; + _seqNbr:=swap(word(SequenceNbr)); { force hi-lo } + _dirHandle:=dirHandle; + _searchAttrib:=searchAttrib; + _filePath:=FilePath; + len:=6+ord(_filePath[0]); + F2SystemCall($17,len+2,SizeOf(Trep),result); + end; +with TPrep(GlobalReplyBuf)^ +do begin + FillChar(fileInfo,sizeOf(fileInfo),#0); + SequenceNbr:=Integer(swap(_seqNbr)); { force lo-hi } + ZstrCopy(fileInfo.EntryName,_filename,15); + fileInfo.Attributes:=(_ExtFattr SHL 8)+_Fattr; + fileInfo.filesize:=Lswap(_Fsize); { force lo-hi} + fileinfo.OwnerID:=Lswap(_ownerObjID); { force lo-hi} + DosTime2NovTime(MakeLong(swap(_CrDate),0),fileinfo.creationTime); + DosTime2NovTime(MakeLong(swap(_LastAccDate),0),fileinfo.lastAccessTime); + DosTime2NovTime(MakeLong(swap(_LastUpdDate),swap(_LastUpdTime)) + ,fileinfo.ModifyTime); + DosTime2NovTime(MakeLong(swap(_LastArchDate),swap(_lastArchTime)) + ,fileinfo.ArchiveTime); + end; +ScanFileInformation:=(result=0) +{ 89 No search privileges FF No more matching files } +end; + + +{F217/10 [2.15c+]} +Function SetFileInformation(DirHandle:Byte; FilePath:string; + SearchAttrib:Byte; + fileInfo:TEntry):boolean; +Type Treq=record + len :word; + subFunc :byte; + _Fattr, + _ExtFattr :Byte; + reserved1 :LongInt; {hi-lo} + _crDate :word; {hi-lo} + _lastAccDate :word; {hi-lo} + _lastUpdTime :Longint; + _ownerObjId :Longint; {hi-lo} + _lastArchTime:Longint; + reserved2 :array[1..56] of byte; + _dirHandle :Byte; + _searchAttr :byte; + _filePath :string; + end; + TPreq=^Treq; +Var DummyDate:Longint; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + subFunc:=$10; + _Fattr:=Lo(LowLong(fileInfo.Attributes)); + _ExtFattr:=Hi(LowLong(fileinfo.Attributes)); + _ownerObjId:=Lswap(fileinfo.OwnerId); {force hi-lo} + _dirHandle:=DirHandle; + _searchAttr:=SearchAttrib; + _filePath:=FilePath; + If Dirhandle=0 + then ConvertPathToVolFormat(_FilePath); + UpString(_filePath); + NovTime2DosTime(fileinfo.CreationTime,dummyDate); + _crDate:=HiLong(dummyDate); + NovTime2DosTime(fileinfo.LastAccessTime,dummyDate); + _lastAccDate:=HiLong(dummyDate); + NovTime2DosTime(fileinfo.ModifyTime,_lastUpdTime); + NovTime2DosTime(fileinfo.ArchiveTime,_lastArchTime); + len:=82+ord(_filepath[0]); + F2SystemCall($17,len+2,0,result); + end; +SetFileInformation:=(result=0); +{ result codes: 00 Success } +end; + + +{F244 [2.1x/3.x]} +Function EraseFiles(dirHandle, searchAttrib:Byte; filePath:string ):boolean; +{ marks files for deletion / in DOS parlance: delete file, file remains purgable } +Type Treq=record + _dirHandle:Byte; + _Sattr:Byte; + _filePath:string; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + _dirHandle:=dirHandle; + _Sattr:=searchAttrib; + _filePath:=filePath; + F2SystemCall($44,3+ord(_filepath[0]),0,result); + end; +EraseFiles:=(result=0); +{ resultcodes: 00 Success; 98h Volume doesn't exist; 9Bh bad directory handle; + 9Ch invalid path; FFh no files found error. } +end; + +{F216/1B [3.0+]} +Function ScanSalvagableFiles(DirHandle:Byte; + {i/o} Var EntryId:Longint; + {out} Var Entry:Tentry ):boolean; +{ Iterate (with entryId set to -1 at first) until an error $FF occurs } +Type Treq=record + len :word; + subFunc :byte; + _DirHandle:Byte; + _EntryId :Longint; {low_word-hi_word & each word lo-hi } + end; + Trep=record + _EntryId :Longint; + _Entry :TintEntry; + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subFunc:=$1B; + _DirHandle:=DirHandle; + _EntryId:=EntryId; + end; +F2SystemCall($16,SizeOf(Treq),Sizeof(Trep),result); +With TPrep(GlobalReplyBuf)^ + do begin + EntryId:=_EntryId; {return next EntryId for iteration} + {low_word-hi_word & each word lo-hi } + Convert2ExtEntry(_Entry,Entry); + end; +ScanSalvagableFiles:=(result=0) +{ 98 Volume does not exist FF No more erased files } +end; + +{F216/1D [3.0+]} +Function PurgeSalvagableFile(DirHandle:Byte; + EntryId:Longint; FileName:string):boolean; +{ either supply an entryId and an empty filename, + or supply an entryId of -1 and a filename. Note that the filename + may not be unique: there may be more than one old deleted versions + of a filename. } +Type Treq=record + len :word; + subFunc :byte; + _DirHandle:Byte; + _EntryId :Longint; {low_word-hi_word & each word lo-hi } + _Name :string[255]; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$1D; + _DirHandle:=DirHandle; + _EntryId:=EntryId; + _Name:=FileName; + UpString(_name); + len:=7+ord(_Name[0]); + F2SystemCall($16,len+2,0,result); + end; +PurgeSalvagableFile:=(result=0) +end; + +{F216/1C [3.0+] } +Function RecoverSalvagableFile(dirHandle:Byte; EntryId:Longint; + OldName,NewName:string):boolean; +{ entryId may be set to -1 + OldName is the name of the file before it was deleted. + NewName is the name to be assigned to the recovered file } +Type Treq=record + len :word; + subFunc :byte; + _DirHandle :Byte; + _EntryId :Longint; {low_word-hi_word & each word lo-hi } + _OldAndNewName:string[255]; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$1C; + _DirHandle:=DirHandle; + _EntryId:=EntryId; + UpString(OldName); + UpString(NewName); + _OldAndNewName:=OldName; + move(NewName[0],_OldAndNewName[ord(oldname[0])+1],ord(NewName[0])+1); + len:=8+ord(oldName[0])+ord(NewName[0]); + F2SystemCall($16,len+2,0,result); + end; +RecoverSalvagableFile:=(result=0) +{ 98 Volume does not exist FF No more erased files } +end; + + +{F216/24 [3.0+]} +Function SetDirRestriction(DirHandle:Byte; DiskSpaceLimit:Longint):boolean; +{ limit expressed in Blocks. set limit to 0 to lift limit. + use a negative number if limit should be equal to 0 } +Type Treq=record + len :word; + subFunc :byte; + _DirHandle:Byte; + _Limit :Longint; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subFunc:=$24; + _DirHandle:=DirHandle; + _Limit:=DiskSpaceLimit; + end; +F2SystemCall($16,SizeOf(Treq),0,result); +SetDirRestriction:=(result=0) +end; + + +{F216/23 [3.0+]} +Function ScanDirRestrictions(DirHandle:Byte; + Var NumberOfEntries:Byte; + Var RestrInfo:TdirRestrList):boolean; +Type Treq=record + len:word; + subFunc:byte; + _DirHandle:Byte; + end; + Trep=record + _Entries:Byte; + _Info:TdirRestrList; + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subFunc:=$23; + _DirHandle:=DirHandle; + end; +F2SystemCall($16,SizeOf(Treq),Sizeof(Trep),result); +With TPrep(GlobalReplyBuf)^ + do begin + NumberOfEntries:=_Entries; + RestrInfo:=_Info; + end; +ScanDirRestrictions:=(result=0) +end; + + +Procedure FixEntryNameFormat(Var s:string); +Var res:string; + p:byte; +begin +res:=''; +for p:=1 to ord(s[0]) + do begin + if s[p]='?' + then res:=res+#$FF+#$BF + else if s[p]='*' + then res:=res+#$FF+'*' + else res:=res+s[p] + end; +s:=res; +end; + + +{F216/1E [2.15c+]} +Function ScanDirectoryEntry(DirHandle:Byte; EntryName:string; SearchFlags:Longint; + {i/o} Var EntryId:Longint; + {out} Var Entry:Tentry ):boolean; +Type Treq=record + len :word; + subFunc :byte; + _DirHandle :Byte; + _SearchFlags:Byte; { standard: $16 for dirs / $06 for files } + _SeqNbr :Longint; { lo-hi , set to -1 initially } + _EntryName :string; + end; + + Trep=record { len = 84h = 132 dec. } + _EntryID :Longint; { lo-hi } + _Entry :TintEntry; + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$1E; + _DirHandle:=DirHandle; + _SearchFlags:=SearchFlags; + _SeqNbr:=EntryId; + _EntryName:=EntryName;UpString(_EntryName); + FixEntryNameFormat(_EntryName); + len:=8+ord(_EntryName[0]); + F2SystemCall($16,len+2,Sizeof(Trep),result); + end; +With TPrep(GlobalReplyBuf)^ + do begin + EntryId:=_EntryId; {return next EntryId for iteration} + Convert2ExtEntry(_Entry,entry); + end; +ScanDirectoryEntry:=(result=0) +end; + +{F216/25 [2.15c+] } +Function SetEntry(DirHandle:Byte;EntryId:Longint;SearchFlags:Byte; + ModFlags:Longint; Entry:Tentry ):boolean; +Type Treq=record + len :word; + subFunc :byte; + _dirHandle:Byte; + _SFlags :Byte; + _EntryId :Longint; {lo-hi} + _ModFlags :Longint; {lo-hi} + _Entry :TintEntry; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subFunc:=$25; + _dirHandle:=DirHandle; + _EntryId:=EntryId; + _ModFlags:=ModFlags; + Convert2IntEntry(Entry,_Entry); + end; +F2SystemCall($16,SizeOf(Treq),0,result); +SetEntry:=(result=0) +end; + +{------------------ Secondary Functions ----------------------------} + +Function IsFileShareable(Path : String):boolean; + +var F: File; + FAttr : Word; + +begin + { Assign(F, Path); + GetFAttr(F, FAttr); + result:=DOSerror; } + IsFileShareable:=(result=0) and ((FAttr and $80)>0) +end; + +function FlagFileShareable(Path : String) : Boolean; +{ when the file could NOT be made shareable, false is returned as the + function result, a doserror# is returned as the result code. } +var F : File; + Attr : Word; + ErrCode : word; + Share : Boolean; +begin +if NOT IsFileShareable(Path) { Share: is it sharable? } + then begin + Assign(F,Path); + {SetFAttr(F,Attr or A_SHAREABLE); OR existing atrib. with SHARE bit } + {Result := DOSError;} + end; +FlagFileShareable := (Result=0); +end; + + +Function GetFileHandle(Var f):word; +begin +{GetFileHandle:=filerec(f).handle;} +end; + +{------===================-- Trustee/Max. Rights masks --=================--} + + +{F216/27 [3.0+]} +Function SetTrustee(DirHandle:Byte;DirPath:string; + TrusteeObjectID:Longint; + RightsMask:Word ):boolean; +Type Treq=record + len :word; + subFunc :byte; + _DirHandle:byte; + _ObjId :Longint; { hi-lo } + _Rights :Word; { lo-hi } + _DirPath :string; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$27; + _DirHandle:=DirHandle; + if DirHandle=0 + then ConvertPathToVolFormat(DirPath); + _DirPath:=DirPath;UpString(_DirPath); + _ObjId:=Lswap(TrusteeObjectId); + _Rights:=RightsMask; + len:=9+ord(_DirPath[0]); + F2SystemCall($16,len+2,0,result); + end; +SetTrustee:=(result=0) +{ Possible resultcodes: 8C No modify privileges; + 98 Volume doesn't exist; 9B Bad directory handle + 9C Invalid path; FC No such bindery object } +end; + + +{F216/2B [3.0+]} +Function DeleteTrustee(DirHandle:Byte;DirPath:String; + TrusteeObjectId:Longint):boolean; +{ If DirHandle equals 0, DirPath should be according to the + VOL:\path format. All other path formats will result in + an resultcode of 98h (No such volume) } +Type Treq=record + len :word; + subFunc :byte; + _DirHandle:byte; + _ObjId :Longint; { hi-lo } + _Unused :Byte; + _DirPath :string; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$2B; + _DirHandle:=DirHandle; + if DirHandle=0 + then ConvertPathToVolFormat(DirPath); + _DirPath:=DirPath;UpString(_DirPath); + _ObjId:=Lswap(TrusteeObjectId); + _Unused:=0; + len:=8+ord(_DirPath[0]); + F2SystemCall($16,len+2,0,result); + end; +DeleteTrustee:=(result=0); +{ Possible resultcodes: 98 Volume doesn't exist + 9B Bad directory handle; 9C Invalid path + FE no such trustee } +end; + + +{F216/2A [3.0+]} +function GetEffectiveRights(DirHandle:Byte;DirPath:String; + var Rights:Word) : Boolean; +{ returns the requesting workstation's effective directory rights } +Type Treq=record + Len : word; + SubF : Byte; + _DirHandle : Byte; + _DirName : String; + end; + TRep=record + _RightsMask : Word; + end; + TPreq=^Treq; + TPrep=^Trep; +begin +with TPreq(GlobalReqBuf)^ + do begin + SubF := $2A; + _DirHandle := DirHandle; + if DirHandle=0 + then ConvertPathToVolFormat(DirPath); + _DirName := DirPath;UpString(_DirName); + Len := 3+ord(DirPath[0]); + F2SystemCall($16,len+2,SizeOf(Trep),result); + end; +with TPrep(GlobalReplyBuf)^ + do Rights:=_RightsMask; +GetEffectiveRights:=(Result=0); +{ return byte + 00h - Success + 98h - Volume Does Not Exist + 9Bh - Bad Directory Handle } +end; + + +{F216/04 [2.15c+]} +Function ModifyMaximumRightsMask(DirHandle:Byte;DirPath:string; + RevokeRightsMask,GrantRightsMask:Word):boolean; +Type Treq=record + len:word; + subFunc:byte; + _DirHandle:Byte; + _GrantRM, + _RevokeRM:Byte; + _DirPath:String; + end; + Trep=record + _EffectiveRightsMask:Byte; + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len:=5+ord(DirPath[0]); + subFunc:=$04; + _DirHandle:=DirHandle; + if DirHandle=0 + then ConvertPathToVolFormat(DirPath); + _GrantRM:=MapV3RightsToV2(GrantRightsMask); + _RevokeRM:=MapV3RightsToV2(RevokeRightsMask); + _DirPath:=DirPath; + F2SystemCall($16,len+2,Sizeof(Trep),result); + end; +{With TPrep(GlobalReplyBuf)^ + do begin + --- nothing is done with the returned value--- + end;} +ModifyMaximumRightsMask:=(result=0) +{ result codes: 8C No modify privileges; 98 Volume dosn't exist; + 9C Invalid path } +end; + + + +{F217/47 [2.15c+]} +Function ScanBinderyObjectTrusteePaths(TrusteeObjectId:Longint; + VolumeNumber:Byte; + {i/o} Var SequenceNumber:word; + {out} Var AccessMask:Word; + Var Path:string ):boolean; +{ You must be supervisor (-equivalent) or the TrusteeObject itself + to use this function. + Initially, sequencenumber should be set to 0. } +Type Treq=record + len :word; + subFunc:byte; + _VolNbr:Byte; + _SeqNbr:word; {hi-lo} + _ObjId :Longint; {hi-lo} + end; + Trep=record + _NextSeqNbr:Word; {hi-lo} + _ObjId :Longint; {hi-lo} + _AccMask :byte; + _Path :string; + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subFunc:=$47; + _VolNbr:=VolumeNumber; + _SeqNbr:=swap(SequenceNumber); + _ObjId:=Lswap(TrusteeObjectId); + end; +F2SystemCall($17,SizeOf(Treq),Sizeof(Trep),result); +With TPrep(GlobalReplyBuf)^ + do begin + SequenceNumber:=Lswap(_NextSeqNbr); + Accessmask:=_AccMask; {MapV2RightsToV3(_accMask);} + Path:=_Path; + end; +ScanBinderyObjectTrusteePaths:=(result=0) +{ resultcodes: + $96 Server out of memory; $F0 Wildcard not allowed; + $F1 Invalid bindery security; $FC No such object; + $FE Server bindery locked; $FF Bindery failure } +end; + +{F216/26 [3.0+]} +Function ScanEntryForTrustees(DirHandle:Byte;DirPath:String; + {i/o} Var SequenceNumber:Byte; + {out} Var TrusteeInfo: TtrusteeInformation):boolean; +{ Set SequenceNumber to 0 initially, + iterate until error $9C (no more trustees) is returned } +{ see GETTR in the XFILE archive for an example } +Type Treq=record + len:word; + subFunc:byte; + _DirHandle:Byte; + _SeqNbr:Byte; + _DirPath:String; + end; + Trep=record + _Info:TtrusteeInformation; + end; + TPreq=^Treq; + TPrep=^Trep; +Var t:Byte; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len:=4+ord(DirPath[0]); + subFunc:=$26; + _DirHandle:=DirHandle; + if DirHandle=0 + then ConvertPathToVolFormat(DirPath); + _SeqNbr:=SequenceNumber; + _DirPath:=DirPath;UpString(_DirPath); + F2SystemCall($16,len+2,Sizeof(Trep),result); + end; +With TPrep(GlobalReplyBuf)^ + do begin + inc(SequenceNumber); + TrusteeInfo.NumberOfTrustees:=_Info.NumberOfTrustees; + for t:=1 to 20 + do begin + TrusteeInfo.TrusteeId[t]:=Lswap(_Info.TrusteeId[t]); + TrusteeInfo.TrusteeRights[t]:=_Info.TrusteeRights[t]; + end; + end; +ScanEntryForTrustees:=(result=0) +{ resultcodes: + $9C No more trustees } +end; + + + + +{F2 [2.15c+] +Function ( ):boolean; +Type Treq=record + len:word; + subFunc:byte; + + end; + Trep=record + + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subFunc:=$ + + end; +F2SystemCall($ ,SizeOf(Treq),Sizeof(Trep),result); +With TPrep(GlobalReplyBuf)^ + do begin + + end; + :=(result=0) +end; } + +end. \ No newline at end of file diff --git a/NWTP/NWINTR.PAS b/NWTP/NWINTR.PAS new file mode 100644 index 0000000..8a5b037 --- /dev/null +++ b/NWTP/NWINTR.PAS @@ -0,0 +1,761 @@ +UNIT NWintr; + +{ DPMI Protected mode calls: Hubert Plattfaut of 2:2447/203.4 + Windows Protected Mode calls: + -Based on EZDPMI by Julian M. Bucknall [1993: 100116.1572@Compuserve.Com] + -Based on the NetCalls and WinDPMI units by Siebrand Dijkstra [1995: 2:512/250.595] + -Corrections by Berend de Boer [1995: berend@beard.nest.nl or 2:281/527.23] + + NwTP Version 0.6, 950301, Copyright 1993,1995 R. Spronk +} + +INTERFACE + +{$B-,F+,O-,R-,S-,X+} + +{$DEFINE ProtMode} +{$IFDEF MSDOS} +{$DEFINE RealMode} +{$UNDEF ProtMode} +{$ENDIF} + +uses +{$IFDEF RealMode} Dos +{$ENDIF} +{$IFDEF DPMI} Dos,WinApi { we need the GlobalDosAlloc-Function} +{$ENDIF} +{$IFDEF WINDOWS} WinTypes,WinDOS,WinProcs +{$ENDIF}; + +CONST VLM_ID_UNKNOWN = $0000; { non-VLM application } + VLM_ID_VLM = $0001; + VLM_ID_CONN = $0010; + VLM_ID_TRAN = $0020; + VLM_ID_IPX = $0021; + VLM_ID_TCP = $0022; + VLM_ID_NWP = $0030; + VLM_ID_BIND = $0031; + VLM_ID_NDS = $0032; + VLM_ID_PNW = $0033; + VLM_ID_RSA = $0034; + VLM_ID_REDIR = $0040; + VLM_ID_FIO = $0041; + VLM_ID_PRINT = $0042; + VLM_ID_GENR = $0043; + VLM_ID_NETX = $0050; + VLM_ID_AUTO = $0060; + VLM_ID_SECURITY = $0061; + VLM_ID_NMR = $0100; + VLM_ID_DRVPRN = $09F2; + VLM_ID_SAA = $09F5; { SAA Client API for NetWare } + VLM_ID_IPXMIB = $09F6; + VLM_ID_PNWMIB = $09F7; + VLM_ID_PNTRAP = $09F8; + VLM_ID_MIB2PROT = $09F9; + VLM_ID_MIB2IF = $09FA; + VLM_ID_NVT = $09FB; + VLM_ID_TRAP = $09FC; + VLM_ID_REG = $09FD; + VLM_ID_ASN1 = $09FE; + VLM_ID_SNMP = $09FF; + +Type +{$ifdef ProtMode} + + TTregisters= Record {This is the data-structure for the} + Case Byte Of {real-mode-interrupts in DPMI-mode} + 0: {32 bit registers} + (EDI,ESI,EBP,Reserved,EBX,EDX, + ECX,EAX:LongInt); + 1: {16 bit registers} + (DI,DIHigh,SI,SIHigh, + BP,BPHigh,ReservedLow,ReservedHigh, + BX,BXHigh,DX,DXHigh, + CX,CXHigh,AX,AXHigh, + Flags,ES,DS,FS,GS,IP, + CS,SP,SS:Word); + 2: {8 bit registers} + (DILowLow,DILowHigh,DIHighLow,DIHighHigh, + SILowLow,SILowHigh,SIHighLow,SIHighHigh, + BPLowLow,BPLowHigh,BPHighLow,BPHighHigh, + ReservedLowLow,ReservedLowHigh,ReservedHighLow,ReservedHighHigh, + BL,BH,BXHighLow,BXHighHigh, + DL,DH,DXHighLow,DXHighHigh, + CL,CH,CXHighLow,CXHighHigh, + AL,AH,AXHighLow,AXHighHigh:Byte) + End; + +{$else} {RealMode} + + TTregisters= Record + case Integer of + 0: (AX, BX, CX, DX, BP, SI, DI, DS, ES, Flags: Word); + 1: (AL, AH, BL, BH, CL, CH, DL, DH: Byte); + end; +{$endif} + + TPtrRec=record Ofs,Seg:word end; + + TintrBuffer=array[1..576] of byte; + TPintrBuffer=^TintrBuffer; + + TVLMheader=record + unknown1 :array[1..4] of byte; + ptr1ofs,ptr1seg, { pointers to 'procedures' } + ptr2ofs,ptr2seg, + ptr3ofs,ptr3seg, + ptr4ofs,ptr4seg :word; + unknown2 :array[1..4] of byte; { 00 00 00 00 } + HeaderLen :byte; { 1.11-> 4E; 1.20-> 4E} + MultiplexIDstring :array[1..3] of char; { 56 4C 4D 'VLM' } + unknown3 :array[1..4] of byte; { 01 00 80 00 } + + TransientSwitchCount :word; + CallCount :word; + ControlBlockOfs :word; { in same segment as this header } + CurrentVLMID :word; + MemoryType :byte; { 04 = XMS } + ModulesLoaded :byte; + BlockId :word; + TransientBlock :word; + GlobalSegment :word; + AsyncQueue :array[1..3] of record { head, tail, s } + pqofs,pqseg:word; + end; + BusyQueue :array[1..3] of record { head, tail, s } + pqofs,pqseg:word; + end; + ReEntranceLevel :word; + FullMapCount :word; + unknown5 :word; { 00 00 } + end; + + TVLMcontrolBlockEntry=record + Flag :word; + ID :word; + Func :word; + Maps :word; + TimesCalled :word; + unknown1 :word; { SSeg ? } + TransientSeg,GlobalSeg :word; + AddressLow,AddressHi :word; + TsegSize,GSegSize,SSegSize:word; { in 16 byte paragraphs } + VLMname :array[1..9] of char; + { null terminated string } + end; + +Var GlobalReqBuf,GlobalReplyBuf:TPintrBuffer; + + { real-mode only, DPMI: all flags are set to false } + VLM_EXE_loaded :Boolean; + NETX_VLM_loaded:Boolean; { if true, then VLM_EXE_loaded must also be true. } + NETX_EXE_loaded:Boolean; + +Function RealModeIntr(intNo:byte;Var regs:TTregisters):boolean; +Procedure F2SystemCall(subf:byte;req_size,rep_size:word;Var result:word); +Procedure nwMsDos(VAR R:ttregisters); +Function InRealMode:Boolean; + +Function MapRealmodeSegment(RSeg:Word):Word; +Function nwPtr(s,o:word):Pointer; +Procedure GetGlobalBufferAddress(VAR Sreq,Oreq,Srep,Orep:Word); + +{$IFDEF RealMode} +Function GetVLMheader(Var VLMheader:TVLMheader):Boolean; +Function GetVLMControlBlock(Entry:Byte; + Var ControlBlock:TVLMControlBlockEntry):Boolean; + { entry: 0 .. VLMheader.ModulesLoaded } +{$ENDIF} + +IMPLEMENTATION {===========================================================} + +Var GlobalRegisters:TTregisters; { all Modes ! } + + VLMCall:Procedure; + +{$IFDEF RealMode} + +Var VLMtransientSeg:word; + +{ ---------- Real mode procedures ------------------------------------} + +{$F+} + +Var RequesterProc:Procedure(Var regs:Registers); + { VLMCall:Procedure; } + +Procedure VlmSystemCall(Var regs:registers); assembler; +asm +push ds + + { check if VLMCall known. If not, return error $FF in fake AL } +xor ah,ah +mov al,$FF +les di,VLMCall +mov bx,es +cmp bx,$0000 +je @1 + { move fake regs registers to 'real' registers } + { AX, CX, DX, DS, SI, DI, ES only. } +les di,regs +mov ax,es:[di+16] +push ax { push new es } +mov ax,es:[di+12] +push ax { push new di } +mov ds,es:[di+14] +mov ax,es:[di] +mov cx,es:[di+4] +mov dx,es:[di+6] +mov si,es:[di+10] +pop di +pop es + { farr call to VLM handler } +push bp +CALL VLMCall +pop bp +@1: { move 'real' registers to fake regs registers } + +{push es +push di} +les di,regs +mov es:[di],ax +{mov es:[di+4],cx +mov es:[di+6],dx +mov es:[di+10],si +pop ax ax:= 'di' +mov es:[di+12],ax +pop ax ax:= 'es' +mov es:[di+16],ax } + +pop ds +end; + +Procedure VLMcheck; +CONST DOS_MULTIPLEX =$2F; +Var regs:registers; + ccode:byte; + Function getBinderyAccessLevel:boolean; { to be replaced by a non-bindery call } + Type Treq=record + len :word; + subF :byte; + end; + Trep=record + accLeveL:byte; + _objId:longInt; + fill:array[1..20] of byte; + end; + TPreq=^Treq; + TPrep=^Trep; + Var result:word; + BEGIN + With TPreq(GlobalReqBuf)^ + do begin + subF:=$46; + len:=sizeOf(Treq)-2; + end; + F2SystemCall($17,sizeOf(Treq),sizeOf(Trep),result); + GetBinderyAccessLevel:=(result=0); + end; + +Var phdr:^TVLMHeader; + pVLMcbl:^TVLMcontrolBlockEntry; + t:word; + +begin +VLM_EXE_Loaded:=false; +Regs.AX:=$7A20; +Regs.BX:=$0000; +Regs.CX:=$0000; +Intr($2F,Regs); +if regs.AX=$0000 + then begin + { OK. AX=0000. All seems well. But is it really the 2F VLM handler? } + phdr:=ptr(regs.es,$0000); + VLM_EXE_Loaded:=(phdr^.MultiplexIdString[1]='V') + and (phdr^.MultiplexIdString[2]='L') + and (phdr^.MultiplexIdString[3]='M'); + + IF VLM_EXE_Loaded + then begin + NETX_EXE_loaded:=False; + + { Determine whether netx.vlm is loaded } + NETX_VLM_Loaded:=False; + t:=0; + While t$0000 + then begin + p:=ptr(VLMtransientSeg,$0000); + move(p^,VLMheader,SizeOf(TVLMHeader)); + end; +GetVLMHeader:=(VLMtransientSeg<>$0000); +end; + +Function GetVLMControlBlock(Entry:Byte; + Var ControlBlock:TVLMControlBlockEntry):Boolean; + { entry: 0 .. VLMheader.ModulesLoaded } +Var ph:^TVLMHeader; + pcb:^TVLMControlBlockEntry; +begin +if VLMtransientSeg<>$0000 + then begin + ph:=ptr(VLMtransientSeg,$0000); + pcb:=ptr(VLMtransientSeg,ph^.ControlBlockOfs+entry*SizeOf(TVLMControlBlockEntry)); + move(pcb^,ControlBlock,SizeOf(TVLMControlBlockEntry)); + end; +GetVLMControlBlock:=(VLMtransientSeg<>$0000); +end; + +Function nwPtr(s,o:word):Pointer; +begin +nwPtr:=Ptr(s,o); +end; + +Function MapRealmodeSegment(RSeg:Word):Word; +begin +MapRealmodeSegment:=RSeg; +end; + +{$ENDIF} {------------- end of real-mode procedures -------------------} + +{$IFDEF ProtMode} + +Type pRealSegItem=^tRealSegItem; + tRealSegItem=record {structure to store information} + Seg:word; {about allocated selectors} + Sel:Word; + prev,next:pRealSegItem; + end; + {we need to allocate selectors which map real-mode segments.} + {all these selectors are stored in an dynamic list} + {and are cleand up them at then end of the program} + +Var GlobalRealReqSeg, + GlobalRealReplySeg:Word; + SelectorList:pRealSegItem; + +Function RealModeIntr (IntNo:Byte;VAR Regs:ttregisters):Boolean;Assembler; +{Simulate a call to the spectified real mode interrupt. The registers passed + to the real mode code are held in RealModeRegisters. This structure contains + the register content upon termination of the real mode ISR. + Returns False if there was an error.} + + ASM + push di + push es + + mov bh,00 {For DOSX to reset the int controller and A20 line. Windows ingores it.} + mov bl,IntNo {Tell DPMI which interrupt to simulate} + xor cx,cx {0 bytes to copy to real mode stack} + les di,Regs {Get the real mode structure} + mov word ptr es:[di+$c],0 {reserved to 0} + mov word ptr es:[di+$c+2],0 + mov word ptr es:[di+$26],0 {fs to 0} + mov word ptr es:[di+$28],0 {gs to 0} + mov word ptr es:[di+$2e],0 {sp to 0} + mov word ptr es:[di+$30],0 {ss to 0} + + mov ax,$0300 {Function 0300h is simulate real mode interrupt} + int 31h + + jc @Error {The carry flag was set, so there was an error} + mov ax,True {Return no error} + jmp @AllDone + + @Error: + mov ax,False {Return false indicating an error} + + @AllDone: + pop es + pop di + End; + +Procedure F2SystemCall(subf:byte;req_size,rep_size:word;Var result:word); +begin +With GlobalRegisters + do begin + CX := Req_size; + DX := rep_size; + AH := $f2; + AL := subf; + DS := GlobalRealReqSeg; {Use then REAL-MODE segments} + ES := GlobalRealReplySeg; {of the global buffers} + DI := 0; {OFFSET always 0 for} + SI := 0; {'GlobalDosAlloc'ated memory} + if not RealModeIntr($21,GlobalRegisters) + then RUNERROR(217); + {DPMI-ERRORS, maybe we should stop the system with the new Errorcode 217} + Result:=al; + end; +end; + +Procedure nwMsDos(VAR R:ttregisters); +begin +if not RealModeIntr($21,R) + then RUNERROR(217); + {DPMI-ERRORS, maybe we should stop then system with the new Errorcode 217} +end; + +Procedure GetGlobalBufferAddress(VAR Sreq,Oreq,Srep,Orep:Word); +begin +Sreq := GlobalRealReqSeg; {Use the REAL-MODE segments} +Srep := GlobalRealReplySeg; {of the global buffers} +Oreq := 0; {OFFSET always 0 for} +Orep := 0; {'GlobalDosAlloc'ated memory} +end; + +{----- Some low-level functions for DPMI -----------} +TYPE os = record + o, s : Word; + end; {for typecasts} + LDTStr = record {Structure of LDT-Elements} + limit : Word; + base : Word; + data : Array[0..1] of Word; + end; + +Procedure Halt218; {runError 218: low-level DPMI-Errors} +begin +RunError(218); +end; + +{DMPI-Function 0: Allocate LDT Descriptor} +function AllocLDTD(var NEWD : Word) : Word; Assembler; +asm + xor ax,ax + mov cx,1 {only 1 descriptor needed} + int 31h {Call DPMI} + jnc @@ok + Call Halt218 {Error on carry} +@@ok: + les di,NEWD {save descriptor to VAR NEWD} + mov es:[di],ax + xor ax,ax +end; + +{DMPI-Function 1: Free LDT Descriptor} +function FreeLDTD(D : Word) : Word; Assembler; +asm + mov ax,0001h + mov bx,D + int 31h + jc @@Ex {carry: return Error in ax} + xor ax,ax +@@Ex: +end; + +{DMPI-Function 7: Set Segment Base Address} +function SetSBA(S: Word; BA: LongInt) : Word; Assembler; +asm + mov ax,0007h + mov bx,S + mov cx,word ptr BA+2 + mov dx,word ptr BA + int 31h + jc @@Ex {carry: return Error in ax} + xor ax,ax +@@Ex: +end; + +{DMPI-Function 8: Set Segment Limit} +function SetSL(S: Word; L: LongInt) : Word; Assembler; +asm + mov ax,0008h + mov bx,S + mov dx,word ptr L + mov cx,word ptr L+2 + int 31h + jc @@Ex {carry: return Error in ax} + xor ax,ax +@@Ex: +end; + +{DMPI-Function 9: Set Descriptor Access Rights} +function SetDAS(S: Word; R: Word) : Word; Assembler; +asm + mov ax,0009h + mov bx,S + mov cx,R + int 31h + jc @@Ex {carry: return Error in ax} + xor ax,ax +@@Ex: +end; + +{DMPI-Function 11: Get Descriptor} +function GetD(S: Word; var D : LDTStr) : Word; Assembler; +asm + mov ax,000Bh + mov bx,S + les di,D + int 31h + jc @@Ex {carry: return Error in ax} + xor ax,ax +@@Ex: +end; + + +{Set then Length of the Descriptor-Segment} +function SetLimit(Sele: Word; L: LongInt) : Word; +var St,R: Word; + Des : LDTStr; +begin +St:= GetD(Sele, Des); {get the Descriptor-Entry from LDT} +if St <> 0 + then begin + SetLimit:= St; {not in LDT, return Error} + Exit; + end; +with Des + do R := (Data[0] shr 8) or ((Data[1] and $00F0) shl 8); + {form then rights for the DPMI-9-Call, register cl} +if L > $FFFFF + then begin {> 1MB: Page aligned} + if L and $FFF <> $FFF + then begin {Limit=Length-1!} + SetLimit := $8021; {return Error: not page aligned} + Exit; + end; + R:= R or $8000; {set Page granularity} + end + else R:= R and $7FFF; {set Byte granularity} +St := SetSL(Sele, 0); {fist set limit to 0} +if St = 0 then St := SetDAS(Sele, R); {ok, set the new rights} +if St = 0 then St:= SetSL(Sele, L); {ok, set then limit} +SetLimit := St; {return errorcode} +end; + + +{get a Selector for a part of then real-mode memory} +function RealMemSel(RealP : Pointer; Limit : LongInt; var Sele : Word) : Word; + function NP(P : Pointer) : LongInt; + VAR TC:OS absolute P; + begin + NP := (LongInt(TC.S) shl 4)+LongInt(TC.O); + end; +var St : Word; +begin +St := AllocLDTD(Sele); {get a new Selector} +if St = 0 + then begin + St := SetSBA(Sele, NP(RealP)); {set base addresse to the linear} + if St = 0 + then begin {address of the Real-Segment} + St := SetLimit(Sele, Limit); {set the selector-limit} + if St <> 0 + then if FreeLDTD(Sele)<>0 then; {on error: free selector} + end + else if FreeLDTD(Sele)<>0 then; {on error: free selector} + end; +RealMemSel := St; {return errorcode} +end; + +{check if the required selector is already allocated} +Function InSelectorList(S:Word):pRealSegItem; +VAR li:pRealSegItem; +begin +li:=SelectorList; +while li<>NIL + do begin + if li^.Seg=S + then begin + InSelectorList:=Li; + exit; + end; + li:=li^.Next; + end; +InSelectorList:=NIL; +end; + +{insert a new SelectorItem at start of the list} +Procedure AddToSelectorlist(Segment,Selector:Word); +VAR li:pRealSegItem; +begin +new(li); +with li^ + do begin + Seg:=segment; + Sel:=Selector; + next:=Selectorlist; + prev:=NIL; + end; +Selectorlist^.prev:=li; +Selectorlist:=li; +end; + +{clean up} +Procedure FreeSelectorList; +VAR li:pRealSegItem; +begin +while Selectorlist<>NIL + do begin + li:=selectorlist; + selectorlist:=li^.next; + if li^.sel<>0 + then FreeLDTD(li^.Sel); + dispose(li); + end; +end; + +Function MapRealmodeSegment(RSeg:Word):Word; +VAR sel:Word; + li:pRealSegItem; +begin +li:=InSelectorList(RSeg); +if li=NIL + then begin + if RealMemSel(Ptr(RSeg,0),$ffff,Sel)<>0 + then RUNERROR(217); {something's wrong: Errorcode 217} + MapRealModeSegment:=Sel; + AddToSelectorList(Rseg,Sel); + end + else MapRealModeSegment:=li^.Sel; +end; + + +Function nwPtr(s,o:word):Pointer; +begin + nwPtr:=Ptr(MapRealModeSegment(s),o); +end; + + +{$ENDIF} {----------------- end of protected mode procedures -------------} + +Var OldExitProc:pointer; + +Function InRealMode:Boolean; +begin +{$IFDEF Windows} +InRealMode:=(GetWinFlags and wf_PMode)=0; +{$ELSE} + {$IFDEF ProtMode} + InRealMode:=False; + {$ELSE} + InRealMode:=True; + {$ENDIF} +{$ENDIF} +end; + + +{$F+} +Procedure IntrExit; +begin +ExitProc:=OldExitProc; +{$IFDEF ProtMode} +if GlobalDosFree(Seg(GlobalReqBuf^))<>0 then; {ignore Errors} +if GlobalDosFree(Seg(GlobalReplyBuf^))<>0 then; +FreeSelectorList; +{$ELSE} {RealMode} +FreeMem(GlobalReqBuf,SizeOf(TintrBuffer)); +Freemem(GlobalReplyBuf,Sizeof(TintrBuffer)); +{$ENDIF} +end; +{$F-} + + +{$IFDEF ProtMode} +VAR w1:Longint absolute GlobalRegisters; +{ we only need w1 during the initialisation, so we use the static + var GlobalRegisters to save 4 bytes of memory :-) } +{$ENDIF} + +begin +VLM_EXE_Loaded:=false; +NETX_EXE_loaded:=false; +NETX_VLM_loaded:=false; +{$IFDEF ProtMode} +new(SelectorList); +fillchar(Selectorlist^,Sizeof(Selectorlist^),0); +w1:=GlobalDosAlloc(Sizeof(tIntrBuffer)); {alloc REQ-Buffer} +if w1=0 + then runerror(217); {DPMI-ERROR, no free Memory} +GlobalReqBuf:=Ptr(loWord(w1),0); {buffer-address for protected Mode} +GlobalRealReqSeg:=hiWord(w1); {REAL-Mode-Segment of the buffer-address} +w1:=GlobalDosAlloc(Sizeof(tIntrBuffer)); {alloc REPLY-Buffer} +if w1=0 + then runerror(217); +GlobalReplyBuf:=Ptr(loWord(w1),0); +GlobalRealReplySeg:=hiWord(w1); +{$else} {RealMode} +new(GlobalReqBuf); +if GlobalReqBuf=NIL + then RunError(203); {where has all the memory gone?? /Heap-Overflow} +new(GlobalReplyBuf); +if GlobalReplyBuf=NIL + then RunError(203); +VLMtransientSeg:=$0000; +VLMcheck; +{$endif} +OldExitProc:=ExitProc; +ExitProc:=@IntrExit; +end. + + diff --git a/NWTP/NWIPX.PAS b/NWTP/NWIPX.PAS new file mode 100644 index 0000000..8482ee5 --- /dev/null +++ b/NWTP/NWIPX.PAS @@ -0,0 +1,606 @@ +{$B-,V-,X+} +UNIT nwIPX; + +{$DEFINE ProtMode} +{$IFDEF MSDOS} {$UNDEF ProtMode} {$DEFINE RealMode} {$ENDIF} +{$IFDEF ProtMode} sorry, protected mode not supported (yet) {$ENDIF} + +{ nwIPX unit as of 950301 / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +INTERFACE + +Uses Dos,nwMisc; + +{ Primary IPX calls: Subf: Comments: + + IPXCancelEvent 6 AES +* IPXCloseSocket 1 (1) + IPXDisconnectFromTarget B (1) +* IPXGetInterNetworkaddress 9 +* IPXGetIntervalMarker 8 +* IPXGetLocalTarget 2 +- IPXGetPacketSize D (IPX internal use only) +* IPXInitialize INT 2F +- IPXInitializeNetworkAddress C (IPX internal use only) +* IPXListenForPacket 4 +* IPXOpenSocket 0 +* IPXRelinquishControl A +* IPXScheduleIPXEvent 5 AES + IPXScheduleSpecialEvent 7 +* IPXSendPacket 3 +- IPXTerminateSockets E (IPX internal use only) + + Secondary calls: + +* IPXpresent +* IPXsetupSendECB +* IPXsetupListenECB + + Notes: (1) These functions use INT 21 and are not to be called from + within an ESR. +} + +CONST + LONG_LIVED_SOCKET = TRUE; { IPXopenSocket } + SHORT_LIVED_SOCKET = FALSE; + + {*** PACKET TYPES ***} + + UNKNOWN_PACKET_TYPE =0; { (basic) Unknown IPX packet } + IPX_PACKET_TYPE =0; + RIP_PACKET_TYPE =1; { Routing Information Packet } + ECHO_PACKET_TYPE =2; + ERROR_PACKET_TYPE =3; + PEP_PACKET_TYPE =4; { Packet Exchange Protocol } + SPX_PACKET_TYPE =5; { Sequenced Packet Protocol Packet } + PUP_PACKET_TYPE =12; + DOD_IP_PACKET_TYPE =13; { Internet Protocol packet Type } + NCP_PACKET_TYPE =17; { NetWare Core Protocol } + { Experimental packet types: 20 - 37 } + + {*** SOCKET NUMBERS ***} + + {0001-0BB8 Registered with Xerox } + SKT_XEROX_ROUTING_INFORMATION= $0001; + SKT_ECHO_PROTOCOL = $0002; + SKT_ERROR_HANDLER = $0003; + + {0020-003F Xerox : Experimental } + SKT_NW4_TIME_SYNC_SERVER = $0040; { used by OT_NW4_TIME_SYNC_SERVER } + SKT_FILE_SERVICE = $0451; { see also $8140, used by OT_RSPCX_SERVER } + SKT_SERVICE_ADVERTISING = $0452; { SAP } + SKT_ROUTING_INFORMATION = $0453; { Novell's RIP Socket } + SKT_NETBIOS = $0455; + SKT_DIAGNOSTIC = $0456; + { 0457h ??? (appears to be related to server serial numbers) } + + {0BB9-FFFF Xerox : Dynamically assignable Sockets } + {0BB9-3FFF Novell: } + SKT_NMA_AGENT =$2F90; { used by OT_NMA_AGENT (NMS) } + + {4000-7FFF Novell: Dynamically assignable Sockets } + { Use a socket in this range for your own applications. } + { To avoid conflicts with other programs, you are advised NOT } + { to use sockets numbers where the hi-byte equals the low-byte, } + { C programmers mostly use those to avoid byte-order swapping. } + + { ! See the SKT_XXX file in the XIPX archive for the latest info + on socket numbers... } + + {8000-FFFF Novell: Well known sockets, registered with Novell. } + SKT_EMAIL_CHAT =$8055; { Niche Corp. } + SKT_EMAIL_CHAT_2 =$8056; { Niche Corp. } + SKT_BTRIEVE =$8058; + SKT_BTRIEVE_2 =$8059; + SKT_NW_SQL =$805A; + SKT_NW_SQL_2 =$805B; + SKT_GAMESERVER =$805C; + SKT_GAMESERVER_2 =$805D; + SKT_PRINT_SERVER =$8060; + SKT_DIGITAL_CHAT =$806C; { Digital Inc. } + SKT_NW_ACCESS_SERVER =$806F; + SKT_OXXI_EMAIL_CHAT =$80C3; { Oxxi Inc. } + SKT_PRINT_SERVER_2 =$811E; + SKT_INTEL_EMAIL_CHAT =$845F; { Intel Corp. } + SKT_WINDOWS_EMAIL_CHAT =$9017; + SKT_JOB_SERVER =$9022; + +Var Result:word; { unit errorcode variable } + +Type TipxHeader=Record + checksum :word; { not used, set to $FFFF } + length :word; { total number of bytes } + TransportControl :byte; { used by bridges: low 4 bits= hop count } + packetType :byte; { ignored by IPX, used by higher level + protocols only. $00=unknown packet type} + destination, + source :TinternetWorkAddress; + { if dest.network equals 0; dest + assumed on same network as sender } + { if dest.node =$FFFFFFFFFFFF, packet + will be sent to all nodes. } + end; + { Fields within IPX and SPX are high-low. Byte swapping will be done + by the IPX functions, except network and node addresses. } + + Tfragment=record { address and size of buffer fragment. } + Address:Pointer; + Size:word; + end; + + Tecb=record + Linkaddress :Pointer; { used by IPX itself } + ESRaddress :Pointer; + InUseFlag :Byte; { reset to $00 when request completed } + CompletionCode :Byte; { valid after InUseFlag becomes $00; + completionCode=$00: packet sent/received. } + SocketNumber :word; + IPXworkspace :array[1..4] of byte; + DriverWorkspace :array[1..12] of byte; + Immediateaddress:Tnodeaddress; { 6 bytes } + FragmentCount :word; { must be >0 } + Fragment :array[1..2] of Tfragment; { [1..FragmentCount] } + { The number of fragments is unlimited. + However, most applications use 1 or 2. } + end; + Tpecb=^Tecb; + +{ TAESecb=: +Offset Size Description + 00h DWORD Link + 04h DWORD ESR address + 08h BYTE in use flag (see below) + 09h 5 BYTEs AES workspace } + +Function IpxPresent:boolean; +{ Determines if an IPX driver is loaded. Calls IPXInitialize. } + +Function IPXinitialize:Boolean; +{ Determines if an IPX driver is loaded. } + +{IPX/SPX: 09h} +Function IPXGetInternetworkAddress(Var Address:TinterNetworkAddress):boolean; +{ This call returns the network and node address of the requesting workstation. } +{ The two byte socketnumber must be appended to the end to form a full } +{ 12-Byte network address. The socketnumber will be set to 0000, indicating + that it has to be filled later with a meaningfule number. } + +{IPX/SPX: 00h} +Function IPXOpenSocket(Var socket:word; PermanentSocket:boolean):boolean; +{ When an application wants to send or receive packets on a socket, + it should first open the socket. PermanentSocket should be set to TRUE + if the socket is used by a TSR. This way, the socket will only be + closed when the IPXcloseSocket function is called. Otherwise, set to FALSE. } + +{IPX/SPX: 01h} +Function IPXCloseSocket(socket:word):boolean; +{ Closes the socket. TSRs should close permanent sockets before terminating. } + +{IPX/SPX: 02h} +Function IPXGetLocalTarget(Address:TinternetworkAddress; + Var ImmAddr:TnodeAddress; + Var Ticks:word ):boolean; +{ Returns the nodeaddress (Immediate address) of a bridge/router that + connects the senders' network with the target-network. If the target + lies within the same network as the sender, the returned node address + is the same as the target node-address. } + +{IPX/SPX: 03h} +Function IPXSendPacket(Var Ecb:Tecb):boolean; +{ After calling this function, control is immediately turned back to the + calling process, whilst in the background the IPX driver is trying to + send the packet. To check if the message has been sent, check the + ECB.InUseFlag or use a SendESR. + The ecb must be filled with appropriate values before calling this function, + the socket to send on must be open. } + +{IPX/SPX: 0Fh} +Function IPXInternalSendPacket(Var Ecb:Tecb):boolean; + +{IPX/SPX: 04h} +Function IPXListenForPacket(Var Ecb:Tecb):Boolean; +{ After calling this function, control is immediately turned back to the + calling process. The IPX driver will wait in the background for a packet + to be received. To check if a message has been received, check the + ECB.InUseFlag or use a ListenESR. + The ecb must be filled with appropriate values before calling this function, + the socket to receive on must be open. } + +{IPX/SPX: 0Ah} +Function IPXrelinquishControl:boolean; +{ Temporarily gives away CPU time to bakcground processes. This call + improves efficeincy by informing the IPX driver that the CPU is + available. } + +{IPX/SPX: 08h} +Function IPXgetIntervalMarker(Var ticks:word):boolean; +{ Gets a time marker from IPX. The difference between two known + time-markers can be used to determine if a timeout has occurred. + 1 Tick = 1/18.2 second. } + +{IPX/SPX: 06h} +Function IPXcancelEvent(ECB:Tecb):boolean; +{ AES call: Cancel an event. + When the event is canceled, the ECB.InUseFlag will be set to $00 and the + ECB.CompletionCode to $FC: Event Canceled. } + +{IPX/SPX: 0Bh} +Function IPXdisconnectFromTarget(Address:TinternetworkAddress):boolean; +{ Informs the listening socket at the specified adress that no more + packets will be sent to the listening socket. + This function is not required in your application, it is merely used + to inform some drivers that the connection (if any) has ended. } + +{IPX/SPX: 05h} +Function IPXscheduleIPXevent(ticks:word;Var ECB:Tecb):boolean; +{ AES call: schedule an event. + After calling this function, control is immediately turned back to the + calling process. After waiting the number of ticks specified + (1 tick= 1/18.2 sec.), the IPX driver activates the ECB. + This function should never be called with an ECB that is still in use + by the IPXdriver (i.e. ECB.InUseFlag should be 0 before calling) +} + +{UPX: 0007} +Function IPXscheduleSpecialEvent(ticks:word;Var ECB:Tecb):boolean; + +Procedure IpxSpxSystemCall(Var regs:registers); +{ Provides an entry into the INT A7 interrupt handler; + Valid only if IPXinitialize or IPXinstalled were called previously. } + +{************** Secondary Procedures ***************************************} + +Procedure IPXSetupListenECB(ESRptr:Pointer; ReceiveSocket:word; + BufPtr:Pointer; BufSize:word; + {out:} Var IpxHdr:TipxHeader; Var ecb:Tecb); +{ Clears IPXheader and ECB, sets values of the required fields within + the ecb and IPX header. } + +Procedure IPXsetupSendECB(ESRptr:pointer; SourceSocket:Word; + DestAddr:TinterNetworkAddress; + BufPtr:pointer; BufSize:word; + {out:} Var IpxHdr:TipxHeader; Var ecb:Tecb); +{ Clears IPXheader and ECB, sets values of the required fields within + the ecb and IPX header. } + +IMPLEMENTATION {==============================================================} + +CONST + IPX_MAX_DATA_LENGTH =546; + +Var IpxSpxCall:Procedure; + +Procedure IpxSpxSystemCall(Var regs:registers); assembler; +{ This method of calling IPX/SPX is preferred by Novell. } +{ For what its' worth: this call is 48 bytes longer than the other one.. } +asm + { check if IpxSpxCall known. If not, return error $FF in fake AL } +xor ah,ah +mov al,$FF +les di,IpxSpxCall +mov bx,es +cmp bx,$0000 +je @1 + { move fake regs registers to 'real' registers } + { AX, BX, CX, DX, SI, DI, ES only. } +les di,regs +mov ax,es:[di+16] +push ax { push new es } +mov ax,es:[di+12] +push ax { push new di } +mov ax,es:[di] +mov bx,es:[di+2] +mov cx,es:[di+4] +mov dx,es:[di+6] +mov si,es:[di+10] +pop di +pop es + { farr call to A7 interrup handler } +push bp +CALL IpxSpxCall +pop bp +@1: { move 'real' registers to fake regs registers } +push es +push di +les di,regs +mov es:[di],ax +mov es:[di+2],bx +mov es:[di+4],cx +mov es:[di+6],dx +mov es:[di+10],si +pop ax { ax:= 'di' } +mov es:[di+12],ax +pop ax { ax:= 'es' } +mov es:[di+16],ax +end; + +Function IPXinitialize:Boolean; +CONST DOS_MULTIPLEX =$2F; +Var regs:registers; +begin +Regs.AX:=$7A00; +INTR(DOS_MULTIPLEX,Regs); +if regs.AL<>$FF + then begin + Result:=IPX_NOT_INSTALLED; + IpxInitialize:=false + end + else begin + @IpxSpxCall:=Ptr(Regs.es,Regs.di); + Result:=0; + IpxInitialize:=true; + end; +end; + +Function IpxPresent:boolean; +begin +IpxPresent:=IpxInitialize +end; + +{IPX: 09h} +Function IPXGetInternetworkAddress(Var Address:TinterNetworkAddress):boolean; +{ This call returns the network and node address of the requesting workstation. } +{ The two byte socketnumber must be appended to the end to form a full } +{ 12-Byte network address. } +Var regs:registers; +begin +regs.bx:=$0009; +regs.es:=seg(Address); +regs.si:=ofs(Address); +IpxSpxSystemCall(Regs); +result:=regs.al; +address.socket:=$0000; { unknown, to be set later. } +if result<>$FF + then result:=$00; +IPXGetInternetworkAddress:=(result=$00); +{ possible resultcodes: $00 Successful; $FF IPX not initialized } +end; + +{IPX: 00} +Function IPXOpenSocket(Var socket:word; permanentSocket:boolean):boolean; +Var regs:registers; + reqForSocket:boolean; +begin +regs.bx:=$0000; +if permanentSocket + then regs.al:=$FF + else regs.al:=$00; +regs.dx:=swap(socket); {hi-lo} +reqForSocket:=(socket=$0000); + +IpxSpxSystemCall(Regs); + +result:=regs.al; +if reqForSocket + then socket:=swap(regs.dx); {force lo-hi} +IPXopenSocket:=(result=0); +{ resultcodes: $00 successful; $FE Socket Table Is Full; + $FF socket already open OR IPX not initilazed. } +end; + +{IPX: 01} +Function IPXCloseSocket(socket:word):boolean; +Var regs:registers; +begin +regs.bx:=$01; +regs.dx:=swap(socket); +IpxSpxSystemCall(regs); +result:=regs.al; +if result<>$FF then result:=$00; +IPXCloseSocket:=(result=$00); +{ possible resultcodes: $00 Successful; $FF IPX not initialized } +end; + + +{IPX: 02} +Function IPXGetLocalTarget(Address:TinternetworkAddress; + Var ImmAddr:TnodeAddress; + VAR ticks:Word ):boolean; +{ Ticks = estimated transmission time, in number of ticks (1/18 sec) } +Var reqAddr:TinternetworkAddress; + repNode:TnodeAddress; + Regs :registers; +begin +move(Address,reqAddr,10); +reqAddr.socket:=swap(Address.socket); {hi-lo} +With regs + do begin + bx:=$0002; + es:=seg(reqAddr); si:=ofs(reqAddr); di:=ofs(repNode); + IpxSpxSystemCall(regs); + ticks:=regs.cx; + result:=regs.al; + if result=0 + then move(repNode,ImmAddr,6); + end; +IPXGetLocalTarget:=(result=$00); +{ resultcodes: $00 Successful; $FA No path to destination node found; + $FF IPX not initialized. } +end; + +Function IPXSendPacket(Var Ecb:Tecb):boolean; +{ the ecb must be filled, before calling this function } +{ Right after this call, IPXrelinquishControl should be called Iteratively, + this allows the sending of the IPX packet. } +Var regs:Registers; +begin +regs.bx:=$0003; +regs.es:=seg(ecb); +regs.si:=ofs(ecb); +IpxSpxSystemCall(Regs); +result:=regs.al; +if result<>$FF then result:=$00; +IpxSendPacket:=(result=$00); +{ possible resultcodes: $00 Successful; $FF IPX not initialized } +end; + + +Function IPXInternalSendPacket(Var Ecb:Tecb):boolean; +Var regs:Registers; +begin +regs.bx:=$000F; +regs.es:=seg(ecb); +regs.si:=ofs(ecb); +IpxSpxSystemCall(Regs); +result:=regs.al; +if result<>$FF then result:=$00; +IpxInternalSendPacket:=(result=$00); +{ possible resultcodes: $00 Successful; $FF IPX not initialized } +end; + + +Function IPXListenForPacket(Var Ecb:Tecb):Boolean; +{ socket must be opened, ECB (partly) filled. } +Var regs:Registers; +begin +regs.bx:=$0004; +regs.es:=seg(ecb); +regs.si:=ofs(ecb); +IpxSpxSystemCall(Regs); +result:=regs.al; +if result<>$FF + then result:=$00; +IpxListenForPacket:=(result=$00); +{resultcodes: $00 Successful; + $FF Listening Socket doesn't exist OR IPX not initialized } +end; + +Function IPXrelinquishControl:boolean; +Var regs:Registers; +begin +regs.bx:=$000A; +IpxSpxSystemCall(Regs); +result:=regs.al; +if result<>$FF then result:=$00; +IpxrelinquishControl:=(result=$00); +{resultcodes: $00 Successful; $FF IPX not initialized } +end; + +Function IPXgetIntervalMarker(Var ticks:word):boolean; +Var regs:Registers; +begin +regs.bx:=$0008; +IpxSpxSystemCall(Regs); +ticks:=regs.ax; +result:=$00; +IPXgetIntervalMarker:=True; +end; + +Function IPXcancelEvent(ECB:Tecb):boolean; +Var regs:registers; +begin +regs.bx:=$0006; +regs.es:=seg(ecb); +regs.si:=ofs(ecb); +IpxSpxSystemCall(Regs); +result:=regs.al; +IPXcancelEvent:=(result=0); +{ resultcodes: 00 Successful; F9 ECB cannot be canceled; + FF ECB not in use OR IPX not initialized. } +end; + +Function IPXdisconnectFromTarget(Address:TinternetworkAddress):boolean; +VAR regs:registers; + LocAddr:TinternetworkAddress; +begin +move(Address,LocAddr,10); +LocAddr.socket:=swap(Address.socket); +regs.bx:=$000B; +regs.es:=seg(LocAddr); +regs.si:=ofs(LocAddr); +IpxSpxSystemCall(Regs); +result:=regs.al; +if result<>$FF + then result:=$00; +IPXdisconnectFromTarget:=(result=0); +{resultcodes: $00 Successful; $FF IPX not initialized } +end; + +Function IPXscheduleIPXevent(ticks:word;Var ECB:Tecb):boolean; +Var regs:registers; +begin +regs.bx:=$0005; +regs.ax:=ticks; +regs.es:=seg(ECB); +regs.si:=ofs(ECB); +IpxSpxSystemCall(Regs); +if result<>$FF + then result:=$00; +IPXscheduleIPXevent:=(result=0); +{resulcodes: 00 successful; FF IPX not initialized } +end; + +Function IPXscheduleSpecialEvent(ticks:word;Var ECB:Tecb):boolean; +Var regs:registers; +begin +regs.bx:=$0007; +regs.ax:=ticks; +regs.es:=seg(ECB); +regs.si:=ofs(ECB); +IpxSpxSystemCall(Regs); +if result<>$FF + then result:=$00; +IPXscheduleSpecialEvent:=(result=0); +{resulcodes: 00 successful; FF IPX not initialized } +end; + +{************** Secondary Procedures ***************************************} + +Procedure IPXSetupListenECB(ESRptr:Pointer;ReceiveSocket:word; + BufPtr:Pointer;BufSize:word; + {out:} Var IpxHdr:TipxHeader; Var ecb:Tecb); +{ Clears IPXheader and ECB, sets values of the required fields within + the ecb and IPX header. } +{ ECB: ESR adress field, socket number, fragment count, frag.descriptor fields } +begin +FillChar(ecb,SizeOf(Tecb),#0); +FillChar(ipxHdr,SizeOF(TipxHeader),#0); +WITH ECB + do begin + if ESRptr<>NIL + then ESRaddress:=ESRptr; + Fragmentcount:=2; + socketNumber:=swap(ReceiveSocket); {hi-lo} + + Fragment[1].Address:=@ipxHdr; + Fragment[2].Address:=BufPtr; + Fragment[1].size:=SizeOf(Tipxheader); + Fragment[2].size:=BufSize; + end; +end; + +Procedure IPXsetupSendECB(ESRptr:pointer; SourceSocket:word; + DestAddr:TinterNetworkAddress; + BufPtr:pointer; BufSize:word; + {out:} Var IpxHdr:TipxHeader; Var ecb:Tecb); +{ Clears IPXheader and ECB, sets values of the required fields within + the ecb and IPX header. } +Var ImmAddr:TnodeAddress; + Ticks:word; +begin +fillchar(ipxHdr,SizeOf(TipxHeader),#0); +with ipxhdr + do begin + PacketType:=IPX_PACKET_TYPE; + Move(DestAddr,Destination,10); + destination.socket:=swap(DestAddr.socket); {hi-lo} + end; +IPXGetLocalTarget(DestAddr,ImmAddr,Ticks); +fillchar(ecb,sizeOf(ecb),#0); +With ecb + do begin + if ESRptr<>NIL + then ESRaddress:=ESRptr; + socketNumber:=swap(SourceSocket); {hi-lo} + Move(ImmAddr,ImmediateAddress,6); + FragmentCount:=2; + fragment[1].Address:=@ipxhdr; + fragment[1].size:=SizeOf(TipxHeader); + fragment[2].Address:=BufPtr; + fragment[2].size:=BufSize; + end; +end; + + + +end. diff --git a/NWTP/NWLOCK.PAS b/NWTP/NWLOCK.PAS new file mode 100644 index 0000000..33b6f98 --- /dev/null +++ b/NWTP/NWLOCK.PAS @@ -0,0 +1,663 @@ +{$X+,B-,V-} {essential compiler directives} + +UNIT nwLock; + +{ nwLock unit as of 950301 / NwTP 0.6 API. (c) 1993,1995, R. Spronk + + This unit was based on units by + + a. Scott A. Lewis, 36 Maythorpe Drive, Windsor, CT 06095, U.S.A. + Note: (1987) 76515,135@Compuserve.Com + + b. Erik van Heyningen, Hague Consulting Group, + The Hague, the Netherlands. + Note: (1994) hcg@hacktick.nl } + +{ Function: Interrupt: Notes: + + Physical File locking/unlocking + ------------------------------- + +* LogPhysicalFile EB (6) -> F203 +* LockPhysicalFileSet F204 +* ReleasePhysicalFile EC -> F205 +* ReleasePhysicalFileSet CD -> F206 +* ClearPhysicalFile ED (6) -> F207 +* ClearPhysicalFileSet CF -> F208 + + Logical File Locking + -------------------- + ++ LogLogicalFile (5) ++ LogLogicalFileSet (5) ++ ReleaseLogicalFile (5) ++ ReleaseLogicalFileSet (5) ++ ClearLogicalFile (5) ++ ClearLogicalFileSet (5) + + Logical record locking/unlocking + -------------------------------- + +* LogLogicalRecord D0 -> F209 +* LockLogicalRecordSet D1 -> F20A +* ReleaseLogicalRecord D2 -> F20C +* ReleaseLogicalRecordSet D3 -> F20D +* ClearLogicalRecord D4 -> F20B +* ClearLogicalRecordSet D5 -> F20E + + GetLogicalRecordInformation F217/F0 (3) + GetLogicalRecordsByConnection F217/EF (3) + + Physical record locking/unlocking + --------------------------------- + +. LogPhysicalRecord BC -> F21A +. LockPhysicalRecordSet C2 -> F21B +. ReleasePhysicalRecord BD -> F21C +. ReleasePhysicalRecordSet C3 -> F21D +. ClearPhysicalRecord BE -> F21E +. ClearPhysicalRecordSet C4 -> F21F + + GetPhysRecLocksByConnectionAndFile F217/ED (3) + GetPhysRecLocksByFile F217/EE (3) + +- ControlRecordAccess 5C (DOS) (4) + + + Not Implemented + --------------- + +- GetLockMode C600 (1) +- SetLockMode C601 (1) +- BeginLogicalFileLocking C8 / F201 (2) +- EndLogicalFileLocking C9 / F202 (2) + + Notes: -Semaphores can be found in the nwSema Unit + (1) Obsolete + (2) Not supported by (all) 3.x versions + (3) Supported by NW 3.x and upwards + (4) Generic physical record locking call, DOS 3.1+ + Equivalent to: + I . LockPhysicalRecord (without logging) + II. ReleasePhysicalrecord + (5) Use the equivalent LogicalRecordLocking calls + to emulate LogicalFileLocking. NOTE: remember + that there's only ONE Log. + (6) Includes VLM fix for filenames (GetTrueEntryName + in the nwFile unit is called) + -> F2xx To be rewritten to the F2 interface. +} + +INTERFACE + +Uses nwIntr,nwMisc; + +CONST { Log Resource } + LD_LOG = 0; + LD_LOG_LOCK = 1; { Deny all access to file/record } + LD_LOG_LOCK_RO = 3; { Allow read / deny write (record locking only)} + { Lock Resource } + LD_lOCK = 0; { Deny all access to file/record } + LD_LOCK_RO = 1; { Allow read / deny write (record locking only)} + +Var Result:word; + +{------------------- PHYSICAL FILE LOCKING OPERATIONS -----------------------} + +{F204 [2.15c+]} +FUNCTION LockPhysicalFileSet(TimeoutLimit : Word) : Boolean; +{Lock a set of files that were logged by the LogFile function } + +{CD.. [1.0+]} +FUNCTION ReleasePhysicalFileSet:boolean; +{ Release lock on set of files in logged table, files remain logged } + +{CF [1.0+]} +FUNCTION ClearPhysicalFileSet : Boolean; +{ Unlock and UnLog the entire logged file set } + +{EB.. [1.0+]} +FUNCTION LogPhysicalFile(FileName : String; LockDirective : Byte; TimeoutLimit : Word) : Boolean; +{Log files for later use } + +{EC.. [1.0+]} +FUNCTION ReleasePhysicalFile(FileName : String) : boolean; +{Release file lock, but keep logged in the table } + +{ED.. [1.0+]} +FUNCTION ClearPhysicalFile(FileName : String) : boolean; +{Release a file from the file log table, unlock the file if it is locked } + +{ ------------------- LOGICAL RECORD LOCKING OPERATIONS --------------------} + +{D0 [1.0+]} +FUNCTION LogLogicalRecord(Name:string; LockDirective:Byte; Timeout: Word) : Boolean; +{Add a record to the lockable logical record table } + +{D1.. [1.0+]} +FUNCTION LockLogicalRecordSet(LockDirective:Byte; TimeoutLimit : Word) : Boolean; +{Lock all logged records } + +{D2.. [1.0+]} +FUNCTION ReleaseLogicalRecord(Name : String) : Boolean; +{Unlock a record, keep record in logtable } + +{D3.. [1.0+]} +FUNCTION ReleaseLogicalRecordSet : Boolean; +{Unlock all locked records, keep records logged } + +{D4.. [1.0+]} +FUNCTION ClearLogicalRecord(Name : String) : Boolean; +{Unlock and UnLog a record } + +{D5.. [1.0+]} +FUNCTION ClearLogicalRecordSet : Boolean; +{Unlocks and UnLogs all logged records } + +{F217/EF [2.1x+]} +Function GetLogicalRecordLocksByConnection(ConnNbr:word; + {i/o} Var NextRecNbr:word; + Var TaskNbr:word; + Var LockStatus:Byte; + Var LockName:String):Boolean; +{ You need console operator rights to use this function } + + +{----------------------- PHYSICAL RECORD LOCKING OPERATION -----------------} + +{BC.. [1.0+]} +function LogPhysicalRecord(Handle:Word; + LockDirective:Byte; + RecordOffset,RecordLength:Longint; + TimeOutLimit:Word): boolean; +{Add a record to the lockable physical record logtable } + +{BD.. [1.0+]} +function ReleasePhysicalRecord( Handle:Word; RecordOffset,RecordLength:Longint) : boolean; +{Unlock record, keep record logged } + +{BE.. [1.0+]} +function ClearPhysicalRecord(Handle:Word; RecordOffset,RecordLength:Longint): boolean; +{Unlock and Unlog a record } + +{C2.. [1.0+]} +function LockPhysicalRecordSet(LockDirective: byte; TimeoutLimit : Word): boolean; +{Lock all logged records } + +{C3.. [1.0+]} +function ReleasePhysicalRecordSet : boolean; +{Unlock all logged records, keep records logged } + +{C4.. [1.0+]} +function ClearPhysicalRecordSet : boolean; +{Unlocks and unLogs all logged records } + + +IMPLEMENTATION{==============================================================} + +uses nwFile; + +Var regs:TTRegisters; + + +Procedure SetLockMode(mode:Byte); +begin +regs.AH:=$c6; +regs.al:=mode; { 0 or 1 } +RealModeIntr($21,regs); +end; + +(* THE FOLLOWING PROCEDURES ARE FOR LOGGING AND LOCKING/RELEASING FILE SETS *) +(* File locking by set can be very effective in avoiding deadly embrace *) + +{F204 [3.x+]} +FUNCTION LockPhysicalFileSet(TimeoutLimit : Word) : Boolean; +Type Treq=record + _TimeOutLimit:Word; + end; + TPreq=^Treq; +BEGIN +With TPreq(GlobalReqBuf)^ + do begin + _TimeoutLimit:=swap(TimeoutLimit); + end; +F2SystemCall($04,SizeOf(Treq),0,result); +LockPhysicalFileSet:=(result=0); +{ 00 Successful FF Fail FE Timeout } +END; + + +{CD.. [1.0+]} +FUNCTION ReleasePhysicalFileSet:boolean; +{ Release lock on set of files in logged table, files remain logged } +{ These files remain open but cannot be accessed without an error } +{ To reuse them, send another lock file set } +Type Treq=record + end; +BEGIN +WITH Regs + DO BEGIN + AH := $CD; + RealModeIntr($21,Regs); + result:=0; + END; +ReleasePhysicalFileSet:=true; +END; + +{CF [2.0+]} +FUNCTION ClearPhysicalFileSet : Boolean; +{ Unlock and UnLog the entire personal file set (all files are closed) } +BEGIN +WITH Regs + DO BEGIN + AH := $CF; + RealModeIntr($21,Regs); + result:=0; + END; +ClearPhysicalFileSet:=true; +END; + + +{EB.. [2.0+] } +FUNCTION LogPhysicalFile(FileName : String; LockDirective : Byte; TimeoutLimit : Word) : Boolean; +{ This function allows a station to log files for later personal use } +{ After the desired files are logged, function CBh can be used to lock } +{ the entire set of files } +{ !! There is a known problem with lock directive 3 (log and lock shareable) + use 1 instead. } +Type Treq=record + LockDirective:Byte; + TimeOutLimit:Word; + FileName:string[255]; { or Asciiz ? } + end; +Var temp1,temp2:word; + TEname:string; +BEGIN +GetTrueEntryName(FileName,TEname); { also UpCases string } +{ IF this function isn't included and VLMs are used, this call will + *appear* to be successful. No error code is returned, the call is + however unsuccessful. } +WITH Regs + DO BEGIN + AH := $EB; + AL := LockDirective; { 0 = Log Only, 1 Log and Lock } + BP := TimeoutLimit; { in 1/18 seconds, 0 = No wait } + TEname := TEName+#0; { Terminate with a nul for asciiz } + Move(TEname[1],GlobalReqBuf^,ord(TEname[0])); + GetGlobalBufferAddress(DS,DX,temp1,temp2); + { DS:DX real mode pointer to buffer in realmode-range holding Filename } + RealModeIntr($21,Regs); + Result:=AL; + LogPhysicalFile := (Result = 0); + END; +{ FE Timeout FF hardware error } +END; + + +{EC.. [1.0+]} +FUNCTION ReleasePhysicalFile(FileName : String) : boolean; +{ Release file lock, but keep logged in the table } +Var temp1,temp2:word; + TEname:string; +BEGIN +GetTrueEntryName(FileName,TEname); { also UpCases string } +{ IF this function isn't included and VLMs are used, this call will + *appear* to be successful. No error code is returned, the call is + however unsuccessful. } +WITH Regs + DO BEGIN + AH := $EC; + UpString(FileName); + TEName := TEName+#0; { null terminate } + Move(TEname[1],GlobalReqBuf^,ord(TEname[0])); + GetGlobalBufferAddress(DS,DX,temp1,temp2); + { DS:DX real mode pointer to buffer in realmode-range holding Filename } + RealModeIntr($21,Regs); + result:=AL; + ReleasePhysicalFile:=(result=0); + END; +{FF File not found } +END; + +{ED.. [1.0+]} +FUNCTION ClearPhysicalFile(FileName : String) : boolean; +{ Release a file from the file log table, unlock the file if it is locked } +Var temp1,temp2:word; +BEGIN +WITH Regs + DO BEGIN + AH := $ED; + UpString(FileName); + FileName := FileName+#0; { null terminate } + Move(Filename[1],GlobalReqBuf^,ord(Filename[0])); + GetGlobalBufferAddress(DS,DX,temp1,temp2); + { DS:DX real mode pointer to buffer in realmode-range holding Filename } + RealModeIntr($21,Regs); + Result:=AL; + ClearPhysicalFile := (Result = 0); + { 0 means OK FF File not found} + END; +END; + + +(* THE FOLLOWING FUNCTIONS ARE FOR LOGICAL LOCKING OPERATIONS *) +(* Logical locks work only if all software accessing the files use the *) +(* same logical synchronization scheme. Logical locks are much easier *) +(* and faster to implement than physical locks. *) + + +{D0 [1.0+]} +FUNCTION LogLogicalRecord(Name:String; LockDirective:Byte; Timeout: Word) : Boolean; +{ This function will log the specified record string in the record log table } +{ of the requesting station. } +{ Max length of name: 99 chars } +{ LockDirective LD_LOG = 0; + LD_LOG_LOCK = 1; Deny all access to file/record + LD_LOG_LOCK_RO = 3; Allow read / deny write } +{ TimeOut=0 means NoWait } +Var temp1,temp2:word; +BEGIN +WITH Regs + DO BEGIN + AH := $D0; + AL := LockDirective; + UpString(Name); + Move(Name,GlobalReqBuf^,ord(Name[0])+1); + GetGlobalBufferAddress(DS,DX,temp1,temp2); + { DS:DX real mode pointer to buffer in realmode-range holding Filename } + BP := Timeout; { In 1/18th seconds (use only with lock bit set } + RealModeIntr($21,Regs); + Result:=AL; + LogLogicalRecord := (Result=0); + { FFh fail } + { FEh timeout } + { 96h No dynamic memory for file } + END; +END; + + +{D1 [1.0+]} +FUNCTION LockLogicalRecordSet(LockDirective:Byte; TimeoutLimit : Word) : Boolean; +{ Call this to lock all records logged with Log_Logical_Record } +{ LockDirective LD_LOCK = 0; Deny all access to file/record + LD_LOCK_RO = 1; Allow read / deny write } +BEGIN +WITH Regs +DO BEGIN + AH := $D1; + AL := LockDirective; + BP := TimeoutLimit; { In 1/18th seconds, 0 = No wait } + RealModeIntr($21,Regs); + Result:=AL; + LockLogicalRecordSet := (Result=0); + {00 - Success + FF - fail, + FE - timeout } + END; +END; + +{D2.. [1.0+]} +FUNCTION ReleaseLogicalRecord(Name : String) : Boolean; +{ Call this to release a logical record lock without removing the rec } +{ from the table } +Var temp1,temp2:word; +BEGIN +WITH Regs +DO BEGIN + AH := $D2; + UpString(Name); + Move(Name,GlobalReqBuf^,ord(Name[0])+1); + GetGlobalBufferAddress(DS,DX,temp1,temp2); + { DS:DX real mode pointer to buffer in realmode-range holding Filename } + RealModeIntr($21,Regs); + Result:=AL; + ReleaseLogicalRecord := (Result=0); + { FF No record found } + END; +END; + +{D3.. [1.0+]} +FUNCTION ReleaseLogicalRecordSet : Boolean; +{ release all locked logical records, doesn't remove them from the table } +BEGIN +WITH Regs +DO BEGIN + AH := $D3; + RealModeIntr($21,Regs); + Result:=0; + ReleaseLogicalRecordSet := True; + END; +END; + +{D4.. [1.0+]} +FUNCTION ClearLogicalRecord(Name : String) : Boolean; +{ This call unlocks and removes the Logical Record lock from the table } +Var temp1,temp2:word; +BEGIN +WITH Regs +DO BEGIN + AH := $D4; + UpString(Name); + Move(Name,GlobalReqBuf^,ord(Name[0])+1); + GetGlobalBufferAddress(DS,DX,temp1,temp2); + { DS:DX real mode pointer to buffer in realmode-range holding Filename } + RealModeIntr($21,Regs); + Result:=AL; + ClearLogicalRecord := (Result=0); + { FF No record Found } + END; +END; + +{D5.. [1.0+]} +FUNCTION ClearLogicalRecordSet : Boolean; +{ Unlocks and removes from the table all of the stations logical record locks } +BEGIN +WITH Regs +DO BEGIN + AH := $D5; + RealModeIntr($21,Regs); + Result:=0; + ClearLogicalRecordSet := True; + END; +END; + + +(************* THE FOLLOWING ARE PHYSICAL RECORD LOCK CALLS ****************) + +{F:BC..:Lock (& Log) records in a file} +function LogPhysicalRecord(Handle:Word; + LockDirective:Byte; + RecordOffset,RecordLength:Longint; + TimeOutLimit:Word): boolean; +{ Max length of name: 99 chars } +{ LockDirective LD_LOG = 0; + LD_LOG_LOCK = 1; Deny all access to file/record + LD_LOG_LOCK_RO = 3; Allow read / deny write } +{ TimeOut=0 means NoWait; TimeOut not valid if logging only } +{ Handle is the file handle } +begin +with regs +do begin + AH := $BC; + AL := LockDirective; + BX := Handle; + CX := HiLong(RecordOffset); + DX := LowLong(RecordOffset); + BP := TimeOutLimit; + SI := HiLong(RecordLength); + DI := LowLong(RecordLength); + RealModeIntr($21,Regs); + Result:=AL; + LogPhysicalRecord := (Result=0); + { $FF = fail, $FE Timeout, $96 = No dynamic memory } + end; +end; + +{BD.. [1.0+]} +function ReleasePhysicalRecord( Handle:Word; RecordOffset,RecordLength:Longint) : boolean; +{ When a record is released, it is unlocked for use by someone else, but } +{ it remains in the log table } +{ Handle is the file handle, Start_Hi and Start_Lo are the boundaries of } +{ the locked region to be released } +begin +with regs +do begin + AH := $BD; + BX := Handle; + CX := HiLong(RecordOffset); + DX := LowLong(RecordOffset); + SI := HiLong(RecordLength); + DI := LowLong(RecordLength); + RealModeIntr($21,Regs); + Result:=AL; + ReleasePhysicalRecord := (Result=0); + { $FF = No locked record found} + end; +end; + +{BE.. [1.0+]} +function ClearPhysicalRecord(Handle: Word; + RecordOffset,RecordLength:Longint): boolean; +{ Handle is the file handle, Start_Hi and Start_Lo are the boundaries } +{ of the file region to be locked. Clearing a record will unlock it } +{ and remove it from the log table. } +begin +with regs +do begin + AH := $BE; + BX := Handle; + CX := HiLong(RecordOffset); + DX := LowLong(RecordOffset); + SI := HiLong(RecordLength); + DI := LowLong(RecordLength); + RealModeIntr($21,Regs); + Result:=AL; + ClearPhysicalRecord := (Result=0); + { $FF No locked record found } + end; +end; + +{C2.. [1.0+]} +function LockPhysicalRecordSet(LockDirective: byte; TimeoutLimit: Word): boolean; +{ flgs are the lock flags: bit 1 set means shared (non-exclusive) lock } +{ Timeout is in 1/18 seconds, 0 = no wait, -1 means indefinite wait } +{ This function attempts to lock all of the records logged in the station's } +{ log table. } +{ LockDirective LD_LOCK = 0; Deny all access to file/record + LD_LOCK_RO = 1; Allow read / deny write } +{ !! There is known problem when the locking directive equals 1. } +begin +with regs +do begin + AH := $C2; + AL := LockDirective; + BP := TimeOutLimit; + RealModeIntr($21,Regs); + Result:=AL; + LockPhysicalRecordSet := (Result=0); + { $FF = fail, $FE = timeout fail } + end; +end; + +{C3.. [1.0+]} +function ReleasePhysicalRecordSet : boolean; +{ unlocks the entire record log table of the station. records remain in } +{ the log table. } +begin + regs.AH := $C3; + RealModeIntr($21,Regs); + Result:=0; + ReleasePhysicalRecordSet := True; +end; + +{C4.. [1.0+]} +function ClearPhysicalRecordSet : boolean; +{ unlocks and removes from the log table any records logged and locked } +begin + regs.AH := $C4; + RealModeIntr($21,Regs); + Result:=0; + ClearPhysicalRecordSet := True; +end; + + +{F217/EF [2.1x+]} +Function GetLogicalRecordLocksByConnection(ConnNbr:word; + {i/o} Var NextRecNbr:word; + Var TaskNbr:word; + Var LockStatus:Byte; + Var LockName:String):Boolean; +{ You need console operator rights to use this function } +Type Treq=record + len :Word; + subFunc :Byte; + _ConnNbr :word; {lo-hi} { !! Invalid numbers may cause an abend } + _LastRecSeen:word; {lo-hi} + end; + Trep=record + _LastRecSeen :word; {lo-hi} + _NbrOfRecords:word; {lo-hi} + _LockInfo :array[1..508] of byte; + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$EF; + _ConnNbr:=ConnNbr; + _LastRecSeen:=NextRecNbr; + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),SizeOf(Trep),result); +With TPrep(GlobalReplyBuf)^ + do begin + Move(_LastRecSeen,NextRecNbr,2); + + + end; +GetLogicalRecordLocksByConnection:=(result=0) +{ Valid completion codes: + $00 Success + $FF Failure +} +end; + +{$IFDEF xxxx} + +{F217/ [2.1x+]} +Function ( ):Boolean; +Type Treq=record + len:Word; + subFunc:Byte; + + end; + Trep=record + + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$ + + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),SizeOf(Trep),result); +With TPrep(GlobalReplyBuf)^ + do begin + + end; + :=(result=0) +{ Valid completion codes: + $00 Success + $FF Failure. +} +end; + +{$ENDIF} + +Begin +SetLockMode(1); +END. \ No newline at end of file diff --git a/NWTP/NWMESS.PAS b/NWTP/NWMESS.PAS new file mode 100644 index 0000000..c2f50cb --- /dev/null +++ b/NWTP/NWMESS.PAS @@ -0,0 +1,308 @@ +{$X+,B-,V-} {essential compiler directives} + +UNIT nwMess; + +{ nwMess unit as of 950301 / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +INTERFACE + +Uses nwIntr,nwMisc; + +{ Primary functions: Interrupt: comments: + +* BroadcastToConsole (F215/09) +* GetBroadcastMessage (F215/01) +* GetBroadcastMode (DE..(..04)) +* SendBroadcastMessage (F215/00) +* SendConsoleBroadcast (F217/D1) +* SetBroadcastMode (DE..(..0x)), x= 0,1,2,3 + + Secondary Functions: + +* SendMessageToUser + + Not implemented: + +- CheckPipeStatus (F215/08) (1) +- CloseMessagePipe (F215/07) (1) +- DisableStationBroadcast (F215/02) (3) +- EnableStationBroadcast (F215/03) (3) +- GetPersonalMessage (F215/05) (1) +- LogNetworkMessage (F215/0D) (2) +- OpenMessagePipe (F215/06) (1) +- SendPersonalMessage (F215/04) (1) + +Notes: + (1) These calls are NOT supported by Netware 386 versions shipped after + December 1990, because they use pipe mechanisms, which cause a + considerable deal of server overhead. + These functions are not implemented in this unit. + Use IPX/SPX peer-to-peer communication instead (nwIPX,nwSPX,nwPEP). + (2) Network msg file no longer supported by 3.x + (3) Not supported by Netware 3.x. Use SetBroadcastMode instead. +} + +Var result:word; + +{F215/09 [2.15c+]} +Function BroadcastToConsole(message:string):boolean; +{ broadcast a message to the file server console. } + +{F215/01 [2.15c+]} +Function GetBroadcastMessage(var bmessage: string):boolean; +{ Reads a broadcast message strored at server } + +{DE..(..04) [1.x/2.x/ 3.x]} +Function GetBroadcastMode(var bmode:byte):boolean; +{ Returns the message mode. } + +{F215/00 [2.15c+]} +Function SendBroadcastMessage( message:string; + connCount:byte; + connList:TconnectionList; + VAR resultlist:TconnectionList ):boolean; +{ Sends a broadcast message to a number of logical connections. } + +{DE..(..0n) n=0,1,2,3 [1.x/2.x/3.x]} +Function SetBroadcastMode(bmode:byte):boolean; + +{F217/D1 [2.15c+]} +Function SendConsoleBroadcast(message:string; connCount:byte; + connList:TconnectionList ):boolean; +{ Sends a message to a number of connections, as if the message was send + by a console broadcast command. Console oprator privileges required. } + +{--------------------Secondary Functions-------------------------------} + +Procedure SendMessageToUser(UserName,Message:String); +{ sends a message to a (group of) users. + The username may contain wildcards (* and ?). + The message will not be received by stations whose status is CASTOFF.} + + +IMPLEMENTATION {============================================================} + +USES nwConn; + +{DE..(..04) [1.x/2.x/ 3.x]} +Function GetBroadcastMode(var bmode:byte):boolean; +{ Returns the message mode. + + 00 Server Stores : Netware Messages and User Messages, + Shell automaticly displays messages. + 01 Server Stores : Server Messages. (User messages discarded) + Shell automaticly displays messages. + 02 Server stores : Server messages only. + Applications have to use GetBroadCastMessage to see if there is a message. + 03 Server stores : Server messages and User messages. + Applications have to use GetBroadCastMessage to see if there is a message. } +var regs : TTregisters; +begin +regs.ah := $de; +regs.dl := $04; +RealModeIntr($21,regs); +bmode := regs.al; +result:=$00; { the call has no return codes } +GetBroadCastMode:=True; +end; + + +{DE..(..0n) n=0,1,2,3 [1.x/2.x/3.x]} +Function SetBroadcastMode(bmode:byte):boolean; +{ Sets the new message mode. + + possible resultcode: $FF ( illegal broadcastmode supplied or + the broadcastmode after the call is not equal + to the intended broadcast mode ) + + 00 Server Stores : Netware Messages and User Messages, + Shell automaticly displays messages. + 01 Server Stores : Server Messages. (User messages discarded) + Shell automaticly displays messages. + 02 Server stores : Server messages only. + Applications have to use GetBroadCastMessage to see if there is a message. + 03 Server stores : Server messages and User messages. + Applications have to use GetBroadCastMessage to see if there is a message. } +var regs : TTregisters; +begin +if (bmode <4) + then begin + regs.ah := $de; + regs.dl := bmode; + RealModeIntr($21,regs); + if regs.al<>bmode { if confirmation of new mode unequal desired mode } + then result:=$FF + else result:=$00; + end + else result:=$FF; +SetBroadcastMode:=(result=0); +end; + + + +{F215/01 [2.15c+]} +Function GetBroadcastMessage(var bmessage: string):boolean; +{ An application should poll this to see if there is a broadcastmessage + stored (for this workstation) at the default server. + The message mode must be 2 or 3. (No Notification by Shell) + If no message was stored at the server, or the message was empty, + this function will return FALSE and an errorcode of $103. } +Type Treq=record + len :word; + subF :byte; + end; + Trep=record + _message:string[55]; + end; + TPreq=^Treq; + TPrep=^Trep; +BEGIN +With TPreq(GlobalreqBuf)^ + do begin + subF:=$01; + len:=1; + end; +F2SystemCall($15,sizeOf(Treq),sizeOf(Trep),result); +If result=0 + then bmessage:=TPrep(GlobalReplyBuf)^._message; + +if bmessage[0]=#0 then result:=$103; { whups! empty message } + +GetBroadCastMessage:=(result=0); +{ returncodes: + 00 Successful; FC Message Queue Full; + FE I/O failure: Lack of dynamic workspace. + 103 No msgs stored at server. } +end; + + +{F215/00 [2.15c+]} +Function SendBroadcastMessage( message:string; + connCount:byte; + connList:TconnectionList; + VAR resultlist:TconnectionList ):boolean; +{ Sends a broadcast message to a number of logical connections. + The connectionlist is an array[1..connCount] of logical connection numbers, + the result of the broadcast can be found in the resultList parameter. + example: + connCount=5 + connList= [ 4,9,1,5,2 ] + + resultList= [$00, $00, $FC, $FD, $FF] + + possible codes in resultList: + $00: broadcast to this logical connnection was successful. + $FC: message rejected, buffer for this station already contains a message, + $FD: invalid connection number + $FF: The target connection has blocked incoming messages, + or the target connection is not in use. } +Type Treq=record + len :word; + subF :byte; + _connCount :byte; + connLandMessage:array[1..306] of byte; { 250 conn, 56 msg } + end; + Trep=record + connCount:byte; + _ResultList:TconnectionList; + end; + TPreq=^Treq; + TPrep=^Trep; +Var t:byte; +BEGIN +With TPreq(GlobalReqBuf)^ + do begin + subF:=$00; + _connCount:=connCount; + move(connList[1],connLandMessage[1],connCount); + t:=ord(message[0]); if t>55 then t:=55; + move(message[0],connLandMessage[connCount+1],t+1); + len:=3+connCount+t; { 2 bytes + [connList] + len(str) + str[0] } + end; +F2SystemCall($15,sizeOf(Treq),sizeOf(Trep),result); +If result=0 + then with TPrep(GlobalReplyBuf)^ + do resultList:=_resultlist; +SendBroadcastMessage:=(result=0); +end; + + +{F215/09 [2.15c+]} +Function BroadcastToConsole(message:string):boolean; +{ broadcast a message to the file server console. + The message (max 60 chars, in ascii range [$20..$7E]) will be displayed + at the bottom of the console screen. + This function truncates the messagelength to 60 and repaces illegal + characters with a . } +Type Treq=record + len :word; + subF :byte; + _message :string; + end; + TPreq=^Treq; +Var t:byte; +BEGIN +With TPreq(GlobalReqBuf)^ + do begin + subF:=$09; + PstrCopy(_message,message,60); + for t:=1 to 60 + do if (_message[t]<>#$0) and + ((_message[t]<#$20) or (_message[t]>#$7E)) + then _message[t]:='.'; + len:=62; + end; +F2SystemCall($15,sizeOf(Treq),0,result); +BroadcastToConsole:=(result=0); +{ resultcodes: 00 success ; $FC message queue full ; + $FE I/O failure: lack of dynamic workspace } +end; + + +{F217/D1 [2.15c+]} +Function SendConsoleBroadcast(message:string; connCount:byte; + connList:TconnectionList ):boolean; +{Sends a message to a number of connections, as if the message was send + by a console oprator. Console operator privileges required. + If connCount equals 0, then the message is send to all connections. } +Type Treq=record + len :word; + subF :byte; + _ConnCount:byte; + _connAndMess:array[1..306] of byte; + end; + TPreq=^Treq; +Var t:byte; +BEGIN +With TPreq(GlobalReqBuf)^ + do begin + subF:=$D1; + _connCount:=connCount; + Move(connList[1],_connAndMess[1],connCount); + t:=ord(message[0]); if t>55 then t:=55; + {!! to do: strip hi-bit of message.. } + Move(message[0],_connAndMess[connCount+1],t+1); + len:=t+connCount+3; + end; +F2SystemCall($17,sizeOf(Treq),0,result); +SendConsoleBroadcast:=(result=0); +{Resultcodes: $00 success; $C6 No Console Rights} +end; + +{=================== Secondary Functions ===================================} + +Procedure SendMessageToUser(UserName,Message:String); +{ sends a message to a (group of) users. + The username may contain wildcards (* and ?). + The message will not be received by stations whose status is CASTOFF.} +{ calls nwConn.getObjectConnectionNumber and nwMess.SendBroadcastMessage } +Var NbrOfConn:byte; + connList,ResultList:TconnectionList; +begin +IF NwConn.GetObjectConnectionNumbers(UserName,1 {OT_USER},NbrOfConn,connList) + AND (NbrOfConn>0) + then SendBroadcastMessage(Message,NbrOfConn,connList,ResultList); +end; + + +end. {unit nwMess} diff --git a/NWTP/NWMISC.PAS b/NWTP/NWMISC.PAS new file mode 100644 index 0000000..e98715a --- /dev/null +++ b/NWTP/NWMISC.PAS @@ -0,0 +1,961 @@ +{$X+,B-,V-,R-,S-} { essential compiler directives } + +UNIT NWMISC; + +{ nwMisc unit as of 950301 / NwTP 0.6 API. (c) 1993,1995 R.Spronk } +{ Includes a bugfix of the EncryptPassword function by Horst Jelonneck } + +INTERFACE + +uses nwIntr; + +{ Miscellaneous Functions: Comments: + + Diagnostic Functions: + +* IsV3Supported +* GetNWversion + + Novell types comparison functions: + +* IsLowerNetworkAddress +* IsEqualNetworkAddress +* IsLaterNovTime +* IsEqualNovTime + + Password Encryption Functions: + +* EncryptPassword (1) +* EncryptPasswordDifference (1) + + Conversion Functions: + +* UpString (2) +* HexStr +* HexDumpStr +* PStrCopy +* ZStrCopy +* LoNibble +* HiNibble +* Lswap +* HiLong +* LowLong +* MakeLong +* NovTime2String +* DosTime2NovTime +* NovTime2DosTime +* NovPath2DosPath + DosPath2NovPath +. MapV2RightsToV3 +. MapV3RightsToV2 + +Notes: (1)-Encrypt3 and associated tables adapted from a c source + (NVPW.C) by Willem Jan Hengeveld, A.K.A. Itsme@Hacktic.nl + -Source of the encryption routine: LOGON.PAS by Barry Nance, + [1:141/209] BYTE March'93 + (2) Fast upcasestring by Bob Swart. +} + + +Type TnovTime=record + year,month,day,hour,min,sec,DayOfWeek:byte; { 0=sunday } + end; + TconnectionList=array[1..250] of byte; + + TencryptionKey=array[0..7] of byte; + TencrPWdifference=array[0..15] of byte; + + + TnetworkAddress=array[1..4] of byte; { hi-endian } + TnodeAddress =array[1..6] of byte; { Hi-endian } + TinterNetworkAddress=record + net :TnetworkAddress; {hi-lo} + node :Tnodeaddress; {hi-lo} + socket:word; {lo-hi} + end; + +Function IsV3Supported:Boolean; + +Procedure NovTime2String(tim:TnovTime;Var DateStr:string); +{ Puts the time/date information of a NovTime into a string. + output format: 'DOW, dd mmm yyyy hh:mm:ss' DOW= day of the week. } + +Procedure DosTime2NovTime(dt:Longint;Var nt:TnovTime); +{ Converts a compact DOS time record (4 bytes) into a Tnovtime record } + +Procedure NovTime2DosTime(nt:TnovTime;Var dt:Longint); + +Procedure NovPath2DosPath(np:String;Var dp:string); +{ Converts Novell type path into a DOS path of type + Subdir1\Subdir2\.. \subDirN } + +{============================level 0 support functions=======================} + +Procedure UpString(s:string); +{ Converts s to upperstring. Assembler, so it's realy a Var parameter. } + +Function HexStr(dw:LongInt;len:byte):string; +{ Converts dw into a hex-string of length len. } + +Function HexDumpStr(Var dumpVar;len:Byte):String; +{ Converts dumpVar into a hex-string of length len. + Basically the same as HexStr, but accepts variables only. + (Mostly used to dump an array of byte) } + +procedure PStrCopy(Var dest:String;source:String;len:byte); +{ if length(source)>len + then Copy len bytes from source to dest. + else Copy source to dest and fill out with NULLs. + Length(Dest) will allways be set to len. } + +procedure ZStrCopy(dest:String;VAR source;len:byte); +{ 1. Copies len bytes form an array to a pascal type string. } +{ 2. Trailing NULLs are removed from the string. } +{ consequently, the length of dest (dest[0]) will allways be <= len. } +{ -SOURCE is an array of byte: array[ ] of byte; } + +Function IsLowerNetworkAddress(Var a, b): Boolean; +{ Compare two net&node addresses. + a and b should be of type TinternetworkAddress } + +Function IsEqualNetworkAddress(Var a, b): Boolean; +{ Compare two net&node addresses. + a and b should be of type TinternetworkAddress } + +Function IsLaterNovTime(time1,time2:TnovTime):boolean; + +Function IsEqualNovTime(time1,time2:TnovTime):boolean; + +Function MapV2RightsToV3(V2Rights:byte):word; + +Function MapV3RightsToV2(V3Rights:Word):Byte; + +Procedure GetNWversion(Var version:word); +{ determine the version of software installed on the current file server. } +{ see GetFileServerInformation F217/11 for more information } +{ Version: MajorVersion * 100 + MinorVersion; e.g. 311 for 3.11 } + +Procedure EncryptPassword(objId:longint;password:string; + {i/o} Var Ekey:TencryptionKey); +{ called by LoginToFileServer (unit nwConn), + and by VerifyBinderyObjectPassword, ChangeBinderyObjectPassword (nwBindry) } +{ Source of the encryption routine: LOGON.PAS by Barry Nance, [1:141/209] + BYTE March'93 } + + +Procedure EncryptPasswordDifference(objId:Longint; + OldPassword,NewPassword:string; + Var key:TencryptionKey; + Var PWdif:TencrPWdifference; + Var PWdifChecksum:byte + ); + + +Function LoNibble(b:Byte):Byte; +{ Returns the low nibble of the argument (in low nibble position), + with high nibble set to 0000 } +Function HiNibble(b:Byte):Byte; +{ Returns the high nibble of the argument (in low nibble position), + with high nibble set to 0000 } + + +Function Lswap(l:Longint):Longint; +{ swaps bytes in a longInt; ( reverse byte order ) } +Inline( + $5A/ {pop DX ; low word of long } + $58/ {pop AX ; hi word of long } + + $86/$F2/ {xchg dl,dh ; swap bytes } + $86/$E0); {xchg al,ah ; swap bytes } + +function HiLong(Long : LongInt) : Word; +{ This inline directive is similar to Turbo's Hi() function, except } +{ it returns the high word of a LongInt } +Inline( + $5A/ {pop dx ; low word of long } + $58); {pop ax ; hi word of long } + +function LowLong(Long : LongInt) : Word; +{ This inline directive is similar to Turbo's Lo() function, except } +{ it returns the Low word of a LongInt } +Inline( + $5A/ {pop dx ; low word of long } + $58/ {pop ax ; hi word of long } + $89/$D0); {mov ax,dx ; return lo word as func. result in Ax } + +function MakeLong(HiWord,LoWord : Word) : LongInt; +{ Takes hi and lo words and makes a longint } +Inline( + $58/ { pop ax ; pop low word into AX } + $5A); { pop dx ; pop high word into DX } + + +CONST +{** ERRORS DEFINED BY NWxxx UNITS *******} + +{** STANDARD ERRORS AS USED BY NETWARE **} + HARDWARE_FAILURE = 255; + INVALID_INITIAL_SEMAPHORE_VALUE = 255; {nwSema} + INVALID_SEMAPHORE_HANDLE = 255; {nwSema} + BAD_PRINTER_ERROR = 255; + QUEUE_FULL_ERROR = 255; + NO_FILES_FOUND_ERROR = 255; + BAD_RECORD_OFFSET = 255; + PATH_NOT_LOCATABLE = 255; + SOCKET_ALREADY_OPEN = 255; + INVALID_DRIVE_NUMBER = 255; {nwDir} + NO_RECORD_FOUND = 255; + NO_RESPONSE_FROM_SERVER = 255; + REQUEST_NOT_OUTSTANDING = 255; + NO_SUCH_OBJECT_OR_BAD_PASSWORD = 255; + CLOSE_FCB_ERROR = 255; + FILE_EXTENSION_ERROR = 255; + FILE_NAME_ERROR = 255; + IO_BOUND_ERROR = 255; + SPX_IS_INSTALLED = 255; {nwIpx} + SPX_SOCKET_NOT_OPENED = 255; {nwIpx} + EXPLICIT_TRANSACTION_ACTIVE = 255; {nwTTS} + NO_EXPLICIT_TRANSACTION_ACTIVE = 255; {nwTTS} + TRANSACTION_NOT_YET_WRITTEN = 255; {nwTTS} + NO_MORE_MATCHING_FILES = 255; {nwTTS} + BINDERY_FAILURE = 255; + OPEN_FILES = 255; {3.x} + PRINT_JOB_ALREADY_QUEUED = 255; {3.x} + PRINT_JOB_ALREADY_SET = 255; {3.x} + SUPERVISOR_HAS_DISABLED_LOGIN = 254; {nwConn} + TIMEOUT_FAILURE = 254; + BINDERY_LOCKED = 254; {nwBindry} + SERVER_BINDERY_LOCKED = 254; + INVALID_SEMAPHORE_NAME_LENGTH = 254; {nwSema} + PACKET_NOT_DELIVERABLE = 254; + SOCKET_TABLE_FULL = 254; + DIRECTORY_LOCKED = 254; + SPOOL_DIRECTORY_ERROR = 254; + IMPLICIT_TRANSACTION_ACTIVE = 254; {nwTTS} + TRANSACTION_ENDS_RECORD_LOCK = 254; {nwTTS} + IO_FAILURE = 254; {3.x} + UNKNOWN_REQUEST = 253; + INVALID_PACKET_LENGTH = 253; + FIELD_ALREADY_LOCKED = 253; + BAD_STATION_NUMBER = 253; + SPX_MALFORMED_PACKET = 253; + SPX_PACKET_OVERFLOW = 253; + TTS_DISABLED = 253; + NO_SUCH_OBJECT = 252; + UNKNOWN_FILE_SERVER = 252; + INTERNET_PACKET_REQT_CANCELED = 252; + MESSAGE_QUEUE_FULL = 252; {nwMess} + SPX_LISTEN_CANCELED = 252; + NO_SUCH_PROPERTY = 251; + INVALID_PARAMETERS = 251; + {UNKNOWN_REQUEST = 251; ?double see 253} + NO_MORE_SERVER_SLOTS = 250; + TEMP_REMAP_ERROR = 250; + NO_PROPERTY_READ_PRIVILEGE = 249; + NO_FREE_CONNECTION_SLOTS = 249; + NO_PROPERTY_WRITE_PRIVILEGE = 248; + ALREADY_ATTACHED_TO_SERVER = 248; + NOT_ATTACHED_TO_SERVER = 248; + NO_PROPERTY_CREATE_PRIVILEGE = 247; + TARGET_DRIVE_NOT_LOCAL = 247; + NO_PROPERTY_DELETE_PRIVILEGE = 246; + NOT_SAME_LOCAL_DRIVE = 246; + NO_OBJECT_CREATE_PRIVILEGE = 245; + NO_OBJECT_DELETE_PRIVILEGE = 244; + NO_OBJECT_RENAME_PRIVILEGE = 243; + NO_OBJECT_READ_PRIVILEGE = 242; + INVALID_BINDERY_SECURITY = 241; + WILD_CARD_NOT_ALLOWED = 240; + IPX_NOT_INSTALLED = 240; {nwIpx} + INVALID_NAME = 239; + SPX_CONNECTION_TABLE_FULL = 239; + OBJECT_ALREADY_EXISTS = 238; + SPX_INVALID_CONNECTION = 238; + PROPERTY_ALREADY_EXISTS = 237; + SPX_NO_ANSWER_FROM_TARGET = 237; + SPX_CONNECTION_FAILED = 237; + SPX_CONNECTION_TERMINATED = 237; + NO_SUCH_SEGMENT = 236; + SPX_TERMINATED_POORLY = 236; + NOT_GROUP_PROPERTY = 235; + NO_SUCH_MEMBER = 234; + MEMBER_ALREADY_EXISTS = 233; + NOT_ITEM_PROPERTY = 232; + WRITE_PROPERTY_TO_GROUP = 232; + PASSWORD_HAS_EXPIRED = 223; + PASSWORD_HAS_EXPIRED_NO_GRACE = 222; + ACCOUNT_DISABLED = 220; + UNAUTHORIZED_LOGIN_STATION = 219; + MAX_Q_SERVERS = 219; + UNAUTHORIZED_LOGIN_TIME = 218; + Q_HALTED = 218; + LOGIN_DENIED_NO_CONNECTION = 217; + STN_NOT_SERVER = 217; + PASSWORD_TOO_SHORT = 216; + Q_NOT_ACTIVE = 216; + PASSWORD_NOT_UNIQUE = 215; + Q_SERVICING = 215; + NO_JOB_RIGHTS = 214; + NO_Q_JOB = 213; + Q_FULL = 212; + NO_Q_RIGHTS = 211; + NO_Q_SERVER = 210; + NO_QUEUE = 209; + Q_ERROR = 208; + NOT_CONSOLE_OPERATOR = 198; + INTRUDER_DETECTION_LOCK = 197; + ACCOUNT_TOO_MANY_HOLDS = 195; + CREDIT_LIMIT_EXCEEDED = 194; + NO_ACCOUNT_BALANCE = 193; + NO_ACCOUNT_PRIVILEGES = 192; + READ_FILE_WITH_RECORD_LOCKED = 162; + DIRECTORY_IO_ERROR = 161; + DIRECTORY_NOT_EMPTY = 160; + DIRECTORY_ACTIVE = 159; + INVALID_FILENAME = 158; + NO_MORE_DIRECTORY_HANDLES = 157; + NO_MORE_TRUSTEES = 156; + INVALID_PATH = 156; + BAD_DIRECTORY_HANDLE = 155; + RENAMING_ACROSS_VOLUMES = 154; + DIRECTORY_FULL = 153; + VOLUME_DOES_NOT_EXIST = 152; + NO_DISK_SPACE_FOR_SPOOL_FILE = 151; + SERVER_OUT_OF_MEMORY = 150; + OUT_OF_DYNAMIC_WORKSPACE = 150; + FILE_DETACHED = 149; + NO_WRITE_PRIVILEGES = 148; + READ_ONLY = 148; + NO_READ_PRIVILEGES = 147; + NO_FILES_RENAMED_NAME_EXISTS = 146; + SOME_FILES_RENAMED_NAME_EXISTS = 145; + NO_FILES_AFFECTED_READ_ONLY = 144; + SOME_FILES_AFFECTED_READ_ONLY = 143; + NO_FILES_AFFECTED_IN_USE = 142; + SOME_FILES_AFFECTED_IN_USE = 141; + NO_MODIFY_PRIVILEGES = 140; + NO_RENAME_PRIVILEGES = 139; + NO_DELETE_PRIVILEGES = 138; + NO_SEARCH_PRIVILEGES = 137; + INVALID_FILE_HANDLE = 136; + WILD_CARDS_IN_CREATE_FILENAME = 135; + CREATE_FILE_EXISTS_READ_ONLY = 134; + NO_CREATE_DELETE_PRIVILEGES = 133; + NO_CREATE_PRIVILEGES = 132; + IO_ERROR_NETWORK_DISK = 131; + NO_OPEN_PRIVILEGES = 130; + NO_MORE_FILE_HANDLES = 129; + FILE_IN_USE_ERROR = 128; + DOS_LOCK_VIOLATION = 33; + DOS_SHARING_VIOLATION = 32; + DOS_NO_MORE_FILES = 31; + DOS_NOT_SAME_DEVICE = 30; + DOS_ATTEMPT_TO_DEL_CURRENT_DIR = 16; + DOS_INVALID_DRIVE = 15; + DOS_INVALID_DATA = 13; + DOS_INVALID_ACCESS_CODE = 12; + DOS_INVALID_FORMAT = 11; + DOS_INVALID_ENVIRONMENT = 10; + DOS_INVALID_MEMORY_BLOCK_ADDR = 9; + DOS_INSUFFICIENT_MEMORY = 8; + DOS_MEMORY_BLOCKS_DESTROYED = 7; + DOS_INVALID_FILE_HANDLE = 6; + DOS_ACCESS_DENIED = 5; + DOS_TOO_MANY_OPEN_FILES = 4; + DOS_PATH_NOT_FOUND = 3; + DOS_FILE_NOT_FOUND = 2; + TTS_AVAILABLE = 1; + SERVER_IN_USE = 1; + SEMAPHORE_OVERFLOW = 1; + DOS_INVALID_FUNCTION_NUMBER = 1; + TTS_NOT_AVAILABLE = 1; + SERVER_NOT_IN_USE = 1; + +IMPLEMENTATION{=============================================================} + + + +{----------------------- Encryption tables and procedures --------------} + +TYPE + Buf32 = ARRAY [0..31] OF Byte; + Buf16 = ARRAY [0..15] OF Byte; + Buf8 = ARRAY [0..7] OF Byte; + Buf4 = ARRAY [0..3] OF Byte; + + +CONST + EncryptTable : ARRAY [0..255] OF Byte = +($7,$8,$0,$8,$6,$4,$E,$4,$5,$C,$1,$7,$B,$F,$A,$8, + $F,$8,$C,$C,$9,$4,$1,$E,$4,$6,$2,$4,$0,$A,$B,$9, + $2,$F,$B,$1,$D,$2,$1,$9,$5,$E,$7,$0,$0,$2,$6,$6, + $0,$7,$3,$8,$2,$9,$3,$F,$7,$F,$C,$F,$6,$4,$A,$0, + $2,$3,$A,$B,$D,$8,$3,$A,$1,$7,$C,$F,$1,$8,$9,$D, + $9,$1,$9,$4,$E,$4,$C,$5,$5,$C,$8,$B,$2,$3,$9,$E, + $7,$7,$6,$9,$E,$F,$C,$8,$D,$1,$A,$6,$E,$D,$0,$7, + $7,$A,$0,$1,$F,$5,$4,$B,$7,$B,$E,$C,$9,$5,$D,$1, + $B,$D,$1,$3,$5,$D,$E,$6,$3,$0,$B,$B,$F,$3,$6,$4, + $9,$D,$A,$3,$1,$4,$9,$4,$8,$3,$B,$E,$5,$0,$5,$2, + $C,$B,$D,$5,$D,$5,$D,$2,$D,$9,$A,$C,$A,$0,$B,$3, + $5,$3,$6,$9,$5,$1,$E,$E,$0,$E,$8,$2,$D,$2,$2,$0, + $4,$F,$8,$5,$9,$6,$8,$6,$B,$A,$B,$F,$0,$7,$2,$8, + $C,$7,$3,$A,$1,$4,$2,$5,$F,$7,$A,$C,$E,$5,$9,$3, + $E,$7,$1,$2,$E,$1,$F,$4,$A,$6,$C,$6,$F,$4,$3,$0, + $C,$0,$3,$6,$F,$8,$7,$B,$2,$D,$C,$6,$A,$A,$8,$D); + + EncryptKeys : Array[0..31] of byte = +($48,$93,$46,$67,$98,$3D,$E6,$8D,$B7,$10,$7A,$26,$5A,$B9,$B1,$35, + $6B,$0F,$D5,$70,$AE,$FB,$AD,$11,$F4,$47,$DC,$A7,$EC,$CF,$50,$C0); + + EncryptTable1:array [0..7,0..1,0..15] OF byte= { used by encrypt3 } + { 0 1 2 3 4 5 6 7 8 9 a b c d e f } + ((( $F,$8,$5,$7,$C,$2,$E,$9,$0,$1,$6,$D,$3,$4,$B,$A), + ( $2,$C,$E,$6,$F,$0,$1,$8,$D,$3,$A,$4,$9,$B,$5,$7)), + (( $5,$2,$9,$F,$C,$4,$D,$0,$E,$A,$6,$8,$B,$1,$3,$7), + ( $F,$D,$2,$6,$7,$8,$5,$9,$0,$4,$C,$3,$1,$A,$B,$E)), + (( $5,$E,$2,$B,$D,$A,$7,$0,$8,$6,$4,$1,$F,$C,$3,$9), + ( $8,$2,$F,$A,$5,$9,$6,$C,$0,$B,$1,$D,$7,$3,$4,$E)), + (( $E,$8,$0,$9,$4,$B,$2,$7,$C,$3,$A,$5,$D,$1,$6,$F), + ( $1,$4,$8,$A,$D,$B,$7,$E,$5,$F,$3,$9,$0,$2,$6,$C)), + (( $5,$3,$C,$8,$B,$2,$E,$A,$4,$1,$D,$0,$6,$7,$F,$9), + ( $6,$0,$B,$E,$D,$4,$C,$F,$7,$2,$8,$A,$1,$5,$3,$9)), + (( $B,$5,$A,$E,$F,$1,$C,$0,$6,$4,$2,$9,$3,$D,$7,$8), + ( $7,$2,$A,$0,$E,$8,$F,$4,$C,$B,$9,$1,$5,$D,$3,$6)), + (( $7,$4,$F,$9,$5,$1,$C,$B,$0,$3,$8,$E,$2,$A,$6,$D), + ( $9,$4,$8,$0,$A,$3,$1,$C,$5,$F,$7,$2,$B,$E,$6,$D)), + (( $9,$5,$4,$7,$E,$8,$3,$1,$D,$B,$C,$2,$0,$F,$6,$A), + ( $9,$A,$B,$D,$5,$3,$F,$0,$1,$C,$8,$7,$6,$4,$E,$2)) + ); + + EncryptTable3:array[0..15] of byte= { used by encrypt3 } + ( $3,$E,$F,$2,$D,$C,$4,$5,$9,$6,$0,$1,$B,$7,$A,$8 ); + + +PROCEDURE Shuffle(VAR ShuffleKey, buf; buflen : Word; VAR target); +{ UNIT INTERNAL PROCEDURE } +{ id, password[1.. ],length(passw), OUT: buf } + + PROCEDURE Shuffle1(VAR temp : Buf32; VAR target); + VAR _target : Buf16 ABSOLUTE target; + b4 : Word; + b3 : Byte; + d, k, i : Word; + Begin + + {** Step 4: .. } + b4 := 0; + FOR k := 0 TO 1 + DO Begin + FOR i := 0 TO 31 + DO Begin + b3 := Lo( Lo(temp[i] + b4) XOR + Lo(temp[(i + b4) AND 31] - EncryptKeys[i])); + b4 := b4 + b3; + temp[i] := b3; + End; + End; + + {*** Step 5:... } + + FOR i := 0 TO 15 + DO _Target[i] := EncryptTable[temp[i Shl 1]] OR + (EncryptTable[temp[i Shl 1 +1]] Shl 4); + End; + +VAR locShuffleKey : Buf4 ABSOLUTE ShuffleKey; + localBuf : ARRAY [0..127] OF Byte ABSOLUTE buf; + BufBytesUsed : Word; + temp : Buf32; + t, IndexOfBufBytes : Word; +Begin +{ strip trailing NULLs of the to-be-encoded buf, + last element of buf must be a NULL ? } +While (buflen > 0) AND (localBuf[buflen-1] = 0) + DO buflen := buflen - 1; +{ clear output of 1st shuffle } +FillChar(temp, SizeOf(temp), #0); + +{*** 1ST Step: XOR folding of first (32*(buflen DIV 32)) bytes. } + +{ temp= buf[0..31] XOR buf[32..63] XOR buf[64..95] XOR etc.. } + +{ IndexOfBufBytes is a multiple of 32, length password= IndexOfBufBytes + buflen } +{ Temp varuable filled with XOR folding of the first IndexOfBufBytes bytes of the PW. } +IndexOfBufBytes := 0; +WHILE buflen >= 32 + DO Begin + FOR t := 0 TO 31 + DO Begin + temp[t] := temp[t] XOR localBuf[IndexOfBufBytes]; + IndexOfBufBytes := IndexOfBufBytes + 1; + End; + buflen := buflen - 32; + End; + +{*** 2ND step: repetitive XOR folding with (remainder of) password + + password='hello', (BufBytesUsed=0) + or password='12345678901234567890123456789012hello' (BufBytesUsed=32) + of which the first 32 bytes were used in the 1st encryption step. + + temp=temp XOR [hellohellohellohellohellohellohe]; + } +BufBytesUsed:=IndexOfBufBytes; +IF buflen > 0 + Then Begin + FOR t := 0 TO 31 + DO Begin + IF IndexOfBufBytes + buflen = BufBytesUsed + Then Begin + BufBytesUsed := IndexOfBufBytes; + temp[t] := temp[t] XOR EncryptKeys[t]; + End + Else Begin + temp[t] := temp[t] XOR localBuf[BufBytesUsed]; + BufBytesUsed := BufBytesUsed + 1; + End; + End; + End; +{*** 3RD step: XOR-ing with shuffleKey (bytes of a longint)} + +FOR t := 0 TO 31 DO temp[t] := temp[t] XOR locShuffleKey[t AND 3]; + +{*** 4&5 TH Step: see Shuffle1 } + +Shuffle1(temp, target); +End; + +PROCEDURE Encrypt(VAR key, buf, EncrPassword); +{ The encryptionKey 'key' is encrypted with the aid of +the shuffled login name/id within 'buf'. +Result: the encrypted Password (of type TencryptionKey). } +VAR _Key : TencryptionKey ABSOLUTE Key; + _EncrKey : TencryptionKey ABSOLUTE EncrPassword; + _LocalBuf : Buf32; + i: Byte; +Begin +Shuffle(_Key[0], buf, 16, _LocalBuf[0]); +Shuffle(_Key[4], buf, 16, _LocalBuf[16]); +FOR i := 0 TO 15 DO _LocalBuf[i] := _LocalBuf[i] XOR _LocalBuf[31-i]; +FOR i := 0 TO 7 DO _EncrKey[i] := _LocalBuf[i] XOR _LocalBuf[15-i]; +End; + +Procedure EncryptPassword(objId:longint;password:string;Var Ekey:TencryptionKey); +{ Source of the encryption routine: LOGON.PAS by Barry Nance, [1:141/209] BYTE March'93 } +{ Two bugs fixed by Horst Jelonneck (930323) } +Var buf:buf32; + TobjId:Longint; + Tpassword:string; + +begin +TobjId:=Lswap(objId); +Tpassword:=password+#0; +Shuffle(TObjId,Tpassword[1],length(password),buf); +Encrypt(Ekey,buf,Ekey); +end; + +Procedure EncryptPasswordDifference(objId:Longint; + OldPassword,NewPassword:string; + Var key:TencryptionKey; + Var PWdif:TencrPWdifference; + Var PWdifChecksum:byte + ); +{ Used by nwBindry.ChangeEncrBinderyObjectPassword. + + Encrypt3 and associated tables adapted from a c source (NVPW.C) + by Willem Jan Hengeveld, A.K.A. Itsme@Hacktic.nl } + Procedure Encrypt3(Var buf1,buf2,buf3); + { buf1: (part of) encrypted oldPW + buf2: (part of) encrypted newPW + buf3: 'change pw' data (result) } + Var p1:buf8 absolute buf1; + p2:buf8 absolute buf2; + p3:buf8 absolute buf3; + j,c,i:byte; + buf:buf8; + begin + buf:=p2; + for i:=0 to 15 + do begin + + for j:=0 to 7 + do begin + c:=buf[j] XOR p1[j]; + buf[j]:=EncryptTable1[j][0][c AND $0F] + OR (EncryptTable1[j][1][c SHR 4] SHL 4); + end; + + c:=p1[7]; + for j:=7 downto 1 + do p1[j]:=(p1[j] SHL 4) OR (p1[j-1] SHR 4); + p1[0]:=(c SHR 4) OR (p1[0] SHL 4); + + FillChar(p3,8,#$0); + for j:=0 to 15 + do begin + c:=EncryptTable3[j]; + If odd(EncryptTable3[j]) + then c:=buf[c DIV 2] SHR 4 + else c:=buf[c DIV 2] AND $0F; + if Odd(j) + then p3[j DIV 2]:=p3[j DIV 2] XOR (c SHL 4) + else p3[j DIV 2]:=p3[j DIV 2] XOR c; + end; + + buf:=p3; + end; + end; +Var l:byte; + OldShuffledPW,NewShuffledPW:array[0..15] of byte; +begin +objId:=Lswap(objId); +Shuffle(objId,OldPassword[1],Length(OldPassword),OldShuffledPW); +Shuffle(objId,NewPassword[1],Length(NewPassword),NewShuffledPW); +Encrypt(key,OldShuffledPW,key); + +Encrypt3(OldShuffledPW,NewShuffledPW,PWdif); +Encrypt3(OldShuffledPW[8],NewShuffledPW[8],PWdif[8]); +if Length(NewPassword)<63 + then l:=length(NewPassword) + else l:=63; +PWdifChecksum:=(((l XOR OldShuffledPW[1] XOR OldShuffledPW[2]) AND $7F) OR $40); +end; + + +{-------------- End of encryption procedures ----------------------------} + +Procedure UpString(s : String); Assembler; +{ fast upcasestring by Bob Swart } +ASM + PUSH DS + LDS SI, s + LES DI, s + CLD + XOR AH, AH + LODSB + STOSB + XCHG AX, CX { empty string? } + JCXZ @2 +@1: LODSB + SUB AL, 'a' + CMP AL, 'z'-'a'+1 + SBB AH, AH + AND AH, 'a'-'A' + SUB AL, AH + ADD AL, 'a' + STOSB + LOOP @1 +@2: POP DS +end; + + +procedure ZStrCopy(dest:String;Var source;len:byte); assembler; +{ 1. Copies len bytes from an array to a pascal type string. } +{ 2. Trailing NULLs are removed from the string. } +{ consequently, the length of det (dest[0]) will allways be <= len. } +asm + mov dx,ds + + les di,dest + xor ch,ch + mov [es:di],ch { dest[0]:=#0 } + mov cl,len + jcxz @4 { if len=0 then goto @4 } + lds si,source +@3: lodsb + or al,al + jz @2 { determine non-0 length of source } + dec cx + jnz @3 +@2: xor ax,ax + mov al,len + sub ax,cx { ax:= bytes to copy } + + les di,dest + lds si,source + mov es:[di],al { dest[0]:=actual non-0 len } + inc di { es:di => dest[1] ; ds:si => source[0] } + + mov cx,ax + cld + rep movsb { copy cx bytes from ds:si to es:di } + +@4: mov ds,dx +end; + +procedure PStrCopy(Var dest:String;source:String;len:byte); +Var w:byte; +begin +w:=1; +dest[0]:=chr(len); +While w<=ord(source[0]) + do begin + dest[w]:=source[w]; + inc(w) + end; +While w<=len + do begin + dest[w]:=#0; + inc(w) + end; +end; + +Procedure NovTime2String(tim:TnovTime;Var DateStr:string); +CONST day:array[0..6] of string[3] + =('Sun','Mon','Tue','Wed','Thu','Fri','Sat'); + Month:array[1..12] of string[3] + =('Jan','Feb','Mar','Apr','May','Jun', + 'Jul','Aug','Sep','Oct','Nov','Dec'); +Type string4=string[4]; +Var sday,syear,shour,smin,ssec:string4; + Procedure zstr(n:byte;Var s:string4); + begin + str(n,s); + if s[0]=#1 then s:='0'+s; + end; +begin +if (tim.month>12) or (tim.month<1) + or (tim.day<1) or (tim.day>31) + or (tim.hour>23) or (tim.min>59) or (tim.sec>59) + then DateStr:=' ' + else begin + zstr(tim.day,sday); + if sday[1]='0' then sday[1]:=' '; + if tim.year<80 then str(tim.year+2000,syear) + else str(tim.year+1900,syear); + zstr(tim.hour,shour); + zstr(tim.min,smin); + zstr(tim.sec,ssec); + DateStr:=day[tim.DayOfWeek]+', '+ + sday+' '+Month[tim.month]+' '+syear+' '+ + shour+':'+smin+':'+ssec; + end; +end; + +Procedure DosTime2NovTime(dt:Longint;Var nt:TnovTime); +Var k:array[1..2] of word absolute dt; +begin +with nt + do begin + year:=(80+byte(k[2] SHR 9)) MOD 100; + month:=(byte(k[2] SHR 5) AND 15); + day:=byte(k[2] AND 31); + + hour:=byte (k[1] SHR 11); + min:=(byte(k[1] SHR 5) AND 63); + sec:=2*byte(k[1] AND 31); + end; +end; + + +Procedure NovTime2DosTime(nt:TnovTime;Var dt:Longint); +Var k:array[1..2] of word absolute dt; +begin +with nt + do begin + k[2]:=(((100+year-80) mod 100) SHL 9)+(month SHL 5)+day; + k[1]:=(hour SHL 11)+(min SHL 5)+(sec DIV 2); + end; +end; + +Procedure NovPath2DosPath(np:String;Var dp:string); +{ np is a pascal type string with the folowing format: + + chr(length(subdir1)),subdir1, + ... + chr(length(subdirN)),subDirN. + + It will be transformed to a DOS path of type Subdir1\Subdir2\.. \subDirN } +Var t:Byte; +begin +dp:=np; +delete(dp,1,1); +for t:=1 to ord(dp[1]) + do if dp[t]<=#20 then dp[t]:='\'; +end; + + +Function LoNibble(b:Byte):Byte; assembler; +asm +mov al,b +and al,$0F +end; + +Function HiNibble(b:Byte):Byte; assembler; +asm +mov ah,$00 +mov al,b +shr ax,1 +shr ax,1 +shr ax,1 +shr ax,1 +end; + +Function HexStr(dw:LongInt;len:byte):string; +CONST n:array[0..15] of char + =('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'); +Var t:integer; + ldw:LongInt; + res:string; +begin +res:=''; +for t:=1 to len +do begin + ldw:=dw AND $0000000F; + res:=n[ldw]+res; + dw:=dw SHR 4; + end; +HexStr:=res; +end; + +Function HexDumpStr(Var dumpVar;len:Byte):String; +Var arr:Array[1..256] of Byte ABSOLUTE dumpVar; + t:Byte; + res:String; +begin +res:=''; +for t:=1 to (1+(len DIV 2)) + do res:=res+HexStr(arr[t],2); +res[0]:=chr(len); +HexDumpStr:=res; +end; + +Function IsLowerNetworkAddress(Var a, b): Boolean; +Type TCharNetAddress = Array [1..10] of Char; +Var Aaddr : TCharNetAddress ABSOLUTE a; + Baddr : TCharNetAddress ABSOLUTE b; +Begin + IsLowerNetworkAddress := Aaddr < Baddr; +End; + +Function IsEqualNetworkAddress(Var a, b): Boolean; +Type TCharNetAddress = Array [1..10] of Char; +Var Aaddr : TCharNetAddress ABSOLUTE a; + Baddr : TCharNetAddress ABSOLUTE b; +Begin + IsEqualNetworkAddress := (Aaddr = Baddr); +End; + +Function IsLaterNovTime(time1,time2:TnovTime):boolean; +Var bAft:boolean; + y1,y2:word; +begin +if time1.year>=80 + then y1:=1900+time1.year + else y1:=2000+time1.year; +if time2.year>=80 + then y2:=1900+time2.year + else y2:=2000+time2.year; +bAft:=(y1>y2); +if y1=y2 + then begin + bAft:=(time1.month>time2.month); + if time1.month=time2.month + then begin + bAft:=(time1.day>time2.day); + if time1.day=time2.day + then begin + bAft:=(time1.hour>time2.hour); + if time1.hour=time2.hour + then begin + bAft:=(time1.min>time2.min); + if time1.min=time2.min + then bAft:=(time1.sec>time2.sec); + end; + end; + end; + end; +IsLaterNovTime:=bAft +end; + + +Function IsEqualNovTime(time1,time2:TnovTime):boolean; +Var t1:array[1..Sizeof(TnovTime)-1] of char absolute time1; + t2:array[1..SizeOf(TnovTime)-1] of char absolute time2; +begin +IsEqualNovTime:=(t1=t2); +end; + + +Function MapV2RightsToV3(V2Rights:byte):word; +CONST RightsNotChanged:byte=$08+$10+$40+$80; +Var result:Word; +begin +if (V2Rights and $FF)>0 + then result:=$1FF + else begin + result:=(V2Rights and RightsNotChanged); + if (V2Rights and ($01+$04))>0 + then result:=result or $01; + if (V2Rights and ($02+$04))>0 + then result:=result or $02; + if (V2Rights and $04)>0 + then result:=result or $01; + if (V2Rights and $20)>0 + then result:=result or $28; + end; +MapV2RightsToV3:=result; +end; + +Function MapV3RightsToV2(V3Rights:Word):Byte; +CONST RightsNotChanged:word=$10+$20+$40+$80; +Var result:Byte; +begin +If (V3Rights and $0100)>0 + then result:=$FF + else begin + result:=(lo(V3Rights) and RightsNotChanged); + If (V3Rights and $01)>0 + then result:=result or $05; + If (V3Rights and $02)>0 + then result:=result or $06; + {If (V3Rights and $04)>0 + then result:=result or $00;} + If (V3Rights and $08)>0 + then result:=result or $28; + end; +MapV3RightsToV2:=result; +end; + +Procedure GetNWversion(Var version:word); +{ determine the version of the software installed on the current file server. } +{ see GetFileServerInformation F217/11 in the nwServ unit for more information } + +{ version : word; contains the versionnumber of the fileserver we're + currently connected to. Used by primary functions to + determine what type of calls to use to perform a certain function. + + format: (majorVersion*100)+minorVersion + e.g. 311 for 3.11 + Range: 215 (advanced netware 2.15) and upwards } +{ If the version is lower than 2.15, 2.15 is returned. } +{ note: you don't have to be logged in to call this function. } +Type TReq= Record + PacketLength : Word; + FunctionVal : Byte; + End; + TRep=array[1..$80] of byte; + Tpreq=^Treq; + Tprep=^Trep; +Var Result:word; +Begin +With TPreq(GlobalReqBuf)^ +Do Begin + PacketLength := 1; FunctionVal := $11; + End; +F2SystemCall($17,sizeof(Treq),Sizeof(Trep),result); +If result=0 + then version:=(TPrep(GlobalReplyBuf)^[49]*100)+TPrep(GlobalReplyBuf)^[50] + else version:=215; +End; + + +Function IsV3Supported:boolean; +Var version:word; +begin +GetNWversion(version); +IsV3Supported:=(version>=300); +end; + + +END. diff --git a/NWTP/NWQMS.PAS b/NWTP/NWQMS.PAS new file mode 100644 index 0000000..c2a2d25 --- /dev/null +++ b/NWTP/NWQMS.PAS @@ -0,0 +1,1149 @@ +{$X+,B-,V-} {essential compiler directives} + +UNIT nwQMS; + +{ nwQMS unit as of 950301 / NwTP 0.6 API. (c) 1993,1995, R. Spronk } +{ Based in part on a unit containing queue services routines, written by + Erik van Heyningen in April 1994. } + +INTERFACE + +Uses nwMisc; + +{ Function: Interrupt: Comments: + + Queue Server Functions: + +. AbortServicingQueueJob (F217/73) (S) +. AttachQueueServerToQueue (F217/6F) (S) +. ChangeToClientRights (F217/74) (S) +. DetachQueueServerFromQueue (F217/70) (S) +. FinishServicingQueueJob (F217/72) (S) +. RestoreQueueServerRights (F217/75) (S) +. ServiceQueueJob (F217/71) (S) +. SetQueueServerStatus (F217/77) (S) + + Queue Operator Functions: + +. ChangeQueueJobPosition (F217/6E) (O) +* CreateQueue (F217/64) (SUP) +* DestroyQueue (F217/65) (SUP) +. SetQueueStatus (F217/67) (O) + + Queue User Functions: + +. CreateQueueJob (F217/68) (C) +. StartQueueJob (F217/69) (C) (1) + + Miscellaneous Queue Functions: + +. ChangeQueueJobEntry (F217/6D) (C-O) +* GetQueueJobList (F217/6B) (C-O) +* GetQueueJobsFileSize (F217/78) (C-O-S) +* ReadQueueStatus (F217/66) (C-O) +* ReadQueueJobEntry (F217/6C) (C-O-S) +. ReadQueueServerStatus (F217/76) (C-O) +* RemoveJobFromQueue (F217/6A) (C-O) + + +Notes: C : Function available to Clients (Queue Users); + S : Function available to Servers (Queue Servers); + O : Functions availaible to Operators (Queue Operators); + SUP: Functions available to Supervisors/Workgroup managers. + + (1): StartQueueJob is a.k.a. CloseFileAndStartQueueJob +} + +CONST { Queue status flag } + QS_ALL_OK = $00; + QS_CANT_ADD_JOBS = $01; { possibility to add jobs disabled by operator } + QS_SERVERS_CANT_ATTACH = $02; { attachment of servers to queue disabled by operator } + QS_CANT_SERVICE_JOBS = $04; { queue halted by operator } + { QS_XXXX constants can be ORed to form a QstatusFlag } + + QF_NONE = $00; + QF_AUTO_START = $08; + QF_SERVICE_RESTART = $10; + QF_ENTRY_OPEN = $20; + QF_USER_HOLD = $40; + QF_OPERATOR_HOLD = $80; + +CONST MaxQueueJobs = 250; + +Type TQueueStatus= RECORD + ObjectId : Longint; { Object id of queue } + Status : Byte; { status of queue QS_XXX } + NbrOfJobs : Byte; { Number of jobs in queue } + NbrOfServers : Byte; { Number of servers attached to queue } + ServerObjectIds : array[1..25] of Longint; + { List of Objects-ids of attached servers } + ServerConnNbrs : array[1..25] of Byte; + { List of attached server stations } + MaxNbrOfServers : Byte; { ??? } + end; + + TQueueServerStatus= Array[1..64] of Byte; + { undefined structure -as far as QMS is concerned-} + +Type TJobNumberList = Array[1..MaxQueueJobs] OF Word; + TQueueJobList = record + JobCount : Word; + JobNbrs : TJobNumberList; { List of jobs numbers by position in queue } + MaxJobs : Word; {????} { Maximum job numbers } + end; + + TJobFileHandle=Array[1..6] of Byte; + TQueueJobEntry =Record { Unit external Type } + ClientConnNbr : Byte; + ClientTaskNbr : Byte; + ClientObjectID : Longint; + JobEntryTime : TnovTime; + JobNumber : Word; + JobFileName : String[14]; + JobFileHandle : TjobFileHandle; + + TargetServerIDnumber : Longint; {2} + TargetExecutionTime : TnovTime; {2} + JobType : Word; {2} + JobControlFlags : Byte; {2} + JobDescription : String[50]; {2} + ClientRecordArea : Array[1..152] OF Byte; {2} + + JobPosition : Byte; {2/operators only} + + ServerConnNbr, {1} + ServerTaskNbr : Byte; {1} + ServerObjectID : Longint; {1} + End; + { 1: Filled by Queue server. As long as ServerTaskNbr=0, + queue entry is not being serviced. + 2: Can be changed by queue operators and/or the 'owner' of + the job after job has been placed in queue + } + +Var result:Word; + +{F217/64 {2.1x+} +Function CreateQueue(Qname :string; QobjectType:Word; + dirHandle :Byte; pathName :string; + VAR QobjID:Longint ):Boolean; +{ Creates an object of an object_queue_type in the bindery, checks that + all settings are valid before creating. Returns the object_id of the + created queue if creation was successfull. } + +{F217/65 [2.1x+]} +Function DestroyQueue(QobjID:Longint):Boolean; +{ Destroys the specified Queue; aborts all jobs in the queue; + associated files/directories are deleted; + queue object is removed from the bindery. } + + +{F217/76 [2.1x/3.x]} +Function ReadQueueStatus(QobjID:Longint; + Var Qstatus:TQueueStatus):Boolean; +{ Read the status of a queue. This information is changed by queueservers.} + +{F217/67 [2.1x+]} +Function SetQueueStatus(QobjId:Longint; NewQstatusFlag:Byte):Boolean; +{ Change the queue status flag. Use the QS_XXXX constants } + +{F217/6B} +FUNCTION GetQueueJobList( QueueObjId: Longint; + Var QJobList:TQueueJobList): Boolean; +{ You need to be either a Q_USER or a Q_OPERATOR } + +{F217/6C} +FUNCTION ReadQueueJobEntry( QObjId: Longint;JobNbr: Word; + VAR QJob: TQueueJobEntry): Boolean; +{ You need to be either a Q_USER, Q_OPERATOR or a Q_SERVER } + +{F217/6A} +FUNCTION RemoveJobFromQueue( QObjId: Longint; JobNbr: Word): Boolean; +{ You need to be Q_OPERATOR or the Q_USER who queued the job } + +{F217/69 [2.1x+]} +Function StartQueueJob(QobjId:Longint;JobNbr:Word):Boolean; + + +{F217/6E [2.1x+]} +Function ChangeQueueJobPosition(QobjId:Longint; JobNbr:Word; + NewJobPos:Byte ):Boolean; +{ Q_OPERATOR only } + + +{F217/6F [2.1x+]} +Function AttachQueueServerToQueue(QobjId:Longint):Boolean; +{ Q_SERVERs only } + + +{F217/70 [2.1x+]} +Function DetachQueueServerFromQueue(QobjId:Longint):Boolean; +{ Q_SERVERs only } + + +{F217/71 [2.1x+]} +Function ServiceQueueJob(QobjID:Longint; JobType:Word; + Var QjobEntry:TQueueJobEntry):Boolean; +{ Q_SERVERs only } + +{F217/72 [2.1x+]} +Function FinishServicingQueueJob(QobjId:Longint;JobNbr:Word; + Charge:Longint ):Boolean; +{ Q_SERVERs only } + +{F217/73 [2.1x+]} +Function AbortServicingQueueJob(QobjId:Longint; JobNbr:Word):Boolean; + +{F217/74 [2.1x+]} +Function ChangeToClientRights(QobjId:Longint;JobNbr:Word):Boolean; +{ Q_SERVERs servicing job only } + +{F217/75 [2.1x+]} +Function RestoreQueueServerRights:Boolean; +{ Q_SERVERs, servicing job and having previously called + ChangeToClientRights only } + +{F217/76 [2.1x+]} +Function ReadQueueServerStatus(QobjId :Longint; + QserverObjId :Longint; + QserverConnNbr:Byte; + Var Qstatus:TQueueServerStatus):Boolean; + + +{F217/77 [2.1x+]} +Function SetQueueServerStatus(QobjId:Longint; Qstatus:TqueueServerStatus):Boolean; + +{F217/78 [2.1x+]} +Function GetQueueJobsFileSize(QobjId:Longint; JobNbr:Word; + Var JobSize:Longint ):Boolean; + +{F217/68 [2.1x+]} +Function CreateQueueJob(QobjId:Longint; + {i/o} Var Qjob:TqueueJobEntry):Boolean; + +{F217/6D [2.1x+]} +Function ChangeQueueJobEntry(QobjId:Longint;Qjob:TQueueJobEntry):Boolean; + +IMPLEMENTATION {============================================================} + +Uses nwIntr; + +Type TIntJobStruct =Record { Unit internal Type } + _ClientConnNbr, + _ClientTaskNbr : Byte; + _ClientObjectID, {hi-lo} + _TargetServerIDnumber : Longint; {hi-lo} + _TargetExecutionTime, + _JobEntryTime : Array[1..6] OF Byte; { YMDHMS } + _JobNumber, {hi-lo} + _JobType : Word; {hi-lo} + _JobPosition, + _JobControlFlags : Byte; + _JobFileName : Array[1..14] OF CHAR; { ASCIIZ } + _JobFileHandle : TJobFileHandle; + _ServerConnNbr, + _ServerTaskNbr : Byte; + _ServerObjectID : Longint; {hi-lo} + _JobDescription : Array[1..50] OF CHAR; { ASCIIZ } + _ClientRecordArea : Array[1..152] OF Byte + End; + +Procedure ConvertQJE2ext(qje:TintJobStruct;VAR ext:TQueueJobEntry; + Unrestricted:Boolean); +{convert the internal QueueJobEntry type into the equivalent + unit external type } +begin +With qje,ext + do begin + ClientConnNbr:=_ClientConnNbr; + ClientTaskNbr:=_ClientTaskNbr; + ClientObjectId:=Lswap(_ClientObjectId); + Move(_JobEntryTime,JobEntryTime,6); JobEntryTime.DayOfWeek:=0; + { # fix year for year 2000+ ? } + JobNumber:=swap(_JobNumber); + ZstrCopy(JobFileName,_JobFileName,14); + JobFileHandle:=_JobFileHandle; + TargetServerIdNumber:=Lswap(_TargetServerIdNumber); + Move(_TargetExecutionTime,TargetExecutionTime,6); TargetExecutionTime.DayOfWeek:=0; + { # fix year for year 2000+ ? } + JobType:=swap(_JobType); + JobControlFlags:=_JobControlFlags; + IF UnRestricted + then begin + ZstrCopy(JobDescription,_JobDescription,50); + Move(_ClientRecordArea,ClientRecordArea,152); + end; + JobPosition:=_JobPosition; + ServerConnNbr:=_ServerConnNbr; + ServerTaskNbr:=_ServerTaskNbr; + ServerObjectId:=Lswap(_ServerObjectId); + end; +end; + +Procedure ConvertQJE2int(qje:TQueueJobEntry;VAR int:TintJobStruct); +{convert the external QueueJobEntry type into the equivalent + unit internal type } +Var s:string[50]; +begin +With qje,int + do begin + _ClientConnNbr:=ClientConnNbr; + _ClientTaskNbr:=ClientTaskNbr; + _ClientObjectId:=Lswap(ClientObjectId); + _TargetServerIdNumber:=Lswap(TargetServerIdNumber); + Move(TargetExecutionTime,_TargetExecutionTime,6); + { # fix year for year 2000+ ? } + Move(JobEntryTime,_JobEntryTime,6); + { # fix year for year 2000+ ? } + _JobNumber:=swap(JobNumber); + _JobType:=swap(JobType); + _JobPosition:=JobPosition; + _JobControlFlags:=JobControlFlags; + PStrCopy(s,JobFilename,14);Move(s[1],_JobFileName,14); + _JobFileHandle:=JobFileHandle; + _ServerConnNbr:=ServerConnNbr; + _ServerTaskNbr:=ServerTaskNbr; + _ServerObjectId:=Lswap(ServerObjectId); + PstrCopy(s,JobDescription,50);Move(s[1],_JobDescription,50); + Move(ClientRecordArea,_ClientRecordArea,152); + end; +end; + + +{--- Initial Functions, create and destroy Job Queue --------------------} + +{F217/64 {2.1x+} +Function CreateQueue(Qname :string; QobjectType:Word; + dirHandle :Byte; pathName :string; + VAR QobjID:Longint ):Boolean; +{ Creates an object of an object_queue_type in the bindery, checks that + all settings are valid before creating. Returns the object_id of the + created queue if creation was successfull. } +{ QobjectType= OT_PRINT_QUEUE, OT_JOB_QUEUE, + OT_ARCHIVE_QUEUE, own obj.type >$8000 } +{ You need supervisor-equivalent or workgroup-manager rights to perform + this action. } +{ To add (remove) Queue operators or + (dis-)allow Queue servers to attach to a queue or + (dis-)allow objects (users/groups) to use a queue, + use the AddBinderyObjectToSet and DeleteBinderyObjectFromSet functions + in the nwBindry unit with the property names Q_OPERATORS, Q_SERVERS + and Q_USERS respectively. } +Type Treq=record + len :Word; + subFunc :Byte; + _Qtype :Word; { hi-lo} + _QdivData :array[1..168] of Byte; + end; + Trep=record + _Qid:Longint; {hi-lo} + end; + TPreq=^Treq; + TPrep=^Trep; +Var i:Byte; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$64; + _Qtype:=swap(QobjectType); { force hi-lo } + i:=ord(Qname[0])+1; + UpString(Qname);Move(Qname[0],_QdivData[1],i); + + inc(i); + _QdivData[i]:=DirHandle; + + inc(i); + UpString(PathName); + Move(PathName[0],_QDivData[i],ord(PathName[0])+1); + + len:=3+i+ord(PathName[0]); + F2SystemCall($17,len+2,SizeOf(Trep),result); + end; +With TPrep(GlobalReplyBuf)^ + do begin + QobjID:=Lswap(_Qid); { force lo-hi } + end; +CreateQueue:=(result=0) +{ resultcodes: $00 Success ; $96 Server Out Of Memory; $99 Drectory Full; + $9B Bad Directory Handle; $9C Invalid Path; $ED Property Already Exists; + $EE Object Already Exists; $EF Invalid Name; $F0 Wildcard Not Allowed; + $F1 Invalid Bindery Security; $F5 No Object Create Privilege; + $F7 No Property Create Privilege; $FC No Such Object; + $FE Server Bindery Locked; $FF Bindery Failure. } +end; + +{F217/65 [2.1x+]} +Function DestroyQueue(QobjID:Longint):Boolean; +{ Destroys the specified Queue; aborts all jobs in the queue; + associated files/directories are deleted; + queue object is removed from the bindery. } +Type Treq=record + len:Word; + subFunc:Byte; + _QobjID:Longint; {hi-lo} + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subFunc:=$65; + _QobjID:=Lswap(QobjID); { force hi-lo } + end; +F2SystemCall($17,Sizeof(Treq),0,result); +DestroyQueue:=(result=0); +{ resultcodes: $00 Success ; $96 Server Out Of Memory; $9C Invalid Path; + $D0 Queue Error; $D1 No Queue; $FF Hardware Failure. } +end; + +{----------------Client or Diagnostic Functions-----------------------------} + +{F217/76 [2.1x/3.x]} +Function ReadQueueStatus(QobjID:Longint; + Var Qstatus:TQueueStatus):Boolean; +{ Read the status of a queue. This information is changed by queueservers.} +Type Treq=record + len :Word; + subFunc:Byte; + _QobjID:Longint; {hi-lo} + end; + Trep=record + _QobjID:Longint; {hi-lo} + _Qstatus:Byte; + _NbrOfJobs:Byte; + _NbrOfServers:Byte; {max.25} + _serverIDlist:array[1..25] of Longint; {hi-lo} + _ServerConnNbrs:array[1..25] of Byte; + _MaxNumberOfServers:Byte; + end; + TPreq=^Treq; + TPrep=^Trep; +Var t:Byte; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subFunc:=$66; + _QobjID:=Lswap(QobjID); {force hi-lo} + end; +F2SystemCall($17,Sizeof(Treq),SizeOf(Trep),result); +With Qstatus, TPrep(GlobalReplyBuf)^ + do begin + ObjectId:=Lswap(_QobjId); + status:=_Qstatus; + NbrOfJobs:=_NbrOfJobs; + NbrOfServers:=_NbrOfServers; + + for t:=1 to NbrOfServers + do ServerObjectIDs[t]:=Lswap(_ServerIDlist[t]); + Move(_ServerConnNbrs,ServerConnNbrs,25); + MaxNbrOfServers:=_MaxNumberOfServers; + end; +ReadQueueStatus:=(result=0) +end; + +{F217/67 [2.1x+]} +Function SetQueueStatus(QobjId:Longint; NewQstatusFlag:Byte):Boolean; +{ Change the queue status flag. Use the QS_XXXX constants } +Type Treq=record + len:Word; + subFunc:Byte; + _QobjId:Longint; {hi-lo} + _Qstatus:Byte; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$67; + _QobjId:=Lswap(QobjId); + _Qstatus:=NewQStatusFlag; + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),0,result); +SetQueueStatus:=(result=0) +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $9C Invalid Path; + $D0 Queue Error; + $D1 No Queue; + $D3 No Queue Rights; + $FE Server Bindery Locked; + $FF Bindery Failure. +} +end; + + + +{F217/6B} +FUNCTION GetQueueJobList( QueueObjId: Longint; + Var QJobList:TQueueJobList): Boolean; +{ You need to be either a Q_USER or a Q_OPERATOR } +Type TReq=Record + BufLen : Word; + func : Byte; + _QueueObjId: Longint; {hi-lo} + end; + TRep=Record + _JobCount:Word; {max 250, hi-lo} + _JobBuf :TJobNumberList; {array, entries hi-lo} + _MaxJobs :Word; {hi-lo} + End; + TPrep=^Trep; + TPreq=^Treq; +Var i:Word; +Begin +With TPReq(GlobalReqBuf)^ + do Begin + func:= $6B; + _QueueObjId:= LSwap(QueueObjId); + BufLen:=5; + End; +F2SystemCall($17,Sizeof(Treq),SizeOf(Trep),result); +IF result = 0 + Then with QJobList, TPrep(GlobalReplyBuf)^ + do Begin + JobCount:= Swap(_JobCount); + IF (JobCount > MaxQueueJobs) + Then JobCount:= MaxQueueJobs; + FOR i:= 1 TO JobCount + DO JobNbrs[i]:= Swap(_JobBuf[i]); + MaxJobs:=swap(_MaxJobs); + End; +GetQueueJobList:= (result = 0); +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $9C Invalid Path; + $D0 Queue Error; + $D1 No Queue; + $D2 No Queue Server; + $D3 No Queue Rights; + $FC No Such Object; + $FE Server Bindery Locked; + $FF Bindery Failure. } +End; + +{F217/6C} +FUNCTION ReadQueueJobEntry( QObjId: Longint;JobNbr: Word; + VAR QJob: TQueueJobEntry): Boolean; +{ You need to be either a Q_USER, Q_OPERATOR or a Q_SERVER } +Type TReq=Record + BufLen : Word; + func : Byte; + _QueueObjId: Longint; {hi-lo} + _JobNumber : Word {hi-lo} + End; + TRep=Record + buf : TintJobStruct; { Unit INTERNAL type. To be converted } + End; + TPreq=^Treq; + TPrep=^Trep; +Begin +With TPReq(GlobalReqBuf)^ + do Begin + Buflen:= 7; + func:= $6C; + _QueueObjId:= LSwap(QObjId); + _JobNumber:= Swap(JobNbr); + End; +F2SystemCall($17,SizeOf(Treq),SizeOf(Trep),result); +IF result= 0 + Then with TPrep(GlobalReplyBuf)^ + do Begin + ConvertQJE2ext(buf,QJob,True); + End; +ReadQueueJobEntry:= result = 0; +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $D0 Queue Error; + $D1 No Queue; + $D2 No Queue Server; + $D3 No Queue Rights; + $D5 No Queue Job; + $FC No Such Object; + $FE Server Bindery Locked; + $FF Bindery Failure. } +End; + +{F217/6A} +FUNCTION RemoveJobFromQueue( QObjId: Longint; JobNbr: Word): Boolean; +{ You need to be Q_OPERATOR or the Q_USER who queued the job } +Type TReq=Record + BufLen: Word; + func: Byte; + _QueueObjId: Longint; {hi-lo} + _JobNumber:Word {hi-lo} + End; + TPreq=^Treq; +Begin +With TPReq(GlobalReqBuf)^ + do Begin + Buflen:= 7; + func:= $6A; + _QueueObjId:= LSwap(QObjId); + _JobNumber:= Swap(JobNbr); + End; +F2SystemCall($17,SizeOf(Treq),0,result); +RemoveJobFromQueue:= result = 0; +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $D0 Queue Error; + $D1 No Queue; + $D3 No Queue Rights; + $D5 No Queue Job; + $D6 No Job Rights; + $FE Server Bindery Locked; + $FF Bindery Failure. } +End; + + +{F217/69 [2.1x+]} +Function StartQueueJob(QobjId:Longint;JobNbr:Word):Boolean; +Type Treq=record + len :Word; + subFunc:Byte; + _QobjId:Longint; {hi-lo} + _JobNbr:Word; {hi-lo} + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$69; + _QobjId:=Lswap(QobjID); + _JobNbr:=swap(JobNbr); + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),0,result); +StartQueueJob:=(result=0) +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $D0 Queue Error; + $D1 No Queue; + $D3 No Queue Rights; + $D5 No Queue Job; + $D6 No Job Rights; + $FE Server Bindery Locked; + $FF Bindery Failure. } +end; + + +{F217/6E [2.1x+]} +Function ChangeQueueJobPosition(QobjId:Longint; JobNbr:Word; + NewJobPos:Byte ):Boolean; +{ Q_OPERATOR only } +Type Treq=record + len :Word; + subFunc :Byte; + _QobjId :Longint; {hi-lo} + _JobNbr :Word; {hi-lo} + _NewJobPos:Byte; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$6E; + _QobjId:=Lswap(QobjId); + _JobNbr:=swap(JobNbr); + _NewJobPos:=NewJobPos; + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),0,result); +ChangeQueueJobPosition:=(result=0) +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $D0 Queue Error; + $D1 No Queue; + $D5 No Queue Job; + $D6 No Job Rights; + $FE Server Bindery Locked; + $FF Bindery Failure. } +end; + +{F217/6F [2.1x+]} +Function AttachQueueServerToQueue(QobjId:Longint):Boolean; +{ Q_SERVERs only } +Type Treq=record + len :Word; + subFunc:Byte; + _QobjId:Longint; {hi-lo} + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$6F; + _QobjId:=Lswap(QobjId); + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),0,result); +AttachQueueServerToQueue:=(result=0) +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $9C Invalid Path; + $D0 Queue Error; + $D1 No Queue; + $D2 No Queue Server; + $FE Server Bindery Locked; + $FF Bindery Failure. } +end; + + +{F217/70 [2.1x+]} +Function DetachQueueServerFromQueue(QobjId:Longint):Boolean; +{ Q_SERVERs only } +Type Treq=record + len :Word; + subFunc:Byte; + _QobjId:Longint; {hi-lo} + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$70; + _QobjId:=Lswap(QobjId); + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),0,result); +DetachQueueServerFromQueue:=(result=0) +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $9C Invalid Path; + $D0 Queue Error; + $D1 No Queue; + $D2 No Queue Server; + $FE Server Bindery Locked; + $FF Bindery Failure. } +end; + + +{F217/71 [2.1x+]} +Function ServiceQueueJob(QobjID:Longint; JobType:Word; + Var QjobEntry:TQueueJobEntry):Boolean; +{ Q_SERVERs only } +Type Treq=record + len :Word; + subFunc :Byte; + _QobjId :Longint; {hi-lo} + _JobType:Word; {hi-lo} + end; + Trep=Record + _qje:TintJobStruct; { EXCEPT last two fields } + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$71; + _QobjId:=Lswap(QobjId); + _JobType:=swap(JobType); + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),SizeOf(Trep)-50-152,result); +With TPrep(GlobalReplyBuf)^ + do begin + ConvertQJE2Ext(_qje,QjobEntry,false); + FillChar(QjobEntry.JobDescription,50,#$0); + FillChar(QjobEntry.ClientRecordArea,152,#$0); + { Use the ReadQueueJobEntry function to get job's + descriptionstring and clientRecordArea. } + end; +ServiceQueueJob:=(result=0) +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $9C Invalid Path; + $D0 Queue Error; + $D1 No Queue; + $D3 No Queue Rights; + $D5 No Queue Job; + $D9 Connection not Queue Server; + $DA Queue Halted; + $FE Server Bindery Locked; + $FF Bindery Failure. } +end; + +{F217/72 [2.1x+]} +Function FinishServicingQueueJob(QobjId:Longint;JobNbr:Word; + Charge:Longint ):Boolean; +{ Q_SERVERs only } +Type Treq=record + len :Word; + subFunc:Byte; + _QobjId:Longint; {hi-lo} + _JobNbr:Word; {hi-lo} + _Charge:Longint; {hi-lo} + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$72; + _QobjId:=Lswap(QobjId); + _JobNbr:=swap(JobNbr); + _Charge:=Lswap(Charge); + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),0,result); +FinishServicingQueueJob:=(result=0) +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $D0 Queue Error; + $D1 No Queue; + $D6 No Job Rights; } +end; + +{F217/73 [2.1x+]} +Function AbortServicingQueueJob(QobjId:Longint; JobNbr:Word):Boolean; +Type Treq=record + len :Word; + subFunc:Byte; + _QobjId:Longint; {hi-lo} + _JobNbr:Word; {hi-lo} + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$73; + _QobjId:=Lswap(QobjId); + _JobNbr:=swap(JobNbr); + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),0,result); +AbortServicingQueueJob:=(result=0) +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $D0 Queue Error; + $D1 No Queue; + $D6 No Job Rights; + $D9 Connection not Queue Server; } +end; + +{F217/74 [2.1x+]} +Function ChangeToClientRights(QobjId:Longint;JobNbr:Word):Boolean; +{ Q_SERVERs servicing job only } +Type Treq=record + len :Word; + subFunc:Byte; + _QobjId:Longint; {hi-lo} + _JobNbr:Word; {hi-lo} + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$74; + _QobjId:=Lswap(QobjId); + _JobNbr:=swap(JobNbr); + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),0,result); +ChangeToClientRights:=(result=0) +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $D0 Queue Error; + $D1 No Queue; + $D6 No Job Rights; + $D9 Connection not Queue Server; } +end; + +{F217/75 [2.1x+]} +Function RestoreQueueServerRights:Boolean; +{ Q_SERVERs, servicing job and having previously called + ChangeToClientRights only } +Type Treq=record + len :Word; + subFunc:Byte; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$75; + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),0,result); +RestoreQueueServerRights:=(result=0) +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $9C Invalid Path; + $D0 Queue Error; + $D1 No Queue; + $D3 No Queue Rights; + $D5 No Queue Job; + $D9 Connection not Queue Server; + $DA Queue Halted; + $FE Server Bindery Locked; + $FF Bindery Failure. } +end; + +{F217/76 [2.1x+]} +Function ReadQueueServerStatus(QobjId :Longint; + QserverObjId :Longint; + QserverConnNbr:Byte; + Var Qstatus:TQueueServerStatus):Boolean; +Type Treq=record + len :Word; + subFunc :Byte; + _QobjId :Longint; {hi-lo} + _QSobjId :Longint; {hi-lo} + _QSconnNbr:Byte; + end; + Trep=record + _Qstatus:TqueueServerStatus; + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$76; + _QobjId:=Lswap(QobjId); + _QSobjId:=Lswap(QserverObjId); + _QSconnNbr:=QserverConnNbr; + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),SizeOf(Trep),result); +With TPrep(GlobalReplyBuf)^ + do begin + Move(_Qstatus,Qstatus,SizeOf(TQueueServerStatus)); + end; +ReadQueueServerStatus:=(result=0) +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $9C Invalid Path; + $D1 No Queue; + $D2 No Queue Server; + $D3 No Queue Rights; + $F1 Invalid Bindery Security; + $FC No Such Object; + $FE Server Bindery Locked; + $FF Bindery Failure. } +end; + +{F217/77 [2.1x+]} +Function SetQueueServerStatus(QobjId:Longint; Qstatus:TqueueServerStatus):Boolean; +Type Treq=record + len :Word; + subFunc :Byte; + _QobjId :Longint; {hi-lo} + _Qstatus:TQueueServerStatus; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$77; + _QobjId:=Lswap(QobjId); + Move(Qstatus,_Qstatus,Sizeof(TQueueServerStatus)); + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),0,result); +SetQueueServerStatus:=(result=0) +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $9C Invalid Path; + $D0 Queue Error; + $D1 No Queue; + $FE Server Bindery Locked; + $FF Bindery Failure. } +end; + +{F217/78 [2.1x+]} +Function GetQueueJobsFileSize(QobjId:Longint; JobNbr:Word; + Var JobSize:Longint ):Boolean; +Type Treq=record + len :Word; + subFunc:Byte; + _QobjId:Longint; {hi-lo} + _JobNbr:Word; {hi-lo} + end; + Trep=record + _QobjId :Longint; {hi-lo} + _JobNbr :Word; {hi-lo} + _JobSize:Longint; {hi-lo} + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$78; + _QobjId:=Lswap(QobjId); + _JobNbr:=swap(JobNbr); + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),SizeOf(Trep),result); +With TPrep(GlobalReplyBuf)^ + do begin + JobSize:=Lswap(_JobSize); + end; +GetQueueJobsFileSize:=(result=0) +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $9C Invalid Path; + $FE Server Bindery Locked; + $FF Bindery Failure. } +end; + + +{F217/68 [2.1x+]} +Function CreateQueueJob(QobjId:Longint; + {i/o} Var Qjob:TqueueJobEntry):Boolean; +Type Treq=record + len :Word; + subFunc:Byte; + _QobjId:Longint; {hi-lo} + _Qjob :TintJobStruct; + end; + Trep=record + _QjobR:TintJobStruct; { Except the last two fields ! } + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$68; + _QobjId:=Lswap(QobjId); + ConvertQJE2Int(Qjob,_Qjob); + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),SizeOf(Trep)-152-50,result); +With TPrep(GlobalReplyBuf)^ + do begin + ConvertQJE2Ext(_QjobR,Qjob,False); + { False => Last 2 fields remain unchanged } + end; +CreateQueueJob:=(result=0) +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $99 Directory Full; + $9C Invalid Path; + $D0 Queue Error; + $D1 No Queue; + $D2 No Queue Server; + $D3 No Queue Rights; + $D4 Queue Full; + $DA Queue Halted; + $ED Property Already Exists; + $EF Invalid Name; + $F0 Wildcard Not Allowed; + $F1 Invalid Bindery Security; + $F7 No Property Create Privilege; + $FC No Such Object; + $FE Server Bindery Locked; + $FF Bindery Failure. } +end; + +{F217/6D [2.1x+]} +Function ChangeQueueJobEntry(QobjId:Longint;Qjob:TQueueJobEntry):Boolean; +Type Treq=record + len :Word; + subFunc:Byte; + _QobjId:Longint; {hi-lo} + _Qjob :TintJobStruct; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$6D; + _QobjId:=Lswap(QobjId); + ConvertQJE2Int(Qjob,_Qjob); + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),0,result); +ChangeQueueJobEntry:=(result=0) +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $D0 Queue Error; + $D1 No Queue; + $D5 No Queue Job; + $D7 Queue Servicing; + $FE Server Bindery Locked; + $FF Bindery Failure. } +end; + + +{$IFDEF Template} {--------------- Q unit function template ---------------} + +{F217/ [2.1x+]} +Function ( ):Boolean; +Type Treq=record + len:Word; + subFunc:Byte; + + end; + Trep=record + + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$ + + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),SizeOf(Trep),result); +With TPrep(GlobalReplyBuf)^ + do begin + + end; + :=(result=0) +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $99 Drectory Full; + $9B Bad Directory Handle; + $9C Invalid Path; + $D0 Queue Error; + $D1 No Queue; + $D2 No Queue Server; + $D3 No Queue Rights; + $D4 Queue Full; + $D5 No Queue Job; + $D6 No Job Rights; + $D7 Queue Servicing; + $D9 Connection not Queue Server; + $DA Queue Halted; + $DB Max Queue Servers; + $ED Property Already Exists; + $EE Object Already Exists; + $EF Invalid Name; + $F0 Wildcard Not Allowed; + $F1 Invalid Bindery Security; + $F5 No Object Create Privilege; + $F7 No Property Create Privilege; + $FC No Such Object; + $FE Server Bindery Locked; + $FF Bindery Failure. +} +end; + +{$ENDIF} + +end. diff --git a/NWTP/NWSEMA.PAS b/NWTP/NWSEMA.PAS new file mode 100644 index 0000000..0128660 --- /dev/null +++ b/NWTP/NWSEMA.PAS @@ -0,0 +1,330 @@ +{$X+,B-,V-} {essential compiler directives} + +Unit nwSema; + +{ nwSema unit as of 950301 / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +INTERFACE + +{ Primary functions: Interrupt: comments: + +* CloseSemaphore (F220/04) +* ExamineSemaphore (F220/01) +* GetConnectionsSemaphores (F217/F1) +* GetSemaphoreInformation (F217/F2) +* OpenSemaphore (F220/00) +* SignalSemaphore (F220/03) +* WaitOnSemaphore (F220/02) + +Notes: Functions marked with a '*' have been tested and found correct. +} + +Uses nwIntr,nwMisc; + +Type TsemaInfo=record + ConnNbr:word; + TaskNbr:word; + end; + TsemaInfoList=array[1..100] of TsemaInfo; + { used by GetSemaphoreInformation } + + TconnSema=record + OpenCount: Byte; + Value : Integer; + TaskNbr : Word; + unknown : byte; { always 00 ?! } + Name : string[127]; + end; + { used by GetConnectionsSemaphores } + +Var Result:word; + +{F220/00 [2.15? 3.x]} +Function OpenSemaphore(SemName : String; InitVal : Integer; + VAR SemHandle : LongInt; + VAR OpenCount : Word ):Boolean; + +{F220/01 [2.15? 3.x]} +FUNCTION ExamineSemaphore( SemHandle :LongInt; + VAR Value :Integer; + VAR OpenCount :Word ) :Boolean; +{ This functions returns the current value and open count of a semaphore.} + +{F220/02 [3.x]} +FUNCTION WaitOnSemaphore( SemHandle :LongInt; + Wait_Time :Word ) :Boolean; +{ Decrement the semaphore value and, if it is negative, } +{ wait until it becomes non-negative or until a timeout occurs. } + +{F220/03 [3.x]} +FUNCTION SignalSemaphore(SemHandle:LongInt) : Boolean; +{ Increment the semaphore value and release if waiting. } + +{F220/04 [3.x]} +FUNCTION CloseSemaphore(SemHandle:LongInt) : Boolean; +{ Decrement the open count of a semaphore.} +{ When the open count goes to zero, the semaphore is destroyed. } + + +{F217/F1 [2.15+? 3.x+]} +Function GetConnectionsSemaphores(ConnNbr:Word; + {i/o} Var seqNbr:Word; + {out} Var NbrOfSemaLeft:Byte; + {out} Var SemaInfo:TconnSema):Boolean; +{Caller needs console privileges } + +{F217/F2 [2.15? 3.x+]} +Function GetSemaphoreInformation(SemaName:String; + {i/o} Var seqNbr:word; + {out} Var OpenCount:word; + Var SemValue:Integer; + Var NbrOfSemaLeft:byte; + Var info:TsemaInfoList):Boolean; +{ Caller needs console privileges } + + +IMPLEMENTATION {=============================================================} + + +{F220/00 [3.x]} +Function OpenSemaphore(SemName : String; InitVal : Integer; + VAR SemHandle : LongInt; + VAR OpenCount : Word ):Boolean; +Type Treq=Record + subf:byte; + _InitVal:byte; + _SemNameLen:byte; + _SemName:array[0..127] of byte; + end; + Trep=record + _SemHandle:LongInt; + _OpenCount:Byte; + end; + TPreq=^Treq; + TPrep=^Trep; +begin +With TPreq(GlobalReqBuf)^ + do begin + subf:=$00; + If InitVal<0 + then _InitVal:=Lo(256+Initval) + else _InitVal:=Lo(InitVal); + UpString(SemName);SemName:=SemName+#0; + move(semName[1],_SemName[0],ord(SemName[0])); + _SemNameLen:=ord(semName[0])-1; + end; +F2SystemCall($20,SizeOf(treq),SizeOf(trep),result); +With TPrep(GlobalReplyBuf)^ + do begin + SemHandle:=Lswap(_SemHandle); + OpenCount:=_OPenCount; + end; +OpenSemaphore:=(result=0); +end; + + +{F220/02 [3.x]} +Function WaitOnSemaphore( SemHandle : LongInt; + Wait_Time : Word ) : Boolean; +{ Decrement the semaphore value and wait if it is negative. If negative,} +{ the workstation will wait until it becomes non-negative or until a } +{ timeout occurs. } +Type Treq=Record + subf:byte; + _SemHandle:Longint; + _wait :word; { hi-lo } + end; + TPreq=^Treq; +begin +With TPreq(GlobalReqBuf)^ + do begin + subf:=$02; + _semHandle:=Lswap(SemHandle); + _wait:=swap(wait_Time); + end; +F2SystemCall($20,SizeOf(treq),0,result); +WaitOnSemaphore:=(result=0); +end; + + +{F220/03 [3.x+]} +Function SignalSemaphore(SemHandle:LongInt) : Boolean; +{ Increment the semaphore value and release if waiting. If any stations} +{ are waiting, the station that has been waiting the longest will be } +{ signalled to proceed } +Type Treq=Record + subf:byte; + _semhandle:Longint; + end; + TPreq=^Treq; +begin +With TPreq(GlobalReqBuf)^ + do begin + subf:=$03; + _semHandle:=Lswap(SemHandle); + end; +F2SystemCall($20,SizeOf(treq),0,result); +SignalSemaphore:=(result=0); +end; + + +{F220/04 [3.x+]} +Function CloseSemaphore(SemHandle:LongInt) : Boolean; +{ Decrement the open count of a semaphore. When the open count goes } +{ to zero, the semaphore is destroyed. } +Type Treq=Record + subf:byte; + _semhandle:Longint; + end; + TPreq=^Treq; +begin +With TPreq(GlobalReqBuf)^ + do begin + subf:=$04; + _semHandle:=Lswap(SemHandle); + end; +F2SystemCall($20,SizeOf(treq),0,result); +CloseSemaphore:=(result=0); +end; + + +{F220/01 [2.x/3.x]} +FUNCTION ExamineSemaphore(SemHandle:LongInt; + VAR Value : Integer; + VAR OpenCount : Word ) : Boolean; +{ The semaphore value that comes back is the count from the open call } +{ - the open count is incremented } +{ anytime a station opens the semaphore this can be used for controlling } +{ the number of users using your software } +Type Treq=record + subf:byte; + _semHandle:Longint; + end; + Trep=record + _Value:Byte; + _OpenCount:Byte; + end; + TPreq=^Treq; + TPrep=^Trep; +BEGIN +With TPreq(GlobalReqBuf)^ + DO begin + subf:=$01; + _semHandle:=Lswap(SemHandle); + end; +F2SystemCall($20,SizeOf(Treq),SizeOf(Trep),result); +With TPrep(GlobalReplyBuf)^ + do begin + if (_Value and $80)>0 + then Value:=254-_Value + else Value:=_Value; + OpenCount:=_OpenCount; + end; +ExamineSemaphore := (result = 0); +END; + +{F217/F1 [2.15+? 3.x+]} +Function GetConnectionsSemaphores(ConnNbr:Word; + {i/o} Var seqNbr:Word; + {out} Var NbrOfSemaLeft:Byte; + {out} Var SemaInfo:TconnSema):Boolean; +{ To be called iteratively. Inital seqNbr=1. Iterate until seqNbr + becomes 0 (or until NbrOfSemaLeft becomes 0). + + This function can return information about several semaphores at the + same time. However, the size of the reply buffer is limited, causing + several as of now unsolvable problems. For now this function will + return information on a per semaphore basis. } +Type Treq=Record + len:word; + subf:byte; + _ConnNbr:word; {lo-hi} + _SeqNbr:word; {lo-hi} + end; + Trep=record + _NextSeqNbr:word; + _nbrOfSema:byte; { word (lo-hi) ? } + _unknown:byte; { -^ } + _SemaInfoBuf:array[1..508] of byte; + end; + TPreq=^Treq; + TPrep=^Trep; +Var i,t:Byte; +begin +With TPreq(GlobalReqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subf:=$F1; + _ConnNbr:=ConnNbr; + _SeqNbr:=SeqNbr; + end; +F2SystemCall($17,SizeOf(treq),SizeOf(trep),result); +if result=0 + then With TPrep(GlobalReplyBuf)^ + do begin + NbrOfSemaLeft:=(_NbrOfSema-1); + if NbrOfSemaLeft=0 + then seqNbr:=0 + else seqNbr:=seqNbr+1; { unfortunately, _NextSeqNbr returns no valid info. } + + Move(_SemaInfoBuf[1],SemaInfo,7+_SemaInfoBuf[7]); + With SemaInfo + do begin + Value:=swap(Value); + TaskNbr:=swap(TaskNbr); + end; + end; +GetConnectionsSemaphores:=(result=0); +{ 00 Successful C6 No console rights FD Bad connection number } +end; + +{F217/F2 [2.15? 3.x+]} +Function GetSemaphoreInformation(SemaName:String; + {i/o} Var seqNbr:word; + {out} Var OpenCount:word; + Var SemValue:Integer; + Var NbrOfSemaLeft:byte; + Var info:TsemaInfoList):Boolean; +Type Treq=Record + len:word; + subf:byte; + _seqNbr: word; + _semaName:string[127]; + end; + Trep=record + _NextSeqNbr:Word; + _OpenCount:word; + _SemValue:word; + _NbrOfRecords:word; + _SemaInfoBuf:array[1..514] of byte; + end; + TPreq=^Treq; + TPrep=^Trep; +begin +UpString(SemaName); +if SemaName[0]>#127 + then SemaName[0]:=#127; +With TPreq(GlobalReqBuf)^ + do begin + subf:=$F2; + _seqNbr:=seqNbr; + _SemaName:=SemaName; + len:=4+ord(_SemaName[0]); + end; +F2SystemCall($17,SizeOf(treq),SizeOf(trep),result); +With TPrep(GlobalReplyBuf)^ + do begin + OpenCount:=_OpenCount; + SemValue:=Integer(_SemValue); + NbrOfSemaLeft:=_NbrOfRecords; + move(_SemaInfoBuf,Info,SizeOf(TsemaInfoList)); + if NbrOfSemaLeft>100 + then seqNbr:=seqNbr+100 + else seqNbr:=0; + end; +GetSemaphoreInformation:=(result=0); +{ 00 Successful C6 No console rights } +end; + + +END. \ No newline at end of file diff --git a/NWTP/NWSERV.PAS b/NWTP/NWSERV.PAS new file mode 100644 index 0000000..db0ef45 --- /dev/null +++ b/NWTP/NWSERV.PAS @@ -0,0 +1,748 @@ +{$X+,B-,V-} {essential compiler directives} + +UNIT nwServ; + +{ nwServ unit as of 950301 / NwTP 0.6 API. (c) 1993,1995, R. Spronk } + +INTERFACE + +uses nwIntr,NwMisc,nwConn; + +Var result:word; + +{ Primary Functions: Interrupt: Comments: + +* CheckConsolePrivileges (F217/C8) +* ClearConnectionNumber (F217/D2) +* DisableFileServerLogin (F217/CB) +. DisableTransactionTracking (F217/CF) +* DownFileServer (F217/D3) +* EnableFileServerLogin (F217/CC) +. EnableTransactionTracking (F217/D0) +* GetConnectionsOpenFiles (F217/EB) + GetConnectionsUsingAFile (F217/EC) +* GetDiskUtilization (F217/0E) +* GetFileServerDateAndTime (F214) +* GetFileServerDescriptionStrings (F217/C9) +* GetFileServerLoginStatus (F217/CD) +* GetNetworkSerialNumber (F217/12) +* GetFileServerInformation (F217/11) +* SetFileServerDateAndTime (F217/CA) +* VerifyNetworkSerialNumber (F217/0C) + + Secondary functions: + +* CheckNetwareVersion + + Not supported by netware 3.x: (2.x only) + +- GetBinderyObjectDiskSpaceLeft (F217/E6) (failed during testing) +- GetConnectionsTaskInformation (E3../DA) +- GetConnectionsUsageStats (E3../E5) +- GetConnectionsUsingAFile (E3../DC) +- GetDiskCacheStats (E3../D6) +- GetDiskChannelStats (E3../D9) +- GetDriveMappingTable (E3../D7) +- GetFileServerLANIOStats (E3../E7) +- GetFileServerMiscInformation (E3../E8) +- GetFileSystemStats (E3../D4) +- GetLANDriverConfigInfo (E3../E3) +- GetLogicalRecordInformation (E3../E0) +- GetLogicalRecordsByConnection (E3../DF) +- GetPhysicalDiskStats (E3../D8) +- GetPhysicalRecordLocksByFile (E3../DE) +- GetPhysRecLocksByConnectAndFile (E3../DD) +} + + +Type TFileServerInformation + =Record + ServerName : string[48]; + NetwareVersion : Byte; + NetwareSubVersion : Byte; { 0..99 } + ConnectionsMax : word; + ConnectionsInUse : word; + MaxConnVol : word; { max connected volumes } + {---Advanced Netware 2.1x/3.x------------} + OS_revision : byte; + SFT_level : byte; + TTS_level : byte; + peak_conn_used : word; { max simult.used connections} + accounting_version : byte; + vap_version : byte; + queuing_version : byte; + print_server_version : byte; + virtual_console_version : byte; + security_restrictions_level : byte; + Internetwork_bridge_version : byte; + Undefined : Array [1..60] of Byte; + End; + +Type TfileInfoRecord=record + TaskNbr :Word; + LockType :Byte; { 00 no lock; FE file lock; FF locked by Begin Share File Set } + AccessFlag :Byte; + LockFlag :Byte; + VolNbr :Byte; { 0..31 } + ParentEntryId:Longint; + DirEntryId :Longint; + ForkCount :Byte; + NStype :Byte; + FileName :String; + end; + TfileInfoRecList=array[1..28] of TfileInfoRecord; + +Type TfileUsageList=record + UseCount :word; + OpenCount :word; + OpenForReadCount :word; + OpenForWriteCount:word; + DenyReadCount :word; + DenyWriteCount :word; + LockFlag :Byte; { boolean } + NStype :Byte; { Fork Count = File siz? of NStype? } + NbrOfRec :word; { max 70 } + FileUsage:array[1..70] of record + ConnNbr :word; + TaskNbr :word; + LockType :byte; + AccessFlag:Byte; + LockFlag :Byte; + end; + end; + +{F217/C8 [2.15c+]} +FUNCTION CheckConsolePrivileges : Boolean; + +{F217/D2 [2.15c+]} +Function ClearConnectionNumber(connectionNbr:byte):boolean; +{ Console Rights needed; + -Terminates a connection. } + +{F217/CB [2.15c+]} +FUNCTION DisableFileServerLogin : Boolean; + +{F217/CF [2.15c+]} +FUNCTION DisableTransactionTracking : Boolean; + +{F217/D3 [2.15c+]} +FUNCTION DownFileServer (ForceFlag : Boolean) : Boolean; + +{F217/CC [2.15c+]} +FUNCTION EnableFileServerLogin : Boolean; + +{F217/D0 [2.15c+]} +FUNCTION EnableTransactionTracking : Boolean; + + +{F217/EB [3.0+]} +FUNCTION GetConnectionsOpenFiles + ( ConnNumber : Byte; + {i/o:} var LastRecordSeen : word; + {out:} var NbrOfRecords : word; + var FileInfo : TfileInfoRecList ) : Boolean; + +{F217/0E [2.15c+]} +FUNCTION GetDiskUtilization(volNbr:byte; objID:Longint; + Var usedDirs,usedFiles,usedBlocks:Word ):Boolean; + +{F214 [2.15c+]} +FUNCTION GetFileServerDateAndTime ( Var time:TnovTime): Boolean; + +{F217/C9 [2.15c+]} +FUNCTION GetFileServerDescriptionStrings(Var companyName, + VersionAndRevision,revisionDate, + copyrightNotice:String + ):Boolean; + +{F217/CD [2.15c+]} +FUNCTION GetFileServerLoginStatus (Var LoginEnabled:Boolean): Boolean; +{ if Login is enabled then returns TRUE in LoginEnabled } + +{F217/12 [2.15c+]} +Function GetNetworkSerialNumber(Var serialNbr:LongInt; Var ApplicNbr:Word ):Boolean; +{return the serial number and application number for the software + installed on the file server} + +{F217/11 [2.15c+]} +Function GetFileServerInformation (Var serverInfo:TFileServerInformation):boolean; +{determine the version of software installed on the file server and how it is configured} + +{F217/CA [2.15c+]} +FUNCTION SetFileServerDateAndTime(time:TnovTime):Boolean; +{need console operator privileges to do this} + +{F217/OC [2.15c+]} +Function VerifyNetworkSerialNumber(serialNbr: LongInt ; + Var ApplicNbr: Word ):Boolean; +{if the network serial number to be verified is correct, the reply + buffer will contain the corresponding application number } + +{*********************** Secondary Functions ******************************} + +{ [1.x/2.x/3.x] } +FUNCTION CheckNetwareVersion(MinimumVersion,MinimumSubVersion, + MinimumRevision,MinimumSFT,MinimumTTS:word):Boolean; + +IMPLEMENTATION{=============================================================} + + +{F217/D2 [2.15c+]} +Function ClearConnectionNumber(connectionNbr:byte):boolean; +{ Console Rights needed; + -Terminates a connection. } +Type Treq=record + len : word; + subf: byte; + _connNbr:byte; + end; + TPreq=^Treq; +begin +With TPreq(GlobalReqBuf)^ +do begin + len:=2; + subf:=$D2; + _connNbr:=connectionNbr + end; +F2SystemCall($17,SizeOf(Treq),0,result); +ClearConnectionNumber:=(result=0); +{result codes: 00 successful; C6 No Console Rights} +end; + +{F214 [2.15c+]} +FUNCTION GetFileServerDateAndTime ( Var time:TnovTime): Boolean; +Type Trep=TnovTime; + TPrep=^Trep; +BEGIN +F2SystemCall($14,0,SizeOf(Trep),result); +Time:=TPrep(GlobalreplyBuf)^; +if time.year>100 + then time.year:=time.year-100; +{ year<80 : 21st century } +result:=0; +getFileServerDateAndTime:=TRUE; +end; + + +{F217/CA [2.15c+]} +FUNCTION SetFileServerDateAndTime (time:TnovTime): Boolean; +Type Treq=record + Len:word; + subF:byte; + _time:TnovTime + end; + TPreq=^Treq; +BEGIN +{ year<80 : 21st century } +WITH TPreq(GlobalReqBuf)^ +do begin + Len:=SizeOf(Treq)-3; { dow is not a parameter } + subF:=$CA; + _time:=time; + end; +F2SystemCall($17,SizeOf(Treq),0,result); +SetFileServerDateAndTime:=(Result=$00); +{ Resulcodes: $00 Success; $C6 No Console Operator Rights } +end; + + +{F217/11 [2.15c+]} +Function GetFileServerInformation (Var serverInfo:TFileServerInformation):boolean; +{determine the version of software installed on the file server and how it is configured} + +{SeeAlso: GetDiskUtilization, GetNetworkSerialNumber, GetFileServerLoginStatus, + GetFileServerDateAndTime} + +Type TReq=Record + Len : word; + SubF : Byte; + End; + TRep=TFileServerInformation; + TPreq=^Treq; + TPrep=^Trep; + +Var t:word; +Begin +With TPreq(GlobalReqBuf)^ +Do Begin + Len := 1; + SubF:= $11; + End; +F2SystemCall($17,SizeOf(Treq),SizeOf(Trep)-1,result); +Move(GlobalReplyBuf^[1],GlobalReplyBuf^[2],SizeOf(Trep)-1); +serverInfo:=TPrep(GlobalReplyBuf)^; +With serverinfo +do begin + connectionsMax :=Swap(connectionsMax); { force lo-hi again } + ConnectionsInUse:=Swap(connectionsInUse); + MaxConnVol :=Swap(maxConnVol); + peak_conn_used :=Swap(peak_conn_used); + for t:=48 downto 1 + do if serverInfo.serverName[t]=#0 + then serverInfo.serverName[0]:=chr(t-1); + end; +GetFileServerInformation:=(result=0); +End; + + + +{F217/C9 [2.15c+]} +FUNCTION GetFileServerDescriptionStrings(Var companyName, + VersionAndRevision,revisionDate, + copyrightNotice:String + ):Boolean; +{SeeAlso: GetFileServerLoginStatus, GetFileServerInformation. } +Type Treq=record + len : word; + subf: byte; + end; + Trep=record + stuff : array [1..512] of byte; + end; + TPreq=^Treq; + TPrep=^Trep; +Var x,xofs:word; +begin +With TPreq(GlobalReqBuf)^ +do begin + len := 1; + subf := $c9; + end; +F2SystemCall($17,SizeOf(Treq),SizeOf(Trep),result); +companyName:=''; VersionAndRevision:=''; +revisionDate:=''; copyrightNotice:=''; +if result=$00 + then with TPrep(GlobalReplyBuf)^ + do begin + x:=1;xofs:=x; + while (stuff[x]<>$00) and (x<512) do inc(x); + ZStrCopy(companyName,stuff[xofs],x-xofs); + + inc(x);xofs:=x; { skip 1 zero. ? skip more zero's? } + While (stuff[x]<>$00) and (x<512) do inc(x); + ZStrCopy(VersionAndRevision,stuff[xofs],x-xofs); + + inc(x);xofs:=x; + While (stuff[x]<>$00) and (x<512) do inc(x); + ZStrCopy(revisionDate,stuff[xofs],x-xofs); { mm/dd/yy } + + inc(x);xofs:=x; + While (stuff[x]<>$00) and (x<512) do inc(x); + ZStrCopy(copyrightNotice,stuff[xofs],x-xofs); + end; +GetFileServerDescriptionStrings:=(Result=$00); +end; + + + +{F217/D3 [2.15c+]} +FUNCTION DownFileServer (ForceFlag : Boolean) : Boolean; +Type Treq=record + len : word; + subf : byte; + flag : byte; + end; + TPreq=^Treq; +BEGIN +With TPreq(GlobalReqBuf)^ +do begin + len := 2; + subf := $D3; + if ForceFlag then flag := $FF { non-zero } + else flag := $00; + end; +F2SystemCall($17,SizeOf(Treq),0,result); +DownFileServer:=(result=0); +{ resultcodes: 00=successful; C6 No Console Rights ; FF Open Files} +end; + + +{F217/CF [3.x]} +FUNCTION DisableTransactionTracking : Boolean; +{ Caller must have console-operator rights. } +Type Treq=record + len : word; + subf: byte + end; + TPreq=^Treq; +BEGIN +With TPreq(GlobalReqBuf)^ + do begin + len := 1; + subf:= $CF; + end; +F2SystemCall($17,SizeOf(Treq),0,result); +DisableTransactionTracking:=(result=0); +{ resultcodes: 00=successful; C6 No Console Rights } +end; + + +{F217/D0 [3.x]} +FUNCTION EnableTransactionTracking : Boolean; +{ Caller must have console-operator rights. } +Type Treq=record + len : word; + subf: byte + end; + TPreq=^Treq; +BEGIN +With TPreq(GlobalReqBuf)^ + do begin + len := 1; + subf:= $D0; + end; +F2SystemCall($17,SizeOf(Treq),0,result); +EnableTransactionTracking:=(result=0); +{ resultcodes: 00=successful; C6 No Console Rights } +end; + + +{F217/CB [2.15c+]} +FUNCTION DisableFileServerLogin : Boolean; +{ Caller must have console-operator rights. } +Type Treq=record + len : word; + subf: byte + end; + TPreq=^Treq; +BEGIN +With TPreq(GlobalReqBuf)^ + do begin + len := 1; + subf:= $CB; + end; +F2SystemCall($17,SizeOf(Treq),0,result); +DisableFileServerLogin:=(result=0); +{ resultcodes: 00=successful; C6 No Console Rights } +end; + + + +{F217/CC [2.15c+]} +FUNCTION EnableFileServerLogin : Boolean; +{ Caller needs console-operator rights. } +Type Treq=record + len : word; + subf: byte + end; + TPreq=^Treq; +BEGIN +With TPreq(GlobalReqBuf)^ + do begin + len := 1; + subf:= $CC; + end; +F2SystemCall($17,SizeOf(Treq),0,result); +EnableFileServerLogin:=(result=0); +{ resultcodes: 00=successful; C6 No Console Rights } +end; + + + +{F217/CD [2.15c+]} +FUNCTION GetFileServerLoginStatus( Var LoginEnabled:Boolean ): Boolean; +{ if Login is enabled then returns TRUE in LoginEnabled } +{ result byte: 00h - Success, C6h No Console Rights } +{ Caller must have operator status.} +Type TReq=record + Len : Word; + SubF : Byte; + end; + TRep=record + Flag : Byte; + end; + TPreq=^Treq; + TPrep=^Trep; +begin +with TPreq(GlobalReqBuf)^ + do begin + Len := 1; + SubF := $CD; + end; +F2SystemCall($17,SizeOf(Treq),SizeOf(Trep),result); +LoginEnabled:=Boolean(TPrep(GlobalReplyBuf)^.Flag); +GetFileServerLoginStatus := (result=0); +end; + + +{F217/C8 [2.15c+]} +FUNCTION CheckConsolePrivileges : Boolean; +Type TReq=record + Len : Word; + SubF : Byte; + end; + TPreq=^Treq; +begin +with TPReq(GlobalReqBuf)^ + do begin + Len := 1; + SubF := $C8; + end; +F2SystemCall($17,SizeOf(Treq),0,result); +CheckConsolePrivileges := (Result=$00); +{ result byte: 00h - Success, C6h No Console Rights } +end; + + +{F217/EB [3.0+]} +FUNCTION GetConnectionsOpenFiles + ( ConnNumber : Byte; + {i/o:} var LastRecordSeen : word; + {out:} var NbrOfRecords : word; + var FileInfo : TfileInfoRecList ) : Boolean; + +{ the calling workstation must have console operator privileges } +{ LastRecordSeen is an i/o parameter; + -An initial value of 0 has to be supplied; + -The function can be called until LastRecordSeen becomes 0, + indicating the end of the FIR-list. + + to be called iteratively. } + +Type TReq=record + len :word; + subf :byte; + logicalConnNbr:word; + lastRecSeen :word; {lo-hi, $0000 on first call } + end; + TRep=record + nextReqRec : word; { lo-hi, use as lastRecSeen in next iterative call } + { $0000 if no more records } + RecCount : word; + FIRbuf:array[1..508] of byte; + end; + TPreq=^Treq; + TPrep=^Trep; +Var t,Foff:Word; +begin + +With TPreq(GlobalReqBuf)^ + do begin + len:=sizeof(Treq)-2; + subf:=$EB; + logicalConnNbr:=connNumber; + lastRecSeen:=LastRecordSeen; { force hi-lo } + end; +F2SystemCall($17,SizeOf(Treq),SizeOf(Trep),result); +if result=$00 + then with TPrep(GlobalReplyBuf)^ + do begin + { Copy recCount FIRs from FIRbuf to the FileInfo[] array } + LastRecordSeen:=NextReqRec; + NbrOfrecords:=RecCount; + Foff:=0; + For t:=1 to RecCount + do begin + Move(FIRbuf[1+Foff],FileInfo[t],17+FIRbuf[Foff+17]); + inc(Foff,17+FIRbuf[Foff+17]); + { Direntry and ParentEntry may have to be swapped lo-hi } + end; + end + else begin + NbrOfRecords:=0; + LastRecordSeen:=0; + end; +GetConnectionsOpenFiles:=(result=$00); +{ errorcodes: $00 Success; $C6 no console privileges } +end; + + +{F217/EC } +Function GetConnectionsUsingAFile(VolNbr:Byte; EntryId:Longint; NStype:byte; + {i/o} Var LastRecordSeen:word; + Var NbrOfRecords:Word; + Var FileInfo:TfileUsageList):boolean; +{ This call returns all connection numbers using the file specified + by the Volume Number and Directory Entry Id. } + +{ !! UNDER CONSTRUCTION !! } + +Type TReq=record + len :word; + subf :byte; + NStype :Byte; {= data stream type / Fork type } + VolNbr :Byte; + DirEntryId :Longint; + LastRecSeen:Word; {initially set to 0} + end; + + TRep=record + NextRec :word; { iteration } + + UseCount :word; + OpenCount :word; + OpenForReadCount :word; + OpenForWriteCount:word; + DenyReadCount :word; + DenyWriteCount :word; + LockFlag :Byte; { boolean } + NStype :Byte; { Fork Count -> ?? NStype } + NbrOfRec :word; { max 70 } + FileUsage:array[1..70] of record + ConnNbr :word; + TaskNbr :word; + LockType :byte; + AccessFlag:Byte; + LockFlag :Byte; + end; + end; + TPreq=^Treq; + TPrep=^Trep; +begin +With TPreq(GlobalReqBuf)^ + do begin + len:=sizeof(Treq)-2; + subf:=$EC; + + end; +F2SystemCall($17,SizeOf(Treq),SizeOf(Trep),result); +if result=$00 + then with TPrep(GlobalReplyBuf)^ + do begin + + end + else begin + NbrOfRecords:=0; + LastRecordSeen:=0; + end; +GetConnectionsUsingAFile:=(result=$00); +{ errorcodes: $00 Success; $C6 no console privileges } +end; + + +{F217/0E [2.15c+]} +FUNCTION GetDiskUtilization(volNbr:byte; objID:Longint; + Var usedDirs,usedFiles,usedBlocks:Word ):Boolean; +{ SeeAlso: GetFileServerInformation,getBinderyObjectDiskSpaceLeft } +Type TReq=record + Len : Word; + SubF : Byte; + _volNbr:Byte; + _objID:longInt; { hi-lo } + end; + TRep=record + _volNbr:Byte; + _objID:Longint; {hi-lo} + _usedDirs, + _usedFiles, + _usedBlocks:Word; { all hi-lo } + end; + TPreq=^Treq; + TPrep=^Trep; +begin +with TPReq(GlobalReqBuf)^ + do begin + Len := SizeOf(TReq)-2; + SubF := $0E; + _volNbr:=volNbr; + _objID:=Lswap(objID); + end; +F2SystemCall($17,SizeOf(Treq),SizeOf(Trep),result); +if result=$00 +then begin + with TPrep(GlobalReplyBuf)^ + do begin + usedDirs:=swap(_usedDirs); { force lo-hi } + usedFiles:=swap(_usedFiles); { force lo-hi } + usedBlocks:=swap(_usedBlocks);{ force lo-hi } + end; + end; +GetDiskUtilization:=(result=$00); +{Resultcodes: 00h successful; 98h volume doesn't exist + 89h No Search Privileges + F2h no Object read privileges } +end; + + +{F217/12 [2.15c+]} +Function GetNetworkSerialNumber(Var serialNbr:LongInt; Var ApplicNbr:Word ):Boolean; +{return the serial number and application number for the software + installed on the file server} +{SeeAlso: VerifyNetworkSerialNumber,GetFileServerInformation} +Type TReq=record + Len : Word; + SubF : Byte; + end; + TRep=record + _serNbr : LongInt; {hi-lo} + _applicNbr: Word; {hi-lo} + end; + TPreq=^Treq; + TPrep=^Trep; +begin +with TPreq(GlobalReqBuf)^ + do begin + Len := 1; + SubF := $12; + end; +F2SystemCall($17,SizeOf(Treq),SizeOf(Trep),result); +with TPrep(GlobalReplyBuf)^ + do begin + ApplicNbr:=swap(_applicNbr); { force lo-hi } + serialNbr:=Lswap(_serNbr); { force lo-hi } + end; +GetNetworkSerialNumber := (result=0); +end; + + + +{F217/OC [2.15c+]} +Function VerifyNetworkSerialNumber(serialNbr: LongInt ; + Var ApplicNbr: Word ):Boolean; +{if the network serial number to be verified is correct, the reply + buffer will contain the corresponding application number } +{SeeAlso: GetNetworkSerialNumber} +Type Treq=record + Len : Word; + SubF : Byte; + _netwSerNbr: LongInt; {hi-lo} + end; + TRep=record + _applicNbr: word; {hi-lo} + end; + TPreq=^Treq; + TPrep=^Trep; +begin +with TPReq(GlobalReqBuf)^ + do begin + Len := 1; + SubF := $0C; + _netwSerNbr:=Lswap(serialNbr); + end; +F2SystemCall($17,SizeOf(Treq),SizeOf(Trep),result); +with TPrep(GlobalReplyBuf)^ +do begin + ApplicNbr:=swap(_applicNbr); { force lo-hi } + end; +VerifyNetworkSerialNumber := (result=0); +end; + +{****************** secondary functions ************************************} + + +FUNCTION CheckNetwareVersion(MinimumVersion,MinimumSubVersion, + MinimumRevision,MinimumSFT,MinimumTTS:word):Boolean; +{ checks if the current OS/TTS/SFT version is greater or equal to the minimal version } +Var info:TFileServerInformation; + res:boolean; +begin +IF GetFileServerInformation(info) +then begin + IF (info.NetwareVersion>MinimumVersion) + then res:=true + else if (info.NetwareVersion=MinimumVersion) + AND (info.NetwareSubVersion>MinimumSubVersion) + then res:=true + else if (info.NetwareVersion=MinimumVersion) + AND (info.NetwareSubVersion=MinimumSubVersion) + AND (info.OS_Revision>=MinimumRevision) + then res:=true + else res:=false + end +else res:=false; + +CheckNetwareVersion:=res AND (info.SFT_Level>=MinimumSFT) + AND (info.TTS_Level>=MinimumTTS) +end; + +end. {unit nwServ} \ No newline at end of file diff --git a/NWTP/NWSPX.PAS b/NWTP/NWSPX.PAS new file mode 100644 index 0000000..8939fcc --- /dev/null +++ b/NWTP/NWSPX.PAS @@ -0,0 +1,315 @@ +{$B-,V-,X+} + +UNIT nwSPX; + +{ nwSPX unit as of 950301 / NwTP 0.6 API. (c) 1993,1995, R. Spronk } +{ NOTE: These SPX calls are not documented in this version } + +INTERFACE + +Uses Dos,nwMisc,nwIPX; + +{ Primary SPX calls: Subf: Comments: + + SPXabortConnection 14 + SPXGetConnectionStatus 15 + SPXestablishConnection 11 +* SPXinitialize 10 + SPXlistenForConnection 12 + SPXlistenForSequencedPacket 17 + SPXsendSequencedPacket 16 + SPXTerminateConnection 13 + + Secondary calls: + +* SPXpresent + + Notes: (1) These functions use INT 21 and are not to be called from + within an ESR. +} + +Var Result:word; { unit errorcode variable } + +Type TspxHeader=Record + IPXhdr :TipxHeader; { SPX will set packetType to 5 } + connControl :byte; { rarely used, set to $00 } + { ignored by SPX, but passed on to receiver: + $10 End of message; $20 Attention packet } + dataStreamType:Byte; { to be used by higher level protocols. + nust be < $FE, passed on to receiver } + sourceConnId, + destConnId :Word; + sequenceNbr, + acknowledgeNbr, + allocationNbr :Word; + end; + { Fields within IPX and SPX are high-low. Byte swapping will be done + by the IPX functions, except network and node addresses. } + +Type TSPXconnectionInformation + =record + ConnectionState :Byte; { all fields are returned hi-lo } + WatchDogState :Byte; + LocalSPXConnectionId :Word; + RemoteSPXConnectionId :Word; + SequenceNumber :Word; + LocalAcknowledgeNumber :Word; + LocalAllocationNumber :Word; + RemoteAcknowledgeNumber:Word; + RemoteAllocationNumber :Word; + LocalSocket :Word; + ImmediateAddress :TnodeAddress; + RemoteAddress :TinterNetworkAddress; + RetransmissionCount :Word; + EstimatedRoundTripDelay:Word; + RetransmittedPackets :Word; + SuppressedPackets :Word; + end; + +Function SPXpresent:boolean; +{ Determines if SPX is installed. Calls SPXInitialize. } + +{IPX/SPX: 10h} +Function SpxInitialize(Var SPXhiVer,SPXloVer:Byte; + Var MaxConn,AvailConn:word):boolean; +{ Determines if SPX is loaded. (this function also tests the presence of IPX, } +{ as IPX is required for running SPX. Remember: Netware 2.2 allows IPX and SPX } +{ to be loaded seperately, so only IPX may be present. } + +{IPX/SPX: 11h} +Function SPXestablishConnection(retryCount:byte; WatchdogFlag:Byte; + {i/o} Var ECB:Tecb; + {out} Var SPXconnectionID:Word):boolean; + +{IPX/SPX: 12h} +Function SPXlistenForConnection(retryCount:Byte; WatchdogFlag: Byte; + Var ECB:Tecb ):boolean; + +{IPX/SPX: 15h} +Function SPXGetConnectionStatus(SPXconnectionID:word; + Var connInfo:TSPXconnectionInformation):boolean; + +{IPX/SPX: 13h} +Function SPXTerminateConnection(SPXconnectionID:Word; ECB:Tecb):boolean; + +{IPX/SPX: 14h} +Function SPXabortConnection(SPXconnectionID:Word):boolean; + +{IPX/SPX: 16h} +Function SPXsendSequencedPacket(SPXconnectionID:Word; Var ECB:Tecb):boolean; + +{IPX/SPX: 17h} +Function SPXlistenForSequencedPacket(Var ECB:Tecb):boolean; + +{************** Secondary Procedures ***************************************} + +IMPLEMENTATION {==============================================================} + +CONST + SPX_WATCHDOG_ENABLED =1; + SPX_WATCHDOG_DISABLED =0; + SPX_DEFAULT_RETRY_COUNT =0; + + SPX_POOL_SIZE =10; + + SPX_MAX_DATA_LENGTH =534; + +{IPX/SPX: 10 } +Function SpxInitialize(Var SPXhiVer,SPXloVer:Byte; + Var MaxConn,AvailConn:word):boolean; +Var Regs:registers; +begin +With regs + do begin + al:=$00; + bx:=$0010; + IpxSpxSystemCall(Regs); + result:=regs.al; + + if AL<>$FF + then begin + result:=$FF; + SpxInitialize:=false + end + else begin + SPXhiVer:=BH; + SPXloVer:=BL; + MaxConn:=CX; + AvailConn:=DX; + result:=$00; + SpxInitialize:=true; + end; + end; {with} +{ resultcodes: $00 successfull (SPX installed); $FF SPX not installed } +end; + +Function SpxPresent:boolean; +Var SpxHi,SpxLo:Byte; + MaxConn,AvConn:word; +begin +SpxPresent:=( IpxPresent and SpxInitialize(SpxHi,SpxLo,MaxConn,AvConn) ); +end; + +{IPX/SPX: 11h} +Function SPXestablishConnection(retryCount:byte; WatchdogFlag:Byte; + {i/o} Var ECB:Tecb; + {out} Var SPXconnectionID:Word):boolean; +Var regs:registers; +begin +With regs + do begin + BX:=$0011; + AL:=retryCount; + AH:=WatchDogFlag; + ES:=Seg(ECB); + SI:=ofs(ECB); + end; +IpxSpxSystemCall(Regs); +result:=regs.AL; +SPXconnectionID:=regs.DX; +SPXestablishConnection:=(result=$00); +{ resultcodes: $00 SPX Attempting to contact destination socket; + $EF Local connection table full; + $FD Fragment count not 1 AND/OR Buffer size not 42; + $FF Send Socket not open OR IPX/SPX not initialized. } +end; + + +{IPX/SPX: 12h} +Function SPXlistenForConnection(retryCount:Byte; WatchdogFlag: Byte; + Var ECB:Tecb ):boolean; +Var regs:Registers; +begin +With regs + do begin + BX:=$0012; + AL:=retryCount; + AH:=WatchdogFlag; + ES:=Seg(ECB); + SI:=Ofs(ECB); + IpxSpxSystemCall(Regs); + result:=AL; + if result<>$FF + then result:=$00; + end; +SPXlistenForConnection:=(result=$00); +end; + +{IPX/SPX: 15h} +Function SPXGetConnectionStatus(SPXconnectionID:word; + Var connInfo:TSPXconnectionInformation):boolean; +Var regs:Registers; +begin +With regs + do begin + BX:=$0015; + DX:=SPXconnectionID; + ES:=Seg(connInfo); + SI:=Ofs(connInfo); + IpxSpxSystemCall(Regs); + Result:=AL; + end; +if result=0 + then begin + With ConnInfo + do begin { force all returned words lo-hi } + LocalSPXConnectionId :=Swap(LocalSPXconnectionID); + RemoteSPXConnectionId :=Swap(RemoteSPXconnectionID); + SequenceNumber :=swap(SequenceNumber); + LocalAcknowledgeNumber :=swap(LocalAcknowledgeNumber); + LocalAllocationNumber :=swap(LocalAllocationNumber); + RemoteAcknowledgeNumber:=swap(RemoteAcknowledgeNumber); + RemoteAllocationNumber :=swap(RemoteAllocationNumber); + LocalSocket :=swap(LocalSocket); + RemoteAddress.socket :=swap(remoteAddress.socket); + RetransmissionCount :=swap(RetransmissionCount); + EstimatedRoundTripDelay:=swap(EstimatedRoundTripDelay); + RetransmittedPackets :=swap(RetransmittedPackets); + SuppressedPackets :=swap(SuppressedPackets); + end; + end; +SPXGetConnectionStatus:=(result=0); +{ Resulcodes: $00 Connection is active; $EE No such connection } +end; + + +{IPX/SPX: 13h} +Function SPXTerminateConnection(SPXconnectionID:Word; ECB:Tecb):boolean; +Var regs:Registers; +begin +with regs + do begin + BX:=$0013; + DX:=SPXconnectionID; + ES:=seg(ECB); + SI:=Ofs(ECB); + end; +IpxSpxSystemCall(Regs); +result:=regs.al; +if result<>$FF + then result:=$00; +SPXterminateConnection:=(result=0); +{resultcodes: $00 SPX attempting to break connection; + $FF IPX/SPX not loaded. } +end; + +{IPX/SPX: 14h} +Function SPXabortConnection(SPXconnectionID:Word):boolean; +Var regs:Registers; +begin +with regs + do begin + BX:=$0014; + DX:=SPXconnectionID; + end; +IpxSpxSystemCall(Regs); +result:=regs.al; +if result<>$FF + then result:=$00; +SPXabortConnection:=(result=0); +{resultcodes: $00 SPX trying to unilateral break the connection; + $FF IPX/SPX not loaded. } +end; + +{IPX/SPX: 16h} +Function SPXsendSequencedPacket(SPXconnectionID:Word; Var ECB:Tecb):boolean; +Var regs:Registers; +begin +with regs + do begin + BX:=$0016; + DX:=SPXconnectionID; + ES:=Seg(ECB); + SI:=Ofs(ECB); + end; +IpxSpxSystemCall(Regs); +result:=regs.al; +if result<>$FF + then result:=$00; +SPXsendSequencedPacket:=(result=0); +{resultcodes: $00 SPX will attempt to send packet; + $FF IPX/SPX not loaded. } +end; + +{IPX/SPX: 17h} +Function SPXlistenForSequencedPacket(Var ECB:Tecb):boolean; +Var regs:Registers; +begin +with regs + do begin + BX:=$0017; + ES:=Seg(ECB); + SI:=Ofs(ECB); + end; +IpxSpxSystemCall(Regs); +result:=regs.al; +if result<>$FF + then result:=$00; +SPXlistenForSequencedPacket:=(result=0); +{resultcodes: $00 SPX waits for incoming packets; + $FF IPX/SPX not loaded. } +end; + +{************** Secondary Procedures ***************************************} + +end. diff --git a/NWTP/NWTP.FAQ b/NWTP/NWTP.FAQ new file mode 100644 index 0000000..38a5506 --- /dev/null +++ b/NWTP/NWTP.FAQ @@ -0,0 +1,198 @@ +FAQ for the NWTP API +==================== + +B01. How am I knwon at the server ? +B02. How do I give someone Console Operator rights ? + +C01. How can I reset the servertime ? +C02. How can I synchronize the workstation's time to that of the server ? + +L01. How can I place a limit on the number of concurrent users of my + program ? (licensing) + +M01. How can I send a message to another user ? + +S01. How do I determine whether or not I'm logged into a server ? +S02. Is the shell installed ? +S03. What is the name of the server I'm logged into ? + +******************************** Answers ******************************** + +B01. What name do I have ? +-------------------------- +A: You are known at the server by two names: a short (object) name + (i.e. the name you use to login) and a full name stored in the + bindery. + + Var SecurityLevel:Byte; + ObjectID:Longint; + ObjectType:Word; + ObjectName,LongName:String; + begin + GetBinderyAccessLevel(SecurityLevel,ObjectID); + GetBinderyObjectname(ObjectID,ObjectName,ObjectType); + writeln('You''re known to the server as:',ObjectName); + GetRealUserName(ObjectName,LongName); + writeln('And to the supervisor as:',LongName); + if LongName='' + then writeln(''); + end. + +B02. How do I give someone Console Operator rights? +--------------------------------------------------- +A: You'll have to add the ObjectID of the target user to the OPERATORS + property of the supervisor object in the bindery. Note that having + console operator rights differs significantly from being supervisor + equivalent. Console operator rights allow the user to perform actions + from a workstation that could also be done on the server console. + + Var UserName:string; + begin + UserName='Goofy'; + IF NOT CreateProperty('SUPERVISOR',OT_USER,'OPERATORS', + BF_SET or BF_STAT_OBJ, + BS_LOGGED_READ or BS_SUPER_WRITE) + then if nwBindry.result<>$ED { property already exists } + then begin + writeln('Error creating operators property.'); + Halt(1); + end; + + IF AddBinderyObjectToSet('SUPERVISOR',OT_USER,'OPERATORS', + UserName,OT_USER) + then writeln('User ',UserName,' is now a console operator.'); + end. + + +C01. How can I reset the servertime ? +------------------------------------- +A: Use the SetFileServerDateAndTime function in the nwServ unit. In order + for this call to be successful, you have to either be the supervisor + or have console operator privileges. + + Var newTime:TnovTime; + begin + WITH newTime + do begin + year:=94; month:=6; day:=1; + hour:=5; min:=0; sec:=0; + end; + IF NOT SetFileServerDateAndTime(newTime) + then writeln('You need to have console operator rights.'); + end. + +C02. How can I synchronize the workstation's time to that of the server ? +------------------------------------------------------------------------- +A: Use Pascal's SetTime and SetDate functions in combination with the + GetFileServerDateAndTime function. + + Var time:TnovTime; + year:word; + begin + GetFileServerDateAndTime(time); + if time.year<80 + then year:=2000+time.year + else year:=1900+time.year; + setdate(year,time.month,time.day); + settime(time.hour,time.minute,time.second,0); + end. + +L01. How can I place a limit on the number of concurrent users of my program ? +------------------------------------------------------------------------------ +A: Use the semaphore functions in the nwSema unit. Note that these enable + you to limit the number of concurrent users on a server basis. When a + user is refused access because the limit for his current fileserver is + exceeded, he can simply login to another fileserver (if available) and + try to use the program there. Limiting the number of concurrent users + on multiple servers in an internetwork is quite a problem: Novell doesn't + provide a method to synchronize semaphore values between servers. + + + CONST + INITIAL_SEMAPHORE_VALUE=10; + { suppose a licence for 10 concurrent users } + SEMAPHORE_NAME='YourProgramName'; + { anything goes, as long as it's unique. Max. 128 characters.} + + VAR openCount :Word; + semValue :Integer; + semHandle :LongInt; + + BEGIN {main} + + { Open Semaphore } + semValue := INITIAL_SEMAPHORE_VALUE; + { Need in case we're creating the semaphore } + IF NOT OpenSemaphore( SEMAPHORE_NAME, semValue, semHandle, openCount ) + then begin + writeln('Error opening semaphore. error #',nwSema.Result); + Halt(1); + end; + { Wait on the Semaphore (get permission to use the program) } + IF NOT WaitOnSemaphore( semHandle, 0 ) + then begin + if ( nwSema.Result = $FE ) + then begin + writeln( 'Sorry, license exceeded. Please try again later.' ); + halt(1); + end + else begin + writeln('WaitOnSemaphore returned eror# ',nwSema.result); + halt(1); + end; + end; + + { <===== INSERT YOUR to be licensed PROGRAM HERE =====> } + + { Signal Semaphore (that we're through with the program) } + SignalSemaphore( semHandle ); + { Close Semaphore } + CloseSemaphore( semHandle ); + end. + + + +M01. How can I send a message to another user ? +----------------------------------------------- +A: Use the SendMessageToUser function in the nwMess unit. This + function allows you to send a message to a user or to the members + of a group. If there are more than 64 members in the group, only the + first 64 members of the group will receive the message due to the way + this function is implemented on the server. + + var name : string; + message : string; + begin + name := 'MBRAMWEL'; + message := 'Hi Mark, how is it going?'; + SendMessageToUser(name,message); + SendMessageToUser('FINANCE_DEPT','Hand over the money..'); + SendMessageToUser('*','Goodmorning'); + end. + + Note that unlike the messages broadcasted by the SEND utility, the + source of the message is not automatically put in the message itself. + +S01. How do I determine whether or not I'm logged into a server ? +----------------------------------------------------------------- +A: Use the nwBindr IsUserLoggedOn function. + + +S02. Is the shell installed ? +---------------------------------------------- +A: Use the nwBindy IsShellLoaded function or query the variables + NETX_EXE_Loaded or VLM_EXE_Loaded. (see nwIntr) + + +S03. What is the name of the server I'm logged into ? +----------------------------------------------------- +A: If you already know that you're logged in: + + Var ConnId:Byte; + ServerName:string; + begin + GetEffectiveConnectionID(ConnId); + IF GetFileserverName(connId,ServerName) + then Writeln('You''re logged into server :',ServerName); + end. + diff --git a/NWTP/NWTP.TPH b/NWTP/NWTP.TPH new file mode 100644 index 0000000000000000000000000000000000000000..df60de8f097e99bc1f39aa0713721d283b29d4fb GIT binary patch literal 239613 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 literal 0 HcmV?d00001 diff --git a/NWTP/NWTP06.TXT b/NWTP/NWTP06.TXT new file mode 100644 index 0000000..81dee58 --- /dev/null +++ b/NWTP/NWTP06.TXT @@ -0,0 +1,9 @@ +Netware Interface Units for Pascal. +(Novell Netware 3.x and TP/BP 6.x/7.x) +Freeware. Full sources, over 300 kb. of +documentation and lots of examples. The +most complete API available for PASCAL. +KEYWORDS: LAN, Network, Novell, API, +Library, Bindery, IPX, SPX, Queue, +Tool, Reference, Workstation, Client, +Real Mode, Protected Mode, Windows. diff --git a/NWTP/README.1ST b/NWTP/README.1ST new file mode 100644 index 0000000..e65777d --- /dev/null +++ b/NWTP/README.1ST @@ -0,0 +1,22 @@ +Netware Interface Libraries for Borland/Turbo Pascal (NwTP) version 0.6 +======================================================================= + +A set of libraries (units) that allow Pascal programmers to write +Netware-aware programs. Includes functionalty ranging from reading +the bindery to multi-threaded peer tot peer communication. Supports +Real, DPMI, and Windows protected mode. Full sources included. Fully +documented. Ample examples. + +Required: -Knowledge of Pascal; + -BP/TP 6.x/7.x + -Netware 2.15c/2.2/3.x + +A few notes on some files: + +README.EXE Full documentation +NWTP .TPH Turbo Pascal helpfile, use within the 7.x IDE or with THELP. + See Readme.EXE for help on how to install. + +NW____.PAS Source code of the units +X_____.ZIP Source code of examples +REL .TXT Release note diff --git a/NWTP/README.EXE b/NWTP/README.EXE new file mode 100644 index 0000000000000000000000000000000000000000..83397e4ccf3c396c1687f6b6efcc71a2fd61b473 GIT binary patch literal 487800 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 z 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 S%JEsQr=rN;c5JFt;{OLP#u7yU literal 0 HcmV?d00001 diff --git a/NWTP/REL.TXT b/NWTP/REL.TXT new file mode 100644 index 0000000..76dfdf1 --- /dev/null +++ b/NWTP/REL.TXT @@ -0,0 +1,96 @@ +Netware Interface Libraries for Borland/Turbo Pascal (NwTP) +=========================================================== + +> Release note for NwTP Version 0.6 + + NwTP is a set of libraries (units) that allow Pascal + programmers to write Netware-aware programs. Included + functionalty ranges from reading the bindery to multi- + threaded IPX peer tot peer communication. + +> Some characteristics: + + -Over 250 interface functions / Full sources included + -Bindery, Connection, Workstation, Server, Message, + Semaphore, Locking, Accounting and Communication services + -More than 500 Kb of documentation (English) + -Turbo Pascal Helpfile (use within the IDE or with THELP) + -Real mode, DPMI and Windows protected mode supported + -Numerous examples & testprograms (over 200 kb.) + -Free software (GNU) + +If I may be so bold as to quote some users: + +> .... die Unit-Sammlung NwTP, da ist alles dabei, +> was ein Novell-Herz h”her schlagen l„sst :-)) + --------- + Wenn es NWTP nicht kostenlos geben wuerde, ich muessste es + glatt kaufen, vor allendingen, weil [der Author sich] die + Arbeit gemacht hat, Hilfs Files fuer Pascal dazu zu schreiben. + Ich muss sagen ich finde das Paket in seiner jetzigen + Ausbaustufe fuer mich (und bestimmt auch fuer andere) voll + gut. Es beinhaltet alles was ich fuers taegliche Novell + Client Leben brauche. + +> Een een echt goede gedocumenteerde unit voor Netware ! +> Ik heb er al naar gekeken en denk dat ik toch wel aardig +> wat aardigheidjes voor ons netwerk [..] kan maken :)) + ------- + Een heel interessante verzameling van routines die ik prima + kan gebruiken bij het schrijven van NetWare-aware programma's. + +> ..when NWTP wouldn't be free, I'd have to buy it, especially +> since help files are included. + -------- + .. the Unit collection NWTP, which has everything to make a Novell + minded heart beat somewhat faster ;-)) + +(Woops.. back to Earth again: ) + +>*** A few Notes: + + -NwTP V0.6, Netware API for TP. + Copyright (C) 1993,1995 by R.Spronk + NwTP is free software; The terms of the GNU General Public + License as published by the Free Software Foundation apply. + + -Minimum Development Environment + - NetWare 2.15c+ / 3.x + - Turbo/Borland Pascal Compiler (6.x/7.x) + +>*** Availability + +Anonymous FTP: + + novftp.rc.rug.nl:/proglibs/NWTP06.ZIP + +Filerequest/Downloads: + +Germany 04:30-02:30 CET [UTC+1] + Heinz Veenker NWTP (Magic Filename) + Smalltown BBS 2:2426/4030 V.FC/V.32B +49-5935-1224 + 2:2426/4031 ZyX+/V.32B +49-5935-1225 + 2:2426/4032 V.34/V.FC +49-5935-1226 + 2:2426/4033 ISDN X.75/V.110 +49-5935-925254 + Downloading of NWTP at first logon is supported. + +Germany 00:00-24:00 + Stefan Braunstein NWTP (Magic Filename) + Pandora BBS 2:2476/709, Zyxel E+ +49-781-65066 + 2:2476/719, Creatix 1400 (mail only system) + Downloading of NWTP at first logon is supported. + +The Netherlands 08:30-02:00 CET [UTC+1] + Rolf Mulder NWTP (Magic Filename) + Fawlty Towers 2:282/601 Tron 28k8 +31-50-717051 + +Osterreich 00:00-24:00 + Josef Braun NWTP (Magic filename) + Joe's BBS Corner 2:313/2 28k8 +43-2758-3357 + 2:313/22 19k2 +43-2758-3233 + No downloading at first logon. Use guest account. + +United Kingdom 03:30-02:30 GMT [UTC] + Michael Turner NWTP (Magic Filename) + Wizard's Haunt 2:254/80 +44-181-491-0795 + Downloading of NWTP at first logon is supported. diff --git a/NWTP/THELP.CFG b/NWTP/THELP.CFG new file mode 100644 index 0000000..0a59368 --- /dev/null +++ b/NWTP/THELP.CFG @@ -0,0 +1,3 @@ +/fC:\BP\BIN\TURBO.TPH +/fC:\BP\BIN\TVISION.TPH +/fC:\BP\BIN\NWTP.TPH diff --git a/NWTP/THELP.COM b/NWTP/THELP.COM new file mode 100644 index 0000000000000000000000000000000000000000..e4f9ebc6b4a1e572e17b913714b5b7ebf9c30cef GIT binary patch literal 11088 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 + then begin + writeln('Accounting file couldn''t be found..'); + halt(1); + end; + +REPEAT + read(f,b); {length of record} + read(f,b2); + If IOresult<>0 then goto q; + buf[1]:=b2; + buf[2]:=b; + for t:=1 to NoteAKA.Length + do begin + read(f,buf[t+2]); + if IOresult>0 then goto q; + end; + IF (NoteAKA.RecType=RT_ACCOUNT_NOTE) and (swap(NoteAKA.CommentType) IN [3,4]) + then with NoteAKA + do begin + If swap(CommentType)=3 + then write('Logon of ') + else write('Logoff of '); + write(HexStr(Lswap(ClientObjId),8)); + write(' from address ',HexDumpStr(Comment.Net,8),':', + HexDumpStr(Comment.Node,12)); + Move(TimeStamp,ntime.year,6); + ntime.dayOfWeek:=0; + NovTime2String(ntime,s);delete(s,1,4); + write(' at: ',s); + + writeln; + end; +UNTIL eof(f); + +q: ; +close(f); +end. diff --git a/NWTP/XACCT/TSTACCT.PAS b/NWTP/XACCT/TSTACCT.PAS new file mode 100644 index 0000000..af08452 --- /dev/null +++ b/NWTP/XACCT/TSTACCT.PAS @@ -0,0 +1,171 @@ +{$X+,V-,B-} +Program tstacct; {as of 940601} + +{ Testprogram for the nwAcct unit / NwTP 0.5 API. (c) 1993,1994, R.Spronk } + +{ Tests the following nwAcct functions: + + AccountingInstalled + AddAccountingServer + DeleteAccountHolds + DeleteAccountingServer + GetAccountStatus + SetAccountStatus + SubmitAccountCharge + SubmitAccountHold + SubmitAccountNote +} + +uses nwBindry,nwConn,nwAcct; + +CONST testObjName='TEST'; + +Var connId :byte; + currServer:string; + balance,limit,holds :Longint; + newBalance,newLimit :LongInt; + nCharge,nCancelHoldAmount:Longint; + oldBalance,Oldholds :LongInt; + +begin +writeln('Test of the accounting functions.'); +writeln; +writeln('You have to be logged in as SUPERVISOR'); +writeln('User ',testObjName,' has to exists. (hardcoded in source, change if needed).'); +writeln; +If NOT AccountingInstalled + then begin + writeln('error#:',nwAcct.result); + write('Err:Accounting isn''t installed on the current effective server:'); + GetEffectiveConnectionID(ConnID); + GetFileServerName(connId,currServer); + writeln(currServer); + halt(1); + end; + +AddAccountingServer('SUPERVISOR',OT_USER); + + +IF GetAccountStatus(testObjName,OT_USER,balance,limit,holds) + then begin + writeln('Current account status for user ',testObjName,' :'); + writeln('Balance :',balance); + writeln('Credit Limit:',limit); + writeln('Holds :',holds); + end + else writeln('Err: GetAccountStatus failed. error #',nwAcct.result); + +writeln('Setting new account values..'); +newBalance:=1020304; +newLimit:=123456; +IF NOT SetAccountStatus(testObjName,OT_USER,newBalance,newLimit) + then writeln('Err: SetAccountStatus failed. error #',nwAcct.result); + +IF GetAccountStatus(testObjName,OT_USER,balance,limit,holds) + then begin + writeln('Current account status for user ',testObjName,' :'); + writeln('Balance :',balance); + writeln('Credit Limit:',limit); + writeln('Holds :',holds); + if (balance<>newBalance) or (newLimit<>Limit) + then writeln('Err: the new account values where not set!'); + end + else writeln('Err: GetAccountStatus failed. error #',nwAcct.result); + +OldBalance:=balance; +nCharge:=100; +nCancelHoldAmount:=0; +Writeln('Submitting an account charge. charge=',ncharge,',CancelHold=',ncancelholdamount); +IF NOT SubmitAccountCharge(testObjName,OT_USER,nCharge,nCancelHoldAmount, + OT_USER,0,'no note') + then writeln('Err: SubmitAccountCharge failed. error #',nwAcct.result); + +IF GetAccountStatus(testObjName,OT_USER,balance,limit,holds) + then begin + writeln('Current account status for user ',testObjName,' :'); + writeln('Balance :',balance); + writeln('Credit Limit:',limit); + writeln('Holds :',holds); + if (balance<>(oldBalance-nCharge)) + then writeln('Err: the account charge was not carried out !'); + end + else writeln('Err: GetAccountStatus failed. error #',nwAcct.result); + + +OldBalance:=balance; +nCharge:=-200; +nCancelHoldAmount:=0; +Writeln('Submitting a NEGATIVE account charge. charge=',ncharge,',CancelHold=',ncancelholdamount); +Writeln(' (in fact increasing the balance of the ',testObjName,' object.'); +IF NOT SubmitAccountCharge(testObjName,OT_USER,nCharge,nCancelHoldAmount, + OT_USER,99,'charge! charge!') + then writeln('Err: SubmitAccountCharge failed. error #',nwAcct.result); + +IF GetAccountStatus(testObjName,OT_USER,balance,limit,holds) + then begin + writeln('Current account status for user ',testObjName,' :'); + writeln('Balance :',balance); + writeln('Credit Limit:',limit); + writeln('Holds :',holds); + if (balance<>(oldBalance-nCharge)) + then writeln('Err: the account charge was not carried out !'); + end + else writeln('Err: GetAccountStatus failed. error #',nwAcct.result); + +OldHolds:=holds; +writeln('Submit an hold in the amount of 1234'); +IF SubmitAccountHold(testObjName,OT_USER,1234) + then begin + GetAccountStatus(testObjName,OT_USER,balance,limit,holds); + writeln('Current account status for user ',testObjName,' :'); + writeln('Balance :',balance); + writeln('Credit Limit:',limit); + writeln('Holds :',holds); + + if (holds<>(OldHolds+1234)) + then writeln('Err: the account hold was not carried out !'); + end + else writeln('Err: SubmitAccountHold failed. error #',nwAcct.result); + +Writeln('Submit a charge of 1000, unhold 1234'); +OldHolds:=holds; +nCharge:=1000; +nCancelHoldAmount:=1234; +IF SubmitAccountCharge(testObjName,OT_USER,nCharge,nCancelHoldAmount, + OT_USER,99,'let''s charge a few bucks :-)') + then begin + GetAccountStatus(testObjName,OT_USER,balance,limit,holds); + writeln('Current account status for user ',testObjName,' :'); + writeln('Balance :',balance); + writeln('Credit Limit:',limit); + writeln('Holds :',holds); + + if (holds<>(OldHolds-1234)) + then writeln('Err: the account hold was not carried out !'); + end + else writeln('Err: SubmitAccountHold failed. error #',nwAcct.result); + +IF DeleteAccountHolds(testObjName,OT_USER) + then begin + writeln('All further holds bythis accounting server were released.'); + GetAccountStatus(testObjName,OT_USER,balance,limit,holds); + writeln('Current account status for user ',testObjName,' :'); + writeln('Balance :',balance); + writeln('Credit Limit:',limit); + writeln('Holds :',holds); + end + else writeln('Err: DeleteAccountHolds failed. error #',nwAcct.result); + +IF SubmitAccountNote(testObjName,OT_USER, + OT_USER,99,'<>') + then begin + writeln('Accountnote was submitted correctly.'); + writeln('Please use a hexeditor and have look at \system\net$acct.dat'); + writeln('to check if the accountnote was added to the file.'); + end + else writeln('Err: SubmitAccountNote failed. error #',nwAcct.result); + +IF NOT DeleteAccountingServer('SUPERVISOR',1) + then writeln('Err: DeleteAccountServer failed. error #',nwacct.result); + +end. \ No newline at end of file diff --git a/NWTP/XBINDRY/BACKBIN.PAS b/NWTP/XBINDRY/BACKBIN.PAS new file mode 100644 index 0000000..19c8442 --- /dev/null +++ b/NWTP/XBINDRY/BACKBIN.PAS @@ -0,0 +1,143 @@ +{$X+,B-,V-,S-,I-} {essential compiler directives} + +Program Backbin; { as of 950301} + +{ Example for the nwBindry unit / NwTP 0.6 API. (c) 1994,1995 R.Spronk } + +{ Purpose: Backs up the bindery files to the a: drive. } +{ You need to be supervisor-equivalent to run this. } + +{ Tests the following nwBindry calls: + + OpenBindery + CloseBindery +} + +Uses DOS,nwMisc,nwBindry; + +Var BindSeq:byte; + myObjID:LongInt; + Version:word; + s :string; + +LABEL enable; + +Procedure FileCopy(fn1,fn2:string); +LABEL 1; +Var f1,f2 :file; + buffer :array[1..4096] of byte; + BytesRead:word; +begin +assign(f1,fn1); +reset(f1,1); +if ioresult<>0 + then begin + writeln('Error opening input file:',fn1); + writeln('>> File wasn''t copied.'); + goto 1; + end; +assign(f2,fn2); +rewrite(f2,1); +if ioresult<>0 + then begin + writeln('Error opening output file :',fn2); + writeln('>> File wasn''t copied.'); + goto 1; + end; +REPEAT +BlockRead(f1,buffer[1],4096,BytesRead); +BlockWrite(f2,buffer[1],BytesRead); +UNTIL (BytesRead<4096); +1: ; +close(f1); +close(f2); +{$I+} +end; + + +begin +writeln('BACKBIN Test program for the nwBindry unit of the NwTP package.'); +writeln('-Backs up the bindery files to drive A'); +writeln; + +IF NOT IsShellLoaded + then begin + writeln('Load network shell before executing this program.'); + halt(1); + end; + +{ need supervisor privileges to run this test } +GetBinderyAccessLevel(BindSeq,myObjId); +if bindSeq<>(BS_SUPER_WRITE OR BS_SUPER_READ) { $33} + then begin + writeln('You need to be supervisor equivalent to run this program.'); + halt(1); + end; + +writeln; +writeln('WARNING:'); +writeln('Continue this program ONLY WHEN:'); +writeln('-You are the only user logged in.'); +writeln('-No other users will login in next few minutes.'); +writeln('-You are running Netware 2.15c, 2.2 or 3.x'); +writeln('-A diskette with sufficient space is in the A drive.'); +writeln; +writeln('-If you have doubts about any of the above points: PLEASE ABORT NOW'); +writeln('-This program was made for test purposes only.'); +writeln; +write('Type Y to continue, to abort.'); +readln(s); +if NOT ( s[1] IN ['y','Y']) + then begin + writeln('Program aborted..'); + halt(1); + end; + +{determine Advanced Netware version. } +nwMisc.GetNWVersion(version); + +if (Version<215) or (version>399) + then begin + writeln('This util only works with NW 2.15+ or 3.x'); + writeln('The bindery files were NOT backed up.'); + halt(1); + end; +version:=(version DIV 100); + +{Note for final version: make sure no users are logged in. (NwConn functions)} +{Note for final version: disable user login (see nwServ) } + +{close the bindery to backup the files..} +IF NOT CloseBindery + then begin + writeln('Couldn''t close the bindery.'); + writeln('The bindery files were NOT backed up'); + writeln('Error : $',HexStr(NwBindry.Result,2)); + goto enable; + end; + +{back up the bindery files} +if version=2 + then begin + FileCopy('SYS:\SYSTEM\NET$BIND.SYS','A:\NET$BIND.OLD'); + FileCopy('SYS:\SYSTEM\NET$BVAL.SYS','A:\NET$BVAL.OLD'); + end + else begin {3.x} + FileCopy('SYS:\SYSTEM\NET$OBJ.SYS' ,'A:\NET$OBJ.OLD'); + FileCopy('SYS:\SYSTEM\NET$PROP.SYS','A:\NET$PROP.OLD'); + FileCopy('SYS:\SYSTEM\NET$VAL.SYS' ,'A:\NET$VAL.OLD'); + end; + +{open the bindery again} +IF NOT OpenBindery + then begin + writeln('Couldn''t open the bindery after copying the bindery files.'); + writeln('Error : $',HexStr(NwBindry.Result,2),' (',NwBindry.result,')'); + end + else writeln('The bindery files were successfully backed up.'); + +{Note for final version: enable users to login (see nwServ) } + +enable: ; + +end. diff --git a/NWTP/XBINDRY/NEW.TXT b/NWTP/XBINDRY/NEW.TXT new file mode 100644 index 0000000..0da337e --- /dev/null +++ b/NWTP/XBINDRY/NEW.TXT @@ -0,0 +1,69 @@ +ScanBind V1.2 +Provides information about all accessible bindery objects. +All objects with a read security level <= Sup (3) will be shown. + +01000001 EVERYONE +The object type is :User group +The object is a static object. +Security: Read: Log (1) / Write: Sup (3) +The object has the following properties: + GROUP_MEMBERS (Static Set-Property) + Security: Read: Log (1) /Write: Sup (3) + *SUPERVISOR (User)(00000001) + *DALEK (User)(00010002) + *TARDIS (User)(00010003) + +00010002 DALEK +The object type is :User +The object is a static object. +Security: Read: Log (1) / Write: Sup (3) +The object has the following properties: + UNIX_USER (Static Item-Property) + Security: Read: Any (0) /Write: Sup (3) + *64 61 6C 65 6B 00 00 00 00 00 00 00 00 00 00 00 dalek + SECURITY_EQUALS (Static Set-Property) + Security: Read: Obj (2) /Write: Sup (3) + *EVERYONE (Group)(01000001) + GROUPS_I'M_IN (Static Set-Property) + Security: Read: Log (1) /Write: Sup (3) + *EVERYONE (Group)(01000001) + +03000001 DOCTORWHO +The object type is :Fileserver +The object is a dynamic object. +Security: Read: Any (0) / Write: Netw(4) +The object has the following properties: + NET_ADDRESS (Static property), Property type= 1 (Unknown, not Item or Set) + Security: Read: Any (0) /Write: Netw(4) + *C0 A8 01 02 00 00 00 00 00 01 04 51 00 00 00 00 À¨ Q + +00000001 SUPERVISOR +The object type is :User +The object is a static object. +Security: Read: Log (1) / Write: Sup (3) +The object has the following properties: + GROUPS_I'M_IN (Static Set-Property) + Security: Read: Log (1) /Write: Sup (3) + *EVERYONE (Group)(01000001) + UNIX_USER (Static Item-Property) + Security: Read: Any (0) /Write: Sup (3) + *72 6F 6F 74 00 00 00 00 00 00 00 00 00 00 00 00 root + SECURITY_EQUALS (Static Set-Property) + Security: Read: Obj (2) /Write: Sup (3) + *EVERYONE (Group)(01000001) + +00010003 TARDIS +The object type is :User +The object is a static object. +Security: Read: Log (1) / Write: Sup (3) +The object has the following properties: + GROUPS_I'M_IN (Static Set-Property) + Security: Read: Log (1) /Write: Sup (3) + *EVERYONE (Group)(01000001) + UNIX_USER (Static Item-Property) + Security: Read: Any (0) /Write: Sup (3) + *74 61 72 64 69 73 00 00 00 00 00 00 00 00 00 00 tardis + SECURITY_EQUALS (Static Set-Property) + Security: Read: Obj (2) /Write: Sup (3) + *EVERYONE (Group)(01000001) + diff --git a/NWTP/XBINDRY/NEW2.TXT b/NWTP/XBINDRY/NEW2.TXT new file mode 100644 index 0000000..5bce7d7 --- /dev/null +++ b/NWTP/XBINDRY/NEW2.TXT @@ -0,0 +1,53 @@ +ScanBind V1.2 +Provides information about all accessible bindery objects. +All objects with a read security level <= Sup (3) will be shown. + +01000001 EVERYONE +The object type is :User group +The object is a static object. +Security: Read: Log (1) / Write: Sup (3) +The object has the following properties: + GROUP_MEMBERS (Static Set-Property) + Security: Read: Log (1) /Write: Sup (3) + *SUPERVISOR (User)(00000001) + *DALEK (User)(00010002) + +00010002 DALEK +The object type is :User +The object is a static object. +Security: Read: Log (1) / Write: Sup (3) +The object has the following properties: + UNIX_USER (Static Item-Property) + Security: Read: Any (0) /Write: Sup (3) + *64 61 6C 65 6B 00 00 00 00 00 00 00 00 00 00 00 dalek + SECURITY_EQUALS (Static Set-Property) + Security: Read: Obj (2) /Write: Sup (3) + *EVERYONE (Group)(01000001) + GROUPS_I'M_IN (Static Set-Property) + Security: Read: Log (1) /Write: Sup (3) + *EVERYONE (Group)(01000001) + +03000001 DOCTORWHO +The object type is :Fileserver +The object is a dynamic object. +Security: Read: Any (0) / Write: Netw(4) +The object has the following properties: + NET_ADDRESS (Static property), Property type= 1 (Unknown, not Item or Set) + Security: Read: Any (0) /Write: Netw(4) + *C0 A8 01 02 00 00 00 00 00 01 04 51 00 00 00 00 À¨ Q + +00000001 SUPERVISOR +The object type is :User +The object is a static object. +Security: Read: Log (1) / Write: Sup (3) +The object has the following properties: + GROUPS_I'M_IN (Static Set-Property) + Security: Read: Log (1) /Write: Sup (3) + *EVERYONE (Group)(01000001) + UNIX_USER (Static Item-Property) + Security: Read: Any (0) /Write: Sup (3) + *72 6F 6F 74 00 00 00 00 00 00 00 00 00 00 00 00 root + SECURITY_EQUALS (Static Set-Property) + Security: Read: Obj (2) /Write: Sup (3) + *EVERYONE (Group)(01000001) + diff --git a/NWTP/XBINDRY/NONAME00.EXE b/NWTP/XBINDRY/NONAME00.EXE new file mode 100644 index 0000000000000000000000000000000000000000..25fa4b33b4715c462bbdf3e51710ec1d8ff1de27 GIT binary patch literal 3904 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 K0n_z;-+uwd&w|PT literal 0 HcmV?d00001 diff --git a/NWTP/XBINDRY/NWPN9401.TXT b/NWTP/XBINDRY/NWPN9401.TXT new file mode 100644 index 0000000..c381a25 --- /dev/null +++ b/NWTP/XBINDRY/NWPN9401.TXT @@ -0,0 +1,199 @@ +NWTP Note 0501 "About the encryption mechanism" + +This note discribes the password encryption mechanisms as used by Novell +Netware 3.x/4.x. Passwords are encrypted by workstations +and servers. The password verification process is also based on encryption. + +This note describes the entire process of password verification and +password change. It has 3 appendices: +1. A description of the ASM calls involved with encrypted passwords; +2. Sourcecode of the encryption routines and tables in Turbo Pascal; +3. Sourcecode of the encryption routines and tables in C. + + Initial state + ============= + + Our transaction model starts with a description of the initial state + of the server. + + The following tables are stored at the server: + + -The EncryptionKeyTable, containing an 8 byte EncryptionKey for + every workstation connection. This table can be queried by + workstations using the GetEncryptionKey (INT 21h, AX=F217h, subf. 17h). + Table entry [c] is renewed whenever connection c used a call using + encrypted passwords. + + -The PasswordTable containing an 16 Byte encrypted password + (socalled "Shuffled" Password) for every connection. + + A short description of the various encryption mechanisms involved: + + -Shuffling. ('S' for short) + The encryption of the password (string of char) into 16 bytes (the + shuffled password), using a number of static tables and the objectID + of the object the password is associated with. + + In Mathematical terms: + ShuffledPassWord=S(PasswordString) or Spw=S(pw) + + -Encryption ('E' for short) + The main encryption process, encrypting the shuffled password (S(pw) + for short) into 8 bytes, using the same static tables as the Shuffling + functions and a dynamic encryption key requested from the server. + + In mathematical terms: + EncryptedPassWord=E(EncryptionKey,ShuffledPassword) or Epw=E(Ekey,Spw) + + -EncryptDifference ('D' for short) + Encrypts the 'difference' between the Shuffled old password and the + Shuffled new password, using a static table. The encrypted difference + is passed to the server. The computed 'difference' consist of + 16 bytes of data and 1 checksum byte. + + In mathematical terms: + PasswordDiff=D(ShuffledOldPW,ShuffledNewPW) or pwDiff=D(SOpw,SNpw) + + -DecryptDifference ('Dinv' for short) + Server decryption process. Decrypts a 'password-difference' + encrypted block as suplied by a workstation to a shuffled version + of the new password. The shuffled old password, as stored in the + servers' EncryptionKeyTable is used in the decryption process. + + In mathematical terms: + ShuffledNewPW=Dinv(ShuffledOldPW,PasswordDiff) or SNpw=Dinv(SOpw,pwDiff) + Notes:-ShuffledOldPassword is taken by the server from its' + EncryptionKeyTable, i.e. ShuffledOldPassword=EncryptionKeyTable[c] + where c is the connection number of the object the password + is associated with. + -SNpw=Dinv(SOpw,D(SNpw,Opw)), hence the name "D inverse". + + -GenerateNewKey ('GNK' for short) + The server process creating a new encryption key for a certain + connection after the previous one has been used by that connection. + + In mathematical terms: + EncryptionKeyTable[c]:=GenerateNewKey(EncryptionTable[c]) + + + Password Verification + ===================== + + Password verification procedure when the encrypted password calls + (VerifyEncrBinderyObjectPassword and LoginEncrToFileServer) + are used: + + Workstation Server + =========== ====== + + GetEncryptionKey ----------------> Return EncryptionKeyTable[c] + + EncrKey <--------------------- + + Epw:=E(EncrKey,S(pw)) + + Verify/Login(Epw) ---------------> Epw'=E(EncryptionKeyTable[c], + PasswordTable[c]) + + Epw'=Epw ? + completion code <-------------------- + + EncryptionKeyTable[c]= + GNK(EncryptionKeyTable[c]) + + Note: c = Workstation connection number + pw = Password (string, max. 128 characters) + Epw= Encrypted Password (8 bytes) + + + Password verification procedure when the calls using unencrypted + passwords (VerifyBinderyObjectPassword and LoginToFileserver) + are used in combination with a 3.x server: + + Workstation Server + =========== ====== + + + Verify/Login(pw) ----------------> + + S(pw)=PasswordTable[c] ? + completion code <-------------------- + + EncryptionKeyTable[c]= + GNK(EncryptionKeyTable[c]) + + Note: c = Workstation connection number + pw = Password (string, max. 128 characters) + + + + Changing Passwords + ================== + + The process of changeing a password using encrypted passwords: + + Workstation Server + =========== ====== + + GetEncryptionKey ----------------> Return EncryptionKeyTable[c] + + EncrKey <--------------------- + + SOpw=S(oldPW) + SNpw=S(newPW) + + EOpw=E(encrKey,SOpw) + + PWdif=D(SNpw,SOpw) + + ChangeEncrBinderyObjPW + (EOpw,PWdiff) + --------------> Epw'=E(EncryptionKeyTable[c], + PasswordTable[c]) + + Epw'=Epw ? + completion code <-------------------- + + SNpw'=Dinv(PasswordTable[c], + PWdiff) + PasswordTable[c]=SNpw' + + EncryptionKeyTable[c]= + GNK(EncryptionKeyTable[c]) + + + Note: c = Workstation connection number + OldPW = Old password (string, max. 128 characters) + NewPW = New password (string, max. 128 characters) + SNpw = Shuffled new password (12 bytes) + SOpw = Shuffled old password (12 bytes) + EOpw = Encrypted old Password (8 bytes) + + + THe process of chageing a password when using unencrypted passwords: + + Workstation Server + =========== ====== + + + ChangeBinderyObjPW + (OldPW,NewPW) + --------------> + S(OldPW)=PasswordTable[c] ? + completion code <-------------------- + + PasswordTable[c]=S(NewPW) + + EncryptionKeyTable[c]= + GNK(EncryptionKeyTable[c]) + + + Note: c = Workstation connection number + OldPW = Old password (string, max. 128 characters) + NewPW = New password (string, max. 128 characters) + + +Sources: NVPW.C by Itsme [Itsme@Hacktic.nl] + LOGON.PAS by Barry Nance/Terje Mathesen, Byte, March 1993. + + diff --git a/NWTP/XBINDRY/OT_XXX b/NWTP/XBINDRY/OT_XXX new file mode 100644 index 0000000..8d615d8 --- /dev/null +++ b/NWTP/XBINDRY/OT_XXX @@ -0,0 +1,1212 @@ +# ----------------------------------------------------------------- +# OT_XXX: Known Server/Object types +# ----------------------------------------------------------------- + +0000 Unknown Novell Inc. # used as a reply on queries +0001 User Novell Inc. +0002 User Group Novell Inc. +0003 Print Queue Novell Inc. +0004 File Server Novell Inc. +0005 Job Server Novell Inc. +0006 Gateway Novell Inc. +0007 Print Server Novell Inc. +0008 Archive Queue Novell Inc. +0009 Archive Server Novell Inc. +000A Job Queue Novell Inc. +000B Adminsitration Novell Inc. +0021 SNA Gateway National Advanced Systems +0022 Sperry Corp. Computer Systems +0023 ACS2 KTA +0024 Remote Bridge Server Novell Inc. +0025 Data Language Corp +0026 Async Bridge Server ? Computer Logics +0027 TCP/IP Gateway ? Santa Clara Systems/ Racal Interlan ? +0028 X25 Bridge Point-Point Eicon Technology +0029 X25 Gateway Multi-Point Eicon Technology +002A Chi Corp +002B Compass Computing +002C Intel Corp. +002D Time Synchronization VAP Novell Inc. +002E Target Service Agent Novell Inc. +0045 DI3270 Gateway ? +0047 Advertising Print Server Novell Inc. +0048 TCP/IP Gateway Micom Interlan +0049 Business Records Corp +004A Paradata Computer Networks +004B Btrieve Server 5.0 Novell Inc. +004C Netware SQL VAP Novell Inc. +004D Xtree-Net Central Point Software +004E ICL Gateway (Novell to TCP/IP) Computer Communication Consult +004F Database Server Emerald Bay +0050 Btrieve VAP 4.11 Novell Inc. +0051 Lan Services +0052 Icm +0053 Print Queue User/ Mac Project ? Novell Inc. +0054 Value Added File System Novell Inc. +0055 Terminal Emulator Systems Analysis Inc +0056 Stocknet Broker Sap Type Novell Inc. +0057 Stocknet Exchanger Sap Type Novell Inc. +0058 Multi-Point X.25 Router Eicon Technology +0059 Lan Services +005A Business Records Corp +005B Business Records Corp +005C Business Records Corp +005D Business Records Corp +005E Business Records Corp +005F Business Records Corp +0060 Stocknet Broker - Static Novell Inc. +0061 Stocknet Queue Type Novell Inc. +0062 Stocknet Player Type Novell Inc. +0063 +0064 +0065 +0066 ArcServe 3.0 +0067 +0068 +0069 +006A +006B +006C +006D Stocknet Exchange - Static Novell Inc. +006E Nacs Netpro Inc +006F Rabbit Software Corp +0070 MIC SNA DFV Server Computerland +0071 Tape Drive Server Digi Data Corp +0072 Wancopy Utility Novell Inc. +0073 Novell Inc. +0074 Novell Inc. +0075 Netware Btrieve Novell Inc. +0076 Netware Sql Novell Inc. +0077 Novell Inc. +0078 Novell Inc. +0079 Novell Inc. +007A TES - Netware for VMS Novell Inc. +007B Mergent International +007C +007D +007E +007F +0080 +0081 +0082 +0083 +0084 +0085 +0086 +0087 +0088 +0089 +008A +008B +008C +008D Mail Server Mcgill University +008E Rational Data Systems +008F Queue Types Tate Associates Inc +0090 Tnet X.21 IDA Bridge British Telecom +0091 Tnet X.21 Bridge British Telecom +0092 Tape Backup Server Emerald Systems Corp +0093 Watcom Debugger Watcom +0094 Sila Com Software Novell Inc. +0095 Vms Router Control Interconnections +0096 Micro Data Base Systems +0097 Dart College Hill Systems +0098 Netware Access Server Novell Inc. +0099 Network Courier Microsoft Workgroup Canada +009A Named Pipes Server Novell Inc. +009B Job Server Dis Inc +009C Raylynn Knight +009D CQ3270 Lan Cq Computer Communications +009E Unix - Portable Netware Novell Inc. +009F Progress Database Progress Software Corp +00A0 Gupta SQL Base Server Gupta Technologies +00A1 Powerchute VAP/NLM American Power Conversion +00A2 Auditor Package Blue Lance Network Info Sys +00A3 Security Blue Lance Network Info Sys +00A4 Corel Optical Driver Product Corel Systems, Optical Div +00A5 Archive Server Gigatrend Inc +00A6 Menu Program R&s Data Systems +00A7 386 NLM Unisys - Camarillo +00A8 LAN 1 Router Atlanta Technologies +00A9 Object Type Corel Systems, Optical Div +00AA Object Type Corel Systems, Optical Div +00AC IDA Status Monitor Compaq Computer Corp +00AD Lanport Microtest Inc +0100 Peer Logic +0101 R21PX Crosstalk +0102 LANProtect Intel Corp. +0103 Sequelnet (DB server?) Oracle Corp +0104 Pillsbury Co +0105 Gateway To Unisys Marshfield Clinic +0106 Gateways To Unisys Marshfield Clinic +0107 RSPCX Server ('Rconsole') Novell Inc. +0108 Netware For VM & MVS Phaser Systems +0109 IRMA LAN gateway (VM/MVS) Phaser Systems +010A Netware For VM & MVS Phaser Systems +010B Netware For VM & MVS Phaser Systems +010C Net 3270 Mcgill University Computing Ct +010D Image Server File Net Corporation +010E RTK Owl Micro Systems +010F SAP Novell Inc. +0110 Artefact Network Support +0111 Test Server Novell Inc. +0112 Print Server Hewlett Packard +0113 Communication Server Novell Inc. +0114 Comm. Server Appl. (MUX) Novell Inc. +0115 Comm. Server Appl. (LSA) Novell Inc. +0116 Comm. Server Appl. (CM) Novell Inc. +0117 Comm. Server Appl. (SMA) Novell Inc. +0118 Comm. Server Appl. (DBA) Novell Inc. +0119 Comm. Server Appl. (NMA) Novell Inc. +011A Comm. Server Appl. (SSA) Novell Inc. +011B Comm. Server Appl. (Status) Novell Inc. +011C Saa Data Link Agent Novell Inc. +011D Communication Server Novell Inc. +011E Comm. Server Appl. (APPC) Novell Inc. +011F Communication Server Novell Inc. +0120 Communication Server Novell Inc. +0121 Communication Server Novell Inc. +0122 Communication Server Novell Inc. +0123 Communication Server Novell Inc. +0124 Communication Server Novell Inc. +0125 Communication Server Novell Inc. +0126 Comm. Server Appl. (Test/SAA ?) Novell Inc. +0127 Communication Server Novell Inc. +0128 Communication Server Novell Inc. +0129 Communication Server Novell Inc. +012A Comm. Server Appl. (Trace) Novell Inc. +012B Super Sna Agent Novell Inc. +012C Communication Server Novell Inc. +012D Communications Server Novell Inc. +012E Communications Server Novell Inc. +012F Communications Server Novell Inc. +0130 Comm. Server Appl. (Exec) Novell Inc. +0131 Image Server Wang Laboratories +0132 BT X.25 British Telecom +0133 NNS Domain Novell Inc. +0134 NNS Novell Inc. +0135 NNS Profile Novell Inc. +0136 NNS Novell Inc. +0137 NNS Queue Novell Inc. +0138 NNS Novell Inc. +0139 NNS Novell Inc. +013A NNS Novell Inc. +013B NNS Novell Inc. +013C NNS Novell Inc. +013D Securities Trading & Technolog +013E Securities Trading & Technolog +013F Network Designers Ltd +0140 Network Management System Accunetics +0141 Lufthansa +0142 Aladdin Knowledge Systems +0143 CDrom Server Online Computer Systems +0144 Netwise Inc +0145 Communication Processor Evergreen Systems +0146 XDB Database Server XDB Systems +0147 Piggyback Login Net_inc Micro Enhancement Inc +0148 Network Software Associates +0149 Advertising Remote Server Artefact Network Support +014A ID 5001 Weather Station Zenith Data Systems +014B Novell Inc. +014C Netfram Netware 386 Netframe +014D Netfram Netware 386 Netframe +014E Netfram Netware 386 Netframe +014F Netfram Netware 386 Netframe +0150 Maxiback - VAP Sysgen +0151 Dcs System Server Computer Concepts Corporation +0152 DCA IPX Communication Product Digital Communications Ass. +0153 DCA IPX Communication Product Digital Communications Ass. +0154 Forms Capability Rochester Telephone Corp +0155 Forms Capability Rochester Telephone Corp +0156 Forms Capability Rochester Telephone Corp +0157 Forms Capability Rochester Telephone Corp +0158 Forms Capability Rochester Telephone Corp +0159 Forms Capability Rochester Telephone Corp +015A Forms Capability Rochester Telephone Corp +015B Forms Capability Rochester Telephone Corp +015C Network Computing Inc (NCI) +015D Network Computing Inc (NCI) +015E Network Computing Inc (NCI) +015F Network Computing Inc (NCI) +0160 Network Computing Inc (NCI) +0161 Advertising Remote Server Artefact Network Support +0162 System 9 Hbf Group +0163 System 9 Hbf Group +0164 System 9 Hbf Group +0165 System 9 Hbf Group +0166 NW Management Novell Inc. +0167 Instantcom Communications Server Instant Information +0168 Pickit Communications Server Intel +0169 Peer Logic +016A Open Image For Netware Wang Laboratories +016B Open Image For Netware Wang Laboratories +016C Open Image For Netware Wang Laboratories +016D Open Image For Netware Wang Laboratories +016E Open Image For Netware Wang Laboratories +016F Luminar Optical Server Corel Systems Corp +0170 TXD Thomas Conrad Corp +0171 Lanfax Redirector Alcom Inc +0172 File Share Compaq Computer Corp +0173 File Share Compaq Computer Corp +0174 File Share/ SNMP Agent ? Compaq Computer Corp +0175 File Share Compaq Computer Corp +0176 File Share Compaq Computer Corp +0177 Lanware Horizon Technology Inc +0178 Lanware Horizon Technology Inc +0179 Lanware Horizon Technology Inc +017A Lanware Horizon Technology Inc +017B Lanware Horizon Technology Inc +017C Lanware Horizon Technology Inc +017D Lanware Horizon Technology Inc +017E Lanware Horizon Technology Inc +017F Lanware Horizon Technology Inc +0180 Lanware Network Management Inc +0188 Sysm/lan2 H&W Computer Systems +0189 Xtree Server Central Point Software +018E Pc Metro Crystal Point +018F Pc Metro Crystal Point +0190 Service Point Interpoint Software +0191 Service Point Interpoint Software +0192 Netway 2000 Tri Data Systems +0193 Netway SNA Tri Data Systems +0194 Maxway 500 Tri Data Systems +0195 TCP/IP Gateway Computervision Services +0196 Integrated Technologies Inc +0197 Share Master Storage Dimensions +0198 Zenith Data Systems +0199 Zenith Data Systems +019A Zenith Data Systems +019B Apt Net Remote Automated Programming Tech +019C Apt Net Remote Automated Programming Tech +019D Apt Net Remote Automated Programming Tech +019E Apt Net Remote Automated Programming Tech +019F Mailslots IBM +01A0 Terminal Gateway For Unisys Upstanding Systems +01A1 DB Server Lodgistix Inc +01A2 Lodgistix Inc +01A3 Gateway, C. Page, & Etc Server Teknos Systems +01A4 Gateway, C. Page, & Etc Server Teknos Systems +01A5 Gateway, C. Page, & Etc Server Teknos Systems +01A6 Gateway, C. Page, & Etc Server Teknos Systems +01A7 Gateway, C. Page, & Etc Server Teknos Systems +01A8 Gateway, C. Page, & Etc Server Teknos Systems +01A9 Gateway, C. Page, & Etc Server Teknos Systems +01AA Gateway, C. Page, & Etc Server Teknos Systems +01AB Gateway, C. Page, & Etc Server Teknos Systems +01AC Gateway, C. Page, & Etc Server Teknos Systems +01AD Menu Program R&s Data Systems +01AE Menu Program R&s Data Systems +01B0 Garp Gateway Net Research Pty Ltd +01B1 Licensing Restrictions Lan Support Group +01B2 Licensing Restrictions Lan Support Group +01B3 Media Touch Systems +01B4 Network Management Product NCR +01B5 Network Management Product NCR +01B6 Network Management Product NCR +01B7 Network Management Product NCR +01B8 Network Management Product NCR +01B9 Bonsai Technologies +01BA Biztech +01BB Bonsai Technologies +01BC Bonsai Technologies +01BD Bonsai Technologies +01BE Km Systems +01BF Connect Computer +01C0 La Cite Collegiale +01C1 J&l Information Systems +01C2 J&l Information Systems +01C3 J&l Information Systems +01C4 J&l Information Systems +01C5 J&l Information Systems +01C6 Distributed Application Folio Corporation +01C7 Microtest Inc +01C8 Madge Networks Ltd +01C9 Funk Software +01CA Funk Software +01CB Shiva Corp +01CC Shiva Corp +01CD Shiva Corp +01CE Shiva Corp +01CF E-Mail Queue C&D Data Services +01D0 E-Mail Server C&D Data Services +01D1 Lanlord Product Microcom Client Server Technol +01D2 Mark Hurst +01D3 Mark Hurst +01D5 Centers For Disease Control +01D6 On-Queue Task Queue Netplus Software Inc +01D7 On-Queue Task Server Netplus Software Inc +01D8 Castelle Inc +01D9 Castelle Inc +01DA LANPress Castelle Inc +01DB Castelle Inc +01DC Castelle Inc +01DD Castelle Inc +01DE Castelle Inc +01DF Castelle Inc +01E0 Castelle Inc +01E1 Castelle Inc +01E2 Area Code Look-Up Server Equinox Information Systems +01E3 Sorting Server Equinox Information Systems +01E4 Wall Data +01E6 X25 Automated Bridge Monitor Automated Interactions Div Of +01E7 Rational Data Systems +01E8 Rational Data Systems +01E9 Rational Data Systems +01EA Rational Data Systems +01EB Media Touch Systems +01EC Powergrid Network Daemon Cognos Inc +01ED Integralis Ltd +01EE Integralis Ltd +01EF Felsina Software +01F0 Legato Systems +01F1 Legato Systems +01F2 Legato Systems +01F3 Legato Systems +01F4 Legato Systems +01F5 Legato Systems +01F6 Andersen Consulting +01F8 Sytron Corp +01F9 Integralis Ltd/ Unibase BV +01FB Northeast Broadcast Consultant +01FC Extended Systems +01FD IBM +0200 NP/SQL Server Novell Inc. +0201 The Make Server Novell Inc. +0202 Generic Job Server Novell Inc. +0203 RMF2 Utility Novell Inc. +0204 Novell Inc. +0205 Novell Inc. +0206 Novell Inc. +0207 Novell Inc. +0208 Novell Inc. +0209 Novell Inc. +020A Novell Inc. +020B Novell Inc. +020C Novell Inc. +020D Novell Inc. +020E Novell Inc. +020F Novell Inc. +0210 Novell Inc. +0211 Novell Inc. +0212 Novell Inc. +0213 Novell Inc. +0214 Novell Inc. +0215 Novell Inc. +0216 +0217 +0218 +0219 +021A Messaging Server Novell Inc. +021D Home Router Novell Inc. +021E Netware Lontalk Gateway Novell Inc. +0220 +0222 Novell Inc. +0233 Network Management Agent (NMS) Novell Inc. +0234 Network Management Info Server Novell Inc. +0235 TIRPC Service Novell Inc. +0236 Novell Inc. +0237 IPX Discovery (NMS) Novell Inc. +0238 IP Discovery (NMS) Novell Inc. +0239 Netware Management (NMS) Novell Inc. +023A Netware Management (NMS) Novell Inc. +023B Broadcast Novell Inc. +023C Dos Target Service Agent Novell Inc. +023D SMS Workstation Name Object Novell Inc. +023E SMS Testing & Development Novell Inc. +023F SMS Testing & Development Novell Inc. +0240 Novell Inc. +0241 Novell Inc. +0242 Novell Inc. +0243 Novell MHS DS Gateway For Oce Novell Inc. +0244 Nds Gateway For Oce Novell Inc. +0245 Superlab File Distribution Server Novell Inc. +0246 Version Control Queue Novell Inc. +0247 Nvt Remote Login Over SPX Novell Inc. +0248 Queue Server For IBM PSf/2 Novell Inc. +0249 Remote monitor (NMS) Novell Inc. +024A Lat Transport Service Provider Novell Inc. +024B Lat Session Manager Novell Inc. +024C Lat Network From Netware Novell Inc. +024D Address Server Novell Inc. +025E Xapia Interface For NW 3.11 Novell Inc. +025F X.400 Protocol Access Module Novell Inc. +0260 Snads Protocol Access Module Novell Inc. +0261 Superlab Network Switch Server Novell Inc. +0262 Hub Services Novell Inc. +0263 Netware Management Agent Novell Inc. +0264 Global MHS Novell Inc. +0265 SNMP Novell Inc. +0266 Version Control Server Novell Inc. +0267 Application Rights Program Novell Inc. +0268 Novell Inc. +0269 Superlab Automation Server Novell Inc. +026A Console (NMS) Novell Inc. +026B Time Synchronization Server Novell Inc. +026D Advertising Job Server Novell Inc. +0272 Datalink Switching (DLSW) Novell Inc. +0277 Sap Server Type Novell Inc. +0278 Directory Server (NDS) Novell Inc. +0280 Novell Inc. +0281 Domain Application Services Novell Inc. +0282 Domain Application Services Novell Inc. +0283 Domain Application Services Novell Inc. +0284 Domain Application Services Novell Inc. +0285 Domain Application Services Novell Inc. +0286 Domain Application Services Novell Inc. +0287 Domain Application Services Novell Inc. +0288 Domain Application Services Novell Inc. +02FF Novell Inc. +0300 Firefox Communications Ltd +0301 Firefox Communications Ltd +0302 Gateway (Novell to IP) Firefox Communications Ltd ? +0303 Firefox Communications Ltd +0304 Firefox Communications Ltd +0305 Firefox Communications Ltd +0306 NPS (netware Print Services) Inter Connections Inc +0307 NPS Spool Client Inter Connections Inc +0308 HP NS Util Hewlett Packard +0309 Document Management Package Perfect Solutions Corporation +030A BBS Server Galacticomm Inc +030B Lucid Systems Pty Ltd +030C Laserjet/Quick Silver ? Hewlett Packard +030D Broadcast Operation Sup Dynatech Utah Scientific +030E Fault Tolerant Control Dynatech Utah Scientific +030F CD Rom Server Trantor System Ltd +0310 CD Rom Server Trantor System Ltd +0311 CD Rom Server Trantor System Ltd +0312 CD Rom Server Trantor System Ltd +0313 CD Rom Server Trantor System Ltd +0314 CD Rom Server Trantor System Ltd +0315 CD Rom Server Trantor System Ltd +0316 CD Rom Server Trantor System Ltd +0317 Batch Processor Computer Aided Business Sol +0318 +0319 +031A +0320 Gateway Attachmate Corporation +0321 Chicago Research & Trading +0322 Frye Computer Systems +0323 Wang Laboratories +0324 Wang Laboratories +0325 X.500 DSA Server Aac Systems +0326 Novell Remote ISDN Router Lanworks +0327 Bootware/MSD Lanworks +0328 Watcom +0329 Aetna Life & Casualty +032A Aetna Life & Casualty +032B Fax Server Digital Visions Corp +032C Voice Server Digital Visions Corp +032D Interprocess Exchange Server Digital Visions Corp +032E Application Server Nationsbank Appl Systems Supp +0330 SAS Share Server Sas Institute +0331 SAS Connect Sas Institute +0332 Archetype +0333 Archetype +0334 Aetna Life & Casualty +0335 Communications Server Multitech +0336 Communications Server Multitech +0337 Magee Enterprises Inc +0338 Magee Enterprises Inc +0339 Magee Enterprises Inc +033A Magee Enterprises Inc +033B Magee Enterprises Inc +033C Magee Enterprises Inc +033D Magee Enterprises Inc +033E Magee Enterprises Inc +033F Magee Enterprises Inc +0340 Magee Enterprises Inc +0341 Data Service To Workstation Chancery Software +0342 Microtest Inc +0343 Microtest Inc +0344 Microtest Inc +0345 Microtest Inc +0346 Microtest Inc +0347 Microtest Inc +0348 Microtest Inc +0349 Microtest Inc +034A Preferred Health Care Ltd +034B Preferred Health Care Ltd +034C Preferred Health Care Ltd +034D Preferred Health Care Ltd +034E Preferred Health Care Ltd +034F Preferred Health Care Ltd +0350 Preferred Health Care Ltd +0351 Preferred Health Care Ltd +0352 Fujitsu Ltd +0353 Fujitsu Ltd +0354 Fujitsu Ltd +0355 Arcada Software +0356 Lanovation +0357 Lanovation +0358 Cbis Inc +035A Mbac +035B Right-Hand-Man, E-Mail/scheduling Lan Aces Inc +035C Fax Server Transfax Corporation +035D Fax Print Server Transfax Corporation +035E Fax Merge Server Transfax Corporation +035F Network Management Server Transfax Corporation +0360 Funk Software +0362 Pseudo Peer-To-Peer Us Army Corps Of Engineers +0363 Print Server - Laser Jet Extended Systems +0364 Lan Times Japan DEMO Lan Times Japan, Softbank Corp +0365 Lan Times Japan DEMO: Queue Type Lan Times Japan, Softbank Corp +0366 Mgate - Communication Gateway Coefficient Systems Corp +0367 Excalibur Technologies Corp +0368 Excalibur Technologies Corp +0369 Excalibur Technologies Corp +036A Excalibur Technologies Corp +036B Aetna Life & Casualty +036C IPX Peer-To-Peer Sewell Development +036D Micro Integration +036E NLM Server Praxis +036F Avail Systems Corp +0372 Digital Equipment +0373 UPS Info NLM Brainstorm Engineering +0374 Shareware Communications Server Cherry Tree Software +0375 Enterprise ECS Intel Corp +0376 Enterprise Initialization Mode Intel Corp +0377 Comm. Serverr - NETBios IPX US Robotics Software +0378 NLM Advertising For UPS Info Brainstorm Engineering +0379 Fax Server Extended Systems +037A Fax Server Extended Systems +037B Fax Server Extended Systems +037C Fax Server Extended Systems +037D Gateway Management Wall Data +037E Powerchute Alert - Ups Monitoring American Power Conversion +037F Virusafe Notify Central Point Software +0380 Fax Server Optus Information Systems +0381 Transport Network Substrate (TNS) Oracle +0382 Lasermaster Printer Products Laser Master Corp +0383 Powerchute Administrative American Power Conversion +0384 Sequel Link Cl/S Middleware Techgnosis Inc +0385 Mail Systems Synectic Systems Ltd +0386 Hewlett Packard Bridges Hewlett Packard +0387 Hewlett Packard Hubs Hewlett Packard +0388 Workstn Peer-To-Peer Communication IBM +0389 Datanex Corporation +039A Hp Open Mail & Portable Netware Hewlett Packard +039B ?? Lotus notes on OS/2 Iris Associates +039C Communications Server Bindery Dator 3 Spol Sro +039D Communications Server SAP Dator 3 Spol Sro +039E Fax Server Ferrari Electronic Gmbh +039F Computervision Services +03A0 CD Server Jostens Learning Corp +03A1 Neumeier & Walch Systemtechnik +03A2 Hyprotech Ltd +03A3 Kyocera Corp, Yohaga Office +03A4 Kyocera Corp, Yohaga Office +03A5 Kyocera Corp, Yohaga Office +03A6 Kyocera Corp, Yohaga Office +03A7 Group (Pforzheim POA) Stadt Pforzheim POA +03A8 Queue (Pforzheim POA) Stadt Pforzheim POA +03A9 Server (pforzheim POA) Stadt Pforzheim POA +03AA Uds Motorola +03AB Uds Motorola +03AC Uds Motorola +03AD Uds Motorola +03AE Raima Corp +03AF Copy Protection Server Pace Software Systems Inc +03B0 TNA Communication Palindrome +03B1 Lan Controller For Netware Bus Technology +03B2 Network Designers +03B3 File Management Services Systems Axis Plc +03B4 Queue Management Services Systems Axis Plc +03B5 Iwi +03B6 Lantech Services +03B7 Antivirus NLM Certus +03B8 Modem Server Lansource Technologies +03B9 Global Info Appl. Exec. Funk Software +03BA Magix Database Server Advanced Software Technologies +03BB Performance Monitor Ameridata +03BC Netport Advertising Intel Pced +03BD Wan Connection Server Ideassociation +03BE Wicat Jostens Learning Corp +03BF Wicat Server Jostens Learning Corp +03C0 Oem Plotter Product Cal Comp +03C1 Oem Plotter Product Cal Comp +03C2 Oem Plotter Product Cal Comp +03C3 Oem Plotter Product Cal Comp +03C4 ArcServe 4.0 Cheyenne +03C7 Lan Spool 3.5 Intel +03C8 Network Management Madge Networks Ltd +03C9 Diatek Patient Mgmt Systems +03CA Point-Of-Sale Server Optical Mark Systems Ltd +03CB Software Access Control Server U Of Plymouth +03CC Axnetwar - Print Server Tidemark/COSTAR +03CF Database Engine Blue Lance Network Info Sys +03D0 Report Engine Blue Lance Network Info Sys +03D1 Job Server Blue Lance Network Info Sys +03D2 Blue Lance Network Info Sys +03D3 Blue Lance Network Info Sys +03D4 Visinet NLM Technology Dynamics Inc +03D5 Print Servers Lexmark International +03D6 Print Servers Lexmark International +03D7 Print Servers Lexmark International +03D8 Print Servers Lexmark International +03D9 Server Monitoring Program Trellis +03DA Multiple Services & Applications Think Systems Corp +03DB Multiple Services & Applications Think Systems Corp +03DC Multiple Services & Applications Think Systems Corp +03DD Server Performance Analisys Banyan Systems Inc +03DE Gupta SQL DB Server Gupta Technologies +03DF Remote Database Services Interactive Data +03E0 Object Store Server Object Design +03E1 Unixware Univel +03E2 Unixware Univel +03E3 Unixware Univel +03E4 Unixware Univel +03E5 Unixware Univel +03E6 Unixware Univel +03E7 Unixware Univel +03E8 Unixware Univel +03E9 Unixware Univel +03EA Unixware Univel +03EB Unixware Univel +03EC Unixware Univel +03ED Unixware Univel +03EE Unixware Univel +03EF Unixware Univel +03F0 Unixware Univel +03F1 First Call Thomson Financial +03F2 Access Rights Server Qm Consulting +03F3 Vital Signs/lan Server Blueline Software Inc +03F4 LAA Server Saber Software +03F5 Microsoft SQL Server Microsoft +03F6 Asynchronous Serial Communications Black Creek Integrated Systems +03F7 Asynchronous Serial Communications Black Creek Integrated Systems +03F8 Asynchronous Serial Communications Black Creek Integrated Systems +03F9 Asynchronous Serial Communications Black Creek Integrated Systems +03FA Watson - Communications Server Prodigy Services +03FB Netport Intel Pced +03FC Netport Intel Pced +03FD Netport Intel Pced +03FE Netport Intel Pced +03FF Modular Software Corporation +0400 Artefact Network Support +0401 Artefact Network Support +0402 Artefact Network Support +0403 Artefact Network Support +0404 Artefact Network Support +0405 Artefact Network Support +0406 Artefact Network Support +0407 Artefact Network Support +0408 Artefact Network Support +0409 Artefact Network Support +040A Image Application Laser Data +040B Image Application Laser Data +040C Image Application Laser Data +040D Image Application Laser Data +040E Image Application Laser Data +040F Image Application Laser Data +0410 Image Application Laser Data +0411 Image Application Laser Data +0412 Image Application Laser Data +0413 Image Application Laser Data +0414 Netsprint Digital Products Inc +0415 Remote Database Services Interactive Data +0416 Dealing Room Servers Teknos Systems Ltd +0417 Dealing Room Servers Teknos Systems Ltd +0418 Dealing Room Servers Teknos Systems Ltd +0419 Dealing Room Servers Teknos Systems Ltd +041A Dealing Room Servers Teknos Systems Ltd +041B Dealing Room Servers Teknos Systems Ltd +041C Dealing Room Servers Teknos Systems Ltd +041D Dealing Room Servers Teknos Systems Ltd +041E Dealing Room Servers Teknos Systems Ltd +041F Dealing Room Servers Teknos Systems Ltd +0420 Dealing Room Servers Teknos Systems Ltd +0421 Dealing Room Servers Teknos Systems Ltd +0422 Dealing Room Servers Teknos Systems Ltd +0423 Dealing Room Servers Teknos Systems Ltd +0424 Dealing Room Servers Teknos Systems Ltd +0425 Dealing Room Servers Teknos Systems Ltd +0426 Dealing Room Servers Teknos Systems Ltd +0427 Dealing Room Servers Teknos Systems Ltd +0428 Dealing Room Servers Teknos Systems Ltd +0429 Dealing Room Servers Teknos Systems Ltd +042A Full Text Retrieval Cl/S Db Impact Italiana Srl +042B Gateway Software Datev Eg +042C Gateway Software Datev Eg +042D Client-Server Driver For IPX/SPX Reference Point Software +042E Intrak Inc +042F Loader Casper Systems Inc +0430 Finder Casper Systems Inc +0432 Filemaker Pro Claris Corp +0433 Networking Hub Synoptics +0434 Network Terminal Emulator Ide Association +0435 Administration Server Mcgill University Fac Of Engin +0436 Network Dynamic Data Exchange Netlogic Inc +0437 Asynch Comm Svr US Robotics Software +0438 Back Up Product Corel Systems, Optical Div +0439 Back Up Product Corel Systems, Optical Div +043A Software Communications Tentera Computer Services +043B Enterprise In Maintenance Mode Intel +043C Connection Station Service Corollary Inc +043D Connection Station Service Corollary Inc +043E Connection Station Service Corollary Inc +043F Connection Station Service Corollary Inc +0440 Connection Station Service Corollary Inc +0441 Connection Station Service Corollary Inc +0442 Connection Station Service Corollary Inc +0443 Connection Station Service Corollary Inc +0444 SNA Gateway Microsoft +0445 Workstation Terminal Access Hsd Hardware Software Developm +0446 De International Ltd +0447 Distributive Cache Product Raima Corp +044C +044D IBM Host Gateway Idea Courier +044E Urban Science Applications +044F Common Communication Interface Computer Associates +0450 Communications Server SDD Scandinavian Airlines Data +0451 Tape Back-Up For NLM Applications Mountain Network Solutions Inc +0452 Tape Back-Up For NLM Applications Mountain Network Solutions Inc +0453 Tape Back-Up For NLM Applications Mountain Network Solutions Inc +0454 Tape Back-Up For NLM Applications Mountain Network Solutions Inc +0455 Tape Back-Up For NLM Applications Mountain Network Solutions Inc +0456 Tape Back-Up For NLM Applications Mountain Network Solutions Inc +0457 Canon Peripheral Server Canon Information Systems +0458 Netware Server Product Intel Corp +0459 Object Oriented Database System Ontos Inc +045A QMS Printer - Remote Configuration QMS +045B Client Server Monitoring Utility Dell Computer +045C Application Definitition Lanovation +045D Fax Server Ferrari Electronic Gmbh +045E Fax Server Ferrari Electronic Gmbh +045F Fax Server Ferrari Electronic Gmbh +0460 Fax Server Ferrari Electronic Gmbh +0461 Communications Gateway - OSI Eicon Technology +0462 Communications Gateway - SNA Eicon Technology +0463 Network Archive Server Palindrome +0464 Batchfiler Application Jovandi International Inc +0465 NLM On File Server Jovandi International Inc +0466 Time Synchronization Server Jovandi International Inc +0468 Telephone Answering System A&m Communications +046A Fax Server Extended Systems +046B Cadence Time Service Polygon Inc +046C Poly-Portal For Netware/ethernet Polygon Inc +046D Poly-Link Pc To Vax Networking Polygon Inc +046E LAT Service Polygon Inc +046F Automatic Tracking System Holten White Associates +0470 Measureservers And Measureclients Advantech Benelux Bv +0471 Disk Monitor Storage Deminsions +0472 Enterprise Network Services Banyan Systems Inc +0474 Sybase SQL Server Sybase Inc +0475 Sybase SQL Server Console Sybase Inc +0476 Sybase SQL Server Monitor Sybase Inc +0477 Sybase SQL Server Back-Up Sybase Inc +0478 Sybase Open Server Sybase Inc +0479 Sybase Open Server Console Sybase Inc +047A +047B +047C Remote Printer Console Peerless Group +047D Auto-On/off Control NLM Mitsubisi Denki Computer +047E Ascom Fax Server Ascom Telecommunication Ltd +047F Ascom Advertising Fax Server Ascom Telecommunication Ltd +0480 Ascom Fax Queue Ascom Telecommunication Ltd +0481 Job Server High Aspect Development +0482 Job Queue High Aspect Development +0483 Finance Think Systems Corp +0484 Forcasting Think Systems Corp +0485 Schema Think Systems Corp +0486 Fail Safe Analysis Think Systems Corp +0487 Think Systems Corp +0488 Cl/s communication CDC +0489 Store Name Of Special File Delta Information Systems +048A Calendar Management NLM Russell Information Sciences +048B Array Monitor Server Core International +048C Document Processing Server NLM Boss Logic Inc +048D Document Processing Server NLM Boss Logic Inc +048E Document Processing Server NLM Boss Logic Inc +048F Document Processing Server NLM Boss Logic Inc +0490 Power Product For File Server Brainstorm Engineering +0491 Netblazer Communication Server Telebit Corporation +0492 RTS Terminal Emulation Data Research & Applications +0493 RSCF Client-Server API Data Research & Applications +0494 Cd Networker Bindery Type Lotus +0495 Remote Back-Up Device Astora Software Inc +0496 SQL Server IPX/SPX Hidden Server Microsoft +0497 Database Lock Server High Aspect Development +0498 Meter Server Polymeter Response Ltd +0499 Lancorp Eoms Lancorp Pty Ltd +049A Bull HN SDM Lancorp Pty Ltd +049B Network Management Agent Eicon Technology +049C Icot Sna Gateway Icot Corp +049D Software NLM Interconections +049E Internet Gateway Metascybe Systems Ltd +049F Email & Calendaring Attachmate Canada +04A1 Automation Information Router Kurt Manufacturing +04A2 Xbase Record Engine Extended Systems +04A3 Remote Procedure Call System Fortunet Inc +04A4 Valuable Info Transmission Wave Systems Corporation +04A5 Remote Access Server Traveling Software +04A6 Program Metering Database Pilott Systems +04A7 LT Auditor 4.00 Plus Blue Lance Inc +04A8 Fujitsu Ltd +04A9 Fujitsu Ltd +04AA SAP Service Fujitsu Ltd +04AB SAP Service Fujitsu Ltd +04AC Calendar Server Campbell Services +04AE Led Server Inova Corporation +04B0 CDnet Server Meridian Data Inc +04B1 Policy Engine Emerald Systems +04B2 Policy Engine Emerald Systems +04B3 Policy Engine Emerald Systems +04B4 Policy Engine Emerald Systems +04B5 Policy Engine Emerald Systems +04B6 Policy Engine Emerald Systems +04B7 Lan Assist Plus Remote Control Microtest +04B8 Lan Assist Plus Remote Control Microtest +04B9 Lan Assist Plus Remote Control Microtest +04BA Lan Assist Plus Remote Control Microtest +04BB Map Assist Peer-To-Peer Microtest +04BC Map Assist Peer-To-Peer Microtest +04BD Map Assist Peer-To-Peer Microtest +04BE Map Assist Peer-To-Peer Microtest +04BF Jetnet Driver Jetstream Technology Ltd +04C0 Database Server NLM Lodestar Data Systems +04C1 Asynchronous Comm. Servers Us Robotics Software +04C2 Database Server Fair Com +04C3 Taurus Database Server Dci +04C4 Taurus Serial Server Dci +04C5 Casper Queue Casper Systems Inc +04C6 Casper Ghost Casper Systems Inc +04C7 Conefrencing Service Fujitsu Networks Industry +04C8 Mail System Queue Service Mitsubishi Electric Engineerin +04C9 Video Server Novell - Multi Media +04CA Message Express Product Horizon Strategies Inc +04CB Cd Rom Server Cbis Inc +04CC Cost Recovery Server Vincent Larsen +04CD Pc-Based Sna Gateway Ungermann Bass +04CF User Restrictions Val Laboratory Co Ltd +04D0 User Restrictions Val Laboratory Co Ltd +04D1 Sap Advertising On Print Server Nissin Electric Co Ltd +04D2 Ellipse Server Bachman Information Systems +04D3 Asynchronous Access Server Skyline Technology +04D4 Enterprise Description Object Hans Spatzier +04D5 Print Server Foresyte Technologies +04D7 Cubix Ql Server Cubix Corp +04D8 Cubix Ql Client Cubix Corp +04D9 Job Server Storage Dimensions +04DA Communication Server Firefox Communications Ltd +04DB Communication Server Firefox Communications Ltd +04DC Communication Server Firefox Communications Ltd +04DD Communication Server Firefox Communications Ltd +04DE Communication Server Firefox Communications Ltd +04DF Communication Server Firefox Communications Ltd +04E0 Communication Server Firefox Communications Ltd +04E1 Communication Server Firefox Communications Ltd +04E2 Communication Server Firefox Communications Ltd +04E3 Communication Server Firefox Communications Ltd +04E4 Communication Server Firefox Communications Ltd +04E5 Communication Server Firefox Communications Ltd +04E6 Communication Server Firefox Communications Ltd +04E7 Communication Server Firefox Communications Ltd +04E8 Communication Server Firefox Communications Ltd +04E9 Communication Server Firefox Communications Ltd +04EA Communication Server Firefox Communications Ltd +04EB Communication Server Firefox Communications Ltd +04EC Communication Server Firefox Communications Ltd +04ED Communication Server Firefox Communications Ltd +04EE Remote Access Server DCA +04EF Statistic Management Multitech +04F0 Statistic Management Multitech +04F1 Remote Control Software Multitech +04F2 Remote Control Software Multitech +04F3 Multitech +04F4 Netlynx Communication Server Andrew Corporation +04F5 Netlynx Communication Server Andrew Corporation +04F6 Netlynx Communication Server Andrew Corporation +04F7 Netlynx Communication Server Andrew Corporation +04F8 Netlynx Communication Server Andrew Corporation +04F9 Netlynx Communication Server Andrew Corporation +04FA Netlynx Communication Server Andrew Corporation +04FB Netlynx Communication Server Andrew Corporation +04FC Netlynx Communication Server Andrew Corporation +04FD Netlynx Communication Server Andrew Corporation +04FE Netlynx Communication Server Andrew Corporation +04FF Netlynx Communication Server Andrew Corporation +0500 Netlynx Communication Server Andrew Corporation +0501 Netlynx Communication Server Andrew Corporation +0502 Netlynx Communication Server Andrew Corporation +0503 Netlynx Communication Server Andrew Corporation +0504 Deskview X Quarterdeck Office Systems +0505 Print Server Add-On Intel Corp. +0506 Index Sequential Access NLM Infopoint Systems +0507 Associative Index Server Infopoint Systems +0508 Netscribe Server Meridian Data Corp +0509 Print Server For Remote Wkstn. Fuji Xerox Co Ltd +050A Netmagic Bindery Id Net Magic Systems Inc +050B Financial Market Information Srvr AT Financial +050C Network Modem Nanagram +050D Network Modem Nanagram +050E Document Management Service Imagery Software Inc +050F Image Management Service Imagery Software Inc +0510 Mass Storage Service Imagery Software Inc +0511 Database Server Tobit Software Gmbh +0512 Teli-Link Voice Server Computer & Communications Co +0513 Print Server Emulex Corporation +0514 1012 Hub Agent Asante Technologies +0515 1012 Hub Agent Asante Technologies +0516 1012 Hub Agent Asante Technologies +0517 1012 Hub Agent Asante Technologies +0518 1012 Hub Agent Asante Technologies +0519 1012 Hub Agent Asante Technologies +051A 1012 Hub Agent Asante Technologies +051B 1012 Hub Agent Asante Technologies +051C 1012 Hub Agent Asante Technologies +051D 1012 Hub Agent Asante Technologies +051E Network Modems Lansource Technologies +051F Application Programs University Of Plymouth +0520 Bloomberg Audio Server Bloomberg Lp +0521 Bloomberg Process Server Bloomberg Lp +0522 Net Modem Server Practical Peripherals Inc +0525 Agent For Hub Management Idea Courier +0526 Named Pipe Communications Service Symantec Peter Norton Group +0527 Print Server/remote Printer Pacific Data Products +0528 Print Server Seiko Epson Corp +0529 Interserver File Copying Bankers Trust Co +052A Fax & Voice Server Dca +052B Angora Enterprise Gateway Rabbit Software Corp +052C Remote Login Terminal Gordian +052D OS/2 Application Server Citrix Systems +052E Bloomberg Media Touch Systems +052F Microcom Remote Access Server Microcom Inc +0530 Remark Voice Server Simpact & Assoc Inc +0531 IPX Communication Server Symantec Peter Norton Group +0532 Access Server For Modem Sharing Bay Technical Associates +0533 Visa Gateway Hemko Systems Corp +0534 Device Location Via Remote Config Milan Technology Corp +0535 Device Location Via Remote Config Milan Technology Corp +0536 Device Location Via Remote Config Milan Technology Corp +0537 Device Location Via Remote Config Milan Technology Corp +0538 Device Location Via Remote Config Milan Technology Corp +0539 Device Location Via Remote Config Milan Technology Corp +053A Device Location Via Remote Config Milan Technology Corp +053B Device Location Via Remote Config Milan Technology Corp +053C Device Location Via Remote Config Milan Technology Corp +053D Device Location Via Remote Config Milan Technology Corp +053E Remoted SMS Server New Era Systems Services Ltd +053F Listener/netware Sterling Tefen Lab +0540 Listener/dos Sterling Tefen Lab +0541 Listener/windows Sterling Tefen Lab +0542 Listener/os2 Sterling Tefen Lab +0543 Listener/mac Sterling Tefen Lab +0544 Listener/unix Sterling Tefen Lab +0545 Techra Client/server Rdbms Kvatro As +0546 Docra Cl/Sr Doc Mgmt System Kvatro As +0547 Voice/fax Responding Machine System Sophia +0548 I4/ls Naming Service Gradient Technologies +0549 TNSI Network Utilities Toadally Network Systems Inc +054A RM3 Configuration Cayman Systems Inc +054B SNMP Configuration Cayman Systems Inc +054D Imagesolve Ofs Imagesolve International +054E District Communication Gateway Chancery Software Ltd +054F District Link Chancery Software Ltd +0550 EDM Client/Pc Computer Vision +0552 Security Object Bank Of New Zealand +0555 Printers, Plotters & Routers Seiko Instruments Inc +0556 Fax Services Oaz +0557 Fax Services Oaz +0558 Fax Services Oaz +0559 At&t Joint Venture Telephony Srvr Novell Inc. +055A At&t Joint Venture Telephony Srvr Novell Inc. +055B At&t Joint Venture Telephony Srvr Novell Inc. +055C At&t Joint Venture Telephony Srvr Novell Inc. +055D Hostview Utility Attachmate Corp +055E Print Server Lexmark International Inc +055F Print Server Lexmark International Inc +0560 Print Server Lexmark International Inc +0561 Print Server Lexmark International Inc +0562 Statistics Gathering Agent Netcraft Software Development +0563 Erl Database Server Silver Platter Information Ltd +0564 Erl Directory Server Silver Platter Information Ltd +0565 Newswire Notification Generation Technologies Corp +0566 Ziff Proprietary Services Ziff Information Services +0567 Time Server NLM Meinberg Funkuhren +0568 X-Base Database Server Extended Systems +0569 Advertising Media-Db Server Ravi Technologies Inc +056A Oem Remote Access For Ethernet Shiva Corp +056B Oem Remote Access For Tokenring Shiva Corp +056C Remote Access For Ethernet Shiva Corp +056D Remote Access For Tokenring Shiva Corp +056F Med Station Server Pyxis Corporation +0570 Telnet IPX Router Teletrol Systems Inc +0571 NLM Server Sr Associates/cybermedia +0572 Netmodem Server Practical Peripherals Inc +0573 Satellite Connections Ulf Zimmermann +0574 Lan/CD Server Logicraft +0576 Peer Communications Tool Intelec Systems Corporation +0577 Communication Server Norman Data Defanse Systems +0578 Modem Sharing Software Seg +0579 Hops Database Server Hops International +057A Datafile Access Handler Lms +057B Novus Gateway Product Firefox Communications +057C Lgs Interconections +057D Lgs-Pft Interconections +057E Lgs-Pps Interconections +057F Tn 3270 Gateway Bus Tech Inc +0580 Mcafee Virus Pattern Server Mcafee Associates +0581 Optical Server Optisys +0582 Proride X.500 Ldap Support Control Data Systems +0583 Net Trax Alarm Monitor Net X Corp +0584 Net Trax File Server Agent Net X Corp +0585 Net Trax Workstation Agent Net X Corp +0586 Net Trax Bridge Agent Net X Corp +0587 Team Office Product Icl Personal Systems Oy +0589 Hyperdesk Distribute Object Mgmt Hyperdesk +058A Hyperdesk Database Type Hyperdesk +058B TCP/superlat Host Print SAP Meridian Technology Corp +058C Ocs Safeserver Omnitech Corporate Solutions +058D Jukebox User Group Todd Enterprises Inc +058E Full Screen Login Confirm +058F Meeting Space Server World Benders Inc +0590 Lan Product Server Bmc Software Inc +0591 CBT Server First Class Systems +0592 Net Tune Service Hawknet Inc +0593 Print Server Ricoh Company Ltd +0594 Remote Printer Ricoh Company Ltd +0595 Slathp Status NLM Meridian Technology Corp +0597 IPX Named Pipes Communication Srvr Symantec Peter Norton Group +0598 Appmeter For Suites Funk Software +059C Serverlog Diamond Software Inc +05A0 Service Location Protocol Eicon Technology +05A1 Nms Icons Networth Inc +05A2 Nms Icons Networth Inc +05A3 Nms Icons Networth Inc +05A4 Nms Icons Networth Inc +05A5 Nms Icons Networth Inc +05A6 Nms Icons Networth Inc +05A7 Nms Icons Networth Inc +05A8 Nms Icons Networth Inc +05A9 Nms Icons Networth Inc +05AA Nms Icons Networth Inc +05AB Nms Icons Networth Inc +05AC Nms Icons Networth Inc +05AD Nms Icons Networth Inc +05AE Nms Icons Networth Inc +05AF Nms Icons Networth Inc +05B0 Nms Icons Networth Inc +05B1 Nms Icons Networth Inc +05B2 Nms Icons Networth Inc +05B3 Multi-System Manager - Netview Ibm - Research Triangle Park +05B4 Online Transaction Transp. Srvr Abg Technology/united Card Svc +05B6 Datastar NLM Apertus Technologies +05B7 DS_LCC - Logical Link Controller Apertus Technologies +05B8 Database Management Services Revelation Technologies +05B9 Distribution Services Discovery Ibm - Rome +05BA Managing Hardware Routers Compatible Systems Corp +05C0 Calendar Server Campbell Services Inc +05C1 Network Management Application Xircom +05C2 Network Management Application Xircom +05C3 Network Management Application Xircom +05C4 Network Management Application Xircom +05C5 Network Management Application Xircom +05C6 Network Management Application Xircom +05D9 Ethernet-Managed Stackable Hub Ibm - Research Triangle Park +05E4 Filenet Network Clearinghouse File Net +05E5 Hpcs Data-Based Products Tobit Software Gmbh +05E6 Ast SNMP Instrumented Server Ast Research Inc +05EB Office Extend Server Fransen King +05EC Windows Nt Named Pipe Server Optus Information Systems +05ED Gateway Server Fujitsu Ltd +05EE Bindery File Fujitsu Ltd +05EF Fax Workstation Object Sofnet Inc +05F2 Router Administration Server Livingston Enterprises +05F3 Cl/S Array Monitor Program Allodyne Inc +05F4 Evergreen Management Agent Goodall Software +05F5 Windows Bulletin Board System Pacer Software +05F8 Ups SNMP Monitor NLM Computer Site Technologies Inc +05FA Goodall Virtual Protocol Adaptor Goodall Software +0603 Time Card Server Advanced Management Solutions +0606 Image Server Address Look-Up Watermark Software +0607 Arts Rlogind Server American Real Time, Reuters Co +0608 Arts Generic Server American Real Time, Reuters Co +0609 Arts Ruupd - Are You Up Daemon American Real Time, Reuters Co +060A Money Center Data Server Knight Ridder Financial Inc +060C Axis Printer Server Axis Communications Ab +060D Unix Mail Server Felpausch +0610 Scsi Management Adaptec +0611 Stealth Event Capture Engine Intelligence Quotient Intl Ltd +0613 Universal Data Transporter Conseil Formation Et Developpe +0614 Communication Software (NLM) NEC - Nippon Electric Company +0615 Communication Software (NLM) NEC - Nippon Electric Company +0616 Fax Gateway Server Russell Consulting +061C Print Server Emulex Corporation +061D Print Server Emulex Corporation +061E Print Server Emulex Corporation +061F Print Server Emulex Corporation +0620 Print Server Emulex Corporation +0625 Setup Manager Access Control Maximized Software +062F Metering Program Secure Design +0636 Comet Terminal Server Goodall Software +0637 Comet File Server Goodall Software +073D Connect:direct NLM Server Sterling Software +073E Connect:direct NLM Server Sterling Software +073F Connect:direct NLM Server Sterling Software +0740 Connect:direct NLM Server Sterling Software +0741 Connect:direct NLM Server Sterling Software +0742 Connect:direct NLM Server Sterling Software +0743 Maxserv 3270 Maxserv +0744 Maxserv Price Maxserv +0745 Maxserv Mail Maxserv +0746 Maxserv Macs Maxserv +0747 Maxserv Reserved Maxserv +0748 Maxserv Reserved Maxserv +0749 Tasking IPX/lwsi Gateway Tasking Software Bv +074D NLM Applications Knx Ltd +074E NLM Applications Knx Ltd +074F NLM Applications Knx Ltd +0750 NLM Applications Knx Ltd +0753 STP Protocol Service Agent Digital Technologies +0754 Ind$file File Transfer Agentt Digital Technologies +0755 Kalpana Switches Kalpana +0756 Chat Server Microsoft +0757 Titanium Database Engine Micro Data Base Systems Inc +0758 Tcs Communication Server Micro Tempus Inc +0759 Pc Communication Partner Sap Fujitsu Ltd +075A Pc Communication Partner Sap Fujitsu Ltd +075B Pc Communication Partner Bindery Fujitsu Ltd +075C Dpc Server SAP Fujitsu Ltd +075D Dpc Server Bindery Fujitsu Ltd +075E Cam Host Tmd Consulting +075F Server Monitoring Application Softwork Gmbh +076A Alarm Manager NLM Conner Peripherals +076B Event Manager NLM Conner Peripherals +076D Desktop Management NLM Advanced Modular Solutions Inc +0770 Real-Time Integration Services Industrial Peer To Peer +0771 Landeep Server Monitor Deerfield Computer Solutions +0773 Hitecsoft Api Manager Hitecsoft Corp +0774 Hitecsoft Public Library Hitecsoft Corp +0775 Hitecsoft Phone Server Hitecsoft Corp +077B Advantage X-Base Server Extended Systems +077F Security Auditor Secure Design +0B29 Site Lock LAI +0C29 License Profile Integrity Software +0C2A Virus File List Integrity Software +0C2C Global License Sap Integrity Software +0C31 License Report Definition Integrity Software +2380 Site Lock (licensing?) +238C Meeting maker +4088 Chat Server (Shareware Pkg) +4808 Site Lock Server Brightworks +5555 Site Lock User +6F00 3270 Rabbit Gateway +7F82 Tapeware Agent +9620 License Profile Administrator Integrity Software + + +# Object types are reserveb by Novell 0000-7FFF +# 8000 - FFFE can be assigned dynamically +# Some products use static numbers within this range.. + +8002 NPxxx = Netport (other uses known) Intel +8707 Frye utilities Frye +8888 Quick NW mngmnt/ Wordperfect NW ? +9000 Netshield McAfee +9009 Pegasus Mail David Harris +B0EF Access Workstation Quality Netware Tools BV +E0F0 Castaway! / Qview printqueue ? +F11F Sitelock + +# FFFF is a special type: used to query all server/object types + +FFFF All Types Novell Inc. \ No newline at end of file diff --git a/NWTP/XBINDRY/SCANBIND.BAK b/NWTP/XBINDRY/SCANBIND.BAK new file mode 100644 index 0000000..ddce16d --- /dev/null +++ b/NWTP/XBINDRY/SCANBIND.BAK @@ -0,0 +1,381 @@ +{$X+,B-,V-,S-,I-} {essential compiler directives} + +Program ScanBind; + +{ Example for the nwBindry unit / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +{ Purpose: Dumps the entire contents of the bindery. } + +{ Tests the following nwBindry calls: + + IsShellLoaded + GetBinderyAccessLevel + ScanBinderyObject + ScanProperty + ReadPropertyValue + GetRealUserName +} + +Uses nwMisc,nwBindry; + +Type string30=string[30]; + PobjRec =^objRec; + objRec =Record + objId:LongInt; + name:string30; + next:PobjRec; + end; + +Var PstartObj:Pobjrec; + GlobalPath:string; + f:text; + +procedure WriteReadSecurity(sec:Byte); +begin +Case LoNibble(Sec) of + BS_ANY_READ :write('Any (0)'); + BS_LOGGED_READ :write('Log (1)'); + BS_OBJECT_READ :write('Obj (2)'); + BS_SUPER_READ :write('Sup (3)'); + BS_BINDERY_READ :write('Netw(4)'); + else writeln('Unknown. (Read Rights Flag=$',HexStr(LoNibble(Sec),2),')'); +end;{case} +end; + +Procedure WriteWriteSecurity(Sec:Byte); +begin +Case (HiNibble(Sec) SHL 4) of + BS_ANY_WRITE :write('Any (0)'); + BS_LOGGED_WRITE :write('Log (1)'); + BS_OBJECT_WRITE :write('Obj (2)'); + BS_SUPER_WRITE :write('Sup (3)'); + BS_BINDERY_WRITE :write('Netw(4)'); + else writeln('Unknown. (Write Rights Flag=$',HexStr(HiNibble(Sec) SHL 4,2),')'); +end; {case} +end; + +Procedure PutInLinkedList(objId:LongInt;objName:String;objType:Word); +Var rp,np,lp:PobjRec; + lName :string; +begin +lName:=objname; +if lName[0]>#20 + then lName[0]:=#20; { shorten object name; } +New(np); +if objType=OT_USER + then lname:=lname+' (User)' + else if objType=OT_USER_GROUP + then lname:=lname+' (Group)'; +np^.name:=lname; +np^.objId:=objId; +np^.next:=NIL; +If PstartObj=NIL + then PstartObj:=np + else begin + lp:=PstartObj; + while (lp^.next<>NIL) do lp:=lp^.next; + lp^.next:=np; + end; +end; + +Function getNameFromLL(id:Longint):String; +Var rp:PobjRec; +begin +rp:=PstartObj; +While ((rp<>NIL) and (rp^.objId<>id)) do rp:=rp^.next; +if rp=NIL then getNameFromLL:='!error: ID not found in stored ID List.' + else getNameFromLL:=rp^.name; +end; + +Procedure ShowSet(pset:Tproperty); +Var i :Byte; + objId:LongInt; +begin +{ A segment of a set-property consists of a list of object IDs, + each ID 4 bytes long, stored hi-lo. + The end of the list (within THIS segment) is marked by an ID of 00000000. } +i:=1; +Repeat + objId:=MakeLong((pset[i] *256 +pset[i+1]), ( pset[i+2] *256 + pset[i+3] ) ); + if objId<>0 + then writeln(' *',GetNameFromLL(objId),'(',HexStr(objId,8),')'); + inc(i,4); +Until (i>128) or (objId=0); +end; + +Procedure DumpPropVal(DontSkipZeros:boolean;pv:Tproperty); +Var t,g,skip:Byte; + c :char; + s :string; +begin +if DontSkipZeros + then skip:=7 + else begin + skip:=128; + while (pv[skip]=$00) and (skip>1) do dec(skip); + skip:=(skip-1) DIV 16; + end; +t:=0; +While t<=skip +do begin + s:=''; + write(' *'); + for g:=1 to 16 + do begin + write(HexStr(pv[t*16+g],2),' '); + c:=chr(pv[t*16+g]); + if c>=' ' then s:=s+c else s:=s+' '; + end; + writeln(s); + inc(t); + end; +end; + + +Var lastObjSeen:LongInt; + objName :String; + objType :Word; + objId :LongInt; + objFlag :Byte; + objSec :Byte; + objHasProp :Boolean; + + SecAccessLevel:Byte; + MyObjId :LongInt; + + SeqNumber :LongInt; + propName :String; + propFlags, + propSecurity :Byte; + propHasValue, + moreProperties:Boolean; + + SegNbr :Byte; + propValue:Tproperty; { array[1..128] of byte } + accVal: record + balance :LongInt; {hi-lo} + limit :LongInt; {hi-lo} + Reserved:array[1..120] of byte; { NW internal info } + end ABSOLUTE PropValue; + holdVal: array[1..16] + of record + AccountServerID:Longint; {hi-lo} + HoldAmount :LongInt; {hi-lo} + end ABSOLUTE PropValue; + holds :Longint; + moreSeg:boolean; + + t :word; + tempString:String; + + OTfileFound:Boolean; + ObjTypeStr,s:string; + +begin +Writeln('ScanBind V1.2'); +Writeln('Provides information about all accessible bindery objects.'); + +GlobalPath:=ParamStr(0); +while NOT (GlobalPath[ord(GlobalPath[0])] IN [':','\','/']) + do dec(GlobalPath[0]); + +assign(f,GlobalPath+'OT_XXX.'); +reset(f); +OTfileFound:=(IOresult=0); +IF NOT OTfileFound + then begin + writeln('WARNING: OT_XXX. file with object types not found.'); + writeln(' A limited number of object type descriptions will be shown.'); + writeln; + end; + +If NOT ({IpxInitialize and} IsShellLoaded) + then begin + writeln('Error: Scanbind requires:'); + writeln(' -IPX to be loaded;'); + writeln(' -The Netware Shell to be loaded.'); + halt(1); + end; +GetBinderyAccessLevel(SecAccessLevel,MyObjId); +write('All objects with a read security level <= '); +WriteReadSecurity(SecAccessLevel); writeln(' will be shown.'); +writeln; + +{ put all objects in a table} +lastObjSeen:=-1; +PstartObj:=NIL; + +While ScanBinderyObject('*',OT_WILD,lastObjSeen, + objName,objType,objID,objFlag,objSec,objHasProp) + do PutInLinkedList(objId,objName,objType); + +if nwBindry.Result<>$FC { no such object } + then writeln('Error Scanning Objects: $',HexStr(nwBindry.Result,2)); + + +{ show all objects and asociated properties/values:} +lastObjSeen:=-1; + +While ScanBinderyObject('*',OT_WILD,lastObjSeen, + objName,objType,objID,objFlag,objSec,objHasProp) +do begin + writeln(HexStr(objId,8),' ',objName); + + write('The object type is :'); + Case objType of + OT_UNKNOWN :writeln('Unknown Object Type '); + OT_USER :writeln('User '); + OT_USER_GROUP :writeln('User group '); + OT_PRINT_QUEUE :writeln('Print Queue '); + OT_FILE_SERVER :writeln('Fileserver '); + OT_JOB_SERVER :writeln('Jobserver '); + OT_GATEWAY :writeln('Gateway '); + OT_PRINT_SERVER :writeln('Printserver '); + OT_ARCHIVE_QUEUE :writeln('Archive Queue '); + OT_ARCHIVE_SERVER :writeln('Archive Server '); + OT_JOB_QUEUE :writeln('Job Queue '); + OT_ADMINISTRATION :writeln('Administration Object'); + OT_RSPCX_SERVER :writeln('RSPCX Server (Rconsole) '); + else begin + if OTfileFound + then begin + reset(f); + ObjTypeStr:=HexStr(objType,4); + REPEAT + readln(f,s); + UNTIL eof(f) or (pos(ObjTypeStr,s)=1); + if pos(ObjTypeStr,s)=1 + then begin + delete(s,1,5); + writeln(s); + end; + end + else writeln('objType= 0x',HexStr(objType,4),' (unknown)'); + end; + end; {case} + + Case objFlag of + 0:writeln('The object is a static object.'); + 1:writeln('The object is a dynamic object.'); + else writeln('Unknown objectFlag:',objFlag); + end; {case} + + write('Security: Read: ');WriteReadSecurity(objSec); + write(' / Write: ');WriteWriteSecurity(objSec); writeln; + + if objHasProp + then begin + SeqNumber:=-1; + writeln('The object has the following properties:'); + + While ScanProperty({in} objName,objType,'*', + {i/o} SeqNumber, + {out} propName,propFlags,propSecurity, + propHasValue,moreProperties) + do begin + write(' ',propName); + + if HiNibble(propFlags)=0 + then write (' (Static') { 0 } + else write (' (Dynamic'); { 1 } + + Case LoNibble(propFlags) of + BF_ITEM:writeln(' Item-Property)'); + BF_SET :writeln(' Set-Property)'); + else writeln(' property), Property type= ',LoNibble(propFlags),' (Unknown, not Item or Set)'); + end; {case} + + write(' Security: Read: ');WriteReadSecurity(propSecurity); + write(' /Write: ');WriteWriteSecurity(propSecurity); writeln; + + { show value of properties: } + if propHasValue + then begin + if LoNibble(propFlags)=BF_SET + then begin + SegNbr:=1; + + While ReadPropertyValue(objName,objType,propName,SegNbr, + propValue,moreSeg,propFlags) + do begin + ShowSet(propValue); + inc(SegNbr); + end; + If nwBindry.Result<>$EC { no such segment } + then writeln('Error Reading Property Values: $', + HexStr(nwBindry.Result,2)); + end + else begin { item property } + if propName='IDENTIFICATION' + then begin + getRealUserName(objName,tempString); + writeln(' *',tempString) + end + else if propname='Q_DIRECTORY' + then begin + { asciiz string in 1st seg } + SegNbr:=1; + IF ReadPropertyValue(objName,objType,propName,SegNbr, + propValue,moreSeg,propFlags) + then begin + ZStrCopy(tempString,propValue,127); + writeln(' *',tempString); + end + end + else if propname='ACCOUNT_BALANCE' + then begin + { conversion of 1st 4 bytes to longint } + SegNbr:=1; + IF ReadPropertyValue(objName,objType,propName,SegNbr, + propValue,moreSeg,propFlags) + then writeln(' * Balance:',Lswap(accVal.balance),' Limit: ',Lswap(accVal.Limit)); + end + else if propname='ACCOUNT_HOLDS' + then begin + SegNbr:=1; + IF ReadPropertyValue(objName,objType,propName,SegNbr, + propValue,moreSeg,propFlags) + then begin + holds:=0; + for t:=1 to 16 + do if holdVal[t].AccountServerID<>0 + then holds:=holds+Lswap(holdVal[t].HoldAmount); + writeln(' * Total holds:',holds) + end; + end + else begin { structure not known, dump it } + SegNbr:=1; + While ReadPropertyValue(objName,objType,propName,SegNbr, + propValue,moreSeg,propFlags) + do begin + inc(segNbr); + DumpPropVal(moreSeg,propValue); + end; + + If nwBindry.Result<>$EC { no such segment } + then writeln('Error Reading Property Values: $', + HexStr(nwBindry.Result,2)); + end + + end; + end {if propHasValue then } + else begin { prop has NO value } + writeln(' *'); + end; + end; { While scanProperty do } + + If nwBindry.Result<>$FB { no such property } + then writeln('Error Scanning Properties: $',HexStr(nwBindry.Result,2)); + end { if objHasProp then } + else begin { object has NO properties } + writeln(' '); + end; + + writeln; + end; { While scanObject } +if nwBindry.Result<>$FC { no such object } + then writeln('Error Scanning Objects: $',HexStr(nwBindry.Result,2)); + +IF OTfileFound + then close(f); +end. diff --git a/NWTP/XBINDRY/SCANBIND.EXE b/NWTP/XBINDRY/SCANBIND.EXE new file mode 100644 index 0000000000000000000000000000000000000000..e3623966f928e6c61f3206447720a4dcab8cbbec GIT binary patch literal 13456 zcmbt*3tSZEx%WFWyTh=+LKKNX#j(*1h(UA{Vz>pPyJ})|10qDL9#n{U5X>?QHPvM) z4RLqS_Dgy><)m#0O@IA~_F!sT)P#$R;3d{uj~dO_7&YnAHHpzw3?lpe-3NQ=E0`h=DU_DR) z)B^RucHrm0e*t@dW56lkLts)AA^E_1zzS3Ydw?^*Uw}T~CU6^w(!(||4VVEu3s`{l zKn36gwgbNbb^*P>`@pAw7tltd9e~L|5- zLSlhL;3?pFU@5R3r~zIDegymscnA0+a2)tE-~qk{hJcs{5OW{}FamRdGTgH0@_ii127s$0#X1o@GP(jSO-)Ab--4j5%?w02D}IS3HTToGa9)AECp5q zYXBQi5Bvx4Hn0PD7w`g1ENlWIFa>x5C`6n8=hf{|WpT&gf)B*c|-I{X7#JtN`fK@;R@EWk1oMFPD6ccA8g)Lsa zUQC>s6qaY(C?*<{!WI=v)rpTKjn6Ot_wtJB@)R*Kx43Anm|ObdI;m1LmleI3H96n; zN$pj_xXc<$;|20AlSypx9hucD>F{zT3T64<84$S;B;$} zh2H)Jt~ci3;=H=z089Sas7)A-T7k7xD*ooM{S`J-uk6*3(-*UlW&ZG`G53b=Ajyge zlCwvUe@(K)AlW>E{3^*xC4nFH-5GKf@*DB`&W133fg?c(zrW< zVI6pktg*cEHVqPkaRsVe<4+;ev4zzaG{!|T>^;i7 zsgPi8Q%6d?@(mKIg6{TPL$$pc!WXn9`2CX2I(ca%B^aDqgEOC{w=+5oPSeA{l%~}r zf?rE)56Ey;hO5o1i8F33@y-K9#*MT7D^%g;Pfv>x&379qy zI+qT~=D`T_kn~8meb69l4RSu4v6Q!IGt3%kv{DA2s22*e_oT{h8`JR-r!VNX3kXG^ ziM$VjUk%UQ1nQ%PXhr-@Rhqt@4K%&oaj;6W*Fk8z_te+;7#A+7S90WAt~z=npHK?| z&%1@w7r6r#wNT5EKzV>KCn)LXEL`TSf7$2!$!4F7--!CnK54C*;B}hePPeW7<)OF& z-yb&ne1CY^=Q)cOR0p%x#NfC99wcOdr&ai$2M`D9p>ed=_#D+yLCaj!cQ)ePZB;n>!`8C7-RakvGb8Icdf z`@UnS7>t#o^_%6lq8S|Rn|-4737Aqk?PkPZ>9hgcn|+qX-=XXJ`!A&rXBv7g^$^#9 zSjSILsr&Id+U>p=2@wcZ4I+W&RmS=ugsJb;5N#NqMCGk1Du22Zy=Uo+l$q%kYeiM* z+Tu#|rIHHkh9ar7qFgLmU15{NqOvlvXw919%F5E!WyRuZ$P`;Qi508Y7q5{jQ^FQ6 zSyfnAm@>0qcJ87Di{_<^{sOV26ztWd(mFq{C~evZ3&DPyGMDxgQyCF!lTv2V&ytIfj?wYM+90Q>YHdSe#us9Ev`&|$glj- z1(rfls-Ple6-8@{*JeBtpe$KeEYcN0k+oRNTUQLdp?oP*XQRYlQMD#TP>4c6W>vLHOvKVkF+J8lxBB_S zB?y+NrSpjxpt1t#4y6|4QX!n`7>xVmVQpV?ok zpKV>UuC%JyFB=_r%o~>Xlg7+myP>okUVTvqD@^{B_)K^5)~}M7!7r6{1s1`crSVFrt+cNJb&EN zMY?KB7j@!vQCW$ki-{rn))iHX5@K9ZQC3z_O@qJDTCuU%DwQHFSWyfY#l$?t8y)WE zs)nP*1yb>bN6`}~=cXh*swxdXjt}VAlr&8YFe%NKC5q!P^Q+aKrsOx(Bvx3dhNM`U zZ1*}IE>8FREh_n|)Q376)V#E)%m#18$@&FzO^cQ+FfW)hd&z>ui`3~Q;`vo`7v!4e zELoiULiFr8a~9_>TC(ct*$Zbcnq$&agHy$)i^_`1*A%AEo56Ongb#l7BV{1wWwPB#E9Y&1}kqXiW?VT_1{4s8+&or_?d=b!#i7v zjmddS$2+ohJh9DEwY)q+i?V*-b>CuTbZm^nHbv;PJ*O(TeqYXxNa4Ot8}2P`V@P8h z_A4Eg9GsVMX?zA-BvtpKP;R=5c(anTc(&e9shdKn!MG?s_VOjH&)3VZ!*xa{mRQYe zNrw~sZ9bo+qlAU2^~!}BEu)u>^zstDSaCs1&^k(SzO-`AmsZ9p?7p=1oG0(^-rv_| zM+$>PdQA004)H@>sy0Om_nQw#xc2|yl8x)@6%@OwsepqcmLJ1&uj2b%6P)Vz+aZ1{ z^K;XBE@Tg~FO*Ef+gHqHF~Lp7@eyP*o5cpn|2=~I2%E(P$sdm(3v8AqNd9~TxsS=x z2FU{>$VZqgr;H^$3HkiR5qcjfwiO@@24Kh4&2N}6H!O}DNck$l4Nl1O3hhz#== zCDDyIp@WAqEKJuiEc$S)BBO9MK8>{%ZQq{zlqIQ(7-DJDVxRLA4i(fKqWbSjE$?iV zAcfTAzTO6;IvgRKN)LH+BZq?o|8`%#m+Cd)9KlcAF@l(=T0as(fI4-HPc2@hVF->D z9e)YoQ@X&LFJXXr><2!V)*{obNxZ6T6c6zD7rLi4@AR374tJbLM9@Y&fwIUV>pz8N#Bmv!uL&hH* zLx_OF<9$%olzK1WO)Bg^zwrxagz`}5y@fcS@e`jNLCjD!FAE_AmEIlvj_MGekd}Bo zv{)JHw`eS$pMRz*{>^=ouLj5ZcS0Qb$76lpFIcsOPeZJ6D>|4h zO`Ujju-cf9iBzrMr4Y3ploo7U8vDKizZy?a&H8_}R%wb_rE0A;mc~RnUMMsDKQR3v zqt@W7fN8{Z>%Sb+C8{k=#BdJ#(!Q_prF~Dctg!xi{l#2mr$~Ee$LPD5wt}gC1e4O_ zeK~&L#zR7hE_~bdFZ;F$3j|i%tU(tRv<&fTeE5~K1?_I`^k3yyUe-jsvRNZd36%JL z@6h;uZ}+>z@vL@qoJI=`PW;qCEXLNZ)^_S$713yYX{`3HF{rQRUv_pBP0BZUt(v@^ z6JK}_{BII8FeGG!xT?nX`P)?Q?O@_B3*q?3i678g9U?W{*;NZn|NmwgrOb+wPMysr z@yY@1j80`Kshn{uD`+8jtkga`n& zv#|!><7!gk^Pj_X=U@b>bvm}R@)nnqO6#`k4O_K}xMRyK(n_kBoNhZGD07+MlP>Ti zn>6*^-S#+~D=HFqxoRKfKW_A&+0nZA&mZmad;t}x?d9P)^_lkmIy5f~VoR{q>~opv zG1QT75hZN=-C>LOhYfYUbFeD*o*)h&h=VkUCMAejvU#i$D9&XbW!P#;tL@d^Cte79 zkLhv5ixQQ1b+-?|!zWZ;9Obk|2Vxs24~BIf!uq|?uqt)2<*3nB9YU4udYnZ%uv}S> z#ST?(aJ=Urfgr14MWO4JTB64Mejyo4LsEB@rx`<&#EhCUgf=b*FCH4{MT+8ueW1ZS zXmDx$&1WA#VsQZD&n&dD+B)~MDcrXi0@g>f zhUA}B+nfFk?Ly0g?eYvFGBz|Kp$!7%vB0Q60OEt`8AsD|;0{s~O-@r&X)Bj0rW;93 zdk1YK(*uFNLfdgm+=ym$Y*l-^O#1kS3JK}8pA2z5-rhSt(y#!)i8+19R@YTrT)wtp8_z2`p% zVQ~yHj*cRf-0D!P?G2-8*rO%)^IZtOxl?c8N&?=IKw>cU3S7ASz|Lt!nek(AHb{a`;5axOfj*Arj0Nbl&0wCi=^Q}4mS-7;() zGX#qFjKXQ=tH*>AQDudCiH<>vWoVupz2XQ?99`bTNGhSl|@>1QDXi|F#Z{p1Z&ZT&-bPEHEY#(sF!eUsUhsoR82halk zV`6$6JW^KW@nUYqg~npMJ93%@=Te<)9-?M!zfc8HxsdlA)O~!qp!~t{x~sxBjvCwS zIONuQqM17T;Ju@;QtP^_VhwBBb z=#-ZZI?bOtmwLMGpQ3v_wA2=>-o-Dp>D0S%&q@CLek)FJWJ9-9*29k#um*WhfC{Ji zB4u=$l2Wf^m@igMZ+n>ZmndbNX|iJAl5^>mZu=!e-4@nj)AgD!5{3#hKJ!JdTQ+|V zrsfpE8JCIn^kYt#~Dr4V}%2%8m0@^JLtN5>PT#xj~6R=!mBiEkS7Bj2BYjDjjA0Db+Mf zkmJ&Y=4`xf%OSqB7{QmO7us_0-uIVybLdNpMZu`y0>u}l35J8DaMSmU%~(Ff#RyG$ zA%c$)Y(m>n(in|XtYHSs^Kr34(dWYEfGFD!4JWXXFzcWHitd^N?FmX2hPOnEDVpdSwIvh0%2?UE1el27cCJ@n}o zF55dNt}Hx8-rx)J<*OJy2U}!!i`>;BA809Tk$Z4yIEPDNOUe)Ubb|3Akgc?x*zM;V z`1!>6nP+{p=S?DaYey6M_Nd_$cc3|*G><17A4{4ONOK}-o<^DeoMS_0fzDTBxOgT1b-)N@oVfuT4 zv`wbJ8m8?j{7!7k6cQ{6mbNvl-lF&H*62OS+BR%dhRc4AvhmuZ#?CjEJN_D-_wzSi zaC~yjap#((&v6{T=6{xO()n%THg;kRxn0-I7uNlCVr}nFh-2`{O0L7_E7-H-*d+O? zV^JJaxcr4>D_5U(7SQT?&=5o&t;LA%9wpIr2i7vWeqcA@B%(@GBm{!ydgc*;7(4MPwKoePW@cs zOfZw~gly8#33a25ooMxDB*H?{hA%`ceBMzd1bWXljZ$yD=leJT2#x8Kgo9%cDH_si zO>^-&*-WIQ{R>&oRMPY`@ysOqm$RM((qtx{9MYQZ$tU3QJWlri8|w+9EPECno0Q@T z({)O`XBL^`z2V-y7L?sO4~y;3%qus))TCOVq0rUswz#@I3@Nni)|~QqI8rz{T9cZY zZe=xLt`mFsW0Ty39XILW$&Wpr_Qb3w(=#%&vY)c$+RCNU4aK7J3R)y$ktC9oC>^m> z;Kc%cF*T=R<0dQpkVH&elO)b~a+Y!0lQYGqE37z(UyIb0&Ag|Rtgv!uJ#?+UjYDc- z_adRe?Rklz6rQxbPx|n>`opyD8qHwSRMOHkjkI9OQ%elpS7Tlr zQqW~nNisi`jEkE_FtsGe+0)3uw3)*da9u0iYMX!+Lm1BM@H4L>lk1}B)DD|o7SRhYpUg$K59*)8P#&)gv*8F1Sw zR8Ps^Q%LnhzCiwZySsTR2Eu8?zX~&UT6_uVgv$ETw(978CS(1h^oORcy1q;ws){Zvxoo(XHE{W?KSnLDwXB*5N0bjR+}bcApE!B`l>86O4~k{MWe7aVVh$O8Cq*JZK2IV4Z1U0h&kGJ znDl*eyX`FL`{=fou-2&L?#l4~a~b?I#0u&W(${-?pYfFZVFFT8NIO`q88mjbw6&gX z!8W7?w=FH(Oj1h&%6=Qw(#EhT+p8xxTJ|m`5vOe7JJSzUX#@xgD2o&FtJU z@ra-%v@T7ahp?J7_{Fqlio~ygU%iojTSD)2SUc_8eby`Nd*`f|+xNS($|S%WWD}dU z`>Zr7>z%VwR95R*DW(^-FCpH^xjfb&Q^+!^A|KhLs@WeyJPV1$8+uJ_>sh*pRF}nm zb+ubqXd7$5FBA$?*NenE*0MjAc*c-IxE{HGJn`trusmPD7Z`jGA&WSSbF0GKYKSz3 zgZYtaqEFcK)Uip{7+MQB(`&+VcOneqPKWRPJ`dqH9yYxCoM4bA?8$*dSaO%dwG}JO z-^j!oJE=1NxFAocyRE6>_hdsH6cE%rc-&B1gPPe=405$#$f(vy$q9U3uHj$-d|agq zhibmg)__}>{& zOB;73-v&h)# zZZisQzdTFY|BW=9_Z0euRXnPwP#ILUIyEMoXOu$X^9;UKvHt9ED^FtsPY_d?XTd#x z3q3;PhY;y(F`ffHTJC?AMjL$S6sWtw#jH50Hce~1_bbIXzRTjDv2&gcL#oCke+;K6 zLR5uZprOJpGd4rBeHjBbzI&z4v&u(9K$VpezpBW#h2iodUbYL^W6blscSr8U7f#8& z^LNlS6^&Ljq7|_tN(!2nJW!%q!h1BjHW*3A@(f`M-D25|U1?qRPxz z8&Vy#-d9E1B)#E#MY7F)vEh^_|Biedcge@4=(sQ}f~@Ri9rbHHF?Px)Ax(WlY9F=mJcl}H%H9-VGWCCeS*QPkWXzYply4g^$VcQ8#?JR*$jOT*Pn|q@ z@^nt}pT>Push!XLY4k_W+4@#^<5pm;*mv^pm-}CTt^do{`@e+9Y2$^HpH&KeWv1tB zVSY0!{9f|WyH1?`+tIp@CrUhKq;r=Bf`qpZoWAHMz$6HRO0@j5$<7_FLo?|Yk>`<* zDkUV2JJY+WSXb9DkIv82T@#6T3ofke5N|%>a1Cuf?#tvZ>*yTX0uhI|u*Sm~T~)l? z<;9S=Lq4P!5rPVo$;{x2n)%sI1JPFEBuwjbb=^F{eLc^m^5UdHzKRnG-5{O3rnyec zyh*oTR3!O`b^PpkJFoxNHb(B1Kah_sxbbUAFW-`n%2hhE_fuo1S2I7^%wsT=O(LuvQF2ONaU*w@~P>iPE3& zsWMw<=b0_qUNg@S^%NTiblAb;s0M4!`PgwB9a7E>^&ZX#1{??|48AI!9t`L`JBRe1 zKCnQ@SA~t-cZO*9h@_SGt0(qv*Wp7w`_fvw;3GAQYwf&`*r`e1NRt9JSvVA83B8gt z^A^=wHmrSDwWbz?E9U5mAR+BwjmCJ`8P!Iz)EPOdmGx7JC6!3w+wxN} z%Pn(i+sgTU_y`=^8I^guJfV%uJvQm)0Y{sglH^13d0VU^ZsR=3r0o|00jvVvIqNjV zI`iY4&y97ii0?Ixzs&cV5};3+SBT!-BBOt9dZ7OTdeH@f zZOURK-=nkL##68N-^7281F4RIbSvjL5Vjb{O!A3{)3(dW$FatPUpFtWFgUW3r7&C{ zwP_d4TOcjO?PO`b!8O6*O?S9;l~Zo^T(hO+(LSDQQF=P=mUHgqqfalN>EqlOBIaE> zA)kvlFGWY(+!WS-9-f>h#_QiM4Gj#20 + then lName[0]:=#20; { shorten object name; } +New(np); +if objType=OT_USER + then lname:=lname+' (User)' + else if objType=OT_USER_GROUP + then lname:=lname+' (Group)'; +np^.name:=lname; +np^.objId:=objId; +np^.next:=NIL; +If PstartObj=NIL + then PstartObj:=np + else begin + lp:=PstartObj; + while (lp^.next<>NIL) do lp:=lp^.next; + lp^.next:=np; + end; +end; + +Function getNameFromLL(id:Longint):String; +Var rp:PobjRec; +begin +rp:=PstartObj; +While ((rp<>NIL) and (rp^.objId<>id)) do rp:=rp^.next; +if rp=NIL then getNameFromLL:='!error: ID not found in stored ID List.' + else getNameFromLL:=rp^.name; +end; + +Procedure ShowSet(pset:Tproperty); +Var i :Byte; + objId:LongInt; +begin +{ A segment of a set-property consists of a list of object IDs, + each ID 4 bytes long, stored hi-lo. + The end of the list (within THIS segment) is marked by an ID of 00000000. } +i:=1; +Repeat + objId:=MakeLong((pset[i] *256 +pset[i+1]), ( pset[i+2] *256 + pset[i+3] ) ); + if objId<>0 + then writeln(' *',GetNameFromLL(objId),'(',HexStr(objId,8),')'); + inc(i,4); +Until (i>128) or (objId=0); +end; + +Procedure DumpPropVal(DontSkipZeros:boolean;pv:Tproperty); +Var t,g,skip:Byte; + c :char; + s :string; +begin +if DontSkipZeros + then skip:=7 + else begin + skip:=128; + while (pv[skip]=$00) and (skip>1) do dec(skip); + skip:=(skip-1) DIV 16; + end; +t:=0; +While t<=skip +do begin + s:=''; + write(' *'); + for g:=1 to 16 + do begin + write(HexStr(pv[t*16+g],2),' '); + c:=chr(pv[t*16+g]); + if c>=' ' then s:=s+c else s:=s+' '; + end; + writeln(s); + inc(t); + end; +end; + + +Var lastObjSeen:LongInt; + objName :String; + objType :Word; + objId :LongInt; + objFlag :Byte; + objSec :Byte; + objHasProp :Boolean; + + SecAccessLevel:Byte; + MyObjId :LongInt; + + SeqNumber :LongInt; + propName :String; + propFlags, + propSecurity :Byte; + propHasValue, + moreProperties:Boolean; + + SegNbr :Byte; + propValue:Tproperty; { array[1..128] of byte } + accVal: record + balance :LongInt; {hi-lo} + limit :LongInt; {hi-lo} + Reserved:array[1..120] of byte; { NW internal info } + end ABSOLUTE PropValue; + holdVal: array[1..16] + of record + AccountServerID:Longint; {hi-lo} + HoldAmount :LongInt; {hi-lo} + end ABSOLUTE PropValue; + holds :Longint; + moreSeg:boolean; + + t :word; + tempString:String; + + OTfileFound:Boolean; + ObjTypeStr,s:string; + +begin +Writeln('ScanBind V1.2'); +Writeln('Provides information about all accessible bindery objects.'); +assign(f,'OT_XXX.'); +reset(f); +OTfileFound:=(IOresult=0); +IF NOT OTfileFound + then begin + writeln('WARNING: OT_XXX. file with object types not found.'); + writeln(' A limited number of object type descriptions will be shown.'); + writeln; + end; + +If NOT ({IpxInitialize and} IsShellLoaded) + then begin + writeln('Error: Scanbind requires:'); + writeln(' -IPX to be loaded;'); + writeln(' -The Netware Shell to be loaded.'); + halt(1); + end; +GetBinderyAccessLevel(SecAccessLevel,MyObjId); +write('All objects with a read security level <= '); +WriteReadSecurity(SecAccessLevel); writeln(' will be shown.'); +writeln; + +{ put all objects in a table} +lastObjSeen:=-1; +PstartObj:=NIL; + +While ScanBinderyObject('*',OT_WILD,lastObjSeen, + objName,objType,objID,objFlag,objSec,objHasProp) + do PutInLinkedList(objId,objName,objType); + +if nwBindry.Result<>$FC { no such object } + then writeln('Error Scanning Objects: $',HexStr(nwBindry.Result,2)); + + +{ show all objects and asociated properties/values:} +lastObjSeen:=-1; + +While ScanBinderyObject('*',OT_WILD,lastObjSeen, + objName,objType,objID,objFlag,objSec,objHasProp) +do begin + writeln(HexStr(objId,8),' ',objName); + + write('The object type is :'); + Case objType of + OT_UNKNOWN :writeln('Unknown Object Type '); + OT_USER :writeln('User '); + OT_USER_GROUP :writeln('User group '); + OT_PRINT_QUEUE :writeln('Print Queue '); + OT_FILE_SERVER :writeln('Fileserver '); + OT_JOB_SERVER :writeln('Jobserver '); + OT_GATEWAY :writeln('Gateway '); + OT_PRINT_SERVER :writeln('Printserver '); + OT_ARCHIVE_QUEUE :writeln('Archive Queue '); + OT_ARCHIVE_SERVER :writeln('Archive Server '); + OT_JOB_QUEUE :writeln('Job Queue '); + OT_ADMINISTRATION :writeln('Administration Object'); + OT_RSPCX_SERVER :writeln('RSPCX Server (Rconsole) '); + else begin + if OTfileFound + then begin + reset(f); + ObjTypeStr:=HexStr(objType,4); + REPEAT + readln(f,s); + UNTIL eof(f) or (pos(ObjTypeStr,s)=1); + if pos(ObjTypeStr,s)=1 + then begin + delete(s,1,5); + writeln(s); + end; + end + else writeln('objType= 0x',HexStr(objType,4),' (unknown)'); + end; + end; {case} + + Case objFlag of + 0:writeln('The object is a static object.'); + 1:writeln('The object is a dynamic object.'); + else writeln('Unknown objectFlag:',objFlag); + end; {case} + + write('Security: Read: ');WriteReadSecurity(objSec); + write(' / Write: ');WriteWriteSecurity(objSec); writeln; + + if objHasProp + then begin + SeqNumber:=-1; + writeln('The object has the following properties:'); + + While ScanProperty({in} objName,objType,'*', + {i/o} SeqNumber, + {out} propName,propFlags,propSecurity, + propHasValue,moreProperties) + do begin + write(' ',propName); + + if HiNibble(propFlags)=0 + then write (' (Static') { 0 } + else write (' (Dynamic'); { 1 } + + Case LoNibble(propFlags) of + BF_ITEM:writeln(' Item-Property)'); + BF_SET :writeln(' Set-Property)'); + else writeln(' property), Property type= ',LoNibble(propFlags),' (Unknown, not Item or Set)'); + end; {case} + + write(' Security: Read: ');WriteReadSecurity(propSecurity); + write(' /Write: ');WriteWriteSecurity(propSecurity); writeln; + + { show value of properties: } + if propHasValue + then begin + if LoNibble(propFlags)=BF_SET + then begin + SegNbr:=1; + + While ReadPropertyValue(objName,objType,propName,SegNbr, + propValue,moreSeg,propFlags) + do begin + ShowSet(propValue); + inc(SegNbr); + end; + If nwBindry.Result<>$EC { no such segment } + then writeln('Error Reading Property Values: $', + HexStr(nwBindry.Result,2)); + end + else begin { item property } + if propName='IDENTIFICATION' + then begin + getRealUserName(objName,tempString); + writeln(' *',tempString) + end + else if propname='Q_DIRECTORY' + then begin + { asciiz string in 1st seg } + SegNbr:=1; + IF ReadPropertyValue(objName,objType,propName,SegNbr, + propValue,moreSeg,propFlags) + then begin + ZStrCopy(tempString,propValue,127); + writeln(' *',tempString); + end + end + else if propname='ACCOUNT_BALANCE' + then begin + { conversion of 1st 4 bytes to longint } + SegNbr:=1; + IF ReadPropertyValue(objName,objType,propName,SegNbr, + propValue,moreSeg,propFlags) + then writeln(' * Balance:',Lswap(accVal.balance),' Limit: ',Lswap(accVal.Limit)); + end + else if propname='ACCOUNT_HOLDS' + then begin + SegNbr:=1; + IF ReadPropertyValue(objName,objType,propName,SegNbr, + propValue,moreSeg,propFlags) + then begin + holds:=0; + for t:=1 to 16 + do if holdVal[t].AccountServerID<>0 + then holds:=holds+Lswap(holdVal[t].HoldAmount); + writeln(' * Total holds:',holds) + end; + end + else begin { structure not known, dump it } + SegNbr:=1; + While ReadPropertyValue(objName,objType,propName,SegNbr, + propValue,moreSeg,propFlags) + do begin + inc(segNbr); + DumpPropVal(moreSeg,propValue); + end; + + If nwBindry.Result<>$EC { no such segment } + then writeln('Error Reading Property Values: $', + HexStr(nwBindry.Result,2)); + end + + end; + end {if propHasValue then } + else begin { prop has NO value } + writeln(' *'); + end; + end; { While scanProperty do } + + If nwBindry.Result<>$FB { no such property } + then writeln('Error Scanning Properties: $',HexStr(nwBindry.Result,2)); + end { if objHasProp then } + else begin { object has NO properties } + writeln(' '); + end; + + writeln; + end; { While scanObject } +if nwBindry.Result<>$FC { no such object } + then writeln('Error Scanning Objects: $',HexStr(nwBindry.Result,2)); + +IF OTfileFound + then close(f); +end. diff --git a/NWTP/XBINDRY/SUPEQ.PAS b/NWTP/XBINDRY/SUPEQ.PAS new file mode 100644 index 0000000..3a8467e --- /dev/null +++ b/NWTP/XBINDRY/SUPEQ.PAS @@ -0,0 +1,38 @@ +{$X+,B-,V-,S-} {essential compiler directives} + +Program SupEq; {as of 950301} + +{ Example for the nwBindry unit / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +{ Purpose: Shows all supervisor equivalent users. } + +uses nwMisc,nwBindry; + +Var Info :TobjIdArray; + SeqNbr :Longint; + NbrOfObj:word; + ObjName :string; + ObjType :word; + NbrOfEqUsers:word; + t :word; +begin +writeln('Objects that are supervisor equivalent:'); +writeln; +NbrOfEqUsers:=0; +SeqNbr:=-1; +REPEAT + if GetRelationOfBinderyObject('SUPERVISOR',1,'SECURITY_EQUALS', + SeqNbr,NbrOfObj,Info) + then for t:=1 to NbrOfObj + do begin + inc(NbrOfEqUsers); + write(HexStr(Info[t],8)); + GetBinderyObjectName(Info[t],ObjName,ObjType); + writeln(' ',ObjName); + end; +UNTIL SeqNbr=-1; +if nwBindry.result<>0 + then writeln('Search for security equivalent users aborted due to an error.'); +if NbrOfEqUsers=0 + then writeln('No supervisor equivalent users found'); +end. \ No newline at end of file diff --git a/NWTP/XBINDRY/SWAPNAME.PAS b/NWTP/XBINDRY/SWAPNAME.PAS new file mode 100644 index 0000000..c6ba0b9 --- /dev/null +++ b/NWTP/XBINDRY/SWAPNAME.PAS @@ -0,0 +1,96 @@ +{$X+,B-,V-} {essential compiler directives} + +program swapnames; { as of 950301 } + +{ Example for the nwBindry unit / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +{ AREA:NOVELL +(1394) Thu 30 Sep 93 16:07 +By: JAMES SIMONSON +To: All +Re: Novell 3.11/4.01 bindery access +------------------------------------------------------------ + +Is there a way to access the full name information inthe bindery files? +Here's the scoop: + +We've got "firstname mi lastname" in the FULLNAME space in the user record. +We need to convert that to "lastname, firstname mi" & place that +information BACK into the FULLNAME field. Is there any relatively fast way +to do this conversion? + +--- SLMAIL v3.0 (#0623) + * Origin: WorkStations Unlimited / 312-404-2824 (1:115/404) } + +{ This program will reverse all first & last names in the bindery. + Lastname is defined as being everything after the last space in the full name. + This may not be true for all names. + If there is no space in the full name, nothing changes. } + +uses nwBindry; + +Var lastObjSeen:LongInt; + objName :string; + objType :word; + objId :LongInt; + objFlag,objSec:Byte; + hasProp :boolean; + + propVal :Tproperty; + moreseg :boolean; + propFlags:Byte; + + NewName,FullName:string; + t:byte; + s:string; +begin +IF NOT IsShellLoaded + then begin + writeln('Load network shell before executing this testprogram.'); + halt(1); + end; + +writeln('SWAPNAME: Will swap the first and last names of the IDENTIFICATION'); +writeln(' property in the bindery. (Full Name of an object)'); +writeln; +writeln('--WARNING: Changes the property values irrevokably ! --'); +writeln; +writeln('Type ''y'' and to continue.. (all else will abort)'); +readln(s); +if (s[0]=#0) or ((s[1]<>'y') and (s[1]<>'Y')) + then halt(1); + +LastObjSeen:=-1; +WHILE ScanBinderyObject('*',1,LastObjSeen, + objName,objType,objId,objFlag,objSec,hasProp) + do begin + IF ReadPropertyValue(objName,objType,'IDENTIFICATION',1, + propVal,moreSeg,propFlags) + then begin + t:=1; + while (propVal[t]<>0) + do begin FullName[t]:=chr(propVal[t]);inc(t) end; + FullName[0]:=chr(t-1); + IF pos(',',FullName)=0 + then begin + writeln(FullName); + while fullName[ord(fullName[0])]=' ' + do dec(FullName[0]); + t:=ord(FullName[0]); + while (t>0) and (FullName[t]<>' ') do dec(t); + if t>0 + then begin + NewName:=copy(FullName,t+1,255)+', ' + +copy(FullName,1,t-1); + writeln(newname); + fillChar(propVal,SizeOf(propVal),#0); + for t:=1 to ord(newName[0]) + do propVal[t]:=ord(newName[t]); + WritePropertyValue(objName,objType, + 'IDENTIFICATION',1,propVal,FALSE); + end; + end; + end; + end; + if nwBindry.result<>$FC then writeln('error scanning bindery'); +end. diff --git a/NWTP/XBINDRY/TSTBIND.PAS b/NWTP/XBINDRY/TSTBIND.PAS new file mode 100644 index 0000000..ee1b9a8 --- /dev/null +++ b/NWTP/XBINDRY/TSTBIND.PAS @@ -0,0 +1,249 @@ +{$X+,B-,V-,S-} {essential compiler directives} + +Program TSTBin; { as of 950301 } + +{ Testprogram for the nwBindry unit / NwTP 0.6 API. (c) 1994,1995 R.Spronk } + +{ Purpose: Testing only. } + +{ Tests the following nwBindry calls: + + AddBinderyObjectToSet + ChangeBinderyObjectSecurity + ChangeBinderyObjectPassword + ChangeEncrBinderyObjectPassword + ChangePropertySecurity + CreateBinderyObject + CreateProperty + DeleteBinderyObject + DeleteBinderyObjectFromSet + DeleteProperty + GetBinderyAccessLevel + GetBinderyObjectID + GetBinderyObjectName + IsBinderyObjectInSet + RenameBinderyObject + VerifyBinderyObjectPassword + VerifyEncrBinderyObjectPassword + WritePropertyValue +} + +Uses nwMisc,nwBindry; + +Procedure Warning(mess:string); +begin +writeln(' ERROR:',mess); +writeln(' ERROR#: $',hexstr(result,2),' (',result,')'); +end; + + +Function ExistsProperty(objName:string;objType:word;propertyName:String):boolean; + +Var propName:string; + pf,ps :byte; + phv,mp :boolean; + seqNbr :LongInt; +begin +seqNbr:=-1; +ExistsProperty:=ScanProperty(objName,objType,propertyname, + seqNbr,propName,pf,ps,phv,mp); +end; + + +Var myObjId:longInt; + BindSeq:Byte; + + ObjId :longint; + usrName,TrueName:string; + pTrueName:Tproperty; + + replyUsrName:string; + replyObjType:word; + + t:byte; + s:string; + +begin +writeln('BINTEST Test program for the nwBindry unit of the NwTP package.'); + +IF not IsShellLoaded + then begin + writeln('Please load shell before running.'); + halt(1); + end; +{ need supervisor privileges to run this test } +GetBinderyAccessLevel(BindSeq,myObjId); +if bindSeq<>(BS_SUPER_WRITE OR BS_SUPER_READ) { $33} + then begin + writeln('you need to be supervisor equivalent to run this test program.'); + halt(1); + end; + +writeln('-Assumes there is a group ''EVERYONE'''); +writeln('-Non destructive to the bindery. '); +writeln(' (unless you already have a user named ''USR_OINK'' or ''THE_DIVA'')'); +writeln; +writeln('For testing of the unencrypted calls, you must have'); +writeln(' SET ALLOW UNENCRYPTED PASSWORDS=ON on the server--'); +writeln(' Otherwise these calls will fail and trigger the servers'' intruder detection.'); +writeln; +writeln(' To Continue..'); +readln; + +{ you are reminded that the bindery functions turn all object names, property + names and passwords to upcase. Returned strings are also upcase. } + +usrName:='UsR_OiNk'; +TrueName:='Miss Piggy'; + +writeln('Creating Bindery object :',usrName); +IF NOT CreateBinderyObject(usrName,OT_USER, + BF_ITEM,BS_ANY_READ OR BS_ANY_WRITE) + then Warning('couldn''t create a bindery object.'); + +IF NOT GetBinderyObjectID(usrName,OT_USER,objID) + then Warning('couldn''t find the created user object'); + +writeln('Changing object security.'); +IF NOT ChangeBinderyObjectSecurity(usrName,OT_USER,BS_LOGGED_READ OR BS_SUPER_WRITE) + then warning('Couldn''t change object security.'); + +{ this program assumes there is a group called everyone. } +writeln('Making ',usrName,' a member of the group EVERYONE.'); +IF IsBinderyObjectInSet(usrName,OT_USER, + 'GROUP_MEMBERS','EVERYONE',OT_USER_GROUP) + then writeln('??: object already is a member of everyone (group)'); + +IF NOT AddBinderyObjectToSet('EVERYONE',OT_USER_GROUP,'GROUP_MEMBERS', + usrName,OT_USER) + then Warning('couldn''t make user a member of everyone'); + +IF NOT IsBinderyObjectInSet('EVERYONE',OT_USER_GROUP,'GROUP_MEMBERS', + usrName,OT_USER) + then writeln('??: user is NOT a member of everyone.'); + + +{ ------------AND NOW: the property test. + create a static property with default security... } +writeln; +writeln('Creating a property IDENTIFICATION associated withe the ',usrName,' object.'); +IF NOT CreateProperty(usrName,OT_USER, + 'IDENTIFICATION',BF_ITEM,BS_ANY_WRITE OR BS_ANY_READ) + then writeln('Couldn''t create property.'); + +IF NOT ChangePropertySecurity(usrName,OT_USER,'IDENTIFICATION', + BS_LOGGED_READ or BS_SUPER_WRITE) + then writeln('Couldn''t change property security.'); + +writeln('Writing the property value: ',trueName); +FillChar(pTrueName[1],SizeOf(pTrueName),#0); +for t:=1 to ord(truename[0]) do pTrueName[t]:=ord(TrueName[t]); + +IF NOT WritePropertyValue(usrName,OT_USER,'IDENTIFICATION',1,pTrueName,FALSE) + then Warning('Couldn''t write the property value.'); + +{ The next calls were tested before, so they are not tested again. + They create the minimal properties needed to login as the new object. } + +CreateProperty(usrName,OT_USER,'GROUPS_I''M_IN', + BF_SET,BS_SUPER_WRITE OR BS_LOGGED_READ); + +AddBinderyObjectToSet(usrName,OT_USER,'GROUPS_I''M_IN', + 'EVERYONE',OT_USER_GROUP); + +CreateProperty(usrName,OT_USER,'SECURITY_EQUALS', + BF_SET,BS_SUPER_WRITE OR BS_LOGGED_READ); + +AddBinderyObjectToSet(usrName,OT_USER,'SECURITY_EQUALS', + 'EVERYONE',OT_USER_GROUP); + +{------------- Renaming the object. } +writeln; +writeln('Renaming the object.'); +UpString(usrName); { make usrName upstring for comparison with found name.} + +GetBinderyObjectName(objId,replyUsrName,replyObjType); +IF (nwBindry.result>0) or (replyUsrName<>usrName) or (replyObjType<>OT_USER) + then Warning('Something very wrong here.'); +writeln(' Object name was :',replyUsrName); + +IF NOT RenameBinderyObject(usrName,'THE_DIVA',OT_USER) + then Warning('Couldn''t rename the object.'); + +usrName:='THE_DIVA'; {that's what it should be now} + +GetBinderyObjectName(objId,replyUsrName,replyObjType); +IF (nwBindry.result<>0) or (replyUsrName<>usrName) or (replyObjType<>OT_USER) + then Warning('Object was NOT renamed.'); +writeln(' Object name now is:',replyUsrName); + +{------------ Change and verify bindery object password. } + +writeln; +writeln('Changing Object Password. (encrypted)'); +IF ChangeEncrBinderyObjectPassword(usrName,OT_USER,'','KERMIT') + then writeln('Password successfully changed. (encrypted)') + else Warning('Couldn''t change password. (encrypted)'); + +writeln('Verifying new password. (encrypted)'); +IF VerifyEncrBinderyObjectPassword(usrName,OT_USER,'wrong password') + then Warning('A wrong (encrypted) password was verified as being OK.'); + +IF NOT VerifyEncrBinderyObjectPassword(usrName,OT_USER,'KERMIT') + then Warning('The correct (encrypted) Password was NOT verified.'); + +{ If you stop execution of this program AT THIS POINT, you will + have added a user THE_DIVA with password KERMIT, member of the + group EVERYONE to your bindery. } + +{ halt(0); } + +writeln; +writeln('WARNING: If you didn''t SET ALLOW UNENCRYPTED PASSWORDS=ON,'); +writeln(' -The server will beep;'); +writeln(' -Supervisor(s) will receive a 1 line message.'); +writeln(' (unless CASTOFF ALL was used);'); +writeln(' -The next(unencrypted) calls will fail.'); +writeln(' (All the above is essentially harmless)'); +writeln; +writeln(' to continue...'); +readln; + +writeln; +writeln('Changing Object Password. (unencrypted)'); +IF ChangeBinderyObjectPassword(usrName,OT_USER,'KERMIT','SECRET') + then writeln('Password successfully changed. (unencrypted)') + else Warning('Couldn''t change password. (unencrypted)'); + +writeln('Verifying new password. (unencrypted)'); +IF VerifyBinderyObjectPassword(usrName,OT_USER,'wrong password') + then Warning('A wrong (unencrypted) password was verified as being OK.'); + +IF NOT VerifyBinderyObjectPassword(usrName,OT_USER,'SECRET') + then Warning('The correct (unencrypted) Password was NOT verified.'); + +{------------ Deleting properties and objects } +writeln; +writeln('Deleting a property.'); +IF NOT DeleteProperty(usrName,OT_USER,'IDENTIFICATION') + then writeln('Couldn''t delete property.'); + +IF ExistsProperty(usrName,OT_USER,'IDENTIFICATION') + then writeln('??:Property wasn''t deleted.'); + +writeln('Removing the user object from the group EVERYONE.'); +DeleteBinderyObjectFromSet(usrName,OT_USER, + 'GROUP_MEMBERS','EVERYONE',OT_USER_GROUP); + +IF IsBinderyObjectInSet(usrName,OT_USER, + 'GROUP_MEMBERS','EVERYONE',OT_USER_GROUP) + then writeln('Couldn''t throw '+usrName+' out of everyone (group)'); + +writeln('Deleting the ',usrName,' object and all related properties.'); +IF NOT DeleteBinderyObject(usrName,OT_USER) + then writeln('Couldn''t delete object.'); + +IF GetBinderyObjectID(usrName,OT_USER,objID) + then writeln('??: deleted object still exists.'); + +end. \ No newline at end of file diff --git a/NWTP/XCONN/CEXPPW.PAS b/NWTP/XCONN/CEXPPW.PAS new file mode 100644 index 0000000..2830b17 --- /dev/null +++ b/NWTP/XCONN/CEXPPW.PAS @@ -0,0 +1,66 @@ +{$X+,B-,V-} {essential compiler directives} + +Program CexpPw; + +{ Example for the nwConn unit / NwTP 0.6 API. (c) 1993,1995, R. Spronk } + +{ Andre Middendorp [2:512/220] wrote on Sun 3 Jul 94 + +Q: Ik ben op zoek naar een tooltje waarmee ik op een NW 3.1x server + een file aan kan maken met daarin alle gebruikers waarvan het + wachtwoord is vervallen. Wie weet iets....? + +A: Here's a short program that will check whether or not the password + has expired of any user object. It will present you with a list of + all accounts that are expired.} + +Uses nwMisc,nwBindry,nwConn,nwServ; + { nwServ used for GetFileServerDateAndTime only } + +{ Demonstates the GetObjectLoginControl function. + see also the information provided with the ObjectCanLoginAt call } + +Var TimeNow :TnovTime; + lastObjSeen :Longint; + RepName :String; + RepType :Word; + RepId :LongInt; + RepFlag :Byte; + RepSecurity :Byte; + RepHasProperties:Boolean; + LogControlInfo :TloginControl; + NbrOfExp :word; +Begin +Writeln('CEXPPW: Check Expired Passwords.'); +IF NOT (IsShellLoaded and IsUserLoggedOn) + then begin + writeln('Load network shell and logon before running this program'); + halt(1); + end; + +GetFileServerDateAndTime(TimeNow); + +NbrOfExp:=0; +lastObjSeen:=-1; +WHILE +ScanBinderyObject('*',OT_USER,lastObjSeen, + RepName, RepType, RepId, + RepFlag, RepSecurity, RepHasProperties) + do begin + IF GetObjectLoginControl(RepName,RepType,LogControlInfo) + then with LogControlInfo.AccountExpirationDate + do if (year>0) { year=0: no expiration date was set } + and (TimeNow.year>=year) + and (TimeNow.month>=month) + and (TimeNow.day>=day) + then begin + writeln('Account of ',Repname,' expired on: ',day,'/',month,'/',year); + inc(NbrOfExp); + end; + end; +if nwBindry.Result<>$FC { NO_SUCH_OBJECT, indicates end of search } + then writeln('Error scanning bindery : $',HexStr( nwBindry.Result,2)); +if NbrOfExp=0 + then writeln(#10#13,'No expired passwords found.') + else writeln('(dates expressed in dd/mm/yy format.'); +end. diff --git a/NWTP/XCONN/CHKATT.PAS b/NWTP/XCONN/CHKATT.PAS new file mode 100644 index 0000000..058cb92 --- /dev/null +++ b/NWTP/XCONN/CHKATT.PAS @@ -0,0 +1,44 @@ +{X+,V-,B-} + +program ChkAtt; + +{ Example for the nwConn unit/ NwTP 0.6 API. (c) 1993,1995, R. Spronk } + +uses nwMisc,nwBindry,nwConn; + +Var srvr,usr:string; + connId,PrConnId:Byte; + MyObjName:string; + MyObjType:word; + MyObjId:Longint; + accLev:byte; + +begin +if ParamCount<>2 + then begin + writeln('CHKATT - Batch file utility to check for an attachment.'); + writeln; + writeln('Supply 2 parameters : CHKATT '); + writeln('If attached/logged on : ChkAtt returns errorlevel 0'); + writeln('in *all* other circumstances, errorlevel 1 will be returned.'); + halt(1); + end; +srvr:=ParamStr(1);UpString(srvr); +Usr:=paramStr(2);UpString(usr); +IF NOT GetConnectionId(srvr,connId) + then halt(1); +{ ok.. attachment to exists... am I logged in as ? } +GetPreferredConnectionId(prConnId); +SetPreferredConnectionId(connId); +IF GetBinderyAccessLevel(accLev,MyObjId) + and GetBinderyObjectName(MyObjId,MyObjName,MyObjType) + and (MyObjName=usr) + then begin + SetPreferredConnectionId(PrConnId); + halt(0); + end + else begin + SetPreferredConnectionId(PrConnId); + halt(1); + end +end. \ No newline at end of file diff --git a/NWTP/XCONN/DETACH.PAS b/NWTP/XCONN/DETACH.PAS new file mode 100644 index 0000000..7ba27e0 --- /dev/null +++ b/NWTP/XCONN/DETACH.PAS @@ -0,0 +1,31 @@ +Program Detach; + +{ Example for the NwConn unit / NwTP 0.6, (c) 1993,1995 R.Spronk } + +{ Detach from the fileserver whose name is in parameter #1, + delete all drivemappings to directories of target server's volumes } + +Uses nwMisc,nwConn,nwFile; + +Var ConnId:Byte; + Srvr:String; +begin +If paramCount<>1 + then begin + writeln('ERR: Supply name of server to detach from as a parameter.'); + writeln; + writeln('Detaches from server/ removes all drive mappings to server.'); + writeln('Returns errorlevel 1 when detaching was successful. 0 otherwise.'); + halt(0); + end; +Srvr:=ParamStr(1);UpString(Srvr); +IF NOT GetConnectionId(Srvr,connId) + then begin + writeln('ERR: Not attached to server ',Srvr); + halt(0); + end; +DeleteConnectionsDriveMappings(connId); +IF DetachFromFileServer(connId) + then halt(1) + else halt(0); +end. diff --git a/NWTP/XCONN/LOGCON.PAS b/NWTP/XCONN/LOGCON.PAS new file mode 100644 index 0000000..066ad52 --- /dev/null +++ b/NWTP/XCONN/LOGCON.PAS @@ -0,0 +1,81 @@ +{$X+,V-,B-} +program LogCon; + +{ Example program for the nwConn unit/ NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +{ Shows the use of the GetObjectLoginControl Function. } + +uses nwMisc,nwConn; + +CONST TestObjName='TEST'; + { name of the object whose LOGIN_CONTROL property is read. } + { must be of objecttype 'ot_user' } + +Var li:TloginControl; + s :string; + +{ + BadLoginCount :byte; + AccountResetTime :TnovTime; dmy, hms valid only + LastIntruderAdress :TinterNetworkAdress; + + MaxConcurrentConnections :byte; + LoginTimes :array[1..42] of byte; + + unknown1 :byte; + unknown2 :array[1..6] of byte; +} + +begin +IF NOT GetObjectLoginControl('TEST',1,li) + then begin + writeln('GetObjectLoginControl Failed. error#',nwConn.result); + writeln; + writeln('(Hint: change the hardcoded username in the source to a username that exists.)'); + halt(1); + end; + +writeln('Logincontrol Information:'); +writeln; +if li.AccountDisabled then writeln('The account is DISABLED.'); + +NovTime2String(li.AccountExpirationDate,s);delete(s,1,5); +if pos('lid date &',s)>0 then s:='No expiration date set.'; +writeln('Account Expires: ',s); + +writeln; +writeln('Minimum Password length =',li.MinimumPasswordLength); +writeln('Days between password changes =',li.DaysBetweenPasswordChanges); + +NovTime2String(li.PasswordExpirationDate,s);delete(s,1,5); +if pos('lid date &',s)>0 then s:='No Expiration date set.'; +writeln('Password Expiration date: ',s); + +write('Password control flag: '); +CASE li.PasswordControlFlag of + 0:writeln('User is allowed to change password.'); + 1:writeln('Supervisor must change password.'); + 2:writeln('User is allowed to change password. Passwords must be unique.'); + 3:writeln('Supervisor must change passwords. Passwords must be unique.'); + else writeln('Unknown Password control Flag:',li.PasswordControlFlag); +end; {case} + +writeln; +IF li.MaxGraceLoginsAllowed=0 + then writeln('Grace logins are unlimited.') + else begin + writeln('Max. Grace Logins: ',li.MaxGraceLoginsAllowed); + writeln('Remaining Grace Logins: ',li.GraceLoginsRemaining); + end; +writeln; + +NovTime2String(li.LastLoginTime,s);delete(s,1,5); +if pos('lid date &',s)>0 then s:='Object has never logged in.'; +writeln('Last login time: ',s); + +writeln; +NovTime2String(li.AccountResetTime,s);delete(s,1,5); +if pos('lid date &',s)>0 then s:='?? time not set.'; +writeln('Date of last Account reset: ',s); + +end. \ No newline at end of file diff --git a/NWTP/XCONN/LOGOUT.PAS b/NWTP/XCONN/LOGOUT.PAS new file mode 100644 index 0000000..c0731c1 --- /dev/null +++ b/NWTP/XCONN/LOGOUT.PAS @@ -0,0 +1,214 @@ +{$X+,B-,V-} {essential compiler directives} + +Program Logout; + +{ Fixed by RPL } + +uses crt,dos,graph,nwConn; +{ demo of a program that logs out the user, and fills the screen with + a worm, functionally equal to the worm of the netware console monitor. } + +const + MaxTailLen = 30; + MaxDeviations = 15; + MaxSymbols = 5; + TailSegments : array[1..MaxSymbols] of byte + = (32,176,177,178,219); + +type + BorderColl = (left,right,upside,downside); + +var + gd,gm : integer; + + color : boolean; + wormrecord : record + x_head,y_head : integer; + ChosenDir : integer; + PreferredDir : integer; + LengthFactor : integer; + TailLen : integer; + x,y : array[1..MaxTailLen] of integer; + end; + + +procedure Initialization; +var CurrSegment : integer; +begin +randomize; + +with wormrecord + do begin + LengthFactor:=random(5)+3; + TailLen:=MaxSymbols*LengthFactor; + if TailLen>MaxTailLen then TailLen:=MaxTailLen; + x_head:=40; + y_head:=12; + PreferredDir:=random(8); + for CurrSegment:=1 to MaxTailLen + do begin + x[CurrSegment]:=0; + y[CurrSegment]:=0; + end; + end; +end; + +procedure ChooseDir; +{ This procedure determines the future direction of the worm. } +VAR NbrOfDev : integer; +begin +NbrOfDev:=0; +with wormrecord + do begin + repeat + repeat + inc (NbrOfDev); + ChosenDir:=random(8); + until (NbrOfDev>=MaxDeviations) + or (ChosenDir=PreferredDir); + until abs(PreferredDir-ChosenDir)<>4; + PreferredDir:=ChosenDir; + end; +end; + +procedure DrawWorm; +var CurrSegment : integer; + SegmentSym : integer; +begin +with wormrecord + do begin + if color then textcolor (7); + for CurrSegment:=1 to TailLen + do begin + SegmentSym:=(CurrSegment-1) div LengthFactor+1; + if (x[CurrSegment]<>0) + then begin + gotoxy (x[CurrSegment],y[CurrSegment]); + write (chr(TailSegments[SegmentSym]), + chr(TailSegments[SegmentSym])); + end; + if (CurrSegment77) + then begin + x_head:=77-(x_head-77); + ReverseDir (right); + end; + if (y_head<1) + then begin + y_head:=2-y_head; + ReverseDir (upside); + end; + if (y_head>24) + then begin + y_head:=24-(y_head-24); + ReverseDir (downside); + end; + end; {with} +end; + + +procedure logoutservers; +{ Logs you out form all servers by logging out and detaching on + a server by server basis. You are not detached from your primary server. } +Var connId:byte; + servName:string; + primserv:byte; +begin +GetPrimaryConnectionId(primServ); +for connId:=1 to 8 + do begin + IF GetFileServerName(ConnId,servName) + then begin + IF LogoutFromFileServer(ConnId) + then begin + if (connId<>PrimServ) + then begin + DetachFromFileServer(connId); + writeln('You are now detached from fileserver ',servName); + end + else writeln('You are now logged out from fileserver ',servName); + end; + end + end; +delay(2500); +end; + + +begin + color:=false; + detectgraph (gd,gm); + color:=(gd<>7); + logoutservers; + clrscr; + Initialization; + repeat + ChooseDir ; + DeterminePos; + DrawWorm ; + delay (150); + until keypressed; + clrscr; +end. diff --git a/NWTP/XCONN/PWEXP.PAS b/NWTP/XCONN/PWEXP.PAS new file mode 100644 index 0000000..ea0f0c8 --- /dev/null +++ b/NWTP/XCONN/PWEXP.PAS @@ -0,0 +1,63 @@ +{$X+,B-,V-} {essential compiler directives} + +program pwexp; + +{ Example for the nwConn unit / NwTP 0.6 API. (c) 1993,1995, R. Spronk } + + +{ Q: We're forcing our users to change their passwords every 40 days. + After the password expiration date, they have 3 grace logins, one + of which they should use to change their password. To force them + to change their passwords whenever they login after the expiration + date, we need an utility that returns a distinctive Errorlevel + whenever the password has expired. + + The following program will return an errorlevel 0 whenever the calling + station's password has expired (current date later than expiration + date). In all other cases (i.e. an expiration date wasn't set, 1 will + be returned. +} + +Uses nwMisc,NwBindry,nwConn,nwServ; + +Function LaterDate(Var t1,t2):Boolean; +Type Tascii3=array[1..3] of char; +Var ta:Tascii3 ABSOLUTE t1; + tb:Tascii3 ABSOLUTE t2; +begin +if ta[1]<#80 then inc(ta[1],100); +if tb[1]<#80 then inc(tb[1],100); +LaterDate:=(ta>tb); +end; + +Var AccLev:Byte; + MyObjId:Longint; + MyObjName:string; + MyObjType:word; + Info:TloginControl; + Now:TnovTime; + +begin +IF GetBinderyAccessLevel(AccLev,MyObjId) + and GetBinderyObjectname(MyObjId,MyObjName,MyObjType) + then begin + IF GetObjectLoginControl(MyObjName,MyObjType,info) + then with Info.PasswordExpirationDate + do begin + if (year=0) and (month=0) and (day=0) + then begin + writeln('PWEXP: Expiration date not set.'); + halt(1); { exp. date not set } + end; + GetFileServerDateAndTime(now); + IF LaterDate(now,info.PasswordExpirationDate) + then begin + writeln('PWEXP: Password expired !'); + halt(0) { PW expired } + end + else halt(1); + end; + end; +writeln('PWEXP: Bindery read error. Shell wel geladen ? Ingelogd ?'); +halt(1); +end. diff --git a/NWTP/XCONN/TRCOPY.PAS b/NWTP/XCONN/TRCOPY.PAS new file mode 100644 index 0000000..1b49d17 --- /dev/null +++ b/NWTP/XCONN/TRCOPY.PAS @@ -0,0 +1,188 @@ +{$X+,B-,V-} {essential compiler directives} + +Program TrCopy; + +{ Example for the nwConn unit/ NwTP 0.6 API. (c) 1993,1995, R. Spronk } + +{ Copies the time restrictions of a supplied user to amother user, + or another user group. The destination may conatain wildcards. } + +{ Note that it is possible to change the time restrictions of a number of + users by tagging them with F5 (in SYSCON) and then changing the time + restrictions. } + +{(4395) Tue 8 Feb 94 8:43 +By: Jos Cobbenhagen +To: Allen +Re: MUTATIE TIME-RESTR. +St: +------------------------------------------------------------ +Aan :Allen Van :Jos Cobbenhagen Betreft :Mutaties Time-restrictions + +Ik moet incidenteel voor een aantal wisselende afdelingen/gebruikers de +time-restrictions aanpassen. Dit komt voor bij crises en apparte gebeurtenissen +waarbij users ijdelijk 's avonds en in weekenden moeten werken. Enige +mogelijkheid die ik hiervoor ken is voor de betreffende users appart deze +restricties aanpassen, en voor 50+ gebruikers is dat vrij omslachtig. Vraag is +of er een bindery utility is die dit in een keer voor een bepaalde groep +gebruikers kan regelen? +} + +uses nwMisc,nwBindry; + +Var source :string; + TimeRestr:array[1..42] of byte; + +Function ChangeTRforUser(dest:string):boolean; +Var res:boolean; + + ObjName :string; + ObjType :word; + ObjId :LongInt; + Iter :LongInt; + Flag,Sec :byte; + HasProperties:boolean; + + propValue:Tproperty; + moreSegs :boolean; + propFlags:byte; +begin +res :=false; +iter:=-1; +WHILE ScanBinderyObject(dest,OT_USER,iter, + ObjName,ObjType,ObjId, Flag,Sec,HasProperties) + do begin + res:=true; + IF (source<>ObjName) + and ReadPropertyValue(ObjName,OT_USER,'LOGIN_CONTROL',1, + propValue,moreSegs,propFlags) + then begin + Move(TimeRestr[1],propValue[15],42); + IF WritePropertyvalue(ObjName,ObjType,'LOGIN_CONTROL',1, + propValue,FALSE) + then writeln('Time restrictions of user ',ObjName,' changed.'); + end; + end; +ChangeTRForUser:=res; +end; + + +Function ChangeTRforGroup(GroupDest:string):boolean; +Var res:boolean; + + ObjName :string; + ObjType :word; + ObjId :LongInt; + Iter :LongInt; + Flag,Sec :byte; + HasProperties:boolean; + + propValue:Tproperty; + moreSegs :boolean; + propFlags:byte; + seg,i :byte; + + UserObjId :LongInt; + UserObjName:string; +begin +res :=false; +iter:=-1; +WHILE ScanBinderyObject(GroupDest,OT_USER_GROUP,iter, + ObjName,ObjType,ObjId, Flag,Sec,HasProperties) + do begin + res:=true; + seg:=1; + + WHILE ReadPropertyValue(ObjName,OT_USER_GROUP,'GROUP_MEMBERS',seg, + propValue,moreSegs,propFlags) + do begin + i:=1; + Repeat + UserObjId:=MakeLong((propValue[i] *256 + PropValue[i+1] ), + (propValue[i+2] *256 + PropValue[i+3] ) ); + if UserObjId<>0 + then begin + IF GetBinderyObjectName(UserObjId,UserObjName,ObjType) + and (UserObjName<>Source) + then ChangeTRForUser(UserObjName); + end; + inc(i,4); + Until (i>128) or (objId=0); + + inc(seg); + end; + end; +ChangeTRForGroup:=res; +end; + +Procedure GetSourceTR(source:string); +Var propValue:Tproperty; + MoreSegs :boolean; + PropFlags:byte; +begin +IF NOT ReadPropertyValue(source,OT_USER,'LOGIN_CONTROL',1, + propValue,moreSegs,propFlags) + then begin + writeln; + Case nwBindry.result OF + $F0 :writeln('Wildcards in source not allowed.'); + $FC,$FB:writeln('No such userobject'); + $F9,$F1:writeln('Not enough privileges..'); + else writeln('General error reading the bindery.'); + end;{case} + Halt(1); + end; +Move(propValue[15],TimeRestr[1],42); +end; + + +{--------------------------------- main -------------------------------------} +Var dest :string; + MyObjId :Longint; + secLevel:byte; + version :word; + +begin +If ParamCount<>2 + then begin + writeln('----- TRCOPY: Copy Login Time Restrictions from one user to another. -----'); + writeln; + writeln('Usage: TRCOPY '); + writeln; + writeln('Where is the name of a USER,'); + writeln('and the name of a user or group the time restrictions'); + writeln('are to be copied to. may contain wildcards.'); + writeln; + writeln('----- Use with NetWare 3.x only ---------- Written with the NwTP API -----'); + halt(0); + end; +writeln('TRCOPY: Copy Time Restrictions.'); +source:=ParamStr(1); +dest :=ParamStr(2); +UpString(Source); +UpString(dest); + +IF (NOT GetBinderyAccessLevel(secLevel,MyObjId)) + or (secLevel<>$33) + then begin + writeln; + writeln('You need to be logged in as a Supervisor equivalent user'); + writeln('to use this utility.'); + halt(1); + end; + +GetNWversion(version); +if (version DIV 100)<>3 + then begin + writeln; + writeln('TRCOPY runs with NetWare 3.X only.'); + halt(1); + end; + +GetSourceTR(source); + +IF NOT ChangeTRforUser(dest) + then IF NOT ChangeTRforGroup(dest) + then writeln('Error: destination user or group doesn''t exist.'); + +end. \ No newline at end of file diff --git a/NWTP/XCONN/TSTCONN.PAS b/NWTP/XCONN/TSTCONN.PAS new file mode 100644 index 0000000..4eb4a6a --- /dev/null +++ b/NWTP/XCONN/TSTCONN.PAS @@ -0,0 +1,172 @@ +{$X+,V-,B-} +Program testconn; + +{ Testprogram for the nwConn unit / NwTP 0.6 API. (c) 1993, 1995, R.Spronk } + +{ Purpose: testing of nwConn calls } + +{ Tests the following nwConn functions: + + GetConnectionId + GetConnectionInformation + GetConnectionNumber + GetDefaultConnectionId + GetFileserverName + GetInternetAddress + GetObjectConnectionNumbers + GetPreferredConnectionId + GetPrimaryConnectionId + GetWorkstationNodeAddress + GetUserAtConnection +} + +Uses nwMisc,nwConn; + +Procedure Warning(s:string); +begin +writeln(s); +writeln(' ERROR #: $',HexStr(nwConn.Result,2),' (',nwConn.result,')'); +writeln; +writeln('...Press to Continue..'); +readln; +end; + +Var myConnNumber:byte; + myConnId :byte; { connID of server I'm attached to } + myPhysNode2:TnodeAddress; + myObjName :string; + myObjType :word; + myObjId :longInt; + myLoginTime :TnovTime; + + myAddress:TinternetworkAddress; + + nbrOfConn:byte; + connList :TconnectionList; + + objName :string; + objType :word; + objId :LongInt; + LoginTime:TnovTime; + + connId :byte; + serverName:string; + routeInfo :string; + + t :byte; + tempStr:string; + +begin + +IF GetConnectionNumber(myConnNumber) + then writeln('Your connection number is:',myConnNumber) + else warning('!!! The GetConnectionNumber call failed'); + +IF GetInterNetAddress(myConnNumber,myAddress) + then begin + write('Your Netw.:Node:Socket Nbr is: [$'); + for t:=1 to 4 do write(hexStr(myAddress.Net[t],2)); + write(':'); + for t:=1 to 6 do write(HexStr(myAddress.Node[t],2)); + writeln(':',hexstr(myAddress.socket,4),']'); + end + else warning('!!! GetInterNetAdress failed.'); + + +IF GetWorkstationNodeAddress(myPhysNode2) + and (myAddress.Node[6]=myPhysNode2[6]) + and (myAddress.Node[5]=myPhysNode2[5]) + and (myAddress.Node[4]=myPhysNode2[4]) + then { ok } + else begin + warning('!!! GetStationadress failed'); + write('returned: $'); + for t:=1 to 6 do write(HexStr(myPhysNode2[t],2)); + end; + +IF GetConnectionInformation(myConnNumber, + myObjName,myObjType,myObjId,myLoginTime) + then begin + writeln('You are :',myObjName); + if myObjType=$1 { OT_USER} + then writeln(' of object type : USER') + else writeln(' of object type : $',HexStr(myObjType,4)); + writeln(' with object ID: $',HexStr(myObjId,8)); + NovTime2String(myLoginTime,tempStr); + writeln(' logged in at ',tempStr); + end + else warning('!!! GetConnectionInformation failed.'); + +if NOT (GetUserAtConnection(myConnNumber,tempStr) and (tempStr=myObjName)) + then warning('!!! GetUserAtConnection (2) failed.'); + +IF GetObjectConnectionNumbers(myObjName,1 {OT_USER}, + nbrOfConn,connList) + then begin + writeln('User ',myObjName,' has ',nbrOfConn,' active connection(s).'); + t:=nbrOfConn; + if t>0 + then begin + t:=1; + while t<=nbrOfConn + do begin + writeln(' at connectionNumber:',connList[t]); + inc(t); + end; + end; + end + else warning('!!! GetObjectConnectionNumbers failed.'); + + +writeln; +t:=1; +writeln('ConnNbr Name LoginTime'); +WHILE t<250 { nw 3.x / 2.x 100 } +do begin + IF GetConnectionInformation(t, objName,objType,objId,LoginTime) + then begin + PstrCopy(TempStr,objName,15); + objName:=TempStr; + NovTime2String(LoginTime,TempStr); + writeln(t:4,' ',objName,' ',TempStr); + end + else if nwConn.result<>$FD { bad_station_number / nbr not in use } + then warning('!!! GetConnectionInformation failed.'); + inc(t); + end; + +{*********** connection ID's ( server numbers in server table )************ } + +{ to which server have we been sending all the above requests? } + +routeInfo:='preferred'; +GetPreferredConnectionID(ConnId); + { if set previously, this server has the highest priority. } + +if connId=0 { preferred server was not set } + then begin + RouteInfo:='default'; + GetDefaultConnectionID(ConnId); + end; + { your current default drive is attached to this server } + +if connId=0 + then begin + RouteInfo:='primary'; + GetPrimaryConnectionID(ConnId); + end; + { the server your shell initially attached to, used if the default drive + is a local drive. Lowest priority. } + +{ These three calls are also incorporated in the secondary function: + GetEffectiveConnectionID. } +writeln; +writeln('All requests are routed to the ',RouteInfo,'-server with conn.ID=',connId); + +GetFileServerName(connId,{out:} serverName); +GetConnectionID(serverName,{out} t); +if t<>connId + then warning('!!! GetFileServerName and GetConnectionId report different values.') + else writeln('Name of the server: ',serverName); + +end. diff --git a/NWTP/XCONN/TSTCONN2.PAS b/NWTP/XCONN/TSTCONN2.PAS new file mode 100644 index 0000000..95a171a --- /dev/null +++ b/NWTP/XCONN/TSTCONN2.PAS @@ -0,0 +1,127 @@ +{$X+,V-,B-} +program tstconn2; + +{ Testprogram for the nwConn unit / NwTP 0.6 API. (c) 1993, 1995, R.Spronk } + +{ Purpose: testing of nwConn calls } + +{ Tests the following nwConn functions: + + GetConnectionIdTable + GetEndOfJobStatus + GetPrimaryConnectionId + GetNetwareErrorMode + GetNetwareShellVersion + GetWorkstationEnvironment + SetEndOfJobStatus + SetNetwareErrorMode + SetPrimaryConnectionId + +} + +uses nwMisc,nwConn; + +Var MajorVersion,MinorVersion,RevisionLevel:byte; + OStype,OSversion,HardwareType,ShortHWType :string; + + primConnId,TestConnId:byte; + + c:byte; + ConnInfo:TconnectionIDtableEntry; + + status,status1:boolean; + + mode,mode1:byte; + +begin +Writeln('Testing GetNWshellVersion.'); +IF GetNetwareShellVersion(MajorVersion,MinorVersion,RevisionLevel) + then begin + write(' Shell version: ',MajorVersion,'.',Minorversion); + if RevisionLevel>0 + then writeln(' Rev.',chr(ord('A')+RevisionLevel-1)) + else writeln; + if MajorVersion>=3 + then begin + writeln; + Writeln('Testing GetWSEnvironment.'); + IF GetWorkstationEnvironment(OStype,OSversion, + HardwareType,ShortHWType) + then begin + writeln(' OStype :',OStype); + writeln(' OSversion :',OSversion); + writeln(' HardwareType:',HardwareType); + writeln(' ShortHWtype :',ShortHWtype); + end + else writeln('GetWSenvironment returned error#:',HexStr(nwConn.result,2)); + + + + end; + end + else writeln('GetNWshellversion returned error#:',HexStr(nwConn.result,2)); + +writeln; +writeln('Tesing SetPrimaryConnectionId.'); +GetPrimaryConnectionId(primConnId); +writeln(' Primary connId=',primConnId); +IF SetPrimaryConnectionId(0) + then begin + writeln(' OK. prim.connid set to 0'); + GetPrimaryConnectionId(testConnId); + if testConnId<>0 + then writeln('ERR. primary connection wasn''t changed.'); + end; +SetPrimaryConnectionId(primConnId); +GetPrimaryConnectionId(testConnId); +If testConnId=primConnId + then writeln(' Primary connId reset to ',testConnId) + else writeln('Error setting primary connectionId'); + +writeln; +writeln('Testing GetConnectionIDtable.'); +for c:=1 to 8 + do begin + GetConnectionIdTable(c,ConnInfo); + if ConnInfo.SlotInuse>0 + then with ConnInfo + do begin + writeln(' Data for server with connId=',c); + Writeln(' Adress of server :$',HexDumpstr(ServerAddress,24),' (net,node HI-LO, socket LO-HI)'); + Writeln(' Router address :$',HexDumpStr(RouterAddress,12)); + writeln(' My connection Nbr:',ConnectionNumber); + writeln(' Connection Status:$',hexStr(connectionStatus,2)); + + end; + + end; + +writeln; +writeln('Testing Set/Get endOfJobStatus'); +GetEndOfJobStatus(status); +SetEndOfJobStatus(NOT status); +GetEndOfJobStatus(status1); +if status1=NOT status + then writeln(' Tested OK.') + else writeln(' Error: test failed.'); +SetEndOfJobStatus(status); +GetEndOfJobStatus(status1); +if status1=status + then writeln(' EOJ status reset to original mode.') + else writeln('Err: status not reset to original mode.'); + +writeln; +writeln('Testing Set/Get netwareErrorMode.'); +GetNetwareErrorMode(mode); +SetNetwareErrorMode(2); +GetNetwareErrorMode(mode1); +if mode1=2 + then writeln(' Tested OK.') + else writeln(' Error: test failed.'); +SetNetwareErrorMode(mode); +GetNetwareErrorMode(mode1); +if mode1=mode + then writeln(' Error Mode reset to original mode.') + else writeln('Err: error mode not reset to original mode.'); + +end. \ No newline at end of file diff --git a/NWTP/XCONN/TSTCONN3.PAS b/NWTP/XCONN/TSTCONN3.PAS new file mode 100644 index 0000000..76a3659 --- /dev/null +++ b/NWTP/XCONN/TSTCONN3.PAS @@ -0,0 +1,53 @@ +{$X+,V-,B-} +program tstconn3; + +{ Testprogram for the nwConn unit / NwTP 0.6 API. (c) 1993, 1995, R.Spronk } + +{ Purpose: testing of nwConn calls } + +{ Tests the following nwConn functions: + + AttachToFileServer + AttachToFileServerWithAddress (called by AttachToFileServer) + +} + +uses nwMisc,nwConn; + +Var connId,PrimConnId,t:byte; + serverName:string; + +begin +writeln('This program tests the AttachToFileServer call.'); +writeln('Can be tested in a multi-server network only.'); +getPrimaryConnectionId(PrimConnId); +GetFileServerName(PrimConnId,serverName); +writeln; +writeln('Your primary server is ',serverName,' with connectionId ',PrimConnId); + +writeln; +writeln('ConnectionIDtable:'); +for t:=1 to 8 + do If GetFileServerName(t,servername) + and (serverName<>'') + then writeln('ConnId: ',t:2,' Servername: ',serverName); + +writeln; +write('Enter name of new server (not in above list) to attach to:'); +readln(servername); +IF NOT AttachToFileServer(serverName,connId) + then begin + writeln('AttachtoFileserver returned error: $',HexStr(nwConn.result,2)); + if nwconn.result=$7D + then writeln(' (wrong servername or server unknown)'); + halt(1); + end; + +writeln; +writeln('ConnectionIDtable:'); +for t:=1 to 8 + do If GetFileServerName(t,servername) + and (serverName<>'') + then writeln('ConnId: ',t:2,' Servername: ',serverName); + +end. \ No newline at end of file diff --git a/NWTP/XCONN/WHO.PAS b/NWTP/XCONN/WHO.PAS new file mode 100644 index 0000000..70f8b71 --- /dev/null +++ b/NWTP/XCONN/WHO.PAS @@ -0,0 +1,307 @@ +{$X+,V-,B-} +program who; + +{ Adaption of a similar program privided with one of the other public + domain TP API's. + + Example program for the nwConn unit / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +uses nwMisc,nwBindry,nwConn,nwServ; + {nwServ used for GetFileServerDateAndTime only} + +Type String25=string[25]; + PTuserInfo=^TuserInfo; + TuserInfo=record + objName :string25; + objId :LongInt; + TrueName :string25; + LoginTime:TnovTime; { time of last logon } + ConnNbr :byte; { 0= not logged on} + next :PTuserInfo; + end; + +var Param : string; + DispAll,DispHelp : boolean; + MyConnNbr : byte; + MyServer : string; + ConnInUse,UsersConnected,ConnNotLogIn:byte; + startPtr : PTuserInfo; + +Procedure ScanBinderyUsers; +Var lastObjSeen:LongInt; + UserName :string; + UserType :word; + UserId :LongInt; + Flag,Security:Byte; + hp :boolean; + nUser,lUser,wUser:PTuserInfo; + tempStr :string; + LogInfo :TloginControl; + +begin +LastObjSeen:=-1; +WHILE ScanBinderyObject('*',1 {OT_USER},LastObjSeen, + UserName,UserType,UserId,Flag,Security,hp) + do begin + New(nUser); + PstrCopy(nUser^.objName,UserName,25); + nUser^.objId:=UserId; + nUser^.ConnNbr:=0; + nUser^.next:=NIL; + + GetObjectLoginControl(UserName,1 {ot_user},LogInfo); + nUser^.LoginTime:=LogInfo.LastLoginTime; + + IF nwBindry.GetRealUserName(UserName,tempstr) + then if (tempStr='') + then tempStr:='_'; + PstrCopy(nUser^.TrueName,tempStr,25); + + wUser:=startPtr; + While (wUser<>NIL) and (wUser^.objName$FC { no such object} + then writeln('Error scanning Bindery.'); + +end; + +Procedure DumpLoginTime(connNbr:byte;objName:string;objId:LongInt;time:TnovTime); +Var nUser,lUser:PTuserInfo; +begin +lUser:=startPtr^.next; +while (lUser<>NIL) and (luser^.objId<>objId) + do lUser:=lUser^.next; +if lUser<>NIL + then begin + if lUser^.ConnNbr=0 { first time the user is found at some connection } + then begin + lUser^.LoginTime:=time; + lUser^.ConnNbr:=ConnNbr; + end + else begin { user logged in at multiple connections } + new(nUser); + nUser^:=lUser^; + {nUser^.next:=lUser^.next} + nUser^.LoginTime:=time; + nUser^.ConnNbr:=ConnNbr; + lUser^.next:=nUser; + end; + end + else begin + writeln('SECURITY WARNING: USER ''',objName,''' @ connection:',connNbr); + writeln(' IS LOGGED IN W/O CORRESPONDING BINDERY OBJECT.'); + end +end; + +procedure DisplayHeader; +Var connId :byte; + username:string; + objType :word; + objID :LongInt; + dateTime:TnovTime; +begin + UpString(Param); + If NOT (GetPreferredConnectionID(connId) and (connId<>0)) + then if NOT (GetDefaultConnectionID(connId) and (connId<>0)) + then GetPrimaryConnectionId(connId); + GetFileServerName(connId,MyServer); + GetConnectionNumber(MyConnNbr); + GetConnectionInformation(MyconnNbr,username,objType,objID,datetime); + if Param='' then writeln('List of currently logged on users for server ',MyServer) + else writeln('List for user ',Param,' on ',MyServer,'.'); + writeln; + writeln('Con: Name: Login/off Time:'); + writeln('--- -------------------- -------------------------'); +end; + + +procedure GetConnectedUsers; +Var connNbr:byte; + objName:string; + objType:word; + objId :LongInt; + LogTime:TnovTime; + {serverInfo:TFileServerInformation;} +begin +ConnInUse:=0; +UsersConnected:=0; +ConnNotLogIn:=0; +{ To determine the maximum number of connections allowed by the + license, you would normally use the + nwServ.GetFileServerInformation(servername,serverInfo) + call. For now, we'll suppose there are max. 250 connectios allowed. } + +for connNbr := 1 to 250 {serverinfo.ConnectionsMax} + do begin + IF GetConnectionInformation(connNbr,objName,objType,objId,LogTime) + then begin + if objName='NOT-LOGGED-IN' + then begin + inc(ConnNotLogIn); + inc(connInUse); + DumpLoginTime(connNbr,objName,objId,LogTime);{ logOUT time } + end + else if objType=1 {OT_USER} + then begin + inc(ConnInUse); + inc(UsersConnected); + DumpLoginTime(connNbr,objName,objId,LogTime);{ logIN } + end + else inc(connInUse); + end + end; {do} +end; + + +procedure DisplayAllUsers; +Var lUser :PTuserInfo; + time,tempStr:string; +Begin +lUser:=startPtr^.next; +while lUser<>NIL + do begin + if (param='') or (pos(param,lUser^.objName)>0) + then begin + if lUser^.ConnNbr=0 + then begin + if DispAll and (lUser^.objName<>'NOT-LOGGED-IN') + then begin + PstrCopy(tempStr,lUser^.objName,20); + write('N/A ',tempStr); + if lUser^.LoginTime.day<>0 + then begin + NovTime2String(lUser^.LoginTime,time); + time[1]:='?';time[2]:='?';time[3]:='?'; + writeln(' ',time); + end + else writeln(' ------not available------'); + writeln('':5,lUser^.TrueName); + end + end + else begin + + NovTime2String(lUser^.LoginTime,time); + PstrCopy(tempStr,lUser^.objName,20); + + write(lUser^.connNbr:3); + if Luser^.ConnNbr=MyConnNbr + then write(' *') + else write(' '); + + writeln(tempstr,' ',time); + writeln('':5,lUser^.TrueName); + end; + end; + lUser:=lUser^.next + end; +end; + + +procedure DisplayFooter; +Var now:TnovTime; + nowStr:string; + remainder:byte; +begin +getFileServerDateAndTime(now); +NovTime2String(now,nowStr); +If UsersConnected=1 then write('1 user is'); +if UsersConnected>1 then write(UsersConnected,' users are'); +if UsersConnected>0 then writeln(' logged into ',MyServer,' as of ',nowStr); +IF ConnNotLogIn=1 then write('1 connection is'); +IF ConnNotLogIn>1 then write(ConnNotLogIn,' connections are'); +IF ConnNotLogIn>0 then writeln(' in use, but the workstation has logged out.'); +remainder:=ConnInUse-UsersConnected-ConnNotLogIn; +IF remainder>0 then writeln(remainder,' connection(s) used by non-user objects.'); +end; + +procedure credits; +begin +writeln; +writeln('WHO: Displays a list of currently logged in users.'); +writeln; +writeln('SYNTAX: WHO [servername/][username] [/A]'); +writeln; +writeln('Servername has to match an existing server.'); +writeln('All users with ''username'' contained in them wil be displayed.'); +writeln; +writeln('Example: WHO Display everyone'); +writeln(' WHO username Display a particular user.'); +writeln(' WHO server/ Display a different server.'); +writeln; +halt(0); +end; + + +procedure ChangeServer; { change default server to something else } +var ServerChanged:Boolean; + p,connId:byte; + NewServer : string; + servername : string; +begin +ServerChanged:=False; +p := pos('/',Param); +NewServer := copy(Param,1,p-1); +UpString(NewServer); +Param := copy(Param,p+1,255); +for connId := 1 to 8 + do begin + GetFileServerName(connId,servername); + if servername=NewServer + then begin + serverChanged:=True; + SetPreferredConnectionId(connId); + end; + end; +if NOT ServerChanged + then begin + writeln('Server ',NewServer,' not found.'); + halt(1); + end; +end; + +Var OldConnId:Byte; + nliConn:PTuserInfo; + +begin {---------main-----------------------------------------------------} + New(startPtr); + New(nliConn); + nliConn^.objName:='NOT-LOGGED-IN'; + nliConn^.objId:=0; + nliConn^.TrueName:=''; + nliConn^.next:=NIL; + nliConn^.connNbr:=0; + startPtr^.next:=nliConn; + startPtr^.objName:=#0; + + if paramcount > 0 + then Param := paramstr(1) + else Param := ''; + DispAll:=(paramCount > 0) + and ( (pos('/A',paramstr(1))=1) + or (pos('/a',paramStr(1))=1) + ); + If dispall then param:=''; + DispAll:=DispAll or ( (paramCount > 1) + and ( (pos('/A',paramstr(2))=1) + or (pos('/a',paramStr(2))=1) + ) + ); + UpString(Param); + DispHelp:=(Param = '?') or (Pos('/H',Param)=1); + + + GetPreferredConnectionId(OldConnId); + if DispHelp then credits; + if pos('/',Param) > 1 then ChangeServer; + ScanBinderyUsers; + GetConnectedUsers; + DisplayHeader; + DisplayAllUsers; + DisplayFooter; + SetPreferredConnectionId(OldConnId); +end. + diff --git a/NWTP/XFILE/GETOFIL.PAS b/NWTP/XFILE/GETOFIL.PAS new file mode 100644 index 0000000..230960a --- /dev/null +++ b/NWTP/XFILE/GETOFIL.PAS @@ -0,0 +1,87 @@ +program GetOFil; + +{ Testprogram for the nwFile unit / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +{ Uses the following calls: + + GetConnectionsOpenFiles (nwServ) + MapDirEntryIdToPath (nwFile) + +} + +uses nwMisc,nwConn,nwFile,nwServ; + + +Var ConnNumber : Byte; + LastRecordSeen : word; + NbrOfRecords : word; + FileInfo : TfileInfoRecList; + t : Byte; + ExtPath,DosPath: string; + VolName : string; + + errCode:Integer; + dHelp:byte; + INaddress:TinternetworkAddress; + +begin +dHelp:=0; + +If (NOT CheckConsolePrivileges) + then dHelp:=4; + +IF (ParamCount<>1) + then dHelp:=1; + +if dHelp=0 + then begin + Val(ParamStr(1),ConnNumber,errCode); + if (errCode<>0) then dHelp:=2; + end; + +{ determine whether connectionnumber is valid. + Remember: using invalid connectionnumbers in combination with the + GetConnection'sOpenFiles function may result in an Abend.. } +if (dHelp=0) and (NOT GetInterNetAddress(connNumber,INaddress)) + then dHelp:=3; + +if dHelp>0 + then begin + if (dHelp=2) or (dHelp=3) + then writeln('Error: invalid connection number specified.'); + if dHelp=4 + then writeln('!! You need console operator privileges to use this utility.'); + writeln; + writeln('GETOFIL - Get connection''s open files.'); + writeln; + writeln('Usage: GETOFIL '); + halt(1); + end; + +LastRecordSeen:=0; + +Writeln('Files currently held open by connection ',ConnNumber); +REPEAT { iterate / "only" 28 files (max.) returned per call } + IF GetConnectionsOpenFiles (ConnNumber, LastRecordSeen, + NbrOfRecords ,FileInfo) + then begin + for t:=1 to NbrOfRecords + do with FileInfo[t] + do begin + IF MapDirEntryIdToPath(VolNbr,ParentEntryID,NStype, + FileName) + then begin + NovPath2DosPath(ExtPath,DosPath); + GetVolumeName(VolNbr,VolName); + writeln(VolName,':',DosPath,'\',FileName) + end + else writeln('Error calling MapDirEntryIdToPath, err#',nwFile.result); + end; + end + else begin + writeln('err: ',nwServ.result); + halt(1); + end; +UNTIL LastRecordSeen=0; + +end. diff --git a/NWTP/XFILE/LDIR.PAS b/NWTP/XFILE/LDIR.PAS new file mode 100644 index 0000000..36d3e48 --- /dev/null +++ b/NWTP/XFILE/LDIR.PAS @@ -0,0 +1,243 @@ +program ldir; + +{ Testprogram for the nwFile unit / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +{ LDIR is an alternative for NDIR, showing the use of the + ScanDirectoryEntry function. } + +uses nwMisc,nwBindry,nwFile; + +Function GetEntryAttributeString(Attr:Longint):string; +Var res:string; +begin +if (Attr and A_DIRECTORY)=0 + then res:=' RwSAXHSyTPCDR ' + else res:=' -----HSy-P-DR '; +if (Attr and A_HIDDEN)=0 then res[7]:='-'; +if (Attr and A_SYSTEM)=0 then begin res[8]:='-';res[9]:='-'; end; +if (Attr and A_RENAME_INHIBIT)=0 then res[14]:='-'; +if (Attr and A_DELETE_INHIBIT)=0 then res[13]:='-'; +if (Attr and A_PURGE)=0 then res[11]:='-'; +if (Attr and A_DIRECTORY)=0 + then begin + if ((Attr and A_READ_ONLY)>0) then res[3]:='o'; + if (Attr and A_EXECUTE_ONLY)=0 then res[6]:='-'; + if (Attr and A_NEEDS_ARCHIVED)=0 then res[5]:='-'; + if (Attr and A_SHAREABLE)=0 then res[4]:='-'; + if (Attr and A_TRANSACTIONAL)=0 then res[10]:='-'; + if (Attr and A_COPY_INHIBIT)=0 then res[12]:='-'; + end; +GetEntryAttributeString:=res; +end; + +Var DirHandle:Byte; + DirPath:String; + SequenceNumber:Byte; + TrusteeInfo: TtrusteeInformation; + + t:Byte; + ObjName:string; + ObjType:word; + ObjId:Longint; + DH,EffRights:byte; + + EntryName:string; + SearchFlags:Longint; + EntryId:Longint; + Entry:Tentry; + s,s1:string; + + p:byte; + OwnerName:string; + OwnerType:word; + + entry2:Tentry; +begin + +DirHandle:=0; + +if ParamCount>0 + then s:=paramStr(1) + else s:='.'; + +IF NOT GetTrueEntryName(s,DirPath) + then begin + writeln('Error resolving given filename (err: 0-',nwfile.result,')'); + halt(1); + end; + +if pos(':',DirPath)=2 + then begin + writeln('You cannot use this program on a local drive.'); + halt(1); + end; + +{ ok. Try to separate EntryName from path } + +p:=ord(DirPath[0]); +while (p>0) and (DirPath[p]<>'\') do dec(p); + +s:=copy(DirPath,p+1,255); +if (pos('.',s)>0) or (pos('*',s)>0) or (pos('?',s)>0) + then begin { last part is definately a filename } + EntryName:=s; + DirPath[0]:=chr(p-1); + IF NOT AllocTemporaryDirHandle(31,0,DirPath,DH,EffRights) + then begin + writeln('Could not locate directory (err: 1-',nwfile.result,')'); + halt(1); + end; + end + else begin + IF AllocTemporaryDirHandle(31,0,DirPath,DH,EffRights) { assume it's a path} + then begin + { whole thing appears to be a path.. } + EntryName:='*'; + end + else begin + { whoops.. not a path, but a filename without an extension } + EntryName:=s; + DirPath[0]:=chr(p-1); + IF NOT AllocTemporaryDirHandle(31,0,DirPath,DH,EffRights) + then begin + writeln('Could not locate directory (err: 2-',nwfile.result,')'); + halt(1); + end; + end; + end; + + +writeln('EntryName Size Flags EntryID Creation Owner'); +writeln('------------+---------+---------------+--------+---------------+----------'); + +SearchFlags:=$ef; { all NON directories, i.e. all files } +EntryId:=-1; + +While ScanDirectoryEntry(DH,EntryName,SearchFlags,EntryID,Entry) + do begin + s:=entry.EntryName; + p:=pos('.',s); + if p=0 + then begin + s:=s+' '; + s[0]:=#12; + end + else begin + s1:=copy(s,1,p-1)+' '; + s1[0]:=#8; + s:=s1+copy(s,p,255)+' '; + s[0]:=#12; + end; + write(s); + write(entry.FileSize:10); + s:=GetEntryAttributeString(entry.Attributes); + write(' F',s); + write(HexStr(entryId,8)); + + NovTime2String(entry.CreationTime,s); + delete(s,1,5);dec(s[0],3); + delete(s,8,2); + s[3]:='-';s[7]:='-'; + write(' ',s); + + GetBinderyObjectName(entry.OwnerId,OwnerName,OwnerType); + s:=ownerName+' '; + s[0]:=#10; write(' ',s); + writeln; + + end; + +if nwFile.result<>$FF { no more matching entries } + then writeln('Error scanning directory information (err : 3-',nwfile.result,')'); + + +{-- As an extra gimmick: if you DEFINE ShowScan, salvagable files will + also be shown.. } + +{$IFDEF ShowScan} + +{ Scan salvagable files.. } +EntryId:=-1; + +WHILE ScanSalvagableFiles(DH,EntryId,Entry) + do begin + s:=entry.EntryName; + p:=pos('.',s); + if p=0 + then begin + s:=s+' '; + s[0]:=#12; + end + else begin + s1:=copy(s,1,p-1)+' '; + s1[0]:=#8; + s:=s1+copy(s,p,255)+' '; + s[0]:=#12; + end; + write(s); + write(entry.FileSize:10); + s:=GetEntryAttributeString(entry.Attributes); + write(' S',s); + write(HexStr(entryId,8)); + + NovTime2String(entry.CreationTime,s); + delete(s,1,5);dec(s[0],3); + delete(s,8,2); + s[3]:='-';s[7]:='-'; + write(' ',s); + + GetBinderyObjectName(entry.OwnerId,OwnerName,OwnerType); + s:=ownerName+' '; + s[0]:=#10; write(' ',s); + + writeln; + + end; +If nwFile.result<>$FF { normal iteration end } + then writeln('Error using ScanSalvagableFiles.'); + +{$ENDIF} + +{------------------ show subdir info } + +SearchFlags:=$3f; +EntryId:=-1; + +While ScanDirectoryEntry(DH,EntryName,SearchFlags,EntryID,Entry) + do begin + s:=entry.EntryName; + p:=pos('.',s); + if p=0 + then begin + s:=s+' '; + s[0]:=#11; + end + else begin + delete(s,p,1); + s:=s+' '; + s[0]:=#11; + end; + write('\',s); + + write(0:10); { filesize } + + s:=GetEntryAttributeString(entry.Attributes); + write(' D',s); + write(HexStr(entryId,8)); + + NovTime2String(entry.CreationTime,s); + delete(s,1,5);dec(s[0],3); + delete(s,8,2); + s[3]:='-';s[7]:='-'; + write(' ',s); + + GetBinderyObjectName(entry.OwnerId,OwnerName,OwnerType); + s:=ownerName+' '; + s[0]:=#12; write(' ',s); + writeln; + end; +if nwFile.result<>$FF { no more matching entries } + then writeln('Error scanning directory information (err : 4-',nwfile.result,')'); + +DeallocateDirHandle(DH); +end. \ No newline at end of file diff --git a/NWTP/XFILE/TSTDH.PAS b/NWTP/XFILE/TSTDH.PAS new file mode 100644 index 0000000..169860b --- /dev/null +++ b/NWTP/XFILE/TSTDH.PAS @@ -0,0 +1,211 @@ +program tstdh; + +{ Testprogram for the nwFile unit / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +uses nwMisc,nwConn,nwFile; + +{ Test the following drive(handle) related calls: + + GetDirectoryHandle + GetDirectoryPath + GetDriveConnectionId + GetDriveHandle + GetDriveFlag + GetNumberOfLocalDrives (nwConn) + GetVolumeNameWithHandle + IsNetworkDrive + + GetEnvPath + GetSearchDriveVector + IsSearchDrive + SetEnvPath + SetSearchDriveVector + +} + +Var t,dirhandle,status:byte; + status2,dirHandle2:byte; + volname:string; + connId:byte; + locDrives:byte; + dirPath:string; + serverName:string; + + NewDirHandle, EffectiveRights:byte; + connId2,Dflag:byte; + + vector,vector2:TsearchDriveVector; + pth,pth2:string; + +begin +{-- drivenumbers, dirhandles --} + +nwconn.GetNumberofLocalDrives(locDrives); +writeln('Local Drives:',locDrives); { Drivenumbers 0..locDrives-1 taken by local drives } +writeln; + +for t:=0 to locDrives-1 + do If IsNetworkDrive(t) + then writeln('Error?: IsNetworkDrive failed for drive ',chr(ord('A')+t)); + +for t:=locDrives to 31 + do begin + IF GetDirectoryHandle(t,dirHandle,status) + then begin + writeln(chr(t+ord('A')),' handle=',dirHandle,' status=',status); + GetDriveHandle(t,dirHandle2); + GetDriveFlag(t,status2); + if (status<>status2) or (dirHandle<>dirHandle2) + then writeln('Error: GetDirectoryHandle data differs from GetDriveHandle/Flag data.'); + + if IsNetworkDrive(t) XOR ((status AND $80)=0) + then writeln('Error: IsNetworkDrive failed.'); + + GetDirectoryPath(dirHandle,dirpath); + write('=> ',dirpath); + + if GetDriveConnectionId(t,connId) + then begin + write(' connId=',connId); + nwConn.GetFileServerName(connId,serverName); + write(' volume=\\',servername,'\'); + end; + + IF (status<3) {temporary or permanent mapping} + and GetVolumeNameWithHandle(dirHandle,volName) + then write(volName); + + writeln; + end + end; + + +{-- seachdrives, searchdrive vector-- } +writeln; +writeln(' to continue..'); +writeln; +readln; + +GetSearchDriveVector(vector); +write('Searchdrivevector: '); +for t:=1 to 17 + do if vector[t]<255 + then write(chr(byte(vector[t]+ord('A'))),' ') + else write('$FF '); +writeln; + +FillChar(vector2,Sizeof(TsearchDriveVector),#$FF); +SetSearchDriveVector(vector2); +FillChar(vector2,Sizeof(TsearchDriveVector),#$00); +GetSearchDriveVector(vector2); +if (vector2[1]<>$FF) + then writeln('Error: SetSearchdriveVector Failed.'); +SetSearchDriveVector(vector); { restore old vector } + +GetEnvPath(pth); +writeln('PATH setting in the master environment=',pth); +SetEnvPath('this_is_a_bogus_path;'); +GetEnvPath(pth2); +writeln('Path is temporarily set to: ',pth2); +SetEnvPath(pth); +writeln('Path reset to original value.'); + +Write('Searchdrives (using IsSearchDrive):'); +for t:=locDrives to 31 + do If IsSearchDrive(t) + then write(chr(t+ord('A')),' '); +writeln; + +{----------------- directory handles ---------------------} +writeln(' to continue..'); +writeln; + +t:=locDrives; +While (t<26) and GetDirectoryHandle(t,dirHandle,status) + do inc(t); +Writeln('First free drive: ',chr(t+ord('A'))); + +IF NOT AllocPermanentDirHandle(t,0,'SYS:LOGIN',NewDirHandle, EffectiveRights) + then writeln('Error calling AllocPermanentDirHandle: ',nwFile.result); + +GetDirectoryPath(NewDirHandle,pth); +IF SetDriveHandle(t,NewDirHandle) + then writeln('SetDriveHandle: drive ',chr(t+ord('A')),' asociated with dirHandle to path ',pth) + else writeln('Error using SetDriveHandle: ',nwFile.result); + +GetDriveHandle(t,dirHandle); +if dirHandle<>NewDirHandle + then writeln('Error in SetDriveHandle.'); + +GetEffectiveConnectionId(connId); +{ all requests have been sent to the effective server up to now, so + that's the server the drive is connected to } +SetDriveConnectionId(t,connId); +GetDriveConnectionId(t,connId2); +If ConnId2<>ConnId + then writeln('Error in Set/GetDriveConnectionId'); +GetDriveFlag(t,Dflag); +SetDriveFlag(t,(Dflag and $80) or 1); + +writeln('(non-root) Mapping completed.'); +{ see the MAPDRIVE function in nwFile; + it creates (temporary) mappings in much the same way as above } + + +{map a temporary drive} +IF MapDrive(31,'SYS:MAIL',{root drive:} false, {permanent mapping:} false {true}) + then begin + GetDriveHandle(31,dirHandle); + GetDirectoryPath(dirHAndle,pth); + Writeln('Temporary! drive ',chr(31+ord('A')),' mapped to ',pth); + { If mapdrive works, so must + + DeleteFakeRootDirectory + GetTrueEntryName + MapFakeRootDirectory + + } + end + else writeln('Error using MapDrive: ',nwFile.result); + + +writeln; +t:=locDrives; +While (t<26) and GetDirectoryHandle(t,dirHandle,status) + do inc(t); +Writeln('Another free drive: ',chr(t+ord('A'))); + +IF MapSearchDrive(t,'SYS:PUBLIC',16,{insert:}false, + {root:}false,{permanent:}true) + then begin + GetDriveHandle(t,dirHandle); + GetDirectoryPATh(dirHAndle,pth); + Writeln('drive ',chr(t+ord('A')),' mapped (as a searchdrive) to ',pth); + end + else writeln('Error using MapSearchDrive: ',nwFile.result); + + +writeln; +t:=locDrives; +While (t<26) and GetDirectoryHandle(t,dirHandle,status) + do inc(t); +Writeln('Another free drive: ',chr(t+ord('A'))); + +IF MapPermanentDrive(t,'SYS:PUBLIC',{root:}true) + then begin + GetDriveHandle(t,dirHandle); + GetDirectoryPATh(dirHAndle,pth); + Writeln('drive ',chr(t+ord('A')),' mapped (permanently) to ',pth); + + IF NOT SetDirectoryHandle(0,'SYS:MAIL',dirHandle) + then writeln('Error using SetDirectoryHandle: ',nwFile.result) + else begin + GetDirectoryPATh(dirHAndle,pth); + Writeln('drive ',chr(t+ord('A')),' => handle changed to ',pth); + end; + + end + else writeln('Error using MapPermanentDrive: ',nwFile.result); + + +end. \ No newline at end of file diff --git a/NWTP/XFILE/TSTENT2.PAS b/NWTP/XFILE/TSTENT2.PAS new file mode 100644 index 0000000..241fc17 --- /dev/null +++ b/NWTP/XFILE/TSTENT2.PAS @@ -0,0 +1,138 @@ +Program tstent2; + +{ Testprogram for the nwFile unit / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +{ Tests the following nwFile calls: + + GetDirectoryEntry + ScanDirectoryInformation + ScanFileInformation + ScanSalvagableFiles +} + +uses nwMisc,nwBindry,nwFile; + + +Procedure PrintEntry(e:Tentry); +VAr s,ObjName:String; + ObjType:Word; +begin +with e + do begin + writeln('----------------------------------'); + writeln('Name:',EntryName); + writeln('FileSize :',FileSize); + writeln('Name space :',NStype); + writeln('Data fork size :',DataForkSize); + writeln; + writeln('Attributes:',HexStr(Attributes,8)); + writeln('RightsMask:',HexStr(RightsMask,4)); + writeln; + + objName:=''; + NovTime2String(CreationTime,s); + GetBinderyObjectName(OwnerId,ObjName,ObjType); + writeln('Created at :',s,' by ',ObjName,' (',HexStr(OwnerId,8),')'); + + objName:=''; + NovTime2String(ArchiveTime,s); + GetBinderyObjectName(ArchiverId,ObjName,ObjType); + writeln('Last archived at :',s,' by ',ObjName,' (',HexStr(ArchiverId,8),')'); + + objName:=''; + NovTime2String(ModifyTime,s); + GetBinderyObjectName(ModifierId,ObjName,ObjType); + writeln('Last modified at :',s,' by ',ObjName,' (',HexStr(ModifierId,8),')'); + + NovTime2String(LastAccessTime,s); + writeln('Last accessed at :',s); + + objName:=''; + NovTime2String(DeleteTime,s); + GetBinderyObjectName(DeletorId,ObjName,ObjType); + writeln('Deleted at :',s,' by ',ObjName,' (',HexStr(DeletorId,8),')'); + writeln; + end; +end; + +Var DirHandle,EffRights:Byte; + DirEntry:Tentry; + SearchFlags,EntryId:Longint; + PathNAme:STring; + SeqNbr:Integer; + WseqNbr:word; + +begin + +AllocPermanentDirHandle(7,0,'SYS:PUBLIC',DirHandle,EffRights); + +writeln; +writeln('Test of GetDirectoryEntry (get entry SYS:\PUBLIC'); +write(' to contimue...'); +readln; + +IF GetDirectoryEntry(dirHandle,dirEntry) + then PrintEntry(dirEntry) + else writeln('Error using GetDirectoryEntry, err#',nwFile.result); + +SetDirectoryHandle(0,'SYS:',DirHandle); + +GetDirectoryPAth(DirHandle,PathNAme); +writeln('handle ass. with:',PathName); + +writeln; +writeln('Test of ScanDirectoryInformation (scan subdirs of SYS:\PUBLIC'); +write(' to contimue...'); +readln; + +WseqNbr:=0; +while ScanDirectoryInformation(dirHandle,'PUBLIC\*',WseqNbr,dirEntry) + do begin + writeln('SeqNbr now is: ',WseqNbr); + PrintEntry(DirEntry); + end; +if nwFile.result<>$9C + then writeln('error using ScanDirInfo :',nwFile.result); + + +writeln; +writeln('Test of ScanFileInformation (scan *.EXE files in SYS:\PUBLIC'); +write(' to contimue...'); +readln; + +seqNbr:=-1; +while ScanFileInformation(DirHandle,'PUBLIC\*.EXE', + $FF, + SeqNbr, + dirEntry) + do begin + PrintEntry(DirEntry); + end; +if nwFile.result<>$FF + then writeln('error using ScanFileInfo :',nwFile.result); + + +writeln; +writeln('Test of ScanSalvagableFiles (erased files in SYS:\PUBLIC'); +write(' to contimue...'); +readln; + +SetDirectoryHandle(0,'SYS:\PUBLIC',DirHandle); + + +EntryId:=-1; +WHILE ScanSalvagableFiles(DirHandle, + EntryId, + dirEntry) + do begin + PrintEntry(dirEntry); + writeln('NextEntryId:',hexStr(entryId,8)); + end; +if nwFile.result<>$FF + then writeln('error using ScanSalvagable files :',nwFile.result); + + +DeallocateDirHandle(DirHandle); + + +end. \ No newline at end of file diff --git a/NWTP/XFILE/TSTENTRY.PAS b/NWTP/XFILE/TSTENTRY.PAS new file mode 100644 index 0000000..9e87bd8 --- /dev/null +++ b/NWTP/XFILE/TSTENTRY.PAS @@ -0,0 +1,91 @@ +Program tstentry; + +{ Example for the nwFile unit / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +{ Tests the following nwFile calls: + + AllocPermanentDirHandle + CreateDirectory + DeallocateDirHandle + RenameDirectory + ScanDirRestrictions + SetDirectoryHandle + SetDirRestriction + +} +uses nwMisc,nwFile; + +Var DirHandle:Byte; + NumberOfEntries:Byte; + RestrInfo:TdirRestrList; + t,EffRights:Byte; + +begin + + +AllocPermanentDirHandle(7,0,'SYS:',DirHandle,EffRights); + +IF NOT CreateDirectory(DirHandle,'NWTP',$FF) + then writeln('Error using CreateDirectory, err# ',nwFile.result); + +SetDirectoryHandle(DirHandle,'NWTP',DirHandle); + +IF NOT SetDirRestriction(DirHandle,4096) { 4096 blocks = 16 Mb } + then writeln('Error using SetDirRestriction, err# ',nwFile.result); + +IF NOT ScanDirRestrictions(DirHandle,NumberOfEntries,RestrInfo) + then writeln('Error calling ScanDirRestrictions:',nwFile.result); +writeln(NumberOfEntries); +For t:=1 to NumberOfEntries + do begin + writeln; + writeln('Level :',RestrInfo[t].level); + writeln('MaxBlocks :',HexStr(RestrInfo[t].MaxBlocks,8)); + writeln('AvailBlocks:',HexStr(RestrInfo[t].AvailableBlocks,8)); + end; +writeln('------'); + +IF NOT CreateDirectory(DirHandle,'TEST',$FF) + then writeln('Error using CreateDirectory, err# ',nwFile.result); + +IF NOT RenameDirectory(DirHandle,'TEST','TESTDIR') + then writeln('Error using RenameDirectory, err# ',nwFile.result); + + +IF NOT SetDirectoryHandle(DirHandle,'TESTDIR',DirHandle) + then writeln('Error using SetDirectoryHandle, err# ',nwFile.result) + else writeln('SetDirHandle: ok'); + +IF NOT SetDirRestriction(DirHandle,8192) + then writeln('Error using SetDirRestriction, err# ',nwFile.result); + +IF NOT ScanDirRestrictions(DirHandle,NumberOfEntries,RestrInfo) + then writeln('Error calling ScanDirRestrictions:',nwFile.result); +writeln(NumberOfEntries); +For t:=1 to NumberOfEntries + do begin + writeln; + writeln('Level :',RestrInfo[t].level); + writeln('MaxBlocks :',HexStr(RestrInfo[t].MaxBlocks,8)); + writeln('AvailBlocks:',HexStr(RestrInfo[t].AvailableBlocks,8)); + end; + +writeln(' to continue...........'); +readln; + +SetDirRestriction(DirHandle,0); + +SetDirectoryHandle(0,'SYS:NWTP',DirHandle); +IF Not DeleteDirectory(DirHandle,'TESTDIR') + then writeln('Error using DeleteDirectory, err#', nwFile.result); + +{SetDirRestriction(DirHandle,0);} + +SetDirectoryHandle(0,'SYS:',DirHandle); +IF Not DeleteDirectory(DirHandle,'NWTP') + then writeln('Error using DeleteDirectory, err#', nwFile.result); + + +DeallocateDirHandle(DirHandle); + +end. \ No newline at end of file diff --git a/NWTP/XFILE/TSTTRUST.PAS b/NWTP/XFILE/TSTTRUST.PAS new file mode 100644 index 0000000..9ab618c --- /dev/null +++ b/NWTP/XFILE/TSTTRUST.PAS @@ -0,0 +1,58 @@ +program tsttrust; + +{ Testprogram for the NwFile unit / NwTP 0.6, (c) 1993,1995 R.Spronk } + +uses nwMisc,nwBindry,nwFile; + +Var DirHandle:Byte; + DirPath:String; + SequenceNumber:Byte; + TrusteeInfo: TtrusteeInformation; + + t:Byte; + ObjName:string; + ObjType:word; + ObjId:Longint; + DH,EffRights:byte; +begin + +DirHandle:=0; +DirPath:='SYS:SYSTEM'; + +IF NOT AllocTemporaryDirHandle(31,0,DirPath,DH,EffRights) + then writeln('allocTempDH returned err: ',nwfile.result); + +{ Before scanning the trustees, you might add or delete a + trustee for testing purposes. + +ObjId:=$06000006; +IF NOT DeleteTrustee(0,DirPath,ObjId) + then writeln('DeleteTrustee returned error: ',nwFile.result); + +ObjId:=$06000006; +IF NOT SetTrustee(0,DirPath,ObjId,TA_READ or TA_SEARCH) + then writeln('SetTrustee returned error: ',nwFile.result); } + +SequenceNumber:=0; +While ScanEntryForTrustees(DirHandle,DirPath, + SequenceNumber,TrusteeInfo) + do begin + with TrusteeInfo + do begin + for t:=1 to TrusteeInfo.NumberOfTrustees + do begin + write(HexStr(TrusteeID[t],8)); + GetBinderyObjectName(TrusteeId[t],ObjName,oBjType); + writeln(' ',HexStr(TrusteeRights[t],4),' ',ObjName); + end; + + end; + end; + +if nwFile.result<>$9C { no more trustees } + then writeln('ScanEntryForTrustees returned error :',nwfile.result); + +DeallocateDirHandle(DH); + +readln; +end. diff --git a/NWTP/XFILE/TSTVOL.PAS b/NWTP/XFILE/TSTVOL.PAS new file mode 100644 index 0000000..e4c3b0e --- /dev/null +++ b/NWTP/XFILE/TSTVOL.PAS @@ -0,0 +1,170 @@ +{$X+,B-,V-} {essential compiler directives} + +program tstvol; + +{ Example for the nwFile unit / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +{ Tests the following volume related functions in nwFile and nwServ: + + ClearObjectVolRestriction + GetDiskUtilization (nwServ) + GetObjectVolRestriction + GetVolumeName + GetVolumeNumber + GetVolumeUsage + IsVolumeRemovable + SetObjectVolRestriction + +} + +uses nwMisc,nwServ,nwFile; + + +Var volNbr :byte; + volName:string; + + volUsage :TvolUsage; + isremovable:boolean; + + MaxAllowedBlocks, + BlocksInUse:Longint; + + VolumeNbr :byte; + sequenceNbr :LongInt; + NbrOfObjects:byte; + ResultBuffer:TobjVolRestr; + + usedDirs,usedFiles,usedBlocks:word; + + t:byte; +begin +writeln('Testing GetVolumeNumber'); +IF GetVolumeNumber('XXXX',volNbr) + then writeln('--Err. GetVolumeNuber returned a volumenumber when it should have failed.'); +IF GetVolumeNumber('VOL',volNbr) + then writeln(' VolNbr of ''VOL:'' equals ',volNbr) + else writeln(' Volume VOL appears not to exist.'); +IF GetVolumeNumber('SYS',volNbr) + then writeln(' VolNbr of SYS: equals ',volNbr) + else begin + writeln('Error Using GetVolumeNumber, err#',nwFile.result); + writeln('Critical error. Test aborted.'); + halt(1); + end; + +writeln; +writeln('Testing GetVolumeName'); +for volNbr:=0 to 31 + do begin + IF GetVolumeName(volNbr,volName) + then writeln(' VolNbr ',volNbr,' is ''',volName,'''') + else if result<>$98 { no such volume } + then writeln('--Error testing GetVolumeName, err#',result); + end; + +writeln; +writeln('Testing GetVolumeUsage.'); +IF GetVolumeNumber('SYS:',volNbr) and GetVolumeUsage(volNbr,VolUsage) + then with VolUsage + do begin + writeln(' Volume Usage Information:'); + writeln(' Total Blocks: ',totalBlocks,' Free: ',freeBlocks); + writeln(' Total Dir entries: ',totalDirEntries,' Free: ',availDirEntries); + writeln(' Volumename: ''',volumename,''''); + writeln(' Blocksize: ',SectorsPerBlock*512,' bytes.'); + end; + + +writeln; +writeln('Testing IsVolumeRemovable'); +IF IsVolumeRemovable(0,isremovable) + then writeln(' Removable: ',isremovable) + else writeln('--IsVolumeRemovable failed.'); + + +{==================Testing the volume restriction calls====================} + +GetVolumeNumber('SYS',volNbr); + +writeln('Getting the volume restrictions for user supervisor on SYS volume..'); +IF GetObjectVolRestriction(volNbr,1 {objectId of Supervisor}, + MaxAllowedBlocks,BlocksInUse) + then begin + writeln(' GetObjectVolRestriction:'); + writeln(' Supervisor on volume SYS'); + write(' Restricted to:'); + if MaxAllowedBlocks=$40000000 + then write('(unlimited)') + else write(MaxAllowedBlocks); + writeln(' Blocks ,BlocksInUse: ',BlocksInUse); + end + else writeln('--Error returned by GetObjectVolRestriction, err#',nwFile.result); + +writeln; +writeln('Testing GetDiskUtilization (nwServ) for user supervisor'); +IF NOT nwServ.GetDiskUtilization(volNbr,1 {supervisor ObjId},usedDirs,usedFiles,usedBlocks) + then begin + writeln('--Error returned by nwServ.GetDiskUtilization, err#',nwServ.result); + if nwServ.result>=$89 + then writeln('--( Object Search/Read rights are necessary.)'); + end + else begin + writeln(' dirs in use :',usedDirs); + writeln(' files in use :',usedFiles); + writeln(' blocks in use:',usedBlocks); + { note that blocksinuse is a word (Max ownership= 262 Mb.) + GetObjectVolRestriction returns the BlocksInUse value as a LongInt } + writeln(' (created/owned dirs/files/blocks)'); + end; + +writeln('Test of SetObjectVolRestriction:'); +IF NOT SetObjectVolRestriction(volNbr,$1,BlocksInUse+100) + then begin + writeln('--Error returned by setObjectVolRestriction, err#',nwFile.result); + if nwFile.result=140 + then writeln('--( Supervisor equivalent rights are necessary.)'); + end + else writeln('--OK, volume restriction set.'); + +IF GetObjectVolRestriction(volNbr,$1 {objectId of Supervisor}, + MaxAllowedBlocks,BlocksInUse) + then begin + if MaxAllowedBlocks=$40000000 { no restriction } + then writeln('--SetObjectVolRestriction failed.'); + end; + +writeln; +writeln('Testing ScanVolForRestrictions:'); +sequenceNbr:=0; +While ScanVolForRestrictions(volNbr,sequenceNbr,NbrOfObjects,ResultBuffer) + do begin + for t:=1 to NbrOfObjects + do begin + write(' ObjectId:',HexStr(resultbuffer[t].objId,8)); + writeln(' /Restriction: ',resultBuffer[t].MaxallowedBlocks,' Blocks'); + end; + end; +IF nwFile.result=$FF { NO MORE DATA, normal iteration end } + then writeln('--No (more) volume restrictions.') + else writeln('--Error returned by ScanVolForRestrictions, err#',nwFile.result); + + +writeln; +writeln('Clearing SUPERVISOR restrictions using ClearObjectVolRestriction.'); +IF NOT ClearObjectVolRestriction(VolNbr,$1) + then begin + writeln('--Error calling ClearObjectVolRestriction, err#',nwFile.result); + if nwFile.result=140 + then writeln('--( Supervisor equivalent rights are necessary.)'); + end; + + +IF GetObjectVolRestriction(volNbr,1 {objectId of Supervisor}, + MaxAllowedBlocks,BlocksInUse) + then begin + if MaxAllowedBlocks=$40000000 + then writeln('--OK, user now has no restriction.') + else writeln('--Error: ClearObjectVolRestriction Failed.'); + end; + +end. \ No newline at end of file diff --git a/NWTP/XFILE/USPACE.PAS b/NWTP/XFILE/USPACE.PAS new file mode 100644 index 0000000..eb3f5a6 --- /dev/null +++ b/NWTP/XFILE/USPACE.PAS @@ -0,0 +1,100 @@ +{$X+,B-,V-} {essential compiler directives} + +program uspace; + +{ Example for the nwFile unit / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +{ Purpose: A quick way of telling how much space each of your users is + taking up. For Novell 3.x networks. + + +(3867) Tue 29 Mar 94 19:01 +By: Richard Jary +To: All +Re: Disk Space by Login Name +St: +------------------------------------------------------------ +After a quick way of telling how much space each of our users is +taking up. Novell 3.11 system, same info as you get by SYSCON -> +UserInfo -> Volume/Disk Restrictions. But preferably for all +users in a text file format, rather than one by one. + +Reason - we have a shared area with many directories. All with +fun names like CAMBODIA, where any one of 10 people could be +saving stuff. I'd like to go the the (l)users and say "OI! - +You've got 40MB of stuff on this server - Clean It Up!" sort +of thing. Either more or less polite depending on the user +involved! + +Richard +Internet: rjary@nibueng.ccdn.otc.com.au + +--- msgedsq 2.1a + * Origin: Networking from Narara. (3:711/445) } + + +Uses nwMisc,nwBindry,nwFile; + +Var lastObjSeen : Longint; + RepName : String; + RepType : Word; + RepId : LongInt; + RepFlag : Byte; + RepSecurity : Byte; + RepHasProperties: Boolean; + FullUserName : string; + MaxAllowedBlocks,BlocksInUse:Longint; + VolumeNbr : Byte; + VolUsageInfo : TvolUsage; + Version : Word; + + VolInfo:array[0..31] of record + Name :string; + SectorsPerBlock:Byte; + end; + +begin +If NOT IsShellLoaded + then begin + writeln('USPACE requires:'); + writeln(' -The shell to be loaded;'); + halt(1); + end; + +GetNWversion(version); +if version<300 + then begin + writeln('Netware 3.x only.'); + halt(1); + end; + +for VolumeNbr:=0 to 31 + do begin + IF GetVolumeName(VolumeNbr,VolInfo[VolumeNbr].name) + and GetVolumeUsage(VolumeNbr,VolUsageInfo) + then VolInfo[VolumeNbr].SectorsPerBlock:=VolUsageInfo.SectorsPerBlock; + end; + +lastObjSeen:=-1; +While ScanBinderyObject('*',OT_USER, + lastObjSeen, + RepName,RepType,RepId,RepFlag,RepSecurity,RepHasProperties) + do begin + GetRealUserName(RepName,FullUserName); + writeln(Repname+' ('+FullUserName+')'); + + For VolumeNbr:=0 to 31 + do if GetObjectVolRestriction(VolumeNbr,RepId,MaxAllowedBlocks,BlocksInUse) + then begin + write(' ',volInfo[VolumeNbr].Name,' :', + BlocksInUse*(VolInfo[VolumeNbr].SectorsPerBlock DIV 2),' Kb.'); + if MaxAllowedBlocks=$40000000 + then writeln + else writeln(' Restriction: ',MaxAllowedBlocks*4,' Kb.'); + end; + end; +if nwBindry.Result=$FC { NO_SUCH_OBJECT, indicates end of search } + then writeln('') + else writeln('error scanning bindery:',HexStr( nwBindry.Result,2)); + +end. diff --git a/NWTP/XFILE/VOLSTAT.PAS b/NWTP/XFILE/VOLSTAT.PAS new file mode 100644 index 0000000..bcd8448 --- /dev/null +++ b/NWTP/XFILE/VOLSTAT.PAS @@ -0,0 +1,111 @@ +{$X+,B-,V-} {essential compiler directives} + +program volstat; + +{ Example for the nwFile unit / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +{ Purpose: Lists all volume information for each volume on all + servers logged in to. If used with the parameter 'c', + the volume information will be displayed in a condensed + form and will be updated every 5 seconds. + +Tests the following functions in the nwDir Unit: + + GetVolumeUsage + GetVolumeName + GetVolumeNumber + + +(3410) Thu 10 Feb 94 11:24 +By: Frank Van.Wensveen +To: All +Re: MULTISERVER VOLINFO +St: +------------------------------------------------------------ +Ik zoek z.s.m. een utility waarmee ik de volspace van meerdere +servers tegelijk in de gaten kan houden. Een soort multi-server +VolInfo dus. Er zijn pakketten om dat heel mooi te doen (ik heb +er zelfs een ter evaluatie liggen momenteel) maar ik zoek nu +even snel (want de nood is hoog) iets eenvoudigs. + +FVW + +--- + * Origin: * NGN Point-Service -31-4752-6190 * (2:512/250) } + +Uses Crt,nwMisc,nwBindry,nwConn,nwFile; + +CONST testing=TRUE; + +Var volNbr:Byte; + volumeName:String; + volInfo:TvolUsage; + cont:Boolean; + ConnId,OldConnId:Byte; + ServerName:string; + version:word; + +Begin +If NOT (IsShellLoaded and IsUserLoggedOn) + then begin + writeln('VolStat requires:'); + writeln(' -The shell to be loaded;'); + writeln(' -You to be logged in.'); + halt(1); + end; + +GetNWversion(version); +if version<300 + then begin + writeln('Netware 3.x only.'); + halt(1); + end; + +cont:=(ParamCount>0) and ((pos('c',paramstr(1))>0) or (pos('C',paramStr(1))>0)); + +GetPreferredConnectionId(OldConnId); +REPEAT + clrscr; + For ConnId:=1 to MaxServers + do begin + IF GetFileServerName(connId,ServerName) + then begin + SetPreferredConnectionId(connId); + volNbr:=0; + While volNbr<32 + do begin + + If GetVolumeUsage(volNbr,volInfo) + then with volInfo + do begin + if cont { condensed output } + then begin + writeln('\\'+Servername+'\'+VolumeName); + writeln(' VS: ',totalblocks*(sectorsPerBlock div 2),' ',freeblocks*(sectorsperBlock div 2),' '+ + 'DE: ',totalDirentries,' ',AvailDirEntries); + end + else begin { normal output } + writeln; + writeln('Servername : ',ServerName); + writeln('Volumenumber: ',volNbr); + writeln('Volume name : ',volumeName); + writeln('Blocksize : ',SectorsPerBlock * 512,' bytes.'); + writeln('Total blocks: ',totalblocks,' (=',(totalblocks * (sectorsPerBlock div 2)),' Kb.)'); + writeln('Free blocks : ',freeblocks,' (=',(freeblocks * (sectorsPerBlock div 2)),' Kb.)'); + writeln('Purgable blocks : ',purgableblocks,' (=', + purgableblocks * (sectorsPerBlock div 2),' Kb.)'); + writeln('TotalDirEntries : ',totalDirEntries); + writeln('Available DE : ',availDirEntries); + end; + end; + + inc(volNbr); + end; + end; + end; +if cont + then delay(5000); { 5 second interval } +UNTIL KeyPressed or (not cont); + +SetPreferredConnectionId(OldConnId); +end. \ No newline at end of file diff --git a/NWTP/XIPX/APPN9001.TXT b/NWTP/XIPX/APPN9001.TXT new file mode 100644 index 0000000..3be2307 --- /dev/null +++ b/NWTP/XIPX/APPN9001.TXT @@ -0,0 +1,1644 @@ +(Note: AppNotes September 1990) + +NetWare Communications Processes + + Paul Turner + Consultant + Systems Engineering Division + +Abstract: + +This AppNote provides a comprehensive explanation of the protocols and +algorithms that govern communications in the 286-based NetWare, NetWare +386, and Portable NetWare environments. Topics covered include routing +and connection control. + +Disclaimer + +Novell, Inc. makes no representations or warranties with respect to the +contents or use of these Application Notes (AppNotes) or any of the +third-party products discussed in the AppNotes. Novell reserves the right +to revise these AppNotes and to make changes in their contents at any +time, without obligation to notify any person or entity of such revisions +or changes. These AppNotes do not constitute an endorsement of the third- +party product or products that were tested. The configuration or +configurations tested or described may or may not be the only available +solution. Any test is not a determination of product quality or +correctness, nor does it ensure compliance with any federal, state or +local requirements. Novell does not warranty products except as stated in +applicable Novell product warranties or license agreements. + +Copyright { 1990 by Novell, Inc., Provo, Utah. All rights reserved. + +As a means of promoting NetWare AppNotes, Novell grants you without +charge the right to reproduce, distribute and use copies of the AppNotes +provided you do not receive any payment, commercial benefit or other +consideration for the reproduction or distribution, or change any +copyright notices appearing on or in the document. + +Introduction + +This AppNote is a preliminary excerpt from an upcoming Novell Systems +Engineering Division research report entitled "NetWare Internals and +Structure." It provides a technical description of the protocols that +make client-server communications possible on NetWare networks. The +information contained in this document will be most valuable to those +individuals designing, implementing or administrating large NetWare +internetworks. It will also be useful to individuals and organizations +developing applications specifically for NetWare. + +The document begins with an explanation of the packet structures defined +by each protocol. It then describes the algorithms followed by +workstations, routers and file servers when transmitting or receiving +packets. + +Protocols + +Most computer networks require that information transferred between two +nodes be broken up into blocks, called packets. This packetizing makes +the information more manageable for the sending and receiving nodes, and +any intermediate nodes (bridges or routers). In addition to the +information, or data, that is being transferred, each packet contains +control information used for error checking, addressing, and other +purposes. The protocols being used on the network define the content of +this control information. In most cases multiple protocols exist within a +packet; each protocol defines a different portion of the control +information for the packet and the control information for each protocol +serves a different purpose. When multiple protocols are used, the control +information for the highest level protocol is first placed around the +data, then the control information for each subsequent protocol in the +protocol stack is added to the beginning and/or end of the packet. This +is called envoloping. (See Fig. 1.) + +The enveloping pattern illustrated in Fig. 1 is common in the computer +communications industry but the tasks assigned to each protocol in the +packet differs for different vendor's implementations. In an effort to +standardize the definition of protocols-and therefore make the networking +implementations of different vendors interoperable-several standards +organizations have been formed by governments and corporations. One of +these, the International Standards Organization (ISO), has developed a +model, called the Open Systems Interconnection (OSI) model, that +specifies how protocols should be defined in the future. The OSI model +separates the functions required for effective computer communications +(such as error checking and addressing) into seven catagories, or layers. +These layers are the Application, Presentation, Session, Transport, +Network, Data-Link and Physical layers. + +: Example of Multiple Protocols in a Packet + + Having been defined prior to the finalization of the OSI model, the +protocols used by NetWare do not all correspond exactly to the OSI +model's definitions. NetWare uses a variety of protocols. Some of these +protocols were developed specifically for NetWare; some are used +throughout the networking industry. The protocols required for +communications between NetWare workstations and file servers are the +following: + +o Medium-access Protocols + +o Internetwork Packet Exchange (IPX) + +o Routing Information Protocol (RIP) + +o Service Advertising Protocol (SAP) + +o NetWare Core Protocol (NCP) + +Fig. 2 provides a relative mapping of the NetWare protocols-also called +the NetWare protocol stack-to the OSI model; in actuality, a direct +correlation to the layer boundaries of the two architectures does not +exist. The NetWare protocols follow the enveloping pattern shown in Fig. +1. More specifically, the upper level protocols (NCP, SAP, and RIP) are +enveloped by the IPX and IPX is subsequently enveloped by the medium- +access protocol header and trailer. + +: Mapping of NetWare Protocols to OSI Model + +Medium-Access Protocol Implementations + +A number of medium-access protocols have been defined, many of which are +used with NetWare. The focus within this document is on the +implementations of medium-access protocols, the most common of which are +802.5 Token-Ring, 802.3 Ethernet, Ethernet v2.0, and Arcnet. The 802.x +protocols have been defined by the Institute of Electrical and Electronic +Engineers (IEEE). Ethernet v2.0 was co-developed by Xerox and Digital +Equipment Corporation, and Arcnet was developed by Datapoint, Inc. These +medium-access protocol implementations are primarily concerned with the +transport of packets from one node to another on a single network +segment. + +Medium-access protocols provide bit-level error checking in the form of a +cyclic redundancy check (CRC). This CRC, which is appended to every +packet that is transmitted, assures that 99.9999 percent of the packets +successfully received will be free of corruption. In view of this level +of integrity, NetWare does not provide any additional bit-level error +checking within any of its upper-level protocols. (Note that bit-level +error checking checks to make sure that bits within a packet have not +been corrupted. The packet-level error checking discussed later checks +that complete packets are not lost.) + +Medium-access protocol implementations define the addressing that +distinguishes each node on a NetWare network. This addressing is +implemented within the hardware of each network interface card (NIC). To +move a packet to the proper node on a network, a medium-access control +(MAC) header is placed at the beginning of every packet. This header +contains source and destination node address fields to indicate where the +packet originated and where it is going. Each NIC checks the destination +address in the MAC header of each packet sent on the network segment is +is attached to. If the destination address matches the NIC's own address, +or if the packet is a broadcast packet intended for all nodes, the NIC +will copy the packet. + +Bit-level error checking and node addressing are provided by the majority +of medium-access protocol implementations. IBM's Token-Ring (802.5) +implementation defines a method of routing called source routing. Source +routing allows ring segments to be interconnected by bridges, allowing +administrators to segment network traffic. This requires that each +workstation maintain a table of routes to the nodes it is communicating +with. Furthermore, routing information must be included in the MAC header +of each packet it sends. This information instructs bridges how to +properly forward each packet to its destination. Source routing can be +used instead of or in conjunction with NetWare routing. + +Internetwork Packet Exchange (IPX) + +The IPX protocol was adopted by Novell from Xerox Network System's (XNS) +Internet Datagram Protocol. IPX is a datagram, connectionless protocol +that does not require an acknowledgment for each packet sent. This packet +acknowledgment, or connection control, must be provided by protocols +above IPX. IPX defines internetwork and intranode addressing schemes, +while relying on the network hardware for the definition of node +addressing. + +The network number assigned in NETGEN (NetWare 2.1x) is the basis of +IPX's internetwork addressing. Each network segment on a NetWare +internetwork must be assigned a unique network number. This network +number is used by routers to forward packets to their final destination +segment. + +The IPX intranode address comes in the form of socket numbers. Since +several processes are normally operating within a node, socket numbers +provide a sort of mail slot so that each process can distinguish itself +to IPX. As a process needs to communicate on the network, it requests +that a socket number be assigned to it. Any packets that IPX receives +that are addressed to that socket are passed on to the process. Hence, +socket numbers provide a quick method of routing packets within a node. + +Novell has reserved several socket numbers for specific purposes. These +are shown in Fig. 3. Since socket numbers are internal to each node, +several workstations can use the same socket number at one time without +any fear of confusion. All NCP requests from workstations must be +addressed to socket 451h. + +: Socket Numbers Used in The NetWare Environment + +The network, node, and socket addresses for the both the destination and +the source are held within the packet's IPX header. The IPX header is +placed after the MAC header and before the packet data. (Packet data is +usually the header of a higher-level protocol.) Fig. 4 illustrates the +structure of an IPX packet on an 802.3 network. + +: Structure of an IPX Packet + +Routing Information Protocol + +The Routing Information Protocol (RIP) facilitates the exchange of +routing information on a NetWare internetwork. Like IPX, the RIP was +derived from XNS. However, an extra field was added to the packet +structure to improve the decision criteria for selecting the fastest +route to a destination. This change prohibits the straight integration of +NetWare's RIP with other undeviating XNS implementations. + +The single packet structure defined by the RIP allows the following +exchanges of information: + +o Workstations locate the fastest route to a network number by + broadcasting a route request (represented by "Route Request" entry + on the TRACK ON screen). + +o Routers request routing information from other routers to update + their own internal tables by broadcasting a route request + (represented by "Route Request" entry on the TRACK ON screen). + +o Routers respond to route requests from workstations and other + routers. + +o Routers perform periodic broadcasts to make sure that all other + routers are aware of the internetwork configuration. + +o Routers perform broadcasts whenever they detect a change in the + internetwork configuration. + +The RIP packet structure is shown in Fig. 5. This structure is enveloped +within the data area of IPX. The Operation field indicates whether the +packet is a request or a response. A 1 in this field indicates a request +and a 2 indicates a response. The Operation field can be followed by one +or more (n) sets of information, each consisting of a network number and +the number of Hops and Ticks to that network number. A RIP packet can +contain a maximum of 50 sets of network number information. + +The term "Hops" refers to the number of routers that must be passed +through to reach a network number. A "tick" is roughly 1/18 of a second +(there are 18.21 Ticks in a second, to be precise). The number of Ticks +measures how much time the packet takes to reach a network number. The +number in this field is always at least one. The original XNS definition +of the RIP did not include the Number of Ticks field. The Ticks field was +added by the developers of NetWare so that the NetWare shell could +estimate how long it should wait for a response from a file server. (This +will be explained in the discussion of the shell's receive time-out.) +Also, if multiple routes exist to a network number, a router uses the +route with the shortest number of Ticks when forwarding packets to that +network number. + +If a RIP packet is a request for information, only the Network Number +field applies; the Hops and Ticks fields are essentially nulled out. A +response packet can be either a reply to a request from a router or +workstation or a periodic broadcast by a router. + +: RIP Packet Structure + +Service Advertising Protocol (SAP) + +The Service Advertising Protocol (SAP) allows service-providing nodes- +such as file servers, print servers, and gateway servers-to advertise +their services and addresses. The SAP makes the process of adding and +removing services on an internetwork dynamic. As servers are booted up, +they advertise their services using the SAP; when they are brought down, +they use the SAP to indicate that their services will no longer be +available. + +Through the SAP, clients on the network can determine what services are +available on the network and obtain the internetwork address of the nodes +(servers) where they can access those services. This is an important +function, since a workstation cannot initiate a session with a file +server without first having that server's address. + +A gateway server, for instance, will broadcast a SAP packet every 60 +seconds (the period defined for all servers advertising with the SAP) +onto the network segment it is connected to. The SAP agent in each router +on that segment copies the information contained in the SAP packet into +an internal table called the Server Information table. Because the SAP +agent in each router keeps up-to-date information on available servers, a +client wanting to locate the gateway server can access a nearby router +for the correct internetwork address. + +Like the RIP, the SAP uses IPX and the medium-access protocol for its +transport. Fig. 6 illustrates the SAP packet structure. The first field +defines the operation that the packet is performing. The packet can +perform five different operations: + +o A workstation request for the name and address of the nearest server + of a certain type (this is represented by a "Get Nearest Server" + entry on a TRACK ON screen.) + +o A general request, by a router, for the names and addresses of + either all the servers or all the servers of a certain type on the + internetwork ("Send All Server Info." on TRACK ON.) + +o A response to either a "Get Nearest Server" request ("Give Nearest + Server" entry on TRACK ON) or a general request + +o Periodic broadcasts by servers and routers + +o Changed server information broadcasts + +Following the Operation field are one or more sets of fields. Each set +includes a service type server name, network address, node address, +socket number and a number of Hop fields. If the packet contains +information about more than one server, it will contain more than one set +of fields (n sets of fields). Each SAP packet can contain information +about up to seven servers. + +: SAP Packet Structure + +NetWare Core Protocol + +The NetWare Core Protocol (NCP) makes interaction between clients and +file servers possible by defining two aspects of their interaction, +connection control and service request encoding. Because the creation and +handling of NCP packets is done by the NetWare shell or NetWare Requester +for OS/2, you do not need an in-depth understanding of the NCP,but you +should have some idea of what the protocol does. + +The NCP provides its own session control and packet-level error checking +instead of relying on other protocols for these functions. Consequently, +the modularity of the protocol stack is reduced but, in the long run, a +more efficient mechanism is attained. Fig. 7 shows the general structure +of an NCP packet. When a client establishes a session with a file server, +it is assigned a connection number. This connection number must be +included in all subsequent service requests that the client submits. The +connection number allows the file server to keep track of which clients +are making requests, for response and security purposes. + +: Structure of an NCP Packet + +Each NCP request packet submitted on a given connection must be assigned +a sequence number by the client. The first request following the +establishment of the connection is assigned the number 1; that number is +incremented by one for each subsequent request. When a file server +finishes processing a request, it places the sequence number for that +request in the response packet. Hence, the client can make sure that it +is receiving the correct responses for the requests that it submits. + +Each of the services available at a NetWare file server has been assigned +a number. When it needs to submit a request to a server, the shell or +requester places the number-as well as any additional information that +might be needed-in the service code field of the NCP packet. Depending on +the service being requested, the NCP might provide additional fields for +the shell to give specific instructions to the file server-such as which +part of a file to read. The file server might report any problems or +errors that might have occurred while processing the request in these +additional fields. + +Packet Delivery + +On a NetWare network, the successful delivery of a packet depends on the +proper addressing of the packet and the internetwork configuration +(whether it is a single segment network or series of segments +interconnected by repeaters, bridges and/or routers). The addressing of +the packet is handled in its medium-access protocol and IPX address +fields. To send a packet to another node, the sending node must know the +full internetwork address (network, node, and socket) of the node it +desires to send to (the destination node). (The process of obtaining +another node's address is explained in the section entitled "Establishing +a Connection.") Once the sending node has the destination node's address +it can proceed with addressing the packet. The way the MAC header of that +packet is addressed, however, depends on whether the sending and +destination nodes are separated by a router. + +In the event that the sending and destination node are on the same +network segment-that is, they both have the same IPX network address-the +sending node addresses the packet in the following way: The node address +of the destination node is placed in the MAC header destination address +field. The node address of the sending node is placed in the MAC header +source address field. The full internetwork address of the destination +node is placed in the IPX header destination address fields. The full +internetwork address of the sending node is placed in the IPX header +source address fields. + +Fig. 8 shows an example of two nodes that are connected to network number +AA. The sending node (node 01) sending a packet to node 02. The sending +node places node address 02 in the destination field and node address 01 +in the source field of the MAC. In the destination address fields of the +IPX header, the sending node places AA, 02 and 451 (the full internetwork +address of the receiving node). The sending node places its own +internetwork address of AA, 01 and 4003 in the source address fields of +the IPX header. + +: Transmission to Same Network Segment (No Routing Required) + +Network Interconnection Devices + +The addressing method depicted in Fig. 8 is used when the two nodes +reside on the same physical segment (or ring) or if they reside on +separate segments interconnected by repeaters or bridges. A repeater is a +Physical Layer (OSI model) device that amplifies the signal of one +segment onto one or more other segments. Repeaters are used to extend the +maximum possible distance between end nodes on a segment. They are +completely transparent to the sending and receiving nodes. + +A bridge is a Data-Link Layer device used to interconnect cable segments +locally or over wide area network links. Instead of simply amplifying a +signal as repeaters do, bridges retransmit packets received on one +segment onto another segment. Bridges are considered Data-Link Layer +devices because they examine the data-link (or MAC header) portion of +packets before retransmitting them onto other segments. There are two +predominant types of bridges, transparent bridges and source routing +bridges. + +Transparent bridges interconnect two or more segments. They examine the +MAC header source and destination fields of every packet transmitted on +their connected segments. From the source address fields of packets, +these bridges develop a table of the nodes that reside on (or are +accessible through) each of their connected segments. With this table of +information, a bridge can determine whether packets should be passed on +to other segments. + +Fig. 9 shows a transparent bridge connected to two separate segments. +After examining the packets transmitted on both segments it creates a +table that tracks which nodes exist on each segment. With this table, the +bridge can filter unnecessary traffic. For instance, if node 1 sends a +packet to node 5, the bridge will not retransmit that packet on its port +B. It will, however, retransmit packets sent from node 1 to node 7. Like +repeaters, transparent bridges-as their name implies-are transparent to +the sending and receiving nodes. + +: Example Transparent Bridge + +Routers + +Routers, like bridges, interconnect different network segments; however, +the operation of routers and bridges is quite different. Routers by +definition are network layer devices. (See Fig. 10.) In other words, +routers receive their instructions for forwarding a packet from one +segment to another from a network layer protocol. The network layer +protocol that routers use in the NetWare environment is IPX. NetWare- +compatible routers are available with NetWare or from third-party +manufacturers. The routers that come packaged with NetWare have actually +been misnamed bridges in the past. The NetWare routers include what has +been called the internal bridge within NetWare file servers and external +bridge installed at workstations. Novell has officially renamed these two +devices internal router and external router. + +NetWare-compatible routers may be configured to interconnect two or more +segments. Each of these segments, however, must be assigned a unique IPX +network number to distinguish it from other segments on the internetwork. +A segment's network number must be configured into each of the routers +connected to that segment. The network number serves as a common address +for each node connected to a segment. + +: OSI Representations of Network Devices + +Packet Routing + +When a node wants to send information to another node, it must first have +network address-as well as the node address-of the destination node. If +the two nodes have the same network number (reside on the same segment), +the sending node can simply send packets to the destination node using +method illustrated in Fig. 8. On the other hand, if the two nodes have +different network numbers (reside on different network segments), the +sending node must find a router on its own segment that can forward +packets to the destination node's network segment. To find this router, +the workstation broadcasts a RIP packet requesting the fastest route to +the destination node's network number (RIP requests are discussed in more +detail later in the section entitled "Establishing a Connection". This +RIP request is responded to by the router residing on the sending nodes +segment with the shortest path to the desired segment; in the response, +the router includes its node address. + +Once the sending node receives the router's node address, it is prepared +to send packets to the destination node. The sending node addresses these +packets in the following way: It places the destination node's +internetwork address (network, node and socket number) in the destination +address fields of the IPX header. Next the sending node places its own +internetwork address in the source address fields of the IPX header. Then +the sending node places the node address of the router-the one that +responded to the RIP request-in the destination address field of the MAC +header. The sending node places its own node address in the source +address field of the MAC header. (See Fig. 11.) + +: Packet Addressing Through a Router + + When a router receives a packet to be routed, it can take one of two +possible actions. If the packet is destined for a network number that the +router is directly connected to, the router will place the destination +node address from the IPX header in the destination address field of the +MAC header, place its own node address in the source address field of the +MAC header, and transmit the packet. (See Fig. 11.) + +If the router is not directly connected to the segment that the final +destination node resides on, however, it will send the packet to the next +router in the path to the destination node. To forward the packet to +another router, the router will place the node address of that other +router in the destination address field of the MAC header. The router +will place its own node address in the source address field of the MAC +header. The router leaves the IPX header as initially set by the sending +node and sends the packet. + +Routing Information Administration + +To forward packets by the best possible route, NetWare routers maintain a +Routing Information table that holds information about all the network +segments on the internetwork. Fig. 12 gives an example of a Routing +Information table (only the fields pertinent to this discussion have +been included). Each entry in the Routing Information table gives the +router forwarding information for a network segment. + +: Portion of Routing Information Table + +The first field contains the network numbers for segments that the router +is currently aware exist. The router simply matches the destination +network number in the packet's IPX header with an entry in this field to +get its forwarding instructions for the packet. The second field +indicates the number of routers that must be traversed to reach the +network segment. + +An estimate of the time necessary to reach the destination segment is +recorded in the third field. The initial time estimate for a segment is +the responsibility of the driver directly connected to it. This driver +reports this estimate to its router. This time estimate is used by the +router in its periodic broadcasts to indicate the time necessary to +deliver a packet to a node on that segment. The method that drivers use +for estimating the time delay on a segment depends on the segment type. +For local segments with more than 1 Mb/sec of bandwidth (Token-Ring, +Ethernet, Arcnet, and so on), the driver makes the assumption that +delivery time is one tick. For remote segments (T1, 64 kbps, X.25, and +asynchronous), the driver will periodically poll to determine the current +time delay. For instance, the delay for a T1 link normally ranges from +six to seven Ticks. If this delay changes, the driver will inform its +router. As information about the segment is passed along throughout the +network (by way of periodic broadcasts), routers will add any additional +delay that they impose to the initial time estimate for the segment. + +The NIC field of the Routing Information table records which NIC in the +router the network segment can be reached through. The Immediate Address +field contains the node address of the router that can forward packets to +each segment. If the segment is directly connected to the router, this +field will remain empty. The "Net Status" field indicates whether the +segment is directly connected to the router and whether the segment is +considered reliable. The final field is used to make sure that +information about the segment is current. + +For NetWare versions prior to 2.15c, the Routing Information table keeps +a list of all alternate routes for each network number in case the +primary shortest route to a network number goes down. In other words, if +the router can reach the segment through more than one of its NICs, it +will make a record of both routes. The fastest route, the one that +requires the least number of Ticks, will always be used as the primary +route. NetWare versions after 2.15c maintain alternate routes only if +these alternate routes require the same amount of Ticks to reach the +segment as the primary route. This reduces the size of the Routing +Information table. + +Routing Information Broadcasts + +On an internetwork, routers are constantly exchanging information with +each other to make sure that their Routing Information tables reflect up- +to-the-minute changes in the layout of the internetwork. To accomplish +this, routers transmit a series of broadcasts from the time they come up +until they are brought down. These broadcasts can be separated by the +time at which they occur: + +o Initial broadcast of directly connected network segments + +o Initial request to receive routing information from other routers + +o Periodic broadcasts (every 60 seconds) of current list of active + network numbers + +o Broadcast of change in internetwork configuration + +o Final broadcast when brought down + +Although the broadcasts occur at different times and, for the most part, +contain different information, they must follow two important rules. +First, each broadcast must be a local broadcast, addressed so that it +will not be immediately passed on, intact, by the routers that receive +it. This reduces the network traffic created by these information +exchanges. Second, routers must follow a "best information" algorithm +when providing information to other routers through a broadcast (since +the second broadcast listed above is a request for information, this rule +does not apply to it). + +Best Information Algorithm + +The purpose of routing information broadcasts is twofold: to allow a +router to share its current impression of the layout of internetwork with +other routers, and to inform the routers of an internetwork change so the +routers can update their tables. A router sends routing information +broadcasts to every network segment that it is directly connected to. The +first rule of the best information algorithm dictates that a router about +to broadcast to a particular network segment should not include any +information about other segments that it has received from the segment to +which the information is being sent. + +For example, if the router within server FS2 in Fig. 13 is going to +broadcast a routing information broadcast to network segment BB, it will +not include information that it received from FS1 about network segment +AA. If it did, someone on segment BB might erroneously conclude that +there are two paths to segment AA-one through FS1 and another through +FS2. + +: The Best Information Algorithm + +The best information algorithm also states that routers should not +include information about the network segment that they are sending +routing information broadcasts to. For example, FS2 would not include +information about BB in its broadcast onto BB. + +Taking these rules into account, the information that FS2 would broadcast +onto segment BB would be information about segments CC and DD. + +Initial and Periodic Broadcasts + +When a router is first brought up, it places the network numbers of its +directly connected segments into its Routing Information table. Then, +following the best information algorithm, the router sends a routing +information broadcast to inform the routers on its directly connected +segments of the segments that the router will be making available. The +router next broadcasts a request to each of its directly connected +segments for information about all other network segments that exist on +the internetwork. This request is responded to by all the routers (each +using the best information algorithm) on these directly connected +segments. The router places the information gleaned from these responses +in its Routing Information table. Fig. 14 illustrates this initial +sequence of broadcasts. + +: Sequence Used to Build and Maintain Routing Information Table + +Once the router has performed these initial broadcasts and updated its +Routing Information table, it is ready to accept routing requests and +route packets. In addition to routing packets, the routers will broadcast +all the information in their Routing Information table (except that +excluded by the best information algorithm) to each of their connected +network segments every 60 seconds. Routers perform these periodic +broadcasts to make sure that all routers on the internetwork remain +synchronized. + +Because of lower bandwidth of X.25 and asynchronous links, routers do not +perform 60 second broadcasts on these links-only initial broadcasts, +changed information broadcasts and final broadcasts are sent over these +links. + +Changed Information Broadcasts + +When a router receives information that causes it to change its Routing +Information table, the router will immediately pass that information on +to its other directly connected network segments except the segment that +the router received the information from. If a new network segment comes +up or an existing segment goes down, all the routers on the internetwork +will learn about the change in a short amount of time. + +The primary cause of a change in the internetwork's configuration are +file servers and external routers coming up or going down. If a router +needs to be brought down (using the DOWN command at the console) the +router will inform its directly connected segments of the fact before +discontinuing service. The router issues broadcasts (as always, using the +best information algorithm) that indicate that the network segments which +the router had made available will no longer be accessible through this +router. (See Fig. 15.) + +: Routers Inform Other Routers When Going Down + +The Process of Aging + +If a router goes down due to a hardware failure, power glitch, or power +outage, other routers will not be notified that a change has occurred. To +safeguard against this eventuality an "aging" mechanism has been built in +to NetWare routers. + +Routers maintain a timer for each entry in their Routing Information +table. Every time that information is received concerning the entry, the +timer is reset to zero. If the timer reaches three minutes, the router +assumes that the route to the network number is down and broadcasts that +fact to its other segments. Since this information is new or changed, the +routers that receive the information will pass it on immediately and the +change will quickly permeate the internetwork. + +Service Advertising + +Using the SAP, servers on a NetWare network can advertise their services +and addresses. The information that these servers broadcast is not +directly used by clients but instead collected by a SAP agent within each +NetWare router on the server's segment. The SAP agents store this +information in a Server Information table and, if they reside within a +server, in their server's bindery. The clients can then contact the +nearest SAP agent or file server for server information. + +The SAP broadcasts that servers perform are local broadcasts and, +therefore, only received by SAP agents on their connected segments. +Consequently, SAP agents periodically broadcast their server information +so that all SAP agents on the internetwork have information about all +servers that are active on the internetwork-this is the same broadcast +method used by routers to distribute and exchange network number (RIP) +information. + +Server Information Table + +The table that SAP agents use to store information received in SAP +broadcasts is called the Server Information table. If all SAP agents on +the internetwork are exchanging SAP information properly, each agent's +Server Information table should have information about all the servers on +the internetwork-thus providing clients with nearby access to the +addresses of all the servers on the internetwork. Fig. 16 illustrates +some of the more pertinent fields of the Server Information table. + +: Portion of a NetWare Router's Server Information Table + +The Server Address field contains the service's full address, including +network, node, and socket addresses. The Server Type field holds a number +designating what type of service the server provides. One server might +provide printing services as opposed to file services, for instance. The +server type designation used to assign numbers to the different services +that servers provide is part of a more generic scheme used in the bindery +to classify objects. Some of the more common object types are shown in +Fig. 17. + +: Object Types + +The Time Since Changed field is used for aging servers that have +unexpectedly gone down. The NIC that the information about the server was +received on is specified in the NIC Number field. + +The way that information within the Server Information table is stored +makes sequential access (send me information about all servers with +server type 4, for instance) possible but makes database access (send me +information about server NCS) very difficult. Therefore, the Server Name, +Server Address, Server Type and Hops to Server fields of the Server +Information table are periodically copied to file server's binderies by +internal SAP agents-SAP agents that reside within file servers. With this +information stored in file server binderies, any client that has a +connection with a NetWare file server can query the bindery for the +address of a specific server. + +Server Information Administration + +When a file server is first brought up, its internal SAP agent places the +name of the server in the agent's Server Information table. The SAP agent +then sends a SAP broadcasts onto each of its directly connected segments +to inform the SAP agents on those segments that a new server has become +available. (See Fig. 18.) + +: Sequence Used to Build and Maintain Server Information Table + +After performing its initial broadcasts, the SAP agent broadcasts a +request onto each of its directly connected segments for information +about other servers that exist on the internetwork. These requests are +responded to by all the SAP agents on these directly connected segments. +The SAP agent places the information received in these responses in its +Server Information table. Thereafter, the SAP agent performs broadcasts +about the servers that it is aware of every 60 seconds (except on +asynchronous and X.25 links). illustrates these initial and periodic +broadcasts. + +As with routing information broadcasts, all server information broadcasts +are local broadcasts and are subject to the best information algorithm. +Any changes in server information are passed on immediately to ensure +current information across the internetwork. The router applies the aging +process to its Server Information table entries in case any servers +become unavailable. Finally, if the router is brought down, it will +indicate to its directly connected segments that the servers the router +has been advertising will no longer be available. (See Fig. 19.) + +: FS2 Brought Down + +File Server Addressing + +Value-added servers, such as gateway and print servers, normally contain +only one network adapter and will use the address of that adapter as the +address they advertise in their periodic SAP broadcasts. NetWare file +servers, on the other hand, may contain multiple adapters. This requires +that they use some sort of convention for advertising the address of +their file services; the convention used for this addressing differs for +286- and 386-based servers. Within the 286-based environment, the file +services of a server are addressed with respect to its NIC A. This +convention guarantees consistency since every server will have at least +one network adapter installed. (See Fig. 20.) If you enter an SLIST +command, the address you see for 286-based servers is the network and +node address assigned to the server's NIC A. + +: Addressing of File Services on a 286-based NetWare File Server + +In the NetWare 386-based servers, an internal network has been added for +the addressing of internal services, as shown in Fig. 21. This different +method of addressing requires that an internal network number be assigned +when a NetWare 386-based file server is brought up. + +: Addressing of File Services in NetWare 386-based Server + +Fig. 22 displays an SLIST screen that contains 286- and 386-based +servers. The 386-based servers can be distinguished by their node address +of one. This node address is assigned to the file services on the +internal network number. The implementation of redundant cabling systems +with 286-based servers is discussed in a later section. + +: Example SLIST Listing + +Client-Server Interaction + +The NetWare shell facilitates client-server communications for DOS-based +workstations. In a typical client-server interaction, one station (the +client) requests services from another station (the server). Through the +shell, DOS-based applications can request file services (such as writing +to and reading from files) from NetWare file servers. At the workstation, +the shell, the user application, and the user together act as the client +requesting file services; the NetWare file server acts as the server +providing file services. + +The shell, then, is the liaison between the client (the user application) +and the server. The shell performs the tasks necessary to request file +services from a NetWare file server: for example, establishing a +connection with the file server, maintaining the connection, and +terminating the connection. + +The NetWare shell is a terminate-and-stay-resident (TSR) program called +NETx.COM (where x depends on the version of DOS being run). NETx.COM is +loaded into a NetWare workstation's memory when the workstation is +booted. Before you load the shell, however, you must load another TSR +called IPX.COM + +IPX.COM + +Novell's IPX protocol serves as the communications link with the NIC +installed in the workstation. At installation, a customized version of +IPX.COM is generated for each workstation by linking in a driver written +specifically for the NIC that resides in that workstation. Once IPX.COM +is loaded, any workstation programs, including the shell, can communicate +on the network through NetWare's IPX protocol. + +In addition to interfacing with the NIC, IPX.COM performs several +communication-related functions. For example, it manages the IPX sockets +used with the workstation. The shell and other applications access +IPX.COM to open and close IPX sockets. When the workstation receives an +IPX packet, IPX.COM checks which socket the packet is addressed to and +passes the packet to the program having that socket open. + +Finally, IPX.COM is responsible for determining the address of the +network segment to which the workstation is physically connected. The +workstation's network number, along with its node address, make up the +workstation's full internetwork address. + +IPX can determine the workstation's network number in one of two ways. In +the first method, IPX.COM watches for any RIP broadcasts sent on the +network. Since RIP packets are not forwarded to other network segments, +IPX knows that this type of broadcast originated on the segment to which +the workstation is directly connected. IPX simply reads the source +network address contained in the IPX header of a RIP broadcast to +determine the workstation's network number. + +IPX uses an alternate method if the shell requests a route to a network +number before IPX can determine the workstation's network number from a +RIP broadcast. In this case, IPX broadcasts a Get Local Target request, +which requests the fastest route to the destination network number +requested by the shell. Upon receiving a response, IPX.COM checks the +source network number in the IPX header of the response packet. This +source network number (the network number of the router that responded to +the Get Local Target request) is the workstation's network number. + +The NetWare Shell + +The shell (NETx.COM) acts as the interface between user applications and +NetWare file servers. As user applications make requests, the shell +determines whether the requests should be handled locally by DOS or sent +to a server on the network. If the shell determines that the request +should be sent to a network server, the shell formulates a request +packet, hands it to IPX.COM for transmission, and waits for a response. + +Prior to submitting any requests to a server, the shell must establish a +connection with that server. The shell can establish a connection to a +server in two ways: When the shell is first loaded at the workstation, it +logically attaches to the first server that responds (usually the server +nearest to the workstation). The LOGIN and ATTACH command line utilities, +when executed, also establish a server connection. + +To establish a connection, the workstation and the server must exchange +several packets: a packet requesting that a connection number be assigned +to the shell, and another proposing the maximum packet size that will be +allowed in the interaction between the file server and the shell. Before +sending these initial packets, the shell needs the address of the server +and a route to the server. + +Getting a Server's Address + +To get a server's address, the shell can use the SAP to broadcast a +request for the address of the nearest server-a Get Nearest Server +request. All routers on the workstation's network segment having +information about the nearest server respond to the Get Nearest Server +request. Each response contains the nearest server's name, its full +internetwork address, and the number of Hops required to reach the +server. (See Fig. 23.) + +: Getting the Address of the Nearest File Server + + When first loaded at a workstation, the shell issues a Get Nearest +Server request to establish an initial connection to a file server. If +the shell loses its connections with all file servers, it resorts to the +Get Nearest Server request method to re-establish a server connection. + +A second method the shell uses to get a server's address is to use the +NCP to access a file server's bindery. The bindery is a database within +NetWare file servers that contains information about many network +entities, including users, groups, and servers. + +Because the shell must already have a server connection before it can +access the server's bindery, the shell can use this second method only +after it has established an initial connection to a file server. The +LOGIN and ATTACH utilities use this method, as does the new "preferred +server" shell (version 3.01). These utilities allow the user to specify a +specific file server name, and then these utilities use that name to scan +the bindery for the server's address. + +Getting a Route to a Server + +Once the shell has the address of a server, it needs a route to that +address. The shell uses this route for all subsequent communications with +the server for the duration of the connection. + +To obtain a route, the shell submits a Get Local Target request to +IPX.COM. IPX first compares the network number of the desired server to +the workstation's network number. If these two numbers are the same, IPX +tells the shell to send requests directly to the server (without going +through an intermediate router). + +If the network number the shell submits and the workstation's network +number are not the same, IPX broadcasts a RIP request for the fastest +route to the submitted network number. Whichever router on the +workstation's network segment has the shortest route to the network will +respond to the request. More than one router might respond if several +routers have a route equal to the shortest route. IPX accepts only the +first router's response, discarding all others. + +IPX then returns to the shell the node address of the first router that +responded. The shell places the node address of this router in the MAC +header of a Create Connection request packet; it addresses the IPX header +of the request packet to the file server it wants to connect to. With the +packet addressed in this fashion, the router will receive the packet +first, check the IPX destination address, and forward the packet toward +the network number on which the file server resides. (See Fig. 24.) + +: Requesting the Fastest Route to an Address + +Establishing an Initial Connection + +To establish its connection to a file server, the shell uses a +combination of the SAP, the RIP, and the NCP. The sequence followed is +slightly different for the new "preferred server" shell (version 3.01) +than it is for previous shell versions. + +Fig. 25 shows the steps taken by pre-v3.01 shells to make a connection +with a file server. The first column represents the call or packet sent. +The second column lists the source, or sender, of the packet. The third +column lists the addressee of the packet. The final column indicates the +protocol used for the packet. + +: Initial Connection Sequence for NetWare Shells + +We have already seen how the first four steps work. In steps 1 and 2, the +shell obtains the address of the nearest server. Step 3 is IPX.COM's +request for the fastest route to the address that the shell received in +step 2. Step 4 is the response by all routers with the shortest route to +that segment. + +Steps 5 through 8 show the packets exchanged between the shell and the +server to establish the initial connection. Once this connection is made, +the shell moves to the background (terminates-and-stays-resident) and +returns the DOS prompt to the user. The user can then execute LOGIN.EXE +to log in to the connected server or to another server. + +The Preferred Server Shell + +The preferred server shell (v3.01 and above) features several additional +functions not offered by older versions of the shell. As its name +implies, the preferred server shell allows users to specify, either at +the command line or in the SHELL.CFG file, which server they would like +to connect with. Whether or not a preferred server is specified, the +preferred server shell goes through the same initial eight steps as the +old shells. + +If the server the shell connects to during the initial eight steps is not +the preferred server the user specified, the preferred server shell +performs several additional steps to establish a connection with the +specified server. (See Fig. 26.) + +For instance, if the workstation in Fig. 24 initially connects to FS1 and +the user specified FS3 as the preferred server, the shell will follow a +sequence similar to that shown in Fig. 25. As you can see in step 9, the +preferred server shell uses the bindery method of acquiring the server's +address. + +: Connection Sequence for the Preferred Server Shell + +Steps 11 and 12 of this preferred server sequence are not always +required. If the preferred server resides on the same network segment as +the workstation, the shell skips these two steps and sends the Create +Connection request directly to the server. The shell destroys the +connection with the initial server once it has successfully established +the connection with the preferred server. + +Another major difference between old shells and the preferred server +shell involves the receipt of Give Nearest Server responses. Older shells +accept the first Give Nearest Server response they receive and ignore all +subsequent responses. Preferred server shells accept the first response +also, but save the next four Give Nearest Server responses in case a +connection cannot be made to the first server. + +Servers respond to Get Nearest Server requests even if they have no free +connections. Consequently, older shells fail to establish a connection +(steps 5 and 6 of Fig. 25) if the first Give Nearest Server response they +receive is from a server with no free connections. The preferred server +shells, on the other hand, can refer to the next saved Give Nearest +Server response if the current attempt to establish a connection fails. + +LOGIN.EXE + +Users can run LOGIN.EXE at any time after they have established a +connection to a NetWare file server. LOGIN submits the user's name and +password to the file server for verification. It also establishes a new +server connection if the user specifies a file server in the LOGIN +command. + +If the server specified at the command line is not the one the shell is +already connected to, LOGIN follows the steps outlined in Fig. 27. Once +these steps are completed, LOGIN verifies the username and password. If +the server specified at the command line is located on the same segment +as the workstation, steps 3 and 4 are not necessary. + +: Additional Steps Performed by LOGIN.EXE + + ATTACH.EXE uses the same sequence as that described for LOGIN.EXE when +establishing connections to a file server. + +Connection Management + +Communication between any two workstations requires a certain amount of +responsibility on both sides to ensure that no information is lost. NICs +maintain error checking at the bit level in the NetWare environment. File +servers and workstation shells handle packet- and session-level error +checking; each maintains a table to handle this level of error checking. +The NCP governs the way that the connection control information is +exchanged. (It is a common misconception that SPX is used for packet +level error checking between workstations and servers; however, SPX is +only used for peer-to-peer interaction.) Every NCP packet submitted to a +NetWare file server by a client must have a connection number and +sequence number attached to it. The connection number is the number that +client was assigned by the file server when the connection was +established. The sequence number identifies each packet so that both the +server and the shell can determine when a packet is lost. + +The Shell's Connection Table + +NETx.COM (the shell) can support up to eight server connections +concurrently. NETx.COM maintains a connection table to track these +connections. (See Fig. 28.) Within each entry in this table, the shell +stores the name and full internetwork address of the server it is +connected to. If the shell is forwarding packets through an intermediate +router to the server, the node address of that router will be stored in +the Router's Node Address field. The shell's connection number and packet +sequence number are also in the table. The sequence number is set to zero +when the connection is first established and incremented with each new +request. + +: Portion of Shell's Connection Table + +The shell's connection table also maintains two time-outs. One time-out +is the maximum time that the shell will wait for a response from the +server before resending a request packet. This time-out is based on an +estimate of the time (in ticks) needed to deliver a packet to the server. +This time estimate is provided by the router in its Give Immediate +Address response. (If the workstation and the server are on the same +segment, this value is set to one tick.) The shell multiplies this +estimate by 16 and adds 10 ticks to the result to set its maximum time- +out for communications with that server. + +The Receive Time-Out is a dynamic time-out that is originally set to four +times the time estimate (received in the Give Local Target response) plus +10 ticks. + +Once initially set, the receive time-out adjusts up or down to adapt to +changing network conditions. The receive time-out is increased if the +shell issues a request to a server and does not receive a response within +its current receive time-out. The receive time-out is multiplied by one +and one-half when the first retry to the server is issued. It remains at +this new value for all subsequent retries on the request and for use on +the next request. If the next request requires a retry, the receive time- +out will be increased again. The receive time-out will continue to be +increased in this fashion until it reaches the maximum time-out. + +The shell decreases the receive time-out each time that the shell does +not have to issue a retry to a request. To decrease the receive time- +out, the shell takes the time necessary to receive a response to the last +request-the request that didn't require a retry-and multiplies that value +by two and adds 10 Ticks to it. The shell sets the new receive time-out +to the average of this calculated value and the current receive time- +out. + +The number of times that the shell will resend a request to a server is +determined by a number called the IPX Retry Count. If this count is +exceeded the shell will give up and present the user with a "Network +error on server xxxxx. Error xxxxx from network. Abort, Retry?" message. +A default for this retry count exists for all drivers. This default +differs from driver to driver but is generally between 20 and 40. The +"IPX RETRY COUNT = xx" option for the SHELL.CFG files allows the default +IPX retry count to be modified; however, some drivers will ignore this +entry in the configuration file and leave the retry count at their +default. + +The File Server Connection Table + +The file server connection table, shown in part in Fig. 29, allows the +server to keep information about each of the clients that it is +servicing. The address fields are used for addressing response packets +and for security purposes. When a packet arrives with a service request, +it contains the connection number assigned to the sender. The server +matches the packet's IPX source address (network, node, socket) with the +address registered for that connection number. If the addresses don't +match, the server regards the request as a security breach. + +: A Portion of the NetWare File Server's Connection Table + +The NIC Number and Intermediate Router's Address fields are used for +sending responses to clients. As a request packet is received, the +number of NIC that the request came in on is placed in the NIC Number +field-this number would be A, B, C, or D for NetWare v2.15c and earlier +versions, or the network number of the NIC for NetWare versions 3.0 and +above. If the packet was forwarded through one or more routers, the node +address of the last router is stored in the Intermediate Router's Address +field. Hence, when the request has been processed, the server does not +have to find a route to the client to send a response. The server places +the node address of the first router in the path to the client-from the +Intermediate Routers Address field-in the MAC header destination address +field and sends the packet through the NIC specified in the NIC number +field. Of course, it first places the client's and its own full network +address in the destination and source address fields of IPX header, +respectively. + +The Sequence Number field is used for packet-level error checking. The +Watchdog Count and Timer fields are used by the watchdog process, which +is discussed later. File servers also maintain a 100-byte reply buffer +for each of their connections. If a response to a client is less than 100 +bytes, the server will make a copy of the response in the buffer that +corresponds to that connection. If the client does not receive the +response and resends the request, the server will not have to reprocess +the request. + +Packet-Level Error Checking + +The bit-level error checking that network adapters provide detects the +corruption of individual bits within a packet. When an adapter finds that +part of a received packet is corrupted, it discards the entire packet. +Due to the driver's simple design, no mechanism exists within the driver +to request that the packet be resent or to inform the upper-layer +processes and applications that an error occurred. Therefore, the upper- +level sending process (shell or file server) must determine when a packet +has not reached its intended destination. + +In the NetWare environment, this packet-level error checking is the +responsibility of the shell. The NCP specifies that a workstation shell +can submit only one request to a server at a time. Furthermore, the +response that the server provides must fit in a single packet-the shell +should never request more than a packet's worth of information. Thus, to +guarantee that no packets have been lost, the shell only has to make sure +that it receives a completed response to each of its requests. + +Each request that a shell sends to a server has a sequence number +attached to it within the NCP header. The response that the server +returns is labeled with the same sequence number. Ultimately, the shell +is responsible for getting completed responses for each of the service +requests that it submits. If the shell does not receive a response to its +most recent request within the specified receive time-out, it will +resubmit the request. The shell continues to resubmit the request until +it receives a response or exceeds its IPX Retry count. + +Three conditions could cause a shell to time-out while waiting for a +response from a server. Fig. 30 illustrates a case in which the request +is lost in transit to the server. The workstation's timer eventually +expires and the shell resends the same request. The server receives the +second request, processes it, and sends back the response. + +: Request Lost in Transit to File Server + +In Fig. 31, the request is received by the server but the response is +lost in transit to the workstation. Once the workstation's timer reaches +its limit, the shell sends a second identical request to the server. + +When a server receives any request, it checks the request's sequence +number to see that it is one greater than the sequence number registered +in the server's connection table. If it is, the server increments that +number and processes the request as usual. However, if the two numbers +are the same, the server determines that the client, for whatever reason, +is resubmitting its last request. In some cases, the server will have a +copy of the last response. NetWare file servers maintain a 100-byte +response buffer for each of their connections. If the server is sending a +response that is less that 100 bytes in size, the server will make a copy +to that client's buffer-that is, the buffer corresponding to that +client's connection number. Since a large percentage of responses are +less that 100 bytes, a good chance exists that a server will have a copy +of the response when requests are resubmitted by clients. (This type of +response increments the Duplicate Replies Sent counter on the FCONSOLE +Statistics->LAN I/O Statistics screen.) On the other hand, if the request +was larger than 100 bytes, the server must reprocess the request and send +the response. (This type of response increments the Reexecuted Requests +counter in FCONSOLE.) + +If the response is still in transit to the workstation when the shell +times out and resubmits the request-that is, the shell receives the +completed response after resending the request-the server will send +another response, but the shell will ignore it. + +: Response Lost in Transit to Shell + +Sometimes a server may be too busy to respond within the shell's time- +out. The shell then resends the request. When the server receives this +second request, it sends a reply to the workstation stating that the +initial request was received successfully, but that the processing of it +has not yet been completed (This intermediate response increments the +Positive Acknowledgments Sent counter within the FCONSOLE Statistics->LAN +I/O Statistics screen.) When the shell receives this reply from the +server, it sets its time-out to zero and waits for the request. If the +shell's time-out expires again, it will send a third copy of the request +just in case the response was sent by the server but lost in transit. +This process will continue until the shell finally receives a completed +response. (See Fig. 32.) + +: File Server is Busy + +Connection-Level Error Checking + +The connection between a workstation and server can be lost due to a +power failure or a communications problem. Both file servers and +workstation shells are equipped to handle this eventuality. On the +workstation side, the connection is checked each time a request is made. +If the shell does not receive a response to a request after it retries a +certain number of times (the number dictated by the IPX Retry Count), the +shell assumes that a problem exists with the connection and displays a +message for the user. At this point, the user has the choice of ordering +the shell to resubmit the request again or to abort the operation +completely. + +If the operation is aborted the shell removes that connection from its +Connection table. If it does not have any other server connections, it +attempts to create a new connection with a server (using the initial +connection sequence outlined in Fig. 25). If this attempt is +unsuccessful, the shell informs the user with the following message: You +are not connected to any file servers. The shell will try to connect to a +file server whenever the current default drive is changed to an invalid +drive. . + +Connection-level error checking at a NetWare file server comes in the +form of address verification and periodic watchdog polling. When a file +server receives a request packet for a certain connection, it verifies +that the IPX source address within the request packet matches the address +recorded for that connection within its connection table. If the +addresses do not match, the server returns a response to the sender of +the request indicating that the connection number and address do not +match. + +The Watchdog Process + +When a workstation is turned off, regardless of whether the LOGOUT +command was issued, the station's connection remains occupied at the +server. To clear these unused connections, the server uses the watchdog +process to poll (send a query packet to) clients that the server hasn't +heard from for a period of five minutes. This five-minute period is +tracked for each connection in the Watchdog Timer field of the server's +Connection table. If the shell within the station that the server is +polling is still operational, it will respond to the query and the server +will reset its timer for that connection. + +However, if the workstation has been turned off or some communications +problem exists on the network, the server will not receive a response +from the shell. In this instance, the watchdog process resets the +connection's Watchdog Timer field to zero, but increments the Watchdog +Counter field by one. The next packet that the watchdog process sends to +the workstation will be sent a minute later. If the watchdog continues to +hear nothing from the workstation, it will send a packet every minute +until it has sent a total of 11 polling packets to that workstation. +Fig. 33 illustrates the timetable for a connection that does not answer +to a server's queries. The server will clear the workstation's connection +if no response to the last watchdog packet is received. (NetWare 386- +based servers provide a setable parameter that allows administrators to +monitor when workstations are logged out by the watchdog process. This +option is set with the following command: SET CONSOLE DISPLAY WATCHDOG +LOGOUTS = ON.) + +: Watchdog Timetable for a Connection that Does Not Respond + +Conclusions + +NetWare's client-server communications are governed by a series of +protocols. These protocols can be broken up by functionality: protocols +used for all communications (the medium access protocols and IPX), +administrative protocols (the RIP and SAP), protocols concerned with +connection control (the NCP and Watchdog) and, finally, the protocol with +defines the coding of service requests (the NCP). This document explains +the operation and interoperation of these protocols; however, it does not +attempt to apply this information to all possible network configurations +and environments. It is up to you to apply this information to your +specific network(s). + +Appendix A: Implementing Redundant Cabling + +In internetworks that contain 286-based NetWare file servers, +incorporating multiple paths to those file servers may result in +inefficient routing. Fig. 34 shows an example of a 286-based NetWare +internet work that contains redundant paths to two file servers. + +: Sample Redundant Path Configuration + +The problem with this sample network configuration involves the route +taken by workstations on segment BB to communicate with file server FS1. +Although the shortest route between the workstations on BB and FS1 is +through NIC B on FS1, there is a good chance that packets may pass +through FS2 onto AA and subsequently through NIC A of FS1. + +Since traversing through an intermediate NetWare router can cause up to +40 percent degradation in the performance of packet exchanges between a +workstation and a file server, the scenario illustrated in Fig. 35 is not +the most desirable. + +: Inefficient Path to FS1 + +Routing problems occur because of the file service addressing scheme used +for 286-based NetWare file servers, combined with the algorithm for +establishing a connection to a file server. + +File Service Addressing + +The file services of a NetWare file server are assigned a specific +address within the file server. With 286-based NetWare servers, file +services are addressed with respect to NIC A in the file server. In other +words, when the file server advertises its existence, it provides only +the network and node address assigned to its NIC A-a socket address is +also included but it is not specific to NIC A. a shows a logical +representation of the file service addressing within a 286-based NetWare +file server. + +: Addressing of File Services in NetWare File Servers + +With NetWare 386, the file services are addressed with respect to an +internal network number assigned when the server is booted up. NetWare +386 assigns the file services node address 1. (See Fig. 36b.) + +The Connection Algorithm + +The problem inherent to the addressing scheme used for 286-based NetWare +file servers arises when LOGIN, ATTACH or the preferred server shell +attempts to connect to a specific server. Fig. 37 illustrates the way +that the file services of both servers appear to the workstations. + +As we have seen, a workstation's Get Local Target request asks for the +fastest route to the network segment on which the file server is located +(segment AA for FS1.) Since the router in FS1 and the router in FS2 both +register the same distance to network segment AA (two Ticks), both will +respond to the Get Local Target request. + +: Logical Positioning of File Services + +If FS2 is the first to respond to the requests, the workstation assumes +that FS2 has the fastest route, and therefore sends the create connection +request packet through FS2. If FS2 is consistently faster than FS1 in +responding to Get Local Target requests, connections to FS1 will always +be established through FS2. + +Fig. 38 shows the entire sequence that the workstation goes through to +connect to FS1 if FS2 responds to a Get Local Target request first. In +this sequence, FS2 is assumed to be consistently faster than FS1 in +responding to Get Local Target requests. + +Since FS2 is always the first to respond, the shell initially connects to +FS2 (using the sequence shown in Fig. 25). After making this initial +connection, the shell returns the DOS prompt to the user. + +: Workstation Sequence For Get Local Target Figure 38 (Continued): +Workstation Sequence For Get Local Target + +The user can then enter the command "LOGIN FS1/" to log in to FS1 +(following the sequence outlined in Fig. 27). First, the shell queries +FS2's bindery for FS1's address. Next the workstation broadcasts a Get +Local Target request. The router for FS1 and the router for FS2 both +answer this request, but FS2's router responds first. Therefore, the +workstation assumes that FS2 must have the fastest route to network +segment AA and sends its connection request-and all subsequent packets +intended for FS1-through FS2. Since FS1 depends on the workstation to +find the fastest route between the, FS1 sends all responses through FS2. + +To avoid this inefficient routing scenario, you can connect workstations +on the same segment as a file server's NIC A when you have redundant +paths to the server. (See Fig. 39.) With the correct configuration, the +shell receives the address of FS1 from FS2's bindery and makes the Get +Local Target call to IPX. IPX determines that FS1 and the workstation are +on the same network segment and tells the shell to address packets +directly to FS1. + +: Correct Configuration of Redundant Paths with 286-based NetWare + +Note that the connection sequence followed for the pre-v3.01 shell and +LOGIN.EXE is the same as that followed by the preferred server shell. +Therefore, the scenario described above applies for the preferred server +shell when a preferred is specified by the user. + +Another Redundant Path Configuration + +Fig. 40 shows another possible configuration that incorporates redundant +paths with 286-based NetWare file servers. In this configuration, +workstations on network BB should have direct access to both FS1 and FS2. +Due to the 286-based NetWare addressing scheme, however, packets destined +for one file server might go through the other file server first. + +For instance, if a workstation on BB wants to log in to FS1 but initially +connects to FS2, it will query FS2's bindery for FS1's address. The +address returned will include network number AA. The workstation will +then issue a Get Local Target request for AA. If FS2 responds to this +request first, the workstation's communications with FS1 will go through +FS2. + +: Redundant Paths With 286-based NetWare File Server + +Unfortunately, there is no all-inclusive solution to the routing problems +possible with this configuration. However, the configuration shown in +Fig. 41 will keep unnecessary routing to a minimum. This configuration +places NIC A for server FS1 and NIC A for server FS2 on different +networks: FS1's NIC A is connected to AA; FS2's NIC A is connected to BB. +Furthermore, workstations that access FS1 the majority of the time are +connected to AA, while those that access FS2 most often are connected to +BB. This configuration guarantees workstations a direct path to the file +server that they access most frequently. + +: Keeping Routing To A Minimum + +Redundant Paths with NetWare 386 + +Thanks to the internal network addressing scheme used by NetWare 386- +based file servers, they avoid the redundant-path problems experienced by +286-based NetWare servers. To illustrate, suppose FS1 is a NetWare 386 +file server with an internal network address of CC. In this case, FS2 +registers two Hops to CC, while FS1 registers only one Hop to CC. + +When the shell obtains the address CC from FS2's bindery, only FS1 +responds to the Get Local Target request. FS2 does not answer the request +because it no longer has a route equal to the fastest route to network +segment CC. + +The algorithms the NetWare shell uses to connect to a file server are +relatively simple in design. The basic procedure is the same: get a +server's address, obtain a route to that address, and send a request to +establish a connection with the server. + +However, when you configure 286-based NetWare file servers in an +internetwork with redundant paths, the shell may inadvertently route +packets through an intermediate router, even though the workstation is +connected to the same network segment as the file server it wants to +communicate with. As a result, you must carefully design redundant-path +networks to avoid such routing inefficiencies. As a general rule, always +connect those workstations that will spend most of the time accessing a +certain server to the NIC A segment of that server. + +Appendix B: Internal Components of a File Server + +It is a common misconception that NIC A enjoys a higher priority within +the 286-based NetWare servers and that it is therefore somewhat faster +than the other NICs. However, NIC A must vie for access to routing and +file services as a peer of NICs B, C, and D. In fact, within 286-based +NetWare servers, the only difference between NIC A and its peers is that +the address of the server is tied to it. + +286-Based NetWare Communication Components + +To fully understand the part that NICs play within 286-based and NetWare +386 servers, it is necessary to look at the communications components +that make up a server. Fig. 42 gives a graphic representation of the +communication-related components of a 286-based server that contains two +NICs. + +: Internal Communication Components of a 286-based NetWare File Server + +Each NIC has a corresponding driver. These drivers can be logically +separated into a send portion that transmits packets through the NIC and +a receive portion that pulls packets off the NIC. The receive portion is +commonly called the driver interrupt service routine (ISR) since it is +executed each time the NIC generates a hardware interrupt. (In most +cases, a hardware interrupt from the NIC indicates that a packet has been +received.) When a packet is received, the ISR checks the length of the +packet to make sure that it is large enough to be a viable IPX packet but +not so large that it will not fit into the server's buffers. If the +packet does not pass this test, the driver simply discards it. If the +packet is viable, the driver attempts to place the packet in a buffer. + +A 286-based file server uses two sets of packet buffers: file service +process (FSP) buffers and communication buffers. The FSP buffers are +primarily used for processing service requests (NCP packets) and can +number between one and 10, depending on the configuration of the server. +These buffers reside within the DGroup memory segment of the server and +are subject to its limitations. (Due to the design of the Intel 80286 +processor, memory must be divided into 64KB segments. The DGroup segment +has been optimized in the NetWare operating system code to be the fastest +segment. It contains several components besides the FSP buffers which, +for larger server configurations, may limit the memory available for +these FSP buffers.) + +All FSP buffers are the same size; they are sized to handle the largest +packet that any of the server's NIC drivers can receive. For instance, +if the server contains an Ethernet driver able to handle 1,024-byte +packets and an Arcnet driver able to handle 512-byte packets, the buffers +will sized to handle 1,024-byte packets. + +The communication buffers act as overflow areas for packets being +received by the server. The number of buffers that exist ranges from 40 +(the default) to 250 for version 2.15c-this number is set within NETGEN +at installation. These communication buffers are also sized to handle the +largest receivable packet size. Both sets of buffers are set up as first +in, first out queues, or linked lists, where the first packets to be +received are placed at the front of the queue and all subsequent packets +placed in line after that. + +Without examining the contents of the received packet, the driver ISR +first attempts to place the packet in an FSP buffer. If the FSP buffers +are full, the ISR will try to place the packet in a communication buffer. +The packet is discarded if both sets of buffers are full. The assumption +is that the packet-level error checking implemented at the transport +layer (handled by the NCP, SPX, and so on.) will cause the sender to send +another packet later when the server is not so busy. Once the ISR has +placed the packet in a buffer or has discarded it, the ISR returns +control of the CPU to the server and waits for another packet to be +received by its NIC. The ISR for each NIC follows this same routine. Each +has equal access to the buffers and places received packets at the end of +the FSP or communication buffer queues. + +A routing process services the FSP and communication buffers. (This +process is technically referred to as the Mux process or polling +process.) The routing process periodically checks the contents of the +FSP and communication buffers. This process is responsible for routing +packets found within these buffers to their proper destination, whether +that be in or outside the server. Generally, five types of packets can be +found in the buffers: + +o Service requests for the file server (NCP packets addressed to the + server) + +o Packets that need to be routed to another network segment + +o RIP packets + +o SAP packets + +o Packets addressed to other processes internal to the file server, + such as a nondedicated DOS process or a value-added process (VAP) + +When the routing process examines a packet in one of its buffers, it +takes one of four actions: + +o If it finds a service request for the server, the routing process + will schedule an FSP to service the request. The routing process + will then go on to the next buffer. + +o If it finds a packet not addressed to the server, the routing + process will check its Routing Information table for the best route + to the destination and send the packet through the appropriate NIC. + In this capacity, the routing process acts as the internal router of + a file server. + +o If it finds a RIP or SAP packet the routing process will update its + Routing or Server Information table, respectively, if necessary. + However, if the packet is a RIP or SAP request (such as a Get + Nearest Server request) the routing process will get the appropriate + information from its tables and send a response. + +o If it finds a packet addressed to another process within the server + (the packet would be identified by the destination socket number in + the IPX header) the routing process will pass the packet on to that + process. + +The routing process first checks the FSP buffers, starting at the top of +the queue. Since file service requests to the server can only be +processed in the FSP buffers, the routing process must try to keep the +FSP buffers as free as possible for these types of packets. Since the NIC +driver ISRs indiscriminately place packets into whichever buffers are +free at the time, the routing process may have to shuffle packets back +and forth between the FSP and communication buffers. Before checking the +contents of the FSP buffers, the routing process checks into see if all +the buffers are full. If so, the routing process assumes that some NCP +requests may have overflowed to the communication buffers. Therefore, any +non-NCP packets that the routing process finds in the FSP buffers are +moved over to the communication buffers to make room in the FSP buffers +for all the NCP requests. If the FSP buffers are not full, the routing +process simply processes all of the packets where they are. + +Having completed the scheduling or movement of packets in the FSP +buffers, the routing process switches its attention to the communication +buffers. The routing process attempts to move any NCP request packets +that it finds over to the FSP buffers. It places these packets in a +separate queue within the communication buffers if the FSP buffers are +full. This queue is later checked by the FSP buffers as they finish +processing their current requests. Any packets that are not NCP requests +are routed or processed within the communication buffers by the routing +process. + +NetWare 386 + +The NetWare 386 servers follow the same communication mode as 286-based +servers, with the following exceptions: + +o NIC drivers may be used re-entrantly to handle one or more NICs, + therefore saving RAM. + +o Only one set of packet buffers exists within a NetWare 386 server + (this difference stems from the 32-bit addressing scheme used by 386 + processors.) + +o FSP buffers are taken from a pool as they are needed and are not + assigned to one specific buffer. The number of FSP buffers may + increase as the load on the server increases. + +o NetWare 386 servers contain an internal network number for server + addressing. + +Fig. 43 illustrates the structure of the NetWare 386 communications +environment. + +: Internal Communication Components of a NetWare 386 File Server + +Appendix C: RIP and SAP Bandwidth Requirements + +On large internetworks with several hundred servers, administrators +become concerned with the load that RIP and SAP broadcasts will place on +their network segments. Of course, these concerns can be appeased for +asynchronous and X.25 links since only changed server and routing +information is sent on these lines. On other segment types the traffic +caused by these broadcasts does not cause a sginificant load. The +requirements and some examples are shown in Fig. 44. As you can see, the +SAP broadcasts for an internetwork containing 250 servers account for +less than one percent of the total bandwidth (10Mb/s) of an Ethernet +segment. + +: Bandwidth Requirements for 60 Second Broadcasts + +Total traffic load of routing and server information broadcasts on any +given segment will be equal to broadcasting information about all the +network segments and servers that exist on the internetwork. For example, +on a T1 link between two NetWare routers, one router will broadcast +information about all of the network segments and servers that it is +making available to the other router (using the best information +algorithm). The other router will broadcast information about all the +segments and servers that it is making available to the first router. The +total of the two equals the total number of servers and segments that +reside on the internetwork. diff --git a/NWTP/XIPX/APPN9008.TXT b/NWTP/XIPX/APPN9008.TXT new file mode 100644 index 0000000..3e32002 --- /dev/null +++ b/NWTP/XIPX/APPN9008.TXT @@ -0,0 +1,288 @@ +(Note: AppNote August 1990) + +A Comparison of NetWare IPX, SPX and NetBIOS + + Bill Bodine + Consultant + Systems Engineering Division + +Abstract: + +One of the first questions always asked during comparisons of NetWare +IPX, SPX and NetBIOS is which of these protocols will transfer data +the fastest, and how much slower the others are. This AppNote details +the results of four benchmarks written to illustrate the relative +speed of each of these communication interfaces. Performance, maximum +packet length, naming capabilities and memory usage are each singled +out as additional factors in the decision to implement systems using +these protocols. Clarification and explanation of SHELL.CFG parameters +are also included. + +Novell, Inc. makes no representations or warranties with respect to +the contents or use of these Application Notes, or any of the third-party +products discussed in the AppNotes. Novell reserves the right to revise +these Application Notes and to make changes in their contents at any +time, without obligation to notify any person or entity of such revisions +or changes. These AppNotes do not constitute an endorsement of the +third-party product or products that were tested. The configuration +or configurations tested or described may or may not be the only available +solution. Any test is not a determination of product quality or +correctness, nor does it ensure compliance with any federal, state or local +requirements. Novell does not warranty products except as stated in +applicable Novell product warranties or license agreements. + +Copyright { 1990 by Novell, Inc., Provo, Utah. All rights reserved. + +As a means of promoting NetWare Application Notes, Novell grants you +without charge the right to reproduce, distribute and use copies of +the AppNotes provided you do not receive any payment, commercial benefit +or other consideration for the reproduction or distribution, or change +any copyright notices appearing on or in the document. + +Introduction + +When Novell began operations in 1982, several proprietary protocols +for transferring data between workstations were used. As time went +on, the decision was made to base Novell's network communications +on a fast and efficient networking standard. Xerox's XNS protocol +was determined to be one of the best available at the time so Novell's +Internetwork Packet Exchange (IPX) protocol was developed to conform +to the XNS standard. NetWare IPX is functionally equivalent to Xerox's +Internet Datagram Protocol (IDP). + +This AppNote discusses the three primary peer-to-peer protocols +that are supported in the NetWare LAN environment-NetWare IPX, SPX +and NetBIOS. Additional protocols supported include the Transport +Layer Interface (TLI), Named Pipes, LU6.2 and others, but are not +covered in this AppNote. + +NetWare IPX + +NetWare IPX is a true datagram protocol. It makes a best-effort +attempt to send packets by using a 12-byte addressing scheme. +The 12-byte address is split into three addresses: the network +address, which is used to address individual workgroups; the node +address, which addresses network nodes within the workgroups; and +the socket address, which can be used to multiplex between functions +within a network node. When sending an NetWare IPX packet from one +node to another, the sending node must know the receiving node's 12-byte +address. + +SPX + +The Sequenced Packet Exchange protocol (SPX) is a connection-oriented +communications protocol that is built upon NetWare IPX. When a call +is made to SPX to send a packet by an application program, SPX will +do some housekeeping-type work on the packet, but will call NetWare +IPX to actually send the packet. SPX guarantees packet delivery, whereas +NetWare IPX only gives a best effort to deliver packets. This added +feature of SPX has obvious advantages, but as we shall see later in +the paper, it also adds overhead to the data transfer cycle and is +slower. + +NetBIOS + +The Network Basic Input/Output System (NetBIOS) functions in either +a connectionless mode or a connection-oriented mode. An application +written to the NetBIOS interface can be designed to use either of +these modes. For instance, if an application functions in a request/reply +mode with a transfer size of only one packet, then the connectionless +mode should be used to take advantage of connectionless response times. +On the other hand, if most of the transfers are one-sided or consist +of large numbers of packets, the transfers should use the connection- +oriented mode in order to ensure packet delivery and integrity of data. +Novell's NetBIOS emulator is built upon NetWare IPX in the same way +that SPX is. + +The NetBIOS emulator is called an emulator because it is implemented +entirely in software, whereas the original NetBIOS introduced by IBM +and Sytek was located in firmware. + +Because NetBIOS was introduced by IBM, it was almost instantly accepted +as an industry standard. Most networking vendors have implemented +the specification given by IBM that allows almost any application +written to the NetBIOS interface to operate in any environment. + +A common problem with the NetBIOS specification, however, is that +it only deals with the upper layer functions of the interface. It +does not specify what communications protocol should be used underneath +it. As a result, almost every networking vendor has written NetBIOS +on top of their own proprietary communications protocol, which cannot +communicate with other vendors' protocols. + +A nice feature that NetBIOS has to offer the networking industry is +its allowance of easy address resolution among locally-connected +workstations. All nodes on a network that use NetBIOS register a unique +name. When a node desires to communicate with another node, all it +needs to know is the node's unique NetBIOS name and NetBIOS will ensure +that the packet arrives at the proper location. + +Performance Results + +One of the first questions regarding the comparison of NetWare IPX, +SPX and NetBIOS is which of these protocols will transfer data the +fastest, and how much slower the others are. As part of this AppNote, +four benchmarks have been written to illustrate the relative speed +of each of these communications interfaces. The scope of the benchmark +is relatively simple-to send 2,000 255-byte packets and to record +the time that it takes for the transfer to complete. All the programs +were written by the same person and were intentionally kept as simple +as possible to make each benchmark represent the speed of the interface +and not efficiencies or lack thereof in the benchmark tests. + +Each of the benchmarks encompassed two programs. One program +was used to send packets and the other was used to receive. The sending +side sent a packet and then incremented a counter. Before the packet +was sent, a call was made by the sender to the system clock. Once +the 2,000th packet had been sent successfully, another call was made +to the system clock. The first value was subtracted from the second +and the result represented the time in clock ticks that it took to +send 2,000 packets on the given communication interface. The receiving +side did nothing but receive packets and count the number that arrived. +No other processing took place within the code. + +The following results were achieved on standard 8MHz 80286-based +machines on a 4MB Token-Ring network. While the test does not +represent any real-world scenario, it does indicate the relative +speed of each interface tested. + + NetWare IPX 366.0 packets per second + + SPX 140.3 packets per second + + Novell NetBIOS datagram 224.8 packets per second + + Novell NetBIOS session 135.9 packets per second + +NetWare IPX is the fastest protocol available from Novell. This is +expected since all others are written in terms of NetWare IPX. SPX +and NetBIOS are slower than NetWare IPX due to the extra overhead +they introduce into the communications process. SPX and the NetBIOS +session level interface run at virtually the same speed. They both +have to maintain the same level of connection overhead for the +guaranteeing of packets and are both written in terms of another +interface. + +Other Decision Criteria + +There are a few primary differences between writing an application +to NetWare IPX or SPX and writing an application to NetBIOS. Two of +these differences deal with the maximum length of packets that can +be sent and the address resolution. + +Maximum Packet Length + +With NetWare IPX and SPX the maximum packet size that can be sent +by an application depends on either of two things. If the packet to +be sent must cross a NetWare bridge, the maximum packet size possible +is 576 bytes. The bridge will drop any packets that exceed this size. + +On the other hand, if the packet will not be crossing any bridges, +the network interface card (NIC) drivers limit the size. While most +drivers allow packets of 1,024 bytes or larger to be transferred, +NetWare documentation recommends that the maximum size transferred +be 576 bytes. This is in case the packet crosses a bridge or the driver +cannot handle larger packets. + +NetBIOS allows an application to send packets up to 64KB in size. +This is possible because the NetBIOS emulator breaks the packet into +smaller packets for the application and sends them out in sizes that +can be handled by NetWare IPX and the NIC drivers. While this feature +is useful, some developers choose to split packets up themselves in +order to optimize the NetWare IPX bandwidth for their application. +This may or may not be a factor in different situations. + +Naming Capabilities + +The second primary difference is the naming capability supplied with +NetBIOS. NetBIOS makes it convenient for an application to determine +the addresses of other nodes on the network. Each workstation identifies +itself with a particular name. Once any other workstation on the LAN +knows that name, data can be sent between the two workstations. + +Novell recognized the need for this easy address resolution when it +developed NetWare IPX, so the Service Advertising Protocol (SAP) was +developed. With SAP, a node advertises, or broadcasts to the entire +network its name and address. This name and address are stored internally +on all NetWare network file servers. When any other node wants to +find an address, it queries a NetWare file server and the necessary +information is returned. There are also other ways of finding an address +without accessing the NetWare file server, but they are not as common. + +Both of these methods have advantages and disadvantages. While it +is probably easier for an application with the naming capability of +NetBIOS to be developed, using the SAP provided by NetWare does not +require much more work. The advantage gained by using the SAP is that +once the address is resolved, the underlying protocol is very fast. +The SAP is designed for a client-server environment, which means +that a client always initiates a dialogue with the server. The client +can always find the server's address through the SAP. Since all packets +on the network contain the 12-byte address of the node they were +sent from, the server will know which address to send responses to. + +Memory Usage + +When an application runs on a network workstation, particularly in +the DOS environment, the amount of memory that is free for the application +to use is often a primary concern. In NetWare the first software to +load on the network is a terminate-and-stay-resident (TSR) +program called IPX.COM. This program contains all the interfaces needed +to run NetWare IPX programs and SPX, and uses about 14KB of workstation +memory. This is the only piece of NetWare software that needs to be +run in the workstation if no communications are needed with any file +servers. If a file server is needed, the TSR NET3.COM is loaded after +IPX.COM. This program contains all the functions needed by the workstation +to communicate with any NetWare file server on the LAN. It uses about +38KB of workstation memory. NetBIOS is an optional TSR like NET3. +IPX.COM must be loaded first. When NetBIOS is loaded it takes up about +30KB of workstation memory. Just as NET3 is only used when communications +are sent to a NetWare file server, NetBIOS is only run if an application +needs to use the NetBIOS services. Native NetWare does not use NetBIOS +for any of its communications services. + +Appendix A lists parameters that have been modified in the recent +versions of NetBIOS. Because of the differences among versions, they +will be discussed as they relate to the specific versions. + +The values listed in Appendix A are approximates. It is not possible +to state exactly how much memory any of the three protocols will use +up because they all contain custom parameters that change their sizes +and configurations. The parameters that alter these configurations +are located in a file named SHELL.CFG. As IPX.COM or NetBIOS.EXE is +loaded, it looks for this file in the local directory or a search +directory. Once it locates SHELL.CFG it searches within the file to +determine if any of its default parameters have been altered. These +parameters can be configured from within the SHELL.CFG file. Appendix +B of the NetWare Supervisor Reference manual also explains +the parameters. + +Conclusion + +While the primary advantage of writing to NetWare IPX is speed, the +main advantage of writing to NetBIOS is that the application will +work on other environments in addition to NetWare. This should obviously +be considered for applications that are marketed on a variety of platforms. + +Even though different vendors' NetBIOSs can rarely communicate with +each other, most applications do port well over these vendors' +implementations. + +There are a variety of reasons applications are developed to +one protocol or another. One reason a protocol is chosen is because +it is perceived as the defacto standard. For many developers, NetBIOS +is seen as a standard. Applications are developed to that platform +for reasons of portability to a variety of environments. On the other +hand, many developers are developing to NetWare IPX because they recognize +NetWare's large market share and want to reach the greatest market +possible with the most efficient protocol available. + +Sometimes, one protocol may be perceived as easier to develop to than +another. Of course whether one is actually easier than another depends +entirely on the resources that are available, such as the Novell C +Interface libraries for NetWare IPX and SPX, the experience of the +development team or even available documentation and training. + +Appendix A: SHELL.CFG Parameters + +******* Note: The appendix (mostly outdated info) has been removed ******** + diff --git a/NWTP/XIPX/BLTS9401.TXT b/NWTP/XIPX/BLTS9401.TXT new file mode 100644 index 0000000..ba1181d --- /dev/null +++ b/NWTP/XIPX/BLTS9401.TXT @@ -0,0 +1,758 @@ +ARTICLES: IPX/SPX for NetBIOS Developers + +Original article: (c) Copyright Novell, 1994 + Novell Professional Developer BULLETS + January 1994 (Volume 6, Number 1) +NwTP additions : (between angular brackets/ minus signs [- ... -]) + + NetBIOS is a popular peer-to-peer communication method that it is + supported under NetWare through a NetBIOS emulator. However, even though + NetBIOS is supported, there are definite advantages to using Novell's + "native tongue" protocols, IPX (Internet Packet eXchange) and SPX + (Sequenced Packet eXchange), when doing peer-to-peer communication. + + This article discusses the advantages of using IPX/SPX and provides an + introduction to Novell's IPX and SPX protocols for developers who have a + working familiarity with NetBIOS. + +Why Use IPX/SPX? + + The most obvious reason to use IPX and SPX is to improve performance; + since NetWare emulates NetBIOS, processing NetBIOS commands involves more + overhead than processing IPX/SPX commands. NetWare encapsulates emulated + NetBIOS packets within IPX packets before they go out on the wire, so + moving to IPX/SPX allows you to "cut out the middleman." + + You lose no connectivity by switching protocols either, since the + emulated NetBIOS layer cannot communicate with hardware NetBIOS systems. + In fact, moving to IPX/SPX gives you a net gain in connectivity; NetWare + has a 70% share of the network operating system market. + + Also, since the NetBIOS emulator adds an additional layer of complexity + to packets being sent out, it is more difficult to troubleshoot problems. + Emulating NetBIOS involves an extra driver and an extra set of potential + incompatibilities. Generally speaking, since IPX and SPX are not + dramatically different from NetBIOS, it makes your job easier to work + with the protocols that NetWare is designed to support. + +Datagram Services + + Novell's IPX protocol provides almost the same functionality as NetBIOS + datagrams. Both specifications deliver packets on a best-effort basis, + but with no guarantee of delivery or sequencing. Both IPX and NetBIOS + also provide the capability to send packets either to a single node or to + multiple nodes. NetBIOS supports the multicast, or the sending of a + datagram to a selected group of nodes with the same group name. Since IPX + is address-based instead of name-based, this capability is not directly + supported; instead IPX must send an individual packet to each node. + + NetBIOS also supports the broadcast datagram, a datagram that is + broadcast to the entire internetwork. IPX supports broadcasts, but only + to one subnet at a time. Usually, this restriction poses no problem, + since mechanisms such as the NetWare Service Advertising Protocol (SAP) + overcome this limitation. + + The data portion of a NetBIOS datagram is limited in length to 512 bytes, + whereas IPX packets allow 546 bytes of data on all networks and can + sometimes be substantially larger than that depending on the maximum + packet size supported by network routers. Some networks can handle packet + sizes of 4096 bytes or more. + +Session Services + + As in the relationship between IPX and NetBIOS datagrams, Novell's SPX + protocol serves much the same function as the NetBIOS session. Both SPX + and NetBIOS sessions provide guaranteed delivery and sequencing of + packets, but at the cost of increased overhead. + + The primary difference between the two is the supported packet size. + NetBIOS sessions support 64K packet sizes (128K with Chain Sends). SPX + has the same 546-byte packet size limitation as IPX and, in fact, SPX + allows slightly less data in a packet than IPX, since the SPX header + requires an additional 12 bytes. SPX therefore supports 534 bytes of data + on all networks with the potential for much larger packets if supported + by the routers, although attaining a 64K packet size is unlikely. + + Probably the most noticeable difference between IPX/SPX and NetBIOS is + how each addresses packets. IPX/SPX addresses packets using network, + node, and socket numbers. NetBIOS uses unique names to address packets. + Each workstation can be uniquely addressed using the network and node + numbers. + + A workstation can then have as many open sockets as desired for receiving + peer-to-peer data packets. Many methods exist for determining a + workstation's network, node, and destination socket number, but for + simplicity the example code in this article uses SAP to obtain this + information. + +The Waiting Game + + With NetBIOS, you can choose to allow most NetBIOS commands to complete + before returning control to the application, but most IPX/SPX commands + return control immediately. In other words, most IPX/SPX commands are + "no-wait" commands; there is no IPX/SPX "wait" counterpart. + + Since most NetBIOS developers use the "no-wait" variants, this difference + should not pose a problem, but if you need to use a "wait," you can code + it very simply by issuing the command and then looping on the in use + field. + +Asynchronous Events + + IPX/SPX also has a feature that is not used with NetBIOS: the + asynchronous event. An asynchronous event can be initiated at any time + and, as the name implies, can be set to occur independent of an + application's execution path. An event could be set up, for example, to + automatically broadcast an IPX packet every 45 seconds. The application + initiating this event could then continue processing and leave the timing + and broadcasting of packets to the IPX event handler. + +The Network Control Block & the Event Control Block + + From a developer's perspective, the "core" of NetBIOS is the Network + Control Block (NCB). IPX and SPX are based on an Event Control Block + (ECB) and an IPX/SPX header. Figure 1 describes the fields in the ECB. + + ********************************************************* + Figure 1: The IPX/SPX Event Control Block [- C structure -] + + void far *linkAddress Set by IPX + void (far *ESRAddress)() Equivalent to NetBIOS POST routine + BYTE inUseFlag Set when the ECB is in use, zero + when it is available + BYTE completionCode Equivalent to NetBIOS Command + Completion + WORD socketNumber Socket number associated with ECB + BYTE IPXWorkspace[4] Set by IPX + BYTE driverWorkspace[12] Set by IPX + BYTE immediateAddress[6] Node address of next "hop" + WORD fragmentCount Number of buffer fragments in packet + ECBFragment fragmentDescriptor[2] Address and size of fragment(s) + + END of FIGURE 1 + ********************************************************* + + [- ********************************************************* + Figure 1a: The IPX/SPX Event Control Block (Pascal syntax) + + linkAddress :Pointer Set by IPX + ESRAddress :Pointer Equivalent to NetBIOS + POST routine + InUseFlag :Byte; Set when the ECB is in use, + zero when it is available + CompletionCode :Byte; Equivalent to NetBIOS Command + Completion + SocketNumber :Word; Socket number associated + with ECB + IPXWorkspace :array[1..4] of byte; Set by IPX + DriverWorkspace :array[1..12] of byte; Set by IPX + ImmediateAddress:array[1..6] of byte; (Tnodeaddress) + Node address of next "hop" + FragmentCount :word; Number of buffer fragments + in packet + Fragment :array[1.. ] of Tfragment Address and size of + fragment(s) + + (Note: this structure is declared as the Tecb type in the nwIPX unit) + + END of FIGURE 1a + ********************************************************* -] + + Note that the ECB contains a field that has no equivalent in the NCB + called the immediate address field. This field should be populated with + the node address of the first "hop" on the way to the packet's ultimate + destination. Novell provides an API call to populate this field, the + IPXGetLocalTarget() API available in the NetWare Client SDK. + +IPX Send Example + + The sample code in this article includes simple examples written under + DOS with the NetWare Client SDK. Figure 2 shows a routine sending an IPX + packet. + + ********************************************************* + Figure 2: IPX Send [- C example -] + + /* Send "Hello!" to the station at network 0x11111111, node + 0x222222222222, socket 0x3333 using IPX */ + + void IPXSayHello() + { + char buffer[] = "Hello!"; + ECB ecb; + IPXHeader header; + int transTime; + header.packetType = 4; + memset(header.destination.network, 0x11, 4); + memset(header.destination.node, 0x22, 6); + memset(header.destination.socket, 0x33, 2); + + ecb.ESRAddress = NULL; + ecb.socketNumber = 0x4444; + IPXGetLocalTarget(header.destination, + ecb.immediateAddress, &transTime); + ecb.fragmentCount = 2; + ecb.fragmentDescriptor[0].address = &header; + ecb.fragmentDescriptor[0].size = sizeof(IPXHeader); + ecb.fragmentDescriptor[1].address = buffer; + ecb.fragmentDescriptor[1].size = strlen(buffer) + 1; + IPXSendPacket(&ecb); + } + + END of FIGURE 2 + ********************************************************* + + [- ********************************************************* + Figure 2a: IPX Send (Pascal example) + + { Send "Hello!" to the station at network $11111111, node + $222222222222, socket $3333 using IPX } + + Procedure IPXSayHello; + Var buffer:string; + ecb:Tecb; + header:TipxHeader; + transTime:Word; + begin + Buffer:="Hello!"; + header.packetType := 4; + FillChar(header.destination.network,4,$11); + FillChar(header.destination.node, 6,$22); + FillChar(header.destination.socket, 2,$33); + + ecb.ESRAddress:=NIL; + ecb.socketNumber:=$4444; + IPXGetLocalTarget(header.destination, + ecb.immediateAddress, transTime); + ecb.fragmentCount:=2; + ecb.fragment[1].address:= @header; + ecb.fragment[1].size := SizeOf(TIPXHeader); + ecb.fragment[2].address:= @buffer[1]; + ecb.fragment[2].size:= ord(buffer[0]); + IPXSendPacket(ecb); + end; + + END of FIGURE 2a + ********************************************************* -] + + + The first apparent difference between IPX and NetBIOS is that IPX uses + two buffers where NetBIOS would use one. The first buffer is the IPX + Header containing the source and destination addresses, the packet type, + and several "housekeeping" fields. Refer to Figure 3 for a description of + the IPX header. + + ********************************************************* + Figure 3: IPX Header + + WORD checkSum Included to conform to Xerox IDP standard + Set to FFFF by IPX + WORD length Length of entire IPX packet including + header + Set by IPX + BYTE transportControl Hop count - Set to zero by IPX + BYTE packetType IPX packet type is 4 + IPXAddress destination Address the packet is sent to + [- Pascal: of type TInternetworkAddress -] + IPXAddress source Address of node sending packet set by IPX + [- Pascal: of type TinternetworkAddress -] + + END of FIGURE 3 + ********************************************************* + + The second buffer is the data to be sent. Two fields in the IPX header + must be set for an IPX send: the packet type and the destination address. + IPX packets are type 4, SPX packets are type 5. [- Note that according + to the original xerox definitions this statement is not correct. Type 4 + packets are reserved for the PEP protocol. Use type 0 (undefined) when + transmitting standard IPX packets-] The destination address consists + of a four-byte network number, a six-byte node number, and a two-byte + socket number. + + If these examples used an Event Service Routine (ESR), the ESR address + would be filled with the address of a procedure to be run when the send + completes, but since NULL is specified, this routine will not be run. The + ESR is equivalent to the NetBIOS POST routine. When the IPX send + executes, the rest of the fields in the IPX header are filled in + automatically, including the source address. You must specify the socket + number to be included in the source address, but the socket need not be + open to send a packet. For this example, socket number 0x4444 was + arbitrarily chosen. + + The immediate address field described above must be filled in as well, + and the IPXGetLocalTarget() API call fills in this field with the + appropriate value. It is passed the final destination of the packet and + it calculates the address of the "first hop" on the way to the final + destination. Note that if the target workstation is on the same subnet as + the sending workstation the immediate address will be the same as the + final destination. Otherwise, it will be a bridge or router on the + subnet. + + Each of the buffers sent in the IPX packet is considered to be a + fragment. Since there are two buffers (the IPX header and the data), the + fragment count is equal to two. The address and size of the fragments are + then entered, starting with the IPX header. As soon as all of the + relevant fields are filled, the example calls IPXSendPacket() and passes + it the address of the ECB. + + Receiving an IPX packet is much like sending one from a programming + standpoint, except that you do not need to set the IPX header fields. In + the ECB, you should set the ESR address, socket number, immediate + address, and fragment descriptors. + + Note about socket numbers: the socket number specified for an IPX send + does not need to be open, but for an IPX receive the socket must be open. + The API call to receive an IPX packet is IPXListenForPacket(). + +SPX Connection Example + + Figure 4 contains a code sample that establishes an SPX connection. + Before the request for an SPX connection is submitted, several ECBs are + already listening for data (this is important). SPX temporarily "steals" + two ECBs from the available and waiting ones for connection maintenance, + and then it puts the stolen ECBs back in the pool when finished. If there + are no pending ECBs for SPX to use, it cannot send an acknowledgement to + the remote site and the connection will stall and time out. + + ********************************************************* + Figure 4: Establishing an SPX Connection [- C code example -] + + /* Start an SPX connection with the station at network + 0x11111111, node 0x222222222222, socket 0x3333, use + local socket 0x4444 */ + + #define NUM_BUFFS 5 + + void call() + { + ECB send, receive[NUM_BUFFS], connect, term; + SPXHeader sendHdr, rcvHdr[NUM_BUFFS], connHdr; + char buffer[NUM_BUFFS][80], sendbuf[] = "Hello!"; + int i, ccode, packetsReceived; + WORD spxConnectionID; + + for (i = 0; i < NUM_BUFFS; i++) { + receive[i].ESRAddress = NULL; + receive[i].socketNumber = 0x4444; + receive[i].fragmentCount = 2; + receive[i].fragmentDescriptor[0].address + = &(rcvHdr[i]); + receive[i].fragmentDescriptor[0].size + = sizeof(SPXHeader); + receive[i].fragmentDescriptor[1].address + = &(buffer[i]); + receive[i].fragmentDescriptor[1].size = 80; + SPXListenForSequencedPacket(receive[i]); + } + + connect.ESRAddress = NULL; + connect.socketNumber = 0x4444; + connect.fragmentCount = 1; + connect.fragmentDescriptor[0].address = &connHdr; + connect.fragmentDescriptor[0].size + = sizeof(SPXHeader); + + memset(connHdr.destination.network, 0x11, 4); + memset(connHdr.destination.node, 0x22, 6); + memset(connHdr.destination.socket, 0x33, 2); + + ccode = SPXEstablishConnection(0, 0, + &spxConnectionID, + &connect); + printf("SPXEstablishConnection return code + = 0x%x\n", ccode); + if (ccode != 0) + return; + while (connect.inUseFlag != 0) + IPXRelinquishControl(); + if (connect.completionCode != 0) + return; + send.ESRAddress = NULL; + send.fragmentCount = 2; + send.fragmentDescriptor[0].address = &sendHdr; + send.fragmentDescriptor[0].size = sizeof(SPXHeader); + send.fragmentDescriptor[1].address = sendbuf; + send.fragmentDescriptor[1].size = 7; + SPXSendSequencedPacket(spxConnectionID, &send); + + packetsReceived = 0; + while (packetsReceived < 10) { + for (i = 0; i < NUM_BUFFS; i++) { + if (receive[i].inUseFlag != 0) { + if (receive[i].completionCode != 0) { + packetsReceived = 10; + /* If we get an error, terminate */ + break; + } + printf("Received: %s\n", buffer[i]); + packetsReceived++; + } + SPXListenForSequencedPacket(receive[i]); + } + IPXRelinquishControl(); + } + + term.ESRAddress = NULL; + term.fragmentCount = 1; + term.fragmentDescriptor[0].address = &connHdr; + term.fragmentDescriptor[0].size = sizeof(SPXHeader); + SPXTerminateConnection(spxConnectionID, &term); + while (term.inUseFlag != 0) + IPXRelinquishControl(); + for (i = 0; i < NUM_BUFFS; i++) + IPXCancelEvent(receive[i]); + } + END of FIGURE 4 + ********************************************************* + + [- ********************************************************* + Figure 4a: Establishing an SPX Connection (Pascal example) + + { Start an SPX connection with the station at network + $11111111, node $222222222222, socket $3333, use + local socket $4444 } + + CONST NUM_BUFFS=5; + + Procedure call; + Var send,connect,term:Tecb; + receive :array[1..NUM_BUFFS] of Tecb; + sendHdr, connHdr : TspxHeader; + rcvHdr :array[1..NUM_BUFFS] of TspxHeader; + buffer :array[1..NUM_BUFFS] of string[80]; + sendBuf :string; + i,packetsReceived:Integer; + spxConnectionId :word; + begin; + sendbuf:="Hello!"; + + for i:= 1 to NUM_BUFFS + do begin + receive[i].ESRAddress := NIL; + receive[i].socketNumber := $4444; + receive[i].fragmentCount = 2; + receive[i].fragment[1].address := @rcvHdr[i]; + receive[i].fragment[1].size := sizeof(TSPXHeader); + receive[i].fragment[2].address := @buffer[i]; + receive[i].fragment[2].size := 80; + SPXListenForSequencedPacket(receive[i]); + end; + + connect.ESRAddress := NIL; + connect.socketNumber := $4444; + connect.fragmentCount := 1; + connect.fragment[1].address := @connHdr; + connect.fragment[1].size := sizeof(TSPXHeader); + + FillChar(connHdr.destination.network, 4, $11); + FillChar(connHdr.destination.node, 6, $22); + FillChar(connHdr.destination.socket, 2, $33); + + IF NOT SPXEstablishConnection(0, 0, + spxConnectionID, + connect) + then begin + writeln('SPXEstablishConnection return code', + HexStr(nwSpx.result,2)); + exit; + end; + + while (connect.inUseFlag <> 0) + do IPXRelinquishControl(); + if (connect.completionCode <> 0) + then exit; + + send.ESRAddress := NIL; + send.fragmentCount := 2; + send.fragment[1].address = @sendHdr; + send.fragment[1].size := sizeof(TSPXHeader); + send.fragment[2].address := @sendbuf[0]; + send.fragment[2].size := ord(sendBuf[0])+1; + SPXSendSequencedPacket(spxConnectionID, send); + + packetsReceived := 0; + while (packetsReceived < 10) + do begin + for i :=1 to NUM_BUFFS + do begin + if (receive[i].inUseFlag <> 0) + and (receive[i].completionCode <> 0) + then begin + packetsReceived := 10; + exit; + { If we get an error, terminate } + end; + writeln('Received: ", buffer[i]); + inc(packetsReceived); + SPXListenForSequencedPacket(receive[i]); + end; + IPXRelinquishControl; + end; + + term.ESRAddress := NIL; + term.fragmentCount := 1; + term.fragment[1].address := @connHdr; + term.fragment[1].size := sizeof(TSPXHeader); + SPXTerminateConnection(spxConnectionID, term); + while (term.inUseFlag <> 0) + do IPXRelinquishControl; + for i:=1 to NUM_BUFFS + do IPXCancelEvent(receive[i]); + end; + + END of FIGURE 4a + ********************************************************* -] + + This process may sound complicated, but everything happens transparently. + As long as there are extra ECBs available, the application never knows + they have been borrowed, since SPX puts them back in the exact same state + they were in when they were pressed into service. + + If the connection is established with the SPX watchdog enabled, the + watchdog monitors the connection and notifies the application if the + connection fails, even if the application is not currently sending data + over the connection. This feature is useful for applications that start + SPX connections, but use them infrequently. For simplicity, however, the + example does not use the SPX watchdog. + + After the listen ECBs have been posted, the connection ECB is then set up + in much the same way the IPX send ECB was, except that this ECB has only + one fragment: the SPX header. The destination network, node, and socket + also are set the same way they were in the previous example. + + SPXEstablishConnection() is passed a retry count of zero, indicating that + you should use the default value for number of retries. This value is set + in the workstation's NET.CFG file using the IPX RETRY COUNT parameter, + which defaults to 20. The last zero passed in SPXEstablishConnection() + indicates not to use the SPX watchdog. The SPX connection ID is returned + as the third parameter. The SPX connection ID can be considered + equivalent to the NetBIOS local session number. + + Next, the sample code attempts to establish a connection. It polls the + ECB's in use flag waiting for the event to complete. The + IPXRelinquishControl() call is very important at this stage. If the code + did nothing but sit in a tight loop, IPX and SPX would never get the + chance to do any processing. IPXRelinquishControl() allows the IPX/SPX + layer to get some work done. + + Once the in use flag is set to zero, the example checks the return code + to see if the attempt to establish a connection was successful. The code + does not illustrate how to handle the various failure cases, but the most + likely cause of a failure would be that the other side is not yet + listening for a connection, just like in NetBIOS. After establishing the + connection, packets can be sent to the remote station. + + The SPXSendSequencedPacket() call requires much less information than its + IPX counterpart. Since the connection is already established, all + SPXSendSequencedPacket() needs is the SPX connection ID, an ESR address, + and the fragment information. + + After sending a packet, the example program waits for ten packets to + arrive. When an ECB comes back, the example displays the data and then + re-submits the ECB so that it can be used to receive a packet again. + After receiving ten packets, it issues an SPXTerminateConnection() call + to notify the other side that it is done. + + The call to terminate the connection takes almost the same parameters + that the establish connection call does, except that there is no need to + fill out any information in the SPX header. Once the connection has been + terminated, the pending listen ECBs must be cancelled. To do so, the + example calls IPXCancelEvent(). Unlike most other ECB-related calls, + IPXCancelEvent() does not return until the ECB has been cancelled so + there is no need to poll the in use flag. + +Event Service Routines + + Event Service Routines (ESRs) serve the same purpose as the NetBIOS POST + routines, but require a little more setup than the standard POST routine. + Most ESRs are written in Assembly, although some call C functions. + + Figure 5 shows a generic ESR that calls a C function after allocating its + own stack. This is very important since the amount of free stack space + (if any) at interrupt time is unknown, and any attempt by a C function to + use the stack could result in memory corruption if the stack is + overflowed. The only way to guarantee that this will not occur is to + allocate sufficient stack space in the ESR. + + ********************************************************* + Figure 5: Example Event Service Routine (ESR) [- C/ASM code -] + + .MODEL LARGE + + public _ReceiveESRHandler + extrn _ProcessReceiveData:PROC + + .DATA + + ; The stack segment and pointer must be saved so that you can set up + ; your own stack. + + stk_seg dw 0 ; variable to store old stack segment + stk_ptr dw 0 ; variable to store old stack pointer + stk_stk dw 512 dup (0) ; new stack of 1024 bytes in length + stk_end dw 0 ; the end of the stack + + .CODE + + ; @datasize is TRUE if the model is MEDIUM or LARGE and FALSE if the + ; model is SMALL or COMPACT. Just modify the .MODEL ???? above for the + ; model you want. ES/SI holds the seg/offset of the currently used ECB + ; that ProcessReceivedData needs to process. + + _ReceiveESRHandler PROC far + mov ax,DGroup + mov ds,ax + mov stk_seg,ss ; Save the stack segment + mov stk_ptr,sp ; Save the stack pointer + mov ss,ax ; move the segment of new_stk into ss + mov sp,offset stk_end ; move offset of new_stk to sp + IF @datasize + push es ; push es if mem. model medium/large + ENDIF + push si + call _ProcessReceivedData + mov ss,stk_seg ; Restore old stack segment + mov sp,stk_ptr ; Restore old stack pointer + retf + _ReceiveESRHandler ENDP + + END END of FIGURE 5 + ********************************************************* + + + [- ********************************************************* + Figure 5a: Example Event Service Routine (ESR) (BASM/Pascal) + + { The stack segment and pointer must be saved so that you can set up + your own stack. } + + Var stk_stk:array[1..512] of word; { new stack of 1024 bytes in length } + stk_end:word; { the end of the stack } + + {$F+} + Procedure ESRhandler(Var p:Tpecb); { * Type TPecb=^Tecb } + begin + . + . + end; + {$F-} + + {$F+} + Procedure ListenESR; assembler; + asm { ES:SI are the only valid registers when entering this procedure ! } + mov dx, seg stk_stk { = seg @DATA } + mov ds, dx + + mov dx,ss { setup of a new local stack } + mov bx,sp { ss:sp copied to dx:bx} + mov ax,ds + mov ss,ax + mov sp,offset stk_end + push dx { push old ss:sp on new stack } + push bx + push es { * push es:si on stack as local vars } + push si { * } + mov di,sp { * } + push ss { * push address of local ptr on stack } + push di { * } + + CALL EsrHandler + + add sp,4 { skip stack ptr-copy } + pop bx { restore ss:sp from new stack } + pop dx + mov sp,bx + mov ss,dx + end; + {$F-} + + Note that a local stack of 1024 bytes (512 words) may not be large + enough for some applications calling other functions within the + ESRhandler. Increase the stacksize by 1024 bytes at a time to + determine the stack requirement. + + + END END of FIGURE 5a + ********************************************************* -] + + Figure 6 contains a code fragment demonstrating the use of an ESR. It + receives ten SPX packets just like the example in Figure 3 does, but it + uses an ESR instead of polling the in use flag. The assembly language + routine from Figure 4 is declared as the ESR, and it in turn calls the + C [-/Pascal-] function ProcessReceivedData(). + + ********************************************************* + Figure 6: Using an Event Service Routine (ESR) [- C Code -] + + int packetCount = 0; + + void ProcessReceivedData(ECB *ecb) + { + packetCount++; + printf("%s\n", ecb->fragmentDescriptor[1].address); + SPXListenForSequencedPacket(ecb); /* Re-issue the listen */ + } + + main() + { + . + . /* This code is identical to SPX setup code in Fig. 4, except */ + . /* for receive[i].ESRAddress line, which will be as follows: */ + + receive[i].ESRAddress = (void (far *) () ) ReceiveESRHandler; + + . + . /* The send ECB does not normally use an ESR. */ + . + + while (packetCount < 10) + IPXRelinquishControl(); + . + . /* Shut down connection, cancel ECBs */ + . + } + + END of FIGURE 6 + ********************************************************* + + [- ********************************************************* + Figure 6a: Using an Event Service Routine (ESR) (Pascal) + + Var PacketCount; + + Procedure ProcessReceivedData(Var ECB:Tecb) + begin + inc(packetCount); + writeln(string(ecb^.fragment[2].address^)); + SPXListenForSequencedPacket(ecb); { Re-issue the listen } + end; + + begin { main body } + PacketCount:=0; + + . + . { This code is identical to SPX setup code in Fig. 4a, except } + . { for receive[i].ESRAddress line, which will be as follows: } + + receive[i].ESRAddress := @ReceiveESRHandler; + . + . { The send ECB does not normally use an ESR. } + . + + while (packetCount < 10) + do IPXRelinquishControl; + . + . { Shut down connection, cancel ECBs } + . + end; + + END of FIGURE 6a + ********************************************************* -] + + IPX and SPX may look a little more complicated than NetBIOS at first, but + as soon as you begin using these protocols, you see how similar they + really are. Using IPX/SPX requires slightly more effort, but the + performance and compatibility gains when running under NetWare more than + compensate. If you are thinking about becoming more familiar with IPX and + SPX development, feel free to contact Novell's Developer Support group at + 1-800-NETWARE (1-800-638-9273) or 1-801-429-5588. diff --git a/NWTP/XIPX/CHKVEND.PAS b/NWTP/XIPX/CHKVEND.PAS new file mode 100644 index 0000000..df04f45 --- /dev/null +++ b/NWTP/XIPX/CHKVEND.PAS @@ -0,0 +1,105 @@ +program chkvend; +{$I-} + +{ Testprogram checking all nodes on all attached servers and + showing the manifacturers of the corresponding ethernet cards. } + +uses nwMisc,nwConn,nwServ; + +var PleaseMail:Boolean; + Path :string; + + StationNbr : byte; + StationAddress: TinternetworkAddress; + Sinfo : TFileServerInformation; + t,conn : byte; + + ObjName :string; + objType :word; + ObjId :Longint; + LoginTime:TnovTime; + + s,ts,subs:string; + f :text; + fnd :boolean; + p :byte; +begin +PleaseMail:=False; + +Path:=ParamStr(0); +while NOT (path[ord(path[0])] IN [':','\','/']) do dec(Path[0]); +{Path now holds the name of the path where the chkvend.exe file is located } + +assign(f,Path+'VEND_XXX.'); +reset(f); +IF IOresult<>0 + then begin + writeln('Couldn''t open VEND_XXX'); + writeln(''); + halt(1); + end; + +{ Check all 8 possible server attchments } +For conn:=1 to 8 + do begin + SetPreferredConnectionId(conn); + If IsConnectionIdInUse(conn) + then begin + GetFileServerInformation(Sinfo); { Get maximum number of conections } + for t:=1 to Sinfo.ConnectionsMax + do begin + { check all connections } + IF GetInternetAddress(t,StationAddress) + then begin + GetConnectionInformation(t,objName,objType,ObjId,LoginTime); + objname:=objName+' '; + objName[0]:=#16; + ts:=HexDumpStr(StationAddress.node,12); + { check file if vendor's code known } + fnd:=False; + reset(f); + REPEAT + readln(f,s); + p:=pos('#',s); + if p>0 then s[0]:=chr(p-1); + p:=pos(' ',s); + if p=0 + then suBs:='' + else begin + subS:=copy(s,1,p-1); + if pos(subs,ts)=1 + then begin + fnd:=true; + writeln(ts,' ',objName,' -',s); + end; + end; + + UNTIL eof(f) or fnd; + + if (NOT fnd) + then begin + PleaseMail:=true; + writeln(ts,' ',objname,' -????'); + end; + + end; + end; + end; + end; + +IF PleaseMail + then begin + writeln; + writeln('A number of unknown Vendor codes have been found.'); + writeln('If you know the vendor(s) of the Ethernet cards in question,'); + writeln('you can update the VEND_XXX. file with a text editor.'); + writeln; + writeln('You are also kindly requested to mail the information to us.'); + writeln('Fido : 2:512/250.4064 or 2:2426/4030.13'); + writeln('InterNet: Rene.Spronk@p4064.f250.n512.z2.fidonet.org'); + writeln; + end; + +SetPreferredConnectionId(0); +close(f); +end. \ No newline at end of file diff --git a/NWTP/XIPX/FGET.PAS b/NWTP/XIPX/FGET.PAS new file mode 100644 index 0000000..de2ab20 --- /dev/null +++ b/NWTP/XIPX/FGET.PAS @@ -0,0 +1,201 @@ +{$X+,V-,B-,I-} +program Fget; { Listening Process / receiver / Slave } + +{ Testprogram for the nwPEP unit / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +{$DEFINE noTRACE} + +uses crt,nwMisc,nwIPX,nwPEP; + +Var ListenECB :Tecb; { used to listen for packets } + ListenPepHdr :TpepHeader; + + SendECB :Tecb; { used to send acknowledgements } + SendPepHdr :TpepHeader; + + IOsocket :word; + DataBuffer :array[1..546] of byte; + SendDataBuffer:byte; + + PacketReceived :Boolean; + LastTransactionID:LongInt; + + NewStack:array[1..8192] of word; { !! used by ESR } + StackBottom:word; { !! used by ESR } + +Procedure CheckError(err:boolean; errNbr:word); +begin +if err + then begin + CASE errNbr of + $0100:writeln('IPX needs to be installed.'); + $0101:writeln('ERROR: Connection not established. A Timeout occured'); + $0102:writeln('ERROR: The transfer is aborted; A timeout occured.'); + $0108:writeln('Transfer aborted.'); + $0300:writeln('The supplied path doesn'' exist / no write rights in directory.'); + $0301:writeln('Error writing to file / no write rights in directory.'); + $10FE:writeln('Error opening socket: Socket Table Is Full.'); + $10FF:writeln('Error opening socket: Socket is already open.'); + else writeln('Unspecified error.'); + end; {case} + IPXcloseSocket(IOsocket); + halt(1); + end; +end; + +Function TimeOut(t1,t2:word;n:byte):boolean; +{ ticks t2 - ticks t1 > n seconds ? } +Var lt1,lt2:LongInt; +begin +lt2:=t2; +if t1>t2 then lt2:=lt2+$FFFF; +TimeOut:=(lt2-t1)>(n*18); +end; + +{$F+} +Procedure ListenAndAckHandler; +begin +If (ListenECB.CompletionCode<>0) + or (ListenPepHdr.IPXhdr.packetType<>PEP_PACKET_TYPE) + or (ListenPepHdr.clienttype<>$EA) + or (ListenPepHdr.TransactionIDLastTransactionID); { new packet received } + + { Acknowledge new packets and duplicates of the latest packet, } + { as the original acknowledgement may have been lost. } + LastTransactionID:=ListenPepHdr.TransactionID; + + { Setup acknowledgement ECB and PEPheader, and send it. } + if SendECB.InUseFlag=0 + then begin + ListenPepHdr.IPXhdr.source.socket:=swap(ListenPepHdr.IPXhdr.source.socket); + { socket is hi-lo in IPX/PEPHeaders. SetupSendECB expects lo-hi } + PEPsetupSendECB(NIL,IOsocket,ListenPepHdr.IPXhdr.source, + @SendDataBuffer,0, + SendPepHdr,SendECB); + SendPepHdr.TransactionId:=LastTransactionID; + SendPepHdr.ClientType:=$EA; + IPXsendPacket(SendECB); + end; + end; +end; +{$F-} + +{$F+} +Procedure ListenAndAckESR; assembler; +asm + mov dx, seg stackbottom + mov ds, dx + + mov dx,ss { setup of a new local stack } + mov bx,sp { ss:sp copied to dx:bx} + mov ax,ds + mov ss,ax + mov sp,offset stackbottom + push dx { push old ss:sp on new stack } + push bx + CALL ListenAndAckHandler + pop bx { restore ss:sp from new stack } + pop dx + mov sp,bx + mov ss,dx +end; +{$F-} + +Var ticks,ticks2 :word; + FileName:string; + FileSize:LongInt; + DirName:string; + f:file; + BytesToWrite,BytesWritten:word; + +begin +IpxInitialize; +CheckError(nwIPX.result>0,$100); + +If (pos('?',ParamStr(1))>0) or (paramcount>1) + then begin + writeln('Usage: FGET '); + writeln('-The File sent by FSEND on another workstation'); + writeln('will be copied to the supplied directory.'); + halt(1); + end; +If paramcount=1 + then DirName:=ParamStr(1) + else DirName:='.'; + +IF NOT (DirName[ord(dirName[0])] IN [':','\']) + then DirName:=DirName+'\'; +assign(f,DirName+'temp.$$$'); +rewrite(f,1); +CheckError(IOresult<>0,$0300); +close(f); + +IOSocket:=$5678; +IPXopenSocket(IOsocket,SHORT_LIVED_SOCKET); +CheckError(nwIPX.result>0,$1000+nwIPX.result); + +{ Setup of ECB and PepHeader, start listening for incoming packets. } +LastTransactionID:=0; +PacketReceived:=False; +PEPSetupListenECB(Addr(ListenAndAckESR),IOsocket,@DataBuffer,546, + ListenPepHdr,ListenECB); +IPXListenForPacket(ListenECB); +writeln('Waiting for FSEND to start sending.. (any key to abort)'); + +IPXGetIntervalMarker(ticks); +REPEAT + IPXrelinquishControl; + IPXGetIntervalMarker(ticks2); + CheckError(TimeOut(ticks,ticks2,130),$101);{ error if a timeout occurred } + CheckError(Keypressed,$108); +UNTIL PacketReceived; + +writeln('Handshaking.. Initiating transfer process.'); +{$IFDEF TRACE} +writeln('Received PacketID:',LastTransactionID); +{$ENDIF} + +{ do something with DataBuffer: the data that was just received. } +{ the first packet contains the filename and filesize } +Move(DataBuffer[1],FileName[0],15); +Move(DataBuffer[16],FileSize,4); +writeln('Receiving file ',FileName,', size: ',FileSize); + +assign(f,DirName+filename); +rewrite(f,1); +BytesToWrite:=512; + +REPEAT { Listen for remaining packets } + Packetreceived:=false; + + While SendECB.InuseFlag<>0 + do IPXrelinquishControl; + + IPXListenForPacket(ListenECB); + IPXGetIntervalMarker(ticks); + Repeat + IPXrelinquishControl; + IPXGetIntervalMarker(ticks2); + CheckError(TimeOut(ticks,ticks2,10),$102); { error if Timeout occurred } + CheckError(Keypressed,$108); + until PacketReceived; + {$IFDEF TRACE} + writeln('Received packet#:',LastTransactionID); + {$ENDIF} + + { Write DataBuffer to disk. } + IF FileSize<512 + then BytesToWrite:=FileSize; + BlockWrite(f,DataBuffer,BytesToWrite,BytesWritten); + CheckError(BytesToWrite<>BytesWritten,$0301); + + FileSize:=FileSize-512; +UNTIL (FileSize<=0); { entire file received } + +writeln('Transfer complete.'); +IPXcloseSocket(IOsocket); +close(f); +end. \ No newline at end of file diff --git a/NWTP/XIPX/FSEND.PAS b/NWTP/XIPX/FSEND.PAS new file mode 100644 index 0000000..c41283e --- /dev/null +++ b/NWTP/XIPX/FSEND.PAS @@ -0,0 +1,255 @@ +{$X+,V-,B-,I-} +program Fsend; { Master / Sender } + +{ Testprogram for the nwPEP unit / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +{$DEFINE noTRACE} + +uses dos,crt,nwMisc,nwBindry,nwConn,nwIPX,nwPEP; + +CONST IOSocket=$5678; { socket to transmit/receive on } + +Var ListenECB :Tecb; { ECB and header, to listen for acknowledgement } + ListenPepHdr :TpepHeader; + + SendECB :Tecb; { ECB and header, used to send the data } + SendPepHdr :TpepHeader; + + socket :word; + + SendDataBuffer :array[1..546] of byte; { SendDataBufferfer for data to be sent } + + ListenDataBuffer:array[1..8] of byte; + + AckReceived :boolean; { set to true within the ListenForAckESR } + + SendTransId :LongInt; { transactionID. This uniquely identifies + the packet. The slave/receiver has to + reply with the same transactionID in the + header of the acknowledgement. Only if + this number is the same as the transactioID + of the sent packet, the pavket is considered + successfully delivered. } + + NewStack:array[1..1024] of word; { !! used by ESR } + StackBottom:word; { !! used by ESR } + + f:file; + + +Procedure CheckError(err:boolean; errNbr:word); +begin +if err + then begin + writeln; + CASE errNbr of + $0100:writeln('IPX needs to be installed.'); + $0200:writeln('Error: can''t locate the spcified username.'); + $0201:begin + writeln('The specified user has multiple connections.'); + writeln('This demonstation program doesn''t support multiple connections.'); + end; + $0202:writeln('Error: can''t find the address of the supplied username.'); + $0204:writeln('Transfer aborted after 50 retries.'); + $0205:writeln('Key pressed: Transfer aborted.'); + $0206:writeln('The supplied file couldn''t be found. Please supply full path.'); + $0300:writeln('Error reading file.'); + $10FE:writeln('Error opening socket: Socket Table Is Full.'); + $10FF:writeln('Error opening socket: Socket is already open.'); + end; {case} + IPXcloseSocket(IOsocket); + close(f); + halt(1); + end; +end; + +Function TimeOut(t1,t2:word;n:byte):boolean; +{ ticks t2 - ticks t1 > n seconds ? } +Var lt1,lt2:LongInt; +begin +lt2:=t2; +if t1>t2 then lt2:=lt2+$FFFF; +TimeOut:=(lt2-t1)>(n*18); +end; + + +{$F+} +Procedure ListenForAckHandler(Var p:TPecb); + { Interrupts are turned off -and should remain turned off- } +begin +IF (ListenECB.CompletionCode<>0) { packet must be suucessfully received.. } + or (ListenPepHdr.IPXhdr.packetType<>PEP_PACKET_TYPE) { of type PEP.. } + or (ListenPepHdr.ClientType<>$EA) { of client type $EA } + or (ListenPepHdr.TransactionID<>SendTransId) { with a correct clientID (of the packet the master sent) } + then IPXListenForPacket(ListenECB) { Invalid packet => listen again } + else AckReceived:=true; { valid packet => ACK received ! } +end; +{$F-} + +{$F+} +Procedure ListenForAckESR; assembler; +asm { ES:SI are the only valid registers when entering this procedure ! } + { interrupts are turned off -and should remain turned off- } + mov dx, seg stackbottom + mov ds, dx + + mov dx,ss { setup of a new local stack } + mov bx,sp { ss:sp copied to dx:bx} + mov ax,ds + mov ss,ax + mov sp,offset stackbottom + push dx { push old ss:sp on new stack } + push bx + + push es { push es:si on stack as local vars } + push si + mov di,sp + + push ss { push address of local ptr on stack } + push di + CALL ListenForAckHandler + + add sp,4 { skip stack ptr-copy } + pop bx { restore ss:sp from new stack } + pop dx + mov sp,bx + mov ss,dx +end; +{$F-} + + +Var dest:TinternetworkAddress; + ticks,ticks2:word; + retries :word; + + Uname,Fname:string; + NbrOfConn:byte; + connList:TconnectionList; + + p:byte; + FileInfo:searchrec; + FileSize:LongInt; + BytesRead:word; + + TransferStartTicks,TransferEndTicks:word; + OriginalFileSize:LongInt; + +begin +If paramcount<>2 + then begin + writeln('Usage: FSEND '); + writeln('-The file will be sent to the workstation of the supplied username.'); + writeln('-Run FGET on that workstation to receive the file.'); + halt(1); + end; +Uname:=ParamStr(1); +UpString(Uname); +NbrOfConn:=0; +GetObjectConnectionNumbers(Uname,OT_USER,NbrOfConn,connList); +CheckError((nwConn.result>0) or (NbrOfConn=0),$200); +CheckError(NbrOfConn>1,$0201); + +GetInternetAddress(connList[1],dest); +CheckError(nwconn.result>0,$0202); +dest.socket:=IOsocket; + +Fname:=ParamStr(2); +Assign(f,Fname); +Reset(f,1); +CheckError(IOresult<>0,$0206); + + +IpxInitialize; +CheckError(nwIPX.result>0,$0100); + +socket:=IOSocket; +IPXopenSocket(Socket,SHORT_LIVED_SOCKET); +CheckError(nwIPX.result>0,$1000+nwIPX.result); + +{ setup listening for ack } +AckReceived:=False; + +PEPsetupListenECB(Addr(ListenForAckESR),IOsocket,@ListenDataBuffer,8, + ListenPepHdr,ListenECB); +IPXListenForPacket(ListenECB); + +{ send initial packet with the name and size of the file to be sent. } +findfirst(Fname,$FF,FileInfo); +Move(FileInfo.size,SendDataBuffer[16],4); +FileSize:=Fileinfo.size; +p:=length(Fname); +while (p>0) and (Fname[p]<>':') and (Fname[p]<>'\') + do dec(p); +If p>0 + then delete (Fname,1,p); +Move(Fname[0],SendDataBuffer[1],15); + +PEPsetupSendECB(NIL,IOsocket,dest,@SendDataBuffer[1],512, + SendPepHdr,SendECB); +SendTransID:=1; +SendPepHdr.ClientType:=$EA; + +OriginalFileSize:=FileSize; +FileSize:=FileSize+512; { compensate length for information header } + +writeln('FSEND waiting for remote handshake. (any key to abort)'); + +While Filesize>0 + do begin + ackreceived:=false; + SendPepHdr.TransactionId:=SendTransId; + IPXsendPacket(SendECB); + {$IFDEF TRACE} + write('Packet#',SendTransID,' sent.'); + {$ENDIF} + while sendECB.InuseFlag<>0 + do IPXrelinquishControl; + + IPXGetIntervalMarker(ticks); + retries:=0; + REPEAT + IPXrelinquishcontrol; + IPXGetIntervalMarker(ticks2); + if (ticks2-ticks)>2 + then begin + inc(retries); + {$IFDEF TRACE} + writeln; + write('Timeout: resending packet#',SendTransID); + {$ENDIF} + IPXsendPacket(SendECB); + while sendECB.InuseFlag<>0 + do IPXrelinquishControl; + IPXGetIntervalMarker(ticks); + end; + CheckError(retries>50,$0204); + CheckError(Keypressed,$0205); + UNTIL AckReceived; + if SendTransID=1 + then begin + writeln('Handshake received. Starting file transfer.'); + IPXGetIntervalMarker(TransferStartTicks); + end; + {$IFDEF TRACE} + writeln(' Ackn.#',ListenPepHdr.TransactionID,' received.'); + {$ENDIF} + FileSize:=FileSize-512; + + { fill buffer with next block of data } + IF FileSize>0 + then begin + BlockRead(f,SendDataBuffer,512,bytesread); + CheckError((bytesread<512) and (filesize<>bytesread),$0300); + end; + + inc(SendTransID); + IPXListenForPacket(ListenECB); { start listening for acks again } + end; +IPXGetIntervalMarker(TransferEndTicks); +IPXcancelEvent(ListenECB); +Writeln('Transfer completed.'); +writeln('Throughput: ', 18*OriginalFileSize/(TransferEndTicks-TransferStartTicks):4:2,' bps'); +IPXcloseSocket(IOsocket); +close(f); + +end. \ No newline at end of file diff --git a/NWTP/XIPX/M1_PEP.PAS b/NWTP/XIPX/M1_PEP.PAS new file mode 100644 index 0000000..da61999 --- /dev/null +++ b/NWTP/XIPX/M1_PEP.PAS @@ -0,0 +1,230 @@ +{$X+,V-,B-,I-} +program M1_PEP; { Master / Sender } + +{ Testprogram for the nwPEP unit / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +uses dos,crt,nwMisc,nwBindry,nwConn,nwIPX,nwPEP; + +CONST IOSocket=$5678; { socket to transmit/receive on } + +Var ListenECB :Tecb; { ECB and header, to listen for acknowledgement } + ListenPepHdr :TpepHeader; + + SendECB :Tecb; { ECB and header, used to send the data } + SendPepHdr :TpepHeader; + + socket :word; + + SendDataBuffer :array[1..546] of byte; { SendDataBufferfer for data to be sent } + + ListenDataBuffer:array[1..8] of byte; + + AckReceived :boolean; { set to true within the ListenForAckESR } + + SendTransId :LongInt; { transactionID. This uniquely identifies + the packet. The slave/receiver has to + reply with the same transactionID in the + header of the acknowledgement. Only if + this number is the same as the transactioID + of the sent packet, the pavket is considered + successfully delivered. } + + NewStack:array[1..1024] of word; { !! used by ESR } + StackBottom:word; { !! used by ESR } + + f:file; + + +Procedure CheckError(err:boolean; errNbr:word); +begin +if err + then begin + CASE errNbr of + $0100:writeln('IPX needs to be installed.'); + $0200:writeln('Error: can''t locate the spcified username.'); + $0201:begin + writeln('The specified user has multiple connections.'); + writeln('This demonstation program doesn''t support multiple connections.'); + end; + $0202:writeln('Error: can''t find the address of the supplied username.'); + $0204:writeln('Transfer aborted after 50 retries.'); + $0205:writeln('Key pressed: Transfer aborted.'); + $0206:writeln('The supplied file couldn''t be found. Please supply full path.'); + $10FE:writeln('Error opening socket: Socket Table Is Full.'); + $10FF:writeln('Error opening socket: Socket is already open.'); + end; {case} + IPXcloseSocket(IOsocket); + close(f); + halt(1); + end; +end; + +Function TimeOut(t1,t2:word;n:byte):boolean; +{ ticks t2 - ticks t1 > n seconds ? } +Var lt1,lt2:LongInt; +begin +lt2:=t2; +if t1>t2 then lt2:=lt2+$FFFF; +TimeOut:=(lt2-t1)>(n*18); +end; + + +{$F+} +Procedure ListenForAckHandler(Var p:TPecb); + { Interrupts are turned off -and should remain turned off- } +begin +IF (ListenECB.CompletionCode<>0) { packet must be suucessfully received.. } + or (ListenPepHdr.IPXhdr.packetType<>PEP_PACKET_TYPE) { of type PEP.. } + or (ListenPepHdr.ClientType<>$EA) { of client type $EA } + or (ListenPepHdr.TransactionID<>SendTransId) { with a correct clientID (of the packet the master sent) } + then IPXListenForPacket(ListenECB) { Invalid packet => listen again } + else AckReceived:=true; { valid packet => ACK received ! } +end; +{$F-} + +{$F+} +Procedure ListenForAckESR; assembler; +asm { ES:SI are the only valid registers when entering this procedure ! } + { interrupts are turned off -and should remain turned off- } + mov dx, seg stackbottom + mov ds, dx + + mov dx,ss { setup of a new local stack } + mov bx,sp { ss:sp copied to dx:bx} + mov ax,ds + mov ss,ax + mov sp,offset stackbottom + push dx { push old ss:sp on new stack } + push bx + + push es { push es:si on stack as local vars } + push si + mov di,sp + + push ss { push address of local ptr on stack } + push di + CALL ListenForAckHandler + + add sp,4 { skip stack ptr-copy } + pop bx { restore ss:sp from new stack } + pop dx + mov sp,bx + mov ss,dx +end; +{$F-} + + +Var dest:TinternetworkAddress; + ticks,ticks2:word; + retries :word; + + Uname,Fname:string; + NbrOfConn:byte; + connList:TconnectionList; + + p:byte; + FileInfo:searchrec; + FileSize:LongInt; + +begin +If paramcount<>2 + then begin + writeln('Usage: M1_PEP '); + writeln('-The file will be sent to the workstation of the supplied username.'); + writeln('-Run S1_PEP on that workstation to receive the file.'); + halt(1); + end; +Uname:=ParamStr(1); +UpString(Uname); +NbrOfConn:=0; +GetObjectConnectionNumbers(Uname,OT_USER,NbrOfConn,connList); +CheckError((nwConn.result>0) or (NbrOfConn=0),$200); +CheckError(NbrOfConn>1,$0201); + +GetInternetAddress(connList[1],dest); +CheckError(nwconn.result>0,$0202); +dest.socket:=IOsocket; + +Fname:=ParamStr(2); +Assign(f,Fname); +Reset(f); +CheckError(IOresult<>0,$0206); + + +IpxInitialize; +CheckError(nwIPX.result>0,$0100); + +socket:=IOSocket; +IPXopenSocket(Socket,SHORT_LIVED_SOCKET); +CheckError(nwIPX.result>0,$1000+nwIPX.result); + +{ setup listening for ack } +AckReceived:=False; + +PEPsetupListenECB(Addr(ListenForAckESR),IOsocket,@ListenDataBuffer,8, + ListenPepHdr,ListenECB); +IPXListenForPacket(ListenECB); + +{ send initial packet with the name and size of the file to be sent. } +findfirst(Fname,$FF,FileInfo); +Move(FileInfo.size,SendDataBuffer[16],4); +FileSize:=Fileinfo.size; +p:=length(Fname); +while (p>0) and (Fname[p]<>':') and (Fname[p]<>'\') + do dec(p); +If p>0 + then delete (Fname,1,p); +Move(Fname[0],SendDataBuffer[1],15); + +PEPsetupSendECB(NIL,IOsocket,dest,@SendDataBuffer[1],512, + SendPepHdr,SendECB); +SendTransID:=1; +SendPepHdr.ClientType:=$EA; + +FileSize:=FileSize+512; { compensate length for information header } + +While Filesize>0 + do begin + ackreceived:=false; + SendPepHdr.TransactionId:=SendTransId; + IPXsendPacket(SendECB); + writeln('Packet#',SendTransID,' was sent.'); + while sendECB.InuseFlag<>0 + do IPXrelinquishControl; + + + IPXGetIntervalMarker(ticks); + retries:=0; + REPEAT + IPXrelinquishcontrol; + IPXGetIntervalMarker(ticks2); + if (ticks2-ticks)>2 + then begin + inc(retries); + writeln('Timeout: resending pkt#',SendTransID); + PEPsetupSendECB(NIL,IOsocket,dest,@SendDataBuffer[1],512, + SendPepHdr,SendECB); + SendPepHdr.ClientType:=$EA; + SendPepHdr.TransactionId:=SendTransId; + IPXsendPacket(SendECB); + while sendECB.InuseFlag<>0 + do IPXrelinquishControl; + IPXGetIntervalMarker(ticks); + end; + CheckError(retries>50,$0204); + CheckError(Keypressed,$0205); + UNTIL AckReceived; + writeln('Ack ',ListenPepHdr.TransactionID,' was received.'); + + FileSize:=FileSize-512; + inc(SendTransID); + {XXX fill buffer met volgende fileblock } + + + IPXListenForPacket(ListenECB); { start listening for acks again } + end; + +IPXcloseSocket(IOsocket); +close(f); + +end. \ No newline at end of file diff --git a/NWTP/XIPX/M_PEP.PAS b/NWTP/XIPX/M_PEP.PAS new file mode 100644 index 0000000..eb7a481 --- /dev/null +++ b/NWTP/XIPX/M_PEP.PAS @@ -0,0 +1,176 @@ +{$X+,V-,B-} +program M_PEP; { Master / Sender } + +{ Testprogram for the nwPEP unit / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +{ Sents a single packet, waits for acknowledgement } + +uses crt,nwMisc,nwBindry,nwConn,nwIPX,nwPEP; + +CONST IOSocket=$5678; { socket to transmit/receive on } + +Var ListenECB :Tecb; { ECB and header, to listen for acknowledgement } + ListenPepHdr :TpepHeader; + + SendECB :Tecb; { ECB and header, used to send the data } + SendPepHdr :TpepHeader; + + socket :word; + + buf :array[1..546] of byte; { buffer for data to be sent } + + AckReceived :boolean; { set to true within the ListenForAckESR } + + SendTransId :LongInt; { transactionID. This uniquely identifies + the packet. The slave/receiver has to + reply with the same transactionID in the + header of the acknowledgement. Only if + this number is the same as the transactioID + of the sent packet, the pavket is considered + successfully delivered. } + + NewStack:array[1..1024] of word; { !! used by ESR } + StackBottom:word; { !! used by ESR } + + +{$F+} +Procedure ListenForAckHandler(Var p:TPecb); + { Interrupts are turned off -and should remain turned off- } +begin +IF (ListenECB.CompletionCode<>0) { packet must be suucessfully received.. } + or (ListenPepHdr.IPXhdr.packetType<>PEP_PACKET_TYPE) { of type PEP.. } + or (ListenPepHdr.ClientType<>$EA) { of client type $EA } + or (ListenPepHdr.TransactionID<>SendTransId) { with a correct clientID (of the packet the master sent) } + then IPXListenForPacket(ListenECB) { Invalid packet => listen again } + else AckReceived:=true; { valid packet => ACK received ! } +end; +{$F-} + +{$F+} +Procedure ListenForAckESR; assembler; +asm { ES:SI are the only valid registers when entering this procedure ! } + { interrupts are turned off -and should remain turned off- } + mov dx, seg stackbottom + mov ds, dx + + mov dx,ss { setup of a new local stack } + mov bx,sp { ss:sp copied to dx:bx} + mov ax,ds + mov ss,ax + mov sp,offset stackbottom + push dx { push old ss:sp on new stack } + push bx + + push es { push es:si on stack as local vars } + push si + mov di,sp + + push ss { push address of local ptr on stack } + push di + CALL ListenForAckHandler + + add sp,4 { skip stack ptr-copy } + pop bx { restore ss:sp from new stack } + pop dx + mov sp,bx + mov ss,dx +end; +{$F-} + + +Var dest:TinternetworkAddress; + ticks,ticks2:word; + retries :word; + + Uname:string; + NbrOfConn:byte; + connList:TconnectionList; + +begin +If paramcount<>1 + then begin + writeln('Usage: M_PEP '); + writeln('-a test pep packet will be sent to the workstation of this user.'); + writeln('-run S_PEP on that workstation to receive the packet.'); + halt(1); + end; +Uname:=ParamStr(1); +UpString(Uname); +NbrOfConn:=0; +IF (NOT GetObjectConnectionNumbers(Uname,OT_USER,NbrOfConn,connList)) + or (NbrOfConn=0) + then begin + writeln('Error: can''t locate user ',Uname); + halt(1); + end; +IF NbrOfConn>1 + then begin + writeln('The specified user has multiple connections.'); + writeln('This demonstation program doesn''t support multiple connections.'); + halt(1); + end; +IF NOT GetInternetAddress(connList[1],dest) + then begin + writeln('Error: can''t find the address of user ',Uname); + halt(1); + end; + + +IF NOT IpxInitialize + then begin + writeln('Ipx needs to be installed.'); + halt(1); + end; +socket:=IOSocket; +IF NOT IPXopenSocket(Socket,SHORT_LIVED_SOCKET) + then begin + writeln('IPXopenSocket returned error# ',nwIPX.result); + halt(1); + end; + + +{ setup listening for ack } +AckReceived:=False; +FillChar(buf,546,#0); +{ Setup ECB and IPX header } +PEPsetupListenECB(Addr(ListenForAckESR),IOsocket,@buf,546, + ListenPepHdr,ListenECB); +IPXListenForPacket(ListenECB); + +{ send packet } + +dest.socket:=IOsocket; +buf[1]:=ord('s');buf[2]:=ord('m'); +PEPsetupSendECB(NIL,IOsocket,dest,@buf[1],2, + SendPepHdr,SendECB); +SendTransID:=1; +SendPepHdr.TransactionId:=SendTransId; +SendPepHdr.ClientType:=$EA; +IPXsendPacket(SendECB); +writeln('Packet was sent.'); +while sendECB.InuseFlag<>0 do IPXrelinquishControl; +IPXGetIntervalMarker(ticks); + +{ wait for acknowledgement or timeout } +retries:=0; + +REPEAT + IPXrelinquishcontrol; + IPXGetIntervalMarker(ticks2); + if (ticks2-ticks)>4 + then begin + inc(retries); + writeln('Timeout: resending'); + IPXsendPacket(SendECB); + while sendECB.InuseFlag<>0 do IPXrelinquishControl; + IPXGetIntervalMarker(ticks); + end; +UNTIL AckReceived or Keypressed or (retries>50); + +if AckReceived + then writeln('Ack was received.'); + +IF NOT IPXcloseSocket(IOsocket) + then writeln('IPXcloseSocket returned error# ',nwIPX.result); + +end. \ No newline at end of file diff --git a/NWTP/XIPX/NWPEP.PAS b/NWTP/XIPX/NWPEP.PAS new file mode 100644 index 0000000..e25b810 --- /dev/null +++ b/NWTP/XIPX/NWPEP.PAS @@ -0,0 +1,97 @@ +{$B-,V-,X+} +UNIT nwPEP; + +{ nwPEP unit as of 950301 / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +INTERFACE + +Uses Dos,nwIPX,nwMisc; + +{ Primary IPX calls: Subf: Comments: + + Secondary calls: + + PEPsetupSendECB + PEPsetupListenECB + +} + +Var Result:word; { unit errorcode variable } + +Type TpepHeader=Record + IPXhdr :TipxHeader; { set packettype to $04 } + TransactionID:Longint; + clientType :word; + end; + +Procedure PEPSetupListenECB(ESRptr:Pointer; ReceiveSocket:word; + BufPtr:Pointer; BufSize:word; + {out:} Var PepHdr:TpepHeader; Var ecb:Tecb); +{ Clears IPXheader and ECB, sets values of the required fields within + the ecb and IPX header. } + +Procedure PEPSetupSendECB(ESRptr:pointer; SourceSocket:word; + DestAddr:TinterNetworkAddress; + BufPtr:pointer; BufSize:word; + {out:} Var PepHdr:TpepHeader; Var ecb:Tecb); +{ Clears IPXheader and ECB, sets values of the required fields within + the ecb and IPX header. } + +IMPLEMENTATION {==============================================================} + +Procedure PEPSetupListenECB(ESRptr:Pointer;ReceiveSocket:word; + BufPtr:Pointer;BufSize:word; + {out:} Var PepHdr:TpepHeader; Var ecb:Tecb); +{ Clears IPXheader and ECB, sets values of the required fields within + the ecb and IPX header. } +{ ECB: ESR adress field, socket number, fragment count, frag.descriptor fields } +begin +FillChar(ecb,SizeOf(Tecb),#0); +FillChar(pepHdr,SizeOF(TpepHeader),#0); +WITH ECB + do begin + if ESRptr<>NIL + then ESRaddress:=ESRptr; + Fragmentcount:=2; + socketNumber:=swap(ReceiveSocket); {hi-lo} + + Fragment[1].Address:=@pepHdr; + Fragment[2].Address:=BufPtr; + Fragment[1].size:=SizeOf(Tpepheader); + Fragment[2].size:=BufSize; + end; +end; + +Procedure PEPsetupSendECB(ESRptr:pointer; SourceSocket:word; + DestAddr:TinterNetworkAddress; + BufPtr:pointer; BufSize:word; + {out:} Var PepHdr:TpepHeader; Var ecb:Tecb); +{ Clears IPXheader and ECB, sets values of the required fields within + the ecb and IPX header. } +Var ImmAddr:TnodeAddress; + Ticks:word; +begin +fillchar(pepHdr,SizeOf(TpepHeader),#0); +with pepHdr.IPXhdr + do begin + PacketType:=PEP_PACKET_TYPE; + Move(DestAddr,Destination,10); + destination.socket:=swap(DestAddr.socket); {hi-lo} + end; +IPXGetLocalTarget(DestAddr,ImmAddr,Ticks); +fillchar(ecb,sizeOf(ecb),#0); +With ecb + do begin + if ESRptr<>NIL + then ESRaddress:=ESRptr; + socketNumber:=swap(SourceSocket); {hi-lo} + Move(ImmAddr,ImmediateAddress,6); + FragmentCount:=2; + fragment[1].Address:=@pephdr; + fragment[1].size:=SizeOf(TpepHeader); + fragment[2].Address:=BufPtr; + fragment[2].size:=BufSize; + end; +end; + +end. \ No newline at end of file diff --git a/NWTP/XIPX/NWRIP.PAS b/NWTP/XIPX/NWRIP.PAS new file mode 100644 index 0000000..3c69215 --- /dev/null +++ b/NWTP/XIPX/NWRIP.PAS @@ -0,0 +1,152 @@ +Unit NwRIP; + +{ NwTP Version 0.6, Copyright 1993,1994 R. Spronk + + WARNING: Test your program thoroughly if you're using nwRip functions. + ---------------------------------------------------------------------- + + Using the RIP functionality the wrong way may very well result in + aborting servers ! (no kidding.) + + As far as I know the diagnostic RIP function(s) included in this unit + are perfectly safe to use. + + Based on: + + -GETALL, written in C by Barry Lagerweij of 2:2802/110.2 + posted in the Fido NOVELL area on Tue 7 Sep 93 0:36. + -GETRIP, written in C by Koos van den Hout of 2:500/101.11012 + last updated 8 Feb 93 } + +INTERFACE + +Uses nwMisc,nwIPX; + +CONST ECB_COUNT=10; +{ assumption : 10 receiveECBs are used, which means a max of 500 networks } + +{ Type definitions for RIP request/response structures} +type TRIPentry=record + network:TnetworkAddress; + hops :word; + ticks :word; + end; + + TRIPanswerPacket=record + operation:word; + entry :array[1..50] of TRIPEntry; + end; + + TRIPinfo=array[1..50*ECB_COUNT] of record + address:TnetworkAddress; + hops :byte; + Ticks :word; + end; + +Function GetAllNetworks(SegmentNetworkAddress:TnetworkAddress; + Var NetInfo:TRIPinfo):word; +{SegmentNetworkAddress: The target network whose routers + will be queried. Set to all zeroes (00 00 00 00) + if querying your own segment. + NetInfo : the buffer where the network-info is stored. + Returns : the number of known networks. + + Assumed is that IPXInitialize is already called. } + +IMPLEMENTATION {===========================================================} + +Function GetAllNetworks(SegmentNetworkAddress:TnetworkAddress; + Var NetInfo:TRIPinfo):word; +Var + RIPrequest :TRIPanswerPacket; + RIPanswer :array[1..ECB_COUNT] of TRIPanswerPacket; + RequestEcb :Tecb; + RequestIPXheader:TipxHeader; + ReplyECB :array[1..ECB_COUNT] of Tecb; + ReplyIPX :array[1..ECB_COUNT] of TipxHeader; + Target :TinternetworkAddress; + Sourcesocket :word; + RIPsocket :word; + + NumberOfNets :word; + cnt :word; + n :word; + RoutableNetworks:word; + timer1,timer2:word; + +Begin +RIPsocket:=$0453; + +SourceSocket:=0; +{ open socket for receiving the RIP packets } +IF NOT IPXopenSocket(SourceSocket,SHORT_LIVED_SOCKET) + then begin + result:=nwIPX.result; + GetAllnetworks:=0; + exit; + end; + +{set-up sendpacket } +target.net:=SegMentNetworkAddress; +fillchar(target.node,6,#$FF); { all nodes i.e. all routers } +target.socket:=RIPsocket; +IPXsetUpSendECB(NIL,SourceSocket,target,@RIPrequest,SizeOf(RIPrequest), + RequestIPXheader, RequestECB); +RequestIPXheader.packetType := 1; + { 1=RIP / Any value will work/ type seems to be ignored by Routers + as long as socket is OK. } + +{ set-up the RIP request } +FillChar(RIPrequest,SizeOf(RIPrequest),#$FF); +RIPrequest.operation := $0100; { hi-lo, RIP request } +FillChar(RIPanswer,SizeOf(RIPanswer),#$00); + + +{ set-up the receive ECBs } +for n:=1 to ECB_COUNT + do begin + IPXsetupListenECB(NIL,SourceSocket, + @RIPanswer[n],SizeOf(TRIPanswerPacket), + ReplyIpx[n],ReplyECB[n]); + IPXListenForPacket(ReplyECB[n]); + end; + +{ send the RIP request } +IPXSendPacket(RequestECB); + +{ wait a while for answers } +IPXgetIntervalMarker(timer1); +REPEAT + IPXrelinquishcontrol; + IPXGetIntervalMarker(timer2); +UNTIL abs(timer2-timer1)>20; + +NumberOfNets := 0; + +{ check all possible RIP responses, and fill the tables } +for n:=1 to ECB_COUNT + do if (ReplyECB[n].INuseFlag=0) and (RIPanswer[n].operation=$0200) + then begin + RoutableNetworks:=(swap(ReplyIPX[n].Length)-32) DIV SizeOf(TRIPentry); + for cnt:=1 to RoutableNetworks + do begin + inc(NumberOfNets); + With NetInfo[NumberOfNets] + do begin + Address:=RIPanswer[n].entry[cnt].network; + hops:=swap(RIPanswer[n].entry[cnt].hops); + ticks:=swap(RIPanswer[n].Entry[cnt].ticks); + end; + end; + end + else IPXcancelEvent(ReplyECB[n]); + +{ our socket is no longer needed } +IPXCloseSocket(SourceSocket); + +{ return the number of networks we found } +GetAllNetworks:=NumberOfNets; +end; + + +end. diff --git a/NWTP/XIPX/NWSAP.PAS b/NWTP/XIPX/NWSAP.PAS new file mode 100644 index 0000000..a6c322b --- /dev/null +++ b/NWTP/XIPX/NWSAP.PAS @@ -0,0 +1,50 @@ +Unit NwSAP; + +{ NwTP Version 0.6, Copyright 1993,1995 R. Spronk + + WARNING: Test your program thoroughly if you're using nwSAP functions. + ---------------------------------------------------------------------- + + Using the SAP functionality the wrong way may very well result in + aborting servers ! (no kidding.) + + NOTE: Type Declarations only. See SHWSAPS.PAS for an example } + + +INTERFACE + +Uses nwMisc,nwIPX; + +CONST { server SAP priodic broadcast type } + PERIODIC_ID_PACKET = $0002; + { server SAP (reply) packet types } + GENERAL_SERVICE_RESPONSE=$0002; + NEAREST_SERVICE_RESPONSE=$0004; + { client SAP (request) packet types } + GENERAL_SERVICE_QUERY =$0001; + NEAREST_SERVICE_QUERY =$0003; + +{ Type definitions for SAP request/response structures} +Type TSAPserver=record + ObjType:word; + Name :array[1..48] of byte; { asciiz } + Address:TinternetworkAddress; + Hops :word; + end; + + TSAPresponse=record + ResponseType:word; { 0002 General server; 0004 nearest server } + ServerEntry:array[1..7] of TSAPserver; + end; + + TSAPrequest=record + RequestType:word; {hi-lo} + ServerType :word; {hi-lo} + end; + +IMPLEMENTATION {===========================================================} + + + + +end. \ No newline at end of file diff --git a/NWTP/XIPX/R1_HELLO.PAS b/NWTP/XIPX/R1_HELLO.PAS new file mode 100644 index 0000000..ae26094 --- /dev/null +++ b/NWTP/XIPX/R1_HELLO.PAS @@ -0,0 +1,136 @@ +{$X+,V-,B-} +program RecHello1; + +{ Simple IPX demonstration program, that uses one receive ESR. + + Run this program on 1 workstation, run S_HELLO or S1_HELLO on another. + S_HELLO will send "hello world" messages, + this workstation will receive them. } + +uses crt,nwMisc,nwIPX; + +CONST IOSocket=$5678; + +Var ReceiveEcb :Tecb; + IpxHdr :TipxHeader; + socket :word; + buf :array[1..546] of byte; + t :byte; + ReceivedBufLen:word; + PacketReceived:boolean; + + NewStack:array[1..1024] of word; { !! used by ESR } + StackBottom:word; { !! used by ESR } + + +{$F+} +Procedure ListenESRhandler; +begin +PacketReceived:=true; +end; +{$F-} + +{$F+} +Procedure ListenESR; assembler; +asm { ES:SI are the only valid registers when entering this procedure ! } + mov dx, seg stackbottom + mov ds, dx + + mov dx,ss { setup of a new local stack } + mov bx,sp { ss:sp copied to dx:bx} + mov ax,ds + mov ss,ax + mov sp,offset stackbottom + push dx + push bx + + CALL ListenEsrHandler + + pop bx + pop dx + mov sp,bx { restore stack } + mov ss,dx +end; +{$F-} + + +begin +IF NOT IpxInitialize + then begin + writeln('Ipx needs to be installed.'); + halt(1); + end; +socket:=IOSocket; +IF NOT IPXopenSocket(Socket,SHORT_LIVED_SOCKET) + then begin + writeln('IPXopenSocket returned error# ',nwIPX.result); + halt(1); + end; + +Repeat + PacketReceived:=False; + { Empty receive buffer (ReceiveEcb.fragment[2].address^) } + FillChar(buf,546,#0); + + { Setup ECB and IPX header } + IPXsetupListenECB(Addr(ListenESR),IOsocket,@buf,546, + IpxHdr,ReceiveEcb); + + IPXListenForPacket(ReceiveECB); + + REPEAT + + delay(1000); + Writeln('Packet received: ',PacketReceived); + + IF PacketReceived { ESR has signalled that a packet has been received } + then begin + Case ReceiveECB.CompletionCode OF + $00:begin { Dump received bytes.. } + Write('Data received : '); + ReceivedBufLen:=swap(IpxHdr.length)-SizeOf(TipxHeader); + for t:=1 to ReceivedBufLen + do write(chr(buf[t])); + writeln; + end; + $FC:Writeln('The listen request has been canceled.'); + { impossible, as the cancelation has to be done by this program, and it doesn't } + $FD:Writeln('Packet overflow error.'); + { 0 fragments, or receiving buffer too small. } + $FF:Writeln('The socket is closed.'); + { Impossible. The socket is definitely open. See above. } + end; + + { Now we're going to squeeze all information out of the IpxHdr } + writeln('*IPX header info*'); + writeln('-total length : ',swap(IpxHdr.length):0); + writeln('-data length : ',swap(IpxHdr.Length)-SizeOf(TipxHeader)); + writeln('-number of hops: ',(IpxHdr.TransportControl AND $0F):0); + write('-sending adress: ['); + for t:=1 to 4 + do write(HexStr(IpxHdr.source.net[t],2)); + write('|'); + for t:=1 to 6 + do write(HexStr(IpxHdr.source.node[t],2)); + write('|'); + writeln(HexStr(swap(IpxHdr.source.socket),4),']'); + write('-destined for : ['); + for t:=1 to 4 + do write(HexStr(IpxHdr.destination.net[t],2)); + write('|'); + for t:=1 to 6 + do write(HexStr(IpxHdr.destination.node[t],2)); + write('|'); + writeln(HexStr(swap(IpxHdr.destination.socket),4),']'); + + writeln; + end; + + UNTIL (KeyPressed or PacketReceived); + +UNTIL keypressed; + +IF NOT IPXcloseSocket(IOsocket) + then writeln('IPXcloseSocket returned error# ',nwIPX.result); + +end. \ No newline at end of file diff --git a/NWTP/XIPX/R2_HELLO.PAS b/NWTP/XIPX/R2_HELLO.PAS new file mode 100644 index 0000000..851daae --- /dev/null +++ b/NWTP/XIPX/R2_HELLO.PAS @@ -0,0 +1,107 @@ +{$X+,V-,B-} +program RecHello2; + +{ Simple IPX demonstration program, that uses one receive ESR. + + Run this program on 1 workstation, run S_HELLO or S1_HELLO on another. + S_HELLO will send "hello world" messages, + this workstation will receive them. } + +uses crt,nwMisc,nwIPX; + +CONST IOSocket=$5678; + +Var ReceiveEcb :Tecb; + IpxHdr :TipxHeader; + socket :word; + buf :array[1..546] of byte; + t :byte; + ReceivedBufLen:word; + PacketReceived:boolean; + + RecString :string; + + NewStack:array[1..1024] of word; { !! used by ESR } + StackBottom:word; { !! used by ESR } + + +{$F+} +Procedure ListenESRhandler(Var p:Tpecb); +begin +RecString[0]:=chr(p^.fragment[2].size); +move(p^.fragment[2].address^,RecString[1],byte(RecString[0])); +PacketReceived:=true; +IPXListenForPacket(ReceiveECB); +end; +{$F-} + +{$F+} +Procedure ListenESR; assembler; +asm { ES:SI are the only valid registers when entering this procedure ! } + mov dx, seg stackbottom + mov ds, dx + + mov dx,ss { setup of a new local stack } + mov bx,sp { ss:sp copied to dx:bx} + mov ax,ds + mov ss,ax + mov sp,offset stackbottom + push dx { push old ss:sp on new stack } + push bx + + push es { push es:si on stack as local vars } + push si + mov di,sp + + push ss { push address of local ptr on stack } + push di + CALL ListenEsrHandler + + add sp,4 { skip stack ptr-copy } + pop bx { restore ss:sp from new stack } + pop dx + mov sp,bx + mov ss,dx +end; +{$F-} + + +begin +IF NOT IpxInitialize + then begin + writeln('Ipx needs to be installed.'); + halt(1); + end; +socket:=IOSocket; +IF NOT IPXopenSocket(Socket,SHORT_LIVED_SOCKET) + then begin + writeln('IPXopenSocket returned error# ',nwIPX.result); + halt(1); + end; + +PacketReceived:=False; +{ Empty receive buffer (ReceiveEcb.fragment[2].address^) } +FillChar(buf,546,#0); + +{ Setup ECB and IPX header } +IPXsetupListenECB(Addr(ListenESR),IOsocket,@buf,546, + IpxHdr,ReceiveEcb); + +IPXListenForPacket(ReceiveECB); + +REPEAT + +IPXrelinquishControl; + +IF PacketReceived { ESR has signalled that a packet has been received } + then begin + writeln(RecString); + PacketReceived:=false; + end; + +UNTIL KeyPressed; + +IF NOT IPXcloseSocket(IOsocket) + then writeln('IPXcloseSocket returned error# ',nwIPX.result); + +end. \ No newline at end of file diff --git a/NWTP/XIPX/R3_HELLO.PAS b/NWTP/XIPX/R3_HELLO.PAS new file mode 100644 index 0000000..974ebe2 --- /dev/null +++ b/NWTP/XIPX/R3_HELLO.PAS @@ -0,0 +1,146 @@ +{$X+,V-,B-} +program RecHello3; + +{ Simple IPX demonstration program, that uses one receive ESR. + + Run this program on 1 workstation, run S_HELLO or S1_HELLO on another. + S_HELLO will send "hello world" messages, + this workstation will receive them. } + +{ This program consists of two concurrent processes: + + 1. (background) An ESR that fills a global receive buffer with + incoming packets. Only when the buffer is full + will packets be discarded. + + 2. (foreground) A process that performs its regular task. Whenever + there is time, the process will process the + received packets. } + +uses crt,nwMisc,nwIPX; + +CONST IOSocket=$5678; + +Var ReceiveEcb :Tecb; + IpxHdr :TipxHeader; + socket :word; + buf :array[1..546] of byte; + t :byte; + ReceivedBufLen:word; + + ReceivedMsg:array[1..100] of record + InUse:Boolean; + Message:string[25]; + end; + + EsrBufInd :Byte; { used by ESR } + + NewStack:array[1..1024] of word; { !! used by ESR } + StackBottom:word; { !! used by ESR } + + +{$F+} +Procedure ListenESRhandler(Var p:Tpecb); +begin +{ look for an empty spot in the global buffer } +EsrBufInd:=1; +while (EsrBufInd<=100) and ReceivedMsg[EsrBufInd].Inuse + do inc(EsrBufInd); +IF EsrBufInd<=100 + then begin + { empty place found. Insert msg } + with ReceivedMsg[EsrBufInd] + do begin + Message[0]:=chr(p^.fragment[2].size); + if Message[0]>#25 then Message[0]:=#25; + move(p^.fragment[2].address^,Message[1],ord(Message[0])); + InUse:=True; + end; + end + else ; { entire buffer is filled => discard packet } +{ Setup to listen for next incoming packet } +IPXListenForPacket(ReceiveECB); +end; +{$F-} + +{$F+} +Procedure ListenESR; assembler; +asm { ES:SI are the only valid registers when entering this procedure ! } + mov dx, seg stackbottom + mov ds, dx + + mov dx,ss { setup of a new local stack } + mov bx,sp { ss:sp copied to dx:bx} + mov ax,ds + mov ss,ax + mov sp,offset stackbottom + push dx { push old ss:sp on new stack } + push bx + + push es { push es:si on stack as local vars } + push si + mov di,sp + + push ss { push address of local ptr on stack } + push di + CALL ListenEsrHandler + + add sp,4 { skip stack ptr-copy } + pop bx { restore ss:sp from new stack } + pop dx + mov sp,bx + mov ss,dx +end; +{$F-} + + +begin +IF NOT IpxInitialize + then begin + writeln('Ipx needs to be installed.'); + halt(1); + end; +socket:=IOSocket; +IF NOT IPXopenSocket(Socket,SHORT_LIVED_SOCKET) + then begin + writeln('IPXopenSocket returned error# ',nwIPX.result); + halt(1); + end; + +FillChar(buf,546,#0); + +{ Setup ECB and IPX header } +IPXsetupListenECB(Addr(ListenESR),IOsocket,@buf,546, + IpxHdr,ReceiveEcb); + +IPXListenForPacket(ReceiveECB); + +writeln('ESR will start filling a global buffer with packets received.'); +writeln('Starting foreground process...'); +writeln; +writeln('Foreground process just writes a ''dot'' to the screen every second.'); +writeln('When a key is pressed, this process is terminated and the received'); +writeln('packets are shown.'); + +REPEAT +IPXrelinquishControl; +delay(1000); +write('.'); +UNTIL KeyPressed; + +writeln; +writeln('Dumping global receive buffer -- filled by background process.'); + +for t:=1 to 100 + do if ReceivedMsg[t].Inuse + then begin + writeln(ReceivedMsg[t].Message); + ReceivedMsg[t].Inuse:=False; { give entry in buffer free } + end; +{ You may also choose to process just 1 entry in the received buffer. + Set Inuse to False after processing, so the ESR can fill it again } + +IF NOT IPXcloseSocket(IOsocket) + then writeln('IPXcloseSocket returned error# ',nwIPX.result); + +end. \ No newline at end of file diff --git a/NWTP/XIPX/R_HELLO.PAS b/NWTP/XIPX/R_HELLO.PAS new file mode 100644 index 0000000..5982c44 --- /dev/null +++ b/NWTP/XIPX/R_HELLO.PAS @@ -0,0 +1,92 @@ +{$X+,V-,B-} +program RecHello; + +{ Simple IPX demonstration program. Run this program on 1 workstation, + run S_HELLO on another. S_HELLO will send "hello world" messages, + this workstation will receive them. + + Polls the ECB until a packet is received. No ESR used. } + +uses crt,nwMisc,nwIPX; + +CONST IOSocket=$5678; + +Var ReceiveEcb:Tecb; + IpxHdr:TipxHeader; + socket:word; + buf:array[1..546] of byte; + t:byte; + w:word; + s:string; + ReceivedBufLen:word; +begin +IF NOT IpxInitialize + then begin + writeln('Ipx needs to be installed.'); + halt(1); + end; +socket:=IOSocket; +IF NOT IPXopenSocket(Socket,SHORT_LIVED_SOCKET) + then begin + writeln('IPXopenSocket returned error# ',nwIPX.result); + halt(1); + end; + +Repeat + { Empty receive buffer (ReceiveEcb.fragment[2].address^) } + FillChar(buf,546,#0); + + { Setup ECB and IPX header } + IPXsetupListenECB(NIL,IOsocket,@buf,546, + IpxHdr,ReceiveEcb); + IPXListenForPacket(ReceiveECB); + + { Poll InUse flag until a packet was received } + while ReceiveECB.InUseFlag<>0 + do IPXrelinquishControl; + + Case ReceiveECB.CompletionCode OF + $00:begin { Dump received bytes.. } + Write('Data received : '); + ReceivedBufLen:=swap(IpxHdr.length)-SizeOf(TipxHeader); + for t:=1 to ReceivedBufLen + do write(chr(buf[t])); + writeln; + end; + $FC:Writeln('The listen request has been canceled.'); + { impossible, as the cancelation has to be done by this program, and it doesn't } + $FD:Writeln('Packet overflow error.'); + { 0 fragments, or receiving buffer too small. } + $FF:Writeln('The socket is closed.'); + { Impossible. The socket is definitely open. See above. } + end; + + { Now we're going to squeeze all information out of the IpxHdr } + writeln('*IPX header info*'); + writeln('-total length : ',swap(IpxHdr.length):0); + writeln('-data length : ',swap(IpxHdr.Length)-SizeOf(TipxHeader)); + writeln('-number of hops: ',(IpxHdr.TransportControl AND $0F):0); + write('-sending adress: ['); + for t:=1 to 4 + do write(HexStr(IpxHdr.source.net[t],2)); + write('|'); + for t:=1 to 6 + do write(HexStr(IpxHdr.source.node[t],2)); + write('|'); + writeln(HexStr(swap(IpxHdr.source.socket),4),']'); + write('-destined for : ['); + for t:=1 to 4 + do write(HexStr(IpxHdr.destination.net[t],2)); + write('|'); + for t:=1 to 6 + do write(HexStr(IpxHdr.destination.node[t],2)); + write('|'); + writeln(HexStr(swap(IpxHdr.destination.socket),4),']'); + + writeln; +UNTIL keypressed; + +IF NOT IPXcloseSocket(IOsocket) + then writeln('IPXcloseSocket returned error# ',nwIPX.result); + +end. \ No newline at end of file diff --git a/NWTP/XIPX/S1_HELLO.PAS b/NWTP/XIPX/S1_HELLO.PAS new file mode 100644 index 0000000..eeb1c08 --- /dev/null +++ b/NWTP/XIPX/S1_HELLO.PAS @@ -0,0 +1,114 @@ +{$X+,B-,V-} +program SendHello; + +{ Simple IPX demonstration program with 1 ESR.} + +uses crt,nwMisc,nwIPX; + +CONST IOSocket=$5678; + +Var NewStack :array[1..512] of word; { !! used by ESR } + StackBottom:word; { !! used by ESR } + + SendEcb :Tecb; + IpxHdr :TipxHeader; + socket :word; + dest :TinternetworkAddress; + buf :array[1..546] of byte; + t :byte; + w :word; + s :string; + PacketSent :boolean; + +{$F+} +Procedure SendESRhandler; +begin +PacketSent:=true; +end; +{$F-} + +{$F+} +Procedure SendESR; assembler; +asm { ES:SI are the only valid registers when entering this procedure ! } + mov dx, seg stackbottom + mov ds, dx + + mov dx,ss { setup of a new local stack } + mov bx,sp { ss:sp copied to dx:bx} + mov ax,ds + mov ss,ax + mov sp,offset stackbottom + push bx + push dx + + CALL SendEsrHandler + + pop dx + pop bx + mov sp,bx { restore stack } + mov ss,dx +end; +{$F-} + +begin +IF NOT IpxInitialize + then begin + writeln('Ipx needs to be installed.'); + halt(1); + end; +socket:=IOSocket; +IF NOT IPXopenSocket(Socket,SHORT_LIVED_SOCKET) + then begin + writeln('IPXopenSocket returned error# ',nwIPX.result); + halt(1); + end; + +for t:=1 to 4 do dest.net[t]:=$00; { this net / segment } +for t:=1 to 6 do dest.node[t]:=$FF; { all nodes } +dest.socket:=IOsocket; +w:=0; + +Repeat + inc (w); + + { Fill buffer (ECB.fragment[2]^) } + str(w:4,s); + s:=s+' IPX: Hello World'; + FillChar(buf,546,#0); + move(s[1],buf,ord(s[0])); + + { setup ECB and IPX header } + PacketSent:=False; + IPXsetupSendECB(Addr(SendESR),IOsocket,dest,@buf,ord(s[0]), + IpxHdr,SendEcb); + IPXsendPacket(SendEcb); + + REPEAT + + IpxRelinquishControl; + delay(100); + + IF PacketSent + then begin + { ECB.InUseFlag was lowered, now determine if packet was sent: } + CASE SendEcb.CompletionCode OF + $00:writeln('IPX packet #',w:0,' was sent.'); + $FC:writeln('The send of packet #',w:0,' was canceled.'); + { impossible, as this cancelation to be done by THIS program, and it doesn't } + $FD:writeln('Packet# ',w:0,' is malformed and was not sent.'); + { illegal param: packet length, number of fragments, fragment size. } + $FE:writeln('Packet# ',w:0,' was undelivered. No stations listening.'); + $FF:writeln('Packet# ',w:0,' not sent due to a hardware error.'); + end; + end; + + UNTIL PacketSent or Keypressed; + + delay(750); { delay 0.75 sec before sending another packet } + +UNTIL keypressed; + +IF NOT IPXcloseSocket(IOsocket) +then writeln('IPXcloseSocket returned error# ',nwIPX.result); + +end. \ No newline at end of file diff --git a/NWTP/XIPX/S1_PEP.PAS b/NWTP/XIPX/S1_PEP.PAS new file mode 100644 index 0000000..d75011b --- /dev/null +++ b/NWTP/XIPX/S1_PEP.PAS @@ -0,0 +1,170 @@ +{$X+,V-,B-} +program S1_PEP; { Listening Process / receiver / Slave } + +{ Testprogram for the nwPEP unit / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +uses crt,nwMisc,nwIPX,nwPEP; + +Var ListenECB :Tecb; { used to listen for packets } + ListenPepHdr :TpepHeader; + + SendECB :Tecb; { used to send acknowledgements } + SendPepHdr :TpepHeader; + + IOsocket :word; + DataBuffer :array[1..546] of byte; + SendDataBuffer:byte; + + PacketReceived :Boolean; + LastTransactionID:LongInt; + BytesReceived :Word; + + NewStack:array[1..8192] of word; { !! used by ESR } + StackBottom:word; { !! used by ESR } + + lnr:word; + +Procedure CheckError(err:boolean; errNbr:word); +begin +if err + then begin + CASE errNbr of + $0100:writeln('IPX needs to be installed.'); + $0101:writeln('ERROR: Connection not established. A Timeout occured'); + $0102:writeln('ERROR: The transfer is aborted; A timeout occured.'); + $10FE:writeln('Error opening socket: Socket Table Is Full.'); + $10FF:writeln('Error opening socket: Socket is already open.'); + else writeln('Unspecified error.'); + end; {case} + IPXcloseSocket(IOsocket); + halt(1); + end; +end; + +Function TimeOut(t1,t2:word;n:byte):boolean; +{ ticks t2 - ticks t1 > n seconds ? } +Var lt1,lt2:LongInt; +begin +lt2:=t2; +if t1>t2 then lt2:=lt2+$FFFF; +TimeOut:=(lt2-t1)>(n*18); +end; + +{$F+} +Procedure ListenAndAckHandler; +begin +lnr:=ListenPepHdr.TransactionID; + +If (ListenECB.CompletionCode<>0) + or (ListenPepHdr.IPXhdr.packetType<>PEP_PACKET_TYPE) + or (ListenPepHdr.clienttype<>$EA) + or (ListenPepHdr.TransactionIDLastTransactionID); { new packet received } + + { Acknowledge new packets and duplicates of the latest packet, } + { as the original acknowledgement may have been lost. } + BytesReceived:=swap(ListenPepHdr.IPXhdr.length)-SizeOf(TpepHeader); + LastTransactionID:=ListenPepHdr.TransactionID; + + { Setup acknowledgement ECB and PEPheader, and send it. } + if 1=1 {SendECB.InUseFlag=0} + then begin + ListenPepHdr.IPXhdr.source.socket:=swap(ListenPepHdr.IPXhdr.source.socket); + { socket is hi-lo in IPX/PEPHeaders. SetupSendECB expects lo-hi } + PEPsetupSendECB(NIL,IOsocket,ListenPepHdr.IPXhdr.source,@SendDataBuffer,0, + SendPepHdr,SendECB); + SendPepHdr.TransactionId:=LastTransactionID; + SendPepHdr.ClientType:=$EA; + IPXsendPacket(SendECB); + end; + end; +end; +{$F-} + +{$F+} +Procedure ListenAndAckESR; assembler; +asm + mov dx, seg stackbottom + mov ds, dx + + mov dx,ss { setup of a new local stack } + mov bx,sp { ss:sp copied to dx:bx} + mov ax,ds + mov ss,ax + mov sp,offset stackbottom + push dx { push old ss:sp on new stack } + push bx + CALL ListenAndAckHandler + pop bx { restore ss:sp from new stack } + pop dx + mov sp,bx + mov ss,dx +end; +{$F-} + +Var ticks,ticks2 :word; + FileName:string; + FileSize:LongInt; + +begin +lnr:=0; + +IpxInitialize; +CheckError(nwIPX.result>0,$100); + +IOSocket:=$5678; +IPXopenSocket(IOsocket,SHORT_LIVED_SOCKET); +CheckError(nwIPX.result>0,$1000+nwIPX.result); + +{ Setup of ECB and PepHeader, start listening for incoming packets. } +LastTransactionID:=0; +PacketReceived:=False; +PEPSetupListenECB(Addr(ListenAndAckESR),IOsocket,@DataBuffer,546, + ListenPepHdr,ListenECB); +IPXListenForPacket(ListenECB); +writeln('Listening for incoming packet.'); + +IPXGetIntervalMarker(ticks); +REPEAT + IPXrelinquishControl; + IPXGetIntervalMarker(ticks2); + CheckError(TimeOut(ticks,ticks2,130),$101);{ error if a timeout occurred } +UNTIL PacketReceived; + +writeln('Packet received.. initiating transfer process.'); +writeln('Received PacketID:',LastTransactionID); +writeln('len of data:',BytesReceived); + +{ do something with DataBuffer: the data that was just received. } +{ the first packet contains the filename and filesize } +Move(DataBuffer[1],FileName[0],15); +Move(DataBuffer[16],FileSize,4); +writeln('Receiving file ',FileName,', size: ',FileSize); + +REPEAT { Listen for remaining packets } + Packetreceived:=false; + + While SendECB.InuseFlag<>0 + do IPXrelinquishControl; + + IPXListenForPacket(ListenECB); + IPXGetIntervalMarker(ticks); + writeln(FileSize); + Repeat + {write(lnr);} + IPXrelinquishControl; + IPXGetIntervalMarker(ticks2); + CheckError(TimeOut(ticks,ticks2,10),$102); { error if Timeout occurred } + until PacketReceived; + writeln('Packet#:',LastTransactionID); + writeln('len of data:',BytesReceived); + FileSize:=FileSize-BytesReceived; + { do something with DataBuffer: the data that was just received. } + +UNTIL (FileSize<=0); { entire file received } + +writeln('Transfer complete.'); +IPXcloseSocket(IOsocket); +end. \ No newline at end of file diff --git a/NWTP/XIPX/SHWSAPS.PAS b/NWTP/XIPX/SHWSAPS.PAS new file mode 100644 index 0000000..b1aec3d --- /dev/null +++ b/NWTP/XIPX/SHWSAPS.PAS @@ -0,0 +1,189 @@ +{$X+,V-,B-} +program ShSAPs; + +{ Testprogram for the nwSAP unit / NwTP 0.6 (c) 1993,1995 R.Spronk } + +{ Dump all incoming SAP broadcasts on screen; + Sends -no- packets; receiving packets only } + +{ Demonstrates + -The use of the Service Advertizing Protocol; + -Asynchronous handling of receiving and processing + (using 1 receive ESR and intermediate buffers } + +uses crt,nwMisc,nwIPX,nwSAP; + +CONST SAPsocket=$0452; + BUFSIZ=511; + { May 'hang' your WS if more than 70 SAP broadcast were received + in a short interval (a few ticks). Increase the BUFSIZ value. } + +Type TSAPserver=record + ObjType:word; + Name :array[1..48] of byte; { asciiz } + Address:TinternetworkAddress; + Hops :word; + end; + + TSAPresponse=record + ResponseType:word; { 0002 General server; 0004 nearest server } + ServerEntry:array[1..7] of TSAPserver; + end; + +Type String48=string[48]; + Tservices=record + InUseFlag :Byte; { 0: not being accessed by other threads } + TimeStamp :Word; { Ticks / max 60. minutes } + ObjType:word; + Name :array[1..48] of byte; { asciiz } + Address:TinternetworkAddress; + Hops :word; + end; + +Var ServBuf:array[0..BUFSIZ] of TServices; + ECBServBufInd:word; { 0..BUFSIZ } + ServBufInd :word; { 0..BUFSIZ } + + StartTicks:Longint; + + PktCount:word; + +Var ReceiveEcb :Tecb; + IpxHdr :TipxHeader; + socket :word; + IPXreceiveBuffer: array[1..546] of byte; + SAPreceiveBuffer: TSAPresponse absolute IPXreceiveBuffer; + + ReceivedBufLen:word; + PacketReceived:boolean; + + RecString :string; + + NewStack:array[1..1024] of word; { !! used by ESR } + StackBottom:word; { !! used by ESR } + ESRctr:byte; { !! used by SAP ESR } + + +{$F+} +Procedure SAPListenESRhandler(Var p:Tpecb); +begin +if SAPreceiveBuffer.Responsetype=$0200 { 0002 hi-lo: general server SAP reply } + then begin + ESRctr:=1; + while (ESRctr<=7) and (SAPreceiveBuffer.ServerEntry[ESRctr].ObjType>$0000) + do begin + while ServBuf[ECBservBufInd].inUseFlag>0 + do begin + inc(ECBServBufInd); + ECBservBufInd:=ECBservBufInd and BUFSIZ; + end; + with SAPreceiveBuffer.ServerEntry[ESRctr] + do begin + Move(ObjType,ServBuf[ECBServBufInd].ObjType,SizeOf(TSAPserver)); + ObjType:=$0000; { To mark that the entry has been dealt with; + to 'clear' receive buffer } + end; + with ServBuf[ECBServBufInd] + do begin + IPXgetIntervalMarker(TimeStamp); + InUseFlag:=$FF; + end; + inc(ESRctr); + end; + PacketReceived:=true; + inc(PktCount); + end; +IPXListenForPacket(ReceiveECB); +end; +{$F-} + +{$F+} +Procedure SAPListenESR; assembler; +asm { ES:SI are the only valid registers when entering this procedure ! } + mov dx, seg stackbottom + mov ds, dx + + mov dx,ss { setup of a new local stack } + mov bx,sp { ss:sp copied to dx:bx} + mov ax,ds + mov ss,ax + mov sp,offset stackbottom + push dx { push old ss:sp on new stack } + push bx + + push es { push es:si on stack as local vars } + push si + mov di,sp + + push ss { push address of local ptr on stack } + push di + CALL SAPListenEsrHandler + + add sp,4 { skip stack ptr-copy } + pop bx { restore ss:sp from new stack } + pop dx + mov sp,bx + mov ss,dx +end; +{$F-} + + +Var ServerName:string; + +begin +IF NOT IpxInitialize + then begin + writeln('Ipx needs to be installed.'); + halt(1); + end; +socket:=SAPSocket; +IF NOT IPXopenSocket(Socket,SHORT_LIVED_SOCKET) + then begin + writeln('IPXopenSocket returned error# ',nwIPX.result); + halt(1); + end; + +PktCount:=0; +ECBservBufInd:=0; +PacketReceived:=False; +{ Empty receive buffer (ReceiveEcb.fragment[2].address^) } +FillChar(IPXreceiveBuffer,546,#0); + +{ Setup ECB and IPX header } +IPXsetupListenECB(Addr(SAPListenESR),SAPsocket,@IPXreceiveBuffer,546, + IpxHdr,ReceiveEcb); + +IPXListenForPacket(ReceiveECB); + +ServBufInd:=0; +REPEAT + + WHILE (ServBufInd<512) and (NOT keypressed) + do begin + IPXrelinquishControl; + + IF ServBuf[ServBufInd].InUseFlag>0 + then begin + with ServBuf[ServBufInd] + do begin + writeln('---------'); + writeln('BufIndex:',ServBufInd); + writeln('Timestamp: ',HexStr(TimeStamp,4)); + writeln('ObjType : ',HexStr(swap(ObjType),4)); + ZStrCopy(ServerName,name[1],48); + writeln('ServerNm : ',ServerName); + writeln('Address : ',HexDumpStr(Address,24)); + writeln('Hops : ',HexStr(swap(Hops),4)); + end; + ServBuf[ServBufInd].InUseFlag:=0; + end; + + inc(ServBufInd);ServBufInd:=ServBufInd and BUFSIZ; + end; + +UNTIL KeyPressed; + +IF NOT IPXcloseSocket(SAPsocket) + then writeln('IPXcloseSocket returned error# ',nwIPX.result); + +end. diff --git a/NWTP/XIPX/SKT_XXX b/NWTP/XIPX/SKT_XXX new file mode 100644 index 0000000..d9782e8 --- /dev/null +++ b/NWTP/XIPX/SKT_XXX @@ -0,0 +1,1881 @@ +# -------------------------------------------------------------------- +# SKT_XXX: IPX Socket numbers +# +# Compiled from various lists: Novell, RFC 1340 +# -------------------------------------------------------------------- + +# Sockets 0001-0BB8 have to be registered with the IEEE + +0001 Routing Information +0002 Echo Protocol +0003 Error handler + +# Sockets 0020-003F are considered 'experimental' + +0040 NW4 Time Synchronisztion Server Novell Inc. +0451 File Service Xerox +0452 Service Adverstising Protocol Xerox +0453 Routing Information Protocol Xerox +0455 NetBIOS +0456 Diagnostic Services +0457 ?? Server Serial Number check Novell Inc. + +# IEEE: dynamically assignable sockets 0BB9-FFFF +# Novell uses this range in the following manner: +# 0BB9-7FFF: dynamically assignable sockets +# 8000-FFFF: 'well known sockets', registered with Novell. + +4444 Sitelock Server Brightworks +4800 LANZ Agent +5555 Sitelock Client Brightworks + +# Novell 'Well known' sockets + +8000 National Advanced Systems +8001 National Advanced Systems +8002 National Advanced Systems +8003 Comm Driver Sperry Corp. Computer Systems +8004 KTA +8005 KTA +8006 KTA +8007 KTA +8008 Novell Inc. +8009 Sperry Term Emulator Turnbull Automations +800A Print Server Communication Horizons +800B Data Language Corp +800C Stats Socket Novell Inc. +800D UPS Novell Inc. +800E Performance Test Novell Inc. +800F Batram Santa Clara Systems +8010 Office Ware Century Analysis +8011 UPS Elgar Corp. +8012 UPS Elgar Corp. +8013 Chi Corp. +8014 Intel - American Fork +8015 Compass Computing +8016 Compass Computing +8017 Compass Computing +8018 Compass Computing +8019 Compass Computing +801A Compass Computing +801B Compass Computing +801C Compass Computing +801D Compass Computing +801E Compass Computing +801F Novell Inc. +8020 Novell Inc. +8021 Novell Inc. +8022 Novell Inc. +8023 Mcafee Associates +8024 Blue Lance Network Info Sys +8027 Gateway Communications Inc. +8028 Gateway Communications Inc. +8029 Gateway Communications Inc. +802A File Sharing Netline Inc. +802B File Sharing Netline Inc. +802C Intel +802D Intel +802E ICM +802F C-Tree Server Fair Com +8030 Micromind +8031 Micromind +8032 North Star Computers +8033 North Star Computers +8034 X.25 Gateway Rsj Software +8035 Sanyo Icon Inc. +8036 Data Access Corp. +8037 Apic's Stocknet Broker Novell Inc. +8038 Apic's Stocknet Broker Novell Inc. +8039 Net Management Novell - Austin +803A Beta Soft +803B Phaser Systems +803C Phaser Systems +803D Phaser Systems +803E Performance Group +803F Performance Group +8040 Horizon Technology Inc. +8041 Cd-Rom Server Meridian Data Corp. +8042 Nationwide Computer Services +8043 Comm Server Computer Language Research Inc. +8046 Netware VMS Novell Inc. +8047 3274 Controller Emulators Software Dynamics +8048 3274 Controller Emulators Software Dynamics +8049 3274 Controller Emulators Software Dynamics +804A 3274 Controller Emulators Software Dynamics +804B Mic Sna Dfv Server Computerland +804C Mic Sna Dfv Server Computerland +804D Database Server Migent Software Inc. +804E Switch Link Systems +804F +8050 Btrieve Server 5.0 ? +8051 +8052 +8053 +8054 +8055 E-Mail Chat Niche Co +8056 Money Transfer IPI Inc. +8057 E-Mail Chat Niche Co +8058 Btrieve Novell Inc. +8059 Btrieve Novell Inc. +805A SQL Novell Inc. +805B SQL Novell Inc. +805C Gameserver Novell Inc. +805D Gameserver Novell Inc. +805E Telebase Systems +805F Telebase Systems +8060 Print Server Novell Inc. +8061 T-Net, Lan Bridges British Telecom +8062 Wollongong Group +8063 Unix R Login Novell Inc. +8064 Micro Data Base Systems +8065 Micro Data Base Systems +8066 Norton Lambert Corp. +8067 Norton Lambert Corp. +8068 Norton Lambert Corp. +8069 Netware To HP Lan Gateway Hewlett Packard +806A Remote Pc Software ALM +806B Wancopy Utility Novell Inc. +806C Chat Program Digital Av Inc. +806D Pc Dex Mergent International +806E DART College Hill Systems +806F Netware Access Server Novell Inc. +8070 Network Courier Microsoft Workgroup Canada +8071 Pipes Peer Logic +8072 Wordperfect Corp. +8073 Progress Database & 4GL Progress Software Corp. +8074 Right Hand Man Futuresoft +8075 Right Hand Man Futuresoft +8076 Laser Disk Program University Of Wisconsin +8077 Fax Link & Vax Manager Optus Information Systems +8078 Fax Link & Vax Manager Optus Information Systems +8079 Telecommunications Van Auto Parts +807A Telecommunications Van Auto Parts +807B Telecommunications Van Auto Parts +807C Telecommunications Van Auto Parts +807D Telecommunications Van Auto Parts +807E R21px Crosstalk +807F Oracle Corp. +8080 Oracle Corp. +8081 Oracle Corp. +8082 Oracle Corp. +8083 Oracle Corp. +8084 Oracle Corp. +8085 Oracle Corp. +8086 Oracle Corp. +8087 Oracle Corp. +8088 Oracle Corp. +8089 Remote Pc Software ALM +808A +808B +808C +808D +808E +808F +8090 Pillsbury Corp. +8091 Pillsbury Corp. +8092 Pillsbury Corp. +8093 Pillsbury Corp. +8094 Pillsbury Corp. +8095 Pillsbury Corp. +8096 Pillsbury Corp. +8097 Pillsbury Corp. +8098 Pillsbury Corp. +8099 Pillsbury Corp. +809A Marshfield Clinic +809B Marshfield Clinic +809C Marshfield Clinic +809D Marshfield Clinic +809E Marshfield Clinic +809F Marshfield Clinic +80AF Dsi Dynapro Systems Inc. +80B0 Dsi Dynapro Systems Inc. +80B1 Dsi Dynapro Systems Inc. +80B2 Dsi Dynapro Systems Inc. +80B3 Dsi Dynapro Systems Inc. +80B4 Chicago Research & Trading +80B5 Streetwise Systems Inc. +80B6 Streetwise Systems Inc. +80B7 Fax Server Digital Visions Corp. +80B8 Voice Server Digital Visions Corp. +80B9 Digital Visions Corp. +80BA Netware Management Application Frye Computer Systems +80BB Major BBS Software Galacticomm Inc. +80BC Major BBS Software Galacticomm Inc. +80BD Major BBS Software Galacticomm Inc. +80BE Major BBS Software Galacticomm Inc. +80BF Major BBS Software Galacticomm Inc. +80C0 Major BBS Software Galacticomm Inc. +80C1 Major BBS Software Galacticomm Inc. +80C2 Major BBS Software Galacticomm Inc. +80C3 Chat Program/IPX Talk Felsina Software +80C4 IPX/SPX Comm Protocol Applications Magee Enterprises Inc. +80C5 Watcom +80C6 Newport Systems Solutions Inc. +80C7 Newport Systems Solutions Inc. +80C8 Newport Systems Solutions Inc. +80C9 Newport Systems Solutions Inc. +80CA Newport Systems Solutions Inc. +80CB Newport Systems Solutions Inc. +80CC Newport Systems Solutions Inc. +80CD Newport Systems Solutions Inc. +80CE Nova Focus Remote Pc Access Driggs Corp. +80CF Appl IPX/SPX Communications Magee Enterprises Inc. +80D0 Appl IPX/SPX Communications Magee Enterprises Inc. +80D1 Appl IPX/SPX Communications Magee Enterprises Inc. +80D2 Appl IPX/SPX Communications Magee Enterprises Inc. +80D3 Appl 1px/SPX Communications Magee Enterprises Inc. +80D5 Database Server Gupta Technologies +80D6 Database Server Gupta Technologies +80D7 Comm Servers/LAN p2p services US Robotics Software +80D8 Powerchute NLM American Power Conversion +80D9 Corel Driver Product Corel Systems, Optical Div +80DA Archive Server Gigatrend Inc. +80DB Gateway Product Atlanta Technologies +80DC Gateway Product Atlanta Technologies +80DD Office Organizer NLM Unisys +80DE Universal Network Systems +80DF Application Server Nationsbank Appl Systems Supp +80E1 Modular Software Corp. +80E2 Client Server Application Software Ag +80E4 Lanport Virtual Extension Of Ports Microtest Inc. +80E5 Work Station Peer-To-Peer Conveyant Systems Inc. +80E6 Deskview X - IPX Socket Interface Quarterdeck Office Systems +80E7 Deskview X - IPX Socket Interface Quarterdeck Office Systems +80E8 Deskview X - IPX Socket Interface Quarterdeck Office Systems +80E9 Deskview X - IPX Socket Interface Quarterdeck Office Systems +80EA Deskview X - IPX Socket Interface Quarterdeck Office Systems +80EB Deskview X - IPX Socket Interface Quarterdeck Office Systems +80EC Meridian Data Inc. +80ED Biztech +80EE Rational Data Systems +80EF Rational Data Systems +80F0 Rational Data Systems +80F1 Rational Data Systems +80F4 Garp Server Communication Net Research Pty Ltd +80F5 Network Management Product NCR +80F6 Network Management Product NCR +80F7 Network Management Product NCR +80F8 Network Management Product NCR +80F9 Network Management Product NCR +80FA Network Management Product NCR +80FB Professional Programming SVCs +80FC Professional Programming SVCs +80FD Peer Logic +80FE Wall Data +80FF Distributed Application Folio Corp. +8100 Marshfield Clinic +8101 Marshfield Clinic +8102 Marshfield Clinic +8103 Marshfield Clinic +8104 Netware 386 Server Novell Inc. +8105 Via +8106 Proteon +8107 Proteon +8108 Proteon +8109 Proteon +810A Proteon +810B Proteon +810C Proteon +810D Proteon +810E Proteon +810F Net 3270 Mcgill University Computing Ct +8110 Professional Productivity Corp. +8111 Tods Teletrak +8112 Lufthansa Lufthansa +8113 Lanport Microtest +8114 Lanport Microtest +8115 Lanport/Netport Microtest/Intel +8116 Lanport Microtest +8117 Lanport Microtest +8118 Lanport Microtest +8119 Lanport Microtest +811A Lanport Microtest +811B Lanport Microtest +811C Lanport Microtest +811D Image Server File Net Corp. +811E Print Server Novell Inc. +811F RTK Operating System Owl Micro Systems +8120 TXD Thomas Conrad Corp. +8121 Special Request Spectrafax +8122 Net Monitor Artefact Network Support +8123 Net Monitor Artefact Network Support +8124 Net Monitor Artefact Network Support +8125 Net Monitor Artefact Network Support +8126 Net Monitor Artefact Network Support +8127 Net Monitor Artefact Network Support +8128 Net Monitor Artefact Network Support +8129 Net Monitor Artefact Network Support +812A Net Monitor Artefact Network Support +812B Net Monitor Artefact Network Support +812C Net Monitor Artefact Network Support +812D Net Monitor Artefact Network Support +812E Test Server Novell Inc. +812F Test Server Novell Inc. +8130 Test Server Novell Inc. +8131 Test Server Novell Inc. +8132 Test Server Novell Inc. +8133 Test Server Novell Inc. +8134 Test Server Novell Inc. +8135 Test Server Novell Inc. +8136 Test Server Novell Inc. +8137 Test Server Novell Inc. +8138 Lansight Lan Systems +8139 Pc Chalkboard Intel - American Fork +813B Real Time Back-Up Emerald Systems +813C Network Management Pure Data Inc. +813D Srs Plus Novell Inc. +813E File Transfer AAC Systems +813F Image Server Wang Laboratories +8140 Image Server Wang Laboratories +8141 Image Server Wang Laboratories +8142 Image Server Wang Laboratories +8143 Network Designers Ltd +8144 Maynestream Arcada Software +8145 Maynestream Arcada Software +8146 Network Management System Accunetics +8147 Network Management System Accunetics +8148 Network Management System Accunetics +8149 Network Management System Accunetics +814A BR Computing +814B Automated Design Systems +814C BR Computing +814D BR Computing +814E BR Computing +814F BR Computing +8150 BR Computing +8151 BR Computing +8152 BR Computing +8153 BR Computing +8154 BR Computing +8155 BR Computing +8156 BR Computing +8157 BR Computing +8158 BR Computing +8159 BR Computing +815A BR Computing +815B BR Computing +815C BR Computing +815D BR Computing +815E BR Computing +815F BR Computing +8160 Printqlan Software Directions Inc. +8161 Printqlan Software Directions Inc. +8162 Printqlan Software Directions Inc. +8163 Printqlan Software Directions Inc. +8164 Printqlan Software Directions Inc. +8165 Printqlan Software Directions Inc. +8166 Printqlan Software Directions Inc. +8167 Printqlan Software Directions Inc. +8168 Printqlan Software Directions Inc. +8169 Printqlan Software Directions Inc. +816A Printqlan Software Directions Inc. +816B Printqlan Software Directions Inc. +816C Printqlan Software Directions Inc. +816D Printqlan Software Directions Inc. +816E Printqlan Software Directions Inc. +816F Printqlan Software Directions Inc. +8170 Cdrom Online Computer Systems +8171 Cdrom Online Computer Systems +8172 Bentley Systems +8173 Avalan +8174 Avalan +8175 Avalan +8176 Avalan +8177 Avalan +8178 Avalan +8179 Avalan +817A Avalan +817B Accounting APD Supernet +817C Acs Instant Information +817D Server Db Xdb Systems +817E Pipes Peer Logic +817F Pipes Peer Logic +8180 Netware Software Associates +8181 Peer-To-Peer Lodgistix Inc. +8182 Peer-To-Peer Lodgistix Inc. +8183 Peer-To-Peer Lodgistix Inc. +8184 Peer-To-Peer Lodgistix Inc. +8185 Peer-To-Peer Lodgistix Inc. +8186 Danware +8187 Danware +8188 Danware +8189 Danware +818A Danware +818B Netframe Nw 386 Netframe +818C Netframe Nw 386 Netframe +818D Netframe Nw 386 Netframe +818E Netframe Nw 386 Netframe +818F Maxiback Sysgen Inc. +8190 Maxiback Sysgen Inc. +8191 Maxiback Sysgen Inc. +8192 Maxiback Sysgen Inc. +8193 DCA IPX Comm Product Digital Communications Assoc +8194 DCA IPX Comm Product Digital Communications Assoc +8195 DCA IPX Comm Product Digital Communications Assoc +8196 Quickchart Healthware +8197 Quickchart Healthware +8198 Quickchart Healthware +8199 Quickchart Healthware +819A Universuty Of Otago +819B University Of Otago +819C Mini SQL Isicad +81A0 Dealing Room Systems Hovland Business Systems Ltd +81A1 Dealing Room Systems Hovland Business Systems Ltd +81A2 Dealing Room Systems Hovland Business Systems Ltd +81A3 Dealing Room Systems Hovland Business Systems Ltd +81A4 Dealing Room Systems Hovland Business Systems Ltd +81A5 Dealing Room Systems Hovland Business Systems Ltd +81A6 Dealing Room Systems Hovland Business Systems Ltd +81A7 Dealing Room Systems Hovland Business Systems Ltd +81A8 Dealing Room Systems Hovland Business Systems Ltd +81A9 Dealing Room Systems Hovland Business Systems Ltd +81AA Dealing Room Systems Hovland Business Systems Ltd +81AB Dealing Room Systems Hovland Business Systems Ltd +81AC Dealing Room Systems Hovland Business Systems Ltd +81AD Dealing Room Systems Hovland Business Systems Ltd +81AE Dealing Room Systems Hovland Business Systems Ltd +81AF Dealing Room Systems Hovland Business Systems Ltd +81B0 Dealing Room Systems Hovland Business Systems Ltd +81B1 Dealing Room Systems Hovland Business Systems Ltd +81B2 Dealing Room Systems Hovland Business Systems Ltd +81B3 Dealing Room Systems Hovland Business Systems Ltd +81B4 Dealing Room Systems Hovland Business Systems Ltd +81B5 Dealing Room Systems Hovland Business Systems Ltd +81B6 Dealing Room Systems Hovland Business Systems Ltd +81B7 Dealing Room Systems Hovland Business Systems Ltd +81B8 Dealing Room Systems Hovland Business Systems Ltd +81B9 Dealing Room Systems Hovland Business Systems Ltd +81BA Dealing Room Systems Hovland Business Systems Ltd +81BB Dealing Room Systems Hovland Business Systems Ltd +81BC Dealing Room Systems Hovland Business Systems Ltd +81BD Dealing Room Systems Hovland Business Systems Ltd +81BE Dealing Room Systems Hovland Business Systems Ltd +81BF Dealing Room Systems Hovland Business Systems Ltd +81C0 Dealing Room Systems Hovland Business Systems Ltd +81C1 Dealing Room Systems Hovland Business Systems Ltd +81C2 Dealing Room Systems Hovland Business Systems Ltd +81C3 Dealing Room Systems Hovland Business Systems Ltd +81C4 Dealing Room Systems Hovland Business Systems Ltd +81C5 Dealing Room Systems Hovland Business Systems Ltd +81C6 Dealing Room Systems Hovland Business Systems Ltd +81C7 Dealing Room Systems Hovland Business Systems Ltd +81C8 Dealing Room Systems Hovland Business Systems Ltd +81C9 Dealing Room Systems Hovland Business Systems Ltd +81CA Dealing Room Systems Hovland Business Systems Ltd +81CB Dealing Room Systems Hovland Business Systems Ltd +81CC Dealing Room Systems Hovland Business Systems Ltd +81CD Dealing Room Systems Hovland Business Systems Ltd +81CE Dealing Room Systems Hovland Business Systems Ltd +81CF Dealing Room Systems Hovland Business Systems Ltd +81D0 Dealing Room Systems Hovland Business Systems Ltd +81D1 Dealing Room Systems Hovland Business Systems Ltd +81D2 Dealing Room Systems Hovland Business Systems Ltd +81D3 Dealing Room Systems Hovland Business Systems Ltd +81D4 Dealing Room Systems Hovland Business Systems Ltd +81D5 Dealing Room Systems Hovland Business Systems Ltd +81D6 Dealing Room Systems Hovland Business Systems Ltd +81D7 Dealing Room Systems Hovland Business Systems Ltd +81D8 Dealing Room Systems Hovland Business Systems Ltd +81D9 Dealing Room Systems Hovland Business Systems Ltd +81DA Dealing Room Systems Hovland Business Systems Ltd +81DB Dealing Room Systems Hovland Business Systems Ltd +81DC Dealing Room Systems Hovland Business Systems Ltd +81DD Dealing Room Systems Hovland Business Systems Ltd +81DE Dealing Room Systems Hovland Business Systems Ltd +81DF Dealing Room Systems Hovland Business Systems Ltd +81E0 Dealing Room Systems Hovland Business Systems Ltd +81E1 Dealing Room Systems Hovland Business Systems Ltd +81E2 Dealing Room Systems Hovland Business Systems Ltd +81E3 Dealing Room Systems Hovland Business Systems Ltd +81E4 Dealing Room Systems Hovland Business Systems Ltd +81E5 Dealing Room Systems Hovland Business Systems Ltd +81E6 Dealing Room Systems Hovland Business Systems Ltd +81E7 Dealing Room Systems Hovland Business Systems Ltd +81E8 Dealing Room Systems Hovland Business Systems Ltd +81E9 Dealing Room Systems Hovland Business Systems Ltd +81EA Dealing Room Systems Hovland Business Systems Ltd +81EB Dealing Room Systems Hovland Business Systems Ltd +81EC Dealing Room Systems Hovland Business Systems Ltd +81ED Dealing Room Systems Hovland Business Systems Ltd +81EE Dealing Room Systems Hovland Business Systems Ltd +81EF Dealing Room Systems Hovland Business Systems Ltd +81F0 Dealing Room Systems Hovland Business Systems Ltd +81F1 Dealing Room Systems Hovland Business Systems Ltd +81F2 Dealing Room Systems Hovland Business Systems Ltd +81F3 Dealing Room Systems Hovland Business Systems Ltd +81F4 Dealing Room Systems Hovland Business Systems Ltd +81F5 Dealing Room Systems Hovland Business Systems Ltd +81F6 Dealing Room Systems Hovland Business Systems Ltd +81F7 Dealing Room Systems Hovland Business Systems Ltd +81F8 Dealing Room Systems Hovland Business Systems Ltd +81F9 Dealing Room Systems Hovland Business Systems Ltd +81FA Dealing Room Systems Hovland Business Systems Ltd +81FB Dealing Room Systems Hovland Business Systems Ltd +81FC Network Support Mgr PCI Ltd +81FD Network Support Mgr PCI Ltd +81FE Iwi +81FF Martello & Associates +8203 Network Computing Inc. (NCI) +8204 Network Computing Inc. (NCI) +8205 Network Computing Inc. (NCI) +8206 Network Computing Inc. (NCI) +8207 Network Computing Inc. (NCI) +8208 Network Computing Inc. (NCI) +8209 Network Computing Inc. (NCI) +820A Network Computing Inc. (NCI) +820B Network Computing Inc. (NCI) +820C Network Computing Inc. (NCI) +820D Data Voice Solutions Corp. +820E Id 5001 Weather Station Zenith Data Systems +820F Write Server Arc Calculon +8210 Write Server Quantum Consulting +8211 System 9 Hbf Group +8212 System 9 Hbf Group +8213 System 9 Hbf Group +8214 System 9 Hbf Group +8215 System 9 Hbf Group +8216 System 9 Hbf Group +8217 System 9 Hbf Group +8218 System 9 Hbf Group +8219 Argus Triticom +821A Argus Triticom +821B TCP/IP Gateway Computervision Services +821C Pickit (comm Server) Intel +821D Peer Logic +821E Peer Logic +821F Data Face Net Batch Computer Aided Business Sol +8220 Data Face Net Batch Computer Aided Business Sol +8221 Luminar Optical Server Corel Systems Corp. +8222 +8223 +8224 +8225 +8226 +8227 X-Bridge Advanced Policy Communications +8228 X-Bridge Advanced Policy Communications +8229 Flexcom Evergreen Systems +822A Flexcom Evergreen Systems +822B Flexcom Evergreen Systems +822C Flexcom Evergreen Systems +822D Flexcom Evergreen Systems +822E Gateways & Wkst Processor Teknos Systems +822F Gateways & Wkst Processor Teknos Systems +8230 Gateways & Wkst Processor Teknos Systems +8231 Gateways & Wkst Processor Teknos Systems +8232 Gateways & Wkst Processor Teknos Systems +8233 Gateways & Wkst Processor Teknos Systems +8234 Gateways & Wkst Processor Teknos Systems +8235 Gateways & Wkst Processor Teknos Systems +8236 Gateways & Wkst Processor Teknos Systems +8237 Gateways & Wkst Processor Teknos Systems +8238 Lanware Horizon Technology Inc. +8239 Lanware Horizon Technology Inc. +823A Lanware Horizon Technology Inc. +823B Lanware Horizon Technology Inc. +823C Lanware Horizon Technology Inc. +823D +823E Team 286 Iwi +823F Dbms Lock Manager Raima Corp. +8240 Dbms Lock Manager Raima Corp. +8241 Central Point Software +8242 Remote Computing Central Point Software +8243 Remote Computing Central Point Software +8244 Remote Computing Central Point Software +8245 Remote Computing Central Point Software +8246 Remote Computing Central Point Software +8247 Remote Computing Central Point Software +8248 Remote Computing Central Point Software +8249 Token Ring Rpl NCR +824A Token Ring Rpl NCR +824B Dealing Room Systems Hovland Business Systems Ltd +824C Dealing Room Systems Hovland Business Systems Ltd +824D Dealing Room Systems Hovland Business Systems Ltd +824E Dealing Room Systems Hovland Business Systems Ltd +824F Dealing Room Systems Hovland Business Systems Ltd +8250 Dealing Room Systems Hovland Business Systems Ltd +8251 Dealing Room Systems Hovland Business Systems Ltd +8252 Dealing Room Systems Hovland Business Systems Ltd +8253 Total Automation Systems Dynatech Utah Scientific +8254 Total Automation Systems Dynatech Utah Scientific +8255 Vantage Point Connect Computer +8256 Netarc Scheduler Emerald Systems +8257 Distributed Processing Brigham Young University +8258 Xtree Net Central Point Software +8259 Xtree Net Central Point Software +825A Sysm/lan2 H&w Computer Systems +825B Vantage Point Connect Computer +825C Vantage Point Connect Computer +825D Vantage Point Connect Computer +825E Time Out Nordra Inc. +825F Mulit-Processor Controller MBAC +8260 Mulit-Processor Controller MBAC +8261 Mulit-Processor Controller MBAC +8262 Mulit-Processor Controller MBAC +8263 Mulit-Processor Controller MBAC +8264 Time Out Nordra Inc. +8265 Ingres Database Ingres Corp. +8266 Easy Street Prem Finance Streetwise Systems Inc. +8267 Easy Street Prem Finance Streetwise Systems Inc. +8269 Apt Net Automated Programming Tech +826A Apt Net Automated Programming Tech +826B Apt Net Automated Programming Tech +826C Apt Net Automated Programming Tech +826D Total Automation Sys Ed Lutheran Social Services +826E Total Automation Sys Mc Lutheran Social Services +826F Total Automation Sys Ml Dynatech Utah Scientific +8270 Total Automation Sys - Adi Dynatech Utah Scientific +8271 Total Automation Sys - Fax Dynatech Utah Scientific +8272 Service Point Interpoint Software +8273 Service Point Interpoint Software +8274 Prodigy Gateway Computerease Software +8275 Prodigy Gateway Computerease Software +8276 Newsmanager VSS Inc. +8277 Newsmanager VSS Inc. +8278 Newsmanager VSS Inc. +827F Martello & Associates +8280 Mark Hurst +8281 Mark Hurst +8282 Mark Hurst +8283 Mark Hurst +8284 Barr gate/PC-Mainframe Comms Barr Systems Inc. +8285 Barr Gate/PC-Mainframe Comms Barr Systems Inc. +8286 Pure Data Research Ltd +8287 Voice Mail Plan Communications +8288 Centers For Disease Control +8289 RPC Calls Integrated Data Systems +828A Integrated Data Systems +828B Folio Corp. +828C Multitech +828D Multitech +828E Multitech +828F Multitech +8290 Multitech +8291 Bus Tech +8292 American Airlines Decision Tec +8293 Microcom Inc. +8294 Gateway Usa +8295 Gateway Usa +8296 Gateway Usa +8297 Gateway Usa +8298 Gateway Usa +8299 Gateway Usa +829A Computervision Services +829B Computervision Services +829C Computervision Services +829D Shiva Corp. +829E Shiva Corp. +829F Todd Weiss +82A0 Todd Weiss +82A1 Todd Weiss +82A2 Tape NLM Arcada Software +82A3 Lanlord Product Microcom Client Server Technol +82A4 Lanlord Product Microcom Client Server Technol +82A5 X25 Automated Bridge Monitor Microcom ?? +82A6 Microcom Inc. +82A7 Peer-To-Peer Communications Witness Systems +82A8 Micro Integration +82A9 Micro Integration +82AA IBM - Poughkeepsie +82AB IBM - Poughkeepsie +82AC IBM - Poughkeepsie +82AD IBM - Poughkeepsie +82AE IBM - Poughkeepsie +82AF IBM - Poughkeepsie +82B0 J&l Information Systems +82B1 J&l Information Systems +82B2 J&l Information Systems +82B3 J&l Information Systems +82B4 J&l Information Systems +82B5 J&l Information Systems +82B6 J&l Information Systems +82B7 J&l Information Systems +82B8 J&l Information Systems +82B9 J&l Information Systems +82BA J&l Information Systems +82BB J&l Information Systems +82BC J&l Information Systems +82BD J&l Information Systems +82BE J&l Information Systems +82BF J&l Information Systems +82C0 J&l Information Systems +82C1 J&l Information Systems +82C2 J&l Information Systems +82C3 J&l Information Systems +82C4 J&l Information Systems +82C5 J&l Information Systems +82C6 J&l Information Systems +82C7 J&l Information Systems +82C8 J&l Information Systems +82C9 J&l Information Systems +82CA J&l Information Systems +82CB J&l Information Systems +82CC J&l Information Systems +82CD J&l Information Systems +82CE J&l Information Systems +82CF J&l Information Systems +82D0 J&l Information Systems +82D1 J&l Information Systems +82D2 J&l Information Systems +82D3 J&l Information Systems +82D4 J&l Information Systems +82D5 J&l Information Systems +82D6 J&l Information Systems +82D7 J&l Information Systems +82D8 Legato Systems +82D9 Legato Systems +82DA Legato Systems +82DB Legato Systems +82DC Legato Systems +82DD Legato Systems +82DE Value Added Server Skyline Technology +82DF Value Added Server Skyline Technology +82E0 FCP For OS/2 Support Andersen Consulting +82E1 FCP For OS/2 Support Andersen Consulting +82E3 Sytron Corp. +82E4 American Airlines Decision Tec +82E5 Image Retrieval Inc. +82E6 Connect Computer +82E7 Connect Computer +82E8 Connect Computer +82E9 Connect Computer +82EA Connect Computer +82EB Connect Computer +82EC Connect Computer +82ED Connect Computer +82EE Connect Computer +82EF Connect Computer +82F0 Connect Computer +82F1 Connect Computer +82F2 Connect Computer +82F3 Connect Computer +82F4 Connect Computer +82F5 Connect Computer +82F6 Hello-1 (Client alive check) Joshin Denki Co Ltd J&p Div +82F7 Hello-1 (Client aluve check) Joshin Denki Co Ltd J&p Div +82F8 Hello-1 (Client alive check) Joshin Denki Co Ltd J&p Div +82F9 Hello-1 (Client alive check) Joshin Denki Co Ltd J&p Div +82FA Hello-1 (Client alive check) Joshin Denki Co Ltd J&p Div +82FD Netware Jukebox Corel Systems, Optical Div +82FE FCP For Windows Support Andersen Consulting +82FF FCP For Windows Support Andersen Consulting +8300 Smith Micro Software Inc. +8301 Smith Micro Software Inc. +8302 Smith Micro Software Inc. +8303 Smith Micro Software Inc. +8304 Smith Micro Software Inc. +8305 Smith Micro Software Inc. +8306 Smith Micro Software Inc. +8307 Smith Micro Software Inc. +8308 Smith Micro Software Inc. +8309 Smith Micro Software Inc. +830A Smith Micro Software Inc. +830B Smith Micro Software Inc. +830C Smith Micro Software Inc. +830D Smith Micro Software Inc. +830E Smith Micro Software Inc. +830F Smith Micro Software Inc. +8310 Smith Micro Software Inc. +8311 Smith Micro Software Inc. +8312 Smith Micro Software Inc. +8313 Smith Micro Software Inc. +8314 Smith Micro Software Inc. +8315 Smith Micro Software Inc. +8316 Smith Micro Software Inc. +8317 Smith Micro Software Inc. +8318 Smith Micro Software Inc. +8319 Smith Micro Software Inc. +831A Smith Micro Software Inc. +831B Smith Micro Software Inc. +831C Smith Micro Software Inc. +831D Smith Micro Software Inc. +831E Smith Micro Software Inc. +831F Smith Micro Software Inc. +8320 Smith Micro Software Inc. +8321 Smith Micro Software Inc. +8322 Smith Micro Software Inc. +8323 Smith Micro Software Inc. +8324 Smith Micro Software Inc. +8325 Smith Micro Software Inc. +8326 Smith Micro Software Inc. +8327 Smith Micro Software Inc. +8328 Smith Micro Software Inc. +8329 Smith Micro Software Inc. +832A Smith Micro Software Inc. +832B Smith Micro Software Inc. +832C Smith Micro Software Inc. +832D Smith Micro Software Inc. +832E Smith Micro Software Inc. +832F Smith Micro Software Inc. +8330 Smith Micro Software Inc. +8331 Smith Micro Software Inc. +8332 Smith Micro Software Inc. +8333 Smith Micro Software Inc. +8334 Smith Micro Software Inc. +8335 Smith Micro Software Inc. +8336 Smith Micro Software Inc. +8337 Smith Micro Software Inc. +8338 Smith Micro Software Inc. +8339 Smith Micro Software Inc. +833A Smith Micro Software Inc. +833B Smith Micro Software Inc. +833C Smith Micro Software Inc. +833D Smith Micro Software Inc. +833E Smith Micro Software Inc. +833F Smith Micro Software Inc. +8340 Smith Micro Software Inc. +8341 Smith Micro Software Inc. +8342 Smith Micro Software Inc. +8343 Smith Micro Software Inc. +8344 Smith Micro Software Inc. +8345 Smith Micro Software Inc. +8346 Smith Micro Software Inc. +8347 Smith Micro Software Inc. +8348 Smith Micro Software Inc. +8349 Smith Micro Software Inc. +834A Smith Micro Software Inc. +834B Smith Micro Software Inc. +834C Smith Micro Software Inc. +834D Smith Micro Software Inc. +834E Smith Micro Software Inc. +834F Smith Micro Software Inc. +8350 Power Grid Server Cognos Inc. +8351 Data Service To Workstation Chancery Software +8352 Transmitting Unisync +8353 Receiving Unisync +8354 Multicom Net Richard Cumming & Associates +8355 Multicom Net Richard Cumming & Associates +8356 Multicom Net Richard Cumming & Associates +8357 Cd Connections Cbis Inc. +8359 Riverview Systems +835A Total Automation Systems Dynatech Utah Scientific +835B Total Automation Systems Dynatech Utah Scientific +835C Itac Inc. +835D Asp Computer Products Inc. +835E Asp Computer Products Inc. +835F Asp Computer Products Inc. +8361 Fax Server Transfax Corp. +8362 Fax Print Server Transfax Corp. +8363 Fax Merge Server Transfax Corp. +8364 Network Management Server Transfax Corp. +8365 Funk Software +8366 Micro Integration +8367 Micro Integration +8368 Micro Integration +8369 Micro Integration +836A Micro Integration +836B Triple A Motor Club +836C Lan Times Japan, Softbank Corp. +836D Watchtower +836E SPX Client Server Comm Systems ProMicrorim +836F Norton Lambert Corp. +8370 Norton Lambert Corp. +8371 Norton Lambert Corp. +8372 Norton Lambert Corp. +8373 Norton Lambert Corp. +8374 Central Point Software +8375 Drivers Presoft Architects +8376 NLM For Remote Volume Mount Inteck Corp. +8377 NLM For Remote Volume Mount Inteck Corp. +8378 Symantec Peter Norton Group +8379 Symantec Peter Norton Group +837A Digital Equipment +837B CBS Facil. Assignment - Request Dynatech Utah Scientific +837C CBS Facilities Assignment - Reply Dynatech Utah Scientific +837D Multi-Protocol Router Inside IPX Research Machines Plc +837E Shareware Cherry Tree Software +837F Enterprise Ecs Intel Corp. +8380 Enterprise Mmt Intel Corp. +8381 Stock Ticker Broadcast Server Ncompass Development Intl +8382 Query Unique Users Us Robotics Software +8383 Cbs Ada Server Dynatech Utah Scientific +8384 Pace Software Systems Inc. +8385 Andersen Consulting +8386 Gateway Management Wall Data +8387 Gateway Management Wall Data +838B Powerchute Alert - Ups Monitoring American Power Conversion +838D Avail Systems Corp. +838E QA+ For Windows/remote Diagnostics Diagsoft Inc. +838F Powerchute Adminstrative Socket American Power Conversion +8390 Datamedic +8391 Corel Driver Corel Systems, Optical Div +8392 Lasermaster Printer Products Laser Master Corp. +8393 TFTP Trivial FTP Hewlett Packard +8394 FTP File Transfer Protocol Hewlett Packard +8395 Hewlett Packard +8396 Hewlett Packard +8397 Sita +8398 Sita +8399 Techgnosis Inc. +839A QA+ For Windows/remote Diagnostics Diagsoft Inc. +839B Mail Systems Synectic Systems Ltd +839C Mail Systems Synectic Systems Ltd +839D NLM/video Files On Novell Service Protocomm Corp. +839E Turnax Emulation Gateway Ide Corp. +839F Cnf 16000 Connection Station Corollary Inc. +83A0 Cnf 16000 Connection Station Corollary Inc. +83A1 Cnf 16000 Connection Station Corollary Inc. +83A2 Cnf 16000 Connection Station Corollary Inc. +83A3 Cnf 16000 Connection Station Corollary Inc. +83A4 Cnf 16000 Connection Station Corollary Inc. +83A5 Ws Peer-To-Peer Communicat IBM +83A6 Ws Peer-To-Peer Communicat IBM +83A7 Ws Peer-To-Peer Communicat IBM +83A8 Ws Peer-To-Peer Communicat IBM +83A9 Ws Peer-To-Peer Communicat IBM +83AA Ws Peer-To-Peer Communicat IBM +83AB Datanex Corp. +83AC Hp Open Mail & Portable Netware Hewlett Packard - Berkshire +83AD Communication/mail Server Software Dator 3 Spol Sro +83AE Communication/mail Server Software Dator 3 Spol Sro +83AF Communication/mail Server Software Dator 3 Spol Sro +83B0 Communication/mail Server Software Dator 3 Spol Sro +83B1 Power Management Server Elgar Corp. +83B2 Power Management Client Elgar Corp. +83B3 Network Peripherals - Print Server Canon Information Systems +83B4 Network Peripherals Canon Information Systems +83B5 Network Peripherals Canon Information Systems +83B6 Network Peripherals Canon Information Systems +83B7 Network Peripherals Canon Information Systems +83B8 Network Peripherals Canon Information Systems +83B9 Fax Server Ferrari Electronic GMbH +83BA Fax Server Ferrari Electronic GMbH +83BB Fax Server Ferrari Electronic GMbH +83BC Fax Server Ferrari Electronic GMbH +83BD SQL Cl/S Database Engine Sybase Inc. +83BE Printer Controller Board Dp Tek +83BF Printer Controller Board Dp Tek +83C0 Iwi +83C1 Lexmark International +83C2 Lexmark International +83C3 Lexmark International +83C4 Lexmark International +83C5 Lexmark International +83C6 Lexmark International +83C7 Okna Corp. +83C8 Okna Corp. +83C9 Okna Corp. +83CA Okna Corp. +83CB Okna Corp. +83CC Okna Corp. +83CD Development/communications Toolkit Michael Rich +83CE Reset Print Servers Motorola +83CF Network Designers +83D0 Remote Printer Socket Industrial Exotica +83D1 File Management Services Systems Axis Plc +83D2 Queue Management Services Systems Axis Plc +83D4 Lantech Services +83D5 Cc Mail Gateway 3.30/SPX Transport Cc Mail +83D6 Remote Control - Node-To-Node Dst - Distributed Systems Tech +83D7 Remote Control - Bank Of Modems Dst - Distributed Systems Tech +83D9 General Communication Forum Credit Lyonnais +83DA Database Engines Sybase Inc. +83DB Database Engines Sybase Inc. +83DC Database Engines Sybase Inc. +83DD Database Engines Sybase Inc. +83DE Database Engines Sybase Inc. +83DF Database Engines Sybase Inc. +83E0 Database Engines Sybase Inc. +83E1 Database Engines Sybase Inc. +83E2 Gateway Server Product Icc +83E3 Wan Connection Server Ideassociation +83E4 Lan Spool 3.5 Intel - American Fork +83E6 Remote Internal Hub Driver Intel Pced +83E7 Software Access Control Server U Of Plymouth +83E9 Communications System Unicables Sa +83EA Communications System Unicables Sa +83EB Communications System Unicables Sa +83EC Communications System Unicables Sa +83ED Communications System Unicables Sa +83EE Communications System Unicables Sa +83EF Communications System Unicables Sa +83F0 Communications System Unicables Sa +83F1 Communications System Unicables Sa +83F2 Communications System Unicables Sa +83F3 Communications System Unicables Sa +83F4 Communications System Unicables Sa +83F5 Communications System Unicables Sa +83F6 Communications System Unicables Sa +83F7 Communications System Unicables Sa +83F8 Communications System Unicables Sa +83F9 Communications System Unicables Sa +83FA Communications System Unicables Sa +83FB Communications System Unicables Sa +83FC Communications System Unicables Sa +83FD Communications System Unicables Sa +83FE Communications System Unicables Sa +83FF Communications System Unicables Sa +8400 Communications System Unicables Sa +8401 Communications System Unicables Sa +8402 Generic Server Greenbaum Associates +8403 Object-Store, DB Access Protocol Object Design +8404 Object-Store, DB Access Protocol Object Design +8405 Object-Store, Directory Protocol Object Design +8406 Object-Store, Directory Protocol Object Design +8407 Object-Store, Cache Coherence Prt Object Design +8408 Object-Store, Cache Coherence Prt Object Design +8409 Visinet NLM Technology Dynamics Inc. +840A WDAGR - Server Jostens Learning Corp. +840B Wkill Jostens Learning Corp. +840C Internet Lan Controller Bus Tech +840D Peer-To-Peer Communications M&m Mars Inc. +840E Peer-To-Peer Communications M&m Mars Inc. +840F Connection Manager Tbr International Corp. +8410 Connection Manager Tbr International Corp. +8411 Connection Manager Tbr International Corp. +8412 Connection Manager Tbr International Corp. +8413 Connection Manager Tbr International Corp. +8414 Connection Manager Tbr International Corp. +8415 For Proxy Host Funk Software +8416 Workstation 3-Lan Advanced Technical Solutions +8417 Server Performance Analisys Banyan Systems Inc. +8418 Server Performance Analisys Banyan Systems Inc. +8419 Server Performance Analisys Banyan Systems Inc. +841A Server Performance Analisys Banyan Systems Inc. +841B Server Performance Analisys Banyan Systems Inc. +841C Server Performance Analisys Banyan Systems Inc. +841D Remote Database Services Interactive Data +841E Remote Database Services Interactive Data +841F Envelope Printer For Network Thuridion Software Engineering +8420 Dacs Office Ii Docunet GMbH +8421 Terminal Emulator - Transmit Intelligent Micro Software Ltd +8422 Terminal Emulator - Receive Intelligent Micro Software Ltd +8423 Praxis +8424 Reflex Compliance Prodigy Services +8425 Reflex Compliance Prodigy Services +8426 Reflex Compliance Prodigy Services +8427 Reflex Compliance Prodigy Services +8428 Reflex Compliance Prodigy Services +8429 Reflex Compliance Prodigy Services +842A IPX Remote Control Function Networth Inc. +842B IPX Remote Control Function Networth Inc. +842C SPX Server-Client Communication At&t Jens Corp. +842D TSR Broadcasting Via IPX At&t Jens Corp. +842E Sita +842F Sita +8430 Instant Recall I Daytimer Technologies +8431 Application Server Fc1 Thomson Financial +8432 Application Server Fc2 Thomson Financial +8433 Application Server Fc3 Thomson Financial +8434 Application Server Fc3 Thomson Financial +8435 Envelope Manager Software Psi Associates +8436 Envelope Manager Software Psi Associates +8437 Envelope Manager Software Psi Associates +8438 Envelope Manager Software Psi Associates +8439 Laa Server Bindary Socket Saber Software +843A 440 IPX Communications Information Builders +843B Vital Signs/lan Server Blueline Software Inc. +843C Vital Signs/lan Server Blueline Software Inc. +843D Envelope Printer For Network Thuridion Software Engineering +843E OS2 Sequel Server IPX/SPX Support Microsoft +843F Asynchronous Serial Communications Black Creek Integrated Systems +8440 Asynchronous Serial Communications Black Creek Integrated Systems +8441 Asynchronous Serial Communications Black Creek Integrated Systems +8442 Asynchronous Serial Communications Black Creek Integrated Systems +8443 Communication Between Sages American Auto Matrix Inc. +8444 TV Broadcast Automation Status Utah Scientific +8445 TV Broadcast Automation Status Utah Scientific +8446 TV Broadcast Automation Status Utah Scientific +8447 Client-Server Version Of Cc Mail Cc Mail +8448 TV Broadcast Automation Status Utah Scientific +8449 Netsprint Digital Products Inc. +844A Workstation Remote Control California Federal +844B Full Text Retrieval Cl/S Impact Italiana Srl +844C Gateway IPX Icot +844D Gateway SPX Icot +844E Workstation IPX Icot +844F Workstation SPX Icot +8450 Network Services IPX Icot +8451 Network Services SPX Icot +8452 Network Logger IPX Icot +8453 Network Logger SPX Icot +8454 Gateway Software Datev Eg +8455 Gateway Software Datev Eg +8456 Novell Inc. +8457 Re:action Concentric Technologies +8458 Re:action Concentric Technologies +8459 Cad Server Isicad +845A Cad Server Isicad +845B ICL Portable Netware ICL +845C Locate Zenith Data Systems +845D BOML Zenith Data Systems +845E Rhotheos Zenith Data Systems +845F Chat Program Intel +8460 Mailslots IBM +8461 Mailslots IBM +8462 File Talk Mountain Network Solutions Inc. +8463 File Talk Mountain Network Solutions Inc. +8464 File Talk Mountain Network Solutions Inc. +8465 File Talk Mountain Network Solutions Inc. +8466 File Talk Mountain Network Solutions Inc. +8467 File Talk Mountain Network Solutions Inc. +8468 Microcom +8469 Microcom +846A Microcom +846B Microcom +846C Microcom +846D Microcom +846E Microcom +846F Microcom +8470 Microcom +8471 Microcom +8472 Microcom +8473 Microcom +8474 Microcom +8475 Microcom +8476 Microcom +8478 Document Mangement Package Perfect Solutions Corp. +8479 Litigation Support Gibson Ochsner & Adkins +847A Monotrex Prime Computer +847B Monotrex Prime Computer +847C Monotrex Prime Computer +847D Monotrex Prime Computer +847E Monotrex Prime Computer +847F Monotrex Prime Computer +8480 Monotrex Prime Computer +8481 Monotrex Prime Computer +8482 Monotrex Prime Computer +8483 Monotrex Prime Computer +8484 Litigation Support Gibson Ochsner & Adkins +8485 Litigation Support Gibson Ochsner & Adkins +8486 Litigation Suuport Gibson Ochsner & Adkins +8487 Argus/n Triticom +8488 Argus/n Triticom +8489 Argus/n Triticom +848A Channel Switcher Application Dynatech Utah Scientific +848B Channel Switcher Application Dynatech Utah Scientific +848C Channel Switcher Application Dynatech Utah Scientific +848D Channel Switcher Application Dynatech Utah Scientific +848E Channel Switcher Application Dynatech Utah Scientific +848F Channel Switcher Application Dynatech Utah Scientific +8490 Channel Switcher Application Dynatech Utah Scientific +8491 Channel Switcher Application Dynatech Utah Scientific +8492 Oxford Information Technology +8493 Gateway Integration Architect Morrisey Associates +8494 Bootware/msd Lanworks +8495 Workgroup Computing Tool Memorex Telex +8496 Workgroup Computing Tool Memorex Telex +8497 Workgroup Computing Tool Memorex Telex +8498 Workgroup Computing Tool Memorex Telex +8499 Workgroup Computing Tool Memorex Telex +849A Workgroup Computing Tool Memorex Telex +849B Workgroup Computing Tool Memorex Telex +84A0 IPX/SPX Sockets Artefact Network Support +84A1 IPX/SPX Sockets Artefact Network Support +84A2 IPX/SPX Sockets Artefact Network Support +84A3 IPX/SPX Sockets Artefact Network Support +84A4 IPX/SPX Sockets Artefact Network Support +84A5 IPX/SPX Sockets Artefact Network Support +84A6 IPX/SPX Sockets Artefact Network Support +84A7 IPX/SPX Sockets Artefact Network Support +84A8 IPX/SPX Sockets Artefact Network Support +84A9 IPX/SPX Sockets Artefact Network Support +84AA Client-Server Driver For IPX/SPX Reference Point Software +84AB Intrak Inc. +84AC Intrak Inc. +84AD Intrak Inc. +84AE Intrak Inc. +84AF Intrak Inc. +84B0 Intrak Inc. +84B1 Database Applications Digital Equipment - Nashua +84B2 Database Applications Digital Equipment - Nashua +84B3 Loader Socket Casper Systems Inc. +84B4 Finder Socket Casper Systems Inc. +84B5 Automated Control System Air Products & Chemicals +84B6 Automated Control System Air Products & Chemicals +84B7 Audit Trail Package Blue Lance Inc. +84B8 Sbackup Enhancement Product Sytron Corp. +84B9 Tape Backup Systems Colorado Memory Systems +84BA QA+ Attention Socket Diagsoft Inc. +84BB Administration Server Mcgill University Fac Of Engin +84BC Administration Server Mcgill University Fac Of Engin +84BD Workstation Communications Symantec Peter Norton Group +84BE Workstation Communications Symantec Peter Norton Group +84BF Network Dynamic Data Exchange Netlogic Inc. +84C0 Asynchronous Communications Server Us Robotics Software +84C1 Software Communications Server Tentera Computer Services +84C2 Forum Send Texas A&m University +84C3 Forum Receive Texas A&m University +84C4 Forum Control Texas A&m University +84C5 Remote Printer Configuration Newgen Systems Corp. +84C6 Audit Trail Package Blue Lance Inc. +84C7 Peer-To-Peer Communications Fujitsu Networks Industry +84C8 Sna Gateway Microsoft +84C9 Sna Gateway Microsoft +84CA Workstation Terminal Access Hsd Hardware Software Developm +84CB Sercomm +84CC De International Ltd +84CD Application Tracking System Automated Interactions Div Of +84CE IBM Host Gateway Idea Courier +84CF Credit Authorization Gateway Merchantec International +84D0 Graphical Hotel Management App Insure Inc. +84D1 Graphical Hotel Management App Insure Inc. +84D2 Network Back-Up Digital Equipment +84D3 Client Server Application Alcon Systems +84D4 Client Server Application Alcon Systems +84D5 Communications Server Sdd Scandinavian Airlines Data +84D6 Information Systems Product Prosoftia Ab +84D7 Information Systems Product Prosoftia Ab +84D8 Information Systems Product Prosoftia Ab +84D9 Information Systems Product Prosoftia Ab +84DA Information Systems Product Prosoftia Ab +84DB Information Systems Product Prosoftia Ab +84DC Information Systems Product Prosoftia Ab +84DD Information Systems Product Prosoftia Ab +84DE Information Systems Product Prosoftia Ab +84DF Information Systems Product Prosoftia Ab +84E0 Object Oriented Database System Ontos Inc. +84E1 Object Oriented Database System Ontos Inc. +84E2 Tape Back-Up For NLM Application Mountain Network Solutions Inc. +84E3 Tape Back-Up For NLM Application Mountain Network Solutions Inc. +84E4 Tape Back-Up For NLM Application Mountain Network Solutions Inc. +84E5 Tape Back-Up For NLM Application Mountain Network Solutions Inc. +84E6 Attachmate Corp. +84E7 Tape Back-Up For NLM Application Mountain Network Solutions Inc. +84E8 Tape Back-Up For NLM Application Mountain Network Solutions Inc. +84E9 Brmsg Network Mail Server Softbridge Inc. +84EA Client Server Monitoring Utility Dell Computer +84EB Synectics For Os/2 Version 2.0 Parallel Pcs Inc. +84EC Synectics For Os/2 Version 2.0 Parallel Pcs Inc. +84ED Information Systems Product Prosoftia Ab +84EE Information Systems Product Prosoftia Ab +84EF Information Systems Product Prosoftia Ab +84F0 Information Systems Product Prosoftia Ab +84F1 Information Systems Product Prosoftia Ab +84F2 Information Systems Product Prosoftia Ab +84F3 Information Systems Product Prosoftia Ab +84F4 Information Systems Product Prosoftia Ab +84F5 Information Systems Product Prosoftia Ab +84F6 Information Systems Product Prosoftia Ab +84F7 Information Systems Product Prosoftia Ab +84F8 Information Systems Product Prosoftia Ab +84F9 Information Systems Product Prosoftia Ab +84FA Information Systems Product Prosoftia Ab +84FB Netscribe Meridian Data Corp. +84FC Netscribe Meridian Data Corp. +84FD Netscribe Meridian Data Corp. +84FE Netscribe Meridian Data Corp. +84FF Fax Server Ferrari Electronic GMbH +8502 Fourth Shift Mfg Add-On Package Computer Aided Business Sol +8503 Computer Aided Business Sol +8504 Computer Aided Business Sol +8505 Computer Aided Business Sol +8506 Computer Aided Business Sol +8507 Computer Aided Business Sol +8508 Computer Aided Business Sol +8509 Computer Aided Business Sol +850A Modem-Sharing Software - Dos Lansource Technologies +850B Modem-Sharing Software - Windows Lansource Technologies +850C Telephone Answering System A&m Communications +850D File/IPX-Based RPC System +850E Network Disc Back-Up Software Fortunet Inc. +850F Archives & Museum Management Cactus Software +8510 Fax Server Extended Systems +8511 Teletext Service University Of Plymouth +8512 Network Error Log University Of Plymouth +8513 Measureservers And Measureclients Advantech Benelux Bv +8514 3270 Netware For SAA Emulator Forvus Research Inc. +8515 3270 Netware For SAA Emulator Forvus Research Inc. +8516 Data Collection (Ws) Network Security Systems +8517 Database Mgr (Ws) Network Security Systems +8518 Database Gateway Information Builders +8519 Fax Server Extended Systems +851A Remote Printer Console Peerless Group +851B Batchfiler Jovandi International Inc. +851C Time Synchronization Jovandi International Inc. +851D Fax Server Login Socket Ascom Telecommunication Ltd +851E Fax Server - Cas Request Socket Ascom Telecommunication Ltd +851F Fax Server - Workstation Utility Ascom Telecommunication Ltd +8520 Fax Server - Faxbios Requests Ascom Telecommunication Ltd +8521 Fax Server - Faxserver Ascom Telecommunication Ltd +8522 Dfdsm Data Facilities Data Storage IBM +8523 Relational Database Gupta Technologies +8524 Report Server Stats Central Point Software +8525 Report Server Stats Central Point Software +8526 Cl/S CDC Program Supprt +8529 Document Processing Server NLM Boss Logic Inc. +852A Document Processing Server NLM Boss Logic Inc. +852B Document Processing Server NLM Boss Logic Inc. +852C Document Processing Server NLM Boss Logic Inc. +852D Document Processing Server NLM Boss Logic Inc. +852E Document Processing Server NLM Boss Logic Inc. +852F Financial Markets Information Srvr At Financial +8530 Email Notification On Technology +8531 Netview Support Memorex Telex +8532 Lanlord Product Microcom Client Server Technol +8533 Rts Terminal Emulation Data Research & Applications +8534 Rscf Client-Server Api Data Research & Applications +8535 CD Networker IPX Version Lotus +8536 SQL Server IPX/SPX Hidden Server Microsoft +853D Database Lock Server High Aspect Development +853E Message Manager LanCo Pty Ltd +853F Object Manager LanCo Pty Ltd +8540 Object Agent LanCo Pty Ltd +8541 Request Manager LanCo Pty Ltd +8544 Workstation 4-Lan Advanced Technical Solutions +8548 Internet Gateway Metascybe Systems Ltd +8549 Intelligent Host Gateway American Airlines Decision Tec +854C Wireless Lan IBM +854D Multi-System Mgr IBM +854E Netprint Calling Channel Interlink Communications Ltd +854F Netprint Working Channel Interlink Communications Ltd +8550 Remote Session Distributed Systems Tech +8551 Remote Session Distributed Systems Tech +8552 Remote Session Distributed Systems Tech +8553 Remote Session Distributed Systems Tech +8554 Peer-To-Peer Communications Fujitsu Networks Industry +8555 Diagnostic Utility Fujitsu Networks Industry +8556 NLM Health Monitor Presoft Architects +8557 Crisler Mcgee Crisler Mckee Software Develop +8558 Crisler Mcgee Crisler Mckee Software Develop +8559 Workstation Communication Intel +855A Policy Engine Emerald Systems +855B Policy Engine Emerald Systems +855C Policy Engine Emerald Systems +855D Policy Engine Emerald Systems +855E Policy Engine Emerald Systems +855F Policy Engine Emerald Systems +8560 Stand-Alone Print Server Bay Technical Associates +8561 Service Distribution Us West Advanced Technologies +8562 Service Distribution Us West Advanced Technologies +8563 Service Distribution Us West Advanced Technologies +8564 Service Distribution Us West Advanced Technologies +8565 Service Distribution Us West Advanced Technologies +8566 Service Distribution Us West Advanced Technologies +8568 Mprst Peer-To-Peer Us Sprint +8569 Mprst Broadcast Us Sprint +856A Lan Assist Plus Remote Control Microtest +856B Lan Assist Plus Remote Control Microtest +856C Lan Assist Plus Remote Control Microtest +856D Lan Assist Plus Remote Control Microtest +856E Map Assist Peer-To-Peer Microtest +856F Map Assist Peer-To-Peer Microtest +8570 Map Assist Peer-To-Peer Microtest +8571 Map Assist Peer-To-Peer Microtest +8572 Asynchronous Comms Servers US Robotics Software +8573 Database Server Fair Com +8574 NLM-Based Database Engine Auto Graphics Inc. +8575 Tiger Quote Server Requests Joshua Group Ltd +8576 Tiger Quote Broadcast Joshua Group Ltd +8577 User Socket Casper Systems Inc. +8578 Ghost Socket Casper Systems Inc. +8579 Remote Procedure Protocol Fortunet Inc. +857A Eicon Interconnect Server Eicon Technology +857B Eicon Security Agent Eicon Technology +857C Cost Recovery Server Vincent Larsen +857D Cost Recovery Server Vincent Larsen +857E Pc-Based Sna Gateway Ungermann Bass +857F Print Server Nissin Electric Co Ltd +8580 Peer-To-Peer Messaging Hans Spatzier +8581 Banking Dealing Rooms Art & Science Ltd +8582 Banking Dealing Rooms Art & Science Ltd +8583 Banking Dealing Rooms Art & Science Ltd +8584 Banking Dealing Rooms Art & Science Ltd +858E Printing Client Utility Tokyo Denshi Sekkei Kk +8590 Network Workstation Control Western Pacific Technologies +8591 Teletext Server Tevescom +8592 Print Server Foresyte Technologies +8599 Wan Networks Prosoftia Ab +859A Wan Networks Prosoftia Ab +859B Wan Networks Prosoftia Ab +859C Wan Networks Prosoftia Ab +859D Wan Networks Prosoftia Ab +859E Wan Networks Prosoftia Ab +859F Wan Networks Prosoftia Ab +85A0 Wan Networks Prosoftia Ab +85A1 Wan Networks Prosoftia Ab +85A2 Wan Networks Prosoftia Ab +85A7 Database Server Softwright Systems +85A8 Change Control Product Occidental Petroleum SVCs Inc. +85A9 Change Control Product Occidental Petroleum SVCs Inc. +85AA Statistic Management Multitech +85AB Statistic Management Multitech +85AC Remote Control Software Multitech +85AD Remote Control Software Multitech +85AE Multitech +85AF Remote Access Server DCA +85B0 Windows-Based Fax System Iconographic Systems +85B1 Print Server Add-On Intel +85B2 AGV Controller Communications Control Engineering +85B3 Index Sequential Access NLM Infopoint Systems +85B4 Associative Index Server Infopoint Systems +85B5 Stressmagic Server Utility Net Magic Systems Inc. +85B6 Network Power Tools Net Magic Systems Inc. +85B7 Document Management SVC Imagery Software Inc. +85B8 Image Management SVC Imagery Software Inc. +85B9 Mass Storage SVC Imagery Software Inc. +85BA Citrix Application Server Citrix Systems +85BB Citrix Application Server Citrix Systems +85BC Klos Technologies Inc. +85BD Hospital Management Package Softwork GMbH +85BE Router Management Application Cisco Systems +85BF Network Modem Nanagram +85C0 Network Modem Nanagram +85C4 5250 Gateway Communications Micro Integration +85C5 Software Distribution Suite Centera Pty Ltd +85C6 Software Distribution Suite Centera Pty Ltd +85C7 Software Distribution Suite Centera Pty Ltd +85C8 Software Distribution Suite Centera Pty Ltd +85C9 Tobit !team - Remote Controlling Tobit Software GMbH +85CA Faxware 3.0 - API Communication Tobit Software GMbH +85CB Tobit Plz5 - Database Server Tobit Software GMbH +85CC Avl NLM Database Aetna Life & Casualty +85CD Lu6.2 Gateway Aetna Life & Casualty +85CF Teli-Link Voice Server Computer & Communications Co +85D0 Remote Download Software Asante Technologies +85D5 Lan Expanders & Data Transfer Gateway Communications Inc. +85D6 Calendar Server Campbell Services +85D7 CA Unicenter Computer Associates +85D8 CA Unicenter Computer Associates +85D9 CA Unicenter Computer Associates +85DA CA Unicenter Computer Associates +85DB CA Unicenter Computer Associates +85DC CA Unicenter Computer Associates +85DD Net Modem SPX Socket Practical Peripherals Inc. +85DE Net Modem IPX Socket Practical Peripherals Inc. +85DF Alert Server NLM Central Point Software +85E0 Message Router Central Point Software +85E1 Optical File Server Communications Pegasus Disk Technologies Inc. +85E2 Optical File Server Login/logout Pegasus Disk Technologies Inc. +85E5 Print Server Rasterops Printer Tech Div +85E6 Quark Express Quark Inc. +85E7 Security NLM Soft Solutions +85E8 Endpoint Mapper For Rpc Microsoft +85E9 Pc Anywhere/netware Lite Symantec Corp. +85EA Interserver File Copying Bankers Trust Co +85EB Connection Services Rabbit Software Corp. +85EC Discovery Services Rabbit Software Corp. +85ED Network Monitor Services Rabbit Software Corp. +85EE Ca-Datacom/pc Computer Associates +85EF Ca-Idms/pc Computer Associates +85F1 Industrial Control Automation Tele Denken +85F2 Print Server Ringdale Uk Ltd +85F3 Communications Server Csb Systems GMbH +85F6 Database Datagram Socket Lync Inc. +85F7 Netport Express Status Responder Intel +85F8 Cadence Time Sync. Polygon Inc. +85F9 Cadence Time Sync. Polygon Inc. +85FA Remote Virus Scanning Mcafee Associates +85FB Remote Memory Control Mcafee Associates +85FC Norton Back-Up Device Sharing Astora Software Inc. +85FD Sams:expert Sterling Tefen Lab +85FE Sams:control Sterling Tefen Lab +85FF Sams:vantage Sterling Tefen Lab +8600 Sams:save Sterling Tefen Lab +8601 Sams:dispatcher Sterling Tefen Lab +8602 Rendezvous IPX Greyhouse Technologies +8603 Rendezvous IPX Greyhouse Technologies +8604 Rendezvous SPX Greyhouse Technologies +8605 Rendezvous SPX Greyhouse Technologies +8606 Voice/fax Responding Machine System Sophia +8608 Share Mode Broadcast Addstor +8609 IPX Encapsulated Rm3 Packets Cayman Systems Inc. +860A Database Service Trifox Inc. +860B Imagesolve ofs Imagesolve International +860C Techra Kvatro As +860D Docra Kvatro As +860E Network Management Application Wandel & Goltermann +860F Network Management Application Wandel & Goltermann +8610 Network Management Application Wandel & Goltermann +8611 Network Management Application Wandel & Goltermann +8612 Network Management Application Wandel & Goltermann +8613 Network Management Application Wandel & Goltermann +8614 Connection Acceptance Socket Chancery Software Ltd +8615 Optidriver-Net Optisys +8616 Edm Client/pc Computer Vision +8617 Video Conferencing Lloyd Allan Corp. +8618 Printer Gateway/peer-To-Peer Comm. Adacom Group +861C Communications Utility Lexmark International Inc. +861D Communications Utility Lexmark International Inc. +861E Print Server Lexmark International Inc. +861F Print Server Lexmark International Inc. +8620 Phone System Control Dash Open Phone Systems +8621 Phone System Control Dash Open Phone Systems +8622 Phone System Control Dash Open Phone Systems +8623 Phone System Control Dash Open Phone Systems +8624 Oversight Agent Network Utilities Software Ltd +8625 Oversight Master Network Utilities Software Ltd +8626 Erl Database Server Silver Platter Information Ltd +8627 Erl Directory Server Silver Platter Information Ltd +8628 IPX Broadcast Intertech Imaging Corp. +8629 SPX Connect Intertech Imaging Corp. +862A Ziff Proprietary Services Ziff Information Services +862B Games Looking Glass +862C Lpt Ports Lexmark International Inc. +862D Lpt Ports Lexmark International Inc. +862E Lpt Ports Lexmark International Inc. +862F Lpt Ports Lexmark International Inc. +8630 Lpt Ports Lexmark International Inc. +8631 Lpt Ports Lexmark International Inc. +8632 Lpt Ports Lexmark International Inc. +8633 Lpt Ports Lexmark International Inc. +8634 Time Server Broadcast Socket Meinberg Funkuhren +8635 Acceleration Data Sable Technology Corp. +8636 Callpath IBM +8637 Callpath IBM +8638 Communication Integrator - SPX Covia Corp. +8639 Communication Integrator - IPX Covia Corp. +863B Information Mgmt Systems Intuitive Solutions +863C Information Mgmt Systems Intuitive Solutions +863D Information Mgmt Systems Intuitive Solutions +863E Information Mgmt Systems Intuitive Solutions +863F Arts Rlogin Application American Real Time, Reuters Co +8640 Arts Generic Server American Real Time, Reuters Co +8641 Netop Program Danware Data As +8642 Netop Program Danware Data As +8643 Netop Program Danware Data As +8644 Netop Program Danware Data As +8645 Netop Program Danware Data As +8646 Document Management System Sr Associates/cybermedia +8647 Security Check Mcafee Associates +8648 Newswire Notification Generation Technologies Corp. +8649 P-Net Gateway Proces Data Silkeborg Aps +864A Virtual Manufacturing Device Proces Data Silkeborg Aps +864B Sales Application Proxim Inc. +864C Broadcasts Remuera Corp. +864D Communications Remuera Corp. +864E Lan/cd Rom Server Logicraft +8650 Server Socket Knight Ridder Financial Inc. +8651 Ping Socket Knight Ridder Financial Inc. +8652 Broadcast Datagram Socket Knight Ridder Financial Inc. +8653 Empower Link Application Loader Network Security Systems +8654 Name Resolution Intelec Systems Corp. +8655 Message Line Norman Data Defanse Systems +8656 Client Message Line Norman Data Defanse Systems +8657 Cd Sharing On Novell Workstation Cross International Corp. +8658 Nettalk Lan Communications Swre Cross International Corp. +8659 Telephone Communications Software Cross International Corp. +865A Lan Chatting Cross International Corp. +865B Network Management Server Pole Position Software GMbH +865C Remote Access Socket #1 Traveling Software +865D Remote Access Socket #2 Traveling Software +865E Net Trax Administration Net X Corp. +865F Net Trax Agent Net X Corp. +8660 Net Trax Alarm Monitor Net X Corp. +8661 AO Client Icl Personal Systems Oy +8662 AO Server For Client Icl Personal Systems Oy +8663 AO Directory Server Icl Personal Systems Oy +8664 AO Server Alarmer Icl Personal Systems Oy +8665 AO Client Alarmer Icl Personal Systems Oy +8666 AO Lan Rts Icl Personal Systems Oy +8667 AO Remote Cmd Server Icl Personal Systems Oy +8668 AO Remote Cmd Client Icl Personal Systems Oy +8669 AO Dir Join Server Icl Personal Systems Oy +866A AO Storage Server For Client Icl Personal Systems Oy +866B Save Utiltiy/2 IBM +866C Save Utiltiy/librarian IBM +866D Save Utiltiy/curator IBM +866E Save Utiltiy/janitor IBM +866F Save Utiltiy/archives I IBM +8670 Save Utiltiy/archives Ii IBM +8671 Save Utiltiy/archives Iii IBM +8672 Save Utiltiy/archives Iv IBM +8673 Save Utiltiy/archives V IBM +8674 Save Utiltiy/archives Vi IBM +8675 Save Utiltiy/archives Vii IBM +8676 Save Utiltiy/archives Viii IBM +8677 Safeserver Omnitech Corp.orate Solutions +8678 Client NLM Communications Software Security Inc. +8679 NLM Server To Server Software Security Inc. +867A File Transfer Application Urs Zurbuchen +867B Sd Rom Jukebox Command Server Todd Enterprises Inc. +867C Courseware Server First Class Systems +867D UDP Over IPX Transmit Synoptics +867E UDP Over IPX Receive Synoptics +867F Realtime Voice Comm. Software Vocaltec Inc. +8681 Person To Person Product IBM +8682 Net Tune Hawknet Inc. +8683 Net Tune Hawknet Inc. +8684 Net Tune Hawknet Inc. +8685 Net Tune Hawknet Inc. +868A Access Control & License Mngmnt Dallas Semiconductor +868B Stand-Alone Print Server Sercomm +868C Cd-Vine Peer-To-Peer Comms Info Line +868D Ftp Central Point Software +868E Tftp Central Point Software +868F Boot Ps Central Point Software +8690 Boot Pc Central Point Software +8692 Client To Server Communication Nbs Systems Inc. +8693 Server To Server Communication Nbs Systems Inc. +8694 Notification Purposes Nbs Systems Inc. +8695 Future Expansion Nbs Systems Inc. +8696 Future Expansion Nbs Systems Inc. +8697 Sna Services Network Controls International +8698 Sna Services Network Controls International +869A Protocol IPX Ost-Ouest Standard Telematique +869B Protocol IPX Ost-Ouest Standard Telematique +869C Multi-Player Game - Doom ID Software +869D Network Management Hewlett Packard +869E Network Management Hewlett Packard +869F Distribution Services Discovery IBM +86A0 Document Management Package Soft Solutions +86A1 Document Management Package Soft Solutions +86A2 Document Management Package Soft Solutions +86A3 Document Management Package Soft Solutions +86A8 Central Monitoring System Talx Corp. +86A9 Central Monitoring System Talx Corp. +86AA Central Monitoring System Talx Corp. +86AB Central Monitoring System Talx Corp. +86AC Central Monitoring System Talx Corp. +86AD End Point Mapper Microsoft +86AE End Point Mapper Microsoft +86AF End Point Mapper Microsoft +86B0 Calendar Server Campbell Services Inc. +86BA Network Management Application Xircom +86BB Network Management Application Xircom +86BC Network Management Application Xircom +86BD Network Management Application Xircom +86BE Network Management Application Xircom +86BF Network Management Application Xircom +86CE Service Location Protocol Eicon Technology +86CF Twinscope For IPX Nippon System Kaihutsu +86D0 Major Bbs Software Galacticomm Inc. +86D1 Major Bbs Software Galacticomm Inc. +86D2 Major Bbs Software Galacticomm Inc. +86D3 Major Bbs Software Galacticomm Inc. +86D4 Major Bbs Software Galacticomm Inc. +86D5 Major Bbs Software Galacticomm Inc. +86D6 Major Bbs Software Galacticomm Inc. +86D7 Major Bbs Software Galacticomm Inc. +86D8 Major Bbs Software Galacticomm Inc. +86D9 Major Bbs Software Galacticomm Inc. +86DC Faxware 3.0 (c-Req) Tobit Software GMbH +86DD Faxware 3.0 (hs-Comm) Tobit Software GMbH +86DE Faxware 3.0 (tld) Tobit Software GMbH +86DF High Performance Comm Srv Tobit Software GMbH +86E0 High Performance Comm Srv Tobit Software GMbH +86E1 High Performance Comm Srv Tobit Software GMbH +86E2 High Performance Comm Srv Tobit Software GMbH +86E3 High Performance Comm Srv Tobit Software GMbH +86E4 High Performance Comm Srv Tobit Software GMbH +86E5 Electronic Spelling Book Tobit Software GMbH +86E6 Electronic Spelling Book Tobit Software GMbH +86E7 Electronic Spelling Book Tobit Software GMbH +86E8 Electronic Spelling Book Tobit Software GMbH +86F1 Office Extend Server Fransen King +86F2 Windows Nt Facsys Server Optus Information Systems +86F3 Windows Nt Facsys Server Optus Information Systems +86F4 Windows Nt Facsys Server Optus Information Systems +86F7 Keyfile Name Service Keyfile Corp. +86F8 Evergreen Management Agent Goodall Software +86F9 Evergreen Management Agent Goodall Software +86FA Evergreen Management Agent Goodall Software +86FB Evergreen Management Agent Goodall Software +86FC Evergreen Management Agent Goodall Software +86FD Evergreen Management Agent Goodall Software +86FE File Synchronizition Nomadic Systems +86FF Windows Bulletin Board System Pacer Software +8702 Lan Netview Management Utilities IBM +8703 Lan Netview Management Utilities IBM +8704 Lan Netview Management Utilities IBM +8705 Lan Netview Management Utilities IBM +8706 Lan Netview Management Utilities IBM +8707 Ethernet-Managed Stackable Hub IBM +8708 Document Management Package Soft Solutions +8709 Document Management Package Soft Solutions +870A Document Management Package Soft Solutions +870B Document Management Package Soft Solutions +870C Document Management Package Soft Solutions +870D Goodall Virtual Protocol Adaptor Goodall Software +870E Goodall Virtual Protocol Adaptor Goodall Software +870F Goodall Virtual Protocol Adaptor Goodall Software +8710 Goodall Virtual Protocol Adaptor Goodall Software +871D Pinnacle Relational Engine Vermont Database Corp. +871E Fault Tolerance Clone Star Software +8724 Unix Mail Server Felpausch +8727 Industiral Test & Handling Eq Q Corp. +8728 Industiral Test & Handling Eq Q Corp. +8729 Industiral Test & Handling Eq Q Corp. +872A Teli-Link Voice Server Computer & Communications Co +872B Secure Fax Client Socket Russell Consulting +8733 Bridge Router Menu Connection Networks Northwest Inc. +8734 Bridge Router Error Log Networks Northwest Inc. +8735 Image Server Connection Watermark Software +8737 Exsekey Interface Clover Informatica Snc +8738 Metering Program Secure Design +873C Comet Terminal Server Goodall Software +873D Comet Terminal Server Goodall Software +873E Comet Terminal Server Goodall Software +873F Comet Terminal Server Goodall Software +8740 Comet Terminal Server Goodall Software +8741 Comet Terminal Server Goodall Software +8742 Comet File Server Goodall Software +8743 Comet File Server Goodall Software +8744 Comet File Server Goodall Software +8745 Comet File Server Goodall Software +8746 Comet File Server Goodall Software +8747 Comet File Server Goodall Software +874A Maxserv Communications Maxserv +874B Maxserv Communications Maxserv +874C NCP Communication - Job Scheduler Simware +874E Trace Route 3com +874F Client Data Share Protocol At&t +875B Tvi Desktop Server Target Vision +875C Mastershow Target Vision +875D User-Access Server Tmd Consulting +875E TCP Gateway Tmd Consulting +875F Cam Server Tmd Consulting +8760 Cam Secure Database Tmd Consulting +8761 Cam Resource Manager Tmd Consulting +8762 Cam Back-Up Tmd Consulting +8763 Firefox NLMs And Client Software Firefox Communications Ltd +8764 Firefox NLMs And Client Software Firefox Communications Ltd +8765 Firefox NLMs And Client Software Firefox Communications Ltd +8766 Firefox NLMs And Client Software Firefox Communications Ltd +8767 Firefox NLMs And Client Software Firefox Communications Ltd +8768 Firefox NLMs And Client Software Firefox Communications Ltd +8769 Firefox NLMs And Client Software Firefox Communications Ltd +876A Firefox NLMs And Client Software Firefox Communications Ltd +876B Firefox NLMs And Client Software Firefox Communications Ltd +876C Firefox NLMs And Client Software Firefox Communications Ltd +876D Data Transactions Tenfore Research & Development +876E Flow Control Tenfore Research & Development +8770 Remote Control Product IBM +8771 Remote Control Product IBM +8772 Remote Control Product IBM +8773 Remote Control Product IBM +877C Hitecsoft Send Socket Hitecsoft Corp. +877D Hitecsoft Receive Socket Hitecsoft Corp. +877E Lan School For Windows Lan Fan Technologies +877F Remote Access Protocol For Desktop Intel - American Fork +8786 Daytimer Organizer Daytimer Technologies +8787 Video Server Corel Systems Corp. +8788 Lan Performance Mngmt Tool IPX/SPX Digital Equipment Corp. +8789 Lan Performance Mngmt Tool TCP Digital Equipment Corp. +878B Status Socket National Software Development +878C Production Socket National Software Development +878D A Non-Name-Pipe Server Team Development Corp. +9000 NP/SQL Server Novell Inc. +9001 Wide Area Router Novell Inc. +9002 Wide Area Router Novell Inc. +9003 Wide Area Router Novell Inc. +9004 Wide Area Router Novell Inc. +9005 Wide Area Router Novell Inc. +9006 Wide Area Router Novell Inc. +9007 Wide Area Router Novell Inc. +9008 Wide Area Router Novell Inc. +9009 Wide Area Router Novell Inc. +900A Wide Area Router Novell Inc. +900B Wide Area Router Novell Inc. +900C Wide Area Router Novell Inc. +900D 386 Profiler Novell Inc. +900E Portable Netware Internal Wat Novell Inc. +900F Smnp Over IPX Novell Inc. +9010 Smnp Over IPX Novell Inc. +9012 Software Distribution Phaser Systems +9013 Software Distribution Phaser Systems +9014 Software Distribution Phaser Systems +9015 Software Distribution Phaser Systems +9016 Software Distribution Phaser Systems +9017 Chat - Windows Novell Inc. +9019 Rpc Bind Novell Inc. +901A Rpc Bind Novell Inc. +901E Novell Inc. +901F Netware 'slurpy' Novell Inc. +9021 SPX Connection Novell Inc. +9022 Job Server Novell Inc. +9023 Netware 'slurpy' Novell Inc. +9024 Netware 'slurpy' Novell Inc. +9025 Netware 'slurpy' Novell Inc. +9026 Netware 'slurpy' Novell Inc. +9027 Novell Inc. +9028 Network Management Novell Inc. +9029 Novell Inc. +902A Novell Inc. +902B Novell Inc. +902C Network Management Client Info Novell Inc. +902D Novell Inc. +902E Dos Target Service Agent Novell Inc. +902F Novell Inc. +9030 Superlab Automation Server Novell Inc. +9031 Rpc Bind Novell Inc. +9032 IPX Biff Univel +9033 IPX Bootpc Univel +9034 IPX Bootps Univel +9035 IPX Chargen Univel +9036 IPX Daytime Univel +9037 IPX Discard Univel +9038 IPX Echo Univel +9039 IPX Eprc Univel +903A IPX Monitor Univel +903B IPX Name Univel +903C IPX Nameserver Univel +903D IPX Netstat Univel +903E IPX New-Rwho Univel +903F IPX Nfsd Univel +9040 IPX Ntp Univel +9041 IPX Qotd Univel +9042 IPX Rmonitor Univel +9043 IPX Route Univel +9044 IPX Syslog Univel +9045 IPX Systat Univel +9046 IPX Talk Univel +9047 IPX Time Univel +9048 IPX Who Univel +9049 IPX Whois Univel +904A SPX Apfs Univel +904B SPX Apts Univel +904C SPX Auth Univel +904D SPX Bftp Univel +904E SPX Chargen Univel +904F SPX Cmip-Agent Univel +9050 SPX Cmip-Manage Univel +9051 SPX Courier Univel +9052 SPX Csnet-Ns Univel +9053 SPX Daytime Univel +9054 SPX Discard Univel +9055 SPX Echo Univel +9056 SPX Exec Univel +9057 SPX Finger Univel +9058 SPX Ftp Univel +9059 SPX Ftp-Data Univel +905A SPX Hostnames Univel +905B SPX Ingreslock Univel +905C SPX Iso-Ip Univel +905D SPX Iso-Tp0 Univel +905E SPX Iso-Tsap Univel +905F SPX Link Univel +9060 SPX Listen Univel +9061 SPX Login Univel +9062 SPX Name Univel +9063 SPX Nameserver Univel +9064 SPX Netstat Univel +9065 SPX Nntp Univel +9066 SPX Ntp Univel +9067 SPX Pcserver Univel +9068 SPX Pop-2 Univel +9069 SPX Print-Srv Univel +906A SPX Printer Univel +906B SPX Qotd Univel +906C SPX Rje Univel +906D SPX Sftp Univel +906E SPX Shell Univel +906F SPX Smtp Univel +9070 SPX Supdup Univel +9071 SPX Systat Univel +9072 SPX Telnet Univel +9073 SPX Time Univel +9074 SPX Ttymon Univel +9075 SPX Uucp Univel +9076 SPX Uucp-Path Univel +9077 SPX Whois Univel +9078 SPX X400 Univel +9079 SPX X400-Snd Univel +907A SPX Xserver0 Univel +907B SMS Novell Inc. +907C SMS Novell Inc. +907D Queue Server For IBM PSf/2 Novell Inc. +907E IPX Socket For Btrieve Requester Novell Inc. +907F Netware For SAA Novell Inc. +9080 Address Server Novell Inc. +9081 Novell MHS DS Gateway For Oce Novell Inc. +9082 NDS Gateway For Oce Novell Inc. +9083 X.400 Protocol Access Module Novell Inc. +9084 Snads Protocol Access Module Novell Inc. +9085 Remote Program Spawning Novell Inc. +9086 IPX Ping Novell Inc. +9087 Enhanced NCP Communications Novell Inc. +9088 Roaming Client Support Novell Inc. +9089 Netware For SAA - Load Balancing Novell Inc. +9093 Monitor Socket Novell Inc. +9094 Datalink Switching (dlsw) Novell Inc. +9095 Remote Control Software Program Novell Inc. diff --git a/NWTP/XIPX/S_HELLO.PAS b/NWTP/XIPX/S_HELLO.PAS new file mode 100644 index 0000000..b2b02b4 --- /dev/null +++ b/NWTP/XIPX/S_HELLO.PAS @@ -0,0 +1,76 @@ +{$X+,B-,V-} +program SendHello; + +{ Simple IPX demonstration program. Run this program on 1 workstation, + run R_HELLO on another. R_HELLO will receive the "hello world" messages + that this program sends. + + Polls the ECB until a packet is sent. No ESR used. } + +uses crt,nwMisc,nwIPX; + +CONST IOSocket=$5678; + +Var SendEcb:Tecb; + IpxHdr:TipxHeader; + socket:word; + dest:TinternetworkAddress; + buf:array[1..546] of byte; + t:byte; + w:word; + s:string; +begin +IF NOT IpxInitialize + then begin + writeln('Ipx needs to be installed.'); + halt(1); + end; +socket:=IOSocket; +IF NOT IPXopenSocket(Socket,SHORT_LIVED_SOCKET) + then begin + writeln('IPXopenSocket returned error# ',nwIPX.result); + halt(1); + end; + +for t:=1 to 4 do dest.net[t]:=$00; { this net / segment } +for t:=1 to 6 do dest.node[t]:=$FF; { all nodes } +dest.socket:=IOsocket; +w:=0; + +Repeat + inc (w); + + { Fill buffer (ECB.fragment[2]^) } + str(w:4,s); + s:=s+' IPX: Hello World'; + FillChar(buf,546,#0); + move(s[1],buf,ord(s[0])); + + { setup ECB and IPX header } + IPXsetupSendECB(NIL,IOsocket,dest,@buf,ord(s[0]), + IpxHdr,SendEcb); + IPXsendPacket(SendEcb); + + { Poll the Inuse Flag until the packet is sent. } + While SendEcb.InUseFlag<>0 + do IPXrelinquishControl; + + { ECB.InUseFlag was lowered, now determine if packet was sent: } + CASE SendEcb.CompletionCode OF + $00:writeln('IPX packet #',w:0,' was sent.'); + $FC:writeln('The send of packet #',w:0,' was canceled.'); + { impossible, as this cancelation to be done by THIS program, and it doesn't } + $FD:writeln('Packet# ',w:0,' is malformed and was not sent.'); + { illegal param: packet length, number of fragments, fragment size. } + $FE:writeln('Packet# ',w:0,' was undelivered. No stations listening.'); + $FF:writeln('Packet# ',w:0,' not sent due to a hardware error.'); + end; + + { Wait 5 seconds between sending packets } + delay(5000); +UNTIL keypressed; + +IF NOT IPXcloseSocket(IOsocket) +then writeln('IPXcloseSocket returned error# ',nwIPX.result); + +end. \ No newline at end of file diff --git a/NWTP/XIPX/S_PEP.PAS b/NWTP/XIPX/S_PEP.PAS new file mode 100644 index 0000000..d2e1c70 --- /dev/null +++ b/NWTP/XIPX/S_PEP.PAS @@ -0,0 +1,125 @@ +{$X+,V-,B-} +program S_PEP; + +{ Testprogram for the nwPEP unit / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +uses crt,nwMisc,nwIPX,nwPEP; { Listener/ Slave } + +{ Listen for incoming packet.. sent acknowledgement of receipt } + +{ Note that the acknowledgement is done automatically by the receiving ESR } + +CONST IOSocket=$5678; + +Var ListenECB :Tecb; + ListenPepHdr :TpepHeader; + + SendECB :Tecb; + SendPepHdr :TpepHeader; + + socket :word; + buf :array[1..546] of byte; + t :byte; + ReceivedBufLen:word; + PacketReceived:boolean; + + NewStack:array[1..1024] of word; { !! used by ESR } + StackBottom:word; { !! used by ESR } + + + +{$F+} +Procedure ListenAndAckHandler(Var p:TPecb); +begin +If (ListenECB.CompletionCode<>0) + or (ListenPepHdr.IPXhdr.packetType<>PEP_PACKET_TYPE) + or (ListenPepHdr.clienttype<>$EA) + then IPXlistenForPacket(ListenECB) + else begin + PacketReceived:=true; + ListenPepHdr.IPXhdr.source.socket:=swap(ListenPepHdr.IPXhdr.source.socket); + { socket is hi-lo in IPX/PEPHeaders. SetupSendECB expects lo-hi } + PEPsetupSendECB(NIL,IOsocket,ListenPepHdr.IPXhdr.source,@buf,0, + SendPepHdr,SendECB); + SendPepHdr.TransactionId:=ListenPepHdr.TransactionId; + SendPepHdr.clientType:=$EA; + IPXsendPacket(SendECB); + end; +end; +{$F-} + +{$F+} +Procedure ListenAndAckESR; assembler; +asm { ES:SI are the only valid registers when entering this procedure ! } + mov dx, seg stackbottom + mov ds, dx + + mov dx,ss { setup of a new local stack } + mov bx,sp { ss:sp copied to dx:bx} + mov ax,ds + mov ss,ax + mov sp,offset stackbottom + push dx { push old ss:sp on new stack } + push bx + + push es { push es:si on stack as local vars } + push si + mov di,sp + + push ss { push address of local ptr on stack } + push di + CALL ListenAndAckHandler + + add sp,4 { skip stack ptr-copy } + pop bx { restore ss:sp from new stack } + pop dx + mov sp,bx + mov ss,dx +end; +{$F-} + + +Var dest:TinternetworkAddress; + ticks,ticks2:word; + +begin +IF NOT IpxInitialize + then begin + writeln('Ipx needs to be installed.'); + halt(1); + end; +socket:=IOSocket; +IF NOT IPXopenSocket(Socket,SHORT_LIVED_SOCKET) + then begin + writeln('IPXopenSocket returned error# ',nwIPX.result); + halt(1); + end; + +{ listen for incoming pep-packet } + +PacketReceived:=False; +{ Empty receive buffer (ListenECB.fragment[2].address^) } +FillChar(buf,546,#0); + +{ Setup ECB and IPX header } +PEPSetupListenECB(Addr(ListenAndAckESR),IOsocket,@buf,546, + ListenPepHdr,ListenECB); + +IPXListenForPacket(ListenECB); +writeln('Listening for incoming packet.'); + +IPXGetIntervalMarker(ticks); +REPEAT + IPXrelinquishControl; + IPXGetIntervalMarker(ticks2) +UNTIL PacketReceived or ((ticks2-ticks)>(30*18)); + +{ do something with received buffer } +IF PacketReceived + then writeln('Packet received and acknowledged.') + else writeln('Timeout: no packet received in 30 seconds.'); + +IF NOT IPXcloseSocket(IOsocket) + then writeln('IPXcloseSocket returned error# ',nwIPX.result); + +end. diff --git a/NWTP/XIPX/TSTRIP.PAS b/NWTP/XIPX/TSTRIP.PAS new file mode 100644 index 0000000..c6fb8c5 --- /dev/null +++ b/NWTP/XIPX/TSTRIP.PAS @@ -0,0 +1,30 @@ +Program tstRIP; + +USES NwMisc,NwIPX,NwRIP; + +Var ripInfo:TRIPinfo; + k,n :word; + NetAddr:TnetworkAddress; +begin +IF NOT IPXinitialize + then begin + writeln('IPX must be loaded before this program can be ran.'); + halt(1); + end; +FillChar(NetAddr,4,#$0); { own segment } +n:=GetAllNetworks(NetAddr,ripInfo); + +writeln; +writeln('MAIN: ',n,' network segment(s) found.'); +writeln('Dumping the RIP tables of all found segments...'); +writeln; + +writeln('NetAddr Tcks Hops'); +for k:=1 to n + do begin + write(HexDumpStr(ripinfo[k].address,8)); + write(' ',ripinfo[k].ticks:4); + writeln(' ',ripinfo[k].hops:4); + end; +writeln('End of RIP table dump.'); +end. diff --git a/NWTP/XIPX/VEND_XXX b/NWTP/XIPX/VEND_XXX new file mode 100644 index 0000000..c89e082 --- /dev/null +++ b/NWTP/XIPX/VEND_XXX @@ -0,0 +1,246 @@ +# ----------------------------------------------------------------------- +# VEND_XXX Ethernet Vendor Address Components +# Ethernet Multicast Addresses +# ----------------------------------------------------------------------- + +# This list is in 'First Fit' format, i.e. the first entry that matches +# the ethernet address contains the 'most fitting' description. + +# Example: cardno = 00001B40EF45. First Fit => 00001B4 'NE 2000' +# cardno = 00001B267EA7. First Fit => 00001B 'Novell Eagle' + +000000000001 Netware Server (dynamically assigned address) +000002 BBN +00000C Cisco +00000E Fujitsu +00000F NeXT +000010 Sytek/Hughes LAN systems +000011 Tektronics +000015 Datapoint +000018 Webster +00001A AMD ? +00001B4 Novell Eagle NE2000 (v1.12?) +00001B3 Novell Eagle NE2000 +00001B2 Novell Eagle NE2000 +00001B1 Novell Eagle NE2000 +00001B Novell Eagle +00001D Cabletron +000020 DIAB (Data Intdustrier AB) +000021 SC&C +000022 Visual Technology (Vistec) +000023 ABB +000029 IMC +00002A TRW +00003C Auspex +00003D ATT +000044 Castelle +000046 Bunker Ramo +000049 Apricot +00004B APT +00004F Logicraft +000051 Hob Electronic +000052 ODS +000055 AT & T +00005A S & Koch/ ?Xerox +00005D RCE +00005E IANA +000061 Gateway +000062 Honeywell +000065 Network General +000069 Silicon Graphics +00006B MIPS +00006E Artisoft +000077 MIPS +000078 Labtam +00007A Ardent +00007B Research Machines +00007D Cray Research/ ? Harris +00007F Linotronic +000080 Dowty Network Systems / ? Harris +000081 SynOptics +000084 ? Aquila +000086 Gateway +000089 Cayman Systems (Gatorbox) +00008A Datahouse Information Systems +00008E Jupiter ? Solbourne ? +000093 Proteon +000094 Asante +000095 Sony/Tektronics +000097 Epoch +000098 CrossCom +00009F Ameristar Technology +0000A0 Sanyo Electronics +0000A2 Wellfleet Communications +0000A3 Network Application Technology (NAT) +0000A6 Network General (internal assignment, not for products) +0000A7 NCD (X-terminals) +0000A8 Stratus +0000A9 Network Systems +0000AA Xerox +0000B3 CIMLinc +0000B7 Dove (Fastnet) +0000BC Allen-Bradley +0000C0 Western Digital (WD/SMC) +0000C6 HP Intelligent Networks Operation (formerly Eon Systems) +0000C8 Altos +0000C9 Emulex (Terminal Servers) +0000D7 Dartmouth College (NED Router) +0000D8 3Com? Novell? PS/2 +0000DD Gould +0000DE Unigraph +0000E2 Acer (Counterpoint) +0000EF Alantec +0000FD High Level Hardware (Orion, UK) +000102 BBN (internal usage - not registered) +00082D Siemens Nixdorf: TACLAN ? +001700 Kabel +0020AF 3Com Etherlink III +0040F62 Novell Eagle NE2000 (v1.05EC?) +0040F6 Novell Eagle +00608CF 3Com +00608CB 3Com Etherlink III +00608C5 3Com +00608C 3Com +008029 Novell Eagle ? / NE2000 compat. ? +00802D Xylogics, Inc. Annex terminal servers +00805A0 Tulip NCC16 +00805A1 AMD PCNTNW Ethernet +00805A Tulip +00805C Agilis ? +00808C Frontier Software Development +0080C2 IEEE (802.1 Committee) +0080D3 Shiva +00AA00 Intel (NetPort printserver) +00C002 Trust (printserver) +00DD00 Ungermann-Bass +00DD01 Ungermann-Bass +01005E1 IANA Internet Multicast (RFC-1112) +01005E2 IANA Internet Multicast (RFC-1112) +01005E3 IANA Internet Multicast (RFC-1112) +01005E4 IANA Internet Multicast (RFC-1112) +01005E5 IANA Internet Multicast (RFC-1112) +01005E6 IANA Internet Multicast (RFC-1112) +01005E6 IANA Internet Multicast (RFC-1112) +01005E7 IANA Internet Multicast (RFC-1112) +01005E8 IANA Internet reserved (Multicast) +01005E9 IANA Internet reserved (Multicast) +01005EA IANA Internet reserved (Multicast) +01005EB IANA Internet reserved (Multicast) +01005EC IANA Internet reserved (Multicast) +01005ED IANA Internet reserved (Multicast) +01005EE IANA Internet reserved (Multicast) +01005EF IANA Internet reserved (Multicast) +0180C2000000 Spanning tree (for bridges) (Multicast) +020701 Racal/Micom InterLan +020406 BBN (internal usage - not registered) +026086 Satelcom MegaPac (UK) +02608C 3Com (IBM PC; Imagen; Valid; Cisco) +02CF1F CMC (Masscomp; Silicon Graphics; Prime EXL) +080001 CmpVsn ? +080002 3Com (Formerly Bridge) +080003 ACC (Advanced Computer Communications) +080005 Symbolics (LISP machines) +080006 Siemens-Nixdorf +080007 Apple +080008 BBN +080009 Hewlett-Packard +08000A Nestar Systems +08000B Unisys +080010 AT & T +080011 Tektronix, Inc. +080014 Excelan (BBN Butterfly, Masscomp, Silicon Graphics) +080017 NSC +08001A Data General +08001B Data General +08001E Apollo +080020 Sun (Sun microsystems) +080022 NBI +080025 CDC +080026 Norsk Data (Nord) +080027 PCS Computer Systems GmbH +080028 Texas Instruments (Explorer) +08002B DEC +08002E Metaphor +08002F Prime Computer (Prime 50-Series LHC300) +080036 Intergraph (CAE stations) +080037 Fujitsu-Xerox +080038 Bull +080039 Spider Systems +080041 DCA Digital Comm. Assoc. +080045 Xylogic !? (they claim not to know this number) +080046 Sony +080047 Sequent +080049 Univation +08004C Encore +08004E BICC +080056 Stanford University +080058 DECsystem-20 +08005A IBM +08005C Agilis ?? +080067 Comdesign +080068 Ridge +080069 Silicon Graphics +08006A AT & T +08006E Excelan +080075 DDE (Danish Data Elektronik A/S) +08007C Vitalink (TransLAN III) +080080 XIOS +080086 Imagen/QMS +080087 Xyplex (terminal servers) +080089 Kinetics (AppleTalk-Ethernet interface) +08008B Pyramid +08008D XyVision (XyVision machines) +080090 Retix Inc. (Bridges) +090002040001 ? Vitalink printer (Multicast) +090002040002 ? Vitalink management (Multicast) +090009000001 HP Probe (Multicast) +090009000001 HP Probe (Multicast) +090009000004 HP DTC (Multicast) +09001E000000 Apollo DOMAIN (Multicast) +09002B000000 DEC MUMPS? (Multicast) +09002B000001 DEC DSM/DTP? (Multicast) +09002B000002 DEC VAXELN? (Multicast) +09002B000003 DEC Lanbridge Traffic Monitor (LTM) (Multicast) +09002B000004 DEC MAP End System Hello (Multicast) +09002B000005 DEC MAP Intermediate System Hello (Multicast) +09002B000006 DEC CSMA/CD Encryption? (Multicast) +09002B000007 DEC NetBios Emulator? (Multicast) +09002B00000F DEC Local Area Transport (LAT) (Multicast) +09002B00001 DEC Experimental (Multicast) +09002B010000 DEC LanBridge Copy packets (All bridges) (Multicast) +09002B010001 DEC LanBridge Hello packets (All local bridges) (Multicast) +09002B020000 DEC DNA Lev.2 Routing Layer routers? (Multicast) +09002B020100 DEC DNA Naming Service Advertisement? (Multicast) +09002B020101 DEC DNA Naming Service Solicitation? (Multicast) +09002B020102 DEC DNA Time Service? (Multicast) +09002B03 DEC default filtering by bridges? (Multicast) +09002B040000 DEC Local Area Sys. Transport (LAST)? (Multicast) +09002B230000 DEC Argonaut Console? (Multicast) +09004E000002 ? Novell IPX (Multicast) +090056FF Stanford V Kernel, version 6.0 (Multicast) +090056 ? Stanford reserved (Multicast) +090077000001 Retix spanning tree bridges (Multicast) +09007C020005 Vitalink diagnostics (Multicast) +09007C050001 Vitalink gateway? (Multicast) +0D1E15BADD06 HP (Multicast) +484453 HDS ??? +800010 AT&T +AA0000 DEC - multicast (obsolete) +AA0001 DEC - multicast (obsolete) +AA0002 DEC - multicast (obsolete) +AA0003 DEC - multicast (Global physical address for some DEC machines) +AA0004 DEC - multicast (Local logical address for systems running DECNET) +AA002C Intel ?? (Multicast) +AB0000010000 DEC Maintenance Operation Protocol (MOP) Dump/Load Assistance (Multicast) +AB0000020000 DEC MOP (System ID packets) DEC LanBridge/DEUNA/DELUA/DEQNA) (Multicast) +AB0000030000 DECNET Phase IV (end node Hello packets) DECNET host (Multicast) +AB0000040000 DECNET Phase IV (Router Hello packets) DECNET router (Multicast) +AB00000 Reserved by DEC (Multicast) +AB0001 Reserved by DEC (Multicast) +AB0002 Reserved by DEC (Multicast) +AB0003000000 DEC Local Area Transport (LAT) -old (Multicast) +AB0003 Reserved by DEC (Multicast) +AB000400 Reserved DEC customer private use (Multicast) +AB000401 DEC Local Area VAX Cluster groups Sys. Communication Architecture (SCA) (Multicast) +CF0000000000 Ethernet Configuration Test protocol (Loopback) (Multicast) +FFFFFFFFFFFF (Multicast address - Used by numerous systems - see RFC 1340) diff --git a/NWTP/XLOCK/TSTLRL.PAS b/NWTP/XLOCK/TSTLRL.PAS new file mode 100644 index 0000000..6598fbd --- /dev/null +++ b/NWTP/XLOCK/TSTLRL.PAS @@ -0,0 +1,74 @@ +Program TstLRL; + +{ Example for the nwLock unit / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +{ Tests the following nwLock calls: (Logical Record Locking) + + LogLocalRecord + LockLogicalrecordSet + ReleaseLogicalRecord + ClearLogicalRecordSet + +} + +Uses nwLock; { using this unit will automatically set the locmode to 1 } + +Var RecordName:string; + TimeOutTicks:word; + +begin +TimeOutTicks:=360; {wait 20 seconds before locking process times out} + +writeln('TSTLRL: Test of logical record locking.'); +writeln(' -Start the exe on 2 or more workstations & enjoy the effect.'); +writeln; +writeln('Suppose we want to perform a transaction on a file, where:'); +writeln('-we want to update (read&write) Records # 123, 678 and 056.'); +writeln; +writeln('To do this, this program uses logical record locking in the following manner:'); +writeln('-It locks records #123,678 and 056 in Exclusive mode.'); +writeln(' (denying all attempts at locking by other stations.'); + +{ Put the names of records-to-be-EXCLUSIVELY-locked in the 'logged record set' } +RecordName:='#123'; +IF NOT LogLogicalRecord(RecordName,LD_LOG,0) + then writeln('LogRecord failed. Error# ',nwLock.result); +{ Note: The recordnames used have no linkage whatsover with physical + records. They form a logical representation of a physical record. + All processes involved in locking processes on the same data must + use the same naming convention } +RecordName:='#678'; +IF NOT LogLogicalRecord(RecordName,LD_LOG,0) + then writeln('LogRecord failed. Error# ',nwLock.result); +RecordName:='#056'; +IF NOT LogLogicalRecord(RecordName,LD_LOG,0) + then writeln('LogRecord failed. Error# ',nwLock.result); + +{ Lock all records that are currently stored in the 'logged record set' } +writeln('Attempting to place locks on records.. '); + + +IF NOT LockLogicalRecordSet(LD_LOCK,TimeoutTicks) + then writeln('LockLogicalRecordSet (after TimeOut=20 sec) failed. Error# ',nwLock.result); + +if nwlock.result=0 { locking of records was successful } + then begin + { ---Update records, change records, etc.--- } + + { Readln to simulate update } + writeln('Now ''processing'' locked records. Press RETURN to release locks.'); + readln; + + { Suppose we're done with record #123, but still need the other record + -unlock 1 record; keep record in log } + IF NOT ReleaseLogicalRecord('#123') + then writeln('ReleaseLogicalRecord Failed. Error# ',nwLock.result); + + writeln('Now ''processing'' still locked records'); + end; + +{ unlock all records; clear 'logged record set' } +IF NOT ClearLogicalRecordSet + then writeln('ClearLogicalRecordSet Failed. Error# ',nwLock.result); + +end. diff --git a/NWTP/XLOCK/TSTPFL.PAS b/NWTP/XLOCK/TSTPFL.PAS new file mode 100644 index 0000000..06cc711 --- /dev/null +++ b/NWTP/XLOCK/TSTPFL.PAS @@ -0,0 +1,65 @@ +Program TstPFL; + +{ Example for the nwLock unit / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +{ Tests the following nwLock calls: (Physical File Locking) + + ClearPhysicalFileSet + LogPhysicalFile + LockPhysicalFileSet + ReleasePhysicalFile +} + +Uses nwFile,nwLock; { using this unit will automatically set the locmode to 1 } + +Var FileName:string; + TimeOutTicks:word; + +begin +TimeOutTicks:=360; {wait 20 seconds before locking process times out} + +writeln('TSTLF: Test of logical file locking.'); +writeln(' -Make sure the files referenced to in this example exist.'); +writeln(' -Start the exe on 2 or more workstations & enjoy the effect.'); +writeln; + +{ Put the names of files-to-be-locked in the 'logged file set' } +FileName:='SYS:PUBLIC/SYSCON.EXE'; +IF NOT LogPhysicalFile(FileName,LD_LOG,0) + then writeln('Logfile failed. Error# ',nwLock.result); +{ Note: The files specified have to exist, or an error 156 is + returned. Using EXE files instead of data files can be + a bit confusing in this example, but at least those + files are very likely to be there. } +FileName:='SYS:\PUBLIC\PCONSOLE.EXE'; +IF NOT LogPhysicalFile(FileName,LD_LOG,0) + then writeln('Logfile failed. Error# ',nwLock.result); + { Repeat logging process for other (if needed) } + + +{ Lock all files that are currently stored in the 'logged file set' } +writeln('Attempting to Lock files.. '); +IF NOT LockPhysicalFileSet(TimeOutTicks) + then writeln('LockFileSet (after TimeOut=20 sec) failed. Error# ',nwLock.result); + +if nwlock.result=0 { locking of files was successful } + then begin + { ---Update file, change file, etc.--- } + + { Readln to simulate update } + writeln('Now ''processing'' files. Press RETURN to release locks.'); + readln; + + { Suppose we're done with the 1st datafile, but still need file #2: + -unlock 1 single file; keep in log } + IF NOT ReleasePhysicalFile('SYS:\PUBLIC\SYSCON.EXE') + then writeln('ReleaseFile Failed. Error# ',nwLock.result); + + writeln('Now ''processing'' still locked file'); + end; + +{ unlock all files; clear 'logged file set' } +IF NOT ClearPhysicalFileSet + then writeln('ClearFileSet Failed. Error# ',nwLock.result); + +end. diff --git a/NWTP/XMESS/PMAIL.PAS b/NWTP/XMESS/PMAIL.PAS new file mode 100644 index 0000000..598901f --- /dev/null +++ b/NWTP/XMESS/PMAIL.PAS @@ -0,0 +1,409 @@ +{$X+,B-,V-} {essential compiler directives} + +Unit pmail; + +{Example unit for the nwMess unit / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +INTERFACE + +uses nwMisc,nwBindry,nwMess,nwServ; + {nwserv used for GetFileServerDateAndTime only. } + +CONST {Mail Options} + PM_NO_NOTIFY =$02; + PM_DELIVER_IF_AF=$10; + PM_NO_CONF_REQ =$08; + PM_NO_MAIL =$04; + +Var result:word; + +Function PMailInstalled:boolean; +{ Checks if an object PEGASUS_MAIL of type OT_PEGASUS_MAIL exists + in the bindery. If the object exists, pmail is installed.} + +Function SendMailFile(DestObjectName:string;objType:word; + subject,fileName:string):boolean; +{ PEGASUS MAIL V3.0 Compatible: + + Sends a messagebody textfile (ASCII) to the mail directory of the + destination object. The object can either be a user or a group object. + Wildcards are allowed. + + The destination object will see the calling object as the message + originating object. + + Notes: + -Autoforwarding will be ignored. + -This is a single server function. + -Possible resultcodes: + $0 Success; + + $100 * The given file could not be found. Supply full path and filename. + $101 * User and Group objects only; + $102 ? Error scanning bindery, see Nwbindry.Result for netware error # ; + $110 ? Group has no members / error reading members of a group. + $111 * Group or user object doesn't exist + + $200 * Insufficient privilege to use the mail system. + $201 * You are not allowed to send to groups. + $202 * The supplied receiver user object has no access to mail / + has halted all incoming mail OR + the receiving object equals the sending object. + + -All msgs were sent when the resultcode is $00; + -No msgs are send. (resultcodes marked with *) + -Some or no msgs may have been sent before this error occured.(marked ?) +} + +IMPLEMENTATION{=============================================================} + +Function PMailInstalled:boolean; +Var lastObj :LongInt; + foundObjName:string; + rt :word; + rid :LongInt; + rf,rs :byte; + rhp :Boolean; +begin +{ Checks if an object PEGASUS_MAIL of type OT_PEGASUS_MAIL exists + in the bindery. If the object exists, pmail is installed.} +lastObj:=-1; +PmailInstalled:=ScanBinderyObject('PEGASUS_MAIL',OT_PEGASUS_MAIL,lastObj, + foundObjName,rt,rid,rf,rs,rhp); +end; + +{------------------Send file as message--------------------------------------} + +Type TPmailHdr=record + from,too,date,subject,xmailer:string; + end; + +var senderObjId:LongInt; + warning :byte; + time :TnovTime; + + +Procedure getRandomFileName(Var filename:string); +{ construct a semi-random filename out of the current date & time } +Var tim:TnovTime; + t :byte; +begin +nwServ.GetFileServerDateAndTime(tim); +fileName[0]:=#8; +filename[1]:=chr(tim.month); +filename[2]:=chr(tim.day); +filename[3]:=chr(tim.hour); +filename[4]:=chr(tim.min DIV 2); +filename[5]:=chr(tim.sec DIV 2); +filename[6]:=chr(random(36)); +filename[7]:=chr(random(36)); +filename[8]:=chr(random(36)); +for t:=1 to 8 + do if filename[t]<=#9 then inc(filename[t],ord('0')) + else inc(filename[t],ord('A')-10); +end; + +Function IsObjGroupMember(objId:longInt;GroupName:string):boolean; +Var objName:string; + objType:word; +begin +IsObjGroupMember:=GetBinderyObjectName(objId,objName,objType) + and IsBinderyObjectInSet(GroupName,OT_USER_GROUP,'GROUP_MEMBERS', + objName,OT_USER); +end; + +Function PmailNotifyUser(objName:string):boolean; +{ Read the MAIL_OPTIONS property (created by Pmail) of the destination object. + Structure of the property: + + 01 len Pmail_forwarding_adress_(asciiz) [OPTIONAL] + 02 len Internet_forwarding_adress_(asciiz) [OPTIONAL] + 03 04 extended_features_byte ???_byte [NOT optional] + 04 len Charon 3.5+ sender synonym. [OPTIONAL] + + Notes: -len= 3+length of the next asciiz string (excluding trailing 0) + -the above fields appear within the property in random order. + + If the PM_NO_NOTIFY or the PM_NO_MAIL flag within the extended features + byte is set, then the destination object won't be notified. } +Var segNbr :word; + propValue:Tproperty; + moreSeg :boolean; + propFlags:Byte; + t :word; + fieldFlag:byte; + Notify :boolean; +begin +SegNbr:=1; +warning:=$00; +IF ReadPropertyValue(objName,OT_USER,'MAIL_OPTIONS',SegNbr, + propValue,moreSeg,propFlags) + then begin + t:=1; + + REPEAT + fieldFlag:=propValue[t]; + if fieldFlag<>3 then t:=t+propValue[t+1]; + UNTIL (t>127) or (fieldFlag=3); + + if fieldFlag=3 + then begin + Notify:=((propValue[t+2] and PM_NO_NOTIFY)=0) + and ((propValue[t+2] and PM_NO_MAIL)=0); + if (propValue[t+2] and PM_NO_MAIL)>0 + then warning:=$02; + end; + end + else if nwBindry.result=$EC { empty property, default: notify. } + then Notify:=true + else Notify:=false; { when in doubt, don't notify } +PmailNotifyUser:=Notify; +end; + + +Procedure SendMsgToUser(UserObjID:LongInt;VAR Hdr:TPmailHdr;fileName:string); +{copy file as a msg to the users' mail directory.} +Var userObjName:string; + objType :word; + buffer :array[1..4096] of byte; + bytesRead,bufOffs:word; + MsgFilePath,MailDir,MailFile:string; + Fin,Fout :file; + sendIt,NotifyReceiver:boolean; + MsgFrom :string; +begin +SendIt:=NOT(UserObjId=SenderObjId); { don't mail yourself } + +{ checking Pmail settings.. } +IF IsObjGroupMember(UserObjId,'NOMAILBOX') + then SendIt:=false; + +IsObjGroupMember(UserObjId,'MAILUSERS'); +if (nwBindry.result=$EA) { no such member } + OR IsObjGroupMember(UserObjId,'NOMAIL') + then sendit:=false; + +GetBinderyObjectName(UserObjID,UserObjName,objType); +NotifyReceiver:=PmailNotifyUser(UserObjName); +if warning=$02 { receiving user has PM_NO_MAIL flag raised } + then sendit:=false; + +if sendit + then begin + warning:=$00; + if pos('From',hdr.from)=0 + then Hdr.from:= 'From: '+Hdr.from; + MsgFrom:=Hdr.From; delete(MsgFrom,1,16); + Hdr.too := 'To: '+UserObjName; + if pos('Date',Hdr.date)=0 + then Hdr.date:= 'Date: '+Hdr.date; + if pos('Subj',Hdr.subject)=0 + then Hdr.subject:='Subject: '+hdr.subject; + Hdr.xmailer:='X-mailer: NwTP gateway to Pmail.'; + + bufOffs:=1; + move(hdr.from[1],buffer[bufOffs],ord(hdr.from[0])); + inc(bufOffs,2+ord(hdr.from[0])); + buffer[bufOffs-2]:=13;buffer[bufOffs-1]:=10; { ret/ lf } + move(hdr.too[1],buffer[bufOffs],ord(hdr.too[0])); + inc(bufOffs,2+ord(hdr.too[0])); + buffer[bufOffs-2]:=13;buffer[bufOffs-1]:=10; { ret/ lf } + move(hdr.date[1],buffer[bufOffs],ord(hdr.date[0])); + inc(bufOffs,2+ord(hdr.date[0])); + buffer[bufOffs-2]:=13;buffer[bufOffs-1]:=10; { ret/ lf } + move(hdr.subject[1],buffer[bufOffs],ord(hdr.subject[0])); + inc(bufOffs,2+ord(hdr.subject[0])); + buffer[bufOffs-2]:=13;buffer[bufOffs-1]:=10; { ret/ lf } + move(hdr.xmailer[1],buffer[bufOffs],ord(hdr.xmailer[0])); + inc(bufOffs,2+ord(hdr.xmailer[0])); + buffer[bufOffs-2]:=13;buffer[bufOffs-1]:=10; { ret/ lf } + buffer[bufOffs]:=13;buffer[bufOffs+1]:=10; { empty line } + inc(bufOffs,2); + + MailDir:=HexStr(UserObjId,8); + while maildir[1]='0' do delete(Maildir,1,1); + GetRandomFileName(MailFile); + + {$I-} + MsgFilePath:='SYS:MAIL\'+MailDir+'\'+MailFile+'.CNM'; + assign(Fin,fileName); + reset(Fin,1); + assign(Fout,MsgFilePath); + rewrite(Fout,1); + { buffOfs-1 = number of bytes in buffer already filled } + BlockRead(Fin,buffer[bufOffs],4096-(bufOffs-1),bytesRead); + BlockWrite(Fout,buffer[1],bytesRead+(bufOffs-1)); + REPEAT + BlockRead(Fin,buffer[1],4096,bytesRead); + BlockWrite(Fout,buffer[1],bytesRead); + UNTIL bytesRead<4096; + close(Fin); + close(Fout); + {$I+} + + IF NotifyReceiver + then nwMess.SendmessageToUser(UserObjName, + '(NwTP/Pmail:) You have mail. (From:'+MsgFrom+')') + end + else warning:=$01; +end; + +Procedure SendMsgToGroup(GroupObjName:string;Hdr:TPmailHdr;fileName:string); +Label abrt; +Var NbrOfWrites:word; + i :byte; + + lastObj :LongInt; + foundGroupName:string; + rt :word; + rid :LongInt; + rf,rs :byte; + rhp :boolean; + + SegNbr :byte; + propValue:Tproperty; + moreSeg :boolean; + propFlags:byte; + + objId : LongInt; +begin +NbrOfWrites:=0; +lastObj:=-1; +WHILE ScanBinderyObject(GroupObjName,OT_USER_GROUP,lastObj, + foundGroupName,rt,rid,rf,rs,rhp) +do begin {1} + if (GroupObjName<>'NOMAIL') and (GroupObjName<>'NOMAILBOX') + then begin {3} + SegNbr:=1; + While ReadPropertyValue(foundGroupName,OT_USER_GROUP,'GROUP_MEMBERS', + SegNbr,propValue,moreSeg,propFlags) + do begin {5} + i:=1; + Repeat + objId:=MakeLong((PropValue[i] *256 +PropValue[i+1]), + (PropValue[i+2] *256 + PropValue[i+3] ) ); + if objId<>0 + then begin + SendMsgToUser(objId,Hdr,fileName); + inc(NbrOfWrites); + end; + inc(i,4); + Until (i>128) or (objId=0); + inc(SegNbr); + end; {5} + If nwBindry.Result<>$EC {no such segment} + then begin + Result:=$110; + goto abrt; + end; + end; {3} + end; {1} +if nwBindry.Result<>$FC {no such object} + then begin + result:=$111; + goto abrt; + end; +if NbrOfWrites=0 {no users found} + then result:=$110; + +abrt: ; +end; + + +Function SendMailFile(DestObjectName:string;objType:word; + subject,fileName:string):boolean; +Var secLevel :byte; + senderName:string; + SenderObjType:word; + Hdr :TPmailHdr; + lastObj :longInt; + foundUserName:string; + rt :word; + rf,rs :byte; + rhp :boolean; + DestObjId :longint; + testFile :file; +begin +Warning:=$00; + +{ check: does filename exist? if not, stop right away. error $100 } +{$I-} +assign(testFile,filename); +reset(testFile); +if IOresult<>0 + then begin + result:=$100; + SendmailFile:=False; + exit; + end + else close(testFile); +{$I+} + +GetBinderyAccessLevel(secLevel,senderObjId); +GetBinderyObjectName(senderObjId,senderName,SenderObjType); + +{checking pmail config. groups... } +IsObjGroupMember(senderObjId,'MAILUSERS'); +if (nwBindry.result=$EA) { mailusers group exists, sender not a member } + OR IsObjGroupMember(senderObjId,'NOMAIL') + then begin + result:=$200; { Insufficient privilege to use the mail system. } + SendMailFile:=false; + exit; + end; + +Hdr.from:=senderName; +Hdr.subject:=subject; +GetFileServerDateAndTime(time); +NovTime2String(time,Hdr.date); + +Result:=0; +if objType=OT_USER + then begin + lastObj:=-1; + WHILE ScanBinderyObject(DestObjectName,OT_USER,lastObj, + foundUserName,rt,DestObjID,rf,rs,rhp) + do begin + SendMsgToUser(DestObjId,Hdr,fileName); + end; + IF nwBindry.result<>$FC { no such object } then result:=$102; + end + else if objType=OT_USER_GROUP + then begin + IsObjGroupMember(senderObjId,'GROUPMAIL'); + if (nwBindry.result=$EA) { group groupmail exists, sender not a member } + OR IsObjGroupMember(senderObjId,'NOGROUPMAIL') + then result:=$201 { don't send } + else SendMsgToGroup(DestObjectName,Hdr,fileName) + end + else result:=$101; + +if (warning=$01) and (objType=OT_USER) and (result=$00) + and (pos('*',DestObjectName)=0) and (pos('?',DestObjectName)=0) + then result:=$202; + +SendMailFile:=(result=0); +{ possible resultcodes: + $0 Success; + + $100 * The given file could not be found. Supply full path and filename. + $101 * User and Group objects only; + $102 ? Error scanning bindery, see Nwbindry.Result for netware error # ; + $110 ? Group has no members / error reading members of a group. + $111 * Group or user object doesn't exist + + $200 * Insufficient privilege to use the mail system. + $201 * You are not allowed to send to groups. + $202 * The supplied receiver user object has no access to mail / + has halted all incoming mail OR + the receiving object equals the sending object. + +Note: -All msgs were send when the resultcode is $00; + -No msgs are send. (resultcodes marked with *) + -Some or no msgs may have been send before this error occured.(marked ?) +} +end; + +begin +Randomize; +end. diff --git a/NWTP/XMESS/TSTMESS.PAS b/NWTP/XMESS/TSTMESS.PAS new file mode 100644 index 0000000..ddd35a5 --- /dev/null +++ b/NWTP/XMESS/TSTMESS.PAS @@ -0,0 +1,118 @@ +{$X+,B-,V-} +program test; + +{ Test program for the nwMess unit / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +{ Test the following nwMess functions: + + BroadcastToConsole + GetBroadcastMessage + GetBroadCastMode + SetBroadcastMode + SendBroadcastMessage + SendConsoleBroadcast + +} + +uses nwmisc,nwMess; + +Var t,tbm,bm:byte; + connL,connResultL:TconnectionList; + mess :string; + +Procedure DisplayBrMode(bm:byte); +begin + Case bm of + 00: begin writeln('Server Stores : Netware Messages and User Messages,'); + writeln('Shell automaticly displays messages.') + end; + 01: begin writeln('Server Stores : Server Messages. (User messages discarded)'); + writeln('Shell automaticly displays messages.') + end; + 02: begin writeln('Server stores : Server messages only.'); + writeln('Applications have to use GetBroadCastMessage to see if there is a message.') + end; + 03: begin writeln('Server stores : Server messages and User messages.'); + writeln('Applications have to use GetBroadCastMessage to see if there is a message.') + end; + else writeln('Unknown broadcastMode') + end; {case} +end; + +begin +writeln; +writeln('Testing BroadcastToConsole..'); +writeln('This will make the server beep ! (NOT an error this time)'); +writeln; +writeln(' to continue..'); +readln(mess); + +if BroadcastToConsole('Hello, Console Operator!') + then writeln('BroadcastToConsole: Msg was broadcasted..') + else writeln('Broadcast To Console error:'+hexstr(nwMess.result,2)); + +writeln; +if GetBroadcastMode(bm) + then begin + writeln('GetBroadcastMode: $',HexStr(bm,2)); + DisplayBrMode(bm); + + t:=3; + while (t>=0) and (t<=3) + do if SetBroadcastMode(t) and GetBroadcastMode(tbm) and (tbm=t) + then dec(t) { ok, try next mode, alowed modes: 0,1,2,3 } + else begin + writeln('SetBroadcastMode/GetBroadcastMode test failed.'); + t:=$80; + end; + + if t=byte(-1) + then begin + SetBroadCastMode(bm); { restore old broadcastmode.. } + if nwmess.result=$00 + then begin + writeln; + writeln('SetBroadcastMode tested OK..'); + end + else writeln('SetBroadcastMode error: Old mode couldn''t be restored..'); + end; + end + else writeln('GetBroadcastMode error:'+hexstr(nwMess.result,2)); + +writeln; +for t:=1 to 20 do connL[t]:=t; +IF sendBroadcastMessage('Hello u there!',20,connL,connResultL) + then begin + writeln('SendBroadcastMessage: Msg was broadcasted..'); + writeln('--and displayed at the folowing connections:'); + for t:=1 to 20 do if connResultL[t]=$00 then write(t,' '); + writeln; + end + else writeln('SendBroadcastMessage error:'+hexstr(nwMess.result,2)); + +writeln; +IF SendConsoleBroadcast('Testmessage from Console-operator..',0,connL) + then writeln('SendConsoleBroadcast: console message sent.') + else begin + write('SendConsoleBroadCast: Error '); + if nwMess.result=$C6 + then writeln('! You need to have console privileges..') + else writeln(HexStr(nwMess.result,2)); + end; + +GetBroadCastMode(bm); + +writeln; +if SetBroadCastMode(3) { store all messages at the server, no notification.. } + then begin + writeln('Test of getBroadCastMessage..'); + writeln('--use SEND on another workstation and send a message to this station.'); + writeln; + writeln('Polling for a message.....'); + REPEAT {} UNTIL GetBroadCastMessage(mess); + writeln('Message: ',mess); + end; + +SetBroadCastMode(bm); { restore broadcastmode } + +end. \ No newline at end of file diff --git a/NWTP/XMESS/XPMAIL.PAS b/NWTP/XMESS/XPMAIL.PAS new file mode 100644 index 0000000..977bd05 --- /dev/null +++ b/NWTP/XMESS/XPMAIL.PAS @@ -0,0 +1,21 @@ +{$X+,B-,V-} {essential compiler directives} + +Program xpmail; + +{ Example program for the pmail unit / NwTP 0.6 API. (c) 1993,1994, R.Spronk } + +uses nwMisc,nwBindry,pmail; + +CONST MsgBodyTXTfile='a:testmsg.txt'; + +{ Simple program illustrating the use of the SendMailFile call. } +begin +IF NOT PmailInstalled + then begin + writeln('PMail not installed on the current server.'); + halt(1); + end; +IF NOT SendMailFile('SUPERVISOR',OT_USER, + 'testmessage',MsgBodyTXTfile) + then writeln('Error sending file as mail. err# :',HexStr(pmail.result,4)); +end. \ No newline at end of file diff --git a/NWTP/XOTHER/PHONE.PAS b/NWTP/XOTHER/PHONE.PAS new file mode 100644 index 0000000..3515f12 --- /dev/null +++ b/NWTP/XOTHER/PHONE.PAS @@ -0,0 +1,819 @@ +Program Phone; +{$IFDEF VER70} +{$A+,B-,D-,E-,F-,G-,I-,L-,N-,O-,P+,Q-,R-,S-,T-,V-,X+} +{$ELSE} +{$A+,B-,D-,E-,F-,G-,I-,L-,N-,O-,R-,S-,V-,X+} +{$ENDIF} + +{ Source code for Borland/Turbo Pascal 6/7. + To be compiled with NwTP version 0.6 or higher. + NwTP is a FreeWare Netware Interface for Pascal. +} + +{ Based on the phone.pas program by Eduardo M. Serrat, + as published in Dr.Dobbs #207, November 1993. + + The NwTP units and this adaption of his program are + (c) 1993,1995 by Rene Spronk ,Groningen, the Netherlands. } + +uses dos,crt,nwMisc,nwBindry,nwConn,nwMess,nwServ,nwIPX; + +const Socket = $80C3; + { This socket was assigned by Novell to an IPX Chatprogram by OXXI } + { Don't use this program in conjunction with theirs.. } +Var + SendECB, + ListenECB :TEcb; { Definition of ECBs } + SendIpxHeader, + ListenIPXheader:TIpxHeader; { Definition of IPX Headers } + SendData, + ReadData :Array [1..100] of Byte; { Data area of packets } + readflg :Boolean; { Flag to signal received packets } + + MyConnNbr :Byte; + MyAddress :TinternetworkAddress; + MyName :String; + MyServerId :Byte; + MyServerName :String; + myx,myy :Byte; { my viewport cursor position } + + RconnNbr :Byte; + Raddress :TinterNetworkAddress; + Rname :String; + RfullName :String; + RserverID :Byte; + RserverName :String; + LocalTarget :TnodeAddress; { Node Address of bridge to remote address } + + NewStack :Array[1..256] of Word; { !! used by ESR } + StackBottom :Word; { !! used by ESR } + HeapCheckPtr :pointer; { Pointer that holds heapPointers } + +{---------------------------------------------------------------------------} + +Procedure CheckError(b:Boolean;errCode:Word; mess:String); +begin +IF b + then begin + writeln; + CASE errCode of + { main body: 0000-000F } + $0001:writeln('IPX not installed.'); + $0002:writeln('Error opening socket.'); + { Procedure whoami } + $0010:writeln('Error whilst determining connectionnumber.'); + $0011:writeln('Error determining internet address.'); + $0012:writeln('Error retreiving connection information.'); + { Procedure process input command } + $0022:writeln('Servername ',mess,' is invalid.'); + $0023:writeln('Error interpreting connection number parameter :',mess); + $0025:begin + writeln('The supplied username is not unique,'); + writeln('or the target user isn''t logged in.'); + end; + $0026:writeln('Please select a target user from the above list.'); + $0027:writeln('Phone cancelled.'); + { handshake with sender } + $0032:writeln('Packet received from a user claiming to be ConnectionNumber $',mess); + { Sendbroadcast message in Procedure HandshakeWithreceiver } + $1000: writeln('Error: Broadcasting a message to the target user failed.'); + $10FC: begin + Writeln('The target user is logged in, but appears not to be at his/her workstation:'); + writeln('The (last) message was rejected, message buffer for the target station is full.'); + end; + $10FD: begin + Writeln('The connection number of the target user has become invalid,'); + Writeln('Most likely because the user has logged out.'); + end; + $10FF: begin + Writeln('The target user is logged in, but has blocked incoming messages.'); + end; + else writeln('An unspecified error occurred.'); + end; {case } + if errCode>$000F then IPXcloseSocket(socket); + if errCode>$001F + then begin + SetPreferredConnectionId(MyServerId); + release(HeapCheckPtr); + end; + if ((errCode=$0026) or (errCode=$0027)) + then halt(0) + else halt(1); + end; +end; + +{-----------------------------------------------------------------------------} + +Function Confirm:Boolean; +Var ch:char; +begin +repeat + repeat {} until keypressed; + ch:=readkey; + if ch=#0 then ch:=readkey; +until ch IN ['y','Y','n','N']; +Confirm:=((ch='Y') or (ch='y')) +end; + +{-----------------------------------------------------------------------------} + +{$F+} +Procedure ESRproc; +begin + ReadFlg:=true; +end; + +Procedure ESRHandler; assembler; +asm { ES:SI are the only valid registers when entering this Procedure ! } + mov dx, seg stackbottom + mov ds, dx + + mov dx,ss { setup of a new local stack } + mov bx,sp { ss:sp copied to dx:bx} + mov ax,ds + mov ss,ax + mov sp,offset stackbottom + push dx + push bx + + CALL EsrProc + + pop bx + pop dx + mov sp,bx + mov ss,dx +end; +{$F-} + +{-----------------------------------------------------------------------------} + +Function SameAddress(Var a,b):Boolean; +{ check if networkaddress a and b have the same net and node address } +Type Taddress=Array[1..10] of char; +Var addrA:Taddress ABSOLUTE a; + addrB:Taddress ABSOLUTE b; +begin +SameAddress:=(addrA=addrB); +end; + +{----------------------------------------------------------------------------} + +Function Time:String; + Function LeadingZero(w:Word):String; + Var s : String; + begin + Str(w:0,s); + if Length(s) = 1 + then s := '0' + s; + LeadingZero := s; + end; +Var h, m, s, hund : Word; +begin +GetTime(h,m,s,hund); +Time:=LeadingZero(h)+':'+LeadingZero(m)+':'+LeadingZero(s); +end; + +{-----------------------------------------------------------------------------} +Procedure HandshakeWithReceiver; +const Progress : Array [1..4] of char = ('/','Ä','\','|'); +Var + SecondInd :Word; + ProgressInd :Byte; + x,y :Byte; + KeyNbr :Byte; + ConnUp :Boolean; + + ObjName :String; + ObjType :Word; + ObjId :LongInt; + LogonTime :TnovTime; + + Message :String; + ConnList, + ResultList :TconnectionList; +begin +Writeln('Calling User ',Rname); +Write('Press to cancel [ ]'); +x:=wherex-2; y:=wherey; +Message:='User '+MyName+' is phoning you........... ['+Time+']'; +SecondInd:=0; ProgressInd:=1; + +SetPreferredConnectionId(RserverId); +ConnList[1]:=RconnNbr; +SendBroadcastMessage(message,1,ConnList,ResultList); +Checkerror(nwMess.result>0,$1000,''); +CheckError(ResultList[1]>0,$1000+ResultList[1],''); + +IPXListenForPacket(ListenECB); + +KeyNbr:=$ff; +ConnUp:=False; +FillChar(SendData,SizeOf(SendData),#0); +SendData[1]:=Hi(MyConnNbr); +SendData[2]:=Lo(MyConnNbr); +Move(MyServerName[1],SendData[3],ord(MyserverName[0])); +Move(MyName[1],SendData[50],ord(Myname[0])); + +repeat { send a packet every 4 seconds and a broadcast message every 30 seconds } + gotoxy(x,y); + write(Progress[ProgressInd]); + inc(ProgressInd); + if ProgressInd > 4 + then begin + ProgressInd:=1; + IPXSendPacket(SendECB); + end; + inc(SecondInd); + if SecondInd = 30 + then begin + SendBroadcastMessage(message,1,ConnList,ResultList); + Checkerror(nwMess.result>0,$1000,''); + CheckError(ResultList[1]>0,$1000+ResultList[1],''); + SecondInd:=0; + end; + delay(1000); + if readflg + then begin + writeln('recieved a packet..'); + if not SameAddress(ListenIPXheader.source,Raddress) + then begin + readflg:=false; + IPXListenForPacket(ListenECB); + end + else ConnUp:=TRUE; + end; + if keypressed + then KeyNbr:=ord(readkey); + +until (KeyNbr = $1b) or ConnUp; + +if KeyNbr = $1b + then begin + Writeln; + Write('Wait...'); + Delay(5000); + SendData[1]:=$1b; + IPXSendPacket(SendECB); + message:='The user phoning you canceled the call... ['+Time+']'; + SendBroadcastMessage(message,1,ConnList,ResultList); + IpxCloseSocket(Socket); + SetPreferredConnectionID(MyServerId); + halt(1); + end; +Writeln; +Write('User ',Rname,' answered your call......!'); +delay(1200); +ReadFlg:=false; +end; + +{--------------------------------------------------------------------------} + +Procedure HandshakeWithSender; +const Progress:Array [1..4] of char = ('/','Ä','\','|'); +Var p :Byte; + ObjType :Word; + ObjId :LongInt; + LoginTime:TnovTime; + ticks :Word; + x,y :Word; +begin +Writeln('Listening for calls..'); +Write('Press to cancel [ ]'); +x:=wherex-2; y:=wherey; +IPXListenForPacket(ListenECB); +p:=1; +while(p<=4) and (not ReadFlg) + do begin + gotoxy(x,y); + write(Progress[p]); + delay(1200); + inc(p); + end; +If not readflg + then begin + Writeln; + Writeln('Nobody is Calling you..........'); + writeln; + writeln('( PHONE ? for help )'); + IpxCloseSocket(Socket); + SetPreferredConnectionId(MyServerId); + halt(1); + end +else begin + readflg:=false; + Raddress:=ListenIPXheader.source; + Raddress.socket:=Socket; + RconnNbr:=(ReadData[1]*256)+ReadData[2]; + ZstrCopy(RserverName,ReadData[3],47); + ZstrCopy(Rname,ReadData[50],47); + IPXGetLocalTarget(Raddress,LocalTarget,ticks); + IPXSetupSendECB(NIL, Socket, Raddress, + Addr(SendData), SizeOf(SendData), + SendIPXheader,SendECB); + IPXSendPacket(SendECB); { acknowledge by sending a packet. Packet contents unimportant. } + end; +end; + + +{-----------------------------------------------------------------------------} + +Procedure InitWindows; +Var i: Byte; +begin +ClrScr; +myx:=1; myy:=1; +gotoxy(1,1); +write('É'); for i:=2 to 79 do write('Í'); write('»'); +write('º'); for i:=2 to 79 do write(' '); write('º'); + +gotoxy(3,2); +Write('User: '+MyName+' ° Server: '+MyServerName); +write(' ° Connection: '); write(MyConnNbr); +gotoxy(1,3); +write('È'); for i:=2 to 79 do write('Í'); write('¼'); + +gotoxy(1,13); +write('É'); for i:=2 to 79 do write('Í'); write('»'); +write('º'); for i:=2 to 79 do write(' '); write('º'); + +gotoxy(3,14); +Write('User: '+Rname+' ° Server: '+RserverName); +Write(' ° Connection: '); write(RconnNbr); +Gotoxy(1,15); +write('È'); for i:=2 to 79 do write('Í'); write('¼'); + +gotoxy(26,25); +Write('±±±²²² Phone Utility ²²²±±±'); +gotoxy(1,1); +HighVideo; +end; + +{-----------------------------------------------------------------------------} + +Procedure Talk; + + Function Timeout(w1,w2:Word;sec:Byte):Boolean; + Var lw2:Longint; + begin + if w2sec; + end; + + Procedure MyWindow; + begin + Window(1,5,80,12); + gotoxy(myx,myy); + end; + + Procedure RemoteWindow; + begin + Window(1,17,80,24); + end; + + +Var currMarker, + SendMarker, + ListenMarker:Word; + ch :Char; + RlastChar, + RlastX, + RlastY :byte; +begin +MyWindow; +IPXListenForPacket(ListenECB); +IPXSetupSendECB(NIL, Socket, Raddress, Addr(SendData), 7, + SendIPXheader,SendECB); { make size of sendBuffer smaller } +IPXgetIntervalMarker(SendMarker); +ListenMarker:=SendMarker; +SendData[1]:=$FF; +RlastChar:=$FF; + +REPEAT + if keypressed + then begin + MyWindow; + SendData[4]:=SendData[1]; { append last typed char to packet. } + SendData[5]:=SendData[2]; { original packet may have been lost } + SendData[6]:=SendData[3]; { Remember: IPX is unreliable ! } + ch:=readkey; + if ch=#0 + then begin + ch:=readkey; + CASE ord(ch) of + 75:begin { <- 'cursor left' } + SendData[2]:=myx-1; + if (myx=1) then SendData[2]:=1; + gotoxy(SendData[2],myy); + SendData[3]:=myy; + SendData[1]:=$00; + end; + 77:begin { -> 'cursor right' } + SendData[2]:=myx+1; + if (myx=80) then SendData[2]:=80; + gotoxy(SendData[2],myy); + SendData[3]:=myy; + SendData[1]:=$00; + end; + else SendData[1]:=$FF; + end; {case} + + end + else begin + SendData[1]:=ord(ch); + SendData[2]:=myx; + SendData[3]:=myy; + Case ord(SendData[1]) of + 8 :write(#8+#$20+#8); { backspace } + 13:writeln; { return } + else write(chr(SendData[1])); + end; {case} + end; + myx:=wherex; + myy:=wherey; + IPXSendPacket(SendECB); { send current and previous char } + IPXGetIntervalMarker(SendMarker); + end; + + if readflg + then begin + If SameAddress(ListenIPXheader.source,Raddress) + then begin + if (readData[4]<>$FF) + and ( (readData[4]<>RlastChar) + or (readData[5]<>Rlastx) + or (readData[6]<>Rlasty) + ) + then begin { if we missed a packet, display char now } + RemoteWindow; + Gotoxy(ReadData[5],ReadData[6]); + CASE ReadData[4] of + 0:begin { don't print, cursor movement only } + end; + 8:write(#8+#$20+#8); { backspace } + 13:writeln; { return } + else write(chr(ReadData[1])); + end;{case} + end; + + if ReadData[1]<>$FF + then begin + RemoteWindow; + Gotoxy(ReadData[2],ReadData[3]); + CASE ReadData[1] of + 0:begin { don't print, cursor movement only } + end; + 8:write(#8+#$20+#8); + 13:writeln; + else write(chr(ReadData[1])); + end;{case} + end; + RlastChar:=ReadData[1]; + RlastX :=ReadData[2]; + RlastY :=ReadData[3]; + IPXGetIntervalMarker(ListenMarker); + end; + readflg:=false; + IPXListenForPacket(ListenECB); + end; + + IPXRelinquishControl; + IPXGetIntervalMarker(currMarker); + IF Timeout(SendMarker,currMarker,5) { send an "I'm alive" msg after 5 idle secs } + then begin + SendData[4]:=SendData[1]; { redundant info: append last char to packet. } + SendData[5]:=SendData[2]; + SendData[6]:=SendData[3]; + SendData[1]:=$FF; + IPXSendPacket(SendECB); + IPXGetIntervalMarker(SendMarker); + end; + IF Timeout(ListenMarker,currMarker,17) { fake an "hang-up" if no msgs received during 17 secs } + then begin + ReadData[1]:=$1B; + RemoteWindow; + end; +UNTIL (ReadData[1]=$1b) or (SendData[1]=$1b); { .. until either party has hung up } + +SendData[1]:=$1b; +IPXSendPacket(SendECB); +IpxCloseSocket(Socket); +Writeln; +Writeln; +writeln(''); +Delay(2000); +Window(1,1,80,25); +LowVideo; +gotoxy(80,25); +end; + +{--------------- ProcessInputCommand----------------------------------------} + +Type PusrInfo=^TusrInfo; + TusrInfo=record + ObjName :String[47]; + FullName:String[40]; + ConnId, + ConnNbr :Byte; + Address :TinterNetworkAddress; { socket field not used } + next :PusrInfo; + end; + +Var startInfo:PusrInfo; + +Procedure PushInLL(_objName,_objFullName:String; + _connId,_connNbr:Byte; + _address:TinternetworkAddress); +Var p,m,l:PusrInfo; +begin +p:=startInfo; +new(l); +With l^ + do begin + if _objFullName[0]>#40 + then _objFullName[0]:=#40; + objName:=_objName; + fullName:=_objFullName; + connId:=_connId; + connNbr:=_connNbr; + address:=_address; + next:=NIL; + end; +if p=NIL + then startInfo:=l + else begin + m:=p; + While (p<>NIL) and (p^.objName<=_obJname) + do begin m:=p;p:=p^.next; end; + if p=startInfo + then begin { insert before first LL entry } + l^.next:=startInfo; + startInfo:=l; + end + else begin { insert in LL or append to end } + l^.next:=m^.next; + m^.next:=l; + end; + end; +end; + +Function GetTargetUser:PusrInfo; +{ returns NIL if a target user was not uniquely identified by the user } +Var l :PusrInfo; + serverName :String; + SelectedUsers:Word; + t :Word; + s :String; + ch :char; + Laddr :TinternetworkAddress; + AddrSame :boolean; +begin +{ are all objNames the same? + Yes => multple logins (connNbr must have been supplied) + or login on multiple servers (serverName must h.b. supplied) + No => the supplied userName is not unique. } +l:=startInfo; +SelectedUsers:=0; +IF l<>NIL + then Laddr:=l^.address; +AddrSame:=true; +While (l<>NIL) + do begin + inc(SelectedUsers); + AddrSame:=AddrSame and SameAddress(Laddr,l^.address); + l:=l^.next; + end; +If AddrSame { are all the users essentially the same ? } + then SelectedUsers:=1; + +CASE SelectedUsers of + 0:begin + GetTargetUser:=NIL; + end; + 1:begin { OK! unique users identified } + GetTargetUser:=StartInfo; + end; + else begin + writeln('The target user has multiple connections.'); + writeln('Please give connection number and/or server name of the intended user.'); + writeln; + writeln('Username | Server | Con | Full Name'); + writeln('---------------------+-----------------+-----+----------------------'); + + t:=3; + l:=startInfo; + while l<>NIL + do begin + GetFileServerName(l^.connId,servername); + PstrCopy(s,l^.objName,20); + write(s,' | '); + PstrCopy(s,serverName,15); + write(s,' | ',l^.connNbr:3,' | '); + PstrCopy(s,l^.fullname,30); + writeln(s); + l:=l^.next; + inc(t); + if t=20 + then begin + writeln('--- more (any key)---'); + repeat {} until keypressed; + ch:=readkey; + if ch=#0 then ch:=readkey; + t:=0; + end; + end; + GetTargetUser:=NIL; + end; + end; {case} +end; + +Procedure ProcessInputCommand; +Var SearchStartServer, + SearchEndServer :Byte; + ConnIdCtr, + ConnNbrCtr :Byte; + + LuserName, + LserverName :String; + LconnId :Byte; + LfullName :String; + LconnNbr :Byte; + + ServerInfo :TFileServerInformation; + objName :String; + objType :Word; + objId :Longint; + ticks :Word; + LoginTime :TnovTime; + IntNWaddress :TinternetworkAddress; + + TargetUserPtr :PusrInfo; + + p :Byte; + errcode :Integer; +begin +StartInfo:=NIL; +If (ParamCount>0) + and ( (pos('?',paramstr(1))>0) + or (pos('help',paramstr(1))>0) + or (pos('HELP',paramstr(1))>0) + ) + then begin + writeln; + writeln('** Phone V 1.3., By E.M. Serrat and R. Spronk'); + writeln; + writeln('** Usage: PHONE'); + writeln; + writeln('Listen for others calling you.'); + writeln; + writeln; + writeln('** Usage: PHONE [servername/]UserName [connection]'); + writeln; + writeln('Call someone.'); + writeln('-Username may be a ''*'' wildcard.'); + writeln(' All logged in users on all attached servers will be shown.'); + writeln('-Sender and receiver must be attached to a common server in the internetwork.'); + writeln('-The supplied username is compared with the first characters of'); + writeln(' the login name and with the full user name, as set by SYSCON.'); + writeln('-Servername must be supplied if the target user has connections'); + writeln(' with more than one server.'); + writeln('-ConnectionNumber must be supplied if the target user is logged in'); + writeln(' at multiple workstations attached to the same server.'); + writeln; + writeln('The program will timeout if the program on the other end of the link'); + writeln('is terminated abnormally.'); + halt(1); + end; +if paramcount=0 { ---- Listen if anyone is calling us ----- } + then begin + HandshakeWithSender; + InitWindows; + Talk; + IpxCloseSocket(Socket); + SetPreferredConnectionId(MyServerId); + halt(0); + end; +{ ** Paramcount>0, We're calling someone ** } +LconnNbr:=0; +SearchStartServer:=1; +SearchEndServer:=8; +LuserName:=ParamStr(1); +UpString(LuserName); +p:=pos('/',LuserName); +checkError((p=1) and (LuserName[0]=#1),$0020,''); +if p>0 + then begin + LserverName:=copy(LuserName,1,p-1); + delete(LuserName,1,p); + if LuserName='' + then LuserName:='*'; + if pos('*',LserverName)=0 + then begin + GetConnectionId(LserverName,LconnId); + checkError(nwConn.result>0,$0022,LserverName); + SearchStartServer:=LconnId; + SearchEndServer:=LconnId; + end; + end; +if paramcount>1 + then begin + Val(ParamStr(2),LconnNbr,errcode); + checkError(errcode<>0,$0023,Paramstr(2)); + end; + +writeln('Scanning logged in users..'); +ConnIdCtr:=SearchStartServer; +While ConnIdCtr<=SearchEndServer + do begin + If IsConnectionIdInUse(ConnIdCtr) + then begin + SetPreferredConnectionId(ConnIdCtr); + IF NOT GetFileServerInformation(ServerInfo) + then ServerInfo.connectionsMax:=250; { patch value if call failed } + for ConnNbrCtr:=1 to ServerInfo.ConnectionsMax + do begin + IF GetConnectionInformation(ConnNbrCtr,ObjName,objType,objId,LoginTime) + and (objType=OT_USER) + then begin + GetInterNetAddress(ConnNbrCtr,IntNWaddress); + GetRealUserName(ObjName,LfullName); + UpString(LfullName); + IF (pos('NOT-LOGGED-',objName)=0) { skip not logged in connections } + and ((LconnNbr=0) or (LconnNbr=ConnNbrCtr)) { if user supplied connNbr, check it } + and (NOT SameAddress(MyAddress,IntNWAddress)) { no mail to yourself } + and ( (LuserName[1]='*') { wildcard overrules nameselection } + or (pos(LuserName,ObjName)=1) { username matched with firts few characters in objName? } + or (pos(LuserName,LfullName)>0) { usermane matches part of objects' Full_Name ? } + ) + then PushInLL(objName,LfullName,ConnIdCtr,ConnNbrCtr, + IntNWaddress); + end; + end; + end; + inc(ConnIdCtr); + end; +TargetUserPtr:=GetTargetUser; +checkError((LuserName[1]<>'*') and (TargetUserPtr=NIL),$0025,''); { No user selected } +checkError(TargetUserPtr=NIL,$0026,''); +RconnNbr:=TargetUserPtr^.connNbr; +Raddress:=TargetUserPtr^.address; +Raddress.Socket:=Socket; +Rname:=TargetUserPtr^.objName; +RserverId:=TargetUserPtr^.connId; +GetFileServerName(RserverId,RserverName); +release(HeapCheckPtr); + +SetPreferredConnectionId(RserverId); +GetRealUserName(Rname,RfullName); +writeln; +writeln(RserverName,'/',Rname,' Connection_Number= ',RconnNbr); +writeln('(Full name =',RfullName,')'); +writeln; +write('Is the above user the intended chat partner ? (Y/N)'); +checkError(NOT Confirm,$0027,''); { user abort } +writeln; + +IPXGetLocalTarget(Raddress,LocalTarget,ticks); +IPXSetupSendECB(NIL, Socket, Raddress, Addr(SendData), SizeOf(SendData), + SendIPXheader,SendECB); +HandShakeWithReceiver; +InitWindows; +Talk; +IpxCloseSocket(Socket); +SetPreferredConnectionId(MyServerId); +halt(0); +end; + +Procedure WhoAmI; {---------------------------------------------------------} +Var ObjType :Word; + ObjId :LongInt; + LogonTime:TnovTime; +begin +GetConnectionNumber(MyConnNbr); +checkError(nwConn.result>0,$0010,''); +GetInternetAddress(MyConnNbr,MyAddress); +checkError(nwConn.result>0,$0011,''); +MyAddress.Socket:=Socket; +GetConnectionInformation(MyConnNbr,MyName,ObjType,ObjId,LogonTime); +checkError(nwConn.result>0,$0012,''); +GetEffectiveConnectionID(MyServerId); +GetFileServerName(MyServerId,MyServerName); +end; + +{-----------------------------------------------------------------------------} +Var LocSocket:Word; + +begin +Writeln('*** PHONE V1.3 ***'); +Mark(HeapCheckPtr); +LocSocket:=Socket; +readflg:=false; +Checkerror(NOT IpxPresent,$0001,''); +IpxOpenSocket(LocSocket,FALSE); +Checkerror(nwIPX.result>0,$0002,''); +WhoAmI; +IPXSetupListenECB(Addr(EsrHandler),socket,Addr(ReadData),SizeOf(ReadData), + ListenIPXheader,ListenECB); +ProcessInputCommand; {doesn't return} +end. \ No newline at end of file diff --git a/NWTP/XOTHER/TVLM.PAS b/NWTP/XOTHER/TVLM.PAS new file mode 100644 index 0000000..c336dda --- /dev/null +++ b/NWTP/XOTHER/TVLM.PAS @@ -0,0 +1,83 @@ +program tvlm; +{$F+} + +{ Example for the nwIntr unit / NwTP 0.6 (c) 1995, R.Spronk + + Shows the same information as the VLM /X command. + + Example for the use of the GetVLMHeader and GetVLMControlBlock + functions in the unit nwIntr } + +uses dos,nwintr,nwmisc; + +Var t:byte; + HDR:TVLMHeader; + CBL:TVLMcontrolBlockEntry; + s:string; + GlobSize,TransSize:longint; + regs:registers; + w:word; + +begin +IF NOT VLM_EXE_Loaded + then begin + writeln('VLM.EXE not loaded.'); + halt(0); + end; +GetVLMHeader(HDR); + +regs.ax:=$7a20; +regs.bx:=$0000; +regs.cx:=$0000; +intr($2f,regs); + +writeln('Handler entry point : ',HexStr(regs.es,4),':',HexStr(regs.bx,4)); +writeln('Headerlength (or id?): ',HexStr(HDR.headerlen,2),'h'); +writeln('IDstring : ',hdr.multiplexIdString[1], + hdr.multiplexIdString[2], + hdr.multiplexIdString[3]); + +writeln('TransientSwitchCount : ',hdr.TransientSwitchCount); +writeln('CallCount : ',hdr.CallCount); +writeln('CurrentVLMid : ',HexStr(hdr.CurrentVLMID,4),'h'); +writeln('MemoryType : ',HexStr(hdr.MemoryType,2),'h [04 = XMS]'); +writeln('ModulesLoaded : ',hdr.ModulesLoaded); +writeln('BlockID : ',HexStr(hdr.BlockId,4),'h'); +writeln('TransientBlock : ',HexStr(hdr.TransientBlock,4),'h'); +writeln('GlobalSegment : ',HexStr(hdr.GlobalSegment,4),'h'); +writeln('FullMapCount : ',hdr.FullMapCount); + +GlobSize:=0;TransSize:=0; +writeln; +writeln('VLM control block information Address Memory size (bytes) '); +writeln('Name ID Flag Func Maps Call TSeg Gseg Low Hi TSize Gsize SSize '); +writeln('-------- ---- ---- ---- ---- ---- ---- ---- ---- ---- ------ ------ ------'); +for t:=0 to hdr.modulesLoaded + do begin + GetVLMcontrolBlock(t,CBL); + + With CBL + do begin + s[0]:=#8; + move(cbl.VLMname,s[1],8); + write(s,' '); + + write(HexStr(id,4),' '); + write(HexStr(Flag,4),' '); + write(HexStr(Func,4),' '); + write(HexStr(Maps,4),' '); + write(HexStr(TimesCalled,4),' '); + write(HexStr(TransientSeg,4),' '); + write(HexStr(GlobalSeg,4),' '); + write(HexStr(AddressLow,4),' '); + write(HexStr(AddressHi,4),' '); + writeln(16*TsegSize:6,' ',16*GSegSize:6,' ',16*SSegSize:6); + if TsegSize>TransSize + then TransSize:=TsegSize; + GlobSize:=GlobSize+GSegSize; + end; + end; +writeln('Transient block size: ',TransSize*16); +writeln('Global segment size : ',GlobSize*16); + +end. \ No newline at end of file diff --git a/NWTP/XQMS/QAVAIL.PAS b/NWTP/XQMS/QAVAIL.PAS new file mode 100644 index 0000000..5faaf61 --- /dev/null +++ b/NWTP/XQMS/QAVAIL.PAS @@ -0,0 +1,95 @@ +program QAvailable; + +{ QMS related utility / NwTP 0.5 API. (c) 1993,1994, R.Spronk } + +uses nwMisc,nwBindry,nwQMS; + +Var Qname :string; + QobjId:Longint; + Qtype :word; + + BinAccLev:Byte; + MyObjId :Longint; + MyObjName:string; + MyObjType:word; + + SegNbr :Byte; + propName :string; + propValue:Tproperty; + moreSeg :boolean; + propFlags:byte; + i :Byte; + + GrpObjId :Longint; + GrpObjName:string; + GrpObjType:word; + +begin +{--- Parameter section } + +if paramCount<>1 + then begin + writeln('QAVAIL usage: QAVAIL '); + writeln('Batch file utility to check for the availability of queues.'); + writeln; + writeln('Errorlevel 0 : Queue exists, calling user is allowed to use Queue'); + writeln(' 1 : Queue doesn''t exist on default fileserver.'); + writeln(' 2 : Queue exists but calling user has no Queue rights.'); + halt(1); { No queue available } + end; +Qname:=ParamStr(1); + +{--- Startup checks } + +IF Not (IsShellLoaded and IsUserLoggedOn) + then halt(1); { Queue not available } + +{--- Queue name in bindery of effective server ? } + +UpString(Qname); +IF not GetBinderyObjectId(Qname,3 {ot_print_queue},QobjId) + then halt(1); + +{--- Queue exists. Does the caller have rights to use the queue ? } + +IF NOT (GetBinderyAccessLevel(BinAccLev,MyObjId) and + GetBinderyObjectName(MyObjId,MyObjName,MyObjType)) + then halt(1); { Oops.. some kind of bindery error } + +IF IsBinderyObjectInSet(Qname,3 {OT_PRINT_QUEUE},'Q_USERS',MyObjName, MyObjType) + then halt(0); { OK. Caller has rights } + +if nwbindry.result=$FB { According to QMS definitions, when the property } + then halt(0); { Q_USERS doesn't exist, all users have queue rights. } + +{--- Is one of the groups I'm a member of a queue user ? } + +SegNbr:=1; +propName:='GROUPS_I''M_IN'; + +While ReadPropertyValue(MyObjName,MyObjType,propName,SegNbr, + propValue,moreSeg,propFlags) + do begin + { A segment of a set-property consists of a list of object IDs, + each ID 4 bytes long, stored hi-lo. + The end of the list (within THIS segment) is marked by an ID of 00000000. } + i:=1; + Repeat + GrpObjId:=MakeLong((propValue[i] *256 +propValue[i+1]), ( propValue[i+2] *256 + propValue[i+3] ) ); + if GrpObjId<>0 + then begin + IF GetBinderyObjectName(GrpObjId,GrpObjName,GrpObjType) + and IsBinderyObjectInSet(Qname,3 {OT_PRINT_QUEUE}, + 'Q_USERS',GrpObjName, GrpObjType) + then halt(0); { OK. Caller's group has queue rights } + end; + inc(i,4); + Until (i>128) or (GrpObjId=0); + inc(SegNbr); + end; + +{--- Still no rights found.. } +halt(2); + +end. + diff --git a/NWTP/XSEMA/SEMATEST.PAS b/NWTP/XSEMA/SEMATEST.PAS new file mode 100644 index 0000000..9095c7d --- /dev/null +++ b/NWTP/XSEMA/SEMATEST.PAS @@ -0,0 +1,91 @@ +{$X+,V-,B-} +Program SemaTest; + +{ */ +/* SemaTest - Tests semaphores by showing application metering example */ +/* */ +/* by Charles Rose */ +/* */} + +{ Testprogram for the nwSema unit, this version (c) 1994,1995 R.Spronk } + +USES Crt,nwMisc,nwSema; + +CONST + INITIAL_SEMAPHORE_VALUE=2; + WAIT_SECONDS=2; + +{ Global data } +VAR openCount :Word; + semValue :Integer; + semHandle :LongInt; + done :boolean; + t :Byte; + +BEGIN {main} + +done := False; + +{ Open Semaphore } +semValue := INITIAL_SEMAPHORE_VALUE; { Need in case we're creating the semaphore } +IF NOT OpenSemaphore( 'TestSemaphore', semValue, semHandle, openCount ) + then begin + writeln('Error opening semaphore. error #',nwSema.Result); + Halt(1); + end; + +{ Wait on the Semaphore (get permission to use the resource) } +IF NOT WaitOnSemaphore( semHandle, 3*18 ) { 0 = Don't wait } + then begin + if ( nwSema.Result = $FE ) + then begin + writeln( 'Sorry, all of the slots for this resource are currently in use' ); + halt(1); + end + else begin + writeln('WaitOnSemaphore returned eror# ',nwSema.result); + halt(1); + end; + end; + + +clrscr; +gotoxy(1,4); +Writeln('Testing semaphore functions.'); +writeln('Workstation ',INITIAL_SEMAPHORE_VALUE+1,' that starts this testprogram'); +writeln('(concurrently) will be refused access to the (imaginary) resource.'); +gotoxy( 24,24 ); +write( 'Press any key to exit' ); + +IF NOT ExamineSemaphore( semHandle, semValue, openCount ) + then begin + writeln('Error while examining semaphore value. Error #',nwSema.Result); + Halt(1); + end; + +{ Wait loop } +while ( NOT done ) +do begin + gotoxy( 1,23 ); + write( 'Semaphore Test --> Open at [',openCount, + '] stations *** Value is [',semValue,'] '); + t:=0; + While (t<100) and (not done) + do begin + delay(WAIT_SECONDS*10); { wait a while }; + done:=KeyPressed; + inc(t); + end; + + gotoxy( 60,23 ); + write( 'Checking...' ); Delay(500); { wait half a sec } + + IF NOT ExamineSemaphore( semHandle, semValue, openCount ) + then writeln('ExamnineSemaphore2 error#',nwsema.result); + end; + +{ Signal Semaphore (that we're through with the resource) } +SignalSemaphore( semHandle ); +{ Close Semaphore } +CloseSemaphore( semHandle ); +end. diff --git a/NWTP/XSEMA/TSTSEMA2.PAS b/NWTP/XSEMA/TSTSEMA2.PAS new file mode 100644 index 0000000..c38abf2 --- /dev/null +++ b/NWTP/XSEMA/TSTSEMA2.PAS @@ -0,0 +1,86 @@ +{$X+,B-,V-} +Program tstsema2; + +{ Example for the nwSema unit / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +uses nwMisc,nwConn,nwSema; + + +Var ConnNbr:Byte; + seqNbr:Word; + NbrOfSema:Byte; + SemaInfo:TconnSema; + + SemValue:Integer; + info:TsemaInfoList; + + t:byte; + s:string; + + handle4,handle5, + handle6,handle7:LongInt; + openCount:word; +begin + +connNbr:=2;seqNbr:=0; + +OpenSemapHore('SEMA4',4,handle4,openCount); +OpenSemaphore('SEMAPHORE5',5,handle5,OpenCount); +OpenSemapHore('SEM6',6,handle6,OpenCount); +OpenSemapHore('SEMAP7',7,handle7,OpenCount); + +writeln('Semaphores have been opened..'); +writeln; +writeln(' to continue..'); +readln(s); + +GetConnectionNumber(connNbr); { my connection number } + +seqNbr:=1; +REPEAT +IF NOT GetConnectionsSemaphores(ConnNbr,seqNbr,NbrOfSema,SemaInfo) + then begin + writeln('GetConnectionsSemaphores returned error #', nwSema.result); + seqNbr:=0; + end + else begin + writeln; + writeln('NbrOfSema Left to Scan: ',NbrOfSema); + writeln('Next seq Nbr : ',seqNbr); + + with SemaInfo + do begin + writeln('Sema Name:',Name); + writeln('Opencount:',OpenCount); + writeln('Value :',Value); + writeln('TaskNbr :',taskNbr); + end; + end; + +UNTIL seqNbr=0; + + +seqNbr:=1; +REPEAT +IF GetSemaphoreInformation('SEMA4',seqNbr, + OpenCount,SemValue,NbrOfSema,info) + then begin + writeln; + writeln('Test of GetSemaphoreInformation...'); + writeln('Connections using semaphore:',Opencount); + writeln('Semaphore value:',semvalue); + + If NbrOfSema>100 + then NbrOfSema:=100; + for t:=1 to NbrOfSema + do writeln('Record #:',t,' Connection: ',info[t].ConnNbr,' Task: ',info[t].taskNbr); + end + else writeln('GetSemaphoreInformation returned error #', nwSema.result); +UNTIL seqNbr=0; + +CloseSemaphore(handle4); +CloseSemaphore(handle5); +CloseSemaPhore(handle6); +CloseSemaphore(handle7); +end. + diff --git a/NWTP/XSERV/CLRCONN.PAS b/NWTP/XSERV/CLRCONN.PAS new file mode 100644 index 0000000..c36d4ae --- /dev/null +++ b/NWTP/XSERV/CLRCONN.PAS @@ -0,0 +1,59 @@ +{$X+,B-,V-} {essential compiler directives} + +Program ClrConn; + +{ Example for the nwServ unit / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +{ Purpose: Utility for users with console privileges: + Terminate a connection on the current server. } + +{ Tests the following nwServ functions: + + CheckConsolePrivileges + ClearConnectionNumber +} + +Uses nwMisc,nwServ; + +Var errCode:Integer; + connNbr:byte; + connStr:String; + showHelp:boolean; + +begin + +IF NOT CheckConsolePrivileges + then begin + IF nwServ.result=$C6 + then writeln('You need console privileges to run this util.') + else writeln('Error checking console privileges, err#',nwServ.result); + halt(1); + end; + +IF ParamCount=1 + then begin + connStr:=ParamStr(1); + Val(connStr,connNbr,errCode); + showhelp:=(errCode<>0); + end + else showHelp:=true; + +IF showHelp + then begin + writeln('CLRCONN-- usage:'); + writeln; + writeln('CLRCONN connection_number'); + writeln; + writeln('the connection_number must be supplied.'); + writeln('it should contain numbers only (range 1..255)'); + halt(1); + end; +IF ClearConnectionNumber(connNbr) + then writeln('Connection ',connNbr,' was terminated.') + else if nwServ.result=253 + then writeln('Connection NOT cleared. The supplied ConnectionNumber was too high.') + else if nwServ.result=162 + then writeln('You cleared your own connection!') + else writeln('Connection NOT cleared. Error# ',nwServ.result); + +end. \ No newline at end of file diff --git a/NWTP/XSERV/LOGLOCK.PAS b/NWTP/XSERV/LOGLOCK.PAS new file mode 100644 index 0000000..0ffbf1b --- /dev/null +++ b/NWTP/XSERV/LOGLOCK.PAS @@ -0,0 +1,39 @@ +{$X+,B-,V-} {essential compiler directives} + +program loglock; + +{ Example for the nwServ unit / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +{ Purpose: This program toggles the login status.. + If User Login was enabled, it will be diabled and v.v. } + +{ Tests the following nwServ functions: + + DisableFileServerLogin + EnableFileServerLogin + GetFileServerLoginStatus +} + +uses nwMisc,nwServ; + +Var allowed:boolean; + +begin +IF GetFileServerLoginStatus(allowed) + then begin + if allowed + then begin + DisableFileServerLogin; + writeln('Login Disabled.') + end + else begin + EnableFileserverLogin; + writeln('Login Enabled.') + end + end + else begin + if nwServ.result=$C6 + then writeln('You need console operator rights to run this utility.') + else writeln('GetFileserverLoginStatus Error : $',HexStr(nwServ.result,2)); + end; +end. diff --git a/NWTP/XSERV/TSTSERV.PAS b/NWTP/XSERV/TSTSERV.PAS new file mode 100644 index 0000000..800a6ee --- /dev/null +++ b/NWTP/XSERV/TSTSERV.PAS @@ -0,0 +1,122 @@ +{$X+,V-,B-} +program tstServ; + +{ Testprogram for the nwServ unit / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +{ Tests the following nwServ functions: + + GetFileServerDateAndTime + GetFileServerDescriptionStrings + GetNetworkSerialNumber + GetFileServerInformation + SetFileServerDateAndTime + VerifyNetworkSerialNumber +} + +uses nwMisc,nwServ; + +Var t1,t2,t3:TnovTime; + s1,s3:string; + + serialNbr:Longint; + appNbr,appNbr2:word; + + Sinfo:TFileServerInformation; + + companyName,VersionAndRevision, + revisionDate,copyrightNotice:String; + +begin +writeln('TSTSERV: Test of some server function calls.'); +writeln; +writeln('This program will change (and reset) the server time & date.'); +writeln('--This will cause the server to beep (twice)--'); +writeln; +writeln('Continue ? (Y/N) + '); +readln(s1); +if (pos('y',s1)=0) and (pos('Y',s1)=0) + then halt(1); + +writeln('Testing the Get/Set ServerTime Calls. (temporarily setting the year to 2020)'); +IF GetFileServerDateAndTime(t1) + then begin + nwMisc.NovTime2String(t1,s1); + writeln('Original server time:',s1); + t2:=t1; + t2.year:=20; { set year to 2020 } + t2.day:=1; + t2.month:=4; + IF SetFileServerDateAndTime(t2) + then begin + GetFileServerDateAndTime(t3); + nwMisc.NovTime2String(t3,s3); + if t3.year<>t2.year + then writeln('Error: FileServerDateAndTime NOT changed..'); + writeln('New server time:',s3); + SetFileServerDateAndTime(t1) {restore old date & time } + end + else begin + if nwServ.result=$C6 + then writeln('Error: You need console privileges in order to change the server time.') + else writeln('SetFileServerDateAndTime Error: $',HexStr(nwServ.result,2)); + end + end + else writeln('GetFileServerDateAndTime Error: $',HexStr(nwServ.result,2)); + +writeln; +IF GetFileServerInformation(Sinfo) + then begin + writeln('Testing GetServerInformation..'); + writeln('Servername:',Sinfo.serverName); + writeln('NW version:',Sinfo.NetwareVersion,'.',Sinfo.NetwareSubVersion); + writeln('Conn Max,Current:',Sinfo.ConnectionsMax,',',Sinfo.ConnectionsInUse); + writeln('Peak Conn Used :',Sinfo.Peak_Conn_Used); + end + else writeln('GetFileServerDateAndTime Error: $',HexStr(nwServ.result,2)); + +writeln; +IF GetFileServerDescriptionStrings(companyName,VersionAndRevision, + revisionDate,copyrightNotice) + then begin + writeln('Testing GetFileServerDescriptionStrings'); + writeln('Company :',companyName); + writeln('Version/Rev:',VersionAndRevision); + writeln('Rev.Date :',revisionDate); + writeln('Copyright :',copyRightNotice); + end + else writeln('GetFileServerDescriptionStrings Error: $',HexStr(nwServ.result,2)); + + +writeln; +IF GetNetworkSerialNumber(serialNbr,appNbr) + then begin + writeln('Testing GetNetworkSerialNumber'); + writeln('SerialNbr=',HexStr(serialNbr,8)); + writeln('AppNbr =',HexStr(appNbr,4)); + end + else writeln('GetNetworkServerSerialNumber Error: $',HexStr(nwServ.result,2)); + +{ The last test is commented out. It works, but it is a bit irritating to + be disconnected every time I'm testing a call.. } + + +writeln('Testing VerifyNetworkSerialNumber (will abort workstations'' connection)'); +writeln('Continue ? (Y/N) + '); +readln(s1); +if (pos('y',s1)=0) and (pos('Y',s1)=0) + then halt(1); + +{ +IF VerifyNetworkSerialNumber(serialNbr,appNbr2) + then begin + if appNbr2=appNbr then writeln('Serial Number Verified.'); + writeln; + writeln('Verifying a wrong network serialnumber..'); + writeln('**** THIS WILL TERMINATE THE CONNECTION **** (if the calls works..)'); + If VerifyNetworkSerialNumber($12345678,appNbr2) + then writeln('false serialnumber verified as being OK'); + end + else writeln('VerifyNetworkSerialNumber Error: $',HexStr(nwServ.result,2)); +} + +end. diff --git a/README b/README new file mode 100644 index 0000000..1a867a1 --- /dev/null +++ b/README @@ -0,0 +1,92 @@ +James@linux-box.demon.co.uk + +README FOR NWE Admin 0.1 +======================== + +Please read COPYING for licence and other important information. +This software is released unde the GNU General Public Licence. + +The latest version of this software can be found +at http://www.linux-box.demon.co.uk. + +Hi Folks! I'm James Jeffrey Thanks for trying my software and I hope +you like it. + +!This software is in a very early state, the interface is fairly self! +!explanatory (I HOPE ;-) ) So I will let you work it out! ! +VERY VERY VERY BUGGY BE VERY VERY VERY CAREFULL! BACK STUFF UP! +YOU COULD SCREW UP YOUR SERVER! + +|============================================================| +|If you use this software, I ask you to mail me and tell me, | +|use James@linux-box.demon.co.uk. | +|============================================================| + +If you hate this software tell me why, mabye I can fix it. +If you find a bug, have a suggetsion or a code fix, mail me it, +your name will be mentioned in the next release! + +I am sorry, the source is next to unreadable, I will clear this +up for 0.2 if enough interest is shown. + +If you wish to make a small donation to this hard-up student then +please feel free, I need UK currency if possible. Donations VERY +gratefully accepted. + +James Jeffrey +41 West Park Avenue +Roundhay +Leeds +LS8 2EB +United Kingdom + + +PLEASE NOTE +=========== + +This program works with Mars_NWE, it runs under Windows 3+ and may +require some DLL's I have not shipped (If it needs them mail me +but they should be easily available.), it requires a running netware +client. + + +INSTRUCTIONS +============ + +Remove the supervisor password form nwserv.conf, also remove all user +entries in nwserv.conf. + +Run NWADMIN.EXE on your windows machine when attached to the mars server. + +Good Luck. + + + + + +PLANS FOR 0.2 or 0.3 or 0.4 etc.. +================================== + +Write a TCP/IP demon to run under INETD on the unix system +to allow for automatic creation and deletion of unix users +and setting of quitas on ext2 file systems under linux, as +well as managing volumes. + +German Language Version (I don't speak German...) + +Any help welcome. + + + +See Ya All + + +P.S. My congratulations to MArtin Stover and Team for a fantastic +piece of software - Mars NWE! + +P.P.S. + +This program was written in delphi using a GPL'd netware library +which I have slightly modified for Delphi. This library was mainly +written by R.Spronk. I have included both original and modified +versions. diff --git a/SRC/COPYING b/SRC/COPYING new file mode 100644 index 0000000..76ce5a6 --- /dev/null +++ b/SRC/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/SRC/DELUSER.DCU b/SRC/DELUSER.DCU new file mode 100644 index 0000000000000000000000000000000000000000..39e84da6902eb02f1ff8a1c5a261522d53012985 GIT binary patch literal 1872 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 literal 0 HcmV?d00001 diff --git a/SRC/EDUSER.PAS b/SRC/EDUSER.PAS new file mode 100644 index 0000000..b3e8463 --- /dev/null +++ b/SRC/EDUSER.PAS @@ -0,0 +1,116 @@ +unit Eduser; + +interface + +uses WinTypes, WinProcs, Classes, Graphics, Forms, Controls, Buttons, + StdCtrls, ExtCtrls, Dialogs; + +type + TBtnBottomDlg3 = class(TForm) + OKBtn: TBitBtn; + CancelBtn: TBitBtn; + Bevel1: TBevel; + Edit1: TEdit; + CheckBox1: TCheckBox; + Button1: TButton; + Label1: TLabel; + procedure FormShow(Sender: TObject); + procedure OKBtnClick(Sender: TObject); + procedure Button1Click(Sender: TObject); + private + { Private declarations } + public + UserNAme: String; + Int1: Integer; + { Public declarations } + end; + +var + BtnBottomDlg3: TBtnBottomDlg3; + +implementation + +{$R *.DFM} + +uses User, nwBindry, SysUtils, ChgPass2; + +function IsUserPrivileged(UserName:String): Boolean; +begin + IsUserPrivileged:=IsBinderyObjectInSet(UserName, OT_USER, 'SECURITY_EQUALS', + 'SUPERVISOR', OT_USER); +end; + +procedure TBtnBottomDlg3.FormShow(Sender: TObject); +begin + UserName:=''; Int1:=1; + While BtnBottomDlg.ListBox1.Items[BtnBottomDlg.ListBox1.ItemIndex][Int1]<>',' do + begin + UserName:=UserName+BtnBottomDlg.ListBox1.Items[BtnBottomDlg.ListBox1.ItemIndex][Int1]; + Int1:=Int1+1; + end; + Edit1.Text:=BtnBottomDlg.GetUnixUser(UserName); + Caption:='Editing User '+UserName; + CheckBox1.Checked:=((IsUserPrivileged(UserName)) or (UserName='SUPERVISOR')); + CheckBox1.Enabled:=(UserName<>'SUPERVISOR'); + Button1.Enabled:=(UserName<>'SUPERVISOR'); +end; + +function GetNewPass: String; +begin + PassWordDlg.ShowModal; + GetNewPAss:=PasswordDlg.Password.Text; +end; + + +procedure TBtnBottomDlg3.OKBtnClick(Sender: TObject); +var SX: Integer; + MyPThing: TProperty; + +begin + For SX:=1 to 128 do MyPThing[SX]:=0; + StrPCopy(Addr(MyPThing), Edit1.Text); + WritePropertyValue(UserName, OT_USER, 'UNIX_USER', 1, MyPThing, False); + if UserName<>'SUPERVISOR' then + begin + DeleteProperty(UserName, OT_USER, 'SECURITY_EQUALS'); + CreateProperty(Edit1.Text, OT_USER, 'SECURITY_EQUALS', BF_SET, $31); + if CheckBox1.Checked then AddBinderyObjectToSet(Edit1.Text, OT_USER, + 'SECURITY_EQUALS','SUPERVISOR', OT_USER) + else AddBinderyObjectToSet(Edit1.Text, OT_USER,'SECURITY_EQUALS','EVERYONE', OT_USER_GROUP); + end; + MessageDlg('Changes to user will take effect next time they login.', mtInformation, [mbOK], 0); + Close; +end; + +procedure TBtnBottomDlg3.Button1Click(Sender: TObject); +var Successish: Boolean; + Int3: Integer; + Prop1: TProperty; +begin + Successish:=DeleteBinderyObject(UserName, OT_USER); + if Successish=TRUE then + begin + if CreateBinderyObject(UserName, OT_USER, 0, $31)=FALSE then Successish:=False + else begin + CreateProperty(UserNAme, OT_USER, 'UNIX_USER', 0, $30); + CreateProperty(UserName, OT_USER, 'SECURITY_EQUALS', BF_SET, $31); + CreateProperty(UserName, OT_USER, 'GROUPS_I''M_IN', BF_SET, $32); + For Int3:=1 to 128 do Prop1[Int3]:=0; + StrPCopy(Addr(Prop1), Edit1.Text); + WritePropertyValue(UserName, OT_USER, 'UNIX_USER', 1, Prop1, False); + {Security} + if CheckBox1.Checked then AddBinderyObjectToSet(UserName, OT_USER, + 'SECURITY_EQUALS','SUPERVISOR', OT_USER) + else AddBinderyObjectToSet(UserName, OT_USER,'SECURITY_EQUALS','EVERYONE', OT_USER_GROUP); + {Groups} + AddBinderyObjectToSet(UserName, OT_USER, 'GROUPS_I''M_IN','EVERYONE', OT_USER_GROUP); + {Set Password} + if ChangeEncrBinderyObjectPassword(Username, OT_USER, '', GetNewPass)=FALSE then + MessageDlg('There was an error setting the password. The password is empty!', mtwarning, [mbOK], 0); + end; + end; + if Successish=FALSE then MessageDlg('Failed. Sorry.', mtError, [mbOK], 0) else + MessageDlg('Password Changed.', mtError, [mbOK], 0); +end; + +end. diff --git a/SRC/MYADMIN.DPR b/SRC/MYADMIN.DPR new file mode 100644 index 0000000..8b124d6 --- /dev/null +++ b/SRC/MYADMIN.DPR @@ -0,0 +1,35 @@ +program Myadmin; + +uses + Forms, + Startfrm in 'STARTFRM.PAS' {Form1}, + nwBindry in 'UNITS\NWBINDRY.PAS', + NWintr in 'UNITS\NWINTR.PAS', + NWMISC in 'UNITS\NWMISC.PAS', + nwConn in 'UNITS\NWCONN.PAS', + nwServ in 'UNITS\NWSERV.PAS', + User in 'USER.PAS' {BtnBottomDlg}, + Deluser in 'DELUSER.PAS' {BtnRightDlg}, + Passform in '\DELPHI\IMAGES\ICONS\PASSFORM.PAS' {BtnBottomDlg1}, + Adduser in '\DELPHI\IMAGES\BUTTONS\ADDUSER.PAS' {BtnBottomDlg2}, + Eduser in 'EDUSER.PAS' {BtnBottomDlg3}, + Svrutil in 'SVRUTIL.PAS' {BtnRightDlg1}, + nwMess in 'UNITS\NWMESS.PAS', + Sendmsg in 'UNITS\SENDMSG.PAS' {BtnBottomDlg4}, + Chgpass2 in 'UNITS\CHGPASS2.PAS' {PasswordDlg}; + +{$R *.RES} + +begin + Application.Title := 'NWE Admin 0.1'; + Application.CreateForm(TForm1, Form1); + Application.CreateForm(TBtnBottomDlg, BtnBottomDlg); + Application.CreateForm(TBtnRightDlg, BtnRightDlg); + Application.CreateForm(TBtnBottomDlg1, BtnBottomDlg1); + Application.CreateForm(TBtnBottomDlg2, BtnBottomDlg2); + Application.CreateForm(TBtnBottomDlg3, BtnBottomDlg3); + Application.CreateForm(TBtnRightDlg1, BtnRightDlg1); + Application.CreateForm(TBtnBottomDlg4, BtnBottomDlg4); + Application.CreateForm(TPasswordDlg, PasswordDlg); + Application.Run; +end. diff --git a/SRC/MYADMIN.EXE b/SRC/MYADMIN.EXE new file mode 100644 index 0000000000000000000000000000000000000000..5f9ec39f64d72d6985afdbea5776afb99b03a152 GIT binary patch literal 237176 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 AjsO4v literal 0 HcmV?d00001 diff --git a/SRC/MYADMIN.OPT b/SRC/MYADMIN.OPT new file mode 100644 index 0000000..6284864 --- /dev/null +++ b/SRC/MYADMIN.OPT @@ -0,0 +1,34 @@ +[Compiler] +A=1 +B=0 +D=1 +F=0 +I=1 +K=1 +L=1 +P=1 +Q=0 +R=0 +S=1 +T=0 +U=1 +V=1 +W=0 +X=1 +Y=1 + +[Linker] +MapFile=0 +LinkBuffer=0 +DebugInfo=0 +OptimizeExe=1 +StackSize=16384 +HeapSize=8192 + +[Directories] +OutputDir= +SearchPath= +Conditionals= + +[Parameters] +RunParams= diff --git a/SRC/MYADMIN.RES b/SRC/MYADMIN.RES new file mode 100644 index 0000000000000000000000000000000000000000..c3cc46d1817486f953e9c980a64e62b4163e0840 GIT binary patch literal 794 zcmc(ev2KGf5QdKy$qZ4Qp%0L;k3m!xhK5(^W=3yj^U@_!9)=;KC0?WB4Ux*`{&&EF zy4Q&>e0+y<=Q#P9azV%A_Y=_@5uGq=%z{cpB9Hrj4-X0B{qli`OH5DWI6`E7Q_gaQ ziI?0$xbDVR)6v5=+h}ilzIWED$3jih-qOM~4Z}dfQ^~feE8sAc!bG;NW{j2LZ4~QC zy`@_0Oh)q)m(Xz_*Z1PnG>T;tf|vDEvN1+)s=i-XwQ(j4rTQ`F=v@!Luza_5!QgTA z7Mg$HOJ#Z7L<_%ij_e%I71AXio?VExf)=(p8QUw zm9v5VOuX?dogLDYv8}Q`Lm$v)%E}|EA;FY3UNPOZAKDNJx<5;@#QTm@yZgDXTKYeR d{6Zh+_R_w07o7Z8t)#$-Kb?+x?&ZHw&@asV9vT1u literal 0 HcmV?d00001 diff --git a/SRC/README.TXT b/SRC/README.TXT new file mode 100644 index 0000000..2e1b4bd --- /dev/null +++ b/SRC/README.TXT @@ -0,0 +1,51 @@ +README FOR NWE Admin 0.1 +======================== + +Please read COPYING for licence and other important information. +This software is released unde the GNU General Public Licence. + +Hi Folks! I'm James Jeffrey Thanks for trying my software and I hope +you like it. + +This software is in a very eaarly state, the interface is fairly self +explanatory (I HOPE ;-) ) So I will let you work it out! + +If you use this software, I ask you to mail me and tell me, +use James@linux-box.demon.co.uk. + +If you hate this software tell me why, mabye I can fix it. +If you find a bug, have a suggetsion or a code fix, mail me it, +your name will be mentioned in the next release! + +I am sorry, the source is next to unreadable, I will clear this +up for 0.2 if enough interest is shown. + +If you wish to make a small donation to this hard-up student then +please feel free, I need UK currency if possible. Donations VERY +gratefully accepted. + + +PLANS FOR 0.2 or 0.3 or 0.4 etc.. +================================== + +Write a TCP/IP demon to run under INETD on the unix system +to allow for automatic creation and deletion of unix users +and setting of quitas on ext2 file systems under linux, as +well as managing volumes. + +Any help welcome. + + + +See Ya All + + +P.S. My congratulations to MArtin Stover and Team for a fantastic +piece of software - Mars NWE! + +P.P.S. + +This program was written in delphi using a GPL'd netware library +which I have slightly modified for Delphi. This library was mainly +written by R.Spronk. I have included both original and modified +versions. \ No newline at end of file diff --git a/SRC/STARTFRM.DCU b/SRC/STARTFRM.DCU new file mode 100644 index 0000000000000000000000000000000000000000..04529455b05ae287798a1b1bc6fb1ddd574cf201 GIT binary patch literal 6416 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 V@=wFy{jI0F%|jhp9}16j{{v^&l-~dV literal 0 HcmV?d00001 diff --git a/SRC/STARTFRM.PAS b/SRC/STARTFRM.PAS new file mode 100644 index 0000000..2e30203 --- /dev/null +++ b/SRC/STARTFRM.PAS @@ -0,0 +1,145 @@ +unit Startfrm; + +interface + +uses + SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, + Forms, Dialogs, NwBindry, NWConn, NWServ, StdCtrls, User, Buttons, + ExtCtrls, PassForm, SvrUTil; + +type + TForm1 = class(TForm) + + Label1: TLabel; + SpeedButton1: TSpeedButton; + SpeedButton2: TSpeedButton; + SpeedButton3: TSpeedButton; + SpeedButton4: TSpeedButton; + Panel1: TPanel; + Panel2: TPanel; + Image1: TImage; + procedure FormCreate(Sender: TObject); + procedure Label1Click(Sender: TObject); + procedure SpeedButton4Click(Sender: TObject); + procedure SpeedButton2Click(Sender: TObject); + procedure SpeedButton1Click(Sender: TObject); + procedure FormActivate(Sender: TObject); + procedure AppActivate(Sender: TObject); + procedure FormPaint(Sender: TObject); + procedure SpeedButton3Click(Sender: TObject); + procedure FormShow(Sender: TObject); + private + { Private declarations } + public + FirstTime: Boolean; + { Public declarations } + end; + +var + Form1: TForm1; + UzerID: String; +implementation + +{$R *.DFM} + + + +procedure TForm1.AppActivate(Sender: TObject); +begin + if FirstTime=FALSE then + begin + FirstTime:=True; + + end; + FormActivate(Sender); +end; + + +procedure TForm1.FormCreate(Sender: TObject); +var Lvl, ID: Byte; + UserName: String; + OwnID: Longint; +begin + Application.OnActivate:=AppActivate; +end; + +procedure TForm1.Label1Click(Sender: TObject); +begin + BtnBottomDlg.ShowModal; +end; + +procedure TForm1.SpeedButton4Click(Sender: TObject); +begin + Close; +end; + +procedure TForm1.SpeedButton2Click(Sender: TObject); +begin + BtnBottomDlg.ShowModal; +end; + +procedure TForm1.SpeedButton1Click(Sender: TObject); +var IDz: Byte; +begin + {Password Util} + UzerID:=''; + GetPrimaryConnectionID(IDz); + if GetUserAtConnection(IDz,UzerID)=TRUE then + begin + if BtnBottomDlg1.ShowModal=mrOK then + begin + if VerifyEncrBinderyObjectPassword(UzerID, OT_USER, BtnBottomDlg1.Edit1.Text)=FALSE + then MessageDlg('Could not verify password!', mtError, [mbOK], 0) + else begin + {Change Password} + if BtnBottomDlg1.Edit2.Text=BtnBottomDlg1.Edit3.Text then + begin + if ChangeEncrBinderyObjectPassword(UzerID, OT_USER, BtnBottomDlg1.Edit1.Text, BtnBottomDlg1.Edit2.Text)=FALSE + then MessageDlg('Could not Change Password', mtError, [mbOK], 0) else + MessageDlg('Password Changed', mtInformation, [mbOK], 0); + end else MessageDlg('New Passwords don''t match!', mtError, [mbOK], 0); + end + end + end else MessageDlg('Could not fetch User Name!', mtError, [mbOK], 0); +end; + +procedure TForm1.FormActivate(Sender: TObject); +var ID, Lvl: Byte; + OwnID: Longint; + UserIDB, UserName: String; +begin + UserIDB:=''; + SpeedButton3.Enabled:=CheckConsolePrivileges; + GetBinderyAccessLevel(Lvl, OwnID); + SpeedButton2.Enabled:=(Lvl=$33); + GetPrimaryConnectionID(ID); + GetUserAtConnection(ID,UserIDB); + Panel2.Caption:=UserIDB; + UserNAme:=''; + GetFileServerName(ID,UserName); + if UserName='' then + begin + UserName:='No Server!'; + SpeedButton1.Enabled:=False; + end else SpeedButton1.Enabled:=True; + Panel1.Caption:=UserName; + +end; + +procedure TForm1.FormPaint(Sender: TObject); +begin + FormActivate(Self); +end; + +procedure TForm1.SpeedButton3Click(Sender: TObject); +begin + BtnRightDlg1.ShowModal; +end; + +procedure TForm1.FormShow(Sender: TObject); +begin + MessageDlg('Welcome to NWE Administrator by James Jeffrey. Please read README for licence/warning etc. info.', + mtInformation, [mbOK], 0); +end; + +end. diff --git a/SRC/SVRUTIL.DCU b/SRC/SVRUTIL.DCU new file mode 100644 index 0000000000000000000000000000000000000000..35f2e14957f6700c5af9a3029e849e5528ff43fc GIT binary patch literal 3168 zcmd5;U2IfE6#nMkyMJ_Bwu-a_i(4qrLcx}m28q(8TWE=(m2D{^(XQ>byRh6_cW(=t z25TT;HN_O7VvI3Hq9mq?Jdzl6qdcgIiHQ%OCMqBSe+a$^>Vx&0xyyE2e;=J?_-4Ly z=FFKhbIxsRq^l8Z7tn(NIG~&Te40UU|!T5k|X^EB;&C zuhe!HR)jceE6f}l6Eo(;mai92)cAnXJ|;}Dps?=3pR|0AOqT+eCpw%SvXd#`+OyZu zElHO<`b5T#?*Z;Ale+xp@^&Z9&L;-r8DL+}0&%@ScBHLdlGkp8gilCKPb3w~WTfKH z=i2CpTHTh84JMLO@zPg2MM#mZjaGUOnNK#FqRml@Sf-t}QY7Czb3p8L?6nNpwq=sL zd{~}L*^AnJ5j!mvAOC*7en661X=jUVZcPp(?AFvk;|Jm@eDJL>AVYYbGr+gSkY~=% zywE+*3n_JJ720?6<6;C}O0P^RC9F|UhKIFbZGx8!oYf)!fUt8K58f>xlHD(Wze^}d~ALpLBe`~BSX*q{$%w{PX)_&b~ zoCc?fR%OR1I-*1~R)wM?m*ld+UFz^nTHi*CKa;WBZvZ%bwXxbJHqqT{bqLA^%DCI0_Usi4dj zH08iah57owr2;o+SOGWT4#RKU;8=G@>0dluJqz;UWiUt@(RiwVy<1T-2BbWZM7($R zmbjRll!_w)LfQd3jb_HQ$d0^$8FnP)*a0Tl)ItOm1n{WBN|X^pVvsukix8kDutJO~ zX%E^_%aQY{fqE1&*FaImI+J{+8kovZ3Z^nMfSVcVg?B0w{qPhrFbLlI&CCm8s&@FL zfX|^{K8H%U&ArYuSc9qS_fnr>-6OA^8*kWWs}!d5b*ElfR1)2`wX1W}=9L}mqfQJ_ zpjtO>k>}l@LSN0$EW+(53!tRJ!-$lyV<9ilb&JL>;~_&EX6V-QW@pQrQ0)S*aYFb| zq0Pc_tppLx&*Uz}uvUg6+H6`6FjLBTZ}T{=#tl8dC$W%R7|ljG%u*LjZ-AX+Z#BNv z7UC&y4K^7|h_%E<_7G)ZeluKu>X*6qo=`n@G_@!hxJK%oD4oS#!m%5xbU!X`D`hWZ zpN+1EYS@>tucYtvOH|Y68v4A9KCh&IjqD-va?dJ^xLUA7m-eghn`a4oE#B}oqRzV- zM?H5dzN&61dbC=+&;A*ERUK_;jkw5ORo8?zt%>6%j@NR$mgBW*-S1jGj^5G2vO1SV z!^}rj!t>0J|5uy-G2nAxD=Lt3S}1~U#+|7Pr>c)~G*!zF3IjzITzE09Zja3%R*laf zLbAL}HP3XvZVH$th0pK`nK|)vFdZMX(sm|j4aUu2e=-%%)Cc>mbZUlNW8hB1@8f6-Me)DkZ138W5}~hsI|_*F=rv{k~8aA-@1!L<2Oay5AUCx z>quyJj$;M;s+y zAqI#)5o?IQ5DyXqEJ2;bO5$C_TH-jdfw+bkCf-AACx(gJi9N&~;%4F?@pj@j#Fx;X zt7V0>i3wts_!_Z;c#^oC81^tHi5^iEqJxsrU&Zb@7hkKZK@m zvG^EWxhv$CidZg8Tuwa6^W7=Tu#ad)i48v2tU*5OID-)b30&YQ_+iLFY{k4^dc{gfx@|8~lp_6LQTY%Bl( literal 0 HcmV?d00001 diff --git a/SRC/SVRUTIL.DFM b/SRC/SVRUTIL.DFM new file mode 100644 index 0000000000000000000000000000000000000000..dd96f8b8654dd2e1ac0303dfe6e4f6e0ad648c58 GIT binary patch literal 951 zcmaiy%W~5&6oyqfwd^?Qlm#=)u<;W>0$VoNA(u(gWMV^EBHN0si7L64+NQ&6uwldS zO1udhwiNl2%cjK}pJQ9+`~Uy=p$$SWyjV>}<8Uw=^+E6arvX5?9QL}Qr}&DdnHsRP z-#KntGqP63UtokHH-3Qb`&iLi;tQ?{!O-bqsy}u-Q6!|8#DYr)G+m(X89`Pg2NW}r zA|L093T~bSq_`!8`%zIw)qTfjlyEgBB^`cs>h_W*Dl>kA_E3Xf`GSc8p_q9L#~Y`5 z)*Sd_SKS4^B4&3UxB=$U)dae(k5(!ib1gl=63|`JEn#x0wemQFU#z8&v{Z%8auLJ@ zA>7wuNMSB`kcm5F4MlNPR_%~%cOtt0g&tMfcx}Im2)-*5f}c+->2Pi~0$*AojuvG+ z5TFIu_Tmj{SFu%fYf5>7TG3`PHRrfUDTfdRIgTk$;e$PqP1H8nQ*9sfm@v(+m-DS! z&Xeeq8e>J9`t1Kub~q};P40=Uy({{)&GX8pRogUvft}CEe!IyA9M#QatD5|x3GjM1_}TH`J6!==Oy{TQ) ztls0SPC$6#&WV&bB?rdI6QkcAj~+JxecuOk@bUZu#}Om12UM1KcP8}K|mj?gNKOaJ9LoJV2V2=Ow*h&-*;|rr7*(7 z4m%Whs7UDGA&Lm0qjZQD@(@8(hhQEG6~fh_got^w@9%y0ZcQ&8{k?nN&wk(M`8~fs z&-1=vWvrLbb}2!>1yMYWs}0vf@wK>#D^A3SlDHkTAo!bin4hA2z<;(orr!j{TP=ux zk!sIY*V(T9_Xx{J`b4Yd=seR{$FVpyXHUg8E)YqwlAKLXX0v@n2c@zVL2|~Hvii(C zQLu?S<0r}EbL!|)dL~P>nCM{Tsu;~^*CGBY2bqU)PnVKHJns0X7{Nl!(L(aW(|-vvCK)|zTscyNSE zXDq$)+(pf@`#siUAD41xi01?&O)!4P;6jDR694ko|^I1Hx2c5oYf zL=hu^g3!SvSOC|+DEJbLgCQ5ueJ~1!K?9ryi(nGm2U8&b9;CrF{sG{2@EV;niipz# z`rsDx0F~flU6S$4>pJ~b{ Xp}z&Y8zfQ<{#j~5oGq}KOJ@5E_8SDbJ$(HTP<+6*!2q#4Ek>Q8XX8cQF- z=1%U-J>Q(1?7IN_qaau=UOaDh4~7>2f_yX?_OMT~>`SW19H*IWp@-%gL2g1^Sy)WUexg5iVool2kJ(jPrVLP+s(0&Zy82l*NgLZ&ukZ zS)`NZFX&Ek{Ix5&R0xIK;UwHx#jFzSIz{(PKB!^OgF!$rz_Su#9!)NFD*( zXIsiMpQ=X@Ch*<#WyTJ+&{-;jP*Ez}+zeG+Ot-Zs8yI*-R+Jk>y(waVW|2atT{wlW zM&-voK6HEl4bm#hTL9X495MYL)G4Q5a+*J&23_{*pxlHgqj6@e+U u(OyjRT9Td$M|YK@c@63xg^kvfi3qh;o5j?a6BRQ7Aqr9wG7-al&iw0 + then holds:=holds+Lswap(holdVal[t].HoldAmount); + end; + if nwBindry.result=$FB + then begin + result:=0; + holds:=0; + end + else result:=nwBindry.result; + end + else if nwBindry.result=$FB { no such property } + then result:=$C1 + else if nwBindry.result=$F1 { invalid bindery security } + then result:=$C0 + else result:=nwBindry.result; +{ resultcodes: 00 success; C0 No Account Privileges; C1 No Account Balance; + 96 Server Out Of memory; FC No Such Object; FE Server Bindery Locked; + FF Bindery Failure} +end; + + +{F217/96 [2.15c+]} +Function GetAccountStatus(objName:string; objType:word; + Var balance,limit,holds:LongInt):boolean; +{ equivalent to reading the ACCOUNT_BALANCE and ACCOUNT_HOLDS properties + of the object. The properties may not exist. } +{ This function will be successful if: + a) the caller is an accounting server on the current fileserver + OR b) the caller is supervisor-equivalent + OR c) the caller is querying his own account status } +Type Treq=record + len:word; + subF:byte; + _objType:word; {hi-lo} + _objName:string[48]; + end; + Trep=record + _balance: LongInt; {hi-lo} + _limit : Longint; {hi-lo} + reserved: array [1..120] of byte; + _holds : array [1..16] + of record + serverObjId:LongInt; {hi-lo} + HoldAmount :LongInt {hi-lo} + end; + end; + TPreq=^Treq; + TPrep=^Trep; +Var t:byte; +begin +With TPreq(GlobalReqBuf)^ + do begin + len:=sizeOf(Treq)-2; + subf:=$96; + _objType:=swap(objType); { force hi-lo} + PstrCopy(_objName,objName,48); UpString(_objName); + end; +F2SystemCall($17,sizeOf(Treq),sizeOf(Trep),result); +With TPrep(GlobalReplyBuf)^ + do begin + balance:=Lswap(_balance); { force lo-hi again } + limit:=Lswap(_limit); { force lo-hi again } + holds:=0; + for t:=1 to 16 + do if _holds[t].serverObjId<>0 + then holds:=holds+Lswap(_holds[t].holdAmount); { force lo-hi again } + end; +IF result=$C0 { no account privileges } + then GetBindryAccountStatus(objName,objType,balance,limit,holds); + { try to read status not as an accounting server, but as a supervisor } +GetAccountStatus:=(result=0); +{ resultcodes: 00 success; C0 No Account Privileges; C1 No Account Balance } +end; + + +{F217/97 [2.15c+]} +Function SubmitAccountCharge(objName:string; objType:word; + charge,cancelHoldAmount:Longint; + serviceType, commentType:word; comment:string):boolean; +{ -The cancelHold amount should be exactly the same as the amount that + was put on huld with the SubmitAccountHold call. If no + SubmitAccountHold call was made, the cancelHoldAmount should be set to zero. + -'negative charges' are allowed. They will increase the balance of + the object objName of objType. + -Use the objectType of caller for the serviceType parameter. + (audit log purposes) + -Set commentType to 0 and comment to '' if you aren't interested in the + audit log. + -To be called by accounting servers only. + -Can be imitated by supervisor-equivalent users by + calling GetAccountStatus and SetAccountStatus. Atomicity + of such a bindery transcation can not be guaranteed. + + } +Type Treq=record + len :word; + subf:byte; + _serviceType:word; {hi-lo} + _charge :Longint; {hi-lo} + _cancelHold :Longint; {hi-lo} + _objType :word; {hi-lo} + _commentType:word; {hi-lo} + _objNameAndComment:Array[1..305] of char; + end; + TPreq=^Treq; +Var p:byte; +begin +With TPreq(GlobalReqBuf)^ + do begin + subf:=$97; + _serviceType:= swap(serviceType); {force hi-lo} + _charge :=Lswap(charge); {force hi-lo} + _cancelHold :=Lswap(cancelHoldAmount); {force hi-lo} + _objType := swap(objType); {force hi-lo} + _commentType:= swap(commentType); {force hi-lo} + p:=ord(objName[0]);if p>48 then p:=48; + UpString(objName); + Move(objname[0],_objNameandComment[1],p+1); + Move(comment[0],_objNameandComment[p+2],ord(comment[0])+1); + len:=15+p+1+ord(comment[0])+1; + F2SystemCall($17,len+2,0,result); + end; +SubmitAccountCharge:=(result=$00); +{ resultcodes: 00 successful; C0 No Account Privileges; + C1 No Account Balance; C2 Credit Limit Exceeded. } +end; + + +{F217/98 [2.15c+]} +Function SubmitAccountHold(objName:string; objType:word; + reserveAmount:Longint ):boolean; +{ To be called by accounting servers only. } +Type Treq=record + len :word; + subf:byte; + _reserveAmount:Longint; {hi-lo} + _objType:word; {hi-lo} + _objName:string[48]; + end; + TPreq=^Treq; +Var p:byte; +begin +With TPreq(GlobalReqBuf)^ + do begin + subf:=$98; + _reserveAmount:=Lswap(ReserveAmount); { force hi-lo} + _objType:=swap(objType); { force hi-lo } + p:=ord(objName[0]); if p>48 then p:=48; + _objName:=objname;UpString(_objName);_objName[0]:=chr(p); + len:=7+p+1; + F2SystemCall($17,len+2,0,result); + end; +SubmitAccountHold:=(result=$00); +{ resultcodes: 00 successful; C0 No Account Privileges; + C1 No Account Balance; C2 Credit Limit Exceeded. + C3 Account Too Many Holds } +end; + +{F217/99 [2.15c+]} +Function SubmitAccountNote(objName:string; objType:word; + serviceType,commentType:word; comment:string):boolean; +{ To be called by accounting servers only.} +Type Treq=record + len:word; + subf:byte; + _serviceType:word; {hi-lo} + _objType:word; {hi-lo} + _commentType:word; {hi-lo} + _objNameAndComment:array[1..305] of char; + end; + TPreq=^Treq; +Var p:byte; +begin +with TPreq(GlobalReqBuf)^ + do begin + subf:=$99; + _serviceType:= swap(serviceType); {force hi-lo} + _objType := swap(objType); {force hi-lo} + _commentType:= swap(commentType); {force hi-lo} + p:=ord(objName[0]);if p>48 then p:=48; + UpString(objName); + Move(objname[0],_objNameandComment[1],p+1); + Move(comment[0],_objNameandComment[p+2],ord(comment[0])+1); + len:=7+p+1+ord(comment[0])+1; + F2SystemCall($17,len+2,0,result); + end; +SubmitAccountNote:=(result=0); +{resultcodes: 00 Successful; C0 No Account Privileges } +end; + +{---------------- Secondary Functions--------------------------------------} + + +Function AccountingInstalled:boolean; +Var propVal:Tproperty; + connId:byte; + moreSegments:boolean; + propFlags:byte; + currServerName:string; +begin +IF NOT GetEffectiveConnectionID(ConnId) + then result:=nwConn.result + else if NOT GetFileServerName(ConnId,currServerName) + then result:=nwConn.result + else begin + ReadPropertyValue(currServerName,OT_FILE_SERVER,'ACCOUNT_SERVERS',1, + propVal,moreSegments,propFlags); + result:=nwBindry.result; + end; +AccountingInstalled:=(result=0); +end; + + +Function SetAccountStatus(objName:string; objType:word; balance,limit:LongInt):boolean; +{ will change the account status to reflect the given parameters. + any holds will not be changed. + You need to be supervisor-eq. to do this...} +Var accPropVal:Tproperty; + accVal: record + _balance:LongInt; {hi-lo} + _limit:LongInt; {hi-lo} + _Reserved:array[1..120] of byte; { NW internal info } + end ABSOLUTE accPropVal; + OldBalance,OldLimit,OldHolds:LongInt; + moreSegments:boolean; + propFlags:byte; +begin +IF ReadPropertyValue(objName,objType,'ACCOUNT_BALANCE',1, + accPropVal,moreSegments,propFlags) + then begin + accVal._balance:=Lswap(balance); { force hi-lo} + accVal._limit:=Lswap(limit); { force hi-lo} + WritePropertyValue(objName,objType,'ACCOUNT_BALANCE', + 1,accPropVal,FALSE); + if (nwBindry.result=$F1) or (nwBindry.result=$F8) + then result:=$C0 + else result:=nwBindry.result; + end + else if nwBindry.result=$FB { no such property } + then result:=$C1 + else if nwBindry.result=$F1 { invalid bindery security } + then result:=$C0 + else result:=nwBindry.result; +SetAccountStatus:=(result=$00); +{ resultcodes: 00 success; C0 No Account Privileges; C1 No Account Balance; + 96 Server Out Of memory; FC No Such Object; FE Server Bindery Locked; + FF Bindery Failure} +end; + + +Function AddAccountingServer(objName:string;objType:word):boolean; +Var ConnId:byte; + currServerName:string; +begin +IF NOT GetEffectiveConnectionID(ConnId) + then result:=nwConn.result + else if NOT GetFileServerName(ConnId,currServerName) + then result:=nwConn.result + else begin + AddBinderyObjectToSet(currServerName,OT_FILE_SERVER,'ACCOUNT_SERVERS', + objName,objType); + result:=nwBindry.result; + end; +AddAccountingServer:=(result=0); +end; + +Function DeleteAccountingServer(objName:string;objType:word):boolean; +Var ConnId:byte; + currServerName:string; +begin +IF NOT GetEffectiveConnectionID(ConnId) + then result:=nwConn.result + else if NOT GetFileServerName(ConnId,currServerName) + then result:=nwConn.result + else begin + DeleteBinderyObjectFromSet(currServerName,OT_FILE_SERVER,'ACCOUNT_SERVERS', + objName,objType); + result:=nwBindry.result; + end; +DeleteAccountingServer:=(result=0); +end; + +{F217/96 } +Function DeleteAccountHolds(objName:string; objType:word):boolean; +{ delete all holds the caller (an accounting server) has on the + object with name objName of type objType. } +Type Treq=record + len:word; + subF:byte; + _objType:word; {hi-lo} + _objName:string[48]; + end; + Trep=record + _balance: LongInt; {hi-lo} + _limit : Longint; {hi-lo} + reserved: array [1..120] of byte; + _holds : array [1..16] + of record + serverObjId:LongInt; {hi-lo} + HoldAmount :LongInt {hi-lo} + end; + end; + TPreq=^Treq; + TPrep=^Trep; +Var t:byte; + holds:LongInt; + level:byte; + accServerId:LongInt; + accServerType:word; + accServerName:string; +begin +GetBinderyAccessLevel(Level,accServerID); +GetBinderyObjectName(accServerID,accServerName,accServerType); +With TPreq(GlobalReqBuf)^ + do begin + len:=sizeOf(Treq)-2; + subf:=$96; + _objType:=swap(objType); { force hi-lo} + PstrCopy(_objName,objName,48); UpString(_objName); + end; +F2SystemCall($17,sizeOf(Treq),sizeOf(Trep),result); +if result=0 + then With TPrep(GlobalReplyBuf)^ + do begin + holds:=0; + for t:=1 to 16 + do if accServerID=Lswap(_holds[t].serverObjId) + then holds:=holds+Lswap(_holds[t].holdAmount); { force lo-hi again } + if holds<>0 + then SubmitAccountCharge(objName,objType,0,holds, + accServerType,0,'clearing holds'); + end; +DeleteAccountHolds:=(result=0); +{ resultcodes: 00 success; C0 No Account Privileges; C1 No Account Balance } +end; + + +Function GetConnectTimeCharge(Var currentCharge:Real;Var chargeRec:TchargeRec):boolean; +Var propVal:Tproperty; + _chargeRec:TchargeRec ABSOLUTE propVal; + _currcharge:record + fill:LongInt; + currMult,currDiv:word; {hi-lo} + end ABSOLUTE propVal; + connId:byte; + moreSegments:boolean; + propFlags:byte; + currServerName:string; +begin +IF NOT GetEffectiveConnectionID(ConnId) + then result:=nwConn.result + else if NOT GetFileServerName(ConnId,currServerName) + then result:=nwConn.result + else if ReadPropertyValue(currServerName,OT_FILE_SERVER, + 'CONNECT_TIME',1, + propVal,moreSegments,propFlags) + then begin + IF _currCharge.currDiv=0 + then currentCharge:=0 + else currentCharge:=Swap(_currCharge.currMult)/Swap(_currCharge.currDiv); + move(propVal[9],propVal[5],124); + chargeRec:=_chargeRec; + result:=0; + end + else result:=nwBindry.result; +GetConnectTimeCharge:=(result=0); +end; + + + +end. \ No newline at end of file diff --git a/SRC/UNITS/NWBINDRY.DCU b/SRC/UNITS/NWBINDRY.DCU new file mode 100644 index 0000000000000000000000000000000000000000..745050225a0e3b9953de33e74ffc87f8acb5820b GIT binary patch literal 33408 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 Then _pw[0]:=#127; + +IF GetEncryptionKey(key) + Then Begin + + IF GetBinderyObjectId(objName,objType,ObjId) + Then Begin + EncryptPassword(objId,_pw,key); + VerifyEncrypted(ObjName, ObjType, key); + End; + End + Else VerifyBinderyObjectPassword(ObjName, ObjType, Password); + +VerifyEncrBinderyObjectPassword := (result1=0); +End; + + +{F217/37 [2.15c+]} +Function ScanBinderyObject( SearchObjName: String; + SearchObjType: Word; + {i/o:} Var lastObjSeen : Longint; + {out:} Var RepName : String; + Var RepType : Word; + Var RepId : LongInt; + Var RepFlag : Byte; + Var RepSecurity : Byte; + Var RepHasProperties: Boolean + ) :boolean; +{ This function scans the bindery and returns complete information about + a bindery object. } +Type TReq = record + length : word; + subfunction : byte; + last_obj_id : longint; {hi-lo} + search_obj_type : word; {hi-lo} + search_obj_name : string[48]; + end; + TRep= record + object_id : longint; {hi-lo} + object_type : word; {hi-lo} + object_name : array [1..48] of byte; + object_flag : byte; + security : byte; + properties : byte; + end; + TPreq=^Treq; + TPrep=^Trep; + +Var TempStr:string; + count : integer; +begin +with TPreq(GlobalReqBuf)^ +do begin + length := SizeOf(Treq)-2; + subfunction := $37; + last_obj_id := Lswap(lastObjseen); { force hi-lo } + search_obj_type:= swap(Word(SearchObjType)); { force hi-lo } + PstrCopy(Search_obj_name,SearchObjName,48); Search_obj_Name[48]:=#0; UpString(Search_obj_name); + end; +F2SystemCall($17,sizeOf(Treq),sizeOf(Trep),result1); +With TPrep(GlobalReplyBuf)^ +do begin + repFlag := object_flag; + repHasProperties := (properties>0); + repSecurity := security; + repType := swap(object_type); { force lo-hi } + repId := Lswap(object_id); { force lo-hi } + lastObjSeen := repId; + ZStrCopy(repName,Object_Name,48); + end; +scanBinderyObject:=(result1=0); +{ Possible result1codes: + 96h server out of memory; EFh Invalid Name; FCh No Such Object; + FEh Server Bindery Locked; FFh Bindery failure } +end; + + +{F217/3D [2.15c+]} +Function ReadPropertyValue( objName:String; objType:Word; + propName:String; segmentNumber:Word; + Var propValue : Tproperty; + Var moreSegments: Boolean; + Var propFlags : Byte ):boolean; +{ Returns the value of a property associated with a Bindery object. } +Type Treq=record + len : word; + subfunction : byte; + _objType : word; { hi-lo } + _ObjName : string[48]; + _segNbr : byte; + _propName : string[15]; + end; + Trep = record + _propValue : Tproperty; {array [1..128] of byte} + _moreSegments : byte; + _propFlags : byte; + end; + TPreq=^Treq; + TPrep=^Trep; +BEGIN + With TPreq(GlobalReqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subfunction := $3d; + _objType:=swap(objType); { force hi-lo } + _segNbr:=segmentNumber; + PStrCopy(_ObjName,objName,48); _ObjName[48]:=#0; UpString(_ObjName); + PStrCopy(_PropName,propName,15); UpString(_propName); + end; +F2SystemCall($17,sizeof(Treq),sizeof(Trep),result1); +if result1=0 + then with TPrep(GlobalreplyBuf)^ + do begin + propValue:=_propValue; + moreSegments:=(_moreSegments>0); + propFlags:=_propFlags; + end; +ReadPropertyValue:=(result1=0); +{ 96 server out of memory; EC no such segment; F0 wilcard not allowed; + f1 invalid bindery security; f9 no property read privileges; + fb no such property; fc no such object; FE Server Bindery Locked; + FF Bindery Failure. } +end; + + +{F217/36 [2.15c+] } +Function GetBinderyObjectName( object_Id:LongInt; + Var objName:String; Var objType:word ):boolean; +{ returns the type and name of an object, given its four BYTE-id. } +Type TReq =record + len:word; + subF:byte; + _objId:LongInt; { hi-lo } + end; + Trep=record + _objId:LongInt; { hi-lo } + _objType:word; { hi-lo } + _objName:array[1..48] of Byte; + end; + TPreq=^Treq; + TPrep=^Trep; +BEGIN +WITH TPreq(GlobalReqBuf)^ +do begin + len :=SizeOf(TReq)-2; + SubF:=$36; + _objId:=Lswap(object_Id); { force hi-lo } + end; +F2SystemCall($17,sizeOf(Treq),sizeOf(Trep),result1); +IF result1=0 + then with TPrep(GlobalReplyBuf)^ + do begin + ZstrCopy(objName,_objName,48); + objType:=swap(_objType); { force lo-hi } + end; +GetBinderyObjectName:=(result1=0); +end; + + +{F217/35 [2.15c+] } +Function GetBinderyObjectID( objName:String; objType:word; + Var objID:Longint ):boolean; +{ returns the object ID of an object, given its type and name. } +Type Treq=record + len:word; + subF:Byte; + _objType:word; { hi-lo } + _objName:string[48]; + end; + TRep=record + _objId:LongInt; { hi-lo } + _objType:word; { hi-lo } + _objName:array[1..48] of char; + end; + TPreq=^Treq; + TPrep=^Trep; +BEGIN +WITH TPreq(GlobalReqBuf)^ +do begin + len :=SizeOf(TReq)-2; + SubF:=$35; + _objType:=swap(objType); { force hi-lo } + PStrCopy(_objName,objName,48); _objName[48]:=#0; + UpString(_objName); + end; +F2SystemCall($17,sizeOf(Treq),sizeOf(Trep),result1); +IF result1=0 + then with TPrep(GlobalReplyBuf)^ + do objID:=Lswap(_objId); { force lo-hi } +GetBinderyObjectID:=(result1=0); +end; + + +{F217/46 [2.15c+]} +Function getBinderyAccessLevel(Var SecurityAccessLevel:byte; + Var objId:Longint ):boolean; +{ It returns the user's access level to the bindery. } +{ Often used as a quick way of determining the current users' object id } +{ use the BS_xxxx constants to determine the exact rights of the user } +Type Treq=record + len :word; + subF :byte; + end; + Trep=record + accLeveL:byte; + _objId:longInt; + fill:array[1..20] of byte; + end; + TPreq=^Treq; + TPrep=^Trep; +BEGIN +With TPreq(GlobalReqBuf)^ + do begin + subF:=$46; + len:=sizeOf(Treq)-2; + end; +F2SystemCall($17,sizeOf(Treq),sizeOf(Trep),result1); +If result1=0 + then with TPrep(GlobalReplyBuf)^ + do begin + SecurityAccessLevel:=accLevel; + objId:=Lswap(_objId); + end; +GetBinderyAccessLevel:=(result1=0); +end; + + + +{F217/45 [2.15c+]} +Function OpenBindery:boolean; +{ This call must be used after the CloseBindery call. No other bindery + call will work while the bindery is closed. } +Type Treq=record + len:word; + subFunc:byte; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + len:=1; + subFunc:=$45; + end; +F2SystemCall($17,sizeOf(Treq),0,result1); +OpenBindery:=(result1=0) +end; + + +{F217/44 [2.15c+]} +Function CloseBindery:boolean; +{ Closes the bindery files so they can be backed up. (Supervisor only) } +Type Treq=record + len:word; + subFunc:byte; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + len:=SizeOf(Treq)-2; + subFunc:=$44; + end; +F2SystemCall($17,sizeOf(Treq),0,result1); +CloseBindery:=(result1=0) +end; + + +{F217/32 [2.15c+] } +Function CreateBinderyObject(objName:string; objType:Word; + objFlaG, objSecurity :Byte ):boolean; +{ Creates an object in the bindery. + objName: name of the new object (47 chars) + objType: object type number (own type number or OT_xxx constant) + objFlag: identifies an object as static (0) or dynamic (1) + (dynamic objects are removed from the bindery when the server goes down) + objSecurity: high nibble: write privileges needed to modify this object + low nibble: read privileges needed to access this object + (default: $31 Supervisor write/Logged read) } +Type Treq=record + len :word; + subFunc :byte; + _objFlag :Byte; + _objSecurity :Byte; + _objType :word; { hi-lo } + _objName :string[48] + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + len:=SizeOf(Treq)-2; + subFunc:=$32; + _objFlag:=objFlag; + _objSecurity:=objSecurity; + _objType:=swap(objType); { force hi-lo } + PStrCopy(_objName,objName,48); _objName[48]:=#0; UpString(_objName); + end; +F2SystemCall($17,sizeof(Treq),0,result1); +CreateBinderyObject:=(result1=0) +{ 96h server out of memory; EEh Object Already Exists; EFh Invalid Name + F1h invalid Bindery security; F5h no object create privileges + FEh Server Bindery Locked; FFh Bindery Failure } +end; + + + +{F217/33 [2.15c+] } +Function DeleteBinderyObject( objName:String; objType:Word ):boolean; +{ deletes a bindery object and all asociated properties. } +Type Treq=record + len :word; + subFunc :byte; + _objType :Word; { hi-lo } + _objName :string[48]; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalreqBuf)^ +do begin + len:=SizeOf(Treq)-2; + subFunc:=$33; + _objType:=swap(objType); { force hi-lo } + PStrCopy(_objName,objName,48); _objName[48]:=#48; UpString(_objName); + end; +F2SystemCall($17,sizeOf(Treq),0,result1); +DeleteBinderyObject:=(result1=0) +{ 96h Server out of memory; EFh Invalid name; F0h wildcard not allowed; + F4h No object delete privileges; FCh no such object + FEh Server Bindery Locked; FFh bindery failure } +end; + + +{F217/34 [2.15c+]} +Function RenameBinderyObject( objName,NewObjName :string; objType :word ):boolean; +{ This function allows the (supervisor-equivalent) user to rename an object, + given its' type and old name. } +Type Treq=record + len :word; + subFunc :byte; + _objType :word; { hi-lo } + _objName :string[48]; + _NewObjName :string[48]; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + len:=SizeOf(Treq)-2; + subFunc:=$34; + _objType:=swap(objType); { force hi-lo } + PstrCopy(_objName,objName,48); _objName[48]:=#0; Upstring(_objName); + PstrCopy(_NewObjName,NewObjName,48); _NewObjName[48]:=#0; UpString(_NewObjName); + end; +F2SystemCall($17,sizeOf(Treq),0,result1); +RenameBinderyObject:=(result1=0) +{ 96h Server out of memory; EFh Invalid name; F0h wildcard not allowed; + F3h No object rename privileges; FCh no such object + FEh Server Bindery Locked; FFh bindery failure } +end; + + + +{F217/43 [2.15c+]} +Function IsBinderyObjectInSet(objName:String; objType:Word; + propName, memberName:String; memberType:Word ):boolean; +{ Allows the programmer to check whether a bindery object is a member of a + set-property. Objectname( of Objecttype) is the object to be searched for, + PropName (attached to the object with name memberName (of memberType)) + is the property containing the set to be searched. + User must have read rights to the object and the property. + Ex: ('SUPERVISOR',OT_USER,'GROUP_MEMBERS','EVERYONE',OT_USER_GROUP) } +Type Treq=record + len :word; + subFunc :byte; + _objType :Word; { hi-lo } + _objName :String[48]; { [48]=#0 } + _propName :String[15]; + _memObjType :Word; { hi-lo } + _memName :String[48]; { [48]=#0 } + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + len:=SizeOf(Treq)-2; + subFunc:=$43; + _objType:=swap(objType); { force hi-lo } + PstrCopy(_objName,objName,48); _objName[48]:=#0; UpString(_objName); + PstrCopy(_propName,propName,15); UpString(_propName); + _memObjType:=swap(memberType); { force hi-lo } + PStrCopy(_memName,memberName,48); _memName[48]:=#0; UpString(_memName); + end; +F2SystemCall($17,sizeOf(Treq),0,result1); +IsBinderyObjectInSet:=(result1=0) +{ 96h Server out of memory; EA No Such member; EB Not Group Property + F0h wildcard not allowed; F9 No Property read privileges; + FCh no such object; FEh Server Bindery Locked; FFh bindery failure } +end; + + + +{F217/41 [2.15c+]} +Function AddBinderyObjectToSet(objName:String; objType:Word; + propName, memberName:String; memberType:Word ):boolean; +{ Adds a bindery object to a property set. + user must have write access to the set property. } +Type Treq=record + len :word; + subFunc :byte; + _objType :Word; { hi-lo } + _objName :String[48]; { [48]=#0 } + _propName :String[15]; + _memObjType :Word; { hi-lo } + _memName :String[48]; { [48]=#0 } + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + len:=SizeOf(Treq)-2; + subFunc:=$41; + _objType:=swap(objType); { force hi-lo } + PstrCopy(_objName,objName,48); _objName[48]:=#0; UpString(_objName); + PstrCopy(_propName,propName,15); UpString(_propName); + _memObjType:=swap(memberType); { force hi-lo } + PStrCopy(_memName,memberName,48); _memName[48]:=#0; UpString(_memName); + end; +F2SystemCall($17,sizeOf(Treq),0,result1); +AddBinderyObjectToSet:=(result1=0) +{ 96h Server out of memory; E9 Member already Exists; EB Not Group Property + F0h wildcard not allowed; F8 No Property write privileges; + FCh no such object; FEh Server Bindery Locked; FFh bindery failure } +end; + + +{F217/42 [2.0/2.1/3.x]} +Function DeleteBinderyObjectFromSet(objName:String; objType:Word; + propName, memberName:String; memberType:Word ):boolean; +{ Deltes a bindery object from a property set. + user must have write access to the set property. } +Type Treq=record + len :word; + subFunc :byte; + _objType :Word; { hi-lo } + _objName :String[48]; { [48]=#0 } + _propName :String[15]; + _memObjType :Word; { hi-lo } + _memName :String[48]; { [48]=#0 } + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + len:=SizeOf(Treq)-2; + subFunc:=$42; + _objType:=swap(objType); { force hi-lo } + PstrCopy(_objName,objName,48); _objName[48]:=#0; UpString(_objName); + PstrCopy(_propName,propName,15); UpString(_propName); + _memObjType:=swap(memberType); { force hi-lo } + PStrCopy(_memName,memberName,48); _memName[48]:=#0; UpString(_memName); + end; +F2SystemCall($17,sizeOf(Treq),0,result1); +DeleteBinderyObjectFromSet:=(result1=0) +{ 96h Server out of memory; EA No Such Member; EB Not Group Property + F0h wildcard not allowed; F8 No Property write privileges; FB No Such property; + FCh no such object; FEh Server Bindery Locked; FFh bindery failure } +end; + + +{F217/38 [2.15c+]} +Function ChangeBinderyObjectSecurity(objName :String; objType :Word; + NewObjSecurity :Byte ):boolean; +{ Changes the security of a Bindery object. This call is made successfully, + if the user is supervisor equivalent and the current security is unequal to + NetWare Read/ NetWare Write. } +Type Treq=record + len :word; + subFunc :byte; + _NobjSec :Byte; + _objType :Word; { hi-lo } + _objName :String[48]; { [48]=#0 } + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + len:=SizeOf(Treq)-2; + subFunc:=$38; + _NobjSec:=NewObjSecurity; + _objType:=swap(objType); { force hi-lo } + PstrCopy(_objName,objName,48); _objName[48]:=#0; UpString(_objName); + end; +F2SystemCall($17,sizeOf(Treq),0,result1); +ChangeBinderyObjectSecurity:=(result1=0) +{ Completion Codes: + 96 Server out of memory; F0 Wildcard Not Allowed; F1 Invalid Bindery Security; + FC No Such Object; FE Server Bindery Locked; FF Bindery Failure. } +end; + + + + +{F217/4B [3.x]} +FUNCTION ChangeEncrBinderyObjectPassword(ObjName: String; ObjType: Word; +{#d} oldPassWord,newPassword: String): Boolean; +{ Changes the password of a bindery object. + Old Password can be NULL. To log into a file server, an object must have a + PASSWORD property. User must have read and write access to the bindery object. } + FUNCTION ChangeEncrypted(ObjName : String; ObjType : Word; + oldEncrPW:TencryptionKey; + Var newPWdif:TencrPWdifference; + PWdifCheckSum:byte + ):boolean; + Type Treq=RECORD + BufLen : Word; + _func : Byte; + _oldPW : TencryptionKey; + _ObjType: Word; + _ObjNameLen : byte; + _Various: array [1..48+1+16] of byte; { ObjName, difCheksum, PWdif } + End; + TPreq=^Treq; + Begin + With TPreq(GlobalreqBuf)^ + do Begin + _func := $4B; + _oldPW:=oldEncrPW; + _ObjType := Swap(objType); + if objName[0]>#48 + then objName[0]:=#48; + move (objName[0],_objNameLen,ord(objName[0])+1); + _Various[_objNamelen+1]:=PWdifCheckSum; + move(newPWdif,_Various[_objNamelen+2],16); + BufLen:=29+_objNameLen; + F2SystemCall($17,buflen+2,0,result1); + end; + ChangeEncrypted:=(result1=0); + End; + +VAR + key : TencryptionKey; + ObjId:LongInt; + PWdif:TencrPWdifference; + PWdifChecksum:byte; + +Begin +UpString(oldPassword); +if oldPassword[0]>#127 + Then oldPassword[0]:=#127; +UpString(newPassword); +if newPassword[0]>#127 + Then newPassword[0]:=#127; +UpString(ObjName); + +IF GetEncryptionKey(key) + Then Begin + IF GetBinderyObjectId(objName,objType,ObjId) + Then Begin + EncryptPasswordDifference(objId, + OldPassword,NewPassword, + key, { i/o, out: EncrOldPW } + PWdif, { out, 16 bytes } + PWdifChecksum { out, 1 byte } + ); + ChangeEncrypted(ObjName, ObjType, key, PWdif, PWdifChecksum); + End; + End + Else ChangeBinderyObjectPassword(ObjName, ObjType, OldPassword, NewPassword); + +ChangeEncrBinderyObjectPassword:= (result1=0); +{ Completion Codes: + 96 Server Out Of Memory; F0 Wildcard Not Allowed; FB No Such Property; + FC No Such Object; FE Server Bindery Locked; FF No Such Object *OR* + No Password Associated With Object *OR* Old Password Invalid. } +End; + + +{F217/40 [2.0/2.1/3.x] } +Function ChangeBinderyObjectPassword(objName:String; objType:Word; + oldPassWord,newPassWord:String ):boolean; +{ Changes the password of a bindery object. + Allow unencrypted passwords must be ON! + Old Password can be NULL. To log into a file server, an object must have a + PASSWORD property. User must have read and write access to the bindery object. } +Type Treq=record + len :word; + subFunc :byte; + _objType :Word; { hi-lo } + _objName :String[48]; { [48]=#0 } + _oldPW :String[128]; { wow! a password of 128 chars! } + _newPW :String[128]; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + len:=SizeOf(Treq)-2; + subFunc:=$40; + _objType:=swap(objType); { force hi-lo } + PStrCopy(_objName,objName,48); _ObjName[48]:=#0; UpString(_objName); + PStrCopy(_oldPW,oldPassWord,128); UpString(_oldPW); + PStrCopy(_newPW,newPassWord,128); UpString(_newPW); + end; +F2SystemCall($17,sizeOf(Treq),0,result1); +ChangeBinderyObjectPassword:=(result1=0) +{ Completion Codes: + 96 Server Out Of Memory; F0 Wildcard Not Allowed; FB No Such Property; + FC No Such Object; FE Server Bindery Locked; FF No Such Object *OR* + No Password Associated With Object *OR* Old Password Invalid. } +end; + + + + +{F217/39 [2.15c+]} +Function CreateProperty( objName:String; objType:Word; + propertyName:String; propFlags,propSecurity:Byte ):boolean; +{ Creates a property to be associated with a bindery object. + property flags tell whether a property is dynamic or static and whether + the property is defined as static or dynamic. } +Type Treq=record + len :word; + subFunc :byte; + _objType :Word; { hi-lo } + _objName :String[48]; { [48]=#0 } + _propFlags:Byte; + _propSec :Byte; + _propName :String[15]; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + len:=SizeOf(Treq)-2; + subFunc:=$39; + _objType:=swap(objType); { force hi-lo } + PStrCopy(_objName,objName,48); _objName[48]:=#0; UpString(_objName); + _propFlags:=propFlags; + _propSec:=propSecurity; + PStrCopy(_propName,propertyName,15); UpString(_propName); + end; +F2SystemCall($17,sizeof(Treq),0,result1); +CreateProperty:=(result1=0) +{ Completion Codes: + 96 Server Out Of Memory; ED Property already exists; EF Invalid Name; + F0 Wildcard Not Allowed; F1 Invalid Bindery Security; F7 No Property Create Privileges; + FC No Such Object; FE Server Bindery Locked; FF Bindery Failure } +end; + + +{F217/3A [2.15c+]} +Function DeleteProperty( objName:String; objType:Word; + propertyName:String ):boolean; +{ Deletes a property from a bindery object. + The property field may contain wildcards. } +Type Treq=record + len :word; + subFunc :byte; + _objType :Word; { hi-lo } + _objName :String[48]; { [48]=#0 } + _propName :String[15]; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + len:=SizeOf(Treq)-2; + subFunc:=$3A; + _objType:=swap(objType); { force hi-lo } + PStrCopy(_objName,objName,48); _objName[48]:=#0; UpString(_objName); + PStrCopy(_propName,propertyName,15); UpString(_propName); + end; +F2SystemCall($17,sizeOf(Treq),0,result1); +DeleteProperty:=(result1=0) +{ Completion Codes: + 96 Server Out Of Memory; F0 Wildcard Not Allowed; F1 Invalid Bindery Security; + F6 No property delete privileges; FB No Such property; + FC No Such Object; FE Server Bindery Locked; FF Bindery Failure } +end; + + +{F217/3C [2.15c+]} +Function ScanProperty( objName:String; objType:Word; searchPropName:String; + {i/o var:} Var SequenceNumber:LongInt; + { output:} Var propName:String; + Var propFlags:Byte; + Var propSecurity:Byte; + Var propHasValue:Boolean; + Var moreProperties:Boolean ):boolean; +{ Sequence number should be -1 the first time this call is made. + The call can be reiterated (by supplying the returned Seq.#) until + moreProperties=FALSE or nwBindry.result1=NO_SUCH_PROPERTY. + searchPropName may contain wildcards; + If propHasValue=TRUE, the value can be read by calling ReadPropertyValue; + moreProperties=TRUE if more properties exist for this object. } +Type Treq=record + len :word; + subFunc :byte; + _objType :Word; {hi-lo} + _objName :String[48]; + _SeqNbr :LongInt; {hi-lo} + _propName :String[15]; + end; + Trep=record + _propName :array[1..16] of Byte; + _propFlags:Byte; + _propSec :Byte; + _SeqNbr :Longint; {hi-lo} + _propHasValue:Byte; + _moreProp :Byte; + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + len:=SizeOf(Treq)-2; + subFunc:=$3C; + _objType:=swap(objType); { force hi-lo } + PStrCopy(_objName,objName,48); _objName[48]:=#0; UpString(_objName); + _SeqNbr:=Lswap(SequenceNumber); { force hi-lo } + PstrCopy(_propName,searchPropName,15); UpString(_propName); + end; +F2SystemCall($17,sizeof(Treq),sizeof(Trep),result1); +With TPrep(GlobalReplyBuf)^ +do begin + SequenceNumber:=Lswap(_SeqNbr); { force lo-hi } + ZStrCopy(propName,_propName,15); + propFlags:=_propFlags; + propSecurity:=_propSec; + propHasValue:=(_propHasValue>0); + moreProperties:=(_moreProp>0); + end; +ScanProperty:=(result1=0) +{ Completion Codes: + 96 Server Out Of Memory; F1 Invalid Bindery Security; FB No Such property; + FC No Such Object; FE Server Bindery Locked; FF Bindery Failure } +end; + + +{F217/3E [2.15c+]} +Function WritePropertyValue( objName:String; objType:Word; + propName:String; segmentNbr: Byte; propValue:Tproperty; + moreSegments:Boolean ):boolean; +{ Changes the value of a (NON-SET) property associated with a Bindery object.} +Type Treq=record + len :word; + subFunc :byte; + _objType :Word; { hi-lo } + _objName :String[48]; + _segNbr :Byte; + _EraseRemainingSeg:Byte; { FF=true 00=false } + _propName :String[15]; + _propValSeg :Tproperty; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + len:=SizeOf(Treq)-2; + subFunc:=$3E; + _objType:=swap(objType); { force hi-lo } + PStrCopy(_objName,objName,48); _objName[48]:=#0; UpString(_objName); + _segNbr:=segmentNbr; + if moreSegments + then _EraseRemainingSeg:=$00 + else _EraseRemainingSeg:=$FF; + PstrCopy(_propName,propName,15); UpString(_propName); + _propValSeg:=propValue; + end; +F2SystemCall($17,sizeOf(Treq),0,result1); +WritePropertyValue:=(result1=0) +{ Completion Codes: + 96 Server Out Of Memory; E8 Not Item Property; EC no such segment; + F0 Wildcard Not Allowed; F1 Invalid Bindery Security; + F8 No property write privileges; FB No Such property; + FC No Such Object; FE Server Bindery Locked; FF Bindery Failure } +end; + +{F217/3B [2.15c+] } +Function ChangePropertySecurity( objName:String; objType:Word; + propName:String; newPropSecurity:Byte ):boolean; +{ The user must have read and write access to the property to make this call. + The call can't assign a greater security level than the security level of + the caller. } +Type Treq=record + len:word; + subFunc:byte; + _objType:Word; { hi-lo } + _objName:String[48]; + _NewPropSec:Byte; + _PropName:String[15]; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + len:=SizeOf(Treq)-2; + subFunc:=$3B; + _objType:=swap(objType); { force hi-lo } + PstrCopy(_objName,objName,48); _objName[48]:=#0; Upstring(_objName); + _NewPropSec:=NewPropSecurity; + PstrCopy(_propName,propName,15); Upstring(_propName); + end; +F2SystemCall($17,sizeOf(Treq),0,result1); +ChangePropertySecurity:=(result1=0) +{ Completion Codes: + 96 Server Out Of Memory; F0 Wildcard Not Allowed; F1 Invalid Bindery Security; + FB No Such property; FC No Such Object; FE Server Bindery Locked; + FF Bindery Failure } +end; + +{F217/49 [3.0+]} +Function IsStationAManager:boolean; +{ Fast way to detremine if: object ID of caller included in the MANAGERS + set property attached to the SUPERVISOR object. } +Type Treq=record + len:word; + subFunc:byte; + end; + Trep=record + unknown:byte; + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subFunc:=$49; + end; +F2SystemCall($17,SizeOf(Treq),Sizeof(Trep),result1); +{With TPrep(GlobalReplyBuf)^ + do begin + + end; } +IsStationAManager:=(result1=0) +{ Completion codes: + 00 Successful (WS is a manager); + FF Not a manager } +end; + +{F217/4C [3.0+]} +Function GetRelationOfBinderyObject(ObjName:string;ObjType:word; + relationPropertyName:string; + {i/o} Var sequenceNbr:longint; + {out} Var NbrOfObjects:word; + Var Info:TobjIdArray ):boolean; +{ OBJ_SUPERVISORS GROUPS_I'M_IN SECURITY_EQUALS } +Type Treq=record + len :word; + subFunc :byte; + _SeqObjId :Longint; {hi-lo} + _ObjType :word; {hi-lo} + _ObjAndPropName:string; + end; + Trep=record + _NbrOfObj:Word; + _Info :TobjIdArray; + end; + TPreq=^Treq; + TPrep=^Trep; +Var t:byte; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$4C; + _SeqObjId:=Lswap(SequenceNbr); + _ObjType:=swap(ObjType); + Upstring(ObjName);UpString(RelationPropertyName); + _ObjAndPropName:=ObjName; + move(RelationPropertyName[0], + _ObjAndPropName[ord(ObjName[0])+1], + ord(RelationPropertyName[0])+1); + len:=9+ord(ObjName[0])+ord(RelationPropertyName[0]); + F2SystemCall($17,len+2,Sizeof(Trep),result1); + end; +With TPrep(GlobalReplyBuf)^ + do begin + NbrOfObjects:=swap(_NbrOfObj); + for t:= 1 to NbrOfObjects + do Info[t]:=Lswap(_Info[t]); + if NbrOfObjects=$20 + then SequenceNbr:=Info[$20] + else SequenceNbr:=-1; + end; +if result1<>0 then SequenceNbr:=-1; +GetRelationOfBinderyObject:=(result1=0) +end; + + + +{=======SECONDARY FUNCTIONS===================================================} + + + +Function IsShellLoaded:boolean; +Var mask:byte; + id:LongInt; +begin +{$IFNDEF MSDOS} +FillChar(nwintr.GlobalReplyBuf^,Sizeof(nwintr.TintrBuffer),#$0); +{ Only needed in protected mode, otherwise an invalid value is reported. + Doesn't harm a bit if you use it in other modes, though. } +{$ENDIF} +IsShellLoaded:=(nwBindry.getBinderyAccessLevel(mask,id) and (id<>0)); +end; + + +Function IsUserLoggedOn:boolean; +Var mask:byte; + id:LongInt; + objName:String; + objType:word; +begin +IsUserLoggedOn:=( nwBindry.getBinderyAccessLevel(mask,id) + and (id<>0) + and nwBindry.GetBinderyObjectName(id,objName,objType) + ) +end; + + +Function GetRealUserName(userObjName:string; Var realname:string):boolean; +Var propValue:Tproperty; + moreSeg:Boolean; + w,propFlag:Byte; +begin +If ReadPropertyValue(userObjName,OT_USER,'IDENTIFICATION',1,propValue,moreSeg,propFlag) + then ZstrCopy(RealName,PropValue,128) + else realname:=''; +GetRealUserName:=(result1=0); +end; + +Function GetUserObjectID:LongInt; +Var mask:byte; + id:LongInt; +begin +if getBinderyAccessLevel(mask,id) + then GetUserObjectID:=id + else getUserObjectID:=-1; +{ -1 : look at nwBindry.result1 for error number } +end; + + +Function ExistsUser(userObjName:string):boolean; +Var ObjId:Longint; +begin +ExistsUser:=GetBinderyObjectId(userObjName,OT_USER,ObjId); +end; + +Function IsGroupMember( GroupName,UserObjName:String): Boolean; +begin +IsGroupMember:=IsBinderyObjectInSet(GroupName,OT_USER_GROUP,'GROUP_MEMBERS', + UserObjName,OT_USER); +end; + + + + +Function AddUserToGroup(userName,GroupName:String):boolean; +begin +{ first create the necessary properties. They may already exist. } + +CreateProperty(userName,OT_USER,'GROUPS_I''M_IN', + BF_SET,BS_SUPER_WRITE OR BS_LOGGED_READ); +IF (result1<>$00) and (result1<>$ED) { property already exists } +then begin AddUserToGroup:=false;exit end; { bindery failure / bad username} + +CreateProperty(userName,OT_USER,'SECURITY_EQUALS', + BF_SET,BS_SUPER_WRITE OR BS_LOGGED_READ); +IF (result1<>$00) and (result1<>$ED) { property already exists } +then begin AddUserToGroup:=false;exit end; + +{ The following construction seems a bit overdone, but it is needed to keep + the bindery consistent. A user is either fully added to a group OR + nothing happens, this way we ensure that a user is not 'patially added' + to a group. + If the user already is a member of the group, no error is returned. } +IF AddBinderyObjectToSet(Groupname,OT_USER_GROUP,'GROUP_MEMBERS', + userName,OT_USER) + then begin + IF AddBinderyObjectToSet(userName,OT_USER,'GROUPS_I''M_IN', + GroupName,OT_USER_GROUP) + then begin + IF AddBinderyObjectToSet(userName,OT_USER,'SECURITY_EQUALS', + GroupName,OT_USER_GROUP) + then begin + AddUserToGroup:=true; + exit; + end + else begin { attempt to delete partially setup member } + DeleteBinderyObjectFromSet(Groupname,OT_USER_GROUP,'GROUP_MEMBERS', + userName,OT_USER); + DeleteBinderyObjectFromSet(userName,OT_USER,'GROUPS_I''M_IN', + GroupName,OT_USER_GROUP) + end + end + else begin + DeleteBinderyObjectFromSet(Groupname,OT_USER_GROUP,'GROUP_MEMBERS', + userName,OT_USER); + end; + end; +if result1=$E9 then result1:=$00; { $E9: user already a member of group } +AddUserToGroup:=(result1=0); +{ As all these called functions are in this unit, you can check nwBindry.result1 + for the errorcode. } +{ result1codes: $FC user OR group object doesn't exist. } +end; + + + + +Function DeleteUserFromGroup(userName,GroupName:String):boolean; +begin +{ The following construction seems a bit overdone, but it is needed to keep + the bindery consistent. A user is either totally deleted from a group OR + nothing happens, this way we ensure that a user is not 'patially deleted' + from a group. + If the user was not a member of the group, no error is returned. } +IF DeleteBinderyObjectFromSet(Groupname,OT_USER_GROUP,'GROUP_MEMBERS', + userName,OT_USER) + then begin + IF DeleteBinderyObjectFromSet(userName,OT_USER,'GROUPS_I''M_IN', + GroupName,OT_USER_GROUP) + then begin + IF DeleteBinderyObjectFromSet(userName,OT_USER,'SECURITY_EQUALS', + GroupName,OT_USER_GROUP) + then begin + DeleteUserFromGroup:=True; + exit; + end + else begin { attempt to repair partially deleted member } + AddBinderyObjectToSet(Groupname,OT_USER_GROUP,'GROUP_MEMBERS', + userName,OT_USER); + AddBinderyObjectToSet(userName,OT_USER,'GROUPS_I''M_IN', + GroupName,OT_USER_GROUP) + end + end + else AddBinderyObjectToSet(Groupname,OT_USER_GROUP,'GROUP_MEMBERS', + userName,OT_USER); + end; +if result1=$EA then result1:=0; { $EA: user obj NOT a member of group } +DeleteUserFromGroup:=false; +{ As all these called functions are in this unit, you can check nwBindry.result1 + for the errorcode. } +{ result1codes: $FC user OR group object doesn't exist. } +end; + +Function ExistsFileServer(ServerName:string):Boolean; +{ You must be attached to at least one server before using this function. } +Var ObjId:Longint; +begin +UpString(ServerName); +ExistsFileServer:=GetBinderyObjectId(ServerName,OT_FILE_SERVER,ObjId); +end; + + + +end. { end of unit nwBindry } + diff --git a/SRC/UNITS/NWCONN.DCU b/SRC/UNITS/NWCONN.DCU new file mode 100644 index 0000000000000000000000000000000000000000..69497bf422ab7fe7e46487c57944d3708ae0ef22 GIT binary patch literal 28768 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 literal 0 HcmV?d00001 diff --git a/SRC/UNITS/NWCONN.PAS b/SRC/UNITS/NWCONN.PAS new file mode 100644 index 0000000..be01679 --- /dev/null +++ b/SRC/UNITS/NWCONN.PAS @@ -0,0 +1,1455 @@ +{$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 result1: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, result1 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); + result1:=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); + result1:=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 result1:=$FF + else result1:=$00; + GetPrimaryConnectionID:=(result1=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 result1:=$FF + else result1:=$00; + GetDefaultConnectionID:=(result1=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 result1:=$FF + else result1:=$00; + GetPreferredConnectionID:=(result1=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 result1:=$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; + result1:=$FC { ConnectionID invalid => servername invalid } + end + else result1:=$00; + end; +GetConnectionID:=(result1=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 (result1=$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 result1:=$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 + result1:=$FF; { ConnectionID invalid => servername invalid } + ServerName:=''; + end + else result1:=$00; + end; +GetFileServerName:=(result1=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 + result1:=$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 + result1:=0; + AttachToFileServerWithAddress := True; + exit; + end; + End; + +{ Create an attachment } +With Regs +do begin + AX := $F100; + DL := ConnectionID; + RealModeIntr($21,Regs); + result1 := 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 result1<>$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:=(result1=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); + result1:=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 + result1:=0; + Move(PropValue,ServAddr,SizeOf(TinternetworkAddress)); + end + else begin + result1:=$FC; + AttachToFileServer:=False; + exit; + end; + End; + +if result1=0 + then AttachToFileServerWithAddress(ServerName,ServAddr,ConnectionID); + +AttachToFileServer:=(result1=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 (result1=$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 result1:=$FF + else begin + regs.ax := $F101; + regs.dl := ConnectionID; { 1..MaxServers } + RealModeIntr($21,regs); + result1 := regs.al; + { returncodes: 00 successful; FF Connection Doesn't exist } + end; +DetachFromFileServer:=(result1=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 (result1=$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 result1:=$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; + result1:=$00; + end; +GetConnectionIDTable:=(result1=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# } +result1:=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),result1); +If result1 = 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 result1:=$FD; { no_such_connection } +GetConnectionInformation:=(result1=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,result1); +LoginToFileServer:=(result1=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,result1); + LoginEncrypted:=(result1=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:= (result1=0); +End; + + +{F219 [2.15c+]} +Function Logout:boolean; +{logout from all file servers, remains attached to Server, effective EOJ } +begin + F2SystemCall($19,0,0,result1); + result1:=$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); + result1:=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); + result1:=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; + result1 := 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),result1); +if result1 = 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:=(result1=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); +result1:=$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,result1); +result1:=$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,result1); +EnterLoginArea:=(result1=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),result1); +With TPrep(GlobalReplyBuf)^ +do begin + connections:=_connList; + NumberOfConnections:=_NbrOfConn; + end; +getObjectConnectionNumbers:=(result1=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; +result1:=$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; +result1:=$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 } +result1:=$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 } +result1:=$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 } +result1:=$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 } +result1:=$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); +result1:=$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} +result1:=$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; +result1:=$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:=(result1=$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; + result1:=$00; + end + else result1:=nwBindry.result1; +GetObjectLoginControl:=(result1=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 result1:=$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:=(result1=$FB); {no such property} + result1:=0; + end; +ObjectCanLoginAt:=(result1=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 result1:=$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 result1:=nwBindry.result1; + end; +GetObjectNodeControl:=(result1=0); +{ $EC No more records (no such segment); + $FB No restrictions found (No such property) } +end; + + +end. { end of unit nwConn } + diff --git a/SRC/UNITS/NWFILE.PAS b/SRC/UNITS/NWFILE.PAS new file mode 100644 index 0000000..3f9bb4e --- /dev/null +++ b/SRC/UNITS/NWFILE.PAS @@ -0,0 +1,2999 @@ +{$X+,B-,V-} {essential compiler directives} + +Unit nwFile; + +{ nwFile unit as of 950301 / NwTP 0.6 API. (c) 1993,1995, R. Spronk } + +INTERFACE +{ Primary Functions Interrupt: comments: + +Volume Management (Volume Tables) +--------------------------------- + +* ClearObjectVolRestriction (F216/22) (3) [aka ClearVolumeRestrictions/RemoveObjectDiskRestrictions] +* GetObjectVolRestriction (F216/29) (3) [aka GetObjDiskRestrictions/GetObjectDiskUsageAndRestrictions] +* GetVolumeName (F216/06) +* GetVolumeNameWithHandle (F216/15) [aka GetVolumeInfoWithHandle] +* GetVolumeNumber (F216/05) +* GetVolumeUsage (F216/2C) (3) [aka GetExtendedVolumeInformation] +* IsVolumeRemovable (F212) [aka GetVolumeInfoWithNumber] +* ScanVolForRestrictions (F216/20) (3) +* SetObjectVolRestriction (F216/21) (3) [aka SetVolumeRestrictions/SetObjectVolSpaceLimit + /AddUserDiskspaceRestriction] + +Directory Handles (Directory Handle Table/Drive tables) +------------------------------------------------------- + +* AllocPermanentDirHandle (F216/12) +* AllocTemporaryDirHandle (F216/13) +* DeallocateDirHandle (F216/14) +* DeleteFakeRootDirectory (E906) +* GetDirectoryHandle (E900) +* GetDriveConnectionId (EF02) +* GetDirectoryPath (F216/01) +* GetDriveFlag (EF01) (6) +* GetDriveHandle (EF00) (6) +* GetRelativeDriveDepth (E907) +* GetSearchDriveVector (E901) +* MapFakeRootDirectory (E905) +* SetDirectoryHandle (F216/00) +* SetDriveConnectionId (EF02) +* SetDriveFlag (EF01) +* SetDriveHandle (EF00) +* SetSearchDriveVector (E902) + + Secondary Functions + +* DeleteConnectionsDriveMappings +* DeleteDriveMapping +* GetEnvPath (BA..) +* IsSearchDrive (BA..) +* IsNetworkDrive (4409) +* MapDrive +* MapPermanentDrive +* MapSearchDrive +* SetEnvPath (BA..) + +Entries (directory/file management) +----------------------------------- + +* ChangeDirectory (3B..) (DOS) +* ConvertPathToDirEntryId (F217/F4) +* CreateDirectory (F216/0A) +* DeleteDirectory (F216/0B) +* EraseFiles (F244) +. FileServerFileCopy (F3..) + GetDirectoryInfo (F216/2D) (3) +* GetDirectoryEntry (F216/1F) (3) +. GetExtendedFileAttributes (B600) =F24E ??? +. GetFileAttributes (4300) (DOS) +* GetTrueEntryName (60..) (DOS) +* MapDirentryIdToPath (F217/F3) + MoveEntry (F216/2E) (3) dir and files +* PurgeSalvagableFile (F216/1D) (3) +* RecoverSalvagebleFile (F216/1C) (3) +* RenameDirectory (F216/0F) +* ScanDirectoryInformation (F216/02) +* ScanDirectoryEntry (F216/1E) (3) +* ScanFileInformation (F217/0F) + ScanFilePhysical (F216/28) (3) +* ScanSalvagableFiles (F216/1B) (3) +* SetEntry (F216/25) (3) dir and files +. SetExtendedFileAttributes (B601) =F24F +. SetFileAttributes (F246) [4301] +* SetFileInformation (F217/10) + +* ScanDirRestrictions (F216/23) (3) +* SetDirRestriction (F216/24) (3) + + Secondary functions: + + DeleteFile + GetFileHandle + IsFileShareable + FlagFileShareable + PurgeFiles (by dirHandle,fileMask) + SalvageFiles (by dirHandle,fileMask) + PurgeAllErasedFiles + + +Trustees/Max. Rights Mask +------------------------- + +* DeleteTrustee (F216/2B) (3) +* GetEffectiveRights (F216/2A) (3) +. ModifyMaximumRightsMask (F216/04) +. ScanBinderyObjectTrusteePaths (F217/47) +* ScanEntryForTrustees (F216/26) (3) +* SetTrustee (F216/27) (3) + + +Not Implemented: +---------------- + +- AddTrusteeToDirectory (F216/0D) (10) +- AllocSpecialDirHandle (F216/16) (2) +- DeleteTrusteeFromDirectory (F216/0E) (10) +- FileServerFileCopy (E6..) (8) +- GetEffectiveDirectoryRights (F216/03) (10) +- GetPathFromDirEntryID (F216/1A) (12) +- GetVolumeInformation (F217/E9) (1) +- GetVolumeInfoWithHandle (F216/15) (5) +- GetVolumeInfoWithNumber (F212) (4) [DA..] +- PurgeErasedFiles (F216/10) (8) +- PurgeAllErasedFiles (F217/CE) (8) +- RestoreDirectoryHandle (F216/18) (2) +- RestoreErasedFile (F216/11) (8) +- SaveDirectoryHandle (F216/17) (2) +- ScanDirectoryForTrustees (F216/0C) (9) +- SetDirectoryInformation (F216/19) (11) +- SetFileAttributes (E4..) (7) +- UpdateFileSize (E5..) (7) + + +Notes: (1) GetVolumeInformation. This call is NOT available in all 3.x versions. + (only with Nw 2.1 & 3.1x and CLIB.NLM dated before 11-11-92 ) + This call is not implemented here. Replaced by GetVolumeUsage. + (2) not available in (all versions of) NW 3.x. + (3) NW 3.x (and upwards) only. + (4) Replaced by GetVolumeUsage and IsVolumeRemovable. + (5) Replaced by GetVolumeUsage and GetVolumeNameWithHandle. + (6) Information can also be obtained by calling GetDirectoryHandle. + (DOS) 'Normal' DOS call, extended by NetWare shell. + (7) Not supported by Adv.NW 3.x. Not implemented here. + These are functions using FCB's. If another function with the same + name is listed here, that function performs the same action. + (8) Not supported by Adv.NW 3.x. Not implemented here. + These functions have been replaced with calls marked (3) + (9) Replaced by a newer version: ScanEntryForTrustees. + (10) Replaced by DeleteTrustee, GetEffectiveRights and SetTrustee. + (11) Replaced by SetEntry + (12) Replaced by MapDirEntryIDtoPath + + } + +Uses nwIntr,nwMisc,nwBindry,nwConn; + +Var Result:Word; + +Type TsearchDriveVector=array [1..17] of byte; + + +CONST + DRIVE_UNUSED = $00; + DRIVE_PERMANENT = $01; { Drive permanently assigned to fileserver directory } + DRIVE_TEMPORARY = $02; { Drive temporary assigned to FS dir. Released by EOJ } + DRIVE_NETWORK = $03; { Normal drive mapping } + DRIVE_LOCAL = $80; { Drive is local. ! By ORing with one of the above bits, + it can be reassigned to a FS directory.} + + {Name Space Type constants} + NS_DOS =0; + NS_MAC =1; + NS_NFS =2; + NS_FTAM =3; + NS_HPFS =4; + + { Attributes / Netware directory & file attributes } + A_NORMAL = $00; {file} + A_READ_ONLY = $01; {file} + A_HIDDEN = $02; {file/dir} + A_SYSTEM = $04; {file/dir} + A_EXECUTE_ONLY = $08; {file} + A_DIRECTORY = $10; {file} + A_NEEDS_ARCHIVED = $20; {file} + A_undocumented = $40; + A_SHAREABLE = $80; {file} + + A_LO_SEARCH = $0100; {file} + A_MID_SEARCH = $0200; {file} + A_HI_SEARCH = $0400; {file} + A_RESERVED = $0800; {file/dir} + A_TRANSACTIONAL = $1000; {file} + A_INDEXED = $2000; {file} + A_READ_AUDIT = $4000; {file} + A_WRITE_AUDIT = $8000; {file} + + A_PURGE = $010000; {file/dir} + A_RENAME_INHIBIT = $020000; {file/dir} + A_DELETE_INHIBIT = $040000; {file/dir} + A_COPY_INHIBIT = $080000; {file} + + { Trustee Attributes / directory access rights } + TA_NONE = $00; + TA_READ = $01; {R open/read} + TA_WRITE = $02; {W open/write} + TA_RESERVED = $04; { reserved, set to 0 } + TA_CREATE = $08; {C create files or dirs} + TA_DELETE = $10; {E delete files/dirs} + TA_ACCESS = $20; {A set /delete trustees} + TA_SEARCH = $40; {F directory can be searched/file is visible} + TA_MODIFY = $80; {M modify dir/file attributes} + TA_SUPERVISOR =$100; {S supervisor rights to file or directory } + + { Entry Modify flags / see SetEntry } + + EM_ENTRYNAME = $00000001; + EM_ATTRIBUTES = $00000002; + EM_CREATIONTIME = $0000000C; { date = $04, time = $08 } + EM_OWNERID = $00000010; + EM_ARCHIVETIME = $00000060; { date = $20, time = $40 } + EM_ARCHIVERID = $00000080; + EM_MODIFYTIME = $00000300; { date = $0100, time =$0200 } + EM_MODIFIERID = $00000400; + EM_LASTACCESSTIME = $00000800; { date = $0800 } + EM_RIGHTSMASK = $00001000; + EM_MAXDISKSPACE = $00002000; + +Type TvolUsage=record + totalBlocks, {static info} + freeBlocks, {dynamic} + purgableBlocks, {dynamic} + notYetPurgableBlocks, {dynamic} + totalDirEntries, {static} + availDirEntries, {dynamic} + Flags :LongInt; {dynamic} + SectorsPerBlock :byte; {static/number of 512 byte sectors per block} + volumeName :string[16];{static} + end; + + { used By ScanVolForRestrictions } + TobjVolRestr=array[1..64] of record + objId :LongInt; + MaxAllowedBlocks:LongInt; + end; + + +Type Tentry=record + EntryName :String[16]; + + NSType :byte; {namespace number} + DataForkSize :Longint; { =FileSize when NStype=0 (dos) } + {ResourceForkSize:Longint; (Mac data) =0 when NStype=0 (dos) } + FileSize :Longint; {FileSize=Resource+Data forksize } + + Attributes :Longint; + RightsMask :word; {(4)} + + CreationTime, + ArchiveTime, + ModifyTime, + LastAccessTime, + DeleteTime :TnovTime; {salvagable file only} + + OwnerId, + ArchiverId, + ModifierId, + DeletorId :Longint; {salvagable file only} + + end; + { Note: (4) When used with ScanDirectoryInfo, this field + contains the MaximumRightsMask. + Otherwise, the InheritedRightsMask } + +Type TdirRestrList=array[1..56] of record + Level:Byte; + MaxBlocks, + AvailableBlocks:Longint; + end; + {when MaxBlocks and Availableblocks are set to to $7FFFFFFF, + no restrictions are enforced -at this level-} + +Type TtrusteeInformation=record + NumberOfTrustees:Byte; + TrusteeID :array[1..20] of Longint; + TrusteeRights:array[1..20] of Word; + end; + +{-------------------- Volumes----------------------- } +{F216/05 [2.15c+]} +Function GetVolumeNumber( volumeName:String; Var volumeNumber:Byte ):boolean; +{ Returns the volume number of a given volume name } + +{F216/06 [2.15c+]} +Function GetVolumeName( volumeNumber:Byte; Var volumeName:String ):boolean; +{ Returns the volume name of a give volume number [0..31]. + If the volume is not mounted at the time of this call, a null-string is returned. } + +{F216/2C [2.15c+]} +Function GetVolumeUsage(volumeNumber:byte; Var VolUsage: TvolUsage):boolean; + +{F212 [2.15c+]} +Function IsVolumeRemovable( volumeNumber:Byte; + Var volIsRemoveable:Boolean):boolean; + +{F216/15 [2.15c+]} +Function GetVolumeNameWithHandle( dirHandle:Byte; + Var volumeName:String ):boolean; +{F216/29 [3.x]} +Function GetObjectVolRestriction(VolumeNumber:byte; objId:LongInt; + Var MaxAllowedBlocks,BlocksInUse:LongInt):boolean; + +{F216/21 [3.x]} +Function SetObjectVolRestriction(VolumeNumber:byte; objId, + MaxAllowedBlocks:LongInt):boolean; +{F216/22 [3.x]} +Function ClearObjectVolRestriction(VolumeNumber:byte; objId:LongInt):boolean; + + +{F216/20 [3.x]} +Function ScanVolForRestrictions(VolumeNumber:byte; + {i/o} Var sequenceNbr:LongInt; + {out} Var NbrOfObjects:byte; + Var ResultBuffer:TobjVolRestr):boolean; +{ 1st call: sequenceNbr=0, + after last call: sequenceNbr=0 again. } + +{-------------------- Directory Handles/ Drives -------------} + +{F216/01} +Function GetDirectoryPath(DirHandle:byte; Var PathName:string):boolean; + +{EF00 [2.0/2.1/3.x]} +Function GetDriveHandle( DriveNumber:Byte; Var DirHandle:Byte ):boolean; +{ The call returns a pointer to the shell's Drive Handle Table. (32 bytes) + (Drives A..Z and temporary drives [\]^_' ) + If a drive has been assigned a directory handle on the file server, + the handle can be found in the DHT at the position corresponding with the drive letter.} + +{EF00 [2.0/2.1/3.x]} +Function SetDriveHandle( DriveNumber:Byte; DirHandle:Byte ):boolean; + +{E900 [2.0/2.1/3.x]} +Function GetDirectoryHandle( DriveNumber:Byte; Var dirHandle,status:byte):Boolean; +{ Returns directory handle and status flags for a drive. } +{ Drivenumber = 0..31 (A..Z = 0..25) and temp drives (26..31) } + +{EF01 [2.0/2.1/3.x]} +Function GetDriveFlag( DriveNumber:Byte; Var DriveStatus:Byte ):Boolean; +{ This call returns a pointer to the shell's Drive Flag Table (32 Bytes) + Each entry indicates a drive's status (permanent,temporary,local,unassigned) + For further explanation see the DRIVE_xxx constants.} + +{EF01 [2.0/2.1/3.x]} +Function SetDriveFlag( DriveNumber:Byte; DriveStatus:Byte ):Boolean; + +{F216/14 [2.15c+]} +function DeallocateDirHandle(DirHandle : Byte) : Boolean; +{ This function deletes a directory handle } + + +{EF02 [2.0/2.1/3.x]} +Function GetDriveConnectionID( DriveNumber:Byte; Var connID:Byte):boolean; +{ returns the servernumber (1..8) associated with a drive. } + +{EF02 [2.0/2.1/3.x]} +Function SetDriveConnectionID( DriveNumber:Byte; connID:Byte):boolean; + +{F216/00 [2.15c+]} +Function SetDirectoryHandle( sourceDirHandle:Byte; sourceDirPath:String; + targetDirHandle:Byte ):boolean; +{ make handle 'targetHandle' point to the directory provided by + sourceHandle and/or sourceDirPath. } + +{F216/12 [2.15c+]} +FUNCTION AllocPermanentDirHandle( DriveNumber:Byte; + DirHandle : byte; DirPath : string ; + var NewDirHandle, EffectiveRights: byte ) :boolean; + +{F216/13 [2.15c+]} +function AllocTemporaryDirHandle( DriveNumber:byte; + DirHandle : Byte; DirPath : String; + var NewDirHandle,EffectiveRights : Byte) : Boolean; +{ Allocates a temporary directory handle, deleted automatically by EOJ. } + +{E901} +Function GetSearchDriveVector(Var vector:TsearchDriveVector):boolean; + +{E902 } +Function SetSearchDriveVector(vector:TsearchDriveVector):boolean; + +{E905 (shell 3.01+)} +Function MapFakeRootDirectory(DriveNumber:byte; DirPath:string):boolean; + +{E906 (shell 3.01+)} +Function DeleteFakeRootDirectory(DriveNumber:byte):boolean; + +{E907 (shell 3.01+)} +Function GetRelativeDriveDepth(DriveNumber:byte; Var depth:byte):boolean; + +{BA.. } +Function GetEnvPath(Var EnvPath:string):boolean; + +{BA.. } +Function SetEnvPath(EnvPath:string):boolean; + + +{secondary } +FUNCTION MapDrive(DriveNumber:byte; DirectoryPath:string; + Root, Permanent:boolean):boolean; + +{secondary } +FUNCTION MapPermanentDrive(DriveNumber:byte; DirectoryPath:string; + Root:boolean):boolean; + +{secondary} +Function MapSearchDrive(DriveNumber:byte; DirPath:string; + PathPosition:byte; + Insert,Root,Permanent:Boolean):boolean; + +{secondary} +Function DeleteDriveMapping(DriveNumber:Byte):boolean; + +{secondary} +Function DeleteConnectionsDriveMappings(ConnId:Byte):Boolean; + +{secondary} +Function IsSearchDrive(DriveNumber:byte):boolean; + +{4409 } +Function IsNetworkDrive(driveNumber:Byte):boolean; +{ isNetworkDrive is set to TRUE if the drive is a) a network drive, and + b) a legal drive letter was used. } + + +{------------------------- entries -----------------------------------------} + +{F217/0F [2.15c+]} +Function ScanFileInformation(DirHandle:Byte; FilePath:string; + SearchAttrib:Byte; + {i/o} VAR SequenceNbr:Integer; + {out} VAR fileInfo:Tentry):Boolean; + +{F217/F4 [3.0+]} +Function ConvertPathToDirEntryId(dirHandle:Byte; dirPath:string; + Var VolNbr :byte; + Var dirEntryID:LongInt):boolean; +{ aka ConvertPathToDirEntry / requires console rights } + +{F216/02} +Function ScanDirectoryInformation(dirHandle:byte; searchDirPath:string; + {i/o} Var sequenceNumber:word; + {out:} Var dirInfo:Tentry ):boolean; + +{F216/1F [2.15c+]} +Function GetDirectoryEntry(DirHandle:byte; + Var dirEntry:Tentry):boolean; + +{F216/1E [2.15c+]} +Function ScanDirectoryEntry(DirHandle:Byte; EntryName:string; SearchFlags:Longint; + {i/o} Var EntryId:Longint; + {out} Var Entry:Tentry ):boolean; + +{F217/F3 [3.0+]} +Function MapDirEntryIdToPath(VolNbr:byte;DirEntryId:Longint; NStype:byte; + Var ExtPath:string):boolean; + +{F216/25 [2.15c+] } +Function SetEntry(DirHandle:Byte;EntryId:Longint;SearchFlags:Byte; + ModFlags:Longint; Entry:Tentry ):boolean; + +{F217/10 [2.15c+]} +Function SetFileInformation(DirHandle:Byte; FilePath:string; + SearchAttrib:Byte; + fileInfo:TEntry):boolean; + +{F216/1B [2.15c+]} +Function ScanSalvagableFiles(DirHandle:Byte; + {i/o} Var EntryId:Longint; + {out} Var Entry:Tentry ):boolean; + +{F216/1D [3.0+]} +Function PurgeSalvagableFile(DirHandle:Byte; + EntryId:Longint; FileName:string):boolean; + +{F216/1C [3.0+] } +Function RecoverSalvagableFile(dirHandle:Byte; EntryId:Longint; + OldName,NewName:string):boolean; + +{F244 [2.1x/3.x]} +Function EraseFiles(dirHandle, searchAttrib:Byte; filePath:string ):boolean; + +{60.. (extended DOS call)} +Function GetTrueEntryName(DirPath:string; Var CanonicalPath:string):boolean; + + +{F216/0F [2.0/2.1/3.x]} +Function RenameDirectory( dirHandle:Byte; dirPath, newDirName :String):Boolean; + +{F216/0B [2.15c+]} +Function DeleteDirectory(DirHandle:Byte; DirPath:string):boolean; + +{F216/0A [2.15+]} +Function CreateDirectory(DirHandle:Byte; DirPath:string; MaxRightsMask:byte):boolean; + +{3B.. } +Function ChangeDirectory(DirPath:string):boolean; + + +{F216/24 [3.0+]} +Function SetDirRestriction(DirHandle:Byte; DiskSpaceLimit:Longint):boolean; + +{F216/23 [3.0+]} +Function ScanDirRestrictions(DirHandle:Byte; + Var NumberOfEntries:Byte; + Var RestrInfo:TdirRestrList):boolean; + +{--------------------------- Rights/trustees ---------------------------} + +{F216/27 [3.0+]} +Function SetTrustee(DirHandle:Byte;DirPath:string; + TrusteeObjectID:Longint; + RightsMask:Word ):boolean; + +{F216/2B [3.0+]} +Function DeleteTrustee(DirHandle:Byte;DirPath:String; + TrusteeObjectId:Longint):boolean; + +{F216/2A [3.0+]} +function GetEffectiveRights(DirHandle:Byte;DirPath:String; + var Rights:Word) : Boolean; + +{F216/04 [2.15c+]} +Function ModifyMaximumRightsMask(DirHandle:Byte;DirPath:string; + RevokeRightsMask,GrantRightsMask:Word):boolean; + + +{F217/47 [2.15c+]} +Function ScanBinderyObjectTrusteePaths(TrusteeObjectId:Longint; + VolumeNumber:Byte; + {i/o} Var SequenceNumber:word; + {out} Var AccessMask:Word; + Var Path:string ):boolean; + +{F216/26 [3.0+]} +Function ScanEntryForTrustees(DirHandle:Byte;DirPath:String; + {i/o} Var SequenceNumber:Byte; + {out} Var TrusteeInfo: TtrusteeInformation):boolean; + +IMPLEMENTATION{============================================================} + +{$IFDEF MSDOS} +uses dos; { file handles / 'normal' file attributes } +{$ENDIF} + +Type TintEntry=record { Unit internal Entry type } + { 0} _res1 :Longint; { low word = Dir Id of parent Dir } + { 4} _attrib :Longint; + { 8} _res2 :word; + { 10} _NStype :Byte; + { 11} _name :string[12]; + { 24} _creationTime :Longint; + { 28} _OwnerId :Longint; { hi-lo} + { 32} _ArchiveTime :Longint; + { 36} _ArchiverId :Longint; { hi-lo} + { 40} _modifyTime :Longint; + + { 44} _ModifierId :Longint; { files only } + { 48} _ForkSize :Longint; { files only } + { 52} _res3 :array[1..44] of byte; { Trustee obj IDs and Tr. rights } + { 96} _FileRightsMask:word; { files only } + { 98} _AccessDate :word; { files only } + + {100} _DirRightsMask :word; { directories only } + {102} _res4 :word; {Unique Dir ID, hi-lo} { directories only } + {104} _DeleteTime :Longint; { salvageable files only } + {108} _DeletorID :LongInt; { salvageable files only } + {112} _res5 :array[1..16] of byte; + {128} end; + +Procedure Convert2ExtEntry(Var Ie:TintEntry;Var Oe:Tentry); +begin +FillChar(Oe,Sizeof(Tentry),#$0); +with Ie,Oe + do begin + Attributes:=_Attrib; + NStype:=_NStype; + Entryname:=_name; + DosTime2NovTime(_CreationTime,CreationTime); + OwnerId:=Lswap(_OwnerId); {force lo-hi} + DosTime2NovTime(_ArchiveTime,ArchiveTime); + ArchiverId:=Lswap(_ArchiverID); {force lo-hi} + DosTime2NovTime(_ModifyTime,ModifyTime); + if (_attrib and $10)>0 { is entry a directory ? } + then begin + RightsMask:=_DirRightsMask; + end + else begin + ModifierId:=LSwap(_ModifierId); {force lo-hi} + DataForksize:=_Forksize; + if _NSType=0 + then FileSize:=_ForkSize; + RightsMask:=_FileRightsMask; + DosTime2NovTime(MakeLong(_accessDate,0),LastAccessTime); + DosTime2NovTime(_DeleteTime,DeleteTime); + DeletorId:=Lswap(_DeletorID); {force lo-hi} + end; + end; +end; + +Procedure Convert2IntEntry(Var Oe:TEntry;Var Ie:TIntEntry); +Var TempTime:Longint; +begin +FillChar(Ie,Sizeof(Tentry),#$0); +with Ie,Oe + do begin + _Attrib:=Attributes; + _NStype:=NStype; + _Name:=EntryName; + NovTime2DosTime(CreationTime,_CreationTime); + _OwnerId:=Lswap(OwnerId); {force hi-lo} + NovTime2DosTime(ArchiveTime,_ArchiveTime); + _ArchiverId:=Lswap(ArchiverId); {force hi-lo} + NovTime2DosTime(ModifyTime,_ModifyTime); + if (Attributes and $10)>0 { is entry a directory ? } + then begin + _DirRightsMask:=RightsMask; + end + else begin + _ModifierId:=Lswap(ModifierId); { force hi-lo } + _ForkSize:=DataForkSize; + _FileRightsMask:=RightsMask; + NovTime2DosTime(LastAccessTime,TempTime); + _AccessDate:=HiLong(TempTime); + NovTime2DosTime(DeleteTime,_DeleteTime); + _DeletorID:=Lswap(DeletorId); { force hi-lo } + end; + end; +end; + +Procedure ConvertPathToVolFormat(Var path:string); +{ reformat \\server\vol\path to VOL:PATH + server/vol:path to VOL:PATH } +Var pcolon,pslash:byte; +begin +if (Path[0]>#1) and (Path[1]='\') and (Path[2]='\') + then begin + delete(Path,1,2); + Path:=Path+'\'; + pslash:=pos('\',Path); + if pslash>0 + then begin + delete(Path,1,pslash); { remove servername from path } + pslash:=pos('\',Path); + if pslash>0 + then Path:=copy(Path,1,pslash-1)+':'+copy(Path,pslash+1,255); + end; + while Path[ord(Path[0])]='\' do dec(Path[0]); + end + else begin + pcolon:=pos(':',path); + if (path[0]>#3) and (pcolon>3) + then begin + pslash:=pos('/',path); + if (pslash=0) or (pslash>pcolon) + then pslash:=pos('\',path); + if (pslash>0) and (pslash#16 + then volumeName[0]:=#16; + volName:=volumeName; + if volname[ord(volName[0])]=':' + then dec(volName[0]); + len:=2+ord(volName[0]); + F2SystemCall($16,len+2,SizeOf(Trep),result); + end; +volumenumber:=TPrep(GlobalReplyBuf)^.volNbr; +getVolumeNumber:=(result=0) +{resultcodes: + 00 success; 98h volume doesn't exist } +end; + + +{F216/15 [2.15c+]} +Function GetVolumeNameWithHandle( dirHandle:Byte; + Var volumeName:String ):boolean; +Type Treq=record + len :word; + subFunc :byte; + _dirHandle :Byte; + end; + Trep=record + _sectPerBlock :Word; {hi-lo} + _TotalBlocks :Word; {hi-lo} + _availBlocks :Word; {hi-lo} { Use GetVolumeUsage for the other fields } + _TotalDirSlots:Word; {hi-lo} + _availDirSlots:Word; {hi-lo} + _volName :array[1..16] of byte; + _volRemoveable:Word; {hi-lo} + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subFunc:=$15; + _dirHandle:=dirHandle; + end; +F2SystemCall($16,Sizeof(Treq),SizeOf(Trep),result); +ZStrCopy(volumeName,TPrep(GlobalReplyBuf)^._volName,16); +if volumeName='' + then result:=$9B; { Invalid directory handle } +getVolumeNameWithHandle:=(result=0) +{ resultcodes: 00 success; $9B invalid directory handle } +end; + + +{F212 [2.15c+]} +Function IsVolumeRemovable( volumeNumber:Byte; + Var volIsRemoveable:Boolean):boolean; +{ stripped down version of the GetVolumeInfoWithNumber call } +Type Treq=Byte; + Trep=record + _sectPerBlock :Word; {hi-lo} + _TotalBlocks :Word; {hi-lo} + _availBlocks :Word; {hi-lo} + _TotalDirSlots :Word; {hi-lo} + _availDirSlots :Word; {hi-lo} + _volName :array[1..16] of byte; + _volRemoveable :Word; {hi-lo} + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +TPreq(GlobalReqBuf)^:=volumeNumber; +F2SystemCall($12,SizeOf(Treq),SizeOf(Trep),result); +With TPrep(GlobalReplyBuf)^ + do begin + volIsRemoveable:=(_volRemoveable>0); + if _volName[1]=0 + then result:=$98; + end; +IsVolumeRemovable:=(result=0); +{ resultcodes: 00 success; 98h Invalid volume number / volume not mounted } +end; + +{F216/22 [3.x]} +Function ClearObjectVolRestriction(VolumeNumber:byte; objId:LongInt):boolean; +{ If the objId doesn't exist, no error is returned. } +Type Treq=record + len:word; + subFunc:byte; + _volNbr:byte; + _objId:LongInt; { hi-lo } + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subFunc:=$22; + _volNbr:=VolumeNumber; + _objId:=Lswap(objId); { force hi-lo } + end; +F2SystemCall($16,SizeOf(Treq),0,result); +ClearObjectVolRestriction:=(result=0) +{ $8C No supervisor rights } +end; + +{F216/29 [3.x]} +Function GetObjectVolRestriction(VolumeNumber:byte; objId:LongInt; + Var MaxAllowedBlocks,BlocksInUse:LongInt):boolean; +{ If MaxAllowedBlocks is equal to $40000000 on return, there are no + disk restrictions for the object on this volume. } +{ You need not be logged in to use this call. } +Type Treq=record + len :word; + subFunc:byte; + _volNbr:byte; + _objId :Longint; {hi-lo} + end; + Trep=record + _MaxAllowedBlocks, + _BlocksInUse :Longint; + end; + TPreq=^Treq; + TPrep=^Trep; +Var objName:string; + objType:word; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len :=SizeOf(Treq)-2; + subFunc:=$29; + _volNbr:=VolumeNumber; + _objId :=Lswap(objId); {force hi-lo} + end; +F2SystemCall($16,SizeOf(Treq),SizeOf(Trep),result); +With TPrep(GlobalReplyBuf)^ + do begin + MaxAllowedBlocks:=_MaxAllowedBlocks; + BlocksInUse:=_BlocksInUse; + If BlocksInUse=0 + then if NOT nwBindry.GetBinderyObjectName(objId,objName,objType) + then result:=$FF; + end; +GetObjectVolRestriction:=(result=0) +{resultcodes: 00 success; $FF Invalid objectId } +end; + +{F216/20 [3.x]} +Function ScanVolForRestrictions(VolumeNumber:byte; + {i/o} Var sequenceNbr:LongInt; + {out} Var NbrOfObjects:byte; + Var ResultBuffer:TobjVolRestr):boolean; +{ 1st call: sequenceNbr=0, + // n-th call: sequenceNbr(n):=sequenceNbr(n-1)+NbrOfObjects + // (addition done by function itself) + + after last call: sequenceNbr=0 again. } +Type Treq=record + len:word; + subFunc:byte; + _volNbr:byte; + _seqNbr:LongInt; { lo-hi !} + end; + Trep=record + _NbrOfObjects:byte; + _buff :TobjVolRestr; + end; + TPreq=^Treq; + TPrep=^Trep; +Var t:byte; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subFunc:=$20; + _volNbr:=VolumeNumber; + _seqNbr:=sequenceNbr; + end; +F2SystemCall($16,SizeOf(Treq),SizeOf(Trep),result); +if result=0 + then begin + With TPrep(GlobalReplyBuf)^ + do begin + NbrOfObjects:=_NbrOfObjects; + ResultBuffer:=_buff; + For t:=1 to NbrOfObjects + do ResultBuffer[t].objId:=Lswap(_buff[t].ObjId); + if _NbrOfObjects=0 + then result:=$FF + else sequenceNbr:=sequenceNbr+_NbrOfObjects; + end + end + else NbrOfObjects:=0; +ScanVolForRestrictions:=(result=0) +{ $98 VolumeNumber doesn't exist; + $FF No New restriction data (end of iteration) } +end; + +{F216/21 [3.x]} +Function SetObjectVolRestriction(VolumeNumber:byte; objId,MaxAllowedBlocks:LongInt):boolean; +{ If the objId doesn't exist, no error is returned. } +Type Treq=record + len :word; + subFunc:byte; + _volNbr:byte; + _objId :Longint; {hi-lo} + _maxBlocks:LongInt; {lo-hi !!} + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalreqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subFunc:=$21; + _volNbr:=VolumeNumber; + _objId:=Lswap(objId); { force hi-lo } + _maxBlocks:=MaxAllowedBlocks; + end; +F2SystemCall($16,SizeOf(Treq),0,result); +SetObjectVolRestriction:=(result=0) +{ $8C No supervisor Rights } +end; + + +{--------------Dir handles/ drive mappings----------------------------------} + + +{BA.. } +Function GetEnvPath(Var EnvPath:string):boolean; {#d} +Type Tarr=array[1..2048] of byte; +Var regs:TTregisters; + penv:^Tarr; + i,envSize:word; + state:byte; +begin +regs.ah:=$BA; +RealModeIntr($21,regs); +envSize:=byte(nwPtr(regs.dx-1,3)^) SHL 4; +penv:=nwPtr(regs.dx,0); +i:=1; +state:=0; +while (i0) + do begin + EnvPath:=EnvPath+chr(penv^[i]); + inc(i); + end; +if i>envSize + then begin + result:=$301; + GetEnvPath:=false; + exit; + end; +result:=0; +GetEnvPath:=true; +{ 00 successful + 300 'Path' not found + 301 Path value could not be read } +end; + +{BA.. } +Function SetEnvPath(EnvPath:string):boolean; {#d} +Type Tarr=array[1..2048] of byte; +Var regs:TTregisters; + penv:^Tarr; + i,t,envSize:word; + state:byte; + pbegin,pend:word; + NewPathSize,OldPathSize:byte; + diff:integer; + sVector:TsearchDriveVector; + Vecind,p:byte; + dn:Byte; +begin +Upstring(EnvPath); +If pos('PATH=',envPath)=1 + then delete(EnvPath,1,5); +regs.ah:=$BA; +RealModeIntr($21,regs); +envSize:=word(nwPtr(regs.dx-1,3)^) SHL 4; +penv:=nwPtr(regs.dx,0); + +i:=1; +state:=0; +while (i0) + do inc(i); +if i>envSize + then begin + result:=$301; + SetEnvPath:=false; + exit; + end; +dec(i); +pend:=i; + +{ determine end of 'active' environment / marked by $00 00} +while (ienvSize + then begin + result:=$302; + SetEnvPath:=false; + exit; + end; + +diff:=NewPathSize-OldPathSize; +if diff>0 + then for t:=i downto pend + do penv^[t+diff]:=penv^[t]; +if diff<0 + then for t:=pend to i + do penv^[t+diff]:=penv^[t]; +Move(EnvPath[1],penv^[pbegin],NewPathSize); + +FillChar(Svector,SizeOf(TsearchDriveVector),#$FF); +VecInd:=1; +REPEAT +p:=pos(':',envPath); +if p>0 + then begin + dn:=ord(ord(envPath[p-1])-ord('A')); + p:=pos(';',envPath); + if p=0 + then envPath:='' + else delete(envPath,1,p); + IF IsNetworkDrive(dn) + then begin + Svector[VecInd]:=dn; + inc(VecInd) + end; + end; +UNTIL (p=0) or (VecInd=17); +SetSearchDriveVector(Svector); + +result:=0; +SetEnvPath:=true; +{ 00 successful + 300 'Path' not found + 301 Environment failure + 302 Environment overflow (new path too large) } +end; + + +{F216/01} +Function GetDirectoryPath(DirHandle:byte; Var PathName:string):boolean; +{ path includes volumename } +Type Treq=record + len :word; + subFunc:byte; + _dh :byte; + end; + Trep=record + DirPath:string[255]; + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subFunc:=$01; + _dh:=DirHandle; + end; +F2SystemCall($16,SizeOf(Treq),SizeOf(Trep),result); +PathName:=TPrep(GlobalReplyBuf)^.DirPath; +GetDirectoryPath:=(result=0) +{ 00 Successful 9B Bad directory handle } +end; + +{EF02 [2.0/2.1/3.x]} +Function GetDriveConnectionID( DriveNumber:Byte; Var connID:Byte):boolean; +{ returns the servernumber (1..8) associated with a drive. } +Type pArr=^arr; + arr=array[0..31] of byte; +Var regs:TTregisters; +begin +regs.ax:=$EF02; +RealModeIntr($21,Regs); +If DriveNumber>31 + then result:=$0105 + else begin + connID:=Parr(nwPtr(Regs.es,regs.si))^[DriveNumber]; + Result:=0; + end; +GetDriveConnectionID:=(Result=0); +end; + +{EF02 [2.0/2.1/3.x]} +Function SetDriveConnectionID( DriveNumber:Byte; connID:Byte):boolean; +Type pArr=^arr; + arr=array[0..31] of byte; +Var regs:TTregisters; +begin +regs.ax:=$EF02; +RealModeIntr($21,Regs); +If DriveNumber>31 + then result:=$0105 + else begin + Parr(nwPtr(Regs.es,regs.si))^[DriveNumber]:=connId; + Result:=0; + end; +SetDriveConnectionID:=(Result=0); +end; + + +{EF00 [2.0/2.1/3.x]} +Function GetDriveHandle( DriveNumber:Byte; Var DirHandle:Byte ):boolean; +{ The call returns a pointer to the shell's Drive Handle Table. (32 bytes) + (Drives A..Z and temporary drives [\]^_' ) + If a drive has been assigned a directory handle on the file server, + the handle can be found in the DHT at the position corresponding with the drive letter.} +Type pArr=^arr; + arr=array[0..31] of byte; +Var regs:TTregisters; +begin +regs.ax:=$EF00; +RealModeIntr($21,regs); +if DriveNumber>31 + then result:=$0105 + else begin + DirHandle:=Parr(nwPtr(Regs.Es,Regs.Si))^[DriveNumber]; + Result:=0; + end; +GetDriveHandle:=(Result=0); +end; + +{EF00 [2.0/2.1/3.x]} +Function SetDriveHandle( DriveNumber:Byte; DirHandle:Byte ):boolean; +Type pArr=^arr; + arr=array[0..31] of byte; +Var regs:TTregisters; +begin +regs.ax:=$EF00; +RealModeIntr($21,regs); +if DriveNumber>31 + then result:=$0105 + else begin + Parr(nwPtr(Regs.Es,Regs.Si))^[DriveNumber]:=DirHandle; + Result:=0; + end; +SetDriveHandle:=(Result=0); +end; + +{EF01 [2.0/2.1/3.x]} +Function GetDriveFlag( DriveNumber:Byte; Var DriveStatus:Byte ):Boolean; +{ This call returns a pointer to the shell's Drive Flag Table (32 Bytes) + Each entry indicates a drive's status (permanent,temporary,local,unassigned) + For further explanation see the DRIVE_xxx constants.} +Type pArr=^arr; + arr=array[0..31] of byte; +Var regs:TTregisters; +begin +regs.ax:=$EF01; +RealModeIntr($21,Regs); +If DriveNumber>31 + then result:=$0105 + else begin + DriveStatus:=Parr(nwPtr(Regs.es,regs.si))^[DriveNumber]; + Result:=0; + end; +GetDriveFlag:=(Result=0); +end; + +{EF01 [2.0/2.1/3.x]} +Function SetDriveFlag( DriveNumber:Byte; DriveStatus:Byte ):Boolean; +Type pArr=^arr; + arr=array[0..31] of byte; +Var regs:TTregisters; +begin +regs.ax:=$EF01; +RealModeIntr($21,Regs); +If DriveNumber>31 + then result:=$0105 + else begin + Parr(nwPtr(Regs.es,regs.si))^[DriveNumber]:=DriveStatus; + Result:=0; + end; +SetDriveFlag:=(Result=0); +end; + + +{E900 [2.0/2.1/3.x]} +Function GetDirectoryHandle( DriveNumber:Byte; Var dirHandle,status:byte):Boolean; +{ Returns directory handle and status flags for a drive. } +{ Drivenumber = 0..31 (A..Z = 0..25) and temp drives (26..31) } +{ Status Byte + 7 6 5 4 3 2 1 0 + | | +-Permenant Directory Handle + | +----Temporary Directory Handle + +----------------------Mapped to a local drive } +{ in case of an invalid driveNumber, handle and status will be set to 0 } +Var Regs:TTRegisters; +begin +With Regs +do begin + AX:=$E900; + DX:=DriveNumber; + RealModeIntr($21,Regs); + { AH = Status Flags; + 01 mapped to a permanent dir handle; + 02 mapped to a temporary dir handle; + 80 local drive. } + dirHandle:=AL; + status:=AH; + If dirHandle=0 + then begin status:=0;Result:=$FF end {INVALID_DRIVE_NUMBER} + else Result:=0; + GetDirectoryHandle:=(Result=0) + end; +{ result: $00 success; $FF Invalid Drive Number } +end; + + +{F216/00 [2.15c+]} +Function SetDirectoryHandle( sourceDirHandle:Byte; sourceDirPath:String; + targetDirHandle:Byte ):boolean; +{ make handle 'targetHandle' point to the directory provided by + sourceHandle and/or sourceDirPath. ( "Volume:dir\subdir" ) } +Type Treq=record + len :word; + subFunc :byte; + _TargetDH :Byte; + _SourceDH :Byte; + _SourceDP :String[255] + end; + TPreq=^Treq; +Var p:Byte; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$00; + _TargetDH:=targetDirHandle; + _SourceDH:=SourceDirHandle; + if SourceDirHandle=0 + then ConvertPathToVolFormat(SourceDirPath); + _sourceDP:=sourceDirPath; + UpString(_sourceDP); + len:=4+ord(_SourceDP[0]); + F2SystemCall($16,len+2,0,result); + end; +SetDirectoryHandle:=(result=0) +{ resultcodes: + 00 Success; 98h Volume does not exist; + 9Bh Bad directory handle; 9Ch Invalid Path. } +end; + + +{F216/12 [2.15c+]} +FUNCTION AllocPermanentDirHandle( DriveNumber:Byte; + DirHandle : byte; DirPath : string ; + var NewDirHandle, EffectiveRights: byte ) :boolean; +{ Effective server must be the server involved, i.e. where the dir is stored } +Type Treq=record + len : word; + subf : byte; + _dirHandle : byte; + _driveLetter : char; + _DirectoryPath: String[255]; + end; + Trep=record + _newDirHandle : byte; + _EffectiveRights : byte; { e.r. mask } + end; + TPreq=^Treq; + TPrep=^Trep; +Var p:Byte; +BEGIN +With TPreq(GlobalReqBuf)^ + do begin + subf := $12; + _dirHandle := dirHandle; + _driveLetter := chr(DriveNumber+ord('A')); + if Dirhandle=0 + then ConvertPathToVolFormat(DirPath); + _DirectoryPath:=DirPath; + UpString(_DirectoryPath); + len:=4+ord(_DirectoryPath[0]); + F2SystemCall($16,len+2,sizeof(Trep),result); + end; +if result = 0 + then with TPrep(GlobalReplyBuf)^ + do begin + effectiveRights := _effectiveRights; + newDirHandle := _newDirHandle; + end; +AllocPermanentDirHandle:=(result=0); +{ $00 Successful $98 Volume doen't exist $9C Invalid path } +end; + + + + + +{F216/13 [2.15c+]} +function AllocTemporaryDirHandle( DriveNumber:byte; + DirHandle : Byte; DirPath : String; + var NewDirHandle,EffectiveRights : Byte) : Boolean; +{ Allocates a temporary directory handle, deleted automatically by EOJ. } +{ Effective server must be the server involved, i.e. where the dir is stored } +Type TReq=record + Len : Word; + SubF : Byte; + Handle : Byte; + Letter : Char; + _DirectoryPath : String; + end; + TRep=record + NewH : Byte; + Mask : Byte; + end; + TPreq=^Treq; + TPrep=^Trep; +Var p:Byte; +begin +with TPReq(GlobalReqBuf)^ + do begin + SubF := $13; + Handle := DirHandle; + Letter := chr(DriveNumber+ord('A')); + { Allocating handles requires paths to be in + the VOL:path format.. NOT canonical } + if handle=0 + then ConvertPathToVolFormat(DirPath); + _DirectoryPath:=DirPath; + UpString(_DirectoryPath); + Len:=4+length(_DirectoryPath); + F2SystemCall($16,len+2,SizeOf(Trep),result); + end; +with TPrep(GlobalReplyBuf)^ + do begin + NewDirHandle := NewH; + EffectiveRights := Mask; + end; +AllocTemporaryDirHandle:=(result=0); +{ result: 00 success; 98h Volume doesn't exist; 9Ch Invalid Path } +end; + + +{F216/14 [2.15c+]} +function DeallocateDirHandle(DirHandle : Byte) : Boolean; +{ This function deletes a directory handle } +Type TReq=record + Len : Word; + SubF : Byte; + Handle : Byte; + end; + TPreq=^Treq; +begin +with TPReq(GlobalReqBuf)^ + do begin + Len := 2; + SubF := $14; + Handle:= DirHandle; + end; +F2SystemCall($16,Sizeof(Treq),0,result); +DeallocateDirHandle:=(result=0); +{ result: + 00h - Success; 9Bh - Bad directory handle } +end; + + +{E901 } +Function GetSearchDriveVector(Var vector:TsearchDriveVector):boolean; +Var regs:TTregisters; + tmp1,tmp2:word; +begin +regs.ax:=$E901; +GetGlobalBufferAddress(tmp1,tmp2,regs.ds,regs.dx); +{ DS:DX real-mode address of GlobalReplyBuffer } +RealModeIntr($21,regs); +result:=0; +Move(GlobalReplyBuf^,vector,sizeof(TsearchDriveVector)); +vector[17]:=$FF; +GetSearchDriveVector:=True; +end; + +{E902 } +Function SetSearchDriveVector(vector:TsearchDriveVector):boolean; +Var regs:TTregisters; + tmp1,tmp2:word; +begin +regs.ax:=$E902; +Move(vector,GlobalReqBuf^,sizeof(TsearchDriveVector)); +GetGlobalBufferAddress(regs.ds,regs.dx,tmp1,tmp2); +{ DS:DX real-mode address of GlobalRequestBuffer } +RealModeIntr($21,regs); +result:=0; +SetSearchDriveVector:=True; +end; + +Function IsSearchDrive(DriveNumber:byte):boolean; +Var pth:string; +begin +IsSearchDrive:=(getEnvPath(pth) + and (pos(chr(DriveNumber+ord('A'))+':',pth)>0)); +end; + + +{E905 (shell 3.00+)} +Function MapFakeRootDirectory(DriveNumber:byte; DirPath:string):boolean; +{ Dirpath may include server and volumename } +Var regs:TTregisters; + tmp1,tmp2:word; + PName:string; +begin +with regs + do begin + ax:=$E905; + bl:=driveNumber+1; { FF default, 0=A, 2= B etc. } + GetGlobalBufferAddress(ds,dx,tmp1,tmp2); + { VLM patch for SERVER/VOL: and VOL: type paths } + if (DirPath[0]>#2) and (pos(':',DirPath)>2) + then GetTrueEntryName(DirPath,PName) + else PName:=DirPath; + Pname:=Pname+#0; + move(PName[1],GlobalReqBuf^,ord(PName[1])); + { DS:DX real-mode address of GlobalRequestBuffer holding new path } + RealModeIntr($21,regs); + if (flags and 1 {carry})>0 + then result:=al + else result:=0; + end; +MapFakeRootDirectory:=(result=0); +{ $00 Successful $03 Invalid path $0F Invalid Drive $11 Not same device } +end; + +{E906 (shell 3.00+)} +Function DeleteFakeRootDirectory(DriveNumber:byte):boolean; +Var regs:TTregisters; +begin +with regs + do begin + ax:=$E906; + bl:=DriveNumber+1; + RealModeIntr($21,regs); + result:=0; + end; +DeleteFakeRootDirectory:=(result=0); +end; + +{E907 (shell 3.00+)} +Function GetRelativeDriveDepth(DriveNumber:byte; Var depth:byte):boolean; +Var regs:TTregisters; +begin +with regs + do begin + ax:=$E907; + bl:=DriveNumber+1; + RealModeIntr($21,regs); + depth:=al; + if al<$FF + then result:=0 + else result:=$FF; { no fake root assigned } + end; +GetRelativeDriveDepth:=(result=0); +{ 00 Succesful $FF No fake root assigned } +end; + +{secondary} +Function DeleteDriveMapping(DriveNumber:Byte):boolean; +Var dirHandle,status:byte; + pth:string; + ch:char; + p:byte; + DDepth,Dflag:byte; +begin +{ if searchdrive, remove drive from searchtable and PATH environment string } +IF GetEnvPath(pth) + then begin + if pth[ord(pth[0])]<>';' + then pth:=pth+';'; + p:=pos(chr(DriveNumber+ord('A'))+':',pth); + if p>0 + then begin { it is a searchdrive, remove from path } + Repeat + ch:=pth[p]; + delete(pth,p,1); + UNTIL ch=';'; + SetEnvPath(pth); { also creates a new searchdriveVector } + end; + end; +IF (result=0) and GetDirectoryHandle(DriveNumber,dirHandle,status) + then begin + IF GetRelativeDriveDepth(DriveNumber,DDepth) { is it a fake root ? } + then DeleteFakeRootDirectory(DriveNumber); + GetDriveFlag(DriveNumber,Dflag); + SetDriveFlag(DriveNumber,(Dflag and $F0) or DRIVE_UNUSED); + SetDriveHandle(DriveNumber,0); + SetDriveConnectionId(DriveNumber,0); + DeallocateDirHandle(dirHandle); + end; +DeleteDriveMapping:=(result=0); +end; + + +{secondary } +FUNCTION MapPermanentDrive(DriveNumber:byte; DirectoryPath:string; + Root:boolean):boolean; +var pth : string; + DriveHandle: Byte; +begin +IF GetTrueEntryName(DirectoryPath,pth) + then begin + while pth[ord(pth[0])] IN ['\','.','*','?'] + do dec(pth[0]); + if pth[1]<>'\' + then result:=$104 { attempt to map network drive to local drive } + else begin + If GetDriveHandle(DriveNumber,DriveHandle) and (DriveHandle<>0) + then DeleteDriveMapping(DriveNumber); + + IF MapFakeRootDirectory(DriveNumber,pth) + then begin + if (not root) + then DeleteFakeRootDirectory(DriveNumber); + { does not delete the mapping itself, + only the fake root. } + end; + end; + end + else result:=$101; { direcory not locatable } +MapPermanentDrive:=(result=0); +end; + +{secondary} +FUNCTION MapDrive(DriveNumber:Byte; DirectoryPath:string; + Root, Permanent:boolean):boolean; +var rights : byte; + newHandle : byte; + HandlePth,pth,srvr,vol: string; + OldConnId,VolConnId:byte; + p:byte; + VolNbr:byte; + Dflag:byte; +begin +IF Permanent + then begin + MapDrive:=MapPermanentDrive(DriveNumber,DirectoryPath,Root); + exit; + end; +{ map temporary drive } +IF GetTrueEntryName(DirectoryPath,pth) + then begin + if pth[ord(pth[0])]<>'\' + then pth:=pth+'\'; + if pth[1]<>'\' + then result:=$104 { attempt to map network drive to local drive } + else begin + delete(pth,1,2); + p:=pos('\',pth); + if p=0 then result:=$106; + srvr:=copy(pth,1,p-1); + delete(pth,1,p); + p:=pos('\',pth); + if p=0 then result:=$105; { volume does not exist } + vol:=copy(pth,1,p-1); + delete(pth,1,p); + IF NOT GetConnectionId(srvr,VolConnId) + then result:=$106; { server does not exist } + end; + end + else result:=$101; { direcory not locatable } +if (result=0) + then begin + while pth[ord(pth[0])] IN ['\','.','*','?'] + do dec(pth[0]); + + { rebuild path: Alloc handle requires VOL:path format } + HandlePth:=vol+':\'+pth; + GetPreferredConnectionId(OldConnId); + SetPreferredConnectionId(VolConnId); + + { IF Permanent + then AllocPermanentDirHandle(DriveNumber,0,HandlePth, + newHandle,rights) + else} + AllocTemporaryDirHandle(DriveNumber,0,HandlePth, + newHandle,rights); + if (result=0) + then begin + GetDriveFlag(DriveNumber,Dflag); + {If Permanent + then SetDriveFlag(DriveNumber,(Dflag and $F0) or DRIVE_PERMANENT) + else} + SetDriveFlag(DriveNumber,(Dflag and $F0) or DRIVE_TEMPORARY); + SetDriveHandle(DriveNumber,newHandle); + SetDriveConnectionId(DriveNumber,VolConnId); + IF root + then MapFakeRootDirectory(DriveNumber,'\\'+srvr+'\'+vol+'\'+pth); + end; + SetPreferredConnectionId(OldConnId); + end; +MapDrive:=(result=0); +end; + + + +Function MapSearchDrive(DriveNumber:byte; DirPath:string; + PathPosition:byte; + Insert,Root,Permanent:Boolean):boolean; +Var pth:string; + p,scCount:byte; + ch:char; +begin +IF MapDrive(DriveNumber,DirPath,Root,Permanent) + then begin + GetEnvPath(pth); + if pth[ord(pth[0])]<>';' + then pth:=pth+';'; + scCount:=1;p:=1; + while (scCount=ord(pth[0])); + pth:=copy(pth,1,p-1)+chr(DriveNumber+ord('A')) + +':.;'+copy(pth,p,255); + end + else pth:=pth+chr(DriveNumber+ord('A'))+':.;'; + SetEnvPath(pth); + end; +MapSearchDrive:=(result=0); +end; + +{secondary} +Function DeleteConnectionsDriveMappings(ConnId:Byte):Boolean; +Var t,connId2,res:Byte; +begin +res:=$FF; +for t:=0 to 31 + do if GetDriveConnectionId(t,connId2) and (connId2=connId) + then begin + DeleteDriveMapping(t); + if result=0 + then res:=0; + end; +result:=res; +DeleteConnectionsDriveMappings:=(result=0); +{00 successful FF No mappings affected OR Invalid connectionId } +end; + + +{4409 / implemented as a secondary function } +Function IsNetworkDrive(driveNumber:Byte):boolean; +{ isNetworkDrive is set to TRUE if the drive is a) a network drive, and + b) a legal drive letter was used. } +Var regs:TTRegisters; +begin +With regs +do begin + AX:=$4409; + BL:=DriveNumber+1; + RealModeIntr($21,Regs); + IsNetworkDrive:=(DX and $1000)<>0 + end; +end; + + +{--======================-- Entries --===============================--} + + +{60.. (extended DOS call)} +Function GetTrueEntryName(DirPath:string; Var CanonicalPath:string):boolean; +{ SERVER/VOL:[\]Path -> \\SERVER\VOL\path + VOL:[\]Path -> \\effective_server_name\VOL\path + D:\ -> D:\. + +{ if a volumename is supplied without a servername, the name of the + effective server will be returned. } + +{ Format of returned string: + a) D:\path\file.ext or + b) \\servername\volumename\path\file.ext } + +LABEL skip; + +Var reply :array[1..128] of byte; + regs :TTregisters; + pcolon, + pslash :byte; + srvr, + volname:string[47]; + connId :Byte; +begin +{ ----- Pre processing } +if DirPath[0]>#2 + then begin + if ((DirPath[1]='\') and (DirPath[2]='\')) + then begin + CanonicalPath:=DirPath; + UpString(Canonicalpath); + goto skip + end; + pcolon:=pos(':',DirPath); + if (pcolon=2) and (DirPath[0]=#3) and (DirPath[3]='\') + then DirPath:=DirPath+'.'; + { fix known problem of netware: D:\. instead of D:\ } + if (pcolon=2) and (DirPath[0]=#2) + then DirPath:=DirPath+'.'; + { fix know problem of -among others- OS/2-dos: D:. instead of D: } + end; +pcolon:=pos(':',DirPath); +if pcolon>2 + then begin { format must be VOL:[\]path or SERVER/VOL:[\]Path } + pslash:=pos('/',DirPath); + if pslash=0 + then pslash:=$FF; + if (pslash#0) and (dirPath[1]='\') + then delete(DirPath,1,1); + DirPath:='\\'+srvr+'\'+volname+'\'+DirPath; + end; +if dirPath='' + then dirPath:='\'; +{ ----- actual call } +dirPath:=dirPath+#0; { zero terminate } +WITH regs + do begin + Move(dirPath[1],GlobalReqBuf^,ord(dirPath[0])); + GetGlobalBufferAddress(ds,si,es,di); + { DS:SI real mode pointer to GlobalRequestBuffer holding asciiz path ; + ES:DI real mode pointer to GlbalReplyBuffer } + ah:=$60; + RealModeIntr($21,regs); + Move(GlobalReplyBuf^,reply[1],128); + if (regs.flags and 1 {carry})>0 + then begin + result:=ax; + reply[1]:=0; + end + else result:=0; + end; +ZstrCopy(CanonicalPath,reply[1],128); +{ ----- post-processing -- strip \ and . } +skip: ; +While CanonicalPath[ord(CanonicalPath[0])] in ['\','.'] + do dec(CanonicalPath[0]); +GetTrueEntryName:=(result=0); +{ $00 successful + $02 Invalid component in directory path OR drive letter only + $03 Malformed path OR invalid drive letter } +end; + + + +{3B.. } +Function ChangeDirectory(DirPath:string):boolean; +{ does not change the default drive } +Var regs:TTregisters; + tmp1,tmp2:word; +begin +if DirPath[0]>#63 + then result:=$110 { length of path too long } + else begin + DirPath:=DirPath+#0; + with regs + do begin + ah:=$3b; + Move(DirPath[1],GlobalReqBuf^,ord(DirPath[0])); + GetGlobalBufferAddress(ds,dx,tmp1,tmp2); + { DS:DX real-mode pointer to GlobalRequestBuffer holding DirPath } + RealModeIntr($21,regs); + If (flags and 1 {carry})>0 + then result:=$111 { invalid pathname } + else result:=0; + end; + end; +ChangeDirectory:=(result=0); +end; + +{F216/0A [2.15+]} +Function CreateDirectory(DirHandle:Byte; DirPath:string; MaxRightsMask:byte):boolean; +Type Treq=record + len :word; + subFunc :byte; + _DirHandle:byte; + _MRM :byte; + _dirPath :string[255]; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalreqBuf)^ +do begin + subFunc:=$0A; + _dirHandle:=DirHandle; + _MRM:=MaxRightsMask; + _DirPath:=DirPath; + len:=4+ord(_dirPath[0]); + F2SystemCall($16,len+2,0,result); + end; +CreateDirectory:=(result=0) +{ 00 successful 84 No create privileges 98 Volume doesn't exist + FF directory already exists } +end; + + +{F216/0B [2.15c+]} +Function DeleteDirectory(DirHandle:Byte; DirPath:string):boolean; +Type Treq=record + len :word; + subFunc :byte; + _DirHandle:byte; + unused :byte; + _DirPath :string[255]; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalreqBuf)^ +do begin + subFunc:=$0B; + _DirHandle:=DirHandle; + _DirPath:=DirPath; + unused:=0; + len:=4+ord(_DirPath[0]); + F2SystemCall($16,len+2,0,result); + end; +DeleteDirectory:=(result=0) +{ 00 successful 8A No delete privileges + 98 Volume doesn't exist 9B Bad directory handle + 9C Invalid path 9F Directory in use + A0 Directory not empty } +end; + + +{F217/F4 [3.0+]} +Function ConvertPathToDirEntryId(dirHandle:Byte; dirPath:string; + Var VolNbr :byte; + Var dirEntryID:LongInt):boolean; +{ aka ConvertPathToDirEntry } +Type Treq=record + len :word; + subFunc :byte; + _dirHandle:byte; + _DirPath :string[255]; + end; + Trep=record + _volNbr:Byte; + _EntryId:Longint; {hi-lo} + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + subFunc:=$F4; + _dirHandle:=DirHandle; + _dirPath:=DirPath; + UpString(_DirPath); + If DirHandle=0 + then ConvertPathToVolFormat(_DirPath); + len:=3+ord(_DirPath[0]); + F2SystemCall($17,len+2,SizeOf(Trep),result); + end; +With TPrep(GlobalReplyBuf)^ +do begin + VolNbr :=_volNbr; + dirEntryId:=_EntryId; + end; +ConvertPathToDirEntryId:=(result=0) +{ 00 Successful 9B Bad directory Handle + 9C Invalid Path C6 No console rights } +end; + +{F217/F3 [3.0+]} +Function MapDirEntryIdToPath(VolNbr:byte;DirEntryId:Longint; NStype:byte; + Var ExtPath:string):boolean; +{aka MapDirectoryNumberToPath } +{ Returns full path/ with nameSpace information; + Doesn't return server or volumename. } +Type Treq=record + len :word; + subFunc :byte; + _VolNbr :byte; + _EntryId:longint; {hi-lo} + _NameSp :byte; + end; + Trep=record + _path:array[1..255] of byte; {!! maximum: 512 bytes in path ! } + end; + TPreq=^Treq; + TPrep=^Trep; +Var TempPath:string; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subFunc:=$F3; + _VolNbr:=VolNbr; + _EntryId:=DirEntryId; + _NameSp:=NStype; + end; +F2SystemCall($17,SizeOf(Treq),SizeOf(Trep),result); +if result=0 + then begin + With TPrep(GlobalReplyBuf)^ + do ZstrCopy(TempPath,_path,255); + { TempPath according to the 'new' Novell format; + translate into a 'DOS' style path } + NovPath2DOSPath(TempPath,ExtPath); { dir\subdir (no server or volume name) } + end; +MapDirentryIdtoPath:=(result=0) +{ 00 Successful C6 No console rights FF ? } +end; + + +{F216/02} +Function ScanDirectoryInformation(dirHandle:byte; searchDirPath:string; + {i/o} Var sequenceNumber:word; + {out:} Var dirInfo:Tentry ):boolean; +{ set sequenceNumber to 0 before the first call. + + If wildcards (* or ?) are included in the searchDirPath: + Iterate until a $9C error is returned. + + If you don't include a wildcard in the searchDirPath, only use + this call once. Do not iterate, the same entry will be returned + eternaly. + + } +Type Treq=record + len :word; + subFunc :byte; + _dirHandle :byte; + _subDirNumber:word; {hi-lo} + _dirPath :string[255] + end; + Trep=record + _subDirName :array[1..16] of byte; + _creationDate :word; + _creationTime :word; + _ownerObjId :LongInt; {hi-lo} + _maxRightsMask:word; + _SubDirNbr :word; {hi-lo} + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$02; + _dirHandle:=dirHandle; + _subDirNumber:=swap(sequenceNumber); { force hi-lo} + _dirPath:=searchDirPath; + UpString(_dirPath); + len:=5+ord(searchDirPath[0]); + F2SystemCall($16,len+2,SizeOf(Trep),result); + end; +With TPrep(GlobalReplyBuf)^ + do begin + FillChar(dirInfo,SizeOf(Tentry),0); + ZstrCopy(dirInfo.EntryName,_SubDirName,16); + + DosTime2NovTime(MakeLong(swap(_CreationDate),swap(_CreationTime)), + dirInfo.creationTime); + dirInfo.ownerId:=Lswap(_ownerObjId); + dirInfo.RightsMask:=_maxRightsMask; + sequenceNumber:=swap(_SubDirNbr)+1; + end; +ScanDirectoryInformation:=(result=0) +{resultcodes: $00 success; $98 Volume does not exist; + $9B Bad directory Handle $9C Invalid Path } +end; + + +{F216/0F [2.0/2.1/3.x]} +Function RenameDirectory( dirHandle:Byte; dirPath, newDirName :String):Boolean; +{ The new directory name must be a regular (legal) directory name, + max 14 chars long. + The user must have Parental and Modify rights in the parent directory of + the directory to be renamed. } +Type Treq=record + len :word; + subFunc :byte; + _dirHandle :Byte; + _dirNames :Array[0..255+1+14] of byte; { _dirpath[0] is allowed to be 0 } + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$0F; + _dirHandle:=dirHandle; + Upstring(dirPath); + UpString(newDirName); + Move(DirPath[0],_DirNames[0],ord(DirPath[0])+1); + Move(newDirName[0],_DirNames[1+_DirNames[0]],ord(newDirName[0])+1); + len:=4+ord(dirPath[0])+ord(newDirName[0]); + F2SystemCall($16,len+2,0,result); + end; +RenameDirectory:=(result=0) +{ Possible ResultCodes: + 8B No Rename Privileges; 9B Bad Directory Handle; + 9C Invalid Path; 9E Invalid (new) Dir Name. } +end; + + +{F216/1F [2.15c+]} +Function GetDirectoryEntry(DirHandle:byte; + Var dirEntry:Tentry):boolean; +Type Treq=record + len:word; + subFunc:byte; + _dirHandle:byte; + end; + Trep=record + _Entry :TintEntry; + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subFunc:=$1F; + _dirHandle:=dirHandle; + end; +F2SystemCall($16,SizeOf(Treq),SizeOf(Trep),result); +With TPrep(GlobalReplyBuf)^ + do begin + Convert2ExtEntry(_entry,dirEntry); + end; +GetDirectoryEntry:=(result=0) +{ 00 successful 98 Volume doesn't exist + 9B Bad directory handle 9C Invalid path } +end; + + +{B601 [2.0+] } +function SetExtendedFileAttributes(FilePath:String; Attr:Byte) : Boolean; +{ See GetExtFAttr for meaning of Attr the Attribute + Function result code: + 00h Success; + FFh File not found; + FEh Access denied } +Var Novregs:TTRegisters; + tmp1,tmp2:word; +begin +with NovRegs +do begin + AX := $B601; + if FilePath[0]=#255 + then FilePath[255]:=#0 + else FilePath:=FilePath+#0; + Move(FilePath[1],GlobalReqBuf^,ord(FilePath[0])); + GetGlobalBufferAddress(ds,dx,tmp1,tmp2); + { DS:DX real mode pointer to GlobalRequestBuffer holding FilePath } + CL := Attr; + RealModeIntr($21,NovRegs); + IF (Flags AND 1 {carry})>0 + then Result:=AL + else Result:=$00; + Result := AL + end; +SetExtendedFileAttributes:=(Result=0); +end; + + + +{B600 [2.0+]} +function GetExtendedFileAttributes(FilePath:String; var Attributes:Byte) : Boolean; +{ Meaning of Attributes: + 7 6 5 4 3 2 1 0 + | | | | | | | + | | | | +---+---+------Search mode + | | | +----------------------transactional bit A_TRANSACTIONAL + | | +--------------------------Indexing bit A_INDEXED + | +------------------------------Read Audit bit A_READ_AUDIT + +----------------------------------Write Audit bit A_WRITE_AUDIT + } +Var NovRegs:TTRegisters; + tmp1,tmp2:word; +begin +with NovRegs +do begin + AX := $B600; + if FilePath[0]=#255 + then FilePath[255]:=#0 + else FilePath:=FilePath+#0; { null terminate string } + Move(FilePath[1],GlobalReqBuf^,ord(FilePath[0])); + GetGlobalBufferAddress(ds,dx,tmp1,tmp2); + { DS:DX real mode pointer to GlobalRequestBuffer hloding FilePath } + RealModeIntr($21,NovRegs); + IF (Flags and 1 {carry})>0 + then Result := AL + else Result:=$00; + Attributes := CL; + end; +GetExtendedFileAttributes:=(Result=0); +{ $8C caller lacks privileges + FEh not permitted to search directory + FFh file not found } +end; + + + + +{F3.. [2.x/3.x]} +Function FileServerFileCopy( sourceFileHandle, destFileHandle:word; + sourceFileOffset, destFileOffset:Longint; + numberOfBytesToCopy :Longint; + VAR numberOfBytesCopied :Longint ):boolean; +{Note: both source and destination must be on the same file server +SeeAlso: 3C..,3F..} +Type Treq=record + _sFH,_dFH :word; {lo-hi} {as returned by GetFileHandle.} + _sFoffs,_dfOffs:Longint; {lo-hi} + _NbrOfBytes :Longint; {lo-hi} + end; + TPreq=^Treq; +Var regs:TTRegisters; + tmp1,tmp2:word; +begin +with TPreq(GlobalReqBuf)^ + do begin + _sFH:=sourceFileHandle; + _dFH:=destFileHandle; + _sFoffs:=sourceFileOffset; + _dFoffs:=destFileOffset; + _NbrOfBytes:=numberOfBytesToCopy; + end; +with regs + do begin + AH:=$F3; + GetGlobalBufferAddress(es,di,tmp1,tmp2); + { ES:DI real mode pointer to GlobalRequestBuffer } + RealModeIntr($21,regs); + result:=AL; + end; +numberOfBytesCopied:=MakeLong(regs.cx,regs.dx); { ? swap those regs for correct byte order ? } +FileServerFileCopy:=(Result=0); +end; + +{level-0 function. See GetFileAttributes and SetFileAttributes } +Function DoFileAttributes(subf:byte;FilePath:string;VAR attr:byte):boolean; +Var regs:TTregisters; + tmp1,tmp2:word; +begin +with regs +do begin + AH:=$43; + AL:=subf; + if subf=$01 then CX:=attr; + if filePath[0]=#255 + then filePath[255]:=#0 + else filePath:=filePath+#0; + Move(FilePath[1],GlobalReqBuf^,ord(FilePath[0])); + GetGlobalBufferAddress(ds,dx,tmp1,tmp2); + { DS:DX real mode pointer to GlobalRequestBuffer holding FilePath } + RealModeIntr($21,regs); + IF ((Flags and 1 {Fcarry})<>0) + then result:=AL + else begin + result:=$00; + if subf=$00 then attr:=CX + end; + end; +DoFileAttributes:=(result=$00); +{ resultcodes: 00 success; 01 invalid function; + 03 path not found; 05 access denied. } +end; + +{4300 [1.x/2.x/3.x]} +Function GetFileAttributes(FilePath:string; Var attr:byte):boolean; +{ A_READ_ONLY,A_HIDDEN,A_SYSTEM and A_SHAREABLE only. } +begin +GetFileAttributes:=DoFileAttributes($00,FilePath,attr); +end; + +{4301 [1.x/2.x/3.x]} +Function SetFileAttributes(FilePath:string; attr:byte):boolean; +{ A_READ_ONLY,A_HIDDEN,A_SYSTEM and A_SHAREABLE only. } +Var _attr:byte; +begin +_attr:=attr; +SetFileAttributes:=DoFileAttributes($01,FilePath,_attr); +end; + + + +{F217/0F [2.15c+]} +Function ScanFileInformation(DirHandle:Byte; FilePath:string; + SearchAttrib:Byte; + {i/o} VAR SequenceNbr:Integer; + {out} VAR fileInfo:Tentry):Boolean; +{ To be called Iteratatively; initial value for seqNbr=-1 } +{ wildcards in filename allowed. + Iterate util an error $FF occurs } +Type Treq=record + len :word; + subFunc :byte; + _seqNbr :word; {hi-lo} + _dirHandle :byte; + _searchAttrib:Byte; + _filePath :string; + end; + Trep=record + _seqNbr :word; {hi-lo} + _fileName :array[1..14] of byte; + _Fattr, + _ExtFattr :Byte; + _Fsize :LongInt; {hi-lo} + _Crdate :word; {hi-lo} + _LastAccDate :word; {hi-lo} + _LastUpdDate, + _LastUpdTime :Word; + _ownerObjId :Longint; {hi-lo} + _LastArchDate, + _lastArchTime:Word; + _reserved :array[1..56] of byte; + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + subFunc:=$0F; + _seqNbr:=swap(word(SequenceNbr)); { force hi-lo } + _dirHandle:=dirHandle; + _searchAttrib:=searchAttrib; + _filePath:=FilePath; + len:=6+ord(_filePath[0]); + F2SystemCall($17,len+2,SizeOf(Trep),result); + end; +with TPrep(GlobalReplyBuf)^ +do begin + FillChar(fileInfo,sizeOf(fileInfo),#0); + SequenceNbr:=Integer(swap(_seqNbr)); { force lo-hi } + ZstrCopy(fileInfo.EntryName,_filename,15); + fileInfo.Attributes:=(_ExtFattr SHL 8)+_Fattr; + fileInfo.filesize:=Lswap(_Fsize); { force lo-hi} + fileinfo.OwnerID:=Lswap(_ownerObjID); { force lo-hi} + DosTime2NovTime(MakeLong(swap(_CrDate),0),fileinfo.creationTime); + DosTime2NovTime(MakeLong(swap(_LastAccDate),0),fileinfo.lastAccessTime); + DosTime2NovTime(MakeLong(swap(_LastUpdDate),swap(_LastUpdTime)) + ,fileinfo.ModifyTime); + DosTime2NovTime(MakeLong(swap(_LastArchDate),swap(_lastArchTime)) + ,fileinfo.ArchiveTime); + end; +ScanFileInformation:=(result=0) +{ 89 No search privileges FF No more matching files } +end; + + +{F217/10 [2.15c+]} +Function SetFileInformation(DirHandle:Byte; FilePath:string; + SearchAttrib:Byte; + fileInfo:TEntry):boolean; +Type Treq=record + len :word; + subFunc :byte; + _Fattr, + _ExtFattr :Byte; + reserved1 :LongInt; {hi-lo} + _crDate :word; {hi-lo} + _lastAccDate :word; {hi-lo} + _lastUpdTime :Longint; + _ownerObjId :Longint; {hi-lo} + _lastArchTime:Longint; + reserved2 :array[1..56] of byte; + _dirHandle :Byte; + _searchAttr :byte; + _filePath :string; + end; + TPreq=^Treq; +Var DummyDate:Longint; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + subFunc:=$10; + _Fattr:=Lo(LowLong(fileInfo.Attributes)); + _ExtFattr:=Hi(LowLong(fileinfo.Attributes)); + _ownerObjId:=Lswap(fileinfo.OwnerId); {force hi-lo} + _dirHandle:=DirHandle; + _searchAttr:=SearchAttrib; + _filePath:=FilePath; + If Dirhandle=0 + then ConvertPathToVolFormat(_FilePath); + UpString(_filePath); + NovTime2DosTime(fileinfo.CreationTime,dummyDate); + _crDate:=HiLong(dummyDate); + NovTime2DosTime(fileinfo.LastAccessTime,dummyDate); + _lastAccDate:=HiLong(dummyDate); + NovTime2DosTime(fileinfo.ModifyTime,_lastUpdTime); + NovTime2DosTime(fileinfo.ArchiveTime,_lastArchTime); + len:=82+ord(_filepath[0]); + F2SystemCall($17,len+2,0,result); + end; +SetFileInformation:=(result=0); +{ result codes: 00 Success } +end; + + +{F244 [2.1x/3.x]} +Function EraseFiles(dirHandle, searchAttrib:Byte; filePath:string ):boolean; +{ marks files for deletion / in DOS parlance: delete file, file remains purgable } +Type Treq=record + _dirHandle:Byte; + _Sattr:Byte; + _filePath:string; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ +do begin + _dirHandle:=dirHandle; + _Sattr:=searchAttrib; + _filePath:=filePath; + F2SystemCall($44,3+ord(_filepath[0]),0,result); + end; +EraseFiles:=(result=0); +{ resultcodes: 00 Success; 98h Volume doesn't exist; 9Bh bad directory handle; + 9Ch invalid path; FFh no files found error. } +end; + +{F216/1B [3.0+]} +Function ScanSalvagableFiles(DirHandle:Byte; + {i/o} Var EntryId:Longint; + {out} Var Entry:Tentry ):boolean; +{ Iterate (with entryId set to -1 at first) until an error $FF occurs } +Type Treq=record + len :word; + subFunc :byte; + _DirHandle:Byte; + _EntryId :Longint; {low_word-hi_word & each word lo-hi } + end; + Trep=record + _EntryId :Longint; + _Entry :TintEntry; + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subFunc:=$1B; + _DirHandle:=DirHandle; + _EntryId:=EntryId; + end; +F2SystemCall($16,SizeOf(Treq),Sizeof(Trep),result); +With TPrep(GlobalReplyBuf)^ + do begin + EntryId:=_EntryId; {return next EntryId for iteration} + {low_word-hi_word & each word lo-hi } + Convert2ExtEntry(_Entry,Entry); + end; +ScanSalvagableFiles:=(result=0) +{ 98 Volume does not exist FF No more erased files } +end; + +{F216/1D [3.0+]} +Function PurgeSalvagableFile(DirHandle:Byte; + EntryId:Longint; FileName:string):boolean; +{ either supply an entryId and an empty filename, + or supply an entryId of -1 and a filename. Note that the filename + may not be unique: there may be more than one old deleted versions + of a filename. } +Type Treq=record + len :word; + subFunc :byte; + _DirHandle:Byte; + _EntryId :Longint; {low_word-hi_word & each word lo-hi } + _Name :string[255]; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$1D; + _DirHandle:=DirHandle; + _EntryId:=EntryId; + _Name:=FileName; + UpString(_name); + len:=7+ord(_Name[0]); + F2SystemCall($16,len+2,0,result); + end; +PurgeSalvagableFile:=(result=0) +end; + +{F216/1C [3.0+] } +Function RecoverSalvagableFile(dirHandle:Byte; EntryId:Longint; + OldName,NewName:string):boolean; +{ entryId may be set to -1 + OldName is the name of the file before it was deleted. + NewName is the name to be assigned to the recovered file } +Type Treq=record + len :word; + subFunc :byte; + _DirHandle :Byte; + _EntryId :Longint; {low_word-hi_word & each word lo-hi } + _OldAndNewName:string[255]; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$1C; + _DirHandle:=DirHandle; + _EntryId:=EntryId; + UpString(OldName); + UpString(NewName); + _OldAndNewName:=OldName; + move(NewName[0],_OldAndNewName[ord(oldname[0])+1],ord(NewName[0])+1); + len:=8+ord(oldName[0])+ord(NewName[0]); + F2SystemCall($16,len+2,0,result); + end; +RecoverSalvagableFile:=(result=0) +{ 98 Volume does not exist FF No more erased files } +end; + + +{F216/24 [3.0+]} +Function SetDirRestriction(DirHandle:Byte; DiskSpaceLimit:Longint):boolean; +{ limit expressed in Blocks. set limit to 0 to lift limit. + use a negative number if limit should be equal to 0 } +Type Treq=record + len :word; + subFunc :byte; + _DirHandle:Byte; + _Limit :Longint; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subFunc:=$24; + _DirHandle:=DirHandle; + _Limit:=DiskSpaceLimit; + end; +F2SystemCall($16,SizeOf(Treq),0,result); +SetDirRestriction:=(result=0) +end; + + +{F216/23 [3.0+]} +Function ScanDirRestrictions(DirHandle:Byte; + Var NumberOfEntries:Byte; + Var RestrInfo:TdirRestrList):boolean; +Type Treq=record + len:word; + subFunc:byte; + _DirHandle:Byte; + end; + Trep=record + _Entries:Byte; + _Info:TdirRestrList; + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subFunc:=$23; + _DirHandle:=DirHandle; + end; +F2SystemCall($16,SizeOf(Treq),Sizeof(Trep),result); +With TPrep(GlobalReplyBuf)^ + do begin + NumberOfEntries:=_Entries; + RestrInfo:=_Info; + end; +ScanDirRestrictions:=(result=0) +end; + + +Procedure FixEntryNameFormat(Var s:string); +Var res:string; + p:byte; +begin +res:=''; +for p:=1 to ord(s[0]) + do begin + if s[p]='?' + then res:=res+#$FF+#$BF + else if s[p]='*' + then res:=res+#$FF+'*' + else res:=res+s[p] + end; +s:=res; +end; + + +{F216/1E [2.15c+]} +Function ScanDirectoryEntry(DirHandle:Byte; EntryName:string; SearchFlags:Longint; + {i/o} Var EntryId:Longint; + {out} Var Entry:Tentry ):boolean; +Type Treq=record + len :word; + subFunc :byte; + _DirHandle :Byte; + _SearchFlags:Byte; { standard: $16 for dirs / $06 for files } + _SeqNbr :Longint; { lo-hi , set to -1 initially } + _EntryName :string; + end; + + Trep=record { len = 84h = 132 dec. } + _EntryID :Longint; { lo-hi } + _Entry :TintEntry; + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$1E; + _DirHandle:=DirHandle; + _SearchFlags:=SearchFlags; + _SeqNbr:=EntryId; + _EntryName:=EntryName;UpString(_EntryName); + FixEntryNameFormat(_EntryName); + len:=8+ord(_EntryName[0]); + F2SystemCall($16,len+2,Sizeof(Trep),result); + end; +With TPrep(GlobalReplyBuf)^ + do begin + EntryId:=_EntryId; {return next EntryId for iteration} + Convert2ExtEntry(_Entry,entry); + end; +ScanDirectoryEntry:=(result=0) +end; + +{F216/25 [2.15c+] } +Function SetEntry(DirHandle:Byte;EntryId:Longint;SearchFlags:Byte; + ModFlags:Longint; Entry:Tentry ):boolean; +Type Treq=record + len :word; + subFunc :byte; + _dirHandle:Byte; + _SFlags :Byte; + _EntryId :Longint; {lo-hi} + _ModFlags :Longint; {lo-hi} + _Entry :TintEntry; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subFunc:=$25; + _dirHandle:=DirHandle; + _EntryId:=EntryId; + _ModFlags:=ModFlags; + Convert2IntEntry(Entry,_Entry); + end; +F2SystemCall($16,SizeOf(Treq),0,result); +SetEntry:=(result=0) +end; + +{------------------ Secondary Functions ----------------------------} + +Function IsFileShareable(Path : String):boolean; + +var F: File; + FAttr : Word; + +begin + { Assign(F, Path); + GetFAttr(F, FAttr); + result:=DOSerror; } + IsFileShareable:=(result=0) and ((FAttr and $80)>0) +end; + +function FlagFileShareable(Path : String) : Boolean; +{ when the file could NOT be made shareable, false is returned as the + function result, a doserror# is returned as the result code. } +var F : File; + Attr : Word; + ErrCode : word; + Share : Boolean; +begin +if NOT IsFileShareable(Path) { Share: is it sharable? } + then begin + Assign(F,Path); + {SetFAttr(F,Attr or A_SHAREABLE); OR existing atrib. with SHARE bit } + {Result := DOSError;} + end; +FlagFileShareable := (Result=0); +end; + + +Function GetFileHandle(Var f):word; +begin +{GetFileHandle:=filerec(f).handle;} +end; + +{------===================-- Trustee/Max. Rights masks --=================--} + + +{F216/27 [3.0+]} +Function SetTrustee(DirHandle:Byte;DirPath:string; + TrusteeObjectID:Longint; + RightsMask:Word ):boolean; +Type Treq=record + len :word; + subFunc :byte; + _DirHandle:byte; + _ObjId :Longint; { hi-lo } + _Rights :Word; { lo-hi } + _DirPath :string; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$27; + _DirHandle:=DirHandle; + if DirHandle=0 + then ConvertPathToVolFormat(DirPath); + _DirPath:=DirPath;UpString(_DirPath); + _ObjId:=Lswap(TrusteeObjectId); + _Rights:=RightsMask; + len:=9+ord(_DirPath[0]); + F2SystemCall($16,len+2,0,result); + end; +SetTrustee:=(result=0) +{ Possible resultcodes: 8C No modify privileges; + 98 Volume doesn't exist; 9B Bad directory handle + 9C Invalid path; FC No such bindery object } +end; + + +{F216/2B [3.0+]} +Function DeleteTrustee(DirHandle:Byte;DirPath:String; + TrusteeObjectId:Longint):boolean; +{ If DirHandle equals 0, DirPath should be according to the + VOL:\path format. All other path formats will result in + an resultcode of 98h (No such volume) } +Type Treq=record + len :word; + subFunc :byte; + _DirHandle:byte; + _ObjId :Longint; { hi-lo } + _Unused :Byte; + _DirPath :string; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$2B; + _DirHandle:=DirHandle; + if DirHandle=0 + then ConvertPathToVolFormat(DirPath); + _DirPath:=DirPath;UpString(_DirPath); + _ObjId:=Lswap(TrusteeObjectId); + _Unused:=0; + len:=8+ord(_DirPath[0]); + F2SystemCall($16,len+2,0,result); + end; +DeleteTrustee:=(result=0); +{ Possible resultcodes: 98 Volume doesn't exist + 9B Bad directory handle; 9C Invalid path + FE no such trustee } +end; + + +{F216/2A [3.0+]} +function GetEffectiveRights(DirHandle:Byte;DirPath:String; + var Rights:Word) : Boolean; +{ returns the requesting workstation's effective directory rights } +Type Treq=record + Len : word; + SubF : Byte; + _DirHandle : Byte; + _DirName : String; + end; + TRep=record + _RightsMask : Word; + end; + TPreq=^Treq; + TPrep=^Trep; +begin +with TPreq(GlobalReqBuf)^ + do begin + SubF := $2A; + _DirHandle := DirHandle; + if DirHandle=0 + then ConvertPathToVolFormat(DirPath); + _DirName := DirPath;UpString(_DirName); + Len := 3+ord(DirPath[0]); + F2SystemCall($16,len+2,SizeOf(Trep),result); + end; +with TPrep(GlobalReplyBuf)^ + do Rights:=_RightsMask; +GetEffectiveRights:=(Result=0); +{ return byte + 00h - Success + 98h - Volume Does Not Exist + 9Bh - Bad Directory Handle } +end; + + +{F216/04 [2.15c+]} +Function ModifyMaximumRightsMask(DirHandle:Byte;DirPath:string; + RevokeRightsMask,GrantRightsMask:Word):boolean; +Type Treq=record + len:word; + subFunc:byte; + _DirHandle:Byte; + _GrantRM, + _RevokeRM:Byte; + _DirPath:String; + end; + Trep=record + _EffectiveRightsMask:Byte; + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len:=5+ord(DirPath[0]); + subFunc:=$04; + _DirHandle:=DirHandle; + if DirHandle=0 + then ConvertPathToVolFormat(DirPath); + _GrantRM:=MapV3RightsToV2(GrantRightsMask); + _RevokeRM:=MapV3RightsToV2(RevokeRightsMask); + _DirPath:=DirPath; + F2SystemCall($16,len+2,Sizeof(Trep),result); + end; +{With TPrep(GlobalReplyBuf)^ + do begin + --- nothing is done with the returned value--- + end;} +ModifyMaximumRightsMask:=(result=0) +{ result codes: 8C No modify privileges; 98 Volume dosn't exist; + 9C Invalid path } +end; + + + +{F217/47 [2.15c+]} +Function ScanBinderyObjectTrusteePaths(TrusteeObjectId:Longint; + VolumeNumber:Byte; + {i/o} Var SequenceNumber:word; + {out} Var AccessMask:Word; + Var Path:string ):boolean; +{ You must be supervisor (-equivalent) or the TrusteeObject itself + to use this function. + Initially, sequencenumber should be set to 0. } +Type Treq=record + len :word; + subFunc:byte; + _VolNbr:Byte; + _SeqNbr:word; {hi-lo} + _ObjId :Longint; {hi-lo} + end; + Trep=record + _NextSeqNbr:Word; {hi-lo} + _ObjId :Longint; {hi-lo} + _AccMask :byte; + _Path :string; + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subFunc:=$47; + _VolNbr:=VolumeNumber; + _SeqNbr:=swap(SequenceNumber); + _ObjId:=Lswap(TrusteeObjectId); + end; +F2SystemCall($17,SizeOf(Treq),Sizeof(Trep),result); +With TPrep(GlobalReplyBuf)^ + do begin + SequenceNumber:=Lswap(_NextSeqNbr); + Accessmask:=_AccMask; {MapV2RightsToV3(_accMask);} + Path:=_Path; + end; +ScanBinderyObjectTrusteePaths:=(result=0) +{ resultcodes: + $96 Server out of memory; $F0 Wildcard not allowed; + $F1 Invalid bindery security; $FC No such object; + $FE Server bindery locked; $FF Bindery failure } +end; + +{F216/26 [3.0+]} +Function ScanEntryForTrustees(DirHandle:Byte;DirPath:String; + {i/o} Var SequenceNumber:Byte; + {out} Var TrusteeInfo: TtrusteeInformation):boolean; +{ Set SequenceNumber to 0 initially, + iterate until error $9C (no more trustees) is returned } +{ see GETTR in the XFILE archive for an example } +Type Treq=record + len:word; + subFunc:byte; + _DirHandle:Byte; + _SeqNbr:Byte; + _DirPath:String; + end; + Trep=record + _Info:TtrusteeInformation; + end; + TPreq=^Treq; + TPrep=^Trep; +Var t:Byte; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len:=4+ord(DirPath[0]); + subFunc:=$26; + _DirHandle:=DirHandle; + if DirHandle=0 + then ConvertPathToVolFormat(DirPath); + _SeqNbr:=SequenceNumber; + _DirPath:=DirPath;UpString(_DirPath); + F2SystemCall($16,len+2,Sizeof(Trep),result); + end; +With TPrep(GlobalReplyBuf)^ + do begin + inc(SequenceNumber); + TrusteeInfo.NumberOfTrustees:=_Info.NumberOfTrustees; + for t:=1 to 20 + do begin + TrusteeInfo.TrusteeId[t]:=Lswap(_Info.TrusteeId[t]); + TrusteeInfo.TrusteeRights[t]:=_Info.TrusteeRights[t]; + end; + end; +ScanEntryForTrustees:=(result=0) +{ resultcodes: + $9C No more trustees } +end; + + + + +{F2 [2.15c+] +Function ( ):boolean; +Type Treq=record + len:word; + subFunc:byte; + + end; + Trep=record + + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subFunc:=$ + + end; +F2SystemCall($ ,SizeOf(Treq),Sizeof(Trep),result); +With TPrep(GlobalReplyBuf)^ + do begin + + end; + :=(result=0) +end; } + +end. \ No newline at end of file diff --git a/SRC/UNITS/NWINTR.DCU b/SRC/UNITS/NWINTR.DCU new file mode 100644 index 0000000000000000000000000000000000000000..7e4e1241d096a198ecff1f9dd7f61080098b5bbe GIT binary patch literal 12784 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 4E; 1.20-> 4E} + MultiplexIDstring :array[1..3] of char; { 56 4C 4D 'VLM' } + unknown3 :array[1..4] of byte; { 01 00 80 00 } + + TransientSwitchCount :word; + CallCount :word; + ControlBlockOfs :word; { in same segment as this header } + CurrentVLMID :word; + MemoryType :byte; { 04 = XMS } + ModulesLoaded :byte; + BlockId :word; + TransientBlock :word; + GlobalSegment :word; + AsyncQueue :array[1..3] of record { head, tail, s } + pqofs,pqseg:word; + end; + BusyQueue :array[1..3] of record { head, tail, s } + pqofs,pqseg:word; + end; + ReEntranceLevel :word; + FullMapCount :word; + unknown5 :word; { 00 00 } + end; + + TVLMcontrolBlockEntry=record + Flag :word; + ID :word; + Func :word; + Maps :word; + TimesCalled :word; + unknown1 :word; { SSeg ? } + TransientSeg,GlobalSeg :word; + AddressLow,AddressHi :word; + TsegSize,GSegSize,SSegSize:word; { in 16 byte paragraphs } + VLMname :array[1..9] of char; + { null terminated string } + end; + +Var GlobalReqBuf,GlobalReplyBuf:TPintrBuffer; + + { real-mode only, DPMI: all flags are set to false } + VLM_EXE_loaded :Boolean; + NETX_VLM_loaded:Boolean; { if true, then VLM_EXE_loaded must also be true. } + NETX_EXE_loaded:Boolean; + +Function RealModeIntr(intNo:byte;Var regs:TTregisters):boolean; +Procedure F2SystemCall(subf:byte;req_size,rep_size:word;Var result:word); +Procedure nwMsDos(VAR R:ttregisters); +Function InRealMode:Boolean; + +Function MapRealmodeSegment(RSeg:Word):Word; +Function nwPtr(s,o:word):Pointer; +Procedure GetGlobalBufferAddress(VAR Sreq,Oreq,Srep,Orep:Word); + +{$IFDEF RealMode} +Function GetVLMheader(Var VLMheader:TVLMheader):Boolean; +Function GetVLMControlBlock(Entry:Byte; + Var ControlBlock:TVLMControlBlockEntry):Boolean; + { entry: 0 .. VLMheader.ModulesLoaded } +{$ENDIF} + +IMPLEMENTATION {===========================================================} + +Var GlobalRegisters:TTregisters; { all Modes ! } + + VLMCall:Procedure; + +{$IFDEF RealMode} + +Var VLMtransientSeg:word; + +{ ---------- Real mode procedures ------------------------------------} + +{$F+} + +Var RequesterProc:Procedure(Var regs:Registers); + { VLMCall:Procedure; } + +Procedure VlmSystemCall(Var regs:registers); assembler; +asm +push ds + + { check if VLMCall known. If not, return error $FF in fake AL } +xor ah,ah +mov al,$FF +les di,VLMCall +mov bx,es +cmp bx,$0000 +je @1 + { move fake regs registers to 'real' registers } + { AX, CX, DX, DS, SI, DI, ES only. } +les di,regs +mov ax,es:[di+16] +push ax { push new es } +mov ax,es:[di+12] +push ax { push new di } +mov ds,es:[di+14] +mov ax,es:[di] +mov cx,es:[di+4] +mov dx,es:[di+6] +mov si,es:[di+10] +pop di +pop es + { farr call to VLM handler } +push bp +CALL VLMCall +pop bp +@1: { move 'real' registers to fake regs registers } + +{push es +push di} +les di,regs +mov es:[di],ax +{mov es:[di+4],cx +mov es:[di+6],dx +mov es:[di+10],si +pop ax ax:= 'di' +mov es:[di+12],ax +pop ax ax:= 'es' +mov es:[di+16],ax } + +pop ds +end; + +Procedure VLMcheck; +CONST DOS_MULTIPLEX =$2F; +Var regs:registers; + ccode:byte; + Function getBinderyAccessLevel:boolean; { to be replaced by a non-bindery call } + Type Treq=record + len :word; + subF :byte; + end; + Trep=record + accLeveL:byte; + _objId:longInt; + fill:array[1..20] of byte; + end; + TPreq=^Treq; + TPrep=^Trep; + Var result:word; + BEGIN + With TPreq(GlobalReqBuf)^ + do begin + subF:=$46; + len:=sizeOf(Treq)-2; + end; + F2SystemCall($17,sizeOf(Treq),sizeOf(Trep),result); + GetBinderyAccessLevel:=(result=0); + end; + +Var phdr:^TVLMHeader; + pVLMcbl:^TVLMcontrolBlockEntry; + t:word; + +begin +VLM_EXE_Loaded:=false; +Regs.AX:=$7A20; +Regs.BX:=$0000; +Regs.CX:=$0000; +Intr($2F,Regs); +if regs.AX=$0000 + then begin + { OK. AX=0000. All seems well. But is it really the 2F VLM handler? } + phdr:=ptr(regs.es,$0000); + VLM_EXE_Loaded:=(phdr^.MultiplexIdString[1]='V') + and (phdr^.MultiplexIdString[2]='L') + and (phdr^.MultiplexIdString[3]='M'); + + IF VLM_EXE_Loaded + then begin + NETX_EXE_loaded:=False; + + { Determine whether netx.vlm is loaded } + NETX_VLM_Loaded:=False; + t:=0; + While t$0000 + then begin + p:=ptr(VLMtransientSeg,$0000); + move(p^,VLMheader,SizeOf(TVLMHeader)); + end; +GetVLMHeader:=(VLMtransientSeg<>$0000); +end; + +Function GetVLMControlBlock(Entry:Byte; + Var ControlBlock:TVLMControlBlockEntry):Boolean; + { entry: 0 .. VLMheader.ModulesLoaded } +Var ph:^TVLMHeader; + pcb:^TVLMControlBlockEntry; +begin +if VLMtransientSeg<>$0000 + then begin + ph:=ptr(VLMtransientSeg,$0000); + pcb:=ptr(VLMtransientSeg,ph^.ControlBlockOfs+entry*SizeOf(TVLMControlBlockEntry)); + move(pcb^,ControlBlock,SizeOf(TVLMControlBlockEntry)); + end; +GetVLMControlBlock:=(VLMtransientSeg<>$0000); +end; + +Function nwPtr(s,o:word):Pointer; +begin +nwPtr:=Ptr(s,o); +end; + +Function MapRealmodeSegment(RSeg:Word):Word; +begin +MapRealmodeSegment:=RSeg; +end; + +{$ENDIF} {------------- end of real-mode procedures -------------------} + +{$IFDEF ProtMode} + +Type pRealSegItem=^tRealSegItem; + tRealSegItem=record {structure to store information} + Seg:word; {about allocated selectors} + Sel:Word; + prev,next:pRealSegItem; + end; + {we need to allocate selectors which map real-mode segments.} + {all these selectors are stored in an dynamic list} + {and are cleand up them at then end of the program} + +Var GlobalRealReqSeg, + GlobalRealReplySeg:Word; + SelectorList:pRealSegItem; + +Function RealModeIntr (IntNo:Byte;VAR Regs:ttregisters):Boolean;Assembler; +{Simulate a call to the spectified real mode interrupt. The registers passed + to the real mode code are held in RealModeRegisters. This structure contains + the register content upon termination of the real mode ISR. + Returns False if there was an error.} + + ASM + push di + push es + + mov bh,00 {For DOSX to reset the int controller and A20 line. Windows ingores it.} + mov bl,IntNo {Tell DPMI which interrupt to simulate} + xor cx,cx {0 bytes to copy to real mode stack} + les di,Regs {Get the real mode structure} + mov word ptr es:[di+$c],0 {reserved to 0} + mov word ptr es:[di+$c+2],0 + mov word ptr es:[di+$26],0 {fs to 0} + mov word ptr es:[di+$28],0 {gs to 0} + mov word ptr es:[di+$2e],0 {sp to 0} + mov word ptr es:[di+$30],0 {ss to 0} + + mov ax,$0300 {Function 0300h is simulate real mode interrupt} + int 31h + + jc @Error {The carry flag was set, so there was an error} + mov ax,True {Return no error} + jmp @AllDone + + @Error: + mov ax,False {Return false indicating an error} + + @AllDone: + pop es + pop di + End; + +Procedure F2SystemCall(subf:byte;req_size,rep_size:word;Var result:word); +begin +With GlobalRegisters + do begin + CX := Req_size; + DX := rep_size; + AH := $f2; + AL := subf; + DS := GlobalRealReqSeg; {Use then REAL-MODE segments} + ES := GlobalRealReplySeg; {of the global buffers} + DI := 0; {OFFSET always 0 for} + SI := 0; {'GlobalDosAlloc'ated memory} + if not RealModeIntr($21,GlobalRegisters) + then RUNERROR(217); + {DPMI-ERRORS, maybe we should stop the system with the new Errorcode 217} + Result:=al; + end; +end; + +Procedure nwMsDos(VAR R:ttregisters); +begin +if not RealModeIntr($21,R) + then RUNERROR(217); + {DPMI-ERRORS, maybe we should stop then system with the new Errorcode 217} +end; + +Procedure GetGlobalBufferAddress(VAR Sreq,Oreq,Srep,Orep:Word); +begin +Sreq := GlobalRealReqSeg; {Use the REAL-MODE segments} +Srep := GlobalRealReplySeg; {of the global buffers} +Oreq := 0; {OFFSET always 0 for} +Orep := 0; {'GlobalDosAlloc'ated memory} +end; + +{----- Some low-level functions for DPMI -----------} +TYPE os = record + o, s : Word; + end; {for typecasts} + LDTStr = record {Structure of LDT-Elements} + limit : Word; + base : Word; + data : Array[0..1] of Word; + end; + +Procedure Halt218; {runError 218: low-level DPMI-Errors} +begin +RunError(218); +end; + +{DMPI-Function 0: Allocate LDT Descriptor} +function AllocLDTD(var NEWD : Word) : Word; Assembler; +asm + xor ax,ax + mov cx,1 {only 1 descriptor needed} + int 31h {Call DPMI} + jnc @@ok + Call Halt218 {Error on carry} +@@ok: + les di,NEWD {save descriptor to VAR NEWD} + mov es:[di],ax + xor ax,ax +end; + +{DMPI-Function 1: Free LDT Descriptor} +function FreeLDTD(D : Word) : Word; Assembler; +asm + mov ax,0001h + mov bx,D + int 31h + jc @@Ex {carry: return Error in ax} + xor ax,ax +@@Ex: +end; + +{DMPI-Function 7: Set Segment Base Address} +function SetSBA(S: Word; BA: LongInt) : Word; Assembler; +asm + mov ax,0007h + mov bx,S + mov cx,word ptr BA+2 + mov dx,word ptr BA + int 31h + jc @@Ex {carry: return Error in ax} + xor ax,ax +@@Ex: +end; + +{DMPI-Function 8: Set Segment Limit} +function SetSL(S: Word; L: LongInt) : Word; Assembler; +asm + mov ax,0008h + mov bx,S + mov dx,word ptr L + mov cx,word ptr L+2 + int 31h + jc @@Ex {carry: return Error in ax} + xor ax,ax +@@Ex: +end; + +{DMPI-Function 9: Set Descriptor Access Rights} +function SetDAS(S: Word; R: Word) : Word; Assembler; +asm + mov ax,0009h + mov bx,S + mov cx,R + int 31h + jc @@Ex {carry: return Error in ax} + xor ax,ax +@@Ex: +end; + +{DMPI-Function 11: Get Descriptor} +function GetD(S: Word; var D : LDTStr) : Word; Assembler; +asm + mov ax,000Bh + mov bx,S + les di,D + int 31h + jc @@Ex {carry: return Error in ax} + xor ax,ax +@@Ex: +end; + + +{Set then Length of the Descriptor-Segment} +function SetLimit(Sele: Word; L: LongInt) : Word; +var St,R: Word; + Des : LDTStr; +begin +St:= GetD(Sele, Des); {get the Descriptor-Entry from LDT} +if St <> 0 + then begin + SetLimit:= St; {not in LDT, return Error} + Exit; + end; +with Des + do R := (Data[0] shr 8) or ((Data[1] and $00F0) shl 8); + {form then rights for the DPMI-9-Call, register cl} +if L > $FFFFF + then begin {> 1MB: Page aligned} + if L and $FFF <> $FFF + then begin {Limit=Length-1!} + SetLimit := $8021; {return Error: not page aligned} + Exit; + end; + R:= R or $8000; {set Page granularity} + end + else R:= R and $7FFF; {set Byte granularity} +St := SetSL(Sele, 0); {fist set limit to 0} +if St = 0 then St := SetDAS(Sele, R); {ok, set the new rights} +if St = 0 then St:= SetSL(Sele, L); {ok, set then limit} +SetLimit := St; {return errorcode} +end; + + +{get a Selector for a part of then real-mode memory} +function RealMemSel(RealP : Pointer; Limit : LongInt; var Sele : Word) : Word; + function NP(P : Pointer) : LongInt; + VAR TC:OS absolute P; + begin + NP := (LongInt(TC.S) shl 4)+LongInt(TC.O); + end; +var St : Word; +begin +St := AllocLDTD(Sele); {get a new Selector} +if St = 0 + then begin + St := SetSBA(Sele, NP(RealP)); {set base addresse to the linear} + if St = 0 + then begin {address of the Real-Segment} + St := SetLimit(Sele, Limit); {set the selector-limit} + if St <> 0 + then if FreeLDTD(Sele)<>0 then; {on error: free selector} + end + else if FreeLDTD(Sele)<>0 then; {on error: free selector} + end; +RealMemSel := St; {return errorcode} +end; + +{check if the required selector is already allocated} +Function InSelectorList(S:Word):pRealSegItem; +VAR li:pRealSegItem; +begin +li:=SelectorList; +while li<>NIL + do begin + if li^.Seg=S + then begin + InSelectorList:=Li; + exit; + end; + li:=li^.Next; + end; +InSelectorList:=NIL; +end; + +{insert a new SelectorItem at start of the list} +Procedure AddToSelectorlist(Segment,Selector:Word); +VAR li:pRealSegItem; +begin +new(li); +with li^ + do begin + Seg:=segment; + Sel:=Selector; + next:=Selectorlist; + prev:=NIL; + end; +Selectorlist^.prev:=li; +Selectorlist:=li; +end; + +{clean up} +Procedure FreeSelectorList; +VAR li:pRealSegItem; +begin +while Selectorlist<>NIL + do begin + li:=selectorlist; + selectorlist:=li^.next; + if li^.sel<>0 + then FreeLDTD(li^.Sel); + dispose(li); + end; +end; + +Function MapRealmodeSegment(RSeg:Word):Word; +VAR sel:Word; + li:pRealSegItem; +begin +li:=InSelectorList(RSeg); +if li=NIL + then begin + if RealMemSel(Ptr(RSeg,0),$ffff,Sel)<>0 + then RUNERROR(217); {something's wrong: Errorcode 217} + MapRealModeSegment:=Sel; + AddToSelectorList(Rseg,Sel); + end + else MapRealModeSegment:=li^.Sel; +end; + + +Function nwPtr(s,o:word):Pointer; +begin + nwPtr:=Ptr(MapRealModeSegment(s),o); +end; + + +{$ENDIF} {----------------- end of protected mode procedures -------------} + +Var OldExitProc:pointer; + +Function InRealMode:Boolean; +begin +{$IFDEF Windows} +InRealMode:=(GetWinFlags and wf_PMode)=0; +{$ELSE} + {$IFDEF ProtMode} + InRealMode:=False; + {$ELSE} + InRealMode:=True; + {$ENDIF} +{$ENDIF} +end; + + +{$F+} +Procedure IntrExit; +begin +ExitProc:=OldExitProc; +{$IFDEF ProtMode} +if GlobalDosFree(Seg(GlobalReqBuf^))<>0 then; {ignore Errors} +if GlobalDosFree(Seg(GlobalReplyBuf^))<>0 then; +FreeSelectorList; +{$ELSE} {RealMode} +FreeMem(GlobalReqBuf,SizeOf(TintrBuffer)); +Freemem(GlobalReplyBuf,Sizeof(TintrBuffer)); +{$ENDIF} +end; +{$F-} + + +{$IFDEF ProtMode} +VAR w1:Longint absolute GlobalRegisters; +{ we only need w1 during the initialisation, so we use the static + var GlobalRegisters to save 4 bytes of memory :-) } +{$ENDIF} + +begin +VLM_EXE_Loaded:=false; +NETX_EXE_loaded:=false; +NETX_VLM_loaded:=false; +{$IFDEF ProtMode} +new(SelectorList); +fillchar(Selectorlist^,Sizeof(Selectorlist^),0); +w1:=GlobalDosAlloc(Sizeof(tIntrBuffer)); {alloc REQ-Buffer} +if w1=0 + then runerror(217); {DPMI-ERROR, no free Memory} +GlobalReqBuf:=Ptr(loWord(w1),0); {buffer-address for protected Mode} +GlobalRealReqSeg:=hiWord(w1); {REAL-Mode-Segment of the buffer-address} +w1:=GlobalDosAlloc(Sizeof(tIntrBuffer)); {alloc REPLY-Buffer} +if w1=0 + then runerror(217); +GlobalReplyBuf:=Ptr(loWord(w1),0); +GlobalRealReplySeg:=hiWord(w1); +{$else} {RealMode} +new(GlobalReqBuf); +if GlobalReqBuf=NIL + then RunError(203); {where has all the memory gone?? /Heap-Overflow} +new(GlobalReplyBuf); +if GlobalReplyBuf=NIL + then RunError(203); +VLMtransientSeg:=$0000; +VLMcheck; +{$endif} +OldExitProc:=ExitProc; +ExitProc:=@IntrExit; +end. + + diff --git a/SRC/UNITS/NWIPX.PAS b/SRC/UNITS/NWIPX.PAS new file mode 100644 index 0000000..8482ee5 --- /dev/null +++ b/SRC/UNITS/NWIPX.PAS @@ -0,0 +1,606 @@ +{$B-,V-,X+} +UNIT nwIPX; + +{$DEFINE ProtMode} +{$IFDEF MSDOS} {$UNDEF ProtMode} {$DEFINE RealMode} {$ENDIF} +{$IFDEF ProtMode} sorry, protected mode not supported (yet) {$ENDIF} + +{ nwIPX unit as of 950301 / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +INTERFACE + +Uses Dos,nwMisc; + +{ Primary IPX calls: Subf: Comments: + + IPXCancelEvent 6 AES +* IPXCloseSocket 1 (1) + IPXDisconnectFromTarget B (1) +* IPXGetInterNetworkaddress 9 +* IPXGetIntervalMarker 8 +* IPXGetLocalTarget 2 +- IPXGetPacketSize D (IPX internal use only) +* IPXInitialize INT 2F +- IPXInitializeNetworkAddress C (IPX internal use only) +* IPXListenForPacket 4 +* IPXOpenSocket 0 +* IPXRelinquishControl A +* IPXScheduleIPXEvent 5 AES + IPXScheduleSpecialEvent 7 +* IPXSendPacket 3 +- IPXTerminateSockets E (IPX internal use only) + + Secondary calls: + +* IPXpresent +* IPXsetupSendECB +* IPXsetupListenECB + + Notes: (1) These functions use INT 21 and are not to be called from + within an ESR. +} + +CONST + LONG_LIVED_SOCKET = TRUE; { IPXopenSocket } + SHORT_LIVED_SOCKET = FALSE; + + {*** PACKET TYPES ***} + + UNKNOWN_PACKET_TYPE =0; { (basic) Unknown IPX packet } + IPX_PACKET_TYPE =0; + RIP_PACKET_TYPE =1; { Routing Information Packet } + ECHO_PACKET_TYPE =2; + ERROR_PACKET_TYPE =3; + PEP_PACKET_TYPE =4; { Packet Exchange Protocol } + SPX_PACKET_TYPE =5; { Sequenced Packet Protocol Packet } + PUP_PACKET_TYPE =12; + DOD_IP_PACKET_TYPE =13; { Internet Protocol packet Type } + NCP_PACKET_TYPE =17; { NetWare Core Protocol } + { Experimental packet types: 20 - 37 } + + {*** SOCKET NUMBERS ***} + + {0001-0BB8 Registered with Xerox } + SKT_XEROX_ROUTING_INFORMATION= $0001; + SKT_ECHO_PROTOCOL = $0002; + SKT_ERROR_HANDLER = $0003; + + {0020-003F Xerox : Experimental } + SKT_NW4_TIME_SYNC_SERVER = $0040; { used by OT_NW4_TIME_SYNC_SERVER } + SKT_FILE_SERVICE = $0451; { see also $8140, used by OT_RSPCX_SERVER } + SKT_SERVICE_ADVERTISING = $0452; { SAP } + SKT_ROUTING_INFORMATION = $0453; { Novell's RIP Socket } + SKT_NETBIOS = $0455; + SKT_DIAGNOSTIC = $0456; + { 0457h ??? (appears to be related to server serial numbers) } + + {0BB9-FFFF Xerox : Dynamically assignable Sockets } + {0BB9-3FFF Novell: } + SKT_NMA_AGENT =$2F90; { used by OT_NMA_AGENT (NMS) } + + {4000-7FFF Novell: Dynamically assignable Sockets } + { Use a socket in this range for your own applications. } + { To avoid conflicts with other programs, you are advised NOT } + { to use sockets numbers where the hi-byte equals the low-byte, } + { C programmers mostly use those to avoid byte-order swapping. } + + { ! See the SKT_XXX file in the XIPX archive for the latest info + on socket numbers... } + + {8000-FFFF Novell: Well known sockets, registered with Novell. } + SKT_EMAIL_CHAT =$8055; { Niche Corp. } + SKT_EMAIL_CHAT_2 =$8056; { Niche Corp. } + SKT_BTRIEVE =$8058; + SKT_BTRIEVE_2 =$8059; + SKT_NW_SQL =$805A; + SKT_NW_SQL_2 =$805B; + SKT_GAMESERVER =$805C; + SKT_GAMESERVER_2 =$805D; + SKT_PRINT_SERVER =$8060; + SKT_DIGITAL_CHAT =$806C; { Digital Inc. } + SKT_NW_ACCESS_SERVER =$806F; + SKT_OXXI_EMAIL_CHAT =$80C3; { Oxxi Inc. } + SKT_PRINT_SERVER_2 =$811E; + SKT_INTEL_EMAIL_CHAT =$845F; { Intel Corp. } + SKT_WINDOWS_EMAIL_CHAT =$9017; + SKT_JOB_SERVER =$9022; + +Var Result:word; { unit errorcode variable } + +Type TipxHeader=Record + checksum :word; { not used, set to $FFFF } + length :word; { total number of bytes } + TransportControl :byte; { used by bridges: low 4 bits= hop count } + packetType :byte; { ignored by IPX, used by higher level + protocols only. $00=unknown packet type} + destination, + source :TinternetWorkAddress; + { if dest.network equals 0; dest + assumed on same network as sender } + { if dest.node =$FFFFFFFFFFFF, packet + will be sent to all nodes. } + end; + { Fields within IPX and SPX are high-low. Byte swapping will be done + by the IPX functions, except network and node addresses. } + + Tfragment=record { address and size of buffer fragment. } + Address:Pointer; + Size:word; + end; + + Tecb=record + Linkaddress :Pointer; { used by IPX itself } + ESRaddress :Pointer; + InUseFlag :Byte; { reset to $00 when request completed } + CompletionCode :Byte; { valid after InUseFlag becomes $00; + completionCode=$00: packet sent/received. } + SocketNumber :word; + IPXworkspace :array[1..4] of byte; + DriverWorkspace :array[1..12] of byte; + Immediateaddress:Tnodeaddress; { 6 bytes } + FragmentCount :word; { must be >0 } + Fragment :array[1..2] of Tfragment; { [1..FragmentCount] } + { The number of fragments is unlimited. + However, most applications use 1 or 2. } + end; + Tpecb=^Tecb; + +{ TAESecb=: +Offset Size Description + 00h DWORD Link + 04h DWORD ESR address + 08h BYTE in use flag (see below) + 09h 5 BYTEs AES workspace } + +Function IpxPresent:boolean; +{ Determines if an IPX driver is loaded. Calls IPXInitialize. } + +Function IPXinitialize:Boolean; +{ Determines if an IPX driver is loaded. } + +{IPX/SPX: 09h} +Function IPXGetInternetworkAddress(Var Address:TinterNetworkAddress):boolean; +{ This call returns the network and node address of the requesting workstation. } +{ The two byte socketnumber must be appended to the end to form a full } +{ 12-Byte network address. The socketnumber will be set to 0000, indicating + that it has to be filled later with a meaningfule number. } + +{IPX/SPX: 00h} +Function IPXOpenSocket(Var socket:word; PermanentSocket:boolean):boolean; +{ When an application wants to send or receive packets on a socket, + it should first open the socket. PermanentSocket should be set to TRUE + if the socket is used by a TSR. This way, the socket will only be + closed when the IPXcloseSocket function is called. Otherwise, set to FALSE. } + +{IPX/SPX: 01h} +Function IPXCloseSocket(socket:word):boolean; +{ Closes the socket. TSRs should close permanent sockets before terminating. } + +{IPX/SPX: 02h} +Function IPXGetLocalTarget(Address:TinternetworkAddress; + Var ImmAddr:TnodeAddress; + Var Ticks:word ):boolean; +{ Returns the nodeaddress (Immediate address) of a bridge/router that + connects the senders' network with the target-network. If the target + lies within the same network as the sender, the returned node address + is the same as the target node-address. } + +{IPX/SPX: 03h} +Function IPXSendPacket(Var Ecb:Tecb):boolean; +{ After calling this function, control is immediately turned back to the + calling process, whilst in the background the IPX driver is trying to + send the packet. To check if the message has been sent, check the + ECB.InUseFlag or use a SendESR. + The ecb must be filled with appropriate values before calling this function, + the socket to send on must be open. } + +{IPX/SPX: 0Fh} +Function IPXInternalSendPacket(Var Ecb:Tecb):boolean; + +{IPX/SPX: 04h} +Function IPXListenForPacket(Var Ecb:Tecb):Boolean; +{ After calling this function, control is immediately turned back to the + calling process. The IPX driver will wait in the background for a packet + to be received. To check if a message has been received, check the + ECB.InUseFlag or use a ListenESR. + The ecb must be filled with appropriate values before calling this function, + the socket to receive on must be open. } + +{IPX/SPX: 0Ah} +Function IPXrelinquishControl:boolean; +{ Temporarily gives away CPU time to bakcground processes. This call + improves efficeincy by informing the IPX driver that the CPU is + available. } + +{IPX/SPX: 08h} +Function IPXgetIntervalMarker(Var ticks:word):boolean; +{ Gets a time marker from IPX. The difference between two known + time-markers can be used to determine if a timeout has occurred. + 1 Tick = 1/18.2 second. } + +{IPX/SPX: 06h} +Function IPXcancelEvent(ECB:Tecb):boolean; +{ AES call: Cancel an event. + When the event is canceled, the ECB.InUseFlag will be set to $00 and the + ECB.CompletionCode to $FC: Event Canceled. } + +{IPX/SPX: 0Bh} +Function IPXdisconnectFromTarget(Address:TinternetworkAddress):boolean; +{ Informs the listening socket at the specified adress that no more + packets will be sent to the listening socket. + This function is not required in your application, it is merely used + to inform some drivers that the connection (if any) has ended. } + +{IPX/SPX: 05h} +Function IPXscheduleIPXevent(ticks:word;Var ECB:Tecb):boolean; +{ AES call: schedule an event. + After calling this function, control is immediately turned back to the + calling process. After waiting the number of ticks specified + (1 tick= 1/18.2 sec.), the IPX driver activates the ECB. + This function should never be called with an ECB that is still in use + by the IPXdriver (i.e. ECB.InUseFlag should be 0 before calling) +} + +{UPX: 0007} +Function IPXscheduleSpecialEvent(ticks:word;Var ECB:Tecb):boolean; + +Procedure IpxSpxSystemCall(Var regs:registers); +{ Provides an entry into the INT A7 interrupt handler; + Valid only if IPXinitialize or IPXinstalled were called previously. } + +{************** Secondary Procedures ***************************************} + +Procedure IPXSetupListenECB(ESRptr:Pointer; ReceiveSocket:word; + BufPtr:Pointer; BufSize:word; + {out:} Var IpxHdr:TipxHeader; Var ecb:Tecb); +{ Clears IPXheader and ECB, sets values of the required fields within + the ecb and IPX header. } + +Procedure IPXsetupSendECB(ESRptr:pointer; SourceSocket:Word; + DestAddr:TinterNetworkAddress; + BufPtr:pointer; BufSize:word; + {out:} Var IpxHdr:TipxHeader; Var ecb:Tecb); +{ Clears IPXheader and ECB, sets values of the required fields within + the ecb and IPX header. } + +IMPLEMENTATION {==============================================================} + +CONST + IPX_MAX_DATA_LENGTH =546; + +Var IpxSpxCall:Procedure; + +Procedure IpxSpxSystemCall(Var regs:registers); assembler; +{ This method of calling IPX/SPX is preferred by Novell. } +{ For what its' worth: this call is 48 bytes longer than the other one.. } +asm + { check if IpxSpxCall known. If not, return error $FF in fake AL } +xor ah,ah +mov al,$FF +les di,IpxSpxCall +mov bx,es +cmp bx,$0000 +je @1 + { move fake regs registers to 'real' registers } + { AX, BX, CX, DX, SI, DI, ES only. } +les di,regs +mov ax,es:[di+16] +push ax { push new es } +mov ax,es:[di+12] +push ax { push new di } +mov ax,es:[di] +mov bx,es:[di+2] +mov cx,es:[di+4] +mov dx,es:[di+6] +mov si,es:[di+10] +pop di +pop es + { farr call to A7 interrup handler } +push bp +CALL IpxSpxCall +pop bp +@1: { move 'real' registers to fake regs registers } +push es +push di +les di,regs +mov es:[di],ax +mov es:[di+2],bx +mov es:[di+4],cx +mov es:[di+6],dx +mov es:[di+10],si +pop ax { ax:= 'di' } +mov es:[di+12],ax +pop ax { ax:= 'es' } +mov es:[di+16],ax +end; + +Function IPXinitialize:Boolean; +CONST DOS_MULTIPLEX =$2F; +Var regs:registers; +begin +Regs.AX:=$7A00; +INTR(DOS_MULTIPLEX,Regs); +if regs.AL<>$FF + then begin + Result:=IPX_NOT_INSTALLED; + IpxInitialize:=false + end + else begin + @IpxSpxCall:=Ptr(Regs.es,Regs.di); + Result:=0; + IpxInitialize:=true; + end; +end; + +Function IpxPresent:boolean; +begin +IpxPresent:=IpxInitialize +end; + +{IPX: 09h} +Function IPXGetInternetworkAddress(Var Address:TinterNetworkAddress):boolean; +{ This call returns the network and node address of the requesting workstation. } +{ The two byte socketnumber must be appended to the end to form a full } +{ 12-Byte network address. } +Var regs:registers; +begin +regs.bx:=$0009; +regs.es:=seg(Address); +regs.si:=ofs(Address); +IpxSpxSystemCall(Regs); +result:=regs.al; +address.socket:=$0000; { unknown, to be set later. } +if result<>$FF + then result:=$00; +IPXGetInternetworkAddress:=(result=$00); +{ possible resultcodes: $00 Successful; $FF IPX not initialized } +end; + +{IPX: 00} +Function IPXOpenSocket(Var socket:word; permanentSocket:boolean):boolean; +Var regs:registers; + reqForSocket:boolean; +begin +regs.bx:=$0000; +if permanentSocket + then regs.al:=$FF + else regs.al:=$00; +regs.dx:=swap(socket); {hi-lo} +reqForSocket:=(socket=$0000); + +IpxSpxSystemCall(Regs); + +result:=regs.al; +if reqForSocket + then socket:=swap(regs.dx); {force lo-hi} +IPXopenSocket:=(result=0); +{ resultcodes: $00 successful; $FE Socket Table Is Full; + $FF socket already open OR IPX not initilazed. } +end; + +{IPX: 01} +Function IPXCloseSocket(socket:word):boolean; +Var regs:registers; +begin +regs.bx:=$01; +regs.dx:=swap(socket); +IpxSpxSystemCall(regs); +result:=regs.al; +if result<>$FF then result:=$00; +IPXCloseSocket:=(result=$00); +{ possible resultcodes: $00 Successful; $FF IPX not initialized } +end; + + +{IPX: 02} +Function IPXGetLocalTarget(Address:TinternetworkAddress; + Var ImmAddr:TnodeAddress; + VAR ticks:Word ):boolean; +{ Ticks = estimated transmission time, in number of ticks (1/18 sec) } +Var reqAddr:TinternetworkAddress; + repNode:TnodeAddress; + Regs :registers; +begin +move(Address,reqAddr,10); +reqAddr.socket:=swap(Address.socket); {hi-lo} +With regs + do begin + bx:=$0002; + es:=seg(reqAddr); si:=ofs(reqAddr); di:=ofs(repNode); + IpxSpxSystemCall(regs); + ticks:=regs.cx; + result:=regs.al; + if result=0 + then move(repNode,ImmAddr,6); + end; +IPXGetLocalTarget:=(result=$00); +{ resultcodes: $00 Successful; $FA No path to destination node found; + $FF IPX not initialized. } +end; + +Function IPXSendPacket(Var Ecb:Tecb):boolean; +{ the ecb must be filled, before calling this function } +{ Right after this call, IPXrelinquishControl should be called Iteratively, + this allows the sending of the IPX packet. } +Var regs:Registers; +begin +regs.bx:=$0003; +regs.es:=seg(ecb); +regs.si:=ofs(ecb); +IpxSpxSystemCall(Regs); +result:=regs.al; +if result<>$FF then result:=$00; +IpxSendPacket:=(result=$00); +{ possible resultcodes: $00 Successful; $FF IPX not initialized } +end; + + +Function IPXInternalSendPacket(Var Ecb:Tecb):boolean; +Var regs:Registers; +begin +regs.bx:=$000F; +regs.es:=seg(ecb); +regs.si:=ofs(ecb); +IpxSpxSystemCall(Regs); +result:=regs.al; +if result<>$FF then result:=$00; +IpxInternalSendPacket:=(result=$00); +{ possible resultcodes: $00 Successful; $FF IPX not initialized } +end; + + +Function IPXListenForPacket(Var Ecb:Tecb):Boolean; +{ socket must be opened, ECB (partly) filled. } +Var regs:Registers; +begin +regs.bx:=$0004; +regs.es:=seg(ecb); +regs.si:=ofs(ecb); +IpxSpxSystemCall(Regs); +result:=regs.al; +if result<>$FF + then result:=$00; +IpxListenForPacket:=(result=$00); +{resultcodes: $00 Successful; + $FF Listening Socket doesn't exist OR IPX not initialized } +end; + +Function IPXrelinquishControl:boolean; +Var regs:Registers; +begin +regs.bx:=$000A; +IpxSpxSystemCall(Regs); +result:=regs.al; +if result<>$FF then result:=$00; +IpxrelinquishControl:=(result=$00); +{resultcodes: $00 Successful; $FF IPX not initialized } +end; + +Function IPXgetIntervalMarker(Var ticks:word):boolean; +Var regs:Registers; +begin +regs.bx:=$0008; +IpxSpxSystemCall(Regs); +ticks:=regs.ax; +result:=$00; +IPXgetIntervalMarker:=True; +end; + +Function IPXcancelEvent(ECB:Tecb):boolean; +Var regs:registers; +begin +regs.bx:=$0006; +regs.es:=seg(ecb); +regs.si:=ofs(ecb); +IpxSpxSystemCall(Regs); +result:=regs.al; +IPXcancelEvent:=(result=0); +{ resultcodes: 00 Successful; F9 ECB cannot be canceled; + FF ECB not in use OR IPX not initialized. } +end; + +Function IPXdisconnectFromTarget(Address:TinternetworkAddress):boolean; +VAR regs:registers; + LocAddr:TinternetworkAddress; +begin +move(Address,LocAddr,10); +LocAddr.socket:=swap(Address.socket); +regs.bx:=$000B; +regs.es:=seg(LocAddr); +regs.si:=ofs(LocAddr); +IpxSpxSystemCall(Regs); +result:=regs.al; +if result<>$FF + then result:=$00; +IPXdisconnectFromTarget:=(result=0); +{resultcodes: $00 Successful; $FF IPX not initialized } +end; + +Function IPXscheduleIPXevent(ticks:word;Var ECB:Tecb):boolean; +Var regs:registers; +begin +regs.bx:=$0005; +regs.ax:=ticks; +regs.es:=seg(ECB); +regs.si:=ofs(ECB); +IpxSpxSystemCall(Regs); +if result<>$FF + then result:=$00; +IPXscheduleIPXevent:=(result=0); +{resulcodes: 00 successful; FF IPX not initialized } +end; + +Function IPXscheduleSpecialEvent(ticks:word;Var ECB:Tecb):boolean; +Var regs:registers; +begin +regs.bx:=$0007; +regs.ax:=ticks; +regs.es:=seg(ECB); +regs.si:=ofs(ECB); +IpxSpxSystemCall(Regs); +if result<>$FF + then result:=$00; +IPXscheduleSpecialEvent:=(result=0); +{resulcodes: 00 successful; FF IPX not initialized } +end; + +{************** Secondary Procedures ***************************************} + +Procedure IPXSetupListenECB(ESRptr:Pointer;ReceiveSocket:word; + BufPtr:Pointer;BufSize:word; + {out:} Var IpxHdr:TipxHeader; Var ecb:Tecb); +{ Clears IPXheader and ECB, sets values of the required fields within + the ecb and IPX header. } +{ ECB: ESR adress field, socket number, fragment count, frag.descriptor fields } +begin +FillChar(ecb,SizeOf(Tecb),#0); +FillChar(ipxHdr,SizeOF(TipxHeader),#0); +WITH ECB + do begin + if ESRptr<>NIL + then ESRaddress:=ESRptr; + Fragmentcount:=2; + socketNumber:=swap(ReceiveSocket); {hi-lo} + + Fragment[1].Address:=@ipxHdr; + Fragment[2].Address:=BufPtr; + Fragment[1].size:=SizeOf(Tipxheader); + Fragment[2].size:=BufSize; + end; +end; + +Procedure IPXsetupSendECB(ESRptr:pointer; SourceSocket:word; + DestAddr:TinterNetworkAddress; + BufPtr:pointer; BufSize:word; + {out:} Var IpxHdr:TipxHeader; Var ecb:Tecb); +{ Clears IPXheader and ECB, sets values of the required fields within + the ecb and IPX header. } +Var ImmAddr:TnodeAddress; + Ticks:word; +begin +fillchar(ipxHdr,SizeOf(TipxHeader),#0); +with ipxhdr + do begin + PacketType:=IPX_PACKET_TYPE; + Move(DestAddr,Destination,10); + destination.socket:=swap(DestAddr.socket); {hi-lo} + end; +IPXGetLocalTarget(DestAddr,ImmAddr,Ticks); +fillchar(ecb,sizeOf(ecb),#0); +With ecb + do begin + if ESRptr<>NIL + then ESRaddress:=ESRptr; + socketNumber:=swap(SourceSocket); {hi-lo} + Move(ImmAddr,ImmediateAddress,6); + FragmentCount:=2; + fragment[1].Address:=@ipxhdr; + fragment[1].size:=SizeOf(TipxHeader); + fragment[2].Address:=BufPtr; + fragment[2].size:=BufSize; + end; +end; + + + +end. diff --git a/SRC/UNITS/NWLOCK.PAS b/SRC/UNITS/NWLOCK.PAS new file mode 100644 index 0000000..33b6f98 --- /dev/null +++ b/SRC/UNITS/NWLOCK.PAS @@ -0,0 +1,663 @@ +{$X+,B-,V-} {essential compiler directives} + +UNIT nwLock; + +{ nwLock unit as of 950301 / NwTP 0.6 API. (c) 1993,1995, R. Spronk + + This unit was based on units by + + a. Scott A. Lewis, 36 Maythorpe Drive, Windsor, CT 06095, U.S.A. + Note: (1987) 76515,135@Compuserve.Com + + b. Erik van Heyningen, Hague Consulting Group, + The Hague, the Netherlands. + Note: (1994) hcg@hacktick.nl } + +{ Function: Interrupt: Notes: + + Physical File locking/unlocking + ------------------------------- + +* LogPhysicalFile EB (6) -> F203 +* LockPhysicalFileSet F204 +* ReleasePhysicalFile EC -> F205 +* ReleasePhysicalFileSet CD -> F206 +* ClearPhysicalFile ED (6) -> F207 +* ClearPhysicalFileSet CF -> F208 + + Logical File Locking + -------------------- + ++ LogLogicalFile (5) ++ LogLogicalFileSet (5) ++ ReleaseLogicalFile (5) ++ ReleaseLogicalFileSet (5) ++ ClearLogicalFile (5) ++ ClearLogicalFileSet (5) + + Logical record locking/unlocking + -------------------------------- + +* LogLogicalRecord D0 -> F209 +* LockLogicalRecordSet D1 -> F20A +* ReleaseLogicalRecord D2 -> F20C +* ReleaseLogicalRecordSet D3 -> F20D +* ClearLogicalRecord D4 -> F20B +* ClearLogicalRecordSet D5 -> F20E + + GetLogicalRecordInformation F217/F0 (3) + GetLogicalRecordsByConnection F217/EF (3) + + Physical record locking/unlocking + --------------------------------- + +. LogPhysicalRecord BC -> F21A +. LockPhysicalRecordSet C2 -> F21B +. ReleasePhysicalRecord BD -> F21C +. ReleasePhysicalRecordSet C3 -> F21D +. ClearPhysicalRecord BE -> F21E +. ClearPhysicalRecordSet C4 -> F21F + + GetPhysRecLocksByConnectionAndFile F217/ED (3) + GetPhysRecLocksByFile F217/EE (3) + +- ControlRecordAccess 5C (DOS) (4) + + + Not Implemented + --------------- + +- GetLockMode C600 (1) +- SetLockMode C601 (1) +- BeginLogicalFileLocking C8 / F201 (2) +- EndLogicalFileLocking C9 / F202 (2) + + Notes: -Semaphores can be found in the nwSema Unit + (1) Obsolete + (2) Not supported by (all) 3.x versions + (3) Supported by NW 3.x and upwards + (4) Generic physical record locking call, DOS 3.1+ + Equivalent to: + I . LockPhysicalRecord (without logging) + II. ReleasePhysicalrecord + (5) Use the equivalent LogicalRecordLocking calls + to emulate LogicalFileLocking. NOTE: remember + that there's only ONE Log. + (6) Includes VLM fix for filenames (GetTrueEntryName + in the nwFile unit is called) + -> F2xx To be rewritten to the F2 interface. +} + +INTERFACE + +Uses nwIntr,nwMisc; + +CONST { Log Resource } + LD_LOG = 0; + LD_LOG_LOCK = 1; { Deny all access to file/record } + LD_LOG_LOCK_RO = 3; { Allow read / deny write (record locking only)} + { Lock Resource } + LD_lOCK = 0; { Deny all access to file/record } + LD_LOCK_RO = 1; { Allow read / deny write (record locking only)} + +Var Result:word; + +{------------------- PHYSICAL FILE LOCKING OPERATIONS -----------------------} + +{F204 [2.15c+]} +FUNCTION LockPhysicalFileSet(TimeoutLimit : Word) : Boolean; +{Lock a set of files that were logged by the LogFile function } + +{CD.. [1.0+]} +FUNCTION ReleasePhysicalFileSet:boolean; +{ Release lock on set of files in logged table, files remain logged } + +{CF [1.0+]} +FUNCTION ClearPhysicalFileSet : Boolean; +{ Unlock and UnLog the entire logged file set } + +{EB.. [1.0+]} +FUNCTION LogPhysicalFile(FileName : String; LockDirective : Byte; TimeoutLimit : Word) : Boolean; +{Log files for later use } + +{EC.. [1.0+]} +FUNCTION ReleasePhysicalFile(FileName : String) : boolean; +{Release file lock, but keep logged in the table } + +{ED.. [1.0+]} +FUNCTION ClearPhysicalFile(FileName : String) : boolean; +{Release a file from the file log table, unlock the file if it is locked } + +{ ------------------- LOGICAL RECORD LOCKING OPERATIONS --------------------} + +{D0 [1.0+]} +FUNCTION LogLogicalRecord(Name:string; LockDirective:Byte; Timeout: Word) : Boolean; +{Add a record to the lockable logical record table } + +{D1.. [1.0+]} +FUNCTION LockLogicalRecordSet(LockDirective:Byte; TimeoutLimit : Word) : Boolean; +{Lock all logged records } + +{D2.. [1.0+]} +FUNCTION ReleaseLogicalRecord(Name : String) : Boolean; +{Unlock a record, keep record in logtable } + +{D3.. [1.0+]} +FUNCTION ReleaseLogicalRecordSet : Boolean; +{Unlock all locked records, keep records logged } + +{D4.. [1.0+]} +FUNCTION ClearLogicalRecord(Name : String) : Boolean; +{Unlock and UnLog a record } + +{D5.. [1.0+]} +FUNCTION ClearLogicalRecordSet : Boolean; +{Unlocks and UnLogs all logged records } + +{F217/EF [2.1x+]} +Function GetLogicalRecordLocksByConnection(ConnNbr:word; + {i/o} Var NextRecNbr:word; + Var TaskNbr:word; + Var LockStatus:Byte; + Var LockName:String):Boolean; +{ You need console operator rights to use this function } + + +{----------------------- PHYSICAL RECORD LOCKING OPERATION -----------------} + +{BC.. [1.0+]} +function LogPhysicalRecord(Handle:Word; + LockDirective:Byte; + RecordOffset,RecordLength:Longint; + TimeOutLimit:Word): boolean; +{Add a record to the lockable physical record logtable } + +{BD.. [1.0+]} +function ReleasePhysicalRecord( Handle:Word; RecordOffset,RecordLength:Longint) : boolean; +{Unlock record, keep record logged } + +{BE.. [1.0+]} +function ClearPhysicalRecord(Handle:Word; RecordOffset,RecordLength:Longint): boolean; +{Unlock and Unlog a record } + +{C2.. [1.0+]} +function LockPhysicalRecordSet(LockDirective: byte; TimeoutLimit : Word): boolean; +{Lock all logged records } + +{C3.. [1.0+]} +function ReleasePhysicalRecordSet : boolean; +{Unlock all logged records, keep records logged } + +{C4.. [1.0+]} +function ClearPhysicalRecordSet : boolean; +{Unlocks and unLogs all logged records } + + +IMPLEMENTATION{==============================================================} + +uses nwFile; + +Var regs:TTRegisters; + + +Procedure SetLockMode(mode:Byte); +begin +regs.AH:=$c6; +regs.al:=mode; { 0 or 1 } +RealModeIntr($21,regs); +end; + +(* THE FOLLOWING PROCEDURES ARE FOR LOGGING AND LOCKING/RELEASING FILE SETS *) +(* File locking by set can be very effective in avoiding deadly embrace *) + +{F204 [3.x+]} +FUNCTION LockPhysicalFileSet(TimeoutLimit : Word) : Boolean; +Type Treq=record + _TimeOutLimit:Word; + end; + TPreq=^Treq; +BEGIN +With TPreq(GlobalReqBuf)^ + do begin + _TimeoutLimit:=swap(TimeoutLimit); + end; +F2SystemCall($04,SizeOf(Treq),0,result); +LockPhysicalFileSet:=(result=0); +{ 00 Successful FF Fail FE Timeout } +END; + + +{CD.. [1.0+]} +FUNCTION ReleasePhysicalFileSet:boolean; +{ Release lock on set of files in logged table, files remain logged } +{ These files remain open but cannot be accessed without an error } +{ To reuse them, send another lock file set } +Type Treq=record + end; +BEGIN +WITH Regs + DO BEGIN + AH := $CD; + RealModeIntr($21,Regs); + result:=0; + END; +ReleasePhysicalFileSet:=true; +END; + +{CF [2.0+]} +FUNCTION ClearPhysicalFileSet : Boolean; +{ Unlock and UnLog the entire personal file set (all files are closed) } +BEGIN +WITH Regs + DO BEGIN + AH := $CF; + RealModeIntr($21,Regs); + result:=0; + END; +ClearPhysicalFileSet:=true; +END; + + +{EB.. [2.0+] } +FUNCTION LogPhysicalFile(FileName : String; LockDirective : Byte; TimeoutLimit : Word) : Boolean; +{ This function allows a station to log files for later personal use } +{ After the desired files are logged, function CBh can be used to lock } +{ the entire set of files } +{ !! There is a known problem with lock directive 3 (log and lock shareable) + use 1 instead. } +Type Treq=record + LockDirective:Byte; + TimeOutLimit:Word; + FileName:string[255]; { or Asciiz ? } + end; +Var temp1,temp2:word; + TEname:string; +BEGIN +GetTrueEntryName(FileName,TEname); { also UpCases string } +{ IF this function isn't included and VLMs are used, this call will + *appear* to be successful. No error code is returned, the call is + however unsuccessful. } +WITH Regs + DO BEGIN + AH := $EB; + AL := LockDirective; { 0 = Log Only, 1 Log and Lock } + BP := TimeoutLimit; { in 1/18 seconds, 0 = No wait } + TEname := TEName+#0; { Terminate with a nul for asciiz } + Move(TEname[1],GlobalReqBuf^,ord(TEname[0])); + GetGlobalBufferAddress(DS,DX,temp1,temp2); + { DS:DX real mode pointer to buffer in realmode-range holding Filename } + RealModeIntr($21,Regs); + Result:=AL; + LogPhysicalFile := (Result = 0); + END; +{ FE Timeout FF hardware error } +END; + + +{EC.. [1.0+]} +FUNCTION ReleasePhysicalFile(FileName : String) : boolean; +{ Release file lock, but keep logged in the table } +Var temp1,temp2:word; + TEname:string; +BEGIN +GetTrueEntryName(FileName,TEname); { also UpCases string } +{ IF this function isn't included and VLMs are used, this call will + *appear* to be successful. No error code is returned, the call is + however unsuccessful. } +WITH Regs + DO BEGIN + AH := $EC; + UpString(FileName); + TEName := TEName+#0; { null terminate } + Move(TEname[1],GlobalReqBuf^,ord(TEname[0])); + GetGlobalBufferAddress(DS,DX,temp1,temp2); + { DS:DX real mode pointer to buffer in realmode-range holding Filename } + RealModeIntr($21,Regs); + result:=AL; + ReleasePhysicalFile:=(result=0); + END; +{FF File not found } +END; + +{ED.. [1.0+]} +FUNCTION ClearPhysicalFile(FileName : String) : boolean; +{ Release a file from the file log table, unlock the file if it is locked } +Var temp1,temp2:word; +BEGIN +WITH Regs + DO BEGIN + AH := $ED; + UpString(FileName); + FileName := FileName+#0; { null terminate } + Move(Filename[1],GlobalReqBuf^,ord(Filename[0])); + GetGlobalBufferAddress(DS,DX,temp1,temp2); + { DS:DX real mode pointer to buffer in realmode-range holding Filename } + RealModeIntr($21,Regs); + Result:=AL; + ClearPhysicalFile := (Result = 0); + { 0 means OK FF File not found} + END; +END; + + +(* THE FOLLOWING FUNCTIONS ARE FOR LOGICAL LOCKING OPERATIONS *) +(* Logical locks work only if all software accessing the files use the *) +(* same logical synchronization scheme. Logical locks are much easier *) +(* and faster to implement than physical locks. *) + + +{D0 [1.0+]} +FUNCTION LogLogicalRecord(Name:String; LockDirective:Byte; Timeout: Word) : Boolean; +{ This function will log the specified record string in the record log table } +{ of the requesting station. } +{ Max length of name: 99 chars } +{ LockDirective LD_LOG = 0; + LD_LOG_LOCK = 1; Deny all access to file/record + LD_LOG_LOCK_RO = 3; Allow read / deny write } +{ TimeOut=0 means NoWait } +Var temp1,temp2:word; +BEGIN +WITH Regs + DO BEGIN + AH := $D0; + AL := LockDirective; + UpString(Name); + Move(Name,GlobalReqBuf^,ord(Name[0])+1); + GetGlobalBufferAddress(DS,DX,temp1,temp2); + { DS:DX real mode pointer to buffer in realmode-range holding Filename } + BP := Timeout; { In 1/18th seconds (use only with lock bit set } + RealModeIntr($21,Regs); + Result:=AL; + LogLogicalRecord := (Result=0); + { FFh fail } + { FEh timeout } + { 96h No dynamic memory for file } + END; +END; + + +{D1 [1.0+]} +FUNCTION LockLogicalRecordSet(LockDirective:Byte; TimeoutLimit : Word) : Boolean; +{ Call this to lock all records logged with Log_Logical_Record } +{ LockDirective LD_LOCK = 0; Deny all access to file/record + LD_LOCK_RO = 1; Allow read / deny write } +BEGIN +WITH Regs +DO BEGIN + AH := $D1; + AL := LockDirective; + BP := TimeoutLimit; { In 1/18th seconds, 0 = No wait } + RealModeIntr($21,Regs); + Result:=AL; + LockLogicalRecordSet := (Result=0); + {00 - Success + FF - fail, + FE - timeout } + END; +END; + +{D2.. [1.0+]} +FUNCTION ReleaseLogicalRecord(Name : String) : Boolean; +{ Call this to release a logical record lock without removing the rec } +{ from the table } +Var temp1,temp2:word; +BEGIN +WITH Regs +DO BEGIN + AH := $D2; + UpString(Name); + Move(Name,GlobalReqBuf^,ord(Name[0])+1); + GetGlobalBufferAddress(DS,DX,temp1,temp2); + { DS:DX real mode pointer to buffer in realmode-range holding Filename } + RealModeIntr($21,Regs); + Result:=AL; + ReleaseLogicalRecord := (Result=0); + { FF No record found } + END; +END; + +{D3.. [1.0+]} +FUNCTION ReleaseLogicalRecordSet : Boolean; +{ release all locked logical records, doesn't remove them from the table } +BEGIN +WITH Regs +DO BEGIN + AH := $D3; + RealModeIntr($21,Regs); + Result:=0; + ReleaseLogicalRecordSet := True; + END; +END; + +{D4.. [1.0+]} +FUNCTION ClearLogicalRecord(Name : String) : Boolean; +{ This call unlocks and removes the Logical Record lock from the table } +Var temp1,temp2:word; +BEGIN +WITH Regs +DO BEGIN + AH := $D4; + UpString(Name); + Move(Name,GlobalReqBuf^,ord(Name[0])+1); + GetGlobalBufferAddress(DS,DX,temp1,temp2); + { DS:DX real mode pointer to buffer in realmode-range holding Filename } + RealModeIntr($21,Regs); + Result:=AL; + ClearLogicalRecord := (Result=0); + { FF No record Found } + END; +END; + +{D5.. [1.0+]} +FUNCTION ClearLogicalRecordSet : Boolean; +{ Unlocks and removes from the table all of the stations logical record locks } +BEGIN +WITH Regs +DO BEGIN + AH := $D5; + RealModeIntr($21,Regs); + Result:=0; + ClearLogicalRecordSet := True; + END; +END; + + +(************* THE FOLLOWING ARE PHYSICAL RECORD LOCK CALLS ****************) + +{F:BC..:Lock (& Log) records in a file} +function LogPhysicalRecord(Handle:Word; + LockDirective:Byte; + RecordOffset,RecordLength:Longint; + TimeOutLimit:Word): boolean; +{ Max length of name: 99 chars } +{ LockDirective LD_LOG = 0; + LD_LOG_LOCK = 1; Deny all access to file/record + LD_LOG_LOCK_RO = 3; Allow read / deny write } +{ TimeOut=0 means NoWait; TimeOut not valid if logging only } +{ Handle is the file handle } +begin +with regs +do begin + AH := $BC; + AL := LockDirective; + BX := Handle; + CX := HiLong(RecordOffset); + DX := LowLong(RecordOffset); + BP := TimeOutLimit; + SI := HiLong(RecordLength); + DI := LowLong(RecordLength); + RealModeIntr($21,Regs); + Result:=AL; + LogPhysicalRecord := (Result=0); + { $FF = fail, $FE Timeout, $96 = No dynamic memory } + end; +end; + +{BD.. [1.0+]} +function ReleasePhysicalRecord( Handle:Word; RecordOffset,RecordLength:Longint) : boolean; +{ When a record is released, it is unlocked for use by someone else, but } +{ it remains in the log table } +{ Handle is the file handle, Start_Hi and Start_Lo are the boundaries of } +{ the locked region to be released } +begin +with regs +do begin + AH := $BD; + BX := Handle; + CX := HiLong(RecordOffset); + DX := LowLong(RecordOffset); + SI := HiLong(RecordLength); + DI := LowLong(RecordLength); + RealModeIntr($21,Regs); + Result:=AL; + ReleasePhysicalRecord := (Result=0); + { $FF = No locked record found} + end; +end; + +{BE.. [1.0+]} +function ClearPhysicalRecord(Handle: Word; + RecordOffset,RecordLength:Longint): boolean; +{ Handle is the file handle, Start_Hi and Start_Lo are the boundaries } +{ of the file region to be locked. Clearing a record will unlock it } +{ and remove it from the log table. } +begin +with regs +do begin + AH := $BE; + BX := Handle; + CX := HiLong(RecordOffset); + DX := LowLong(RecordOffset); + SI := HiLong(RecordLength); + DI := LowLong(RecordLength); + RealModeIntr($21,Regs); + Result:=AL; + ClearPhysicalRecord := (Result=0); + { $FF No locked record found } + end; +end; + +{C2.. [1.0+]} +function LockPhysicalRecordSet(LockDirective: byte; TimeoutLimit: Word): boolean; +{ flgs are the lock flags: bit 1 set means shared (non-exclusive) lock } +{ Timeout is in 1/18 seconds, 0 = no wait, -1 means indefinite wait } +{ This function attempts to lock all of the records logged in the station's } +{ log table. } +{ LockDirective LD_LOCK = 0; Deny all access to file/record + LD_LOCK_RO = 1; Allow read / deny write } +{ !! There is known problem when the locking directive equals 1. } +begin +with regs +do begin + AH := $C2; + AL := LockDirective; + BP := TimeOutLimit; + RealModeIntr($21,Regs); + Result:=AL; + LockPhysicalRecordSet := (Result=0); + { $FF = fail, $FE = timeout fail } + end; +end; + +{C3.. [1.0+]} +function ReleasePhysicalRecordSet : boolean; +{ unlocks the entire record log table of the station. records remain in } +{ the log table. } +begin + regs.AH := $C3; + RealModeIntr($21,Regs); + Result:=0; + ReleasePhysicalRecordSet := True; +end; + +{C4.. [1.0+]} +function ClearPhysicalRecordSet : boolean; +{ unlocks and removes from the log table any records logged and locked } +begin + regs.AH := $C4; + RealModeIntr($21,Regs); + Result:=0; + ClearPhysicalRecordSet := True; +end; + + +{F217/EF [2.1x+]} +Function GetLogicalRecordLocksByConnection(ConnNbr:word; + {i/o} Var NextRecNbr:word; + Var TaskNbr:word; + Var LockStatus:Byte; + Var LockName:String):Boolean; +{ You need console operator rights to use this function } +Type Treq=record + len :Word; + subFunc :Byte; + _ConnNbr :word; {lo-hi} { !! Invalid numbers may cause an abend } + _LastRecSeen:word; {lo-hi} + end; + Trep=record + _LastRecSeen :word; {lo-hi} + _NbrOfRecords:word; {lo-hi} + _LockInfo :array[1..508] of byte; + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$EF; + _ConnNbr:=ConnNbr; + _LastRecSeen:=NextRecNbr; + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),SizeOf(Trep),result); +With TPrep(GlobalReplyBuf)^ + do begin + Move(_LastRecSeen,NextRecNbr,2); + + + end; +GetLogicalRecordLocksByConnection:=(result=0) +{ Valid completion codes: + $00 Success + $FF Failure +} +end; + +{$IFDEF xxxx} + +{F217/ [2.1x+]} +Function ( ):Boolean; +Type Treq=record + len:Word; + subFunc:Byte; + + end; + Trep=record + + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$ + + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),SizeOf(Trep),result); +With TPrep(GlobalReplyBuf)^ + do begin + + end; + :=(result=0) +{ Valid completion codes: + $00 Success + $FF Failure. +} +end; + +{$ENDIF} + +Begin +SetLockMode(1); +END. \ No newline at end of file diff --git a/SRC/UNITS/NWMESS.DCU b/SRC/UNITS/NWMESS.DCU new file mode 100644 index 0000000000000000000000000000000000000000..25c7829bb76bade077412b18b3eed212ad81d99f GIT binary patch literal 5280 zcmcIndvH|M8UN1Sd(XYQ4nuMMmV!0aSd6xYI>lg zgy^|pcd3Q>hRj)AQW{uJlo`i?@Q<~o7M2zh4c{?9+zqp)FD+R_G--8<%M#Gi%;G>v z*|JhD8atXILip7aClnVgSiERKN$H%T?4rVwqGbVLS&)1riOg7(5Z|w$E9kGpzoaKk z4wOc$IK2gVut|X4m#A-7n`5w92b+X0HbskJ&=&?zKVp!EPzDTo^=$khbyuJ^43Ipszeg7}yVd-h3wghzoZj6P? zQMw_L82?;k@Z>KPmP9DTOoqn`nx+>A@=C-p#TuX|3ifhZ+a2x2D~WS{#0xXoOJk=O zX4zf_>2Cds-V7*e*23jB-s&v1sTf{e(&3%Pj!pZ9D$fOxJ!R>T5+zF1R_8NJ9e*?eIdN@;>e;glA}FBzFce)$a7uCq#5J0ExsI%DQL_25e3sP#da>+zU$maIzoiwHME$SG9G)lw6sLQ^&8Ad=eU6?t5i;cSUsLcR)k3Bm2$Xv~bRyfhtT zi*^J^G*e7TruMs8SoXx&NiA+<@I`q`D|B**ij&4wgbY>2{SW#!fnokM~drQ zFDurPG!1dPPPolXwRpm@R%F6$<_L>}rdohnc6dtwYHj8;B^Y68F$#m@ZKwffZ3yv# z^}lhp1Ulhu`rPXnghE`gYUIWF5_%@f|T^!SaBYLj2lr7Tt92 z5HAJ8+z%|xJ!4_<%q5(^2z4-@=XA#EETqsCyW4JN^a{^IHn1gx977B)O5-1Ve04{E zd^7VGF78l!NPG_x{fun+FEQ?g@lu*X!$BX=ESd)T1^9WO=W*1n1$`d++d+|l`kE;6 zgn69gjNIu{bFvAx=q^>SnD<5G*`t$lIpWxxvG=!wJy4&Nlan!Pd=C4{6ny>6LvJyX zjnnlQx1pPSa}mf@1ICSMMlAb5ikp-hHB31$+-Lk*wFN828BK1V%9 z#E@YQ=ryDtHkO_gYck%iLH2LR(jiOk@e}Inrk=1gd1Xz=dx`k49l;PXlZ&1X(Qsi< z=HrxtSwz0exRWx(aGEH5xXLndyJq3Y9gX`j8{5Q>*XMNb=Ft)@3wQA%@QY}-mW>;= z9QvE+ww8%IZ8Pqy*Knh4!A-Ii7t-%=eQcvE`gZu*gF68Jj4?Qs#?nmVDcl$>ID9#8K~!6bqTWgpn_TJQ18X|aIvwzT zLP+;8F%WH=dpKQn`Oq@48?r!gJ8EMDHR#@g>}#|;;9*~ArnrhH z^EP*^AT16p9xVYaNnA7fiT<)bXo}cxrh@l`I4Z`{QSlVoWVF3#`_Mk6>v}t=1^F-` z^+lBJ4p5D?gvt`~XoNeTq+Sl*OW>^q?`81T(bf1jsXn2OLh<$JpGN-z`WMl^i+%{S zx2R7@6&tkxzt7O}fRB98d|+uQ@KOMbQ~)C{p{+%KE$GW=>(E~Zol3Odpsfd;AjW%^cKc-;MYOU?T^4(Jup=deAT1>Hh6CXgS$X?0Y4{!s}sP^Y2fM$ z;OY!8cLta{3w)hL|19uy4p`*Y3SKLCt>Co+i|5flkN$c1xBy=l;FH@wfWwQx>?Ppx z60m$3yvxAiZQ$=Nu-FDHwgaE-z~X<(>eZ>K@AMn}_{eHk}f8d&13V12trGS?LY!3t-i19%9NQJLdG24{} znkHIY8NhdjxP>O&On)TK3!gY+`Ox=)=L6n-Vgh8F>2*BsytI{mkC&X6{y;m`xP^}4 z4dJEZ^ckKDUiusTU5zi%B{jZD-{2DV(me_)8Y6m&HjI>?ImvdVw4 zn~R_NWvSJICr60BvORqz?cv(7i2V+giE&l_kaF!(u4OnY*#wRB=1DG*-eL$U=v|-{ zWE~_LWap>s%aT}0<~EgEGB>nRs?0G+uNXsRDv%}~4J1_{mrH4{x#4ovoO

n%0N4 z4Z+}{l?l85^s4XXot?D}I!2!T_vy~gS6VsF`i5XfO(g|6DXR9zrB_YTSSvZV36eD} zxPNlN_FbK2SFkb|tg6(46%AT&)Jmfn-BBDO*;X3IMq2xt7QCi%t0DAV9Tzs_!G_P2 zFGC~kLsm^+Zxr;-A1U%5Q1>eI;q2D52vrFwbMx;zMFhKRjpNkRE#XYv5?!V)ovDYX z+a9c`ysxI~w$scHkv)Tg(aJ+}Wk*nar3XQ((R^2%8rp+edwRvMH7Xs|ot0Lc)SXqn zvS$}o^CT^!qM;)=x@;G_!pe`u%I9l1s=7_O3v_H$e%Fe|bXigP9V>dG+VA=H74`g( z6@}9Gtf(JiET~##?L(|-ct!hQMZbS;9<^lmG-t`~X^|zP5bzn98v(7I3s~Dgq2$~G zQQHveNX`XH@vA&ojirMtz@(Ka&|xrEhB{{13bhTMhmP9eBjNWPpAkfj@8D%(QgS1+ z2a$IiKhQF|@zWiCrbEX#G~1z5JN1=)g2NvSzx=Em>Wnie8FHO|4E+`0PjL9N9h%$C z58eZf2^!G?hi-NB8Xdofobge|?@8$K^Yn_tzuwK?Q0mb*1@ftm{4b85gUSV;blR`Y)O@BWeHu literal 0 HcmV?d00001 diff --git a/SRC/UNITS/NWMESS.PAS b/SRC/UNITS/NWMESS.PAS new file mode 100644 index 0000000..231e567 --- /dev/null +++ b/SRC/UNITS/NWMESS.PAS @@ -0,0 +1,308 @@ +{$X+,B-,V-} {essential compiler directives} + +UNIT nwMess; + +{ nwMess unit as of 950301 / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +INTERFACE + +Uses nwIntr,nwMisc; + +{ Primary functions: Interrupt: comments: + +* BroadcastToConsole (F215/09) +* GetBroadcastMessage (F215/01) +* GetBroadcastMode (DE..(..04)) +* SendBroadcastMessage (F215/00) +* SendConsoleBroadcast (F217/D1) +* SetBroadcastMode (DE..(..0x)), x= 0,1,2,3 + + Secondary Functions: + +* SendMessageToUser + + Not implemented: + +- CheckPipeStatus (F215/08) (1) +- CloseMessagePipe (F215/07) (1) +- DisableStationBroadcast (F215/02) (3) +- EnableStationBroadcast (F215/03) (3) +- GetPersonalMessage (F215/05) (1) +- LogNetworkMessage (F215/0D) (2) +- OpenMessagePipe (F215/06) (1) +- SendPersonalMessage (F215/04) (1) + +Notes: + (1) These calls are NOT supported by Netware 386 versions shipped after + December 1990, because they use pipe mechanisms, which cause a + considerable deal of server overhead. + These functions are not implemented in this unit. + Use IPX/SPX peer-to-peer communication instead (nwIPX,nwSPX,nwPEP). + (2) Network msg file no longer supported by 3.x + (3) Not supported by Netware 3.x. Use SetBroadcastMode instead. +} + +Var result1:word; + +{F215/09 [2.15c+]} +Function BroadcastToConsole(message:string):boolean; +{ broadcast a message to the file server console. } + +{F215/01 [2.15c+]} +Function GetBroadcastMessage(var bmessage: string):boolean; +{ Reads a broadcast message strored at server } + +{DE..(..04) [1.x/2.x/ 3.x]} +Function GetBroadcastMode(var bmode:byte):boolean; +{ Returns the message mode. } + +{F215/00 [2.15c+]} +Function SendBroadcastMessage( message:string; + connCount:byte; + connList:TconnectionList; + VAR result1list:TconnectionList ):boolean; +{ Sends a broadcast message to a number of logical connections. } + +{DE..(..0n) n=0,1,2,3 [1.x/2.x/3.x]} +Function SetBroadcastMode(bmode:byte):boolean; + +{F217/D1 [2.15c+]} +Function SendConsoleBroadcast(message:string; connCount:byte; + connList:TconnectionList ):boolean; +{ Sends a message to a number of connections, as if the message was send + by a console broadcast command. Console oprator privileges required. } + +{--------------------Secondary Functions-------------------------------} + +Procedure SendMessageToUser(UserName,Message:String); +{ sends a message to a (group of) users. + The username may contain wildcards (* and ?). + The message will not be received by stations whose status is CASTOFF.} + + +IMPLEMENTATION {============================================================} + +USES nwConn; + +{DE..(..04) [1.x/2.x/ 3.x]} +Function GetBroadcastMode(var bmode:byte):boolean; +{ Returns the message mode. + + 00 Server Stores : Netware Messages and User Messages, + Shell automaticly displays messages. + 01 Server Stores : Server Messages. (User messages discarded) + Shell automaticly displays messages. + 02 Server stores : Server messages only. + Applications have to use GetBroadCastMessage to see if there is a message. + 03 Server stores : Server messages and User messages. + Applications have to use GetBroadCastMessage to see if there is a message. } +var regs : TTregisters; +begin +regs.ah := $de; +regs.dl := $04; +RealModeIntr($21,regs); +bmode := regs.al; +result1:=$00; { the call has no return codes } +GetBroadCastMode:=True; +end; + + +{DE..(..0n) n=0,1,2,3 [1.x/2.x/3.x]} +Function SetBroadcastMode(bmode:byte):boolean; +{ Sets the new message mode. + + possible result1code: $FF ( illegal broadcastmode supplied or + the broadcastmode after the call is not equal + to the intended broadcast mode ) + + 00 Server Stores : Netware Messages and User Messages, + Shell automaticly displays messages. + 01 Server Stores : Server Messages. (User messages discarded) + Shell automaticly displays messages. + 02 Server stores : Server messages only. + Applications have to use GetBroadCastMessage to see if there is a message. + 03 Server stores : Server messages and User messages. + Applications have to use GetBroadCastMessage to see if there is a message. } +var regs : TTregisters; +begin +if (bmode <4) + then begin + regs.ah := $de; + regs.dl := bmode; + RealModeIntr($21,regs); + if regs.al<>bmode { if confirmation of new mode unequal desired mode } + then result1:=$FF + else result1:=$00; + end + else result1:=$FF; +SetBroadcastMode:=(result1=0); +end; + + + +{F215/01 [2.15c+]} +Function GetBroadcastMessage(var bmessage: string):boolean; +{ An application should poll this to see if there is a broadcastmessage + stored (for this workstation) at the default server. + The message mode must be 2 or 3. (No Notification by Shell) + If no message was stored at the server, or the message was empty, + this function will return FALSE and an errorcode of $103. } +Type Treq=record + len :word; + subF :byte; + end; + Trep=record + _message:string[55]; + end; + TPreq=^Treq; + TPrep=^Trep; +BEGIN +With TPreq(GlobalreqBuf)^ + do begin + subF:=$01; + len:=1; + end; +F2SystemCall($15,sizeOf(Treq),sizeOf(Trep),result1); +If result1=0 + then bmessage:=TPrep(GlobalReplyBuf)^._message; + +if bmessage[0]=#0 then result1:=$103; { whups! empty message } + +GetBroadCastMessage:=(result1=0); +{ returncodes: + 00 Successful; FC Message Queue Full; + FE I/O failure: Lack of dynamic workspace. + 103 No msgs stored at server. } +end; + + +{F215/00 [2.15c+]} +Function SendBroadcastMessage( message:string; + connCount:byte; + connList:TconnectionList; + VAR result1list:TconnectionList ):boolean; +{ Sends a broadcast message to a number of logical connections. + The connectionlist is an array[1..connCount] of logical connection numbers, + the result1 of the broadcast can be found in the result1List parameter. + example: + connCount=5 + connList= [ 4,9,1,5,2 ] + + result1List= [$00, $00, $FC, $FD, $FF] + + possible codes in result1List: + $00: broadcast to this logical connnection was successful. + $FC: message rejected, buffer for this station already contains a message, + $FD: invalid connection number + $FF: The target connection has blocked incoming messages, + or the target connection is not in use. } +Type Treq=record + len :word; + subF :byte; + _connCount :byte; + connLandMessage:array[1..306] of byte; { 250 conn, 56 msg } + end; + Trep=record + connCount:byte; + _result1List:TconnectionList; + end; + TPreq=^Treq; + TPrep=^Trep; +Var t:byte; +BEGIN +With TPreq(GlobalReqBuf)^ + do begin + subF:=$00; + _connCount:=connCount; + move(connList[1],connLandMessage[1],connCount); + t:=ord(message[0]); if t>55 then t:=55; + move(message[0],connLandMessage[connCount+1],t+1); + len:=3+connCount+t; { 2 bytes + [connList] + len(str) + str[0] } + end; +F2SystemCall($15,sizeOf(Treq),sizeOf(Trep),result1); +If result1=0 + then with TPrep(GlobalReplyBuf)^ + do result1List:=_result1list; +SendBroadcastMessage:=(result1=0); +end; + + +{F215/09 [2.15c+]} +Function BroadcastToConsole(message:string):boolean; +{ broadcast a message to the file server console. + The message (max 60 chars, in ascii range [$20..$7E]) will be displayed + at the bottom of the console screen. + This function truncates the messagelength to 60 and repaces illegal + characters with a . } +Type Treq=record + len :word; + subF :byte; + _message :string; + end; + TPreq=^Treq; +Var t:byte; +BEGIN +With TPreq(GlobalReqBuf)^ + do begin + subF:=$09; + PstrCopy(_message,message,60); + for t:=1 to 60 + do if (_message[t]<>#$0) and + ((_message[t]<#$20) or (_message[t]>#$7E)) + then _message[t]:='.'; + len:=62; + end; +F2SystemCall($15,sizeOf(Treq),0,result1); +BroadcastToConsole:=(result1=0); +{ result1codes: 00 success ; $FC message queue full ; + $FE I/O failure: lack of dynamic workspace } +end; + + +{F217/D1 [2.15c+]} +Function SendConsoleBroadcast(message:string; connCount:byte; + connList:TconnectionList ):boolean; +{Sends a message to a number of connections, as if the message was send + by a console oprator. Console operator privileges required. + If connCount equals 0, then the message is send to all connections. } +Type Treq=record + len :word; + subF :byte; + _ConnCount:byte; + _connAndMess:array[1..306] of byte; + end; + TPreq=^Treq; +Var t:byte; +BEGIN +With TPreq(GlobalReqBuf)^ + do begin + subF:=$D1; + _connCount:=connCount; + Move(connList[1],_connAndMess[1],connCount); + t:=ord(message[0]); if t>55 then t:=55; + {!! to do: strip hi-bit of message.. } + Move(message[0],_connAndMess[connCount+1],t+1); + len:=t+connCount+3; + end; +F2SystemCall($17,sizeOf(Treq),0,result1); +SendConsoleBroadcast:=(result1=0); +{result1codes: $00 success; $C6 No Console Rights} +end; + +{=================== Secondary Functions ===================================} + +Procedure SendMessageToUser(UserName,Message:String); +{ sends a message to a (group of) users. + The username may contain wildcards (* and ?). + The message will not be received by stations whose status is CASTOFF.} +{ calls nwConn.getObjectConnectionNumber and nwMess.SendBroadcastMessage } +Var NbrOfConn:byte; + connList,result1List:TconnectionList; +begin +IF NwConn.GetObjectConnectionNumbers(UserName,1 {OT_USER},NbrOfConn,connList) + AND (NbrOfConn>0) + then SendBroadcastMessage(Message,NbrOfConn,connList,result1List); +end; + + +end. {unit nwMess} diff --git a/SRC/UNITS/NWMISC.DCU b/SRC/UNITS/NWMISC.DCU new file mode 100644 index 0000000000000000000000000000000000000000..472f511089bf7e8e12ed5158c43313825cb7e1c2 GIT binary patch literal 22368 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 + then Copy len bytes from source to dest. + else Copy source to dest and fill out with NULLs. + Length(Dest) will allways be set to len. } + +procedure ZStrCopy(dest:String;VAR source;len:byte); +{ 1. Copies len bytes form an array to a pascal type string. } +{ 2. Trailing NULLs are removed from the string. } +{ consequently, the length of dest (dest[0]) will allways be <= len. } +{ -SOURCE is an array of byte: array[ ] of byte; } + +Function IsLowerNetworkAddress(Var a, b): Boolean; +{ Compare two net&node addresses. + a and b should be of type TinternetworkAddress } + +Function IsEqualNetworkAddress(Var a, b): Boolean; +{ Compare two net&node addresses. + a and b should be of type TinternetworkAddress } + +Function IsLaterNovTime(time1,time2:TnovTime):boolean; + +Function IsEqualNovTime(time1,time2:TnovTime):boolean; + +Function MapV2RightsToV3(V2Rights:byte):word; + +Function MapV3RightsToV2(V3Rights:Word):Byte; + +Procedure GetNWversion(Var version:word); +{ determine the version of software installed on the current file server. } +{ see GetFileServerInformation F217/11 for more information } +{ Version: MajorVersion * 100 + MinorVersion; e.g. 311 for 3.11 } + +Procedure EncryptPassword(objId:longint;password:string; + {i/o} Var Ekey:TencryptionKey); +{ called by LoginToFileServer (unit nwConn), + and by VerifyBinderyObjectPassword, ChangeBinderyObjectPassword (nwBindry) } +{ Source of the encryption routine: LOGON.PAS by Barry Nance, [1:141/209] + BYTE March'93 } + + +Procedure EncryptPasswordDifference(objId:Longint; + OldPassword,NewPassword:string; + Var key:TencryptionKey; + Var PWdif:TencrPWdifference; + Var PWdifChecksum:byte + ); + + +Function LoNibble(b:Byte):Byte; +{ Returns the low nibble of the argument (in low nibble position), + with high nibble set to 0000 } +Function HiNibble(b:Byte):Byte; +{ Returns the high nibble of the argument (in low nibble position), + with high nibble set to 0000 } + + +Function Lswap(l:Longint):Longint; +{ swaps bytes in a longInt; ( reverse byte order ) } +Inline( + $5A/ {pop DX ; low word of long } + $58/ {pop AX ; hi word of long } + + $86/$F2/ {xchg dl,dh ; swap bytes } + $86/$E0); {xchg al,ah ; swap bytes } + +function HiLong(Long : LongInt) : Word; +{ This inline directive is similar to Turbo's Hi() function, except } +{ it returns the high word of a LongInt } +Inline( + $5A/ {pop dx ; low word of long } + $58); {pop ax ; hi word of long } + +function LowLong(Long : LongInt) : Word; +{ This inline directive is similar to Turbo's Lo() function, except } +{ it returns the Low word of a LongInt } +Inline( + $5A/ {pop dx ; low word of long } + $58/ {pop ax ; hi word of long } + $89/$D0); {mov ax,dx ; return lo word as func. result in Ax } + +function MakeLong(HiWord,LoWord : Word) : LongInt; +{ Takes hi and lo words and makes a longint } +Inline( + $58/ { pop ax ; pop low word into AX } + $5A); { pop dx ; pop high word into DX } + + +CONST +{** ERRORS DEFINED BY NWxxx UNITS *******} + +{** STANDARD ERRORS AS USED BY NETWARE **} + HARDWARE_FAILURE = 255; + INVALID_INITIAL_SEMAPHORE_VALUE = 255; {nwSema} + INVALID_SEMAPHORE_HANDLE = 255; {nwSema} + BAD_PRINTER_ERROR = 255; + QUEUE_FULL_ERROR = 255; + NO_FILES_FOUND_ERROR = 255; + BAD_RECORD_OFFSET = 255; + PATH_NOT_LOCATABLE = 255; + SOCKET_ALREADY_OPEN = 255; + INVALID_DRIVE_NUMBER = 255; {nwDir} + NO_RECORD_FOUND = 255; + NO_RESPONSE_FROM_SERVER = 255; + REQUEST_NOT_OUTSTANDING = 255; + NO_SUCH_OBJECT_OR_BAD_PASSWORD = 255; + CLOSE_FCB_ERROR = 255; + FILE_EXTENSION_ERROR = 255; + FILE_NAME_ERROR = 255; + IO_BOUND_ERROR = 255; + SPX_IS_INSTALLED = 255; {nwIpx} + SPX_SOCKET_NOT_OPENED = 255; {nwIpx} + EXPLICIT_TRANSACTION_ACTIVE = 255; {nwTTS} + NO_EXPLICIT_TRANSACTION_ACTIVE = 255; {nwTTS} + TRANSACTION_NOT_YET_WRITTEN = 255; {nwTTS} + NO_MORE_MATCHING_FILES = 255; {nwTTS} + BINDERY_FAILURE = 255; + OPEN_FILES = 255; {3.x} + PRINT_JOB_ALREADY_QUEUED = 255; {3.x} + PRINT_JOB_ALREADY_SET = 255; {3.x} + SUPERVISOR_HAS_DISABLED_LOGIN = 254; {nwConn} + TIMEOUT_FAILURE = 254; + BINDERY_LOCKED = 254; {nwBindry} + SERVER_BINDERY_LOCKED = 254; + INVALID_SEMAPHORE_NAME_LENGTH = 254; {nwSema} + PACKET_NOT_DELIVERABLE = 254; + SOCKET_TABLE_FULL = 254; + DIRECTORY_LOCKED = 254; + SPOOL_DIRECTORY_ERROR = 254; + IMPLICIT_TRANSACTION_ACTIVE = 254; {nwTTS} + TRANSACTION_ENDS_RECORD_LOCK = 254; {nwTTS} + IO_FAILURE = 254; {3.x} + UNKNOWN_REQUEST = 253; + INVALID_PACKET_LENGTH = 253; + FIELD_ALREADY_LOCKED = 253; + BAD_STATION_NUMBER = 253; + SPX_MALFORMED_PACKET = 253; + SPX_PACKET_OVERFLOW = 253; + TTS_DISABLED = 253; + NO_SUCH_OBJECT = 252; + UNKNOWN_FILE_SERVER = 252; + INTERNET_PACKET_REQT_CANCELED = 252; + MESSAGE_QUEUE_FULL = 252; {nwMess} + SPX_LISTEN_CANCELED = 252; + NO_SUCH_PROPERTY = 251; + INVALID_PARAMETERS = 251; + {UNKNOWN_REQUEST = 251; ?double see 253} + NO_MORE_SERVER_SLOTS = 250; + TEMP_REMAP_ERROR = 250; + NO_PROPERTY_READ_PRIVILEGE = 249; + NO_FREE_CONNECTION_SLOTS = 249; + NO_PROPERTY_WRITE_PRIVILEGE = 248; + ALREADY_ATTACHED_TO_SERVER = 248; + NOT_ATTACHED_TO_SERVER = 248; + NO_PROPERTY_CREATE_PRIVILEGE = 247; + TARGET_DRIVE_NOT_LOCAL = 247; + NO_PROPERTY_DELETE_PRIVILEGE = 246; + NOT_SAME_LOCAL_DRIVE = 246; + NO_OBJECT_CREATE_PRIVILEGE = 245; + NO_OBJECT_DELETE_PRIVILEGE = 244; + NO_OBJECT_RENAME_PRIVILEGE = 243; + NO_OBJECT_READ_PRIVILEGE = 242; + INVALID_BINDERY_SECURITY = 241; + WILD_CARD_NOT_ALLOWED = 240; + IPX_NOT_INSTALLED = 240; {nwIpx} + INVALID_NAME = 239; + SPX_CONNECTION_TABLE_FULL = 239; + OBJECT_ALREADY_EXISTS = 238; + SPX_INVALID_CONNECTION = 238; + PROPERTY_ALREADY_EXISTS = 237; + SPX_NO_ANSWER_FROM_TARGET = 237; + SPX_CONNECTION_FAILED = 237; + SPX_CONNECTION_TERMINATED = 237; + NO_SUCH_SEGMENT = 236; + SPX_TERMINATED_POORLY = 236; + NOT_GROUP_PROPERTY = 235; + NO_SUCH_MEMBER = 234; + MEMBER_ALREADY_EXISTS = 233; + NOT_ITEM_PROPERTY = 232; + WRITE_PROPERTY_TO_GROUP = 232; + PASSWORD_HAS_EXPIRED = 223; + PASSWORD_HAS_EXPIRED_NO_GRACE = 222; + ACCOUNT_DISABLED = 220; + UNAUTHORIZED_LOGIN_STATION = 219; + MAX_Q_SERVERS = 219; + UNAUTHORIZED_LOGIN_TIME = 218; + Q_HALTED = 218; + LOGIN_DENIED_NO_CONNECTION = 217; + STN_NOT_SERVER = 217; + PASSWORD_TOO_SHORT = 216; + Q_NOT_ACTIVE = 216; + PASSWORD_NOT_UNIQUE = 215; + Q_SERVICING = 215; + NO_JOB_RIGHTS = 214; + NO_Q_JOB = 213; + Q_FULL = 212; + NO_Q_RIGHTS = 211; + NO_Q_SERVER = 210; + NO_QUEUE = 209; + Q_ERROR = 208; + NOT_CONSOLE_OPERATOR = 198; + INTRUDER_DETECTION_LOCK = 197; + ACCOUNT_TOO_MANY_HOLDS = 195; + CREDIT_LIMIT_EXCEEDED = 194; + NO_ACCOUNT_BALANCE = 193; + NO_ACCOUNT_PRIVILEGES = 192; + READ_FILE_WITH_RECORD_LOCKED = 162; + DIRECTORY_IO_ERROR = 161; + DIRECTORY_NOT_EMPTY = 160; + DIRECTORY_ACTIVE = 159; + INVALID_FILENAME = 158; + NO_MORE_DIRECTORY_HANDLES = 157; + NO_MORE_TRUSTEES = 156; + INVALID_PATH = 156; + BAD_DIRECTORY_HANDLE = 155; + RENAMING_ACROSS_VOLUMES = 154; + DIRECTORY_FULL = 153; + VOLUME_DOES_NOT_EXIST = 152; + NO_DISK_SPACE_FOR_SPOOL_FILE = 151; + SERVER_OUT_OF_MEMORY = 150; + OUT_OF_DYNAMIC_WORKSPACE = 150; + FILE_DETACHED = 149; + NO_WRITE_PRIVILEGES = 148; + READ_ONLY = 148; + NO_READ_PRIVILEGES = 147; + NO_FILES_RENAMED_NAME_EXISTS = 146; + SOME_FILES_RENAMED_NAME_EXISTS = 145; + NO_FILES_AFFECTED_READ_ONLY = 144; + SOME_FILES_AFFECTED_READ_ONLY = 143; + NO_FILES_AFFECTED_IN_USE = 142; + SOME_FILES_AFFECTED_IN_USE = 141; + NO_MODIFY_PRIVILEGES = 140; + NO_RENAME_PRIVILEGES = 139; + NO_DELETE_PRIVILEGES = 138; + NO_SEARCH_PRIVILEGES = 137; + INVALID_FILE_HANDLE = 136; + WILD_CARDS_IN_CREATE_FILENAME = 135; + CREATE_FILE_EXISTS_READ_ONLY = 134; + NO_CREATE_DELETE_PRIVILEGES = 133; + NO_CREATE_PRIVILEGES = 132; + IO_ERROR_NETWORK_DISK = 131; + NO_OPEN_PRIVILEGES = 130; + NO_MORE_FILE_HANDLES = 129; + FILE_IN_USE_ERROR = 128; + DOS_LOCK_VIOLATION = 33; + DOS_SHARING_VIOLATION = 32; + DOS_NO_MORE_FILES = 31; + DOS_NOT_SAME_DEVICE = 30; + DOS_ATTEMPT_TO_DEL_CURRENT_DIR = 16; + DOS_INVALID_DRIVE = 15; + DOS_INVALID_DATA = 13; + DOS_INVALID_ACCESS_CODE = 12; + DOS_INVALID_FORMAT = 11; + DOS_INVALID_ENVIRONMENT = 10; + DOS_INVALID_MEMORY_BLOCK_ADDR = 9; + DOS_INSUFFICIENT_MEMORY = 8; + DOS_MEMORY_BLOCKS_DESTROYED = 7; + DOS_INVALID_FILE_HANDLE = 6; + DOS_ACCESS_DENIED = 5; + DOS_TOO_MANY_OPEN_FILES = 4; + DOS_PATH_NOT_FOUND = 3; + DOS_FILE_NOT_FOUND = 2; + TTS_AVAILABLE = 1; + SERVER_IN_USE = 1; + SEMAPHORE_OVERFLOW = 1; + DOS_INVALID_FUNCTION_NUMBER = 1; + TTS_NOT_AVAILABLE = 1; + SERVER_NOT_IN_USE = 1; + +IMPLEMENTATION{=============================================================} + + + +{----------------------- Encryption tables and procedures --------------} + +TYPE + Buf32 = ARRAY [0..31] OF Byte; + Buf16 = ARRAY [0..15] OF Byte; + Buf8 = ARRAY [0..7] OF Byte; + Buf4 = ARRAY [0..3] OF Byte; + + +CONST + EncryptTable : ARRAY [0..255] OF Byte = +($7,$8,$0,$8,$6,$4,$E,$4,$5,$C,$1,$7,$B,$F,$A,$8, + $F,$8,$C,$C,$9,$4,$1,$E,$4,$6,$2,$4,$0,$A,$B,$9, + $2,$F,$B,$1,$D,$2,$1,$9,$5,$E,$7,$0,$0,$2,$6,$6, + $0,$7,$3,$8,$2,$9,$3,$F,$7,$F,$C,$F,$6,$4,$A,$0, + $2,$3,$A,$B,$D,$8,$3,$A,$1,$7,$C,$F,$1,$8,$9,$D, + $9,$1,$9,$4,$E,$4,$C,$5,$5,$C,$8,$B,$2,$3,$9,$E, + $7,$7,$6,$9,$E,$F,$C,$8,$D,$1,$A,$6,$E,$D,$0,$7, + $7,$A,$0,$1,$F,$5,$4,$B,$7,$B,$E,$C,$9,$5,$D,$1, + $B,$D,$1,$3,$5,$D,$E,$6,$3,$0,$B,$B,$F,$3,$6,$4, + $9,$D,$A,$3,$1,$4,$9,$4,$8,$3,$B,$E,$5,$0,$5,$2, + $C,$B,$D,$5,$D,$5,$D,$2,$D,$9,$A,$C,$A,$0,$B,$3, + $5,$3,$6,$9,$5,$1,$E,$E,$0,$E,$8,$2,$D,$2,$2,$0, + $4,$F,$8,$5,$9,$6,$8,$6,$B,$A,$B,$F,$0,$7,$2,$8, + $C,$7,$3,$A,$1,$4,$2,$5,$F,$7,$A,$C,$E,$5,$9,$3, + $E,$7,$1,$2,$E,$1,$F,$4,$A,$6,$C,$6,$F,$4,$3,$0, + $C,$0,$3,$6,$F,$8,$7,$B,$2,$D,$C,$6,$A,$A,$8,$D); + + EncryptKeys : Array[0..31] of byte = +($48,$93,$46,$67,$98,$3D,$E6,$8D,$B7,$10,$7A,$26,$5A,$B9,$B1,$35, + $6B,$0F,$D5,$70,$AE,$FB,$AD,$11,$F4,$47,$DC,$A7,$EC,$CF,$50,$C0); + + EncryptTable1:array [0..7,0..1,0..15] OF byte= { used by encrypt3 } + { 0 1 2 3 4 5 6 7 8 9 a b c d e f } + ((( $F,$8,$5,$7,$C,$2,$E,$9,$0,$1,$6,$D,$3,$4,$B,$A), + ( $2,$C,$E,$6,$F,$0,$1,$8,$D,$3,$A,$4,$9,$B,$5,$7)), + (( $5,$2,$9,$F,$C,$4,$D,$0,$E,$A,$6,$8,$B,$1,$3,$7), + ( $F,$D,$2,$6,$7,$8,$5,$9,$0,$4,$C,$3,$1,$A,$B,$E)), + (( $5,$E,$2,$B,$D,$A,$7,$0,$8,$6,$4,$1,$F,$C,$3,$9), + ( $8,$2,$F,$A,$5,$9,$6,$C,$0,$B,$1,$D,$7,$3,$4,$E)), + (( $E,$8,$0,$9,$4,$B,$2,$7,$C,$3,$A,$5,$D,$1,$6,$F), + ( $1,$4,$8,$A,$D,$B,$7,$E,$5,$F,$3,$9,$0,$2,$6,$C)), + (( $5,$3,$C,$8,$B,$2,$E,$A,$4,$1,$D,$0,$6,$7,$F,$9), + ( $6,$0,$B,$E,$D,$4,$C,$F,$7,$2,$8,$A,$1,$5,$3,$9)), + (( $B,$5,$A,$E,$F,$1,$C,$0,$6,$4,$2,$9,$3,$D,$7,$8), + ( $7,$2,$A,$0,$E,$8,$F,$4,$C,$B,$9,$1,$5,$D,$3,$6)), + (( $7,$4,$F,$9,$5,$1,$C,$B,$0,$3,$8,$E,$2,$A,$6,$D), + ( $9,$4,$8,$0,$A,$3,$1,$C,$5,$F,$7,$2,$B,$E,$6,$D)), + (( $9,$5,$4,$7,$E,$8,$3,$1,$D,$B,$C,$2,$0,$F,$6,$A), + ( $9,$A,$B,$D,$5,$3,$F,$0,$1,$C,$8,$7,$6,$4,$E,$2)) + ); + + EncryptTable3:array[0..15] of byte= { used by encrypt3 } + ( $3,$E,$F,$2,$D,$C,$4,$5,$9,$6,$0,$1,$B,$7,$A,$8 ); + + +PROCEDURE Shuffle(VAR ShuffleKey, buf; buflen : Word; VAR target); +{ UNIT INTERNAL PROCEDURE } +{ id, password[1.. ],length(passw), OUT: buf } + + PROCEDURE Shuffle1(VAR temp : Buf32; VAR target); + VAR _target : Buf16 ABSOLUTE target; + b4 : Word; + b3 : Byte; + d, k, i : Word; + Begin + + {** Step 4: .. } + b4 := 0; + FOR k := 0 TO 1 + DO Begin + FOR i := 0 TO 31 + DO Begin + b3 := Lo( Lo(temp[i] + b4) XOR + Lo(temp[(i + b4) AND 31] - EncryptKeys[i])); + b4 := b4 + b3; + temp[i] := b3; + End; + End; + + {*** Step 5:... } + + FOR i := 0 TO 15 + DO _Target[i] := EncryptTable[temp[i Shl 1]] OR + (EncryptTable[temp[i Shl 1 +1]] Shl 4); + End; + +VAR locShuffleKey : Buf4 ABSOLUTE ShuffleKey; + localBuf : ARRAY [0..127] OF Byte ABSOLUTE buf; + BufBytesUsed : Word; + temp : Buf32; + t, IndexOfBufBytes : Word; +Begin +{ strip trailing NULLs of the to-be-encoded buf, + last element of buf must be a NULL ? } +While (buflen > 0) AND (localBuf[buflen-1] = 0) + DO buflen := buflen - 1; +{ clear output of 1st shuffle } +FillChar(temp, SizeOf(temp), #0); + +{*** 1ST Step: XOR folding of first (32*(buflen DIV 32)) bytes. } + +{ temp= buf[0..31] XOR buf[32..63] XOR buf[64..95] XOR etc.. } + +{ IndexOfBufBytes is a multiple of 32, length password= IndexOfBufBytes + buflen } +{ Temp varuable filled with XOR folding of the first IndexOfBufBytes bytes of the PW. } +IndexOfBufBytes := 0; +WHILE buflen >= 32 + DO Begin + FOR t := 0 TO 31 + DO Begin + temp[t] := temp[t] XOR localBuf[IndexOfBufBytes]; + IndexOfBufBytes := IndexOfBufBytes + 1; + End; + buflen := buflen - 32; + End; + +{*** 2ND step: repetitive XOR folding with (remainder of) password + + password='hello', (BufBytesUsed=0) + or password='12345678901234567890123456789012hello' (BufBytesUsed=32) + of which the first 32 bytes were used in the 1st encryption step. + + temp=temp XOR [hellohellohellohellohellohellohe]; + } +BufBytesUsed:=IndexOfBufBytes; +IF buflen > 0 + Then Begin + FOR t := 0 TO 31 + DO Begin + IF IndexOfBufBytes + buflen = BufBytesUsed + Then Begin + BufBytesUsed := IndexOfBufBytes; + temp[t] := temp[t] XOR EncryptKeys[t]; + End + Else Begin + temp[t] := temp[t] XOR localBuf[BufBytesUsed]; + BufBytesUsed := BufBytesUsed + 1; + End; + End; + End; +{*** 3RD step: XOR-ing with shuffleKey (bytes of a longint)} + +FOR t := 0 TO 31 DO temp[t] := temp[t] XOR locShuffleKey[t AND 3]; + +{*** 4&5 TH Step: see Shuffle1 } + +Shuffle1(temp, target); +End; + +PROCEDURE Encrypt(VAR key, buf, EncrPassword); +{ The encryptionKey 'key' is encrypted with the aid of +the shuffled login name/id within 'buf'. +Result: the encrypted Password (of type TencryptionKey). } +VAR _Key : TencryptionKey ABSOLUTE Key; + _EncrKey : TencryptionKey ABSOLUTE EncrPassword; + _LocalBuf : Buf32; + i: Byte; +Begin +Shuffle(_Key[0], buf, 16, _LocalBuf[0]); +Shuffle(_Key[4], buf, 16, _LocalBuf[16]); +FOR i := 0 TO 15 DO _LocalBuf[i] := _LocalBuf[i] XOR _LocalBuf[31-i]; +FOR i := 0 TO 7 DO _EncrKey[i] := _LocalBuf[i] XOR _LocalBuf[15-i]; +End; + +Procedure EncryptPassword(objId:longint;password:string;Var Ekey:TencryptionKey); +{ Source of the encryption routine: LOGON.PAS by Barry Nance, [1:141/209] BYTE March'93 } +{ Two bugs fixed by Horst Jelonneck (930323) } +Var buf:buf32; + TobjId:Longint; + Tpassword:string; + +begin +TobjId:=Lswap(objId); +Tpassword:=password+#0; +Shuffle(TObjId,Tpassword[1],length(password),buf); +Encrypt(Ekey,buf,Ekey); +end; + +Procedure EncryptPasswordDifference(objId:Longint; + OldPassword,NewPassword:string; + Var key:TencryptionKey; + Var PWdif:TencrPWdifference; + Var PWdifChecksum:byte + ); +{ Used by nwBindry.ChangeEncrBinderyObjectPassword. + + Encrypt3 and associated tables adapted from a c source (NVPW.C) + by Willem Jan Hengeveld, A.K.A. Itsme@Hacktic.nl } + Procedure Encrypt3(Var buf1,buf2,buf3); + { buf1: (part of) encrypted oldPW + buf2: (part of) encrypted newPW + buf3: 'change pw' data (result) } + Var p1:buf8 absolute buf1; + p2:buf8 absolute buf2; + p3:buf8 absolute buf3; + j,c,i:byte; + buf:buf8; + begin + buf:=p2; + for i:=0 to 15 + do begin + + for j:=0 to 7 + do begin + c:=buf[j] XOR p1[j]; + buf[j]:=EncryptTable1[j][0][c AND $0F] + OR (EncryptTable1[j][1][c SHR 4] SHL 4); + end; + + c:=p1[7]; + for j:=7 downto 1 + do p1[j]:=(p1[j] SHL 4) OR (p1[j-1] SHR 4); + p1[0]:=(c SHR 4) OR (p1[0] SHL 4); + + FillChar(p3,8,#$0); + for j:=0 to 15 + do begin + c:=EncryptTable3[j]; + If odd(EncryptTable3[j]) + then c:=buf[c DIV 2] SHR 4 + else c:=buf[c DIV 2] AND $0F; + if Odd(j) + then p3[j DIV 2]:=p3[j DIV 2] XOR (c SHL 4) + else p3[j DIV 2]:=p3[j DIV 2] XOR c; + end; + + buf:=p3; + end; + end; +Var l:byte; + OldShuffledPW,NewShuffledPW:array[0..15] of byte; +begin +objId:=Lswap(objId); +Shuffle(objId,OldPassword[1],Length(OldPassword),OldShuffledPW); +Shuffle(objId,NewPassword[1],Length(NewPassword),NewShuffledPW); +Encrypt(key,OldShuffledPW,key); + +Encrypt3(OldShuffledPW,NewShuffledPW,PWdif); +Encrypt3(OldShuffledPW[8],NewShuffledPW[8],PWdif[8]); +if Length(NewPassword)<63 + then l:=length(NewPassword) + else l:=63; +PWdifChecksum:=(((l XOR OldShuffledPW[1] XOR OldShuffledPW[2]) AND $7F) OR $40); +end; + + +{-------------- End of encryption procedures ----------------------------} + +Procedure UpString(s : String); Assembler; +{ fast upcasestring by Bob Swart } +ASM + PUSH DS + LDS SI, s + LES DI, s + CLD + XOR AH, AH + LODSB + STOSB + XCHG AX, CX { empty string? } + JCXZ @2 +@1: LODSB + SUB AL, 'a' + CMP AL, 'z'-'a'+1 + SBB AH, AH + AND AH, 'a'-'A' + SUB AL, AH + ADD AL, 'a' + STOSB + LOOP @1 +@2: POP DS +end; + + +procedure ZStrCopy(dest:String;Var source;len:byte); assembler; +{ 1. Copies len bytes from an array to a pascal type string. } +{ 2. Trailing NULLs are removed from the string. } +{ consequently, the length of det (dest[0]) will allways be <= len. } +asm + mov dx,ds + + les di,dest + xor ch,ch + mov [es:di],ch { dest[0]:=#0 } + mov cl,len + jcxz @4 { if len=0 then goto @4 } + lds si,source +@3: lodsb + or al,al + jz @2 { determine non-0 length of source } + dec cx + jnz @3 +@2: xor ax,ax + mov al,len + sub ax,cx { ax:= bytes to copy } + + les di,dest + lds si,source + mov es:[di],al { dest[0]:=actual non-0 len } + inc di { es:di => dest[1] ; ds:si => source[0] } + + mov cx,ax + cld + rep movsb { copy cx bytes from ds:si to es:di } + +@4: mov ds,dx +end; + +procedure PStrCopy(Var dest:String;source:String;len:byte); +Var w:byte; +begin +w:=1; +dest[0]:=chr(len); +While w<=ord(source[0]) + do begin + dest[w]:=source[w]; + inc(w) + end; +While w<=len + do begin + dest[w]:=#0; + inc(w) + end; +end; + +Procedure NovTime2String(tim:TnovTime;Var DateStr:string); +CONST day:array[0..6] of string[3] + =('Sun','Mon','Tue','Wed','Thu','Fri','Sat'); + Month:array[1..12] of string[3] + =('Jan','Feb','Mar','Apr','May','Jun', + 'Jul','Aug','Sep','Oct','Nov','Dec'); +Type string4=string[4]; +Var sday,syear,shour,smin,ssec:string4; + Procedure zstr(n:byte;Var s:string4); + begin + str(n,s); + if s[0]=#1 then s:='0'+s; + end; +begin +if (tim.month>12) or (tim.month<1) + or (tim.day<1) or (tim.day>31) + or (tim.hour>23) or (tim.min>59) or (tim.sec>59) + then DateStr:=' ' + else begin + zstr(tim.day,sday); + if sday[1]='0' then sday[1]:=' '; + if tim.year<80 then str(tim.year+2000,syear) + else str(tim.year+1900,syear); + zstr(tim.hour,shour); + zstr(tim.min,smin); + zstr(tim.sec,ssec); + DateStr:=day[tim.DayOfWeek]+', '+ + sday+' '+Month[tim.month]+' '+syear+' '+ + shour+':'+smin+':'+ssec; + end; +end; + +Procedure DosTime2NovTime(dt:Longint;Var nt:TnovTime); +Var k:array[1..2] of word absolute dt; +begin +with nt + do begin + year:=(80+byte(k[2] SHR 9)) MOD 100; + month:=(byte(k[2] SHR 5) AND 15); + day:=byte(k[2] AND 31); + + hour:=byte (k[1] SHR 11); + min:=(byte(k[1] SHR 5) AND 63); + sec:=2*byte(k[1] AND 31); + end; +end; + + +Procedure NovTime2DosTime(nt:TnovTime;Var dt:Longint); +Var k:array[1..2] of word absolute dt; +begin +with nt + do begin + k[2]:=(((100+year-80) mod 100) SHL 9)+(month SHL 5)+day; + k[1]:=(hour SHL 11)+(min SHL 5)+(sec DIV 2); + end; +end; + +Procedure NovPath2DosPath(np:String;Var dp:string); +{ np is a pascal type string with the folowing format: + + chr(length(subdir1)),subdir1, + ... + chr(length(subdirN)),subDirN. + + It will be transformed to a DOS path of type Subdir1\Subdir2\.. \subDirN } +Var t:Byte; +begin +dp:=np; +delete(dp,1,1); +for t:=1 to ord(dp[1]) + do if dp[t]<=#20 then dp[t]:='\'; +end; + + +Function LoNibble(b:Byte):Byte; assembler; +asm +mov al,b +and al,$0F +end; + +Function HiNibble(b:Byte):Byte; assembler; +asm +mov ah,$00 +mov al,b +shr ax,1 +shr ax,1 +shr ax,1 +shr ax,1 +end; + +Function HexStr(dw:LongInt;len:byte):string; +CONST n:array[0..15] of char + =('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'); +Var t:integer; + ldw:LongInt; + res:string; +begin +res:=''; +for t:=1 to len +do begin + ldw:=dw AND $0000000F; + res:=n[ldw]+res; + dw:=dw SHR 4; + end; +HexStr:=res; +end; + +Function HexDumpStr(Var dumpVar;len:Byte):String; +Var arr:Array[1..256] of Byte ABSOLUTE dumpVar; + t:Byte; + res:String; +begin +res:=''; +for t:=1 to (1+(len DIV 2)) + do res:=res+HexStr(arr[t],2); +res[0]:=chr(len); +HexDumpStr:=res; +end; + +Function IsLowerNetworkAddress(Var a, b): Boolean; +Type TCharNetAddress = Array [1..10] of Char; +Var Aaddr : TCharNetAddress ABSOLUTE a; + Baddr : TCharNetAddress ABSOLUTE b; +Begin + IsLowerNetworkAddress := Aaddr < Baddr; +End; + +Function IsEqualNetworkAddress(Var a, b): Boolean; +Type TCharNetAddress = Array [1..10] of Char; +Var Aaddr : TCharNetAddress ABSOLUTE a; + Baddr : TCharNetAddress ABSOLUTE b; +Begin + IsEqualNetworkAddress := (Aaddr = Baddr); +End; + +Function IsLaterNovTime(time1,time2:TnovTime):boolean; +Var bAft:boolean; + y1,y2:word; +begin +if time1.year>=80 + then y1:=1900+time1.year + else y1:=2000+time1.year; +if time2.year>=80 + then y2:=1900+time2.year + else y2:=2000+time2.year; +bAft:=(y1>y2); +if y1=y2 + then begin + bAft:=(time1.month>time2.month); + if time1.month=time2.month + then begin + bAft:=(time1.day>time2.day); + if time1.day=time2.day + then begin + bAft:=(time1.hour>time2.hour); + if time1.hour=time2.hour + then begin + bAft:=(time1.min>time2.min); + if time1.min=time2.min + then bAft:=(time1.sec>time2.sec); + end; + end; + end; + end; +IsLaterNovTime:=bAft +end; + + +Function IsEqualNovTime(time1,time2:TnovTime):boolean; +Var t1:array[1..Sizeof(TnovTime)-1] of char absolute time1; + t2:array[1..SizeOf(TnovTime)-1] of char absolute time2; +begin +IsEqualNovTime:=(t1=t2); +end; + + +Function MapV2RightsToV3(V2Rights:byte):word; +CONST RightsNotChanged:byte=$08+$10+$40+$80; +Var result1:Word; +begin +if (V2Rights and $FF)>0 + then result1:=$1FF + else begin + result1:=(V2Rights and RightsNotChanged); + if (V2Rights and ($01+$04))>0 + then result1:=result1 or $01; + if (V2Rights and ($02+$04))>0 + then result1:=result1 or $02; + if (V2Rights and $04)>0 + then result1:=result1 or $01; + if (V2Rights and $20)>0 + then result1:=result1 or $28; + end; +MapV2RightsToV3:=result1; +end; + +Function MapV3RightsToV2(V3Rights:Word):Byte; +CONST RightsNotChanged:word=$10+$20+$40+$80; +Var result1:Byte; +begin +If (V3Rights and $0100)>0 + then result1:=$FF + else begin + result1:=(lo(V3Rights) and RightsNotChanged); + If (V3Rights and $01)>0 + then result1:=result1 or $05; + If (V3Rights and $02)>0 + then result1:=result1 or $06; + {If (V3Rights and $04)>0 + then result:=result or $00;} + If (V3Rights and $08)>0 + then result1:=result1 or $28; + end; +MapV3RightsToV2:=result1; +end; + +Procedure GetNWversion(Var version:word); +{ determine the version of the software installed on the current file server. } +{ see GetFileServerInformation F217/11 in the nwServ unit for more information } + +{ version : word; contains the versionnumber of the fileserver we're + currently connected to. Used by primary functions to + determine what type of calls to use to perform a certain function. + + format: (majorVersion*100)+minorVersion + e.g. 311 for 3.11 + Range: 215 (advanced netware 2.15) and upwards } +{ If the version is lower than 2.15, 2.15 is returned. } +{ note: you don't have to be logged in to call this function. } +Type TReq= Record + PacketLength : Word; + FunctionVal : Byte; + End; + TRep=array[1..$80] of byte; + Tpreq=^Treq; + Tprep=^Trep; +Var Result:word; +Begin +With TPreq(GlobalReqBuf)^ +Do Begin + PacketLength := 1; FunctionVal := $11; + End; +F2SystemCall($17,sizeof(Treq),Sizeof(Trep),result); +If result=0 + then version:=(TPrep(GlobalReplyBuf)^[49]*100)+TPrep(GlobalReplyBuf)^[50] + else version:=215; +End; + + +Function IsV3Supported:boolean; +Var version:word; +begin +GetNWversion(version); +IsV3Supported:=(version>=300); +end; + + +END. diff --git a/SRC/UNITS/NWQMS.PAS b/SRC/UNITS/NWQMS.PAS new file mode 100644 index 0000000..c2a2d25 --- /dev/null +++ b/SRC/UNITS/NWQMS.PAS @@ -0,0 +1,1149 @@ +{$X+,B-,V-} {essential compiler directives} + +UNIT nwQMS; + +{ nwQMS unit as of 950301 / NwTP 0.6 API. (c) 1993,1995, R. Spronk } +{ Based in part on a unit containing queue services routines, written by + Erik van Heyningen in April 1994. } + +INTERFACE + +Uses nwMisc; + +{ Function: Interrupt: Comments: + + Queue Server Functions: + +. AbortServicingQueueJob (F217/73) (S) +. AttachQueueServerToQueue (F217/6F) (S) +. ChangeToClientRights (F217/74) (S) +. DetachQueueServerFromQueue (F217/70) (S) +. FinishServicingQueueJob (F217/72) (S) +. RestoreQueueServerRights (F217/75) (S) +. ServiceQueueJob (F217/71) (S) +. SetQueueServerStatus (F217/77) (S) + + Queue Operator Functions: + +. ChangeQueueJobPosition (F217/6E) (O) +* CreateQueue (F217/64) (SUP) +* DestroyQueue (F217/65) (SUP) +. SetQueueStatus (F217/67) (O) + + Queue User Functions: + +. CreateQueueJob (F217/68) (C) +. StartQueueJob (F217/69) (C) (1) + + Miscellaneous Queue Functions: + +. ChangeQueueJobEntry (F217/6D) (C-O) +* GetQueueJobList (F217/6B) (C-O) +* GetQueueJobsFileSize (F217/78) (C-O-S) +* ReadQueueStatus (F217/66) (C-O) +* ReadQueueJobEntry (F217/6C) (C-O-S) +. ReadQueueServerStatus (F217/76) (C-O) +* RemoveJobFromQueue (F217/6A) (C-O) + + +Notes: C : Function available to Clients (Queue Users); + S : Function available to Servers (Queue Servers); + O : Functions availaible to Operators (Queue Operators); + SUP: Functions available to Supervisors/Workgroup managers. + + (1): StartQueueJob is a.k.a. CloseFileAndStartQueueJob +} + +CONST { Queue status flag } + QS_ALL_OK = $00; + QS_CANT_ADD_JOBS = $01; { possibility to add jobs disabled by operator } + QS_SERVERS_CANT_ATTACH = $02; { attachment of servers to queue disabled by operator } + QS_CANT_SERVICE_JOBS = $04; { queue halted by operator } + { QS_XXXX constants can be ORed to form a QstatusFlag } + + QF_NONE = $00; + QF_AUTO_START = $08; + QF_SERVICE_RESTART = $10; + QF_ENTRY_OPEN = $20; + QF_USER_HOLD = $40; + QF_OPERATOR_HOLD = $80; + +CONST MaxQueueJobs = 250; + +Type TQueueStatus= RECORD + ObjectId : Longint; { Object id of queue } + Status : Byte; { status of queue QS_XXX } + NbrOfJobs : Byte; { Number of jobs in queue } + NbrOfServers : Byte; { Number of servers attached to queue } + ServerObjectIds : array[1..25] of Longint; + { List of Objects-ids of attached servers } + ServerConnNbrs : array[1..25] of Byte; + { List of attached server stations } + MaxNbrOfServers : Byte; { ??? } + end; + + TQueueServerStatus= Array[1..64] of Byte; + { undefined structure -as far as QMS is concerned-} + +Type TJobNumberList = Array[1..MaxQueueJobs] OF Word; + TQueueJobList = record + JobCount : Word; + JobNbrs : TJobNumberList; { List of jobs numbers by position in queue } + MaxJobs : Word; {????} { Maximum job numbers } + end; + + TJobFileHandle=Array[1..6] of Byte; + TQueueJobEntry =Record { Unit external Type } + ClientConnNbr : Byte; + ClientTaskNbr : Byte; + ClientObjectID : Longint; + JobEntryTime : TnovTime; + JobNumber : Word; + JobFileName : String[14]; + JobFileHandle : TjobFileHandle; + + TargetServerIDnumber : Longint; {2} + TargetExecutionTime : TnovTime; {2} + JobType : Word; {2} + JobControlFlags : Byte; {2} + JobDescription : String[50]; {2} + ClientRecordArea : Array[1..152] OF Byte; {2} + + JobPosition : Byte; {2/operators only} + + ServerConnNbr, {1} + ServerTaskNbr : Byte; {1} + ServerObjectID : Longint; {1} + End; + { 1: Filled by Queue server. As long as ServerTaskNbr=0, + queue entry is not being serviced. + 2: Can be changed by queue operators and/or the 'owner' of + the job after job has been placed in queue + } + +Var result:Word; + +{F217/64 {2.1x+} +Function CreateQueue(Qname :string; QobjectType:Word; + dirHandle :Byte; pathName :string; + VAR QobjID:Longint ):Boolean; +{ Creates an object of an object_queue_type in the bindery, checks that + all settings are valid before creating. Returns the object_id of the + created queue if creation was successfull. } + +{F217/65 [2.1x+]} +Function DestroyQueue(QobjID:Longint):Boolean; +{ Destroys the specified Queue; aborts all jobs in the queue; + associated files/directories are deleted; + queue object is removed from the bindery. } + + +{F217/76 [2.1x/3.x]} +Function ReadQueueStatus(QobjID:Longint; + Var Qstatus:TQueueStatus):Boolean; +{ Read the status of a queue. This information is changed by queueservers.} + +{F217/67 [2.1x+]} +Function SetQueueStatus(QobjId:Longint; NewQstatusFlag:Byte):Boolean; +{ Change the queue status flag. Use the QS_XXXX constants } + +{F217/6B} +FUNCTION GetQueueJobList( QueueObjId: Longint; + Var QJobList:TQueueJobList): Boolean; +{ You need to be either a Q_USER or a Q_OPERATOR } + +{F217/6C} +FUNCTION ReadQueueJobEntry( QObjId: Longint;JobNbr: Word; + VAR QJob: TQueueJobEntry): Boolean; +{ You need to be either a Q_USER, Q_OPERATOR or a Q_SERVER } + +{F217/6A} +FUNCTION RemoveJobFromQueue( QObjId: Longint; JobNbr: Word): Boolean; +{ You need to be Q_OPERATOR or the Q_USER who queued the job } + +{F217/69 [2.1x+]} +Function StartQueueJob(QobjId:Longint;JobNbr:Word):Boolean; + + +{F217/6E [2.1x+]} +Function ChangeQueueJobPosition(QobjId:Longint; JobNbr:Word; + NewJobPos:Byte ):Boolean; +{ Q_OPERATOR only } + + +{F217/6F [2.1x+]} +Function AttachQueueServerToQueue(QobjId:Longint):Boolean; +{ Q_SERVERs only } + + +{F217/70 [2.1x+]} +Function DetachQueueServerFromQueue(QobjId:Longint):Boolean; +{ Q_SERVERs only } + + +{F217/71 [2.1x+]} +Function ServiceQueueJob(QobjID:Longint; JobType:Word; + Var QjobEntry:TQueueJobEntry):Boolean; +{ Q_SERVERs only } + +{F217/72 [2.1x+]} +Function FinishServicingQueueJob(QobjId:Longint;JobNbr:Word; + Charge:Longint ):Boolean; +{ Q_SERVERs only } + +{F217/73 [2.1x+]} +Function AbortServicingQueueJob(QobjId:Longint; JobNbr:Word):Boolean; + +{F217/74 [2.1x+]} +Function ChangeToClientRights(QobjId:Longint;JobNbr:Word):Boolean; +{ Q_SERVERs servicing job only } + +{F217/75 [2.1x+]} +Function RestoreQueueServerRights:Boolean; +{ Q_SERVERs, servicing job and having previously called + ChangeToClientRights only } + +{F217/76 [2.1x+]} +Function ReadQueueServerStatus(QobjId :Longint; + QserverObjId :Longint; + QserverConnNbr:Byte; + Var Qstatus:TQueueServerStatus):Boolean; + + +{F217/77 [2.1x+]} +Function SetQueueServerStatus(QobjId:Longint; Qstatus:TqueueServerStatus):Boolean; + +{F217/78 [2.1x+]} +Function GetQueueJobsFileSize(QobjId:Longint; JobNbr:Word; + Var JobSize:Longint ):Boolean; + +{F217/68 [2.1x+]} +Function CreateQueueJob(QobjId:Longint; + {i/o} Var Qjob:TqueueJobEntry):Boolean; + +{F217/6D [2.1x+]} +Function ChangeQueueJobEntry(QobjId:Longint;Qjob:TQueueJobEntry):Boolean; + +IMPLEMENTATION {============================================================} + +Uses nwIntr; + +Type TIntJobStruct =Record { Unit internal Type } + _ClientConnNbr, + _ClientTaskNbr : Byte; + _ClientObjectID, {hi-lo} + _TargetServerIDnumber : Longint; {hi-lo} + _TargetExecutionTime, + _JobEntryTime : Array[1..6] OF Byte; { YMDHMS } + _JobNumber, {hi-lo} + _JobType : Word; {hi-lo} + _JobPosition, + _JobControlFlags : Byte; + _JobFileName : Array[1..14] OF CHAR; { ASCIIZ } + _JobFileHandle : TJobFileHandle; + _ServerConnNbr, + _ServerTaskNbr : Byte; + _ServerObjectID : Longint; {hi-lo} + _JobDescription : Array[1..50] OF CHAR; { ASCIIZ } + _ClientRecordArea : Array[1..152] OF Byte + End; + +Procedure ConvertQJE2ext(qje:TintJobStruct;VAR ext:TQueueJobEntry; + Unrestricted:Boolean); +{convert the internal QueueJobEntry type into the equivalent + unit external type } +begin +With qje,ext + do begin + ClientConnNbr:=_ClientConnNbr; + ClientTaskNbr:=_ClientTaskNbr; + ClientObjectId:=Lswap(_ClientObjectId); + Move(_JobEntryTime,JobEntryTime,6); JobEntryTime.DayOfWeek:=0; + { # fix year for year 2000+ ? } + JobNumber:=swap(_JobNumber); + ZstrCopy(JobFileName,_JobFileName,14); + JobFileHandle:=_JobFileHandle; + TargetServerIdNumber:=Lswap(_TargetServerIdNumber); + Move(_TargetExecutionTime,TargetExecutionTime,6); TargetExecutionTime.DayOfWeek:=0; + { # fix year for year 2000+ ? } + JobType:=swap(_JobType); + JobControlFlags:=_JobControlFlags; + IF UnRestricted + then begin + ZstrCopy(JobDescription,_JobDescription,50); + Move(_ClientRecordArea,ClientRecordArea,152); + end; + JobPosition:=_JobPosition; + ServerConnNbr:=_ServerConnNbr; + ServerTaskNbr:=_ServerTaskNbr; + ServerObjectId:=Lswap(_ServerObjectId); + end; +end; + +Procedure ConvertQJE2int(qje:TQueueJobEntry;VAR int:TintJobStruct); +{convert the external QueueJobEntry type into the equivalent + unit internal type } +Var s:string[50]; +begin +With qje,int + do begin + _ClientConnNbr:=ClientConnNbr; + _ClientTaskNbr:=ClientTaskNbr; + _ClientObjectId:=Lswap(ClientObjectId); + _TargetServerIdNumber:=Lswap(TargetServerIdNumber); + Move(TargetExecutionTime,_TargetExecutionTime,6); + { # fix year for year 2000+ ? } + Move(JobEntryTime,_JobEntryTime,6); + { # fix year for year 2000+ ? } + _JobNumber:=swap(JobNumber); + _JobType:=swap(JobType); + _JobPosition:=JobPosition; + _JobControlFlags:=JobControlFlags; + PStrCopy(s,JobFilename,14);Move(s[1],_JobFileName,14); + _JobFileHandle:=JobFileHandle; + _ServerConnNbr:=ServerConnNbr; + _ServerTaskNbr:=ServerTaskNbr; + _ServerObjectId:=Lswap(ServerObjectId); + PstrCopy(s,JobDescription,50);Move(s[1],_JobDescription,50); + Move(ClientRecordArea,_ClientRecordArea,152); + end; +end; + + +{--- Initial Functions, create and destroy Job Queue --------------------} + +{F217/64 {2.1x+} +Function CreateQueue(Qname :string; QobjectType:Word; + dirHandle :Byte; pathName :string; + VAR QobjID:Longint ):Boolean; +{ Creates an object of an object_queue_type in the bindery, checks that + all settings are valid before creating. Returns the object_id of the + created queue if creation was successfull. } +{ QobjectType= OT_PRINT_QUEUE, OT_JOB_QUEUE, + OT_ARCHIVE_QUEUE, own obj.type >$8000 } +{ You need supervisor-equivalent or workgroup-manager rights to perform + this action. } +{ To add (remove) Queue operators or + (dis-)allow Queue servers to attach to a queue or + (dis-)allow objects (users/groups) to use a queue, + use the AddBinderyObjectToSet and DeleteBinderyObjectFromSet functions + in the nwBindry unit with the property names Q_OPERATORS, Q_SERVERS + and Q_USERS respectively. } +Type Treq=record + len :Word; + subFunc :Byte; + _Qtype :Word; { hi-lo} + _QdivData :array[1..168] of Byte; + end; + Trep=record + _Qid:Longint; {hi-lo} + end; + TPreq=^Treq; + TPrep=^Trep; +Var i:Byte; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$64; + _Qtype:=swap(QobjectType); { force hi-lo } + i:=ord(Qname[0])+1; + UpString(Qname);Move(Qname[0],_QdivData[1],i); + + inc(i); + _QdivData[i]:=DirHandle; + + inc(i); + UpString(PathName); + Move(PathName[0],_QDivData[i],ord(PathName[0])+1); + + len:=3+i+ord(PathName[0]); + F2SystemCall($17,len+2,SizeOf(Trep),result); + end; +With TPrep(GlobalReplyBuf)^ + do begin + QobjID:=Lswap(_Qid); { force lo-hi } + end; +CreateQueue:=(result=0) +{ resultcodes: $00 Success ; $96 Server Out Of Memory; $99 Drectory Full; + $9B Bad Directory Handle; $9C Invalid Path; $ED Property Already Exists; + $EE Object Already Exists; $EF Invalid Name; $F0 Wildcard Not Allowed; + $F1 Invalid Bindery Security; $F5 No Object Create Privilege; + $F7 No Property Create Privilege; $FC No Such Object; + $FE Server Bindery Locked; $FF Bindery Failure. } +end; + +{F217/65 [2.1x+]} +Function DestroyQueue(QobjID:Longint):Boolean; +{ Destroys the specified Queue; aborts all jobs in the queue; + associated files/directories are deleted; + queue object is removed from the bindery. } +Type Treq=record + len:Word; + subFunc:Byte; + _QobjID:Longint; {hi-lo} + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subFunc:=$65; + _QobjID:=Lswap(QobjID); { force hi-lo } + end; +F2SystemCall($17,Sizeof(Treq),0,result); +DestroyQueue:=(result=0); +{ resultcodes: $00 Success ; $96 Server Out Of Memory; $9C Invalid Path; + $D0 Queue Error; $D1 No Queue; $FF Hardware Failure. } +end; + +{----------------Client or Diagnostic Functions-----------------------------} + +{F217/76 [2.1x/3.x]} +Function ReadQueueStatus(QobjID:Longint; + Var Qstatus:TQueueStatus):Boolean; +{ Read the status of a queue. This information is changed by queueservers.} +Type Treq=record + len :Word; + subFunc:Byte; + _QobjID:Longint; {hi-lo} + end; + Trep=record + _QobjID:Longint; {hi-lo} + _Qstatus:Byte; + _NbrOfJobs:Byte; + _NbrOfServers:Byte; {max.25} + _serverIDlist:array[1..25] of Longint; {hi-lo} + _ServerConnNbrs:array[1..25] of Byte; + _MaxNumberOfServers:Byte; + end; + TPreq=^Treq; + TPrep=^Trep; +Var t:Byte; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subFunc:=$66; + _QobjID:=Lswap(QobjID); {force hi-lo} + end; +F2SystemCall($17,Sizeof(Treq),SizeOf(Trep),result); +With Qstatus, TPrep(GlobalReplyBuf)^ + do begin + ObjectId:=Lswap(_QobjId); + status:=_Qstatus; + NbrOfJobs:=_NbrOfJobs; + NbrOfServers:=_NbrOfServers; + + for t:=1 to NbrOfServers + do ServerObjectIDs[t]:=Lswap(_ServerIDlist[t]); + Move(_ServerConnNbrs,ServerConnNbrs,25); + MaxNbrOfServers:=_MaxNumberOfServers; + end; +ReadQueueStatus:=(result=0) +end; + +{F217/67 [2.1x+]} +Function SetQueueStatus(QobjId:Longint; NewQstatusFlag:Byte):Boolean; +{ Change the queue status flag. Use the QS_XXXX constants } +Type Treq=record + len:Word; + subFunc:Byte; + _QobjId:Longint; {hi-lo} + _Qstatus:Byte; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$67; + _QobjId:=Lswap(QobjId); + _Qstatus:=NewQStatusFlag; + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),0,result); +SetQueueStatus:=(result=0) +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $9C Invalid Path; + $D0 Queue Error; + $D1 No Queue; + $D3 No Queue Rights; + $FE Server Bindery Locked; + $FF Bindery Failure. +} +end; + + + +{F217/6B} +FUNCTION GetQueueJobList( QueueObjId: Longint; + Var QJobList:TQueueJobList): Boolean; +{ You need to be either a Q_USER or a Q_OPERATOR } +Type TReq=Record + BufLen : Word; + func : Byte; + _QueueObjId: Longint; {hi-lo} + end; + TRep=Record + _JobCount:Word; {max 250, hi-lo} + _JobBuf :TJobNumberList; {array, entries hi-lo} + _MaxJobs :Word; {hi-lo} + End; + TPrep=^Trep; + TPreq=^Treq; +Var i:Word; +Begin +With TPReq(GlobalReqBuf)^ + do Begin + func:= $6B; + _QueueObjId:= LSwap(QueueObjId); + BufLen:=5; + End; +F2SystemCall($17,Sizeof(Treq),SizeOf(Trep),result); +IF result = 0 + Then with QJobList, TPrep(GlobalReplyBuf)^ + do Begin + JobCount:= Swap(_JobCount); + IF (JobCount > MaxQueueJobs) + Then JobCount:= MaxQueueJobs; + FOR i:= 1 TO JobCount + DO JobNbrs[i]:= Swap(_JobBuf[i]); + MaxJobs:=swap(_MaxJobs); + End; +GetQueueJobList:= (result = 0); +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $9C Invalid Path; + $D0 Queue Error; + $D1 No Queue; + $D2 No Queue Server; + $D3 No Queue Rights; + $FC No Such Object; + $FE Server Bindery Locked; + $FF Bindery Failure. } +End; + +{F217/6C} +FUNCTION ReadQueueJobEntry( QObjId: Longint;JobNbr: Word; + VAR QJob: TQueueJobEntry): Boolean; +{ You need to be either a Q_USER, Q_OPERATOR or a Q_SERVER } +Type TReq=Record + BufLen : Word; + func : Byte; + _QueueObjId: Longint; {hi-lo} + _JobNumber : Word {hi-lo} + End; + TRep=Record + buf : TintJobStruct; { Unit INTERNAL type. To be converted } + End; + TPreq=^Treq; + TPrep=^Trep; +Begin +With TPReq(GlobalReqBuf)^ + do Begin + Buflen:= 7; + func:= $6C; + _QueueObjId:= LSwap(QObjId); + _JobNumber:= Swap(JobNbr); + End; +F2SystemCall($17,SizeOf(Treq),SizeOf(Trep),result); +IF result= 0 + Then with TPrep(GlobalReplyBuf)^ + do Begin + ConvertQJE2ext(buf,QJob,True); + End; +ReadQueueJobEntry:= result = 0; +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $D0 Queue Error; + $D1 No Queue; + $D2 No Queue Server; + $D3 No Queue Rights; + $D5 No Queue Job; + $FC No Such Object; + $FE Server Bindery Locked; + $FF Bindery Failure. } +End; + +{F217/6A} +FUNCTION RemoveJobFromQueue( QObjId: Longint; JobNbr: Word): Boolean; +{ You need to be Q_OPERATOR or the Q_USER who queued the job } +Type TReq=Record + BufLen: Word; + func: Byte; + _QueueObjId: Longint; {hi-lo} + _JobNumber:Word {hi-lo} + End; + TPreq=^Treq; +Begin +With TPReq(GlobalReqBuf)^ + do Begin + Buflen:= 7; + func:= $6A; + _QueueObjId:= LSwap(QObjId); + _JobNumber:= Swap(JobNbr); + End; +F2SystemCall($17,SizeOf(Treq),0,result); +RemoveJobFromQueue:= result = 0; +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $D0 Queue Error; + $D1 No Queue; + $D3 No Queue Rights; + $D5 No Queue Job; + $D6 No Job Rights; + $FE Server Bindery Locked; + $FF Bindery Failure. } +End; + + +{F217/69 [2.1x+]} +Function StartQueueJob(QobjId:Longint;JobNbr:Word):Boolean; +Type Treq=record + len :Word; + subFunc:Byte; + _QobjId:Longint; {hi-lo} + _JobNbr:Word; {hi-lo} + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$69; + _QobjId:=Lswap(QobjID); + _JobNbr:=swap(JobNbr); + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),0,result); +StartQueueJob:=(result=0) +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $D0 Queue Error; + $D1 No Queue; + $D3 No Queue Rights; + $D5 No Queue Job; + $D6 No Job Rights; + $FE Server Bindery Locked; + $FF Bindery Failure. } +end; + + +{F217/6E [2.1x+]} +Function ChangeQueueJobPosition(QobjId:Longint; JobNbr:Word; + NewJobPos:Byte ):Boolean; +{ Q_OPERATOR only } +Type Treq=record + len :Word; + subFunc :Byte; + _QobjId :Longint; {hi-lo} + _JobNbr :Word; {hi-lo} + _NewJobPos:Byte; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$6E; + _QobjId:=Lswap(QobjId); + _JobNbr:=swap(JobNbr); + _NewJobPos:=NewJobPos; + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),0,result); +ChangeQueueJobPosition:=(result=0) +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $D0 Queue Error; + $D1 No Queue; + $D5 No Queue Job; + $D6 No Job Rights; + $FE Server Bindery Locked; + $FF Bindery Failure. } +end; + +{F217/6F [2.1x+]} +Function AttachQueueServerToQueue(QobjId:Longint):Boolean; +{ Q_SERVERs only } +Type Treq=record + len :Word; + subFunc:Byte; + _QobjId:Longint; {hi-lo} + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$6F; + _QobjId:=Lswap(QobjId); + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),0,result); +AttachQueueServerToQueue:=(result=0) +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $9C Invalid Path; + $D0 Queue Error; + $D1 No Queue; + $D2 No Queue Server; + $FE Server Bindery Locked; + $FF Bindery Failure. } +end; + + +{F217/70 [2.1x+]} +Function DetachQueueServerFromQueue(QobjId:Longint):Boolean; +{ Q_SERVERs only } +Type Treq=record + len :Word; + subFunc:Byte; + _QobjId:Longint; {hi-lo} + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$70; + _QobjId:=Lswap(QobjId); + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),0,result); +DetachQueueServerFromQueue:=(result=0) +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $9C Invalid Path; + $D0 Queue Error; + $D1 No Queue; + $D2 No Queue Server; + $FE Server Bindery Locked; + $FF Bindery Failure. } +end; + + +{F217/71 [2.1x+]} +Function ServiceQueueJob(QobjID:Longint; JobType:Word; + Var QjobEntry:TQueueJobEntry):Boolean; +{ Q_SERVERs only } +Type Treq=record + len :Word; + subFunc :Byte; + _QobjId :Longint; {hi-lo} + _JobType:Word; {hi-lo} + end; + Trep=Record + _qje:TintJobStruct; { EXCEPT last two fields } + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$71; + _QobjId:=Lswap(QobjId); + _JobType:=swap(JobType); + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),SizeOf(Trep)-50-152,result); +With TPrep(GlobalReplyBuf)^ + do begin + ConvertQJE2Ext(_qje,QjobEntry,false); + FillChar(QjobEntry.JobDescription,50,#$0); + FillChar(QjobEntry.ClientRecordArea,152,#$0); + { Use the ReadQueueJobEntry function to get job's + descriptionstring and clientRecordArea. } + end; +ServiceQueueJob:=(result=0) +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $9C Invalid Path; + $D0 Queue Error; + $D1 No Queue; + $D3 No Queue Rights; + $D5 No Queue Job; + $D9 Connection not Queue Server; + $DA Queue Halted; + $FE Server Bindery Locked; + $FF Bindery Failure. } +end; + +{F217/72 [2.1x+]} +Function FinishServicingQueueJob(QobjId:Longint;JobNbr:Word; + Charge:Longint ):Boolean; +{ Q_SERVERs only } +Type Treq=record + len :Word; + subFunc:Byte; + _QobjId:Longint; {hi-lo} + _JobNbr:Word; {hi-lo} + _Charge:Longint; {hi-lo} + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$72; + _QobjId:=Lswap(QobjId); + _JobNbr:=swap(JobNbr); + _Charge:=Lswap(Charge); + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),0,result); +FinishServicingQueueJob:=(result=0) +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $D0 Queue Error; + $D1 No Queue; + $D6 No Job Rights; } +end; + +{F217/73 [2.1x+]} +Function AbortServicingQueueJob(QobjId:Longint; JobNbr:Word):Boolean; +Type Treq=record + len :Word; + subFunc:Byte; + _QobjId:Longint; {hi-lo} + _JobNbr:Word; {hi-lo} + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$73; + _QobjId:=Lswap(QobjId); + _JobNbr:=swap(JobNbr); + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),0,result); +AbortServicingQueueJob:=(result=0) +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $D0 Queue Error; + $D1 No Queue; + $D6 No Job Rights; + $D9 Connection not Queue Server; } +end; + +{F217/74 [2.1x+]} +Function ChangeToClientRights(QobjId:Longint;JobNbr:Word):Boolean; +{ Q_SERVERs servicing job only } +Type Treq=record + len :Word; + subFunc:Byte; + _QobjId:Longint; {hi-lo} + _JobNbr:Word; {hi-lo} + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$74; + _QobjId:=Lswap(QobjId); + _JobNbr:=swap(JobNbr); + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),0,result); +ChangeToClientRights:=(result=0) +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $D0 Queue Error; + $D1 No Queue; + $D6 No Job Rights; + $D9 Connection not Queue Server; } +end; + +{F217/75 [2.1x+]} +Function RestoreQueueServerRights:Boolean; +{ Q_SERVERs, servicing job and having previously called + ChangeToClientRights only } +Type Treq=record + len :Word; + subFunc:Byte; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$75; + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),0,result); +RestoreQueueServerRights:=(result=0) +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $9C Invalid Path; + $D0 Queue Error; + $D1 No Queue; + $D3 No Queue Rights; + $D5 No Queue Job; + $D9 Connection not Queue Server; + $DA Queue Halted; + $FE Server Bindery Locked; + $FF Bindery Failure. } +end; + +{F217/76 [2.1x+]} +Function ReadQueueServerStatus(QobjId :Longint; + QserverObjId :Longint; + QserverConnNbr:Byte; + Var Qstatus:TQueueServerStatus):Boolean; +Type Treq=record + len :Word; + subFunc :Byte; + _QobjId :Longint; {hi-lo} + _QSobjId :Longint; {hi-lo} + _QSconnNbr:Byte; + end; + Trep=record + _Qstatus:TqueueServerStatus; + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$76; + _QobjId:=Lswap(QobjId); + _QSobjId:=Lswap(QserverObjId); + _QSconnNbr:=QserverConnNbr; + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),SizeOf(Trep),result); +With TPrep(GlobalReplyBuf)^ + do begin + Move(_Qstatus,Qstatus,SizeOf(TQueueServerStatus)); + end; +ReadQueueServerStatus:=(result=0) +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $9C Invalid Path; + $D1 No Queue; + $D2 No Queue Server; + $D3 No Queue Rights; + $F1 Invalid Bindery Security; + $FC No Such Object; + $FE Server Bindery Locked; + $FF Bindery Failure. } +end; + +{F217/77 [2.1x+]} +Function SetQueueServerStatus(QobjId:Longint; Qstatus:TqueueServerStatus):Boolean; +Type Treq=record + len :Word; + subFunc :Byte; + _QobjId :Longint; {hi-lo} + _Qstatus:TQueueServerStatus; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$77; + _QobjId:=Lswap(QobjId); + Move(Qstatus,_Qstatus,Sizeof(TQueueServerStatus)); + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),0,result); +SetQueueServerStatus:=(result=0) +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $9C Invalid Path; + $D0 Queue Error; + $D1 No Queue; + $FE Server Bindery Locked; + $FF Bindery Failure. } +end; + +{F217/78 [2.1x+]} +Function GetQueueJobsFileSize(QobjId:Longint; JobNbr:Word; + Var JobSize:Longint ):Boolean; +Type Treq=record + len :Word; + subFunc:Byte; + _QobjId:Longint; {hi-lo} + _JobNbr:Word; {hi-lo} + end; + Trep=record + _QobjId :Longint; {hi-lo} + _JobNbr :Word; {hi-lo} + _JobSize:Longint; {hi-lo} + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$78; + _QobjId:=Lswap(QobjId); + _JobNbr:=swap(JobNbr); + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),SizeOf(Trep),result); +With TPrep(GlobalReplyBuf)^ + do begin + JobSize:=Lswap(_JobSize); + end; +GetQueueJobsFileSize:=(result=0) +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $9C Invalid Path; + $FE Server Bindery Locked; + $FF Bindery Failure. } +end; + + +{F217/68 [2.1x+]} +Function CreateQueueJob(QobjId:Longint; + {i/o} Var Qjob:TqueueJobEntry):Boolean; +Type Treq=record + len :Word; + subFunc:Byte; + _QobjId:Longint; {hi-lo} + _Qjob :TintJobStruct; + end; + Trep=record + _QjobR:TintJobStruct; { Except the last two fields ! } + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$68; + _QobjId:=Lswap(QobjId); + ConvertQJE2Int(Qjob,_Qjob); + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),SizeOf(Trep)-152-50,result); +With TPrep(GlobalReplyBuf)^ + do begin + ConvertQJE2Ext(_QjobR,Qjob,False); + { False => Last 2 fields remain unchanged } + end; +CreateQueueJob:=(result=0) +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $99 Directory Full; + $9C Invalid Path; + $D0 Queue Error; + $D1 No Queue; + $D2 No Queue Server; + $D3 No Queue Rights; + $D4 Queue Full; + $DA Queue Halted; + $ED Property Already Exists; + $EF Invalid Name; + $F0 Wildcard Not Allowed; + $F1 Invalid Bindery Security; + $F7 No Property Create Privilege; + $FC No Such Object; + $FE Server Bindery Locked; + $FF Bindery Failure. } +end; + +{F217/6D [2.1x+]} +Function ChangeQueueJobEntry(QobjId:Longint;Qjob:TQueueJobEntry):Boolean; +Type Treq=record + len :Word; + subFunc:Byte; + _QobjId:Longint; {hi-lo} + _Qjob :TintJobStruct; + end; + TPreq=^Treq; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$6D; + _QobjId:=Lswap(QobjId); + ConvertQJE2Int(Qjob,_Qjob); + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),0,result); +ChangeQueueJobEntry:=(result=0) +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $D0 Queue Error; + $D1 No Queue; + $D5 No Queue Job; + $D7 Queue Servicing; + $FE Server Bindery Locked; + $FF Bindery Failure. } +end; + + +{$IFDEF Template} {--------------- Q unit function template ---------------} + +{F217/ [2.1x+]} +Function ( ):Boolean; +Type Treq=record + len:Word; + subFunc:Byte; + + end; + Trep=record + + end; + TPreq=^Treq; + TPrep=^Trep; +Begin +WITH TPreq(GlobalReqBuf)^ + do begin + subFunc:=$ + + len:=SizeOf(Treq)-2; + end; +F2SystemCall($17,SizeOf(Treq),SizeOf(Trep),result); +With TPrep(GlobalReplyBuf)^ + do begin + + end; + :=(result=0) +{ Valid completion codes: + $00 Success + $96 Server Out Of Memory; + $99 Drectory Full; + $9B Bad Directory Handle; + $9C Invalid Path; + $D0 Queue Error; + $D1 No Queue; + $D2 No Queue Server; + $D3 No Queue Rights; + $D4 Queue Full; + $D5 No Queue Job; + $D6 No Job Rights; + $D7 Queue Servicing; + $D9 Connection not Queue Server; + $DA Queue Halted; + $DB Max Queue Servers; + $ED Property Already Exists; + $EE Object Already Exists; + $EF Invalid Name; + $F0 Wildcard Not Allowed; + $F1 Invalid Bindery Security; + $F5 No Object Create Privilege; + $F7 No Property Create Privilege; + $FC No Such Object; + $FE Server Bindery Locked; + $FF Bindery Failure. +} +end; + +{$ENDIF} + +end. diff --git a/SRC/UNITS/NWSEMA.PAS b/SRC/UNITS/NWSEMA.PAS new file mode 100644 index 0000000..0128660 --- /dev/null +++ b/SRC/UNITS/NWSEMA.PAS @@ -0,0 +1,330 @@ +{$X+,B-,V-} {essential compiler directives} + +Unit nwSema; + +{ nwSema unit as of 950301 / NwTP 0.6 API. (c) 1993,1995, R.Spronk } + +INTERFACE + +{ Primary functions: Interrupt: comments: + +* CloseSemaphore (F220/04) +* ExamineSemaphore (F220/01) +* GetConnectionsSemaphores (F217/F1) +* GetSemaphoreInformation (F217/F2) +* OpenSemaphore (F220/00) +* SignalSemaphore (F220/03) +* WaitOnSemaphore (F220/02) + +Notes: Functions marked with a '*' have been tested and found correct. +} + +Uses nwIntr,nwMisc; + +Type TsemaInfo=record + ConnNbr:word; + TaskNbr:word; + end; + TsemaInfoList=array[1..100] of TsemaInfo; + { used by GetSemaphoreInformation } + + TconnSema=record + OpenCount: Byte; + Value : Integer; + TaskNbr : Word; + unknown : byte; { always 00 ?! } + Name : string[127]; + end; + { used by GetConnectionsSemaphores } + +Var Result:word; + +{F220/00 [2.15? 3.x]} +Function OpenSemaphore(SemName : String; InitVal : Integer; + VAR SemHandle : LongInt; + VAR OpenCount : Word ):Boolean; + +{F220/01 [2.15? 3.x]} +FUNCTION ExamineSemaphore( SemHandle :LongInt; + VAR Value :Integer; + VAR OpenCount :Word ) :Boolean; +{ This functions returns the current value and open count of a semaphore.} + +{F220/02 [3.x]} +FUNCTION WaitOnSemaphore( SemHandle :LongInt; + Wait_Time :Word ) :Boolean; +{ Decrement the semaphore value and, if it is negative, } +{ wait until it becomes non-negative or until a timeout occurs. } + +{F220/03 [3.x]} +FUNCTION SignalSemaphore(SemHandle:LongInt) : Boolean; +{ Increment the semaphore value and release if waiting. } + +{F220/04 [3.x]} +FUNCTION CloseSemaphore(SemHandle:LongInt) : Boolean; +{ Decrement the open count of a semaphore.} +{ When the open count goes to zero, the semaphore is destroyed. } + + +{F217/F1 [2.15+? 3.x+]} +Function GetConnectionsSemaphores(ConnNbr:Word; + {i/o} Var seqNbr:Word; + {out} Var NbrOfSemaLeft:Byte; + {out} Var SemaInfo:TconnSema):Boolean; +{Caller needs console privileges } + +{F217/F2 [2.15? 3.x+]} +Function GetSemaphoreInformation(SemaName:String; + {i/o} Var seqNbr:word; + {out} Var OpenCount:word; + Var SemValue:Integer; + Var NbrOfSemaLeft:byte; + Var info:TsemaInfoList):Boolean; +{ Caller needs console privileges } + + +IMPLEMENTATION {=============================================================} + + +{F220/00 [3.x]} +Function OpenSemaphore(SemName : String; InitVal : Integer; + VAR SemHandle : LongInt; + VAR OpenCount : Word ):Boolean; +Type Treq=Record + subf:byte; + _InitVal:byte; + _SemNameLen:byte; + _SemName:array[0..127] of byte; + end; + Trep=record + _SemHandle:LongInt; + _OpenCount:Byte; + end; + TPreq=^Treq; + TPrep=^Trep; +begin +With TPreq(GlobalReqBuf)^ + do begin + subf:=$00; + If InitVal<0 + then _InitVal:=Lo(256+Initval) + else _InitVal:=Lo(InitVal); + UpString(SemName);SemName:=SemName+#0; + move(semName[1],_SemName[0],ord(SemName[0])); + _SemNameLen:=ord(semName[0])-1; + end; +F2SystemCall($20,SizeOf(treq),SizeOf(trep),result); +With TPrep(GlobalReplyBuf)^ + do begin + SemHandle:=Lswap(_SemHandle); + OpenCount:=_OPenCount; + end; +OpenSemaphore:=(result=0); +end; + + +{F220/02 [3.x]} +Function WaitOnSemaphore( SemHandle : LongInt; + Wait_Time : Word ) : Boolean; +{ Decrement the semaphore value and wait if it is negative. If negative,} +{ the workstation will wait until it becomes non-negative or until a } +{ timeout occurs. } +Type Treq=Record + subf:byte; + _SemHandle:Longint; + _wait :word; { hi-lo } + end; + TPreq=^Treq; +begin +With TPreq(GlobalReqBuf)^ + do begin + subf:=$02; + _semHandle:=Lswap(SemHandle); + _wait:=swap(wait_Time); + end; +F2SystemCall($20,SizeOf(treq),0,result); +WaitOnSemaphore:=(result=0); +end; + + +{F220/03 [3.x+]} +Function SignalSemaphore(SemHandle:LongInt) : Boolean; +{ Increment the semaphore value and release if waiting. If any stations} +{ are waiting, the station that has been waiting the longest will be } +{ signalled to proceed } +Type Treq=Record + subf:byte; + _semhandle:Longint; + end; + TPreq=^Treq; +begin +With TPreq(GlobalReqBuf)^ + do begin + subf:=$03; + _semHandle:=Lswap(SemHandle); + end; +F2SystemCall($20,SizeOf(treq),0,result); +SignalSemaphore:=(result=0); +end; + + +{F220/04 [3.x+]} +Function CloseSemaphore(SemHandle:LongInt) : Boolean; +{ Decrement the open count of a semaphore. When the open count goes } +{ to zero, the semaphore is destroyed. } +Type Treq=Record + subf:byte; + _semhandle:Longint; + end; + TPreq=^Treq; +begin +With TPreq(GlobalReqBuf)^ + do begin + subf:=$04; + _semHandle:=Lswap(SemHandle); + end; +F2SystemCall($20,SizeOf(treq),0,result); +CloseSemaphore:=(result=0); +end; + + +{F220/01 [2.x/3.x]} +FUNCTION ExamineSemaphore(SemHandle:LongInt; + VAR Value : Integer; + VAR OpenCount : Word ) : Boolean; +{ The semaphore value that comes back is the count from the open call } +{ - the open count is incremented } +{ anytime a station opens the semaphore this can be used for controlling } +{ the number of users using your software } +Type Treq=record + subf:byte; + _semHandle:Longint; + end; + Trep=record + _Value:Byte; + _OpenCount:Byte; + end; + TPreq=^Treq; + TPrep=^Trep; +BEGIN +With TPreq(GlobalReqBuf)^ + DO begin + subf:=$01; + _semHandle:=Lswap(SemHandle); + end; +F2SystemCall($20,SizeOf(Treq),SizeOf(Trep),result); +With TPrep(GlobalReplyBuf)^ + do begin + if (_Value and $80)>0 + then Value:=254-_Value + else Value:=_Value; + OpenCount:=_OpenCount; + end; +ExamineSemaphore := (result = 0); +END; + +{F217/F1 [2.15+? 3.x+]} +Function GetConnectionsSemaphores(ConnNbr:Word; + {i/o} Var seqNbr:Word; + {out} Var NbrOfSemaLeft:Byte; + {out} Var SemaInfo:TconnSema):Boolean; +{ To be called iteratively. Inital seqNbr=1. Iterate until seqNbr + becomes 0 (or until NbrOfSemaLeft becomes 0). + + This function can return information about several semaphores at the + same time. However, the size of the reply buffer is limited, causing + several as of now unsolvable problems. For now this function will + return information on a per semaphore basis. } +Type Treq=Record + len:word; + subf:byte; + _ConnNbr:word; {lo-hi} + _SeqNbr:word; {lo-hi} + end; + Trep=record + _NextSeqNbr:word; + _nbrOfSema:byte; { word (lo-hi) ? } + _unknown:byte; { -^ } + _SemaInfoBuf:array[1..508] of byte; + end; + TPreq=^Treq; + TPrep=^Trep; +Var i,t:Byte; +begin +With TPreq(GlobalReqBuf)^ + do begin + len:=SizeOf(Treq)-2; + subf:=$F1; + _ConnNbr:=ConnNbr; + _SeqNbr:=SeqNbr; + end; +F2SystemCall($17,SizeOf(treq),SizeOf(trep),result); +if result=0 + then With TPrep(GlobalReplyBuf)^ + do begin + NbrOfSemaLeft:=(_NbrOfSema-1); + if NbrOfSemaLeft=0 + then seqNbr:=0 + else seqNbr:=seqNbr+1; { unfortunately, _NextSeqNbr returns no valid info. } + + Move(_SemaInfoBuf[1],SemaInfo,7+_SemaInfoBuf[7]); + With SemaInfo + do begin + Value:=swap(Value); + TaskNbr:=swap(TaskNbr); + end; + end; +GetConnectionsSemaphores:=(result=0); +{ 00 Successful C6 No console rights FD Bad connection number } +end; + +{F217/F2 [2.15? 3.x+]} +Function GetSemaphoreInformation(SemaName:String; + {i/o} Var seqNbr:word; + {out} Var OpenCount:word; + Var SemValue:Integer; + Var NbrOfSemaLeft:byte; + Var info:TsemaInfoList):Boolean; +Type Treq=Record + len:word; + subf:byte; + _seqNbr: word; + _semaName:string[127]; + end; + Trep=record + _NextSeqNbr:Word; + _OpenCount:word; + _SemValue:word; + _NbrOfRecords:word; + _SemaInfoBuf:array[1..514] of byte; + end; + TPreq=^Treq; + TPrep=^Trep; +begin +UpString(SemaName); +if SemaName[0]>#127 + then SemaName[0]:=#127; +With TPreq(GlobalReqBuf)^ + do begin + subf:=$F2; + _seqNbr:=seqNbr; + _SemaName:=SemaName; + len:=4+ord(_SemaName[0]); + end; +F2SystemCall($17,SizeOf(treq),SizeOf(trep),result); +With TPrep(GlobalReplyBuf)^ + do begin + OpenCount:=_OpenCount; + SemValue:=Integer(_SemValue); + NbrOfSemaLeft:=_NbrOfRecords; + move(_SemaInfoBuf,Info,SizeOf(TsemaInfoList)); + if NbrOfSemaLeft>100 + then seqNbr:=seqNbr+100 + else seqNbr:=0; + end; +GetSemaphoreInformation:=(result=0); +{ 00 Successful C6 No console rights } +end; + + +END. \ No newline at end of file diff --git a/SRC/UNITS/NWSERV.DCU b/SRC/UNITS/NWSERV.DCU new file mode 100644 index 0000000000000000000000000000000000000000..3896f5f65a40bcd2deb0009fba856f6b696d6a6c GIT binary patch literal 15632 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 + then time.year:=time.year-100; +{ year<80 : 21st century } +result1:=0; +getFileServerDateAndTime:=TRUE; +end; + + +{F217/CA [2.15c+]} +FUNCTION SetFileServerDateAndTime (time:TnovTime): Boolean; +Type Treq=record + Len:word; + subF:byte; + _time:TnovTime + end; + TPreq=^Treq; +BEGIN +{ year<80 : 21st century } +WITH TPreq(GlobalReqBuf)^ +do begin + Len:=SizeOf(Treq)-3; { dow is not a parameter } + subF:=$CA; + _time:=time; + end; +F2SystemCall($17,SizeOf(Treq),0,result1); +SetFileServerDateAndTime:=(result1=$00); +{ Resulcodes: $00 Success; $C6 No Console Operator Rights } +end; + + +{F217/11 [2.15c+]} +Function GetFileServerInformation (Var serverInfo:TFileServerInformation):boolean; +{determine the version of software installed on the file server and how it is configured} + +{SeeAlso: GetDiskUtilization, GetNetworkSerialNumber, GetFileServerLoginStatus, + GetFileServerDateAndTime} + +Type TReq=Record + Len : word; + SubF : Byte; + End; + TRep=TFileServerInformation; + TPreq=^Treq; + TPrep=^Trep; + +Var t:word; +Begin +With TPreq(GlobalReqBuf)^ +Do Begin + Len := 1; + SubF:= $11; + End; +F2SystemCall($17,SizeOf(Treq),SizeOf(Trep)-1,result1); +Move(GlobalReplyBuf^[1],GlobalReplyBuf^[2],SizeOf(Trep)-1); +serverInfo:=TPrep(GlobalReplyBuf)^; +With serverinfo +do begin + connectionsMax :=Swap(connectionsMax); { force lo-hi again } + ConnectionsInUse:=Swap(connectionsInUse); + MaxConnVol :=Swap(maxConnVol); + peak_conn_used :=Swap(peak_conn_used); + for t:=48 downto 1 + do if serverInfo.serverName[t]=#0 + then serverInfo.serverName[0]:=chr(t-1); + end; +GetFileServerInformation:=(result1=0); +End; + + + +{F217/C9 [2.15c+]} +FUNCTION GetFileServerDescriptionStrings(Var companyName, + VersionAndRevision,revisionDate, + copyrightNotice:String + ):Boolean; +{SeeAlso: GetFileServerLoginStatus, GetFileServerInformation. } +Type Treq=record + len : word; + subf: byte; + end; + Trep=record + stuff : array [1..512] of byte; + end; + TPreq=^Treq; + TPrep=^Trep; +Var x,xofs:word; +begin +With TPreq(GlobalReqBuf)^ +do begin + len := 1; + subf := $c9; + end; +F2SystemCall($17,SizeOf(Treq),SizeOf(Trep),result1); +companyName:=''; VersionAndRevision:=''; +revisionDate:=''; copyrightNotice:=''; +if result1=$00 + then with TPrep(GlobalReplyBuf)^ + do begin + x:=1;xofs:=x; + while (stuff[x]<>$00) and (x<512) do inc(x); + ZStrCopy(companyName,stuff[xofs],x-xofs); + + inc(x);xofs:=x; { skip 1 zero. ? skip more zero's? } + While (stuff[x]<>$00) and (x<512) do inc(x); + ZStrCopy(VersionAndRevision,stuff[xofs],x-xofs); + + inc(x);xofs:=x; + While (stuff[x]<>$00) and (x<512) do inc(x); + ZStrCopy(revisionDate,stuff[xofs],x-xofs); { mm/dd/yy } + + inc(x);xofs:=x; + While (stuff[x]<>$00) and (x<512) do inc(x); + ZStrCopy(copyrightNotice,stuff[xofs],x-xofs); + end; +GetFileServerDescriptionStrings:=(result1=$00); +end; + + + +{F217/D3 [2.15c+]} +FUNCTION DownFileServer (ForceFlag : Boolean) : Boolean; +Type Treq=record + len : word; + subf : byte; + flag : byte; + end; + TPreq=^Treq; +BEGIN +With TPreq(GlobalReqBuf)^ +do begin + len := 2; + subf := $D3; + if ForceFlag then flag := $FF { non-zero } + else flag := $00; + end; +F2SystemCall($17,SizeOf(Treq),0,result1); +DownFileServer:=(result1=0); +{ result1codes: 00=successful; C6 No Console Rights ; FF Open Files} +end; + + +{F217/CF [3.x]} +FUNCTION DisableTransactionTracking : Boolean; +{ Caller must have console-operator rights. } +Type Treq=record + len : word; + subf: byte + end; + TPreq=^Treq; +BEGIN +With TPreq(GlobalReqBuf)^ + do begin + len := 1; + subf:= $CF; + end; +F2SystemCall($17,SizeOf(Treq),0,result1); +DisableTransactionTracking:=(result1=0); +{ result1codes: 00=successful; C6 No Console Rights } +end; + + +{F217/D0 [3.x]} +FUNCTION EnableTransactionTracking : Boolean; +{ Caller must have console-operator rights. } +Type Treq=record + len : word; + subf: byte + end; + TPreq=^Treq; +BEGIN +With TPreq(GlobalReqBuf)^ + do begin + len := 1; + subf:= $D0; + end; +F2SystemCall($17,SizeOf(Treq),0,result1); +EnableTransactionTracking:=(result1=0); +{ result1codes: 00=successful; C6 No Console Rights } +end; + + +{F217/CB [2.15c+]} +FUNCTION DisableFileServerLogin : Boolean; +{ Caller must have console-operator rights. } +Type Treq=record + len : word; + subf: byte + end; + TPreq=^Treq; +BEGIN +With TPreq(GlobalReqBuf)^ + do begin + len := 1; + subf:= $CB; + end; +F2SystemCall($17,SizeOf(Treq),0,result1); +DisableFileServerLogin:=(result1=0); +{ result1codes: 00=successful; C6 No Console Rights } +end; + + + +{F217/CC [2.15c+]} +FUNCTION EnableFileServerLogin : Boolean; +{ Caller needs console-operator rights. } +Type Treq=record + len : word; + subf: byte + end; + TPreq=^Treq; +BEGIN +With TPreq(GlobalReqBuf)^ + do begin + len := 1; + subf:= $CC; + end; +F2SystemCall($17,SizeOf(Treq),0,result1); +EnableFileServerLogin:=(result1=0); +{ result1codes: 00=successful; C6 No Console Rights } +end; + + + +{F217/CD [2.15c+]} +FUNCTION GetFileServerLoginStatus( Var LoginEnabled:Boolean ): Boolean; +{ if Login is enabled then returns TRUE in LoginEnabled } +{ result1 byte: 00h - Success, C6h No Console Rights } +{ Caller must have operator status.} +Type TReq=record + Len : Word; + SubF : Byte; + end; + TRep=record + Flag : Byte; + end; + TPreq=^Treq; + TPrep=^Trep; +begin +with TPreq(GlobalReqBuf)^ + do begin + Len := 1; + SubF := $CD; + end; +F2SystemCall($17,SizeOf(Treq),SizeOf(Trep),result1); +LoginEnabled:=Boolean(TPrep(GlobalReplyBuf)^.Flag); +GetFileServerLoginStatus := (result1=0); +end; + + +{F217/C8 [2.15c+]} +FUNCTION CheckConsolePrivileges : Boolean; +Type TReq=record + Len : Word; + SubF : Byte; + end; + TPreq=^Treq; +begin +with TPReq(GlobalReqBuf)^ + do begin + Len := 1; + SubF := $C8; + end; +F2SystemCall($17,SizeOf(Treq),0,result1); +CheckConsolePrivileges := (result1=$00); +{ result1 byte: 00h - Success, C6h No Console Rights } +end; + + +{F217/EB [3.0+]} +FUNCTION GetConnectionsOpenFiles + ( ConnNumber : Byte; + {i/o:} var LastRecordSeen : word; + {out:} var NbrOfRecords : word; + var FileInfo : TfileInfoRecList ) : Boolean; + +{ the calling workstation must have console operator privileges } +{ LastRecordSeen is an i/o parameter; + -An initial value of 0 has to be supplied; + -The function can be called until LastRecordSeen becomes 0, + indicating the end of the FIR-list. + + to be called iteratively. } + +Type TReq=record + len :word; + subf :byte; + logicalConnNbr:word; + lastRecSeen :word; {lo-hi, $0000 on first call } + end; + TRep=record + nextReqRec : word; { lo-hi, use as lastRecSeen in next iterative call } + { $0000 if no more records } + RecCount : word; + FIRbuf:array[1..508] of byte; + end; + TPreq=^Treq; + TPrep=^Trep; +Var t,Foff:Word; +begin + +With TPreq(GlobalReqBuf)^ + do begin + len:=sizeof(Treq)-2; + subf:=$EB; + logicalConnNbr:=connNumber; + lastRecSeen:=LastRecordSeen; { force hi-lo } + end; +F2SystemCall($17,SizeOf(Treq),SizeOf(Trep),result1); +if result1=$00 + then with TPrep(GlobalReplyBuf)^ + do begin + { Copy recCount FIRs from FIRbuf to the FileInfo[] array } + LastRecordSeen:=NextReqRec; + NbrOfrecords:=RecCount; + Foff:=0; + For t:=1 to RecCount + do begin + Move(FIRbuf[1+Foff],FileInfo[t],17+FIRbuf[Foff+17]); + inc(Foff,17+FIRbuf[Foff+17]); + { Direntry and ParentEntry may have to be swapped lo-hi } + end; + end + else begin + NbrOfRecords:=0; + LastRecordSeen:=0; + end; +GetConnectionsOpenFiles:=(result1=$00); +{ errorcodes: $00 Success; $C6 no console privileges } +end; + + +{F217/EC } +Function GetConnectionsUsingAFile(VolNbr:Byte; EntryId:Longint; NStype:byte; + {i/o} Var LastRecordSeen:word; + Var NbrOfRecords:Word; + Var FileInfo:TfileUsageList):boolean; +{ This call returns all connection numbers using the file specified + by the Volume Number and Directory Entry Id. } + +{ !! UNDER CONSTRUCTION !! } + +Type TReq=record + len :word; + subf :byte; + NStype :Byte; {= data stream type / Fork type } + VolNbr :Byte; + DirEntryId :Longint; + LastRecSeen:Word; {initially set to 0} + end; + + TRep=record + NextRec :word; { iteration } + + UseCount :word; + OpenCount :word; + OpenForReadCount :word; + OpenForWriteCount:word; + DenyReadCount :word; + DenyWriteCount :word; + LockFlag :Byte; { boolean } + NStype :Byte; { Fork Count -> ?? NStype } + NbrOfRec :word; { max 70 } + FileUsage:array[1..70] of record + ConnNbr :word; + TaskNbr :word; + LockType :byte; + AccessFlag:Byte; + LockFlag :Byte; + end; + end; + TPreq=^Treq; + TPrep=^Trep; +begin +With TPreq(GlobalReqBuf)^ + do begin + len:=sizeof(Treq)-2; + subf:=$EC; + + end; +F2SystemCall($17,SizeOf(Treq),SizeOf(Trep),result1); +if result1=$00 + then with TPrep(GlobalReplyBuf)^ + do begin + + end + else begin + NbrOfRecords:=0; + LastRecordSeen:=0; + end; +GetConnectionsUsingAFile:=(result1=$00); +{ errorcodes: $00 Success; $C6 no console privileges } +end; + + +{F217/0E [2.15c+]} +FUNCTION GetDiskUtilization(volNbr:byte; objID:Longint; + Var usedDirs,usedFiles,usedBlocks:Word ):Boolean; +{ SeeAlso: GetFileServerInformation,getBinderyObjectDiskSpaceLeft } +Type TReq=record + Len : Word; + SubF : Byte; + _volNbr:Byte; + _objID:longInt; { hi-lo } + end; + TRep=record + _volNbr:Byte; + _objID:Longint; {hi-lo} + _usedDirs, + _usedFiles, + _usedBlocks:Word; { all hi-lo } + end; + TPreq=^Treq; + TPrep=^Trep; +begin +with TPReq(GlobalReqBuf)^ + do begin + Len := SizeOf(TReq)-2; + SubF := $0E; + _volNbr:=volNbr; + _objID:=Lswap(objID); + end; +F2SystemCall($17,SizeOf(Treq),SizeOf(Trep),result1); +if result1=$00 +then begin + with TPrep(GlobalReplyBuf)^ + do begin + usedDirs:=swap(_usedDirs); { force lo-hi } + usedFiles:=swap(_usedFiles); { force lo-hi } + usedBlocks:=swap(_usedBlocks);{ force lo-hi } + end; + end; +GetDiskUtilization:=(result1=$00); +{result1codes: 00h successful; 98h volume doesn't exist + 89h No Search Privileges + F2h no Object read privileges } +end; + + +{F217/12 [2.15c+]} +Function GetNetworkSerialNumber(Var serialNbr:LongInt; Var ApplicNbr:Word ):Boolean; +{return the serial number and application number for the software + installed on the file server} +{SeeAlso: VerifyNetworkSerialNumber,GetFileServerInformation} +Type TReq=record + Len : Word; + SubF : Byte; + end; + TRep=record + _serNbr : LongInt; {hi-lo} + _applicNbr: Word; {hi-lo} + end; + TPreq=^Treq; + TPrep=^Trep; +begin +with TPreq(GlobalReqBuf)^ + do begin + Len := 1; + SubF := $12; + end; +F2SystemCall($17,SizeOf(Treq),SizeOf(Trep),result1); +with TPrep(GlobalReplyBuf)^ + do begin + ApplicNbr:=swap(_applicNbr); { force lo-hi } + serialNbr:=Lswap(_serNbr); { force lo-hi } + end; +GetNetworkSerialNumber := (result1=0); +end; + + + +{F217/OC [2.15c+]} +Function VerifyNetworkSerialNumber(serialNbr: LongInt ; + Var ApplicNbr: Word ):Boolean; +{if the network serial number to be verified is correct, the reply + buffer will contain the corresponding application number } +{SeeAlso: GetNetworkSerialNumber} +Type Treq=record + Len : Word; + SubF : Byte; + _netwSerNbr: LongInt; {hi-lo} + end; + TRep=record + _applicNbr: word; {hi-lo} + end; + TPreq=^Treq; + TPrep=^Trep; +begin +with TPReq(GlobalReqBuf)^ + do begin + Len := 1; + SubF := $0C; + _netwSerNbr:=Lswap(serialNbr); + end; +F2SystemCall($17,SizeOf(Treq),SizeOf(Trep),result1); +with TPrep(GlobalReplyBuf)^ +do begin + ApplicNbr:=swap(_applicNbr); { force lo-hi } + end; +VerifyNetworkSerialNumber := (result1=0); +end; + +{****************** secondary functions ************************************} + + +FUNCTION CheckNetwareVersion(MinimumVersion,MinimumSubVersion, + MinimumRevision,MinimumSFT,MinimumTTS:word):Boolean; +{ checks if the current OS/TTS/SFT version is greater or equal to the minimal version } +Var info:TFileServerInformation; + res:boolean; +begin +IF GetFileServerInformation(info) +then begin + IF (info.NetwareVersion>MinimumVersion) + then res:=true + else if (info.NetwareVersion=MinimumVersion) + AND (info.NetwareSubVersion>MinimumSubVersion) + then res:=true + else if (info.NetwareVersion=MinimumVersion) + AND (info.NetwareSubVersion=MinimumSubVersion) + AND (info.OS_Revision>=MinimumRevision) + then res:=true + else res:=false + end +else res:=false; + +CheckNetwareVersion:=res AND (info.SFT_Level>=MinimumSFT) + AND (info.TTS_Level>=MinimumTTS) +end; + +end. {unit nwServ} \ No newline at end of file diff --git a/SRC/UNITS/NWSPX.PAS b/SRC/UNITS/NWSPX.PAS new file mode 100644 index 0000000..8939fcc --- /dev/null +++ b/SRC/UNITS/NWSPX.PAS @@ -0,0 +1,315 @@ +{$B-,V-,X+} + +UNIT nwSPX; + +{ nwSPX unit as of 950301 / NwTP 0.6 API. (c) 1993,1995, R. Spronk } +{ NOTE: These SPX calls are not documented in this version } + +INTERFACE + +Uses Dos,nwMisc,nwIPX; + +{ Primary SPX calls: Subf: Comments: + + SPXabortConnection 14 + SPXGetConnectionStatus 15 + SPXestablishConnection 11 +* SPXinitialize 10 + SPXlistenForConnection 12 + SPXlistenForSequencedPacket 17 + SPXsendSequencedPacket 16 + SPXTerminateConnection 13 + + Secondary calls: + +* SPXpresent + + Notes: (1) These functions use INT 21 and are not to be called from + within an ESR. +} + +Var Result:word; { unit errorcode variable } + +Type TspxHeader=Record + IPXhdr :TipxHeader; { SPX will set packetType to 5 } + connControl :byte; { rarely used, set to $00 } + { ignored by SPX, but passed on to receiver: + $10 End of message; $20 Attention packet } + dataStreamType:Byte; { to be used by higher level protocols. + nust be < $FE, passed on to receiver } + sourceConnId, + destConnId :Word; + sequenceNbr, + acknowledgeNbr, + allocationNbr :Word; + end; + { Fields within IPX and SPX are high-low. Byte swapping will be done + by the IPX functions, except network and node addresses. } + +Type TSPXconnectionInformation + =record + ConnectionState :Byte; { all fields are returned hi-lo } + WatchDogState :Byte; + LocalSPXConnectionId :Word; + RemoteSPXConnectionId :Word; + SequenceNumber :Word; + LocalAcknowledgeNumber :Word; + LocalAllocationNumber :Word; + RemoteAcknowledgeNumber:Word; + RemoteAllocationNumber :Word; + LocalSocket :Word; + ImmediateAddress :TnodeAddress; + RemoteAddress :TinterNetworkAddress; + RetransmissionCount :Word; + EstimatedRoundTripDelay:Word; + RetransmittedPackets :Word; + SuppressedPackets :Word; + end; + +Function SPXpresent:boolean; +{ Determines if SPX is installed. Calls SPXInitialize. } + +{IPX/SPX: 10h} +Function SpxInitialize(Var SPXhiVer,SPXloVer:Byte; + Var MaxConn,AvailConn:word):boolean; +{ Determines if SPX is loaded. (this function also tests the presence of IPX, } +{ as IPX is required for running SPX. Remember: Netware 2.2 allows IPX and SPX } +{ to be loaded seperately, so only IPX may be present. } + +{IPX/SPX: 11h} +Function SPXestablishConnection(retryCount:byte; WatchdogFlag:Byte; + {i/o} Var ECB:Tecb; + {out} Var SPXconnectionID:Word):boolean; + +{IPX/SPX: 12h} +Function SPXlistenForConnection(retryCount:Byte; WatchdogFlag: Byte; + Var ECB:Tecb ):boolean; + +{IPX/SPX: 15h} +Function SPXGetConnectionStatus(SPXconnectionID:word; + Var connInfo:TSPXconnectionInformation):boolean; + +{IPX/SPX: 13h} +Function SPXTerminateConnection(SPXconnectionID:Word; ECB:Tecb):boolean; + +{IPX/SPX: 14h} +Function SPXabortConnection(SPXconnectionID:Word):boolean; + +{IPX/SPX: 16h} +Function SPXsendSequencedPacket(SPXconnectionID:Word; Var ECB:Tecb):boolean; + +{IPX/SPX: 17h} +Function SPXlistenForSequencedPacket(Var ECB:Tecb):boolean; + +{************** Secondary Procedures ***************************************} + +IMPLEMENTATION {==============================================================} + +CONST + SPX_WATCHDOG_ENABLED =1; + SPX_WATCHDOG_DISABLED =0; + SPX_DEFAULT_RETRY_COUNT =0; + + SPX_POOL_SIZE =10; + + SPX_MAX_DATA_LENGTH =534; + +{IPX/SPX: 10 } +Function SpxInitialize(Var SPXhiVer,SPXloVer:Byte; + Var MaxConn,AvailConn:word):boolean; +Var Regs:registers; +begin +With regs + do begin + al:=$00; + bx:=$0010; + IpxSpxSystemCall(Regs); + result:=regs.al; + + if AL<>$FF + then begin + result:=$FF; + SpxInitialize:=false + end + else begin + SPXhiVer:=BH; + SPXloVer:=BL; + MaxConn:=CX; + AvailConn:=DX; + result:=$00; + SpxInitialize:=true; + end; + end; {with} +{ resultcodes: $00 successfull (SPX installed); $FF SPX not installed } +end; + +Function SpxPresent:boolean; +Var SpxHi,SpxLo:Byte; + MaxConn,AvConn:word; +begin +SpxPresent:=( IpxPresent and SpxInitialize(SpxHi,SpxLo,MaxConn,AvConn) ); +end; + +{IPX/SPX: 11h} +Function SPXestablishConnection(retryCount:byte; WatchdogFlag:Byte; + {i/o} Var ECB:Tecb; + {out} Var SPXconnectionID:Word):boolean; +Var regs:registers; +begin +With regs + do begin + BX:=$0011; + AL:=retryCount; + AH:=WatchDogFlag; + ES:=Seg(ECB); + SI:=ofs(ECB); + end; +IpxSpxSystemCall(Regs); +result:=regs.AL; +SPXconnectionID:=regs.DX; +SPXestablishConnection:=(result=$00); +{ resultcodes: $00 SPX Attempting to contact destination socket; + $EF Local connection table full; + $FD Fragment count not 1 AND/OR Buffer size not 42; + $FF Send Socket not open OR IPX/SPX not initialized. } +end; + + +{IPX/SPX: 12h} +Function SPXlistenForConnection(retryCount:Byte; WatchdogFlag: Byte; + Var ECB:Tecb ):boolean; +Var regs:Registers; +begin +With regs + do begin + BX:=$0012; + AL:=retryCount; + AH:=WatchdogFlag; + ES:=Seg(ECB); + SI:=Ofs(ECB); + IpxSpxSystemCall(Regs); + result:=AL; + if result<>$FF + then result:=$00; + end; +SPXlistenForConnection:=(result=$00); +end; + +{IPX/SPX: 15h} +Function SPXGetConnectionStatus(SPXconnectionID:word; + Var connInfo:TSPXconnectionInformation):boolean; +Var regs:Registers; +begin +With regs + do begin + BX:=$0015; + DX:=SPXconnectionID; + ES:=Seg(connInfo); + SI:=Ofs(connInfo); + IpxSpxSystemCall(Regs); + Result:=AL; + end; +if result=0 + then begin + With ConnInfo + do begin { force all returned words lo-hi } + LocalSPXConnectionId :=Swap(LocalSPXconnectionID); + RemoteSPXConnectionId :=Swap(RemoteSPXconnectionID); + SequenceNumber :=swap(SequenceNumber); + LocalAcknowledgeNumber :=swap(LocalAcknowledgeNumber); + LocalAllocationNumber :=swap(LocalAllocationNumber); + RemoteAcknowledgeNumber:=swap(RemoteAcknowledgeNumber); + RemoteAllocationNumber :=swap(RemoteAllocationNumber); + LocalSocket :=swap(LocalSocket); + RemoteAddress.socket :=swap(remoteAddress.socket); + RetransmissionCount :=swap(RetransmissionCount); + EstimatedRoundTripDelay:=swap(EstimatedRoundTripDelay); + RetransmittedPackets :=swap(RetransmittedPackets); + SuppressedPackets :=swap(SuppressedPackets); + end; + end; +SPXGetConnectionStatus:=(result=0); +{ Resulcodes: $00 Connection is active; $EE No such connection } +end; + + +{IPX/SPX: 13h} +Function SPXTerminateConnection(SPXconnectionID:Word; ECB:Tecb):boolean; +Var regs:Registers; +begin +with regs + do begin + BX:=$0013; + DX:=SPXconnectionID; + ES:=seg(ECB); + SI:=Ofs(ECB); + end; +IpxSpxSystemCall(Regs); +result:=regs.al; +if result<>$FF + then result:=$00; +SPXterminateConnection:=(result=0); +{resultcodes: $00 SPX attempting to break connection; + $FF IPX/SPX not loaded. } +end; + +{IPX/SPX: 14h} +Function SPXabortConnection(SPXconnectionID:Word):boolean; +Var regs:Registers; +begin +with regs + do begin + BX:=$0014; + DX:=SPXconnectionID; + end; +IpxSpxSystemCall(Regs); +result:=regs.al; +if result<>$FF + then result:=$00; +SPXabortConnection:=(result=0); +{resultcodes: $00 SPX trying to unilateral break the connection; + $FF IPX/SPX not loaded. } +end; + +{IPX/SPX: 16h} +Function SPXsendSequencedPacket(SPXconnectionID:Word; Var ECB:Tecb):boolean; +Var regs:Registers; +begin +with regs + do begin + BX:=$0016; + DX:=SPXconnectionID; + ES:=Seg(ECB); + SI:=Ofs(ECB); + end; +IpxSpxSystemCall(Regs); +result:=regs.al; +if result<>$FF + then result:=$00; +SPXsendSequencedPacket:=(result=0); +{resultcodes: $00 SPX will attempt to send packet; + $FF IPX/SPX not loaded. } +end; + +{IPX/SPX: 17h} +Function SPXlistenForSequencedPacket(Var ECB:Tecb):boolean; +Var regs:Registers; +begin +with regs + do begin + BX:=$0017; + ES:=Seg(ECB); + SI:=Ofs(ECB); + end; +IpxSpxSystemCall(Regs); +result:=regs.al; +if result<>$FF + then result:=$00; +SPXlistenForSequencedPacket:=(result=0); +{resultcodes: $00 SPX waits for incoming packets; + $FF IPX/SPX not loaded. } +end; + +{************** Secondary Procedures ***************************************} + +end. diff --git a/SRC/UNITS/SENDMSG.DCU b/SRC/UNITS/SENDMSG.DCU new file mode 100644 index 0000000000000000000000000000000000000000..208d45d2f8682487888b24e559aca686f78a8e8f GIT binary patch literal 2256 zcmd6oUrbw79LIm>+}qnfv`}!F%#pRs&G0bi_+vthF&!7tts1d(LfdVSDjS51jis z=X-wV+~4`#-#HEVdm0I4o1*lC@~g6kKTXxtMK!OktDEX7eZ+`9M^))LrMAYwTj(<} z;a`?3(ykBljna%(kZqT`PjQbQmUa(!(VM~wo9ttzvlGQt@0hEXjqL|Ci!~hA2Szf3 zL>GS9C24Na!eg0ie1xc_QrP0(V(T5&BV(y}hG;Z;g6*1OD4pzw_{U$(%+7?R)t`uE zGQ#lBtswl6vxDhaYG_y(-v7CeHA~UbkxY+3`MyJELBq_S)U)Yi0^;)e6zenAYkN1F zP3q7dTopT$>xHue{%l$pzVrJko06iYU+#`)gmmT(Vj%6>N3%t{_m+rOg~%%-ZCSl7 ziA6^OiNThK>=D_?{-TP=5jprH1j^p6%5WJy8!6UBCPvS4AOG!moQ;zW<2(D(h*~8o z@l5ayzQXh7cTy~GT)@k6BB8TJY zdIxR5L*YF{H5^a8bzYN_@L8076xkrJgj+s6EX9O=irlau>Y@gz8#zGaB=bP1bl@Nu zo#Fvdshs#2C?~v=N{J4+oZwEfnu%RYwsJz-$V_A#*~$s*rqWS`Uy&~tp05bEzaHog z2hWDihmCNMDFr&Z#THJ>-FwjT;*P3%$x+&p+}}3hw8`w`;|_}O3S_U7Cb^3icqOgy zD#~*==2z1mKL&beS*a%7Wg*=L+B~=yEl<@4STEFg($ufJ9CY1PfxbqAbuQFO`pQ{_ z>ZaS?da2KGgO_WZ@aGt+2Y%GUk9wTUqe#&i>2LY5h!6b#Do$h`E>y-l#_e$byNt7cnEZTf~7x%?$g(&th3x0!BI12sk}_Wx)c(#$|y(s!*uf4HApmX=s!mTlEySo!C=*l*x?c8LH$R zSnwoBJO=NY-0YKrfe~3KA zJ)x9X4p@5H-RtTTa;t1uvysRx**%XHy(7Nhst^n|g0cR&IC}tvs zKF$>t+&PE{PuwYy5~t+AXOwVtNoblW`#b2E{u519X1}kshnoDx7fcio#>`_lUUbTD z=7jaDRdzGHBu;l4xe?~ljYvUn&Cyas$GMfBUH-IOtZz#PtY&FIvtg7T*PKewx)EhKClJo%u+XYnBU!oA z0va^Ia}6qM1?rWcrv_xDJ5KlBuiityTqWN9#W^v83@5Av{Q&=hUd)W=?noZHudZ{= za!=0ATtCAFj`gkOYXBhfsL~;tC{5a5!B(UF1ymxZJNEM?!t-Dowgq6ob$0Orw#y|b z-|J(_6Ih=wg0VBjMM}A4!6?Tu<*BvVvt~E9X67{K3v>8?vl{3k zp{lVhdm;2I^Pb(O`J^?)s|n$$-o_31b6cyvT@a@oU56J}A4gGe1;7L9UL-eFQ@KJ{NAZmgmWmayQBeUTFW0006%Mi=`v* literal 0 HcmV?d00001 diff --git a/SRC/UNITS/SENDMSG.PAS b/SRC/UNITS/SENDMSG.PAS new file mode 100644 index 0000000..46b030c --- /dev/null +++ b/SRC/UNITS/SENDMSG.PAS @@ -0,0 +1,37 @@ +unit Sendmsg; + +interface + +uses WinTypes, WinProcs, Classes, Graphics, Forms, Controls, Buttons, + StdCtrls, nwMess, ExtCtrls; + +type + TBtnBottomDlg4 = class(TForm) + OKBtn: TBitBtn; + CancelBtn: TBitBtn; + Bevel1: TBevel; + Edit1: TEdit; + Edit2: TEdit; + Label1: TLabel; + Label2: TLabel; + procedure OKBtnClick(Sender: TObject); + private + { Private declarations } + public + { Public declarations } + end; + +var + BtnBottomDlg4: TBtnBottomDlg4; + +implementation + +{$R *.DFM} + +procedure TBtnBottomDlg4.OKBtnClick(Sender: TObject); +begin + SendMessageToUser(Edit1.Text, Edit2.Text); + Close; +end; + +end. diff --git a/SRC/USER.DCU b/SRC/USER.DCU new file mode 100644 index 0000000000000000000000000000000000000000..1808a1eed4ddc6aa157b43f03d1e9caddceedc63 GIT binary patch literal 5808 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 NJ(s0-kEOSm{tY259TETl literal 0 HcmV?d00001 diff --git a/SRC/USER.DFM b/SRC/USER.DFM new file mode 100644 index 0000000000000000000000000000000000000000..f8dfd634c1513ece762c772b76fffa316c2e3fde GIT binary patch literal 2684 zcmd5-zi-<{6h2Y5s2^5kv`gDbV~4^}a{-6|29#2YQv?15h(Xbm*Q~6b)}&{DswY&mUO+Zr^i7 z?Hcuh=Z>Y8k44uu9q&}_nB&0q+*0+?zz&*EgylCkI*x6*!8ewDIt+@xv08?|wFkkl z_~qTzM+nsI50&rBN1m`LB=w`Q*X#S1<#rH_<*SG8k!ibu3iJL*zLpN%-p~tG z*!4#p-!cQMYFOua=t`ZH4Ba}joRaLn%F^#2~O8G%wvGv~299yb-EOvYZ zU)dEM9M$)n3WKFR^B9(7pIiD=TH4Gly<)i~rM}4O=ahsmn^|6WyY%MT=S`YjF?ml0 zS7e3vvspIwY!T?*c{v}KLT%=u{&an4#Z*D$vym$lrMeqfqnyHe*)Wd}sd^N~m}=;D zfI3sNAvf8SW%iO~HkI$LPygBC^yRMy19|itYXz`~a_T=-H*#YVRUbRg$HT3z8JN`{ z3XGkh>HE}^oS}Vz)2TwIU>-a=An=a>Nt;9Q^Q`xh7+XqmUE zgGj$I>2toFV+{@@euD{~rtsK~xBq~=xgdY=7%pQ$>U>-3XC7dJ>>++&ijov^jPV-F zN!e8no{c0KsEAw4=S4bUZ>7G{wVbUL?v+*U@7?151sFe<+*42TpWI{ZV{lJxStkFV z`xaS}X+d&hq$vr*G2u9l6T-8}MB)UJ;qeN7r*+z%#BmGu#^V;(G&zAPAp1qIPFP5P z%l5|*iig%gtvopClSG_1Uj7({z!54r1zuW*J>HVXfq|652bRaBI-tT}>`wN1c12C! zf&Imw$<3EvTxFksXzb_tLLy{7F*cCKSvyXnxZO?|l9e{tEKI_%EkbQto6n&z!*||B zu9GQlmPR_8p>RSGC)#vApU-9u!tEr9G+JjhiE%dwxjvs^f855m&9M%;U?5E4Ucg^% z&R(bRC(=;kcv{#Ee;`Z1WdinI)2#*eG>$!-!+4`H<6J}<9xOw>QAD*Myh!j$5g6*B zh|(15EvgSx3yj0z+t|OKvHwv1K^5=1_SZX(C#)Oh*H)Roe~bAK<<1A^kUulI!ktSJ zPvv*St~BHc5>FEv##dTQ+FCr7-1 then + begin + UserName:=''; Int1:=1; + While ListBox1.Items[ListBox1.ItemIndex][Int1]<>',' do + begin + UserName:=UserName+ListBox1.Items[ListBox1.ItemIndex][Int1]; + Int1:=Int1+1; + end; + if UserName='SUPERVISOR' then MessageDlg('Deleting SUPERVISOR would be a VERY BAD idea!', mtWarning, [mbOK], 0); + if MessageDlg('Delete user '+UserName+'?', mtConfirmation, [mbYes, mbNo], 0)=mrYes + then begin + if DeleteBinderyObject(UserName,OT_USER)<>TRUE then + MessageDlg('Could not remove user '+UserName, mtError, [mbCancel], 0); + end; + Paint; + end; +end; + +procedure TBtnBottomDlg.FormPaint(Sender: TObject); +begin + ListBox1.Items.Clear; + FormCreate(self); +end; + +procedure TBtnBottomDlg.BitBtn1Click(Sender: TObject); +begin + BtnBottomDlg2.ShowModal; + FormPaint(self); +end; + +procedure TBtnBottomDlg.BitBtn3Click(Sender: TObject); +begin + if ListBox1.ItemIndex<>-1 then BtnBottomDlg3.ShowModal; + FormPaint(self); +end; + +end.