2017-11-07 14:33:06 +08:00
// Copyright 2017 The Gitea Authors. All rights reserved.
2022-11-28 02:20:29 +08:00
// SPDX-License-Identifier: MIT
2017-11-07 14:33:06 +08:00
package external
import (
2023-01-14 04:41:23 +08:00
"bytes"
2021-04-20 06:25:08 +08:00
"fmt"
2017-11-07 14:33:06 +08:00
"io"
"os"
"os/exec"
2018-11-21 06:11:21 +08:00
"runtime"
2017-11-07 14:33:06 +08:00
"strings"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup"
2021-07-01 04:07:23 +08:00
"code.gitea.io/gitea/modules/process"
2017-11-07 14:33:06 +08:00
"code.gitea.io/gitea/modules/setting"
2020-08-12 04:05:34 +08:00
"code.gitea.io/gitea/modules/util"
2017-11-07 14:33:06 +08:00
)
2021-04-20 06:25:08 +08:00
// RegisterRenderers registers all supported third part renderers according settings
func RegisterRenderers ( ) {
for _ , renderer := range setting . ExternalMarkupRenderers {
if renderer . Enabled && renderer . Command != "" && len ( renderer . FileExtensions ) > 0 {
markup . RegisterRenderer ( & Renderer { renderer } )
2017-11-07 14:33:06 +08:00
}
}
}
2021-04-20 06:25:08 +08:00
// Renderer implements markup.Renderer for external tools
type Renderer struct {
2021-06-24 05:09:51 +08:00
* setting . MarkupRenderer
2017-11-07 14:33:06 +08:00
}
2022-06-16 11:33:23 +08:00
var (
_ markup . PostProcessRenderer = ( * Renderer ) ( nil )
_ markup . ExternalRenderer = ( * Renderer ) ( nil )
)
2017-11-07 14:33:06 +08:00
// Name returns the external tool name
2021-04-20 06:25:08 +08:00
func ( p * Renderer ) Name ( ) string {
2017-11-07 14:33:06 +08:00
return p . MarkupName
}
2021-04-20 06:25:08 +08:00
// NeedPostProcess implements markup.Renderer
func ( p * Renderer ) NeedPostProcess ( ) bool {
return p . MarkupRenderer . NeedPostProcess
2021-04-13 15:06:31 +08:00
}
2017-11-07 14:33:06 +08:00
// Extensions returns the supported extensions of the tool
2021-04-20 06:25:08 +08:00
func ( p * Renderer ) Extensions ( ) [ ] string {
2017-11-07 14:33:06 +08:00
return p . FileExtensions
}
2021-06-24 05:09:51 +08:00
// SanitizerRules implements markup.Renderer
func ( p * Renderer ) SanitizerRules ( ) [ ] setting . MarkupSanitizerRule {
return p . MarkupSanitizerRules
}
2022-03-06 16:41:54 +08:00
// SanitizerDisabled disabled sanitize if return true
func ( p * Renderer ) SanitizerDisabled ( ) bool {
2022-06-16 11:33:23 +08:00
return p . RenderContentMode == setting . RenderContentModeNoSanitizer || p . RenderContentMode == setting . RenderContentModeIframe
}
// DisplayInIFrame represents whether render the content with an iframe
func ( p * Renderer ) DisplayInIFrame ( ) bool {
return p . RenderContentMode == setting . RenderContentModeIframe
2022-03-06 16:41:54 +08:00
}
2018-11-21 06:11:21 +08:00
func envMark ( envName string ) string {
if runtime . GOOS == "windows" {
return "%" + envName + "%"
}
return "$" + envName
}
2017-11-07 14:33:06 +08:00
// Render renders the data of the document to HTML via the external tool.
2021-04-20 06:25:08 +08:00
func ( p * Renderer ) Render ( ctx * markup . RenderContext , input io . Reader , output io . Writer ) error {
2017-11-07 14:33:06 +08:00
var (
2024-01-15 16:49:24 +08:00
command = strings . NewReplacer (
2024-11-24 16:18:57 +08:00
envMark ( "GITEA_PREFIX_SRC" ) , ctx . RenderHelper . ResolveLink ( "" , markup . LinkTypeDefault ) ,
envMark ( "GITEA_PREFIX_RAW" ) , ctx . RenderHelper . ResolveLink ( "" , markup . LinkTypeRaw ) ,
2024-01-15 16:49:24 +08:00
) . Replace ( p . Command )
2018-11-21 06:11:21 +08:00
commands = strings . Fields ( command )
2017-11-07 14:33:06 +08:00
args = commands [ 1 : ]
)
if p . IsInputFile {
// write to temp file
2021-09-22 13:38:34 +08:00
f , err := os . CreateTemp ( "" , "gitea_input" )
2017-11-07 14:33:06 +08:00
if err != nil {
2022-10-25 03:29:17 +08:00
return fmt . Errorf ( "%s create temp file when rendering %s failed: %w" , p . Name ( ) , p . Command , err )
2017-11-07 14:33:06 +08:00
}
2020-08-12 04:05:34 +08:00
tmpPath := f . Name ( )
defer func ( ) {
if err := util . Remove ( tmpPath ) ; err != nil {
log . Warn ( "Unable to remove temporary file: %s: Error: %v" , tmpPath , err )
}
} ( )
2017-11-07 14:33:06 +08:00
2021-04-20 06:25:08 +08:00
_ , err = io . Copy ( f , input )
2017-11-07 14:33:06 +08:00
if err != nil {
2024-11-18 13:25:42 +08:00
_ = f . Close ( )
2022-10-25 03:29:17 +08:00
return fmt . Errorf ( "%s write data to temp file when rendering %s failed: %w" , p . Name ( ) , p . Command , err )
2017-11-07 14:33:06 +08:00
}
err = f . Close ( )
if err != nil {
2022-10-25 03:29:17 +08:00
return fmt . Errorf ( "%s close temp file when rendering %s failed: %w" , p . Name ( ) , p . Command , err )
2017-11-07 14:33:06 +08:00
}
args = append ( args , f . Name ( ) )
}
2024-11-24 16:18:57 +08:00
processCtx , _ , finished := process . GetManager ( ) . AddContext ( ctx , fmt . Sprintf ( "Render [%s] for %s" , commands [ 0 ] , ctx . RenderHelper . ResolveLink ( "" , markup . LinkTypeDefault ) ) )
2021-12-01 04:06:32 +08:00
defer finished ( )
2021-07-01 04:07:23 +08:00
cmd := exec . CommandContext ( processCtx , commands [ 0 ] , args ... )
2018-10-30 22:34:12 +08:00
cmd . Env = append (
os . Environ ( ) ,
2024-11-24 16:18:57 +08:00
"GITEA_PREFIX_SRC=" + ctx . RenderHelper . ResolveLink ( "" , markup . LinkTypeDefault ) ,
"GITEA_PREFIX_RAW=" + ctx . RenderHelper . ResolveLink ( "" , markup . LinkTypeRaw ) ,
2018-10-30 22:34:12 +08:00
)
2017-11-07 14:33:06 +08:00
if ! p . IsInputFile {
2021-04-20 06:25:08 +08:00
cmd . Stdin = input
2017-11-07 14:33:06 +08:00
}
2023-01-14 04:41:23 +08:00
var stderr bytes . Buffer
2021-04-20 06:25:08 +08:00
cmd . Stdout = output
2023-01-14 04:41:23 +08:00
cmd . Stderr = & stderr
2022-06-03 22:36:18 +08:00
process . SetSysProcAttribute ( cmd )
2017-11-07 14:33:06 +08:00
if err := cmd . Run ( ) ; err != nil {
2023-01-14 04:41:23 +08:00
return fmt . Errorf ( "%s render run command %s %v failed: %w\nStderr: %s" , p . Name ( ) , commands [ 0 ] , args , err , stderr . String ( ) )
2017-11-07 14:33:06 +08:00
}
2021-04-20 06:25:08 +08:00
return nil
2017-11-07 14:33:06 +08:00
}