gecko-dev/cmd/macfe/central/umimemap.cp
pinkerton%netscape.com 6b5406fb2f remove volatile.
1998-09-29 17:33:46 +00:00

1174 lines
32 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
// umimemap.cp
// CMimeMapper class and CMimeList
// Created by atotic, June 6th, 1994
// ===========================================================================
// Front End
#include "umimemap.h"
#include "macutil.h"
#include "resae.h"
#include "uapp.h"
#include "CAppleEventHandler.h"
#include "ulaunch.h"
#include "ufilemgr.h"
#include "uprefd.h"
#include "resgui.h"
#include "BufferStream.h"
// Netscape
#include "client.h"
#include "net.h"
// stdc
#include <String.h>
#include "uerrmgr.h"
#include "prefapi.h"
//----------------------------------------------------------------------
// class CMimeMapper
//----------------------------------------------------------------------
// ¥¥ Constructors/destructors
// Initialize all the variables to defaults
// Every initializer calls it
void CMimeMapper::InitMapper()
{
fTemporary = false; // This is only a temporary mapper
fRegisteredViewer = false; // This is a registered viewer, use all the special codes
fTempAppSig = '????'; // Application
fTempFileType = '????'; // File signature
fFromOldPrefs = false; // Was this mapper from pre-plugin prefs?
fLatentPlugin = false; // Was the plug-in disabled because itÕs missing?
fLoadAction = CMimeMapper::Unknown;
fBasePref = nil; // Corresponding XP pref branch name
fIsLocked = false; // was this locked by Mission Control (disables edit and delete)
fNumChildrenFound = 0;
fFileFlags = 0;
}
Boolean
CMimeMapper::NetscapeCanHandle(const CStr255& mimeType)
{
return ((mimeType == IMAGE_GIF) ||
(mimeType == IMAGE_JPG) ||
(mimeType == IMAGE_XBM) ||
(mimeType == IMAGE_PNG) ||
(mimeType == APPLICATION_BINHEX) ||
(mimeType == HTML_VIEWER_APPLICATION_MIME_TYPE) ||
(mimeType == APPLICATION_PRE_ENCRYPTED) ||
(mimeType == TEXT_PLAIN));
}
// ¥¥ XP Prefs
//
// CMimeMapper is technically now redundant because all the information
// it contains is also reflected in xp prefs. Keeping duplicate
// structures is not ideal but it's minimal-impact.
//
// The xp pref routines are:
// - CreateMapperFor: creates a mapper corresponding to a specific
// xp mime pref (e.g. mime.image_gif)
// - ReadMimePref: converts an xp pref to a mapper (called from
// CreateMapperFor, and when xp prefs are updated by auto-config).
// - WriteMimePref: converts a mapper to its xp pref representation
// (called when initializing 3.0-format preferences, after editing
// a mime type from its pref UI, and when registering plug-ins).
static const char* Pref_MimeType = ".mimetype";
static const char* Pref_AppName = ".mac_appname";
static const char* Pref_AppSig = ".mac_appsig";
static const char* Pref_FileType = ".mac_filetype";
static const char* Pref_Extension = ".extension";
static const char* Pref_PluginName = ".mac_plugin_name";
static const char* Pref_Description = ".description";
static const char* Pref_LoadAction = ".load_action";
static const char* Pref_LatentPlugin = ".latent_plug_in";
static const char* Pref_FileFlags = ".file_flags";
#ifdef ANTHRAX
static const char* Pref_AppletName = ".applet";
#endif
// CreateMapperFor converts an xp pref name into a mimetype mapper.
// Finds an existing mapper or creates a new one, and
// populates its fields from user pref values.
CMimeMapper* CMimeMapper::CreateMapperFor( const char* basepref, Boolean newPrefFormat )
{
CMimeMapper* mapper = CPrefs::sMimeTypes.FindBasePref(basepref);
if (mapper) {
mapper->ReadMimePrefs();
return NULL; // already exists; caller doesn't use it.
}
else {
mapper = new CMimeMapper(basepref);
// FromOldPrefs triggers plug-ins to install themselves as preferred viewers
mapper->fFromOldPrefs = !newPrefFormat;
// Throw out this mime mapper if we didn't find any useful info about it in prefs
if (mapper->GetNumChildrenFound() <= 0)
{
delete mapper;
return NULL;
}
else
return mapper;
}
}
// CreateMapperForRes converts a 3.0-format mime resource
// into both a mimetype mapper and xp prefs.
CMimeMapper* CMimeMapper::CreateMapperForRes(Handle res)
{
SInt8 flags = ::HGetState(res);
::HNoPurge(res);
CMimeMapper* mapper = new CMimeMapper( res );
::HSetState(res, flags);
if (mapper->fMimeType == HTML_VIEWER_APPLICATION_MIME_TYPE) {
mapper->SetLoadAction(CPrefs::sViewSourceInline ? Internal : Launch);
}
mapper->WriteMimePrefs(false); // convert to xp format
mapper->SetDefaultDescription();
return mapper;
}
// Constructor to build mapper from xp prefs
CMimeMapper::CMimeMapper( const char* basepref )
{
InitMapper();
fBasePref = XP_STRDUP(basepref);
if (strlen(fBasePref) > 200)
fBasePref[200] = '\0';
ReadMimePrefs();
SetDefaultDescription();
}
// Copies an xp mimetype pref into the mapper
void CMimeMapper::ReadMimePrefs()
{
int err;
int size = 256,
actualSize;
char value[256];
fAppSig = 0;
fNumChildrenFound = 0; // keep track of how many mapper prefs we successfully read in from prefs file
err = PREF_GetCharPref( CPrefs::Concat(fBasePref, Pref_AppSig), value, &size );
if (PREF_NOERROR == err)
{
fNumChildrenFound++;
// values stored as binary (Base64) will always be longer than 4 bytes
// so if the string is 4 bytes long (or shorter), it's a straight character string
actualSize = strlen(value);
if (actualSize <= sizeof(OSType))
::BlockMoveData(value, (Ptr) &fAppSig, actualSize );
else
{
err = PREF_GetBinaryPref ( CPrefs::Concat( fBasePref, Pref_AppSig ), value, &size);
if (PREF_NOERROR == err)
::BlockMoveData( value, (Ptr) &fAppSig, sizeof(OSType) );
}
}
fFileType = 0;
err = PREF_GetCharPref( CPrefs::Concat(fBasePref, Pref_FileType), value, &size );
if (PREF_NOERROR == err)
{
// see notes above for fAppSig
fNumChildrenFound++;
actualSize = strlen(value);
if (actualSize <= sizeof(OSType))
::BlockMoveData( value, (Ptr) &fFileType, actualSize );
else
{
err = PREF_GetBinaryPref ( CPrefs::Concat( fBasePref, Pref_FileType ), value, &size);
if (PREF_NOERROR == err)
::BlockMoveData( value, (Ptr) &fFileType, sizeof(OSType) );
}
}
#ifdef ANTHRAX
err = PREF_GetCharPref( CPrefs::Concat(fBasePref, Pref_AppletName), value, &size );
if (PREF_NOERROR == err)
{
fNumChildrenFound++;
fAppletName = value;
}
#endif
err = PREF_GetCharPref( CPrefs::Concat(fBasePref, Pref_AppName), value, &size );
if (PREF_NOERROR == err)
{
fNumChildrenFound++;
fAppName = value;
}
err = PREF_GetCharPref( CPrefs::Concat(fBasePref, Pref_MimeType), value, &size );
if (PREF_NOERROR == err)
{
fNumChildrenFound++;
fMimeType = value;
if (PREF_PrefIsLocked(CPrefs::Concat(fBasePref,Pref_MimeType))) //for mission control - this is a convienient place to check this
fIsLocked = true;
}
err = PREF_GetCharPref( CPrefs::Concat(fBasePref, Pref_Extension), value, &size );
if (PREF_NOERROR == err)
{
fNumChildrenFound++;
fExtensions = value;
}
err = PREF_GetCharPref( CPrefs::Concat(fBasePref, Pref_PluginName), value, &size );
if (PREF_NOERROR == err)
{
fNumChildrenFound++;
fPluginName = value;
}
err = PREF_GetCharPref( CPrefs::Concat(fBasePref, Pref_Description), value, &size );
if (PREF_NOERROR == err)
{
fNumChildrenFound++;
fDescription = value;
}
int32 intvalue;
err = PREF_GetIntPref( CPrefs::Concat(fBasePref, Pref_LoadAction), &intvalue );
if (PREF_NOERROR == err)
{
fNumChildrenFound++;
fLoadAction = (LoadAction) intvalue;
}
err = PREF_GetIntPref( CPrefs::Concat(fBasePref, Pref_FileFlags), &intvalue );
if (PREF_NOERROR == err)
{
fFileFlags = intvalue;
}
XP_Bool boolvalue;
err = PREF_GetBoolPref( CPrefs::Concat(fBasePref, Pref_LatentPlugin), &boolvalue );
if (PREF_NOERROR == err)
{
fNumChildrenFound++;
fLatentPlugin = (Boolean) boolvalue;
}
}
// -- For 3.0 format compatability --
// Initializes off a resource handle of one of the mime types.
// See pref.r for resource definition
// The memory layout of the resource is very important.
#define NAME_LENGTH_OFFSET 9
CMimeMapper::CMimeMapper( Handle resRecord )
{
ThrowIfNil_(resRecord);
Size size = ::GetHandleSize(resRecord);
InitMapper();
char temp[256];
Ptr resPtr = *resRecord;
Byte lengthByte = 0;
long offset = 0;
ThrowIf_(size < 9);
::BlockMoveData( resPtr, (Ptr)&fAppSig, 4 );
::BlockMoveData( resPtr + 4, (Ptr)&fFileType, 4 );
uint32 loadAction = *((unsigned char*) (resPtr + 8));
fLoadAction = (LoadAction) loadAction;
offset = NAME_LENGTH_OFFSET;
ThrowIf_(offset > size);
// ¥ read the application name
lengthByte = resPtr[ offset ];
ThrowIf_(lengthByte > 255);
::BlockMoveData( &resPtr[ offset + 1 ], (Ptr)temp, lengthByte );
temp[ lengthByte ] = 0;
fAppName = temp;
// ¥ read in the MIME type
offset += ( lengthByte + 1 );
ThrowIf_(offset > size);
lengthByte = resPtr[ offset ];
::BlockMoveData( &resPtr[ offset + 1 ], temp, lengthByte );
temp[ lengthByte ] = 0;
fMimeType = temp;
// ¥ read in the extensions string
offset += ( lengthByte + 1 );
ThrowIf_(offset > size);
lengthByte = resPtr[ offset ];
ThrowIf_(lengthByte > 255);
::BlockMove ( &resPtr[ offset + 1 ], temp, lengthByte );
temp[ lengthByte ] = 0;
fExtensions = temp;
// ¥ read in the plug-in name string
// Since the plug-in name doesn't exist in 2.0-vintage prefs resources, make
// sure to check that there is still data left in the handle before proceeding.
offset += ( lengthByte + 1 );
if (offset < size)
{
lengthByte = resPtr[ offset ];
ThrowIf_(lengthByte > 255);
::BlockMove ( &resPtr[ offset + 1 ], temp, lengthByte );
temp[ lengthByte ] = 0;
fPluginName = temp;
// Now read the description string (it too could be non-existant)
offset += ( lengthByte + 1 );
if (offset < size)
{
lengthByte = resPtr[ offset ];
ThrowIf_(lengthByte > 255);
::BlockMove ( &resPtr[ offset + 1 ], temp, lengthByte );
temp[ lengthByte ] = 0;
fDescription = temp;
// Last, read the "latent plug-in" flag (if it exists)
offset += ( lengthByte + 1 );
if (offset < size)
{
Boolean latentPlugin = *((unsigned char*) (resPtr + offset));
XP_ASSERT(latentPlugin == 0 || latentPlugin == 1);
fLatentPlugin = latentPlugin;
}
}
}
else
{
fPluginName = "";
fFromOldPrefs = true; // No plug-in info in these prefs
}
}
//
// For old prefs that don't have a description stored in the handle,
// look up the default description in netlib, defaulting to just the
// MIME type itself if it can't be found. This description will then
// get written out to the prefs so it can be edited and saved by the user.
//
void CMimeMapper::SetDefaultDescription()
{
if (fMimeType == HTML_VIEWER_APPLICATION_MIME_TYPE)
return;
if (fNumChildrenFound <= 0 && fMimeType == CStr255::sEmptyString) // this means the pref did not even come with a mime type, so we ignore it.
return;
CStr255 description;
char* mimetype = (char*) fMimeType;
NET_cdataStruct* cdata = NULL;
XP_List* list_ptr = cinfo_MasterListPointer();
while ((cdata = (NET_cdataStruct*) XP_ListNextObject(list_ptr)) != NULL)
if (strcasecomp(mimetype, cdata->ci.type) == 0)
break;
if (cdata && cdata->ci.desc)
description = cdata->ci.desc;
else
description = fMimeType;
if (fDescription == CStr255::sEmptyString) {
fDescription = description;
CPrefs::SetModified();
}
PREF_SetDefaultCharPref( CPrefs::Concat(fBasePref, Pref_Description), description );
}
// Duplicate
CMimeMapper::CMimeMapper( const CMimeMapper& clone )
{
InitMapper();
fMimeType = clone.fMimeType;
fAppName = clone.fAppName;
fAppSig = clone.fAppSig;
fFileType = clone.fFileType;
fLoadAction = clone.fLoadAction;
fExtensions = clone.fExtensions;
fTemporary = clone.fTemporary;
fRegisteredViewer = clone.fRegisteredViewer;
fTempAppSig = clone.fTempAppSig; // Application
fTempFileType = clone.fTempFileType; // File signature
fPluginName = clone.fPluginName;
fFromOldPrefs = clone.fFromOldPrefs;
fDescription = clone.fDescription;
fLatentPlugin = clone.fLatentPlugin;
fFileFlags = clone.fFileFlags;
fBasePref = clone.fBasePref ? XP_STRDUP(clone.fBasePref) : nil;
fIsLocked = clone.fIsLocked;
fNumChildrenFound = clone.fNumChildrenFound;
#ifdef ANTHRAX
fAppletName = clone.fAppletName;
#endif
}
// Copies values from the mapper to xp user pref tree.
void CMimeMapper::WriteMimePrefs( Boolean )
{
if (fBasePref == nil) {
// Convert mime type to a pref-name string with
// non-alpha chars replaced with underscores
char typecopy[256];
strcpy(typecopy, "mime.");
int start = strlen(typecopy);
strncat(typecopy, (char*) fMimeType, 200);
for (int i = start; i < strlen(typecopy); i++)
if (!isalnum(typecopy[i]))
typecopy[i] = '_';
// Mimetype may be blank, so pick an arbitrary name
if (fMimeType.Length() == 0)
strcat(typecopy, "unknown_type_999");
fBasePref = XP_STRDUP(typecopy);
}
char appsig[sizeof(OSType)+1], filetype[sizeof(OSType)+1];
appsig[sizeof(OSType)] = '\0';
::BlockMoveData( (Ptr) &fAppSig, appsig, sizeof(OSType) );
filetype[sizeof(OSType)] = '\0';
::BlockMoveData( (Ptr) &fFileType, filetype, sizeof(OSType) );
PREF_SetCharPref( CPrefs::Concat(fBasePref, Pref_Extension), fExtensions );
fNumChildrenFound++; // since we are creating children prefs, we should keep this record updated
if (fDescription.Length() > 0)
{
PREF_SetCharPref( CPrefs::Concat(fBasePref, Pref_Description), fDescription );
fNumChildrenFound++;
}
PREF_SetCharPref( CPrefs::Concat(fBasePref, Pref_MimeType), fMimeType );
PREF_SetCharPref( CPrefs::Concat(fBasePref, Pref_AppName), fAppName );
PREF_SetCharPref( CPrefs::Concat(fBasePref, Pref_PluginName), fPluginName );
#ifdef ANTHRAX
PREF_SetCharPref( CPrefs::Concat(fBasePref, Pref_AppletName), fAppletName );
++fNumChildrenFound;
#endif
fNumChildrenFound += 3;
// store appsig and filetype as 4-byte character strings, if possible.
// otherwise, store as binary.
if (PrintableChars( &appsig, sizeof(OSType)))
PREF_SetCharPref( CPrefs::Concat(fBasePref, Pref_AppSig), appsig );
else
PREF_SetBinaryPref( CPrefs::Concat(fBasePref, Pref_AppSig), appsig, sizeof(OSType));
if (PrintableChars( &filetype, sizeof(OSType)))
PREF_SetCharPref( CPrefs::Concat(fBasePref, Pref_FileType), filetype );
else
PREF_SetBinaryPref( CPrefs::Concat(fBasePref, Pref_FileType), filetype, sizeof(OSType));
PREF_SetIntPref( CPrefs::Concat(fBasePref, Pref_LoadAction), fLoadAction );
PREF_SetBoolPref( CPrefs::Concat(fBasePref, Pref_LatentPlugin), (XP_Bool) fLatentPlugin );
PREF_SetIntPref ( CPrefs::Concat(fBasePref, Pref_FileFlags ), fFileFlags );
fNumChildrenFound += 4;
}
CMimeMapper::CMimeMapper(
LoadAction loadAction,
const CStr255& mimeType,
const CStr255& appName,
const CStr255& extensions,
OSType appSignature,
OSType fileType )
{
fLoadAction = loadAction;
fMimeType = mimeType;
fAppName = appName;
fAppSig = appSignature;
fFileType = fileType;
fExtensions = extensions;
fTemporary = false;
fRegisteredViewer = false;
fPluginName = "";
fDescription = "";
fLatentPlugin = false;
fBasePref = nil;
#ifdef ANTHRAX
fAppletName = "";
#endif
}
// Disposes of all allocated structures
CMimeMapper::~CMimeMapper()
{
if (fBasePref)
free (fBasePref);
}
CMimeMapper & CMimeMapper::operator= (const CMimeMapper& mapper)
{
fMimeType = mapper.fMimeType;
fAppName = mapper.fAppName;
fAppSig = mapper.fAppSig;
fFileType = mapper.fFileType;
fLoadAction = mapper.fLoadAction;
fPluginName = mapper.fPluginName;
fFromOldPrefs = mapper.fFromOldPrefs;
fDescription = mapper.fDescription;
fLatentPlugin = mapper.fLatentPlugin;
fFileFlags = mapper.fFileFlags;
#ifdef ANTHRAX
fAppletName = mapper.fAppletName;
#endif
return *this;
}
#ifdef ANTHRAX
void CMimeMapper::SetAppletName(const CStr255& newAppletName)
{
fAppletName = newAppletName;
}
#endif /* ANTHRAX */
void CMimeMapper::SetAppName(const CStr255& newName)
{
fAppName = newName;
}
void CMimeMapper::SetMimeType(const CStr255& newType)
{
fMimeType = newType;
}
void CMimeMapper::SetAppSig(OSType newSig)
{
fAppSig = newSig;
}
void CMimeMapper::SetAppSig(FSSpec& appSpec)
{
FInfo finderInfo;
OSErr err = FSpGetFInfo(&appSpec, &finderInfo );
ThrowIfOSErr_(err);
fAppSig = finderInfo.fdCreator;
}
void CMimeMapper::SetExtensions(const CStr255& newExtensions)
{
fExtensions = newExtensions;
SyncNetlib();
}
void CMimeMapper::SetDocType(OSType newType)
{
fFileType = newType;
}
void CMimeMapper::SetLoadAction(LoadAction newAction)
{
//
// If the user explicitly changed the load action,
// cancel the latent plug-in setting so their choice
// is maintained persistently.
//
fLatentPlugin = false;
//
// If the user explicitly changed the load action,
// cancel the registration of an external viewer.
//
fRegisteredViewer = false;
fLoadAction = newAction;
SyncNetlib();
}
void CMimeMapper::SetLatentPlugin()
{
fLatentPlugin = true;
fLoadAction = CMimeMapper::Unknown;
}
void CMimeMapper::RegisterViewer(OSType tempAppSig, OSType tempFileType)
{
fTempAppSig = tempAppSig; // Application
fTempFileType = tempFileType; // File signature
fRegisteredViewer = TRUE;
CFrontApp::RegisterMimeType(this);
}
void CMimeMapper::UnregisterViewer()
{
fRegisteredViewer = FALSE;
CFrontApp::RegisterMimeType(this);
}
// Typical Spy apple event creation routine.
// It creates the event, with two arguments, URL and the MIME type.
// URL type is stored as attribute urlAttribute, MIME type as AE_spy_viewDocFile_mime
OSErr CMimeMapper::MakeSpyEvent(AppleEvent & event,
URL_Struct * request,
AEEventID id,
AEKeyword urlAttribute)
{
OSErr err;
Try_
{
AEAddressDesc targetApp;
// Specify the application, and create the event
{
err = ::AECreateDesc(typeApplSignature, &fTempAppSig,
sizeof(fTempAppSig), &targetApp);
ThrowIfOSErr_(err);
OSErr err = ::AECreateAppleEvent(AE_spy_send_suite, id,
&targetApp,
kAutoGenerateReturnID,
kAnyTransactionID,
&event);
::AEDisposeDesc(&targetApp);
}
// URL
{
AEDesc urlDesc;
err = ::AECreateDesc(typeChar, request->address, strlen(request->address), &urlDesc);
ThrowIfOSErr_(err);
err = ::AEPutParamDesc(&event, urlAttribute, &urlDesc);
ThrowIfOSErr_(err);
::AEDisposeDesc(&urlDesc);
}
// MIME type
if (request->content_type)
{
AEDesc mimeDesc;
err = ::AECreateDesc(typeChar, request->content_type, strlen(request->content_type), &mimeDesc);
ThrowIfOSErr_(err);
err = ::AEPutParamDesc(&event, AE_spy_queryViewer_mime, &mimeDesc);
::AEDisposeDesc(&mimeDesc);
}
}
Catch_(inErr)
{ return inErr;
}
EndCatch_
return noErr;
}
/* Returns whether the contents of inBuffer are all visible, printable characters.
Actually, that's a misnomer ... it returns whether the contents are printable
on a Macintosh, which we define to be "not control characters." This is intended
for determining whether a binary buffer can be stored as a text string, or needs
to be encoded. */
Boolean CMimeMapper::PrintableChars(const void *inBuffer, Int32 inLength)
{
unsigned char *buffer = (unsigned char *) inBuffer,
*bufferEnd = buffer + inLength;
while (buffer < bufferEnd)
{
if (*buffer <= 0x20)
break;
buffer++;
}
return buffer == bufferEnd;
}
// if we have a registered viewer, query it for a file spec
OSErr CMimeMapper::GetFileSpec(URL_Struct * request,
FSSpec & destination)
{
OSErr err = noErr;
if (!fRegisteredViewer)
return fnfErr;
else
Try_
{
AppleEvent queryViewerEvent;
err = MakeSpyEvent(queryViewerEvent, request,AE_spy_queryViewer,
keyDirectObject); // URL is the direct object
// Send the event
AppleEvent reply;
err = ::AESend(&queryViewerEvent,&reply,kAEWaitReply,kAEHighPriority,600,nil, nil);
ThrowIfOSErr_(err);
// Handle the reply. We want to get out the transaction ID
{
DescType realType;
Size actualSize;
if (!MoreExtractFromAEDesc::DisplayErrorReply(reply))
{
err = ::AEGetParamPtr(&reply, keyAEResult, typeFSS,
&realType, &destination, sizeof(destination),
&actualSize);
ThrowIfOSErr_(err);
}
else
Throw_(fnfErr);
::AEDisposeDesc(&reply);
}
}
Catch_(inErr)
{
return inErr;
} EndCatch_
return noErr;
}
// If we have a registered viewer, use ViewDocFile
// if anything fails, or we have no viewer, use ordinary launch sequence
OSErr CMimeMapper::LaunchFile(LFileBufferStream * inFile, URL_Struct * request, Int32 windowID)
{
OSErr err;
if (fRegisteredViewer)
{
Try_
{
AppleEvent viewDocEvent;
// AppleEvent(fileSpec, URL, MIME, windowID)
// Create the event, with MIME and URL arguments
err = MakeSpyEvent(viewDocEvent, request,AE_spy_viewDocFile,
AE_spy_viewDocFile_url); // URL is the direct object
ThrowIfOSErr_(err);
// Make the file spec
FSSpec fileSpec;
inFile->GetSpecifier(fileSpec);
err = ::AEPutParamPtr(&viewDocEvent,keyDirectObject,typeFSS,&fileSpec,sizeof(fileSpec));
ThrowIfOSErr_(err);
// URL
if (request->address)
{
err = ::AEPutParamPtr(&viewDocEvent,AE_spy_viewDocFile_url, typeChar, request->address,strlen(request->address));
ThrowIfOSErr_(err);
}
// MIME type
{
StAEDescriptor mimeDesc((StringPtr)fMimeType);
err = ::AEPutParamDesc(&viewDocEvent,AE_spy_viewDocFile_mime,&mimeDesc.mDesc);
ThrowIfOSErr_(err);
}
// WindowID
{
StAEDescriptor windowDesc(windowID);
err = ::AEPutParamDesc(&viewDocEvent,AE_spy_viewDocFile_wind, &windowDesc.mDesc);
ThrowIfOSErr_(err);
}
// Send the event
AppleEvent reply;
err = ::AESend(&viewDocEvent,&reply,kAEWaitReply,kAEHighPriority,60,nil, nil);
if (err != errAETimeout)
ThrowIfOSErr_(err);
// If we got an error code back, launch as usual
if (reply.descriptorType != typeNull)
{
DescType realType;
Size actualSize;
long errNumber;
err = ::AEGetParamPtr(&reply, keyErrorNumber, typeLongInteger,
&realType, &errNumber, sizeof(errNumber),
&actualSize);
if ((err == noErr) && (errNumber != noErr))
Throw_(errNumber);
}
}
Catch_(inErr)
{
// Launch through ViewDoc failed, use our original creator and launch as usual
FSSpec fileSpec;
inFile->GetSpecifier(fileSpec);
err = CFileMgr::SetFileTypeCreator(fAppSig, fFileType, &fileSpec);
::LaunchFile(inFile);
}
EndCatch_
}
else
::LaunchFile(inFile);
return noErr;
}
void CMimeMapper::SyncNetlib()
{
CFrontApp::RegisterMimeType(this);
}
//----------------------------------------------------------------------
// class CMimeList
//----------------------------------------------------------------------
// ¥¥ Constructors/destructors
CMimeList::CMimeList() : LArray()
{
}
CMimeList::~CMimeList()
{
}
// ¥¥ÊUtility functions
// Overrode this on the assumption that every time you add a MIME mapper
// to this list, you want to sync up the netlib
ArrayIndexT CMimeList::InsertItemsAt(
Uint32 inCount,
ArrayIndexT inAtIndex,
const void *inItem,
Uint32 inItemSize)
{
ArrayIndexT result = LArray::InsertItemsAt(inCount,inAtIndex,inItem, inItemSize);
CMimeMapper ** mapperPtr = (CMimeMapper **)inItem;
(*mapperPtr)->SyncNetlib();
return result;
}
// Deletes all the items in the list
void CMimeList::DeleteAll(Boolean)
{
CMimeMapper* oldMap;
for ( short i = 1; i <= mItemCount; i++ )
{
FetchItemAt( i, &oldMap );
delete oldMap;
}
RemoveItemsAt( mItemCount,1);
}
// Finds a CMimeMapper with a given XP pref branch name.
CMimeMapper* CMimeList::FindBasePref( const char* basepref )
{
CMimeMapper* foundMap = NULL;
for ( Int32 i = mItemCount; i >= 1; i-- )
{
CMimeMapper* oldMap;
FetchItemAt( i, &oldMap );
if ( oldMap->GetBasePref() != NULL &&
XP_STRCMP(oldMap->GetBasePref(), basepref) == 0 )
{
foundMap = oldMap;
break;
}
}
return foundMap;
}
// Finds a CMimeMapper with a given mimeType. NULL if none
// Search is linear. Might want to make it faster
CMimeMapper* CMimeList::FindMimeType( char* mimeType )
{
if ( mimeType == NULL )
return NULL;
CMimeMapper* foundMap = NULL;
for ( Int32 i = 1; i <= mItemCount; i++ )
{
CMimeMapper* oldMap;
FetchItemAt( i, &oldMap );
// MIME types are defined to be case insensitive
if ( XP_STRCASECMP (oldMap->GetMimeName(), mimeType) == 0 )
{
foundMap = oldMap;
break;
}
}
return foundMap;
}
// Find a "built-in" mime type, which were separate in 3.0
// but are integrated into the 4.0 mimetype list.
CMimeMapper* CMimeList::FindMimeType(BuiltIn mimeBuiltin)
{
char* mimetype = nil;
switch (mimeBuiltin) {
case HTMLViewer:
mimetype = HTML_VIEWER_APPLICATION_MIME_TYPE;
break;
case Telnet:
mimetype = TELNET_APPLICATION_MIME_TYPE;
break;
case Tn3270:
mimetype = TN3270_VIEWER_APPLICATION_MIME_TYPE;
break;
}
return FindMimeType(mimetype);
}
// FindMimeType finds a Mime mapper that
// matches this file's type and creator
// Algorithm is inexact
// TEXT files are not typed because netlib figures out the
// HTML files, and hqx ones have the extension
// Look for exact match.
// If not found, look for inexact one
// I don't like the above algorithm. There is no way of telling what type of match occured. In
// cases other than exact or FileType match the results are unsatisfactory.
// New behavoir is to return NULL and let the caller call CMimeList::FindCreatorFindCreator if
// needed. Provides a way to hand off to IC if needed.
CMimeMapper* CMimeList::FindMimeType(const FSSpec& inFileSpec)
{
FInfo fndrInfo;
OSErr err = FSpGetFInfo( &inFileSpec, &fndrInfo );
if ((err != noErr) || (fndrInfo.fdType == 'TEXT'))
return NULL;
CMimeMapper* fileTypeMatch = NULL;
// Find the cinfo (which gives us the MIME type) for this file name
NET_cinfo* cinfo = NET_cinfo_find_type((CStr255)inFileSpec.name);
for (Int32 i = 1; i <= mItemCount; i++)
{
CMimeMapper* map;
FetchItemAt(i, &map);
//
// If this MIME type is configured for a plug-in, see if it matches
// the MIME type of the file (based on the file extension -> MIME
// type mapping). If not, try for an exact or partial match based
// on type and/or creator. bing: If we decide to support Mac file
// types for plug-ins, we should check the file type here.
//
#ifdef ANTHRAX
if(map->GetLoadAction() == CMimeMapper::Plugin ||
map->GetLoadAction() == CMimeMapper::Applet)
#else
if (map->GetLoadAction() == CMimeMapper::Plugin)
#endif /* ANTHRAX */
{
if (cinfo && (map->GetMimeName() == cinfo->type))
return map;
}
else if ((map->GetAppSig() == fndrInfo.fdCreator) &&
(map->GetDocType() == fndrInfo.fdType) &&
!(map->GetFileFlags() &ICmap_not_outgoing_mask) )
return map;
else
{
#if 0
if (map->GetAppSig() == fndrInfo.fdCreator)
creatorMatch = map;
#endif
if (map->GetDocType() == fndrInfo.fdType && !(map->GetFileFlags() &ICmap_not_outgoing_mask) )
fileTypeMatch = map;
}
}
return fileTypeMatch;
}
// FindCreator finds a CMimeMapper whose application signature
// matches appSig
CMimeMapper* CMimeList::FindCreator(OSType appSig)
{
CMimeMapper * foundMap = NULL;
for (Int32 i = 1; i <= mItemCount; i++)
{
CMimeMapper* oldMap;
FetchItemAt(i, &oldMap);
if (oldMap->GetAppSig() == appSig)
{
foundMap = oldMap;
break;
}
}
return foundMap;
}
// ------------------------- Apple Event handling -------------------------------
// HandleRegisterViewerEvent
// registers a viewer.
// If the MIME type does not exist, we create one on the fly
void CMimeList::HandleRegisterViewerEvent(const AppleEvent &inAppleEvent,
AppleEvent &outAEReply,
AEDesc &/*outResult*/,
long /*inAENumber*/)
{
OSType appSign; // app signature
OSType fileType = 'TEXT'; // file type to be created
char * mimeType = NULL; // MIME type
OSType realType;
Size realSize;
CMimeMapper * mapper;
OSErr err;
Try_ {
{ // Get the application signature. Required
err = ::AEGetParamPtr(&inAppleEvent,keyDirectObject,typeApplSignature,
&realType, &appSign, sizeof(appSign), &realSize);
ThrowIfOSErr_(err);
}
{ // Get the MIME type. Required
AEDesc mimeDesc;
err = ::AEGetParamDesc(&inAppleEvent,AE_spy_registerViewer_mime,typeWildCard,&mimeDesc);
ThrowIfOSErr_(err);
MoreExtractFromAEDesc::TheCString(mimeDesc, mimeType);
::AEDisposeDesc(&mimeDesc);
if (mimeType == nil)
ThrowOSErr_(errAEWrongDataType);
}
mapper = FindMimeType(mimeType);
if (mapper)
fileType = mapper->GetDocType();
{ // Get the file type. If none is specified, it defaults to already registered type, or 'TEXT'
AEDesc fileTypeDesc;
err = ::AEGetParamDesc(&inAppleEvent,AE_spy_registerViewer_ftyp,typeWildCard,&fileTypeDesc);
if (err == noErr)
{
Try_ {
UExtractFromAEDesc::TheType(fileTypeDesc, fileType);
} Catch_(inErr)
{} EndCatch_
}
::AEDisposeDesc(&fileTypeDesc);
}
// We have all the arguments, set up the new mapper if necessary
if (mapper == NULL)
{
mapper = new CMimeMapper(CMimeMapper::Launch,
mimeType,
"-", // I10L - this string is never seen by a user
CStr255::sEmptyString,
appSign,
fileType);
ThrowIfNil_(mapper);
mapper->SetTemporary(TRUE);
InsertItemsAt( 1, LArray::index_Last, &mapper);
}
// If the type is being handled by a plug-in, donÕt let the AE override
Boolean result = false;
#ifdef ANTHRAX // is this addition for anthrax correct? - amusil
if (mapper->GetLoadAction() != CMimeMapper::Plugin &&
mapper->GetLoadAction() != CMimeMapper::Applet)
#else
if (mapper->GetLoadAction() != CMimeMapper::Plugin)
#endif /* ANTHRAX */
{
mapper->RegisterViewer(appSign, fileType);
result = true;
}
// Reply success
// Create the reply, it will automatically be stuck into the outgoing AE by PP
{
StAEDescriptor booleanDesc(result);
err = ::AEPutKeyDesc(&outAEReply,keyAEResult,&booleanDesc.mDesc); /* IM VI chap. 6 pg 91 */
}
}
Catch_(inErr)
{
MoreExtractFromAEDesc::MakeErrorReturn(outAEReply,
(unsigned char *)GetCString(REG_EVENT_ERR_RESID) , inErr);
} EndCatch_
// Dispose
if (mimeType) XP_FREE(mimeType);
}
void CMimeList::HandleUnregisterViewerEvent(const AppleEvent &inAppleEvent,
AppleEvent &outAEReply,
AEDesc &/*outResult*/,
long /*inAENumber*/)
{
OSType appSign; // app signature
OSType fileType = 'TEXT'; // file type to be created
char* mimeType = NULL; // MIME type
CMimeMapper * mapper = NULL;
OSErr err;
Try_
{
{ // Get the application signature. Required
AEDesc appSignDesc;
err = ::AEGetParamDesc(&inAppleEvent,keyDirectObject,typeWildCard,&appSignDesc);
ThrowIfOSErr_(err);
UExtractFromAEDesc::TheType(appSignDesc, appSign);
::AEDisposeDesc(&appSignDesc);
}
{ // Get the MIME type. Required
AEDesc mimeDesc;
err = ::AEGetParamDesc(&inAppleEvent,AE_spy_unregisterViewer_mime,typeWildCard,&mimeDesc);
ThrowIfOSErr_(err);
MoreExtractFromAEDesc::TheCString(mimeDesc, mimeType);
::AEDisposeDesc(&mimeDesc);
if (mimeType == nil)
ThrowOSErr_(errAEWrongDataType);
}
mapper = FindMimeType(mimeType);
if (mapper == NULL || mapper->IsViewerRegistered() == false)
{
//
// If the type isnÕt found, or wasnÕt registered
// to an external viewer, return an error.
//
MoreExtractFromAEDesc::MakeErrorReturn(outAEReply,
(unsigned char *)GetCString(APP_NOT_REG_RESID), errAEDescNotFound);
}
else
{
//
// Otherwise unregister the viewer and remove the
// temporary mapper if necessary.
//
mapper->UnregisterViewer();
if (mapper->IsTemporary())
{
Remove(&mapper);
delete mapper;
}
}
}
Catch_(inErr)
{
MoreExtractFromAEDesc::MakeErrorReturn(outAEReply,
(unsigned char *)GetCString(UNREG_EVENT_ERR_RESID), inErr);
}
EndCatch_
if (mimeType) XP_FREE(mimeType);
}