-
Notifications
You must be signed in to change notification settings - Fork 1
/
analyze.go
130 lines (122 loc) · 3.74 KB
/
analyze.go
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// Package analyze handles the parsing, analysis, and storage of incoming
// messages, and the generation of responses to them.
package analyze
import (
"crypto/sha1"
"encoding/hex"
"log"
"strings"
"github.com/rothskeller/packet/envelope"
"github.com/rothskeller/packet/message"
"github.com/rothskeller/wppsvr/store"
)
// An Analysis contains the analysis of a received message.
type Analysis struct {
// sm is the message record that will be stored in the database.
sm store.Message
// env is the envelope of the received message.
env *envelope.Envelope
// subject is the message subject line.
subject string
// body is the message body (encoded).
body string
// msg is the decoded message contents.
msg message.Message
// mb is the underlying BaseMessage.
mb *message.BaseMessage
// session is the session for which the message was received.
session *store.Session
// score is the number of score points, 0 <= score <= outOf.
score int
// outOf is the maximum number of score points.
outOf int
// analysis is a strings.Builder for building sm.Analysis.
analysis *strings.Builder
}
// astore is the interface that the store passed into analyze package functions
// must implement. In production it will be *store.Store, but it can be stubbed
// for testing.
type astore interface {
HasMessageHash(string) string
NextMessageID(string) string
SaveMessage(*store.Message)
}
// Analyze analyzes a single received message, and returns its analysis. The
// analysis is not persisted in the database until its Commit method is called.
func Analyze(st astore, session *store.Session, bbs, raw string) *Analysis {
var (
a Analysis
sum [20]byte
err error
)
// Store the basic message information in the analysis.
a.sm.Message = raw
a.sm.Session = session.ID
a.session = session
a.sm.ToBBS = bbs
sum = sha1.Sum([]byte(raw))
a.sm.Hash = hex.EncodeToString(sum[:])
// Log receipt of the message.
a.env, a.body, err = envelope.ParseRetrieved(raw, bbs, "")
a.subject = a.env.SubjectLine
a.sm.DeliveryTime = a.env.Date
a.sm.FromAddress = a.env.ReturnAddr
if err != nil && a.env.ReturnAddr == "" {
log.Printf("Received at %s@%s: [UNPARSEABLE with hash %s]", session.CallSign, bbs, a.sm.Hash)
} else {
log.Printf("Received at %s@%s: from %q subject %q", session.CallSign, bbs, a.env.ReturnAddr, a.subject)
}
// If we've already handled the message, stop.
if a.sm.LocalID = st.HasMessageHash(a.sm.Hash); a.sm.LocalID != "" {
log.Printf("=> already handled as %s", a.sm.LocalID)
return nil
}
// Assign it a local message ID.
a.sm.LocalID = st.NextMessageID(a.session.Prefix)
// Determine the message type (if no parse error and not a bounce).
if err == nil && !a.env.Autoresponse {
a.msg = message.Decode(a.subject, a.body)
a.mb = a.msg.Base()
a.sm.MessageType = a.mb.Type.Tag
}
// Find the problems with the message.
a.analysis = new(strings.Builder)
if a.messageCounts(err) {
a.checkCorrectness()
if a.session.ModelMsg == nil {
a.checkNonModel()
} else {
a.compareAgainstModel()
}
}
if a.sm.Summary == "" && a.score == a.outOf {
a.sm.Summary = "OK"
}
a.sm.Analysis = a.analysis.String()
a.sm.Score = a.score * 100 / a.outOf
return &a
}
// setSummary sets the summary line of the analysis, handling the possibility
// of multiple issues.
func (a *Analysis) setSummary(s string) {
if a.sm.Summary != "" {
a.sm.Summary = "multiple issues"
} else {
a.sm.Summary = s
}
}
// Commit commits the analyzed message to the database.
func (a *Analysis) Commit(st astore) {
var tag string
if a == nil { // message already handled, nothing to commit
return
}
a.fetchJurisdiction()
st.SaveMessage(&a.sm)
if a.msg != nil {
tag = a.msg.Base().Type.Tag
} else {
tag = "-"
}
log.Printf("=> %s %s %d", a.sm.LocalID, tag, a.sm.Score)
}