Skip to content

Commit

Permalink
deep-copy update public and private fields
Browse files Browse the repository at this point in the history
  • Loading branch information
or-else committed Feb 1, 2019
1 parent ede3d8c commit a0c79d4
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 33 deletions.
2 changes: 1 addition & 1 deletion docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ The `fnd` topic expects `public` to be a string representing a [search query](#q

### Private

The format of the `private` field in group and peer to peer topics is expected to be a dictionary. The following fields are currently defined:
The format of the `private` field in group and peer to peer topics is expected to be a set of key-value pairs. The following keys are currently defined:
```js
private: {
comment: "some comment", // string, optional user comment about a topic or a peer user
Expand Down
16 changes: 0 additions & 16 deletions server/store/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -471,22 +471,6 @@ type MessagesObjMapper struct{}
// Messages is an instance of MessagesObjMapper to map methods to.
var Messages MessagesObjMapper

func interfaceToStringSlice(src interface{}) []string {
var dst []string
if src != nil {
if arr, ok := src.([]string); ok {
dst = arr
} else if arr, ok := src.([]interface{}); ok {
for _, val := range arr {
if str, ok := val.(string); ok {
dst = append(dst, str)
}
}
}
}
return dst
}

// Save message
func (MessagesObjMapper) Save(msg *types.Message) error {
msg.InitTimes()
Expand Down
22 changes: 8 additions & 14 deletions server/topic.go
Original file line number Diff line number Diff line change
Expand Up @@ -1351,15 +1351,9 @@ func (t *Topic) replySetDesc(sess *Session, asUid types.Uid, set *MsgClientSet)
return nil
}

assignGenericValues := func(upd map[string]interface{}, what string, p interface{}) (changed bool) {
if isNullValue(p) {
// Request to clear the value
upd[what] = nil
changed = true
} else if p != nil {
// A new non-nil value
upd[what] = p
changed = true
assignGenericValues := func(upd map[string]interface{}, what string, dst, src interface{}) (changed bool) {
if dst, changed = mergeInterfaces(dst, src); changed {
upd[what] = dst
}
return
}
Expand All @@ -1376,11 +1370,11 @@ func (t *Topic) replySetDesc(sess *Session, asUid types.Uid, set *MsgClientSet)
case types.TopicCatMe:
// Update current user
err = assignAccess(core, set.Desc.DefaultAcs)
sendPres = assignGenericValues(core, "Public", set.Desc.Public)
sendPres = assignGenericValues(core, "Public", t.public, set.Desc.Public)
case types.TopicCatFnd:
// set.Desc.DefaultAcs is ignored.
// Do not send presence if fnd.Public has changed.
assignGenericValues(core, "Public", set.Desc.Public)
assignGenericValues(core, "Public", t.fndGetPublic(sess), set.Desc.Public)
case types.TopicCatP2P:
// Reject direct changes to P2P topics.
if set.Desc.Public != nil || set.Desc.DefaultAcs != nil {
Expand All @@ -1391,7 +1385,7 @@ func (t *Topic) replySetDesc(sess *Session, asUid types.Uid, set *MsgClientSet)
// Update group topic
if t.owner == asUid {
err = assignAccess(core, set.Desc.DefaultAcs)
sendPres = assignGenericValues(core, "Public", set.Desc.Public)
sendPres = assignGenericValues(core, "Public", t.public, set.Desc.Public)
} else if set.Desc.DefaultAcs != nil || set.Desc.Public != nil {
// This is a request from non-owner
sess.queueOut(ErrPermissionDenied(set.Id, set.Topic, now))
Expand All @@ -1404,15 +1398,15 @@ func (t *Topic) replySetDesc(sess *Session, asUid types.Uid, set *MsgClientSet)
return err
}

sendPres = sendPres || assignGenericValues(sub, "Private", set.Desc.Private)
sendPres = sendPres || assignGenericValues(sub, "Private", t.perUser[asUid].private, set.Desc.Private)
}

if len(core) > 0 {
switch t.cat {
case types.TopicCatMe:
err = store.Users.Update(asUid, core)
case types.TopicCatFnd:
// The only value is Public, and Public for fnd is not saved according to specs.
// The only value to be stored in topic is Public, and Public for fnd is not saved according to specs.
default:
err = store.Topics.Update(t.name, core)
}
Expand Down
86 changes: 84 additions & 2 deletions server/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"errors"
"fmt"
"path/filepath"
"reflect"
"regexp"
"sort"
"strconv"
Expand All @@ -24,6 +25,8 @@ import (

var tagPrefixRegexp = regexp.MustCompile(`^([a-z]\w{0,5}):\S`)

const nullValue = "\u2421"

// Convert a list of IDs into ranges
func delrangeDeserialize(in []types.Range) []MsgDelRange {
if len(in) == 0 {
Expand Down Expand Up @@ -225,9 +228,8 @@ func msgOpts2storeOpts(req *MsgGetOpts) *types.QueryOpt {

// Check if the interface contains a string with a single Unicode Del control character.
func isNullValue(i interface{}) bool {
const clearValue = "\u2421"
if str, ok := i.(string); ok {
return str == clearValue
return str == nullValue
}
return false
}
Expand Down Expand Up @@ -643,3 +645,83 @@ func parseTLSConfig(tlsEnabled bool, jsconfig json.RawMessage) (*tls.Config, err
}
return &tls.Config{Certificates: []tls.Certificate{cert}}, nil
}

// Merge source interface{} into destination interface.
// If values are maps,deep-merge them. Otherwise shallow-copy.
// Returns dst, true if the dst value was changed.
func mergeInterfaces(dst, src interface{}) (interface{}, bool) {
var changed bool

if src == nil {
return dst, changed
}

vsrc := reflect.ValueOf(src)
switch vsrc.Kind() {
case reflect.Map:
if xsrc, ok := src.(map[string]interface{}); ok {
xdst, _ := dst.(map[string]interface{})
dst, changed = mergeMaps(xdst, xsrc)
} else {
changed = true
dst = src
}
case reflect.String:
if vsrc.String() == nullValue {
changed = dst != nil
dst = nil
} else if src != nil {
changed = true
dst = src
}
default:
changed = true
dst = src
}
return dst, changed
}

// Deep copy maps.
func mergeMaps(dst, src map[string]interface{}) (map[string]interface{}, bool) {
var changed bool

if len(src) == 0 {
return dst, changed
}

if dst == nil {
dst = make(map[string]interface{})
}

for key, val := range src {
xval := reflect.ValueOf(val)
switch xval.Kind() {
case reflect.Map:
if xsrc, _ := val.(map[string]interface{}); xsrc != nil {
// Deep-copy map[string]interface{}
xdst, _ := dst[key].(map[string]interface{})
var lchange bool
dst[key], lchange = mergeMaps(xdst, xsrc)
changed = changed || lchange
} else if val != nil {
// The map is shallow-copied if it's not of the type map[string]interface{}
dst[key] = val
changed = true
}
case reflect.String:
changed = true
if xval.String() == nullValue {
delete(dst, key)
} else if val != nil {
dst[key] = val
}
default:
if val != nil {
dst[key] = val
changed = true
}
}
}

return dst, changed
}

0 comments on commit a0c79d4

Please sign in to comment.