Skip to content

Instantly share code, notes, and snippets.

@ovatsus
Last active June 13, 2016 00:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ovatsus/2dd82172dcd8308d559c to your computer and use it in GitHub Desktop.
Save ovatsus/2dd82172dcd8308d559c to your computer and use it in GitHub Desktop.
Windows Phone to Android message importer
#r "packages/FSharp.Data.2.3.0-beta1/lib/net40/FSharp.Data.dll"
#r "System.Xml.Linq.dll"
open System
open System.Text
open FSharp.Data
[<Literal>]
let androidSample = """
<smses count="40">
<sms protocol="0" address="ToNumber" date="1450377931962" type="2" subject="null"
body="Content" toa="null" sc_toa="null" service_center="null" read="1" status="-1"
locked="0" date_sent="0" readable_date="17 Dec 2015 6:45:31 p.m." contact_name="ToName" />
<sms protocol="0" address="FromNumber" date="1450378666072" type="1" subject="null" body="Content"
toa="null" sc_toa="null" service_center="ServiceCenterNumber" read="1" status="-1" locked="0"
date_sent="1450378662000" readable_date="17 Dec 2015 6:57:46 p.m." contact_name="FromName" />
<mms text_only="0" ct_t="application/vnd.wap.multipart.related" msg_box="2" v="18" sub="null" seen="1"
rr="nubmer" ct_cls="null" retr_txt_cs="null" ct_l="null" m_size="number" exp="number" sub_cs="null"
st="null" creator="com.google.android.apps.messaging" tr_id="T151bb723573" sub_id="1" read="1"
resp_st="number" date="1450548803000" m_id="34F545AE-A67C-71E5-981B-005056B920E3" date_sent="0"
pri="129" m_type="128" address="ToNumber" d_rpt="129" d_tm="null" read_status="null" locked="0"
retr_txt="null" resp_txt="null" rpt_a="null" retr_st="null" m_cls="personal"
readable_date="19 Dec 2015 6:13:23 p.m." contact_name="ToName">
<parts>
<part seq="-1" ct="application/smil" name="null" chset="null" cd="null" fn="null" cid="&lt;smil&gt;"
cl="smil.xml" ctt_s="null" ctt_t="null" text='&lt;smil&gt;&lt;head&gt;&lt;layout&gt;&lt;root-layout/&gt;&lt;region id="Image" fit="meet" top="0" left="0" height="100%" width="100%"/&gt;&lt;/layout&gt;&lt;/head&gt;&lt;body&gt;&lt;par dur="5000ms"&gt;&lt;img src="image000000.jpg" region="Image" /&gt;&lt;/par&gt;&lt;/body&gt;&lt;/smil&gt;' />
<part seq="0" ct="image/jpeg" name="null" chset="null" cd="null" fn="null" cid="&lt;image000000&gt;"
cl="image000000.jpg" ctt_s="null" ctt_t="null" text="null" data="Data" />
</parts>
<addrs>
<addr address="FromNumber" type="137" charset="106" />
<addr address="ToNumber" type="151" charset="106" />
</addrs>
</mms>
<mms text_only="0" ct_t="application/vnd.wap.multipart.related" msg_box="1" v="16" sub="null" seen="1"
rr="null" ct_cls="null" retr_txt_cs="null" ct_l="http://mms2/mmsc/?A_4OyYlvXyr_aB5LE" m_size="null"
exp="null" sub_cs="null" st="null" creator="com.google.android.apps.messaging" tr_id="A_4OyYlvXyr_aB5LE"
sub_id="1" read="1" resp_st="null" date="1450548989000" m_id="9D765F0A-A67C-71E5-8938-005056B920E3"
date_sent="0" pri="129" m_type="132" address="FromNumber" d_rpt="129" d_tm="null" read_status="null"
locked="0" retr_txt="null" resp_txt="null" rpt_a="null" retr_st="null" m_cls="personal"
readable_date="19 Dec 2015 6:16:29 p.m." contact_name="FromName">
<parts>
<part seq="-1" ct="application/smil" name="smil.smil" chset="106" cd="null" fn="null"
cid="&lt;smil.smil&gt;" cl="null" ctt_s="null" ctt_t="null" text='&lt;smil&gt;&#10; &lt;head&gt;&#10; &lt;layout&gt;&#10; &lt;root-layout width="1080" height="1900" /&gt;&#10; &lt;region id="Image" width="1080" height="1520" left="0" top="0" fit="meet" /&gt;&#10; &lt;region id="Text" width="1080" height="380" left="0" top="1520" /&gt;&#10; &lt;/layout&gt;&#10; &lt;/head&gt;&#10; &lt;body&gt;&#10; &lt;par dur="5000ms" &gt;&#10; &lt;img src="image000000.jpg" region="Image"/&gt;&#10; &lt;/par&gt;&#10; &lt;par dur="5000ms" &gt;&#10; &lt;img src="image000001.jpg" region="Image"/&gt;&#10; &lt;/par&gt;&#10; &lt;par dur="5000ms" &gt;&#10; &lt;text src="text.000002.txt" region="Text"/&gt;&#10; &lt;/par&gt;&#10; &lt;/body&gt;&#10;&lt;/smil&gt;&#10;' />
<part seq="0" ct="image/jpeg" name="image000000.jpg" chset="null" cd="null" fn="null"
cid="&lt;image000000.jpg&gt;" cl="image000000.jpg" ctt_s="null" ctt_t="null" text="null" data="DATA" />
<part seq="0" ct="image/jpeg" name="image000001.jpg" chset="null" cd="null" fn="null"
cid="&lt;image000001.jpg&gt;" cl="image000001.jpg" ctt_s="null" ctt_t="null" text="null" data="DATA" />
<part seq="0" ct="text/plain" name="text.000002.txt" chset="106" cd="null" fn="null"
cid="&lt;text.000002.txt&gt;" cl="text.000002.txt" ctt_s="null" ctt_t="null" text="Content" />
</parts>
<addrs>
<addr address="FromNumber" type="137" charset="106" />
<addr address="ToNumber" type="151" charset="106" />
</addrs>
</mms>
</smses>
"""
[<Literal>]
let wpSample = """
<ArrayOfMessage xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Message>
<Recepients>
<string>ToNumber</string>
</Recepients>
<Body>Content</Body>
<IsIncoming>false</IsIncoming>
<IsRead>true</IsRead>
<Attachments />
<LocalTimestamp>130948371823950673</LocalTimestamp>
<Sender />
</Message>
<Message>
<Recepients />
<Body>Content</Body>
<IsIncoming>true</IsIncoming>
<IsRead>true</IsRead>
<Attachments />
<LocalTimestamp>130948367549678013</LocalTimestamp>
<Sender>FromNumber</Sender>
</Message>
<Message>
<Recepients>
<string>ToNumber</string>
</Recepients>
<Body />
<IsIncoming>false</IsIncoming>
<IsRead>true</IsRead>
<Attachments>
<MessageAttachment>
<AttachmentContentType>image/jpeg</AttachmentContentType>
<AttachmentDataBase64String>Data</AttachmentDataBase64String>
</MessageAttachment>
<MessageAttachment>
<AttachmentContentType>text/plain</AttachmentContentType>
<AttachmentDataBase64String>Data</AttachmentDataBase64String>
</MessageAttachment>
<MessageAttachment>
<AttachmentContentType>application/smil</AttachmentContentType>
<AttachmentDataBase64String>Data</AttachmentDataBase64String>
</MessageAttachment>
</Attachments>
<LocalTimestamp>130892991988570000</LocalTimestamp>
<Sender />
</Message>
</ArrayOfMessage>
"""
type Android = XmlProvider<androidSample>
type WP = XmlProvider<wpSample>
type MessageType = Sent|Received
type MMSPart =
| SMIL of xmil: string
| Text of text: string
| Photo of base64: string
type MessageContent =
| SMSContent of string
| MMSContent of parts: MMSPart[]
type Message =
Message of messageType: MessageType *
phoneNumber: string *
date: DateTime *
content: MessageContent
module DateTime =
let unixEpoch = DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).Ticks
let fromUnixTime androidTime =
DateTime(unixEpoch + androidTime * 10000L)
let toUnixTime (dotNetTime:DateTime) =
(dotNetTime.Ticks - unixEpoch) / 10000L
let parseWP (msg:WP.Message) =
let messageType, phoneNumber =
if msg.IsIncoming then
MessageType.Received, msg.Sender.Value
else
MessageType.Sent, msg.Recepients.String.Value
let date = DateTime.FromFileTime msg.LocalTimestamp
let content =
if msg.Attachments.MessageAttachments.Length = 0 then
SMSContent msg.Body.Value
else
let toText (att:WP.MessageAttachment) =
att.AttachmentDataBase64String
|> Convert.FromBase64String
|> Encoding.Unicode.GetString
let toPart (att:WP.MessageAttachment) =
match att.AttachmentContentType with
| "image/jpeg" -> Some (Photo att.AttachmentDataBase64String)
| "text/plain" -> Some (Text (toText att))
| "application/smil" -> Some (SMIL (toText att))
| contentType ->
printfn "Unsupported content type %s" contentType
None
let parts =
msg.Attachments.MessageAttachments
|> Array.choose toPart
MMSContent parts
Message(messageType, phoneNumber, date, content)
let toAndroidSms (Message(messageType, phoneNumber, date, content)) =
let protocol = 0
let smsType =
match messageType with
| Sent -> 2
| Received -> 1
let subject = "null"
let text =
match content with
| SMSContent text -> text
| MMSContent _ -> failwith "Expected SMS, got MMS"
let toa = "null"
let sc_toa = "null"
let serviceCenter = "null"
let read = 1
let status = -1
let locked = 0
let dateSent = 0L
let readableDate = date.ToString()
let contactName = "(Unknown)"
Android.Sms(
protocol, phoneNumber, DateTime.toUnixTime date, smsType, subject, text, toa, sc_toa,
serviceCenter, read, status, locked, dateSent, readableDate, contactName)
let toAndroidMms (Message(messageType, phoneNumber, date, content)) =
let textOnly = 0
let ctT = "application/vnd.wap.multipart.related"
let msgBox, v, mType, addrs =
match messageType with
| Sent -> 2 , 18, 128, [| Android.Addr("", 137, 106); Android.Addr(phoneNumber, 137, 106) |]
| Received -> 1, 16, 132, [| Android.Addr(phoneNumber, 137, 106); Android.Addr("", 137, 106) |]
let sub = "null"
let seen = 1
let rr = "null"
let ctCls = "null"
let retrTxtCs = "null"
let ctL = "null"
let mSize = "null"
let exp = "null"
let subCs = "null"
let st = "null"
let creator = "com.google.android.apps.messaging"
let trId = "null"
let subId = 1
let read = 1
let respSt = "null"
let mId = Guid.NewGuid()
let dateSent = 0
let pri = 129
let address = phoneNumber
let dRpt = 129
let dTm = "null"
let readStatus = "null"
let locked = 0
let retrTxt = "null"
let respTxt = "null"
let rptA = "null"
let retrSt = "null"
let mCls = "personal"
let readableDate = date.ToString()
let contactName = "(Unknown)"
let parts =
match content with
| SMSContent _ -> failwith "Expected MMS, got SMS"
| MMSContent parts ->
let toAndroidPart index part =
let seq, ct, cl, chset, text, data =
match part with
| SMIL smil ->
-1,
"application/smil",
"smil.smil",
Android.ChsetChoice 106,
smil,
None
| Text text ->
0,
"text/plain",
sprintf "text.%06d.txt" (index - 1),
Android.ChsetChoice 106,
text,
None
| Photo base64 ->
0,
"image/jpeg",
sprintf "image%06d.jpg" (index - 1),
Android.ChsetChoice "null",
"null",
Some base64
let name = "null"
let cd = "null"
let fn = "null"
let cid = "&lt;" + cl + "&gt;"
let cttS = "null"
let cttT = "null"
Android.Part(seq, ct, name, chset, cd, fn, cid, cl, cttS, cttT, text, data)
parts |> Array.sortBy (function SMIL _ -> 0 | _ -> 1) |> Array.mapi toAndroidPart
Android.Mms(
textOnly, ctT, msgBox, v, sub, seen, rr, ctCls, retrTxtCs, ctL, mSize, exp, subCs, st,
creator, trId, subId, read, respSt, DateTime.toUnixTime date, mId, dateSent, pri, mType, address, dRpt,
dTm, readStatus, locked, retrTxt, respTxt, rptA, retrSt, mCls, readableDate, contactName, parts, addrs)
let convert (smsInputFile:string) (mmsInputFile:string) (outputFile:string) =
let wpSms = WP.Load smsInputFile
let wpMms = WP.Load mmsInputFile
let count = wpSms.Messages.Length + wpMms.Messages.Length
let androidSms = wpSms.Messages |> Array.map (parseWP >> toAndroidSms)
let androidMms = wpMms.Messages |> Array.map (parseWP >> toAndroidMms)
let android = Android.Smses(count, androidSms, androidMms)
android.XElement.Save(outputFile)
(* example usage:
let smsInputFile = "C:/users/guguer/desktop/sms.msg"
let mmsInputFile = "C:/users/guguer/desktop/mms.msg"
let outputFile = "C:/users/guguer/desktop/all.xml"
convert smsInputFile mmsInputFile outputFile
*)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment