/****************************************************************************/
/* WebWorks Help API Platform Functions                                     */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

/*$Id: wwhmac.c,v 1.2 2004/03/11 21:11:18 allums Exp $*/


#include "wwhelp/wwhplat.h"

#include "wwhelp/wwhurl.h"

#include <AppleEvents.h>
#include <CodeFragments.h>
#include <Files.h>
#include <Gestalt.h>
#include <InternetConfig.h>
#include <Processes.h>
#include <Resources.h>
#if (TARGET_API_MAC_CARBON)
#include <LaunchServices.h>
#endif

#include <stdlib.h>
#include <string.h>


/****************************************************************************/
/* Platform specific function prototypes                                    */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

#ifdef __cplusplus
extern "C"
{
#endif

static int     sgbRunningUnderOSX       = 0;
static OSType  sgApplicationCreatorCode = '????';

int  private_WWHelp_Platform_DetermineApplicationCreatorCode();

int  private_WWHelp_Platform_CheckInternetConfig();

int  private_WWHelp_Platform_GetProcessSN(const OSType               ParamCreatorCode,
                                                ProcessSerialNumber* pParamProcessSN);

int  private_WWHelp_Platform_LocalVolume(const short  ParamVolumeRefNumber);

int  private_WWHelp_Platform_FindApplication(const OSType  ParamCreatorCode,
                                                   FSSpec* pParamFSSpec);

int  private_WWHelp_Platform_LaunchApplication(const FSSpec* pParamApplicationFSSpec);

#ifdef __cplusplus
}
#endif


/****************************************************************************/
/* private_WWHelp_Platform_DetermineApplicationCreatorCode                  */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
private_WWHelp_Platform_DetermineApplicationCreatorCode()
{
  int                  ReturnCode = -1;
  OSStatus             Error;
  ProcessSerialNumber  ProcessSN;
  ProcessInfoRec       ProcessInfo;


  /* Determine creator code of this application */
  /*                                            */
  Error = MacGetCurrentProcess(&ProcessSN);
  if (Error == noErr)
  {
    ProcessInfo.processName       = NULL;
    ProcessInfo.processAppSpec    = NULL;
    ProcessInfo.processInfoLength = sizeof(ProcessInfo);

    Error = GetProcessInformation(&ProcessSN, &ProcessInfo);
    if (Error == noErr)
    {
      /* Record application creator code for later use */
      /*                                               */
      sgApplicationCreatorCode = ProcessInfo.processSignature;

      ReturnCode = 0;
    }
  }

  return ReturnCode;
}


/****************************************************************************/
/* private_WWHelp_Platform_CheckInternetConfig                              */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
private_WWHelp_Platform_CheckInternetConfig()
{
  int     ReturnCode = -1;
  OSErr   Error;
  SInt32  Response;


  /* See if program is executing under Mac OS X */
  /*                                            */
  if (sgbRunningUnderOSX)
  {
    /* Success! */
    /*          */
    ReturnCode = 0;
  }
  else
  {
    /* Check for InternetConfig */
    /*                          */
    Error = Gestalt('ICAp', &Response);
#if (TARGET_RT_MAC_CFM)
    if (Error == noErr)
    {
      Error = ((Ptr)ICStart) == ((Ptr)kUnresolvedCFragSymbolAddress);
    }
#endif
    if (Error == noErr)
    {
      /* Success! */
      /*          */
      ReturnCode = 0;
    }
  }

  return ReturnCode;
}


/****************************************************************************/
/* private_WWHelp_Platform_GetProcessSN                                     */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
private_WWHelp_Platform_GetProcessSN(const OSType               ParamCreatorCode,
                                           ProcessSerialNumber* pParamProcessSN)
{
  int             ReturnCode = -1;
  OSStatus        Error;
  ProcessInfoRec  ProcessInfo;


  /* Clear process serial number */
  /*                             */
  pParamProcessSN->lowLongOfPSN  = kNoProcess;
  pParamProcessSN->highLongOfPSN = 0;

  /* Setup process info record */
  /*                           */
  ProcessInfo.processInfoLength = sizeof(ProcessInfoRec);
  ProcessInfo.processName       = NULL;
  ProcessInfo.processAppSpec    = NULL;

  /* Loop through running processes and try to find a match */
  /*                                                        */
  while ((ReturnCode != 0) &&
         (GetNextProcess(pParamProcessSN) == noErr))
  {
    Error = GetProcessInformation(pParamProcessSN, &ProcessInfo);
    if (Error == noErr)
    {
      if (ProcessInfo.processSignature == ParamCreatorCode)
      {
        /* Success! */
        /*          */
        ReturnCode = 0;
      }
    }
  }

  return ReturnCode;
}


/****************************************************************************/
/* private_WWHelp_Platform_LocalVolume                                      */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
private_WWHelp_Platform_LocalVolume(const short  ParamVolumeRefNumber)
{
  int                    ReturnCode = -1;
  HParamBlockRec         VolumeParameters;
  GetVolParmsInfoBuffer  VolumeInfo;
  OSErr                  Error;


  VolumeParameters.ioParam.ioVRefNum = ParamVolumeRefNumber;
  VolumeParameters.ioParam.ioNamePtr = NULL;
  VolumeParameters.ioParam.ioBuffer = (Ptr)&VolumeInfo;
  VolumeParameters.ioParam.ioReqCount = sizeof(GetVolParmsInfoBuffer);
  Error = PBHGetVolParmsSync(&VolumeParameters);
  if (Error == noErr)
  {
    if (VolumeInfo.vMServerAdr == 0)
    {
      /* Success! */
      /*          */
      ReturnCode = 0;
    }
  }

  return ReturnCode;
}


/****************************************************************************/
/* private_WWHelp_Platform_FindApplication                                  */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
private_WWHelp_Platform_FindApplication(const OSType  ParamCreatorCode,
                                              FSSpec* pParamApplicationFSSpec)
{
  int             ReturnCode = -1;
#if (TARGET_API_MAC_CARBON)
  FSRef           ApplicationFSRef;
#endif
  int             bContinue;
  ItemCount       VolumeIndex;
  OSErr           Error;
  FSVolumeRefNum  VolumeRefNumber;
  CSParam         CatalogSearchParameters;
  CInfoPBRec      SearchInfo1;
  CInfoPBRec      SearchInfo2;


  /* Use Launch Services if running under Mac OS X */
  /*                                               */
  if (sgbRunningUnderOSX)
  {
#if (TARGET_API_MAC_CARBON)
    Error = ((UInt32)LSGetApplicationForInfo) == ((UInt32)kUnresolvedCFragSymbolAddress);
    if (Error == noErr)
    {
      Error = LSGetApplicationForInfo(kLSUnknownType, ParamCreatorCode,
                                      NULL, kLSRolesAll, &ApplicationFSRef, NULL);
      if (Error == noErr)
      {
        Error = FSGetCatalogInfo(&ApplicationFSRef, kFSCatInfoNone, NULL,
                                 NULL, pParamApplicationFSSpec, NULL);
        if (Error == noErr)
        {
          /* Success! */
          /*          */
          ReturnCode = 0;
        }
      }
    }
#endif
  }

  /* Use Desktop Database Information */
  /*                                  */
  if (ReturnCode != 0)
  {
    /* Iterate over all volumes */
    /*                          */
    bContinue = 1;
    VolumeIndex = 1;
    while (bContinue)
    {
      Error = FSGetVolumeInfo(kFSInvalidVolumeRefNum, VolumeIndex,
                              &VolumeRefNumber, kFSVolInfoNone,
                              NULL, NULL, NULL);
      if (Error == noErr)
      {
        Error = private_WWHelp_Platform_LocalVolume(VolumeRefNumber);
        if (Error == noErr)
        {
          /* Search each volume for match */
          /*                              */
          memset(&CatalogSearchParameters, '\0', sizeof(CSParam));
          memset(&SearchInfo1, '\0', sizeof(CInfoPBRec));
          memset(&SearchInfo2, '\0', sizeof(CInfoPBRec));

          CatalogSearchParameters.ioVRefNum       = VolumeRefNumber;
          CatalogSearchParameters.ioMatchPtr      = pParamApplicationFSSpec;
          CatalogSearchParameters.ioReqMatchCount = 1;
          CatalogSearchParameters.ioSearchTime    = 0;
          CatalogSearchParameters.ioSearchBits    = fsSBFlFndrInfo;
          CatalogSearchParameters.ioSearchInfo1   = &SearchInfo1;
          CatalogSearchParameters.ioSearchInfo2   = &SearchInfo2;

          SearchInfo1.hFileInfo.ioNamePtr              = NULL;
          SearchInfo1.hFileInfo.ioFlFndrInfo.fdCreator = ParamCreatorCode;
          SearchInfo1.hFileInfo.ioFlFndrInfo.fdType    = 'APPL';

          SearchInfo2.hFileInfo.ioNamePtr              = NULL;
          SearchInfo2.hFileInfo.ioFlFndrInfo.fdCreator = 0xFFFFFFFF;
          SearchInfo2.hFileInfo.ioFlFndrInfo.fdType    = 0xFFFFFFFF;

          Error = PBCatSearchSync(&CatalogSearchParameters);
          if (Error == noErr)
          {
            /* Success! */
            /*          */
            ReturnCode = 0;
            bContinue = 0;
          }
        }
      }
      else
      {
        bContinue = 0;
      }

      VolumeIndex++;
    }
  }

  return ReturnCode;
}


/****************************************************************************/
/* private_WWHelp_Platform_LaunchApplication                                */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
private_WWHelp_Platform_LaunchApplication(const FSSpec* pParamApplicationFSSpec)
{
  int                  ReturnCode = -1;
  FSSpec               ApplicationFSSpec;
  LaunchParamBlockRec  LaunchParameters;
  OSErr                Error;


  /* Create editable copy of application FSSpec */
  /*                                            */
  memcpy(&ApplicationFSSpec, pParamApplicationFSSpec, sizeof(FSSpec));

  /* Define launch parameters */
  /*                          */
  LaunchParameters.launchBlockID       = extendedBlock;
  LaunchParameters.launchEPBLength     = extendedBlockLen;
  LaunchParameters.launchFileFlags     = launchNoFileFlags;
  LaunchParameters.launchControlFlags  = launchContinue | launchNoFileFlags | launchDontSwitch;
  LaunchParameters.launchAppSpec       = &ApplicationFSSpec;
  LaunchParameters.launchAppParameters = NULL;

  /* Launch application */
  /*                    */
  Error = LaunchApplication(&LaunchParameters);
  if (Error == noErr)
  {
    /* Success! */
    /*          */
    ReturnCode = 0;
  }
  else
  {
    /* Define launch parameters */
    /*                          */
    LaunchParameters.launchBlockID       = extendedBlock;
    LaunchParameters.launchEPBLength     = extendedBlockLen;
    LaunchParameters.launchFileFlags     = launchNoFileFlags;
    LaunchParameters.launchControlFlags  = launchContinue | launchNoFileFlags | launchDontSwitch | launchUseMinimum;
    LaunchParameters.launchAppSpec       = &ApplicationFSSpec;
    LaunchParameters.launchAppParameters = NULL;

    /* Launch application */
    /*                    */
    Error = LaunchApplication(&LaunchParameters);
    if (Error == noErr)
    {
      /* Success! */
      /*          */
      ReturnCode = 0;
    }
  }

  return ReturnCode;
}


/****************************************************************************/
/* WWHelp_Platform_Startup                                                  */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
WWHelp_Platform_Startup()
{
  int     ReturnCode = -1;
  int     Error;
  SInt32  Response;


  /* See if application is running under Mac OS X */
  /*                                              */
  sgbRunningUnderOSX = 0;
  Error = Gestalt(gestaltSystemVersion, &Response);
  if (Error == noErr)
  {
    if (Response >= 0x01000)
    {
      /* System software version greater than 10! */
      /*                                          */
      sgbRunningUnderOSX = 1;
    }

    /* Determine application creator code and IC availability */
    /*                                                        */
    Error = private_WWHelp_Platform_DetermineApplicationCreatorCode();
    if (Error == 0)
    {
      Error = private_WWHelp_Platform_CheckInternetConfig();
      if (Error == 0)
      {
        /* Success! */
        /*          */
        ReturnCode = 0;
      }
    }
  }

  return ReturnCode;
}



/****************************************************************************/
/* WWHelp_Platform_Shutdown                                                 */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
WWHelp_Platform_Shutdown()
{
  int  ReturnCode = -1;


  /* Nothing to do */
  /*               */
  ReturnCode = 0;

  return ReturnCode;
}


/****************************************************************************/
/* WWHelp_Platform_SetDefaultBrowserInfo                                    */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
WWHelp_Platform_SetDefaultBrowserInfo(WWHelp_BrowserInfo* pParamBrowserInfo)
{
  int         ReturnCode = -1;
  short       SavedResFile;
  OSStatus    Error;
  ICInstance  InternetConfigPrefs;
  ICPerm      ReadOnly;
  Handle      hTargetBrowser;
  ICAttr      Attribute;


  /* Reset browser info */
  /*                    */
  WWHelp_Comm_ClearBrowserInfo(pParamBrowserInfo);

  /* All Macintosh browsers support smart URLs */
  /*                                           */
  pParamBrowserInfo->mbAllowSmartURLs = 1;

  /* InternetConfig can switch current resfile */
  /*                                           */
  SavedResFile = CurResFile();

  /* Create InternetConfig instance */
  /*                                */
  Error = ICStart(&InternetConfigPrefs, sgApplicationCreatorCode);
#if (TARGET_API_MAC_OS8)
  if (Error == noErr)
  {
    Error = ICFindConfigFile(InternetConfigPrefs, 0, NULL);
  }
#endif
  if (Error == noErr)
  {
    /* Access InternetConfig preferences */
    /*                                   */
    ReadOnly = icReadOnlyPerm;
    ICBegin(InternetConfigPrefs, ReadOnly);
    hTargetBrowser = NewHandle(1);
    if (hTargetBrowser != NULL)
    {
      /* Determine creator code of default browser */
      /*                                           */
      Attribute = kICAttrNoChange;
      Error = ICFindPrefHandle(InternetConfigPrefs, "\pHelper¥http",
                               &Attribute, hTargetBrowser);
      if (Error == noErr)
      {
        /* Record helper application creator code */
        /*                                        */
        HLock(hTargetBrowser);
        pParamBrowserInfo->mCreatorCode = (**((ICAppSpec**)hTargetBrowser)).fCreator;
        HUnlock(hTargetBrowser);

        /* Determine if this is a Netscape browser */
        /*                                         */
        switch (pParamBrowserInfo->mCreatorCode)
        {
          case 'MOSS':
          case 'MOZZ':
            pParamBrowserInfo->mbNetscapeBrowser = 1;
            break;
        }

        /* Success! */
        /*          */
        ReturnCode = 0;
      }

      DisposeHandle(hTargetBrowser);
    }

    /* Finished with InternetConfig preferences */
    /*                                          */
    ICEnd(InternetConfigPrefs);

    /* Release InternetConfig instance */
    /*                                 */
    ICStop(InternetConfigPrefs);
  }

  /* Restore previous resfile */
  /*                          */
  UseResFile(SavedResFile);

  return ReturnCode;
}


/****************************************************************************/
/* WWHelp_Platform_CheckBrowserRunning                                      */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
WWHelp_Platform_CheckBrowserRunning(const WWHelp_BrowserInfo* pParamBrowserInfo)
{
  int                  ReturnCode = -1;
  ProcessSerialNumber  ProcessSN;


  ReturnCode = private_WWHelp_Platform_GetProcessSN(pParamBrowserInfo->mCreatorCode,
                                                    &ProcessSN);

  return ReturnCode;
}


/****************************************************************************/
/* WWHelp_Platform_LaunchWithURL                                            */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
WWHelp_Platform_LaunchWithURL(const WWHelp_BrowserInfo* pParamBrowserInfo,
                              const char*               pParamURL,
                              const size_t              ParamURLLength)
{
  int     ReturnCode = -1;
  int     Error;
  FSSpec  BrowserFSSpec;


  /* Locate browser */
  /*                */
  Error = private_WWHelp_Platform_FindApplication(pParamBrowserInfo->mCreatorCode,
                                                  &BrowserFSSpec);
  if (Error == 0)
  {
    /* Launch browser */
    /*                */
    Error = private_WWHelp_Platform_LaunchApplication(&BrowserFSSpec);
    if (Error == 0)
    {
      ReturnCode = WWHelp_Platform_SendURL(pParamBrowserInfo,
                                           pParamURL, ParamURLLength);
    }
  }

  return ReturnCode;
}


/****************************************************************************/
/* WWHelp_Platform_SendURL                                                  */
/*                                                                          */
/*                                                                          */
/****************************************************************************/

int
WWHelp_Platform_SendURL(const WWHelp_BrowserInfo* pParamBrowserInfo,
                        const char*               pParamURL,
                        const size_t              ParamURLLength)
{
  int                  ReturnCode = -1;
  OSErr                Error;
  ProcessSerialNumber  ProcessSN;
  AEAddressDesc        TargetAppDesc  = {typeNull, nil};
  AppleEvent           GURLEvent      = {typeNull, nil};
  AEDesc               URLDesc        = {typeNull, nil};
  AppleEvent           ReplyEvent     = {typeNull, nil};
  AEDesc               ReplyErrorDesc = {typeNull, NULL};


  Error = private_WWHelp_Platform_GetProcessSN(pParamBrowserInfo->mCreatorCode,
                                               &ProcessSN);
  if (Error == 0)
  {
    Error = AECreateDesc(typeProcessSerialNumber,
                         (Ptr)&ProcessSN, sizeof(ProcessSerialNumber),
                         &TargetAppDesc);
    if (Error == noErr)
    {
      Error = AECreateAppleEvent('GURL', 'GURL', &TargetAppDesc,
                                 kAutoGenerateReturnID, kAnyTransactionID,
                                 &GURLEvent);
      if (Error == noErr)
      {
        Error = AECreateDesc(typeChar, (Ptr)pParamURL, ParamURLLength, &URLDesc);
        if (Error == noErr)
        {
          Error = AEPutParamDesc(&GURLEvent, keyDirectObject, &URLDesc);
          if (Error == noErr)
          {
            Error = AESend(&GURLEvent, &ReplyEvent,
                           kAEWaitReply + kAEAlwaysInteract + kAECanSwitchLayer,
                           kAENormalPriority, kAEDefaultTimeout, NULL, NULL);
            if (Error == noErr)
            {
              if (ReplyEvent.descriptorType == typeAppleEvent)
              {
                Error = AEGetParamDesc(&ReplyEvent, keyErrorNumber,
                                       typeLongInteger, &ReplyErrorDesc);
                if (Error == noErr)
                {
                  AEDisposeDesc(&ReplyErrorDesc);
                }
                else if (Error == errAEDescNotFound)
                {
                  Error = SetFrontProcess(&ProcessSN);
                  if (Error == noErr)
                  {
                    /* Success! */
                    /*          */
                    ReturnCode = 0;
                  }
                }
              }

              AEDisposeDesc(&ReplyEvent);
            }
          }

          AEDisposeDesc(&URLDesc);
        }

        AEDisposeDesc(&GURLEvent);
      }

      AEDisposeDesc(&TargetAppDesc);
    }
  }

  return ReturnCode;
}
