1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
package org.meshbay.client.phonesync
import java.io.Writer
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import java.util.TimeZone
/** One text message, as the platform's SMS provider stores it. */
data class Sms(
val id: Long,
val address: String,
/** Milliseconds, received or sent. */
val date: Long,
val dateSent: Long,
/** 1 received, 2 sent, 3 draft, 4 outbox, 5 failed, 6 queued. */
val type: Int,
val body: String,
val read: Int,
val status: Int,
val locked: Int,
val protocol: Int,
val subject: String?,
val serviceCenter: String?,
val contactName: String?,
)
/**
* Messages written as the `<smses>` XML that SMS backup applications read and
* restore, so a copy on the node goes back onto a phone with tools that exist.
* Pure, so the JVM tests hold the format and its escaping.
*/
object SmsXml {
fun write(messages: List<Sms>, out: Writer, zone: TimeZone) {
val readable = SimpleDateFormat("d MMM yyyy HH:mm:ss", Locale.ROOT).apply { timeZone = zone }
out.write("<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>\n")
out.write("<smses count=\"${messages.size}\">\n")
for (m in messages) {
out.write(" <sms protocol=\"${m.protocol}\" address=${q(m.address)} date=\"${m.date}\"" +
" type=\"${m.type}\" subject=${q(m.subject ?: "null")} body=${q(m.body)}" +
" toa=\"null\" sc_toa=\"null\" service_center=${q(m.serviceCenter ?: "null")}" +
" read=\"${m.read}\" status=\"${m.status}\" locked=\"${m.locked}\" date_sent=\"${m.dateSent}\"" +
" readable_date=${q(readable.format(Date(m.date)))}" +
" contact_name=${q(m.contactName ?: "(Unknown)")} />\n")
}
out.write("</smses>\n")
}
/**
* A quoted attribute value. Line breaks are kept as character references,
* which an attribute would otherwise fold into spaces; characters XML 1.0
* cannot carry at all, even escaped, are left out.
*/
fun q(text: String): String {
val b = StringBuilder(text.length + 2).append('"')
for (c in text) when {
c == '&' -> b.append("&")
c == '<' -> b.append("<")
c == '>' -> b.append(">")
c == '"' -> b.append(""")
c == '\n' -> b.append(" ")
c == '\r' -> b.append(" ")
c == '\t' -> b.append("	")
c < ' ' || c == '' || c == '' -> {}
else -> b.append(c)
}
return b.append('"').toString()
}
}
|