|
@@ -0,0 +1,1067 @@
|
|
|
+package types
|
|
|
+
|
|
|
+import (
|
|
|
+ "database/sql/driver"
|
|
|
+ "encoding/base32"
|
|
|
+ "encoding/base64"
|
|
|
+ "encoding/binary"
|
|
|
+ "encoding/json"
|
|
|
+ "errors"
|
|
|
+ "sort"
|
|
|
+ "strings"
|
|
|
+ "time"
|
|
|
+)
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+type StoreError string
|
|
|
+
|
|
|
+
|
|
|
+func (s StoreError) Error() string {
|
|
|
+ return string(s)
|
|
|
+}
|
|
|
+
|
|
|
+const (
|
|
|
+
|
|
|
+ ErrInternal = StoreError("internal")
|
|
|
+
|
|
|
+ ErrMalformed = StoreError("malformed")
|
|
|
+
|
|
|
+ ErrFailed = StoreError("failed")
|
|
|
+
|
|
|
+ ErrDuplicate = StoreError("duplicate value")
|
|
|
+
|
|
|
+ ErrUnsupported = StoreError("unsupported")
|
|
|
+
|
|
|
+ ErrExpired = StoreError("expired")
|
|
|
+
|
|
|
+ ErrPolicy = StoreError("policy")
|
|
|
+
|
|
|
+ ErrCredentials = StoreError("credentials")
|
|
|
+
|
|
|
+ ErrNotFound = StoreError("not found")
|
|
|
+
|
|
|
+ ErrPermissionDenied = StoreError("denied")
|
|
|
+)
|
|
|
+
|
|
|
+
|
|
|
+type Uid uint64
|
|
|
+
|
|
|
+
|
|
|
+const ZeroUid Uid = 0
|
|
|
+
|
|
|
+
|
|
|
+const (
|
|
|
+ uidBase64Unpadded = 11
|
|
|
+ p2pBase64Unpadded = 22
|
|
|
+)
|
|
|
+
|
|
|
+
|
|
|
+func (uid Uid) IsZero() bool {
|
|
|
+ return uid == ZeroUid
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (uid Uid) Compare(u2 Uid) int {
|
|
|
+ if uid < u2 {
|
|
|
+ return -1
|
|
|
+ } else if uid > u2 {
|
|
|
+ return 1
|
|
|
+ }
|
|
|
+ return 0
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (uid *Uid) MarshalBinary() ([]byte, error) {
|
|
|
+ dst := make([]byte, 8)
|
|
|
+ binary.LittleEndian.PutUint64(dst, uint64(*uid))
|
|
|
+ return dst, nil
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (uid *Uid) UnmarshalBinary(b []byte) error {
|
|
|
+ if len(b) < 8 {
|
|
|
+ return errors.New("Uid.UnmarshalBinary: invalid length")
|
|
|
+ }
|
|
|
+ *uid = Uid(binary.LittleEndian.Uint64(b))
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (uid *Uid) UnmarshalText(src []byte) error {
|
|
|
+ if len(src) != uidBase64Unpadded {
|
|
|
+ return errors.New("Uid.UnmarshalText: invalid length")
|
|
|
+ }
|
|
|
+ dec := make([]byte, base64.URLEncoding.WithPadding(base64.NoPadding).DecodedLen(uidBase64Unpadded))
|
|
|
+ count, err := base64.URLEncoding.WithPadding(base64.NoPadding).Decode(dec, src)
|
|
|
+ if count < 8 {
|
|
|
+ if err != nil {
|
|
|
+ return errors.New("Uid.UnmarshalText: failed to decode " + err.Error())
|
|
|
+ }
|
|
|
+ return errors.New("Uid.UnmarshalText: failed to decode")
|
|
|
+ }
|
|
|
+ *uid = Uid(binary.LittleEndian.Uint64(dec))
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (uid *Uid) MarshalText() ([]byte, error) {
|
|
|
+ if *uid == ZeroUid {
|
|
|
+ return []byte{}, nil
|
|
|
+ }
|
|
|
+ src := make([]byte, 8)
|
|
|
+ dst := make([]byte, base64.URLEncoding.WithPadding(base64.NoPadding).EncodedLen(8))
|
|
|
+ binary.LittleEndian.PutUint64(src, uint64(*uid))
|
|
|
+ base64.URLEncoding.WithPadding(base64.NoPadding).Encode(dst, src)
|
|
|
+ return dst, nil
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (uid *Uid) MarshalJSON() ([]byte, error) {
|
|
|
+ dst, _ := uid.MarshalText()
|
|
|
+ return append(append([]byte{'"'}, dst...), '"'), nil
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (uid *Uid) UnmarshalJSON(b []byte) error {
|
|
|
+ size := len(b)
|
|
|
+ if size != (uidBase64Unpadded + 2) {
|
|
|
+ return errors.New("Uid.UnmarshalJSON: invalid length")
|
|
|
+ } else if b[0] != '"' || b[size-1] != '"' {
|
|
|
+ return errors.New("Uid.UnmarshalJSON: unrecognized")
|
|
|
+ }
|
|
|
+ return uid.UnmarshalText(b[1 : size-1])
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (uid Uid) String() string {
|
|
|
+ buf, _ := uid.MarshalText()
|
|
|
+ return string(buf)
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (uid Uid) String32() string {
|
|
|
+ data, _ := uid.MarshalBinary()
|
|
|
+ return strings.ToLower(base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(data))
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func ParseUid(s string) Uid {
|
|
|
+ var uid Uid
|
|
|
+ uid.UnmarshalText([]byte(s))
|
|
|
+ return uid
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func ParseUid32(s string) Uid {
|
|
|
+ var uid Uid
|
|
|
+ if data, err := base32.StdEncoding.WithPadding(base32.NoPadding).DecodeString(s); err == nil {
|
|
|
+ uid.UnmarshalBinary(data)
|
|
|
+ }
|
|
|
+ return uid
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (uid Uid) UserId() string {
|
|
|
+ return uid.PrefixId("usr")
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (uid Uid) FndName() string {
|
|
|
+ return uid.PrefixId("fnd")
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (uid Uid) PrefixId(prefix string) string {
|
|
|
+ if uid.IsZero() {
|
|
|
+ return ""
|
|
|
+ }
|
|
|
+ return prefix + uid.String()
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func ParseUserId(s string) Uid {
|
|
|
+ var uid Uid
|
|
|
+ if strings.HasPrefix(s, "usr") {
|
|
|
+ (&uid).UnmarshalText([]byte(s)[3:])
|
|
|
+ }
|
|
|
+ return uid
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+type UidSlice []Uid
|
|
|
+
|
|
|
+func (us UidSlice) find(uid Uid) (int, bool) {
|
|
|
+ l := len(us)
|
|
|
+ if l == 0 || us[0] > uid {
|
|
|
+ return 0, false
|
|
|
+ }
|
|
|
+ if uid > us[l-1] {
|
|
|
+ return l, false
|
|
|
+ }
|
|
|
+ idx := sort.Search(l, func(i int) bool {
|
|
|
+ return uid <= us[i]
|
|
|
+ })
|
|
|
+ return idx, idx < l && us[idx] == uid
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (us *UidSlice) Add(uid Uid) bool {
|
|
|
+ idx, found := us.find(uid)
|
|
|
+ if found {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ *us = append(*us, ZeroUid)
|
|
|
+ copy((*us)[idx+1:], (*us)[idx:])
|
|
|
+ (*us)[idx] = uid
|
|
|
+ return true
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (us *UidSlice) Rem(uid Uid) bool {
|
|
|
+ idx, found := us.find(uid)
|
|
|
+ if !found {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ if idx == len(*us)-1 {
|
|
|
+ *us = (*us)[:idx]
|
|
|
+ } else {
|
|
|
+ *us = append((*us)[:idx], (*us)[idx+1:]...)
|
|
|
+ }
|
|
|
+ return true
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (us UidSlice) Contains(uid Uid) bool {
|
|
|
+ _, contains := us.find(uid)
|
|
|
+ return contains
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (uid Uid) P2PName(u2 Uid) string {
|
|
|
+ if !uid.IsZero() && !u2.IsZero() {
|
|
|
+ b1, _ := uid.MarshalBinary()
|
|
|
+ b2, _ := u2.MarshalBinary()
|
|
|
+
|
|
|
+ if uid < u2 {
|
|
|
+ b1 = append(b1, b2...)
|
|
|
+ } else if uid > u2 {
|
|
|
+ b1 = append(b2, b1...)
|
|
|
+ } else {
|
|
|
+
|
|
|
+ return ""
|
|
|
+ }
|
|
|
+
|
|
|
+ return "p2p" + base64.URLEncoding.WithPadding(base64.NoPadding).EncodeToString(b1)
|
|
|
+ }
|
|
|
+
|
|
|
+ return ""
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func ParseP2P(p2p string) (uid1, uid2 Uid, err error) {
|
|
|
+ if strings.HasPrefix(p2p, "p2p") {
|
|
|
+ src := []byte(p2p)[3:]
|
|
|
+ if len(src) != p2pBase64Unpadded {
|
|
|
+ err = errors.New("ParseP2P: invalid length")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ dec := make([]byte, base64.URLEncoding.WithPadding(base64.NoPadding).DecodedLen(p2pBase64Unpadded))
|
|
|
+ var count int
|
|
|
+ count, err = base64.URLEncoding.WithPadding(base64.NoPadding).Decode(dec, src)
|
|
|
+ if count < 16 {
|
|
|
+ if err != nil {
|
|
|
+ err = errors.New("ParseP2P: failed to decode " + err.Error())
|
|
|
+ } else {
|
|
|
+ err = errors.New("ParseP2P: invalid decoded length")
|
|
|
+ }
|
|
|
+ return
|
|
|
+ }
|
|
|
+ uid1 = Uid(binary.LittleEndian.Uint64(dec))
|
|
|
+ uid2 = Uid(binary.LittleEndian.Uint64(dec[8:]))
|
|
|
+ } else {
|
|
|
+ err = errors.New("ParseP2P: missing or invalid prefix")
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+type ObjHeader struct {
|
|
|
+ Id string
|
|
|
+ id Uid
|
|
|
+ CreatedAt time.Time
|
|
|
+ UpdatedAt time.Time
|
|
|
+ DeletedAt *time.Time `json:"DeletedAt,omitempty"`
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (h *ObjHeader) Uid() Uid {
|
|
|
+ if h.id.IsZero() && h.Id != "" {
|
|
|
+ h.id.UnmarshalText([]byte(h.Id))
|
|
|
+ }
|
|
|
+ return h.id
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (h *ObjHeader) SetUid(uid Uid) {
|
|
|
+ h.id = uid
|
|
|
+ h.Id = uid.String()
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func TimeNow() time.Time {
|
|
|
+ return time.Now().UTC().Round(time.Millisecond)
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (h *ObjHeader) InitTimes() {
|
|
|
+ if h.CreatedAt.IsZero() {
|
|
|
+ h.CreatedAt = TimeNow()
|
|
|
+ }
|
|
|
+ h.UpdatedAt = h.CreatedAt
|
|
|
+ h.DeletedAt = nil
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (h *ObjHeader) MergeTimes(h2 *ObjHeader) {
|
|
|
+
|
|
|
+ if h.CreatedAt.IsZero() || (!h2.CreatedAt.IsZero() && h2.CreatedAt.Before(h.CreatedAt)) {
|
|
|
+ h.CreatedAt = h2.CreatedAt
|
|
|
+ }
|
|
|
+
|
|
|
+ if h.UpdatedAt.Before(h2.UpdatedAt) {
|
|
|
+ h.UpdatedAt = h2.UpdatedAt
|
|
|
+ }
|
|
|
+
|
|
|
+ if h2.DeletedAt != nil && (h.DeletedAt == nil || h.DeletedAt.Before(*h2.DeletedAt)) {
|
|
|
+ h.DeletedAt = h2.DeletedAt
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (h *ObjHeader) IsDeleted() bool {
|
|
|
+ return h.DeletedAt != nil
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+type StringSlice []string
|
|
|
+
|
|
|
+
|
|
|
+func (ss *StringSlice) Scan(val interface{}) error {
|
|
|
+ return json.Unmarshal(val.([]byte), ss)
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (ss StringSlice) Value() (driver.Value, error) {
|
|
|
+ return json.Marshal(ss)
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+type User struct {
|
|
|
+ ObjHeader
|
|
|
+
|
|
|
+ State int
|
|
|
+
|
|
|
+
|
|
|
+ Access DefaultAccess
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ LastSeen *time.Time
|
|
|
+
|
|
|
+ UserAgent string
|
|
|
+
|
|
|
+ Public interface{}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ Tags StringSlice
|
|
|
+
|
|
|
+
|
|
|
+ Devices map[string]*DeviceDef
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+type AccessMode uint
|
|
|
+
|
|
|
+
|
|
|
+const (
|
|
|
+ ModeJoin AccessMode = 1 << iota
|
|
|
+ ModeRead
|
|
|
+ ModeWrite
|
|
|
+ ModePres
|
|
|
+ ModeApprove
|
|
|
+ ModeShare
|
|
|
+ ModeDelete
|
|
|
+ ModeOwner
|
|
|
+ ModeUnset
|
|
|
+
|
|
|
+
|
|
|
+ ModeNone AccessMode = 0
|
|
|
+
|
|
|
+
|
|
|
+ ModeCPublic AccessMode = ModeJoin | ModeRead | ModeWrite | ModePres | ModeShare
|
|
|
+
|
|
|
+ ModeCSelf AccessMode = ModeJoin | ModePres | ModeShare
|
|
|
+
|
|
|
+ ModeCFull AccessMode = ModeJoin | ModeRead | ModeWrite | ModePres | ModeApprove | ModeShare | ModeDelete | ModeOwner
|
|
|
+
|
|
|
+ ModeCP2P AccessMode = ModeJoin | ModeRead | ModeWrite | ModePres | ModeApprove
|
|
|
+
|
|
|
+ ModeCReadOnly = ModeJoin | ModeRead
|
|
|
+
|
|
|
+
|
|
|
+ ModeCAdmin = ModeOwner | ModeApprove
|
|
|
+
|
|
|
+ ModeCSharer = ModeCAdmin | ModeShare
|
|
|
+
|
|
|
+
|
|
|
+ ModeInvalid AccessMode = 0x100000
|
|
|
+
|
|
|
+
|
|
|
+ ModeBitmask AccessMode = ModeJoin | ModeRead | ModeWrite | ModePres | ModeApprove | ModeShare | ModeDelete | ModeOwner
|
|
|
+)
|
|
|
+
|
|
|
+
|
|
|
+func (m AccessMode) MarshalText() ([]byte, error) {
|
|
|
+ if m == ModeNone {
|
|
|
+ return []byte{'N'}, nil
|
|
|
+ }
|
|
|
+
|
|
|
+ if m == ModeInvalid {
|
|
|
+ return nil, errors.New("AccessMode invalid")
|
|
|
+ }
|
|
|
+
|
|
|
+ var res = []byte{}
|
|
|
+ var modes = []byte{'J', 'R', 'W', 'P', 'A', 'S', 'D', 'O'}
|
|
|
+ for i, chr := range modes {
|
|
|
+ if (m & (1 << uint(i))) != 0 {
|
|
|
+ res = append(res, chr)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return res, nil
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+func (m *AccessMode) UnmarshalText(b []byte) error {
|
|
|
+ m0 := ModeUnset
|
|
|
+
|
|
|
+Loop:
|
|
|
+ for i := 0; i < len(b); i++ {
|
|
|
+ switch b[i] {
|
|
|
+ case 'J', 'j':
|
|
|
+ m0 |= ModeJoin
|
|
|
+ case 'R', 'r':
|
|
|
+ m0 |= ModeRead
|
|
|
+ case 'W', 'w':
|
|
|
+ m0 |= ModeWrite
|
|
|
+ case 'A', 'a':
|
|
|
+ m0 |= ModeApprove
|
|
|
+ case 'S', 's':
|
|
|
+ m0 |= ModeShare
|
|
|
+ case 'D', 'd':
|
|
|
+ m0 |= ModeDelete
|
|
|
+ case 'P', 'p':
|
|
|
+ m0 |= ModePres
|
|
|
+ case 'O', 'o':
|
|
|
+ m0 |= ModeOwner
|
|
|
+ case 'N', 'n':
|
|
|
+ m0 = ModeNone
|
|
|
+ break Loop
|
|
|
+ default:
|
|
|
+ return errors.New("AccessMode: invalid character '" + string(b[i]) + "'")
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if m0 != ModeUnset {
|
|
|
+ *m = m0
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (m AccessMode) String() string {
|
|
|
+ res, err := m.MarshalText()
|
|
|
+ if err != nil {
|
|
|
+ return ""
|
|
|
+ }
|
|
|
+ return string(res)
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (m AccessMode) MarshalJSON() ([]byte, error) {
|
|
|
+ res, err := m.MarshalText()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ return append(append([]byte{'"'}, res...), '"'), nil
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (m *AccessMode) UnmarshalJSON(b []byte) error {
|
|
|
+ if b[0] != '"' || b[len(b)-1] != '"' {
|
|
|
+ return errors.New("syntax error")
|
|
|
+ }
|
|
|
+
|
|
|
+ return m.UnmarshalText(b[1 : len(b)-1])
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+func (m *AccessMode) Scan(val interface{}) error {
|
|
|
+ if bb, ok := val.([]byte); ok {
|
|
|
+ return m.UnmarshalText(bb)
|
|
|
+ }
|
|
|
+ return errors.New("scan failed: data is not a byte slice")
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (m AccessMode) Value() (driver.Value, error) {
|
|
|
+ res, err := m.MarshalText()
|
|
|
+ if err != nil {
|
|
|
+ return "", err
|
|
|
+ }
|
|
|
+ return string(res), nil
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (grant AccessMode) BetterThan(want AccessMode) bool {
|
|
|
+ return ModeBitmask&grant&^want != 0
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (grant AccessMode) BetterEqual(want AccessMode) bool {
|
|
|
+ return ModeBitmask&grant&want == want
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+func (o AccessMode) Delta(n AccessMode) string {
|
|
|
+
|
|
|
+ o2n := ModeBitmask & o &^ n
|
|
|
+ var removed string
|
|
|
+ if o2n > 0 {
|
|
|
+ removed = o2n.String()
|
|
|
+ if removed != "" {
|
|
|
+ removed = "-" + removed
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ n2o := ModeBitmask & n &^ o
|
|
|
+ var added string
|
|
|
+ if n2o > 0 {
|
|
|
+ added = n2o.String()
|
|
|
+ if added != "" {
|
|
|
+ added = "+" + added
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return added + removed
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (m AccessMode) IsJoiner() bool {
|
|
|
+ return m&ModeJoin != 0
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (m AccessMode) IsOwner() bool {
|
|
|
+ return m&ModeOwner != 0
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (m AccessMode) IsApprover() bool {
|
|
|
+ return m&ModeApprove != 0
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (m AccessMode) IsAdmin() bool {
|
|
|
+ return m.IsOwner() || m.IsApprover()
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (m AccessMode) IsSharer() bool {
|
|
|
+ return m.IsAdmin() || (m&ModeShare != 0)
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (m AccessMode) IsWriter() bool {
|
|
|
+ return m&ModeWrite != 0
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (m AccessMode) IsReader() bool {
|
|
|
+ return m&ModeRead != 0
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (m AccessMode) IsPresencer() bool {
|
|
|
+ return m&ModePres != 0
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (m AccessMode) IsDeleter() bool {
|
|
|
+ return m&ModeDelete != 0
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (m AccessMode) IsZero() bool {
|
|
|
+ return m == ModeNone
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (m AccessMode) IsInvalid() bool {
|
|
|
+ return m == ModeInvalid
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+func (m AccessMode) IsDefined() bool {
|
|
|
+ return m != ModeInvalid && m != ModeUnset
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+type DefaultAccess struct {
|
|
|
+ Auth AccessMode
|
|
|
+ Anon AccessMode
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+func (da *DefaultAccess) Scan(val interface{}) error {
|
|
|
+ return json.Unmarshal(val.([]byte), da)
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (da DefaultAccess) Value() (driver.Value, error) {
|
|
|
+ return json.Marshal(da)
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+type Credential struct {
|
|
|
+ ObjHeader
|
|
|
+
|
|
|
+ User string
|
|
|
+
|
|
|
+ Method string
|
|
|
+
|
|
|
+ Value string
|
|
|
+
|
|
|
+ Resp string
|
|
|
+
|
|
|
+ Done bool
|
|
|
+
|
|
|
+ Retries int
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+type Subscription struct {
|
|
|
+ ObjHeader
|
|
|
+
|
|
|
+ User string
|
|
|
+
|
|
|
+ Topic string
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ DelId int
|
|
|
+
|
|
|
+ RecvSeqId int
|
|
|
+
|
|
|
+ ReadSeqId int
|
|
|
+
|
|
|
+
|
|
|
+ ModeWant AccessMode
|
|
|
+
|
|
|
+ ModeGiven AccessMode
|
|
|
+
|
|
|
+ Private interface{}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ public interface{}
|
|
|
+
|
|
|
+ seqId int
|
|
|
+
|
|
|
+ touchedAt *time.Time
|
|
|
+
|
|
|
+ lastSeen time.Time
|
|
|
+
|
|
|
+ userAgent string
|
|
|
+
|
|
|
+
|
|
|
+ with string
|
|
|
+
|
|
|
+ modeDefault *DefaultAccess
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (s *Subscription) SetPublic(pub interface{}) {
|
|
|
+ s.public = pub
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (s *Subscription) GetPublic() interface{} {
|
|
|
+ return s.public
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (s *Subscription) SetWith(with string) {
|
|
|
+ s.with = with
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (s *Subscription) GetWith() string {
|
|
|
+ return s.with
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (s *Subscription) GetTouchedAt() *time.Time {
|
|
|
+ return s.touchedAt
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (s *Subscription) SetTouchedAt(touchedAt *time.Time) {
|
|
|
+ if s.touchedAt == nil || touchedAt.After(*s.touchedAt) {
|
|
|
+ s.touchedAt = touchedAt
|
|
|
+ }
|
|
|
+
|
|
|
+ if s.touchedAt.Before(s.UpdatedAt) {
|
|
|
+ s.touchedAt = &s.UpdatedAt
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (s *Subscription) GetSeqId() int {
|
|
|
+ return s.seqId
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (s *Subscription) SetSeqId(id int) {
|
|
|
+ s.seqId = id
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (s *Subscription) GetLastSeen() time.Time {
|
|
|
+ return s.lastSeen
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (s *Subscription) GetUserAgent() string {
|
|
|
+ return s.userAgent
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (s *Subscription) SetLastSeenAndUA(when *time.Time, ua string) {
|
|
|
+ if when != nil {
|
|
|
+ s.lastSeen = *when
|
|
|
+ }
|
|
|
+ s.userAgent = ua
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (s *Subscription) SetDefaultAccess(auth, anon AccessMode) {
|
|
|
+ s.modeDefault = &DefaultAccess{auth, anon}
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (s *Subscription) GetDefaultAccess() *DefaultAccess {
|
|
|
+ return s.modeDefault
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+type Contact struct {
|
|
|
+ Id string
|
|
|
+ MatchOn []string
|
|
|
+ Access DefaultAccess
|
|
|
+ LastSeen time.Time
|
|
|
+ Public interface{}
|
|
|
+}
|
|
|
+
|
|
|
+type perUserData struct {
|
|
|
+ private interface{}
|
|
|
+ want AccessMode
|
|
|
+ given AccessMode
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+type Topic struct {
|
|
|
+ ObjHeader
|
|
|
+
|
|
|
+
|
|
|
+ TouchedAt *time.Time
|
|
|
+
|
|
|
+
|
|
|
+ UseBt bool
|
|
|
+
|
|
|
+
|
|
|
+ Owner string
|
|
|
+
|
|
|
+
|
|
|
+ Access DefaultAccess
|
|
|
+
|
|
|
+
|
|
|
+ SeqId int
|
|
|
+
|
|
|
+ DelId int
|
|
|
+
|
|
|
+ Public interface{}
|
|
|
+
|
|
|
+
|
|
|
+ Tags StringSlice
|
|
|
+
|
|
|
+
|
|
|
+ perUser map[Uid]*perUserData
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (t *Topic) GiveAccess(uid Uid, want, given AccessMode) {
|
|
|
+ if t.perUser == nil {
|
|
|
+ t.perUser = make(map[Uid]*perUserData, 1)
|
|
|
+ }
|
|
|
+
|
|
|
+ pud := t.perUser[uid]
|
|
|
+ if pud == nil {
|
|
|
+ pud = &perUserData{}
|
|
|
+ }
|
|
|
+
|
|
|
+ pud.want = want
|
|
|
+ pud.given = given
|
|
|
+
|
|
|
+ t.perUser[uid] = pud
|
|
|
+ if want&given&ModeOwner != 0 && t.Owner == "" {
|
|
|
+ t.Owner = uid.String()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (t *Topic) SetPrivate(uid Uid, private interface{}) {
|
|
|
+ if t.perUser == nil {
|
|
|
+ t.perUser = make(map[Uid]*perUserData, 1)
|
|
|
+ }
|
|
|
+ pud := t.perUser[uid]
|
|
|
+ if pud == nil {
|
|
|
+ pud = &perUserData{}
|
|
|
+ }
|
|
|
+ pud.private = private
|
|
|
+ t.perUser[uid] = pud
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (t *Topic) GetPrivate(uid Uid) (private interface{}) {
|
|
|
+ if t.perUser == nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ pud := t.perUser[uid]
|
|
|
+ if pud == nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ private = pud.private
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (t *Topic) GetAccess(uid Uid) (mode AccessMode) {
|
|
|
+ if t.perUser == nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ pud := t.perUser[uid]
|
|
|
+ if pud == nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ mode = pud.given & pud.want
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+type SoftDelete struct {
|
|
|
+ User string
|
|
|
+ DelId int
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+type MessageHeaders map[string]interface{}
|
|
|
+
|
|
|
+
|
|
|
+func (mh *MessageHeaders) Scan(val interface{}) error {
|
|
|
+ return json.Unmarshal(val.([]byte), mh)
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (mh MessageHeaders) Value() (driver.Value, error) {
|
|
|
+ return json.Marshal(mh)
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+type Message struct {
|
|
|
+ ObjHeader
|
|
|
+
|
|
|
+ DelId int `json:"DelId,omitempty"`
|
|
|
+
|
|
|
+ DeletedFor []SoftDelete `json:"DeletedFor,omitempty"`
|
|
|
+ SeqId int
|
|
|
+ Topic string
|
|
|
+
|
|
|
+ From string
|
|
|
+ Head MessageHeaders `json:"Head,omitempty"`
|
|
|
+ Content interface{}
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+type Range struct {
|
|
|
+ Low int
|
|
|
+ Hi int `json:"Hi,omitempty"`
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+type RangeSorter []Range
|
|
|
+
|
|
|
+
|
|
|
+func (rs RangeSorter) Len() int {
|
|
|
+ return len(rs)
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (rs RangeSorter) Swap(i, j int) {
|
|
|
+ rs[i], rs[j] = rs[j], rs[i]
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+func (rs RangeSorter) Less(i, j int) bool {
|
|
|
+ if rs[i].Low < rs[j].Low {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ if rs[i].Low == rs[j].Low {
|
|
|
+ return rs[i].Hi >= rs[j].Hi
|
|
|
+ }
|
|
|
+ return false
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+func (rs RangeSorter) Normalize() RangeSorter {
|
|
|
+ ll := rs.Len()
|
|
|
+ if ll > 1 {
|
|
|
+ prev := 0
|
|
|
+ for i := 1; i < ll; i++ {
|
|
|
+ if rs[prev].Low == rs[i].Low {
|
|
|
+
|
|
|
+
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ if rs[prev].Hi > 0 && rs[prev].Hi+1 >= rs[i].Low {
|
|
|
+
|
|
|
+ if rs[prev].Hi < rs[i].Hi {
|
|
|
+ rs[prev].Hi = rs[i].Hi
|
|
|
+ }
|
|
|
+
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ prev++
|
|
|
+ }
|
|
|
+ rs = rs[:prev+1]
|
|
|
+ }
|
|
|
+
|
|
|
+ return rs
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+type DelMessage struct {
|
|
|
+ ObjHeader
|
|
|
+ Topic string
|
|
|
+ DeletedFor string
|
|
|
+ DelId int
|
|
|
+ SeqIdRanges []Range
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+type QueryOpt struct {
|
|
|
+
|
|
|
+ User Uid
|
|
|
+ Topic string
|
|
|
+ IfModifiedSince *time.Time
|
|
|
+
|
|
|
+ Since int
|
|
|
+ Before int
|
|
|
+
|
|
|
+ Limit int
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+type TopicCat int
|
|
|
+
|
|
|
+const (
|
|
|
+
|
|
|
+ TopicCatMe TopicCat = iota
|
|
|
+
|
|
|
+ TopicCatFnd
|
|
|
+
|
|
|
+ TopicCatP2P
|
|
|
+
|
|
|
+ TopicCatGrp
|
|
|
+)
|
|
|
+
|
|
|
+
|
|
|
+func GetTopicCat(name string) TopicCat {
|
|
|
+ switch name[:3] {
|
|
|
+ case "usr":
|
|
|
+ return TopicCatMe
|
|
|
+ case "p2p":
|
|
|
+ return TopicCatP2P
|
|
|
+ case "grp":
|
|
|
+ return TopicCatGrp
|
|
|
+ case "fnd":
|
|
|
+ return TopicCatFnd
|
|
|
+ default:
|
|
|
+ panic("invalid topic type for name '" + name + "'")
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+type DeviceDef struct {
|
|
|
+
|
|
|
+ DeviceId string
|
|
|
+
|
|
|
+ Platform string
|
|
|
+
|
|
|
+ LastSeen time.Time
|
|
|
+
|
|
|
+ Lang string
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+const (
|
|
|
+
|
|
|
+ UploadStarted = iota
|
|
|
+
|
|
|
+ UploadCompleted
|
|
|
+
|
|
|
+ UploadFailed
|
|
|
+)
|
|
|
+
|
|
|
+
|
|
|
+type FileDef struct {
|
|
|
+ ObjHeader
|
|
|
+
|
|
|
+ Status int
|
|
|
+
|
|
|
+ User string
|
|
|
+
|
|
|
+ MimeType string
|
|
|
+
|
|
|
+ Size int64
|
|
|
+
|
|
|
+ Location string
|
|
|
+}
|