Пример #1
0
func (g *Gmail) saveHeaderFields(headerValue string) {

	emails, err := mail.ParseAddressList(headerValue)
	if err != nil {
		log.Println("Unable to parse:", headerValue)
	} else {

		for _, v := range emails {

			name := v.Name
			email := v.Address

			if !g.isKnownEmail(email) {

				err := errors.New("")
				err = g.db.SetContact(name, email)

				if err != nil {
					log.Println("Unable to save email:", email)
				}
			} else {
				log.Println("Known email. Ignoring:", email)
			}
		}
	}
}
Пример #2
0
// 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
}
Пример #3
0
func getMultipleAddress(raw string) []*mail.Address {
	addr, err := mail.ParseAddressList(raw)
	if err != nil {
		return nil
	}
	return addr
}
Пример #4
0
func TestAddBccRecipients(t *testing.T) {
	m := NewMail()
	emails, _ := mail.ParseAddressList("Joe <*****@*****.**>, Doe <*****@*****.**>")
	m.AddBccRecipients(emails)
	if len(m.Bcc) != 2 {
		t.Errorf("AddBccRecipients should append to SGMail.Bcc")
	}
}
Пример #5
0
func parseRecipients(input *string) []*mail.Address {
	recipients, err := mail.ParseAddressList(*input)
	if err != nil {
		return []*mail.Address{}
	}

	log.Println("Recipients are", recipients)
	return recipients
}
Пример #6
0
func buildCCs(m *Message) []*mail.Address {
	cc, err := mail.ParseAddressList(m.CCs)
	if err != nil {
		cc = []*mail.Address{}
	}
	to, err := mail.ParseAddressList(m.To)
	if err != nil {
		to = []*mail.Address{}
	}
	all := append(to, cc...)
	ret := make([]*mail.Address, 0)
	for _, e := range all {
		if !isMe(e) {
			ret = append(ret, e)
		}
	}
	return ret
}
Пример #7
0
func (m *Mailer) String() (string, error) {
	message, header := "", make(map[string]string)

	headers := map[string][]string{
		"From": []string{m.Mail.From},
		"To":   m.Mail.To,
		"Cc":   m.Mail.Cc,
		"Bcc":  m.Mail.Bcc,
	}

	for key, addresses := range headers {
		formated_addresses := []string{}
		for _, address := range addresses {
			parsed_addresses, err := mail.ParseAddressList(address)
			if err == nil {
				for _, parsed_address := range parsed_addresses {
					if len(parsed_address.Name) > 0 {
						formated_addresses = append(formated_addresses, fmt.Sprintf("%v <%v>", parsed_address.Name, parsed_address.Address))
					} else {
						formated_addresses = append(formated_addresses, fmt.Sprintf("%v", parsed_address.Address))
					}
				}
			}
		}
		header[key] = strings.Join(formated_addresses, ", ")
	}

	header["Subject"] = m.Mail.Subject
	header["MIME-Version"] = "1.0"
	header["Content-Type"] = m.contentType()
	header["Content-Transfer-Encoding"] = m.contentTransferEncoding()

	for k, v := range header {
		if len(v) > 0 {
			message += fmt.Sprintf("%s: %s\r\n", k, v)
		}
	}

	if len(m.Mail.Bodys) == 0 {
		m.Body("")
	}

	for _, body := range m.Mail.Bodys {
		message += m.crlfBoundary()
		message += body.Encode() + "\r\n"
	}

	for _, attachment := range m.Mail.Attachments {
		message += m.crlfBoundary()
		message += attachment.Encode() + "\r\n"
	}

	message += m.endBoundary()

	return message, nil
}
Пример #8
0
func TestAddRecipients(t *testing.T) {
	m := NewMail()
	emails, _ := mail.ParseAddressList("Joe <*****@*****.**>, Doe <*****@*****.**>")
	m.AddRecipients(emails)
	switch {
	case len(m.To) != 2:
		t.Errorf("AddRecipients should append to SGMail.To")
	case len(m.ToName) != 2:
		t.Errorf("AddRecipients should append to SGMail.ToName if a valid email is supplied")
	}
}
Пример #9
0
// Given a string like "Cathal Garvey <*****@*****.**>, Stephen Barr <*****@*****.**>"
// return []string{"Cathal Garvey <*****@*****.**>", "Stephen Barr <*****@*****.**>"}
func parseMultiExpressiveEmails(entry string) ([]string, error) {
	out := make([]string, 0)
	parsed, err := mail.ParseAddressList(entry)
	if err != nil {
		return nil, err
	}
	for _, m := range parsed {
		out = append(out, m.String())
	}
	return out, nil
}
Пример #10
0
// ParseAddressList splits a comma separated list of addresses
// into multiple email addresses. The returned addresses can be
// used to call Send().
func ParseAddressList(s string) ([]string, error) {
	addrs, err := mail.ParseAddressList(s)
	if err != nil {
		return nil, err
	}
	return generic.Map(addrs, func(addr *mail.Address) string {
		if addr.Name != "" {
			return fmt.Sprintf("%s <%s>", addr.Name, addr.Address)
		}
		return addr.Address
	}).([]string), nil
}
Пример #11
0
func ParseAddressList() {
	const list = `[email protected], Alice <*****@*****.**>, "Bob Uncle" <*****@*****.**>, "Yang-Tan, Eve" <*****@*****.**>`
	emails, err := mail.ParseAddressList(list)
	if err != nil {
		log.Fatal(err)
	}

	for _, v := range emails {
		fmt.Println(v.Name, "=>", v.Address)
	}

}
Пример #12
0
// Create a new email message, returning any reasons for failure.
func NewMessageDebug(from, to, subject, body string) (*Message, error) {
	fromAddress, err := mail.ParseAddress(from)
	if err != nil {
		return nil, errors.New("Invalid from address; " + err.Error())
	}

	toAddresses, err := mail.ParseAddressList(to)
	if err != nil {
		return nil, errors.New("Invalid to address(es); " + err.Error())
	}

	return &Message{fromAddress, &AddressList{toAddresses}, subject, body}, nil
}
Пример #13
0
func validateEmail(ctx context.Context, address string, resolver bdns.DNSResolver) (prob *probs.ProblemDetails) {
	emails, err := mail.ParseAddressList(address)
	if err != nil {
		return &probs.ProblemDetails{
			Type:   probs.InvalidEmailProblem,
			Detail: unparseableEmailDetail,
		}
	}
	if len(emails) > 1 {
		return &probs.ProblemDetails{
			Type:   probs.InvalidEmailProblem,
			Detail: multipleAddressDetail,
		}
	}
	splitEmail := strings.SplitN(emails[0].Address, "@", -1)
	domain := strings.ToLower(splitEmail[len(splitEmail)-1])
	var resultMX []string
	var resultA []net.IP
	var errMX, errA error
	var wg sync.WaitGroup
	wg.Add(2)
	go func() {
		resultMX, errMX = resolver.LookupMX(ctx, domain)
		wg.Done()
	}()
	go func() {
		resultA, errA = resolver.LookupHost(ctx, domain)
		wg.Done()
	}()
	wg.Wait()

	if errMX != nil {
		prob := bdns.ProblemDetailsFromDNSError(errMX)
		prob.Type = probs.InvalidEmailProblem
		return prob
	} else if len(resultMX) > 0 {
		return nil
	}
	if errA != nil {
		prob := bdns.ProblemDetailsFromDNSError(errA)
		prob.Type = probs.InvalidEmailProblem
		return prob
	} else if len(resultA) > 0 {
		return nil
	}

	return &probs.ProblemDetails{
		Type:   probs.InvalidEmailProblem,
		Detail: emptyDNSResponseDetail,
	}
}
Пример #14
0
func (w EMailWriter) Write(rs ...models.ScanResult) (err error) {
	conf := config.Conf
	to := strings.Join(conf.EMail.To[:], ", ")
	cc := strings.Join(conf.EMail.Cc[:], ", ")
	mailAddresses := append(conf.EMail.To, conf.EMail.Cc...)
	if _, err := mail.ParseAddressList(strings.Join(mailAddresses[:], ", ")); err != nil {
		return fmt.Errorf("Failed to parse email addresses: %s", err)
	}

	for _, r := range rs {
		subject := fmt.Sprintf("%s%s %s",
			conf.EMail.SubjectPrefix,
			r.ServerInfo(),
			r.CveSummary(),
		)

		headers := make(map[string]string)
		headers["From"] = conf.EMail.From
		headers["To"] = to
		headers["Cc"] = cc
		headers["Subject"] = subject
		headers["Date"] = time.Now().Format(time.RFC1123Z)
		headers["Content-Type"] = "text/plain; charset=utf-8"

		var message string
		for k, v := range headers {
			message += fmt.Sprintf("%s: %s\r\n", k, v)
		}
		message += "\r\n" + toFullPlainText(r)

		smtpServer := net.JoinHostPort(conf.EMail.SMTPAddr, conf.EMail.SMTPPort)
		err = smtp.SendMail(
			smtpServer,
			smtp.PlainAuth(
				"",
				conf.EMail.User,
				conf.EMail.Password,
				conf.EMail.SMTPAddr,
			),
			conf.EMail.From,
			conf.EMail.To,
			[]byte(message),
		)

		if err != nil {
			return fmt.Errorf("Failed to send emails: %s", err)
		}
	}
	return nil
}
Пример #15
0
// checkContactEmail checks the specified Contact Email and sets an appropriate erorr message
// if there's something wrong with it.
// Returns true if email is acceptable (valid or empty).
func checkContactEmail(p *page.Params, email string) (ok bool) {
	if email == "" {
		return true
	}
	if len(email) > 500 {
		p.ErrorMsg = template.HTML(`<span class="code">Contact email</span> is too long! (cannot be longer than 500 characters)`)
		return false
	}

	if _, err := mail.ParseAddressList(email); err != nil {
		p.ErrorMsg = template.HTML(`Invalid <span class="code">Contact email</span>!`)
		return false
	}
	return true
}
Пример #16
0
func validateEmail(ctx context.Context, address string, resolver bdns.DNSResolver) (prob *probs.ProblemDetails) {
	emails, err := mail.ParseAddressList(address)
	if err != nil {
		return probs.InvalidEmail(unparseableEmailDetail)
	}
	if len(emails) > 1 {
		return probs.InvalidEmail(multipleAddressDetail)
	}
	splitEmail := strings.SplitN(emails[0].Address, "@", -1)
	domain := strings.ToLower(splitEmail[len(splitEmail)-1])
	var resultMX []string
	var resultA []net.IP
	var errMX, errA error
	var wg sync.WaitGroup
	wg.Add(2)
	go func() {
		resultMX, errMX = resolver.LookupMX(ctx, domain)
		wg.Done()
	}()
	go func() {
		resultA, errA = resolver.LookupHost(ctx, domain)
		wg.Done()
	}()
	wg.Wait()

	// We treat timeouts as non-failures for best-effort email validation
	// See: https://github.com/letsencrypt/boulder/issues/2260
	if problemIsTimeout(errMX) || problemIsTimeout(errA) {
		return nil
	}

	if errMX != nil {
		prob := bdns.ProblemDetailsFromDNSError(errMX)
		prob.Type = probs.InvalidEmailProblem
		return prob
	} else if len(resultMX) > 0 {
		return nil
	}
	if errA != nil {
		prob := bdns.ProblemDetailsFromDNSError(errA)
		prob.Type = probs.InvalidEmailProblem
		return prob
	} else if len(resultA) > 0 {
		return nil
	}

	return probs.InvalidEmail(emptyDNSResponseDetail)
}
Пример #17
0
func ExampleParseAddressList() {
	const list = "Alice <*****@*****.**>, Bob <*****@*****.**>, Eve <*****@*****.**>"
	emails, err := mail.ParseAddressList(list)
	if err != nil {
		log.Fatal(err)
	}

	for _, v := range emails {
		fmt.Println(v.Name, v.Address)
	}

	// Output:
	// Alice [email protected]
	// Bob [email protected]
	// Eve [email protected]
}
Пример #18
0
func contactFormHandler(w http.ResponseWriter, r *http.Request) {
	if corsUrl != "" {
		w.Header().Set("Access-Control-Allow-Origin", corsUrl)
	}
	w.Header().Set("Content-Type", "application/json; charset=utf-8")

	fromAddress, _ := mail.ParseAddress(from)
	toAddresses, _ := mail.ParseAddressList(to)

	response := make(map[string]string)
	err := r.ParseForm()
	if err != nil {
		response["status"] = "FAILURE"
		response["message"] = err.Error()
		jsonStr, _ := json.Marshal(response)
		fmt.Fprintf(w, string(jsonStr))
		return
	}

	body := ""
	for k, v := range r.Form {
		if k != "body" {
			body += fmt.Sprintf("%s: %s\r\n", k, v)
		}
	}
	body += "---------------------\r\n"
	body += r.Form.Get("body")

	err = sendContactFormEmail(smtpUser,
		smtpPass,
		smtpServer,
		toAddresses,
		fromAddress,
		subject,
		body,
	)
	if err == nil {
		response["status"] = "SUCCESS"
		jsonStr, _ := json.Marshal(response)
		fmt.Fprintf(w, string(jsonStr))
	} else {
		response["status"] = "FAILURE"
		response["message"] = err.Error()
		jsonStr, _ := json.Marshal(response)
		fmt.Fprintf(w, string(jsonStr))
	}
}
Пример #19
0
func Test_MakeAddressList(t *testing.T) {
	testSuite := []struct {
		list []*mail.Address
		exp  string
	}{
		{
			[]*mail.Address{
				&mail.Address{"First Last", "*****@*****.**"},
			},
			"\"First Last\" <*****@*****.**>",
		}, {
			[]*mail.Address{
				&mail.Address{"", "*****@*****.**"},
			},
			"<*****@*****.**>",
		}, {
			[]*mail.Address{
				&mail.Address{"", "*****@*****.**"},
				&mail.Address{"First Last", "*****@*****.**"},
			},
			"<*****@*****.**>, \"First Last\" <*****@*****.**>",
		}, {
			[]*mail.Address{
				&mail.Address{"", "*****@*****.**"},
				&mail.Address{"First Last", "*****@*****.**"},
				&mail.Address{"Last", "*****@*****.**"},
				&mail.Address{"", "*****@*****.**"},
			},
			"<*****@*****.**>, \"First Last\" <*****@*****.**>, \"Last\" <*****@*****.**>, <*****@*****.**>",
		}, {[]*mail.Address{}, ""},
	}

	for _, test := range testSuite {
		list := MakeAddressList(test.list)

		if list != test.exp {
			t.Errorf("Expected '%s', got '%s'", test.exp, list)
		}

		if len(test.list) > 0 {
			parsed, err := mail.ParseAddressList(list)
			if err != nil || !reflect.DeepEqual(parsed, test.list) {
				t.Errorf("Expected to reconstruct %+v, but got %+v", test.list, parsed)
			}
		}
	}
}
Пример #20
0
func getSenderAddress(m *Message) string {
	hdr := m.GetHeaderValue("From")
	if hdr == "" {
		return ""
	}
	as, err := mail.ParseAddressList(hdr)
	if err != nil {
		logger.Warning("Failed to parse sender address " + hdr)
		return ""
	}
	if len(as) == 0 {
		return ""
	} else if len(as) > 1 {
		logger.Warning("Multiple addresses found as sender address " + hdr)
	}
	return as[0].Address
}
Пример #21
0
func TestAddressConvertToStrings(t *testing.T) {
	addresses, _ := mail.ParseAddressList(sampleAddressList)
	addressList := &AddressList{addresses}
	strAddresses := addressList.ToStrings()

	if len(strAddresses) != 2 {
		t.Error("Unexpected post-conversion length")
	}

	if strAddresses[0] != "*****@*****.**" {
		t.Error("First address does not match")
	}

	if strAddresses[1] != "*****@*****.**" {
		t.Error("Second address does not match")
	}
}
Пример #22
0
func addressesWith(params map[string]interface{}, nm string) ([]*mail.Address, error) {
	o, ok := params[nm]
	if !ok {
		return nil, nil
	}
	if nil == o {
		return nil, nil
	}
	if s, ok := o.(string); ok {
		if 0 == len(s) {
			return nil, nil
		}
		addr, e := mail.ParseAddressList(s)
		if nil != e {
			return nil, errors.New("'" + nm + "' is invalid - " + e.Error())
		}
		return addr, nil
	}

	if m, ok := o.(map[string]interface{}); ok {
		address := stringWithDefault(m, "address", "")
		if 0 == len(address) {
			return nil, errors.New("'" + nm + "' is invalid.")
		}
		return []*mail.Address{&mail.Address{Name: stringWithDefault(m, "name", ""), Address: address}}, nil
	}

	if m, ok := o.([]interface{}); ok {
		addresses := make([]*mail.Address, len(m))
		var e error
		for i := range m {
			addresses[i], e = toAddress(m[i], nm)
			if nil != e {
				return nil, e
			}
		}
		return addresses, nil
	}
	return nil, errors.New("'" + nm + "' is invalid.")
}
Пример #23
0
// Begins a new story with the given form inputs (authors, words)
func beginPost(r request) response {
	authorList := strings.Join(
		SplitterOnAny(",\n\r").TrimResults().OmitEmpty().SplitToList(r.req.FormValue("authors")), ",")
	authors, err := mail.ParseAddressList(authorList)
	if err != nil {
		panic(&appError{err, "Could not parse author email addresses: " + authorList, http.StatusBadRequest})
	}
	if len(authors) == 0 {
		panic(&appError{errors.New("No authors"), "No authors", http.StatusBadRequest})
	}
	words, err := strconv.ParseUint(r.req.FormValue("words"), 10, 16)
	if err != nil {
		panic(&appError{err, "Could not parse word count as an integer", http.StatusBadRequest})
	}
	story := newStory(r, authors, int(words))
	user, _ := r.user()
	if user == nil || story.NextAuthor != user.Email {
		maybeSendMail(r.ctx(), story)
	}
	// Now issue the redirect.
	return redirect("/story/" + story.Id)
}
Пример #24
0
// Return AddressList 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
	}
	ret, err := mail.ParseAddressList(str)
	if err != nil {
		return nil, err
	}
	return ret, nil
}
Пример #25
0
func getRecipientAddresses(m *Message) []string {
	as := []string{}
	for _, hName := range recipientHeaders {
		for _, hVal := range m.GetHeaders(hName) {
			addrs, err := mail.ParseAddressList(hVal)
			if err == nil {
				for _, addr := range addrs {
					as = append(as, addr.Address)
				}
			}
		}
	}
	if encryptToSelf {
		self := getSenderAddress(m)
		if self == "" {
			logger.Warning("Was unable to determine sender address while compiling recipient key list")
		} else {
			as = append(as, self)
		}
	}
	return as
}
Пример #26
0
func (b MessageBuilder) Done() {
	b.Headers["MIME-Version"] = "1.0"
	b.Headers["Content-Type"] = "text/plain; charset=\"utf-8\""
	b.Headers["Content-Transfer-Encoding"] = "base64"

	msg822 := []byte{}
	for k := range b.Headers {
		msg822 = append(msg822, []byte(fmt.Sprintf("%s: %s\r\n", k, b.Headers[k]))...)
	}
	msg822 = append(msg822, []byte("\r\n")...)

	var body bytes.Buffer
	encoder := base64.NewEncoder(base64.StdEncoding, &body)
	encoder.Write([]byte(b.Body))
	encoder.Close()
	msg822 = append(msg822, body.Bytes()...)

	to_addrs, err := mail.ParseAddressList(b.Headers["To"])
	if err != nil {
		panic(err) // FIXME
	}
	to_strs := make([]string, len(to_addrs))
	for i, addr := range to_addrs {
		to_strs[i] = addr.Address
	}

	if len(to_strs) > 0 {
		// send the message out
		err = smtp.SendMail("localhost:25",
			smtp.PlainAuth("", "", "", ""),
			b.Headers["From"],
			to_strs,
			msg822)
		if err != nil {
			panic(err) // FIXME
		}
	}
}
Пример #27
0
func main() {

	flag.IntVar(&port, "port", 8001, "Port to listen on")
	flag.StringVar(&from, "from", "", "User that the email will be from")
	flag.StringVar(&to, "to", "", "List of recipients for the email")
	flag.StringVar(&subject, "subject", "Contact Form Details", "subject")
	flag.StringVar(&smtpServer, "smtpserver", "", "Outgoing SMTP Server")
	flag.StringVar(&smtpUser, "smtpuser", "", "SMTP User")
	flag.StringVar(&smtpPass, "smtppass", "", "SMTP password")
	flag.StringVar(&corsUrl, "corsurl", "", "The URL to put in the CORS Access-Control-Allow-Origin header")
	flag.Parse()

	if smtpServer == "" || smtpUser == "" || smtpPass == "" ||
		from == "" || to == "" {
		flag.PrintDefaults()
		return
	}

	_, err := mail.ParseAddress(from)
	if err != nil {
		log.Fatal("Error parsing From address: ", err)
		return
	}

	_, err = mail.ParseAddressList(to)
	if err != nil {
		log.Fatal("Error parsing To addresses: ", err)
		return
	}

	http.HandleFunc("/excusemeihaveaquestion", contactFormHandler)
	var server = ":" + strconv.Itoa(port)

	http.ListenAndServe(server, nil)

}
Пример #28
0
// newEmailMessage will parse an imap.FieldMap into an Email. This
// will expect the message to container the internaldate and the body with
// all headers included.
func newEmail(msgFields imap.FieldMap) (Email, error) {
	var email Email
	// parse the header
	var message bytes.Buffer
	message.Write(imap.AsBytes(msgFields["RFC822.HEADER"]))
	message.Write([]byte("\n\n"))
	rawBody := imap.AsBytes(msgFields["BODY[]"])
	message.Write(rawBody)
	msg, err := mail.ReadMessage(&message)
	if err != nil {
		return email, fmt.Errorf("unable to read header: %s", err)
	}

	from, err := mail.ParseAddress(msg.Header.Get("From"))
	if err != nil {
		return email, fmt.Errorf("unable to parse from address: %s", err)
	}

	to, err := mail.ParseAddressList(msg.Header.Get("To"))
	if err != nil {
		return email, fmt.Errorf("unable to parse to address: %s", err)
	}

	email = Email{
		Message:      msg,
		InternalDate: imap.AsDateTime(msgFields["INTERNALDATE"]),
		Precedence:   msg.Header.Get("Precedence"),
		From:         from,
		To:           to,
		Subject:      parseSubject(msg.Header.Get("Subject")),
	}

	// chunk the body up into simple chunks
	email.HTML, email.Text, email.IsMultiPart, err = parseBody(msg.Header, rawBody)
	return email, err
}
Пример #29
0
func (c *Conf) loadNotification(s *parse.SectionNode) {
	name := s.Name.Text
	if _, ok := c.Notifications[name]; ok {
		c.errorf("duplicate notification name: %s", name)
	}
	n := Notification{
		Vars:         make(map[string]string),
		ContentType:  "application/x-www-form-urlencoded",
		Name:         name,
		RunOnActions: true,
	}
	n.Text = s.RawText
	funcs := ttemplate.FuncMap{
		"V": func(v string) string {
			return c.Expand(v, n.Vars, false)
		},
		"json": func(v interface{}) string {
			b, err := json.Marshal(v)
			if err != nil {
				slog.Errorln(err)
			}
			return string(b)
		},
	}
	c.Notifications[name] = &n
	pairs := c.getPairs(s, n.Vars, sNormal)
	for _, p := range pairs {
		c.at(p.node)
		v := p.val
		switch k := p.key; k {
		case "email":
			if c.SMTPHost == "" || c.EmailFrom == "" {
				c.errorf("email notifications require both smtpHost and emailFrom to be set")
			}
			n.email = v
			email, err := mail.ParseAddressList(n.email)
			if err != nil {
				c.error(err)
			}
			n.Email = email
		case "post":
			n.post = v
			post, err := url.Parse(n.post)
			if err != nil {
				c.error(err)
			}
			n.Post = post
		case "get":
			n.get = v
			get, err := url.Parse(n.get)
			if err != nil {
				c.error(err)
			}
			n.Get = get
		case "print":
			n.Print = true
		case "contentType":
			n.ContentType = v
		case "next":
			n.next = v
			next, ok := c.Notifications[n.next]
			if !ok {
				c.errorf("unknown notification %s", n.next)
			}
			n.Next = next
		case "timeout":
			d, err := opentsdb.ParseDuration(v)
			if err != nil {
				c.error(err)
			}
			n.Timeout = time.Duration(d)
		case "body":
			n.body = v
			tmpl := ttemplate.New(name).Funcs(funcs)
			_, err := tmpl.Parse(n.body)
			if err != nil {
				c.error(err)
			}
			n.Body = tmpl
		case "runOnActions":
			n.RunOnActions = v == "true"
		default:
			c.errorf("unknown key %s", k)
		}
	}
	c.at(s)
	if n.Timeout > 0 && n.Next == nil {
		c.errorf("timeout specified without next")
	}
}
Пример #30
0
func keybindings(amua *Amua, g *gocui.Gui) error {
	switchToModeInt := func(mode Mode) func(g *gocui.Gui, v *gocui.View) error {
		return func(g *gocui.Gui, v *gocui.View) error {
			return switchToMode(amua, g, mode)
		}
	}

	maildirMove := func(dy int) func(g *gocui.Gui, v *gocui.View) error {
		return func(g *gocui.Gui, v *gocui.View) error {
			amua.curMaildirView.scroll(v, dy)
			drawSlider(amua, g)
			return nil
		}
	}
	maildirAllDown := func() func(g *gocui.Gui, v *gocui.View) error {
		return func(g *gocui.Gui, v *gocui.View) error {
			dy := len(amua.curMaildirView.md.messages) - amua.curMaildirView.cur - 1
			amua.curMaildirView.scroll(v, dy)
			drawSlider(amua, g)
			return nil
		}
	}
	messageModeToggle := func(g *gocui.Gui, v *gocui.View) error {
		switch amua.mode {
		case MessageMode:
			switchToMode(amua, g, MessageMimeMode)
		case MessageMimeMode:
			switchToMode(amua, g, MessageMode)
		}
		return nil
	}
	search := func(forward bool) func(g *gocui.Gui, v *gocui.View) error {
		return func(g *gocui.Gui, v *gocui.View) error {
			setStatus("Looking for: " + amua.searchPattern + " in " + amua.curMaildirView.md.path)
			found := false
			direction := 1
			if forward == false {
				direction = -1
			}
			for i := 0; i < len(amua.curMaildirView.md.messages); i++ {
				idx := ((direction * i) + amua.curMaildirView.cur + direction) % len(amua.curMaildirView.md.messages)
				if idx < 0 {
					idx = len(amua.curMaildirView.md.messages) + idx
				}
				if idx < 0 {
					panic(idx)
				}
				m := amua.getMessage(idx)
				if strings.Contains(m.Subject, amua.searchPattern) {
					setStatus("Found: " + amua.searchPattern + " in " + amua.curMaildirView.md.path)
					found = true
					amua.curMaildirView.curTop = idx
					amua.curMaildirView.cur = idx
					break
				}
			}
			if found {
				mv, err := g.View(MAILDIR_VIEW)
				if err != nil {
					return err
				}
				err = amua.curMaildirView.Draw(mv)
				if err != nil {
					setStatus(err.Error())
				}
			} else {
				setStatus(amua.searchPattern + " not found")
			}
			return nil
		}
	}
	enterSearch := func(forward bool) func(g *gocui.Gui, v *gocui.View) error {
		return func(g *gocui.Gui, v *gocui.View) error {
			v.Rewind()
			spbuf, err := ioutil.ReadAll(v)
			if err != nil {
				return err
			}
			prompt := amua.prompt
			amua.searchPattern = strings.TrimSpace(string(spbuf[len(prompt):]))
			switchToMode(amua, g, MaildirMode)
			search(forward)(g, v)
			return nil
		}
	}
	cancelSearch := func(g *gocui.Gui, v *gocui.View) error {
		setStatus("")
		return switchToMode(amua, g, amua.prevMode)
	}
	setFlag := func(flag MessageFlags) func(g *gocui.Gui, v *gocui.View) error {
		return func(g *gocui.Gui, v *gocui.View) error {
			m := amua.curMessage()
			m.Flags |= flag
			amua.curMaildirView.Draw(v)
			return nil
		}
	}
	unsetFlag := func(flag MessageFlags) func(g *gocui.Gui, v *gocui.View) error {
		return func(g *gocui.Gui, v *gocui.View) error {
			m := amua.curMessage()
			m.Flags &= ^flag
			amua.curMaildirView.Draw(v)
			return nil
		}
	}
	toggleFlag := func(flag MessageFlags) func(g *gocui.Gui, v *gocui.View) error {
		return func(g *gocui.Gui, v *gocui.View) error {
			m := amua.curMessage()
			if (m.Flags & flag) != 0 {
				m.Flags &= ^flag
			} else {
				m.Flags |= flag
			}
			amua.curMaildirView.Draw(v)
			return nil
		}
	}
	quit := func(g *gocui.Gui, v *gocui.View) error {
		amua.applyCurMaildirChanges()
		return gocui.ErrQuit
	}

	deleteMessage := setFlag(Trashed)
	undeleteMessage := unsetFlag(Trashed)
	unreadMessage := unsetFlag(Seen)
	readMessage := setFlag(Seen)
	toggleFlagged := toggleFlag(Flagged)

	syncMaildir := func(g *gocui.Gui, v *gocui.View) error {
		amua.applyCurMaildirChanges()
		amua.RefreshMaildir(g, v)
		v, _ = g.View(SIDE_VIEW)
		drawKnownMaildirs(amua, g, v)
		return nil
	}
	commandEnter := func(g *gocui.Gui, v *gocui.View) error {
		switch amua.mode {
		case CommandSearchMode:
			return enterSearch(true)(g, v)
		case CommandMailModeTo:
			var err error
			amua.newMail.to, err = mail.ParseAddressList(getPromptInput())
			if err != nil {
				displayError(err.Error())
				return nil
			}
			setStatus("")
			switchToMode(amua, g, SendMailMode)
		case CommandMailModeCc:
			var err error
			amua.newMail.cc, err = mail.ParseAddressList(getPromptInput())
			if err != nil {
				//flash error
				return nil
			}
			setStatus("")
			switchToMode(amua, g, SendMailMode)
		case CommandMailModeBcc:
			var err error
			amua.newMail.bcc, err = mail.ParseAddressList(getPromptInput())
			if err != nil {
				//flash error
				return nil
			}
			setStatus("")
			switchToMode(amua, g, SendMailMode)
		case CommandNewMailMode:
			if len(amua.newMail.to) == 0 {
				var err error
				amua.newMail.to, err = mail.ParseAddressList(getPromptInput())
				if err != nil {
					displayError(err.Error())
					return nil
				}
				displayPromptWithPrefill(SUBJECT_PROMPT, amua.newMail.subject)
				amua.newMail.subject = ""
			} else if amua.newMail.subject == "" {
				amua.newMail.subject = getPromptInput()
				/* Exec $EDITOR */
				tf, err := ioutil.TempFile("", "amuamail")
				if err != nil {
					log.Fatal(err)
				}
				defer os.Remove(tf.Name())
				tf.Close()
				err = ioutil.WriteFile(tf.Name(), amua.newMail.body, 0600)
				if err != nil {
					log.Fatal(err)
				}

				cmd := exec.Command(amua.ExtEditor(), tf.Name())
				cmd.Stdin = os.Stdin
				cmd.Stdout = os.Stdout
				if err := cmd.Run(); err != nil {
					log.Fatal(err)
				}
				amua.newMail.body, err = ioutil.ReadFile(tf.Name())
				if err != nil {
					log.Fatal(err.Error())
				}
				setStatus("")
				switchToMode(amua, g, SendMailMode)
				err = g.Sync()
				if err != nil {
					log.Fatal(err)
				}
			}
		}
		return nil
	}
	sendMail := func(g *gocui.Gui, v *gocui.View) error {
		err := send(&amua.newMail, cfg)
		if err != nil {
			setStatus(err.Error())
			return nil
		}
		setStatus("Sent to " + cfg.SMTPConfig.Host)
		switchToMode(amua, g, MaildirMode)
		amua.newMail = NewMail{}
		return nil
	}
	reply := func(group bool) func(g *gocui.Gui, v *gocui.View) error {
		return func(g *gocui.Gui, v *gocui.View) error {
			m := amua.curMessage()
			amua.newMail.to = buildTo(m)
			if group {
				amua.newMail.cc = buildCCs(m)
			}
			amua.newMail.subject = "Re: " + m.Subject
			buf, err := ioutil.ReadAll((*MessageAsText)(m))
			if err != nil {
				return err
			}
			buf = bytes.Replace(buf, []byte("\n"), []byte("\n> "), -1)
			replyHeader := fmt.Sprintf("On %s, %s wrote:\n> ", m.Date.Format("Mon Jan 2 15:04:05 -0700 MST 2006"), m.From)
			amua.newMail.body = append([]byte(replyHeader), buf...)
			switchToMode(amua, g, CommandNewMailMode)
			return nil
		}
	}
	replyMessage := reply(false)
	groupReplyMessage := reply(true)
	pipeMessage := func(g *gocui.Gui, v *gocui.View) error {
		m := amua.curMessage()
		cmd := exec.Command(amua.ExtEditor(), m.path)
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		if err := cmd.Run(); err != nil {
			log.Fatal(err)
		}
		setStatus("")
		switchToMode(amua, g, MaildirMode)
		err := g.Sync()
		if err != nil {
			log.Fatal(err)
		}
		/* exec a command, pipe the message there */
		return nil
	}
	type keybinding struct {
		key interface{}
		fn  gocui.KeybindingHandler
		mod bool
	}
	bindings := map[string][]keybinding{
		MAILDIR_VIEW: {
			{gocui.KeyEnter, switchToModeInt(MessageMode), false},
			{'c', switchToModeInt(KnownMaildirsMode), false},
			{'v', switchToModeInt(MessageMimeMode), false},
			{'q', quit, false},
			{'d', deleteMessage, false},
			{'u', undeleteMessage, false},
			{'G', maildirAllDown(), false},
			{'k', maildirMove(-1), false},
			{gocui.KeyArrowUp, maildirMove(-1), false},
			{'j', maildirMove(1), false},
			{'n', search(true), false},
			{'N', search(false), false},
			{'$', syncMaildir, false},
			{'F', toggleFlagged, false},
			{gocui.KeyCtrlR, readMessage, false},
			{gocui.KeyCtrlN, unreadMessage, false},
			{gocui.KeyArrowDown, maildirMove(1), false},
			{gocui.KeyCtrlF, maildirMove(10), false},
			{gocui.KeyPgdn, maildirMove(10), false},
			{gocui.KeyCtrlB, maildirMove(-10), false},
			{gocui.KeyPgup, maildirMove(-10), false},
			{'/', switchToModeInt(CommandSearchMode), false},
			{'m', switchToModeInt(CommandNewMailMode), false},
			{'r', replyMessage, false},
			{'g', groupReplyMessage, false},
			{'|', pipeMessage, false},
		},
		MESSAGE_VIEW: {
			{'q', switchToModeInt(MaildirMode), false},
			{'v', messageModeToggle, false},
			{gocui.KeyPgup, scrollMessageView(-10), false},
			{gocui.KeyPgdn, scrollMessageView(10), false},
			{gocui.KeySpace, scrollMessageView(10), false},
			{'j', scrollMessageView(1), false},
			{'k', scrollMessageView(-1), false},
			{'r', replyMessage, false},
			{'g', groupReplyMessage, false},
			{'|', pipeMessage, false},
		},
		SEND_MAIL_VIEW: {
			{'q', switchToModeInt(MaildirMode), false},
			{'t', switchToModeInt(CommandMailModeTo), false},
			{'c', switchToModeInt(CommandMailModeCc), false},
			{'b', switchToModeInt(CommandMailModeBcc), false},
			{'y', sendMail, false},
			{gocui.KeyCtrlG, switchToModeInt(MaildirMode), false},
		},
		STATUS_VIEW: {
			{gocui.KeyEnter, commandEnter, false},
			{gocui.KeyCtrlG, cancelSearch, false},
		},
		SIDE_VIEW: {
			{'j', scrollSideView(amua, 1), false},
			{gocui.KeyArrowDown, scrollSideView(amua, 1), false},
			{'k', scrollSideView(amua, -1), false},
			{gocui.KeyArrowUp, scrollSideView(amua, 1), false},
			{gocui.KeyEnter, selectNewMaildir(amua), false},
		},
		"": {
			{gocui.KeyCtrlC, quit, false},
		},
	}

	for vn, binds := range bindings {
		for _, b := range binds {
			err := g.SetKeybinding(vn, b.key, gocui.ModNone, b.fn)
			if err != nil {
				return err
			}
		}
	}
	return nil
}