aptly/pgp/gnupg_finder.go

151 lines
3.4 KiB
Go

package pgp
import (
"errors"
"os/exec"
"regexp"
)
// GPGVersion stores discovered GPG version
type GPGVersion int
// GPG version as discovered
const (
GPG1x GPGVersion = 1
GPG20x GPGVersion = 2
GPG21x GPGVersion = 3
GPG22xPlus GPGVersion = 4
)
// GPGFinder implement search for gpg executables and returns version of discovered executables
type GPGFinder interface {
FindGPG() (gpg string, version GPGVersion, err error)
FindGPGV() (gpgv string, version GPGVersion, err error)
}
type pathGPGFinder struct {
gpgNames []string
gpgvNames []string
errorMessage string
expectedVersionSubstring string
}
type iteratingGPGFinder struct {
finders []GPGFinder
errorMessage string
}
// GPGDefaultFinder looks for GPG1 first, but falls back to GPG2 if GPG1 is not available
func GPGDefaultFinder() GPGFinder {
return &iteratingGPGFinder{
finders: []GPGFinder{GPG1Finder(), GPG2Finder()},
errorMessage: "Couldn't find a suitable gpg executable. Make sure gnupg is installed",
}
}
// GPG1Finder looks for GnuPG1.x only
func GPG1Finder() GPGFinder {
return &pathGPGFinder{
gpgNames: []string{"gpg", "gpg1"},
gpgvNames: []string{"gpgv", "gpgv1"},
expectedVersionSubstring: `\(GnuPG.*\) (1).(\d)`,
errorMessage: "Couldn't find a suitable gpg executable. Make sure gnupg1 is available as either gpg(v) or gpg(v)1 in $PATH",
}
}
// GPG2Finder looks for GnuPG2.x only
func GPG2Finder() GPGFinder {
return &pathGPGFinder{
gpgNames: []string{"gpg", "gpg2"},
gpgvNames: []string{"gpgv", "gpgv2"},
expectedVersionSubstring: `\(GnuPG.*\) (2).(\d)`,
errorMessage: "Couldn't find a suitable gpg executable. Make sure gnupg2 is available as either gpg(v) or gpg(v)2 in $PATH",
}
}
func (pgf *pathGPGFinder) FindGPG() (gpg string, version GPGVersion, err error) {
for _, cmd := range pgf.gpgNames {
var result bool
result, version = cliVersionCheck(cmd, pgf.expectedVersionSubstring)
if result {
gpg = cmd
break
}
}
if gpg == "" {
err = errors.New(pgf.errorMessage)
}
return
}
func (pgf *pathGPGFinder) FindGPGV() (gpgv string, version GPGVersion, err error) {
for _, cmd := range pgf.gpgvNames {
var result bool
result, version = cliVersionCheck(cmd, pgf.expectedVersionSubstring)
if result {
gpgv = cmd
break
}
}
if gpgv == "" {
err = errors.New(pgf.errorMessage)
}
return
}
func (it *iteratingGPGFinder) FindGPG() (gpg string, version GPGVersion, err error) {
for _, finder := range it.finders {
gpg, version, err = finder.FindGPG()
if err == nil {
return
}
}
err = errors.New(it.errorMessage)
return
}
func (it *iteratingGPGFinder) FindGPGV() (gpg string, version GPGVersion, err error) {
for _, finder := range it.finders {
gpg, version, err = finder.FindGPGV()
if err == nil {
return
}
}
err = errors.New(it.errorMessage)
return
}
func cliVersionCheck(cmd string, marker string) (result bool, version GPGVersion) {
output, err := exec.Command(cmd, "--version").CombinedOutput()
if err != nil {
return
}
strOutput := string(output)
regex := regexp.MustCompile(marker)
version = GPG22xPlus
matches := regex.FindStringSubmatch(strOutput)
result = (matches != nil)
if matches != nil {
if matches[1] == "1" {
version = GPG1x
} else if matches[1] == "2" && matches[2] == "0" {
version = GPG20x
} else if matches[1] == "2" && matches[2] == "1" {
version = GPG21x
}
}
return
}