diff options
Diffstat (limited to 'embed/xulrunner/components/MozDownload.cpp')
-rw-r--r-- | embed/xulrunner/components/MozDownload.cpp | 812 |
1 files changed, 812 insertions, 0 deletions
diff --git a/embed/xulrunner/components/MozDownload.cpp b/embed/xulrunner/components/MozDownload.cpp new file mode 100644 index 000000000..d6d28ec59 --- /dev/null +++ b/embed/xulrunner/components/MozDownload.cpp @@ -0,0 +1,812 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright © 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Conrad Carlen <ccarlen@netscape.com> + * + * Adapted for epiphany by Marco Pesenti Gritti <marco@gnome.org> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** + * + * $Id$ + */ + +#include <xpcom-config.h> +#include "config.h" + +#include <stdlib.h> + +#include <glib/gi18n.h> + +#include <nsStringGlue.h> + +#include <nsComponentManagerUtils.h> +#include <nsICancelable.h> +#include <nsIChannel.h> +#include <nsIDOMDocument.h> +#include <nsIFileURL.h> +#include <nsIIOService.h> +#include <nsILocalFile.h> +#include <nsIMIMEInfo.h> +#include <nsIMIMEService.h> +#include <nsIObserver.h> +#include <nsIRequest.h> +#include <nsIURI.h> +#include <nsIWritablePropertyBag2.h> +#include <nsIWebBrowserPersist.h> + +#include <nsMemory.h> +#include <nsNetError.h> +#include <nsServiceManagerUtils.h> + +#include "EphyBadCertRejector.h" +#include "EphyUtils.h" + +#include "eel-gconf-extensions.h" +#include "ephy-debug.h" +#include "ephy-file-helpers.h" +#include "ephy-prefs.h" +#include "mozilla-download.h" + +#include "MozDownload.h" + +/* Minimum time between progress updates */ +#define PROGRESS_RATE 500000 /* microsec */ + +const char* const persistContractID = "@mozilla.org/embedding/browser/nsWebBrowserPersist;1"; + +MozDownload::MozDownload() : + mTotalProgress(-1), + mCurrentProgress(0), + mMaxSize(-1), + mAddToRecent(PR_TRUE), + mStatus(NS_OK), + mEmbedPersist(nsnull), + mDownloadState(EPHY_DOWNLOAD_INITIALISING) +{ + LOG ("MozDownload ctor (%p)", (void *) this); +} + +MozDownload::~MozDownload() +{ + LOG ("MozDownload dtor (%p)", (void *) this); + + NS_ASSERTION (!mEphyDownload, "MozillaDownload still alive!"); +} + +NS_IMPL_ISUPPORTS4 (MozDownload, + nsIWebProgressListener, + nsIWebProgressListener2, + nsITransfer, + nsIInterfaceRequestor) + +nsresult +MozDownload::InitForEmbed (nsIURI *aSource, nsIURI *aTarget, const nsAString &aDisplayName, + nsIMIMEInfo *aMIMEInfo, PRTime aStartTime, nsILocalFile *aTempFile, + nsICancelable *aCancelable, MozillaEmbedPersist *aEmbedPersist, + PRInt64 aMaxSize) +{ + mEmbedPersist = aEmbedPersist; + mMaxSize = aMaxSize; + return Init (aSource, aTarget, aDisplayName, aMIMEInfo, aStartTime, aTempFile, aCancelable); +} + +/* void init (in nsIURI aSource, in nsIURI aTarget, in AString aDisplayName, in nsIMIMEInfo aMIMEInfo, in PRTime startTime, in nsILocalFile aTempFile, in nsICancelable aCancelable); */ +NS_IMETHODIMP +MozDownload::Init (nsIURI *aSource, + nsIURI *aTarget, + const nsAString &aDisplayName, + nsIMIMEInfo *aMIMEInfo, + PRTime aStartTime, + nsILocalFile *aTempFile, + nsICancelable *aCancelable) +{ + PRBool addToView = PR_TRUE; + + if (mEmbedPersist) + { + EphyEmbedPersistFlags flags; + + flags = ephy_embed_persist_get_flags (EPHY_EMBED_PERSIST (mEmbedPersist)); + + addToView = !(flags & EPHY_EMBED_PERSIST_NO_VIEW); + } + + mSource = aSource; + mDestination = aTarget; + mStartTime = aStartTime; + mTotalProgress = 0; + mCurrentProgress = 0; + mPercentComplete = 0; + mInterval = PROGRESS_RATE; + mLastUpdate = mStartTime; + mMIMEInfo = aMIMEInfo; + mAddToRecent = addToView; + + /* This will create a refcount cycle, which needs to be broken in ::OnStateChange */ + mCancelable = aCancelable; + + if (addToView) + { + DownloaderView *dview; + dview = EPHY_DOWNLOADER_VIEW + (ephy_embed_shell_get_downloader_view (embed_shell)); + mEphyDownload = mozilla_download_new (this); + g_object_add_weak_pointer (G_OBJECT (mEphyDownload), + (gpointer *) &mEphyDownload); + downloader_view_add_download (dview, mEphyDownload); + g_object_unref (mEphyDownload); + } + else + { + mEphyDownload = nsnull; + } + + return NS_OK; +} + +NS_IMETHODIMP +MozDownload::GetSource(nsIURI **aSource) +{ + NS_ENSURE_ARG_POINTER(aSource); + NS_IF_ADDREF(*aSource = mSource); + + return NS_OK; +} + +NS_IMETHODIMP +MozDownload::GetTargetFile (nsILocalFile** aTargetFile) +{ + nsresult rv; + + nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mDestination, &rv); + if (NS_FAILED(rv)) return rv; + + nsCOMPtr<nsIFile> file; + rv = fileURL->GetFile(getter_AddRefs(file)); + if (NS_SUCCEEDED(rv)) + rv = CallQueryInterface(file, aTargetFile); + return rv; +} + +NS_IMETHODIMP +MozDownload::GetPercentComplete(PRInt32 *aPercentComplete) +{ + NS_ENSURE_ARG_POINTER(aPercentComplete); + *aPercentComplete = mPercentComplete; + + return NS_OK; +} + +NS_IMETHODIMP +MozDownload::GetTotalProgress(PRInt64 *aTotalProgress) +{ + NS_ENSURE_ARG_POINTER(aTotalProgress); + *aTotalProgress = mTotalProgress; + + return NS_OK; +} + +NS_IMETHODIMP +MozDownload::GetCurrentProgress(PRInt64 *aCurrentProgress) +{ + NS_ENSURE_ARG_POINTER(aCurrentProgress); + *aCurrentProgress = mCurrentProgress; + + return NS_OK; +} + +NS_IMETHODIMP +MozDownload::GetState(EphyDownloadState *aDownloadState) +{ + NS_ENSURE_ARG_POINTER(aDownloadState); + *aDownloadState = mDownloadState; + + return NS_OK; +} + +NS_IMETHODIMP +MozDownload::GetElapsedTime(PRInt64 *aElapsedTime) +{ + NS_ENSURE_ARG_POINTER(aElapsedTime); + *aElapsedTime = PR_Now() - mStartTime; + + return NS_OK; +} + +NS_IMETHODIMP +MozDownload::GetMIMEInfo(nsIMIMEInfo **aMIMEInfo) +{ + NS_ENSURE_ARG_POINTER(aMIMEInfo); + NS_IF_ADDREF(*aMIMEInfo = mMIMEInfo); + + return NS_OK; +} + +NS_IMETHODIMP +MozDownload::OnStateChange (nsIWebProgress *aWebProgress, nsIRequest *aRequest, + PRUint32 aStateFlags, nsresult aStatus) +{ + nsresult rv; + + if (NS_FAILED(aStatus) && NS_SUCCEEDED(mStatus)) + mStatus = aStatus; + + if (aStateFlags & STATE_START) + { + mDownloadState = EPHY_DOWNLOAD_DOWNLOADING; + + if (mEphyDownload) + { + g_signal_emit_by_name (mEphyDownload, "changed"); + } + } + + /* We will get this even in the event of a cancel */ + /* Due to a mozilla bug [https://bugzilla.mozilla.org/show_bug.cgi?id=304353], + * we'll only get STATE_STOP if we're driven from external app handler; elsewhere + * we get STATE_STOP | STATE_IS_NETWORK | STATE_IS_REQUEST. So check first if + * STATE_IS_REQUEST is set. + */ + /* Be careful that download is only completed when STATE_IS_NETWORK is set + * and many lonely STOP events may be triggered before. + */ +#ifdef GNOME_ENABLE_DEBUG +{ + nsCString spec; + if (mSource) mSource->GetSpec(spec); + + LOG ("url %s, status %x, state %x (is-stop:%s, is-network:%s, is-request:%s)", + spec.get(), aStatus, aStateFlags, + aStateFlags & STATE_STOP ? "t" : "f", + aStateFlags & STATE_IS_NETWORK ? "t" : "f", + aStateFlags & STATE_IS_REQUEST ? "t" : "f"); +} +#endif + + if (((aStateFlags & STATE_IS_REQUEST) && + (aStateFlags & STATE_IS_NETWORK) && + (aStateFlags & STATE_STOP)) || + aStateFlags == STATE_STOP) + { + LOG ("STATE_STOP"); + + /* Keep us alive */ + nsCOMPtr<nsITransfer> kungFuDeathGrip(this); + + mDownloadState = NS_SUCCEEDED (aStatus) ? EPHY_DOWNLOAD_COMPLETED : EPHY_DOWNLOAD_FAILED; + if (mEphyDownload) + { + g_signal_emit_by_name (mEphyDownload, "changed"); + } + + /* break refcount cycle */ + mCancelable = nsnull; + + nsCString destSpec; + nsCString mimeType; + + mDestination->GetSpec (destSpec); + + if (NS_SUCCEEDED (aStatus) && mMIMEInfo) + { + rv = mMIMEInfo->GetMIMEType (mimeType); + NS_ENSURE_SUCCESS (rv, NS_ERROR_FAILURE); + } + + if (mAddToRecent) + { + ephy_file_add_recent_item (destSpec.get(), mimeType.get()); + } + + if (mEmbedPersist) + { + if (NS_SUCCEEDED (aStatus)) + { + mozilla_embed_persist_completed (mEmbedPersist); + } + else + { + mozilla_embed_persist_cancelled (mEmbedPersist); + } + } + else if (NS_SUCCEEDED (aStatus)) + { + /* see http://bugzilla.gnome.org/show_bug.cgi?id=456945 */ +#if 1 //def HAVE_GECKO_1_9 + // FIXMEchpe fix this! + return NS_OK; +#else + GnomeVFSMimeApplication *helperApp; + NS_ENSURE_TRUE (mMIMEInfo, NS_ERROR_FAILURE); + + nsString description; + mMIMEInfo->GetApplicationDescription (description); + + nsCString cDesc; + NS_UTF16ToCString (description, NS_CSTRING_ENCODING_UTF8, cDesc); + + /* HACK we use the application description to decide + if we have to open the saved file */ + if (g_str_has_prefix (cDesc.get(), "gnome-default:")) + { + /* Format gnome-default:<usertime>:<helperapp id> */ + char **str = g_strsplit (cDesc.get(), ":", -1); + g_return_val_if_fail (g_strv_length (str) == 3, NS_ERROR_FAILURE); + + char *end; + guint32 user_time = strtoul (str[1], &end, 0); + + helperApp = gnome_vfs_mime_application_new_from_desktop_id (str[2]); + if (!helperApp) return NS_ERROR_FAILURE; + + nsCString aDest; + rv = mDestination->GetSpec (aDest); + NS_ENSURE_SUCCESS (rv, NS_ERROR_FAILURE); + + ephy_file_launch_application (helperApp, destSpec.get (), user_time); + + gnome_vfs_mime_application_free (helperApp); + g_strfreev (str); + } + else if (g_str_has_prefix (cDesc.get(), "gnome-browse-to-file:")) + { + /* Format gnome-browse-to-file:<usertime> */ + char **str = g_strsplit (cDesc.get(), ":", -1); + g_return_val_if_fail (g_strv_length (str) == 2, NS_ERROR_FAILURE); + + char *end; + guint32 user_time = strtoul (str[1], &end, 0); + + nsCString aDest; + rv = mDestination->GetSpec (aDest); + NS_ENSURE_SUCCESS (rv, NS_ERROR_FAILURE); + + ephy_file_browse_to (aDest.get (), user_time); + + g_strfreev (str); + } +#endif /* HAVE_GECKO_1_9 */ + } + } + + return NS_OK; +} + +NS_IMETHODIMP +MozDownload::OnProgressChange (nsIWebProgress *aWebProgress, + nsIRequest *aRequest, + PRInt32 aCurSelfProgress, + PRInt32 aMaxSelfProgress, + PRInt32 aCurTotalProgress, + PRInt32 aMaxTotalProgress) +{ + return OnProgressChange64 (aWebProgress, aRequest, + aCurSelfProgress, aMaxSelfProgress, + aCurTotalProgress, aMaxTotalProgress); +} + +/* void onProgressChange64 (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in long long aCurSelfProgress, in long long aMaxSelfProgress, in long long aCurTotalProgress, + in long long aMaxTotalProgress); */ +NS_IMETHODIMP +MozDownload::OnProgressChange64 (nsIWebProgress *aWebProgress, + nsIRequest *aRequest, + PRInt64 aCurSelfProgress, + PRInt64 aMaxSelfProgress, + PRInt64 aCurTotalProgress, + PRInt64 aMaxTotalProgress) +{ + if (mMaxSize >= 0 && + ((aMaxTotalProgress > 0 && mMaxSize < aMaxTotalProgress) || + mMaxSize < aCurTotalProgress)) + { + Cancel (); + } + + if (!mRequest) + mRequest = aRequest; + + PRInt64 now = PR_Now (); + + if ((now - mLastUpdate < mInterval) && + (aMaxTotalProgress == -1 || aCurTotalProgress < aMaxTotalProgress)) + return NS_OK; + + mLastUpdate = now; + + if (aMaxTotalProgress <= 0) + { + mPercentComplete = -1; + } + else + { + /* Make sure not to round up, so we don't display 100% unless + * it's really finished! + */ + mPercentComplete = (PRInt32)(((float)aCurTotalProgress / (float)aMaxTotalProgress) * 100.0); + } + + mTotalProgress = aMaxTotalProgress; + mCurrentProgress = aCurTotalProgress; + + if (mEphyDownload) + { + g_signal_emit_by_name (mEphyDownload, "changed"); + } + + return NS_OK; +} + +/* boolean onRefreshAttempted (in nsIWebProgress aWebProgress, in nsIURI aRefreshURI, in long aDelay, in boolean aSameURI); */ +NS_IMETHODIMP +MozDownload::OnRefreshAttempted(nsIWebProgress *aWebProgress, + nsIURI *aUri, + PRInt32 aDelay, + PRBool aSameUri, + PRBool *allowRefresh) +{ + *allowRefresh = PR_TRUE; + return NS_OK; +} + +NS_IMETHODIMP +MozDownload::OnLocationChange (nsIWebProgress *aWebProgress, nsIRequest *aRequest, nsIURI *location) +{ + return NS_OK; +} + +NS_IMETHODIMP +MozDownload::OnStatusChange (nsIWebProgress *aWebProgress, nsIRequest *aRequest, + nsresult aStatus, const PRUnichar *aMessage) +{ + return NS_OK; +} + +NS_IMETHODIMP +MozDownload::OnSecurityChange (nsIWebProgress *aWebProgress, nsIRequest *aRequest, PRUint32 state) +{ + return NS_OK; +} + +/* void getInterface (in nsIIDRef uuid, [iid_is (uuid), retval] out nsQIResult result); */ +NS_IMETHODIMP +MozDownload::GetInterface(const nsIID & uuid, void * *result) +{ + if (uuid.Equals (NS_GET_IID (nsIBadCertListener)) && + mEmbedPersist) + { + EphyEmbedPersistFlags flags; + + g_object_get (mEmbedPersist, "flags", &flags, (char *) NULL); + + if (flags & EPHY_EMBED_PERSIST_NO_CERTDIALOGS) + { + nsIBadCertListener *badCertRejector = new EphyBadCertRejector (); + if (!badCertRejector) return NS_ERROR_OUT_OF_MEMORY; + + *result = badCertRejector; + NS_ADDREF (badCertRejector); + + return NS_OK; + } + } + + return NS_ERROR_NO_INTERFACE; +} + +void +MozDownload::Cancel() +{ + if (mDownloadState != EPHY_DOWNLOAD_DOWNLOADING && + mDownloadState != EPHY_DOWNLOAD_PAUSED) + { + return; + } + + if (mCancelable) + { + /* FIXME: error code? */ + mCancelable->Cancel (NS_BINDING_ABORTED); + } +} + +void +MozDownload::Pause() +{ + if (mRequest) + { + mRequest->Suspend (); + mDownloadState = EPHY_DOWNLOAD_PAUSED; + } +} + +void +MozDownload::Resume() +{ + if (mRequest) + { + mRequest->Resume (); + mDownloadState = EPHY_DOWNLOAD_DOWNLOADING; + } +} + +nsresult InitiateMozillaDownload (nsIDOMDocument *domDocument, nsIURI *sourceURI, + nsILocalFile* inDestFile, const char *contentType, + nsIURI* inOriginalURI, MozillaEmbedPersist *embedPersist, + nsIInputStream *postData, nsISupports *aCacheKey, + PRInt64 aMaxSize) +{ + nsresult rv = NS_OK; + + EphyEmbedPersistFlags ephy_flags; + ephy_flags = ephy_embed_persist_get_flags (EPHY_EMBED_PERSIST (embedPersist)); + + if (!ephy_embed_persist_get_dest (EPHY_EMBED_PERSIST (embedPersist))) + { + nsCString cPath; + inDestFile->GetNativePath (cPath); + + ephy_embed_persist_set_dest (EPHY_EMBED_PERSIST (embedPersist), + cPath.get()); + } + + nsCOMPtr<nsIMIMEService> mimeService (do_GetService ("@mozilla.org/mime;1")); + nsCOMPtr<nsIMIMEInfo> mimeInfo; + if (mimeService) + { + mimeService->GetFromTypeAndExtension (nsCString(contentType), + nsCString(), + getter_AddRefs (mimeInfo)); + } + + PRBool isHTML = (contentType && + (strcmp (contentType, "text/html") == 0 || + strcmp (contentType, "text/xml") == 0 || + strcmp (contentType, "application/xhtml+xml") == 0)); + + nsCOMPtr<nsIWebBrowserPersist> webPersist (do_CreateInstance(persistContractID, &rv)); + NS_ENSURE_SUCCESS (rv, rv); + + PRInt64 timeNow = PR_Now(); + + nsString fileDisplayName; + inDestFile->GetLeafName(fileDisplayName); + + nsCOMPtr<nsIIOService> ioService; + rv = EphyUtils::GetIOService (getter_AddRefs (ioService)); + NS_ENSURE_SUCCESS (rv, rv); + + nsCOMPtr<nsIURI> destURI; + ioService->NewFileURI (inDestFile, getter_AddRefs(destURI)); + + MozDownload *downloader = new MozDownload (); + /* dlListener attaches to its progress dialog here, which gains ownership */ + /* FIXME is that still true? */ + rv = downloader->InitForEmbed (inOriginalURI, destURI, fileDisplayName, + mimeInfo, timeNow, nsnull, webPersist, embedPersist, aMaxSize); + NS_ENSURE_SUCCESS (rv, rv); + + rv = webPersist->SetProgressListener (downloader); + NS_ENSURE_SUCCESS (rv, rv); + + PRInt32 flags = nsIWebBrowserPersist::PERSIST_FLAGS_REPLACE_EXISTING_FILES; + + if (!domDocument && !isHTML && !(ephy_flags & EPHY_EMBED_PERSIST_COPY_PAGE) && + !(ephy_flags & EPHY_EMBED_PERSIST_DO_CONVERSION)) + { + flags |= nsIWebBrowserPersist::PERSIST_FLAGS_NO_CONVERSION; + } + if (ephy_flags & EPHY_EMBED_PERSIST_COPY_PAGE) + { + flags |= nsIWebBrowserPersist::PERSIST_FLAGS_FROM_CACHE; + } + webPersist->SetPersistFlags(flags); + + /* Create a new tagged channel if we need to block cookies from server */ + if (ephy_flags & EPHY_EMBED_PERSIST_NO_COOKIES) + { + nsCOMPtr<nsIChannel> tmpChannel; + rv = ioService->NewChannelFromURI (sourceURI, getter_AddRefs (tmpChannel)); + NS_ENSURE_SUCCESS (rv, rv); + + nsCOMPtr<nsIWritablePropertyBag2> props = do_QueryInterface(tmpChannel); + rv = props->SetPropertyAsBool (NS_LITERAL_STRING("epiphany-blocking-cookies"), PR_TRUE); + NS_ENSURE_SUCCESS (rv, rv); + + rv = webPersist->SaveChannel (tmpChannel, inDestFile); + } + else if (!domDocument || !isHTML || ephy_flags & EPHY_EMBED_PERSIST_COPY_PAGE) + { + rv = webPersist->SaveURI (sourceURI, aCacheKey, nsnull, + postData, nsnull, inDestFile); + } + else + { + PRInt32 encodingFlags = 0; + nsCOMPtr<nsILocalFile> filesFolder; + + /** + * Construct a directory path to hold the associated files; mozilla + * will create the directory as needed. + */ + + nsCString cPath; + inDestFile->GetNativePath (cPath); + + char *basename = g_path_get_basename (cPath.get()); + char *dirname = g_path_get_dirname (cPath.get()); + char *dot_pos = strchr (basename, '.'); + if (dot_pos) + { + *dot_pos = 0; + } + /* translators: this is the directory name to store auxilary files when saving html files */ + char *new_basename = g_strdup_printf (_("%s Files"), basename); + char *new_path = g_build_filename (dirname, new_basename, NULL); + g_free (new_basename); + g_free (basename); + g_free (dirname); + + filesFolder = do_CreateInstance ("@mozilla.org/file/local;1"); + filesFolder->InitWithNativePath (nsCString(new_path)); + + g_free (new_path); + + rv = webPersist->SaveDocument (domDocument, inDestFile, filesFolder, + contentType, encodingFlags, 80); + } + + return rv; +} + +static char* +GetFilePath (const char *filename) +{ + const char *home_dir; + char *download_dir, *path; + + download_dir = ephy_file_get_downloads_dir (); + + if (ephy_ensure_dir_exists (download_dir, NULL)) + { + path = g_build_filename (download_dir, filename, (char *) NULL); + } + else + { + home_dir = g_get_home_dir (); + path = g_build_filename (home_dir ? home_dir : "/", filename, (char *) NULL); + } + g_free (download_dir); + + return path; +} + +static const char* +file_is_compressed (const char *filename) +{ + int i; + static const char * const compression[] = {".gz", ".bz2", ".Z", ".lz", NULL}; + + for (i = 0; compression[i] != NULL; i++) + { + if (g_str_has_suffix (filename, compression[i])) + return compression[i]; + } + + return NULL; +} + +static const char* +parse_extension (const char *filename) +{ + const char *compression; + + compression = file_is_compressed (filename); + + /* If the file is compressed we might have a double extension */ + if (compression != NULL) + { + int i; + static const char * const extensions[] = {"tar", "ps", "xcf", "dvi", "txt", "text", NULL}; + + for (i = 0; extensions[i] != NULL; i++) + { + char *suffix; + suffix = g_strdup_printf (".%s%s", extensions[i], + compression); + + if (g_str_has_suffix (filename, suffix)) + { + char *p; + + p = g_strrstr (filename, suffix); + g_free (suffix); + + return p; + } + + g_free (suffix); + } + } + + /* default case */ + return g_strrstr (filename, "."); +} + +nsresult BuildDownloadPath (const char *defaultFileName, nsILocalFile **_retval) +{ + char *path; + + path = GetFilePath (defaultFileName); + + if (g_file_test (path, G_FILE_TEST_EXISTS)) + { + int i = 1; + const char *dot_pos; + char *serial = NULL; + GString *tmp_path; + gssize position; + + dot_pos = parse_extension (defaultFileName); + if (dot_pos) + { + position = dot_pos - defaultFileName; + } + else + { + position = strlen (defaultFileName); + } + tmp_path = g_string_new (NULL); + + do { + g_free (path); + g_string_assign (tmp_path, defaultFileName); + serial = g_strdup_printf ("(%d)", i++); + g_string_insert (tmp_path, position, serial); + g_free (serial); + path = GetFilePath (tmp_path->str); + + } while (g_file_test (path, G_FILE_TEST_EXISTS)); + + g_string_free (tmp_path, TRUE); + } + + nsCOMPtr <nsILocalFile> destFile (do_CreateInstance(NS_LOCAL_FILE_CONTRACTID)); + NS_ENSURE_TRUE (destFile, NS_ERROR_FAILURE); + + destFile->InitWithNativePath (nsCString (path)); + g_free (path); + + NS_IF_ADDREF (*_retval = destFile); + return NS_OK; +} |