package models import ( "bytes" "encoding/json" "net/mail" "net/url" "path" "text/template" ) // TemplateContext is an interface that allows both campaigns and email // requests to have a PhishingTemplateContext generated for them. type TemplateContext interface { getFromAddress() string getBaseURL() string } // PhishingTemplateContext is the context that is sent to any template, such // as the email or landing page content. type PhishingTemplateContext struct { From string URL string Tracker string TrackingURL string RId string BaseURL string BaseRecipient } // NewPhishingTemplateContext returns a populated PhishingTemplateContext, // parsing the correct fields from the provided TemplateContext and recipient. func NewPhishingTemplateContext(ctx TemplateContext, r BaseRecipient, rid string) (PhishingTemplateContext, error) { f, err := mail.ParseAddress(ctx.getFromAddress()) if err != nil { return PhishingTemplateContext{}, err } fn := f.Name if fn == "" { fn = f.Address } templateURL, err := ExecuteTemplate(ctx.getBaseURL(), r) if err != nil { return PhishingTemplateContext{}, err } // For the base URL, we'll reset the the path and the query // This will create a URL in the form of http://example.com baseURL, err := url.Parse(templateURL) if err != nil { return PhishingTemplateContext{}, err } baseURL.Path = "" baseURL.RawQuery = "" phishURL, _ := url.Parse(templateURL) q := phishURL.Query() q.Set(RecipientParameter, rid) phishURL.RawQuery = q.Encode() trackingURL, _ := url.Parse(templateURL) trackingURL.Path = path.Join(trackingURL.Path, "/track") trackingURL.RawQuery = q.Encode() return PhishingTemplateContext{ BaseRecipient: r, BaseURL: baseURL.String(), URL: phishURL.String(), TrackingURL: trackingURL.String(), Tracker: "", From: fn, RId: rid, }, nil } // ExecuteTemplate creates a templated string based on the provided // template body and data. func ExecuteTemplate(text string, data interface{}) (string, error) { buff := bytes.Buffer{} // replacing template params with no corresponding map keys with empty string. tmpl, err := template.New("template").Option("missingkey=zero").Parse(text) if err != nil { return buff.String(), err } err = tmpl.Execute(&buff, data) return buff.String(), err } // ValidationContext is used for validating templates and pages type ValidationContext struct { FromAddress string BaseURL string } func (vc ValidationContext) getFromAddress() string { return vc.FromAddress } func (vc ValidationContext) getBaseURL() string { return vc.BaseURL } // ValidateTemplate ensures that the provided text in the page or template // uses the supported template variables correctly. func ValidateTemplate(text string) error { vc := ValidationContext{ FromAddress: "foo@bar.com", BaseURL: "http://example.com", } td := Result{ BaseRecipient: BaseRecipient{ Email: "foo@bar.com", FirstName: "Foo", LastName: "Bar", Position: "Test", }, RId: "123456", } ptx, err := NewPhishingTemplateContext(vc, td.BaseRecipient, td.RId) if err != nil { return err } _, err = ExecuteTemplate(text, ptx) if err != nil { return err } return nil } // GetAllTemplateParams merges the PhishingTemplateContext with the extended_parameters // if extended_parameters are valid JSON func GetAllTemplateParams(ptx PhishingTemplateContext) map[string]string { currentTemplateParams := map[string]string{ "From": ptx.From, "URL": ptx.URL, "Tracker": ptx.Tracker, "TrackingURL": ptx.TrackingURL, "RId": ptx.RId, "BaseURL": ptx.BaseURL, "Email": ptx.BaseRecipient.Email, "FirstName": ptx.BaseRecipient.FirstName, "LastName": ptx.BaseRecipient.LastName, "Position": ptx.BaseRecipient.Position, } extendedTemplateParams := map[string]string{} err := json.Unmarshal([]byte(ptx.BaseRecipient.ExtendedTemplate), &extendedTemplateParams) if err == nil { // only merge if we have a valid JSON in extended_parameters for k, v := range extendedTemplateParams { currentTemplateParams[k] = v } } return currentTemplateParams }