Date: Mon, 15 Feb 1993 00:54:51 -0500 From: Monty Solomon Subject: IXO and Alphanumeric Pagers "tpage" or "Tom's Pager System" is a set of programs that let you send messages to alpha-numeric pagers using the "IXO" protocol. It supports a dialing directory, a "who's on duty now" schedule, and can do special tricks with RFC822-format email. There is a mailing list for talking about this software (and other ixo topics) called ixo@warren.mentorg.com. To subscribe, send email to ixo-request@warren.mentorg.com. Here is the current version of the tpage package. ---- Cut Here and unpack ---- #!/bin/sh # This is a shell archive (shar 3.21) # made 11/24/1992 17:07 UTC by tal@milk # Source directory /tmp_mnt/nfs/usr10/tal/work/beep2 # # existing files WILL be overwritten # # This shar contains: # length mode name # ------ ---------- ------------------------------------------ # 2321 -rw-rw-r-- README # 2174 -rw-rw-r-- HISTORY # 1756 -rw-rw-r-- LEGALSTUFF # 539 -rw-rw-r-- MAILINGLIST # 5454 -rw-rw-r-- INSTALL # 6781 -rwx------ tpage.l # 10116 -r-xr-xr-x tpage.pl # 12085 -rwxrwxr-x tpaged.pl # 595 -rwxrwxr-x startdaemon # 12894 -r--r--r-- ixocico.c # 390 -rw-rw-r-- depend # 2140 -rw-rw-r-- schedule # 623 -rw-rw-r-- table # 8737 -rw-rw-r-- ixo.doc # if touch 2>&1 | fgrep '[-amc]' > /dev/null then TOUCH=touch else TOUCH=true fi # ============= README ============== echo "x - extracting README (Text)" sed 's/^X//' << 'SHAR_EOF' > README && XREADDME -- General information about "tpage" X by Tom Limoncelli, tal@warren.mentorg.com X Copyright (c) 1992, Tom Limoncelli X The sources can be freely copied for non-commercial use only X and only if the source is unmodified. X X"tpage" or "Tom's Pager System" is a set of programs that let Xyou send messages to alpha-numeric pagers using the "IXO" protocol. XIt supports a dialing directory, a "who's on duty now" schedule, Xand can do special tricks with RFC822-format email. X XThe system has the following features: X X...sends pages to any pager system that supports the IXO protocol. X X...additional protocols can be added. (I'll write the touch-tone X protocol soon). X X...can parse email messages and extract the interesting info from X them resulting in shorter messages. X X...can copy it's input to stdout and therefore can be used as a "tee". X X...maintains a directory of people's phone numbers/PINs. X X...can page "the person on duty" (searches a schedule). X X...schedule can have slots that are empty, but find someone anyway if X the message is marked "urgent". X X...with programs like procmail, permits you to send certain email X messages to your pager. X X...a list of modems can be given to the daemon. X XHow it works (and how all the programs fit together): X Xo beep2.pl takes command-line and stdin, reads the schedule, looks X up people's paging info in the directory, and queues the message. Xo tpaged.pl should always be running. Every 30 seconds it wakes up X to check the queue for messages. Xo tpaged.pl then sorts and batches the messages. Xo tpaged.pl then calls the appropriate program to do the protocol X (currently that's ixocico but others can be written) and watches X the output for messages: X "#MESOK x" which means that message "x" was successful and can be X deleted from the queue. X "#MESREJECT x" which means that message "x" was rejected and should X be deleted from the queue. Email will be sent to the person X that sent the page. X Messages that can't be deleted from the queue (due to NFS problems X or whatever) are added to a "blacklist" in memory, and are not X retried. X XFor installation instructions, read INSTALL. XFor program history, read HISTORY. X XIf you aren't using "procmail", you're working too hard. Check X"archie" for a location near you! SHAR_EOF $TOUCH -am 0505135192 README && chmod 0664 README || echo "restore of README failed" # ============= HISTORY ============== echo "x - extracting HISTORY (Text)" sed 's/^X//' << 'SHAR_EOF' > HISTORY && XHISTORY X by Tom Limoncelli, tal@warren.mentorg.com X Copyright (c) 1992, Tom Limoncelli X The sources can be freely copied for non-commercial use only X and only if the source is unmodified. X XFor installation instructions, read INSTALL. XFor program history, read HISTORY. XFor general program information, read README. X X X(( This program is the successor to "fbeep". X(( X(( fbeep history: X(( X(( 1.0 Oct 29, 1991 -- First working version. X(( X(( 1.1 Oct 30, 1991 -- Debugging levels implemented (-s -v -vv). X(( X(( 1.2 Oct 30, 1991 -- Cleaned the output, error message on failure. X(( X(( 1.3 Nov 20, 1991 -- Added -m, -M and "null person" options. X(( X(( Made -a (abort) replace -w (wait). X(( X(( 1.4 Jan 2, 1992 -- Destination can now be a list of people. X(( X(( (project ended, replaced by tpage, tpaged, and ixocico) X X Xbeep2.pl: X 1.0 Jan 31, 1992 -- First release. X 1.1 Apr 20, 1992 -- Second release. X 2.0 May 4, 1992 -- Sets umask before creating queue entries. X Xtpaged.pl history: X 1.0 Jan 31, 1992 -- First release. X 1.1 Apr 20, 1992 -- New "wait before dial" algorithm. X Hunted for "endless-repeat" bug. X Now checks status of unlink. X 2.0 May 4, 1992 -- Re-wrote data structures of ixo_* routines X to make things more reliable. No more parallel X arrays, maintains records instead. X Now "blacklists" files that couldn't be deleted. X2.1 patch 1, May 6, 1992-- X-- &do_protocol_ixo should declare $index as a local variable. X-- &ixo_listindex should always be called with 1 parameter. X-- Rejected messages should be deleted from queue directory and from X memory. If they can't be deleted from the directory, blacklist that X file. (previously they were not properly deleted from memory and no X attempt was made to remove them from disk) (LOOPING BUG FIXED) X-- Debug mode prints a few new messages. X-- &ixo_mesg_append didn't declare $count as local. X-- &ixo_mesg_get didn't declare $pin, $error, $mess, $file as local. X Xixocico.c X 1.0 Jan 31, 1992 -- First release. X 1.1 Feb 4, 1992 -- Tuned some of the time-outs X 2.0 May 4, 1992 -- Added DJ's 2 patches X SHAR_EOF $TOUCH -am 0507144092 HISTORY && chmod 0664 HISTORY || echo "restore of HISTORY failed" # ============= LEGALSTUFF ============== echo "x - extracting LEGALSTUFF (Text)" sed 's/^X//' << 'SHAR_EOF' > LEGALSTUFF && X XCopyright (c) 1992 Thomas A. Limoncelli, tom_limoncelli@warren.mentorg.com X XPermission to use, copy, modify, distribute, and sell this software and Xits documentation for any purpose is hereby granted without fee, Xprovided that (i) the above copyright notices and this permission Xnotice appear in all copies of the software and related documentation, Xand (ii) the name of Mentor Graphics Corp or any company or Xorganization that I am associated with may not be used in any Xadvertising or publicity relating to the software without the specific, Xprior written permission of that company or orgaization X XTHE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, XEXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY XWARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. X XIN NO EVENT SHALL THOMAS A. LIMONCELLI OR MENTOR GRAPHICS CORP BE XLIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES XOF ANY KIND, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA XOR PROFITS, WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON XANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE XOR PERFORMANCE OF THIS SOFTWARE. X XUse this software at your own risk. Think before using. Do not use Xthis software for mission-critical applications without careful backup Xprocedures. Though every effort has been made to make the software bug Xfree, some bugs are sure to still exist. More importantly, many paging Xservices are known to drop some pages when under heavy load, when Xupgrading their equipment, or at other times. The author and the Xauthor's employer are not responsible for problems with the software, Xthe paging service, or malfunctions of your pager. Dead batteries are Xyour problem. X SHAR_EOF $TOUCH -am 1111122392 LEGALSTUFF && chmod 0664 LEGALSTUFF || echo "restore of LEGALSTUFF failed" # ============= MAILINGLIST ============== echo "x - extracting MAILINGLIST (Text)" sed 's/^X//' << 'SHAR_EOF' > MAILINGLIST && XUPDATES: If you have source code changes please send them into the Xauthor at "tal@warren.mentorg.com". He does make an effort to Xintegrate them into the source code, or at least make them available to Xothers. He is currently compiling a list of modifications required to Xuse this software with the various paging services that aren't 100% Xcompliant. X XMAILING LIST: There is a mailng list for talking about this software X(and other ixo topics) called ixo@warren.mentorg.com. To subscribe, Xsend email to ixo-request@warren.mentorg.com. SHAR_EOF $TOUCH -am 1023143892 MAILINGLIST && chmod 0664 MAILINGLIST || echo "restore of MAILINGLIST failed" # ============= INSTALL ============== echo "x - extracting INSTALL (Text)" sed 's/^X//' << 'SHAR_EOF' > INSTALL && XINSTALL -- How to install the Tom Page set of programs. X by Tom Limoncelli, tal@warren.mentorg.com X Copyright (c) 1992, Tom Limoncelli X The sources can be freely copied for non-commercial use only X and only if the source is unmodified. X XFor program history, read HISTORY. XFor general program information, read README. X X XTHEORY OF OPERATION: (this assumes you've read README already) X-------------------- X Xtpage.pl (which is often renamed to "beep" or "page") takes a number of Xoptions, figures out who and how to send to a person, figures Xout the message, and stuffs the info into a file for the daemon Xto read. X Xtpage.pl refers to /home/adm/lib/tpage/table to find out the phone Xnumber and other information that must be given to the daemon. You Xcan also specify a different file name with the "-T" option. X Xtpaged.pl wakes up every couple minutes to see if there Xis anything new in the queue. If there is, it takes the information, Xdials out to your paging company, and transmits the message. X XRather than page a particular person, tpage.pl can be given a schedule Xand be told, "page whoever is on duty right now". This is done Xby giving it "-" as the "who" parameter. The schedule is stored Xin /home/adm/lib/tpage/schedule, or by specifying a file name with Xthe "-S" option. X Xtpage can take the message from the command line, or from stdin. If Xit is coming from stdin, it can parse it as email and do various Xthings with it (see the man page). X X XINSTALLATION: X------------- X X1. Install the Perl programming language. The front-end to tpaged Xis written in Perl. Any version should do, but it was developed with Xversion 4.019. Hopefully, this program already exists on your system. XIf not, I highly recommend that you get it from ftp.uu.net:/pub/gnu Xand install it. X XFor the rest of this document, $TPAGE will refer to the directory Xthat you wish to keep the files associated with the tpage system. X XALL FILES SHOULD BE COPIED to $TPAGE except tpage (belongs in X/usr/local/bin/tpage). The queue directory should be $TPAGE/pqueue but Xnothing will break if you put it someplace else. X"chmod 1777 $TPAGE/pqueue" so that people can write to it but only Xdelete their own files. X X2. Edit tpage.pl and copy it to "/usr/local/bin/tpage". The first Xsection explains what variables need to be edited and why. You might Xwant to call it "page" or "beep" instead. X XEdit tpaged.pl and copy it to "$TPAGE/tpage". The first Xsection of each file explains what variables need to be edited and why. X XEdit tpage.l and copy it to "/usr/local/man/manl" or wherever local Xmanpages are kept. The first section of each file explains what Xvariables need to be edited and why. X XIf you run SunOS you won't have to edit ixocico.c. If you don't run SunOS Xyou're going to have the fun of porting this program. I put in a couple Xof #defines to get you started. Anyway, compile it with a simple X"make ixocico" and you're done with it. ixocico doesn't require many Xedits because tpaged gives it everything it needs to know. ixocico Xalso belongs in $TPAGE/ixocico X XCopy the sample "table" file and copy it to $DEFAULT_T (which should Xbe $TPAGE/table if you're smart). Read it. Then edit it to your Xrequirements. X XCopy the sample "schedule" file and copy it to $DEFAULT_S (which should Xbe $TPAGE/schedule if you're smart). Read it. Then edit it to your Xrequirements. You can edit this later or even skip this step if you Xdon't care about the schedule feature. X X3. Read the man page ("nroff -man tpage.l | more"). X X4. Create the $QUEUE_DIR directory (as defined in $TPAGE/tpage.pl and X$TPAGE/tpaged.pl) and do a chmod 0777 on it. Make sure that users on any Xmachine that should be able to run "tpage" can create files there and Xdelete at least their own files. If you use NFS you'll have to export Xthat directory. If you don't use NFS, you're smart. X X5. Set up a cron job that runs "startdaemon" every 20 minutes on the Xmachine with the modems. You might want to run it as root, you might Xwant to run it as any user that can access those modems. X X6. Read the man page ("nroff -man tpage.l | more"). X XTest it and you're done. Try: X Xtpage -d 1xxxyyyzzzz -p 123456 "this is a test" X(substitute your phone number and pin) X Xtpage alias "this is a second test" X(replace "alias" with a name from the "table" file) X Xtpage - - Xthis is a test using the schedule and stdin. X^D X X(this should page the "person on duty") X XBUGS: X X-------------------------- INSTALL PROBLEM -------------------------- XSome people have reported that in ixocico.c in the "main()" function Xneeds "sleep(2); send(modem, "\r"); sleep(2); send(modem, "\r");" added Xor their paging service doesn't respond. Here's where to add it: X X------------- Xthis code segment is from main() X need to send a few \r's to wake-up some paging terminals X------------ X Xprintf("Waiting for ID=\n"); X X /* send a CR every second until "ID=" comes back */ X failcnt = 10; X X/* CRs needed by PacTel Paging DJ */ X X-----> sleep(2); X-----> send(modem, "\r"); X-----> sleep(2); X-----> send(modem, "\r"); X X while (failcnt--) { X if (match(modem, "ID=", 5)){ X XSpecial thanks to Daryl Jones for reporting this. X X-------------------------- INSTALL PROBLEM -------------------------- XDo you run SCO Unix? Have you gotten ixocico to compile? Nobody Xelse has! Please join the ixo mailing list (ixo-request@warren.mentorg.com) Xand tell us what you did! X SHAR_EOF $TOUCH -am 0505135392 INSTALL && chmod 0664 INSTALL || echo "restore of INSTALL failed" # ============= tpage.l ============== echo "x - extracting tpage.l (Text)" sed 's/^X//' << 'SHAR_EOF' > tpage.l && X.ds l local X.TH TPAGE N "05 May 1992" X.BY "Tom Limoncelli, tal@warren.mentorg.com" X.SH NAME Xtpage \- front-end to Tom's Pager System X.br Xtpaged \- daemon that manages paging queue X.br Xixocico \- program that executes the IXO protocol X.SH SYNOPSIS X.B /usr/local/bin/tpage X[ X.B \-T Xtable file X] [ X.B \-S Xschedule file X] [ X.B \-U X] [ X.B \-d Xphone num X] [ X.B \-p Xpin X] [ X.B \-t X] [ X.B \-v X] [ X.B \-m X] [ X.B \-M X] [ X.B \-e X] X.B who X[ X.B message Xor X.B \- X] X.br X.B /home/adm/lib/tpage/tpaged X.br X.B /home/adm/lib/tpage/ixocico Xdev phonenum X.br X.SH DESCRIPTION X.I tpage Xis the front-end to the system. A user can run this program to Xsubmit a message to someone's pager. X.PP XThe program wants 4 Xthings: (1) the phone number to dial, (2) the person's PIN Xpin (Personal Identification Number), (3) who to email if the Xpage isn't accepted, and (4) the message. X.PP XOnce it has all that information, it dumps it into a file Xwhich is read by X.B tpaged Xwhich delivers the message to the paging service using Xthe appropriate protocol. tpaged selects the correct Xprogram for you. Each protocol is implemented as Xa separate program. Currently \fBixocico\fR Xis the only program to choose from, and it implements Xthe IXO protocol. X.PP XThe phone Xnumber (1) and PIN (2) can be specified with the X.B \-d Xand X.B \-p Xoptions, OR by specifying a name (which instructs tpage Xto look up the information in the pager table file), OR by Xspecifying the name ``\-'' which Xmeans ``whoever is on duty'' (which instructs tpage to find Xout who is on duty from the schedule file, and then look up Xtheir phone number and PIN in the pager table file). X.PP XSpecifying a phone number without a PIN (or vice-versa) results Xin the missing data being looked up in the pager table file. X.PP XA comma-separated list of names may be given. It is much more Xefficient to use a list of names than to do single pages. X.PP XNOTE: A name must always be specified, so if you use the X.B \-d Xand X.B \-p Xoptions you must also specify a name (such as ``foo'') which will Xbe ignored. Combining a list of names with the X.B \-d Xand/or X.B \-p Xoptions works in a logical but undefined manner. X.PP XTo specify who to email if there is a problem (3) use the X.B \-e Xoption. The default for this is to not send email to anyone. X.PP XThe message (4) can be specified on the command line or if ``-'' is Xgiven, stdin is read for the message. No matter how many Xbytes you give it, the high-bit is stripped, RETURNs and TABs are Xturned into spaces, and groups of spaces are turned into single Xspaces. The first 160 bytes (configurable in the program) is Xall that's sent, since that's all that the pager will display. X.PP XCommand-line options are: X.TP 10 X.BR \-T " table file" X\fItable file\fR is the file that has the Xtable of people and the info needed to communicate Xwith their pager. The default is /home/adm/lib/tpage/table. X.TP X.BR \-S " schedule file" X\fIschedule file\fR is the file that has the Xis the file that is used Xto find out who's on duty at this moment. This file is only Xconsulted if ``who'' is ``\-''. The default is X/home/adm/lib/tpage/schedule. X.TP X.B \-U XThis option marks the message as \fIurgent\fR. If the schedule Xlists that no one is on duty at that time but the message is Xmarked \fIurgent\fR, a secondary schedule is consulted. X.TP X.BR \-d " phone num" XIgnore what the table file says, use this phone number when Xdialing. If a list of people is specified, this phone number Xis used for the first person, the others will be looked Xup in the \-T file. X.TP X.BR \-p " pin" XIgnore what the table file says, use this PIN when transmitting Xthe message. XIf a list of people is specified, this phone number Xis used for the first person, the others will be looked Xup in the \-T file. X.TP X.B \-t XAct as a ``tee''. Copy stdin to stdout. If you give this Xoption and the message is not coming from X.TP X.B \-v XVerbose mode. Currently useless since there isn't anything Xextra worth printing. X.TP X.B \-m XParse the input as mail. Skip all the headers but extract the ``From'', X``Subject'', and ``Priority'' lines. If they exist, append to the Xbeginning of the message ``F: frominfo'', ``S: subject line'', X``P: priority''. ``F:'' and ``P:'' are clipped to be one screenful Xin length. They are all padded out to the end of the screen. X.TP X.B \-M XSkip ``mail quoted'' lines. Netnews and Mail often have other messages Xquoted by prefixing each line with greater than symbol. This option Xskips any input line that begins with zero or more whitespace charactors, Xfollowed by zero or more letters or numbers, followed by zero or more Xof \<, \>, {, or }. This should catch the normal quoting methods Xas well as anything the perverse superquote.el package Xfor GNU Emacs Xmight come up with. X.TP X.B \-e XOn error, send email to this person. If any errors happen when Xthe tpage command is beign run, the user is notified. This Xis for sending email when the page is being processed. In Xother words, if the PIN is incorrect. If the phone number is Xincorrect the tpaged daemon will keep redialing and redialing it Xtrying to figure out why it can't get through. X.PP X.IR tpaged Xtpage is a program that you don't need to know about. Your Xsysadmin should have installed it for you. It wakes up about Xevery 20 seconds, sees if there are any new messages to send Xout and tries to send them. It can understand multiple paging Xprotocols (tpage picks the best one for you) though it Xcurrently only knows about the IXO protocol. tpage can run Xas ``root'' but is often just run as ``daemon''. X.PP X.IR ixocico Xis the message transport program for the tpage system. It is called Xby X.B tpaged Xand told what device to use and what phone number to dial on Xthe command line. It Xgets the PINs and messages to send from stdin. X.PP XIt co-exists with the uucp programs fine as it uses the Xsame methods to lock the modems. It notices stale locks Xand blows them away. Not all locking features have been Xproven to work on HPUX, only SunOS. XIt will not wait for a modem to be unlocked. X.PP X.B tpaged Xwatches the output of X.B ixocico Xfor lines beginning with # to know success Xor failure of particular messages and of the entire batch. X.PP X.SH FILES X.ta 6c X.nf X/home/adm/lib/tpage/schedule schedule of who's on duty X/home/adm/lib/tpage/table table of people and their pager info X.SH SEE ALSO Xuucico(1), xkill(l) X.SH HISTORY XWritten by Tom Limoncelli (tal@Warren.MENTORG.COM) Xat Mentor Graphics Corporation, Silicon Design Division, XWarren, New Jersey. May be re-distributed only in it's unmodified Xform. X.SH BUGS XIf X.B \-d Xand X.B \-p Xare specified, a name still must be specified. X.PP XIt currently only compiles under SunOS even though some Xdefines are inserted so that it doesn't fail all over the Xplace on silly operating systems like HPUX. SHAR_EOF $TOUCH -am 0505135492 tpage.l && chmod 0700 tpage.l || echo "restore of tpage.l failed" # ============= tpage.pl ============== echo "x - extracting tpage.pl (Text)" sed 's/^X//' << 'SHAR_EOF' > tpage.pl && X#! /usr/local/bin/perl4.035 X X# tpage.pl -- front-end to tpage system. X# by Tom Limoncelli, tal@warren.mentorg.com X# Copyright (c) 1992, Tom Limoncelli X# The sources can be freely copied for non-commercial use only X# and only if they are unmodified. X X# $Header: /home/tal/work/beep2/RCS/tpage.pl,v 1.2 1992/09/21 20:11:51 root Exp $ X X# Version 2.0 -- See file HISTORY for details. X X# $Log: tpage.pl,v $ X# Revision 1.2 1992/09/21 20:11:51 root X# new tr's to remove high bits X# X# Revision 1.2 1992/09/21 20:11:51 root X# new tr's to remove high bits X# X# Revision 1.1 1992/09/21 20:09:37 root X# Initial revision X X#################################################################### X# X# Parameters that the user can set: X# X#################################################################### X X$debug = 0; X# leave that off X X$MAX_WINDOW = 16; X#This is the number of charactors at a time do you see on your X# pager. This is used when word-wrapping. X X$MAX_MESSAGE = 110; X# How many bytes can one message be. This must be less than 250 X# minus the length of your PIN. This is because each packet in the ixo X# protocol must be less than 250 chars. If you have a pager that can X# receive longer messages, you'll have to modify the ixocico.c program X# to handle the "packet continuation" feature. No biggie, just X# something that I didn't feel like implementing since I can't even X# test it with my pager. X X$DEFAULT_S = '/home/adm/lib/tpage/schedule'; X# (default: '/home/adm/lib/tpage/schedule') X# If you plan on using the schedule feature, this is the file X# name where beep2.pl will look for the schedule. It must be accessable X# on the machine that runs tpage.pl, not the machine that runs the X# daemon (tpaged.pl). X X$DEFAULT_T = '/home/adm/lib/tpage/table'; X# (default: '/home/adm/lib/tpage/table') X# If you plan on using the table feature (that is, store a list X# of people and their paging info), this is the file name where tpage.pl X# will look for the data. It must be accessable on the machine that X# runs tpage.pl, not the machine that runs the daemon (tpaged.pl). X X$QUEUE_DIR = '/home/adm/lib/tpage/pqueue/'; X# (default: '/home/adm/lib/tpage/pqueue/' X# This is the directory where messages will be queued. The trailing "/" X# is required. X X#################################################################### X# some helping functions X Xrequire("getopts.pl"); X Xsub strip_string { X local($s) = @_; Xprint "DEBUG: REMOVE_CONTROLS :", $s, ":\n" if $debug; X $s =~ tr/\200-\377/\000-\177/; # remove high-bit X $s =~ tr/\000-\037\177//d; # delete unprintables X $s =~ s/\s+/ /g; # change groups of white space into " " X $s =~ s/^ //; # remove spaces from the front X $s =~ s/ $//; # remove spaces from the end X Xprint "DEBUG: REMOVE_DONE :", $s, ":\n" if $debug; X return $s; X} X X#################################################################### X# Here's the actual program X X#################################################################### X# Get the command line options. X X# set the defaults X Xprint "\n"; X X# -S schedule file X$opt_S = $DEFAULT_S; X# -T pager table X$opt_T = $DEFAULT_T; X# -U use urgent schedule if no one is scheduled for that time. X$opt_U = 0; X# -d number to dial. (first name in list only) X$opt_d = ""; X# -p pager id to use. (first name in list only) X$opt_p = ""; X# -t tee all stdin into stdout. X$opt_t = 0; X# -v verbose mode. X$opt_v = 0; X# -m input will be in RFC822, skip boring stuff. X$opt_m = 0; X# -M like -m but also skip >-quoted text. X$opt_M = 0; X# -e if it errors, send email to this person. X$opt_e = ""; X X$line_from = ""; X$line_subj = ""; X$line_prio = ""; X Xdo Getopts('S:T:Ud:p:tvmMe:'); X X# get the wholist X$opt_wholist = shift (@ARGV); X$opt_wholist = "special" if $opt_d && $opt_p; X X#################################################################### X# Get the message (either on the command line or stdin; handle -t -m -M X X# bunch up all the rest X$opt_message = join(' ', @ARGV); Xprint "opt_message = :$opt_message:\n" if $debug; X$opt_message = &strip_string( $opt_message ) if $opt_message; Xprint "opt_message = :$opt_message:\n" if $debug; Xdie "$0: No message. Cat got your tongue?" if ( $opt_message eq "" ); X Xdie "$0: Can't use -m/-M and have message on the command line" X if ($opt_m || $opt_M) && $opt_message ne '-'; X X# maybe get message from stdin, echoing to stdout if $opt_t; Xif ($opt_message eq '-') { X $opt_message = ''; X # handle -m headers first X if ($opt_m) { X print "DEBUG: Doing -m work\n" if $debug; X local($line) = ""; X while (<>) { X if ( /^\S/ || /^$/ ) { # start of new header, do previous one X $line_from = substr($line, 6) if $line =~ /^From/; X $line_subj = substr($line, 9) if $line =~ /^Subject: /; X $line_prio = substr($line, 10) if $line =~ /^Priority: /; X $line = $_; X } else { X $line .= $_; X } X last if /^$/; # end of headers, start processing X } X } X $line_from = &strip_string( $line_from ) if $line_from; X $line_subj = &strip_string( $line_subj ) if $line_subj; X $line_prio = &strip_string( $line_prio ) if $line_prio; X X while (<>) { X# -M means skip if the line is news quoted email. X# a line is news quoted if it begins with one of the following: X# [white] [word] quote X# where "white" is any amount of whitespace (zero or one times) X# where word is any letters/numbers (userid) (zero or one times) X# where quote is any of >, <, }, or {. X next if $opt_M && /^\s*\S*[\>\}\<\{]/; X print if $opt_t; X $_ = &strip_string( $_ ); X $opt_message .= $_; X $opt_message .= " "; X # once we've got quite a bunch, screw the rest. X if ( length($opt_message) > ($MAX_MESSAGE * 8)) { X while (<>) { print if $opt_t; } X } X } X} X X#################################################################### X# massage the message X Xif ($debug) { X print "DEBUG: pre-processed messages\n"; X print "FROM=:$line_from:\n"; X print "PRIO=:$line_prio:\n"; X print "SUBJ=:$line_subj:\n"; X print "MESS=:$opt_message:\n"; X} X X$line_from = substr( "F: " . $line_from . ' ' x $MAX_WINDOW, X 0, $MAX_WINDOW) if $line_from; # pad to display size X X$line_prio = substr( "P: " . $line_prio . ' ' x $MAX_WINDOW, X 0, $MAX_WINDOW) if $line_prio; # pad to display size X X$l = $MAX_WINDOW * int ((length($line_subj)+$MAX_WINDOW+2) / $MAX_WINDOW); X$line_subj = substr( "S: " . $line_subj . ' ' x $MAX_WINDOW, X 0, $l) if $line_subj; # pad to display size X X$opt_message = &strip_string( $opt_message ); X# put it all together X$the_message = substr( $line_prio . $line_from . $line_subj . $opt_message, 0, $MAX_MESSAGE - 1); X Xif ($debug) { X print "DEBUG: post-processed messages\n"; X print "FROM=:$line_from:\n"; X print "PRIO=:$line_prio:\n"; X print "SUBJ=:$line_subj:\n"; X print "MESS=:$opt_message:\n"; X print "COMPLETE=:$the_message:\n"; X} X X#################################################################### X# At this point we can do some more of the sanity checking. X X#die "$0: Conflicting verbosity levels" if ($opt_s && ($opt_v || $opt_V)); Xdie "$0: Schedule file $opt_S can't be read/found" X unless ( ($opt_wholist eq '-') || (-r $opt_S && -r $opt_T) ); Xdie "$0: Pager table $opt_T can't be read" X unless ($opt_d && $opt_p) || ( -r $opt_T ); X X#################################################################### X# use the schedule to fill in "who" if we need. X Xif ($opt_wholist eq '-') { X local($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); X local($l) = $wday; X local($h) = $hour * 2 + int ($hour / 30) + 1; X local($w,$found1) = 0; X Xprint "L = $l\n" if $debug; Xprint "H = $h\n" if $debug; Xprint "U = $opt_U\n" if $debug; X X # Read from schedule until you hit a line beginning with $l. X # At that point, get the char $h bytes in. If that byte is "-", X # and $opt_U, keep going. X print "\nChecking schedule:\n\n"; X open(SCHED, "<$opt_S") || die "Can't open $opt_S: $!"; X while (1) { X $w = ''; X while () { X last if /^${l}/; X } X $w = substr($_, $h, 1); X $found1 = 1 if $w; # we found one! X next if $opt_U && $w eq '-'; X last; X } X X die "$0: Schedule doesn't have a line for this day of the week.\n" unless $found1; X die "$0: No one is assigned to be on duty at this time.\n" if $w eq '-'; X X # Now search until a line begins with $w= and assign line to wholist X $opt_wholist = ''; X while () { X next unless /^${w}\=/; X chop( $opt_wholist = substr($_, 2) ); X } X die "$0: Schedule error: No people assigned to '" . $w . "'\n" unless $opt_wholist; X close SCHED; X} X X#################################################################### X# we we still don't know who to call, bail out. X Xdie "$0: The schedule didn't specify anyone to call!" X unless ($opt_wholist) || ($opt_d && $opt_p); Xdie "$0: There isn't anyone scheduled for this time of day." X if $opt_wholist eq '-'; X X#################################################################### X# rotate through "$opt_wholist" and queue each person X X$cnt = 0; Xforeach $who ( split(',', $opt_wholist) ) { X $cnt++; X X # look up "who"'s information X open(TABL, "<$opt_T") || die "Can't open $opt_T: $!"; X while () { X next if /^#/; X chop; X local($name,$phonen,$phonea,$pin) = split; X if ($name eq $who) { X $opt_d = $phonea unless $opt_d; # might have it from ARGV X $opt_p = $pin unless $opt_p; # might have it from ARGV X print "Got $who is :$opt_d:$opt_p:\n" if $debug; X last; X } X } X close TABL; X X die "$0: We were not able to find a phone number for $who.\n" unless $opt_d; X die "$0: We were not able to find a PIN for $who.\n" unless $opt_p; X X # write into the queue the proper information. X chop( $thishost = `hostname` ); X $qname = $QUEUE_DIR . "P" . $thishost . time . $cnt; X print "QUEUE=$qname\n" if $debug; X local($um) = umask 2; X open(QU, ">$qname" ) || die "Can't open $qname for writing: $!"; X umask $um; X print QU "A\n"; X print QU $opt_d, "\n"; X print QU $opt_p, "\n"; X if ($opt_e eq '-') { # '-' means send errors to $who, X print QU $who, "\n"; X } else { X print QU $opt_e, "\n"; X } X print QU $the_message, "\n"; X print QU "X\n"; X close QU; X print "Message queued for $who: $the_message\n"; X X # zap the phone# and PIN so that ARGV's info only effects us once. X $opt_d = ""; X $opt_p = ""; X} X Xprint "\n"; SHAR_EOF $TOUCH -am 0922135192 tpage.pl && chmod 0555 tpage.pl || echo "restore of tpage.pl failed" # ============= tpaged.pl ============== echo "x - extracting tpaged.pl (Text)" sed 's/^X//' << 'SHAR_EOF' > tpaged.pl && X#! /usr/local/bin/perl4.019 X X# tpaged -- back-end to tpage system. X# by Tom Limoncelli, tal@warren.mentorg.com X# Copyright (c) 1992, Tom Limoncelli X# The sources can be freely copied for non-commercial use only X# and only if they are unmodified. X X# Version 2.0 -- See file HISTORY for details. X X#################################################################### X# X# Parameters that the user can set: X# X#################################################################### X X$debug = 0; X# $| = 1; open( STDOUT, ">/home/adm/lib/tpage/log.txt" ) if $debug; $| = 1; X$QUEUE_DIR = '/home/adm/lib/tpage/pqueue/'; # same as in tpage.pl X#$IXOCICO = '/home/tal/work/beep2/ixocico'; # where is ixocico? X$IXOCICO = '/home/adm/lib/tpage/ixocico'; # where is ixocico? X$MAIL = '/usr/ucb/mail'; # which mail to use? X # Recommended mailers: SunOS & BSD's: /usr/ucb/mail, AT&T Unix's xmail X # Not recommended mailers: /bin/mail X X# list of devices to rotate through. X@DEVICES = ( "/dev/ttyz4" ); # currently they are all spoken X# to at the same speed and same parameters. Some day I'll set up X# a modemtab system, but I don't think more than one modem is X# really needed for this system. X X# amount of time to sleep between scans of the queue X$SLEEP_TIME = 150; # 2.5 minutes X$SLEEP_TIME = 10 if $debug; # smaller when I'm debugging X# Small amount of time to wait between finding anything in the queue X# and doing a real scan of the queue. X$MULT_SLEEP_TIME = 10; X X#################################################################### X# QUEUE FILES FORMAT: X# X# Files in the queue have the name of the format in the X# first line. Currently there is only one format and it X# is named "A". The first line marks it as the "A" format. X# a subroutine called read_format_A reads this format. Other X# formats can be written (see comments by read_format_A) X# X# The "A" format: X# line contents X# 1: A\n X# 2: number to dial\n X# 3: pin\n X# 4: entire message\n X# 5: X\n X X# read_format_* -- modules that read various data formats. X# Currently implemented: The "A" format. X# do_proto_* -- modules that do various beeper protocols. X# Currently implmented: the ixo protocol. X# Future protocols: numeric-only pagers. X X#################################################################### X# Here's the actual program X X# define some globals X Xlocal(%protocols); X Xwhile (1) { X local ($first, @allfiles, @anyfiles); X X # We could scoop up all the files and process them, but chances X # are if one file is found, more are on the way. So, instead X # we scoop, if any are found we sleep 5 seconds and re-scoop. X X # wait for any files to appear. X while (1) { X @anyfiles = &scan_queue; X print "DEBUG: anyfiles= ", join(' ', @anyfiles), "\n" if $debug; X X if ($#anyfiles!=-1) { # files? take a rest and then process. X sleep $MULT_SLEEP_TIME unless $debug; X last; X } else { # no files? hibernate. X sleep $SLEEP_TIME; X next; X } X } X X # re-get the files in the queue X @allfiles = &scan_queue; X print "DEBUG: allfiles= ", join(' ', @allfiles), "\n" if $debug; X X # get all the data out of the queue'd files. X foreach $file (@allfiles) { X print "DEBUG: Doing $file\n" if $debug; X open(DATA, "<" . $QUEUE_DIR . $file) || print "Can't open $file: $!"; X chop( $first = ); Xprint "DEBUG: first=$first\n" if $debug; X eval "do read_format_$first()"; X } X X # process all the extracted data (do_protocol_* should delete the files) X foreach $proto (keys %protocols) { X eval "do do_protocol_$proto()"; X delete $protocols{ $proto }; X sleep $SLEEP_TIME; X } X} X X# scan the queue for entries (avoid "blacklisted" files) Xsub scan_queue { X local(@files); X # scan the directory for "P files (pager files) X opendir(QDIR, $QUEUE_DIR) || die "$0: Can't open $QUEUE_DIR: $!"; X @files = grep( /^P/, readdir(QDIR) ); X closedir(QDIR); X print "DEBUG: filescan= ", join(' ', @files), "\n" if $debug; X # remove the blacklisted files X @files = grep( ! defined $blacklist_data{ $_ }, @files); X print "DEBUG: goodfiles= ", join(' ', @files), "\n" if $debug; X # return the files X @files; X} X X# blacklist a file in the queue (couldn't delete it for some reason X# and we don't want to repeat it) Xsub blacklist { X local($file) = @_; X $blacklist_data{ $file } = 1; X} X X# Each read_format_ must: X# read from and then close(DATA). X# %protocols{ protocol name } = 1 (for the protocol to use) X# and stuff the right data into the right variables for that protocol X# to use. X Xsub read_format_A X{ X local($dial,$pin,$error,$mess,$X); # $file is by sideeffect X print "DEBUG: reading format A\n" if $debug; X chop( $dial = ); X chop( $pin = ); X chop( $error = ); X chop( $mess = ); X chop( $X = ); X X return if $X ne "X"; # file isn't in correct format or isn't done. X return if $dial eq ""; X return if $pin eq ""; X return if $mess eq ""; X X $protocols{ 'ixo' } = 1; X &ixo_mesg_append( $dial, $pin, $error, $mess, $file ); X} X X# Each do_protocol_ must: X# delete files out of the queue that are successful. X# delete files out of the queue that are aged. X# clean up so that the routine can be called again. X Xsub do_protocol_ixo { X print "DEBUG: doing protocol IXO\n" if $debug; X local($pin, $error, $mess, $file, $cmd, $status, $index); X local($general_reject, $general_error_message); X # build the temp file and the command line X local($tmpfile) = "/tmp/tpaged.$$"; X foreach $dial ( &ixo_listphones ) { X print "DEBUG: Number to dial is $dial\n" if $debug; X X # fill the data file X open(IX, ">$tmpfile" ) || die "$0: Can't create $tmpfile: $!"; X foreach $index ( &ixo_listindexes( $dial ) ) { X ($pin, $error, $mess, $file) = &ixo_mesg_get( $dial, $index ); X # put it in the file for ixocico to use X print IX "$pin\n$mess\n"; X } X close IX; X X print "DEBUG: messages to send", &ixo_listindexes( $dial ), "\n" if $debug; X X $general_reject = 1; # when done, 1=cancel remaining; 0=retry remaining X $general_error_message = "SHOULD NOT HAPPEN"; # if all messages are cancelled X X $cmd = $IXOCICO . " <" . $tmpfile . " " X . push(@DEVICES, shift @DEVICES) . " " . $dial; X print "DEBUG: About to execute: $cmd\n" if $debug; X open(IX, $cmd . "|") || die "$0: Can't execute ixocico: $!"; X X while () { X print if $debug; X next unless /^#/; X X print unless $debug; X X /^#WRONGARGS / && X die("$0: Major program bug: $!"); X /^#NOCONN / && do { X printf("$0: Nobody answered the phone!\n") if $debug; X $general_reject = 0; X last; X }; X /^#UNKNOWNPROTO / && do { X print "$0: Uhhh, are you sure that's a pager service?\n" if $debug; X $general_reject = 1; X $general_error_message = "other end using different protocol"; X last; X }; X /^\#MESOK (\d) / && do { X $index = $1; X print "DEBUG: message $index done.\n" if $debug; X X ($pin, $error, $mess, $file) = &ixo_mesg_get( $dial, $index ); X print "DEBUG: ERROR=$error; FILE=$file\n" if $debug; X X print "DEBUG: unlinking " . $QUEUE_DIR . $file . "\n" if $debug; X $status = unlink $QUEUE_DIR . $file; X print "DEBUG: unlink status=$status; $!\n" if $debug; X &blacklist( $file) unless $status; X X # remove from queue X &ixo_mesg_delete( $dial, $index ); X }; X /^#MESREJECT (\d) / && do { # very similar to #MESOK X $index = $1; X print "DEBUG: message $index rejected.\n" if $debug; X X ($pin, $error, $mess, $file) = &ixo_mesg_get( $dial, $index ); X print "DEBUG: ERROR=$error; FILE=$file\n" if $debug; X X # notify anyone that wants to know about failures X if ($error + 0) { X $cmd = "$MAIL <" X . $QUEUE_DIR . $file X . " -s 'TPAGE_MESSAGE: request rejected by service' " X . $error; X print "DEBUG: About to execute $cmd\n" if $debug; X system $cmd; X } X X print "DEBUG: unlinking " . $QUEUE_DIR . $file . "\n" if $debug; X $status = unlink $QUEUE_DIR . $file; X print "DEBUG: unlink status=$status; $!\n" if $debug; X &blacklist( $file) unless $status; X X # remove from queue X &ixo_mesg_delete( $dial, $index ); X }; X /^#FORDIS / && do { X print "Forced disconnect from server.\n" if $debug; X $general_reject = 1; X $general_error_message = "other end requesting disconnect"; X last; X }; X /^#PROTERR / && do { X print "Server not following protocol.\n" if $debug; X $general_reject = 1; X $general_error_message = "other end having a protocol error"; X last; X }; X ( /^#DONE / || /#BYE / ) && do { X print "Done with sending batch. Waiting BYE.\n" if $debug; X $general_reject = 0; X $general_error_message = "been told we're done but weren't". X next; X }; X /^#WRONGANY / && do { X print "We've been notified that one of the batch may have been not xmited.\n(great protocol, eh?)\n" if $debug; X next; X }; X /^#BADQUEUE / && do { X die "$0: Programmer error. Data in queue is bad: $_\n"; X }; X /^#MODOPEN / && do { X print "Modem can't be opened\n" if $debug; X $general_reject = 0; X last; X }; X /^#PACKLEN / && do { X die "$0: Protocol error. Should never happen: $_\n"; X }; X /^#GOTMESSEQ / && do { X print "MESSAGE: $_\n" if $debug; X }; X /^#LONELY / && do { X print "Hello? Hello? Either I'm getting the silent treatment or the server is dead." if $debug; X $general_reject = 0; X last; X }; X } X close IX; X unlink $tmpfile; X X print "DEBUG: rejecting remaining messages\n" if $debug; X # now reject remaining messages X foreach $index ( &ixo_listindexes( $dial) ) { X # if general_reject then we have work to do X if ($general_reject) { X print "DEBUG: removing $dial:$index\n" if $debug; X ($pin, $error, $mess, $file) = &ixo_mesg_get( $dial, $index ); X ###### mail a warning X if ($error + 0) { X $cmd = "$MAIL <" X . $QUEUE_DIR . $file X . " -s 'TPAGE_MESSAGE: unprocessed message deleted due to " X . $general_error_message . "' " X . $error; X print "DEBUG: About to execute $cmd\n" if $debug; X system $cmd; X } X ###### make sure it gets deleted X print "DEBUG: unlinking (leftover) " . $QUEUE_DIR . $file . "\n" if $debug; X $status = unlink $QUEUE_DIR . $file; X print "DEBUG: unlink status=$status; $!\n" if $debug; X &blacklist( $file) unless $status; X } X print "DEBUG: deleting from memory $dial:$index\n" if $debug; X # delete it from the ixo list X &ixo_mesg_delete( $dial, $index ); X } X # at this point %ixo_data should be empty X &ixo_end_asserts; X X X # now do the next phone number X } X} X Xsub ixo_end_asserts { X # test a couple assertions X print "DEBUG: testing assertions\n" if $debug; X X # $ixo_count{ $dial } should be zero X die "$0: bug1\n" if $ixo_count{ $dial }; X X # %ixo_data should be empty at this point X die "$0: bug2\n" if grep(1,keys %ixo_data); # fast key counter X} X Xsub ixo_mesg_append { X local($dial, $pin, $error, $mess, $file, $count) = @_; X print "APPEND: dial=$dial pin=$pin error=$error file=$file mess=$mess\n" if $debug; X $count = 0 + $ixo_count{ $dial }++; X $ixo_data{ "$dial:$count" } = "$pin\n$error\n$mess\n$file"; X print "APPEND: data=", $ixo_data{ "$dial:$count" }, "\n" if $debug; X} X Xsub ixo_mesg_get { X local($dial, $index) = @_; X local($pin, $error, $mess, $file, @list); X print "GET: dial=$dial index=$index\n" if $debug; X @list = split( '\n', $ixo_data{ "$dial:$index" } ); X ($pin, $error, $mess, $file) = @list; X print "GET: pin=$pin error=$error file=$file mess=$mess\n" if $debug; X @list; X} X Xsub ixo_mesg_delete { X local($dial, $index) = @_; X print "DELETE: dial=$dial, index=$index\n" if $debug; X delete $ixo_data{ "$dial:$index" }; X $ixo_count{ $dial }--; X} X Xsub ixo_listindexes { X local($dial) = @_; X X # gather and sort the second field X sort grep( s/^$dial:(.+)/$1/, keys %ixo_data ); X} X Xsub ixo_listphones { X local(@list); X local($l) = undef; X X # gather and sort the first field. X @list = sort grep( s/^(.+):.+$/$1/, keys %ixo_data ); X # uniq them X @list = grep (!($_ eq $l || ($l = $_, 0)), @list ); X # return them X @list; X} SHAR_EOF $TOUCH -am 0922132292 tpaged.pl && chmod 0775 tpaged.pl || echo "restore of tpaged.pl failed" # ============= startdaemon ============== echo "x - extracting startdaemon (Text)" sed 's/^X//' << 'SHAR_EOF' > startdaemon && X#! /usr/local/bin/perl4.019 X X# "start daemon" -- Start the tpaged daemon if it isn't running. X# by Tom Limoncelli, tal@warren.mentorg.com X# Copyright (c) 1992, Tom Limoncelli X X X$BSD = -f '/vmunix'; X$pscmd = $BSD ? "ps -auxww" : "ps -ef"; X Xopen(PS, "$pscmd|") || die "Can't run ps: !$"; X$title = ; X$found = 0; Xwhile () { X chop; X $found = 1 if /tpaged/; X} X Xif (!$found) { X unless (fork) { # this is the child X unless (fork) { # this is the child's child X sleep 1 until getppid == 1; X system "/home/adm/lib/tpage/tpaged &"; X exit 0; X } X exit 0; X } X print "Started tpaged\n"; X} SHAR_EOF $TOUCH -am 0505135492 startdaemon && chmod 0775 startdaemon || echo "restore of startdaemon failed" # ============= ixocico.c ============== echo "x - extracting ixocico.c (Text)" sed 's/^X//' << 'SHAR_EOF' > ixocico.c && X/* ixocico -- IXO protocol call-in call-out. X** by Tom Limoncelli, tal@warren.mentorg.com X** Copyright (c) 1992, Tom Limoncelli X** The sources can be freely copied for non-commercial use only X** and only if they are unmodified. X** X** Version 2.0 -- See file HISTORY for details. X** $Id: ixocico.c,v 1.4 1992/09/22 17:31:01 root Exp $ X X$Log: ixocico.c,v $ X * Revision 1.4 1992/09/22 17:31:01 root X * set serial parameters to 300bps, 7e1 X * X * Revision 1.3 1992/09/21 19:41:31 root X * made a small change to a comment X * X * Revision 1.2 1992/09/21 19:40:22 root X * changed parity to ODD X * X * Revision 1.1 1992/09/21 19:38:02 root X * Initial revision X * X X*/ X X/****************************************************************/ X/* USER CONFIGURABLE OPTIONS: */ X X/* this should be "#define" if you use SunOS, or "#undef" if you X** use HPUX. This controls the name of LOCKDIR and if getpriority() X** is used. I'm sure more needs to be done, but that's a small start. X*/ X#define REAL_OS X X#ifdef REAL_OS X#define LOCKDIR "/var/spool/uucp" X#else X#define LOCKDIR "/usr/spool/locks" X/* That may not be correct */ X#endif X X/* not talking to the modem correctly? Try mucking with Xthe grabmodem() routine. */ X X/* END OF USER CONFIGURABLE OPTIONS */ X/****************************************************************/ X X#include X#include X#include X/* #include */ X#include X#include X#include X X#ifdef REAL_OS X#include /* required for */ X#include /* required for getpriority() */ X#endif X X/* ASCII constants */ X#define STX (2) X#define EOT (4) X#define ACK (6) X#define LF (10) X#define CR (13) X#define NAK (21) X#define ESC (27) X#define RS (30) X X#define MAX_PACKET (10000) /* we'll never get a packet this big */ X#define MAXLINE (1000) X X/* only two little global variables, how's that? */ X Xint modem = 0; Xchar *lockname = NULL; X X/* print a string without worrying about unprintable charactors */ Xvoid Xsafeprint(str) Xchar *str; X{ X while (*str) { X if (isgraph(*str)) X (void) fprintf(stdout, "%c", *str); X else { X switch (*str) { X case LF: X (void) fprintf(stdout, "\\n"); X break; X case CR: X (void) fprintf(stdout, "\\r"); X break; X case 32: X (void) fprintf(stdout, "\\s"); X break; X default: X (void) fprintf(stdout, "\\%d", *str); X break; X } X } X str++; X } X fflush(stdout); X} X X/* calculate checksum of a packet */ Xchar *checksum(pk) Xchar *pk; X{ X static char check[10]; X int sum = 0; X X for (;*pk; pk++) sum += *pk; X check[2] = '0' + (sum & 15); sum = sum >> 4; X check[1] = '0' + (sum & 15); sum = sum >> 4; X check[0] = '0' + (sum & 15); X check[3] = 0; X Xprintf("CHECKSUM=:"); safeprint(check); printf(":\n"); X return check; X} X X/* open the modem. You should have done a lockmodem() first */ Xint grabmodem(dev) Xchar *dev; X{ X struct termios ti; X int modem; X X errno = 0; X modem = open(dev, O_RDWR, 0); X if (errno) { X printf("#MODOPEN modem can't be opened\n"); X return 0; X } X X /* set tty params to 300bps, even parity, 7-1-e */ X errno = 0; X ioctl(modem, TCGETS, &ti); X if (errno) { X close(modem); X return 0; X } X ti.c_iflag |= IGNBRK; /* ignore breaks */ X /* ti.c_iflag |= IGNPAR; *//* ignore parity */ X ti.c_iflag &= ~INPCK; /* ignore parity errors */ X ti.c_iflag |= ISTRIP; /* strip 8th bit */ X ti.c_iflag &= ~INLCR; /* don't cr->nl */ X ti.c_iflag &= ~ICRNL; /* don't cr->nl */ X ti.c_iflag &= ~IGNCR; /* don't ignore cr */ X /* ti.c_iflag &= ~IXON; *//* don't do xon */ X /* ti.c_iflag &= ~IXOFF; *//* don't do xoff */ X X ti.c_oflag &= ~OPOST; /* don't post-process */ X X ti.c_cflag &= ~CBAUD; X ti.c_cflag |= B300; /* baud=300 */ X /* ti.c_cflag |= B1200; /* baud=1200 */ X X ti.c_cflag &= ~CSIZE; /* 7-bit bytes */ X ti.c_cflag |= CS7; X/* ti.c_cflag |= CS8; */ X X ti.c_cflag &= ~CSTOPB; /* one stop bit */ X/* ti.c_cflag |= CSTOPB; /* two stop bit */ X X ti.c_cflag |= PARENB; /* parity */ X ti.c_cflag &= ~PARODD; /* even parity */ X/* ti.c_cflag |= PARODD; /* odd parity */ X X ti.c_cflag |= HUPCL; /* hang up on last close */ X /* ti.c_cflag |= CRTSCTS; *//* hardware handshaking */ X ti.c_cc[VMIN] = 0; /* read() can get as few as 0 bytes */ X ti.c_cc[VTIME] = 50; /* time out at 5 seconds no matter what */ X X ti.c_lflag &= ~ISIG; /* disable signals */ X ti.c_lflag &= ~ICANON; /* disable signals */ X ti.c_lflag &= ~ECHO; /* don't echo */ X X errno = 0; X ioctl(modem, TCSETS, &ti); X if (errno) { X close(modem); X return 0; X } X return modem; X} X X/* send data to the modem */ Xvoid send(fd, str) Xint fd; Xchar *str; X{ X printf("Sending: :"); safeprint(str); printf(":\n", str); X write(fd, str, strlen(str)); X} X X/* wait for a particular string from the modem (err = # of retries permitted ) */ Xint match(mod, str, err) X int mod; X char *str; X int err; X{ X int len; X char c; X Xprintf("MATCHING on :"); safeprint(str); printf(":\n", str); X X while (1) { X c = 0; X/* printf("waiting for :%c:\n", *str); */ X len = read(mod, &c, 1); X if (len) { X/* printf("got=%d:%c\n", c,c); */ X if (c == *str) { X str++; X } X if (!(*str)) break; /* matched all? Exit loop */ X } X if (!err--) { X printf("NOT MATCHED\n"); X return 1; X } X } Xprintf("MATCHED\n"); X return 0; X} X X/* hang up the modem */ Xvoid Xhangup_modem(fd) Xint fd; X{ X sleep(3); X send(fd, "+++"); X sleep(3); X send(fd, "ATH\r"); X sleep(1); X} X X/* unlock the modem */ Xvoid unlockmodem(name) Xchar *name; X{ X printf("Unlocking modem.\n"); X (void)unlink(name); X return; X} X X/* clean up and leave this program */ Xvoid bail_out() X{ X if (modem) { X hangup_modem(modem); X close(modem); X } X if (lockname) unlockmodem(lockname); X exit(0); X} X X/* lock the modem OR DIE*/ Xchar *lockmodem(dev) Xchar *dev; X{ X int lock, pid; X int failcnt = 3; X char waitcnt = 0; X char *lkn, lname[200]; X X strcpy(lname, LOCKDIR); X strcat(lname, "/LCK.."); X strcat(lname, 1 + rindex(dev, '/')); X Xprintf("Lockfile = %s\n", lname); X lkn = strdup(lname); X while (failcnt--) { X errno = 0; X lock = open(lname, O_CREAT | O_WRONLY | O_EXCL, 0777); X if (lock == -1) { X#ifdef REAL_OS X printf("Modem is locked, attempting to steal.\n"); X /* locked, let's read the cookie in the lock */ X pid = 0; X if ((lock = open(lname, O_RDONLY)) != -1) { X (void)read(lock, &pid, sizeof(int) ); X printf("Device is locked by process %d\n", pid); X close(lock); X } X printf("Lock = %d\n", lock); X if (pid < 3) { X printf("#MODOPEN device is locked by pid < 3\n"); X bail_out(); X } X /* see if the process still is alive */ X errno = 0; X (void) getpriority(PRIO_PROCESS, pid); X if (errno == ESRCH) { /* lock process dead, let's go! */ X if (unlink(lname)) { X printf("#MODOPEN Can't steal lock.\n"); X bail_out(); X } else { X printf("Lock is stale, stealing!\n"); X } X } else { X printf("#MODOPEN Valid lock in the way.\n"); X bail_out(); X } X#else X printf("#MODOPEN it's locked, I'm out of here!\n"); X bail_out(); X#endif X } else { X /* lock opened, stuff and go */ X pid = getpid(); X write(lock, &pid, sizeof(int)); X close(lock); X break; X } X } X if (failcnt==-1) { X printf("#MODOPEN Couldn't lock modem after many tries.\n"); X bail_out(); X } X return lkn; X} X X/* get a line from stdin OR DIE */ Xchar *getline(line) Xchar *line; X{ X int len; X char *r; X X /* get a line, if EOF return 0 */ X if (!(r = fgets(line, MAXLINE, stdin))) return 0; X X printf("Data in queue=:"); safeprint(line); printf(":\n", line); X X if (!(len = strlen(line))) { X printf("#BADQUEUE Blank line in queued data\n"); X bail_out(); X } X X if (line[len-1] == '\n') { X line[len-1] = 0; X } else { X /* if fgets didn't return a string ending in \n */ X printf("#BADQUEUE Data in queue has line too long\n"); X bail_out(); X } X return r; X} X X/* Loop until you get a valid packet. If you get a "message sequence" then Xdisplay it. If you get an invalid pack DIE */ X Xvoid getpacket(fd, str) Xint fd; Xchar *str; X{ X int max; X char c; X int len; X char *buf = str; X int err; X X *str = 0; X err=50; /* permit up to 500 message sequences or bad packets */ X while (err--) { X printf("Getting packet\n"); X max = MAX_PACKET; X while (read(fd, &c, 1) == 1) { X if (c == LF) continue; /* skip LF's */ X if (c == CR) { X /* don't actually put CR in the string */ X break; X } X *buf++ = c; X max--; X if (!max) { X /* packet was too long */ X printf("#PACKLEN packet was too long\n"); X bail_out(); X } X } X *buf = 0; X len = buf - str; X if (len) { X printf("Got packet--length=%d\n", len); X if (isgraph(*str)) { X printf("#GOTMESSEQ message sequece=:"); X safeprint(str); X printf(":\n"); X return; /* ddj 4/30 */ X } else { X /* print packet contents */ X printf("packet is data=:"); safeprint(str); printf(":013:\n"); X return; X } X } X } X printf("#LONELY It's too quiet in here. Disconnecting.\n"); X bail_out(); X} X Xint main(argc, argv) Xint argc; Xchar *argv[]; X{ X char dialstring[MAX_PACKET]; X char *pack = dialstring; /* use same workspace */ X char line[MAXLINE+1]; X char pin[MAXLINE+1]; X char mesg[MAXLINE+1]; X int mesnum, failcnt, len; X char c; X X /* check arguments */ X if (argc != 3) { X printf("#WRONGARGS wrong number of arguments\n"); X bail_out(); X } X X /* lock modem or die */ X lockname = lockmodem( argv[1] ); X /* open modem or die */ X printf("opening modem\n"); X modem = grabmodem( argv[1] ); X if (!modem) bail_out(); X X /* see if modem is awake or hangup; after 3 tries die */ X failcnt = 3; X while (failcnt--) { X send(modem, "AT\r"); X if (match(modem, "OK", 6)) { X printf("No response. Hang up and try again.\n"); X hangup_modem(modem); X while (read(modem, &c, 1)) { /*DONOTHING*/ }; X } else break; X } X if (failcnt==-1) bail_out(); X Xprintf("dialing\n"); X X /* send the "A" of "ATDT" */ X do { X send(modem, "A"); X } while (match(modem, "A", 2)); X /* send the rest of the dial string */ X sprintf( dialstring, "TDT%s\r", argv[2] ); X send(modem, dialstring); X (void) match(modem, argv[2], 10); X X /* wait for the modem to connect */ X printf("waiting for CONNECT\n"); X if (match(modem, "CONNECT", 30)) { X printf("#NOCONN no connect\n"); X bail_out(); X } X Xprintf("Waiting for ID=\n"); X X /* send a CR ever second until "ID=" comes back */ X failcnt = 10; X /* CRs needed by PacTel DJ */ X send(modem, "\r"); X /* end of DJ */ X while (failcnt--) { X if (match(modem, "ID=", 4)) send(modem, "\r"); X else break; X } X if (failcnt==-1) bail_out(); X Xprintf("Logging in\n"); X X failcnt = 3; X while (failcnt--) { X /* switch to "automatic protocol" */ X printf("Sending ESC\n"); X write(modem, "\033", 1); X send(modem, "PG1000000\r"); X X printf("Waiting for acceptance (aren't we all)\n"); X getpacket(modem, pack); X len = strlen(pack); X if ((len==1) && (*pack == ACK)) { X printf("login successful\n"); X break; X } X if ((len==1) && (*pack == NAK)) { X printf("retrying to log in.\n"); X continue; X } X if ((len==2) && (*pack == ESC) && (pack[1] == EOT)) { X printf("forced disconnect\n"); X bail_out(); X } X printf("#UNKNOWNPROTO not the response we're looking for. Disconnecting.\n"); X bail_out(); X } X if (failcnt==-1) bail_out(); X Xprintf("waiting for message go ahead\n"); X failcnt=40; X while (failcnt--) { X getpacket(modem, pack); X if (!strcmp(pack, "\033[p")) break; X } X if (failcnt==-1) bail_out(); X printf("got message go-ahead\n"); X X for (mesnum=0; getline(pin); ) { X getline(mesg); X failcnt=100; X while (failcnt--) { X /* build the packet to be sent */ X pack[0] = STX; X pack[1] = 0; X strcat(pack, pin); X strcat(pack, "\r"); X strcat(pack, mesg); X strcat(pack, "\r\3"); /* CR then ETX */ X strcat(pack, checksum(pack)); X strcat(pack, "\r"); X send(modem, pack); X X /* wait for response and deal */ X printf("waiting for validation\n"); X getpacket(modem, pack); X len = strlen(pack); X if ((len==1)&&(*pack==ACK)) { X printf("#MESOK %d message xmitted fine\n", mesnum++); X break; X } else if ((len==1)&&(*pack==NAK)) { X printf("re-retrying message %d\n", mesnum); X } else if ((len==1)&&(*pack==RS)) { X printf("#MESREJECT %d message rejected\n", mesnum++); X break; X } else if ((len==2)&&(*pack==ESC)&&(*pack==EOT)) { X printf("#FORDIS forced disconnect\n"); X bail_out(); X } X } X if (failcnt==-1) { X printf("#PROTERR couldn't send packets\n"); X bail_out(); X } X } X printf("#DONE we're done. Logging out.\n"); X send(modem, "\4\r"); /* EOT then CR */ X X failcnt=3; X while (failcnt--) { X printf("waiting for system logout\n"); X getpacket(modem, pack); X len=strlen(pack); X if ((len==2) && (pack[0] == ESC) && (pack[1] == EOT)) { X printf("#BYE asked to hangup\n"); X bail_out(); X } X if ((len==1)&&(*pack==RS)) { X printf("#WRONGANY something went wrong. (useless msg)\n"); X bail_out(); X } X/* if (!stricmp(line, "NO CARRIER")) break; */ X } X printf("#BYE we're leaving.\n"); X bail_out(); X} X X SHAR_EOF $TOUCH -am 0922133192 ixocico.c && chmod 0444 ixocico.c || echo "restore of ixocico.c failed" # ============= depend ============== echo "x - extracting depend (Text)" sed 's/^X//' << 'SHAR_EOF' > depend && XWhat routines use on what routines X---------------------------------- X Xsafeprint: X Xchecksum: safeprint X Xgrabmodem: X Xsend: safeprint X Xmatch: safeprint X Xhangup_modem: send X Xunlockmodem: X Xbail_out: hangup_modem unlockmodem X Xlockmodem: bail_out X Xgetline: bailout safe_print X Xgetpacket: bail_out safeprint X Xmain: lockmodem grabmodem bail_out send match hangup_modem getpacket getline checksum X SHAR_EOF $TOUCH -am 0505135492 depend && chmod 0664 depend || echo "restore of depend failed" # ============= schedule ============== echo "x - extracting schedule (Text)" sed 's/^X//' << 'SHAR_EOF' > schedule && X# beepersched -- the schedule of who's on-call at what times. X# [For use with tpage.pl -- By Tom Limoncelli (tal@warren.mentorg.com)] X# X# This file is only used if "who" is "-" X# X# The lines that begin with 0-6 refer to the days Sun-Sat. The 48 X# bytes after refer to who's on duty each half-hour. X# X# This schedule shows tal, ingo, and greg as X# having different hours. On Monday night, all three are paged. X# X# A "-" in this list (not to be confused with "-" on the command X# line) means "no body is on duty". If the "-U" (urgent) is used, X# beep2.pl keeps looking for another schedule to see if maybe it X# can find someone else scheduled for that time. The way I manage X# things is to have lower-case letters mean individual people and X# capitol letters meaning the fall-back "urgent" people (usually a list X# of the person and their fallback). For example, "g" is for greg, X# and "G" is for greg and the person that backs him up. X# Of course, you can use any system you wish. X# X# N X# O X# 1 1 O 1 1 X#0 1 2 3 4 5 6 7 8 9 0 1 N 1 2 3 4 5 6 7 8 9 0 1 X0--------------------ttttttttttttttttAAAAAAAAAAAA X1------------ttttgggttttttggggiiiiiiitt---------- X2------------ttttgggttttttggggiiiiiiitttttttttttt X3------------ttttgggttttttggggiiiiiiitt---------- X4------------ttttgggttttttggggiiiiiiitttttttttttt X5------------ttttgggttttttggggiiiiiiitt---------- X6-------------------tttttttttttttttttttttttttt--- X# X# these will only be accessed if -U is used. X# A "-" in this list means "don't call anyone" X# X# 1 1 O 1 1 X#0 1 2 3 4 5 6 7 8 9 0 1 N 1 2 3 4 5 6 7 8 9 0 1 X0TTTTTTTTTTTTTTTTTTT--------------TTTTTTTTTTTTTTT X1TTTTTTTTTTTTTTTTTTT--------------TTTTTTTTTTTTTTT X2TTTTTTTTTTTTTTTTTTT--------------TTTTT---------- X3TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT X4TTTTTTTTTTTTTTTTTTT--------------TTTTT---------- X5TTTTTTTTTTTTTTTTTTT--------------TTTTTTTTTTTTTTT X6TTTTTTTTTTTTTTTTTTT--------------TTTTTTTTTTTTTTT X# X# Xg=greg Xi=ingo Xt=tal X# X# a person and their backup XG=greg,tal XI=ingo,greg XT=tal,greg X# XA=greg,tal,ingo SHAR_EOF $TOUCH -am 0505135492 schedule && chmod 0664 schedule || echo "restore of schedule failed" # ============= table ============== echo "x - extracting table (Text)" sed 's/^X//' << 'SHAR_EOF' > table && X# beepertab -- table of information about people's beepers. X# [ For use with tpage.pl by Tom Limoncelli (tal@warren.mentorg.com)] X# X# Comment lines begin with "#". X# X# The columns are name, numeric pager phone # (currently unused), IXO X# protocol number to dial, and the person's pagerid. X# X# Aliases are implemented by repeating data. (ooo, fancy!) X# Xgreg 5551212 18006789012 1234567 Xgregt 5551212 18006789012 1234567 X# Xingo 5551213 18006789012 7654321 Xidean 5551213 18006789012 7654321 X# Xtom 5551214 18006789012 1122334 Xtal 5551214 18006789012 1122334 SHAR_EOF $TOUCH -am 0505135492 table && chmod 0664 table || echo "restore of table failed" # ============= ixo.doc ============== echo "x - extracting ixo.doc (Text)" sed 's/^X//' << 'SHAR_EOF' > ixo.doc && XHere is a summary of the IXO protocol. All typos and Xmisinterpretations are mine. This is based on reading the Glenayre XElectronics book. I have not yet implemented the automatic protocol, Xbut I have implemented the manual one. X XYou are "remote entry device" and the system that you dial into is X"paging central". X X(1) Remote sends CR at two second intervals until paging central Xresponds with ID= at correct baud rate or until three transmissions Xhave been completed. (This step exists to allow for possible future Xbaud rate recognition). X XSome systems have chosen to send ID= from the central if they do not Xreceive CR in about two seconds. This variation appears to be Xacceptable as the central continues to respond to CR's with ID='s. X X(2) Central sends ID=. Request for ID returned within one second of Xreceipt of CR. Paging central may, at its option, send CR or CR LF Xafter ID= X XPaging central may optionally choose to send ID= if it does not Xreceive CR after appropriate waiting time. X XFOR AUTOMATIC REMOTE ENTRY: XAt this point you can start following 2A or 2M depending on which Xsub-protocol you want to use. X X(2A) Remote sends ESC SST (typically ESC PG1) XESC signifies entry device intends to talk in automatic dump mode. X XSS is a set of two alphanumeric charactoers signifying a type of Xservice to be accessed: X XFor a paging service where: XField 1 = Pager ID XField 2 = Message XSS will be sent at "PG". X XWhere T is a single alphanumeric character relating to the type of Xterminal or device attempting to send the message. T = "1" is a Xcategory of entry devices using the same protocol. The PETs and IXO Xdevices are members of this category. T = "7" or "8" or "9" are Xreserved for wild card terminals or devices which may relate to a Xspecific users' system. X X(3A) Remote sends 6-char alphanumeric password then CR. (Password is Xoptional and is, in general, reserved for future services. Password Xmay be interpreted as either a caller ID or a system entry key. XLength of password, when used, may be different in some systems.) X XWhen an incorrect logon sequence beginning with ESC is received, the Xpaging central may respond with an ID= if it requires a Xretransmission. X X(4A) Central responds with a message sequence (lines of text, each one Xfollowed by a CR). (Typical messages are "Processing - Please Wait", X"4 pages sent" or "Bad Phone Line Call Again". X X(5A) Central then sends one of three sequences: XCR ACK CR This means "logon accepted" XCR NAK CR This means "requested again" XCR ESC EOT CR This means "Forced disconnect" X X(6A) Central may then send another message sequence (optional). X X(7A) Central sends ESC [p CR XMessage go ahead is sent when paging central is ready for new Xinformation. The "p" is lowercase. X X(8A) Remote sends STX field1 CR field2 CR field3 CR ... fieldn CR XEEE CR XYou can send as many fields as you wish. PG terminals send the Xpager-id in field1 and the message in field2. (no other fields are Xdefined). This entire message can be a max of 250 bytes. A field may Xbe any length and where necessary may be continued in following Xblocks. A field always ends with a CR. The CR field delimiter Xsuggests CR may not be used within a field. A block always begins Xwith a STX and ends with a checksum followed by a CR. The characters Xpreceding the checksum depends on what, if anything, is continued Xbeyond the block boundary. X XEEE can be an ETX, ETB, or RS. X XThe ETX is used as a block termination indicator if a given Xtransaction (fields 1 through N) ends within the block being Xtransmitted. X XThe ETB is used as a block terminator if the transaction is continued Xinto the next block, but the last field in the current block is Xcomplete. X XIf the last field within the current block is to be continued in the Xnext block, no CR is inserted at the end of the first portion of the Xfield and the US character is used as a block termination character. XThe CR terminating the broken field is sent at the end of the field in Xwhatever block the field actually terminates. X XNo limit is established within the protocol itself regarding the Xnumber of transactions, the number of fields or the number of blocks Xper field; however, a particular user system may have limits on any of Xthese items. X XSome systems may be limited to one block per transaction and one Xtransaction per telephone connection. X XEach checksum is computed by performing the simple arithmetic sum of X7-bit values of all characters preceding it in that block. (This Xmeans that the STX and ETB/ETX are included in the sum). The checksum Xis then the least significant 12 bits of this resulting sum. X XThe checksum is transmitted as three printable ASCII characters having Xvalues between HEX 30 and HEX 3F (the characters 0123456789:;<=>?). XThe most significant four bits of the sum are encoded as the 4 LSB of Xthe third chacter. See example. X XA normal paging system will have two fields only: X XField 1 = Pager ID X(normally up to eight digits. May include function and check digit). X XField 2 = Message XField 1 or field 2 may be empty. For example, when a page is tone only, Xfield 2 will be empty. Even when empty, a field is followed by a CR. XSome systems will reject transactions which have an empty field 2 for Xa display page or transactions which have an empty field 1. Other Xsystems are less restrictive. X X(9A) The response to each block is one of four: XMessage sequence CR ACK CR = OK, send next block. XMessage sequence CR NAK CR = Checksum or transmission error, send last X block again. XMessage sequence CR RS CR = Abandon current transaction and go to next. X RS may occur when the checksum is OK, X but the current transaction violates a X system rule. At the option of the X system, it may occure in other cases. XMessage sequence CR ESC EOT CR = Begin disconnect. X XAny of the responses may have an optional message sequence before Xthem, although the system designer should understand the consequences Xto the user with all planned entry devices. X XIt is expected that many systems will save their message sequence Xresponses until immediately before disconnect. For some entry Xdevices, it may also be desirable that message describing non-checksum Xerror assocaiated with a particular transaction in a PG service will Xbegin with the letter ID followed by the contents of field 1 for that Xtransaction. X X(10A) Remote sends EOT CR XAfter reception of an ACK or RS for the last transaction in a given Xservice, the entry device sends EOT CR meaning there are no more Xtransactions remaining in this service. X X(11A) Optional: Central sends a message sequence CR. XAlthough optional, it is highly desirable. X X(12A) Central sends RS CR XAn RS CR should be sent at this point if the paging central finds any Xdata ACK in previous steps by the system to be unacceptable because of Xcontent (e.g. invalid pager number or a message field inappropriate Xfor the type of pages, etc.) X X(13A) Central sends ESC EOT CR Xthen hangs up. X X(14A) stop. X X(2M) Remote sends M CR (capitol M then CR) XLack of ESC at beginning of response to ID signifies manual operation Xwhere applicable. From here on it's undefined, but most systems ask X(in English) for the info they need and then return you to step (1). X XNOTES: XBell 103 300bps modem is standard, others may be used. Protocol is XASCII with XON, XOFF either direction using 10-bit code (1 start, 7 Xdata, 1 parity, 1 stop) with even parity. X XIn the case of delays, the paging central shall wait at least four Xseconds (eight seconds for (1) (2) (2A)) before disconnecting the Xremote entry device. The remote entry device shall wait at least 10 Xseconds for a character from the central before hanging up. X XPaging central may, at its option, send CR LF in place of CR at the Xend of any sequence. X XFor inital use of protocol, the paging central shall be equipped to Xreceive full duplex using a Bell 103 compatible modem at 300 baud. XOptionally, certain inputs may be capable of receiving 110 baud Bell X103 full duplex, 300/1200 baud Bell 212 full duplex. No echo shall be Xemployed in full duplex mode. Any attempts at automatic baud rate Xdetermination shall be within the restraints of the specified Xprotocol. X X XSAMPLE Checksum Example: X XSTX 000 0010 X1 011 0001 X2 011 0010 X3 011 0011 XCR 000 1101 XA 100 0001 XB 100 0010 XC 100 0011 XCR 000 1101 XETX 000 0011 X 10111 1011 X 1 7 ; X XSo, the checksum is "17;" X XThere is also a really nice flowchart, which I can't reproduce here Xwithout much too much trouble. ...and you wouldn't have known it Xexisted if I hadn't told you. :-) SHAR_EOF $TOUCH -am 1029172991 ixo.doc && chmod 0664 ixo.doc || echo "restore of ixo.doc failed" exit 0 --