spamscanner 7.0.10 → 7.0.11

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/esm/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/index.js
2
- import { Buffer as Buffer13 } from "node:buffer";
2
+ import { Buffer as Buffer14 } from "node:buffer";
3
3
  import { readFile } from "node:fs/promises";
4
4
  import { debuglog as debuglog2 } from "node:util";
5
5
 
@@ -1107,103 +1107,285 @@ var HomographDetector = class {
1107
1107
  }
1108
1108
  };
1109
1109
 
1110
+ // src/entities.js
1111
+ var NAMED = [
1112
+ "AElig=c6,AMP=26,Aacute=c1,Abreve=102,Acirc=c2,Acy=410,Afr=1d504,Agrave=c0,Alpha=391,Amacr=100,And=2a",
1113
+ "53,Aogon=104,Aopf=1d538,ApplyFunction=2061,Aring=c5,Ascr=1d49c,Assign=2254,Atilde=c3,Auml=c4,Backsla",
1114
+ "sh=2216,Barv=2ae7,Barwed=2306,Bcy=411,Because=2235,Bernoullis=212c,Beta=392,Bfr=1d505,Bopf=1d539,Bre",
1115
+ "ve=2d8,Bscr=212c,Bumpeq=224e,CHcy=427,COPY=a9,Cacute=106,Cap=22d2,CapitalDifferentialD=2145,Cayleys=",
1116
+ "212d,Ccaron=10c,Ccedil=c7,Ccirc=108,Cconint=2230,Cdot=10a,Cedilla=b8,CenterDot=b7,Cfr=212d,Chi=3a7,C",
1117
+ "ircleDot=2299,CircleMinus=2296,CirclePlus=2295,CircleTimes=2297,ClockwiseContourIntegral=2232,CloseC",
1118
+ "urlyDoubleQuote=201d,CloseCurlyQuote=2019,Colon=2237,Colone=2a74,Congruent=2261,Conint=222f,ContourI",
1119
+ "ntegral=222e,Copf=2102,Coproduct=2210,CounterClockwiseContourIntegral=2233,Cross=2a2f,Cscr=1d49e,Cup",
1120
+ "=22d3,CupCap=224d,DD=2145,DDotrahd=2911,DJcy=402,DScy=405,DZcy=40f,Dagger=2021,Darr=21a1,Dashv=2ae4,",
1121
+ "Dcaron=10e,Dcy=414,Del=2207,Delta=394,Dfr=1d507,DiacriticalAcute=b4,DiacriticalDot=2d9,DiacriticalDo",
1122
+ "ubleAcute=2dd,DiacriticalGrave=60,DiacriticalTilde=2dc,Diamond=22c4,DifferentialD=2146,Dopf=1d53b,Do",
1123
+ "t=a8,DotDot=20dc,DotEqual=2250,DoubleContourIntegral=222f,DoubleDot=a8,DoubleDownArrow=21d3,DoubleLe",
1124
+ "ftArrow=21d0,DoubleLeftRightArrow=21d4,DoubleLeftTee=2ae4,DoubleLongLeftArrow=27f8,DoubleLongLeftRig",
1125
+ "htArrow=27fa,DoubleLongRightArrow=27f9,DoubleRightArrow=21d2,DoubleRightTee=22a8,DoubleUpArrow=21d1,",
1126
+ "DoubleUpDownArrow=21d5,DoubleVerticalBar=2225,DownArrow=2193,DownArrowBar=2913,DownArrowUpArrow=21f5",
1127
+ ",DownBreve=311,DownLeftRightVector=2950,DownLeftTeeVector=295e,DownLeftVector=21bd,DownLeftVectorBar",
1128
+ "=2956,DownRightTeeVector=295f,DownRightVector=21c1,DownRightVectorBar=2957,DownTee=22a4,DownTeeArrow",
1129
+ "=21a7,Downarrow=21d3,Dscr=1d49f,Dstrok=110,ENG=14a,ETH=d0,Eacute=c9,Ecaron=11a,Ecirc=ca,Ecy=42d,Edot",
1130
+ "=116,Efr=1d508,Egrave=c8,Element=2208,Emacr=112,EmptySmallSquare=25fb,EmptyVerySmallSquare=25ab,Eogo",
1131
+ "n=118,Eopf=1d53c,Epsilon=395,Equal=2a75,EqualTilde=2242,Equilibrium=21cc,Escr=2130,Esim=2a73,Eta=397",
1132
+ ",Euml=cb,Exists=2203,ExponentialE=2147,Fcy=424,Ffr=1d509,FilledSmallSquare=25fc,FilledVerySmallSquar",
1133
+ "e=25aa,Fopf=1d53d,ForAll=2200,Fouriertrf=2131,Fscr=2131,GJcy=403,GT=3e,Gamma=393,Gammad=3dc,Gbreve=1",
1134
+ "1e,Gcedil=122,Gcirc=11c,Gcy=413,Gdot=120,Gfr=1d50a,Gg=22d9,Gopf=1d53e,GreaterEqual=2265,GreaterEqual",
1135
+ "Less=22db,GreaterFullEqual=2267,GreaterGreater=2aa2,GreaterLess=2277,GreaterSlantEqual=2a7e,GreaterT",
1136
+ "ilde=2273,Gscr=1d4a2,Gt=226b,HARDcy=42a,Hacek=2c7,Hat=5e,Hcirc=124,Hfr=210c,HilbertSpace=210b,Hopf=2",
1137
+ "10d,HorizontalLine=2500,Hscr=210b,Hstrok=126,HumpDownHump=224e,HumpEqual=224f,IEcy=415,IJlig=132,IOc",
1138
+ "y=401,Iacute=cd,Icirc=ce,Icy=418,Idot=130,Ifr=2111,Igrave=cc,Im=2111,Imacr=12a,ImaginaryI=2148,Impli",
1139
+ "es=21d2,Int=222c,Integral=222b,Intersection=22c2,InvisibleComma=2063,InvisibleTimes=2062,Iogon=12e,I",
1140
+ "opf=1d540,Iota=399,Iscr=2110,Itilde=128,Iukcy=406,Iuml=cf,Jcirc=134,Jcy=419,Jfr=1d50d,Jopf=1d541,Jsc",
1141
+ "r=1d4a5,Jsercy=408,Jukcy=404,KHcy=425,KJcy=40c,Kappa=39a,Kcedil=136,Kcy=41a,Kfr=1d50e,Kopf=1d542,Ksc",
1142
+ "r=1d4a6,LJcy=409,LT=3c,Lacute=139,Lambda=39b,Lang=27ea,Laplacetrf=2112,Larr=219e,Lcaron=13d,Lcedil=1",
1143
+ "3b,Lcy=41b,LeftAngleBracket=27e8,LeftArrow=2190,LeftArrowBar=21e4,LeftArrowRightArrow=21c6,LeftCeili",
1144
+ "ng=2308,LeftDoubleBracket=27e6,LeftDownTeeVector=2961,LeftDownVector=21c3,LeftDownVectorBar=2959,Lef",
1145
+ "tFloor=230a,LeftRightArrow=2194,LeftRightVector=294e,LeftTee=22a3,LeftTeeArrow=21a4,LeftTeeVector=29",
1146
+ "5a,LeftTriangle=22b2,LeftTriangleBar=29cf,LeftTriangleEqual=22b4,LeftUpDownVector=2951,LeftUpTeeVect",
1147
+ "or=2960,LeftUpVector=21bf,LeftUpVectorBar=2958,LeftVector=21bc,LeftVectorBar=2952,Leftarrow=21d0,Lef",
1148
+ "trightarrow=21d4,LessEqualGreater=22da,LessFullEqual=2266,LessGreater=2276,LessLess=2aa1,LessSlantEq",
1149
+ "ual=2a7d,LessTilde=2272,Lfr=1d50f,Ll=22d8,Lleftarrow=21da,Lmidot=13f,LongLeftArrow=27f5,LongLeftRigh",
1150
+ "tArrow=27f7,LongRightArrow=27f6,Longleftarrow=27f8,Longleftrightarrow=27fa,Longrightarrow=27f9,Lopf=",
1151
+ "1d543,LowerLeftArrow=2199,LowerRightArrow=2198,Lscr=2112,Lsh=21b0,Lstrok=141,Lt=226a,Map=2905,Mcy=41",
1152
+ "c,MediumSpace=205f,Mellintrf=2133,Mfr=1d510,MinusPlus=2213,Mopf=1d544,Mscr=2133,Mu=39c,NJcy=40a,Nacu",
1153
+ "te=143,Ncaron=147,Ncedil=145,Ncy=41d,NegativeMediumSpace=200b,NegativeThickSpace=200b,NegativeThinSp",
1154
+ "ace=200b,NegativeVeryThinSpace=200b,NestedGreaterGreater=226b,NestedLessLess=226a,NewLine=a,Nfr=1d51",
1155
+ "1,NoBreak=2060,NonBreakingSpace=a0,Nopf=2115,Not=2aec,NotCongruent=2262,NotCupCap=226d,NotDoubleVert",
1156
+ "icalBar=2226,NotElement=2209,NotEqual=2260,NotEqualTilde=2242.338,NotExists=2204,NotGreater=226f,Not",
1157
+ "GreaterEqual=2271,NotGreaterFullEqual=2267.338,NotGreaterGreater=226b.338,NotGreaterLess=2279,NotGre",
1158
+ "aterSlantEqual=2a7e.338,NotGreaterTilde=2275,NotHumpDownHump=224e.338,NotHumpEqual=224f.338,NotLeftT",
1159
+ "riangle=22ea,NotLeftTriangleBar=29cf.338,NotLeftTriangleEqual=22ec,NotLess=226e,NotLessEqual=2270,No",
1160
+ "tLessGreater=2278,NotLessLess=226a.338,NotLessSlantEqual=2a7d.338,NotLessTilde=2274,NotNestedGreater",
1161
+ "Greater=2aa2.338,NotNestedLessLess=2aa1.338,NotPrecedes=2280,NotPrecedesEqual=2aaf.338,NotPrecedesSl",
1162
+ "antEqual=22e0,NotReverseElement=220c,NotRightTriangle=22eb,NotRightTriangleBar=29d0.338,NotRightTria",
1163
+ "ngleEqual=22ed,NotSquareSubset=228f.338,NotSquareSubsetEqual=22e2,NotSquareSuperset=2290.338,NotSqua",
1164
+ "reSupersetEqual=22e3,NotSubset=2282.20d2,NotSubsetEqual=2288,NotSucceeds=2281,NotSucceedsEqual=2ab0.",
1165
+ "338,NotSucceedsSlantEqual=22e1,NotSucceedsTilde=227f.338,NotSuperset=2283.20d2,NotSupersetEqual=2289",
1166
+ ",NotTilde=2241,NotTildeEqual=2244,NotTildeFullEqual=2247,NotTildeTilde=2249,NotVerticalBar=2224,Nscr",
1167
+ "=1d4a9,Ntilde=d1,Nu=39d,OElig=152,Oacute=d3,Ocirc=d4,Ocy=41e,Odblac=150,Ofr=1d512,Ograve=d2,Omacr=14",
1168
+ "c,Omega=3a9,Omicron=39f,Oopf=1d546,OpenCurlyDoubleQuote=201c,OpenCurlyQuote=2018,Or=2a54,Oscr=1d4aa,",
1169
+ "Oslash=d8,Otilde=d5,Otimes=2a37,Ouml=d6,OverBar=203e,OverBrace=23de,OverBracket=23b4,OverParenthesis",
1170
+ "=23dc,PartialD=2202,Pcy=41f,Pfr=1d513,Phi=3a6,Pi=3a0,PlusMinus=b1,Poincareplane=210c,Popf=2119,Pr=2a",
1171
+ "bb,Precedes=227a,PrecedesEqual=2aaf,PrecedesSlantEqual=227c,PrecedesTilde=227e,Prime=2033,Product=22",
1172
+ "0f,Proportion=2237,Proportional=221d,Pscr=1d4ab,Psi=3a8,QUOT=22,Qfr=1d514,Qopf=211a,Qscr=1d4ac,RBarr",
1173
+ "=2910,REG=ae,Racute=154,Rang=27eb,Rarr=21a0,Rarrtl=2916,Rcaron=158,Rcedil=156,Rcy=420,Re=211c,Revers",
1174
+ "eElement=220b,ReverseEquilibrium=21cb,ReverseUpEquilibrium=296f,Rfr=211c,Rho=3a1,RightAngleBracket=2",
1175
+ "7e9,RightArrow=2192,RightArrowBar=21e5,RightArrowLeftArrow=21c4,RightCeiling=2309,RightDoubleBracket",
1176
+ "=27e7,RightDownTeeVector=295d,RightDownVector=21c2,RightDownVectorBar=2955,RightFloor=230b,RightTee=",
1177
+ "22a2,RightTeeArrow=21a6,RightTeeVector=295b,RightTriangle=22b3,RightTriangleBar=29d0,RightTriangleEq",
1178
+ "ual=22b5,RightUpDownVector=294f,RightUpTeeVector=295c,RightUpVector=21be,RightUpVectorBar=2954,Right",
1179
+ "Vector=21c0,RightVectorBar=2953,Rightarrow=21d2,Ropf=211d,RoundImplies=2970,Rrightarrow=21db,Rscr=21",
1180
+ "1b,Rsh=21b1,RuleDelayed=29f4,SHCHcy=429,SHcy=428,SOFTcy=42c,Sacute=15a,Sc=2abc,Scaron=160,Scedil=15e",
1181
+ ",Scirc=15c,Scy=421,Sfr=1d516,ShortDownArrow=2193,ShortLeftArrow=2190,ShortRightArrow=2192,ShortUpArr",
1182
+ "ow=2191,Sigma=3a3,SmallCircle=2218,Sopf=1d54a,Sqrt=221a,Square=25a1,SquareIntersection=2293,SquareSu",
1183
+ "bset=228f,SquareSubsetEqual=2291,SquareSuperset=2290,SquareSupersetEqual=2292,SquareUnion=2294,Sscr=",
1184
+ "1d4ae,Star=22c6,Sub=22d0,Subset=22d0,SubsetEqual=2286,Succeeds=227b,SucceedsEqual=2ab0,SucceedsSlant",
1185
+ "Equal=227d,SucceedsTilde=227f,SuchThat=220b,Sum=2211,Sup=22d1,Superset=2283,SupersetEqual=2287,Supse",
1186
+ "t=22d1,THORN=de,TRADE=2122,TSHcy=40b,TScy=426,Tab=9,Tau=3a4,Tcaron=164,Tcedil=162,Tcy=422,Tfr=1d517,",
1187
+ "Therefore=2234,Theta=398,ThickSpace=205f.200a,ThinSpace=2009,Tilde=223c,TildeEqual=2243,TildeFullEqu",
1188
+ "al=2245,TildeTilde=2248,Topf=1d54b,TripleDot=20db,Tscr=1d4af,Tstrok=166,Uacute=da,Uarr=219f,Uarrocir",
1189
+ "=2949,Ubrcy=40e,Ubreve=16c,Ucirc=db,Ucy=423,Udblac=170,Ufr=1d518,Ugrave=d9,Umacr=16a,UnderBar=5f,Und",
1190
+ "erBrace=23df,UnderBracket=23b5,UnderParenthesis=23dd,Union=22c3,UnionPlus=228e,Uogon=172,Uopf=1d54c,",
1191
+ "UpArrow=2191,UpArrowBar=2912,UpArrowDownArrow=21c5,UpDownArrow=2195,UpEquilibrium=296e,UpTee=22a5,Up",
1192
+ "TeeArrow=21a5,Uparrow=21d1,Updownarrow=21d5,UpperLeftArrow=2196,UpperRightArrow=2197,Upsi=3d2,Upsilo",
1193
+ "n=3a5,Uring=16e,Uscr=1d4b0,Utilde=168,Uuml=dc,VDash=22ab,Vbar=2aeb,Vcy=412,Vdash=22a9,Vdashl=2ae6,Ve",
1194
+ "e=22c1,Verbar=2016,Vert=2016,VerticalBar=2223,VerticalLine=7c,VerticalSeparator=2758,VerticalTilde=2",
1195
+ "240,VeryThinSpace=200a,Vfr=1d519,Vopf=1d54d,Vscr=1d4b1,Vvdash=22aa,Wcirc=174,Wedge=22c0,Wfr=1d51a,Wo",
1196
+ "pf=1d54e,Wscr=1d4b2,Xfr=1d51b,Xi=39e,Xopf=1d54f,Xscr=1d4b3,YAcy=42f,YIcy=407,YUcy=42e,Yacute=dd,Ycir",
1197
+ "c=176,Ycy=42b,Yfr=1d51c,Yopf=1d550,Yscr=1d4b4,Yuml=178,ZHcy=416,Zacute=179,Zcaron=17d,Zcy=417,Zdot=1",
1198
+ "7b,ZeroWidthSpace=200b,Zeta=396,Zfr=2128,Zopf=2124,Zscr=1d4b5,aacute=e1,abreve=103,ac=223e,acE=223e.",
1199
+ "333,acd=223f,acirc=e2,acute=b4,acy=430,aelig=e6,af=2061,afr=1d51e,agrave=e0,alefsym=2135,aleph=2135,",
1200
+ "alpha=3b1,amacr=101,amalg=2a3f,amp=26,and=2227,andand=2a55,andd=2a5c,andslope=2a58,andv=2a5a,ang=222",
1201
+ "0,ange=29a4,angle=2220,angmsd=2221,angmsdaa=29a8,angmsdab=29a9,angmsdac=29aa,angmsdad=29ab,angmsdae=",
1202
+ "29ac,angmsdaf=29ad,angmsdag=29ae,angmsdah=29af,angrt=221f,angrtvb=22be,angrtvbd=299d,angsph=2222,ang",
1203
+ "st=c5,angzarr=237c,aogon=105,aopf=1d552,ap=2248,apE=2a70,apacir=2a6f,ape=224a,apid=224b,apos=27,appr",
1204
+ "ox=2248,approxeq=224a,aring=e5,ascr=1d4b6,ast=2a,asymp=2248,asympeq=224d,atilde=e3,auml=e4,awconint=",
1205
+ "2233,awint=2a11,bNot=2aed,backcong=224c,backepsilon=3f6,backprime=2035,backsim=223d,backsimeq=22cd,b",
1206
+ "arvee=22bd,barwed=2305,barwedge=2305,bbrk=23b5,bbrktbrk=23b6,bcong=224c,bcy=431,bdquo=201e,becaus=22",
1207
+ "35,because=2235,bemptyv=29b0,bepsi=3f6,bernou=212c,beta=3b2,beth=2136,between=226c,bfr=1d51f,bigcap=",
1208
+ "22c2,bigcirc=25ef,bigcup=22c3,bigodot=2a00,bigoplus=2a01,bigotimes=2a02,bigsqcup=2a06,bigstar=2605,b",
1209
+ "igtriangledown=25bd,bigtriangleup=25b3,biguplus=2a04,bigvee=22c1,bigwedge=22c0,bkarow=290d,blackloze",
1210
+ "nge=29eb,blacksquare=25aa,blacktriangle=25b4,blacktriangledown=25be,blacktriangleleft=25c2,blacktria",
1211
+ "ngleright=25b8,blank=2423,blk12=2592,blk14=2591,blk34=2593,block=2588,bne=3d.20e5,bnequiv=2261.20e5,",
1212
+ "bnot=2310,bopf=1d553,bot=22a5,bottom=22a5,bowtie=22c8,boxDL=2557,boxDR=2554,boxDl=2556,boxDr=2553,bo",
1213
+ "xH=2550,boxHD=2566,boxHU=2569,boxHd=2564,boxHu=2567,boxUL=255d,boxUR=255a,boxUl=255c,boxUr=2559,boxV",
1214
+ "=2551,boxVH=256c,boxVL=2563,boxVR=2560,boxVh=256b,boxVl=2562,boxVr=255f,boxbox=29c9,boxdL=2555,boxdR",
1215
+ "=2552,boxdl=2510,boxdr=250c,boxh=2500,boxhD=2565,boxhU=2568,boxhd=252c,boxhu=2534,boxminus=229f,boxp",
1216
+ "lus=229e,boxtimes=22a0,boxuL=255b,boxuR=2558,boxul=2518,boxur=2514,boxv=2502,boxvH=256a,boxvL=2561,b",
1217
+ "oxvR=255e,boxvh=253c,boxvl=2524,boxvr=251c,bprime=2035,breve=2d8,brvbar=a6,bscr=1d4b7,bsemi=204f,bsi",
1218
+ "m=223d,bsime=22cd,bsol=5c,bsolb=29c5,bsolhsub=27c8,bull=2022,bullet=2022,bump=224e,bumpE=2aae,bumpe=",
1219
+ "224f,bumpeq=224f,cacute=107,cap=2229,capand=2a44,capbrcup=2a49,capcap=2a4b,capcup=2a47,capdot=2a40,c",
1220
+ "aps=2229.fe00,caret=2041,caron=2c7,ccaps=2a4d,ccaron=10d,ccedil=e7,ccirc=109,ccups=2a4c,ccupssm=2a50",
1221
+ ",cdot=10b,cedil=b8,cemptyv=29b2,cent=a2,centerdot=b7,cfr=1d520,chcy=447,check=2713,checkmark=2713,ch",
1222
+ "i=3c7,cir=25cb,cirE=29c3,circ=2c6,circeq=2257,circlearrowleft=21ba,circlearrowright=21bb,circledR=ae",
1223
+ ",circledS=24c8,circledast=229b,circledcirc=229a,circleddash=229d,cire=2257,cirfnint=2a10,cirmid=2aef",
1224
+ ",cirscir=29c2,clubs=2663,clubsuit=2663,colon=3a,colone=2254,coloneq=2254,comma=2c,commat=40,comp=220",
1225
+ "1,compfn=2218,complement=2201,complexes=2102,cong=2245,congdot=2a6d,conint=222e,copf=1d554,coprod=22",
1226
+ "10,copy=a9,copysr=2117,crarr=21b5,cross=2717,cscr=1d4b8,csub=2acf,csube=2ad1,csup=2ad0,csupe=2ad2,ct",
1227
+ "dot=22ef,cudarrl=2938,cudarrr=2935,cuepr=22de,cuesc=22df,cularr=21b6,cularrp=293d,cup=222a,cupbrcap=",
1228
+ "2a48,cupcap=2a46,cupcup=2a4a,cupdot=228d,cupor=2a45,cups=222a.fe00,curarr=21b7,curarrm=293c,curlyeqp",
1229
+ "rec=22de,curlyeqsucc=22df,curlyvee=22ce,curlywedge=22cf,curren=a4,curvearrowleft=21b6,curvearrowrigh",
1230
+ "t=21b7,cuvee=22ce,cuwed=22cf,cwconint=2232,cwint=2231,cylcty=232d,dArr=21d3,dHar=2965,dagger=2020,da",
1231
+ "leth=2138,darr=2193,dash=2010,dashv=22a3,dbkarow=290f,dblac=2dd,dcaron=10f,dcy=434,dd=2146,ddagger=2",
1232
+ "021,ddarr=21ca,ddotseq=2a77,deg=b0,delta=3b4,demptyv=29b1,dfisht=297f,dfr=1d521,dharl=21c3,dharr=21c",
1233
+ "2,diam=22c4,diamond=22c4,diamondsuit=2666,diams=2666,die=a8,digamma=3dd,disin=22f2,div=f7,divide=f7,",
1234
+ "divideontimes=22c7,divonx=22c7,djcy=452,dlcorn=231e,dlcrop=230d,dollar=24,dopf=1d555,dot=2d9,doteq=2",
1235
+ "250,doteqdot=2251,dotminus=2238,dotplus=2214,dotsquare=22a1,doublebarwedge=2306,downarrow=2193,downd",
1236
+ "ownarrows=21ca,downharpoonleft=21c3,downharpoonright=21c2,drbkarow=2910,drcorn=231f,drcrop=230c,dscr",
1237
+ "=1d4b9,dscy=455,dsol=29f6,dstrok=111,dtdot=22f1,dtri=25bf,dtrif=25be,duarr=21f5,duhar=296f,dwangle=2",
1238
+ "9a6,dzcy=45f,dzigrarr=27ff,eDDot=2a77,eDot=2251,eacute=e9,easter=2a6e,ecaron=11b,ecir=2256,ecirc=ea,",
1239
+ "ecolon=2255,ecy=44d,edot=117,ee=2147,efDot=2252,efr=1d522,eg=2a9a,egrave=e8,egs=2a96,egsdot=2a98,el=",
1240
+ "2a99,elinters=23e7,ell=2113,els=2a95,elsdot=2a97,emacr=113,empty=2205,emptyset=2205,emptyv=2205,emsp",
1241
+ "=2003,emsp13=2004,emsp14=2005,eng=14b,ensp=2002,eogon=119,eopf=1d556,epar=22d5,eparsl=29e3,eplus=2a7",
1242
+ "1,epsi=3b5,epsilon=3b5,epsiv=3f5,eqcirc=2256,eqcolon=2255,eqsim=2242,eqslantgtr=2a96,eqslantless=2a9",
1243
+ "5,equals=3d,equest=225f,equiv=2261,equivDD=2a78,eqvparsl=29e5,erDot=2253,erarr=2971,escr=212f,esdot=",
1244
+ "2250,esim=2242,eta=3b7,eth=f0,euml=eb,euro=20ac,excl=21,exist=2203,expectation=2130,exponentiale=214",
1245
+ "7,fallingdotseq=2252,fcy=444,female=2640,ffilig=fb03,fflig=fb00,ffllig=fb04,ffr=1d523,filig=fb01,fjl",
1246
+ "ig=66.6a,flat=266d,fllig=fb02,fltns=25b1,fnof=192,fopf=1d557,forall=2200,fork=22d4,forkv=2ad9,fparti",
1247
+ "nt=2a0d,frac12=bd,frac13=2153,frac14=bc,frac15=2155,frac16=2159,frac18=215b,frac23=2154,frac25=2156,",
1248
+ "frac34=be,frac35=2157,frac38=215c,frac45=2158,frac56=215a,frac58=215d,frac78=215e,frasl=2044,frown=2",
1249
+ "322,fscr=1d4bb,gE=2267,gEl=2a8c,gacute=1f5,gamma=3b3,gammad=3dd,gap=2a86,gbreve=11f,gcirc=11d,gcy=43",
1250
+ "3,gdot=121,ge=2265,gel=22db,geq=2265,geqq=2267,geqslant=2a7e,ges=2a7e,gescc=2aa9,gesdot=2a80,gesdoto",
1251
+ "=2a82,gesdotol=2a84,gesl=22db.fe00,gesles=2a94,gfr=1d524,gg=226b,ggg=22d9,gimel=2137,gjcy=453,gl=227",
1252
+ "7,glE=2a92,gla=2aa5,glj=2aa4,gnE=2269,gnap=2a8a,gnapprox=2a8a,gne=2a88,gneq=2a88,gneqq=2269,gnsim=22",
1253
+ "e7,gopf=1d558,grave=60,gscr=210a,gsim=2273,gsime=2a8e,gsiml=2a90,gt=3e,gtcc=2aa7,gtcir=2a7a,gtdot=22",
1254
+ "d7,gtlPar=2995,gtquest=2a7c,gtrapprox=2a86,gtrarr=2978,gtrdot=22d7,gtreqless=22db,gtreqqless=2a8c,gt",
1255
+ "rless=2277,gtrsim=2273,gvertneqq=2269.fe00,gvnE=2269.fe00,hArr=21d4,hairsp=200a,half=bd,hamilt=210b,",
1256
+ "hardcy=44a,harr=2194,harrcir=2948,harrw=21ad,hbar=210f,hcirc=125,hearts=2665,heartsuit=2665,hellip=2",
1257
+ "026,hercon=22b9,hfr=1d525,hksearow=2925,hkswarow=2926,hoarr=21ff,homtht=223b,hookleftarrow=21a9,hook",
1258
+ "rightarrow=21aa,hopf=1d559,horbar=2015,hscr=1d4bd,hslash=210f,hstrok=127,hybull=2043,hyphen=2010,iac",
1259
+ "ute=ed,ic=2063,icirc=ee,icy=438,iecy=435,iexcl=a1,iff=21d4,ifr=1d526,igrave=ec,ii=2148,iiiint=2a0c,i",
1260
+ "iint=222d,iinfin=29dc,iiota=2129,ijlig=133,imacr=12b,image=2111,imagline=2110,imagpart=2111,imath=13",
1261
+ "1,imof=22b7,imped=1b5,in=2208,incare=2105,infin=221e,infintie=29dd,inodot=131,int=222b,intcal=22ba,i",
1262
+ "ntegers=2124,intercal=22ba,intlarhk=2a17,intprod=2a3c,iocy=451,iogon=12f,iopf=1d55a,iota=3b9,iprod=2",
1263
+ "a3c,iquest=bf,iscr=1d4be,isin=2208,isinE=22f9,isindot=22f5,isins=22f4,isinsv=22f3,isinv=2208,it=2062",
1264
+ ",itilde=129,iukcy=456,iuml=ef,jcirc=135,jcy=439,jfr=1d527,jmath=237,jopf=1d55b,jscr=1d4bf,jsercy=458",
1265
+ ",jukcy=454,kappa=3ba,kappav=3f0,kcedil=137,kcy=43a,kfr=1d528,kgreen=138,khcy=445,kjcy=45c,kopf=1d55c",
1266
+ ",kscr=1d4c0,lAarr=21da,lArr=21d0,lAtail=291b,lBarr=290e,lE=2266,lEg=2a8b,lHar=2962,lacute=13a,laempt",
1267
+ "yv=29b4,lagran=2112,lambda=3bb,lang=2329,langd=2991,langle=27e8,lap=2a85,laquo=ab,larr=2190,larrb=21",
1268
+ "e4,larrbfs=291f,larrfs=291d,larrhk=21a9,larrlp=21ab,larrpl=2939,larrsim=2973,larrtl=21a2,lat=2aab,la",
1269
+ "tail=2919,late=2aad,lates=2aad.fe00,lbarr=290c,lbbrk=2772,lbrace=7b,lbrack=5b,lbrke=298b,lbrksld=298",
1270
+ "f,lbrkslu=298d,lcaron=13e,lcedil=13c,lceil=2308,lcub=7b,lcy=43b,ldca=2936,ldquo=201c,ldquor=201e,ldr",
1271
+ "dhar=2967,ldrushar=294b,ldsh=21b2,le=2264,leftarrow=2190,leftarrowtail=21a2,leftharpoondown=21bd,lef",
1272
+ "tharpoonup=21bc,leftleftarrows=21c7,leftrightarrow=2194,leftrightarrows=21c6,leftrightharpoons=21cb,",
1273
+ "leftrightsquigarrow=21ad,leftthreetimes=22cb,leg=22da,leq=2264,leqq=2266,leqslant=2a7d,les=2a7d,lesc",
1274
+ "c=2aa8,lesdot=2a7f,lesdoto=2a81,lesdotor=2a83,lesg=22da.fe00,lesges=2a93,lessapprox=2a85,lessdot=22d",
1275
+ "6,lesseqgtr=22da,lesseqqgtr=2a8b,lessgtr=2276,lesssim=2272,lfisht=297c,lfloor=230a,lfr=1d529,lg=2276",
1276
+ ",lgE=2a91,lhard=21bd,lharu=21bc,lharul=296a,lhblk=2584,ljcy=459,ll=226a,llarr=21c7,llcorner=231e,llh",
1277
+ "ard=296b,lltri=25fa,lmidot=140,lmoust=23b0,lmoustache=23b0,lnE=2268,lnap=2a89,lnapprox=2a89,lne=2a87",
1278
+ ",lneq=2a87,lneqq=2268,lnsim=22e6,loang=27ec,loarr=21fd,lobrk=27e6,longleftarrow=27f5,longleftrightar",
1279
+ "row=27f7,longmapsto=27fc,longrightarrow=27f6,looparrowleft=21ab,looparrowright=21ac,lopar=2985,lopf=",
1280
+ "1d55d,loplus=2a2d,lotimes=2a34,lowast=2217,lowbar=5f,loz=25ca,lozenge=25ca,lozf=29eb,lpar=28,lparlt=",
1281
+ "2993,lrarr=21c6,lrcorner=231f,lrhar=21cb,lrhard=296d,lrm=200e,lrtri=22bf,lsaquo=2039,lscr=1d4c1,lsh=",
1282
+ "21b0,lsim=2272,lsime=2a8d,lsimg=2a8f,lsqb=5b,lsquo=2018,lsquor=201a,lstrok=142,lt=3c,ltcc=2aa6,ltcir",
1283
+ "=2a79,ltdot=22d6,lthree=22cb,ltimes=22c9,ltlarr=2976,ltquest=2a7b,ltrPar=2996,ltri=25c3,ltrie=22b4,l",
1284
+ "trif=25c2,lurdshar=294a,luruhar=2966,lvertneqq=2268.fe00,lvnE=2268.fe00,mDDot=223a,macr=af,male=2642",
1285
+ ",malt=2720,maltese=2720,map=21a6,mapsto=21a6,mapstodown=21a7,mapstoleft=21a4,mapstoup=21a5,marker=25",
1286
+ "ae,mcomma=2a29,mcy=43c,mdash=2014,measuredangle=2221,mfr=1d52a,mho=2127,micro=b5,mid=2223,midast=2a,",
1287
+ "midcir=2af0,middot=b7,minus=2212,minusb=229f,minusd=2238,minusdu=2a2a,mlcp=2adb,mldr=2026,mnplus=221",
1288
+ "3,models=22a7,mopf=1d55e,mp=2213,mscr=1d4c2,mstpos=223e,mu=3bc,multimap=22b8,mumap=22b8,nGg=22d9.338",
1289
+ ",nGt=226b.20d2,nGtv=226b.338,nLeftarrow=21cd,nLeftrightarrow=21ce,nLl=22d8.338,nLt=226a.20d2,nLtv=22",
1290
+ "6a.338,nRightarrow=21cf,nVDash=22af,nVdash=22ae,nabla=2207,nacute=144,nang=2220.20d2,nap=2249,napE=2",
1291
+ "a70.338,napid=224b.338,napos=149,napprox=2249,natur=266e,natural=266e,naturals=2115,nbsp=a0,nbump=22",
1292
+ "4e.338,nbumpe=224f.338,ncap=2a43,ncaron=148,ncedil=146,ncong=2247,ncongdot=2a6d.338,ncup=2a42,ncy=43",
1293
+ "d,ndash=2013,ne=2260,neArr=21d7,nearhk=2924,nearr=2197,nearrow=2197,nedot=2250.338,nequiv=2262,nesea",
1294
+ "r=2928,nesim=2242.338,nexist=2204,nexists=2204,nfr=1d52b,ngE=2267.338,nge=2271,ngeq=2271,ngeqq=2267.",
1295
+ "338,ngeqslant=2a7e.338,nges=2a7e.338,ngsim=2275,ngt=226f,ngtr=226f,nhArr=21ce,nharr=21ae,nhpar=2af2,",
1296
+ "ni=220b,nis=22fc,nisd=22fa,niv=220b,njcy=45a,nlArr=21cd,nlE=2266.338,nlarr=219a,nldr=2025,nle=2270,n",
1297
+ "leftarrow=219a,nleftrightarrow=21ae,nleq=2270,nleqq=2266.338,nleqslant=2a7d.338,nles=2a7d.338,nless=",
1298
+ "226e,nlsim=2274,nlt=226e,nltri=22ea,nltrie=22ec,nmid=2224,nopf=1d55f,not=ac,notin=2209,notinE=22f9.3",
1299
+ "38,notindot=22f5.338,notinva=2209,notinvb=22f7,notinvc=22f6,notni=220c,notniva=220c,notnivb=22fe,not",
1300
+ "nivc=22fd,npar=2226,nparallel=2226,nparsl=2afd.20e5,npart=2202.338,npolint=2a14,npr=2280,nprcue=22e0",
1301
+ ",npre=2aaf.338,nprec=2280,npreceq=2aaf.338,nrArr=21cf,nrarr=219b,nrarrc=2933.338,nrarrw=219d.338,nri",
1302
+ "ghtarrow=219b,nrtri=22eb,nrtrie=22ed,nsc=2281,nsccue=22e1,nsce=2ab0.338,nscr=1d4c3,nshortmid=2224,ns",
1303
+ "hortparallel=2226,nsim=2241,nsime=2244,nsimeq=2244,nsmid=2224,nspar=2226,nsqsube=22e2,nsqsupe=22e3,n",
1304
+ "sub=2284,nsubE=2ac5.338,nsube=2288,nsubset=2282.20d2,nsubseteq=2288,nsubseteqq=2ac5.338,nsucc=2281,n",
1305
+ "succeq=2ab0.338,nsup=2285,nsupE=2ac6.338,nsupe=2289,nsupset=2283.20d2,nsupseteq=2289,nsupseteqq=2ac6",
1306
+ ".338,ntgl=2279,ntilde=f1,ntlg=2278,ntriangleleft=22ea,ntrianglelefteq=22ec,ntriangleright=22eb,ntria",
1307
+ "nglerighteq=22ed,nu=3bd,num=23,numero=2116,numsp=2007,nvDash=22ad,nvHarr=2904,nvap=224d.20d2,nvdash=",
1308
+ "22ac,nvge=2265.20d2,nvgt=3e.20d2,nvinfin=29de,nvlArr=2902,nvle=2264.20d2,nvlt=3c.20d2,nvltrie=22b4.2",
1309
+ "0d2,nvrArr=2903,nvrtrie=22b5.20d2,nvsim=223c.20d2,nwArr=21d6,nwarhk=2923,nwarr=2196,nwarrow=2196,nwn",
1310
+ "ear=2927,oS=24c8,oacute=f3,oast=229b,ocir=229a,ocirc=f4,ocy=43e,odash=229d,odblac=151,odiv=2a38,odot",
1311
+ "=2299,odsold=29bc,oelig=153,ofcir=29bf,ofr=1d52c,ogon=2db,ograve=f2,ogt=29c1,ohbar=29b5,ohm=3a9,oint",
1312
+ "=222e,olarr=21ba,olcir=29be,olcross=29bb,oline=203e,olt=29c0,omacr=14d,omega=3c9,omicron=3bf,omid=29",
1313
+ "b6,ominus=2296,oopf=1d560,opar=29b7,operp=29b9,oplus=2295,or=2228,orarr=21bb,ord=2a5d,order=2134,ord",
1314
+ "erof=2134,ordf=aa,ordm=ba,origof=22b6,oror=2a56,orslope=2a57,orv=2a5b,oscr=2134,oslash=f8,osol=2298,",
1315
+ "otilde=f5,otimes=2297,otimesas=2a36,ouml=f6,ovbar=233d,par=2225,para=b6,parallel=2225,parsim=2af3,pa",
1316
+ "rsl=2afd,part=2202,pcy=43f,percnt=25,period=2e,permil=2030,perp=22a5,pertenk=2031,pfr=1d52d,phi=3c6,",
1317
+ "phiv=3d5,phmmat=2133,phone=260e,pi=3c0,pitchfork=22d4,piv=3d6,planck=210f,planckh=210e,plankv=210f,p",
1318
+ "lus=2b,plusacir=2a23,plusb=229e,pluscir=2a22,plusdo=2214,plusdu=2a25,pluse=2a72,plusmn=b1,plussim=2a",
1319
+ "26,plustwo=2a27,pm=b1,pointint=2a15,popf=1d561,pound=a3,pr=227a,prE=2ab3,prap=2ab7,prcue=227c,pre=2a",
1320
+ "af,prec=227a,precapprox=2ab7,preccurlyeq=227c,preceq=2aaf,precnapprox=2ab9,precneqq=2ab5,precnsim=22",
1321
+ "e8,precsim=227e,prime=2032,primes=2119,prnE=2ab5,prnap=2ab9,prnsim=22e8,prod=220f,profalar=232e,prof",
1322
+ "line=2312,profsurf=2313,prop=221d,propto=221d,prsim=227e,prurel=22b0,pscr=1d4c5,psi=3c8,puncsp=2008,",
1323
+ "qfr=1d52e,qint=2a0c,qopf=1d562,qprime=2057,qscr=1d4c6,quaternions=210d,quatint=2a16,quest=3f,questeq",
1324
+ "=225f,quot=22,rAarr=21db,rArr=21d2,rAtail=291c,rBarr=290f,rHar=2964,race=223d.331,racute=155,radic=2",
1325
+ "21a,raemptyv=29b3,rang=232a,rangd=2992,range=29a5,rangle=27e9,raquo=bb,rarr=2192,rarrap=2975,rarrb=2",
1326
+ "1e5,rarrbfs=2920,rarrc=2933,rarrfs=291e,rarrhk=21aa,rarrlp=21ac,rarrpl=2945,rarrsim=2974,rarrtl=21a3",
1327
+ ",rarrw=219d,ratail=291a,ratio=2236,rationals=211a,rbarr=290d,rbbrk=2773,rbrace=7d,rbrack=5d,rbrke=29",
1328
+ "8c,rbrksld=298e,rbrkslu=2990,rcaron=159,rcedil=157,rceil=2309,rcub=7d,rcy=440,rdca=2937,rdldhar=2969",
1329
+ ",rdquo=201d,rdquor=201d,rdsh=21b3,real=211c,realine=211b,realpart=211c,reals=211d,rect=25ad,reg=ae,r",
1330
+ "fisht=297d,rfloor=230b,rfr=1d52f,rhard=21c1,rharu=21c0,rharul=296c,rho=3c1,rhov=3f1,rightarrow=2192,",
1331
+ "rightarrowtail=21a3,rightharpoondown=21c1,rightharpoonup=21c0,rightleftarrows=21c4,rightleftharpoons",
1332
+ "=21cc,rightrightarrows=21c9,rightsquigarrow=219d,rightthreetimes=22cc,ring=2da,risingdotseq=2253,rla",
1333
+ "rr=21c4,rlhar=21cc,rlm=200f,rmoust=23b1,rmoustache=23b1,rnmid=2aee,roang=27ed,roarr=21fe,robrk=27e7,",
1334
+ "ropar=2986,ropf=1d563,roplus=2a2e,rotimes=2a35,rpar=29,rpargt=2994,rppolint=2a12,rrarr=21c9,rsaquo=2",
1335
+ "03a,rscr=1d4c7,rsh=21b1,rsqb=5d,rsquo=2019,rsquor=2019,rthree=22cc,rtimes=22ca,rtri=25b9,rtrie=22b5,",
1336
+ "rtrif=25b8,rtriltri=29ce,ruluhar=2968,rx=211e,sacute=15b,sbquo=201a,sc=227b,scE=2ab4,scap=2ab8,scaro",
1337
+ "n=161,sccue=227d,sce=2ab0,scedil=15f,scirc=15d,scnE=2ab6,scnap=2aba,scnsim=22e9,scpolint=2a13,scsim=",
1338
+ "227f,scy=441,sdot=22c5,sdotb=22a1,sdote=2a66,seArr=21d8,searhk=2925,searr=2198,searrow=2198,sect=a7,",
1339
+ "semi=3b,seswar=2929,setminus=2216,setmn=2216,sext=2736,sfr=1d530,sfrown=2322,sharp=266f,shchcy=449,s",
1340
+ "hcy=448,shortmid=2223,shortparallel=2225,shy=ad,sigma=3c3,sigmaf=3c2,sigmav=3c2,sim=223c,simdot=2a6a",
1341
+ ",sime=2243,simeq=2243,simg=2a9e,simgE=2aa0,siml=2a9d,simlE=2a9f,simne=2246,simplus=2a24,simrarr=2972",
1342
+ ",slarr=2190,smallsetminus=2216,smashp=2a33,smeparsl=29e4,smid=2223,smile=2323,smt=2aaa,smte=2aac,smt",
1343
+ "es=2aac.fe00,softcy=44c,sol=2f,solb=29c4,solbar=233f,sopf=1d564,spades=2660,spadesuit=2660,spar=2225",
1344
+ ",sqcap=2293,sqcaps=2293.fe00,sqcup=2294,sqcups=2294.fe00,sqsub=228f,sqsube=2291,sqsubset=228f,sqsubs",
1345
+ "eteq=2291,sqsup=2290,sqsupe=2292,sqsupset=2290,sqsupseteq=2292,squ=25a1,square=25a1,squarf=25aa,squf",
1346
+ "=25aa,srarr=2192,sscr=1d4c8,ssetmn=2216,ssmile=2323,sstarf=22c6,star=2606,starf=2605,straightepsilon",
1347
+ "=3f5,straightphi=3d5,strns=af,sub=2282,subE=2ac5,subdot=2abd,sube=2286,subedot=2ac3,submult=2ac1,sub",
1348
+ "nE=2acb,subne=228a,subplus=2abf,subrarr=2979,subset=2282,subseteq=2286,subseteqq=2ac5,subsetneq=228a",
1349
+ ",subsetneqq=2acb,subsim=2ac7,subsub=2ad5,subsup=2ad3,succ=227b,succapprox=2ab8,succcurlyeq=227d,succ",
1350
+ "eq=2ab0,succnapprox=2aba,succneqq=2ab6,succnsim=22e9,succsim=227f,sum=2211,sung=266a,sup=2283,sup1=b",
1351
+ "9,sup2=b2,sup3=b3,supE=2ac6,supdot=2abe,supdsub=2ad8,supe=2287,supedot=2ac4,suphsol=27c9,suphsub=2ad",
1352
+ "7,suplarr=297b,supmult=2ac2,supnE=2acc,supne=228b,supplus=2ac0,supset=2283,supseteq=2287,supseteqq=2",
1353
+ "ac6,supsetneq=228b,supsetneqq=2acc,supsim=2ac8,supsub=2ad4,supsup=2ad6,swArr=21d9,swarhk=2926,swarr=",
1354
+ "2199,swarrow=2199,swnwar=292a,szlig=df,target=2316,tau=3c4,tbrk=23b4,tcaron=165,tcedil=163,tcy=442,t",
1355
+ "dot=20db,telrec=2315,tfr=1d531,there4=2234,therefore=2234,theta=3b8,thetasym=3d1,thetav=3d1,thickapp",
1356
+ "rox=2248,thicksim=223c,thinsp=2009,thkap=2248,thksim=223c,thorn=fe,tilde=2dc,times=d7,timesb=22a0,ti",
1357
+ "mesbar=2a31,timesd=2a30,tint=222d,toea=2928,top=22a4,topbot=2336,topcir=2af1,topf=1d565,topfork=2ada",
1358
+ ",tosa=2929,tprime=2034,trade=2122,triangle=25b5,triangledown=25bf,triangleleft=25c3,trianglelefteq=2",
1359
+ "2b4,triangleq=225c,triangleright=25b9,trianglerighteq=22b5,tridot=25ec,trie=225c,triminus=2a3a,tripl",
1360
+ "us=2a39,trisb=29cd,tritime=2a3b,trpezium=23e2,tscr=1d4c9,tscy=446,tshcy=45b,tstrok=167,twixt=226c,tw",
1361
+ "oheadleftarrow=219e,twoheadrightarrow=21a0,uArr=21d1,uHar=2963,uacute=fa,uarr=2191,ubrcy=45e,ubreve=",
1362
+ "16d,ucirc=fb,ucy=443,udarr=21c5,udblac=171,udhar=296e,ufisht=297e,ufr=1d532,ugrave=f9,uharl=21bf,uha",
1363
+ "rr=21be,uhblk=2580,ulcorn=231c,ulcorner=231c,ulcrop=230f,ultri=25f8,umacr=16b,uml=a8,uogon=173,uopf=",
1364
+ "1d566,uparrow=2191,updownarrow=2195,upharpoonleft=21bf,upharpoonright=21be,uplus=228e,upsi=3c5,upsih",
1365
+ "=3d2,upsilon=3c5,upuparrows=21c8,urcorn=231d,urcorner=231d,urcrop=230e,uring=16f,urtri=25f9,uscr=1d4",
1366
+ "ca,utdot=22f0,utilde=169,utri=25b5,utrif=25b4,uuarr=21c8,uuml=fc,uwangle=29a7,vArr=21d5,vBar=2ae8,vB",
1367
+ "arv=2ae9,vDash=22a8,vangrt=299c,varepsilon=3f5,varkappa=3f0,varnothing=2205,varphi=3d5,varpi=3d6,var",
1368
+ "propto=221d,varr=2195,varrho=3f1,varsigma=3c2,varsubsetneq=228a.fe00,varsubsetneqq=2acb.fe00,varsups",
1369
+ "etneq=228b.fe00,varsupsetneqq=2acc.fe00,vartheta=3d1,vartriangleleft=22b2,vartriangleright=22b3,vcy=",
1370
+ "432,vdash=22a2,vee=2228,veebar=22bb,veeeq=225a,vellip=22ee,verbar=7c,vert=7c,vfr=1d533,vltri=22b2,vn",
1371
+ "sub=2282.20d2,vnsup=2283.20d2,vopf=1d567,vprop=221d,vrtri=22b3,vscr=1d4cb,vsubnE=2acb.fe00,vsubne=22",
1372
+ "8a.fe00,vsupnE=2acc.fe00,vsupne=228b.fe00,vzigzag=299a,wcirc=175,wedbar=2a5f,wedge=2227,wedgeq=2259,",
1373
+ "weierp=2118,wfr=1d534,wopf=1d568,wp=2118,wr=2240,wreath=2240,wscr=1d4cc,xcap=22c2,xcirc=25ef,xcup=22",
1374
+ "c3,xdtri=25bd,xfr=1d535,xhArr=27fa,xharr=27f7,xi=3be,xlArr=27f8,xlarr=27f5,xmap=27fc,xnis=22fb,xodot",
1375
+ "=2a00,xopf=1d569,xoplus=2a01,xotime=2a02,xrArr=27f9,xrarr=27f6,xscr=1d4cd,xsqcup=2a06,xuplus=2a04,xu",
1376
+ "tri=25b3,xvee=22c1,xwedge=22c0,yacute=fd,yacy=44f,ycirc=177,ycy=44b,yen=a5,yfr=1d536,yicy=457,yopf=1",
1377
+ "d56a,yscr=1d4ce,yucy=44e,yuml=ff,zacute=17a,zcaron=17e,zcy=437,zdot=17c,zeetrf=2128,zeta=3b6,zfr=1d5",
1378
+ "37,zhcy=436,zigrarr=21dd,zopf=1d56b,zscr=1d4cf,zwj=200d,zwnj=200c"
1379
+ ].join("");
1380
+ var NAMED_REFERENCES = new Map(NAMED.split(",").map((entry) => {
1381
+ const [name, codes] = entry.split("=");
1382
+ return [name, String.fromCodePoint(...codes.split(".").map((code) => Number.parseInt(code, 16)))];
1383
+ }));
1384
+
1110
1385
  // src/html.js
1111
1386
  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(" ");
1112
- var ENTITIES = new Map([
1113
- ...LATIN1.map((name, index) => [name, String.fromCodePoint(160 + index)]),
1114
- ["amp", "&"],
1115
- ["lt", "<"],
1116
- ["gt", ">"],
1117
- ["quot", '"'],
1118
- ["apos", "'"],
1119
- ["AMP", "&"],
1120
- ["LT", "<"],
1121
- ["GT", ">"],
1122
- ["QUOT", '"'],
1123
- ["COPY", "\xA9"],
1124
- ["REG", "\xAE"],
1125
- ["OElig", "\u0152"],
1126
- ["oelig", "\u0153"],
1127
- ["Scaron", "\u0160"],
1128
- ["scaron", "\u0161"],
1129
- ["Yuml", "\u0178"],
1130
- ["fnof", "\u0192"],
1131
- ["circ", "\u02C6"],
1132
- ["tilde", "\u02DC"],
1133
- ["ensp", "\u2002"],
1134
- ["emsp", "\u2003"],
1135
- ["thinsp", "\u2009"],
1136
- ["zwnj", "\u200C"],
1137
- ["zwj", "\u200D"],
1138
- ["lrm", "\u200E"],
1139
- ["rlm", "\u200F"],
1140
- ["ndash", "\u2013"],
1141
- ["mdash", "\u2014"],
1142
- ["lsquo", "\u2018"],
1143
- ["rsquo", "\u2019"],
1144
- ["sbquo", "\u201A"],
1145
- ["ldquo", "\u201C"],
1146
- ["rdquo", "\u201D"],
1147
- ["bdquo", "\u201E"],
1148
- ["dagger", "\u2020"],
1149
- ["Dagger", "\u2021"],
1150
- ["bull", "\u2022"],
1151
- ["hellip", "\u2026"],
1152
- ["permil", "\u2030"],
1153
- ["prime", "\u2032"],
1154
- ["Prime", "\u2033"],
1155
- ["lsaquo", "\u2039"],
1156
- ["rsaquo", "\u203A"],
1157
- ["euro", "\u20AC"],
1158
- ["trade", "\u2122"],
1159
- ["larr", "\u2190"],
1160
- ["uarr", "\u2191"],
1161
- ["rarr", "\u2192"],
1162
- ["darr", "\u2193"],
1163
- ["hearts", "\u2665"],
1164
- ["check", "\u2713"],
1165
- // The punctuation of a web address, as browsers read these names
1166
- // ("https://bank.example&commat;evil.example/" goes to evil.example)
1167
- ["Tab", " "],
1168
- ["NewLine", "\n"],
1169
- ["excl", "!"],
1170
- ["num", "#"],
1171
- ["dollar", "$"],
1172
- ["percnt", "%"],
1173
- ["lpar", "("],
1174
- ["rpar", ")"],
1175
- ["ast", "*"],
1176
- ["midast", "*"],
1177
- ["plus", "+"],
1178
- ["comma", ","],
1179
- ["period", "."],
1180
- ["sol", "/"],
1181
- ["colon", ":"],
1182
- ["semi", ";"],
1183
- ["equals", "="],
1184
- ["quest", "?"],
1185
- ["commat", "@"],
1186
- ["lsqb", "["],
1187
- ["lbrack", "["],
1188
- ["bsol", "\\"],
1189
- ["rsqb", "]"],
1190
- ["rbrack", "]"],
1191
- ["Hat", "^"],
1192
- ["lowbar", "_"],
1193
- ["UnderBar", "_"],
1194
- ["grave", "`"],
1195
- ["DiacriticalGrave", "`"],
1196
- ["lcub", "{"],
1197
- ["lbrace", "{"],
1198
- ["verbar", "|"],
1199
- ["vert", "|"],
1200
- ["VerticalLine", "|"],
1201
- ["rcub", "}"],
1202
- ["rbrace", "}"]
1203
- ]);
1204
1387
  var LEGACY = /* @__PURE__ */ new Set([...LATIN1, "amp", "lt", "gt", "quot", "AMP", "LT", "GT", "QUOT", "COPY", "REG"]);
1205
1388
  var LEGACY_LONGEST = Math.max(...[...LEGACY].map((name) => name.length));
1206
- 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(" "));
1207
1389
  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];
1208
1390
  var REFERENCE = /&(?:#([xX])([\da-fA-F]+)|#(\d+)|([a-zA-Z][a-zA-Z\d]{0,31}))(;?)/g;
1209
1391
  function fromCode(code) {
@@ -1221,8 +1403,8 @@ function decodeEntities(value, { attribute = false } = {}) {
1221
1403
  if (hex !== void 0 || decimal !== void 0) {
1222
1404
  return fromCode(hex === void 0 ? Number.parseInt(decimal, 10) : Number.parseInt(hex, 16));
1223
1405
  }
1224
- if (semicolon && (ENTITIES.has(name) || LONGER_NAMES.has(name))) {
1225
- return ENTITIES.get(name) ?? match;
1406
+ if (semicolon && NAMED_REFERENCES.has(name)) {
1407
+ return NAMED_REFERENCES.get(name);
1226
1408
  }
1227
1409
  let legacy;
1228
1410
  for (let length = Math.min(name.length, LEGACY_LONGEST); length >= 2 && legacy === void 0; length--) {
@@ -1236,7 +1418,7 @@ function decodeEntities(value, { attribute = false } = {}) {
1236
1418
  if (attribute && /[\da-zA-Z=]/.test(next)) {
1237
1419
  return match;
1238
1420
  }
1239
- return ENTITIES.get(legacy) + rest;
1421
+ return NAMED_REFERENCES.get(legacy) + rest;
1240
1422
  });
1241
1423
  }
1242
1424
  var UNSHOWN = /* @__PURE__ */ new Set(["script", "style", "title", "noembed", "noframes", "iframe"]);
@@ -2382,7 +2564,7 @@ function endsInsideTag(html, end, link) {
2382
2564
  return joined !== host && (!isHost(host) || isHost(joined) && registrableDomain(joined) !== registrableDomain(host));
2383
2565
  }
2384
2566
  var MAX_SHOWN_HTML = 2e5;
2385
- function extractLinks(text, html, { maxLength = 5e5, bareHosts = false } = {}) {
2567
+ function extractLinks(text, html, { maxLength = 5e5, bareHosts: bareHosts2 = false } = {}) {
2386
2568
  text = typeof text === "string" ? foldToAscii(text) : text;
2387
2569
  html = typeof html === "string" ? foldToAscii(html) : html;
2388
2570
  const links = [];
@@ -2449,7 +2631,7 @@ ${shown || ""}`;
2449
2631
  }
2450
2632
  add(link);
2451
2633
  }
2452
- if (bareHosts && index < 2) {
2634
+ if (bareHosts2 && index < 2) {
2453
2635
  bareLinkHosts(read, (host) => {
2454
2636
  bareFound.push(host);
2455
2637
  });
@@ -3971,7 +4153,8 @@ async function parseWithin(raw, parse = parseMessage, deadline = Date.now() + PA
3971
4153
  }
3972
4154
  }
3973
4155
  }
3974
- async function expandAttachments(attachments = [], errors = [], parse = parseMessage, unopened = []) {
4156
+ async function expandAttachments(attachments = [], errors = [], parse = parseMessage, gets = {}) {
4157
+ const { unopened = [], mails = [] } = Array.isArray(gets) ? { unopened: gets } : gets;
3975
4158
  const all = [];
3976
4159
  const deadline = Date.now() + PARSE_TIMEOUT;
3977
4160
  let budget = MAX_NESTED_BYTES;
@@ -4020,6 +4203,7 @@ async function expandAttachments(attachments = [], errors = [], parse = parseMes
4020
4203
  if (inner.error) {
4021
4204
  errors.push(inner.error);
4022
4205
  }
4206
+ mails.push(inner.mail);
4023
4207
  await visit(inner.mail.attachments, prefix ? `${prefix}/${name}` : name || "attached message", depth + 1);
4024
4208
  }
4025
4209
  }
@@ -5436,8 +5620,917 @@ var Classifier = class _Classifier {
5436
5620
  }
5437
5621
  };
5438
5622
 
5439
- // src/is-arbitrary.js
5623
+ // src/links.js
5624
+ import { Buffer as Buffer7 } from "node:buffer";
5625
+ import { isIP as isIP3 } from "node:net";
5440
5626
  import { parse as parseDomain3 } from "tldts";
5627
+ var LINK_ATTRIBUTES = /* @__PURE__ */ new Set([
5628
+ "href",
5629
+ "src",
5630
+ "srcset",
5631
+ "background",
5632
+ "action",
5633
+ "formaction",
5634
+ "poster",
5635
+ "cite",
5636
+ "longdesc",
5637
+ "xlink:href",
5638
+ "style",
5639
+ "data",
5640
+ "ping",
5641
+ // (of a <meta> refresh: "0;url=https://example.com/")
5642
+ "content",
5643
+ // (a document of its own, see linksOf)
5644
+ "srcdoc"
5645
+ ]);
5646
+ var TAG_START = /<[a-z][^\s/>]*/gi;
5647
+ var URL_START = /\b(?:https?:\/\/|www\.)[^\s<>"'`]+/gi;
5648
+ var CSS_URL = /url\(\s*["']?([^()"']*)/gi;
5649
+ var CSS_IMPORT = /@import\s+["']([^"'<>]*)/gi;
5650
+ var LENIENT = /[\s"'/<](href|src|srcset|background|action|formaction|poster|cite|longdesc|xlink:href|style|data|ping|content|srcdoc)\s*=\s*/gi;
5651
+ var IN_WORD2 = /[\p{L}\p{N}\p{M}_%]/u;
5652
+ var NAMESPACES = /* @__PURE__ */ new Set(["www.w3.org", "w3.org"]);
5653
+ var MAX_HOST = 760;
5654
+ var MAX_SEEN = 1e5;
5655
+ var TRAILING = /* @__PURE__ */ new Set([")", ".", ",", ";", ":", "!", "?", "'", '"', "]"]);
5656
+ var DECODE_CHUNK = 1024 * 1024;
5657
+ var CHUNK_OVERLAP = 4096;
5658
+ var MAX_EMBED_DEPTH = 3;
5659
+ var MAX_EMBEDDED = 1e3;
5660
+ var MAX_EMBEDDED_LENGTH = 10 * 1024 * 1024;
5661
+ var MAX_ATTACHED = 100;
5662
+ var MAX_ATTACHED_BYTES = 50 * 1024 * 1024;
5663
+ var MAX_READ_TIME = 5e3;
5664
+ var MAX_SHOWN_BYTES = 4 * 1024 * 1024;
5665
+ function isHostCode2(code) {
5666
+ return code >= 97 && code <= 122 || code >= 65 && code <= 90 || code >= 48 && code <= 57 || code === 45 || code === 46;
5667
+ }
5668
+ function isSpace(code) {
5669
+ return code === 32 || code >= 9 && code <= 13;
5670
+ }
5671
+ function urlOf(value) {
5672
+ let href = value.replaceAll(/[\t\n\r]/g, "");
5673
+ let start = 0;
5674
+ let end = href.length;
5675
+ while (start < end && href.codePointAt(start) <= 32) {
5676
+ start++;
5677
+ }
5678
+ while (end > start && href.codePointAt(end - 1) <= 32) {
5679
+ end--;
5680
+ }
5681
+ while (end > start && TRAILING.has(href[end - 1])) {
5682
+ end--;
5683
+ }
5684
+ href = href.slice(start, end);
5685
+ if (/^[\\/]{2}/.test(href)) {
5686
+ href = `https://${href.slice(2)}`;
5687
+ } else if (/^www\./i.test(href)) {
5688
+ href = `http://${href}`;
5689
+ } else if (!/^https?:/i.test(href)) {
5690
+ return null;
5691
+ }
5692
+ try {
5693
+ return new URL(href);
5694
+ } catch {
5695
+ return null;
5696
+ }
5697
+ }
5698
+ function hostOfUrl(url) {
5699
+ let host = url.hostname.toLowerCase();
5700
+ let dots = host.length;
5701
+ while (dots > 0 && host[dots - 1] === ".") {
5702
+ dots--;
5703
+ }
5704
+ host = host.slice(0, dots);
5705
+ if (host.startsWith("[") && host.endsWith("]")) {
5706
+ host = host.slice(1, -1);
5707
+ }
5708
+ if (!host || NAMESPACES.has(host)) {
5709
+ return null;
5710
+ }
5711
+ return isIP3(host) ? host : lookupName(host);
5712
+ }
5713
+ function hostsOf(value) {
5714
+ const url = urlOf(value);
5715
+ if (!url) {
5716
+ return [];
5717
+ }
5718
+ const host = hostOfUrl(url);
5719
+ const hosts = host ? [host] : [];
5720
+ const target = unwrapLink(url.href);
5721
+ if (target && target.href !== url.href) {
5722
+ const targetHost = hostOfUrl(target);
5723
+ if (targetHost && targetHost !== host) {
5724
+ hosts.push(targetHost);
5725
+ }
5726
+ const further = unwrapLink(target.href);
5727
+ if (further && further.href !== target.href) {
5728
+ hosts.unresolved = true;
5729
+ }
5730
+ }
5731
+ return hosts;
5732
+ }
5733
+ function* attributesOf(html) {
5734
+ TAG_START.lastIndex = 0;
5735
+ let match;
5736
+ while ((match = TAG_START.exec(html)) !== null) {
5737
+ let i = match.index + match[0].length;
5738
+ while (i < html.length && html[i] !== ">") {
5739
+ if (isSpace(html.codePointAt(i)) || html[i] === "/") {
5740
+ i++;
5741
+ continue;
5742
+ }
5743
+ let j = i;
5744
+ while (j < html.length && html[j] !== "=" && html[j] !== ">" && html[j] !== "/" && !isSpace(html.codePointAt(j))) {
5745
+ j++;
5746
+ }
5747
+ const name = html.slice(i, j).toLowerCase();
5748
+ if (j === i) {
5749
+ j++;
5750
+ }
5751
+ while (j < html.length && isSpace(html.codePointAt(j))) {
5752
+ j++;
5753
+ }
5754
+ if (html[j] !== "=") {
5755
+ i = j;
5756
+ continue;
5757
+ }
5758
+ j++;
5759
+ while (j < html.length && isSpace(html.codePointAt(j))) {
5760
+ j++;
5761
+ }
5762
+ let value;
5763
+ if (html[j] === '"' || html[j] === "'") {
5764
+ const close = html.indexOf(html[j], j + 1);
5765
+ const stop = close === -1 ? html.length : close;
5766
+ value = html.slice(j + 1, stop);
5767
+ j = stop + 1;
5768
+ } else {
5769
+ const start = j;
5770
+ while (j < html.length && html[j] !== ">" && !isSpace(html.codePointAt(j))) {
5771
+ j++;
5772
+ }
5773
+ value = html.slice(start, j);
5774
+ }
5775
+ if (name) {
5776
+ yield [name, value];
5777
+ }
5778
+ i = j;
5779
+ }
5780
+ TAG_START.lastIndex = Math.max(i, match.index + 1);
5781
+ }
5782
+ }
5783
+ function linksOf(name, value, add, embed) {
5784
+ const decoded = decodeEntities(value, { attribute: true });
5785
+ switch (name) {
5786
+ case "srcdoc": {
5787
+ embed(decoded);
5788
+ break;
5789
+ }
5790
+ case "style": {
5791
+ for (const [, link] of decoded.matchAll(CSS_URL)) {
5792
+ add(link);
5793
+ }
5794
+ break;
5795
+ }
5796
+ case "srcset": {
5797
+ for (const candidate of decoded.split(",")) {
5798
+ add(candidate.trim().split(/\s/, 1)[0]);
5799
+ }
5800
+ break;
5801
+ }
5802
+ // (several, separated by spaces)
5803
+ case "ping": {
5804
+ for (const link of decoded.split(/\s+/)) {
5805
+ add(link);
5806
+ }
5807
+ break;
5808
+ }
5809
+ // (only the address of a refresh)
5810
+ case "content": {
5811
+ const match = /(?:^|[\s;,])url\s*=\s*["']?([^\s"';]+)/i.exec(decoded.slice(0, 4096));
5812
+ if (match) {
5813
+ add(match[1]);
5814
+ }
5815
+ break;
5816
+ }
5817
+ default: {
5818
+ add(decoded);
5819
+ }
5820
+ }
5821
+ }
5822
+ var MAX_LENIENT_VALUE = 4096;
5823
+ var UNQUOTED_END = /* @__PURE__ */ new Set(["<", ">", '"', "'", "`", "="]);
5824
+ function lenientLinks(html, add, done, embed) {
5825
+ LENIENT.lastIndex = 0;
5826
+ let match;
5827
+ while (!done() && (match = LENIENT.exec(html)) !== null) {
5828
+ const name = match[1].toLowerCase();
5829
+ let i = LENIENT.lastIndex;
5830
+ let end = i;
5831
+ if (html[i] === '"' || html[i] === "'") {
5832
+ const quote = html[i];
5833
+ i++;
5834
+ end = i;
5835
+ while (end < html.length && html[end] !== quote && html[end] !== "<" && html[end] !== ">") {
5836
+ end++;
5837
+ }
5838
+ if (end < html.length && html[end] !== quote) {
5839
+ const close = html.indexOf(quote, end);
5840
+ if (close !== -1 && close - i <= MAX_LENIENT_VALUE) {
5841
+ linksOf(name, html.slice(i, close), add, embed);
5842
+ }
5843
+ }
5844
+ } else {
5845
+ while (end < html.length && !isSpace(html.codePointAt(end)) && !UNQUOTED_END.has(html[end])) {
5846
+ end++;
5847
+ }
5848
+ }
5849
+ linksOf(name, html.slice(i, end), add, embed);
5850
+ }
5851
+ }
5852
+ function endsLink2(text, i) {
5853
+ const code = text.codePointAt(i);
5854
+ if (code < 128) {
5855
+ return isSpace(code) || code === 34 || code === 39 || code === 60 || code === 62 || code === 96;
5856
+ }
5857
+ return /\s/u.test(text[i]);
5858
+ }
5859
+ function linkReader(text) {
5860
+ let read = 0;
5861
+ let space = -1;
5862
+ let scheme = -1;
5863
+ return (start) => {
5864
+ for (; read < start; read++) {
5865
+ if (endsLink2(text, read)) {
5866
+ space = read;
5867
+ } else if (text[read] === "/" && text[read - 1] === "/" && text[read - 2] === ":") {
5868
+ scheme = read;
5869
+ }
5870
+ }
5871
+ return scheme > space;
5872
+ };
5873
+ }
5874
+ function isSchemeless(text, start) {
5875
+ if (start < 2 || text[start - 2] !== "/") {
5876
+ return false;
5877
+ }
5878
+ if (start === 2) {
5879
+ return true;
5880
+ }
5881
+ const before = text[start - 3];
5882
+ return !(before === ":" || before === "/" || isHostCode2(text.codePointAt(start - 3)));
5883
+ }
5884
+ function bareName(name) {
5885
+ const labels = name.split(".");
5886
+ const tld = labels.pop();
5887
+ if (!/^(?:[a-z]{2,24}|xn--[a-z\d-]{1,59})$/i.test(tld)) {
5888
+ return null;
5889
+ }
5890
+ const names = [];
5891
+ while (labels.length > 0 && names.length < 11) {
5892
+ const label = labels.pop();
5893
+ if (!/^[a-z\d-]{1,63}$/i.test(label)) {
5894
+ break;
5895
+ }
5896
+ names.unshift(label);
5897
+ }
5898
+ if (names.length === 0) {
5899
+ return null;
5900
+ }
5901
+ names[0] = names[0].replace(/^-+/, "");
5902
+ if (!names[0]) {
5903
+ return null;
5904
+ }
5905
+ return `${names.join(".")}.${tld}`.toLowerCase();
5906
+ }
5907
+ function isRealName(host) {
5908
+ const parsed = parseDomain3(host);
5909
+ return Boolean(parsed.isIcann && parsed.domain);
5910
+ }
5911
+ function bareHosts(text, add, { done, seen, from = 0, cutEnd = false }) {
5912
+ let previous = -1;
5913
+ const inLink = linkReader(text);
5914
+ for (let slash = text.indexOf("/"); slash !== -1; slash = text.indexOf("/", slash + 1)) {
5915
+ if (done()) {
5916
+ return;
5917
+ }
5918
+ const floor = Math.max(previous + 1, slash - MAX_HOST);
5919
+ previous = slash;
5920
+ let end = slash;
5921
+ while (end > floor && text.codePointAt(end - 1) >= 48 && text.codePointAt(end - 1) <= 57) {
5922
+ end--;
5923
+ }
5924
+ end = end > floor && text[end - 1] === ":" ? end - 1 : slash;
5925
+ let start = end;
5926
+ while (start > floor && isHostCode2(text.codePointAt(start - 1))) {
5927
+ start--;
5928
+ }
5929
+ if (start === end || start < from || cutEnd && slash === text.length - 1) {
5930
+ continue;
5931
+ }
5932
+ if (start > 0 && (IN_WORD2.test(text[start - 1]) || text[start - 1] === "/" && !isSchemeless(text, start)) || inLink(start)) {
5933
+ continue;
5934
+ }
5935
+ while (end > start && text[end - 1] === ".") {
5936
+ end--;
5937
+ }
5938
+ const host = bareName(text.slice(start, end));
5939
+ if (!host || seen.has(host)) {
5940
+ continue;
5941
+ }
5942
+ if (seen.size < MAX_SEEN) {
5943
+ seen.add(host);
5944
+ }
5945
+ if (isRealName(host)) {
5946
+ add(`http://${host}/`);
5947
+ }
5948
+ }
5949
+ }
5950
+ function looseHosts(text, add, { from = 0, cutEnd = false, seen, done }) {
5951
+ const inLink = linkReader(text);
5952
+ const { length } = text;
5953
+ let i = 0;
5954
+ while (i < length) {
5955
+ if (!isHostCode2(text.codePointAt(i))) {
5956
+ i++;
5957
+ continue;
5958
+ }
5959
+ const start = i;
5960
+ while (i < length && isHostCode2(text.codePointAt(i))) {
5961
+ i++;
5962
+ }
5963
+ let end = i;
5964
+ if (end - start > MAX_HOST || start < from || cutEnd && end === length) {
5965
+ continue;
5966
+ }
5967
+ while (end > start && text[end - 1] === ".") {
5968
+ end--;
5969
+ }
5970
+ const before = start > 0 ? text[start - 1] : "";
5971
+ const after = end < length ? text[end] : "";
5972
+ if (after === "/" || after === "@" || before === "@") {
5973
+ continue;
5974
+ }
5975
+ if (before && (IN_WORD2.test(before) || before === "/" || before === "\\" || before === "&" || before === "#")) {
5976
+ continue;
5977
+ }
5978
+ if (after && after !== "." && IN_WORD2.test(after)) {
5979
+ continue;
5980
+ }
5981
+ const name = text.slice(start, end);
5982
+ if (!name.includes(".") || inLink(start)) {
5983
+ continue;
5984
+ }
5985
+ const lower = name.toLowerCase();
5986
+ if (seen.has(lower)) {
5987
+ continue;
5988
+ }
5989
+ if (seen.size < MAX_SEEN) {
5990
+ seen.add(lower);
5991
+ }
5992
+ if (done()) {
5993
+ return;
5994
+ }
5995
+ const host = bareName(name);
5996
+ if (host && isRealName(host)) {
5997
+ add(host);
5998
+ }
5999
+ }
6000
+ }
6001
+ function* chunksOf(html) {
6002
+ let start = 0;
6003
+ let cutStart = false;
6004
+ while (start < html.length) {
6005
+ const end = Math.min(html.length, start + DECODE_CHUNK);
6006
+ if (end < html.length) {
6007
+ let cut = end;
6008
+ while (cut > end - 65536 && cut > start) {
6009
+ if (html[cut] === "<" || isSpace(html.codePointAt(cut))) {
6010
+ break;
6011
+ }
6012
+ cut--;
6013
+ }
6014
+ if (cut > start && cut > end - 65536) {
6015
+ yield { text: html.slice(start, cut), cutStart, cutEnd: false };
6016
+ start = cut;
6017
+ cutStart = false;
6018
+ continue;
6019
+ }
6020
+ yield { text: html.slice(start, end), cutStart, cutEnd: true };
6021
+ start = end - CHUNK_OVERLAP;
6022
+ cutStart = true;
6023
+ continue;
6024
+ }
6025
+ yield { text: html.slice(start, end), cutStart, cutEnd: false };
6026
+ start = end;
6027
+ }
6028
+ }
6029
+ function withoutNul(text) {
6030
+ return text.includes("\0") ? `${text}
6031
+ ${text.replaceAll("\0", "")}` : text;
6032
+ }
6033
+ function hexValue(code) {
6034
+ if (code >= 48 && code <= 57) {
6035
+ return code - 48;
6036
+ }
6037
+ if (code >= 65 && code <= 70) {
6038
+ return code - 55;
6039
+ }
6040
+ if (code >= 97 && code <= 102) {
6041
+ return code - 87;
6042
+ }
6043
+ return -1;
6044
+ }
6045
+ function percentDecoded(text) {
6046
+ const input = Buffer7.from(text);
6047
+ const output = Buffer7.allocUnsafe(input.length);
6048
+ let length = 0;
6049
+ for (let i = 0; i < input.length; i++) {
6050
+ const high = input[i] === 37 ? hexValue(input[i + 1]) : -1;
6051
+ const low = high === -1 ? -1 : hexValue(input[i + 2]);
6052
+ if (low === -1) {
6053
+ output[length++] = input[i];
6054
+ continue;
6055
+ }
6056
+ output[length++] = high * 16 + low;
6057
+ i += 2;
6058
+ }
6059
+ return output.subarray(0, length);
6060
+ }
6061
+ var UTF16LE = /* @__PURE__ */ new Set(["csunicode", "iso-10646-ucs-2", "ucs-2", "unicode", "unicodefeff", "utf-16", "utf-16le"]);
6062
+ var UTF16BE = /* @__PURE__ */ new Set(["unicodefffe", "utf-16be"]);
6063
+ function swapped(bytes) {
6064
+ return Buffer7.from(bytes.subarray(0, bytes.length - bytes.length % 2)).swap16().toString("utf16le");
6065
+ }
6066
+ function decodeText(content, charset = "") {
6067
+ if (content[0] === 255 && content[1] === 254) {
6068
+ return content.subarray(2).toString("utf16le");
6069
+ }
6070
+ if (content[0] === 254 && content[1] === 255) {
6071
+ return swapped(content.subarray(2));
6072
+ }
6073
+ if (UTF16LE.has(charset)) {
6074
+ return content.toString("utf16le");
6075
+ }
6076
+ return UTF16BE.has(charset) ? swapped(content) : content.toString("utf8");
6077
+ }
6078
+ var DATA_DOCUMENT = /^\s*data:\s*(?:text\/html|application\/xhtml\+xml|image\/svg\+xml|text\/xml|application\/xml)\b([^,]*),/i;
6079
+ function dataDocument(value) {
6080
+ const match = DATA_DOCUMENT.exec(value.slice(0, 1024));
6081
+ if (!match) {
6082
+ return null;
6083
+ }
6084
+ const bytes = percentDecoded(value.slice(match[0].length));
6085
+ const content = /;\s*base64\s*$/i.test(match[1]) ? Buffer7.from(bytes.toString("latin1"), "base64") : bytes;
6086
+ const charset = /;\s*charset\s*=\s*"?([^;"\s]+)/i.exec(match[1]);
6087
+ return decodeText(content, charset ? charset[1].toLowerCase() : "");
6088
+ }
6089
+ var LINE_BREAK = /\r\n?|\n/;
6090
+ var FOLD = /(?:\r\n?|\n)[ \t]/g;
6091
+ var QUOTED_PRINTABLE = /;\s*(?:encoding\s*=\s*)?"?quoted-printable"?\s*(?:;|$)/i;
6092
+ var MEDIA_PROPERTIES = /* @__PURE__ */ new Set(["photo", "logo", "sound", "key", "attach"]);
6093
+ var HTML_PROPERTIES = /^(?:x-alt-desc|styled-description|description|comment)[;:]/i;
6094
+ function readEscapes(text, lenient = false) {
6095
+ let index = text.indexOf("\\");
6096
+ if (index === -1) {
6097
+ return text;
6098
+ }
6099
+ let out = "";
6100
+ let parts = [];
6101
+ let start = 0;
6102
+ while (index !== -1 && index + 1 < text.length) {
6103
+ const next = text[index + 1];
6104
+ if (lenient || "nN,;\\".includes(next)) {
6105
+ parts.push(text.slice(start, index), next === "n" || next === "N" ? "\n" : next);
6106
+ start = index + 2;
6107
+ index = text.indexOf("\\", start);
6108
+ if (parts.length >= 65536) {
6109
+ out += parts.join("");
6110
+ parts = [];
6111
+ }
6112
+ } else {
6113
+ index = text.indexOf("\\", index + 1);
6114
+ }
6115
+ }
6116
+ parts.push(text.slice(start));
6117
+ return out + parts.join("");
6118
+ }
6119
+ function valueColon(line) {
6120
+ let colon = line.indexOf(":");
6121
+ let from = 0;
6122
+ for (; ; ) {
6123
+ const quote = line.indexOf('"', from);
6124
+ if (quote === -1 || colon === -1 || colon < quote) {
6125
+ return colon;
6126
+ }
6127
+ const end = line.indexOf('"', quote + 1);
6128
+ if (end === -1) {
6129
+ return -1;
6130
+ }
6131
+ from = end + 1;
6132
+ if (colon < from) {
6133
+ colon = line.indexOf(":", from);
6134
+ }
6135
+ }
6136
+ }
6137
+ function trimSpaceEnd(text) {
6138
+ let end = text.length;
6139
+ while (end > 0 && (text[end - 1] === " " || text[end - 1] === " ")) {
6140
+ end--;
6141
+ }
6142
+ return end === text.length ? text : text.slice(0, end);
6143
+ }
6144
+ function decodeQuotedPrintable(value, charset) {
6145
+ let decoder;
6146
+ try {
6147
+ decoder = new TextDecoder(charset || "utf8");
6148
+ } catch {
6149
+ decoder = new TextDecoder("utf8");
6150
+ }
6151
+ if (!/[\u0080-\u{10FFFF}]/u.test(value)) {
6152
+ const bytes = Buffer7.from(value, "latin1");
6153
+ let length = 0;
6154
+ for (let index = 0; index < bytes.length; index++) {
6155
+ const hex = bytes[index] === 61 ? value.slice(index + 1, index + 3) : "";
6156
+ if (/^[\da-f]{2}$/i.test(hex)) {
6157
+ bytes[length++] = Number.parseInt(hex, 16);
6158
+ index += 2;
6159
+ } else {
6160
+ bytes[length++] = bytes[index];
6161
+ }
6162
+ }
6163
+ return decoder.decode(bytes.subarray(0, length));
6164
+ }
6165
+ return value.replaceAll(/(?:=[\da-f]{2})+/gi, (run) => decoder.decode(Buffer7.from(run.replaceAll("=", ""), "hex")));
6166
+ }
6167
+ function quotedPrintableValues(text) {
6168
+ if (!/quoted-printable/i.test(text)) {
6169
+ return text;
6170
+ }
6171
+ const lines2 = text.split(LINE_BREAK);
6172
+ const out = [];
6173
+ for (let i = 0; i < lines2.length; i++) {
6174
+ const line = lines2[i];
6175
+ const colon = valueColon(line);
6176
+ const parameters = colon === -1 ? "" : line.slice(0, colon);
6177
+ if (colon === -1 || !QUOTED_PRINTABLE.test(parameters)) {
6178
+ out.push(line);
6179
+ continue;
6180
+ }
6181
+ const charset = /;\s*charset\s*=\s*"?([\w.:-]+)/i.exec(parameters)?.[1];
6182
+ const parts = [trimSpaceEnd(line.slice(colon + 1))];
6183
+ while (parts.at(-1).endsWith("=") && i + 1 < lines2.length) {
6184
+ parts[parts.length - 1] = parts.at(-1).slice(0, -1);
6185
+ parts.push(trimSpaceEnd(lines2[++i]));
6186
+ }
6187
+ out.push(line.slice(0, colon + 1) + decodeQuotedPrintable(parts.join(""), charset));
6188
+ }
6189
+ return out.join("\n");
6190
+ }
6191
+ function hasQuotedPrintable(text) {
6192
+ const unfolded = text.replaceAll(FOLD, "");
6193
+ if (!/quoted-printable/i.test(unfolded)) {
6194
+ return false;
6195
+ }
6196
+ return unfolded.split(LINE_BREAK).some((line) => {
6197
+ const colon = valueColon(line);
6198
+ return colon !== -1 && QUOTED_PRINTABLE.test(line.slice(0, colon));
6199
+ });
6200
+ }
6201
+ function shownText(text, full = true) {
6202
+ const unfolded = text.replaceAll(FOLD, "");
6203
+ const shown = /* @__PURE__ */ new Set([text, readEscapes(unfolded)]);
6204
+ let decoded = unfolded;
6205
+ if (full) {
6206
+ decoded = quotedPrintableValues(unfolded);
6207
+ if (decoded !== unfolded) {
6208
+ shown.add(readEscapes(decoded));
6209
+ if (unfolded !== text) {
6210
+ shown.add(quotedPrintableValues(text));
6211
+ }
6212
+ }
6213
+ }
6214
+ if (/\\[^nN,;\\]/.test(decoded)) {
6215
+ shown.add(readEscapes(decoded, true));
6216
+ }
6217
+ return [...shown].reverse().join("\n");
6218
+ }
6219
+ function htmlDescriptions(text) {
6220
+ if (!/[<&]|altrep/i.test(text)) {
6221
+ return "";
6222
+ }
6223
+ const html = [];
6224
+ for (const line of text.replaceAll(FOLD, "").split(LINE_BREAK)) {
6225
+ const colon = valueColon(line);
6226
+ if (colon === -1) {
6227
+ continue;
6228
+ }
6229
+ const altrep = /;\s*altrep\s*=\s*"([^"]*)"/i.exec(line.slice(0, colon))?.[1];
6230
+ if (altrep) {
6231
+ html.push(`<iframe src="${altrep.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("<", "&lt;")}"></iframe>`);
6232
+ }
6233
+ if (!HTML_PROPERTIES.test(line)) {
6234
+ continue;
6235
+ }
6236
+ const value = readEscapes(line.slice(colon + 1));
6237
+ if (/[<&]/.test(value)) {
6238
+ html.push(value);
6239
+ }
6240
+ }
6241
+ return html.join("\n");
6242
+ }
6243
+ var BINARY = /;\s*(?:(?:encoding\s*=\s*)?"?(?:b|base64)"?|value\s*=\s*"?binary"?)\s*(?:;|$)/i;
6244
+ function withoutBinaryValues(text) {
6245
+ if (!/base64|binary|encoding\s*=\s*"?b|;\s*b\s*[;:]/i.test(text)) {
6246
+ return text;
6247
+ }
6248
+ const lines2 = text.split(LINE_BREAK);
6249
+ const out = [];
6250
+ for (let i = 0; i < lines2.length; i++) {
6251
+ const line = lines2[i];
6252
+ const colon = valueColon(line);
6253
+ if (colon === -1 || !BINARY.test(line.slice(0, colon))) {
6254
+ out.push(line);
6255
+ continue;
6256
+ }
6257
+ const value = [line.slice(colon + 1)];
6258
+ let next = i + 1;
6259
+ while (next < lines2.length && (lines2[next].startsWith(" ") || lines2[next].startsWith(" "))) {
6260
+ value.push(lines2[next++]);
6261
+ }
6262
+ if (!value.every((part) => /^[\w+/=\s]*$/.test(part))) {
6263
+ for (let index = i; index < next; index++) {
6264
+ out.push(lines2[index]);
6265
+ }
6266
+ i = next - 1;
6267
+ continue;
6268
+ }
6269
+ while (next < lines2.length && lines2[next] !== "" && /^[\w+/=]+$/.test(lines2[next])) {
6270
+ value.push(lines2[next++]);
6271
+ }
6272
+ if (next < lines2.length && lines2[next] === "") {
6273
+ next++;
6274
+ }
6275
+ i = next - 1;
6276
+ const name = line.slice(0, colon).split(";")[0].split(".").pop().trim().toLowerCase();
6277
+ out.push(MEDIA_PROPERTIES.has(name) ? line.slice(0, colon + 1) : line.slice(0, colon + 1) + Buffer7.from(value.join("").replaceAll(/\s/g, ""), "base64").toString("utf8"));
6278
+ }
6279
+ return out.join("\n");
6280
+ }
6281
+ var HTML_TYPES = /* @__PURE__ */ new Set(["text/html", "application/xhtml+xml", "image/svg+xml", "text/xml", "application/xml"]);
6282
+ var HTML_EXTENSIONS = /* @__PURE__ */ new Set(["html", "htm", "shtml", "xhtml", "xht", "svg", "xml"]);
6283
+ var CARD_TYPES = /* @__PURE__ */ new Set(["text/calendar", "text/x-vcalendar", "application/ics", "text/vcard", "text/x-vcard", "text/directory"]);
6284
+ var CARD_EXTENSIONS = /* @__PURE__ */ new Set(["ics", "ical", "ifb", "vcs", "vcf", "vcard"]);
6285
+ var MESSAGE_TYPES = /* @__PURE__ */ new Set(["message/rfc822", "message/global"]);
6286
+ function extensionOf2(filename) {
6287
+ let end = filename.length;
6288
+ while (end > 0 && (filename[end - 1] === "." || filename[end - 1] === " ")) {
6289
+ end--;
6290
+ }
6291
+ const name = filename.slice(0, end).trim().toLowerCase();
6292
+ const index = name.lastIndexOf(".");
6293
+ return index !== -1 && index < name.length - 1 ? name.slice(index + 1) : null;
6294
+ }
6295
+ function extensionsOf(attachment) {
6296
+ const names = [];
6297
+ if (typeof attachment.filename === "string") {
6298
+ names.push(attachment.filename);
6299
+ }
6300
+ const { headers } = attachment;
6301
+ if (headers && typeof headers.get === "function") {
6302
+ for (const [field, parameter] of [["content-type", "name"], ["content-disposition", "filename"]]) {
6303
+ const value = headers.get(field)?.params?.[parameter];
6304
+ if (typeof value === "string" && value !== "") {
6305
+ names.push(value);
6306
+ }
6307
+ }
6308
+ }
6309
+ return new Set(names.flatMap((name) => [extensionOf2(name), extensionOf(name)]).filter(Boolean));
6310
+ }
6311
+ function charsetOf(attachment) {
6312
+ const { headers } = attachment;
6313
+ const charset = headers && typeof headers.get === "function" ? headers.get("content-type")?.params?.charset : null;
6314
+ return typeof charset === "string" ? charset.toLowerCase() : "";
6315
+ }
6316
+ function documentsOf(attachments, until) {
6317
+ const documents = [];
6318
+ let bytes = 0;
6319
+ let opened = 0;
6320
+ for (const attachment of attachments) {
6321
+ const { content } = attachment ?? {};
6322
+ if (!Buffer7.isBuffer(content) || content.length === 0) {
6323
+ continue;
6324
+ }
6325
+ const type = typeof attachment.contentType === "string" ? attachment.contentType.toLowerCase() : "";
6326
+ const extensions = extensionsOf(attachment);
6327
+ const has = (set) => [...extensions].some((name) => set.has(name));
6328
+ if (MESSAGE_TYPES.has(type) || extensions.has("eml") || extensions.has("u8msg")) {
6329
+ continue;
6330
+ }
6331
+ const isHtml = HTML_TYPES.has(type) || has(HTML_EXTENSIONS);
6332
+ const isCard = !isHtml && (CARD_TYPES.has(type) || has(CARD_EXTENSIONS));
6333
+ const isText = isCard || !isHtml && type === "text/plain";
6334
+ if (!isHtml && !isText) {
6335
+ continue;
6336
+ }
6337
+ if (!isText && ++opened > MAX_ATTACHED || bytes + content.length > MAX_ATTACHED_BYTES) {
6338
+ documents.tooComplex = true;
6339
+ return documents;
6340
+ }
6341
+ if (Date.now() > until) {
6342
+ documents.incomplete = true;
6343
+ return documents;
6344
+ }
6345
+ bytes += content.length;
6346
+ const text = decodeText(content, charsetOf(attachment));
6347
+ if (isHtml) {
6348
+ documents.push({ html: text });
6349
+ continue;
6350
+ }
6351
+ if (!isCard) {
6352
+ documents.push({ text });
6353
+ continue;
6354
+ }
6355
+ const readable = withoutBinaryValues(text);
6356
+ const full = readable.length <= MAX_SHOWN_BYTES;
6357
+ if (!full && hasQuotedPrintable(readable)) {
6358
+ documents.tooComplex = true;
6359
+ continue;
6360
+ }
6361
+ documents.push({ text: shownText(readable, full) });
6362
+ const html = htmlDescriptions(readable);
6363
+ if (html !== "") {
6364
+ documents.push({ html });
6365
+ }
6366
+ }
6367
+ return documents;
6368
+ }
6369
+ function carriedLinkHosts(mail, {
6370
+ mails = [],
6371
+ attachments = [],
6372
+ maxHosts = 250,
6373
+ hostsInText = false
6374
+ } = {}) {
6375
+ const hosts = /* @__PURE__ */ new Set();
6376
+ const loose = /* @__PURE__ */ new Set();
6377
+ const seen = /* @__PURE__ */ new Set();
6378
+ const seenBare = /* @__PURE__ */ new Set();
6379
+ const seenLoose = /* @__PURE__ */ new Set();
6380
+ let more = false;
6381
+ let tooComplex = false;
6382
+ const done = () => more;
6383
+ const looseDone = () => more || loose.size > maxHosts;
6384
+ const addLoose = (host) => {
6385
+ if (!looseDone()) {
6386
+ loose.add(host);
6387
+ }
6388
+ };
6389
+ const until = Date.now() + MAX_READ_TIME;
6390
+ let incomplete = false;
6391
+ const late = () => {
6392
+ if (!incomplete && Date.now() > until) {
6393
+ incomplete = true;
6394
+ }
6395
+ return incomplete;
6396
+ };
6397
+ const carried = documentsOf(attachments, until);
6398
+ tooComplex = Boolean(carried.tooComplex);
6399
+ incomplete = Boolean(carried.incomplete);
6400
+ const queue = [mail, ...mails, ...carried].map((document) => ({ document, depth: 0 }));
6401
+ let depth = 0;
6402
+ let embedded = 0;
6403
+ let embeddedLength = 0;
6404
+ const embed = (html) => {
6405
+ if (depth >= MAX_EMBED_DEPTH || embedded >= MAX_EMBEDDED || embeddedLength + html.length > MAX_EMBEDDED_LENGTH) {
6406
+ tooComplex = true;
6407
+ return;
6408
+ }
6409
+ embedded++;
6410
+ embeddedLength += html.length;
6411
+ queue.push({ document: { html }, depth: depth + 1 });
6412
+ };
6413
+ const add = (value) => {
6414
+ if (more || typeof value !== "string" || value === "" || seen.has(value)) {
6415
+ return;
6416
+ }
6417
+ if (seen.size < MAX_SEEN) {
6418
+ seen.add(value);
6419
+ }
6420
+ if (/^\s*data:/i.test(value)) {
6421
+ const html = dataDocument(value);
6422
+ if (html !== null) {
6423
+ embed(html);
6424
+ }
6425
+ return;
6426
+ }
6427
+ const found = hostsOf(value);
6428
+ if (found.unresolved) {
6429
+ tooComplex = true;
6430
+ }
6431
+ for (const host of found) {
6432
+ if (hosts.has(host)) {
6433
+ continue;
6434
+ }
6435
+ if (hosts.size >= maxHosts) {
6436
+ more = true;
6437
+ return;
6438
+ }
6439
+ hosts.add(host);
6440
+ }
6441
+ };
6442
+ const readText = (source, { cutStart = false, cutEnd = false } = {}) => {
6443
+ for (const match of source.matchAll(URL_START)) {
6444
+ if (more) {
6445
+ break;
6446
+ }
6447
+ if (cutStart && match.index === 0) {
6448
+ continue;
6449
+ }
6450
+ if (cutEnd && match.index + match[0].length === source.length) {
6451
+ const value = match[0];
6452
+ const at = /^www\./i.test(value) ? 0 : value.indexOf("//") + 2;
6453
+ if (/[/?#\\]/.test(value.slice(at))) {
6454
+ add(value);
6455
+ } else if (source.length - match.index > CHUNK_OVERLAP) {
6456
+ tooComplex = true;
6457
+ }
6458
+ continue;
6459
+ }
6460
+ add(match[0]);
6461
+ }
6462
+ const from = cutStart ? 1 : 0;
6463
+ bareHosts(source, add, {
6464
+ done,
6465
+ seen: seenBare,
6466
+ from,
6467
+ cutEnd
6468
+ });
6469
+ if (hostsInText && !looseDone()) {
6470
+ looseHosts(source, addLoose, {
6471
+ from,
6472
+ cutEnd,
6473
+ seen: seenLoose,
6474
+ done: looseDone
6475
+ });
6476
+ }
6477
+ };
6478
+ for (const element of queue) {
6479
+ if (more || late()) {
6480
+ break;
6481
+ }
6482
+ ({ depth } = element);
6483
+ const { document } = element;
6484
+ const html = withoutNul(typeof document?.html === "string" ? document.html : "");
6485
+ const text = withoutNul(typeof document?.text === "string" ? document.text : "");
6486
+ for (const [name, value] of attributesOf(html)) {
6487
+ if (more) {
6488
+ break;
6489
+ }
6490
+ if (LINK_ATTRIBUTES.has(name)) {
6491
+ linksOf(name, value, add, embed);
6492
+ }
6493
+ }
6494
+ lenientLinks(html, add, done, embed);
6495
+ for (const pattern of [CSS_URL, CSS_IMPORT]) {
6496
+ for (const [, link] of html.matchAll(pattern)) {
6497
+ if (more) {
6498
+ break;
6499
+ }
6500
+ add(decodeEntities(link, { attribute: true }));
6501
+ }
6502
+ }
6503
+ if (!more) {
6504
+ readText(text);
6505
+ }
6506
+ for (const chunk2 of chunksOf(html)) {
6507
+ if (more || late()) {
6508
+ break;
6509
+ }
6510
+ readText(decodeEntities(chunk2.text), chunk2);
6511
+ }
6512
+ }
6513
+ if (!more && !incomplete) {
6514
+ for (const name of loose) {
6515
+ if (hosts.size >= maxHosts) {
6516
+ break;
6517
+ }
6518
+ const [host] = hostsOf(`http://${name}/`);
6519
+ if (host) {
6520
+ hosts.add(host);
6521
+ }
6522
+ }
6523
+ }
6524
+ return {
6525
+ hosts: [...hosts],
6526
+ more,
6527
+ tooComplex,
6528
+ incomplete
6529
+ };
6530
+ }
6531
+
6532
+ // src/is-arbitrary.js
6533
+ import { parse as parseDomain4 } from "tldts";
5441
6534
  var GTUBE = "XJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X";
5442
6535
  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;
5443
6536
  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;
@@ -5628,13 +6721,13 @@ function joinsDisplay(brand, display, name) {
5628
6721
  return false;
5629
6722
  }
5630
6723
  function isOfficial({ brand, official }, domain, display, shared = true) {
5631
- const parsed = parseDomain3(domain);
6724
+ const parsed = parseDomain4(domain);
5632
6725
  const name = parsed.domainWithoutSuffix ?? "";
5633
6726
  const compact = brand.replaceAll(" ", "");
5634
6727
  if (parsed.publicSuffix === compact || compact.length >= 5 && joinsDisplay(brand, display, name) && !JOINED_WORDS.some((word) => name.slice(compact.length).includes(word))) {
5635
6728
  return true;
5636
6729
  }
5637
- return official.some((entry) => entry instanceof RegExp ? entry.test(registrableDomain(domain)) : entry.includes(".") ? (shared || parseDomain3(entry).domain !== null) && (domain === entry || domain.endsWith(`.${entry}`)) : name === entry);
6730
+ 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);
5638
6731
  }
5639
6732
  function headerString(mail, name) {
5640
6733
  const value = mail.headers?.get?.(name);
@@ -5736,7 +6829,7 @@ ${withoutHelp(markup)}`) || isInjection(readable) || attributeText(markup).some(
5736
6829
  }
5737
6830
  } else if (claimed && isWholeName(name, claimed)) {
5738
6831
  const domain = claimed[1];
5739
- const brand = MAILBOXES.has(parseDomain3(domain).domainWithoutSuffix) ? null : DISPLAY_BRANDS.find((entry) => isOfficial(entry, domain, "", false) && !isOfficial(entry, session.originalFromAddressDomain, ""));
6832
+ const brand = MAILBOXES.has(parseDomain4(domain).domainWithoutSuffix) ? null : DISPLAY_BRANDS.find((entry) => isOfficial(entry, domain, "", false) && !isOfficial(entry, session.originalFromAddressDomain, ""));
5740
6833
  if (brand) {
5741
6834
  hit("FROM_NAME_BRAND", 2, `Display name is an address of "${brand.brand}" but the message is from ${fromRoot}`, "PHISHING");
5742
6835
  }
@@ -5771,7 +6864,7 @@ ${withoutHelp(markup)}`) || isInjection(readable) || attributeText(markup).some(
5771
6864
  }
5772
6865
 
5773
6866
  // src/llm.js
5774
- import { Buffer as Buffer7 } from "node:buffer";
6867
+ import { Buffer as Buffer8 } from "node:buffer";
5775
6868
  import { createHash as createHash2, randomBytes } from "node:crypto";
5776
6869
  import os from "node:os";
5777
6870
  import process from "node:process";
@@ -6132,7 +7225,7 @@ function authHeaders(config) {
6132
7225
  case "basic": {
6133
7226
  const user = config.username ?? "";
6134
7227
  const pass = config.password ?? config.apiKey ?? "";
6135
- headers.authorization = `Basic ${Buffer7.from(`${user}:${pass}`).toString("base64")}`;
7228
+ headers.authorization = `Basic ${Buffer8.from(`${user}:${pass}`).toString("base64")}`;
6136
7229
  break;
6137
7230
  }
6138
7231
  case "x-api-key": {
@@ -7078,7 +8171,7 @@ function scoreResults(results, options) {
7078
8171
  var package_default = {
7079
8172
  name: "spamscanner",
7080
8173
  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.",
7081
- version: "7.0.10",
8174
+ version: "7.0.11",
7082
8175
  author: "Forward Email (https://forwardemail.net)",
7083
8176
  bin: {
7084
8177
  spamscanner: "./dist/esm/cli.js"
@@ -7225,7 +8318,7 @@ var package_default = {
7225
8318
  var VERSION = package_default.version;
7226
8319
 
7227
8320
  // src/headers.js
7228
- import { Buffer as Buffer8 } from "node:buffer";
8321
+ import { Buffer as Buffer9 } from "node:buffer";
7229
8322
  function foldHeader(name, value, newline = "\r\n") {
7230
8323
  const pieces = String(value).split(/(?<=[ ,])/);
7231
8324
  const lines2 = [];
@@ -7260,10 +8353,10 @@ function splitMessage(raw) {
7260
8353
  const match = /^\r?\n|\r?\n\r?\n/.exec(text);
7261
8354
  if (!match) {
7262
8355
  const newline2 = text.includes("\n") && !text.includes("\r\n") ? "\n" : "\r\n";
7263
- return { header: text.replace(/\r?\n$/, ""), body: Buffer8.alloc(0), newline: newline2 };
8356
+ return { header: text.replace(/\r?\n$/, ""), body: Buffer9.alloc(0), newline: newline2 };
7264
8357
  }
7265
8358
  const newline = match[0].startsWith("\r") ? "\r\n" : "\n";
7266
- return { header: text.slice(0, match.index), body: raw.subarray(Buffer8.byteLength(text.slice(0, match.index + match[0].length), "latin1")), newline };
8359
+ return { header: text.slice(0, match.index), body: raw.subarray(Buffer9.byteLength(text.slice(0, match.index + match[0].length), "latin1")), newline };
7267
8360
  }
7268
8361
  var ENCODED_BYTES = 39;
7269
8362
  var PLAIN_WORD = 64;
@@ -7279,14 +8372,14 @@ function subjectTagText(tag, spaceAfter = false) {
7279
8372
  const words2 = [];
7280
8373
  let chunk2 = "";
7281
8374
  for (const character of spaceAfter ? `${line} ` : line) {
7282
- if (Buffer8.byteLength(chunk2 + character, "utf8") > ENCODED_BYTES) {
8375
+ if (Buffer9.byteLength(chunk2 + character, "utf8") > ENCODED_BYTES) {
7283
8376
  words2.push(chunk2);
7284
8377
  chunk2 = "";
7285
8378
  }
7286
8379
  chunk2 += character;
7287
8380
  }
7288
8381
  words2.push(chunk2);
7289
- return words2.map((word) => `=?UTF-8?B?${Buffer8.from(word, "utf8").toString("base64")}?=`).join(" ");
8382
+ return words2.map((word) => `=?UTF-8?B?${Buffer9.from(word, "utf8").toString("base64")}?=`).join(" ");
7290
8383
  }
7291
8384
  function taggedSubject(tag, current, newline = "\r\n") {
7292
8385
  const unfolded = current.replaceAll(/\r?\n/g, "");
@@ -7317,7 +8410,7 @@ function taggedSubject(tag, current, newline = "\r\n") {
7317
8410
  return lines2.join(newline).slice("Subject: ".length);
7318
8411
  }
7319
8412
  function rewriteMessage(source, headers = [], options = {}) {
7320
- const raw = Buffer8.isBuffer(source) ? source : Buffer8.from(String(source), "utf8");
8413
+ const raw = Buffer9.isBuffer(source) ? source : Buffer9.from(String(source), "utf8");
7321
8414
  const { header, body, newline } = splitMessage(raw);
7322
8415
  const remove = options.remove ?? /^x-spam-/i;
7323
8416
  const lines2 = header === "" ? [] : header.split(/\r?\n/);
@@ -7342,10 +8435,10 @@ function rewriteMessage(source, headers = [], options = {}) {
7342
8435
  }
7343
8436
  }
7344
8437
  }
7345
- const added = headers.map(([name, value]) => foldHeader(name, Buffer8.from(String(value), "utf8").toString("latin1"), newline));
8438
+ const added = headers.map(([name, value]) => foldHeader(name, Buffer9.from(String(value), "utf8").toString("latin1"), newline));
7346
8439
  const mbox = kept.length > 0 && kept[0][0].startsWith("From ") ? kept.shift() : [];
7347
8440
  const head = [...mbox, ...added, ...kept.map((field) => field.join(newline))].join(newline);
7348
- return Buffer8.concat([Buffer8.from(head === "" && header === "" ? newline : `${head}${newline}${newline}`, "latin1"), body]);
8441
+ return Buffer9.concat([Buffer9.from(head === "" && header === "" ? newline : `${head}${newline}${newline}`, "latin1"), body]);
7349
8442
  }
7350
8443
 
7351
8444
  // src/train.js
@@ -7353,7 +8446,7 @@ import { createHash as createHash3 } from "node:crypto";
7353
8446
  import path3 from "node:path";
7354
8447
 
7355
8448
  // src/sources.js
7356
- import { Buffer as Buffer9 } from "node:buffer";
8449
+ import { Buffer as Buffer10 } from "node:buffer";
7357
8450
  import { createReadStream } from "node:fs";
7358
8451
  import fs from "node:fs/promises";
7359
8452
  import path2 from "node:path";
@@ -7533,7 +8626,7 @@ async function* readJsonl(file, options = {}) {
7533
8626
  }
7534
8627
  async function* walk(directory) {
7535
8628
  const entries = await fs.readdir(directory, { withFileTypes: true });
7536
- entries.sort((a, b) => Buffer9.compare(Buffer9.from(a.name), Buffer9.from(b.name)));
8629
+ entries.sort((a, b) => Buffer10.compare(Buffer10.from(a.name), Buffer10.from(b.name)));
7537
8630
  for (const entry of entries) {
7538
8631
  if (SKIP.has(entry.name) || entry.name.startsWith(".")) {
7539
8632
  continue;
@@ -7558,7 +8651,7 @@ async function* readMessages(source) {
7558
8651
  const handle = await fs.open(file);
7559
8652
  let head;
7560
8653
  try {
7561
- const { buffer, bytesRead } = await handle.read(Buffer9.alloc(64), 0, 64, 0);
8654
+ const { buffer, bytesRead } = await handle.read(Buffer10.alloc(64), 0, 64, 0);
7562
8655
  head = buffer.subarray(0, bytesRead).toString("latin1");
7563
8656
  } finally {
7564
8657
  await handle.close();
@@ -7682,12 +8775,12 @@ async function evaluate(classifier, examples) {
7682
8775
  }
7683
8776
 
7684
8777
  // src/milter.js
7685
- import { Buffer as Buffer11 } from "node:buffer";
8778
+ import { Buffer as Buffer12 } from "node:buffer";
7686
8779
  import { EventEmitter } from "node:events";
7687
8780
  import net3 from "node:net";
7688
8781
 
7689
8782
  // src/server.js
7690
- import { Buffer as Buffer10 } from "node:buffer";
8783
+ import { Buffer as Buffer11 } from "node:buffer";
7691
8784
  import { timingSafeEqual } from "node:crypto";
7692
8785
  import http2 from "node:http";
7693
8786
  import net2 from "node:net";
@@ -7727,8 +8820,8 @@ function sessionFromQuery(query) {
7727
8820
  return session;
7728
8821
  }
7729
8822
  function sameToken(given, expected) {
7730
- const a = Buffer10.from(String(given));
7731
- const b = Buffer10.from(String(expected));
8823
+ const a = Buffer11.from(String(given));
8824
+ const b = Buffer11.from(String(expected));
7732
8825
  return a.length === b.length && timingSafeEqual(a, b);
7733
8826
  }
7734
8827
  function readBody(request, limit) {
@@ -7748,7 +8841,7 @@ function readBody(request, limit) {
7748
8841
  reject(error);
7749
8842
  return;
7750
8843
  }
7751
- resolve(Buffer10.concat(chunks));
8844
+ resolve(Buffer11.concat(chunks));
7752
8845
  });
7753
8846
  request.on("error", reject);
7754
8847
  });
@@ -7763,7 +8856,7 @@ function createHttpServer(scanner, options = {}) {
7763
8856
  let active = 0;
7764
8857
  const server = http2.createServer(async (request, response) => {
7765
8858
  const send = (status, body, headers = {}) => {
7766
- const isBuffer = Buffer10.isBuffer(body);
8859
+ const isBuffer = Buffer11.isBuffer(body);
7767
8860
  response.writeHead(status, { "content-type": isBuffer ? "message/rfc822" : "application/json; charset=utf-8", "cache-control": "no-store", ...headers });
7768
8861
  response.end(isBuffer ? body : `${JSON.stringify(body)}
7769
8862
  `);
@@ -7843,7 +8936,7 @@ function createTcpServer(scanner, options = {}) {
7843
8936
  const onEnd = async () => {
7844
8937
  socket.setTimeout(0);
7845
8938
  try {
7846
- const result = await scanner.scan(Buffer10.concat(chunks));
8939
+ const result = await scanner.scan(Buffer11.concat(chunks));
7847
8940
  socket.end(json ? `${JSON.stringify(serializeResult(result))}
7848
8941
  ` : `${result.isSpam ? "SPAM" : "HAM"} ${result.score.toFixed(1)}/${result.threshold.toFixed(1)} ${result.tests.map((test) => test.name).join(",")}
7849
8942
  `);
@@ -7937,12 +9030,12 @@ var PROTOCOL = {
7937
9030
  HDR_LEADSPC: 1048576
7938
9031
  };
7939
9032
  var MAX_PACKET = 1024 * 1024;
7940
- function packet(command, data = Buffer11.alloc(0)) {
7941
- const payload = Buffer11.isBuffer(data) ? data : Buffer11.from(data, "latin1");
7942
- const header = Buffer11.alloc(5);
9033
+ function packet(command, data = Buffer12.alloc(0)) {
9034
+ const payload = Buffer12.isBuffer(data) ? data : Buffer12.from(data, "latin1");
9035
+ const header = Buffer12.alloc(5);
7943
9036
  header.writeUInt32BE(payload.length + 1);
7944
9037
  header.write(command, 4, "latin1");
7945
- return Buffer11.concat([header, payload]);
9038
+ return Buffer12.concat([header, payload]);
7946
9039
  }
7947
9040
  function cstrings(data) {
7948
9041
  const parts = data.toString("latin1").split("\0");
@@ -7952,7 +9045,7 @@ function cstrings(data) {
7952
9045
  return parts;
7953
9046
  }
7954
9047
  function cstring(value) {
7955
- return Buffer11.from(`${value}\0`, "latin1");
9048
+ return Buffer12.from(`${value}\0`, "latin1");
7956
9049
  }
7957
9050
  function address(value) {
7958
9051
  const text = String(value ?? "").trim();
@@ -8009,7 +9102,7 @@ var MilterSession = class {
8009
9102
  if (this.buffered < this.needed) {
8010
9103
  return;
8011
9104
  }
8012
- let buffer = this.chunks.length === 1 ? this.chunks[0] : Buffer11.concat(this.chunks);
9105
+ let buffer = this.chunks.length === 1 ? this.chunks[0] : Buffer12.concat(this.chunks);
8013
9106
  this.needed = 4;
8014
9107
  while (buffer.length >= 4) {
8015
9108
  const length = buffer.readUInt32BE(0);
@@ -8054,7 +9147,7 @@ var MilterSession = class {
8054
9147
  const protocol = data.readUInt32BE(8);
8055
9148
  const wanted = ACTIONS.ADDHDRS | ACTIONS.CHGHDRS | ACTIONS.QUARANTINE;
8056
9149
  this.leadingSpace = Boolean(protocol & PROTOCOL.HDR_LEADSPC);
8057
- const reply2 = Buffer11.alloc(12);
9150
+ const reply2 = Buffer12.alloc(12);
8058
9151
  reply2.writeUInt32BE(Math.min(version, 6), 0);
8059
9152
  reply2.writeUInt32BE(actions & wanted, 4);
8060
9153
  reply2.writeUInt32BE(protocol & (PROTOCOL.HDR_LEADSPC | PROTOCOL.NOUNKNOWN), 8);
@@ -8119,7 +9212,7 @@ var MilterSession = class {
8119
9212
  if (this.truncated || this.size + data.length > options.maxSize) {
8120
9213
  this.truncated = true;
8121
9214
  } else {
8122
- this.body.push(Buffer11.from(data));
9215
+ this.body.push(Buffer12.from(data));
8123
9216
  this.size += data.length;
8124
9217
  }
8125
9218
  this.send(RESPONSES.CONTINUE);
@@ -8154,7 +9247,7 @@ var MilterSession = class {
8154
9247
  /** @returns {Buffer} */
8155
9248
  rawMessage() {
8156
9249
  const head = this.headers.map(([name, value]) => `${name}:${this.leadingSpace || value.startsWith(" ") || value.startsWith(" ") ? "" : " "}${value}`).join("\r\n");
8157
- return Buffer11.concat([Buffer11.from(this.headers.length > 0 ? `${head}\r
9250
+ return Buffer12.concat([Buffer12.from(this.headers.length > 0 ? `${head}\r
8158
9251
  \r
8159
9252
  ` : "\r\n", "latin1"), ...this.body]);
8160
9253
  }
@@ -8165,9 +9258,9 @@ var MilterSession = class {
8165
9258
  const key = name.toLowerCase();
8166
9259
  counts.set(key, (counts.get(key) || 0) + 1);
8167
9260
  if (key.startsWith("x-spam-")) {
8168
- const index = Buffer11.alloc(4);
9261
+ const index = Buffer12.alloc(4);
8169
9262
  index.writeUInt32BE(counts.get(key));
8170
- this.send(RESPONSES.CHGHEADER, Buffer11.concat([index, cstring(name), cstring("")]));
9263
+ this.send(RESPONSES.CHGHEADER, Buffer12.concat([index, cstring(name), cstring("")]));
8171
9264
  }
8172
9265
  }
8173
9266
  }
@@ -8190,7 +9283,7 @@ var MilterSession = class {
8190
9283
  }
8191
9284
  this.server.report(error);
8192
9285
  this.removeForgedHeaders();
8193
- this.send(RESPONSES.ADDHEADER, Buffer11.concat([cstring("X-Spam-Virus-Scan"), cstring(`${this.leadingSpace ? " " : ""}failed`)]));
9286
+ this.send(RESPONSES.ADDHEADER, Buffer12.concat([cstring("X-Spam-Virus-Scan"), cstring(`${this.leadingSpace ? " " : ""}failed`)]));
8194
9287
  this.send(RESPONSES.CONTINUE);
8195
9288
  this.resetMessage();
8196
9289
  return;
@@ -8211,15 +9304,15 @@ var MilterSession = class {
8211
9304
  headers.unshift(["Authentication-Results", formatAuthResultsHeader(result.results.authentication, options.hostname)]);
8212
9305
  }
8213
9306
  for (const [name, value] of headers) {
8214
- this.send(RESPONSES.ADDHEADER, Buffer11.concat([cstring(name), cstring(`${this.leadingSpace ? " " : ""}${value}`.replaceAll("\r\n", "\n"))]));
9307
+ this.send(RESPONSES.ADDHEADER, Buffer12.concat([cstring(name), cstring(`${this.leadingSpace ? " " : ""}${value}`.replaceAll("\r\n", "\n"))]));
8215
9308
  }
8216
9309
  if (result.isSpam && options.subjectTag) {
8217
9310
  const subject = this.headers.find(([name]) => name.toLowerCase() === "subject");
8218
9311
  const value = taggedSubject(options.subjectTag, subject ? subject[1].trim() : "", "\n");
8219
9312
  if (value !== null) {
8220
- const index = Buffer11.alloc(4);
9313
+ const index = Buffer12.alloc(4);
8221
9314
  index.writeUInt32BE(1);
8222
- this.send(RESPONSES.CHGHEADER, Buffer11.concat([index, cstring("Subject"), cstring(`${this.leadingSpace ? " " : ""}${value}`.replaceAll("\r\n", "\n"))]));
9315
+ this.send(RESPONSES.CHGHEADER, Buffer12.concat([index, cstring("Subject"), cstring(`${this.leadingSpace ? " " : ""}${value}`.replaceAll("\r\n", "\n"))]));
8223
9316
  }
8224
9317
  }
8225
9318
  if (result.action === "reject" && options.reject) {
@@ -8322,7 +9415,7 @@ var MilterServer = class extends EventEmitter {
8322
9415
  };
8323
9416
 
8324
9417
  // src/spamd.js
8325
- import { Buffer as Buffer12 } from "node:buffer";
9418
+ import { Buffer as Buffer13 } from "node:buffer";
8326
9419
  import net4 from "node:net";
8327
9420
  var EX_OK = 0;
8328
9421
  var EX_USAGE = 64;
@@ -8353,7 +9446,7 @@ function reply(code, text, headers = [], body = null) {
8353
9446
  lines2.push(`Content-length: ${body.length}`);
8354
9447
  }
8355
9448
  lines2.push(...headers.map(([name, value]) => `${name}: ${value}`), "", "");
8356
- return body === null ? Buffer12.from(lines2.join("\r\n")) : Buffer12.concat([Buffer12.from(lines2.join("\r\n")), body]);
9449
+ return body === null ? Buffer13.from(lines2.join("\r\n")) : Buffer13.concat([Buffer13.from(lines2.join("\r\n")), body]);
8357
9450
  }
8358
9451
  function report(result) {
8359
9452
  const lines2 = [
@@ -8398,11 +9491,11 @@ async function answer(scanner, request, message, options) {
8398
9491
  return reply(EX_OK, "EX_OK", [verdict]);
8399
9492
  }
8400
9493
  case "SYMBOLS": {
8401
- return reply(EX_OK, "EX_OK", [verdict], Buffer12.from(result.tests.map((test) => test.name).join(",")));
9494
+ return reply(EX_OK, "EX_OK", [verdict], Buffer13.from(result.tests.map((test) => test.name).join(",")));
8402
9495
  }
8403
9496
  case "REPORT":
8404
9497
  case "REPORT_IFSPAM": {
8405
- return reply(EX_OK, "EX_OK", [verdict], command === "REPORT" || result.isSpam ? Buffer12.from(report(result)) : Buffer12.alloc(0));
9498
+ return reply(EX_OK, "EX_OK", [verdict], command === "REPORT" || result.isSpam ? Buffer13.from(report(result)) : Buffer13.alloc(0));
8406
9499
  }
8407
9500
  default: {
8408
9501
  const rewritten = rewriteMessage(message, spamHeaders(result, { version: VERSION }), { subjectTag: result.isSpam ? options.subjectTag : null });
@@ -8410,7 +9503,7 @@ async function answer(scanner, request, message, options) {
8410
9503
  return reply(EX_OK, "EX_OK", [verdict], rewritten);
8411
9504
  }
8412
9505
  const { header, newline } = splitMessage(rewritten);
8413
- return reply(EX_OK, "EX_OK", [verdict], Buffer12.from(`${header}${newline}${newline}`, "latin1"));
9506
+ return reply(EX_OK, "EX_OK", [verdict], Buffer13.from(`${header}${newline}${newline}`, "latin1"));
8414
9507
  }
8415
9508
  }
8416
9509
  }
@@ -8425,7 +9518,7 @@ function createSpamdServer(scanner, options = {}) {
8425
9518
  ...options
8426
9519
  };
8427
9520
  const server = net4.createServer({ allowHalfOpen: true }, (socket) => {
8428
- let head = Buffer12.alloc(0);
9521
+ let head = Buffer13.alloc(0);
8429
9522
  const chunks = [];
8430
9523
  let size = 0;
8431
9524
  let request = null;
@@ -8451,7 +9544,7 @@ function createSpamdServer(scanner, options = {}) {
8451
9544
  return;
8452
9545
  }
8453
9546
  if (!request) {
8454
- head = Buffer12.concat([head, chunk2]);
9547
+ head = Buffer13.concat([head, chunk2]);
8455
9548
  const end = head.indexOf("\r\n\r\n");
8456
9549
  if (end === -1) {
8457
9550
  if (head.length > 64 * 1024) {
@@ -8486,7 +9579,7 @@ function createSpamdServer(scanner, options = {}) {
8486
9579
  chunks.push(chunk2);
8487
9580
  size += chunk2.length;
8488
9581
  if (request.length === null ? ["PING", "SKIP"].includes(request.command) : size >= request.length) {
8489
- finish(Buffer12.concat(chunks).subarray(0, request.length ?? 0));
9582
+ finish(Buffer13.concat(chunks).subarray(0, request.length ?? 0));
8490
9583
  } else if (size > settings.maxSize) {
8491
9584
  fail(EX_DATAERR, `Message larger than ${settings.maxSize} bytes`);
8492
9585
  }
@@ -8498,7 +9591,7 @@ function createSpamdServer(scanner, options = {}) {
8498
9591
  if (!request) {
8499
9592
  fail(EX_PROTOCOL, "Incomplete request");
8500
9593
  } else if (request.length === null) {
8501
- finish(Buffer12.concat(chunks));
9594
+ finish(Buffer13.concat(chunks));
8502
9595
  } else {
8503
9596
  fail(EX_PROTOCOL, `Message shorter than its Content-length of ${request.length} bytes`);
8504
9597
  }
@@ -8672,21 +9765,21 @@ async function withTimeout(promise, ms, fallback) {
8672
9765
  }
8673
9766
  }
8674
9767
  async function toBuffer(source) {
8675
- if (Buffer13.isBuffer(source)) {
9768
+ if (Buffer14.isBuffer(source)) {
8676
9769
  return source;
8677
9770
  }
8678
9771
  if (typeof source === "string") {
8679
- return Buffer13.from(source, "utf8");
9772
+ return Buffer14.from(source, "utf8");
8680
9773
  }
8681
9774
  if (source instanceof Uint8Array) {
8682
- return Buffer13.from(source);
9775
+ return Buffer14.from(source);
8683
9776
  }
8684
9777
  if (source && typeof source[Symbol.asyncIterator] === "function") {
8685
9778
  const chunks = [];
8686
9779
  for await (const chunk2 of source) {
8687
- chunks.push(Buffer13.isBuffer(chunk2) ? chunk2 : Buffer13.from(chunk2));
9780
+ chunks.push(Buffer14.isBuffer(chunk2) ? chunk2 : Buffer14.from(chunk2));
8688
9781
  }
8689
- return Buffer13.concat(chunks);
9782
+ return Buffer14.concat(chunks);
8690
9783
  }
8691
9784
  throw new TypeError("A message must be a Buffer, a string, a Uint8Array or a readable stream");
8692
9785
  }
@@ -8708,6 +9801,19 @@ var DEFAULTS2 = {
8708
9801
  cloudflare: true,
8709
9802
  adult: true,
8710
9803
  maxHosts: 25,
9804
+ // Also read the links of the whole message and of what it carries
9805
+ // (attached messages, HTML, SVG and XML files, text files, invitations
9806
+ // and contact cards, documents in iframes and data: URLs) when link
9807
+ // hosts are looked up, for at most five seconds: off by default, as it
9808
+ // reads all of it, up to 50 MB of attachments.
9809
+ attachments: false,
9810
+ // Milliseconds the link lookups may take in all (default: timeout).
9811
+ timeout: void 0,
9812
+ // Also look up host names written in the text without a link or a path
9813
+ // ("evil.example"), which mail clients make links of, as far as there is
9814
+ // room: off by default, as a quoted reply or a signature may name any
9815
+ // site (they are checked for lookalikes either way).
9816
+ hostsInText: false,
8711
9817
  // Characters of text read for links (twice as many of HTML): more than
8712
9818
  // for the classifier (maxLength), as a link anywhere in a message may be
8713
9819
  // followed; a longer message sets results.linksTruncated.
@@ -8820,7 +9926,7 @@ function fromDomain(mail) {
8820
9926
  function withHeader(raw) {
8821
9927
  const first = raw.subarray(0, 1e3).toString("latin1");
8822
9928
  const headerless = raw.length > 0 && !/^(?:[!-9;-~]+[ \t]*:|From |[\r\n])/.test(first);
8823
- return headerless ? Buffer13.concat([Buffer13.from("\r\n"), raw]) : raw;
9929
+ return headerless ? Buffer14.concat([Buffer14.from("\r\n"), raw]) : raw;
8824
9930
  }
8825
9931
  var SpamScanner = class {
8826
9932
  /**
@@ -8982,7 +10088,10 @@ var SpamScanner = class {
8982
10088
  async scanFile(file, options) {
8983
10089
  return this.scan(await readFile(file), options);
8984
10090
  }
8985
- async checkLinks(links, config, hosts = [], from = "") {
10091
+ // (`from`: the sender's domain; `carried`: the link hosts of the whole
10092
+ // message and of what it carries, see carriedLinkHosts)
10093
+ async checkLinks(links, config, hosts = [], options = {}) {
10094
+ const { from = "", carried = null } = typeof options === "string" ? { from: options } : options;
8986
10095
  const phishing = [];
8987
10096
  const homographs = [];
8988
10097
  const seenHosts = /* @__PURE__ */ new Set();
@@ -9038,7 +10147,11 @@ var SpamScanner = class {
9038
10147
  for (const host of hosts) {
9039
10148
  lookalike(host, { source: "text" });
9040
10149
  }
9041
- const ordered = [...new Set([...found.filter((link) => link.text), ...found.filter((link) => !link.text), ...bare].map((link) => lookupName(destination(link))).filter(Boolean))];
10150
+ const carriedHosts = (carried?.hosts ?? []).map((host) => lookupName(host)).filter(Boolean);
10151
+ for (const host of carriedHosts) {
10152
+ seenHosts.add(host);
10153
+ }
10154
+ const ordered = [.../* @__PURE__ */ new Set([...[...found.filter((link) => link.text), ...found.filter((link) => !link.text), ...bare].map((link) => lookupName(destination(link))).filter(Boolean), ...carriedHosts])];
9042
10155
  const firsts = /* @__PURE__ */ new Map();
9043
10156
  for (const host of ordered) {
9044
10157
  const domain = registrableDomain(host);
@@ -9050,6 +10163,7 @@ var SpamScanner = class {
9050
10163
  const domains = [...new Set(checked.map((host) => registrableDomain(host)))];
9051
10164
  const blocked = [];
9052
10165
  let answered = 0;
10166
+ const linkTimeout = Number(config.phishing.timeout) > 0 ? Number(config.phishing.timeout) : config.timeout;
9053
10167
  const cloudflare = async () => {
9054
10168
  await withTimeout(Promise.all(checked.map(async (host) => {
9055
10169
  const found2 = await this.dns.checkCloudflare([host], { adult: config.phishing.adult });
@@ -9057,12 +10171,12 @@ var SpamScanner = class {
9057
10171
  if (!found2.unanswered?.length) {
9058
10172
  answered++;
9059
10173
  }
9060
- })), config.timeout, null);
10174
+ })), linkTimeout, null);
9061
10175
  return { found: [...blocked], unanswered: checked.length - answered };
9062
10176
  };
9063
10177
  const [cloudflareResult, listed] = await Promise.all([
9064
10178
  config.phishing.cloudflare && checked.length > 0 ? cloudflare() : { found: [], unanswered: 0 },
9065
- config.dnsbl.domain?.length > 0 && domains.length > 0 ? withTimeout(this.dns.checkDomains(domains, config.dnsbl.domain), config.timeout, []) : []
10179
+ config.dnsbl.domain?.length > 0 && domains.length > 0 ? withTimeout(this.dns.checkDomains(domains, config.dnsbl.domain), linkTimeout, []) : []
9066
10180
  ]);
9067
10181
  for (const { host, category } of cloudflareResult.found) {
9068
10182
  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` }));
@@ -9078,7 +10192,8 @@ var SpamScanner = class {
9078
10192
  return {
9079
10193
  phishing,
9080
10194
  homographs,
9081
- uncheckedHosts: seenHosts.size - checked.length,
10195
+ // (at least one when the message has more hosts than were read)
10196
+ uncheckedHosts: Math.max(seenHosts.size - checked.length, carried?.more ? 1 : 0),
9082
10197
  timedOutHosts: cloudflareResult.unanswered
9083
10198
  };
9084
10199
  }
@@ -9250,10 +10365,17 @@ ${visibleText(html.slice(0, config.maxLength * 2)).text}`, { maxLength: config.m
9250
10365
  styled: plainObfuscation.styled || shownObfuscation.styled
9251
10366
  };
9252
10367
  const unopened = [];
9253
- const attached = await expandAttachments(mail.attachments, parseErrors, void 0, unopened);
10368
+ const opened = [];
10369
+ const attached = await expandAttachments(mail.attachments, parseErrors, void 0, { unopened, mails: opened });
10370
+ const carried = config.phishing.attachments === true && (config.phishing.cloudflare || config.dnsbl.domain?.length > 0) ? carriedLinkHosts(mail, {
10371
+ mails: opened,
10372
+ attachments: attached,
10373
+ maxHosts: config.phishing.maxHosts,
10374
+ hostsInText: config.phishing.hostsInText === true
10375
+ }) : null;
9254
10376
  const attachments = config.attachments === false ? [] : inspectAttachments(attached).filter((item) => config.macros !== false || !["macro", "pdf_active", "rtf_object"].includes(item.type)).map((item) => finding(item));
9255
10377
  const [links, authentication, checkedReputation, dnsbl, virusScan, toxicity, nsfw] = await Promise.all([
9256
- this.checkLinks(checkedLinks, config, extracted.hosts, fromDomain(mail)),
10378
+ this.checkLinks(checkedLinks, config, extracted.hosts, { from: fromDomain(mail), carried }),
9257
10379
  config.authenticationResults && typeof config.authenticationResults === "object" ? normalizeAuthOutput(config.authenticationResults) : config.authentication && session.remoteAddress ? withTimeout(authenticate(raw, {
9258
10380
  timeout: config.timeout,
9259
10381
  ...typeof config.authentication === "object" ? config.authentication : {},
@@ -9381,6 +10503,14 @@ ${visibleText(html.slice(0, config.maxLength * 2)).text}`, { maxLength: config.m
9381
10503
  // Whether the message has more text than phishing.maxLength, whose
9382
10504
  // links were not read.
9383
10505
  linksTruncated,
10506
+ // Whether a link host of the message or of what it carries was
10507
+ // never read whole (it runs past where long text is cut to be
10508
+ // read, it goes through more security gateways than are
10509
+ // unwrapped, or it is in more documents than are read), and whether
10510
+ // an attachment was left unread for time: in either case not every
10511
+ // link was checked (see phishing.attachments).
10512
+ linksTooComplex: Boolean(carried?.tooComplex),
10513
+ linksIncomplete: Boolean(carried?.incomplete),
9384
10514
  idnHomographAttack: {
9385
10515
  detected: links.homographs.length > 0,
9386
10516
  domains: links.homographs,
@@ -9392,6 +10522,9 @@ ${visibleText(html.slice(0, config.maxLength * 2)).text}`, { maxLength: config.m
9392
10522
  language: extracted.language,
9393
10523
  tokens: extracted.words,
9394
10524
  mail,
10525
+ // (the attachments of the message and of its attached messages, as
10526
+ // read, an attached message's named after it)
10527
+ attached,
9395
10528
  version: VERSION,
9396
10529
  metrics: { totalTime: time }
9397
10530
  };