Read Me About QISA

1.0a3

QISA, or the Q Internet Setup Assistant, is a sample that demonstrates the basics of writing an Internet Setup Assistant for Mac OS. The sample is a Carbon application that runs on both Mac OS 9 and Mac OS X. It uses platform-specific technologies to configure the network for the appropriate platform. On Mac OS 9, it uses the Network Setup library for this, and on Mac OS X it uses the System Configuration framework. Platform-specific code is compiled into distinct bundles, which are then loaded and called at runtime via CFBundle.

QISA makes extensive use of the MoreIsBetter DTS sample code library. The Mac OS 9 platform support bundle uses the MoreNetworkSetup module as a high-level interface to the Network Setup library. The Mac OS X platform support bundle uses MoreSCF as a high-level interface to System Configuration framework, and MoreSecurity to execute that code in a privileged helper tool.

QISA runs on Mac OS 9 and higher (excluding Mac OS X 10.0.x, which did not support a public System Configuration framework API). On traditional Mac OS, it requires CarbonLib 1.6 or higher.

Packing List

The sample contains the following items.

Using the Sample

To try out the sample for yourself, launch the QISA application. The application will create a new setup window which is made up of a number of panels. When you've finished entering information in a panel, click Next. In the "Config" panel, click the "Do It" button to apply your changes. QISA will create a new network location (on Mac OS X, or a set of configurations on traditional Mac OS) named "QISA Test" and make those settings active.

Building the Sample

You can build the sample using Project Builder 2.1 from the December 2002 Mac OS X developer tools release. Open the project file (QISA.pbproj), make sure that the "QISA" target is selected, and choose Build from the Build menu. This will automatically build the application and the platform-support bundle.

The project also builds with CodeWarrior Pro 8.3 on Mac OS X 10.2. Open the project file (QISA.mcp), choose either the CFM or Mach-O target, and then choose Make from the Project menu. This will automatically build the application and the platform-support bundles.

The Mach-O target builds are provided to keep me honest and to allow easier debugging on Mac OS X. If you want to deploy a product, like QISA, that runs on Mac OS 9 and X, you would ship a CFM build.

How it Works

QISA is a modern Carbon-based application, packaged to support both Mac OS 9 and Mac OS X. The application is modern in the sense that it's entirely Carbon-event based and creates its user interface using NIBs. The application is divided into three main layers.

These layers share one key data structure, a dictionary of global values. The set of keys for this dictionary and their semantics are defined in "QISA.h". The accessor routines for this dictionary are all implemented in "QISA.c". There is one such dictionary per setup window. The application layer initialises the dictionary to its default value based on a property list file within the application bundle ("SetupInfo.plist", read by CopyGlobalValuesFromFile) supplemented by a property list from the platform plug-in ("PlatformProperties.plist", read by QISACopyPlatformProperties which is called by CopyGlobalValuesFromFile). Each panel reads values from the dictionary (using QISAGetGlobalValue) to display the current user settings and writes values to the dictionary (using QISASetGlobalValue) when those settings change. Finally, when the "Config" panel wants to create a new network location, it makes a copy of the global values dictionary (by calling QISACopyGlobalsDict) and passes that to the platform support layer, which creates the network location based on the information in the dictionary.

Application Layer

The application layer is surprisingly simple. It consists of just two source files "QISA.h" and "QISA.c". The first contains definitions and entry points that are used by the other two layers, for example, the global value dictionary routines described above. The file "QISA.c" contains the implementation of these routines and some application infrastructure. This infrastructure is surprisingly small due to the miracle of Carbon events.

The highlights of "QISA.c" are:

The application layer maintains per-window storage, in the form of the QISAState structure. A pointer to this state is held in the window's refCon. This state, in turn, holds a WindowRef for the window, along with all the other state needed to manage the window. This includes an array of panel state structures that holds the state for each panel within the window (the panels field), and a mutable CFDictionary that holds the global values (the globalValues field).

Panels Layer

The interface between the application and the panel layers is declared in "QISAPanels.h" (application to panel) and "QISA.h" (panel to application). The interface is object oriented in design, although implemented in C. The application maintains a per-panel data structure, QISAPanel, for each panel in the window. Every time it calls the panel or the panel calls it, a pointer to this data structure is passed. This data structure contains the state needed by the application layer to manage the panel, including a number of panel entry point function pointers. Each pointer is initialised to point to a default implementation (within "QISA.c"). When the panel is initialised it can override one or more of these default implementations by overwriting the entry point function pointers.

The entry points currently defined are:

The panel state also contains a refCon field that the panel can use to store a pointer to its own per-panel instance data structure.

There are three panels implemented in "QISAPanels.c".

Platform Support Layer

The platform support layer consists of two files, "QISAPlatform.h" and "QISAPlatform.c". The header file contains an abstract API that is supported by both platforms (traditional Mac OS and Mac OS X). The implementation file contains three main components.

There are two platform-specific bundles.

The platform-specific functions are listed below.

In addition, each platform has a properties dictionary (stored in the "PlatformProperties.plist") that is merged into the global values dictionary. This dictionary currently only contains a single key.

Mac OS X Security Considerations

As Mac OS X is multi-user system, the Mach-O platform has to be concerned about security. Specifically, it must be careful to prevent a non-privileged user from messing up the machine's network configuration. It does this by tagging any set that it creates with a custom key, and only allowing you to modify sets that contain that key. See CopyCustomDictionary and SetCustomDictionary in "QISASetupTool.c" for the details.

Ultimately I would like to be able to use Authorization Services to determine whether the user has the authority to create network locations and to switch the current location. However, limitations of the Authorization Services API prevent me from doing this at this time. Furthermore, the Apple menu (and its agent, the scselect command line tool) allows any user to switch the current location, so QISA does no worse than Mac OS X's out-of-the-box security.

Caveats

QISA is still very much a work in progress. Ultimately I want it to create a temporary network configuration, dial the modem, download a configuration file, disconnect the modem, and then set up a permanent network configuration based on the configuration file. At the moment all that it does is create a basic network configuration. However, it does this with a single binary on both traditional Mac OS and Mac OS X, which makes it a valuable example already.

I would also like to support plug-in panels in QISA. I didn't implement this feature in the current release because I wanted to ship it quickly. Also, there are technical problems with using NIB-based user interfaces in a plug-in environment on traditional Mac OS. Specifically, you can't move controls from one window to another on traditional Mac OS (EmbedControl returns paramErr in this case), so there's no way to load a panel's user interface from a NIB and then embed that user interface inside the main window.

To simplify the platform plug-in API, I make use of Core Foundation types. However, the CFM platform is linked with InterfaceLib and would not normally have access to Core Foundation. To work around this, I built my own Core Foundation stub library and linked to that. This works in practice and is not particularly bad in theory. After all, the CFM platform is being hosted in a CarbonLib application, which means that Core Foundation is already instantiated within the process. I'm just simplifying the job of calling it from InterfaceLib-based code. I'm also confident that this approach will remain compatible because CarbonLib and Mac OS 9 are no longer being updated.

There's currently no support for PPPoE. This would not be hard to do for Mac OS X but can't easily be supported on traditional Mac OS.

QISADoesNetworkConfigExist is not currently used for anything. The goal is to present a user interface to confirm the overwriting of the current QISA location.

On traditional Mac OS on certain machines, the machine dials the modem as soon as QISA has finished configuring the network. I believe that this is being caused by some background-only application, not by QISA itself. Curiously, if you disconnect, then set up the network again using QISA, it doesn't happen again. I discovered this problem during testing (version 1.0a2), however, I didn't consider it significant enough to hold up the release.

There are a number of other problems that I would classify as bugs. I didn't fix them because they didn't seem important enough.

Credits and Version History

If you find any problems with this sample, mail <DTS@apple.com> and I’ll try to fix them up.

1.0d1 (Nov 2002) was a pre-release version with limited functionality. It was distributed to a small number of developers at a kitchen.

1.0d2 (Nov 2002) was another pre-release version.

1.0a1 (Mar 2002) was yet another pre-release version.

1.0a2 (Apr 2003) was sent to a limited number of DTS engineers for internal testing.

1.0a3 (Apr 2003) was the first released version.

Share and Enjoy.

Apple Developer Technical Support
Networking, Communications, Hardware

25 Apr 2003