diff --git a/go.mod b/go.mod index f6f02a7..a1536be 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.25.5 require ( github.com/pkg/sftp v1.13.10 github.com/pquerna/otp v1.5.0 - github.com/rwinkhart/go-boilerplate v0.1.1-0.20251110055016-10ee4f91fcb6 + github.com/rwinkhart/go-boilerplate v0.1.1-0.20251211171453-df1fb21b2366 github.com/rwinkhart/rcw v0.2.3 golang.org/x/crypto v0.45.0 gopkg.in/ini.v1 v1.67.0 diff --git a/go.sum b/go.sum index eba247d..7d58b2f 100644 --- a/go.sum +++ b/go.sum @@ -12,8 +12,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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.1.1-0.20251110055016-10ee4f91fcb6 h1:tGGtw5Wr4laDrU7Ooe95g4E+O6c+oY18atamcMPaMY8= -github.com/rwinkhart/go-boilerplate v0.1.1-0.20251110055016-10ee4f91fcb6/go.mod h1:cnzIF45I0FCOvE4YIB+26pLCUx2kWyY2llKYZruNaRY= +github.com/rwinkhart/go-boilerplate v0.1.1-0.20251211171453-df1fb21b2366 h1:aKB7jcFstg11bRqDnjNzQIGM4+VA4JlE1HDNaPRpm8g= +github.com/rwinkhart/go-boilerplate v0.1.1-0.20251211171453-df1fb21b2366/go.mod h1:cnzIF45I0FCOvE4YIB+26pLCUx2kWyY2llKYZruNaRY= github.com/rwinkhart/go-winio v0.1.0 h1:b72agLW+dETGmhR3VbcbwnStfgKfc5AfgJOXBJDkaHg= github.com/rwinkhart/go-winio v0.1.0/go.mod h1:ZWa7ssZJT30CCDGJ7fk/2SBTq9BIQrrVjrcss0UW2s0= github.com/rwinkhart/peercred-mini v0.1.2 h1:4cGWDbv0whvLeVvbUdx84V/9p+2fS+DEXgrA1KxlRFo= diff --git a/libmuttonserver.go b/libmuttonserver.go index 0693668..4f2ad23 100644 --- a/libmuttonserver.go +++ b/libmuttonserver.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "os" + "runtime/debug" "strings" "github.com/rwinkhart/go-boilerplate/back" @@ -33,6 +34,14 @@ func main() { } } + // allow reporting panic details to clients + defer func() { + if r := recover(); r != nil { + fmt.Printf("{\"errMsg\":\"SERVER-SIDE PANIC OCCURRED!%sARGS: (%v)%sSTDIN: (%v)%sSTACK TRACE: %s\"}", global.FSSpace, os.Args, global.FSSpace, stdin, global.FSSpace, strings.ReplaceAll(strings.ReplaceAll(string(debug.Stack()), "\n", global.FSSpace), "\t", "")) + return + } + }() + switch args[1] { case "fetch": // print all information needed for syncing to stdout for interpretation by the client diff --git a/syncclient/client.go b/syncclient/client.go index 2e139c1..f3a4225 100644 --- a/syncclient/client.go +++ b/syncclient/client.go @@ -155,7 +155,7 @@ func getRemoteDataFromClient(sshClient *ssh.Client) (map[string]int64, map[strin return nil, nil, nil, nil, 0, 0, errors.New("unable to unmarshal server fetch response: " + err.Error()) } if fetchResp.ErrMsg != nil { - return nil, nil, nil, nil, 0, 0, errors.New("unable to complete fetch; server-side error occurred: " + *fetchResp.ErrMsg) + return nil, nil, nil, nil, 0, 0, errors.New("unable to complete fetch; server-side error occurred: " + strings.ReplaceAll(*fetchResp.ErrMsg, global.FSSpace, "\n")) } entryModMap := make(map[string]int64) diff --git a/syncclient/oneOff.go b/syncclient/oneOff.go index b3dd766..be3b1dc 100644 --- a/syncclient/oneOff.go +++ b/syncclient/oneOff.go @@ -48,7 +48,7 @@ func ShearRemoteFromClient(vanityPath string, onlyShearAgeFile bool) error { return errors.New("unable to shear target remotely: " + err.Error()) } if len(output) > 11 { - return errors.New("unable to complete shear; server-side error occurred: " + string(output)[11:len(output)-2]) + return errors.New("unable to complete shear; server-side error occurred: " + strings.ReplaceAll(string(output)[11:len(output)-2], global.FSSpace, "\n")) } // close the SSH client @@ -100,7 +100,7 @@ func RenameRemoteFromClient(oldVanityPath, newVanityPath string) error { return errors.New("unable to rename target remotely: " + err.Error()) } if len(output) > 11 { - return errors.New("unable to complete rename; server-side error occurred: " + string(output)[11:len(output)-2]) + return errors.New("unable to complete rename; server-side error occurred: " + strings.ReplaceAll(string(output)[11:len(output)-2], global.FSSpace, "\n")) } // close the SSH client @@ -141,7 +141,7 @@ func AddFolderRemoteFromClient(vanityPath string) error { return errors.New("unable to add folder remotely: " + err.Error()) } if len(output) > 11 { - return errors.New("unable to complete addfolder; server-side error occurred: " + string(output)[11:len(output)-2]) + return errors.New("unable to complete addfolder; server-side error occurred: " + strings.ReplaceAll(string(output)[11:len(output)-2], global.FSSpace, "\n")) } // close the SSH client diff --git a/synccycles/init.go b/synccycles/init.go index 5bc1ccf..69392ec 100644 --- a/synccycles/init.go +++ b/synccycles/init.go @@ -6,9 +6,11 @@ import ( "math/rand" "os" "strconv" + "strings" "time" "github.com/rwinkhart/go-boilerplate/back" + "github.com/rwinkhart/go-boilerplate/stringy" "github.com/rwinkhart/libmutton/global" "github.com/rwinkhart/libmutton/syncclient" "github.com/rwinkhart/libmutton/synccommon" @@ -24,7 +26,7 @@ func DeviceIDGen(oldDeviceID, prefix string) (string, string, string, error) { if prefix == "" { prefix, _ = os.Hostname() } - newDeviceID := prefix + "-" + StringGen(rand.Intn(32)+48, 0.2, 1) + "-" + strconv.FormatInt(time.Now().Unix(), 10) + newDeviceID := prefix + "-" + stringy.StringGen(rand.Intn(32)+48, 0.2, 1) + "-" + strconv.FormatInt(time.Now().Unix(), 10) // create new device ID file (locally) newDeviceIDPath := global.ConfigDir + global.PathSeparator + "devices" + global.PathSeparator + newDeviceID @@ -68,7 +70,7 @@ func DeviceIDGen(oldDeviceID, prefix string) (string, string, string, error) { } if registerResp.ErrMsg != nil { cleanupOnFail() - return "", "", "", errors.New("unable to complete register; server-side error occurred: " + *registerResp.ErrMsg) + return "", "", "", errors.New("unable to complete register; server-side error occurred: " + strings.ReplaceAll(*registerResp.ErrMsg, global.FSSpace, "\n")) } _ = sshClient.Close() // ignore error; non-critical/unlikely/not much could be done about it diff --git a/synccycles/utilities.go b/synccycles/utilities.go deleted file mode 100644 index d59beba..0000000 --- a/synccycles/utilities.go +++ /dev/null @@ -1,67 +0,0 @@ -package synccycles - -import ( - "crypto/rand" - "fmt" - "math" - "math/big" - "strings" -) - -// StringGen generates a random string of a specified length and complexity. -// Requires: complexity (minimum percentage of special characters to be returned in the generated string; set to 0 to generate a simple string), -// complexCharsetLevel (1 = safe for filenames, 2 = safe for most password entries, 3 = safe only for well-made password entries) -func StringGen(length int, complexity float64, complexCharsetLevel uint8) string { - var actualSpecialChars int // track the number of special characters in the generated string - var minSpecialChars int // track the minimum number of special characters to accept - var extendedCharset string // additions to character set used for complex strings - - charset := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" // default character set used for all strings - const extendedCharsetFiles = "!#$%&+,-.;=@_~^()[]{}`'" // additional special characters for complex strings (safe in file names) - const extendedCharsetMostPassword = "*:> 0 { - minSpecialChars = int(math.Round(float64(length) * complexity)) // determine minimum number of special characters to accept - switch complexCharsetLevel { - case 1: - extendedCharset = extendedCharsetFiles - case 2: - extendedCharset = extendedCharsetMostPassword + extendedCharsetFiles[:len(extendedCharsetFiles)-9] - case 3: - extendedCharset = extendedCharsetFiles + extendedCharsetMostPassword + extendedCharsetSpecialPassword - } - charset += extendedCharset - } - - // loop until a string of the desired complexity is generated - for { - // generate a random string - result := make([]byte, length) - for i := range result { - val, _ := rand.Int(rand.Reader, big.NewInt(int64(len(charset)))) - result[i] = charset[val.Int64()] - } - - // return early if the string is not complex - if complexity <= 0 { - return string(result) - } - - // count the number of special characters in the generated string - for _, char := range string(result) { - if strings.ContainsRune(extendedCharset, char) { - actualSpecialChars++ - } - } - - // return the generated string if it contains enough special characters - if actualSpecialChars >= minSpecialChars { - return string(result) - } - - // reset special character counter - fmt.Println("Regenerating string until desired complexity is achieved...") - actualSpecialChars = 0 - } -}