github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/emails/utils.go (about)

     1  package emails
     2  
     3  import (
     4  	"context"
     5  	"net/mail"
     6  	"strings"
     7  
     8  	"github.com/keybase/client/go/externals"
     9  	"github.com/keybase/client/go/libkb"
    10  )
    11  
    12  // splitBulk splits on newline or comma.
    13  func splitBulk(s string) []string {
    14  	f := func(c rune) bool {
    15  		return c == '\n' || c == ','
    16  	}
    17  	split := strings.FieldsFunc(s, f)
    18  	for i, s := range split {
    19  		split[i] = strings.TrimSpace(s)
    20  	}
    21  	return split
    22  }
    23  
    24  // ParseSeparatedEmails parses a comma-or-new-line-separated email list that
    25  // comes in a string. It can extract emails that conform to RFC 5322 and RFC
    26  // 6532 (see net/mail documentation for more info).
    27  //
    28  // Examples of a valid e-mail entities would be:
    29  // - Jan Smith <jan@example.com>
    30  // - alice@example.org
    31  //
    32  // The "name" portion is ignored, caller will always get the raw e-mails, in
    33  // this case: { "jan@example.com", "alice@example.org" }.
    34  //
    35  // Individual e-mails have to be separated by comma or newline, and they can be
    36  // surrounded by any amount of whitespace characters that will be ignored.
    37  //
    38  // `malformed` is an optional pointer to string list to which this function can
    39  // append malformed e-mails for the caller. It can be nil.
    40  func ParseSeparatedEmails(mctx libkb.MetaContext, emails string, malformed *[]string) (ret []string) {
    41  	emailList := splitBulk(emails)
    42  	mctx.Debug("ParseSeparatedEmails: bulk email invite count: %d", len(emailList))
    43  	for _, email := range emailList {
    44  		addr, parseErr := mail.ParseAddress(email)
    45  		if parseErr != nil {
    46  			mctx.Debug("ParseSeparatedEmails: skipping malformed email %q: %s", email, parseErr)
    47  			if malformed != nil {
    48  				*malformed = append(*malformed, email)
    49  			}
    50  			continue
    51  		}
    52  
    53  		// API server side of this only accepts x.yy domain name:
    54  		parts := strings.Split(addr.Address, ".")
    55  		if len(parts[len(parts)-1]) < 2 {
    56  			mctx.Debug("ParseSeparatedEmails: skipping malformed email (domain) %q", email)
    57  			if malformed != nil {
    58  				*malformed = append(*malformed, email)
    59  			}
    60  			continue
    61  		}
    62  
    63  		ret = append(ret, addr.Address)
    64  	}
    65  	return ret
    66  }
    67  
    68  // CreateAssertionFromEmail creates AssertionURL from an e-mail address. E-mail
    69  // address is not validated apart from the minimal validation that assertion
    70  // code does, so pretty much anything that has '@' in it will pass.
    71  func CreateAssertionFromEmail(ctx context.Context, email string) (libkb.AssertionURL, error) {
    72  	actx := externals.MakeStaticAssertionContext(ctx)
    73  	// `strict` argument here doesn't actually do anything for "email" assertions.
    74  	return libkb.ParseAssertionURLKeyValue(actx, "email", email, false /* strict */)
    75  }