home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / cmd / macfe / central / umimemap.cp < prev    next >
Encoding:
Text File  |  1998-04-08  |  30.2 KB  |  1,063 lines

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. // umimemap.cp
  20. // CMimeMapper class and CMimeList
  21. // Created by atotic, June 6th, 1994
  22. // ===========================================================================
  23.  
  24. // Front End
  25. #include "umimemap.h"
  26. #include "macutil.h"
  27. #include "resae.h"
  28. #include "uapp.h"
  29. #include "CAppleEventHandler.h"
  30. #include "ulaunch.h"
  31. #include "ufilemgr.h"
  32. #include "uprefd.h"
  33. #include "resgui.h"
  34. #include "BufferStream.h"
  35.  
  36. // Netscape
  37. #include "client.h"
  38. #include "net.h"
  39. // stdc
  40. #include <String.h>
  41.  
  42. #include "uerrmgr.h"
  43. #include "prefapi.h"
  44.  
  45. //----------------------------------------------------------------------
  46. // class CMimeMapper
  47. //----------------------------------------------------------------------
  48.  
  49.     // ÑÑ Constructors/destructors
  50.  
  51. // Initialize all the variables to defaults
  52. // Every initializer calls it
  53. void CMimeMapper::InitMapper()
  54. {
  55.     fTemporary = false;            // This is only a temporary mapper
  56.     fRegisteredViewer = false;    // This is a registered viewer, use all the special codes
  57.     fTempAppSig = '????';        // Application
  58.     fTempFileType = '????';        // File signature
  59.     fFromOldPrefs = false;        // Was this mapper from pre-plugin prefs?
  60.     fLatentPlugin = false;        // Was the plug-in disabled because it╒s missing?
  61.     fLoadAction = CMimeMapper::Unknown;
  62.     fBasePref = nil;            // Corresponding XP pref branch name
  63. }
  64.  
  65.  
  66. Boolean
  67. CMimeMapper::NetscapeCanHandle(CStr255& mimeType)
  68. {
  69.     return ((mimeType == IMAGE_GIF) ||
  70.             (mimeType == IMAGE_JPG) ||
  71.             (mimeType == IMAGE_XBM) ||
  72.             (mimeType == APPLICATION_BINHEX) ||
  73.             (mimeType == HTML_VIEWER_APPLICATION_MIME_TYPE) ||
  74.             (mimeType == TEXT_PLAIN));
  75. }
  76.  
  77.  
  78. // ÑÑ XP Prefs
  79. //
  80. // CMimeMapper is technically now redundant because all the information 
  81. // it contains is also reflected in xp prefs.  Keeping duplicate
  82. // structures is not ideal but it's minimal-impact.
  83. //
  84. // The xp pref routines are:
  85. //  - CreateMapperFor: creates a mapper corresponding to a specific
  86. //    xp mime pref (e.g. mime.image_gif)
  87. //  - ReadMimePref: converts an xp pref to a mapper (called from
  88. //      CreateMapperFor, and when xp prefs are updated by auto-config).
  89. //    - WriteMimePref: converts a mapper to its xp pref representation
  90. //      (called when initializing 3.0-format preferences, after editing
  91. //      a mime type from its pref UI, and when registering plug-ins).
  92.  
  93. static const char* Pref_MimeType = ".mimetype";
  94. static const char* Pref_AppName = ".mac_appname";
  95. static const char* Pref_AppSig = ".mac_appsig";
  96. static const char* Pref_FileType = ".mac_filetype";
  97. static const char* Pref_Extension = ".extension";
  98. static const char* Pref_PluginName = ".mac_plugin_name";
  99. static const char* Pref_Description = ".description";
  100. static const char* Pref_LoadAction = ".load_action";
  101. static const char* Pref_LatentPlugin = ".latent_plug_in";
  102.  
  103. // CreateMapperFor converts an xp pref name into a mimetype mapper.
  104. // Finds an existing mapper or creates a new one, and
  105. // populates its fields from user pref values.
  106. CMimeMapper* CMimeMapper::CreateMapperFor( const char* basepref, Boolean newPrefFormat )
  107. {
  108.     CMimeMapper* mapper = CPrefs::sMimeTypes.FindBasePref(basepref);
  109.  
  110.     if (mapper) {
  111.         mapper->ReadMimePrefs();
  112.         return NULL;    // already exists; caller doesn't use it.
  113.     }
  114.     else {
  115.         mapper = new CMimeMapper(basepref);
  116.         // FromOldPrefs triggers plug-ins to install themselves as preferred viewers
  117.         mapper->fFromOldPrefs = !newPrefFormat;
  118.         return mapper;
  119.     }
  120. }
  121.  
  122. // CreateMapperForRes converts a 3.0-format mime resource
  123. // into both a mimetype mapper and xp prefs.
  124. CMimeMapper* CMimeMapper::CreateMapperForRes(Handle res)
  125. {
  126.     SInt8 flags = ::HGetState(res);
  127.     ::HNoPurge(res);
  128.     
  129.     CMimeMapper* mapper = new CMimeMapper( res );
  130.     ::HSetState(res, flags);
  131.  
  132.     if (mapper->fMimeType == HTML_VIEWER_APPLICATION_MIME_TYPE) {
  133.         mapper->SetLoadAction(CPrefs::sViewSourceInline ? Internal : Launch);
  134.     }
  135.  
  136.     mapper->WriteMimePrefs(false);            // convert to xp format
  137.  
  138.     mapper->SetDefaultDescription();
  139.  
  140.     return mapper;
  141. }
  142.  
  143.  
  144. // Constructor to build mapper from xp prefs
  145. CMimeMapper::CMimeMapper( const char* basepref )
  146. {
  147.     InitMapper();
  148.     
  149.     fBasePref = XP_STRDUP(basepref);
  150.     if (strlen(fBasePref) > 200)
  151.         fBasePref[200] = '\0';
  152.     ReadMimePrefs();
  153.     
  154.     SetDefaultDescription();
  155. }
  156.  
  157. // Copies an xp mimetype pref into the mapper
  158. void CMimeMapper::ReadMimePrefs()
  159. {
  160.     int err;
  161.     int size = 256,
  162.         actualSize;
  163.     char value[256];
  164.  
  165.     fAppSig = 0;
  166.     err = PREF_GetCharPref( CPrefs::Concat(fBasePref, Pref_AppSig), value, &size );
  167.     if (PREF_NOERROR == err)
  168.     {
  169.         // values stored as binary (Base64) will always be longer than 4 bytes
  170.         // so if the string is 4 bytes long (or shorter), it's a straight character string
  171.         actualSize = strlen(value);
  172.         if (actualSize <= sizeof(OSType))
  173.             ::BlockMoveData(value, (Ptr) &fAppSig, actualSize );
  174.         else
  175.         {
  176.             err = PREF_GetBinaryPref ( CPrefs::Concat( fBasePref, Pref_AppSig ), value, &size);
  177.             if (PREF_NOERROR == err)
  178.                 ::BlockMoveData( value, (Ptr) &fAppSig, sizeof(OSType) );
  179.         }
  180.     }
  181.  
  182.     fFileType = 0;
  183.     err = PREF_GetCharPref( CPrefs::Concat(fBasePref, Pref_FileType), value, &size );
  184.     if (PREF_NOERROR == err)
  185.     {
  186.         // see notes above for fAppSig
  187.         actualSize = strlen(value);
  188.         if (actualSize <= sizeof(OSType))
  189.             ::BlockMoveData( value, (Ptr) &fFileType, actualSize );
  190.         else
  191.         {
  192.             err = PREF_GetBinaryPref ( CPrefs::Concat( fBasePref, Pref_FileType ), value, &size);
  193.             if (PREF_NOERROR == err)
  194.                 ::BlockMoveData( value, (Ptr) &fFileType, sizeof(OSType) );
  195.         }
  196.     }
  197.     
  198.     err = PREF_GetCharPref( CPrefs::Concat(fBasePref, Pref_AppName), value, &size );
  199.     if (PREF_NOERROR == err)
  200.         fAppName = value;
  201.     
  202.     err = PREF_GetCharPref( CPrefs::Concat(fBasePref, Pref_MimeType), value, &size );
  203.     if (PREF_NOERROR == err)
  204.         fMimeType = value;
  205.     
  206.     err = PREF_GetCharPref( CPrefs::Concat(fBasePref, Pref_Extension), value, &size );
  207.     if (PREF_NOERROR == err)
  208.         fExtensions = value;
  209.     
  210.     err = PREF_GetCharPref( CPrefs::Concat(fBasePref, Pref_PluginName), value, &size );
  211.     if (PREF_NOERROR == err)
  212.         fPluginName = value;
  213.     
  214.     err = PREF_GetCharPref( CPrefs::Concat(fBasePref, Pref_Description), value, &size );
  215.     if (PREF_NOERROR == err)
  216.         fDescription = value;
  217.     
  218.     int32 intvalue;
  219.     err = PREF_GetIntPref( CPrefs::Concat(fBasePref, Pref_LoadAction), &intvalue );
  220.     if (PREF_NOERROR == err)
  221.         fLoadAction = (LoadAction) intvalue;
  222.     
  223.     XP_Bool boolvalue;
  224.     err = PREF_GetBoolPref( CPrefs::Concat(fBasePref, Pref_LatentPlugin), &boolvalue );
  225.     if (PREF_NOERROR == err)
  226.         fLatentPlugin = (Boolean) boolvalue;
  227. }
  228.  
  229. // -- For 3.0 format compatability --
  230. // Initializes off a resource handle of one of the mime types. 
  231. // See pref.r for resource definition
  232. // The memory layout of the resource is very important.
  233. #define NAME_LENGTH_OFFSET 9
  234. CMimeMapper::CMimeMapper( Handle resRecord )
  235. {
  236.     ThrowIfNil_(resRecord);
  237.     Size size = ::GetHandleSize(resRecord);
  238.     InitMapper();
  239.  
  240.     char     temp[256];
  241.     Ptr        resPtr = *resRecord;
  242.     Byte    lengthByte = 0;
  243.     long    offset = 0;
  244.     
  245.     ThrowIf_(size < 9);
  246.     ::BlockMoveData( resPtr, (Ptr)&fAppSig, 4 );
  247.     ::BlockMoveData( resPtr + 4, (Ptr)&fFileType, 4 );
  248.     uint32 loadAction = *((unsigned char*) (resPtr + 8));
  249.     fLoadAction = (LoadAction) loadAction;
  250.  
  251.     offset = NAME_LENGTH_OFFSET;
  252.     ThrowIf_(offset > size);
  253.     // Ñ read the application name
  254.     lengthByte = resPtr[ offset ];
  255.     ThrowIf_(lengthByte > 255);
  256.     ::BlockMoveData( &resPtr[ offset + 1 ], (Ptr)temp, lengthByte );
  257.  
  258.     temp[ lengthByte ] = 0;
  259.  
  260.     fAppName = temp;
  261.  
  262.     // Ñ read in the MIME type
  263.     offset += ( lengthByte + 1 );
  264.     ThrowIf_(offset > size);
  265.     lengthByte = resPtr[ offset ];
  266.     ::BlockMoveData( &resPtr[ offset + 1 ], temp, lengthByte );
  267.  
  268.     temp[ lengthByte ] = 0;
  269.  
  270.     fMimeType = temp;
  271.  
  272.     // Ñ read in the extensions string
  273.     offset += ( lengthByte + 1 );
  274.     ThrowIf_(offset > size);
  275.     lengthByte = resPtr[ offset ];
  276.     ThrowIf_(lengthByte > 255);
  277.     ::BlockMove ( &resPtr[ offset + 1 ], temp, lengthByte );
  278.     
  279.     temp[ lengthByte ] = 0;
  280.     
  281.     fExtensions = temp;
  282.         
  283.     // Ñ read in the plug-in name string
  284.     // Since the plug-in name doesn't exist in 2.0-vintage prefs resources, make
  285.     // sure to check that there is still data left in the handle before proceeding.
  286.     offset += ( lengthByte + 1 );
  287.     if (offset < size)
  288.     {
  289.         lengthByte = resPtr[ offset ];
  290.         ThrowIf_(lengthByte > 255);
  291.         ::BlockMove ( &resPtr[ offset + 1 ], temp, lengthByte );
  292.         temp[ lengthByte ] = 0;
  293.         fPluginName = temp;
  294.         
  295.         // Now read the description string (it too could be non-existant)
  296.         offset += ( lengthByte + 1 );
  297.         if (offset < size)
  298.         {
  299.             lengthByte = resPtr[ offset ];
  300.             ThrowIf_(lengthByte > 255);
  301.             ::BlockMove ( &resPtr[ offset + 1 ], temp, lengthByte );
  302.             temp[ lengthByte ] = 0;
  303.             fDescription = temp;
  304.             
  305.             // Last, read the "latent plug-in" flag (if it exists)
  306.             offset += ( lengthByte + 1 );
  307.             if (offset < size)
  308.             {
  309.                 Boolean latentPlugin = *((unsigned char*) (resPtr + offset));
  310.                 XP_ASSERT(latentPlugin == 0 || latentPlugin == 1);
  311.                 fLatentPlugin = latentPlugin;
  312.             }
  313.         }
  314.     }
  315.     else
  316.     {
  317.         fPluginName = "";
  318.         fFromOldPrefs = true;                // No plug-in info in these prefs
  319.     }
  320. }
  321.  
  322. //
  323. // For old prefs that don't have a description stored in the handle,
  324. // look up the default description in netlib, defaulting to just the
  325. // MIME type itself if it can't be found.  This description will then
  326. // get written out to the prefs so it can be edited and saved by the user.
  327. //
  328. void CMimeMapper::SetDefaultDescription()
  329. {
  330.     if (fMimeType == HTML_VIEWER_APPLICATION_MIME_TYPE)
  331.         return;
  332.  
  333.     CStr255 description;
  334.     char* mimetype = (char*) fMimeType;
  335.     NET_cdataStruct* cdata = NULL;
  336.     XP_List* list_ptr = cinfo_MasterListPointer();
  337.     while ((cdata = (NET_cdataStruct*) XP_ListNextObject(list_ptr)) != NULL)
  338.         if (strcasecomp(mimetype, cdata->ci.type) == 0) 
  339.             break;
  340.  
  341.     if (cdata && cdata->ci.desc)
  342.         description = cdata->ci.desc;
  343.     else
  344.         description = fMimeType;
  345.     
  346.     if (fDescription == CStr255::sEmptyString) {
  347.         fDescription = description;
  348.         CPrefs::SetModified();
  349.     }
  350.     
  351.     PREF_SetDefaultCharPref( CPrefs::Concat(fBasePref, Pref_Description), description );
  352. }
  353.  
  354.  
  355. // Duplicate
  356. CMimeMapper::CMimeMapper( const CMimeMapper& clone )
  357. {
  358.     InitMapper();
  359.     fMimeType = clone.fMimeType;
  360.     fAppName = clone.fAppName;
  361.     fAppSig = clone.fAppSig;
  362.     fFileType = clone.fFileType;
  363.     fLoadAction = clone.fLoadAction;
  364.     fExtensions = clone.fExtensions;
  365.     
  366.     fTemporary = clone.fTemporary;        
  367.     fRegisteredViewer = clone.fRegisteredViewer;
  368.     fTempAppSig = clone.fTempAppSig;            // Application
  369.     fTempFileType = clone.fTempFileType;        // File signature
  370.     
  371.     fPluginName = clone.fPluginName;
  372.     fFromOldPrefs = clone.fFromOldPrefs;
  373.     fDescription = clone.fDescription;
  374.     fLatentPlugin = clone.fLatentPlugin;
  375.     
  376.     fBasePref = clone.fBasePref ? XP_STRDUP(clone.fBasePref) : nil;
  377. }
  378.  
  379. // Copies values from the mapper to xp user pref tree.
  380. void CMimeMapper::WriteMimePrefs( Boolean )
  381. {
  382.     if (fBasePref == nil) {
  383.         // Convert mime type to a pref-name string with 
  384.         // non-alpha chars replaced with underscores
  385.         char typecopy[256];
  386.         strcpy(typecopy, "mime.");
  387.         int start = strlen(typecopy);
  388.         strncat(typecopy, (char*) fMimeType, 200);
  389.         for (int i = start; i < strlen(typecopy); i++)
  390.             if (!isalnum(typecopy[i]))
  391.                 typecopy[i] = '_';
  392.         
  393.         // Mimetype may be blank, so pick an arbitrary name
  394.         if (fMimeType.Length() == 0)
  395.             strcat(typecopy, "unknown_type_999");
  396.         
  397.         fBasePref = XP_STRDUP(typecopy);
  398.     }
  399.     
  400.     char appsig[sizeof(OSType)+1], filetype[sizeof(OSType)+1];
  401.     appsig[sizeof(OSType)] = '\0';
  402.     ::BlockMoveData( (Ptr) &fAppSig, appsig, sizeof(OSType) );
  403.     filetype[sizeof(OSType)] = '\0';
  404.     ::BlockMoveData( (Ptr) &fFileType, filetype, sizeof(OSType) );
  405.     
  406.     PREF_SetCharPref( CPrefs::Concat(fBasePref, Pref_Extension), fExtensions );
  407.     if (fDescription.Length() > 0)
  408.         PREF_SetCharPref( CPrefs::Concat(fBasePref, Pref_Description), fDescription );
  409.     PREF_SetCharPref( CPrefs::Concat(fBasePref, Pref_MimeType), fMimeType );
  410.     PREF_SetCharPref( CPrefs::Concat(fBasePref, Pref_AppName), fAppName );
  411.     PREF_SetCharPref( CPrefs::Concat(fBasePref, Pref_PluginName), fPluginName );
  412.     // store appsig and filetype as 4-byte character strings, if possible.
  413.     // otherwise, store as binary.
  414.     if (PrintableChars( &appsig, sizeof(OSType)))
  415.         PREF_SetCharPref( CPrefs::Concat(fBasePref, Pref_AppSig), appsig );
  416.     else
  417.         PREF_SetBinaryPref( CPrefs::Concat(fBasePref, Pref_AppSig), appsig, sizeof(OSType));
  418.     if (PrintableChars( &filetype, sizeof(OSType)))
  419.         PREF_SetCharPref( CPrefs::Concat(fBasePref, Pref_FileType), filetype );        
  420.     else
  421.         PREF_SetBinaryPref( CPrefs::Concat(fBasePref, Pref_FileType), filetype, sizeof(OSType));
  422.     PREF_SetIntPref( CPrefs::Concat(fBasePref, Pref_LoadAction), fLoadAction );
  423.     PREF_SetBoolPref( CPrefs::Concat(fBasePref, Pref_LatentPlugin), (XP_Bool) fLatentPlugin );
  424. }
  425.  
  426. CMimeMapper::CMimeMapper(
  427.     LoadAction loadAction,
  428.     const CStr255&    mimeType,
  429.     const CStr255&    appName,
  430.     const CStr255&    extensions,
  431.     OSType            appSignature,
  432.     OSType            fileType )
  433. {
  434.     fLoadAction = loadAction;
  435.     fMimeType = mimeType;
  436.     fAppName = appName;
  437.     fAppSig = appSignature;
  438.     fFileType = fileType;
  439.     fExtensions = extensions;
  440.     
  441.     fTemporary = false;
  442.     fRegisteredViewer = false;
  443.     fPluginName = "";
  444.     fDescription = "";
  445.     fLatentPlugin = false;
  446.     fBasePref = nil;
  447. }
  448.  
  449. // Disposes of all allocated structures
  450. CMimeMapper::~CMimeMapper()
  451. {
  452.     if (fBasePref)
  453.         free (fBasePref);
  454. }
  455.  
  456. CMimeMapper & CMimeMapper::operator= (const CMimeMapper& mapper)
  457. {
  458.     fMimeType = mapper.fMimeType;    
  459.     fAppName = mapper.fAppName;
  460.     fAppSig = mapper.fAppSig;
  461.     fFileType = mapper.fFileType;
  462.     fLoadAction = mapper.fLoadAction;
  463.     fPluginName = mapper.fPluginName;
  464.     fFromOldPrefs = mapper.fFromOldPrefs;
  465.     fDescription = mapper.fDescription;
  466.     fLatentPlugin = mapper.fLatentPlugin;
  467.     return *this;
  468. }
  469.  
  470. void CMimeMapper::SetAppName(const CStr255& newName) 
  471. {
  472.     fAppName = newName;
  473. }
  474.  
  475. void CMimeMapper::SetMimeType(const CStr255& newType) 
  476. {
  477.     fMimeType = newType;
  478. }
  479.  
  480. void CMimeMapper::SetAppSig(OSType newSig) 
  481. {
  482.     fAppSig = newSig;
  483. }
  484.  
  485. void CMimeMapper::SetAppSig(FSSpec& appSpec) 
  486. {
  487.     FInfo finderInfo;
  488.     OSErr err = FSpGetFInfo(&appSpec, &finderInfo );    
  489.     ThrowIfOSErr_(err);
  490.     fAppSig = finderInfo.fdCreator;
  491. }
  492.  
  493. void CMimeMapper::SetExtensions(const CStr255& newExtensions)
  494. {
  495.     fExtensions = newExtensions;
  496.     SyncNetlib();
  497. }
  498.  
  499. void CMimeMapper::SetDocType(OSType newType) 
  500. {
  501.     fFileType = newType;
  502. }    
  503.  
  504. void CMimeMapper::SetLoadAction(LoadAction newAction)
  505. {
  506.     //
  507.     // If the user explicitly changed the load action,
  508.     // cancel the latent plug-in setting so their choice
  509.     // is maintained persistently.
  510.     //
  511.     fLatentPlugin = false;
  512.         
  513.     //
  514.     // If the user explicitly changed the load action,
  515.     // cancel the registration of an external viewer.
  516.     //
  517.     fRegisteredViewer = false;
  518.     
  519.     fLoadAction = newAction;
  520.     SyncNetlib();
  521. }
  522.  
  523. void CMimeMapper::SetLatentPlugin()
  524. {
  525.     fLatentPlugin = true;
  526.     fLoadAction = CMimeMapper::Unknown;
  527. }
  528.  
  529.  
  530. void CMimeMapper::RegisterViewer(OSType tempAppSig, OSType tempFileType)
  531. {
  532.     fTempAppSig = tempAppSig;        // Application
  533.     fTempFileType = tempFileType;        // File signature
  534.     fRegisteredViewer = TRUE;
  535.     CFrontApp::RegisterMimeType(this);
  536. }
  537.  
  538. void CMimeMapper::UnregisterViewer()
  539. {
  540.     fRegisteredViewer = FALSE;
  541.     CFrontApp::RegisterMimeType(this);
  542. }
  543.  
  544. // Typical Spy apple event creation routine.
  545. // It creates the event, with two arguments, URL and the MIME type.
  546. // URL type is stored as attribute urlAttribute, MIME type as AE_spy_viewDocFile_mime
  547. OSErr    CMimeMapper::MakeSpyEvent(AppleEvent & event, 
  548.                                 URL_Struct * request, 
  549.                                 AEEventID id,
  550.                                 AEKeyword urlAttribute)
  551. {
  552.     OSErr err;
  553.     Try_
  554.     {
  555.         AEAddressDesc targetApp;
  556.         
  557.         // Specify the application, and create the event
  558.         {
  559.         err = ::AECreateDesc(typeApplSignature, &fTempAppSig, 
  560.                                  sizeof(fTempAppSig), &targetApp);
  561.         ThrowIfOSErr_(err);
  562.         OSErr err = ::AECreateAppleEvent(AE_spy_send_suite, id,
  563.                                     &targetApp,
  564.                                     kAutoGenerateReturnID,
  565.                                     kAnyTransactionID,
  566.                                     &event);
  567.         ::AEDisposeDesc(&targetApp);
  568.         }
  569.         // URL
  570.         {
  571.             AEDesc urlDesc;
  572.             err = ::AECreateDesc(typeChar, request->address, strlen(request->address), &urlDesc);
  573.             ThrowIfOSErr_(err);
  574.             
  575.             err = ::AEPutParamDesc(&event, urlAttribute, &urlDesc);
  576.             ThrowIfOSErr_(err);
  577.             ::AEDisposeDesc(&urlDesc);
  578.         }
  579.         // MIME type
  580.         if (request->content_type)
  581.         {
  582.             AEDesc mimeDesc;
  583.             err = ::AECreateDesc(typeChar, request->content_type, strlen(request->content_type), &mimeDesc);
  584.             ThrowIfOSErr_(err);
  585.             err = ::AEPutParamDesc(&event, AE_spy_queryViewer_mime, &mimeDesc);            
  586.             ::AEDisposeDesc(&mimeDesc);
  587.         }
  588.     }
  589.     Catch_(inErr)
  590.     {    return inErr;
  591.     }
  592.     EndCatch_
  593.     return noErr;
  594. }
  595.  
  596. /* Returns whether the contents of inBuffer are all visible, printable characters.
  597.    Actually, that's a misnomer ... it returns whether the contents are printable
  598.    on a Macintosh, which we define to be "not control characters."  This is intended
  599.    for determining whether a binary buffer can be stored as a text string, or needs
  600.    to be encoded. */
  601. Boolean CMimeMapper::PrintableChars(const void *inBuffer, Int32 inLength)
  602. {
  603.     unsigned char    *buffer = (unsigned char *) inBuffer,
  604.                     *bufferEnd = buffer + inLength;
  605.  
  606.     while (buffer < bufferEnd)
  607.     {
  608.         if (*buffer <= 0x20)
  609.             break;
  610.         buffer++;
  611.     }
  612.     return buffer == bufferEnd;
  613. }
  614.  
  615. // if we have a registered viewer, query it for a file spec
  616. OSErr    CMimeMapper::GetFileSpec(URL_Struct * request, 
  617.                                 FSSpec & destination)
  618. {
  619.     OSErr err = noErr;
  620.     if (!fRegisteredViewer)
  621.         return fnfErr;
  622.     else
  623.     Try_
  624.     {    
  625.         AppleEvent queryViewerEvent;
  626.  
  627.         err = MakeSpyEvent(queryViewerEvent, request,AE_spy_queryViewer,
  628.                         keyDirectObject); // URL is the direct object
  629.         //     Send the event
  630.         AppleEvent    reply;
  631.         err = ::AESend(&queryViewerEvent,&reply,kAEWaitReply,kAEHighPriority,600,nil, nil);
  632.         ThrowIfOSErr_(err);
  633.         // Handle the reply. We want to get out the transaction ID
  634.         {
  635.             DescType realType;
  636.             Size actualSize;
  637.             if (!MoreExtractFromAEDesc::DisplayErrorReply(reply))
  638.             {
  639.                 err = ::AEGetParamPtr(&reply, keyAEResult, typeFSS, 
  640.                          &realType, &destination, sizeof(destination), 
  641.                         &actualSize);
  642.                 ThrowIfOSErr_(err);
  643.             }
  644.             else
  645.                 Throw_(fnfErr);
  646.             ::AEDisposeDesc(&reply);
  647.         }
  648.     }
  649.     Catch_(inErr)
  650.     {
  651.         return inErr;
  652.     }    EndCatch_
  653.     return noErr;
  654. }
  655.  
  656. // If we have a registered viewer, use ViewDocFile
  657. // if anything fails, or we have no viewer, use ordinary launch sequence
  658. OSErr CMimeMapper::LaunchFile(LFileBufferStream * inFile, URL_Struct * request, Int32 windowID)
  659. {
  660.     OSErr err;
  661.     if (fRegisteredViewer)
  662.     {
  663.         Try_
  664.         {
  665.             AppleEvent viewDocEvent;
  666.             // AppleEvent(fileSpec, URL, MIME, windowID)
  667.             // Create the event, with MIME and URL arguments
  668.             err = MakeSpyEvent(viewDocEvent, request,AE_spy_viewDocFile,
  669.                             AE_spy_viewDocFile_url); // URL is the direct object
  670.             ThrowIfOSErr_(err);
  671.             // Make the file spec
  672.             FSSpec fileSpec;
  673.             inFile->GetSpecifier(fileSpec);
  674.             err = ::AEPutParamPtr(&viewDocEvent,keyDirectObject,typeFSS,&fileSpec,sizeof(fileSpec));
  675.             ThrowIfOSErr_(err);
  676.             // URL
  677.             if (request->address)
  678.             {
  679.                 err = ::AEPutParamPtr(&viewDocEvent,AE_spy_viewDocFile_url, typeChar, request->address,strlen(request->address));
  680.                 ThrowIfOSErr_(err);
  681.             }
  682.             // MIME type
  683.             {
  684.                 StAEDescriptor mimeDesc((StringPtr)fMimeType);
  685.                 err = ::AEPutParamDesc(&viewDocEvent,AE_spy_viewDocFile_mime,&mimeDesc.mDesc);
  686.                 ThrowIfOSErr_(err);
  687.             }
  688.             // WindowID
  689.             {
  690.             StAEDescriptor windowDesc(windowID);
  691.             err = ::AEPutParamDesc(&viewDocEvent,AE_spy_viewDocFile_wind, &windowDesc.mDesc);
  692.             ThrowIfOSErr_(err);
  693.             }
  694.             // Send the event
  695.             AppleEvent    reply;
  696.             err = ::AESend(&viewDocEvent,&reply,kAEWaitReply,kAEHighPriority,60,nil, nil);
  697.             if (err != errAETimeout)
  698.                 ThrowIfOSErr_(err);
  699.             // If we got an error code back, launch as usual
  700.             if (reply.descriptorType != typeNull)
  701.             {
  702.                 DescType realType;
  703.                 Size actualSize;
  704.                 long    errNumber;
  705.                 err = ::AEGetParamPtr(&reply, keyErrorNumber, typeLongInteger, 
  706.                             &realType, &errNumber, sizeof(errNumber), 
  707.                             &actualSize);
  708.                 if ((err == noErr) && (errNumber != noErr))
  709.                     Throw_(errNumber);
  710.             }
  711.         }
  712.         Catch_(inErr)
  713.         {
  714.             // Launch through ViewDoc failed, use our original creator and launch as usual
  715.             FSSpec fileSpec;
  716.             inFile->GetSpecifier(fileSpec);
  717.             err = CFileMgr::SetFileTypeCreator(fAppSig, fFileType, &fileSpec);
  718.             ::LaunchFile(inFile);
  719.         }
  720.         EndCatch_
  721.     }
  722.     else
  723.         ::LaunchFile(inFile);
  724.     return noErr;
  725. }
  726.  
  727.  
  728. void CMimeMapper::SyncNetlib()
  729. {
  730.     CFrontApp::RegisterMimeType(this);
  731. }
  732.  
  733. //----------------------------------------------------------------------
  734. // class CMimeList
  735. //----------------------------------------------------------------------
  736.  
  737.     // ÑÑ Constructors/destructors
  738.  
  739. CMimeList::CMimeList() : LArray()
  740. {
  741. }
  742.  
  743. CMimeList::~CMimeList()
  744. {
  745. }
  746.  
  747.     // ÑÑ╩Utility functions
  748. // Overrode this on the assumption that every time you add a MIME mapper
  749. // to this list, you want to sync up the netlib
  750. ArrayIndexT CMimeList::InsertItemsAt(
  751.     Uint32            inCount,
  752.     ArrayIndexT        inAtIndex,
  753.     const void        *inItem,
  754.     Uint32            inItemSize)
  755.  
  756. {
  757.     ArrayIndexT result = LArray::InsertItemsAt(inCount,inAtIndex,inItem, inItemSize);
  758.     CMimeMapper ** mapperPtr = (CMimeMapper **)inItem;
  759.     (*mapperPtr)->SyncNetlib();
  760.     return result;
  761. }
  762.  
  763. // Deletes all the items in the list
  764. void CMimeList::DeleteAll(Boolean)
  765. {
  766.     CMimeMapper*        oldMap;
  767.     for ( short i = 1; i <= mItemCount; i++ )
  768.     {
  769.         FetchItemAt( i, &oldMap );
  770.         delete oldMap;
  771.     }
  772.     RemoveItemsAt( mItemCount,1);
  773. }
  774.  
  775. // Finds a CMimeMapper with a given XP pref branch name.
  776. CMimeMapper* CMimeList::FindBasePref( const char* basepref )
  777. {
  778.     CMimeMapper* foundMap = NULL;
  779.     for ( Int32 i = mItemCount; i >= 1; i-- )
  780.     {
  781.         CMimeMapper* oldMap;
  782.         FetchItemAt( i, &oldMap );
  783.         if ( oldMap->GetBasePref() != NULL &&
  784.              XP_STRCMP(oldMap->GetBasePref(), basepref) == 0 )
  785.         {
  786.             foundMap = oldMap;
  787.             break;
  788.         }
  789.     }
  790.     return foundMap;
  791. }
  792.  
  793.  
  794. // Finds a CMimeMapper with a given mimeType. NULL if none
  795. // Search is linear. Might want to make it faster
  796. CMimeMapper* CMimeList::FindMimeType( char* mimeType )
  797. {
  798.     if ( mimeType == NULL )
  799.         return NULL;
  800.  
  801.     CMimeMapper* foundMap = NULL;
  802.     for ( Int32 i = 1; i <= mItemCount; i++ )
  803.     {
  804.         CMimeMapper* oldMap;
  805.         FetchItemAt( i, &oldMap );
  806.         if ( XP_STRCMP(oldMap->GetMimeName(), mimeType) == 0 )
  807.         {
  808.             foundMap = oldMap;
  809.             break;
  810.         }
  811.     }
  812.     return foundMap;
  813. }
  814.  
  815. // Find a "built-in" mime type, which were separate in 3.0
  816. // but are integrated into the 4.0 mimetype list.
  817. CMimeMapper* CMimeList::FindMimeType(BuiltIn mimeBuiltin)
  818. {
  819.     char* mimetype = nil;
  820.     switch (mimeBuiltin) {
  821.         case HTMLViewer:
  822.             mimetype = HTML_VIEWER_APPLICATION_MIME_TYPE;
  823.             break;
  824.         case Telnet:
  825.             mimetype = TELNET_APPLICATION_MIME_TYPE;
  826.             break;
  827.         case Tn3270:
  828.             mimetype = TN3270_VIEWER_APPLICATION_MIME_TYPE;
  829.             break;
  830.     }
  831.     return FindMimeType(mimetype);
  832. }
  833.  
  834.  
  835. // FindMimeType finds a Mime mapper that
  836. // matches this file's type and creator
  837. // Algorithm is inexact
  838. // TEXT files are not typed    because netlib figures out the 
  839. // HTML files, and hqx ones have the extension
  840. // Look for exact match.
  841. // If not found, look for inexact one
  842.  
  843. // I don't like the above algorithm. There is no way of telling what type of match occured. In
  844. // cases other than exact or FileType  match the results are unsatisfactory. 
  845. // New behavoir is to return NULL and let the caller call CMimeList::FindCreatorFindCreator if
  846. // needed. Provides a way to hand off to IC if needed.
  847. CMimeMapper* CMimeList::FindMimeType(const FSSpec& inFileSpec)
  848. {
  849.     FInfo        fndrInfo;
  850.     
  851.     OSErr err = FSpGetFInfo( &inFileSpec, &fndrInfo );
  852.     if ((err != noErr) || (fndrInfo.fdType == 'TEXT'))
  853.         return NULL;
  854.  
  855.     CMimeMapper* fileTypeMatch = NULL;
  856.  
  857.     // Find the cinfo (which gives us the MIME type) for this file name
  858.     NET_cinfo* cinfo = NET_cinfo_find_type((CStr255)inFileSpec.name);
  859.  
  860.     for (Int32 i = 1; i <= mItemCount; i++)
  861.     {
  862.         CMimeMapper* map;
  863.         FetchItemAt(i, &map);
  864.         
  865.         //
  866.         // If this MIME type is configured for a plug-in, see if it matches
  867.         // the MIME type of the file (based on the file extension -> MIME
  868.         // type mapping).  If not, try for an exact or partial match based
  869.         // on type and/or creator.  bing: If we decide to support Mac file
  870.         // types for plug-ins, we should check the file type here.
  871.         //
  872.         if (map->GetLoadAction() == CMimeMapper::Plugin)
  873.         {
  874.             if (cinfo && (map->GetMimeName() == cinfo->type))
  875.                 return map;
  876.         }
  877.         else if ((map->GetAppSig() == fndrInfo.fdCreator) &&
  878.                  (map->GetDocType() == fndrInfo.fdType))
  879.             return map;
  880.         else 
  881.         {
  882.             #if 0
  883.                 if (map->GetAppSig() == fndrInfo.fdCreator)
  884.                     creatorMatch = map;
  885.             #endif
  886.             if (map->GetDocType() == fndrInfo.fdType)
  887.                 fileTypeMatch = map;
  888.         }
  889.     }
  890.  
  891.     return fileTypeMatch;    
  892. }
  893.  
  894.  
  895. // FindCreator finds a CMimeMapper whose application signature
  896. // matches appSig
  897. CMimeMapper* CMimeList::FindCreator(OSType appSig)
  898. {
  899.     CMimeMapper * foundMap = NULL;
  900.     for (Int32 i = 1; i <= mItemCount; i++)
  901.     {
  902.         CMimeMapper* oldMap;
  903.         FetchItemAt(i, &oldMap);
  904.         if (oldMap->GetAppSig() == appSig)
  905.         {
  906.             foundMap = oldMap;
  907.             break;
  908.         }
  909.     }
  910.     return foundMap;
  911. }
  912.  
  913. // ------------------------- Apple Event handling -------------------------------
  914.  
  915. // HandleRegisterViewerEvent
  916. // registers a viewer.
  917. // If the MIME type does not exist, we create one on the fly
  918. void CMimeList::HandleRegisterViewerEvent(const AppleEvent    &inAppleEvent,
  919.                                     AppleEvent            &outAEReply,
  920.                                     AEDesc                &/*outResult*/,
  921.                                     long                /*inAENumber*/)
  922. {
  923.     OSType    appSign;    //    app signature
  924.     OSType    fileType = 'TEXT';    //    file type to be created
  925.     volatile char *    mimeType = NULL;    // MIME type
  926.     OSType    realType;
  927.     Size    realSize;
  928.     CMimeMapper * mapper;
  929.     OSErr err;
  930.     Try_ {
  931.         {    // Get the application signature. Required
  932.             err = ::AEGetParamPtr(&inAppleEvent,keyDirectObject,typeApplSignature,
  933.                                     &realType, &appSign, sizeof(appSign), &realSize);
  934.             ThrowIfOSErr_(err);
  935.         }
  936.         {    // Get the MIME type. Required
  937.             AEDesc mimeDesc;
  938.             err = ::AEGetParamDesc(&inAppleEvent,AE_spy_registerViewer_mime,typeWildCard,&mimeDesc);
  939.             ThrowIfOSErr_(err);        
  940.             MoreExtractFromAEDesc::TheCString(mimeDesc, mimeType);
  941.             ::AEDisposeDesc(&mimeDesc);
  942.             if (mimeType == nil)
  943.                 ThrowOSErr_(errAEWrongDataType);
  944.         }
  945.     
  946.         mapper = FindMimeType(mimeType);
  947.         if (mapper)
  948.             fileType = mapper->GetDocType();
  949.         {    // Get the file type. If none is specified, it defaults to already registered type, or 'TEXT'
  950.             AEDesc fileTypeDesc;
  951.             err = ::AEGetParamDesc(&inAppleEvent,AE_spy_registerViewer_ftyp,typeWildCard,&fileTypeDesc);
  952.             if (err == noErr)
  953.             {
  954.                 Try_    {
  955.                     UExtractFromAEDesc::TheType(fileTypeDesc, fileType);
  956.                 } Catch_(inErr)
  957.                 {} EndCatch_
  958.             }
  959.             ::AEDisposeDesc(&fileTypeDesc);
  960.         }
  961.         // We have all the arguments, set up the new mapper if necessary
  962.         if (mapper == NULL)
  963.         {
  964.             mapper = new CMimeMapper(CMimeMapper::Launch, 
  965.                                 mimeType,
  966.                                 "-",    // I10L - this string is never seen by a user
  967.                                 CStr255::sEmptyString,
  968.                                 appSign,
  969.                                 fileType);
  970.             ThrowIfNil_(mapper);
  971.             mapper->SetTemporary(TRUE);
  972.             InsertItemsAt( 1, LArray::index_Last, &mapper);
  973.         }
  974.         
  975.         // If the type is being handled by a plug-in, don╒t let the AE override
  976.         Boolean result = false;
  977.         if (mapper->GetLoadAction() != CMimeMapper::Plugin)
  978.         {
  979.             mapper->RegisterViewer(appSign, fileType);
  980.             result = true;
  981.         }
  982.         
  983.         // Reply success
  984.         // Create the reply, it will automatically be stuck into the outgoing AE by PP
  985.         {
  986.             StAEDescriptor booleanDesc(result);
  987.             err = ::AEPutKeyDesc(&outAEReply,keyAEResult,&booleanDesc.mDesc);   /* IM VI chap. 6 pg 91 */
  988.         }
  989.     }
  990.     Catch_(inErr)
  991.     {
  992.         MoreExtractFromAEDesc::MakeErrorReturn(outAEReply,
  993.                     (unsigned char *)GetCString(REG_EVENT_ERR_RESID) , inErr);
  994.     }    EndCatch_
  995.     // Dispose
  996.     if (mimeType) XP_FREE(mimeType);    
  997. }
  998.  
  999. void CMimeList::HandleUnregisterViewerEvent(const AppleEvent    &inAppleEvent,
  1000.                                     AppleEvent            &outAEReply,
  1001.                                     AEDesc                &/*outResult*/,
  1002.                                     long                /*inAENumber*/)
  1003. {
  1004.     OSType    appSign;    //    app signature
  1005.     OSType    fileType = 'TEXT';    //    file type to be created
  1006.     volatile char*    mimeType = NULL;    // MIME type
  1007.  
  1008.     CMimeMapper * mapper = NULL;
  1009.     OSErr err;
  1010.     Try_
  1011.     {
  1012.         {    // Get the application signature. Required
  1013.             AEDesc appSignDesc;
  1014.             err = ::AEGetParamDesc(&inAppleEvent,keyDirectObject,typeWildCard,&appSignDesc);
  1015.             ThrowIfOSErr_(err);
  1016.             UExtractFromAEDesc::TheType(appSignDesc, appSign);
  1017.             ::AEDisposeDesc(&appSignDesc);
  1018.         }
  1019.         {    // Get the MIME type. Required
  1020.             AEDesc mimeDesc;
  1021.             err = ::AEGetParamDesc(&inAppleEvent,AE_spy_unregisterViewer_mime,typeWildCard,&mimeDesc);
  1022.             ThrowIfOSErr_(err);        
  1023.             MoreExtractFromAEDesc::TheCString(mimeDesc, mimeType);
  1024.             ::AEDisposeDesc(&mimeDesc);
  1025.             if (mimeType == nil)
  1026.                 ThrowOSErr_(errAEWrongDataType);
  1027.         }
  1028.         
  1029.         mapper = FindMimeType(mimeType);
  1030.         
  1031.         if (mapper == NULL || mapper->IsViewerRegistered() == false)
  1032.         {
  1033.             //
  1034.             // If the type isn╒t found, or wasn╒t registered
  1035.             // to an external viewer, return an error.
  1036.             //
  1037.             MoreExtractFromAEDesc::MakeErrorReturn(outAEReply, 
  1038.                 (unsigned char *)GetCString(APP_NOT_REG_RESID), errAEDescNotFound);
  1039.         }
  1040.         else
  1041.         {
  1042.             //
  1043.             // Otherwise unregister the viewer and remove the 
  1044.             // temporary mapper if necessary.
  1045.             //
  1046.             mapper->UnregisterViewer();
  1047.             if (mapper->IsTemporary())
  1048.             {
  1049.                 Remove(&mapper);
  1050.                 delete mapper;
  1051.             }
  1052.         }
  1053.     }
  1054.     Catch_(inErr)
  1055.     {
  1056.         MoreExtractFromAEDesc::MakeErrorReturn(outAEReply, 
  1057.                 (unsigned char *)GetCString(UNREG_EVENT_ERR_RESID), inErr);
  1058.     }
  1059.     EndCatch_
  1060.     if (mimeType) XP_FREE(mimeType);
  1061. }
  1062.  
  1063.