Fix for bug 91407 r=ddrinan, sr=tor

Make downloading a CA cert mime type blob smart enough
to handle blobs with more than one cert.
This commit is contained in:
javi%netscape.com 2001-08-22 04:05:45 +00:00
Родитель 1c6ede373a
Коммит f0d4abd132
6 изменённых файлов: 335 добавлений и 174 удалений

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

@ -124,16 +124,18 @@ interface nsIX509CertDB : nsISupports {
out nsISupportsArray certs);
/*
* importCertificate
*
* Import a certificate into the database.
* XXX For now, this only imports into the default internal slot.
* should there be a importCertificateToSlot? Or change the sig?
* importCertificates
* Use this to import a stream sent down as a mime type into
* the default cert db. The stream may consist of one or more
* certificates.
* XXX We may want to add a parameter for a PK11 Token where
* the certs will utlimtately live. Currently, they'll
* be placed in the default token.
*/
[noscript] void importCertificate(in nsIX509Cert cert,
in unsigned long type,
in unsigned long trust,
in wstring nickname);
[noscript] void importCertificates(in charPtr data,
in unsigned long length,
in unsigned long type,
in nsIInterfaceRequestor ctx);
/*
* importCertificate

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

@ -129,6 +129,7 @@ PKCS12UnknownErrRestore=Failed to restore the PKCS #12 file for unknown reasons.
PKCS12UnknownErrBackup=Failed to create the PKCS #12 backup file for unknown reasons.
PKCS12UnknownErr=The PKCS #12 operation failed for unknown reasons.
PKCS12InfoNoSmartcardBackup=It is not possible to back up certificates from a hardware security device such as a smart card.
PKCS12DupData=The certificate and private key already exist on the security device.
AddModulePrompt=Are you sure you want to install this security module?
AddModuleName=Module Name: %S
AddModulePath=Path: %S

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

@ -32,7 +32,7 @@
* may use your version of this file under either the MPL or the
* GPL.
*
* $Id: nsNSSCertificate.cpp,v 1.46 2001/08/21 01:12:38 rangansen%netscape.com Exp $
* $Id: nsNSSCertificate.cpp,v 1.47 2001/08/22 04:05:42 javi%netscape.com Exp $
*/
#include "prmem.h"
@ -2517,64 +2517,269 @@ nsNSSCertificateDB::GetCertsByType(PRUint32 aType,
return PR_TRUE;
}
SECStatus PR_CALLBACK
collect_certs(void *arg, SECItem **certs, int numcerts)
{
CERTDERCerts *collectArgs;
SECItem *cert;
SECStatus rv;
collectArgs = (CERTDERCerts *)arg;
collectArgs->numcerts = numcerts;
collectArgs->rawCerts = (SECItem *) PORT_ArenaZAlloc(collectArgs->arena,
sizeof(SECItem) * numcerts);
if ( collectArgs->rawCerts == NULL )
return(SECFailure);
cert = collectArgs->rawCerts;
while ( numcerts-- ) {
rv = SECITEM_CopyItem(collectArgs->arena, cert, *certs);
if ( rv == SECFailure )
return(SECFailure);
cert++;
certs++;
}
return (SECSuccess);
}
CERTDERCerts*
nsNSSCertificateDB::getCertsFromPackage(PRArenaPool *arena, char *data,
PRUint32 length)
{
CERTDERCerts *collectArgs =
(CERTDERCerts *)PORT_ArenaZAlloc(arena, sizeof(CERTDERCerts));
if ( collectArgs == nsnull )
return nsnull;
collectArgs->arena = arena;
SECStatus sec_rv = CERT_DecodeCertPackage(data, length, collect_certs,
(void *)collectArgs);
if (sec_rv != SECSuccess)
return nsnull;
return collectArgs;
}
nsresult
nsNSSCertificateDB::handleCACertDownload(nsISupportsArray *x509Certs,
nsIInterfaceRequestor *ctx)
{
// First thing we have to do is figure out which certificate we're
// gonna present to the user. The CA may have sent down a list of
// certs which may or may not be a chained list of certs. Until
// the day we can design some solid UI for the general case, we'll
// code to the > 90% case. That case is where a CA sends down a
// list that is a chain up to its root in either ascending or
// descending order. What we're gonna do is compare the first
// 2 entries, if the first was signed by the second, we assume
// the leaf cert is the first cert and display it. If the second
// cert was signed by the first cert, then we assume the first cert
// is the root and the last cert in the array is the leaf. In this
// case we display the last cert.
PRUint32 numCerts;
x509Certs->Count(&numCerts);
NS_ASSERTION(numCerts > 0, "Didn't get any certs to import.");
if (numCerts == 0)
return NS_OK; // Nothing to import, so nothing to do.
nsCOMPtr<nsIX509Cert> certToShow;
nsCOMPtr<nsISupports> isupports;
PRUint32 selCertIndex;
if (numCerts == 1) {
// There's only one cert, so let's show it.
selCertIndex = 0;
isupports = dont_AddRef(x509Certs->ElementAt(selCertIndex));
certToShow = do_QueryInterface(isupports);
} else {
nsCOMPtr<nsIX509Cert> cert0;
nsCOMPtr<nsIX509Cert> cert1;
isupports = dont_AddRef(x509Certs->ElementAt(0));
cert0 = do_QueryInterface(isupports);
isupports = dont_AddRef(x509Certs->ElementAt(1));
cert1 = do_QueryInterface(isupports);
nsXPIDLString cert0SubjectName;
nsXPIDLString cert0IssuerName;
nsXPIDLString cert1SubjectName;
nsXPIDLString cert1IssuerName;
cert0->GetIssuerName(getter_Copies(cert0IssuerName));
cert0->GetSubjectName(getter_Copies(cert0SubjectName));
cert1->GetIssuerName(getter_Copies(cert1IssuerName));
cert1->GetSubjectName(getter_Copies(cert1SubjectName));
if (nsCRT::strcmp(cert1IssuerName.get(), cert0SubjectName.get()) == 0) {
// In this case, the first cert in the list signed the second,
// so the first cert is the root. Let's display the last cert
// in the list.
selCertIndex = numCerts-1;
isupports = dont_AddRef(x509Certs->ElementAt(selCertIndex));
certToShow = do_QueryInterface(isupports);
} else
if (nsCRT::strcmp(cert0IssuerName.get(), cert1SubjectName.get()) == 0) {
// In this case the second cert has signed the first cert. The
// first cert is the leaf, so let's display it.
selCertIndex = 0;
certToShow = cert0;
} else {
// It's not a chain, so let's just show the first one in the
// downloaded list.
selCertIndex = 0;
certToShow = cert0;
}
}
if (!certToShow)
return NS_ERROR_FAILURE;
nsCOMPtr<nsICertificateDialogs> dialogs;
nsresult rv = ::getNSSDialogs(getter_AddRefs(dialogs),
NS_GET_IID(nsICertificateDialogs));
if (NS_FAILED(rv))
return rv;
SECItem der;
rv=certToShow->GetRawDER((char **)&der.data, &der.len);
if (NS_FAILED(rv))
return rv;
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Creating temp cert\n"));
CERTCertificate *tmpCert;
CERTCertDBHandle *certdb = CERT_GetDefaultCertDB();
tmpCert = CERT_NewTempCertificate(certdb, &der,
nsnull, PR_FALSE, PR_TRUE);
if (!tmpCert) {
NS_ASSERTION(0,"Couldn't create cert from DER blob\n");
return NS_ERROR_FAILURE;
}
PRBool canceled;
if (tmpCert->isperm) {
dialogs->CACertExists(ctx, &canceled);
return NS_ERROR_FAILURE;
}
PRUint32 trustBits;
rv = dialogs->DownloadCACert(ctx, certToShow, &trustBits, &canceled);
if (NS_FAILED(rv))
return rv;
if (canceled)
return NS_ERROR_NOT_AVAILABLE;
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("trust is %d\n", trustBits));
nsXPIDLCString nickname;
nickname.Adopt(CERT_MakeCANickname(tmpCert));
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Created nick \"%s\"\n", nickname.get()));
nsNSSCertTrust trust;
trust.SetValidCA();
trust.AddCATrust(trustBits & nsIX509CertDB::TRUSTED_SSL,
trustBits & nsIX509CertDB::TRUSTED_EMAIL,
trustBits & nsIX509CertDB::TRUSTED_OBJSIGN);
SECStatus srv = CERT_AddTempCertToPerm(tmpCert,
NS_CONST_CAST(char*,nickname.get()),
trust.GetTrust());
if (srv != SECSuccess)
return NS_ERROR_FAILURE;
// Now it's time to add the rest of the certs we just downloaded.
// Since we didn't prompt the user about any of these certs, we
// won't set any trust bits for them.
nsNSSCertTrust defaultTrust;
defaultTrust.SetValidCA();
defaultTrust.AddCATrust(0,0,0);
for (PRUint32 i=0; i<numCerts; i++) {
if (i == selCertIndex)
continue;
isupports = dont_AddRef(x509Certs->ElementAt(i));
certToShow = do_QueryInterface(isupports);
certToShow->GetRawDER((char **)&der.data, &der.len);
tmpCert = CERT_NewTempCertificate(certdb, &der,
nsnull, PR_FALSE, PR_TRUE);
if (!tmpCert) {
NS_ASSERTION(0, "Couldn't create temp cert from DER blob\n");
continue; // Let's try to import the rest of 'em
}
nickname.Adopt(CERT_MakeCANickname(tmpCert));
CERT_AddTempCertToPerm(tmpCert, NS_CONST_CAST(char*,nickname.get()),
defaultTrust.GetTrust());
CERT_DestroyCertificate(tmpCert);
}
return NS_OK;
}
/*
* [noscript] void importCertificate (in nsIX509Cert cert,
* in unsigned long type,
* in unsigned long trust,
* in wchar tokenName);
* [noscript] void importCertificates(in charPtr data, in unsigned long length,
* in unsigned long type,
* in nsIInterfaceRequestor ctx);
*/
NS_IMETHODIMP
nsNSSCertificateDB::ImportCertificate(nsIX509Cert *cert,
PRUint32 type,
PRUint32 trusted,
const PRUnichar *nickname)
nsNSSCertificateDB::ImportCertificates(char * data, PRUint32 length,
PRUint32 type,
nsIInterfaceRequestor *ctx)
{
SECStatus srv = SECFailure;
nsresult nsrv;
CERTCertificate *tmpCert = NULL;
nsNSSCertTrust trust;
char *nick;
SECItem der;
PRArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (!arena)
return NS_ERROR_OUT_OF_MEMORY;
CERTDERCerts *certCollection = getCertsFromPackage(arena, data, length);
if (!certCollection) {
PORT_FreeArena(arena, PR_FALSE);
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsISupportsArray> array;
nsresult rv = NS_NewISupportsArray(getter_AddRefs(array));
if (NS_FAILED(rv)) {
PORT_FreeArena(arena, PR_FALSE);
return rv;
}
// Now let's create some certs to work with
nsCOMPtr<nsIX509Cert> x509Cert;
nsNSSCertificate *nssCert;
SECItem *currItem;
for (int i=0; i<certCollection->numcerts; i++) {
currItem = &certCollection->rawCerts[i];
nssCert = new nsNSSCertificate((char*)currItem->data, currItem->len);
if (!nssCert)
return NS_ERROR_OUT_OF_MEMORY;
x509Cert = do_QueryInterface(nssCert);
array->AppendElement(x509Cert);
}
switch (type) {
case nsIX509Cert::CA_CERT:
trust.SetValidCA();
trust.AddCATrust(trusted & nsIX509CertDB::TRUSTED_SSL,
trusted & nsIX509CertDB::TRUSTED_EMAIL,
trusted & nsIX509CertDB::TRUSTED_OBJSIGN);
nsrv = handleCACertDownload(array, ctx);
break;
default:
return NS_ERROR_NOT_IMPLEMENTED;
}
nsrv = cert->GetRawDER((char **)&der.data, &der.len);
if (nsrv != NS_OK)
return NS_ERROR_FAILURE;
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Creating temp cert\n"));
tmpCert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &der,
NULL, PR_FALSE, PR_TRUE);
if (!tmpCert) goto done;
if (nickname) {
nick = NS_CONST_CAST(char*, NS_ConvertUCS2toUTF8(nickname).get());
} else {
nick = CERT_MakeCANickname(tmpCert);
}
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Created nick \"%s\"\n", nick));
/* XXX check to see if cert is perm (it shouldn't be, but NSS asserts if it is */
/* XXX this is an ugly peek into NSS */
//Check moved to PSMContentDownloader::OnStopRequest in nsNSSComponent.cpp
//so that the user can be informed before downloading, if cert exists
/*
if (tmpCert->isperm) {
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Cert was already in db %s\n", nick));
return NS_ERROR_FAILURE;
}
*/
srv = CERT_AddTempCertToPerm(tmpCert, nick, trust.GetTrust());
done:
if (tmpCert)
CERT_DestroyCertificate(tmpCert);
return (srv) ? NS_ERROR_FAILURE : NS_OK;
// We only deal with import CA certs in this method currently.
nsrv = NS_ERROR_FAILURE;
break;
}
PORT_FreeArena(arena, PR_FALSE);
if (srv != SECSuccess && nsrv == NS_OK)
nsrv = NS_ERROR_FAILURE;
return nsrv;
}
char *
@ -2714,32 +2919,7 @@ done:
PR_FREEIF(tmp);
return(nickname);
}
static SECStatus PR_CALLBACK
collect_certs(void *arg, SECItem **certs, int numcerts)
{
CERTDERCerts *collectArgs;
SECItem *cert;
SECStatus rv;
collectArgs = (CERTDERCerts *)arg;
collectArgs->numcerts = numcerts;
collectArgs->rawCerts = (SECItem *) PORT_ArenaZAlloc(collectArgs->arena,
sizeof(SECItem) * numcerts);
if ( collectArgs->rawCerts == NULL )
return(SECFailure);
cert = collectArgs->rawCerts;
while ( numcerts-- ) {
rv = SECITEM_CopyItem(collectArgs->arena, cert, *certs);
if ( rv == SECFailure )
return(SECFailure);
cert++;
certs++;
}
return (SECSuccess);
}
NS_IMETHODIMP
nsNSSCertificateDB::ImportUserCertificate(char *data, PRUint32 length, nsIInterfaceRequestor *ctx)
@ -2748,23 +2928,17 @@ nsNSSCertificateDB::ImportUserCertificate(char *data, PRUint32 length, nsIInterf
char * nickname = NULL;
SECStatus sec_rv;
int numCACerts;
SECItem *CACerts;
CERTDERCerts * collectArgs;
PRArenaPool *arena;
CERTCertificate * cert=NULL;
SECItem *CACerts;
CERTDERCerts * collectArgs;
PRArenaPool *arena;
CERTCertificate * cert=NULL;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if ( arena == NULL )
goto loser;
collectArgs = (CERTDERCerts *)PORT_ArenaZAlloc(arena, sizeof(CERTDERCerts));
if ( collectArgs == NULL )
goto loser;
collectArgs->arena = arena;
sec_rv = CERT_DecodeCertPackage(data, length, collect_certs,
(void *)collectArgs);
if (sec_rv != SECSuccess)
collectArgs = getCertsFromPackage(arena, data, length);
if (!collectArgs)
goto loser;
cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), collectArgs->rawCerts,

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

@ -77,6 +77,7 @@ private:
PRUint32 *_verified,
PRUint32 *_count,
PRUnichar **tmpUsages);
};
class nsNSSCertificateDB : public nsIX509CertDB
@ -97,6 +98,10 @@ private:
PRUint32 *_count,
PRUnichar ***_certNameList);
CERTDERCerts *getCertsFromPackage(PRArenaPool *arena, char *data,
PRUint32 length);
nsresult handleCACertDownload(nsISupportsArray *x509Certs,
nsIInterfaceRequestor *ctx);
};
// Use this function to generate a default nickname for a user

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

@ -1069,61 +1069,20 @@ PSMContentDownloader::OnStopRequest(nsIRequest* request,
nsCOMPtr<nsIX509CertDB> certdb = do_GetService(NS_X509CERTDB_CONTRACTID);
nsresult rv;
SECItem der;
CERTCertificate *tmpCert = NULL;
nsCOMPtr<nsIInterfaceRequestor> ctx = new PSMContentDownloaderContext();
switch (mType) {
case PSMContentDownloader::X509_CA_CERT:
{
nsCOMPtr<nsIX509Cert> cert = new nsNSSCertificate(mByteData, mBufferOffset);
if (certdb == nsnull)
return NS_ERROR_FAILURE;
nsCOMPtr<nsICertificateDialogs> dialogs;
PRBool canceled;
PRUint32 trust;
rv = ::getNSSDialogs(getter_AddRefs(dialogs),
NS_GET_IID(nsICertificateDialogs));
if (NS_FAILED(rv)) goto loser;
rv=cert->GetRawDER((char **)&der.data, &der.len);
if (rv != NS_OK)
return NS_ERROR_FAILURE;
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Creating temp cert\n"));
tmpCert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &der,
NULL, PR_FALSE, PR_TRUE);
//Added to check if cert exists
if (tmpCert->isperm)
{
dialogs->CACertExists(ctx, &canceled);
rv = NS_ERROR_FAILURE;
break;
}
//end added
rv = dialogs->DownloadCACert(ctx, cert, &trust, &canceled);
if (NS_FAILED(rv)) goto loser;
if (canceled) { rv = NS_ERROR_NOT_AVAILABLE; goto loser; }
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("trust is %d\n", trust));
return certdb->ImportCertificate(cert, mType, trust, nsnull);
}
return certdb->ImportCertificates(mByteData, mBufferOffset, mType, ctx);
case PSMContentDownloader::X509_USER_CERT:
return certdb->ImportUserCertificate(mByteData, mBufferOffset, ctx);
break;
case PSMContentDownloader::PKCS7_CRL:
return certdb->ImportCrl(mByteData, mBufferOffset, mURI, SEC_CRL_TYPE);
default:
rv = NS_ERROR_FAILURE;
break;
rv = NS_ERROR_FAILURE;
break;
}
loser:
if (tmpCert)
CERT_DestroyCertificate(tmpCert);
return rv;
}

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

@ -31,7 +31,7 @@
* may use your version of this file under either the MPL or the
* GPL.
*
* $Id: nsPKCS12Blob.cpp,v 1.18 2001/08/15 01:34:36 javi%netscape.com Exp $
* $Id: nsPKCS12Blob.cpp,v 1.19 2001/08/22 04:05:45 javi%netscape.com Exp $
*/
#include "prmem.h"
@ -74,6 +74,7 @@ static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
#define PIP_PKCS12_NOSMARTCARD_EXPORT 4
#define PIP_PKCS12_RESTORE_FAILED 5
#define PIP_PKCS12_BACKUP_FAILED 6
#define PIP_PKCS12_NSS_ERROR 7
// constructor
nsPKCS12Blob::nsPKCS12Blob():mCertArray(0),
@ -182,7 +183,12 @@ nsPKCS12Blob::ImportFromFile(nsILocalFile *file)
// Later - check to see if this should become default email cert
handleError(PIP_PKCS12_RESTORE_OK);
finish:
if (NS_FAILED(rv) || srv != SECSuccess) {
// If srv != SECSuccess, NSS probably set a specific error code.
// We should use that error code instead of inventing a new one
// for every error possible.
if (srv != SECSuccess) {
handleError(PIP_PKCS12_NSS_ERROR);
} else if (NS_FAILED(rv)) {
handleError(PIP_PKCS12_RESTORE_FAILED);
}
// finish the decoder
@ -726,53 +732,67 @@ nsPKCS12Blob::handleError(int myerr)
if (NS_FAILED(rv)) return rv;
errPrompt->Alert(nsnull, errorMsg.get());
return PR_TRUE;
case 0:
default:
break;
}
switch (prerr) {
// The following errors have the potential to be "handled", by asking
// the user (via a dialog) whether s/he wishes to continue
case 0: break;
case SEC_ERROR_PKCS12_CERT_COLLISION:
/* pop a dialog saying the cert is already in the database */
/* ask to keep going? what happens if one collision but others ok? */
// The following errors cannot be "handled", notify the user (via an alert)
// that the operation failed.
case PIP_PKCS12_NSS_ERROR:
switch (prerr) {
// The following errors have the potential to be "handled", by asking
// the user (via a dialog) whether s/he wishes to continue
case 0: break;
case SEC_ERROR_PKCS12_CERT_COLLISION:
/* pop a dialog saying the cert is already in the database */
/* ask to keep going? what happens if one collision but others ok? */
// The following errors cannot be "handled", notify the user (via an alert)
// that the operation failed.
#if 0
// XXX a boy can dream...
// but the PKCS12 lib never throws this error
// but then again, how would it? anyway, convey the info below
case SEC_ERROR_PKCS12_PRIVACY_PASSWORD_INCORRECT:
rv = nssComponent->GetPIPNSSBundleString(
// XXX a boy can dream...
// but the PKCS12 lib never throws this error
// but then again, how would it? anyway, convey the info below
case SEC_ERROR_PKCS12_PRIVACY_PASSWORD_INCORRECT:
rv = nssComponent->GetPIPNSSBundleString(
NS_LITERAL_STRING("PKCS12PasswordInvalid").get(),
errorMsg);
if (NS_FAILED(rv)) return rv;
errPrompt->Alert(nsnull, errorMsg.get());
if (NS_FAILED(rv)) return rv;
errPrompt->Alert(nsnull, errorMsg.get());
break;
#endif
case SEC_ERROR_BAD_PASSWORD:
rv = nssComponent->GetPIPNSSBundleString(
NS_LITERAL_STRING("PK11BadPassword").get(),
case SEC_ERROR_BAD_PASSWORD:
rv = nssComponent->GetPIPNSSBundleString(
NS_LITERAL_STRING("PK11BadPassword").get(),
errorMsg);
if (NS_FAILED(rv)) return rv;
errPrompt->Alert(nsnull, errorMsg.get());
break;
case SEC_ERROR_BAD_DER:
case SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE:
case SEC_ERROR_PKCS12_INVALID_MAC:
rv = nssComponent->GetPIPNSSBundleString(
NS_LITERAL_STRING("PKCS12DecodeErr").get(),
errorMsg);
if (NS_FAILED(rv)) return rv;
errPrompt->Alert(nsnull, errorMsg.get());
break;
case SEC_ERROR_PKCS12_DUPLICATE_DATA:
rv = nssComponent->GetPIPNSSBundleString(
NS_LITERAL_STRING("PKCS12DupData").get(),
errorMsg);
if (NS_FAILED(rv)) return rv;
errPrompt->Alert(nsnull, errorMsg.get());
break;
case SEC_ERROR_BAD_DER:
case SEC_ERROR_PKCS12_CORRUPT_PFX_STRUCTURE:
case SEC_ERROR_PKCS12_INVALID_MAC:
rv = nssComponent->GetPIPNSSBundleString(
NS_LITERAL_STRING("PKCS12DecodeErr").get(),
if (NS_FAILED(rv)) return rv;
errPrompt->Alert(nsnull, errorMsg.get());
break;
default:
rv = nssComponent->GetPIPNSSBundleString(
NS_LITERAL_STRING("PKCS12UnknownErr").get(),
errorMsg);
if (NS_FAILED(rv)) return rv;
errPrompt->Alert(nsnull, errorMsg.get());
if (NS_FAILED(rv)) return rv;
errPrompt->Alert(nsnull, errorMsg.get());
}
break;
case 0:
default:
rv = nssComponent->GetPIPNSSBundleString(
NS_LITERAL_STRING("PKCS12UnknownErr").get(),
errorMsg);
if (NS_FAILED(rv)) return rv;
errPrompt->Alert(nsnull, errorMsg.get());
break;
}
if (NS_FAILED(rv)) return rv;
return keepGoing;