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:
gavin@gavinsharp.com 2007-08-22 14:55:57 -07:00
Родитель a7a2fe9ba2
Коммит 173a3cb7dc
6 изменённых файлов: 571 добавлений и 111 удалений

Просмотреть файл

@ -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