/**************************************************************************** ** ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** GNU Lesser General Public License Usage ** This file may be used under the terms of the GNU Lesser General Public ** License version 2.1 as published by the Free Software Foundation and ** appearing in the file LICENSE.LGPL included in the packaging of this ** file. Please review the following information to ensure the GNU Lesser ** General Public License version 2.1 requirements will be met: ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU General ** Public License version 3.0 as published by the Free Software Foundation ** and appearing in the file LICENSE.GPL included in the packaging of this ** file. Please review the following information to ensure the GNU General ** Public License version 3.0 requirements will be met: ** http://www.gnu.org/copyleft/gpl.html. ** ** Other Usage ** Alternatively, this file may be used in accordance with the terms and ** conditions contained in a signed written agreement between you and Nokia. ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include // TDriveUnit etc #include // PathInfo #ifndef USE_SCHEMEHANDLER #include // CRichText #include // CEikonEnv #include // RApaLsSession #include // TApaTaskList, TApaTask #include // RSendAs #include // RSendAsMessage #ifdef USE_DOCUMENTHANDLER #include // CDocumentHandler #include #endif #else // USE_SCHEMEHANDLER #include #endif QT_BEGIN_NAMESPACE _LIT(KCacheSubDir, "Cache\\"); _LIT(KSysBin, "\\Sys\\Bin\\"); _LIT(KBrowserPrefix, "4 " ); _LIT(KFontsDir, "z:\\resource\\Fonts\\"); #ifndef USE_SCHEMEHANDLER // copied from miutset.h, so we don't get a dependency into the app layer const TUid KUidMsgTypeSMTP = {0x10001028}; // 268439592 const TUid KUidBrowser = { 0x10008D39 }; template class QAutoClose { public: QAutoClose(R& aObj) : mPtr(&aObj) {} ~QAutoClose() { if (mPtr) mPtr->Close(); } void Forget() { mPtr = 0; } private: QAutoClose(const QAutoClose&); QAutoClose& operator=(const QAutoClose&); private: R* mPtr; }; #ifdef USE_DOCUMENTHANDLER class QS60DocumentHandler : public MAknServerAppExitObserver { public: QS60DocumentHandler() :docHandler(0) {} ~QS60DocumentHandler() { delete docHandler; } CDocumentHandler& documentHandler() { // In case user calls openUrl twice subsequently, before the first embedded app is closed // we use the same CDocumentHandler instance. Using same instance makes sure the first // launched embedded app is closed and latter one gets embedded to our app. // Using different instance would help only theoretically since user cannot interact with // several embedded apps at the same time. if(!docHandler) { QT_TRAP_THROWING(docHandler = CDocumentHandler::NewL()); docHandler->SetExitObserver(this); } return *docHandler; } private: // From MAknServerAppExitObserver void HandleServerAppExit(TInt /*aReason*/) { delete docHandler; docHandler = 0; } private: CDocumentHandler* docHandler; }; Q_GLOBAL_STATIC(QS60DocumentHandler, qt_s60_documenthandler); #endif static void handleMailtoSchemeLX(const QUrl &url) { // this function has many intermingled leaves and throws. Qt and Symbian objects do not have // destructor dependencies, and cleanup object is used to prevent cleanup stack dependency on stack. QString recipient = url.path(); QString subject = url.queryItemValue(QLatin1String("subject")); QString body = url.queryItemValue(QLatin1String("body")); QString to = url.queryItemValue(QLatin1String("to")); QString cc = url.queryItemValue(QLatin1String("cc")); QString bcc = url.queryItemValue(QLatin1String("bcc")); // these fields might have comma separated addresses QStringList recipients = recipient.split(QLatin1String(","), QString::SkipEmptyParts); QStringList tos = to.split(QLatin1String(","), QString::SkipEmptyParts); QStringList ccs = cc.split(QLatin1String(","), QString::SkipEmptyParts); QStringList bccs = bcc.split(QLatin1String(","), QString::SkipEmptyParts); RSendAs sendAs; User::LeaveIfError(sendAs.Connect()); QAutoClose sendAsCleanup(sendAs); CSendAsAccounts* accounts = CSendAsAccounts::NewL(); CleanupStack::PushL(accounts); sendAs.AvailableAccountsL(KUidMsgTypeSMTP, *accounts); TInt count = accounts->Count(); CleanupStack::PopAndDestroy(accounts); if(!count) { // TODO: Task 259192: We should try to create account if count == 0 // CSendUi would provide account creation service for us, but it requires ridicilous // capabilities: LocalServices NetworkServices ReadDeviceData ReadUserData WriteDeviceData WriteUserData User::Leave(KErrNotSupported); } else { RSendAsMessage sendAsMessage; sendAsMessage.CreateL(sendAs, KUidMsgTypeSMTP); QAutoClose sendAsMessageCleanup(sendAsMessage); // Subject sendAsMessage.SetSubjectL(qt_QString2TPtrC(subject)); // Body sendAsMessage.SetBodyTextL(qt_QString2TPtrC(body)); // To foreach(QString item, recipients) sendAsMessage.AddRecipientL(qt_QString2TPtrC(item), RSendAsMessage::ESendAsRecipientTo); foreach(QString item, tos) sendAsMessage.AddRecipientL(qt_QString2TPtrC(item), RSendAsMessage::ESendAsRecipientTo); // Cc foreach(QString item, ccs) sendAsMessage.AddRecipientL(qt_QString2TPtrC(item), RSendAsMessage::ESendAsRecipientCc); // Bcc foreach(QString item, bccs) sendAsMessage.AddRecipientL(qt_QString2TPtrC(item), RSendAsMessage::ESendAsRecipientBcc); // send the message sendAsMessage.LaunchEditorAndCloseL(); // sendAsMessage is already closed sendAsMessageCleanup.Forget(); } } static bool handleMailtoScheme(const QUrl &url) { TRAPD(err, QT_TRYCATCH_LEAVING(handleMailtoSchemeLX(url))); return err ? false : true; } static void handleOtherSchemesL(const TDesC& aUrl) { // Other schemes are at the moment passed to WEB browser HBufC* buf16 = HBufC::NewLC(aUrl.Length() + KBrowserPrefix.iTypeLength); buf16->Des().Copy(KBrowserPrefix); // Prefix used to launch correct browser view buf16->Des().Append(aUrl); TApaTaskList taskList(CEikonEnv::Static()->WsSession()); TApaTask task = taskList.FindApp(KUidBrowser); if (task.Exists()){ // Switch to existing browser instance task.BringToForeground(); HBufC8* param8 = HBufC8::NewLC(buf16->Length()); param8->Des().Append(buf16->Des()); task.SendMessage(TUid::Uid( 0 ), *param8); // Uid is not used CleanupStack::PopAndDestroy(param8); } else { // Start a new browser instance RApaLsSession appArcSession; User::LeaveIfError(appArcSession.Connect()); CleanupClosePushL(appArcSession); TThreadId id; appArcSession.StartDocument(*buf16, KUidBrowser, id); CleanupStack::PopAndDestroy(); // appArcSession } CleanupStack::PopAndDestroy(buf16); } static bool handleOtherSchemes(const QUrl &url) { QString encUrl(QString::fromUtf8(url.toEncoded())); TPtrC urlPtr(qt_QString2TPtrC(encUrl)); TRAPD( err, handleOtherSchemesL(urlPtr)); return err ? false : true; } static void openDocumentL(const TDesC& aUrl) { #ifndef USE_DOCUMENTHANDLER // Start app associated to file MIME type by using RApaLsSession // Apparc base method cannot be used to open app in embedded mode, // but seems to be most stable way at the moment RApaLsSession appArcSession; User::LeaveIfError(appArcSession.Connect()); CleanupClosePushL(appArcSession); TThreadId id; // ESwitchFiles means do not start another instance // Leaves if file does not exist, leave is trapped in openDocument and false returned to user. User::LeaveIfError(appArcSession.StartDocument(aUrl, id, RApaLsSession::ESwitchFiles)); // ELaunchNewApp CleanupStack::PopAndDestroy(); // appArcSession #else // This is an alternative way to launch app associated to MIME type // CDocumentHandler also supports opening apps in embedded mode. TDataType temp; qt_s60_documenthandler()->documentHandler().OpenFileEmbeddedL(aUrl, temp); #endif } static bool launchWebBrowser(const QUrl &url) { if (!url.isValid()) return false; if (url.scheme() == QLatin1String("mailto")) { return handleMailtoScheme(url); } return handleOtherSchemes( url ); } static bool openDocument(const QUrl &file) { if (!file.isValid()) return false; QString filePath = file.toLocalFile(); filePath = QDir::toNativeSeparators(filePath); TPtrC filePathPtr(qt_QString2TPtrC(filePath)); TRAPD(err, openDocumentL(filePathPtr)); return err ? false : true; } #else //USE_SCHEMEHANDLER // The schemehandler component only exist in private SDK. This implementation // exist here just for convenience in case that we need to use it later on // The schemehandle based implementation is not yet tested. // The biggest advantage of schemehandler is that it can handle // wide range of schemes and is extensible by plugins static void handleUrlL(const TDesC& aUrl) { CSchemeHandler* schemeHandler = CSchemeHandler::NewL(aUrl); CleanupStack::PushL(schemeHandler); schemeHandler->HandleUrlStandaloneL(); // Process the Url in standalone mode CleanupStack::PopAndDestroy(); } static bool handleUrl(const QUrl &url) { if (!url.isValid()) return false; QString urlString(url.toEncoded()); TPtrC urlPtr(qt_QString2TPtrC(urlString)); TRAPD( err, handleUrlL(urlPtr)); return err ? false : true; } static bool launchWebBrowser(const QUrl &url) { return handleUrl(url); } static bool openDocument(const QUrl &file) { return handleUrl(file); } #endif //USE_SCHEMEHANDLER QT_END_NAMESPACE