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

  1. {$I-}
  2. program Talkd;
  3.  
  4. { This program was written by Peter N Lewis, Mar 1992 in THINK Pascal 4.0.1 }
  5. { To compile it you will need the System 7 versions of the Interface files }
  6. { You may use this source in your own free/shareware projects as long as you acknowledge me }
  7. { in your About box and documentation files.  You may include it in commercial products }
  8. { only if I explicitly allow it. }
  9.  
  10.     uses
  11.         AppleTalk, PPCToolbox, Processes, EPPC, Notification, AppleEvents, MyTrackIdle, MyUtils, {}
  12.         TCPTypes, TCPStuff, UDPStuff, TalkdTypes, Tables, MyLists, MyNotifier, MyPreferences, BaseGlobals;
  13.  
  14.     const
  15.         min_idle_time = longInt(1) * 60 * 60;  { 1 minute }
  16.         max_notify_display_time = longInt(1) * 60 * 60; { 1 minutes }
  17.  
  18.     var
  19.         dnrptr: ptr;
  20.         has_AppleEvents: boolean;
  21.  
  22.     function GotRequiredParams (theAppleEvent: AppleEvent): OSErr;        { <aevt> }
  23.         var
  24.             typeCode: DescType;
  25.             actualSize: Size;
  26.             err: OSErr;
  27.     begin
  28.         err := AEGetAttributePtr(theAppleEvent, keyMissedKeywordAttr, typeWildCard, typeCode, nil, 0, actualSize);    { nil ok: need only function result }
  29.         if err = errAEDescNotFound then        { we got all the required params: all is ok }
  30.             GotRequiredParams := noErr
  31.         else if err = noErr then
  32.             GotRequiredParams := errAEEventNotHandled
  33.         else
  34.             GotRequiredParams := err;
  35.     end; { GotRequiredParams }
  36.  
  37.     function HandleQUIT (theAppleEvent, reply: AppleEvent; quitp: ptr): OSErr;        { <aevt> }
  38.         var
  39.             oe: OSErr;
  40.             errStr: Str255;
  41.             willQuit: Boolean;                { did the user allow the quit or cancel }
  42.     begin
  43.     { We don't expect any params at all, but check in case the client requires any }
  44.         oe := GotRequiredParams(theAppleEvent);
  45.         quitNow := true;
  46.         if reply.dataHandle <> nil then            { a reply is sought }
  47.             begin
  48.             if oe = noErr then
  49.                 errStr := 'OK'
  50.             else
  51.                 errStr := 'user cancelled quit';
  52.             oe := AEPutParamPtr(reply, 'errs', 'TEXT', Ptr(@errStr[1]), length(errStr));
  53.         end;
  54.         HandleQUIT := oe;
  55.     end;
  56.  
  57.     procedure WNE;
  58.         var
  59.             dummy: boolean;
  60.             er: eventRecord;
  61.             oe: OSErr;
  62.     begin
  63.         dummy := WaitNextEvent(everyEvent, er, 15, nil);
  64.         if er.what = kHighLevelEvent then
  65.             if has_AppleEvents then
  66.                 oe := AEProcessAppleEvent(er);
  67.         if er.what = keydown then
  68.             quitnow := true;
  69.         TrackIdle;
  70.         NotifyIdle(true);
  71.     end;
  72.  
  73.     function IsRunning (signature: OSType): boolean;
  74.         var
  75.             process: ProcessSerialNumber;
  76.             info: ProcessInfoRec;
  77.             s: str63;
  78.             fs: FSSpec;
  79.             oe: OSErr;
  80.             gv: longInt;
  81.     begin
  82.         IsRunning := false;
  83.         if (Gestalt(gestaltOSAttr, gv) = noErr) & (BTST(gv, gestaltLaunchControl)) then begin
  84.             process.highLongOfPSN := 0;
  85.             process.lowLongOfPSN := kNoProcess;
  86.             info.processInfoLength := sizeof(ProcessInfoRec);
  87.             info.processName := @s;
  88.             info.processAppSpec := @fs;
  89.             while GetNextProcess(process) = noErr do begin
  90.                 if GetProcessInformation(process, info) = noErr then
  91.                     if (info.processType = longInt('APPL')) and (info.processSignature = signature) then begin
  92.                         IsRunning := true;
  93.                         leave;
  94.                     end;
  95.             end;
  96.         end;
  97.     end;
  98.  
  99.     function AnnounceRequest (var request: ctlMsg; var remote_machine: str255): answers;
  100. { See if the user is accepting messages. If so, announce that  a talk is requested. }
  101.         var
  102.             sp: stringPtr;
  103.             s: str255;
  104.             oe: OSErr;
  105.             gv: longInt;
  106.             talk_running: boolean;
  107.             secs: longInt;
  108.     begin
  109.         JustGetPrefs(prefs);
  110.         talk_running := IsRunning('tALK');
  111.         if (prefs.allowconnect = AC_Never) | ((prefs.allowconnect = AC_Talk) and not talk_running) then
  112.             AnnounceRequest := A_permission_denied
  113.         else begin
  114.             if not talk_running then begin
  115. { if Talk is running it will notice by itself in a few secs and notify the user much more sedately }
  116.                 GetDateTime(secs);
  117.                 IUTimeString(secs, false, s);
  118.                 SPrintS5(s, GetGlobalString(alert_pattern), UserToPStr(request.l_name), remote_machine, UserToPStr(request.r_name), s, '');
  119.                 sp := stringPtr(NewPtr(Length(s) + 1));
  120.                 sp^ := s;
  121.                 NotifyH(mark_none, nil, nil, sp, max_notify_display_time);
  122.             end;
  123.             AnnounceRequest := A_success;
  124.         end;
  125.     end;
  126.  
  127.     procedure AddrToName (ip: longInt; var name: str255);
  128.         var
  129.             hi: hostInfo;
  130.             done: signedByte;
  131.             oe: OSErr;
  132.     begin
  133.         done := 0;
  134.         oe := TCPAddrToName(dnrptr, ip, hi, done);
  135.         if (oe = cacheFault) or (oe = noErr) then begin
  136.             while done = 0 do
  137.                 WNE;
  138.             oe := hi.rtnCode;
  139.             if oe = noErr then begin
  140.                 SanitizeHostName(hi.rtnHostName);
  141.                 name := hi.rtnHostName;
  142.             end;
  143.         end;
  144.         if oe <> noErr then
  145.             TCPAddrToStr(dnrptr, ip, name);
  146.     end;
  147.  
  148. {    ANNOUNCE - announce to a user that a talk is wanted }
  149. {    LEAVE_INVITE - insert the request into the table }
  150. {    LOOK_UP - look up to see if a request is waiting in }
  151. {          in the table for the local user }
  152. {    DELETE - delete invitation }
  153. {   SCAN - return the id_num'th entry in the table - Added specifically to allow Talk }
  154. {         to scan for waiting connections. WARNING: Returns an extended packet (ie, a }
  155. {         ctlMsg instead of a ctlResponse) }
  156. {  VERIFY_ANNOUNCE - check that the announce request is still there }
  157.  
  158.     procedure GetSendString (var name: str255);
  159.         var
  160.             f: longInt;
  161.     begin
  162.         f := (TickCount - IdleSince) div 60;
  163.         if f < 180 then
  164.             name := concat(NumToStr(f), GetGlobalString(s_seconds))
  165.         else if f < longInt(180) * 60 then
  166.             name := concat(NumToStr(f div 60), GetGlobalString(s_minutes))
  167.         else if f < 48 * longInt(3600) then
  168.             name := concat(NumToStr(f div 3600), GetGlobalString(s_hours))
  169.         else
  170.             name := concat(NumToStr(f div 86400), GetGlobalString(s_days));
  171.         SPrintS5(name, GetGlobalString(auto_reply_pattern), name, '', '', '', '');
  172.         name := concat(chr($7F), chr($15), chr($13), name);
  173.     end;
  174.  
  175.     procedure DoAnnounce (var m: ctlMsg; var r: ctlMsg);
  176.         var
  177.             oe: OSErr;
  178.             name: str255;
  179.             ptr: ctlMsgPtr;
  180.             tcpc: TCPConnectionPtr;
  181.             f: longInt;
  182.     begin
  183.         JustGetPrefs(prefs);
  184.         if not ValidUserName(UserToPStr(m.r_name), prefs) then begin
  185.             r.answer := A_not_here;
  186.         end
  187.         else begin
  188.             AddrToName(m.ctl_addr.ip, name);
  189.  
  190.             ptr := FindRequest(m);
  191.             if ptr = nil then begin
  192.                 r.answer := InsertTable(m);
  193.                 r.id_num := m.id_num;
  194.                 if r.answer = A_success then
  195.                     r.answer := AnnounceRequest(m, name);
  196.                 exit(DoAnnounce);
  197.             end
  198.             else if m.id_num > ptr^.id_num then begin
  199. { This is an explicit re-announce, so update the id_num  }
  200. { field to avoid duplicates and re-announce the talk. }
  201. { But we don't handle re-announcing (it screws up Talk anyway) }
  202. {    ptr^.id_num := NewID;}
  203.                 r.id_num := ptr^.id_num;
  204. {    r.answer := AnnounceRequest(m, name);}
  205.                 r.answer := a_success;
  206.                 if prefs.reply_if_idle and (TickCount - IdleSince > min_idle_time) then begin
  207.                     oe := TCPActiveOpen(tcpc, Minimum_TCPBUFFERSIZE, 0, m.addr.ip, m.addr.port, nil);
  208.                     if oe = noErr then begin
  209.                         f := TickCount + 10 * 60; { 10 seconds to connect }
  210.                         while (TickCount < f) and (TCPState(tcpc) <> T_established) do
  211.                             WNE;
  212.                         if TCPState(tcpc) = T_Established then begin
  213.                             GetSendString(name);
  214.                             oe := TCPSend(tcpc, @name[1], length(name));
  215.                         end;
  216.                         oe := TCPClose(tcpc, nil);
  217.                     end;
  218.                 end;
  219.             end
  220.             else begin
  221.                 r.id_num := ptr^.id_num;
  222.                 r.answer := A_success;
  223.             end;
  224.         end;
  225.     end;
  226.  
  227.     procedure ProcessRequest (var m: ctlMsg; var r: ctlMsg; var extended: boolean);
  228.         var
  229.             ptr: ctlMsgPtr;
  230.     begin
  231.         extended := false;
  232.         r.vers := talk_version;
  233.         r.typ := m.typ;
  234.         r.addr.family := AF_INET;
  235.         r.id_num := 0;
  236.         if m.vers <> talk_version then begin
  237.             r.answer := A_badversion;
  238.             exit(ProcessRequest);
  239.         end;
  240.         m.id_num := m.id_num; { convert byte order }
  241.         m.addr.family := m.addr.family; { convert byte order }
  242.         if m.addr.family <> AF_INET then begin
  243.             r.answer := A_badaddr;
  244.             exit(ProcessRequest);
  245.         end;
  246.         m.ctl_addr.family := m.ctl_addr.family; { convert byte order }
  247.         if m.ctl_addr.family <> AF_INET then begin
  248.             r.answer := A_badctladdr;
  249.             exit(processRequest);
  250.         end;
  251.         m.pid := m.pid; { convert byte order }
  252. {    writeln(m.typ, ', l=', m.l_name, ', r=', m.r_name);}
  253.         case m.typ of
  254.             CT_announce: 
  255.                 DoAnnounce(m, r);
  256.             CT_leave_invite:  begin
  257.                 ptr := FindRequest(m);
  258.                 if ptr <> nil then begin
  259.                     r.id_num := ptr^.id_num;
  260.                     r.answer := A_success;
  261.                 end
  262.                 else begin
  263.                     r.answer := InsertTable(m);
  264.                     r.id_num := m.id_num;
  265.                 end;
  266.             end;
  267.             CT_lookup:  begin
  268.                 ptr := FindMatch(m);
  269.                 if ptr <> nil then begin
  270.                     r.id_num := ptr^.id_num;
  271.                     r.addr := ptr^.addr;
  272.                     r.addr.family := r.addr.family;{ convert to net byte order }
  273.                     r.answer := A_success;
  274.                 end
  275.                 else
  276.                     r.answer := A_not_here;
  277.             end;
  278.             CT_scan:  begin
  279.                 ptr := FindIndexedAnnounce(m.id_num);
  280.                 extended := true;
  281.                 if ptr <> nil then begin
  282.                     r := ptr^;
  283.                     r.typ := CT_scan;
  284.                     r.answer := A_success;
  285.                 end
  286.                 else
  287.                     r.answer := A_not_here;
  288.             end;
  289.             CT_VerifyAnnounce:  begin
  290.                 m.typ := CT_announce;
  291.                 ptr := FindRequest(m);
  292.                 if ptr <> nil then begin
  293.                     r.id_num := ptr^.id_num;
  294.                     r.answer := A_success;
  295.                 end
  296.                 else
  297.                     r.answer := A_not_here;
  298.             end;
  299.             CT_quit:  begin
  300.                 quitNow := true;
  301.                 r.answer := A_success;
  302.             end;
  303.             CT_delete:  begin
  304.                 r.answer := DeleteInvite(m.id_num);
  305.             end;
  306.             otherwise
  307.                 r.answer := A_unknown_request;
  308.         end;
  309.     end;
  310.  
  311.     procedure OToNRequest (datap: ptr; datalen: integer; recport: integer; var request: ctlMsg; var cvt: longInt);
  312.         var
  313.             r: ctlMsg;
  314.             ocr: octlMsg;
  315.             i: integer;
  316.     begin
  317.         if datalen = SizeOf(ctlMsg) then begin
  318.             BlockMove(datap, @request, SizeOf(request));
  319.             cvt := 0  { ntalk }
  320.         end
  321.         else begin
  322.             if datalen = SizeOf(octlMsg) then begin
  323.                 BlockMove(datap, @ocr, SizeOf(ocr));
  324.                 cvt := 1;
  325.                 request.typ := ctlTypes(ord(ocr.data[1]));
  326.                 BlockMove(@ocr.data[2], @request.l_name, oname_size);
  327.                 for i := oname_size + 1 to name_size do
  328.                     request.l_name[i] := chr(0);
  329.                 BlockMove(@ocr.data[11], @request.r_name, oname_size);
  330.                 for i := oname_size + 1 to name_size do
  331.                     request.r_name[i] := chr(0);
  332.                 request.pid := ocr.pid;
  333.                 request.id_num := ocr.id_num;
  334.                 request.r_tty := ocr.r_tty;
  335.                 request.addr := ocr.addr;
  336.                 request.ctl_addr := ocr.ctl_addr;
  337.                 if request.addr.family = 0 then
  338.                     request.addr.family := AF_INET;
  339.                 if request.ctl_addr.family = 0 then
  340.                     request.ctl_addr.family := AF_INET;
  341.                 request.vers := talk_version;
  342.             end
  343.             else begin
  344.                 request.vers := -1;
  345.                 cvt := -1;
  346.             end;
  347.         end;
  348.     end;
  349.  
  350.     procedure NToOResponse (cvt: longInt; var response: ctlMsg; extend: boolean; var datalen: integer);
  351.         var
  352.             ocr: octlResponse;
  353.     begin
  354.         if extend then
  355.             datalen := SizeOf(ctlMsg)  { must be ntalk, must be local, must be us! }
  356.         else begin
  357.             case cvt of
  358.                 -1:  begin
  359.                     datalen := 0;
  360.                 end;
  361.                 0: 
  362.                     datalen := SizeOf(ctlResponse);
  363.                 1:  begin
  364.                     ocr.typ := response.typ;
  365.                     ocr.answer := response.answer;
  366.                     ocr.id_num := response.id_num;
  367.                     ocr.addr := response.addr;
  368.                     BlockMove(@ocr, @response, SizeOf(ocr));
  369.                     datalen := SizeOf(octlResponse);
  370.                 end;
  371.             end;
  372.         end;
  373.     end;
  374.  
  375.     function StackPtr: longInt;
  376.     inline
  377.         $2E8F;
  378.  
  379.     var
  380.         request: ctlMsg;
  381.         response: ctlMsg;
  382.         extended: boolean;
  383.         udpcn, udpco: UDPConnectionPtr;
  384.         oe: OSErr;
  385.         remoteIP: longInt;
  386.         remoteport: integer;
  387.         datap: ptr;
  388.         datalen: integer;
  389.         s: str255;
  390.         r: rect;
  391.         gv: longInt;
  392.         applLimitP: ^longInt;
  393.         cvt: longInt;
  394. begin
  395.     applLimitP := POINTER($130);
  396.     applLimitP^ := StackPtr - 8000;
  397. {    SetApplLimit(ptr(StackPtr - 8000));}
  398.     MaxApplZone;
  399.     MoreMasters;
  400.     oe := Gestalt(gestaltAppleEventsAttr, gv);
  401.     has_AppleEvents := (oe = noErr) and (gv = 1);
  402.     if has_AppleEvents then
  403.         oe := AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, @HandleQUIT, 0, false);
  404.     InitNotify;
  405.     InitTrackIdle;
  406.     if UDPInit = noErr then begin
  407.         if TCPInit = noErr then begin
  408.             s := '';
  409.             if TCPOpenResolver(s, dnrptr) = noErr then begin
  410.                 InitTables;
  411.                 if UDPCreate(udpcn, 0, talkd_port) = noErr then begin
  412.                     if UDPCreate(udpco, 0, otalkd_port) = noErr then begin
  413.                         quitNow := false;
  414.                         while not quitNow do begin
  415.                             WNE;
  416.                             while (UDPDatagramsAvailable(udpcn) = 0) and (UDPDatagramsAvailable(udpco) = 0) and not quitNow do
  417.                                 WNE;
  418.                             if not quitNow then begin
  419.                                 if UDPDatagramsAvailable(udpcn) > 0 then begin { new talk port }
  420.                                     oe := UDPRead(udpcn, 5, remoteIP, remoteport, datap, datalen);
  421.                                     if datalen = SizeOf(ctlMsg) then begin
  422.                                         BlockMove(datap, @request, SizeOf(request));
  423.                                         oe := UDPReturnBuffer(udpcn, datap);
  424.                                         ProcessRequest(request, response, extended);
  425.                                         if extended then
  426.                                             oe := UDPWrite(udpcn, remoteIP, remoteport, @response, SizeOf(response), false)
  427.                                         else
  428.                                             oe := UDPWrite(udpcn, remoteIP, remoteport, @response, SizeOf(ctlResponse), false);
  429.                                     end
  430.                                     else if datalen > 0 then
  431.                                         oe := UDPReturnBuffer(udpco, datap);
  432.                                 end
  433.                                 else begin { old talk port }
  434.                                     oe := UDPRead(udpco, 5, remoteIP, remoteport, datap, datalen);
  435.                                     if datalen > 0 then begin
  436.                                         OToNRequest(datap, datalen, otalkd_port, request, cvt);
  437.                                         oe := UDPReturnBuffer(udpco, datap);
  438.                                         ProcessRequest(request, response, extended);
  439.                                         NToOResponse(cvt, response, extended, datalen);
  440.                                         oe := UDPWrite(udpco, remoteIP, remoteport, @response, datalen, false)
  441.                                     end;
  442.                                 end;
  443.                             end;
  444.                         end;
  445.                         oe := UDPRelease(udpco);
  446.                     end;
  447.                     oe := UDPRelease(udpcn);
  448.                 end;
  449.                 TCPCloseResolver(dnrptr);
  450.             end;
  451.             TCPFinish;
  452.         end;
  453.         UDPFinish;
  454.     end;
  455.     FinishTrackIdle;
  456.     FinishNotify;
  457. end.