1 /* |
1 /* |
2 * Copyright (c) 2006-2010 Nokia Corporation and/or its subsidiary(-ies). |
2 * Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies). |
3 * All rights reserved. |
3 * All rights reserved. |
4 * This component and the accompanying materials are made available |
4 * This component and the accompanying materials are made available |
5 * under the terms of "Eclipse Public License v1.0" |
5 * under the terms of "Eclipse Public License v1.0" |
6 * which accompanies this distribution, and is available |
6 * which accompanies this distribution, and is available |
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
34 #include <siprequestelements.h> |
34 #include <siprequestelements.h> |
35 #include <sipclienttransaction.h> |
35 #include <sipclienttransaction.h> |
36 #include <siprefresh.h> |
36 #include <siprefresh.h> |
37 #include <sipmessageelements.h> |
37 #include <sipmessageelements.h> |
38 #include <sipsubscribedialogassoc.h> |
38 #include <sipsubscribedialogassoc.h> |
|
39 #include <sipregistrationcontext.h> |
39 |
40 |
40 // sip codec api |
41 // sip codec api |
41 #include <sipfromheader.h> |
42 #include <sipfromheader.h> |
42 #include <siptoheader.h> |
43 #include <siptoheader.h> |
43 #include <sipexpiresheader.h> |
44 #include <sipexpiresheader.h> |
49 #include <sipextensionheader.h> |
50 #include <sipextensionheader.h> |
50 #include <sipsubscriptionstateheader.h> |
51 #include <sipsubscriptionstateheader.h> |
51 #include <sipsupportedheader.h> |
52 #include <sipsupportedheader.h> |
52 #include <sipacceptheader.h> |
53 #include <sipacceptheader.h> |
53 |
54 |
|
55 #include <cvimpstsettingsstore.h> |
|
56 |
54 // own simple |
57 // own simple |
55 #include "simplesipconnection.h" |
58 #include "simplesipconnection.h" |
56 #include "simplesipconnectionobserver.h" |
59 #include "simplesipconnectionobserver.h" |
57 #include "simplesipprofileobserver.h" |
60 #include "simplesipprofileobserver.h" |
58 #include "simplerefreshtimer.h" |
61 #include "simplerefreshtimer.h" |
78 _LIT8 ( KSimpleTimeout, "timeout" ); |
81 _LIT8 ( KSimpleTimeout, "timeout" ); |
79 _LIT8 ( KSimpleGiveup, "giveup" ); |
82 _LIT8 ( KSimpleGiveup, "giveup" ); |
80 _LIT8 ( KSimpleNoresource, "Noresource" ); |
83 _LIT8 ( KSimpleNoresource, "Noresource" ); |
81 _LIT8 ( KSipPrefix, "sip:" ); |
84 _LIT8 ( KSipPrefix, "sip:" ); |
82 |
85 |
83 // UID given for CSIP class. SIP Utils listens to new SIP requests |
|
84 // (when receiving IM) outside SIP dialogs. |
|
85 const TUid KSimpleSIPUtilsUID = { 0x10281EEC }; |
|
86 |
|
87 // ================= MEMBER FUNCTIONS ======================= |
86 // ================= MEMBER FUNCTIONS ======================= |
88 // |
87 // |
89 |
88 |
90 // ---------------------------------------------------------- |
89 // ---------------------------------------------------------- |
91 // CSimpleSipConnection::CSimpleSipConnection |
90 // CSimpleSipConnection::CSimpleSipConnection |
112 delete iSipConnection; |
111 delete iSipConnection; |
113 delete iProfileObserver; |
112 delete iProfileObserver; |
114 delete iConnectionObserver; |
113 delete iConnectionObserver; |
115 delete iSip; |
114 delete iSip; |
116 DeleteRequests(); |
115 DeleteRequests(); |
|
116 DeleteRequests( CSimpleRequest::EReqReceiveIM ); |
117 SIPStrings::Close(); |
117 SIPStrings::Close(); |
118 } |
118 } |
119 |
119 |
120 // ---------------------------------------------------------- |
120 // ---------------------------------------------------------- |
121 // CSimpleSipConnection::NewL |
121 // CSimpleSipConnection::NewL |
138 { |
138 { |
139 #ifdef _DEBUG |
139 #ifdef _DEBUG |
140 TSimpleLogger::Log( |
140 TSimpleLogger::Log( |
141 _L("SipConnection: ConstructL 20-01-07 this=%d" ), (TInt)this ); |
141 _L("SipConnection: ConstructL 20-01-07 this=%d" ), (TInt)this ); |
142 #endif |
142 #endif |
143 // initialize members |
143 |
144 iSip = CSIP::NewL( KSimpleSIPUtilsUID, *this ); |
|
145 iConnectionObserver = CSimpleSipConnectionObserver::NewL( *this); |
144 iConnectionObserver = CSimpleSipConnectionObserver::NewL( *this); |
146 iProfileObserver = |
|
147 CSimpleSipProfileObserver::NewL( iSip, *iConnectionObserver); |
|
148 SIPStrings::OpenL(); |
145 SIPStrings::OpenL(); |
149 // read static cenrep settings |
146 // read static cenrep settings |
150 iSettings.ReadCentRepSettings(); |
147 iSettings.ReadCentRepSettings(); |
151 |
148 |
152 #ifdef _DEBUG |
149 #ifdef _DEBUG |
448 CUri8* uri = CUri8::NewL( parser ); |
445 CUri8* uri = CUri8::NewL( parser ); |
449 |
446 |
450 // Start to fill header, Remote URI |
447 // Start to fill header, Remote URI |
451 CSIPRequestElements* elems = CSIPRequestElements::NewL( uri ); |
448 CSIPRequestElements* elems = CSIPRequestElements::NewL( uri ); |
452 CleanupStack::PushL( elems ); // CS: 3 |
449 CleanupStack::PushL( elems ); // CS: 3 |
453 |
450 |
454 TUriParser8 parser3; |
451 TUriParser8 parser3; |
455 User::LeaveIfError( parser3.Parse( iProfileObserver-> |
452 User::LeaveIfError( parser3.Parse( iProfileObserver-> |
456 GiveUserAorL() )); |
453 GiveUserAorL() )); |
457 uri = CUri8::NewLC( parser3 ); // CS: 4 |
454 uri = CUri8::NewL( parser3 ); |
458 |
|
459 CSIPAddress* sipAddress = CSIPAddress::NewL( uri ); |
455 CSIPAddress* sipAddress = CSIPAddress::NewL( uri ); |
460 CleanupStack::Pop( uri ); // ownership given to sipAddress // CS: 3 |
|
461 CleanupStack::PushL( sipAddress ); // CS: 4 |
|
462 |
456 |
463 // From Header |
457 // From Header |
464 CSIPFromHeader* fromH = CSIPFromHeader::NewL( sipAddress ); |
458 CSIPFromHeader* fromH = CSIPFromHeader::NewL( sipAddress ); |
465 CleanupStack::Pop( sipAddress ); // ownership given to FromH // CS: 3 |
459 elems->SetFromHeaderL( fromH ); // fromH, ownership given to elems |
466 CleanupStack::PushL( fromH ); // CS: 4 |
|
467 elems->SetFromHeaderL( fromH ); |
|
468 // fromH, ownership given to elems |
|
469 CleanupStack::Pop( fromH ); // CS: 3 |
|
470 |
460 |
471 uri = CUri8::NewLC( parser ); // CS: 4 |
461 uri = CUri8::NewL( parser ); |
472 |
|
473 CSIPAddress* addr = CSIPAddress::NewL( uri ); |
462 CSIPAddress* addr = CSIPAddress::NewL( uri ); |
474 CleanupStack::Pop( uri ); // CS: 3 |
|
475 CleanupStack::PushL( addr ); // CS: 4 |
|
476 |
|
477 // To Header |
463 // To Header |
478 CSIPToHeader* toHeader = CSIPToHeader::NewL( addr ); |
464 CSIPToHeader* toHeader = CSIPToHeader::NewL( addr ); |
479 CleanupStack::Pop( addr ); // CS: 3 |
|
480 CleanupStack::PushL( toHeader ); // CS: 4 |
|
481 elems->SetToHeaderL( toHeader ); |
465 elems->SetToHeaderL( toHeader ); |
482 CleanupStack::Pop( toHeader ); // CS: 3 |
|
483 |
466 |
484 CSIPMessageElements& mesElems = elems->MessageElements(); |
467 CSIPMessageElements& mesElems = elems->MessageElements(); |
485 |
468 |
486 // Add Expires Header |
469 // Add Expires Header |
487 if ( aReq.RefreshTime() ) |
470 if ( aReq.RefreshTime() ) |
495 User::LeaveIfError( headers.Append( exprH ) ); |
478 User::LeaveIfError( headers.Append( exprH ) ); |
496 CleanupStack::Pop( exprH ); // CS: 4 |
479 CleanupStack::Pop( exprH ); // CS: 4 |
497 |
480 |
498 mesElems.SetUserHeadersL( headers ); |
481 mesElems.SetUserHeadersL( headers ); |
499 // headers ownership given to mesElems |
482 // headers ownership given to mesElems |
500 CleanupStack::Pop( &headers ); // CS: 3 |
483 CleanupStack::Pop( &headers ); // CS: 3 |
501 } |
484 } |
502 |
485 |
503 // Set content type and content |
486 // Set content type and content |
504 CSIPContentTypeHeader* contTypeH = CSIPContentTypeHeader::NewLC( |
487 HBufC8* buffer = aContent.AllocLC(); // CS: 4 |
505 KSimpleMediaType, KSimpleMediaSubType ); // CS: 4 |
488 CSIPContentTypeHeader* contTypeH = CSIPContentTypeHeader::NewL( |
506 HBufC8* buffer = aContent.AllocLC(); // CS: 5 |
489 KSimpleMediaType, KSimpleMediaSubType ); |
|
490 CleanupStack::Pop( buffer ); // CS: 3 |
507 mesElems.SetContentL( buffer, contTypeH ); |
491 mesElems.SetContentL( buffer, contTypeH ); |
508 // buffer ownership given to mesElems |
492 // buffer ownership given to mesElems |
509 CleanupStack::Pop( buffer ); // CS: 4 |
|
510 // contTypeH ownership given to mesElems |
493 // contTypeH ownership given to mesElems |
511 CleanupStack::Pop( contTypeH ); // CS: 3 |
|
512 |
494 |
513 // Set method |
495 // Set method |
514 elems->SetMethodL( SIPStrings::StringF( SipStrConsts::EMessage ) ); |
496 elems->SetMethodL( SIPStrings::StringF( SipStrConsts::EMessage ) ); |
515 |
497 |
|
498 CleanupStack::Pop( elems ); // CS: 2 |
516 // Send the request transaction |
499 // Send the request transaction |
|
500 // elems, ownership given |
517 CSIPClientTransaction* sipTrans = iSipConnection->SendRequestL( elems, |
501 CSIPClientTransaction* sipTrans = iSipConnection->SendRequestL( elems, |
518 *regContext ); |
502 *regContext ); |
519 |
503 |
520 // Save SIP client transaction |
504 // Save SIP client transaction |
521 request->SetTransaction( sipTrans ); |
505 request->SetTransaction( sipTrans ); |
522 CleanupStack::Pop( elems ); // elems, ownership given // CS: 2 |
506 |
523 CleanupStack::PopAndDestroy( temp ); // CS: 1 |
507 CleanupStack::PopAndDestroy( temp ); // CS: 1 |
524 |
508 |
525 // Start refresh timer, it's used for garbage collection too. |
509 // Start refresh timer, it's used for garbage collection too. |
526 request->StartRefreshTimer(); |
510 request->StartRefreshTimer(); |
527 } |
511 } |
1089 |
1073 |
1090 acceH = CSIPAcceptHeader::NewLC( |
1074 acceH = CSIPAcceptHeader::NewLC( |
1091 KSimpleMultiType, KSimpleMultipartSubType ); |
1075 KSimpleMultiType, KSimpleMultipartSubType ); |
1092 User::LeaveIfError( headers.Append( acceH )); |
1076 User::LeaveIfError( headers.Append( acceH )); |
1093 ++popCount; |
1077 ++popCount; |
|
1078 |
|
1079 // add supported header with value eventlist |
|
1080 RPointerArray<CSIPSupportedHeader> suppHs = |
|
1081 CSIPSupportedHeader::DecodeL( KSimpleEventlist); |
|
1082 for( TInt count=0; count < suppHs.Count(); count++ ) |
|
1083 { |
|
1084 User::LeaveIfError( headers.Append( suppHs[count] )); |
|
1085 } |
|
1086 suppHs.Close(); |
1094 } |
1087 } |
1095 if ( r->Match( CSimpleRequest::EReqSubscribeWinfo )) |
1088 if ( r->Match( CSimpleRequest::EReqSubscribeWinfo )) |
1096 { |
1089 { |
1097 acceH = CSIPAcceptHeader::NewLC( |
1090 acceH = CSIPAcceptHeader::NewLC( |
1098 KSimpleApplicationType, KSimpleWinfoSubType ); |
1091 KSimpleApplicationType, KSimpleWinfoSubType ); |
1268 // memory for heap descriptors in DoHandleReceivedMessageL |
1261 // memory for heap descriptors in DoHandleReceivedMessageL |
1269 return error; |
1262 return error; |
1270 } |
1263 } |
1271 |
1264 |
1272 // ---------------------------------------------------------- |
1265 // ---------------------------------------------------------- |
|
1266 // CSimpleSipConnection::ConnectionChanged |
|
1267 // ---------------------------------------------------------- |
|
1268 // |
|
1269 void CSimpleSipConnection::ConnectionChanged() |
|
1270 { |
|
1271 #ifdef _DEBUG |
|
1272 TSimpleLogger::Log(_L("SipConnection: ConnectionChanged")); |
|
1273 if( iSipConnection ) |
|
1274 { |
|
1275 TSimpleLogger::Log(_L("SipConnection: ConnectionChanged : old conn state %d (internal:%d)"), iSipConnection->State(), iSipState ); |
|
1276 TSimpleLogger::Log(_L("SipConnection: ConnectionChanged : old IAP %d"), iSipConnection->IapId() ); |
|
1277 } |
|
1278 #endif |
|
1279 |
|
1280 iCurrentNbrSubs = 0; |
|
1281 CSIPConnection* conn = NULL; |
|
1282 TRAPD( err, conn = iProfileObserver->GiveConnectionL() ); |
|
1283 if( !err ) |
|
1284 { |
|
1285 delete iSipConnection; |
|
1286 iSipConnection = conn; |
|
1287 #ifdef _DEBUG |
|
1288 TSimpleLogger::Log(_L("SipConnection: ConnectionChanged : new conn state %d"), iSipConnection->State() ); |
|
1289 TSimpleLogger::Log(_L("SipConnection: ConnectionChanged : new IAP %d"), iSipConnection->IapId() ); |
|
1290 #endif |
|
1291 } |
|
1292 #ifdef _DEBUG |
|
1293 else |
|
1294 { |
|
1295 TSimpleLogger::Log(_L("SipConnection: ConnectionChanged : Get SIP connection error %d"), err ); |
|
1296 } |
|
1297 #endif |
|
1298 |
|
1299 if( iSipConnection ) |
|
1300 { |
|
1301 if( iSipState != iSipConnection->State() ) |
|
1302 { |
|
1303 iConnectionObserver->ConnectionStateChanged( iSipConnection->State() ); |
|
1304 } |
|
1305 } |
|
1306 } |
|
1307 |
|
1308 // ---------------------------------------------------------- |
1273 // CSimpleSipConnection::StartToCheckExpiryL |
1309 // CSimpleSipConnection::StartToCheckExpiryL |
1274 // ---------------------------------------------------------- |
1310 // ---------------------------------------------------------- |
1275 // |
1311 // |
1276 void CSimpleSipConnection::StartToCheckExpiryL( CSimpleRequest& aReq ) |
1312 void CSimpleSipConnection::StartToCheckExpiryL( CSimpleRequest& aReq ) |
1277 { |
1313 { |
1368 |
1404 |
1369 while ( rIter ) |
1405 while ( rIter ) |
1370 { |
1406 { |
1371 CSimpleRequest* req = rIter; |
1407 CSimpleRequest* req = rIter; |
1372 rIter++; //lint !e1757 |
1408 rIter++; //lint !e1757 |
1373 req->Destroy(); |
1409 |
|
1410 // open request EReqReceiveIM should not be deleted |
|
1411 // will be deleted only when im message received |
|
1412 // or destructor is called. |
|
1413 if ( !req->Match( CSimpleRequest::EReqReceiveIM ) ) |
|
1414 { |
|
1415 req->Destroy(); |
|
1416 } |
|
1417 } |
|
1418 } |
|
1419 |
|
1420 // ----------------------------------------------------------------------------- |
|
1421 // CSimpleSipConnection::DeleteRequests |
|
1422 // ----------------------------------------------------------------------------- |
|
1423 void CSimpleSipConnection::DeleteRequests( |
|
1424 CSimpleRequest::TSimpleSipReqType aRequestType ) |
|
1425 { |
|
1426 #ifdef _DEBUG |
|
1427 TSimpleLogger::Log(_L("SipConnection: DeleteRequests type=%d" ), |
|
1428 aRequestType ); |
|
1429 #endif |
|
1430 // Delete buffered transaction requests match to the aRequestType |
|
1431 TDblQueIter<CSimpleRequest> rIter( iRequestList ); |
|
1432 rIter.SetToFirst(); |
|
1433 |
|
1434 while ( rIter ) |
|
1435 { |
|
1436 CSimpleRequest* req = rIter; |
|
1437 rIter++; //lint !e1757 |
|
1438 |
|
1439 if ( req->Match( aRequestType ) ) |
|
1440 { |
|
1441 req->Destroy(); |
|
1442 } |
1374 } |
1443 } |
1375 } |
1444 } |
1376 |
1445 |
1377 // ----------------------------------------------------------------------------- |
1446 // ----------------------------------------------------------------------------- |
1378 // CSimpleSipConnection::DeleteRequest |
1447 // CSimpleSipConnection::DeleteRequest |
1674 if ( header->Name() == extensionName ) |
1743 if ( header->Name() == extensionName ) |
1675 { |
1744 { |
1676 HBufC8* hValue = header->ToTextValueL(); |
1745 HBufC8* hValue = header->ToTextValueL(); |
1677 // hValue ownership is transferred |
1746 // hValue ownership is transferred |
1678 aReq->SetETag( hValue ); |
1747 aReq->SetETag( hValue ); |
|
1748 // store etag to vimpstsettingstore |
|
1749 StoreETagL( *hValue ); |
1679 ETagReceived = ETrue; |
1750 ETagReceived = ETrue; |
1680 } |
1751 } |
1681 else if ( header->Name() == SIPStrings::StringF( SipStrConsts::EExpiresHeader)) |
1752 else if ( header->Name() == SIPStrings::StringF( SipStrConsts::EExpiresHeader)) |
1682 { |
1753 { |
1683 // Save the new refresh time. |
1754 // Save the new refresh time. |
1693 } |
1764 } |
1694 if ( !ETagReceived || !aReq->RefreshTime() ) |
1765 if ( !ETagReceived || !aReq->RefreshTime() ) |
1695 { |
1766 { |
1696 // Remove old ETag if nore received or if the expires header was 0 in our request. |
1767 // Remove old ETag if nore received or if the expires header was 0 in our request. |
1697 aReq->SetETag( NULL ); |
1768 aReq->SetETag( NULL ); |
|
1769 //TPtrC8 nullETag = nullETag.Alloc( KNullDesC8 ); |
|
1770 TBufC8<1> nullETag( KNullDesC8 ); |
|
1771 HBufC8* buf; |
|
1772 buf = nullETag.Alloc(); |
|
1773 StoreETagL( *buf ); |
|
1774 delete buf; |
1698 } |
1775 } |
1699 // PopAndDestroy calls extensionName.Close() |
1776 // PopAndDestroy calls extensionName.Close() |
1700 CleanupStack::PopAndDestroy( &extensionName ); |
1777 CleanupStack::PopAndDestroy( &extensionName ); |
1701 } |
1778 } |
1702 |
1779 |
2068 #ifdef _DEBUG |
2145 #ifdef _DEBUG |
2069 TSimpleLogger::Log(_L("SipConnection: RegisterAnyL" )); |
2146 TSimpleLogger::Log(_L("SipConnection: RegisterAnyL" )); |
2070 #endif |
2147 #endif |
2071 |
2148 |
2072 iSettings.ReadOTASettingsL( aReq.Aux() ); |
2149 iSettings.ReadOTASettingsL( aReq.Aux() ); |
2073 |
2150 |
|
2151 TInt32 uniqueId = iSettings.SipProfileId(); |
|
2152 TUid uniqueUid; |
|
2153 uniqueUid.iUid = uniqueId; |
|
2154 |
|
2155 if ( !iSip && !iProfileObserver ) |
|
2156 { |
|
2157 iSip = CSIP::NewL( uniqueUid, *this ); |
|
2158 |
|
2159 iProfileObserver = CSimpleSipProfileObserver::NewL( |
|
2160 iSip, *iConnectionObserver); |
|
2161 } |
|
2162 |
2074 iProfileObserver->RegisterGivenProfileL( iSettings.SipProfileId() ); |
2163 iProfileObserver->RegisterGivenProfileL( iSettings.SipProfileId() ); |
2075 SetSipState( ESimpleSipIdle ); |
2164 SetSipState( ESimpleSipIdle ); |
2076 TRAPD( err, iSipConnection = iProfileObserver->GiveConnectionL() ); |
2165 TRAPD( err, iSipConnection = iProfileObserver->GiveConnectionL() ); |
2077 if ( !err ) |
2166 if ( !err ) |
2078 { |
2167 { |
2537 void CSimpleSipConnection::RecognizeSipState( |
2626 void CSimpleSipConnection::RecognizeSipState( |
2538 TSimpleSipState aState ) |
2627 TSimpleSipState aState ) |
2539 { |
2628 { |
2540 if ( aState == ESimpleSipActive ) |
2629 if ( aState == ESimpleSipActive ) |
2541 { |
2630 { |
2542 // Check that both SIP Profile and SIP connection are ready. |
2631 // Check that SIP Profile is ready. |
2543 // iProfileObserver is created in ConstructL. |
2632 // iProfileObserver is created in ConstructL. |
2544 if ( iProfileObserver->IsProfileActive() && |
2633 if ( iProfileObserver->IsProfileActive() ) |
2545 iSipConnection && |
|
2546 iSipConnection->State() == CSIPConnection::EActive ) |
|
2547 { |
2634 { |
2548 SetSipState( aState ); |
2635 SetSipState( aState ); |
2549 } |
2636 } |
2550 } |
2637 } |
2551 else |
2638 else |
2575 TSimpleLogger::Log( _L( |
2662 TSimpleLogger::Log( _L( |
2576 "CSimpleSipConnection::DoHandleReceivedMessageL - End" ) ); |
2663 "CSimpleSipConnection::DoHandleReceivedMessageL - End" ) ); |
2577 #endif |
2664 #endif |
2578 } |
2665 } |
2579 |
2666 |
|
2667 // ----------------------------------------------------------------------------- |
|
2668 // CSimpleRequest::SetServiceId |
|
2669 // ----------------------------------------------------------------------------- |
|
2670 EXPORT_C void CSimpleSipConnection::SetServiceId( TInt32 aServiceId ) |
|
2671 { |
|
2672 #ifdef _DEBUG |
|
2673 TSimpleLogger::Log( _L( |
|
2674 "CSimpleSipConnection::SetServiceId old serviceId = %d, new serviceId = %d" ), |
|
2675 iServiceId, aServiceId ); |
|
2676 #endif |
|
2677 iServiceId = aServiceId; |
|
2678 } |
|
2679 |
|
2680 // ----------------------------------------------------------------------------- |
|
2681 // CSimpleRequest::StoreETag |
|
2682 // ----------------------------------------------------------------------------- |
|
2683 void CSimpleSipConnection::StoreETagL( HBufC8& aETag ) |
|
2684 { |
|
2685 #ifdef _DEBUG |
|
2686 TBuf<255> printDocumentId; |
|
2687 printDocumentId.Copy( aETag ); |
|
2688 TSimpleLogger::Log(_L("CSimpleSipConnection: StoreETag ETag = %S, serviceId = %d" ), |
|
2689 &printDocumentId, iServiceId ); |
|
2690 #endif |
|
2691 MVIMPSTSettingsStore* settings = CVIMPSTSettingsStore::NewLC(); |
|
2692 |
|
2693 // Store ETag to uiservicetabsettings |
|
2694 User::LeaveIfError( settings->SetL( |
|
2695 iServiceId, EServicePresenceSessionIdentifier, aETag ) ); |
|
2696 |
|
2697 CleanupStack::PopAndDestroy(); //settings |
|
2698 } |
|
2699 |