home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1999 January: Mac OS SDK / Dev.CD Jan 99 SDK2.toast / What's New / Sample Code / Networking / MoreNetworkSetup / NetworkSetup / NetworkSetupHelpers.c < prev    next >
Encoding:
Text File  |  1998-11-10  |  33.0 KB  |  1,231 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        NetworkSetupHelpers.c
  3.  
  4.     Contains:    High-level network preference routines.
  5.  
  6.     Written by:    Quinn
  7.  
  8.     Copyright:    Copyright © 1998 by Apple Computer, Inc., all rights reserved.
  9.  
  10.                 You may incorporate this Apple sample source code into your program(s) without
  11.                 restriction. This Apple sample source code has been provided "AS IS" and the
  12.                 responsibility for its operation is yours. You are not permitted to redistribute
  13.                 this Apple sample source code as "Apple sample source code" after having made
  14.                 changes. If you're going to re-distribute the source, we require that you make
  15.                 it clear in the source that the code was descended from Apple sample source
  16.                 code, but that you've made changes.
  17.  
  18.     Change History (most recent first):
  19.  
  20.          <7>    10/11/98    Quinn   Fix = vs == in an assert.
  21.          <6>    10/11/98    Quinn   Convert "MorePrefix.h" to "MoreSetup.h".
  22.          <5>     9/11/98    Quinn   AppleTalk on/off support.
  23.          <4>     9/11/98    Quinn   Add "TCP will dial" code.
  24.          <3>     5/11/98    Quinn   Use MoreAssertQ instead of MoreAssert.
  25.          <2>     5/11/98    Quinn   Fix header.
  26.          <1>     5/11/98    Quinn   First checked in.
  27. */
  28.  
  29. /////////////////////////////////////////////////////////////////
  30. // MoreIsBetter Setup
  31.  
  32. #include "MoreSetup.h"
  33.  
  34. /////////////////////////////////////////////////////////////////
  35. // Mac OS Interfaces
  36.  
  37. #include <Types.h>
  38. #include <Files.h>
  39. #include <Errors.h>
  40. #include <Folders.h>
  41. #include <Resources.h>
  42. #include <Gestalt.h>
  43. #include <CodeFragments.h>
  44. #include <NetworkSetup.h>
  45. #include <OpenTptLinks.h>
  46.  
  47. /////////////////////////////////////////////////////////////////
  48. // MIB Prototypes
  49.  
  50. #include "MoreNetworkSetup.h"
  51. #include "OldOTConfigLib.h"
  52.  
  53. /////////////////////////////////////////////////////////////////
  54. // Our Prototypes
  55.  
  56. #include "NetworkSetupHelpers.h"
  57.  
  58. /////////////////////////////////////////////////////////////////
  59. // Testing Parameters
  60.  
  61. enum {
  62.  
  63.     // Throw this switch if you want to debug the direct preference
  64.     // file access code on a machine with Network Setup installed.
  65.  
  66.     kUseNetworkSetup = true,
  67.     
  68.     // If you set kUseInetInterfaceInfo to false, NSHTCPWillDial will not
  69.     // use the heuristic of "if the TCP/IP stack is loaded, it's safe
  70.     // to open an endpoint".  This is especially useful when debugging.
  71.  
  72.     kUseInetInterfaceInfo = true
  73.     
  74. };
  75.  
  76. /////////////////////////////////////////////////////////////////
  77.  
  78. extern pascal ItemCount NSHCountConfigurationList(NSHConfigurationListHandle configList)
  79.     // See comment in header file.
  80. {
  81.     return GetHandleSize( (Handle) configList ) / sizeof(NSHConfigurationEntry);
  82. }
  83.  
  84. /////////////////////////////////////////////////////////////////
  85. #pragma mark ----- Database Configuration List -----
  86.  
  87. // Parameter structure for SetterIterator.
  88.  
  89. struct TypeAndClassParam {
  90.     OSType  fType;
  91.     OSType  fClass;
  92.     Boolean found;
  93.     CfgEntityRef *currentEntity;
  94. };
  95. typedef struct TypeAndClassParam TypeAndClassParam;
  96.  
  97. static pascal void TypeAndClassIterator(const MNSDatabadeRef *ref, CfgSetsElement *thisElement, void *p)
  98.     // This routine is used as a callback for MNSIterateSet.
  99.     // It looks for the entity specified by the fType and fClass
  100.     // fields of the param, and puts its entityRef into the
  101.     // variable pointed to by the currentEntity field of param.
  102. {
  103.     TypeAndClassParam *param;
  104.     
  105.     param = (TypeAndClassParam *) p;
  106.  
  107.     MoreAssertQ(MNSValidDatabase(ref));
  108.     MoreAssertQ(thisElement != nil);
  109.     MoreAssertQ(param != nil);
  110.     MoreAssertQ(param->currentEntity != nil);
  111.     
  112.     if (thisElement->fEntityInfo.fClass == param->fClass && thisElement->fEntityInfo.fType == param->fType) {
  113.         MoreAssertQ( ! param->found );
  114.         param->found = true;
  115.         *(param->currentEntity) = thisElement->fEntityRef;
  116.     }
  117. }
  118.  
  119. static OSStatus FindCurrentConnection(const MNSDatabadeRef *ref, OSType protocol, CfgEntityRef *currentEntity)
  120.     // This routine finds the current connection entity for the specified
  121.     // protocol in the active set, returning it in currentEntity.
  122. {
  123.     OSStatus err;
  124.     CfgEntityRef activeSet;
  125.     TypeAndClassParam param;
  126.     
  127.     MoreAssertQ(MNSValidDatabase(ref));
  128.     MoreAssertQ(currentEntity != nil);
  129.     
  130.     param.fClass = kOTNetworkConnectionClass;
  131.     param.fType = protocol;
  132.     param.found = false;
  133.     param.currentEntity = currentEntity;
  134.     
  135.     err = MNSFindActiveSet(ref, &activeSet);
  136.     if (err == noErr) {
  137.         err = MNSIterateSet(ref, &activeSet, TypeAndClassIterator, ¶m, false);
  138.     }
  139.     if (err == noErr) {
  140.         if (param.found) {
  141.             // Set preferences contain entities from weird areas because
  142.             // of the way the database is committed.  We can safely fix
  143.             // that up here.  I discussed with the Network Setup engineer
  144.             // and he reassured me that this was cool -- Quinn, 9 Nov 1998
  145.             
  146.             currentEntity->fLoc = ref->area;
  147.         } else {
  148.             err = -6;
  149.         }
  150.     }
  151.     
  152.     return err;
  153. }
  154.  
  155. static OSStatus AddEntityToConfigurationList(const MNSDatabadeRef *ref,
  156.                                             const CfgEntityRef *entity,
  157.                                             const CfgEntityInfo *entityInfo,
  158.                                             NSHConfigurationListHandle configList)
  159.     // This routines adds the entity specified by entity and entityInfo
  160.     // in the database specified by ref to the configList.
  161. {
  162.     OSStatus err;
  163.     NSHConfigurationEntry thisEntry;
  164.     StringPtr entityName;
  165.     ByteCount junkSize;
  166.  
  167.     MoreAssertQ(configList != nil);
  168.     MoreAssertQ(MNSValidDatabase(ref));
  169.     MoreAssertQ(entity != nil);
  170.     MoreAssertQ(entityInfo != nil);
  171.     
  172.     // Get the user-visible name from the configuration, which is
  173.     // stored in the 'pnam' preferences.  [This should have a proper
  174.     // identifier in some future "NetworkSetup.h".]
  175.     
  176.     entityName = nil;
  177.     err = MNSGetPref(ref, entity, 'pnam', &entityName, &junkSize);
  178.     if (err == noErr) {
  179.         MoreAssertQ(junkSize == (entityName[0] + 1));
  180.         BlockMoveData(entityName, &thisEntry.name, entityName[0] + 1);
  181.         thisEntry.selected = false;
  182.         thisEntry.cookie = 0;
  183.         thisEntry.cookie2 = *entity;
  184.         thisEntry.cookie3 = *entityInfo;
  185.     }
  186.     
  187.     if (err == noErr) {
  188.         err = PtrAndHand(&thisEntry, (Handle) configList, sizeof(thisEntry));
  189.     }
  190.     
  191.     // Clean up.
  192.     
  193.     if (entityName != nil) {
  194.         DisposePtr( (Ptr) entityName);
  195.         MoreAssertQ(MemError() == noErr);
  196.     }
  197.     
  198.     return err;
  199. }
  200.  
  201. // Parameter structure for SetterIterator.
  202.  
  203. struct SetterParam {
  204.     OSType fType;
  205.     OSType fClass;
  206.     const CfgEntityRef *chosenConfig;
  207.     const CfgEntityInfo *chosenConfigInfo;
  208. };
  209. typedef struct SetterParam SetterParam;
  210.  
  211. static pascal void SetterIterator(const MNSDatabadeRef *ref, CfgSetsElement *thisElement, void *p)
  212.     // This routine is used as a callback for MNSIterateSet.
  213.     // It looks for the entity specified by the fType and fClass
  214.     // fields of the param, and replaces it with the chosen
  215.     // entity and info from the param.  It expects that the caller
  216.     // of MNSIterateSet has specified writeAfterIterate so that
  217.     // the changes get written back to the set.
  218. {
  219.     SetterParam *param;
  220.     
  221.     param = (SetterParam *) p;
  222.  
  223.     MoreAssertQ(MNSValidDatabase(ref));
  224.     MoreAssertQ(MNSDatabaseWritable(ref));
  225.     MoreAssertQ(thisElement != nil);
  226.     MoreAssertQ(param != nil);
  227.     MoreAssertQ(param->chosenConfig != nil);
  228.     MoreAssertQ(param->chosenConfigInfo != nil);
  229.     
  230.     if (thisElement->fEntityInfo.fClass == param->fClass && thisElement->fEntityInfo.fType == param->fType) {
  231.         thisElement->fEntityRef = *param->chosenConfig;
  232.         thisElement->fEntityInfo = *param->chosenConfigInfo;
  233.     }
  234. }
  235.  
  236. static OSStatus GetConfigurationListFromDatabase(OSType protocol, NSHConfigurationListHandle configList)
  237.     // Implementation of NSHGetConfigurationList which uses the Network Setup
  238.     // database.  See NSHGetConfigurationList's comment in header
  239.     // file for interface specification.
  240. {
  241.     OSStatus err;
  242.     OSStatus err2;
  243.     MNSDatabadeRef ref;
  244.     ItemCount entityCount;
  245.     CfgEntityRef *entityRefs;
  246.     CfgEntityInfo *entityInfos;
  247.     CfgEntityRef activeConn;
  248.     ItemCount i;
  249.  
  250.     entityRefs = nil;
  251.     entityInfos = nil;
  252.  
  253.     err = MNSOpenDatabase(&ref, false);
  254.     if (err == noErr) {
  255.     
  256.         // Find all the network connection entities for this protocol.
  257.     
  258.         err = MNSGetEntitiesList(&ref,
  259.                                 kOTNetworkConnectionClass, protocol,
  260.                                 &entityCount,
  261.                                 &entityRefs,
  262.                                 &entityInfos);
  263.  
  264.         // Add each to the list of possible connections.
  265.         
  266.         if (err == noErr) {
  267.             for (i = 0; i < entityCount; i++) {
  268.                 err = AddEntityToConfigurationList(&ref, &entityRefs[i], &entityInfos[i], configList);
  269.             }
  270.         }
  271.         
  272.         // Find the current configuration and mark it as selected
  273.         // in the list.
  274.         
  275.         if (err == noErr) {
  276.             err = FindCurrentConnection(&ref, protocol, &activeConn);
  277.         }
  278.         if (err == noErr) {
  279.             for (i = 0; i < entityCount; i++) {
  280.                 if ( OTCfgIsSameEntityRef(&activeConn, &(*configList)[i].cookie2, kOTCfgIgnoreArea) ) {
  281.                     (*configList)[i].selected = true;
  282.                 }
  283.             }
  284.         }
  285.  
  286.         err2 = MNSCloseDatabase(&ref, false);
  287.         if (err == noErr) {
  288.             err = err2;
  289.         }
  290.     }
  291.     
  292.     // Clean up.
  293.     
  294.     if (entityInfos != nil) {
  295.         DisposePtr( (Ptr) entityInfos);
  296.         MoreAssertQ(MemError() == noErr);
  297.     }
  298.     if (entityRefs != nil) {
  299.         DisposePtr( (Ptr) entityRefs);
  300.         MoreAssertQ(MemError() == noErr);
  301.     }
  302.     return err;
  303. }
  304.  
  305. static OSStatus SelectConfigurationFromDatabase(OSType protocol, const NSHConfigurationEntry *chosenEntry)
  306.     // Implementation of NSHSelectConfiguration which uses the Network Setup
  307.     // database.  See NSHSelectConfiguration's comment in header
  308.     // file for interface specification.
  309. {
  310.     OSStatus err;
  311.     OSStatus err2;
  312.     MNSDatabadeRef ref;
  313.     CfgEntityRef activeSet;
  314.     SetterParam param;
  315.  
  316.     err = MNSOpenDatabase(&ref, true);
  317.     if (err == noErr) {
  318.         param.fClass = kOTNetworkConnectionClass;
  319.         param.fType = protocol;
  320.         param.chosenConfig = &chosenEntry->cookie2;
  321.         param.chosenConfigInfo = &chosenEntry->cookie3;
  322.         
  323.         err = MNSFindActiveSet(&ref, &activeSet);
  324.         if (err == noErr) {
  325.             err = MNSIterateSet(&ref, &activeSet, SetterIterator, ¶m, true);
  326.         }
  327.  
  328.         err2 = MNSCloseDatabase(&ref, err == noErr);
  329.         if (err == noErr) {
  330.             err = err2;
  331.         }
  332.     }
  333.     return err;
  334. }
  335.  
  336. /////////////////////////////////////////////////////////////////
  337. #pragma mark ----- File Configuration List -----
  338.  
  339. enum {
  340.     kOTNetworkPrefFileType = 'pref',
  341.     kOTTCPPrefFileCreator = 'ztcp',
  342.     kOTAppleTalkPrefFileCreator = 'atdv'
  343. };
  344.  
  345. static OSStatus FindNetworkPrefFile(OSType protocol, FSSpec *fss)
  346.     // This routine scans the Preferences folder looking
  347.     // for the specified network preferences file by type and creator.
  348. {
  349.     OSStatus err;
  350.     Boolean found;
  351.     CInfoPBRec cpb;
  352.     SInt16 index;
  353.     OSType creatorToSearchFor;
  354.     
  355.     switch (protocol) {
  356.         case kOTTCPv4NetworkConnection:
  357.             creatorToSearchFor = kOTTCPPrefFileCreator;
  358.             break;
  359.         case kOTAppleTalkNetworkConnection:
  360.             creatorToSearchFor = kOTAppleTalkPrefFileCreator;
  361.             break;
  362.         default:
  363.             MoreAssertQ(false);
  364.             break;
  365.     }
  366.     
  367.     err = FindFolder(kOnSystemDisk, kPreferencesFolderType, kCreateFolder, &fss->vRefNum, &fss->parID);
  368.     if (err == noErr) {
  369.         found = false;
  370.         index = 1;
  371.         do {
  372.             cpb.hFileInfo.ioVRefNum = fss->vRefNum;
  373.             cpb.hFileInfo.ioDirID = fss->parID;
  374.             cpb.hFileInfo.ioNamePtr = fss->name;
  375.             cpb.hFileInfo.ioFDirIndex = index;
  376.             err = PBGetCatInfoSync(&cpb);
  377.             if (err == noErr) {
  378.                 found = (    cpb.hFileInfo.ioFlFndrInfo.fdType == kOTNetworkPrefFileType &&
  379.                             cpb.hFileInfo.ioFlFndrInfo.fdCreator == creatorToSearchFor );
  380.             }
  381.             index += 1;
  382.         } while (err == noErr & ! found);
  383.     }
  384.     return err;
  385. }
  386.  
  387. static OSStatus CheckResError(void *testH)
  388.     // A trivial wrapper routine for ResError,
  389.     // which is too lame to report an error code
  390.     // in all cases when GetResource fails.
  391. {
  392.     OSStatus err;
  393.  
  394.     err = ResError();
  395.     if (err == noErr && testH == nil) {
  396.         err = resNotFound;
  397.     }
  398.     return err;
  399. }
  400.  
  401. static OSStatus CommitChangesToPrefFile(OSType protocol, SInt16 refNum, SInt16 config)
  402. {
  403.     OSStatus err;
  404.     
  405.     err = noErr;
  406.     switch (protocol) {
  407.         case kOTTCPv4NetworkConnection:
  408.             if ( TCPCheckChangeConfigurationConsequences(refNum, config) == kMustReboot ) {
  409.                 err = -7;
  410.             }
  411.             if ( err == noErr ) {
  412.                 err = TCPChangeConfiguration(refNum, config);
  413.             }
  414.             break;
  415.         case kOTAppleTalkNetworkConnection:
  416.             if ( ATCheckChangeConfigurationConsequences(refNum, config) == kMustReboot ) {
  417.                 err = -7;
  418.             }
  419.             if ( err == noErr ) {
  420.                 err = ATChangeConfiguration(refNum, config);
  421.             }
  422.             break;
  423.         default:
  424.             MoreAssertQ(false);
  425.             err = -7;
  426.             break;
  427.     }
  428.     return err;
  429. }
  430.  
  431. static OSStatus GetCurrentConfig(SInt16 *config)
  432. {
  433.     OSStatus err;
  434.     Handle currentConfigResourceH;
  435.     
  436.     MoreAssertQ(config != nil);
  437.     
  438.     currentConfigResourceH = Get1Resource('ccfg', 1);
  439.     err = CheckResError(currentConfigResourceH);
  440.  
  441.     if (err == noErr && GetHandleSize(currentConfigResourceH) != sizeof(SInt16) ) {
  442.         // Assert: 'ccfg' is of the wrong size
  443.         MoreAssertQ(false);
  444.         err = -1;
  445.     } else {
  446.         *config = **(SInt16 **)currentConfigResourceH;
  447.     }
  448.     return err;
  449. }
  450.  
  451. static OSStatus AddResourceToConfigurationList(Handle cnamHandle, NSHConfigurationListHandle configList)
  452.     // Given a handle to a 'cnam' resource, generate a
  453.     // NSHConfigurationEntry and append it to the list
  454.     // of configurations.
  455. {
  456.     OSStatus err;
  457.     NSHConfigurationEntry thisEntry;
  458.     SInt16 cnamID;
  459.     ResType junkType;
  460.     
  461.     GetResInfo(cnamHandle, &cnamID, &junkType, thisEntry.name);
  462.     MoreAssertQ(ResError() == noErr);
  463.     MoreAssertQ(junkType == 'cnam');
  464.     thisEntry.selected = false;
  465.     thisEntry.cookie = cnamID;
  466.     OTMemzero(&thisEntry.cookie2, sizeof(thisEntry.cookie2));
  467.     OTMemzero(&thisEntry.cookie3, sizeof(thisEntry.cookie3));
  468.     
  469.     err = PtrAndHand(&thisEntry, (Handle) configList, sizeof(thisEntry));
  470.     return err;
  471. }
  472.  
  473. static OSStatus GetConfigurationListFromFile(OSType protocol, NSHConfigurationListHandle configList)
  474.     // Implementation of NSHGetConfigurationList which uses the old-style
  475.     // preference files.  See NSHGetConfigurationList's comment in header
  476.     // file for interface specification.
  477. {
  478.     OSStatus err;
  479.     FSSpec fss;
  480.     Handle cnamHandle;
  481.     SInt16 refNum;
  482.     SInt16 resCount;
  483.     SInt16 i;
  484.     SInt16 **currentConfigH;
  485.     SInt16 oldResFile;
  486.     
  487.     oldResFile = CurResFile();
  488.     err = FindNetworkPrefFile(protocol, &fss);
  489.     if (err == noErr) {
  490.         SetResLoad(false);
  491.         refNum = FSpOpenResFile(&fss, fsRdPerm);
  492.         err = ResError();
  493.         SetResLoad(true);
  494.         if (err == noErr) {
  495.             
  496.             resCount = Count1Resources('cnam');
  497.  
  498.             for (i = 1; i <= resCount; i++) {
  499.                 SetResLoad(false);
  500.                 cnamHandle = Get1IndResource('cnam', i);
  501.                 err = CheckResError(cnamHandle);
  502.                 SetResLoad(true);
  503.                 
  504.                 if (err == noErr) {
  505.                     err = AddResourceToConfigurationList(cnamHandle, configList);
  506.                 }
  507.                 
  508.                 // Don't need to release the resource because CloseResFile will
  509.                 // clean it up.
  510.                 
  511.                 if (err != noErr) {
  512.                     break;
  513.                 }
  514.             }
  515.             
  516.             if (err == noErr) {
  517.                 currentConfigH = (SInt16 **) Get1Resource('ccfg', 1);
  518.                 err = CheckResError(currentConfigH);
  519.             }
  520.             if (err == noErr) {
  521.                 for (i = 1; i <= resCount; i++) {
  522.                     if ( (*configList)[i].cookie == **currentConfigH ) {
  523.                         (*configList)[i].selected = true;
  524.                     }
  525.                 }
  526.             }
  527.             
  528.             CloseResFile(refNum);
  529.             MoreAssertQ(ResError() == noErr);
  530.         }
  531.     }
  532.     UseResFile(oldResFile);
  533.     MoreAssertQ(ResError() == noErr);
  534.  
  535.     return err;
  536. }
  537.  
  538. static OSStatus SelectConfigurationFromFile(OSType protocol, const NSHConfigurationEntry *chosenEntry)
  539.     // Implementation of NSHGetConfigurationList which uses the old-style
  540.     // preference files.  See NSHGetConfigurationList's comment in header
  541.     // file for interface specification.
  542. {
  543.     OSStatus err;
  544.     FSSpec fss;
  545.     SInt16 refNum;
  546.     SInt16 oldResFile;
  547.     
  548.     oldResFile = CurResFile();
  549.     err = FindNetworkPrefFile(protocol, &fss);
  550.     if (err == noErr) {
  551.     
  552.         // ••• Gotcha •••
  553.         // Really need to be careful here because it's possible
  554.         // that fss is open in our current resource chain.
  555.         // See DTS Technote 1120 "Opening Resource Files Twice Considered
  556.         // Hard?" for details.
  557.         //
  558.         //   <http://developer.apple.com/technotes/tn/tn1120.html>
  559.         //
  560.         // I'll probably put real code for this into MoreResources soon.
  561.         // Meanwhile, you have to live with this limitation.
  562.         // -- Quinn, 9 Nov 1998
  563.         
  564.         SetResLoad(false);
  565.         refNum = FSpOpenResFile(&fss, fsRdWrPerm);
  566.         err = ResError();
  567.         SetResLoad(true);
  568.         if (err == noErr) {
  569.             err = CommitChangesToPrefFile(protocol, refNum, chosenEntry->cookie);
  570.             
  571.             CloseResFile(refNum);
  572.             MoreAssertQ(ResError() == noErr);
  573.         }
  574.     }
  575.     UseResFile(oldResFile);
  576.     MoreAssertQ(ResError() == noErr);
  577.  
  578.     return err;
  579. }
  580.  
  581. /////////////////////////////////////////////////////////////////
  582. #pragma mark ----- Configuration List Abstraction ------
  583.  
  584. extern pascal OSStatus  NSHGetConfigurationList(OSType protocol, NSHConfigurationListHandle configList)
  585.     // See comment in header file.
  586. {
  587.     OSStatus err;
  588.     
  589.     SetHandleSize( (Handle) configList, 0);
  590.     MoreAssertQ(MemError() == noErr);
  591.     
  592.     if ( kUseNetworkSetup && IsNetworkSetupAvailable() ) {
  593.         #if TARGET_RT_MAC_CFM
  594.             err = GetConfigurationListFromDatabase(protocol, configList);
  595.         #else
  596.             // Network Setup has no Mixed Mode glue.  When running
  597.             // code on a PowerPC with Network Setup available, you
  598.             // should either compile your code as Fat or, if that's
  599.             // infeasible, write your own Mixed Mode glue.
  600.             return -5;
  601.         #endif
  602.     } else {
  603.         err = GetConfigurationListFromFile(protocol, configList);
  604.     }
  605.     return err;
  606. }
  607.  
  608. extern pascal OSStatus  NSHSelectConfiguration(OSType protocol, const NSHConfigurationEntry *chosenEntry)
  609.     // See comment in header file.
  610. {
  611.     OSStatus err;
  612.     
  613.     if ( kUseNetworkSetup && IsNetworkSetupAvailable() ) {
  614.         #if TARGET_RT_MAC_CFM
  615.             err = SelectConfigurationFromDatabase(protocol, chosenEntry);
  616.         #else
  617.             // Network Setup has no Mixed Mode glue.  When running
  618.             // code on a PowerPC with Network Setup available, you
  619.             // should either compile your code as Fat or, if that's
  620.             // infeasible, write your own Mixed Mode glue.
  621.             return -5;
  622.         #endif
  623.     } else {
  624.         err = SelectConfigurationFromFile(protocol, chosenEntry);
  625.     }
  626.     return err;
  627. }
  628.  
  629. /////////////////////////////////////////////////////////////////
  630. #pragma mark ----- 'iitf' Parsing Code ------
  631.  
  632. // The structure of an 'iitf' is the same regardless of whether it
  633. // comes from a resource or from the configuration database.
  634.  
  635. // An 'iitf' preference consists of a UInt16 count followed
  636. // by 0 or more interface specifications.  Each interface
  637. // specification is a variable length data structure, with
  638. // some fixed length and some variable length fields.
  639. // This structure is used to represent an interface as 
  640. // a fixed size data structure, much more suitable for
  641. // C programming.
  642.  
  643. // In current versions of OT, only one interface is allowed.
  644.  
  645. struct TCPiitfPref {
  646.     UInt8    fActive;
  647.     InetHost fIPAddress;
  648.     InetHost fSubnetMask;
  649.     Str255   fAppleTalkZone;
  650.     UInt8    fPath[36];            // Pascal string
  651.     UInt8    fModuleName[31];    // Pascal string
  652.     UInt32   fFramingFlags;
  653. };
  654. typedef struct TCPiitfPref TCPiitfPref;
  655.  
  656. static void UnpackIITF(Ptr *buffer, TCPiitfPref *unpackedIITF)
  657.     // This routine unpacks an interface from an 'iitf' preference
  658.     // into a TCPiitfPref.  *buffer must point to the beginning
  659.     // of the interface, ie two bytes into the pref data if
  660.     // if you're extracting the first interface.  *buffer
  661.     // is updated to point to the byte after the last byte
  662.     // parsed, so you can parse multiple interfaces by
  663.     // repeatedly calling this routine.
  664. {
  665.     UInt8 *cursor;
  666.     
  667.     cursor = (UInt8 *) *buffer;
  668.     
  669.     unpackedIITF->fActive = *cursor;
  670.     cursor += sizeof(UInt8);
  671.     unpackedIITF->fIPAddress = *((InetHost *) cursor);
  672.     cursor += sizeof(InetHost);
  673.     unpackedIITF->fSubnetMask = *((InetHost *) cursor);
  674.     cursor += sizeof(InetHost);
  675.     BlockMoveData(cursor, unpackedIITF->fAppleTalkZone, *cursor + 1);
  676.     cursor += (*cursor + 1);
  677.     BlockMoveData(cursor, unpackedIITF->fPath, 36);
  678.     cursor += 36;
  679.     BlockMoveData(cursor, unpackedIITF->fModuleName, 32);
  680.     cursor += 32;
  681.     unpackedIITF->fFramingFlags = *((UInt32 *) cursor);
  682.     cursor += sizeof(UInt32);
  683.  
  684.     *buffer = (Ptr) cursor;
  685. }
  686.  
  687. static OSStatus GetPortNameFromIITF(Ptr buffer, SInt32 prefSize, char *portName)
  688.     // This routine takes the address and size of an 'iitf' preference
  689.     // and extracts the port name from the first interface.
  690. {
  691.     OSStatus err;
  692.     UInt16 interfaceCount;
  693.     Ptr cursor;
  694.     TCPiitfPref firstInterface;
  695.     UInt8 portNameLength;
  696.     
  697.     // Get the count of interfaces, checking for possibly bogus
  698.     // preference data.
  699.     
  700.     err = noErr;
  701.     if (prefSize < sizeof(UInt16)) {
  702.         err = -1;
  703.     }
  704.     if (err == noErr) {
  705.         interfaceCount = *((UInt16 *)buffer);
  706.         if (interfaceCount < 1) {
  707.             err = -1;
  708.         }
  709.     }
  710.     
  711.     // Unpack the first interface out of the 'iitf'.
  712.     
  713.     if (err == noErr) {
  714.         cursor = buffer + sizeof(UInt16);
  715.         UnpackIITF(&cursor, &firstInterface);
  716.  
  717.         // Assert: Did not consume correct number of bytes
  718.         MoreAssertQ( interfaceCount > 1 || (cursor == buffer + prefSize) );
  719.     }
  720.     
  721.     // Copy the port name out of the unpacked interface.
  722.     
  723.     if (err == noErr) {
  724.         portNameLength = firstInterface.fPath[0];
  725.         if ( portNameLength > kMaxProviderNameLength) {
  726.             err = -1;
  727.         } else {
  728.  
  729.             // Poor Man's C2PString avoids me having to figure
  730.             // out which wacky library CodeWarrior wants me to link with
  731.             // today!
  732.             
  733.             BlockMoveData(firstInterface.fPath + 1, portName, portNameLength);
  734.             portName[ portNameLength ] = 0;
  735.         }
  736.     }
  737.  
  738.     return err;
  739. }
  740.  
  741. /////////////////////////////////////////////////////////////////
  742. #pragma mark ----- Database Will Dial ------
  743.  
  744. static OSStatus GetInfoForTCPEntity(const MNSDatabadeRef *ref, const CfgEntityRef *entityID,
  745.                                     Boolean *enabled, char *portName)
  746.     // This routine returns the enabled status and port name
  747.     // for the TCP/IP preferences entity described by entityID
  748.     // in the ref database.
  749. {    
  750.     OSStatus err;
  751.     SInt16 enabledInt;
  752.     Ptr buffer;
  753.     ByteCount prefSize;
  754.  
  755.     buffer = nil;
  756.  
  757.     // First return enabled using the simple API.
  758.     
  759.     err = MNSGetFixedSizePref(ref, entityID, 'unld', &enabledInt, sizeof(SInt16));
  760.     if (err == noErr) {
  761.         *enabled = (enabledInt != 3);
  762.     }
  763.     
  764.     // Now return the port name.  Now call the variable sized
  765.     // API to get the 'iitf' resource and then extract the port name 
  766.     // from the preference buffer.
  767.     
  768.     if (err == noErr) {
  769.         err = MNSGetPref(ref, entityID, 'iitf', &buffer, &prefSize);
  770.     }
  771.     if (err == noErr) {
  772.         err = GetPortNameFromIITF(buffer, prefSize, portName);
  773.     }
  774.     
  775.     // Clean up.
  776.     
  777.     if (buffer != nil) {
  778.         DisposePtr(buffer);
  779.         MoreAssertQ(MemError() == noErr);
  780.     }
  781.     return err;
  782. }
  783.  
  784. static OSStatus GetTCPInfoFromDatabase(Boolean *enabled, char *portName)
  785.     // The high-level entry point into the configuration database
  786.     // implementation.  We open the database, find the current
  787.     // TCP entity and read the info we need out of that entity.
  788. {
  789.     OSStatus err;
  790.     OSStatus err2;
  791.     MNSDatabadeRef ref;
  792.     CfgEntityRef currentTCPEntity;
  793.  
  794.     err = MNSOpenDatabase(&ref, false);
  795.     if (err == noErr) {
  796.         err = FindCurrentConnection(&ref, kOTTCPv4NetworkConnection, ¤tTCPEntity);
  797.         if (err == noErr) {
  798.             err = GetInfoForTCPEntity(&ref, ¤tTCPEntity, enabled, portName);
  799.         }
  800.  
  801.         err2 = MNSCloseDatabase(&ref, false);
  802.         if (err == noErr) {
  803.             err = err2;
  804.         }
  805.     }
  806.     return err;
  807. }
  808.  
  809. /////////////////////////////////////////////////////////////////
  810. #pragma mark ----- File Will Dial ------
  811.  
  812. static OSStatus GetTCPInfoFromFile(Boolean *enabled, char *portName)
  813.     // This is the high-level entry point into the direct file
  814.     // access implementation.  It simply finds the preferences
  815.     // file and reads the preferences out directly.
  816. {
  817.     OSStatus err;
  818.     FSSpec fss;
  819.     SInt16 oldResFile;
  820.     SInt16 refNum;
  821.     Handle unldResource;
  822.     Handle iitfResource;
  823.     SInt8  s;
  824.     SInt16 config;
  825.     
  826.     oldResFile = CurResFile();
  827.     err = FindNetworkPrefFile(kOTTCPv4NetworkConnection, &fss);
  828.     if (err == noErr) {
  829.         SetResLoad(false);
  830.         refNum = FSpOpenResFile(&fss, fsRdPerm);
  831.         err = ResError();
  832.         SetResLoad(true);
  833.     }
  834.     if (err == noErr) {
  835.         err = GetCurrentConfig(&config);
  836.  
  837.         if (err == noErr) {
  838.             unldResource = Get1Resource('unld', config);
  839.             err = CheckResError(unldResource);
  840.         }
  841.         if (err == noErr) {
  842.             *enabled = ( **((SInt16 **) unldResource) != 3);
  843.         }
  844.  
  845.         if (err == noErr) {
  846.             iitfResource = Get1Resource('iitf', config);
  847.             err = CheckResError(iitfResource);
  848.         }
  849.  
  850.         if (err == noErr) {
  851.             s = HGetState(iitfResource);
  852.             HLock(iitfResource);
  853.             err = GetPortNameFromIITF(*iitfResource, GetHandleSize(iitfResource), portName);
  854.             HSetState(iitfResource, s);
  855.         }
  856.         
  857.         CloseResFile(refNum);
  858.         MoreAssertQ(ResError() == noErr);
  859.     }
  860.     UseResFile(oldResFile);
  861.     MoreAssertQ(ResError() == noErr);
  862.     
  863.     return err;
  864. }
  865.  
  866. /////////////////////////////////////////////////////////////////
  867. #pragma mark ----- Will Dial Abstraction -----
  868.  
  869. static OSStatus GetTCPInfo(Boolean *enabled, char *portName)
  870.     // A dispatcher.  If the config database is available,
  871.     // we call it, otherwise we fall back to reading the
  872.     // preferences file directly.
  873. {
  874.     OSStatus err;
  875.     
  876.     if ( kUseNetworkSetup && IsNetworkSetupAvailable() ) {
  877.         #if TARGET_RT_MAC_CFM
  878.             err = GetTCPInfoFromDatabase(enabled, portName);
  879.         #else
  880.             // Network Setup has no Mixed Mode glue.  When running
  881.             // code on a PowerPC with Network Setup available, you
  882.             // should either compile your code as Fat or, if that's
  883.             // infeasible, write your own Mixed Mode glue.
  884.             return -5;
  885.         #endif
  886.     } else {
  887.         err = GetTCPInfoFromFile(enabled, portName);
  888.     }
  889.     return err;
  890. }
  891.  
  892. extern pascal OSStatus NSHTCPWillDial(UInt32 *willDial)
  893.     // The main entry point.  We call our core
  894.     // implementation and then generate the result
  895.     // based on the returned information.
  896. {
  897.     OSStatus err;
  898.     InetInterfaceInfo info;
  899.     Boolean enabled;
  900.     char currentPortName[kMaxProviderNameSize];
  901.     OTPortRecord portRecord;
  902.     
  903.     MoreAssertQ(willDial != nil);
  904.     
  905.     *willDial = kNSHTCPDialUnknown;
  906.     
  907.     err = noErr;
  908.     if ( kUseInetInterfaceInfo && OTInetGetInterfaceInfo(&info, kDefaultInetInterface) == noErr) {
  909.     
  910.         // The TCP/IP stack is already loaded.  With the current
  911.         // way TCP/IP is organised, the stack being loaded implies
  912.         // that we're already dialled in.
  913.         
  914.         *willDial = kNSHTCPDialNo;
  915.         
  916.     } else {
  917.         err = GetTCPInfo(&enabled, currentPortName);
  918.         if (err == noErr) {
  919.             if (enabled) {
  920.                 if ( OTStrEqual(currentPortName, "ddp") ) { 
  921.  
  922.                     // A special case for MacIP, because "ddp" does
  923.                     // not have an active port if AppleTalk is disabled.
  924.                     
  925.                     *willDial = kNSHTCPDialNo;
  926.                     
  927.                 } else if ( OTFindPort(&portRecord, currentPortName) ) {
  928.                 
  929.                     // We know the port.  Look at the device type
  930.                     // to decide whether we might dial.
  931.                 
  932.                     switch ( OTGetDeviceTypeFromPortRef(portRecord.fRef) ) {
  933.                         case kOTADEVDevice:
  934.                         case kOTIRTalkDevice:
  935.                         case kOTSMDSDevice:
  936.                             // Assert: TCP shouldn't be using this link type
  937.                             MoreAssertQ(false);
  938.                             *willDial = kNSHTCPDialNo;
  939.                             break;
  940.                             
  941.                         case kOTISDNDevice:
  942.                         case kOTATMDevice:
  943.                         case kOTSerialDevice:
  944.                         case kOTModemDevice:
  945.                             // Assert: TCP shouldn't be using this link type
  946.                             MoreAssertQ(false);
  947.                             *willDial = kNSHTCPDialYes;
  948.                             break;
  949.  
  950.                         case kOTLocalTalkDevice:
  951.                         case kOTTokenRingDevice:
  952.                         case kOTEthernetDevice:
  953.                         case kOTFastEthernetDevice:
  954.                         case kOTFDDIDevice:
  955.                         case kOTIrDADevice:
  956.                         case kOTATMSNAPDevice:
  957.                         case kOTFibreChannelDevice:
  958.                         case kOTFireWireDevice:
  959.                             *willDial = kNSHTCPDialNo;
  960.                             break;
  961.  
  962.                         case kOTMDEVDevice:
  963.                         case kOTSLIPDevice:
  964.                         case kOTPPPDevice:
  965.                             *willDial = kNSHTCPDialYes;
  966.                             break;
  967.  
  968.                         default:
  969.                             MoreAssertQ(*willDial == kNSHTCPDialUnknown);
  970.                             break;
  971.                     }
  972.                 } else {
  973.                     err = -1;
  974.                 }
  975.             } else {
  976.                 *willDial = kNSHTCPDialTCPDisabled;
  977.             }
  978.         }
  979.     }
  980.     
  981.     return err;
  982. }
  983.  
  984. /////////////////////////////////////////////////////////////////
  985. #pragma mark ----- 'atpf' Constants -----
  986.  
  987. // The value in the fLoadType field was originally designed to be
  988. // "minutes of inactivity till AppleTalk unloads", but then dynamic
  989. // load and unload of AppleTalk was disabled (it was basically
  990. // impossible to implement), so now we just use the values
  991. // inserted by the current control panels, ie 0xFF for active,
  992. // 0 for inactive.
  993. // 
  994. // Note that a default prefs file may still have
  995. // 5 in this field.  We handle this in IsAppleTalkActivePref but
  996. // never generate it.
  997.  
  998. enum {
  999.     kAppleTalkActive = 0xFF,
  1000.     kAppleTalkInactive = 0
  1001. };
  1002.  
  1003. /////////////////////////////////////////////////////////////////
  1004. #pragma mark ----- Database AppleTalk On/Off -----
  1005.  
  1006. static OSStatus IsAppleTalkActiveDatabase(Boolean *active)
  1007. {
  1008.     OSStatus err;
  1009.     OSStatus err2;
  1010.     MNSDatabadeRef ref;
  1011.     CfgEntityRef currentAppleTalkEntity;
  1012.     atpfPreferences atpfPref;
  1013.  
  1014.     err = MNSOpenDatabase(&ref, false);
  1015.     if (err == noErr) {
  1016.         err = FindCurrentConnection(&ref, kOTAppleTalkNetworkConnection, ¤tAppleTalkEntity);
  1017.         if (err == noErr) {
  1018.             err = MNSGetFixedSizePref(&ref, ¤tAppleTalkEntity, 'atpf', &atpfPref, sizeof(atpfPref));
  1019.         }
  1020.         if (err == noErr) {
  1021.             *active = atpfPref.fDDP.fLoadType;
  1022.         }
  1023.  
  1024.         err2 = MNSCloseDatabase(&ref, false);
  1025.         if (err == noErr) {
  1026.             err = err2;
  1027.         }
  1028.     }
  1029.     return err;
  1030. }
  1031.  
  1032. static OSStatus SetAppleTalkActiveDatabase(Boolean active)
  1033. {
  1034.     OSStatus err;
  1035.     OSStatus err2;
  1036.     MNSDatabadeRef ref;
  1037.     CfgEntityRef currentAppleTalkEntity;
  1038.     atpfPreferences atpfPref;
  1039.     Boolean changeNeeded;
  1040.     UInt8 newValue;
  1041.     
  1042.     err = MNSOpenDatabase(&ref, true);
  1043.     if (err == noErr) {
  1044.         changeNeeded = true;
  1045.         
  1046.         err = FindCurrentConnection(&ref, kOTAppleTalkNetworkConnection, ¤tAppleTalkEntity);
  1047.         if (err == noErr) {
  1048.             err = MNSGetFixedSizePref(&ref, ¤tAppleTalkEntity, 'atpf', &atpfPref, sizeof(atpfPref));
  1049.         }
  1050.         if (err == noErr) {
  1051.             if (active) {
  1052.                 newValue = kAppleTalkActive;
  1053.             } else {
  1054.                 newValue = kAppleTalkInactive;
  1055.             }
  1056.             changeNeeded = (newValue != atpfPref.fDDP.fLoadType);
  1057.             if (changeNeeded) {
  1058.                 atpfPref.fDDP.fLoadType = newValue;
  1059.                 err = MNSSetPref(&ref, ¤tAppleTalkEntity, 'atpf', &atpfPref, sizeof(atpfPref));
  1060.             }        
  1061.         }
  1062.  
  1063.         err2 = MNSCloseDatabase(&ref, (err == noErr) && changeNeeded );
  1064.         if (err == noErr) {
  1065.             err = err2;
  1066.         }
  1067.     }
  1068.     return err;
  1069. }
  1070.  
  1071. /////////////////////////////////////////////////////////////////
  1072. #pragma mark ----- File AppleTalk On/Off -----
  1073.  
  1074. static OSStatus IsAppleTalkActiveFile(Boolean *active)
  1075. {
  1076.     OSStatus err;
  1077.     FSSpec fss;
  1078.     SInt16 oldResFile;
  1079.     SInt16 refNum;
  1080.     Handle atpfResource;
  1081.     SInt16 config;
  1082.     
  1083.     oldResFile = CurResFile();
  1084.     err = FindNetworkPrefFile(kOTAppleTalkNetworkConnection, &fss);
  1085.     if (err == noErr) {
  1086.         SetResLoad(false);
  1087.         refNum = FSpOpenResFile(&fss, fsRdPerm);
  1088.         err = ResError();
  1089.         SetResLoad(true);
  1090.     }
  1091.     if (err == noErr) {
  1092.         err = GetCurrentConfig(&config);
  1093.  
  1094.         if (err == noErr) {
  1095.             atpfResource = Get1Resource('atpf', config);
  1096.             err = CheckResError(atpfResource);
  1097.         }
  1098.         if (err == noErr && GetHandleSize(atpfResource) != sizeof(atpfPreferences)) {
  1099.             err = -1;
  1100.         }
  1101.         if (err == noErr) {
  1102.             *active = (**(atpfPreferences **) atpfResource).fDDP.fLoadType != 0;
  1103.         }
  1104.         CloseResFile(refNum);
  1105.         MoreAssertQ(ResError() == noErr);
  1106.     }
  1107.     UseResFile(oldResFile);
  1108.     MoreAssertQ(ResError() == noErr);
  1109.     
  1110.     return err;
  1111. }
  1112.  
  1113. static OSStatus SetAppleTalkActiveFile(Boolean active)
  1114. {
  1115.     OSStatus err;
  1116.     FSSpec fss;
  1117.     SInt16 refNum;
  1118.     SInt16 oldResFile;
  1119.     Handle atpfResource;
  1120.     SInt16 config;
  1121.     Boolean changeNeeded;
  1122.     UInt8 newValue;
  1123.     
  1124.     oldResFile = CurResFile();
  1125.     err = FindNetworkPrefFile(kOTAppleTalkNetworkConnection, &fss);
  1126.     if (err == noErr) {
  1127.     
  1128.         // ••• Gotcha •••
  1129.         // Really need to be careful here because it's possible
  1130.         // that fss is open in our current resource chain.
  1131.         // See DTS Technote 1120 "Opening Resource Files Twice Considered
  1132.         // Hard?" for details.
  1133.         //
  1134.         //   <http://developer.apple.com/technotes/tn/tn1120.html>
  1135.         //
  1136.         // I'll probably put real code for this into MoreResources soon.
  1137.         // Meanwhile, you have to live with this limitation.
  1138.         // -- Quinn, 9 Nov 1998
  1139.         
  1140.         SetResLoad(false);
  1141.         refNum = FSpOpenResFile(&fss, fsRdWrPerm);
  1142.         err = ResError();
  1143.         SetResLoad(true);
  1144.         if (err == noErr) {
  1145.             err = GetCurrentConfig(&config);
  1146.             
  1147.             if (err == noErr) {
  1148.                 atpfResource = Get1Resource('atpf', config);
  1149.                 err = CheckResError(atpfResource);
  1150.             }
  1151.             if (err == noErr && GetHandleSize(atpfResource) != sizeof(atpfPreferences)) {
  1152.                 err = -1;
  1153.             }
  1154.             if (err == noErr) {
  1155.                 if (active) {
  1156.                     newValue = kAppleTalkActive;
  1157.                 } else {
  1158.                     newValue = kAppleTalkInactive;
  1159.                 }
  1160.                 changeNeeded = (newValue != (**(atpfPreferences **) atpfResource).fDDP.fLoadType);
  1161.                 if (changeNeeded) {
  1162.                     (**(atpfPreferences **) atpfResource).fDDP.fLoadType = newValue;
  1163.                     ChangedResource(atpfResource);
  1164.                     err = ResError();
  1165.                     if (err == noErr) {
  1166.                         UpdateResFile(refNum);
  1167.                         err = ResError();
  1168.                     }
  1169.                     if (err == noErr) {
  1170.                         err = CommitChangesToPrefFile(kOTAppleTalkNetworkConnection, refNum, config);
  1171.                     }
  1172.                 }
  1173.             }
  1174.             
  1175.             CloseResFile(refNum);
  1176.             MoreAssertQ(ResError() == noErr);
  1177.         }
  1178.     }
  1179.     UseResFile(oldResFile);
  1180.     MoreAssertQ(ResError() == noErr);
  1181.  
  1182.     return err;
  1183. }
  1184.  
  1185. /////////////////////////////////////////////////////////////////
  1186. #pragma mark ----- AppleTalk On/Off -----
  1187.  
  1188. extern pascal OSStatus NSHIsAppleTalkActive(Boolean *active)
  1189.     // See comment in header file.
  1190. {
  1191.     OSStatus err;
  1192.     
  1193.     if ( kUseNetworkSetup && IsNetworkSetupAvailable() ) {
  1194.         #if TARGET_RT_MAC_CFM
  1195.             err = IsAppleTalkActiveDatabase(active);
  1196.         #else
  1197.             // Network Setup has no Mixed Mode glue.  When running
  1198.             // code on a PowerPC with Network Setup available, you
  1199.             // should either compile your code as Fat or, if that's
  1200.             // infeasible, write your own Mixed Mode glue.
  1201.             return -5;
  1202.         #endif
  1203.     } else {
  1204.         err = IsAppleTalkActiveFile(active);
  1205.     }
  1206.     return err;
  1207. }
  1208.  
  1209. extern pascal OSStatus HSHSetAppleTalkActive(Boolean active)
  1210.     // See comment in header file.
  1211. {
  1212.     OSStatus err;
  1213.     
  1214.     if ( kUseNetworkSetup && IsNetworkSetupAvailable() ) {
  1215.         #if TARGET_RT_MAC_CFM
  1216.             err = SetAppleTalkActiveDatabase(active);
  1217.         #else
  1218.             // Network Setup has no Mixed Mode glue.  When running
  1219.             // code on a PowerPC with Network Setup available, you
  1220.             // should either compile your code as Fat or, if that's
  1221.             // infeasible, write your own Mixed Mode glue.
  1222.             return -5;
  1223.         #endif
  1224.     } else {
  1225.         err = SetAppleTalkActiveFile(active);
  1226.     }
  1227.     return err;
  1228. }
  1229.  
  1230. /////////////////////////////////////////////////////////////////
  1231.