-
Notifications
You must be signed in to change notification settings - Fork 8
/
database.go
355 lines (317 loc) · 9.14 KB
/
database.go
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
package main
import (
_ "code.google.com/p/go-sqlite/go1/sqlite3"
"database/sql"
"errors"
_ "github.com/go-sql-driver/mysql"
"time"
)
var (
Db DB
)
type DB struct {
*sql.DB
ReadOnly bool
}
// InitializeTables issues the commands to create all tables and
// columns. Will not create the tables if they exist already. If
// it encounters an error, it is returned.
func (db DB) InitializeTables() (err error) {
// First, create the 'nodes' table.
_, err = db.Query(`CREATE TABLE IF NOT EXISTS nodes (
address BINARY(16) PRIMARY KEY,
owner VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL,
contact VARCHAR(255),
details VARCHAR(255),
pgp BINARY(8),
lat FLOAT NOT NULL,
lon FLOAT NOT NULL,
status INT NOT NULL,
updated INT NOT NULL);`)
if err != nil {
return
}
_, err = db.Query(`CREATE TABLE IF NOT EXISTS nodes_cached (
address BINARY(16) PRIMARY KEY,
owner VARCHAR(255) NOT NULL,
details VARCHAR(255),
lat FLOAT NOT NULL,
lon FLOAT NOT NULL,
status INT NOT NULL,
source INT NOT NULL,
retrieved INT NOT NULL);`)
if err != nil {
return
}
_, err = db.Query(`CREATE TABLE IF NOT EXISTS nodes_verify_queue (
id INT PRIMARY KEY,
address BINARY(16) NOT NULL,
owner VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL,
contact VARCHAR(255),
details VARCHAR(255),
pgp BINARY(8),
lat FLOAT NOT NULL,
lon FLOAT NOT NULL,
status INT NOT NULL,
verifysent BOOL NOT NULL,
expiration INT NOT NULL);`)
if err != nil {
return
}
_, err = db.Query(`CREATE TABLE IF NOT EXISTS cached_maps (
id INTEGER PRIMARY KEY AUTOINCREMENT,
hostname VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL);`)
if err != nil {
return
}
_, err = db.Query(`CREATE TABLE IF NOT EXISTS captcha (
id BINARY(32) NOT NULL,
solution BINARY(6) NOT NULL,
expiration INT NOT NULL);`)
if err != nil {
return
}
return
}
// LenNodes returns the number of nodes in the database. If there is
// an error, it returns -1 and logs the incident.
func (db DB) LenNodes(useCached bool) (n int) {
// Count the number of rows in the 'nodes' table.
var row *sql.Row
if useCached {
row = db.QueryRow("SELECT COUNT(*) FROM (SELECT address FROM nodes UNION SELECT address FROM nodes_cached);")
} else {
row = db.QueryRow("SELECT COUNT(*) FROM nodes;")
}
// Write that number to n, and return if there is no
// error. Otherwise, log it and return zero.
if err := row.Scan(&n); err != nil {
l.Errf("Error counting the number of nodes: %s", err)
n = -1
}
return
}
// DumpNodes returns an array containing all nodes in the database,
// including both local and cached nodes.
func (db DB) DumpNodes() (nodes []*Node, err error) {
// Begin by getting the required length of the array. If we get
// -1, then there has been an error.
if n := db.LenNodes(true); n != -1 {
// If successful, initialize the array with the length.
nodes = make([]*Node, n)
} else {
// Otherwise, error out.
l.Errf("Could not count number of nodes in database\n")
return nil, errors.New("Could not count number of nodes")
}
// Perform the query.
rows, err := db.Query(`
SELECT address,owner,contact,details,pgp,lat,lon,status,0
FROM nodes
UNION SELECT address,owner,"",details,"",lat,lon,status,source
FROM nodes_cached;`)
if err != nil {
l.Errf("Error dumping database: %s", err)
return
}
defer rows.Close()
// Now, loop through, initialize the nodes, and fill them out
// using only the selected columns.
for i := 0; rows.Next(); i++ {
// Initialize the node and put it in the table.
node := new(Node)
nodes[i] = node
// Create temporary values to simplify scanning.
contact := sql.NullString{}
details := sql.NullString{}
// Scan all of the values into it.
err = rows.Scan(&node.Addr, &node.OwnerName,
&contact, &details, &node.PGP,
&node.Latitude, &node.Longitude, &node.Status, &node.SourceID)
if err != nil {
l.Errf("Error dumping database: %s", err)
return
}
node.Contact = contact.String
node.Details = details.String
}
return
}
// DumpLocal returns a slice containing all of the local nodes in the
// database.
func (db DB) DumpLocal() (nodes []*Node, err error) {
// Begin by getting the required length of the array. If we get
// -1, then there has been an error.
if n := db.LenNodes(true); n != -1 {
// If successful, initialize the array with the length.
nodes = make([]*Node, n)
} else {
// Otherwise, error out.
l.Errf("Could not count number of nodes in database\n")
return nil, errors.New("Could not count number of nodes")
}
// Perform the query.
rows, err := db.Query(`
SELECT address,owner,contact,details,pgp,lat,lon,status
FROM nodes;`)
if err != nil {
l.Errf("Error dumping database: %s", err)
return
}
defer rows.Close()
// Now, loop through, initialize the nodes, and fill them out
// using only the selected columns.
for i := 0; rows.Next(); i++ {
// Initialize the node and put it in the table.
node := new(Node)
nodes[i] = node
// Create temporary values to simplify scanning.
contact := sql.NullString{}
details := sql.NullString{}
// Scan all of the values into it.
err = rows.Scan(&node.Addr, &node.OwnerName,
&contact, &details, &node.PGP,
&node.Latitude, &node.Longitude, &node.Status)
if err != nil {
l.Errf("Error dumping database: %s", err)
return
}
node.Contact = contact.String
node.Details = details.String
}
return
}
// DumpChanges returns all nodes, both local and cached, which have
// been updated or retrieved more recently than the given time.
func (db DB) DumpChanges(time time.Time) (nodes []*Node, err error) {
rows, err := db.Query(`
SELECT address,owner,contact,details,pgp,lat,lon,status
FROM nodes WHERE updated >= ?
UNION
SELECT address,owner,"",details,"",lat,lon,status
FROM nodes_cached WHERE retrieved >= ?;`, time, time)
if err != nil {
return
}
// Append each node to the array in sequence.
for rows.Next() {
node := new(Node)
contact := sql.NullString{}
details := sql.NullString{}
err = rows.Scan(&node.Addr, &node.OwnerName,
&contact, &details, &node.PGP,
&node.Latitude, &node.Longitude, &node.Status)
if err != nil {
return
}
node.Contact = contact.String
node.Details = details.String
nodes = append(nodes, node)
}
return
}
// AddNode inserts a node into the 'nodes' table with the current
// timestamp.
func (db DB) AddNode(node *Node) (err error) {
// Inserts a new node into the database
stmt, err := db.Prepare(`INSERT INTO nodes
(address, owner, email, contact, details, pgp, lat, lon, status, updated)
VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
if err != nil {
return
}
_, err = stmt.Exec([]byte(node.Addr), node.OwnerName, node.OwnerEmail,
node.Contact, node.Details, []byte(node.PGP),
node.Latitude, node.Longitude, node.Status,
time.Now())
stmt.Close()
return
}
func (db DB) AddNodes(nodes []*Node) (err error) {
stmt, err := db.Prepare(`INSERT INTO nodes
(address, owner, email, contact, details, pgp, lat, lon, status, updated)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);`)
if err != nil {
return
}
for _, node := range nodes {
_, err = stmt.Exec([]byte(node.Addr),
node.OwnerName, node.OwnerEmail,
node.Contact, node.Details, []byte(node.PGP),
node.Latitude, node.Longitude, node.Status,
time.Now())
if err != nil {
return
}
}
stmt.Close()
return
}
// UpdateNode replaces the node in the database with the IP matching
// the given node.
func (db DB) UpdateNode(node *Node) (err error) {
// Updates an existing node in the database
stmt, err := db.Prepare(`UPDATE nodes SET
owner = ?, contact = ?, details = ?, pgp = ?, lat = ?, lon = ?, status = ?
WHERE address = ?`)
if err != nil {
return
}
_, err = stmt.Exec(node.OwnerName, node.Contact,
node.Details, []byte(node.PGP),
node.Latitude, node.Longitude, node.Status, []byte(node.Addr))
stmt.Close()
return
}
// DeleteNode removes the node with the matching IP from the database.
func (db DB) DeleteNode(addr IP) (err error) {
// Deletes the given node from the database
stmt, err := db.Prepare("DELETE FROM nodes WHERE address = ?")
if err != nil {
return
}
_, err = stmt.Exec([]byte(addr))
stmt.Close()
return
}
// GetNode retrieves a single node from the database using the given
// address. If there is a database error, it will be returned. If no
// node matches, however, both return values will be nil.
func (db DB) GetNode(addr IP) (node *Node, err error) {
// Retrieves the node with the given address from the database
stmt, err := db.Prepare(`
SELECT owner, email, contact, details, pgp, lat, lon, status
FROM nodes
WHERE address = ?
UNION
SELECT owner, "", "", details, "", lat, lon, status
FROM nodes_cached
WHERE address = ?
LIMIT 1`)
if err != nil {
return
}
// Initialize the node and temporary variable.
node = &Node{Addr: addr}
baddr := []byte(addr)
contact := sql.NullString{}
details := sql.NullString{}
// Perform the actual query.
row := stmt.QueryRow(baddr, baddr)
err = row.Scan(&node.OwnerName, &node.OwnerEmail,
&contact, &details, &node.PGP,
&node.Latitude, &node.Longitude, &node.Status)
stmt.Close()
node.Contact = contact.String
node.Details = details.String
// If the error is of the particular type sql.ErrNoRows, it simply
// means that the node does not exist. In that case, return (nil,
// nil).
if err == sql.ErrNoRows {
return nil, nil
}
return
}