зеркало из https://github.com/mozilla/gecko-dev.git
Bug 456705 - Firefox 2.0.0.17 crashes when opening a https-site or on shutdown with FoxyProxy 2.8.5 [@ nsSSLThread::Run], r=kaie
This commit is contained in:
Родитель
f961ae77c4
Коммит
47975f691c
|
@ -275,8 +275,68 @@ nsTokenEventRunnable::Run()
|
|||
return nssComponent->DispatchEvent(mType, mTokenName);
|
||||
}
|
||||
|
||||
// We must ensure that the nsNSSComponent has been loaded before
|
||||
// creating any other components.
|
||||
PRBool EnsureNSSInitialized(EnsureNSSOperator op)
|
||||
{
|
||||
static PRBool loading = PR_FALSE;
|
||||
static PRBool haveLoaded = PR_FALSE;
|
||||
|
||||
switch (op)
|
||||
{
|
||||
// In following 4 cases we are protected by monitor of XPCOM component
|
||||
// manager - we are inside of do_GetService call for nss component, so it is
|
||||
// safe to move with the flags here.
|
||||
case nssLoading:
|
||||
if (loading)
|
||||
return PR_FALSE; // We are reentered during nss component creation
|
||||
loading = PR_TRUE;
|
||||
return PR_TRUE;
|
||||
|
||||
case nssInitSucceeded:
|
||||
NS_ASSERTION(loading, "Bad call to EnsureNSSInitialized(nssInitSucceeded)");
|
||||
loading = PR_FALSE;
|
||||
haveLoaded = PR_TRUE;
|
||||
return PR_TRUE;
|
||||
|
||||
case nssInitFailed:
|
||||
NS_ASSERTION(loading, "Bad call to EnsureNSSInitialized(nssInitFailed)");
|
||||
loading = PR_FALSE;
|
||||
// no break
|
||||
|
||||
case nssShutdown:
|
||||
haveLoaded = PR_FALSE;
|
||||
return PR_FALSE;
|
||||
|
||||
// In this case we are called from a component to ensure nss initilization.
|
||||
// If the component has not yet been loaded and is not currently loading
|
||||
// call do_GetService for nss component to ensure it.
|
||||
case nssEnsure:
|
||||
// We are reentered during nss component creation or nss component is already up
|
||||
if (haveLoaded || loading)
|
||||
return PR_TRUE;
|
||||
|
||||
{
|
||||
nsCOMPtr<nsISupports> nssComponent
|
||||
= do_GetService(PSM_COMPONENT_CONTRACTID);
|
||||
|
||||
// Nss component failed to initialize, inform the caller of that fact.
|
||||
// Flags are appropriately set by component constructor itself.
|
||||
if (!nssComponent)
|
||||
return PR_FALSE;
|
||||
|
||||
return PR_TRUE;
|
||||
}
|
||||
|
||||
default:
|
||||
NS_ASSERTION(PR_FALSE, "Bad operator to EnsureNSSInitialized");
|
||||
return PR_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
nsNSSComponent::nsNSSComponent()
|
||||
:mNSSInitialized(PR_FALSE), mThreadList(nsnull)
|
||||
:mNSSInitialized(PR_FALSE), mThreadList(nsnull),
|
||||
mSSLThread(NULL), mCertVerificationThread(NULL)
|
||||
{
|
||||
mutex = PR_NewLock();
|
||||
|
||||
|
@ -296,19 +356,11 @@ nsNSSComponent::nsNSSComponent()
|
|||
// registering all identity data until first needed.
|
||||
memset(&mIdentityInfoCallOnce, 0, sizeof(PRCallOnceType));
|
||||
|
||||
nsSSLIOLayerHelpers::Init();
|
||||
|
||||
NS_ASSERTION( (0 == mInstanceCount), "nsNSSComponent is a singleton, but instantiated multiple times!");
|
||||
++mInstanceCount;
|
||||
hashTableCerts = nsnull;
|
||||
mShutdownObjectList = nsNSSShutDownList::construct();
|
||||
mIsNetworkDown = PR_FALSE;
|
||||
mSSLThread = new nsSSLThread();
|
||||
if (mSSLThread)
|
||||
mSSLThread->startThread();
|
||||
mCertVerificationThread = new nsCertVerificationThread();
|
||||
if (mCertVerificationThread)
|
||||
mCertVerificationThread->startThread();
|
||||
}
|
||||
|
||||
nsNSSComponent::~nsNSSComponent()
|
||||
|
@ -357,6 +409,10 @@ nsNSSComponent::~nsNSSComponent()
|
|||
mutex = nsnull;
|
||||
}
|
||||
|
||||
// We are being freed, drop the haveLoaded flag to re-enable
|
||||
// potential nss initialization later.
|
||||
EnsureNSSInitialized(nssShutdown);
|
||||
|
||||
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsNSSComponent::dtor finished\n"));
|
||||
}
|
||||
|
||||
|
@ -1470,9 +1526,6 @@ nsNSSComponent::InitializeNSS(PRBool showWarningBox)
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
hashTableCerts = PL_NewHashTable( 0, certHashtable_keyHash, certHashtable_keyCompare,
|
||||
certHashtable_valueCompare, 0, 0 );
|
||||
|
||||
nsresult rv;
|
||||
nsCAutoString profileStr;
|
||||
nsCOMPtr<nsIFile> profilePath;
|
||||
|
@ -1521,6 +1574,9 @@ nsNSSComponent::InitializeNSS(PRBool showWarningBox)
|
|||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
hashTableCerts = PL_NewHashTable( 0, certHashtable_keyHash, certHashtable_keyCompare,
|
||||
certHashtable_valueCompare, 0, 0 );
|
||||
|
||||
#if defined(XP_MACOSX)
|
||||
// function may modify the parameters
|
||||
// ignore return code from conversion, we continue anyway
|
||||
|
@ -1710,8 +1766,7 @@ nsNSSComponent::Init()
|
|||
|
||||
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Beginning NSS initialization\n"));
|
||||
|
||||
if (!mutex || !mShutdownObjectList ||
|
||||
!mSSLThread || !mCertVerificationThread)
|
||||
if (!mutex || !mShutdownObjectList)
|
||||
{
|
||||
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("NSS init, out of memory in constructor\n"));
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
@ -1747,9 +1802,30 @@ nsNSSComponent::Init()
|
|||
rv = InitializeNSS(PR_TRUE); // ok to show a warning box on failure
|
||||
if (NS_FAILED(rv)) {
|
||||
PR_LOG(gPIPNSSLog, PR_LOG_ERROR, ("Unable to Initialize NSS.\n"));
|
||||
|
||||
DeregisterObservers();
|
||||
mPIPNSSBundle = nsnull;
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsSSLIOLayerHelpers::Init();
|
||||
|
||||
mSSLThread = new nsSSLThread();
|
||||
if (mSSLThread)
|
||||
mSSLThread->startThread();
|
||||
mCertVerificationThread = new nsCertVerificationThread();
|
||||
if (mCertVerificationThread)
|
||||
mCertVerificationThread->startThread();
|
||||
|
||||
if (!mSSLThread || !mCertVerificationThread)
|
||||
{
|
||||
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("NSS init, could not create threads\n"));
|
||||
|
||||
DeregisterObservers();
|
||||
mPIPNSSBundle = nsnull;
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
InitializeCRLUpdateTimer();
|
||||
RegisterPSMContentListener();
|
||||
|
||||
|
@ -2186,6 +2262,31 @@ nsNSSComponent::RegisterObservers()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsNSSComponent::DeregisterObservers()
|
||||
{
|
||||
if (!mObserversRegistered)
|
||||
return NS_OK;
|
||||
|
||||
nsCOMPtr<nsIObserverService> observerService(do_GetService("@mozilla.org/observer-service;1"));
|
||||
NS_ASSERTION(observerService, "could not get observer service");
|
||||
if (observerService) {
|
||||
mObserversRegistered = PR_FALSE;
|
||||
PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsNSSComponent: removing observers\n"));
|
||||
|
||||
observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
|
||||
|
||||
observerService->RemoveObserver(this, PROFILE_APPROVE_CHANGE_TOPIC);
|
||||
observerService->RemoveObserver(this, PROFILE_CHANGE_TEARDOWN_TOPIC);
|
||||
observerService->RemoveObserver(this, PROFILE_CHANGE_TEARDOWN_VETO_TOPIC);
|
||||
observerService->RemoveObserver(this, PROFILE_BEFORE_CHANGE_TOPIC);
|
||||
observerService->RemoveObserver(this, PROFILE_AFTER_CHANGE_TOPIC);
|
||||
observerService->RemoveObserver(this, PROFILE_CHANGE_NET_TEARDOWN_TOPIC);
|
||||
observerService->RemoveObserver(this, PROFILE_CHANGE_NET_RESTORE_TOPIC);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNSSComponent::RememberCert(CERTCertificate *cert)
|
||||
{
|
||||
|
|
|
@ -93,6 +93,17 @@
|
|||
#define NS_CRYPTO_HMAC_CLASSNAME "Mozilla Crypto HMAC Function Component"
|
||||
#define NS_CRYPTO_HMAC_CID {0xa496d0a2, 0xdff7, 0x4e23, {0xbd, 0x65, 0x1c, 0xa7, 0x42, 0xfa, 0x17, 0x8a}}
|
||||
|
||||
enum EnsureNSSOperator
|
||||
{
|
||||
nssLoading = 0,
|
||||
nssInitSucceeded = 1,
|
||||
nssInitFailed = 2,
|
||||
nssShutdown = 3,
|
||||
nssEnsure = 4
|
||||
};
|
||||
|
||||
extern PRBool EnsureNSSInitialized(EnsureNSSOperator op);
|
||||
|
||||
//--------------------------------------------
|
||||
// Now we need a content listener to register
|
||||
//--------------------------------------------
|
||||
|
@ -287,6 +298,7 @@ private:
|
|||
nsresult ConfigureInternalPKCS11Token();
|
||||
nsresult RegisterPSMContentListener();
|
||||
nsresult RegisterObservers();
|
||||
nsresult DeregisterObservers();
|
||||
nsresult DownloadCrlSilently();
|
||||
nsresult PostCRLImportEvent(const nsCSubstring &urlString, nsIStreamListener *psmDownloader);
|
||||
nsresult getParamsForNextCrlToDownload(nsAutoString *url, PRTime *time, nsAutoString *key);
|
||||
|
|
|
@ -777,8 +777,10 @@ void nsSSLIOLayerHelpers::Cleanup()
|
|||
if (mSharedPollableEvent)
|
||||
PR_DestroyPollableEvent(mSharedPollableEvent);
|
||||
|
||||
if (mutex)
|
||||
if (mutex) {
|
||||
PR_DestroyLock(mutex);
|
||||
mutex = nsnull;
|
||||
}
|
||||
}
|
||||
|
||||
static nsresult
|
||||
|
@ -1771,6 +1773,7 @@ nsSSLIOLayerPoll(PRFileDesc *fd, PRInt16 in_flags, PRInt16 *out_flags)
|
|||
return nsSSLThread::requestPoll(socketInfo, in_flags, out_flags);
|
||||
}
|
||||
|
||||
PRBool nsSSLIOLayerHelpers::nsSSLIOLayerInitialized = PR_FALSE;
|
||||
PRDescIdentity nsSSLIOLayerHelpers::nsSSLIOLayerIdentity;
|
||||
PRIOMethods nsSSLIOLayerHelpers::nsSSLIOLayerMethods;
|
||||
PRLock *nsSSLIOLayerHelpers::mutex = nsnull;
|
||||
|
@ -1932,40 +1935,43 @@ static PRStatus PR_CALLBACK PSMConnectcontinue(PRFileDesc *fd, PRInt16 out_flags
|
|||
|
||||
nsresult nsSSLIOLayerHelpers::Init()
|
||||
{
|
||||
nsSSLIOLayerIdentity = PR_GetUniqueIdentity("NSS layer");
|
||||
nsSSLIOLayerMethods = *PR_GetDefaultIOMethods();
|
||||
if (!nsSSLIOLayerInitialized) {
|
||||
nsSSLIOLayerInitialized = PR_TRUE;
|
||||
nsSSLIOLayerIdentity = PR_GetUniqueIdentity("NSS layer");
|
||||
nsSSLIOLayerMethods = *PR_GetDefaultIOMethods();
|
||||
|
||||
nsSSLIOLayerMethods.available = (PRAvailableFN)_PSM_InvalidInt;
|
||||
nsSSLIOLayerMethods.available64 = (PRAvailable64FN)_PSM_InvalidInt64;
|
||||
nsSSLIOLayerMethods.fsync = (PRFsyncFN)_PSM_InvalidStatus;
|
||||
nsSSLIOLayerMethods.seek = (PRSeekFN)_PSM_InvalidInt;
|
||||
nsSSLIOLayerMethods.seek64 = (PRSeek64FN)_PSM_InvalidInt64;
|
||||
nsSSLIOLayerMethods.fileInfo = (PRFileInfoFN)_PSM_InvalidStatus;
|
||||
nsSSLIOLayerMethods.fileInfo64 = (PRFileInfo64FN)_PSM_InvalidStatus;
|
||||
nsSSLIOLayerMethods.writev = (PRWritevFN)_PSM_InvalidInt;
|
||||
nsSSLIOLayerMethods.accept = (PRAcceptFN)_PSM_InvalidDesc;
|
||||
nsSSLIOLayerMethods.bind = (PRBindFN)_PSM_InvalidStatus;
|
||||
nsSSLIOLayerMethods.listen = (PRListenFN)_PSM_InvalidStatus;
|
||||
nsSSLIOLayerMethods.shutdown = (PRShutdownFN)_PSM_InvalidStatus;
|
||||
nsSSLIOLayerMethods.recvfrom = (PRRecvfromFN)_PSM_InvalidInt;
|
||||
nsSSLIOLayerMethods.sendto = (PRSendtoFN)_PSM_InvalidInt;
|
||||
nsSSLIOLayerMethods.acceptread = (PRAcceptreadFN)_PSM_InvalidInt;
|
||||
nsSSLIOLayerMethods.transmitfile = (PRTransmitfileFN)_PSM_InvalidInt;
|
||||
nsSSLIOLayerMethods.sendfile = (PRSendfileFN)_PSM_InvalidInt;
|
||||
nsSSLIOLayerMethods.available = (PRAvailableFN)_PSM_InvalidInt;
|
||||
nsSSLIOLayerMethods.available64 = (PRAvailable64FN)_PSM_InvalidInt64;
|
||||
nsSSLIOLayerMethods.fsync = (PRFsyncFN)_PSM_InvalidStatus;
|
||||
nsSSLIOLayerMethods.seek = (PRSeekFN)_PSM_InvalidInt;
|
||||
nsSSLIOLayerMethods.seek64 = (PRSeek64FN)_PSM_InvalidInt64;
|
||||
nsSSLIOLayerMethods.fileInfo = (PRFileInfoFN)_PSM_InvalidStatus;
|
||||
nsSSLIOLayerMethods.fileInfo64 = (PRFileInfo64FN)_PSM_InvalidStatus;
|
||||
nsSSLIOLayerMethods.writev = (PRWritevFN)_PSM_InvalidInt;
|
||||
nsSSLIOLayerMethods.accept = (PRAcceptFN)_PSM_InvalidDesc;
|
||||
nsSSLIOLayerMethods.bind = (PRBindFN)_PSM_InvalidStatus;
|
||||
nsSSLIOLayerMethods.listen = (PRListenFN)_PSM_InvalidStatus;
|
||||
nsSSLIOLayerMethods.shutdown = (PRShutdownFN)_PSM_InvalidStatus;
|
||||
nsSSLIOLayerMethods.recvfrom = (PRRecvfromFN)_PSM_InvalidInt;
|
||||
nsSSLIOLayerMethods.sendto = (PRSendtoFN)_PSM_InvalidInt;
|
||||
nsSSLIOLayerMethods.acceptread = (PRAcceptreadFN)_PSM_InvalidInt;
|
||||
nsSSLIOLayerMethods.transmitfile = (PRTransmitfileFN)_PSM_InvalidInt;
|
||||
nsSSLIOLayerMethods.sendfile = (PRSendfileFN)_PSM_InvalidInt;
|
||||
|
||||
nsSSLIOLayerMethods.getsockname = PSMGetsockname;
|
||||
nsSSLIOLayerMethods.getpeername = PSMGetpeername;
|
||||
nsSSLIOLayerMethods.getsocketoption = PSMGetsocketoption;
|
||||
nsSSLIOLayerMethods.setsocketoption = PSMSetsocketoption;
|
||||
nsSSLIOLayerMethods.recv = PSMRecv;
|
||||
nsSSLIOLayerMethods.send = PSMSend;
|
||||
nsSSLIOLayerMethods.connectcontinue = PSMConnectcontinue;
|
||||
nsSSLIOLayerMethods.getsockname = PSMGetsockname;
|
||||
nsSSLIOLayerMethods.getpeername = PSMGetpeername;
|
||||
nsSSLIOLayerMethods.getsocketoption = PSMGetsocketoption;
|
||||
nsSSLIOLayerMethods.setsocketoption = PSMSetsocketoption;
|
||||
nsSSLIOLayerMethods.recv = PSMRecv;
|
||||
nsSSLIOLayerMethods.send = PSMSend;
|
||||
nsSSLIOLayerMethods.connectcontinue = PSMConnectcontinue;
|
||||
|
||||
nsSSLIOLayerMethods.connect = nsSSLIOLayerConnect;
|
||||
nsSSLIOLayerMethods.close = nsSSLIOLayerClose;
|
||||
nsSSLIOLayerMethods.write = nsSSLIOLayerWrite;
|
||||
nsSSLIOLayerMethods.read = nsSSLIOLayerRead;
|
||||
nsSSLIOLayerMethods.poll = nsSSLIOLayerPoll;
|
||||
nsSSLIOLayerMethods.connect = nsSSLIOLayerConnect;
|
||||
nsSSLIOLayerMethods.close = nsSSLIOLayerClose;
|
||||
nsSSLIOLayerMethods.write = nsSSLIOLayerWrite;
|
||||
nsSSLIOLayerMethods.read = nsSSLIOLayerRead;
|
||||
nsSSLIOLayerMethods.poll = nsSSLIOLayerPoll;
|
||||
}
|
||||
|
||||
mutex = PR_NewLock();
|
||||
if (!mutex)
|
||||
|
|
|
@ -248,6 +248,7 @@ public:
|
|||
static nsresult Init();
|
||||
static void Cleanup();
|
||||
|
||||
static PRBool nsSSLIOLayerInitialized;
|
||||
static PRDescIdentity nsSSLIOLayerIdentity;
|
||||
static PRIOMethods nsSSLIOLayerMethods;
|
||||
|
||||
|
|
|
@ -79,26 +79,6 @@
|
|||
#include "nsSSLStatus.h"
|
||||
#include "nsNSSIOLayer.h"
|
||||
|
||||
// We must ensure that the nsNSSComponent has been loaded before
|
||||
// creating any other components.
|
||||
static void EnsureNSSInitialized(PRBool triggeredByNSSComponent)
|
||||
{
|
||||
static PRBool haveLoaded = PR_FALSE;
|
||||
if (haveLoaded)
|
||||
return;
|
||||
|
||||
haveLoaded = PR_TRUE;
|
||||
|
||||
if (triggeredByNSSComponent) {
|
||||
// We must prevent a recursion, as nsNSSComponent creates
|
||||
// additional instances
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISupports> nssComponent
|
||||
= do_GetService(PSM_COMPONENT_CONTRACTID);
|
||||
}
|
||||
|
||||
// These two macros are ripped off from nsIGenericFactory.h and slightly
|
||||
// modified.
|
||||
#define NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(triggeredByNSSComponent, \
|
||||
|
@ -110,16 +90,27 @@ _InstanceClass##Constructor(nsISupports *aOuter, REFNSIID aIID, \
|
|||
nsresult rv; \
|
||||
_InstanceClass * inst; \
|
||||
\
|
||||
EnsureNSSInitialized(triggeredByNSSComponent); \
|
||||
\
|
||||
*aResult = NULL; \
|
||||
if (NULL != aOuter) { \
|
||||
rv = NS_ERROR_NO_AGGREGATION; \
|
||||
return rv; \
|
||||
} \
|
||||
\
|
||||
if (triggeredByNSSComponent) \
|
||||
{ \
|
||||
if (!EnsureNSSInitialized(nssLoading)) \
|
||||
return NS_ERROR_FAILURE; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
if (!EnsureNSSInitialized(nssEnsure)) \
|
||||
return NS_ERROR_FAILURE; \
|
||||
} \
|
||||
\
|
||||
NS_NEWXPCOM(inst, _InstanceClass); \
|
||||
if (NULL == inst) { \
|
||||
if (triggeredByNSSComponent) \
|
||||
EnsureNSSInitialized(nssInitFailed); \
|
||||
rv = NS_ERROR_OUT_OF_MEMORY; \
|
||||
return rv; \
|
||||
} \
|
||||
|
@ -127,6 +118,14 @@ _InstanceClass##Constructor(nsISupports *aOuter, REFNSIID aIID, \
|
|||
rv = inst->QueryInterface(aIID, aResult); \
|
||||
NS_RELEASE(inst); \
|
||||
\
|
||||
if (triggeredByNSSComponent) \
|
||||
{ \
|
||||
if (NS_SUCCEEDED(rv)) \
|
||||
EnsureNSSInitialized(nssInitSucceeded); \
|
||||
else \
|
||||
EnsureNSSInitialized(nssInitFailed); \
|
||||
} \
|
||||
\
|
||||
return rv; \
|
||||
} \
|
||||
|
||||
|
@ -140,16 +139,27 @@ _InstanceClass##Constructor(nsISupports *aOuter, REFNSIID aIID, \
|
|||
nsresult rv; \
|
||||
_InstanceClass * inst; \
|
||||
\
|
||||
EnsureNSSInitialized(triggeredByNSSComponent); \
|
||||
\
|
||||
*aResult = NULL; \
|
||||
if (NULL != aOuter) { \
|
||||
rv = NS_ERROR_NO_AGGREGATION; \
|
||||
return rv; \
|
||||
} \
|
||||
\
|
||||
if (triggeredByNSSComponent) \
|
||||
{ \
|
||||
if (!EnsureNSSInitialized(nssLoading)) \
|
||||
return NS_ERROR_FAILURE; \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
if (!EnsureNSSInitialized(nssEnsure)) \
|
||||
return NS_ERROR_FAILURE; \
|
||||
} \
|
||||
\
|
||||
NS_NEWXPCOM(inst, _InstanceClass); \
|
||||
if (NULL == inst) { \
|
||||
if (triggeredByNSSComponent) \
|
||||
EnsureNSSInitialized(nssInitFailed); \
|
||||
rv = NS_ERROR_OUT_OF_MEMORY; \
|
||||
return rv; \
|
||||
} \
|
||||
|
@ -160,6 +170,14 @@ _InstanceClass##Constructor(nsISupports *aOuter, REFNSIID aIID, \
|
|||
} \
|
||||
NS_RELEASE(inst); \
|
||||
\
|
||||
if (triggeredByNSSComponent) \
|
||||
{ \
|
||||
if (NS_SUCCEEDED(rv)) \
|
||||
EnsureNSSInitialized(nssInitSucceeded); \
|
||||
else \
|
||||
EnsureNSSInitialized(nssInitFailed); \
|
||||
} \
|
||||
\
|
||||
return rv; \
|
||||
} \
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче