home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / Pascal / Snippets / PNL Libraries / TCPOOConnections.p < prev    next >
Encoding:
Text File  |  1996-11-04  |  24.1 KB  |  1,065 lines  |  [TEXT/CWIE]

  1. unit TCPOOConnections;
  2.  
  3. { TCPOOConnections © Peter Lewis, April 1993 }
  4.  
  5. interface
  6.  
  7.     uses
  8.         TCPTypes, TCPStuff, MyTypes, MyStrings;
  9.  
  10.     const
  11.         tooManyConnections = -23099;
  12.         timeoutError = -23098;
  13.         failedToOpenError = -23097;
  14.  
  15. { Sequence: }
  16. { new(obj) }
  17. { oe:=obj.Create }
  18. { if oe=noErr then begin }
  19. {   do stuff}
  20. { end; }
  21. { obj.Destroy }
  22.  
  23.     type
  24.         ConnectionBaseObject = object
  25.                 timetodie: boolean; { Set this to have Destroy called at the end of HandleConnection }
  26.                 connection_index: integer; { private! }
  27.                 closedone, terminatedone: boolean;
  28.                 heartbeat_period: longint; { set to <=0 to disable heartbeats }
  29.                 heartbeat_time: longint; { set to time of next Heartbeat, it is automatically incrememnted by the period }
  30. { To enable heartbeats, set heartbeat_time to TickCount, and heartbeat_period to the period in ticks }
  31.                 timeout_time: longint; { set to time to timeout TickCount }
  32.                 drp: DNRRecordPtr; { private! }
  33.                 function Create: OSErr;
  34.                 procedure Destroy;
  35.                 procedure Heartbeat;
  36.                 procedure Failed (oe: OSErr);
  37.                 procedure Timeout;
  38.                 procedure Terminate;
  39.                 procedure Close;
  40.                 function HandleConnection: boolean;
  41.             end;
  42.         NameSearchObject = object(ConnectionBaseObject)
  43.                 ip: longint;
  44.                 function HandleConnection: boolean;
  45.                 override;
  46.                 procedure FindName (hostIP: longint);
  47.                 procedure FoundName (name: Str255; error: OSErr);
  48.             end;
  49.         DNRSearchObject = object(ConnectionBaseObject)
  50.                 object_host: Str255;
  51.                 function HandleConnection: boolean;
  52.                 override;
  53.                 procedure Terminate;
  54.                 override;
  55.                 procedure DoQuery;
  56.                 procedure Find (hostName: Str255);
  57.                 procedure Found;
  58.             end;
  59.         AddressSearchObject = object(DNRSearchObject)
  60.                 procedure DoQuery;
  61.                 override;
  62.                 procedure Found;
  63.                 override;
  64.                 procedure FoundAddress (ip: longint);
  65.             end;
  66.         HInfoSearchObject = object(DNRSearchObject)
  67.                 procedure DoQuery;
  68.                 override;
  69.                 procedure Found;
  70.                 override;
  71.                 procedure FoundHInfo ( cpu, os: Str31 );
  72.             end;
  73.         UDPObject = object(ConnectionBaseObject)
  74.                 udpcp: UDPConnectionPtr;
  75.                 localport: integer;
  76.                 function Create: OSErr;
  77.                 override;
  78.                 function CreatePort (buffer_size: longint; port: integer): OSErr;
  79.                 procedure Close;
  80.                 override;
  81.                 procedure Terminate;
  82.                 override;
  83.                 procedure Destroy;
  84.                 override;
  85.                 function HandleConnection: boolean;
  86.                 override;
  87.                 procedure PacketAvailable (remoteIP: longint; remoteport: integer; datap: Ptr; datalen: integer);
  88.                 procedure PacketsAvailable (count: integer);
  89.                 function SendPacket (remoteIP: longint; remoteport: integer; datap: Ptr; datalen: integer; checksum: boolean): OSErr;
  90.             end;
  91.         statusType = (CS_LookingUpAddr, CS_Opening, CS_Established, CS_Closing);
  92.         ConnectionObject = object(ConnectionBaseObject)
  93.                 lookingupname: boolean;
  94.                 active: boolean;
  95.                 thebuffersize: longint;
  96.                 ourip: longint;
  97.                 ourport: integer;
  98.                 theirip: longint;
  99.                 theirport: integer;
  100.                 tcpc: TCPConnectionPtr;
  101.                 status: statusType;
  102.                 object_host: Str255;
  103.                 procedure Destroy;
  104.                 override;
  105.                 function HandleConnection: boolean;
  106.                 override;
  107.                 procedure NewConnection (actve: boolean; buffersize: longint; localport: integer; remotehost: Str255; remoteport: integer);
  108.                 procedure NewPassiveConnection (buffersize: longint; localport: integer);
  109.                 procedure NewActiveConnection (buffersize: longint; remotehost: Str255; remoteport: integer);
  110.                 procedure StartConnection;
  111.                 procedure Close;
  112.                 override;
  113.                 procedure Terminate;
  114.                 override;
  115.                 procedure BeginConnection; { override these }
  116.                 procedure Established;
  117.                 procedure Closing;
  118.                 procedure CharsAvailable (count: longint);
  119.                 function MyCharsAvailable: longint;
  120.             end;
  121.         LineConnectionObject = object(ConnectionObject)
  122.                 crlf: CRLFTypes;
  123.                 buffer_len: longint; { Current number of characters in buffer }
  124.                 buffer: Handle; { Size initially set to 512 bytes, change it as you wish }
  125.                 last_check: longint; { buffer_len when we last checked for a line, don't recheck unless it changes }
  126.                 pushFlag: boolean; { Hack for the occasionally non-pushed lines, set to true every send }
  127.                 line_send_error: OSErr;
  128.                 function Create: OSErr;
  129.                 override;
  130.                 procedure Destroy;
  131.                 override;
  132.                 procedure SendLine (s: Str255);
  133.                 procedure LineAvailable (line: Str255);
  134.                 function CheckLineAvailable: boolean; { You can override this and use buffer & buffer_len yourself }
  135.                 function HandleConnection: boolean;
  136.                 override;
  137.                 procedure CharsAvailable (count: longint); { Note: includes buffer_len }
  138.                 override;
  139.                 function MyCharsAvailable: longint;
  140.                 override;
  141.             end;
  142.  
  143.     var
  144.         tcp_our_ip: ipAddr;
  145.         tcp_our_str: Str31;
  146.         tcp_our_name: Str255;
  147.  
  148.     procedure StartupConnections;
  149.     procedure ConfigureConnections( findourname: Boolean );
  150.     function HandleConnections (maxtime: integer): boolean;
  151.     procedure ConnectionsAddrToString (ip: longint; var addrStr: Str255);
  152.     function ConnectionsAddrToStr (ip: longint): Str255;
  153.     function ConnectionsStrToAddr (s: Str255; var addr: longint): boolean;
  154. { You probably wont need these: }
  155.     procedure TerminateConnections;
  156.     procedure CloseConnections;
  157.     function CanQuit: boolean;
  158.  
  159. implementation
  160.  
  161.     uses
  162.         DNR, MyStartup, MyMemory, MyAssertions, MyCStrings;
  163.  
  164.     const
  165.         TCPCMagic = 'TCPC';
  166.         TCPCBadMagic = 'badc';
  167.  
  168.     const  { Tuning parameters }
  169.         max_connections = 64;
  170.         TO_FindAddress = 40 * second_in_ticks;
  171.         TO_FindName = 40 * second_in_ticks;
  172.         TO_ActiveOpen = 20 * second_in_ticks;
  173.         TO_Closing = longint(2) * minute_in_ticks;
  174.         TO_PassiveOpen = longint(1) * 365 * day_in_ticks;  { One years should be safe enough right? :-) }
  175.  
  176.     type
  177.         myHostInfo = record
  178.                 hi: hostInfo;
  179.                 done: SignedByte;
  180.             end;
  181.         myHIP = ^myHostInfo;
  182.  
  183.     type
  184.         connectionRecord = record
  185.                 obj: ConnectionBaseObject;
  186.             end;
  187.  
  188.     var
  189.         connections: array[1..max_connections] of connectionRecord;
  190.         quiting: boolean;
  191.         gFindOurName: Boolean;
  192.  
  193.     procedure TrashHandle (h: Handle);
  194.         var
  195.             p: Ptr;
  196.             i: longint;
  197.     begin
  198.         if (h <> nil) & (h^ <> nil) then begin
  199.             p := h^;
  200.             for i := 1 to GetHandleSize(h) do begin
  201.                 p^ := -27;
  202.                 longint(p) := longint(p) + 1;
  203.             end;
  204.         end;
  205.     end;
  206.  
  207.     function MyTCPState (con: TCPConnectionPtr): TCPStateType;
  208.     begin
  209.         if con = nil then begin
  210.             MyTCPState := T_Dead;
  211.         end else begin
  212.             MyTCPState := TCPState(con);
  213.         end;
  214.     end;
  215.  
  216.     type
  217.         LookupMyName = object(NameSearchObject)
  218.                 procedure FoundName (name: Str255; error: OSErr);
  219.                 override;
  220.             end;
  221.  
  222.     procedure LookupMyName.FoundName (name: Str255; error: OSErr);
  223.     begin
  224. {$unused(error)}
  225.         tcp_our_name := name;
  226.     end;
  227.  
  228.     function InitConnections(var msg: integer): OSStatus;
  229.         var
  230.             oe: OSErr;
  231.             i: integer;
  232.             lobj: LookupMyName;
  233.     begin
  234. {$unused(msg)}
  235.         quiting := false;
  236.         icmp_sent_out := 0;
  237.         icmp_got_back := 0;
  238.         for i := 1 to max_connections do begin
  239.             connections[i].obj := nil;
  240.         end;
  241.         oe := OpenResolver;
  242.         if oe = noErr then begin
  243.             oe := IPGetMyIPAddr(tcp_our_ip);
  244.             tcp_our_str := ConnectionsAddrToStr(tcp_our_ip);
  245.             tcp_our_name := tcp_our_str;
  246.             if gFindOurName then begin
  247.                 new(lobj);
  248.                 lobj.FindName(tcp_our_ip);
  249.             end;
  250.         end;
  251.         InitConnections := oe;
  252.     end;
  253.  
  254.     procedure TerminateConnections;
  255.         var
  256.             i: integer;
  257.     begin
  258.         for i := 1 to max_connections do begin
  259.             if connections[i].obj <> nil then begin
  260.                 if not connections[i].obj.terminatedone then begin
  261.                     connections[i].obj.Terminate;
  262.                 end;
  263.             end;
  264.         end;
  265.     end;
  266.     
  267.     procedure CloseConnections;
  268.         var
  269.             i: integer;
  270.     begin
  271.         for i := 1 to max_connections do begin
  272.             if connections[i].obj <> nil then begin
  273.                 connections[i].obj.Close;
  274.             end;
  275.         end;
  276.     end;
  277.  
  278.     function CanQuit: boolean;
  279.         var
  280.             i: integer;
  281.     begin
  282.         CanQuit := icmp_sent_out = icmp_got_back;
  283.         for i := 1 to max_connections do begin
  284.             if connections[i].obj <> nil then begin
  285.                 CanQuit := false;
  286.                 leave;
  287.             end;
  288.         end;
  289.     end;
  290.  
  291.     procedure FinishConnections;
  292.         var
  293.             dummy: boolean;
  294.             er: EventRecord;
  295.     begin
  296.         quiting := true;
  297.         while not CanQuit do begin
  298.             TerminateConnections;
  299.             if HandleConnections(3) then begin
  300.                 dummy := WaitNextEvent(everyEvent, er, 0, nil);
  301.             end else begin
  302.                 dummy := WaitNextEvent(everyEvent, er, 5, nil);
  303.             end;
  304.         end;
  305.         CloseResolver;
  306.     end;
  307.  
  308.     procedure StartupConnections;
  309.     begin
  310.         StartupTCPStuff;
  311.         SetStartup( InitConnections, nil, 0, FinishConnections );
  312.     end;
  313.  
  314.     procedure ConfigureConnections( findourname: Boolean );
  315.     begin
  316.         gFindOurName := findourname;
  317.         StartupConnections;
  318.     end;
  319.     
  320.     function ConnectionBaseObject.Create: OSErr;
  321.         var
  322.             i: integer;
  323.             oe: OSErr;
  324.     begin
  325.         MoveHHi(Handle(self));
  326.         HLock(Handle(self));
  327.         connection_index := -1;
  328.         drp := nil;
  329.         if quiting then begin
  330.             oe := -12;
  331.         end else begin
  332.             i := 1;
  333.             while (i <= max_connections) & (connections[i].obj <> nil) do begin
  334.                 i := i + 1;
  335.             end;
  336.             if i <= max_connections then begin
  337.                 timetodie := false;
  338.                 connection_index := i;
  339.                 connections[connection_index].obj := self;
  340.                 heartbeat_period := 0;
  341.                 heartbeat_time := 0;
  342.                 timeout_time := maxLongInt;
  343.                 closedone := false;
  344.                 terminatedone := false;
  345.                 oe := MNewPtr( drp, SizeOf(DNRRecord) );
  346.                 if ( oe = noErr ) then begin
  347.                     drp^.ioResult := noErr;
  348.                 end;
  349.             end else begin
  350.                 connection_index := -1;
  351.                 oe := tooManyConnections;
  352.             end;
  353.         end;
  354.         Create := oe;
  355.     end;
  356.  
  357.     procedure ConnectionBaseObject.Destroy;
  358.     begin
  359.         if connection_index > 0 then begin
  360.             connections[connection_index].obj := nil;
  361.         end;
  362.         if (drp <> nil) & (drp^.ioResult <> inProgress) then begin { we leak the record if it is in progress - better than crashing }
  363.             MDisposePtr(drp);
  364.         end;
  365.         dispose(self);
  366.     end;
  367.  
  368.     procedure ConnectionBaseObject.Heartbeat;
  369.     begin
  370.     end;
  371.  
  372.     procedure ConnectionBaseObject.Failed (oe: OSErr);
  373.     begin
  374. {$unused(oe)}
  375.         timetodie := true;
  376.     end;
  377.  
  378.     procedure ConnectionBaseObject.Timeout;
  379.     begin
  380.         Failed(timeoutError);
  381.     end;
  382.  
  383.     procedure ConnectionBaseObject.Terminate;
  384.     begin
  385.         terminatedone := true;
  386.     end;
  387.  
  388.     procedure ConnectionBaseObject.Close;
  389.     begin
  390.         closedone := true;
  391.     end;
  392.  
  393.     function ConnectionBaseObject.HandleConnection: boolean;
  394.         var
  395.             now: longint;
  396.     begin
  397.         HandleConnection := false;
  398.         now := TickCount;
  399.         if now > timeout_time then begin
  400.             timeout_time := maxLongInt;
  401.             Timeout;
  402.             HandleConnection := true;
  403.         end else if (heartbeat_period > 0) & (now >= heartbeat_time) then begin
  404.             Heartbeat;
  405.             heartbeat_time := heartbeat_time + heartbeat_period;
  406.             HandleConnection := true;
  407.         end;
  408.     end;
  409.  
  410.     procedure DNRSearchObject.Find (hostName: Str255);
  411.         var
  412.             oe: OSErr;
  413.     begin
  414.         oe := Create;
  415.         if oe = noErr then begin
  416.             object_host := hostName;
  417.             DoQuery;
  418.             timeout_time := TickCount + TO_FindAddress;
  419.         end else begin 
  420.             Failed(oe);
  421.             Destroy;
  422.         end;
  423.     end;
  424.     
  425.     procedure DNRSearchObject.Terminate;
  426.     begin
  427.         if quiting then begin
  428.             drp := nil;
  429.             Failed( commandTimeoutErr );
  430.         end;
  431.         inherited Terminate;
  432.     end;
  433.     
  434.     procedure DNRSearchObject.DoQuery;
  435.     begin
  436.         Assert( false );
  437.     end;
  438.  
  439.     procedure DNRSearchObject.Found;
  440.     begin
  441.         Assert( false );
  442.     end;
  443.  
  444.     function DNRSearchObject.HandleConnection: boolean;
  445.     begin
  446.         if drp^.ioResult = noErr then begin
  447. {    TCPSetCache(hi, object_host);}
  448.             Found;
  449.             timetodie := true;
  450.             HandleConnection := true;
  451.         end else if drp^.ioResult <> inProgress then begin
  452.             Failed(drp^.ioResult);
  453.             timetodie := true;
  454.             HandleConnection := true;
  455.         end else begin
  456.             HandleConnection := inherited HandleConnection;
  457.         end;
  458.     end;
  459.  
  460.     procedure AddressSearchObject.DoQuery;
  461.     begin
  462.         DNRNameToAddr( object_host, drp, nil );
  463.     end;
  464.     
  465.     procedure AddressSearchObject.Found;
  466.     begin
  467.         FoundAddress( drp^.addr );
  468.     end;
  469.     
  470.     procedure AddressSearchObject.FoundAddress (ip: longint);
  471.     begin
  472. {$unused(ip)}
  473.         Assert( false );
  474.     end;
  475.  
  476.     procedure HInfoSearchObject.DoQuery;
  477.     begin
  478.         DNRNameToHInfo( object_host, drp, nil );
  479.     end;
  480.     
  481.     procedure HInfoSearchObject.Found;
  482.         var
  483.             cpu, os: Str255;
  484.     begin
  485.         CopyC2P( @drp^.hi.hinfo.cpuType, cpu );
  486.         if length( cpu ) > 30 then begin
  487.             cpu[0] := chr(30);
  488.         end;
  489.         CopyC2P( @drp^.hi.hinfo.osType, os );
  490.         if length( os ) > 30 then begin
  491.             os[0] := chr(30);
  492.         end;
  493.         FoundHInfo( cpu, os );
  494.     end;
  495.     
  496.     procedure HInfoSearchObject.FoundHInfo ( cpu, os: Str31 );
  497.     begin
  498. {$unused(cpu, os)}
  499.         Assert( false );
  500.     end;
  501.  
  502.     procedure NameSearchObject.FindName (hostIP: longint);
  503.         var
  504.             oe: OSErr;
  505.     begin
  506.         ip := hostIP;
  507.         oe := Create;
  508.         if oe = noErr then begin
  509.             DNRAddrToName(hostIP, drp, nil);
  510.             timeout_time := TickCount + TO_FindName;
  511.         end;
  512.         if oe <> noErr then begin
  513.             Failed(oe);
  514.             Destroy;
  515.         end;
  516.     end;
  517.  
  518.     procedure NameSearchObject.FoundName (name: Str255; error: OSErr);
  519.     begin
  520. {$unused(name, error)}
  521.     end;
  522.  
  523.     function NameSearchObject.HandleConnection: boolean;
  524.     begin
  525.         if drp^.ioResult <> inProgress then begin
  526.             FoundName(drp^.name, drp^.ioResult);
  527.             timetodie := true;
  528.             HandleConnection := true;
  529.         end else begin
  530.             HandleConnection := inherited HandleConnection;
  531.         end;
  532.     end;
  533.  
  534.     procedure ConnectionObject.Established;
  535.     begin
  536.     end;
  537.  
  538.     procedure ConnectionObject.Closing;
  539.     begin
  540.         Close;
  541.     end;
  542.  
  543.     procedure ConnectionObject.CharsAvailable (count: longint);
  544.     begin
  545. {$unused(count)}
  546.     end;
  547.  
  548.     function ConnectionObject.MyCharsAvailable: longint;
  549.     begin
  550.         MyCharsAvailable := TCPCharsAvailable(tcpc);
  551.     end;
  552.     
  553.     procedure ConnectionObject.Destroy;
  554.         var
  555.             tmp_tcpc: TCPConnectionPtr;
  556.             oe: OSErr;
  557.     begin
  558.         if tcpc <> nil then begin
  559.             tmp_tcpc := tcpc;
  560.             oe := TCPRelease(tmp_tcpc);
  561.         end;
  562.         inherited Destroy;
  563.     end;
  564.  
  565.     procedure ConnectionObject.BeginConnection;
  566.     begin
  567.     end;
  568.  
  569.     procedure ConnectionObject.StartConnection;
  570.         var
  571.             oe: OSErr;
  572.             tmp_tcpc: TCPConnectionPtr;
  573.     begin
  574.         if active then begin
  575.             oe := TCPActiveOpen(tmp_tcpc, thebuffersize, ourport, theirip, theirport, nil);
  576.             timeout_time := TickCount + TO_ActiveOpen;
  577.         end else begin
  578.             oe := TCPPassiveOpen(tmp_tcpc, thebuffersize, ourport, theirip, theirport, nil);
  579.             timeout_time := TickCount + TO_PassiveOpen;
  580.         end;
  581.         tcpc := tmp_tcpc;
  582.         status := CS_Opening;
  583.         if oe = noErr then begin
  584.             ourport := TCPLocalPort(tcpc);
  585.             BeginConnection;
  586.         end else begin
  587.             Failed(oe);
  588.             timetodie := true;
  589.         end;
  590.     end;
  591.  
  592.     procedure ConnectionObject.NewConnection (actve: boolean; buffersize: longint; localport: integer; remotehost: Str255; remoteport: integer);
  593.         var
  594.             oe: OSErr;
  595.             ip: longint;
  596.     begin
  597.         status := CS_LookingUpAddr;
  598.         tcpc := nil;
  599.         oe := Create;
  600.         if oe = noErr then begin
  601.             active := actve;
  602.             thebuffersize := buffersize;
  603.             ourport := localport;
  604.             ourip := tcp_our_ip;
  605.             theirip := 0;
  606.             theirport := remoteport;
  607.             ip := 0;
  608.             if (remotehost = '') | ConnectionsStrToAddr(remotehost, ip) then begin
  609.                 if (ip = 0) & active then begin
  610.                     oe := -11;
  611.                 end else begin
  612.                     theirip := ip;
  613.                     MDisposePtr(drp);
  614.                     StartConnection;
  615.                 end;
  616.             end else begin
  617.                 object_host := remotehost;
  618.                 DNRNameToAddr(remotehost, drp, nil);
  619.                 timeout_time := TickCount + TO_FindAddress;
  620.             end;
  621.         end;
  622.         if oe <> noErr then begin
  623.             tcpc := nil;
  624.             Failed(oe);
  625.             timetodie := true;
  626.         end;
  627.         if timetodie then begin
  628.             Destroy;
  629.         end;
  630.     end;
  631.  
  632.     procedure ConnectionObject.NewPassiveConnection (buffersize: longint; localport: integer);
  633.     begin
  634.         NewConnection(false, buffersize, localport, '', 0);
  635.     end;
  636.  
  637.     procedure ConnectionObject.NewActiveConnection (buffersize: longint; remotehost: Str255; remoteport: integer);
  638.     begin
  639.         NewConnection(true, buffersize, 0, remotehost, remoteport);
  640.     end;
  641.  
  642.     procedure ConnectionObject.Close;
  643.         var
  644.             oe: OSErr;
  645.     begin
  646.         if not closedone and (tcpc <> nil) then begin
  647.             oe := TCPClose(tcpc, nil);
  648.             closedone := true;
  649.         end;
  650.     end;
  651.  
  652.     procedure ConnectionObject.Terminate;
  653.         var
  654.             oe: OSErr;
  655.     begin
  656.         if not terminatedone and (tcpc <> nil) then begin
  657.             oe := TCPAbort(tcpc);
  658.             terminatedone := true;
  659.         end;
  660.     end;
  661.  
  662.     function ConnectionObject.HandleConnection: boolean;
  663.         var
  664.             didit: boolean;
  665.             count: longint;
  666.             state: TCPStateType;
  667.     begin
  668.         didit := false;
  669.         state := MyTCPState(tcpc);
  670.         case status of
  671.             CS_LookingUpAddr:  begin
  672.                 if drp^.ioResult = noErr then begin
  673. {    TCPSetCache(myHIP(hip)^.hi, object_host);}
  674.                     theirip := drp^.addr;
  675.                     MDisposePtr(drp);
  676.                     StartConnection;
  677.                     didit := true;
  678.                 end else if drp^.ioResult <> inProgress then begin
  679.                     Failed(drp^.ioResult);
  680.                     timetodie := true;
  681.                     didit := true;
  682.                 end;
  683.             end;
  684.             CS_Opening:  begin
  685.                 case state of
  686.                     T_WaitingForOpen, T_Opening, T_Bored: 
  687.                         ;
  688.                     T_Established:  begin
  689.                         Established;
  690.                         status := CS_Established;
  691.                         timeout_time := maxLongInt;
  692.                         didit := true;
  693.                     end;
  694.                     T_PleaseClose, T_Closing, T_Dead:  begin
  695.                         didit := true;
  696.                         Failed(failedToOpenError);
  697.                         timetodie := true;
  698.                     end;
  699.                     otherwise begin
  700.                         { do nothing }
  701.                     end;
  702.                 end; {case }
  703.             end;
  704.             CS_Established:  begin
  705.                 case state of
  706.                     T_Established:  begin
  707.                         count := MyCharsAvailable;
  708.                         if count > 0 then begin
  709.                             CharsAvailable(count);
  710.                             didit := true;
  711.                         end;
  712.                     end;
  713.                     T_PleaseClose, T_Closing:  begin
  714.                         count := MyCharsAvailable;
  715.                         if count > 0 then begin
  716.                             CharsAvailable(count);
  717.                             didit := true;
  718.                         end else begin
  719.                             Closing;
  720.                             status := CS_Closing;
  721.                             timeout_time := TickCount + TO_Closing;
  722.                             didit := true;
  723.                         end;
  724.                     end;
  725.                     T_Dead:  begin
  726.                         Closing;
  727.                         status := CS_Closing;
  728.                         timeout_time := TickCount + TO_Closing;
  729.                         didit := true;
  730.                     end;
  731.                     otherwise begin
  732.                         { do nothing }
  733.                     end;
  734.                 end;
  735.             end;
  736.             CS_Closing:  begin
  737.                 case state of
  738.                     T_PleaseClose, T_Closing, T_Established:  begin
  739.                         count := MyCharsAvailable;
  740.                         if count > 0 then begin
  741.                             CharsAvailable(count);
  742.                             didit := true;
  743.                         end;
  744.                     end;
  745.                     T_Dead:  begin
  746.                         timetodie := true;
  747.                         didit := true;
  748.                     end;
  749.                     otherwise begin
  750.                         { do nothing }
  751.                     end;
  752.                 end;
  753.             end;
  754.             otherwise begin
  755.                 { do nothing }
  756.             end;
  757.         end;
  758.         didit := didit | inherited HandleConnection;
  759.         HandleConnection := didit;
  760.     end;
  761.  
  762.     function LineConnectionObject.Create: OSErr;
  763.         var
  764.             err, err2: OSErr;
  765.     begin
  766.         err := inherited Create;
  767.         crlf := CL_CRLF;
  768.         err2 := MNewHandle( buffer, 512 );
  769.         if err = noErr then begin
  770.             err := err2;
  771.         end;
  772.         buffer_len := 0;
  773.         last_check := -1;
  774.         pushFlag := true;
  775.         line_send_error := noErr;
  776.         Create := err;
  777.     end;
  778.  
  779.     procedure LineConnectionObject.Destroy;
  780.     begin
  781.         MDisposeHandle(buffer);
  782.         inherited Destroy;
  783.     end;
  784.  
  785.     procedure LineConnectionObject.SendLine (s: Str255);
  786.         var
  787.             oe: OSErr;
  788.     begin
  789.         if crlf <> CL_LF then begin
  790.             s := concat(s, cr);
  791.         end;
  792.         if crlf <> CL_CR then begin
  793.             s := concat(s, lf);
  794.         end;
  795.         oe := TCPSendAsync(tcpc, @s[1], length(s), pushFlag, nil);
  796.         if line_send_error = noErr then begin
  797.             line_send_error := oe;
  798.         end;
  799.         pushFlag := true;
  800.     end;
  801.  
  802.     procedure LineConnectionObject.LineAvailable (line: Str255);
  803.     begin
  804. {$unused(line)}
  805.     end;
  806.  
  807.     procedure LineConnectionObject.CharsAvailable (count: longint);
  808.         var
  809.             space: longint;
  810.             oe: OSErr;
  811.             dummy: boolean;
  812.     begin
  813.         count := TCPCharsAvailable(tcpc);
  814.         space := GetHandleSize(buffer) - buffer_len;
  815.         if count > space then begin
  816.             count := space;
  817.         end;
  818.         if count > 32767 then begin
  819.             count := 32767;
  820.         end;
  821.         if count > 0 then begin
  822.             HLock(buffer);
  823.             oe := TCPRawReceiveChars(tcpc, Ptr(ord(buffer^) + buffer_len), count);
  824.             HUnlock(buffer);
  825.             buffer_len := buffer_len + count;
  826.         end;
  827.         dummy := CheckLineAvailable;
  828.     end;
  829.  
  830.     function LineConnectionObject.MyCharsAvailable: longint;
  831.     begin
  832.         MyCharsAvailable := TCPCharsAvailable(tcpc) + buffer_len;
  833.     end;
  834.     
  835.     function LineConnectionObject.CheckLineAvailable: boolean;
  836.         var
  837.             len: longint;
  838.             p: Ptr;
  839.             s: Str255;
  840.     begin
  841.         CheckLineAvailable := false;
  842.         if (buffer_len > 0) & (buffer_len <> last_check) then begin
  843.             p := buffer^;
  844.             len := 0;
  845.             while (len < buffer_len) & (len < 255) & (p^ <> ord(lf)) & (p^ <> ord(cr)) do begin
  846.                 p := Ptr(ord(p) + 1);
  847.                 len := len + 1;
  848.             end;
  849.             if (len = 255) | ((len < buffer_len) & ((p^ = ord(lf)) | (p^ = ord(cr)))) then begin
  850. {$PUSH}
  851. {$R-}
  852.                 s[0] := chr(len);
  853.                 BlockMoveData(buffer^, @s[1], len);
  854. {$POP}
  855.                 if (len < buffer_len) & (p^ = ord(cr)) then begin
  856.                     p := Ptr(ord(p) + 1);
  857.                     len := len + 1;
  858.                 end;
  859.                 if (len < buffer_len) & (p^ = ord(lf)) then begin
  860.                     p := Ptr(ord(p) + 1);
  861.                     len := len + 1;
  862.                 end;
  863.                 BlockMoveData(p, buffer^, buffer_len - len);
  864.                 buffer_len := buffer_len - len;
  865.                 LineAvailable(s);
  866.                 CheckLineAvailable := true;
  867.                 last_check := -1;
  868.             end else begin
  869.                 last_check := buffer_len;
  870.             end;
  871.         end;
  872.     end;
  873.  
  874.     function LineConnectionObject.HandleConnection: boolean;
  875.         var
  876.             result: boolean;
  877.     begin
  878.         result := inherited HandleConnection;
  879.         if not result & (status in [CS_Established, CS_Closing]) then begin
  880.             result := CheckLineAvailable;
  881.         end;
  882.         HandleConnection := result;
  883.     end;
  884.  
  885.     function UDPObject.Create: OSErr;
  886.     begin
  887.         udpcp := nil;
  888.         localport := 0;
  889.         Create := inherited Create;
  890.     end;
  891.  
  892.     function UDPObject.CreatePort (buffer_size: longint; port: integer): OSErr;
  893.         var
  894.             oe: OSErr;
  895.             tmp_udpcp: UDPConnectionPtr;
  896.     begin
  897.         udpcp := nil;
  898.         oe := Create;
  899.         if oe = noErr then begin
  900.             oe := UDPCreate(tmp_udpcp, buffer_size, port);
  901.             udpcp := tmp_udpcp;
  902.             localport := port;
  903.             timeout_time := maxLongInt;
  904.         end;
  905.         if oe <> noErr then begin
  906.             Destroy;
  907.         end;
  908.         CreatePort := oe;
  909.     end;
  910.  
  911.     procedure UDPObject.Terminate;
  912.     begin
  913.         terminatedone := true;
  914.         timetodie := true;
  915.     end;
  916.  
  917.     procedure UDPObject.Close;
  918.         var
  919.             tmp_udpcp: UDPConnectionPtr;
  920.             oe: OSErr;
  921.     begin
  922.         if udpcp <> nil then begin
  923.             tmp_udpcp := udpcp;
  924.             oe := UDPRelease(tmp_udpcp);
  925.             udpcp := nil;
  926.         end;
  927.         timetodie := true;
  928.     end;
  929.  
  930.     procedure UDPObject.Destroy;
  931.     begin
  932.         if udpcp <> nil then begin
  933.             Close;
  934.         end;
  935.         inherited Destroy;
  936.     end;
  937.  
  938.     procedure UDPObject.PacketAvailable (remoteIP: longint; remoteport: integer; datap: Ptr; datalen: integer);
  939.     begin
  940. {$unused(remoteIP, remoteport, datap, datalen)}
  941.     end;
  942.  
  943.     procedure UDPObject.PacketsAvailable (count: integer);
  944.         var
  945.             oe: OSErr;
  946.             remoteIP: longint;
  947.             remoteport: integer;
  948.             datap: Ptr;
  949.             datalen: integer;
  950.             u: UDPConnectionPtr;
  951.     begin
  952. {$unused(count)}
  953.         oe := UDPRead(udpcp, 1, remoteIP, remoteport, datap, datalen);
  954.         if oe = noErr then begin
  955.             u := udpcp;
  956.             PacketAvailable(remoteIP, remoteport, datap, datalen);
  957. { self may be nil now }
  958.             oe := UDPReturnBuffer(u, datap);
  959.         end;
  960.     end;
  961.  
  962.     function UDPObject.SendPacket (remoteIP: longint; remoteport: integer; datap: Ptr; datalen: integer; checksum: boolean): OSErr;
  963.     begin
  964.         SendPacket := UDPWrite(udpcp, remoteIP, remoteport, datap, datalen, checksum);
  965.     end;
  966.  
  967.     function UDPObject.HandleConnection: boolean;
  968.         var
  969.             didit: boolean;
  970.             count: longint;
  971.     begin
  972.         didit := false;
  973.         if udpcp <> nil then begin
  974.             count := UDPDatagramsAvailable(udpcp);
  975.             if count > 0 then begin
  976.                 PacketsAvailable(count);
  977.                 didit := true;
  978.             end;
  979.         end;
  980.         HandleConnection := didit | inherited HandleConnection;
  981.     end;
  982.  
  983.     function HandleConnections (maxtime: integer): boolean;
  984.         var
  985.             did, didany: boolean;
  986.             start: longint;
  987.             i: integer;
  988.     begin
  989.         start := TickCount;
  990.         didany := false;
  991.         repeat
  992.             did := false;
  993.             for i := 1 to max_connections do begin
  994.                 if connections[i].obj <> nil then begin
  995.                     if connections[i].obj.HandleConnection then begin
  996.                         did := true;
  997.                         didany := true;
  998.                     end;
  999.                     if (connections[i].obj <> nil) & (connections[i].obj.timetodie) then begin
  1000.                         connections[i].obj.Destroy;
  1001.                     end;
  1002.                 end;{if}
  1003.             end; {for}
  1004.         until not did or (TickCount >= start + maxtime);
  1005.         HandleConnections := didany;
  1006.     end;
  1007.  
  1008.     function ConnectionsStrToAddr (s: Str255; var addr: longint): boolean;
  1009.         var
  1010.             good: boolean;
  1011.         procedure Get1;
  1012.             var
  1013.                 b: integer;
  1014.         begin
  1015.             if (length(s) = 0) | not (s[1] in ['0'..'9']) then begin
  1016.                 good := false;
  1017.             end else begin
  1018.                 b := ord(s[1]) - 48;
  1019.                 s := TPcopy(s, 2, 255);
  1020.                 if (s <> '') & (s[1] in ['0'..'9']) then begin
  1021.                     b := b * 10 + ord(s[1]) - 48;
  1022.                     s := TPcopy(s, 2, 255);
  1023.                 end;
  1024.                 if (s <> '') & (s[1] in ['0'..'9']) then begin
  1025.                     b := b * 10 + ord(s[1]) - 48;
  1026.                     s := TPcopy(s, 2, 255);
  1027.                 end;
  1028.                 if (s <> '') & (s[1] = '.') then begin
  1029.                     s := TPcopy(s, 2, 255);
  1030.                 end;
  1031.                 if b > 255 then begin
  1032.                     good := false;
  1033.                     b := 0; { avoid overflow error? }
  1034.                 end;
  1035.                 addr := BOR(BSL(addr, 8), b);
  1036.             end;
  1037.         end;
  1038.     begin
  1039.         good := true;
  1040.         addr := 0;
  1041.         Get1;
  1042.         Get1;
  1043.         Get1;
  1044.         Get1;
  1045.         good := good & (s = '');
  1046.         if not good then begin
  1047.             addr := 0;
  1048.         end;
  1049.         ConnectionsStrToAddr := good;
  1050.     end;
  1051.  
  1052.     procedure ConnectionsAddrToString (ip: longint; var addrStr: Str255);
  1053.     begin
  1054.         AddrToStr(ip, addrStr);
  1055.     end;
  1056.  
  1057.     function ConnectionsAddrToStr (ip: longint): Str255;
  1058.         var
  1059.             s: Str255;
  1060.     begin
  1061.         AddrToStr(ip, s);
  1062.         ConnectionsAddrToStr := s;
  1063.     end;
  1064.  
  1065. end.