~ubuntu-branches/ubuntu/utopic/hockeypuck/utopic-proposed

« back to all changes in this revision

Viewing changes to cmd/hockeypuck/load.go

  • Committer: Package Import Robot
  • Author(s): Casey Marshall
  • Date: 2014-04-13 20:06:01 UTC
  • Revision ID: package-import@ubuntu.com-20140413200601-oxdlqn1gy0x8m55u
Tags: 1.0~rel20140413+7a1892a~trusty
Hockeypuck 1.0 release

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
   Hockeypuck - OpenPGP key server
 
3
   Copyright (C) 2012-2014  Casey Marshall
 
4
 
 
5
   This program is free software: you can redistribute it and/or modify
 
6
   it under the terms of the GNU Affero General Public License as published by
 
7
   the Free Software Foundation, version 3.
 
8
 
 
9
   This program is distributed in the hope that it will be useful,
 
10
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
   GNU Affero General Public License for more details.
 
13
 
 
14
   You should have received a copy of the GNU Affero General Public License
 
15
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
16
*/
 
17
 
 
18
// hockeypuck is an OpenPGP keyserver.
 
19
package main
 
20
 
 
21
import (
 
22
        "crypto/md5"
 
23
        "encoding/hex"
 
24
        "fmt"
 
25
        "log"
 
26
        "os"
 
27
        "path/filepath"
 
28
 
 
29
        "github.com/cmars/conflux"
 
30
        "github.com/cmars/conflux/recon"
 
31
        "github.com/jmoiron/sqlx"
 
32
        "github.com/lib/pq"
 
33
        "launchpad.net/gnuflag"
 
34
 
 
35
        . "github.com/hockeypuck/hockeypuck"
 
36
        "github.com/hockeypuck/hockeypuck/openpgp"
 
37
)
 
38
 
 
39
type loadCmd struct {
 
40
        configuredCmd
 
41
        path            string
 
42
        txnSize         int
 
43
        ignoreDups      bool
 
44
        verifyRoundTrip bool
 
45
 
 
46
        db    *openpgp.DB
 
47
        w     *openpgp.Worker
 
48
        ptree recon.PrefixTree
 
49
        nkeys int
 
50
        tx    *sqlx.Tx
 
51
}
 
52
 
 
53
func (ec *loadCmd) Name() string { return "load" }
 
54
 
 
55
func (ec *loadCmd) Desc() string { return "Load OpenPGP keyring files into database" }
 
56
 
 
57
func newLoadCmd() *loadCmd {
 
58
        cmd := new(loadCmd)
 
59
        flags := gnuflag.NewFlagSet(cmd.Name(), gnuflag.ExitOnError)
 
60
        flags.StringVar(&cmd.configPath, "config", "", "Hockeypuck configuration file")
 
61
        flags.StringVar(&cmd.path, "path", "", "OpenPGP keyring file path or glob pattern")
 
62
        flags.IntVar(&cmd.txnSize, "txn-size", 5000, "Transaction size; public keys per commit")
 
63
        flags.BoolVar(&cmd.ignoreDups, "ignore-dups", false, "Ignore duplicate entries")
 
64
        flags.BoolVar(&cmd.verifyRoundTrip, "verify-round-trip", false, "Fetch key after insert and verify digest (slow)")
 
65
        cmd.flags = flags
 
66
        return cmd
 
67
}
 
68
 
 
69
func (ec *loadCmd) Main() {
 
70
        if ec.path == "" {
 
71
                Usage(ec, "--path is required")
 
72
        }
 
73
        if ec.verifyRoundTrip {
 
74
                ec.txnSize = 1
 
75
        }
 
76
        if ec.txnSize < 1 {
 
77
                Usage(ec, "Invalid --txn-size, must be >= 1")
 
78
        }
 
79
        ec.configuredCmd.Main()
 
80
        InitLog()
 
81
        var err error
 
82
        if ec.db, err = openpgp.NewDB(); err != nil {
 
83
                die(err)
 
84
        }
 
85
        ec.w = &openpgp.Worker{Loader: openpgp.NewLoader(ec.db, true)}
 
86
        // Ensure tables all exist
 
87
        if err = ec.db.CreateTables(); err != nil {
 
88
                die(err)
 
89
        }
 
90
        reconSettings := recon.NewSettings(openpgp.Config().Settings.TomlTree)
 
91
        if ec.ptree, err = openpgp.NewSksPTree(reconSettings); err != nil {
 
92
                die(err)
 
93
        }
 
94
        // Create the prefix tree (if not exists)
 
95
        if err = ec.ptree.Create(); err != nil {
 
96
                die(fmt.Errorf("Unable to create prefix tree: %v", err))
 
97
        }
 
98
        // Ensure tables all exist
 
99
        if err = ec.db.CreateTables(); err != nil {
 
100
                die(fmt.Errorf("Unable to create database tables: %v", err))
 
101
        }
 
102
        // Load all keys from input material
 
103
        ec.loadAllKeys(ec.path)
 
104
        // Close the prefix tree
 
105
        if err = ec.ptree.Close(); err != nil {
 
106
                log.Println("Close ptree:", err)
 
107
        }
 
108
        // Close the database connection
 
109
        if err = ec.db.Close(); err != nil {
 
110
                log.Println("Close database:", err)
 
111
        }
 
112
}
 
113
 
 
114
func (ec *loadCmd) flushDb() {
 
115
        if ec.tx != nil {
 
116
                if !ec.verifyRoundTrip {
 
117
                        log.Println("Loaded", ec.nkeys, "keys")
 
118
                }
 
119
                if err := ec.tx.Commit(); err != nil {
 
120
                        die(fmt.Errorf("Error committing transaction: %v", err))
 
121
                }
 
122
                ec.tx = nil
 
123
                ec.nkeys = 0
 
124
        }
 
125
}
 
126
 
 
127
func (ec *loadCmd) insertKey(keyRead *openpgp.ReadKeyResult) error {
 
128
        var err error
 
129
        if ec.tx == nil {
 
130
                if ec.tx, err = ec.w.Begin(); err != nil {
 
131
                        die(fmt.Errorf("Error starting new transaction: %v", err))
 
132
                }
 
133
        } else if ec.nkeys%ec.txnSize == 0 {
 
134
                ec.flushDb()
 
135
                if ec.tx, err = ec.w.Begin(); err != nil {
 
136
                        die(fmt.Errorf("Error starting new transaction: %v", err))
 
137
                }
 
138
        }
 
139
        // Load key into relational database
 
140
        if err = ec.w.InsertKeyTx(ec.tx, keyRead.Pubkey); err != nil {
 
141
                log.Println("Error inserting key:", keyRead.Pubkey.Fingerprint(), ":", err)
 
142
                if _, is := err.(pq.Error); is {
 
143
                        die(fmt.Errorf("Unable to load due to database errors."))
 
144
                }
 
145
        }
 
146
        ec.nkeys++
 
147
 
 
148
        if ec.verifyRoundTrip {
 
149
                ec.flushDb()
 
150
                loadKey := keyRead.Pubkey
 
151
                loadDigest := openpgp.SksDigest(loadKey, md5.New())
 
152
                if loadKey.Md5 != loadDigest {
 
153
                        log.Println("RTC: loaded key", loadKey.Md5, "!=", "recalculated", loadDigest)
 
154
                }
 
155
                checkKey, err := ec.w.FetchKey(loadKey.RFingerprint)
 
156
                if err != nil {
 
157
                        log.Println("RTC: check failed for", loadKey.Fingerprint(), ":", err)
 
158
                        return err
 
159
                }
 
160
                checkDigest := openpgp.SksDigest(checkKey, md5.New())
 
161
                if checkKey.Md5 != checkDigest {
 
162
                        log.Println("RTC: check key", checkKey.Md5, "!=", "recalculated", checkDigest)
 
163
                }
 
164
                if loadKey.Md5 != checkKey.Md5 {
 
165
                        log.Println("RTC: load key", loadKey.Md5, "!=", "check key", checkKey.Md5)
 
166
                }
 
167
        }
 
168
        return err
 
169
}
 
170
 
 
171
func (ec *loadCmd) loadAllKeys(path string) {
 
172
        keyfiles, err := filepath.Glob(path)
 
173
        if err != nil {
 
174
                die(err)
 
175
        }
 
176
        for _, keyfile := range keyfiles {
 
177
                var f *os.File
 
178
                if f, err = os.Open(keyfile); err != nil {
 
179
                        log.Println("Failed to open", keyfile, ":", err)
 
180
                        continue
 
181
                }
 
182
                defer f.Close()
 
183
                log.Println("Loading keys from", keyfile)
 
184
                defer ec.flushDb()
 
185
                for keyRead := range openpgp.ReadKeys(f) {
 
186
                        if keyRead.Error != nil {
 
187
                                log.Println("Error reading key:", keyRead.Error)
 
188
                                continue
 
189
                        }
 
190
                        digest, err := hex.DecodeString(keyRead.Pubkey.Md5)
 
191
                        if err != nil {
 
192
                                log.Println("bad digest:", keyRead.Pubkey.Md5)
 
193
                                continue
 
194
                        }
 
195
                        digest = recon.PadSksElement(digest)
 
196
                        digestZp := conflux.Zb(conflux.P_SKS, digest)
 
197
                        err = ec.ptree.Insert(digestZp)
 
198
                        if err != nil {
 
199
                                log.Println("Error inserting digest ", keyRead.Pubkey.Md5, ":", err)
 
200
                                continue
 
201
                        }
 
202
                        err = ec.insertKey(keyRead)
 
203
                        if err != nil {
 
204
                                log.Println("Error inserting key", keyRead.Pubkey.Md5, "into database:", err)
 
205
                                // Attempt to remove digest from ptree, since it was not successfully loaded
 
206
                                ec.ptree.Remove(digestZp)
 
207
                                continue
 
208
                        }
 
209
                }
 
210
        }
 
211
}