SECURITY APPARATUS AND METHOD

Abstract: A system for locating and monitoring electronic devices utilizing a security system that is secretly and transparently embedded within the software, firmware, or hardware of a computer. This security system causes the client computer to periodically and conditionally call a host system to report its serial number via an encoded series of dialed numbers. A host monitoring system receives calls from various clients and determines which calls to accept and which to reject. This determination is made by comparing the decoded client serial numbers with a predefined and updated list of numbers corresponding to reported stolen computers. Only calls from clients on the predefined list are accepted. The host also concurrently obtains the caller ID of the calling client to determine the physical location of the client computer. The caller ID, indicating the physical location of the stolen device, and the serial number are subsequently transmitted to a notifying station in order to facilitate the recovery of the stolen device. The security system remains hidden from the user, and actively resists attempts to disable it.

Description: SECURITY APPARATUS AND METHOD BACKGROUND OF THE INVENTION Many electronic devices, such as laptop computers and cellular telephones, are becoming more compact and portable. While such portability is extremely convenient for the user, it has given rise to an increased risk of theft. These electronic devices are often very expensive and are easily lost or stolen. Previously, attempts have been made to provide means for retrieving lost or stolen items of various types. The simplest approach is marking the item with the name and the address of the owner, or some other identification such as a driver's license number. If the item falls into the hands of an honest person, then the owner can be located. However, this approach may not deter a thief who can remove visible markings on the device. Password protection schemes are of dubious value in discouraging theft or retrieving an item. Although the data can be protected from theft, the computer hardware cannot be found or retrieved. Another approach has been to place a radio transmitter on the item. This has been done in the context of automobile anti-theft devices. The police or a commercial organization monitors the applicable radio frequency to try to locate a stolen vehicle. This method is not suitable for smaller items such as cellular telephones or laptop computers. First, it is inconvenient to disassemble such devices in order to attempt to install a transmitter therein. Second, there may not be any convenient space available to affix such a transmitter. Furthermore, a rather elaborate monitoring service, including directional antennas or the like, is required to trace the source of radio transmissions. It is therefore an object of the invention to provide an improved means for tracing or locating smaller lost or stolen objects, particularly laptop computers, cellular telephones, desktop computers and other small, portable electronic devices or expensive home and office electronic equipment. It is also an object of the invention to provide an improved means for tracing such electronic devices which can be installed without disassembly or physical alteration of the devices concerned. It is a further object of the invention to provide an improved means for locating lost or stolen items, this means being hidden from unauthorized users in order to reduce the risk of such means being disabled by the unauthorized user. It is a still further object of the invention to provide an improved means for locating lost or stolen items which actively resist attempts to disable the means by an unauthorized user. SUBSTITUTE SHEET It is a still further object of the invention to provide an improved means for inexpensively and reliably locating lost or stolen items. The invention overcomes disadvantages associated with the prior art by yielding a security device for small computers, cellular telephones or the like which can be programmed onto existing memory devices such as ROM devices, hard disks or the like. Accordingly, no physical alteration is necessary or apparent to a thief. The existence of the security device is well cloaked and it cannot be readily located or disabled even if the possibility of its existence is suspected. Apparatuses and methods according to the invention can be very cost effective, requiring relatively inexpensive modifications to software or hardware and operation of relatively few monitoring devices. SUMMARY OF THE INVENTION This invention, Electronic Article Surveillance System, relates to a security apparatus and method for retrieving lost or stolen electronic devices, such as portable computers. This invention enables electronic articles to be surveyed or monitored by implanting an intelligent Agent with a pre-defined task set onto an electronic device. This Agent communicates with a preselected Host Monitoring System which is capable of multiple services including; tracing location, identifying the serial number, and electronically notifying the end user/owner of its location. The Agent hides within the software/firmware/hardware of the electronic device, and operates without interfering with the regular operation of the device. The Agent is designed to evade detection and resist possible attempts to disable it by an unauthorized user. According to one aspect of the invention there is provided an electronic device with an integral security system. The security system includes means for sending signals to a remote station at spaced apart intervals of time. The signals including identifying indicia for the device. Preferably, the means for sending signals includes a telecommunications interface connectable to a telecommunications system, and means for dialing a preselected telecommunications number. The remote station includes a telecommunications receiver having said preselected telecommunications number. Where die electronic device is a computer, the means for sending signals includes means for providing signals to the telecommunication interface to dial the preselected telecommunication number and send the identifying indicia. The telecommunication interface may include a modem. The means for providing signals may include security software programmed on the computer. SUBSTITUTE SHEET The Agent security system may be recorded on die boot sector of a hard disk or, alternatively, on a hidden system file such as IO.SYS, MSDOS.SYS, IBMBIO.COM or IBMD0S.COM. There is provided according to another aspect of the invention a method for tracing lost or stolen electronic devices whereby a telecommunications interface is connectable to a telecommunications system at a first telecommunications station. The method includes providing the electronic device with means for sending signals to the telecommunications interface. The means is instructed by the program to send first signals to the telecommunications interface which dials a remote telecommunications station. These first signals contain the encoded identification (serial number) of the sending computer. The telecommunications interface then dials a remote telecommunications station corresponding to the intended receiving computer. Upon detecting a ring signal, the remote computer retrieves the caller phone number and the identification of the sending computer from the telephone company. The remote computer decodes the serial number of the sending computer, and compares it with a predefined listing of serial numbers of lost or stolen computers. The call will only be answered if the sending computer is on the predefined list. In an alternative embodiment, if the remote computer answers the ring then the means for sending signals automatically sends second signals to die telecommunications interface, which transmits to the remote telecommunications station identifying indicia for the device as well as any other pertinent information. There is provided according to another aspect of the invention a method for encoding the serial number of the sending computer within a sequential series of dialed numbers. In this method, a predetermined digit within the dialed number sequence corresponds to one of the digits of the serial number. The preceding digit within the encoded signal indicates which digit within the serial number sequence that the predetermined digit represents. BRIEF DESCRIPTION OF THE DRAWINGS These and other objects and advantages will become apparent by reference to the following detailed description and accompanying drawings, in which: FIG. 1 is a functional block diagram of the Electronic Article Surveillance System in accordance with the teachings of this invention. SUBSTITUTE SHEET FIG. 2 is a simplified illustration of FIG. 1 for the purpose of showing an illustrative embodiment of the present invention. FIG. 2A is a flowchart of the process by which the operating system and Agent are able to start up and run simultaneously. FIG. 2B is a flowchart of the process by which the Host Identification and Filtering Subsystem identifies and filters out unwanted calls from Agents. FIG. 2C is a flowchart of the process by which the Host Processing, Auditing and Communication Subsystem, contained within the host computer, exchanges data with an Agent. FIG. 2D is a flowchart of the process by which the Host Notification Subsystem, contained within the host computer, notifies end-users of the status of monitored devices. FIG. 3 is a flowchart showing the conventional method of booting up a personal computer with alternative loading points for the Agent security system shown in broken lines. FIG. 3A is a flowchart showing a method for startup loading of an Agent security system according to an embodiment of the invention wherein the operating system boot sector is loaded with the Agent. FIG. 3B is a flowchart similar to FIG. 3 A wherein the hidden system file IO.SYS or IBMBIO.COM is modified to be loaded with the Agent. FIG. 3C is a flowchart similar to FIG. 3 A and 3B wherein the partition boot sector is modified to be loaded with the Agent. FIG. 3D is a flowchart similar to FIG. 3B and 3C wherein die Agent security system is ROM BIOS based. SUBSTITUTE SHEET FIG. 3F, 3G are portions of a flowchart showing the Agents' work cycle apparatus and method according to an embodiment of the invention. FIG. 3H is an isometric view, partly diagrammatic, of the physical structure of a computer disc. FIG. 4 is a schematic showing the encoding/decoding method whereby the monitoring service would have to subscribe to 60 telephone numbers. FIG. 4 A is a schematic showing the encoding/decoding method whereby the monitoring service would have to subscribe to 300 telephone numbers. DESCRIPTION OF THE PREFERRED EMBODIMENTS System Overview Referring to Figure 1, the Electronic Article Surveillance System is comprised of three main components: (1) Client device A consisting of any electronic device which has been implanted with the Agent; (2) A telecommunication link B such as a switched communications system, cable networks, radio/microwave signal; and (3) The host monitoring system C which controls the communications between the client device A and d e host monitoring system C. Referring to FIG. 1, the client device can be a cablevision device A2, laptop computer A3, or other type of electronic device A4. However, for illustrative purposes, the client device consists of a computer Al attached to modem M. The host monitoring system C sends and receives data packets from the client computer 10 over a suitable bi-directional transmission medium, such as a common telephone line LI. Telephone line LI couples the client device C to die host monitoring system C, and the host computer 3, through Public Switch BI (telephone company). The host monitoring system C notifies the appropriate parties C3 (owner O, law enforcement agency, or monitoring company) of the status of the client device A via suitable communication means such as electronic mail Nl, fax N2, telephone N3 or pager N4. Host monitoring system C also identifies and filters incoming calls Cl, and also provides processing, auditing and communication functions C2 . SUBSTITUTE SHEET In another embodiment of the invention cablevision device A2 is connected to cablevision network B2 via cable L2. This cable L2 further connects cablevision network L2 to the host monitoring system C. In another embodiment of die invention laptop computer A3 is connected to radio tower B3 via radio frequency (RF) transmissions L3. These RF transmissions are received by satellite dish S at the host monitoring system C. In yet another embodiment of the invention electronic device A4 is connected to satellite B4 via microwave signal L4. Microwave signal L4 further connects satellite B4 to satellite dish S at die host monitoring system C. Referring to FIG. 2, the Host Monitoring system C is comprised of a Voice Board 2, Host Monitoring Computer 3, Hard Disk Controller 4, Hard Disk 5, CRT 6, Keyboard 7, and Printer 8. The host monitoring computer 3 is coupled to a suitable display device, such as a CRT monitor 6, keyboard 7, and to printer 8. The keyboard 7 permits the operator to interact with the Host Monitoring System C. For example, die operator may use keyboard 7 to enter commands to print out a log file of the clients that have called into the system. The host computer 3 illustratively takes the form of an IBM personal computer. The source codes for the host monitoring system C, in Visual C+ + by Microsoft, are attached in the Appendix. Telephone line 1 is connected to die computer 3 by a voice board 2 adapted to receive and recognize the audible tones of both caller ID and dialed numbers transmitted via die telephone line 1. Client computer 10 is connected to modem 9 via serial ports 9a. Host computer 3 is connected to voice board 2 via serial port 2a. The modem 9 and voice board 2 are connected to telephone line 1 which is routed through public switch 9b in accordance with a conventional telephone system. Computer 10 and modem 9 form a first telecommunication station, while computer 3 and voice board 2 form a second, or remote telecommunications system. The Host Monitoring System C sends and receives data packets from client computer 10. Ring signals are received on phone line 1 as an input to voice board 2. In an illustrative embodiment of the invention, voice board 2 may take the form of the DID/120, DTI/211 and D/12X Voice boards manufactured by Dialogic Corporation. The voice board 2 is coupled to host computer 3 via data bus 2a. The voice board 2 is operative to recognize die ring signal. Then it receives the caller ID and dialed numbers and converts them into corresponding digital signals. Host computer 3 uses these signals for comparison against a list stored in hard disk 5. In an illustrative embodiment of die invention, the hard disk controller 4 may comprise memory control boards manufactured by Seagate Tech under the designation Hard Disk Controller. The hard disk controller 4 is particularly suitable to control the illustrative embodiment of die hard disk memory 5 manufactured by Seagate Tech under dieir designation ST-251. The Agent is a terminated and stay resident program which is installed on hardware, software, or firmware. The alternative methods of installation are described in detail in FIGS. 3A, 3B, 3C, and 3D. Once the Agent is installed it will report its identity and its location to the host after specified periods of time have elapsed, and upon die occurrence of certain predetermined conditions. This is further illustrated in FIG. 2A. Client source codes are disclosed, in Tazam Assembler Code by Borland, in die Appendix. Installing and Loading the Agent The Agent is installed during a typical boot up sequence to die operating system of a computer. FIG. 3 shows a boot-up process for a typical personal computer. The details of die boot up process are discussed in Appendix I. It should be understood that this invention is applicable to other types of computers and electronic devices presently available or as marketed in die future with suitable modifications. The aspect of die invention described below is the process of installing die security software onto a portable computer such as client computer 10. The method of installation is crucial because die software must remain undetectable once installed. Furthermore, the software should be as difficult as possible to erase. In summary, the invention achieves these objects by installing the software in such a manner that it remains hidden to die operating system, such as MS-DOS. Three alternative ways of installing the Agent security system during die disk boot are illustrated in FIG. 3A-3C respectively. A conventional boot up method is described in detail in Appendix I. A fourth alternative, installing via ROM, is shown in FIG. 3D. The system can also be installed with MS. SYS or IBMDOS.COM, but these are more difficult and less preferred than the dtree alternatives set out below. The loading program TENDER (further described in die Appendix) can be used to install the Agent by one or more of these alternative installation mediods. Thus, die Agent may be installed in a variety of locations whereby second and third Agents can provide back up support for the primary Agent. The three locations where the Agent can be installed on die client device are as follows: 1. The operating system boot sector- See FIG. 3A. SUBSTITUTE SHEET 2. A hidden system file such as IO.SYS for MS-DOS or IBMBIO.COM for PC- DOS- See FIG. 3B. 3. The partition boot sector- See FIG. 3C. Referring to FIG. 3A, the Agent loading sequence is described for loading the Agent on die operating system boot sector. The computer 10 is powered on and the loading sequence begins 64. As is well known in die art, the computer 10 performs an initial testing routine to assure that all components are working properly 65. Illustratively, the program incorporated is the IBM-PC compatible Power-On Self Test (POST) routine. The partition boot sector is loaded 66. Next die operating system boot sector with die installed Agent is loaded 67. In an effort to maintain die transparency of the Agent, die CPU registers (corresponding to die current state of the computer) are saved 68. Before die Agent is installed there is a check for a Remote Procedure Load (RPL) signature 69. If the signature is present this indicates that the Agent is already in memory and will not be loaded again. However, if there is no RPL signature then preparation is made to load die Agent. First, space is reserved for the Agent at die ceiling of conventional memory 70. Next, Interprocess Communication Interrupt (2Fh) is hooked 71 which enables communication with other programs. Interrupt 13h, which is the disc input/output handler, is hooked 72. The old timer interrupt is saved, and new hook timer interrupt is put into place 73. Now the CPU registers are restored 74 in order to maintain the transparency of the system. The original operating system boot sector is loaded 75. The original operating system had been moved to accommodate die Agent installation. Finally, die operating system is loaded 76 and running 77 again. Referring to FIG. 3B, die Agent loading sequence is described 78-91 for loading die Agent on a hidden system file such as IO.SYS for MS-DOS or IBMBIO.COM for PC-DOS. The sequence is analogous to that disclosed above for the operating system boot sector. However, instead of lading the Agent with the operating system boot sector, die Agent is loaded with die operating system file 82 (load modified IO.SYS or IBMBIO.COM). Referring to FIG. 3C, the Agent loading sequence is described 92-104 for loading the Agent on die partition boot sector. The sequence is analogous to that disclosed above for the operating system boot sector. However, instead of loading die Agent with the operating system boot sector, the Agent is loaded with the operating system partition boot sector 94. Referring to FIG. 3D, the Agent loading sequence is described 105-116 for loading the Agent via ROM BIOS. This schematic illustrates an embodiment of this invention on firmware. The sequence is analogous to that disclosed above for the operating boot sector. However, die SUBSTITUTE SHEET Agent is loaded from the ROM after die CPU registers are saved 107. At that time the ROM can take control of the system and load die Agent. Once die CPU registers are restored 113, die ROM can no longer load the Agent. FIG. 2A is a flow chart of die Agent Work Cycle. This Work Cycle describes the method by which die Agent is loaded when die computer 10 is initially turned on, and die manner in which the operating system and die Agent run simultaneously. Once the client computer 10 is powered on 11, it performs a power on self-test (POST) 12. The POST tests the system hardware, initializes some of the devices for operation, and loads the master boot record (MBR) 13. Since the MBR was installed with an Agent Subloader, the Subloader is loaded into memory 14 and executed. The Subloader's first task is to load die Agent 15 into memory. Then die Subloader loads die operating system (OS) into memory 16 and returns control to the operating system. Now both the operating system 17 and the Agent 18 are running simultaneously. Functions of the Agent Referring to Figure 2A, the Agent's primary job is to determine die appropriate time for it to call die Host Monitoring System (Host) 19 to report its status (such as identity, location and other information). Secondarily, like any terminated and stay resident program, the Agent will not interfere with any running applications unless designed to interfere. Thus, the Agent can avoid being detected. The Agent will determine if it should call the Host 18 times per second. The Agent will only call the host when a pre-defined time period has elapsed, or a pre¬ determined event has occurred which triggers the client to contact the host. The Agent compares the current date and time with the date and time corresponding to die next time that the Agent is due to call the host. If die Agent determines that it is time to call the Host, it will do a thorough search within the computer 10 to find free (not currently being used by any running application) communication equipment 20. In an illustrative embodiment, die communication equipment is a modem 9. If die agent fails to find any free equipment, dien it will abort its attempt to call the Host and repeat the cycle 18. However if the Agent locates free communication equipment, it will call the Host 21. Upon receiving a call from the client 10, the Host examines the Agent identity and determines if a connection should be established 22. If the Host does not accept the call then the Agent will not call back until the next appropriate time (after predetermined time period has elapsed) 18. If die Host accepts the call, dien die Agent will send die Host its encoded identity (serial number), location (caller ID) and any other pertinent information such as local date and time 23. The Agent then checks if the Host has any data or commands for die client 24. If the Host has no data or commands to be sent, then the Agent will terminate the call and repeat the cycle 18. Otherwise, the client will receive the data or commands from the Host before it terminates the call and repeats the cycle 18. This Work Cycle is described in much greater detail in FIGS. 3F and 3G and is described in die Detailed Operation section. The system remains transparent to an unauthorized user via implementation of well known deflection methods. Attempts to read or write to the location where die Agent has been installed are deflected in order to prevent discovery of the Agent. When read attempts are made to die Agent location the system generates meaningless bytes of data to be returned to die user. When write attempts are made to the location where the Agent is installed, the client computer 10 accepts the input data and informs the user that die write has been successful. However, the data is not really stored, and thus the Agent is preserved. In the Appendix, die source code for the disk deflection routines are disclosed within file SNTLI13V.ASM. Detailed Operation of Agent Work Cvcle Referring to FIG. 3F, the following is a description of what happens during die period of time when the Agent security system is in "active" mode 117, 118: Once die system is powered on, die timer interrupt will occur 18.2 times per second 117. Every 18 timer interrupts, die complementary metal-oxide semiconductor (CMOS) real-time clock will be accessed, and die time and date will be stored for comparison with die previous real-time clock access. If the date and/or time changes towards die future, no action will be taken to track the time displacement. In this way the Agent determines whether it is time to call the host 118. Thus if the current date has advanced far enough into the future (past the date and time to call die host), die Agent security system will change its mode of operation from active to alert 119 whereby calls will be regularly attempted until a call is made and a transaction with the host server has been completed. If the system time has been backdated, this will also cause a modal change from active to alert. Referring to FIGS. 3F and 3G, the following is a description of what happens when die Agent security system is in "alert" mode 119-161: The communications ports are checked 119-125 (via a port address table 120) to see if they exist. If die first one encountered is not in use 123, it will be dynamically hooked 126 into by swapping die appropriate interrupt handler and unmasking die appropriate interrupt request line. If an error occurs, die next port will be checked 124 until either a valid port is found or the port address table has been exhausted 125. Appropriate cleanup routines restore "swapped" ports to their initial settings. If die communications port responds properly, the system will dien attempt to connect to a modem via issue of die Hayes compatible AT command 128. If die modem does not exist, dien die next port will be checked 124. If the modem responds with an OK' to the AT command 129, die system will attempt to initialize die modem by sending it a modem initialization string 130, 132 (from a table of initialization strings 131). If die modem does not respond with an "OK" 134, this indicates that the initialization attempt failed 135. If die initialization attempt failed, dien the next string in the table will be tried 136, and so on until a valid initialization string is found 134, or the modem initialization string table is exhausted 136 (at which point, the routine will delay for some seconds dien try again from the start, using the first initialization string 130). Once a valid and available communications port has been found, and it has been verified that a functional modem is associated with that port, the system will attempt to dial out to the remote host server 137, 138. A dial string table 140 is used 139 to attempt the call since a PBX or switchboard etc. may need to be exited via a dialing prefix. If successful 141-143, the CONNECT result code (numeric or letters) from die remote host server will be received by die client 143. The host will send a signal ("Query") to the client requesting its serial number. If the client does not receive the query signal 148 it will abort 149 and repeat the cycle 119. If the client receives the "Query" signal, dien the serial number is sent 151. At this point, telecommunications have been established and die client-server transaction begins. If the transaction succeeds, die resultant state will be "active", otherwise "alert". If, for some reason, a "NO DIALTONE" event happens 144, a delay will occur 147 and die next dial string 141 will be attempted. If the line is "BUSY" 145, dien a redial attempt 146 will occur using die same dial string for a predefined number of attempts or a telecommunications connection is made, whichever comes first. The client to remote host server transaction involves the sending of die computer serial number 151 via the telephone company or carrier service. The "Caller ID" is implicitly received by die remote server (typically during the initial telecommunications event known as "RING"). Upon the telecommunications event called "CONNECT", the remote host server sends the Agent security system client a vendor specific message called "QUERY" 148 which in effect tells the client to send die serial number. The sending of this serial number 151 involves the server acknowledging that it has indeed received 152 and processed 154 die serial number (validating it). The client computer will attempt to send this serial number a predefined number of times 153 SUB before it gives up (disconnect, cleanup, unhooks port 127, 155 and returns to "alert" mode 156). At this point, the modem disconnects 160. Any other cleanup necessary (such as changing the date of the last call to the present) will also be done here 160. Finally, the resultant state will be reset to active 161. If die computer diat called in was not reported stolen, no further action with regard to the computer system diat called in will be taken. If, however, the serial number transmitted to the remote host server matches one of the serial numbers on a currently valid list of stolen computers, further processing will occur to facilitate the recovery of die missing equipment. Such processing includes, but is not limited to, placing either an automatic or manual call to die local authorities in the vicinity of the missing equipment or the owner of such equipment. Host Identification and Filtering System The Host Identification and Filtering System identifies and filters out unwanted calls from Agents. FIG. 2B is a flow diagram of die Host Identification and Filtering program executed by host computer 3. Once die security program is executed 26, die voice board waits 27 for the ring signal on the telephone line 1. When a ring signal is detected 28, die voice board 2 acknowledges the incoming call by sending a signal to the telephone company 9B via telephone line 1 requesting that the caller ID and the dialed numbers be sent to it. The voice board dien waits until these numbers are received 29, 30. Once the caller ID and the dialed numbers have been received, they are saved to die hard disk 31, 32. The security program then compares the dialed numbers 33, which provide a coded version of the serial number of the client computer 10 (coding scheme explained in detail below), against a list of serial numbers stored on the hard disk 4. If no match is found, the program lets the phone ring until the client computer 10 hangs up the telephone line 1. In the preferred embodiment, die client computer is programmed to hang up after 30 seconds of unanswered ringing. However, if a match is found, die security program routes the call to an appropriate receiving line connected to a modem 35, which answers the call. Encoding of the client computer serial number Referring to FIG. 4, die serial number of client computer 10 is encoded within the dialed numbers it sends to the host 3. In the preferred embodiment of the invention, die client computer transmits its six digit serial number 170 to the host via a series of six complete dialed phone numbers 172. The first eight dialed digits after the first " 1" are meaningless. The ninth dialed digit "N" 175, indicates which digit position within the serial number that die tenth dialed CCT number corresponds to. The tenth dialed digit "D" provides die Nth digit of die serial number. The host computer 3 receives the six complete dialed phone numbers 172 and decodes diem 173 by looking at only the ninth and tenth dialed digits. The client computer serial number 174 is thus reproduced. For example, in die sequence "800-996-5511", die only relevant digits are the "11" portion. The first "1" indicates that the digit immediate to its right (1) is the first digit in the serial number. Similarly, in the sequence "800-996-5526", the "2" indicates that the number immediate to its right (6) is the second number in die serial number. The client 10, in total, dials six numbers 172 in order to convey its six-digit serial number to die host. In order to accommodate this method of serial number coding, the host monitoring system needs to subscribe to sixty different phone numbers. All sixty numbers should have the same first eight digits, and only vary from one another with respect to the last two digits. The ninth digit need only vary from " 1 " through "6" corresponding to die six digits within a serial code. However, the last digit must vary from "0" to "9". Referring to FIG. 4A, the coding system can alternatively be modified such that the client computer 10 need only call the host three times to convey its serial number 180. According to this coding method, two digits of die serial number 186 would be transmitted in each call. Thus, the eighdi dialed digit 185 would vary from "1" to "3", corresponding to die three packets of two digits 186 that make up the serial number 180. The ninth and tenth dialed digits 186 would vary from "0" through "9". However, this would require the operator of the monitoring system to subscribe to three hundred different phone numbers. Host Processing. Auditing and Communication Subsystem Referring to FIG. 2C, the Host Processing, Auditing and Communication Subsystem receives and transmits information to and from clients. FIG. 2C is a flow diagram of the Host Communication program executed by host computer 3. After the host computer 3 is powered on 36, communication equipment is instructed to wait 37 for the telecommunication begin signal from the client computer 10. The telecommunication equipment acknowledges die begin signal by initiating a session to communicate with the client computer 38. The program first checks the client computer 39 to establish that it is sending data packets 40, and then receives the packets 41. Next, the program determines if the client has any data or commands to be sent to the host 42. If not, die session is terminated 43, and the cycle is repeated 37. When all data packets have been received, die program permits the host to send data packets to the client computer. The program prepares to send data packets 44, and dien establishes diat there are SUBSTITUTE SHEET more data packets to be sent 45 before sending each packet 46. Once all data packets have been sent, die program terminates die session 43, hangs up die phone, and prepares to repeat die entire cycle 37. Host-side source codes are disclosed in die Appendix in Visual C+ + (Microsoft) Code. Host Notification Subsystem The Host Notification Subsystem notifies the end-users regarding the status of their electronic devices. In FIG. 1, various methods of notification such as; electronic mail Nl, fax N2, paging N4, and telephone call N3, are depicted. FIG. 2D is a flow diagram of the Host Notification program executed by host computer 3. The Host Notification program determines whether mere are any pending notification instructions or commands 48. If there are pending notifications, die information is retrieved 49. The program then determines the preferred preselected notification method 50, and formulates the message to be dispatched 51 according to die preselected notification method. This message is dispatched to the end-user 52. After dispatching the message, the program repeats the entire cycle 47. Host-side source codes are disclosed in the Appendix in Visual C+ + (Microsoft) Code. Variations and Alternatives The above description relates to the Agent security system installed and operating in a conventional PC with an Intel 80X86 microprocessor or equivalent and with a conventional MS- DOS or PC-DOS operating system. It will be recognized that die system can be modified to fit other types of computers including, for example, those sold under the trademark Macintosh. The system can easily be modified to suit other types of operating systems or computers as they develop in this rapidly advancing art. The above system is also intended to be added to existing computers without physical alteration. Another approach is to modify the ROM of such computers to contain the Agent security system as shown in FIG. 3D. This is generally not considered to be feasible for computers sold without die security feature, but is a theoretical possibility. More likely is the possibility of incorporating the Agent security system into the ROM of portable computers, cellular telephones or other such items when they are manufactured. FIG. 3D above describes the loading of die system from such a modified ROM. SUBSTITUTE SHEET The description above also assumes that die computer device has a modem connected thereto or includes an internal modem. In the future it is likely that telephone systems will be digitized, thus obviating die need for a modem. The system could also be included in die ROM of a cellular telephone. In this case, the program should be designed to hide die outgoing calls from the user by silencing audio signals and maintaining a normal screen display. It is also conceivable diat portable computers can be supplied with integral cellular telephones modified in this manner or with some other telecommunication device. It is not clear at the time of this invention exactly which direction die field of telecommunications will likely go in die immediate future. The main telecommunication criteria for this Agent security system is that the outgoing transmission (wire, radio signal or odierwise), be received by a switching mechanism, and contain information that causes die switching mechanism to forward die information received to a remote station. Presently, this information is a telephone number. But other indicia of die remote station may be substituted in alternative switchable communications systems. Attached hereto are appendices relating to the following: (1) Description of the conventional boot up method; (2) Details of agent installation; (3) Brief description of the routines; and (4) Copy of the source code of bodi the client-side and host-side. The host-side source code is in Visual C+ + (Microsoft). The client-side source code is in Tazam Assembler Code by Borland. It will be understood by someone skilled in the art that many of the details described above are by way of example only and are not intended to limit the scope of die invention which is to be interpreted with reference to the following claims. BSTITUTE SHEET APPENDIX I - CONVENTIONAL BOOT UP METHOD Referring to FIG. 3H, an isometric view of a computer disc is shown. This figure illustrates die location of the start of user data 162, partition gap 163, boot sector 164, partition sector 165, and partition gap 166. Referring to FIG. 3, upon hitting the on switch of a personal computer (PC) 53, the computer first goes through a conventional power-on self-test (POST) 54. At this point the Agent could be loaded if ROM-BIOS loading is used 60. POST ensures that all hardware components are running and diat die central processing unit (CPU) and memory are functioning properly. Upon completion of the POST, die next task is to load software onto die random access memory (RAM) of the computer. Conventionally, there is a read-only memory (ROM) device which contains a boot program. The boot program searches specific locations on the hard disk, diskette or floppy disk for files which make up the operating system. A typical disk is shown in FIG. 3H. Once these files are found, die boot program on the ROM reads die data stored on die appl αDle portions of the disk and copies that data to specific locations in RAM. The first portion of the disk boot sector to be loaded is the partition boot sector 55 shown in FIG. 3H as 165. At this point the load partition boot sector method could be used 61. The partition boot sector 165 then loads the remaining boot sector 164 from the disk, namely the operating system boot sector 56. Now the Agent could be loaded according to die load operating system boot sector method 62. The operating system boot sector 164 loads into memory a system file, normally named IO.SYS on personal computers or IBMBIO.COM on IBM computers 57. Now the Agent could be loaded according to the IO.SYS or IBMMIO.COM methods. Each of these files is marked with a special file attribute that hides it from the DOS Dir. The IO.SYS or equivalent then loads the rest of the operating system, conventionally called MSDOS.SYS on MS-DOS systems, and IBMD0S.COM for PC-DOS systems. Next the AUTOEXEC.BAT is processed and run 58. Now the operating system is running 59. The Agent security system according to the invention is loaded during die boot up process and accordingly is transparent to die operating system. APPENDIX II - DETAILS OF AGENT INSTALLATION Once the TENDER program, which enables die Agent to be installed, has been run and die Agent has been determined to be loaded via one, two or three of these alternatives, die system is primed and proceeds to attempt to install the Agent security system according to die present UTE SHEET state of the computer memory and die instructions given by the programmer. The SNTLINIT routine initializes the Agent security system and is passed one of diree possible loading options via the AX microprocessor register by the calling program (SUBLOADR), which itself was loaded on any one of the three enumerated locations described above. The SUBLOADR program reads die configuration file (which may be encrypted) that was generated for user input. The validity of the configuration file is checked at this point to see if it is corrupted or not. If for some reason it cannot read die configuration file, it initializes die Agent security system from a table of default settings. The SUBLOADR program then checks to see if die Agent security system is in memory by looking for the "RPL" signature. SUBLOADR saves the application programmer interface (API) entry point and dien determines which version of the security program, if any, is in memory. If not in memory, the SUBLOADR program searches the disk for the SNTLINIT routine. Depending upon the version of the SUBLOADR program, it may perform a validity check on the SNTLINIT routine. This routine may be a cyclical redundancy check (CRC) of 16 or 32 bits, a checksum check or a hash count. The TENDER program checks the partition boot sector, the operating system boot sector, and the IO.SYS (or IBMBIO.COM on PC-DOS systems) to see if any of them have been modified to contain die SNTLINIT code. A comparison to the configuration file is made to determine if the Agent has already been installed in any of die alternative locations. If die Agent has already been installed, die TENDER program takes no action. It then tracks the level of modification that was requested by die user (i.e. whether one, two or three areas were to be modified). Each of diese areas has all the modem related information written to it amongst other user selected settings. At this point it writes the current configuration file to disk. The TENDER program then takes a system snapshot of the partition boot sector, the operating system boot sector and the IO.SYS or IBMBI0.COM file, validating them, determines and then writes this file to disk. It dien checks die partition gap between die partitions, calculating die number of unused sectors between the valid boot sectors (be they partition or operating system boot sectors). There is almost certainly at least 8K of space in the partition gap 163. The Agent security system requires only 4K. The SNTLINIT module is usually stored here. If for some reason there is not enough space in die partition gap, or if die data area is physically unusable, die TENDER program will pick a suitable cluster of sectors, mark the data area logically as being unusable, dien store SNTLINIT in the cluster of sectors. The TENDER program sets out the attributes to system, hidden etc in order to hide die program image. It then calculates the physical coordinates of die cluster that was used and writes this information to die configuration file. At this point the system is ready to proceed and will be loaded prior to die completion of die loading of die operating system regardless of what strategy the programmer has chosen. In a manner similar to how viruses reinfect the boot sector 164 of the hard disk drive, the Agent security system according to die invention uses such technology to help protect against theft of die computer. Other technologies such as system timer programming and communications programming are bound to this virus like technology to create a new technology. It should also be understood diat a security company which handles incoming calls from clients may readily redefine the time period between successive calls from a client to its host. The system is typically in one of two modes of operation: (1) Waiting until it is time to call/ report into the server - "active mode"; (2) Calling or attempting to call the server - "alert mode". When die Agent security system changes it mode of operation from active to alert mode, die activation period is reduced to a minimal period such diat die Agent calls the host eighteen times per second until a successful connection is made. The activation period in active mode is predetermined, and likely to be days if not weeks. This shortened activation period (time between successive calls) is necessary to prevent busy signals and other temporal error conditions from precluding transaction attempts. The system will stay in this alert mode until a valid transaction has been completed. Since MS-DOS and PC-DOS were designed to be single-user, single-tasking operating systems, the timer interrupt is used to run the system unattended and automatically in die background to provide multi-tasking. Neither the user nor a potential thief would notice this background process although registered owners will be aware of its existence. In a standard personal computer, routine housekeeping tasks are performed periodic--, y and automatically by die CPU without instructions from die user. There is a timer routine which is called 18.2 times per second to perform such tasks as turning off the floppy disk motor after a certain period of inactivity. The Agent security system hooks into this timer routine. The total timer routine takes about 55 milliseconds and die Agent security system utilizes a small portion of CPU time during that period; this is limited to less than 0.5% of the total timer routine. This is not sufficient time to run the entire security program. Accordingly, die security program is run in small increments with each timer routine. It is important that die security program not "steal " enough computer time to be noticed . Otherwise die computer would be noticeably slowed and die existence of die program might be suspected. Serial port and modem setup routines must be called by die timer interrupt. Once this is done, die serial interrupt handler that is being used will handle die details of data transfer between the client and host systems. Once the system is set up, the serial port interrupt handler does most of die work with die timer interrupt acting as a monitor watching die transaction when it happens between the client and die server. It analyzes the receive buffer and takes die appropriate actions as necessary. The communication portion of the system can handle outgoing and incoming data transfers on its own since it has its own access to the CPU via its own interrupt request (IRQ) line, typically IRQ3 or IRQ4. Therefore the system can handle die data flow between the client machine and die server unattended. At die start of its time-slice, the timer interrupt checks the flag, which is set when a user uses the modem, in die Agent security system, die InComlSR flag byte (In Communications Interrupt Service Routine). If the flag is set, the timer interrupt exits immediately so as not to interfere with die progress of any serial communications diat may be occurring, therefore not disrupting any transaction in progress. If the flag is not set, the timer interrupt routine will check to see if die Agent security system is in an error state. If not in error, a flag called TimerlSR count is set to indicate diat a timer interrupt is in progress. A deferred execution function pointer is used to point to die upcoming routine to be executed. Just before the timer interrupt routine finishes, it points to the next routine to be executed. When die next timer interrupt occurs the routine diat was pointed to will be executed. The routine must complete in less than 55 milliseconds so diat die next timer interrupt does not occur while die routine is still executing. Attached to the PC's system bus are communications ports, all of which are optional and typically called COM1, COM2, COM3, COM4 for the first four ports. It is unusual to have more than four serial ports in a PC that is using only MS-DOS or PC-DOS as its operating system. The Agent security system also requires that a modem be connected to one of these serial ports so that calls can be made to a remote host server using normal telephone lines or dedicated telecommunications lines. When alerted 118, the Agent security system needs to be able to find an available serial port 119-122, once it does so it checks to see if a modem is attached 128-129 and tries to initialize it by sending it an initialization string 132. If successful, it checks for a dial tone, then tries to make a quiet call to a remote host server 141. Once the server has been connected, die client machine attempts to initiate a data transaction with the server so it can send its serial number and other data defined to be part of the transaction 151. The server is configured to connect at 2400 bps with no parity, 8 data bits and 1 stop bit. Thus die client matches this configuration. This allows a high connection reliability. APPENDIX III - DESCRIPTION OF ROUTINES SNTUNIT: After this routine has been loaded high into conventional memory 67 and execution has been passed to it, die machine state is saved 68. Conventional memory is the first 640 kilobytes (655,360 bytes) of memory on an Intel 80X86 compatible computer for example. Registers 15 that are affected by this routine are saved on the stack, "saving the machine state". The stack referred to is a LIFO structure, where the LIFO stands for "last in first out". It is where you can temporarily save the contents of CPU registers so that you can restore their initial values. The microprocessor register AX is used to pass one of three values to the SNTLINIT routine. Depending upon which of die diree values are passed to this routine, three different courses of action will be taken. Each course of action describes how the program will initialize itself. To summarize, this routine initializes the Agent security system from either the partition boot sector 55, the operating system boot sector 56 or the input/output module of the operating system 57. If the microprocessor register AX contains the value 0: The partition sector 165 is loaded into memory (which has been overwritten on the disc with the boot sector version of the SUBLOADR module). On execution of this code, die SNTLINIT is called. SUBSTITUTE SHEET If die microprocessor register AX contains die value 1: The boot sector 55 of the hard disk (which has been overwritten on the disc with the boot sector version of the SUBLOADR module) is loaded into memory. On execution of this code, the SNTLINIT routine is called. If die microprocessor register AX contains the value 2: The first sector of IO.SYS/IBMBIO.COM 57 (which has been overwritten on die disk widi die 10 version of the SUBLOADR module) is loaded into memory. This routine then tests to see if it is in memory already by checking for the 'RPL' signature 69, 84, 96, 108 located at the start of die address for Interrupt 2FH. If it is in memory, this routine exits 77 (to avoid loading more than one copy of die program into memory). If it is not already in memory, then it points (hooks) Interrupt 2FH to an internal routine 71, and does die same with Interrupt EAH 72. It then hooks Interrupt 8 after saving the original Interrupt 8 vector to an internal memory location (internal to the Agent security system). The machine state is restored 74 and die routine exits by jumping to memory location 0000:7C00H for the partition table and boot sector execution padis or 0070:0000H for the 10 execution path 75, 76. SNTLAPI: This API is for use by an external program. It has three functions as follows: 1. Get state of Agent security system. (Checks to see if Agent is already installed.) 2. Set state of Agent security system. 3. Set serial number of system. SWAPINT: Swaplnt stores die existing interrupt vector by replacing the vector for the interrupt number in the CPU register BX with the new vector pointed to by the CPU register pair DS:CX after it stores the current vector at a location pointed to by the register pair DS:DI. If the CPU register SUBSTITUTE SHEET DI contains 0 then the vector for the interrupt number contained in the CPU register BX is not stored. DELAYFUNC: This is a delay function used for hardware timing purposes. This routine is used in FIG. 3F, block 125. TIMERISR: Interrupt 8h/lCh is the System Timer Interrupt which executes 18.2 times per second 117 and is used to do die following: 1. Call the old system timer interrupt. 2. Check to see if a communications interrupt is occurring, exiting immediately if so. 3. Save affected CPU registers. 4. Check for an internal state error, exiting immediately if so. 5. Call die state routine. 6. Restore the saved CPU registers. ACnVEROUTINE: The ActiveRoutine checks to see if the activation period has been exceeded 118. By activation period we mean a period of time diat has elapsed since die last valid security call. This period of time is set during the transaction to the server, but is initially set to approximately 7 days. CHECKNEXT PORT: This is a check for valid serial ports, d involves checking a table of serial port addresses 120 and then testing them to ensure their functionality 122. If a valid serial port cannot be found, a sleep state is entered 125. Upon awakening, this routine is repeated 119. E SHEET DELAYLOOP: This delay is used for communications delays due to busy signals or no dial-tone and other problems that can affect die communications link. PORTFINDINIT: This procedure calls die previously described CHECKNEXTPORT function 118, 119 in its quest for a valid serial port to initialize. On finding a valid serial port, it stores die ports address, and its corresponding interrupt vector. PORTFIND: This is a check to see if die serial communications port is in use 123 by dynamically testing die registers in the universal asynchronous receiver - transmitter (UART) that is associated with the current serial port address. Specifically, it tests the Interrupt Enable Register of the UART. This UART register is read into the AL register of the CPU, and if any of the bits are set (logical 1), then the port is in use, otherwise the port is idle. It also tests the interrupt enable bit of the modem control register in the UART. If the bit is not set (logical 1) then the port is idle and available for use. Each serial port in the port table 120 is checked until either a valid one is found 123, or the routine goes to sleep 125. If a serial port is found 123, this routine will decide whether or not to initialize the UART using the system BIOS. Interrupt 14H routine, or bypass this routine, programming the UART registers directly. If an error occurs during this process, the routine is exited, and CHECKNEXT PORT is invoked. If die serial port is successfully initialized 128, 129 to die predefined bit rate, parity, word size, number of stop bits etc., the UART is cleared of any pending errors. The serial port buffer is flushed (emptied), so there is no chance of old data being picked up a second time. The state flag diat die timer interrupt checks on each clock tick is cleared, as interrupt driven communications have not yet been set up. The appropriate interrupt number is selected and die old interrupt vector is swapped widi die new one by calling SWAPINT. The statuses RTS (Request to Send) and DTR (Data Terminal Ready), are enabled in the UART. The 8259 PIC UTE SHEET is then unmasked, interrupts are enabled in die UART, then the hardware interrupts for the CPU are enabled. Then this routine exits. MODEMFINDDELAY: This procedure sets the [state-routine] function pointer to point to the MODEMFINDINIT routine, delaying execution until the next interrupt. MODEMFINDINIT: This routine points to a string to send to the modem, then calls die COMTRANSINIT routine. MODEMINITINIT: This procedure tries to initialize die modem 130 widi an appropriate initialization string from a table of initialization strings 131, and will try until either the modem is initialized or there are no more initialization strings in the table to try. The COMTRANSINIT routine is called from within this procedure 132-136. MODEMINIT: This procedure checks the state of the transmission, and checks for incoming data by calling the COMTRANS and COMTRANSCHECK routines 132. This procedure ends by jumping to a jump table which points to the next appropriate routine. MODEMCALLINIT: This routine attempts to place a call 137, 138 by selecting a telephone number 139 (and its appropriate prefix if necessary) from a table of dial strings 140. It will continue to do so until eidier a call is completed 148 or diere are no more initialization strings in the table to try. If a call could not be made 144 then the CLEANUPROUTINE and ERRORROUTINE procedures are to be run during the next state(s) (Interrupt 8 system timer ticks) 155. SUBSTITUTE SHEET MODEMCALLINIT2: This routine checks the state of die transmission, ending if it is complete. This procedure is called from within the MODEMCALLINIT routine. It in turn calls the MODEMCALL procedure. MODEMCALL: This routine checks the state of die transmission, ending if it is incomplete. It also checks to see if data has been received yet or not. MODEMCONNECTINIT: This procedure waits for a query from the host server 148 (at the odier end of the communications link), and sends the serial number 151 of the computer. If a call could not be made then the CLEANUPROUTINE and ERRORROUTINE procedures 155 are to be run during the next state(s) (Interrupt g S y S tern timer ticks). MODEMCONNECT: This routine checks the state of die transmission, ending if die transmission is incomplete. CLEANUPROUTINE: This routine resets the Agent security system 155, 156 (sometimes referred to as Sentinel in the source code) back to a known state (ACTIVE), zeroes the transmissionstate flags, flushes the UART buffer. Then it disables all interrupts, restores the old communications interrupt service routine via the SWAPINT procedure. It then sets the state routine function pointer to the CLEANUPROUTINE (to be rim during die next Interrupt 8). ERRORROUTINE: The Agent security system state is set to SNTL STATEERROR (the Agent security system is put in an error state). TITUTE SHEET COMISR: The interrupt service routine used to control one of the systems serial communications ports (and one of the Interrupt Request lines) in order to provide telecommunications services to the Agent security system. It calls the SENDBYTE and BUT PUTCHAR procedures. It handles die low- level details of sending and receiving data during die transmission when it happens. SENDBYTE: This procedure attempts to send a byte of data to die referenced serial communications port (a variable containing die port address). This routine is used in 141, 151. COMTRANSINIT: This procedure initializes a transaction between the Agent security system and die modem. A transaction involves sending a string of data 151 to die modem to be sent via telecommunications link to a host server, which after receiving the string of data, in return, sends back a string of data to the client machine 152 containing the Agent security system. The returned string can then be analyzed by die Agent security system to determine what action should be taken next. COMTRANS: This procedure handles much of die technical details regarding the maintenance of the transaction between the Agent security system and the host server 129, 134, 135, 143, 144, 145, 152, 157. It is primarily responsible for error handling such as incomplete transactions and stalled transmissions. COMTRANSCHECK: Checks die results of a completed transaction between the host server, and die client security system against a table of strings. Three possible outcomes are allowed for: 1. If die incoming data has not been completely received, die carry flag of die client CPU is set (logical 1). TE SHEET 2. If the function timed out (exceeded a time threshold value) and no Agent security system internal string matched the string received from the host server, die carry flag of the client CPU is set, and the AH register is zeroed. 3. If a matching string was found, die carry flag on die client CPU is reset (local O), and die AL register contains a value that matches die internal table entry. BUF_FLUSH: Flushes the internal serial port communications receive buffer on the client machine (containing Agent security system). The buffer is a circular queue. A circular queue is a data structure diat has what is called a head pointer and a tail pointer where the head pointer chases the tail pointer around the queue, never really catching it, but processes each byte of the data stored in it. As a byte of data is received by die serial port, it is latched and must be put into a buffer (an area of memory reserved for this purpose) before the next byte arrives (which overwrites the existing latched byte). Whenever a communications session starts, it is important that both die input and output buffers are flushed so that all new incoming and outgoing data are not contaminated by old data still sitting in die buffer. BUF_GETCHAR: Gets a character from the internal serial port communications receive buffer, removing it from the buffers as it does so. BUF_PUTCHAR: Adds a character to die internal serial port communications receive buffer. Increments die head pointer, checking to see if the buffer is full, and setting die carry flag it if it is. SUBSTITUTE SHEET BUF_INC_PTR: Increments the receive buffer pointer assigned to the client CPU register SI, and wraps it if necessary. INT2FVECT: Reserves the required space at die top of conventional memory for the RAM resident portion of the Agent security system. The undocumented Interrupt 21 H, Function 4AH, SubFunction 06 is used to do this. APPENDIX IV - SOURCE CODES Electronic Article Surveillance System Source Code for Client-side (Tazam Assembler Code by Borland) . * Copyright (c) Absolute Software 1994, 1995 :* SENTINEL.INC - Sentinel definition file r* I * PURPOSE: r* This file contains or INCLUDES all constants, macros, and directives used ;* by the Sentinel Module. ;* ;* HISTORY: ;* 1995.09.05 - CCOTI ;* New source file taken fro build 63a. ;* See the subdirectory OldFxles for the original ;* SENTINEL.INC ;* ;* NOTES: ;* . **************** * ********************* **** * **** * **** * ****** * ** * ***** IDEAL ; Set parsing mode. JUMPS ; Allow local jumps. P286N ; Allow 286 instructions only. INCLUDE "UART.INC" ; RS232 UART constants. ; Enable Debugging. Debug - 0 TITUTE SHEET Sentinel Signature. SNTL SIG1 = OFDFEh SNTL~SIG2 = OEFCDh ; Sentinel Version number. SNTL_VERSION * 0 * 256 + 100 ; Conditional compilation switches. EMIT ON - 0 enables debugging, IODELAY_ON « 1 enables io delays, TWODSKHKS - 0 to maintain deflection with 32- bit disk access ; Timing & Delays. PθRT_LθθP_DELAY « 18 1 second delay DIAL_LθθP_DELAY ■ 18 * 5 provide an inter-dial delay of 5 seconds ~ " PREINT13_TIMEOUT = 18 * 120 ; Timeout before sentinel hook the system. ; Magic Numbers and Fixed Offsets. DATA_SECTOR_OFFSET = 13Oh ; MUST Be Sector aligned for disk write — ; (see Intl3ISR) .*********** D0 N0T CHANGE WITHOUT UPDATING SUBLOADR. ************ ; Sentinel State constants. SNSTACTIVE « 0 SNSTALERT •= 1 SNSTCALLING = 2 SNSTCONNECT * 3 SNSTERROR * 4 ; Check for error: >- SNTL_STATE_ERROR * *************************************************************** ; Bit flag settings for <xmit state flags>. XMIT RECEIVE_BIT = 00000001b XMIT~SEND BIT = 00000010b XMIT SENT~AWK_BIT « 00000100b XMIT-RECEΪVE AWK BIT = OOOOlOOOb IFDEF Testing RECEIVE TIMEOUT - OFFFFh ; test timeout huge ELSE RECEIVE_TIMEOUT ■ 18 * 40 ; timeout -= 40 seconds. ENDIF SUBSTITUTE SHEET timer values (based on 18 ticks/second ; timeouts ; modem find timeout -5 ; modem initialization timeout ; modem dial out timeout -40 ; server response timeout -40 ; power-up delay before ; call status ; no attempt yet ; searching for an available ; searching fo a modem on the ; initializing modem ; sending predial string to ; sending dial string to modem ; waiting for modem to connect ; waiting for ENQ from server ; waiting for ACK from server ; waiting for next-call-date ; call passed ; call failed ; receiver structure ; receiver state ; receive timer ; received packet running-sum ; packet length to receive ; received bytes in current ; packet type ; packet subtype ; pointer to receive buffer transmit structure current transmitter state next transmitter state transmit timer packet type to transmit SUBSTITUTE SHEET ; transmission in progress ; transmit NAK count ; transmit ENQ count ; transmit packet running-sum ; remaining data bytes to ; index of next data byte to ; packet type ; packet subtype ; pointer to transmit buffer transmit packet types: data packet modem packet datalink ACK datalink NAK datalink ENQ datalink EOT protocol control characters STX character ETX character EOT character ENQ character ACK character NAK character protocol message types SNSERVER = 80h message from the server protocol message subtypes SNNEXTCALL - Oh next call packet SNDISABLE = lh disable sentinel packet SNSNTLSIZE 11 Sentinel sector size SUBSTITUTE SHEET . ******************************************************************** ;* Copyright (c) Absolute Software 1994, 1995 ;* SNTLAPI.INC .* ;* Contains global labels for the api module. .* ;* HISTORY: ;* 1995.09.05 - CCOTI ;* Created. .* SEGMENT SNTL_SEG BYTE PUBLIC 'CODE' GLOBAL SntlAPI : FAR GLOBAL Swaplnt : NEAR IF IODELAY_ON GLOBAL DelayFunc NEAR ENDIF GLOBAL C pDates NEAR ENDS SUBSTITUTE SHEET A******************************************************************* ********** * Copyright (c) Absolute Software 1994, 1995 * * SNTLBUFF.INC * * Contains global labels for the buffer module. * * HISTORY: * 1995.09.05 - CCOTI * Created. * ******************************* ********** SEGMENT SNTL_SEG BYTE PUBLIC 'CODE' GLOBAL buf_flush : NEAR GLOBAL buf_getchar : NEAR GLOBAL buf_putchar : NEAR GLOBAL buf_inc_ptr : NEAR ENDS SUBSTITUTE SHEET . ******************************************************************** * Copyright (c) Absolute Software 1994, 1995 * * SNTLCOMM.INC * Contains the global labels for the comm module. * HISTORY: * 1995.09.05 - CCOTI * Created. * SEGMENT SNTL SEG BYTE PUBLIC 'CODE' GLOBAL cmftxnak NEAR GLOBAL cmftxenq NEAR GLOBAL cmfprpmdm NEAR GLOBAL cmftx NEAR GLOBAL cmfpack NEAR ENDS SUBSTITUTE SHEET j ******************************************************************** ********** * Copyright (c) Absolute Software 1994, 1995 * * SNTLCOMV.INC * * Contains global lable for the Comm ISR. * * HISTORY: * 1995.09.05 - CCOTI * Created. * ******************************************************************** ********** SEGMENT SNTL_SEG BYTE PUBLIC 'CODE' GLOBAL cmfisr : FAR ENDS SUBSTITUTE SHEET * * ********************** * ** ** * ** **** * ****** * * * ** ** ************* ** * ** ********** * Copyright (c) Absolute Software 1994, 1995 • * SNTLDATA.INC * * PURPOSE: * Contains the global labels for the data segment. * * HISTORY: * 1995.09.05 - CCOTI * Created. * ******************************************************************** ********** SEGMENT SNTL SEG BYTE PUBLIC 'CODE' GLOBAL sngstftn WORD GLOBAL Sentinel state BYTE ;Scatch vars to store the current port info being used. GLOBAL sngrndmprt WORD GLOBAL sngmdmprtint WORD GLOBAL sngrndmprtadd WORD /Previous ISR vectors. GLOBAL sngprvtmr DWORD GLOBAL sngprvcom DWORD GLOBAL sngprvdskl DWORD IF TWODSKHKS GLOBAL sngprvdsk2 DWORD GLOBAL sng2dskhks BYTE GLOBAL sngdskskip BYTE ENDIF GLOBAL sngprvint2f DWORD ;ROR'd to limit updating the real-time clock once every 16 ticks (see ActiveRoutine) . GLOBAL cycle_var : WORD SUBSTITUTE SHEET GLOBAL dkgsctr BYTE GLOBAL sngapifl BYTE GLOBAL sngpwdl WORD GLOBAL sngpwd2 WORD ;Sentienl Settings. GLOBAL modem_default_port WORD GLOBAL ρort_table WORD PORT_TABLE_SIZE ■ 4 ; Disk location of data sector. GLOBAL data_cyl_sect WORD GLOBAL data~head_drive WORD GLOBAL sngdskwrt BYTE ; Output strings. GLOBAL init str num WORD GLOBAL init~str~table WORD INIT_STR_TABLE_SIZE - 6 GLOBAL dial str num WORD GLOBAL dial~str~table WORD DIAL_STR_TABLE_SIZE « 5 GLOBAL dial_number BYTE GLOBAL sn_packet_start UNKNOWN GLOBAL stx_byte BYTE GLOBAL lsb_length_byte BYTE GLOBAL msb_length_byte BYTE GLOBAL sn text_start UNKNOWN GLOBAL text_type BYTE GLOBAL text_sub_type BYTE GLOBAL sn_data_s " tart UNKNOWN GLOBAL sngsernu BYTE : 6 GLOBAL now_date UNKNOWN GLOBAL now~year BYTE GLOBAL now_month BYTE GLOBAL now_day BYTE GLOBAL now_hour BYTE GLOBAL now minute BYTE GLOBAL sn d~ata_end UNKNOWN GLOBAL etx byte BYTE GLOBAL lrc~byte BYTE GLOBAL sn_packet_end UNKNOWN GLOBAL sngsernum_str UNKNOWN GLOBAL sngsernum~βtr_len BYTE GLOBAL sngdatalen ~ BYTE GLOBAL rx RXZCM GLOBAL tx : TXZCM Result tables. GLOBAL command_result_table_len : BYTE GLOBAL command_result~table~ : UNKNOWN GLOBAL mdm_init_result_table_len : BYTE GLOBAL mdm_init~result_table : UNKNOWN GLOBAL dial result table len : BYTE SUBSTITUTE SHEET GLOBAL dial_result_table UNKNOWN GLOBAL connect_result_table_len BYTE GLOBAL connect~result_table~ UNKNOWN Modem and result string pool. GLOBAL βtring_pool BYTE : 127 GLOBAL modem_find_str UNKNOWN next call date GLOBAL next_call_date UNKNOWN GLOBAL next~call_year BYTE GLOBAL next~call_month BYTE GLOBAL next_call~day BYTE GLOBAL next_call~hour BYTE GLOBAL next call~minute BYTE GLOBAL sngrxbufhd WORD GLOBAL sngrxbuftl WORD GLOBAL angrxbufst UNKNOWN GLOBAL βngrxbuf BYTE GLOBAL sngrxbufend UNKNOWN GLOBAL nextcall text BYTE : 5 GLOBAL sngtxindex BYTE GLOBAL sngtxbufst UNKNOWN GLOBAL βngtxbuf BYTE GLOBAL sngtxbufend UNKNOWN ; Result jump tables. ; Table for ModemFind GLOBAL find_jump_table CODEPTR ; Table for Modemlnit. GLOBAL init_jump_table CODEPTR ; Table for dial results. GLOBAL dial_jump_table CODEPTR GLOBAL cnct_jump_table CODEPTR ENDS SUBSTITUTE SHEET j ******************************************************************** ******* * ** * Copyright (c) Absolute Software 1994, 1995 * * SNTLI13V.INC * * PURPOSE: * Contains INT 13 ISRs and disk deflection routines. * * HISTORY: * 1995.09.05 - CCOTI * Created. * •••♦•A******************************************.******************** SEGMENT SNTL SEG BYTE PUBLIC 'CODE' GLOBAL load_time WORD GLOBAL Intl3ISR FAR ENDS SUBSTITUTE SHEET ******************************************************************** ********** * Copyright (c) Absolute Software 1994, 1995 * * SNTLI2FV.INC - SNTLI2FV.ASM global lables. * * PURPOSE: * * HISTORY: * 1995.09.05 - CCOTI * Created. * * NOTES: * ******************************************************************** ********** SEGMENT SNTL_SEG BYTE PUBLIC 'CODE' GLOBAL Int2FVect : FAR GLOBAL snfint2f : FAR ENDS SUBSTITUTE SHEET *********************************** * ******************************** ******* * ** * Copyright (c) Absolute Software 1994, 1995 • * SNTLJTBL.INC * * Contains the global labels for the jump table. * * HISTORY: * 1995.09.05 - CCOTI * Created. * ******************************************************************** ********** SEGMENT SNTL_SEG BYTE PUBLIC 'CODE' GLOBAL JumpTable : NEAR GLOBAL cleanup : NEAR GLOBAL find_ok NEAR GLOBAL find timeout NEAR GLOBAL init_ok NEAR GLOBAL init error NEAR GLOBAL dial_server NEAR GLOBAL dial_busy NEAR GLOBAL dial_error NEAR GLOBAL dial_no_carr NEAR GLOBAL dial no tone NEAR GLOBAL cnct_ack NEAR GLOBAL cnct_eng NEAR GLOBAL cnct_error NEAR GLOBAL cnct_eot NEAR GLOBAL cnct_nak NEAR GLOBAL cnct_resend NEAR GLOBAL cmrxpktto NEAR ENDS SUBSTITUTE SHEET •A******************************************************************* * Copyright (c) Absolute Software 1994, 1995 * * SNTLSTRT.INC * * Contains global lables for the string table module. * * HISTORY: » 1995.09.05 - CCOTI * Created. * ******************************************************************** ********** SEGMENT SNTL_SEG BYTE PUBLIC 'CODE' GLOBAL Co TransCheck : NEAR ENDS SUBSTITUTE SHEET . ******************************************************************** * Copyright (c) Absolute Software 1994, 1995 * * SNTLTIMR.ASM * * Contains the global labels for the TimerlSR. * * HISTORY: * 1995.09.05 - CCOTI * Created. * SEGMENT SNTL SEG BYTE PUBLIC 'CODE' GLOBAL tmfisr FAR GLOBAL ActiveRoutine NEAR GLOBAL snfsnrst NEAR GLOBAL Modemlnitlnit NEAR GLOBAL ModemCalllnit NEAR GLOBAL ModemFindlnit NEAR GLOBAL snftxchkin NEAR GLOBAL snfgetpkt NEAR ENDS SUBSTITUTE SHEET UART.INC — Asm header file for programming the UART chip. UART memory port base addresses COM1 ADDRESS equ 3F8h COM2~ADDRESS equ 2F8h COM3_ADDRESS equ 3E8h COM4 ADDRESS equ 2E8h UART port interupts COMl_INTERUPT equ 04h COM2_INTERUPT equ 03h COM3_INTERUPT equ 04h COM4_INTERUPT equ 03h port offsets 0 Transmitter holding register (out). 0 Receiver data register (in). 0 Low byte, baud rate divisor (alternate 1 Interupt enable register. 1 High byte, baud rate divisor (alternate 2 Interupt ID register. 3 Line control register. 4 Modem control register. 5 Line status register. 6 Modem status register. UART memory bit masks Interupt enable register. IER_RDR_FULL IER_THR_EMPTY IER_DATA ERR IER_MSR_CHANGED Interupt ID register. IIR MULT INT IIR_INT_ID_MASK IIR_MSR CHANGED IIR THR~EMPTY IIR~RDR~FULL IIR_DATA_ERR Line control register. LCR CHAR_MASK LCR_CHAR 5 LCR_CHAR~6 LCR_CHAR~7 LCR_CHAR~8 LCR STOP_BIT MASK LCR_lSTOP~BIT LCR_2STOP~BIT LCR_PARITY_MASK LRC_NO PARITY LRC_ODD PARITY LRC EVEN_PARITY LRC~MARK_PARITY LRC SPACE PARITY SUBSTITUTE SHEET ;NOT CONFIRMED! 1 t SUBSTITUTE SHEET * Copyright (c) Absolute Software 1994, 1995 * * SENTINEL. SM - Sentinel Initialization and TSR Code • * PURPOSE: * This is the main build file for the Sentinel module. * * HISTORY: * 1995.09.05 - CCOTI * Source taken from build 63a and broken up into separate source * files. See the subdirectory OldFiles for the original * SNTLINIT.ASM * IDEAL include "SENTINEL.INC" , • .. . ******************************************************************** ■ * SNTL_SEG - Resident segment. SEGMENT SNTL SEG PARA PUBLIC 'CODE' include "SNTL TBL.ASM" include "SNTLCOMV.ASM" include "SNTLSTRT.ASM" include "SNTLBUFF.ASM" include "SNTLI2FV.ASM" include "SNTLI13V.ASM" ENDS END SUBSTITUTE SHEET ********* * Copyright (c) Absolute Software 1994, 1995 * * SNTLAPI.ASM * * Contains the sentinel API routine and general purpose routines used by all * modules. * * HISTORY: * 1995.09.05 - CCOTI * Created. * IDEAL %NOLIST include "SENTINEL.INC" include "SNTLAPI.INC" include "SNTLDATA.INC" include "SNTLTIMR.INC" %LIST SEGMENT SNTL SEG BYTE PUBLIC 'CODE' * SNTLAPI * * PURPOSE: * This function provides an external API for the Ward and Tender modules, * as well as development software tools, to gain access to the Sentinel. The following functions are supported: Function 0 - Get Sentinel State returns AL = Sentinel_state BX ■ sngstftn- Function 1 - Set Sentinel State to ALERT returns CF - 0 if successful CF - 1 if failed Function 2 - Get Sentinel Version Number returns AH = major version number AL = minor version number Function 3 - Get Sentinel Serial Number returns ES:DI = pointer to serial number Function 4 - Cancel Sentinel ALERT returns CF = 0 if successful CF = 1 if failed Function 5 - Set next-call date and time returns ES *= Sentinel data segment DI »*■ offset of next_call_date SI = offset of sngdskwrt SUBSTITUTE SHEET ;* Function 6 - Get call status ;* returns AL ■ sngclst: SNCALLNA 0 no call attempt yet SNPRTSRCH 1 searching for an available port J* SNMDMSRCH 2 searching fo a modem on the port * SNMDMINIT 3 initializing modem * SNMDMPD 4 sending predial string to modem * SNMDMDL 5 sending dial string to modem * SNWTCON 6 waiting for modem to connect to server ;* SNWTENQ 7 waiting for ENQ from server * SNWTACK 8 waiting for ACK from server * SNWTNCD 9 waiting for next-call- date from server * SNCALLPASS 10 call passed * SNCALLFAIL 11 call failed * Function 7 - Disable Sentinel disk deflection * returns CF = 0 if successful * CF - 1 if failed * Function 8 - Enable Sentinel disk deflection returns CF = 0 if successful CF « 1 if failed Function 9 - return data segment pointers returns ES:DI * Sentinel Data Segment (SntlDataSeg in sentinel.h) ES:SI » Sentinel Settings (SntlSettings in sentinel.h) * * PARAMETERS: * None * * Registers destroyed: none * * Globals referenced: * Sentinel_state * * Globals modified: * Sentinel_state - set to SNSTALERT by function 1 * βngstftn-- set to * * BIOS calls: none * * DOS calls: none * * proc calls: none * * hardware access: none * A******************************************************************* ********** ASSUME CS:SNTL_SEG, DS:NOTHING, ES:NOTHING PROC SntlAPI FAR β@checkO: ; Return the state. CMP AH,0 JNE @@checkl MOV AL, [Sentinel state] MOV BX, [sngstftn] RET βθcheckl : Attempt to set the state to ALERT. CMP AH,1 JNE @@check2 CMP [Sentinel_βtate] ,SNSTACTIVE JNE @@exit w error MOV [SentineT_state] ,SNSTALERT ; MOV [sngstftn], OFFSET snfβnrβt CLC RET @@check2 : Return the version number . CMP AH,2 JNE @@check3 MOD CCOTI 48:95.01.27 MOV AX,SNTL VERSION RET @@check3: Return the serial number. CMP AH,3 JNE @@check4 PUSH CS POP ES MOV DI,OFFSET sngsernum RET @@check4: CMP AH,4 JNE @@check5 CMP [Sentinel state], SNSTACTIVE JE @@check4_3one MOV [Sentinel_state] , SNSTACTIVE MOV [sngstftn], OFFSET snfsnrst @@check4_done: RET @@check5: CMP AH,5 test for function 5 JNE @@check6 not detected, continue prepare to copy string PUSH CS get ES » CS POP ES ES:DI points to next_call_date MOV DI, OFFSET next call date ES:SI points to data_write flag MOV SI, OFFSET sngdskwrt RET exit @@check6: CMP AH,6 test for function 6 JNE @@check7 not detected, continue MOV AL, [sngclst] get the call status into AL RET exit @@check7: CMP AH, 7 ; test for function 7 JNE 6@check8 ; not detected, continue SUBSTITUTE SHEET MOV [sngdeflect], ; clear the Sentinel disk deflection flag CLC ; clear the carry flag RET ; exit Θ@check8: CMP AH, 8 ; test for function 8 JNE @@check9 ; not detected, exit with error MOV [sngdeflect], 1 ; set the Sentinel disk deflection flag ;This is commented out to maintain the data segment offset with the CTM.EXE (See CCOTI). ; CLC ; clear the carry flag RET exit @@check9: CMP AH, 9 test for function 9 JNE @@exit_w_error not detected, exit with error PUSH CS get ES - CS POP ES ES:DI points to data segment MOV DI, OFFSET sngstftn ; ES:SI points to sentinel settings MOV SI, OFFSET modem_default_port CLC ; clear the carry flag RET ; exit §§exit_w_error: STC θθexit: RET ENDP SntlAPI ASSUME NOTHING A******************************************************************* Routine: Swaplnt Descript: Swaplnt stores the existing vector replaces the vector for the interrupt in BX with the new vector DS:CX after it stores the current vector at [DS:DI]. If DI ■ 0 the current vector is not stored. Arguments: BX * the interrupt to hook into DS:DI = address to save the existing vector; if DI = 0 the existing vector if not stored. DS:CX «= the new vector to install Registers destroyed: AX, BX, ES, FLAGS Returns: nothing BIOS calls: none DOS calls: none SUBSTITUTE SHEET ;proc calls: none . ********************** **** ****************************************** ASSUME CS:SNTL_SEG, DS:NOTHING, ES:NOTHING 5 PROC Swaplnt XOR AX,AX MOV ES,AX SHL BX,2 ; BX - address of vector 10 ; load the existing vector and save it to DS:DI (if requested). OR DI,DI JZ @@no store MOV AX, [ES:BX] MOV [DS:DI],AX 15 MOV AX,[ES:BX+2] MOV [DS:DI+2],AX e@no_βtore: ; install the new vector CLI 20 MOV [ES:BX],CX MOV [ES:BX+2],DS STI RETN ENDP Swaplnt 25 ASSUME NOTHING Routine: DelayFunc 30 Descript: DelayFunc - introduces an delay. Arguments: none 35 Registers destroyed: none Returns: nothing BIOS calls: none 40 DOS calls: none ;proc calls: none ••A****************************************************************** 45 ********** IF IODELAY_ON ASSUME CS:SNTL_SEG, DS:NOTHING, ES: OTHING PROC DelayFunc " ~ PUSH CX 50 MOV CX,1 @@loop_βtart: LOOP @@looρ_start POP CX RETN 55 ENDP DelayFunc ASSUME NOTHING ENDIF . ******************************************************************** (50 ********** / Routine: CmpDates } /Descript: CmpDates - compares two dates and sets the CF=1 if datel < date2. 65 SUBSTITUTE SHEET Arguments: [SI] -> datel [DI] -> date2 Registers destroyed: SI, DI, CX, ES Returns: CF = 1 if datel < date2 BIOS calls: none DOS calls: none proc calls: none A******************************************************************* ********** ASSUME CS:SNTL_SEG, DS:NOTHING, ES:NOTHING PROC CmpDates ~ SUBSTITUTE SHEET * Copyright (c) Absolute Software 1994, 1995 * * SNTLBUFF.ASM * * Contains the circular buffer access routines. * * HISTORY: * 1995.09.05 - CCOTI * Created. * IDEAL %NOLIST include "SENTINEL.INC" include "SNTLBUFF.INC" include "SNTLDATA.INC" %LIST SEGMENT SNTL_SEG BYTE PUBLIC 'CODE' ASSUME CS.-SNTL SEG, DS:SNTL SEG, ES:NOTHING ; ******************************************************************** BUF_FLUSH - flush receive buffer PURPOSE: This function flushses the receive buffer by setting the tail index equal to the head index. PARAMETERS: None RETURNS: Nothing REGISTERS DESTROYED: None GLOBALS REFERENCES: None GLOBALS MODIFIED: sngrxbufhd sngrxbuftl BIOS CALLS: None DOS CALLS: None PROCEDURE CALLS: None HARDWARE ACCESS: None NOTES: SUBSTITUTE SHEET .******************************************************************** PROC buf_flush NEAR MOV [sngrxbufhd],OFFSET sngrxbuf MOV [sngrxbuf l],OFFSET sngrxbuf RET ; exit ENDP buf_flush BUF_GETCHAR - get a character from receive buffer PURPOSE: This function returns the next available character in the receive buffer and increments the tail pointer. Arguments: none Registers destroyed: AL, SI Globals referenced: sngrxbuftl sngrxbufhd Globals modified: received_buf_tail - moved to the location of the next character Returns: AL = the character taken, CF«-0 If the buffer is empty CF-*-l BIOS calls: none DOS calls: none proc calls: buf_inc_ptr hardware access: none PROC buf_getchar NEAR MOV SI, [sngrxbuftl] / get the tail pointer CMP SI, [sngrxbufhd] / is it the same as the head JE @@empty / yes, exit with status MOV AL,[SI] / no, get the next byte CALL buf_inc_ptr / increment tail pointer MOV [sngrxbuftl], SI / set new tail pointer position CLC / set status RET / exit @@empty: STC set status RET exit ENDP buf_getchar .************.************************************.******.*********** SUBSTITUTE SHEET Routine: buf_putchar Descript: Adds a character to the buffer. Arguments: AL = the character to add Registers destroyed: SI Globals referenced: sngrxbuftl sngrxbufhd Globals modified: received_buf_head - moved to the location of the next free space Returns: CF*-0 if the character is stored correctly. CF«1 if the buffer is full. BIOS calls: none DOS calls: none proc calls: buf_inc_ptr hardware access: none ******************************************************************** ********** PROC buf putchar NEAR MOV SI, [sngrxbufhd] point to the head of the buffer MOV [SI], AL store the received character CALL buf_inc_ptr increment the head MOV [sngrxbufhd],SI set new head pointer CLC set return status RET exit ENDP buf_putchar ********** * * BUF_INC_PTR - increment bffer pointer * ~~ — * PURPOSE: * This function increments the head or tail pointerassociated with the * receive buffer. * * PARAMETERS: * SI = the pointer to increment * * RETURNS: * SI * the next location in sngrxbuf * * REGISTERS DESTROYED: * SI * * GLOBALS REFERENCED: * OFFSET sngrxbuf * OFFSET sngrxbufend SUBSTITUTE SHEET * GLOBALS MODIFIED: None * BIOS CALLS: None * DOS CALLS: * None * PROCEDURE CALLS: * None • * HARDWARE ACCESS: None * NOTES: * *******************************************************************, ********** PROC buf_inc_ptr NEAR INC SI ; increment SI CMP SI,OFFSET sngrxbufend / check if the pointer has wrapped JNE @@no_buf_wrap / no, continue MOV SI,OFFSET sngrxbuf ; yes, set back to beginning of buffer @@no_buf_wrap: RET ; exit ENDP buf_inc_ptr ENDS END SUBSTITUTE SHEET . ******************************************************************** * Copyright (c) Absolute Software 1994, 1995 * * SNTLCOMM.ASM * * Contains comm routines. * * HISTORY: * 1995.09.05 - CCOTI * Created. IDEAL %NOLIST include "SENTINEL.INC" include "SNTLCOMM.INC" include "SNTLDATA.INC" include "SNTLTIMR.INC" %LIST SEGMENT SNTL_SEG BYTE PUBLIC 'CODE' ;* * **** * * ** ** ** * ** * *** * * * * * * ** *** * *** * ****** * ******* **** *** *** *** * *** f ; * CMFTMOUT - transmit a NAK I* /* PURPOSE: ;* This functions transmits a NAK. If 3 NAK's have already been transmitted, * the transaction is terminated with an EOT. * * PARAMETERS: * DX = UART Transmit Holding Register * * RETURNS: * Nothing * * NOTE: * } ******************************************************************** ASSUME CS:SNTL_SEG, DS:NOTHING, ES: OTHING PROC cmftxnak NEAR CMP [tx.txxnakcnt] , 3 ; only send 3 NAK's before aborting JE @@aborttx / send another NAK OFFSET snfgetpkt / set state function 10SEC / set response to NAK @@aborttx: SUBSTITUTE SHEET MOV AL, DLEOT ; send EOT to terminate transaction OUT DX, AL MOV [tx.txxnxtst], OFFSET snfsnrst / set state function following tx @@exit: MOV [sngstftn], OFFSET cmftx set next state function MOV [tx.txxstate], OFFSET CS:cmtxcomp; set transmitter state: tx complete MOV [rx.rxxstate], OFFSET cmfpstx / reset receiver RETN ENDP cmftxnak ASSUME NOTHING A******************************************************************* ****** * *** * * CMFTXENQ - transmit an ENQ * * PURPOSE: * This functions transmits a NAK. If 3 NAK's have already been transmitted, * the transaction is terminated with an EOT. * * PARAMETERS: * DX *= UART Transmit Holding Register * * RETURNS: * CF * 0 if not timed out * CF » 1 if timed out * * NOTE: * A******************************************************************* ASSUME CS:SNTL_SEG, DS:NOTHING, ES:NOTHING PROC cmftxenq NEAR CMP [tx.txxenqcnt], only send 3 NAK's before aborting JE @@aborttx MOV AL, DLENQ / send another ENQ OUT DX, AL INC [tx.txxenqcnt] / increment transmitted ENQ count MOV [tx.txxnxtst], OFFSET snfgetpkt set state function following tx MOV [rx.rxxtmr], TM10SEC ; set response to ENQ timeout JMP @@exit θβaborttx: MOV AL, DLEOT / send EOT to terminate transaction OUT DX, AL MOV [tx.txxnxtst], OFFSET snfsnrst / set state function following tx MOV [rx.rxxstate], OFFSET cmfpstx / reset receiver SUBSTITUTE SHEET @@exit: MOV [sngstftn], OFFSET c ftx / set next state function MOV [tx.txxstate], OFFSET CSrcmtxcomp; set transmitter state: tx complete RETN ENDP cmftxenq ASSUME NOTHING *** * ***** ** ;* CMFPRPMDM - prepare to transmit modem string ;* .* PURPOSE: ;* This function prepares the transmit structure before initiating ;* transmission of a string to the modem. * * PARAMETERS: * BX *-> the string to transmit (see note below) * * RETURNS: * Nothing * * REGISTERS DESTROYED: * * GLOBALS REFERENCED: * * GLOBALS MODIFIED: • * BIOS CALLS: * None * * DOS CALLS: * None * r* PROCEDURE CALLS: :* None .* :* HARDWARE ACCESS: ' * None •* ;* NOTE: * BX points to the length of the string to transmit, which is preceeded in >* memory by the string (eg. AT<CR>3). >* ********** ASSUME CS:SNTL_SEG, DS:SNTL_SEG, ES: OTHING PROC cmfprpmdm MOV AL, [BX] / get the length of the packet MOV [BYTE LOW tx.txxpktlen], AL MOV [BYTE HIGH tx.txxpktlen], 0 SUB BX, [tx.txxpktlen] set pointer to start of string MOV [tx.txxbufp], BX MOV [tx.txxpkttyp], CMTXMDMPKT transmitting modem packet MOV [tx.txxtmr], TM1SEC set maximum transmit time SUBSTITUTE SHEET MOV [tx.txxtxing], 0 clear transmission in progress flag MOV [sngstftn],OFFSET cmftx next state: transmit MOV [rx.rxxtmr], TM6SEC wait 5 seconds after tx for rx RETN ENDP cmfprpmdm ASSUME NOTHING ,******************************************************************** /* CMFTX - transmit state machine I* / * PURPOSE: ;* This function acts as the transmitter state machine performing all packet * transmissions and data-link ACK's, NAK's, and ENQ's. * * PARAMETERS: * None * * RETURNS: * Nothing * * REGISTERS DESTROYED: * * GLOBALS REFERENCED: * GLOBALS MODIFIED: * * BIOS CALLS: * None * * DOS CALLS: * None * * PROCEDURE CALLS: * None * * HARDWARE ACCESS: * UART (IN LSR, OUT THR) * * NOTE: . A******************************************************************* ********** ASSUME CS:SNTL_SEG, DS:SNTL_SEG, ES:NOTHING PROC cmftx CMP [tx.txxtmr], / has the transmitter been on too long? JE cmtxrβt / yes, reset transmitter and Sentinel no, continue ensure THR is empty. MOV DX, [sngrndmprtadd] get DX - LSR SUBSTITUTE SHEET and determine if the THR and load another byte if not needy for DOS world works fine but in WINDOWS TX ISR chokes and this called by ComTrans() THR empty, get THR transmission in progress? yes, continue no, clear data bytes tx'd set transmission in transmitting a NAK? yes transmitting an ACK? yes transmitting an ENQ? yes transmitting an EOT? es ransmitting modem o, must be data packet es, just transmit data ransmitting data packet lear LRC checksum cmtxlenlsb: MOV AL, [BYTE LOW tx.txxpktlen] OUT DX, AL XOR [tx.txxlrc], AL MOV [tx.txxstate], OFFSET CS:cmtxlenmsb JMP @@exit cmtxlenmsb: MOV AL, [BYTE HIGH tx.txxpktlen] OUT DX, AL XOR [tx.txxlrc], AL MOV [tx.txxstate], OFFSET CS:cratxtype JMP @@exit cmtxtype: MOV AL, [tx.txxtype] OUT DX, AL XOR [tx.txxlrc], AL SUBSTITUTE SHEET MOV [tx.txxstate], OFFSET CS:cmtxstype JMP @Sexit transmit the next byte update the LRC increment data byte index decrement data bytes to and exit if more to send transmission complete, CMP Itx.txxpkttyp] , CMTXMDMPKT transmitting modem packet? JNE cmtxsetetx no, data packet, set to finish tx MOV [tx.txxstate], OFFSET CS:cmtxcomp / yes, transmission complete JMP @§exit cmtxsetetx: MOV [tx.txxstate) , OFFSET CS:cmtxetx / or set next state tx data packet JMP @@exit cmtxetx: MOV AL, DLETX OUT DX, AL XOR [tx.txxlrc], AL MOV [tx.txxstate], OFFSET CS:cmtxcomp JMP @@exit cmtxlrc: MOV AL, [tx.txxlrc] OUT DX, AL MOV [tx.txxstate], OFFSET CS:cmtxcomp JMP @@exit cmtxack: MOV AL, DLACK OUT DX, AL MOV [tx.txxstate], OFFSET CS:cmtxcomp JMP @@exit cmtxnak: CALL cmftxnak JMP @@exit cmtxenq: CALL cmftxenq JMP @@exit cmtxeot: MOV AL, DLEOT OUT DX, AL MOV [tx.txxstate], OFFSET CS:cmtxcomp JMP @@exit UBSTITUTE SHEET cmtxcomp: / transmission complete MOV [tx.txxtxing], 0 / clear transmission in progress flag MOV AX, [tx.txxnxtst] / move onto the next state function MOV [sngstftn], AX JMP @@exit cmtxrst: transmitter timeout MOV [tx.txxtxing], 0 clear transmission in progress flag MOV [sngstftn], OFFSET snfsnrst next state: reset Sentinel RET ENDP cmftx ASSUME NOTHING .******************************************************************** * * CMFPACK - process expected ACK * * PURPOSE: * This functions tests for an acknowledgement from the CT Server. * * PARAMETERS: * None * * RETURNS: * Nothing * * NOTE: * ASSUME CS:SNTL_SEG, DS: OTHING, ES:NOTHING PROC cmfpack NEAR CMP AL, DLACK ACK received? JNE @@testnak no, test for NAK MOV [rx.rxxstate], OFFSET cmfpstx ; yes, transfer complete go RETN / await another packet @@testnak: CALL cmfpnak / treat as potential NAK @@exit: RETN ENDP cmfpack ASSUME NOTHING I******************************************************************** ;* CMFPNAK - process NAK .* SUBSTITUTE SHEET ; * PURPOSE: / * This functions tests for a negative-acknowledgement from the CT Server . ? * * PARAMETERS: * AL contains the character that may be a NAK * * RETURNS: * Nothing * * NOTE: * ******************************************************************** ********** ASSUME CS:SNTL_SEG, DS:NOTHING, ES:NOTHING PROC cmfpnak NEAR CMP AL,DLNAK / NAK received? JNE @@exit / no, exit 6@cont: ; MOV BX, OFFSET sngsernum_str / point to string to send ; CALL ComTransInit / initiate retransmission MOV (sngstftn), OFFSET snftxchkin @@exit: RETN ENDP cmfpnak ASSUME NOTHING A******************************************************************* ********** * * CMFPSTX - process STX * * PURPOSE: * This functions tests for a start-of-text character. * * PARAMETERS: * None * * RETURNS: * Nothing * * NOTE: • A******************************************************************* ********** ASSUME CS:SNTL_SEG, DS:NOTHING, ES:NOTHING PROC cmfpstx NEAR CMP AL, DLSTX / STX received? JE @@cont ; yes, continue CALL cmfrstrx / no, reset receiver RETN / exit @@cont: MOV [rx.rxxlrc], 0 / clear LRC checksum MOV [rx.rxxstate], OFFSET cmfplenl / set next state SUBSTITUTE SHEET e@exit: ENDP cmfpstx ASSUME NOTHING ******************************************************************** ********** * * CMFPLEN1 - process first byte of length * * PURPOSE: * This functions accepts the least significant byte of the length field of * a packet. * * PARAMETERS: * None * * RETURNS: * Nothing * * NOTE: • ********** ASSUME CS:SNTL_SEG, DS:NOTHING, ES: OTHING PROC cmfplenl NEAR MOV [BYTE LOW rx.rxxpktlen) , AL store LSB of length XOR [rx.rxxlrc], AL update LRC MOV [rx.rxxstate], OFFSET cmfplen2 / set next state @@exit: RETN ENDP cmfplenl ASSUME NOTHING A******************************************************************* ********** * * CMFPLEN2 - process second byte of length * * PURPOSE: * This functions accepts the most signifcant byte of the length field of * a packet. * * PARAMETERS: * None * * RETURNS: * Nothing * * NOTE: A******************************************************************* ********** ASSUME CS:SNTL_SEG, DS: OTHING, ES:NOTHING PROC cmfplen2 NEAR SUBSTITUTE SHEET MOV [BYTE HIGH rx.rxxpktlen], AL βtore LSB of length XOR [rx.rxxlrc], AL update LRC MOV [rx.rxxstate], OFFSET cmfptype / set next state θθexit: RETN ENDP cmfplen2 ASSUME NOTHING ******************************************************************** ********** * * CMFPTYPE - process packet type * * PURPOSE: * This functions accepts the packet type field. * * PARAMETERS: * None * * RETURNS: * Nothing * * NOTE: * A******************************************************************* ASSUME CS:SNTL_SEG, DS:NOTHING, ES:NOTHING PROC cmfptype NEAR MOV [rx.rxxtype], AL store packet type XOR [rx.rxxlrc], AL update LRC DEC [rx.rxxpktlen] decrement bytes remaining MOV [rx.rxxstate], OFFSET cmfpstyp set next state @@exit: RETN ENDP cmfptype ASSUME NOTHING •A******************************************************************* ********** • * CMFPSTYP - process packet subtype * * PURPOSE: * This functions accepts the packet subtype field. * * PARAMETERS: * None • * RETURNS: * Nothing * * NOTE: * A******************************************************************* ********** ASSUME CS:SNTL SEG, DS:NOTHING, ES:NOTHING SUBSTITUTE SHEET PROC cmfpstyp NEAR MOV [rx.rxxstype] ,AL store packet subtype XOR [rx.rxxlrc] ,AL update LRC DEC [rx.rxxpktlen] decrement bytes remaining JNZ @@cont continue if more data MOV [rx.rxxstate], OFFSET cmfpetx expect ETX next if over JMP @@exit @@cont: MOV [rx.rxxstate], OFFSET cmfpdata set next state MOV [rx.rxxbytcnt] , 0 clear the received byte count @@exit: RETN ENDP cmfpstyp ASSUME NOTHING . ******************************************************************** * * CMFPDATA - process packet data * * PURPOSE: * This functions accepts the packet data field. * * PARAMETERS: * None * * RETURNS: * Nothing * * NOTE: * ASSUME CS:SNTL_SEG, DS:NOTHING, ES:NOTHING PROC cmfpdata NEAR MOV SI, [rx.rxxbufp] ; get offset to store data ADD SI, [rx.rxxbytcnt] MOV [SI], AL / store packet data XOR [rx.rxxlrc], AL / update LRC INC [rx.rxxbytcnt] / increment data byte count DEC [rx.rxxpktlen] / decrement bytes remaining to receive JNZ @@exit / and exit if more to come MOV [rx.rxxstate], OFFSET cmfpetx / or set next state if finished @@exit: RETN ENDP cmfpdata ASSUME NOTHING . ******************************************************************** SUBSTITUTE SHEET CMFPETX - process ETX PURPOSE: This functions accepts the packet ETX delimiter. PARAMETERS: None RETURNS: Nothing NOTE: ASSUME CS:SNTL_SEG, DS:NOTHING, ES:NOTHING PROC cmfpetx NEAR XOR [rx.rxxlrc],AL update LRC CMP AL,DLETX test for ETX JE @@cont ETX rx'd, continue CALL cmfrstrx ETX not rx'd, reset rx'r JMP @@exit @@cont: MOV [rx.rxxstate], OFFSET cmfplrc ; set next state θβexit: RETN ENDP cmfpetx ASSUME NOTHING j ******************************************************************** * * CMFPLRC - process LRC * * PURPOSE: * This functions accepts the packet LRC checksum. • * PARAMETERS: * None * * RETURNS: * Nothing * * NOTE: * ASSUME CS:SNTL_SEG, DS:NOTHING, ES:NOTHING PROC cmfplrc NEAR IF 0 CMP AL, [rx.rxxlrc] test for valid LRC JE @@cont LRC valid, continue CALL cmfrstrx LRC invalid, reset rx'r RETN and exit ELSE SUBSTITUTE SHEET / test for 0 for now / LRC valid, continue / LRC invalid, send a NAK ; set next state: transmit / set packet type: send NAK / exit eβcont: MOV [sngstftn], OFFSET cmftx / set next Sentinel state function MOV [tx.txxpkttyp], CMTXDLACK / transmitting an ACK MOV [tx.txxtmr], TM1SEC / give tx one second to complete MOV [tx.txxnxtst], OFFSET cmfprsdata / set state fuction following tx ββexit: RETN ENDP cmfplrc ASSUME NOTHING ******************************************************************** ********** * * CMFGETNEXT - get next call date * * PURPOSE: * This functions extracts the next call date from a received packet. * * PARAMETERS: * None * * RETURNS: * Nothing * * NOTE: * ******************************************************************** ********** ASSUME CS:SNTL_SEG, DS: OTHING, ES:NOTHING PROC c fgetnext NEAR PUSH DS / get ES • = DS POP ES MOV DI, OFFSET next_call_date / ES:DI points to next_call_date MOV SI, [rx.rxxbufp] / DS:SI points to received data / move up through pointers / copy five bytes of BCD / YYMMDDHHMM / copy the new date/time ; set the disk write flag / set the call status SUBSTITUTE SHEET MOV AX, [sngrndmprt] ; set default modem for next call MOV [modem_default_port], AX ; call based upon current port ~ @@exit: RETN ENDP cmfgetnext ASSUME NOTHING IF 0 * * CMFDISABLE - disable Sentinel * * PURPOSE: * This functions disables the Sentinel based upon a packet received fron * the tracking server. The Sentinel is disabled by recording a call date * and time of OxFFFFFFFFFF. * * PARAMETERS: * None * * RETURNS: * Nothing * * NOTE: * ASSUME CS:SNTL_SEG, DS:NOTHING, ES:NOTHING PROC cmfdisable NEAR PUSH DS ; get ES - DS POP ES MOV DI, OFFSET next_call_date / ES:DI points to next_call_date MOV SI, OFFSET rx.rxxdata / DS:SI points to received data CLD / move up through pointers MOV CX, 5 / copy five bytes of BCD data: / YYMMDDHHMM REP MOVSB ; copy the new date/time INC (sngdskwrt] / set the disk write flag ββexit: RETN ENDP cmfdisable ASSUME NOTHING ENDIF . ******************************************************************** :* SUBSTITUTE SHEET ;* CMFPRSDATA - parse received data ;* ; * PURPOSE: /* This functions parses received data and takes appropriate action. * * PARAMETERS: * None * * RETURNS: * Nothing * * NOTE: * A******************************************************************* ********** ASSUME CS:SNTL_SEG, DS:NOTHING, ES:NOTHING PROC cmfprsdata NEAR MOV AL, [rx.rxxtype] test for valid data type CMP AL, SNSERVER JNE @@reset MOV AL, [rx.rxxstype] / test for valid subtype CMP AL, SNNEXTCALL ; test for next call packet JNE @@nxtestl CALL cmfgetnext ; extract next call date from packet JMP @@reset @@nxtestl: IF 0 CMP AL, SNDISABLE ; test for disable packet JNE @@nxtest2 CALL snfdisable / disable Sentinel ENDIF @@reset: CALL cmfrstrx / reset receiver @@exit: RETN ENDP cmfprsdata ASSUME NOTHING ********** * * CMFRSTRX - reset the receiver * * PURPOSE: * This functions resets the receiver. * * PARAMETERS: * None * * RETURNS: * Nothing * * NOTE: SUBSTITUTE SHEET ********** ASSUME CS:SNTL_SEG, DS:NOTHING, ES:NOTHING PROC cmfrstrx NEAR MOV [rx.rxxstate],OFFSET cmfpstx / reset receiver state machine MOV [sngstftn] ,OFFSET snfsnrst / reset the Sentinel to active mode MOV [Sentinel_state],SNSTACTIVE @@exit: RETN ENDP cmfrstrx ASSUME NOTHING ENDS END SUBSTITUTE SHEET * Copyright (c) Absolute Software 1994, 1995 * * SNTLCOMV.ASM * * Contains the comm ISR routine. * * HISTORY: * 1995.09.05 - CCOTI * Created. * IDEAL %NOLIST include "SENTINEL.INC" include "SNTLCOMV.INC" include "SNTLDATA.INC" include "SNTLBUFF.INC" include "SNTLAPI.INC" %LIST SEGMENT SNTL SEG BYTE PUBLIC 'CODE' CMFISR - communications interrupt service routine PURPOSE: This function implements the communications ISR that supports bith receiving and transmitting data. This function hooks the system's communications port interrupt (IRQ 4/3). PARAMETERS: None RETURNS: Nothing GLOBALS REFERENCED: βngmdmprtadd GLOBALS MODIFIED; sngincmisr - Incremented on entrance, decremented on exit, sngstftn - set to error handler if error is detected. BIOS CALLS: None DOS CALLS: NONE PROCEDURE CALLS: cmftxbyte, buf_putchar HARDWARE ACCESS: UART (IN IIR, I/O MCR, IN RDR) TITUTE SHEET j ******************************************************************** ********** ASSUME CS:SNTL_SEG, DS:NOTHING, ES:NOTHING PROC cmfisr FAR / save registers / set DS / set ISR in progress flag / increment comm ISR count §@check_iir: ; Check the reason for the call (error, ready to send, data received) . MOV DX, [sngmdmprtadd] / get interrupt identification register ; test for receive ; proceed with data / test for transmit / proceed with data / reading the register Move DX to MCR. turn off RTS TITUTE SHEET ; DX = RDR. / AL = received byte. / Put the byte into the Move to IIR reg. / signal end of interrupt get the modem control turn RTS back on set the modem control clear ISR in progress ag ASSUME DS:NOTHING ; recover registers exit SUBSTITUTE SHE ET ********** * Copyright (c) Absolute Software 1994, 1995 * * SNTLDATA.ASM * * Contains the global data segment for the sentinel. * * HISTORY: * 1995.09.05 - CCOTI * Created. * A******************************************************************* ********** IDEAL INOLIST include "SENTINEL.INC" include "SNTLTIMR.INC" include "SNTLJTBL.INC" include "SNTLCOMM.INC" %LIST SEGMENT SNTL SEG BYTE PUBLIC 'CODE' ; Transient variables A******************************************************* sngstftn DW NEAR PTR OFFSET ActiveRoutine CCOTI Sentinel state DB SNSTACTIVE /Scatch vars to store the current port info being used, sngrndmprt DW ? sngrndmprtint DW ? sngmdmprtadd DW ? /Previous ISR vectors, sngprvtmr DD FAR PTR 0 sngprvcom DD FAR PTR 0 sngprvdskl DD FAR PTR 0 sngprvint2f DD FAR PTR 0 /ROR'd to limit updating the real-time clock once every 16 ticks (see ActiveRoutine) . cycle_var DW OOOlh win_flag win_vm sngincmisr send_buf_len send_buf_ptr sngcomcnt count sngcomerr TimerISR_count sent count receTved_count sngflent sngclst βngcomhk sngsuspend SUBSTITUTE SHEET wait 2 minutes for an XMS ; Sentinel disk disk access cylinder disk access sector API fialed request count / API request user API request user port_table DW 03F8h, OOOCh, \ 02F8h, OOOBh, \ OOOOh, OOOOh, \ 02E8h, OOODh PORT_TABLE_SIZE * 4 ; Disk location of data sector. dial_number_start DB • 18003396122", ODh LABEL dial_number ~ BYTE dial number len DB 12 - sn_packet_start SUBSTITUTE SHEET sngdatalen DB sn_data_end - sn_data_βtart /END MOD / initialize receive structure rx RXZCM < OFFSET cmfpack, \ 0, 0, 0, 0, 0, 0, \ OFFSET CS:nextcall_text > ; initialize transmit structure tx TXZCM < 0, 0, 0, 0, 0, 0, 0, 0, \ 0, 0, 0, 0, \ OFFSET CS:sngtxbuf > Result tables. command_result_table_len DB 3 command result-table DW 3 DUP( 0 ) mdm init result table len DB 2 mdm_init_reβult_table DW 2 DUP{ 0 ) dial_result_table_len DB 6 dial_result~table — DW 6 DUP( 0 ) connect_result_table_len DB 4 connect~result~table DW 10 DUP( 0 / Modem and result string pool. string_pool DB 127 DUP( 0 ) modem_find_str_start DB 'ATZ', ODh LABEL modem_find_str UNKNOWN modem_find_βtr_len DB 4 / next call date LABEL next_call_date UNKNOWN next_call_year DB OFFh next_call~month DB OFFh next_call_day DB OFFh next_call_hour DB OFFh next-call minute DB OFFh sngrxbufhd DW 0 receive buffer sngrxbuftl DW 0 LABEL sngrxbufst UNKNOWN sngrxbuf DB 80h DUP( 0 ) LABEL sngrxbufend UNKNOWN nextcall_text DB 05h DUP( 0 ) sngtxindex DB 0 ; transmit buffer LABEL sngtxbufst UNKNOWN sngtxbuf DB 7Bh DUP( 0 ) LABEL sngtxbufend UNKNOWN / Result jump tables. ; Table for ModemFind find_jump_table DW NEAR PTR find_timeout TIMEOUT DW NEAR PTR find_ok NO CARRIER (NOTE 1) DW NEAR PTR find timeout ERROR DW NEAR PTR find~ok OK SUBSTITUTE SHEET ; NOTE 1: 29 March 1995 - DBOYD USR modem (and maybe others) does not return <NO CARRIER> when the server disconnects. <NO CARRIER> returned when next I signal (command or control line) sent to modem, Sometimes this response is sent before the next command, sometimes after. When the Sentinel receives this response to a modem query (<AT>) it should interpret it as <OK>. / Table for Modemlnit. init jump table DW NEAR PTR init_error TIMEOUT DW NEAR PTR init_error ERROR DW NEAR PTR init ok OK / Table for dial results. dial_jump_table DW NEAR PTR dial_error / TIMEOUT DW NEAR PTR dial error ; ERROR DW NEAR PTR dial~busy / BUSY DW NEAR PTR dial_no_tone / NO DIAL TONE DW NEAR PTR dial_no_carr / NO CARRIER DW NEAR PTR dial_βerver / Server Query (NAK) DW NEAR PTR dial_server ; Server Query (ENQ) cnct_jump_ able DW NEAR PTR cnct_error / TIMEOUT DW NEAR PTR cnct_error / NO CARRIER DW NEAR PTR cnct_eot / Server EOT DW NEAR PTR cnct_eng / Server ENQ DW NEAR PTR cnct_nak / Server NAK DW NEAR PTR cnct ack / Server ACK ENDS include "SNTLDATA.INC" END SUBSTITUTE SHEET ******************************************************************** ********** * Copyright (c) Absolute Software 1994, 1995 * * SNTLI13V.ASM * * Contains INT 13 ISRs and disk deflection routines. * * HISTORY: * 1995.09.05 - CCOTI * Created. * A******************************************************************* ***•••*••* IDEAL %NOLIST include "SENTINEL.INC" include "SNTLI13V.INC" include "SNTLDATA.INC" include "SNTLI2FV.INC" include "SNTLTIMR.INC" include "SNTLAPI.INC" %LIST SEGMENT SNTL SEG BYTE PUBLIC 'CODE' A******************************************************************* ********** DKFDFLRD - Disk deflect read PURPOSE: This function performs disk read deflections by filling up the destination buffer with erroneous characters. PROTOTYPE: PARAMETERS: AL - number of sectors to read (must be non-zero) CH •= low eight bits of cylinder number CL = sector number 1-63 (bits 0-5) high two bits of cylinder number (bits 6-7, hard disk only) DH * head number DL ■ drive number (bit 7 set for hard disk) ES:BX • => data destination RETURNS: The flags register as set by the ROM interrupt 13 handler: - CF -e 0 if successful AH = status AL = number of sectors transferred NOTE: ********** ASSUME CS:SNTL_SEG, DS:NOTHING, ES:NOTHING PROC dkfdflrd NEAR SUBSTITUTE SHEET ; get offset of destination / store disk access / store register ; set DS:SI pointer / deflect loop / set pointers to increment / fill 256 words (512 bytes ; single sector deflection ; copy filler to ; decrement source pointer / by 2 for word moves ; decrement the number of / restore register / restore disk access / set success parameters ENDP dkfdflrd ASSUME NOTHING .A******************************************************************* ********** DKFDFLMBR - Disk deflect MBR access PURPOSE: This function performs disk deflection on attempted access to MBR sector. ; Access is deflected from our subloader in the MBR to the original MBR. PROTOTYPE: PARAMETERS: AH - disk function: 0x02 = disk read 0x03 - disk write AL - number of sectors to read (must be non-zero) CH • * ■ low eight bits of cylinder number CL « sector number 1-63 (bits 0-5) high two bits of cylinder number (bits 6-7, hard disk only) DH - head number DL = drive number (bit 7 set for hard disk) ES:BX -> the data source (writes) or data destination (reads) SUBSTITUTE SHEET RETURNS: The flags register as set by the ROM interrupt 13 handler: - CF - 0 if successful AH = status AL " number of sectors transferred NOTE: *************************************** ***************************** ********** ASSUME CS:SNTL_SEG, DS: OTHING, ES:NOTHING PROC dkfdflmbr NEAR read access to MBR? yes, deflect read write access to cylinder yes, deflect write / save disk access / load CX to access / load AL to access a / push flags because IRET / from original handler ; error?, no, continue ; yes, recover access / discard original AX / exit / recover disk access ; set success indication / all sectors read? / yes, exit / no, load crap to the next / save disk access / increment destination / by 512 (512 bytes - 1 / recover disk access ; fill one less sector than / fill destination buffer / reset destination buffer SUBSTITUTE SHEET / increment number of / set success indication / exit / save disk access / load CX to access / load AL to access a / push flags because IRET ; from original handler / error? no, continue ; yes, recover access / discard original AX / exit / read in the (possibly) / image of true MBR / push flags because IRET / from original handler / error? yes, exit / get copy of partition / save register / save disk access / set DS / set ES ; get a pointer to a buffer / prepare to move 256 bytes ; do the move / get copy of subloader / restore disk access / read subloader from the push flags because IRET from original handler error? yes, exit SUBSTITUTE SHEET / copy partition table into / set DS / DS:SI *> partition table / ES:DI *» partition table / prepare to move 256 bytes / do the move / write the subloader / push flags because IRET ; from original handler ; error? yes, exit / read new MBR back into ; push flags because IRET / from original handler ; error? yes, exit / recover register / recover disk access / set success indication / if exiting due to disk / deflection error @@exit: RET ENDP dkfdflmbr ASSUME NOTHING . ******************************************************************** ********** / INT13ISR - Sentinel interrupt 13 ISR ; PURPOSE: / This function provides the Sentinel's interrupt 13 hook for disk access. / It serves two purposes: to store next-call information to disk after a / transaction with the Sentinel server, and to prevent disk reads of the / sectors that contain the Sentinel. After a tracking transaction with the server, the Sentinel will have STITUTE SHEET received a next-call-date that must be recorded to disk. The Sentinel disk access is piggy-backed onto a disk read or write to the disk that the Sentinel is installed on. If a program (such as a Norton Disk Editor or Anit-Virus) attempts to read a section of the hard disk that the Sentinel occupies, this function will deflect the read to the original code that occupied the Sentinel'B disk space. Disk access other than read/writes is passed through to the original interrupt 13h handler. PROTOTYPE: PARAMETERS: AH = disk function: 0x02 = disk read 0x03 ■= disk write AL χ number of sectors to read (must be non-zero) CH - low eight bits of cylinder number CL - sector number 1-63 (bits 0-5) high two bits of cylinder number (bits 6-7, hard disk only) DH *= head number DL ■ drive number (bit 7 set for hard disk) ES:BX -> the data source (writes) or data destination (reads) RETURNS: The flags register as set by the ROM interrupt 13 handler: - CF = 0 if successful AH = status AL = number of sectors transferred NOTE: ********** ASSUME CS:SNTL_SEG, DS:NOTHING, ES:NOTHING OFFSET TO PREHANDLER = PreIntl3Handler - JMP REL OFFSET OFFSET~TO~FULLHANDLER - FullIntl3Handler - JMP~REL~OFFSET load_time DW ? the time the system loaded. PROC Intl3ISR FAR JMP_SHORT_REL_OPCODE DB OEBh Intl3 RelOffset DB OFFSET TO_PREHANDLER JMP REL OFFSET: PreIntl3Handler: PUSH AX PUSH ES PUSH DS PUSH CS POP DS ASSUME DS:SNTL_SEG / Check for an XMS manager. MOV AX, 4300h SUBSTITUTE SHEET XMS loaded, re-hook INT / ES « bios segment. / Load current bios time. / Find delta since / Check for timeout. / If timeout, continue and @@hooklC: MOV BX, OOlCh MOV DI, OFFSET sngprvtmr MOV CX, OFFSET tmfisr CALL Swaplnt / Enable full intl3 handler. MOV [Intl3_RelOffset] , OFFSET TO FULLHANDLER POP DI POP CX POP BX @@jmp_to_full_isr: ASSUME DS:NOTHING POP DS POP ES POP AX / JMP [DWORD CS:sngprvdskl] ; pass control to original handler ; this invocation directed / yes, pass control through / set flag for (possible) / is the sector quantity ; no, continue ; pass control through / disk deflection enabled? / no, check for piggy-back SUBSTITUTE SHEET / access to Sentinel head ; no, check for piggy-back / access to first 12 / (MBR subloader and ; no, check for piggy-back save important register get the cylinder / get the sector ; recover important / at this point it has been / that the system is / access the first 12 / cylinder 0 and we must access starting on MBR? yes, go deflect read access to cylinder yes, deflect read write access to cylinder yes, deflect write pass control through deflect access from MBR to original MBR exit deflect a read exit deflect a write set success parameters / does the Sentinel need / yes, continue piggy-back ; pass control through SUBSTITUTE SHEET / write next-call-date to / at this point we are / piggy-back onto a write ; same drive that the ; are we hooked twiced? ; yes, execute second / execute first disk / push flags because IRET / from original handler ; exit if disk access error execute second handler push flags because IRET from original handler exit if disk access error push flags because IRET from original handler exit if disk access error / set DS set ES clear the Sentinel flag Load registers for intl3 03«disk write/ 01«1 set cylinder and sector set the head and drive push flags because IRET from original handler disk access error, jmp disk write successful SUBSTITUTE SHEET clear disk access skip / discard FLAGS from stack clear disk access skip / discard FLAGS from stack / cannot piggy-back this / is disk access hooked / yes, pass control to / no, pass control to / earlier disk hook / pass control to original ; and it will IRET / cannot piggy-back this ; pass control to original SUBSTITUTE SHEET ********** * Copyright (c) Absolute Software 1994, 1995 * * SNTLI2FV.ASM * * PURPOSE: * Contians INT 2F ISRs used by the sentinel. * * HISTORY: * 1995.09.05 - CCOTI * Created. * A******************************************************************* ********** IDEAL %NOLIST include "SENTINEL.INC" include "SNTLI2FV.INC" include "SNTLDATA.INC" include "SNTLTIMR.INC" include "SNTLAPI.INC" %LIST SEGMENT SNTL SEG BYTE PUBLIC 'CODE' .**•****** • ************•*********************************** / Unmovable code. ********************************************************** ********************************************************** Routine: Int2FVect Descript: Provides an API and RPL 2F/4A06 support A********************************************************* ASSUME CS.-SNTL SEG, DS:NOTHING, ES:NOTHING PROC Int2FVect FAR JMP SHORT @@entr must be a Sentinel check proper signature provided? no, exit proper signature provided? no, exit yes, return API address ENDP Int2FVect SUBSTITUTE SHEET ASSUME NOTHING A******************************************************************* ********** * * SNFINT2F - interrupt 2F hook * * PURPOSE: * This is the interrupt 2F hook that supports the Sentinel API request and * monitors WINDOWS activation/deactivation * * PARAMETERS: * None * * RETURNS: * Nothing * * REGSITERS DESTROYED: * * GLOBALS REFERENCED: * * GLOBALS MODIFIED: * * BIOS CALLS: * None • * DOS CALLS: * None * * PROCEDURE CALLS: * None * * HARDWARE ACCESS: * None * ******************************************************************** ********** ASSUME CS:SNTL_SEG, DS:NOTHING, ES:NOTHING PROC βnfint2f FAR CMP AX, 1605h ; check if WINDOWS is starting to load JNE @@checkl MOV [BYTE CS:sngsuspend), 1 / suspend Sentinel until WINDOWS is loaded PUSHF / push flags because IRET from call CALL [DWORD CS:sngprvint2f] / to previous handler pops flags IRET / return from interrupt @@checkl: CMP AX, 1608h / check if WINDOWS has finished loading JNE @@check2 MOV [BYTE CS:sngsuspend], 0 ; allow Sentinel to resume MOV [BYTE CS:sngdlytmr], 90 ; set the delay timer reset after delay MOV [WORD CS:sngstftn], OFFSET snfsnrst SUBSTITUTE SHEET starting exit JNE @@check4 MOV [BYTE CS:sngsuspend], 1 ; suspend Sentinel until WINDOWS has exiited PUSHF / push flags because IRET from call CALL [DWORD CS:sngprvint2f] / to previous handler pops flags IRET / return from interrupt @@check4: CMP AX, 1607h ; check if WINDOWS is testing for 32 JNE @@check5 ; bit disk access support CMP BX, OOlOh JNE @@check5 CMP CX, 0003h JNE @@check5 MOV CX, OOOOh set return value to indicate 32-bit support IRET return from interrupt @@check5: check for API request CMP AX, SNTL_SIG1 / check for signature in AX:DX JNE eβorg / no match, go to previous handler CMP DX, SNTL SIG2 SUBSTITUTE SHEET JNE @@org / no match, go to previous handler AX:DX match, but no access yet CMP [ sngapifl ] , 10 / more than ten failed API requests? JAE @@a ifail ; yes, jump to / check for passwords / no match, check for ; check for passwords / no match, check for / oki check for backdoor access / no match, increment ] / no match, increment ; signature and / return API entry / return from / alert CTM to ; return from ; pass control to ENDP snfint2f ASSUME NOTHING ENDS END SUBSTITUTE SHEET ; * Copyright (c) Absolute Software 1994, 1995 •* * SNTLINIT.ASM .* ;* contains all initialization code that is discarded from memory. .* ; * HISTORY: ;* 1995.09.05 - CCOTI ;* File created from the old SNTLINIT.ASM. SEGMENT SNTL INIT_SEG PARA PUBLIC 'CODE' ENDS ~ IDEAL %NOLIST include "SENTINEL.INC" include "SNTLDATA.INC" include "SNTLI2FV.INC" include "SNTLI13V.INC" %LIST j ******************************** ************************************ i* /* SNTL_INIT SEG - Transient segment. ;* .**********************.***********,***********.*****.**«****.******* SEGMENT SNTL_INIT_SEG PARA PUBLIC 'CODE' ASSUME CS:SNTL_INIT_SEG, DS:NOTHING ; *** * *** * ** **** * * ********* ** ** * **** * ******************** * ************ ********** / Sntlinit Header part_sector DB 512 DUP( 0 ) boot_sector DB 512 DUP( 0 ) io_sector DB 512 DUP( 0 ) SntlSignature DW SNTL_SIG1, SNTL_SIG2 JMP NEAR Sntlinit fdddataseg: sentinel source image parameters fdgssihddrv DW OOOOh determined with Norton Disk Editor fdgesicylsec DW OlOlh determined with Norton Disk Editor fdgssisec DB 11 written by CTM sentinel target image parameters fdgstihddrv DW OOOOh written by CTM fdgβticylsec DW OOOOh written by CTM fdgstisec DB 00 written by CTM subloader source image parameters SUBSTITUTE SHEET fdgslsihddrv DW OlOOh determined with Norton Disk Editor fdgslsicylβec DW OOlOh determined with Norton Disk Editor fdgslβisec DB 1 written by CTM subloader target image parameters fdgsltihddrv DW OOOOh written by CTM fdgβlticylsec DW OOOOh written by CTM fdgsltisec DB 0 written by CTM fdginstall DB 0 written by CTM to activate HDD infection by FDD boot program fdgdskerr DB 0 disk access error count fdghddbshd DW ? ; HDD Boot Sector head and drive fdghddbscs DW ? / HDD Boot Sector cylinder and sector fdghddid DD ? / HDD volume ID written by CTM to prevent / FDD boot program from infecting wrong disk . ******************************************************************** ********** ; Sntlinit entry points (stack- AX,BX,CX,DX,DS,ES) Sntlinit: EMIT 'S' PUSH SI PUSH DI PUSH CS POP DS ASSUME DS:SNTL INIT SEG XOR BX, BX / copy original MBR over the subloader MOV ES, BX / at location 0000:7C00h MOV DI, 7C00h MOV AX, OFFSET part_sector MOV SI, AX / SI - sector to copy. MOV CX, lOOh / 256 words to copy. CLD REP MOVSW EMIT *M' Check if sntlinit is already in memory SUBSTITUTE SHEET CMP DX, SNTL_SIG2 / Is the sentinel installed? JNE exit / Yes, exit now. EMIT RPL check fail : XOR AX, AX MOV ES, AX ES - IVT segment. ; Calculate and assign SNTL_SEG to DS. MOV AX, CS MOV BX, OFFSET SNTL_SEGMENT SHR BX, 4 ADD AX, BX MOV DS, AX ASSUME DS:SNTL_SEG ; Hook the interrupt handlers into the system: CLI ; DISABLE INTERRUPTS Hook 2Fh. MOV [WORD ES:00BCh], OFFSET Int2FVect MOV (WORD ES:00BEh], AX Hook 13h. MOV AX, [WORD ES:004Ch] first hook of INT 13h to control disk access MOV [WORD sngprvdskl], AX MOV AX, [WORD ES:004Eh] MOV [WORD sngprvdskl+2] , AX MOV [WORD ES:004Ch], OFFSET Intl3ISR MOV [WORD ES:004Eh], DS .**•*********************************** MOV AX, [WORD ES:0064h] hook INT 19h to track reboot MOV (WORD sngprvintl9],AX MOV AX, [WORD ES:0066h] MOV [WORD sngprvintl9+2],AX MOV [WORD ES:0064h],OFFSET snfintl9 MOV [WORD ES:0066h),DS QEMM requirement MOV [BYTE ES:03C4h], 'N' to work with QEMM DOSDATA look like MOV [BYTE ES:03C5h], *e' a Novell NetWare RPL by loading MOV [BYTE ES:03C6h], 't' this string at INT Flh and our code MOV [BYTE ES:03C7h), 'W' segment (less DOS wrapper) at segment portion of INT F3h MOV AX, DS Novell puts its INT 13h address at SUB AX, OOOlh INT F3h, so try that for our hook MOV [WORD ES:03CCh + 2), AX MOV [WORD ES:03CCh), OFFSET Intl3ISR .************************************** ; Initialize runtime variables (if any). MOV AX, [modem_default_port] / set first port to attempt dial out ° ~ ~ " MOV [sngrndmprt], AX / Set the load_time variable for the preintl2_handler / MOV AX, 0040h SUBSTITUTE SHEET M POP DI POP SI ASSUME ES:NOTHING POP ES ASSUME DS:NOTHING POP DS POP DX POP CX POP BX POP AX / Jump back to sector. JmpOpcode DB OEAh EntryPnt DW 7C00h SectSeg DW OOOOh IF EMIT_ON /Only needed for EMIT macro --* Puts the character in AL to the console. PROC PutChar NEAR PUSH AX / Output a character ZTCN - For old BIOS /TCN - For old BIOS ENDP PutChar ENDIF /EMIT ON————————————————" B —" a ——* B —————————————— ; Padding to maintain segment offset that matches the current CTM.EXE Padding DB 2Oh DUP( 90h ) /Following statments must be at the end of the SNTL_INIT SEG. ALIGN 16 SNTL_SEGMENT: ; Used to calculate the location of SNTL_SEG. ENDS END STITUTE SHEET j******************************************************************** ********** * Copyright (c) Absolute Software 1994, 1995 * * SNTLJTBL. SM * * Contains the main jumptable code used by TimerlSR. * * HISTORY: * 1995.09.05 - CCOTI * Created. * A******************************************************************* ********** IDEAL %NOLIST include "SENTINEL.INC" include "SNTLJTBL.INC" include "SNTLDATA.INC" include "SNTLTIMR.INC" %LIST SEGMENT SNTL_SEG BYTE PUBLIC 'CODE' ASSUME CS:SNTL_SEG, DS:SNTL_SEG, ES:NOTHING Enter: AL - table index, BX - table offset. PROC JumpTable NEAR XOR AH,AH ; zero AH SHL AX,1 ; multiply AX by 2 to get offset ADD BX,AX ; add offset to the table base JMP [WORD BX] / jump the indexed address ENDP JumpTable cleanup: MOV [sngstftn],OFFSET snfsnrst ; MOV [cleanup_routine] ,OFFSET ActiveRoutine MOV [Sentinel_state],SNSTACTIVE RETN find_timeout: IF 1 MOV [sngstftn],OFFSET snfsnrst ELSE MOV [sngstftn],OFFSET snfsnrst MOV [cleanuρ_routine],OFFSET CheckNextPort ENDIF RETN find ok: / MoHem successfully initialized. MOV [BYTE init_str_num],INIT_STR_TABLE_SIZE / reset modem init string table index ~ tn],OFFSET Modemlnitlnit SUBSTITUTE SHEET / reset the dial string number when the Sentinel goes active, doing this will / allow the system to search for another port and continue on from the last ; pre-dial string used MOV [BYTE dial_str_num],DIAL_STR_TABLE_SIZE ENDIF MOV [sngstftn],OFFSET ModemCalllnit RETN IF 0 dial to work on opposite system, that the next pre-dial string will reuse the last ModemCalllnit ENDIF IF 1 dial_busy: / MOD DBOYD 50:95.02.22 dial_error: /MOD DBOYD 55:95.04.12 dial_no_carr / MOD DBOYD 55:95.04.12 cnct_error: / MOD DBOYD 55:95.04.13 ENDIF dial_no_tone MOV [sngdlytmr], TM1SEC / delay 1 second before redialing MOV sngstftn], OFFSET ModemFindlnit ; search for modem before redialing RETN IF 0 /MOD DBOYD 55:95.04.12: / to allow 8 prefix to work on 9 prefix PBX's and direct dial, treat / the responses below the same as no dial tone so that the next pre¬ dial / string will be used dial error: /ADD CCOTI 48:95.02.07 [sngstftn] ,OFFSET snfsnrst [cleanup routine] ,OFFSET ActiveRoutine [sngclst]7 SNCALLFAIL /MOD DBOYD 55:95.04.13 ;sngstftn] ,OFFSET snfsnrst [cleanup_routine] ,OFFSET ActiveRoutine [sngstftn] ,OFFSET snftxchkin SUBSTITUTE SHEET - 100 - RETN [ sngstftn] , OFFSET snfgetpkt [ receive_t ick_count ] , 0 / Reset ENDS END SUBSTITUTE SHEET .A******************************************************************* * Copyright (c) Absolute Software 1994, 1995 * * SNTLSTRT.ASM * * Contians routines for using the sentinel's string tables. * * HISTORY: * 1995.09.05 - CCOTI * Created. * IDEAL %NOLIST include "SENTINEL.INC" include "SNTLSTRT.INC" include "SNTLDATA.INC" include "SNTLBUFF.INC" %LIST SEGMENT SNTL SEG BYTE PUBLIC 'CODE' .A******************************************************************* COMTRANSCHECK - check result of transmission PURPOSE: This function checks the result of a transmission between the Sentinel / and the modem. It test modem responses against a table of strings. More PARAMETERS: BX - the beginning of the string table (more than one table is supported) RETURNS: CF - 1 if response is not completely received CF = 0 and AL - 0 if time-out without a match CF = 0, AL - index of match in response table (1 = last string in table) GLOBALS REFERENCES: receive tick_count βngrxbuTtl sngrxbufhd GLOBALS MODIFIED: sngrxbuftl - if a string is found, set past the found string. BIOS CALLS: None DOS CALLS: None PROCEDURE CALLS: buf_inc_ptr, buf_getchar HARDWARE ACCESS: SUBSTITUTE SHE! None NOTES: A******************************************************************* ASSUME CS:SNTL_SEG, DS:SNTL_SEG, ES: OTHING PROC ComTransCheck / set CMPSB pointers to / get ES * DS / AL - the number of strings / zero CH (CL is defined ; make AH zero §@str_loop_start: / Initialize the inner loop. MOV SI, [sngrxbuftl] MOV DI,[BX] / DI - the string to check ADD BX,2 MOV CL, [DI-1] / CX = the string len §@char_loop start: CMP ~ SI, [sngrxbufhd] JE @@buffer overrun / at the end of the buffer CMPSB / this increments SI and DI JNE @@unmatched_byte / this string doesn't match LOOP @@char_loop~start @@found match: MOV [sngrxbuftl],SI e@clear_carry: CLC / set return status RET / exit @@buffer overrun: INC AH / AH I- 0 if no match has been found @@unmatched_byte: / Have we checked all of the strings? DEC AL / decrement string counter JNZ e@str_loop_start §@no_match: / AL - 0 ; Check if we have timed out. CMP [rx.rxxtmr], 0 JE @@clear_carry / timed out, exit Check if we need to consume a character. OR AH,AH AH I- 0 if no match was found JNZ @@exit CALL buf_getchar @@exit: STC / set return status RET / exit UBSTITUTE SHEET ENDP ComTransCheck ASSUME NOTHING ENDS END SUBSTITUTE SHEET j******************************************************************** ********** * Copyright (c) Absolute Software 1994, 1995 * * SNTLTIMR.ASM * * Contains Timer ISR and related subroutines. * * HISTORY: * 1995.09.05 - CCOTI * Created. * A******************************************************************* ********** IDEAL %NOLIST include "SENTINEL .INC" include "SNTLTIMR.INC" include "SNTLDATA •INC" include "SNTLJTBL.INC" include "SNTLAPI INC" include "SNTLCOMM.INC" include "SNTLCOMV.INC" include "SNTLBUFF •INC" include "SNTLSTRT.INC" %LIST / TCN Nov 1/95 MACRO TCN_EMIT ch PUSH AX MOV AL,ch CALL TCN_PutChar POP AX ENDM / TCN Nov 1/95 SEGMENT SNTL_SEG BYTE PUBLIC 'CODE' ASSUME CS:SNTL SEG, DS:NOTHING, ES:NOTHING /Output a character /TCN - For old BIOS /TCN - For old BIOS .A******************************************************************* ********** .* TMFISR - timer interrupt service routine ;* / * PURPOSE: ;* This function implements the timer ISR that is hooked to the system SUBSTITUTE SHEET timer. This function and performs the following: ;* /* Checks the Sentinel's state <Sentinel state> /* Executeβ one of the following subroutines based on the state: /* SNSTACTIVE ;* ActiveRoutine: ;* ,* SNSTALERT /* PortFindlnit: PortFind CheckNextPort: SNSTCALL ModemFindlnit: ModemFind: ModemFindError: Modemlnitlnit: Modemlnit: ModemlnitError: ModemCalllnit: ModemCall: ModemDialError: SNSTCONNECT snftxchkin: * ModemConnect: * ModemConnectError: * * SNSTERROR * ErrorRoutine: * * PARAMETERS: * None * * RETURNS: * Nothing * * REGISTERS DETROYED: * None GLOBALS REFERENCED: sngincmisr Sentinel_state sngstftn " time_count activation_period modem_default_port * port table * βngm ϊmprtadd * sngrndmprtint * modem_init_str * init_result_table * * GLOBALS MODIFIED: * sngrndmprt - set to the port currently being used. * Sentinel_state - set to the now state of the Sentinel * sngstftn - set to the routine that will perform the next action. ;* sngmdmprtadd - set to the address used by the current port (sngrndmprt) ;* sngrndmprtint - set to the interrupt used by the current port (sngrndmprt) /* sngincmisr - reset to 0 before cmfisr is hooked in. send buf len - reset before cmfisr is hooked in. SUBSTITUTE SHEET * sngprvcom - stores the old com ISR before hooking in cmfisr. * * * BIOS CALLS: * None * * DOS CALLS: * None * * PROCEDURE CALLS: * buf_flush * Swaplnt * ComTransInit * ComTransCheck * * HARDWARE ACCESS: * UART (I/O MCR, OUT IER) , I/O PIC * **** *************** ************************************************* PROC tmfisr FAR / Entry point for TimerlSR. IF Debug INC [CS:TimerlSR count] / increment debug timer ENDIF CMP [CS:sngsuspend] , 0 / is the Sentinel suspended? JNE TimerAbort / yes, exit CMP [CS:sngincmisr] , 0 / is the Sentinel in the comm. ISR? JNE TimerAbort / yes, exit PUSH DS / save registers PUSH ES PUSHA PUSH CS / set DS - CS « SNTL_COM_SEG POP DS ASSUME DS:SNTL SEG CLI / halt interrupts CMP [sngcomhk], 0 / have we hooked the comm. interrupt? JE @@tmcont / no, continue / yes, determine if we still have the hook MOV BX, [sngmdmprtint] / the IVT entry to test SHL BX, 2 / BX - the IVT offset to get ISR vector / clear ES / get offset of installed / is it our routine? / no, Reset sentinel / get our segment / compare to installed SUBSTITUTE SHEET JNE @@tmrβt / if not equal, reset Sentinel JMP @@tmcont / we still have the interrupt, continue / reset the Sentinel and ιutine Ε / clear the the comm. restore interrupts are we in Windows? no, go check running yes, determine virtual should be a word!. not in virtual machine 1, is the transmit timer no, continue yes, decrement the is the receive timer no, continue yes, decrement the port / is the port delay timer / no, continue / yes, decrement the port / is the Sentinel delay / no, execute state / yes, decrement timer / no, call previous timer execute the state recover registers SUBSTITUTE SHEET TimerAbort: JMP [DWORD CS:sngprvtmr] ENDP tmfiβr ASSUME NOTHING .A******************************************************************* ********** ;* .* SNFWTFORXMS - wait for XMS .* ; * PURPOSE: /* This function waits for the extended memory manager (XMS) to be loaded /* and then hooks interrupt 2Fh. This hook allows the Sentinel to track the ;* PC moving in and out of WINDOWS, and allows ASC utilities to communicate * with the utility. * * PARAMETERS: * None * * RETURNS: * Nothing * * GLOBALS REFERENCED: * * GLOBALS MODIFIED: * * BIOS CALLS: * None * * DOS CALLS: * None * * PROCEDURE CALLS: * None * * HARDWARE ACCESS: * Nothing • A******************************************************************* ********** ASSUME CS:SNTL_SEG, DS:SNTL_SEG, ES:NOTHING .*** STOLEN BY CCOTI *** ASSUME NOTHING •A******************************************************************* ********** ;* /* SNFWAIT - wait for timer to expire ;* .* PURPOSE: /* This function waits for the delay timer, sngdlytmr, to expire before ;* allowing the Sentinel to proceed. This function is used to delay the /* Sentinel from activating on power-up and when entering and exiting SUBSTITUTE SHEET /* WINDOWS. Since the delay may be started at any time for a number of * reasons, when the delay expires the Sentinel goes to snfsnrst() * before going back to ActiveRoutine() . * * PARAMETERS: * None * * RETURNS: * Nothing * * GLOBALS REFERENCED: * * GLOBALS MODIFIED: * * BIOS CALLS: * None * * DOS CALLS: * NONE * * PROCEDURE CALLS: * None * * HARDWARE ACCESS: * None * ******************************************************************** ********** ASSUME CS:SNTL SEG, DS:SNTL SEG, ES:NOTHING IF 0 PROC snfwait NEAR CMP [sngdlytmr], 0 / wait for delay timer to expire JNE @@exit ; not yet expired, exit @@reset: / reset the Sentinel MOV [sngstftn] ,OFFSET snfsnrst / MOV [cleanup_routine],OFFSET ActiveRoutine @θexit: RETN / exit ENDP snfwait ASSUME NOTHING ENDIF ASSUME CS:SNTL_SEG, DS:SNTL_SEG, ES:NOTHING PROC ActiveRoutine NEAR Check if the activation eriod has been exceeded. / Get RTC date. / Store year. SUBSTITUTE SHEET MOV [WORD now month],DX Store month and day. MOV AH,2 INT lAh JC @@end XCHG CL,CH MOV [WORD now_hour),CX Store hour and minute. Check if next_call_date has been passed. MOV SI, OFFSET next call_date MOV DI, OFFSET now_3ate ~ CALL CmpDates JNC @@end βθalert: MOV [Sentinel_state] ,SNSTALERT Date passed, set to alert. @@end: / Check if we've been activated. CMP [Sentinel_state],SNSTALERT Check state. JNE @@exit @@activated: / Set the Sentinel to the ALERT state. IF 0 MOV AX, [modem_defau1t port] MOV [sngrndmprt] ,AX _ set first port ENDIF MOV [BYTE dial_Btr_num],DIAL_STR_TABLE_SIZE set first pre-dial string — ~ ~ — MOV [sngstftn],OFFSET PortFindlnit set next state function @@exit: RETN ENDP ActiveRoutine PROC CheckNextPort NEAR MOV AX, [sngrndmprt] INC AX CMP AL,PORT_TABLE_SIZE JB @@assigή_port XOR AX,AX start back at first port @@assign port: MOV [sngrndmprt] ,AX set the modem port to check go look for modem on the port MOV [sngstftn],OFFSET PortFindlnit RETN / exit ENDP CheckNextPort PROC PortFindlnit NEAR ; initialize PortFind variables (based on sngrndmprt which was set previously) . SUBSTITUTE SHEET PROC PortFind NEAR / Check if the port exists . / . . . / NOT IMPLEMENTED - NEEDED FOR PCMCIA / TCN Nov 1/95 / DX - current port's / DX = current port's IER / AL = IER port status / if IER = Oxff, UART does / port exists, go check / port does not exist, go / check if the port is in /TCN Nov 1/95 / test PIC IMR first / get bit of interest / bit mask ready IN AL,21h / get primary PIC IMR IODELAY AND AL,BL / bit set -> interrupt disabled JNZ @@port_idle TCN EMIT '3' / TCN Nov 1/95 SUBSTITUTE SHEET / PIC IMR bit set, test IER tadd] / DX - current port's / DX = current port's IER ; AL - IER port status / Are any IER bits set? / if no, port is idle /TCN Nov 1/95 ; PIC IMR bit set, and IER bits / set, test OUT2 next ; DX « current port's / DX - current port's MCR / AL - MCR port status ; is MCR OUT2 bit set? / if no, port is idle / TCN Nov 1/95 / all checks failed /TCN Nov 1/95 CMP [sngprtdlytmr] , / port must be available for a set period JNE @@exit / before a call is attempted / set port for no parity, eight / data bits, and 1 sto bit SUBSTITUTE SHEET OUT DX, AL set value in LCR IODELAY @@init_ok: / Clear any pending errors in the UART. MOV DX, [sngmdmprtadd] get address of LSR ADD DX,LSR IN AL,DX IODELAY Hook into the port, first init and install the interrupt vector. uffer to vector. CALL Swaplnt MOV [sngcomhk] , 1 set the comm. hooked flag CLI disable interrupts MOV DX, [sngmdmprtadd] get address of MCR ADD DX, MCR IN AL, DX IODELAY OR AL, 00001011b OUT DX, AL interrupts enabled in the UART IODELAY MOV CX, [sngmdmprtint] clear (enable) IRQ bit mask in PIC SUB CL,08h MOV BL,01h SHL BL,CL NOT BL IN AL,21h IODELAY AND AL,BL OUT 21h,AL interrupts enabled in the PIC IODELAY MOV DX, [sngmdmprtadd] / get address of IER INC DX MOV AL,00000001b ; interrupt when data received OUT DX,AL IODELAY STI / enable interrupts MOV [Sentinel_state] , SNSTCALLING MOV [sngdlytmr], TM1SEC / delay 1 second before attempting to MOV [sngstftn], OFFSET ModemFindlnit / find modem @@exit: RETN ENDP PortFind SUBSTITUTE SHEET PROC ModemFindlnit NEAR MOV [sngclst], SNMDMSRCH / set call status MOV BX, OFFSET modem_find_βtr / get a pointer to modem string ~ CALL cmfprpmdm / prepare transmit structure MOV [tx.txxnxtst], OFFSET ModemFind / set next state after transmission RETN ENDP ModemFindlnit PROC ModemFind NEAR MOV BX,OFFSET command_result_table / check for received data CALL ComTransCheck JC θ@end / data not received yet MOV BX,OFFSET find_jump_table / check for acceptable response ~ mov [sngdlytmr], 9 /TCN Nov 1/95 /According to Hayes Modem spec. /we should wait at least 0.5 sees /after sending the "ATZ" command JMP JumpTable @@end: RETN ENDP ModemFind PROC Modemlnitlnit NEAR / Attempt to initialize the modem (send modem_init_str) . MOV [sngclεt], SNMDMINIT / set call status MOV BX, OFFSET init_str_num / get the index of the next string DEC [BYTE BX] JZ @@reset wrap-around and start over prepare transmit structure SUBSTITUTE SHEET MOV AX, [BX] get a pointer to the next string SHL AX, 1 ADD BX, AX MOV BX, [BX] CALL cmfprpmdm prepare transmit structure MOV [tx.txxnxtst], OFFSET Modemlnit set state following transmission RETN θβreset: MOV [BYTE BX], INIT_STR_TABLE_SIZE / retry initialization strings MOV [sngstftn], OFFSET ModemCalllnit RETN ENDP Modemlnitlnit PROC Modemlnit NEAR / Check for reply. MOV BX,OFFSET mdm_init_result_table CALL ComTransCheck JC @@end / data not received yet MOV BX,OFFSET init_jump_table JMP JumpTable @@end: RETN ENDP Modemlnit PROC ModemCalllnit NEAR / Attempt to dial (send modem pre-dial string) / set call status / get the index of the next / wrap-around and start LL cm prpm m / prepare transmit structure MOV [tx.txxnxtst], OFFSET ModemCallInit2 / set state following transmission SUBSTITUTE SHEET RETN @@reset : MOV [ BYTE dial_8tr_num] , DIAL_STR TABLE_SIZE JMP e@getβtr ~ RETN ENDP ModemCalllnit PROC ModemCallInit2 NEAR MOV [sngclst], SNMDMDL / set call status MOV BX, OFFSET dial_number / get the packet length CALL cmfprpmdm / prepare transmit structure MOV [tx.txxnxtst], OFFSET ModemCall / set state following transmission / override default response time and MOV (rx.rxxtmr], TM40SEC / wait 40 seconds for response RETN ENDP ModemCallInit2 PROC ModemCall NEAR MOV [sngclst], SNWTCON / set call status MOV BX,OFFSET dial_reβult_table CALL ComTransCheck ; Check for reply. JC @@end / Data not received yet. MOV BX,OFFSET dial_jump_table / attempt to parse data JMP JumpTable θ@end: RETN ENDP ModemCall PROC snftxchkin NEAR / Query from server received by this point, send data packet / prepare transmit structure MOV AL, [βngdatalen] / get the data segment length ADD AL, 2 / add 2 for type and subtype SUBSTITUTE SHEET MOV [BYTE LOW tx.txxpktlen] , AL MOV [BYTE HIGH tx.txxpktlen], 0 MOV [tx.txxbufp], OFFSET sn_data_start MOV [tx.txxnxtst], OFFSET snfgetpkt / set state following transmission MOV [tx.txxpkttyp], CMTXDATPKT / transmitting data packet MOV [tx.txxtmr], TM3SEC / set transmission timeout MOV [sngstftn], OFFSET cmftx / next state: transmit MOV [rx.rxxtmr], TM10SEC / wait 10 seconds for response MOV [rx rxxstate], OFFSET cmfpack / receiver state: process expected ACK RETN ENDP snftxchkin A******************************************************************* ********** SNFGETPKT - collect packet data PURPOSE: This functions collects packet data and determines if a receive timeout has occurred. PARAMETERS: None RETURNS: Nothing NOTE: •A****************************************************************** ********** PROC snfgetpkt NEAR MOV [sngclst), SNWTNCD set call status CMP [rx.rxxtmr], 0 test for timeout JE @@timeout timed out CALL buf_getchar / retrieve a character JC @@exit / none available, exit CALL [rx.rxxstate] / run the rx state function RETN @@timeout: MOV [sngstftn], OFFSET cmftx / set next Sentinel state function MOV [tx.txxpkttyp], CMTXDLENQ / set transmitter state: send ENQ @@exit: RETN ENDP snfgetpkt SUBSTITUTE SHEET PROC snfsnrst NEAR Reset the Sentinel to a known state (ACTIVE) , assume nothing. CALL buf_flush CMP (sngcomhk], 1 / have we hooked the comm. port JNE βecont / no, continue / yes, unhook the com / the interrupt to install / DS:CX - the com vector to / clear the comm. hooked / DX - IER / Disable all interrupts. / DX - MCR / leave RTS & DTR asserted / MCR OUT2 bit - 0 SUBSTITUTE SHEET Electronic Article Surveillance System Source Code for Host-side Visual C+ + (Microsoft) / *== — ======= — ====. — ========= — ___ ========= — ===* \ Description: Source code for CompuTrace Server and DBServer. Copyright: Copyright 1993-1995 Absolute Software Inc. All Rights Reserved. \* =. ================= = == ============ /define INCL_NOPMAPI // no PM in this program, /define INCL_DOS /define INCL_BSE /include <os2.h> /include <fstream.h> /include <time.h> include <server.h> /include <DB_Objects.HPP> /include <CTMessage.HPP> ///include <packet.h> /include "CT Trans.H" FLAG fQueryCTIDStatus( MessagePipe &Pipe, const QueryCTIDStatusMsg SStatus, CTIDStatusResultMsg SResult ); FLAG fStoreMonitorEvent( MessagePipe &Pipe, const StoreMonitorEventMsg SStore, StoreResultMsg &Result ) ; FLAG fSignalQuit( MessagePipe &Pipe ); void AssignTS( TTimestamp &ts, const SNTL_DATE &Date ); void AssignSNTL_DATE( SNTL_DATE &Date, const TTimestamp &ts ) ; // Temp function. void ProcessClient( TPort SPort, TConnectlnfo &ConnectInfo, CTID_TEXT *text ) ; extern MessagePipe *pipe; // // SntlConnect: called when a CONNECT comand has been received, this function processes SUBSTITUTE SHEET // a transaction between the server and a Sentinel client. // void SntlConnect( TPort SPort, MessagePipe &Pipe, TConnectlnfo *cnct_info ) { WORD msg_type; DosGetDateTime( &cnct_info->start_time ); // Fill start time. TPacket packet( Port ) ; while (TRUE) { // Get a packet. if (packet.rGetPacketf) != TPacket: :TRANS_ACK) { cout « "Packet Error" « endl; return; } // Determine packet type. packet.cbCopyText( &msg_type, sizeof( msg_type ) ); switch( msg_type ) { case CTID_TEXT_TYPE: // Create a new client object. // TClient Client( Port, Pipe, *cnct_info ); // Get CTID Text and add to Client object. CTID_TEXT Text; packet.cbCopyText( &Text, sizeof( Text ) ); // Client.SetCTID( Text ) ; // ProcessClient. // ProcessClient( Client ); ProcessClient( Port, *cnct_info, &Text ) ; return; default: return; } } } void ProcessClient( TPort &Port, TConnectlnfo &ConnectInfo, CTID TEXT *text ) { SNTL_DATE next_call; // ENTER APPLICATION LAYER... // Query the Client state. QueryCTIDStatusMsg StatusMsg; StatusMsg.CTID = (ULONG)text->sn[0] + ( (ULONG)text- >sn[l] « 16) ; CTIDStatusResultMsg Result; cout « "QueryCTIDStatus for CTID " « StatusMsg.CTID « SUBSTITUTE SHEET if (1fQueryCTIDStatus( *pipe, StatusMsg, Result )) { cout « "Error in QueryCTIDStatus!" « endl; } else { cout « "CTIDStatusResult Received..." « endl; cout « " Status = " « (STRING)Result.Status « endl; cout « " PeriodDays = - « Result.PeriodDays « endl; cout « " PeriodMinutes = " « Result.PeriodMinutes « endl; cout « " StolenFlag = " « (STRING)Result.StolenFlag « endl; cout « " SpecialProcess = " « Result.SpecialProcess « endl; cout « " Orgnum = " « Result.Orgnum_n « endl; } // Send NextCall Message back to the Client. CTTimesta p next_ts; AssignTS( next_ts, text->now_date ); if (next ^ ts.usYearO < 1900) { // If date is not valid substitute the local date instead. next_ts = Connectlnfo.start time; } next_ts.AddToDate( 0, 0, Result.PeriodDays, 0, Result.PeriodMinutes ); AssignSNTL_DATE( next_call, next_ts ); SendDatePacket( Port, next_call ); SntlDisconnect( Port, Connectlnfo ); // Store the Monitor Event. StoreMonitorEventMsg Event; Event.StoreAsStolen = Result.StolenFlag; Event.StoreAsExpire - FALSE; Event.LicenseStatus = Result.Status; AssignTS( Event.ClientTS, text->now_date ); Event.ServerTS = Connectlnfo.start_time; Event.NextCallTS_n = Event.ServerTS; Event.NextCallTS_n.AddToDate( 0, 0, Result.PeriodDays, 0, Result.PeriodMinutes ); Event.NextCallClientTS_n = next_ts; Event.CTID = StatusMsg.CTID; Event.TelcoTS_n.Assign( Event.ServerTS.usYear() , Connectlnfo.end. ont , Connectlnfo.end.day, Connectlnfo.end.hour, Connectlnfo.end.minute ); Event.DurationSec_n •= 0; Event.CallerID_n = (const char(*) [CALLERID_SIZE])Connectlnfo.end.number; Event.LineNum = 1; Event.LogFlag = FALSE; SUBSTITUTE SHEET Event.EnvironmentID = "DBS-9508"; Event.ErrorCnt *» 0; StoreResultMsg ResultMsg; cout « endl « "Storing the MonitorEvent... "; if (!fStoreMonitorEvent( *pipe, Event, ResultMsg )) { cout « "Error in StoreMonitorEvent!" « endl; } else { cout « "StoreResult = " « (ResultMsg. esult ? "TRUE" : "FALSE") « endl; } } void SendDatePacket( TPort& Port, const SNTL_DATE& date ) { NC_PACKET packet; packet.header.stx = STX; packet.header.lsb_length = sizeof( NC_TEXT ); packet.header.msb_length -= 0; packet.text.type = NC_TEXT_TYPE; packet.text.next_call_date = date; packet.footer.etx = ETX; packet.footer.Ire = 0; Port.fWritePort( (PVOID)&packet, sizeof( packet ) ); } FLAG fQueryCTIDStatus( MessagePipe &Pipe, const QueryCTIDStatusMsg &Status, CTIDStatusResultMsg &Result ) { TStream in_strm, out_strm; out_strm « Status; if (!Pipe.fTransact( out_strm, in_strm )) return FALSE; in_strm >> Result; if (Result.eType() — CTID_STATUS_RESULT) return TRUE; else return FALSE; } FLAG fStoreMonitorEvent( MessagePipe &Pipe, const StoreMonitorEventMsg &Store, StoreResultMsg &Result ) { TStream in_strm, out_strm; out_strm « Store; SUBSTITUTE SHEET if (!Pipe.fTransact( out_strm, in_strm )) return FALSE; in_strm » Result; if (Result.eType() == STORE_RESULT) return TRUE; else return FALSE; } FLAG fSignalQuit( MessagePipe &Pipe ) { TStream stream; CliQuitMsg QuitMsg; stream « QuitMsg; return Pipe.fSendMessage( stream ); } void SntlDisconnect( TPort &Port, TConnectlnfo &ConnectInfo ) { // Drop DTR. DosSleep( 500 ); // Broc - 13 Feb 95 // Add delay to let modem clear xmt buffer // to fix intermittent modem fault. Port.fDropDTR() ; cout << "Disconnecting..." « flush; DosGetDateTime( &ConnectInfo.end_time ); // Fill end time. DosSleep( 200 ); // Raise DTR. Port.fRaiseDTR() ; } // *** helper functions. UCHAR BCD2ToUChar( BYTE bed ) { // Convert a two digit bed number to decinal. return (bed » 4) * 10 + (bed & OxOF) ; } BYTE UCharToBCD2( UCHAR dec ) { // Convert a 8 bit decimal number to bed. return (dec % 10) + (((dec / 10) % 10) « 4); } USHORT BCD4ToUShort( WORD bed ) { SUBSTITUTE SHEET // Convert a four digit bed number to decimal. return (bed » 12) * 1000 + ((bed & OxOFOO) » 8) * 100 + ((bed & OxOOFO) » 4) * 10 + (bed & OxOOOF) ; } WORD UShortToBCD4( USHORT dec ) { // Convert a 16 bit decimal number to a 4 digit decimal, return (dec % 10) + (((dec / 10) % 10) « 4) + (((dec / 100) % 10) « 8) + (((dec / 1000) % 10) « 12); } void AssignTS( TTimestamp &ts, const SNTL_DATE &Date ) { ts.Assign( BCD2ToUChar( Date.year ), BCD2ToUChar( Date.month ), BCD2ToUChar( Date.day ), BCD2ToUChar( Date.hour ), BCD2ToUChar( Date.minute ) ); } void AssignSNTL_DATE( SNTL_DATE &Date, const TTimestamp &ts ) { Date.year = UCharToBCD2( ts.usYear() % 100 ); Date.month = UCharToBCD2( ts.usMonth() ) ; Date.day =• UCharToBCD2( ts.usDay() ) ; Date.hour = UCharToBCD2( ts.usHour() ); Date.minute = UCharToBCD2( ts.usMinute() ); } /* inline BYTE HiNibble( BYTE b ) { return (BYTE)((b & OxFO) » 4); } inline BYTE LoNibble( BYTE b ) { return (BYTE) (b & OxOF); } void AddDays( SNTL_DATE *next_call, int days ) { static BYTE days_per_month[18] = { 0x31, SUBSTITUTE SHEET Oct Dec }; BYTE old_day = next_call->day; // Save for BCD adjust. // Add the days to the current date. next_call->day += days; // Check if we passed the end of the current month. if (next_call->day > days_per_month[next_call->month]) { // Add one to month. if (++next_call->month > 12) { next_call->month = 1; ++next_call->year; } next_call->day -= days_per_month[next_call->month] • 1; // Roll over to proper day. } // Adjust the day back to BCD. if (LoNibble( next_call->day ) > 0x9 J J HiNibble( next_call->day ) != HiNibble( old_day )) next_call->day += 6; // Adjust the month to BCD. if (LoNibble( next_call->month ) > 0x9) next_call- >month += 6; // Adjust the year back to BCD. if (LoNibble( next_call->year ) > 0x9) next_call->year +- 6; if (HiNibble( next_call->year ) > 0x9) next_call->year = LoNibble( next_call->year ); } */ /define INCL_DOSNMPIPES /include <os2.h> /include <iostream.h> /include <fstream.h> /include <string.h> /include <server.h> /include "DBServer.H" /include <usertype.h> /include <DB_Objects.HPP> /include <CTID.H> /include <CTIMS.HPP> /include <CTMessage.HPP> /include <MessagePipe.HPP> SUBSTITUTE SHEET FLAG fProcessClientEvent( MessagePipe &Pipe, TStream SMsgStream ) ; FLAG fProcessQueryCTIDStatus( MessagePipe &Pipe, QueryCTIDStatusMsg &Status ) ; FLAG fProcessStoreMonitorEvent( MessagePipe &Pipe, StoreMonitorEventMsg &MEvent ) ; FLAG fUpdateLieenseStatus( StoreMonitorEventMsgfi ) ; // Helper functions. FLAG _fCopyTStoDBVars( char *tsstring, short *indicator, CTTimestamp its, STRING varname = "Timestamp" ) ; DataBase DB; int main( int argc, char *argv[] ) { if (argc != 3) { cout << "Usage: dbserver <database_name> <pipe_name>" « endl; } DB.SetName( argv[1] ) ; SvrMsgPipeFactory Factory( argv[2], 512, 10 ); MessagePipe *pipe; if (!DB.fConnect()) { cout « "Unable to connect to " « argv[l] « " SQLCODE = " « (long)DB.ulSQLCode() « endl; return 1; } if (!Factory.fCreatePipe( pipe )) { cout « "Unable to create pipe DosErrorCode = " « Factory.rcDosErrorCode() « endl; return 2; } cout « "Waiting for pipe to connect to client..." « endl; if (!pipe->fOpenPipe() ) { cout « "Error connecting to the client DosErrorCode = " « pipe->rcDosErrorCode() « endl; return 2; } cout << "Pipe connected to client." « endl; TStream MsgStream; while (fProcessClientEvent( *pipe, MsgStream )) MsgStream.Reset() ; pipe->fClosePipe() ; return 0; } SUBSTITUTE SHEET FLAG fProcessClientEvent( MessagePipe &Pipe, TStream &MsgStream ) { if (!Pipe.fGetMessage( MsgStream )) { cout « "Error reading message from pipe DosErrorCode = " « Pipe.rcDosErrorCode() « endl; return FALSE; } CTMessageHeader Header; MsgStream » Header; switch (Header.eType() ) { case QUERY_CTID_STATUS: { QueryCTIDStatusMsg StatusMsg( Header ) ; MsgStream » *(QueryCTIDStatus*)&StatusMsg; if (!fProcessQueryCTIDStatus( Pipe, StatusMsg )) cout « "Error in fProcessQueryCTIDStatus, SQLCODE = " « (long)ulGetSQLCode() « endl; } break; case STORE_MONITOREVENT: { StoreMonitorEventMsg EventMsg( Header ) ; MsgStream » *(StoreMonitorEvent*) SEventMsg; if (!fProcessStoreMonitorEvent( Pipe, EventMsg )) { cout « "Error in fProcessStoreMonitorEvent, SQLCODE = " « (long)ulGetSQLCode() « endl; } } break; case CLI_QUIT: return FALSE; default: cout « "Unknown Command Received!" « endl; return FALSE; } return TRUE; } FLAG fProcessQueryCTIDStatus( MessagePipe &Pipe, QueryCTIDStatusMsg &CTID ) { _CTlicense Rec; CTIDStatusResultMsg ResultMsg; if (!fXlatCliCTID( CTID.CTID, CTID.CTID )) { cout « "Error converting client CTID to server CTID" « endl; // Proccess error here. } SUBSTITUTE SHEET ResultMsg.QueryResult =• _fQueryLicense( &Rec, CTID.CTID ) ; if (!ResultMsg.QueryResult) { ResultMsg.CTID * CTID.CTID; ResultMsg.Status CTLicStatus: :ACTIVE; ResultMsg.PeriodDays ' 2; ResultMsg.PeriodMinutes ResultMsg.StolenFlag - FALSE; ResultMsg.SpecialProcess ResultMsg.Orgnum_n .fSetNullO ResultMsg.LastCallTS_n .fSetNullO ResultMsg.NextCallTS n •fSetNullO ResultMsg.NextCallClTentTS_n .fSetNullO ResultMsg.ProductType .fSetNull() } else { ResultMsg.CTID Rec.CTID; ResultMsg.Status Rec.LicStatus; ResultMsg.PeriodDays Rec.PeriodDays; ResultMsg.PeriodMinutes Rec.PeriodMinutes; ResultMsg.StolenFlag Rec.StolenFlag == ResultMsg.SpecialProcess = Rec.SpecialProcess; ResultMsg.LastCallTS_n .Assign( Rec.LastCallTS_N, DB_ISNULL( Rec.IsNullJLastCallTS ) ); ResultMsg.NextCallTS_n .Assign( Rec.NextCallTS_N, DB_ISNULL( Rec.IsNull_NextCallTS ) ); ResultMsg.NextCallClientTS_n .Assign( Rec.NextCallClientTS_N, DB_ISNULL( Rec.IsNull_NextCallClientTS ) ); if (DB_ISNULL( Rec.IsNull_Orgnum )) ResultMsg.Orgnum_n .fSetNull() ; else ResultMsg.Orgnum_n = Rec.Orgnum_N; ResultMsg.ProductType = Rec.ProductType; } cout « "SQLCODE = " « (long)ulGetSQLCode() « endl; // Return Query results. TStream Stream; Stream « ResultMsg; return Pipe.fSendMessage( Stream ); } FLAG fProcessStoreMonitorEvent( MessagePipe &Pipe, StoreMonitorEventMsg &Msg ) { StoreResultMsg ResultMsg; // Prepare reply message. ResultMsg.Result = TRUE; SUBSTITUTE SHEET // Prepare the monitorevent data. _CTmonitorEvent Rec; if (!fXlatCliCTID( (ULONG&)Rec.CTID, Msg.CTID )) { cout « "Error converting client CTID to server CTID" « endl; // Proccess error here. } _fCopyTStoDBVars( Rec.ServerTS, NULL, Msg.ServerTS, "ServerTS" ) ; _fCopyTStoDBVars( Rec.ClientTS, NULL, Msg.ClientTS, "ClientTS" ); _fCopyTStoDBVars( Rec.TelcoTS_N, &Rec.IsNull_TelcoTS, Msg.TelcoTS_n, "TelcoTS" ); Rec.DurationSec_N = Msg.DurationSec_n; Rec.IsNull_DurationSec = DB_NOT_NULL; if (!Msg.CallerID_n) { Rec.IsNull_CallerID - DB_NULL; } else { Rec.IsNull_CallerID = DB_NOT_NULL; strncpy( Rec.CallerlD N, Msg.CallerID_n, sizeof( Rec.CallerID_N ) ); } Rec.LineNum •= Msg.LineNum; if (!Msg.LogFlag) { cout « "INVALID_DATA_ERROR: LogFlag is NULL, defaulting to FALSE" « endl; Rec.LogFlag = 'N'; } else { Rec.LogFlag = ( (STRING) sg.LogFlag) [0] ; } strncpy( Rec.EnvironmentID, Msg.EnvironmentID, sizeof( Rec.EnvironmentID ) ); Rec.ErrorCnt = Msg.ErrorCnt; // Update the License Record. if (!fUpdateLieenseStatus( Msg ) ) { if (ulGetSQLCode() != 100) { cout « "DB2_ERROR: Error updating License Table, CliCTID = " « Msg.CTID « " SQLCODE = " « (long)ulGetSQLCode() « endl; } } // Perform the insert. SUBSTITUTE SHEET if (!_fInsertIntoMonitorEvent( &Rec )) { ResultMsg.Result = FALSE; } else { if (Msg.StoreAsStolen) { if (!_fInsertIntoMonitorEventStolen( &Rec )) { ResultMsg.Result = FALSE; > } if (Msg.StoreAsExpire) { if (J_fInsertIntoMonitorEventExpired( &Rec )) { ResultMsg.Result = FALSE; • } } } cout « "SQLCODE = " « (long)ulGetSQLCode() « endl; TStream Stream; Stream « ResultMsg; if (Pipe.fSendMessage( Stream ) && ResultMsg.Result == TRUE) { DB.Commit() ; return TRUE; } else { DB.Rollback() ; return FALSE; } } FLAG fUpdateLieenseStatus( StoreMonitorEventMsg &Msg ) { _CTupdateLicenseStatus Rec; short dummy1; // Used to quiet the Null validation below. fXlatCliCTID( (ULONG&)Rec.CTID, Msg.CTID ); strncpy( Rec.Status, Msg.LicenseStatus, sizeof( Rec.Status ) ) ; _fCopyTStoDBVars( Rec.LastCallTS_N, fidummyl, Msg.ServerTS, "LastCallTS" ); _fCopyTStoDBVars( Rec.NextCallTS_N, Sdummyl, Msg.NextCallTS_n, "NextCallTS" ); _fCopyTStoDBVars( Rec.NextCallClientTS_N, Sdummyl, Msg.NextCallClientTS_n, "NextCallClientTS" ) ; if (!Msg.NextCallTS_n) strcpy( Rec.NextCallTS_N, "0001-01-01-00.00.00.000000" ) ; if (!Msg.NextCallClientTS_n) strcpy( Rec.NextCallClientTS_N, "0001-01-01-00.00.00.000000" ); return _fUpdateLieenseStatus( &Rec ) ; SUBSTITUTE SHEET FLAG _fCopyTStoDBVars( char *tsstring, short *indicator, CTTimestamp fits, STRING varname ) { if (Its) { if (indicator == NULL) { cout « "INVALID_DATA_ERROR: " « varname « - is NULL, forcing validation" « endl; ts.ForceValidate() ; } else { ♦indicator = DB_NULL; tsstring . O] = ' \x0 ' ; return FALSE; } } else if (!ts.fValidate() ) { cout « "INVALID_DATA_ERROR: " « varname « " is invalid, forcing validation - " « ts << endl; ts.ForceValidate() ; } if (indicator != NULL) *indicator = DB_NOT_NULL; ts.ToSTRING( tsstring ); return TRUE; /define INCL_NOPMAPI // no PM in this program /define INCL_DOS /define INCL_BSE /define INCL_DOSSEMAPHORES /define INCL_DOSNMPIPES /include <os2.h> /include <ctype.h> /include <stdlib.h> /include <iostream.h> /include <fstream.h> /include <server.h> /include <MessagePipe.HPP> /include <TModem.HPP> /include "CT_Trans.H" / ♦ GLOBAL VARIABLES*****************************************/ HEV hQuitSe ; SUBSTITUTE SHEET // Temp, move to thread. CltMsgPipeFactory *factory; MessagePipe *pipe; /************************ **/ FLAG fLoadLineThreads( TModem&, PCSZ, PCSZ ); void _Optlink CT_CommandThread( PVOID ); FLAG fParseCmd( TPort &Port, TConnectlnfo *CnctInfo, STRING buffer ) ; TPort: :ComSettings ComSetting = { "COM1", // port name 0, // not used 38400, // bps 8, // data bits TPort::NO, // no parity TPort: :ONE // one stop bit }; int main( int argc, char *argv[] ) { APIRET re; cout << "Co puTrace Server V0.99q" « endl; // Check arguments, if (argc != 4) { cout « "Usage: server <pipe_name> <port_name> <init_string>" « endl « endl; return 0; } // Create quit semaphore. if ((re = DosCreateEventSem( NULL, ShQuitSem, 0, FALSE )) •= 0) return 1; factory = new CltMsgPipeFactory( argv[l], 512 ); // Load port server threads. TPort Port; TModem Modem -= Port; if (!fLoadLineThreads( Modem, argv[2J, argv[3] )) return 2; cout « "Successfully connected to local modem" « endl; // Wait for quit signal. DosWaitEventSem( hQuitSem, SEM INDEFINITE WAIT ); return 0; } SUBSTITUTE SHEET // // fLoadLineThreads: Loads the threads to operate a server line. This function // should be called for each server line. // FLAG fLoadLineThreads( TModem SModem, PCSZ port_str, PCSZ init_str ) { // Start port log. // Port.LogOn() ; // Open port. ComSetting.port_name = port_str; if (!Modem.Port() .fθpenPort( ComSetting )) { cout « "Error openning port" « endl; return FALSE; } // Start the port manage thread. if (!Modem.Port() .fStartManageThread()) { cout « "Thread execution error" « endl; return FALSE; } // Initialize the modem. STRING result = Modem.strSendCo mand( init_str, -1 ); if (strcmp( result, "OK" ) != 0) { cout « "Error initiallizing modem" « endl; return FALSE; } // Connect pipe to dbserver. if (!factory->fCreatePipe( pipe )) return FALSE; if (!pipe->fθpenPipe() ) return FALSE; // Start the command thread. if (!Modem.Port() .fStartCommandThread( CT_CommandThread, (PVOID)&Modem )) { cout « "Thread execution error" « endl; Modem.Port() .KillManageThread() ; return FALSE; } return TRUE; // // CT_CommandThread: Processes incoming data from a server line. // void _Optlink CT_CommandThread( PVOID ptr ) { SUBSTITUTE SHEET TModem &Modem = *(TModem*)ptr; // Alias (should be optimized out by the compiler) . // Thread local variables STRING result; TConnectlnfo cnct_info; while (TRUE) { result' » Modem.strGetString( -1 ); // Parse buffer for cmd. if (!fParseCmd( Modem.Port(), &cnct_info, result )) { meιrtset ( (PVOID) &cnct_info, ' \x0 ' , sizeof cnct_inf o ) ; } } } /define CND DATE_FIELD "DATE -" /define CND~TIME_FIELD "TIME =" /define CND_NUMBER_FIELD "NMBR -" /define CND NONUM_FIELD "REASON FOR NO NUMBER:" /define CND~NAME_FIELD "CALLER NAME:" /define CND_NONAME_FIELD "REASON FOR NO NAME:" // // fParseCmd: called when a ' \n' has been received, this function will process the string. // Returns TRUE if a transaction is occuring, FALSE if the buffers should be cleared. // FLAG fParseCmd( TPort &Port, TConnectlnfo *cnct_info, STRING buffer ) { const char *index; // Parse command. if (strstr( buffer, "RING" ) != NULL) { cout « "Command parsed as RING" « endl; } else if ((index = strstr( buffer, CND_DATE_FIELD )) != NULL) { index += sizeof CND_DATE_FIELD; while (!isdigit( *index )) index++; // Grab the month. if (!isdigit( *index ) j | !isdigit( *(index+l) )) return FALSE; cnct_info->cnd.month = (*index++ - / 0 ) * 10; cnct_info->cnd.month += *index++ - '0'; // Grab the day. if (!isdigit( *index ) | | !isdigit( *(index+l) )) return FALSE; cnct info->cnd.day = (*index++ - '0') * 10; SUBSTITUTE SHEET cnct_info->cnd.day +•= *index++ - '0'; cout « buffer « endl; } else if ((index = strstr( buffer, CND_TIME_FIELD )) != NULL) { index +-*= sizeof CND_TIME_FIELD; while (!isdigit( *index )) index++; // Grab the hour. if (!isdigit( *index ) \ \ !isdigit( *(index+l) )) return FALSE; cnct_info->cnd.hour = (*index++ - 0 ) * 10; cnct_info->cnd.hour += *index++ - '0'; // Grab the minute. if (!isdigit( *index ) |} !isdigit( *(index+l) )) return FALSE; cnct_info->cnd.minute = (*index++ - '0') * 10; cnct_info->cnd.minute += *index++ - 'O' ; cout « buffer « endl; } else if ((index = strstr( buffer, CND_NUMBER_FIELD )) != NULL) { index += sizeof CND_NUMBER FIELD; while (isspace( *index )) Tndex++; // Grab the number. for (int i = 0; i < CND_NUM_MAXLEN; i++) { if (indexfi] == '\xθ' \ \ indexfi] =•= '\r') { cnct_info->cnd.number[i] = '\x0'; break; } else { cnct_info->cnd.number[i] = index[i]; } } cout << buffer « endl; } else if (strstr( buffer, CND_NONUM_FIELD ) != NULL) { index +- sizeof CND_NONUM_FIELD; // Grab the string. while (isspace( *index )) index++; for (int i = 0; i < CND_NUM_MAXLEN; i++) { if (indexfi] == '\xθ' }j index[i] =•= '\r') { cnct_info->cnd.number[i] •= ' \x0 ' ; break; } else { cnct_info->cnd.number[i] = index[i]; } } cout « buffer « endl; } else if (strstr( buffer, CND_NAME_FIELD ) != NULL) { index += sizeof CND NAME FIELD; SUBSTITUTE SHEET // Grab the name. while (isspace( *index )) index++; for (int i = 0; i < CND_NAME MAXLEN; i++) { if (index[i] == '\x0' | j Tndex[i] == '\r') { cnct_info->cnd.name[i] = , \x0'; break; } else { cnct_info->cnd.name[i] = indexfi]; } } cout « buffer « endl; } else if (strstr( buffer, CND_NONAME_FIELD ) != NULL) { index += sizeof CND_NONAME_FIELD; // Grab the string. while (isspace( *index )) index++; for (int i = 0; i < CND_NAME MAXLEN; i++) { if (index[i] — '\x0' |j Tndex[i] == '\r') { cnct_info->cnd.name[i] = '\x0'; break; } else { cnct_info->cnd.name[i] = index[i]; } } cout « buffer « endl; } else if (strstr( buffer, "CONNECT" ) != NULL) { cout << "Command parsed as CONNECT" « endl; SntlConnect( Port, *pipe, cnct_info ); return FALSE; } else if (strstr( buffer, "NO CARRIER" ) != NULL) { cout « "Command parsed as NO CARRIER" « endl; return FALSE; } else if (strstr( buffer, "OK" ) != NULL) { cout « "Command parsed as OK" « endl; return FALSE; } else if (strstr( buffer, "ERROR" ) != NULL) { cout << "Command parsed as ERROR" « endl; return FALSE; } else { cout « "Unknown command received: " << buffer « endl; return FALSE; } return TRUE; SUBSTITUTE SHEET } /include <CTIMS.HPP> f ι =============================================== II 1/ CTStatus friends and members. // CTStatus: :CTStatus() { memset( value, ' ' , sizeof( value ) ); } CTStatus: :CTStatus( STRING str ) { ASSERT( strlen( str ) < sizeof( value ) ); memcpy( value, str, strlen( str ) ) ; } const char CTLicStatus: :STR_SET[] [CT_TOK_SIZE+l] = { UNUSED_TOK, NOTEST_TOK, ACTIVE_TOK, EXPIRED_TOK }; CTLicStatusϋ CTLicStatus::operator = ( STRING str ) { for (int i = 0; i <= EXPIRED; i++) { if (strcmp( STR_SET[i], str ) == NULL) { setNotNull() ; value = VALUE( i ) ; return *this; } } ASSERT( FALSE ); // No match was found for the string. return *this; } /********************** FLAG CTOrgnum: :fSetPrefix( STRING str ) { if (strlen( str ) != ORGNUM_PREFIX_SIZE) { return FALSE; } else { value[0] = str[0]; value[1] = strfl]; value[2] = str[2]; value[3] = str[3]; SUBSTITUTE SHEET return TRUE; } } FLAG CTOrgnum: :fSetIndex( UINT num ) { if (num > 9999) { return FALSE; } else { value[ORGNUM_PREFIX_SIZE + 0] = (num%10000) / 1000 + '0'; value[ORGNUM_PREFIX_SIZE + 1] = (num%1000) / 100 + '0'; value[ORGNUM_PREFIX SIZE + 2] = (num%100) / 10 + '0' ; " ~ value[ORGNUM_PREFIX_SIZE + 3] = (num % 10) + '0'; } } FLAG CTOrgnum: :fGetPrefix( char *str ) const { if (strlen( str ) != ORGNUM_PREFIX_SIZE) { return FALSE; } else { str[0] = value[0] ; str[l] = valuefl]; str[2] = value[2] ; str[3] = value[3]; str[4] = '\x0'; } } FLAG CTOrgnum: :fGetIndex( UINT &i ) const { i = atoi( &(value[ORGNUM_PREFIX_SIZE]) ); return TRUE; } FLAG CTOrgnum: :fGeneratePrefix( STRING org_name ) { char pre[ORGNUM_PREFIX_SIZE]; // Grab first four alphanu characters. for (int i = 0, j = 0; i < ORGNUM_PREFIX_SIZE;) { if (isalnum( orgname[j++] )) pre[i]; } } **********************/ //************************** *********************** // // iostrea stream operators. SUBSTITUTE SHEET // ostream& operator «( ostream &os, const CTStatus &status ) { return os « (STRING)status; } //******************************************************* ********************** // // TStream stream operators. // TStreamfc operator « ( TStream &buf, const CTStatus &status ) { buf « *(TNull*)&status; if (!status) return buf; else return buf.Put( PVOID( status.value ), sizeof( status.value ) ); } TStreamS operator » ( TStream &buf, CTStatus fcstatus ) { buf » *(TNull*)&status; if (!status) return buf; else return buf.Get( status.value, sizeof( status.value ) ) ; } TStreami operator << ( TStream &buf, const CTCallerlD &id ) { buf « *(TNull*)&id; if (!id) return buf; else return buf.Put( PVOID( id.value ), sizeof( id.value ) ) ; } TStrea fc operator » ( TStream &buf, CTCallerlD &id ) { buf » *(TNull*)&id; if (!id) return buf; else return buf.Get( id.value, sizeof( id.value ) ); } TStream& operator « ( TStream &buf, const CTLicStatus ilic ) { buf « *(TNull*)&lic; if (!lic) return buf; else return buf « USHORT( lie.value ); } TStreamS operator » ( TStream Sbuf, CTLicStatus &lic ) { SUBSTITUTE SHEET USHORT num; buf » *(TNull*)&lic; if (!lic) return buf; else { buf » num; lie.value = CTLicStatus: :VALUE( num ); return buf; } } TStream& operator « ( TStream &buf, const CTOrgnum &num ) { buf « *(TNull*)&num; if (!num) return buf; else return buf.Put( PVOID( num.value ), sizeof( num.value ) ) ; } TStream& operator » ( TStream &buf, CTOrgnum &num ) { buf » *(TNull*)&num; if (!num) return buf; else return buf.Get( num.value, sizeof( num.value ) ); } TStreamS operator « ( TStream &buf, const CTMonitorEvent &event ) { return buf « event.CTID « event.ServerTS « event.ClientTS « event.TelcoTS_n « event.DurationSec_n « event.CallerID_n « event.LineNum << event.LogFlag « event.EnvironmentID « event.ErrorCnt; } TStreamfc operator >> ( TStream &buf , CTMonitorEvent &event ) { SUBSTITUTE SHEET » event.ErrorCnt; /include <CTMessage.HPP> //******************************************************* ********************** // // TStream stream operators. // TStreamS operator « ( TStream &buf, const CTMessageHeader &head ) { return buf « head.ID « head.Type « head.Len; } TStreamS operator » ( TStream &buf, CTMessageHeader &head ) { buf » head.ID; buf » head. ype; buf » head.Len; return buf; /define INCL_NOPMAPI // no PM in this program /define INCL_DOS /define INCL_BSE /define INCL_DOSSEMAPHORES /define INCL_DOSNMPIPES /include <os2.h> /include "CT Buffer.HPP" CT_Buffer: :CT_Buffer() ~: head( 0 ) , tail( CT_BUFFER_MAXLEN ) { // Create the mutex sem. re = DosCreateMutexSem ( NULL, fihBufSem, 0 , 0 ) ; if (re) { } // Create the event sem. re = DosCreateEventSem( NULL, ShReleaseGetSem, 0, 0 ) ; } CT_Buffer: :-CT_Buffer() { DosCloseMutexSem( hBufSem ) ; } void CT_Buffer: :Flush() SUBSTITUTE SHEET { ULONG post_count; DosRequestMutexSem( hBufSem, SEM_INDEFINITE_WAIT ); head = 0; tail = CT_BUFFER_MAXLEN; DosResetEventSem( hReleaseGetSem, &post_count ); DosReleaseMutexSem( hBufSem ) ; FLAG CT_Buffer: :fPutChar( char eh ) { FLAG ret_val; // Get ownership of the semaphore. re = DosRequestMutexSe ( hBufSem, SEM_INDEFINITE_WAIT ); if (re) return FALSE; // First check that the log buffer hasn't overflown, if (!fIsFull()) { // Store the char, update head, signal the event, buffer[head] = ch; head = IncBufPtr( head ) ; DosPostEventSe ( hReleaseGetSem ); ret_val = TRUE; > else ret_val = FALSE; // Release the semaphore. DosReleaseMutexSem( hBufSem ) ; return ret_val; } FLAG CT Buffer: :fGetChar( char &ch ) { ULONG post_count; FLAG ret_val; // If empty wait for timeout. if (flsEmptyO) DosWaitEventSem( hReleaseGetSem, SEM_INDEFINITE_WAIT ) ; // Get ownership of the semaphore. re = DosRequestMutexSem( hBufSem, SEM_INDEFINITE_WAIT ); if (re) return FALSE; if (IflsEmptyO) { // Fetch the char, update tail. tail = IncBufPtr( tail ); ch = bufferftail] ; ret_val = TRUE; } SUBSTITUTE SHEET else ret_val = FALSE; DosResetEventSe ( hReleaseGetSem, &post_count ); // Release the semaphore. DosReleaseMutexSem( hBufSem ) ; return ret_val; } /define INCL_NOPMAPI // no PM in this program /define INCL_DOS /define INCL_BSE /define INCL_DOSSEMAPHORES /define INCL_DOSNMPIPES /include <os2.h> /include "CT_Log.HPP" /include <fstream.h> CT_Log: :CT_Log( UINT len ) : buf_len( len ) , index( 0 ) if ((buffer = new BYTE[buf_len]) == NULL) buf_len = index = 0; } } CT_Log: :-CT_Log() { if (buffer) DosFreeMem( buffer ); } BOOL CT_Log: :fPostChar( char ch ) { // First check that the log buffer hasn't overflown. if (!fIsFull()) { // Store the char, update head, buffer[index++] = ch; return TRUE; } else return FALSE; } BOOL CT_Log::fDumpLog( const char *fname ) { fstream dump; dump.open( fname, ios::out ); if (Idump) return FALSE; dump.write( buffer, index ); dump.close() ; SUBSTITUTE SHEET return TRUE; } /define INCL_DOSNMPIPES /include <os2.h> /include <MessagePipe.HPP> //******************************************************* ********************** // SvrMsgPipeFactory Implementation. /******************************************************* ********************** SvrMsgPipeFactory: :SvrMsgPipeFactory( PCSZ name, UINT msg_len, UINT pipe_len ) : MsgPipeFactory( msg_len ) , pipe_name( name ) , pipe_len( pipe_len ) {} FLAG SvrMsgPipeFactory: :fCreatePipe( MessagePipe *&ppipe ppipe = new MessagePipe( this ) ; return TRUE; FLAG SvrMsgPipeFactory: :fDestroyPipe( MessagePipe *ppipe delete ppipe; return TRUE; FLAG SvrMsgPipeFactory: :fOpenPipe( MessagePipe *pipe ) HPIPE hPipe; / Create and connect the named pipe. pipe->rc = DosCreateNPipe( (PSZ)pipe_name, &hPipe, NP NOWRITEBEHIND | Data sent to remote pipes Tmmediatly. NP_ACCESS_DUPLEX, Two-way client/server communications. NP_WAIT j I/O to pipe blocked until data available. NP_TYPE_MESSAGE j Message pipe type. NP_READMODE_MESSAGE j Messafe read mode type. OxOOFF, Infinite number of allowed instances of this pipe. SUBSTITUTE SHEET (uMaxMsgLen() + 2) * pipe_len,// Size of output buffer. (uMaxMsgLen() + 2) * pipe_len,// Size of input buffer. // Client open timeout (see DosWaitNPipe) . ); if (pipe->rc) return FALSE; pipe->rc = DosConnectNPipe( hPipe ) ; if (pipe->rc) return FALSE; pipe->SetHandle( hPipe ); return TRUE; FLAG SvrMsgPipeFactory: :fClosePipe( MessagePipe *pipe ) { HPIPE hPipe = pipe->GetHandle() ; // Wait till the pipe is empty. pipe->rc = DosResetBuffer( hPipe ); if (pipe->rc) return FALSE; // Disconnect the pipe handle. pipe->rc = DosDisConnectNPipe( hPipe ) ; if (pipe->rc) return FALSE; return TRUE; } //******************************************************* ********************** // CltMsgPipeFactory Implementation. //******************************************************* ********************** CltMsgPipeFactory: :CltMsgPipeFactory( PCSZ name, UINT msg_len ) : MsgPipeFactory( msg_len ) , pipe_name( name ) {} FLAG CltMsgPipeFactory: :fCreatePipe( MessagePipe *&ppipe ) { ppipe = new MessagePipe( this ); return TRUE; } FLAG CltMsgPipeFactory: :fDestroyPipe( MessagePipe *ppipe ) { delete ppipe; SUBSTITUTE SHEET return TRUE; } FLAG CltMsgPipeFactory: :fOpenPipe( MessagePipe *pipe ) { HPIPE hPipe; ULONG ulAction; pipe->rc = Dosθpen( pipe_name, &hPipe, fiulAction, 0, FILE_NORMAL, FILE_OPEN, OPEN_ACCESS_READWRITE J OPEN_SHARE_DENYNONE, (PEAOP2 ) NULL ) ; if (pipe->rc) return FALSE; pipe->SetHandle( hPipe ); return TRUE; } FLAG CltMsgPipeFactory: :fClosePipe( MessagePipe *pipe ) { HPIPE hPipe = pipe->GetHandle() ; // Wait till the pipe is empty. pipe->rc •= DosResetBuffer( hPipe ); if (pipe->rc) return FALSE; // Close the pipe handle. re = DosClose( hPipe ); if (pipe->rc) return FALSE; return TRUE; //******************************************************* ********************** // MessagePipe Implementation //******************************************************* ********************** MessagePipe: :MessagePipe( MsgPipeFactory *mom ) : factory( mom ) { factory->InitPipe( this ); } MessagePipe: :-MessagePipe() { factory->DeinitPipe( this ) ; } FLAG MessagePipe: :fOpenPipe() { return factory->fOpenPipe( this ); } SUBSTITUTE SHEET FLAG MessagePipe: :fClosePipe() { return factory->fClosePipe( this ) ; } FLAG MessagePipe: :fSendMessage( PCVOID msg, ULONG msg_len ) { ULONG cbWritten; re = DosWrite( hPipe, (PVOID)msg, msg_len, &cbWritten ); return (re == 0 && msg_len == cbWritten) ? TRUE : FALSE; } FLAG MessagePipe: :fGetMessage( PVOID msg, PULONG msg_len ) { // PRECONDITION( msg_len != 0 && *msg_len <= uMaxMsgLen() ) ; re = DosRead( hPipe, msg, *msg_len, msg_len ); return (re == 0) ? TRUE : FALSE; } FLAG MessagePipe: :fTransact( PCVOID out_msg, ULONG out_msg_len, PVOID in_msg, PULONG in_msg_len ) { // PRECONDITION( in_msg_len != 0 && *in_msg_len <= uMaxMsgLen() ) ; re = DosTransactNPipe( hPipe, (PVOID)out_msg, out_msg_len, in_msg, *in_msg_len, in_msg_len ) ; return (re == 0) ? TRUE : FALSE; } MessagePipe: :PIPE_STATE MessagePipe: :estate() { ULONG cbRead; AVAILDATA avail; ULONG state; // Use DosPeekNPipe to find the state of the pipe. re = DosPeekNPipe( hPipe, NULL, 0, ScbRead, &avail, &state ) ; return (PIPE_STATE)state; } /ifdef OS2 /define INCL DOSDATETIME SUBSTITUTE SHEET /include <os2.h> /endif /include <ctype.h> /include <Objects.HPP> //******************************************************* ********************** // // TFlag members. // TFlag: :TFlag() : TNull( TRUE ) {} TFlag: :TFlag( FLAG flag ) : value( (flag != FALSE) ), TNull( FALSE ) {} TFlag: :-TFlag() { /ifdef DEBUG fSetNullO ; value = UNINIT_DATA; /endif } //******************************************************* ********************** // // TTimestamp members. // const UINT TTimestamp: :TSStringLen = 27; TTimestamp: :TTimestamp() : TNull( TRUE ) { /ifdef DEBUG Year = Month = Day = Hour = Minute = Second = Millisec - UNINIT_DATA; /endif } TTimestamp::TTimestamp( USHORT yr, UCHAR mo, UCHAR dy, UCHAR hr, UCHAR mn, UCHAR SC, USHORT ms ) : Year( yr ) , Month( mo ) , Day( dy ) , Hour( hr ) , Minute( mn ) , SUBSTITUTE SHEET Second( sc ) , Millisec( ms ) , TNull( FALSE ) {} TTimestamp: :-TTimestamp() { /ifdef DEBUG fSetNullO ; Year = Month = Day = Hour = Minute = Second = Millisec = UNINIT_DATA; /endif } FLAG TTimestamp: :fValidate() const { if (fIsNullO) return FALSE; // Check year. if (!Year | J Year > 9999) return FALSE; // Check month and day. if (!Day) return FALSE; switch (Month) { case 1: if (Day > 31) return FALSE; break; case 2: if (Year % 4 — 0 && Year % 100 != 0) // Check for a leapyear. if (Day > 29) return FALSE; else if (Day > 28) return FALSE; break; case 3: if (Day > 31) return FALSE; break; case 4: if (Day > 30) return FALSE; break; case 5: if (Day > 31) return FALSE; break; case 6: if (Day > 30) return FALSE; break; case 7: if (Day > 31) return FALSE; brea ; case 8: if (Day > 31) return FALSE; break; case 9: if (Day > 30) return FALSE; break; case 10: SUBSTITUTE SHEET if (Day > 31) return FALSE; break; case 11: if (Day > 30) return FALSE; break; case 12: if (Day > 31) return FALSE; break; default: return FALSE; } // Check hours. if (Hour > 23) { if (Hour > 24 ! I Minute j | Second J j Millisec) return FALSE; } // Check minutes, seconds and milliseconds. if (Minute > 59 | J Second > 59 j j Millisec > 999) return FALSE; return TRUE; } void TTimestamp: :ForceValidate() { setNotNull() ; Year = Month = Day = 1; Hour = Minute = Second = Millisec - 0; } FLAG TTimestamp: :fIsValidTSString( STRING ts ) { if ( isdigit ts[0] // Check Year. && isdigit ts[l] && isdigit ts[2] && isdigit ts[3] && ts[4] == && isdigit ts[5] && isdigit ts[6] && ts[7] == && isdigit ts[8] && isdigit ts[9] && ts[10] := I — I && isdigit ts[ll] && isdigit ts[12] && ts[13] = - / && isdigit ts[14] && isdigit ts[15] && ts[l6] == ' ' && isdigit ts[17] && isdigit ts[18] && ts[19] < - r i && isdigit ts[20] && isdigit ts[21] && isdigit ts[22] SUBSTITUTE SHEET && isdigit( ts[23] ) && isdigit( ts[24] ) && isdigit( ts[25] ) && ts[26] — '\x0') return TRUE; else return FALSE; } TTimestamp& TTimestamp: :Assign( const TTimestamp &ts ) { if (Its) { fSetNullO ; } else { setNotNull(); Year = ts.Year; Month = ts.Month; Day = ts.Day; Hour = ts.Hour; Minute = ts. inute; Second = ts.Second; Millisec = ts.Millisec; } return (*this) ; } TTimestampS TTimestamp: :Assign( USHORT yr, UCHAR mo, UCHAR dy, UCHAR hr, UCHAR mn, UCHAR sc, USHORT ms ) { setNotNull() ; Year = yr; Month = mo; Day = dy; Hour = hr; Minute = mn; Second = sc; Millisec = ms; return (*this) ; } TTimestamp& TTimestamp: :Assign( STRING ts, FLAG isnull ) { unsigned num; if (isnull) { fSetNullO; return *this; } setNotNull() ; SUBSTITUTE SHEET ASSERT( fIsValidTSString( ts ) ); /* Convert year */ Convert second */ num = (ts[17] - , 0 10; num += (ts[18] - '0 Second = UCHAR( num Convert millisec */ num = (ts[20] - '0 * 100; num += (ts[21] - '0 * 10; num += (ts[22] - Millisec - USHORT( num ) return *this; } /ifdef OS2 TTimestamp& TTimestamp: :Assign( const DATETIME &Date ) { setNotNull(); Year = Date.year; Month = Date.month; Day = Date.day; Hour = Date.hours; Minute = Date.minutes; Second = Date.seconds; Millisec = Date.hundredths 10; return (*this) ; } /endif // 0S2 STRING TTimestamp::ToSTRING( char *ts ) const SUBSTITUTE SHEET { unsigned num; /* Convert year */ num = Year; ts[0] = (num%10000) / 1000 + '0'; ts[l] = (num%1000) / 100 + '0'; ts[2] = (num%100) / 10 + '0'; ts[3] - (num % 10) + '0'; ts[4] = '-'; /* Convert month */ num = Month; ts[5] = (num%100) / 10 + '0'; ts[6] = (num % 10) + 'O^- ts[7] = '-'; /* Convert day */ num = Day; ts[8] = (num%100) / 10 + '0'; ts[9] = (num % 10) + '0'; ts[10] = '-'; /* Convert hour */ num = Hour; ts[ll] = (num%100) / 10 + '0'; ts[12] = (num % 10) + '0'; ts[13] = ' . ' ; /* Convert minute */ num = Minute; ts[14] = (num%100) / 10 + '0'; ts[15] = (num % 10) + O'; ts[16] = '.'; /* Convert second */ num = Second; ts[17] = (num%100) / 10 + '0'; ts[18] = (num % 10) + '0'; ts[19] = ' . ' ; /* Convert millisec */ num = Millisec; ts[20] = (num%1000) / 100 + '0'; ts[21] = (num%100) / 10 + '0'; ts[22] = (num % 10) + '0'; ts[23] = '0'; ts[24] = '0'; ts[25] = '0'; ts[26] = '\x0'; return ts; } FLAG TTimestamp: :operator > ( const TTimestamp &ts ) const { useAsValue() ; if (Year > ts.Year) return TRUE; SUBSTITUTE SHEET else if (Year == ts.Year) { if (Month > ts.Month) return TRUE; else if (Month == ts.Month) { if (Day > ts.Day) return TRUE; else if (Day == ts.Day) { if (Hour > ts.Hour) return TRUE; else if (Hour •*= ts.Hour) { if (Minute > ts.Minute) return TRUE; else if (Minute == ts.Minute) { if (Second > ts.Second) return TRUE; else if (Second =•= ts.Second) { if (Millisec > ts.Millisec) return TRUE; else return FALSE; } } } return FALSE; } FLAG TTimestamp: :operator >•= ( const TTimestamp &ts ) const { return (*this > ts J | *this == ts) ; } FLAG TTimestamp: :operator == ( const TTimestamp fits ) const { useAsValue() ; if (Year == ts.Year && Month == ts.Month && Day == ts.Day && Hour == ts.Hour && Minute == ts.Minute && Second == ts.Second && Millisec == ts.Millisec) { return TRUE; } else { return FALSE; } } // Date and time add function. TTimestampfc TTimestamp: :AddToDate( UINT yr, UINT mon, UINT day, UINT hr, UINT min, UINT sec, UINT ms ) { if (!fIsNull()) { SUBSTITUTE SHEET ms += Millisec; sec += Second; min += Minute; hr += Hour; day += Day; mon += Month; yr += Year; } // Adjust and carry ms. while (ms > usMaxMillisec() ) { ms -= usMaxMillisec() + 1; sec++; } // Adjust and carry sec. while (sec > usMaxSecond() ) { sec -= usMaxSecond() + 1; min++; } // Adjust and carry min. while (min > usMaxMinute() ) { min -= usMaxMinute() + 1; hr++; } // Adjust and carry hr. while (hr > usMaxHour()) { hr -= usMaxHour() + 1; day++; } // Adjust and carry mon (day adjust is dependent on mon and yr) . while (mon > usMaxMonth()) { mon -= usMaxMonth() ; yr++; } // Now adjust and carry day now that yr and mon is known, while (day > usMaxDay( yr, mon )) { day -= usMaxDay( yr, mon ) ; mon++; if (mon > usMaxMonth()) { mon --*= usMaxMonth() ; yr++; } } // Copy new values to members. Assign( yr, mon, day, hr, min, sec, ms ); CHECK( fValidate() ); return *this; } // static member. USHORT TTimestamp: :usMaxDay( USHORT year, USHORT month ) SUBSTITUTE SHEET { switch (month) { case 1: // Jan. return 31; case 2: // Feb. return fIsLeapYear( year ) ? 29 : 28; //******************************************************* ********************** // // TStream stream operators. // TStream& operator « ( TStream &buf, const TFlag &flag ) { if (iflag) return buf « FLAG( TRUE ); else return buf « FLAG( FALSE ) « flag.value; } TStreamfc operator » ( TStream &buf, TFlag &flag ) SUBSTITUTE SHEET buf » *(TNull*)&flag; if (flag.fIsNull() == FALSE) buf » flag.value; return buf; } TStreamfit operator « ( TStream &buf, const TTimestamp fits ) { if (!ts) return buf « FLAG( TRUE ); else { return buf « FLAG( FALSE ) « ts.Year « ts. onth << ts.Day « ts.Hour « ts.Minute « ts.Second « ts.Millisec; } } TStreamfit operator » ( TStream &buf, TTimestamp &ts ) { buf » *(TNull*)&ts; if (Its) { return buf; } else { return buf » ts.Year » ts.Month » ts.Day » ts.Hour » ts.Minute » ts.Second >> ts.Millisec; } } //******************************************************* ********************** // // iostream friend function members. // ostreamfi operator « ( ostream &os, const TFlag &flag ) { if (!flag) return os « NULL_TOK; else return os « (STRING) flag; } /******************** istreamS operator « ( istream &is, TFlag &flag ) { SUBSTITUTE SHEET char ch, buffer[12]; is » ws; // Extract leading whitespace. for (int i = 0; i < sizeof( buffer ); i++) { is » buffer[i] ; if (!isalpha( bufferfi] )) break; } if (i == sizeof( buffer ) ASSERT( FALSE ); buffer[i] = '\x0'; if (strcmp( buffer, NULLJTOK) == 0) { fSetNull(); } else if (strcmp( buffer, TRUE_TOK) == 0) { Assign( TRUE ) ; } else if (strcmp( buffer, FALSE TOK) == 0) { Assign( FALSE ) ; } else ASSERT( FALSE ); return is; } ******************** ostreamS operator « ( ostream &os, const TTimestamp &ts ) { char tsstring[TTimestamp: :TSStringLen] ; if (its) return os « "NULL"; else return os « ts.ToSTRING( tsstring ); } /define INCL_NOPMAPI // no PM in this program /define INCL_DOS ///define INCL_BSE ///define INCL_DOSSEMAPHORES /include <os2.h> /include <usertype.h> /include <TModem.HPP> TModem: :TModem( TPort &_port ) : port( _port ) {} TModem: :RC TModem::rcSendCommand( STRING, ULONG timeout ) { NOTIMPLEMENTED; } SUBSTITUTE SHEET STRING TModem: :strSendCommand( STRING str, ULONG timeout ) { port.fWritePort( str ); port.fPutChar( '\r' ); STRING result = strGetString( timeout ) ; if (strcmp( str, result ) == 0) { return strGetString( timeout ) ; } else { return result; } } STRING TModem: :strGetString( ULONG timeout ) { UINT i - 0; last_result[0] = '\x0'; // Eat Leading CR/NL. while (Iport.fGetChar( last_result[i] ) I I I I laεt_result[i] == '\r' I , I, last_result[i] == '\n') {} i++; // (already got 1 char ok) // Grab text until a CR/NL. while (port.fGetChar( last_result[i] ) && last_result[i] != '\n' && last_result[i] != '\r' && i <= sizeof( last_result )) { i++; } last_result[i] = '\x0'; // Null terminate buffer. return last_result; } /include <TObject.HPP> //******************************************************* ********************** // // TObject members. // TObject: :-TObject() {} //******************************************************* ********************** // // TNull members. // TNull: :TNull( FLAG is_null ) : isnull( is_null ) SUBSTITUTE SHEET { > FLAG TNull: :fSetNullO { isnull = TRUE; return TRUE; } /define INCL_NOPMAPI // no PM in this program /define INCL DOS /define INCL_BSE /define INCL_DOSSEMAPHORES /define INCL_DOSNMPIPES /include <os2.h> /include <usertype.h> /include "TPacket.HPP" TPacket: :TPacket( TPortfc p ) : Port( p ) , text_length( 0 ) , state( TRANS_NULL ) {} TPacket: :TRANS_STATE TPacket: :rGetPacket() { enq_count = 0; nak_count = 0; text_length = 0; if (state != TRANS_NULL) return TRANS_NULL; // Enquiry Loop. while (fSendENQO) { if ((state = rReceivePacket() ) == TRANS_NAK) { while (fSendNAKO) if ((state = rReceivePacket() ) == TRANS_ACK) { fSendACK() ; return state; } } else if (state == TRANS_ACK) { fSendACK() ; return state; } } fSendEOT(); SUBSTITUTE SHEET return state; TPacket::TRANS_STATE TPacket: :rReceivePacket() { char ch; int i=0,j; // Get STX. if (!Port.fGetChar( ch )) return TRANS_ETO; // packet_text[i++] = ch; if (ch != STX) return TRANS_NAK; // Get Length. if (!Port.fGetChar( ch )) return TRANS_NAK; // packet_text[i++] = ch; text_length = (USHORT)ch; if (!Port.fGetChar( ch ) ) return TRANS_NAK; // packet_text[i++] = ch; text_length = (USHORT) (ch « 8) + text_length; if (text_length > MAX_TEXT_LEN) return TRANS_NAK; // Get Text. for (j=0 ; j < text_length; j++ ) { if ( Port.fGetChar( ch )) packet_text[ j ] = ch; else return ( TRANS_NAK ) ; } // Get ETX. if ( Port.fGetChar( ch )) { if ( ch == ETX ) // packet_text[ i++ ] = ch; else return ( TRANS_NAK ) ; } else { SUBSTITUTE SHEET return ( TRANS_NAK ) ; } // Get LRC. if (!Port.fGetChar( ch )) return TRANS_NAK; // packet text[i++]=ch; return TRANS_ACK; } UINT TPacket::cbCopyText( PVOID ptr, UINT len ) { len = len < text_length ? len : text_length; memcpy( ptr, packet_text, len ) ; return len; } FLAG TPacket: :fSendENQ() { char enq = ENQ; enq_count++; if (enq_count > MAX_ENQ) return FALSE; Port.FlushlnputBuffer() ; return Port.fWritePort( &enq, 1 ); } FLAG TPacket: :fSendACK() { char ack = ACK; Port.FlushlnputBuffer() ; return Port.fWritePort( tack, 1 ); } FLAG TPacket: :fSendNAK() { char nak = NAK; nak_count++ ; if (nak_count > MAX_NAK) return FALSE; Port.FlushlnputBuffer() ; return Port.fWritePort( &nak, 1 ); } FLAG TPacket: :fSendEOT() { char eot -= EOT; return Port.fWritePort( &eot, l ); } /define INCL_NOPMAPI // no PM in this program /define INCL DOS SUBSTITUTE SHEET /define INCL_BSE /define INCL_DOSSEMAPHORES /define INCL_DOSNMPIPES /define INCL_DOSDEVIOCTL /include <os2.h> /define _THREADS // This implemetation is multi-threaded. /include <process.h> /include <string.h> /include <stdlib.h> /include "TPort.HPP" TPort: :TPort() : manage_thread( -1 ), log_flag( FALSE ) {} TPort: :-TPort() { while (manage_thread != -1) { KillManageThread() ; DosSleep( 1000 ); // Wait 1 second. } } FLAG TPort: :fOpenPort( const ComSettings ϋsettings ) { LINECONTROL lctl; DCBINFO deb; ULONG ulAction; ULONG ulPio, ulDio; ULONG cbTrans; // Open the port. re = DosOpen( settings.port_name, ϋhPort, &ulAction, 0, 0, OPEN_ACTION_OPEN_IF_EXISTS, OPEN_FLAGS_WRITE_THROUGH } OPEN_ACCESS_READWRITE j OPEN_SHARE_DENYREADWRITE, NULL ) ; if (re) return FALSE; // Set the line speed. ulPio = sizeof( settings.bps ); re = DosDevIOCtl( hPort, IOCTL_ASYNC, ASYNC_SETBAUDRATE, (PVOID) &settings.bps, ulPio, &ulPio, NULL, 0, NULL ); if (re) { DosClose( hPort ) ; return FALSE; } // Set the line characteristics. Ictl.bDataBits = settings.data_bits; SUBSTITUTE SHEET lctl.bParity = (BYTE)settings.parity; lctl.bStopBits = (BYTE)settings.stop_bits; ulPio •= sizeof lctl; re = DosDevIOCtl( hPort, IOCTL_ASYNC, ASYNC_SETLINECTRL, &lctl, ulPio, &ulPio, NULL, 0, NULL ); if (re) { DosClose( hPort ) ; return FALSE; } // Set the flow control. ulDio = sizeof deb; re - DosDevIOCtl( hPort, IOCTL_ASYNC, ASYNC_GETDCBINFO, NULL, 0, NULL, &dcb, ulDio, &ulDio ); if (re) { DosClose( hPort ) ; return FALSE; } /******************************************************** ********************** dcb.usReadTimeout = 100; dcb.fbCtlHndShake = MODE_CTS_HANDSHAKE; // flagsl = 00001000 dcb.fbFlowReplace &= 0x30; // flags2 = 00??0000 dcb.fbFlowReplace j= MODE_RTS_HANDSHAKE; // flags2 = 10??0000 dcb.fbTimeout &= 0xF8; // flags3 = ?????000 dcb.fbTimeout \ = MODE_WAIT_READ TIMEOUT; // flags3 = ?????100 "* ********************************************************* *********************/ dcb.usReadTimeout - 300; dcb.fbCtlHndShake * MODE_CTS_HANDSHAKE; dcb.fbFlowReplace - MODE_RTS_HANDSHAKE; dcb.fbTimeout « MODE_NO_WRITE_TIMEOUT | MODE WAIT READ TIMEOUT; re = DosDevIOCtl( hPort, IOCTL_ASYNC, ASYNC_SETDCBINFO, &dcb, ulPio, SulPio, NULL, 0, NULL ); if (re) { DosClose( hPort ) ; return FALSE; } fRaiseDTR() ; return TRUE; } FLAG TPort: :fClosePort() SUBSTITUTE SHEET re = DosClose ( hPort ) ; if (re) return FALSE; else return TRUE; } void TPort: :FlushlnputBuffer() { BYTE cmd; // Scratch, Needed by API. ULONG len; // Scratch, Needed by API. re = DosDevIOCtl( hPort, IOCTL_GENERAL, DEV_FLUSHINPUT, &cmd, sizeof( cmd ), &len, &cmd, sizeof ( cmd ) , fiilen ) ; DosSleep(lO) ; // Timing Kludge - Give the Device Driver // time to flush buffer before resetting // semaphore stuff. buffer.Flush() ; } void TPort::FlushOutputBuffer() { BYTE cmd; // Scratch, Needed by API. ULONG len; // Scratch, Needed by API. re = DosDevIOCtl( hPort, IOCTL_GENERAL, DEV_FLUSHOUTPUT, &cmd, sizeof( cmd ), fiilen, &cmd, sizeof( cmd ), fiilen ); } FLAG TPort: :fReadPort( PVOID buf, UINT &len ) { for (int i = 0; i < len; i++) { if (buffer.flsEmpty(j) { len = i; return TRUE; } else buffer.fGetChar( ((char*)buf) [i] ); } return TRUE; } FLAG TPort: :fWritePort( PVOID buf, UINT len ) { ULONG cbWritten; re = DosWrite( hPort, buf, len, ScbWritten ); if (re) return FALSE; SUBSTITUTE SHEET else return TRUE; } FLAG TPort::fDropDT () { ULONG ulPio, ulDio; MODEMSTATUS ms; ULONG com_err; ms . fbModemOn •*= 0 ; ms.fbModemOff = DTR_OFF; ulPio = sizeof s; ulDio « sizeof com_err; re = DosDevIOCtl( hPort, IOCTL_ASYNC, ASYNC_SETMODEMCTRL, &ms, ulPio, &ulPio, &com_err, ulDio, &ulDio ) ; if (re) return FALSE; else return TRUE; } FLAG TPort: :fRaiseDTR() { ULONG ulPio, ulDio; MODEMSTATUS ms; ULONG com_err; ms.fbModemOn = DTR_ON; ms.fbModemOff = OxFF; ulPio = sizeof ms; ulDio = sizeof com_err; re = DosDevIOCtl( hPort, IOCTL_ASYNC, AS NC_SETMODEMCTRL, &ms, ulPio, fiiulPio, &com_err, ulDio, &ulDio ) ; if (re) return FALSE; else return TRUE; } void _Optlink ManageThread( PVOID ); // Used internally by fStartManageThread() . void _Optlink ManageThread( PVOID ptr ) ((TPort*)ptr)->ManagePort() ; FLAG TPort: :fStartManageThread() fManThread = TRUE; manage thread = _beginthread( ManageThread, 8192, PVOID)this ) ; if (manage_thread == -1) return FALSE; else return TRUE; void TPort::ManagePort() SUBSTITUTE SHEET char read_buf[32]; ULONG cbRead; while (TRUE) { re = DosRead( hPort, read_buf, sizeof read_buf, &cbRead ) ; if (re) { // handle error here... } else if (IfManThread) break; for (int i = 0; i < cbRead; i++) { if (log_flag) log.fPostChar( read_buf[i] ); buffer.fPutChar( read_buf[i] ) ; } buffer.SignalRelease() ; } // Signal threads exit. manage_thread = -1; } FLAG TPort: :fStartCommandThread( TTHREAD CommandThread, PVOID data ) { fCmdThread = TRUE; command_thread = _beginthread( CommandThread, 8192, data ) ; if (command_thread == -1) return FALSE; else return TRUE; } /include <TStream.HPP> /include <debug.h> /include <string.h> //******************************************************* ********************** // // TStream members. // TStream: :TStream( UINT buf_size ) : buf_len( buf_size ), buffer( new BYTE[buf_size] ), iptr( buffer ) , xptr( buffer ) { /ifdef DEBUG memset( buffer, UNDEF_DATA, buf_len ); /endif } TStream: :-TStream() { SUBSTITUTE SHEET delete buffer; void TStream: : Reset ( ) iptr = xptr = buffer; TStreamS TStream: : operator « ( const FLAG flag ) *(FLAG*)iptr ■ flag; return inclnserter( sizeof( flag ) ); TStreamfit TStream: :operator « ( const USHORT num ) *(USHORT*)iptr = num; return inclnserter( sizeof( num ) ); TStreamfit TStream: :operator « ( const ULONG num ) *(ULONG*)iptr = num; return inclnserter( sizeof( num ) ); TStreamS TStream: :operator « ( const char *str ) strcpy( iptr, str ) ; return inclnserter( strlen( str ) + 1 ); TStreamϋ TStream: :Put( const PVOID data, UINT size ) memcpy( iptr, data, size ); return inclnserter( size ); TStream& TStream: :operator » ( FLAG fiiflag ) flag = *(FLAG*)xptr; return incExtractor( sizeof( flag ) ); TStreamfii TStream: :operator » ( USHORT &num ) num ■ *(USHORT*)xptr; return incExtractor( sizeof( num ) ); TStreamfii TStream::operator » ( ULONG &num ) num = *(ULONG*)xptr; return incExtractor( sizeof( num ) ); SUBSTITUTE SHEET TStreamfit TStream: :operator » ( char *str ) { strcpy( str, xptr ) ; return incExtractor( strlen( str ) + 1 ); } TStreamfii TStream::Get( PVOID data, UINT size ) { memcpy( data, xptr, size ) ; return incExtractor( size ); } TStreamfii TStream: :incExtractor( UINT n ) { xptr += n; ASSERT( xptr <= iptr ) ; return *this; } TStreamfit TStream: :inclnserter( UINT n ) { iptr += n; ASSERT( iptr <= buffer + buf_len ) ; return *this; } ******************************************************** ***************** ;* ;* Copyright (C) 1995 Absolute Software Corporation •* ;******************************************************** **************** NAME DBServer WINDOWCOMPAT IMPORTS CTIMS.fGenerateSerCTID CTIMS.fXlatSerCTID CTIMS.fXlatCliCTID CTIMS.fGenerateCTCODE CTIMS.fConvertStrToCTCODE CTIMS.fConvertCTCODEToStr .\TObject.obj: \ f:\Server\TObject.CPP \ DBServer.MAK . \objects.obj: \ f:\Server\objects.cpp \ DBServer.MAK . \MessagePipe.obj : \ f:\Server\MessagePipe.CPP \ DBServer.MAK SUBSTITUTE SHEET . \CTMessage.obj : \ f:\Server\CTMessage.CPP \ DBServer.MAK .\ctims.obj: \ f:\Server\ctims.cpp \ DBServer.MAK . \DBServer.obj: \ f:\Server\DBServer.C \ {f:\Server;F:\Server\INCLUDE;E:\SQLLIB;E: \TOOLKT21\CPLUS\ OS2H;E:\Tools\IBMCPP\INCLUDE;}DBServer.H \ DBServer.MAK .\TSTREAM.obj: \ f:\Server\TSTREAM.CPP \ DBServer.MAK .\TPacket.obj: \ f:\Server\TPacket.CPP \ {f:\Server;M:\SRC\Include;M:\CT\Include;$(INCLUDE) ;>TPack et.HPP \ Server.MAK . \TModem.obj: \ f:\Server\TModem.CPP \ Server.MAK . \CT_Log.obj: \ f: \Server\CT_Log.CPP \ {f:\Server;M:\SRC\Include;M:\CT\Include;$(INCLUDE) ;}CT Lo g.HPP \ Server.MAK . \CT_Buffer.obj: \ f:\Server\CT_Buffer.CPP \ {f:\Server;M:\SRC\Include;M:\CT\Include;$(INCLUDE) ;}CT_Bu ffer.HPP \ {f:\Server;M:\SRC\Include;M:\CT\Include;$(INCLUDE) ;}serve r.h \ Server.MAK .\Server.obj: \ f:\Server\Server.C \ {f: \Server;M: \SRC\Include;M: \CT\Include;$(INCLUDE) ;}CT_Tr ans.H \ " ~ {f:\Server;M:\SRC\Include;M:\CT\Include;$(INCLUDE) ;}TPack et.HPP \ SUBSTITUTE SHEET Server. AK •\CT_Trans.obj: \ f:\Server\CT_Trans.C \ {f:\Server;M:\SRC\Include;M:\CT\Include;$(INCLUDE) ;}CT_Tr ans.H \ {f:\Server;M:\SRC\Include;M:\CT\Include;$(INCLUDE) ;}TPack et.HPP \ Server.MAK .\TPort.obj: \ f:\Server\TPort.CPP \ {f:\Server;M:\SRC\Include;M:\CT\Include;$(INCLUDE) ;}TPort .HPP \ {f:\Server;M:\SRC\Include;M:\CT\Include;$(INCLUDE) ;}CT_Bu ffer.HPP \ {f:\Server;M:\SRC\Include;M:\CT\Include;$(INCLUDE) ;}CT_Lo g.HPP \ {f:\Server;M:\SRC\Include;M:\CT\Include;$(INCLUDE) ;}serve r.h \ Server.MAK /ifndef CT_TRANS_H /define CT_TRANS_H ///include <DB_Objects.HPP> /include <MessagePipe.HPP> /include "TPacket.HPP" void SntlConnect( TPort &Port, MessagePipe fiiPipe, TConnectlnfo *cnct_info ) ; void SntlDisconnect( TPort &Port, TConnectlnfo fiiConnectlnfo ) ; void SendDatePacket( TPort fiiPort, const SNTL_DATE fitdate )7 void AddDays( SNTL_DATE *next_call, int days ); FLAG fGetDateTime( PDATETIME ) ; /endif /ifndef MESSAGE_H /define MESSAGE_H /******************************************************** ********************** Message.H SUBSTITUTE SHEET Defines all valid messages used by the Server and ServerShell. ********************************************************* *********************/ // Define standard types, /include <os2def.h> /include <time.h> // Definition for the Sentinel date packet, struct CT_DATE { BYTE year; BYTE month; BYTE day; BYTE hour; BYTE minute; }; // Definition for the Sentinel serial number packet, struct CT_SN { USHORT sn[3] ; USHORT cksum; CT_DATE date; }; /define CND_NUM MAXLEN 20 /define CND NAME MAXLEN 20 Struct CALLERID_INFO { BYTE month; BYTE day; BYTE hour; BYTE minute; CHAR number[CND_NUM_MAXLEN]; CHAR name[CND_NAME_MAXLEN]; }; enum TRANS_STATE { TRANS_OK - 0X00, TRANS_BAD_CND = 0x01, TRANS_BAD_SN = 0x02, TRANS_BAD_DATE = 0x04 In¬ struct CT_Transaction { DATETIME start_time ; CALLERID_INFO end ; CT_SN sn ; TRANS_STATE state; DATETIME end_time; }; enum CT SN QUERY { SUBSTITUTE SHEET CT_SN_OK = 0, CT_SN_REDFLAG = 1, CT_SN_UNKNOWN - 2 }; /define CT_BUFFER_LEN 256 // Allowable length of modem communications for a cycle, /define CT GUARD CHAR ' ! ' /* Definitions for stripped CompuTrace messages. ******************/ /define MAX_PHONE_NUM_LEN 16 // Max length of a phone number string. /define CT_SERIAL_NUM_LEN sizeof( CT_SN ) // Length of serial number packet sent by the modem. /define MAX_ERROR_STR_LEN 32 // Max length of an error string. enum CTMSGJTYPE { CTMSG_UNDEF = 0, CTMSG_CONNECT, CTMSG_SERIAL_NUM, CTMSG_ERROR_LOG, CTMSG_DISCONNECT }; struct CT_ConnectMsg { time_t connect_time; char phone_num[MAX_PHONE_NUM_LEN] ; }; struct CT_SerialNumMsg { CT_SN serial_num; }; struct CT_ErrorLogMsg { char error_str[MAX_ERROR_STR_LEN] ; }; struct CT_DisconnectMsg { time_t disconnect_time; char log[CT_BUFFER_LEN] ; }; struct CTMessage { CTMSGJTYPE type; union "" { CT_ConnectMsg Connect; CT_SerialNumMsg SerialNum; CT_ErrorLogMsg ErrorLog; CT DisconnectMsg Disconnect; SUBSTITUTE SHEET } Msg; } ; /define MAX_CTMSG_SIZE sizeof( CTMessage ) // Max size of a stripped (CompuTrace) message. /* Definitions for pipe messages. **********************************/ // Define all valid events. The following prefixes are used: // C _ For general _. messages // CT_SER_ For server originated messages not related to a transaction. // CT_CLI_ For client originated messages not related to a transaction. // CT SER MSG For server originated messages related to a transaction. // CT_CLI_MSG_ For client originated messages related to a transaction. // For more detailed information please see the proper message structure. enum EVENT_TYPE { CT_SER ^ MSG_AWK, // Server awknowledges last received message. CT_SER_MSG_ERROR, // Server has had a non- fatal error. CT_SER_MSG_FATAL, // Server has had a fatal error and will unconditionally terminate. CT_SER_MSG_MESSAGE, // Server has a message to be processed by the client CT_SER_STOP, // Server requests the client(s) stop sending messages. CT_SER_START, // Server allows the client(s) to continue sending messages. CT_SER_ERROR, // Server has had an internal non-fatal error. CT_SER_FATAL, // Server has had an internal fatal error and will terminate. CT_SER_STRING, // Server has a general string to be stored. CT_SER_QUIT, // Server has requested all clients to terminate. CT_CLI_MSG_AWK, // Client awknowledges last received message. CT_CLI_MSG_ERROR, // Client has had a non- fatal error. CT_CLI_MSG_FATAL, // Client has had a fatal error and will unconditionally terminate. CT_CLI_MSG_MESSAGE // Client has a message to be processed by the server. SUBSTITUTE SHEET }; // Define message transfer template used to transfer a message through a pipe. struct CT MessageHead { ULONG Td; // The message id number. EVENT_TYPE type; // The event type (see above) . BYTE len; // The length the message data, in¬ struct CT_MessageBuffer { CT_MessageHead header; char message[MAX_CTMSG_SIZE] ; }; /define MAX_MSG_SIZE sizeof( CT_MessageBuffer ) // Max size of a pipe message. /endif // MESSAGE_H /ifndef PACKET_H /define PACKET H // Ensure byte alignment enforced! /pragma pack( 1 ) // For C-Set-M- /pragma option -al // For BC++ /* Packet Level Defines ******************************************************/ /define STX text. /define ETX text. /define EOT transmission. /define ENQ /define ACK Acknowledgement. /define NAK acknowledgement. /define MAX_ENQ number of ENQs. /define MAX_NAK number of NAKs. /define MAX_TEXT_LEN of a packets TEXT. Struct PKT_HEADER { BYTE stx; BYTE lsb_length; SUBSTITUTE SHEET BYTE msb_length; }; struct PKT_FOOTER { BYTE etx; BYTE Ire; }; /* Packet type definitions ***************************************************/ // Text Type IDs. /define CTID_TEXT_TYPE (WORD)0x0000 // Sentinel Subscription Number Packet. /define NC_TEXT_TYPE (WORD)0x0080 // Server Next Call Packet. struct SNTL_DATE { BYTE year; BYTE month; BYTE day; BYTE hour; BYTE minute; }; struct CTID_TEXT { BYTE type; BYTE sub_type; WORD sn[3]; SNTL_DATE now_date; }; /define SN_TEXT CTID_TEXT // Old name (uses should be changed to CTID_TEXT) . struct CTID_PACKET { PKT_HEADER header; CTID_TEXT text; PKT_F00TER footer; }; /define SN_PACKET CTID_PACKET // Old name (uses should be changed to CTID_PACKET) . struct NC_TEXT { WORD type; SNTL_DATE next_call_date; }; Struct NC_PACKET { PKT_HEADER header; NC TEXT text ; PKT_FOOTER footer; }; /pragma pack() // Back to default. /pragma option -a. SUBSTITUTE SHEET /endif /ifndef SERVER_H /define SERVER_H /define DEBUG 4 /include <debug.h> /include <usertype.h> // // TConnectlnfo definition. // /define CND_NUM_MAXLEN 20 /define CND NAME MAXLEN 20 Struct CALLERID_INFO { BYTE month; BYTE day; BYTE hour; BYTE minute; CHAR number[CND_NUM_MAXLEN]; CHAR name[CND_NAME_MAXLEN] ; }; struct TConnectlnfo { DATETIME start_time, end_time; CALLERID_INFO end; }; // // End of TConnectlnfo // /endif // SERVER_H /ifndef CT_BUFFER_HPP /define CT_BUFFER_HPP /include "server.h" /define TRUE 1 /define FALSE 0 /define CT_BUFFER_MAXLEN 256 class CT Buffer { char buffer[CT_BUFFER_MAXLEN] ; UINT head, tail; HMTX hBufSem; HEV hReleaseGetSem; APIRET re; UINT IncBufPtr( UINT ptr ) const { return (++ptr >= CT BUFFER MAXLEN) ? ptr; } public: SUBSTITUTE SHEET CT_Buffer() ; -CT_Buffer() ; void Flush() ; BOOL fIsEmptyO const { return head == IncBufPtr( tail ); } BOOL fIsFullO const { return head •== tail; } void SignalReleaseO { DosPostEventSem( hReleaseGetSem ); } BOOL fPutChar( char ) ; BOOL fGetChar( char& ) ; }; /endif /ifndef CT_LOG_HPP /define CT LOG HPP /define TRUE 1 /define FALSE 0 class CT_Log { char *buffer; UINT index, buf_len; public: CT_Log( UINT = 4096 ); -CT_Log(); void Flush() { index - 0; } BOOL fIsEmptyO const { return index == 0; } BOOL fIsFullO const { return index >= buf_len; } BOOL fPostChar( char ) ; BOOL fDumpLog( const char * ) ; }; /endif /ifndef TCLIENT_HPP /define TCLIENT HPP class TClient { TConnectlnfo Connectlnfo; WORD ctid[3]; SNTL_DATE client_date; SUBSTITUTE SHEET Pipe public: /endif // CLIENT_HPP /ifndef TPACKET_HPP /define TPACKET_HPP /include <os2def.h> /include "packet.h" /include <TPort.HPP> //******************************************************* ********************** // Class TPacket - Encapsulates the reception of a packet for a port // // TPacket: :TPacket( TPortfii Port ) Initializes internal state. // Arguments: // TPortfii Port - the port to receive the packet from. // // TRANS_STATE TPacket: :rGetPacket() // Description: // Attempts to receive a packet from Port using the protocol // defined in the CompuTrace Protocol Specification (CTPSpec) . // // Returns: The result of the attempt: // TRANS_ACK - packet successfully received as defined by CTPSpec. // TRANS_NAK - reception aborted due to invalid reception, EOT sent. // TRANS_ETO - ENQ timeout, no date recieved, EOT sent. // // UINT TPacket: :cbCopyText( ptr, len ) // Arguments: // PVOID ptr - the buffer to copy data to. // UINT len - the maximum number of bytes to copy. SUBSTITUTE SHEET // // Description: // Copies text from a sucessfully received packet into buffer pointed to // by ptr. Copies up to len bytes or the size of the received packet // text (whichever is smaller) . Can only be called if rGetPacket // returned TRANS_ACK. // // Returns: number of bytes copied, or 0 if packet not successfully // received. // // TRANS_STATE rState ( ) const // Returns: the current state of the instance. //******************************************************* ********************** class TPacket { public: enum TRANS_STATE { TRANS_NULL, // No activity. TRANS_ACK, TRANS_NAK, TRANS_ETO }; // ETO = Enquiry time-out. TPacket( TPort& ); TRANS_STATE rGetPacket() ; UINT cbCopyText( PVOID ptr, UINT len ); TRANS_STATE rState() const { return state; } protected: FLAG fSendENQ() ; FLAG fSendACK() ; FLAG fSendNAK() ; FLAG fSendEOTO ; private: TPortfii Port ; int enq_count; int nak_count; USHORT text_length; BYTE packet_text[MAX_TEXT_LEN]; TRANS_STATE state; TRANS_STATE rReceivePacket() ; }; /endif SUBSTITUTE SHEET / Created by IBM WorkFrame/2 MakeMake at 17:36:34 on 08/22/95 / / This makefile should be run in the following directory: / d:\Server / / The actions included in this makefile are: / COMPILE::CLC C++ / LINK: :CLC Link .all: \ . \DBServer.EXE .SUFFIXES: •SUFFIXES: .C .CPP .CPP.obj : §echo WF: :COMPILE::CLC C++ icc.exe /Tl- /Xi /ID: \Server\INCLUDE /IE:\SQLLIB /IE: \TOOLKT21\CPLUS\OS2H /IE:\Tools\IBMCPP\INCLUDE /DDEBUG=4 /Tdp /Q /Wall /Fi /Ti /Gm /G5 /Tm /C %s .C.obj : §echo WF: :COMPILE::CLC C++ icc.exe /Tl- /Xi /ID:\Server\INCLUDE /IE:\SQLLIB /IE: \TOOLKT21\CPLUS\OS2H /IE: \Tools\IBMCPP\INCLUDE /DDEBUG=4 /Tdp /Q /Wall /Fi /Ti /Gm /G5 /Tm /C %s .\DBServer.EXE: \ . \TObject.obj \ . \TSTREAM.obj \ . \DBServer.obj \ .\ctims.obj \ . \CTMessage.obj \ . \MessagePipe.obj \ . \objects.obj \ {$(LIB) }DB_Objects.LIB \ {$(LIB)}SQL_DYN.LIB \ {$(LIB)}DBServer.DEF \ DBServer.MAK §echo WF: :LINK: :CLC Link icc.exe @« /Tl- /Xi /ID:\Server\INCLUDE /IE:\SQLLIB /IE: \TOOLKT21\CPLUS\OS2H /IE: \Tools\IBMCPP\INCLUDE /DDEBUG=4 /Tdp /Q /Wall /Fi /Ti /Gm /G5 /Tm /B" /de" /FeDBServer.EXE SUBSTITUTE SHEET DB_Objects.LIB SQL_DYN.LIB DBServer.DEF . \TObject.obj . \TSTREAM.obj . \DBServer.obj .\ctims.obj .\CTMessage.obj . \MessagePipe.obj . \objects.obj « !include DBServer.Dep / Created by IBM WorkFrame/2 MakeMake at 10:20:11 on 05/30/95 / / This makefile should be run in the following directory: / d:\Server / / The actions included in this makefile are: / COMPILE::CLC C++ / LINK: :CLC Link .all: \ . \Server.EXE .SUFFIXES: .SUFFIXES: .C .CPP .CPP.obj: §echo WF: :COMPILE::CLC C++ icc.exe /Tl- /ID: \Server\Include /IM:\CT\Include /Tdp /Q /Wall /Fi /Si /Ti /O /Gm /G5 /Tm /C %s .C.obj : §echo WF: :COMPILE::CLC C++ icc.exe /Tl- /ID: \Server\Include /IM:\CT\Include /Tdp /Q /Wall /Fi /Si /Ti /0 /Gm /G5 /Tm /C %s .\Server.EXE: \ . \TPacket.obj \ . \TPort.obj \ . \CT_Trans.obj \ •\Server.obj \ . \CT_Buffer.obj \ .\CT_Log.obj \ .\TModem.obj \ {$(LIB)}CTIMS.LIB \ {$(LIB)}MessagePipe.LIB \ Server. AK §echo WF: :LINK: :CLC Link icc.exe §« /Tl- SUBSTITUTE SHEET /ID:\Server\Include /IM:\CT\Include /Tdp /Q /Wall /Fi /Si /Ti /O /Gm /G5 /Tm /B" /de" /FeServer.EXE CTIMS.LIB MessagePipe.LIB . \TPacket.obj . \TPort.obj . \CT_Trans.obj . \Server.obj .\CT_Buffer.obj •\CT_Log.obj . \TModem.obj « !include Server.Dep /define INCL_NOPMAPI // no PM in this program. /define INCL_DOS /define INCL_BSE /include <os2.h> /include <fstream.h> /include <time.h> /include <server.h> /include <DB_Objects.HPP> /include <CTMessage.HPP> ///include <packet.h> /include "CT Trans.H" FLAG fQueryCTIDStatus( MessagePipe &Pipe, const QueryCTIDStatusMsg &Status, CTIDStatusResultMsg fiiResult ); FLAG fStoreMonitorEvent( MessagePipe fiiPipe, const StoreMonitorEventMsg &Store, StoreResultMsg &Result ) ; FLAG fSignalQuit( MessagePipe fiiPipe ) ; void AssignTS( TTimestamp fiits, const SNTL_DATE &Date ); void AssignSNTL_DATE( SNTL_DATE fiiDate, const TTimestamp δ-ts ); // Temp function. void ProcessClient( TPort &Port, TConnectlnfo ficConnectlnfo, CTID TEXT *text ); extern MessagePipe *pipej // // SntlConnect: called when a CONNECT comand has been received, this function processes SUBSTITUTE SHEET // a transaction between the server and a Sentinel client. // void SntlConnect( TPort &Port, MessagePipe fiiPipe, TConnectlnfo *cnct_info ) { WORD msg_type; DosGetDateTime( &cnct_info->start_time ); // Fill start time. TPacket packet( Port ) ; while (TRUE) { // Get a packet. if (packet.rGetPacket() != TPacket: :TRANS_ACK) { cout « "Packet Error" « endl; return; } // Determine packet type. packet.cbCopyText( &msg_type, sizeof( msg_type ) ); switch( msg_type ) { case CTID_TEXT_TYPE: // Create a new client object. // TClient Client( Port, Pipe, *cnct_info ); // Get CTID Text and add to Client object. CTID_TEXT Text; packet.cbCopyText( &Text, sizeof( Text ) ); // Client.SetCTID( Text ); // ProcessClient. // ProcessClient( Client ); ProcessClient( Port, *cnct_info, SText ); return; default: return; } } } void ProcessClient( TPort fiiPort, TConnectlnfo fiiConnectlnfo, CTID_TEXT *text ) SNTL_DATE next_call; // ENTER APPLICATION LAYER... // Query the Client state. QueryCTIDStatusMsg StatusMsg; StatusMsg.CTID = (ULONG)text->sn[0] + ((ULONG)text- >sn[l] « 16) ; CTIDStatusResultMsg Result; cout « "QueryCTIDStatus for CTID " « StatusMsg.CTID « "... "; SUBSTITUTE SHEET if (!fQueryCTIDStatus( *pipe, StatusMsg, Result )) { cout « "Error in QueryCTIDStatus!" « endl; } else { cout « "CTIDStatusResult Received..." « endl; cout « " Status = " « (STRING)Result.Status « endl; cout « " PeriodDays = " « Result.PeriodDays « endl; cout « " PeriodMinutes = " « Result.PeriodMinutes « endl; cout « " StolenFlag = " « (STRING)Result.StolenFlag « endl; cout « " SpecialProcess = " « Result.SpecialProcess « endl; cout « " Orgnum = " « Result.Orgnum_n « endl; } // Send NextCall Message back to the Client. CTTimestamp next_ts; AssignTS( next_ts, text->now_date ); if (next_ts.us ear() < 1900) { // If date is not valid substitute the local date instead. next_ts = Connectlnfo.start_time; } next_ts.AddToDate( 0, 0, Result.PeriodDays, 0, Result.PeriodMinutes ); AssignSNTL_DATE( next_call, next_ts ); SendDatePacket( Port, next_call ); SntlDisconnect( Port, Connectlnfo ); // Store the Monitor Event. StoreMonitorEventMsg Event; Event.StoreAsStolen = Result.StolenFlag; Event.StoreAsExpire = FALSE; Event.LicenseStatus = Result.Status; AssignTS( Event.ClientTS, text->now_date ); Event.ServerTS = Connectlnfo.start_time; Event.NextCallTS_n = Event.ServerTS; Event.NextCallTS_n.AddToDate( 0, 0, Result.PeriodDays, 0, Result.PeriodMinutes ); Event.NextCallClientTS_n = next_ts; Event.CTID = StatusMsg.CTID; Event.TelcoTS_n.Assign( Event.ServerTS.usYear() , Connectlnfo.end.month, Connectlnfo.end.day, Connectlnfo.end.hour, Connectlnfo.end.minute ); Event.DurationSec_n «= 0; Event.CallerID_n = (const char(*) [CALLERID_SIZE])Connectlnfo.end.number; Event. ineNum •*= 1; Event.LogFlag = FALSE; SUBSTITUTE SHEET Event.EnvironmentID = "DBS-9508"; Event.ErrorCnt = 0; StoreResultMsg ResultMsg; cout « endl « "Storing the MonitorEvent... "; if (!fStoreMonitorEvent( *pipe, Event, ResultMsg )) { cout « "Error in StoreMonitorEvent!" « endl; } else { cout << "StoreResult = " << (ResultMsg.Result ? "TRUE" : "FALSE") « endl; } } void SendDatePacket( TPortfit Port, const SNTL_DATE& date ) { NC_PACKET packet; packet.header.stx = STX; packet.header.lsb_length = sizeof( NC_TEXT ); packet.header.msb_length = 0; packet.text.type = NC_TEXT_TYPE; packet.text.next_call_date = date; packet.footer.etx = ETX; packet.footer.Ire = 0; Port.fWritePort( (PVOID) ipacket, sizeof( packet ) ); } FLAG fQueryCTIDStatus( MessagePipe fiiPipe, const QueryCTIDStatusMsg fitStatus, CTIDStatusResultMsg fiiResult ) { TStream in_strm, out_strm; out_strm « Status; if (!Pipe.fTransact( out_strm, in_strm )) return FALSE; in_strm » Result; if (Result.eType() — CTID_STATUS_RESULT) return TRUE; else return FALSE; } FLAG fStoreMonitorEvent( MessagePipe fiiPipe, const StoreMonitorEventMsg fiiStore, StoreResultMsg fiiResult ) { TStream in_strm, out_strm; out_strm « Store; SUBSTITUTE SHEET if (!Pipe.fTransact( out_strm, in_strm )) return FALSE; in_strm » Result; if (Result.eType() == STORE_RESULT) return TRUE; else return FALSE; > FLAG fSignalQuit( MessagePipe fiiPipe ) { TStream stream; CliQuitMsg QuitMsg; stream « QuitMsg; return Pipe . fSendMessage ( stream ) ; } void SntlDisconnect ( TPort fiiPort , TConnectlnfo δtConnectlnfo ) { / / Drop DTR. DosSleep( 500 ); // Broc - 13 Feb 95 // Add delay to let modem clear xmt buffer // to fix intermittent modem fault. Port.fDropDTR() ; cout « "Disconnecting..." « flush; DosGetDateTime ( StConnectlnf o . end_time ) ; / / Fill end time. DosSleep ( 200 ) ; // Raise DTR. Port.fRaiseDTR() } // *** helper functions. UCHAR BCD2ToUChar( BYTE bed ) { // Convert a two digit bed number to decinal. return (bed » 4) * 10 + (bed & OxOF); } BYTE UCharToBCD2( UCHAR dec ) { // Convert a 8 bit decimal number to bed. return (dec % 10) + (((dec / 10) % 10) « 4) ; } USHORT BCD4ToUShort( WORD bed ) { SUBSTITUTE SHEET // Convert a four digit bed number to decimal. return (bed » 12) * 1000 + ((bed & OxOFOO) » 8) * 100 + ((bed & OxOOFO) » 4) * 10 + (bed & OxOOOF) ; } WORD UShortToBCD4( USHORT dec ) { // Convert a 16 bit decimal number to a 4 digit decimal, return (dec % 10) + (((dec / 10) % 10) « 4) + (((dec / 100) % 10) « 8) + (((dec / 1000) % 10) « 12); } void AssignTSf TTimestamp &ts, const SNTL_DATE fiiDate ) { ts.Assign( BCD2ToUChar( Date.year ), BCD2ToUChar( Date.month ), BCD2ToUChar( Date.day ) , BCD2ToUChar( Date.hour ), BCD2ToUChar( Date. inute ) ); } void AssignSNTL_DATE( SNTL_DATE fiiDate, const TTimestamp &ts ) { Date.year = UCharToBCD2( ts.usYear() % 100 ); Date.month = UCharToBCD2( ts.usMonth() ); Date.day = UCharToBCD2( ts.usDay() ) ; Date.hour = UCharToBCD2( ts.usHour() ) ; Date.minute = UCharToBCD2( ts.usMinute() ); } /* inline BYTE HiNibble( BYTE b ) { return (BYTE) ((b & OxFO) » 4); } inline BYTE LoNibble( BYTE b ) { return (BYTE) (b & OxOF); } void AddDays ( SNTL_DATE *next_call , int days ) { static BYTE days_per_month[ 18] = { 0x31 , SUBSTITUTE SHEET 0x31, // 0x10 - Oct 0x30, 0x31 // 0x12 - Dec }; BYTE old_day = next call->day; // Save for BCD adjust. // Add the days to the current date. next_call->day += days; // Check if we passed the end of the current month. if (next_call->day > days_per_month[next_call->month] ) { // Add one to month. if (++next_call->month > 12) { next_call->month = 1; ++next_call->year; } next_call->day -= days_per_month[next_call->month] * 1; // Roll over to proper day. } // Adjust the day back to BCD. if (LoNibble( next call->day ) > 0x9 | | HiNibble( next_call->day ) != HΪNibble( old_day )) next_call->day += 6; // Adjust the month to BCD. if (LoNibble( next_call->month ) > 0x9) next_call- >month += 6; // Adjust the year back to BCD. if (LoNibble( next_call->year ) > 0x9) next_call->year += 6; if (HiNibble( next_call->year ) > 0x9) next_call->year = LoNibble( next_call->year ); } */ /define INCL_DOSNMPIPES /include <os2.h> /include <iostream.h> /include <fstream.h> /include <string.h> /include <server.h> /include "DBServer.H" /include <usertype.h> /include <DB_Objects.HPP> /include <CTID.H> /include <CTIMS.HPP> /include <CTMessage.HPP> /include <MessagePipe.HPP> SUBSTITUTE SHEET FLAG fProcessClientEvent( MessagePipe fiiPipe, TStream fiiMsgStream ) ; FLAG fProcessQueryCTIDStatus( MessagePipe &Pipe, QueryCTIDStatusMsg &Status ) ; FLAG fProcessStoreMonitorEvent( MessagePipe &Pipe, StoreMonitorEventMsg ϋMEvent ) ; FLAG fUpdateLieenseStatus( StoreMonitorEventMsgfii ) ; // Helper functions. FLAG _fCopyTStoDBVars( char *tsstring, short *indicator, CTTimestamp &ts, STRING varname = "Timestamp" ); DataBase DB; int main( int argc, char *argv[] ) { if (argc != 3) { cout « "Usage: dbserver <database_name> <pipe_name>" << endl; } DB.SetName( argv[1] ) ; SvrMsgPipeFactory Factory( argv[2], 512, 10 ); MessagePipe *pipe; if (!DB.fConnect()) { cout « "Unable to connect to " « argv[l] « " SQLCODE = " « (long)DB.ulSQLCode() « endl; return 1; } if (!Factory.fCreatePipe( pipe )) { cout « "Unable to create pipe DosErrorCode •= " « Factory.rcDosErrorCode() « endl; return 2; } cout « "Waiting for pipe to connect to client..." « endl; if (!pipe->fOpenPipe() ) { cout « "Error connecting to the client DosErrorCode = " << pipe->rcDosErrorCode() « endl; return 2; } cout « "Pipe connected to client." « endl; TStream MsgStream; while (fProcessClientEvent( *pipe, MsgStream )) MsgStream.Reset() ; pipe->fClosePipe() ; return 0; } SUBSTITUTE SHEET FLAG fProcessClientEvent( MessagePipe fiiPipe, TStream &MsgStream ) { if (!Pipe.fGetMessage( MsgStream )) { cout « "Error reading message from pipe DosErrorCode = " « Pipe.rcDosErrorCode() « endl; return FALSE; } CTMessageHeader Header; MsgStream >> Header; switch (Header.eType() ) { case QUERY_CTID_STATUS: { QueryCTIDStatusMsg StatusMsg( Header ) ; MsgStream » *(QueryCTIDStatus*) fiiStatusMsg; if (!fProcessQueryCTIDStatus( Pipe, StatusMsg )) cout « "Error in fProcessQueryCTIDStatus, SQLCODE = " « (long)ulGetSQLCode() « endl; } break; case STORE_MONITOREVENT: { StoreMonitorEventMsg EventMsg( Header ); MsgStream » *(StoreMonitorEvent*) &EventMsg; if (!fProcessStoreMonitorEvent( Pipe, EventMsg )) { cout « "Error in fProcessStoreMonitorEvent, SQLCODE - • ' « (long)ulGetSQLCode() « endl; } } break; case CLI_QUIT: return FALSE; default: cout « "Unknown Command Received!" « endl; return FALSE; } return TRUE; } FLAG fProcessQueryCTIDStatus( MessagePipe fiiPipe, QueryCTIDStatusMsg &CTID ) { _CTlicense Rec; CTIDStatusResultMsg ResultMsg; if (!fXlatCliCTID( CTID.CTID, CTID.CTID )) { cout « "Error converting client CTID to server CTID" « endl; // Proccess error here. } SUBSTITUTE SHEET ResultMsg.QueryResult _fQueryLicense( &Rec, CTID.CTID ) ; if (!ResultMsg.QueryResult) { ResultMsg.CTID = CTID.CTID; ResultMsg.Status CTLicStatus: :ACTIVE; ResultMsg.PeriodDays 2; ResultMsg.PeriodMinutes o; ResultMsg.StolenFlag FALSE; ResultMsg.SpecialProcess o; ResultMsg.Orgnum_n .fSetNullO ResultMsg.LastCallTS_n fSetNullO ResultMsg.NextCallTS n fSetNullO ResultMsg.NextCallClTentTS_n fSetNullO ResultMsg.ProductType fSetNullO } else { ResultMsg.CTID Rec.CTID; ResultMsg.Status Rec.LicStatus; ResultMsg.PeriodDays Rec.PeriodDays; ResultMsg.PeriodMinutes Rec.PeriodMinutes; ResultMsg.StolenFlag Rec.StolenFlag •=•= ResultMsg.SpecialProcess = Rec.SpecialProcess; ResultMsg.LastCallTS_n .Assign( Rec.LastCallTS_N, DB_ISNULL( Rec.IsNull_LastCallTS ) ); ResultMsg.NextCallTS_n .Assign( Rec.NextCallTS_N, DB_ISNULL( Rec.IsNull_NextCallTS ) ); ResultMsg.NextCallClientTS_n .Assign( Rec.NextCallClientTS_N, DB_ISNULL( Rec.IsNull_NextCallClientTS ) ); if (DB_ISNULL( Rec.IsNull_Orgnum )) ResultMsg.Orgnum_n .fSetNull() ; else ResultMsg.Orgnum_n = Rec.Orgnum_N; ResultMsg.ProductType = Rec.ProductType; } cout « "SQLCODE = " « (long)ulGetSQLCode() « endl; // Return Query results. TStream Stream; Stream « ResultMsg; return Pipe.fSendMessage( Stream ); } FLAG fProcessStoreMonitorEvent( MessagePipe fiiPipe, StoreMonitorEventMsg &Msg ) { StoreResultMsg ResultMsg; // Prepare reply message. ResultMsg.Result = TRUE; SUBSTITUTE SHEET // Prepare the monitorevent data. _CTmonitorEvent Rec; if (!fXlatCliCTID( (ULONG&)Rec.CTID, Msg.CTID )) { cout « "Error converting client CTID to server CTID" « endl; // Proccess error here. } _fCopyTStoDBVars( Rec.ServerTS, NULL, Msg.ServerTS, "ServerTS" ) ; _fCopyTStoDBVars( Rec.ClientTS, NULL, Msg.ClientTS, "ClientTS" ); _fCopyTStoDBVars( Rec.TelcoTS_N, &Rec.IsNull_TelcoTS ( Msg.TelcoTS_n, "TelcoTS" ); Rec.DurationSec_N = Msg.DurationSec_n; Rec.IsNull_DurationSec = DB_NOT_NULL; if (!Msg.CallerID_n) { Rec.IsNull_CallerID = DB_NULL; } else { Rec.IsNull_CallerID = DB_NOT_NULL; strncpy( Rec.CallerID_N, Msg.CallerID_n, sizeof( Rec.CallerID_N ) ) ; } Rec.LineNum = Msg.LineNum; if (!Msg.LogFlag) { cout « "I VALID_DATA_ERROR: LogFlag is NULL, defaulting to FALSE" « endl; Rec.LogFlag = 'N' ; } else { Rec.LogFlag = ( (STRING)Msg.LogFlag) [0] ; } strncpy( Rec.EnvironmentID, Msg.EnvironmentID, sizeof( Rec.EnvironmentID ) ) ; Rec.ErrorCnt = Msg.ErrorCnt; // Update the License Record. if (!fUpdateLieenseStatus( Msg )) { if (ulGetSQLCode() != 100) { cout << "DB2_ERROR: Error updating License Table, CliCTID = " « Msg.CTID « " SQLCODE = " « (long)ulGetSQLCode() « endl; } } // Perform the insert. SUBSTITUTE SHEET if (!_fInsertIntoMonitorEvent( &Rec )) { ResultMsg.Result = FALSE; } else { if (Msg.StoreAsStolen) { if (!_fInsertIntoMonitorEventStolen( &Rec )) { ResultMsg.Result = FALSE; } } if (Msg.StoreAsExpire) { if (!_fInsertIntoMonitorEventExpired( &Rec )) { ResultMsg.Result - FALSE; } } } cout « "SQLCODE = " « (long)ulGetSQLCode() « endl; TStream Stream; Stream « ResultMsg; if (Pipe.fSendMessage( Stream ) fiifii ResultMsg.Result =•= TRUE) { DB.Commit() ; return TRUE; } else { DB.RollbackO ; return FALSE; } } FLAG fUpdateLieenseStatus( StoreMonitorEventMsg SMsg ) { _CTupdateLicenseStatus Rec; short dummy1; // Used to quiet the Null validation below. fXlatCliCTID( (ULONG&)Rec.CTID, Msg.CTID ); strncpy( Rec.Status, Msg.LicenseStatus, sizeof( Rec.Status ) ) ; _fCopyTStoDBVars( Rec.LastCallTS_N, fiidummyl, Msg.ServerTS, "LastCallTS" ); _fCopyTStoDBVars( Rec.NextCallTS_N, fiidummyl, Msg.NextCallTS_n, "NextCallTS" ) ; _fCopyTStoDBVars( Rec.NextCallClientTS_N, fiidummyl, Msg.NextCallClientTS_n, "NextCallClientTS" ); if (!Msg.NextCallTS_n) strcpy( Rec.NextCal1TS_N, "0001-01-01-00.00.00.000000" ) ; if (!Msg.NextCallClientTS_n) strcpy( Rec.NextCallClientTS_N, "0001-01-01-00.00.00.000000" ); return _fUpdateLieenseStatus( &Rec ) ; SUBSTITUTE SHEET FLAG _fCopyTStoDBVars( char *tsstring, short *indicator, CTTimestamp fits, STRING varname ) { if (Its) { if (indicator == NULL) { cout « "INVALID_DATA_ERROR: " « varname « " is NULL, forcing validation" « endl; ts.ForceValidate() ; } else { ♦indicator = DB_NULL; tsstring[0] = '\x0'; return FALSE; } } else if ( !ts.fValidate() ) { cout « "INVALID_DATA_ERROR: " « varname « " is invalid, forcing validation - " « ts « endl; ts.ForceValidate() ; } if (indicator != NULL) *indicator = DB_NOT_NULL; ts.ToSTRING( tsstring ); return TRUE; } /define INCL_NOPMAPI // no PM in this program /define INCL_DOS /define INCL_BSE /define INCL_DOSSEMAPHORES /define INCL_DOSNMPIPES /include <os2.h> /include <ctype.h> /include <stdlib.h> /include <iostream.h> /include <fstream.h> /include <server.h> /include <MessagePipe.HPP> /include <TModem.HPP> /include "CT_Trans.H" /♦GLOBAL VARIABLES*****************************************/ HEV hQuitSem; SUBSTITUTE SHEET // Temp, move to thread. CltMsgPipeFactory *factory; MessagePipe *pipe; /******************************************************** **/ FLAG fLoadLineThreads( TModemS, PCSZ, PCSZ ); void _Optlink CT_CommandThread( PVOID ); FLAG fParseCmd( TPort fiiPort, TConnectlnfo *CnctInfo, STRING buffer ) ; TPort: :ComSettings ComSetting = { "COM1", // port name 0, // not used 38400, // bps 8, // data bits TPort::NO, // no parity TPort::ONE // one stop bit }; int main( int argc, char *argv[] ) { APIRET re; cout « "CompuTrace Server V0.99q" « endl; // Check arguments, if (argc J= 4) { cout « "Usage: server <pipe_name> <port_name> <init_string>" « endl « endl; return 0; } // Create quit semaphore. if ((re = DosCreateEventSem( NULL, fiihQuitSem, 0, FALSE )) !- 0) return 1; factory •= new CltMsgPipeFactory( argvfl], 512 ); // Load port server threads. TPort Port; TModem Modem = Port; if (!fLoadLineThreads( Modem, argv[2], argv[3] )) return 2; cout « "Successfully . nnected to local modem" « endl; // Wait for quit signal. DosWaitEventSem( hQuitSem, SEM INDEFINITE WAIT ); return 0; } SUBSTITUTE SHEET // // fLoadLineThreads: Loads the threads to operate a server line. This function // should be called for each server line. // FLAG fLoadLineThreads( TModem &Modem, PCSZ port_str, PCSZ init_str ) { // Start port log. // Port.Logon() ; // Open port. ComSetting. port_name = port_str; if (!Modem.Port() .fOpenPort( ComSetting )) { cout « "Error openning port" « endl; return FALSE; } // Start the port manage thread. if (!Modem.Port() .fStartManageThread() ) { cout « "Thread execution error" « endl; return FALSE; } // Initialize the modem. STRING result = Modem.strSendCommand( init_str, -1 ); if (strcmp( result, "OK" ) !-= 0) { cout « "Error initiallizing modem" « endl; return FALSE; } // Connect pipe to dbserver. if (!factory->fCreatePipe( pipe )) return FALSE; if (!pipe->fOpenPipe() ) return FALSE; // Start the command thread. if (!Modem.Port() .fStartCommandThread( CT_CommandThread, (PVOID) &Modem )) { cout « "Thread execution error" « endl; Modem.Port() -KillManageThread() ; return FALSE; } return TRUE; // // CT_CommandThread: Processes incoming data from a server line. // void _Optlink CT_CommandThread( PVOID ptr ) { SUBSTITUTE SHEET TModem ϋModem = * (TModem*)ptr; // Alias (should be optimized out by the compiler) . // Thread local variables STRING result; TConnectlnfo cnct_info; while (TRUE) { result «= Modem.strGetString( -1 ); // Parse buffer for cmd. if (!fParseCmd( Modem.Port(), &cnct_info, result )) { memset( (PVOID) &cnct_info, '\x0', sizeof cnct_info ) ; } } } /define CND_DATE_FIELD "DATE =" /define CND_TIME_FIELD "TIME =" /define CND_NUMBER_FIELD "NMBR =" /define CND_NONUM_FIELD "REASON FOR NO NUMBER:" /define CND_NAME_FIELD "CALLER NAME:" /define CND_NONAME_FIELD "REASON FOR NO NAME:" // // fParseCmd: called when a '\n' has been received, this function will process the string. // Returns TRUE if a transaction is occuring, FALSE if the buffers should be cleared. // FLAG fParseCmd( TPort &Port, TConnectlnfo *cnct_info, STRING buffer ) { const char *index; // Parse command. if (strstr( buffer, "RING" ) !=>= NULL) { cout « "Command parsed as RING" « endl; } else if ((index = strstr( buffer, CND_DATE_FIELD )) != NULL) { index += sizeof CND_DATE_FIELD; while (!isdigit( *index )) index++; // Grab the month. if (!isdigit( *index ) J | !isdigit( *(index+l) )) return FALSE; cnct_info->cnd.month = (*index++ - '0') * 10; cnct_info->cnd.month += *index++ - '0'; // Grab the day. if (!isdigit( *index ) j j !isdigit( *(index+l) )) return FALSE; cnct info->cnd.day = (*index++ - '0') * 10; BSTITUTE SHEET cnct_info->cnd.day += *index++ - '0'; cout « buffer « endl; } else if ((index = strstr( buffer, CND_TIME_FIELD )) != NULL) { index += sizeof CND_TIME_FIELD; while (!isdigit( *index )) index++; // Grab the hour. if (!isdigit( *index ) J | !isdigit( *(index+l) )) return FALSE; cnct_info->cnd.hour - (*index++ - '0') * 10; cnct_info->cnd.hour += *index++ - '0'; // Grab the minute. if (!isdigit( *index ) | j !isdigit( *(index+l) )) return FALSE; cnct_info->cnd.minute = (*index++ - '0') * 10; cnct_info->cnd.minute += *index++ - '0'; cout « buffer « endl; } else if ((index = strstr( buffer, CND_NUMBER_FIELD )) != NULL) { index += sizeof CND_NUMBER FIELD; while (isspace( *index )) Tndex++; // Grab the number. for (int i = 0; i < CND_NUM_MAXLEN; i++) { if (indexfi] == '\xθ' | j index[i] == '\r') { cnct_info->cnd.number[i] = '\x0'; break; } else { cnct_info->cnd.number[i] — indexfi]; } } cout « buffer « endl; } else if (strstr( buffer, CND_NONUM_FIELD ) . = NULL) { index += sizeof CND_NONUM_FIELD; // Grab the string. while (isspace( *index )) index++; for (int i - 0; i < CND_NUM_MAXLE ; i++) { if (indexfi] == '\xθ' | | index[i] == , \r') { cnct_info->cnd.number[i] = '\x0'; break; } else { cnct_info->cnd.number[i] = indexfi]; } } cout « buffer « endl; } else if (strstr( buffer, CND_NAME_FIELD ) != NULL) { index += sizeof CND NAME FIELD; SUBSTITUTE SHEET // Grab the name. while (isspace( *index )) index++; for (int i ■ 0; i < CND_NAME MAXLEN; i++) { if (indexfi] == '\x0' J j Indexfi] == '\r') { cnct_info->cnd.name[i] = '\x0'; break; } else { cnct_info->cnd.name[i] = indexfi]; } } cout « buffer « endl; } else if (strstr( buffer, CND_NONAME_FIELD ) != NULL) { index += sizeof CND_NONAME_FIELD; // Grab the string. while (isspace( *index )) index++; for (int i •= 0; i < CND_NAME MAXLEN; i++) { if (indexfi] == '\xθ' |j Tndex[i] -— '\r') { cnct_info->cnd.name[i] = '\xθ'; break; } else { cnct_info->cnd.name[i] = index[i] ; } } cout « buffer « endl; } else if (strstr( buffer, "CONNECT" ) != NULL) { cout « "Command parsed as CONNECT" « endl; SntlConnect( Port, *pipe, cnct_info ); return FALSE; } else if (strstr( buffer, "NO CARRIER" ) != NULL) { cout « "Command parsed as NO CARRIER" « endl; return FALSE; } else if (strstr( buffer, "OK" ) != NULL) { cout « "Command parsed as OK" « endl; return FALSE; } else if (strstr( buffer, "ERROR" ) != NULL) { cout « "Command parsed as ERROR" « endl; return FALSE; } else { cout « "Unknown command received: " « buffer « endl; return FALSE; } return TRUE; SUBSTITUTE SHEET } /include <CTIMS.HPP> // // CTStatus friends and members. // CTStatus: :CTStatus() { memset( value, ' ', sizeof( value ) ); } CTStatus: :CTStatus( STRING str ) { ASSERT( strlen( str ) < sizeof( value ) ); memcpy( value, str, strlen( str ) ); } const char CTLicStatus: :STR_SET[ ] [CT_TOK_SIZE+l] = { UNUSED_TOK, NOTEST_TOK, ACTIVE TOK, EXPIRED_TOK }; CTLicStatusfit CTLicStatus: :operator = ( STRING str ) { for (int i = 0; i <= EXPIRED; i++) { if (strcmp( STR_SET[i], str ) == NULL) { setNotNull() ; value = VALUE( i ) ; return *this; } } ASSERT( FALSE ); // No match was found for the string. return *this; } /************★********* FLAG CTOrgnum: :fSetPrefix( STRING str ) { if (strlen( str ) != ORGNUM_PREFIX_SIZE) { return FALSE; } else { value[0] = str[0]; value[l] = strfl]; value[2] = str[2] ; value[3] = str[3]; SUBSTITUTE SHEET return TRUE; } } FLAG CTOrgnum::fSetIndex( UINT num ) { if (num > 9999) { return FALSE; } else { value[ORGNUM_PREFIX_SIZE + 0] = (num%10000) / 1000 + '0'; value[ORGNUM_PREFIX_SIZE + 1] = (num%1000) / 100 + '0'; value[ORGNUM_PREFIX_SIZE + 2] « (num%100) / 10 + '0'; value[ORGNUM_PREFIX_SIZE + 3] - (num % 10) + '0'; } } FLAG CTOrgnum::fGetPrefix( char *str ) const { if (strlen( str ) != ORGNUM_PREFIX_SIZE) { return FALSE; } else { strfO] = valuefO]; strfl] = value[l]; str[2] = value[2]; str[3] = valuef3]; str[4] - '\x0'; } } FLAG CTOrgnum: :fGetIndex( UINT &i ) const { i = atoi( &(value[ORGNUM_PREFIX_SIZE]) ); return TRUE; } FLAG CTOrgnum::fGeneratePrefix( STRING org_name ) { char pre[ORGNUM_PREFIX_SIZE]; // Grab first four alphanum characters. for (int i = 0, j = 0; i < ORGNUM_PREFIX_SIZE;) { if (isalnum( orgname[j++] )) prefi]; } } **********************/ //******************************************************* ********************** // II iostream stream operators. SUBSTITUTE SHEET // ostreamfii operator «( ostream &os, const CTStatus fiistatus ) { return os « (STRING)status; } //******************************************************* ********************** // // TStream stream operators. // TStreamfii operator « ( TStream &buf, const CTStatus fiistatus ) { buf « *(TNull*) fiistatus; if (!status) return buf; else return buf.Put( PVOID( status.value ) , sizeof( status.value ) ) ; } TStreamfii operator >> ( TStream fiibuf , CTStatus &status ) { buf » *(TNull*) fiistatus; if (!status) return buf; else return buf.Get( status.value, sizeof( status.value ) ) ; } TStreamfit operator « ( TStream &buf, const CTCallerlD &id ) { buf « *(TNull*) &id; if (!id) return buf; else return buf.Put( PVOID( id.value ), sizeof( id.value ) ) ; } TStreamfii operator » ( TStream fiibuf, CTCallerlD fiiid ) { buf » *(TNull*) &id; if (!id) return buf; else return buf.Get( id.value, sizeof( id.value ) ); } TStreamfit operator « ( TStream &buf, const CTLicStatus &lic ) { buf « * (TNull*) fiilic; if ( l lic) return buf ; else return buf « USHORT ( lie. value ) ; } TStreamfit operator » ( TStream &buf , CTLicStatus fitlic ) { SUBSTITUTE SHEET USHORT num; buf » *(TNull*)&lic; if (!lic) return buf; else { buf » num; lie.value = CTLicStatus: :VALUE( num ); return buf; } } TStreamfii operator « ( TStream ϋbuf, const CTOrgnum fiinum ) { buf « *(TNull*)fi-num; if (!num) return buf; else return buf.Put( PVOID( num.value ), sizeof( num.value ) ) ; } TStreamfit operator » ( TStream &buf, CTOrgnum fiinum ) { buf » *(TNull*)fiinum; if (!num) return buf; else return buf.Get( num.value, sizeof( num.value ) ); } TStreamfit operator « ( TStream fiibuf , const CTMonitorEvent & event ) { return buf « event.CTID « event.ServerTS « event.ClientTS « event.TelcoTS_n << event.DurationSec_n « event.CallerID_n « event.LineNum « event.LogFlag « event.EnvironmentID « event.ErrorCnt; } TStreamfii operator » ( TStream fiibuf , CTMonitorEvent & event ) { SUBSTITUTE SHEET » event.ErrorCnt; /include <CTMessage.HPP> //******************************************************* ********************** // // TStream stream operators. // TStreamfii operator « ( TStream &buf, const CTMessageHeader fiihead ) { return buf << head.ID « head.Type « head.Len; } TStreamfit operator >> ( TStream &buf , CTMessageHeader fiihead ) { buf » head.ID; buf » head.Type; buf » head.Len; return buf; /define INCL_NOPMAPI // no PM in this program /define INCL_DOS /define INCL_BSE /define INCL_DOSSEMAPHORES /define INCL_DOSNMPIPES /include <os2.h> /include "CT Buffer.HPP" CT_Buffer: :CT_Buffer() : head( 0 ) , tail( CT_BUFFER_MAXLEN ) { // Create the mutex sem. re = DosCreateMutexSe ( NULL, ϋhBufSem, 0, 0 ); if (re) {} / / Create the event sem. re = DosCreateEventSem ( NULL, fiihReleaseGetSem, 0 , 0 ) ; } CT_Buffer: :-CT_Buffer() { DosCloseMutexSem( hBufSem ) ; } void CT_Buf f er : : Flush ( ) SUBSTITUTE SHEET { ULONG post_count; DosRequestMutexSem( hBufSem, SEM_INDEFINITE_WAIT ); head = 0; tail = CT_BUFFER_MAXLEN; DosResetEventSem( hReleaseGetSem, &post_count ); DosReleaseMutexSem( hBufSem ) ; FLAG CT_Buffer: :fPutChar( char ch ) { FLAG ret_val; // Get ownership of the semaphore. re = DosRequestMutexSem( hBufSem, SEM_INDEFINITE_WAIT ); if (re) return FALSE; // First check that the log buffer hasn't overflown, if (!fIsFull()) { // Store the char, update head, signal the event, buffer[head] = ch; head = IncBufPtr( head ) ; DosPostEventSem( hReleaseGetSem ) ; ret_val = TRUE; } else ret_val = FALSE; // Release the semaphore. DosReleaseMutexSem( hBufSem ); return ret_val; } FLAG CT_Buffer: : f GetChar ( char fiich ) { ULONG post_count; FLAG ret_val; // If empty wait for timeout. if (fIsEmptyO) DosWaitEventSem( hReleaseGetSem, SEM_INDEFINITE_WAIT ) ; // Get ownership of the semaphore. re = DosRequestMutexSe ( hBufSem, SEM_INDEFINITE_WAIT ); if (re) return FALSE; if (!fIsEmpty()) { // Fetch the char, update tail. tail = IncBufPtr( tail ); ch = buffer[tail] ; ret_val = TRUE; } SUBSTITUTE SHEET else ret_val = FALSE; DosResetEventSe ( hReleaseGetSem, &post_count ) ; // Release the semaphore. DosReleaseMutexSem( hBufSem ) ; return ret_val; } /define INCL_NOPMAPI // no PM in this program /define INCL_DOS /define INCL_BSE /define INCL_DOSSEMAPHORES /define INCL_DOSNMPIPES /include <os2.h> /include "CT_Log.HPP" /include <fstream.h> CT_Log: :CT_Log( UINT len ) : buf_len( len ) , index( 0 ) { if ((buffer = new BYTE[buf_len] ) == NULL) { buf_len = index = 0; } } CT_Log: :-CT_Log() { if (buffer) DosFreeMem( buffer ); } BOOL CT_Log: :fPostChar( char ch ) { // First check that the log buffer hasn't overflown. if (!fIsFull()) { // Store the char, update head, buffer[index++] = ch; return TRUE; } else return FALSE; } BOOL CT_Log::fDumpLog( const char *fname ) { fstream dump; dump.open( fname, ios: :out ) ; if (Idump) return FALSE; dump.write( buffer, index ); dump,close(); SUBSTITUTE SHEET return TRUE; } /define INCL_DOSNMPIPES /include <os2.h> /include <MessagePipe.HPP> //******************************************************* ********************** // SvrMsgPipeFactory Implementation. //******************************************************* ********************** SvrMsgPipeFactory::SvrMsgPipeFactory( PCSZ name, UINT msg_len, UINT pipe_len ) : MsgPipeFactory( msg_len ) , pipe_name( name ) , pipe_len( pipe_len ) {} FLAG SvrMsgPipeFactory: :fCreatePipe( MessagePipe *&ppipe ppipe = new MessagePipe( this ); return TRUE; FLAG SvrMsgPipeFactory: :fDestroyPipe( MessagePipe *ppipe delete ppipe; return TRUE; FLAG SvrMsgPipeFactory::fOpenPipe( MessagePipe *pipe ) HPIPE hPipe; / Create and connect the named pipe. pipe->rc « DosCreateNPipe( (PSZ)pipe_name, ϋhPipe, NP ^ NOWRITEBEHIND | // Data sent to remote pipes Tm ediatly. NP_ACCESS_DUPLEX, // Two-way client/server communications. NP_WAIT ; // I/O to pipe blocked until data available. NP_TYPE_MESSAGE | // Message pipe type. NP_READMODE_MESSAGE j // Messafe read mode type. OxOOFF, // Infinite number of allowed instances of this pipe. SUBSTITUTE SHEET (uMaxMsgLen() + 2) * pipe_len,// Size of output buffer. (uMaxMsgLen() + 2) * pipe_len,// Size of input buffer. // Client open timeout (see DosWaitNPipe) . ); if (pipe->rc) return FALSE; pipe->rc = DosConnectNPipe( hPipe ) ; if (pipe->rc) return FALSE; pipe->SetHandle( hPipe ) ; return TRUE; FLAG SvrMsgPipeFactory: :fClosePipe( MessagePipe *pipe ) { HPIPE hPipe = pipe->GetHandle() ; // Wait till the pipe is empty. pipe->rc = DosResetBuffer( hPipe ) ; if (pipe->rc) return FALSE; // Disconnect the pipe handle. pipe->rc = DosDisConnectNPipe( hPipe ); if (pipe->rc) return FALSE; return TRUE; } //******************************************************* ********************** // CltMsgPipeFactory Implementation. //******************************************************* ********************** CltMsgPipeFactory: :CltMsgPipeFactory( PCSZ name, UINT msg_len ) : MsgPipeFactory( msg_len ) , pipe_name( name ) {} FLAG CltMsgPipeFactory: :fCreatePipe( MessagePipe *&ppipe ) { ppipe = new MessagePipe( this ) ; return TRUE; } FLAG CltMsgPipeFactory: :fDestroyPipe( MessagePipe *ppipe ) { delete ppipe; SUBSTITUTE SHEET return TRUE; } FLAG CltMsgPipeFactory: :fOpenPipe( MessagePipe *pipe ) { HPIPE hPipe; ULONG ulAction; pipe->rc = DosOpen ( pipe_name, fithPipe, fitulAction, 0 , FILE NORMAL, FILE_OPEN, OPEN~ACCESS_READWRITE | OPEN_SHARE_DENYNONE, (PEAOP2)NULL ) ; if (pipe->rc) return FALSE; pipe->SetHandle( hPipe ); return TRUE; } FLAG CltMsgPipeFactory: :fClosePipe( MessagePipe *pipe ) { HPIPE hPipe = pipe->GetHandle() ; // Wait till the pipe is empty. pipe->rc = DosResetBuffer( hPipe ) ; if (pipe->rc) return FALSE; // Close the pipe handle. re = DosClose( hPipe ); if (pipe->rc) return FALSE; return TRUE; //******************************************************* ********************** // MessagePipe Implementation //******************************************************* ********************** MessagePipe: :MessagePipe( MsgPipeFactory *mom ) : factory( mom ) { factory->InitPipe( this ); } MessagePipe: :-MessagePipe() { factory->DeinitPipe( this ); } FLAG MessagePipe::fOpenPipe() { return factory->fOpenPipe( this ); } SUBSTITUTE SHEET FLAG MessagePipe: :fClosePipe() { return factory->fClosePipe( this ); } FLAG MessagePipe: :fSendMessage( PCVOID msg, ULONG msg_len ) { ULONG cbWritten; re = DosWrite ( hPipe, (PVOID) msg , msg_len, fitcbWritten ); return (re == 0 && msg_len == cbWritten) ? TRUE : FALSE; } FLAG MessagePipe: :fGetMessage( PVOID msg, PULONG msg_len ) { // PRECONDITION( msg_len != 0 && *msg_len <= uMaxMsgLen() ) ; re = DosRead( hPipe, msg, *msg_len, msg_len ); return (re == 0) ? TRUE : FALSE; } FLAG MessagePipe: :fTransact( PCVOID out_msg, ULONG out_msg_len, PVOID in_msg, PULONG in_msg_len ) { // PRECONDITION( in_msg_len != 0 && *in_msg_len <= uMaxMsgLen() ) ; re = DosTransactNPipe( hPipe, (PVOID)out_msg, out_msg_len, in_msg, *in_msg_len, in_msg_len ) ; return (re == 0) ? TRUE : FALSE; } MessagePipe: :PIPE_STATE MessagePipe: :estate() { ULONG cbRead; AVAILDATA avail; ULONG state; // Use DosPeekNPipe to find the state of the pipe. re = DosPeekNPipe( hPipe, NULL, 0, &cbRead, fcavail, δtstate ) ; return (PIPE_STATE)state; } /ifdef OS2 /define INCL DOSDATETIME SUBSTITUTE SHEET /include <os2.h> /endif /include <ctype.h> /include <Objects.HPP> //A*******************************-*********************** ********************** // // TFlag members. // TFlag: :TFlag() : TNull( TRUE ) {} TFlag: :TFlag( FLAG flag ) : value( (flag != FALSE) ), TNull ( FALSE ) {} TFlag: :-TFlag() { /ifdef DEBUG fSetNullO / value = UNINIT_DATA; /endif } //******************************************************* ********************** // // TTimestamp members. // const UINT TTimestamp: :TSStringLen = 27; TTimestamp: :TTimestamp() : TNull( TRUE ) { /ifdef DEBUG Year = Month ■ Day = Hour = Minute = Second = Millisec = UNINIT_DATA; /endif } TTimestamp: :TTimestamp( USHORT yr, UCHAR mo, UCHAR dy, UCHAR hr, UCHAR mn, UCHAR sc, USHORT ms ) : Year( yr ) , Month( mo ) , Day( dy ) Hour( hr ) , Minute( mn ) , SUBSTITUTE SHEET Second( sc ) , Millisec( ms ) , TNull( FALSE ) {} TTimestamp: :-TTimestamp() { /ifdef DEBUG fSetNullO ; Year = Month = Day - Hour = Minute = Second = Millisec = UNINIT_DATA; /endif } FLAG TTimestamp: :fValidate() const { if (fIsNullO) return FALSE; // Check year. if (!Year J | Year > 9999) return FALSE; // Check month and day. if (!Day) return FALSE; switch (Month) { case 1: if (Day > 31) return FALSE; break; case 2: if (Year % 4 == 0 fitfii Year % 100 ! = 0) // Check for a leapyear. if (Day > 29) return FALSE; else if (Day > 28) return FALSE; break; case 3: if (Day > 31) return FALSE; break; case 4: if (Day > 30) return FALSE; break; case 5: if (Day > 31) return FALSE; break; case 6: if (Day > 30) return FALSE; break; case 7: if (Day > 31) return FALSE; break; case 8: if (Day > 31) return FALSE; break; case 9: if (Day > 30) return FALSE; break; case 10: SUBSTITUTE SHEET if (Day > 31) return FALSE; break; case 11: if (Day > 30) return FALSE; break; case 12: if (Day > 31) return FALSE; break; default: return FALSE; } // Check hours . if (Hour > 23) { if (Hour > 24 J J Minute j J Second J | Millisec) return FALSE; } // Check minutes, seconds and milliseconds. if (Minute > 59 J | Second > 59 J J Millisec > 999) return FALSE; return TRUE; } void TTimestamp: :ForceValidate() { setNotNullO ; Year - Month = Day = 1; Hour = Minute = Second = Millisec = 0; } FLAG TTimestamp::fIsValidTSString( STRING tS ) { if ( isdigit( ts[0] // Check Year. && isdigit( ts[l] && isdigit( ts[2] fitfit isdigit( tS[3] fiifii ts[4] == '_' && isdigit( ts[5] // Check Month. && isdigit( ts[6] fiifii ts[7] == I m. 1 && isdigit( ts[8] // Check Day. && isdigit( ts[9] && ts[10] - && isdigit( ts[ll] // Check Hour. && isdigit( ts[12] fiifii ts[13] « —■ I . I && isdigit( ts[14] // Check Minute. fiifii isdigit( ts[15] fiifii ts[16] «= = 1 1 && isdigit( ts[17] // Check Second. fiifii isdigit( ts[18] && ts[19] * • SB / * / && isdigit( ts[20] // Check Millisec. && isdigit( ts[21] && isdigit( ts[22] SUBSTITUTE SHEET && isdigit( ts[23] ) && isdigit( ts[24] ) && isdigit( ts[25] ) && ts[26] — '\x0') return TRUE; else return FALSE; } TTimestampfit TTimestamp: :Assign( const TTimestamp fiits ) { if (!ts) { fSetNullO ; } else { setNotNullO; Year = ts.Year; Month = ts.Month; Day = ts.Day; Hour = ts.Hour; Minute = ts.Minute; Second = ts.Second; Millisec = ts.Millisec; }. return (*this) ; } TTimestampfit TTimestamp: :Assign( USHORT yr, UCHAR mo, UCHAR dy, UCHAR hr, UCHAR mn, UCHAR sc, USHORT ms ) { setNotNullO ; Year = yr; Month = mo; Day = dy; Hour = hr; Minute = mn; Second = sc; Millisec = ms; return (*this) ; } TTimestampfii TTimestamp: :Assign( STRING ts, FLAG isnull ) { unsigned num; if (isnull) { fSetNullO; return *this; } setNotNullO ; SUBSTITUTE SHEET ASSERT( fIsValidTSString( ts ) ); return *this; } /ifdef 0S2 TTimestampfii TTimestamp: :Assign( const DATETIME fiiDate ) { setNotNullO ; Year = Date.year; Month = Daτ month; Day •**• Date, y; Hour = Date.nours; Minute = Date.minutes; Second = Date.seconds; Millisec = Date.hundredths * 10; return (*this) ; } /endif // OS2 STRING TTimestamp: :ToSTRING( char *ts ) const SUBSTITUTE SHEET { unsigned num; /* Convert year */ num = Year; ts[0] - (num%10000) / 1000 + '0'; ts[l] - (num%1000) / 100 + '0'; ts[2] - (num%100) / 10 + '0'; ts[3] = (num % 10) + '0'; ts[4] = '-'; /* Convert month */ num = Month; ts[5] = (num%100) / 10 + '0'; ts[6] = (num % 10) + '0'; ts[7] - '-'; /* Convert day */ num = Day; tsfβ] = (num%100) / 10 + '0'; ts[9] = (num % 10) + '0'; tsflO] = '-'; /* Convert hour */ num = Hour; tsfll] = (num%100) / 10 + '0'; ts[12] = (num % 10) + '0'; ts[13] = '.'; /* Convert minute */ num = Minute; ts[14] = (num%100) / 10 + '0'; ts[15] = (num % 10) + '0'; ts[16] = '.'; /* Convert second */ num = Second; ts[17] = (num%100) / 10 + '0'; ts[18] = (num % 10) + '0'; ts[19] = ' . ' ; /* Convert millisec */ num = Millisec; ts[20] = (num%1000) / 100 + '0'; ts[21] = (num%100) / 10 + '0'; ts[22] = (num % 10) + '0'; ts[23] ■ '0' ts[24] == '0' ts[25] « '0' return ts; } FLAG TTimestamp: :operator > ( const TTimestamp fitts ) const { useAsValue ( ) ; if (Year > ts . Year) return TRUE ; SUBSTITUTE SHEET else if (Year =•= ts.Year) { if (Month > ts. onth) return TRUE; else if (Month == ts.Month) { if (Day > ts.Day) return TRUE; else if (Day == ts.Day) { if (Hour > ts.Hour) return TRUE; else if (Hour == ts.Hour) { if (Minute > ts.Minute) return TRUE; else if (Minute =•= ts.Minute) { if (Second > ts.Second) return TRUE; else if (Second =-= ts.Second) { if (Millisec > ts.Millisec) return TRUE; else return FALSE; } } } return FALSE; } FLAG TTimestamp: :operator >= ( const TTimestamp fiits ) const { return (*this > ts \ \ *this === ts) ; } FLAG TTimestamp: :operator =-= ( const TTimestamp fiits ) const { useAsValue() ; if (Year == ts . Year fiifii Month == ts . Month fiifii Day == ts . Day && Hour == ts.Hour && Minute =•= ts.Minute && Second == ts.Second && Millisec == ts.Millisec) { return TRUE; } else { return FALSE; } } // Date and time add function. TTimestampfii TTimestamp: :AddToDate( UINT yr, UINT mon, UINT day, UINT hr, UINT min, UINT sec, UINT ms ) { if (!fIsNull()) { SUBSTITUTE SHEET ms += Millisec; sec += Second; min +•= Minute; hr += Hour; day +-= Day; mon +•= Month; yr +-= Year; } // Adjust and carry ms. while (ms > usMaxMillisec() ) { ms -•= usMaxMillisec() + 1; sec++; } // Adjust and carry sec. while (sec > usMaxSecond() ) { sec -= usMaxSecond() + 1; min++; } // Adjust and carry min. while (min > usMaxMinute() ) { min -= usMaxMinute() + 1; hr++; } // Adjust and carry hr. while (hr > usMaxHour()) { hr -= usMaxHour() + 1; day++; } // Adjust and carry mon (day adjust is dependent on mon and yr) . while (mon > usMaxMonth()) { mon -= usMaxMonth() ; yr++; } // Now adjust and carry day now that yr and mon is known, while (day > usMaxDay( yr, mon )) { day -= usMaxDay( yr, mon ) ; mon++; if (mon > usMaxMonth() ) { mon -- usMaxMonth() ; yr++; } } // Copy new values to members. Assign( yr, mon, day, hr, min, sec, ms ); CHECK( fValidate() ); return *this; } // static member. USHORT TTimestamp::usMaxDay( USHORT year, USHORT month ) SUBSTITUTE SHEET { switch (month) { case 1: // Jan. return 31; case 2: // Feb. return fIsLeapYear( year ) ? 29 : 28; //******************************************************* ********************** // // TStream stream operators. // TStreamfit operator « ( TStream fiibuf , const TFlag fiiflag ) { if (!flag) return buf « FLAG( TRUE ); else return buf « FLAG( FALSE ) « flag.value; } TStreamfii operator » ( TStream fiibuf , TFlag fiiflag ) SUBSTITUTE SHEET buf » * (TNull*) &f lag; if (flag. fIsNull ( ) == FALSE) buf » flag. value; return buf ; TStreamfii operator « ( TStream fiibuf , const TTimestamp fiits ) { if (Its) return buf « FLAG( TRUE ); else { return buf « FLAG( FALSE ) « ts.Year << ts.Month << ts.Day « ts.Hour « ts.Minute « ts.Second « ts.Millisec; } } TStreamfit operator » ( TStream fiibuf, TTimestamp fiits ) { buf » *(TNull*) &ts; if (Its) { return buf; } else { return buf » ts.Year >> ts.Month >> ts.Day » ts.Hour » ts.Minute » ts.Second >> ts.Millisec; } } //******************************************************* ********************** // // iostream friend function members. // ostreamfii operator « ( ostream fiios, const TFlag &flag ) { if (Iflag) return os « NULL_TOK; else return os « (STRING) flag; } /******************** istreamS operator « ( istrea &is , TFlag fitflag ) { SUBSTITUTE SHEET char ch, buffer[12]; is » ws; // Extract leading whitespace. for (int i = 0; i < sizeof( buffer ); i++) { is » buffer[i] ; if (!isalpha( bufferfi] )) break; } if (i == sizeof( buffer ) ASSERT( FALSE ); buffer[i] = '\x0'; if (strcmp( buffer, NULL_TOK) •= 0) { fSetNullO; } else if (strcmp( buffer, TRUE TOK) — 0) { Assign( TRUE ) ; } else if (strcmp( buffer, FALSE TOK) == 0) { Assign( FALSE ) ; } else ASSERT( FALSE ); return is; } ********************/ ostreamS operator « ( ostream &os, const TTimestamp fiits ) { char tsstring[TTimestamp: :TSStringLen] ; if (!ts) return os « "NULL"; else return os « ts.ToSTRING( tsstring ) ; } /define INCL_NOPMAPI // no PM in this program /define INCL_DOS ///define INCL_BSE ///define INCL_DOSSEMAPHORES /include <os2.h> /include <usertype.h> /include <TModem.HPP> TModem: :TModem( TPort &_port ) : port( _port ) {} TModem: :RC TModem: :rcSendCommand( STRING, ULONG timeout ) { NOTIMPLEMENTED; } SUBSTITUTE SHEET STRING TModem: :strSendCommand( STRING str, ULONG timeout ) { port.fWritePort( str ); port.fPutChar( '\r' ); STRING result = strGetString( timeout ); if (strcmp( str, result ) == 0) { return strGetString( timeout ) ; } else { return result; } } STRING TModem: :strGetString( ULONG timeout ) { UINT i = 0; last_result[0] = '\x0'; // Eat Leading CR/NL. while (!port.fGetChar( last_result[i] ) last_result[i] =•= '\r' ! | last_result[i] == '\n') {} i++; // (already got 1 char ok) // Grab text until a CR/NL. while (port.fGetChar( last_result[i] ) && last_result[i] != '\n' && last_result[i] != '\r' && i <= sizeof( last_result )) { i++; } last_result[i] = '\x0'; // Null terminate buffer. return last_result; } /include <TObject.HPP> //******************************************************* ********************** // // TObject members. // TObject: :-TObject() {} //******************************************************* ********************** // // TNull members. // TNull: :TNull( FLAG is_null ) : isnull( is_null ) SUBSTITUTE SHEET { } FLAG TNull: :fSetNullO { isnull = TRUE; return TRUE; } /define INCL_NOPMAPI // no PM in this program /define INCL DOS /define INCL_BSE /define INCL_DOSSEMAPHORES /define INCL_DOSNMPIPES /include <os2.h> /include <usertype.h> /include "TPacket.HPP" TPacket: :TPacket( TPortfit p ) : Port( p ) , text_length( 0 ) , state( TRANS NULL ) {} TPacket: :TRANS_STATE TPacket: :rGetPacket() { enq_count = 0 ; nak_count = 0 ; text_length = 0; if (state != TRANS_NULL) return TRANS_NULL; // Enquiry Loop. while (fSendENQO) { if ((state = rReceivePacket(J) == TRANS_NAK) { while (fSendNAKO) if ((state = rReceivePacket() ) == TRANS_ACK) { fSendACK() ; return state; } } else if (state » TRANS_ACK) { fSendACK() ; return state; } } fSendEOTO; SUBSTITUTE SHEET return state; } TPacket: :TRANS_STATE TPacket: :rReceivePacket() { char ch; int i=0,j; // Get STX. if (!Port.fGetChar( ch )) return TRANS_ETO; // packet_text[i++] = ch; if (ch !•= STX) return TRANS_NAK; // Get Length. if (!Port.fGetChar( ch )) return TRANS_NAK; // packet_text[i++] = ch; text_length - (USHORT)ch; if (!Port.fGetChar( ch )) return TRANS_NAK; // packet_text[i++] = ch; text_length = (USHORT) (ch « 8) + text_length; if (text_length > MAX_TEXT_LEN) return TRANS_NAK; // Get Text. for (j=0 ; j < text_length; j++ ) { if ( Port.fGetChar( ch )) packet_text[ j ] = ch; else return ( TRANS NAK ) ; } // Get ETX. if ( Port.fGetChar( ch )) { if ( ch == ETX ) // packet_text[ i++ ] = ch; else return ( TRANS_NAK ) ; } else { SUBSTITUTE SHEET return ( TRANS_NAK ) ; } // Get LRC. if (!Port.fGetChar( ch )) return TRANS_NAK; // packet_text[i++]*=ch; return TRANS_ACK; } UINT TPacket::cbCopyText( PVOID ptr, UINT len ) { len = len < text_length ? len : text_length; memcpy( ptr, packet_text, len ); return len; } FLAG TPacket: :fSendENQ() { char enq = ENQ; enq_count++; if (enq_count > MAX_ENQ) return FALSE; Port.FlushlnputBuffer() ; return Port.fWritePort( &enq, 1 ); } FLAG TPacket: :fSendACK() { char ack = ACK; Port.FlushlnputBuffer() ; return Port.fWritePort( &ack, 1 ); } FLAG TPacket: :fSendNAK() { char nak = NAK; nak_count++; if (nak_count > MAX_NAK) return FALSE; Port.FlushlnputBuffer() ; return Port.fWritePort( &nak, 1 ); } FLAG TPacket: :fSendEOT() { char eot = EOT; return Port.fWritePort( &eot, 1 ); } /define INCL_NOPMAPI // no PM in this program /define INCL DOS SUBSTITUTE SHEET /define INCL_BSE /define INCL_DOSSEMAPHORES /define INCL_DOSNMPIPES /define INCL_DOSDEVIOCTL /include <os2.h> /define _THREADS // This implemetation is multi-threaded. /include <process.h> /include <string.h> /include <stdlib.h> /include "TPort.HPP" TPort: :TPort() : manage_thread( -1 ) log_flag( FALSE ) O TPort: :-TPort() { while (manage_thread != -1) { KillManageThread() ; DosSleep( 1000 ); // Wait 1 second. } } FLAG TPort::fOpenPort( const ComSettings &settings ) { LINECONTROL lctl; DCBINFO deb; ULONG ulAction; ULONG UlPio, ulDio; ULONG cbTrans; // Open the port. re = DosOpen( settings.port_name, fiihPort, fitulAction, 0, 0, OPEN_ACTION_OPEN_IF_EXISTS, OPEN_FLAGS_WRITE_THROUGH | OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYREADWRITE, NULL ) ; if (re) return FALSE; // Set the line speed. ulPio = sizeof( settings.bps ); re ■ DosDevIOCtl( hPort, IOCTL_ASYNC, ASYNC_SETBAUDRATE, (PVOID) fitsettings.bps, UlPio, fiiulPio, NULL, 0, NULL ); if (re) { DosClose( hPort ) ; return FALSE; } // Set the line characteristics. lctl.bDataBits = settings.data_bits; SUBSTITUTE SHEET Ictl.bParity = (BYTE)settings.parity; lctl.bStopBits ■ (BYTE)settings.stop_bits; ulPio - sizeof lctl; re -= DosDevIOCtl( hPort, IOCTL_ASYNC, ASYNC_SETLINECTRL, iilctl, ulPio, iulPio, NULL, 0, NULL ); if (re) { DosClose( hPort ) ; return FALSE; } // Set the flow control. ulDio = sizeof deb; re - DosDevIOCtl( hPort, IOCTL_ASYNC, ASYNC_GETDCBINFO, NULL, 0, NULL, fitdcb, UlDio, fiiulDio ); if (re) { DosClose( hPort ) ; return FALSE; } /******************************************************** ********************** dcb.usReadTimeout = 100; dcb.fbCtlHndShake - MODE_CTS_HANDSHAKE; // flagsl = 00001000 dcb.fbFlowReplace &= 0x30; // flags2 = 00770000 dcb.fbFlowReplace |= MODE_RTS_HANDSHAKE; // flags2 = 10??0000 dcb.fbTimeout &= 0xF8; // flags3 = ?????000 dcb.fbTimeout |= MODE_WAIT_READ_TIMEOUT; // flags3 = ?????100 ********************************************************* *********************/ dcb.usReadTimeout = 300; dcb.fbCtlHndShake - MODE_CTS_HANDSHAKE; dcb.fbFlowReplace = MODE_RTS_RANDSHAKE; dcb.fbTimeout ■ MODE_NO_WRITE_TIMEOUT J MODE_WAIT_READ_TIMEOUT; re = DosDevIOCtl( hPort, IOCTL_ASYNC, ASYNC_SETDCBINFO, &dcb, ulPio, fiiulPio, NULL, 0, NULL ); if (re) { DosClose( hPort ) ; return FALSE; } fRaiseDTR(); return TRUE; } FLAG TPort: :fClosePort() SUBSTITUTE SHEET re = DosClose ( hPort ) ; if (re) return FALSE; else return TRUE; } void TPort: :FlushlnputBuffer() { BYTE cmd; // Scratch, Needed by API. ULONG len; // Scratch, Needed by API. re = DosDevIOCtl( hPort, IOCTL_GENERAL, DEV_FLUSHINPUT, &cmd, sizeof( cmd ), fiilen, fiicmd, sizeof( cmd ), &len ),* DosSleep(lO) ; // Timing Kludge - Give the Device Driver // time to flush buffer before resetting // semaphore stuff. buffer.Flus () ; } void TPort: :FlushOutputBuffer() { BYTE cmd; // Scratch, Needed by API. ULONG len; // Scratch, Needed by API. re = DosDevIOCtl( hPort, IOCTL_GENERAL, DEV_FLUSHOUTPUT, fiicmd, sizeof( cmd ), fiilen, &cmd, sizeof( cmd ), &len ); } FLAG TPort::fReadPort( PVOID buf, UINT &len ) { for (int i = 0; i < len; i++) { if (buffer.fIsEmptyO) { len = i; return TRUE; } else buffer.fGetChar( ((char*)buf) [i] ); } return TRUE; } FLAG TPort::fWritePort( PVOID buf, UINT len ) { ULONG cbWritten; re = DosWrite ( hPort , buf , len, fiicbWritten ) ; if (re) return FALSE; SUBSTITUTE SHEET else return TRUE; } FLAG TPort: :fDropDTR() { ULONG ulPio, ulDio; MODEMSTATUS ms; ULONG com_err; ms . fbModemOn = 0 ; ms.fbModemOff ■ DTR_OFF; ulPio = sizeof ms; ulDio •= sizeof com_err; re = DosDevIOCtl( hPort, IOCTL_ASYNC, ASYNC_SETMODEMCTRL, &ms, ulPio, fiiulPio, &com_err, ulDio, &ulDio ) ; if (re) return FALSE; else return TRUE; } FLAG TPort: :fRaiseDTR() { ULONG ulPio, UlDio; MODEMSTATUS ms; ULONG com_err; ms.fbModemOn = DTR_ON; ms.fbModemOff = OxFF; ulPio = sizeof ms; ulDio = sizeof com_err; re = DosDevIOCtl( hPort, IOCTL_ASYNC, ASYNC_SETMODEMCTRL, &ms, ulPio, &ulPio, &com_err, ulDio, &ulDio ) ; if (re) return FALSE; else return TRUE; } void _θptlink ManageThread( PVOID ); // Used internally by fStartManageThread() . void _Optlink ManageThread( PVOID ptr ) ((TPort*)ptr)->ManagePort() ; FLAG TPort: :fStartManageThread() fManThread = TRUE; manage thread = _beginthread( ManageThread, 8192, PVOID)this ) ; if (manage_thread == -1) return FALSE; else return TRUE; void TPort: :ManagePort() SUBSTITUTE SHEET char read_buf[32]; ULONG cbRead; while (TRUE) { re = DosRead( hPort, read_buf, sizeof read_buf, &cbRead ) ; if (re) { // handle error here... } else if (IfManThread) break; for (int i = 0; i < cbRead; i++) { if (log_flag) log.fPostChar( read_buf[i] ); buffer.fPutChar( read_buf[i] ) ; } buffer.SignalReleaseO ; } // Signal threads exit. manage_thread = -1; } FLAG TPort: :fStartCommandThread( TTHREAD CommandThread, PVOID data ) { fCmdThread = TRUE; command_thread = _beginthread( CommandThread, 8192, data ) ; if (command_thread == -1) return FALSE; else return TRUE; } /include <TStream.HPP> /include <debug.h> /include <string.h> //******************************************************* ********************** // // TStream members. // TStream::TStream( UINT buf_size ) : buf_len( buf_size ), buffer( new BYTE[buf_size] ), iptr( buffer ) , xptr( buffer ) { /ifdef DEBUG memset( buffer, UNDEF_DATA, buf_len ); /endif } TStream: :-TStream() { SUBSTITUTE SHEET delete buffer; void TStream: : Reset ( ) iptr = xptr = buffer; TStreamfii TStream: : operator « ( const FLAG flag ) *(FLAG*)iptr - flag; return inclnserter( sizeof( flag ) ); TStreamfii TStream: :operator « ( const USHORT num ) *(USHORT*) iptr = num; return inclnserter( sizeof( num ) ); TStreamfii TStream: :operator « ( const ULONG num ) *(ULONG*)iptr = num; return inclnserter( sizeof( num ) ); TStreamfit TStream: :operator « ( const char *str ) strcpy( iptr, str ) ; return inclnserter( strlen( str ) + 1 ) ; TStreamfii TStream: :Put( const PVOID data, UINT size ) memcpy( iptr, data, size ); return inclnserter( size ); TStreamfii TStream: :operator » ( FLAG fiiflag ) flag = *(FLAG*)xptr; return incExtractor( sizeof( flag ) ); TStreamfit TStream: :operator » ( USHORT fiinum ) num = *(USHORT*)xptr; return incExtractor( sizeof( num ); TStreamfi TStream: :operator » ( ULONG fiinum ) num •= *(ULONG*)xptr; return incExtractor( sizeof( num ) ); SUBSTITUTE SHEET TStreamfii TStream: :operator » ( char *str ) strcpy( str, xptr ) ; return incExtractor( strlen( str ) + 1 ) ; TStreamfii TStream: :Get( PVOID data, UINT size ) memcpy( data, xptr, size ) ; return incExtractor( size ); TStreamfii TStream: :incExtractor( UINT n ) xptr += n; ASSERT( xptr <•= iptr ) ; return *this; TStreamfii TStream: :inclnserter( UINT n ) iptr += n; ASSERT( iptr <= buffer + buf_len ) ; return *this; ;******************************************************** ***************** ;* ;* Copyright (C) 1995 Absolute Software Corporation ;* ;******************************************************** **************** NAME DBServer WINDOWCOMPAT IMPORTS CTIMS.fGenerateSerCTID CTIMS.fXlatSerCTID CTIMS.fXlatCliCTID CTIMS.fGenerateCTCODE CTIMS.fConvertStrToCTCODE CTIMS.fConvertCTCODEToStr \TObject.obj: \ f:\Server\TObject.CPP \ DBServer.MAK \objects.obj: \ f:\Server\objects.cpp \ DBServer.MAK \MessagePipe.obj: \ f:\Server\MessagePipe.CPP \ DBServer.MAK SUBSTITUTE SHEET . \CTMessage.obj: \ f:\Server\CTMessage.CPP \ DBServer.MAK .\ctims.obj: \ f:\Server\ctims.cpp \ DBServer.MAK . \DBServer.obj: \ f:\Server\DBServer.C \ {f:\Server;F: \Server\INCLUDE;E: \SQLLIB;E: \TOOLKT21\CPLUS\ OS2H;E:\Tools\IBMCPP\INCLUDE;}DBServer.H \ DBServer .MAK . \ TSTREAM. obj : \ f:\Server\TSTREAM.CPP \ DBServer.MAK . \TPacket.obj: \ f:\Server\TPacket.CPP \ {f:\Server;M:\SRC\Include;M:\CT\Include;$(INCLUDE) ;}TPack et.HPP \ Server.MAK . \TModem.obj: \ f: \Server\TModem.CPP \ Server.MAK .\CT_Log.obj: \ f: \Server\CT_Log.CPP {f:\Server;M:\SRC\Include;M:\CT\Include;$(INCLUDE) ;}CT_Lo g.HPP \ Server.MAK . \CT_Buffer.obj: \ f:\Server\CT_Buffer.CPP \ {f:\Server;M:\SRC\Include;M:\CT\Include;$(INCLUDE) ;}CT_Bu ffer.HPP \ {f:\Server;M:\SRC\Include;M:\CT\Include;$(INCLUDE) ;}serve r.h \ Server.MAK .\Server.obj: \ f: \Server\Server.C \ {f:\Server;M:\SRC\Include;M:\CT\Include;$(INCLUDE) ;}CT_Tr ans.H \ {f:\Server;M: \SRC\Include;M: \CT\Include;$(INCLUDE) ;}TPack et.HPP \ SUBSTITUTE SHEET Server . MAK .\CT_Trans.obj: \ f:\Server\CT_Trans.C \ {f:\Server;M:\SRC\Include;M:\CT\Include;$(INCLUDE) ;}CT_Tr ans.H \ {f:\Server;M:\SRC\Include;M:\CT\Include;$(INCLUDE) ;}TPack et.HPP \ Server.MAK .\TPort.obj: \ f:\Server\TPort.CPP \ {f:\Server;M:\SRC\Include;M:\CT\Include;$(INCLUDE) ;}TPort .HPP \ {f:\Server;M:\SRC\Include;M:\CT\Include;$(INCLUDE) ;}CT_Bu ffer.HPP \ {f:\Server;M:\SRC\Include;M:\CT\Include;$(INCLUDE) ;}CT_Lo g.HPP \ {f:\Server;M:\SRC\Include;M:\CT\Include;$(INCLUDE) ;}serve r.h \ Server.MAK /ifndef CT_TRANS_H /define CT_TRANS_H ///include <DB_Objects.HPP> /include <MessagePipe.HPP> /include "TPacket.HPP" void SntlConnect( TPort &Port, MessagePipe fiiPipe, TConnectlnfo *cnct_info ) ; void SntlDisconnect( TPort fiiPort, TConnectlnfo ϋConnectlnfo ) ; void SendDatePacket( TPort &Port, const SNTL_DATE &date ); void AddDays( SNTL_DATE *next_call, int days ); FLAG fGetDateTime( PDATETIME ) ; /endif /ifndef MESSAGE_H /define MESSAGE_H /******************************************************** ********************** Message.H SUBSTITUTE SHEET Defines all valid messages used by the Server and ServerShell. ********************************************************* *********************/ // Define standard types, /include <os2def.h> /include <time. h> // Definition for the Sentinel date packet, struct CT_DATE { BYTE year; BYTE month; BYTE day; BYTE hour; BYTE minute; }; // Definition for the Sentinel serial number packet, struct CT SN { USHORT sn[3]; USHORT cksum; CT_DATE date ; } ; /define CND_NUM_MAXLEN 20 /define CND NAME MAXLEN 20 Struct CALLERID_INFO { BYTE month; BYTE day; BYTE hour; BYTE minute; CHAR number[CND_NUM_MAXLEN] ; CHAR name[CND_NAME_MAXLEN] ; } ; enum TRANS_STATE { TRANS_OK = 0x00, TRANS_BAD_CND ' - 0x01, TRANS_BAD_SN = 0x02, TRANS_BAD_DATE - 0x04 in¬ struct CT_Transaction { DATETIME start_time; CALLERID_INFO end; CT_SN sn; TRANS_STATE state; DATETIME end_time; } ; enum CT_SN_QUERY { SUBSTITUTE SHEET }; /define CT_BUFFER_LEN 256 // Allowable length of modem communications for a cycle, /define CT GUARD CHAR ' ! ' /* Definitions for stripped CompuTrace messages. ******************/ /define MAX_PHONE_NUM_LEN 16 // Max length of a phone number string. /define CT_SERIAL_NUM_LEN sizeof( CT_SN ) // Length of serial number packet sent by the modem. /define MAX_ERROR_STR_LEN 32 // Max length of an error string. enum CTMSG TYPE { CTMSG_UNDEF - 0, CTMSG_CONNECT, CTMSG_SERIAL_NUM , CTMSG_ERROR_LOG , CTMSG_DISCONNECT } ; struct CT__ConnectMsg { time_t connect_time; char phone_nu [ MAX_PHONE_NUM_LE ] ; }; struct CT_SerialNumMsg { CT_SN serial_num; }; struct CT_ErrorLogMsg { char error_str[MAX_ERROR_STR_LEN] ; }; struct CT_DisconnectMsg { time_t~disconnect_time; char log[CT_BUFFER_LEN] ; }; struct CTMessage { CTMSG_TYPE type; union { CT_ConnectMsg Connect; CT_SerialNumMsg SerialNum; CT_ErrorLogMsg ErrorLog; CT_DisconnectMsg Disconnect; SUBSTITUTE SHEET } Msg; }; /define MAX_CTMSG_SIZE sizeof( CTMessage ) // Max size of a stripped (CompuTrace) message. /* Definitions for pipe messages. **********************************/ // Define all valid events. The following prefixes are used: // cτ _ - ?or general messages // CT_SER_ For server originated messages not related to a transaction. // CT_CLI_ For client originated messages not related to a transaction. // CT_SER_MSG_ For server originated messages related to a transaction. // CT_CLI_MSG_ For client originated messages related to a transaction. // For more detailed information please see the proper message structure. enum EVENT_TYPE { CT_SER τ MSG_AWK, // Server awknowledges last receϊved message. CT_SER_MSG_ERROR, // Server has had a non- fatal error. CT_SER_MSG_FATAL, // Server has had a fatal error and will unconditionally terminate. CT_SER_MSG_MESSAGE, // Server has a message to be processed by the client. CT_SER_STOP, // Server requests the client(s) stop sending messages. CT_SER_START, // Server allows the client(s) to continue sending messages. CT_SER_ERROR, // Server has had an internal non-fatal error. CT_SER_FATAL, // Server has had an internal fatal error and will terminate. CT_SER_STRING, // Server has a general string to be stored. CT_SER_QUIT, // Server has requested all clients to terminate. CT_CLI MSG_AWK. // Client awknowledges last received message. CT_CLI_MSG_ERROR, // Client has had a non- fatal error. CT_CLI_MSG_FATAL, // Client has had a fatal error and will unconditionally terminate. CT_CLI_MSG_M-*:SSAGE // Client has a message to be processed by the server. SUBSTITUTE SHEET }; // Define message transfer template used to transfer a message through a pipe. struct CT MessageHead { ULONG Td; // The message id number. EVENT_TYPE type; // The event type (see above) . BYTE len; // The length the message data. }; struct CT_MessageBuffer { CT_MessageHead header; char message[MAX_CTMSG_SIZE] ; /define MAX_MSG_SIZE sizeof( CT_MessageBuffer ) // Max size of a pipe message. /endif // MESSAGE_H /ifndef PACKET_H /define PACKET_H // Ensure byte alignment enforced! /pragma pack( 1 ) // For C-Set++ /pragma option -al // For BC++ /* Packet Level Defines ******************************************************/ /define STX text. /define ETX text. /define EOT transmission. /define ENQ /define ACK Acknowledgement. /define NAK acknowledgement. /define MAX_ENQ number of ENQs. /define MAX_NAK number of NAKs. /define MAX_TEXT_LEN of a packets TEXT. Struct PKT_HEADER { BYTE stx; BYTE lsb length; SUBSTITUTE SHEET BYTE msb_length; }; struct PKT_FOOTER { BYTE etx; BYTE lrc; }; /* Packet type definitions ***************************************************/ // Text Type IDs. /define CTID_TEXT_TYPE (WORD)0x0000 // Sentinel Subscription Number Packet. /define NCJTEXTJTYPE (WORD)0x0080 // Server Next Call Packet. struct SNTL_DATE { BYTE year; BYTE month; BYTE day; BYTE hour; BYTE minute; }; struct CTID_TEXT { BYTE type; BYTE sub_type; WORD sn [ 3 ] ; SNTL_DATE now_date; }; /define SN_TEXT CTID_TEXT // Old name (uses should be changed to CTID_TEXT) . struct CTID_PACKET { PKT HEADER header; CTID_TEXT text; PKT_FOOTER footer; }; /define SN_PACKET CTID_PACKET // Old name (uses should be changed to CTID_PACKET) . struct NC_TEXT { WORD type; SNTL_DATE next_call_date; }; struct NC_PACKET { PKT_HEADER header; NC TEXT text; PKT__FOOTER footer; }; /pragma pack() // Back to default. /pragma option -a. SUBSTITUTE SHEET /endif /ifndef SERVER_H /define SERVER_H /define DEBUG 4 /include <debug.h> /include <usertype.h> // // TConnectlnfo definition. // /define CND_NUM_MAXLEN 20 /define CND NAME MAXLEN 20 struct CALLERID_INFO { BYTE month; BYTE day; BYTE hour; BYTE minute; CHAR number[CND_NUM_MAXLEN] ; CHAR name[CND_NAME_MAXLEN] ; }; struct TConnectlnfo { DATETIME start_time, end_time; CALLERID_INFO end; }; // // End of TConnectlnfo // /endif // SERVER_H /ifndef CT_BUFFER_HPP /define CT_BUFFER_HPP /include "server.h" /define TRUE 1 /define FALSE 0 /define CT_BUFFER_MAXLEN 256 class CT_Buffer { char buffer[CT BUFFERJ4AXLEN]; UINT head, tail; HMTX hBufSem; HEV hReleaseGetSem; APIRET re; UINT IncBufPtr( UINT ptr ) const { return (++ptr >= CT_BUFFER_MAXLEN) ? 0 : ptr; } public: SUBSTITUTE SHEET CT_Buffer() ; -CT_Buffer(); void Flush() ; BOOL fIsEmptyO const { return head — IncBufPtr( tail ); } BOOL fIsFullO const { return head *= tail; } void SignalReleaseO { DosPostEventSem( hReleaseGetSem ); } BOOL fPutChar( char ) ; BOOL fGetChar( charδt ) ; }; /endif /ifndef CT_LOG_HPP /define CT LOG HPP /define TRUE 1 /define FALSE 0 class CT_Log { char *buffer; UINT index, buf_len; public: CT_Log( UINT ■ 4096 ); ~CT_Log(); void Flush() { index •= 0; } BOOL fIsEmptyO const { return index « 0; } BOOL fIsFullO const { return index >■ buf_len; } BOOL fPostchar( char ); BOOL fDumpLog( const char * ) ; ' /endif /ifndef TCLIENT_HPP /define TCLIENT HPP class TClient { TConnectlnfo Connectlnfo; WORD ctid[3]; SNTL DATE client date; SUBSTITUTE SHEET Pipe public: /endif // CLIENT_HPP /ifndef TPACKET_HPP /define TPACKET_HPP /include <os2def.h> /include "packet.h" /include <TPort.HPP> /******************************************************* ********************* / Class TPacket - Encapsulates the reception of a packet for a port / / TPacket: :TPacket( TPortfii Port ) Initializes internal state. / Arguments: / TPortfit Port - the port to receive the packet from. / TRANS_STATE TPacket::rGetPacket() / Description: / Attempts to receive a packet from Port using the protocol / defined in the CompuTrace Protocol Specification (CTPSpec) . / / Returns: The result of the attempt: / TRANS_ACK - packet successfully received as defined by CTPSpec. TRANS_NAK - reception aborted due to invalid reception, EOT sent. / TRANS_ETO - ENς timeout, no date recieved, EOT sent. / / UINT TPacket::cbCopyText( ptr, len ) / Arguments: PVOID ptr - the buffer to copy data to. UINT len - the maximum number of bytes to copy. SUBSTITUTE SHEET // // Description: // Copies text from a sucessfully received packet into buffer pointed to // by ptr. Copies up to len bytes or the size of the received packet // text (whichever is smaller) . Can only be called if rGetPacket // returned TRANS_ACK. // // Returns: number of bytes copied, or 0 if packet not successfully // received. // // TRANS_STATE rState() const // Returns: the current state of the instance. / ******************************************************* ********************** class TPacket { public: enum TRANS_STATE { TRANS_NULL, // No activity. TRANS_ACK, TRANS_NAK, TRANS_ETO }; // ETO = Enquiry time-out. TPacket( TPortfii ) ; TRANS_STATE rGetPacket() ; UINT cbCopyText( PVOID ptr, UINT len ); TRANS_STATE rState() const { return state; } protected: FLAG fSendENQO ; FLAG fSendACK() ; FLAG fSendNAK() ; FLAG fSendEOT() ; private: TPortfii Port; int enq_count; int nak_count; USHORT text_length; BYTE packet_text[MAX_TEXT_LEN]; TRANS_STATE state; TRANS_STATE rReceivePacket() ; }; /endif SUBSTITUTE SHEET / Created by IBM WorkFrame/2 MakeMake at 17:36:34 on 08/22/95 / / This makefile should be run in the following directory: / d:\Server / / The actions included in this makefile are: / COMPILE::CLC C++ / LINK::CLC Link .all: \ . \DBServer.EXE .SUFFIXES: .SUFFIXES: .C .CPP .CPP.obj: θecho WF: :COMPILE::CLC C++ icc.exe /Tl- /Xi /ID:\Server\INCLUDE /IE:\SQLLIB /IE: \TOOLKT21\CPLUS\OS2H /IE: \TooIs\IBMCPP\INCLUDE /DDEBUG=4 /Tdp /Q /Wall /Fi /Ti /G /G5 /Tm /C %s .C.obj: §echo WF: -.COMPILE: :CLC C++ icc.exe /Tl- /Xi /ID: \Server\INCLUDE /IE:\SQLLIB /IE: \TOOLKT21\CPLUS\OS2H /IE: \Tools\IBMCPP\INCLUDE /DDEBUG=4 /Tdp /Q /Wall /Fi /Ti /Gm /G5 /Tm /C %s .\DBServer.EXE: \ . \TObject.obj \ . \TSTREAM.obj \ . \DBServer.obj \ .\ctims.obj \ .\CTMessage.obj \ .\MessagePipe.obj \ .\objects.obj \ {$(LIB) }DB_Objects.LIB \ {$(LIB)}SQL_DYN.LIB \ {$(LIB)}DBServer.DEF \ DBServer.MAK §echo WF: :LINK: :CLC Link icc.exe §« /Tl- /Xi /ID: \Server\INCLUDE /IE:\SQLLIB /IE:\TOOLKT21\CPLUS\OS2H /IE: \Tools\IBMCPP\INCLUDE /DDEBUG=4 /Tdp /Q /Wall /Fi /Ti /Gm /G5 /Tm /B" /de" /FeDBServer.EXE SUBSTITUTE SHEET DB_Objects.LIB SQL_DYN.LIB DBServer.DEF .\TObject.obj .\TSTREAM.obj . \DBServer.obj . \ctims.obj .\CTMessage.obj . \MessagePipe.obj . \objects.obj « !include DBServer.Dep / Created by IBM WorkFrame/2 MakeMake at 10:20:11 on 05/30/95 / / This makefile should be run in the following directory: / d:\Server / / The actions included in this makefile are: / COMPILE: :CLC C++ / LINK: :CLC Link .all: \ . \Server.EXE .SUFFIXES: .SUFFIXES: .C .CPP .CPP.obj : §echo WF: :COMPILE::CLC C++ icc.exe /Tl- /ID: \Server\Include /IM:\CT\Include /Tdp /Q /Wall /Fi /Si /Ti /O /Gm /G5 /Tm /C %s .C.obj : θecho WF: :COMPILE::CLC C++ icc.exe /Tl- /ID: \Server\Include /IM:\CT\Include /Tdp /Q /Wall /Fi /Si /Ti /O /Gm /G5 /Tm /C %s . \Server.EXE: \ . \TPacket.obj \ .\TPort.obj \ . \CT_Trans.obj \ . \Server.obj \ .\CT_Buffer.obj \ .\CT_Log.obj \ .\TModem.obj \ {$(LIB)}CTIMS.LIB \ {$(LIB)}MessagePipe.LIB \ Server.MAK §echo WF: :LINK::CLC Link icc.exe §« /Tl- SUBSTITUTE SHEET /ID:\Server\Include /IM:\CT\Include /Tdp /Q /Wall /Fi /Si /Ti /O /Gm /G5 /Tm /B" /de" /FeServer.EXE CTIMS.LIB MessagePipe.LIB .\TPacket.obj .\TPort.obj .\CT_Trans.obj . \ Server. obj . \CT_Buffer. obj . \CT_Log . obj .\TModem.obj « !include Server.Dep /ifndef CTID_H /define CTID_H /*** MOVE TO USERTYPE ***/ /* /define LOWORD( x ) ((WORD) ((DWORD) (x) ) ) /define HIWORD( x ) ((WORD)((x) » 16))*/ /************************/ /define CTCODE_STR_LEN 10 typedef WORD *CTCODE; extern "C" { // // fGenerateSerCTID - Creates a new valid Server CTID value. // FLAG APIENTRY fGenerateSerCTID( ULONG &ctid ); // // fXlatSerCTID - Translates a ServerCTID to a ClientCTID. // FLAG APIENTRY fXlatSerCTID( ULONG &cli_ctid, ULONG ser_ctid ) ; // // fXlatCliCTID - Translates a ClientCTID to a ServerCTID. // FLAG APIENTRY fXlatCliCTID( ULONG &ser_ctid, ULONG cli_ctid ) ; // SUBSTITUTE SHEET // fGenerateCTCODE - Creates a 48 bit CTCODE from a valid Client CTID. // FLAG APIENTRY fGenerateCTCODE( CTCODE eteode, ULONG cli_ctid ) ; // // fConvertStrToCTCODE - Converts a string to CTCODE. // Arguments - WORD *ctcode: an array of 3 WORDS to be set to the 48 bit // binary representation of the input string. // STRING str: the input string of size CTID_STR_LEN. // FLAG APIENTRY fConvertStrToCTCODE( CTCODE eteode, STRING str ) ; // // fConvertCTCODEToStr - Converts a CTCODE number to a string. // Arguments - char *str: the output string of size CTID_STR_LEN. // WORD *eteode: the input array of 3 WORDS. // FLAG APIENTRY fConvertCTCODEToStr( char *str, const CTCODE eteode ) ; }; // end extern "C" /endif // CTID_H /ifndef CTIMS_H /define CTIMS H /include <usertype.h> /ifdef cplusplus extern "C" { /endif /define CALLERID_SIZE 21 /define CTSTATUS_SIZE 9 /define CTORGNUM_SIZE 9 /define CTTS_SIZE 27 typedef struct { long CTID; char LicStatus[CTSTATUS_SIZE]; long PeriodDays; long PeriodMinutes; char StolenFlag; long SpecialProcess; char LastCallTS_N[CTTS_SIZE]; short IsNull_LastCallTS; SUBSTITUTE SHEET char NextCallTS_N[CTTS_SIZE]; short IsNull_NextCallTS; char NextCallClientTS_N[CTTS_SIZE]; short IsNull_NextCallClientTS; char Orgnum_N[CTORGNUM_SIZE]; - short IsNull_Orgnum; char ProductType[CTSTATUS_SIZE]; } _CTlicense; typedef struct { long CTID; char Status[CTSTATUS_SIZE] ; char LastCallTS_N[CTTS_SIZE]; char NextCallTS N[CTTS_SIZE] ; char NextCallClTentTS_N[CTTS_SIZE] ; } _CTupdateLicenseStatus; typedef struct { long CTID; char ServerTS[CTTS_SIZE] ; char ClientTS[CTTS_SIZE) ; char TelcoTS_N[CTTS_SIZE] ; short IsNull_TelcoTS; short DurationSec_N; short IsNull_DurationSec; char Ca11erID_N[CALLERID_SIZE] ; short IsNull_CallerID; short LineNum; char LogFlag; char EnvironmentID[CTSTATUS_SIZE] ; short ErrorCnt; } _CTmonitorEvent; /* CTIMS.SQC */ FLAG _fQueryLicense( _CTlicense*, ULONG CTID ); FLAG _fUpdateLieenseStatus( const _CTupdateLicenseStatus* ); FLAG _fInsertIntoMonitorEvent ( const _CTmonitorEvent* ) ; FLAG _fInsertIntoMonitorEventStolen ( const _CTmonitorEvent* ) ; FLAG __fInsertIntoMonitorEventExpired( const _CTmonitorEvent* ) ; /* Index.SQC */ long lLastSQLCODE() ; FLAG ~fGetNextTablelndex( ULONG *index, ULONG *count, STRING ViewName ) ; /* ORG01.SQC */ SUBSTITUTE SHEET FLAG _fMayRemoveCustomer( STRING orgnum ); // Checks if a customer may be removed. FLAG _fDbArchiveCustomer( STRING orgnum ); // Archives a customer. FLAG _fDbDeleteCustomer ( STRING orgnum ) ; // Deletes a customer and all associated data. FLAG _fDbDeleteOrg( STRING orgnum ); // Deletes an org and all associated data. FLAG _fIsACustomer( STRING orgnum, FLAG exclusive ); // Determines whether an org is a customer. /ifdef cplusplus } /endif /endif // CTIMS_H /ifndef DB_H /define DB_H /include "DB_Structs.H" /ifdef cplusplus extern "C" { /endif FLAG flnitDBO ; FLAG fConnectDB( PCSZ db_str ); ULONG ulGetSQLCode() ; void CommitWorkO ; void RollbackWorkO ; /ifdef cplusplus } /endif /endif // DB_H /ifndef DBSERVER_H /define DBSERVER_H /define SHIP 0 /define DEBUG 4 /include <debug.h> /include <usertype.h> /endif // SERVER_H /ifndef DB STRUCTS_H /define DB~STRUCTS_H /ifdef cplusplus extern "C" { /endif SUBSTITUTE SHEET /pragma pack( 1 ) } T meStampStruct; typedef struct _MonitorEventStruct { ULONG CompuTracelD; TimeStampStruct ServerTS; TimeStampStruct PropertyTS; TimeStampStruct TelcoTS; char CallerID[20] ; SHORT CallSeconds; char EnvID[8] ; } MonitorEventStruet; /pragma pack() /ifdef cplusplus } /endif /endif // DB_STRUCTS_H /ifndef DEBUG_H /define DEBUG_H //******************************************************* ********************** // // DEBUG_H - sets the debug level of the code. // /define SHIP - 1 and /undef DEBUG for ship code. // // /define SHIP = 0 and DEBUG is defined for debug code. // DEBUG = 1 - beta level, PRECONDITION active. // DEBUG ■ 2 - alpha level, adds CONDITION. // DEBUG = 3 - pre-alpha level, adds CHECK. // DEBUG = 4 - sanity check level, adds SANITYCHECK. // //******************************************************* ********************** /ifdef DEBUG SUBSTITUTE SHEET /define ASSERT ( x ) assert( x ) /define NOTIMPLEMENTED assert( 0 /* Not implemented error */ ) /else /define NDEBUG // Disables debugging in assert.h /define ASSERT( x ) (void)0 /define NOTIMPLEMENTED (void)O /endif // DEBUG /include <assert.h> /if DEBUG >= 1 /define PRECONDITION( x ) /else /define PRECONDITION( x ) /endif /if DEBUG >= 2 /define CONDITION( x ) /else /define CONDITION( x ) /endif /if DEBUG >= 3 /define CHECK( x ) /else /define CHECK( x ) /endif /if DEBUG >= 4 /define SANITYCHECK( x ) /else /define SANITYCHECK( x ) /endif /define UNDEF_DATA Used to show unallocated memory. /define JUNK /define UNINIT_DATA Used to show uninitialized data. /endif // DEBUG_H /ifndef USERTYPE_H /define USERTYPE_H /ifdef OS2 /include <os2def.h> /endif /ifndef CSET /define _Optlink SUBSTITUTE SHEET /endif // Standard typedef's for Absolute Software. /define MAX( x, y ) ((x) > (y) (x) (y)) /define MIN( x, y ) ((x) < (y) (x) (y)> /ifndef NULL /define NULL 0 /endif /define TRUE 1 /define FALSE 0 typedef unsigned char FLAG; typedef unsigned char BYTE; typedef unsigned char UCHAR; typedef unsigned short USHORT; typedef unsigned int UINT; typedef unsigned long ULONG; /ifndef _Windows typedef unsigned short WORD; typedef unsigned long DWORD; /endif typedef const char* STRING; typedef const void* PCVOID; /ifdef OS2 typedef void (* _Optlink TTHREAD) ( PVOID ) ; /endif /ifdef cplusplus template <class Tl, class T2> FLAG operator == ( Tl cl, T2 c2 ) { return FLAG( cl =•= c2 ) ; } template <class Tl, class T2> FLAG operator != ( Tl cl, T2 c2 ) { return FLAG( cl != c2 ) ; } /endif // cplusplus /endif // USERTYPE_H /ifndef CTIMS_HPP /define CTIMS HPP /include <iostream.h> /include <string.h> SUBSTITUTE SHEET /define INCL_DOSDATETIME /include <os2.h> /include <debug.h> ///include <packet.h> /include <Objects.HPP> /include <CTIMS.H> /pragma pack( 1 ) // Needed for CTStatus, CTOrgNum. /define CT TOK SIZE /define UNUSED_TOK "UNUSED /define NOTEST_TOK "NOTEST /define ACTIVE_TOK "ACTIVE /define EXPIRED_TOK "EXPIRED /define UNDEFINED TOK ///define Y_TOK •ιγιι ///define N_TOK "N" ///define UNDEF_FLAG_TOK M II /define ORGNUM_SIZE 8 /define ORGNUM PREFIX SIZE 4 //βββββββββββββββββββββββββββββββββββββββββββββββββββββββ ββββββββββββββββββββββ // // CTIMS General types. // // The following types are general types used in CTIMS. They are not specific // to a single implementation: // // TFlags - used for boolean fields such as StolenFlag and InsuredFlag. // TTimestamp - used to represent timestamps with millisecond resolution. // TString - represents a string of characters. // // //βββββββββββββββββββββββββββββββββββββββββββββββββββββββ ββββββββββββββββββββββ ittllttiitiϊitiitϊlilt II II CTIMS Flag II llttttittttitϊlititttttilitttiiϊϊiiϊttttitttttttiiilttϊti /****************** class CTFlag : public TFlag { SUBSTITUTE SHEET public: CTFlagO : TFlag() {} CTFlag( FLAG flag ) : TFlag( flag ) {} }; ********************/ typedef TFlag CTFlag; //lilllfllϊlflflfϊlfllffflflllfffllfffimilfiil«ll«fff ϊlllfll-tlllflfllflllll / / // CTIMS Timestamp //fffffffffffffffϊlϊffffffifffiftiffϊffffifffffffiftfffff llffflifllllϊlllfllfϊf /****************** class CTTimesta p : public TTimestamp { public: CTTimestamp() : TTimestamp() {} CTTimestamp( USHORT yr, UCHAR mo, UCHAR dy, UCHAR hr = 0, UCHAR mn ■ 0, UCHAR SC - 0, USHORT ms = 0 ) : TTimestamp( yr, mo, dy, hr, mn, sc, ms ) {} }; ******************/ typedef TTimestamp CTTimestamp; // UNDER CONSTRUCTION!!! fllflillfffftllffϊffll / / // CTIMS Status // class CTStatus : public virtual TNull { public: CTStatus() ; CTStatus( STRING str ) ; operator STRING() const { return value; } friend ostreamfii operator « ( ostream&, const CTStatus& ) ; friend TStreamS operator « ( TStreamfii, const CTStatus& ) ; SUBSTITUTE SHEET friend TStreamfit operator » ( TStreamfit, CTStatus& ) ; private: char value[9] ; }; //βββββββββββββββββββββββββββββββββββ β β β β β β β β β β β ββ β β β β β ββ ββββββββββββββββββββββ // // CTIMS Specific types. // // The following types represent specific data types in CTIMS. // // CTCallerlD - used to store the CallerlD data received from the modem. // CTLicStatus - Used in the License table to denote its status. // CTOrgnum - Orginization number used throughout CTIMS. // //βββββββββββββββββββββββββββββββββββββββββββββββββββββββ ββββββββββββββββββββββ lliiltititiiititttltttitittlϊiiϊttlitiiiiittiititiiϊlilii lititiliiiiiitϊiϊttiii // // CTIMS CallerlD // lltitiititltiiitiiϊttiltiiiitiiiϊϊtitttiiiiiϊiitiliiiltii Ifftttffffffϊffffifϊfϊ class CTCallerlD : public virtual TNull { public: CTCallerlD() : TNull( TRUE ) {} CTCallerID( const char (*str) [CALLERID_SIZE] ) : TNull( FALSE ) { memcpy( value, str, sizeof( value ) ); } CTCallerlD( STRING str ) : TNull( FALSE ) < memset( value, ' ', sizeof( value ) ); memcpy( value, str, strlen( str ) ); } CTCallerlDfii Assign( STRING str ) { setNotNullO ; strncpy( value, str, sizeof( CALLERID_SIZE ) ); return *this; SUBSTITUTE SHEET > operator STRING() const { useAsValue() ; return value; } friend ostreamfii operator « ( ostream fiios , const CTCallerlD fitid ) { if (id.flsNullO) return os « "NULL"; else return os.write( id.value, sizeof( id.value ) ); } friend TStreamfit operator « ( TStreamfii, const CTCallerlDϋ ) ; friend TStreamfii operator » ( TStreamfii, CTCallerlDfit ) ; private: char value[CALLERID_SIZE] ; } ; //fϊlifl«ffflffli«tllfflfffll«ffflll-Cfi«ffMl««llll ffflfifllllϊll-Clflll-tl / / // CTIMS License.Status // lllflffffilfflilffϊlϊi class CTLicStatus : public virtual TNull { public: enum VALUE { UNUSED = 0, NOTEST = 1, ACTIVE = 2, EXPIRED - 3 }; CTLicStatus() : TNull( TRUE ) {} CTLicStatus( VALUE val ) : TNull( FALSE ), value( val {} CTLicStatus( STRING str ) ; CTLicStatusfii operator •*= ( VALUE newval ) { setNotNullO; value = newval; return *this; } CTLicStatusfii operator = ( STRING ) ; operator STRING() const { useAsValue() ; return STR_SET[value]; } SUBSTITUTE SHEET operator VALUE() const { useAsValue(); return value; } FLAG operator •== ( const CTLicStatus fiistatus ) const { useAsValue() ; return FLAG( value == (VALUE)status ); } FLAG operator « ( VALUE val ) const { useAsValue(); return FLAG( value = val ); } friend ostreamfii operator « ( ostream fiios, const CTLicStatus fitlie ) { if (lic.flsNullO) return os « "NULL"; else return os « STRING( lie ) ; } friend TStreamfii operator « ( TStreamfii, const CTLicStatusfii ) ; friend TStreamfii operator » ( TStreamfii, CTLicStatusfii ) ; private: static const char STR_SET[] [CT_TOK_SIZE+l]; VALUE value; }; //llttttttttlttttltlllltlllttttlttlttttlttltlltltltlttlll fffflffϊflffffffϊlffff // // CTIMS Orginization Number. // //ttltltlttltttlttltlllltltltltllttlllttltttiltlttt±tt±tt ttltlltt±tlttllllltlll class CTOrgnum : public virtual TNull { public: CTOrgnum() : TNull( TRUE ) {} FLAG fSetPrefix( STRING ) ; FLAG fSetlndex( UINT ) ; FLAG fGetPrefix( char * ) const; FLAG fGetIndex( UINT &i ) const; FLAG fGeneratePrefix( STRING org_name ) ; operator STRING() const; CTOrgnumδi operator = ( STRING str ) { setNotNullO ; strncpy( value, str, sizeof( value ) ); return *this; } SUBSTITUTE SHEET friend ostreamfii operator « ( ostream fiios, const CTOrgnum &lic ) { if (lic.flsNullO) return os « "NULL"; else return os.write( lie.value, sizeof( lie.value ) ) ; } friend TStreamfii operator « ( TStreamfit, const CTOrgnumδt ) ; friend TStreamfit operator » ( TStreamfit, CTOrgnumfii ) ; private: char value[ORGNUM_SIZE] ; }; //βββββββββββββββββββββββββββββββββββββββββββββββββββββββ ββββββββββββββββββββββ // // CTIMS Records. // // The following types represent records stored in CTIMS. // // //βββββββββββββββββββββββββββββββββββββββββββββββββββββββ ββββββββββββββββββββββ //l«lllfiiMlll-tf«lffiil«ffmilimi«l«lftiiiflilff ffffffttliffϊϊftlϊffϊf / / / / CTIMS MonitorEvent /ltlttlltttltlltttttttltttlttttlttllttltlllltttttttttttt Itltttllltlltltllttlll struct CTMonitorEvent { ULONG CTID; CTTimestamp ServerTS; CTTimestamp ClientTS; CTTimestamp TelcoTS_n; USHORT DurationSec_n; CTCallerlD CallerID_n; USHORT LineNum; CTFlag LogFlag; CTStatus EnvironmentID; USHORT ErrorCnt; friend ostreamfii operator « ( ostreamfii, const CTMonitorEventfii ) ; friend TStreamfit operator « ( TStreamfii, const CTMonitorEventfii ) ; SUBSTITUTE SHEET friend TStreamfit operator » ( TStreamfii, CTMonitorEventfii ) ; }; /pragma pack() /endif // CTIMS_HPP /ifndef CTMESSAGE_HPP /define CTMESSAGE_HPP /include <stddef.h> /include <TStream.HPP> /include <CTIMS.HPP> //******************************************************* ********************** // // //******************************************************* ********************** // CT Message Type Enum. enum CT_MSG_TYPE { QUERY_CTID_STATUS, CTID_STATUS_RESULT, STORE_MONITOREVENT, STORE_RESULT, CLI_QUIT }; inline TStreamfii operator « ( TStream fiibuf, const CT_MSG_TYPE type ) { return buf « USHORT( type ) ; } inline TStreamfii operator » ( TStream fiibuf, CT_MSG_TYPE &type ) { USHORT num; buf » num; type = CT_MSG_TYPE( num ) ; return buf; } // // Header for all CT Messages. // class CTMessageHeader { public: CTMessageHeader() {} CTMessageHeader( ULONG id, CT_MSG_TYPE type, USHORT len ) SUBSTITUTE SHEET : ID( id ) , Type( type ) , Len( len ) {} CT_MSG_TYPE eType() const { return Type; } friend TStreamfii operator « ( TStreamfit, const CTMessageHeaderfii ) ; friend TStreamfii operator » ( TStreamfit, CTMessageHeaderfii ) ; protected: ULONG ID; // The message id number. CT_MSG_TYPE Type; // The event type (see above) . USHORT Len; // The length the message data. }; // // Template for message types. // template < class TText, CT_MSG_TYPE type > class CTMessage : public CTMessageHeader, public TText { public: CTMessage() : CTMessageHeader( 0, type, sizeof( *this ) ) {} CTMessage( const CTMessageHeader fitHeader ) : CTMessageHeader( Header ) { ASSERT( Type == type ) ; } - friend TStreamfit operator « ( TStream fiibuf , const CTMessage< TText, type > fiimsg ) { return buf « * (const CTMessageHeader *) &msg « * ( const TText* ) fiimsg ; } friend TStreamfii operator » ( TStream fiibuf, CTMessage< TText, type > &msg ) { return buf » * (CTMessageHeader*) fiimsg » * (TText* ) fiimsg; } } ; /************************************** // Doesn't seem to work in OS/2 BC++. template < class TText, CT_MSG_TYPE type > SUBSTITUTE SHEET TStreamfii operator « ( TStream fiibuf , const CTMessage< TText, type > fiimsg ) { return buf « * (const CTMessageHeader*) fiimsg « * (const TText*) fiimsg; } template < class TText, CT_MSG_TYPE type > TStreamfii operator » ( TStream fiibuf, CTMessage< TText, type > &msg ) { return buf » * (CTMessageHeader*) &msg » * (TText*) fiimsg; } **************************************/ // // CT Message structures. // struct QueryCTIDStatus { ULONG CTID; friend TStreamfii operator « ( TStreamfii, const QueryCTIDStatus& ) ; friend TStreamfit operator » ( TStreamfii, QueryCTIDStatusfii ) ; }; inline TStreamfit operator « ( TStream fitbuf, const QueryCTIDStatus &rec ) { return buf « rec. CTID; } inline TStreamfii operator » ( TStream fiibuf , QueryCTIDStatus fiirec ) { return buf » rec. CTID; } struct CTIDStatusResult { FLAG QueryResuIt; ULONG CTID; CTLicStatus Status; ULONG PeriodDays; ULONG PeriodMinutes; CTFlag StolenFlag; ULONG SpecialProcess; CTTimestamp LastCallTS_n; CTTimestamp NextCallTS^n; CTTimestamp NextCallClTentTS_n; CTOrgnum Orgnum_n; CTStatus ProductType; SUBSTITUTE SHEET friend TStreamfii operator « ( TStreamfii, const CTIDStatusResultfit ) ; friend TStreamfit operator » ( TStreamfii, CTIDStatusResultfit ) ; }; inline TStreamfii operator « ( TStream fitbuf, const CTIDStatusResult &rec ) { return buf « rec.QueryResult « rec.CTID « rec.Status « rec.PeriodDays « rec.PeriodMinutes « rec.StolenFlag « rec.SpecialProcess « rec.LastCallTS_n « rec.NextCallTS n « rec.NextCallClTentTS_n « rec.Orgnum_n « rec.ProductType; } inline TStreamfit operator » ( TStream fiibuf, CTIDStatusResult &rec ) { return buf » rec.QueryResult » rec.CTID » rec.Status » rec.PeriodDays >> rec.PeriodMinutes » rec.StolenFlag » rec.SpecialProcess » rec.LastCallTS_n » rec.NextCallTS n » rec.NextCallClTentTS_n >> rec.Orgnum_n » rec.ProductType; } struct StoreMonitorEvent : public CTMonitorEvent { // Control. FLAG StoreAsStolen; FLAG StoreAsExpire; // Data. CTLicStatus Licenses' tus; CTTimestamp NextCalll, * n; CTTimestamp NextCallClTentTS_n; friend TStreamfii operator « ( TStreamfit, const StoreMonitorEventfit ) ; friend TStreamfii operator » ( TStreamfit, StoreMonitorEventfit ) ; SUBSTITUTE SHEET } ; inline TStreamfii operator « ( TStream fiibuf , const StoreMonitorEvent &rec ) { return buf « rec.StoreAsStolen « rec.StoreAsExpire « rec.LicenseStatus « rec.NextCallTS n « rec.NextCallClTentTS_n « (const CTMonitorEventfii)rec; } inline TStreamfii operator » ( TStream fitbuf, StoreMonitorEvent &rec ) { return buf » rec.StoreAsStolen » rec.StoreAsExpire » rec.LicenseStatus » rec.NextCallTS^n >> rec.NextCallClTentTS_n >> (CTMonitorEventfii)rec; } struct StoreResult { FLAG Result; friend TStreamfii operator « ( TStreamfii, const StoreResultfit ) ; friend TStreamfit operator » ( TStreamfii, StoreResultfit ) ; }; inline TStreamfii operator « ( TStream fiibuf, const StoreResult &rec ) { return buf « rec.Result; } inline TStreamfii operator » ( TStream fiibuf , StoreResult fiirec ) { return buf » rec.Result; } struct CliQuit { friend TStreamfii operator « ( TStream fitbuf, const CliQuit& ) { return buf; } friend TStream& operator » ( TStream fiibuf, CliQuitfit ) { return buf; } }; typedef CTMessage< QueryCTIDStatus, QUERY_CTID_STATUS > QueryCTIDStatusMsg; SUBSTITUTE SHEET typedef CTMessage< CTIDStatusResult, CTID_STATUS_RESULT > CTIDStatusResultMsg; typedef CTMessage< StoreMonitorEvent, STORE_MONITOREVENT > StoreMonitorEventMsg; typedef CTMessage< StoreResult, STORE_RESULT > StoreResultMsg; typedef CTMessage< CliQuit, CLI_QUIT > CliQuitMsg; /endif // CTMESSAGE_HPP /ifndef DB_OBJECTS_HPP /define DB_OBJECTS_HPP /include <DB. H> /define DB_NULL -1 /define DB_NOT_NULL 0 /define DB_ISNULL ( n ) (FLAG ( (n) < 0 ) ) class DataBase { PCSZ name; public: DataBase() { fInitDB() ; } DataBase( PCSZ db__name ) : name( db_name ) { fInitDB() ; } void SetName( PCSZ str ) { name =■ str; } FLAG fConnect() { return fConnectDB( name ) ; } ULONG ulSQLCode() const { return ulGetSQLCode() ; } void Commit() { CommitWork() ; } void Rollback() { RollbackWork() ; } }; /endif // DB_OBJECTS_HPP /ifndef MESSAGEPIPE_HPP /define MESSAGEPIPE_HPP /include <debug.h> /include <usertype.h> /include <TStream.HPP> //******************************************************* ********************** // MsgPipeFactory - Factory to create MessagePipe instances. // Each MessagePipe instance represents a connection between a // client and the server. // SUBSTITUTE SHEET // *** PUBLIC INTERFACE *** // // FLAG fCreatePipe( MessagePipefit *pιpe ) // Description: // Creates a MessagePipe instance and returns a pointer to it (via pipe) . // Returns: // TRUE if the operation is successful. // FALSE if the operation fails. // // FLAG fDestroyPipe( MessagePipe *pipe ) // Description: // Destroys the MessagePipe instance pointed to by pipe. // Returns: // TRUE if the operation is successful. // FALSE if the operation fails. // // *** PROTECTED INTERFACE *** // // virtual void InitPipe( MessagePipe *pipe ) // virtual void DeinitPipe( MessagePipe *pipe ) // Description: // Called by the constructor or destructor of MessagePipe respectively. // Manages any internal work needed to support an MessagePipe instance. // // virtual FLAG fOpenPipe( MessagePipe* ) // virtual FLAG fClosePipe( MessagePipe* ) // Description: // Called by MessagePipe: :fOpenPipe and MessagePipe: :fClosePipe. This // in turn allocates/deallocated a pipe using the needed OS API calls. // //******************************************************* ********************** class MsgPipeFactory { friend class MessagePipe; public: MsgPipeFactory( UINT msg_len ) : max_msg_len( msg_len ) , rc( 0 ) {} virtual -MsgPipeFactory() {} virtual FLAG fCreatePipe( MessagePipe*& ) = 0; virtual FLAG fDestroyPipe( MessagePipe* ) = 0; UINT uMaxMsgLen() const { return max_msg_len; } APIRET rcDosErrorCode() const { return re; } SUBSTITUTE SHEET protected : virtual void InitPipe( MessagePipe* ) {} virtual void DeinitPipe( MessagePipe* ) {} virtual FLAG fOpenPipe( MessagePipe* ) = 0; virtual FLAG fClosePipe( MessagePipe* ) = 0; APIRET re; private: UINT max_msg_len; }; //******************************************************* ********************** // SvrMsgPipeFactory - Factory to create MessagePipe instances from the // Server process. // // See MsgPipeFactory. // //***********★******************************************* ********************** class SvrMsgPipeFactory : public MsgPipeFactory { public: SvrMsgPipeFactory( PCSZ pipe_name, UINT max_msg_size, UINT max_msg_num ) ; -SvrMsgPipeFactory() {} FLAG fCreatePipe( MessagePipe*& ),* FLAG fDestroyPipe( MessagePipe* ); protected: // void InitPipe( MessagePipe* ); // void DeinitPipe( MessagePipe* ); FLAG fOpenPipe( MessagePipe* ) ; FLAG fClosePipe( MessagePipe* ) ; private: PCSZ pipe_name; UINT pipe_len; }; //******************************************************* ********************** // CltMsgPipeFactory - Factory to create MessagePipe instances from the // Client process. SUBSTITUTE SHEET // // See MsgPipeFactory. // //******************************************************* ********************** class CltMsgPipeFactory : public MsgPipeFactory { public: CltMsgPipeFactory( PCSZ pipe_name, UINT max_msg_size ); -CltMsgPipeFactory() {} FLAG fCreatePipe( MessagePipe*& ); FLAG fDestroyPipe( MessagePipe* ) ; protected: // void InitPipe( MessagePipe* ); // void DeinitPipe( MessagePipe* ) ; FLAG fOpenPipe( MessagePipe* ) ; FLAG fClosePipe( MessagePipe* ); private: PCSZ pipe_name; } ; //******************************************************* * ********** *********** // Class MessagePipe - Implements a message pipe connection between the client // and the server. This same class is used for both the client and the // server sides. MsgPipeFactory is used to hide the connection differences. // // FLAG fOpenPipe() // FLAG fClosePipe() // Description: // Called to open/close a valid connection between the client and the // server, fOpenPipe must be called before any data can be transfered. // // FLAG fSendMessage( PCVOID msg, ULONG msg_len ) // Description: // Sends msg[msg_len] through the pipe as raw data. // Returns: TRUE = success; FALSE = failure. // // FLAG fGetMessage( PVOID msg, PULONG msg_len ) // Description: // Receives up to msg_len byte into msg. Does not return until a message SUBSTITUTE SHEET // is recieved. // Returns: TRUE = success; FALSE = failure. // // FLAG fTransact( PCVOID out_msg, ULONG out_msg_len, PVOID in_msg, // PULONG in_msg_len ) // Description: // Sends out_msg and then receives in_msg. Does not return until a // message has been received. // Returns: TRUE = success; FALSE = failure. // // PIPE_STATE estate() // Returns: // The current state of the pipe: // DISCONNECTED - the pipe is not connected to another process. // LISTENING - the pipe is waiting for the two sides to connect. // CONNECTED - the pipe is connected; data transfer is allowed. // CLOSING - pipe is waiting for one side to acknowledge closure. // // UINT uMaxMsgLen() const // Returns: // The maximun message length that can be sent or received. // // APIRET rcDosErrorCode() const // Returns: // The OS API return code of the last API operation. Commonly used // to determine the type of error once a FALSE has been returned by // one of the member functions above. // //******************************************************* ********************** class MessageBuffer; // Forward declaration. class MessagePipe { friend class SvrMsgPipeFactory; friend class CltMsgPipeFactory; MessagePipe( MsgPipeFactory* ) ; -MessagePipe() ; public: // Pipe state enum. Fixed numbers are set to match API state (see impementation) ! enum PIPE_STATE { DISCONNECTED ■ 1, SUBSTITUTE SHEET LISTENING = 2, CONNECTED = 3, CLOSING = 4 }; FLAG fOpenPipe() ; FLAG fClosePipe() ; FLAG fSendMessage( PCVOID msg, ULONG msg_len ); FLAG fGetMessage( PVOID msg, PULONG msg_len ); FLAG fTransact( PCVOID out_msg, ULONG out_msg_len, PVOID in_msg, PULONG in_msg_len ) ; FLAG fSendMessage( TStreamfii ) ; FLAG fGetMessage( TStream& ) ; FLAG fTransact( TStream &out, TStream &in ) ; PIPE_STATE estate() ; UINT uMaxMsgLen() const { return factory- >uMaxMsgLen() ; } APIRET rcDosErrorCode() const { return re; } protected: void SetHandle( HPIPE h ) { hPipe = h; } HPIPE GetHandle() const { return hPipe; } private: MsgPipeFactory *factory; HPIPE hPipe; APIRET re; }; //******************************************************* ********************** // // MessagePipe inline members. // inline FLAG MessagePipe: :fSendMessage( TStream fiistream ) { return fSendMessage( stream.buffer, stream.iptr - stream.buffer ) ; } inline FLAG MessagePipe::fGetMessage( TStream fitstream ) { ULONG get_len = stream.buf_len; if (fGetMessage( stream.buffer, &get_len )) { stream.iptr = stream.buffer + get_len; return TRUE; } else return FALSE; SUBSTITUTE SHEET } inline FLAG MessagePipe: :fTransact( TStream &out_strm, TStream &in_strm ) { ULONG get_len = in_strm.buf_len; if (fTransact( out_strm.buffer, out_strm.iptr - out_strm.buffer, in_strm.buffer, &get_len )) { in_strm.iptr « in_strm.buffer + get_len; return TRUE; } else return FALSE; } /endif // MESSAGEPIPE_HPP /ifndef OBJECTS_HPP /define OBJECTS HPP /include <iomanip.h> /include <TObject.HPP> llfllflffliflflllfllfl // // TFlag (used for boolean fields such as StolenFlag and InsuredFlag) . // // TFlag - sets initial value. // // typecast operators: // FLAG - throws exception if NULL. // STRING - throws exception if NULL. // // assignment operators: // // // comparison operators: // == - boolean compare, throws exception if NULL. // != // // iostream operators (friend operators) . // « // » // //ffffflfffffflffflilffffffϊffffffflffffffffiftffffftffff tffftfflϊfffϊfffffϊflf /define NULL_TOK "NULL" /define TRUE TOK "Y" /define FALSE_TOK "N" class TFlag : public virtual TNull { SUBSTITUTE SHEET public: TFlag ( ) ; TFlag( FLAG flag ) ; -TFlag() ; virtual void SetDefault() ; TFlagfii Assign( const TFlagfii ); TFlagfii Assign( FLAG ); operator FLAG() const; operator FLAG() { return value; } operator STRING() const; TFlagfii operator = ( FLAG ) ; TFlagfii operator = ( const TFlagfii ) ; FLAG operator •== FLAG operator •== FLAG operator •== FLAG operator != FLAG operator !•*•= FLAG operator != friend ostreamfii operator « ( ostreamfii, const TFlagfii ) ; friend istreamfii operator » ( istreamfii, TFlagfii ) ; friend TStreamfit operator « ( TStreamfii, const TFlagfii ) ; friend TStreamfii operator » ( TStreamfit, TFlagfii ) ; // • PRIVATE IMPLEMENTATION protected : FLAG value; } ; //lllllflfl-tll-tlll-ClllϊϊlllfimilfMllllϊlϊlllfMMlMlf flflffiflfffftifflfiff // // TTimestamp // // fValidate - returns TRUE if object contains a valid timestamp. // ForceValidate - sets value to a known valid value. // ToSTRING - converts timestamp to a string representation: " YYY-MM-DD-HH.mm.ss.uuuuuu". // static flsValidTSString - checks a string to verify it's a valid timestamp. // UINT TSStringLen - value equals the length of the string representation of a timestamp. SUBSTITUTE SHEET manipulator operators : += typecase operators: STRING ison operators: llffff«l-tflfllf«-tfflff«liiflιlf«lll«flf ffff-Ciiiimiiiif-tff struct TTimestamp : public virtual TNull { TTimestamp() ; TTimestamp( USHORT yr, UCHAR mo, UCHAR dy, UCHAR hr = 0, UCHAR mn = 0, UCHAR sc - 0, USHORT ms = 0 ) ; -TTimestamp() ; FLAG fValidate() const; void ForceValidate() ; STRING ToSTRING( char * ) const; virtual void SetDefault() ; static FLAG fIsValidTSString( STRING ); static const UINT TSStringLen; TTimestampfii Assign( const TTimestampfii ); TTimestampfii Assign( USHORT, UCHAR, UCHAR, UCHAR - 0, UCHAR = 0, UCHAR - 0, USHORT = 0 ) ; TTimestampfii Assign( STRING, FLAG isnull = FALSE ); /ifdef OS2 TTimestampfii Assign( const DATETIME& ) ; /endif // *** manipulator operators TTimestampfii operator = ( const TTimestampfii ) ; /ifdef OS2 TTimestampfit operator «= ( const DATETIME& ) ; /endif operator += ( const TTimestampfii ) ; // *** typecast opertors SUBSTITUTE SHEET operator STRING ( ) const; // *** accessors USHORT usYear() const; USHORT usMonth() const; USHORT usDay() const; USHORT usHour() const; USHORT usMinute() const; USHORT usSecond() const; USHORT usMillisec() const; // *** comparison operators FLAG operator == const TTimestamp fiits const; FLAG operator !•= const TTimestamp fiits const; FLAG operator < const TTimestamp &ts const; FLAG operator > const TTimestamp &ts const; FLAG operator < β const TTimestamp &ts cons ; FLAG operator >= const TTimestamp fiits const; FLAG operator =•*** ( STRING ) const; friend ostreamfii operator « ( ostreamfii, const TTimestampfii ) ; friend TStreamfii operator « ( TStreamfit, const TTimestampfii ) ; friend TStreamfii operator » ( TStreamfii, TTimestampfii ) ; TTimestampfii AddToDate( UINT yr, UINT mon, UINT day, UINT hr = 0, UINT min ■ o, UINT sec = 0, UINT ms = 0 ) ; // Class properties. static FLAG fIsLeapYear( USHORT year ) ; static USHORT usMaxMonth() ; static USHORT usMaxDay( USHORT year, USHORT month ); static USHORT usMaxHour(); static USHORT usMaxMinute() ; static USHORT usMaxSecondO ; static USHORT usMaxMillisecO ; PROTECTED IMPLEMENTATION protected: USHORT Year; UCHAR Month; UCHAR Day; UCHAR Hour; UCHAR Minute; UCHAR Second; USHORT Millisec; } ; SUBSTITUTE SHEET ffllϊlflfffflllfffffff // // TString llflilllϊllfflfillfllt class TString { }; /include <Objects.INL> /endif // OBJECTS_HPP /ifndef POINTER_HPP /define POINTER_HPP template <class T> class TPointer { TPointer() ; FLAG operator !() const { return fIsNull(); } operator const T& const { return useAsRValue() ; } operator T& { return useAsLValue() ; } const T& operator () const { return useAsRValue() ; } T& operator () { return useAsLValue() ; } const T* operator -> const { return &useAsRValue() ; } T* operator -> { return fiiuseAsLValue() ; } const T& operator * const { return useAsRValue() ; } T& operator * { return useAsLValue() ; } // operator = () { private: T *ptr; }; /endif POINTER_HPP /ifndef BITFLAGS_HPP /define BITFLAGS_HPP /include <TStream.HPP> template <class Enum> class TBitflag { public: SUBSTITUTE SHEET TBitflag( Enum ); TBitflagO; Enum Assign( Enum ),* Enum Set( Enum ) ; Enum Clear( Enum ) ; Enum Change( Enum mask, Enum setting ); FLAG fIsSet( Enum ) const; FLAG fIsClear( Enum ) const; FLAG fIsAnySet( Enum ) const; FLAG fIsAnyClear( Enum ) const; Enum operator = ( Enum ) ; operator ULONG () const; operator Enum () const; friend TStreamfit operator « ( TStreamfii, const TBitflag<Enum>& ) ; friend TStreamfii operator » ( TStreamfii, TBitflag<Enum>& ) ; private: ULONG flags; }; template <class Enum> TBitflag<Enum>: :TBitflag( Enum e ) : flags( e ) {} template <class Enum> TBitflag<Enum>: :TBitflagO { /ifdef DEBUG flags ■ UNINITJDATA; /endif } template <class Enum> inline Enum TBitflag<Enum>::Assign( Enum e ) { return Enum( flags = e ); } template <class Enum> inline Enum TBitflag<Enum>: :Set( Enum e ) { return Enum( flags |= e ); } template <class Enum> inline Enum TBitflag<Enum>: :Clear( Enum e ) { return Enum( flags &= -((ULONG)e) ); SUBSTITUTE SHEET } template <class Enum> inline Enum TBitflag<Enum>: :Change( Enum mask, Enum settings ) { return Enum( flags = (flags & -mask) | (settings & mask) ) ; } template <class Enum> inline FLAG TBitflag<Enum>: :fIsSet( Enum e ) const { return FLAG( (flags & e) == e ) ; } template <class Enum> inline FLAG TBitflag<Enum>: :fIsClear( Enum e ) const { return FLAG( (flags & e) == 0 ) ; } template <class Enum> inline FLAG TBitflag<Enum>: :fIsAnySet( Enum e ) const { return !fIsClear( e ) ; } template <class Enum> inline FLAG TBitflag<Enum>: :fIsAnyClear( Enum e ) const { return !fIsSet( e ); } template <class Enum> inline Enum TBitflag<Enum>::operator = ( Enum e ) { return Assign( e ); } template <class Enum> inline TBitflag<Enum>: :operator ULONG () const { return flags; } template <class Enum> inline TBitflag<Enum>: :operator Enum () const { return (Enum)flags; } template <class Enum> inline TStreamfit operator « ( TStream &str, const TBitflag<Enum> &bf ) { return str « bf.flags; SUBSTITUTE SHEET } template <class Enum> inline TStreamfit operator » ( TStream fiistr, TBitf lag<Enum> fiibf ) { return str » bf.flags; } /endif // BITFLAGS_HPP /ifndef TBUFFER_HPP /define TBUFFER_HPP /include <iostream.h> /include <debug.h> /include <TBitflag.HPP> /include <TStream.HPP> /define BUFFER_UNIT 16 // // class TBaseBuffer - implements a simple variable length memory block. // class TBaseBuffer { public: TBaseBuffer() ; TBaseBuffer( UINT bufsize ); -TBaseBuffer() ; BYTE* Buf() ; const BYTE* Buf() const; FLAG fRealloc( UINT new_size ) ; friend TStreamfii operator « ( TStreamfii, const TBaseBufferfit ) ; friend TStreamfii operator » ( TStreamfit, TBaseBufferfii ) ; protected: TBaseBuffer( const TBaseBufferfii ) ; // Copy constructor. static UINT alloc_limit( UINT ); // Given a number, returns a valid adjustment. BYTE* _buf() { return buffer; const BYTE* _buf() const { return buffer; BYTE* _newBuf( UINT new_limit ); SUBSTITUTE SHEET II- private implementation private: BYTE ♦buffer- // Beginning of buffer. UINT limit; // Current allocated buffer size. inline BYTE* TBaseBuffer: :Buf() return buffer; inline const BYTE* TBaseBuffer: :Buf() const return buffer; // // class TBuffer - implements a sofisticated memory block. // includes reference counting, operators, generic properties, etc. // class TBuffer : private TBaseBuffer { public: // Type for properties of TBuffer. enum PROPS { DEFAULT - 0, FIXED = 0x00000001, // Lock the size of the buffer. READONLY ■ 0x00000002, // Block any attempt to modify. SHARED = 0X00000004, // Changes to this string are shared by all. USER1 = 0x01000000, // User settings for general use. USER2 •= 0X02000000, USER3 - 0x04000000, USER4 ■ 0x08000000, USER5 = 0x10000000, USER6 = 0x20000000, USER7 = 0X40000000 // USER8 = 0x80000000 // Too big??? (give compiler error with CSet++ 2.1) >; typedef TBitflag< PROPS > TProps; SUBSTITUTE SHEET // Construction/Destruction. TBuffer( PROPS - DEFAULT ) ; TBuffer( UINT length, PROPS = DEFAULT ) ; TBuffer( TBufferfii ) ; // copy constructor. -TBuffer() ; // Attribute access. UINT uLength() const; // Returns the length of the buffer. FLAG fResize( UINT new_size ); // Shink or grow to a new size, returns TRUE if successful. void Resize( UINT new_size ); // Throws an exception if fails. const BYTE* Buf() const; // Read-only access to data. BYTE* Buf() ; // Access to data, throws exeption if READONLY or !SHARED && ref_c > 1. const BYTE* Buf( UINT index ) const;// Returns Buf() + index, checks range. BYTE* Buf( UINT index ) ; // Returns Buf() + index, checks range. // Reference counting. UINT uRef() ; // Add a reference. UINT uDeref() ; // Remove a reference. UINT uRefCount() const; // Return the reference count. TBufferfii PrepareToChange() ; // Makes a copy of needed (if COPYMOD=l) TBufferfii Copy() ; // Makes a new copy of this TBuffer. // Generic property interface. FLAG fQueryProperty( PROPS ) const; // Returns TRUE if specified props are set. PROPS SetProperty( PROPS ) ; // Sets specified props. PROPS ClearProperty( PROPS ) // Clears specified props. // Specific propery interface. FLAG fQueryReadOnly() const; // TRUE if this buffer is read-only. FLAG fSetReadOnly( FLAG setting FLAG fQueryFixed() const; // TRUE if this buffer's length is fixed. FLAG fSetFixed( FLAG setting ) ; FLAG fQueryShared() const; // TRUE if this buffer's value is shared. FLAG fSetShared( FLAG setting ) ; // String functions. SUBSTITUTE SHEET TBufferfii StrCopy( const TBuffer& ) ; TBufferfii StrCopy( STRING ); TBuffer& strConcat( const TBufferfii ) ; TBufferfii StrConcat( STRING ) ; TBufferfii StrTrunc( UINT index ) ; TBufferfii StrGrow( UINT index ); TBufferfii StrGrow( UINT index, BYTE pad ); // stream operators. friend TStreamfii operator « ( TStreamfii, const TBufferfii ); friend TStreamfii operator » ( TStreamfit, TBufferfii ); friend ostreamfii operator «( ostream fiios, const TBuffer &Buf ) ; II- protected implementation protected: // direct buffer manipulation functions. TBuffer& _strCopy( const TBufferfii ) ; TBufferfii _strCopy( STRING ) ; TBufferfii _strConcat( const TBufferfii ) TBufferfii _strConcat( STRING ) ; TBufferfii _strTrunc( UINT index ) ; TBufferfii _strGrow( UINT index ) ; // Grows buffer (pads with eos) . TBufferfii _strGrow( UINT index, BYTE pad ) // Grows and pads buffer. // private implementation private: // static TBufferHeap *heap; // Manages all TBuffers. UINT length; // Length of allocated data (actual buffer is // guaranteed 1 byte larger for eos) . UINT ref_c; // Reference Count. TProps props; // Attribute properties. }; /include <TBuffer.INL> /endif // TBUFFER_HPP /ifndef TMSG HPP /define TMSG~HPP typedef ULONG MSG_ID; enum MSG TYPE // Derived event classtype. SUBSTITUTE SHEET { TSYSMSG, // TSysMsg type. TOBJMSG // TObjMsg type. } // // TMessageHandlerObject - Abstract base class for TMessage aware objects. // AKA - TMsgHObj. // class TMessageHandlerObject { public: friend class TMessage; typedef FLAG (TMessageHandlerObject::* fHandleMessage) ( TMessage * ) ; protected: virtual FLAG handleMessage( TMessage* ) = 0; virtual FLAG postMessage ( TMessage* ) = 0; }; typedef TMessageHandlerObject TMsgHObj; // Define synonym. // // TMessage - Abstract base class for all messages. // class TMessage { public: enum STATE { PRODUCED, POSTED, PENDING, EXECUTING, CONSUMED }; TMessage( TMsgHObj *source, MSG_ID id, PVOID data ); virtual -TMessage() ; // Message Properties. virtual const TMsgHObj* Source() const { return source; } virtual const STATE State() const { return state; } virtual const MSG_ID Id() const { return id; } virtual const MSG_TYPE Type() const = 0; SUBSTITUTE SHEET virtual PVOID Data() { return data; } // Message Methods. virtual FLAG fSend() = 0; protected: STATE state; private: TMsgHObj *source; MSG_ID id; PVOID data; }; // // // class TSysMsg : public TMessage { public: static void SetSystemHandler( TMsgHObj *syshnd ) { system_handler = syshnd; } TSysMsg( TMsgHObj *source, MSG_ID id, PVOID data ); virtual const MSG_TYPE Type() const { return TSYSMSG; } virtual FLAG fSend(); private: static TMsgHObj *system_handler; >; class TObjMsg : public TMessage { public: TObjMsg( TMsgHObj *source, TMsgHObj *target, MSG_ID id, PVOID data ) ; virtual const MSG_TYPE Type() const { return TOBJMSG; } virtual FLAG fSend() ; private: TMsgHObj *target; }; class TEMode : public TModem, public TMessageHandlerObject { public: SUBSTITUTE SHEET FLAG handleMessage( TMessage* ) ; FLAG postMessage ( TMessage* ) ; }; FLAG TEModem: :handleMessage( TMessage *event ) { if (event->Source() == &Port()) { if (fResultReceived() ) { TModemMessage event = new TModemMessage( this, rcResultCode() ) ; event->fSend( Mode Handler ); // —> ModemHandler.handleMessage( event ); } } else return FALSE; } FLAG TEConnect: :handleModemMessage( TModemMessage *event ) { if (event->ResultCode() == TModem: :CONNECT) { waitForEnqO ; return TRUE; } } /endif // TMSG_HPP /ifndef TEXCEPTION_HPP /define TEXCEPTION HPP /include <iostream.h> /include <usertype.h> typedef ULONG ERROR_ID; /define EXP_STRLIST_SIZE 10 class TException { public: • enum SEVERITY { UNRECOVERABLE, RECOVERABLE }; TException( STRING string, ERROR_ID id - 0, SEVERITY = UNRECOVERABLE ) ; TException( const TExceptionfii ) ; -TException() ; TExceptionfii AddString( STRING error_str ) ; SUBSTITUTE SHEET // TExceptionfii AppendString( STRING error_str ); TExceptioni SetSeverity( SEVERITY sev ) { severity = sev; return *this; } TExceptionfii SetErrorId( ERROR_ID id ) { error_id - id; return *this; } virtual FLAG fIsRecoverable() const { return severity — RECOVERABLE; } virtual STRING GetName() const { return "TException"; } STRING GetString( UINT i =- 0 ) const { return strlist[i]; } UINT uGetStringCount() const { return str_count; } ERROR_ID GetErrorId() const { return error_id; } private: STRING Strlist[EXP_STRLIST_SIZE] ; UINT str_count; ERROR_ID error ^ id; SEVERITY severity; }; ostreamfii operator « ( ostreamfii, const TExceptionfii ) ; /endif // TEXCEPTION_HPP /ifndef TMESSAGE_HPP /define TMESSAGE HPP /include <usertype.h> typedef ULONG MSG_ID ; enum MSG_TYPE // Derived event classtype. { TSYSMSG, // TSysMsg type. TOBJMSG, // TObjMsg type. TSPECMSG // TSpecMsg type. }; //= // // TMessageHandlerObject - Abstract base class for TMessage aware objects. // AKA - TMsgHObj. // class TMessageHandlerObject { public: friend class TMessage; typedef FLAG (TMessageHandlerObject: :*HANDLER) ( TMessage* ) ; SUBSTITUTE SHEET FLAG fHandleMessage( TMessage* ) ; // Front-end for virtual function handlerMessage() private: virtual FLAG handleMessage( TMessage* ) - 0; // Should'nt call directly (call fHandleMessage() instead). }; typedef TMessageHandlerObject TMsgHObj; // Define synonym. I I'¬ ll 1/ TMessage - Abstract base class for all messages. // class TMessage { public: enum STATE { PRODUCED, // Message has been created but not used. PENDING, // Message has been sent and is pending execution. EXECUTING, // Message has been sent and is being executed. CONSUMED // Message was consumed and can be destroyed. }; TMessage( TMsgHObj *source, MSG_ID id, PVOID data ); virtual -TMessage(); // Message Properties. virtual const TMsgHObj* Source() const { return source; } virtual const STATE State() const { return state; } virtual const MSG_ID Id() const { return id; } virtual const MSG_TYPE Type() const = 0; virtual PVOID Data() { return data; } // Message Methods. FLAG fSend() ; // Front-end to the send() virtual function. // State changes. void StateToPending() { state ■ PENDING; } void StateToExecute() { state = EXECUTING; } void StateToConsumed() { state = CONSUMED; } private: virtual FLAG send() = 0; // Should not call directly (call fSend() instead) . SUBSTITUTE SHEET STATE state; TMsgHObj *source; MSG_ID id; PVOID data; }; // // // class TSysMsg : public TMessage { public: static void SetSystemHandler( TMsgHObj *syshnd ) { system_handler «= syεhnd; } TSysMsg( TMsgHObj *source, MSG_ID id, PVOID data ); virtual const MSG_TYPE Type() const { return TSYSMSG; } private: virtual FLAG send(); static TMsgHObj *system_handler; }; // // // class TObjMsg : public TMessage { public: TObjMsg( TMsgHObj *source, TMsgHObj *target, MSG_ID - 0, PVOID data = NULL ); virtual const MSG_TYPE Type() const { return TOBJMSG; } protected: virtual FLAG send(); TMsgHObj *target; }; class TSpecMsg : public TObjMsg { public: TSpecMsg( TMsgHObj *src, TMsgHObj *trt, TMsgHObj: :HANDLER, MSG_ID = 0, PVOID data = NULL ); virtual const MSG_TYPE Type() const { return TSPECMSG; } private: virtual FLAG send() ; SUBSTITUTE SHEET TMsgHObj::HANDLER handler; }; /************************************** class TEModem : public TModem, public TMessageHandlerObject { public: FLAG handleMessage( TMessage* ) ; FLAG postMessage ( TMessage* ) ; }; FLAG TEModem: :handleMessage( TMessage *event ) { if (event->Source() == fiiPort()) { if (fResultReceived()) { TModemMessage event = new TModemMessage( this, rcResultCode() ) ; event->fSend( ModemHandler ) ; // —> ModemHandler.handleMessage( event ); } } else return FALSE; } FLAG TEConnect: :handleModemMessage( TModemMessage *event ) { if (event->ResultCode() == TModem: :CONNECT) { waitForEnq() ; return TRUE; } } **************************************/ /endif // TMESSAGE_HPP /ifndef TMODEM_HPP /define TMODEM_HPP /include <TPort.HPP> iiϊliitiltiitittiiϊtittitiitϊiiiiltiiilittitilitiiiilttit lllttllltttltllt±lllll II II class TModem - // // // llttlttiiitiltiiilititilititiiiittϊititiitittϊitliitltili flffliflϊffϊfffltffffi class TModem { SUBSTITUTE SHEET public: }; TModem( TPort fiiport ) ; FLAG fSendCommand( STRING ) ; FLAG fResultReceived() ; RC rcResultCode() const; STRING strResultCode() const; RC rcSendCommand( STRING, ULONG timeout ); STRING strSendCommand( STRING str, ULONG timeout STRING strGetString( ULONG timeout ) ; const TPortfii Port() const { return port; } TPort& Port() { return port; } /ifndef _THREADS void ManageEvents() ; // For single threaded usage, /endif // PRIVATE IMPLEMENTATION private: TPortfii port; char last_command[80]; char last_result[80] ; RC last re; SUBSTITUTE SHEET /endif // TMODEM_HPP /ifndef TOBJECT_HPP /define TOBJECT_HPP /include <usertype.h> /include <debug.h> /include <TStream.HPP> /include <iostream.h> //βββββββββββββββββββββββββββββββββββββββββββββββββββββββ ββββββββββββββββββββββ // // CTIMS Root types. // // These types are used by derivation only. They are not meant to be // implemented. // // TObject - // TNull - // //βββββββββββββββββββββββββββββββββββββββββββββββββββββββ ββββββββββββββββββββββ lltiϊitttitiiiiiiittilitiitittliitilittittiiittiϊtttltϊϊi ϊititϊiilittitiiiitϊtt II // Object root class. // llitiiitiϊiiiiitiiiiiiiiiiittiililtiiittiiiltitltltlilitt tiititittitiϊiiϊtϊtili class TObject { public: virtual -TObject(); // ... // not implemented. // ... }; //tltllllltlllltlttllltlllttttlltttt±tttllttlttllttlttlll tlttllltltlltttllttltt // I / CTIMS Nullable Object root class. // // Public interface: // // TNull - sets initial value. // TRUE - object is NULL. // FALSE = object has a value. // flsNull - returns TRUE if NULL. SUBSTITUTE SHEET // fSetNull - sets object to NULL. // fSetDefault - sets object to its default value. (pure virtual) . // // operator ! - returns TRUE if object is NULL. fffllilfϊϊlifflflilϊff class TNull : public virtual TObject { public: TNull( FLAG isnull = TRUE ); FLAG fIsNullO const; virtual FLAG fSetNull(); virtual void SetDefault() {} // This should be pure virtual!!! FLAG operator ! () const; friend TStreamfii operator << ( TStreamfit, const TNullfii ); friend TStreamfii operator » ( TStreamfit, TNullfii ); // PR O TECTED IMPLEMENTATION protected: virtual void setNotNullO; // called when used as an L-Value. virtual void useAsValue() const; // called when used as an R-Value. // PRIVATE IMPLEMENTATION private: FLAG isnull; }; /include <TObject.INL> /endif // TOBJECT_HPP /ifndef TPOINTER_HPP /define TPOINTER_HPP /include <debug.h> template <class T> class TPointer { public: TPointer() : ptr( NULL ) {} TPointer( T *pt ) : ptr( pt ) {} SUBSTITUTE SHEET FLAG operator !() const { return ptr »•= NULL; } operator const T* () const { return useAsRValueO ; } operator T* () { return useAsLValue() ; } const T* operator -> () const { return useAsRValue() ; } T* operator -> () { return useAsLValue() ; } const T& operator * () const { return ♦useAsRValue() ; } T& operator * () { return *useAsLValue() ; } TPointer& operator = ( T* pt ) { ptr = pt; return *this; } FLAG operator =-= ( PVOID p ) const { return (PVOID)ptr == p; } FLAG operator != ( PVOID p ) const { return ! (*this == p); } protected: const T* useAsRValueO const { ASSERT( ptr != NULL ); return ptr; } T* useAsLValue() { ASSERT( ptr != NULL ); return ptr; } private: T *ptr; }; /endif // TPOINTER_HPP /ifndef TPORT_HPP /define TPORT_HPP /include <debug.h> /ifdef OS2 /define _THREADS /include "CT_Buffer.HPP" /include "CT_Log.HPP" /endif /ifdef Windows // windows includes here, /endif iiϊitlttiltltitϊttiltllllitttlttltittϊiilltttillittilittt lϊϊtϊtitilϊitiiitiittt II II TPort - implements a com port as an object. // SUBSTITUTE SHEET // fOpenPort - opens the port, initializes it to desired settings. // fClosePort - closes the port. // // FlushlnputBuffer - flushes the input buffer. // FlushOutputBuffer - flushes the output buffer. // flsEmpty - returns TRUE if the Ports buffers are empty. // flsFull - returns TRUE if the Ports buffers are full. // // fGetChar - gets a character from the input buffer. // fPutChar - puts a character into the output buffer. // fReadPort - reads a block of data from the input buffer. // fWritePort - reads a block of data to the output buffer. // fDropDTR - drops DTR (signals that the computer is not ready) . // fRaiseDTR - raises DTR (signals that the computer is ready) . // // . StartLog - start logging all incoming characters. // StopLog - stops logging incoming characters. // fDumpLog - writes the log to a file and resets the log. // rcErrorCode - returns the os specific error code from the last operation. // tf ϊiiπffiifπϊiϊif class TPort { public : /ifdef OS2 env PARITY { 3 = 0, // No parity. ODD = 1, // Odd parity. EVEN = 2, // Even parity. MARK - 3, // Mark parity (parity bit always 1). SPACE - 4 // Space parity (parity bit always 0) . }; enum STOP_BITS { ONE = 0, H stop bit. ONE_AND_HALF - 1, // 1.5 stop bits (valid with 5 data bit length only) . TWO = 2 I f 2 stop bits (not valid with 5 bit WORD length) . }; /endif // OS2 /ifdef _Windows enum PARITY { SUBSTITUTE SHEET NO, // No parity. ODD, // Odd parity. EVEN, // Even parity. MARK, // Mark parity (parity bit always 1). SPACE // Space parity (parity bit always 0) . }; enum STOP_BITS { ONE, // 1 stop bit. ONE_AND_HALF, // 1.5 stop bits (valid with 5 data bit length only) TWO // 2 stop bits (not valid with 5 bit WORD length) . }; /endif // Windows struct ComSettings { STRING port_name; UINT port_num; UINT bps; UINT data_bits; PARITY parity; STOP_BITS stop_bits; }; TPort() ; -TPort() ; FLAG fOpenPort ( const ComSettings fiisettings ) ; FLAG fClosePort O ; void FlushInputBuffer() ; void FlushOutputBuffer() ; FLAG fIsEmptyO const; FLAG fIsFullO const; FLAG fGetChar( char 4ch ) ; FLAG fPutChar( char ch ) ; FLAG fReadPort( PVOID, UINT & ); FLAG fWritePort( PVOID, UINT ); FLAG fWritePort( PCSZ sz ); FLAG fDropDTR() ; FLAG fRaiseDTR(); /ifdef _THREADS FLAG fStartManageThread() ; void ManagePort() ; // Default manage thread. void KillManageThread() ; FLAG fStartCommandThread( TTHREAD ) ; SUBSTITUTE SHEET FLAG fStartCommandThread( TTHREAD, PVOID data ); void KillCommandThreadO ; /endif void StartLog() ; void StopLog() ; FLAG fDumpLog( const char *fname ); ULONG rcErrorCode() const; // PRIVATE IMPLEMENTATION private: /ifdef OS2 HFILE hPort; CT_Buffer buffer; CT_Log log; int manage_thread, command_thread; APIRET re; FLAG fManThread, fCmdThread, log_flag; /endif /ifdef _Windows // Windows variables inserted here. /endif }; // Include inline functions. /ifdef OS2 /include <tport.os2> /endif /ifdef _Windows /include <tport.win> /endif /endif // TPORT_HPP /ifndef TSTREAM_HPP /define TSTREAM_HPP /include <usertype.h> /define MAX_CTMSG_SIZE 512 /define DEF_TSTREAM_SIZE 512 // // TStream // class TStream { public: TStream( UINT buf_size •= DEF_TSTREAM_SIZE ); -TStream() ; SUBSTITUTE SHEET void Reset ( ) ; TStreamfii operator « const FLAG ) TStreamfii operator « const USHORT ) TStreamfit operator « const UINT ) TStreamfit operator « const ULONG ) TStreamS operator « const char* ) TStreamfit operator » FLAG& TStreamfii operator » USHORT& TStreamfii operator » UINT fit TStreamfii operator » ULONG fit TStreamfii operator » char* TStreamfii Put ( const PVOID data, UINT size ) ; TStreamfii Get ( PVOID data, UINT size ) ; protected: TStreamfii incExtractor( UINT ) ; TStreamfii inclnserter( UINT ) ; private: ULONG buf_len; BYTE *buffer; BYTE *iptr, *xptr; friend class MessagePipe; // KLUDGE for DBServer.C }; /************★******************************************* ********************** template <class T> TStreamfii operator « ( TStreamfii, const T& ); template <class T> TStreamfii operator » ( TStreamfii, T& ); template <class T> TStreamfii operator « ( TStream fiistream, const T &t ) { return stream.Put( PVOID( fiit ), sizeof( T ) ); } template <class T> TStreamfii operator » ( TStream fiistream, T &t ) { return stream.Get( PVOID( &t ), sizeof( T ) ); ********************************************************* *********************/ /endif // TSTREAM_HPP /ifndef TSTRING_HPP /define TSTRING HPP SUBSTITUTE SHEET /include <iostream.h> /include <usertype.h> /include <debug. h> /include <TStream. HPP> /include <TBuffer . HPP> FLAG fIsNull ( STRING str ) ; FLAG fIsNotNull ( STRING str ) ; FLAG fStrCmpE ( STRING strl , STRING str2 ) ; FLAG fStrCmpL( STRING strl , STRING str 2 ) ; FLAG fStrCmpG ( STRING strl , STRING Str2 ) ; FLAG operator ( STRING strl, STRING str2 FLAG operator Is ( STRING strl, STRING Str2 FLAG operator ( STRING strl, STRING Str2 FLAG operator <= ( STRING strl. STRING Str2 ) FLAG operator > ( STRING strl, STRING str2 ) FLAG operator >■ ( STRING strl, STRING Str2 ) /include <StrOps.INL> class TString { public: TString() ; // Constructs null string. TString( const TString & ) ; // Copy constructor. TString( STRING ) ; // Copy constructor. TString( STRING, STRING ) ; // Constructs a concatinaton of two strings. -TString() ; // *** Testing functions. FLAG flsAlphanumeric () const; // TRUE if entire string is alpha-num. FLAG flsAlphabetic () const; // TRUE if entire string is alphabetic. FLAG flsUpperCase () const; // TRUE if entire string is upper case. FLAG flsLowerCase () const; // TRUE if entire string is lower case. FLAG flsWhiteSpace () const; // TRUE if entire string is whitespace. FLAG flsPrintable () const; // TRUE if entire string is printable. FLAG flsPunctuation () const; // TRUE if entire string is punctuation. FLAG flsControl () const; // TRUE if entire string is control characters. FLAG flsGraphics () const; // TRUE if entire string is alphabetic. SUBSTITUTE SHEET // TRUE if entire // TRUE if entire // TRUE if entire // TRUE if entire // *** manipulator operators. TStringfi operator •* ( const TString &str ) ; TString operator - ( ) const; TStringfit operator += ( STRING ) ; TStringfit operator &= ( STRING ) ; TStringfit operator |= ( STRING ) ; TStringfit operator "= ( STRING ) ; friend TString operator + ( STRING strl, STRING str2 ); friend TString operator & ( STRING strl, STRING str2 ); friend TString operator j ( STRING strl, STRING str? ); friend TString operator " ( STRING strl, STRING str2 ); // *** accessors. UINT uLength() const; TString substring( UINT start_pos ) const; TString substring( UINT startPos, UINT length, char pad_char - ' ' ) const; chars operator [] ( unsigned index ) ; const chars operator [] ( unsigned index ) const; // *** typecase operators. operator STRING () const; operator unsigned char* () ; operator char* () ; // *** stream operators. TStringS operator « ( const TStringfit ) ; TStringfit operator « ( char ) ; • TStringfi operator « ( int ) ; TStringS operator « ( long ) ; friend TStreamfii operator « ( TStreamS, const TStringfi ); friend TStreamfii operator » ( TStreamfit, TStringfit ); friend ostreamfii operator «( ostream &os, const TString &Str ) ; SUBSTITUTE SHEET // *** properties. FLAG fQueryReadOnly() const; // TRUE if this string is read-only. FLAG fSetReadOnly( FLAG setting ) ; FLAG fQueryFixed() const; // TRUE if this string's length is fixed. FLAG fSetFixed( FLAG setting ) ; FLAG fQueryShared() const; // TRUE if this string's value is shared. FLAG fSetShared( FLAG setting ) ; private: TString( TBuffer *pBuffer ) ; // Create a new TString based on a TBuffer. void prepareToChange() ; // Called before any change to the string is made. TBuffer* assignBuffer( TBuffer* ); // Assigns the new buffer to the old one. TBuffer ♦buffer- // Pointer to allocated memory block. }; template <class base> class TSTRING { }; template <UINT angth, char padding> class TCharArray { TCharArray() ; // Constructs padded array. TCharArray( STRING ); // STRING Copy constructor. private : } ; /include <TString.INL> /endif // TSTRING_HPP /******************************************************* ********************** // // TFlag inline members. // inline void TFlag: :SetDefault() SUBSTITUTE SHEET { } inline TFlagfii TFlag: : Assign ( const TFlag fiiflag ) { setNotNullO ; value •= flag.value; return (*this) ; } inline TFlagfii TFlag: :Assign( FLAG flag ) { setNotNullO ; value - FLAG( flag != FALSE ); return (*this) ; } inline TFlag: :operator FLAG() const { useAsValue() ; return FLAG( value != FALSE ) ; } inline TFlag: :operator STRING() const { useAsValue() ; return (value) ? TRUE_T0K : FALSE TOK; } inline TFlagfii TFlag: :operator = ( const TFlag fiiflag ) { return Assign( flag ); } inline TFlagfii TFlag: :operator = ( FLAG flag ) { return Assign( flag ) ; } // *** Comparison operators *** inline FLAG TFlag: :operator == ( const TFlag fiiflag ) const { useAsValue(); return FLAG( value •*== FLAG( flag ) ); } inline FLAG TFlag: :operator -= ( FLAG flag ) const { useAsValue() ; return FLAG( value == flag ) ; } inline FLAG TFlag: :operator == ( int flag ) const SUBSTITUTE SHEET { useAsValue() ; return FLAG( value = (flag !•= 0) ); } inline FLAG TFlag: : operator ! = ( const TFlag fiiflag ) const { useAsValue() ; return FLAG( (*this — flag) == 0 ); } inline FLAG TFlag: :operator != ( FLAG flag ) const { useAsValue() ; return FLAG( (*this == flag) == 0 ) ; } inline FLAG TFlag: :operator !-= ( int flag ) const { useAsValue() ; return FLAG( (*this == flag) = • = 0 ) ; } //******************************************************* ********************** // // TTimestamp inline members. // inline void TTimestamp: :SetDefault() { ForceValidate() ; } inline TTimestampfii TTimestamp::operator = ( const TTimestamp fiits ) { return Assign( ts ); } /ifdef OS2 inline TTimestampfii TTimestamp::operator = ( const DATETIME fiiDate ) { return Assign( Date ); } /endif // OS2 inline USHORT TTimestamp: :usYear() const { return Year; } inline USHORT TTimestamp::usMonth() const SUBSTITUTE SHEET { return Month; } inline USHORT TTimestamp: :usDay() const { return Day; } inline USHORT TTimestamp::usHour() const { return Hour; } inline USHORT TTimestamp: :usMinute() const { return Minute; } inline USHORT TTimestamp: :usSecond() const { return Second; } inline USHORT TTimestamp: :usMillisec() const { return Millisec; } inline FLAG TTimestamp::operator < ( const TTimestamp fitts ) const { return FLAG( ! (*this >= ts) ); } inline FLAG TTimestamp: :operator <= ( const TTimestamp &ts ) const < return FLAG( ! (*this > ts) ); } inline FLAG TTimestamp: .operator != ( const TTimestamp fiits ) const { return FLAG( ! (*this == ts) ); } // static member. inline FLAG TTimestamp::fIsLeapYear( USHORT year ) { if (year % 4 && ! (year % 100 j| ! (year % 400))) return TRUE; else return FALSE; } SUBSTITUTE SHEET / static member, inline USHORT TTimestamp::usMaxMonth() return 12; / static member, inline USHORT TTimestamp: :usMaxHour() return 23; / static member, inline USHORT TTimestamp: :usMaxMinute() return 59; / static member, inline USHORT TTimestamp: :usMaxSecond() return 59; / static member, inline USHORT TTimestamp: :usMaxMillisec() return 999; II- // // Inline members of TBuffer. // inline UINT TBuffer::uLength() const { return length; } inline void TBuffer: :Resize( UINT new_size ) { if ( ! fResize ( new_size ) ) ASSERT ( FALSE ) ; } inline const BYTE* TBuffer: :Buf() const { return TBaseBuffer: :Buf() ; } inline BYTE* TBuffer: :Buf() { ASSERT( fQueryProperty( READONLY ) — FALSE ) ; SUBSTITUTE SHEET ASSERT( fQueryProperty( SHARED ) — TRUE | | uRefCount() ββ 1 ) ; return TBaseBuffer: :Buf() ; } inline const BYTE* TBuffer: :Buf( UINT index ) const { ASSERT( index < uLength() ) ; return Buf() + index; } inline BYTE* TBuffer: :Buf( UINT index ) { ASSERT( index < uLength() ) ; return Buf() + index; } inline UINT TBuffer::uRef() { return ++ref_c; } inline UINT TBuffer: :uDeref() { // Decrement ref_c. If ref_c = 0 then delete this object, if (—ref_c) return ref_c; else { delete this; return 0; } } inline UINT TBuffer: :uRefCount() const { return ref_c; } inline FLAG TBuffer: :fQueryProperty( PROPS prop ) const { return props.fIsSet( prop ); } inline TBuffer: :PROPS TBuffer: :SetProperty( PROPS prop ) { return props.Set( prop ) ; } inline TBuffer: :PROPS TBuffer::ClearProperty( PROPS prop ) { return props.Clear( prop ); } inline FLAG TBuffer: :fQueryReadOnly() const { SUBSTITUTE SHEET return props.fIsSet( READONLY ); } inline FLAG TBuffer: :fSetReadOnly( FLAG f ) { return FLAG( ((f ? props.Set( READONLY ) : props.Clear( READONLY )) | READONLY) == TRUE ); } inline FLAG TBuffer: :fQueryFixed() const { return props.fIsSet( FIXED ); } inline FLAG TBuffer: :fSetFixed( FLAG f ) { return FLAG( ((f ? props.Set( FIXED ) : props.Clear( FIXED )) j FIXED) == TRUE ) ; } inline FLAG TBuffer::fQueryShared() const { return props.fIsSet( SHARED ); } inline FLAG TBuffer: :fSetShared( FLAG f ) { return FLAG( ((f ? props.Set( SHARED ) : props.Clear( SHARED ) ) j SHARED) — TRUE ) ; } // String functions. inline TBuffer& TBuffer: :StrCopy( const TBuffer fitbuf ) { return PrepareToChange() ._strCopy( buf ); } inline TBufferfii TBuffer::StrCopy( STRING str ) { return PrepareToChange() ._strCopy( str ) ; } inline TBufferfii TBuffer: :StrConcat( const TBuffer fiibuf ) { return PrepareToChange() ._strConcat( buf ) ; } inline TBufferfii TBuffer: :StrConcat( STRING str ) { return PrepareToChange() ._strConcat( str ); } inline TBufferfii TBuffer: :StrTrunc( UINT index ) { return PrepareToChange() ._strTrunc( index ); SUBSTITUTE SHEET > inline TBufferfii TBuffer::StrGrow( UINT index ) { return PrepareToChange() ._strGrow( index ); } inline TBufferfii TBuffer: :StrGrow( UINT index, BYTE pad ) { return PrepareToChange() ,_strGrow( index, pad ); } //******************************************************* ********************** // // TNull inline members. // inline FLAG TNull: :fIsNull() const { return isnull; } inline FLAG TNull: :operator ! () const { return fIsNull(); } inline void TNull: :setNotNullO { isnull = FALSE; } inline void TNull::useAsValue() const { // This funciton is called when a TObject is used is such a way that it must // have a value. // // Once the exception layer is implemented this routine will throw an exception. // ASSERT( isnull == FALSE ); } inline TStreamfit operator « ( TStream fiistream, const TNull fiinull ) { return stream « FLAG( null.isnull ); } inline TStreamfii operator » ( TStream fiistream, TNull &null ) { FLAG isnl; stream » isnl; SUBSTITUTE SHEET if (isnl) null.fSetNullO ; else null.setNotNullO ; return stream; } /include <string.h> // Private members. inline void TString: :prepareToChange() { buffer = &buffer->PrepareToChange() ; } // *** typecast operators. inline TString: :operator STRING () const { return buf f er->Buf ( ) ; } inline TString: :operator unsigned char* () { return buffer->Buf() ; } inline TString: :operator char* () { return (char*)buffer->Buf() ; } inline TStringfit TString: :operator += ( STRING str ) { buffer = &buffer->StrConcat( str ); return *this; } TString operator + ( STRING strl, STRING str2 ) return TString( strl, str2 ) ; } inline UINT TString::uLength() const { return buffer->uLength() ; } inline charfii TString: :operator [] ( unsigned index ) { prepareToChange() ; return *((char*)buffer->Buf( index )); > inline const charfii TString: :operator [J ( unsigned index ) const SUBSTITUTE SHEET { return *((const char*)buffer->Buf( index )); } // *** friend stream operators . inline TStreamfii operator « ( TStream fiibuf , const TString fiistr ) { return buf « * (str. buffer) ; } inline TStreamfii operator >> ( TStream fiibuf , TString fiistr ) { return buf » * (str. buffer) ; } inline ostreamfii operator « ( ostream &os, const TString fiiStr ) { return os « *(Str.buffer) ; } inline FLAG TString: :fQueryReadOnly() const { return buffer->fQueryReadOnly() ; } inline FLAG TString: :fSetReadOnly( FLAG setting ) { prepareToChange() ; return buffer->fSetReadOnly( setting ) ; } inline FLAG TString: :fQueryFixed() const { return buffer->fQueryFixed() ; } inline FLAG TString: :fSetFixed( FLAG setting ) { prepareToChange() ; return buffer->fSetFixed( setting ); } inline FLAG TString: :fQueryShared() const { return buffer->fQueryShared() ; } inline FLAG TString::fSetShared( FLAG setting ) { prepareToChange() ; return buffer->fSetShared( setting ) ; } SUBSTITUTE SHEET /include <string.h> / / tttltttltltttttttttllttlllttllttlllttttlltltttttllltlll lllffililtlllf-tllllffl // // TPort OS/2 inline functions. inline FLAG TPort: :fIsEmpty() const { return buffer.fIsEmpty() ; } inline FLAG TPort: :fIsFull() const { return buffer.flsFull() ; } inline FLAG TPort::fPutChar( char ch ) { return fWritePort ( fiich, sizeof ( ch ) ) ; } inline FLAG TPort : : f GetChar ( char fiich ) i return buffer.fGetChar( ch ); } inline FLAG TPort: :fWritePort( PCSZ sz ) { return fWritePort( (PVOID)sz, strlen( sz ) ); } /ifdef _THREADS inline FLAG TPort::fStartCommandThread( TTHREAD thread ) { return fStartCommandThread( thread, (PVOID) his ); } inline void TPort: :KillManageThread() { fManThread = FALSE; } inline void TPort: :KillCommandThread() { fCmdThread = FALSE; } /endif // THREADS inline void TPort::StartLog() { SUBSTITUTE SHEET log_flag - TRUE; inline void TPort::StopLog() log_flag = FALSE; inline FLAG TPort::fDumpLog( const char *fname ) return log.fDumpLog( fname ); inline ULONG TPort::rcErrorCode() const return re; ********************************************************* *************** * * bpb.h * ********************************************************* *************** */ /ifndef BPB_INC /define " BPB INC /include <standard.h> /pragma pack (1) struct BPB { WORD wBytesPerSector; BYTE cSectorsPerCluster; WORD wReservedSectors; BYTE cFATs; WORD wRootDirEntries; WORD wSectors; BYTE cMediaDescriptor; WORD wSectorsPerFAT; WORD wSectorsPerTrack; WORD wHeads; DWORD dwHiddenSectors; DWORD dwHugeSectors; }; /pragma pack () /endif SUBSTITUTE SHEET /* ********************************************************* *************** */ /* ********************************************************* *************** * * cds.h * * ********************************************************* *************** */ /ifndef _CDS_INC /define _CDS_INC /include <dpb.h> /include <standard.h> /pragma pack (1) struct CDS { struct CDS3 { CHAR cDirectory [0x43]; WORD wFlags; struct DPB _far *lpDPB; union { WORD wStartingCluster; DWORD lpRedirBlock; }; WORD wUserValue; WORD wRootCount; }; BYTE cDevicelD; void _far *lpIFS; WORD wIFSValue; }; /define CDS_CDROM 0x0080 /define CDS_SUBST 0x1000 /define CDS_J0IN 0x2000 /define CDS_VALID 0x4000 /define CDS_REMOTE 0x8000 /pragma pack () /endif /* ********************************************************* *************** */ SUBSTITUTE SHEET /* ********************************************************* *************** * * dpb. h * * ********************************************************* *************** */ /ifndef _DPB_INC /define _DPB_INC /include <driver.h> /include <standard.h> /pragma pack (1) struct DPB { BYTE cDrive; BYTE cUnit; WORD wBytesPerSector; BYTE cClusterMask; BYTE cClusterShift; WORD wFirstFATSector; BYTE cFATs; WORD wRootDirEntries; WORD wFirstDataSector; WORD wMaxCluster; WORD wSectorsPerFAT; WORD wRootDirSector; struct DRIVER_HEADER _far *lpDriver; BYTE cMediaDescriptor; BYTE cAccessFlag; struct DPB _far *lpNext; WORD wNextCluster; WORD wFreeClusters; }; /pragma pack () /endif /* ********************************************************* *************** */ /* ********************************************************* *************** * * driver.h * * ********************************************************* *************** */ /ifndef DRIVER INC SUBSTITUTE SHEET /define DRIVER INC /include <standard.h> /pragma pack (1) /* Device driver header */ Struct DRIVER_HEADER { struct DRIVER_HEADER _far *lpNext; WORD wAttribute; /ifdef BORLANDC WORD *pstrategy; WORD *plnterrupt; /else void _based ( (_segment) _self) *pStrategy; void _based ( (_segment) _self) *plnterrupt; /endif union { CHAR cName [8] ; BYTE cUnitsSupported; }; }; /* Attribute values */ /define IS_STDIN 0x0001 /define IS_STDOUT 0x0002 /define IS_HUGE_BLOCK 0x0002 /define IS_NUL 0x0004 /define IS_CLOCK 0x0008 /define INT29H_OK 0x0010 /define GIOCTL_OK 0x0040 /define GIOCTL_QUERY_OK 0x0080 /define OCRM_OK 0x0800 /define OTB_OK 0x2000 /define FAT_REQUIRED 0x2000 /define IOCTL_OK 0x4000 /define IS CHAR DEVICE 0x8000 /' Device driver commands '/ /define D_INIT 0x00 /define D_MEDIA_CHECK 0x01 /define D_BUILD_BPB 0x02 /define D_IOCTL_READ 0x03 /define D_READ 0x04 /define D_NONDESTRUCTIVE_READ 0x05 /define D_INPUT_STATUS 0x06 /define D_INPUT_FLUSH 0x07 /define D_WRITE 0x08 /define D_WRITE_WITH_VERIFY 0x09 /define D_OUTPUT STATUS OxOA /define D_OUTPUT~FLUSH OxOB /define D IOCTL WRITE OXOC SUBSTITUTE SHEET /define D_OPEN_DEVICE OxOD /define D_CLOSE_DEVICE OxOE /define D_REMOVABLE_MEDIA OxOF /define D_OUTPUT_UNTIL_BUSY OxlO /define D_GENERIC_IOCTL 0x13 /define D_GET_LOGICAL_DEVICE 0x17 /define D_SET_LOGICAL_DEVICE 0x18 /define D IOCTL QUERY 0x19 /define MAX DRIVER COMMAND 0X19 /' Driver status values /define D_DONE 0X0100 /define D_BUSY 0x0200 /define D ERROR 0X8000 /' Driver error values •/ /define D_WRITE_PROTECTED 0x00 /define D_BAD_UNIT 0x01 /define D_NOT_READY 0x02 /define D_BAD_COMMAND 0X03 /define D_BAD_CRC 0x04 /define D_BAD_HEADER 0x05 /define D_SEEK_FAILURE 0x06 /define D_BAD_MEDIA 0x07 /define D_SECTOR_NOT_FOUND 0X08 /define D_NO_PAPER 0x09 /define D_WRITE_ERROR OxOA /define D_READ_ERROR OxOB /define D_GENERAL_FAILURE OxOC /define D_BAD_DISK_CHANGE OxOF /* Request header structure */ Struct REQUEST_HEADER { / ♦ The format of the request header's first portion is common to all commands. */ BYTE cHeaderLength; BYTE cUnit; BYTE cCommand; WORD wStatus; char cReserved [8] ; /*No further fields are required for commands 06h (input status) 07h (input flush) OAh (output status) OBh (output flush) ODh (open device)OEh (close device) 17h (get logical device) 18h (set logical device) SUBSTITUTE SHEET The request header format for the remaining commands can be handled by a set of overlapping structures. */ union { struct { /♦command OOh (initialise driver) */ BYTE cUnitsSupported; void _far ♦IpEndOfMemory; union { CHAR _far ♦lpCommandLine; void _far *lpBPBTable; }; BYTE cDrive; WORD wMessageFlag; }; /* Many commands are provided with a media descriptor byte at the first location in the variable portion of the request header - hence another set of overlapping structures. */ struct { BYTE cMediaDescriptor; union { struct { / ♦ command Olh (media check) */ BYTE cChangeStatus; CHAR _far *lpVolumeIDForCheck; In¬ struct { /♦command 02h (build BPB) ♦/ void _far ♦lpFATSector; void _far ♦lpBPB; • }; struct { /♦Commands 03h (IOCTL Read), 04h (Read), 08h (Write), 09h (Write with verify) and OCh (IOCTL Write) all transfer data to or from a buffer, though only some of these commands require all the following fields. ♦/ BYTE _far ♦lpBuffer; WORD wCount; WORD wStart; SUBSTITUTE SHEET CHAR _far *lpVolumeIDForIO; DWORD dwHugeStart; }; }; }; /* Command 05h (non-destructive read) simply returns a character waiting for input, if one is present and requires only one field in its request header. */ CHAR cCharWaiting; struct { /♦Commands 13h (Generic IOCTL) and 19h (IOCTL query) */ BYTE cCategory; BYTE cMinorCode; WORD wGIOCTLReserved; BYTE _far *lpData; }; }; }; /pragma pack ( ) /endif /* ********************************************************* *************** */ /* ********************************************************* *************** * * iosys. h * * ********************************************************* *************** */ /ifndef _IOSYS_INC /define IOSYS INC /include <bpb.h> /include <standard.h> /pragma pack (1) Struct IOSYSDRIVETABLE { struct IOSYSDRIVETABLE _far *lpNext; BYTE cBIOSDrive; SUBSTITUTE SHEET BYTE cDOSDrive; struct BPB DiskBPB; BYTE cFileSystemFlag; WORD wOpenCloseCount; BYTE cDeviceType; WORD wFlags; WORD wCylinders; struct BPB DriveBPB; BYTE cReserved [6] ; BYTE cLastTrack; union { DWORD dwLastTime; struct { WORD wPartitionFlag; WORD wStartingCylinder; ); }; CHAR cVolumeLabel [12]; DWORD dwSerialNu ber; CHAR cFileSystem [9]; }; /pragma pack ( ) /endif /* ********************************************************* *************** */ /* ********************************************************* *************** * * sft . h * * ********************************************************* *************** */ /ifndef _SFT_INC /define _SFT_INC /include <dpb.h> /include <driver.h> /include <standard.h> /pragma pack (1) /* System File Table Header */ Struct SFT_HEADER { struct SFT_HEADER _far *lpNext; WORD wCount; }; SUBSTITUTE SHEET /* System File Table */ struct SFT { WORD wHandles; WORD wAccess; BYTE cAttribute; WORD wMode; union { struct DPB _far *lpDPB; struct DRIVER_HEADER _far *lpDriver; }; WORD wStartingCluster; WORD wTime; WORD wDate; DWORD dwSize; DWORD dwFilePointer; WORD wRelativeCluster; DWORD dwDirSector; BYTE cDirSectorEntry; CHAR cName [11]; struct SFT _far *lpNextShare; WORD wMachine; /ifdef BORLANDC void _seg *spOwner; WORD pSharingRecord; /else _segment spOwner; void _based (void) *pSharingRecord; /endif WORD wAbsoluteCluster; void _far *lpIFS; }; /pragma pack () /endif /* ********************************************************* *************** */ /* ********************************************************* *************** * * standard.h * * ********************************************************* *************** */ /ifndef _STANDARD_INC /define _STANDARD_INC /* Logical operators and values */ SUBSTITUTE SHEET // for consistency with TRUE /* Convenient data types */ typedef unsigned charBYTE; typedef unsigned shortWORD; typedef unsigned longDWORD; typedef signed charSBYTE; typedef signed intSWORD; typedef signed longSDWORD; typedef unsigned charCHAR; typedef intBOOL; /* Macro for generating a far pointer from segment and offset*/ /ifndef MK_FP /define MK_FP(seg,off) (((_segment) (seg)) :> ((void _based (void) *) (off))) /endif /* The above form for MK_FP has a problem (at least in C 6.00) with multiple dereferencing through structures.On the other hand, the compiler generates much more efficient code with it. As an alternative, keep the more familiar macro on standby. ♦/ /if FALSE /define MK_FP(seg,off) ((void _far *) (((DWORD) (seg) « 16) j ((WORD) (off)))) /endif /* Macros to decompose 16-bit and 32-bit objects into high and low components and to reconstitute them */ /define HIGHBYTE(x) ((BYTE) ((x) » 8)) /define LOWBYTE(X) ((BYTE) (x)) SUBSTITUTE SHEET /define MK_WORD(high,low) (((WORD) (high) « 8) | (low)) /define HIGHWORD(X) ((WORD) ((x) » 16)) /define LOWWORD(x) ((WORD) (x)) /define MK_DWORD(high,low) (((DWORD) (high) « 16) | (low)) /* Macros for directing the compiler to use current segment register values rather than generate relocatable references*/ /define CODESEG _based (_segname ("_CODE")) /define CONSTSEG _based (_segname ("_CONST")) /define DATASEG _based (_segname ("_DATA")) /define STACKSEG _based (_segname ("_STACK")) /* Macro for NULL in case using STDLIB.H would be inappropriate */ /ifndef NULL /define NULL ((void *) 0) /endif /endif ********************************************************* *************** */ ********************************************************* *************** ; * driver.inc * ; ********************************************************* *************** ; Device driver header DRIVER_HEADERSTRUCT lpNextddOFFFFFFFFh wAttributedwOOOOh pStrategydwOOOOh plnterruptdwOOOOh UNION cNamedb" " cUnitsSupporteddb? ENDS DRIVER_HEADERENDS ; Attribute values IS_STDINEQU0001h SUBSTITUTE SHEET IS_STDOUTEQU0002h IS_HUGE_BLOCKEQU0002h IS_NULEQU0004h IS_CLOCKEQU0008h INT29H_OKEQU0010h GIOCTL_OKEQU0040h GIOCTL_QUERY_OK EQU0080h OCRM_OK EQU0800h OTB_OKEQU2000h FAT_REQUIREDEQU200Oh IOCTL_OKEQU4000h IS_CHAR_DEVICEEQU800Oh ; Device driver commands - these do not follow the upper case convention ; because they are used to generate the names of the procedures for each ; driver command. D_INITEQU00h D_MEDIA_CHECKEQUOlh D_BUILD_BPBEQU02h D_IOCTL_READEQU03h D_READEQU04h D_NONDESTRUCTIVE_READEQU05h D_INPUT_STATUSEQU06h D_INPUT_FLUSHEQU07h D_WRITE EQUOβh D_WRITE_WITH_VERIFYEQU09h D_OUTPUT_STATUS EQUOAh D_OUTPUT_FLUSHEQU0Bh D_IOCTL_WRITEEQU0Ch D OPEN_DEVICEEQU0Dh D " LOSE_DEVICEEQU0Eh D . £MOVABLE_MEDIAEQU0Fh D^OUTPUT_UNTIL_BUSYEQUlOh D_GENERIC_IOCTL EQU13h D_GET_LOGICAL_DEVICEEQU17h D_SET_LOGICAL_DEVICEEQU18h D_IOCTL_QUERYEQU19h MAX_DRIVER_COMMANDEQU19h ; Driver status values D_DONEEQU0100h D_BUSYEQU0200h D_ERROR EQU8000h ; Driver error values D_WRITE_PROTECTEDEQU0Oh D_BAD_UNITEQU0lh D_NOT_READYEQU02h D BAD COMMANDEQU03h SUBSTITUTE SHEET D_BAD_CRCEQUO4h D_BAD_HEADEREQUO5h D_SEEK_FAILUREEQUO6h D_BAD_MEDIAEQUO7h D_SECTOR_NOT_FOUNDEQU08h D_NO_PAPEREQU09h D_WRITE_ERROREQU0Ah D_READ_ERROREQU0Bh D_GENERAL_FAILUREEQUOCh D_BAD_DISK_CHANGEEQUOFh ; Request Header structure REQUEST_HEADERSTRUCT cHeaderLength db? cUnit db? cCommanddb? wStatusdw? cReserveddbOδh DUP (?) UNION STRUCT cUnitsSupporteddb? IpEndOfMemorydd? UNION lpCommandLinedd? lpBPBTabledd? ENDS cDrivedb? wMessageFlagdw? ENDS STRUCT cMediaDescriptordb? UNION STRUCT cChangeStatus db? lpVolumelDForCheckdd? ENDS STRUCT lpFATSectordd? lpBPB dd? ENDS STRUCT lpBufferdd? wCountdw? wStartdw? lpVolumelDForlOdd? dwHugeStartdd? ENDS ENDS ENDS cCharWaitingdb? STRUCT cCategory db? cMinorCodedb? wGIOCTLReserveddw? SUBSTITUTE SHEET lpDatadd? ENDS ENDS REQUEST_HEADERENDS . I ********************************************************* *************** ********************************************************* *************** ; * sft.inc * . ********************************************************* *************** ; System File Table Header SFT_HEADERSTRUCT lpNextddOFFFFFFFFh wCountdwOOOOh SFT HEADERENDS ; System File Table (with default initialisation suitable for use with ; FCBs) SFTSTRUCT wHandlesdwOOOOh wAccessdw'AA' cAttributedb'A' wMode dw'AA' UNION lpDPBdd'AAAA' lpDriverdd'AAAA' ENDS wStartingClusterdw'AA' wTime dw'AA' wDate dw'AA' dwSizedd'AAAA' dwFilePointer ddOOOOOOOOh wRelativeClusterdw'AA' dwDirSectordd'AAAA' cDirSectorEntrydb'A' cName db'AAiViAAAAA,J.' lpNextSharedd'AAAA' wMachinedw'AA' spOwnerdw'AA' pSharingRecorddw'AA' wAbsoluteClusterdw'AA' lpIFS dd'AAAA' SFTENDS SUBSTITUTE SHEET I ********************************************************* *************** ********************************************************* *************** ; * standard . inc ********************************************************* *************** .NOCREF standard_inc IFNDEFstandard_inc ; Logical symbols .NOCREF FALSE, TRUE FALSEEQUO TRUEEQU(NOT FALSE) standard_inc = TRUE ENDIF ********************************************************* *************** SUBSTITUTE SHEET

Claims: VYHAT IS CLAIMED IS: 1. An apparatus with an integral computer tracing and security monitoring system comprising a transparent agent controlling means for sending signals to a host monitoring system via a telecommunication link, at spaced-apart intervals of time, said signals including identifying indicia for said device. 2. An apparatus as claimed in claim 1 , wherein the means for sending signals includes a telecommunication interface connectable to a communication link. 3. An apparatus as claimed in claim 1, wherein the means sends signals at regular periodic intervals. 4. A computer tracing and security monitoring system, comprising: a computer; a telecommunication interface operatively connected to the computer; and means controlled by the computer for sending signals to the telecommunication interface including signals for contacting a host monitoring system, and for providing the host monitoring system with identification indicia. 5. A system as claimed in claim 4, wherein the computer has addressable memory (such as read-only memory or random-access memory, and the means includes software. 6. A method for providing a computer with a Agent security system, comprising the steps of preparing software for the computer with instructions for dialing a host monitoring system number without visual or audible signals and transmitting identification indicia, and programming the software into addressable memory of the computer at a location not normally accessible to operating software for the computer. SUBSTITUTE SHEET

Inventors: CAIN FRASER, COTICHINI CHRISTIAN, NGUYEN THANH CAM, CAIN, FRASER, COTICHINI, CHRISTIAN, NGUYEN, THANH, CAM
Applicants: ABSOLUTE SOFTWARE CORP, CAIN FRASER, COTICHINI CHRISTIAN, NGUYEN THANH CAM, ABSOLUTE SOFTWARE CORPORATION, CAIN, FRASER, COTICHINI, CHRISTIAN, NGUYEN, THANH, CAM
Application year: 1995
Main classification: G06F1
IPC codes: G06F1, G06F13, G06F21, G08B25, H04L9, H04L12, H04Q7