spamscanner 7.0.10 → 7.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/arf.cjs.map +1 -1
- package/dist/cjs/index.cjs +1456 -191
- package/dist/cjs/index.cjs.map +4 -4
- package/dist/esm/arf.js.map +1 -1
- package/dist/esm/cli.js +1392 -142
- package/dist/esm/cli.js.map +4 -4
- package/dist/esm/index.js +1423 -174
- package/dist/esm/index.js.map +4 -4
- package/dist/types/attachments.d.ts +1 -0
- package/dist/types/entities.d.ts +2 -0
- package/dist/types/html.d.ts +2 -2
- package/dist/types/index.d.ts +26 -6
- package/dist/types/links.d.ts +26 -0
- package/dist/types/parse.d.ts +1 -1
- package/dist/types/tokenizer.d.ts +9 -0
- package/package.json +1 -1
package/dist/esm/cli.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import process4 from "node:process";
|
|
5
5
|
|
|
6
6
|
// src/cli.js
|
|
7
|
-
import { Buffer as
|
|
7
|
+
import { Buffer as Buffer15 } from "node:buffer";
|
|
8
8
|
import { spawn } from "node:child_process";
|
|
9
9
|
import {
|
|
10
10
|
createReadStream as createReadStream2,
|
|
@@ -549,9 +549,12 @@ function rewriteMessage(source, headers = [], options = {}) {
|
|
|
549
549
|
}
|
|
550
550
|
}
|
|
551
551
|
const added = headers.map(([name, value]) => foldHeader(name, Buffer2.from(String(value), "utf8").toString("latin1"), newline));
|
|
552
|
-
const mbox = kept.length > 0 && kept[0][0].startsWith("From ") ? kept.shift() : [];
|
|
552
|
+
const mbox = kept.length > 0 && kept[0][0].startsWith("From ") ? kept.shift().slice(0, 1) : [];
|
|
553
|
+
if (kept.length > 0 && /^[ \t]/.test(kept[0][0])) {
|
|
554
|
+
kept.shift();
|
|
555
|
+
}
|
|
553
556
|
const head = [...mbox, ...added, ...kept.map((field) => field.join(newline))].join(newline);
|
|
554
|
-
return Buffer2.concat([Buffer2.from(head === ""
|
|
557
|
+
return Buffer2.concat([Buffer2.from(head === "" ? newline : `${head}${newline}${newline}`, "latin1"), body]);
|
|
555
558
|
}
|
|
556
559
|
|
|
557
560
|
// src/llm.js
|
|
@@ -1730,103 +1733,285 @@ var HomographDetector = class {
|
|
|
1730
1733
|
}
|
|
1731
1734
|
};
|
|
1732
1735
|
|
|
1736
|
+
// src/entities.js
|
|
1737
|
+
var NAMED = [
|
|
1738
|
+
"AElig=c6,AMP=26,Aacute=c1,Abreve=102,Acirc=c2,Acy=410,Afr=1d504,Agrave=c0,Alpha=391,Amacr=100,And=2a",
|
|
1739
|
+
"53,Aogon=104,Aopf=1d538,ApplyFunction=2061,Aring=c5,Ascr=1d49c,Assign=2254,Atilde=c3,Auml=c4,Backsla",
|
|
1740
|
+
"sh=2216,Barv=2ae7,Barwed=2306,Bcy=411,Because=2235,Bernoullis=212c,Beta=392,Bfr=1d505,Bopf=1d539,Bre",
|
|
1741
|
+
"ve=2d8,Bscr=212c,Bumpeq=224e,CHcy=427,COPY=a9,Cacute=106,Cap=22d2,CapitalDifferentialD=2145,Cayleys=",
|
|
1742
|
+
"212d,Ccaron=10c,Ccedil=c7,Ccirc=108,Cconint=2230,Cdot=10a,Cedilla=b8,CenterDot=b7,Cfr=212d,Chi=3a7,C",
|
|
1743
|
+
"ircleDot=2299,CircleMinus=2296,CirclePlus=2295,CircleTimes=2297,ClockwiseContourIntegral=2232,CloseC",
|
|
1744
|
+
"urlyDoubleQuote=201d,CloseCurlyQuote=2019,Colon=2237,Colone=2a74,Congruent=2261,Conint=222f,ContourI",
|
|
1745
|
+
"ntegral=222e,Copf=2102,Coproduct=2210,CounterClockwiseContourIntegral=2233,Cross=2a2f,Cscr=1d49e,Cup",
|
|
1746
|
+
"=22d3,CupCap=224d,DD=2145,DDotrahd=2911,DJcy=402,DScy=405,DZcy=40f,Dagger=2021,Darr=21a1,Dashv=2ae4,",
|
|
1747
|
+
"Dcaron=10e,Dcy=414,Del=2207,Delta=394,Dfr=1d507,DiacriticalAcute=b4,DiacriticalDot=2d9,DiacriticalDo",
|
|
1748
|
+
"ubleAcute=2dd,DiacriticalGrave=60,DiacriticalTilde=2dc,Diamond=22c4,DifferentialD=2146,Dopf=1d53b,Do",
|
|
1749
|
+
"t=a8,DotDot=20dc,DotEqual=2250,DoubleContourIntegral=222f,DoubleDot=a8,DoubleDownArrow=21d3,DoubleLe",
|
|
1750
|
+
"ftArrow=21d0,DoubleLeftRightArrow=21d4,DoubleLeftTee=2ae4,DoubleLongLeftArrow=27f8,DoubleLongLeftRig",
|
|
1751
|
+
"htArrow=27fa,DoubleLongRightArrow=27f9,DoubleRightArrow=21d2,DoubleRightTee=22a8,DoubleUpArrow=21d1,",
|
|
1752
|
+
"DoubleUpDownArrow=21d5,DoubleVerticalBar=2225,DownArrow=2193,DownArrowBar=2913,DownArrowUpArrow=21f5",
|
|
1753
|
+
",DownBreve=311,DownLeftRightVector=2950,DownLeftTeeVector=295e,DownLeftVector=21bd,DownLeftVectorBar",
|
|
1754
|
+
"=2956,DownRightTeeVector=295f,DownRightVector=21c1,DownRightVectorBar=2957,DownTee=22a4,DownTeeArrow",
|
|
1755
|
+
"=21a7,Downarrow=21d3,Dscr=1d49f,Dstrok=110,ENG=14a,ETH=d0,Eacute=c9,Ecaron=11a,Ecirc=ca,Ecy=42d,Edot",
|
|
1756
|
+
"=116,Efr=1d508,Egrave=c8,Element=2208,Emacr=112,EmptySmallSquare=25fb,EmptyVerySmallSquare=25ab,Eogo",
|
|
1757
|
+
"n=118,Eopf=1d53c,Epsilon=395,Equal=2a75,EqualTilde=2242,Equilibrium=21cc,Escr=2130,Esim=2a73,Eta=397",
|
|
1758
|
+
",Euml=cb,Exists=2203,ExponentialE=2147,Fcy=424,Ffr=1d509,FilledSmallSquare=25fc,FilledVerySmallSquar",
|
|
1759
|
+
"e=25aa,Fopf=1d53d,ForAll=2200,Fouriertrf=2131,Fscr=2131,GJcy=403,GT=3e,Gamma=393,Gammad=3dc,Gbreve=1",
|
|
1760
|
+
"1e,Gcedil=122,Gcirc=11c,Gcy=413,Gdot=120,Gfr=1d50a,Gg=22d9,Gopf=1d53e,GreaterEqual=2265,GreaterEqual",
|
|
1761
|
+
"Less=22db,GreaterFullEqual=2267,GreaterGreater=2aa2,GreaterLess=2277,GreaterSlantEqual=2a7e,GreaterT",
|
|
1762
|
+
"ilde=2273,Gscr=1d4a2,Gt=226b,HARDcy=42a,Hacek=2c7,Hat=5e,Hcirc=124,Hfr=210c,HilbertSpace=210b,Hopf=2",
|
|
1763
|
+
"10d,HorizontalLine=2500,Hscr=210b,Hstrok=126,HumpDownHump=224e,HumpEqual=224f,IEcy=415,IJlig=132,IOc",
|
|
1764
|
+
"y=401,Iacute=cd,Icirc=ce,Icy=418,Idot=130,Ifr=2111,Igrave=cc,Im=2111,Imacr=12a,ImaginaryI=2148,Impli",
|
|
1765
|
+
"es=21d2,Int=222c,Integral=222b,Intersection=22c2,InvisibleComma=2063,InvisibleTimes=2062,Iogon=12e,I",
|
|
1766
|
+
"opf=1d540,Iota=399,Iscr=2110,Itilde=128,Iukcy=406,Iuml=cf,Jcirc=134,Jcy=419,Jfr=1d50d,Jopf=1d541,Jsc",
|
|
1767
|
+
"r=1d4a5,Jsercy=408,Jukcy=404,KHcy=425,KJcy=40c,Kappa=39a,Kcedil=136,Kcy=41a,Kfr=1d50e,Kopf=1d542,Ksc",
|
|
1768
|
+
"r=1d4a6,LJcy=409,LT=3c,Lacute=139,Lambda=39b,Lang=27ea,Laplacetrf=2112,Larr=219e,Lcaron=13d,Lcedil=1",
|
|
1769
|
+
"3b,Lcy=41b,LeftAngleBracket=27e8,LeftArrow=2190,LeftArrowBar=21e4,LeftArrowRightArrow=21c6,LeftCeili",
|
|
1770
|
+
"ng=2308,LeftDoubleBracket=27e6,LeftDownTeeVector=2961,LeftDownVector=21c3,LeftDownVectorBar=2959,Lef",
|
|
1771
|
+
"tFloor=230a,LeftRightArrow=2194,LeftRightVector=294e,LeftTee=22a3,LeftTeeArrow=21a4,LeftTeeVector=29",
|
|
1772
|
+
"5a,LeftTriangle=22b2,LeftTriangleBar=29cf,LeftTriangleEqual=22b4,LeftUpDownVector=2951,LeftUpTeeVect",
|
|
1773
|
+
"or=2960,LeftUpVector=21bf,LeftUpVectorBar=2958,LeftVector=21bc,LeftVectorBar=2952,Leftarrow=21d0,Lef",
|
|
1774
|
+
"trightarrow=21d4,LessEqualGreater=22da,LessFullEqual=2266,LessGreater=2276,LessLess=2aa1,LessSlantEq",
|
|
1775
|
+
"ual=2a7d,LessTilde=2272,Lfr=1d50f,Ll=22d8,Lleftarrow=21da,Lmidot=13f,LongLeftArrow=27f5,LongLeftRigh",
|
|
1776
|
+
"tArrow=27f7,LongRightArrow=27f6,Longleftarrow=27f8,Longleftrightarrow=27fa,Longrightarrow=27f9,Lopf=",
|
|
1777
|
+
"1d543,LowerLeftArrow=2199,LowerRightArrow=2198,Lscr=2112,Lsh=21b0,Lstrok=141,Lt=226a,Map=2905,Mcy=41",
|
|
1778
|
+
"c,MediumSpace=205f,Mellintrf=2133,Mfr=1d510,MinusPlus=2213,Mopf=1d544,Mscr=2133,Mu=39c,NJcy=40a,Nacu",
|
|
1779
|
+
"te=143,Ncaron=147,Ncedil=145,Ncy=41d,NegativeMediumSpace=200b,NegativeThickSpace=200b,NegativeThinSp",
|
|
1780
|
+
"ace=200b,NegativeVeryThinSpace=200b,NestedGreaterGreater=226b,NestedLessLess=226a,NewLine=a,Nfr=1d51",
|
|
1781
|
+
"1,NoBreak=2060,NonBreakingSpace=a0,Nopf=2115,Not=2aec,NotCongruent=2262,NotCupCap=226d,NotDoubleVert",
|
|
1782
|
+
"icalBar=2226,NotElement=2209,NotEqual=2260,NotEqualTilde=2242.338,NotExists=2204,NotGreater=226f,Not",
|
|
1783
|
+
"GreaterEqual=2271,NotGreaterFullEqual=2267.338,NotGreaterGreater=226b.338,NotGreaterLess=2279,NotGre",
|
|
1784
|
+
"aterSlantEqual=2a7e.338,NotGreaterTilde=2275,NotHumpDownHump=224e.338,NotHumpEqual=224f.338,NotLeftT",
|
|
1785
|
+
"riangle=22ea,NotLeftTriangleBar=29cf.338,NotLeftTriangleEqual=22ec,NotLess=226e,NotLessEqual=2270,No",
|
|
1786
|
+
"tLessGreater=2278,NotLessLess=226a.338,NotLessSlantEqual=2a7d.338,NotLessTilde=2274,NotNestedGreater",
|
|
1787
|
+
"Greater=2aa2.338,NotNestedLessLess=2aa1.338,NotPrecedes=2280,NotPrecedesEqual=2aaf.338,NotPrecedesSl",
|
|
1788
|
+
"antEqual=22e0,NotReverseElement=220c,NotRightTriangle=22eb,NotRightTriangleBar=29d0.338,NotRightTria",
|
|
1789
|
+
"ngleEqual=22ed,NotSquareSubset=228f.338,NotSquareSubsetEqual=22e2,NotSquareSuperset=2290.338,NotSqua",
|
|
1790
|
+
"reSupersetEqual=22e3,NotSubset=2282.20d2,NotSubsetEqual=2288,NotSucceeds=2281,NotSucceedsEqual=2ab0.",
|
|
1791
|
+
"338,NotSucceedsSlantEqual=22e1,NotSucceedsTilde=227f.338,NotSuperset=2283.20d2,NotSupersetEqual=2289",
|
|
1792
|
+
",NotTilde=2241,NotTildeEqual=2244,NotTildeFullEqual=2247,NotTildeTilde=2249,NotVerticalBar=2224,Nscr",
|
|
1793
|
+
"=1d4a9,Ntilde=d1,Nu=39d,OElig=152,Oacute=d3,Ocirc=d4,Ocy=41e,Odblac=150,Ofr=1d512,Ograve=d2,Omacr=14",
|
|
1794
|
+
"c,Omega=3a9,Omicron=39f,Oopf=1d546,OpenCurlyDoubleQuote=201c,OpenCurlyQuote=2018,Or=2a54,Oscr=1d4aa,",
|
|
1795
|
+
"Oslash=d8,Otilde=d5,Otimes=2a37,Ouml=d6,OverBar=203e,OverBrace=23de,OverBracket=23b4,OverParenthesis",
|
|
1796
|
+
"=23dc,PartialD=2202,Pcy=41f,Pfr=1d513,Phi=3a6,Pi=3a0,PlusMinus=b1,Poincareplane=210c,Popf=2119,Pr=2a",
|
|
1797
|
+
"bb,Precedes=227a,PrecedesEqual=2aaf,PrecedesSlantEqual=227c,PrecedesTilde=227e,Prime=2033,Product=22",
|
|
1798
|
+
"0f,Proportion=2237,Proportional=221d,Pscr=1d4ab,Psi=3a8,QUOT=22,Qfr=1d514,Qopf=211a,Qscr=1d4ac,RBarr",
|
|
1799
|
+
"=2910,REG=ae,Racute=154,Rang=27eb,Rarr=21a0,Rarrtl=2916,Rcaron=158,Rcedil=156,Rcy=420,Re=211c,Revers",
|
|
1800
|
+
"eElement=220b,ReverseEquilibrium=21cb,ReverseUpEquilibrium=296f,Rfr=211c,Rho=3a1,RightAngleBracket=2",
|
|
1801
|
+
"7e9,RightArrow=2192,RightArrowBar=21e5,RightArrowLeftArrow=21c4,RightCeiling=2309,RightDoubleBracket",
|
|
1802
|
+
"=27e7,RightDownTeeVector=295d,RightDownVector=21c2,RightDownVectorBar=2955,RightFloor=230b,RightTee=",
|
|
1803
|
+
"22a2,RightTeeArrow=21a6,RightTeeVector=295b,RightTriangle=22b3,RightTriangleBar=29d0,RightTriangleEq",
|
|
1804
|
+
"ual=22b5,RightUpDownVector=294f,RightUpTeeVector=295c,RightUpVector=21be,RightUpVectorBar=2954,Right",
|
|
1805
|
+
"Vector=21c0,RightVectorBar=2953,Rightarrow=21d2,Ropf=211d,RoundImplies=2970,Rrightarrow=21db,Rscr=21",
|
|
1806
|
+
"1b,Rsh=21b1,RuleDelayed=29f4,SHCHcy=429,SHcy=428,SOFTcy=42c,Sacute=15a,Sc=2abc,Scaron=160,Scedil=15e",
|
|
1807
|
+
",Scirc=15c,Scy=421,Sfr=1d516,ShortDownArrow=2193,ShortLeftArrow=2190,ShortRightArrow=2192,ShortUpArr",
|
|
1808
|
+
"ow=2191,Sigma=3a3,SmallCircle=2218,Sopf=1d54a,Sqrt=221a,Square=25a1,SquareIntersection=2293,SquareSu",
|
|
1809
|
+
"bset=228f,SquareSubsetEqual=2291,SquareSuperset=2290,SquareSupersetEqual=2292,SquareUnion=2294,Sscr=",
|
|
1810
|
+
"1d4ae,Star=22c6,Sub=22d0,Subset=22d0,SubsetEqual=2286,Succeeds=227b,SucceedsEqual=2ab0,SucceedsSlant",
|
|
1811
|
+
"Equal=227d,SucceedsTilde=227f,SuchThat=220b,Sum=2211,Sup=22d1,Superset=2283,SupersetEqual=2287,Supse",
|
|
1812
|
+
"t=22d1,THORN=de,TRADE=2122,TSHcy=40b,TScy=426,Tab=9,Tau=3a4,Tcaron=164,Tcedil=162,Tcy=422,Tfr=1d517,",
|
|
1813
|
+
"Therefore=2234,Theta=398,ThickSpace=205f.200a,ThinSpace=2009,Tilde=223c,TildeEqual=2243,TildeFullEqu",
|
|
1814
|
+
"al=2245,TildeTilde=2248,Topf=1d54b,TripleDot=20db,Tscr=1d4af,Tstrok=166,Uacute=da,Uarr=219f,Uarrocir",
|
|
1815
|
+
"=2949,Ubrcy=40e,Ubreve=16c,Ucirc=db,Ucy=423,Udblac=170,Ufr=1d518,Ugrave=d9,Umacr=16a,UnderBar=5f,Und",
|
|
1816
|
+
"erBrace=23df,UnderBracket=23b5,UnderParenthesis=23dd,Union=22c3,UnionPlus=228e,Uogon=172,Uopf=1d54c,",
|
|
1817
|
+
"UpArrow=2191,UpArrowBar=2912,UpArrowDownArrow=21c5,UpDownArrow=2195,UpEquilibrium=296e,UpTee=22a5,Up",
|
|
1818
|
+
"TeeArrow=21a5,Uparrow=21d1,Updownarrow=21d5,UpperLeftArrow=2196,UpperRightArrow=2197,Upsi=3d2,Upsilo",
|
|
1819
|
+
"n=3a5,Uring=16e,Uscr=1d4b0,Utilde=168,Uuml=dc,VDash=22ab,Vbar=2aeb,Vcy=412,Vdash=22a9,Vdashl=2ae6,Ve",
|
|
1820
|
+
"e=22c1,Verbar=2016,Vert=2016,VerticalBar=2223,VerticalLine=7c,VerticalSeparator=2758,VerticalTilde=2",
|
|
1821
|
+
"240,VeryThinSpace=200a,Vfr=1d519,Vopf=1d54d,Vscr=1d4b1,Vvdash=22aa,Wcirc=174,Wedge=22c0,Wfr=1d51a,Wo",
|
|
1822
|
+
"pf=1d54e,Wscr=1d4b2,Xfr=1d51b,Xi=39e,Xopf=1d54f,Xscr=1d4b3,YAcy=42f,YIcy=407,YUcy=42e,Yacute=dd,Ycir",
|
|
1823
|
+
"c=176,Ycy=42b,Yfr=1d51c,Yopf=1d550,Yscr=1d4b4,Yuml=178,ZHcy=416,Zacute=179,Zcaron=17d,Zcy=417,Zdot=1",
|
|
1824
|
+
"7b,ZeroWidthSpace=200b,Zeta=396,Zfr=2128,Zopf=2124,Zscr=1d4b5,aacute=e1,abreve=103,ac=223e,acE=223e.",
|
|
1825
|
+
"333,acd=223f,acirc=e2,acute=b4,acy=430,aelig=e6,af=2061,afr=1d51e,agrave=e0,alefsym=2135,aleph=2135,",
|
|
1826
|
+
"alpha=3b1,amacr=101,amalg=2a3f,amp=26,and=2227,andand=2a55,andd=2a5c,andslope=2a58,andv=2a5a,ang=222",
|
|
1827
|
+
"0,ange=29a4,angle=2220,angmsd=2221,angmsdaa=29a8,angmsdab=29a9,angmsdac=29aa,angmsdad=29ab,angmsdae=",
|
|
1828
|
+
"29ac,angmsdaf=29ad,angmsdag=29ae,angmsdah=29af,angrt=221f,angrtvb=22be,angrtvbd=299d,angsph=2222,ang",
|
|
1829
|
+
"st=c5,angzarr=237c,aogon=105,aopf=1d552,ap=2248,apE=2a70,apacir=2a6f,ape=224a,apid=224b,apos=27,appr",
|
|
1830
|
+
"ox=2248,approxeq=224a,aring=e5,ascr=1d4b6,ast=2a,asymp=2248,asympeq=224d,atilde=e3,auml=e4,awconint=",
|
|
1831
|
+
"2233,awint=2a11,bNot=2aed,backcong=224c,backepsilon=3f6,backprime=2035,backsim=223d,backsimeq=22cd,b",
|
|
1832
|
+
"arvee=22bd,barwed=2305,barwedge=2305,bbrk=23b5,bbrktbrk=23b6,bcong=224c,bcy=431,bdquo=201e,becaus=22",
|
|
1833
|
+
"35,because=2235,bemptyv=29b0,bepsi=3f6,bernou=212c,beta=3b2,beth=2136,between=226c,bfr=1d51f,bigcap=",
|
|
1834
|
+
"22c2,bigcirc=25ef,bigcup=22c3,bigodot=2a00,bigoplus=2a01,bigotimes=2a02,bigsqcup=2a06,bigstar=2605,b",
|
|
1835
|
+
"igtriangledown=25bd,bigtriangleup=25b3,biguplus=2a04,bigvee=22c1,bigwedge=22c0,bkarow=290d,blackloze",
|
|
1836
|
+
"nge=29eb,blacksquare=25aa,blacktriangle=25b4,blacktriangledown=25be,blacktriangleleft=25c2,blacktria",
|
|
1837
|
+
"ngleright=25b8,blank=2423,blk12=2592,blk14=2591,blk34=2593,block=2588,bne=3d.20e5,bnequiv=2261.20e5,",
|
|
1838
|
+
"bnot=2310,bopf=1d553,bot=22a5,bottom=22a5,bowtie=22c8,boxDL=2557,boxDR=2554,boxDl=2556,boxDr=2553,bo",
|
|
1839
|
+
"xH=2550,boxHD=2566,boxHU=2569,boxHd=2564,boxHu=2567,boxUL=255d,boxUR=255a,boxUl=255c,boxUr=2559,boxV",
|
|
1840
|
+
"=2551,boxVH=256c,boxVL=2563,boxVR=2560,boxVh=256b,boxVl=2562,boxVr=255f,boxbox=29c9,boxdL=2555,boxdR",
|
|
1841
|
+
"=2552,boxdl=2510,boxdr=250c,boxh=2500,boxhD=2565,boxhU=2568,boxhd=252c,boxhu=2534,boxminus=229f,boxp",
|
|
1842
|
+
"lus=229e,boxtimes=22a0,boxuL=255b,boxuR=2558,boxul=2518,boxur=2514,boxv=2502,boxvH=256a,boxvL=2561,b",
|
|
1843
|
+
"oxvR=255e,boxvh=253c,boxvl=2524,boxvr=251c,bprime=2035,breve=2d8,brvbar=a6,bscr=1d4b7,bsemi=204f,bsi",
|
|
1844
|
+
"m=223d,bsime=22cd,bsol=5c,bsolb=29c5,bsolhsub=27c8,bull=2022,bullet=2022,bump=224e,bumpE=2aae,bumpe=",
|
|
1845
|
+
"224f,bumpeq=224f,cacute=107,cap=2229,capand=2a44,capbrcup=2a49,capcap=2a4b,capcup=2a47,capdot=2a40,c",
|
|
1846
|
+
"aps=2229.fe00,caret=2041,caron=2c7,ccaps=2a4d,ccaron=10d,ccedil=e7,ccirc=109,ccups=2a4c,ccupssm=2a50",
|
|
1847
|
+
",cdot=10b,cedil=b8,cemptyv=29b2,cent=a2,centerdot=b7,cfr=1d520,chcy=447,check=2713,checkmark=2713,ch",
|
|
1848
|
+
"i=3c7,cir=25cb,cirE=29c3,circ=2c6,circeq=2257,circlearrowleft=21ba,circlearrowright=21bb,circledR=ae",
|
|
1849
|
+
",circledS=24c8,circledast=229b,circledcirc=229a,circleddash=229d,cire=2257,cirfnint=2a10,cirmid=2aef",
|
|
1850
|
+
",cirscir=29c2,clubs=2663,clubsuit=2663,colon=3a,colone=2254,coloneq=2254,comma=2c,commat=40,comp=220",
|
|
1851
|
+
"1,compfn=2218,complement=2201,complexes=2102,cong=2245,congdot=2a6d,conint=222e,copf=1d554,coprod=22",
|
|
1852
|
+
"10,copy=a9,copysr=2117,crarr=21b5,cross=2717,cscr=1d4b8,csub=2acf,csube=2ad1,csup=2ad0,csupe=2ad2,ct",
|
|
1853
|
+
"dot=22ef,cudarrl=2938,cudarrr=2935,cuepr=22de,cuesc=22df,cularr=21b6,cularrp=293d,cup=222a,cupbrcap=",
|
|
1854
|
+
"2a48,cupcap=2a46,cupcup=2a4a,cupdot=228d,cupor=2a45,cups=222a.fe00,curarr=21b7,curarrm=293c,curlyeqp",
|
|
1855
|
+
"rec=22de,curlyeqsucc=22df,curlyvee=22ce,curlywedge=22cf,curren=a4,curvearrowleft=21b6,curvearrowrigh",
|
|
1856
|
+
"t=21b7,cuvee=22ce,cuwed=22cf,cwconint=2232,cwint=2231,cylcty=232d,dArr=21d3,dHar=2965,dagger=2020,da",
|
|
1857
|
+
"leth=2138,darr=2193,dash=2010,dashv=22a3,dbkarow=290f,dblac=2dd,dcaron=10f,dcy=434,dd=2146,ddagger=2",
|
|
1858
|
+
"021,ddarr=21ca,ddotseq=2a77,deg=b0,delta=3b4,demptyv=29b1,dfisht=297f,dfr=1d521,dharl=21c3,dharr=21c",
|
|
1859
|
+
"2,diam=22c4,diamond=22c4,diamondsuit=2666,diams=2666,die=a8,digamma=3dd,disin=22f2,div=f7,divide=f7,",
|
|
1860
|
+
"divideontimes=22c7,divonx=22c7,djcy=452,dlcorn=231e,dlcrop=230d,dollar=24,dopf=1d555,dot=2d9,doteq=2",
|
|
1861
|
+
"250,doteqdot=2251,dotminus=2238,dotplus=2214,dotsquare=22a1,doublebarwedge=2306,downarrow=2193,downd",
|
|
1862
|
+
"ownarrows=21ca,downharpoonleft=21c3,downharpoonright=21c2,drbkarow=2910,drcorn=231f,drcrop=230c,dscr",
|
|
1863
|
+
"=1d4b9,dscy=455,dsol=29f6,dstrok=111,dtdot=22f1,dtri=25bf,dtrif=25be,duarr=21f5,duhar=296f,dwangle=2",
|
|
1864
|
+
"9a6,dzcy=45f,dzigrarr=27ff,eDDot=2a77,eDot=2251,eacute=e9,easter=2a6e,ecaron=11b,ecir=2256,ecirc=ea,",
|
|
1865
|
+
"ecolon=2255,ecy=44d,edot=117,ee=2147,efDot=2252,efr=1d522,eg=2a9a,egrave=e8,egs=2a96,egsdot=2a98,el=",
|
|
1866
|
+
"2a99,elinters=23e7,ell=2113,els=2a95,elsdot=2a97,emacr=113,empty=2205,emptyset=2205,emptyv=2205,emsp",
|
|
1867
|
+
"=2003,emsp13=2004,emsp14=2005,eng=14b,ensp=2002,eogon=119,eopf=1d556,epar=22d5,eparsl=29e3,eplus=2a7",
|
|
1868
|
+
"1,epsi=3b5,epsilon=3b5,epsiv=3f5,eqcirc=2256,eqcolon=2255,eqsim=2242,eqslantgtr=2a96,eqslantless=2a9",
|
|
1869
|
+
"5,equals=3d,equest=225f,equiv=2261,equivDD=2a78,eqvparsl=29e5,erDot=2253,erarr=2971,escr=212f,esdot=",
|
|
1870
|
+
"2250,esim=2242,eta=3b7,eth=f0,euml=eb,euro=20ac,excl=21,exist=2203,expectation=2130,exponentiale=214",
|
|
1871
|
+
"7,fallingdotseq=2252,fcy=444,female=2640,ffilig=fb03,fflig=fb00,ffllig=fb04,ffr=1d523,filig=fb01,fjl",
|
|
1872
|
+
"ig=66.6a,flat=266d,fllig=fb02,fltns=25b1,fnof=192,fopf=1d557,forall=2200,fork=22d4,forkv=2ad9,fparti",
|
|
1873
|
+
"nt=2a0d,frac12=bd,frac13=2153,frac14=bc,frac15=2155,frac16=2159,frac18=215b,frac23=2154,frac25=2156,",
|
|
1874
|
+
"frac34=be,frac35=2157,frac38=215c,frac45=2158,frac56=215a,frac58=215d,frac78=215e,frasl=2044,frown=2",
|
|
1875
|
+
"322,fscr=1d4bb,gE=2267,gEl=2a8c,gacute=1f5,gamma=3b3,gammad=3dd,gap=2a86,gbreve=11f,gcirc=11d,gcy=43",
|
|
1876
|
+
"3,gdot=121,ge=2265,gel=22db,geq=2265,geqq=2267,geqslant=2a7e,ges=2a7e,gescc=2aa9,gesdot=2a80,gesdoto",
|
|
1877
|
+
"=2a82,gesdotol=2a84,gesl=22db.fe00,gesles=2a94,gfr=1d524,gg=226b,ggg=22d9,gimel=2137,gjcy=453,gl=227",
|
|
1878
|
+
"7,glE=2a92,gla=2aa5,glj=2aa4,gnE=2269,gnap=2a8a,gnapprox=2a8a,gne=2a88,gneq=2a88,gneqq=2269,gnsim=22",
|
|
1879
|
+
"e7,gopf=1d558,grave=60,gscr=210a,gsim=2273,gsime=2a8e,gsiml=2a90,gt=3e,gtcc=2aa7,gtcir=2a7a,gtdot=22",
|
|
1880
|
+
"d7,gtlPar=2995,gtquest=2a7c,gtrapprox=2a86,gtrarr=2978,gtrdot=22d7,gtreqless=22db,gtreqqless=2a8c,gt",
|
|
1881
|
+
"rless=2277,gtrsim=2273,gvertneqq=2269.fe00,gvnE=2269.fe00,hArr=21d4,hairsp=200a,half=bd,hamilt=210b,",
|
|
1882
|
+
"hardcy=44a,harr=2194,harrcir=2948,harrw=21ad,hbar=210f,hcirc=125,hearts=2665,heartsuit=2665,hellip=2",
|
|
1883
|
+
"026,hercon=22b9,hfr=1d525,hksearow=2925,hkswarow=2926,hoarr=21ff,homtht=223b,hookleftarrow=21a9,hook",
|
|
1884
|
+
"rightarrow=21aa,hopf=1d559,horbar=2015,hscr=1d4bd,hslash=210f,hstrok=127,hybull=2043,hyphen=2010,iac",
|
|
1885
|
+
"ute=ed,ic=2063,icirc=ee,icy=438,iecy=435,iexcl=a1,iff=21d4,ifr=1d526,igrave=ec,ii=2148,iiiint=2a0c,i",
|
|
1886
|
+
"iint=222d,iinfin=29dc,iiota=2129,ijlig=133,imacr=12b,image=2111,imagline=2110,imagpart=2111,imath=13",
|
|
1887
|
+
"1,imof=22b7,imped=1b5,in=2208,incare=2105,infin=221e,infintie=29dd,inodot=131,int=222b,intcal=22ba,i",
|
|
1888
|
+
"ntegers=2124,intercal=22ba,intlarhk=2a17,intprod=2a3c,iocy=451,iogon=12f,iopf=1d55a,iota=3b9,iprod=2",
|
|
1889
|
+
"a3c,iquest=bf,iscr=1d4be,isin=2208,isinE=22f9,isindot=22f5,isins=22f4,isinsv=22f3,isinv=2208,it=2062",
|
|
1890
|
+
",itilde=129,iukcy=456,iuml=ef,jcirc=135,jcy=439,jfr=1d527,jmath=237,jopf=1d55b,jscr=1d4bf,jsercy=458",
|
|
1891
|
+
",jukcy=454,kappa=3ba,kappav=3f0,kcedil=137,kcy=43a,kfr=1d528,kgreen=138,khcy=445,kjcy=45c,kopf=1d55c",
|
|
1892
|
+
",kscr=1d4c0,lAarr=21da,lArr=21d0,lAtail=291b,lBarr=290e,lE=2266,lEg=2a8b,lHar=2962,lacute=13a,laempt",
|
|
1893
|
+
"yv=29b4,lagran=2112,lambda=3bb,lang=27e8,langd=2991,langle=27e8,lap=2a85,laquo=ab,larr=2190,larrb=21",
|
|
1894
|
+
"e4,larrbfs=291f,larrfs=291d,larrhk=21a9,larrlp=21ab,larrpl=2939,larrsim=2973,larrtl=21a2,lat=2aab,la",
|
|
1895
|
+
"tail=2919,late=2aad,lates=2aad.fe00,lbarr=290c,lbbrk=2772,lbrace=7b,lbrack=5b,lbrke=298b,lbrksld=298",
|
|
1896
|
+
"f,lbrkslu=298d,lcaron=13e,lcedil=13c,lceil=2308,lcub=7b,lcy=43b,ldca=2936,ldquo=201c,ldquor=201e,ldr",
|
|
1897
|
+
"dhar=2967,ldrushar=294b,ldsh=21b2,le=2264,leftarrow=2190,leftarrowtail=21a2,leftharpoondown=21bd,lef",
|
|
1898
|
+
"tharpoonup=21bc,leftleftarrows=21c7,leftrightarrow=2194,leftrightarrows=21c6,leftrightharpoons=21cb,",
|
|
1899
|
+
"leftrightsquigarrow=21ad,leftthreetimes=22cb,leg=22da,leq=2264,leqq=2266,leqslant=2a7d,les=2a7d,lesc",
|
|
1900
|
+
"c=2aa8,lesdot=2a7f,lesdoto=2a81,lesdotor=2a83,lesg=22da.fe00,lesges=2a93,lessapprox=2a85,lessdot=22d",
|
|
1901
|
+
"6,lesseqgtr=22da,lesseqqgtr=2a8b,lessgtr=2276,lesssim=2272,lfisht=297c,lfloor=230a,lfr=1d529,lg=2276",
|
|
1902
|
+
",lgE=2a91,lhard=21bd,lharu=21bc,lharul=296a,lhblk=2584,ljcy=459,ll=226a,llarr=21c7,llcorner=231e,llh",
|
|
1903
|
+
"ard=296b,lltri=25fa,lmidot=140,lmoust=23b0,lmoustache=23b0,lnE=2268,lnap=2a89,lnapprox=2a89,lne=2a87",
|
|
1904
|
+
",lneq=2a87,lneqq=2268,lnsim=22e6,loang=27ec,loarr=21fd,lobrk=27e6,longleftarrow=27f5,longleftrightar",
|
|
1905
|
+
"row=27f7,longmapsto=27fc,longrightarrow=27f6,looparrowleft=21ab,looparrowright=21ac,lopar=2985,lopf=",
|
|
1906
|
+
"1d55d,loplus=2a2d,lotimes=2a34,lowast=2217,lowbar=5f,loz=25ca,lozenge=25ca,lozf=29eb,lpar=28,lparlt=",
|
|
1907
|
+
"2993,lrarr=21c6,lrcorner=231f,lrhar=21cb,lrhard=296d,lrm=200e,lrtri=22bf,lsaquo=2039,lscr=1d4c1,lsh=",
|
|
1908
|
+
"21b0,lsim=2272,lsime=2a8d,lsimg=2a8f,lsqb=5b,lsquo=2018,lsquor=201a,lstrok=142,lt=3c,ltcc=2aa6,ltcir",
|
|
1909
|
+
"=2a79,ltdot=22d6,lthree=22cb,ltimes=22c9,ltlarr=2976,ltquest=2a7b,ltrPar=2996,ltri=25c3,ltrie=22b4,l",
|
|
1910
|
+
"trif=25c2,lurdshar=294a,luruhar=2966,lvertneqq=2268.fe00,lvnE=2268.fe00,mDDot=223a,macr=af,male=2642",
|
|
1911
|
+
",malt=2720,maltese=2720,map=21a6,mapsto=21a6,mapstodown=21a7,mapstoleft=21a4,mapstoup=21a5,marker=25",
|
|
1912
|
+
"ae,mcomma=2a29,mcy=43c,mdash=2014,measuredangle=2221,mfr=1d52a,mho=2127,micro=b5,mid=2223,midast=2a,",
|
|
1913
|
+
"midcir=2af0,middot=b7,minus=2212,minusb=229f,minusd=2238,minusdu=2a2a,mlcp=2adb,mldr=2026,mnplus=221",
|
|
1914
|
+
"3,models=22a7,mopf=1d55e,mp=2213,mscr=1d4c2,mstpos=223e,mu=3bc,multimap=22b8,mumap=22b8,nGg=22d9.338",
|
|
1915
|
+
",nGt=226b.20d2,nGtv=226b.338,nLeftarrow=21cd,nLeftrightarrow=21ce,nLl=22d8.338,nLt=226a.20d2,nLtv=22",
|
|
1916
|
+
"6a.338,nRightarrow=21cf,nVDash=22af,nVdash=22ae,nabla=2207,nacute=144,nang=2220.20d2,nap=2249,napE=2",
|
|
1917
|
+
"a70.338,napid=224b.338,napos=149,napprox=2249,natur=266e,natural=266e,naturals=2115,nbsp=a0,nbump=22",
|
|
1918
|
+
"4e.338,nbumpe=224f.338,ncap=2a43,ncaron=148,ncedil=146,ncong=2247,ncongdot=2a6d.338,ncup=2a42,ncy=43",
|
|
1919
|
+
"d,ndash=2013,ne=2260,neArr=21d7,nearhk=2924,nearr=2197,nearrow=2197,nedot=2250.338,nequiv=2262,nesea",
|
|
1920
|
+
"r=2928,nesim=2242.338,nexist=2204,nexists=2204,nfr=1d52b,ngE=2267.338,nge=2271,ngeq=2271,ngeqq=2267.",
|
|
1921
|
+
"338,ngeqslant=2a7e.338,nges=2a7e.338,ngsim=2275,ngt=226f,ngtr=226f,nhArr=21ce,nharr=21ae,nhpar=2af2,",
|
|
1922
|
+
"ni=220b,nis=22fc,nisd=22fa,niv=220b,njcy=45a,nlArr=21cd,nlE=2266.338,nlarr=219a,nldr=2025,nle=2270,n",
|
|
1923
|
+
"leftarrow=219a,nleftrightarrow=21ae,nleq=2270,nleqq=2266.338,nleqslant=2a7d.338,nles=2a7d.338,nless=",
|
|
1924
|
+
"226e,nlsim=2274,nlt=226e,nltri=22ea,nltrie=22ec,nmid=2224,nopf=1d55f,not=ac,notin=2209,notinE=22f9.3",
|
|
1925
|
+
"38,notindot=22f5.338,notinva=2209,notinvb=22f7,notinvc=22f6,notni=220c,notniva=220c,notnivb=22fe,not",
|
|
1926
|
+
"nivc=22fd,npar=2226,nparallel=2226,nparsl=2afd.20e5,npart=2202.338,npolint=2a14,npr=2280,nprcue=22e0",
|
|
1927
|
+
",npre=2aaf.338,nprec=2280,npreceq=2aaf.338,nrArr=21cf,nrarr=219b,nrarrc=2933.338,nrarrw=219d.338,nri",
|
|
1928
|
+
"ghtarrow=219b,nrtri=22eb,nrtrie=22ed,nsc=2281,nsccue=22e1,nsce=2ab0.338,nscr=1d4c3,nshortmid=2224,ns",
|
|
1929
|
+
"hortparallel=2226,nsim=2241,nsime=2244,nsimeq=2244,nsmid=2224,nspar=2226,nsqsube=22e2,nsqsupe=22e3,n",
|
|
1930
|
+
"sub=2284,nsubE=2ac5.338,nsube=2288,nsubset=2282.20d2,nsubseteq=2288,nsubseteqq=2ac5.338,nsucc=2281,n",
|
|
1931
|
+
"succeq=2ab0.338,nsup=2285,nsupE=2ac6.338,nsupe=2289,nsupset=2283.20d2,nsupseteq=2289,nsupseteqq=2ac6",
|
|
1932
|
+
".338,ntgl=2279,ntilde=f1,ntlg=2278,ntriangleleft=22ea,ntrianglelefteq=22ec,ntriangleright=22eb,ntria",
|
|
1933
|
+
"nglerighteq=22ed,nu=3bd,num=23,numero=2116,numsp=2007,nvDash=22ad,nvHarr=2904,nvap=224d.20d2,nvdash=",
|
|
1934
|
+
"22ac,nvge=2265.20d2,nvgt=3e.20d2,nvinfin=29de,nvlArr=2902,nvle=2264.20d2,nvlt=3c.20d2,nvltrie=22b4.2",
|
|
1935
|
+
"0d2,nvrArr=2903,nvrtrie=22b5.20d2,nvsim=223c.20d2,nwArr=21d6,nwarhk=2923,nwarr=2196,nwarrow=2196,nwn",
|
|
1936
|
+
"ear=2927,oS=24c8,oacute=f3,oast=229b,ocir=229a,ocirc=f4,ocy=43e,odash=229d,odblac=151,odiv=2a38,odot",
|
|
1937
|
+
"=2299,odsold=29bc,oelig=153,ofcir=29bf,ofr=1d52c,ogon=2db,ograve=f2,ogt=29c1,ohbar=29b5,ohm=3a9,oint",
|
|
1938
|
+
"=222e,olarr=21ba,olcir=29be,olcross=29bb,oline=203e,olt=29c0,omacr=14d,omega=3c9,omicron=3bf,omid=29",
|
|
1939
|
+
"b6,ominus=2296,oopf=1d560,opar=29b7,operp=29b9,oplus=2295,or=2228,orarr=21bb,ord=2a5d,order=2134,ord",
|
|
1940
|
+
"erof=2134,ordf=aa,ordm=ba,origof=22b6,oror=2a56,orslope=2a57,orv=2a5b,oscr=2134,oslash=f8,osol=2298,",
|
|
1941
|
+
"otilde=f5,otimes=2297,otimesas=2a36,ouml=f6,ovbar=233d,par=2225,para=b6,parallel=2225,parsim=2af3,pa",
|
|
1942
|
+
"rsl=2afd,part=2202,pcy=43f,percnt=25,period=2e,permil=2030,perp=22a5,pertenk=2031,pfr=1d52d,phi=3c6,",
|
|
1943
|
+
"phiv=3d5,phmmat=2133,phone=260e,pi=3c0,pitchfork=22d4,piv=3d6,planck=210f,planckh=210e,plankv=210f,p",
|
|
1944
|
+
"lus=2b,plusacir=2a23,plusb=229e,pluscir=2a22,plusdo=2214,plusdu=2a25,pluse=2a72,plusmn=b1,plussim=2a",
|
|
1945
|
+
"26,plustwo=2a27,pm=b1,pointint=2a15,popf=1d561,pound=a3,pr=227a,prE=2ab3,prap=2ab7,prcue=227c,pre=2a",
|
|
1946
|
+
"af,prec=227a,precapprox=2ab7,preccurlyeq=227c,preceq=2aaf,precnapprox=2ab9,precneqq=2ab5,precnsim=22",
|
|
1947
|
+
"e8,precsim=227e,prime=2032,primes=2119,prnE=2ab5,prnap=2ab9,prnsim=22e8,prod=220f,profalar=232e,prof",
|
|
1948
|
+
"line=2312,profsurf=2313,prop=221d,propto=221d,prsim=227e,prurel=22b0,pscr=1d4c5,psi=3c8,puncsp=2008,",
|
|
1949
|
+
"qfr=1d52e,qint=2a0c,qopf=1d562,qprime=2057,qscr=1d4c6,quaternions=210d,quatint=2a16,quest=3f,questeq",
|
|
1950
|
+
"=225f,quot=22,rAarr=21db,rArr=21d2,rAtail=291c,rBarr=290f,rHar=2964,race=223d.331,racute=155,radic=2",
|
|
1951
|
+
"21a,raemptyv=29b3,rang=27e9,rangd=2992,range=29a5,rangle=27e9,raquo=bb,rarr=2192,rarrap=2975,rarrb=2",
|
|
1952
|
+
"1e5,rarrbfs=2920,rarrc=2933,rarrfs=291e,rarrhk=21aa,rarrlp=21ac,rarrpl=2945,rarrsim=2974,rarrtl=21a3",
|
|
1953
|
+
",rarrw=219d,ratail=291a,ratio=2236,rationals=211a,rbarr=290d,rbbrk=2773,rbrace=7d,rbrack=5d,rbrke=29",
|
|
1954
|
+
"8c,rbrksld=298e,rbrkslu=2990,rcaron=159,rcedil=157,rceil=2309,rcub=7d,rcy=440,rdca=2937,rdldhar=2969",
|
|
1955
|
+
",rdquo=201d,rdquor=201d,rdsh=21b3,real=211c,realine=211b,realpart=211c,reals=211d,rect=25ad,reg=ae,r",
|
|
1956
|
+
"fisht=297d,rfloor=230b,rfr=1d52f,rhard=21c1,rharu=21c0,rharul=296c,rho=3c1,rhov=3f1,rightarrow=2192,",
|
|
1957
|
+
"rightarrowtail=21a3,rightharpoondown=21c1,rightharpoonup=21c0,rightleftarrows=21c4,rightleftharpoons",
|
|
1958
|
+
"=21cc,rightrightarrows=21c9,rightsquigarrow=219d,rightthreetimes=22cc,ring=2da,risingdotseq=2253,rla",
|
|
1959
|
+
"rr=21c4,rlhar=21cc,rlm=200f,rmoust=23b1,rmoustache=23b1,rnmid=2aee,roang=27ed,roarr=21fe,robrk=27e7,",
|
|
1960
|
+
"ropar=2986,ropf=1d563,roplus=2a2e,rotimes=2a35,rpar=29,rpargt=2994,rppolint=2a12,rrarr=21c9,rsaquo=2",
|
|
1961
|
+
"03a,rscr=1d4c7,rsh=21b1,rsqb=5d,rsquo=2019,rsquor=2019,rthree=22cc,rtimes=22ca,rtri=25b9,rtrie=22b5,",
|
|
1962
|
+
"rtrif=25b8,rtriltri=29ce,ruluhar=2968,rx=211e,sacute=15b,sbquo=201a,sc=227b,scE=2ab4,scap=2ab8,scaro",
|
|
1963
|
+
"n=161,sccue=227d,sce=2ab0,scedil=15f,scirc=15d,scnE=2ab6,scnap=2aba,scnsim=22e9,scpolint=2a13,scsim=",
|
|
1964
|
+
"227f,scy=441,sdot=22c5,sdotb=22a1,sdote=2a66,seArr=21d8,searhk=2925,searr=2198,searrow=2198,sect=a7,",
|
|
1965
|
+
"semi=3b,seswar=2929,setminus=2216,setmn=2216,sext=2736,sfr=1d530,sfrown=2322,sharp=266f,shchcy=449,s",
|
|
1966
|
+
"hcy=448,shortmid=2223,shortparallel=2225,shy=ad,sigma=3c3,sigmaf=3c2,sigmav=3c2,sim=223c,simdot=2a6a",
|
|
1967
|
+
",sime=2243,simeq=2243,simg=2a9e,simgE=2aa0,siml=2a9d,simlE=2a9f,simne=2246,simplus=2a24,simrarr=2972",
|
|
1968
|
+
",slarr=2190,smallsetminus=2216,smashp=2a33,smeparsl=29e4,smid=2223,smile=2323,smt=2aaa,smte=2aac,smt",
|
|
1969
|
+
"es=2aac.fe00,softcy=44c,sol=2f,solb=29c4,solbar=233f,sopf=1d564,spades=2660,spadesuit=2660,spar=2225",
|
|
1970
|
+
",sqcap=2293,sqcaps=2293.fe00,sqcup=2294,sqcups=2294.fe00,sqsub=228f,sqsube=2291,sqsubset=228f,sqsubs",
|
|
1971
|
+
"eteq=2291,sqsup=2290,sqsupe=2292,sqsupset=2290,sqsupseteq=2292,squ=25a1,square=25a1,squarf=25aa,squf",
|
|
1972
|
+
"=25aa,srarr=2192,sscr=1d4c8,ssetmn=2216,ssmile=2323,sstarf=22c6,star=2606,starf=2605,straightepsilon",
|
|
1973
|
+
"=3f5,straightphi=3d5,strns=af,sub=2282,subE=2ac5,subdot=2abd,sube=2286,subedot=2ac3,submult=2ac1,sub",
|
|
1974
|
+
"nE=2acb,subne=228a,subplus=2abf,subrarr=2979,subset=2282,subseteq=2286,subseteqq=2ac5,subsetneq=228a",
|
|
1975
|
+
",subsetneqq=2acb,subsim=2ac7,subsub=2ad5,subsup=2ad3,succ=227b,succapprox=2ab8,succcurlyeq=227d,succ",
|
|
1976
|
+
"eq=2ab0,succnapprox=2aba,succneqq=2ab6,succnsim=22e9,succsim=227f,sum=2211,sung=266a,sup=2283,sup1=b",
|
|
1977
|
+
"9,sup2=b2,sup3=b3,supE=2ac6,supdot=2abe,supdsub=2ad8,supe=2287,supedot=2ac4,suphsol=27c9,suphsub=2ad",
|
|
1978
|
+
"7,suplarr=297b,supmult=2ac2,supnE=2acc,supne=228b,supplus=2ac0,supset=2283,supseteq=2287,supseteqq=2",
|
|
1979
|
+
"ac6,supsetneq=228b,supsetneqq=2acc,supsim=2ac8,supsub=2ad4,supsup=2ad6,swArr=21d9,swarhk=2926,swarr=",
|
|
1980
|
+
"2199,swarrow=2199,swnwar=292a,szlig=df,target=2316,tau=3c4,tbrk=23b4,tcaron=165,tcedil=163,tcy=442,t",
|
|
1981
|
+
"dot=20db,telrec=2315,tfr=1d531,there4=2234,therefore=2234,theta=3b8,thetasym=3d1,thetav=3d1,thickapp",
|
|
1982
|
+
"rox=2248,thicksim=223c,thinsp=2009,thkap=2248,thksim=223c,thorn=fe,tilde=2dc,times=d7,timesb=22a0,ti",
|
|
1983
|
+
"mesbar=2a31,timesd=2a30,tint=222d,toea=2928,top=22a4,topbot=2336,topcir=2af1,topf=1d565,topfork=2ada",
|
|
1984
|
+
",tosa=2929,tprime=2034,trade=2122,triangle=25b5,triangledown=25bf,triangleleft=25c3,trianglelefteq=2",
|
|
1985
|
+
"2b4,triangleq=225c,triangleright=25b9,trianglerighteq=22b5,tridot=25ec,trie=225c,triminus=2a3a,tripl",
|
|
1986
|
+
"us=2a39,trisb=29cd,tritime=2a3b,trpezium=23e2,tscr=1d4c9,tscy=446,tshcy=45b,tstrok=167,twixt=226c,tw",
|
|
1987
|
+
"oheadleftarrow=219e,twoheadrightarrow=21a0,uArr=21d1,uHar=2963,uacute=fa,uarr=2191,ubrcy=45e,ubreve=",
|
|
1988
|
+
"16d,ucirc=fb,ucy=443,udarr=21c5,udblac=171,udhar=296e,ufisht=297e,ufr=1d532,ugrave=f9,uharl=21bf,uha",
|
|
1989
|
+
"rr=21be,uhblk=2580,ulcorn=231c,ulcorner=231c,ulcrop=230f,ultri=25f8,umacr=16b,uml=a8,uogon=173,uopf=",
|
|
1990
|
+
"1d566,uparrow=2191,updownarrow=2195,upharpoonleft=21bf,upharpoonright=21be,uplus=228e,upsi=3c5,upsih",
|
|
1991
|
+
"=3d2,upsilon=3c5,upuparrows=21c8,urcorn=231d,urcorner=231d,urcrop=230e,uring=16f,urtri=25f9,uscr=1d4",
|
|
1992
|
+
"ca,utdot=22f0,utilde=169,utri=25b5,utrif=25b4,uuarr=21c8,uuml=fc,uwangle=29a7,vArr=21d5,vBar=2ae8,vB",
|
|
1993
|
+
"arv=2ae9,vDash=22a8,vangrt=299c,varepsilon=3f5,varkappa=3f0,varnothing=2205,varphi=3d5,varpi=3d6,var",
|
|
1994
|
+
"propto=221d,varr=2195,varrho=3f1,varsigma=3c2,varsubsetneq=228a.fe00,varsubsetneqq=2acb.fe00,varsups",
|
|
1995
|
+
"etneq=228b.fe00,varsupsetneqq=2acc.fe00,vartheta=3d1,vartriangleleft=22b2,vartriangleright=22b3,vcy=",
|
|
1996
|
+
"432,vdash=22a2,vee=2228,veebar=22bb,veeeq=225a,vellip=22ee,verbar=7c,vert=7c,vfr=1d533,vltri=22b2,vn",
|
|
1997
|
+
"sub=2282.20d2,vnsup=2283.20d2,vopf=1d567,vprop=221d,vrtri=22b3,vscr=1d4cb,vsubnE=2acb.fe00,vsubne=22",
|
|
1998
|
+
"8a.fe00,vsupnE=2acc.fe00,vsupne=228b.fe00,vzigzag=299a,wcirc=175,wedbar=2a5f,wedge=2227,wedgeq=2259,",
|
|
1999
|
+
"weierp=2118,wfr=1d534,wopf=1d568,wp=2118,wr=2240,wreath=2240,wscr=1d4cc,xcap=22c2,xcirc=25ef,xcup=22",
|
|
2000
|
+
"c3,xdtri=25bd,xfr=1d535,xhArr=27fa,xharr=27f7,xi=3be,xlArr=27f8,xlarr=27f5,xmap=27fc,xnis=22fb,xodot",
|
|
2001
|
+
"=2a00,xopf=1d569,xoplus=2a01,xotime=2a02,xrArr=27f9,xrarr=27f6,xscr=1d4cd,xsqcup=2a06,xuplus=2a04,xu",
|
|
2002
|
+
"tri=25b3,xvee=22c1,xwedge=22c0,yacute=fd,yacy=44f,ycirc=177,ycy=44b,yen=a5,yfr=1d536,yicy=457,yopf=1",
|
|
2003
|
+
"d56a,yscr=1d4ce,yucy=44e,yuml=ff,zacute=17a,zcaron=17e,zcy=437,zdot=17c,zeetrf=2128,zeta=3b6,zfr=1d5",
|
|
2004
|
+
"37,zhcy=436,zigrarr=21dd,zopf=1d56b,zscr=1d4cf,zwj=200d,zwnj=200c"
|
|
2005
|
+
].join("");
|
|
2006
|
+
var NAMED_REFERENCES = new Map(NAMED.split(",").map((entry) => {
|
|
2007
|
+
const [name, codes] = entry.split("=");
|
|
2008
|
+
return [name, String.fromCodePoint(...codes.split(".").map((code) => Number.parseInt(code, 16)))];
|
|
2009
|
+
}));
|
|
2010
|
+
|
|
1733
2011
|
// src/html.js
|
|
1734
2012
|
var LATIN1 = "nbsp iexcl cent pound curren yen brvbar sect uml copy ordf laquo not shy reg macr deg plusmn sup2 sup3 acute micro para middot cedil sup1 ordm raquo frac14 frac12 frac34 iquest Agrave Aacute Acirc Atilde Auml Aring AElig Ccedil Egrave Eacute Ecirc Euml Igrave Iacute Icirc Iuml ETH Ntilde Ograve Oacute Ocirc Otilde Ouml times Oslash Ugrave Uacute Ucirc Uuml Yacute THORN szlig agrave aacute acirc atilde auml aring aelig ccedil egrave eacute ecirc euml igrave iacute icirc iuml eth ntilde ograve oacute ocirc otilde ouml divide oslash ugrave uacute ucirc uuml yacute thorn yuml".split(" ");
|
|
1735
|
-
var ENTITIES = new Map([
|
|
1736
|
-
...LATIN1.map((name, index) => [name, String.fromCodePoint(160 + index)]),
|
|
1737
|
-
["amp", "&"],
|
|
1738
|
-
["lt", "<"],
|
|
1739
|
-
["gt", ">"],
|
|
1740
|
-
["quot", '"'],
|
|
1741
|
-
["apos", "'"],
|
|
1742
|
-
["AMP", "&"],
|
|
1743
|
-
["LT", "<"],
|
|
1744
|
-
["GT", ">"],
|
|
1745
|
-
["QUOT", '"'],
|
|
1746
|
-
["COPY", "\xA9"],
|
|
1747
|
-
["REG", "\xAE"],
|
|
1748
|
-
["OElig", "\u0152"],
|
|
1749
|
-
["oelig", "\u0153"],
|
|
1750
|
-
["Scaron", "\u0160"],
|
|
1751
|
-
["scaron", "\u0161"],
|
|
1752
|
-
["Yuml", "\u0178"],
|
|
1753
|
-
["fnof", "\u0192"],
|
|
1754
|
-
["circ", "\u02C6"],
|
|
1755
|
-
["tilde", "\u02DC"],
|
|
1756
|
-
["ensp", "\u2002"],
|
|
1757
|
-
["emsp", "\u2003"],
|
|
1758
|
-
["thinsp", "\u2009"],
|
|
1759
|
-
["zwnj", "\u200C"],
|
|
1760
|
-
["zwj", "\u200D"],
|
|
1761
|
-
["lrm", "\u200E"],
|
|
1762
|
-
["rlm", "\u200F"],
|
|
1763
|
-
["ndash", "\u2013"],
|
|
1764
|
-
["mdash", "\u2014"],
|
|
1765
|
-
["lsquo", "\u2018"],
|
|
1766
|
-
["rsquo", "\u2019"],
|
|
1767
|
-
["sbquo", "\u201A"],
|
|
1768
|
-
["ldquo", "\u201C"],
|
|
1769
|
-
["rdquo", "\u201D"],
|
|
1770
|
-
["bdquo", "\u201E"],
|
|
1771
|
-
["dagger", "\u2020"],
|
|
1772
|
-
["Dagger", "\u2021"],
|
|
1773
|
-
["bull", "\u2022"],
|
|
1774
|
-
["hellip", "\u2026"],
|
|
1775
|
-
["permil", "\u2030"],
|
|
1776
|
-
["prime", "\u2032"],
|
|
1777
|
-
["Prime", "\u2033"],
|
|
1778
|
-
["lsaquo", "\u2039"],
|
|
1779
|
-
["rsaquo", "\u203A"],
|
|
1780
|
-
["euro", "\u20AC"],
|
|
1781
|
-
["trade", "\u2122"],
|
|
1782
|
-
["larr", "\u2190"],
|
|
1783
|
-
["uarr", "\u2191"],
|
|
1784
|
-
["rarr", "\u2192"],
|
|
1785
|
-
["darr", "\u2193"],
|
|
1786
|
-
["hearts", "\u2665"],
|
|
1787
|
-
["check", "\u2713"],
|
|
1788
|
-
// The punctuation of a web address, as browsers read these names
|
|
1789
|
-
// ("https://bank.example@evil.example/" goes to evil.example)
|
|
1790
|
-
["Tab", " "],
|
|
1791
|
-
["NewLine", "\n"],
|
|
1792
|
-
["excl", "!"],
|
|
1793
|
-
["num", "#"],
|
|
1794
|
-
["dollar", "$"],
|
|
1795
|
-
["percnt", "%"],
|
|
1796
|
-
["lpar", "("],
|
|
1797
|
-
["rpar", ")"],
|
|
1798
|
-
["ast", "*"],
|
|
1799
|
-
["midast", "*"],
|
|
1800
|
-
["plus", "+"],
|
|
1801
|
-
["comma", ","],
|
|
1802
|
-
["period", "."],
|
|
1803
|
-
["sol", "/"],
|
|
1804
|
-
["colon", ":"],
|
|
1805
|
-
["semi", ";"],
|
|
1806
|
-
["equals", "="],
|
|
1807
|
-
["quest", "?"],
|
|
1808
|
-
["commat", "@"],
|
|
1809
|
-
["lsqb", "["],
|
|
1810
|
-
["lbrack", "["],
|
|
1811
|
-
["bsol", "\\"],
|
|
1812
|
-
["rsqb", "]"],
|
|
1813
|
-
["rbrack", "]"],
|
|
1814
|
-
["Hat", "^"],
|
|
1815
|
-
["lowbar", "_"],
|
|
1816
|
-
["UnderBar", "_"],
|
|
1817
|
-
["grave", "`"],
|
|
1818
|
-
["DiacriticalGrave", "`"],
|
|
1819
|
-
["lcub", "{"],
|
|
1820
|
-
["lbrace", "{"],
|
|
1821
|
-
["verbar", "|"],
|
|
1822
|
-
["vert", "|"],
|
|
1823
|
-
["VerticalLine", "|"],
|
|
1824
|
-
["rcub", "}"],
|
|
1825
|
-
["rbrace", "}"]
|
|
1826
|
-
]);
|
|
1827
2013
|
var LEGACY = /* @__PURE__ */ new Set([...LATIN1, "amp", "lt", "gt", "quot", "AMP", "LT", "GT", "QUOT", "COPY", "REG"]);
|
|
1828
2014
|
var LEGACY_LONGEST = Math.max(...[...LEGACY].map((name) => name.length));
|
|
1829
|
-
var LONGER_NAMES = new Set("centerdot copysr divideontimes gtcc gtcir gtdot gtlPar gtquest gtrapprox gtrarr gtrdot gtreqless gtreqqless gtrless gtrsim ltcc ltcir ltdot lthree ltimes ltlarr ltquest ltrPar ltri ltrie ltrif notin notinE notindot notinva notinvb notinvc notni notniva notnivb notnivc parallel timesb timesbar timesd".split(" "));
|
|
1830
2015
|
var WINDOWS_1252 = [8364, 129, 8218, 402, 8222, 8230, 8224, 8225, 710, 8240, 352, 8249, 338, 141, 381, 143, 144, 8216, 8217, 8220, 8221, 8226, 8211, 8212, 732, 8482, 353, 8250, 339, 157, 382, 376];
|
|
1831
2016
|
var REFERENCE = /&(?:#([xX])([\da-fA-F]+)|#(\d+)|([a-zA-Z][a-zA-Z\d]{0,31}))(;?)/g;
|
|
1832
2017
|
function fromCode(code) {
|
|
@@ -1844,8 +2029,8 @@ function decodeEntities(value, { attribute = false } = {}) {
|
|
|
1844
2029
|
if (hex !== void 0 || decimal !== void 0) {
|
|
1845
2030
|
return fromCode(hex === void 0 ? Number.parseInt(decimal, 10) : Number.parseInt(hex, 16));
|
|
1846
2031
|
}
|
|
1847
|
-
if (semicolon &&
|
|
1848
|
-
return
|
|
2032
|
+
if (semicolon && NAMED_REFERENCES.has(name)) {
|
|
2033
|
+
return NAMED_REFERENCES.get(name);
|
|
1849
2034
|
}
|
|
1850
2035
|
let legacy;
|
|
1851
2036
|
for (let length = Math.min(name.length, LEGACY_LONGEST); length >= 2 && legacy === void 0; length--) {
|
|
@@ -1859,7 +2044,7 @@ function decodeEntities(value, { attribute = false } = {}) {
|
|
|
1859
2044
|
if (attribute && /[\da-zA-Z=]/.test(next)) {
|
|
1860
2045
|
return match;
|
|
1861
2046
|
}
|
|
1862
|
-
return
|
|
2047
|
+
return NAMED_REFERENCES.get(legacy) + rest;
|
|
1863
2048
|
});
|
|
1864
2049
|
}
|
|
1865
2050
|
var UNSHOWN = /* @__PURE__ */ new Set(["script", "style", "title", "noembed", "noframes", "iframe"]);
|
|
@@ -2706,6 +2891,34 @@ function continuesName(text, index) {
|
|
|
2706
2891
|
var FILE_TLDS = /* @__PURE__ */ new Set(["ai", "app", "md", "mov", "pm", "ps", "py", "rs", "sh", "so", "zip"]);
|
|
2707
2892
|
var LINK_FILE_TLDS = /* @__PURE__ */ new Set(["md", "mov", "pm", "ps", "py", "rs", "sh", "so", "zip"]);
|
|
2708
2893
|
var CLICK_TRACKERS = ["sendgrid.net", "list-manage.com", "mandrillapp.com", "awstrack.me", "pstmrk.it", "hubspotlinks.com", "rs6.net", "klclick.com", "mjt.lu", "exacttarget.com", "convertkit-mail.com", "convertkit-mail2.com", "kit-mail3.com", "sendibt2.com", "sendibt3.com", "createsend1.com", "cmail20.com", "clicks.mlsend.com", "acemlnb.com", "link.mail.beehiiv.com", "bhclick.com", "safelinks.protection.outlook.com", "urldefense.com", "urldefense.proofpoint.com", "mimecast.com", "mimecastprotect.com", "cudasvc.com", "secure-web.cisco.com"];
|
|
2894
|
+
var MAX_URL_HOST = 4096;
|
|
2895
|
+
var AUTHORITY_END = /[/\\?#]/g;
|
|
2896
|
+
var IGNORED_IN_HOST = /[\u00AD\u034F\u180B-\u180D\u180F\u200B\u2060\u2064\uFE00-\uFE0F\uFEFF]|\uD82F[\uDCA0-\uDCA3]|\uDB40[\uDD00-\uDDEF]/g;
|
|
2897
|
+
function boundedUrl(address2) {
|
|
2898
|
+
const text = String(address2).replaceAll(/[\t\n\r]/g, "");
|
|
2899
|
+
const colon = text.indexOf(":");
|
|
2900
|
+
let start = colon === -1 ? 0 : colon + 1;
|
|
2901
|
+
while (start < text.length && (text[start] === "/" || text[start] === "\\")) {
|
|
2902
|
+
start++;
|
|
2903
|
+
}
|
|
2904
|
+
AUTHORITY_END.lastIndex = start;
|
|
2905
|
+
const end = AUTHORITY_END.exec(text)?.index ?? text.length;
|
|
2906
|
+
const file = /^[\0- ]*file:/i.test(text);
|
|
2907
|
+
const at = file ? start : Math.max(start, text.lastIndexOf("@", end - 1) + 1);
|
|
2908
|
+
const bracket = text.lastIndexOf("]", end - 1);
|
|
2909
|
+
const port = text.lastIndexOf(":", end - 1);
|
|
2910
|
+
let host = text.slice(at, !file && port > Math.max(at - 1, bracket) ? port : end);
|
|
2911
|
+
if (host.length > MAX_URL_HOST && host.includes("%")) {
|
|
2912
|
+
try {
|
|
2913
|
+
host = decodeURIComponent(host);
|
|
2914
|
+
} catch {
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2917
|
+
if (host.length > MAX_URL_HOST && host.replaceAll(IGNORED_IN_HOST, "").length > MAX_URL_HOST) {
|
|
2918
|
+
throw new TypeError("Invalid URL: host too long");
|
|
2919
|
+
}
|
|
2920
|
+
return new URL(text);
|
|
2921
|
+
}
|
|
2709
2922
|
var isUnder = (host, domain) => host === domain || host.endsWith(`.${domain}`);
|
|
2710
2923
|
function isClickTracker(url) {
|
|
2711
2924
|
const host = url.hostname.toLowerCase();
|
|
@@ -2734,7 +2947,7 @@ var MAX_GATEWAYS = 16;
|
|
|
2734
2947
|
function unwrapLink(address2) {
|
|
2735
2948
|
let url;
|
|
2736
2949
|
try {
|
|
2737
|
-
url =
|
|
2950
|
+
url = boundedUrl(address2);
|
|
2738
2951
|
} catch {
|
|
2739
2952
|
return null;
|
|
2740
2953
|
}
|
|
@@ -2745,7 +2958,7 @@ function unwrapLink(address2) {
|
|
|
2745
2958
|
}
|
|
2746
2959
|
let next;
|
|
2747
2960
|
try {
|
|
2748
|
-
next =
|
|
2961
|
+
next = boundedUrl(target);
|
|
2749
2962
|
} catch {
|
|
2750
2963
|
break;
|
|
2751
2964
|
}
|
|
@@ -3005,7 +3218,7 @@ function endsInsideTag(html, end, link) {
|
|
|
3005
3218
|
return joined !== host && (!isHost(host) || isHost(joined) && registrableDomain(joined) !== registrableDomain(host));
|
|
3006
3219
|
}
|
|
3007
3220
|
var MAX_SHOWN_HTML = 2e5;
|
|
3008
|
-
function extractLinks(text, html, { maxLength = 5e5, bareHosts = false } = {}) {
|
|
3221
|
+
function extractLinks(text, html, { maxLength = 5e5, bareHosts: bareHosts2 = false } = {}) {
|
|
3009
3222
|
text = typeof text === "string" ? foldToAscii(text) : text;
|
|
3010
3223
|
html = typeof html === "string" ? foldToAscii(html) : html;
|
|
3011
3224
|
const links = [];
|
|
@@ -3023,7 +3236,7 @@ function extractLinks(text, html, { maxLength = 5e5, bareHosts = false } = {}) {
|
|
|
3023
3236
|
}
|
|
3024
3237
|
let url;
|
|
3025
3238
|
try {
|
|
3026
|
-
url =
|
|
3239
|
+
url = boundedUrl(href);
|
|
3027
3240
|
} catch {
|
|
3028
3241
|
return;
|
|
3029
3242
|
}
|
|
@@ -3072,7 +3285,7 @@ ${shown || ""}`;
|
|
|
3072
3285
|
}
|
|
3073
3286
|
add(link);
|
|
3074
3287
|
}
|
|
3075
|
-
if (
|
|
3288
|
+
if (bareHosts2 && index < 2) {
|
|
3076
3289
|
bareLinkHosts(read, (host) => {
|
|
3077
3290
|
bareFound.push(host);
|
|
3078
3291
|
});
|
|
@@ -3348,7 +3561,7 @@ function isDeceptiveLink(link, { from } = {}) {
|
|
|
3348
3561
|
}
|
|
3349
3562
|
let shownUrl;
|
|
3350
3563
|
try {
|
|
3351
|
-
shownUrl =
|
|
3564
|
+
shownUrl = boundedUrl(/^https?:\/\//i.test(link.text) ? link.text : `http://${link.text}`);
|
|
3352
3565
|
} catch {
|
|
3353
3566
|
return false;
|
|
3354
3567
|
}
|
|
@@ -5160,7 +5373,8 @@ async function parseWithin(raw, parse = parseMessage, deadline = Date.now() + PA
|
|
|
5160
5373
|
}
|
|
5161
5374
|
}
|
|
5162
5375
|
}
|
|
5163
|
-
async function expandAttachments(attachments = [], errors = [], parse = parseMessage,
|
|
5376
|
+
async function expandAttachments(attachments = [], errors = [], parse = parseMessage, gets = {}) {
|
|
5377
|
+
const { unopened = [], mails = [] } = Array.isArray(gets) ? { unopened: gets } : gets ?? {};
|
|
5164
5378
|
const all = [];
|
|
5165
5379
|
const deadline = Date.now() + PARSE_TIMEOUT;
|
|
5166
5380
|
let budget = MAX_NESTED_BYTES;
|
|
@@ -5209,6 +5423,7 @@ async function expandAttachments(attachments = [], errors = [], parse = parseMes
|
|
|
5209
5423
|
if (inner.error) {
|
|
5210
5424
|
errors.push(inner.error);
|
|
5211
5425
|
}
|
|
5426
|
+
mails.push(inner.mail);
|
|
5212
5427
|
await visit(inner.mail.attachments, prefix ? `${prefix}/${name}` : name || "attached message", depth + 1);
|
|
5213
5428
|
}
|
|
5214
5429
|
}
|
|
@@ -5227,6 +5442,11 @@ import { isIP } from "node:net";
|
|
|
5227
5442
|
import { domainToASCII } from "node:url";
|
|
5228
5443
|
import { getPublicSuffix } from "tldts";
|
|
5229
5444
|
var LOOKUP_LABEL = /^[a-z\d_-]{1,63}$/;
|
|
5445
|
+
var LABEL_SEPARATOR = /[.\u3002\uFF0E\uFF61]/g;
|
|
5446
|
+
function lastLabels(name) {
|
|
5447
|
+
const separators = [...name.matchAll(LABEL_SEPARATOR)];
|
|
5448
|
+
return separators.length < 128 ? 0 : separators.at(-128).index + 1;
|
|
5449
|
+
}
|
|
5230
5450
|
function lookupName(host) {
|
|
5231
5451
|
if (typeof host !== "string") {
|
|
5232
5452
|
return null;
|
|
@@ -5237,6 +5457,7 @@ function lookupName(host) {
|
|
|
5237
5457
|
}
|
|
5238
5458
|
let name = host.slice(0, end).toLowerCase();
|
|
5239
5459
|
if (/[\u0080-\u{10FFFF}]/u.test(name)) {
|
|
5460
|
+
name = name.slice(lastLabels(name));
|
|
5240
5461
|
name = domainToASCII(name) || name;
|
|
5241
5462
|
}
|
|
5242
5463
|
if (isIP(name.replaceAll(/^\[|]$/g, "")) !== 0) {
|
|
@@ -5507,7 +5728,7 @@ function asciiHostname(value) {
|
|
|
5507
5728
|
return null;
|
|
5508
5729
|
}
|
|
5509
5730
|
const lower = value.normalize("NFKC").toLowerCase().replace(/\.$/, "");
|
|
5510
|
-
if (lower === "" || /[\0-,/:-^`{-\u007F]/.test(lower)) {
|
|
5731
|
+
if (lower === "" || lower.length > 1024 || /[\0-,/:-^`{-\u007F]/.test(lower)) {
|
|
5511
5732
|
return null;
|
|
5512
5733
|
}
|
|
5513
5734
|
const ascii = domainToASCII2(lower);
|
|
@@ -5689,8 +5910,8 @@ function trustReputation(result, trusted = []) {
|
|
|
5689
5910
|
for (const value of result.checkedValues.filter((item) => allowed.has(item))) {
|
|
5690
5911
|
const signers = allowed.get(value);
|
|
5691
5912
|
const counts = (entry) => Boolean(entry) && (signers === null || signers.some((signer) => vouches(normalizeEntry(entry), signer)));
|
|
5692
|
-
const detail = result.details[value];
|
|
5693
|
-
const listed = result.allowlisted[value] || (detail?.isAllowlisted ? detail.allowlistValue : null);
|
|
5913
|
+
const detail = Object.hasOwn(result.details, value) ? result.details[value] : void 0;
|
|
5914
|
+
const listed = Object.hasOwn(result.allowlisted, value) && result.allowlisted[value] || (detail?.isAllowlisted ? detail.allowlistValue : null);
|
|
5694
5915
|
if (counts(listed) && !out.isAllowlisted) {
|
|
5695
5916
|
out.isAllowlisted = true;
|
|
5696
5917
|
out.allowlistValue = listed;
|
|
@@ -6089,7 +6310,7 @@ import net from "node:net";
|
|
|
6089
6310
|
var package_default = {
|
|
6090
6311
|
name: "spamscanner",
|
|
6091
6312
|
description: "Spam, phishing and malware filter for email in any language: a trainable classifier, link and attachment checks, SPF/DKIM/DMARC, and an optional AI second opinion from any LLM, including local Ollama models. Works as a library, CLI, Postfix milter or HTTP API.",
|
|
6092
|
-
version: "7.0.
|
|
6313
|
+
version: "7.0.12",
|
|
6093
6314
|
author: "Forward Email (https://forwardemail.net)",
|
|
6094
6315
|
bin: {
|
|
6095
6316
|
spamscanner: "./dist/esm/cli.js"
|
|
@@ -6239,7 +6460,12 @@ var VERSION = package_default.version;
|
|
|
6239
6460
|
var MAX_MESSAGE_SIZE = 25 * 1024 * 1024;
|
|
6240
6461
|
var MAX_CONNECTIONS = 200;
|
|
6241
6462
|
function serializeResult(result, options = {}) {
|
|
6242
|
-
const {
|
|
6463
|
+
const {
|
|
6464
|
+
mail,
|
|
6465
|
+
tokens,
|
|
6466
|
+
attached,
|
|
6467
|
+
...rest
|
|
6468
|
+
} = result;
|
|
6243
6469
|
const output = {
|
|
6244
6470
|
...rest,
|
|
6245
6471
|
results: { ...result.results }
|
|
@@ -7140,6 +7366,7 @@ function reply(code, text, headers = [], body = null) {
|
|
|
7140
7366
|
lines2.push(...headers.map(([name, value]) => `${name}: ${value}`), "", "");
|
|
7141
7367
|
return body === null ? Buffer9.from(lines2.join("\r\n")) : Buffer9.concat([Buffer9.from(lines2.join("\r\n")), body]);
|
|
7142
7368
|
}
|
|
7369
|
+
var oneLine2 = (text) => String(text ?? "").replaceAll(/[\p{Cc}\u061C\u200E\u200F\u2028\u2029\u202A-\u202E\u2066-\u2069]+/gu, " ");
|
|
7143
7370
|
function report(result) {
|
|
7144
7371
|
const lines2 = [
|
|
7145
7372
|
`Spam detection software, running on the system "spamscanner", has ${result.isSpam ? "identified this incoming email as possible spam" : "scanned this incoming email"}.`,
|
|
@@ -7148,7 +7375,7 @@ function report(result) {
|
|
|
7148
7375
|
"",
|
|
7149
7376
|
" pts rule name description",
|
|
7150
7377
|
"---- ---------------------- --------------------------------------------------",
|
|
7151
|
-
...result.tests.map((test) => `${test.score.toFixed(1).padStart(4)} ${test.name.padEnd(22)} ${test.description}`)
|
|
7378
|
+
...result.tests.map((test) => `${test.score.toFixed(1).padStart(4)} ${oneLine2(test.name).padEnd(22)} ${oneLine2(test.description)}`)
|
|
7152
7379
|
];
|
|
7153
7380
|
return `${lines2.join("\r\n")}\r
|
|
7154
7381
|
`;
|
|
@@ -7629,7 +7856,7 @@ async function evaluate(classifier, examples) {
|
|
|
7629
7856
|
}
|
|
7630
7857
|
|
|
7631
7858
|
// src/index.js
|
|
7632
|
-
import { Buffer as
|
|
7859
|
+
import { Buffer as Buffer14 } from "node:buffer";
|
|
7633
7860
|
import { readFile } from "node:fs/promises";
|
|
7634
7861
|
import { debuglog as debuglog2 } from "node:util";
|
|
7635
7862
|
|
|
@@ -8130,8 +8357,990 @@ async function scanBuffer(buffer, options = {}) {
|
|
|
8130
8357
|
throw new Error(`clamd: ${reply2 || "no reply"}`);
|
|
8131
8358
|
}
|
|
8132
8359
|
|
|
8133
|
-
// src/
|
|
8360
|
+
// src/links.js
|
|
8361
|
+
import { Buffer as Buffer13 } from "node:buffer";
|
|
8362
|
+
import { isIP as isIP3 } from "node:net";
|
|
8134
8363
|
import { parse as parseDomain3 } from "tldts";
|
|
8364
|
+
var LINK_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
8365
|
+
"href",
|
|
8366
|
+
"src",
|
|
8367
|
+
"srcset",
|
|
8368
|
+
"background",
|
|
8369
|
+
"action",
|
|
8370
|
+
"formaction",
|
|
8371
|
+
"poster",
|
|
8372
|
+
"cite",
|
|
8373
|
+
"longdesc",
|
|
8374
|
+
"xlink:href",
|
|
8375
|
+
"style",
|
|
8376
|
+
"data",
|
|
8377
|
+
"ping",
|
|
8378
|
+
// (of a <meta> refresh: "0;url=https://example.com/")
|
|
8379
|
+
"content",
|
|
8380
|
+
// (a document of its own, see linksOf)
|
|
8381
|
+
"srcdoc"
|
|
8382
|
+
]);
|
|
8383
|
+
var TAG_START = /<[a-z][^\s/>]*/gi;
|
|
8384
|
+
var URL_START = /\b(?:https?:\/\/|www\.)[^\s<>"'`]+/gi;
|
|
8385
|
+
var CSS_URL = /url\(\s*["']?([^()"']*)/gi;
|
|
8386
|
+
var CSS_IMPORT = /@import\s+["']([^"'<>]*)/gi;
|
|
8387
|
+
var LENIENT = /[\s"'/<](href|src|srcset|background|action|formaction|poster|cite|longdesc|xlink:href|style|data|ping|content|srcdoc)\s*=\s*/gi;
|
|
8388
|
+
var IN_WORD2 = /[\p{L}\p{N}\p{M}_%]/u;
|
|
8389
|
+
var NAMESPACES = /* @__PURE__ */ new Set(["www.w3.org", "w3.org"]);
|
|
8390
|
+
var MAX_HOST = 760;
|
|
8391
|
+
var MAX_SEEN = 1e5;
|
|
8392
|
+
var TRAILING = /* @__PURE__ */ new Set([")", ".", ",", ";", ":", "!", "?", "'", '"', "]"]);
|
|
8393
|
+
var DECODE_CHUNK = 1024 * 1024;
|
|
8394
|
+
var CHUNK_OVERLAP = 4096;
|
|
8395
|
+
var MAX_EMBED_DEPTH = 3;
|
|
8396
|
+
var MAX_EMBEDDED = 1e3;
|
|
8397
|
+
var MAX_EMBEDDED_LENGTH = 10 * 1024 * 1024;
|
|
8398
|
+
var MAX_ATTACHED = 100;
|
|
8399
|
+
var MAX_ATTACHED_BYTES = 50 * 1024 * 1024;
|
|
8400
|
+
var MAX_READ_TIME = 5e3;
|
|
8401
|
+
var MAX_CARD_BYTES = 4 * 1024 * 1024;
|
|
8402
|
+
var MAX_SHOWN_BYTES = 1024 * 1024;
|
|
8403
|
+
function isHostCode2(code) {
|
|
8404
|
+
return code >= 97 && code <= 122 || code >= 65 && code <= 90 || code >= 48 && code <= 57 || code === 45 || code === 46;
|
|
8405
|
+
}
|
|
8406
|
+
function isSpace(code) {
|
|
8407
|
+
return code === 32 || code >= 9 && code <= 13;
|
|
8408
|
+
}
|
|
8409
|
+
function urlOf(value) {
|
|
8410
|
+
let href = value.replaceAll(/[\t\n\r]/g, "");
|
|
8411
|
+
let start = 0;
|
|
8412
|
+
let end = href.length;
|
|
8413
|
+
while (start < end && href.codePointAt(start) <= 32) {
|
|
8414
|
+
start++;
|
|
8415
|
+
}
|
|
8416
|
+
while (end > start && href.codePointAt(end - 1) <= 32) {
|
|
8417
|
+
end--;
|
|
8418
|
+
}
|
|
8419
|
+
while (end > start && TRAILING.has(href[end - 1])) {
|
|
8420
|
+
end--;
|
|
8421
|
+
}
|
|
8422
|
+
href = href.slice(start, end);
|
|
8423
|
+
if (/^[\\/]{2}/.test(href)) {
|
|
8424
|
+
href = `https://${href.slice(2)}`;
|
|
8425
|
+
} else if (/^www\./i.test(href)) {
|
|
8426
|
+
href = `http://${href}`;
|
|
8427
|
+
} else if (!/^https?:/i.test(href)) {
|
|
8428
|
+
return null;
|
|
8429
|
+
}
|
|
8430
|
+
try {
|
|
8431
|
+
return boundedUrl(href);
|
|
8432
|
+
} catch {
|
|
8433
|
+
return null;
|
|
8434
|
+
}
|
|
8435
|
+
}
|
|
8436
|
+
function hostOfUrl(url) {
|
|
8437
|
+
let host = url.hostname.toLowerCase();
|
|
8438
|
+
let dots = host.length;
|
|
8439
|
+
while (dots > 0 && host[dots - 1] === ".") {
|
|
8440
|
+
dots--;
|
|
8441
|
+
}
|
|
8442
|
+
host = host.slice(0, dots);
|
|
8443
|
+
if (host.startsWith("[") && host.endsWith("]")) {
|
|
8444
|
+
host = host.slice(1, -1);
|
|
8445
|
+
}
|
|
8446
|
+
if (!host || NAMESPACES.has(host)) {
|
|
8447
|
+
return null;
|
|
8448
|
+
}
|
|
8449
|
+
return isIP3(host) ? host : lookupName(host);
|
|
8450
|
+
}
|
|
8451
|
+
function hostsOf(value) {
|
|
8452
|
+
const url = urlOf(value);
|
|
8453
|
+
if (!url) {
|
|
8454
|
+
return [];
|
|
8455
|
+
}
|
|
8456
|
+
const host = hostOfUrl(url);
|
|
8457
|
+
const hosts = host ? [host] : [];
|
|
8458
|
+
const target = unwrapLink(url.href);
|
|
8459
|
+
if (target && target.href !== url.href) {
|
|
8460
|
+
const targetHost = hostOfUrl(target);
|
|
8461
|
+
if (targetHost && targetHost !== host) {
|
|
8462
|
+
hosts.push(targetHost);
|
|
8463
|
+
}
|
|
8464
|
+
const further = unwrapLink(target.href);
|
|
8465
|
+
if (further && further.href !== target.href) {
|
|
8466
|
+
hosts.unresolved = true;
|
|
8467
|
+
}
|
|
8468
|
+
}
|
|
8469
|
+
return hosts;
|
|
8470
|
+
}
|
|
8471
|
+
function* attributesOf(html) {
|
|
8472
|
+
TAG_START.lastIndex = 0;
|
|
8473
|
+
let match;
|
|
8474
|
+
while ((match = TAG_START.exec(html)) !== null) {
|
|
8475
|
+
let i = match.index + match[0].length;
|
|
8476
|
+
while (i < html.length && html[i] !== ">") {
|
|
8477
|
+
if (isSpace(html.codePointAt(i)) || html[i] === "/") {
|
|
8478
|
+
i++;
|
|
8479
|
+
continue;
|
|
8480
|
+
}
|
|
8481
|
+
let j = i;
|
|
8482
|
+
while (j < html.length && html[j] !== "=" && html[j] !== ">" && html[j] !== "/" && !isSpace(html.codePointAt(j))) {
|
|
8483
|
+
j++;
|
|
8484
|
+
}
|
|
8485
|
+
const name = html.slice(i, j).toLowerCase();
|
|
8486
|
+
if (j === i) {
|
|
8487
|
+
j++;
|
|
8488
|
+
}
|
|
8489
|
+
while (j < html.length && isSpace(html.codePointAt(j))) {
|
|
8490
|
+
j++;
|
|
8491
|
+
}
|
|
8492
|
+
if (html[j] !== "=") {
|
|
8493
|
+
i = j;
|
|
8494
|
+
continue;
|
|
8495
|
+
}
|
|
8496
|
+
j++;
|
|
8497
|
+
while (j < html.length && isSpace(html.codePointAt(j))) {
|
|
8498
|
+
j++;
|
|
8499
|
+
}
|
|
8500
|
+
let value;
|
|
8501
|
+
if (html[j] === '"' || html[j] === "'") {
|
|
8502
|
+
const close = html.indexOf(html[j], j + 1);
|
|
8503
|
+
const stop = close === -1 ? html.length : close;
|
|
8504
|
+
value = html.slice(j + 1, stop);
|
|
8505
|
+
j = stop + 1;
|
|
8506
|
+
} else {
|
|
8507
|
+
const start = j;
|
|
8508
|
+
while (j < html.length && html[j] !== ">" && !isSpace(html.codePointAt(j))) {
|
|
8509
|
+
j++;
|
|
8510
|
+
}
|
|
8511
|
+
value = html.slice(start, j);
|
|
8512
|
+
}
|
|
8513
|
+
if (name) {
|
|
8514
|
+
yield [name, value];
|
|
8515
|
+
}
|
|
8516
|
+
i = j;
|
|
8517
|
+
}
|
|
8518
|
+
TAG_START.lastIndex = Math.max(i, match.index + 1);
|
|
8519
|
+
}
|
|
8520
|
+
}
|
|
8521
|
+
var MAX_VALUE2 = 1024 * 1024;
|
|
8522
|
+
var LONG_VALUE = 64 * 1024;
|
|
8523
|
+
function linksOf(name, value, { add, embed, cut }) {
|
|
8524
|
+
if (value.length > MAX_VALUE2) {
|
|
8525
|
+
cut();
|
|
8526
|
+
value = value.slice(0, MAX_VALUE2);
|
|
8527
|
+
}
|
|
8528
|
+
const decoded = decodeEntities(value, { attribute: true });
|
|
8529
|
+
switch (name) {
|
|
8530
|
+
case "srcdoc": {
|
|
8531
|
+
embed(decoded);
|
|
8532
|
+
break;
|
|
8533
|
+
}
|
|
8534
|
+
case "style": {
|
|
8535
|
+
for (const [, link] of decoded.matchAll(CSS_URL)) {
|
|
8536
|
+
add(link);
|
|
8537
|
+
}
|
|
8538
|
+
break;
|
|
8539
|
+
}
|
|
8540
|
+
case "srcset": {
|
|
8541
|
+
for (const candidate of decoded.split(",")) {
|
|
8542
|
+
add(candidate.trim().split(/\s/, 1)[0]);
|
|
8543
|
+
}
|
|
8544
|
+
break;
|
|
8545
|
+
}
|
|
8546
|
+
// (several, separated by spaces)
|
|
8547
|
+
case "ping": {
|
|
8548
|
+
for (const link of decoded.split(/\s+/)) {
|
|
8549
|
+
add(link);
|
|
8550
|
+
}
|
|
8551
|
+
break;
|
|
8552
|
+
}
|
|
8553
|
+
// (only the address of a refresh)
|
|
8554
|
+
case "content": {
|
|
8555
|
+
const match = /(?:^|[\s;,])url\s*=\s*["']?([^\s"';]+)/i.exec(decoded.slice(0, 4096));
|
|
8556
|
+
if (match) {
|
|
8557
|
+
add(match[1]);
|
|
8558
|
+
}
|
|
8559
|
+
break;
|
|
8560
|
+
}
|
|
8561
|
+
default: {
|
|
8562
|
+
add(decoded);
|
|
8563
|
+
}
|
|
8564
|
+
}
|
|
8565
|
+
}
|
|
8566
|
+
var MAX_LENIENT_VALUE = 4096;
|
|
8567
|
+
var UNQUOTED_END = /* @__PURE__ */ new Set(["<", ">", '"', "'", "`", "="]);
|
|
8568
|
+
function lenientLinks(html, done, reader) {
|
|
8569
|
+
LENIENT.lastIndex = 0;
|
|
8570
|
+
let match;
|
|
8571
|
+
while (!done() && (match = LENIENT.exec(html)) !== null) {
|
|
8572
|
+
const name = match[1].toLowerCase();
|
|
8573
|
+
let i = LENIENT.lastIndex;
|
|
8574
|
+
let end = i;
|
|
8575
|
+
if (html[i] === '"' || html[i] === "'") {
|
|
8576
|
+
const quote = html[i];
|
|
8577
|
+
i++;
|
|
8578
|
+
end = i;
|
|
8579
|
+
while (end < html.length && html[end] !== quote && html[end] !== "<" && html[end] !== ">") {
|
|
8580
|
+
end++;
|
|
8581
|
+
}
|
|
8582
|
+
if (end < html.length && html[end] !== quote) {
|
|
8583
|
+
const close = html.indexOf(quote, end);
|
|
8584
|
+
if (close !== -1 && close - i <= MAX_LENIENT_VALUE && !reader.spend(close - i)) {
|
|
8585
|
+
linksOf(name, html.slice(i, close), reader);
|
|
8586
|
+
}
|
|
8587
|
+
}
|
|
8588
|
+
} else {
|
|
8589
|
+
while (end < html.length && !isSpace(html.codePointAt(end)) && !UNQUOTED_END.has(html[end])) {
|
|
8590
|
+
end++;
|
|
8591
|
+
}
|
|
8592
|
+
}
|
|
8593
|
+
if (reader.spend(end - i)) {
|
|
8594
|
+
break;
|
|
8595
|
+
}
|
|
8596
|
+
linksOf(name, html.slice(i, end), reader);
|
|
8597
|
+
}
|
|
8598
|
+
}
|
|
8599
|
+
function endsLink2(text, i) {
|
|
8600
|
+
const code = text.codePointAt(i);
|
|
8601
|
+
if (code < 128) {
|
|
8602
|
+
return isSpace(code) || code === 34 || code === 39 || code === 60 || code === 62 || code === 96;
|
|
8603
|
+
}
|
|
8604
|
+
return /\s/u.test(text[i]);
|
|
8605
|
+
}
|
|
8606
|
+
function linkReader(text) {
|
|
8607
|
+
let read = 0;
|
|
8608
|
+
let space = -1;
|
|
8609
|
+
let scheme = -1;
|
|
8610
|
+
return (start) => {
|
|
8611
|
+
for (; read < start; read++) {
|
|
8612
|
+
if (endsLink2(text, read)) {
|
|
8613
|
+
space = read;
|
|
8614
|
+
} else if (text[read] === "/" && text[read - 1] === "/" && text[read - 2] === ":") {
|
|
8615
|
+
scheme = read;
|
|
8616
|
+
}
|
|
8617
|
+
}
|
|
8618
|
+
return scheme > space;
|
|
8619
|
+
};
|
|
8620
|
+
}
|
|
8621
|
+
function isSchemeless(text, start) {
|
|
8622
|
+
if (start < 2 || text[start - 2] !== "/") {
|
|
8623
|
+
return false;
|
|
8624
|
+
}
|
|
8625
|
+
if (start === 2) {
|
|
8626
|
+
return true;
|
|
8627
|
+
}
|
|
8628
|
+
const before = text[start - 3];
|
|
8629
|
+
return !(before === ":" || before === "/" || isHostCode2(text.codePointAt(start - 3)));
|
|
8630
|
+
}
|
|
8631
|
+
function bareName(name) {
|
|
8632
|
+
const labels = name.split(".");
|
|
8633
|
+
const tld = labels.pop();
|
|
8634
|
+
if (!/^(?:[a-z]{2,24}|xn--[a-z\d-]{1,59})$/i.test(tld)) {
|
|
8635
|
+
return null;
|
|
8636
|
+
}
|
|
8637
|
+
const names = [];
|
|
8638
|
+
while (labels.length > 0 && names.length < 11) {
|
|
8639
|
+
const label = labels.pop();
|
|
8640
|
+
if (!/^[a-z\d-]{1,63}$/i.test(label)) {
|
|
8641
|
+
break;
|
|
8642
|
+
}
|
|
8643
|
+
names.unshift(label);
|
|
8644
|
+
}
|
|
8645
|
+
if (names.length === 0) {
|
|
8646
|
+
return null;
|
|
8647
|
+
}
|
|
8648
|
+
names[0] = names[0].replace(/^-+/, "");
|
|
8649
|
+
if (!names[0]) {
|
|
8650
|
+
return null;
|
|
8651
|
+
}
|
|
8652
|
+
return `${names.join(".")}.${tld}`.toLowerCase();
|
|
8653
|
+
}
|
|
8654
|
+
function isRealName(host) {
|
|
8655
|
+
const parsed = parseDomain3(host);
|
|
8656
|
+
return Boolean(parsed.isIcann && parsed.domain);
|
|
8657
|
+
}
|
|
8658
|
+
function bareHosts(text, add, { done, seen, from = 0, cutEnd = false }) {
|
|
8659
|
+
let previous = -1;
|
|
8660
|
+
const inLink = linkReader(text);
|
|
8661
|
+
for (let slash = text.indexOf("/"); slash !== -1; slash = text.indexOf("/", slash + 1)) {
|
|
8662
|
+
if (done()) {
|
|
8663
|
+
return;
|
|
8664
|
+
}
|
|
8665
|
+
const floor = Math.max(previous + 1, slash - MAX_HOST);
|
|
8666
|
+
previous = slash;
|
|
8667
|
+
let end = slash;
|
|
8668
|
+
while (end > floor && text.codePointAt(end - 1) >= 48 && text.codePointAt(end - 1) <= 57) {
|
|
8669
|
+
end--;
|
|
8670
|
+
}
|
|
8671
|
+
end = end > floor && text[end - 1] === ":" ? end - 1 : slash;
|
|
8672
|
+
let start = end;
|
|
8673
|
+
while (start > floor && isHostCode2(text.codePointAt(start - 1))) {
|
|
8674
|
+
start--;
|
|
8675
|
+
}
|
|
8676
|
+
if (start === end || start < from || cutEnd && slash === text.length - 1) {
|
|
8677
|
+
continue;
|
|
8678
|
+
}
|
|
8679
|
+
if (start > 0 && (IN_WORD2.test(text[start - 1]) || text[start - 1] === "/" && !isSchemeless(text, start)) || inLink(start)) {
|
|
8680
|
+
continue;
|
|
8681
|
+
}
|
|
8682
|
+
while (end > start && text[end - 1] === ".") {
|
|
8683
|
+
end--;
|
|
8684
|
+
}
|
|
8685
|
+
const host = bareName(text.slice(start, end));
|
|
8686
|
+
if (!host || seen.has(host)) {
|
|
8687
|
+
continue;
|
|
8688
|
+
}
|
|
8689
|
+
if (seen.size < MAX_SEEN) {
|
|
8690
|
+
seen.add(host);
|
|
8691
|
+
}
|
|
8692
|
+
if (isRealName(host)) {
|
|
8693
|
+
add(`http://${host}/`);
|
|
8694
|
+
}
|
|
8695
|
+
}
|
|
8696
|
+
}
|
|
8697
|
+
function looseHosts(text, add, { from = 0, cutEnd = false, seen, done }) {
|
|
8698
|
+
const inLink = linkReader(text);
|
|
8699
|
+
const { length } = text;
|
|
8700
|
+
let i = 0;
|
|
8701
|
+
while (i < length) {
|
|
8702
|
+
if (!isHostCode2(text.codePointAt(i))) {
|
|
8703
|
+
i++;
|
|
8704
|
+
continue;
|
|
8705
|
+
}
|
|
8706
|
+
const start = i;
|
|
8707
|
+
while (i < length && isHostCode2(text.codePointAt(i))) {
|
|
8708
|
+
i++;
|
|
8709
|
+
}
|
|
8710
|
+
let end = i;
|
|
8711
|
+
if (end - start > MAX_HOST || start < from || cutEnd && end === length) {
|
|
8712
|
+
continue;
|
|
8713
|
+
}
|
|
8714
|
+
while (end > start && text[end - 1] === ".") {
|
|
8715
|
+
end--;
|
|
8716
|
+
}
|
|
8717
|
+
const before = start > 0 ? text[start - 1] : "";
|
|
8718
|
+
const after = end < length ? text[end] : "";
|
|
8719
|
+
if (after === "/" || after === "@" || before === "@") {
|
|
8720
|
+
continue;
|
|
8721
|
+
}
|
|
8722
|
+
if (before && (IN_WORD2.test(before) || before === "/" || before === "\\" || before === "&" || before === "#")) {
|
|
8723
|
+
continue;
|
|
8724
|
+
}
|
|
8725
|
+
if (after && after !== "." && IN_WORD2.test(after)) {
|
|
8726
|
+
continue;
|
|
8727
|
+
}
|
|
8728
|
+
const name = text.slice(start, end);
|
|
8729
|
+
if (!name.includes(".") || inLink(start)) {
|
|
8730
|
+
continue;
|
|
8731
|
+
}
|
|
8732
|
+
const lower = name.toLowerCase();
|
|
8733
|
+
if (seen.has(lower)) {
|
|
8734
|
+
continue;
|
|
8735
|
+
}
|
|
8736
|
+
if (seen.size < MAX_SEEN) {
|
|
8737
|
+
seen.add(lower);
|
|
8738
|
+
}
|
|
8739
|
+
if (done()) {
|
|
8740
|
+
return;
|
|
8741
|
+
}
|
|
8742
|
+
const host = bareName(name);
|
|
8743
|
+
if (host && isRealName(host)) {
|
|
8744
|
+
add(host);
|
|
8745
|
+
}
|
|
8746
|
+
}
|
|
8747
|
+
}
|
|
8748
|
+
function* chunksOf(html) {
|
|
8749
|
+
let start = 0;
|
|
8750
|
+
let cutStart = false;
|
|
8751
|
+
while (start < html.length) {
|
|
8752
|
+
const end = Math.min(html.length, start + DECODE_CHUNK);
|
|
8753
|
+
if (end < html.length) {
|
|
8754
|
+
let cut = end;
|
|
8755
|
+
while (cut > end - 65536 && cut > start) {
|
|
8756
|
+
if (html[cut] === "<" || isSpace(html.codePointAt(cut))) {
|
|
8757
|
+
break;
|
|
8758
|
+
}
|
|
8759
|
+
cut--;
|
|
8760
|
+
}
|
|
8761
|
+
if (cut > start && cut > end - 65536) {
|
|
8762
|
+
yield { text: html.slice(start, cut), cutStart, cutEnd: false };
|
|
8763
|
+
start = cut;
|
|
8764
|
+
cutStart = false;
|
|
8765
|
+
continue;
|
|
8766
|
+
}
|
|
8767
|
+
let hard = end;
|
|
8768
|
+
while (hard > start + CHUNK_OVERLAP + 1 && /[\dA-Za-z#]/.test(html[hard - 1])) {
|
|
8769
|
+
hard--;
|
|
8770
|
+
}
|
|
8771
|
+
if (hard < end && html[hard - 1] === "&" && hard - 1 > start + CHUNK_OVERLAP) {
|
|
8772
|
+
hard--;
|
|
8773
|
+
} else {
|
|
8774
|
+
hard = end;
|
|
8775
|
+
}
|
|
8776
|
+
yield { text: html.slice(start, hard), cutStart, cutEnd: true };
|
|
8777
|
+
start = hard - CHUNK_OVERLAP;
|
|
8778
|
+
cutStart = true;
|
|
8779
|
+
continue;
|
|
8780
|
+
}
|
|
8781
|
+
yield { text: html.slice(start, end), cutStart, cutEnd: false };
|
|
8782
|
+
start = end;
|
|
8783
|
+
}
|
|
8784
|
+
}
|
|
8785
|
+
function withoutNul(text) {
|
|
8786
|
+
return text.includes("\0") ? `${text}
|
|
8787
|
+
${text.replaceAll("\0", "")}` : text;
|
|
8788
|
+
}
|
|
8789
|
+
function hexValue(code) {
|
|
8790
|
+
if (code >= 48 && code <= 57) {
|
|
8791
|
+
return code - 48;
|
|
8792
|
+
}
|
|
8793
|
+
if (code >= 65 && code <= 70) {
|
|
8794
|
+
return code - 55;
|
|
8795
|
+
}
|
|
8796
|
+
if (code >= 97 && code <= 102) {
|
|
8797
|
+
return code - 87;
|
|
8798
|
+
}
|
|
8799
|
+
return -1;
|
|
8800
|
+
}
|
|
8801
|
+
function percentDecoded(text) {
|
|
8802
|
+
const input = Buffer13.from(text);
|
|
8803
|
+
const output = Buffer13.allocUnsafe(input.length);
|
|
8804
|
+
let length = 0;
|
|
8805
|
+
for (let i = 0; i < input.length; i++) {
|
|
8806
|
+
const high = input[i] === 37 ? hexValue(input[i + 1]) : -1;
|
|
8807
|
+
const low = high === -1 ? -1 : hexValue(input[i + 2]);
|
|
8808
|
+
if (low === -1) {
|
|
8809
|
+
output[length++] = input[i];
|
|
8810
|
+
continue;
|
|
8811
|
+
}
|
|
8812
|
+
output[length++] = high * 16 + low;
|
|
8813
|
+
i += 2;
|
|
8814
|
+
}
|
|
8815
|
+
return output.subarray(0, length);
|
|
8816
|
+
}
|
|
8817
|
+
var UTF16LE = /* @__PURE__ */ new Set(["csunicode", "iso-10646-ucs-2", "ucs-2", "unicode", "unicodefeff", "utf-16", "utf-16le"]);
|
|
8818
|
+
var UTF16BE = /* @__PURE__ */ new Set(["unicodefffe", "utf-16be"]);
|
|
8819
|
+
function swapped(bytes) {
|
|
8820
|
+
return Buffer13.from(bytes.subarray(0, bytes.length - bytes.length % 2)).swap16().toString("utf16le");
|
|
8821
|
+
}
|
|
8822
|
+
function decodeText(content, charset = "") {
|
|
8823
|
+
if (content[0] === 255 && content[1] === 254) {
|
|
8824
|
+
return content.subarray(2).toString("utf16le");
|
|
8825
|
+
}
|
|
8826
|
+
if (content[0] === 254 && content[1] === 255) {
|
|
8827
|
+
return swapped(content.subarray(2));
|
|
8828
|
+
}
|
|
8829
|
+
if (UTF16LE.has(charset)) {
|
|
8830
|
+
return content.toString("utf16le");
|
|
8831
|
+
}
|
|
8832
|
+
return UTF16BE.has(charset) ? swapped(content) : content.toString("utf8");
|
|
8833
|
+
}
|
|
8834
|
+
var DATA_DOCUMENT = /^\s*data:\s*(?:text\/html|application\/xhtml\+xml|image\/svg\+xml|text\/xml|application\/xml)\b([^,]*),/i;
|
|
8835
|
+
function dataDocument(value) {
|
|
8836
|
+
const match = DATA_DOCUMENT.exec(value.slice(0, 1024));
|
|
8837
|
+
if (!match) {
|
|
8838
|
+
return null;
|
|
8839
|
+
}
|
|
8840
|
+
const bytes = percentDecoded(value.slice(match[0].length));
|
|
8841
|
+
const content = /;\s*base64\s*$/i.test(match[1]) ? Buffer13.from(bytes.toString("latin1"), "base64") : bytes;
|
|
8842
|
+
const charset = /;\s*charset\s*=\s*"?([^;"\s]+)/i.exec(match[1]);
|
|
8843
|
+
return decodeText(content, charset ? charset[1].toLowerCase() : "");
|
|
8844
|
+
}
|
|
8845
|
+
var LINE_BREAK = /\r\n?|\n/;
|
|
8846
|
+
var FOLD = /(?:\r\n?|\n)[ \t]/g;
|
|
8847
|
+
var QUOTED_PRINTABLE = /;\s*(?:encoding\s*=\s*)?"?quoted-printable"?\s*(?:;|$)/i;
|
|
8848
|
+
var MEDIA_PROPERTIES = /* @__PURE__ */ new Set(["photo", "logo", "sound", "key", "attach"]);
|
|
8849
|
+
var HTML_PROPERTIES = /^(?:x-alt-desc|styled-description|description|comment)[;:]/i;
|
|
8850
|
+
function readEscapes(text, lenient = false) {
|
|
8851
|
+
let index = text.indexOf("\\");
|
|
8852
|
+
if (index === -1) {
|
|
8853
|
+
return text;
|
|
8854
|
+
}
|
|
8855
|
+
let out = "";
|
|
8856
|
+
let parts = [];
|
|
8857
|
+
let start = 0;
|
|
8858
|
+
while (index !== -1 && index + 1 < text.length) {
|
|
8859
|
+
const next = text[index + 1];
|
|
8860
|
+
if (lenient || "nN,;\\".includes(next)) {
|
|
8861
|
+
parts.push(text.slice(start, index), next === "n" || next === "N" ? "\n" : next);
|
|
8862
|
+
start = index + 2;
|
|
8863
|
+
index = text.indexOf("\\", start);
|
|
8864
|
+
if (parts.length >= 65536) {
|
|
8865
|
+
out += parts.join("");
|
|
8866
|
+
parts = [];
|
|
8867
|
+
}
|
|
8868
|
+
} else {
|
|
8869
|
+
index = text.indexOf("\\", index + 1);
|
|
8870
|
+
}
|
|
8871
|
+
}
|
|
8872
|
+
parts.push(text.slice(start));
|
|
8873
|
+
return out + parts.join("");
|
|
8874
|
+
}
|
|
8875
|
+
function valueColon(line) {
|
|
8876
|
+
let colon = line.indexOf(":");
|
|
8877
|
+
let from = 0;
|
|
8878
|
+
for (; ; ) {
|
|
8879
|
+
const quote = line.indexOf('"', from);
|
|
8880
|
+
if (quote === -1 || colon === -1 || colon < quote) {
|
|
8881
|
+
return colon;
|
|
8882
|
+
}
|
|
8883
|
+
const end = line.indexOf('"', quote + 1);
|
|
8884
|
+
if (end === -1) {
|
|
8885
|
+
return -1;
|
|
8886
|
+
}
|
|
8887
|
+
from = end + 1;
|
|
8888
|
+
if (colon < from) {
|
|
8889
|
+
colon = line.indexOf(":", from);
|
|
8890
|
+
}
|
|
8891
|
+
}
|
|
8892
|
+
}
|
|
8893
|
+
function trimSpaceEnd(text) {
|
|
8894
|
+
let end = text.length;
|
|
8895
|
+
while (end > 0 && (text[end - 1] === " " || text[end - 1] === " ")) {
|
|
8896
|
+
end--;
|
|
8897
|
+
}
|
|
8898
|
+
return end === text.length ? text : text.slice(0, end);
|
|
8899
|
+
}
|
|
8900
|
+
function decodeQuotedPrintable(value, charset) {
|
|
8901
|
+
let decoder;
|
|
8902
|
+
try {
|
|
8903
|
+
decoder = new TextDecoder(charset || "utf8");
|
|
8904
|
+
} catch {
|
|
8905
|
+
decoder = new TextDecoder("utf8");
|
|
8906
|
+
}
|
|
8907
|
+
if (!/[\u0080-\u{10FFFF}]/u.test(value)) {
|
|
8908
|
+
const bytes = Buffer13.from(value, "latin1");
|
|
8909
|
+
let length = 0;
|
|
8910
|
+
for (let index = 0; index < bytes.length; index++) {
|
|
8911
|
+
const hex = bytes[index] === 61 ? value.slice(index + 1, index + 3) : "";
|
|
8912
|
+
if (/^[\da-f]{2}$/i.test(hex)) {
|
|
8913
|
+
bytes[length++] = Number.parseInt(hex, 16);
|
|
8914
|
+
index += 2;
|
|
8915
|
+
} else {
|
|
8916
|
+
bytes[length++] = bytes[index];
|
|
8917
|
+
}
|
|
8918
|
+
}
|
|
8919
|
+
return decoder.decode(bytes.subarray(0, length));
|
|
8920
|
+
}
|
|
8921
|
+
return value.replaceAll(/(?:=[\da-f]{2})+/gi, (run) => decoder.decode(Buffer13.from(run.replaceAll("=", ""), "hex")));
|
|
8922
|
+
}
|
|
8923
|
+
function quotedPrintableValues(text) {
|
|
8924
|
+
if (!/quoted-printable/i.test(text)) {
|
|
8925
|
+
return text;
|
|
8926
|
+
}
|
|
8927
|
+
const lines2 = text.split(LINE_BREAK);
|
|
8928
|
+
const out = [];
|
|
8929
|
+
for (let i = 0; i < lines2.length; i++) {
|
|
8930
|
+
const line = lines2[i];
|
|
8931
|
+
const colon = valueColon(line);
|
|
8932
|
+
const parameters = colon === -1 ? "" : line.slice(0, colon);
|
|
8933
|
+
if (colon === -1 || !QUOTED_PRINTABLE.test(parameters)) {
|
|
8934
|
+
out.push(line);
|
|
8935
|
+
continue;
|
|
8936
|
+
}
|
|
8937
|
+
const charset = /;\s*charset\s*=\s*"?([\w.:-]+)/i.exec(parameters)?.[1];
|
|
8938
|
+
const parts = [trimSpaceEnd(line.slice(colon + 1))];
|
|
8939
|
+
while (parts.at(-1).endsWith("=") && i + 1 < lines2.length) {
|
|
8940
|
+
parts[parts.length - 1] = parts.at(-1).slice(0, -1);
|
|
8941
|
+
parts.push(trimSpaceEnd(lines2[++i]));
|
|
8942
|
+
}
|
|
8943
|
+
out.push(line.slice(0, colon + 1) + decodeQuotedPrintable(parts.join(""), charset));
|
|
8944
|
+
}
|
|
8945
|
+
return out.join("\n");
|
|
8946
|
+
}
|
|
8947
|
+
function shownText(text) {
|
|
8948
|
+
const unfolded = text.replaceAll(FOLD, "");
|
|
8949
|
+
const shown = /* @__PURE__ */ new Set([text, readEscapes(unfolded)]);
|
|
8950
|
+
const decoded = quotedPrintableValues(unfolded);
|
|
8951
|
+
if (decoded !== unfolded) {
|
|
8952
|
+
shown.add(readEscapes(decoded));
|
|
8953
|
+
if (unfolded !== text) {
|
|
8954
|
+
shown.add(quotedPrintableValues(text));
|
|
8955
|
+
}
|
|
8956
|
+
}
|
|
8957
|
+
if (/\\[^nN,;\\]/.test(decoded)) {
|
|
8958
|
+
shown.add(readEscapes(decoded, true));
|
|
8959
|
+
}
|
|
8960
|
+
return [...shown].reverse().join("\n");
|
|
8961
|
+
}
|
|
8962
|
+
function htmlDescriptions(text) {
|
|
8963
|
+
if (!/[<&]|altrep/i.test(text)) {
|
|
8964
|
+
return "";
|
|
8965
|
+
}
|
|
8966
|
+
const html = [];
|
|
8967
|
+
for (const line of text.replaceAll(FOLD, "").split(LINE_BREAK)) {
|
|
8968
|
+
const colon = valueColon(line);
|
|
8969
|
+
if (colon === -1) {
|
|
8970
|
+
continue;
|
|
8971
|
+
}
|
|
8972
|
+
const altrep = /;\s*altrep\s*=\s*"([^"]*)"/i.exec(line.slice(0, colon))?.[1];
|
|
8973
|
+
if (altrep) {
|
|
8974
|
+
html.push(`<iframe src="${altrep.replaceAll("&", "&").replaceAll('"', """).replaceAll("<", "<")}"></iframe>`);
|
|
8975
|
+
}
|
|
8976
|
+
if (!HTML_PROPERTIES.test(line)) {
|
|
8977
|
+
continue;
|
|
8978
|
+
}
|
|
8979
|
+
const value = readEscapes(line.slice(colon + 1));
|
|
8980
|
+
if (/[<&]/.test(value)) {
|
|
8981
|
+
html.push(value);
|
|
8982
|
+
}
|
|
8983
|
+
}
|
|
8984
|
+
return html.join("\n");
|
|
8985
|
+
}
|
|
8986
|
+
var BINARY = /;\s*(?:(?:encoding\s*=\s*)?"?(?:b|base64)"?|value\s*=\s*"?binary"?)\s*(?:;|$)/i;
|
|
8987
|
+
function withoutBinaryValues(text) {
|
|
8988
|
+
if (!/base64|binary|encoding\s*=\s*"?b|;\s*b\s*[;:]/i.test(text)) {
|
|
8989
|
+
return text;
|
|
8990
|
+
}
|
|
8991
|
+
const lines2 = text.split(LINE_BREAK);
|
|
8992
|
+
const out = [];
|
|
8993
|
+
for (let i = 0; i < lines2.length; i++) {
|
|
8994
|
+
const line = lines2[i];
|
|
8995
|
+
const colon = valueColon(line);
|
|
8996
|
+
if (colon === -1 || !BINARY.test(line.slice(0, colon))) {
|
|
8997
|
+
out.push(line);
|
|
8998
|
+
continue;
|
|
8999
|
+
}
|
|
9000
|
+
const value = [line.slice(colon + 1)];
|
|
9001
|
+
let next = i + 1;
|
|
9002
|
+
while (next < lines2.length && (lines2[next].startsWith(" ") || lines2[next].startsWith(" "))) {
|
|
9003
|
+
value.push(lines2[next++]);
|
|
9004
|
+
}
|
|
9005
|
+
if (!value.every((part) => /^[\w+/=\s]*$/.test(part))) {
|
|
9006
|
+
for (let index = i; index < next; index++) {
|
|
9007
|
+
out.push(lines2[index]);
|
|
9008
|
+
}
|
|
9009
|
+
i = next - 1;
|
|
9010
|
+
continue;
|
|
9011
|
+
}
|
|
9012
|
+
while (next < lines2.length && lines2[next] !== "" && /^[\w+/=]+$/.test(lines2[next])) {
|
|
9013
|
+
value.push(lines2[next++]);
|
|
9014
|
+
}
|
|
9015
|
+
if (next < lines2.length && lines2[next] === "") {
|
|
9016
|
+
next++;
|
|
9017
|
+
}
|
|
9018
|
+
i = next - 1;
|
|
9019
|
+
const name = line.slice(0, colon).split(";")[0].split(".").pop().trim().toLowerCase();
|
|
9020
|
+
out.push(MEDIA_PROPERTIES.has(name) ? line.slice(0, colon + 1) : line.slice(0, colon + 1) + Buffer13.from(value.join("").replaceAll(/\s/g, ""), "base64").toString("utf8"));
|
|
9021
|
+
}
|
|
9022
|
+
return out.join("\n");
|
|
9023
|
+
}
|
|
9024
|
+
var HTML_TYPES = /* @__PURE__ */ new Set(["text/html", "application/xhtml+xml", "image/svg+xml", "text/xml", "application/xml"]);
|
|
9025
|
+
var HTML_EXTENSIONS = /* @__PURE__ */ new Set(["html", "htm", "shtml", "xhtml", "xht", "svg", "xml"]);
|
|
9026
|
+
var CARD_TYPES = /* @__PURE__ */ new Set(["text/calendar", "text/x-vcalendar", "application/ics", "text/vcard", "text/x-vcard", "text/directory"]);
|
|
9027
|
+
var CARD_EXTENSIONS = /* @__PURE__ */ new Set(["ics", "ical", "ifb", "vcs", "vcf", "vcard"]);
|
|
9028
|
+
var MESSAGE_TYPES = /* @__PURE__ */ new Set(["message/rfc822", "message/global"]);
|
|
9029
|
+
function extensionOf2(filename) {
|
|
9030
|
+
let end = filename.length;
|
|
9031
|
+
while (end > 0 && (filename[end - 1] === "." || filename[end - 1] === " ")) {
|
|
9032
|
+
end--;
|
|
9033
|
+
}
|
|
9034
|
+
const name = filename.slice(0, end).trim().toLowerCase();
|
|
9035
|
+
const index = name.lastIndexOf(".");
|
|
9036
|
+
return index !== -1 && index < name.length - 1 ? name.slice(index + 1) : null;
|
|
9037
|
+
}
|
|
9038
|
+
function extensionsOf(attachment) {
|
|
9039
|
+
const names = [];
|
|
9040
|
+
if (typeof attachment.filename === "string") {
|
|
9041
|
+
names.push(attachment.filename);
|
|
9042
|
+
}
|
|
9043
|
+
const { headers } = attachment;
|
|
9044
|
+
if (headers && typeof headers.get === "function") {
|
|
9045
|
+
for (const [field, parameter] of [["content-type", "name"], ["content-disposition", "filename"]]) {
|
|
9046
|
+
const value = headers.get(field)?.params?.[parameter];
|
|
9047
|
+
if (typeof value === "string" && value !== "") {
|
|
9048
|
+
names.push(value);
|
|
9049
|
+
}
|
|
9050
|
+
}
|
|
9051
|
+
}
|
|
9052
|
+
return new Set(names.flatMap((name) => [extensionOf2(name), extensionOf(name)]).filter(Boolean));
|
|
9053
|
+
}
|
|
9054
|
+
function charsetOf(attachment) {
|
|
9055
|
+
const { headers } = attachment;
|
|
9056
|
+
const charset = headers && typeof headers.get === "function" ? headers.get("content-type")?.params?.charset : null;
|
|
9057
|
+
return typeof charset === "string" ? charset.toLowerCase() : "";
|
|
9058
|
+
}
|
|
9059
|
+
function kindOf(attachment) {
|
|
9060
|
+
const { content } = attachment ?? {};
|
|
9061
|
+
if (!Buffer13.isBuffer(content) || content.length === 0) {
|
|
9062
|
+
return null;
|
|
9063
|
+
}
|
|
9064
|
+
const type = typeof attachment.contentType === "string" ? attachment.contentType.toLowerCase() : "";
|
|
9065
|
+
const extensions = extensionsOf(attachment);
|
|
9066
|
+
const has = (set) => [...extensions].some((name) => set.has(name));
|
|
9067
|
+
if (MESSAGE_TYPES.has(type) || extensions.has("eml") || extensions.has("u8msg")) {
|
|
9068
|
+
return null;
|
|
9069
|
+
}
|
|
9070
|
+
const isHtml = HTML_TYPES.has(type) || has(HTML_EXTENSIONS);
|
|
9071
|
+
const isCard = !isHtml && (CARD_TYPES.has(type) || has(CARD_EXTENSIONS));
|
|
9072
|
+
const isText = isCard || !isHtml && type === "text/plain";
|
|
9073
|
+
return isHtml || isText ? { isHtml, isText, isCard } : null;
|
|
9074
|
+
}
|
|
9075
|
+
function* documentsOf(attachments, state, late) {
|
|
9076
|
+
const read = [];
|
|
9077
|
+
let bytes = 0;
|
|
9078
|
+
let opened = 0;
|
|
9079
|
+
for (const attachment of attachments) {
|
|
9080
|
+
const kind = kindOf(attachment);
|
|
9081
|
+
if (!kind) {
|
|
9082
|
+
continue;
|
|
9083
|
+
}
|
|
9084
|
+
if (!kind.isText && ++opened > MAX_ATTACHED || bytes + attachment.content.length > MAX_ATTACHED_BYTES) {
|
|
9085
|
+
state.tooComplex = true;
|
|
9086
|
+
break;
|
|
9087
|
+
}
|
|
9088
|
+
bytes += attachment.content.length;
|
|
9089
|
+
read.push({ attachment, kind });
|
|
9090
|
+
}
|
|
9091
|
+
for (const { attachment, kind: { isHtml, isCard } } of read) {
|
|
9092
|
+
if (late()) {
|
|
9093
|
+
state.incomplete = true;
|
|
9094
|
+
return;
|
|
9095
|
+
}
|
|
9096
|
+
const text = decodeText(attachment.content, charsetOf(attachment));
|
|
9097
|
+
if (isHtml) {
|
|
9098
|
+
yield { html: text };
|
|
9099
|
+
continue;
|
|
9100
|
+
}
|
|
9101
|
+
if (!isCard) {
|
|
9102
|
+
yield { text };
|
|
9103
|
+
continue;
|
|
9104
|
+
}
|
|
9105
|
+
let readable = withoutBinaryValues(text.length > MAX_CARD_BYTES ? text.slice(0, MAX_CARD_BYTES) : text);
|
|
9106
|
+
if (text.length > MAX_CARD_BYTES || readable.length > MAX_SHOWN_BYTES) {
|
|
9107
|
+
state.tooComplex = true;
|
|
9108
|
+
readable = readable.slice(0, MAX_SHOWN_BYTES);
|
|
9109
|
+
}
|
|
9110
|
+
if (late()) {
|
|
9111
|
+
state.incomplete = true;
|
|
9112
|
+
return;
|
|
9113
|
+
}
|
|
9114
|
+
yield { text: shownText(readable) };
|
|
9115
|
+
if (late()) {
|
|
9116
|
+
state.incomplete = true;
|
|
9117
|
+
return;
|
|
9118
|
+
}
|
|
9119
|
+
const html = htmlDescriptions(readable);
|
|
9120
|
+
if (html !== "") {
|
|
9121
|
+
yield { html };
|
|
9122
|
+
}
|
|
9123
|
+
}
|
|
9124
|
+
}
|
|
9125
|
+
var LATE_EVERY = 1024;
|
|
9126
|
+
function carriedLinkHosts(mail, {
|
|
9127
|
+
mails = [],
|
|
9128
|
+
attachments = [],
|
|
9129
|
+
maxHosts = 250,
|
|
9130
|
+
hostsInText = false
|
|
9131
|
+
} = {}) {
|
|
9132
|
+
const hosts = /* @__PURE__ */ new Set();
|
|
9133
|
+
const loose = /* @__PURE__ */ new Set();
|
|
9134
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9135
|
+
const seenBare = /* @__PURE__ */ new Set();
|
|
9136
|
+
const seenLoose = /* @__PURE__ */ new Set();
|
|
9137
|
+
let more = false;
|
|
9138
|
+
let full = false;
|
|
9139
|
+
const domains = /* @__PURE__ */ new Set();
|
|
9140
|
+
const state = { tooComplex: false, incomplete: false };
|
|
9141
|
+
const until = Date.now() + MAX_READ_TIME;
|
|
9142
|
+
const late = () => {
|
|
9143
|
+
if (!state.incomplete && Date.now() > until) {
|
|
9144
|
+
state.incomplete = true;
|
|
9145
|
+
}
|
|
9146
|
+
return state.incomplete;
|
|
9147
|
+
};
|
|
9148
|
+
let calls = 0;
|
|
9149
|
+
const done = () => full || state.incomplete || ++calls % LATE_EVERY === 0 && late();
|
|
9150
|
+
const looseDone = () => done() || loose.size > maxHosts;
|
|
9151
|
+
const addLoose = (host) => {
|
|
9152
|
+
if (!looseDone()) {
|
|
9153
|
+
loose.add(host);
|
|
9154
|
+
}
|
|
9155
|
+
};
|
|
9156
|
+
const queue = [mail, ...mails].map((document) => ({ document, depth: 0 }));
|
|
9157
|
+
const carried = documentsOf(attachments, state, late);
|
|
9158
|
+
let depth = 0;
|
|
9159
|
+
let embedded = 0;
|
|
9160
|
+
let embeddedLength = 0;
|
|
9161
|
+
const embeddedSeen = /* @__PURE__ */ new Set();
|
|
9162
|
+
const embed = (html) => {
|
|
9163
|
+
if (html === "" || embeddedSeen.has(html)) {
|
|
9164
|
+
return;
|
|
9165
|
+
}
|
|
9166
|
+
if (depth >= MAX_EMBED_DEPTH || embedded >= MAX_EMBEDDED || embeddedLength + html.length > MAX_EMBEDDED_LENGTH) {
|
|
9167
|
+
state.tooComplex = true;
|
|
9168
|
+
return;
|
|
9169
|
+
}
|
|
9170
|
+
embeddedSeen.add(html);
|
|
9171
|
+
embedded++;
|
|
9172
|
+
embeddedLength += html.length;
|
|
9173
|
+
queue.push({ document: { html }, depth: depth + 1 });
|
|
9174
|
+
};
|
|
9175
|
+
const add = (value) => {
|
|
9176
|
+
if (full || typeof value !== "string" || value === "" || seen.has(value)) {
|
|
9177
|
+
return;
|
|
9178
|
+
}
|
|
9179
|
+
if (seen.size < MAX_SEEN) {
|
|
9180
|
+
seen.add(value);
|
|
9181
|
+
}
|
|
9182
|
+
if (/^\s*data:/i.test(value)) {
|
|
9183
|
+
const html = dataDocument(value);
|
|
9184
|
+
if (html !== null) {
|
|
9185
|
+
embed(html);
|
|
9186
|
+
}
|
|
9187
|
+
return;
|
|
9188
|
+
}
|
|
9189
|
+
const found = hostsOf(value);
|
|
9190
|
+
if (found.unresolved) {
|
|
9191
|
+
state.tooComplex = true;
|
|
9192
|
+
}
|
|
9193
|
+
for (const host of found) {
|
|
9194
|
+
if (hosts.has(host)) {
|
|
9195
|
+
continue;
|
|
9196
|
+
}
|
|
9197
|
+
const domain = registrableDomain(host);
|
|
9198
|
+
if (hosts.size < maxHosts || !domains.has(domain) && domains.size < maxHosts) {
|
|
9199
|
+
hosts.add(host);
|
|
9200
|
+
domains.add(domain);
|
|
9201
|
+
continue;
|
|
9202
|
+
}
|
|
9203
|
+
more = true;
|
|
9204
|
+
if (domains.size >= maxHosts) {
|
|
9205
|
+
full = true;
|
|
9206
|
+
return;
|
|
9207
|
+
}
|
|
9208
|
+
}
|
|
9209
|
+
};
|
|
9210
|
+
let spent = 0;
|
|
9211
|
+
const spend = (length) => {
|
|
9212
|
+
spent += length;
|
|
9213
|
+
if (spent < LONG_VALUE) {
|
|
9214
|
+
return false;
|
|
9215
|
+
}
|
|
9216
|
+
spent = 0;
|
|
9217
|
+
return late();
|
|
9218
|
+
};
|
|
9219
|
+
const reader = {
|
|
9220
|
+
add,
|
|
9221
|
+
embed,
|
|
9222
|
+
spend,
|
|
9223
|
+
cut() {
|
|
9224
|
+
state.tooComplex = true;
|
|
9225
|
+
}
|
|
9226
|
+
};
|
|
9227
|
+
const readText = (source, { cutStart = false, cutEnd = false, exact = false } = {}) => {
|
|
9228
|
+
for (const match of source.matchAll(URL_START)) {
|
|
9229
|
+
if (done()) {
|
|
9230
|
+
break;
|
|
9231
|
+
}
|
|
9232
|
+
if (cutStart && match.index === 0) {
|
|
9233
|
+
continue;
|
|
9234
|
+
}
|
|
9235
|
+
if (cutEnd && match.index + match[0].length === source.length) {
|
|
9236
|
+
if (exact && source.length - match.index < CHUNK_OVERLAP) {
|
|
9237
|
+
continue;
|
|
9238
|
+
}
|
|
9239
|
+
const value = match[0];
|
|
9240
|
+
const at = /^www\./i.test(value) ? 0 : value.indexOf("//") + 2;
|
|
9241
|
+
if (!/[/?#\\]/.test(value.slice(at))) {
|
|
9242
|
+
state.tooComplex = true;
|
|
9243
|
+
continue;
|
|
9244
|
+
}
|
|
9245
|
+
add(value);
|
|
9246
|
+
const url = urlOf(value);
|
|
9247
|
+
if (url && isClickTracker(url)) {
|
|
9248
|
+
state.tooComplex = true;
|
|
9249
|
+
}
|
|
9250
|
+
continue;
|
|
9251
|
+
}
|
|
9252
|
+
add(match[0]);
|
|
9253
|
+
}
|
|
9254
|
+
const from = cutStart ? 1 : 0;
|
|
9255
|
+
bareHosts(source, add, {
|
|
9256
|
+
done,
|
|
9257
|
+
seen: seenBare,
|
|
9258
|
+
from,
|
|
9259
|
+
cutEnd
|
|
9260
|
+
});
|
|
9261
|
+
if (hostsInText && !looseDone()) {
|
|
9262
|
+
looseHosts(source, addLoose, {
|
|
9263
|
+
from,
|
|
9264
|
+
cutEnd,
|
|
9265
|
+
seen: seenLoose,
|
|
9266
|
+
done: looseDone
|
|
9267
|
+
});
|
|
9268
|
+
}
|
|
9269
|
+
};
|
|
9270
|
+
for (let index = 0; ; index++) {
|
|
9271
|
+
if (full || late()) {
|
|
9272
|
+
break;
|
|
9273
|
+
}
|
|
9274
|
+
if (index >= queue.length) {
|
|
9275
|
+
const next = carried.next();
|
|
9276
|
+
if (next.done) {
|
|
9277
|
+
break;
|
|
9278
|
+
}
|
|
9279
|
+
queue.push({ document: next.value, depth: 0 });
|
|
9280
|
+
}
|
|
9281
|
+
({ depth } = queue[index]);
|
|
9282
|
+
const { document } = queue[index];
|
|
9283
|
+
const html = withoutNul(typeof document?.html === "string" ? document.html : "");
|
|
9284
|
+
const text = withoutNul(typeof document?.text === "string" ? document.text : "");
|
|
9285
|
+
for (const [name, value] of attributesOf(html)) {
|
|
9286
|
+
if (done()) {
|
|
9287
|
+
break;
|
|
9288
|
+
}
|
|
9289
|
+
if (!LINK_ATTRIBUTES.has(name)) {
|
|
9290
|
+
continue;
|
|
9291
|
+
}
|
|
9292
|
+
if (spend(value.length)) {
|
|
9293
|
+
break;
|
|
9294
|
+
}
|
|
9295
|
+
linksOf(name, value, reader);
|
|
9296
|
+
}
|
|
9297
|
+
lenientLinks(html, done, reader);
|
|
9298
|
+
for (const pattern of [CSS_URL, CSS_IMPORT]) {
|
|
9299
|
+
for (const [, link] of html.matchAll(pattern)) {
|
|
9300
|
+
if (done() || spend(link.length)) {
|
|
9301
|
+
break;
|
|
9302
|
+
}
|
|
9303
|
+
if (link.length > MAX_VALUE2) {
|
|
9304
|
+
state.tooComplex = true;
|
|
9305
|
+
}
|
|
9306
|
+
add(decodeEntities(link.slice(0, MAX_VALUE2), { attribute: true }));
|
|
9307
|
+
}
|
|
9308
|
+
}
|
|
9309
|
+
for (const chunk2 of chunksOf(text)) {
|
|
9310
|
+
if (full || late()) {
|
|
9311
|
+
break;
|
|
9312
|
+
}
|
|
9313
|
+
readText(chunk2.text, { ...chunk2, exact: true });
|
|
9314
|
+
}
|
|
9315
|
+
for (const chunk2 of chunksOf(html)) {
|
|
9316
|
+
if (full || late()) {
|
|
9317
|
+
break;
|
|
9318
|
+
}
|
|
9319
|
+
const decoded = decodeEntities(chunk2.text);
|
|
9320
|
+
readText(decoded, { ...chunk2, exact: decoded.endsWith(chunk2.text.slice(-CHUNK_OVERLAP)) });
|
|
9321
|
+
}
|
|
9322
|
+
}
|
|
9323
|
+
if (!more && !state.incomplete) {
|
|
9324
|
+
for (const name of loose) {
|
|
9325
|
+
if (hosts.size >= maxHosts) {
|
|
9326
|
+
break;
|
|
9327
|
+
}
|
|
9328
|
+
const [host] = hostsOf(`http://${name}/`);
|
|
9329
|
+
if (host) {
|
|
9330
|
+
hosts.add(host);
|
|
9331
|
+
}
|
|
9332
|
+
}
|
|
9333
|
+
}
|
|
9334
|
+
return {
|
|
9335
|
+
hosts: [...hosts],
|
|
9336
|
+
more,
|
|
9337
|
+
tooComplex: state.tooComplex,
|
|
9338
|
+
incomplete: state.incomplete
|
|
9339
|
+
};
|
|
9340
|
+
}
|
|
9341
|
+
|
|
9342
|
+
// src/is-arbitrary.js
|
|
9343
|
+
import { parse as parseDomain4 } from "tldts";
|
|
8135
9344
|
var GTUBE = "XJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X";
|
|
8136
9345
|
var BLOCKED_SUBJECT = /cheecck y0ur acc0untt|\brecorded\s+you\s+(?:masturbating|jerking|touching\s+yourself|watching\s+porn)\b|personal data has leaked|private information has been stolen/i;
|
|
8137
9346
|
var HACKED_SUBJECT = /\byou['’]ve been hacked\b|\baccount is hacked\b|\b(?:i|we)(?:\s+have|['’]ve)?\s+recorded\s+you\b|\brecorded\s+you\s+(?:doing|while|watching|through|on\s+(?:cam|camera|video|webcam))\b/i;
|
|
@@ -8322,13 +9531,13 @@ function joinsDisplay(brand, display, name) {
|
|
|
8322
9531
|
return false;
|
|
8323
9532
|
}
|
|
8324
9533
|
function isOfficial({ brand, official }, domain, display, shared = true) {
|
|
8325
|
-
const parsed =
|
|
9534
|
+
const parsed = parseDomain4(domain);
|
|
8326
9535
|
const name = parsed.domainWithoutSuffix ?? "";
|
|
8327
9536
|
const compact = brand.replaceAll(" ", "");
|
|
8328
9537
|
if (parsed.publicSuffix === compact || compact.length >= 5 && joinsDisplay(brand, display, name) && !JOINED_WORDS.some((word) => name.slice(compact.length).includes(word))) {
|
|
8329
9538
|
return true;
|
|
8330
9539
|
}
|
|
8331
|
-
return official.some((entry) => entry instanceof RegExp ? entry.test(registrableDomain(domain)) : entry.includes(".") ? (shared ||
|
|
9540
|
+
return official.some((entry) => entry instanceof RegExp ? entry.test(registrableDomain(domain)) : entry.includes(".") ? (shared || parseDomain4(entry).domain !== null) && (domain === entry || domain.endsWith(`.${entry}`)) : name === entry);
|
|
8332
9541
|
}
|
|
8333
9542
|
function headerString(mail, name) {
|
|
8334
9543
|
const value = mail.headers?.get?.(name);
|
|
@@ -8430,7 +9639,7 @@ ${withoutHelp(markup)}`) || isInjection(readable) || attributeText(markup).some(
|
|
|
8430
9639
|
}
|
|
8431
9640
|
} else if (claimed && isWholeName(name, claimed)) {
|
|
8432
9641
|
const domain = claimed[1];
|
|
8433
|
-
const brand = MAILBOXES.has(
|
|
9642
|
+
const brand = MAILBOXES.has(parseDomain4(domain).domainWithoutSuffix) ? null : DISPLAY_BRANDS.find((entry) => isOfficial(entry, domain, "", false) && !isOfficial(entry, session.originalFromAddressDomain, ""));
|
|
8434
9643
|
if (brand) {
|
|
8435
9644
|
hit("FROM_NAME_BRAND", 2, `Display name is an address of "${brand.brand}" but the message is from ${fromRoot}`, "PHISHING");
|
|
8436
9645
|
}
|
|
@@ -8701,21 +9910,21 @@ async function withTimeout(promise, ms, fallback) {
|
|
|
8701
9910
|
}
|
|
8702
9911
|
}
|
|
8703
9912
|
async function toBuffer(source) {
|
|
8704
|
-
if (
|
|
9913
|
+
if (Buffer14.isBuffer(source)) {
|
|
8705
9914
|
return source;
|
|
8706
9915
|
}
|
|
8707
9916
|
if (typeof source === "string") {
|
|
8708
|
-
return
|
|
9917
|
+
return Buffer14.from(source, "utf8");
|
|
8709
9918
|
}
|
|
8710
9919
|
if (source instanceof Uint8Array) {
|
|
8711
|
-
return
|
|
9920
|
+
return Buffer14.from(source);
|
|
8712
9921
|
}
|
|
8713
9922
|
if (source && typeof source[Symbol.asyncIterator] === "function") {
|
|
8714
9923
|
const chunks = [];
|
|
8715
9924
|
for await (const chunk2 of source) {
|
|
8716
|
-
chunks.push(
|
|
9925
|
+
chunks.push(Buffer14.isBuffer(chunk2) ? chunk2 : Buffer14.from(chunk2));
|
|
8717
9926
|
}
|
|
8718
|
-
return
|
|
9927
|
+
return Buffer14.concat(chunks);
|
|
8719
9928
|
}
|
|
8720
9929
|
throw new TypeError("A message must be a Buffer, a string, a Uint8Array or a readable stream");
|
|
8721
9930
|
}
|
|
@@ -8737,6 +9946,19 @@ var DEFAULTS2 = {
|
|
|
8737
9946
|
cloudflare: true,
|
|
8738
9947
|
adult: true,
|
|
8739
9948
|
maxHosts: 25,
|
|
9949
|
+
// Also read the links of the whole message and of what it carries
|
|
9950
|
+
// (attached messages, HTML, SVG and XML files, text files, invitations
|
|
9951
|
+
// and contact cards, documents in iframes and data: URLs) when link
|
|
9952
|
+
// hosts are looked up, for at most five seconds: off by default, as it
|
|
9953
|
+
// reads all of it, up to 50 MB of attachments.
|
|
9954
|
+
attachments: false,
|
|
9955
|
+
// Milliseconds the link lookups may take in all (default: timeout).
|
|
9956
|
+
timeout: void 0,
|
|
9957
|
+
// Also look up host names written in the text without a link or a path
|
|
9958
|
+
// ("evil.example"), which mail clients make links of, as far as there is
|
|
9959
|
+
// room: off by default, as a quoted reply or a signature may name any
|
|
9960
|
+
// site (they are checked for lookalikes either way).
|
|
9961
|
+
hostsInText: false,
|
|
8740
9962
|
// Characters of text read for links (twice as many of HTML): more than
|
|
8741
9963
|
// for the classifier (maxLength), as a link anywhere in a message may be
|
|
8742
9964
|
// followed; a longer message sets results.linksTruncated.
|
|
@@ -8849,7 +10071,7 @@ function fromDomain(mail) {
|
|
|
8849
10071
|
function withHeader(raw) {
|
|
8850
10072
|
const first = raw.subarray(0, 1e3).toString("latin1");
|
|
8851
10073
|
const headerless = raw.length > 0 && !/^(?:[!-9;-~]+[ \t]*:|From |[\r\n])/.test(first);
|
|
8852
|
-
return headerless ?
|
|
10074
|
+
return headerless ? Buffer14.concat([Buffer14.from("\r\n"), raw]) : raw;
|
|
8853
10075
|
}
|
|
8854
10076
|
var SpamScanner = class {
|
|
8855
10077
|
/**
|
|
@@ -9011,7 +10233,10 @@ var SpamScanner = class {
|
|
|
9011
10233
|
async scanFile(file, options) {
|
|
9012
10234
|
return this.scan(await readFile(file), options);
|
|
9013
10235
|
}
|
|
9014
|
-
|
|
10236
|
+
// (`from`: the sender's domain; `carried`: the link hosts of the whole
|
|
10237
|
+
// message and of what it carries, see carriedLinkHosts)
|
|
10238
|
+
async checkLinks(links, config, hosts = [], options = {}) {
|
|
10239
|
+
const { from = "", carried = null } = typeof options === "string" ? { from: options } : options ?? {};
|
|
9015
10240
|
const phishing = [];
|
|
9016
10241
|
const homographs = [];
|
|
9017
10242
|
const seenHosts = /* @__PURE__ */ new Set();
|
|
@@ -9067,18 +10292,23 @@ var SpamScanner = class {
|
|
|
9067
10292
|
for (const host of hosts) {
|
|
9068
10293
|
lookalike(host, { source: "text" });
|
|
9069
10294
|
}
|
|
9070
|
-
const
|
|
10295
|
+
const carriedHosts = (carried?.hosts ?? []).map((host) => lookupName(host)).filter(Boolean);
|
|
10296
|
+
for (const host of carriedHosts) {
|
|
10297
|
+
seenHosts.add(host);
|
|
10298
|
+
}
|
|
10299
|
+
const own = [...found.filter((link) => link.text), ...found.filter((link) => !link.text), ...bare].map((link) => lookupName(destination(link))).filter(Boolean);
|
|
9071
10300
|
const firsts = /* @__PURE__ */ new Map();
|
|
9072
|
-
for (const host of
|
|
10301
|
+
for (const host of [...own, ...carriedHosts]) {
|
|
9073
10302
|
const domain = registrableDomain(host);
|
|
9074
10303
|
if (!firsts.has(domain)) {
|
|
9075
10304
|
firsts.set(domain, host);
|
|
9076
10305
|
}
|
|
9077
10306
|
}
|
|
9078
|
-
const checked = [.../* @__PURE__ */ new Set([...firsts.values(), ...
|
|
10307
|
+
const checked = [.../* @__PURE__ */ new Set([...firsts.values(), ...own, ...carriedHosts])].slice(0, config.phishing.maxHosts);
|
|
9079
10308
|
const domains = [...new Set(checked.map((host) => registrableDomain(host)))];
|
|
9080
10309
|
const blocked = [];
|
|
9081
10310
|
let answered = 0;
|
|
10311
|
+
const linkTimeout = Number(config.phishing.timeout) > 0 ? Number(config.phishing.timeout) : config.timeout;
|
|
9082
10312
|
const cloudflare = async () => {
|
|
9083
10313
|
await withTimeout(Promise.all(checked.map(async (host) => {
|
|
9084
10314
|
const found2 = await this.dns.checkCloudflare([host], { adult: config.phishing.adult });
|
|
@@ -9086,12 +10316,12 @@ var SpamScanner = class {
|
|
|
9086
10316
|
if (!found2.unanswered?.length) {
|
|
9087
10317
|
answered++;
|
|
9088
10318
|
}
|
|
9089
|
-
})),
|
|
10319
|
+
})), linkTimeout, null);
|
|
9090
10320
|
return { found: [...blocked], unanswered: checked.length - answered };
|
|
9091
10321
|
};
|
|
9092
10322
|
const [cloudflareResult, listed] = await Promise.all([
|
|
9093
10323
|
config.phishing.cloudflare && checked.length > 0 ? cloudflare() : { found: [], unanswered: 0 },
|
|
9094
|
-
config.dnsbl.domain?.length > 0 && domains.length > 0 ? withTimeout(this.dns.checkDomains(domains, config.dnsbl.domain),
|
|
10324
|
+
config.dnsbl.domain?.length > 0 && domains.length > 0 ? withTimeout(this.dns.checkDomains(domains, config.dnsbl.domain), linkTimeout, []) : []
|
|
9095
10325
|
]);
|
|
9096
10326
|
for (const { host, category } of cloudflareResult.found) {
|
|
9097
10327
|
phishing.push(finding(category === "malware" ? { type: "malicious_domain", domain: host, message: `Link hostname ${host} is blocked by Cloudflare's malware filtering as phishing or malware` } : { type: "adult_domain", domain: host, message: `Link hostname ${host} was detected by Cloudflare's family filtering to contain adult-related content` }));
|
|
@@ -9107,7 +10337,8 @@ var SpamScanner = class {
|
|
|
9107
10337
|
return {
|
|
9108
10338
|
phishing,
|
|
9109
10339
|
homographs,
|
|
9110
|
-
|
|
10340
|
+
// (at least one when the message has more hosts than were read)
|
|
10341
|
+
uncheckedHosts: Math.max(seenHosts.size - checked.length, carried?.more ? 1 : 0),
|
|
9111
10342
|
timedOutHosts: cloudflareResult.unanswered
|
|
9112
10343
|
};
|
|
9113
10344
|
}
|
|
@@ -9279,10 +10510,17 @@ ${visibleText(html.slice(0, config.maxLength * 2)).text}`, { maxLength: config.m
|
|
|
9279
10510
|
styled: plainObfuscation.styled || shownObfuscation.styled
|
|
9280
10511
|
};
|
|
9281
10512
|
const unopened = [];
|
|
9282
|
-
const
|
|
10513
|
+
const opened = [];
|
|
10514
|
+
const attached = await expandAttachments(mail.attachments, parseErrors, void 0, { unopened, mails: opened });
|
|
10515
|
+
const carried = config.phishing.attachments === true && (config.phishing.cloudflare || config.dnsbl.domain?.length > 0) ? carriedLinkHosts(mail, {
|
|
10516
|
+
mails: opened,
|
|
10517
|
+
attachments: attached,
|
|
10518
|
+
maxHosts: config.phishing.maxHosts,
|
|
10519
|
+
hostsInText: config.phishing.hostsInText === true
|
|
10520
|
+
}) : null;
|
|
9283
10521
|
const attachments = config.attachments === false ? [] : inspectAttachments(attached).filter((item) => config.macros !== false || !["macro", "pdf_active", "rtf_object"].includes(item.type)).map((item) => finding(item));
|
|
9284
10522
|
const [links, authentication, checkedReputation, dnsbl, virusScan, toxicity, nsfw] = await Promise.all([
|
|
9285
|
-
this.checkLinks(checkedLinks, config, extracted.hosts, fromDomain(mail)),
|
|
10523
|
+
this.checkLinks(checkedLinks, config, extracted.hosts, { from: fromDomain(mail), carried }),
|
|
9286
10524
|
config.authenticationResults && typeof config.authenticationResults === "object" ? normalizeAuthOutput(config.authenticationResults) : config.authentication && session.remoteAddress ? withTimeout(authenticate(raw, {
|
|
9287
10525
|
timeout: config.timeout,
|
|
9288
10526
|
...typeof config.authentication === "object" ? config.authentication : {},
|
|
@@ -9410,6 +10648,14 @@ ${visibleText(html.slice(0, config.maxLength * 2)).text}`, { maxLength: config.m
|
|
|
9410
10648
|
// Whether the message has more text than phishing.maxLength, whose
|
|
9411
10649
|
// links were not read.
|
|
9412
10650
|
linksTruncated,
|
|
10651
|
+
// Whether a link host of the message or of what it carries was
|
|
10652
|
+
// never read whole (it runs past where long text is cut to be
|
|
10653
|
+
// read, it goes through more security gateways than are
|
|
10654
|
+
// unwrapped, or it is in more documents than are read), and whether
|
|
10655
|
+
// an attachment was left unread for time: in either case not every
|
|
10656
|
+
// link was checked (see phishing.attachments).
|
|
10657
|
+
linksTooComplex: Boolean(carried?.tooComplex),
|
|
10658
|
+
linksIncomplete: Boolean(carried?.incomplete),
|
|
9413
10659
|
idnHomographAttack: {
|
|
9414
10660
|
detected: links.homographs.length > 0,
|
|
9415
10661
|
domains: links.homographs,
|
|
@@ -9421,6 +10667,9 @@ ${visibleText(html.slice(0, config.maxLength * 2)).text}`, { maxLength: config.m
|
|
|
9421
10667
|
language: extracted.language,
|
|
9422
10668
|
tokens: extracted.words,
|
|
9423
10669
|
mail,
|
|
10670
|
+
// (the attachments of the message and of its attached messages, as
|
|
10671
|
+
// read, an attached message's named after it)
|
|
10672
|
+
attached,
|
|
9424
10673
|
version: VERSION,
|
|
9425
10674
|
metrics: { totalTime: time }
|
|
9426
10675
|
};
|
|
@@ -9773,13 +11022,13 @@ async function readMessage(stream, maxSize) {
|
|
|
9773
11022
|
for (; ; ) {
|
|
9774
11023
|
const { value, done } = await iterator.next();
|
|
9775
11024
|
if (done) {
|
|
9776
|
-
return { data:
|
|
11025
|
+
return { data: Buffer15.concat(chunks), complete: true, rest: iterator };
|
|
9777
11026
|
}
|
|
9778
|
-
const chunk2 =
|
|
11027
|
+
const chunk2 = Buffer15.from(value);
|
|
9779
11028
|
chunks.push(chunk2);
|
|
9780
11029
|
size += chunk2.length;
|
|
9781
11030
|
if (size > maxSize) {
|
|
9782
|
-
return { data:
|
|
11031
|
+
return { data: Buffer15.concat(chunks), complete: false, rest: iterator };
|
|
9783
11032
|
}
|
|
9784
11033
|
}
|
|
9785
11034
|
}
|
|
@@ -9792,17 +11041,18 @@ async function readInput(file, stdin, maxSize) {
|
|
|
9792
11041
|
}
|
|
9793
11042
|
return data;
|
|
9794
11043
|
}
|
|
11044
|
+
var printable = (text) => String(text ?? "").replaceAll(/[\p{Cc}\u061C\u200E\u200F\u2028\u2029\u202A-\u202E\u2066-\u2069]+/gu, " ");
|
|
9795
11045
|
function formatResult(result, verbose) {
|
|
9796
11046
|
const lines2 = [`${result.isSpam ? "SPAM" : "HAM"} score ${result.score.toFixed(1)} (spam at ${result.threshold.toFixed(1)}, reject at ${result.rejectThreshold.toFixed(1)}) action: ${result.action}${result.language ? ` language: ${result.language}` : ""}`];
|
|
9797
11047
|
const tests = verbose ? result.tests : result.tests.filter((test) => test.score !== 0);
|
|
9798
11048
|
for (const test of [...tests].sort((a, b) => Math.abs(b.score) - Math.abs(a.score))) {
|
|
9799
|
-
lines2.push(` ${`${test.score > 0 ? "+" : ""}${test.score.toFixed(1)}`.padStart(7)} ${test.name.padEnd(28)} ${test.description}`);
|
|
11049
|
+
lines2.push(` ${`${test.score > 0 ? "+" : ""}${test.score.toFixed(1)}`.padStart(7)} ${printable(test.name).padEnd(28)} ${printable(test.description)}`);
|
|
9800
11050
|
}
|
|
9801
11051
|
if (verbose && result.results.classification.clues?.length > 0) {
|
|
9802
|
-
lines2.push(" Strongest clues:", ...result.results.classification.clues.slice(0, 10).map((clue) => ` ${clue.probability.toFixed(3)} ${clue.feature}`));
|
|
11052
|
+
lines2.push(" Strongest clues:", ...result.results.classification.clues.slice(0, 10).map((clue) => ` ${clue.probability.toFixed(3)} ${printable(clue.feature)}`));
|
|
9803
11053
|
}
|
|
9804
11054
|
if (result.results.llm?.error) {
|
|
9805
|
-
lines2.push(` Language model failed: ${result.results.llm.error}`);
|
|
11055
|
+
lines2.push(` Language model failed: ${printable(result.results.llm.error)}`);
|
|
9806
11056
|
}
|
|
9807
11057
|
return lines2.join("\n");
|
|
9808
11058
|
}
|
|
@@ -9940,11 +11190,11 @@ var HANDLERS = {
|
|
|
9940
11190
|
message = rewriteMessage(raw, spamHeaders(result, { version: VERSION }), { subjectTag: result.isSpam ? values["subject-tag"] ?? null : null });
|
|
9941
11191
|
} catch (error) {
|
|
9942
11192
|
if (!accept) {
|
|
9943
|
-
stderr.write(`filter: scan failed, deferring: ${error.message}
|
|
11193
|
+
stderr.write(`filter: scan failed, deferring: ${printable(error.message)}
|
|
9944
11194
|
`);
|
|
9945
11195
|
return 75;
|
|
9946
11196
|
}
|
|
9947
|
-
stderr.write(`filter: scan failed, passing the message on: ${error.message}
|
|
11197
|
+
stderr.write(`filter: scan failed, passing the message on: ${printable(error.message)}
|
|
9948
11198
|
`);
|
|
9949
11199
|
result = null;
|
|
9950
11200
|
message = rewriteMessage(raw, [["X-Spam-Virus-Scan", "failed"]]);
|
|
@@ -9977,10 +11227,10 @@ var HANDLERS = {
|
|
|
9977
11227
|
hostname: values.name || "spamscanner",
|
|
9978
11228
|
onError: onError(values)
|
|
9979
11229
|
});
|
|
9980
|
-
milter.on("error", (error) => stderr.write(`milter: ${error.message}
|
|
11230
|
+
milter.on("error", (error) => stderr.write(`milter: ${printable(error.message)}
|
|
9981
11231
|
`));
|
|
9982
11232
|
if (values.verbose) {
|
|
9983
|
-
milter.on("scan", ({ session, result }) => stderr.write(`${session.remoteAddress || "-"} ${session.envelope?.mailFrom?.address || "<>"} ${result.score.toFixed(1)} ${result.action} ${result.tests.map((test) => test.name).join(",")}
|
|
11233
|
+
milter.on("scan", ({ session, result }) => stderr.write(`${session.remoteAddress || "-"} ${printable(session.envelope?.mailFrom?.address) || "<>"} ${result.score.toFixed(1)} ${result.action} ${result.tests.map((test) => test.name).join(",")}
|
|
9984
11234
|
`));
|
|
9985
11235
|
}
|
|
9986
11236
|
await listen(milter.server, values, 7831, { stderr });
|