Program Phone;
{$IFDEF VER70}
{$A+,B-,D-,E-,F-,G-,I-,L-,N-,O-,P+,Q-,R-,S-,T-,V-,X+}
{$ELSE}
{$A+,B-,D-,E-,F-,G-,I-,L-,N-,O-,R-,S-,V-,X+}
{$ENDIF}

{ Source code for Borland/Turbo Pascal 6/7.
  To be compiled with NwTP version 0.6 or higher.
  NwTP is a FreeWare Netware Interface for Pascal.
}

{ Based on the phone.pas program by Eduardo M. Serrat,
  as published in Dr.Dobbs #207, November 1993.

  The NwTP units and this adaption of his program are
  (c) 1993,1995 by Rene Spronk ,Groningen, the Netherlands. }

uses dos,crt,nwMisc,nwBindry,nwConn,nwMess,nwServ,nwIPX;

const Socket  = $80C3;
      { This socket was assigned by Novell to an IPX Chatprogram by OXXI }
      { Don't use this program in conjunction with theirs..              }
Var
   SendECB,
   ListenECB      :TEcb;                     { Definition of ECBs        }
   SendIpxHeader,
   ListenIPXheader:TIpxHeader;               { Definition of IPX Headers }
   SendData,
   ReadData       :Array [1..100] of Byte;    { Data area of packets      }
   readflg        :Boolean;  { Flag to signal received packets }

   MyConnNbr      :Byte;
   MyAddress      :TinternetworkAddress;
   MyName         :String;
   MyServerId     :Byte;
   MyServerName   :String;
   myx,myy        :Byte;  { my viewport cursor position }

   RconnNbr       :Byte;
   Raddress       :TinterNetworkAddress;
   Rname          :String;
   RfullName      :String;
   RserverID      :Byte;
   RserverName    :String;
   LocalTarget    :TnodeAddress;  { Node Address of bridge to remote address }

   NewStack       :Array[1..256] of Word;   { !! used by ESR }
   StackBottom    :Word;                    { !! used by ESR }
   HeapCheckPtr   :pointer;  { Pointer that holds heapPointers }

{---------------------------------------------------------------------------}

Procedure CheckError(b:Boolean;errCode:Word; mess:String);
begin
IF b
 then begin
      writeln;
      CASE errCode of
       { main body: 0000-000F }
       $0001:writeln('IPX not installed.');
       $0002:writeln('Error opening socket.');
       { Procedure whoami }
       $0010:writeln('Error whilst determining connectionnumber.');
       $0011:writeln('Error determining internet address.');
       $0012:writeln('Error retreiving connection information.');
       { Procedure process input command }
       $0022:writeln('Servername ',mess,' is invalid.');
       $0023:writeln('Error interpreting connection number parameter :',mess);
       $0025:begin
             writeln('The supplied username is not unique,');
             writeln('or the target user isn''t logged in.');
             end;
       $0026:writeln('Please select a target user from the above list.');
       $0027:writeln('Phone cancelled.');
       { handshake with sender }
       $0032:writeln('Packet received from a user claiming to be ConnectionNumber $',mess);
       { Sendbroadcast message in Procedure HandshakeWithreceiver }
       $1000: writeln('Error: Broadcasting a message to the target user failed.');
       $10FC: begin
              Writeln('The target user is logged in, but appears not to be at his/her workstation:');
              writeln('The (last) message was rejected, message buffer for the target station is full.');
              end;
       $10FD: begin
              Writeln('The connection number of the target user has become invalid,');
              Writeln('Most likely because the user has logged out.');
              end;
       $10FF: begin
              Writeln('The target user is logged in, but has blocked incoming messages.');
              end;
      else writeln('An unspecified error occurred.');
      end; {case }
      if errCode>$000F then IPXcloseSocket(socket);
      if errCode>$001F
       then begin
            SetPreferredConnectionId(MyServerId);
            release(HeapCheckPtr);
            end;
      if ((errCode=$0026) or (errCode=$0027))
       then halt(0)
       else halt(1);
      end;
end;

{-----------------------------------------------------------------------------}

Function Confirm:Boolean;
Var ch:char;
begin
repeat
  repeat {} until keypressed;
  ch:=readkey;
  if ch=#0 then ch:=readkey;
until ch IN ['y','Y','n','N'];
Confirm:=((ch='Y') or (ch='y'))
end;

{-----------------------------------------------------------------------------}

{$F+}
Procedure ESRproc;
begin
 ReadFlg:=true;
end;

Procedure ESRHandler; assembler;
asm { ES:SI are the only valid registers when entering this Procedure ! }
    mov dx, seg stackbottom
    mov ds, dx

    mov dx,ss  { setup of a new local stack }
    mov bx,sp  { ss:sp copied to dx:bx}
    mov ax,ds
    mov ss,ax
    mov sp,offset stackbottom
    push dx
    push bx

    CALL EsrProc

    pop bx
    pop dx
    mov sp,bx
    mov ss,dx
end;
{$F-}

{-----------------------------------------------------------------------------}

Function SameAddress(Var a,b):Boolean;
{ check if networkaddress a and b have the same net and node address }
Type Taddress=Array[1..10] of char;
Var addrA:Taddress ABSOLUTE a;
    addrB:Taddress ABSOLUTE b;
begin
SameAddress:=(addrA=addrB);
end;

{----------------------------------------------------------------------------}

Function Time:String;
   Function LeadingZero(w:Word):String;
   Var s : String;
   begin
   Str(w:0,s);
   if Length(s) = 1
    then s := '0' + s;
   LeadingZero := s;
   end;
Var h, m, s, hund : Word;
begin
GetTime(h,m,s,hund);
Time:=LeadingZero(h)+':'+LeadingZero(m)+':'+LeadingZero(s);
end;

{-----------------------------------------------------------------------------}
Procedure HandshakeWithReceiver;
const Progress  :  Array [1..4] of char = ('/','Ä','\','|');
Var
   SecondInd   :Word;
   ProgressInd :Byte;
   x,y         :Byte;
   KeyNbr      :Byte;
   ConnUp      :Boolean;

   ObjName     :String;
   ObjType     :Word;
   ObjId       :LongInt;
   LogonTime   :TnovTime;

   Message     :String;
   ConnList,
   ResultList  :TconnectionList;
begin
Writeln('Calling User ',Rname);
Write('Press <ESC> to cancel [ ]');
x:=wherex-2; y:=wherey;
Message:='User '+MyName+' is phoning you........... ['+Time+']';
SecondInd:=0; ProgressInd:=1;

SetPreferredConnectionId(RserverId);
ConnList[1]:=RconnNbr;
SendBroadcastMessage(message,1,ConnList,ResultList);
Checkerror(nwMess.result>0,$1000,'');
CheckError(ResultList[1]>0,$1000+ResultList[1],'');

IPXListenForPacket(ListenECB);

KeyNbr:=$ff;
ConnUp:=False;
FillChar(SendData,SizeOf(SendData),#0);
SendData[1]:=Hi(MyConnNbr);
SendData[2]:=Lo(MyConnNbr);
Move(MyServerName[1],SendData[3],ord(MyserverName[0]));
Move(MyName[1],SendData[50],ord(Myname[0]));

repeat { send a packet every 4 seconds and a broadcast message every 30 seconds }
  gotoxy(x,y);
  write(Progress[ProgressInd]);
  inc(ProgressInd);
  if ProgressInd > 4
   then begin
        ProgressInd:=1;
        IPXSendPacket(SendECB);
        end;
  inc(SecondInd);
  if SecondInd = 30
   then begin
        SendBroadcastMessage(message,1,ConnList,ResultList);
        Checkerror(nwMess.result>0,$1000,'');
        CheckError(ResultList[1]>0,$1000+ResultList[1],'');
        SecondInd:=0;
        end;
  delay(1000);
  if readflg
   then begin
        writeln('recieved a packet..');
         if not SameAddress(ListenIPXheader.source,Raddress)
          then begin
               readflg:=false;
               IPXListenForPacket(ListenECB);
               end
          else ConnUp:=TRUE;
        end;
  if keypressed
   then KeyNbr:=ord(readkey);

until (KeyNbr = $1b) or ConnUp;

if KeyNbr = $1b
 then begin
      Writeln;
      Write('Wait...');
      Delay(5000);
      SendData[1]:=$1b;
      IPXSendPacket(SendECB);
      message:='The user phoning you canceled the call... ['+Time+']';
      SendBroadcastMessage(message,1,ConnList,ResultList);
      IpxCloseSocket(Socket);
      SetPreferredConnectionID(MyServerId);
      halt(1);
      end;
Writeln;
Write('User ',Rname,' answered your call......!');
delay(1200);
ReadFlg:=false;
end;

{--------------------------------------------------------------------------}

Procedure HandshakeWithSender;
const Progress:Array [1..4] of char = ('/','Ä','\','|');
Var p        :Byte;
    ObjType  :Word;
    ObjId    :LongInt;
    LoginTime:TnovTime;
    ticks    :Word;
    x,y      :Word;
begin
Writeln('Listening for calls..');
Write('Press <ESC> to cancel [ ]');
x:=wherex-2; y:=wherey;
IPXListenForPacket(ListenECB);
p:=1;
while(p<=4) and (not ReadFlg)
 do begin
    gotoxy(x,y);
    write(Progress[p]);
    delay(1200);
    inc(p);
    end;
If not readflg
 then begin
      Writeln;
      Writeln('Nobody is Calling you..........');
      writeln;
      writeln('( PHONE ? for help )');
      IpxCloseSocket(Socket);
      SetPreferredConnectionId(MyServerId);
      halt(1);
      end
else  begin
      readflg:=false;
      Raddress:=ListenIPXheader.source;
      Raddress.socket:=Socket;
      RconnNbr:=(ReadData[1]*256)+ReadData[2];
      ZstrCopy(RserverName,ReadData[3],47);
      ZstrCopy(Rname,ReadData[50],47);
      IPXGetLocalTarget(Raddress,LocalTarget,ticks);
      IPXSetupSendECB(NIL, Socket, Raddress,
                      Addr(SendData), SizeOf(SendData),
                      SendIPXheader,SendECB);
      IPXSendPacket(SendECB); { acknowledge by sending a packet. Packet contents unimportant. }
      end;
end;


{-----------------------------------------------------------------------------}

Procedure InitWindows;
Var i: Byte;
begin
ClrScr;
myx:=1; myy:=1;
gotoxy(1,1);
write('É'); for i:=2 to 79 do write('Í'); write('»');
write('º'); for i:=2 to 79 do write(' '); write('º');

gotoxy(3,2);
Write('User: '+MyName+' ° Server: '+MyServerName);
write(' ° Connection: '); write(MyConnNbr);
gotoxy(1,3);
write('È'); for i:=2 to 79 do write('Í'); write('¼');

gotoxy(1,13);
write('É'); for i:=2 to 79 do write('Í'); write('»');
write('º'); for i:=2 to 79 do write(' '); write('º');

gotoxy(3,14);
Write('User: '+Rname+' ° Server: '+RserverName);
Write(' ° Connection: '); write(RconnNbr);
Gotoxy(1,15);
write('È'); for i:=2 to 79 do write('Í'); write('¼');

gotoxy(26,25);
Write('±±±²²² Phone Utility ²²²±±±');
gotoxy(1,1);
HighVideo;
end;

{-----------------------------------------------------------------------------}

Procedure Talk;

    Function Timeout(w1,w2:Word;sec:Byte):Boolean;
    Var lw2:Longint;
    begin
    if w2<w1
     then lw2:=$10000+w2
     else lw2:=w2;
    Timeout:=((lw2-w1) DIV 18)>sec;
    end;

    Procedure MyWindow;
    begin
    Window(1,5,80,12);
    gotoxy(myx,myy);
    end;

    Procedure RemoteWindow;
    begin
    Window(1,17,80,24);
    end;


Var currMarker,
    SendMarker,
    ListenMarker:Word;
    ch          :Char;
    RlastChar,
    RlastX,
    RlastY      :byte;
begin
MyWindow;
IPXListenForPacket(ListenECB);
IPXSetupSendECB(NIL, Socket, Raddress, Addr(SendData), 7,
          SendIPXheader,SendECB);  { make size of sendBuffer smaller }
IPXgetIntervalMarker(SendMarker);
ListenMarker:=SendMarker;
SendData[1]:=$FF;
RlastChar:=$FF;

REPEAT
 if keypressed
  then begin
       MyWindow;
       SendData[4]:=SendData[1];  { append last typed char to packet. }
       SendData[5]:=SendData[2];  { original packet may have been lost }
       SendData[6]:=SendData[3];  { Remember: IPX is unreliable ! }
       ch:=readkey;
       if ch=#0
        then begin
             ch:=readkey;
             CASE ord(ch) of
              75:begin { <- 'cursor left' }
                 SendData[2]:=myx-1;
                 if (myx=1) then SendData[2]:=1;
                 gotoxy(SendData[2],myy);
                 SendData[3]:=myy;
                 SendData[1]:=$00;
                 end;
              77:begin { -> 'cursor right' }
                 SendData[2]:=myx+1;
                 if (myx=80) then SendData[2]:=80;
                 gotoxy(SendData[2],myy);
                 SendData[3]:=myy;
                 SendData[1]:=$00;
                 end;
             else SendData[1]:=$FF;
             end; {case}

             end
        else begin
             SendData[1]:=ord(ch);
             SendData[2]:=myx;
             SendData[3]:=myy;
             Case ord(SendData[1]) of
              8 :write(#8+#$20+#8); { backspace }
              13:writeln;           { return    }
             else write(chr(SendData[1]));
             end; {case}
             end;
       myx:=wherex;
       myy:=wherey;
       IPXSendPacket(SendECB);           { send current and previous char }
       IPXGetIntervalMarker(SendMarker);
       end;

 if readflg
  then begin
       If SameAddress(ListenIPXheader.source,Raddress)
        then begin
             if (readData[4]<>$FF)
              and (   (readData[4]<>RlastChar)
                   or (readData[5]<>Rlastx)
                   or (readData[6]<>Rlasty)
                  )
             then begin   { if we missed a packet, display char now }
                  RemoteWindow;
                  Gotoxy(ReadData[5],ReadData[6]);
                  CASE ReadData[4] of
                   0:begin { don't print, cursor movement only }
                     end;
                   8:write(#8+#$20+#8);  { backspace }
                  13:writeln;            { return    }
                  else write(chr(ReadData[1]));
                  end;{case}
                  end;

             if ReadData[1]<>$FF
              then begin
                   RemoteWindow;
                   Gotoxy(ReadData[2],ReadData[3]);
                   CASE ReadData[1] of
                    0:begin { don't print, cursor movement only }
                      end;
                    8:write(#8+#$20+#8);
                   13:writeln;
                   else write(chr(ReadData[1]));
                   end;{case}
                   end;
             RlastChar:=ReadData[1];
             RlastX   :=ReadData[2];
             RlastY   :=ReadData[3];
             IPXGetIntervalMarker(ListenMarker);
             end;
       readflg:=false;
       IPXListenForPacket(ListenECB);
       end;

 IPXRelinquishControl;
 IPXGetIntervalMarker(currMarker);
 IF Timeout(SendMarker,currMarker,5)  { send an "I'm alive" msg after 5 idle secs }
  then begin
       SendData[4]:=SendData[1];  { redundant info: append last char to packet. }
       SendData[5]:=SendData[2];
       SendData[6]:=SendData[3];
       SendData[1]:=$FF;
       IPXSendPacket(SendECB);
       IPXGetIntervalMarker(SendMarker);
       end;
 IF Timeout(ListenMarker,currMarker,17) { fake an "hang-up" if no msgs received during 17 secs }
  then begin
       ReadData[1]:=$1B;
       RemoteWindow;
       end;
UNTIL (ReadData[1]=$1b) or (SendData[1]=$1b); { .. until either party has hung up }

SendData[1]:=$1b;
IPXSendPacket(SendECB);
IpxCloseSocket(Socket);
Writeln;
Writeln;
writeln('<Hanging Up...........>');
Delay(2000);
Window(1,1,80,25);
LowVideo;
gotoxy(80,25);
end;

{--------------- ProcessInputCommand----------------------------------------}

Type PusrInfo=^TusrInfo;
     TusrInfo=record
              ObjName :String[47];
              FullName:String[40];
              ConnId,
              ConnNbr :Byte;
              Address :TinterNetworkAddress; { socket field not used }
              next    :PusrInfo;
              end;

Var startInfo:PusrInfo;

Procedure PushInLL(_objName,_objFullName:String;
                   _connId,_connNbr:Byte;
                   _address:TinternetworkAddress);
Var p,m,l:PusrInfo;
begin
p:=startInfo;
new(l);
With l^
 do begin
    if _objFullName[0]>#40
     then _objFullName[0]:=#40;
    objName:=_objName;
    fullName:=_objFullName;
    connId:=_connId;
    connNbr:=_connNbr;
    address:=_address;
    next:=NIL;
    end;
if p=NIL
 then startInfo:=l
 else begin
      m:=p;
      While (p<>NIL) and (p^.objName<=_obJname)
       do begin m:=p;p:=p^.next; end;
      if p=startInfo
       then begin { insert before first LL entry }
            l^.next:=startInfo;
            startInfo:=l;
            end
       else begin { insert in LL or append to end }
            l^.next:=m^.next;
            m^.next:=l;
            end;
      end;
end;

Function GetTargetUser:PusrInfo;
{ returns NIL if a target user was not uniquely identified by the user }
Var l            :PusrInfo;
    serverName   :String;
    SelectedUsers:Word;
    t            :Word;
    s            :String;
    ch           :char;
    Laddr        :TinternetworkAddress;
    AddrSame     :boolean;
begin
{ are all objNames the same?
   Yes => multple logins (connNbr must have been supplied)
          or login on multiple servers (serverName must h.b. supplied)
   No => the supplied userName is not unique. }
l:=startInfo;
SelectedUsers:=0;
IF l<>NIL
 then Laddr:=l^.address;
AddrSame:=true;
While (l<>NIL)
 do begin
    inc(SelectedUsers);
    AddrSame:=AddrSame and SameAddress(Laddr,l^.address);
    l:=l^.next;
    end;
If AddrSame { are all the users essentially the same ? }
 then SelectedUsers:=1;

CASE SelectedUsers of
 0:begin
   GetTargetUser:=NIL;
   end;
 1:begin { OK! unique users identified }
   GetTargetUser:=StartInfo;
   end;
 else begin
      writeln('The target user has multiple connections.');
      writeln('Please give connection number and/or server name of the intended user.');
      writeln;
      writeln('Username             | Server          | Con | Full Name');
      writeln('---------------------+-----------------+-----+----------------------');

      t:=3;
      l:=startInfo;
      while l<>NIL
       do begin
          GetFileServerName(l^.connId,servername);
          PstrCopy(s,l^.objName,20);
          write(s,' | ');
          PstrCopy(s,serverName,15);
          write(s,' | ',l^.connNbr:3,' | ');
          PstrCopy(s,l^.fullname,30);
          writeln(s);
          l:=l^.next;
          inc(t);
          if t=20
           then begin
                writeln('--- more (any key)---');
                repeat {} until keypressed;
                ch:=readkey;
                if ch=#0 then ch:=readkey;
                t:=0;
                end;
          end;
      GetTargetUser:=NIL;
      end;
 end; {case}
end;

Procedure ProcessInputCommand;
Var SearchStartServer,
    SearchEndServer   :Byte;
    ConnIdCtr,
    ConnNbrCtr        :Byte;

    LuserName,
    LserverName       :String;
    LconnId           :Byte;
    LfullName         :String;
    LconnNbr          :Byte;

    ServerInfo        :TFileServerInformation;
    objName           :String;
    objType           :Word;
    objId             :Longint;
    ticks             :Word;
    LoginTime         :TnovTime;
    IntNWaddress      :TinternetworkAddress;

    TargetUserPtr     :PusrInfo;

    p                 :Byte;
    errcode           :Integer;
begin
StartInfo:=NIL;
If (ParamCount>0)
   and ( (pos('?',paramstr(1))>0)
         or (pos('help',paramstr(1))>0)
         or (pos('HELP',paramstr(1))>0)
       )
 then begin
      writeln;
      writeln('** Phone V 1.3., By E.M. Serrat and R. Spronk');
      writeln;
      writeln('** Usage: PHONE');
      writeln;
      writeln('Listen for others calling you.');
      writeln;
      writeln;
      writeln('** Usage: PHONE [servername/]UserName [connection]');
      writeln;
      writeln('Call someone.');
      writeln('-Username may be a ''*'' wildcard.');
      writeln(' All logged in users on all attached servers will be shown.');
      writeln('-Sender and receiver must be attached to a common server in the internetwork.');
      writeln('-The supplied username is compared with the first characters of');
      writeln(' the login name and with the full user name, as set by SYSCON.');
      writeln('-Servername must be supplied if the target user has connections');
      writeln(' with more than one server.');
      writeln('-ConnectionNumber must be supplied if the target user is logged in');
      writeln(' at multiple workstations attached to the same server.');
      writeln;
      writeln('The program will timeout if the program on the other end of the link');
      writeln('is terminated abnormally.');
      halt(1);
      end;
if paramcount=0 { ---- Listen if anyone is calling us ----- }
 then begin
      HandshakeWithSender;
      InitWindows;
      Talk;
      IpxCloseSocket(Socket);
      SetPreferredConnectionId(MyServerId);
      halt(0);
      end;
{ ** Paramcount>0, We're calling someone ** }
LconnNbr:=0;
SearchStartServer:=1;
SearchEndServer:=8;
LuserName:=ParamStr(1);
UpString(LuserName);
p:=pos('/',LuserName);
checkError((p=1) and (LuserName[0]=#1),$0020,'');
if p>0
 then begin
      LserverName:=copy(LuserName,1,p-1);
      delete(LuserName,1,p);
      if LuserName=''
       then LuserName:='*';
      if pos('*',LserverName)=0
       then begin
            GetConnectionId(LserverName,LconnId);
            checkError(nwConn.result>0,$0022,LserverName);
            SearchStartServer:=LconnId;
            SearchEndServer:=LconnId;
            end;
      end;
if paramcount>1
 then begin
      Val(ParamStr(2),LconnNbr,errcode);
      checkError(errcode<>0,$0023,Paramstr(2));
      end;

writeln('Scanning logged in users..');
ConnIdCtr:=SearchStartServer;
While ConnIdCtr<=SearchEndServer
 do begin
    If IsConnectionIdInUse(ConnIdCtr)
     then begin
          SetPreferredConnectionId(ConnIdCtr);
          IF NOT GetFileServerInformation(ServerInfo)
           then ServerInfo.connectionsMax:=250; { patch value if call failed }
          for ConnNbrCtr:=1 to ServerInfo.ConnectionsMax
           do begin
              IF GetConnectionInformation(ConnNbrCtr,ObjName,objType,objId,LoginTime)
                 and (objType=OT_USER)
               then begin
                    GetInterNetAddress(ConnNbrCtr,IntNWaddress);
                    GetRealUserName(ObjName,LfullName);
                    UpString(LfullName);
                    IF (pos('NOT-LOGGED-',objName)=0)        { skip not logged in connections }
                      and ((LconnNbr=0) or (LconnNbr=ConnNbrCtr))     { if user supplied connNbr, check it }
                      and (NOT SameAddress(MyAddress,IntNWAddress)) { no mail to yourself }
                      and ( (LuserName[1]='*')               { wildcard overrules nameselection }
                            or (pos(LuserName,ObjName)=1)    { username matched with firts few characters in objName? }
                            or (pos(LuserName,LfullName)>0)  { usermane matches part of objects' Full_Name ? }
                          )
                     then PushInLL(objName,LfullName,ConnIdCtr,ConnNbrCtr,
                                   IntNWaddress);
                    end;
              end;
          end;
    inc(ConnIdCtr);
    end;
TargetUserPtr:=GetTargetUser;
checkError((LuserName[1]<>'*') and (TargetUserPtr=NIL),$0025,''); { No user selected }
checkError(TargetUserPtr=NIL,$0026,'');
RconnNbr:=TargetUserPtr^.connNbr;
Raddress:=TargetUserPtr^.address;
Raddress.Socket:=Socket;
Rname:=TargetUserPtr^.objName;
RserverId:=TargetUserPtr^.connId;
GetFileServerName(RserverId,RserverName);
release(HeapCheckPtr);

SetPreferredConnectionId(RserverId);
GetRealUserName(Rname,RfullName);
writeln;
writeln(RserverName,'/',Rname,' Connection_Number= ',RconnNbr);
writeln('(Full name =',RfullName,')');
writeln;
write('Is the above user the intended chat partner ? (Y/N)');
checkError(NOT Confirm,$0027,''); { user abort }
writeln;

IPXGetLocalTarget(Raddress,LocalTarget,ticks);
IPXSetupSendECB(NIL, Socket, Raddress, Addr(SendData), SizeOf(SendData),
                SendIPXheader,SendECB);
HandShakeWithReceiver;
InitWindows;
Talk;
IpxCloseSocket(Socket);
SetPreferredConnectionId(MyServerId);
halt(0);
end;

Procedure WhoAmI; {---------------------------------------------------------}
Var ObjType  :Word;
    ObjId    :LongInt;
    LogonTime:TnovTime;
begin
GetConnectionNumber(MyConnNbr);
checkError(nwConn.result>0,$0010,'');
GetInternetAddress(MyConnNbr,MyAddress);
checkError(nwConn.result>0,$0011,'');
MyAddress.Socket:=Socket;
GetConnectionInformation(MyConnNbr,MyName,ObjType,ObjId,LogonTime);
checkError(nwConn.result>0,$0012,'');
GetEffectiveConnectionID(MyServerId);
GetFileServerName(MyServerId,MyServerName);
end;

{-----------------------------------------------------------------------------}
Var LocSocket:Word;

begin
Writeln('*** PHONE V1.3 ***');
Mark(HeapCheckPtr);
LocSocket:=Socket;
readflg:=false;
Checkerror(NOT IpxPresent,$0001,'');
IpxOpenSocket(LocSocket,FALSE);
Checkerror(nwIPX.result>0,$0002,'');
WhoAmI;
IPXSetupListenECB(Addr(EsrHandler),socket,Addr(ReadData),SizeOf(ReadData),
                  ListenIPXheader,ListenECB);
ProcessInputCommand; {doesn't return}
end.