updates.go 3.3 KB
Newer Older
1
package irmaclient
Sietse Ringers's avatar
Sietse Ringers committed
2
3

import (
4
5
	"encoding/json"
	"fmt"
6
	"github.com/timshannon/bolthold"
7
	"time"
Sietse Ringers's avatar
Sietse Ringers committed
8

9
	"github.com/privacybydesign/irmago"
10
	"go.etcd.io/bbolt"
Sietse Ringers's avatar
Sietse Ringers committed
11
12
)

13
// This file contains the update mechanism for Client
Sietse Ringers's avatar
Sietse Ringers committed
14
15
// as well as updates themselves.

16
type update struct {
17
	When    irma.Timestamp
18
19
20
	Number  int
	Success bool
	Error   *string
21
22
}

23
var clientUpdates = []func(client *Client) error{
24
	// 0: Convert old cardemu.xml Android storage to our own storage format
25
	nil, // No longer necessary as the Android app was deprecated long ago
26

27
	// 1: Adding scheme manager index, signature and public key
28
29
	// Check the signatures of all scheme managers, if any is not ok,
	// copy the entire irma_configuration folder from assets
30
	nil, // made irrelevant by irma_configuration-autocopying
31

32
	// 2: Rename config -> preferences
33
	nil, // No longer necessary
34

35
	// 3: Copy new irma_configuration out of assets
36
	nil, // made irrelevant by irma_configuration-autocopying
37

38
	// 4: For each keyshare server, include in its struct the identifier of its scheme manager
39
	nil, // No longer necessary
40

41
	// 5: Remove the test scheme manager which was erroneously included in a production build
42
	nil, // No longer necessary, also broke many unit tests
Tomas's avatar
Tomas committed
43

44
	// 6: Remove earlier log items of wrong format
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
	nil, // No longer necessary

	// 7: Concert log entries to bolthold database
	func(client *Client) error {
		var logs []*LogEntry
		var err error

		start := time.Now()
		fmt.Println(start.UnixNano(), "started")
		if err = client.storage.load(&logs, logsFile); err != nil {
			return err
		}

		loaded := time.Now()
		fmt.Println(loaded.Sub(start), "loaded")

		// Open one bolt transaction to process all our log entries in
		err = client.storage.db.Bolt().Update(func(tx *bbolt.Tx) error {
			for _, log := range logs {
64
				// As log.Request is a json.RawMessage it would not get updated to the new session request
65
66
67
68
69
70
71
72
73
74
				// format by re-marshaling the containing struct, as normal struct members would,
				// so update it manually now by marshaling the session request into it.
				req, err := log.SessionRequest()
				if err != nil {
					return err
				}
				log.Request, err = json.Marshal(req)
				if err != nil {
					return err
				}
75
				if err = client.storage.db.TxUpsert(tx, bolthold.NextSequence(), log); err != nil {
76
77
78
79
80
81
82
83
84
85
					return err
				}
			}
			return nil
		})

		done := time.Now()
		fmt.Println(done.Sub(loaded), "done converting", len(logs), "logs")

		return err
Tomas's avatar
Tomas committed
86
	},
87
}
88

89
// update performs any function from clientUpdates that has not
90
91
// already been executed in the past, keeping track of previously executed updates
// in the file at updatesFile.
92
func (client *Client) update() error {
93
94
	// Load and parse file containing info about already performed updates
	var err error
95
	if client.updates, err = client.storage.LoadUpdates(); err != nil {
96
97
98
99
		return err
	}

	// Perform all new updates
100
	for i := len(client.updates); i < len(clientUpdates); i++ {
101
102
103
		err = nil
		if clientUpdates[i] != nil {
			err = clientUpdates[i](client)
104
		}
Sietse Ringers's avatar
Sietse Ringers committed
105
		u := update{
106
			When:    irma.Timestamp(time.Now()),
107
108
109
110
111
			Number:  i,
			Success: err == nil,
		}
		if err != nil {
			str := err.Error()
Sietse Ringers's avatar
Sietse Ringers committed
112
			u.Error = &str
113
		}
114
		client.updates = append(client.updates, u)
115
116
117
		if err != nil {
			break
		}
118
119
	}

120
121
122
123
124
	storeErr := client.storage.StoreUpdates(client.updates)
	if storeErr != nil {
		return storeErr
	}
	return err
125
}