<commit_before>package enmime

import (
	"fmt"
	"github.com/jaytaylor/html2text"
	"mime"
	"net/mail"
	"net/textproto"
	"strings"
)

// MIMEBody is the outer wrapper for MIME messages.
type MIMEBody struct {
	Text           string      // The plain text portion of the message
	HTML           string      // The HTML portion of the message
	IsTextFromHTML bool        // Plain text was empty; down-converted HTML
	Root           MIMEPart    // The top-level MIMEPart
	Attachments    []MIMEPart  // All parts having a Content-Disposition of attachment
	Inlines        []MIMEPart  // All parts having a Content-Disposition of inline
	OtherParts     []MIMEPart  // All parts not in Attachments and Inlines
	header         mail.Header // Header from original message
}

// AddressHeaders enumerates SMTP headers that contain email addresses
var AddressHeaders = []string{"From", "To", "Delivered-To", "Cc", "Bcc", "Reply-To"}

// IsMultipartMessage returns true if the message has a recognized multipart Content-Type header.
// You don't need to check this before calling ParseMIMEBody, it can handle non-multipart messages.
func IsMultipartMessage(mailMsg *mail.Message) bool {
	// Parse top-level multipart
	ctype := mailMsg.Header.Get("Content-Type")
	mediatype, _, err := mime.ParseMediaType(ctype)
	if err != nil {
		return false
	}
	switch mediatype {
	case "multipart/alternative",
		"multipart/mixed",
		"multipart/related",
		"multipart/signed":
		return true
	default:
		if strings.HasPrefix(mediatype, "multipart/") {
			// according to rfc2046#section-5.1.7 all other multipart should
			// be treated as multipart/mixed
			return true
		}
	}
	return false
}

// IsAttachment returns true, if the given header defines an attachment.  First it checks if the
// Content-Disposition header defines an attachement or inline attachment. If this test is false,
// the Content-Type header is checked for attachment, but not inline.  Email clients use inline for
// their text bodies.
//
// Valid Attachment-Headers:
//
//  - Content-Disposition: attachment; filename="frog.jpg"
//  - Content-Disposition: inline; filename="frog.jpg"
//  - Content-Type: attachment; filename="frog.jpg"
func IsAttachment(header mail.Header) bool {
	mediatype, _, _ := mime.ParseMediaType(header.Get("Content-Disposition"))
	if strings.ToLower(mediatype) == "attachment" ||
		strings.ToLower(mediatype) == "inline" {
		return true
	}

	mediatype, _, _ = mime.ParseMediaType(header.Get("Content-Type"))
	if strings.ToLower(mediatype) == "attachment" {
		return true
	}

	return false
}

// IsPlain returns true, if the the MIME headers define a valid 'text/plain' or 'text/html' part. If
// the emptyContentTypeIsPlain argument is set to true, a missing Content-Type header will result in
// a positive plain part detection.
func IsPlain(header mail.Header, emptyContentTypeIsPlain bool) bool {
	ctype := header.Get("Content-Type")
	if ctype == "" && emptyContentTypeIsPlain {
		return true
	}

	mediatype, _, err := mime.ParseMediaType(ctype)
	if err != nil {
		return false
	}
	switch mediatype {
	case "text/plain",
		"text/html":
		return true
	}

	return false

}

// IsBinaryBody returns true if the mail header defines a binary body.
func IsBinaryBody(mailMsg *mail.Message) bool {
	if IsAttachment(mailMsg.Header) == true {
		return true
	}

	return !IsPlain(mailMsg.Header, true)
}

// ParseMIMEBody parses the body of the message object into a  tree of MIMEPart objects, each of
// which is aware of its content type, filename and headers.  If the part was encoded in
// quoted-printable or base64, it is decoded before being stored in the MIMEPart object.
func ParseMIMEBody(mailMsg *mail.Message) (*MIMEBody, error) {
	mimeMsg := &MIMEBody{
		IsTextFromHTML: false,
		header:         mailMsg.Header,
	}

	if !IsMultipartMessage(mailMsg) {
		// Attachment only?
		if IsBinaryBody(mailMsg) {
			if err := parseBinaryOnlyBody(mailMsg, mimeMsg); err != nil {
				return nil, err
			}
		}
		if err := parseTextOnlyBody(mailMsg, mimeMsg); err != nil {
			return nil, err
		}
	} else {
		if err := parseMultiPartBody(mailMsg, mimeMsg); err != nil {
			return nil, err
		}
	}

	// Down-convert HTML to text if necessary
	if mimeMsg.Text == "" && mimeMsg.HTML != "" {
		mimeMsg.IsTextFromHTML = true
		var err error
		if mimeMsg.Text, err = html2text.FromString(mimeMsg.HTML); err != nil {
			// Fail gently
			mimeMsg.Text = ""
			return mimeMsg, err
		}
	}

	return mimeMsg, nil
}

// parseTextOnlyBody parses a plain text message in mailMsg that has MIME-like headers, but
// only contains a single part - no boundaries, etc.  The result is placed in mimeMsg.
func parseTextOnlyBody(mailMsg *mail.Message, mimeMsg *MIMEBody) error {
	bodyBytes, err := decodeSection(
		mailMsg.Header.Get("Content-Transfer-Encoding"), mailMsg.Body)
	if err != nil {
		return fmt.Errorf("Error decoding text-only message: %v", err)
	}

	// Handle plain ASCII text, content-type unspecified, may be reverted later
	mimeMsg.Text = string(bodyBytes)

	// Process top-level content-type
	ctype := mailMsg.Header.Get("Content-Type")
	if ctype != "" {
		if mediatype, mparams, err := mime.ParseMediaType(ctype); err == nil {
			if mparams["charset"] != "" {
				// Convert plain text to UTF8 if content type specified a charset
				newStr, err := ConvertToUTF8String(mparams["charset"], bodyBytes)
				if err != nil {
					return err
				}
				mimeMsg.Text = newStr
			} else if mediatype == "text/html" {
				// charset is empty, look in HTML body for charset
				charset, err := charsetFromHTMLString(mimeMsg.Text)
				if charset != "" && err == nil {
					newStr, err := ConvertToUTF8String(charset, bodyBytes)
					if err == nil {
						mimeMsg.Text = newStr
					}
				}
			}
			if mediatype == "text/html" {
				mimeMsg.HTML = mimeMsg.Text
				// Empty Text should trigger html2text conversion
				mimeMsg.Text = ""
			}
		}
	}

	return nil
}

// parseBinaryOnlyBody parses a message where the only content is a binary attachment with no
// other parts. The result is placed in mimeMsg.
func parseBinaryOnlyBody(mailMsg *mail.Message, mimeMsg *MIMEBody) error {
	// Determine mediatype
	ctype := mailMsg.Header.Get("Content-Type")
	mediatype, mparams, err := mime.ParseMediaType(ctype)
	if err != nil {
		mediatype = "attachment"
	}

	// Build the MIME part representing most of this message
	p := NewMIMEPart(nil, mediatype)
	content, err := decodeSection(mailMsg.Header.Get("Content-Transfer-Encoding"), mailMsg.Body)
	if err != nil {
		return err
	}
	p.SetContent(content)
	p.SetHeader(make(textproto.MIMEHeader, 4))

	// Determine and set headers for: content disposition, filename and character set
	disposition, dparams, err := mime.ParseMediaType(mailMsg.Header.Get("Content-Disposition"))
	if err == nil {
		// Disposition is optional
		p.SetDisposition(disposition)
		p.SetFileName(DecodeHeader(dparams["filename"]))
	}
	if p.FileName() == "" && mparams["name"] != "" {
		p.SetFileName(DecodeHeader(mparams["name"]))
	}
	if p.FileName() == "" && mparams["file"] != "" {
		p.SetFileName(DecodeHeader(mparams["file"]))
	}
	if p.Charset() == "" {
		p.SetCharset(mparams["charset"])
	}

	p.Header().Set("Content-Type", mailMsg.Header.Get("Content-Type"))
	p.Header().Set("Content-Disposition", mailMsg.Header.Get("Content-Disposition"))

	// Add our part to the appropriate section of MIMEBody
	mimeMsg.Root = NewMIMEPart(nil, mediatype)

	if disposition == "inline" {
		mimeMsg.Inlines = append(mimeMsg.Inlines, p)
	} else {
		mimeMsg.Attachments = append(mimeMsg.Attachments, p)
	}

	return nil
}

// parseMultiPartBody parses a multipart message in mailMsg.  The result is placed in mimeMsg.
func parseMultiPartBody(mailMsg *mail.Message, mimeMsg *MIMEBody) error {
	// Parse top-level multipart
	ctype := mailMsg.Header.Get("Content-Type")
	mediatype, params, err := mime.ParseMediaType(ctype)
	if err != nil {
		return fmt.Errorf("Unable to parse media type: %v", err)
	}
	if !strings.HasPrefix(mediatype, "multipart/") {
		return fmt.Errorf("Unknown mediatype: %v", mediatype)
	}
	boundary := params["boundary"]
	if boundary == "" {
		return fmt.Errorf("Unable to locate boundary param in Content-Type header")
	}
	// Root Node of our tree
	root := NewMIMEPart(nil, mediatype)
	mimeMsg.Root = root
	err = parseParts(root, mailMsg.Body, boundary)
	if err != nil {
		return err
	}

	// Locate text body
	if mediatype == "multipart/altern" {
		match := BreadthMatchFirst(root, func(p MIMEPart) bool {
			return p.ContentType() == "text/plain" && p.Disposition() != "attachment"
		})
		if match != nil {
			if match.Charset() != "" {
				newStr, err := ConvertToUTF8String(match.Charset(), match.Content())
				if err != nil {
					return err
				}
				mimeMsg.Text += newStr
			} else {
				mimeMsg.Text += string(match.Content())
			}
		}
	} else {
		// multipart is of a mixed type
		match := DepthMatchAll(root, func(p MIMEPart) bool {
			return p.ContentType() == "text/plain" && p.Disposition() != "attachment"
		})
		for i, m := range match {
			if i > 0 {
				mimeMsg.Text += "\n--\n"
			}
			if m.Charset() != "" {
				newStr, err := ConvertToUTF8String(m.Charset(), m.Content())
				if err != nil {
					return err
				}
				mimeMsg.Text += newStr
			} else {
				mimeMsg.Text += string(m.Content())
			}
		}
	}

	// Locate HTML body
	match := BreadthMatchFirst(root, func(p MIMEPart) bool {
		return p.ContentType() == "text/html" && p.Disposition() != "attachment"
	})
	if match != nil {
		if match.Charset() != "" {
			newStr, err := ConvertToUTF8String(match.Charset(), match.Content())
			if err != nil {
				return err
			}
			mimeMsg.HTML += newStr
		} else {
			mimeMsg.HTML = string(match.Content())
		}

	}

	// Locate attachments
	mimeMsg.Attachments = BreadthMatchAll(root, func(p MIMEPart) bool {
		return p.Disposition() == "attachment" || p.ContentType() == "application/octet-stream"
	})

	// Locate inlines
	mimeMsg.Inlines = BreadthMatchAll(root, func(p MIMEPart) bool {
		return p.Disposition() == "inline"
	})

	// Locate others parts not considered in attachments or inlines
	mimeMsg.OtherParts = BreadthMatchAll(root, func(p MIMEPart) bool {
		if strings.HasPrefix(p.ContentType(), "multipart/") {
			return false
		}
		if p.Disposition() != "" {
			return false
		}
		if p.ContentType() == "application/octet-stream" {
			return false
		}
		return p.ContentType() != "text/plain" && p.ContentType() != "text/html"
	})

	return nil
}

// GetHeader processes the specified header for RFC 2047 encoded words and return the result
func (m *MIMEBody) GetHeader(name string) string {
	return DecodeHeader(m.header.Get(name))
}

// AddressList returns a mail.Address slice with RFC 2047 encoded encoded names.
func (m *MIMEBody) AddressList(key string) ([]*mail.Address, error) {
	isAddrHeader := false
	for _, hkey := range AddressHeaders {
		if strings.ToLower(hkey) == strings.ToLower(key) {
			isAddrHeader = true
			break
		}
	}
	if !isAddrHeader {
		return nil, fmt.Errorf("%s is not address header", key)
	}

	str := DecodeToUTF8Base64Header(m.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)
	if err != nil {
		return nil, err
	}
	return ret, nil
}
<commit_msg>mail.go tweaks<commit_after>package enmime

import (
	"fmt"
	"github.com/jaytaylor/html2text"
	"mime"
	"net/mail"
	"net/textproto"
	"strings"
)

// MIMEBody is the outer wrapper for MIME messages.
type MIMEBody struct {
	Text           string      // The plain text portion of the message
	HTML           string      // The HTML portion of the message
	IsTextFromHTML bool        // Plain text was empty; down-converted HTML
	Root           MIMEPart    // The top-level MIMEPart
	Attachments    []MIMEPart  // All parts having a Content-Disposition of attachment
	Inlines        []MIMEPart  // All parts having a Content-Disposition of inline
	OtherParts     []MIMEPart  // All parts not in Attachments and Inlines
	header         mail.Header // Header from original message
}

// AddressHeaders enumerates SMTP headers that contain email addresses
var AddressHeaders = []string{"From", "To", "Delivered-To", "Cc", "Bcc", "Reply-To"}

// IsMultipartMessage returns true if the message has a recognized multipart Content-Type header.
// You don't need to check this before calling ParseMIMEBody, it can handle non-multipart messages.
func IsMultipartMessage(mailMsg *mail.Message) bool {
	// Parse top-level multipart
	ctype := mailMsg.Header.Get("Content-Type")
	mediatype, _, err := mime.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, "multipart/")
}

// IsAttachment returns true, if the given header defines an attachment.  First it checks if the
// Content-Disposition header defines an attachement or inline attachment. If this test is false,
// the Content-Type header is checked for attachment, but not inline.  Email clients use inline for
// their text bodies.
//
// Valid Attachment-Headers:
//
//  - Content-Disposition: attachment; filename="frog.jpg"
//  - Content-Disposition: inline; filename="frog.jpg"
//  - Content-Type: attachment; filename="frog.jpg"
func IsAttachment(header mail.Header) bool {
	mediatype, _, _ := mime.ParseMediaType(header.Get("Content-Disposition"))
	if strings.ToLower(mediatype) == "attachment" ||
		strings.ToLower(mediatype) == "inline" {
		return true
	}

	mediatype, _, _ = mime.ParseMediaType(header.Get("Content-Type"))
	if strings.ToLower(mediatype) == "attachment" {
		return true
	}

	return false
}

// IsPlain returns true, if the the MIME headers define a valid 'text/plain' or 'text/html' part. If
// the emptyContentTypeIsPlain argument is set to true, a missing Content-Type header will result in
// a positive plain part detection.
func IsPlain(header mail.Header, emptyContentTypeIsPlain bool) bool {
	ctype := header.Get("Content-Type")
	if ctype == "" && emptyContentTypeIsPlain {
		return true
	}

	mediatype, _, err := mime.ParseMediaType(ctype)
	if err != nil {
		return false
	}
	switch mediatype {
	case "text/plain",
		"text/html":
		return true
	}

	return false

}

// IsBinaryBody returns true if the mail header defines a binary body.
func IsBinaryBody(mailMsg *mail.Message) bool {
	if IsAttachment(mailMsg.Header) == true {
		return true
	}

	return !IsPlain(mailMsg.Header, true)
}

// ParseMIMEBody parses the body of the message object into a  tree of MIMEPart objects, each of
// which is aware of its content type, filename and headers.  If the part was encoded in
// quoted-printable or base64, it is decoded before being stored in the MIMEPart object.
func ParseMIMEBody(mailMsg *mail.Message) (*MIMEBody, error) {
	mimeMsg := &MIMEBody{
		IsTextFromHTML: false,
		header:         mailMsg.Header,
	}

	if IsMultipartMessage(mailMsg) {
		// Multi-part message (message with attachments, etc)
		if err := parseMultiPartBody(mailMsg, mimeMsg); err != nil {
			return nil, err
		}
	} else {
		if IsBinaryBody(mailMsg) {
			// Attachment only, no text
			if err := parseBinaryOnlyBody(mailMsg, mimeMsg); err != nil {
				return nil, err
			}
		}
		// Only text, no attachments
		if err := parseTextOnlyBody(mailMsg, mimeMsg); err != nil {
			return nil, err
		}
	}

	// Down-convert HTML to text if necessary
	if mimeMsg.Text == "" && mimeMsg.HTML != "" {
		mimeMsg.IsTextFromHTML = true
		var err error
		if mimeMsg.Text, err = html2text.FromString(mimeMsg.HTML); err != nil {
			// Fail gently
			mimeMsg.Text = ""
			return mimeMsg, err
		}
	}

	return mimeMsg, nil
}

// parseTextOnlyBody parses a plain text message in mailMsg that has MIME-like headers, but
// only contains a single part - no boundaries, etc.  The result is placed in mimeMsg.
func parseTextOnlyBody(mailMsg *mail.Message, mimeMsg *MIMEBody) error {
	bodyBytes, err := decodeSection(
		mailMsg.Header.Get("Content-Transfer-Encoding"), mailMsg.Body)
	if err != nil {
		return fmt.Errorf("Error decoding text-only message: %v", err)
	}

	// Handle plain ASCII text, content-type unspecified, may be reverted later
	mimeMsg.Text = string(bodyBytes)

	// Process top-level content-type
	ctype := mailMsg.Header.Get("Content-Type")
	if ctype != "" {
		if mediatype, mparams, err := mime.ParseMediaType(ctype); err == nil {
			if mparams["charset"] != "" {
				// Convert plain text to UTF8 if content type specified a charset
				newStr, err := ConvertToUTF8String(mparams["charset"], bodyBytes)
				if err != nil {
					return err
				}
				mimeMsg.Text = newStr
			} else if mediatype == "text/html" {
				// charset is empty, look in HTML body for charset
				charset, err := charsetFromHTMLString(mimeMsg.Text)
				if charset != "" && err == nil {
					newStr, err := ConvertToUTF8String(charset, bodyBytes)
					if err == nil {
						mimeMsg.Text = newStr
					}
				}
			}
			if mediatype == "text/html" {
				mimeMsg.HTML = mimeMsg.Text
				// Empty Text should trigger html2text conversion
				mimeMsg.Text = ""
			}
		}
	}

	return nil
}

// parseBinaryOnlyBody parses a message where the only content is a binary attachment with no
// other parts. The result is placed in mimeMsg.
func parseBinaryOnlyBody(mailMsg *mail.Message, mimeMsg *MIMEBody) error {
	// Determine mediatype
	ctype := mailMsg.Header.Get("Content-Type")
	mediatype, mparams, err := mime.ParseMediaType(ctype)
	if err != nil {
		mediatype = "attachment"
	}

	// Build the MIME part representing most of this message
	p := NewMIMEPart(nil, mediatype)
	content, err := decodeSection(mailMsg.Header.Get("Content-Transfer-Encoding"), mailMsg.Body)
	if err != nil {
		return err
	}
	p.SetContent(content)
	p.SetHeader(make(textproto.MIMEHeader, 4))

	// Determine and set headers for: content disposition, filename and character set
	disposition, dparams, err := mime.ParseMediaType(mailMsg.Header.Get("Content-Disposition"))
	if err == nil {
		// Disposition is optional
		p.SetDisposition(disposition)
		p.SetFileName(DecodeHeader(dparams["filename"]))
	}
	if p.FileName() == "" && mparams["name"] != "" {
		p.SetFileName(DecodeHeader(mparams["name"]))
	}
	if p.FileName() == "" && mparams["file"] != "" {
		p.SetFileName(DecodeHeader(mparams["file"]))
	}
	if p.Charset() == "" {
		p.SetCharset(mparams["charset"])
	}

	p.Header().Set("Content-Type", mailMsg.Header.Get("Content-Type"))
	p.Header().Set("Content-Disposition", mailMsg.Header.Get("Content-Disposition"))

	// Add our part to the appropriate section of MIMEBody
	mimeMsg.Root = NewMIMEPart(nil, mediatype)

	if disposition == "inline" {
		mimeMsg.Inlines = append(mimeMsg.Inlines, p)
	} else {
		mimeMsg.Attachments = append(mimeMsg.Attachments, p)
	}

	return nil
}

// parseMultiPartBody parses a multipart message in mailMsg.  The result is placed in mimeMsg.
func parseMultiPartBody(mailMsg *mail.Message, mimeMsg *MIMEBody) error {
	// Parse top-level multipart
	ctype := mailMsg.Header.Get("Content-Type")
	mediatype, params, err := mime.ParseMediaType(ctype)
	if err != nil {
		return fmt.Errorf("Unable to parse media type: %v", err)
	}
	if !strings.HasPrefix(mediatype, "multipart/") {
		return fmt.Errorf("Unknown mediatype: %v", mediatype)
	}
	boundary := params["boundary"]
	if boundary == "" {
		return fmt.Errorf("Unable to locate boundary param in Content-Type header")
	}
	// Root Node of our tree
	root := NewMIMEPart(nil, mediatype)
	mimeMsg.Root = root
	err = parseParts(root, mailMsg.Body, boundary)
	if err != nil {
		return err
	}

	// Locate text body
	if mediatype == "multipart/altern" {
		match := BreadthMatchFirst(root, func(p MIMEPart) bool {
			return p.ContentType() == "text/plain" && p.Disposition() != "attachment"
		})
		if match != nil {
			if match.Charset() != "" {
				newStr, err := ConvertToUTF8String(match.Charset(), match.Content())
				if err != nil {
					return err
				}
				mimeMsg.Text += newStr
			} else {
				mimeMsg.Text += string(match.Content())
			}
		}
	} else {
		// multipart is of a mixed type
		match := DepthMatchAll(root, func(p MIMEPart) bool {
			return p.ContentType() == "text/plain" && p.Disposition() != "attachment"
		})
		for i, m := range match {
			if i > 0 {
				mimeMsg.Text += "\n--\n"
			}
			if m.Charset() != "" {
				newStr, err := ConvertToUTF8String(m.Charset(), m.Content())
				if err != nil {
					return err
				}
				mimeMsg.Text += newStr
			} else {
				mimeMsg.Text += string(m.Content())
			}
		}
	}

	// Locate HTML body
	match := BreadthMatchFirst(root, func(p MIMEPart) bool {
		return p.ContentType() == "text/html" && p.Disposition() != "attachment"
	})
	if match != nil {
		if match.Charset() != "" {
			newStr, err := ConvertToUTF8String(match.Charset(), match.Content())
			if err != nil {
				return err
			}
			mimeMsg.HTML += newStr
		} else {
			mimeMsg.HTML = string(match.Content())
		}

	}

	// Locate attachments
	mimeMsg.Attachments = BreadthMatchAll(root, func(p MIMEPart) bool {
		return p.Disposition() == "attachment" || p.ContentType() == "application/octet-stream"
	})

	// Locate inlines
	mimeMsg.Inlines = BreadthMatchAll(root, func(p MIMEPart) bool {
		return p.Disposition() == "inline"
	})

	// Locate others parts not considered in attachments or inlines
	mimeMsg.OtherParts = BreadthMatchAll(root, func(p MIMEPart) bool {
		if strings.HasPrefix(p.ContentType(), "multipart/") {
			return false
		}
		if p.Disposition() != "" {
			return false
		}
		if p.ContentType() == "application/octet-stream" {
			return false
		}
		return p.ContentType() != "text/plain" && p.ContentType() != "text/html"
	})

	return nil
}

// GetHeader processes the specified header for RFC 2047 encoded words and return the result
func (m *MIMEBody) GetHeader(name string) string {
	return DecodeHeader(m.header.Get(name))
}

// AddressList returns a mail.Address slice with RFC 2047 encoded encoded names.
func (m *MIMEBody) AddressList(key string) ([]*mail.Address, error) {
	isAddrHeader := false
	for _, hkey := range AddressHeaders {
		if strings.ToLower(hkey) == strings.ToLower(key) {
			isAddrHeader = true
			break
		}
	}
	if !isAddrHeader {
		return nil, fmt.Errorf("%s is not address header", key)
	}

	str := DecodeToUTF8Base64Header(m.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)
	if err != nil {
		return nil, err
	}
	return ret, nil
}
<|endoftext|>


<commit_before>// Package mail implements a parser for electronic mail messages as specified
// in RFC5322.
//
// We allow both CRLF and LF to be used in the input, possibly mixed.
package mail

import (
	"bytes"
	"encoding/base64"
	"errors"
	"strings"
	"time"
)

var benc = base64.URLEncoding

type Message struct {
	FullHeaders []Header // all headers
	OptHeaders  []Header // unprocessed headers

	MessageId   string
	Id          string
	Date        time.Time
	From        []Address
	Sender      Address
	ReplyTo     []Address
	To          []Address
	Cc          []Address
	Bcc         []Address
	Subject     string
	Comments    []string
	Keywords    []string

	InReply     []string
	References  []string

	Text        string
}

type Header struct {
	Key, Value string
}

func Parse(s []byte) (m Message, e error) {
	r, e := ParseRaw(s)
	if e != nil {
		return
	}
	return Process(r)
}

func trunc(s string, i int) string {
	if len(s) < i {
		return s
	}
	return s[0:i]
}

func Process(r RawMessage) (m Message, e error) {
	m.FullHeaders = []Header{}
	m.OptHeaders = []Header{}
	m.Text = string(r.Body) // TODO mime
	for _, rh := range r.RawHeaders {
		h := Header{string(rh.Key), string(rh.Value)}
		m.FullHeaders = append(m.FullHeaders, h)
		switch string(rh.Key) {
		case `Message-ID`:
			v := bytes.Trim(rh.Value, `<>`)
			m.MessageId = string(v)
			m.Id = trunc(benc.EncodeToString(v), 64)
		case `In-Reply-To`:
			ids := strings.Split(string(rh.Value), ` `)
			for _, id := range ids {
				m.InReply = append(m.InReply, strings.Trim(id, `<> `))
			}
		case `References`:
			ids := strings.Split(string(rh.Value), ` `)
			for _, id := range ids {
				m.References = append(m.References, strings.Trim(id, `<> `))
			}
		case `Date`:
			m.Date = ParseDate(string(rh.Value))
		case `From`:
			m.From, e = parseAddressList(rh.Value)
		case `Sender`:
			m.Sender, e = ParseAddress(rh.Value)
		case `Reply-To`:
			m.ReplyTo, e = parseAddressList(rh.Value)
		case `To`:
			m.To, e = parseAddressList(rh.Value)
		case `Cc`:
			m.Cc, e = parseAddressList(rh.Value)
		case `Bcc`:
			m.Bcc, e = parseAddressList(rh.Value)
		case `Subject`:
			m.Subject = string(rh.Value)
		case `Comments`:
			m.Comments = append(m.Comments, string(rh.Value))
		case `Keywords`:
			ks := strings.Split(string(rh.Value), ",")
			for _, k := range ks {
				m.Keywords = append(m.Keywords, strings.TrimSpace(k))
			}
		default:
			m.OptHeaders = append(m.OptHeaders, h)
		}
		if e != nil {
			return
		}
	}
	if m.Sender == nil && len(m.From) > 0 {
		m.Sender = m.From[0]
	}
	return
}

type RawHeader struct {
	Key, Value []byte
}

type RawMessage struct {
	RawHeaders []RawHeader
	Body       []byte
}

func isWSP(b byte) bool {
	return b == ' ' || b == '\t'
}

func ParseRaw(s []byte) (m RawMessage, e error) {
	// parser states
	const (
		READY = iota
		HKEY
		HVWS
		HVAL
	)

	const (
		CR = '\r'
		LF = '\n'
	)
	CRLF := []byte{CR, LF}

	state := READY
	kstart, kend, vstart := 0, 0, 0
	done := false

	m.RawHeaders = []RawHeader{}

	for i := 0; i < len(s); i++ {
		b := s[i]
		switch state {
		case READY:
			if b == CR && i < len(s)-1 && s[i+1] == LF {
				// we are at the beginning of an empty header
				m.Body = s[i+2:]
				done = true
				goto Done
			}
			if b == LF {
				m.Body = s[i+1:]
				done = true
				goto Done
			}
			// otherwise this character is the first in a header
			// key
			kstart = i
			state = HKEY
		case HKEY:
			if b == ':' {
				kend = i
				state = HVWS
			}
		case HVWS:
			if !isWSP(b) {
				vstart = i
				state = HVAL
			}
		case HVAL:
			if b == CR && i < len(s)-2 && s[i+1] == LF && !isWSP(s[i+2]) {
				v := bytes.Replace(s[vstart:i], CRLF, nil, -1)
				hdr := RawHeader{s[kstart:kend], v}
				m.RawHeaders = append(m.RawHeaders, hdr)
				state = READY
				i++
			} else if b == LF && i < len(s)-1 && !isWSP(s[i+1]) {
				v := bytes.Replace(s[vstart:i], CRLF, nil, -1)
				hdr := RawHeader{s[kstart:kend], v}
				m.RawHeaders = append(m.RawHeaders, hdr)
				state = READY
			}
		}
	}
Done:
	if !done {
		e = errors.New("unexpected EOF")
	}
	return
}
<commit_msg>Short IDs<commit_after>// Package mail implements a parser for electronic mail messages as specified
// in RFC5322.
//
// We allow both CRLF and LF to be used in the input, possibly mixed.
package mail

import (
	"bytes"
	"crypto/sha1"
	"encoding/base64"
	"errors"
	"strings"
	"time"
)

var benc = base64.URLEncoding

func mkId(s []byte) string {
	return benc.EncodeToString(sha1.New().Sum(s))[0:20]
}

type Message struct {
	FullHeaders []Header // all headers
	OptHeaders  []Header // unprocessed headers

	MessageId   string
	Id          string
	Date        time.Time
	From        []Address
	Sender      Address
	ReplyTo     []Address
	To          []Address
	Cc          []Address
	Bcc         []Address
	Subject     string
	Comments    []string
	Keywords    []string

	InReply     []string
	References  []string

	Text        string
}

type Header struct {
	Key, Value string
}

func Parse(s []byte) (m Message, e error) {
	r, e := ParseRaw(s)
	if e != nil {
		return
	}
	return Process(r)
}

func Process(r RawMessage) (m Message, e error) {
	m.FullHeaders = []Header{}
	m.OptHeaders = []Header{}
	m.Text = string(r.Body) // TODO mime
	for _, rh := range r.RawHeaders {
		h := Header{string(rh.Key), string(rh.Value)}
		m.FullHeaders = append(m.FullHeaders, h)
		switch string(rh.Key) {
		case `Message-ID`:
			v := bytes.Trim(rh.Value, `<>`)
			m.MessageId = string(v)
			m.Id = mkId(v)
		case `In-Reply-To`:
			ids := strings.Split(string(rh.Value), ` `)
			for _, id := range ids {
				m.InReply = append(m.InReply, strings.Trim(id, `<> `))
			}
		case `References`:
			ids := strings.Split(string(rh.Value), ` `)
			for _, id := range ids {
				m.References = append(m.References, strings.Trim(id, `<> `))
			}
		case `Date`:
			m.Date = ParseDate(string(rh.Value))
		case `From`:
			m.From, e = parseAddressList(rh.Value)
		case `Sender`:
			m.Sender, e = ParseAddress(rh.Value)
		case `Reply-To`:
			m.ReplyTo, e = parseAddressList(rh.Value)
		case `To`:
			m.To, e = parseAddressList(rh.Value)
		case `Cc`:
			m.Cc, e = parseAddressList(rh.Value)
		case `Bcc`:
			m.Bcc, e = parseAddressList(rh.Value)
		case `Subject`:
			m.Subject = string(rh.Value)
		case `Comments`:
			m.Comments = append(m.Comments, string(rh.Value))
		case `Keywords`:
			ks := strings.Split(string(rh.Value), ",")
			for _, k := range ks {
				m.Keywords = append(m.Keywords, strings.TrimSpace(k))
			}
		default:
			m.OptHeaders = append(m.OptHeaders, h)
		}
		if e != nil {
			return
		}
	}
	if m.Sender == nil && len(m.From) > 0 {
		m.Sender = m.From[0]
	}
	return
}

type RawHeader struct {
	Key, Value []byte
}

type RawMessage struct {
	RawHeaders []RawHeader
	Body       []byte
}

func isWSP(b byte) bool {
	return b == ' ' || b == '\t'
}

func ParseRaw(s []byte) (m RawMessage, e error) {
	// parser states
	const (
		READY = iota
		HKEY
		HVWS
		HVAL
	)

	const (
		CR = '\r'
		LF = '\n'
	)
	CRLF := []byte{CR, LF}

	state := READY
	kstart, kend, vstart := 0, 0, 0
	done := false

	m.RawHeaders = []RawHeader{}

	for i := 0; i < len(s); i++ {
		b := s[i]
		switch state {
		case READY:
			if b == CR && i < len(s)-1 && s[i+1] == LF {
				// we are at the beginning of an empty header
				m.Body = s[i+2:]
				done = true
				goto Done
			}
			if b == LF {
				m.Body = s[i+1:]
				done = true
				goto Done
			}
			// otherwise this character is the first in a header
			// key
			kstart = i
			state = HKEY
		case HKEY:
			if b == ':' {
				kend = i
				state = HVWS
			}
		case HVWS:
			if !isWSP(b) {
				vstart = i
				state = HVAL
			}
		case HVAL:
			if b == CR && i < len(s)-2 && s[i+1] == LF && !isWSP(s[i+2]) {
				v := bytes.Replace(s[vstart:i], CRLF, nil, -1)
				hdr := RawHeader{s[kstart:kend], v}
				m.RawHeaders = append(m.RawHeaders, hdr)
				state = READY
				i++
			} else if b == LF && i < len(s)-1 && !isWSP(s[i+1]) {
				v := bytes.Replace(s[vstart:i], CRLF, nil, -1)
				hdr := RawHeader{s[kstart:kend], v}
				m.RawHeaders = append(m.RawHeaders, hdr)
				state = READY
			}
		}
	}
Done:
	if !done {
		e = errors.New("unexpected EOF")
	}
	return
}
<|endoftext|>


<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package lxc

import (
	"fmt"

	"launchpad.net/juju-core/instance"
)

type lxcInstance struct {
	id string
}

var _ instance.Instance = (*lxcInstance)(nil)

// Id implements instance.Instance.Id.
func (lxc *lxcInstance) Id() instance.Id {
	return instance.Id(lxc.id)
}

// DNSName implements instance.Instance.DNSName.
func (lxc *lxcInstance) DNSName() (string, error) {
	return "", instance.ErrNoDNSName
}

// WaitDNSName implements instance.Instance.WaitDNSName.
func (lxc *lxcInstance) WaitDNSName() (string, error) {
	return "", instance.ErrNoDNSName
}

// OpenPorts implements instance.Instance.OpenPorts.
func (lxc *lxcInstance) OpenPorts(machineId string, ports []instance.Port) error {
	return fmt.Errorf("not implemented")
}

// ClosePorts implements instance.Instance.ClosePorts.
func (lxc *lxcInstance) ClosePorts(machineId string, ports []instance.Port) error {
	return fmt.Errorf("not implemented")
}

// Ports implements instance.Instance.Ports.
func (lxc *lxcInstance) Ports(machineId string) ([]instance.Port, error) {
	return nil, fmt.Errorf("not implemented")
}

// Metadata implements instance.Instance.Metadata.
func (lxc *lxcInstance) Metadata() *instance.Metadata {
	return nil
}
<commit_msg>Remove the instance Metadata again.<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package lxc

import (
	"fmt"

	"launchpad.net/juju-core/instance"
)

type lxcInstance struct {
	id string
}

var _ instance.Instance = (*lxcInstance)(nil)

// Id implements instance.Instance.Id.
func (lxc *lxcInstance) Id() instance.Id {
	return instance.Id(lxc.id)
}

// DNSName implements instance.Instance.DNSName.
func (lxc *lxcInstance) DNSName() (string, error) {
	return "", instance.ErrNoDNSName
}

// WaitDNSName implements instance.Instance.WaitDNSName.
func (lxc *lxcInstance) WaitDNSName() (string, error) {
	return "", instance.ErrNoDNSName
}

// OpenPorts implements instance.Instance.OpenPorts.
func (lxc *lxcInstance) OpenPorts(machineId string, ports []instance.Port) error {
	return fmt.Errorf("not implemented")
}

// ClosePorts implements instance.Instance.ClosePorts.
func (lxc *lxcInstance) ClosePorts(machineId string, ports []instance.Port) error {
	return fmt.Errorf("not implemented")
}

// Ports implements instance.Instance.Ports.
func (lxc *lxcInstance) Ports(machineId string) ([]instance.Port, error) {
	return nil, fmt.Errorf("not implemented")
}
<|endoftext|>


<commit_before>// Copyright 2018 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package mesos

import (
	"fmt"
	"github.com/Rican7/retry"
	"github.com/Rican7/retry/strategy"
	"github.com/mesos/mesos-go/api/v1/lib"
	"github.com/mesos/mesos-go/api/v1/lib/agent"
	"github.com/mesos/mesos-go/api/v1/lib/agent/calls"
	mclient "github.com/mesos/mesos-go/api/v1/lib/client"
	"github.com/mesos/mesos-go/api/v1/lib/encoding/codecs"
	"github.com/mesos/mesos-go/api/v1/lib/httpcli"
	"net/url"
	"sync"
)

const (
	maxRetryAttempts = 3
	invalidPID       = -1
)

var (
	mesosClientOnce sync.Once
	mesosClient     *client
)

type client struct {
	hc *httpcli.Client
}

type mesosAgentClient interface {
	ContainerInfo(id string) (*containerInfo, error)
	ContainerPid(id string) (int, error)
}

type containerInfo struct {
	cntr   *mContainer
	labels map[string]string
}

// Client is an interface to query mesos agent http endpoints
func Client() (mesosAgentClient, error) {
	mesosClientOnce.Do(func() {
		// Start Client
		apiURL := url.URL{
			Scheme: "http",
			Host:   *MesosAgentAddress,
			Path:   "/api/v1",
		}

		mesosClient = &client{
			hc: httpcli.New(
				httpcli.Endpoint(apiURL.String()),
				httpcli.Codec(codecs.ByMediaType[codecs.MediaTypeProtobuf]),
				httpcli.Do(httpcli.With(httpcli.Timeout(*MesosAgentTimeout))),
			),
		}
	})
	return mesosClient, nil
}

// ContainerInfo returns the container information of the given container id
func (self *client) ContainerInfo(id string) (*containerInfo, error) {
	c, err := self.getContainer(id)
	if err != nil {
		return nil, err
	}

	// Get labels of the container
	l, err := self.getLabels(c)
	if err != nil {
		return nil, err
	}

	return &containerInfo{
		cntr:   c,
		labels: l,
	}, nil
}

// Get the Pid of the container
func (self *client) ContainerPid(id string) (int, error) {
	var pid int
	var err error
	err = retry.Retry(
		func(attempt uint) error {
			c, err := self.ContainerInfo(id)
			if err != nil {
				return err
			}

			if c.cntr.ContainerStatus != nil {
				pid = int(*c.cntr.ContainerStatus.ExecutorPID)
			} else {
				err = fmt.Errorf("error fetching Pid")
			}
			return err
		},
		strategy.Limit(maxRetryAttempts),
	)
	if err != nil {
		return invalidPID, fmt.Errorf("failed to fetch pid")
	}
	return pid, err
}

func (self *client) getContainer(id string) (*mContainer, error) {
	// Get all containers
	cntrs, err := self.getContainers()
	if err != nil {
		return nil, err
	}

	// Check if there is a container with given id and return the container
	for _, c := range cntrs.Containers {
		if c.ContainerID.Value == id {
			return &c, nil
		}
	}
	return nil, fmt.Errorf("can't locate container %s", id)
}

func (self *client) getContainers() (mContainers, error) {
	req := calls.NonStreaming(calls.GetContainers())
	result, err := self.fetchAndDecode(req)
	if err != nil {
		return nil, fmt.Errorf("failed to get mesos containers: %v", err)
	}
	cntrs := result.GetContainers
	return cntrs, nil
}

func (self *client) getLabels(c *mContainer) (map[string]string, error) {
	// Get mesos agent state which contains all containers labels
	var s state
	req := calls.NonStreaming(calls.GetState())
	result, err := self.fetchAndDecode(req)
	if err != nil {
		return map[string]string{}, fmt.Errorf("failed to get mesos agent state: %v", err)
	}
	s.st = result.GetState

	// Fetch labels from state object
	labels, err := s.FetchLabels(c.FrameworkID.Value, c.ExecutorID.Value)
	if err != nil {
		return labels, fmt.Errorf("error while fetching labels from executor: %v", err)
	}

	return labels, nil
}

func (self *client) fetchAndDecode(req calls.RequestFunc) (*agent.Response, error) {
	var res mesos.Response
	var err error

	// Send request
	err = retry.Retry(
		func(attempt uint) error {
			res, err = mesosClient.hc.Send(req, mclient.ResponseClassSingleton, nil)
			return err
		},
		strategy.Limit(maxRetryAttempts),
	)
	if err != nil {
		return nil, fmt.Errorf("error fetching %s: %s", req.Call(), err)
	}

	// Decode the result
	var target agent.Response
	err = res.Decode(&target)
	if err != nil {
		return nil, fmt.Errorf("error while decoding response body from %s: %s", res, err)
	}

	return &target, nil
}
<commit_msg>Ensure that empty mesos containers reference as a failure to fetch containers<commit_after>// Copyright 2018 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package mesos

import (
	"fmt"
	"github.com/Rican7/retry"
	"github.com/Rican7/retry/strategy"
	"github.com/mesos/mesos-go/api/v1/lib"
	"github.com/mesos/mesos-go/api/v1/lib/agent"
	"github.com/mesos/mesos-go/api/v1/lib/agent/calls"
	mclient "github.com/mesos/mesos-go/api/v1/lib/client"
	"github.com/mesos/mesos-go/api/v1/lib/encoding/codecs"
	"github.com/mesos/mesos-go/api/v1/lib/httpcli"
	"net/url"
	"sync"
)

const (
	maxRetryAttempts = 3
	invalidPID       = -1
)

var (
	mesosClientOnce sync.Once
	mesosClient     *client
)

type client struct {
	hc *httpcli.Client
}

type mesosAgentClient interface {
	ContainerInfo(id string) (*containerInfo, error)
	ContainerPid(id string) (int, error)
}

type containerInfo struct {
	cntr   *mContainer
	labels map[string]string
}

// Client is an interface to query mesos agent http endpoints
func Client() (mesosAgentClient, error) {
	mesosClientOnce.Do(func() {
		// Start Client
		apiURL := url.URL{
			Scheme: "http",
			Host:   *MesosAgentAddress,
			Path:   "/api/v1",
		}

		mesosClient = &client{
			hc: httpcli.New(
				httpcli.Endpoint(apiURL.String()),
				httpcli.Codec(codecs.ByMediaType[codecs.MediaTypeProtobuf]),
				httpcli.Do(httpcli.With(httpcli.Timeout(*MesosAgentTimeout))),
			),
		}
	})
	return mesosClient, nil
}

// ContainerInfo returns the container information of the given container id
func (self *client) ContainerInfo(id string) (*containerInfo, error) {
	c, err := self.getContainer(id)
	if err != nil {
		return nil, err
	}

	// Get labels of the container
	l, err := self.getLabels(c)
	if err != nil {
		return nil, err
	}

	return &containerInfo{
		cntr:   c,
		labels: l,
	}, nil
}

// Get the Pid of the container
func (self *client) ContainerPid(id string) (int, error) {
	var pid int
	var err error
	err = retry.Retry(
		func(attempt uint) error {
			c, err := self.ContainerInfo(id)
			if err != nil {
				return err
			}

			if c.cntr.ContainerStatus != nil {
				pid = int(*c.cntr.ContainerStatus.ExecutorPID)
			} else {
				err = fmt.Errorf("error fetching Pid")
			}
			return err
		},
		strategy.Limit(maxRetryAttempts),
	)
	if err != nil {
		return invalidPID, fmt.Errorf("failed to fetch pid")
	}
	return pid, err
}

func (self *client) getContainer(id string) (*mContainer, error) {
	// Get all containers
	cntrs, err := self.getContainers()
	if err != nil {
		return nil, err
	}

	// Check if there is a container with given id and return the container
	for _, c := range cntrs.Containers {
		if c.ContainerID.Value == id {
			return &c, nil
		}
	}
	return nil, fmt.Errorf("can't locate container %s", id)
}

func (self *client) getContainers() (mContainers, error) {
	req := calls.NonStreaming(calls.GetContainers())
	result, err := self.fetchAndDecode(req)
	if err != nil {
		return nil, fmt.Errorf("failed to get mesos containers: %v", err)
	}
	cntrs := result.GetContainers

	if cntrs == nil {
		return nil, fmt.Errorf("failed to get mesos containers")
	}
	return cntrs, nil
}

func (self *client) getLabels(c *mContainer) (map[string]string, error) {
	// Get mesos agent state which contains all containers labels
	var s state
	req := calls.NonStreaming(calls.GetState())
	result, err := self.fetchAndDecode(req)
	if err != nil {
		return map[string]string{}, fmt.Errorf("failed to get mesos agent state: %v", err)
	}
	s.st = result.GetState

	// Fetch labels from state object
	labels, err := s.FetchLabels(c.FrameworkID.Value, c.ExecutorID.Value)
	if err != nil {
		return labels, fmt.Errorf("error while fetching labels from executor: %v", err)
	}

	return labels, nil
}

func (self *client) fetchAndDecode(req calls.RequestFunc) (*agent.Response, error) {
	var res mesos.Response
	var err error

	// Send request
	err = retry.Retry(
		func(attempt uint) error {
			res, err = mesosClient.hc.Send(req, mclient.ResponseClassSingleton, nil)
			return err
		},
		strategy.Limit(maxRetryAttempts),
	)
	if err != nil {
		return nil, fmt.Errorf("error fetching %s: %s", req.Call(), err)
	}

	// Decode the result
	var target agent.Response
	err = res.Decode(&target)
	if err != nil {
		return nil, fmt.Errorf("error while decoding response body from %s: %s", res, err)
	}

	return &target, nil
}
<|endoftext|>


<commit_before>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package v3

import (
	"bytes"
	"context"
	"fmt"
	"strings"
	"testing"

	"cloud.google.com/go/storage"
	"github.com/GoogleCloudPlatform/golang-samples/internal/testutil"
	"github.com/google/uuid"
)

func TestBatchTranslateText(t *testing.T) {
	tc := testutil.SystemTest(t)

	bucketName := fmt.Sprintf("%s-batch_translate_text-%v", tc.ProjectID, uuid.New().ID())
	location := "us-central1"
	inputURI := "gs://cloud-samples-data/translation/text.txt"
	outputURI := fmt.Sprintf("gs://%s/translation/output/", bucketName)
	sourceLang := "en"
	targetLang := "es"

	// Create a temporary bucket to store annotation output.
	ctx := context.Background()
	client, err := storage.NewClient(ctx)
	if err != nil {
		t.Fatalf("storage.NewClient: %v", err)
	}
	defer client.Close()

	bucket := client.Bucket(bucketName)
	if err := bucket.Create(ctx, tc.ProjectID, nil); err != nil {
		t.Fatalf("bucket.Create: %v", err)
	}
	defer deleteBucket(ctx, t, bucket)

	// Translate a sample text and check the number of translated characters.
	var buf bytes.Buffer
	if err := batchTranslateText(&buf, tc.ProjectID, location, inputURI, outputURI, sourceLang, targetLang); err != nil {
		t.Fatalf("batchTranslateText: %v", err)
	}
	if got, want := buf.String(), "Total characters"; !strings.Contains(got, want) {
		t.Errorf("batchTranslateText got:\n----\n%s----\nWant to contain:\n----\n%s\n----", got, want)
	}
}
<commit_msg>tests(translate): skip flaky TestBatchTranslateText (#1700)<commit_after>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package v3

import (
	"bytes"
	"context"
	"fmt"
	"strings"
	"testing"

	"cloud.google.com/go/storage"
	"github.com/GoogleCloudPlatform/golang-samples/internal/testutil"
	"github.com/google/uuid"
)

func TestBatchTranslateText(t *testing.T) {
	t.Skip("https://github.com/GoogleCloudPlatform/golang-samples/issues/1572")
	tc := testutil.SystemTest(t)

	bucketName := fmt.Sprintf("%s-batch_translate_text-%v", tc.ProjectID, uuid.New().ID())
	location := "us-central1"
	inputURI := "gs://cloud-samples-data/translation/text.txt"
	outputURI := fmt.Sprintf("gs://%s/translation/output/", bucketName)
	sourceLang := "en"
	targetLang := "es"

	// Create a temporary bucket to store annotation output.
	ctx := context.Background()
	client, err := storage.NewClient(ctx)
	if err != nil {
		t.Fatalf("storage.NewClient: %v", err)
	}
	defer client.Close()

	bucket := client.Bucket(bucketName)
	if err := bucket.Create(ctx, tc.ProjectID, nil); err != nil {
		t.Fatalf("bucket.Create: %v", err)
	}
	defer deleteBucket(ctx, t, bucket)

	// Translate a sample text and check the number of translated characters.
	var buf bytes.Buffer
	if err := batchTranslateText(&buf, tc.ProjectID, location, inputURI, outputURI, sourceLang, targetLang); err != nil {
		t.Fatalf("batchTranslateText: %v", err)
	}
	if got, want := buf.String(), "Total characters"; !strings.Contains(got, want) {
		t.Errorf("batchTranslateText got:\n----\n%s----\nWant to contain:\n----\n%s\n----", got, want)
	}
}
<|endoftext|>


<commit_before>package app_helpers

import (
	"strings"
	"time"

	"github.com/cloudfoundry-incubator/cf-test-helpers/cf"
	"github.com/cloudfoundry-incubator/cf-test-helpers/helpers"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gexec"
)

func guidForAppName(appName string) string {
	cfApp := cf.Cf("app", appName, "--guid")
	Expect(cfApp.Wait()).To(Exit(0))

	appGuid := strings.TrimSpace(string(cfApp.Out.Contents()))
	Expect(appGuid).NotTo(Equal(""))
	return appGuid
}

func ConditionallyEnableDiego(appName string) {
	config := helpers.LoadConfig()
	if config.UseDiego {
		guid := guidForAppName(appName)
		Eventually(cf.Cf("curl", "/v2/apps/"+guid, "-X", "PUT", "-d", `{"diego": true}`)).Should(Exit(0))
	}
}

func AppReport(appName string, timeout time.Duration) {
	Eventually(cf.Cf("app", appName, "--guid"), timeout).Should(Exit())
	Eventually(cf.Cf("logs", appName, "--recent"), timeout).Should(Exit())
}
<commit_msg>Replace UseDiego config with Backend.<commit_after>package app_helpers

import (
	"strings"
	"time"

	"github.com/cloudfoundry-incubator/cf-test-helpers/cf"
	"github.com/cloudfoundry-incubator/cf-test-helpers/helpers"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gexec"
)

func guidForAppName(appName string) string {
	cfApp := cf.Cf("app", appName, "--guid")
	Expect(cfApp.Wait()).To(Exit(0))

	appGuid := strings.TrimSpace(string(cfApp.Out.Contents()))
	Expect(appGuid).NotTo(Equal(""))
	return appGuid
}

func ConditionallyEnableDiego(appName string) {
	config := helpers.LoadConfig()
	if config.Backend == "diego" {
		guid := guidForAppName(appName)
		Eventually(cf.Cf("curl", "/v2/apps/"+guid, "-X", "PUT", "-d", `{"diego": true}`)).Should(Exit(0))
	} else if config.Backend == "dea" {
		guid := guidForAppName(appName)
		Eventually(cf.Cf("curl", "/v2/apps/"+guid, "-X", "PUT", "-d", `{"diego": false}`)).Should(Exit(0))
	}
}

func AppReport(appName string, timeout time.Duration) {
	Eventually(cf.Cf("app", appName, "--guid"), timeout).Should(Exit())
	Eventually(cf.Cf("logs", appName, "--recent"), timeout).Should(Exit())
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/chrisseto/pty"
	"github.com/chrisseto/sux/pansi"
	"github.com/nsf/termbox-go"
	"io"
	"os"
	"os/exec"
)

type Pane struct {
	*exec.Cmd

	cx, cy        int
	sx, sy        int
	fg, bg        termbox.Attribute
	width, height uint16
	scrollOffset  int

	Prog string
	Args []string

	Pty    *os.File
	output io.Reader
	cells  [][]termbox.Cell
}

func CreatePane(width, height uint16, prog string, args ...string) *Pane {
	return &Pane{
		Cmd: exec.Command(prog, args...),
		cx:  0, cy: 0,
		fg: 0, bg: 0,
		scrollOffset: 0,
		drawOffset:   0,
		Prog:         prog, Args: args,
		width: width, height: height,
		Pty: nil,
	}
}

func (p *Pane) Start() error {
	pterm, err := pty.Start(p.Cmd)
	if err != nil {
		panic(err)
	}
	if err = pty.Setsize(pterm, p.height, p.width); err != nil {
		panic(err)
	}
	p.Pty = pterm
	p.cells = make([][]termbox.Cell, 1, p.height)
	p.cells[0] = make([]termbox.Cell, p.width)
	go p.outputPipe()
	return nil
}

func (p *Pane) Close() error {
	return p.Process.Kill()
}

func (p *Pane) Cells() [][]termbox.Cell {
	return p.cells[p.drawOffset:bound(p.drawOffset+int(p.height), p.drawOffset, len(p.cells))]
}

func (p *Pane) Width() uint16 {
	return p.width
}

func (p *Pane) Height() uint16 {
	return p.height
}

func (p *Pane) Scroll(far int) {
	// p.scrollOffset += far
	p.scrollOffset = bound(p.scrollOffset+far, -len(p.cells), 0)
	Redraw()
}

func (p *Pane) bottomLine() *[]termbox.Cell {
	return &p.cells[len(p.cells)-1]
}

func (p *Pane) newLine() *[]termbox.Cell {
	p.cy++
	p.cells = append(p.cells, make([]termbox.Cell, p.width))
	if len(p.cells)-p.drawOffset > int(p.height) {
		p.drawOffset++
	}
	return p.bottomLine()
}

func (p *Pane) Redraw() {
	for y, line := range p.Cells() {
		for x, cell := range line {
			termbox.SetCell(x, y, cell.Ch, cell.Fg, cell.Bg)
		}
	}
	termbox.SetCursor(p.Cursor())
}

func bound(val, min, max int) int {
	if val < min {
		return min
	}
	if val > max {
		return max
	}
	return val
}

func (p *Pane) Cursor() (int, int) {
	p.cx = bound(p.cx, 0, int(p.width)-1)
	p.cy = bound(p.cy, 0, int(p.height)-1)
	return p.cx, p.cy
}

func (p *Pane) outputPipe() {
	lexer := pansi.NewLexer()
	buf := make([]byte, 32*1024)
	// f, _ := os.Create("output.log")
	for {
		nr, err := p.Pty.Read(buf)
		if nr > 0 {
			f.Write(buf[:nr])

			for _, char := range buf[:nr] {
				lexer.Feed(char)
				if res := lexer.Result(); res != nil {
					p.handleEscapeCode(res)
					lexer.Clear()
					continue
				}
				if lexer.State() != pansi.Ground {
					continue
				}

				switch char {
				case 0x7: //Terminal Bell. Skip for the moment
				case 0xA:
					row = p.newLine()
				case 0xD:
					x, p.cx = 0, 0
				case 0x8:
					if x != 0 {
						x--
						p.cx--
					}
					(*row)[x] = termbox.Cell{' ', p.fg, p.bg}
				default:
					if x >= int(p.width) {
						row = p.newLine()
						x = 0
					}
					(*row)[x] = termbox.Cell{rune(char), p.fg, p.bg}
					x++
					p.cx++
				}
			}

			Redraw()
		}
		if err != nil {
			if err == io.EOF {
				break
			}
			panic(err)
		}

	}
}

func (p *Pane) handleEscapeCode(c *pansi.AnsiEscapeCode) {
	switch c.Type {
	case pansi.SetGraphicMode:
		p.SetGraphicMode(c.Values)
	case pansi.CursorPosition:
		if len(c.Values) == 0 {
			p.cx, p.cy = 0, 0
		} else {
			p.cx, p.cy = c.Values[0], c.Values[1]
		}
	case pansi.CursorUp:
		p.cy--
	case pansi.CursorDown:
		p.cy++
	case pansi.CursorBackward:
		p.cx--
	case pansi.CursorForward:
		p.cx++
	case pansi.EraseLine:
		row := p.bottomLine()
		for i := p.cx; i < len(*row); i++ {
			(*row)[i] = termbox.Cell{' ', p.fg, p.bg}
		}
	case pansi.EraseDisplay:
		p.Clear()
	}
}

func (p *Pane) SetGraphicMode(vals []int) {
	for i := 0; i < len(vals); i++ {
		switch vals[i] {
		case 0:
			p.fg, p.bg = 8, 1
		case 1:
			p.fg |= termbox.AttrBold
		case 7:
			p.fg, p.bg = p.bg, p.fg
		case 38:
			i++
			switch vals[i] {
			case 5:
				i++
				p.fg = termbox.Attribute(vals[i] + 1)
			case 2:
				i += 3 //TODO
			}
		case 39:
			p.fg = termbox.ColorWhite
		case 48:
			i++
			switch vals[i] {
			case 5:
				i++
				p.bg = termbox.Attribute(vals[i] + 1)
			case 2:
				i += 3 //TODO
			}
		case 49:
			p.bg = termbox.ColorBlack
		}
	}
}

func (p *Pane) Clear() {
	p.drawOffset = len(p.cells) - 1
	termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
	Redraw()
}
<commit_msg>Add a marning message for when escape codes are isgnored<commit_after>package main

import (
	"github.com/chrisseto/pty"
	"github.com/chrisseto/sux/pansi"
	"github.com/nsf/termbox-go"
	"io"
	"os"
	"os/exec"
)

type Pane struct {
	*exec.Cmd

	cx, cy        int
	sx, sy        int
	fg, bg        termbox.Attribute
	width, height uint16
	scrollOffset  int

	Prog string
	Args []string

	Pty    *os.File
	output io.Reader
	cells  [][]termbox.Cell
}

func CreatePane(width, height uint16, prog string, args ...string) *Pane {
	return &Pane{
		Cmd: exec.Command(prog, args...),
		cx:  0, cy: 0,
		fg: 0, bg: 0,
		scrollOffset: 0,
		drawOffset:   0,
		Prog:         prog, Args: args,
		width: width, height: height,
		Pty: nil,
	}
}

func (p *Pane) Start() error {
	pterm, err := pty.Start(p.Cmd)
	if err != nil {
		panic(err)
	}
	if err = pty.Setsize(pterm, p.height, p.width); err != nil {
		panic(err)
	}
	p.Pty = pterm
	p.cells = make([][]termbox.Cell, 1, p.height)
	p.cells[0] = make([]termbox.Cell, p.width)
	go p.outputPipe()
	return nil
}

func (p *Pane) Close() error {
	return p.Process.Kill()
}

func (p *Pane) Cells() [][]termbox.Cell {
	return p.cells[p.drawOffset:bound(p.drawOffset+int(p.height), p.drawOffset, len(p.cells))]
}

func (p *Pane) Width() uint16 {
	return p.width
}

func (p *Pane) Height() uint16 {
	return p.height
}

func (p *Pane) Scroll(far int) {
	// p.scrollOffset += far
	p.scrollOffset = bound(p.scrollOffset+far, -len(p.cells), 0)
	Redraw()
}

func (p *Pane) bottomLine() *[]termbox.Cell {
	return &p.cells[len(p.cells)-1]
}

func (p *Pane) newLine() *[]termbox.Cell {
	p.cy++
	p.cells = append(p.cells, make([]termbox.Cell, p.width))
	if len(p.cells)-p.drawOffset > int(p.height) {
		p.drawOffset++
	}
	return p.bottomLine()
}

func (p *Pane) Redraw() {
	for y, line := range p.Cells() {
		for x, cell := range line {
			termbox.SetCell(x, y, cell.Ch, cell.Fg, cell.Bg)
		}
	}
	termbox.SetCursor(p.Cursor())
}

func bound(val, min, max int) int {
	if val < min {
		return min
	}
	if val > max {
		return max
	}
	return val
}

func (p *Pane) Cursor() (int, int) {
	p.cx = bound(p.cx, 0, int(p.width)-1)
	p.cy = bound(p.cy, 0, int(p.height)-1)
	return p.cx, p.cy
}

func (p *Pane) outputPipe() {
	lexer := pansi.NewLexer()
	buf := make([]byte, 32*1024)
	// f, _ := os.Create("output.log")
	for {
		nr, err := p.Pty.Read(buf)
		if nr > 0 {
			f.Write(buf[:nr])

			for _, char := range buf[:nr] {
				lexer.Feed(char)
				if res := lexer.Result(); res != nil {
					p.handleEscapeCode(res)
					lexer.Clear()
					continue
				}
				if lexer.State() != pansi.Ground {
					continue
				}

				switch char {
				case 0x7: //Terminal Bell. Skip for the moment
				case 0xA:
					row = p.newLine()
				case 0xD:
					x, p.cx = 0, 0
				case 0x8:
					if x != 0 {
						x--
						p.cx--
					}
					(*row)[x] = termbox.Cell{' ', p.fg, p.bg}
				default:
					if x >= int(p.width) {
						row = p.newLine()
						x = 0
					}
					(*row)[x] = termbox.Cell{rune(char), p.fg, p.bg}
					x++
					p.cx++
				}
			}

			Redraw()
		}
		if err != nil {
			if err == io.EOF {
				break
			}
			panic(err)
		}

	}
}

func (p *Pane) handleEscapeCode(c *pansi.AnsiEscapeCode) {
	switch c.Type {
	case pansi.SetGraphicMode:
		p.SetGraphicMode(c.Values)
	case pansi.CursorPosition:
		if len(c.Values) == 0 {
			p.cx, p.cy = 0, 0
		} else {
			p.cx, p.cy = c.Values[0], c.Values[1]
		}
	case pansi.CursorUp:
		p.cy--
	case pansi.CursorDown:
		p.cy++
	case pansi.CursorBackward:
		p.cx--
	case pansi.CursorForward:
		p.cx++
	case pansi.EraseLine:
		row := p.bottomLine()
		for i := p.cx; i < len(*row); i++ {
			(*row)[i] = termbox.Cell{' ', p.fg, p.bg}
		}
	case pansi.EraseDisplay:
		p.Clear()
	default:
		log.Printf("Doing nothing with %+v\n", *c)
	}
}

func (p *Pane) SetGraphicMode(vals []int) {
	for i := 0; i < len(vals); i++ {
		switch vals[i] {
		case 0:
			p.fg, p.bg = 8, 1
		case 1:
			p.fg |= termbox.AttrBold
		case 7:
			p.fg, p.bg = p.bg, p.fg
		case 38:
			i++
			switch vals[i] {
			case 5:
				i++
				p.fg = termbox.Attribute(vals[i] + 1)
			case 2:
				i += 3 //TODO
			}
		case 39:
			p.fg = termbox.ColorWhite
		case 48:
			i++
			switch vals[i] {
			case 5:
				i++
				p.bg = termbox.Attribute(vals[i] + 1)
			case 2:
				i += 3 //TODO
			}
		case 49:
			p.bg = termbox.ColorBlack
		}
	}
}

func (p *Pane) Clear() {
	p.drawOffset = len(p.cells) - 1
	termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
	Redraw()
}
<|endoftext|>


<commit_before>// Copyright 2016 The Vermeer Light Tools Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package wfobj

import (
	"bufio"
	"fmt"
	"github.com/jamiec7919/vermeer/builtin/geom/polymesh"
	"github.com/jamiec7919/vermeer/builtin/shader"
	m "github.com/jamiec7919/vermeer/math"
	"io"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"strings"
)

func parseFaceField(field string) (p, n, t int) {
	fields := strings.Split(field, "/")

	p, _ = strconv.Atoi(fields[0])

	if len(fields) > 1 {
		if len(fields[1]) > 0 {
			t, _ = strconv.Atoi(fields[1])
		}
	}

	if len(fields) > 2 {
		if len(fields[2]) > 0 {
			n, _ = strconv.Atoi(fields[2])
		}
	}

	return
}

func parseFace(lscan *lineScanner, mesh *polymesh.PolyMesh, mtl byte) error {

	verts := 0

	for {

		field := lscan.Token()

		if field == "" {
			break
		}

		p, n, t := parseFaceField(field)

		//log.Printf("%v %v %v", p, n, t)
		if p <= 0 {
			p = len(mesh.Verts.Elems) + p + 1
		}

		//				if r.MergeVertPos {
		//					p = int(vertmergeface[p-1])
		//				}

		if t <= 0 {
			t = len(mesh.UV.Elems) + t + 1
		}

		//				if r.MergeTexVert {
		//					t = int(texvertmergeface[t-1])
		//				}

		if n <= 0 {
			n = len(mesh.Normals.Elems) + n + 1
		}

		mesh.FaceIdx = append(mesh.FaceIdx, int32(p-1))

		if mesh.Normals.Elems != nil {
			mesh.NormalIdx = append(mesh.NormalIdx, int32(n-1))
		}

		if mesh.UV.Elems != nil {
			mesh.UVIdx = append(mesh.UVIdx, int32(t-1))
		}
		verts++
	}

	mesh.PolyCount = append(mesh.PolyCount, int32(verts))
	mesh.ShaderIdx = append(mesh.ShaderIdx, int32(mtl))
	return nil
}

func (wfobj *File) parse(r io.Reader) (mesh *polymesh.PolyMesh, shaders []*shader.ShaderStd, err error) {

	scanner := bufio.NewScanner(r)
	scanner.Buffer(make([]byte, 1024*1024*64), 64)
	lineno := 0

	mesh = &polymesh.PolyMesh{}

	mesh.Verts.MotionKeys = 1
	mesh.Normals.MotionKeys = 1
	mesh.UV.MotionKeys = 1

	mtl := byte(0)

	for scanner.Scan() {
		line := scanner.Text()
		lineno++

		lscan := lineScanner{}
		lscan.init(line)

		cmd := lscan.Token()

		switch cmd {
		case "mtllib":
			filename := lscan.Rest()

			path := filepath.Join(filepath.Dir(wfobj.Filename), filename)
			f, err := os.Open(path)

			if err != nil {
				log.Printf("wfobj.parse: Error opening \"%v\": %v", path, err)
				continue
			}

			s, err := wfobj.parseMTL(f, filepath.Dir(wfobj.Filename))

			f.Close()

			if err != nil {
				log.Printf("wfobj.parse: Error parsing \"%v\": %v", path, err)
				continue
			}

			shaders = append(shaders, s...)

			if len(shaders) > 255 {
				return nil, nil, fmt.Errorf("wfobj.parse: too many shaders")
			}

		case "usemtl":

			name := lscan.Rest()

			for i, shader := range shaders {
				if shader.MtlName == name {
					mtl = byte(i)
				}
			}
		case "f":
			if err := parseFace(&lscan, mesh, mtl); err != nil {
				log.Printf("Error parsing face: %v", err)
			}

		case "v":
			x, err := strconv.ParseFloat(lscan.Token(), 32)

			if err != nil {
				log.Printf("Error parsing vertex %v: %v", len(mesh.Verts)+1, err)

			}

			y, err := strconv.ParseFloat(lscan.Token(), 32)

			if err != nil {
				log.Printf("Error parsing vertex %v: %v", len(mesh.Verts)+1, err)

			}

			z, err := strconv.ParseFloat(lscan.Token(), 32)

			if err != nil {
				log.Printf("Error parsing vertex %v: %v", len(mesh.Verts)+1, err)

			}

			mesh.Verts.Elems = append(mesh.Verts.Elems, m.Vec3{float32(x), float32(y), float32(z)})
			mesh.Verts.ElemsPerKey++

		case "vn":
			x, err := strconv.ParseFloat(lscan.Token(), 32)

			if err != nil {
				fmt.Printf("Error parsing vertex vn %v: %v\n", len(mesh.Normals.Elems)+1, err)

			}

			y, err := strconv.ParseFloat(lscan.Token(), 32)

			if err != nil {
				fmt.Printf("Error parsing vertex vn %v: %v\n", len(mesh.Normals.Elems)+1, err)

			}

			z, err := strconv.ParseFloat(lscan.Token(), 32)

			if err != nil {
				fmt.Printf("Error parsing vertex vn %v: %v\n", len(mesh.Normals.Elems)+1, err)

			}

			mesh.Normals.Elems = append(mesh.Normals.Elems, m.Vec3{float32(x), float32(y), float32(z)})
			mesh.Normals.ElemsPerKey++

		case "vt":
			x, err := strconv.ParseFloat(lscan.Token(), 32)

			if err != nil {
				fmt.Printf("Error parsing vertex vt %v: %v\n", len(mesh.UV.Elems)+1, err)

			}

			y, err := strconv.ParseFloat(lscan.Token(), 32)

			if err != nil {
				fmt.Printf("Error parsing vertex vt %v: %v\n", len(mesh.UV.Elems)+1, err)

			}

			mesh.UV.Elems = append(mesh.UV.Elems, m.Vec2{float32(x), float32(y)})
			mesh.UV.ElemsPerKey++

		default:
			//Comment or blank line
		}
	}

	err = nil

	//log.Printf("%v %v", len(mesh.FaceIdx), mesh.FaceIdx)
	return
}
<commit_msg>Minor fix to proc for vet code.<commit_after>// Copyright 2016 The Vermeer Light Tools Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package wfobj

import (
	"bufio"
	"fmt"
	"github.com/jamiec7919/vermeer/builtin/geom/polymesh"
	"github.com/jamiec7919/vermeer/builtin/shader"
	m "github.com/jamiec7919/vermeer/math"
	"io"
	"log"
	"os"
	"path/filepath"
	"strconv"
	"strings"
)

func parseFaceField(field string) (p, n, t int) {
	fields := strings.Split(field, "/")

	p, _ = strconv.Atoi(fields[0])

	if len(fields) > 1 {
		if len(fields[1]) > 0 {
			t, _ = strconv.Atoi(fields[1])
		}
	}

	if len(fields) > 2 {
		if len(fields[2]) > 0 {
			n, _ = strconv.Atoi(fields[2])
		}
	}

	return
}

func parseFace(lscan *lineScanner, mesh *polymesh.PolyMesh, mtl byte) error {

	verts := 0

	for {

		field := lscan.Token()

		if field == "" {
			break
		}

		p, n, t := parseFaceField(field)

		//log.Printf("%v %v %v", p, n, t)
		if p <= 0 {
			p = len(mesh.Verts.Elems) + p + 1
		}

		//				if r.MergeVertPos {
		//					p = int(vertmergeface[p-1])
		//				}

		if t <= 0 {
			t = len(mesh.UV.Elems) + t + 1
		}

		//				if r.MergeTexVert {
		//					t = int(texvertmergeface[t-1])
		//				}

		if n <= 0 {
			n = len(mesh.Normals.Elems) + n + 1
		}

		mesh.FaceIdx = append(mesh.FaceIdx, int32(p-1))

		if mesh.Normals.Elems != nil {
			mesh.NormalIdx = append(mesh.NormalIdx, int32(n-1))
		}

		if mesh.UV.Elems != nil {
			mesh.UVIdx = append(mesh.UVIdx, int32(t-1))
		}
		verts++
	}

	mesh.PolyCount = append(mesh.PolyCount, int32(verts))
	mesh.ShaderIdx = append(mesh.ShaderIdx, int32(mtl))
	return nil
}

func (wfobj *File) parse(r io.Reader) (mesh *polymesh.PolyMesh, shaders []*shader.ShaderStd, err error) {

	scanner := bufio.NewScanner(r)
	scanner.Buffer(make([]byte, 1024*1024*64), 64)
	lineno := 0

	mesh = &polymesh.PolyMesh{}

	mesh.Verts.MotionKeys = 1
	mesh.Normals.MotionKeys = 1
	mesh.UV.MotionKeys = 1

	mtl := byte(0)

	for scanner.Scan() {
		line := scanner.Text()
		lineno++

		lscan := lineScanner{}
		lscan.init(line)

		cmd := lscan.Token()

		switch cmd {
		case "mtllib":
			filename := lscan.Rest()

			path := filepath.Join(filepath.Dir(wfobj.Filename), filename)
			f, err := os.Open(path)

			if err != nil {
				log.Printf("wfobj.parse: Error opening \"%v\": %v", path, err)
				continue
			}

			s, err := wfobj.parseMTL(f, filepath.Dir(wfobj.Filename))

			f.Close()

			if err != nil {
				log.Printf("wfobj.parse: Error parsing \"%v\": %v", path, err)
				continue
			}

			shaders = append(shaders, s...)

			if len(shaders) > 255 {
				return nil, nil, fmt.Errorf("wfobj.parse: too many shaders")
			}

		case "usemtl":

			name := lscan.Rest()

			for i, shader := range shaders {
				if shader.MtlName == name {
					mtl = byte(i)
				}
			}
		case "f":
			if err := parseFace(&lscan, mesh, mtl); err != nil {
				log.Printf("Error parsing face: %v", err)
			}

		case "v":
			x, err := strconv.ParseFloat(lscan.Token(), 32)

			if err != nil {
				log.Printf("Error parsing vertex %v: %v", len(mesh.Verts.Elems)+1, err)

			}

			y, err := strconv.ParseFloat(lscan.Token(), 32)

			if err != nil {
				log.Printf("Error parsing vertex %v: %v", len(mesh.Verts.Elems)+1, err)

			}

			z, err := strconv.ParseFloat(lscan.Token(), 32)

			if err != nil {
				log.Printf("Error parsing vertex %v: %v", len(mesh.Verts.Elems)+1, err)

			}

			mesh.Verts.Elems = append(mesh.Verts.Elems, m.Vec3{float32(x), float32(y), float32(z)})
			mesh.Verts.ElemsPerKey++

		case "vn":
			x, err := strconv.ParseFloat(lscan.Token(), 32)

			if err != nil {
				fmt.Printf("Error parsing vertex vn %v: %v\n", len(mesh.Normals.Elems)+1, err)

			}

			y, err := strconv.ParseFloat(lscan.Token(), 32)

			if err != nil {
				fmt.Printf("Error parsing vertex vn %v: %v\n", len(mesh.Normals.Elems)+1, err)

			}

			z, err := strconv.ParseFloat(lscan.Token(), 32)

			if err != nil {
				fmt.Printf("Error parsing vertex vn %v: %v\n", len(mesh.Normals.Elems)+1, err)

			}

			mesh.Normals.Elems = append(mesh.Normals.Elems, m.Vec3{float32(x), float32(y), float32(z)})
			mesh.Normals.ElemsPerKey++

		case "vt":
			x, err := strconv.ParseFloat(lscan.Token(), 32)

			if err != nil {
				fmt.Printf("Error parsing vertex vt %v: %v\n", len(mesh.UV.Elems)+1, err)

			}

			y, err := strconv.ParseFloat(lscan.Token(), 32)

			if err != nil {
				fmt.Printf("Error parsing vertex vt %v: %v\n", len(mesh.UV.Elems)+1, err)

			}

			mesh.UV.Elems = append(mesh.UV.Elems, m.Vec2{float32(x), float32(y)})
			mesh.UV.ElemsPerKey++

		default:
			//Comment or blank line
		}
	}

	err = nil

	//log.Printf("%v %v", len(mesh.FaceIdx), mesh.FaceIdx)
	return
}
<|endoftext|>


<commit_before>package apifaker

import (
	"github.com/Focinfi/gtester"
	"github.com/gin-gonic/gin"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
	"time"
)

type ApiFaker struct {
	// Engine composing pointer of gin.Engine to let ApiFaker
	// has the ability of routing
	*gin.Engine

	// ApiDir the directory contains api json files
	ApiDir string

	// Routers a array of pointer for Router
	Routers map[string]*Router

	// ExtMux external mux for the real api
	ExtMux http.Handler

	// Prefix the prefix of fake apis
	Prefix string
}

// NewWithApiDir create a new ApiFaker with the ApiDir,
// returns the pointer of ApiFaker and error will not be nil if
// there are some errors of manipulating ApiDir or json.Unmarshal
func NewWithApiDir(dir string) (*ApiFaker, error) {
	faker := &ApiFaker{
		ApiDir:  dir,
		Routers: map[string]*Router{},
	}

	err := gtester.NewCheckQueue().Add(func() error {
		// traverse dir
		return filepath.Walk(dir, func(path string, f os.FileInfo, err error) error {
			if f == nil {
				return err
			}
			if f.IsDir() || !strings.HasSuffix(path, ".json") {
				return nil
			}

			if router, err := NewRouterWithPath(path, faker); err != nil {
				return err
			} else {
				if _, ok := faker.Routers[router.Model.Name]; ok {
					panic(router.Model.Name + " has been existed")
				} else {
					faker.Routers[router.Model.Name] = router
				}
			}
			return nil
		})
	}).Add(func() error {
		return faker.CheckUniqueness()
	}).Add(func() error {
		return faker.CheckRelationships()
	}).Run()

	if err == nil {
		faker.setHandlers()
		faker.setSaveToFileTimer()
	}

	return faker, err
}

// CheckUniqueness
func (af *ApiFaker) CheckUniqueness() error {
	for _, router := range af.Routers {
		if err := router.Model.CheckUniqueness(); err != nil {
			return err
		}
	}
	return nil
}

// CheckRelationships
func (af *ApiFaker) CheckRelationships() error {
	for _, router := range af.Routers {
		if err := router.Model.CheckRelationships(); err != nil {
			return err
		}
	}
	return nil
}

// ServeHTTP serve the req and write response to rw.
// It will use gin.Engine when req.URL.Path hasing prefix of af.Prefix
// otherwise it will call af.ExtMux.ServeHTTP()
func (af *ApiFaker) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	path := req.URL.Path
	if af.Prefix == "" || strings.HasPrefix(path, af.Prefix+"/") || af.ExtMux == nil {
		af.Engine.ServeHTTP(rw, req)
	} else {
		af.ExtMux.ServeHTTP(rw, req)
	}
}

// MountTo assign path as af's Prefix and assign handler as af's ExtMux.
// At same time set hte handlers for af
func (af *ApiFaker) MountTo(path string, handler http.Handler) {
	af.Prefix = path
	af.setHandlers()
	af.ExtMux = handler
}

// SaveToFile
func (af *ApiFaker) SaveToFile() {
	for _, router := range af.Routers {
		router.SaveToFile()
	}
}

// setSaveToFileTimer set a periodic timer to call SaveToFile()
func (af *ApiFaker) setSaveToFileTimer() {
	go func() {
		for {
			now := time.Now()
			next := now.Add(time.Hour * 24)
			nextSaveDate := time.Date(next.Year(),
				next.Month(),
				next.Day(),
				0, 0, 0, 0,
				next.Location())
			timer := time.NewTimer(nextSaveDate.Sub(now))
			<-timer.C
			af.SaveToFile()
		}
	}()
}

// setHandlers set all handlers into af.Engine.
// It will panic when there are has repeat combination of route.Method and route.Path
func (af *ApiFaker) setHandlers() {
	// if any panic exsits, Save data to json
	defer func() {
		if err := recover(); err != nil {
			log.Fatal(err)
			af.SaveToFile()
		}
	}()

	// reset Engine
	af.Engine = NewGinEngineWithFaker(af)

	for _, router := range af.Routers {
		for _, route := range router.Routes {
			model := router.Model
			method := route.Method
			path := af.Prefix + route.Path
			switch method {
			case GET:
				af.GET(path, func(ctx *gin.Context) {
					idStr := ctx.Param("id")
					// GET /collection
					if idStr == "" {
						models := model.ToLineItems()
						sort.Sort(models)
						ctx.JSON(http.StatusOK, models.ToSlice())
					} else if id, err := strconv.ParseFloat(idStr, 64); err == nil {
						// GET /collection/:id
						if li, ok := model.Get(id); ok {
							model.InsertRelatedData(&li)
							ctx.JSON(http.StatusOK, li.ToMap())
						} else {
							ctx.JSON(http.StatusNotFound, nil)
						}
					} else {
						// GET /collection/xxx
						ctx.JSON(http.StatusBadRequest, nil)
					}
				})
			case POST:
				af.POST(path, func(ctx *gin.Context) {
					// create a new lineitems with ctx.PostForm()
					li, err := NewLineItemWithGinContext(ctx, model)
					if err != nil {
						ctx.JSON(http.StatusBadRequest, gin.H{"message": err.Error()})
					} else {
						// add this lineitem to model
						if err = model.Add(li); err != nil {
							ctx.JSON(http.StatusBadRequest, gin.H{"message": err.Error()})
						} else {
							ctx.JSON(http.StatusOK, li.ToMap())
						}
					}
				})
			case PUT:
				af.PUT(path, func(ctx *gin.Context) {
					// allocate a new item
					newLi, err := NewLineItemWithGinContext(ctx, model)

					if err != nil {
						ctx.JSON(http.StatusBadRequest, gin.H{"message": err.Error()})
						return
					}

					// update
					id, _ := ctx.Get("idFloat64")
					if err := model.Update(id.(float64), &newLi); err != nil {
						ctx.JSON(http.StatusBadRequest, map[string]string{"message": err.Error()})
					} else {
						ctx.JSON(http.StatusOK, newLi.ToMap())
					}
				})
			case PATCH:
				af.PATCH(path, func(ctx *gin.Context) {
					// update with attrs, got error if attrs is not complete
					id, _ := ctx.Get("idFloat64")
					if li, err := model.UpdateWithAttrs(id.(float64), ctx); err != nil {
						ctx.JSON(http.StatusBadRequest, map[string]string{"message": err.Error()})
					} else {
						ctx.JSON(http.StatusOK, li.ToMap())
					}
				})
			case DELETE:
				af.DELETE(path, func(ctx *gin.Context) {
					// delete
					id, _ := ctx.Get("idFloat64")
					model.Delete(id.(float64))
					ctx.JSON(http.StatusOK, nil)
				})
			}
		}
	}
}

func NewGinEngineWithFaker(faker *ApiFaker) *gin.Engine {
	engine := gin.Default()
	// check id
	engine.Use(func(ctx *gin.Context) {
		// check if param "id" is int
		idStr := ctx.Param("id")
		if idStr == "" {
			ctx.Next()
			return
		}

		id, err := strconv.ParseFloat(idStr, 64)
		if err != nil {
			ctx.JSON(http.StatusBadRequest, gin.H{"message": err.Error()})
			return
		}

		path := strings.TrimSuffix(ctx.Request.URL.Path, "/")
		pathPieces := strings.Split(path, "/")
		resourceName := pathPieces[len(pathPieces)-2]

		// check if element does exsit
		if router, ok := faker.Routers[resourceName]; ok {
			if _, ok := router.Model.Get(id); ok {
				ctx.Set("idFloat64", id)
				ctx.Next()
			} else {
				ctx.JSON(http.StatusNotFound, nil)
				return
			}
		} else {
			ctx.JSON(http.StatusNotFound, nil)
			return
		}
	})

	return engine
}
<commit_msg>ApiFaker: Refactor setHandlers()<commit_after>package apifaker

import (
	"github.com/Focinfi/gtester"
	"github.com/gin-gonic/gin"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"sort"
	"strconv"
	"strings"
	"time"
)

type ApiFaker struct {
	// Engine composing pointer of gin.Engine to let ApiFaker
	// has the ability of routing
	*gin.Engine

	// ApiDir the directory contains api json files
	ApiDir string

	// Routers a array of pointer for Router
	Routers map[string]*Router

	// ExtMux external mux for the real api
	ExtMux http.Handler

	// Prefix the prefix of fake apis
	Prefix string
}

// NewWithApiDir create a new ApiFaker with the ApiDir,
// returns the pointer of ApiFaker and error will not be nil if
// there are some errors of manipulating ApiDir or json.Unmarshal
func NewWithApiDir(dir string) (*ApiFaker, error) {
	faker := &ApiFaker{
		ApiDir:  dir,
		Routers: map[string]*Router{},
	}

	err := gtester.NewCheckQueue().Add(func() error {
		// traverse dir
		return filepath.Walk(dir, func(path string, f os.FileInfo, err error) error {
			if f == nil {
				return err
			}
			if f.IsDir() || !strings.HasSuffix(path, ".json") {
				return nil
			}

			if router, err := NewRouterWithPath(path, faker); err != nil {
				return err
			} else {
				if _, ok := faker.Routers[router.Model.Name]; ok {
					panic(router.Model.Name + " has been existed")
				} else {
					faker.Routers[router.Model.Name] = router
				}
			}
			return nil
		})
	}).Add(func() error {
		return faker.CheckUniqueness()
	}).Add(func() error {
		return faker.CheckRelationships()
	}).Run()

	if err == nil {
		faker.setHandlers()
		faker.setSaveToFileTimer()
	}

	return faker, err
}

// CheckUniqueness
func (af *ApiFaker) CheckUniqueness() error {
	for _, router := range af.Routers {
		if err := router.Model.CheckUniqueness(); err != nil {
			return err
		}
	}
	return nil
}

// CheckRelationships
func (af *ApiFaker) CheckRelationships() error {
	for _, router := range af.Routers {
		if err := router.Model.CheckRelationships(); err != nil {
			return err
		}
	}
	return nil
}

// ServeHTTP serve the req and write response to rw.
// It will use gin.Engine when req.URL.Path hasing prefix of af.Prefix
// otherwise it will call af.ExtMux.ServeHTTP()
func (af *ApiFaker) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	path := req.URL.Path
	if af.Prefix == "" || strings.HasPrefix(path, af.Prefix+"/") || af.ExtMux == nil {
		af.Engine.ServeHTTP(rw, req)
	} else {
		af.ExtMux.ServeHTTP(rw, req)
	}
}

// MountTo assign path as af's Prefix and assign handler as af's ExtMux.
// At same time set hte handlers for af
func (af *ApiFaker) MountTo(path string, handler http.Handler) {
	af.Prefix = path
	af.setHandlers()
	af.ExtMux = handler
}

// SaveToFile
func (af *ApiFaker) SaveToFile() {
	for _, router := range af.Routers {
		router.SaveToFile()
	}
}

// setSaveToFileTimer set a periodic timer to call SaveToFile()
func (af *ApiFaker) setSaveToFileTimer() {
	go func() {
		for {
			now := time.Now()
			next := now.Add(time.Hour * 24)
			nextSaveDate := time.Date(next.Year(),
				next.Month(),
				next.Day(),
				0, 0, 0, 0,
				next.Location())
			timer := time.NewTimer(nextSaveDate.Sub(now))
			<-timer.C
			af.SaveToFile()
		}
	}()
}

// setHandlers set all handlers into af.Engine.
// It will panic when there are has repeat combination of route.Method and route.Path
func (af *ApiFaker) setHandlers() {
	// if any panic exsits, Save data to json
	defer func() {
		if err := recover(); err != nil {
			log.Fatal(err)
			af.SaveToFile()
		}
	}()

	// reset Engine
	af.Engine = NewGinEngineWithFaker(af)

	for _, router := range af.Routers {
		for _, route := range router.Routes {
			model := router.Model
			method := route.Method
			path := af.Prefix + route.Path
			switch method {
			case GET:
				af.GET(path, func(ctx *gin.Context) {
					if id, ok := ctx.Get("idFloat64"); ok {
						// GET /collection/:id
						li, _ := model.Get(id.(float64))
						model.InsertRelatedData(&li)
						ctx.JSON(http.StatusOK, li.ToMap())
					} else {
						// GET /collection
						models := model.ToLineItems()
						sort.Sort(models)
						ctx.JSON(http.StatusOK, models.ToSlice())
					}
				})
			case POST:
				af.POST(path, func(ctx *gin.Context) {
					// create a new lineitems with ctx.PostForm()
					li, err := NewLineItemWithGinContext(ctx, model)
					if err != nil {
						ctx.JSON(http.StatusBadRequest, gin.H{"message": err.Error()})
					} else {
						// add this lineitem to model
						if err = model.Add(li); err != nil {
							ctx.JSON(http.StatusBadRequest, gin.H{"message": err.Error()})
						} else {
							ctx.JSON(http.StatusOK, li.ToMap())
						}
					}
				})
			case PUT:
				af.PUT(path, func(ctx *gin.Context) {
					// allocate a new item
					newLi, err := NewLineItemWithGinContext(ctx, model)

					if err != nil {
						ctx.JSON(http.StatusBadRequest, gin.H{"message": err.Error()})
						return
					}

					// update
					id, _ := ctx.Get("idFloat64")
					if err := model.Update(id.(float64), &newLi); err != nil {
						ctx.JSON(http.StatusBadRequest, map[string]string{"message": err.Error()})
					} else {
						ctx.JSON(http.StatusOK, newLi.ToMap())
					}
				})
			case PATCH:
				af.PATCH(path, func(ctx *gin.Context) {
					// update with attrs, got error if attrs is not complete
					id, _ := ctx.Get("idFloat64")
					if li, err := model.UpdateWithAttrs(id.(float64), ctx); err != nil {
						ctx.JSON(http.StatusBadRequest, map[string]string{"message": err.Error()})
					} else {
						ctx.JSON(http.StatusOK, li.ToMap())
					}
				})
			case DELETE:
				af.DELETE(path, func(ctx *gin.Context) {
					// delete
					id, _ := ctx.Get("idFloat64")
					model.Delete(id.(float64))
					ctx.JSON(http.StatusOK, nil)
				})
			}
		}
	}
}

func NewGinEngineWithFaker(faker *ApiFaker) *gin.Engine {
	engine := gin.Default()
	// check id
	engine.Use(func(ctx *gin.Context) {
		// check if param "id" is int
		idStr := ctx.Param("id")
		if idStr == "" {
			ctx.Next()
			return
		}

		id, err := strconv.ParseFloat(idStr, 64)
		if err != nil {
			ctx.JSON(http.StatusBadRequest, gin.H{"message": err.Error()})
			return
		}

		path := strings.TrimSuffix(ctx.Request.URL.Path, "/")
		pathPieces := strings.Split(path, "/")
		resourceName := pathPieces[len(pathPieces)-2]

		// check if element does exsit
		if router, ok := faker.Routers[resourceName]; ok {
			if _, ok := router.Model.Get(id); ok {
				ctx.Set("idFloat64", id)
				ctx.Next()
			} else {
				ctx.JSON(http.StatusNotFound, nil)
				return
			}
		} else {
			ctx.JSON(http.StatusNotFound, nil)
			return
		}
	})

	return engine
}
<|endoftext|>


<commit_before>/*
The MIT License (MIT)

transfer.sh was originally written by and
Copyright (c) 2014 DutchCoders [https://github.com/dutchcoders/]

Some modifications
Copyright (c) 2015 John Ko

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

package main

import (
	"flag"
	"fmt"
	"github.com/PuerkitoBio/ghost/handlers"
	"github.com/gorilla/mux"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"
)

const SERVER_INFO = "dtfc"
const SERVER_VERSION = "0.0.1"

// we use these commands to reduce the amount of garbage collection golang needs to do
const cmdSHASUM = "/usr/local/bin/shasum"
const cmdSHA512 = "/sbin/sha512"
const cmdTAIL   = "/usr/bin/tail"

const timeLayout = "2006-01-02 15:04:05 MST"
const timeHTTPLayout = "Mon, 2 Jan 2006 15:04:05 MST"

// parse request with maximum memory of _24Kilobits
const _24K = (1 << 20) * 24

var config struct {
    ALLOWDELETE        string
	ALLOWGET           string
    ALLOWPUT           string
	Temp               string
}

var storage Storage

func init() {
	config.Temp = os.TempDir()
}

func main() {
	rand.Seed(time.Now().UTC().UnixNano())

	port        := flag.String("port", "8080", "port number, default: 8080")
	temp        := flag.String("temp", config.Temp, "")
	basedir     := flag.String("basedir", "", "")
	logpath     := flag.String("log", "", "")
	provider    := flag.String("provider", "local", "")
    allowdelete := flag.String("allowdelete", "true", "true or false, default: true")
    allowget    := flag.String("allowget", "true", "true or false, default: true")
	allowput    := flag.String("allowput", "true", "true or false, default: true")

	flag.Parse()

	if *logpath != "" {
		f, err := os.OpenFile(*logpath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
		if err != nil {
			log.Fatalf("error opening file: %v", err)
		}
		defer f.Close()
		log.SetOutput(f)
	}

	config.Temp = *temp
	config.ALLOWDELETE = *allowdelete
	config.ALLOWGET = *allowget
	config.ALLOWPUT = *allowput

	r := mux.NewRouter()

    r.HandleFunc("/health.html", healthHandler).Methods("GET")
	r.HandleFunc("/{hash}", getHandler).Methods("GET")

	//r.HandleFunc("/{hash}", headHandler).Methods("HEAD")

	r.HandleFunc("/{filename}", putHandler).Methods("PUT")

	r.NotFoundHandler = http.HandlerFunc(notFoundHandler)

	var err error
	switch *provider {
	case "local":
		if *basedir == "" {
			log.Panic("basedir not set")
		}
		storage, err = NewLocalStorage(*basedir)
	}
	if err != nil {
		log.Panic("Error while creating storage.", err)
	}

	log.Printf("%s/%s server started. :\nlistening on port: %v",
        SERVER_INFO, SERVER_VERSION, *port)
    log.Printf("using temp folder: %s\nusing storage provider: %s",
        config.Temp, *provider)
    log.Printf("allow delete: %s\nallow get: %s\nallow put: %s",
        config.ALLOWDELETE, config.ALLOWGET, config.ALLOWPUT)
	log.Printf("---------------------------")

	s := &http.Server{
		Addr:    fmt.Sprintf(":%s", *port),
		Handler: handlers.PanicHandler(RedirectHandler(handlers.LogHandler(r, handlers.NewLogOptions(log.Printf, "_default_"))), nil),
	}

	log.Panic(s.ListenAndServe())
	log.Printf("Server stopped.")
}
<commit_msg>don't init config.Temp<commit_after>/*
The MIT License (MIT)

transfer.sh was originally written by and
Copyright (c) 2014 DutchCoders [https://github.com/dutchcoders/]

Some modifications
Copyright (c) 2015 John Ko

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

package main

import (
	"flag"
	"fmt"
	"github.com/PuerkitoBio/ghost/handlers"
	"github.com/gorilla/mux"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"os"
	"strings"
	"time"
)

const SERVER_INFO = "dtfc"
const SERVER_VERSION = "0.0.1"

// we use these commands to reduce the amount of garbage collection golang needs to do
const cmdSHASUM = "/usr/local/bin/shasum"
const cmdSHA512 = "/sbin/sha512"
const cmdTAIL   = "/usr/bin/tail"

const timeLayout = "2006-01-02 15:04:05 MST"
const timeHTTPLayout = "Mon, 2 Jan 2006 15:04:05 MST"

// parse request with maximum memory of _24Kilobits
const _24K = (1 << 20) * 24

var config struct {
    ALLOWDELETE        string
	ALLOWGET           string
    ALLOWPUT           string
	Temp               string
}

var storage Storage

func init() {
	//config.Temp = os.TempDir()
}

func main() {
	rand.Seed(time.Now().UTC().UnixNano())

	port        := flag.String("port", "8080", "port number, default: 8080")
	temp        := flag.String("temp", config.Temp, "")
	basedir     := flag.String("basedir", "", "")
	logpath     := flag.String("log", "", "")
	provider    := flag.String("provider", "local", "")
    allowdelete := flag.String("allowdelete", "true", "true or false, default: true")
    allowget    := flag.String("allowget", "true", "true or false, default: true")
	allowput    := flag.String("allowput", "true", "true or false, default: true")

	flag.Parse()

	if *logpath != "" {
		f, err := os.OpenFile(*logpath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
		if err != nil {
			log.Fatalf("error opening file: %v", err)
		}
		defer f.Close()
		log.SetOutput(f)
	}

	config.Temp = *temp
	config.ALLOWDELETE = *allowdelete
	config.ALLOWGET = *allowget
	config.ALLOWPUT = *allowput

	r := mux.NewRouter()

    r.HandleFunc("/health.html", healthHandler).Methods("GET")
	r.HandleFunc("/{hash}", getHandler).Methods("GET")

	//r.HandleFunc("/{hash}", headHandler).Methods("HEAD")

	r.HandleFunc("/{filename}", putHandler).Methods("PUT")

	r.NotFoundHandler = http.HandlerFunc(notFoundHandler)

	var err error
	switch *provider {
	case "local":
		if *basedir == "" {
			log.Panic("basedir not set")
		}
		storage, err = NewLocalStorage(*basedir)
	}
	if err != nil {
		log.Panic("Error while creating storage.", err)
	}

	log.Printf("%s/%s server started. :\nlistening on port: %v",
        SERVER_INFO, SERVER_VERSION, *port)
    log.Printf("using temp folder: %s\nusing storage provider: %s",
        config.Temp, *provider)
    log.Printf("allow delete: %s\nallow get: %s\nallow put: %s",
        config.ALLOWDELETE, config.ALLOWGET, config.ALLOWPUT)
	log.Printf("---------------------------")

	s := &http.Server{
		Addr:    fmt.Sprintf(":%s", *port),
		Handler: handlers.PanicHandler(RedirectHandler(handlers.LogHandler(r, handlers.NewLogOptions(log.Printf, "_default_"))), nil),
	}

	log.Panic(s.ListenAndServe())
	log.Printf("Server stopped.")
}
<|endoftext|>