forked from all_user/stat_center
代码提交
This commit is contained in:
@@ -17,6 +17,7 @@ import (
|
||||
"stat/statg"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/xxjwxc/public/myglobal/snowflake"
|
||||
)
|
||||
|
||||
@@ -51,7 +52,54 @@ func GenerateConfigFile(origin, target, configFile, DC, PC string) ([]byte, stri
|
||||
if err != nil {
|
||||
output, _ = os.ReadFile("conf/" + configFile)
|
||||
}
|
||||
return output, configFile
|
||||
return uniqueProfileIdentity(output, configFile), configFile
|
||||
}
|
||||
|
||||
// profileIDNamespace 标识符派生的固定命名空间,保证同一模板每次生成的值一致
|
||||
var profileIDNamespace = uuid.NewSHA1(uuid.NameSpaceURL, []byte("stat_center/mobileconfig"))
|
||||
|
||||
var (
|
||||
rePayloadIdent = regexp.MustCompile(`(<key>PayloadIdentifier</key>[ \t]*\r?\n[ \t]*<string>)([^<]*)(</string>)`)
|
||||
rePayloadUUID = regexp.MustCompile(`(<key>PayloadUUID</key>[ \t]*\r?\n[ \t]*<string>)([^<]*)(</string>)`)
|
||||
reUUIDValue = regexp.MustCompile(`[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}`)
|
||||
)
|
||||
|
||||
// uniqueProfileIdentity 把 mobileconfig 里的 PayloadIdentifier/PayloadUUID 改写成按 configFile 派生的值。
|
||||
// conf 下各 app 的模板是互相复制出来的,标识符完全相同,而 iOS 按顶层 PayloadIdentifier 判重:
|
||||
// 相同即替换已安装的描述文件,用户装了第二个 app 第一个就没了。
|
||||
// 按 configFile 派生可保证各 app 互不覆盖;同一 app 多次下载得到相同标识符,
|
||||
// 重复安装只覆盖自己,不会在桌面堆出一排图标。
|
||||
func uniqueProfileIdentity(data []byte, configFile string) []byte {
|
||||
if len(data) == 0 {
|
||||
return data
|
||||
}
|
||||
derive := func(i int) string {
|
||||
return uuid.NewSHA1(profileIDNamespace, []byte(fmt.Sprintf("%s#%d", configFile, i))).String()
|
||||
}
|
||||
|
||||
n := 0
|
||||
data = rePayloadUUID.ReplaceAllFunc(data, func(m []byte) []byte {
|
||||
g := rePayloadUUID.FindSubmatch(m)
|
||||
v := derive(n)
|
||||
n++
|
||||
return []byte(string(g[1]) + v + string(g[3]))
|
||||
})
|
||||
|
||||
// 第 i 个 PayloadIdentifier 与第 i 个 PayloadUUID 共用派生值,保留原前缀(luli. / com.apple.webClip.managed.)
|
||||
i := 0
|
||||
data = rePayloadIdent.ReplaceAllFunc(data, func(m []byte) []byte {
|
||||
g := rePayloadIdent.FindSubmatch(m)
|
||||
v := derive(i)
|
||||
i++
|
||||
val := g[2]
|
||||
if reUUIDValue.Match(val) {
|
||||
val = reUUIDValue.ReplaceAll(val, []byte(v))
|
||||
} else {
|
||||
val = []byte(string(val) + "." + v)
|
||||
}
|
||||
return []byte(string(g[1]) + string(val) + string(g[3]))
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
func GetAppConfig(appId int32) (origin, target, config string) {
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
package versionSer
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var reAllIdent = regexp.MustCompile(`<key>Payload(?:Identifier|UUID)</key>[ \t]*\r?\n[ \t]*<string>([^<]*)</string>`)
|
||||
|
||||
func collectIdentity(t *testing.T, data []byte) []string {
|
||||
t.Helper()
|
||||
var out []string
|
||||
for _, m := range reAllIdent.FindAllSubmatch(data, -1) {
|
||||
out = append(out, string(m[1]))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// TestUniqueProfileIdentityMatchesTemplates 确认 uniqueProfileIdentity 的派生结果与 conf 下
|
||||
// 已改好的模板完全一致:两边用同一套规则,部署后不会再变一次标识符。
|
||||
func TestUniqueProfileIdentityMatchesTemplates(t *testing.T) {
|
||||
files, err := filepath.Glob("../../conf/*.mobileconfig")
|
||||
if err != nil || len(files) == 0 {
|
||||
t.Fatalf("找不到 mobileconfig 模板: %v", err)
|
||||
}
|
||||
|
||||
seen := make(map[string]string)
|
||||
for _, path := range files {
|
||||
name := filepath.Base(path)
|
||||
raw, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatalf("%s 读取失败: %v", name, err)
|
||||
}
|
||||
|
||||
want := collectIdentity(t, raw)
|
||||
if len(want) == 0 {
|
||||
t.Fatalf("%s 没有解析到任何标识符", name)
|
||||
}
|
||||
got := collectIdentity(t, uniqueProfileIdentity(raw, name))
|
||||
if len(got) != len(want) {
|
||||
t.Fatalf("%s 标识符数量不一致: got %d want %d", name, len(got), len(want))
|
||||
}
|
||||
for i := range want {
|
||||
if got[i] != want[i] {
|
||||
t.Errorf("%s 第 %d 个标识符不一致:\n got %s\n want %s", name, i, got[i], want[i])
|
||||
}
|
||||
}
|
||||
|
||||
// 跨文件判重:顶层 PayloadIdentifier 相同会让 iOS 覆盖安装
|
||||
for _, v := range got {
|
||||
if prev, ok := seen[v]; ok {
|
||||
t.Errorf("标识符 %s 同时出现在 %s 和 %s,iOS 上会互相覆盖", v, prev, name)
|
||||
}
|
||||
seen[v] = name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestUniqueProfileIdentityIsStable 同一模板多次生成必须得到相同标识符,
|
||||
// 否则用户每次下载都会在桌面多出一个图标。
|
||||
func TestUniqueProfileIdentityIsStable(t *testing.T) {
|
||||
raw, err := os.ReadFile("../../conf/hgmj.mobileconfig")
|
||||
if err != nil {
|
||||
t.Skipf("模板不存在: %v", err)
|
||||
}
|
||||
a := collectIdentity(t, uniqueProfileIdentity(raw, "hgmj.mobileconfig"))
|
||||
b := collectIdentity(t, uniqueProfileIdentity(raw, "hgmj.mobileconfig"))
|
||||
for i := range a {
|
||||
if a[i] != b[i] {
|
||||
t.Fatalf("第 %d 个标识符不稳定: %s != %s", i, a[i], b[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestUniqueProfileIdentityDiffersByConfigFile 不同 configFile 必须派生出不同标识符。
|
||||
func TestUniqueProfileIdentityDiffersByConfigFile(t *testing.T) {
|
||||
raw, err := os.ReadFile("../../conf/hgmj.mobileconfig")
|
||||
if err != nil {
|
||||
t.Skipf("模板不存在: %v", err)
|
||||
}
|
||||
a := collectIdentity(t, uniqueProfileIdentity(raw, "hgmj.mobileconfig"))
|
||||
b := collectIdentity(t, uniqueProfileIdentity(raw, "newapp.mobileconfig"))
|
||||
for i := range a {
|
||||
if a[i] == b[i] {
|
||||
t.Fatalf("不同 configFile 派生出相同标识符: %s", a[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user