diff --git a/0.go b/0.go index 21f7756..49a6fcf 100644 --- a/0.go +++ b/0.go @@ -42,6 +42,16 @@ func getCPascalStringArrayFromStringSlice(goSlice []string) C.PascalStringArray return C.PascalStringArray{data: cArrPtr, len: C.int(len(goSlice))} } +// getStringSliceFromCPascalStringArray converts a C.PascalStringArray to a go slice. +func getStringSliceFromCPascalStringArray(pascalStringArray C.PascalStringArray) []string { + var goSlice []string + cArr := (*[1 << 30]C.PascalString)(unsafe.Pointer(pascalStringArray.data))[:pascalStringArray.len:pascalStringArray.len] + for i := 0; i < int(pascalStringArray.len); i++ { + goSlice = append(goSlice, C.GoStringN(cArr[i].data, cArr[i].len)) + } + return goSlice +} + // GetPascalStringFromCString is a helper meant to be used from C. // //export GetPascalStringFromCString diff --git a/README.md b/README.md index 0d1b322..d832199 100644 --- a/README.md +++ b/README.md @@ -78,16 +78,16 @@ int main() { - [X] config - [X] Write(cfg *CfgT, appendMode bool) error - [X] Load() (*CfgT, error) -- [ ] core - - [ ] EntryAddPrecheck(realPath string) (uint8, error) - - [ ] EntryIsNotEmpty(entryData []string) bool - - [ ] EntryRefresh(oldRCWPassword, newRCWPassword []byte, removeOldDir bool) error - - [ ] GenTOTP(secret string, time time.Time) (string, error) - - [ ] GetOldEntryData(realPath string, field int, rcwPassword []byte) ([]string, error) - - [ ] LibmuttonInit(inputCB func(prompt string) string, rcwPassword []byte, ...) error - - [ ] RCWSanityCheckGen(password []byte) error - - [ ] VerifyEntries(rcwPassword []byte) error - - [ ] WriteEntry(realPath string, decSlice []string, passwordIsNew bool, rcwPassword []byte) error +- [X] core + - [X] EntryAddPrecheck(realPath string) (uint8, error) + - [X] EntryIsNotEmpty(entryData []string) bool + - [X] EntryRefresh(oldRCWPassword, newRCWPassword []byte, removeOldDir bool) error + - [X] GenTOTP(secret string, time time.Time) (string, error) + - [X] GetOldEntryData(realPath string, field int, rcwPassword []byte) ([]string, error) + - [X] LibmuttonInit(inputCB func(prompt string) string, rcwPassword []byte, ...) error + - [X] RCWSanityCheckGen(password []byte) error + - [X] VerifyEntries(rcwPassword []byte) error + - [X] WriteEntry(realPath string, decSlice []string, passwordIsNew bool, rcwPassword []byte) error - [X] crypt - [X] ~~VAR: RetryPassword bool~~ (RCWD not supported) - [X] DecryptFileToSlice(realPath string) ([]string, error) @@ -102,7 +102,7 @@ int main() { - [X] GetRealPath(vanityPath string) string - [X] ~~GetSysProcAttr() *syscall.SysProcAttr~~ (not for use outside of libmutton) - [X] GetVanityPath(realPath string) string -- [ ] syncclient +- [X] syncclient - [X] AddFolderRemote(vanityPath string) error - [X] GenDeviceID(oldDeviceID, prefix string) (string, string, bool, error) - [X] ~~GetSSHClient() (*ssh.Client, bool, *bool, *string, *string, error)~~ (not for use outside of libmutton) diff --git a/clip.go b/clip.go index 045a406..ed66ede 100644 --- a/clip.go +++ b/clip.go @@ -9,7 +9,7 @@ import ( // ClearProcess clears the clipboard in 30 seconds if its contents still match assignedContents. // If assignedContents is an empty string, the clipboard will clear immediately and unconditionally. -// +// Returns: // r0: err // //export ClearProcess diff --git a/config.go b/config.go index b7dd6b1..1e6fda2 100644 --- a/config.go +++ b/config.go @@ -8,6 +8,7 @@ import ( ) // LoadOfficialCfg returns the currently set (libmutton official) values in the libmutton config file. +// Returns: // r0: err // // r1: OfflineMode (bool) @@ -38,6 +39,7 @@ func LoadOfficialCfg() (*C.char, bool, C.PascalString, C.PascalString, C.PascalS } // WriteOfficialCfg writes the provided (libmutton official) values to the libmutton config file. +// Returns: // r0: err // //export WriteOfficialCfg diff --git a/core.go b/core.go new file mode 100644 index 0000000..e0fe3bb --- /dev/null +++ b/core.go @@ -0,0 +1,142 @@ +package main + +// #include +// #include "types.h" +import "C" +import ( + "strings" + "time" + + "github.com/rwinkhart/libmutton/core" +) + +// EntryAddPrecheck returns: +// r0: err +// +// r1: status +// +//export EntryAddPrecheck +func EntryAddPrecheck(realPath C.PascalString) (*C.char, uint8) { + status, err := core.EntryAddPrecheck(C.GoStringN(realPath.data, realPath.len)) + if err != nil { + return C.CString(err.Error()), status + } + return nil, status +} + +// EntryIsNotEmpty returns: +// r0: notEmpty +// +//export EntryIsNotEmpty +func EntryIsNotEmpty(entryData C.PascalStringArray) bool { + notEmpty := core.EntryIsNotEmpty(getStringSliceFromCPascalStringArray(entryData)) + return notEmpty +} + +// EntryRefresh returns: +// r0: err +// +//export EntryRefresh +func EntryRefresh(oldRCWPassword, newRCWPassword C.PascalString, removeOldDir bool) *C.char { + err := core.EntryRefresh([]byte(C.GoStringN(oldRCWPassword.data, oldRCWPassword.len)), []byte(C.GoStringN(newRCWPassword.data, newRCWPassword.len)), removeOldDir) + if err != nil { + return C.CString(err.Error()) + } + return nil +} + +// GenTOTP returns: +// r0: err +// +// r1: token +// +//export GenTOTP +func GenTOTP(secret C.PascalString, unixTimestamp int64) (*C.char, C.PascalString) { + token, err := core.GenTOTP(C.GoStringN(secret.data, secret.len), time.Unix(unixTimestamp, 0)) + if err != nil { + return C.CString(err.Error()), C.PascalString{data: nil, len: -1} + } + return nil, getPascalString(token) +} + +// GetOldEntryData returns: +// r0: err +// +// r1: lines +// +//export GetOldEntryData +func GetOldEntryData(realPath C.PascalString, field int, rcwPassword C.PascalString) (*C.char, C.PascalStringArray) { + lines, err := core.GetOldEntryData(C.GoStringN(realPath.data, realPath.len), field, []byte(C.GoStringN(rcwPassword.data, rcwPassword.len))) + if err != nil { + return C.CString(err.Error()), C.PascalStringArray{} + } + return nil, getCPascalStringArrayFromStringSlice(lines) +} + +// LibmuttonInit is a partial implementation that does not allow for client-specific config. +// Returns: +// r0: err +// +// Special requirement: +// An array of ordered responses to inputCB questions. +// +//export LibmuttonInit +func LibmuttonInit(inputCBResponses C.PascalStringArray, rcwPassword C.PascalString, appendMode bool, forceOfflineMode bool, deviceIDPrefix C.PascalString) *C.char { + inputCBRespSlice := getStringSliceFromCPascalStringArray(inputCBResponses) + supplyInputForInit := func(prompt string) string { + if strings.HasPrefix(prompt, "Configure SSH settings (for synchronization)? (Y/n)") { + return inputCBRespSlice[0] + } else if strings.HasPrefix(prompt, "SSH private identity file path (falls back to") { + return inputCBRespSlice[1] + } else if strings.HasPrefix(prompt, "Is the identity file password-protected? (y/N)") { + return inputCBRespSlice[2] + } else if strings.HasPrefix(prompt, "Remote SSH username:") { + return inputCBRespSlice[3] + } else if strings.HasPrefix(prompt, "Remote SSH IP/domain:") { + return inputCBRespSlice[4] + } else if strings.HasPrefix(prompt, "Remote SSH port:") { + return inputCBRespSlice[5] + } + return "" + } + + if err := core.LibmuttonInit(supplyInputForInit, []byte(C.GoStringN(rcwPassword.data, rcwPassword.len)), appendMode, forceOfflineMode, C.GoStringN(deviceIDPrefix.data, deviceIDPrefix.len), nil); err != nil { + return C.CString(err.Error()) + } + return nil +} + +// RCWSanityCheckGen returns: +// r0: err +// +//export RCWSanityCheckGen +func RCWSanityCheckGen(password C.PascalString) *C.char { + err := core.RCWSanityCheckGen([]byte(C.GoStringN(password.data, password.len))) + if err != nil { + return C.CString(err.Error()) + } + return nil +} + +// VerifyEntries returns: +// r0: err +// +//export VerifyEntries +func VerifyEntries(rcwPassword C.PascalString) *C.char { + err := core.VerifyEntries([]byte(C.GoStringN(rcwPassword.data, rcwPassword.len))) + if err != nil { + return C.CString(err.Error()) + } + return nil +} + +// WriteEntry returns: +// r0: err +// +//export WriteEntry +func WriteEntry(realPath C.PascalString, decSlice C.PascalStringArray, passwordIsNew bool, rcwPassword C.PascalString) *C.char { + if err := core.WriteEntry(C.GoStringN(realPath.data, realPath.len), getStringSliceFromCPascalStringArray(decSlice), passwordIsNew, []byte(C.GoStringN(rcwPassword.data, rcwPassword.len))); err != nil { + return C.CString(err.Error()) + } + return nil +} diff --git a/go.mod b/go.mod index a2f4054..69aa860 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,8 @@ module cmutton go 1.26rc2 require ( - github.com/rwinkhart/libmutton v0.4.3-0.20260124190333-b963ccf33e54 - github.com/rwinkhart/rcw v0.2.4 + github.com/rwinkhart/libmutton v0.4.3-0.20260125215941-ec9c6b506232 + github.com/rwinkhart/rcw v0.2.5 ) require ( diff --git a/go.sum b/go.sum index 910fbb0..4f0974d 100644 --- a/go.sum +++ b/go.sum @@ -16,12 +16,12 @@ github.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs= github.com/pquerna/otp v1.5.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg= github.com/rwinkhart/go-boilerplate v0.2.2 h1:SVHTAQU+HWFivtUnDBcfrgClJV5ZmHyFS7/uERh7NKU= github.com/rwinkhart/go-boilerplate v0.2.2/go.mod h1:/NVRKGslU20E5xU5YOgXzWxA6aa94BMtv5MtHRTb5Ek= -github.com/rwinkhart/libmutton v0.4.3-0.20260124190333-b963ccf33e54 h1:04PtrDqnj6NrrhY5Uu21wc/N3n9lio+WCD6+55xbaIo= -github.com/rwinkhart/libmutton v0.4.3-0.20260124190333-b963ccf33e54/go.mod h1:4/Ry5uDUaWTdNqVR/URhqM6NveB3dFfxCmO+LyisBcQ= +github.com/rwinkhart/libmutton v0.4.3-0.20260125215941-ec9c6b506232 h1:NzUoqkjBy9u9a3wV+D1DR2DemVpYi0Hxug800ybarZE= +github.com/rwinkhart/libmutton v0.4.3-0.20260125215941-ec9c6b506232/go.mod h1:63SCVQaWcg4JiEBxYEtLwAiRxXQvlpyFsmWUxxfdIpU= github.com/rwinkhart/peercred-mini v0.1.2 h1:4cGWDbv0whvLeVvbUdx84V/9p+2fS+DEXgrA1KxlRFo= github.com/rwinkhart/peercred-mini v0.1.2/go.mod h1:LLHG7YshHEpbpJJP+Il9nx2dnGj5O3VGE32rWmflj0c= -github.com/rwinkhart/rcw v0.2.4 h1:FrRSsl+ui1WLmDwqjLSROtFPKqSeGLEfF2SPENt4qP0= -github.com/rwinkhart/rcw v0.2.4/go.mod h1:nhW4RAHvEjW2QcOlqEdWqRQOFJLjoOGSPkSRZlMjU3s= +github.com/rwinkhart/rcw v0.2.5 h1:3GJeii9sDZsZNL+HRkfHrnbgxnaB8a9kejiMIfPXnpE= +github.com/rwinkhart/rcw v0.2.5/go.mod h1:qVw8Yp/SKtsuVZYOzzluVEVkoRJ1G7UEh2aR1CXso/U= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=