~hockeypuck/hockeypuck/0.8

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
/*
   Hockeypuck - OpenPGP key server
   Copyright (C) 2012  Casey Marshall

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published by
   the Free Software Foundation, version 3.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package hockeypuck

// Merge the contents of srcKey into dstKey, modifying in-place.
// Packets in src not found in dst are appended to the matching parent.
// Conflicting packets and unmatched parents are ignored.
func MergeKey(dstKey *PubKey, srcKey *PubKey) {
	dstObjects := mapKey(dstKey)
	pktObjChan := make(chan PacketObject)
	go func() {
		srcKey.Traverse(pktObjChan)
		close(pktObjChan)
	}()
	// Track src parent object in src traverse
	var srcPubKey *PubKey
	var srcUserId *UserId
	var srcSignable PacketObject
	var srcParent PacketObject
	var hasParent bool
	for srcObj := range pktObjChan {
		switch srcObj.(type) {
		case *PubKey:
			srcPubKey = srcObj.(*PubKey)
			srcSignable = srcObj
			srcParent = nil
			hasParent = false
		case *UserId:
			srcUserId = srcObj.(*UserId)
			srcSignable = srcObj
			srcParent = srcPubKey
			hasParent = true
		case *UserAttribute:
			srcSignable = srcObj
			srcParent = srcUserId
			hasParent = true
		case *SubKey:
			srcSignable = srcObj
			srcParent = srcPubKey
			hasParent = true
		case *Signature:
			srcParent = srcSignable
			hasParent = true
		}
		// match in dst tree
		_, dstHas := dstObjects[srcObj.GetDigest()]
		if dstHas {
			continue // We already have it
		}
		if hasParent {
			dstParentObj, dstHasParent := dstObjects[srcParent.GetDigest()]
			if dstHasParent {
				appendPacketObject(dstParentObj, srcObj)
			}
		}
	}
}

// Map a tree of packet objects by strong hash.
func mapKey(root PacketObject) (objects map[string]PacketObject) {
	objects = make(map[string]PacketObject)
	pktObjChan := make(chan PacketObject)
	go func() {
		root.Traverse(pktObjChan)
		close(pktObjChan)
	}()
	for pktObj := range pktObjChan {
		objects[pktObj.GetDigest()] = pktObj
	}
	return
}

// Append a src packet under dst parent.
func appendPacketObject(dstParent PacketObject, srcObj PacketObject) {
	if sig, isa := srcObj.(*Signature); isa {
		if dst, isa := dstParent.(Signable); isa {
			dst.AppendSig(sig)
		}
	} else if uattr, isa := srcObj.(*UserAttribute); isa {
		if uid, isa := dstParent.(*UserId); isa {
			uid.Attributes = append(uid.Attributes, uattr)
		}
	} else if uid, isa := srcObj.(*UserId); isa {
		if pubKey, isa := dstParent.(*PubKey); isa {
			pubKey.Identities = append(pubKey.Identities, uid)
		}
	} else if subKey, isa := srcObj.(*SubKey); isa {
		if pubKey, isa := dstParent.(*PubKey); isa {
			pubKey.SubKeys = append(pubKey.SubKeys, subKey)
		}
	}
}