2020-10-05 13:07:43 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
2020-10-06 12:25:20 +00:00
|
|
|
"strconv"
|
2020-10-05 13:07:43 +00:00
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
type pigeonBodyItem struct {
|
|
|
|
key string
|
|
|
|
value string
|
|
|
|
}
|
|
|
|
|
|
|
|
type pigeonMessage struct {
|
|
|
|
author string
|
2020-10-06 12:25:20 +00:00
|
|
|
depth int64
|
2020-10-05 13:07:43 +00:00
|
|
|
kind string
|
|
|
|
prev string
|
2020-10-06 12:50:41 +00:00
|
|
|
body []pigeonBodyItem
|
2020-10-05 13:07:43 +00:00
|
|
|
signature string
|
|
|
|
}
|
|
|
|
|
|
|
|
type parserMode int
|
|
|
|
|
|
|
|
const (
|
|
|
|
parsingHeader parserMode = iota
|
|
|
|
parsingBody parserMode = iota
|
|
|
|
parsingFooter parserMode = iota
|
|
|
|
parsingDone parserMode = iota
|
2020-10-08 13:06:46 +00:00
|
|
|
parsingError parserMode = iota
|
2020-10-05 13:07:43 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type parserState struct {
|
|
|
|
mode parserMode
|
|
|
|
scanner *bufio.Scanner
|
2020-10-06 12:25:20 +00:00
|
|
|
buffer pigeonMessage
|
|
|
|
results []pigeonMessage
|
2020-10-08 13:06:46 +00:00
|
|
|
error error
|
2020-10-05 13:07:43 +00:00
|
|
|
}
|
|
|
|
|
2020-10-28 13:06:56 +00:00
|
|
|
type parserOutput struct {
|
|
|
|
/** `messages` is an array of messages. The messages are SHALLOW
|
|
|
|
verified. That means the message has a valid signature and syntax,
|
2020-11-08 21:34:29 +00:00
|
|
|
but `depth` and `prev` have not been scrutinized for validity. */
|
2020-10-28 13:06:56 +00:00
|
|
|
messages []pigeonMessage
|
|
|
|
/** `blobIndex` is a hash where keys represent each unique blob
|
|
|
|
foud in a bundle. This is required to avoid ingesting unwanted
|
|
|
|
blobs to disk. */
|
|
|
|
blobIndex map[string]bool
|
|
|
|
}
|
|
|
|
|
2020-10-05 13:07:43 +00:00
|
|
|
func newState(message string) parserState {
|
|
|
|
return parserState{
|
|
|
|
mode: parsingHeader,
|
|
|
|
scanner: bufio.NewScanner(strings.NewReader(message)),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-08 01:29:36 +00:00
|
|
|
func maybeIndexBlob(index map[string]bool, input string) {
|
|
|
|
if isBlob(input) {
|
|
|
|
index[input] = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-28 13:06:56 +00:00
|
|
|
func parseMessage(message string) (parserOutput, error) {
|
|
|
|
empty := parserOutput{
|
|
|
|
messages: []pigeonMessage{},
|
|
|
|
blobIndex: map[string]bool{},
|
|
|
|
}
|
2020-10-05 13:07:43 +00:00
|
|
|
state := newState(message)
|
|
|
|
for state.scanner.Scan() {
|
2020-10-11 17:53:54 +00:00
|
|
|
// Exit early if any step produces an error.
|
|
|
|
if state.error != nil {
|
2020-10-28 13:06:56 +00:00
|
|
|
return empty, state.error
|
2020-10-11 17:53:54 +00:00
|
|
|
}
|
|
|
|
|
2020-10-05 13:07:43 +00:00
|
|
|
switch state.mode {
|
2020-10-08 13:06:46 +00:00
|
|
|
case parsingDone:
|
|
|
|
maybeContinue(&state)
|
2020-10-05 13:07:43 +00:00
|
|
|
case parsingHeader:
|
2020-10-06 12:25:20 +00:00
|
|
|
parseHeader(&state)
|
2020-10-05 13:07:43 +00:00
|
|
|
case parsingBody:
|
2020-10-06 12:25:20 +00:00
|
|
|
parseBody(&state)
|
2020-10-05 13:07:43 +00:00
|
|
|
case parsingFooter:
|
2020-10-06 12:25:20 +00:00
|
|
|
parseFooter(&state)
|
2020-10-08 13:06:46 +00:00
|
|
|
case parsingError:
|
|
|
|
break
|
2020-10-05 13:07:43 +00:00
|
|
|
}
|
|
|
|
}
|
2020-10-08 13:06:46 +00:00
|
|
|
if state.mode == parsingError {
|
2020-10-28 13:06:56 +00:00
|
|
|
return empty, state.error
|
|
|
|
}
|
|
|
|
blobIndex := map[string]bool{}
|
|
|
|
for _, msg := range state.results {
|
2020-11-08 01:29:36 +00:00
|
|
|
if getPeerStatus(msg.author) == following {
|
|
|
|
for _, pair := range msg.body {
|
|
|
|
maybeIndexBlob(blobIndex, pair.key)
|
|
|
|
maybeIndexBlob(blobIndex, pair.value)
|
|
|
|
}
|
2020-10-28 13:06:56 +00:00
|
|
|
}
|
2020-10-08 13:06:46 +00:00
|
|
|
}
|
2020-10-28 13:06:56 +00:00
|
|
|
output := parserOutput{messages: state.results, blobIndex: blobIndex}
|
|
|
|
return output, nil
|
2020-10-05 13:07:43 +00:00
|
|
|
}
|
|
|
|
|
2020-10-06 12:25:20 +00:00
|
|
|
func parseHeader(state *parserState) {
|
2020-10-05 13:07:43 +00:00
|
|
|
t := state.scanner.Text()
|
|
|
|
chunks := strings.Split(t, " ")
|
2020-10-06 12:25:20 +00:00
|
|
|
|
|
|
|
switch chunks[0] {
|
|
|
|
case "":
|
|
|
|
state.mode = parsingBody
|
|
|
|
return
|
|
|
|
case "author":
|
|
|
|
state.buffer.author = chunks[1]
|
|
|
|
return
|
|
|
|
case "kind":
|
|
|
|
state.buffer.kind = chunks[1]
|
|
|
|
return
|
|
|
|
case "prev":
|
|
|
|
state.buffer.prev = chunks[1]
|
|
|
|
return
|
|
|
|
case "depth":
|
|
|
|
depth, err := strconv.ParseInt(chunks[1], 10, 32)
|
2020-11-15 19:14:18 +00:00
|
|
|
check(err, "Parsing bad depth in message %d: %q", len(state.results), chunks[1])
|
2020-10-06 12:25:20 +00:00
|
|
|
state.buffer.depth = depth
|
|
|
|
return
|
2020-10-05 13:07:43 +00:00
|
|
|
default:
|
2020-10-06 12:25:20 +00:00
|
|
|
panicf("BAD HEADER: %q", t)
|
2020-10-05 13:07:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-06 12:25:20 +00:00
|
|
|
func parseBody(state *parserState) {
|
2020-10-06 12:50:41 +00:00
|
|
|
t := state.scanner.Text()
|
|
|
|
chunks := strings.Split(t, ":")
|
|
|
|
if chunks[0] == "" {
|
|
|
|
state.mode = parsingFooter
|
|
|
|
return
|
|
|
|
}
|
|
|
|
pair := pigeonBodyItem{key: chunks[0], value: chunks[1]}
|
|
|
|
state.buffer.body = append(state.buffer.body, pair)
|
|
|
|
return
|
2020-10-05 13:07:43 +00:00
|
|
|
}
|
|
|
|
|
2020-10-06 12:25:20 +00:00
|
|
|
func parseFooter(state *parserState) {
|
2020-10-15 12:30:30 +00:00
|
|
|
t := state.scanner.Text()
|
|
|
|
chunks := strings.Split(t, " ")
|
|
|
|
state.buffer.signature = chunks[1]
|
2020-10-08 13:06:46 +00:00
|
|
|
state.mode = parsingDone
|
2020-10-11 17:53:54 +00:00
|
|
|
err := verifyShallow(&state.buffer)
|
2020-11-15 19:14:18 +00:00
|
|
|
check(err, "Message verification failed for %s. %s", state.buffer.signature, err)
|
2020-10-15 12:30:30 +00:00
|
|
|
state.results = append(state.results, state.buffer)
|
|
|
|
state.buffer.body = []pigeonBodyItem{}
|
|
|
|
state.buffer = pigeonMessage{}
|
2020-10-08 13:06:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func maybeContinue(state *parserState) {
|
|
|
|
t1 := state.scanner.Text()
|
|
|
|
if t1 == "" {
|
|
|
|
state.mode = parsingHeader
|
|
|
|
}
|
2020-10-05 13:07:43 +00:00
|
|
|
}
|