зеркало из https://github.com/mozilla/pjs.git
Bug 103487: use default anti-virus scanner to scan downloaded files on Windows, patch by Rob Arnold <robarnold@mozilla.com>, r=sdwilsh, r=jmathies, r=me
This commit is contained in:
Родитель
a7a2fe9ba2
Коммит
173a3cb7dc
|
@ -55,13 +55,14 @@ interface mozIStorageConnection;
|
|||
interface nsIDownloadManager : nsISupports {
|
||||
// Download States
|
||||
const short DOWNLOAD_NOTSTARTED = -1;
|
||||
const short DOWNLOAD_QUEUED = 5;
|
||||
const short DOWNLOAD_DOWNLOADING = 0;
|
||||
const short DOWNLOAD_FINISHED = 1;
|
||||
const short DOWNLOAD_FAILED = 2;
|
||||
const short DOWNLOAD_CANCELED = 3;
|
||||
const short DOWNLOAD_PAUSED = 4;
|
||||
const short DOWNLOAD_QUEUED = 5;
|
||||
const short DOWNLOAD_BLOCKED = 6;
|
||||
const short DOWNLOAD_SCANNING = 7;
|
||||
|
||||
const short DOWNLOAD_TYPE_DOWNLOAD = 0;
|
||||
|
||||
|
|
|
@ -65,12 +65,17 @@ REQUIRES = xpcom \
|
|||
embed_base \
|
||||
alerts \
|
||||
storage \
|
||||
xulapp \
|
||||
$(NULL)
|
||||
|
||||
CPPSRCS = \
|
||||
nsDownloadManager.cpp \
|
||||
$(NULL)
|
||||
|
||||
ifeq ($(OS_ARCH),WINNT)
|
||||
CPPSRCS += nsDownloadScanner.cpp
|
||||
endif
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
EXTRA_DSO_LDOPTS += $(MOZ_COMPONENT_LIBS)
|
||||
|
|
|
@ -72,6 +72,7 @@
|
|||
|
||||
#ifdef XP_WIN
|
||||
#include <shlobj.h>
|
||||
#include "nsDownloadScanner.h"
|
||||
#endif
|
||||
|
||||
static PRBool gStoppingDownloads = PR_FALSE;
|
||||
|
@ -122,6 +123,9 @@ nsDownloadManager::GetSingleton()
|
|||
|
||||
nsDownloadManager::~nsDownloadManager()
|
||||
{
|
||||
#ifdef XP_WIN
|
||||
delete mScanner;
|
||||
#endif
|
||||
gDownloadManagerService = nsnull;
|
||||
}
|
||||
|
||||
|
@ -141,28 +145,13 @@ nsDownloadManager::CancelAllDownloads()
|
|||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDownloadManager::FinishDownload(nsDownload *aDownload, DownloadState aState,
|
||||
const char *aTopic) {
|
||||
// We don't want to lose access to the download's member variables
|
||||
nsRefPtr<nsDownload> kungFuDeathGrip = aDownload;
|
||||
|
||||
void
|
||||
nsDownloadManager::CompleteDownload(nsDownload *aDownload)
|
||||
{
|
||||
// we've stopped, so break the cycle we created at download start
|
||||
aDownload->mCancelable = nsnull;
|
||||
|
||||
// This has to be done in this exact order to not mess up our invariants
|
||||
// 1) when the state changed listener is dispatched, it must no longer be
|
||||
// an active download.
|
||||
// 2) when the observer is dispatched, the same conditions for 1 must be
|
||||
// true as well as the state being up to date.
|
||||
(void)mCurrentDownloads.RemoveObject(aDownload);
|
||||
|
||||
nsresult rv = aDownload->SetState(aState);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
(void)mObserverService->NotifyObservers(aDownload, aTopic, nsnull);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -573,6 +562,17 @@ nsDownloadManager::Init()
|
|||
getter_AddRefs(mBundle));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
#ifdef XP_WIN
|
||||
mScanner = new nsDownloadScanner();
|
||||
if (!mScanner)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
rv = mScanner->Init();
|
||||
if (NS_FAILED(rv)) {
|
||||
delete mScanner;
|
||||
mScanner = nsnull;
|
||||
}
|
||||
#endif
|
||||
|
||||
rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
|
||||
"UPDATE moz_downloads "
|
||||
"SET startTime = ?1, endTime = ?2, state = ?3, referrer = ?4 "
|
||||
|
@ -691,6 +691,12 @@ nsDownloadManager::GetDownloadFromDB(PRUint32 aID, nsDownload **retVal)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsDownloadManager::SendEvent(nsDownload *aDownload, const char *aTopic)
|
||||
{
|
||||
(void)mObserverService->NotifyObservers(aDownload, aTopic, nsnull);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//// nsIDownloadManager
|
||||
|
||||
|
@ -1010,8 +1016,7 @@ nsDownloadManager::CancelDownload(PRUint32 aID)
|
|||
dl->mTempFile->Remove(PR_FALSE);
|
||||
}
|
||||
|
||||
nsresult rv = FinishDownload(dl, nsIDownloadManager::DOWNLOAD_CANCELED,
|
||||
"dl-cancel");
|
||||
nsresult rv = dl->SetState(nsIDownloadManager::DOWNLOAD_CANCELED);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
|
@ -1507,13 +1512,150 @@ nsDownload::SetState(DownloadState aState)
|
|||
PRInt16 oldState = mDownloadState;
|
||||
mDownloadState = aState;
|
||||
|
||||
nsresult rv;
|
||||
|
||||
nsCOMPtr<nsIPrefBranch> pref = do_GetService(NS_PREFSERVICE_CONTRACTID);
|
||||
|
||||
// We don't want to lose access to our member variables
|
||||
nsRefPtr<nsDownload> kungFuDeathGrip = this;
|
||||
|
||||
// When the state changed listener is dispatched, queries to the database and
|
||||
// the download manager api should reflect what the nsIDownload object would
|
||||
// return. So, if a download is done (finished, canceled, etc.), it should
|
||||
// first be removed from the current downloads. We will also have to update
|
||||
// the database *before* notifying listeners. At this point, you can safely
|
||||
// dispatch to the observers as well.
|
||||
switch (aState) {
|
||||
case nsIDownloadManager::DOWNLOAD_BLOCKED:
|
||||
case nsIDownloadManager::DOWNLOAD_CANCELED:
|
||||
mDownloadManager->CompleteDownload(this);
|
||||
break;
|
||||
#ifdef XP_WIN
|
||||
case nsIDownloadManager::DOWNLOAD_SCANNING:
|
||||
{
|
||||
nsresult rv = mDownloadManager->mScanner ? mDownloadManager->mScanner->ScanDownload(this) : NS_ERROR_NOT_INITIALIZED;
|
||||
// If we failed, then fall through to 'download finished'
|
||||
if (NS_SUCCEEDED(rv))
|
||||
break;
|
||||
mDownloadState = aState = nsIDownloadManager::DOWNLOAD_FINISHED;
|
||||
}
|
||||
#endif
|
||||
case nsIDownloadManager::DOWNLOAD_FINISHED:
|
||||
{
|
||||
mDownloadManager->CompleteDownload(this);
|
||||
|
||||
// Master pref to control this function.
|
||||
PRBool showTaskbarAlert = PR_TRUE;
|
||||
if (pref)
|
||||
pref->GetBoolPref(PREF_BDM_SHOWALERTONCOMPLETE, &showTaskbarAlert);
|
||||
|
||||
if (showTaskbarAlert) {
|
||||
PRInt32 alertInterval = 2000;
|
||||
if (pref)
|
||||
pref->GetIntPref(PREF_BDM_SHOWALERTINTERVAL, &alertInterval);
|
||||
|
||||
PRInt64 alertIntervalUSec = alertInterval * PR_USEC_PER_MSEC;
|
||||
PRInt64 goat = PR_Now() - mStartTime;
|
||||
showTaskbarAlert = goat > alertIntervalUSec;
|
||||
|
||||
PRInt32 size = mDownloadManager->mCurrentDownloads.Count();
|
||||
if (showTaskbarAlert && size == 0) {
|
||||
nsCOMPtr<nsIAlertsService> alerts =
|
||||
do_GetService("@mozilla.org/alerts-service;1");
|
||||
if (alerts) {
|
||||
nsXPIDLString title, message;
|
||||
|
||||
mDownloadManager->mBundle->GetStringFromName(
|
||||
NS_LITERAL_STRING("downloadsCompleteTitle").get(),
|
||||
getter_Copies(title));
|
||||
mDownloadManager->mBundle->GetStringFromName(
|
||||
NS_LITERAL_STRING("downloadsCompleteMsg").get(),
|
||||
getter_Copies(message));
|
||||
|
||||
PRBool removeWhenDone =
|
||||
mDownloadManager->GetRetentionBehavior() == 0;
|
||||
|
||||
|
||||
// If downloads are automatically removed per the user's
|
||||
// retention policy, there's no reason to make the text clickable
|
||||
// because if it is, they'll click open the download manager and
|
||||
// the items they downloaded will have been removed.
|
||||
alerts->ShowAlertNotification(
|
||||
NS_LITERAL_STRING(DOWNLOAD_MANAGER_ALERT_ICON), title,
|
||||
message, !removeWhenDone, EmptyString(), mDownloadManager);
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef XP_WIN
|
||||
PRBool addToRecentDocs = PR_TRUE;
|
||||
if (pref)
|
||||
pref->GetBoolPref(PREF_BDM_ADDTORECENTDOCS, &addToRecentDocs);
|
||||
|
||||
if (addToRecentDocs) {
|
||||
LPSHELLFOLDER lpShellFolder = NULL;
|
||||
|
||||
if (SUCCEEDED(::SHGetDesktopFolder(&lpShellFolder))) {
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mTarget, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIFile> file;
|
||||
rv = fileURL->GetFile(getter_AddRefs(file));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoString path;
|
||||
rv = file->GetPath(path);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRUnichar *filePath = ToNewUnicode(path);
|
||||
LPITEMIDLIST lpItemIDList = NULL;
|
||||
if (SUCCEEDED(lpShellFolder->ParseDisplayName(NULL, NULL, filePath,
|
||||
NULL, &lpItemIDList, NULL))) {
|
||||
::SHAddToRecentDocs(SHARD_PIDL, lpItemIDList);
|
||||
::CoTaskMemFree(lpItemIDList);
|
||||
}
|
||||
nsMemory::Free(filePath);
|
||||
lpShellFolder->Release();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Now remove the download if the user's retention policy is "Remove when Done"
|
||||
if (mDownloadManager->GetRetentionBehavior() == 0)
|
||||
mDownloadManager->RemoveDownload(mID);
|
||||
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Before notifying the listener, we must update the database so that calls
|
||||
// to it work out properly.
|
||||
nsresult rv = UpdateDB();
|
||||
rv = UpdateDB();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
||||
mDownloadManager->NotifyListenersOnDownloadStateChange(oldState, this);
|
||||
|
||||
switch (mDownloadState) {
|
||||
case nsIDownloadManager::DOWNLOAD_DOWNLOADING:
|
||||
mDownloadManager->SendEvent(this, "dl-start");
|
||||
break;
|
||||
case nsIDownloadManager::DOWNLOAD_FAILED:
|
||||
mDownloadManager->SendEvent(this, "dl-failed");
|
||||
break;
|
||||
case nsIDownloadManager::DOWNLOAD_SCANNING:
|
||||
mDownloadManager->SendEvent(this, "dl-scanning");
|
||||
break;
|
||||
case nsIDownloadManager::DOWNLOAD_FINISHED:
|
||||
mDownloadManager->SendEvent(this, "dl-done");
|
||||
break;
|
||||
case nsIDownloadManager::DOWNLOAD_BLOCKED:
|
||||
mDownloadManager->SendEvent(this, "dl-blocked");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1577,7 +1719,6 @@ nsDownload::OnProgressChange64(nsIWebProgress *aWebProgress,
|
|||
// Update the state and the database
|
||||
rv = SetState(nsIDownloadManager::DOWNLOAD_DOWNLOADING);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
mDownloadManager->mObserverService->NotifyObservers(this, "dl-start", nsnull);
|
||||
}
|
||||
|
||||
// filter notifications since they come in so frequently
|
||||
|
@ -1661,9 +1802,7 @@ nsDownload::OnStatusChange(nsIWebProgress *aWebProgress,
|
|||
// We don't want to lose access to our member variables
|
||||
nsRefPtr<nsDownload> kungFuDeathGrip = this;
|
||||
|
||||
(void)mDownloadManager->FinishDownload(this,
|
||||
nsIDownloadManager::DOWNLOAD_FAILED,
|
||||
"dl-failed");
|
||||
(void)SetState(nsIDownloadManager::DOWNLOAD_FAILED);
|
||||
|
||||
// Get title for alert.
|
||||
nsXPIDLString title;
|
||||
|
@ -1705,7 +1844,6 @@ nsDownload::OnStateChange(nsIWebProgress* aWebProgress,
|
|||
|
||||
// We need to update mDownloadState before updating the dialog, because
|
||||
// that will close and call CancelDownload if it was the last open window.
|
||||
nsCOMPtr<nsIPrefBranch> pref = do_GetService(NS_PREFSERVICE_CONTRACTID);
|
||||
|
||||
if (aStateFlags & STATE_START) {
|
||||
nsresult rv;
|
||||
|
@ -1721,9 +1859,7 @@ nsDownload::OnStateChange(nsIWebProgress* aWebProgress,
|
|||
(void)mCancelable->Cancel(NS_BINDING_ABORTED);
|
||||
|
||||
// Fail the download - DOWNLOAD_BLOCKED
|
||||
mDownloadManager->FinishDownload(this,
|
||||
nsIDownloadManager::DOWNLOAD_BLOCKED,
|
||||
"dl-blocked");
|
||||
(void)SetState(nsIDownloadManager::DOWNLOAD_BLOCKED);
|
||||
|
||||
mDownloadManager->NotifyListenersOnStateChange(aWebProgress, aRequest,
|
||||
aStateFlags, aStatus, this);
|
||||
|
@ -1733,7 +1869,7 @@ nsDownload::OnStateChange(nsIWebProgress* aWebProgress,
|
|||
}
|
||||
} else if (aStateFlags & STATE_STOP) {
|
||||
if (nsDownloadManager::IsInFinalStage(mDownloadState)) {
|
||||
// Set file size at the end of a tranfer (for unknown transfer amounts)
|
||||
// Set file size at the end of a transfer (for unknown transfer amounts)
|
||||
if (mMaxBytes == LL_MAXUINT)
|
||||
mMaxBytes = mCurrBytes;
|
||||
|
||||
|
@ -1745,88 +1881,17 @@ nsDownload::OnStateChange(nsIWebProgress* aWebProgress,
|
|||
|
||||
mPercentComplete = 100;
|
||||
|
||||
(void)mDownloadManager->FinishDownload(this,
|
||||
nsIDownloadManager::DOWNLOAD_FINISHED,
|
||||
"dl-done");
|
||||
|
||||
// Master pref to control this function.
|
||||
PRBool showTaskbarAlert = PR_TRUE;
|
||||
if (pref)
|
||||
pref->GetBoolPref(PREF_BDM_SHOWALERTONCOMPLETE, &showTaskbarAlert);
|
||||
|
||||
if (showTaskbarAlert) {
|
||||
PRInt32 alertInterval = -1;
|
||||
pref->GetIntPref(PREF_BDM_SHOWALERTINTERVAL, &alertInterval);
|
||||
|
||||
PRInt64 alertIntervalUSec = alertInterval * PR_USEC_PER_MSEC;
|
||||
PRInt64 goat = PR_Now() - mStartTime;
|
||||
showTaskbarAlert = goat > alertIntervalUSec;
|
||||
|
||||
PRInt32 size = mDownloadManager->mCurrentDownloads.Count();
|
||||
if (showTaskbarAlert && size == 0) {
|
||||
nsCOMPtr<nsIAlertsService> alerts =
|
||||
do_GetService("@mozilla.org/alerts-service;1");
|
||||
if (alerts) {
|
||||
nsXPIDLString title, message;
|
||||
|
||||
mDownloadManager->mBundle->GetStringFromName(NS_LITERAL_STRING("downloadsCompleteTitle").get(), getter_Copies(title));
|
||||
mDownloadManager->mBundle->GetStringFromName(NS_LITERAL_STRING("downloadsCompleteMsg").get(), getter_Copies(message));
|
||||
|
||||
PRBool removeWhenDone = mDownloadManager->GetRetentionBehavior() == 0;
|
||||
|
||||
|
||||
// If downloads are automatically removed per the user's retention policy,
|
||||
// there's no reason to make the text clickable because if it is, they'll
|
||||
// click open the download manager and the items they downloaded will have
|
||||
// been removed.
|
||||
alerts->ShowAlertNotification(NS_LITERAL_STRING(DOWNLOAD_MANAGER_ALERT_ICON), title, message, !removeWhenDone,
|
||||
EmptyString(), mDownloadManager);
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef XP_WIN
|
||||
PRBool addToRecentDocs = PR_TRUE;
|
||||
if (pref)
|
||||
pref->GetBoolPref(PREF_BDM_ADDTORECENTDOCS, &addToRecentDocs);
|
||||
|
||||
if (addToRecentDocs) {
|
||||
LPSHELLFOLDER lpShellFolder = NULL;
|
||||
|
||||
if (SUCCEEDED(::SHGetDesktopFolder(&lpShellFolder))) {
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mTarget, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIFile> file;
|
||||
rv = fileURL->GetFile(getter_AddRefs(file));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsAutoString path;
|
||||
rv = file->GetPath(path);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRUnichar *filePath = ToNewUnicode(path);
|
||||
LPITEMIDLIST lpItemIDList = NULL;
|
||||
if (SUCCEEDED(lpShellFolder->ParseDisplayName(NULL, NULL, filePath, NULL, &lpItemIDList, NULL))) {
|
||||
::SHAddToRecentDocs(SHARD_PIDL, lpItemIDList);
|
||||
::CoTaskMemFree(lpItemIDList);
|
||||
}
|
||||
nsMemory::Free(filePath);
|
||||
lpShellFolder->Release();
|
||||
}
|
||||
}
|
||||
(void)SetState(nsIDownloadManager::DOWNLOAD_SCANNING);
|
||||
#else
|
||||
(void)SetState(nsIDownloadManager::DOWNLOAD_FINISHED);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Now remove the download if the user's retention policy is "Remove when Done"
|
||||
if (mDownloadManager->GetRetentionBehavior() == 0)
|
||||
mDownloadManager->RemoveDownload(mID);
|
||||
}
|
||||
|
||||
mDownloadManager->NotifyListenersOnStateChange(aWebProgress, aRequest,
|
||||
aStateFlags, aStatus, this);
|
||||
|
||||
return UpdateDB();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -71,6 +71,10 @@ typedef PRInt16 DownloadType;
|
|||
|
||||
class nsDownload;
|
||||
|
||||
#ifdef XP_WIN
|
||||
class nsDownloadScanner;
|
||||
#endif
|
||||
|
||||
class nsDownloadManager : public nsIDownloadManager,
|
||||
public nsIObserver
|
||||
{
|
||||
|
@ -84,6 +88,11 @@ public:
|
|||
static nsDownloadManager *GetSingleton();
|
||||
|
||||
virtual ~nsDownloadManager();
|
||||
#ifdef XP_WIN
|
||||
nsDownloadManager() : mScanner(nsnull) { };
|
||||
private:
|
||||
nsDownloadScanner *mScanner;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
struct TimerParams {
|
||||
|
@ -103,6 +112,8 @@ protected:
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void SendEvent(nsDownload *aDownload, const char *aTopic);
|
||||
|
||||
|
||||
/**
|
||||
* Adds a download with the specified information to the DB.
|
||||
|
@ -136,14 +147,12 @@ protected:
|
|||
nsresult CancelAllDownloads();
|
||||
|
||||
/**
|
||||
* Removes download from "current downloads," updates download state, and
|
||||
* notifies observers.
|
||||
* Removes download from "current downloads".
|
||||
*
|
||||
* This method removes the cycle created when starting the download, so
|
||||
* make sure to use kungFuDeathGrip if you want to access member variables
|
||||
*/
|
||||
nsresult FinishDownload(nsDownload *aDownload, DownloadState aState,
|
||||
const char *aTopic);
|
||||
void CompleteDownload(nsDownload *aDownload);
|
||||
|
||||
void ConfirmCancelDownloads(PRInt32 aCount,
|
||||
nsISupportsPRBool* aCancelDownloads,
|
||||
|
|
|
@ -0,0 +1,313 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: se cin sw=2 ts=2 et : */
|
||||
/* ***** 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 download manager code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Corporation
|
||||
*
|
||||
* Contributor(s):
|
||||
* Rob Arnold <robarnold@mozilla.com> (Original Author)
|
||||
*
|
||||
* 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 ***** */
|
||||
|
||||
#include "nsDownloadScanner.h"
|
||||
#include <comcat.h>
|
||||
#include <process.h>
|
||||
#include "nsDownloadManager.h"
|
||||
#include "nsIXULAppInfo.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
#include "nsIPrefService.h"
|
||||
#include "nsNetUtil.h"
|
||||
|
||||
/**
|
||||
* Code overview
|
||||
*
|
||||
* Antivirus vendors on Windows can implement the IOfficeAntiVirus interface
|
||||
* so that other programs (not just Office) can use their functionality.
|
||||
* According to the Microsoft documentation, this interface requires only
|
||||
* Windows 95/NT 4 and IE 5 so it is used in preference to IAttachmentExecute
|
||||
* which requires XP SP2 (Windows 2000 is still supported at this time).
|
||||
*
|
||||
* The interface is rather simple; it provides a Scan method which takes a
|
||||
* small structure describing what to scan. Unfortunately, the method is
|
||||
* synchronous and could take a while, so it is not a good idea to call it from
|
||||
* the main thread. Some antivirus scanners can take a long time to scan or the
|
||||
* call might block while the scanner shows its UI so if the user were to
|
||||
* download many files that finished around the same time, they would have to
|
||||
* wait a while if the scanning were done on exactly one other thread. Since
|
||||
* the overhead of creating a thread is relatively small compared to the time
|
||||
* it takes to download a file and scan it, a new thread is spawned for each
|
||||
* download that is to be scanned. Since most of the mozilla codebase is not
|
||||
* threadsafe, all the information needed for the scanner is gathered in the
|
||||
* main thread in nsDownloadScanner::Scan::Start. The only function of
|
||||
* nsDownloadScanner::Scan which is invoked on another thread is DoScan.
|
||||
*
|
||||
* There are 4 possible outcomes of the virus scan:
|
||||
* AVSCAN_GOOD => the file is clean
|
||||
* AVSCAN_BAD => the file has a virus
|
||||
* AVSCAN_UGLY => the file had a virus, but it was cleaned
|
||||
* AVSCAN_FAILED => something else went wrong with the virus scanner.
|
||||
*
|
||||
* Both the good and ugly states leave the user with a benign file, so they
|
||||
* transition to the finished state. Bad files are sent to the blocked state.
|
||||
* Failed states transition to finished downloads.
|
||||
*
|
||||
* Possible Future enhancements:
|
||||
* * Use all available virus scanners instead of just the first one that is
|
||||
* enumerated (or use some heuristic)
|
||||
* * Create an interface for scanning files in general
|
||||
* * Make this a service
|
||||
* * Get antivirus scanner status via WMI/registry
|
||||
*/
|
||||
|
||||
#define PREF_BDA_DONTCLEAN "browser.download.antivirus.dontclean"
|
||||
|
||||
nsDownloadScanner::nsDownloadScanner()
|
||||
: mHaveAVScanner(PR_FALSE)
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDownloadScanner::Init()
|
||||
{
|
||||
// This CoInitialize/CoUninitialize pattern seems to be common in the Mozilla
|
||||
// codebase. All other COM calls/objects are made on different threads.
|
||||
nsresult rv = NS_OK;
|
||||
CoInitialize(NULL);
|
||||
if (FindCLSID() < 0)
|
||||
rv = NS_ERROR_NOT_AVAILABLE;
|
||||
CoUninitialize();
|
||||
return rv;
|
||||
}
|
||||
|
||||
PRInt32
|
||||
nsDownloadScanner::FindCLSID()
|
||||
{
|
||||
nsRefPtr<ICatInformation> catInfo;
|
||||
HRESULT hr;
|
||||
hr = CoCreateInstance(CLSID_StdComponentCategoriesMgr, NULL, CLSCTX_INPROC,
|
||||
IID_ICatInformation, getter_AddRefs(catInfo));
|
||||
if (FAILED(hr)) {
|
||||
NS_WARNING("Could not create category information class\n");
|
||||
return -1;
|
||||
}
|
||||
nsRefPtr<IEnumCLSID> clsidEnumerator;
|
||||
GUID guids [1] = { CATID_MSOfficeAntiVirus };
|
||||
hr = catInfo->EnumClassesOfCategories(1, guids, 0, NULL,
|
||||
getter_AddRefs(clsidEnumerator));
|
||||
if (FAILED(hr)) {
|
||||
NS_WARNING("Could not get class enumerator for category\n");
|
||||
return -2;
|
||||
}
|
||||
ULONG nReceived;
|
||||
clsidEnumerator->Next(1, &mScannerCLSID, &nReceived);
|
||||
if (nReceived == 0) {
|
||||
// No installed Anti Virus program
|
||||
return -3;
|
||||
}
|
||||
mHaveAVScanner = PR_TRUE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int __stdcall
|
||||
nsDownloadScanner::ScannerThreadFunction(void *p)
|
||||
{
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Antivirus scan should not be run on the main thread");
|
||||
nsDownloadScanner::Scan *scan = static_cast<nsDownloadScanner::Scan*>(p);
|
||||
scan->DoScan();
|
||||
_endthreadex(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
nsDownloadScanner::Scan::Scan(nsDownloadScanner *scanner, nsDownload *download)
|
||||
: mDLScanner(scanner), mAVScanner(NULL), mThread(NULL),
|
||||
mDownload(download), mStatus(AVSCAN_NOTSTARTED)
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDownloadScanner::Scan::Start()
|
||||
{
|
||||
mThread = (HANDLE)_beginthreadex(NULL, 0, ScannerThreadFunction,
|
||||
this, CREATE_SUSPENDED, NULL);
|
||||
if (!mThread)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
// Default is to try to clean downloads
|
||||
mIsReadOnlyRequest = PR_FALSE;
|
||||
|
||||
nsCOMPtr<nsIPrefBranch> pref =
|
||||
do_GetService(NS_PREFSERVICE_CONTRACTID);
|
||||
if (pref)
|
||||
rv = pref->GetBoolPref(PREF_BDA_DONTCLEAN, &mIsReadOnlyRequest);
|
||||
|
||||
// Get the path to the file on disk
|
||||
nsCOMPtr<nsILocalFile> file;
|
||||
rv = mDownload->GetTargetFile(getter_AddRefs(file));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = file->GetPath(mPath);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
||||
// Grab the app name
|
||||
nsCOMPtr<nsIXULAppInfo> appinfo =
|
||||
do_GetService(XULAPPINFO_SERVICE_CONTRACTID, &rv);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCAutoString name;
|
||||
rv = appinfo->GetName(name);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
CopyUTF8toUTF16(name, mName);
|
||||
|
||||
|
||||
// Get the origin
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
rv = mDownload->GetSource(getter_AddRefs(uri));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCAutoString origin;
|
||||
rv = uri->GetSpec(origin);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
CopyUTF8toUTF16(origin, mOrigin);
|
||||
|
||||
|
||||
// We count https/ftp/http as an http download
|
||||
PRBool isHttp(PR_FALSE), isFtp(PR_FALSE), isHttps(PR_FALSE);
|
||||
nsCOMPtr<nsIURI> innerURI = NS_GetInnermostURI(uri);
|
||||
(void)innerURI->SchemeIs("http", &isHttp);
|
||||
(void)innerURI->SchemeIs("ftp", &isFtp);
|
||||
(void)innerURI->SchemeIs("https", &isHttps);
|
||||
mIsHttpDownload = isHttp || isFtp || isHttps;
|
||||
|
||||
// ResumeThread returns the previous suspend count
|
||||
if (1 != ::ResumeThread(mThread)) {
|
||||
CloseHandle(mThread);
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDownloadScanner::Scan::Run()
|
||||
{
|
||||
// Cleanup our thread
|
||||
WaitForSingleObject(mThread, INFINITE);
|
||||
CloseHandle(mThread);
|
||||
|
||||
DownloadState downloadState = 0;
|
||||
switch (mStatus) {
|
||||
case AVSCAN_BAD:
|
||||
downloadState = nsIDownloadManager::DOWNLOAD_BLOCKED;
|
||||
break;
|
||||
default:
|
||||
case AVSCAN_FAILED:
|
||||
case AVSCAN_GOOD:
|
||||
case AVSCAN_UGLY:
|
||||
downloadState = nsIDownloadManager::DOWNLOAD_FINISHED;
|
||||
break;
|
||||
}
|
||||
(void)mDownload->SetState(downloadState);
|
||||
|
||||
NS_RELEASE_THIS();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsDownloadScanner::Scan::DoScan()
|
||||
{
|
||||
HRESULT hr;
|
||||
MSOAVINFO info;
|
||||
info.cbsize = sizeof(MSOAVINFO);
|
||||
info.fPath = TRUE;
|
||||
info.fInstalled = FALSE;
|
||||
info.fReadOnlyRequest = mIsReadOnlyRequest;
|
||||
info.fHttpDownload = mIsHttpDownload;
|
||||
info.hwnd = NULL;
|
||||
|
||||
info.pwzHostName = mName.BeginWriting();
|
||||
info.u.pwzFullPath = mPath.BeginWriting();
|
||||
|
||||
info.pwzOrigURL = mOrigin.BeginWriting();
|
||||
|
||||
CoInitialize(NULL);
|
||||
hr = CoCreateInstance(mDLScanner->mScannerCLSID, NULL, CLSCTX_ALL,
|
||||
IID_IOfficeAntiVirus, getter_AddRefs(mAVScanner));
|
||||
if (FAILED(hr)) {
|
||||
NS_WARNING("Could not instantiate antivirus scanner");
|
||||
mStatus = AVSCAN_FAILED;
|
||||
} else {
|
||||
mStatus = AVSCAN_SCANNING;
|
||||
hr = mAVScanner->Scan(&info);
|
||||
switch (hr) {
|
||||
case S_OK:
|
||||
mStatus = AVSCAN_GOOD;
|
||||
break;
|
||||
case S_FALSE:
|
||||
mStatus = AVSCAN_UGLY;
|
||||
break;
|
||||
case E_FAIL:
|
||||
mStatus = AVSCAN_BAD;
|
||||
break;
|
||||
default:
|
||||
case ERROR_FILE_NOT_FOUND:
|
||||
NS_WARNING("Downloaded file disappeared before it could be scanned");
|
||||
mStatus = AVSCAN_FAILED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
CoUninitialize();
|
||||
|
||||
// We need to do a few more things on the main thread
|
||||
NS_DispatchToMainThread(this);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDownloadScanner::ScanDownload(nsDownload *download)
|
||||
{
|
||||
if (!mHaveAVScanner)
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
// No ref ptr, see comment below
|
||||
Scan *scan = new Scan(this, download);
|
||||
if (!scan)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
NS_ADDREF(scan);
|
||||
|
||||
nsresult rv = scan->Start();
|
||||
|
||||
// Note that we only release upon error. On success, the scan is passed off
|
||||
// to a new thread. It is eventually released in Scan::Run on the main thread.
|
||||
if (NS_FAILED(rv))
|
||||
NS_RELEASE(scan);
|
||||
|
||||
return rv;
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: se cin sw=2 ts=2 et : */
|
||||
#ifndef nsDownloadScanner_h_
|
||||
#define nsDownloadScanner_h_
|
||||
|
||||
#ifdef WIN32_LEAN_AND_MEAN
|
||||
#undef WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#define INITGUID
|
||||
#include <Windows.h>
|
||||
#define AVVENDOR
|
||||
#include <msoav.h>
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsDownloadManager.h"
|
||||
|
||||
enum AVScanState
|
||||
{
|
||||
AVSCAN_NOTSTARTED = 0,
|
||||
AVSCAN_SCANNING,
|
||||
AVSCAN_GOOD,
|
||||
AVSCAN_BAD,
|
||||
AVSCAN_UGLY,
|
||||
AVSCAN_FAILED
|
||||
};
|
||||
|
||||
class nsDownloadScanner
|
||||
{
|
||||
public:
|
||||
nsDownloadScanner();
|
||||
nsresult Init();
|
||||
nsresult ScanDownload(nsDownload *download);
|
||||
|
||||
private:
|
||||
PRBool mHaveAVScanner;
|
||||
CLSID mScannerCLSID;
|
||||
PRInt32 FindCLSID();
|
||||
|
||||
static unsigned int __stdcall ScannerThreadFunction(void *p);
|
||||
class Scan : public nsRunnable
|
||||
{
|
||||
public:
|
||||
Scan(nsDownloadScanner *scanner, nsDownload *download);
|
||||
nsresult Start();
|
||||
|
||||
private:
|
||||
nsDownloadScanner *mDLScanner;
|
||||
nsRefPtr<IOfficeAntiVirus> mAVScanner;
|
||||
HANDLE mThread;
|
||||
nsRefPtr<nsDownload> mDownload;
|
||||
AVScanState mStatus;
|
||||
nsString mPath;
|
||||
nsString mName;
|
||||
nsString mOrigin;
|
||||
// Also true if it is an ftp download
|
||||
PRBool mIsHttpDownload;
|
||||
PRBool mIsReadOnlyRequest;
|
||||
|
||||
NS_IMETHOD Run();
|
||||
|
||||
void DoScan();
|
||||
|
||||
friend unsigned int __stdcall nsDownloadScanner::ScannerThreadFunction(void *);
|
||||
};
|
||||
};
|
||||
#endif
|
Загрузка…
Ссылка в новой задаче