2017-11-07 14:33:06 +08:00
// Copyright 2017 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package external
import (
2021-06-30 21:07:23 +01:00
"context"
2021-04-20 06:25:08 +08:00
"fmt"
2017-11-07 14:33:06 +08:00
"io"
"io/ioutil"
"os"
"os/exec"
2018-11-21 06:11:21 +08:00
"runtime"
2017-11-07 14:33:06 +08:00
"strings"
2021-08-28 21:15:56 +01:00
"code.gitea.io/gitea/modules/graceful"
2017-11-07 14:33:06 +08:00
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup"
2021-06-30 21:07:23 +01:00
"code.gitea.io/gitea/modules/process"
2017-11-07 14:33:06 +08:00
"code.gitea.io/gitea/modules/setting"
2020-08-11 21:05:34 +01: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-23 23:09:51 +02:00
* setting . MarkupRenderer
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-23 23:09:51 +02:00
// SanitizerRules implements markup.Renderer
func ( p * Renderer ) SanitizerRules ( ) [ ] setting . MarkupSanitizerRule {
return p . MarkupSanitizerRules
}
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 (
2021-04-20 06:25:08 +08:00
urlRawPrefix = strings . Replace ( ctx . URLPrefix , "/src/" , "/raw/" , 1 )
command = strings . NewReplacer ( envMark ( "GITEA_PREFIX_SRC" ) , ctx . URLPrefix ,
2018-11-21 06:11:21 +08:00
envMark ( "GITEA_PREFIX_RAW" ) , urlRawPrefix ) . Replace ( p . Command )
commands = strings . Fields ( command )
2017-11-07 14:33:06 +08:00
args = commands [ 1 : ]
)
if p . IsInputFile {
// write to temp file
f , err := ioutil . TempFile ( "" , "gitea_input" )
if err != nil {
2021-04-20 06:25:08 +08:00
return fmt . Errorf ( "%s create temp file when rendering %s failed: %v" , p . Name ( ) , p . Command , err )
2017-11-07 14:33:06 +08:00
}
2020-08-11 21:05:34 +01: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 {
f . Close ( )
2021-04-20 06:25:08 +08:00
return fmt . Errorf ( "%s write data to temp file when rendering %s failed: %v" , p . Name ( ) , p . Command , err )
2017-11-07 14:33:06 +08:00
}
err = f . Close ( )
if err != nil {
2021-04-20 06:25:08 +08:00
return fmt . Errorf ( "%s close temp file when rendering %s failed: %v" , p . Name ( ) , p . Command , err )
2017-11-07 14:33:06 +08:00
}
args = append ( args , f . Name ( ) )
}
2021-07-12 23:13:59 +02:00
if ctx == nil || ctx . Ctx == nil {
2021-08-28 21:15:56 +01:00
if ctx == nil {
log . Warn ( "RenderContext not provided defaulting to empty ctx" )
ctx = & markup . RenderContext { }
}
log . Warn ( "RenderContext did not provide context, defaulting to Shutdown context" )
ctx . Ctx = graceful . GetManager ( ) . ShutdownContext ( )
2021-07-12 23:13:59 +02:00
}
2021-06-30 21:07:23 +01:00
processCtx , cancel := context . WithCancel ( ctx . Ctx )
defer cancel ( )
pid := process . GetManager ( ) . Add ( fmt . Sprintf ( "Render [%s] for %s" , commands [ 0 ] , ctx . URLPrefix ) , cancel )
defer process . GetManager ( ) . Remove ( pid )
cmd := exec . CommandContext ( processCtx , commands [ 0 ] , args ... )
2018-10-30 15:34:12 +01:00
cmd . Env = append (
os . Environ ( ) ,
2021-04-20 06:25:08 +08:00
"GITEA_PREFIX_SRC=" + ctx . URLPrefix ,
2018-11-21 06:11:21 +08:00
"GITEA_PREFIX_RAW=" + urlRawPrefix ,
2018-10-30 15:34:12 +01: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
}
2021-04-20 06:25:08 +08:00
cmd . Stdout = output
2017-11-07 14:33:06 +08:00
if err := cmd . Run ( ) ; err != nil {
2021-04-20 06:25:08 +08:00
return fmt . Errorf ( "%s render run command %s %v failed: %v" , p . Name ( ) , commands [ 0 ] , args , err )
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
}