File: header.go

package info (click to toggle)
golang-github-la5nta-wl2k-go 0.11.9-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 3,856 kB
  • sloc: ansic: 14; makefile: 2
file content (176 lines) | stat: -rw-r--r-- 4,898 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
// Copyright 2016 Martin Hebnes Pedersen (LA5NTA). All rights reserved.
// Use of this source code is governed by the MIT-license that can be
// found in the LICENSE file.

package fbb

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"mime"
	"net/textproto"
	"sort"
	"strings"
	"unicode/utf8"

	"github.com/paulrosania/go-charset/charset"
	_ "github.com/paulrosania/go-charset/data"
)

// This file contains code from from net/http/header.go

// Common Winlink 2000 Message headers
const (
	HEADER_MID     = `Mid`
	HEADER_TO      = `To`
	HEADER_DATE    = `Date`
	HEADER_TYPE    = `Type`
	HEADER_FROM    = `From`
	HEADER_CC      = `Cc`
	HEADER_SUBJECT = `Subject`
	HEADER_MBO     = `Mbo`
	HEADER_BODY    = `Body`
	HEADER_FILE    = `File`

	// These headers are stripped by the winlink system, but let's
	// include it anyway... just in case the winlink team one day
	// starts taking encoding seriously.
	HEADER_CONTENT_TYPE              = `Content-Type`
	HEADER_CONTENT_TRANSFER_ENCODING = `Content-Transfer-Encoding`

	// The default body charset seems to be ISO-8859-1
	//
	// The Winlink Message Structure docs says that the body should
	// be ASCII-only, but RMS Express seems to encode the body as
	// ISO-8859-1. This is also the charset set (Content-Type header)
	// when a message reaches an SMTP server.
	DefaultCharset = "ISO-8859-1"

	// Mails going out over SMTP from the Winlink system is sent
	// with the header 'Content-Transfer-Encoding: 7bit', but
	// let's be reasonable... we don't send ASCII-only body.
	DefaultTransferEncoding = "8bit"

	// The date (in UTC) format as described in the Winlink
	// Message Structure docs (YYYY/MM/DD HH:MM).
	DateLayout = `2006/01/02 15:04`
)

// A Header represents the key-value pairs in a Winlink 2000 Message header.
type Header map[string][]string

// Add adds the key, value pair to the header.
// It appends to any existing values associated with key.
func (h Header) Add(key, value string) {
	textproto.MIMEHeader(h).Add(key, value)
}

// Set sets the header entries associated with key to
// the single element value.  It replaces any existing
// values associated with key.
func (h Header) Set(key, value string) {
	textproto.MIMEHeader(h).Set(key, value)
}

// Get gets the first value associated with the given key.
// If there are no values associated with the key, Get returns "".
// To access multiple values of a key, access the map directly
// with CanonicalHeaderKey.
func (h Header) Get(key string) string {
	return textproto.MIMEHeader(h).Get(key)
}

// get is like Get, but key must already be in CanonicalHeaderKey form.
func (h Header) get(key string) string {
	if v := h[key]; len(v) > 0 {
		return v[0]
	}
	return ""
}

// Del deletes the values associated with key.
func (h Header) Del(key string) {
	textproto.MIMEHeader(h).Del(key)
}

// Write writes a header in wire format.
func (h Header) Write(w io.Writer) error {
	var err error

	// Mid is required
	if h.get(HEADER_MID) == "" {
		return errors.New("Missing MID in header")
	}

	// Write mid, this is defined to be the first value
	_, err = fmt.Fprintf(w, "Mid: %s\r\n", h.get(HEADER_MID))
	if err != nil {
		return err
	}

	// The rest should be printed in a stable order to ensure reproducibility
	keys := make([]string, 0, len(h))
	for k, _ := range h {
		if !strings.EqualFold(k, HEADER_MID) {
			keys = append(keys, k)
		}
	}
	sort.Sort(sort.StringSlice(keys))
	for _, key := range keys {
		for _, v := range h[key] {
			v = textproto.TrimString(v)
			_, err = fmt.Fprintf(w, "%s: %s\r\n", key, v)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

// WordDecoder decodes MIME headers containing RFC 2047 encoded-words.
//
// (See DecodeHeader for mime.WordDecoder differences).
type WordDecoder struct{ mime.WordDecoder }

// Decode decodes an encoded-word.
//
// If word is not a valid RFC 2047 encoded-word, word is decoded as raw ISO-8859-1 as a work-around for RMS Express' non-conforming encoding of the Subject header.
func (d *WordDecoder) DecodeHeader(header string) (string, error) {
	i := strings.Index(header, "=?")
	if i > -1 {
		return d.WordDecoder.DecodeHeader(header)
	}

	// If there is no encoded-word, the data may be ISO-8859-1 or UTF-8 depending on how CMS decoded it.
	//
	// It turns out that if CMS receives a Q-encoded subject it decodes it and forwards it as UTF-8.
	// If CMS on the other hand receives an SMTP email from gmail, it is enocded as ISO-8859-1.
	if utf8.Valid([]byte(header)) {
		return header, nil
	}

	r, err := charset.NewReader(DefaultCharset, bytes.NewReader([]byte(header)))
	if err != nil {
		return header, err
	}

	decoded, _ := ioutil.ReadAll(r)
	return string(decoded), nil
}

func toCharset(set, s string) (string, error) {
	buf := new(bytes.Buffer)
	w, err := charset.NewWriter(set, buf)
	if err != nil {
		return s, err
	}

	fmt.Fprint(w, s)
	w.Close()
	return buf.String(), nil
}