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