|
1 /* |
|
2 * Copyright (c) 2005 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: Push support |
|
15 * |
|
16 */ |
|
17 |
|
18 |
|
19 #include "EcmtMenubar.h" |
|
20 #include "EcmtWin32.h" |
|
21 #include "httpfile.h" |
|
22 |
|
23 #include <e32base.h> |
|
24 #include <apgtask.h> |
|
25 #include <apgcli.h> |
|
26 #include <vwsdef.h> |
|
27 #include <viewcli.h> |
|
28 #include <cmanobserver.h> |
|
29 #include <e32property.h> |
|
30 #include <PSVariables.h> |
|
31 #include <EscapeUtils.h> |
|
32 #include <emulator.h> |
|
33 #include <sacls.h> |
|
34 #include <msvids.h> |
|
35 #include <msvstd.hrh> |
|
36 #include <mmssettings.h> |
|
37 #include <PushLog.h> |
|
38 #include <PushMessage.h> |
|
39 #include <PushDispatcher.h> |
|
40 #include <CPushHandlerBase.h> |
|
41 |
|
42 // We define CWatcherLog locally because CWatcherLog::NewL is not |
|
43 // exported from watcher.dll |
|
44 #undef IMPORT_C |
|
45 #define IMPORT_C |
|
46 #include <watcher.h> |
|
47 |
|
48 // MACROS |
|
49 #define __S8(x) _S8(x) |
|
50 |
|
51 // Some view ids |
|
52 #define KMessagingUid TUid::Uid(0x100058C5) |
|
53 #define KMessagingCentreMainViewUid TVwsViewId(KMessagingUid,TUid::Uid(0x01)) |
|
54 #define KMessagingCentreInboxView TVwsViewId(KMessagingUid,TUid::Uid(0x02)) |
|
55 |
|
56 // Known push content types |
|
57 typedef enum _TPushMessageType { |
|
58 EWapPush, |
|
59 EMultipart, |
|
60 EMmsMessage |
|
61 } TPushMessageType; |
|
62 |
|
63 typedef struct _PushType { |
|
64 const TText* ext; |
|
65 const TText8* contentType; |
|
66 TPushMessageType msgType; |
|
67 TUint8 wapContentType; |
|
68 } PushType; |
|
69 |
|
70 // WAP encoded content types must have 0x80 bit set |
|
71 const TUint8 WAP_MP_MIX = 0xA3; // application/vnd.wap.multipart.mixed |
|
72 const TUint8 WAP_MP_ALT = 0xA6; // application/vnd.wap.multipart.alternative |
|
73 const TUint8 WAP_MP_REL = 0xB3; // application/vnd.wap.multipart.related |
|
74 static const PushType pushTypes[] = { |
|
75 {_S("sic"),__S8("application/vnd.wap.sic"), EWapPush, 0xAE}, |
|
76 {_S("slc"),__S8("application/vnd.wap.slc"), EWapPush, 0xB0}, |
|
77 {_S("coc"),__S8("application/vnd.wap.coc"), EWapPush, 0xB2}, |
|
78 {_S("mms"),__S8("application/vnd.wap.mms-message"), EMmsMessage, 0}, |
|
79 {_S("mpc"),__S8("application/vnd.wap.multipart.mixed"), EMultipart, WAP_MP_MIX}, |
|
80 {_S("mpc"),__S8("application/vnd.wap.multipart.related"), EMultipart, WAP_MP_REL}, |
|
81 {_S("mpc"),__S8("application/vnd.wap.multipart.alternative"), EMultipart, WAP_MP_ALT} |
|
82 }; |
|
83 |
|
84 // -------------------------------------------------------------------------- |
|
85 // This class implements MWapPushLog and MConnManObserver interfaces |
|
86 // -------------------------------------------------------------------------- |
|
87 |
|
88 class CEcmtMenubar::CWapPushSupport : |
|
89 public MWapPushLog, |
|
90 public MConnManObserver |
|
91 { |
|
92 public: |
|
93 // MWapPushLog |
|
94 void WPLPrintf(const TDesC&); |
|
95 void WPLPrintfL(CPushMessage&); |
|
96 void WPLLogBinaryAsHex(const TDesC&); |
|
97 void WPLLogError(const TDesC&,TInt ); |
|
98 |
|
99 // MConnManObserver |
|
100 void CMOpenConnectionL(TPushConnPoint&); |
|
101 void CMWatcherComplete(CCOWatcherBase&, TInt); |
|
102 }; |
|
103 |
|
104 // MWapPushLog |
|
105 void CEcmtMenubar::CWapPushSupport::WPLPrintf(const TDesC&) {} |
|
106 void CEcmtMenubar::CWapPushSupport::WPLPrintfL(CPushMessage&) {} |
|
107 void CEcmtMenubar::CWapPushSupport::WPLLogBinaryAsHex(const TDesC&) {} |
|
108 void CEcmtMenubar::CWapPushSupport::WPLLogError(const TDesC&,TInt ) {} |
|
109 |
|
110 // MConnManObserver |
|
111 void CEcmtMenubar::CWapPushSupport::CMOpenConnectionL(TPushConnPoint&) {} |
|
112 void CEcmtMenubar::CWapPushSupport::CMWatcherComplete(CCOWatcherBase&,TInt) {} |
|
113 |
|
114 // -------------------------------------------------------------------------- |
|
115 // CWatcherLog |
|
116 // -------------------------------------------------------------------------- |
|
117 CWatcherLog* CWatcherLog::NewL(RFs& aFs) |
|
118 { |
|
119 CWatcherLog* self = new(ELeave)CWatcherLog(aFs); |
|
120 CleanupStack::PushL(self); |
|
121 self->ConstructL(); |
|
122 CleanupStack::Pop(); |
|
123 return self; |
|
124 } |
|
125 |
|
126 CWatcherLog::CWatcherLog(RFs& aFs) : CBase(), iFs(aFs) |
|
127 { |
|
128 } |
|
129 |
|
130 CWatcherLog::~CWatcherLog() |
|
131 { |
|
132 } |
|
133 |
|
134 void CWatcherLog::ConstructL() |
|
135 { |
|
136 } |
|
137 |
|
138 void CWatcherLog::Printf(TRefByValue<const TDesC16>,...) |
|
139 { |
|
140 } |
|
141 |
|
142 void CWatcherLog::Printf(TRefByValue<const TDesC8>,...) |
|
143 { |
|
144 } |
|
145 |
|
146 TBool CWatcherLog::IsLogging() const |
|
147 { |
|
148 return EFalse; |
|
149 } |
|
150 |
|
151 // -------------------------------------------------------------------------- |
|
152 // Some obscure code |
|
153 // -------------------------------------------------------------------------- |
|
154 |
|
155 inline void LaunchViewL(const TVwsViewId& aViewId, |
|
156 TUid aCustomMessageId, |
|
157 const TDesC8& aCustomMessage) |
|
158 { |
|
159 CVwsSessionWrapper* view = CVwsSessionWrapper::NewLC(); |
|
160 view->CreateActivateViewEvent(aViewId, aCustomMessageId, aCustomMessage); |
|
161 CleanupStack::PopAndDestroy(view); |
|
162 } |
|
163 |
|
164 inline void LaunchMmsViewL() |
|
165 { |
|
166 LaunchViewL(KMessagingCentreInboxView, |
|
167 TUid::Uid(KMsvGlobalInBoxIndexEntryIdValue), |
|
168 KNullDesC8()); |
|
169 } |
|
170 |
|
171 inline void LaunchPushViewL() |
|
172 { |
|
173 // The same view handles Wap Push messages as well as MMS messages |
|
174 LaunchMmsViewL(); |
|
175 } |
|
176 |
|
177 inline void InboxStatusNotifyL() |
|
178 { |
|
179 TInt r = RProperty::Define(KUidSystemCategory, |
|
180 KUidInboxStatus.iUid, |
|
181 RProperty::EInt); |
|
182 |
|
183 if (r == KErrNone) |
|
184 { |
|
185 RProperty::Set(KUidSystemCategory, |
|
186 KUidInboxStatus.iUid, |
|
187 ESADocumentsInInbox ); |
|
188 } |
|
189 } |
|
190 |
|
191 // -------------------------------------------------------------------------- |
|
192 // MMS support |
|
193 // -------------------------------------------------------------------------- |
|
194 |
|
195 /** |
|
196 * Reads the location of the mmsin directory from localmode.ini |
|
197 * This is a Java-style property file, only in Unicode. |
|
198 * See CMmsWatcher::ReadLocalModeConfigData() method in |
|
199 * S60\mmsengine\mmswatcher\src\mmswatcher.cpp |
|
200 * The algorithm is reproduced here, with all its problems, |
|
201 * because we want it to exactly match what CMmsWatcher does |
|
202 */ |
|
203 HBufC* CEcmtMenubar::ReadMmsInDir(RFs& aFs) |
|
204 { |
|
205 HBufC* mmsInBuf = NULL; |
|
206 RFileReadStream reader; |
|
207 |
|
208 // Actually, if localmode.ini file does not exist, the MMS watcher |
|
209 // does not poll the mmsin directory and everything we are doing |
|
210 // here is useless. Should we create localmode.ini file if it |
|
211 // doesn't exist? But then SDK would have to be restarted because |
|
212 // MMS watcher only attempts to read this file on startup... Hmm. |
|
213 TInt err = reader.Open(aFs,KMmsLocalModeConfigFile,EFileShareReadersOnly); |
|
214 if (err == KErrNone) |
|
215 { |
|
216 TChar delim = 0x000A; |
|
217 TBuf<128> line; |
|
218 while (err == KErrNone) |
|
219 { |
|
220 TRAP(err, reader.ReadL( line, delim ) ); |
|
221 if (err == KErrNone) |
|
222 { |
|
223 TInt length = line.Length(); |
|
224 if (length > 2) |
|
225 { |
|
226 // Check for comment line |
|
227 if (line[0] != '#') |
|
228 { |
|
229 // Check for byte order mark |
|
230 if (line[0] == 0xFEFF) |
|
231 { |
|
232 line.Delete( 0, 1 ); |
|
233 length = line.Length(); |
|
234 } |
|
235 // Drop CR+LF from the end of line |
|
236 line.Delete(length-2, 2); |
|
237 TInt sep = line.Locate('='); |
|
238 if (sep > 0) |
|
239 { |
|
240 TPtrC key = line.Left(sep); |
|
241 if (key.CompareF(KMmsLocalmodeInDirectory) == 0) |
|
242 { |
|
243 mmsInBuf = line.Mid(sep+1).Alloc(); |
|
244 break; |
|
245 } |
|
246 } |
|
247 } |
|
248 } |
|
249 } |
|
250 } |
|
251 reader.Close(); |
|
252 } |
|
253 return mmsInBuf; |
|
254 } |
|
255 |
|
256 // Returns reference to the mmsin directory |
|
257 const TDesC& CEcmtMenubar::MmmInDirL() |
|
258 { |
|
259 if (!iMmsInDir) |
|
260 { |
|
261 iMmsInDirBuf = ReadMmsInDir(FsSessionL()); |
|
262 if (iMmsInDirBuf) |
|
263 { |
|
264 iMmsInDir = iMmsInDirBuf; |
|
265 } |
|
266 else |
|
267 { |
|
268 iMmsInDir = &KMmsDefaultLocalModeDir; |
|
269 } |
|
270 } |
|
271 return *iMmsInDir; |
|
272 } |
|
273 |
|
274 // Simulates an MMS message |
|
275 void CEcmtMenubar::SimulateMmsL(const TDesC8& aMessageBody) |
|
276 { |
|
277 // First copy the file to the phone's file system |
|
278 const TDesC& mmsDir = MmmInDirL(); |
|
279 RFs& fs = FsSessionL(); |
|
280 |
|
281 // Make sure the directory exists |
|
282 TUint attr = 0; |
|
283 if (fs.Att(mmsDir,attr) != KErrNone || !(attr & KEntryAttDir)) |
|
284 { |
|
285 fs.Delete(mmsDir); // Ignore errors |
|
286 User::LeaveIfError(fs.MkDir(mmsDir)); |
|
287 } |
|
288 |
|
289 // Create a temporary file. The extension must be .mms |
|
290 RFile file; |
|
291 TInt err = KErrAlreadyExists; |
|
292 for (TInt i=0; err == KErrAlreadyExists && i<1000; i++) |
|
293 { |
|
294 _LIT(KMmsFilePrefix,"TMP"); |
|
295 _LIT(KMmsExt,".mms"); |
|
296 TFileName name; |
|
297 name.Copy(mmsDir); |
|
298 name.Append(KMmsFilePrefix()); |
|
299 name.AppendFormat(_L("%04d"),i); |
|
300 name.Append(KMmsExt()); |
|
301 err = file.Create(fs,name,EFileWrite); |
|
302 } |
|
303 |
|
304 // Write the file |
|
305 User::LeaveIfError(err); |
|
306 err = file.Write(aMessageBody); |
|
307 file.Close(); |
|
308 User::LeaveIfError(err); |
|
309 |
|
310 // Open the Messaging application |
|
311 InboxStatusNotifyL(); |
|
312 LaunchMmsViewL(); |
|
313 } |
|
314 |
|
315 // -------------------------------------------------------------------------- |
|
316 // Push support |
|
317 // -------------------------------------------------------------------------- |
|
318 |
|
319 /** |
|
320 * Returns a reference to CWatcherLog. |
|
321 */ |
|
322 CWatcherLog& CEcmtMenubar::WatcherLogL() |
|
323 { |
|
324 if (!iWatcherLog) |
|
325 { |
|
326 iWatcherLog = CWatcherLog::NewL(FsSessionL()); |
|
327 } |
|
328 return *iWatcherLog; |
|
329 } |
|
330 |
|
331 /** |
|
332 * Wap push simulator. Takes care of destroying CPushMessage |
|
333 */ |
|
334 void CEcmtMenubar::SimulateWapPushL(CPushMessage* aPushMessage) |
|
335 { |
|
336 TPtrC8 rAppURI; |
|
337 TInt rAppID; |
|
338 TBool rIsAnInt; |
|
339 CPushHandlerBase* appHandlerPtr = NULL; |
|
340 |
|
341 CleanupStack::PushL(aPushMessage); |
|
342 if (!iWapPushSupport) iWapPushSupport = new(ELeave)CWapPushSupport; |
|
343 if (aPushMessage->GetAppIdL(rAppURI, rAppID, rIsAnInt)) |
|
344 { |
|
345 if(rIsAnInt) |
|
346 { |
|
347 appHandlerPtr = &PushAppDispatcher::GetHandlerL(rAppID, |
|
348 *iWapPushSupport, *iWapPushSupport); |
|
349 } |
|
350 else |
|
351 { |
|
352 appHandlerPtr = &PushAppDispatcher::GetHandlerL(rAppURI, |
|
353 *iWapPushSupport, *iWapPushSupport); |
|
354 } |
|
355 } |
|
356 else |
|
357 { // If no AppID defined, use the default User Agent |
|
358 appHandlerPtr= &PushAppDispatcher::GetHandlerL(KUserAgentAppHandler, |
|
359 *iWapPushSupport, *iWapPushSupport); |
|
360 } |
|
361 |
|
362 if (appHandlerPtr) |
|
363 { |
|
364 appHandlerPtr->HandleMessageL(aPushMessage); |
|
365 CleanupStack::Pop(aPushMessage); |
|
366 } |
|
367 else |
|
368 { |
|
369 CleanupStack::PopAndDestroy(aPushMessage); |
|
370 } |
|
371 |
|
372 // Show the indicator |
|
373 InboxStatusNotifyL(); |
|
374 LaunchPushViewL(); |
|
375 } |
|
376 |
|
377 |
|
378 // -------------------------------------------------------------------------- |
|
379 // The main routine that does all the Push/MMS work |
|
380 // -------------------------------------------------------------------------- |
|
381 |
|
382 /** |
|
383 * Detects whether the file is an MMS or a Wap Push message and |
|
384 * attempts to push it to the phone. Returns ETrue on success, |
|
385 * EFalse on failure or if the file does not seem to contain |
|
386 * push content. |
|
387 */ |
|
388 TBool CEcmtMenubar::LoadFileAsPushL(const TDesC& aFileName) |
|
389 { |
|
390 // First check if this is an HTTP response file. |
|
391 ParseHttpResponse* resp = NULL; |
|
392 const PushType* pushType = NULL; |
|
393 char* fname = STRING_ToMultiByteN(aFileName.Ptr(),aFileName.Length()); |
|
394 if (fname) |
|
395 { |
|
396 resp = HTTP_ParseFile(fname, HttpFalse); |
|
397 if (resp) |
|
398 { |
|
399 if (HTTP_ResponseStatus(resp) == HTTP_STATUS_CODE_OK) |
|
400 { |
|
401 const HttpContent* content = HTTP_ResponseContent(resp); |
|
402 if (content && content->type) |
|
403 { |
|
404 for (TInt i=0; i<COUNT(pushTypes) && !pushType; i++) |
|
405 { |
|
406 const char* type = (char*)pushTypes[i].contentType; |
|
407 if (!strcmp(type, content->type)) |
|
408 { |
|
409 pushType = pushTypes + i; |
|
410 } |
|
411 } |
|
412 } |
|
413 } |
|
414 if (!pushType) |
|
415 { |
|
416 HTTP_ResponseDelete(resp); |
|
417 resp = NULL; |
|
418 } |
|
419 } |
|
420 MEM_Free(fname); |
|
421 } |
|
422 |
|
423 // Now check if this file has one of the DRM extensions |
|
424 if (!pushType) |
|
425 { |
|
426 TPtrC fileExt(FileExt(aFileName)); |
|
427 for (TInt i=0; i<COUNT(pushTypes) && !pushType; i++) |
|
428 { |
|
429 if (fileExt.CompareF(TPtrC(pushTypes[i].ext)) == 0) { |
|
430 pushType = pushTypes + i; |
|
431 } |
|
432 } |
|
433 } |
|
434 |
|
435 // If we suspect that this is a push or MMS message, load the file |
|
436 // into a memory buffer |
|
437 HBufC8* msgBody = NULL; |
|
438 if (pushType) |
|
439 { |
|
440 if (resp) |
|
441 { |
|
442 const HttpContent* hc = HTTP_ResponseContent(resp); |
|
443 msgBody = HBufC8::New(hc->size); // No leaving, please |
|
444 if (msgBody) |
|
445 { |
|
446 msgBody->Des().Append(TPtrC8((TText8*)hc->data, hc->size)); |
|
447 } |
|
448 HTTP_ResponseDelete(resp); |
|
449 } |
|
450 else |
|
451 { |
|
452 msgBody = ReadExtFile(aFileName); |
|
453 } |
|
454 } |
|
455 |
|
456 if (msgBody && msgBody->Length() > 0) |
|
457 { |
|
458 // File has been loaded. Do something with it |
|
459 CleanupStack::PushL(msgBody); |
|
460 if (pushType->msgType == EWapPush || |
|
461 pushType->msgType == EMultipart) |
|
462 { |
|
463 HBufC8* msgHeader = NULL; |
|
464 TUint8 wapContentType = pushType->wapContentType; |
|
465 if (pushType->msgType == EMultipart) |
|
466 { |
|
467 const TUint8 * msgData = msgBody->Ptr(); |
|
468 TInt msgLen = msgBody->Length(); |
|
469 if (msgLen > 1 && msgData[0] >= 1 && msgData[0] < msgLen && ( |
|
470 msgData[1] == WAP_MP_MIX || |
|
471 msgData[1] == WAP_MP_ALT || |
|
472 msgData[1] == WAP_MP_REL)) |
|
473 { |
|
474 // Looks like multipart message contains encoded headers |
|
475 // Separate body from the headers |
|
476 HBufC8* msgBody2 = msgBody->Mid(msgData[0]+1, |
|
477 msgLen-msgData[0]-1).AllocLC(); |
|
478 msgHeader = msgBody->Mid(1,msgData[0]).AllocL(); |
|
479 |
|
480 // Switch to the new body |
|
481 CleanupStack::Pop(msgBody2); |
|
482 CleanupStack::PopAndDestroy(msgBody); |
|
483 msgBody = msgBody2; |
|
484 msgLen = msgBody->Length(); |
|
485 CleanupStack::PushL(msgBody); |
|
486 CleanupStack::PushL(msgHeader); |
|
487 } |
|
488 else |
|
489 { |
|
490 // Assume application/vnd.wap.multipart.mixed |
|
491 wapContentType = WAP_MP_MIX; |
|
492 } |
|
493 } |
|
494 |
|
495 if (!msgHeader) |
|
496 { |
|
497 // We have no headers, allocate a fake one with just |
|
498 // the content type |
|
499 msgHeader = HBufC8::NewLC(1); |
|
500 TPtr8 msgHeaderDes(msgHeader->Des()); |
|
501 msgHeaderDes.SetLength(1); |
|
502 msgHeaderDes[0] = wapContentType; |
|
503 } |
|
504 |
|
505 CPushMessage* msg = CPushMessage::NewL(msgHeader, msgBody); |
|
506 CleanupStack::Pop(msgHeader); |
|
507 CleanupStack::Pop(msgBody); |
|
508 SimulateWapPushL(msg); |
|
509 } |
|
510 else |
|
511 { |
|
512 SimulateMmsL(*msgBody); |
|
513 CleanupStack::PopAndDestroy(msgBody); |
|
514 } |
|
515 |
|
516 return ETrue; |
|
517 } |
|
518 return EFalse; |
|
519 } |
|
520 |
|
521 /** |
|
522 * Local Variables: |
|
523 * c-basic-offset: 4 |
|
524 * indent-tabs-mode: nil |
|
525 * End: |
|
526 */ |