diff options
Diffstat (limited to 'pkg/motd')
-rw-r--r-- | pkg/motd/fetch.go | 54 | ||||
-rw-r--r-- | pkg/motd/motd-example.json | 15 | ||||
-rw-r--r-- | pkg/motd/motd.go | 118 | ||||
-rw-r--r-- | pkg/motd/motd_test.go | 83 |
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 +} |