Files
gotenberg/app/converter/process/process.go
2018-04-06 12:19:14 +02:00

140 lines
3.2 KiB
Go

// Package process handles all commands executions.
package process
import (
"bytes"
"fmt"
"os/exec"
"text/template"
"time"
"github.com/thecodingmachine/gotenberg/app/config"
gfile "github.com/thecodingmachine/gotenberg/app/converter/file"
)
var commandsConfig *config.CommandsConfig
// Load loads the commands configuration coming from the application configuration.
func Load(config *config.CommandsConfig) {
commandsConfig = config
}
// conversionData will be applied to the data-driven templates of conversions commands.
type conversionData struct {
FilePath string
ResultFilePath string
}
type impossibleConversionError struct{}
const impossibleConversionErrorMessage = "Impossible conversion"
func (e *impossibleConversionError) Error() string {
return impossibleConversionErrorMessage
}
// Unconv converts a file to PDF and returns the new file path.
func Unconv(workingDir string, file *gfile.File) (string, error) {
cmdData := &conversionData{
FilePath: file.Path,
ResultFilePath: fmt.Sprintf("%s%s", gfile.MakeFilePath(workingDir), gfile.PDFExt),
}
var (
cmdTemplate *template.Template
cmdTimeout int
)
switch file.Type {
case gfile.HTMLType:
cmdTemplate = commandsConfig.HTML.Template
cmdTimeout = commandsConfig.HTML.Timeout
break
case gfile.OfficeType:
cmdTemplate = commandsConfig.Office.Template
cmdTimeout = commandsConfig.Office.Timeout
break
default:
return "", &impossibleConversionError{}
}
var data bytes.Buffer
if err := cmdTemplate.Execute(&data, cmdData); err != nil {
return "", err
}
err := run(data.String(), cmdTimeout)
if err != nil {
return "", err
}
return cmdData.ResultFilePath, nil
}
// mergeData will be applied to the data-driven template of the merge command.
type mergeData struct {
FilesPaths []string
ResultFilePath string
}
// Merge merges many PDF files to one unique PDF file and returns the new file path.
func Merge(workingDir string, filesPaths []string) (string, error) {
cmdData := &mergeData{
FilesPaths: filesPaths,
ResultFilePath: fmt.Sprintf("%s%s", gfile.MakeFilePath(workingDir), gfile.PDFExt),
}
cmdTemplate := commandsConfig.Merge.Template
cmdTimeout := commandsConfig.Merge.Timeout
var data bytes.Buffer
if err := cmdTemplate.Execute(&data, cmdData); err != nil {
return "", err
}
err := run(data.String(), cmdTimeout)
if err != nil {
return "", err
}
return cmdData.ResultFilePath, nil
}
type commandTimeoutError struct{}
const commandTimeoutErrorMessage = "The command has reached timeout"
func (e *commandTimeoutError) Error() string {
return commandTimeoutErrorMessage
}
// run runs the given command. If timeout is reached or
// something bad happened, returns an error.
func run(command string, timeout int) error {
cmd := exec.Command("/bin/sh", "-c", command)
if err := cmd.Start(); err != nil {
return err
}
done := make(chan error, 1)
go func() {
done <- cmd.Wait()
}()
// wait for the process to finish or kill it after a timeout.
select {
case <-time.After(time.Duration(timeout) * time.Second):
if err := cmd.Process.Kill(); err != nil {
return err
}
return &commandTimeoutError{}
case err := <-done:
if err != nil {
return err
}
return nil
}
}