mirror of
				https://codeberg.org/forgejo/forgejo.git
				synced 2025-11-04 08:21:11 +00:00 
			
		
		
		
	Since `modules/context` has to depend on `models` and many other packages, it should be moved from `modules/context` to `services/context` according to design principles. There is no logic code change on this PR, only move packages. - Move `code.gitea.io/gitea/modules/context` to `code.gitea.io/gitea/services/context` - Move `code.gitea.io/gitea/modules/contexttest` to `code.gitea.io/gitea/services/contexttest` because of depending on context - Move `code.gitea.io/gitea/modules/upload` to `code.gitea.io/gitea/services/context/upload` because of depending on context (cherry picked from commit 29f149bd9f517225a3c9f1ca3fb0a7b5325af696) Conflicts: routers/api/packages/alpine/alpine.go routers/api/v1/repo/issue_reaction.go routers/install/install.go routers/web/admin/config.go routers/web/passkey.go routers/web/repo/search.go routers/web/repo/setting/default_branch.go routers/web/user/home.go routers/web/user/profile.go tests/integration/editor_test.go tests/integration/integration_test.go tests/integration/mirror_push_test.go trivial context conflicts also modified all other occurrences in Forgejo specific files
		
			
				
	
	
		
			178 lines
		
	
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			178 lines
		
	
	
	
		
			4.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright 2023 The Gitea Authors. All rights reserved.
 | 
						|
// SPDX-License-Identifier: MIT
 | 
						|
 | 
						|
package incoming
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"context"
 | 
						|
	"fmt"
 | 
						|
 | 
						|
	issues_model "code.gitea.io/gitea/models/issues"
 | 
						|
	access_model "code.gitea.io/gitea/models/perm/access"
 | 
						|
	repo_model "code.gitea.io/gitea/models/repo"
 | 
						|
	user_model "code.gitea.io/gitea/models/user"
 | 
						|
	"code.gitea.io/gitea/modules/log"
 | 
						|
	"code.gitea.io/gitea/modules/setting"
 | 
						|
	"code.gitea.io/gitea/modules/util"
 | 
						|
	attachment_service "code.gitea.io/gitea/services/attachment"
 | 
						|
	"code.gitea.io/gitea/services/context/upload"
 | 
						|
	issue_service "code.gitea.io/gitea/services/issue"
 | 
						|
	incoming_payload "code.gitea.io/gitea/services/mailer/incoming/payload"
 | 
						|
	"code.gitea.io/gitea/services/mailer/token"
 | 
						|
	pull_service "code.gitea.io/gitea/services/pull"
 | 
						|
)
 | 
						|
 | 
						|
type MailHandler interface {
 | 
						|
	Handle(ctx context.Context, content *MailContent, doer *user_model.User, payload []byte) error
 | 
						|
}
 | 
						|
 | 
						|
var handlers = map[token.HandlerType]MailHandler{
 | 
						|
	token.ReplyHandlerType:       &ReplyHandler{},
 | 
						|
	token.UnsubscribeHandlerType: &UnsubscribeHandler{},
 | 
						|
}
 | 
						|
 | 
						|
// ReplyHandler handles incoming emails to create a reply from them
 | 
						|
type ReplyHandler struct{}
 | 
						|
 | 
						|
func (h *ReplyHandler) Handle(ctx context.Context, content *MailContent, doer *user_model.User, payload []byte) error {
 | 
						|
	if doer == nil {
 | 
						|
		return util.NewInvalidArgumentErrorf("doer can't be nil")
 | 
						|
	}
 | 
						|
 | 
						|
	ref, err := incoming_payload.GetReferenceFromPayload(ctx, payload)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	var issue *issues_model.Issue
 | 
						|
 | 
						|
	switch r := ref.(type) {
 | 
						|
	case *issues_model.Issue:
 | 
						|
		issue = r
 | 
						|
	case *issues_model.Comment:
 | 
						|
		comment := r
 | 
						|
 | 
						|
		if err := comment.LoadIssue(ctx); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
 | 
						|
		issue = comment.Issue
 | 
						|
	default:
 | 
						|
		return util.NewInvalidArgumentErrorf("unsupported reply reference: %v", ref)
 | 
						|
	}
 | 
						|
 | 
						|
	if err := issue.LoadRepo(ctx); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	perm, err := access_model.GetUserRepoPermission(ctx, issue.Repo, doer)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	// Locked issues require write permissions
 | 
						|
	if issue.IsLocked && !perm.CanWriteIssuesOrPulls(issue.IsPull) && !doer.IsAdmin {
 | 
						|
		log.Debug("can't write issue or pull")
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	if !perm.CanReadIssuesOrPulls(issue.IsPull) {
 | 
						|
		log.Debug("can't read issue or pull")
 | 
						|
		return nil
 | 
						|
	}
 | 
						|
 | 
						|
	switch r := ref.(type) {
 | 
						|
	case *issues_model.Issue:
 | 
						|
		attachmentIDs := make([]string, 0, len(content.Attachments))
 | 
						|
		if setting.Attachment.Enabled {
 | 
						|
			for _, attachment := range content.Attachments {
 | 
						|
				a, err := attachment_service.UploadAttachment(ctx, bytes.NewReader(attachment.Content), setting.Attachment.AllowedTypes, int64(len(attachment.Content)), &repo_model.Attachment{
 | 
						|
					Name:       attachment.Name,
 | 
						|
					UploaderID: doer.ID,
 | 
						|
					RepoID:     issue.Repo.ID,
 | 
						|
				})
 | 
						|
				if err != nil {
 | 
						|
					if upload.IsErrFileTypeForbidden(err) {
 | 
						|
						log.Info("Skipping disallowed attachment type: %s", attachment.Name)
 | 
						|
						continue
 | 
						|
					}
 | 
						|
					return err
 | 
						|
				}
 | 
						|
				attachmentIDs = append(attachmentIDs, a.UUID)
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if content.Content == "" && len(attachmentIDs) == 0 {
 | 
						|
			return nil
 | 
						|
		}
 | 
						|
 | 
						|
		_, err = issue_service.CreateIssueComment(ctx, doer, issue.Repo, issue, content.Content, attachmentIDs)
 | 
						|
		if err != nil {
 | 
						|
			return fmt.Errorf("CreateIssueComment failed: %w", err)
 | 
						|
		}
 | 
						|
	case *issues_model.Comment:
 | 
						|
		comment := r
 | 
						|
 | 
						|
		if content.Content == "" {
 | 
						|
			return nil
 | 
						|
		}
 | 
						|
 | 
						|
		if comment.Type == issues_model.CommentTypeCode {
 | 
						|
			_, err := pull_service.CreateCodeComment(
 | 
						|
				ctx,
 | 
						|
				doer,
 | 
						|
				nil,
 | 
						|
				issue,
 | 
						|
				comment.Line,
 | 
						|
				content.Content,
 | 
						|
				comment.TreePath,
 | 
						|
				false, // not pending review but a single review
 | 
						|
				comment.ReviewID,
 | 
						|
				"",
 | 
						|
				nil,
 | 
						|
			)
 | 
						|
			if err != nil {
 | 
						|
				return fmt.Errorf("CreateCodeComment failed: %w", err)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
// UnsubscribeHandler handles unwatching issues/pulls
 | 
						|
type UnsubscribeHandler struct{}
 | 
						|
 | 
						|
func (h *UnsubscribeHandler) Handle(ctx context.Context, _ *MailContent, doer *user_model.User, payload []byte) error {
 | 
						|
	if doer == nil {
 | 
						|
		return util.NewInvalidArgumentErrorf("doer can't be nil")
 | 
						|
	}
 | 
						|
 | 
						|
	ref, err := incoming_payload.GetReferenceFromPayload(ctx, payload)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	switch r := ref.(type) {
 | 
						|
	case *issues_model.Issue:
 | 
						|
		issue := r
 | 
						|
 | 
						|
		if err := issue.LoadRepo(ctx); err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
 | 
						|
		perm, err := access_model.GetUserRepoPermission(ctx, issue.Repo, doer)
 | 
						|
		if err != nil {
 | 
						|
			return err
 | 
						|
		}
 | 
						|
 | 
						|
		if !perm.CanReadIssuesOrPulls(issue.IsPull) {
 | 
						|
			log.Debug("can't read issue or pull")
 | 
						|
			return nil
 | 
						|
		}
 | 
						|
 | 
						|
		return issues_model.CreateOrUpdateIssueWatch(ctx, doer.ID, issue.ID, false)
 | 
						|
	}
 | 
						|
 | 
						|
	return fmt.Errorf("unsupported unsubscribe reference: %v", ref)
 | 
						|
}
 |