aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbUrlFormatter.kt
blob: add35154b74b5fb930202b14aa7ca1daf0357cc9 (plain)
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package com.pitchedapps.frost.facebook

import com.pitchedapps.frost.utils.L
import java.net.URLDecoder
import java.nio.charset.StandardCharsets

/**
 * Created by Allan Wang on 2017-07-07.
 *
 * Custom url builder so we can easily test it without the Android framework
 */
inline val String.formattedFbUrl: String
    get() = FbUrlFormatter(this).toString()

class FbUrlFormatter(url: String) {
    private val queries = mutableMapOf<String, String>()
    private val cleaned: String

    /**
     * Formats all facebook urls
     *
     * The order is very important:
     * 1. Wrapper links (discardables) are stripped away, resulting in the actual link
     * 2. CSS encoding is converted to normal encoding
     * 3. Url is completely decoded
     * 4. Url is split into sections
     */
    init {
        cleaned = clean(url)
    }

    fun clean(url: String): String {
        if (url.isBlank()) return ""
        var cleanedUrl = url
        if (cleanedUrl.startsWith("#!")) cleanedUrl = cleanedUrl.substring(2)
        val urlRef = cleanedUrl
        discardable.forEach { cleanedUrl = cleanedUrl.replace(it, "", true) }
        val changed = cleanedUrl != urlRef
        converter.forEach { (k, v) -> cleanedUrl = cleanedUrl.replace(k, v, true) }
        try {
            cleanedUrl = URLDecoder.decode(cleanedUrl, StandardCharsets.UTF_8.name())
        } catch (e: Exception) {
            L.e(e) { "Failed url formatting" }
            return url
        }
        cleanedUrl = cleanedUrl.replace("&amp;", "&")
        if (changed && !cleanedUrl.contains("?")) //ensure we aren't missing '?'
            cleanedUrl = cleanedUrl.replaceFirst("&", "?")
        val qm = cleanedUrl.indexOf("?")
        if (qm > -1) {
            cleanedUrl.substring(qm + 1).split("&").forEach {
                val p = it.split("=")
                queries[p[0]] = p.elementAtOrNull(1) ?: ""
            }
            cleanedUrl = cleanedUrl.substring(0, qm)
        }
        discardableQueries.forEach { queries.remove(it) }
        if (cleanedUrl.startsWith("/")) cleanedUrl = FB_URL_BASE + cleanedUrl.substring(1)
        cleanedUrl = cleanedUrl.replaceFirst(".facebook.com//", ".facebook.com/") //sometimes we are given a bad url
        L.v { "Formatted url from $url to $cleanedUrl" }
        return cleanedUrl
    }

    override fun toString(): String {
        val builder = StringBuilder()
        builder.append(cleaned)
        if (queries.isNotEmpty()) {
            builder.append("?")
            queries.forEach { (k, v) -> builder.append("$k=$v&") }
        }
        return builder.removeSuffix("&").toString()
    }

    fun toLogList(): List<String> {
        val list = mutableListOf(cleaned)
        queries.forEach { (k, v) -> list.add("\n- $k\t=\t$v") }
        list.add("\n\n${toString()}")
        return list
    }

    companion object {

        const val VIDEO_REDIRECT = "/video_redirect/?src="
        /**
         * Items here are explicitly removed from the url
         * Taken from FaceSlim
         * https://github.com/indywidualny/FaceSlim/blob/master/app/src/main/java/org/indywidualni/fblite/util/Miscellany.java
         *
         * Note: Typically, in this case, the redirect url should have all the necessary queries
         * I am unsure how Facebook reacts in all cases, so the ones after the redirect are appended on afterwards
         * That shouldn't break anything
         */
        val discardable = arrayOf(
                "http://lm.facebook.com/l.php?u=",
                "https://lm.facebook.com/l.php?u=",
                "http://m.facebook.com/l.php?u=",
                "https://m.facebook.com/l.php?u=",
                "http://touch.facebook.com/l.php?u=",
                "https://touch.facebook.com/l.php?u=",
                VIDEO_REDIRECT
        )

        val discardableQueries = arrayOf("ref", "refid", "acontext", "SharedWith")

        val converter = listOf(
                "\\3C " to "%3C", "\\3E " to "%3E", "\\23 " to "%23", "\\25 " to "%25",
                "\\7B " to "%7B", "\\7D " to "%7D", "\\7C " to "%7C", "\\5C " to "%5C",
                "\\5E " to "%5E", "\\7E " to "%7E", "\\5B " to "%5B", "\\5D " to "%5D",
                "\\60 " to "%60", "\\3B " to "%3B", "\\2F " to "%2F", "\\3F " to "%3F",
                "\\3A " to "%3A", "\\40 " to "%40", "\\3D " to "%3D", "\\26 " to "%26",
                "\\24 " to "%24", "\\2B " to "%2B", "\\22 " to "%22", "\\2C " to "%2C",
                "\\20 " to "%20"
        )
    }
}