aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/phonesync/SmsXml.kt
blob: 624e1f13c833f44c637cb3d3cf4f9a8a0e1c5036 (plain) (blame)
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("&amp;")
            c == '<' -> b.append("&lt;")
            c == '>' -> b.append("&gt;")
            c == '"' -> b.append("&quot;")
            c == '\n' -> b.append("&#10;")
            c == '\r' -> b.append("&#13;")
            c == '\t' -> b.append("&#9;")
            c < ' ' || c == '￾' || c == '￿' -> {}
            else -> b.append(c)
        }
        return b.append('"').toString()
    }
}