home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / Pascal / Snippets / PNL Libraries / MyConnections.p < prev    next >
Encoding:
Text File  |  1997-04-01  |  20.9 KB  |  887 lines  |  [TEXT/CWIE]

  1. unit MyConnections;
  2.  
  3. { MyConnections © Peter N Lewis, 1993-96 }
  4.  
  5. interface
  6.  
  7.     uses
  8.         Types, TCPTypes, MyTypes, OpenTransport, MyTransport, MyAssertions;
  9.  
  10.     const
  11.         tooManyConnections = -23099;
  12.         timeoutError = -23098;
  13.         failedToOpenError = -23097;
  14.         k_max_found_addresses = 10;
  15.  
  16. { Sequence: }
  17. { new(obj) }
  18. { oe:=obj.Create }
  19. { if oe=noErr then begin }
  20. {   do stuff}
  21. { end; }
  22. { obj.timetodie := true } { Don't call Destroy yourself }
  23.  
  24.     type
  25.         ConnectionBaseObject = object
  26.                 timetodie: boolean; { Set this to have Destroy called at the end of HandleConnection }
  27.                 connection_index: integer; { private! }
  28.                 closedone: boolean;
  29.                 heartbeat_period: longint; { set to <=0 to disable heartbeats }
  30.                 heartbeat_time: longint; { set to time of next Heartbeat, it is automatically incrememnted by the period }
  31. { To enable heartbeats, set heartbeat_time to TickCount, and heartbeat_period to the period in ticks }
  32.                 timeout_time: longint; { set to time to timeout TickCount }
  33.                 dnr_token: Ptr;
  34.                 hack_do_test_bad_connections: boolean;
  35.                 hack_test_bad_connections: boolean;
  36.                 function Create: OSStatus;
  37.                 procedure Destroy;
  38.                 procedure Heartbeat;
  39.                 procedure Failed (oe: OSStatus);
  40.                 procedure Close;
  41.                 procedure HandleConnection;
  42.                 procedure SetHeartBeat(period: longint);
  43.             end;
  44.         NameSearchObject = object(ConnectionBaseObject)
  45.                 ip: longint;
  46.                 procedure HandleConnection;
  47.                 override;
  48.                 procedure FindName (hostIP: longint);
  49.                 procedure FoundName (name: Str255; error: OSStatus);
  50.             end;
  51.         AddressSearchObject = object(ConnectionBaseObject)
  52.                 object_host: Str255;
  53.                 addresses: array[1..k_max_found_addresses] of ipAddr;
  54.                 procedure HandleConnection;
  55.                 override;
  56.                 procedure FindAddress (hostName: Str255);
  57.                 procedure FoundAddress (ip: longint);
  58.             end;
  59.         ListenerObject = object(ConnectionBaseObject)
  60.                 listener: Ptr;
  61.                 localport: ipPort;
  62.                 function Create: OSStatus;
  63.                 override;
  64.                 procedure Destroy;
  65.                 override;
  66.                 function CreateListener(buffersize:longint; port:ipPort; listeners:integer): OSStatus;
  67.                 procedure HandleConnection;
  68.                 override;
  69.                 procedure ConnectionAvailable( connection: TransportRef ); { override this - do not call it! }
  70.             end;
  71.         UDPObject = object(ConnectionBaseObject)
  72.                 tref: TransportUDPRef;
  73.                 localport: ipPort;
  74.                 function Create: OSStatus;
  75.                 override;
  76.                 function CreatePort (buffersize: longint; port: ipPort): OSStatus;
  77.                 procedure Close;
  78.                 override;
  79.                 procedure Destroy;
  80.                 override;
  81.                 procedure HandleConnection;
  82.                 override;
  83.                 procedure PacketAvailable (remoteip: ipAddr; remoteport: ipPort; datap: Ptr; datalen: integer);
  84.                 procedure PacketsAvailable (count: integer);
  85.                 function SendPacket (remoteip: longint; remoteport: ipPort; datap: Ptr; datalen: integer; checksum: boolean): OSStatus;
  86.             end;
  87.         statusType = (CS_None, CS_Opening, CS_Established, CS_Closing);
  88.         ConnectionObject = object(ConnectionBaseObject)
  89.                 tref: TransportRef;
  90.                 status: statusType;
  91.                 ourport: ipPort;
  92.                 input_buffer: Handle;
  93.                 output_buffer: Handle;
  94.                 transfer_error:OSStatus;
  95.                 do_send_close: Boolean;
  96.                 function Create: OSStatus;
  97.                 override;
  98.                 procedure Destroy;
  99.                 override;
  100.                 procedure HandleConnection;
  101.                 override;
  102.                 procedure NewConnection (actve: boolean; buffersize: longint; localport: ipPort; remotehost: Str255);
  103.                 procedure NewPassiveConnection (buffersize: longint; localport: ipPort);
  104.                 procedure NewActiveConnection (buffersize: longint; remotehost: Str255);
  105.                 procedure NewExistingConnection(newtref: TransportRef);
  106.                 procedure Close;
  107.                 override;
  108.                 procedure BeginConnection; { override these }
  109.                 procedure Established;
  110.                 procedure Closing;
  111.                 procedure CharsAvailable;
  112.                 procedure DoTransfer;
  113.                 procedure SendString (s: Str255);
  114.                 procedure SendData(datap: Ptr; len: longint);
  115.             end;
  116.         LineConnectionObject = object(ConnectionObject)
  117.                 crlf: CRLFTypes;
  118.                 last_check: longint; { last input_buffer size, dont recheck unless it changes }
  119.                 function Create: OSStatus;
  120.                 override;
  121.                 procedure CharsAvailable;
  122.                 override;
  123.                 procedure SendLine (s: Str255);
  124.                 procedure LineAvailable (line: Str255);
  125.                 procedure CheckLineAvailable; { You can override this and use input_buffer yourself }
  126.             end;
  127.  
  128. {$ifc not do_debug}
  129. {$definec AssertValidConnection(c) }
  130. {$elsec}
  131. {$definec AssertValidConnection(c) AssertValidConnectionCode(c)}
  132. {$endc}
  133.  
  134. {$ifc do_debug}
  135.     procedure AssertValidConnectionCode( connection: ConnectionBaseObject );
  136. {$endc}
  137.  
  138.     procedure StartupConnections;
  139.     procedure FinishConnections;
  140.     function ValidConnection( connection: ConnectionBaseObject ): boolean;
  141.  
  142. implementation
  143.  
  144.     uses
  145.         Devices, TextUtils, Memory, Events,
  146.         MyLowLevel, 
  147.         DNR, MyStrings, MyMemory, MyMathUtils, MyIPStrings, TCPUtils, MyStartup;
  148.  
  149. {$ifc undefined objects_are_handles}
  150. {$setc objects_are_handles := 1}
  151. {$endc}
  152.  
  153.     const
  154.         TCPCMagic = 'TCPC';
  155.         TCPCBadMagic = 'badc';
  156.  
  157.     const  { Tuning parameters }
  158.         connections_max = 128;
  159.         TO_FindAddress = 40 * second_in_ticks;
  160.         TO_FindName = 40 * second_in_ticks;
  161.         TO_ActiveOpen = 20 * second_in_ticks;
  162.         TO_Closing = longint(2) * minute_in_ticks;
  163.         TO_PassiveOpen = longint(1) * 365 * day_in_ticks;  { One years should be safe enough right? :-) }
  164.  
  165.     type
  166.         myHostInfo = record
  167.                 hi: hostInfo;
  168.                 done: SignedByte;
  169.             end;
  170.         myHIP = ^myHostInfo;
  171.  
  172. {$ifc do_debug}
  173.     var
  174.         startup_check: integer;
  175. {$endc}
  176.  
  177.     var
  178.         max_connections: integer;
  179.         connections: array[1..connections_max] of ConnectionBaseObject;
  180.         quiting: boolean;
  181.  
  182.     function ValidConnection( connection: ConnectionBaseObject ): boolean;
  183.         var
  184.             i: integer;
  185.     begin
  186.         ValidConnection := false;
  187.         for i := 1 to max_connections do begin
  188.             if connections[i] = connection then begin
  189.                 ValidConnection := true;
  190.                 leave;
  191.             end;
  192.         end;
  193.     end;
  194.     
  195.     procedure AssertValidConnectionCode( connection: ConnectionBaseObject );
  196.     begin
  197.         Assert( ValidConnection( connection ) );
  198.     end;
  199.     
  200.     function ConnectionBaseObject.Create: OSStatus;
  201.         var
  202.             i: integer;
  203.             err: OSStatus;
  204.     begin
  205.         AssertDidStartup( startup_check );
  206. {$ifc objects_are_handles}
  207.         LockHigh(Handle(self));
  208. {$endc}
  209.  
  210.         hack_test_bad_connections := false;
  211.         hack_do_test_bad_connections := false;
  212.  
  213.         dnr_token := nil;
  214.         err := noErr;
  215.         if quiting then begin
  216.             err := -12;
  217.         end;
  218.         if err = noErr then begin
  219.             err := OpenTransportSystem;
  220.         end;
  221.         if err = noErr then begin
  222.             i := 1;
  223.             while (i <= connections_max) & (connections[i] <> nil) do begin
  224.                 i := i + 1;
  225.             end;
  226.             if i <= connections_max then begin
  227.                 timetodie := false;
  228.                 connection_index := i;
  229.                 max_connections := Max( max_connections, i );
  230.                 connections[i] := self;
  231.                 heartbeat_period := -1;
  232.                 heartbeat_time := 0;
  233.                 timeout_time := maxLongInt;
  234.                 closedone := false;
  235.             end else begin
  236.                 connection_index := -1;
  237.                 err := tooManyConnections;
  238.             end;
  239.         end;
  240.         Create := err;
  241.     end;
  242.  
  243.     procedure ConnectionBaseObject.Destroy;
  244.     begin
  245.         if connection_index > 0 then begin
  246.             connections[connection_index] := nil;
  247.         end;
  248.         TransportAbortDNR(dnr_token);
  249.         dispose(self);
  250.     end;
  251.  
  252.     procedure ConnectionBaseObject.Heartbeat;
  253.     begin
  254.     end;
  255.  
  256.     procedure ConnectionBaseObject.Failed (err: OSStatus);
  257.     begin
  258. {$unused(err)}
  259.         timetodie := true;
  260.     end;
  261.  
  262.     procedure ConnectionBaseObject.Close;
  263.     begin
  264.         closedone := true;
  265.     end;
  266.  
  267.     procedure ConnectionBaseObject.SetHeartBeat(period: longint);
  268.         var
  269.             time: longint;
  270.     begin
  271.         time := TickCount;
  272.         if (heartbeat_period <= 0) or (period < 0) then begin
  273.             heartbeat_time := time;
  274.         end;
  275.         heartbeat_period := period;
  276.         if heartbeat_time < time then begin
  277.             heartbeat_time := time;
  278.         end;
  279.         if (heartbeat_period > 0) & (heartbeat_time > time + heartbeat_period) then begin
  280.             heartbeat_time := time + heartbeat_period;
  281.         end;
  282.     end;
  283.  
  284.     procedure ConnectionBaseObject.HandleConnection;
  285.         var
  286.             now: longint;
  287.     begin
  288.         now := TickCount;
  289.         if now > timeout_time then begin
  290.             timeout_time := maxLongInt;
  291.             Failed(timeoutError);
  292.         end else if (heartbeat_period > 0) & (now >= heartbeat_time) then begin
  293.             Heartbeat;
  294.             heartbeat_time := heartbeat_time + heartbeat_period;
  295.             if heartbeat_time < now then begin
  296.                 heartbeat_time := now;
  297.             end;
  298.         end;
  299.     end;
  300.  
  301.     procedure AddressSearchObject.FindAddress (hostName: Str255);
  302.         var
  303.             err: OSStatus;
  304.     begin
  305.         err := Create;
  306.         if err = noErr then begin
  307.             object_host := hostName;
  308.             err := TransportNameToAddr(hostName, dnr_token);
  309.             timeout_time := TickCount + TO_FindAddress;
  310.         end;
  311.         if err <> noErr then begin
  312.             Failed(err);
  313.             timetodie := true;
  314.         end;
  315.     end;
  316.  
  317.     procedure AddressSearchObject.FoundAddress (ip: longint);
  318.     begin
  319. {$unused(ip)}
  320.     end;
  321.  
  322.     procedure AddressSearchObject.HandleConnection;
  323.         var
  324.             result: OSStatus;
  325.     begin
  326.         inherited HandleConnection;
  327.         if not timetodie then begin
  328.             TransportGetNameToAddrResult(dnr_token, result, nil, @addresses, k_max_found_addresses);
  329.             if result = noErr then begin
  330.                 FoundAddress(addresses[1]);
  331.                 timetodie := true;
  332.             end else if result <> inProgress then begin
  333.                 Failed(result);
  334.                 timetodie := true;
  335.             end;
  336.         end;
  337.     end;
  338.  
  339.     procedure NameSearchObject.FindName (hostIP: longint);
  340.         var
  341.             err: OSStatus;
  342.     begin
  343.         ip := hostIP;
  344.         err := Create;
  345.         if err = noErr then begin
  346.             err := TransportAddrToName(hostIP, dnr_token);
  347.             timeout_time := TickCount + TO_FindName;
  348.         end;
  349.         if err <> noErr then begin
  350.             Failed(err);
  351.             timetodie := true;
  352.         end;
  353.     end;
  354.  
  355.     procedure NameSearchObject.FoundName (name: Str255; error: OSStatus);
  356.     begin
  357. {$unused(name, error)}
  358.     end;
  359.  
  360.     procedure NameSearchObject.HandleConnection;
  361.         var
  362.             result: OSStatus;
  363.             name:Str255;
  364.     begin
  365.         inherited HandleConnection;
  366.         if not timetodie then begin
  367.             TransportGetAddrToNameResult(dnr_token, result, name);
  368.             if result <> inProgress then begin
  369.                 if result <> noErr then begin
  370.                     IPAddrToString( ip, name );
  371.                 end;
  372.                 FoundName(name, result);
  373.                 timetodie := true;
  374.             end;
  375.         end;
  376.     end;
  377.  
  378.     function ListenerObject.Create: OSStatus;
  379.     begin
  380.         listener := nil;
  381.         localport := 0;
  382.         Create := inherited Create;
  383.     end;
  384.  
  385.     procedure ListenerObject.Destroy;
  386.     begin
  387.         if listener <> nil then begin
  388.             TransportDestroyListener( listener );
  389.         end;
  390.         inherited Destroy;
  391.     end;
  392.  
  393.     function ListenerObject.CreateListener(buffersize:longint; port:ipPort; listeners:integer): OSStatus;
  394.         var
  395.             err: OSStatus;
  396.     begin
  397.         err := Create;
  398.         if err = noErr then begin
  399.             localport := port;
  400.             err := TransportListen( listener, localport, listeners, buffersize);
  401.             timeout_time := maxLongInt;
  402.         end;
  403.         if err <> noErr then begin
  404.             timetodie := true;
  405.         end;
  406.         CreateListener := err;
  407.     end;
  408.     
  409.     procedure ListenerObject.ConnectionAvailable( connection: TransportRef );
  410.     begin
  411.         TransportDestroy( connection );
  412.     end;
  413.     
  414.     procedure ListenerObject.HandleConnection;
  415.         var
  416.             connection:TransportRef;
  417.     begin
  418.         if TransportGetListenerConnection( listener, connection ) = noErr then begin
  419.             ConnectionAvailable( connection );
  420.         end;
  421.         inherited HandleConnection;
  422.     end;
  423.     
  424.     function UDPObject.Create: OSStatus;
  425.     begin
  426.         tref := nil;
  427.         localport := 0;
  428.         Create := inherited Create;
  429.     end;
  430.  
  431.     function UDPObject.CreatePort (buffersize: longint; port: ipPort): OSStatus;
  432.         var
  433.             err: OSStatus;
  434.     begin
  435.         err := Create;
  436.         if err = noErr then begin
  437.             err := TransportUDPOpenPort(tref, port, buffersize);
  438.             localport := port;
  439.             timeout_time := maxLongInt;
  440.         end;
  441.         if err <> noErr then begin
  442.             timetodie := true;
  443.         end;
  444.         CreatePort := err;
  445.     end;
  446.  
  447.     procedure UDPObject.Close;
  448.     begin
  449.         timetodie := true;
  450.         inherited Close;
  451.     end;
  452.  
  453.     procedure UDPObject.Destroy;
  454.     begin
  455.         TransportUDPDestroy(tref);
  456.         inherited Destroy;
  457.     end;
  458.  
  459.     procedure UDPObject.PacketAvailable (remoteip: ipAddr; remoteport: ipPort; datap: Ptr; datalen: integer);
  460.     begin
  461. {$unused(remoteip, remoteport, datap, datalen)}
  462.     end;
  463.  
  464.     procedure UDPObject.PacketsAvailable (count: integer);
  465.         var
  466.             err: OSStatus;
  467.             remoteip: longint;
  468.             remoteport: ipPort;
  469.             datap: Ptr;
  470.             datalen: integer;
  471.     begin
  472. {$unused(count)}
  473.         err := TransportUDPRead (tref, remoteip, remoteport, datap, datalen);
  474.         if err = noErr then begin
  475.             PacketAvailable(remoteip, remoteport, datap, datalen);
  476.             err := TransportUDPReturnBuffer(tref, datap);
  477.         end;
  478.     end;
  479.  
  480.     function UDPObject.SendPacket (remoteip: longint; remoteport: ipPort; datap: Ptr; datalen: integer; checksum: boolean): OSStatus;
  481.     begin
  482.         SendPacket := TransportUDPWrite (tref, remoteip, remoteport, datap, datalen, checksum);
  483.     end;
  484.  
  485.     procedure UDPObject.HandleConnection;
  486.         var
  487.             count: longint;
  488.     begin
  489.         inherited HandleConnection;
  490.         if not timetodie & (tref <> nil) then begin
  491.             count := TransportUDPDatagramsAvailable(tref);
  492.             if count > 0 then begin
  493.                 PacketsAvailable(count);
  494.             end;
  495.         end;
  496.     end;
  497.  
  498.     procedure ConnectionObject.Established;
  499.     begin
  500.     end;
  501.  
  502.     procedure ConnectionObject.Closing;
  503.     begin
  504.         Close;
  505.     end;
  506.  
  507.     procedure ConnectionObject.CharsAvailable;
  508.     begin
  509.     end;
  510.  
  511.     function ConnectionObject.Create: OSStatus;
  512.         var
  513.             err, err2:OSStatus;
  514.     begin
  515.         err := inherited Create;
  516.         status := CS_None;
  517.         transfer_error := noErr;
  518.         do_send_close := false;
  519.         err2 := MNewHandle(input_buffer, 0);
  520.         if err = noErr then begin
  521.             err := err2;
  522.         end;
  523.         err2 := MNewHandle(output_buffer, 0);
  524.         if err = noErr then begin
  525.             err := err2;
  526.         end;
  527.         Create := err;
  528.     end;
  529.     
  530.     procedure ConnectionObject.Destroy;
  531.     begin
  532.         TransportDestroy(tref);
  533.         MDisposeHandle(input_buffer);
  534.         MDisposeHandle(output_buffer);
  535.         inherited Destroy;
  536.     end;
  537.  
  538.     procedure ConnectionObject.SendData(datap: Ptr; len: longint);
  539.         var
  540.             err: OSStatus;
  541.     begin
  542.         if ((status = CS_Established) or (status = CS_Closing)) and not closedone then begin
  543.             err := PtrAndHand(datap, output_buffer, len);
  544.         end else begin
  545.             err := -24;
  546.         end;
  547.         if transfer_error = noErr then begin
  548.             transfer_error := err;
  549.         end;
  550.     end;
  551.  
  552.     procedure ConnectionObject.SendString (s: Str255);
  553.     begin
  554.         SendData(@s[1], length(s));
  555.     end;
  556.  
  557.     procedure ConnectionObject.DoTransfer;
  558.         procedure SetErr(err:OSStatus);
  559.         begin
  560.             if (transfer_error = noErr) then begin
  561.                 transfer_error := err;
  562.             end;
  563.         end;
  564.         var
  565.             err: OSStatus;
  566.             count, len:longint;
  567.     begin
  568.         len := MGetHandleSize(input_buffer);
  569.         count := Min(TransportCharsAvailable(tref), 10240-len);
  570.         if count > 0 then begin
  571.             err := MSetHandleSize(input_buffer, len + count);
  572.             if err = noErr then begin
  573.                 HLock(input_buffer);
  574.                 err := TransportReceive(tref, AddPtrLong(input_buffer^, len), count, count);
  575.                 HUnlock(input_buffer);
  576.                 SetErr(err);
  577.                 SetHandleSize(input_buffer, len + count);
  578.             end;
  579.         end;
  580.  
  581.         len := MGetHandleSize(output_buffer);
  582.         if len > 0 then begin
  583.             HLock(output_buffer);
  584.             err := TransportSend(tref, output_buffer^, len);
  585.             HUnlock(output_buffer);
  586.             SetHandleSize(output_buffer, 0);
  587.             SetErr(err);
  588.         end else if do_send_close then begin
  589.             do_send_close := false;
  590.             timeout_time := TickCount + TO_Closing;
  591.             TransportSendClose(tref);
  592.         end;
  593.     end;
  594.     
  595.     procedure ConnectionObject.BeginConnection;
  596.     begin
  597.     end;
  598.  
  599.     procedure ConnectionObject.NewExistingConnection(newtref: TransportRef);
  600.         var
  601.             err: OSStatus;
  602.     begin
  603.         err := Create;
  604.         tref := newtref;
  605.         if err = noErr then begin
  606.             err := TransportHandleTransfers(tref);
  607.         end;
  608.         if err = noErr then begin
  609.             status := CS_Established;
  610.             ourport := 0;
  611.             timeout_time := maxLongInt;
  612.             BeginConnection;
  613.             Established;
  614.         end else begin
  615.             Failed(err);
  616.         end;
  617.     end;
  618.     
  619.     procedure ConnectionObject.NewConnection (active: boolean; buffersize: longint; localport: ipPort; remotehost: Str255);
  620.         var
  621.             err: OSStatus;
  622.     begin
  623.         tref := nil;
  624.         err := Create;
  625.         if err = noErr then begin
  626.             status := CS_Opening;
  627.             ourport := localport;
  628.             if active then begin
  629.                 err := TransportOpenActiveConnection(tref, remotehost, ourport, buffersize);
  630.                 timeout_time := TickCount + TO_ActiveOpen;
  631.             end else begin
  632.                 err := TransportOpenPassiveConnection(tref, ourport, buffersize);
  633.                 timeout_time := TickCount + TO_PassiveOpen;
  634.             end;
  635.         end;
  636.         if err = noErr then begin
  637.             err := TransportHandleTransfers(tref);
  638.         end;
  639.         if err = noErr then begin
  640.             BeginConnection;
  641.         end else begin
  642.             Failed(err);
  643.             timetodie := true;
  644.         end;
  645.     end;
  646.  
  647.     procedure ConnectionObject.NewPassiveConnection (buffersize: longint; localport: ipPort);
  648.     begin
  649.         NewConnection(false, buffersize, localport, '');
  650.     end;
  651.  
  652.     procedure ConnectionObject.NewActiveConnection (buffersize: longint; remotehost: Str255);
  653.     begin
  654.         NewConnection(true, buffersize, 0, remotehost);
  655.     end;
  656.  
  657.     procedure ConnectionObject.Close;
  658.     begin
  659.         if not closedone and (tref <> nil) then begin
  660.             if MGetHandleSize(output_buffer) > 0 then begin
  661.                 do_send_close := true;
  662.             end else begin
  663.                 timeout_time := TickCount + TO_Closing;
  664.                 TransportSendClose(tref);
  665.             end;
  666.         end;
  667.         closedone := true;
  668.     end;
  669.  
  670.     procedure ConnectionObject.HandleConnection;
  671.         var
  672.             state: TCPStateType;
  673.             result: OSStatus;
  674.     begin
  675.         inherited HandleConnection;
  676.         if not timetodie then begin
  677.             case status of
  678.                 CS_Opening:  begin
  679.                     TransportGetOpenResult(tref, result);
  680.                     if result = noErr then begin
  681.                         status := CS_Established;
  682.                         timeout_time := maxLongInt;
  683.                         Established;
  684.                     end else if result <> inProgress then begin
  685.                         Failed(result);
  686.                         timetodie := true;
  687.                     end;
  688.                 end;
  689.                 CS_Established:  begin
  690.                     DoTransfer;
  691.                     state := TransportGetConnectionState(tref);
  692.                     
  693.                     if hack_test_bad_connections then begin
  694.                         state := T_Dead;
  695.                     end;
  696.                     
  697.                     case state of
  698.                         T_Established:  begin
  699.                             if MGetHandleSize(input_buffer) > 0 then begin
  700.                                 CharsAvailable;
  701.  
  702.                                 if hack_do_test_bad_connections & (band( Random(), 31 ) = 1) then begin 
  703.                                     hack_test_bad_connections := true;
  704.                                 end;
  705.  
  706.                             end;
  707.                         end;
  708.                         T_PleaseClose, T_Closing:  begin
  709.                             if MGetHandleSize(input_buffer) > 0 then begin
  710.                                 CharsAvailable;
  711.                             end else begin
  712.                                 status := CS_Closing;
  713.                                 timeout_time := TickCount + TO_Closing;
  714.                                 Closing;
  715.                             end;
  716.                         end;
  717.                         T_Dead, T_Bored:  begin
  718.                             status := CS_Closing;
  719.                             timeout_time := TickCount + TO_Closing;
  720.                             Closing;
  721.                         end;
  722.                         otherwise
  723.                             ;
  724.                     end;
  725.                 end;
  726.                 CS_Closing:  begin
  727.                     DoTransfer;
  728.                     state := TransportGetConnectionState(tref);
  729.  
  730.                     if hack_test_bad_connections then begin
  731.                         state := T_Dead;
  732.                     end;
  733.                     
  734.                     case state of
  735.                         T_PleaseClose, T_Closing, T_Established:  begin
  736.                             if MGetHandleSize(input_buffer) > 0 then begin
  737.                                 CharsAvailable;
  738.                             end;
  739.                         end;
  740.                         T_Dead, T_Bored:  begin
  741.                             timetodie := true;
  742.                         end;
  743.                         otherwise
  744.                             ;
  745.                     end;
  746.                 end;
  747.                 otherwise
  748.                     ;
  749.             end;
  750.         end;
  751.     end;
  752.  
  753.     function LineConnectionObject.Create: OSStatus;
  754.     begin
  755.         crlf := CL_CRLF;
  756.         last_check := -1;
  757.         Create := inherited Create;
  758.     end;
  759.  
  760.     procedure LineConnectionObject.SendLine (s: Str255);
  761.     begin
  762.         if crlf <> CL_LF then begin
  763.             s := concat(s, cr);
  764.         end;
  765.         if crlf <> CL_CR then begin
  766.             s := concat(s, lf);
  767.         end;
  768.         SendData(@s[1], length(s));
  769.     end;
  770.  
  771.     procedure LineConnectionObject.LineAvailable (line: Str255);
  772.     begin
  773. {$unused(line)}
  774.     end;
  775.  
  776.     procedure LineConnectionObject.CharsAvailable;
  777.     begin
  778.         CheckLineAvailable;
  779.     end;
  780.  
  781.     procedure LineConnectionObject.CheckLineAvailable;
  782.         var
  783.             len, inbuf: longint;
  784.             p: Ptr;
  785.             s: Str255;
  786.     begin
  787.         while true do begin
  788.             inbuf := MGetHandleSize(input_buffer);
  789.             if (inbuf = 0) | (inbuf = last_check) then begin
  790.                 leave;
  791.             end;
  792.             p := input_buffer^;
  793.             len := 0;
  794.             while (len < inbuf) & (len < 255) & (p^ <> ord(lf)) & (p^ <> ord(cr)) do begin
  795.                 p := Ptr(ord(p) + 1);
  796.                 len := len + 1;
  797.             end;
  798.             if (len = 255) | ((len < inbuf) & ((p^ = ord(lf)) | (p^ = ord(cr)))) then begin
  799. {$PUSH}
  800. {$R-}
  801.                 s[0] := chr(len);
  802.                 BlockMoveData(input_buffer^, @s[1], len);
  803. {$POP}
  804.                 if (len < inbuf) & (p^ = ord(cr)) then begin
  805.                     p := Ptr(ord(p) + 1);
  806.                     len := len + 1;
  807.                 end;
  808.                 if (len < inbuf) & (p^ = ord(lf)) then begin
  809.                     p := Ptr(ord(p) + 1);
  810.                     len := len + 1;
  811.                 end;
  812.                 MMungerDelete(input_buffer, 0, len);
  813.                 LineAvailable(s);
  814.                 last_check := -1;
  815.             end else begin
  816.                 last_check := inbuf;
  817.             end;
  818.         end;
  819.     end;
  820.  
  821.     procedure IdleConnections;
  822.         var
  823.             i: integer;
  824.     begin
  825.         for i := 1 to max_connections do begin
  826.             if connections[i] <> nil then begin
  827.                 if not connections[i].timetodie then begin
  828.                     connections[i].HandleConnection;
  829.                 end;
  830.                 if connections[i].timetodie then begin
  831.                     connections[i].Destroy;
  832.                 end;
  833.             end;
  834.         end;
  835.     end;
  836.  
  837.     procedure DestroyAll( fail: Boolean );
  838.         var
  839.             i: integer;
  840.     begin
  841.         for i := 1 to max_connections do begin
  842.             if connections[i] <> nil then begin
  843.                 if fail then begin
  844.                     connections[i].Failed( kOTClientNotInittedErr );
  845.                 end;
  846.                 connections[i].Destroy;
  847.             end;
  848.         end;
  849.         max_connections := 0;
  850.     end;
  851.     
  852.     procedure FinishConnections;
  853.     begin
  854.         quiting := true;
  855.         DestroyAll( false );
  856.     end;
  857.     
  858.     procedure TransitionNotifier( up: boolean );
  859.     begin
  860.         if not up then begin
  861.             DestroyAll( true );
  862.         end;
  863.     end;
  864.     
  865.     function InitConnections( var msg: integer ): OSStatus;
  866.         var
  867.             i: integer;
  868.     begin
  869. {$unused(msg)}
  870.         DidStartup( startup_check );
  871.         TransportInstallTransitionNotifier( TransitionNotifier );
  872.         quiting := false;
  873.         for i := 1 to connections_max do begin
  874.             connections[i] := nil;
  875.         end;
  876.         max_connections := 0;
  877.         InitConnections := noErr;
  878.     end;
  879.     
  880.     procedure StartupConnections;
  881.     begin
  882.         StartupTransport;
  883.         SetStartup(InitConnections, IdleConnections, 0, FinishConnections);
  884.     end;
  885.  
  886. end.
  887.