declare const window: any import * as url from 'url' import * as CryptoJS from 'crypto-js' import { getDomain } from 'tldjs' import { get, some, includes, map } from 'lodash' import { CLICK_TRACKING_URL } from './ResourceConstants' import { IClickTrackData, IExtractedLink, IShouldParseLinkOpts } from './contentInterfaces' import { encodeQueryData } from './helpers' const BLACKLIST_DOMAINS = [ 'click.api.driftqa.com', 'click.api.drift.com', ] export const isHttpProtocol = (url: string) => /^https?:\/\//i.test(url) export const domainIncludes = (domainList: string[], domain: string) => some(domainList, ((domainOnList) => { const domainRegex = new RegExp(`\\.${domainOnList}\/*`) return !!domain && !!domain.match(domainRegex) || domain === domainOnList })) const checkForProfileLinks = (url: string, options: IShouldParseLinkOpts) => { const { handle, globalHandle, orgHandle, profileVisibility } = options const globalHandleLink = globalHandle && `drift.me/${globalHandle}` const orgProfile = orgHandle && handle && `${orgHandle}.drift.com/${handle}` return profileVisibility !== 'PRIVATE' && (includes(url, globalHandleLink) || includes(url, orgProfile)) } export const shouldParseLink = (link: IExtractedLink, options: IShouldParseLinkOpts) => { const { orgUrl } = options const linkDomain = getDomain(link.url) const orgUrlDomain = getDomain(orgUrl) const isProfileLink = checkForProfileLinks(link.url, options) return isProfileLink || ((linkDomain === orgUrlDomain) && !domainIncludes(BLACKLIST_DOMAINS, linkDomain)) } export const addIdentityTrackingToLink = (link: string, token: string) => { const hrefParams = url.parse(link, true) hrefParams.query.d_utk = token delete hrefParams.search return url.format(hrefParams) } export const getApplicableLinksFromDoc = (doc: any) => { const links = [].slice.call(doc.getElementsByTagName('a')) return links.filter((a: HTMLAnchorElement) => isHttpProtocol(a.getAttribute('href'))) } export const extractLinksToReplace = (compose: any, emailUUID: string): IExtractedLink[] => { const body = compose.body() const endUserEmail = getTrackedEmail(compose.recipients().to[0]) const emailSubject = compose.subject() const doc = new window.DOMParser().parseFromString(body, 'text/html') const links = getApplicableLinksFromDoc(doc) return links.map((a: HTMLAnchorElement): IExtractedLink => { const href = a.getAttribute('href') const linkText = a.textContent return { linkText, emailSubject, endUserEmail, emailUUID, url: href } }) } export const replaceLinksWithWrappedLinksInInbox = (doc: any, wrappedLinks: IClickTrackData[]): string => { const links = getApplicableLinksFromDoc(doc) links.map((a: HTMLAnchorElement, index: number) => { const clickData = wrappedLinks[index] const href = clickData.url const hrefMd5 = CryptoJS.MD5(href) a.setAttribute('href', `${CLICK_TRACKING_URL}/click/${clickData.clickId}?${encodeQueryData({ u: href, h: hrefMd5 })}`) }) return doc.innerHTML } export const replaceLinksWithWrappedLinks = (body: string, wrappedLinks: IClickTrackData[]): string => { const doc = new window.DOMParser().parseFromString(body, 'text/html') const links = getApplicableLinksFromDoc(doc) links.map((a: HTMLAnchorElement, index: number) => { const clickData = wrappedLinks[index] const href = clickData.url const hrefMd5 = CryptoJS.MD5(href) a.setAttribute('href', `${CLICK_TRACKING_URL}/click/${clickData.clickId}?${encodeQueryData({ u: href, h: hrefMd5 })}`) }) return doc.getElementsByTagName('body')[0].innerHTML } export const replaceLinksWithWCalendarLinkInInbox = (doc: any, encoded: IExtractedLink[]): string => { const links = getApplicableLinksFromDoc(doc) links.map((a: HTMLAnchorElement, index: number) => { const link = encoded[index] const href = link.url a.setAttribute('href', href) }) return doc.innerHTML } export function getTrackedEmail(to: string): string { const EMAIL_RE = /<([^>]+)>/g const parsedTo = EMAIL_RE.exec(to) if (parsedTo) { return parsedTo[1] } else { return to } } export const generateOrgIdRegex = (orgId: number) => new RegExp(`orgId=${orgId}$`) export const getGmailIdFromResponse = (xhrResponse: any) => { let providerMsgId some(xhrResponse, (arr: any[]) => { if ( get(arr, `[0][0]`) === 'mla' && typeof get(arr, `[1][3]`) === 'object' && typeof get(arr, `[1][3][0]`) === 'string' ) { providerMsgId = get(arr, `[1][3][0]`) return true } if ( get(arr, `[0][0]`) === 'csd' && typeof get(arr, `[0][1]`) === 'string' ) { providerMsgId = get(arr, `[0][1]`) return true } if ( get(arr, `[0][0]`) === 'a' && typeof get(arr, `[0][3]`) === 'object' && typeof get(arr, `[0][3][0]`) === 'string' ) { providerMsgId = get(arr, `[0][3][0]`) return true } if ( get(arr, `[0][0]`) === 'a' && typeof get(arr, `[0][3]`) === 'object' && typeof get(arr, `[0][3][0]`) === 'string' ) { providerMsgId = get(arr, `[0][3][0]`) return true } if ( get(arr, `[5][0]`) === 'a' && (get(arr, `[5][3]`) as any[]).length === 3 && typeof get(arr, `[5][3][0]`) === 'string' ) { providerMsgId = get(arr, `[5][3][0]`) return true } }) return providerMsgId } export const isCalendaringLink = (url: string, baseUrl: string): boolean => { return includes(url, baseUrl) } interface IRecipient { emailAddress: string name: string } export const encodeRecipients = (link: string, recipients: IRecipient[]): string => { const hrefParams = url.parse(link, true) hrefParams.query.recipients = map(recipients, recipient => encodeURI(recipient.emailAddress)) delete hrefParams.search return url.format(hrefParams) }