From e93df9b5bc32df01eb90e32b7f0bb1001cf18c53 Mon Sep 17 00:00:00 2001 From: Runxi Yu Date: Wed, 02 Apr 2025 15:06:15 +0800 Subject: [PATCH] LMTP: Fix patch handling (\r\n, mbox format, double-Wait) --- git_plumbing.go | 6 ------ lmtp_handle_patch.go | 35 ++++++++++++++++++----------------- lmtp_server.go | 19 ++++++++++++++++--- diff --git a/git_plumbing.go b/git_plumbing.go index c210027106fa89ce7b65309bda276ad1bb4477b2..e787c59dbbeb3beaa06874bb7734461926b7af1f 100644 --- a/git_plumbing.go +++ b/git_plumbing.go @@ -53,9 +53,6 @@ cmd.Stdout = &out if err := cmd.Run(); err != nil { return "", err } - if err := cmd.Wait(); err != nil { - return "", err - } return strings.TrimSpace(out.String()), nil } @@ -69,9 +66,6 @@ cmd.Env = append(os.Environ(), "GIT_DIR="+repoPath) var out bytes.Buffer cmd.Stdout = &out if err := cmd.Run(); err != nil { - return err - } - if err := cmd.Wait(); err != nil { return err } data := out.Bytes() diff --git a/lmtp_handle_patch.go b/lmtp_handle_patch.go index d69424b512c3ec2a890be0a03707f018e36b0800..6bcb272825be94bfd7ffc2a0d67d8a16599e4188 100644 --- a/lmtp_handle_patch.go +++ b/lmtp_handle_patch.go @@ -7,46 +7,47 @@ import ( "bytes" "crypto/rand" "encoding/hex" + "fmt" + "io" "os" "os/exec" "strings" "time" "github.com/bluekeyes/go-gitdiff/gitdiff" - "github.com/emersion/go-message" "github.com/go-git/go-git/v5" ) -func lmtpHandlePatch(session *lmtpSession, groupPath []string, repoName string, email *message.Entity) (err error) { +func lmtpHandlePatch(session *lmtpSession, groupPath []string, repoName string, mbox io.Reader) (err error) { var diffFiles []*gitdiff.File var preamble string - if diffFiles, preamble, err = gitdiff.Parse(email.Body); err != nil { - return + if diffFiles, preamble, err = gitdiff.Parse(mbox); err != nil { + return fmt.Errorf("failed to parse patch: %w", err) } var header *gitdiff.PatchHeader if header, err = gitdiff.ParsePatchHeader(preamble); err != nil { - return + return fmt.Errorf("failed to parse patch headers: %w", err) } var repo *git.Repository var fsPath string repo, _, _, fsPath, err = openRepo(session.ctx, groupPath, repoName) if err != nil { - return + return fmt.Errorf("failed to open repo: %w", err) } headRef, err := repo.Head() if err != nil { - return + return fmt.Errorf("failed to get repo head hash: %w", err) } headCommit, err := repo.CommitObject(headRef.Hash()) if err != nil { - return + return fmt.Errorf("failed to get repo head commit: %w", err) } headTree, err := headCommit.Tree() if err != nil { - return + return fmt.Errorf("failed to get repo head tree: %w", err) } headTreeHash := headTree.Hash.String() @@ -55,17 +56,17 @@ blobUpdates := make(map[string][]byte) for _, diffFile := range diffFiles { sourceFile, err := headTree.File(diffFile.OldName) if err != nil { - return err + return fmt.Errorf("failed to get file at old name %#v: %w", diffFile.OldName, err) } sourceString, err := sourceFile.Contents() if err != nil { - return err + return fmt.Errorf("failed to get contents: %w", err) } sourceBuf := bytes.NewReader(stringToBytes(sourceString)) var patchedBuf bytes.Buffer if err := gitdiff.Apply(&patchedBuf, sourceBuf, diffFile); err != nil { - return err + return fmt.Errorf("failed to apply patch: %w", err) } var hashBuf bytes.Buffer @@ -78,13 +79,13 @@ cmd.Env = append(os.Environ(), "GIT_DIR="+fsPath) cmd.Stdout = &hashBuf cmd.Stdin = &patchedBuf if err := cmd.Run(); err != nil { - return err + return fmt.Errorf("failed to run git hash-object: %w", err) } newHashStr := strings.TrimSpace(hashBuf.String()) newHash, err := hex.DecodeString(newHashStr) if err != nil { - return err + return fmt.Errorf("failed to decode hex string from git: %w", err) } blobUpdates[diffFile.NewName] = newHash @@ -95,7 +96,7 @@ } newTreeSha, err := buildTreeRecursive(session.ctx, fsPath, headTreeHash, blobUpdates) if err != nil { - return err + return fmt.Errorf("failed to recursively build a tree: %w", err) } commitMsg := header.Title @@ -115,7 +116,7 @@ var commitOut bytes.Buffer commitCmd.Stdout = &commitOut if err := commitCmd.Run(); err != nil { - return err + return fmt.Errorf("failed to commit tree: %w", err) } newCommitSha := strings.TrimSpace(commitOut.String()) @@ -124,7 +125,7 @@ refCmd := exec.CommandContext(session.ctx, "git", "update-ref", "refs/heads/contrib/"+newBranchName, newCommitSha) //#nosec G204 refCmd.Env = append(os.Environ(), "GIT_DIR="+fsPath) if err := refCmd.Run(); err != nil { - return err + return fmt.Errorf("failed to update ref: %w", err) } return nil diff --git a/lmtp_server.go b/lmtp_server.go index 44dc8d41db3ee1fa38a8520db2b25ae3c7a6cb7e..cdfcffbb6a7214a82797f872248e4ab6f94bae5c 100644 --- a/lmtp_server.go +++ b/lmtp_server.go @@ -158,12 +158,24 @@ err = errors.New("illegal path") goto end } + mbox := bytes.Buffer{} + if _, err = fmt.Fprint(&mbox, "From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001\r\n"); err != nil { + slog.Error("error handling patch... malloc???", "error", err) + goto end + } + data = bytes.ReplaceAll(data, []byte("\r\n"), []byte("\n")) + if _, err = mbox.Write(data); err != nil { + slog.Error("error handling patch... malloc???", "error", err) + goto end + } + // TODO: Is mbox's From escaping necessary here? + groupPath := segments[:sepIndex] moduleType := segments[sepIndex+1] moduleName := segments[sepIndex+2] switch moduleType { case "repos": - err = lmtpHandlePatch(session, groupPath, moduleName, email) + err = lmtpHandlePatch(session, groupPath, moduleName, &mbox) if err != nil { slog.Error("error handling patch", "error", err) goto end @@ -182,8 +194,9 @@ case nil: return nil default: return &smtp.SMTPError{ - Code: 550, - Message: err.Error(), + Code: 550, + Message: "Permanent failure: " + err.Error(), + EnhancedCode: [3]int{5, 7, 1}, } } } -- 2.48.1