chore: package send recording and capability tests
This commit is contained in:
585
cmd/isphere-capability-smoke/main.go
Normal file
585
cmd/isphere-capability-smoke/main.go
Normal file
@@ -0,0 +1,585 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/cipher"
|
||||
"crypto/des"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
|
||||
"isphere-ai-bridge/internal/mcpserver"
|
||||
)
|
||||
|
||||
var expectedTools = []string{
|
||||
"win_helper_version",
|
||||
"win_helper_self_check",
|
||||
"win_helper_scan_windows",
|
||||
"win_helper_dump_uia",
|
||||
"isphere_receive_messages",
|
||||
"isphere_search_contacts",
|
||||
"isphere_search_groups",
|
||||
"isphere_receive_files",
|
||||
"isphere_send_message",
|
||||
}
|
||||
|
||||
type smokeResult struct {
|
||||
OK bool `json:"ok"`
|
||||
Package string `json:"package"`
|
||||
Transport string `json:"transport"`
|
||||
Initialized bool `json:"initialized"`
|
||||
ToolCount int `json:"tool_count"`
|
||||
Tools []string `json:"tools"`
|
||||
HelperName string `json:"helper_name"`
|
||||
ReceiveMessageCount int `json:"receive_message_count"`
|
||||
ContactCount int `json:"contact_count"`
|
||||
GroupCount int `json:"group_count"`
|
||||
FileCount int `json:"file_count"`
|
||||
FixtureReceiveMessageCount int `json:"fixture_receive_message_count"`
|
||||
FixtureContactCount int `json:"fixture_contact_count"`
|
||||
FixtureGroupCount int `json:"fixture_group_count"`
|
||||
FixtureFileCount int `json:"fixture_file_count"`
|
||||
FixtureDirReceiveMessageCount int `json:"fixture_dir_receive_message_count"`
|
||||
FixtureDirContactCount int `json:"fixture_dir_contact_count"`
|
||||
FixtureDirGroupCount int `json:"fixture_dir_group_count"`
|
||||
FixtureDirFileCount int `json:"fixture_dir_file_count"`
|
||||
SendPreviewToolPresent bool `json:"send_preview_tool_present"`
|
||||
ProductionSendEnabled bool `json:"production_send_enabled"`
|
||||
LocalLoginRequired bool `json:"local_login_required"`
|
||||
RealISphereLoginRequired bool `json:"real_isphere_login_required"`
|
||||
PythonRuntimeRequired bool `json:"python_runtime_required"`
|
||||
NoRealSend bool `json:"no_real_send"`
|
||||
ActionToolsPresent bool `json:"action_tools_present"`
|
||||
PacketLogFileConfiguredAfterRun bool `json:"packet_log_file_configured_after_run"`
|
||||
PacketLogDirConfiguredAfterRun bool `json:"packet_log_dir_configured_after_run"`
|
||||
MsgLibConfigured bool `json:"msglib_configured"`
|
||||
SendAuditPath string `json:"send_audit_path"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
if err := run(); err != nil {
|
||||
fail("capability_smoke", err)
|
||||
}
|
||||
}
|
||||
|
||||
func run() error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
clearRuntimeEnv()
|
||||
auditPath := filepath.Join("runs", "capability-smoke", "send-preview.jsonl")
|
||||
if err := os.Setenv("ISPHERE_SEND_AUDIT_PATH", auditPath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
session, cleanup, err := newSession(ctx, "isphere-capability-smoke")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
toolNames, err := listToolNames(ctx, session)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := requireExpectedTools(toolNames); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
helperName, err := verifyHelperVersion(ctx, session)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
receiveCount, err := verifyReceiveMessages(ctx, session, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
contactCount, err := verifySearchContacts(ctx, session, "sender", 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
groupCount, err := verifySearchGroups(ctx, session, "project", 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fileCount, err := verifyReceiveFiles(ctx, session, "report", 0, "", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sendPreviewOK, productionSendEnabled, err := verifySendMessage(ctx, session)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fixtureReceiveCount, fixtureContactCount, fixtureGroupCount, fixtureFileCount, err := verifyPacketLogFileFixture(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fixtureDirReceiveCount, fixtureDirContactCount, fixtureDirGroupCount, fixtureDirFileCount, err := verifyPacketLogDirFixture(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
result := smokeResult{
|
||||
OK: true,
|
||||
Package: "send-capability-test",
|
||||
Transport: "sdk_in_memory",
|
||||
Initialized: true,
|
||||
ToolCount: len(toolNames),
|
||||
Tools: toolNames,
|
||||
HelperName: helperName,
|
||||
ReceiveMessageCount: receiveCount,
|
||||
ContactCount: contactCount,
|
||||
GroupCount: groupCount,
|
||||
FileCount: fileCount,
|
||||
FixtureReceiveMessageCount: fixtureReceiveCount,
|
||||
FixtureContactCount: fixtureContactCount,
|
||||
FixtureGroupCount: fixtureGroupCount,
|
||||
FixtureFileCount: fixtureFileCount,
|
||||
FixtureDirReceiveMessageCount: fixtureDirReceiveCount,
|
||||
FixtureDirContactCount: fixtureDirContactCount,
|
||||
FixtureDirGroupCount: fixtureDirGroupCount,
|
||||
FixtureDirFileCount: fixtureDirFileCount,
|
||||
SendPreviewToolPresent: sendPreviewOK,
|
||||
ProductionSendEnabled: productionSendEnabled,
|
||||
LocalLoginRequired: false,
|
||||
RealISphereLoginRequired: false,
|
||||
PythonRuntimeRequired: false,
|
||||
NoRealSend: true,
|
||||
ActionToolsPresent: false,
|
||||
PacketLogFileConfiguredAfterRun: os.Getenv("ISPHERE_PACKET_LOG_FILE") != "",
|
||||
PacketLogDirConfiguredAfterRun: os.Getenv("ISPHERE_PACKET_LOG_DIR") != "",
|
||||
MsgLibConfigured: false,
|
||||
SendAuditPath: auditPath,
|
||||
}
|
||||
encoded, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println(string(encoded))
|
||||
return nil
|
||||
}
|
||||
|
||||
func clearRuntimeEnv() {
|
||||
for _, key := range []string{
|
||||
"ISPHERE_PACKET_LOG_FILE",
|
||||
"ISPHERE_PACKET_LOG_DIR",
|
||||
"ISPHERE_MSGLIB_SIDECAR_EXE",
|
||||
"ISPHERE_MSGLIB_SQLITE_DLL",
|
||||
"ISPHERE_MSGLIB_DB",
|
||||
"ISPHERE_MSGLIB_PASSWORD",
|
||||
} {
|
||||
_ = os.Unsetenv(key)
|
||||
}
|
||||
}
|
||||
|
||||
func newSession(ctx context.Context, name string) (*mcp.ClientSession, func(), error) {
|
||||
serverTransport, clientTransport := mcp.NewInMemoryTransports()
|
||||
server, err := mcpserver.NewServerFromEnv()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("server config: %w", err)
|
||||
}
|
||||
serverCtx, cancel := context.WithCancel(ctx)
|
||||
serverErr := make(chan error, 1)
|
||||
go func() {
|
||||
serverErr <- server.Run(serverCtx, serverTransport)
|
||||
}()
|
||||
|
||||
client := mcp.NewClient(&mcp.Implementation{Name: name, Version: "0.1.0"}, nil)
|
||||
session, err := client.Connect(serverCtx, clientTransport, nil)
|
||||
if err != nil {
|
||||
cancel()
|
||||
return nil, nil, fmt.Errorf("connect: %w", err)
|
||||
}
|
||||
cleanup := func() {
|
||||
_ = session.Close()
|
||||
cancel()
|
||||
select {
|
||||
case <-serverErr:
|
||||
case <-time.After(2 * time.Second):
|
||||
fmt.Fprintln(os.Stderr, `{"ok":false,"step":"server_shutdown","error":"server did not stop before timeout"}`)
|
||||
}
|
||||
}
|
||||
return session, cleanup, nil
|
||||
}
|
||||
|
||||
func listToolNames(ctx context.Context, session *mcp.ClientSession) ([]string, error) {
|
||||
result, err := session.ListTools(ctx, &mcp.ListToolsParams{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list tools: %w", err)
|
||||
}
|
||||
names := make([]string, 0, len(result.Tools))
|
||||
for _, tool := range result.Tools {
|
||||
names = append(names, tool.Name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
return names, nil
|
||||
}
|
||||
|
||||
func requireExpectedTools(got []string) error {
|
||||
want := append([]string(nil), expectedTools...)
|
||||
sort.Strings(want)
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
return fmt.Errorf("tool names = %#v, want %#v", got, want)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func verifyHelperVersion(ctx context.Context, session *mcp.ClientSession) (string, error) {
|
||||
payload, err := callToolObject(ctx, session, "win_helper_version", map[string]any{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if payload["ok"] != true {
|
||||
return "", fmt.Errorf("win_helper_version ok = %#v, want true", payload["ok"])
|
||||
}
|
||||
data, ok := payload["data"].(map[string]any)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("win_helper_version data missing: %#v", payload["data"])
|
||||
}
|
||||
helperName, _ := data["helper_name"].(string)
|
||||
if helperName != "ISphereWinHelper" {
|
||||
return "", fmt.Errorf("helper_name = %q, want ISphereWinHelper", helperName)
|
||||
}
|
||||
return helperName, nil
|
||||
}
|
||||
|
||||
func verifyReceiveMessages(ctx context.Context, session *mcp.ClientSession, wantCount int) (int, error) {
|
||||
payload, err := callToolObject(ctx, session, "isphere_receive_messages", map[string]any{
|
||||
"source_preference": "local_readonly",
|
||||
"preview": true,
|
||||
"cursor": "",
|
||||
"include_attachment_metadata": true,
|
||||
"limit": 5,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if payload["ok"] != true {
|
||||
return 0, fmt.Errorf("isphere_receive_messages ok = %#v, want true", payload["ok"])
|
||||
}
|
||||
messages, ok := payload["messages"].([]any)
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("messages missing or not array: %#v", payload["messages"])
|
||||
}
|
||||
if len(messages) != wantCount {
|
||||
return 0, fmt.Errorf("message count = %d, want %d", len(messages), wantCount)
|
||||
}
|
||||
return len(messages), nil
|
||||
}
|
||||
|
||||
func verifySearchContacts(ctx context.Context, session *mcp.ClientSession, query string, wantCount int) (int, error) {
|
||||
payload, err := callToolObject(ctx, session, "isphere_search_contacts", map[string]any{
|
||||
"query": query,
|
||||
"source_preference": "local_readonly",
|
||||
"include_inactive": true,
|
||||
"cursor": "",
|
||||
"limit": 5,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if payload["ok"] != true {
|
||||
return 0, fmt.Errorf("isphere_search_contacts ok = %#v, want true", payload["ok"])
|
||||
}
|
||||
contacts, ok := payload["contacts"].([]any)
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("contacts missing or not array: %#v", payload["contacts"])
|
||||
}
|
||||
if len(contacts) != wantCount {
|
||||
return 0, fmt.Errorf("contact count = %d, want %d", len(contacts), wantCount)
|
||||
}
|
||||
return len(contacts), nil
|
||||
}
|
||||
|
||||
func verifySearchGroups(ctx context.Context, session *mcp.ClientSession, query string, wantCount int) (int, error) {
|
||||
payload, err := callToolObject(ctx, session, "isphere_search_groups", map[string]any{
|
||||
"query": query,
|
||||
"source_preference": "local_readonly",
|
||||
"include_archived": true,
|
||||
"cursor": "",
|
||||
"limit": 5,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if payload["ok"] != true {
|
||||
return 0, fmt.Errorf("isphere_search_groups ok = %#v, want true", payload["ok"])
|
||||
}
|
||||
groups, ok := payload["groups"].([]any)
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("groups missing or not array: %#v", payload["groups"])
|
||||
}
|
||||
if len(groups) != wantCount {
|
||||
return 0, fmt.Errorf("group count = %d, want %d", len(groups), wantCount)
|
||||
}
|
||||
return len(groups), nil
|
||||
}
|
||||
|
||||
func verifyReceiveFiles(ctx context.Context, session *mcp.ClientSession, nameContains string, wantCount int, wantFileID string, wantFileName string) (int, error) {
|
||||
payload, err := callToolObject(ctx, session, "isphere_receive_files", map[string]any{
|
||||
"mode": "list",
|
||||
"name_contains": nameContains,
|
||||
"source_preference": "local_readonly",
|
||||
"preview": true,
|
||||
"cursor": "",
|
||||
"output_dir": "",
|
||||
"limit": 5,
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if payload["ok"] != true {
|
||||
return 0, fmt.Errorf("isphere_receive_files ok = %#v, want true", payload["ok"])
|
||||
}
|
||||
files, ok := payload["files"].([]any)
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("files missing or not array: %#v", payload["files"])
|
||||
}
|
||||
if len(files) != wantCount {
|
||||
return 0, fmt.Errorf("file count = %d, want %d", len(files), wantCount)
|
||||
}
|
||||
if wantCount > 0 {
|
||||
first, ok := files[0].(map[string]any)
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("first file not object: %#v", files[0])
|
||||
}
|
||||
if first["file_id"] != wantFileID || first["file_name"] != wantFileName {
|
||||
return 0, fmt.Errorf("unexpected file: %#v", first)
|
||||
}
|
||||
if first["download_ref"] != nil || first["saved_path"] != nil || first["sha256"] != nil {
|
||||
return 0, fmt.Errorf("file list should not download or save: %#v", first)
|
||||
}
|
||||
}
|
||||
return len(files), nil
|
||||
}
|
||||
|
||||
func verifySendMessage(ctx context.Context, session *mcp.ClientSession) (bool, bool, error) {
|
||||
content := "redacted preview body"
|
||||
contentHash := sha256Hex(content)
|
||||
preview, err := callToolObject(ctx, session, "isphere_send_message", map[string]any{
|
||||
"target_type": "direct",
|
||||
"target_id": "sender@imopenfire1-lanzhou",
|
||||
"content_text": content,
|
||||
"content_sha256": contentHash,
|
||||
"idempotency_key": "capability-preview-idempotency-key",
|
||||
"execution_mode": "preview",
|
||||
})
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
if preview["ok"] != true || preview["send_status"] != "planned" || preview["connector_stage"] != "preview" {
|
||||
return false, false, fmt.Errorf("unexpected send preview payload: %#v", preview)
|
||||
}
|
||||
sideEffects, ok := preview["side_effects"].(map[string]any)
|
||||
if !ok || sideEffects["sent_message"] != false || sideEffects["clicked_ui"] != false || sideEffects["typed_text"] != false {
|
||||
return false, false, fmt.Errorf("send preview side effects mismatch: %#v", preview["side_effects"])
|
||||
}
|
||||
|
||||
production, err := callToolObject(ctx, session, "isphere_send_message", map[string]any{
|
||||
"target_type": "direct",
|
||||
"target_id": "sender@imopenfire1-lanzhou",
|
||||
"content_text": content,
|
||||
"content_sha256": contentHash,
|
||||
"idempotency_key": "capability-production-idempotency-key",
|
||||
"execution_mode": "production",
|
||||
})
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
if production["ok"] != false || production["send_status"] != "blocked" || production["connector_stage"] != "blocked" {
|
||||
return false, false, fmt.Errorf("production send was not blocked: %#v", production)
|
||||
}
|
||||
productionSideEffects, ok := production["side_effects"].(map[string]any)
|
||||
if !ok || productionSideEffects["sent_message"] != false || productionSideEffects["uploaded_file"] != false || productionSideEffects["captured_network"] != false {
|
||||
return false, false, fmt.Errorf("production side effects mismatch: %#v", production["side_effects"])
|
||||
}
|
||||
return true, false, nil
|
||||
}
|
||||
|
||||
func verifyPacketLogFileFixture(ctx context.Context) (int, int, int, int, error) {
|
||||
plaintext := fixturePacketLogPlaintext("msg-fixture-1", "redacted-report.docx")
|
||||
line, err := encryptPacketLogLine(plaintext)
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, err
|
||||
}
|
||||
file, err := os.CreateTemp("", "isphere-capability-packet-*.log")
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, err
|
||||
}
|
||||
fixturePath := file.Name()
|
||||
defer os.Remove(fixturePath)
|
||||
if _, err := file.WriteString("\n" + line + "\n"); err != nil {
|
||||
_ = file.Close()
|
||||
return 0, 0, 0, 0, err
|
||||
}
|
||||
if err := file.Close(); err != nil {
|
||||
return 0, 0, 0, 0, err
|
||||
}
|
||||
|
||||
restore := replaceEnv("ISPHERE_PACKET_LOG_FILE", fixturePath)
|
||||
defer restore()
|
||||
_ = os.Unsetenv("ISPHERE_PACKET_LOG_DIR")
|
||||
|
||||
session, cleanup, err := newSession(ctx, "isphere-capability-smoke-file-fixture")
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, err
|
||||
}
|
||||
defer cleanup()
|
||||
receiveCount, err := verifyReceiveMessages(ctx, session, 1)
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, err
|
||||
}
|
||||
contactCount, err := verifySearchContacts(ctx, session, "sender", 1)
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, err
|
||||
}
|
||||
groupCount, err := verifySearchGroups(ctx, session, "project", 1)
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, err
|
||||
}
|
||||
fileCount, err := verifyReceiveFiles(ctx, session, "report", 1, "msg-fixture-1:redacted-report.docx", "redacted-report.docx")
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, err
|
||||
}
|
||||
return receiveCount, contactCount, groupCount, fileCount, nil
|
||||
}
|
||||
|
||||
func verifyPacketLogDirFixture(ctx context.Context) (int, int, int, int, error) {
|
||||
plaintext := fixturePacketLogPlaintext("msg-dir-fixture-1", "redacted-dir-report.docx")
|
||||
line, err := encryptPacketLogLine(plaintext)
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, err
|
||||
}
|
||||
dir, err := os.MkdirTemp("", "isphere-capability-packet-dir-*")
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, err
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
if err := os.WriteFile(filepath.Join(dir, "a-packet-reader.log"), []byte("\n"+line+"\n"), 0o600); err != nil {
|
||||
return 0, 0, 0, 0, err
|
||||
}
|
||||
if err := os.WriteFile(filepath.Join(dir, "ignored.tmp"), []byte("ignored\n"), 0o600); err != nil {
|
||||
return 0, 0, 0, 0, err
|
||||
}
|
||||
|
||||
restoreFile := replaceEnv("ISPHERE_PACKET_LOG_FILE", "")
|
||||
restoreDir := replaceEnv("ISPHERE_PACKET_LOG_DIR", dir)
|
||||
defer restoreFile()
|
||||
defer restoreDir()
|
||||
|
||||
session, cleanup, err := newSession(ctx, "isphere-capability-smoke-dir-fixture")
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, err
|
||||
}
|
||||
defer cleanup()
|
||||
receiveCount, err := verifyReceiveMessages(ctx, session, 1)
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, err
|
||||
}
|
||||
contactCount, err := verifySearchContacts(ctx, session, "sender", 1)
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, err
|
||||
}
|
||||
groupCount, err := verifySearchGroups(ctx, session, "project", 1)
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, err
|
||||
}
|
||||
fileCount, err := verifyReceiveFiles(ctx, session, "dir-report", 1, "msg-dir-fixture-1:redacted-dir-report.docx", "redacted-dir-report.docx")
|
||||
if err != nil {
|
||||
return 0, 0, 0, 0, err
|
||||
}
|
||||
return receiveCount, contactCount, groupCount, fileCount, nil
|
||||
}
|
||||
|
||||
func callToolObject(ctx context.Context, session *mcp.ClientSession, name string, args map[string]any) (map[string]any, error) {
|
||||
result, err := session.CallTool(ctx, &mcp.CallToolParams{Name: name, Arguments: args})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %w", name, err)
|
||||
}
|
||||
if result.IsError {
|
||||
return nil, fmt.Errorf("%s returned IsError=true: %#v", name, result.Content)
|
||||
}
|
||||
payload, err := json.Marshal(result.StructuredContent)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s marshal structuredContent: %w", name, err)
|
||||
}
|
||||
var object map[string]any
|
||||
if err := json.Unmarshal(payload, &object); err != nil {
|
||||
return nil, fmt.Errorf("%s decode structuredContent %s: %w", name, payload, err)
|
||||
}
|
||||
return object, nil
|
||||
}
|
||||
|
||||
func fixturePacketLogPlaintext(messageID string, body string) string {
|
||||
return "--------------------------------------------------------------------------------------------------------------------------------------------\n" +
|
||||
"2026/7/7 15:30:07\n" +
|
||||
"<message id=\"" + messageID + "\" from=\"project-room@conference.imopenfire1-lanzhou/imp_pc_4.1.2.6842\" to=\"sender@imopenfire1-lanzhou\" type=\"groupchat\">\n" +
|
||||
" <body>" + body + "</body>\n" +
|
||||
" <received xmlns=\"urn:xmpp:receipts\" id=\"receipt-" + messageID + "\" type=\"1\" stamp=\"\" />\n" +
|
||||
" <subject>FILE_TRANSFER_CANCEL</subject>\n" +
|
||||
" <isphere xmlns=\"isphere.im\" type=\"1001\" sendtime=\"1783423807000\" version=\"1\" />\n" +
|
||||
" <readed />\n" +
|
||||
"</message>"
|
||||
}
|
||||
|
||||
func encryptPacketLogLine(plaintext string) (string, error) {
|
||||
block, err := des.NewCipher([]byte("hyhccdtm"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
plain := padPKCS7([]byte(plaintext), des.BlockSize)
|
||||
ciphertext := make([]byte, len(plain))
|
||||
iv := []byte{0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF}
|
||||
cipher.NewCBCEncrypter(block, iv).CryptBlocks(ciphertext, plain)
|
||||
return base64.StdEncoding.EncodeToString(ciphertext), nil
|
||||
}
|
||||
|
||||
func padPKCS7(data []byte, blockSize int) []byte {
|
||||
pad := blockSize - len(data)%blockSize
|
||||
out := append([]byte(nil), data...)
|
||||
for i := 0; i < pad; i++ {
|
||||
out = append(out, byte(pad))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func sha256Hex(value string) string {
|
||||
sum := sha256.Sum256([]byte(value))
|
||||
return hex.EncodeToString(sum[:])
|
||||
}
|
||||
|
||||
func replaceEnv(key string, value string) func() {
|
||||
oldValue, hadOldValue := os.LookupEnv(key)
|
||||
if value == "" {
|
||||
_ = os.Unsetenv(key)
|
||||
} else {
|
||||
_ = os.Setenv(key, value)
|
||||
}
|
||||
return func() {
|
||||
if hadOldValue {
|
||||
_ = os.Setenv(key, oldValue)
|
||||
} else {
|
||||
_ = os.Unsetenv(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fail(step string, err error) {
|
||||
encoded, _ := json.Marshal(map[string]any{
|
||||
"ok": false,
|
||||
"step": step,
|
||||
"error": err.Error(),
|
||||
})
|
||||
fmt.Fprintln(os.Stderr, string(encoded))
|
||||
os.Exit(1)
|
||||
}
|
||||
Reference in New Issue
Block a user