summaryrefslogtreecommitdiff
path: root/pkg/motd
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/motd')
-rw-r--r--pkg/motd/fetch.go54
-rw-r--r--pkg/motd/motd-example.json15
-rw-r--r--pkg/motd/motd.go118
-rw-r--r--pkg/motd/motd_test.go83
4 files changed, 270 insertions, 0 deletions
diff --git a/pkg/motd/fetch.go b/pkg/motd/fetch.go
new file mode 100644
index 0000000..4f22388
--- /dev/null
+++ b/pkg/motd/fetch.go
@@ -0,0 +1,54 @@
+package motd
+
+import (
+ "io/ioutil"
+ "log"
+ "net/http"
+ "os"
+
+ "0xacab.org/leap/bitmask-vpn/pkg/config"
+)
+
+func FetchLatest() []Message {
+ empty := []Message{}
+ if os.Getenv("SKIP_MOTD") == "1" {
+ return empty
+ }
+ url := ""
+ switch config.Provider {
+ case "riseup.net":
+ url = "https://downloads.leap.se/motd/riseup/motd.json"
+ default:
+ return empty
+ }
+ log.Println("Fetching MOTD for", config.Provider)
+ b, err := fetchURL(url)
+ if err != nil {
+ log.Println("WARN Error fetching json from", url)
+ return empty
+ }
+ allMsg, err := getFromJSON(b)
+ if err != nil {
+ log.Println("WARN Error parsing json from", url)
+ return empty
+ }
+ valid := empty[:]
+ if allMsg.Length() != 0 {
+ log.Printf("There are %d pending messages\n", allMsg.Length())
+ }
+ for _, msg := range allMsg.Messages {
+ if msg.IsValid() {
+ valid = append(valid, msg)
+ }
+ }
+ return valid
+}
+
+func fetchURL(url string) ([]byte, error) {
+ resp, err := http.Get(url)
+ if err != nil {
+ panic(err)
+ }
+ defer resp.Body.Close()
+ return ioutil.ReadAll(resp.Body)
+}
diff --git a/pkg/motd/motd-example.json b/pkg/motd/motd-example.json
new file mode 100644
index 0000000..20a7727
--- /dev/null
+++ b/pkg/motd/motd-example.json
@@ -0,0 +1,15 @@
+{
+ "motd": [{
+ "begin": "01 Nov 21 00:00 -0700",
+ "end": "31 Jan 22 00:00 -0700",
+ "type": "daily",
+ "platform": "all",
+ "urgency": "normal",
+ "text": [
+ { "lang": "en",
+ "str": "Thanks for using RiseupVPN! Please report us <a href='https://0xacab.org/leap/bitmask-vpn'>any issue or feature request</a>."},
+ { "lang": "es",
+ "str": "¡Gracias por usar RiseupVPN! Por favor reportanos <a href='https://0xacab.org/leap/bitmask-vpn'>cualquier bug o petición</a>."}
+ ]}
+ ]
+}
diff --git a/pkg/motd/motd.go b/pkg/motd/motd.go
new file mode 100644
index 0000000..db21025
--- /dev/null
+++ b/pkg/motd/motd.go
@@ -0,0 +1,118 @@
+package motd
+
+import (
+ "encoding/json"
+ "io/ioutil"
+ "log"
+ "os"
+ "time"
+)
+
+const TimeString = "02 Jan 06 15:04 -0700" // RFC822 with numeric zone
+const ExampleFile = "motd-example.json"
+
+func ParseFile(f string) (Messages, error) {
+ jsonFile, err := os.Open(f)
+ if err != nil {
+ panic(err)
+ }
+ defer jsonFile.Close()
+ byteVal, err := ioutil.ReadAll(jsonFile)
+ if err != nil {
+ panic(err)
+ }
+ return getFromJSON(byteVal)
+}
+
+func getFromJSON(b []byte) (Messages, error) {
+ var m Messages
+ json.Unmarshal(b, &m)
+ return m, nil
+}
+
+type Messages struct {
+ Messages []Message `json:"motd"`
+}
+
+func (m *Messages) Length() int {
+ return len(m.Messages)
+}
+
+type Message struct {
+ Begin string `json:"begin"`
+ End string `json:"end"`
+ Type string `json:"type"`
+ Platform string `json:"platform"`
+ Urgency string `json:"urgency"`
+ Text []LocalizedText `json:"text"`
+}
+
+func (m *Message) IsValid() bool {
+ valid := (m.IsValidBegin() && m.IsValidEnd() &&
+ m.IsValidType() && m.IsValidPlatform() && m.IsValidUrgency() &&
+ m.HasLocalizedText())
+ return valid
+}
+
+func (m *Message) IsValidBegin() bool {
+ _, err := time.Parse(TimeString, m.Begin)
+ if err != nil {
+ log.Println(err)
+ return false
+ }
+ return true
+}
+
+func (m *Message) IsValidEnd() bool {
+ endTime, err := time.Parse(TimeString, m.End)
+ if err != nil {
+ log.Println(err)
+ return false
+ }
+ beginTime, err := time.Parse(TimeString, m.Begin)
+ if err != nil {
+ log.Println(err)
+ return false
+ }
+ if !beginTime.Before(endTime) {
+ log.Println("begin ts should be before end")
+ return false
+ }
+ return true
+}
+
+func (m *Message) IsValidType() bool {
+ switch m.Type {
+ case "once", "daily":
+ return true
+ default:
+ return false
+ }
+}
+
+func (m *Message) IsValidPlatform() bool {
+ switch m.Platform {
+ case "windows", "linux", "osx", "all":
+ return true
+ default:
+ return false
+ }
+}
+
+func (m *Message) IsValidUrgency() bool {
+ switch m.Urgency {
+ case "normal", "critical":
+ return true
+ default:
+ return false
+ }
+}
+
+func (m *Message) HasLocalizedText() bool {
+ return len(m.Text) > 0
+}
+
+type LocalizedText struct {
+ Lang string `json:"lang"`
+ Str string `json:"str"`
+}
diff --git a/pkg/motd/motd_test.go b/pkg/motd/motd_test.go
new file mode 100644
index 0000000..85a2f1c
--- /dev/null
+++ b/pkg/motd/motd_test.go
@@ -0,0 +1,83 @@
+package motd
+
+import (
+ "testing"
+)
+
+func TestGoodMotd(t *testing.T) {
+ m, err := ParseFile(ExampleFile)
+ if err != nil {
+ t.Errorf("error parsing default file")
+ }
+ if m.Length() == 0 {
+ t.Errorf("zero messages in file")
+ }
+ for _, msg := range m.Messages {
+ if !msg.IsValid() {
+ t.Errorf("invalid motd json at %s", ExampleFile)
+ }
+ }
+}
+
+const emptyDate = `
+{
+ "motd": [{
+ "begin": "",
+ "end": "",
+ "type": "daily",
+ "platform": "all",
+ "urgency": "normal",
+ "text": [
+ { "lang": "en",
+ "str": "test"
+ }]
+ }]
+}`
+
+func TestEmptyDateFails(t *testing.T) {
+ m, err := getFromJSON([]byte(emptyDate))
+ if err != nil {
+ t.Errorf("error parsing json")
+ }
+ if allValid(t, m) {
+ t.Errorf("empty string should not be valid")
+ }
+}
+
+const badEnd = `
+{
+ "motd": [{
+ "begin": "02 Jan 21 00:00 +0100",
+ "end": "01 Jan 21 00:00 +0100",
+ "type": "daily",
+ "platform": "all",
+ "urgency": "normal",
+ "text": [
+ { "lang": "en",
+ "str": "test"
+ }]
+ }]
+}`
+
+func TestBadEnd(t *testing.T) {
+ m, err := getFromJSON([]byte(badEnd))
+ if err != nil {
+ t.Errorf("error parsing json")
+ }
+ if allValid(t, m) {
+ t.Errorf("begin > end must fail")
+ }
+}
+
+func allValid(t *testing.T, m Messages) bool {
+ if m.Length() == 0 {
+ t.Errorf("expected at least one message")
+
+ }
+ for _, msg := range m.Messages {
+ if !msg.IsValid() {
+ return false
+ }
+ }
+ return true
+}