diff --git a/cmd/host/main.go b/cmd/host/main.go new file mode 100644 index 00000000..f4168721 --- /dev/null +++ b/cmd/host/main.go @@ -0,0 +1,119 @@ +package main + +import ( + "encoding/binary" + "encoding/json" + "errors" + "fmt" + "io" + "os" + "strings" + + "github.com/pkg/browser" + "github.com/shirou/gopsutil/v4/process" +) + +const identifier = "gopeed" + +type Message struct { + Method string `json:"method"` + Params json.RawMessage `json:"params"` +} + +type Response struct { + Code int `json:"code"` + Data any `json:"data,omitempty"` + Message string `json:"message,omitempty"` +} + +var apiMap = map[string]func(params json.RawMessage) (data any, err error){ + "ping": func(params json.RawMessage) (data any, err error) { + processes, err := process.Processes() + if err != nil { + return false, err + } + + for _, p := range processes { + name, err := p.Name() + if err != nil { + continue + } + + if strings.Contains(strings.ToLower(name), strings.ToLower(identifier)) { + return true, nil + } + } + return false, nil + }, + "create": func(params json.RawMessage) (data any, err error) { + err = browser.OpenURL(fmt.Sprintf("%s:///create?params=%s", identifier, string(params))) + return + }, +} + +func main() { + for { + // Read message length (first 4 bytes) + var length uint32 + if err := binary.Read(os.Stdin, binary.NativeEndian, &length); err != nil { + if err == io.EOF { + // Connection closed by Chrome + return + } + sendError("Failed to read message length: " + err.Error()) + return + } + + // Read the message + input := make([]byte, length) + if _, err := io.ReadFull(os.Stdin, input); err != nil { + sendError("Failed to read message: " + err.Error()) + return + } + + // Parse message + var message Message + if err := json.Unmarshal(input, &message); err != nil { + sendError("Failed to parse message: " + err.Error()) + return + } + + // Handle request + var data any + var err error + if handler, ok := apiMap[message.Method]; ok { + data, err = handler(message.Params) + } else { + err = errors.New("Unknown method: " + message.Method) + } + if err != nil { + sendError(err.Error()) + continue + } + sendResponse(0, data, "") + } +} + +func sendResponse(code int, data interface{}, message string) { + response := Response{ + Code: code, + Data: data, + Message: message, + } + + // Encode response + responseBytes, err := json.Marshal(response) + if err != nil { + sendError("Failed to encode response: " + err.Error()) + return + } + + // Write message length + binary.Write(os.Stdout, binary.NativeEndian, uint32(len(responseBytes))) + // Write message + os.Stdout.Write(responseBytes) +} + +func sendError(msg string) { + sendResponse(1, nil, msg) +} diff --git a/go.mod b/go.mod index 09b8ea68..cd8ce9fd 100644 --- a/go.mod +++ b/go.mod @@ -24,6 +24,16 @@ require ( golang.org/x/sync v0.9.0 ) +require ( + github.com/ebitengine/purego v0.8.1 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect + github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect + github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect +) + require ( dario.cat/mergo v1.0.0 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect @@ -99,9 +109,11 @@ require ( github.com/pion/turn/v4 v4.0.0 // indirect github.com/pion/webrtc/v4 v4.0.2 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect + github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c github.com/protolambda/ctxlock v0.1.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/sergi/go-diff v1.1.0 // indirect + github.com/shirou/gopsutil/v4 v4.24.12 github.com/skeema/knownhosts v1.2.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/tidwall/btree v1.7.0 // indirect @@ -113,7 +125,7 @@ require ( golang.org/x/crypto v0.29.0 // indirect golang.org/x/mod v0.22.0 // indirect golang.org/x/net v0.31.0 // indirect - golang.org/x/sys v0.27.0 // indirect + golang.org/x/sys v0.28.0 // indirect golang.org/x/text v0.20.0 // indirect golang.org/x/time v0.8.0 // indirect golang.org/x/tools v0.27.0 // indirect diff --git a/go.sum b/go.sum index 7de3d593..3f49480d 100644 --- a/go.sum +++ b/go.sum @@ -143,6 +143,8 @@ github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+m github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= +github.com/ebitengine/purego v0.8.1 h1:sdRKd6plj7KYW33EH5As6YKfe8m9zbN9JMrOjNVF/BE= +github.com/ebitengine/purego v0.8.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/edsrzf/mmap-go v1.2.0 h1:hXLYlkbaPzt1SaQk+anYwKSRNhufIDCchSPkUD6dD84= github.com/edsrzf/mmap-go v1.2.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q= github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819 h1:RIB4cRk+lBqKK3Oy0r2gRX4ui7tuhiZq2SuTtTCi0/0= @@ -187,6 +189,8 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= github.com/go-sourcemap/sourcemap v2.1.4+incompatible h1:a+iTbH5auLKxaNwQFg0B+TCYl6lbukKPc7b5x0n1s6Q= @@ -288,6 +292,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c h1:VtwQ41oftZwlMnOEbMWQtSEUgU64U4s+GHk7hZK+jtY= +github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c/go.mod h1:JKx41uQRwqlTZabZc+kILPrO/3jlKnQ2Z8b7YiVw5cE= github.com/matoous/go-nanoid v1.5.0/go.mod h1:zyD2a71IubI24efhpvkJz+ZwfwagzgSO6UNiFsZKN7U= github.com/matoous/go-nanoid/v2 v2.0.0 h1:d19kur2QuLeHmJBkvYkFdhFBzLoo1XVm2GgTpL+9Tj0= github.com/matoous/go-nanoid/v2 v2.0.0/go.mod h1:FtS4aGPVfEkxKxhdWPAspZpZSh1cOjtM7Ej/So3hR0g= @@ -363,6 +369,8 @@ github.com/pion/webrtc/v4 v4.0.2 h1:fBwm5/hqSUybrCWl0DDBSTDrpbkcgkqpeLmXw9CsBQA= github.com/pion/webrtc/v4 v4.0.2/go.mod h1:moylBT2A4dNoEaYBCdV1nThM3TLwRHzWszIG+eSPaqQ= github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4= github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -370,6 +378,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c h1:NRoLoZvkBTKvR5gQLgA3e0hqjkY9u1wm+iOL45VN/qI= +github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= @@ -412,6 +422,8 @@ github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 h1:GHRpF1pTW19a github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= +github.com/shirou/gopsutil/v4 v4.24.12 h1:qvePBOk20e0IKA1QXrIIU+jmk+zEiYVVx06WjBRlZo4= +github.com/shirou/gopsutil/v4 v4.24.12/go.mod h1:DCtMPAad2XceTeIAbGyVfycbYQNBGk2P8cvDi7/VN9o= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= @@ -447,6 +459,10 @@ github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EU github.com/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU= @@ -458,6 +474,8 @@ github.com/xiaoqidun/setft v0.0.0-20220310121541-be86327699ad/go.mod h1:Jj8p9bgK github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0= go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= @@ -543,6 +561,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -552,6 +571,7 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -562,13 +582,18 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= diff --git a/ui/flutter/lib/app/modules/extension/views/extension_view.dart b/ui/flutter/lib/app/modules/extension/views/extension_view.dart index 0a9dbe4a..1c92607a 100644 --- a/ui/flutter/lib/app/modules/extension/views/extension_view.dart +++ b/ui/flutter/lib/app/modules/extension/views/extension_view.dart @@ -16,7 +16,6 @@ import '../../../../api/model/extension.dart'; import '../../../../api/model/install_extension.dart'; import '../../../../api/model/switch_extension.dart'; import '../../../../api/model/update_extension_settings.dart'; -import '../../../../util/mac_secure_util.dart'; import '../../../../util/message.dart'; import '../../../../util/util.dart'; import '../../../views/icon_button_loading.dart'; @@ -96,7 +95,6 @@ class ExtensionView extends GetView { var dir = await FilePicker.platform.getDirectoryPath(); if (dir != null) { - MacSecureUtil.saveBookmark(dir); try { await installExtension( InstallExtension(devMode: true, url: dir)); diff --git a/ui/flutter/lib/app/views/directory_selector.dart b/ui/flutter/lib/app/views/directory_selector.dart index 27904bc8..d10f68c5 100644 --- a/ui/flutter/lib/app/views/directory_selector.dart +++ b/ui/flutter/lib/app/views/directory_selector.dart @@ -9,7 +9,6 @@ import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:toggle_switch/toggle_switch.dart'; -import '../../util/mac_secure_util.dart'; import '../../util/message.dart'; import '../../util/util.dart'; @@ -42,7 +41,6 @@ class _DirectorySelectorState extends State { var dir = await FilePicker.platform.getDirectoryPath(); if (dir != null) { widget.controller.text = dir; - MacSecureUtil.saveBookmark(dir); } }); } diff --git a/ui/flutter/lib/main.dart b/ui/flutter/lib/main.dart index 245eb63b..2d678f73 100644 --- a/ui/flutter/lib/main.dart +++ b/ui/flutter/lib/main.dart @@ -10,9 +10,9 @@ import 'app/modules/app/views/app_view.dart'; import 'core/libgopeed_boot.dart'; import 'database/database.dart'; import 'i18n/message.dart'; +import 'util/browser_extension_host/browser_extension_host.dart'; import 'util/locale_manager.dart'; import 'util/log_util.dart'; -import 'util/mac_secure_util.dart'; import 'util/package_info.dart'; import 'util/scheme_register/scheme_register.dart'; import 'util/util.dart'; @@ -91,9 +91,16 @@ Future init(Args args) async { logger.e("register scheme fail", e); } + for (final browser in Browser.values) { + try { + await installManifest(browser); + } catch (e) { + logger.e("browser [${browser.name}] extension host integration fail", e); + } + } + try { await controller.loadDownloaderConfig(); - MacSecureUtil.loadBookmark(); } catch (e) { logger.e("load config fail", e); } diff --git a/ui/flutter/lib/util/browser_extension_host/browser_extension_host.dart b/ui/flutter/lib/util/browser_extension_host/browser_extension_host.dart new file mode 100644 index 00000000..4b00f97f --- /dev/null +++ b/ui/flutter/lib/util/browser_extension_host/browser_extension_host.dart @@ -0,0 +1,15 @@ +import 'browser_extension_host_stub.dart' + if (dart.library.io) 'entry/browser_extension_host_native.dart'; + +enum Browser { chrome, edge, firefox } + +/// Check if specified browser is installed +Future checkBrowserInstalled(Browser browser) => + doCheckBrowserInstalled(browser); + +/// Check if browser extension manifest is properly installed +Future checkManifestInstalled(Browser browser) => + doCheckManifestInstalled(browser); + +/// Install browser extension manifest +Future installManifest(Browser browser) => doInstallManifest(browser); diff --git a/ui/flutter/lib/util/browser_extension_host/browser_extension_host_stub.dart b/ui/flutter/lib/util/browser_extension_host/browser_extension_host_stub.dart new file mode 100644 index 00000000..108b36c0 --- /dev/null +++ b/ui/flutter/lib/util/browser_extension_host/browser_extension_host_stub.dart @@ -0,0 +1,7 @@ +import 'browser_extension_host.dart'; + +Future doCheckBrowserInstalled(Browser browser) => + throw UnimplementedError(); +Future doCheckManifestInstalled(Browser browser) => + throw UnimplementedError(); +Future doInstallManifest(Browser browser) => throw UnimplementedError(); diff --git a/ui/flutter/lib/util/browser_extension_host/entry/browser_extension_host_native.dart b/ui/flutter/lib/util/browser_extension_host/entry/browser_extension_host_native.dart new file mode 100644 index 00000000..955d928f --- /dev/null +++ b/ui/flutter/lib/util/browser_extension_host/entry/browser_extension_host_native.dart @@ -0,0 +1,275 @@ +import 'dart:io'; +import 'dart:convert'; +import 'package:path/path.dart' as path; +import 'package:win32_registry/win32_registry.dart'; + +import '../../win32.dart'; +import '../browser_extension_host.dart'; + +const _hostName = 'com.gopeed.gopeed'; +const _chromeExtensionId = 'mijpgljlfcapndmchhjffkpckknofcnd'; +const _edgeExtensionId = 'dkajnckekendchdleoaenoophcobooce'; +const _firefoxExtensionId = '{c5d69a8f-2ed0-46a7-afa4-b3a00dc58088}'; + +// Windows NativeMessagingHosts registry constants +const _chromeNativeHostsKey = r'Software\Google\Chrome\NativeMessagingHosts'; +const _edgeNativeHostsKey = r'Software\Microsoft\Edge\NativeMessagingHosts'; +const _firefoxNativeHostsKey = r'Software\Mozilla\NativeMessagingHosts'; + +/// Check if specified browser is installed +Future doCheckBrowserInstalled(Browser browser) async { + if (Platform.isWindows) { + switch (browser) { + case Browser.chrome: + return await _checkWindowsRegistry( + r'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\chrome.exe') || + await _checkWindowsRegistry( + r'SOFTWARE\WOW6432Node\Google\Chrome') || + await _checkWindowsExecutable(browser); + case Browser.edge: + return await _checkWindowsRegistry( + r'SOFTWARE\Microsoft\Edge\BLBeacon') || + await _checkWindowsRegistry( + r'SOFTWARE\WOW6432Node\Microsoft\Edge\BLBeacon') || + await _checkWindowsExecutable(browser); + case Browser.firefox: + return await _checkWindowsRegistry(r'SOFTWARE\Mozilla\Firefox') || + await _checkWindowsRegistry( + r'SOFTWARE\WOW6432Node\Mozilla\Firefox') || + await _checkWindowsExecutable(browser); + } + } else { + return await _checkUnixExecutable(browser); + } +} + +/// Check if browser extension manifest is properly installed +Future doCheckManifestInstalled(Browser browser) async { + if (await checkBrowserInstalled(browser) == false) return false; + + final manifestPath = _getManifestPath(browser); + if (manifestPath == null) return false; + + if (Platform.isWindows) { + final regKey = _getWindowsRegistryKey(browser); + if (!checkRegistry('$regKey\\$_hostName', '', manifestPath)) { + return false; + } + } + + if (!await File(manifestPath).exists()) { + return false; + } + + final existingContent = await File(manifestPath).readAsString(); + final expectedContent = _getManifestContent(); + return existingContent == expectedContent; +} + +/// Install browser extension manifest +Future doInstallManifest(Browser browser) async { + if (await checkBrowserInstalled(browser) == false) return; + if (await checkManifestInstalled(browser)) return; + + final manifestPath = _getManifestPath(browser)!; + final manifestContent = _getManifestContent(); + final manifestDir = path.dirname(manifestPath); + await Directory(manifestDir).create(recursive: true); + await File(manifestPath).writeAsString(manifestContent); + + if (Platform.isWindows) { + final regKey = _getWindowsRegistryKey(browser); + upsertRegistry('$regKey\\$_hostName', '', manifestPath); + } +} + +Future _checkWindowsExecutable(Browser browser) async { + final paths = _getWindowsExecutablePaths(browser); + for (var execPath in paths) { + if (await File(execPath).exists()) { + return true; + } + } + return false; +} + +List _getWindowsExecutablePaths(Browser browser) { + final programFiles = Platform.environment['PROGRAMFILES']; + final programFilesX86 = Platform.environment['PROGRAMFILES(X86)']; + final localAppData = Platform.environment['LOCALAPPDATA']; + + switch (browser) { + case Browser.chrome: + return [ + if (programFiles != null) + path.join( + programFiles, 'Google', 'Chrome', 'Application', 'chrome.exe'), + if (programFilesX86 != null) + path.join( + programFilesX86, 'Google', 'Chrome', 'Application', 'chrome.exe'), + if (localAppData != null) + path.join( + localAppData, 'Google', 'Chrome', 'Application', 'chrome.exe'), + ]; + case Browser.edge: + return [ + if (programFiles != null) + path.join( + programFiles, 'Microsoft', 'Edge', 'Application', 'msedge.exe'), + if (programFilesX86 != null) + path.join(programFilesX86, 'Microsoft', 'Edge', 'Application', + 'msedge.exe'), + if (localAppData != null) + path.join( + localAppData, 'Microsoft', 'Edge', 'Application', 'msedge.exe'), + ]; + case Browser.firefox: + return [ + if (programFiles != null) + path.join(programFiles, 'Mozilla Firefox', 'firefox.exe'), + if (programFilesX86 != null) + path.join(programFilesX86, 'Mozilla Firefox', 'firefox.exe'), + ]; + } +} + +Future _checkUnixExecutable(Browser browser) async { + final paths = _getUnixExecutablePaths(browser); + for (var execPath in paths) { + execPath = execPath.replaceAll('~', Platform.environment['HOME'] ?? ''); + if (await Directory(execPath).exists() || await File(execPath).exists()) { + return true; + } + } + return false; +} + +List _getUnixExecutablePaths(Browser browser) { + if (Platform.isMacOS) { + switch (browser) { + case Browser.chrome: + return [ + '/Applications/Google Chrome.app', + '~/Applications/Google Chrome.app', + '/Users/${Platform.environment['USER']}/Applications/Google Chrome.app' + ]; + case Browser.edge: + return [ + '/Applications/Microsoft Edge.app', + '~/Applications/Microsoft Edge.app', + '/Users/${Platform.environment['USER']}/Applications/Microsoft Edge.app' + ]; + case Browser.firefox: + return [ + '/Applications/Firefox.app', + '~/Applications/Firefox.app', + '/Users/${Platform.environment['USER']}/Applications/Firefox.app' + ]; + } + } else { + switch (browser) { + case Browser.chrome: + return [ + '/usr/bin/google-chrome', + '/usr/bin/google-chrome-stable', + '/usr/bin/chrome', + '/snap/bin/google-chrome', + '/opt/google/chrome/google-chrome' + ]; + case Browser.edge: + return [ + '/usr/bin/microsoft-edge', + '/usr/bin/microsoft-edge-stable', + '/snap/bin/microsoft-edge', + '/opt/microsoft/msedge/msedge' + ]; + case Browser.firefox: + return [ + '/usr/bin/firefox', + '/snap/bin/firefox', + '/usr/lib/firefox/firefox', + '/opt/firefox/firefox' + ]; + } + } +} + +String? _getManifestPath(Browser browser) { + if (Platform.isWindows) { + final execPath = Platform.resolvedExecutable; + final execDir = path.dirname(execPath); + return path.join(execDir, '$_hostName.json'); + } + + final home = + Platform.environment['HOME'] ?? Platform.environment['USERPROFILE']; + if (home == null) return null; + + if (Platform.isMacOS) { + switch (browser) { + case Browser.chrome: + return path.join(home, 'Library', 'Application Support', 'Google', + 'Chrome', 'NativeMessagingHosts', '$_hostName.json'); + case Browser.edge: + return path.join(home, 'Library', 'Application Support', + 'Microsoft Edge', 'NativeMessagingHosts', '$_hostName.json'); + case Browser.firefox: + return path.join(home, 'Library', 'Application Support', 'Mozilla', + 'NativeMessagingHosts', '$_hostName.json'); + } + } else if (Platform.isLinux) { + switch (browser) { + case Browser.chrome: + return path.join(home, '.config', 'google-chrome', + 'NativeMessagingHosts', '$_hostName.json'); + case Browser.edge: + return path.join(home, '.config', 'microsoft-edge', + 'NativeMessagingHosts', '$_hostName.json'); + case Browser.firefox: + return path.join( + home, '.mozilla', 'native-messaging-hosts', '$_hostName.json'); + } + } + return null; +} + +Future _checkWindowsRegistry(String keyPath) async { + try { + final key = Registry.openPath(RegistryHive.localMachine, path: keyPath); + key.close(); + return true; + } catch (e) { + return false; + } +} + +String _getManifestContent() { + final execPath = Platform.resolvedExecutable; + final execDir = path.dirname(execPath); + final hostPath = + path.join(execDir, 'host') + (Platform.isWindows ? '.exe' : ''); + final manifest = { + 'name': _hostName, + 'description': 'Gopeed browser extension host', + 'path': hostPath, + 'type': 'stdio', + 'allowed_origins': [ + 'chrome-extension://gjddllnejledbfaeondocjpejpamclkk/', + 'chrome-extension://$_chromeExtensionId/', + 'chrome-extension://$_edgeExtensionId/', + ], + 'allowed_extensions': [_firefoxExtensionId], + }; + return const JsonEncoder.withIndent(' ').convert(manifest); +} + +String _getWindowsRegistryKey(Browser browser) { + switch (browser) { + case Browser.chrome: + return _chromeNativeHostsKey; + case Browser.edge: + return _edgeNativeHostsKey; + case Browser.firefox: + return _firefoxNativeHostsKey; + } +} diff --git a/ui/flutter/lib/util/mac_secure_util.dart b/ui/flutter/lib/util/mac_secure_util.dart deleted file mode 100644 index 19905833..00000000 --- a/ui/flutter/lib/util/mac_secure_util.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'dart:io'; - -import 'package:macos_secure_bookmarks/macos_secure_bookmarks.dart'; - -import '../database/database.dart'; -import 'util.dart'; - -class MacSecureUtil { - static Future saveBookmark(String dir) async { - if (!Util.isMacos()) { - return; - } - final secureBookmarks = SecureBookmarks(); - final bookmark = await secureBookmarks.bookmark(Directory(dir)); - Database.instance.saveBookmark(MapEntry(dir, bookmark)); - } - - static Future loadBookmark() async { - if (!Util.isMacos()) { - return; - } - final secureBookmarks = SecureBookmarks(); - final bookmark = Database.instance.getBookmark() ?? {}; - bookmark.forEach((_, v) async { - final resolvedFile = - await secureBookmarks.resolveBookmark(v, isDirectory: true); - await secureBookmarks.startAccessingSecurityScopedResource(resolvedFile); - }); - } -} diff --git a/ui/flutter/lib/util/scheme_register/entry/scheme_register_native.dart b/ui/flutter/lib/util/scheme_register/entry/scheme_register_native.dart index 0ccf2e74..426f85db 100644 --- a/ui/flutter/lib/util/scheme_register/entry/scheme_register_native.dart +++ b/ui/flutter/lib/util/scheme_register/entry/scheme_register_native.dart @@ -3,18 +3,19 @@ import 'dart:io'; import 'package:win32_registry/win32_registry.dart'; import '../../util.dart'; +import '../../win32.dart'; doRegisterUrlScheme(String scheme) { if (Util.isWindows()) { final schemeKey = 'Software\\Classes\\$scheme'; final appPath = Platform.resolvedExecutable; - _upsertRegistry( + upsertRegistry( schemeKey, 'URL Protocol', '', ); - _upsertRegistry( + upsertRegistry( '$schemeKey\\shell\\open\\command', '', '"$appPath" "%1"', @@ -43,22 +44,22 @@ doRegisterDefaultTorrentClient() { final appPath = Platform.resolvedExecutable; final iconPath = '${File(appPath).parent.path}\\data\\flutter_assets\\assets\\tray_icon\\icon.ico'; - _upsertRegistry( + upsertRegistry( _torrentRegKey, '', _torrentRegValue, ); - _upsertRegistry( + upsertRegistry( _torrentAppRegKey, '', 'Torrent file', ); - _upsertRegistry( + upsertRegistry( '$_torrentAppRegKey\\DefaultIcon', '', iconPath, ); - _upsertRegistry( + upsertRegistry( '$_torrentAppRegKey\\shell\\open\\command', '', '"$appPath" "file:///%1"', @@ -74,20 +75,3 @@ doUnregisterDefaultTorrentClient() { Registry.currentUser.deleteKey(_torrentAppRegKey, recursive: true); } } - -// Add Windows registry key and value if not exists -_upsertRegistry(String keyPath, String valueName, String value) { - RegistryKey regKey; - try { - regKey = Registry.openPath(RegistryHive.currentUser, - path: keyPath, desiredAccessRights: AccessRights.allAccess); - } catch (e) { - regKey = Registry.currentUser.createKey(keyPath); - } - - if (regKey.getValueAsString(valueName) != value) { - regKey - .createValue(RegistryValue(valueName, RegistryValueType.string, value)); - } - regKey.close(); -} diff --git a/ui/flutter/lib/util/win32.dart b/ui/flutter/lib/util/win32.dart new file mode 100644 index 00000000..c02a9a6f --- /dev/null +++ b/ui/flutter/lib/util/win32.dart @@ -0,0 +1,32 @@ +import 'package:win32_registry/win32_registry.dart'; + +/// Check registry key +/// If the key does not exist or the value is different, return false +checkRegistry(String keyPath, String valueName, String value) { + RegistryKey regKey; + try { + regKey = Registry.openPath(RegistryHive.currentUser, path: keyPath); + return regKey.getValueAsString(valueName) == value; + } catch (e) { + return false; + } +} + +/// Upsert registry key +/// If the key does not exist, create it +/// If the value does not exist or is different, update it +upsertRegistry(String keyPath, String valueName, String value) { + RegistryKey regKey; + try { + regKey = Registry.openPath(RegistryHive.currentUser, + path: keyPath, desiredAccessRights: AccessRights.allAccess); + } catch (e) { + regKey = Registry.currentUser.createKey(keyPath); + } + + if (regKey.getValueAsString(valueName) != value) { + regKey + .createValue(RegistryValue(valueName, RegistryValueType.string, value)); + } + regKey.close(); +} diff --git a/ui/flutter/macos/Podfile.lock b/ui/flutter/macos/Podfile.lock index 839cca50..84da3e2e 100644 --- a/ui/flutter/macos/Podfile.lock +++ b/ui/flutter/macos/Podfile.lock @@ -6,8 +6,6 @@ PODS: - device_info_plus (0.0.1): - FlutterMacOS - FlutterMacOS (1.0.0) - - macos_secure_bookmarks (0.0.3): - - FlutterMacOS - package_info_plus (0.0.1): - FlutterMacOS - path_provider_foundation (0.0.1): @@ -32,7 +30,6 @@ DEPENDENCIES: - desktop_drop (from `Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos`) - device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`) - FlutterMacOS (from `Flutter/ephemeral`) - - macos_secure_bookmarks (from `Flutter/ephemeral/.symlinks/plugins/macos_secure_bookmarks/macos`) - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) - screen_retriever (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos`) @@ -51,8 +48,6 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos FlutterMacOS: :path: Flutter/ephemeral - macos_secure_bookmarks: - :path: Flutter/ephemeral/.symlinks/plugins/macos_secure_bookmarks/macos package_info_plus: :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos path_provider_foundation: @@ -75,7 +70,6 @@ SPEC CHECKSUMS: desktop_drop: 69eeff437544aa619c8db7f4481b3a65f7696898 device_info_plus: 74e614483d05c89290d30a4c8feae15d555f7427 FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 - macos_secure_bookmarks: cff041c4b377fa00941bb901aaec4d27fac5edf2 package_info_plus: f5790acc797bf17c3e959e9d6cf162cc68ff7523 path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38 diff --git a/ui/flutter/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ui/flutter/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 07ef78d0..6e74e48d 100644 --- a/ui/flutter/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/ui/flutter/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -15,7 +15,7 @@ @@ -31,7 +31,7 @@ @@ -54,7 +54,7 @@ @@ -71,7 +71,7 @@ diff --git a/ui/flutter/macos/Runner/DebugProfile.entitlements b/ui/flutter/macos/Runner/DebugProfile.entitlements index 63f40e06..974d290f 100644 --- a/ui/flutter/macos/Runner/DebugProfile.entitlements +++ b/ui/flutter/macos/Runner/DebugProfile.entitlements @@ -2,19 +2,9 @@ - com.apple.security.app-sandbox - com.apple.security.cs.allow-jit com.apple.security.files.bookmarks.app-scope - com.apple.security.files.downloads.read-write - - com.apple.security.files.user-selected.read-write - - com.apple.security.network.client - - com.apple.security.network.server - diff --git a/ui/flutter/macos/Runner/Release.entitlements b/ui/flutter/macos/Runner/Release.entitlements index ee03f688..0791a622 100644 --- a/ui/flutter/macos/Runner/Release.entitlements +++ b/ui/flutter/macos/Runner/Release.entitlements @@ -2,17 +2,7 @@ - com.apple.security.app-sandbox - com.apple.security.files.bookmarks.app-scope - com.apple.security.files.downloads.read-write - - com.apple.security.files.user-selected.read-write - - com.apple.security.network.client - - com.apple.security.network.server - diff --git a/ui/flutter/pubspec.lock b/ui/flutter/pubspec.lock index d4c99b74..2a45fb62 100644 --- a/ui/flutter/pubspec.lock +++ b/ui/flutter/pubspec.lock @@ -658,22 +658,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" - logging_appenders: - dependency: transitive - description: - name: logging_appenders - sha256: e329e7472f99416d0edaaf6451fe6c02dec91d34535bd252e284a0b94ab23d79 - url: "https://pub.dev" - source: hosted - version: "1.3.1" - macos_secure_bookmarks: - dependency: "direct main" - description: - name: macos_secure_bookmarks - sha256: c3163875f8fd530a1654a7cf8aa426e6e208d28bf05724a1d4960e4b7d2ca1c6 - url: "https://pub.dev" - source: hosted - version: "0.2.1" macros: dependency: transitive description: diff --git a/ui/flutter/pubspec.yaml b/ui/flutter/pubspec.yaml index 6c40985d..167e2b91 100644 --- a/ui/flutter/pubspec.yaml +++ b/ui/flutter/pubspec.yaml @@ -42,7 +42,6 @@ dependencies: dio: ^5.2.1 path_provider: ^2.1.4 file_picker: ^8.1.2 - macos_secure_bookmarks: ^0.2.1 rounded_loading_button_plus: ^3.0.1 url_launcher: ^6.3.1 logger: ^1.4.0