Symbian端彩信读取初探上周由于项目需要对彩信读取进行了预研,虽然并未涉及彩信的拦截,只是对SymbianS60手机收件箱中的彩信和彩信通知的内容进行提取并分析,但是也算是趟了下之前一直没有搞过的彩信这浑水,小结一下。为了体现分析过程的逻辑性,下文并非是由简入繁,而是采用由已知到未知的说明过程来进行。彩信和彩信通知的区别正如前面博文《SymbianOS中的消息存储与常用操作》中提及的那样,手机中的各种消息都是以数据项(Entry)形式供程序操作,鉴于Nokia之前的SDK有众多API采用非公开的方式,很多东西碰上了只能拼命的试,试通了就算OK了,短信就是这个样子的,自从Cxt将发送短信的代码调通后,目前用的代码基本都是他那份原型。写了这么多只想说虽然手机中的各种消息都是以数据项形式给出的,但是数据项又由文件夹类型、消息类型、附件类型和服务类型的数据项,我们所指的消息如短信、彩信和邮箱乃至用户自定义的一些消息等等都是消息类型的数据项即KuidMsvMessageEntry类型。既然消息类型的数据项这么多,那么如何区分呢,这就靠各自不同的Mtm类型,由于SDK没有系统性阐述,我们虽然已经知道短信的类型是KUidMsgTypeSMS,但是彩信的Mtm类型值到底是什么,它跟彩信通知是否是有区分的,我们不得而知。没办法只能通过数据项的通用操作,对手机中已存的彩信和彩信通知进行读取后进行分析内容,才得知彩信和彩信通知是两个完全不同的Mtm类型值,一个是0x100058E1,另一个是0x100059C8,后来通过google搜索才知道前者的宏定义类型为KuidMsgTypeMultimedia,后者的宏定义类型为KUidMsgMMSNotification,两者在mmsconst.h头文件中有定义。同样使用通用的数据项操作,我们可以获取彩信和彩信通知消息数据项的消息存储CMsvStore和附件情况,两者的CMsvStore都是有数据的(SizeL()函数返回都是大于0的),但是却没有文本体(HasBodyTextL()返回都是Efalse);另外彩信都是有附件的,而彩信通知一般是没有附件的。因为附件本身也是数据项(KuidMsvAttachmentEntry类型的数据项),所以我们可以更进一步通过通用的数据项操作将彩信的所有附件全部都读取出来。而附件类型由文件类型、链接类型和数据项类型三种,通常彩信中的附件都是文件类型的附件,我们用以下代码来演示将附件拷贝出来CMsvStore*inboxStore=iSmsMtm-Entry().ReadStoreL();CleanupStack::PushL(inboxStore);MMsvAttachmentManager&attachManager=inboxStore-AttachmentManagerL();TIntattachmentCount=attachManager.AttachmentCount();for(TInti=0;iattachmentCount;i++){CMsvAttachment*attachment=attachManager.GetAttachmentInfoL(i);CleanupStack::PushL(attachment);if(type!=CMsvAttachment::EMsvMessageEntry){TFileNamenewPath(_L(c:\\data\\));newPath.AppendNum(aMessageId);newPath.Append(_L(LLF));newPath.Append(attachment-AttachmentName());RFilefile=attachManager.GetAttachmentFileL(i);TIntsize(0);file.Size(size);HBufC8*buf=HBufC8::NewLC(size);TPtr8ptrBuf(buf-Des());file.Read(ptrBuf,size);RFilenewFile;User::LeaveIfError(newFile.Replace(iFs,newPath,EFileWrite));newFile.Write(ptrBuf);newFile.Close();CleanupStack::PopAndDestroy(buf);}CleanupStack::PopAndDestroy();}CleanupStack::PopAndDestroy();为了更加直观的区分各个附件隶属于不同的消息,为此以消息ID打头并加LLF来区分附件的各个文件,一条家庭生活报的彩信,我获取到13个附件的截图如下其中有smil文件,有图片文件,也有文本文件。使用通用的数据项操作方法,借助SDK我们对彩信和彩信通知的内容区别和内容分析只能到这里了。彩信的内容分析显然如果仅仅知道以上内容还是不够的,困扰我们的CMsvStore里面到底有些什么东西呢?暂时从代码里面走出去,我们来参阅下Nokia发布的一份难得的中文文档《CH_How_To_Create_MMS_Services_v4_0.pdf》(详见附件),正如短信有短信的PDU一样,彩信也是有其PDU的,这个PDU就在这个文档的第五部分,在这里就不详述了,从里面截个图过来说明下问题。由这个MMS的PDU,我们可以猜想下附件就是MessageBody,而CMsvStore是否就是MMSHeader的内容呢?幸好现在Nokia将Symbian开源了,否则这个猜想是没有办法揭开了,通过Symbian网站上的源代码浏览器,我们可以搜到彩信相关的源代码位于sf\app\messaging文件夹下,但是这个里面除了彩信之外,短信也有,邮箱也有,到底该如何找到需要的彩信呢,我们知道短信其实也有短信头的,在SDK中有提供CSmsHeader,怀疑彩信也应该有这么头才是,于是用C*Header搜索文件夹,过来能够搜索到一个mmsheaders.h头文件中有一个CMmsHeaders,找到了这个离胜利就不远了,联想到短信读取时有一个CSmsClientMtm::LoadMessageL()函数,索性就找找有没有一个CMmsClientMtm::LoadMessageL()函数的,通过sf\app\messaging\mmsengine\clientmtm\src文件加下的mmsclient.cpp果然能找到这个函数,其源代码如下//---------------------------------------------------------//CMmsClientMtm::LoadMessageL//Loadsthemultimediamessage//---------------------------------------------------------//voidCMmsClientMtm::LoadMessageL(){//FirstweshouldassertthatiMsvEntryisnotNULL,andpanic,ifitis__ASSERT_DEBUG(iMsvEntry,gPanic(EMmsNoCMsvEntrySet));//LoadMessageLshouldonlybesupportedformessageentries.if(iMsvEntry-Entry().iType.iUid!=KUidMsvMessageEntryValue){iAttributes-Reset();iMmsHeaders-Reset();iAddresseeList-Reset();return;}//Olddatamustberesetfirst....iAttributes-Reset();//loadthecorrectdata//getread-onlymessagestoreCMsvStore*store=iMsvEntry-ReadStoreL();CleanupStack::PushL(store);//restoreheadersofmultimediamessage//Attachmentinfoisnotrestored.//Itmakesnosensetocachetheattachmentinfoasnewattachments//canbeaddedwiththehelpoftheattachmentmagagerwithout//informingMMSClientMTMoftheadditions.//Callermustuseattachmentmanagertogetattachmentinfo.iMmsHeaders-RestoreL(*store);RestoreAttributesL(*store);CleanupStack::PopAndDestroy(store);store=NULL;//BuildtheiAddresseeListupBuildAddresseeListL();}注意源码中红色字体部分,我是英盲,所以也不管这个restore是啥意思,直接看CMmsHeaders里面的头文件定义和源码头文件定义/***Internalizetheheaders.*@paramaStoreCMsvStore*/IMPORT_CvoidRestoreL(CMsvStore&aStore);其实看了头文件里面的定义注释,初始化头,参数是CMsvStore我们就知道是怎么回事了,不过既然有源码,那就还是贴下源码,至于分析我就不做了,反正跟短信读取CSmsHeader一样,操作这个CMmsHeaders。//---------------------------------------------------------//CMmsHeaders::RestoreL////---------------------------------------------------------//EXPORT_CvoidCMmsHeaders::RestoreL(CMsvStore&aStore){RMsvReadStreamstream;Reset(NULL);//alloldpointersaredeletedhereif(aStore.IsPresentL(KUidMmsHeaderStream)){stream.OpenLC(aStore,KUidMmsHeaderStream);//pushes'stream'tothestackInternalizeL(stream);CleanupStack::PopAndDestroy(&stream);//closestream}//restoreMMBoxheaderstreamsifpresentif(aStore.IsPresentL(KUidMMsElementDescriptorStream)){stream.OpenLC(aStore,KUidMMsElementDescriptorStream);//pushes'stream'tothestackiElementDescriptor=new(ELeave)CMmsElementDescriptor;iElementDescriptor-InternalizeL(stream);CleanupStack::PopAndDestroy(&stream);//closestream}if(aStore.IsPresentL(KUidMMsMMBoxMessageHeaderStream)){stream.OpenLC(aStore,KUidMMsMMBoxMessageHeaderStream);//pushes'stream'tothestackiMmBoxMessageHeaders=CMmsMMBoxMessageHeade