mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-08 04:27:37 +00:00
1039 lines
26 KiB
C++
1039 lines
26 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.
|
||
*/
|
||
|
||
// Macintosh front-end
|
||
|
||
#include "msv2dsk.h"
|
||
|
||
#include "earlmgr.h"
|
||
#include "macutil.h"
|
||
#include "uerrmgr.h"
|
||
#include "uprefd.h"
|
||
#include "ufilemgr.h"
|
||
#include "ulaunch.h"
|
||
#include "macutil.h"
|
||
#include "resgui.h"
|
||
#include "prefwutil.h"
|
||
#include "uapp.h"
|
||
#include "resae.h"
|
||
|
||
#include "CNSContext.h"
|
||
#include "CDownloadProgressWindow.h"
|
||
|
||
// Netscape
|
||
#include "merrors.h"
|
||
#include "xp_thrmo.h"
|
||
#include "mkutils.h"
|
||
#include "glhist.h"
|
||
#include "xlate.h"
|
||
#include "prefapi.h"
|
||
|
||
#include "BufferStream.h"
|
||
#include "PascalString.h"
|
||
|
||
// system
|
||
#include <StandardFile.h>
|
||
#include "merrors.h"
|
||
#include "InternetConfig.h"
|
||
|
||
|
||
static Boolean GetMacFileTypesFromMimeHeader( const URL_Struct * fRequest,
|
||
OSType * fileCreator,
|
||
OSType * fileType );
|
||
|
||
static OSType TextToOSType( const char* text);
|
||
|
||
|
||
|
||
extern int MK_DISK_FULL;
|
||
|
||
|
||
|
||
extern CMimeMapper * CreateUnknownMimeTypeMapper (URL_Struct * request);
|
||
extern void GUI_PostAlertNoReply (const CStr255 & message);
|
||
extern OSErr GUI_AskForFileSpec (StandardFileReply & reply);
|
||
|
||
/*------------------=----------------------------------------------------------
|
||
External function so everyone can start a progress load without having
|
||
to include the full .h file
|
||
--------------------=--------------------------------------------------------*/
|
||
|
||
extern "C" void SaveAsCompletionProc( PrintSetup* p );
|
||
extern "C" void SaveAsCompletionProc( PrintSetup* p )
|
||
{
|
||
XP_FileClose (p->out);
|
||
CNSContext* theContext = (CNSContext*)p->url->fe_data;
|
||
|
||
// FIX ME!!! need to put this back in
|
||
// if (theContext != NULL)
|
||
// theContext->AllConnectionsComplete();
|
||
|
||
|
||
}
|
||
|
||
extern int MK_OUT_OF_MEMORY;
|
||
|
||
int PROGRESS_GetUrl(
|
||
URL_Struct *,
|
||
int /*format_out*/,
|
||
const FSSpec * /*destination*/,
|
||
Boolean /*delayed*/)
|
||
{
|
||
Assert_(false); // NO LONGER VALID
|
||
return 0;
|
||
}
|
||
|
||
|
||
/*-----------------------------------------------------------------------------
|
||
When we decide to do something with an url (such as save it to disk, print
|
||
it, mail it, etc), we sometimes have extra data which specializes the
|
||
action. Save-to-disk is the best example: a destination file. We need a
|
||
place to put it all.
|
||
|
||
We have been putting this data in the context. This is a mistake because
|
||
the context is not specific to any particular url action, and multiple
|
||
requests will cause a race condition.
|
||
|
||
The url struct is where it belongs, since this object is created at that
|
||
time (making it like MacApp's Command class). It has an fe_data field,
|
||
but we need to put *lots* of data there. Argh.
|
||
|
||
Things go to disk when we launch an external viewer (click -> download in
|
||
normal context) or explicitly request a download (SAVE_AS, SAVE_TO_DISK,
|
||
VIEW_SOURCE, and Drag&Drop).
|
||
FO_PRESENT auto-launch external viewer (by normal click).
|
||
VIEW_SOURCE auto-launch with view source application
|
||
SAVE_TO_DISK prompt for location and save to disk
|
||
SAVE_AS prompt for location and save to disk
|
||
INTERNAL_IMAGE display internally.
|
||
Drag&Drop Variant of SAVE_TO_DISK
|
||
SAVE_ALL Save page & images. Like SAVE_AS.
|
||
-----------------------------------------------------------------------------*/
|
||
|
||
class DownloadFilePipe: public Pipe
|
||
{
|
||
public:
|
||
DownloadFilePipe (
|
||
CNSContext* progressContext,
|
||
URL_Struct* request);
|
||
~DownloadFilePipe ();
|
||
OSErr Open ();
|
||
int Write (const char *buffer, int len);
|
||
void Complete ();
|
||
void Abort (int reason);
|
||
|
||
OSErr GetFileSpec ();
|
||
inline Boolean TranslateLinefeeds ();
|
||
Boolean LaunchWhenDone ();
|
||
CNSContext* mContext;
|
||
|
||
protected:
|
||
URL_Struct * fRequest;
|
||
NET_StreamClass * fInput;
|
||
// fFile logic gets complicated. There are two questions:
|
||
// - where to create the file
|
||
// - when should the file object be deleted
|
||
// - when should the file be deleted
|
||
// The answers depend upon the stream:
|
||
// case FO_PRESENT:
|
||
// ask mapper for the registered viewer file spec.
|
||
// if we get these specs, registered viewer will handle the physical file deletion
|
||
// we need to delete the fFile object
|
||
// else, do the same as
|
||
// case FO_VIEW_SOURCE:
|
||
// register with the app, do not delete fFile yourself
|
||
// case FO_SAVE_AS:
|
||
// do not register, delete file object -
|
||
|
||
LFileBufferStream * fFile;
|
||
Boolean fDeleteOnDestroy; // Should we delete the file object?
|
||
int fIntention;
|
||
CMimeMapper * fHandler;
|
||
Boolean fHasDestination;
|
||
FSSpec fDestination;
|
||
|
||
OSErr GetSilentFileSpec();
|
||
OSErr GetUserFileSpec();
|
||
|
||
friend NET_StreamClass * NewFilePipe (int format_out, void *registration, URL_Struct * request, MWContext *context);
|
||
};
|
||
|
||
static LArray DownloadList; // Keeps track of all downloads, so that we can see if we are interrupting any
|
||
|
||
Boolean HasDownloads(MWContext * context)
|
||
{
|
||
DownloadFilePipe * pipe;
|
||
LArrayIterator iter(DownloadList);
|
||
while (iter.Next(&pipe))
|
||
{
|
||
MWContext* theContext = *(pipe->mContext);
|
||
if (theContext == context)
|
||
return true;
|
||
|
||
}
|
||
return false;
|
||
}
|
||
|
||
//--------------------------------------------------------------------------------
|
||
// DownloadFilePipe
|
||
//--------------------------------------------------------------------------------
|
||
|
||
DownloadFilePipe::DownloadFilePipe (
|
||
CNSContext* progressContext,
|
||
URL_Struct* request)
|
||
{
|
||
mContext = progressContext;
|
||
mContext->AddUser(this);
|
||
|
||
fRequest = request;
|
||
fInput = nil;
|
||
fFile = nil;
|
||
fIntention = -1;
|
||
|
||
fHandler = nil;
|
||
fHasDestination = false;
|
||
fDeleteOnDestroy = true;
|
||
DownloadList.InsertItemsAt(1,1,&this);
|
||
}
|
||
|
||
|
||
DownloadFilePipe::~DownloadFilePipe ()
|
||
{
|
||
mContext->RemoveUser(this);
|
||
|
||
if (fFile && fDeleteOnDestroy)
|
||
{
|
||
CFileMgr::sFileManager.CancelRegister(fFile);
|
||
delete fFile;
|
||
}
|
||
Int32 where = DownloadList.FetchIndexOf(&this);
|
||
DownloadList.RemoveItemsAt(1,where);
|
||
}
|
||
|
||
OSErr DownloadFilePipe::GetSilentFileSpec()
|
||
{
|
||
OSErr err;
|
||
//CStr31 defaultName = CFileMgr::FileNameFromURL( fRequest->address );
|
||
CStr31 defaultName;
|
||
if( fRequest->content_name )
|
||
{
|
||
char* fileName = XP_STRDUP ( fRequest->content_name );
|
||
if ( fileName )
|
||
SwapSlashColon( fileName );
|
||
defaultName = fileName;
|
||
XP_FREEIF( fileName );
|
||
}
|
||
else
|
||
defaultName = CFileMgr::FileNameFromURL( fRequest->address ) ;
|
||
|
||
|
||
FSSpec defaultFolder;
|
||
|
||
defaultFolder = CPrefs::GetFilePrototype( CPrefs::DownloadFolder );
|
||
err = CFileMgr::UniqueFileSpec( defaultFolder, defaultName, fDestination );
|
||
if ( err )
|
||
return err;
|
||
fDeleteOnDestroy = FALSE;
|
||
fHasDestination = TRUE;
|
||
return noErr;
|
||
}
|
||
|
||
// Get specs from the user
|
||
OSErr DownloadFilePipe::GetUserFileSpec()
|
||
{
|
||
OSErr err;
|
||
CStr31 defaultName;
|
||
if( fRequest->content_name )
|
||
defaultName = fRequest->content_name;
|
||
else
|
||
defaultName = CFileMgr::FileNameFromURL( fRequest->address ) ;
|
||
|
||
StandardFileReply reply;
|
||
|
||
(CStr63&)reply.sfFile.name = defaultName;
|
||
err = GUI_AskForFileSpec( reply );
|
||
|
||
if ( err )
|
||
return err;
|
||
|
||
fDestination = reply.sfFile;
|
||
fHasDestination = TRUE;
|
||
fDeleteOnDestroy = TRUE; // do not delete automatically
|
||
|
||
return noErr;
|
||
}
|
||
|
||
// Obtain the location of where the stream should be downloaded to
|
||
OSErr DownloadFilePipe::GetFileSpec ()
|
||
{
|
||
OSErr err = noErr;
|
||
if (fHasDestination)
|
||
fDeleteOnDestroy = true;
|
||
else
|
||
{
|
||
// case FO_PRESENT:
|
||
// ask mapper for the registered viewer file spec.
|
||
// if we get these specs, registered viewer will handle the physical file deletion
|
||
// we need to delete the fFile object
|
||
// else, do the same as
|
||
// case FO_VIEW_SOURCE:
|
||
// register with the app, do not delete fFile yourself
|
||
// case FO_SAVE_AS:
|
||
// we should have the spec already, die otherwise
|
||
|
||
switch (fIntention) {
|
||
case FO_PRESENT:
|
||
|
||
|
||
if (fHandler)
|
||
{
|
||
// See if the helper app is already running and
|
||
// has registered with us at runtime. If so,
|
||
// give it a chance to tell us where to put the file.
|
||
//
|
||
err = fHandler->GetFileSpec(fRequest, fDestination);
|
||
|
||
// tj, with mourey and timm:
|
||
//
|
||
// This wasn't guarded by fHandler but we're sure it should be.
|
||
// We should only delete on destroy if the helper app has registered
|
||
// with us at runtime and tells us where to save the file. Otherwise,
|
||
// we need to prompt for the file name/location.
|
||
//
|
||
// This never used to be a problem because fHandler was never NULL.
|
||
// Now with support for mac type/creator from the mime header, fHandler
|
||
// may be NULL.
|
||
|
||
if (err == noErr)
|
||
{
|
||
fDeleteOnDestroy = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (LaunchWhenDone())
|
||
err = GetSilentFileSpec();
|
||
else
|
||
err = GetUserFileSpec();
|
||
|
||
break;
|
||
case FO_SAVE_AS:
|
||
err = GetUserFileSpec();
|
||
break;
|
||
case FO_VIEW_SOURCE:
|
||
err = GetSilentFileSpec();
|
||
break;
|
||
default:
|
||
Assert_(false);
|
||
}
|
||
}
|
||
if (err == noErr) // No error in creating the spec, make the stream
|
||
{
|
||
try
|
||
{
|
||
fFile = new LFileBufferStream (fDestination);
|
||
}
|
||
catch (OSErr err)
|
||
{
|
||
return err;
|
||
}
|
||
|
||
fFile->DoUseBuffer();
|
||
if (fRequest && fRequest->address)
|
||
fFile->SetURL(XP_STRDUP(fRequest->address));
|
||
|
||
if (fDeleteOnDestroy == false) // If we are not taking care of the file, file manager is
|
||
CFileMgr::sFileManager.RegisterFile(fFile);
|
||
}
|
||
return err;
|
||
}
|
||
|
||
inline Boolean DownloadFilePipe::TranslateLinefeeds ()
|
||
{
|
||
return (fIntention == FO_VIEW_SOURCE);
|
||
}
|
||
|
||
Boolean DownloadFilePipe::LaunchWhenDone ()
|
||
{
|
||
/*
|
||
For view source we always want to launch the viewer.
|
||
For saving HTML, don't launch it, just save.
|
||
For drag&drop, we want to launch Stuffit (for FTP convenience) but not
|
||
a text viewer, so text/html->false, the rest according to mime mapper
|
||
*/
|
||
if (fIntention == FO_VIEW_SOURCE)
|
||
return true;
|
||
if (fIntention == FO_SAVE_AS)
|
||
return false;
|
||
if (strcmp(fRequest->content_type, "text/html") == 0) // LAM need entry for text/html!
|
||
return false;
|
||
|
||
if (fHandler != NULL)
|
||
return fHandler->GetLoadAction() == CMimeMapper::Launch;
|
||
else
|
||
return false;
|
||
}
|
||
|
||
|
||
OSErr DownloadFilePipe::Open ()
|
||
{
|
||
/*
|
||
If this is a drag&drop or SAVE_AS then we already have a filespec.
|
||
If the helper application is set to "launch" then we automatically
|
||
choose a filespec. Otherwise we have to ask.
|
||
*/
|
||
OSErr err;
|
||
cstring appName = "\0"; // app that can open the attachment
|
||
CMimeMapper * mapper = nil; // mime-type -> mac-type mapper
|
||
OSType fileType = 0; // mac file type of the attachment
|
||
OSType fileCreator = 0; // mac creator type of the attachment
|
||
|
||
|
||
//
|
||
// Figure out the appropriate file type and creator
|
||
//
|
||
|
||
if (fIntention == FO_VIEW_SOURCE || strcmp(fRequest->content_type,"text/html") == 0)
|
||
mapper = CPrefs::sMimeTypes.FindMimeType (CMimeList::HTMLViewer);
|
||
|
||
if (mapper == NULL)
|
||
{
|
||
//
|
||
// If we have content of application/octet-stream, look for the mac file type
|
||
// and creator in the URL struct (from the mime header).
|
||
//
|
||
// If we find valid mac file type/creator data, or if the content_type
|
||
// isn't octet-stream, then we attempt content_type -> mime-type mapping
|
||
//
|
||
// MIME content types are case insensitive
|
||
if ( (XP_STRCASECMP(fRequest->content_type, APPLICATION_OCTET_STREAM) != 0) ||
|
||
!GetMacFileTypesFromMimeHeader(fRequest, &fileCreator, &fileType) )
|
||
{
|
||
mapper = CPrefs::sMimeTypes.FindMimeType (fRequest->content_type);
|
||
|
||
if (mapper && mapper->GetLoadAction() == CMimeMapper::Unknown)
|
||
{
|
||
//
|
||
// If they specified "Unknown" in the prefs, then we have an existing
|
||
// mapper that tells us this fact. To know how to handle the stream
|
||
// this time, we need to ask them what to do and create a new mapper.
|
||
//
|
||
mapper = CreateUnknownMimeTypeMapper (fRequest);
|
||
if (!mapper)
|
||
return userCanceledErr;
|
||
}
|
||
else if (mapper == NULL)
|
||
{
|
||
if (!GetMacFileTypesFromMimeHeader(fRequest, &fileCreator, &fileType))
|
||
{
|
||
// TRY IC
|
||
|
||
// Time to call IC to see if it knows anything
|
||
ICMapEntry ICMapper;
|
||
|
||
ICError error = 0;
|
||
CStr255 saveName;
|
||
if( fRequest->content_name )
|
||
{
|
||
char* fileName = XP_STRDUP ( fRequest->content_name );
|
||
if ( fileName )
|
||
SwapSlashColon( fileName );
|
||
saveName = fileName;
|
||
XP_FREEIF( fileName );
|
||
}
|
||
else
|
||
saveName = CFileMgr::FileNameFromURL( fRequest->address ) ;
|
||
|
||
error = CInternetConfigInterface::MapFileName( saveName , &ICMapper );
|
||
|
||
if( error != icPrefNotFoundErr && StrLength(ICMapper.MIME_type) )
|
||
{
|
||
fileCreator = ICMapper.file_creator;
|
||
fileType = ICMapper.file_type;
|
||
}
|
||
else
|
||
{
|
||
if (fIntention != FO_SAVE_AS)
|
||
{
|
||
mapper = CreateUnknownMimeTypeMapper (fRequest);
|
||
if (!mapper)
|
||
return userCanceledErr;
|
||
}
|
||
else
|
||
{
|
||
fileType = 'TEXT';
|
||
fileCreator = emSignature;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// If fHandler is NULL, then fileType and fileCreator have been set
|
||
// from the mime header via GetMacFileTypesFromMimeHeader.
|
||
//
|
||
// When fHandler is NULL, appName will be empty because we're not
|
||
// doing anything further to match the file to its application
|
||
//
|
||
fHandler = mapper;
|
||
if (fHandler != NULL)
|
||
{
|
||
fileCreator = fHandler->GetAppSig();
|
||
fileType = fHandler->GetDocType();
|
||
appName = fHandler->GetAppName();
|
||
|
||
OSType headerCreator, headerType;
|
||
|
||
// if the file creator is the same as the one suggested by the mime mapper
|
||
// use the macheader to determine file type.
|
||
if( GetMacFileTypesFromMimeHeader(fRequest, &headerCreator, &headerType) )
|
||
{
|
||
if( headerCreator == fileCreator )
|
||
{
|
||
fileType = headerType;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Additional little hack to make sure if we've got a text/html type file and we're supposed
|
||
// to view it internally we set it to our creator code
|
||
if (strcmp(fRequest->content_type,"text/html") == 0 &&
|
||
mapper->GetLoadAction() == CMimeMapper::Internal)
|
||
{
|
||
fileType = 'TEXT';
|
||
fileCreator = emSignature;
|
||
}
|
||
|
||
Assert_(fileCreator != (OSType) 0 && fileType != (OSType) 0);
|
||
|
||
|
||
//
|
||
// Figure out the appropriate file type and creator
|
||
//
|
||
|
||
err = GetFileSpec ();
|
||
if (err)
|
||
return err;
|
||
// Make a file object, file stream, and stream buffer.
|
||
Try_
|
||
{
|
||
// Open an existing file before creating because for drag&drop we will have
|
||
// created a Bookmark file as a placeholder. For all other cases, we have
|
||
// a unique filespec.
|
||
fFile->OpenDataFork (fsRdWrPerm);
|
||
err = CFileMgr::SetFileTypeCreator(fileCreator, fileType, &fDestination);
|
||
ThrowIfOSErr_ (err);
|
||
}
|
||
Catch_ (openErr)
|
||
{
|
||
if (openErr == fnfErr) {
|
||
fFile->CreateNewDataFile (fileCreator, fileType, smSystemScript);
|
||
fFile->OpenDataFork (fsRdWrPerm);
|
||
}
|
||
else
|
||
Throw_ (openErr);
|
||
}
|
||
EndCatch_
|
||
|
||
if (fFile && fRequest && fRequest->address)
|
||
{
|
||
FSSpec macSpec;
|
||
fFile->GetSpecifier(macSpec);
|
||
CFileMgr::FileSetComment (macSpec, CStr255(fRequest->address));
|
||
}
|
||
CStr255 message;
|
||
StringHandle messageHdl = ::GetString(SAVE_QUOTE_RESID);
|
||
|
||
if(messageHdl && *messageHdl) {
|
||
::HLock((Handle) messageHdl);
|
||
CopyString((unsigned char *) message, (const unsigned char *) *messageHdl);
|
||
::HUnlock((Handle) messageHdl);
|
||
}
|
||
message += CStr255(fDestination.name);
|
||
message += "\"";
|
||
|
||
CContextProgress* theProgress = mContext->GetCurrentProgressStats();
|
||
Assert_(theProgress != NULL);
|
||
StSharer theShareLock(theProgress);
|
||
|
||
theProgress->mAction = message;
|
||
|
||
if (LaunchWhenDone()) {
|
||
|
||
messageHdl = ::GetString(WILL_OPEN_WITH_RESID);
|
||
if(messageHdl && *messageHdl) {
|
||
::HLock((Handle) messageHdl);
|
||
CopyString((unsigned char *) message, (const unsigned char *) *messageHdl);
|
||
::HUnlock((Handle) messageHdl);
|
||
}
|
||
message += appName;
|
||
message += GetCString(WILL_OPEN_TERM_RESID);
|
||
}
|
||
else {
|
||
message = "";
|
||
messageHdl = ::GetString(SAVE_AS_A_RESID);
|
||
if(messageHdl && *messageHdl) {
|
||
::HLock((Handle) messageHdl);
|
||
CopyString((unsigned char *) message, (const unsigned char *) *messageHdl);
|
||
::HUnlock((Handle) messageHdl);
|
||
}
|
||
message += appName;
|
||
message += GetCString(FILE_RESID);
|
||
}
|
||
|
||
theProgress->mComment = message;
|
||
mContext->UpdateCurrentProgressStats();
|
||
|
||
if( fRequest->server_can_do_byteranges || fRequest->server_can_do_restart )
|
||
fRequest->must_cache = TRUE;
|
||
|
||
return noErr;
|
||
}
|
||
|
||
int
|
||
DownloadFilePipe::Write (const char *buffer, int len)
|
||
{
|
||
int written = 0;
|
||
Try_
|
||
{
|
||
if (TranslateLinefeeds()) {
|
||
for (int i = 0; i < len; i++) {
|
||
char c = buffer[i];
|
||
if (c == LF)
|
||
c = CR;
|
||
fFile->WriteData (&c, 1);
|
||
}
|
||
written = len;
|
||
}
|
||
else
|
||
written = fFile->WriteData (buffer, len);
|
||
}
|
||
Catch_ (writeErr)
|
||
{
|
||
CStr255 message = (const char*) GetCString(COULD_NOT_SAVE_RESID);
|
||
message += CStr63(fDestination.name);
|
||
if (writeErr == dskFulErr)
|
||
message += CStr255((const char*)GetCString(DISK_FULL_RESID));
|
||
else
|
||
message += CStr255((const char*)GetCString(DISK_ERR_RESID));
|
||
ErrorManager::PlainAlert(message);
|
||
return MK_DISK_FULL;
|
||
}
|
||
EndCatch_
|
||
|
||
return written;
|
||
}
|
||
|
||
// Close the file
|
||
// Launch the helper application if necessary
|
||
void DownloadFilePipe::Complete ()
|
||
{
|
||
if (fRequest)
|
||
GH_UpdateGlobalHistory(fRequest);
|
||
if (fFile)
|
||
Try_
|
||
{
|
||
fFile->CloseDataFork();
|
||
CFileMgr::UpdateFinderDisplay(fDestination);
|
||
}
|
||
Catch_(inErr){}
|
||
EndCatch_
|
||
|
||
NET_RemoveURLFromCache(fRequest);
|
||
NET_RemoveDiskCacheObjects(0);
|
||
|
||
Try_
|
||
{
|
||
if (LaunchWhenDone() && fHandler && fFile)
|
||
|
||
// fHandler->LaunchFile(fFile, fRequest, fCppContext->GetContextUniqueID());
|
||
// FIX ME!!! we need to implement the unique ID stuff in the CNSContext
|
||
fHandler->LaunchFile(fFile, fRequest, 69);
|
||
}
|
||
Catch_(inErr){}
|
||
EndCatch_
|
||
}
|
||
|
||
void DownloadFilePipe::Abort (int /*reason*/)
|
||
{
|
||
if (fFile)
|
||
{
|
||
Try_
|
||
{
|
||
fFile->CloseDataFork();
|
||
}
|
||
Catch_(inErr)
|
||
{}
|
||
EndCatch_
|
||
FSSpec fileSpec;
|
||
fFile->GetSpecifier (fileSpec);
|
||
FSpDelete (&fileSpec);
|
||
}
|
||
fDeleteOnDestroy = true;
|
||
}
|
||
|
||
/*-----------------------------------------------------------------------------
|
||
C++ URL Streams
|
||
|
||
Streams are responsible for reading data in to memory/disk
|
||
They will also get "hinting" information and be responsible for
|
||
ultimately deciding where to direct the data.
|
||
|
||
display_converter and Pipe::Handle* are called from C code. They cannot
|
||
pass any exceptions up.
|
||
-----------------------------------------------------------------------------*/
|
||
|
||
int Pipe::HandleProcess (NET_StreamClass *stream, const char *buffer, int32 buffLen)
|
||
{
|
||
void *streamData=stream->data_object;
|
||
volatile int result = -1;
|
||
Try_ {
|
||
Pipe *self = (Pipe*) streamData;
|
||
result = self->Write (buffer, buffLen);
|
||
}
|
||
Catch_ (err) {
|
||
XP_TRACE(("*** Caught an error (%i) in Pipe::HandleProcess", err));
|
||
result = -1;
|
||
}
|
||
EndCatch_
|
||
return result;
|
||
}
|
||
|
||
void Pipe::HandleComplete (NET_StreamClass *stream)
|
||
{
|
||
void *streamData=stream->data_object;
|
||
Try_ {
|
||
Pipe *self = (Pipe*) streamData;
|
||
self->Complete ();
|
||
delete self;
|
||
}
|
||
Catch_ (err) {
|
||
XP_TRACE(("*** Caught an error (%i) in Pipe::HandleComplete but can't return it", err));
|
||
}
|
||
EndCatch_
|
||
}
|
||
|
||
void Pipe::HandleAbort (NET_StreamClass *stream, int reason)
|
||
{
|
||
void *streamData=stream->data_object;
|
||
Try_ {
|
||
XP_TRACE(("*** HandleAbort %i", reason));
|
||
Pipe *self = (Pipe*) streamData;
|
||
self->Abort (reason);
|
||
delete self;
|
||
}
|
||
Catch_ (err) {
|
||
XP_TRACE(("*** Caught an error (%i) in Pipe::HandleAbort but can't return it", err));
|
||
}
|
||
EndCatch_
|
||
}
|
||
|
||
unsigned int Pipe::HandleWriteReady (NET_StreamClass * /*stream*/)
|
||
{
|
||
return 32765;
|
||
}
|
||
|
||
//
|
||
//
|
||
//
|
||
|
||
OSErr
|
||
Pipe::Open ()
|
||
{
|
||
return noErr;
|
||
}
|
||
|
||
int
|
||
Pipe::Write (const char */*buffer*/, int buffLen)
|
||
{
|
||
return buffLen;
|
||
}
|
||
|
||
void
|
||
Pipe::Complete ()
|
||
{
|
||
}
|
||
|
||
void
|
||
Pipe::Abort (int /*reason*/)
|
||
{
|
||
}
|
||
|
||
//
|
||
// api
|
||
//
|
||
|
||
NET_StreamClass *
|
||
Pipe::MakeStreamObject (char * name, MWContext * context)
|
||
{
|
||
NET_StreamClass * stream = NET_NewStream (
|
||
name,
|
||
Pipe::HandleProcess,
|
||
Pipe::HandleComplete,
|
||
Pipe::HandleAbort,
|
||
Pipe::HandleWriteReady,
|
||
this,
|
||
context);
|
||
return stream;
|
||
}
|
||
|
||
//
|
||
// Construction & Stuff
|
||
//
|
||
|
||
Pipe::Pipe ()
|
||
{
|
||
}
|
||
|
||
Pipe::~Pipe ()
|
||
{
|
||
}
|
||
|
||
|
||
CMimeMapper * CreateUnknownMimeTypeMapper(URL_Struct * request);
|
||
// After receiving the request for unknown mime type, this routine is called
|
||
// to create a mapper for an unknown MIME type
|
||
CMimeMapper* CreateUnknownMimeTypeMapper( URL_Struct* request )
|
||
{
|
||
CStr255 fileName;
|
||
CStr255 content_type;
|
||
CStr31 newName;
|
||
int result;
|
||
CMimeMapper* mapper = NULL;
|
||
|
||
if (!ErrorManager::TryToInteract(900))
|
||
return NULL;
|
||
// Notify the user with nice names
|
||
newName = CFileMgr::FileNameFromURL( request->address );
|
||
fileName = newName;
|
||
content_type = request->content_type;
|
||
|
||
::ParamText( newName, content_type, CStr255::sEmptyString, CStr255::sEmptyString );
|
||
|
||
UDesktop::Deactivate();
|
||
result = ::CautionAlert( ALRT_UnknownMimeType, NULL );
|
||
UDesktop::Activate();
|
||
|
||
// What does user want?
|
||
switch ( result )
|
||
{
|
||
case ALRT_UnknownMimeType_Cancel:
|
||
return NULL;
|
||
case ALRT_UnknownMimeType_Save:
|
||
mapper = CPrefs::CreateDefaultUnknownMapper( request->content_type, TRUE );
|
||
mapper->SetLoadAction( CMimeMapper::Save );
|
||
return mapper;
|
||
case ALRT_UnknownMimeType_PickApp:
|
||
StandardFileReply reply;
|
||
CFilePicker::DoCustomGetFile( reply, CFilePicker::Applications, FALSE );
|
||
if ( reply.sfGood )
|
||
mapper = CPrefs::CreateDefaultAppMapper( reply.sfFile, request->content_type, TRUE );
|
||
return mapper;
|
||
case ALRT_UnknownMimeType_MoreInfo:
|
||
{
|
||
char* string;
|
||
if (PREF_CopyConfigString("internal_url.more_info_plugin.url", &string) != PREF_NOERROR)
|
||
return NULL;
|
||
|
||
cstring url;
|
||
url = (const char*) string; // cstring<6E>s operator= expects unsigned char* to be a P string, so we have to cast
|
||
url += "?";
|
||
url += request->content_type;
|
||
|
||
AppleEvent getURLEvent;
|
||
UAppleEventsMgr::MakeAppleEvent( AE_url_suite, AE_url_getURL, getURLEvent );
|
||
|
||
OSErr err = ::AEPutParamPtr( &getURLEvent, keyDirectObject, typeChar, url.data(), strlen(url.data()) );
|
||
|
||
if ( err == noErr )
|
||
err = ::AEPutParamPtr( &getURLEvent, AE_url_getURLname, typeChar, url.data(), strlen(url.data()) );
|
||
|
||
if ( err == noErr )
|
||
UAppleEventsMgr::SendAppleEvent (getURLEvent );
|
||
|
||
return NULL;
|
||
}
|
||
default:
|
||
return NULL;
|
||
}
|
||
return NULL;
|
||
}
|
||
|
||
NET_StreamClass * NewFilePipe (
|
||
int format_out,
|
||
void * /*registration*/,
|
||
URL_Struct * request,
|
||
MWContext* context)
|
||
{
|
||
CNSContext* theContext = ExtractNSContext(context);
|
||
Assert_(theContext != NULL);
|
||
|
||
GH_UpdateGlobalHistory(request);
|
||
|
||
// 97-06-15 pkc -- We really should make a function to determine if a URL is a
|
||
// mail attachment instead of duplicating this code.
|
||
const char* urlAddress = request ? request->address : nil;
|
||
Boolean isMailMessage = false;
|
||
if (urlAddress)
|
||
{
|
||
// check to see if this is a mail or news messages
|
||
if (!strncasecomp (urlAddress, "mailbox:", 8) || !strncasecomp (urlAddress, "news:", 5)
|
||
|| !strncasecomp (urlAddress, "IMAP:", 5) )
|
||
{
|
||
{
|
||
// this is a mail message
|
||
isMailMessage = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 97-06-15 pkc -- The bug is that when you close the browser
|
||
// window for the mail message, the MimeObject* in the context is freed. That pointer is also
|
||
// referenced somewhere else and used by libnet. Whoops. So, since we're trying to ship, don't
|
||
// spawn a seperate progress context for mail messages.
|
||
// 97-08-15 sdagley - Removing check for theContext->IsCloneRequired() as it won't be set to true
|
||
// in frames which is the cause of bug #75288. The alternative fix is to change the CNSContext constructors
|
||
// to initialize mRequiresClone to true instead of the current default of false.
|
||
if (/*theContext->IsCloneRequired() &&*/ !isMailMessage)
|
||
{
|
||
CNSContext* theProgressContext = NULL;
|
||
CDownloadProgressWindow* theProgressWindow = NULL;
|
||
try
|
||
{
|
||
theProgressContext = new CNSContext(*theContext);
|
||
StSharer theShareLock(theProgressContext);
|
||
|
||
// 97-06-12 pkc -- Move call to NET_SetNewContext here so that if it fails
|
||
// (which can happen if mocha calls us) we still use context
|
||
if(NET_SetNewContext(request, *theProgressContext, EarlManager::DispatchFinishLoadURL) == 0)
|
||
theContext = theProgressContext;
|
||
|
||
theProgressWindow = dynamic_cast<CDownloadProgressWindow*>(LWindow::CreateWindow(WIND_DownloadProgress, LCommander::GetTopCommander()));
|
||
ThrowIfNULL_(theProgressWindow);
|
||
theProgressWindow->Show();
|
||
|
||
// theProgressWindow->SetWindowContext(theProgressContext);
|
||
theProgressWindow->SetWindowContext(theContext);
|
||
// the window will be shown upon progress initialization
|
||
|
||
}
|
||
catch(...)
|
||
{
|
||
delete theProgressWindow;
|
||
return NULL;
|
||
}
|
||
}
|
||
|
||
DownloadFilePipe* thePipe = NULL;
|
||
NET_StreamClass* theStream = NULL;
|
||
|
||
try
|
||
{
|
||
// 97-06-05 pkc -- if theContext doesn't have ContextProgress, make one now
|
||
if (!theContext->GetContextProgress())
|
||
theContext->EnsureContextProgress();
|
||
thePipe = new DownloadFilePipe(theContext, request);
|
||
|
||
thePipe->fIntention = CLEAR_CACHE_BIT(format_out);
|
||
if (request->fe_data)
|
||
{
|
||
thePipe->fDestination = *(FSSpec*)request->fe_data;
|
||
thePipe->fHasDestination = true;
|
||
XP_FREE(request->fe_data);
|
||
}
|
||
|
||
OSErr theErr = thePipe->Open();
|
||
ThrowIfOSErr_(theErr);
|
||
|
||
theStream = thePipe->MakeStreamObject("Download File", *theContext);
|
||
thePipe->fInput = theStream;
|
||
}
|
||
catch (...)
|
||
{
|
||
if (thePipe != NULL)
|
||
thePipe->Abort (-1);
|
||
delete thePipe;
|
||
|
||
XP_DELETE(theStream);
|
||
theStream = NULL;
|
||
}
|
||
|
||
return theStream;
|
||
}
|
||
|
||
|
||
//
|
||
// Snarf the mac file type and creator out of the mime header.
|
||
//
|
||
// Returns true if valid data is found, false if not.
|
||
//
|
||
static Boolean
|
||
GetMacFileTypesFromMimeHeader( const URL_Struct * fRequest,
|
||
OSType * fileCreator,
|
||
OSType * fileType )
|
||
{
|
||
*fileCreator = 0;
|
||
*fileType = 0;
|
||
|
||
Assert_(fRequest != NULL);
|
||
|
||
if ( fRequest->x_mac_creator == NULL ||
|
||
fRequest->x_mac_type == NULL )
|
||
{
|
||
return false;
|
||
}
|
||
|
||
*fileCreator = TextToOSType(fRequest->x_mac_creator);
|
||
*fileType = TextToOSType(fRequest->x_mac_type);
|
||
|
||
return (*fileCreator != 0 && *fileType != 0);
|
||
}
|
||
|
||
|
||
static OSType
|
||
TextToOSType( const char* text)
|
||
{
|
||
OSType result = 0;
|
||
UInt32 len;
|
||
|
||
//
|
||
// If the text is 4-characters long, we treat it
|
||
// as a raw OSType.
|
||
//
|
||
// If it is 8-characters, it is hex-encoded.
|
||
//
|
||
// If it's not 4 or 8, we return 0;
|
||
//
|
||
|
||
len = strlen(text);
|
||
|
||
if (len == 4)
|
||
result = * ((OSType *) text);
|
||
else
|
||
if (len == 8)
|
||
sscanf(text, "%lx", &result);
|
||
|
||
return result;
|
||
}
|
||
|
||
|
||
|
||
|