Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
NetworkSetup/NetworkSetupHelpers.c
/* |
File: NetworkSetupHelpers.c |
Contains: High-level network preference routines. |
Written by: Quinn |
Copyright: Copyright © 1998 by Apple Computer, Inc., all rights reserved. |
You may incorporate this Apple sample source code into your program(s) without |
restriction. This Apple sample source code has been provided "AS IS" and the |
responsibility for its operation is yours. You are not permitted to redistribute |
this Apple sample source code as "Apple sample source code" after having made |
changes. If you're going to re-distribute the source, we require that you make |
it clear in the source that the code was descended from Apple sample source |
code, but that you've made changes. |
Change History (most recent first): |
<21> 18/1/00 Quinn Further updates for the new Network Setup header file. |
<20> 17/1/00 Quinn Updates for latest Network Setup headers. |
<19> 6/1/00 Quinn Updated NSHEncodeRemotePassword to use the Network Setup routine |
OTCfgEncrypt if it's available (Mac OS 9.0 and higher). |
<18> 7/12/99 Quinn AOL registers as an Ethernet device, even though it supports |
dialling. They will fix this, but I've added a special case to |
handle it here as well. |
<17> 19/10/99 Quinn Fix embarrassing spelling error. |
<16> 12/10/99 Quinn Fixed bug in the 'isdm' stage of |
BuildPackedPrefsFromTCPv4ConfigurationDigest where, if no |
fSearchDomains was specified, we would set up |
kTCPRoutersListAttr instead of kTCPDomainsListAttr. Thanks to |
John Norstad. |
<15> 13/9/99 Quinn Fix bug where CreateConfigurationDatabase wasn't setting up |
cookie4 correctly. Thanks for Tom Bayley. |
<14> 13/9/99 Quinn Fix bug in creating MacIP configurations. Thanks to Thomas |
Weisbach for the fix. |
<13> 13/9/99 Quinn Implement DHCPRelease. |
<12> 26/5/99 Quinn Use cookie4 in NSHConfigurationEntry. Implement support for |
Internet setup routines on legacy files. Improved code sharing |
between database and legacy files. Fixed bug in |
BuildPackedPrefsFromModemConfigurationDigest; duplicated 'mdpw' |
prefs. |
<11> 7/5/99 Quinn This "redux" word, I don't think it means what you think it |
means. |
<10> 22/4/99 Quinn Added code to encode Remote Access passwords. Also reworked how |
the default 'lcp ' preference was generated, using Network Setup |
if possible. |
<9> 21/4/99 Quinn Added massive amounts of code to create, duplicate, get, set, |
delete and rename configurations for TCP/IP, Remote Access, and |
Modem. Also fixed a bug in NSHIsAppleTalkActive where it was |
return an assembly Boolean (0/0xff) rather than a C Boolean |
(0/1). |
<8> 16/3/99 Quinn Fixed one-based array problem in GetConfigurationListFromFile. |
<7> 10/11/98 Quinn Fix = vs == in an assert. |
<6> 10/11/98 Quinn Convert "MorePrefix.h" to "MoreSetup.h". |
<5> 9/11/98 Quinn AppleTalk on/off support. |
<4> 9/11/98 Quinn Add "TCP will dial" code. |
<3> 5/11/98 Quinn Use MoreAssertQ instead of MoreAssert. |
<2> 5/11/98 Quinn Fix header. |
<1> 5/11/98 Quinn First checked in. |
*/ |
///////////////////////////////////////////////////////////////// |
// MoreIsBetter Setup |
#include "MoreSetup.h" |
// Mac OS Interfaces |
#include <Types.h> |
#include <Files.h> |
#include <Errors.h> |
#include <Folders.h> |
#include <Resources.h> |
#include <Gestalt.h> |
#include <CodeFragments.h> |
#include <NetworkSetup.h> |
#include <OpenTptLinks.h> |
#include <OpenTptConfig.h> |
#include <Traps.h> |
// MIB Prototypes (stuff that should be in Universal Interfaces) |
#include "RemoteAccessInterface.h" |
// MIB Prototypes (stuff that should be in Universal Interfaces) |
#include "MoreTextUtils.h" |
#include "MoreInterfaceLib.h" |
#include "MoreNetworkSetup.h" |
#include "OldOTConfigLib.h" |
// Our Prototypes |
#include "NetworkSetupHelpers.h" |
///////////////////////////////////////////////////////////////// |
// Testing Parameters |
enum { |
// Throw this switch if you want to debug the direct preference |
// file access code on a machine with Network Setup installed. |
kUseNetworkSetup = true, |
// If you set kUseInetInterfaceInfo to false, NSHTCPWillDial will not |
// use the heuristic of "if the TCP/IP stack is loaded, it's safe |
// to open an endpoint". This is especially useful when debugging. |
kUseInetInterfaceInfo = true |
}; |
///////////////////////////////////////////////////////////////// |
extern pascal ItemCount NSHCountConfigurationList(NSHConfigurationListHandle configList) |
// See comments in interface part. |
{ |
return GetHandleSize( (Handle) configList ) / sizeof(NSHConfigurationEntry); |
} |
///////////////////////////////////////////////////////////////// |
#pragma mark ----- Configuration List using Database ----- |
// Parameter structure for SetterIterator. |
struct TypeAndClassParam { |
OSType fType; |
OSType fClass; |
Boolean found; |
CfgEntityRef *currentEntity; |
}; |
typedef struct TypeAndClassParam TypeAndClassParam; |
static pascal void TypeAndClassIterator(const MNSDatabaseRef *ref, CfgSetsElement *thisElement, void *p) |
// This routine is used as a callback for MNSIterateSet. |
// It looks for the entity specified by the fType and fClass |
// fields of the param, and puts its entityRef into the |
// variable pointed to by the currentEntity field of param. |
{ |
TypeAndClassParam *param; |
param = (TypeAndClassParam *) p; |
MoreAssertQ(MNSValidDatabase(ref)); |
MoreAssertQ(thisElement != nil); |
MoreAssertQ(param != nil); |
MoreAssertQ(param->currentEntity != nil); |
if (thisElement->fEntityInfo.fClass == param->fClass && thisElement->fEntityInfo.fType == param->fType) { |
MoreAssertQ( ! param->found ); |
param->found = true; |
*(param->currentEntity) = thisElement->fEntityRef; |
} |
} |
enum { |
kNoCurrentConnectionErr = -6 |
}; |
static OSStatus FindCurrentConnection(const MNSDatabaseRef *ref, OSType protocol, CfgEntityRef *currentEntity) |
// This routine finds the current connection entity for the specified |
// protocol in the active set, returning it in currentEntity. |
{ |
OSStatus err; |
CfgEntityRef activeSet; |
TypeAndClassParam param; |
MoreAssertQ(MNSValidDatabase(ref)); |
MoreAssertQ(currentEntity != nil); |
param.fClass = kOTCfgClassNetworkConnection; |
param.fType = protocol; |
param.found = false; |
param.currentEntity = currentEntity; |
err = MNSFindActiveSet(ref, &activeSet); |
if (err == noErr) { |
err = MNSIterateSet(ref, &activeSet, TypeAndClassIterator, ¶m, false); |
} |
if (err == noErr) { |
if (param.found) { |
// Set preferences contain entities from weird areas because |
// of the way the database is committed. We can safely fix |
// that up here. I discussed with the Network Setup engineer |
// and he reassured me that this was cool -- Quinn, 9 Nov 1998 |
currentEntity->fLoc = ref->area; |
} else { |
err = kNoCurrentConnectionErr; |
} |
} |
return err; |
} |
static OSStatus AddEntityToConfigurationList(const MNSDatabaseRef *ref, |
const CfgEntityRef *entity, |
const CfgEntityInfo *entityInfo, |
OSType protocol, |
NSHConfigurationListHandle configList) |
// This routines adds the entity specified by entity and entityInfo |
// in the database specified by ref to the configList. |
{ |
OSStatus err; |
NSHConfigurationEntry thisEntry; |
StringPtr entityName; |
ByteCount junkSize; |
MoreAssertQ(configList != nil); |
MoreAssertQ(MNSValidDatabase(ref)); |
MoreAssertQ(entity != nil); |
MoreAssertQ(entityInfo != nil); |
// Get the user-visible name from the configuration, which is |
// stored in the 'pnam' preferences. |
entityName = nil; |
err = MNSGetPref(ref, entity, kOTCfgUserVisibleNamePref, &entityName, &junkSize); |
if (err == noErr) { |
MoreAssertQ(junkSize == (entityName[0] + 1)); |
BlockMoveData(entityName, &thisEntry.name, entityName[0] + 1); |
thisEntry.selected = false; |
thisEntry.cookie = 0; |
thisEntry.cookie2 = *entity; |
thisEntry.cookie3 = *entityInfo; |
thisEntry.cookie4 = protocol; |
} |
if (err == noErr) { |
err = PtrAndHand(&thisEntry, (Handle) configList, sizeof(thisEntry)); |
} |
// Clean up. |
if (entityName != nil) { |
DisposePtr( (Ptr) entityName); |
MoreAssertQ(MemError() == noErr); |
} |
return err; |
} |
// Parameter structure for SetterIterator. |
struct SetterParam { |
OSType fType; |
OSType fClass; |
const CfgEntityRef *chosenConfig; |
const CfgEntityInfo *chosenConfigInfo; |
}; |
typedef struct SetterParam SetterParam; |
static pascal void SetterIterator(const MNSDatabaseRef *ref, CfgSetsElement *thisElement, void *p) |
// This routine is used as a callback for MNSIterateSet. |
// It looks for the entity specified by the fType and fClass |
// fields of the param, and replaces it with the chosen |
// entity and info from the param. It expects that the caller |
// of MNSIterateSet has specified writeAfterIterate so that |
// the changes get written back to the set. |
{ |
SetterParam *param; |
param = (SetterParam *) p; |
MoreAssertQ(MNSValidDatabase(ref)); |
MoreAssertQ(MNSDatabaseWritable(ref)); |
MoreAssertQ(thisElement != nil); |
MoreAssertQ(param != nil); |
MoreAssertQ(param->chosenConfig != nil); |
MoreAssertQ(param->chosenConfigInfo != nil); |
if (thisElement->fEntityInfo.fClass == param->fClass && thisElement->fEntityInfo.fType == param->fType) { |
thisElement->fEntityRef = *param->chosenConfig; |
thisElement->fEntityInfo = *param->chosenConfigInfo; |
} |
} |
static OSStatus GetConfigurationListFromDatabase(OSType protocol, NSHConfigurationListHandle configList) |
// Implementation of NSHGetConfigurationList which uses the Network Setup |
// database. See NSHGetConfigurationList's comment in header |
// file for interface specification. |
{ |
OSStatus err; |
OSStatus err2; |
MNSDatabaseRef ref; |
ItemCount entityCount; |
CfgEntityRef *entityRefs; |
CfgEntityInfo *entityInfos; |
CfgEntityRef activeConn; |
ItemCount i; |
entityRefs = nil; |
entityInfos = nil; |
err = MNSOpenDatabase(&ref, false); |
if (err == noErr) { |
// Find all the network connection entities for this protocol. |
err = MNSGetEntitiesList(&ref, |
kOTCfgClassNetworkConnection, protocol, |
&entityCount, |
&entityRefs, |
&entityInfos); |
// Add each to the list of possible connections. |
if (err == noErr) { |
for (i = 0; i < entityCount; i++) { |
err = AddEntityToConfigurationList(&ref, &entityRefs[i], &entityInfos[i], protocol, configList); |
} |
} |
// Find the current configuration and mark it as selected |
// in the list. |
if (err == noErr) { |
err = FindCurrentConnection(&ref, protocol, &activeConn); |
} |
if (err == noErr) { |
for (i = 0; i < entityCount; i++) { |
if ( OTCfgIsSameEntityRef(&activeConn, &(*configList)[i].cookie2, kOTCfgIgnoreArea) ) { |
(*configList)[i].selected = true; |
} |
} |
} |
err2 = MNSCloseDatabase(&ref, false); |
if (err == noErr) { |
err = err2; |
} |
} |
// Clean up. |
if (entityInfos != nil) { |
DisposePtr( (Ptr) entityInfos); |
MoreAssertQ(MemError() == noErr); |
} |
if (entityRefs != nil) { |
DisposePtr( (Ptr) entityRefs); |
MoreAssertQ(MemError() == noErr); |
} |
return err; |
} |
static OSStatus SelectConfigurationFromDatabase(const NSHConfigurationEntry *chosenEntry) |
// Implementation of NSHSelectConfiguration which uses the Network Setup |
// database. See NSHSelectConfiguration's comment in header |
// file for interface specification. |
{ |
OSStatus err; |
OSStatus err2; |
MNSDatabaseRef ref; |
CfgEntityRef activeSet; |
SetterParam param; |
err = MNSOpenDatabase(&ref, true); |
if (err == noErr) { |
param.fClass = kOTCfgClassNetworkConnection; |
param.fType = chosenEntry->cookie4; |
param.chosenConfig = &chosenEntry->cookie2; |
param.chosenConfigInfo = &chosenEntry->cookie3; |
err = MNSFindActiveSet(&ref, &activeSet); |
if (err == noErr) { |
err = MNSIterateSet(&ref, &activeSet, SetterIterator, ¶m, true); |
} |
err2 = MNSCloseDatabase(&ref, err == noErr); |
if (err == noErr) { |
err = err2; |
} |
} |
return err; |
} |
///////////////////////////////////////////////////////////////// |
#pragma mark ----- Configuration List using File ----- |
static OSStatus SearchFolder(SInt16 vRefNum, SInt32 dirID, |
OSType typeToSearchFor, OSType creatorToSearchFor, |
FSSpec *fss) |
// Search a particular folder for a file of a particular |
// type and creator. If it's found, return an FSSpec to |
// the file. If it's not found, return an error. |
{ |
OSStatus err; |
Boolean found; |
SInt16 index; |
HParamBlockRec pb; |
MoreAssertQ(fss != nil); |
fss->vRefNum = vRefNum; |
fss->parID = dirID; |
found = false; |
index = 1; |
do { |
pb.fileParam.ioVRefNum = vRefNum; |
pb.fileParam.ioDirID = dirID; |
pb.fileParam.ioNamePtr = fss->name; |
pb.fileParam.ioFDirIndex = index; |
err = PBHGetFInfoSync(&pb); |
if (err == noErr) { |
found = ( pb.fileParam.ioFlFndrInfo.fdType == typeToSearchFor && |
pb.fileParam.ioFlFndrInfo.fdCreator == creatorToSearchFor ); |
} |
index += 1; |
} while (err == noErr & ! found); |
return err; |
} |
enum { |
kOTNetworkPrefFileType = 'pref', |
kOTTCPPrefFileCreator = 'ztcp', |
kOTAppleTalkPrefFileCreator = 'atdv', |
kModemPrefFileType = 'mdpf', |
kModemPrefFileCreator = 'modm', |
kRemotePrefFileType = 'lzcn', |
kRemotePrefFileCreator = 'rmot' |
}; |
static OSStatus FindNetworkPrefFile(OSType protocol, FSSpec *fss) |
// This routine scans the Preferences folder looking |
// for the preferences for the given network protocol. |
// Scans are done by file type and creator to avoid |
// problems on localised systems. |
{ |
OSStatus err; |
Boolean searchSubFolders; |
OSType typeToSearchFor; |
OSType creatorToSearchFor; |
SInt16 prefFolderVRefNum; |
SInt32 prefFolderDirID; |
// Set up the file type and creator based on the protocol. |
searchSubFolders = false; |
switch (protocol) { |
case kOTCfgTypeTCPv4: |
typeToSearchFor = kOTNetworkPrefFileType; |
creatorToSearchFor = kOTTCPPrefFileCreator; |
break; |
case kOTCfgTypeAppleTalk: |
typeToSearchFor = kOTNetworkPrefFileType; |
creatorToSearchFor = kOTAppleTalkPrefFileCreator; |
break; |
case kOTCfgTypeRemote: |
typeToSearchFor = kRemotePrefFileType; |
creatorToSearchFor = kRemotePrefFileCreator; |
searchSubFolders = true; |
break; |
case kOTCfgTypeModem: |
typeToSearchFor = kModemPrefFileType; |
creatorToSearchFor = kModemPrefFileCreator; |
break; |
default: |
MoreAssertQ(false); |
break; |
} |
// Search the Preferences folder for a file with that type and creator. |
err = FindFolder(kOnSystemDisk, kPreferencesFolderType, kCreateFolder, &prefFolderVRefNum, &prefFolderDirID); |
if (err == noErr) { |
// The Remote Access preference file is stored in a folder within |
// the preferences folder. We can't hard wire the name "Remote Access" |
// because the name is localised. Instead, we search all the folders |
// within the preferences folder for the file. In all other cases, |
// we just search the preferences folder for the file. |
if (searchSubFolders) { |
Boolean found; |
CInfoPBRec cpb; |
SInt16 index; |
found = false; |
index = 1; |
do { |
cpb.dirInfo.ioVRefNum = prefFolderVRefNum; |
cpb.dirInfo.ioDrDirID = prefFolderDirID; |
cpb.dirInfo.ioNamePtr = nil; |
cpb.dirInfo.ioFDirIndex = index; |
err = PBGetCatInfoSync(&cpb); |
if (err == noErr && ((cpb.dirInfo.ioFlAttrib & ioDirMask) != 0)) { |
found = ( SearchFolder(prefFolderVRefNum, cpb.dirInfo.ioDrDirID, |
typeToSearchFor, creatorToSearchFor, |
fss) == noErr); |
} |
index += 1; |
} while (err == noErr & ! found); |
} else { |
err = SearchFolder(prefFolderVRefNum, prefFolderDirID, |
typeToSearchFor, creatorToSearchFor, |
fss); |
} |
} |
return err; |
} |
static OSStatus CheckResError(void *testH) |
// A trivial wrapper routine for ResError, |
// which is too lame to report an error code |
// in all cases when GetResource fails. |
{ |
OSStatus err; |
err = ResError(); |
if (err == noErr && testH == nil) { |
err = resNotFound; |
} |
return err; |
} |
static OSStatus OpenNetworkPrefFile(OSType protocol, SInt8 permission, |
SInt16 *oldResFile, SInt16 *networkResFile) |
// Opens the legacy preference file for the given protocol with |
// the specified permission (typicallly fsRdPerm or fsRdWrPerm). |
// Returns the previous CurResFile and the refnum of the new |
// file, both of which you pass to CloseNetworkPrefFile to clean up |
// the open. |
{ |
OSStatus err; |
FSSpec fss; |
MoreAssertQ(oldResFile != nil); |
MoreAssertQ(networkResFile != nil); |
*oldResFile = CurResFile(); |
err = FindNetworkPrefFile(protocol, &fss); |
if (err == noErr) { |
if (permission == fsRdWrPerm) { |
// ¥¥¥ Gotcha ¥¥¥ |
// Really need to be careful here because it's possible |
// that fss is open in our current resource chain. |
// See DTS Technote 1120 "Opening Resource Files Twice Considered |
// Hard?" for details. |
// |
// <http://developer.apple.com/technotes/tn/tn1120.html> |
// |
// I'll probably put real code to detect and handle this into |
// MoreResources eventually; in the mean time, you have to live |
// with the limitation that you can't use this library when |
// a legacy preference file might be open in your resource chain. |
// -- Quinn, 9 Nov 1998 |
} |
// SetResLoad to false around the open to avoid bringing |
// any preload resources in the file into memory. |
SetResLoad(false); |
*networkResFile = FSpOpenResFile(&fss, permission); |
err = ResError(); |
SetResLoad(true); |
} |
// If we error, setup the outputs to values we can use to |
// detect client logic errors in CloseNetworkPrefFile. |
if (err != noErr) { |
*oldResFile = 0; |
*networkResFile = 0; |
} |
return err; |
} |
static OSStatus CloseNetworkPrefFile(SInt16 oldResFile, SInt16 networkResFile) |
// Closes the legacy preference file you opened using OpenNetworkPrefFile. |
{ |
OSStatus err; |
MoreAssertQ(oldResFile != 0); |
MoreAssertQ(networkResFile != 0); |
CloseResFile(networkResFile); |
err = ResError(); |
if (err == noErr) { |
UseResFile(oldResFile); |
MoreAssertQ(ResError() == noErr); |
} |
return err; |
} |
static OSStatus CommitChangesToPrefFile(OSType protocol, SInt16 refNum, SInt16 config) |
// This routine represents the magic that allows you to force OT |
// to notice a configuration file change without rebooting. It uses |
// some previously undocumented routines, the glue for which is provided |
// as part of this sample. This routine is called by any direct file |
// modification code which modifies the active configuration. |
// |
// IMPORTANT: |
// The only reason it's safe to document these routines now is that we |
// know that they work on all old versions of OT, and new versions of OT |
// include the Network Setup library which allows you to change network |
// preferences without any of this hackery. Never ship a product that |
// relies on these routines that doesn't also use Network Setup if it's |
// available. |
{ |
OSStatus err; |
err = noErr; |
switch (protocol) { |
case kOTCfgTypeTCPv4: |
if ( TCPCheckChangeConfigurationConsequences(refNum, config) == kMustReboot ) { |
err = -7; |
} |
if ( err == noErr ) { |
err = TCPChangeConfiguration(refNum, config); |
} |
break; |
case kOTCfgTypeAppleTalk: |
if ( ATCheckChangeConfigurationConsequences(refNum, config) == kMustReboot ) { |
err = -7; |
} |
if ( err == noErr ) { |
err = ATChangeConfiguration(refNum, config); |
} |
break; |
case kOTCfgTypeRemote: |
case kOTCfgTypeModem: |
{ |
Handle currentConfigResourceH; |
// For Remote and Modem, we directly munge the preferences |
// file. There's no way to tweak these protocol stacks |
// to get them to recognise the updated file. Instead, |
// they'll pick up the preferences the next time you connect. |
// Hmmm, except for ARA Personal Server, for which we |
// have no solution at the moment. |
currentConfigResourceH = Get1Resource(kOTCfgCompatSelectedPref, 1); |
err = CheckResError(currentConfigResourceH); |
if (err == noErr && GetHandleSize(currentConfigResourceH) != sizeof(SInt16) ) { |
// Assert: 'ccfg' is of the wrong size |
MoreAssertQ(false); |
err = -1; |
} else { |
**(SInt16 **)currentConfigResourceH = config; |
ChangedResource(currentConfigResourceH); |
err = ResError(); |
} |
} |
break; |
default: |
MoreAssertQ(false); |
err = -7; |
break; |
} |
return err; |
} |
static OSStatus GetCurrentConfigFromPrefFile(SInt16 *config) |
// This routine returns the resource ID of the current |
// configurator in a legacy preferences file. |
{ |
OSStatus err; |
Handle currentConfigResourceH; |
MoreAssertQ(config != nil); |
currentConfigResourceH = Get1Resource(kOTCfgCompatSelectedPref, 1); |
err = CheckResError(currentConfigResourceH); |
if (err == noErr && GetHandleSize(currentConfigResourceH) != sizeof(SInt16) ) { |
// Assert: 'ccfg' is of the wrong size |
MoreAssertQ(false); |
err = -1; |
} else { |
*config = **(SInt16 **)currentConfigResourceH; |
} |
return err; |
} |
static OSStatus AddResourceToConfigurationList(OSType protocol, Handle cnamHandle, NSHConfigurationListHandle configList) |
// Given a handle to a 'cnam' resource, generate a |
// NSHConfigurationEntry and append it to the list |
// of configurations. |
{ |
OSStatus err; |
NSHConfigurationEntry thisEntry; |
SInt16 cnamID; |
ResType junkType; |
GetResInfo(cnamHandle, &cnamID, &junkType, thisEntry.name); |
MoreAssertQ(ResError() == noErr); |
MoreAssertQ(junkType == kOTCfgCompatNamePref); |
thisEntry.selected = false; |
thisEntry.cookie = cnamID; |
OTMemzero(&thisEntry.cookie2, sizeof(thisEntry.cookie2)); |
OTMemzero(&thisEntry.cookie3, sizeof(thisEntry.cookie3)); |
thisEntry.cookie4 = protocol; |
err = PtrAndHand(&thisEntry, (Handle) configList, sizeof(thisEntry)); |
return err; |
} |
static OSStatus GetConfigurationListFromFile(OSType protocol, NSHConfigurationListHandle configList) |
// Implementation of NSHGetConfigurationList which uses the legacy |
// preference files. See NSHGetConfigurationList's comment in header |
// file for interface specification. |
{ |
OSStatus err; |
OSStatus err2; |
Handle cnamHandle; |
SInt16 refNum; |
SInt16 resCount; |
SInt16 i; |
SInt16 currentConfigID; |
SInt16 oldResFile; |
err = OpenNetworkPrefFile(protocol, fsRdPerm, &oldResFile, &refNum); |
if (err == noErr) { |
resCount = Count1Resources(kOTCfgCompatNamePref); |
for (i = 1; i <= resCount; i++) { |
SetResLoad(false); |
cnamHandle = Get1IndResource(kOTCfgCompatNamePref, i); |
err = CheckResError(cnamHandle); |
SetResLoad(true); |
if (err == noErr) { |
err = AddResourceToConfigurationList(protocol, cnamHandle, configList); |
} |
// Don't need to release the resource because CloseResFile will |
// clean it up. |
if (err != noErr) { |
break; |
} |
} |
if (err == noErr) { |
err = GetCurrentConfigFromPrefFile(¤tConfigID); |
} |
if (err == noErr) { |
for (i = 0; i < resCount; i++) { |
if ( (*configList)[i].cookie == currentConfigID ) { |
(*configList)[i].selected = true; |
} |
} |
} |
err2 = CloseNetworkPrefFile(oldResFile, refNum); |
if (err == noErr) { |
err = err2; |
} |
} |
return err; |
} |
static OSStatus SelectConfigurationFromFile(const NSHConfigurationEntry *chosenEntry) |
// Implementation of NSHGetConfigurationList which uses the legacy |
// preference files. See NSHGetConfigurationList's comment in header |
// file for interface specification. |
{ |
OSStatus err; |
OSStatus err2; |
SInt16 refNum; |
SInt16 oldResFile; |
err = OpenNetworkPrefFile(chosenEntry->cookie4, fsRdWrPerm, &oldResFile, &refNum); |
if (err == noErr) { |
err = CommitChangesToPrefFile(chosenEntry->cookie4, refNum, chosenEntry->cookie); |
err2 = CloseNetworkPrefFile(oldResFile, refNum); |
if (err == noErr) { |
err = err2; |
} |
} |
return err; |
} |
///////////////////////////////////////////////////////////////// |
#pragma mark ----- Configuration List Abstraction ------ |
extern pascal OSStatus NSHGetConfigurationList(OSType protocol, NSHConfigurationListHandle configList) |
// See comments in interface part. |
{ |
OSStatus err; |
SetHandleSize( (Handle) configList, 0); |
MoreAssertQ(MemError() == noErr); |
if ( kUseNetworkSetup && IsNetworkSetupAvailable() ) { |
#if TARGET_RT_MAC_CFM |
err = GetConfigurationListFromDatabase(protocol, configList); |
#else |
// Network Setup has no Mixed Mode glue. When running |
// code on a PowerPC with Network Setup available, you |
// should either compile your code as Fat or, if that's |
// infeasible, write your own Mixed Mode glue. |
return -5; |
#endif |
} else { |
err = GetConfigurationListFromFile(protocol, configList); |
} |
return err; |
} |
extern pascal OSStatus NSHSelectConfiguration(const NSHConfigurationEntry *chosenEntry) |
// See comments in interface part. |
{ |
OSStatus err; |
if ( kUseNetworkSetup && IsNetworkSetupAvailable() ) { |
#if TARGET_RT_MAC_CFM |
err = SelectConfigurationFromDatabase(chosenEntry); |
#else |
// Network Setup has no Mixed Mode glue. When running |
// code on a PowerPC with Network Setup available, you |
// should either compile your code as Fat or, if that's |
// infeasible, write your own Mixed Mode glue. |
return -5; |
#endif |
} else { |
err = SelectConfigurationFromFile(chosenEntry); |
} |
return err; |
} |
///////////////////////////////////////////////////////////////// |
#pragma mark ----- Internet Setup Documentation ------ |
/* |
The mapping from NSHTCPv4ConfigurationDigest fields to preferences |
is as follows: |
fProtocol -> kOTCfgTypeTCPv4 |
fConfigName; -> 'pnam' |
fPortRef; -> 'port', 'iitf' |
fUnloadAttr; -> 'unld' |
fDNSServerList; -> 'idns' |
fLocalDomain; -> 'ihst' |
fAdminDomain; -> 'ihst' |
fConfigMethod; -> 'iitf' |
fIPAddress; -> 'iitf' |
fSubnetMask; -> 'iitf' |
fAppleTalkZone; -> 'iitf' |
fFraming; -> 'iitf' |
fSearchDomains; -> 'isdm' |
fRouterList; -> 'irte' |
The reverse mapping, from preference to NSHTCPv4ConfigurationDigest field, |
is: |
'pnam' -> fConfigName |
'port' -> UserVisibleName(fPortRef) -- or "AppleTalk (Mac IP)", localisation? |
'pwrd' -> "\p" |
'cvrs' -> 1 |
'prot' -> "tcp" |
'unld' -> fUnloadAttr |
'idns' -> GetHandleSize(fDNSServerList) div sizeof(InetHost), fDNSServerList; |
'ihst' -> 1, fLocalDomain, fAdminDomain (packed) |
'iitf' -> 1, fConfigMethod, fIPAddress, fSubnetMask, fAppleTalkZone, PortName(fPortRef), fModuleName(fPortRef), fFraming |
'dtyp' -> DeviceType(fPortRef) -- or kOTNoDevice for MacIP |
'stng' -> $00 * 25 |
'isdm' -> fSearchDomains |
'irte' -> fRouterList, padded appropriately |
*/ |
/* |
Mapping from NSHRemoteConfigurationDigest fields to preference types: |
fProtocol; kOTCfgTypeRemote |
fConfigName; -> 'pnam' |
fGuestLogin -> 'conn' (isGuest) |
fPasswordValid -> 'conn' (passwordSaved) |
fUserName; -> 'cusr' |
fPassword; -> 'pass' |
fPhoneNumber; -> 'cadr', 'conn' (addressLength) |
fRedialMode; -> 'cdia' (dialMode) |
fRedialTimes; -> 'cdia' (redialTries) |
fRedialDelay; -> 'cdia' (redialDelay) |
fAlternatePhoneNumber; -> 'cead' |
fVerboseLogging; -> 'logo' (logLevel) |
fFlashIconWhileConnected; -> 'conn' (flashConnectedIcon) |
fPromptToRemainConnected; -> 'conn' (issueConnectedReminders) |
fPromptInterval; -> 'conn' (reminderMinutes) |
fDisconnectIfIdle; -> 'ipcp' (idleTimerEnabled) |
fDisconnectInterval; -> 'ipcp' (idleTimerMilliseconds) |
fSerialProtocol; -> 'conn' (serialProtocolMode) |
fPPPConnectAutomatically; -> 'cmsc' (isAutoConnect) |
fPPPAllowModemCompression; -> 'conn' (allowModemDataCompression) |
fPPPAllowTCPIPHeaderCompression; -> 'ipcp' (compressTCPHeaders) |
fPPPConnectMode; -> 'conn' (chatMode) |
fPPPConnectScriptName; -> 'conn' (chatScriptName) |
fPPPConnectScript; -> 'ccha', 'conn' (chatScriptLength) |
fixed value (see below) -> 'lcp ' |
$0002 $0003 "Script" [36] -> 'arap' |
$0002 $0003 $00 * 596 -> 'x25 ' |
$0002 $0003 $00 * 68 -> 'dass' |
$00010000 $00000001 $00 * 256 -> 'usmd' |
$0002 $0003 $00 * 64 -> 'clks' |
This is the mapping from preference type to NSHRemoteConfigurationDigest field. |
'pnam' -> fConfigName |
'cmsc' -> fPPPConnectAutomatically |
'conn' -> fGuestLogin, fPasswordValid, fPhoneNumber, fFlashIconWhileConnected, fPromptToRemainConnected, fPromptInterval, fSerialProtocol, fPPPAllowModemCompression, fPPPConnectMode, fPPPConnectScriptName, fPPPConnectScript |
'cusr' -> fUserName |
'pass' -> fPassword |
'cdia' -> fRedialMode, fRedialTimes, fRedialDelay |
'ipcp' -> fDisconnectIfIdle, fDisconnectInterval, fPPPAllowTCPIPHeaderCompression |
'logo' -> fVerboseLogging |
'cadr' -> fPhoneNumber |
'cead' -> fAlternatePhoneNumber |
'ccha' -> fPPPConnectScript |
'usmd' -> junk |
'clks' -> junk |
'lcp ' -> junk |
'arap' -> junk |
'dass' -> junk |
'x25 ' -> junk |
'term' -> junk |
'cnam' -> junk |
'resn' -> junk |
'resi' -> junk |
*/ |
// This fixed value for the Remote Access 'lcp ' preference is only used if |
// OTCfgGetDefault is not available. |
static UInt8 gRemoteLCPValue[108] = { |
0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x63, 0x72, 0x69, |
0x70, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0A, |
0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x27, 0x10, 0x00, 0x00, 0x00, 0x05, |
0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0xDC, 0x00, 0x00, 0x11, 0x94, 0x00, 0x00, 0x00, 0x00, |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 |
}; |
/* |
This is the mapping from preference type to NSHModemConfigurationDigest field. |
'pnam' -> fConfigName |
'ccl ' -> fPortRef, fModemScript, fDialToneMode, fSpeakerOn, fPulseDial, fHintPortName |
'lkmd' -> $00000001 + $00000000 * 4 |
'mdpw' -> $00 * 256 |
'cnam' -> junk |
'resn' -> junk |
'resi' -> junk |
*/ |
///////////////////////////////////////////////////////////////// |
#pragma mark ----- Packed Pref Builder/Writer Infrastructure ------ |
// All the code that manipulates groups of preferences, ie an entity |
// in a Network Setup sense, uses "packed preferences". This is |
// a group of preferences packed into a handle. Each preference |
// contains a header of its type (OSType) and its data size (ByteCount), |
// not including the header itself. The handle is terminated by |
// an entry with a null preference type. |
// |
// The following routines are an easy way to build and parse these |
// packed preferences. The key focus here was to minimise error |
// checked. So when you build a packed preference, all the checking |
// is done inside these helper routines and you only have to check |
// for errors at the end. Similarly, when you parse a packed preference |
// handle, you know it was built successfully so you can rely on its |
// structure. |
// |
// You typically use these routines in the following way. First, |
// call BuilderNew to initialise the PrefBuilderState record. Then |
// call BuilderNewPref to add a new preference to the handle. If |
// necessary, you can also call BuilderAddPrefData to add extra |
// data to the most recently added preference. Finally, call |
// BuilderDone to extract the packed preference handle, or obtain |
// any error that might have happened while building. |
// When building a packed preference, the following state record |
// is used to keep track of what's going on. |
struct PrefBuilderState { |
Handle prefData; // handle of packed preference itself, or nil if we got an error somewhere |
OSStatus latchedError; // if an error occured, this field is used to store it until BuilderDone is called |
ByteCount offsetToMostRecentPrefSize; // offset to most recent pref size value, |
// allows BuilderAddPrefData to look back in the handle and bump this size |
}; |
typedef struct PrefBuilderState PrefBuilderState; |
static void BuilderError(PrefBuilderState *state, OSStatus errNum) |
// This routine is called when an error happens while building. |
// It disposes of the packed preference handle (which ensures |
// that no more building is done) and latches the error where |
// BuilderDone can find it. |
{ |
DisposeHandle(state->prefData); |
MoreAssertQ(MemError() == noErr); |
state->prefData = nil; |
state->latchedError = errNum; |
} |
static void BuilderNew(PrefBuilderState *state) |
// Initialise the builder state record. See above for |
// this routines place in the big picture. If you call |
// this routine, you must also call BuilderDone to clean |
// up the builder (ie dispose of the handle and recover |
// the latched error code). |
{ |
OSStatus err; |
state->latchedError = noErr; |
state->offsetToMostRecentPrefSize = 0; |
state->prefData = NewHandle(0); |
err = MemError(); |
if (err != noErr) { |
BuilderError(state, err); |
} |
} |
static void BuilderAddPrefData(PrefBuilderState *state, const void *data, ByteCount size) |
// Add preference data, described by data and size, to the previous |
// preference added to the builder. It's an error to call this without |
// first calling BuilderNewPref. |
{ |
OSStatus err; |
if (state->prefData != nil) { |
// If this assert fires, it means you've called this routine without first calling BuilderNewPref. |
MoreAssertQ(state->offsetToMostRecentPrefSize != 0); |
// Add the data to the preference handle. |
err = PtrAndHand(data, state->prefData, size); |
// Reach back into the preference handle, find the size |
// of the current preference, and increment it by the amount |
// of data we added. Note that this might fail on a 68000 |
// processor (because it can't handle memory accesses to words |
// off word boundaries) but this isn't an issue because OT |
// requires an 68030 or above. |
if (err == noErr) { |
*((ByteCount *)((*(state->prefData)) + state->offsetToMostRecentPrefSize)) += size; |
} |
// Handle errors. |
if (err != noErr) { |
BuilderError(state, err); |
} |
} |
} |
static void BuilderNewPref(PrefBuilderState *state, OSType prefType, const void *data, ByteCount size) |
// Add a new preference to the builder, with the data specified |
// by data and size. You can pass nil and 0 to these parameters |
// to add an empty preference. Regardless, you can later call |
// BuilderAddPrefData to add extra data to this preference, up |
// until you call BuilderNewPref again. |
{ |
OSStatus err; |
ByteCount initialPrefSize; |
if (state->prefData != nil) { |
// Add the preference type to the preference handle. |
err = PtrAndHand(&prefType, state->prefData, sizeof(prefType)); |
// Record the current offset into the preference handle |
// (which is the offset of this preference size value, |
// which is needed by BuilderAddPrefData) and then add |
// a default size value of 0 to the preference. |
if (err == noErr) { |
state->offsetToMostRecentPrefSize = GetHandleSize(state->prefData); |
initialPrefSize = 0; |
err = PtrAndHand(&initialPrefSize, state->prefData, sizeof(initialPrefSize)); |
} |
// Handle errors. |
if (err != noErr) { |
BuilderError(state, err); |
} |
} |
// Finally, call BuilderAddPrefData to add the actual |
// preference data. Note that if the above errored, the |
// error will have been latched in the state record, and |
// this call will be a no-op. |
if (size != 0) { |
BuilderAddPrefData(state, data, size); |
} |
} |
static OSStatus BuilderDone(PrefBuilderState *state, Handle *prefData) |
// Extracts the prefData from the builder state. You must |
// pass in a pointer to a nil handle. If building was successful, |
// this routine returns noErr and sets the handle to be |
// the built packed preferences. From there on, the memory |
// belongs to you. |
// |
// If the building was unsuccessful, the routine returns an error |
// and the handle remains nil. |
{ |
OSStatus err; |
MoreAssertQ(prefData != nil); |
// Add a sentinel null OSType to the data handle. |
BuilderNewPref(state, 0, nil, 0); |
// Return the latched error code. |
if (state->prefData == nil) { |
err = state->latchedError; |
} else { |
err = noErr; |
} |
*prefData = state->prefData; |
MoreAssertQ( err == noErr && *prefData != nil || err != noErr && *prefData == nil ); |
return err; |
} |
static void WriterGetNextPref(ByteCount *cookie, Ptr prefData, OSType *prefType, void **prefPtr, ByteCount *prefSize) |
// This routine provides a simple way for you to iterate over |
// a group of packed preferences. You get the first preference |
// by setting *cookie to 0 and calling the routine. The routine |
// returns the preference, and updates *cookie so that next time |
// you call it you get the second preference, and so on. |
// |
// prefData must point to a packed preference structure, as described |
// in the comments at the start of this section. Typically you get |
// this by locking and dereferencing the handle returned by BuilderDone. |
// [Locking the handle is advised by not strictly necessary as |
// long as you take care not to move or purge between when this routine |
// returns and when you use *prefPtr.] You don't have to pass in |
// the size of this handle because it's terminated by a null preference |
// type. You can keep calling this routine until *prefType is returned |
// as 0, in which case you've hit the end of the packed preferences. |
// |
// The routine sets *prefType to the type of the preference, and |
// *prefPtr and *prefSize to point to the preference data itself. |
// Note that *prefPtr is not necessarily aligned to any memory |
// boundary, so if you access it as a word or long pointer on an |
// original 68000 you may cause an address error. |
{ |
MoreAssertQ(cookie != nil); |
MoreAssertQ(prefData != nil); |
MoreAssertQ(prefType != nil); |
MoreAssertQ(prefSize != nil); |
MoreAssertQ(prefPtr != nil); |
*prefType = *((OSType *)(prefData + *cookie)); |
*cookie += sizeof(OSType); // skip cookie past the prefType |
*prefSize = *((ByteCount *)(prefData + *cookie)); |
*cookie += sizeof(ByteCount); // skip cookie past the prefSize |
*prefPtr = prefData + *cookie; |
*cookie += *prefSize; // skip cookie past the data |
} |
static Handle NSHGetDefaultPreference(ResType entityType, ResType entityClass, ResType recordType) |
// Returns a handle containing a default value for the preference |
// of the given entity, class and preference type, or nil if there |
// is not such value (or there isn't enough memory to get it). |
// The result is a memory handle, for which you are responsible for |
// disposing. |
// |
// I decided not to export this routine because of its limited value |
// to external clients. But it follows the standard outline used |
// by external routines. |
{ |
Handle result; |
if ( kUseNetworkSetup && IsNetworkSetupAvailable() ) { |
#if TARGET_RT_MAC_CFM |
result = OTCfgGetDefault(entityType, entityClass, recordType); |
#else |
// Network Setup has no Mixed Mode glue. When running |
// code on a PowerPC with Network Setup available, you |
// should either compile your code as Fat or, if that's |
// infeasible, write your own Mixed Mode glue. |
return nil; |
#endif |
} else { |
if ( entityType == kOTCfgTypeRemote && entityClass == kOTCfgClassNetworkConnection && recordType == kOTCfgRemoteLCPPref) { |
if ( PtrToHand(gRemoteLCPValue, &result, sizeof(gRemoteLCPValue)) != noErr ) { |
result = nil; |
} |
} else { |
result = nil; |
} |
} |
return result; |
} |
///////////////////////////////////////////////////////////////// |
#pragma mark ----- TCP/IP Packer/Unpacker ------ |
// These routines convert between the TCP/IP configuration digest |
// (a big record with all the relevant fields in it) and the |
// packed preferences data (which is read from or written to the |
// preferences store). These routines are shared between the |
// preferences database and preferences file implementations. |
static const char *gProtocolName = "tcp"; |
static OSStatus BuildPackedPrefsFromTCPv4ConfigurationDigest( |
const NSHTCPv4ConfigurationDigest *configurationDigest, |
Boolean forceDefaults, |
Handle *packedPrefs) |
// This routine converts a TCP/IP configuration digest |
// to a handle containing packed preferences. The forceDefaults |
// parameter controls what happens to handle-based fields, |
// ie Handle fields in the digest. Normally, if such a field |
// is nil, it's taken to mean "don't change". However, if |
// forceDefaults is true, a nil means "write default value". |
// This happens when we're creating a new configuration, |
// where we want to make sure that empty default preferences |
// are created for these handle-based fields. |
{ |
OSStatus err; |
OTPortRecord portRec; |
Str255 portUserVisibleName; |
Boolean isMacIP; |
Str63 portName; |
Str63 moduleName; |
UInt8 tmpUInt8; |
UInt16 tmpUInt16; |
const char *kMacIPUserVisibleName = "AppleTalk (Mac IP)"; |
static UInt8 zeros[25] = {0}; |
PrefBuilderState state; |
SInt8 s; |
MoreAssertQ(configurationDigest != nil); |
MoreAssertQ(packedPrefs != nil); |
*packedPrefs = nil; |
err = noErr; |
if ( OTFindPortByRef(&portRec, configurationDigest->fPortRef) ) { |
portName[0] = OTStrLength(portRec.fPortName); |
BlockMoveData(portRec.fPortName, &portName[1], portName[0]); |
moduleName[0] = OTStrLength(portRec.fModuleName); |
BlockMoveData(portRec.fModuleName, &moduleName[1], moduleName[0]); |
} else { |
err = kOTNotFoundErr; |
} |
if (err == noErr) { |
BuilderNew(&state); |
// 'pnam' |
BuilderNewPref(&state, kOTCfgUserVisibleNamePref, configurationDigest->fConfigName, configurationDigest->fConfigName[0] + 1); |
// 'port' |
isMacIP = OTStrEqual(portRec.fModuleName, "ddp"); |
if (isMacIP) { |
portUserVisibleName[0] = OTStrLength(kMacIPUserVisibleName); |
BlockMoveData(kMacIPUserVisibleName, &portUserVisibleName[1], portUserVisibleName[0]); |
} else { |
OTGetUserPortNameFromPortRef(configurationDigest->fPortRef, portUserVisibleName); |
} |
BuilderNewPref(&state, kOTCfgPortUserVisibleNamePref, portUserVisibleName, portUserVisibleName[0] + 1); |
// 'pwrd' |
tmpUInt8 = 0; |
BuilderNewPref(&state, kOTCfgAdminPasswordPref, &tmpUInt8, sizeof(tmpUInt8)); |
// 'cvrs' |
tmpUInt16 = 1; |
BuilderNewPref(&state, kOTCfgVersionPref, &tmpUInt16, sizeof(tmpUInt16)); |
// 'prot' |
BuilderNewPref(&state, kOTCfgProtocolUserVisibleNamePref, gProtocolName, OTStrLength(gProtocolName) + 1); |
// 'unld' |
BuilderNewPref(&state, kOTCfgTCPUnloadAttrPref, &configurationDigest->fUnloadAttr, sizeof(configurationDigest->fUnloadAttr)); |
// 'idns' |
if (configurationDigest->fDNSServerList != nil) { |
UInt16 numServers; |
MoreAssertQ(GetHandleSize(configurationDigest->fDNSServerList) % sizeof(InetHost) == 0); |
numServers = GetHandleSize(configurationDigest->fDNSServerList) / sizeof(InetHost); |
BuilderNewPref(&state, kOTCfgTCPDNSServersListPref, &numServers, sizeof(numServers)); |
s = HGetState(configurationDigest->fDNSServerList); |
HLock(configurationDigest->fDNSServerList); |
BuilderAddPrefData(&state, *configurationDigest->fDNSServerList, GetHandleSize(configurationDigest->fDNSServerList)); |
HSetState(configurationDigest->fDNSServerList, s); |
} else if (forceDefaults) { |
tmpUInt16 = 0; |
BuilderNewPref(&state, kOTCfgTCPDNSServersListPref, &tmpUInt16, sizeof(tmpUInt16)); |
} |
// 'ihst' |
tmpUInt8 = 1; |
BuilderNewPref(&state, kOTCfgTCPSearchListPref, &tmpUInt8, sizeof(tmpUInt8)); |
BuilderAddPrefData(&state, configurationDigest->fLocalDomain, configurationDigest->fLocalDomain[0] + 1); |
BuilderAddPrefData(&state, configurationDigest->fAdminDomain, configurationDigest->fAdminDomain[0] + 1); |
// 'iitf' |
tmpUInt16 = 1; |
BuilderNewPref(&state, kOTCfgTCPInterfacesPref, &tmpUInt16, sizeof(tmpUInt16)); |
BuilderAddPrefData(&state, &configurationDigest->fConfigMethod, sizeof(configurationDigest->fConfigMethod)); |
BuilderAddPrefData(&state, &configurationDigest->fIPAddress, sizeof(configurationDigest->fIPAddress)); |
BuilderAddPrefData(&state, &configurationDigest->fSubnetMask, sizeof(configurationDigest->fSubnetMask)); |
if (isMacIP) { |
BuilderAddPrefData(&state, configurationDigest->fAppleTalkZone, configurationDigest->fAppleTalkZone[0] + 1); |
BuilderAddPrefData(&state, "\pddp", 36); |
} else { |
BuilderAddPrefData(&state, "\p*", 2); |
BuilderAddPrefData(&state, portName, 36); |
} |
BuilderAddPrefData(&state, moduleName, 32); |
BuilderAddPrefData(&state, &configurationDigest->fFraming, sizeof(configurationDigest->fFraming)); |
// 'dtyp' |
if (isMacIP) { |
tmpUInt16 = kOTNoDeviceType; |
} else { |
tmpUInt16 = OTGetDeviceTypeFromPortRef(configurationDigest->fPortRef); |
} |
BuilderNewPref(&state, kOTCfgTCPDeviceTypePref, &tmpUInt16, sizeof(tmpUInt16)); |
// 'stng' |
BuilderNewPref(&state, kOTCfgTCPLocksPref, zeros, sizeof(zeros)); |
// 'isdm' |
if (configurationDigest->fSearchDomains != nil) { |
s = HGetState(configurationDigest->fSearchDomains); |
HLock(configurationDigest->fSearchDomains); |
BuilderNewPref(&state, kOTCfgTCPSearchDomainsPref, *configurationDigest->fSearchDomains, GetHandleSize(configurationDigest->fSearchDomains)); |
HSetState(configurationDigest->fSearchDomains, s); |
} else if (forceDefaults) { |
tmpUInt16 = 0; |
BuilderNewPref(&state, kOTCfgTCPSearchDomainsPref, &tmpUInt16, sizeof(tmpUInt16)); |
} |
// 'irte' |
if (configurationDigest->fRouterList != nil || forceDefaults) { |
UInt16 numRouters; |
UInt16 routerIndex; |
if (configurationDigest->fRouterList == nil) { |
numRouters = 0; |
} else { |
MoreAssertQ(GetHandleSize(configurationDigest->fRouterList) % sizeof(InetHost) == 0); |
numRouters = GetHandleSize(configurationDigest->fRouterList) / sizeof(InetHost); |
} |
BuilderNewPref(&state, kOTCfgTCPRoutersListPref, &numRouters, sizeof(numRouters)); |
for (routerIndex = 0; routerIndex < numRouters; routerIndex++) { |
OTCfgTCPRoutersListEntry entry; |
entry.fToHost = 0; |
entry.fViaHost = (*((InetHost **) configurationDigest->fRouterList))[routerIndex]; |
entry.fLocal = 0; |
entry.fHost = 0; |
BuilderAddPrefData(&state, &entry, sizeof(entry)); |
} |
} |
err = BuilderDone(&state, packedPrefs); |
} |
return err; |
} |
static Boolean UnpackTCPSearchList(const UInt8 *data, ByteCount length, NSHTCPv4ConfigurationDigest *configurationDigest) |
// This routine is used to unpacked the TCP search list preference |
// (kOTCfgTCPSearchListPref == 'ihst'). The preference is a weird |
// combination of packed strings and straight data, so we have to |
// mess around a bit. The routine returns true if it could |
// parse the data successfully; false if it found a formatting |
// error in the data. |
{ |
const UInt8 *cursor; |
UInt8 primaryInterfaceIndex; |
MoreAssertQ(data != nil); |
MoreAssertQ(configurationDigest != nil); |
cursor = data; |
if (cursor + sizeof(SInt8) <= data + length) { |
primaryInterfaceIndex = *cursor; |
cursor += sizeof(SInt8); |
} |
if (cursor + *cursor + 1 <= data + length) { |
BlockMoveData(cursor, configurationDigest->fLocalDomain, *cursor + 1); |
cursor += (*cursor + 1); |
} |
if (cursor + *cursor + 1 <= data + length) { |
BlockMoveData(cursor, configurationDigest->fAdminDomain, *cursor + 1); |
cursor += (*cursor + 1); |
} |
return (primaryInterfaceIndex == 1) && (cursor == data + length); |
} |
static void UnpackTCPPrefs(Ptr *buffer, NSHTCPv4ConfigurationDigest *configurationDigest) |
// This routine unpacks an interface from an 'iitf' (kOTCfgTCPInterfacesPref) |
// preference into the relevant fields of a NSHTCPv4ConfigurationDigest. |
// *buffer must point to the beginning |
// of the interface, ie two bytes into the pref data if |
// if you're extracting the first interface. *buffer |
// is updated to point to the byte after the last byte |
// parsed, so you can parse multiple interfaces by |
// repeatedly calling this routine. You can also |
// check *buffer to determine whether the routine |
// consumed all the data you expected, and hence whether |
// the preference is formatted correctly. |
{ |
UInt8 *cursor; |
cursor = (UInt8 *) *buffer; |
configurationDigest->fConfigMethod = *cursor; |
cursor += sizeof(UInt8); |
configurationDigest->fIPAddress = *((InetHost *) cursor); |
cursor += sizeof(InetHost); |
configurationDigest->fSubnetMask = *((InetHost *) cursor); |
cursor += sizeof(InetHost); |
// fAppleTalkZone is a Str32. A longer string in the |
// 'iitf' is a bug in the person who wrote the code and |
// causes us to stop parsing. The caller will notice that |
// the cursor did not advance far enough and error out. |
if ( *cursor <= 32 ) { |
BlockMoveData(cursor, configurationDigest->fAppleTalkZone, *cursor + 1); |
cursor += (*cursor + 1); |
BlockMoveData(cursor, configurationDigest->fHintPortName, 36); |
cursor += 36; |
BlockMoveData(cursor, configurationDigest->fHintDriverName, 32); |
cursor += 32; |
configurationDigest->fFraming = *((UInt32 *) cursor); |
cursor += sizeof(UInt32); |
} |
*buffer = (Ptr) cursor; |
} |
static Boolean PortMatchesTCPv4Hints(const OTPortRecord *portRec, const NSHTCPv4ConfigurationDigest *hints) |
// This routine checks whether a particular port matches the set |
// of hints extracted from the TCP/IP preferences, and returns true |
// if it does. The hints include: |
// |
// a) the user-visible name of the port, |
// b) the port name itself, |
// c) the name of the module controlling the part, and |
// d) the device type of the module controlling the port. |
{ |
Str255 userVisibleName; |
OTGetUserPortNameFromPortRef(portRec->fRef, userVisibleName); |
return OTMemcmp(userVisibleName, hints->fHintUserVisiblePortName, userVisibleName[0] + 1) |
&& OTStrEqual(portRec->fPortName, ((char *) hints->fHintPortName) + 1) |
&& OTStrEqual(portRec->fModuleName, ((char *) hints->fHintDriverName) + 1) |
&& OTGetDeviceTypeFromPortRef(portRec->fRef) == hints->fHintDeviceType; |
} |
static void ClearTCPv4ConfigurationDigest(NSHTCPv4ConfigurationDigest *configurationDigest) |
// Clear out the entire parameter block, preserving the handle-based fields. |
{ |
Handle saveRouterList; |
Handle saveDNSServerList; |
Handle saveSearchDomains; |
saveRouterList = configurationDigest->fRouterList; |
saveDNSServerList = configurationDigest->fDNSServerList; |
saveSearchDomains = configurationDigest->fSearchDomains; |
OTMemzero(configurationDigest, sizeof(configurationDigest)); |
configurationDigest->fRouterList = saveRouterList; |
configurationDigest->fDNSServerList = saveDNSServerList; |
configurationDigest->fSearchDomains = saveSearchDomains; |
} |
static OSStatus BuildTCPv4ConfigurationDigestFromPackedPrefs(Handle packedPrefs, |
NSHTCPv4ConfigurationDigest *configurationDigest) |
// This routine fills out a TCP/IP configuration digest |
// based on the packed preferences. The basic algorithm |
// is to iterate through all the preferences, looking at |
// each one and putting the data from that preference into |
// the configuration digest. At the end it does some jiggery |
// pokery that's explained in the comment down there. |
{ |
OSStatus err; |
SInt8 s; |
ByteCount cookie; |
OSType prefType; |
ByteCount prefSize; |
void *prefData; |
UInt16 numServers; |
UInt16 numRouters; |
UInt16 routerIndex; |
OTPortRecord portRec; |
MoreAssertQ(packedPrefs != nil); |
MoreAssertQ(configurationDigest != nil); |
ClearTCPv4ConfigurationDigest(configurationDigest); |
configurationDigest->fProtocol = kOTCfgTypeTCPv4; |
s = HGetState(packedPrefs); |
HLock(packedPrefs); |
err = noErr; |
cookie = 0; |
do { |
WriterGetNextPref(&cookie, *packedPrefs, &prefType, &prefData, &prefSize); |
switch (prefType) { |
case 0: |
// do nothing, this is the loop exit condition |
break; |
// 'pnam' |
case kOTCfgUserVisibleNamePref: |
if ( prefSize <= sizeof(Str255) && prefSize == ((UInt8 *)prefData)[0] + 1 ) { |
BlockMoveData(prefData, configurationDigest->fConfigName, sizeof(Str255)); |
} else { |
err = -8; |
} |
break; |
// 'port' |
case kOTCfgPortUserVisibleNamePref: |
if ( prefSize <= sizeof(Str255) && prefSize == ((UInt8 *)prefData)[0] + 1 ) { |
BlockMoveData(prefData, configurationDigest->fHintUserVisiblePortName, sizeof(Str255)); |
} else { |
err = -8; |
} |
break; |
// 'pwrd' |
case kOTCfgAdminPasswordPref: |
// do nothing, we can ignore password prefs |
break; |
// 'cvrs' |
case kOTCfgVersionPref: |
// do nothing, except check that it's what we expect |
if ( prefSize != sizeof(UInt16) || *((UInt16 *)prefData) != 1) { |
err = -8; |
} |
break; |
// 'prot' |
case kOTCfgProtocolUserVisibleNamePref: |
// do nothing, except check it's TCP/IP |
if ( prefSize != OTStrLength(prefData) + 1 || ! OTStrEqual(prefData, gProtocolName) ) { |
err = -8; |
} |
break; |
// 'unld' |
case kOTCfgTCPUnloadAttrPref: |
configurationDigest->fUnloadAttr = *((UInt16 *)prefData); |
if ( prefSize != sizeof(UInt16) |
|| configurationDigest->fUnloadAttr < kOTCfgTCPActiveLoadedOnDemand |
|| configurationDigest->fUnloadAttr > kOTCfgTCPInactive ) { |
err = -8; |
} |
break; |
// 'idns' |
case kOTCfgTCPDNSServersListPref: |
numServers = *((UInt16 *)prefData); |
if ( prefSize == sizeof(UInt16) + numServers * sizeof(InetHost) ) { |
if (configurationDigest->fDNSServerList != nil) { |
err = PtrToXHand( ((char *) prefData) + sizeof(UInt16), configurationDigest->fDNSServerList, prefSize - sizeof(UInt16)); |
} |
} else { |
err = -8; |
} |
break; |
// 'ihst' |
case kOTCfgTCPSearchListPref: |
if ( ! UnpackTCPSearchList(prefData, prefSize, configurationDigest) ) { |
err = -8; |
} |
break; |
// 'iitf' |
case kOTCfgTCPInterfacesPref: |
if ( prefSize >= sizeof(UInt16) && *((UInt16 *)prefData) == 1 ) { |
Ptr cursor; |
cursor = (Ptr) prefData; |
cursor += sizeof(UInt16); |
UnpackTCPPrefs(&cursor, configurationDigest); |
if ( cursor != ((Ptr) prefData) + prefSize ) { |
err = -8; |
} |
} else { |
err = -8; |
} |
break; |
// 'dtyp' |
case kOTCfgTCPDeviceTypePref: |
if (prefSize == sizeof(SInt16)) { |
configurationDigest->fHintDeviceType = *((UInt16 *)prefData); |
} else { |
err = -8; |
} |
break; |
// 'stng' |
case kOTCfgTCPLocksPref: |
// The pref should be of size 25, but I've seen |
// cases where it was set to 27. I have no idea |
// what causes this, but seeing as the size (or |
// indeed the contents) doesn't matter for this |
// program, I decided to handle that case. |
if (prefSize != 25 && prefSize != 27) { |
err = -8; |
} |
break; |
// 'isdm' |
case kOTCfgTCPSearchDomainsPref: |
if (prefSize >= sizeof(UInt16)) { |
if ( configurationDigest->fSearchDomains != nil ) { |
err = PtrToXHand(prefData, configurationDigest->fSearchDomains, prefSize); |
if (err == noErr) { |
if ( ! ValidStringListHandle( configurationDigest->fSearchDomains ) ) { |
err = -8; |
} |
} |
} |
} else { |
err = -8; |
} |
break; |
// 'irte' |
case kOTCfgTCPRoutersListPref: |
numRouters = *((UInt16 *)prefData); |
if ( prefSize == sizeof(UInt16) + numRouters * sizeof(OTCfgTCPRoutersListEntry) ) { |
if (configurationDigest->fRouterList != nil) { |
SetHandleSize(configurationDigest->fRouterList, numRouters * sizeof(InetHost)); |
err = MemError(); |
if (err == noErr) { |
OTCfgTCPRoutersList *prefRouterList; |
InetHost *digestRouterList; |
prefRouterList = (OTCfgTCPRoutersList *) prefData; |
digestRouterList = (InetHost *) *configurationDigest->fRouterList; |
for (routerIndex = 0; routerIndex < numRouters; routerIndex++) { |
digestRouterList[routerIndex] = prefRouterList->fList[routerIndex].fViaHost; |
// Should really check that the other fields of |
// the OTCfgTCPRoutersListEntry are zero, but I can't be bothered |
// right now. |
} |
} |
} |
} else { |
err = -8; |
} |
break; |
case kOTCfgUserModePref: // user level |
case kOTCfgPrefWindowPositionPref: // control panel window position |
case kOTCfgTCPDHCPClientIDPref: // DHCP client ID |
case kOTCfgTCPDHCPLeaseInfoPref: // DHCP persistent state |
case kOTCfgTCPPushBelowIPPref: // below IP module |
case kOTCfgTCPPushBelowIPListPref: // below IP module list (OT 2.5 and higher) |
case kOTCfgProtocolOptionsPref: // wacky protocol options |
case 'vers': // occasionally you find these in TCP/IP configs, although they shouldn't be there |
// Known preferences which we don't need to pay |
// attention to but shouldn't cause the following |
// assert to trigger. |
break; |
default: |
// Unexpected preference type in the packed prefs. |
// This is not super-fatal, but let the developer know in debug builds. |
MoreAssertQ(false); |
break; |
} |
} while (err == noErr && prefType != 0); |
HSetState(packedPrefs, s); |
// Before we leave, we have to set up the fPortRef field of the |
// configuration digest. This is a tricky business because the |
// port that we last used might have been removed, or ejected, |
// and replaced by another remarkably similar port. So we |
// only set up fPortRef is *all* of the hints we extracted from |
// the preferences match the port we found. Otherwise, we |
// leave fPortRef set to 0 and expect the client to do the |
// matching based on the hints. |
// |
// Except, of course, for MacIP, where the algorithm is completely |
// different, as always. |
if (err == noErr) { |
// Put a null byte after the last byte of these two hints. |
// We know we have the space because they're declared as |
// Str63s. We need this for later string comparisons, |
// both here and in PortMatchesTCPv4Hints. |
configurationDigest->fHintPortName[configurationDigest->fHintPortName[0] + 1] = 0; |
configurationDigest->fHintDriverName[configurationDigest->fHintDriverName[0] + 1] = 0; |
// Translation of the below: |
// |
// if the driver name was "ddp" and AppleTalk is active, |
// return the port ref of the active AppleTalk port |
// else if we have a port which exactly matches all the hints, |
// return the port ref of that port |
// else |
// return 0 |
// end-if |
if ( ( OTStrEqual(((char *) configurationDigest->fHintDriverName) + 1, "ddp") |
&& OTFindPort(&portRec, "ddp1") ) |
|| ( OTFindPort(&portRec, ((char *) configurationDigest->fHintPortName) + 1) |
&& PortMatchesTCPv4Hints(&portRec, configurationDigest) ) ) { |
configurationDigest->fPortRef = portRec.fRef; |
} else { |
configurationDigest->fPortRef = kOTInvalidPortRef; |
} |
} |
return err; |
} |
///////////////////////////////////////////////////////////////// |
#pragma mark ----- Remote Access Packer/Unpacker ------ |
// These routines convert between the Remote Access configuration digest |
// (a big record with all the relevant fields in it) and the |
// packed preferences data (which is read from or written to the |
// preferences store). These routines are shared between the |
// preferences database and preferences file implementations. |
static Boolean RemoteVersionAcceptable(UInt32 version) |
// This routine returns true if the version number passed |
// in indicates that the preference was written by a |
// version of Remote Access whose preferences we understand. |
{ |
return version == kOTCfgRemoteDefaultVersion || version == kOTCfgRemoteAcceptedVersion; |
} |
static OSStatus BuildPackedPrefsFromRemoteConfigurationDigest( |
const NSHRemoteConfigurationDigest *configurationDigest, |
Boolean forceDefaults, |
Handle *packedPrefs) |
// This routine converts a Remote Access configuration digest |
// to a handle containing packed preferences. |
// |
// forceDefaults is not needed for Remote Access because the |
// only handle-based field is an optional preference. |
{ |
#pragma unused(forceDefaults) |
OSStatus err; |
PrefBuilderState state; |
UInt8 buffer[600]; |
UInt32 tmpUInt32; |
Str255 encodedPassword; |
Handle prefH; |
MoreAssertQ(configurationDigest != nil); |
MoreAssertQ(packedPrefs != nil); |
*packedPrefs = nil; |
err = noErr; |
if (err == noErr) { |
BuilderNew(&state); |
// 'pnam' |
BuilderNewPref(&state, kOTCfgUserVisibleNamePref, configurationDigest->fConfigName, configurationDigest->fConfigName[0] + 1); |
// 'conn' |
{ |
OTCfgRemoteConnect *connectPtr; |
OTMemzero(buffer, sizeof(buffer)); |
MoreAssertQ(sizeof(buffer) >= sizeof(OTCfgRemoteConnect)); |
connectPtr = (OTCfgRemoteConnect *) buffer; |
connectPtr->version = kOTCfgRemoteDefaultVersion; |
connectPtr->isGuest = configurationDigest->fGuestLogin; |
connectPtr->canInteract = true; |
connectPtr->passwordSaved = configurationDigest->fPasswordValid; |
connectPtr->flashConnectedIcon = configurationDigest->fFlashIconWhileConnected; |
connectPtr->issueConnectedReminders = configurationDigest->fPromptToRemainConnected; |
connectPtr->reminderMinutes = configurationDigest->fPromptInterval; |
connectPtr->allowModemDataCompression = configurationDigest->fPPPAllowModemCompression; |
connectPtr->chatMode = configurationDigest->fPPPConnectMode; |
connectPtr->serialProtocolMode = configurationDigest->fSerialProtocol; |
connectPtr->addressLength = configurationDigest->fPhoneNumber[0]; |
BlockMoveData(configurationDigest->fPPPConnectScriptName, connectPtr->chatScriptName, sizeof(Str63)); |
if ( configurationDigest->fPPPConnectScript != nil ) { |
connectPtr->chatScriptLength = GetHandleSize(configurationDigest->fPPPConnectScript); |
} |
BuilderNewPref(&state, kOTCfgRemoteConnectPref, connectPtr, sizeof(*connectPtr)); |
} |
// 'cusr' |
BuilderNewPref(&state, kOTCfgRemoteUserPref, configurationDigest->fUserName, configurationDigest->fUserName[0] + 1); |
// 'pass' |
err = NSHEncodeRemotePassword(configurationDigest->fUserName, configurationDigest->fPassword, encodedPassword); |
if (err == noErr) { |
BuilderNewPref(&state, kOTCfgRemotePasswordPref, encodedPassword, sizeof(Str255)); |
} else { |
BuilderError(&state, err); |
} |
// 'cadr' |
BuilderNewPref(&state, kOTCfgRemoteAddressPref, &configurationDigest->fPhoneNumber[1], configurationDigest->fPhoneNumber[0]); |
// 'cdia' |
{ |
OTCfgRemoteDialing *dialPtr; |
OTMemzero(buffer, sizeof(buffer)); |
MoreAssertQ(sizeof(buffer) >= sizeof(OTCfgRemoteDialing)); |
dialPtr = (OTCfgRemoteDialing *) buffer; |
dialPtr->version = kOTCfgRemoteDefaultVersion; |
dialPtr->fType = 'dial'; |
dialPtr->dialMode = configurationDigest->fRedialMode; |
dialPtr->redialTries = configurationDigest->fRedialTimes; |
dialPtr->redialDelay = configurationDigest->fRedialDelay; |
BuilderNewPref(&state, kOTCfgRemoteDialingPref, dialPtr, sizeof(*dialPtr)); |
} |
// 'cead' |
if ( configurationDigest->fRedialMode == kOTCfgRemoteRedialMainAndAlternate ) { |
tmpUInt32 = 0; |
BuilderNewPref(&state, kOTCfgRemoteAlternateAddressPref, &tmpUInt32, sizeof(tmpUInt32)); |
BuilderAddPrefData(&state, configurationDigest->fAlternatePhoneNumber, sizeof(Str255)); |
} |
// 'logo' |
{ |
OTCfgRemoteLogOptions *logPtr; |
OTMemzero(buffer, sizeof(buffer)); |
MoreAssertQ(sizeof(buffer) >= sizeof(OTCfgRemoteLogOptions)); |
logPtr = (OTCfgRemoteLogOptions *) buffer; |
logPtr->version = kOTCfgRemoteDefaultVersion; |
logPtr->fType = 'lgop'; |
logPtr->logLevel = configurationDigest->fVerboseLogging; |
BuilderNewPref(&state, kOTCfgRemoteLogOptionsPref, logPtr, sizeof(*logPtr)); |
} |
// 'ipcp' |
{ |
OTCfgRemoteIPCP *ipcpPtr; |
OTMemzero(buffer, sizeof(buffer)); |
MoreAssertQ(sizeof(buffer) >= sizeof(OTCfgRemoteIPCP)); |
ipcpPtr = (OTCfgRemoteIPCP *) buffer; |
ipcpPtr->version = kOTCfgRemoteDefaultVersion; |
ipcpPtr->reserved[0] = 'ipcp'; |
ipcpPtr->maxConfig = 10; |
ipcpPtr->maxTerminate = 10; |
ipcpPtr->maxFailureLocal = 10; |
ipcpPtr->maxFailureRemote = 10; |
ipcpPtr->timerPeriod = 10000; |
ipcpPtr->localIPAddress = 0; |
ipcpPtr->remoteIPAddress = 0; |
ipcpPtr->allowAddressNegotiation = 1; |
ipcpPtr->idleTimerEnabled = configurationDigest->fDisconnectIfIdle; |
ipcpPtr->compressTCPHeaders = configurationDigest->fPPPAllowTCPIPHeaderCompression; |
ipcpPtr->idleTimerMilliseconds = configurationDigest->fDisconnectInterval; |
BuilderNewPref(&state, kOTCfgRemoteIPCPPref, ipcpPtr, sizeof(*ipcpPtr)); |
} |
// 'cmsc' |
tmpUInt32 = kOTCfgRemoteDefaultVersion; |
BuilderNewPref(&state, kOTCfgRemoteClientMiscPref, &tmpUInt32, sizeof(tmpUInt32)); |
tmpUInt32 = configurationDigest->fPPPConnectAutomatically; |
BuilderAddPrefData(&state, &tmpUInt32, sizeof(tmpUInt32)); |
// 'ccha' |
if ( configurationDigest->fPPPConnectScript != nil ) { |
SInt8 s; |
s = HGetState(configurationDigest->fPPPConnectScript); |
HLock(configurationDigest->fPPPConnectScript); |
BuilderNewPref(&state, kOTCfgRemoteChatPref, *configurationDigest->fPPPConnectScript, GetHandleSize(configurationDigest->fPPPConnectScript)); |
HSetState(configurationDigest->fPPPConnectScript, s); |
} |
// 'lcp ' |
prefH = NSHGetDefaultPreference(kOTCfgTypeRemote, kOTCfgClassNetworkConnection, kOTCfgRemoteLCPPref); |
if (prefH != nil) { |
MoreAssertQ(GetHandleSize(prefH) == sizeof(gRemoteLCPValue)); |
HLock(prefH); |
MoreAssertQ(MemError() == noErr); |
BuilderNewPref(&state, kOTCfgRemoteLCPPref, *prefH, GetHandleSize(prefH)); |
DisposeHandle(prefH); |
MoreAssertQ(MemError() == noErr); |
} else { |
BuilderError(&state, resNotFound); |
} |
// 'arap' |
tmpUInt32 = kOTCfgRemoteDefaultVersion; |
BuilderNewPref(&state, kOTCfgRemoteARAPPref, &tmpUInt32, sizeof(tmpUInt32)); |
OTMemzero(buffer, sizeof(buffer)); |
OTStrCopy( (char *) buffer, "Script"); |
BuilderAddPrefData(&state, buffer, 36); |
// 'x25 ' |
{ |
OTCfgRemoteX25 *x25Ptr; |
OTMemzero(buffer, sizeof(buffer)); |
MoreAssertQ(sizeof(buffer) >= sizeof(OTCfgRemoteX25)); |
x25Ptr = (OTCfgRemoteX25 *) buffer; |
x25Ptr->version = kOTCfgRemoteDefaultVersion; |
BuilderNewPref(&state, kOTCfgRemoteX25Pref, x25Ptr, sizeof(*x25Ptr)); |
} |
// 'dass' |
tmpUInt32 = kOTCfgRemoteDefaultVersion; |
BuilderNewPref(&state, kOTCfgRemoteDialAssistPref, &tmpUInt32, sizeof(tmpUInt32)); |
OTMemzero(buffer, sizeof(buffer)); |
BuilderAddPrefData(&state, buffer, 68); |
// 'usmd' |
tmpUInt32 = kOTCfgRemoteDefaultVersion; |
BuilderNewPref(&state, kOTCfgRemoteUserModePref, &tmpUInt32, sizeof(tmpUInt32)); |
tmpUInt32 = 0x00000001; |
BuilderAddPrefData(&state, &tmpUInt32, sizeof(tmpUInt32)); |
OTMemzero(buffer, sizeof(buffer)); |
BuilderAddPrefData(&state, buffer, 256); |
// 'clks' |
tmpUInt32 = kOTCfgRemoteDefaultVersion; |
BuilderNewPref(&state, kOTCfgRemoteClientLocksPref, &tmpUInt32, sizeof(tmpUInt32)); |
OTMemzero(buffer, sizeof(buffer)); |
BuilderAddPrefData(&state, buffer, 64); |
err = BuilderDone(&state, packedPrefs); |
} |
return err; |
} |
static void ClearRemoteConfigurationDigest(NSHRemoteConfigurationDigest *configurationDigest) |
// Clear out the entire parameter block, preserving the handle-based fields. |
{ |
Handle savePPPConnectScript; |
savePPPConnectScript = configurationDigest->fPPPConnectScript; |
OTMemzero(configurationDigest, sizeof(configurationDigest)); |
configurationDigest->fPPPConnectScript = savePPPConnectScript; |
} |
static OSStatus BuildRemoteConfigurationDigestFromPackedPrefs(Handle packedPrefs, |
NSHRemoteConfigurationDigest *configurationDigest) |
// This routine fills out a Remote Access configuration digest |
// based on the packed preferences. The basic algorithm |
// is to iterate through all the preferences, looking at |
// each one and putting the data from that preference into |
// the configuration digest. |
{ |
OSStatus err; |
SInt8 s; |
ByteCount cookie; |
OSType prefType; |
ByteCount prefSize; |
void *prefData; |
MoreAssertQ(packedPrefs != nil); |
MoreAssertQ(configurationDigest != nil); |
ClearRemoteConfigurationDigest(configurationDigest); |
configurationDigest->fProtocol = kOTCfgTypeRemote; |
s = HGetState(packedPrefs); |
HLock(packedPrefs); |
err = noErr; |
cookie = 0; |
do { |
WriterGetNextPref(&cookie, *packedPrefs, &prefType, &prefData, &prefSize); |
switch (prefType) { |
case 0: |
// do nothing, this is the loop exit condition |
break; |
// 'pnam' |
case kOTCfgUserVisibleNamePref: |
if ( prefSize <= sizeof(Str255) && prefSize == ((UInt8 *)prefData)[0] + 1 ) { |
BlockMoveData(prefData, configurationDigest->fConfigName, sizeof(Str255)); |
} else { |
err = -8; |
} |
break; |
// 'cmsc' -> fPPPConnectAutomatically |
case kOTCfgRemoteClientMiscPref: |
if ( prefSize == 8 && RemoteVersionAcceptable(((UInt32 *) prefData)[0]) ) { |
configurationDigest->fPPPConnectAutomatically = ((UInt32 *) prefData)[1]; |
} else { |
err = -8; |
} |
break; |
// 'conn' -> fGuestLogin, fPasswordValid, fPhoneNumber, fFlashIconWhileConnected, fPromptToRemainConnected, fPromptInterval, fSerialProtocol, fPPPAllowModemCompression, fPPPConnectMode, fPPPConnectScriptName, fPPPConnectScript |
case kOTCfgRemoteConnectPref: |
if ( prefSize == sizeof(OTCfgRemoteConnect) && RemoteVersionAcceptable(((UInt32 *) prefData)[0]) ) { |
OTCfgRemoteConnect *configPtr; |
configPtr = (OTCfgRemoteConnect *) prefData; |
configurationDigest->fGuestLogin = configPtr->isGuest; |
configurationDigest->fPasswordValid = configPtr->passwordSaved; |
configurationDigest->fFlashIconWhileConnected = configPtr->flashConnectedIcon; |
configurationDigest->fPromptToRemainConnected = configPtr->issueConnectedReminders; |
configurationDigest->fPromptInterval = configPtr->reminderMinutes; |
configurationDigest->fSerialProtocol = configPtr->serialProtocolMode; |
configurationDigest->fPPPAllowModemCompression = configPtr->allowModemDataCompression; |
configurationDigest->fPPPConnectMode = configPtr->chatMode; |
BlockMoveData(configPtr->chatScriptName, configurationDigest->fPPPConnectScriptName, sizeof(Str63)); |
} else { |
err = -8; |
} |
break; |
// 'cusr' -> fUserName |
case kOTCfgRemoteUserPref: |
if ( prefSize <= sizeof(Str255) && prefSize == ((UInt8 *)prefData)[0] + 1 ) { |
BlockMoveData(prefData, configurationDigest->fUserName, sizeof(Str255)); |
} else { |
err = -8; |
} |
break; |
// 'pass' -> fPassword |
case kOTCfgRemotePasswordPref: |
if ( prefSize == sizeof(Str255) ) { |
BlockMoveData(prefData, configurationDigest->fPassword, sizeof(Str255)); |
// I deliberately didn't decode the password here. If you want |
// the decoded password, you have to do it yourself. |
} else { |
err = -8; |
} |
break; |
// 'cdia' -> fRedialMode, fRedialTimes, fRedialDelay |
case kOTCfgRemoteDialingPref: |
if ( prefSize == sizeof(OTCfgRemoteDialing) |
&& RemoteVersionAcceptable(((UInt32 *)prefData)[0]) |
&& ((UInt32 *)prefData)[1] == 'dial') { |
OTCfgRemoteDialing *dialingPtr; |
dialingPtr = (OTCfgRemoteDialing *) prefData; |
configurationDigest->fRedialMode = dialingPtr->dialMode; |
configurationDigest->fRedialTimes = dialingPtr->redialTries; |
configurationDigest->fRedialDelay = dialingPtr->redialDelay; |
} else { |
err = -8; |
} |
break; |
// 'ipcp' -> fDisconnectIfIdle, fDisconnectInterval, fPPPAllowTCPIPHeaderCompression |
case kOTCfgRemoteIPCPPref: |
// Sometimes the second long of prefData has 'ipcp' in it, but |
// we can't check that because often it doesn't! |
if ( prefSize == sizeof(OTCfgRemoteIPCP) && RemoteVersionAcceptable(((UInt32 *)prefData)[0])) { |
OTCfgRemoteIPCP *ipcpPtr; |
ipcpPtr = (OTCfgRemoteIPCP *) prefData; |
configurationDigest->fDisconnectIfIdle = ipcpPtr->idleTimerEnabled; |
configurationDigest->fDisconnectInterval = ipcpPtr->idleTimerMilliseconds; |
configurationDigest->fPPPAllowTCPIPHeaderCompression = ipcpPtr->compressTCPHeaders; |
} else { |
err = -8; |
} |
break; |
// 'logo' -> fVerboseLogging |
case kOTCfgRemoteLogOptionsPref: |
if ( prefSize == sizeof(OTCfgRemoteLogOptions) |
&& RemoteVersionAcceptable(((UInt32 *)prefData)[0]) |
&& ((UInt32 *)prefData)[1] == 'lgop') { |
OTCfgRemoteLogOptions *logPtr; |
logPtr = (OTCfgRemoteLogOptions *) prefData; |
configurationDigest->fVerboseLogging = logPtr->logLevel; |
} else { |
err = -8; |
} |
break; |
// 'cadr' -> fPhoneNumber |
case kOTCfgRemoteAddressPref: |
if ( prefSize <= 255 ) { |
configurationDigest->fPhoneNumber[0] = prefSize; |
BlockMoveData(prefData, &configurationDigest->fPhoneNumber[1], prefSize); |
} else { |
err = -8; |
} |
break; |
// 'cead' -> fAlternatePhoneNumber |
case kOTCfgRemoteAlternateAddressPref: |
if ( prefSize == sizeof(Str255) + sizeof(UInt32) |
&& ((UInt32 *)prefData)[0] == 0) { |
BlockMoveData( ((char *)prefData) + sizeof(UInt32), configurationDigest->fAlternatePhoneNumber, sizeof(Str255)); |
} else { |
err = -8; |
} |
break; |
// 'ccha' -> fPPPConnectScript |
case kOTCfgRemoteChatPref: |
if ( configurationDigest->fPPPConnectScript != nil ) { |
err = PtrToXHand(prefData, configurationDigest->fPPPConnectScript, prefSize); |
} |
break; |
case kOTCfgRemoteUserModePref: |
case kOTCfgRemoteClientLocksPref: |
case kOTCfgRemoteLCPPref: |
case kOTCfgRemoteARAPPref: |
case kOTCfgRemoteDialAssistPref: |
case kOTCfgRemoteX25Pref: |
case kOTCfgRemoteTerminalPref: |
// Known preferences which we don't need to pay |
// attention to but shouldn't cause the following |
// assert to trigger. |
break; |
default: |
// Unexpected preference type in the packed prefs. |
// This is not super-fatal, but let the developer know in debug builds. |
MoreAssertQ(false); |
break; |
} |
} while (err == noErr && prefType != 0); |
HSetState(packedPrefs, s); |
// Fix up the redial preferences. We should only return |
// an alternate redial phone number if the redial mode is |
// appropriate. This is not strictly necessary, but it |
// tidies up one of the anomalies noticed by the test case. |
if (err == noErr) { |
if ( configurationDigest->fRedialMode != kOTCfgRemoteRedialMainAndAlternate ) { |
configurationDigest->fAlternatePhoneNumber[0] = 0; |
} |
} |
return err; |
} |
///////////////////////////////////////////////////////////////// |
#pragma mark ----- Modem Packer/Unpacker ------ |
// These routines convert between the Modem configuration digest |
// (a big record with all the relevant fields in it) and the |
// packed preferences data (which is read from or written to the |
// preferences store). These routines are shared between the |
// preferences database and preferences file implementations. |
static OSStatus BuildPackedPrefsFromModemConfigurationDigest( |
const NSHModemConfigurationDigest *configurationDigest, |
Boolean forceDefaults, |
Handle *packedPrefs) |
// This routine converts a Modem configuration digest |
// to a handle containing packed preferences. |
// |
// forceDefaults is not needed because the Modem digest |
// has no handle-based field. |
{ |
#pragma unused(forceDefaults) |
OSStatus err; |
OTPortRecord portRec; |
PrefBuilderState state; |
UInt8 buffer[256]; |
OTCfgModemLocks *locksPtr; |
OTCfgModemGeneral *modemConfig; |
MoreAssertQ(configurationDigest != nil); |
MoreAssertQ(packedPrefs != nil); |
*packedPrefs = nil; |
err = noErr; |
if ( ! OTFindPortByRef(&portRec, configurationDigest->fPortRef) ) { |
err = kOTNotFoundErr; |
} |
if (err == noErr) { |
BuilderNew(&state); |
// 'pnam' |
BuilderNewPref(&state, kOTCfgUserVisibleNamePref, configurationDigest->fConfigName, configurationDigest->fConfigName[0] + 1); |
// 'ccl ' -> fPortRef, fModemScript, fDialToneMode, fSpeakerOn, fPulseDial, fHintPortName |
OTMemzero(buffer, sizeof(buffer)); |
MoreAssertQ(sizeof(*modemConfig) <= sizeof(buffer)); |
modemConfig = (OTCfgModemGeneral *) buffer; |
modemConfig->version = 0x00010000; |
modemConfig->useModemScript = true; |
modemConfig->modemScript = configurationDigest->fModemScript; |
modemConfig->modemSpeakerOn = configurationDigest->fSpeakerOn; |
modemConfig->modemPulseDial = configurationDigest->fPulseDial; |
modemConfig->modemDialToneMode = configurationDigest->fDialToneMode; |
BlockMoveData(portRec.fPortName, modemConfig->lowerLayerName, 36); |
BuilderNewPref(&state, kOTCfgModemGeneralPrefs, modemConfig, sizeof(*modemConfig)); |
// 'lkmd' -> $00000001 + $00000000 * 4 |
OTMemzero(buffer, sizeof(buffer)); |
MoreAssertQ(sizeof(*locksPtr) <= sizeof(buffer)); |
locksPtr = (OTCfgModemLocks *) buffer; |
locksPtr->version = 1; |
BuilderNewPref(&state, kOTCfgModemLocksPref, locksPtr, sizeof(*locksPtr)); |
// 'mdpw' -> $00 * 256 |
OTMemzero(buffer, sizeof(buffer)); |
BuilderNewPref(&state, kOTCfgModemAdminPasswordPref, buffer, 256); |
err = BuilderDone(&state, packedPrefs); |
} |
return err; |
} |
static OSStatus BuildModemConfigurationDigestFromPackedPrefs(Handle packedPrefs, |
NSHModemConfigurationDigest *configurationDigest) |
// This routine fills out a Modem configuration digest |
// based on the packed preferences. The basic algorithm |
// is to iterate through all the preferences, looking at |
// each one and putting the data from that preference into |
// the configuration digest. At the end it does some jiggery |
// pokery that's explained in the comment down there. |
{ |
OSStatus err; |
SInt8 s; |
ByteCount cookie; |
OSType prefType; |
ByteCount prefSize; |
void *prefData; |
OTPortRecord portRec; |
OTCfgModemGeneral *modemConfig; |
MoreAssertQ(packedPrefs != nil); |
MoreAssertQ(configurationDigest != nil); |
OTMemzero(configurationDigest, sizeof(configurationDigest)); |
configurationDigest->fProtocol = kOTCfgTypeModem; |
s = HGetState(packedPrefs); |
HLock(packedPrefs); |
err = noErr; |
cookie = 0; |
do { |
WriterGetNextPref(&cookie, *packedPrefs, &prefType, &prefData, &prefSize); |
switch (prefType) { |
case 0: |
// do nothing, this is the loop exit condition |
break; |
// 'pnam' |
case kOTCfgUserVisibleNamePref: |
if ( prefSize <= sizeof(Str255) && prefSize == ((UInt8 *)prefData)[0] + 1 ) { |
BlockMoveData(prefData, configurationDigest->fConfigName, sizeof(Str255)); |
} else { |
err = -8; |
} |
break; |
// 'ccl ' |
case kOTCfgModemGeneralPrefs: |
modemConfig = (OTCfgModemGeneral *) prefData; |
if (prefSize == sizeof(OTCfgModemGeneral) && modemConfig->version == 0x00010000) { |
if (modemConfig->useModemScript) { |
configurationDigest->fModemScript = modemConfig->modemScript; |
} else { |
// leave fModemScript set to all zeros |
} |
configurationDigest->fSpeakerOn = modemConfig->modemSpeakerOn; |
configurationDigest->fPulseDial = modemConfig->modemPulseDial; |
configurationDigest->fDialToneMode = modemConfig->modemDialToneMode; |
configurationDigest->fHintPortName[0] = OTStrLength( (char *) modemConfig->lowerLayerName); |
MoreAssertQ(configurationDigest->fHintPortName[0] <= 63); |
BlockMoveData(modemConfig->lowerLayerName, &configurationDigest->fHintPortName[1], configurationDigest->fHintPortName[0]); |
} else { |
err = -8; |
} |
break; |
// 'lkmd' |
case kOTCfgModemLocksPref: |
// just check the size |
if (prefSize != 20) { |
err = -8; |
} |
break; |
// 'mdpw' |
case kOTCfgModemAdminPasswordPref: |
// just check the size |
if (prefSize != 256) { |
err = -8; |
} |
break; |
default: |
// Unexpected preference type in the packed prefs. |
// This is not super-fatal, but let the developer know in debug builds. |
MoreAssertQ(false); |
break; |
} |
} while (err == noErr && prefType != 0); |
HSetState(packedPrefs, s); |
// Before we leave, we have to set up the fPortRef field of the |
// configuration digest. This is much easier than it was for TCP/IP. |
// Basically, if we have a port that matches the port name stored |
// in the preferences, that's the one we're going to return. |
if (err == noErr) { |
// Put a null byte after the last byte of the hint. |
// We know we have the space because they're declared as |
// Str63s. We need this to pass it as a C string to OTFindPort. |
configurationDigest->fHintPortName[configurationDigest->fHintPortName[0] + 1] = 0; |
if ( OTFindPort(&portRec, ((char *) configurationDigest->fHintPortName) + 1) ) { |
configurationDigest->fPortRef = portRec.fRef; |
} else { |
configurationDigest->fPortRef = kOTInvalidPortRef; |
} |
} |
return err; |
} |
///////////////////////////////////////////////////////////////// |
#pragma mark ----- Packer/Unpacker Dispatch ------ |
static OSStatus BuildConfigurationDigestFromPackedPrefs(OSType protocol, Handle packedPrefs, |
NSHConfigurationDigest *configurationDigest) |
// A simple dispatcher that calls the appropriate protocol-specific |
// BuildXxxConfigurationDigestFromPackedPrefs routine based on the supplied |
// protocol. |
{ |
OSStatus err; |
switch (protocol) { |
case kOTCfgTypeTCPv4: |
err = BuildTCPv4ConfigurationDigestFromPackedPrefs(packedPrefs, &configurationDigest->fTCPv4); |
break; |
case kOTCfgTypeRemote: |
err = BuildRemoteConfigurationDigestFromPackedPrefs(packedPrefs, &configurationDigest->fRemote); |
break; |
case kOTCfgTypeModem: |
err = BuildModemConfigurationDigestFromPackedPrefs(packedPrefs, &configurationDigest->fModem); |
break; |
default: |
err = paramErr; |
break; |
} |
return err; |
} |
static OSStatus BuildPackedPrefsFromConfigurationDigest(const NSHConfigurationDigest *configurationDigest, Boolean forceDefaults, Handle *packedPrefs) |
// A simple dispatcher that calls the appropriate protocol-specific |
// BuildPackedPrefsFromXxxConfigurationDigest routine based on the |
// protocol in the digest. |
{ |
OSStatus err; |
switch (configurationDigest->fCommon.fProtocol) { |
case kOTCfgTypeTCPv4: |
err = BuildPackedPrefsFromTCPv4ConfigurationDigest(&configurationDigest->fTCPv4, forceDefaults, packedPrefs); |
break; |
case kOTCfgTypeRemote: |
err = BuildPackedPrefsFromRemoteConfigurationDigest(&configurationDigest->fRemote, forceDefaults, packedPrefs); |
break; |
case kOTCfgTypeModem: |
err = BuildPackedPrefsFromModemConfigurationDigest(&configurationDigest->fModem, forceDefaults, packedPrefs); |
break; |
default: |
err = paramErr; |
break; |
} |
return err; |
} |
///////////////////////////////////////////////////////////////// |
#pragma mark ----- Internet Setup using Database ------ |
static OSStatus WritePackedPrefsToDatabase(const MNSDatabaseRef *ref, |
const CfgEntityRef *configEntity, |
Handle packedPrefs) |
// This is a utility routine that simply writes the entire |
// set of packed preferences to the configuration database |
// entity specified by ref and configEntity. |
{ |
SInt8 s; |
OSStatus err; |
ByteCount cookie; |
OSType prefType; |
ByteCount prefSize; |
void *prefPtr; |
MoreAssertQ(ref != nil); |
MoreAssertQ(configEntity != nil); |
MoreAssertQ(packedPrefs != nil); |
// Lock down the packed preferences. |
s = HGetState(packedPrefs); |
HLock(packedPrefs); |
MoreAssertQ(MemError() == noErr); |
// Iterate through each preference, write it out to the database. |
err = noErr; |
cookie = 0; |
do { |
WriterGetNextPref(&cookie, *packedPrefs, &prefType, &prefPtr, &prefSize); |
if (prefType != 0) { |
err = MNSSetPref(ref, configEntity, prefType, prefPtr, prefSize); |
} |
} while (err == noErr && prefType != 0); |
// Clean up. |
HSetState(packedPrefs, s); |
return err; |
} |
static pascal void BuilderIterator(OSType prefType, void *prefData, ByteCount prefSize, void *refcon) |
// This is an MNSIterateEntity callback routine. It simply |
// adds each preference in the entity to the the builder |
// whose state is passed in as the refcon parameter. The upshot |
// is that the entity's entire preference set is added to |
// the packed prefs. I love it when concepts like builders |
// and iterators work together (-: |
{ |
switch (prefType) { |
case kOTCfgCompatResourceIDPref: |
case kOTCfgCompatResourceNamePref: |
case kOTCfgCompatNamePref: |
// Do nothing. These preferences are used purely by |
// Network Setup to maintain accurate roundtrip |
// conversions between the backward compatibility |
// files and the database. We never need the |
// information from the files, so we can just |
// ignore them completely. |
break; |
default: |
BuilderNewPref( (PrefBuilderState *) refcon, prefType, prefData, prefSize); |
break; |
} |
} |
static OSStatus ReadPackedPrefsFromDatabase(const MNSDatabaseRef *ref, |
const CfgEntityRef *configEntity, |
Handle *packedPrefs) |
// This routine builds a packed preference handle based on |
// all the preferences in the entity specified by ref and |
// configEntity. *packedPrefs must be nil before you call |
// the routine. The routine either succeeds (returning noErr |
// and setting *packedPrefs to a valid handle), or fails |
// (returning an error, and leaving *packedRefs as nil). |
{ |
OSStatus err; |
OSStatus err2; |
PrefBuilderState state; |
MoreAssertQ(ref != nil); |
MoreAssertQ(configEntity != nil); |
MoreAssertQ(packedPrefs != nil); |
MoreAssertQ(*packedPrefs == nil); |
BuilderNew(&state); |
err = MNSIterateEntity(ref, configEntity, BuilderIterator, &state); |
err2 = BuilderDone(&state, packedPrefs); |
if (err == noErr) { |
err = err2; |
} |
MoreAssertQ(err != noErr && packedPrefs == nil || err == noErr && packedPrefs != nil); |
return err; |
} |
static void FillOutConfigBits(OSType protocol, ConstStr255Param name, NSHConfigurationEntry *thisConfig) |
// Fills out the name, selected and cookie3 fields of thisConfig. |
// This is basically common code extracted from CreateConfigurationDatabase |
// and DuplicateConfigurationDatabase. |
{ |
MoreAssertQ(thisConfig != nil); |
BlockMoveData(name, thisConfig->name, sizeof(Str255)); |
thisConfig->selected = false; |
thisConfig->cookie = 0; |
thisConfig->cookie3.fClass = kOTCfgClassNetworkConnection; |
thisConfig->cookie3.fType = protocol; |
BlockMoveData(name, thisConfig->cookie3.fName, sizeof(Str255)); |
thisConfig->cookie3.fIcon.fFile.vRefNum = 0; |
thisConfig->cookie3.fIcon.fFile.parID = 0; |
thisConfig->cookie3.fIcon.fFile.name[0] = 0; |
thisConfig->cookie3.fIcon.fResID = 0; |
thisConfig->cookie4 = protocol; |
} |
static OSStatus CreateConfigurationDatabase(const NSHConfigurationDigest *configurationDigest, |
NSHConfigurationEntry *createdConfig) |
// Implementation of NSHCreateConfiguration which uses the Network Setup |
// database. See NSHCreateConfiguration's comment in header |
// file for interface specification. |
// |
// This routine opens up the database, creates an entity, and sets |
// the contents of the entity from the digest using common utility |
// routines. Plus there's a bunch of housekeeping that's documented |
// by comments in the routine. |
{ |
OSStatus err; |
OSStatus err2; |
NSHConfigurationEntry tmpConfig; |
NSHConfigurationEntry *destConfig; |
MNSDatabaseRef ref; |
CfgAreaID originalArea; |
Handle packedPrefs; |
MoreAssertQ(configurationDigest != nil); |
// createdConfig is an optional parameter. To simplify our |
// code, we use a temporary config if the user didn't supply it. |
if (createdConfig != nil) { |
destConfig = createdConfig; |
} else { |
destConfig = &tmpConfig; |
} |
packedPrefs = nil; |
err = MNSOpenDatabase(&ref, true); |
if (err == noErr) { |
FillOutConfigBits(configurationDigest->fCommon.fProtocol, configurationDigest->fCommon.fConfigName, destConfig); |
// Create the entity, getting back the CfgEntityRef. |
err = OTCfgCreateEntity(ref.dbRef, ref.area, &destConfig->cookie3, |
&destConfig->cookie2); |
if (err == noErr) { |
// Fill out the entity with the information from the digest. |
err = BuildPackedPrefsFromConfigurationDigest(configurationDigest, true, &packedPrefs); |
if (err == noErr) { |
err = WritePackedPrefsToDatabase(&ref, &destConfig->cookie2, packedPrefs); |
} |
if (err != noErr) { |
// There is no need to delete the half-created config |
// because we're about to abort the database transaction, |
// which will discard any changes we made. |
} |
} |
originalArea = ref.originalArea; |
err2 = MNSCloseDatabase(&ref, err == noErr); |
if (err == noErr) { |
err = err2; |
} |
// The entity was created in a temporary area. After we close |
// that area, that temporary entity goes away, being replaced by |
// the final entity in the real area. To avoid client confusion, |
// we set the returned entity's area to that original area. |
// Otherwise, when they pass this returned entity to |
// the get or set routine, Network Setup will complain that |
// the entity's area doesn't exist. |
if (err == noErr) { |
destConfig->cookie2.fLoc = originalArea; |
} |
} |
// Clean up. |
if (packedPrefs != nil) { |
DisposeHandle(packedPrefs); |
MoreAssertQ(MemError() == noErr); |
} |
return err; |
} |
static OSStatus DuplicateConfigurationDatabase(const NSHConfigurationEntry *config, |
ConstStr255Param newConfigName, |
NSHConfigurationEntry *createdConfig) |
// Implementation of NSHDuplicateConfiguration which uses the Network Setup |
// database. See NSHDuplicateConfiguration's comment in header |
// file for interface specification. |
// |
// This routine opens up the database, creates an entity, and sets |
// the contents of the entity by duplicating the entity referenced |
// by config. And then there's a bunch of housekeeping. |
{ |
OSStatus err; |
OSStatus err2; |
NSHConfigurationEntry tmpConfig; |
NSHConfigurationEntry *destConfig; |
MNSDatabaseRef ref; |
CfgAreaID originalArea; |
MoreAssertQ(config != nil); |
MoreAssertQ(newConfigName != nil); |
// createdConfig is an optional parameter. To simplify our |
// code, we use a temporary config if the user didn't supply it. |
if (createdConfig != nil) { |
destConfig = createdConfig; |
} else { |
destConfig = &tmpConfig; |
} |
err = MNSOpenDatabase(&ref, true); |
if (err == noErr) { |
FillOutConfigBits(config->cookie3.fType, newConfigName, destConfig); |
// Create the entity, getting back the CfgEntityRef. |
err = OTCfgCreateEntity(ref.dbRef, ref.area, &destConfig->cookie3, |
&destConfig->cookie2); |
if (err == noErr) { |
err = OTCfgDuplicateEntity(ref.dbRef, &config->cookie2, &destConfig->cookie2); |
if (err == noErr) { |
// Force the user-visible name to newConfigName. |
err = MNSSetPref(&ref, &destConfig->cookie2, kOTCfgUserVisibleNamePref, newConfigName, *newConfigName + 1); |
} |
if (err != noErr) { |
// There is no need to delete the half-created config |
// because we're about to abort the database transaction, |
// which will discard any changes we made. |
} |
} |
originalArea = ref.originalArea; |
err2 = MNSCloseDatabase(&ref, err == noErr); |
if (err == noErr) { |
err = err2; |
} |
// The entity was created in a temporary area. After we close |
// that area, that temporary entity goes away, being replaced by |
// the final entity in the real area. To avoid client confusion, |
// we set the returned entity's area to that original area. |
// Otherwise, when they pass this returned entity to |
// the get or set routine, Network Setup will complain that |
// the entity's area doesn't exist. |
if (err == noErr) { |
destConfig->cookie2.fLoc = originalArea; |
} |
} |
return err; |
} |
static OSStatus GetConfigurationDatabase(const NSHConfigurationEntry *config, |
NSHConfigurationDigest *configurationDigest) |
// Implementation of NSHGetConfiguration which uses the Network Setup |
// database. See NSHGetConfiguration's comment in header |
// file for interface specification. |
// |
// This routine opens up the database, reads the preferences out of |
// the configuration, and then converts them to a configuration digest. |
{ |
OSStatus err; |
OSStatus err2; |
MNSDatabaseRef ref; |
Handle packedPrefs; |
MoreAssertQ(config != nil); |
MoreAssertQ(configurationDigest != nil); |
packedPrefs = nil; |
err = MNSOpenDatabase(&ref, false); |
if (err == noErr) { |
err = ReadPackedPrefsFromDatabase(&ref, &config->cookie2, &packedPrefs); |
if (err == noErr) { |
err = BuildConfigurationDigestFromPackedPrefs(config->cookie4, packedPrefs, configurationDigest); |
} |
err2 = MNSCloseDatabase(&ref, false); |
if (err == noErr) { |
err = err2; |
} |
} |
if (packedPrefs != nil) { |
DisposeHandle(packedPrefs); |
MoreAssertQ(MemError() == noErr); |
} |
return err; |
} |
static void FixClientEntityForWriting(const MNSDatabaseRef *ref, const CfgEntityRef *clientEntity, |
CfgEntityRef *fixedEntity) |
// Entitys, as we expose them to the outside world, always |
// refer to the read area of the database. But if we've opened |
// this area for writing, we're actually supposed to write to |
// a temporary writable area. But the incoming entity references |
// from the client refer to the read area. This routine |
// creates a fixed entity which is entirely based on the |
// incoming client entity except that it refers to the write |
// area we've just opened. |
{ |
MoreAssertQ(clientEntity->fLoc == ref->originalArea); |
*fixedEntity = *clientEntity; |
fixedEntity->fLoc = ref->area; |
} |
static OSStatus SetConfigurationDatabase(const NSHConfigurationEntry *config, |
const NSHConfigurationDigest *configurationDigest) |
// Implementation of NSHSetConfiguration which uses the Network Setup |
// database. See NSHSetConfiguration's comment in header |
// file for interface specification. |
// |
// This routine opens up the database and then sets the configuration |
// from the digest using using common utility routines |
{ |
OSStatus err; |
OSStatus err2; |
MNSDatabaseRef ref; |
CfgEntityRef tmpEntity; |
Handle packedPrefs; |
MoreAssertQ(config != nil); |
MoreAssertQ(configurationDigest != nil); |
packedPrefs = nil; |
err = MNSOpenDatabase(&ref, true); |
if (err == noErr) { |
FixClientEntityForWriting(&ref, &config->cookie2, &tmpEntity); |
err = BuildPackedPrefsFromConfigurationDigest(configurationDigest, false, &packedPrefs); |
if (err == noErr) { |
err = WritePackedPrefsToDatabase(&ref, &tmpEntity, packedPrefs); |
} |
err2 = MNSCloseDatabase(&ref, err == noErr); |
if (err == noErr) { |
err = err2; |
} |
} |
// Clean up. |
if (packedPrefs != nil) { |
DisposeHandle(packedPrefs); |
MoreAssertQ(MemError() == noErr); |
} |
return err; |
} |
// The DeleteConfigFromSetParam data structure is used |
// to pass parameters to DeleteConfigFromSetIterator. |
enum { |
kDeleteConfigFromSetParamSignature = 'DcSp' |
}; |
struct DeleteConfigFromSetParam { |
OSType signature; // is always kDeleteConfigFromSetParamSignature, debugging check |
CfgEntityRef *entityWereDeleting; |
CfgSetsVector **newSetsVector; |
OSStatus latchedError; |
Boolean dirty; |
Boolean newSetContainsAppleTalk; |
Boolean newSetContainsTCPv4; |
Boolean newSetContainsRemote; |
Boolean newSetContainsModem; |
Boolean newSetContainsInfrared; |
}; |
typedef struct DeleteConfigFromSetParam DeleteConfigFromSetParam; |
static void InitDeleteConfigFromSetParam(DeleteConfigFromSetParam *param, CfgEntityRef *entityToDelete, CfgSetsVector **newSetsVector) |
// This routine initialises the DeleteConfigFromSetParam |
// data structure. |
{ |
param->signature = kDeleteConfigFromSetParamSignature; |
param->entityWereDeleting = entityToDelete; |
param->newSetsVector = newSetsVector; |
param->latchedError = noErr; |
param->dirty = false; |
param->newSetContainsAppleTalk = false; |
param->newSetContainsTCPv4 = false; |
param->newSetContainsRemote = false; |
param->newSetContainsModem = false; |
param->newSetContainsInfrared = false; |
} |
static pascal void DeleteConfigFromSetIterator(const MNSDatabaseRef *ref, CfgSetsElement *thisElement, void *p) |
// This routine is an iterator passed to MNSIterateSet. MNSIterateSet |
// repeatedly calls this routine, once for each element of the set. |
// The purpose of the routine is to: |
// |
// a) build a new set which contains all of the entities |
// in the existing set except for the one we're deleting, and |
// b) ensure that the new set has an entity for each of the various |
// backward compatibility protocols. If it does, we set the |
// appropriate Boolean flag in the DeleteConfigFromSetParam passed |
// in as the "p" parameter. |
// |
// The reason why we need to do this is explained in the comment |
// for DeleteConfigurationDatabase. |
{ |
OSStatus err; |
DeleteConfigFromSetParam *param; |
#pragma unused(ref) |
param = (DeleteConfigFromSetParam *) p; |
MoreAssertQ(param != nil); |
MoreAssertQ(param->signature == kDeleteConfigFromSetParamSignature); |
if (param->latchedError == noErr) { |
if ( ! OTCfgIsSameEntityRef(&thisElement->fEntityRef, param->entityWereDeleting, kOTCfgIgnoreArea) ) { |
// This entity isn't the one we're deleting, so copy it across to |
// newSetsVector and, if it's an entity for a protocol which has |
// a legacy prefs file, mark it as existing in the new set. |
err = PtrAndHand(thisElement, (Handle) param->newSetsVector, sizeof(*thisElement)); |
if (err == noErr) { |
(**(param->newSetsVector)).fCount += 1; |
switch (thisElement->fEntityInfo.fType) { |
case kOTCfgTypeAppleTalk: param->newSetContainsAppleTalk = true; break; |
case kOTCfgTypeTCPv4: param->newSetContainsTCPv4 = true; break; |
case kOTCfgTypeRemote: param->newSetContainsRemote = true; break; |
case kOTCfgTypeModem: param->newSetContainsModem = true; break; |
case kOTCfgTypeInfrared: param->newSetContainsInfrared = true; break; |
} |
} |
param->latchedError = err; |
} else { |
// This entity is the one we're deleting, so don't copy it, and mark |
// the newSetsVector as dirty so that we write it back. |
param->dirty = true; |
} |
} |
} |
static OSStatus DeleteConfigurationDatabase(const NSHConfigurationEntry *config) |
// Implementation of NSHDeleteConfiguration which uses the Network Setup |
// database. See NSHDeleteConfiguration's comment in header |
// file for interface specification. |
// |
// At first glance, this should be easy, ie a straight pass through to |
// OTCfgDeleteEntity. But then you have to worry about sets. If any |
// set contains a reference to this entity, you have to delete that |
// reference out of the set. And then you have to worry about |
// backward compatibility. What happens if you delete the TCP/IP |
// configuration out of the active set? When Network Setup goes |
// to export the database to the legacy preferences file, what |
// does it do if there's no active TCP/IP configuration. The answer: |
// it messes up horribly. So we prevent you from deleting an entity |
// if it's the last entity for a backward compatibility protocol. |
// |
// [Should this continue "... in the active set." I don't think |
// so, because we want to allow folks to switch sets easily, and having |
// them worry about "Oh, this set doesn't have a Remote Access entity |
// so I can't switch to it." is asking too much.] |
{ |
OSStatus err; |
OSStatus err2; |
MNSDatabaseRef ref; |
CfgEntityRef entityToDelete; |
ItemCount entityCount; |
CfgEntityRef *entityRefs; |
ItemCount entityIndex; |
CfgSetsVector **newSetsVector; |
DeleteConfigFromSetParam param; |
MoreAssertQ(config != nil); |
entityRefs = nil; |
newSetsVector = (CfgSetsVector **) NewHandle(sizeof(UInt32)); |
err = MemError(); |
if (err == noErr) { |
err = MNSOpenDatabase(&ref, true); |
} |
if (err == noErr) { |
FixClientEntityForWriting(&ref, &config->cookie2, &entityToDelete); |
// Cool. I don't have to worry about which order to delete |
// in (ie should I delete the entity or modify the set vectors |
// first) and how I roll back the changes in the case of a failure, |
// because changes are committed atomically by MNSCloseDatabase. |
// Delete the entity... |
if (err == noErr) { |
err = OTCfgDeleteEntity(ref.dbRef, &entityToDelete); |
} |
// Now remove it from any sets. First get the list of sets... |
if (err == noErr) { |
err = MNSGetEntitiesList(&ref, |
kOTCfgClassSetOfSettings, kOTCfgTypeSetOfSettings, |
&entityCount, |
&entityRefs, nil); |
} |
if (err == noErr) { |
// Then, for each set entity we found... |
for (entityIndex = 0; entityIndex < entityCount; entityIndex++) { |
// Set up newSetsVector to be an empty CfgSetsVector, |
// ie a handle with a 4 byte fCount field which is set |
// to 0. |
SetHandleSize( (Handle) newSetsVector, sizeof(UInt32)); |
err = MemError(); |
if (err == noErr) { |
(**newSetsVector).fCount = 0; |
// Fill out param, which is a parameter block containing |
// all the information that DeleteConfigFromSetIterator needs. |
InitDeleteConfigFromSetParam(¶m, &entityToDelete, newSetsVector); |
// Call DeleteConfigFromSetIterator on each element of |
// the entityIndex'th set entity. |
err = MNSIterateSet(&ref, |
&entityRefs[entityIndex], |
DeleteConfigFromSetIterator, ¶m, |
false); |
if (err == noErr) { |
err = param.latchedError; |
} |
} |
if (err == noErr && param.dirty) { |
if (param.newSetContainsAppleTalk |
&& param.newSetContainsTCPv4 |
&& param.newSetContainsRemote |
&& param.newSetContainsModem |
&& param.newSetContainsInfrared) { |
err = MNSSetPrefHandle(&ref, &entityRefs[entityIndex], kOTCfgSetsVectorPref, (Handle) newSetsVector); |
} else { |
err = -11; |
} |
} |
if (err != noErr) { |
break; |
} |
} |
} |
err2 = MNSCloseDatabase(&ref, err == noErr); |
if (err == noErr) { |
err = err2; |
} |
} |
// Clean up. |
if (newSetsVector != nil) { |
DisposeHandle( (Handle) newSetsVector ); |
MoreAssertQ(MemError() == noErr); |
} |
if (entityRefs != nil) { |
DisposePtr( (Ptr) entityRefs ); |
MoreAssertQ(MemError() == noErr); |
} |
return err; |
} |
static OSStatus RenameConfigurationDatabase(const NSHConfigurationEntry *config, |
ConstStr255Param newConfigName, |
NSHConfigurationEntry *newConfig) |
// Implementation of NSHRenameConfiguration which uses the Network Setup |
// database. See NSHRenameConfiguration's comment in header |
// file for interface specification. |
// |
// A relatively simple pass through to OTCfgSetEntityName. |
{ |
OSStatus err; |
OSStatus err2; |
NSHConfigurationEntry tmpConfig; |
NSHConfigurationEntry *destConfig; |
MNSDatabaseRef ref; |
CfgEntityRef tmpEntity; |
MoreAssertQ(config != nil); |
MoreAssertQ(newConfigName != nil); |
if (newConfig != nil) { |
destConfig = newConfig; |
} else { |
destConfig = &tmpConfig; |
} |
err = MNSOpenDatabase(&ref, true); |
if (err == noErr) { |
FillOutConfigBits(config->cookie3.fType, newConfigName, destConfig); |
FixClientEntityForWriting(&ref, &config->cookie2, &tmpEntity); |
err = OTCfgSetEntityName(ref.dbRef, &tmpEntity, newConfigName, &destConfig->cookie2); |
err2 = MNSCloseDatabase(&ref, err == noErr); |
if (err == noErr) { |
err = err2; |
} |
} |
return err; |
} |
///////////////////////////////////////////////////////////////// |
#pragma mark ----- Internet Setup using File ------ |
static OSStatus ReadPackedPrefsFromFile(SInt16 configID, Handle *packedPrefs) |
// This routine builds a packed preference handle based on |
// all the preferences in the specified configuration of the |
// current resource file. *packedPrefs must be nil before you call |
// the routine. The routine either succeeds (returning noErr |
// and setting *packedPrefs to a valid handle), or fails |
// (returning an error, and leaving *packedRefs as nil). |
{ |
OSStatus err; |
OSStatus err2; |
PrefBuilderState state; |
SInt16 typeCount; |
SInt16 typeIndex; |
OSType thisType; |
Handle thisResourceH; |
SInt16 junkID; |
OSType junkType; |
Str255 configName; |
MoreAssertQ(packedPrefs != nil); |
MoreAssertQ(*packedPrefs == nil); |
BuilderNew(&state); |
// Iterate through all the resource types in the file. |
// For each type, check to see whether there's a resource |
// with ID equal to configID in the file. If there is, |
// add it to the packedPrefs. |
typeCount = Count1Types(); |
err = ResError(); |
if (err == noErr) { |
for (typeIndex = 1; typeIndex <= typeCount; typeIndex++) { |
Get1IndType(&thisType, typeIndex); |
err = ResError(); |
if (err == noErr) { |
thisResourceH = Get1Resource(thisType, configID); |
err = CheckResError(thisResourceH); |
if (err == noErr) { |
HLock(thisResourceH); |
// We have to handle the 'cnam' as a special case. In |
// legacy files, the user-visible name of the preference |
// is stored in the resource name of the 'cnam' preference. |
// The preference data contains no useful information. |
// So when we encounter a 'cnam' preference, we extract |
// the resource name and write that to the packed prefs |
// as a 'pnam' preference. This is where the various |
// digest routines expect the user-visible name to be. |
// In addition, WritePackedPrefsToFile is smart enough to |
// undo this. |
if (thisType == kOTCfgCompatNamePref) { |
GetResInfo(thisResourceH, &junkID, &junkType, configName); |
BuilderNewPref(&state, kOTCfgUserVisibleNamePref, configName, configName[0] + 1); |
} else { |
BuilderNewPref(&state, thisType, *thisResourceH, GetHandleSize(thisResourceH)); |
} |
// ¥¥¥ Gotcha ¥¥¥ |
// Release the resource we just got. This was a contentious |
// issue when coding this. We want to release the resource because |
// it avoids the resources building up in the memory of the client |
// application. But what happens if the resource file |
// is being used by other code (ie someone else in this |
// process has the file open read/write, so we're sharing |
// a resource map with them) and we release the resource |
// out from underneath them. The answer is, of course, |
// "bad things", but we have no way of detecting this condition. |
// I'm just making the assumption that this situation won't |
// happen. As it is, it's fairly unlikely. Most software |
// that modifies preferences (most importantly the network |
// control panels) runs as a separate process, so resource |
// map sharing is unlikely. It could happen if you're running |
// an extension inside the context of one of those applications, |
// but doing that is asking for trouble anyway (-: |
// |
// For more background on this problem. see the comment about |
// fsRdWrPerm in OpenNetworkPrefFile. |
// |
// One added advantage to releasing the resource is that we |
// can simply HLock the handle above, rather than using the |
// state HGetState/HLock/HSetState tuple. |
// |
// -- Quinn, 25 May 1999 |
ReleaseResource(thisResourceH); |
err = ResError(); |
} else if (err == resNotFound) { |
err = noErr; |
} |
} |
if (err != noErr) { |
break; |
} |
} |
} |
err2 = BuilderDone(&state, packedPrefs); |
if (err == noErr) { |
err = err2; |
} |
MoreAssertQ(err != noErr && packedPrefs == nil || err == noErr && packedPrefs != nil); |
return err; |
} |
static OSStatus Set1ResourceFromPtr(OSType theType, SInt16 theID, void *dataPtr, ByteCount dataSize) |
// God I hate the Resource Manager API. There's no |
// simple way of saying "set this resource to this value". |
// Instead, you have to get it, see whether it exists, |
// if it does, change it and mark it changed, if it doesn't, |
// create it. Blurgh. All the while checking for weirdo |
// errors and handling the facts that a) you can't release |
// changed resources (otherwise there's no copy of the handle |
// containing the data for the Resource Manager to write |
// when you call UpdateResFile), and b) AddResource converts |
// a memory handle to a resource handle, but if it fails you |
// still have to dispose of the memory. |
{ |
OSStatus err; |
Handle thisResourceH; |
Handle tmpDataH; |
thisResourceH = Get1Resource(theType, theID); |
err = CheckResError(thisResourceH); |
if (err == noErr) { |
err = PtrToXHand(dataPtr, thisResourceH, dataSize); |
if (err == noErr) { |
ChangedResource(thisResourceH); |
err = ResError(); |
} |
} else if (err == resNotFound) { |
err = PtrToHand(dataPtr, &tmpDataH, dataSize); |
if (err == noErr) { |
AddResource(tmpDataH, theType, theID, "\p"); |
err = ResError(); |
if (err != noErr) { |
DisposeHandle(tmpDataH); |
MoreAssertQ(MemError() == noErr); |
} |
} |
} |
return err; |
} |
static OSStatus WritePackedPrefsToFile(OSType protocol, SInt16 configID, Handle packedPrefs) |
// This is a utility routine that simply writes the entire |
// set of packed preferences to the configuration specified |
// by configID of the current resource file. |
{ |
OSStatus err; |
SInt8 s; |
ByteCount cookie; |
OSType prefType; |
ByteCount prefSize; |
void *prefPtr; |
Handle cnamHandle; |
MoreAssertQ(packedPrefs != nil); |
// Lock down the packed preferences. |
s = HGetState(packedPrefs); |
HLock(packedPrefs); |
MoreAssertQ(MemError() == noErr); |
// Iterate through each preference, write it out to the database. |
err = noErr; |
cookie = 0; |
do { |
WriterGetNextPref(&cookie, *packedPrefs, &prefType, &prefPtr, &prefSize); |
switch (prefType) { |
case kOTCfgUserVisibleNamePref: |
// We have to handle the 'pnam' preference as a special |
// case. In legacy files, the user-visible name of the |
// configuration is stored as the resource name of the |
// 'cnam' resource. So we need to first write a 'cnam' |
// resource then change its name to the name given as the |
// value of the 'pnam' preference. |
// ¥¥¥ Gotcha ¥¥¥ |
// The following switch is a bit of a hack. It seems that |
// the OT preference files use a slightly different format |
// from the Remote Access files. Specifically, ARA puts |
// the configuration ID of the configuration into the |
// resource data of the 'cnam' resource, but OT preference |
// files leave the 'cnam' resource empty. I'm faithfully |
// emulating this behaviour, even though I'm pretty sure that |
// ARA doesn't care about the contents of the resource |
// (I've seen plenty of preference files where the resource |
// contents is wrong and ARA still seems to work) primarily |
// to prevent the legacy file format drifting any further |
// than it also has. I default to the OT mechanism because |
// I'm pretty sure that no one else is going to depend on this. |
switch (protocol) { |
case kOTCfgTypeRemote: |
case kOTCfgTypeModem: |
err = Set1ResourceFromPtr(kOTCfgCompatNamePref, configID, &configID, sizeof(configID)); |
break; |
default: |
err = Set1ResourceFromPtr(kOTCfgCompatNamePref, configID, &configID, 0); |
break; |
} |
// Now update the resource name with the user-visible name |
// of the configuration. |
if (err == noErr) { |
cnamHandle = Get1Resource(kOTCfgCompatNamePref, configID); |
// Assert: We just created this resource, where did it go! |
MoreAssertQ( CheckResError(cnamHandle) == noErr ); |
if (cnamHandle != nil) { |
// Assert: prefPtr should point to a Pascal string. |
MoreAssertQ( *((UInt8 *) prefPtr) + 1 == prefSize ); |
SetResInfo(cnamHandle, configID, prefPtr); |
} |
} |
break; |
case 0: |
// do nothing, termination condition |
break; |
default: |
err = Set1ResourceFromPtr(prefType, configID, prefPtr, prefSize); |
break; |
} |
} while (err == noErr && prefType != 0); |
// Clean up. |
HSetState(packedPrefs, s); |
return err; |
} |
static SInt16 GenerateUniqueNewConfigID(void) |
// Generate a unique ID for the new configuration. The loop |
// might seem a bit gung ho, but it's actually fairly fail secure. |
// As a rule, if it fails Get1Resource will return nil, and we |
// fall out. |
{ |
SInt16 newConfigID; |
newConfigID = 128; |
while ( Get1Resource(kOTCfgCompatNamePref, newConfigID) != nil ) { |
newConfigID += 1; |
} |
return newConfigID; |
} |
static void DeleteHalfCreatedConfig(Handle packedPrefs, SInt16 configID) |
// If we get halfway through creating a configuration and fail, |
// we have to make sure there aren't any bits of the new configuration |
// left lying around in the preferences file. This routine does this. |
// It shares a lot of code with DeleteConfigurationFile, but not |
// really enough to justify one calling the other. The key differences |
// are: a) this routine assumes the preference file is already open, and |
// b) this routine doesn't raise an error if the preference doesn't exist. |
{ |
OSStatus err; |
UInt32 cookie; |
OSType prefType; |
void *junkPtr; |
ByteCount junkSize; |
Handle prefToDelete; |
err = noErr; |
cookie = 0; |
do { |
WriterGetNextPref(&cookie, *packedPrefs, &prefType, &junkPtr, &junkSize); |
if (prefType != 0) { |
if (prefType == kOTCfgUserVisibleNamePref) { |
prefType = kOTCfgCompatNamePref; |
} |
SetResLoad(false); |
prefToDelete = Get1Resource(prefType, configID); |
err = CheckResError(prefToDelete); |
SetResLoad(true); |
if (err == noErr) { |
RemoveResource(prefToDelete); |
err = ResError(); |
if (err == noErr) { |
DisposeHandle(prefToDelete); |
MoreAssertQ(MemError() == noErr); |
} |
} else if (err == resNotFound) { |
err = noErr; |
} |
} |
} while (err == noErr && prefType != 0); |
MoreAssertQ(err == noErr); |
} |
static OSStatus CreateConfigurationFile(const NSHConfigurationDigest *configurationDigest, |
NSHConfigurationEntry *createdConfig) |
// Implementation of NSHCreateConfiguration which uses the legacy |
// preference files. See NSHCreateConfiguration's comment in header |
// file for interface specification. |
{ |
OSStatus err; |
OSStatus err2; |
SInt16 oldResFile; |
SInt16 refNum; |
SInt16 newConfigID; |
Handle packedPrefs; |
MoreAssertQ(configurationDigest != nil); |
packedPrefs = nil; |
err = OpenNetworkPrefFile(configurationDigest->fCommon.fProtocol, fsRdWrPerm, &oldResFile, &refNum); |
if (err == noErr) { |
newConfigID = GenerateUniqueNewConfigID(); |
// Fill out the entity with the information from the digest. |
err = BuildPackedPrefsFromConfigurationDigest(configurationDigest, true, &packedPrefs); |
if (err == noErr) { |
err = WritePackedPrefsToFile(configurationDigest->fCommon.fProtocol, newConfigID, packedPrefs); |
if (err != noErr) { |
// If we get an error, make sure to eelete any bits of the |
// half-created config. |
DeleteHalfCreatedConfig(packedPrefs, newConfigID); |
} |
} |
err2 = CloseNetworkPrefFile(oldResFile, refNum); |
if (err == noErr) { |
err = err2; |
} |
} |
// If the client requested the NSHConfigurationEntry for the new |
// configuration, give it to them. |
if (err == noErr && createdConfig != nil) { |
OTMemzero(createdConfig, sizeof(*createdConfig)); |
BlockMoveData(configurationDigest->fCommon.fConfigName, createdConfig->name, sizeof(createdConfig->name)); |
createdConfig->cookie = newConfigID; |
createdConfig->cookie4 = configurationDigest->fCommon.fProtocol; |
} |
// Clean up. |
if (packedPrefs != nil) { |
DisposeHandle(packedPrefs); |
MoreAssertQ(MemError() == noErr); |
} |
return err; |
} |
static OSStatus DuplicateConfigurationFile(const NSHConfigurationEntry *config, |
ConstStr255Param newConfigName, |
NSHConfigurationEntry *createdConfig) |
// Implementation of NSHDuplicateConfiguration which uses the legacy |
// preference files. See NSHDuplicateConfiguration's comment in header |
// file for interface specification. |
{ |
OSStatus err; |
OSStatus err2; |
SInt16 oldResFile; |
SInt16 refNum; |
Handle packedPrefs; |
SInt16 newConfigID; |
Handle cnamHandle; |
MoreAssertQ(config != nil); |
MoreAssertQ(newConfigName != nil); |
packedPrefs = nil; |
err = OpenNetworkPrefFile(config->cookie4, fsRdWrPerm, &oldResFile, &refNum); |
if (err == noErr) { |
newConfigID = GenerateUniqueNewConfigID(); |
// Read the preferences from the original configuration |
// and write them to the new configuration. |
if (err == noErr) { |
err = ReadPackedPrefsFromFile(config->cookie, &packedPrefs); |
} |
if (err == noErr) { |
err = WritePackedPrefsToFile(config->cookie4, newConfigID, packedPrefs); |
} |
// Set the name of the new configuration. |
if (err == noErr) { |
cnamHandle = Get1Resource(kOTCfgCompatNamePref, newConfigID); |
err = CheckResError(cnamHandle); |
if (err == noErr) { |
SetResInfo(cnamHandle, newConfigID, newConfigName); |
} |
} |
err2 = CloseNetworkPrefFile(oldResFile, refNum); |
if (err == noErr) { |
err = err2; |
} |
} |
// If the client requested the NSHConfigurationEntry for the new |
// configuration, give it to them. |
if (err == noErr && createdConfig != nil) { |
*createdConfig = *config; |
BlockMoveData(newConfigName, createdConfig->name, sizeof(createdConfig->name)); |
createdConfig->cookie = newConfigID; |
} |
// Clean up. |
if (packedPrefs != nil) { |
DisposeHandle(packedPrefs); |
MoreAssertQ(MemError() == noErr); |
} |
return err; |
} |
static OSStatus GetConfigurationFile(const NSHConfigurationEntry *config, |
NSHConfigurationDigest *configurationDigest) |
// Implementation of NSHGetConfiguration which uses the legacy |
// preference files. See NSHGetConfiguration's comment in header |
// file for interface specification. |
{ |
OSStatus err; |
OSStatus err2; |
SInt16 oldResFile; |
SInt16 refNum; |
Handle packedPrefs; |
MoreAssertQ(config != nil); |
MoreAssertQ(configurationDigest != nil); |
packedPrefs = nil; |
err = OpenNetworkPrefFile(config->cookie4, fsRdWrPerm, &oldResFile, &refNum); |
if (err == noErr) { |
err = ReadPackedPrefsFromFile(config->cookie, &packedPrefs); |
if (err == noErr) { |
err = BuildConfigurationDigestFromPackedPrefs(config->cookie4, packedPrefs, configurationDigest); |
} |
err2 = CloseNetworkPrefFile(oldResFile, refNum); |
if (err == noErr) { |
err = err2; |
} |
} |
if (packedPrefs != nil) { |
DisposeHandle(packedPrefs); |
MoreAssertQ(MemError() == noErr); |
} |
return err; |
} |
static OSStatus SetConfigurationFile(const NSHConfigurationEntry *config, |
const NSHConfigurationDigest *configurationDigest) |
// Implementation of NSHSetConfiguration which uses the legacy |
// preference files. See NSHSetConfiguration's comment in header |
// file for interface specification. |
{ |
OSStatus err; |
OSStatus err2; |
SInt16 oldResFile; |
SInt16 refNum; |
Handle packedPrefs; |
SInt16 currentConfigID; |
MoreAssertQ(config != nil); |
MoreAssertQ(configurationDigest != nil); |
packedPrefs = nil; |
err = OpenNetworkPrefFile(config->cookie4, fsRdWrPerm, &oldResFile, &refNum); |
if (err == noErr) { |
err = BuildPackedPrefsFromConfigurationDigest(configurationDigest, false, &packedPrefs); |
if (err == noErr) { |
err = WritePackedPrefsToFile(config->cookie4, config->cookie, packedPrefs); |
} |
// If we're modifying the current configuration, tell the protocol |
// stacks about it. |
if (err == noErr) { |
err = GetCurrentConfigFromPrefFile(¤tConfigID); |
if (err == noErr) { |
if (config->cookie == currentConfigID) { |
err = CommitChangesToPrefFile(config->cookie4, refNum, config->cookie); |
} |
} |
} |
err2 = CloseNetworkPrefFile(oldResFile, refNum); |
if (err == noErr) { |
err = err2; |
} |
} |
// Clean up. |
if (packedPrefs != nil) { |
DisposeHandle(packedPrefs); |
MoreAssertQ(MemError() == noErr); |
} |
return err; |
} |
static OSStatus DeleteConfigurationFile(const NSHConfigurationEntry *config) |
// Implementation of NSHDeleteConfiguration which uses the legacy |
// preference files. See NSHDeleteConfiguration's comment in header |
// file for interface specification. |
{ |
OSStatus err; |
OSStatus err2; |
SInt16 oldResFile; |
SInt16 refNum; |
SInt16 currentConfigID; |
Handle packedPrefs; |
UInt32 cookie; |
OSType prefType; |
void *junkPtr; |
ByteCount junkSize; |
Handle prefToDelete; |
MoreAssertQ(config != nil); |
packedPrefs = nil; |
err = OpenNetworkPrefFile(config->cookie4, fsRdWrPerm, &oldResFile, &refNum); |
if (err == noErr) { |
// You're not allowed to delete an active configuration. |
err = GetCurrentConfigFromPrefFile(¤tConfigID); |
if (err == noErr) { |
if (config->cookie == currentConfigID) { |
err = -11; |
} |
} |
// Now read a slice out of the preference file, ie all |
// the resource with ID of config->cookie regardless of their |
// type. We do this in before starting to delete stuff because |
// a) we already have the code (we share it with various other |
// routines) and b) the API to the Resource Manager is based |
// on indexes, so if you start deleting stuff midway through |
// things get very confusing. |
// |
// This wastes some memory (we read in all the preference data |
// even though we're never going to use it), but that's the price |
// you pay for having me write the code for you (-: |
if (err == noErr) { |
err = ReadPackedPrefsFromFile(config->cookie, &packedPrefs); |
} |
// Now delete each preference in the slice from the resource file. |
// We exercise two little known Resource Manager features here. |
// Firstly, we SetResLoad to false before calling Get1Resource. |
// This saves memory (and time) by preventing the Resource Manager |
// from actually loading the preference data into memory. |
// Secondly, we must DisposeHandle(prefToDelete) after we've |
// successfully removed it from the resource file. Removing |
// a handle from a resource file leaves you with a memory |
// handle which you must dispose lest you leak. |
if (err == noErr) { |
HLock(packedPrefs); // Unilaterally lock because we're going to dispose at the end of this routine anyway. |
cookie = 0; |
do { |
WriterGetNextPref(&cookie, *packedPrefs, &prefType, &junkPtr, &junkSize); |
if (prefType != 0) { |
if (prefType == kOTCfgUserVisibleNamePref) { |
prefType = kOTCfgCompatNamePref; |
} |
SetResLoad(false); |
prefToDelete = Get1Resource(prefType, config->cookie); |
err = CheckResError(prefToDelete); |
SetResLoad(true); |
if (err == noErr) { |
RemoveResource(prefToDelete); |
err = ResError(); |
} |
if (err == noErr) { |
DisposeHandle(prefToDelete); |
MoreAssertQ(MemError() == noErr); |
} |
} |
} while (err == noErr && prefType != 0); |
} |
err2 = CloseNetworkPrefFile(oldResFile, refNum); |
if (err == noErr) { |
err = err2; |
} |
} |
// Clean up. |
if (packedPrefs != nil) { |
DisposeHandle(packedPrefs); |
MoreAssertQ(MemError() == noErr); |
} |
return err; |
} |
static OSStatus RenameConfigurationFile(const NSHConfigurationEntry *config, |
ConstStr255Param newConfigName, |
NSHConfigurationEntry *newConfig) |
// Implementation of NSHRenameConfiguration which uses the legacy |
// preference files. See NSHRenameConfiguration's comment in header |
// file for interface specification. |
{ |
OSStatus err; |
OSStatus err2; |
SInt16 oldResFile; |
SInt16 refNum; |
Handle cnamHandle; |
MoreAssertQ(config != nil); |
MoreAssertQ(newConfigName != nil); |
err = OpenNetworkPrefFile(config->cookie4, fsRdWrPerm, &oldResFile, &refNum); |
if (err == noErr) { |
cnamHandle = Get1Resource(kOTCfgCompatNamePref, config->cookie); |
err = CheckResError(cnamHandle); |
if (err == noErr) { |
SetResInfo(cnamHandle, config->cookie, newConfigName); |
} |
err2 = CloseNetworkPrefFile(oldResFile, refNum); |
if (err == noErr) { |
err = err2; |
} |
} |
// If the client requested the NSHConfigurationEntry for the renamed |
// configuration, give it to them. |
if (err == noErr && newConfig != nil) { |
*newConfig = *config; |
BlockMoveData(newConfigName, newConfig->name, sizeof(newConfig->name)); |
} |
return err; |
} |
///////////////////////////////////////////////////////////////// |
#pragma mark ----- Internet Setup Abstraction ------ |
extern pascal OSStatus NSHCreateConfiguration(const NSHConfigurationDigest *configurationDigest, |
NSHConfigurationEntry *createdConfig) |
// See comments in interface part. |
{ |
OSStatus err; |
if ( kUseNetworkSetup && IsNetworkSetupAvailable() ) { |
#if TARGET_RT_MAC_CFM |
err = CreateConfigurationDatabase(configurationDigest, createdConfig); |
#else |
// Network Setup has no Mixed Mode glue. When running |
// code on a PowerPC with Network Setup available, you |
// should either compile your code as Fat or, if that's |
// infeasible, write your own Mixed Mode glue. |
return -5; |
#endif |
} else { |
err = CreateConfigurationFile(configurationDigest, createdConfig); |
} |
return err; |
} |
extern pascal OSStatus NSHDuplicateConfiguration(NSHConfigurationEntry *config, |
ConstStr255Param newConfigName, |
NSHConfigurationEntry *createdConfig) |
// See comments in interface part. |
{ |
OSStatus err; |
if ( kUseNetworkSetup && IsNetworkSetupAvailable() ) { |
#if TARGET_RT_MAC_CFM |
err = DuplicateConfigurationDatabase(config, newConfigName, createdConfig); |
#else |
// Network Setup has no Mixed Mode glue. When running |
// code on a PowerPC with Network Setup available, you |
// should either compile your code as Fat or, if that's |
// infeasible, write your own Mixed Mode glue. |
return -5; |
#endif |
} else { |
err = DuplicateConfigurationFile(config, newConfigName, createdConfig); |
} |
return err; |
} |
extern pascal OSStatus NSHGetConfiguration(const NSHConfigurationEntry *config, |
NSHConfigurationDigest *configurationDigest) |
// See comments in interface part. |
{ |
OSStatus err; |
if ( kUseNetworkSetup && IsNetworkSetupAvailable() ) { |
#if TARGET_RT_MAC_CFM |
err = GetConfigurationDatabase(config, configurationDigest); |
#else |
// Network Setup has no Mixed Mode glue. When running |
// code on a PowerPC with Network Setup available, you |
// should either compile your code as Fat or, if that's |
// infeasible, write your own Mixed Mode glue. |
return -5; |
#endif |
} else { |
err = GetConfigurationFile(config, configurationDigest); |
} |
return err; |
} |
extern pascal OSStatus NSHSetConfiguration(const NSHConfigurationEntry *config, |
const NSHConfigurationDigest *configurationDigest) |
// See comments in interface part. |
{ |
OSStatus err; |
if ( kUseNetworkSetup && IsNetworkSetupAvailable() ) { |
#if TARGET_RT_MAC_CFM |
err = SetConfigurationDatabase(config, configurationDigest); |
#else |
// Network Setup has no Mixed Mode glue. When running |
// code on a PowerPC with Network Setup available, you |
// should either compile your code as Fat or, if that's |
// infeasible, write your own Mixed Mode glue. |
return -5; |
#endif |
} else { |
err = SetConfigurationFile(config, configurationDigest); |
} |
return err; |
} |
extern pascal OSStatus NSHDeleteConfiguration(const NSHConfigurationEntry *config) |
// See comments in interface part. |
{ |
OSStatus err; |
if ( kUseNetworkSetup && IsNetworkSetupAvailable() ) { |
#if TARGET_RT_MAC_CFM |
err = DeleteConfigurationDatabase(config); |
#else |
// Network Setup has no Mixed Mode glue. When running |
// code on a PowerPC with Network Setup available, you |
// should either compile your code as Fat or, if that's |
// infeasible, write your own Mixed Mode glue. |
return -5; |
#endif |
} else { |
err = DeleteConfigurationFile(config); |
} |
return err; |
} |
extern pascal OSStatus NSHRenameConfiguration(const NSHConfigurationEntry *config, |
ConstStr255Param newConfigName, |
NSHConfigurationEntry *newConfig) |
// See comments in interface part. |
{ |
OSStatus err; |
if ( kUseNetworkSetup && IsNetworkSetupAvailable() ) { |
#if TARGET_RT_MAC_CFM |
err = RenameConfigurationDatabase(config, newConfigName, newConfig); |
#else |
// Network Setup has no Mixed Mode glue. When running |
// code on a PowerPC with Network Setup available, you |
// should either compile your code as Fat or, if that's |
// infeasible, write your own Mixed Mode glue. |
return -5; |
#endif |
} else { |
err = RenameConfigurationFile(config, newConfigName, newConfig); |
} |
return err; |
} |
///////////////////////////////////////////////////////////////// |
#pragma mark ----- Database Will Dial ------ |
static OSStatus GetPortNameFromTCPPrefs(Ptr buffer, SInt32 prefSize, char *portName) |
// This routine takes the address and size of an 'iitf' preference |
// and extracts the port name from the first interface. |
{ |
OSStatus err; |
UInt16 interfaceCount; |
Ptr cursor; |
NSHTCPv4ConfigurationDigest firstInterface; |
UInt8 portNameLength; |
// Get the count of interfaces, checking for possibly bogus |
// preference data. |
err = noErr; |
if (prefSize < sizeof(UInt16)) { |
err = -1; |
} |
if (err == noErr) { |
interfaceCount = *((UInt16 *)buffer); |
if (interfaceCount < 1) { |
err = -1; |
} |
} |
// Unpack the first interface out of the 'iitf'. |
if (err == noErr) { |
cursor = buffer + sizeof(UInt16); |
UnpackTCPPrefs(&cursor, &firstInterface); |
// Assert: Did not consume correct number of bytes |
MoreAssertQ( interfaceCount > 1 || (cursor == buffer + prefSize) ); |
} |
// Copy the port name out of the unpacked interface. |
if (err == noErr) { |
portNameLength = firstInterface.fHintPortName[0]; |
if ( portNameLength > kMaxProviderNameLength) { |
err = -1; |
} else { |
// Poor Man's C2PString avoids me having to figure |
// out which wacky library CodeWarrior wants me to link with |
// today! |
BlockMoveData(firstInterface.fHintPortName + 1, portName, portNameLength); |
portName[ portNameLength ] = 0; |
} |
} |
return err; |
} |
static OSStatus GetInfoForTCPEntity(const MNSDatabaseRef *ref, const CfgEntityRef *entityID, |
Boolean *enabled, char *portName) |
// This routine returns the enabled status and port name |
// for the TCP/IP preferences entity described by entityID |
// in the ref database. |
{ |
OSStatus err; |
SInt16 enabledInt; |
Ptr buffer; |
ByteCount prefSize; |
buffer = nil; |
// First return enabled using the simple API. |
err = MNSGetFixedSizePref(ref, entityID, kOTCfgTCPUnloadAttrPref, &enabledInt, sizeof(SInt16)); |
if (err == noErr) { |
*enabled = (enabledInt != 3); |
} |
// Now return the port name. Now call the variable sized |
// API to get the 'iitf' resource and then extract the port name |
// from the preference buffer. |
if (err == noErr) { |
err = MNSGetPref(ref, entityID, kOTCfgTCPInterfacesPref, &buffer, &prefSize); |
} |
if (err == noErr) { |
err = GetPortNameFromTCPPrefs(buffer, prefSize, portName); |
} |
// Clean up. |
if (buffer != nil) { |
DisposePtr(buffer); |
MoreAssertQ(MemError() == noErr); |
} |
return err; |
} |
static OSStatus GetTCPInfoFromDatabase(Boolean *enabled, char *portName) |
// The high-level entry point into the configuration database |
// implementation. We open the database, find the current |
// TCP entity and read the info we need out of that entity. |
{ |
OSStatus err; |
OSStatus err2; |
MNSDatabaseRef ref; |
CfgEntityRef currentTCPEntity; |
err = MNSOpenDatabase(&ref, false); |
if (err == noErr) { |
err = FindCurrentConnection(&ref, kOTCfgTypeTCPv4, ¤tTCPEntity); |
if (err == noErr) { |
err = GetInfoForTCPEntity(&ref, ¤tTCPEntity, enabled, portName); |
} |
err2 = MNSCloseDatabase(&ref, false); |
if (err == noErr) { |
err = err2; |
} |
} |
return err; |
} |
///////////////////////////////////////////////////////////////// |
#pragma mark ----- File Will Dial ------ |
static OSStatus GetTCPInfoFromFile(Boolean *enabled, char *portName) |
// This is the high-level entry point into the direct file |
// access implementation. It simply finds the preferences |
// file and reads the preferences out directly. |
{ |
OSStatus err; |
OSStatus err2; |
SInt16 oldResFile; |
SInt16 refNum; |
Handle unldResource; |
Handle iitfResource; |
SInt8 s; |
SInt16 config; |
err = OpenNetworkPrefFile(kOTCfgTypeTCPv4, fsRdPerm, &oldResFile, &refNum); |
if (err == noErr) { |
err = GetCurrentConfigFromPrefFile(&config); |
if (err == noErr) { |
unldResource = Get1Resource(kOTCfgTCPUnloadAttrPref, config); |
err = CheckResError(unldResource); |
} |
if (err == noErr) { |
*enabled = ( **((SInt16 **) unldResource) != 3); |
} |
if (err == noErr) { |
iitfResource = Get1Resource(kOTCfgTCPInterfacesPref, config); |
err = CheckResError(iitfResource); |
} |
if (err == noErr) { |
s = HGetState(iitfResource); |
HLock(iitfResource); |
err = GetPortNameFromTCPPrefs(*iitfResource, GetHandleSize(iitfResource), portName); |
HSetState(iitfResource, s); |
} |
err2 = CloseNetworkPrefFile(oldResFile, refNum); |
if (err == noErr) { |
err = err2; |
} |
} |
return err; |
} |
///////////////////////////////////////////////////////////////// |
#pragma mark ----- Will Dial Abstraction ----- |
static OSStatus GetTCPInfo(Boolean *enabled, char *portName) |
// A dispatcher. If the config database is available, |
// we call it, otherwise we fall back to reading the |
// preferences file directly. |
{ |
OSStatus err; |
if ( kUseNetworkSetup && IsNetworkSetupAvailable() ) { |
#if TARGET_RT_MAC_CFM |
err = GetTCPInfoFromDatabase(enabled, portName); |
#else |
// Network Setup has no Mixed Mode glue. When running |
// code on a PowerPC with Network Setup available, you |
// should either compile your code as Fat or, if that's |
// infeasible, write your own Mixed Mode glue. |
return -5; |
#endif |
} else { |
err = GetTCPInfoFromFile(enabled, portName); |
} |
return err; |
} |
extern pascal OSStatus NSHTCPWillDial(UInt32 *willDial) |
// The main entry point. We call our core |
// implementation and then generate the result |
// based on the returned information. |
{ |
OSStatus err; |
InetInterfaceInfo info; |
Boolean enabled; |
char currentPortName[kMaxProviderNameSize]; |
OTPortRecord portRecord; |
MoreAssertQ(willDial != nil); |
*willDial = kNSHTCPDialUnknown; |
err = noErr; |
if ( kUseInetInterfaceInfo && OTInetGetInterfaceInfo(&info, kDefaultInetInterface) == noErr) { |
// The TCP/IP stack is already loaded. With the current |
// way TCP/IP is organised, the stack being loaded implies |
// that we're already dialled in. |
*willDial = kNSHTCPDialNo; |
} else { |
err = GetTCPInfo(&enabled, currentPortName); |
if (err == noErr) { |
if (enabled) { |
if ( OTStrEqual(currentPortName, "ddp") ) { |
// A special case for MacIP, because "ddp" does |
// not have an active port if AppleTalk is disabled. |
*willDial = kNSHTCPDialNo; |
} else if ( OTFindPort(&portRecord, currentPortName) ) { |
// We know the port. Look at the device type |
// to decide whether we might dial. |
switch ( OTGetDeviceTypeFromPortRef(portRecord.fRef) ) { |
case kOTADEVDevice: |
case kOTIRTalkDevice: |
case kOTSMDSDevice: |
// Assert: TCP shouldn't be using this link type |
MoreAssertQ(false); |
*willDial = kNSHTCPDialNo; |
break; |
case kOTISDNDevice: |
case kOTATMDevice: |
case kOTSerialDevice: |
case kOTModemDevice: |
// Assert: TCP shouldn't be using this link type |
MoreAssertQ(false); |
*willDial = kNSHTCPDialYes; |
break; |
case kOTEthernetDevice: |
// For Ethernet, special case AOL because, well, they're special. |
if ( OTStrEqual(currentPortName, "AOLLink0") ) { |
*willDial = kNSHTCPDialYes; |
} else { |
*willDial = kNSHTCPDialNo; |
} |
break; |
case kOTLocalTalkDevice: |
case kOTTokenRingDevice: |
case kOTFastEthernetDevice: |
case kOTFDDIDevice: |
case kOTIrDADevice: |
case kOTATMSNAPDevice: |
case kOTFibreChannelDevice: |
case kOTFireWireDevice: |
*willDial = kNSHTCPDialNo; |
break; |
case kOTMDEVDevice: |
case kOTSLIPDevice: |
case kOTPPPDevice: |
*willDial = kNSHTCPDialYes; |
break; |
default: |
MoreAssertQ(*willDial == kNSHTCPDialUnknown); |
break; |
} |
} else { |
err = -1; |
} |
} else { |
*willDial = kNSHTCPDialTCPDisabled; |
} |
} |
} |
return err; |
} |
///////////////////////////////////////////////////////////////// |
#pragma mark ----- DHCP Release ----- |
static pascal OSStatus DHCPReleaseDatabase(void) |
// Implementation of NSHDHCPRelease which uses the Network Setup |
// database. See NSHDHCPRelease's comment in header |
// file for interface specification. |
{ |
OSStatus err; |
OSStatus err2; |
MNSDatabaseRef ref; |
CfgEntityRef currentTCPEntity; |
OTCfgTCPUnloadAttr oldUnloadAttr; |
OTCfgTCPUnloadAttr newUnloadAttr; |
const UInt8 dummyPref[16] = {0}; |
// Start by forcing the TCP/IP stack to unload (by deactivating it), saving |
// the old state in oldUnloadAttr. |
err = MNSOpenDatabase(&ref, true); |
if (err == noErr) { |
err = FindCurrentConnection(&ref, kOTCfgTypeTCPv4, ¤tTCPEntity); |
if (err == noErr) { |
err = MNSGetFixedSizePref(&ref, ¤tTCPEntity, kOTCfgTCPUnloadAttrPref, &oldUnloadAttr, sizeof(oldUnloadAttr)); |
} |
if (err == noErr) { |
newUnloadAttr = kOTCfgTCPInactive; |
err = MNSSetPref(&ref, ¤tTCPEntity, kOTCfgTCPUnloadAttrPref, &newUnloadAttr, sizeof(newUnloadAttr)); |
} |
err2 = MNSCloseDatabase(&ref, err == noErr); |
if (err == noErr) { |
err = err2; |
} |
} |
// Then reactive the TCP/IP stack, zeroing the 'dclt' preference in the process. |
if (err == noErr) { |
err = MNSOpenDatabase(&ref, true); |
if (err == noErr) { |
err = FindCurrentConnection(&ref, kOTCfgTypeTCPv4, ¤tTCPEntity); |
if (err == noErr) { |
newUnloadAttr = kOTCfgTCPInactive; |
err = MNSSetPref(&ref, ¤tTCPEntity, kOTCfgTCPUnloadAttrPref, &oldUnloadAttr, sizeof(oldUnloadAttr)); |
} |
if (err == noErr) { |
err = MNSSetPref(&ref, ¤tTCPEntity, kOTCfgTCPDHCPLeaseInfoPref, dummyPref, sizeof(dummyPref)); |
} |
err2 = MNSCloseDatabase(&ref, err == noErr); |
if (err == noErr) { |
err = err2; |
} |
} |
} |
return err; |
} |
static pascal OSStatus DHCPReleaseFile(void) |
// Implementation of NSHDHCPRelease which uses the legacy |
// preference files. See NSHDHCPRelease's comment in header |
// file for interface specification. |
{ |
OSStatus err; |
OSStatus err2; |
SInt16 refNum; |
SInt16 oldResFile; |
OTCfgTCPUnloadAttr **unldResourceH; |
SInt16 config; |
OTCfgTCPUnloadAttr oldUnloadAttr; |
Handle dcltResourceH; |
err = OpenNetworkPrefFile(kOTCfgTypeTCPv4, fsRdWrPerm, &oldResFile, &refNum); |
if (err == noErr) { |
err = GetCurrentConfigFromPrefFile(&config); |
// Start by forcing the TCP/IP stack to unload (by deactivating it), saving |
// the old state in oldUnloadAttr. |
if (err == noErr) { |
unldResourceH = (OTCfgTCPUnloadAttr **) Get1Resource(kOTCfgTCPUnloadAttrPref, config); |
err = CheckResError(unldResourceH); |
} |
if (err == noErr && GetHandleSize( (Handle) unldResourceH) != sizeof(OTCfgTCPUnloadAttr)) { |
err = -1; |
} |
if (err == noErr) { |
oldUnloadAttr = **unldResourceH; |
**unldResourceH = kOTCfgTCPInactive; |
ChangedResource( (Handle) unldResourceH); |
err = ResError(); |
if (err == noErr) { |
UpdateResFile(refNum); |
err = ResError(); |
} |
if (err == noErr) { |
err = CommitChangesToPrefFile(kOTCfgTypeTCPv4, refNum, config); |
} |
} |
// ¥¥¥ Gotcha ¥¥¥ |
// In some circumstances, CommitChangesToPrefFile can close the "TCP/IP Preferences" |
// file. The following is a hackaround to detect and safely fail in this case. |
// I haven't implemented the full workaround, which would be to walk the resource |
// chain looking to see whether my file has been closed because this problem should |
// never happen in practice. |
// My justification? Well, the code which accidentally closes |
// the "TCP/IP Preferences" file was added in Mac OS 8.5 as part of the "maintain |
// the DHCP lease over reboots" effort. But Mac OS 8.5 and up include the Network |
// Setup API, so you shouldn't be running this routine on those systems anyway. |
// |
// I only discovered this problem because I set kUseNetworkSetup to false to test |
// the "backward compatibility" code on an Mac OS 8.6 system. |
// |
// It turns out that Network Setup is bitten by this problem as well, but it's |
// less paranoid error checking fails to detect this case. I've filed a bug |
// to get OT fixed. [Radar ID 2354522] |
if (err == noErr) { |
if ( CurResFile() != refNum ) { |
MoreAssertQ(false); |
return -2; |
} |
} |
// Then reactive the TCP/IP stack, zeroing the 'dclt' preference in the process. |
// Re-get the 'unld' resource because the TCP/IP stack calls ReleaseResource |
// on it when it reconfigures. |
if (err == noErr) { |
unldResourceH = (OTCfgTCPUnloadAttr **) Get1Resource(kOTCfgTCPUnloadAttrPref, config); |
err = CheckResError(unldResourceH); |
} |
if (err == noErr && GetHandleSize( (Handle) unldResourceH) != sizeof(OTCfgTCPUnloadAttr)) { |
err = -1; |
} |
if (err == noErr) { |
**unldResourceH = oldUnloadAttr; |
ChangedResource( (Handle) unldResourceH); |
err = ResError(); |
} |
if (err == noErr) { |
dcltResourceH = Get1Resource(kOTCfgTCPDHCPLeaseInfoPref, config); |
err = CheckResError(dcltResourceH); |
} |
if (err == noErr) { |
// In current OT builds, the 'dclt' preference is 16 bytes. |
// If this changes, we want to know about it. |
MoreAssertQ(GetHandleSize(dcltResourceH) == 16); |
MoreBlockZero(*dcltResourceH, GetHandleSize(dcltResourceH)); |
ChangedResource(dcltResourceH); |
err = ResError(); |
} else if (err == resNotFound) { |
err = noErr; |
} |
if (err == noErr) { |
UpdateResFile(refNum); |
err = ResError(); |
} |
if (err == noErr) { |
err = CommitChangesToPrefFile(kOTCfgTypeTCPv4, refNum, config); |
} |
err2 = CloseNetworkPrefFile(oldResFile, refNum); |
if (err == noErr) { |
err = err2; |
} |
} |
return err; |
} |
extern pascal OSStatus NSHDHCPRelease(void) |
// See comments in interface part. |
{ |
OSStatus err; |
if ( kUseNetworkSetup && IsNetworkSetupAvailable() ) { |
#if TARGET_RT_MAC_CFM |
err = DHCPReleaseDatabase(); |
#else |
// Network Setup has no Mixed Mode glue. When running |
// code on a PowerPC with Network Setup available, you |
// should either compile your code as Fat or, if that's |
// infeasible, write your own Mixed Mode glue. |
return -5; |
#endif |
} else { |
err = DHCPReleaseFile(); |
} |
return err; |
} |
///////////////////////////////////////////////////////////////// |
#pragma mark ----- AppleTalk On/Off using Database ----- |
static OSStatus IsAppleTalkActiveDatabase(Boolean *active) |
// Implementation of NSHIsAppleTalkActive which uses the Network Setup |
// database. See NSHIsAppleTalkActive's comment in header |
// file for interface specification. |
{ |
OSStatus err; |
OSStatus err2; |
MNSDatabaseRef ref; |
CfgEntityRef currentAppleTalkEntity; |
OTCfgATalkGeneral atpfPref; |
err = MNSOpenDatabase(&ref, false); |
if (err == noErr) { |
err = FindCurrentConnection(&ref, kOTCfgTypeAppleTalk, ¤tAppleTalkEntity); |
if (err == noErr) { |
err = MNSGetFixedSizePref(&ref, ¤tAppleTalkEntity, kOTCfgATalkGeneralPref, &atpfPref, sizeof(atpfPref)); |
} |
if (err == noErr) { |
*active = (atpfPref.ddpPrefs.fLoadType != 0); |
} |
err2 = MNSCloseDatabase(&ref, false); |
if (err == noErr) { |
err = err2; |
} |
} |
return err; |
} |
static OSStatus SetAppleTalkActiveDatabase(Boolean active) |
// Implementation of NSHSetAppleTalkActive which uses the Network Setup |
// database. See NSHSetAppleTalkActive's comment in header |
// file for interface specification. |
{ |
OSStatus err; |
OSStatus err2; |
MNSDatabaseRef ref; |
CfgEntityRef currentAppleTalkEntity; |
OTCfgATalkGeneral atpfPref; |
Boolean changeNeeded; |
UInt8 newValue; |
err = MNSOpenDatabase(&ref, true); |
if (err == noErr) { |
changeNeeded = true; |
err = FindCurrentConnection(&ref, kOTCfgTypeAppleTalk, ¤tAppleTalkEntity); |
if (err == noErr) { |
err = MNSGetFixedSizePref(&ref, ¤tAppleTalkEntity, kOTCfgATalkGeneralPref, &atpfPref, sizeof(atpfPref)); |
} |
if (err == noErr) { |
if (active) { |
newValue = kOTCfgATalkActive; |
} else { |
newValue = kOTCfgATalkInactive; |
} |
changeNeeded = (newValue != atpfPref.ddpPrefs.fLoadType); |
if (changeNeeded) { |
atpfPref.ddpPrefs.fLoadType = newValue; |
err = MNSSetPref(&ref, ¤tAppleTalkEntity, kOTCfgATalkGeneralPref, &atpfPref, sizeof(atpfPref)); |
} |
} |
err2 = MNSCloseDatabase(&ref, (err == noErr) && changeNeeded ); |
if (err == noErr) { |
err = err2; |
} |
} |
return err; |
} |
///////////////////////////////////////////////////////////////// |
#pragma mark ----- AppleTalk On/Off using File ----- |
static OSStatus IsAppleTalkActiveFile(Boolean *active) |
// Implementation of NSHIsAppleTalkActive which uses the legacy |
// preference files. See NSHIsAppleTalkActive's comment in header |
// file for interface specification. |
{ |
OSStatus err; |
OSStatus err2; |
SInt16 oldResFile; |
SInt16 refNum; |
Handle atpfResource; |
SInt16 config; |
err = OpenNetworkPrefFile(kOTCfgTypeAppleTalk, fsRdPerm, &oldResFile, &refNum); |
if (err == noErr) { |
err = GetCurrentConfigFromPrefFile(&config); |
if (err == noErr) { |
atpfResource = Get1Resource(kOTCfgATalkGeneralPref, config); |
err = CheckResError(atpfResource); |
} |
if (err == noErr && GetHandleSize(atpfResource) != sizeof(OTCfgATalkGeneral)) { |
err = -1; |
} |
if (err == noErr) { |
*active = (**(OTCfgATalkGeneral **) atpfResource).ddpPrefs.fLoadType != 0; |
} |
err2 = CloseNetworkPrefFile(oldResFile, refNum); |
if (err == noErr) { |
err = err2; |
} |
} |
return err; |
} |
static OSStatus SetAppleTalkActiveFile(Boolean active) |
// Implementation of NSHSetAppleTalkActive which uses the legacy |
// preference files. See NSHSetAppleTalkActive's comment in header |
// file for interface specification. |
{ |
OSStatus err; |
OSStatus err2; |
SInt16 refNum; |
SInt16 oldResFile; |
Handle atpfResource; |
SInt16 config; |
Boolean changeNeeded; |
UInt8 newValue; |
err = OpenNetworkPrefFile(kOTCfgTypeAppleTalk, fsRdWrPerm, &oldResFile, &refNum); |
if (err == noErr) { |
err = GetCurrentConfigFromPrefFile(&config); |
if (err == noErr) { |
atpfResource = Get1Resource(kOTCfgATalkGeneralPref, config); |
err = CheckResError(atpfResource); |
} |
if (err == noErr && GetHandleSize(atpfResource) != sizeof(OTCfgATalkGeneral)) { |
err = -1; |
} |
if (err == noErr) { |
if (active) { |
newValue = kOTCfgATalkActive; |
} else { |
newValue = kOTCfgATalkInactive; |
} |
changeNeeded = (newValue != (**(OTCfgATalkGeneral **) atpfResource).ddpPrefs.fLoadType); |
if (changeNeeded) { |
(**(OTCfgATalkGeneral **) atpfResource).ddpPrefs.fLoadType = newValue; |
ChangedResource(atpfResource); |
err = ResError(); |
if (err == noErr) { |
UpdateResFile(refNum); |
err = ResError(); |
} |
if (err == noErr) { |
err = CommitChangesToPrefFile(kOTCfgTypeAppleTalk, refNum, config); |
} |
} |
} |
err2 = CloseNetworkPrefFile(oldResFile, refNum); |
if (err == noErr) { |
err = err2; |
} |
} |
return err; |
} |
///////////////////////////////////////////////////////////////// |
#pragma mark ----- AppleTalk On/Off Abtraction ----- |
extern pascal OSStatus NSHIsAppleTalkActive(Boolean *active) |
// See comments in interface part. |
{ |
OSStatus err; |
if ( kUseNetworkSetup && IsNetworkSetupAvailable() ) { |
#if TARGET_RT_MAC_CFM |
err = IsAppleTalkActiveDatabase(active); |
#else |
// Network Setup has no Mixed Mode glue. When running |
// code on a PowerPC with Network Setup available, you |
// should either compile your code as Fat or, if that's |
// infeasible, write your own Mixed Mode glue. |
return -5; |
#endif |
} else { |
err = IsAppleTalkActiveFile(active); |
} |
return err; |
} |
extern pascal OSStatus HSHSetAppleTalkActive(Boolean active) |
// See comments in interface part. |
{ |
OSStatus err; |
if ( kUseNetworkSetup && IsNetworkSetupAvailable() ) { |
#if TARGET_RT_MAC_CFM |
err = SetAppleTalkActiveDatabase(active); |
#else |
// Network Setup has no Mixed Mode glue. When running |
// code on a PowerPC with Network Setup available, you |
// should either compile your code as Fat or, if that's |
// infeasible, write your own Mixed Mode glue. |
return -5; |
#endif |
} else { |
err = SetAppleTalkActiveFile(active); |
} |
return err; |
} |
///////////////////////////////////////////////////////////////// |
#pragma mark ----- Remote Access Password Encode ----- |
static OSStatus EncodeRemotePasswordNetworkSetup( |
ConstStr255Param userName, |
ConstStr255Param password, |
Str255 encodedPassword) |
// Implementation of NSHEncodeRemotePassword which uses the Network Setup |
// routines added in Mac OS 9.0. See NSHEncodeRemotePassword's comment in |
// header file for interface specification. |
{ |
MoreBlockZero(encodedPassword, sizeof(Str255)); |
BlockMoveData(password + 1, encodedPassword, password[0]); |
(void) OTCfgEncrypt( (UInt8 *) userName, encodedPassword, sizeof(Str255)); |
return noErr; |
} |
enum { |
_RemoteAccess = 0xAA5B |
}; |
static OSStatus EncodeRemotePasswordARA( |
ConstStr255Param userName, |
ConstStr255Param password, |
Str255 encodedPassword) |
// Implementation of NSHEncodeRemotePassword which uses the ARA 2.x |
// API (which is also emulated by ARA 3.x). See NSHEncodeRemotePassword's |
// comment in header file for interface specification. |
{ |
OSStatus err; |
TRemoteAccessPasswordMunger pb; |
UInt8 chunkOfPassword[9]; |
ByteCount offset; |
// Only call PBRemoteAccess if it's implemented! |
err = unimpErr; |
if ( GetToolboxTrapAddress(_RemoteAccess) != GetToolboxTrapAddress(_Unimplemented) ) { |
// Zero the encoded password buffer and copy the |
// password (sans length byte) into it. |
OTMemzero(encodedPassword, sizeof(Str255)); |
BlockMoveData(&password[1], encodedPassword, password[0]); |
// Now walk through the password in chunks, copying |
// a chunk into the chunkOfPassword buffer, encrypting |
// that buffer with the ARA API, and then copying the |
// buffer back out to the encrypted password. |
// Note that the for loop increments offset by 8 each time. |
for (offset = 0; offset < 256 ; offset += 8) { |
// Copy a chunk of the password into chunkOfPassword. |
BlockMoveData(encodedPassword + offset, &chunkOfPassword[1], 8); |
chunkOfPassword[0] = 8; |
// Setup the parameter block for a remote access API call. |
pb.csCode = RAM_EXTENDED_CALL; |
pb.resultStrPtr = nil; |
pb.extendedType = (char*) REMOTEACCESSNAME; |
pb.extendedCode = CmdRemoteAccess_PassWordMunger; |
pb.userNamePtr = (UInt8 *) userName; |
pb.passWordPtr = chunkOfPassword; |
pb.reserved = 0; |
err = MorePBRemoteAccess((TPRemoteAccessParamBlock) &pb, false); |
if (err == noErr) { |
err = pb.ioResult; |
} |
if (err == noErr) { |
// Copy the encrypted chunk of password back out into |
// encodedPassword. |
BlockMoveData(&chunkOfPassword[1], encodedPassword + offset, 8); |
} else { |
break; |
} |
} |
// Tidy up encoded password. Move the encrypted data up |
// one byte and insert a zero length byte. Don't ask me |
// why this is required, I didn't invent this scheme (-: |
BlockMoveData(encodedPassword, encodedPassword + 1, sizeof(Str255) - 1); |
encodedPassword[0] = 0; |
} |
return err; |
} |
extern pascal OSStatus NSHEncodeRemotePassword( |
ConstStr255Param userName, |
ConstStr255Param password, |
Str255 encodedPassword) |
// See comments in interface part. |
{ |
OSStatus err; |
if ( kUseNetworkSetup && IsNetworkSetupAvailable() ) { |
#if TARGET_RT_MAC_CFM |
if ( (void *) OTCfgEncrypt != (void *) kUnresolvedCFragSymbolAddress ) { |
err = EncodeRemotePasswordNetworkSetup(userName, password, encodedPassword); |
} else |
#endif |
{ |
err = EncodeRemotePasswordARA(userName, password, encodedPassword); |
} |
} else { |
err = EncodeRemotePasswordARA(userName, password, encodedPassword); |
} |
return err; |
} |
///////////////////////////////////////////////////////////////// |
Copyright © 2003 Apple Computer, Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2003-07-22