source: code/trunk/msgstore.go@ 509

Last change on this file since 509 was 488, checked in by contact, 4 years ago

Use BARE for internal message IDs

This allows to have shorter and more future-proof IDs. This also
guarantees the IDs will only use reasonable ASCII characters (no
spaces), removing the need to encode them for PING/PONG tokens.

File size: 2.6 KB
RevLine 
[423]1package soju
2
3import (
[488]4 "bytes"
5 "encoding/base64"
[440]6 "fmt"
[423]7 "time"
8
[488]9 "git.sr.ht/~sircmpwn/go-bare"
[423]10 "gopkg.in/irc.v3"
11)
12
13// messageStore is a per-user store for IRC messages.
[439]14type messageStore interface {
15 Close() error
16 // LastMsgID queries the last message ID for the given network, entity and
17 // date. The message ID returned may not refer to a valid message, but can be
18 // used in history queries.
19 LastMsgID(network *network, entity string, t time.Time) (string, error)
20 LoadLatestID(network *network, entity, id string, limit int) ([]*irc.Message, error)
21 Append(network *network, entity string, msg *irc.Message) (id string, err error)
[423]22}
[440]23
[441]24// chatHistoryMessageStore is a message store that supports chat history
25// operations.
26type chatHistoryMessageStore interface {
27 messageStore
28
29 LoadBeforeTime(network *network, entity string, t time.Time, limit int) ([]*irc.Message, error)
30 LoadAfterTime(network *network, entity string, t time.Time, limit int) ([]*irc.Message, error)
31}
32
[488]33type msgIDType uint
34
35const (
36 msgIDNone msgIDType = iota
37 msgIDMemory
38 msgIDFS
39)
40
41const msgIDVersion uint = 0
42
43type msgIDHeader struct {
44 Version uint
45 Network bare.Int
46 Target string
47 Type msgIDType
[440]48}
49
[488]50type msgIDBody interface {
51 msgIDType() msgIDType
52}
53
54func formatMsgID(netID int64, target string, body msgIDBody) string {
55 var buf bytes.Buffer
56 w := bare.NewWriter(&buf)
57
58 header := msgIDHeader{
59 Version: msgIDVersion,
60 Network: bare.Int(netID),
61 Target: target,
62 Type: body.msgIDType(),
[440]63 }
[488]64 if err := bare.MarshalWriter(w, &header); err != nil {
65 panic(err)
66 }
67 if err := bare.MarshalWriter(w, body); err != nil {
68 panic(err)
69 }
70 return base64.RawURLEncoding.EncodeToString(buf.Bytes())
71}
72
73func parseMsgID(s string, body msgIDBody) (netID int64, target string, err error) {
74 b, err := base64.RawURLEncoding.DecodeString(s)
[440]75 if err != nil {
[488]76 return 0, "", fmt.Errorf("invalid internal message ID: %v", err)
[440]77 }
[488]78
79 r := bare.NewReader(bytes.NewReader(b))
80
81 var header msgIDHeader
82 if err := bare.UnmarshalBareReader(r, &header); err != nil {
83 return 0, "", fmt.Errorf("invalid internal message ID: %v", err)
84 }
85
86 if header.Version != msgIDVersion {
87 return 0, "", fmt.Errorf("invalid internal message ID: got version %v, want %v", header.Version, msgIDVersion)
88 }
89
90 if body != nil {
91 typ := body.msgIDType()
92 if header.Type != typ {
93 return 0, "", fmt.Errorf("invalid internal message ID: got type %v, want %v", header.Type, typ)
94 }
95
96 if err := bare.UnmarshalBareReader(r, body); err != nil {
97 return 0, "", fmt.Errorf("invalid internal message ID: %v", err)
98 }
99 }
100
101 return int64(header.Network), header.Target, nil
[440]102}
Note: See TracBrowser for help on using the repository browser.