Created by ic@stairways.com
This document describes the programming interface to the Internet Configuration System, commonly known as Internet Config, or IC for short. You should read this document if you intend to write, or need to maintain, a program that:
The document begins with an introduction to Internet Config. Everyone should read this. It continues with a chapter describing how to use IC to access Internet preference. Everyone should read this too! The third chapter lists the preferences that are currently managed by IC and the forth chapter describes the application programmer interface (API) in detail. The rest of the document consists of appendices: you should probably look over Appendix B, which contains a discussion of some of the complexities of programming with IC.
The goal of Internet Config is to simplify the Macintosh user's experience of the Internet. The primary focus is to reduce the number of times that the user is required to enter information like their email address.
Another important design goal was programmer simplicity. We recognised that IC would not be adopted if it was too complicated to use. Another aspect of this is that IC should be available in all major development environments.
The core of IC is a shared file, the Internet Preferences file, that contains this common preference information and an Internet Configuration application which the user runs to modify these preferences. This design was complicated by the requirement that it be capable of supporting Macs that are shared by various people, or by a single person who has location dependent preferences.
The final important design goal was to allow IC to be extended in the future to support new ideas such as application specific preferences. To achieve this we have complicated IC slightly by introducing an Internet Config Extension. This is a component that can be used to extend IC without relinking existing applications.
See Also: For more information about components, see "Inside Macintosh: More Toolbox Essentials".
As of version 2.0, Internet Config requires System 7.0 or greater, and Component Manager. The system can be used from, and has been tested with, the following development environments:
Note: The Internet Config interface files assume you are using Universal Interfaces 2.0 or higher. If you are not using Universal Interfaces 2.0 or higher, you should download the latest interfaces and libraries from the Apple developer web site. Think Pascal developers must upgrade to Think Pascal 4.5d4 or higher to use Universal Interfaces.
The system is made up of four major parts. The first is the IC API, as defined in the interface file "InternetConfig.[ph]". These provide the declarations required to use IC.
The second part is the component, held in the Internet Config Extension, that implements the functions of the API in an extensible and patchable manner. The Internet Configuration application contains a copy of this extension, and installs and registers the component when it is run.
The third part is the libraries with which you link your program. Classic 68K code links with an MPW object file, "ICGlue.o", that provides glue for three the IC API routines that are not straight component calls. PowerPC and CFM-68K code link with InternetConfigLib. This is a stub library that is linked, at runtime, with the Internet Config Extension itself
Finally there is a preferences file, Internet Preferences, which is usually kept in the Preferences folder. This file holds the actual preference data and is modified by either the component or the glue depending on which system is in use at the time. This files is actually an implementation detail and its presence is only dictated by the current IC implementation. Future versions of IC may store preferences in a completely different manner.
The primary user interface to the Internet Preferences file is the Internet Config application. Although this application can be replaced, it is important that you do not attempt to duplicate its functionality within your own application. Otherwise the focus of your application will be lost; it will become unclear whether your program is a news reader or a preference setting program.
If you dislike the Internet Config application's user interface then you should write a small focused application that replaces it. You can choose to replace it in its entirety or just some component of it. For example, it would be quite sensible to write a small focused application that replaces the Internet Config application's File Mappings dialog with something altogether less modal.
One of the problems with providing an interface for changing preferences is that your interface will have to be modified to keep up with any modifications made to the Internet Config back end. For example, the Internet Config RandomSignature extension will obsolete any user interface you provide for changing the signature.
So, in general, we recommend that you do not provide the ability to change the Internet Preferences within your application. You might want to provide an easy mechanism for launching Internet Config so that the user can change these preferences quickly. The Internet Config API provides an easy mechanism for doing this.
If you do provide a mechanism to change a preference, you should pay special care to the preference's locked attribute. Any extension that is not compatible with a simple preference changing user interface will set the locked attribute. You can test this to disable your preference setting user interface, and thereby remain compatible with these extensions.
If you take this approach, the Internet Config ReadOnly extension is helpful in testing how well your code deals with locked preferences.
A program gets a preference, typically using the
ICGetPref
routine. The program specifies the preference
using a key. A key is a Str255
that
uniquely identifies the preference. Keys are not case sensitive and
all high-bit set characters are either reserved or have a special
meaning. The current list of keys is defined in
Chapter 3.
IC responds to a ICGetPref
request by returning a
chunk of data that is the value of the preference.
Some keys will return data of a fixed size; other data may be of
variable length. There is no practical limit to the size of
preference data.
Each preference also has a set of attributes, a long word of flags that provide additional information about the preference. The currently defined attributes include a locked bit and a volatile bit. The locked bit defines whether a request to modify the key's data will succeed. The volatile bit is discussed in the section on caching.
Each Internet Preferences file can hold multiple sets of preferences that act independently. This is useful for computers that are shared between multiple users, and for computers where one user works in multiple locations.
Each independent set of preferences is known as a profile. At any time, each preferences file has one current profile that determines which preferences programs will read or write by default. There are API routines to switch, add and remove.
One of the most important designed goals of IC was that it be easily extended. This is achieved in three different ways.
Firstly the key space (remember keys are defined by
Str255
) is huge and more keys can be added as more
common preferences are requires.
Secondly IC can be patched by replacing the component with a later, and hopefully improved, version. This component can be a bug fix component, based on the existing design, or it can be an entirely new component, one that stores preferences on a networked server for example.
Thirdly, you can write override components, that partially override an existing component using the Component Manager's capturing facility.
This chapter describes how a normal Internet aware application would access, and even modify, the common Internet preferences. This chapter is important for anyone developing Internet application.
When you start your application, you should call
ICStart
and give it your application creator. If IC
starts correctly, it returns you an ICInstance
. This is
a private type whose only use is to supply back to IC. The value
describes your unique connection to Internet Config. Normally you
would store this instance in a global variable so that it can be
accessed by other Internet Config related routines.
Once you have an ICInstance
, you should configure it
by calling ICFindConfigFile
with default parameters.
When your application shuts down it is important that you call
ICStop
with the ICInstance
returned by
ICStart
. You should not call ICStop
if
ICStart
fails.
This whole process is shown in the listing below.
var gICInstance : ICInstance; procedure Main; var err, junk : ICError; begin err := ICStart(gICInstance, kMyCreator); if err = noErr then begin err := ICFindConfigFile(gICInstance, 0, nil); if err = noErr then begin err := DoMyApplication; end; (* if *) junk := ICStop(gICInstance); end; (* if *) end; (* Main *)
The creator you pass to ICStart
is not used in the
current implementation, but may be used in the future to support
application specific preferences.
The requirement to call ICFindConfigFile
is a
historical artifact of the original way IC handled multiple users on
the same machine.
This section describes some basic preference operations, such as getting normal preferences, monitoring for preference changes, and getting all preferences.
Once you have created your connection to IC, you can proceed to
get preference data. To do this, call ICGetPref
, as
demonstrated in the following routine.
function GetEmailAddress : Str255; var err : ICError; size : longint; result : Str255; junkAttr : ICAttr; begin size := sizeof(result); // max size for returned data err := ICGetPref(gICInstance, kICEmail, junkAttr, @result, size); if err <> noErr then begin result := ''; end; (* if *) GetEmailAddress := result; end; (* GetEmailAddress *)
You can optionally bracket these calls with
ICBegin
/ICEnd
pairs. If you access multiple
preferences, these bracketing calls can significantly speed up the
job. The following routine demonstrates this.
function GetManyPreferences(var email, realname, mailhost : Str255) : ICError; var err, junk : ICError; size : longint; junkAttr : ICAttr; begin err := ICBegin(inst, icReadOnlyPerm); if err = noErr then begin size := sizeof(email); err := ICGetPref(gICInstance, kICEmail, junkAttr, @email, size); if err = noErr then begin size := sizeof(email); err := ICGetPref(gICInstance, kICRealName, junkAttr, @realname, size); end; (* if *) if err = noErr then begin size := sizeof(email); err := ICGetPref(gICInstance, kICSMTPHost, junkAttr, @mailhost, size); end; (* if *) junk := ICEnd(gICInstance); end; (* if *) GetManyPreferences := err; end; (* GetManyPreferences *)
It is important that you call ICEnd
if and only if
ICBegin
does not return an error. It is also important
that you do not allow other applications to run (for example, by
calling WaitNextEvent
) between the ICBegin
and ICEnd
.
The API also contains routines for getting preferences directly to a handle. These routines are described in Section 4.2.
The Internet Preferences file is a shared data structure that can be accessed by multiple programs. This obviously causes consistency problems if your application reads and remembers a preference which is then modified by some other application. Internet Config provides three mechanisms to deal with this problem. These are discussed in order of correctness.
The on demand approach requires that your application read its preferences only when it actually needs them. Because you do not hold copies of the preference, you can safely ignore the coherency problem.
The primary problem with this approach is that it requires you to track down all references to specific preferences and change them to calls to IC. This may or may not be easy depending on how your code base is structured.
The cache watching approach allows you to get a preference and then watch for modifications to that preference. It is centred around the preference seed, which is a number that changes whenever the preference data changes. You should get this seed and remember it immediately after reading your preference information. You should then get the seed again at regular intervals and, if the seed changes, flush any cached preferences. For example, the pseudocode for your application might look like the following.
program MyCacheWatchingProgram; begin start IC get my preferences err := ICGetSeed(gICInstance, gCurrentSeed); while not quit do begin process events err := ICGetSeed(gICInstance, newSeed); if newSeed <> gCurrentSeed then begin reread my preferences gCurrentSeed := newSeed; end; (* if *) end; (* while *) end; (* MyCacheWatchingProgram *)
The cache watching approach is further complicated by the volatile attribute. If you get a preference and it has this attribute set, you should not cache that preference. This feature allows certain preferences to change dynamically without affecting the seed.
The final approach is the ostrich approach. In this mechanism you just get your preferences and ignore caching issues entirely. This approach has the advantage of being the easiest to implement, although it does mean that your application will not work properly at all times. Specifically, your application will not notice when the current profile changes, so you might end up using the wrong user's preferences!
The approach you choose is up to you. We highly recommend the on demand approach but recognise that this may be difficult with an existing code base.
Regardless of which approach you take, you must flush any cached preferences when you launch. The seed value is not valid across reboots.
In some cases you might want to index through all of the
preferences. You can do this using the ICCountPref
and
ICGetIndPref
routines. The following routine shows how
this is done.
procedure DumpKeys; var err : ICError; junk : ICError; ndx : longint; count : longint; key : Str255; begin err := ICBegin(gICInstance, icReadOnlyPerm); if err = noErr then begin err := ICCountPref( gICInstance, count); if err = noErr then begin for ndx := 1 to count do begin err := ICGetIndPref( gICInstance, ndx, key); if err = noErr then begin writeln(key); end; (* if *) end; (* for *) end; (* if *) junk := ICEnd(gICInstance); end; (* if *) end; (* DumpKeys *)
As well as shared preference storage, Internet Config also provides direct support for a number of operations that are commonly performed by Macintosh Internet applications. This includes launching a URL and mapping extensions to file types and vice versa.
The ICLaunchURL
routine combines Internet Config
helper database with the 'GURL'
AppleEvent Suite
standard to provide a simple mechanism for handing off a known URL to
the user's preferred helper. The following sample code demonstrates
how you can do this.
function LaunchURL(url : Str255): ICError; var start, fin : longint; begin start := 0; fin := length(url); LaunchURL := ICLaunchURL(gICInstance, '', @url[1], length(url), start, fin); end; (* LaunchURL *)
One common use for launching a URL is to provide support for the command clicking standard used by many Macintosh Internet applications. The following routine demonstrates how you might use Internet Config to implement command clicking in a TextEdit window.
procedure DoTextClick(teh : TEHandle; event : EventRecord); var err : ICError; selStart, selEnd : longint; textH : Handle; s : SInt8; begin if band(event.modifiers, cmdKey) <> 0 then begin selStart := teh^^.selStart; selEnd := teh^^.selEnd; textH := Handle(TEGetText(teh)); s := HGetState(textH); HLock(textH); err := ICLaunchURL(gICInstance, '', texth^, GetHandleSize(textH), selStart, selEnd ); HSetState( textH, s); if err = noErr then begin TESetSelect(selStart, sel_end, teh); end; (* if *) end else begin TEClick(event.where, band(event.modifiers, shiftKey) <> 0, teh); end; (* if *) end; (* DoTextClick *)
There is another API routine, ICParseURL
, that lets
you parse a URL out of a chunk of text. This is useful if you want to
get the URL without launching the corresponding helper application.
Note: Incorporating this code in your application
would be foolish because ICeTEe, an extension that ships with
Internet Config, implements this as a patch to _TEClick
.
However the code is a good outline for how you might implement
command clicking in a program that doesn't use TextEdit.
Another operation that is commonly performed by Internet applications is to set the type of a file based on its extension. Internet Config allows you to do this very easily, using a routine such as the following.
function SetCorrectFileType(downloadFile : FSSpec) : ICError; var err : ICError; entry : ICMapEntry; info : FInfo; begin err := ICMapFilename(gICInstance, downloadFile.name, entry); if err = noErr then begin err := FSpGetFinfo(downloadFile, info); if err = noErr then begin info.fdType := entry.file_type; info.fdCreator := entry.file_creator; err := FSpSetFInfo(downloadFile, info); end; (* if *) end; (* if *) SetCorrectFileType := err; end; (* SetCorrectFileType *)
ICMapFilename
is one of the high level interfaces to
the mappings database. There are a variety of other API routines that
trade ease of use for greater efficiency and flexibility.
The reverse operation to the previous example is setting the extension of a file you are going to send to a foreign file system based on its Macintosh type and creator. Again Internet Config provides the facilities for doing this is a user configurable fashion.
function SetCorrectExtension(fileToUpload : FSSpec; var uploadName : Str255) : ICError; var err : ICError; info : FInfo; entry : ICMapEntry; begin err := FSpGetFinfo( fileToUpload, info); if err = noErr then begin err := ICMapTypeCreator(gICInstance, info.fdType, info.fdCreator, fileToUpload.name, entry); end; (* if *) if err = noErr then begin // This assumes the file doesn't already have an extension, // the procedure is a bit more complication if it does. uploadName := concat(fileToUpload.name, entry.extension); end; (* if *) SetCorrectExtension := err; end; (* SetCorrectExtension *)
Again the routine demonstrated here is the highest level interface to this operation. There are a variety of other API routines that trade ease of use for greater efficiency and flexibility.
This chapter describes the keys used by Internet Config to denote preferences. It starts with a discussion of how the range of possible keys is divided and then continues on to describe each currently supported key and the type of data it returns.
Keys must are Str255
s that are case insensitive. All
high bit set characters are either reserved or defined to be special.
There is currently only one special character in key strings, namely the bullet (option-8 on the Macintosh keyboard, shown as "*" in this document). This is used in two places. Firstly it allows applications to store application private preferences using the mechanism described below. Secondly it is used as a field separator for indexed entries, such as the "Helpers*" (where * is the option-8 character) entry. Indexed entries rely on the fact that the bullet character is not valid within normal keys, so all the keys beginning with "Helper*" must be helper mapping entries.
If an applications wishes to store a private preference then it should prepend its key with the hexadecimal representation of its creator type and a bullet.
For example, the Internet Config application stores its window positions with the following key: "49434170*WindowPositions".
You can register more keys by mailing details to the support address for Internet Config.
You should look in "InternetConfig.[ph]" for a list of the currently defined standard keys. However, a good way of getting a rough idea of what is available is to look through the Internet Config application. The available keys cover a number of broad areas, including:
More keys are being added to IC with each release, so you should check in the interface files and on the config mailing list before creating your own keys.
A number of keys are based around common string types. This section describes some features of those keys.
The following common types are used, with their standard definitions.
'STR#'
resource,
that is a 2 byte count followed by packed Pascal strings.
PStrings containing passwords are scrambled, to prevent idle snooping. The scrambling algorithm for PStrings is as following:
for i in 1 .. length(str) str[i] := str[i] xor ($55 + i); end-for;
Note that this algorithm is its own inverse, so the same code will
both scramble and descramble, ie Scramble(Scramble(X)) =
X
.
Both PStrings and each entry in STR# preferences can be formatted to contain all the required information about a service. Formatted strings contain three fields, each separated by colons ":". The first field contains the user displayable name for a specific service. The second field contains the machine's DNS name. The final field contains the path, which is empty for Archie servers. For example:
Australia:archie.au:/micros/mac/info-mac
A large number of host PStrings are used to denote default services. Internet Config itself does not interpret these strings but applications are required to. The format is as follows:
[ whitespace ] ( DNS_name | IP_number ) [ (whitespace | colon) port ] [ whitespace anything ]
Applications that read this preference should endeavour to support both port numbers and also string names for ports, using the services data structure described later in the next section.
Applications that write this preference should write it using colon as the name/port separator.
"InternetConfig.[ph]" defines a number of data types that are the type of various preferences. The following types are defined:
The ICFontRecord
is a fixed length record used to
specify a font, size and face.
ICFontRecord = record size : integer; face : Style; font : Str255; end; ICFontRecordPtr = ^ICFontRecord; ICFontRecordHandle = ^ICFontRecordPtr;
The ICAppSpec
is a fixed length record used to
specify an application.
ICAppSpec = record fCreator: OSType; name: Str63; end; ICAppSpecPtr = ^ICAppSpec; ICAppSpecHandle = ^ICAppSpecPtr;
The program using this specification is expected to look up the location of the application in the desktop database. The name is provided to display to the user, and should not affect the search.
This is a variable length data structure used to specify a file or folder.
ICFileSpec = record vol_name : Str31; vol_creation_date : longint; fss : FSSpec; alias : AliasRecord; end; ICFileSpecPtr = ^ICFileSpec; ICFileSpecHandle = ^ICFileSpecPtr; ICfile_spec_header_size = sizeof(ICFileSpec) - sizeof(AliasRecord);
This type contains both an alias and a 'poor man's alias'. All
modern applications can use the real alias and ignore the poor man's
alias. You can use Munger to convert an ICFileSpecHandle
to an AliasHandle
, as shown below.
junkLong := Munger(prefH, // Operate on this handle, 0, // starting at offset 0, nil, // grab the name, creation date, and fss fields, ICfile_spec_header_size, Ptr(-1), // and delete them. 0); // Munger is your friend!
The poor man's alias was included so that programs running under
System 6 could specify file positions using vol_name
and
vol_creation_date
for the volume, fss.parID
for the directory on the volume, and fss.name
for the
file in the directory.
This is used to specify a mapping from Mac Roman encoding to Mac Net ASCII and vice versa.
ICCharTable = record net_to_mac : packed array [char] of char; mac_to_net : packed array [char] of char; end; ICCharTablePtr = ^ICCharTable; ICCharTableHandle = ^ICCharTablePtr;
The table maps Mac Roman codes (as used by the US and most European Mac OS systems) to ISO-Latin-1 in such a way that most Mac Roman codes end up in the correct place. Mac Roman codes that have no representation in ISO-Latin-1 are mapped to an arbitrary code point in ISO-Latin-1.
This mapping has two important attributes:
Since this technique was defined, Internet text encodings have become increasingly sophisticated. Anyone seriously involved in Mac Internet programming should check out the Apple Text Encoding Converter API.
This is used to specify extension and MIME mappings. It is discussed in detail in a later section.
This is used to the mapping between TCP service names and their
ports. The data returned is an ICService
record, which
contains a count followed by an unbounded array of
ICServiceEntries
.
ICServices = record count : integer; services : array [1..1] of ICServiceEntry; // this array is packed, so you can't index it directly end; ICServicesPtr = ^ICServices; ICServicesHandle = ^ICServicesPtr;
Note that each element in the array is tightly packed, which means
you can't index the array directly. The format of an
ICServiceEntry
is defined below.
ICServiceEntry = record name : Str255; // This strings is tightly packed, port : integer; // which means, these fields might have an flags : integer; // odd address. end; ICServiceEntryPtr = ^ICServiceEntry; ICServiceEntryHandle = ICServiceEntryPtr;
The bits in the flags field are:
ICservices_tcp_bit = 0; (* this is a TCP service *) ICservices_tcp_mask = $00000001; ICservices_udp_bit = 1; (* this is a UDP service *) ICservices_udp_mask = $00000002;
It is possible for both the UDP and TCP bits to be set, which means that the service is available via both protocols.
This chapter is divided into a number of sections. The first section describes the types and constants provided by the interface. The subsequent sections describe groups of API routines, starting with the core routines.
Caution: The Internet Config API is defined originally in Pascal. This has a number of important consequences for C programmers:
var
parameters are converted from
var
x : y
to y *x
.
This means that when you see the parameter y *x
, you
cannot pass in nil. In future this restriction may be eased,
especially for the attr parameter to ICGetPref. Parameters where
nil is legal are declared using the explicit pointer type, that is
"yPtr x".
As of Internet Config 1.2, all interface files are generated automatically by the most evil HyperCard stack on the planet. If you find any problems with any of the interface files, please email the support address for Internet Config.
Caution: Except otherwise noted, it is illegal to call Internet Config API routines at interrupt time.
This section describes the types and constants provided by Internet Config.
The following error codes can be returned by the IC API.
icPrefNotFoundErr = -666; // preference not found (duh!) icPermErr = -667; // cannot set preference because of permissions icPrefDataErr = -668; // problem with preference data icInternalErr = -669; // hmmm, this is not good icTruncatedErr = -670; // more data was present than was returned icNoMoreWritersErr = -671; // you cannot begin a write session // because someone else is already writing icNothingToOverrideErr = -672; // no component for the override // component to capture icNoURLErr = -673; // no URL found icConfigNotFoundErr = -674; // no configuration was found icConfigInappropriateErr = -675; // incorrect manufacturer code icProfileNotFoundErr = -676; // profile not found icTooManyProfilesErr = -677; // too many profiles in database
The ICAttr
type is simply a longint containing flags
that describe the attributes of a key and its data.
ICAttr = longint;
The ICattr_no_change
constant is used when you call
ICSetPref
and do not want to mess around with
attributes. You can supply this value and IC will not change the
attribute of the preference.
ICattr_no_change = -1;
The following bits are defined in the ICAttr
type:
ICattr_locked_bit = 0; // bit in the ICAttr ICattr_locked_mask = $00000001; // corresponding mask ICattr_volatile_bit = 1; ICattr_ volatile _mask = $00000002;
If the locked bit is set, any attempt to set the preference will result in an error. If the volatile bit is set, you should not cache the value of this preference because it is subject to non-seed changing changes. See the section on caching preferences for more information about this issue.
The following values define the file type, creator and default name of the Internet Preferences file.
ICfiletype = 'ICAp'; ICcreator = 'ICAp'; ICdefault_file_name = 'Internet Preferences';
The ICDirSpec
record is used to hold the
vRefNum
and dirID of a directory. An array of
these is supplied to ICFindConfigFile
to specify the
search path. This array is defined to contain just 4 elements, but is
in fact arbitrarily extensible.
ICDirSpec = record vRefNum : integer; dirID : longint; end; ICDirSpecArray = array [0..3] of ICDirSpec; ICDirSpecArrayPtr = ^ICDirSpecArray;
The ICError
type is used for all error results from
IC. A longint
is used because we make lots of calls to
Component Manager which uses longint
s for error codes.
ICError = longint;
The ICInstance
type is an opaque type that is used to
hold a reference to a session with Internet Config. Applications can
create instances by calling ICStart
, used them with any
of the API routines and destroy them by calling ICStop
.
ICInstance = Ptr;
Note that an ICInstance
is a pointer, so you can use
the system nil
value to denote an invalid
instance. Do not pass an ICInstance
between processes.
Also, be careful to not pass an ICInstance
between
instruction set architectures, ie from PowerPC to 68K or vice versa.
The ICPerm
type is used to specify whether you wish
to access the preferences for read-only or read/write.
ICPerm = (ioNoPerm, icReadOnlyPerm, icReadWritePerm);
The ICConfigRef
type is used to store a permanent
reference to an Internet Config configuration. The type varies in
length and only the first four bytes, the manufacturer field, has a
public meaning, which is described in the
IC
Internals Documentation.
ICConfigRef = record manufacturer: OSType; (* other private data follows *) end; ICConfigRefPtr = ^ICConfigRef; ICConfigRefHandle = ^ICConfigRefPtr;
The following constants define bits in the flags field passed to
ICSetConfigReference
.
icNoUserInteraction_bit = 0; icNoUserInteraction_mask = $00000001;
The ICProfileID type is an opaque reference to a particular profile. IC returns these to you so that you can denote that profile in subsequent calls to the IC API.
ICProfileID = longint; ICProfileIDPtr = ^ICProfileID;
You can also use the const kICNilProfileID
to denote
no profile.
kICNilProfileID = 0;
You following three constants define the specific details of the
AppleEvent that the ICEditPreferences
routine sends to
the Internet Config application to request it to open the edit window
for a specific preference. You should consult the IC terminology
resource ('aete'
) for more details on the meaning of
these constants.
kICEditPreferenceEventClass = 'ICAp'; kICEditPreferenceEvent = 'ICAp'; keyICEditPreferenceDestination = 'dest';
The following constants define the component type, subtype and manufacturer of the Internet Config component.
kICComponentType = 'PREF'; kICComponentSubType = 'ICAp'; kICComponentManufacturer = 'JPQE';
The following constants define the possible version numbers
returned by the Internet Config component in response to a
GetComponentVersion
call. The constants only define the
high word of the version number, ie the version of the programming
interface. The low word of the version number is the implementation
version, and changes with each bug fix release of IC.
kICComponentInterfaceVersion0 = $00000000; // IC >= 1.0 kICComponentInterfaceVersion1 = $00010000; // IC >= 1.1 kICComponentInterfaceVersion2 = $00020000; // IC >= 1.2 kICComponentInterfaceVersion3 = $00030000; // IC >= 2.0 kICComponentInterfaceVersion = kICComponentInterfaceVersion3;
The constant kICComponentInterfaceVersion
is always
defined to be the latest version number at the time the interface
file was created.
The Internet Config interfaces also define a number of constants
of the form kICC*
that are the component selectors used
for each API routine. These values are useful for as parameters to
the ComponentFunctionImplemented
(see the
example later in this
document), and for authors of override components.
This section documents the routines that make up the core of the Internet Config API.
The routines in the sub-section let you create, configure and
destroy connections to Internet Config, denoted by the
ICInstance
type. Although it is usual to create one
connection when your program starts and destroy it when it
terminates, it is legal to create an arbitrary number of connections
at any time.
function ICStart(var inst : ICInstance; creator : OSType) : ICError;
You should call this routine when you wish to start using Internet
Config -- typically at application initialisation time -- passing it
your program's creator type. If it returns noErr
, the
resulting ICInstance
is valid for you to pass to other
IC routines. If it returns an error, the ICInstance
will
be nil
and you should not call
ICStop
.
function ICFindConfigFile(inst : ICInstance; count : integer; folders : ICDirSpecArrayPtr) : ICError;
After you have created an ICInstance, you should configure it
using this routine. You should pass 0 for the count
parameter, and nil
for the folders
parameter.
Note: There are other ways of configuring an instance, some of them using non-standard parameters to this function These techniques no longer make sense in the face of the profile support added in IC 2.0. However, they still work, and are documented in the section Obsolete and Private Routines.
function ICStop(inst : ICInstance) : ICError;
You should call this when your application is done using Internet
Config, passing it the instance you got from ICStart
.
The routines in this sub-section return various pieces of information about an instance.
function ICGetConfigName(inst : ICInstance; longname : Boolean; var name : Str255) : ICError;
This function returns a displayable string (in the system script
system) that represents the instance's current configuration. The
longname
parameter is a hint as to what type of string
should be returned. A short string would usually be less than 32
characters, a long string may be up to 255 characters. This is only a
hint which the implementation is free to ignore.
Caution: The string returned by this routine is for user display only. Relying on the format of this string will guarantee incompatibility with future releases of Internet Config.
Caution: This routine requires Internet Config 1.2 or later.
function ICGetSeed(inst : ICInstance; var seed : longint) : ICError;
This routine returns the seed for the current preferences set. The
seed is a value that changes when any preferences are changed. You
can repeatedly call this routine to determine whether any preference
information you have cached is out of date. The value returned by
ICGetSeed
is only valid until the machine reboots, which
basically means that you should not use this values across repeated
launches of your application. The seed value is not valid inside a
pair of ICBegin
and ICEnd
calls. You should
sample the seed after calling ICEnd
.
function ICGetPerm(inst : ICInstance; var perm : ICPerm) : ICError;
This routine returns the current permissions for this instance, ie
the permission value you used when you called ICBegin
,
or ioNoPerm
if you haven't call ICBegin
.
This routine is not very useful for applications. It was included so
that overriding components can obtain this information easily.
function ICGetComponentInstance(inst : ICInstance; var componentInst : ComponentInstance) : ICError;
This routine returns the underlying ComponentInstance
being used by the ICInstance
. It returns an error and,
and sets componentInst
to nil
, if
there is no underlying component instance. This was possible prior to
IC 2.0, where IC could operate without the extension installed. With
IC 2.0 and higher, you're guaranteed to get a valid
ComponentInstance
if you pass in a valid
ICInstance
.
Accessing the underlying component instance is useful when you need to do version testing (see the example later in this document), or some other component-specific operation.
The routines in this sub-section are used to prepare an instance for reading or writing preferences. These routines are not always required because the commonly used reading and writing calls perform this operation automatically. However, even in that case, these routines are useful if you are making repeated calls because they allow those calls to work faster.
function ICBegin(inst : ICInstance; perm : ICPerm): ICError;
This routine prepares IC to read (if perm is
icReadOnlyPerm
) or read and write (if perm is
icReadWritePerm
) preferences. If this routine returns an
error, you cannot access the preferences. If it returns
noErr
, you should proceed to access your preferences and
eventually call ICEnd
.
IC defines a "one writer or multiple readers" model of preference
access: either one writer can be accessing the preferences, or
multiple readers. You should not attempt to call ICBegin
on this or any other instance while you are inside an
ICBegin
/ICEnd
pair. To avoid breaking this
rule, you must not let any other application run, by calling
WaitNextEvent
or any other routine that gives time,
between these calls. IC attempts to detect these infringements, but
it may not be successful in all cases.
Except where otherwise noted, any attempt to read, delete or write
preferences without calling ICBegin
will result in a
paramErr
.
Any attempt to reconfigure an instance while inside an
ICBegin
/ICEnd
pair will result in a
paramErr
.
Caution: Calling ICBegin
is likely
to modify the current resource chain, normally by adding the Internet
Preferences resource file at the start of the chain. This will be
undone when you call the corresponding ICEnd
. You should
not rely on, or fail because of, this behaviour.
function ICEnd(inst : ICInstance) : ICError;
This routine tells IC that you have finished accessing preference
information. You must have successfully called ICBegin
to call this.
The routines in this sub-section provide the ability to read, write, modify and delete Internet Config preferences. These routines all operate on the current profile. See the section Profile Routines for information on how to change the current profile.
function ICGetPref(inst : ICInstance; key : Str255; var attr : ICAttr; buf : Ptr; var size : longint) : ICError;
This routine gets a preference's data given its key
.
It puts the data into a buffer that you supply. It also returns the
attributes in attr
. You should point buf
to
the beginning of your buffer and set size
to its size.
You can also use this routine to just get information about the
preference by setting buf
to nil
.
You do not need to call ICBegin
before calling this
routine. If you do not do so, this routine will automatically called
ICBegin(inst, icReadOnlyPerm)
on entry and
ICEnd(inst)
on exit.
The value of key
must not be the empty string. If
buf
is nil then no data is returned and the value of
size
is ignored; otherwise the value of
size
must not be negative and is the size of the buffer
pointed to by buf
. If the preference is present then the
call sets attr
to be the preference's attributes,
size
to be the preference's true size and returns
noErr
.
The routine may return icTruncatedErr
if the buffer's
size is too small to hold the data. In this case attr
is
valid, size
contains the total size of the preference
and IC has placed as many bytes of the preferences as will fit in the
buffer. You may want to increase the size of the buffer and refetch
the preference to recover the lost data.
On other errors, IC returns attr as ICattr_no_change
and size
as 0. The most common error,
icPrefNotFoundErr
, implies that the preference
associated with key
is not available.
function ICSetPref(inst : ICInstance; key : Str255; attr : ICAttr; buf : Ptr; size: longint) : ICError;
The routine sets a preference given its key, attributes and a
buffer containing the preference data. You can leave the attributes
unchanged by specifying an attribute of
ICattr_no_change
. You can leave the data unchanged by
passing nil
to buf
. Not setting both
values has no effect if the preference already exists but creates an
empty preference with the default attributes otherwise.
You do not need to call ICBegin
before calling this
routine. If you do no do so then this routine will automatically
called ICBegin(inst, icReadWritePerm)
on entry and
ICEnd(inst)
on exit.
The value of key
must not be the empty string. If
buf
is nil
, the value of size is
ignored; otherwise it must be the non-negative size of the data to
store. If the preference is successfully modified then the routine
returns noErr
.
The routine returns icPermErr
if the perm parameter
to ICBegin
was icReadOnlyPerm
.
The routine also returns icPermErr
if the current
attr
is locked, the new attr
is locked and
buf
is not nil
.
function ICFindPrefHandle(inst : ICInstance; key : Str255; var attr : ICAttr; prefh : Handle) : ICError;
This routine is analogous to ICGetPref
except that it
returns the resulting preference in a handle. You must pre-allocate
the prefh
handle, and the routine will resize it
appropriately to hold all the preference data.
You do not need to call ICBegin
before calling this
routine. If you do not do so, this routine will automatically called
ICBegin(inst, icReadOnlyPerm)
on entry and
ICEnd(inst)
on exit.
Caution: This routine requires Internet Config 1.2 or later.
function ICSetPrefHandle(inst : ICInstance; key : Str255; attr : ICAttr; prefh : Handle) : ICError;
This routine is analogous to ICSetPref except that it takes its
input as a handle. Like ICSetPref
, if the handle is
nil
then it sets the attributes only.
You do not need to call ICBegin
before calling this
routine. If you do no do so then this routine will automatically
called ICBegin(inst, icReadWritePerm)
on entry and
ICEnd(inst)
on exit.
Caution: This routine requires Internet Config 1.1 or later.
function ICDeletePref(inst : ICInstance; key : Str255) : ICError;
This routine deletes a preference given its key. The value of
key
must not be the empty string. You must call
ICBegin(inst, icReadWritePerm)
before calling this
routine.
The routine returns icPrefNotFoundErr
if the
preference does not exist.
The routines in this sub-section provide the ability to enumerate
all of the preferences in the Internet Config database. You must call
ICBegin
before calling any of these routines. These
routines all operate on the current profile. See the section
Profile Routines for information on
how to change the current profile.
function ICCountPref(inst : ICInstance; var count : longint) : ICError;
This routine returns the total number of preferences available. If
it returns an error, count
will be 0.
function ICGetIndPref(inst : ICInstance; index : longint; var key : Str255): ICError;
This routine returns the key associated with the
index
'th preference. The value of index
must be positive. The routine returns icPrefNotFoundErr
if index
is beyond the last preference.
We recommend that you do not provide a user interface for editing
Internet Config preferences from within your application. To make it
easier for users to edit Internet Config preferences, you might want
to provide a mechanism to launch Internet Config from within your
application. ICEditPreferences
provides support for
this.
function ICEditPreferences(inst : ICInstance; key : Str255) : ICError;
This routine launches the Internet Config application (or brings it to the front if it's already running) and instructs it to open the preferences database associated with this instance. You must have specified a config file before calling this routine. The key parameter is a hint as to which preference you would like the application to display. If key is empty then the application doesn't display any specific preference, otherwise it displays the window that edits the associated key.
Versions of Internet Config prior to 1.2 provided no means to edit a specific entry in the Mappings database or to edit a specific helper. Prior to version 1.2 you can only display the entire Helper window by setting the key to "Helper*" (where * is the option-8 character). As of IC 1.2 you can provide extra information in the key parameter. If you want to display the helper associated with the "ftp" URL scheme, you should set key to "Helper*ftp". If you want to display the Map Entry dialog for the ".zip" extension, you should set key to "Mapping*.zip".
You do not need to call ICBegin
before calling this
routine.
Note: This routine may have a radically different effect in future implementations.
Caution: This routine requires Internet Config 1.1 or later.
One very common use of Internet Config is to access the list of URL helper applications. Internet Config 1.1 builds on this success by providing two routines for explicitly dealing with URLs and their helpers.
function ICParseURL(inst : ICInstance; hint : Str255; data : Ptr; len : longint; var selStart : longint; var selEnd : longint; url : Handle) : ICError; function ICLaunchURL(inst : ICInstance; hint : Str255; data : Ptr; len : longint; var selStart : longint; var selEnd : longint) : ICError;
These two routines take very similar parameters and will be
discussed as one. The primary difference between the routines is that
ICParseURL
returns the URL to the calling program, while
ICLaunchURL
passes the resulting URL to the appropriate
helper application.
ICParseURL
puts the resulting URL into the
url
handle. You must have created this handle before
calling this routine. The routine resizes the handle appropriately.
The handle should neither be locked nor purgeable.
ICLaunchURL
looks up the helper (in the
current profile) for the resulting
URL and launches it (or brings it to the front if it's already
running) and sends it a GURL AppleEvent with the parsed URL . You can
find out more about the GURL event suite from the specification
referenced in the Recommended
Reading appendix.
Both routines take a hint parameter. This parameter determines how the routines deal with 'slack' URLs. These are URLs of the form "name@host", which are interpreted in a context sensitive manner. For example in a mail program a 'slack' URL would normally be interpreted as "mailto:name@host", whereas in a news program the URL would be interpreted as a news reference. You should pass in the name of a URL scheme to the hint, for example "mailto". Alternatively you can leave the hint parameter empty.
The data
and len
parameters determine
the location and size of the text to be parsed. The
selStart
and selEnd
parameters determine
the current selection in that text; the are interpreted in the same
way as the selStart
and selEnd
fields of a
TERec
. The routine adjusts the selStart
and
selEnd
parameters to 'select' the text that it has
determined is a URL.
The exact URL parsing algorithm is subject in different versions of IC. The algorithm proceeds roughly as follows:
You do not need to call ICBegin
before calling these
routines.
If ICLaunchURL
returns noPortErr
(ie
error -903), it's probably because your application is not high-level
event aware. Remember that ICLaunchURL
sends an
AppleEvent to do its job, and the AppleEvent Manager requires that
applications that send events must also be able to received them. You
can make your application high-level event aware by setting the
appropriate bit in your 'SIZE'
resource. See
Inside Macintosh: Interapplication Communication for
details.
If ICLaunchURL
returns bdNamErr
(ie
error -37), it's most probably because you haven't configured your
ICInstance
. See
Starting and Stopping
Internet Config for details on how to do this.
Caution: These routines requires Internet Config 1.1 or later.
One of the most important preferences in the Internet Config
database is Mappings
, the table of mappings between a
file's extension and its type and creator. This preference is also
one of the hardest to parse. In Internet Config 1.0 there was a
statically linked library that you could use to access this
preference. In Internet Config 1.1 this library was moved into the
main API and significantly enhanced. This section describes these API
extensions.
The routines are divided up into three classes, high-level, mid-level and low-level routines. You must choose which routines to call depending on what level of control you require.
Caution: None of the routines described in this section are available in Internet Config 1.0.
The Mapping key returns an 'array' of the ICMapEntry type.
ICMapEntry = record total_length : integer; // in bytes, from beginning of record fixed_length : integer; // in bytes, from beginning of record version : integer; // version number of the entry, currently 0 file_type : OSType; // type for this entry file_creator : OSType; // creator for this entry post_creator : OSType; // creator of the post-processing flags : longint; // general flags extension : Str255; // extension for this entry creator_app_name : Str255; // name of the creator application post_app_name : Str255; // name of the post-processing application MIME_type : Str255; // MIME type for this entry entry_name : Str255; // user level name of the entry end; ICMapEntryPtr = ^ICMapEntry;; ICMapEntryHandle = ^ICMapEntryPtr; ICmap_fixed_length = 22; // current value of the fixed_length field
The value of the Mappings
preference is not literally
an array and you cannot index it directly. It is actually a packed
array of ICMapEntry
's, with each entry packed to remove
the empty space at the end of the strings. Entries can start on an
odd address and entries can contain user
data. We strongly recommend that you access this data structure
using the routines described in this section. These routines return
an unpacked ICMapEntry
, which is a lot easier to deal
with.
Each ICMapEntry
gives the relationship between an
extension, a MIME type, and a file type and creator. The database of
ICMapEntry's is not normalised, ie there can be multiple entries with
the same creator, file type, MIME type, extension, and so on. In
general, each application determines how these entries are used in a
particular circumstance, although IC does define some policies by way
of its high level mapping routines.
The fields in the ICMapEntry
are defined as follows:
total_length
fixed_length
ICmap_fixed_length
.
version
file_type
'ZIP '
.
file_creator
'ZIP '
.
post_creator
'SITx'
. See the section
Post-Processing for more information
about this field, including the difference between this and the
file_creator
field. In general, applications should
only consult this field if the ICmap_post_bit
is set
in the flags
field, however even if that bit is not
set, you can determine the post-processing application by looking
at this field. If no post processing application has even been
set, this field will be 0.
flags
extension
creator_app_name
file_creator
field, eg "ZipIt". This field is present
so that you can display the name even if the application is not
installed.
post_app_name
post_creator
field, eg "StuffIt Expander". This field
is present so that you can display the name even if the
application is not installed. See the post_creator
field description for details as to when this field is valid.
MIME_type
entry_name
The currently defined bits in the flags
field are:
ICmap_binary_bit = 0; // file should be transferred in ICmap_binary_mask = $00000001; // binary as opposed to text mode ICmap_resource_fork_bit = 1; // the resource fork of the file is significant ICmap_resource_fork_mask = $00000002; ICmap_data_fork_bit = 2; // the data fork of the file is significant ICmap_data_fork_mask = $00000004; ICmap_post_bit = 3; // post process using post fields ICmap_post_mask = $00000008; ICmap_not_incoming_bit = 4; // ignore this mapping for incoming files ICmap_not_incoming_mask = $00000010; ICmap_not_outgoing_bit = 5; // ignore this mapping for outgoing files ICmap_not_outgoing_mask = $00000020;
The first three flags can be used to determine how to transfer a
file, specifically for protocols such as FTP. If the
ICmap_resource_fork_bit
is set, the file should be
transferred in MacBinary mode. Otherwise, the
ICmap_binary_bit
determines whether the file should be
transferred in text or binary mode.
The post-processing flag is set up by the user and indicates whether applications should post-process this type after a download. See the section Post-Processing for details.
The last two flags can be used to make entries asymmetric; they allow the user to use different settings depending on whether the file is being moved to or from the Macintosh. The meaning of these two flags is encoded in the operation of the high- and mid-level routines.
The high-level routines are suitable for applications that want to easily look up a file type and creator based on an extension, or vice version. These routines are significantly slower than their lower level counterparts, especially if you call them repeatedly.
Note: While you do not need to call
ICBegin
to call these routines, if you call them
repeatedly it will be faster if you bracket those calls with an
ICBegin
/ICEnd
pair. However, if you are
calling them repeatedly it may be better to use the mid-level
routines.
function ICMapFilename(inst : ICInstance; filename : Str255; var entry : ICMapEntry) : ICError;
This routine takes a filename, which must not be empty, and
returns the most appropriate ICMapEntry
based on the
filename's extension. If there is no appropriate entry, the routine
returns icPrefNotFoundErr
. The routine works by walking
the Mapping preference (from the current
profile) from start to finish looking for the entry with the
longest matching extension that doesn't have the 'not incoming' flag
set. If there are two equally appropriate entries, the first one is
returned.
function ICMapTypeCreator(inst : ICInstance; fType : OSType; fCreator : OSType; filename : Str255; var entry : ICMapEntry): ICError;
This routine takes a file type and creator (and optionally the
file's name) and returns the most appropriate entry. If there is no
appropriate entry then the routine returns
icPrefNotFoundErr
. The routine works by walking the
Mapping preference (from the current
profile) looking for the most appropriate entry that doesn't have
the 'not outgoing' bit set . If there are two equally appropriate
entries, the first one is returned. The appropriateness of an entry
is determined by a match weight function:
This algorithm implies that the returned entry has a matching file type and the longest matching extension, and that entries with a matching creator are preferred over other entries.
The mid-level routines are exactly analogous to their high level counterparts except that they take a handle to the mappings database as a parameter instead of getting it from the database themselves. These routines are useful if you are doing many searches because they avoid the overhead of getting the mappings database each time.
function ICMapEntriesFilename(inst : ICInstance; entries : Handle; filename : Str255; var entry : ICMapEntry) : ICError; function ICMapEntriesTypeCreator(inst : ICInstance; entries : Handle; fType : OSType; fCreator : OSType; filename : Str255; var entry : ICMapEntry) : ICError;
The semantics of these routines are identical to the high level
routines except that they take a handle to the mappings database in
entries. This parameter must not be nil
.
You do not need to call ICBegin
before calling these
routines.
The low-level routines give you access to the primitive operations
used to implement the other mappings routines. Most of these routines
either take or return a pos
parameter. This is the
offset into the Mapping preference handle. The pos of the first entry
is 0. You can get the pos
of the next entry by adding
entry.total_size
to the pos
of the previous
entry. You can use this to parse the entire table by starting with a
pos
of 0 and repeatedly adding
entry.total_size
to pos
as you look at each
entry.
You do not need to call ICBegin
before calling these
routines.
function ICCountMapEntries(inst : ICInstance; entries : Handle; var count : longint) : ICError;
This routine counts the number of entries in the Mapping
preference provided. The entries
parameter must be a
handle to a valid Mapping preference.
function ICGetIndMapEntry(inst : ICInstance; entries : Handle; index : longint; var pos : longint; var entry : ICMapEntry) : ICError;
This routine returns the entry whose index is index
.
The entries
parameter must be a handle to a valid
Mapping preference. The index
parameter must be in the
range 1 to the number of entries in the entries
handle.
The routine returns the corresponding entry in the entry
variable and sets pos
to the position of that entry in
the handle.
function ICGetMapEntry(inst : ICInstance; entries : Handle; pos : longint; var entry : ICMapEntry) : ICError;
This routine gets a entry based on its position in the Mapping
preference. The entries
parameter must be a handle to a
valid Mapping preference. The pos
parameter must be
greater than or equal to 0 and less than the size of the
entries
handle, and must be the offset to the first byte
of the entry. The routine returns the corresponding entry in the
entry
variable.
function ICSetMapEntry(inst : ICInstance; entries : Handle; pos : longint; var entry : ICMapEntry) : ICError;
This routine sets an entry in the Mapping preference. The
entries
parameter must be a handle to a valid Mapping
preference. The pos
parameter must be greater than or
equal to 0 and less than the size of the entries
handle,
and must be the offset to the first byte of the entry to be set. The
routine sets the corresponding entry to the value specified in the
entry parameter.
Note: The entry
parameter is a
var
parameter solely to prevent the extra stack
usage that a value parameter would entail.
function ICDeleteMapEntry(inst : ICInstance; entries : Handle; pos : longint): ICError;
This routine deletes an entry in the Mapping preference. The
entries
parameter must be a handle to a valid Mapping
preference. The pos
parameter must be greater than or
equal to 0 and less than the size of the entries
handle,
and must be the offset to the first byte of the entry to be deleted.
function ICAddMapEntry(inst : ICInstance; entries : Handle; var entry : ICMapEntry) : ICError;
This routine adds an entry to the database. The
entries
parameter must be a handle to a valid Mapping
preference. The routine adds the entry specified by the
entry
parameter to the end of the database.
Note: The entry
parameter is a
var
parameter solely to prevent the extra stack
usage that a value parameter would entail.
The following routines allow applications to do access the Mapping preference from interrupt time code.
function ICRequiresInterruptSafe(inst : ICInstance) : ICError;
This routine informs IC that you intend to call
ICGetMapEntryInterruptSafe
with this instance. IC uses
this opportunity to load the Mapping preference in memory, which
allows ICGetMapEntryInterruptSafe
to access the
information at interrupt time.
The only way to tell IC that you no longer want this interrupt safe instance is to close the instance.
This routine will fail with an error if this instance was not configured with the default configuration. You cannot reconfigure the instance after registering as interrupt safe.
Note: Because loading the Mapping preference in
memory consumes a noticeable amount of memory (approximately 18 KB),
you should only call this routine on instances that will be calling
ICGetMapEntryInterruptSafe
. Similarly, you should avoid
creating such instances until they are needed. For example, a File
System Manager (FSM) plug-in should create this instance only when a
volume that needs IC file mapping is mounted, not at system startup
time. On the other hand, once you have created one interrupt safe
instance, the overhead for creating a second is minimal, so an FSM
plug-in might want to create an instance per volume, and have IC do
the reference counting.
Caution: This routine requires Internet Config 2.0 or later.
function ICGetMappingInterruptSafe(inst : ICInstance; var mappingPref : Ptr; var mappingPrefSize : longint) : ICError;
This routine is callable from interrupt time and returns the
current Internet Config Mapping preference (kICMapping
).
On return, mappingPref
is the address of the first entry
ICMapEntry
in the preference, and size
is
the size of the preference (in bytes). Remember that a Mapping
preference is packed, so you must parse it according to the rules
defined earlier in this
section..
The memory for the preference is owned by Internet Config; it is guaranteed to be valid until the next non-interrupt-safe call to IC.
Note: You must call
ICRequiresInterruptSafe
on inst before calling this
routine.
Note: This routine is guaranteed not to require the File Manager to operate, so it is safe to call from an FSM plug-in.
Caution: You should not call this routine unless paging is safe. Normal application code and FSM plug-ins do not have to worry about this, but hardware interrupt handlers and block storage device drivers may. See DTS Technote 1094 Virtual Memory Application Compatibility for details.
Caution: This routine requires Internet Config 2.0 or later.
function ICGetSeedInterruptSafe(inst : ICInstance; var seed : longint) : ICError;
This routine returns the seed associated with inst
in
an interrupt safe fashion. The returned seed is comparable to the
value return by ICGetSeed
. and can be used by
cache watching code that runs at
interrupt time.
Note: You must call
ICRequiresInterruptSafe
on inst before calling this
routine.
Note: This routine is guaranteed not to require the File Manager to operate, so it is safe to call from an FSM plug-in.
Caution: You should not call this routine unless paging is safe. Normal application code and FSM plug-ins do not have to worry about this, but hardware interrupt handlers and block storage device drivers may. See DTS Technote 1094 Virtual Memory Application Compatibility for details.
Caution: This routine requires Internet Config 2.0 or later.
When moving files to the Macintosh the application can choose
whether to support post-processing, that is feeding the file on to
some other application which hopefully renders it into a more useful
form. This is controlled by three fields in the
ICMapEntry
. The first is the post-processing bit in the
flags. If this bit is set, downloading applications should
post-process this file (if it supports post-processing). The
post-processing application is determined by the
post_creator
field; the post_app_name
field
is for display purposes only. If the post_creator
is
OSType(0), no post-processing application has ever been specified by
the user.
The difference between the file's creator and post-processor is subtle but important. The creator should be an application that can open and edit the file type. A post-processor is an application that can render the application in a more useful form. For example, the creator for files with the extension ".cpt" should be Compact Pro, whereas the post-processor is more likely to be StuffIt Expander.
The Mapping preference allows for an arbitrary amount of data
between the end of the packed Pascal strings and the end of the entry
as determined by the total_length
field. This
information is available for application specific use, and is known
as user data. The mapping routines guarantee to
preserve this information when they modify the entry.
If you insert data into this area then you must conform to the following convention. Your data must start with a creator type (4 bytes) (usual your application's creator type) followed by the length of the data you've added (4 bytes), which includes the creator and length. You can then add length - 8 bytes of data after that. Your application can modify and remove data as long as it maintain the consistency of the data structure.
This protocol allows any number of applications to add user data without getting in each others way.
Because the minimal length of user data is 8 bytes, the Internet Config application will remove any data that is less than 8 bytes long.
The following routines are provided to operate on profiles.
function ICGetCurrentProfile(inst : ICInstance; var currentID : ICProfileID) : ICError;
This routine returns, in currentID
, the profile ID of
the current profile. If the routine returns an error, the value in
currentID
is undefined.
You do not need to call ICBegin
before calling this
routine.
Caution: This routine requires Internet Config 2.0 or later.
function ICSetCurrentProfile(inst : ICInstance; newID : ICProfileID) : ICError;
The routine sets the current profile to the profile whose profile
ID is newID
. It returns the error
icProfileNotFoundErr
if newID
is not the ID
of a valid profile.
You do not need to call ICBegin
before calling this
routine.
Caution: This routine requires Internet Config 2.0 or later.
function ICCountProfiles(inst : ICInstance; var count : longint) : ICError;
This routine returns, in count
, the number of
available profiles. If the routine returns an error, the value in
count
is undefined.
You must call ICBegin
before calling this routine.
Caution: This routine requires Internet Config 2.0 or later.
function ICGetIndProfile(inst : ICInstance; index : longint; var thisID : ICProfileID) : ICError;
This routine returns, in thisID
, the profile ID of
the index
'th profile. If the routine returns an error,
the value in thisID
is undefined. The index
parameter must be between 1 and the number of profiles (inclusive).
You must call ICBegin
before calling this routine.
Caution: This routine requires Internet Config 2.0 or later.
function ICGetProfileName(inst : ICInstance; thisID : ICProfileID; var name : Str255) : ICError;
This routine returns, in name
, the name of the
profile whose profile ID is thisID
. The name is given in
the system script. If the routine returns an error, the value in
name
is undefined. It returns the error
icProfileNotFoundErr
if thisID
is not the
ID of a valid profile.
You do not need to call ICBegin
before calling this
routine.
Caution: This routine requires Internet Config 2.0 or later.
function ICSetProfileName(inst : ICInstance; thisID : ICProfileID; name : Str255) : ICError;
The routine sets the name of the profile whose profile ID is
newID
to name
. It returns the error
icProfileNotFoundErr
if thisID
is not the
ID of a valid profile. The name should be supplied in the system
script.
You do not need to call ICBegin
before calling this
routine.
Caution: This routine requires Internet Config 2.0 or later.
function ICAddProfile(inst : ICInstance; prototypeID : ICProfileID; var newID : ICProfileID) : ICError;
This routine creates a new profile and returns its ID in
newID
. If prototypeID
is
kICInvalidProfileID
, the routine creates the profile
using a default set of preferences. Otherwise,
prototypeID
must be the ID of a valid profile, and the
routine creates the profile by cloning the preferences from that
profile. The new profile is given a new, unique, name. If the routine
returns an error, the value in newID
is undefined.
The routine does not switch the current profile to the newly
created one. To do this you must call
ICSetCurrentProfile
on newID
.
You must call ICBegin
before calling this routine.
Caution: This routine requires Internet Config 2.0 or later.
function ICDeleteProfile(inst : ICInstance; thisID : ICProfileID) : ICError;
This routine deletes the profile whose profile ID is thisID. It
returns the error icProfileNotFoundErr
if
thisID
is not the ID of a valid profile. An attempt to
delete the current profile or the last profile will fail with an
error.
You must call ICBegin
before calling this routine.
Caution: This routine requires Internet Config 2.0 or later.
This section describes various Internet Config routines that are now considered to be obsolete or private. The routines are grouped by their degree of supportability in the future. Using any these routines means that you're probably doing something wrong; see this description for each routine for details.
Prior to IC 2.0, the Internet Config API provided two sets of routines. The primary set contained routines starting with the prefix "IC". These are the routines defined in this document. Another set of routines, those beginning with the prefix "ICC", provided the direct component interface. This allowed 68K code to call IC without linking with a large amount of glue that provided the link-in implementation when the IC Extension was not installed.
IC 2.0 and higher require that the IC Extension be installed, so the direct component interface offers little benefit. As such, it is now obsolete. It is still defined in the "InternetConfig.[ph]" file, but we now recommend that all programs call IC through the primary set of routines.
The routines in this section are only required in strange circumstances, typically by applications that act as the primary user interface to the Internet Config database, or in a document oriented architecture where you wish to save details of a specific configuration in a document in order to restore it when the document is reopened. The routines allow you to ask IC to present a user interface for choosing an alternate configuration, for choosing a new configuration, and for creating a reference to that configuration for future reconfiguration.
function ICChooseConfig(inst : ICInstance) : ICError;
This function requests the user to choose a configuration,
typically using some sort of modal dialog. In the current
implementation it will pop up a StandardGetFile
dialog
asking the user to choose a configuration file. In a network based
implementation it might ask the user to specify the IP address of the
configuration server and then to log in. If the routine fails with an
error, the instance retains its previous configuration (or lack
thereof). Likely errors include userCanceledErr
and
noUserInteractionAllowed
.
If you configure an instance in this way, the only way to create
another instance with the same configuration is to use
ICGetConfigReference
to create a reference to the
configuration, and then use ICSetConfigReference
to
configure the new instance.
Caution: This routine requires Internet Config 1.2 or later.
function ICChooseNewConfig(inst : ICInstance) : ICError;
This function requests the user to create a new configuration,
typically using some sort of modal dialog. In the current
implementation it will pop up a StandardPutFile
dialog
asking the user to create a configuration file. In a network based
implementation it might ask the user to specify the IP address of the
configuration server and then to specify their new user name. If the
routine fails with an error then the instance retains its previous
configuration (or lack thereof). Likely errors include
userCanceledErr
and
noUserInteractionAllowed
.
If you configure an instance in this way, the only way to create
another instance with the same configuration is to use
ICGetConfigReference
to create a reference to the
configuration, and then use ICSetConfigReference
to
configure the new instance.
Caution: Applications that create configurations in this way should be prepared for the configuration to be empty, that is not containing any of the default preferences such as the mappings database. This caveat was lifted with IC 1.4.
Caution: This routine requires Internet Config 1.2 or later.
function ICGetConfigReference(inst : ICInstance; ref : ICConfigRefHandle) : ICError;
This routine returns a self-contained reference to the instance's
current configuration specification. You must create the
ref
handle and pass it to this routine, which resizes it
appropriately and stores the configuration reference in it. It is the
responsibility of the caller to both allocate and dispose of the
ref
handle.
Caution: The format of the config reference data is private to Internet Config. Relying on the internal details of this data will guarantee incompatibility with any override components, and with future releases of Internet Config.
Caution: This routine requires Internet Config 1.2 or later.
function ICSetConfigReference(inst : ICInstance; ref : ICConfigRefHandle; flags : longint) : ICError;
This routine reconfigures the instance using the configuration
reference contained in ref
. It is responsibility of the
caller to dispose of ref
. Set the
icNoUserInteraction_bit
in flags
to prevent
Internet Config presenting a user interface during this operation,
otherwise the implementation is free to interact with the user using
a modal dialog. If the routine fails with an error, the instance
retains its previous configuration (or lack thereof). Likely errors
include userCanceledErr
and
noUserInteractionAllowed
. An
icConfigInappropriateErr
error implies that this
configuration reference was created with a different Internet Config
implementation from the one where you are trying to restore it.
Caution: This routine requires Internet Config 1.2 or later.
The routines described in this subsection are obsolete and should not be used by new applications.
function ICGetPrefHandle(inst : ICInstance; key : Str255; var attr : ICAttr; var prefh : Handle) : ICError;
This routine has been replaced by ICFindPrefHandle
.
It operates the same as ICFindPrefHandle
except for two
deficiencies:
ICFindPrefHandle
this routine does require
you to a pass in a handle which it will resize. Instead the
routine creates a new handle in the current zone and returns it in
prefh
. This is counter to the design of the rest of
the IC API.
ICGetPref
and is generally considered a mistake.
For these reasons, the routine has been deprecated.
Caution: This routine requires Internet Config 1.1 or later.
function ICGeneralFindConfigFile(inst : ICInstance; searchPrefs : Boolean; canPreate : Boolean; count : integer; folders : ICDirSpecArrayPtr) : ICError;
This routine allows you to configure an instance using a
non-default preference file. The preference is found by searching a
set of folders, as specified by the routine's parameters. The folders
specified in the first count
entries of the
folders
array are searched, with the folder with the
lowest index being searched first. You can set folders
to nil
if and only if count
is 0. If
searchPrefs
is true, the Preferences folder is appended
to the end of the search list. The instance is configured using the
information, if any, contained in the first search folder.
If no appropriate configuration information is found, the
behaviour depends on the canCreate
parameter. If
canCreate
is false, the routine will return
icConfigNotFoundErr
and the instance retains its
previous configuration (or lack thereof). Otherwise the instance is
reconfigured using some default configuration, if necessary creating
a file in the last folder searched.
Note: This routine is obsolete because a) its design conflicts with a basic tenet of IC, namely that nothing should prevent preferences being found on a network based preferences server, and b) it's function -- to support multiple users on the same machine -- has been replaced by the profile API, added with IC 2.0. However, although the routine is considered obsolete, it continues to work as explained here.
Caution: This routine requires Internet Config 1.2 or later.
function ICFindConfigFile(inst : ICInstance; count : integer; folders : ICDirSpecArrayPtr) : ICError;
The simple, and still recommended, use of
ICFindConfigFile
is described in the section
Starting and Stop
Internet Config. That section pointed out that you should always
pass 0 and nil
to the count
and
folders
parameters respectively. This description
defines what happens if you disobey this advice.
ICFindConfigFile
is a cover routine that effectively
expands out to the following:
err := ICCGeneralFindConfigFile(inst, true, true, count, folders);
function ICFindUserConfigFile(inst : ICInstance; where : ICDirSpec) : ICError;
ICFindConfigFile
is a cover routine that effectively
expands out to the following:
err := ICCGeneralFindConfigFile(inst, false, true, 1, @where);
Caution: This routine requires Internet Config 1.1 or later.
These routines are defined for the benefit of the Internet Config application itself, and should not be called by normal applications. The description for each routines describes why this is so.
function ICSpecifyConfigFile(inst : ICInstance; config : FSSpec) : ICError;
This routine is intended for use by the Internet Configuration
application only. It tell IC to use a specific configuration file,
bypassing the search mechanism used by
ICGeneralFindConfigFile
.
Caution: You should not call this routine because, by doing so, you will be incompatible with any version of Internet Config that does not use a simple preferences file for preference storage.
function ICDefaultFileName(inst : ICInstance; var name : Str63) : ICError;
This routine returns in name
the name of the default
file name of the Internet Preferences file. This name is used during
the preference search and is also the name used to create a
preference file if none is found.
Caution: You should not call this routine because doing so implies that you have intimate knowledge of how IC creates preferences files, which is a recipe for future incompatibilities.
function ICRefreshCaches(inst : ICInstance) : ICError;
This routine informs IC that it should re-read any information it
has cached about the preferences file that inst
is
configured to point at. The Internet Config application calls this
routine because it modifies preferences files without using the IC
API, and hence the API does not see the changes and know to flush its
cache. This is a design flaw in the IC application that we hope to
correct very soon.
Caution: You should not call this routine because doing so implies that you have intimate knowledge of how IC creates preferences files, which is a recipe for future incompatibilities.
Caution: This routine requires Internet Config 2.0 or later.
This appendix contains a number of technical notes about how to use Internet Config correctly.
If you are creating a text file and the file does not have an
ICMapEntry
(either because you don't have a foreign file
name (such as saving an article file in NewsWatcher) or because the
file's extension doesn't exist in the Mapping preference), you should
use the mapping for ".txt" to set the file's creator.
If the file has a valid foreign name and that name's extension is found in the Mapping preference, you should use the entry's type and creator.
Similarly, if you are creating a binary file without an appropriate ICMapEntry, use the mapping for ".binary".
If you wish the user to edit or read a text file, you should use the editor helper.
Due to a slight misunderstanding, old versions of the IC APIs had keys "TextCreator" and "BinaryCreator". These are not valid and should not be used.
The following is a draft proposal. Please ask the author for a final version before writing code that relies on this.
Version 1 Dec 94
Obsoletes previous BINA proposal.
PURPOSE
Retyping files by means of extensions is often inadequate, especially when working with non-Mac systems that do not require the use of extensions. The BINA entry in the user field of the IC Mapping preference provides a "binary stamp" that can be used to identify files based on their contents rather than their extensions.
IMPLEMENTATION
This information is stored in the user data field of the IC mappings structure with the signature BINA. See the IC programmer's reference manual for information regarding this mechanism. The format of the entry is as follows:
Bytes Contents 00-03 'BINA' 04-07 length of entire data structure plus 8 (yy) 08-09 number of signatures 0A-xx binary signatures stored as PStrings (byte aligned) xx-yy (any trailing space is reserved and should be preserved)
The binary signature is the first several bytes shared by every file of the given type. If there are multiple file formats with distinct signatures (which are supported by programs such as Binary Pump) the signatures should be stored sequentially in the BINA entry.
USE
Since many file formats do not include a binary stamp, this mechanism should be used in addition to extension checking, not as a replacement for it. To match by stamp, scan the mappings list and compare each stamp to the file in question. Longer entries take precedence over shorter ones.
Binary stamps should not be applied to files marked as a text type in the main mappings record.
CONTACT
Starting with IC 2.0, the Internet Config API requires that the
Internet Config Extension be installed in the Extensions folder.
ICStart
will fail if there is no IC component installed.
However, having a component does not guarantee that it is the latest
version. If your program uses a modern IC routine (ie one introduced
after IC 1.0), it must still check that the specific routine is
available. You can do this in one of two ways, as described below.
You can check the interface version provided by the IC component. If the IC component claims to provide a given interface version, all the routines defined in that version of the interface are available to you. You can determine the set of calls which are available using a routine like the one shown below.
function GetActualAPIVersion : longint; // Returns the interface version of the currently installed // Internet Config. Assumes that gICInstance is set up to contain // an instance of IC. var err : ICError; componentInst : ComponentInstance; begin err := ICGetComponentInstance(gICInstance, componentInst); if err = noErr then begin GetActualAPIVersion := GetComponentVersion(componentInst); end else begin // ICGetComponentInstance failed, which should be impossible // because ICStart should have failed if the IC component // was not installed. ICDebugStr('GetActualAPIVersion: Could not get component instance.'); GetActualAPIVersion := 0; end; (* if *) end; (* GetActualAPIVersion *)
An alternative approach is to use the Component Manager's
ComponentFunctionImplemented
routine to query the
component about a specific API routine, as shown in the next listing.
function ICAPIRoutineAvailable(what : integer) : Boolean; // Returns whether a routine is supported by the currently installed // Internet Config. Assumes that gICInstance is set up to contain // an instance of IC. var err : ICError; componentInst : ComponentInstance; begin err := ICGetComponentInstance(gICInstance, componentInst); if err = noErr then begin ICAPIRoutineAvailable := (ComponentFunctionImplemented( componentInst, what) = 1); end else begin // ICGetComponentInstance failed, which should be impossible // because ICStart should have failed if the IC component // was not installed. ICAPIRoutineAvailable := false; end; (* if *) end; (* ICAPIRoutineAvailable *)
Another alternate method is to just call the routine and check for
the badComponentSelector
error result.
There are a number of approaches to dealing with missing API routines, including:
ICGetPrefHandle
yourself using
ICGetPref
.
ICLaunchURL
is not present.
Of course the approach that you take is up to you.
We're often asked how you can tell if the Internet Config Extension is installed. People want to know this so that they can disable user interface elements that are pertinent to IC.
There are two approaches I recommend. The first is that, starting
with IC 2.0, you can just call ICStart
. If it returns an
error, the IC Extension is not installed.
The second approach is to look for the 'ICAp'
Gestalt
selector. This is installed by IC 2.0 and higher to indicate that IC
is installed. This approach is particularly useful for people writing
installers, which have easy access to Gestalt but would otherwise
require a custom code resource to check for IC.
This section describes a number of IC keys that have been misinterpreted in the past. Before shipping code that reads or writes these preferences, you should check your usage of these keys against the explanations provided here.
If you have questions about keys that are not covered here, you have a number of places to go to get more information. Firstly, you can turn on Balloon Help in the Internet Config application. IC's Balloon Help provides information for user's setting up preferences and that same information will be useful for programmer's interpreting that information. Secondly, you can raise the issue on the IC programmer's mailing list. Instructions for joining this list are given in the Internet Config FAQ.
[Internet Explorer extends the IC mapping database to include extra information useful for web browsers. The following is a brief explanation of their extensions, written by a member of the Internet Explorer team and edited by me for inclusion in this document. -- Quinn]
We follow the guidelines in the Internet
Config Programming Documentation regarding user data for File
Mappings. The creator type of our data is 'msft'
. Here
is a relevant snippet from our headers (with some brief explanation):
#define kAuxDataSig 'msft' #define kICMapExtraVersion 2 typedef enum { kICHelperHandlerBrowser = 0, kICHelperHandlerApplicationViewer, kICHelperHandlerPlugin, kICHelperHandlerApplicationPostprocess, kICHelperHandlerSave, kICHelperHandlerNone, kICHelperHandlerNotPresent, kICHelperMakeShort = 0x7fff } tICFileHelperHandler; // Flag masks enum { kFlagsDownloadLocation_DownloadFolder = 1, kFlagsDownloadLocation_TemporaryFolder = 2, kFlagsDownloadLocation_Prompt = 4, // This flag never persists. It's used by clients who // call PrefMgr_SetBuiltinFileHelpers to indicate that the // built-in must be used and that IC prefs can't // override it. kFlagsBuiltInHandlerCannotBeOverriden = 8 }; enum { kFlagsDownloadLocationMask = kFlagsDownloadLocation_Prompt | kFlagsDownloadLocation_TemporaryFolder | kFlagsDownloadLocation_DownloadFolder }; typedef struct tPrefMgr_FileMapXtra { short version; tICFileHelperHandler handler; Str32 pluginName; long flags; } tPrefMgr_FileMapXtra, **tPrefMgr_FileMapXtraH;
The format of our auxiliary data is specified by the
tPrefMgr_FileMapXtra
structure. The structure is 68K
aligned. Note that the size of the tICFileHelperHandler
enum should be the size of a short (2 bytes).
The value of the version
member should be 2.
The handler member
should be in the range of
kICHelperHandlerBrowser
to
kICHelperHandlerSave
.
The pluginName
is simply the name of the plug-in
(only valid when handler
is
kICHelperHandlerPlugin
).
The flags
member is exactly one of
kFlagsDownloadLocation_DownloadFolder
,
kFlagsDownloadLocation_TemporaryFolder
, or
kFlagsDownloadLocation_Prompt
. The
kFlagsBuiltInHandlerCannotBeOverriden
flag never
persists and is only used for the internal list of helpers that
PrefsKit manages on a per-client basis.
If the additional tPrefMgr_FileMapXtra
is not present
for a given helper, Internet Explorer uses a default handling of
kICHelperHandlerBrowser
and default flags of
kFlagsDownloadLocation_DownloadFolder
.
Mark G Young
Software Design Engineer
Internet Explorer for Macintosh
Microsoft Corporation
Comments: ic@stairways.com