func (p StepIntoParams) WithBreakOnAsyncCall(breakOnAsyncCall bool) *StepIntoParams {
	p.BreakOnAsyncCall = breakOnAsyncCall
	return &p
}

func ( p StepIntoParams ) WithBreakOnAsyncCall ( breakOnAsyncCall bool ) * StepIntoParams { p . BreakOnAsyncCall = breakOnAsyncCall return & p }

// WithBreakOnAsyncCall debugger will issue additional Debugger.paused
// notification if any async task is scheduled before next pause.

WithBreakOnAsyncCall debugger will issue additional Debugger . paused notification if any async task is scheduled before next pause .


func (p *StepIntoParams) Do(ctx context.Context) (err error) {
	return cdp.Execute(ctx, CommandStepInto, p, nil)
}

func ( p * StepIntoParams ) Do ( ctx context . Context ) ( err error ) { return cdp . Execute ( ctx , CommandStepInto , p , nil ) }

// Do executes Debugger.stepInto against the provided context.

Do executes Debugger . stepInto against the provided context .


func (p *StepOutParams) Do(ctx context.Context) (err error) {
	return cdp.Execute(ctx, CommandStepOut, nil, nil)
}

func ( p * StepOutParams ) Do ( ctx context . Context ) ( err error ) { return cdp . Execute ( ctx , CommandStepOut , nil , nil ) }

// Do executes Debugger.stepOut against the provided context.

Do executes Debugger . stepOut against the provided context .


func (p *StepOverParams) Do(ctx context.Context) (err error) {
	return cdp.Execute(ctx, CommandStepOver, nil, nil)
}

func ( p * StepOverParams ) Do ( ctx context . Context ) ( err error ) { return cdp . Execute ( ctx , CommandStepOver , nil , nil ) }

// Do executes Debugger.stepOver against the provided context.

Do executes Debugger . stepOver against the provided context .


func (nb *NotifyBundle) Listen(n Network, a ma.Multiaddr) {
	if nb.ListenF != nil {
		nb.ListenF(n, a)
	}
}

func ( nb * NotifyBundle ) Listen ( n Network , a ma . Multiaddr ) { if nb . ListenF != nil { nb . ListenF ( n , a ) } }

// Listen calls ListenF if it is not null.

Listen calls ListenF if it is not null .


func (nb *NotifyBundle) ListenClose(n Network, a ma.Multiaddr) {
	if nb.ListenCloseF != nil {
		nb.ListenCloseF(n, a)
	}
}

func ( nb * NotifyBundle ) ListenClose ( n Network , a ma . Multiaddr ) { if nb . ListenCloseF != nil { nb . ListenCloseF ( n , a ) } }

// ListenClose calls ListenCloseF if it is not null.

ListenClose calls ListenCloseF if it is not null .


func (nb *NotifyBundle) Connected(n Network, c Conn) {
	if nb.ConnectedF != nil {
		nb.ConnectedF(n, c)
	}
}

func ( nb * NotifyBundle ) Connected ( n Network , c Conn ) { if nb . ConnectedF != nil { nb . ConnectedF ( n , c ) } }

// Connected calls ConnectedF if it is not null.

Connected calls ConnectedF if it is not null .


func (nb *NotifyBundle) Disconnected(n Network, c Conn) {
	if nb.DisconnectedF != nil {
		nb.DisconnectedF(n, c)
	}
}

func ( nb * NotifyBundle ) Disconnected ( n Network , c Conn ) { if nb . DisconnectedF != nil { nb . DisconnectedF ( n , c ) } }

// Disconnected calls DisconnectedF if it is not null.

Disconnected calls DisconnectedF if it is not null .


func (nb *NotifyBundle) OpenedStream(n Network, s Stream) {
	if nb.OpenedStreamF != nil {
		nb.OpenedStreamF(n, s)
	}
}

func ( nb * NotifyBundle ) OpenedStream ( n Network , s Stream ) { if nb . OpenedStreamF != nil { nb . OpenedStreamF ( n , s ) } }

// OpenedStream calls OpenedStreamF if it is not null.

OpenedStream calls OpenedStreamF if it is not null .


func (nb *NotifyBundle) ClosedStream(n Network, s Stream) {
	if nb.ClosedStreamF != nil {
		nb.ClosedStreamF(n, s)
	}
}

func ( nb * NotifyBundle ) ClosedStream ( n Network , s Stream ) { if nb . ClosedStreamF != nil { nb . ClosedStreamF ( n , s ) } }

// ClosedStream calls ClosedStreamF if it is not null.

ClosedStream calls ClosedStreamF if it is not null .


func WithNoDial(ctx context.Context, reason string) context.Context {
	return context.WithValue(ctx, noDial, reason)
}

func WithNoDial ( ctx context . Context , reason string ) context . Context { return context . WithValue ( ctx , noDial , reason ) }

// WithNoDial constructs a new context with an option that instructs the network
// to not attempt a new dial when opening a stream.

WithNoDial constructs a new context with an option that instructs the network to not attempt a new dial when opening a stream .


func GetNoDial(ctx context.Context) (nodial bool, reason string) {
	v := ctx.Value(noDial)
	if v != nil {
		return true, v.(string)
	}

	return false, ""
}

func GetNoDial ( ctx context . Context ) ( nodial bool , reason string ) { v := ctx . Value ( noDial ) if v != nil { return true , v . ( string ) } return false , " " }

// GetNoDial returns true if the no dial option is set in the context.

GetNoDial returns true if the no dial option is set in the context .


func WithDialPeerTimeout(ctx context.Context, timeout time.Duration) context.Context {
	return context.WithValue(ctx, dialPeerTimeoutCtxKey{}, timeout)
}

func WithDialPeerTimeout ( ctx context . Context , timeout time . Duration ) context . Context { return context . WithValue ( ctx , dialPeerTimeoutCtxKey { } , timeout ) }

// WithDialPeerTimeout returns a new context with the DialPeer timeout applied.
//
// This timeout overrides the default DialPeerTimeout and applies per-dial
// independently.

WithDialPeerTimeout returns a new context with the DialPeer timeout applied . This timeout overrides the default DialPeerTimeout and applies per - dial independently .


func (drv *Driver) Open(name string) (driver.Conn, error) {
	conn, err := drv.Driver.Open(name)
	if err != nil {
		return conn, err
	}

	wrapped := &Conn{conn, drv.hooks}
	if isExecer(conn) && isQueryer(conn) && isSessionResetter(conn) {
		return &ExecerQueryerContextWithSessionResetter{wrapped,
			&ExecerContext{wrapped}, &QueryerContext{wrapped},
			&SessionResetter{wrapped}}, nil
	} else if isExecer(conn) && isQueryer(conn) {
		return &ExecerQueryerContext{wrapped, &ExecerContext{wrapped},
			&QueryerContext{wrapped}}, nil
	} else if isExecer(conn) {
		// If conn implements an Execer interface, return a driver.Conn which
		// also implements Execer
		return &ExecerContext{wrapped}, nil
	} else if isQueryer(conn) {
		// If conn implements an Queryer interface, return a driver.Conn which
		// also implements Queryer
		return &QueryerContext{wrapped}, nil
	}
	return wrapped, nil
}


func (f *FixedSizeRingBuf) Prevpos(from int) int {
	if from >= f.N || from < 0 {
		return -2
	}
	if f.Readable == 0 {
		return -1
	}
	if from == f.Beg {
		return -1
	}
	a0, a1, b0, b1 := f.LegalPos()
	switch {
	case from == a0:
		return -1
	case from > a0 && from <= a1:
		return from - 1
	case from == b0:
		return a1
	case from > b0 && from <= b1:
		return from - 1
	}
	return -1
}

func ( f * FixedSizeRingBuf ) Prevpos ( from int ) int { if from >= f . N || from < 0 { return - 2 } if f . Readable == 0 { return - 1 } if from == f . Beg { return - 1 } a0 , a1 , b0 , b1 := f . LegalPos ( ) switch { case from == a0 : return - 1 case from > a0 && from <= a1 : return from - 1 case from == b0 : return a1 case from > b0 && from <= b1 : return from - 1 } return - 1 }

// Prevpos returns the index of the element before
// from, or -1 if no more and from is the
// first in the ring. Returns -2 on bad
// from position.

Prevpos returns the index of the element before from or - 1 if no more and from is the first in the ring . Returns - 2 on bad from position .


func (f *FixedSizeRingBuf) Last() int {
	if f.Readable == 0 {
		return -1
	}

	last := f.Beg + f.Readable - 1
	if last < f.N {
		// we fit without wrapping
		return last
	}

	return last % f.N
}

func
(
f
*
FixedSizeRingBuf
)
Last
(
)
int
{
if
f
.
Readable
==
0
{
return
1
}
last
:=
f
.
Beg
+
f
.
Readable
1
if
last
<
f
.
N
{
// we fit without wrapping
return
last
}
return
last
%
f
.
N
}

// Last returns the index of the last element,
// or -1 if the ring is empty.

Last returns the index of the last element or - 1 if the ring is empty .


func (f *FixedSizeRingBuf) DeleteMostRecentBytes(n int) {
	if n <= 0 {
		return
	}
	if n >= f.Readable {
		f.Readable = 0
		return
	}
	f.Readable -= n
}

func ( f * FixedSizeRingBuf ) DeleteMostRecentBytes ( n int ) { if n <= 0 { return } if n >= f . Readable { f . Readable = 0 return } f . Readable -= n }

// DeleteMostRecentBytes trims back the last n bytes written.

DeleteMostRecentBytes trims back the last n bytes written .


func NewFloat64RingBuf(maxViewItems int) *Float64RingBuf {
	n := maxViewItems
	r := &Float64RingBuf{
		N:        n,
		Beg:      0,
		Readable: 0,
	}
	r.A = make([]float64, n, n)

	return r
}

func NewFloat64RingBuf ( maxViewItems int ) * Float64RingBuf { n := maxViewItems r := & Float64RingBuf { N : n , Beg : 0 , Readable : 0 , } r . A = make ( [ ] float64 , n , n ) return r }

// constructor. NewFloat64RingBuf will allocate internally
// a slice of maxViewItems float64.

constructor . NewFloat64RingBuf will allocate internally a slice of maxViewItems float64 .


func (b *Float64RingBuf) TwoContig(makeCopy bool) (first []float64, second []float64) {

	extent := b.Beg + b.Readable
	if extent <= b.N {
		// we fit contiguously in this buffer without wrapping to the other.
		// Let second stay an empty slice.
		return b.A[b.Beg:(b.Beg + b.Readable)], second
	}

	return b.A[b.Beg:b.N], b.A[0:(extent % b.N)]
}

// TwoContig returns all readable float64, but in two separate slices,
// to avoid copying. The two slices are from the same buffer, but
// are not contiguous. Either or both may be empty slices.

TwoContig returns all readable float64 but in two separate slices to avoid copying . The two slices are from the same buffer but are not contiguous . Either or both may be empty slices .


func (b *Float64RingBuf) Earliest() (v float64, ok bool) {
	if b.Readable == 0 {
		return
	}

	return b.A[b.Beg], true
}

func ( b * Float64RingBuf ) Earliest ( ) ( v float64 , ok bool ) { if b . Readable == 0 { return } return b . A [ b . Beg ] , true }

// Earliest returns the earliest written value v. ok will be
// true unless the ring is empty, in which case ok will be false,
// and v will be zero.

Earliest returns the earliest written value v . ok will be true unless the ring is empty in which case ok will be false and v will be zero .


func (b *Float64RingBuf) Values() []float64 {
	first, second := b.TwoContig(false)

	if len(first) == 0 {
		return second
	}

	if len(second) == 0 {
		return first
	}

	out := make([]float64, len(first) + len(second))

	copy(out, first)
	copy(out[len(first):], second)

	return out
}

func ( b * Float64RingBuf ) Values ( ) [ ] float64 { first , second := b . TwoContig ( false ) if len ( first ) == 0 { return second } if len ( second ) == 0 { return first } out := make ( [ ] float64 , len ( first ) + len ( second ) ) copy ( out , first ) copy ( out [ len ( first ) : ] , second ) return out }

// Values returns all readable float64 in a single buffer. Calling this function
// might allocate a new buffer to store the elements contiguously.

Values returns all readable float64 in a single buffer . Calling this function might allocate a new buffer to store the elements contiguously .


func NewAtomicFixedSizeRingBuf(maxViewInBytes int) *AtomicFixedSizeRingBuf {
	n := maxViewInBytes
	r := &AtomicFixedSizeRingBuf{
		Use: 0, // 0 or 1, whichever is actually in use at the moment.
		// If we are asked for Bytes() and we wrap, linearize into the other.

		N:        n,
		Beg:      0,
		readable: 0,
	}
	r.A[0] = make([]byte, n, n)
	r.A[1] = make([]byte, n, n)

	return r
}

// constructor. NewAtomicFixedSizeRingBuf will allocate internally
// two buffers of size maxViewInBytes.

constructor . NewAtomicFixedSizeRingBuf will allocate internally two buffers of size maxViewInBytes .


func (b *AtomicFixedSizeRingBuf) Reset() {
	b.tex.Lock()
	defer b.tex.Unlock()

	b.Beg = 0
	b.readable = 0
	b.Use = 0
}

func ( b * AtomicFixedSizeRingBuf ) Reset ( ) { b . tex . Lock ( ) defer b . tex . Unlock ( ) b . Beg = 0 b . readable = 0 b . Use = 0 }

// Reset quickly forgets any data stored in the ring buffer. The
// data is still there, but the ring buffer will ignore it and
// overwrite those buffers as new data comes in.

Reset quickly forgets any data stored in the ring buffer . The data is still there but the ring buffer will ignore it and overwrite those buffers as new data comes in .


func NewPointerRingBuf(sliceN int) *PointerRingBuf {
	n := sliceN
	r := &PointerRingBuf{
		N:        n,
		Beg:      0,
		Readable: 0,
	}
	r.A = make([]interface{}, n, n)

	return r
}

func NewPointerRingBuf ( sliceN int ) * PointerRingBuf { n := sliceN r := & PointerRingBuf { N : n , Beg : 0 , Readable : 0 , } r . A = make ( [ ] interface { } , n , n ) return r }

// constructor. NewPointerRingBuf will allocate internally
// a slice of size sliceN

constructor . NewPointerRingBuf will allocate internally a slice of size sliceN


func (b *PointerRingBuf) TwoContig() (first []interface{}, second []interface{}) {

	extent := b.Beg + b.Readable
	if extent <= b.N {
		// we fit contiguously in this buffer without wrapping to the other.
		// Let second stay an empty slice.
		return b.A[b.Beg:(b.Beg + b.Readable)], second
	}

	return b.A[b.Beg:b.N], b.A[0:(extent % b.N)]
}

// TwoContig returns all readable pointers, but in two separate slices,
// to avoid copying. The two slices are from the same buffer, but
// are not contiguous. Either or both may be empty slices.

TwoContig returns all readable pointers but in two separate slices to avoid copying . The two slices are from the same buffer but are not contiguous . Either or both may be empty slices .


func (r *Reader) NumPage() int {
	return int(r.Trailer().Key("Root").Key("Pages").Key("Count").Int64())
}

func ( r * Reader ) NumPage ( ) int { return int ( r . Trailer ( ) . Key ( " " ) . Key ( " " ) . Key ( " " ) . Int64 ( ) ) }

// NumPage returns the number of pages in the PDF file.

NumPage returns the number of pages in the PDF file .


func (p Page) Font(name string) Font {
	return Font{p.Resources().Key("Font").Key(name)}
}

func ( p Page ) Font ( name string ) Font { return Font { p . Resources ( ) . Key ( " " ) . Key ( name ) } }

// Font returns the font with the given name associated with the page.

Font returns the font with the given name associated with the page .


func (f Font) Width(code int) float64 {
	first := f.FirstChar()
	last := f.LastChar()
	if code < first || last < code {
		return 0
	}
	return f.V.Key("Widths").Index(code - first).Float64()
}

func ( f Font ) Width ( code int ) float64 { first := f . FirstChar ( ) last := f . LastChar ( ) if code < first || last < code { return 0 } return f . V . Key ( " " ) . Index ( code - first ) . Float64 ( ) }

// Width returns the width of the given code point.

Width returns the width of the given code point .


func (f Font) Encoder() TextEncoding {
	enc := f.V.Key("Encoding")
	switch enc.Kind() {
	case Name:
		switch enc.Name() {
		case "WinAnsiEncoding":
			return &byteEncoder{&winAnsiEncoding}
		case "MacRomanEncoding":
			return &byteEncoder{&macRomanEncoding}
		case "Identity-H":
			// TODO: Should be big-endian UCS-2 decoder
			return &nopEncoder{}
		default:
			println("unknown encoding", enc.Name())
			return &nopEncoder{}
		}
	case Dict:
		return &dictEncoder{enc.Key("Differences")}
	case Null:
		// ok, try ToUnicode
	default:
		println("unexpected encoding", enc.String())
		return &nopEncoder{}
	}

	toUnicode := f.V.Key("ToUnicode")
	if toUnicode.Kind() == Dict {
		m := readCmap(toUnicode)
		if m == nil {
			return &nopEncoder{}
		}
		return m
	}

	return &byteEncoder{&pdfDocEncoding}
}

// Encoder returns the encoding between font code point sequences and UTF-8.

Encoder returns the encoding between font code point sequences and UTF - 8 .


func Interpret(strm Value, do func(stk *Stack, op string)) {
	rd := strm.Reader()
	b := newBuffer(rd, 0)
	b.allowEOF = true
	b.allowObjptr = false
	b.allowStream = false
	var stk Stack
	var dicts []dict
Reading:
	for {
		tok := b.readToken()
		if tok == io.EOF {
			break
		}
		if kw, ok := tok.(keyword); ok {
			switch kw {
			case "null", "[", "]", "<<", ">>":
				break
			default:
				for i := len(dicts) - 1; i >= 0; i-- {
					if v, ok := dicts[i][name(kw)]; ok {
						stk.Push(Value{nil, objptr{}, v})
						continue Reading
					}
				}
				do(&stk, string(kw))
				continue
			case "dict":
				stk.Pop()
				stk.Push(Value{nil, objptr{}, make(dict)})
				continue
			case "currentdict":
				if len(dicts) == 0 {
					panic("no current dictionary")
				}
				stk.Push(Value{nil, objptr{}, dicts[len(dicts)-1]})
				continue
			case "begin":
				d := stk.Pop()
				if d.Kind() != Dict {
					panic("cannot begin non-dict")
				}
				dicts = append(dicts, d.data.(dict))
				continue
			case "end":
				if len(dicts) <= 0 {
					panic("mismatched begin/end")
				}
				dicts = dicts[:len(dicts)-1]
				continue
			case "def":
				if len(dicts) <= 0 {
					panic("def without open dict")
				}
				val := stk.Pop()
				key, ok := stk.Pop().data.(name)
				if !ok {
					panic("def of non-name")
				}
				dicts[len(dicts)-1][key] = val.data
				continue
			case "pop":
				stk.Pop()
				continue
			}
		}
		b.unreadToken(tok)
		obj := b.readObject()
		stk.Push(Value{nil, objptr{}, obj})
	}
}

func Interpret ( strm Value , do func ( stk * Stack , op string ) ) { rd := strm . Reader ( ) b := newBuffer ( rd , 0 ) b . allowEOF = true b . allowObjptr = false b . allowStream = false var stk Stack var dicts [ ] dict Reading : for { tok := b . readToken ( ) if tok == io . EOF { break } if kw , ok := tok . ( keyword ) ; ok { switch kw { case " " , " " , " " , " " , " " : break default : for i := len ( dicts ) - 1 ; i >= 0 ; i -- { if v , ok := dicts [ i ] [ name ( kw ) ] ; ok { stk . Push ( Value { nil , objptr { } , v } ) continue Reading } } do ( & stk , string ( kw ) ) continue case " " : stk . Pop ( ) stk . Push ( Value { nil , objptr { } , make ( dict ) } ) continue case " " : if len ( dicts ) == 0 { panic ( " " ) } stk . Push ( Value { nil , objptr { } , dicts [ len ( dicts ) - 1 ] } ) continue case " " : d := stk . Pop ( ) if d . Kind ( ) != Dict { panic ( " " ) } dicts = append ( dicts , d . data . ( dict ) ) continue case " " : if len ( dicts ) <= 0 { panic ( " " ) } dicts = dicts [ : len ( dicts ) - 1 ] continue case " " : if len ( dicts ) <= 0 { panic ( " " ) } val := stk . Pop ( ) key , ok := stk . Pop ( ) . data . ( name ) if ! ok { panic ( " " ) } dicts [ len ( dicts ) - 1 ] [ key ] = val . data continue case " " : stk . Pop ( ) continue } } b . unreadToken ( tok ) obj := b . readObject ( ) stk . Push ( Value { nil , objptr { } , obj } ) } }

// Interpret interprets the content in a stream as a basic PostScript program,
// pushing values onto a stack and then calling the do function to execute
// operators. The do function may push or pop values from the stack as needed
// to implement op.
//
// Interpret handles the operators "dict", "currentdict", "begin", "end", "def", and "pop" itself.
//
// Interpret is not a full-blown PostScript interpreter. Its job is to handle the
// very limited PostScript found in certain supporting file formats embedded
// in PDF files, such as cmap files that describe the mapping from font code
// points to Unicode code points.
//
// There is no support for executable blocks, among other limitations.
//

Interpret interprets the content in a stream as a basic PostScript program pushing values onto a stack and then calling the do function to execute operators . The do function may push or pop values from the stack as needed to implement op . Interpret handles the operators dict currentdict begin end def and pop itself . Interpret is not a full - blown PostScript interpreter . Its job is to handle the very limited PostScript found in certain supporting file formats embedded in PDF files such as cmap files that describe the mapping from font code points to Unicode code points . There is no support for executable blocks among other limitations .


func Open(file string) (*Reader, error) {
	// TODO: Deal with closing file.
	f, err := os.Open(file)
	if err != nil {
		return nil, err
	}
	fi, err := f.Stat()
	if err != nil {
		f.Close()
		return nil, err
	}
	return NewReader(f, fi.Size())
}

func
Open
(
file
string
)
(
*
Reader
,
error
)
{
// TODO: Deal with closing file.
f
,
err
:=
os
.
Open
(
file
)
if
err
!=
{
return
,
err
}
fi
,
err
:=
f
.
Stat
(
)
if
err
!=
{
f
.
Close
(
)
return
,
err
}
return
NewReader
(
f
,
fi
.
Size
(
)
)
}


func NewReader(f io.ReaderAt, size int64) (*Reader, error) {
	return NewReaderEncrypted(f, size, nil)
}

func NewReader ( f io . ReaderAt , size int64 ) ( * Reader , error ) { return NewReaderEncrypted ( f , size , nil ) }

// NewReader opens a file for reading, using the data in f with the given total size.

NewReader opens a file for reading using the data in f with the given total size .


func NewReaderEncrypted(f io.ReaderAt, size int64, pw func() string) (*Reader, error) {
	buf := make([]byte, 10)
	f.ReadAt(buf, 0)
	if !bytes.HasPrefix(buf, []byte("%PDF-1.")) || buf[7] < '0' || buf[7] > '7' || buf[8] != '\r' && buf[8] != '\n' {
		return nil, fmt.Errorf("not a PDF file: invalid header")
	}
	end := size
	const endChunk = 100
	buf = make([]byte, endChunk)
	f.ReadAt(buf, end-endChunk)
	for len(buf) > 0 && buf[len(buf)-1] == '\n' || buf[len(buf)-1] == '\r' {
		buf = buf[:len(buf)-1]
	}
	buf = bytes.TrimRight(buf, "\r\n\t ")
	if !bytes.HasSuffix(buf, []byte("%%EOF")) {
		return nil, fmt.Errorf("not a PDF file: missing %%%%EOF")
	}
	i := findLastLine(buf, "startxref")
	if i < 0 {
		return nil, fmt.Errorf("malformed PDF file: missing final startxref")
	}

	r := &Reader{
		f:   f,
		end: end,
	}
	pos := end - endChunk + int64(i)
	b := newBuffer(io.NewSectionReader(f, pos, end-pos), pos)
	if b.readToken() != keyword("startxref") {
		return nil, fmt.Errorf("malformed PDF file: missing startxref")
	}
	startxref, ok := b.readToken().(int64)
	if !ok {
		return nil, fmt.Errorf("malformed PDF file: startxref not followed by integer")
	}
	b = newBuffer(io.NewSectionReader(r.f, startxref, r.end-startxref), startxref)
	xref, trailerptr, trailer, err := readXref(r, b)
	if err != nil {
		return nil, err
	}
	r.xref = xref
	r.trailer = trailer
	r.trailerptr = trailerptr
	if trailer["Encrypt"] == nil {
		return r, nil
	}
	err = r.initEncrypt("")
	if err == nil {
		return r, nil
	}
	if pw == nil || err != ErrInvalidPassword {
		return nil, err
	}
	for {
		next := pw()
		if next == "" {
			break
		}
		if r.initEncrypt(next) == nil {
			return r, nil
		}
	}
	return nil, err
}

func NewReaderEncrypted ( f io . ReaderAt , size int64 , pw func ( ) string ) ( * Reader , error ) { buf := make ( [ ] byte , 10 ) f . ReadAt ( buf , 0 ) if ! bytes . HasPrefix ( buf , [ ] byte ( " " ) ) || buf [ 7 ] < '0' || buf [ 7 ] > '7' || buf [ 8 ] != '\r' && buf [ 8 ] != '\n' { return nil , fmt . Errorf ( " " ) } end := size const endChunk = 100 buf = make ( [ ] byte , endChunk ) f . ReadAt ( buf , end - endChunk ) for len ( buf ) > 0 && buf [ len ( buf ) - 1 ] == '\n' || buf [ len ( buf ) - 1 ] == '\r' { buf = buf [ : len ( buf ) - 1 ] } buf = bytes . TrimRight ( buf , " \r \n \t " ) if ! bytes . HasSuffix ( buf , [ ] byte ( " " ) ) { return nil , fmt . Errorf ( " " ) } i := findLastLine ( buf , " " ) if i < 0 { return nil , fmt . Errorf ( " " ) } r := & Reader { f : f , end : end , } pos := end - endChunk + int64 ( i ) b := newBuffer ( io . NewSectionReader ( f , pos , end - pos ) , pos ) if b . readToken ( ) != keyword ( " " ) { return nil , fmt . Errorf ( " " ) } startxref , ok := b . readToken ( ) . ( int64 ) if ! ok { return nil , fmt . Errorf ( " " ) } b = newBuffer ( io . NewSectionReader ( r . f , startxref , r . end - startxref ) , startxref ) xref , trailerptr , trailer , err := readXref ( r , b ) if err != nil { return nil , err } r . xref = xref r . trailer = trailer r . trailerptr = trailerptr if trailer [ " " ] == nil { return r , nil } err = r . initEncrypt ( " " ) if err == nil { return r , nil } if pw == nil || err != ErrInvalidPassword { return nil , err } for { next := pw ( ) if next == " " { break } if r . initEncrypt ( next ) == nil { return r , nil } } return nil , err }

// NewReaderEncrypted opens a file for reading, using the data in f with the given total size.
// If the PDF is encrypted, NewReaderEncrypted calls pw repeatedly to obtain passwords
// to try. If pw returns the empty string, NewReaderEncrypted stops trying to decrypt
// the file and returns an error.

NewReaderEncrypted opens a file for reading using the data in f with the given total size . If the PDF is encrypted NewReaderEncrypted calls pw repeatedly to obtain passwords to try . If pw returns the empty string NewReaderEncrypted stops trying to decrypt the file and returns an error .


func (r *Reader) Trailer() Value {
	return Value{r, r.trailerptr, r.trailer}
}

func ( r * Reader ) Trailer ( ) Value { return Value { r , r . trailerptr , r . trailer } }

// Trailer returns the file's Trailer value.

Trailer returns the file s Trailer value .


func (v Value) Kind() ValueKind {
	switch v.data.(type) {
	default:
		return Null
	case bool:
		return Bool
	case int64:
		return Integer
	case float64:
		return Real
	case string:
		return String
	case name:
		return Name
	case dict:
		return Dict
	case array:
		return Array
	case stream:
		return Stream
	}
}

func ( v Value ) Kind ( ) ValueKind { switch v . data . ( type ) { default : return Null case bool : return Bool case int64 : return Integer case float64 : return Real case string : return String case name : return Name case dict : return Dict case array : return Array case stream : return Stream } }

// Kind reports the kind of value underlying v.

Kind reports the kind of value underlying v .


func newBuffer(r io.Reader, offset int64) *buffer {
	return &buffer{
		r:           r,
		offset:      offset,
		buf:         make([]byte, 0, 4096),
		allowObjptr: true,
		allowStream: true,
	}
}

func newBuffer ( r io . Reader , offset int64 ) * buffer { return & buffer { r : r , offset : offset , buf : make ( [ ] byte , 0 , 4096 ) , allowObjptr : true , allowStream : true , } }

// newBuffer returns a new buffer reading from r at the given offset.

newBuffer returns a new buffer reading from r at the given offset .


func (r *ResultSet) Paginate(perPage, page int) (*PaginationInfo, error) {

	info := new(PaginationInfo)

	// Get count on a different session to avoid blocking
	sess := r.Collection.Connection.Session.Copy()

	count, err := sess.DB(r.Collection.Database).C(r.Collection.Name).Find(r.Params).Count()
	sess.Close()

	if err != nil {
		return info, err
	}

	// Calculate how many pages
	totalPages := int(math.Ceil(float64(count) / float64(perPage)))

	if page < 1 {
		page = 1
	} else if page > totalPages {
		page = totalPages
	}

	skip := (page - 1) * perPage

	r.Query.Skip(skip).Limit(perPage)

	info.TotalPages = totalPages
	info.PerPage = perPage
	info.Current = page
	info.TotalRecords = count

	if info.Current < info.TotalPages {
		info.RecordsOnPage = info.PerPage
	} else {

		info.RecordsOnPage = int(math.Mod(float64(count), float64(perPage)))

		if info.RecordsOnPage == 0 && count > 0 {
			info.RecordsOnPage = perPage
		}

	}

	return info, nil
}

// Set skip + limit on the current query and generates a PaginationInfo struct with info for your front end


func CascadeDelete(collection *Collection, doc interface{}) {
	// Find out which properties to cascade
	if conv, ok := doc.(interface {
		GetCascade(*Collection) []*CascadeConfig
	}); ok {
		toCascade := conv.GetCascade(collection)

		// Get the ID

		for _, conf := range toCascade {
			if len(conf.ReferenceQuery) == 0 {
				id, err := reflections.GetField(doc, "Id")
				if err != nil {
					panic(err)
				}
				conf.ReferenceQuery = []*ReferenceField{&ReferenceField{"_id", id}}
			}

			cascadeDeleteWithConfig(conf)

		}

	}
}

func
CascadeDelete
(
collection
*
Collection
,
doc
interface
{
}
)
{
// Find out which properties to cascade
if
conv
,
ok
:=
doc
.
(
interface
{
GetCascade
(
*
Collection
)
[
]
*
CascadeConfig
}
)
;
ok
{
toCascade
:=
conv
.
GetCascade
(
collection
)
// Get the ID
for
_
,
conf
:=
range
toCascade
{
if
len
(
conf
.
ReferenceQuery
)
==
0
{
id
,
err
:=
reflections
.
GetField
(
doc
,
"
"
)
if
err
!=
{
panic
(
err
)
}
conf
.
ReferenceQuery
=
[
]
*
ReferenceField
{
&
ReferenceField
{
"
"
,
id
}
}
}
cascadeDeleteWithConfig
(
conf
)
}
}
}

// Deletes references to a document from its related documents


func cascadeDeleteWithConfig(conf *CascadeConfig) (*mgo.ChangeInfo, error) {

	switch conf.RelType {
	case REL_ONE:
		update := map[string]map[string]interface{}{
			"$set": map[string]interface{}{},
		}

		if len(conf.ThroughProp) > 0 {
			update["$set"][conf.ThroughProp] = nil
		} else {
			for _, p := range conf.Properties {
				update["$set"][p] = nil
			}
		}

		return conf.Collection.Collection().UpdateAll(conf.Query, update)
	case REL_MANY:
		update := map[string]map[string]interface{}{
			"$pull": map[string]interface{}{},
		}

		q := bson.M{}
		for _, f := range conf.ReferenceQuery {
			q[f.BsonName] = f.Value
		}
		update["$pull"][conf.ThroughProp] = q
		return conf.Collection.Collection().UpdateAll(conf.Query, update)
	}

	return &mgo.ChangeInfo{}, errors.New("Invalid relation type")
}

func cascadeDeleteWithConfig ( conf * CascadeConfig ) ( * mgo . ChangeInfo , error ) { switch conf . RelType { case REL_ONE : update := map [ string ] map [ string ] interface { } { " " : map [ string ] interface { } { } , } if len ( conf . ThroughProp ) > 0 { update [ " " ] [ conf . ThroughProp ] = nil } else { for _ , p := range conf . Properties { update [ " " ] [ p ] = nil } } return conf . Collection . Collection ( ) . UpdateAll ( conf . Query , update ) case REL_MANY : update := map [ string ] map [ string ] interface { } { " " : map [ string ] interface { } { } , } q := bson . M { } for _ , f := range conf . ReferenceQuery { q [ f . BsonName ] = f . Value } update [ " " ] [ conf . ThroughProp ] = q return conf . Collection . Collection ( ) . UpdateAll ( conf . Query , update ) } return & mgo . ChangeInfo { } , errors . New ( " " ) }

// Runs a cascaded delete operation with one configuration


func cascadeSaveWithConfig(conf *CascadeConfig, doc Document) (*mgo.ChangeInfo, error) {
	// Create a new map with just the props to cascade

	data := conf.Data

	switch conf.RelType {
	case REL_ONE:
		if len(conf.OldQuery) > 0 {

			update1 := map[string]map[string]interface{}{
				"$set": map[string]interface{}{},
			}

			if len(conf.ThroughProp) > 0 {
				update1["$set"][conf.ThroughProp] = nil
			} else {
				for _, p := range conf.Properties {
					update1["$set"][p] = nil
				}
			}

			ret, err := conf.Collection.Collection().UpdateAll(conf.OldQuery, update1)

			if conf.RemoveOnly {
				return ret, err
			}
		}

		update := make(map[string]interface{})

		if len(conf.ThroughProp) > 0 {
			m := bson.M{}
			m[conf.ThroughProp] = data
			update["$set"] = m
		} else {
			update["$set"] = data
		}

		// Just update
		return conf.Collection.Collection().UpdateAll(conf.Query, update)
	case REL_MANY:

		update1 := map[string]map[string]interface{}{
			"$pull": map[string]interface{}{},
		}

		q := bson.M{}
		for _, f := range conf.ReferenceQuery {
			q[f.BsonName] = f.Value
		}
		update1["$pull"][conf.ThroughProp] = q

		if len(conf.OldQuery) > 0 {
			ret, err := conf.Collection.Collection().UpdateAll(conf.OldQuery, update1)
			if conf.RemoveOnly {
				return ret, err
			}
		}

		// Remove self from current relations, so we can replace it
		conf.Collection.Collection().UpdateAll(conf.Query, update1)

		update2 := map[string]map[string]interface{}{
			"$push": map[string]interface{}{},
		}

		update2["$push"][conf.ThroughProp] = data
		return conf.Collection.Collection().UpdateAll(conf.Query, update2)

	}

	return &mgo.ChangeInfo{}, errors.New("Invalid relation type")

}

// Runs a cascaded save operation with one configuration


func MapFromCascadeProperties(properties []string, doc Document) map[string]interface{} {
	data := make(map[string]interface{})

	for _, prop := range properties {
		split := strings.Split(prop, ".")

		if len(split) == 1 {
			data[prop], _ = dotaccess.Get(doc, prop)
		} else {
			actualProp := split[len(split)-1]
			split := append([]string{}, split[:len(split)-1]...)
			curData := data

			for _, s := range split {
				if _, ok := curData[s]; ok {
					if mapped, ok := curData[s].(map[string]interface{}); ok {
						curData = mapped
					} else {
						panic("Cannot access non-map property via dot notation")
					}

				} else {
					curData[s] = make(map[string]interface{})
					if mapped, ok := curData[s].(map[string]interface{}); ok {
						curData = mapped
					} else {
						panic("Cannot access non-map property via dot notation")
					}
				}
			}

			val, _ := dotaccess.Get(doc, prop)
			// if bsonId, ok := val.(bson.ObjectId); ok {
			// 	if !bsonId.Valid() {
			// 		curData[actualProp] = ""
			// 		continue
			// 	}
			// }
			curData[actualProp] = val
		}
	}

	return data
}

// If you need to, you can use this to construct the data map that will be cascaded down to
// related documents. Doing this is not recommended unless the cascaded fields are dynamic.

If you need to you can use this to construct the data map that will be cascaded down to related documents . Doing this is not recommended unless the cascaded fields are dynamic .


func (m *Connection) Connect() (err error) {
	defer func() {
		if r := recover(); r != nil {
			// panic(r)
			// return
			if e, ok := r.(error); ok {
				err = e
			} else if e, ok := r.(string); ok {
				err = errors.New(e)
			} else {
				err = errors.New(fmt.Sprint(r))
			}

		}
	}()

	if m.Config.DialInfo == nil {
		if m.Config.DialInfo, err = mgo.ParseURL(m.Config.ConnectionString); err != nil {
			panic(fmt.Sprintf("cannot parse given URI %s due to error: %s", m.Config.ConnectionString, err.Error()))
		}
	}

	session, err := mgo.DialWithInfo(m.Config.DialInfo)
	if err != nil {
		return err
	}

	m.Session = session

	m.Session.SetMode(mgo.Monotonic, true)

	return nil
}

func
(
m
*
Connection
)
Connect
(
)
(
err
error
)
{
defer
func
(
)
{
if
r
:=
recover
(
)
;
r
!=
{
// panic(r)
// return
if
e
,
ok
:=
r
.
(
error
)
;
ok
{
err
=
e
}
else
if
e
,
ok
:=
r
.
(
string
)
;
ok
{
err
=
errors
.
New
(
e
)
}
else
{
err
=
errors
.
New
(
fmt
.
Sprint
(
r
)
)
}
}
}
(
)
if
m
.
Config
.
DialInfo
==
{
if
m
.
Config
.
DialInfo
,
err
=
mgo
.
ParseURL
(
m
.
Config
.
ConnectionString
)
;
err
!=
{
panic
(
fmt
.
Sprintf
(
"
"
,
m
.
Config
.
ConnectionString
,
err
.
Error
(
)
)
)
}
}
session
,
err
:=
mgo
.
DialWithInfo
(
m
.
Config
.
DialInfo
)
if
err
!=
{
return
err
}
m
.
Session
=
session
m
.
Session
.
SetMode
(
mgo
.
Monotonic
,
true
)
return
}

// Connect to the database using the provided config


func lowerInitial(str string) string {
	for i, v := range str {
		return string(unicode.ToLower(v)) + str[i+1:]
	}
	return ""
}

func lowerInitial ( str string ) string { for i , v := range str { return string ( unicode . ToLower ( v ) ) + str [ i + 1 : ] } return " " }


func (c *Collection) Find(query interface{}) *ResultSet {
	col := c.Collection()

	// Count for testing
	q := col.Find(query)

	resultset := new(ResultSet)

	resultset.Query = q
	resultset.Params = query
	resultset.Collection = c

	return resultset
}

func
(
c
*
Collection
)
Find
(
query
interface
{
}
)
*
ResultSet
{
col
:=
c
.
Collection
(
)
// Count for testing
q
:=
col
.
Find
(
query
)
resultset
:=
new
(
ResultSet
)
resultset
.
Query
=
q
resultset
.
Params
=
query
resultset
.
Collection
=
c
return
resultset
}

// This doesn't actually do any DB interaction, it just creates the result set so we can
// start looping through on the iterator

This doesn t actually do any DB interaction it just creates the result set so we can start looping through on the iterator


func (c *Client) RawInformationQuery(from, to, id, iqType, requestNamespace, body string) (string, error) {
	const xmlIQ = "<iq from='%s' to='%s' id='%s' type='%s'><query xmlns='%s'>%s</query></iq>"
	_, err := fmt.Fprintf(c.conn, xmlIQ, xmlEscape(from), xmlEscape(to), id, iqType, requestNamespace, body)
	return id, err
}

func ( c * Client ) RawInformationQuery ( from , to , id , iqType , requestNamespace , body string ) ( string , error ) { const xmlIQ = " " _ , err := fmt . Fprintf ( c . conn , xmlIQ , xmlEscape ( from ) , xmlEscape ( to ) , id , iqType , requestNamespace , body ) return id , err }

// RawInformationQuery sends an information query request to the server.

RawInformationQuery sends an information query request to the server .


func (o Options) NewClient() (*Client, error) {
	host := o.Host
	c, err := connect(host, o.User, o.Password)
	if err != nil {
		return nil, err
	}

	if strings.LastIndex(o.Host, ":") > 0 {
		host = host[:strings.LastIndex(o.Host, ":")]
	}

	client := new(Client)
	if o.NoTLS {
		client.conn = c
	} else {
		var tlsconn *tls.Conn
		if o.TLSConfig != nil {
			tlsconn = tls.Client(c, o.TLSConfig)
		} else {
			DefaultConfig.ServerName = host
			newconfig := DefaultConfig
			newconfig.ServerName = host
			tlsconn = tls.Client(c, &newconfig)
		}
		if err = tlsconn.Handshake(); err != nil {
			return nil, err
		}
		insecureSkipVerify := DefaultConfig.InsecureSkipVerify
		if o.TLSConfig != nil {
			insecureSkipVerify = o.TLSConfig.InsecureSkipVerify
		}
		if !insecureSkipVerify {
			if err = tlsconn.VerifyHostname(host); err != nil {
				return nil, err
			}
		}
		client.conn = tlsconn
	}

	if err := client.init(&o); err != nil {
		client.Close()
		return nil, err
	}

	return client, nil
}

func ( o Options ) NewClient ( ) ( * Client , error ) { host := o . Host c , err := connect ( host , o . User , o . Password ) if err != nil { return nil , err } if strings . LastIndex ( o . Host , " " ) > 0 { host = host [ : strings . LastIndex ( o . Host , " " ) ] } client := new ( Client ) if o . NoTLS { client . conn = c } else { var tlsconn * tls . Conn if o . TLSConfig != nil { tlsconn = tls . Client ( c , o . TLSConfig ) } else { DefaultConfig . ServerName = host newconfig := DefaultConfig newconfig . ServerName = host tlsconn = tls . Client ( c , & newconfig ) } if err = tlsconn . Handshake ( ) ; err != nil { return nil , err } insecureSkipVerify := DefaultConfig . InsecureSkipVerify if o . TLSConfig != nil { insecureSkipVerify = o . TLSConfig . InsecureSkipVerify } if ! insecureSkipVerify { if err = tlsconn . VerifyHostname ( host ) ; err != nil { return nil , err } } client . conn = tlsconn } if err := client . init ( & o ) ; err != nil { client . Close ( ) return nil , err } return client , nil }

// NewClient establishes a new Client connection based on a set of Options.

NewClient establishes a new Client connection based on a set of Options .


func (c *Client) Close() error {
	if c.conn != (*tls.Conn)(nil) {
		return c.conn.Close()
	}
	return nil
}

func ( c * Client ) Close ( ) error { if c . conn != ( * tls . Conn ) ( nil ) { return c . conn . Close ( ) } return nil }


func (c *Client) startTLSIfRequired(f *streamFeatures, o *Options, domain string) (*streamFeatures, error) {
	// whether we start tls is a matter of opinion: the server's and the user's.
	switch {
	case f.StartTLS == nil:
		// the server does not support STARTTLS
		return f, nil
	case !o.StartTLS && f.StartTLS.Required == nil:
		return f, nil
	case f.StartTLS.Required != nil:
		// the server requires STARTTLS.
	case !o.StartTLS:
		// the user wants STARTTLS and the server supports it.
	}
	var err error

	fmt.Fprintf(c.conn, "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>\n")
	var k tlsProceed
	if err = c.p.DecodeElement(&k, nil); err != nil {
		return f, errors.New("unmarshal <proceed>: " + err.Error())
	}

	tc := o.TLSConfig
	if tc == nil {
		tc = new(tls.Config)
		*tc = DefaultConfig
		//TODO(scott): we should consider using the server's address or reverse lookup
		tc.ServerName = domain
	}
	t := tls.Client(c.conn, tc)

	if err = t.Handshake(); err != nil {
		return f, errors.New("starttls handshake: " + err.Error())
	}
	c.conn = t

	// restart our declaration of XMPP stream intentions.
	tf, err := c.startStream(o, domain)
	if err != nil {
		return f, err
	}
	return tf, nil
}

// startTlsIfRequired examines the server's stream features and, if STARTTLS is required or supported, performs the TLS handshake.
// f will be updated if the handshake completes, as the new stream's features are typically different from the original.

startTlsIfRequired examines the server s stream features and if STARTTLS is required or supported performs the TLS handshake . f will be updated if the handshake completes as the new stream s features are typically different from the original .


func (c *Client) startStream(o *Options, domain string) (*streamFeatures, error) {
	if o.Debug {
		c.p = xml.NewDecoder(tee{c.conn, DebugWriter})
	} else {
		c.p = xml.NewDecoder(c.conn)
	}

	_, err := fmt.Fprintf(c.conn, "<?xml version='1.0'?>\n"+
		"<stream:stream to='%s' xmlns='%s'\n"+
		" xmlns:stream='%s' version='1.0'>\n",
		xmlEscape(domain), nsClient, nsStream)
	if err != nil {
		return nil, err
	}

	// We expect the server to start a <stream>.
	se, err := nextStart(c.p)
	if err != nil {
		return nil, err
	}
	if se.Name.Space != nsStream || se.Name.Local != "stream" {
		return nil, fmt.Errorf("expected <stream> but got <%v> in %v", se.Name.Local, se.Name.Space)
	}

	// Now we're in the stream and can use Unmarshal.
	// Next message should be <features> to tell us authentication options.
	// See section 4.6 in RFC 3920.
	f := new(streamFeatures)
	if err = c.p.DecodeElement(f, nil); err != nil {
		return f, errors.New("unmarshal <features>: " + err.Error())
	}
	return f, nil
}

// startStream will start a new XML decoder for the connection, signal the start of a stream to the server and verify that the server has
// also started the stream; if o.Debug is true, startStream will tee decoded XML data to stderr.  The features advertised by the server
// will be returned.

startStream will start a new XML decoder for the connection signal the start of a stream to the server and verify that the server has also started the stream ; if o . Debug is true startStream will tee decoded XML data to stderr . The features advertised by the server will be returned .


func (c *Client) IsEncrypted() bool {
	_, ok := c.conn.(*tls.Conn)
	return ok
}

func ( c * Client ) IsEncrypted ( ) bool { _ , ok := c . conn . ( * tls . Conn ) return ok }

// IsEncrypted will return true if the client is connected using a TLS transport, either because it used.
// TLS to connect from the outset, or because it successfully used STARTTLS to promote a TCP connection to TLS.

IsEncrypted will return true if the client is connected using a TLS transport either because it used . TLS to connect from the outset or because it successfully used STARTTLS to promote a TCP connection to TLS .


func (c *Client) Recv() (stanza interface{}, err error) {
	for {
		_, val, err := next(c.p)
		if err != nil {
			return Chat{}, err
		}
		switch v := val.(type) {
		case *clientMessage:
			stamp, _ := time.Parse(
				"2006-01-02T15:04:05Z",
				v.Delay.Stamp,
			)
			chat := Chat{
				Remote:    v.From,
				Type:      v.Type,
				Text:      v.Body,
				Subject:   v.Subject,
				Thread:    v.Thread,
				Other:     v.OtherStrings(),
				OtherElem: v.Other,
				Stamp:     stamp,
			}
			return chat, nil
		case *clientQuery:
			var r Roster
			for _, item := range v.Item {
				r = append(r, Contact{item.Jid, item.Name, item.Group})
			}
			return Chat{Type: "roster", Roster: r}, nil
		case *clientPresence:
			return Presence{v.From, v.To, v.Type, v.Show, v.Status}, nil
		case *clientIQ:
			// TODO check more strictly
			if bytes.Equal(bytes.TrimSpace(v.Query), []byte(`<ping xmlns='urn:xmpp:ping'/>`)) || bytes.Equal(bytes.TrimSpace(v.Query), []byte(`<ping xmlns="urn:xmpp:ping"/>`)) {
				err := c.SendResultPing(v.ID, v.From)
				if err != nil {
					return Chat{}, err
				}
			}
			return IQ{ID: v.ID, From: v.From, To: v.To, Type: v.Type, Query: v.Query}, nil
		}
	}
}

func
(
c
*
Client
)
Recv
(
)
(
stanza
interface
{
}
,
err
error
)
{
for
{
_
,
val
,
err
:=
next
(
c
.
p
)
if
err
!=
{
return
Chat
{
}
,
err
}
switch
v
:=
val
.
(
type
)
{
case
*
clientMessage
:
stamp
,
_
:=
time
.
Parse
(
"
"
,
v
.
Delay
.
Stamp
,
)
chat
:=
Chat
{
Remote
:
v
.
From
,
Type
:
v
.
Type
,
Text
:
v
.
Body
,
Subject
:
v
.
Subject
,
Thread
:
v
.
Thread
,
Other
:
v
.
OtherStrings
(
)
,
OtherElem
:
v
.
Other
,
Stamp
:
stamp
,
}
return
chat
,
case
*
clientQuery
:
var
r
Roster
for
_
,
item
:=
range
v
.
Item
{
r
=
append
(
r
,
Contact
{
item
.
Jid
,
item
.
Name
,
item
.
Group
}
)
}
return
Chat
{
Type
:
"
"
,
Roster
:
r
}
,
case
*
clientPresence
:
return
Presence
{
v
.
From
,
v
.
To
,
v
.
Type
,
v
.
Show
,
v
.
Status
}
,
case
*
clientIQ
:
// TODO check more strictly
if
bytes
.
Equal
(
bytes
.
TrimSpace
(
v
.
Query
)
,
[
]
byte
(
`<ping xmlns='urn:xmpp:ping'/>`
)
)
||
bytes
.
Equal
(
bytes
.
TrimSpace
(
v
.
Query
)
,
[
]
byte
(
`<ping xmlns="urn:xmpp:ping"/>`
)
)
{
err
:=
c
.
SendResultPing
(
v
.
ID
,
v
.
From
)
if
err
!=
{
return
Chat
{
}
,
err
}
}
return
IQ
{
ID
:
v
.
ID
,
From
:
v
.
From
,
To
:
v
.
To
,
Type
:
v
.
Type
,
Query
:
v
.
Query
}
,
}
}
}

// Recv waits to receive the next XMPP stanza.
// Return type is either a presence notification or a chat message.

Recv waits to receive the next XMPP stanza . Return type is either a presence notification or a chat message .


func (c *Client) Send(chat Chat) (n int, err error) {
	var subtext = ``
	var thdtext = ``
	if chat.Subject != `` {
		subtext = `<subject>` + xmlEscape(chat.Subject) + `</subject>`
	}
	if chat.Thread != `` {
		thdtext = `<thread>` + xmlEscape(chat.Thread) + `</thread>`
	}

	stanza := "<message to='%s' type='%s' id='%s' xml:lang='en'>" + subtext + "<body>%s</body>" + thdtext + "</message>"

	return fmt.Fprintf(c.conn, stanza,
		xmlEscape(chat.Remote), xmlEscape(chat.Type), cnonce(), xmlEscape(chat.Text))
}

func ( c * Client ) Send ( chat Chat ) ( n int , err error ) { var subtext = `` var thdtext = `` if chat . Subject != `` { subtext = `<subject>` + xmlEscape ( chat . Subject ) + `</subject>` } if chat . Thread != `` { thdtext = `<thread>` + xmlEscape ( chat . Thread ) + `</thread>` } stanza := " " + subtext + " " + thdtext + " " return fmt . Fprintf ( c . conn , stanza , xmlEscape ( chat . Remote ) , xmlEscape ( chat . Type ) , cnonce ( ) , xmlEscape ( chat . Text ) ) }

// Send sends the message wrapped inside an XMPP message stanza body.

Send sends the message wrapped inside an XMPP message stanza body .


func (c *Client) SendOrg(org string) (n int, err error) {
	return fmt.Fprint(c.conn, org)
}

func ( c * Client ) SendOrg ( org string ) ( n int , err error ) { return fmt . Fprint ( c . conn , org ) }

// SendOrg sends the original text without being wrapped in an XMPP message stanza.

SendOrg sends the original text without being wrapped in an XMPP message stanza .


func (c *Client) SendKeepAlive() (n int, err error) {
	return fmt.Fprintf(c.conn, " ")
}

func ( c * Client ) SendKeepAlive ( ) ( n int , err error ) { return fmt . Fprintf ( c . conn , " " ) }

// SendKeepAlive sends a "whitespace keepalive" as described in chapter 4.6.1 of RFC6120.

SendKeepAlive sends a whitespace keepalive as described in chapter 4 . 6 . 1 of RFC6120 .


func (c *Client) SendHtml(chat Chat) (n int, err error) {
	return fmt.Fprintf(c.conn, "<message to='%s' type='%s' xml:lang='en'>"+
		"%s"+
		"%s</message>",
		xmlEscape(chat.Remote), xmlEscape(chat.Type), xmlEscape(chat.Text), chat.Text)
}

func ( c * Client ) SendHtml ( chat Chat ) ( n int , err error ) { return fmt . Fprintf ( c . conn , " " + " " + " " , xmlEscape ( chat . Remote ) , xmlEscape ( chat . Type ) , xmlEscape ( chat . Text ) , chat . Text ) }

// SendHtml sends the message as HTML as defined by XEP-0071

SendHtml sends the message as HTML as defined by XEP - 0071


func nextStart(p *xml.Decoder) (xml.StartElement, error) {
	for {
		t, err := p.Token()
		if err != nil || t == nil {
			return xml.StartElement{}, err
		}
		switch t := t.(type) {
		case xml.StartElement:
			return t, nil
		}
	}
}

func nextStart ( p * xml . Decoder ) ( xml . StartElement , error ) { for { t , err := p . Token ( ) if err != nil || t == nil { return xml . StartElement { } , err } switch t := t . ( type ) { case xml . StartElement : return t , nil } } }

// Scan XML token stream to find next StartElement.

Scan XML token stream to find next StartElement .


func (c *Client) JoinProtectedMUC(jid, nick string, password string, history_type, history int, history_date *time.Time) (n int, err error) {
	if nick == "" {
		nick = c.jid
	}
	switch history_type {
	case NoHistory:
		return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n"+
			"<x xmlns='%s'>\n"+
			"<password>%s</password>"+
			"</x>\n"+
			"</presence>",
			xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password))
	case CharHistory:
		return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n"+
			"<x xmlns='%s'>\n"+
			"<password>%s</password>\n"+
			"<history maxchars='%d'/></x>\n"+
			"</presence>",
			xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history)
	case StanzaHistory:
		return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n"+
			"<x xmlns='%s'>\n"+
			"<password>%s</password>\n"+
			"<history maxstanzas='%d'/></x>\n"+
			"</presence>",
			xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history)
	case SecondsHistory:
		return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n"+
			"<x xmlns='%s'>\n"+
			"<password>%s</password>\n"+
			"<history seconds='%d'/></x>\n"+
			"</presence>",
			xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history)
	case SinceHistory:
		if history_date != nil {
			return fmt.Fprintf(c.conn, "<presence to='%s/%s'>\n"+
				"<x xmlns='%s'>\n"+
				"<password>%s</password>\n"+
				"<history since='%s'/></x>\n"+
				"</presence>",
				xmlEscape(jid), xmlEscape(nick), nsMUC, xmlEscape(password), history_date.Format(time.RFC3339))
		}
	}
	return 0, errors.New("Unknown history option")
}

func ( c * Client ) JoinProtectedMUC ( jid , nick string , password string , history_type , history int , history_date * time . Time ) ( n int , err error ) { if nick == " " { nick = c . jid } switch history_type { case NoHistory : return fmt . Fprintf ( c . conn , " \n " + " \n " + " " + " \n " + " " , xmlEscape ( jid ) , xmlEscape ( nick ) , nsMUC , xmlEscape ( password ) ) case CharHistory : return fmt . Fprintf ( c . conn , " \n " + " \n " + " \n " + " \n " + " " , xmlEscape ( jid ) , xmlEscape ( nick ) , nsMUC , xmlEscape ( password ) , history ) case StanzaHistory : return fmt . Fprintf ( c . conn , " \n " + " \n " + " \n " + " \n " + " " , xmlEscape ( jid ) , xmlEscape ( nick ) , nsMUC , xmlEscape ( password ) , history ) case SecondsHistory : return fmt . Fprintf ( c . conn , " \n " + " \n " + " \n " + " \n " + " " , xmlEscape ( jid ) , xmlEscape ( nick ) , nsMUC , xmlEscape ( password ) , history ) case SinceHistory : if history_date != nil { return fmt . Fprintf ( c . conn , " \n " + " \n " + " \n " + " \n " + " " , xmlEscape ( jid ) , xmlEscape ( nick ) , nsMUC , xmlEscape ( password ) , history_date . Format ( time . RFC3339 ) ) } } return 0 , errors . New ( " " ) }


func (c *Client) LeaveMUC(jid string) (n int, err error) {
	return fmt.Fprintf(c.conn, "<presence from='%s' to='%s' type='unavailable' />",
		c.jid, xmlEscape(jid))
}

func ( c * Client ) LeaveMUC ( jid string ) ( n int , err error ) { return fmt . Fprintf ( c . conn , " " , c . jid , xmlEscape ( jid ) ) }


func (m *Message) AttachBuffer(filename string, buf []byte, inline bool) error {
	m.Attachments[filename] = &Attachment{
		Filename: filename,
		Data:     buf,
		Inline:   inline,
	}
	return nil
}

func ( m * Message ) AttachBuffer ( filename string , buf [ ] byte , inline bool ) error { m . Attachments [ filename ] = & Attachment { Filename : filename , Data : buf , Inline : inline , } return nil }

// AttachBuffer attaches a binary attachment.

AttachBuffer attaches a binary attachment .


func (m *Message) Attach(file string) error {
	return m.attach(file, false)
}

func ( m * Message ) Attach ( file string ) error { return m . attach ( file , false ) }


func (m *Message) Inline(file string) error {
	return m.attach(file, true)
}

func ( m * Message ) Inline ( file string ) error { return m . attach ( file , true ) }

// Inline includes a file as an inline attachment.

Inline includes a file as an inline attachment .


func (m *Message) AddHeader(key string, value string) Header {
	newHeader := Header{Key: key, Value: value}
	m.Headers = append(m.Headers, newHeader)
	return newHeader
}

func ( m * Message ) AddHeader ( key string , value string ) Header { newHeader := Header { Key : key , Value : value } m . Headers = append ( m . Headers , newHeader ) return newHeader }


func (m *Message) Tolist() []string {
	tolist := m.To

	for _, cc := range m.Cc {
		tolist = append(tolist, cc)
	}

	for _, bcc := range m.Bcc {
		tolist = append(tolist, bcc)
	}

	return tolist
}

func ( m * Message ) Tolist ( ) [ ] string { tolist := m . To for _ , cc := range m . Cc { tolist = append ( tolist , cc ) } for _ , bcc := range m . Bcc { tolist = append ( tolist , bcc ) } return tolist }

// Tolist returns all the recipients of the email


func (m *Message) Bytes() []byte {
	buf := bytes.NewBuffer(nil)

	buf.WriteString("From: " + m.From.String() + "\r\n")

	t := time.Now()
	buf.WriteString("Date: " + t.Format(time.RFC1123Z) + "\r\n")

	buf.WriteString("To: " + strings.Join(m.To, ",") + "\r\n")
	if len(m.Cc) > 0 {
		buf.WriteString("Cc: " + strings.Join(m.Cc, ",") + "\r\n")
	}

	//fix  Encode
	var coder = base64.StdEncoding
	var subject = "=?UTF-8?B?" + coder.EncodeToString([]byte(m.Subject)) + "?="
	buf.WriteString("Subject: " + subject + "\r\n")

	if len(m.ReplyTo) > 0 {
		buf.WriteString("Reply-To: " + m.ReplyTo + "\r\n")
	}

	buf.WriteString("MIME-Version: 1.0\r\n")

	// Add custom headers
	if len(m.Headers) > 0 {
		for _, header := range m.Headers {
			buf.WriteString(fmt.Sprintf("%s: %s\r\n", header.Key, header.Value))
		}
	}

	boundary := "f46d043c813270fc6b04c2d223da"

	if len(m.Attachments) > 0 {
		buf.WriteString("Content-Type: multipart/mixed; boundary=" + boundary + "\r\n")
		buf.WriteString("\r\n--" + boundary + "\r\n")
	}

	buf.WriteString(fmt.Sprintf("Content-Type: %s; charset=utf-8\r\n\r\n", m.BodyContentType))
	buf.WriteString(m.Body)
	buf.WriteString("\r\n")

	if len(m.Attachments) > 0 {
		for _, attachment := range m.Attachments {
			buf.WriteString("\r\n\r\n--" + boundary + "\r\n")

			if attachment.Inline {
				buf.WriteString("Content-Type: message/rfc822\r\n")
				buf.WriteString("Content-Disposition: inline; filename=\"" + attachment.Filename + "\"\r\n\r\n")

				buf.Write(attachment.Data)
			} else {
				ext := filepath.Ext(attachment.Filename)
				mimetype := mime.TypeByExtension(ext)
				if mimetype != "" {
					mime := fmt.Sprintf("Content-Type: %s\r\n", mimetype)
					buf.WriteString(mime)
				} else {
					buf.WriteString("Content-Type: application/octet-stream\r\n")
				}
				buf.WriteString("Content-Transfer-Encoding: base64\r\n")

				buf.WriteString("Content-Disposition: attachment; filename=\"=?UTF-8?B?")
				buf.WriteString(coder.EncodeToString([]byte(attachment.Filename)))
				buf.WriteString("?=\"\r\n\r\n")

				b := make([]byte, base64.StdEncoding.EncodedLen(len(attachment.Data)))
				base64.StdEncoding.Encode(b, attachment.Data)

				// write base64 content in lines of up to 76 chars
				for i, l := 0, len(b); i < l; i++ {
					buf.WriteByte(b[i])
					if (i+1)%76 == 0 {
						buf.WriteString("\r\n")
					}
				}
			}

			buf.WriteString("\r\n--" + boundary)
		}

		buf.WriteString("--")
	}

	return buf.Bytes()
}


func Send(addr string, auth smtp.Auth, m *Message) error {
	return smtp.SendMail(addr, auth, m.From.Address, m.Tolist(), m.Bytes())
}

func Send ( addr string , auth smtp . Auth , m * Message ) error { return smtp . SendMail ( addr , auth , m . From . Address , m . Tolist ( ) , m . Bytes ( ) ) }


func (e *Envelope) GetHeader(name string) string {
	if e.header == nil {
		return ""
	}
	return decodeHeader(e.header.Get(name))
}

func ( e * Envelope ) GetHeader ( name string ) string { if e . header == nil { return " " } return decodeHeader ( e . header . Get ( name ) ) }

// GetHeader processes the specified header for RFC 2047 encoded words and returns the result as a
// UTF-8 string

GetHeader processes the specified header for RFC 2047 encoded words and returns the result as a UTF - 8 string


func (e *Envelope) GetHeaderValues(name string) []string {
	if e.header == nil {
		return []string{}
	}

	rawValues := (*e.header)[textproto.CanonicalMIMEHeaderKey(name)]
	var values []string
	for _, v := range rawValues {
		values = append(values, decodeHeader(v))
	}
	return values
}

func ( e * Envelope ) GetHeaderValues ( name string ) [ ] string { if e . header == nil { return [ ] string { } } rawValues := ( * e . header ) [ textproto . CanonicalMIMEHeaderKey ( name ) ] var values [ ] string for _ , v := range rawValues { values = append ( values , decodeHeader ( v ) ) } return values }

// GetHeaderValues processes the specified header for RFC 2047 encoded words and returns all existing
// values as a list of UTF-8 strings

GetHeaderValues processes the specified header for RFC 2047 encoded words and returns all existing values as a list of UTF - 8 strings


func (e *Envelope) SetHeader(name string, value []string) error {
	if name == "" {
		return fmt.Errorf("Provide non-empty header name")
	}

	for i, v := range value {
		if i == 0 {
			e.header.Set(name, mime.BEncoding.Encode("utf-8", v))
			continue
		}
		e.header.Add(name, mime.BEncoding.Encode("utf-8", v))
	}
	return nil
}

func ( e * Envelope ) SetHeader ( name string , value [ ] string ) error { if name == " " { return fmt . Errorf ( " " ) } for i , v := range value { if i == 0 { e . header . Set ( name , mime . BEncoding . Encode ( " " , v ) ) continue } e . header . Add ( name , mime . BEncoding . Encode ( " " , v ) ) } return nil }

// SetHeader sets given header name to the given value.
// If the header exists already, all existing values are replaced.

SetHeader sets given header name to the given value . If the header exists already all existing values are replaced .


func (e *Envelope) AddHeader(name string, value string) error {
	if name == "" {
		return fmt.Errorf("Provide non-empty header name")
	}

	e.header.Add(name, mime.BEncoding.Encode("utf-8", value))
	return nil
}

func ( e * Envelope ) AddHeader ( name string , value string ) error { if name == " " { return fmt . Errorf ( " " ) } e . header . Add ( name , mime . BEncoding . Encode ( " " , value ) ) return nil }

// AddHeader appends given header value to header name without changing existing values.
// If the header does not exist already, it will be created.

AddHeader appends given header value to header name without changing existing values . If the header does not exist already it will be created .


func (e *Envelope) DeleteHeader(name string) error {
	if name == "" {
		return fmt.Errorf("Provide non-empty header name")
	}

	e.header.Del(name)
	return nil
}

func ( e * Envelope ) DeleteHeader ( name string ) error { if name == " " { return fmt . Errorf ( " " ) } e . header . Del ( name ) return nil }


func (e *Envelope) AddressList(key string) ([]*mail.Address, error) {
	if e.header == nil {
		return nil, fmt.Errorf("No headers available")
	}
	if !AddressHeaders[strings.ToLower(key)] {
		return nil, fmt.Errorf("%s is not an address header", key)
	}

	str := decodeToUTF8Base64Header(e.header.Get(key))
	if str == "" {
		return nil, mail.ErrHeaderNotPresent
	}

	// These statements are handy for debugging ParseAddressList errors
	// fmt.Println("in:  ", m.header.Get(key))
	// fmt.Println("out: ", str)
	ret, err := mail.ParseAddressList(str)
	switch {
	case err == nil:
		// carry on
	case err.Error() == "mail: expected comma":
		ret, err = mail.ParseAddressList(ensureCommaDelimitedAddresses(str))
		if err != nil {
			return nil, err
		}
	default:
		return nil, err
	}
	return ret, nil
}

// AddressList returns a mail.Address slice with RFC 2047 encoded names converted to UTF-8

AddressList returns a mail . Address slice with RFC 2047 encoded names converted to UTF - 8


func (e *Envelope) Clone() *Envelope {
	if e == nil {
		return nil
	}

	newEnvelope := &Envelope{
		e.Text,
		e.HTML,
		e.Root.Clone(nil),
		e.Attachments,
		e.Inlines,
		e.OtherParts,
		e.Errors,
		e.header,
	}
	return newEnvelope
}

func ( e * Envelope ) Clone ( ) * Envelope { if e == nil { return nil } newEnvelope := & Envelope { e . Text , e . HTML , e . Root . Clone ( nil ) , e . Attachments , e . Inlines , e . OtherParts , e . Errors , e . header , } return newEnvelope }

// Clone returns a clone of the current Envelope


func ReadEnvelope(r io.Reader) (*Envelope, error) {
	// Read MIME parts from reader
	root, err := ReadParts(r)
	if err != nil {
		return nil, errors.WithMessage(err, "Failed to ReadParts")
	}
	return EnvelopeFromPart(root)
}

func
ReadEnvelope
(
r
io
.
Reader
)
(
*
Envelope
,
error
)
{
// Read MIME parts from reader
root
,
err
:=
ReadParts
(
r
)
if
err
!=
{
return
,
errors
.
WithMessage
(
err
,
"
"
)
}
return
EnvelopeFromPart
(
root
)
}

// ReadEnvelope is a wrapper around ReadParts and EnvelopeFromPart.  It parses the content of the
// provided reader into an Envelope, downconverting HTML to plain text if needed, and sorting the
// attachments, inlines and other parts into their respective slices. Errors are collected from all
// Parts and placed into the Envelope.Errors slice.

ReadEnvelope is a wrapper around ReadParts and EnvelopeFromPart . It parses the content of the provided reader into an Envelope downconverting HTML to plain text if needed and sorting the attachments inlines and other parts into their respective slices . Errors are collected from all Parts and placed into the Envelope . Errors slice .


func EnvelopeFromPart(root *Part) (*Envelope, error) {
	e := &Envelope{
		Root:   root,
		header: &root.Header,
	}

	if detectMultipartMessage(root) {
		// Multi-part message (message with attachments, etc)
		if err := parseMultiPartBody(root, e); err != nil {
			return nil, err
		}
	} else {
		if detectBinaryBody(root) {
			// Attachment only, no text
			if root.Disposition == cdInline {
				e.Inlines = append(e.Inlines, root)
			} else {
				e.Attachments = append(e.Attachments, root)
			}
		} else {
			// Only text, no attachments
			if err := parseTextOnlyBody(root, e); err != nil {
				return nil, err
			}
		}
	}

	// Down-convert HTML to text if necessary
	if e.Text == "" && e.HTML != "" {
		// We always warn when this happens
		e.Root.addWarning(
			ErrorPlainTextFromHTML,
			"Message did not contain a text/plain part")
		var err error
		if e.Text, err = html2text.FromString(e.HTML); err != nil {
			// Downcoversion shouldn't fail
			e.Text = ""
			p := e.Root.BreadthMatchFirst(matchHTMLBodyPart)
			p.addError(
				ErrorPlainTextFromHTML,
				"Failed to downconvert HTML: %v",
				err)
		}
	}

	// Copy part errors into Envelope.
	if e.Root != nil {
		_ = e.Root.DepthMatchAll(func(part *Part) bool {
			// Using DepthMatchAll to traverse all parts, don't care about result.
			for i := range part.Errors {
				// Range index is needed to get the correct address, because range value points to
				// a locally scoped variable.
				e.Errors = append(e.Errors, part.Errors[i])
			}
			return false
		})
	}

	return e, nil
}

// EnvelopeFromPart uses the provided Part tree to build an Envelope, downconverting HTML to plain
// text if needed, and sorting the attachments, inlines and other parts into their respective
// slices.  Errors are collected from all Parts and placed into the Envelopes Errors slice.

EnvelopeFromPart uses the provided Part tree to build an Envelope downconverting HTML to plain text if needed and sorting the attachments inlines and other parts into their respective slices . Errors are collected from all Parts and placed into the Envelopes Errors slice .


func parseTextOnlyBody(root *Part, e *Envelope) error {
	// Determine character set
	var charset string
	var isHTML bool
	if ctype := root.Header.Get(hnContentType); ctype != "" {
		if mediatype, mparams, _, err := parseMediaType(ctype); err == nil {
			isHTML = (mediatype == ctTextHTML)
			if mparams[hpCharset] != "" {
				charset = mparams[hpCharset]
			}
		}
	}

	// Read transcoded text
	if isHTML {
		rawHTML := string(root.Content)
		// Note: Empty e.Text will trigger html2text conversion
		e.HTML = rawHTML
		if charset == "" {
			// Search for charset in HTML metadata
			if charset = coding.FindCharsetInHTML(rawHTML); charset != "" {
				// Found charset in HTML
				if convHTML, err := coding.ConvertToUTF8String(charset, root.Content); err == nil {
					// Successful conversion
					e.HTML = convHTML
				} else {
					// Conversion failed
					root.addWarning(ErrorCharsetConversion, err.Error())
				}
			}
			// Converted from charset in HTML
			return nil
		}
	} else {
		e.Text = string(root.Content)
	}

	return nil
}

// parseTextOnlyBody parses a plain text message in root that has MIME-like headers, but
// only contains a single part - no boundaries, etc.  The result is placed in e.

parseTextOnlyBody parses a plain text message in root that has MIME - like headers but only contains a single part - no boundaries etc . The result is placed in e .


func parseMultiPartBody(root *Part, e *Envelope) error {
	// Parse top-level multipart
	ctype := root.Header.Get(hnContentType)
	mediatype, params, _, err := parseMediaType(ctype)
	if err != nil {
		return fmt.Errorf("Unable to parse media type: %v", err)
	}
	if !strings.HasPrefix(mediatype, ctMultipartPrefix) {
		return fmt.Errorf("Unknown mediatype: %v", mediatype)
	}
	boundary := params[hpBoundary]
	if boundary == "" {
		return fmt.Errorf("Unable to locate boundary param in Content-Type header")
	}

	// Locate text body
	if mediatype == ctMultipartAltern {
		p := root.BreadthMatchFirst(func(p *Part) bool {
			return p.ContentType == ctTextPlain && p.Disposition != cdAttachment
		})
		if p != nil {
			e.Text = string(p.Content)
		}
	} else {
		// multipart is of a mixed type
		parts := root.DepthMatchAll(func(p *Part) bool {
			return p.ContentType == ctTextPlain && p.Disposition != cdAttachment
		})
		for i, p := range parts {
			if i > 0 {
				e.Text += "\n--\n"
			}
			e.Text += string(p.Content)
		}
	}

	// Locate HTML body
	p := root.BreadthMatchFirst(matchHTMLBodyPart)
	if p != nil {
		e.HTML += string(p.Content)
	}

	// Locate attachments
	e.Attachments = root.BreadthMatchAll(func(p *Part) bool {
		return p.Disposition == cdAttachment || p.ContentType == ctAppOctetStream
	})

	// Locate inlines
	e.Inlines = root.BreadthMatchAll(func(p *Part) bool {
		return p.Disposition == cdInline && !strings.HasPrefix(p.ContentType, ctMultipartPrefix)
	})

	// Locate others parts not considered in attachments or inlines
	e.OtherParts = root.BreadthMatchAll(func(p *Part) bool {
		if strings.HasPrefix(p.ContentType, ctMultipartPrefix) {
			return false
		}
		if p.Disposition != "" {
			return false
		}
		if p.ContentType == ctAppOctetStream {
			return false
		}
		return p.ContentType != ctTextPlain && p.ContentType != ctTextHTML
	})

	return nil
}

// parseMultiPartBody parses a multipart message in root.  The result is placed in e.

parseMultiPartBody parses a multipart message in root . The result is placed in e .


func matchHTMLBodyPart(p *Part) bool {
	return p.ContentType == ctTextHTML && p.Disposition != cdAttachment
}

func matchHTMLBodyPart ( p * Part ) bool { return p . ContentType == ctTextHTML && p . Disposition != cdAttachment }

// Used by Part matchers to locate the HTML body.  Not inlined because it's used in multiple places.

Used by Part matchers to locate the HTML body . Not inlined because it s used in multiple places .


func ensureCommaDelimitedAddresses(s string) string {
	// This normalizes the whitespace, but may interfere with CFWS (comments with folding whitespace)
	// RFC-5322 3.4.0:
	//      because some legacy implementations interpret the comment,
	//      comments generally SHOULD NOT be used in address fields
	//      to avoid confusing such implementations.
	s = strings.Join(strings.Fields(s), " ")

	inQuotes := false
	inDomain := false
	escapeSequence := false
	sb := strings.Builder{}
	for _, r := range s {
		if escapeSequence {
			escapeSequence = false
			sb.WriteRune(r)
			continue
		}
		if r == '"' {
			inQuotes = !inQuotes
			sb.WriteRune(r)
			continue
		}
		if inQuotes {
			if r == '\\' {
				escapeSequence = true
				sb.WriteRune(r)
				continue
			}
		} else {
			if r == '@' {
				inDomain = true
				sb.WriteRune(r)
				continue
			}
			if inDomain {
				if r == ';' {
					sb.WriteRune(r)
					break
				}
				if r == ',' {
					inDomain = false
					sb.WriteRune(r)
					continue
				}
				if r == ' ' {
					inDomain = false
					sb.WriteRune(',')
					sb.WriteRune(r)
					continue
				}
			}
		}
		sb.WriteRune(r)
	}
	return sb.String()
}

// Used by AddressList to ensure that address lists are properly delimited


func (p MailBuilder) Date(date time.Time) MailBuilder {
	p.date = date
	return p
}

func ( p MailBuilder ) Date ( date time . Time ) MailBuilder { p . date = date return p }

// Date returns a copy of MailBuilder with the specified Date header.

Date returns a copy of MailBuilder with the specified Date header .


func (p MailBuilder) From(name, addr string) MailBuilder {
	p.from = mail.Address{Name: name, Address: addr}
	return p
}

func ( p MailBuilder ) From ( name , addr string ) MailBuilder { p . from = mail . Address { Name : name , Address : addr } return p }

// From returns a copy of MailBuilder with the specified From header.

From returns a copy of MailBuilder with the specified From header .


func (p MailBuilder) Subject(subject string) MailBuilder {
	p.subject = subject
	return p
}

func ( p MailBuilder ) Subject ( subject string ) MailBuilder { p . subject = subject return p }

// Subject returns a copy of MailBuilder with the specified Subject header.

Subject returns a copy of MailBuilder with the specified Subject header .


func (p MailBuilder) To(name, addr string) MailBuilder {
	p.to = append(p.to, mail.Address{Name: name, Address: addr})
	return p
}

func ( p MailBuilder ) To ( name , addr string ) MailBuilder { p . to = append ( p . to , mail . Address { Name : name , Address : addr } ) return p }

// To returns a copy of MailBuilder with this name & address appended to the To header.  name may be
// empty.

To returns a copy of MailBuilder with this name & address appended to the To header . name may be empty .


func (p MailBuilder) ToAddrs(to []mail.Address) MailBuilder {
	p.to = to
	return p
}

func ( p MailBuilder ) ToAddrs ( to [ ] mail . Address ) MailBuilder { p . to = to return p }

// ToAddrs returns a copy of MailBuilder with the specified To addresses.

ToAddrs returns a copy of MailBuilder with the specified To addresses .


func (p MailBuilder) CC(name, addr string) MailBuilder {
	p.cc = append(p.cc, mail.Address{Name: name, Address: addr})
	return p
}

func ( p MailBuilder ) CC ( name , addr string ) MailBuilder { p . cc = append ( p . cc , mail . Address { Name : name , Address : addr } ) return p }

// CC returns a copy of MailBuilder with this name & address appended to the CC header.  name may be
// empty.

CC returns a copy of MailBuilder with this name & address appended to the CC header . name may be empty .


func (p MailBuilder) CCAddrs(cc []mail.Address) MailBuilder {
	p.cc = cc
	return p
}

func ( p MailBuilder ) CCAddrs ( cc [ ] mail . Address ) MailBuilder { p . cc = cc return p }

// CCAddrs returns a copy of MailBuilder with the specified CC addresses.

CCAddrs returns a copy of MailBuilder with the specified CC addresses .


func (p MailBuilder) ReplyTo(name, addr string) MailBuilder {
	p.replyTo = mail.Address{Name: name, Address: addr}
	return p
}

func ( p MailBuilder ) ReplyTo ( name , addr string ) MailBuilder { p . replyTo = mail . Address { Name : name , Address : addr } return p }

// ReplyTo returns a copy of MailBuilder with this name & address appended to the To header.  name
// may be empty.

ReplyTo returns a copy of MailBuilder with this name & address appended to the To header . name may be empty .


func (p MailBuilder) Header(name, value string) MailBuilder {
	// Copy existing header map
	h := textproto.MIMEHeader{}
	for k, v := range p.header {
		h[k] = v
	}
	h.Add(name, value)
	p.header = h
	return p
}

func
(
p
MailBuilder
)
Header
(
name
,
value
string
)
MailBuilder
{
// Copy existing header map
h
:=
textproto
.
MIMEHeader
{
}
for
k
,
v
:=
range
p
.
header
{
h
[
k
]
=
v
}
h
.
Add
(
name
,
value
)
p
.
header
=
h
return
p
}

// Header returns a copy of MailBuilder with the specified value added to the named header.

Header returns a copy of MailBuilder with the specified value added to the named header .


func (p MailBuilder) AddAttachment(b []byte, contentType string, fileName string) MailBuilder {
	part := NewPart(contentType)
	part.Content = b
	part.FileName = fileName
	part.Disposition = cdAttachment
	p.attachments = append(p.attachments, part)
	return p
}

func ( p MailBuilder ) AddAttachment ( b [ ] byte , contentType string , fileName string ) MailBuilder { part := NewPart ( contentType ) part . Content = b part . FileName = fileName part . Disposition = cdAttachment p . attachments = append ( p . attachments , part ) return p }

// AddAttachment returns a copy of MailBuilder that includes the specified attachment.

AddAttachment returns a copy of MailBuilder that includes the specified attachment .


func (p MailBuilder) AddFileAttachment(path string) MailBuilder {
	// Only allow first p.err value
	if p.err != nil {
		return p
	}
	f, err := os.Open(path)
	if err != nil {
		p.err = err
		return p
	}
	b, err := ioutil.ReadAll(f)
	if err != nil {
		p.err = err
		return p
	}
	name := filepath.Base(path)
	ctype := mime.TypeByExtension(filepath.Ext(name))
	return p.AddAttachment(b, ctype, name)
}

func
(
p
MailBuilder
)
AddFileAttachment
(
path
string
)
MailBuilder
{
// Only allow first p.err value
if
p
.
err
!=
{
return
p
}
f
,
err
:=
os
.
Open
(
path
)
if
err
!=
{
p
.
err
=
err
return
p
}
b
,
err
:=
ioutil
.
ReadAll
(
f
)
if
err
!=
{
p
.
err
=
err
return
p
}
name
:=
filepath
.
Base
(
path
)
ctype
:=
mime
.
TypeByExtension
(
filepath
.
Ext
(
name
)
)
return
p
.
AddAttachment
(
b
,
ctype
,
name
)
}

// AddFileAttachment returns a copy of MailBuilder that includes the specified attachment.
// fileName, will be populated from the base name of path.  Content type will be detected from the
// path extension.

AddFileAttachment returns a copy of MailBuilder that includes the specified attachment . fileName will be populated from the base name of path . Content type will be detected from the path extension .


func (p MailBuilder) AddInline(
	b []byte,
	contentType string,
	fileName string,
	contentID string,
) MailBuilder {
	part := NewPart(contentType)
	part.Content = b
	part.FileName = fileName
	part.Disposition = cdInline
	part.ContentID = contentID
	p.inlines = append(p.inlines, part)
	return p
}

func ( p MailBuilder ) AddInline ( b [ ] byte , contentType string , fileName string , contentID string , ) MailBuilder { part := NewPart ( contentType ) part . Content = b part . FileName = fileName part . Disposition = cdInline part . ContentID = contentID p . inlines = append ( p . inlines , part ) return p }

// AddInline returns a copy of MailBuilder that includes the specified inline.  fileName and
// contentID may be left empty.

AddInline returns a copy of MailBuilder that includes the specified inline . fileName and contentID may be left empty .


func (p MailBuilder) Equals(o MailBuilder) bool {
	return reflect.DeepEqual(p, o)
}

func ( p MailBuilder ) Equals ( o MailBuilder ) bool { return reflect . DeepEqual ( p , o ) }

// Equals uses the reflect package to test two MailBuilder structs for equality, primarily for unit
// tests.

Equals uses the reflect package to test two MailBuilder structs for equality primarily for unit tests .


func (p *Part) Encode(writer io.Writer) error {
	if p.Header == nil {
		p.Header = make(textproto.MIMEHeader)
	}
	cte := p.setupMIMEHeaders()
	// Encode this part.
	b := bufio.NewWriter(writer)
	p.encodeHeader(b)
	if len(p.Content) > 0 {
		b.Write(crnl)
		if err := p.encodeContent(b, cte); err != nil {
			return err
		}
	}
	if p.FirstChild == nil {
		return b.Flush()
	}
	// Encode children.
	endMarker := []byte("\r\n--" + p.Boundary + "--")
	marker := endMarker[:len(endMarker)-2]
	c := p.FirstChild
	for c != nil {
		b.Write(marker)
		b.Write(crnl)
		if err := c.Encode(b); err != nil {
			return err
		}
		c = c.NextSibling
	}
	b.Write(endMarker)
	b.Write(crnl)
	return b.Flush()
}

func
(
p
*
Part
)
Encode
(
writer
io
.
Writer
)
error
{
if
p
.
Header
==
{
p
.
Header
=
make
(
textproto
.
MIMEHeader
)
}
cte
:=
p
.
setupMIMEHeaders
(
)
// Encode this part.
b
:=
bufio
.
NewWriter
(
writer
)
p
.
encodeHeader
(
b
)
if
len
(
p
.
Content
)
>
0
{
b
.
Write
(
crnl
)
if
err
:=
p
.
encodeContent
(
b
,
cte
)
;
err
!=
{
return
err
}
}
if
p
.
FirstChild
==
{
return
b
.
Flush
(
)
}
// Encode children.
endMarker
:=
[
]
byte
(
"
\r
\n
"
+
p
.
Boundary
+
"
"
)
marker
:=
endMarker
[
:
len
(
endMarker
)
2
]
c
:=
p
.
FirstChild
for
c
!=
{
b
.
Write
(
marker
)
b
.
Write
(
crnl
)
if
err
:=
c
.
Encode
(
b
)
;
err
!=
{
return
err
}
c
=
c
.
NextSibling
}
b
.
Write
(
endMarker
)
b
.
Write
(
crnl
)
return
b
.
Flush
(
)
}

// Encode writes this Part and all its children to the specified writer in MIME format.

Encode writes this Part and all its children to the specified writer in MIME format .


func (p *Part) encodeHeader(b *bufio.Writer) {
	keys := make([]string, 0, len(p.Header))
	for k := range p.Header {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	for _, k := range keys {
		for _, v := range p.Header[k] {
			encv := v
			switch selectTransferEncoding([]byte(v), true) {
			case teBase64:
				encv = mime.BEncoding.Encode(utf8, v)
			case teQuoted:
				encv = mime.QEncoding.Encode(utf8, v)
			}
			// _ used to prevent early wrapping
			wb := stringutil.Wrap(76, k, ":_", encv, "\r\n")
			wb[len(k)+1] = ' '
			b.Write(wb)
		}
	}
}

func
(
p
*
Part
)
encodeHeader
(
b
*
bufio
.
Writer
)
{
keys
:=
make
(
[
]
string
,
0
,
len
(
p
.
Header
)
)
for
k
:=
range
p
.
Header
{
keys
=
append
(
keys
,
k
)
}
sort
.
Strings
(
keys
)
for
_
,
k
:=
range
keys
{
for
_
,
v
:=
range
p
.
Header
[
k
]
{
encv
:=
v
switch
selectTransferEncoding
(
[
]
byte
(
v
)
,
true
)
{
case
teBase64
:
encv
=
mime
.
BEncoding
.
Encode
(
utf8
,
v
)
case
teQuoted
:
encv
=
mime
.
QEncoding
.
Encode
(
utf8
,
v
)
}
// _ used to prevent early wrapping
wb
:=
stringutil
.
Wrap
(
76
,
k
,
"
"
,
encv
,
"
\r
\n
"
)
wb
[
len
(
k
)
+
1
]
=
' '
b
.
Write
(
wb
)
}
}
}

// encodeHeader writes out a sorted list of headers.

encodeHeader writes out a sorted list of headers .


func (p *Part) encodeContent(b *bufio.Writer, cte transferEncoding) (err error) {
	switch cte {
	case teBase64:
		enc := base64.StdEncoding
		text := make([]byte, enc.EncodedLen(len(p.Content)))
		base64.StdEncoding.Encode(text, p.Content)
		// Wrap lines.
		lineLen := 76
		for len(text) > 0 {
			if lineLen > len(text) {
				lineLen = len(text)
			}
			if _, err = b.Write(text[:lineLen]); err != nil {
				return err
			}
			b.Write(crnl)
			text = text[lineLen:]
		}
	case teQuoted:
		qp := quotedprintable.NewWriter(b)
		if _, err = qp.Write(p.Content); err != nil {
			return err
		}
		err = qp.Close()
	default:
		_, err = b.Write(p.Content)
	}
	return err
}

func
(
p
*
Part
)
encodeContent
(
b
*
bufio
.
Writer
,
cte
transferEncoding
)
(
err
error
)
{
switch
cte
{
case
teBase64
:
enc
:=
base64
.
StdEncoding
text
:=
make
(
[
]
byte
,
enc
.
EncodedLen
(
len
(
p
.
Content
)
)
)
base64
.
StdEncoding
.
Encode
(
text
,
p
.
Content
)
// Wrap lines.
lineLen
:=
76
for
len
(
text
)
>
0
{
if
lineLen
>
len
(
text
)
{
lineLen
=
len
(
text
)
}
if
_
,
err
=
b
.
Write
(
text
[
:
lineLen
]
)
;
err
!=
{
return
err
}
b
.
Write
(
crnl
)
text
=
text
[
lineLen
:
]
}
case
teQuoted
:
qp
:=
quotedprintable
.
NewWriter
(
b
)
if
_
,
err
=
qp
.
Write
(
p
.
Content
)
;
err
!=
{
return
err
}
err
=
qp
.
Close
(
)
default
:
_
,
err
=
b
.
Write
(
p
.
Content
)
}
return
err
}

// encodeContent writes out the content in the selected encoding.

encodeContent writes out the content in the selected encoding .


func selectTransferEncoding(content []byte, quoteLineBreaks bool) transferEncoding {
	if len(content) == 0 {
		return te7Bit
	}
	// Binary chars remaining before we choose b64 encoding.
	threshold := b64Percent * len(content) / 100
	bincount := 0
	for _, b := range content {
		if (b < ' ' || '~' < b) && b != '\t' {
			if !quoteLineBreaks && (b == '\r' || b == '\n') {
				continue
			}
			bincount++
			if bincount >= threshold {
				return teBase64
			}
		}
	}
	if bincount == 0 {
		return te7Bit
	}
	return teQuoted
}

// selectTransferEncoding scans content for non-ASCII characters and selects 'b' or 'q' encoding.

selectTransferEncoding scans content for non - ASCII characters and selects b or q encoding .


func setParamValue(p map[string]string, k, v string) {
	if v != "" {
		p[k] = v
	}
}

func setParamValue ( p map [ string ] string , k , v string ) { if v != " " { p [ k ] = v } }

// setParamValue will ignore empty values


func NewBase64Cleaner(r io.Reader) *Base64Cleaner {
	return &Base64Cleaner{
		Errors: make([]error, 0),
		r:      r,
	}
}

func NewBase64Cleaner ( r io . Reader ) * Base64Cleaner { return & Base64Cleaner { Errors : make ( [ ] error , 0 ) , r : r , } }

// NewBase64Cleaner returns a Base64Cleaner object for the specified reader.  Base64Cleaner
// implements the io.Reader interface.

NewBase64Cleaner returns a Base64Cleaner object for the specified reader . Base64Cleaner implements the io . Reader interface .


func decodeToUTF8Base64Header(input string) string {
	if !strings.Contains(input, "=?") {
		// Don't scan if there is nothing to do here
		return input
	}

	tokens := strings.FieldsFunc(input, whiteSpaceRune)
	output := make([]string, len(tokens))
	for i, token := range tokens {
		if len(token) > 4 && strings.Contains(token, "=?") {
			// Stash parenthesis, they should not be encoded
			prefix := ""
			suffix := ""
			if token[0] == '(' {
				prefix = "("
				token = token[1:]
			}
			if token[len(token)-1] == ')' {
				suffix = ")"
				token = token[:len(token)-1]
			}
			// Base64 encode token
			output[i] = prefix + mime.BEncoding.Encode("UTF-8", decodeHeader(token)) + suffix
		} else {
			output[i] = token
		}
	}

	// Return space separated tokens
	return strings.Join(output, " ")
}

// decodeToUTF8Base64Header decodes a MIME header per RFC 2047, reencoding to =?utf-8b?

decodeToUTF8Base64Header decodes a MIME header per RFC 2047 reencoding to = ?utf - 8b?


func parseMediaType(ctype string) (mtype string, params map[string]string, invalidParams []string, err error) {
	mtype, params, err = mime.ParseMediaType(ctype)
	if err != nil {
		// Small hack to remove harmless charset duplicate params.
		mctype := fixMangledMediaType(ctype, ";")
		mtype, params, err = mime.ParseMediaType(mctype)
		if err != nil {
			// Some badly formed media types forget to send ; between fields.
			mctype := fixMangledMediaType(ctype, " ")
			if strings.Contains(mctype, `name=""`) {
				mctype = strings.Replace(mctype, `name=""`, `name=" "`, -1)
			}
			mtype, params, err = mime.ParseMediaType(mctype)
			if err != nil {
				// If the media parameter has special characters, ensure that it is quoted.
				mtype, params, err = mime.ParseMediaType(fixUnquotedSpecials(mctype))
				if err != nil {
					return "", nil, nil, errors.WithStack(err)
				}
			}
		}
	}
	if mtype == ctPlaceholder {
		mtype = ""
	}
	for name, value := range params {
		if value != pvPlaceholder {
			continue
		}
		invalidParams = append(invalidParams, name)
		delete(params, name)
	}
	return mtype, params, invalidParams, err
}

// parseMediaType is a more tolerant implementation of Go's mime.ParseMediaType function.

parseMediaType is a more tolerant implementation of Go s mime . ParseMediaType function .


func fixMangledMediaType(mtype, sep string) string {
	if mtype == "" {
		return ""
	}
	parts := strings.Split(mtype, sep)
	mtype = ""
	for i, p := range parts {
		switch i {
		case 0:
			if p == "" {
				// The content type is completely missing. Put in a placeholder.
				p = ctPlaceholder
			}
		default:
			if !strings.Contains(p, "=") {
				p = p + "=" + pvPlaceholder
			}

			// RFC-2047 encoded attribute name
			p = rfc2047AttributeName(p)

			pair := strings.Split(p, "=")
			if strings.Contains(mtype, pair[0]+"=") {
				// Ignore repeated parameters.
				continue
			}

			if strings.ContainsAny(pair[0], "()<>@,;:\"\\/[]?") {
				// attribute is a strict token and cannot be a quoted-string
				// if any of the above characters are present in a token it
				// must be quoted and is therefor an invalid attribute.
				// Discard the pair.
				continue
			}
		}
		mtype += p
		// Only terminate with semicolon if not the last parameter and if it doesn't already have a
		// semicolon.
		if i != len(parts)-1 && !strings.HasSuffix(mtype, ";") {
			mtype += ";"
		}
	}
	if strings.HasSuffix(mtype, ";") {
		mtype = mtype[:len(mtype)-1]
	}
	return mtype
}

// fixMangledMediaType is used to insert ; separators into media type strings that lack them, and
// remove repeated parameters.

fixMangledMediaType is used to insert ; separators into media type strings that lack them and remove repeated parameters .


func detectMultipartMessage(root *Part) bool {
	// Parse top-level multipart
	ctype := root.Header.Get(hnContentType)
	mediatype, _, _, err := parseMediaType(ctype)
	if err != nil {
		return false
	}
	// According to rfc2046#section-5.1.7 all other multipart should
	// be treated as multipart/mixed
	return strings.HasPrefix(mediatype, ctMultipartPrefix)
}

// detectMultipartMessage returns true if the message has a recognized multipart Content-Type header

detectMultipartMessage returns true if the message has a recognized multipart Content - Type header


func detectBinaryBody(root *Part) bool {
	if detectTextHeader(root.Header, true) {
		return false
	}

	isBin := detectAttachmentHeader(root.Header)
	if !isBin {
		// This must be an attachment, if the Content-Type is not
		// 'text/plain' or 'text/html'.
		// Example:
		// Content-Type: application/pdf; name="doc.pdf"
		mediatype, _, _, _ := parseMediaType(root.Header.Get(hnContentType))
		mediatype = strings.ToLower(mediatype)
		if mediatype != ctTextPlain && mediatype != ctTextHTML {
			return true
		}
	}

	return isBin
}

// detectBinaryBody returns true if the mail header defines a binary body.

detectBinaryBody returns true if the mail header defines a binary body .


func (p *Part) BreadthMatchFirst(matcher PartMatcher) *Part {
	q := list.New()
	q.PushBack(p)

	// Push children onto queue and attempt to match in that order
	for q.Len() > 0 {
		e := q.Front()
		p := e.Value.(*Part)
		if matcher(p) {
			return p
		}
		q.Remove(e)
		c := p.FirstChild
		for c != nil {
			q.PushBack(c)
			c = c.NextSibling
		}
	}

	return nil
}

// BreadthMatchFirst performs a breadth first search of the Part tree and returns the first part
// that causes the given matcher to return true

BreadthMatchFirst performs a breadth first search of the Part tree and returns the first part that causes the given matcher to return true


func (p *Part) BreadthMatchAll(matcher PartMatcher) []*Part {
	q := list.New()
	q.PushBack(p)

	matches := make([]*Part, 0, 10)

	// Push children onto queue and attempt to match in that order
	for q.Len() > 0 {
		e := q.Front()
		p := e.Value.(*Part)
		if matcher(p) {
			matches = append(matches, p)
		}
		q.Remove(e)
		c := p.FirstChild
		for c != nil {
			q.PushBack(c)
			c = c.NextSibling
		}
	}

	return matches
}

// BreadthMatchAll performs a breadth first search of the Part tree and returns all parts that cause
// the given matcher to return true

BreadthMatchAll performs a breadth first search of the Part tree and returns all parts that cause the given matcher to return true


func (p *Part) DepthMatchFirst(matcher PartMatcher) *Part {
	root := p
	for {
		if matcher(p) {
			return p
		}
		c := p.FirstChild
		if c != nil {
			p = c
		} else {
			for p.NextSibling == nil {
				if p == root {
					return nil
				}
				p = p.Parent
			}
			p = p.NextSibling
		}
	}
}

func ( p * Part ) DepthMatchFirst ( matcher PartMatcher ) * Part { root := p for { if matcher ( p ) { return p } c := p . FirstChild if c != nil { p = c } else { for p . NextSibling == nil { if p == root { return nil } p = p . Parent } p = p . NextSibling } } }

// DepthMatchFirst performs a depth first search of the Part tree and returns the first part that
// causes the given matcher to return true

DepthMatchFirst performs a depth first search of the Part tree and returns the first part that causes the given matcher to return true


func (p *Part) DepthMatchAll(matcher PartMatcher) []*Part {
	root := p
	matches := make([]*Part, 0, 10)
	for {
		if matcher(p) {
			matches = append(matches, p)
		}
		c := p.FirstChild
		if c != nil {
			p = c
		} else {
			for p.NextSibling == nil {
				if p == root {
					return matches
				}
				p = p.Parent
			}
			p = p.NextSibling
		}
	}
}

func ( p * Part ) DepthMatchAll ( matcher PartMatcher ) [ ] * Part { root := p matches := make ( [ ] * Part , 0 , 10 ) for { if matcher ( p ) { matches = append ( matches , p ) } c := p . FirstChild if c != nil { p = c } else { for p . NextSibling == nil { if p == root { return matches } p = p . Parent } p = p . NextSibling } } }

// DepthMatchAll performs a depth first search of the Part tree and returns all parts that causes
// the given matcher to return true

DepthMatchAll performs a depth first search of the Part tree and returns all parts that causes the given matcher to return true


func ToASCII(s string) string {
	// unicode.Mn: nonspacing marks
	tr := transform.Chain(norm.NFD, runes.Remove(runes.In(unicode.Mn)), runes.Map(mapLatinSpecial),
		norm.NFC)
	r, _, _ := transform.String(tr, s)
	return r
}

func
ToASCII
(
s
string
)
string
{
// unicode.Mn: nonspacing marks
tr
:=
transform
.
Chain
(
norm
.
NFD
,
runes
.
Remove
(
runes
.
In
(
unicode
.
Mn
)
)
,
runes
.
Map
(
mapLatinSpecial
)
,
norm
.
NFC
)
r
,
_
,
_
:=
transform
.
String
(
tr
,
s
)
return
r
}

// ToASCII converts unicode to ASCII by stripping accents and converting some special characters
// into their ASCII approximations.  Anything else will be replaced with an underscore.

ToASCII converts unicode to ASCII by stripping accents and converting some special characters into their ASCII approximations . Anything else will be replaced with an underscore .


func NewPart(contentType string) *Part {
	return &Part{
		Header:      make(textproto.MIMEHeader),
		ContentType: contentType,
	}
}

func NewPart ( contentType string ) * Part { return & Part { Header : make ( textproto . MIMEHeader ) , ContentType : contentType , } }


func (p *Part) AddChild(child *Part) {
	if p == child {
		// Prevent paradox.
		return
	}
	if p != nil {
		if p.FirstChild == nil {
			// Make it the first child.
			p.FirstChild = child
		} else {
			// Append to sibling chain.
			current := p.FirstChild
			for current.NextSibling != nil {
				current = current.NextSibling
			}
			if current == child {
				// Prevent infinite loop.
				return
			}
			current.NextSibling = child
		}
	}
	// Update all new first-level children Parent pointers.
	for c := child; c != nil; c = c.NextSibling {
		if c == c.NextSibling {
			// Prevent infinite loop.
			return
		}
		c.Parent = p
	}
}

// AddChild adds a child part to either FirstChild or the end of the children NextSibling chain.
// The child may have siblings and children attached.  This method will set the Parent field on
// child and all its siblings. Safe to call on nil.

AddChild adds a child part to either FirstChild or the end of the children NextSibling chain . The child may have siblings and children attached . This method will set the Parent field on child and all its siblings . Safe to call on nil .


func (p *Part) TextContent() bool {
	if p.ContentType == "" {
		// RFC 2045: no CT is equivalent to "text/plain; charset=us-ascii"
		return true
	}
	return strings.HasPrefix(p.ContentType, "text/") ||
		strings.HasPrefix(p.ContentType, ctMultipartPrefix)
}

// TextContent indicates whether the content is text based on its content type.  This value
// determines what content transfer encoding scheme to use.

TextContent indicates whether the content is text based on its content type . This value determines what content transfer encoding scheme to use .


func (p *Part) setupHeaders(r *bufio.Reader, defaultContentType string) error {
	header, err := readHeader(r, p)
	if err != nil {
		return err
	}
	p.Header = header
	ctype := header.Get(hnContentType)
	if ctype == "" {
		if defaultContentType == "" {
			p.addWarning(ErrorMissingContentType, "MIME parts should have a Content-Type header")
			return nil
		}
		ctype = defaultContentType
	}
	// Parse Content-Type header.
	mtype, mparams, minvalidParams, err := parseMediaType(ctype)
	if err != nil {
		return err
	}
	if mtype == "" && len(mparams) > 0 {
		p.addWarning(
			ErrorMissingContentType,
			"Content-Type header has parameters but no content type")
	}
	for i := range minvalidParams {
		p.addWarning(
			ErrorMalformedHeader,
			"Content-Type header has malformed parameter %q",
			minvalidParams[i])
	}
	p.ContentType = mtype
	// Set disposition, filename, charset if available.
	p.setupContentHeaders(mparams)
	p.Boundary = mparams[hpBoundary]
	p.ContentID = coding.FromIDHeader(header.Get(hnContentID))
	return nil
}

// setupHeaders reads the header, then populates the MIME header values for this Part.

setupHeaders reads the header then populates the MIME header values for this Part .


func (p *Part) setupContentHeaders(mediaParams map[string]string) {
	// Determine content disposition, filename, character set.
	disposition, dparams, _, err := parseMediaType(p.Header.Get(hnContentDisposition))
	if err == nil {
		// Disposition is optional
		p.Disposition = disposition
		p.FileName = decodeHeader(dparams[hpFilename])
	}
	if p.FileName == "" && mediaParams[hpName] != "" {
		p.FileName = decodeHeader(mediaParams[hpName])
	}
	if p.FileName == "" && mediaParams[hpFile] != "" {
		p.FileName = decodeHeader(mediaParams[hpFile])
	}
	if p.Charset == "" {
		p.Charset = mediaParams[hpCharset]
	}
	if p.FileModDate.IsZero() {
		p.FileModDate, _ = time.Parse(time.RFC822, mediaParams[hpModDate])
	}
}

// setupContentHeaders uses Content-Type media params and Content-Disposition headers to populate
// the disposition, filename, and charset fields.

setupContentHeaders uses Content - Type media params and Content - Disposition headers to populate the disposition filename and charset fields .


func (p *Part) convertFromDetectedCharset(r io.Reader) (io.Reader, error) {
	// Attempt to detect character set from part content.
	var cd *chardet.Detector
	switch p.ContentType {
	case "text/html":
		cd = chardet.NewHtmlDetector()
	default:
		cd = chardet.NewTextDetector()
	}

	buf, err := ioutil.ReadAll(r)
	if err != nil {
		return nil, errors.WithStack(err)
	}

	cs, err := cd.DetectBest(buf)
	switch err {
	case nil:
		// Carry on
	case chardet.NotDetectedError:
		p.addWarning(ErrorCharsetDeclaration, "charset could not be detected: %v", err)
	default:
		return nil, errors.WithStack(err)
	}

	// Restore r.
	r = bytes.NewReader(buf)

	if cs == nil || cs.Confidence < minCharsetConfidence {
		// Low confidence, use declared character set.
		return p.convertFromStatedCharset(r), nil
	}

	// Confidence exceeded our threshold, use detected character set.
	if p.Charset != "" && !strings.EqualFold(cs.Charset, p.Charset) {
		p.addWarning(ErrorCharsetDeclaration,
			"declared charset %q, detected %q, confidence %d",
			p.Charset, cs.Charset, cs.Confidence)
	}

	reader, err := coding.NewCharsetReader(cs.Charset, r)
	if err != nil {
		// Failed to get a conversion reader.
		p.addWarning(ErrorCharsetConversion, err.Error())
	} else {
		r = reader
		p.OrigCharset = p.Charset
		p.Charset = cs.Charset
	}

	return r, nil
}

// convertFromDetectedCharset attempts to detect the character set for the given part, and returns
// an io.Reader that will convert from that charset to UTF-8. If the charset cannot be detected,
// this method adds a warning to the part and automatically falls back to using
// `convertFromStatedCharset` and returns the reader from that method.

convertFromDetectedCharset attempts to detect the character set for the given part and returns an io . Reader that will convert from that charset to UTF - 8 . If the charset cannot be detected this method adds a warning to the part and automatically falls back to using convertFromStatedCharset and returns the reader from that method .


func (p *Part) Clone(parent *Part) *Part {
	if p == nil {
		return nil
	}

	newPart := &Part{
		PartID:      p.PartID,
		Header:      p.Header,
		Parent:      parent,
		Boundary:    p.Boundary,
		ContentID:   p.ContentID,
		ContentType: p.ContentType,
		Disposition: p.Disposition,
		FileName:    p.FileName,
		Charset:     p.Charset,
		Errors:      p.Errors,
		Content:     p.Content,
		Epilogue:    p.Epilogue,
	}
	newPart.FirstChild = p.FirstChild.Clone(newPart)
	newPart.NextSibling = p.NextSibling.Clone(parent)

	return newPart
}

func ( p * Part ) Clone ( parent * Part ) * Part { if p == nil { return nil } newPart := & Part { PartID : p . PartID , Header : p . Header , Parent : parent , Boundary : p . Boundary , ContentID : p . ContentID , ContentType : p . ContentType , Disposition : p . Disposition , FileName : p . FileName , Charset : p . Charset , Errors : p . Errors , Content : p . Content , Epilogue : p . Epilogue , } newPart . FirstChild = p . FirstChild . Clone ( newPart ) newPart . NextSibling = p . NextSibling . Clone ( parent ) return newPart }

// Clone returns a clone of the current Part.

Clone returns a clone of the current Part .


func ReadParts(r io.Reader) (*Part, error) {
	br := bufio.NewReader(r)
	root := &Part{PartID: "0"}
	// Read header; top-level default CT is text/plain us-ascii according to RFC 822.
	err := root.setupHeaders(br, `text/plain; charset="us-ascii"`)
	if err != nil {
		return nil, err
	}
	if strings.HasPrefix(root.ContentType, ctMultipartPrefix) {
		// Content is multipart, parse it.
		err = parseParts(root, br)
		if err != nil {
			return nil, err
		}
	} else {
		// Content is text or data, decode it.
		if err := root.decodeContent(br); err != nil {
			return nil, err
		}
	}
	return root, nil
}

// ReadParts reads a MIME document from the provided reader and parses it into tree of Part objects.

ReadParts reads a MIME document from the provided reader and parses it into tree of Part objects .