6e1a3987b7
This commit adds a command called `self-update` which downloads the latest released version of restic from GitHub and replacing the current binary with it. It does not rely on any external program (so it'll work everywhere), but still verifies the GPG signature using the embedded GPG public key. By default, the `self-update` command is hidden behind the `selfupdate` built tag, which is only set when restic is built using `build.go`. The reason for this is that downstream distributions will then not include the command by default, so users are encouraged to use the platform-specific distribution mechanism.
160 lines
3.4 KiB
Go
160 lines
3.4 KiB
Go
// Copyright 2011 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package packet
|
|
|
|
import (
|
|
"io"
|
|
"io/ioutil"
|
|
"strings"
|
|
)
|
|
|
|
// UserId contains text that is intended to represent the name and email
|
|
// address of the key holder. See RFC 4880, section 5.11. By convention, this
|
|
// takes the form "Full Name (Comment) <email@example.com>"
|
|
type UserId struct {
|
|
Id string // By convention, this takes the form "Full Name (Comment) <email@example.com>" which is split out in the fields below.
|
|
|
|
Name, Comment, Email string
|
|
}
|
|
|
|
func hasInvalidCharacters(s string) bool {
|
|
for _, c := range s {
|
|
switch c {
|
|
case '(', ')', '<', '>', 0:
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// NewUserId returns a UserId or nil if any of the arguments contain invalid
|
|
// characters. The invalid characters are '\x00', '(', ')', '<' and '>'
|
|
func NewUserId(name, comment, email string) *UserId {
|
|
// RFC 4880 doesn't deal with the structure of userid strings; the
|
|
// name, comment and email form is just a convention. However, there's
|
|
// no convention about escaping the metacharacters and GPG just refuses
|
|
// to create user ids where, say, the name contains a '('. We mirror
|
|
// this behaviour.
|
|
|
|
if hasInvalidCharacters(name) || hasInvalidCharacters(comment) || hasInvalidCharacters(email) {
|
|
return nil
|
|
}
|
|
|
|
uid := new(UserId)
|
|
uid.Name, uid.Comment, uid.Email = name, comment, email
|
|
uid.Id = name
|
|
if len(comment) > 0 {
|
|
if len(uid.Id) > 0 {
|
|
uid.Id += " "
|
|
}
|
|
uid.Id += "("
|
|
uid.Id += comment
|
|
uid.Id += ")"
|
|
}
|
|
if len(email) > 0 {
|
|
if len(uid.Id) > 0 {
|
|
uid.Id += " "
|
|
}
|
|
uid.Id += "<"
|
|
uid.Id += email
|
|
uid.Id += ">"
|
|
}
|
|
return uid
|
|
}
|
|
|
|
func (uid *UserId) parse(r io.Reader) (err error) {
|
|
// RFC 4880, section 5.11
|
|
b, err := ioutil.ReadAll(r)
|
|
if err != nil {
|
|
return
|
|
}
|
|
uid.Id = string(b)
|
|
uid.Name, uid.Comment, uid.Email = parseUserId(uid.Id)
|
|
return
|
|
}
|
|
|
|
// Serialize marshals uid to w in the form of an OpenPGP packet, including
|
|
// header.
|
|
func (uid *UserId) Serialize(w io.Writer) error {
|
|
err := serializeHeader(w, packetTypeUserId, len(uid.Id))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = w.Write([]byte(uid.Id))
|
|
return err
|
|
}
|
|
|
|
// parseUserId extracts the name, comment and email from a user id string that
|
|
// is formatted as "Full Name (Comment) <email@example.com>".
|
|
func parseUserId(id string) (name, comment, email string) {
|
|
var n, c, e struct {
|
|
start, end int
|
|
}
|
|
var state int
|
|
|
|
for offset, rune := range id {
|
|
switch state {
|
|
case 0:
|
|
// Entering name
|
|
n.start = offset
|
|
state = 1
|
|
fallthrough
|
|
case 1:
|
|
// In name
|
|
if rune == '(' {
|
|
state = 2
|
|
n.end = offset
|
|
} else if rune == '<' {
|
|
state = 5
|
|
n.end = offset
|
|
}
|
|
case 2:
|
|
// Entering comment
|
|
c.start = offset
|
|
state = 3
|
|
fallthrough
|
|
case 3:
|
|
// In comment
|
|
if rune == ')' {
|
|
state = 4
|
|
c.end = offset
|
|
}
|
|
case 4:
|
|
// Between comment and email
|
|
if rune == '<' {
|
|
state = 5
|
|
}
|
|
case 5:
|
|
// Entering email
|
|
e.start = offset
|
|
state = 6
|
|
fallthrough
|
|
case 6:
|
|
// In email
|
|
if rune == '>' {
|
|
state = 7
|
|
e.end = offset
|
|
}
|
|
default:
|
|
// After email
|
|
}
|
|
}
|
|
switch state {
|
|
case 1:
|
|
// ended in the name
|
|
n.end = len(id)
|
|
case 3:
|
|
// ended in comment
|
|
c.end = len(id)
|
|
case 6:
|
|
// ended in email
|
|
e.end = len(id)
|
|
}
|
|
|
|
name = strings.TrimSpace(id[n.start:n.end])
|
|
comment = strings.TrimSpace(id[c.start:c.end])
|
|
email = strings.TrimSpace(id[e.start:e.end])
|
|
return
|
|
}
|