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