mailcap/mailcap.go

273 lines
5.3 KiB
Go
Raw Normal View History

package mailcap
import (
"os"
"os/exec"
"strings"
"strconv"
"fmt"
"bufio"
"sort"
)
type Entry map[string]string
type Fields []Entry
type Cap map[string]Fields
type Mailcap struct {
Caps Cap
}
func NewMailcap() *Mailcap {
mc := Mailcap{make(Cap)}
mc.getCaps()
return &mc
}
2019-06-28 04:49:52 +00:00
func (m *Mailcap) ShowAll() {
fmt.Print(m.Caps)
fmt.Println()
}
2019-06-28 04:49:52 +00:00
func (m *Mailcap) Show(mime string) {
if v, ok := m.Caps[mime]; ok {
fmt.Println(mime + ":")
fmt.Print(v)
fmt.Println("")
} else {
fmt.Printf("Cannot find %s\n", mime)
}
}
func (m *Mailcap) ShowFiles() {
for _, s := range getMailcapFileList() {
fmt.Println(s)
fmt.Println("")
}
}
func (m *Mailcap) FindMatch(mime, key, path string) (string, error) {
entries := m.lookup(mime, key)
for _, v := range entries {
exitCode := 0;
if _, ok := v["needsterminal"]; ok {
continue
}
if t, ok := v["test"]; ok {
cmdArgs := strings.Split(t, " ")
if len(cmdArgs) < 2 {
continue
}
com := exec.Command(cmdArgs[0], cmdArgs[1:]...)
if err := com.Run(); err != nil {
if exitError, ok := err.(*exec.ExitError); ok {
exitCode = exitError.ExitCode()
}
}
}
if exitCode != 0 {
continue
}
if val, ok := v[key]; ok {
if strings.Index(val, "%s") >= 0 {
return fmt.Sprintf(val, path), nil
} else {
return val, nil
}
}
}
return "", fmt.Errorf("Unable to find key %q in entries for %s", key, mime)
}
func (m *Mailcap) RunMatch(mime, key, path string) {
match, err := m.FindMatch(mime, key, path)
var command *exec.Cmd
if err == nil {
matchFields := strings.Fields(match)
if len(matchFields) > 1 {
command = exec.Command(matchFields[0], matchFields[1:]...)
} else {
command = exec.Command(match)
}
e := command.Run()
fmt.Println(err)
if e != nil {
fmt.Println("There was an issue...")
}
}
}
func (m *Mailcap) lookup(mime, key string) Fields {
f := make(Fields, 0, 5)
if val, ok := m.Caps[mime]; ok {
f = append(f, val...)
}
splitMime := strings.SplitN(mime,"/",2)
catchAllMime := splitMime[0] + "/*"
if val, ok := m.Caps[catchAllMime]; ok && mime != catchAllMime {
f = append(f, val...)
}
output := make(Fields, 0, len(f))
for _, v := range f {
if _, ok := v[key]; ok {
output = append(output, v)
}
}
sort.SliceStable(
output,
func(i, j int) bool {
return output[i]["lineno"] < output[j]["lineno"]
})
return output
}
2019-06-28 04:49:52 +00:00
func (m *Mailcap) getCaps() {
lnNum := 0
2019-06-28 04:49:52 +00:00
var moreCaps Cap
moreCaps = make(Cap)
2019-06-28 04:49:52 +00:00
for _, mailcapFile := range getMailcapFileList() {
file, err := os.Open(mailcapFile)
if err != nil {
continue
}
2019-06-28 04:49:52 +00:00
moreCaps, lnNum = readMailcapFile(file, lnNum)
for k, v := range moreCaps {
2019-06-28 04:49:52 +00:00
if _, ok := m.Caps[k]; ok {
for _, item := range v {
m.Caps[k] = append(m.Caps[k], item)
}
} else {
2019-06-28 04:49:52 +00:00
m.Caps[k] = v
}
}
2019-06-28 04:49:52 +00:00
file.Close()
}
}
// Retrieve a slice of strings with all mailcap files
// found on the system.
func getMailcapFileList() (mCapSlice []string) {
var home string = "."
2019-06-28 04:49:52 +00:00
if val, ok := os.LookupEnv("MAILCAPS"); ok {
mCapSlice = strings.Split(val, string(os.PathListSeparator))
} else {
2019-06-28 04:49:52 +00:00
if val, ok := os.LookupEnv("HOME"); ok {
home = val
}
mCapSlice = []string{
home + "/.mailcap",
"/etc/mailcap",
"/usr/etc/mailcap",
2019-06-28 04:49:52 +00:00
"/usr/local/etc/mailcap",
}
}
2019-06-28 04:49:52 +00:00
return
}
func readMailcapFile(f *os.File,ln int) (Cap, int) {
caps := make(Cap)
reader := bufio.NewReader(f)
for {
l, e := reader.ReadString('\n')
if e != nil {
break
}
if strings.TrimSpace(l) == "" || l[0] == '#' {
continue
}
// Handle continuations for long lines
nxtLn := l
for ;nxtLn[len(nxtLn)-3:] == "\\\n"; {
2019-06-28 04:49:52 +00:00
var er error
nxtLn, er = reader.ReadString('\n')
if er != nil || strings.TrimSpace(nxtLn) == "" {
nxtLn = "\n"
}
l = l[:len(l)-2] + nxtLn
}
// Parse the line
key, fields, err := parseLine(l)
if err != nil {
continue
}
if ln >= 0 {
fields["lineno"] = strconv.Itoa(ln)
ln += 1
}
types := strings.Split(key, "/")
2019-06-28 04:49:52 +00:00
for i, t := range types {
types[i] = strings.TrimSpace(t)
}
key = strings.Join(types, "/")
key = strings.ToLower(key)
2019-06-28 04:49:52 +00:00
if _, ok := caps[key]; ok {
caps[key] = append(caps[key], fields)
} else {
caps[key] = make(Fields,0,10)
2019-06-28 04:49:52 +00:00
caps[key] = append(caps[key], fields)
}
}
return caps, ln
}
2019-06-28 04:49:52 +00:00
func parseLine(ln string) (string, Entry, error) {
outputFields := make(Entry)
i := 0
n := len(ln)
fields := make([]string, 0, 10)
2019-06-28 04:49:52 +00:00
var field string
for ;i < n; {
field, i = parseField(ln, i, n)
2019-06-28 04:49:52 +00:00
fields = append(fields, field)
i += 1
}
if len(fields) < 2 {
return "", outputFields,fmt.Errorf("Not enough fields present in line")
}
key, view := fields[0], fields[1]
outputFields["view"] = view
rest := make([]string,0,0)
if len(fields) > 2 {
rest = fields[2:]
}
2019-06-28 04:49:52 +00:00
for _, f := range rest {
var fkey, fvalue string
2019-06-28 04:49:52 +00:00
i = strings.Index(f, "=")
if i < 0 {
fkey = f
fvalue = ""
} else {
fkey = strings.TrimSpace(f[:i])
fvalue = strings.TrimSpace(f[i+1:])
}
if _, ok := outputFields[fkey]; !ok {
// If the key doesnt exist in the map, add it
outputFields[fkey] = fvalue
}
}
return key, outputFields, nil
}
func parseField(ln string, i, n int) (string, int) {
// get one key-value pair from mailcap entry
start := i
for ;i < n; {
c := ln[i]
if c == ';' {
break
} else if c == '\\' {
i += 2
} else {
i += 1
}
}
2019-06-28 04:49:52 +00:00
return strings.TrimSpace(ln[start:i]), i
}