header.go raw

   1  package message
   2  
   3  import (
   4  	"mime"
   5  
   6  	"github.com/emersion/go-message/textproto"
   7  )
   8  
   9  func parseHeaderWithParams(s string) (f string, params map[string]string, err error) {
  10  	f, params, err = mime.ParseMediaType(s)
  11  	if err != nil {
  12  		return s, nil, err
  13  	}
  14  	for k, v := range params {
  15  		params[k], _ = decodeHeader(v)
  16  	}
  17  	return
  18  }
  19  
  20  func formatHeaderWithParams(f string, params map[string]string) string {
  21  	encParams := make(map[string]string)
  22  	for k, v := range params {
  23  		encParams[k] = encodeHeader(v)
  24  	}
  25  	return mime.FormatMediaType(f, encParams)
  26  }
  27  
  28  // HeaderFields iterates over header fields.
  29  type HeaderFields interface {
  30  	textproto.HeaderFields
  31  
  32  	// Text parses the value of the current field as plaintext. The field
  33  	// charset is decoded to UTF-8. If the header field's charset is unknown,
  34  	// the raw field value is returned and the error verifies IsUnknownCharset.
  35  	Text() (string, error)
  36  }
  37  
  38  type headerFields struct {
  39  	textproto.HeaderFields
  40  }
  41  
  42  func (hf *headerFields) Text() (string, error) {
  43  	return decodeHeader(hf.Value())
  44  }
  45  
  46  // A Header represents the key-value pairs in a message header.
  47  type Header struct {
  48  	textproto.Header
  49  }
  50  
  51  // HeaderFromMap creates a header from a map of header fields.
  52  //
  53  // This function is provided for interoperability with the standard library.
  54  // If possible, ReadHeader should be used instead to avoid loosing information.
  55  // The map representation looses the ordering of the fields, the capitalization
  56  // of the header keys, and the whitespace of the original header.
  57  func HeaderFromMap(m map[string][]string) Header {
  58  	return Header{textproto.HeaderFromMap(m)}
  59  }
  60  
  61  // ContentType parses the Content-Type header field.
  62  //
  63  // If no Content-Type is specified, it returns "text/plain".
  64  func (h *Header) ContentType() (t string, params map[string]string, err error) {
  65  	v := h.Get("Content-Type")
  66  	if v == "" {
  67  		return "text/plain", nil, nil
  68  	}
  69  	return parseHeaderWithParams(v)
  70  }
  71  
  72  // SetContentType formats the Content-Type header field.
  73  func (h *Header) SetContentType(t string, params map[string]string) {
  74  	h.Set("Content-Type", formatHeaderWithParams(t, params))
  75  }
  76  
  77  // ContentDisposition parses the Content-Disposition header field, as defined in
  78  // RFC 2183.
  79  func (h *Header) ContentDisposition() (disp string, params map[string]string, err error) {
  80  	return parseHeaderWithParams(h.Get("Content-Disposition"))
  81  }
  82  
  83  // SetContentDisposition formats the Content-Disposition header field, as
  84  // defined in RFC 2183.
  85  func (h *Header) SetContentDisposition(disp string, params map[string]string) {
  86  	h.Set("Content-Disposition", formatHeaderWithParams(disp, params))
  87  }
  88  
  89  // Text parses a plaintext header field. The field charset is automatically
  90  // decoded to UTF-8. If the header field's charset is unknown, the raw field
  91  // value is returned and the error verifies IsUnknownCharset.
  92  func (h *Header) Text(k string) (string, error) {
  93  	return decodeHeader(h.Get(k))
  94  }
  95  
  96  // SetText sets a plaintext header field.
  97  func (h *Header) SetText(k, v string) {
  98  	h.Set(k, encodeHeader(v))
  99  }
 100  
 101  // Copy creates a stand-alone copy of the header.
 102  func (h *Header) Copy() Header {
 103  	return Header{h.Header.Copy()}
 104  }
 105  
 106  // Fields iterates over all the header fields.
 107  //
 108  // The header may not be mutated while iterating, except using HeaderFields.Del.
 109  func (h *Header) Fields() HeaderFields {
 110  	return &headerFields{h.Header.Fields()}
 111  }
 112  
 113  // FieldsByKey iterates over all fields having the specified key.
 114  //
 115  // The header may not be mutated while iterating, except using HeaderFields.Del.
 116  func (h *Header) FieldsByKey(k string) HeaderFields {
 117  	return &headerFields{h.Header.FieldsByKey(k)}
 118  }
 119