home *** CD-ROM | disk | FTP | other *** search
/ MacGames Sampler / PHT MacGames Bundle.iso / MacSource Folder / Samples from the CD / Pascal / Source□ / Talk Source / Talk ƒ / Talks.p < prev    next >
Encoding:
Text File  |  1992-04-20  |  16.0 KB  |  665 lines  |  [TEXT/PJMM]

  1. unit Talks;
  2.  
  3. { This program was written by Peter N Lewis, Mar 1992 in THINK Pascal 4.0.1 }
  4.  
  5. interface
  6.  
  7.     uses
  8.         OOStatus;
  9.  
  10.     procedure InitTalks;
  11.     procedure FinishTalks;
  12.     procedure HandleStatusCommand (data: longInt; sc: statusCommands; var s: string);
  13.     procedure HandleEvents;
  14.  
  15.     const
  16.         max_notify_display_time = longInt(1) * 60 * 60;
  17.  
  18. implementation
  19.  
  20.     uses
  21.         OOMainLoop, AppGlobals, Preferences, MyNotifier, MyInternetMenu, MyTranslate82728,{}
  22.         TCPTypes, TCPStuff, TCPConnections, OOTalk, TalkUtils, TalkUDPPackets,{}
  23.         PrefsGlobals, MyUtils, MyUtilities;
  24.  
  25.     type
  26.         MyTalkObject = object(TalkObject)
  27.                 tcpc: TCPConnectionPtr;
  28.                 mtc: MyTalkConnection;
  29.                 procedure TransmitKey (ch: char);
  30.                 override;
  31.                 procedure TransmitBlock (h: handle; len: longInt);
  32.                 override;
  33.                 procedure Destroy;
  34.                 override;
  35.             end;
  36.         BaseTalkConnection = object
  37.                 procedure HandleEvent (cer: connectionEventRecord);
  38.                 procedure HandleStatusCommand (sc: statusCommands; var s: string);
  39.                 procedure GotResponse (var request, response: ctlMsg);
  40.             end;
  41.         DaemonTalkConnection = object(BaseTalkConnection)
  42.                 procedure Create;
  43.                 procedure Destroy;
  44.                 procedure GotResponse (var request, response: ctlMsg);
  45.                 override;
  46.             end;
  47.         MyTalkConnection = object(BaseTalkConnection)
  48.                 local_id, remote_id: longInt;
  49.                 mto: MyTalkObject;
  50.                 title: stringHandle;
  51.                 packet_state: (PS_None, PS_Verifying, PS_Connecting);
  52.                 lastresponse: ctlMsg;
  53.                 machname: str63;
  54.                 ourcontrolport: integer;
  55.                 ourtcpport: integer;
  56.                 remotecontrolhost: longInt;
  57.                 con: connectionIndex;
  58.                 waitingforeditchars: boolean;
  59.                 delchar: char;
  60.                 procedure Create;
  61.                 procedure Destroy;
  62.                 procedure SelfDestruct;
  63.                 procedure StartConnect (s: str255);
  64.                 procedure InitiateConnect (remoteIP: longInt);
  65.                 procedure DeletePackets;
  66.                 procedure PartialClose;
  67.                 procedure SendCloseMessage;
  68.                 procedure HandleEvent (cer: connectionEventRecord);
  69.                 override;
  70.                 procedure GotResponse (var request, response: ctlMsg);
  71.                 override;
  72.                 procedure HandleStatusCommand (sc: statusCommands; var s: string);
  73.                 override;
  74.             end;
  75. { HandleEvent(closed) -> Destroy mto }
  76. { Window Close -> Destroy mto }
  77. { Destroy mto -> Destroy mtc }
  78.  
  79.     var
  80.         daemon: DaemonTalkConnection;
  81.         trans_in, trans_out: transTable;
  82.  
  83.     procedure InitTalks;
  84.         var
  85.             oe: OSErr;
  86.     begin
  87.         GetTrans(translateOutResID, trans_out);
  88.         GetTrans(translateInResID, trans_in);
  89.         oe := InitUDPPackets;
  90.         new(daemon);
  91.         daemon.Create;
  92.     end;
  93.  
  94.     procedure FinishTalks;
  95.     begin
  96.         daemon.Destroy;
  97.         FinishUDPPackets;
  98.     end;
  99.  
  100.     procedure BaseTalkConnection.HandleStatusCommand (sc: statusCommands; var s: string);
  101.     begin
  102.         SysBeep(1);
  103.     end;
  104.  
  105.     procedure BaseTalkConnection.GotResponse (var request, response: ctlMsg);
  106.     begin
  107.         SysBeep(1);
  108.     end;
  109.  
  110.     procedure BaseTalkConnection.HandleEvent (cer: connectionEventRecord);
  111.     begin
  112.         SysBeep(1);
  113.     end;
  114.  
  115.     procedure SetupMessage (var m: ctlMsg);
  116.     begin
  117.         m.vers := talk_version;
  118.         m.id_num := -1;
  119.         m.addr.family := AF_INET;
  120.         m.addr.ip := my_machine_addr;
  121.         m.addr.port := 0;
  122.         m.ctl_addr.family := AF_INET;
  123.         m.ctl_addr.ip := my_machine_addr;
  124.         m.ctl_addr.port := 0;
  125.     end;
  126.  
  127.     procedure DaemonTalkConnection.Create;
  128.         var
  129.             m: ctlMsg;
  130.     begin
  131.         SetupMessage(m);
  132.         if CreateUDPChannel(self, -1, m.ctl_addr.port) = noErr then begin
  133.             m.typ := CT_Scan;
  134.             m.id_num := 1;
  135.             SendPacket(self, WT_Soon, m, my_machine_addr, talkd_port);
  136.         end;
  137.     end;
  138.  
  139.     procedure DaemonTalkConnection.Destroy;
  140.     begin
  141.         DestroyUDPChannel(self);
  142.     end;
  143.  
  144.     procedure MyTalkConnection.GotResponse (var request, response: ctlMsg);
  145.         var
  146.             cp: connectionIndex;
  147.             state, localport, remoteport: integer;
  148.             localhost, remotehost, available: longInt;
  149.             tcpc: TCPConnectionPtr;
  150.             a: integer;
  151.     begin
  152.         case request.typ of
  153.             CT_VerifyAnnounce:  begin
  154.                 if response.answer = a_success then
  155.                     SendPacket(self, WT_Delayed, request, my_machine_addr, talkd_port)
  156.                 else
  157.                     SelfDestruct;
  158.             end;
  159.             CT_LookUp:  begin
  160.                 if response.answer = a_success then begin
  161.                     remote_id := response.id_num;
  162.                     if NewActiveConnection(cp, Default_TCPBUFFERSIZE, response.addr.ip, response.addr.port, self) <> noErr then begin
  163.                         SelfDestruct;
  164.                         SysBeep(1);
  165.                     end
  166.                     else begin
  167.                         con := cp;
  168.                     end;
  169.                 end
  170.                 else if NewPassiveConnection(cp, Default_TCPBUFFERSIZE, 0, 0, 0, self) <> noErr then begin
  171.                     SelfDestruct;
  172.                     SysBeep(1);
  173.                 end
  174.                 else begin
  175.                     con := cp;
  176.                     GetConnectionTCPC(cp, tcpc);
  177.                     repeat
  178.                         TCPRawState(tcpc, state, localhost, localport, remotehost, remoteport, available);
  179.                     until (localport <> 0) or (TCPState(tcpc) = T_Closed);
  180.                     ourtcpport := localport;
  181.                     request.addr.port := localport;
  182.                     request.typ := CT_leave_invite;
  183.                     SendPacket(self, WT_Soon, request, my_machine_addr, talkd_port);
  184.                 end;
  185.             end;
  186.             CT_leave_invite:  begin
  187.                 if response.answer = a_success then begin
  188.                     local_id := response.id_num;
  189.                     request.typ := CT_Announce;
  190.                     SendPacket(self, WT_Soon, request, remotecontrolhost, talkd_port);
  191.                 end
  192.                 else if response.answer = a_permission_denied then begin
  193.                     Paramtext(title^^, '', '', '');
  194.                     a := Alert(PermDeniedAlertID, nil);
  195.                     SelfDestruct;
  196.                 end
  197.                 else begin
  198.                     Paramtext(title^^, '', '', '');
  199.                     a := Alert(RefusedAlertID, nil);
  200.                     SelfDestruct;
  201.                 end;
  202.             end;
  203.             CT_Announce:  begin
  204.                 if response.answer = a_success then begin
  205.                     remote_id := response.id_num;
  206.                     SetEntry(self, rd_whatever, rs_connecting, '');
  207. {Hmm, no more packets needed I guess}
  208.                 end
  209.                 else
  210.                     SelfDestruct;
  211.             end;
  212.             otherwise
  213.                 ;
  214.         end;
  215.     end;
  216.  
  217.     procedure DaemonTalkConnection.GotResponse (var request, response: ctlMsg);
  218.         var
  219.             mtc: MyTalkConnection;
  220.             title: str255;
  221.             sh: stringHandle;
  222.             m: ctlMsg;
  223.             cp: connectionIndex;
  224.             when: whenType;
  225.     begin
  226.         when := WT_Delayed;
  227.         if response.answer = a_success then begin
  228.             if not FindRequest(response.id_num, mtc) then begin
  229.                 new(mtc);
  230.                 mtc.Create;
  231.                 mtc.local_id := response.id_num;
  232.                 mtc.con := no_connection;
  233.                 mtc.packet_state := PS_Verifying;
  234.                 mtc.lastresponse := response;
  235.                 mtc.remotecontrolhost := response.ctl_addr.ip;
  236.                 if FindName(cp, response.ctl_addr.ip, mtc) <> noErr then begin
  237.                     FindString(response.ctl_addr.ip, title);
  238.                     mtc.StartConnect(title);
  239.                 end;
  240.                 when := WT_Soon;
  241.             end;
  242.             request.id_num := request.id_num + 1
  243.         end
  244.         else
  245.             request.id_num := 1;
  246.         SendPacket(self, when, request, my_machine_addr, talkd_port);
  247.     end;
  248.  
  249.     procedure HandleStatusCommand (data: longInt; sc: statusCommands; var s: string);
  250.     begin
  251.         if data <> 0 then
  252.             BaseTalkConnection(data).HandleStatusCommand(sc, s);
  253.     end;
  254.  
  255. {$Z+}
  256.     procedure TalkTo (s: str255);
  257.         var
  258.             mtc: MyTalkConnection;
  259.             name, mach: str255;
  260.             sh: stringHandle;
  261.             oe: OSErr;
  262.             cp: connectionIndex;
  263.     begin
  264.         UnPackName(s, name, mach);
  265.         new(mtc);
  266.         mtc.Create;
  267.         mtc.con := no_connection;
  268.         mtc.packet_state := PS_Connecting;
  269.         mtc.lastresponse.l_name := PStrToUser(name);
  270.         mtc.machname := mach;
  271.         sh := NewString(s);
  272.         mtc.title := sh;
  273.         SetEntry(mtc, rd_outgoing, rs_request, s);
  274.         oe := FindAddress(cp, mach, mtc);
  275.         if oe <> noErr then begin
  276.             FailAlert('FindAddress failed with', oe);
  277.         end;
  278.     end;
  279. {$Z-}
  280.  
  281.     procedure MyTalkConnection.Create;
  282.         var
  283.             sh: stringHandle;
  284.     begin
  285.         mto := nil;
  286.         title := nil;
  287.         packet_state := PS_None;
  288.         local_id := -1;
  289.         remote_id := -1;
  290.     end;
  291.  
  292.     procedure MyTalkConnection.PartialClose;
  293.     begin
  294.         if con <> no_connection then begin
  295.             CloseConnection(con);
  296.             SetDataPtr(con, nil);  { ignore all future events }
  297.             con := no_connection;
  298.             if mto <> nil then
  299.                 mto.tcpc := nil;
  300.         end;
  301.     end;
  302.  
  303.     procedure MyTalkConnection.SendCloseMessage;
  304.         var
  305.             s: str255;
  306.             i: integer;
  307.     begin
  308.         s := concat(cr, '[Connection Closed]', cr);
  309.         for i := 1 to length(s) do
  310.             mto.ReceiveKey(s[i]);
  311.     end;
  312.  
  313.     procedure MyTalkConnection.Destroy;
  314.     begin
  315.         if title <> nil then
  316.             DisposHandle(handle(title));
  317.         RemoveEntry(self);
  318.         PartialClose;
  319.         DeletePackets;
  320.         DestroyUDPChannel(self);
  321.         dispose(self);
  322.     end;
  323.  
  324.     procedure MyTalkConnection.SelfDestruct;
  325.     begin
  326.         if mto = nil then
  327.             Destroy
  328.         else
  329.             mto.Destroy;
  330.     end;
  331.  
  332.     procedure MyTalkObject.Destroy;
  333.     begin
  334.         if mtc <> nil then
  335.             mtc.Destroy;
  336.         inherited Destroy;
  337.     end;
  338.  
  339.     function TranslateChar (ch: char): char;
  340.     begin
  341.         case ch of
  342.             cr: 
  343.                 ch := lf;
  344.             bs: 
  345.                 ch := del;
  346.             otherwise
  347.                 ;
  348.         end;
  349.         TranslateChar := chr(BAND(trans_out[ord(ch)], $FF));
  350.     end;
  351.  
  352.     procedure MyTalkObject.TransmitKey (ch: char);
  353.         var
  354.             s: string[1];
  355.             oe: OSErr;
  356.     begin
  357.         if tcpc <> nil then begin
  358.             s := TranslateChar(ch);
  359.             oe := TCPSendAsync(tcpc, @s[1], 1, nil);
  360.         end;
  361.     end;
  362.  
  363.     procedure MyTalkObject.TransmitBlock (h: handle; len: longInt);
  364.         var
  365.             buffer: buf255;
  366.             l, i: integer;
  367.             pos: longInt;
  368.             oe: OSErr;
  369.     begin
  370.         pos := 0;
  371.         while len > 0 do begin
  372.             l := SizeOf(buffer);
  373.             if l > len then
  374.                 l := len;
  375.             BlockMove(ptr(longInt(h^) + pos), @buffer, l);
  376.             for i := 0 to l - 1 do
  377.                 buffer[i] := TranslateChar(buffer[i]);
  378.             oe := TCPSendAsync(tcpc, @buffer, l, nil);
  379.             pos := pos + l;
  380.             len := len - l;
  381.         end;
  382.     end;
  383.  
  384.     procedure MyTalkConnection.HandleStatusCommand (sc: statusCommands; var s: string);
  385.         var
  386.             m: ctlMsg;
  387.             tname: userStr;
  388.     begin
  389.         case sc of
  390.             SC_Abort:  begin
  391.                 if mto <> nil then begin
  392.                     SetEntry(self, rd_whatever, rs_disconnected, '');
  393.                     PartialClose;
  394.                     SendCloseMessage;
  395.                 end
  396.                 else begin
  397.                     SetEntry(self, rd_whatever, rs_failed, '');
  398.                     SelfDestruct;
  399.                 end;
  400.             end;
  401.             SC_Connect:  begin
  402.                 if packet_state = PS_Verifying then begin
  403.                     SetEntry(self, rd_whatever, rs_connecting, '');
  404.                     packet_state := PS_Connecting;
  405.                     m := lastresponse;
  406.                     SetupMessage(m);
  407.                     m.typ := CT_LookUp;
  408.                     m.ctl_addr.port := ourcontrolport;
  409.                     m.id_num := 0;
  410.                     tname := m.l_name;
  411.                     m.l_name := m.r_name;
  412.                     m.r_name := tname;
  413.                     SendPacket(self, WT_Soon, m, lastresponse.ctl_addr.ip, talkd_port);
  414.                 end
  415.                 else if packet_state = PS_Connecting then begin
  416.                     SetEntry(self, rd_whatever, rs_request, '');
  417.                     m := lastresponse;
  418.                     SetupMessage(m);
  419.                     m.typ := CT_Announce;
  420.                     m.ctl_addr.port := ourcontrolport;
  421.                     m.addr.port := ourtcpport;
  422.                     m.id_num := remote_id + 1;
  423.                     m.r_name := lastresponse.l_name;
  424.                     m.r_tty[1] := nul;
  425.                     m.l_name := PStrToUser(GetMyUserName(prefs));
  426.                     SendPacket(self, WT_Soon, m, remotecontrolhost, talkd_port);
  427.                 end
  428.             end;
  429.             SC_BringToFront: 
  430.                 if mto <> nil then
  431.                     SelectWindow(mto.us.window);
  432.             otherwise
  433.                 ;
  434.         end;
  435.     end;
  436.  
  437.     procedure DoNotify (s: str255; user: str31);
  438.         const
  439.             sicn_size = 32;
  440.         var
  441.             sicnH, nmSicn, snd: handle;
  442.             sh: stringPtr;
  443.             time: str255;
  444.             secs: longInt;
  445.     begin
  446.         if prefs.allowconnect <> AC_Never then
  447.             if not in_foreground then begin
  448.                 if prefs.notify_flash then begin
  449.                     nmSicn := NewHandle(sicn_size);
  450.                     sicnH := GetResource('SICN', sicn_id);
  451.                     if (sicnH <> nil) and (nmSicn <> nil) then
  452.                         BlockMove(sicnH^, nmSicn^, sicn_size)
  453.                     else if nmSicn <> nil then begin
  454.                         DisposHandle(nmSicn);
  455.                         nmSicn := nil;
  456.                     end;
  457.                     HPurge(sicnH);
  458.                 end
  459.                 else
  460.                     nmSicn := nil;
  461.                 if prefs.notify_beep then
  462.                     snd := handle(-1)
  463.                 else
  464.                     snd := nil;
  465.                 if prefs.notify_alert then begin
  466.                     GetDateTime(secs);
  467.                     IUTimeString(secs, false, time);
  468.                     SPrintS5(s, GetGlobalString(notify_str), s, user, time, '', '');
  469.                     sh := stringPtr(NewPtr(length(s) + 1));
  470.                     sh^ := s;
  471.                 end
  472.                 else
  473.                     sh := nil;
  474.                 NotifyH(mark_app, snd, nmSicn, sh, max_notify_display_time);
  475.             end
  476.             else if prefs.notify_beep then
  477.                 SysBeep(3);
  478.     end;
  479.  
  480.     procedure MyTalkConnection.StartConnect (s: str255);
  481.         var
  482.             sh: stringHandle;
  483.             m: ctlMsg;
  484.     begin
  485.         PackName(s, UserToPStr(lastresponse.l_name), s);
  486.         sh := NewString(s);
  487.         title := sh;
  488.         SetEntry(self, rd_incoming, rs_request, s);
  489.         if prefs.show_status then
  490.             ShowStatus;
  491.         m := lastresponse;
  492.         SetupMessage(m);
  493.         DoNotify(s, m.l_name);
  494.         if CreateUDPChannel(self, lastresponse.id_num, m.ctl_addr.port) = noErr then begin
  495.             ourcontrolport := m.ctl_addr.port;
  496.             m.typ := CT_VerifyAnnounce;
  497.             m.id_num := lastresponse.id_num;
  498.             SendPacket(self, WT_Soon, m, my_machine_addr, talkd_port);
  499.         end;
  500.     end;
  501.  
  502.     procedure MyTalkConnection.InitiateConnect (remoteIP: longInt);
  503.         var
  504.             m: ctlMsg;
  505.             i: integer;
  506.             oe: OSErr;
  507.     begin
  508.         remotecontrolhost := remoteIP;
  509.         SetupMessage(m);
  510.         oe := CreateUDPChannel(self, lastresponse.id_num, m.ctl_addr.port);
  511.         if oe = noErr then begin
  512.             ourcontrolport := m.ctl_addr.port;
  513.             m.typ := CT_LookUp;
  514.             m.id_num := -1;
  515.             m.r_name := lastresponse.l_name;
  516.             m.l_name := PStrToUser(GetMyUserName(prefs));
  517.             m.r_tty[1] := nul;
  518.             SendPacket(self, WT_Soon, m, remoteIP, talkd_port);
  519.         end
  520.         else
  521.             FailAlert('Failed to open UDP Channel (cryptic error ain''t it?)', oe);
  522.     end;
  523.  
  524.     procedure MyTalkConnection.DeletePackets;
  525.         var
  526.             m: ctlMsg;
  527.     begin
  528.         SetupMessage(m);
  529.         m.typ := CT_Delete;
  530.         m.ctl_addr.port := ourcontrolport;
  531.         if local_id <> -1 then begin
  532.             m.id_num := local_id;
  533.             local_id := -1;
  534.             SendOnePacket(m, my_machine_addr, talkd_port);
  535.         end;
  536.         if remote_id <> -1 then begin
  537.             m.id_num := remote_id;
  538.             remote_id := -1;
  539.             SendOnePacket(m, remotecontrolhost, talkd_port);
  540.         end;
  541.     end;
  542.  
  543.     procedure DebugChar (dc, ch: char);
  544.     begin
  545. { Delete this routine! It does nothing, its for debugging the values of delchar and ch }
  546.         dc := ch;
  547.     end;
  548.  
  549.     procedure MyTalkConnection.HandleEvent (cer: connectionEventRecord);
  550.         var
  551.             tmto: MyTalkObject;
  552.             s: str255;
  553.             oe: OSErr;
  554.             i: integer;
  555.             ch: char;
  556.     begin
  557.         with cer do
  558.             case event of
  559.                 C_Found:  begin
  560.                     InitiateConnect(value);
  561.                 end;
  562.                 C_SearchFailed:  begin
  563.                     FailAlert(concat('Unknown machine "', machname, '"'), 0);
  564.                     SelfDestruct;
  565.                 end;
  566.                 C_NameFound:  begin
  567.                     StartConnect(stringHandle(value)^^);
  568.                     DisposHandle(handle(value));
  569.                 end;
  570.                 C_NameSearchFailed:  begin
  571.                     FindString(lastresponse.ctl_addr.ip, s);
  572.                     StartConnect(s);
  573.                 end;
  574.                 C_FailedToOpen:  begin
  575.                     if timedout then
  576.                         FailAlert(concat('Timed out connecting to ', title^^), 0)
  577.                     else
  578.                         FailAlert(concat('Machine "', title^^, '" doesn''t answer'), 0);
  579.                     SetEntry(self, rd_whatever, rs_failed, '');
  580.                     con := no_connection;
  581.                     SelfDestruct;
  582.                 end;
  583.                 C_Established:  begin
  584.                     new(tmto);
  585.                     mto := tmto; { don't you hate handles? }
  586.                     mto.mtc := self;
  587.                     mto.Create(talk_output_dialog_id);
  588.                     delchar := del;
  589.                     s := title^^;
  590.                     AddInternetCommand(title^^);
  591.                     SetWTitle(mto.us.window, s);
  592.                     SetEntry(self, rd_whatever, rs_connected, title^^);
  593.                     ShowWindow(mto.us.window);
  594.                     mto.tcpc := tcpc;
  595.                     waitingforeditchars := true;
  596. { Send three edit chars, whatever they might be... }
  597.                     s := concat(del, chr($15), chr($17));
  598.                     oe := TCPSendAsync(tcpc, @s[1], length(s), nil);
  599.                     DeletePackets;
  600.                 end;
  601.                 C_CharsAvailable:  begin
  602.                     if waitingforeditchars then begin
  603. {$PUSH}
  604. {$R-}
  605.                         if value >= 3 then begin
  606.                             waitingforeditchars := false;
  607.                             oe := TCPReceiveChars(tcpc, @s[1], 3);
  608.                             delchar := s[1];
  609.                         end;
  610.                     end
  611.                     else begin
  612.                         if value > 255 then
  613.                             value := 255;
  614.                         oe := TCPReceiveChars(tcpc, @s[1], value);
  615.                         if oe = noErr then
  616.                             for i := 1 to value do begin
  617.                                 DebugChar(delchar, s[i]);
  618.                                 if s[i] = delchar then begin
  619.                                     mto.ReceiveKey(del);
  620.                                 end
  621.                                 else begin
  622.                                     ch := chr(trans_in[ord(s[i])]);
  623.                                     if ch = del then
  624.                                         mto.ReceiveKey(spc)
  625.                                     else if ch = bs then begin
  626.                                         mto.ReceiveKey('^');
  627.                                         mto.ReceiveKey('H');
  628.                                     end
  629.                                     else
  630.                                         mto.ReceiveKey(ch);
  631.                                 end;
  632.                             end;
  633. {$R+}
  634.                     end;
  635.                 end;
  636.                 C_Closing:  begin
  637.                     SetEntry(self, rd_whatever, rs_disconnected, '');
  638.                     PartialClose;
  639.                     SendCloseMessage;
  640.                 end;
  641.                 C_Closed:  begin
  642.                     SetEntry(self, rd_whatever, rs_failed, '');
  643.                     PartialClose;
  644.                     SendCloseMessage;
  645.                 end;
  646.                 otherwise
  647.                     ;
  648.             end;{case}
  649.     end;
  650.  
  651.     procedure HandleEvents;
  652.         var
  653.             btc: BaseTalkConnection;
  654.             cer: connectionEventRecord;
  655.         var
  656.             request, response: ctlMsg;
  657.     begin
  658.         if ReceivePacket(btc, request, response) then
  659.             btc.GotResponse(request, response);
  660.         if GetConnectionEvent(any_connection, cer) then
  661.             if cer.dataptr <> nil then
  662.                 BaseTalkConnection(cer.dataptr).HandleEvent(cer);
  663.     end;
  664.  
  665. end.