Created by ic@stairways.com

IC Programmer's Documentation



1 Introduction

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.

1.1 Goals and Design

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".

1.2 System Requirements

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.

1.3 Parts of the System

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.

1.4 User Interface

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.

1.5 Preferences and Their Attributes

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.

1.6 Profiles

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.

1.7 Future Extension

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.

2 Using Internet Config Services

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.

2.1 Initialising Internet Config

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.

2.2 Basic Preference Operations

This section describes some basic preference operations, such as getting normal preferences, monitoring for preference changes, and getting all preferences.

Getting Preference Information

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.

Preference Coherency

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.

Indexing All Preferences

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 *)
 

2.3 High-Level Operations

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.

Launching a URL

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 *)
 

Command Clicking

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.

Extension Mapping Incoming Files

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.

Extension Mapping Outgoing Files

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.

3 Keys

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.

3.1 Key Space

Keys must are Str255s 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.

3.2 Currently Defined Keys

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.

3.3 Simple Key Types

A number of keys are based around common string types. This section describes some features of those keys.

Common Types

The following common types are used, with their standard definitions.

Scrambled PStrings

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.

Formatted PString and STR# Preferences

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
 

Host PStrings

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.

3.4 Complex Key Types

"InternetConfig.[ph]" defines a number of data types that are the type of various preferences. The following types are defined:

Font Specification

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;
 

Application Specification

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.

File Specification

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.

Character Set Specification

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:

  1. It is the same table used by a variety of common Mac OS applications to do this transformation.
  2. It provides lossless roundtrip conversion. If you take a Mac Roman document, use this table to map it to Mac Net ASCII, send it across the net, and then another Mac uses this table to map it back to Mac Roman, they will get the same thing you sent.

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.

File Type Mappings

This is used to specify extension and MIME mappings. It is discussed in detail in a later section.

Services

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.

4 API Reference

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:

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.

4.1 Types and Constants

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 longints 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.

4.2 Core API Routines

This section documents the routines that make up the core of the Internet Config API.

Starting and Stopping Internet Config

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.


Getting Information About an Instance

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.


Preparing to Read and Write Preferences

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.


Reading and Writing Preferences

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.


Enumerating All Preferences

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.


Accessing the User Interface

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.


4.3 URL Routines

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:

  1. if there is a selection skip to step 4
  2. expand the selection forwards to beginning of the word and backwards to the end of the word (never skip a bracket)
  3. if either end has an bracket then expand the other end to search for matching bracket
  4. strip trailing and leading whitespace
  5. strip whitespace return whitespace sequences
  6. take off angle brackets if necessary, or
  7. strip a trailing "." unless there was originally a selection
  8. remove any leading URL:
  9. extract protocol by looking forwards for ":"
  10. if no protocol and hint is not empty then prepend with hint ":"

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.


4.4 Mapping Database Routines

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.

Mapping Preference Structure

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
The total length of this entry, in bytes, including both the fixed length, the length of the packed Pascal strings at the end of the entry, and the length of the user data that follow those strings.
fixed_length
The length of the fixed part of the entry, in bytes. With this version of the type, that length is always a constant, ie ICmap_fixed_length.
version
The version of this entry. The only version that's currently defined is version 0.
file_type
The File Manager file type associated with this entry, eg 'ZIP '.
file_creator
The File Manager creator code associated with this entry, eg 'ZIP '.
post_creator
The creator code of the post-processing application for this entry, eg '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
Flags associated with this entry. The definitions for these flags are listed after this table.
extension
The file extension associated with this entry, eg ".zip".
creator_app_name
The name of the application that corresponds to the 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
The name of the application that corresponds to the 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
The MIME type associated with the entry, eg "application/zip".
entry_name
A user visible name associated with this entry, eg "PC ZIP Archive".

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.

High-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.


Mid-Level Routines

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.


Low-Level 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.


Interrupt Safe File Mapping Routines

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.


Post-Processing

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.

User Data

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.

4.5 Profile Routines

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.


4.6 Obsolete and Private Routines

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.

Component Interface

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.

Semi-Private 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.


Obsolete Routines

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:

  1. Unlike 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.
  2. If the preference does not exist, this routine will create and return an empty handle. This directly contradicts the style set by 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.


Private Routines

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.


A Technical Notes

This appendix contains a number of technical notes about how to use Internet Config correctly.

A.1 Text Files and the Editor Helper

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.

A.2 Binary Stamp Identification with Internet Config

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

Eric Kidd

A.3 Dealing with Missing API Routines

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:

Of course the approach that you take is up to you.

A.4 Determining If IC Is Installed

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.

A.5 Commonly Misinterpreted Keys

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.

A.6 Internet Explorer Extensions

[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

B Recommended Reading

Internet Config

Components

URLs and the Get URL Suite


This document is Public Domain (really, we mean it!). No Rights Reserved

Comments: ic@stairways.com