1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42
43 #include <qcoreapplication.h>
44 #include <qdir.h>
45 #include <qurl.h>
46 #include <private/qcore_symbian_p.h>
47
48 #include <f32file.h> // TDriveUnit etc
49 #include <pathinfo.h> // PathInfo
50
51 #ifndef USE_SCHEMEHANDLER
52 #ifdef Q_WS_S60
53 // This flag changes the implementation to use S60 CDcoumentHandler
54 // instead of apparc when opening the files
55 #define USE_DOCUMENTHANDLER
56 #endif
57
58 #include <txtrich.h> // CRichText
59 #include <eikenv.h> // CEikonEnv
60 #include <apgcli.h> // RApaLsSession
61 #include <apgtask.h> // TApaTaskList, TApaTask
62 #include <rsendas.h> // RSendAs
63 #include <rsendasmessage.h> // RSendAsMessage
64
65 #ifdef USE_DOCUMENTHANDLER
66 #include <DocumentHandler.h> // CDocumentHandler
67 #include <AknServerApp.h>
68 #endif
69 #else // USE_SCHEMEHANDLER
70 #include <schemehandler.h>
71 #endif
72
73 #ifdef Q_WS_S60
74 #include <CDirectoryLocalizer.h> // CDirectoryLocalizer
75 #endif
76
77 QT_BEGIN_NAMESPACE
78
79 _LIT(KCacheSubDir, "Cache\\");
80 _LIT(KSysBin, "\\Sys\\Bin\\");
81 _LIT(KBrowserPrefix, "4 " );
82 _LIT(KFontsDir, "z:\\resource\\Fonts\\");
83
84 #ifndef USE_SCHEMEHANDLER
85 // copied from miutset.h, so we don't get a dependency into the app layer
86 const TUid KUidMsgTypeSMTP = {0x10001028}; // 268439592
87 const TUid KUidBrowser = { 0x10008D39 };
88
89 template<class R>
90 class QAutoClose
91 {
92 public:
QAutoClose(R & aObj)93 QAutoClose(R& aObj) : mPtr(&aObj) {}
~QAutoClose()94 ~QAutoClose()
95 {
96 if (mPtr)
97 mPtr->Close();
98 }
Forget()99 void Forget()
100 {
101 mPtr = 0;
102 }
103 private:
104 QAutoClose(const QAutoClose&);
105 QAutoClose& operator=(const QAutoClose&);
106 private:
107 R* mPtr;
108 };
109
110 #ifdef USE_DOCUMENTHANDLER
111 class QS60DocumentHandler : public MAknServerAppExitObserver
112 {
113 public:
QS60DocumentHandler()114 QS60DocumentHandler() :docHandler(0) {}
115
~QS60DocumentHandler()116 ~QS60DocumentHandler() {
117 delete docHandler;
118 }
119
documentHandler()120 CDocumentHandler& documentHandler() {
121 // In case user calls openUrl twice subsequently, before the first embedded app is closed
122 // we use the same CDocumentHandler instance. Using same instance makes sure the first
123 // launched embedded app is closed and latter one gets embedded to our app.
124 // Using different instance would help only theoretically since user cannot interact with
125 // several embedded apps at the same time.
126 if(!docHandler) {
127 QT_TRAP_THROWING(docHandler = CDocumentHandler::NewL());
128 docHandler->SetExitObserver(this);
129 }
130 return *docHandler;
131 }
132
133 private: // From MAknServerAppExitObserver
HandleServerAppExit(TInt)134 void HandleServerAppExit(TInt /*aReason*/) {
135 delete docHandler;
136 docHandler = 0;
137 }
138
139 private:
140 CDocumentHandler* docHandler;
141 };
142 Q_GLOBAL_STATIC(QS60DocumentHandler, qt_s60_documenthandler);
143 #endif
144
handleMailtoSchemeLX(const QUrl & url)145 static void handleMailtoSchemeLX(const QUrl &url)
146 {
147 // this function has many intermingled leaves and throws. Qt and Symbian objects do not have
148 // destructor dependencies, and cleanup object is used to prevent cleanup stack dependency on stack.
149 QString recipient = url.path();
150 QString subject = url.queryItemValue(QLatin1String("subject"));
151 QString body = url.queryItemValue(QLatin1String("body"));
152 QString to = url.queryItemValue(QLatin1String("to"));
153 QString cc = url.queryItemValue(QLatin1String("cc"));
154 QString bcc = url.queryItemValue(QLatin1String("bcc"));
155
156 // these fields might have comma separated addresses
157 QStringList recipients = recipient.split(QLatin1String(","), QString::SkipEmptyParts);
158 QStringList tos = to.split(QLatin1String(","), QString::SkipEmptyParts);
159 QStringList ccs = cc.split(QLatin1String(","), QString::SkipEmptyParts);
160 QStringList bccs = bcc.split(QLatin1String(","), QString::SkipEmptyParts);
161
162 RSendAs sendAs;
163 User::LeaveIfError(sendAs.Connect());
164 QAutoClose<RSendAs> sendAsCleanup(sendAs);
165
166 CSendAsAccounts* accounts = CSendAsAccounts::NewL();
167 CleanupStack::PushL(accounts);
168 sendAs.AvailableAccountsL(KUidMsgTypeSMTP, *accounts);
169 TInt count = accounts->Count();
170 CleanupStack::PopAndDestroy(accounts);
171
172 if(!count) {
173 // TODO: Task 259192: We should try to create account if count == 0
174 // CSendUi would provide account creation service for us, but it requires ridicilous
175 // capabilities: LocalServices NetworkServices ReadDeviceData ReadUserData WriteDeviceData WriteUserData
176 User::Leave(KErrNotSupported);
177 } else {
178 RSendAsMessage sendAsMessage;
179 sendAsMessage.CreateL(sendAs, KUidMsgTypeSMTP);
180 QAutoClose<RSendAsMessage> sendAsMessageCleanup(sendAsMessage);
181
182
183 // Subject
184 sendAsMessage.SetSubjectL(qt_QString2TPtrC(subject));
185
186 // Body
187 sendAsMessage.SetBodyTextL(qt_QString2TPtrC(body));
188
189 // To
190 foreach(QString item, recipients)
191 sendAsMessage.AddRecipientL(qt_QString2TPtrC(item), RSendAsMessage::ESendAsRecipientTo);
192
193 foreach(QString item, tos)
194 sendAsMessage.AddRecipientL(qt_QString2TPtrC(item), RSendAsMessage::ESendAsRecipientTo);
195
196 // Cc
197 foreach(QString item, ccs)
198 sendAsMessage.AddRecipientL(qt_QString2TPtrC(item), RSendAsMessage::ESendAsRecipientCc);
199
200 // Bcc
201 foreach(QString item, bccs)
202 sendAsMessage.AddRecipientL(qt_QString2TPtrC(item), RSendAsMessage::ESendAsRecipientBcc);
203
204 // send the message
205 sendAsMessage.LaunchEditorAndCloseL();
206 // sendAsMessage is already closed
207 sendAsMessageCleanup.Forget();
208 }
209 }
210
handleMailtoScheme(const QUrl & url)211 static bool handleMailtoScheme(const QUrl &url)
212 {
213 TRAPD(err, QT_TRYCATCH_LEAVING(handleMailtoSchemeLX(url)));
214 return err ? false : true;
215 }
216
handleOtherSchemesL(const TDesC & aUrl)217 static void handleOtherSchemesL(const TDesC& aUrl)
218 {
219 // Other schemes are at the moment passed to WEB browser
220 HBufC* buf16 = HBufC::NewLC(aUrl.Length() + KBrowserPrefix.iTypeLength);
221 buf16->Des().Copy(KBrowserPrefix); // Prefix used to launch correct browser view
222 buf16->Des().Append(aUrl);
223
224 TApaTaskList taskList(CEikonEnv::Static()->WsSession());
225 TApaTask task = taskList.FindApp(KUidBrowser);
226 if (task.Exists()){
227 // Switch to existing browser instance
228 task.BringToForeground();
229 HBufC8* param8 = HBufC8::NewLC(buf16->Length());
230 param8->Des().Append(buf16->Des());
231 task.SendMessage(TUid::Uid( 0 ), *param8); // Uid is not used
232 CleanupStack::PopAndDestroy(param8);
233 } else {
234 // Start a new browser instance
235 RApaLsSession appArcSession;
236 User::LeaveIfError(appArcSession.Connect());
237 CleanupClosePushL<RApaLsSession>(appArcSession);
238 TThreadId id;
239 appArcSession.StartDocument(*buf16, KUidBrowser, id);
240 CleanupStack::PopAndDestroy(); // appArcSession
241 }
242
243 CleanupStack::PopAndDestroy(buf16);
244 }
245
handleOtherSchemes(const QUrl & url)246 static bool handleOtherSchemes(const QUrl &url)
247 {
248 QString encUrl(QString::fromUtf8(url.toEncoded()));
249 TPtrC urlPtr(qt_QString2TPtrC(encUrl));
250 TRAPD( err, handleOtherSchemesL(urlPtr));
251 return err ? false : true;
252 }
253
254
openDocumentL(const TDesC & aUrl)255 static void openDocumentL(const TDesC& aUrl)
256 {
257 #ifndef USE_DOCUMENTHANDLER
258 // Start app associated to file MIME type by using RApaLsSession
259 // Apparc base method cannot be used to open app in embedded mode,
260 // but seems to be most stable way at the moment
261 RApaLsSession appArcSession;
262 User::LeaveIfError(appArcSession.Connect());
263 CleanupClosePushL<RApaLsSession>(appArcSession);
264 TThreadId id;
265 // ESwitchFiles means do not start another instance
266 // Leaves if file does not exist, leave is trapped in openDocument and false returned to user.
267 User::LeaveIfError(appArcSession.StartDocument(aUrl, id,
268 RApaLsSession::ESwitchFiles)); // ELaunchNewApp
269 CleanupStack::PopAndDestroy(); // appArcSession
270 #else
271 // This is an alternative way to launch app associated to MIME type
272 // CDocumentHandler also supports opening apps in embedded mode.
273 TDataType temp;
274 qt_s60_documenthandler()->documentHandler().OpenFileEmbeddedL(aUrl, temp);
275 #endif
276 }
277
launchWebBrowser(const QUrl & url)278 static bool launchWebBrowser(const QUrl &url)
279 {
280 if (!url.isValid())
281 return false;
282
283 if (url.scheme() == QLatin1String("mailto")) {
284 return handleMailtoScheme(url);
285 }
286 return handleOtherSchemes( url );
287 }
288
openDocument(const QUrl & file)289 static bool openDocument(const QUrl &file)
290 {
291 if (!file.isValid())
292 return false;
293
294 QString filePath = file.toLocalFile();
295 filePath = QDir::toNativeSeparators(filePath);
296 TPtrC filePathPtr(qt_QString2TPtrC(filePath));
297 TRAPD(err, openDocumentL(filePathPtr));
298 return err ? false : true;
299 }
300
301 #else //USE_SCHEMEHANDLER
302 // The schemehandler component only exist in private SDK. This implementation
303 // exist here just for convenience in case that we need to use it later on
304 // The schemehandle based implementation is not yet tested.
305
306 // The biggest advantage of schemehandler is that it can handle
307 // wide range of schemes and is extensible by plugins
handleUrlL(const TDesC & aUrl)308 static void handleUrlL(const TDesC& aUrl)
309 {
310 CSchemeHandler* schemeHandler = CSchemeHandler::NewL(aUrl);
311 CleanupStack::PushL(schemeHandler);
312 schemeHandler->HandleUrlStandaloneL(); // Process the Url in standalone mode
313 CleanupStack::PopAndDestroy();
314 }
315
handleUrl(const QUrl & url,bool useEncodedUrl)316 static bool handleUrl(const QUrl &url, bool useEncodedUrl)
317 {
318 if (!url.isValid())
319 return false;
320
321 QString urlString(useEncodedUrl ? url.toEncoded() : url.toString());
322 TPtrC urlPtr(qt_QString2TPtrC(urlString));
323 TRAPD( err, handleUrlL(urlPtr));
324 return err ? false : true;
325 }
326
launchWebBrowser(const QUrl & url)327 static bool launchWebBrowser(const QUrl &url)
328 {
329 return handleUrl(url, true);
330 }
331
openDocument(const QUrl & file)332 static bool openDocument(const QUrl &file)
333 {
334 return handleUrl(file, false);
335 }
336
337 #endif //USE_SCHEMEHANDLER
338
339 // Common functions to all implementations
340
exeDrive()341 static TDriveUnit exeDrive()
342 {
343 RProcess me;
344 TFileName processFileName = me.FileName();
345 TDriveUnit drive(processFileName);
346 return drive;
347 }
348
writableExeDrive()349 static TDriveUnit writableExeDrive()
350 {
351 TDriveUnit drive = exeDrive();
352 if (drive.operator TInt() == EDriveZ)
353 return TDriveUnit(EDriveC);
354 return drive;
355 }
356
writableDataRoot()357 static TPtrC writableDataRoot()
358 {
359 TDriveUnit drive = exeDrive();
360 switch (drive.operator TInt()){
361 case EDriveC:
362 return PathInfo::PhoneMemoryRootPath();
363 break;
364 case EDriveE:
365 return PathInfo::MemoryCardRootPath();
366 break;
367 case EDriveZ:
368 // It is not possible to write on ROM drive ->
369 // return phone mem root path instead
370 return PathInfo::PhoneMemoryRootPath();
371 break;
372 default:
373 return PathInfo::PhoneMemoryRootPath();
374 break;
375 }
376 }
377
storageLocation(StandardLocation type)378 QString QDesktopServices::storageLocation(StandardLocation type)
379 {
380 TFileName path;
381
382 switch (type) {
383 case DesktopLocation:
384 qWarning("No desktop concept in Symbian OS");
385 // But lets still use some feasible default
386 path.Append(writableDataRoot());
387 break;
388 case DocumentsLocation:
389 path.Append(writableDataRoot());
390 break;
391 case FontsLocation:
392 path.Append(KFontsDir);
393 break;
394 case ApplicationsLocation:
395 path.Append(exeDrive().Name());
396 path.Append(KSysBin);
397 break;
398 case MusicLocation:
399 path.Append(writableDataRoot());
400 path.Append(PathInfo::SoundsPath());
401 break;
402 case MoviesLocation:
403 path.Append(writableDataRoot());
404 path.Append(PathInfo::VideosPath());
405 break;
406 case PicturesLocation:
407 path.Append(writableDataRoot());
408 path.Append(PathInfo::ImagesPath());
409 break;
410 case TempLocation:
411 return QDir::tempPath();
412 break;
413 case HomeLocation:
414 path.Append(writableDataRoot());
415 //return QDir::homePath(); break;
416 break;
417 case DataLocation:
418 qt_s60GetRFs().PrivatePath(path);
419 path.Insert(0, writableExeDrive().Name());
420 break;
421 case CacheLocation:
422 qt_s60GetRFs().PrivatePath(path);
423 path.Insert(0, writableExeDrive().Name());
424 path.Append(KCacheSubDir);
425 break;
426 default:
427 // Lets use feasible default
428 path.Append(writableDataRoot());
429 break;
430 }
431
432 // Convert to cross-platform format and clean the path
433 QString nativePath = QString::fromUtf16(path.Ptr(), path.Length());
434 QString qtPath = QDir::fromNativeSeparators(nativePath);
435 qtPath = QDir::cleanPath(qtPath);
436
437 // Note: The storage location returned can be a directory that does not exist;
438 // i.e., it may need to be created by the system or the user.
439 return qtPath;
440 }
441
442 typedef QString (*LocalizerFunc)(QString&);
443
defaultLocalizedDirectoryName(QString &)444 static QString defaultLocalizedDirectoryName(QString&)
445 {
446 return QString();
447 }
448
displayName(StandardLocation type)449 QString QDesktopServices::displayName(StandardLocation type)
450 {
451 QString ret;
452
453 #ifdef Q_WS_S60
454 QString rawPath = storageLocation(type);
455
456 TRAPD(err,
457 QT_TRYCATCH_LEAVING(
458 CDirectoryLocalizer* localizer = CDirectoryLocalizer::NewL();
459 CleanupStack::PushL(localizer);
460 localizer->SetFullPath(qt_QString2TPtrC(QDir::toNativeSeparators(rawPath)));
461 if (localizer->IsLocalized()) {
462 TPtrC locName(localizer->LocalizedName());
463 ret = qt_TDesC2QString(locName);
464 }
465 CleanupStack::PopAndDestroy(localizer);
466 )
467 )
468
469 if (err != KErrNone)
470 ret = QString();
471 #else
472 qWarning("QDesktopServices::displayName() not implemented for this platform version");
473 #endif
474
475 return ret;
476 }
477
478 QT_END_NAMESPACE
479