* Add support for extra sendmail arguments * Sendmail args to exec.command should be a list * Add go-shellquote package * Use go-shellquote lib for parsing Sendmail args * Only parse if sendmail is configured
		
			
				
	
	
		
			145 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			145 lines
		
	
	
		
			3.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package shellquote
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"errors"
 | |
| 	"strings"
 | |
| 	"unicode/utf8"
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	UnterminatedSingleQuoteError = errors.New("Unterminated single-quoted string")
 | |
| 	UnterminatedDoubleQuoteError = errors.New("Unterminated double-quoted string")
 | |
| 	UnterminatedEscapeError      = errors.New("Unterminated backslash-escape")
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	splitChars        = " \n\t"
 | |
| 	singleChar        = '\''
 | |
| 	doubleChar        = '"'
 | |
| 	escapeChar        = '\\'
 | |
| 	doubleEscapeChars = "$`\"\n\\"
 | |
| )
 | |
| 
 | |
| // Split splits a string according to /bin/sh's word-splitting rules. It
 | |
| // supports backslash-escapes, single-quotes, and double-quotes. Notably it does
 | |
| // not support the $'' style of quoting. It also doesn't attempt to perform any
 | |
| // other sort of expansion, including brace expansion, shell expansion, or
 | |
| // pathname expansion.
 | |
| //
 | |
| // If the given input has an unterminated quoted string or ends in a
 | |
| // backslash-escape, one of UnterminatedSingleQuoteError,
 | |
| // UnterminatedDoubleQuoteError, or UnterminatedEscapeError is returned.
 | |
| func Split(input string) (words []string, err error) {
 | |
| 	var buf bytes.Buffer
 | |
| 	words = make([]string, 0)
 | |
| 
 | |
| 	for len(input) > 0 {
 | |
| 		// skip any splitChars at the start
 | |
| 		c, l := utf8.DecodeRuneInString(input)
 | |
| 		if strings.ContainsRune(splitChars, c) {
 | |
| 			input = input[l:]
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		var word string
 | |
| 		word, input, err = splitWord(input, &buf)
 | |
| 		if err != nil {
 | |
| 			return
 | |
| 		}
 | |
| 		words = append(words, word)
 | |
| 	}
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func splitWord(input string, buf *bytes.Buffer) (word string, remainder string, err error) {
 | |
| 	buf.Reset()
 | |
| 
 | |
| raw:
 | |
| 	{
 | |
| 		cur := input
 | |
| 		for len(cur) > 0 {
 | |
| 			c, l := utf8.DecodeRuneInString(cur)
 | |
| 			cur = cur[l:]
 | |
| 			if c == singleChar {
 | |
| 				buf.WriteString(input[0 : len(input)-len(cur)-l])
 | |
| 				input = cur
 | |
| 				goto single
 | |
| 			} else if c == doubleChar {
 | |
| 				buf.WriteString(input[0 : len(input)-len(cur)-l])
 | |
| 				input = cur
 | |
| 				goto double
 | |
| 			} else if c == escapeChar {
 | |
| 				buf.WriteString(input[0 : len(input)-len(cur)-l])
 | |
| 				input = cur
 | |
| 				goto escape
 | |
| 			} else if strings.ContainsRune(splitChars, c) {
 | |
| 				buf.WriteString(input[0 : len(input)-len(cur)-l])
 | |
| 				return buf.String(), cur, nil
 | |
| 			}
 | |
| 		}
 | |
| 		if len(input) > 0 {
 | |
| 			buf.WriteString(input)
 | |
| 			input = ""
 | |
| 		}
 | |
| 		goto done
 | |
| 	}
 | |
| 
 | |
| escape:
 | |
| 	{
 | |
| 		if len(input) == 0 {
 | |
| 			return "", "", UnterminatedEscapeError
 | |
| 		}
 | |
| 		c, l := utf8.DecodeRuneInString(input)
 | |
| 		if c == '\n' {
 | |
| 			// a backslash-escaped newline is elided from the output entirely
 | |
| 		} else {
 | |
| 			buf.WriteString(input[:l])
 | |
| 		}
 | |
| 		input = input[l:]
 | |
| 	}
 | |
| 	goto raw
 | |
| 
 | |
| single:
 | |
| 	{
 | |
| 		i := strings.IndexRune(input, singleChar)
 | |
| 		if i == -1 {
 | |
| 			return "", "", UnterminatedSingleQuoteError
 | |
| 		}
 | |
| 		buf.WriteString(input[0:i])
 | |
| 		input = input[i+1:]
 | |
| 		goto raw
 | |
| 	}
 | |
| 
 | |
| double:
 | |
| 	{
 | |
| 		cur := input
 | |
| 		for len(cur) > 0 {
 | |
| 			c, l := utf8.DecodeRuneInString(cur)
 | |
| 			cur = cur[l:]
 | |
| 			if c == doubleChar {
 | |
| 				buf.WriteString(input[0 : len(input)-len(cur)-l])
 | |
| 				input = cur
 | |
| 				goto raw
 | |
| 			} else if c == escapeChar {
 | |
| 				// bash only supports certain escapes in double-quoted strings
 | |
| 				c2, l2 := utf8.DecodeRuneInString(cur)
 | |
| 				cur = cur[l2:]
 | |
| 				if strings.ContainsRune(doubleEscapeChars, c2) {
 | |
| 					buf.WriteString(input[0 : len(input)-len(cur)-l-l2])
 | |
| 					if c2 == '\n' {
 | |
| 						// newline is special, skip the backslash entirely
 | |
| 					} else {
 | |
| 						buf.WriteRune(c2)
 | |
| 					}
 | |
| 					input = cur
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		return "", "", UnterminatedDoubleQuoteError
 | |
| 	}
 | |
| 
 | |
| done:
 | |
| 	return buf.String(), input, nil
 | |
| }
 |