@@ -12,13 +12,9 @@ import (
"os"
"os/exec"
"path"
"path/filepath"
"reflect"
"strings"
"time"
"github.com/golang/glog"
git "github.com/libgit2/git2go"
)
const debug = true
@@ -55,20 +51,6 @@ func assertExists(path ...string) (e error) {
type Database struct {
Path string
Repo * git.Repository
}
var pw2Signature = git.Signature {
Name : "pw2" ,
Email : gpgUserEmail ,
}
func gitSignature () (s * git.Signature ) {
so := pw2Signature
so .When = time .Now ()
s = & so
return
}
type cmdLogWriter struct {
@@ -103,7 +85,7 @@ func cmd(command string, arguments ...string) (c *exec.Cmd) {
return
}
func (d * Database ) GenerateWebGPGUser (password string ) (err error ) {
func (d * Database ) GenerateWebGPGUser (password [] byte ) (err error ) {
f , err := ioutil .TempFile ("" , "pw2" )
if err != nil {
return
@@ -113,14 +95,14 @@ func (d *Database) GenerateWebGPGUser(password string) (err error) {
err = os .Remove (f .Name ())
}()
if _ , err = f . WriteString ( `
if _ , err = fmt . Fprintf ( f , `
%echo generating web interface gpg key...
Key-Type: RSA
Key-Length: 2048
Name-Real: pw2
Name-Email: ` + gpgUserEmail + `
Name-Email: ` + gpgUserEmail + `
Name-Comment: this is the GPG key for the PW2 web interface
Passphrase: ` + password ); err != nil {
Passphrase: %s` , password ); err != nil {
return
}
@@ -143,259 +125,66 @@ func (d *Database) BlackboxCommand(command string, params ...string) (c *exec.Cm
return
}
func (d * Database ) commitIndex (message string ) (oid * git.Oid , err error ) {
glog .V (2 ).Infof ("~ git commit -m %+q" , message )
// yes, this is really how the library is meant to be used :(
// https://github.com/libgit2/libgit2/blob/091165c53b2bcd5d41fb71d43ed5a23a3d96bf5d/tests/object/commit/commitstagedfile.c#L21-L134
var index * git.Index
if index , err = d .Repo .Index (); err != nil {
return
}
defer index .Free ()
if glog .V (2 ) {
glog .Infof ("%v files in index" , index .EntryCount ())
}
var treeOid * git.Oid
if treeOid , err = index .WriteTree (); err != nil {
return
}
var tree * git.Tree
if tree , err = d .Repo .LookupTree (treeOid ); err != nil {
return
}
var lastCommit * git.Commit
if lastCommit , err = d .LastCommit (); err != nil {
return
}
sig := gitSignature ()
switch lastCommit {
case nil :
if oid , err = d .Repo .CreateCommit (
"HEAD" ,
sig ,
sig ,
message ,
tree ,
); err != nil {
return
}
default :
defer lastCommit .Free ()
if oid , err = d .Repo .CreateCommit (
"HEAD" ,
sig ,
sig ,
message ,
tree ,
lastCommit ,
); err != nil {
return
}
}
return
}
func (d * Database ) LastCommit () (c * git.Commit , err error ) {
// look up last commit
var head * git.Reference
if head , err = d .Repo .Head (); err != nil {
fmt .Println (reflect .TypeOf (err ))
if gE , ok := err .(* git.GitError ); ok && gE .Code == git .ErrUnbornBranch {
err = nil
}
return
}
if c , err = d .Repo .LookupCommit (head .Target ()); err != nil {
return
}
return
}
func findGlob (base string , dirRecurse bool , pattern string ) (matches []string , err error ) {
ms , err := filepath .Glob (filepath .Join (base , pattern ))
if err != nil {
return
}
for _ , m := range ms {
if dirRecurse {
var inf os.FileInfo
inf , err = os .Stat (m )
if err != nil {
return
}
if inf .IsDir () {
var dirMatches []string
var rel string
if rel , err = filepath .Rel (base , m ); err != nil {
return
}
dirMatches , err = findGlob (base , dirRecurse , filepath .Join (rel , "*" ))
if err != nil {
return
}
matches = append (matches , dirMatches ... )
continue
}
}
m , err = filepath .Rel (base , m )
if err != nil {
return
}
matches = append (matches , m )
}
return
}
type ErrUnmatched []string
func (e ErrUnmatched ) Error () string {
return fmt .Sprintf ("expected %s to match files" , strings .Join ([]string (e ), " " ))
}
func findAllGlob (base string , mustMatchAll , dirRecurse bool , pattern ... string ) (matches []string , err error ) {
var unmatchedPattern ErrUnmatched = make ([]string , 0 , len (pattern ))
for _ , p := range pattern {
var ms []string
ms , err = findGlob (base , dirRecurse , p )
if err != nil {
return
}
func (d * Database ) gitCmd (arguments ... string ) (c * exec.Cmd ) {
c = cmd ("git" , arguments ... )
if mustMatchAll && len (ms ) < 1 {
unmatchedPattern = append (unmatchedPattern , p )
}
matches = append (matches , ms ... )
}
if len (unmatchedPattern ) > 0 {
err = unmatchedPattern
}
return
}
func (d * Database ) addFilesGlob (patterns ... string ) (err error ) {
files , err := findAllGlob ("git" , true , true , patterns ... )
if err != nil {
return
}
return d .addFiles (files ... )
}
func (d * Database ) addFiles (files ... string ) (err error ) {
glog .V (2 ).Infof ("~ git add %s" , strings .Join (files , " " ))
var index * git.Index
if index , err = d .Repo .Index (); err != nil {
return
}
for _ , f := range files {
if err = index .AddByPath (f ); err != nil {
return
}
}
c .Dir = d .Path
return
}
func Create (location string , webPassword string ) (d Database , err error ) {
func Create (location string , webPassword [] byte ) (d Database , err error ) {
d .Path = location
// make sure the git repo folder exists
if err = os .Mkdir (location , defaultMode ); err != nil {
return
}
// make the actual git repo
if d .Repo , err = git .InitRepository (location /*🐻*/ , false ); err != nil {
return
}
// add the blackbox submodule
var md * git.Submodule
if md , err = d .Repo .Submodules .Add (blackboxSubmoduleLocation , "blackbox" , false ); err != nil {
return
}
// get the repo of the new submodule
var mdRepo * git.Repository
if mdRepo , err = md .Open (); err != nil {
return
}
// get the latest version of blackbox from the internart
if err = detachedHeadPull (mdRepo ); err != nil {
return
}
// write the submodule to the superproject index
if err = md .AddToIndex (true ); err != nil {
if err = d .gitCmd ("init" ).Run (); err != nil {
return
}
if err = d .addFiles ( ".gitmodules" ); err != nil {
if err = d .gitCmd ( "submodule" , "add" , blackboxSubmoduleLocation , "blackbox" ). Run ( ); err != nil {
return
}
if _ , err = d .commitIndex ( " add blackbox submodule" ); err != nil {
if err = d .gitCmd ( "commit" , "-m" , " add blackbox submodule"). Run ( ); err != nil {
return
}
if err = d .BlackboxCommand ("initialize" , "yes" ).Run (); err != nil {
return
}
if err = d .addFilesGlob ( " keyrings" , ".gitignore" ); err != nil {
if err = d .gitCmd ( "add" , "--" , " keyrings" , ".gitignore" ). Run ( ); err != nil {
return
}
if _ , err = d .commitIndex ( " 🔒 initialize blackbox 🔒" ); err != nil {
if err = d .gitCmd ( "commit" , "-m" , " 🔒 initialize blackbox 🔒"). Run ( ); err != nil {
return
}
if webPassword != "" {
if webPassword == nil {
if err = d .GenerateWebGPGUser (webPassword ); err != nil {
return
}
webPassword = nil
if err = d .BlackboxCommand ("addadmin" , gpgUserEmail , path .Join (".." , gpgHomedir )).
Run (); err != nil {
return
}
if err = d .addFiles (
if err = d .gitCmd ( "add" , "--" ,
"keyrings/live/pubring.kbx" ,
"keyrings/live/trustdb.gpg" ,
"keyrings/live/blackbox-admins.txt" ); err != nil {
"keyrings/live/blackbox-admins.txt" ). Run () ; err != nil {
return
}
if _ , err = d .commitIndex ( " + add web client as blackbox admin" ); err != nil {
if err = d .gitCmd ( "commit" , "-m" , " + add web client as blackbox admin"). Run ( ); err != nil {
return
}
@@ -404,62 +193,10 @@ func Create(location string, webPassword string) (d Database, err error) {
return
}
func detachedHeadPull (r * git.Repository ) (err error ) {
glog .Info ("pulling remote..." )
var origin * git.Remote
if origin , err = r .Remotes .Lookup ("origin" ); err != nil {
return
}
if err = origin .Fetch ([]string {}, & git.FetchOptions {}, "" ); err != nil {
return
}
if err = r .SetHead ("refs/remotes/origin/master" ); err != nil {
return
}
var ref * git.Reference
if ref , err = r .Head (); err != nil {
return
}
var commit * git.Commit
if commit , err = r .LookupCommit (ref .Target ()); err != nil {
return
}
if err = r .ResetToCommit (commit , git .ResetHard , & git.CheckoutOpts {
Strategy : git .CheckoutForce ,
DirMode : 0700 ,
FileMode : 0700 ,
}); err != nil {
return
}
glog .Info ("remote pull complete" )
return
}
//Func DatabaseNotFound returns true if err is a git2go not found error ("object not found").
//This error would be returned for example when you're trying to load a database
//from a directory which doesn't yet exist.
func DatabaseNotFound (err error ) bool {
v , ok := err .(* git.GitError )
return ok && v .Code == git .ErrNotFound
}
//Func open opens a directory as a pw2 Database.
func Open (location string ) (d Database , err error ) {
d .Path = location
if d .Repo , err = git .OpenRepository (location ); err != nil {
return
}
return
}