spamscanner 7.0.10 → 7.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/arf.cjs.map +1 -1
- package/dist/cjs/index.cjs +1456 -191
- package/dist/cjs/index.cjs.map +4 -4
- package/dist/esm/arf.js.map +1 -1
- package/dist/esm/cli.js +1392 -142
- package/dist/esm/cli.js.map +4 -4
- package/dist/esm/index.js +1423 -174
- package/dist/esm/index.js.map +4 -4
- package/dist/types/attachments.d.ts +1 -0
- package/dist/types/entities.d.ts +2 -0
- package/dist/types/html.d.ts +2 -2
- package/dist/types/index.d.ts +26 -6
- package/dist/types/links.d.ts +26 -0
- package/dist/types/parse.d.ts +1 -1
- package/dist/types/tokenizer.d.ts +9 -0
- package/package.json +1 -1
package/dist/esm/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/index.js
|
|
2
|
-
import { Buffer as
|
|
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=27e8,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=27e9,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@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 &&
|
|
1225
|
-
return
|
|
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
|
|
1421
|
+
return NAMED_REFERENCES.get(legacy) + rest;
|
|
1240
1422
|
});
|
|
1241
1423
|
}
|
|
1242
1424
|
var UNSHOWN = /* @__PURE__ */ new Set(["script", "style", "title", "noembed", "noframes", "iframe"]);
|
|
@@ -2083,6 +2265,34 @@ function continuesName(text, index) {
|
|
|
2083
2265
|
var FILE_TLDS = /* @__PURE__ */ new Set(["ai", "app", "md", "mov", "pm", "ps", "py", "rs", "sh", "so", "zip"]);
|
|
2084
2266
|
var LINK_FILE_TLDS = /* @__PURE__ */ new Set(["md", "mov", "pm", "ps", "py", "rs", "sh", "so", "zip"]);
|
|
2085
2267
|
var CLICK_TRACKERS = ["sendgrid.net", "list-manage.com", "mandrillapp.com", "awstrack.me", "pstmrk.it", "hubspotlinks.com", "rs6.net", "klclick.com", "mjt.lu", "exacttarget.com", "convertkit-mail.com", "convertkit-mail2.com", "kit-mail3.com", "sendibt2.com", "sendibt3.com", "createsend1.com", "cmail20.com", "clicks.mlsend.com", "acemlnb.com", "link.mail.beehiiv.com", "bhclick.com", "safelinks.protection.outlook.com", "urldefense.com", "urldefense.proofpoint.com", "mimecast.com", "mimecastprotect.com", "cudasvc.com", "secure-web.cisco.com"];
|
|
2268
|
+
var MAX_URL_HOST = 4096;
|
|
2269
|
+
var AUTHORITY_END = /[/\\?#]/g;
|
|
2270
|
+
var IGNORED_IN_HOST = /[\u00AD\u034F\u180B-\u180D\u180F\u200B\u2060\u2064\uFE00-\uFE0F\uFEFF]|\uD82F[\uDCA0-\uDCA3]|\uDB40[\uDD00-\uDDEF]/g;
|
|
2271
|
+
function boundedUrl(address2) {
|
|
2272
|
+
const text = String(address2).replaceAll(/[\t\n\r]/g, "");
|
|
2273
|
+
const colon = text.indexOf(":");
|
|
2274
|
+
let start = colon === -1 ? 0 : colon + 1;
|
|
2275
|
+
while (start < text.length && (text[start] === "/" || text[start] === "\\")) {
|
|
2276
|
+
start++;
|
|
2277
|
+
}
|
|
2278
|
+
AUTHORITY_END.lastIndex = start;
|
|
2279
|
+
const end = AUTHORITY_END.exec(text)?.index ?? text.length;
|
|
2280
|
+
const file = /^[\0- ]*file:/i.test(text);
|
|
2281
|
+
const at = file ? start : Math.max(start, text.lastIndexOf("@", end - 1) + 1);
|
|
2282
|
+
const bracket = text.lastIndexOf("]", end - 1);
|
|
2283
|
+
const port = text.lastIndexOf(":", end - 1);
|
|
2284
|
+
let host = text.slice(at, !file && port > Math.max(at - 1, bracket) ? port : end);
|
|
2285
|
+
if (host.length > MAX_URL_HOST && host.includes("%")) {
|
|
2286
|
+
try {
|
|
2287
|
+
host = decodeURIComponent(host);
|
|
2288
|
+
} catch {
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
if (host.length > MAX_URL_HOST && host.replaceAll(IGNORED_IN_HOST, "").length > MAX_URL_HOST) {
|
|
2292
|
+
throw new TypeError("Invalid URL: host too long");
|
|
2293
|
+
}
|
|
2294
|
+
return new URL(text);
|
|
2295
|
+
}
|
|
2086
2296
|
var isUnder = (host, domain) => host === domain || host.endsWith(`.${domain}`);
|
|
2087
2297
|
function isClickTracker(url) {
|
|
2088
2298
|
const host = url.hostname.toLowerCase();
|
|
@@ -2111,7 +2321,7 @@ var MAX_GATEWAYS = 16;
|
|
|
2111
2321
|
function unwrapLink(address2) {
|
|
2112
2322
|
let url;
|
|
2113
2323
|
try {
|
|
2114
|
-
url =
|
|
2324
|
+
url = boundedUrl(address2);
|
|
2115
2325
|
} catch {
|
|
2116
2326
|
return null;
|
|
2117
2327
|
}
|
|
@@ -2122,7 +2332,7 @@ function unwrapLink(address2) {
|
|
|
2122
2332
|
}
|
|
2123
2333
|
let next;
|
|
2124
2334
|
try {
|
|
2125
|
-
next =
|
|
2335
|
+
next = boundedUrl(target);
|
|
2126
2336
|
} catch {
|
|
2127
2337
|
break;
|
|
2128
2338
|
}
|
|
@@ -2382,7 +2592,7 @@ function endsInsideTag(html, end, link) {
|
|
|
2382
2592
|
return joined !== host && (!isHost(host) || isHost(joined) && registrableDomain(joined) !== registrableDomain(host));
|
|
2383
2593
|
}
|
|
2384
2594
|
var MAX_SHOWN_HTML = 2e5;
|
|
2385
|
-
function extractLinks(text, html, { maxLength = 5e5, bareHosts = false } = {}) {
|
|
2595
|
+
function extractLinks(text, html, { maxLength = 5e5, bareHosts: bareHosts2 = false } = {}) {
|
|
2386
2596
|
text = typeof text === "string" ? foldToAscii(text) : text;
|
|
2387
2597
|
html = typeof html === "string" ? foldToAscii(html) : html;
|
|
2388
2598
|
const links = [];
|
|
@@ -2400,7 +2610,7 @@ function extractLinks(text, html, { maxLength = 5e5, bareHosts = false } = {}) {
|
|
|
2400
2610
|
}
|
|
2401
2611
|
let url;
|
|
2402
2612
|
try {
|
|
2403
|
-
url =
|
|
2613
|
+
url = boundedUrl(href);
|
|
2404
2614
|
} catch {
|
|
2405
2615
|
return;
|
|
2406
2616
|
}
|
|
@@ -2449,7 +2659,7 @@ ${shown || ""}`;
|
|
|
2449
2659
|
}
|
|
2450
2660
|
add(link);
|
|
2451
2661
|
}
|
|
2452
|
-
if (
|
|
2662
|
+
if (bareHosts2 && index < 2) {
|
|
2453
2663
|
bareLinkHosts(read, (host) => {
|
|
2454
2664
|
bareFound.push(host);
|
|
2455
2665
|
});
|
|
@@ -2725,7 +2935,7 @@ function isDeceptiveLink(link, { from } = {}) {
|
|
|
2725
2935
|
}
|
|
2726
2936
|
let shownUrl;
|
|
2727
2937
|
try {
|
|
2728
|
-
shownUrl =
|
|
2938
|
+
shownUrl = boundedUrl(/^https?:\/\//i.test(link.text) ? link.text : `http://${link.text}`);
|
|
2729
2939
|
} catch {
|
|
2730
2940
|
return false;
|
|
2731
2941
|
}
|
|
@@ -3971,7 +4181,8 @@ async function parseWithin(raw, parse = parseMessage, deadline = Date.now() + PA
|
|
|
3971
4181
|
}
|
|
3972
4182
|
}
|
|
3973
4183
|
}
|
|
3974
|
-
async function expandAttachments(attachments = [], errors = [], parse = parseMessage,
|
|
4184
|
+
async function expandAttachments(attachments = [], errors = [], parse = parseMessage, gets = {}) {
|
|
4185
|
+
const { unopened = [], mails = [] } = Array.isArray(gets) ? { unopened: gets } : gets ?? {};
|
|
3975
4186
|
const all = [];
|
|
3976
4187
|
const deadline = Date.now() + PARSE_TIMEOUT;
|
|
3977
4188
|
let budget = MAX_NESTED_BYTES;
|
|
@@ -4020,6 +4231,7 @@ async function expandAttachments(attachments = [], errors = [], parse = parseMes
|
|
|
4020
4231
|
if (inner.error) {
|
|
4021
4232
|
errors.push(inner.error);
|
|
4022
4233
|
}
|
|
4234
|
+
mails.push(inner.mail);
|
|
4023
4235
|
await visit(inner.mail.attachments, prefix ? `${prefix}/${name}` : name || "attached message", depth + 1);
|
|
4024
4236
|
}
|
|
4025
4237
|
}
|
|
@@ -4038,6 +4250,11 @@ import { isIP } from "node:net";
|
|
|
4038
4250
|
import { domainToASCII } from "node:url";
|
|
4039
4251
|
import { getPublicSuffix } from "tldts";
|
|
4040
4252
|
var LOOKUP_LABEL = /^[a-z\d_-]{1,63}$/;
|
|
4253
|
+
var LABEL_SEPARATOR = /[.\u3002\uFF0E\uFF61]/g;
|
|
4254
|
+
function lastLabels(name) {
|
|
4255
|
+
const separators = [...name.matchAll(LABEL_SEPARATOR)];
|
|
4256
|
+
return separators.length < 128 ? 0 : separators.at(-128).index + 1;
|
|
4257
|
+
}
|
|
4041
4258
|
function lookupName(host) {
|
|
4042
4259
|
if (typeof host !== "string") {
|
|
4043
4260
|
return null;
|
|
@@ -4048,6 +4265,7 @@ function lookupName(host) {
|
|
|
4048
4265
|
}
|
|
4049
4266
|
let name = host.slice(0, end).toLowerCase();
|
|
4050
4267
|
if (/[\u0080-\u{10FFFF}]/u.test(name)) {
|
|
4268
|
+
name = name.slice(lastLabels(name));
|
|
4051
4269
|
name = domainToASCII(name) || name;
|
|
4052
4270
|
}
|
|
4053
4271
|
if (isIP(name.replaceAll(/^\[|]$/g, "")) !== 0) {
|
|
@@ -4387,7 +4605,7 @@ function asciiHostname(value) {
|
|
|
4387
4605
|
return null;
|
|
4388
4606
|
}
|
|
4389
4607
|
const lower = value.normalize("NFKC").toLowerCase().replace(/\.$/, "");
|
|
4390
|
-
if (lower === "" || /[\0-,/:-^`{-\u007F]/.test(lower)) {
|
|
4608
|
+
if (lower === "" || lower.length > 1024 || /[\0-,/:-^`{-\u007F]/.test(lower)) {
|
|
4391
4609
|
return null;
|
|
4392
4610
|
}
|
|
4393
4611
|
const ascii = domainToASCII2(lower);
|
|
@@ -4569,8 +4787,8 @@ function trustReputation(result, trusted = []) {
|
|
|
4569
4787
|
for (const value of result.checkedValues.filter((item) => allowed.has(item))) {
|
|
4570
4788
|
const signers = allowed.get(value);
|
|
4571
4789
|
const counts = (entry) => Boolean(entry) && (signers === null || signers.some((signer) => vouches(normalizeEntry(entry), signer)));
|
|
4572
|
-
const detail = result.details[value];
|
|
4573
|
-
const listed = result.allowlisted[value] || (detail?.isAllowlisted ? detail.allowlistValue : null);
|
|
4790
|
+
const detail = Object.hasOwn(result.details, value) ? result.details[value] : void 0;
|
|
4791
|
+
const listed = Object.hasOwn(result.allowlisted, value) && result.allowlisted[value] || (detail?.isAllowlisted ? detail.allowlistValue : null);
|
|
4574
4792
|
if (counts(listed) && !out.isAllowlisted) {
|
|
4575
4793
|
out.isAllowlisted = true;
|
|
4576
4794
|
out.allowlistValue = listed;
|
|
@@ -5436,8 +5654,990 @@ var Classifier = class _Classifier {
|
|
|
5436
5654
|
}
|
|
5437
5655
|
};
|
|
5438
5656
|
|
|
5439
|
-
// src/
|
|
5657
|
+
// src/links.js
|
|
5658
|
+
import { Buffer as Buffer7 } from "node:buffer";
|
|
5659
|
+
import { isIP as isIP3 } from "node:net";
|
|
5440
5660
|
import { parse as parseDomain3 } from "tldts";
|
|
5661
|
+
var LINK_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
5662
|
+
"href",
|
|
5663
|
+
"src",
|
|
5664
|
+
"srcset",
|
|
5665
|
+
"background",
|
|
5666
|
+
"action",
|
|
5667
|
+
"formaction",
|
|
5668
|
+
"poster",
|
|
5669
|
+
"cite",
|
|
5670
|
+
"longdesc",
|
|
5671
|
+
"xlink:href",
|
|
5672
|
+
"style",
|
|
5673
|
+
"data",
|
|
5674
|
+
"ping",
|
|
5675
|
+
// (of a <meta> refresh: "0;url=https://example.com/")
|
|
5676
|
+
"content",
|
|
5677
|
+
// (a document of its own, see linksOf)
|
|
5678
|
+
"srcdoc"
|
|
5679
|
+
]);
|
|
5680
|
+
var TAG_START = /<[a-z][^\s/>]*/gi;
|
|
5681
|
+
var URL_START = /\b(?:https?:\/\/|www\.)[^\s<>"'`]+/gi;
|
|
5682
|
+
var CSS_URL = /url\(\s*["']?([^()"']*)/gi;
|
|
5683
|
+
var CSS_IMPORT = /@import\s+["']([^"'<>]*)/gi;
|
|
5684
|
+
var LENIENT = /[\s"'/<](href|src|srcset|background|action|formaction|poster|cite|longdesc|xlink:href|style|data|ping|content|srcdoc)\s*=\s*/gi;
|
|
5685
|
+
var IN_WORD2 = /[\p{L}\p{N}\p{M}_%]/u;
|
|
5686
|
+
var NAMESPACES = /* @__PURE__ */ new Set(["www.w3.org", "w3.org"]);
|
|
5687
|
+
var MAX_HOST = 760;
|
|
5688
|
+
var MAX_SEEN = 1e5;
|
|
5689
|
+
var TRAILING = /* @__PURE__ */ new Set([")", ".", ",", ";", ":", "!", "?", "'", '"', "]"]);
|
|
5690
|
+
var DECODE_CHUNK = 1024 * 1024;
|
|
5691
|
+
var CHUNK_OVERLAP = 4096;
|
|
5692
|
+
var MAX_EMBED_DEPTH = 3;
|
|
5693
|
+
var MAX_EMBEDDED = 1e3;
|
|
5694
|
+
var MAX_EMBEDDED_LENGTH = 10 * 1024 * 1024;
|
|
5695
|
+
var MAX_ATTACHED = 100;
|
|
5696
|
+
var MAX_ATTACHED_BYTES = 50 * 1024 * 1024;
|
|
5697
|
+
var MAX_READ_TIME = 5e3;
|
|
5698
|
+
var MAX_CARD_BYTES = 4 * 1024 * 1024;
|
|
5699
|
+
var MAX_SHOWN_BYTES = 1024 * 1024;
|
|
5700
|
+
function isHostCode2(code) {
|
|
5701
|
+
return code >= 97 && code <= 122 || code >= 65 && code <= 90 || code >= 48 && code <= 57 || code === 45 || code === 46;
|
|
5702
|
+
}
|
|
5703
|
+
function isSpace(code) {
|
|
5704
|
+
return code === 32 || code >= 9 && code <= 13;
|
|
5705
|
+
}
|
|
5706
|
+
function urlOf(value) {
|
|
5707
|
+
let href = value.replaceAll(/[\t\n\r]/g, "");
|
|
5708
|
+
let start = 0;
|
|
5709
|
+
let end = href.length;
|
|
5710
|
+
while (start < end && href.codePointAt(start) <= 32) {
|
|
5711
|
+
start++;
|
|
5712
|
+
}
|
|
5713
|
+
while (end > start && href.codePointAt(end - 1) <= 32) {
|
|
5714
|
+
end--;
|
|
5715
|
+
}
|
|
5716
|
+
while (end > start && TRAILING.has(href[end - 1])) {
|
|
5717
|
+
end--;
|
|
5718
|
+
}
|
|
5719
|
+
href = href.slice(start, end);
|
|
5720
|
+
if (/^[\\/]{2}/.test(href)) {
|
|
5721
|
+
href = `https://${href.slice(2)}`;
|
|
5722
|
+
} else if (/^www\./i.test(href)) {
|
|
5723
|
+
href = `http://${href}`;
|
|
5724
|
+
} else if (!/^https?:/i.test(href)) {
|
|
5725
|
+
return null;
|
|
5726
|
+
}
|
|
5727
|
+
try {
|
|
5728
|
+
return boundedUrl(href);
|
|
5729
|
+
} catch {
|
|
5730
|
+
return null;
|
|
5731
|
+
}
|
|
5732
|
+
}
|
|
5733
|
+
function hostOfUrl(url) {
|
|
5734
|
+
let host = url.hostname.toLowerCase();
|
|
5735
|
+
let dots = host.length;
|
|
5736
|
+
while (dots > 0 && host[dots - 1] === ".") {
|
|
5737
|
+
dots--;
|
|
5738
|
+
}
|
|
5739
|
+
host = host.slice(0, dots);
|
|
5740
|
+
if (host.startsWith("[") && host.endsWith("]")) {
|
|
5741
|
+
host = host.slice(1, -1);
|
|
5742
|
+
}
|
|
5743
|
+
if (!host || NAMESPACES.has(host)) {
|
|
5744
|
+
return null;
|
|
5745
|
+
}
|
|
5746
|
+
return isIP3(host) ? host : lookupName(host);
|
|
5747
|
+
}
|
|
5748
|
+
function hostsOf(value) {
|
|
5749
|
+
const url = urlOf(value);
|
|
5750
|
+
if (!url) {
|
|
5751
|
+
return [];
|
|
5752
|
+
}
|
|
5753
|
+
const host = hostOfUrl(url);
|
|
5754
|
+
const hosts = host ? [host] : [];
|
|
5755
|
+
const target = unwrapLink(url.href);
|
|
5756
|
+
if (target && target.href !== url.href) {
|
|
5757
|
+
const targetHost = hostOfUrl(target);
|
|
5758
|
+
if (targetHost && targetHost !== host) {
|
|
5759
|
+
hosts.push(targetHost);
|
|
5760
|
+
}
|
|
5761
|
+
const further = unwrapLink(target.href);
|
|
5762
|
+
if (further && further.href !== target.href) {
|
|
5763
|
+
hosts.unresolved = true;
|
|
5764
|
+
}
|
|
5765
|
+
}
|
|
5766
|
+
return hosts;
|
|
5767
|
+
}
|
|
5768
|
+
function* attributesOf(html) {
|
|
5769
|
+
TAG_START.lastIndex = 0;
|
|
5770
|
+
let match;
|
|
5771
|
+
while ((match = TAG_START.exec(html)) !== null) {
|
|
5772
|
+
let i = match.index + match[0].length;
|
|
5773
|
+
while (i < html.length && html[i] !== ">") {
|
|
5774
|
+
if (isSpace(html.codePointAt(i)) || html[i] === "/") {
|
|
5775
|
+
i++;
|
|
5776
|
+
continue;
|
|
5777
|
+
}
|
|
5778
|
+
let j = i;
|
|
5779
|
+
while (j < html.length && html[j] !== "=" && html[j] !== ">" && html[j] !== "/" && !isSpace(html.codePointAt(j))) {
|
|
5780
|
+
j++;
|
|
5781
|
+
}
|
|
5782
|
+
const name = html.slice(i, j).toLowerCase();
|
|
5783
|
+
if (j === i) {
|
|
5784
|
+
j++;
|
|
5785
|
+
}
|
|
5786
|
+
while (j < html.length && isSpace(html.codePointAt(j))) {
|
|
5787
|
+
j++;
|
|
5788
|
+
}
|
|
5789
|
+
if (html[j] !== "=") {
|
|
5790
|
+
i = j;
|
|
5791
|
+
continue;
|
|
5792
|
+
}
|
|
5793
|
+
j++;
|
|
5794
|
+
while (j < html.length && isSpace(html.codePointAt(j))) {
|
|
5795
|
+
j++;
|
|
5796
|
+
}
|
|
5797
|
+
let value;
|
|
5798
|
+
if (html[j] === '"' || html[j] === "'") {
|
|
5799
|
+
const close = html.indexOf(html[j], j + 1);
|
|
5800
|
+
const stop = close === -1 ? html.length : close;
|
|
5801
|
+
value = html.slice(j + 1, stop);
|
|
5802
|
+
j = stop + 1;
|
|
5803
|
+
} else {
|
|
5804
|
+
const start = j;
|
|
5805
|
+
while (j < html.length && html[j] !== ">" && !isSpace(html.codePointAt(j))) {
|
|
5806
|
+
j++;
|
|
5807
|
+
}
|
|
5808
|
+
value = html.slice(start, j);
|
|
5809
|
+
}
|
|
5810
|
+
if (name) {
|
|
5811
|
+
yield [name, value];
|
|
5812
|
+
}
|
|
5813
|
+
i = j;
|
|
5814
|
+
}
|
|
5815
|
+
TAG_START.lastIndex = Math.max(i, match.index + 1);
|
|
5816
|
+
}
|
|
5817
|
+
}
|
|
5818
|
+
var MAX_VALUE2 = 1024 * 1024;
|
|
5819
|
+
var LONG_VALUE = 64 * 1024;
|
|
5820
|
+
function linksOf(name, value, { add, embed, cut }) {
|
|
5821
|
+
if (value.length > MAX_VALUE2) {
|
|
5822
|
+
cut();
|
|
5823
|
+
value = value.slice(0, MAX_VALUE2);
|
|
5824
|
+
}
|
|
5825
|
+
const decoded = decodeEntities(value, { attribute: true });
|
|
5826
|
+
switch (name) {
|
|
5827
|
+
case "srcdoc": {
|
|
5828
|
+
embed(decoded);
|
|
5829
|
+
break;
|
|
5830
|
+
}
|
|
5831
|
+
case "style": {
|
|
5832
|
+
for (const [, link] of decoded.matchAll(CSS_URL)) {
|
|
5833
|
+
add(link);
|
|
5834
|
+
}
|
|
5835
|
+
break;
|
|
5836
|
+
}
|
|
5837
|
+
case "srcset": {
|
|
5838
|
+
for (const candidate of decoded.split(",")) {
|
|
5839
|
+
add(candidate.trim().split(/\s/, 1)[0]);
|
|
5840
|
+
}
|
|
5841
|
+
break;
|
|
5842
|
+
}
|
|
5843
|
+
// (several, separated by spaces)
|
|
5844
|
+
case "ping": {
|
|
5845
|
+
for (const link of decoded.split(/\s+/)) {
|
|
5846
|
+
add(link);
|
|
5847
|
+
}
|
|
5848
|
+
break;
|
|
5849
|
+
}
|
|
5850
|
+
// (only the address of a refresh)
|
|
5851
|
+
case "content": {
|
|
5852
|
+
const match = /(?:^|[\s;,])url\s*=\s*["']?([^\s"';]+)/i.exec(decoded.slice(0, 4096));
|
|
5853
|
+
if (match) {
|
|
5854
|
+
add(match[1]);
|
|
5855
|
+
}
|
|
5856
|
+
break;
|
|
5857
|
+
}
|
|
5858
|
+
default: {
|
|
5859
|
+
add(decoded);
|
|
5860
|
+
}
|
|
5861
|
+
}
|
|
5862
|
+
}
|
|
5863
|
+
var MAX_LENIENT_VALUE = 4096;
|
|
5864
|
+
var UNQUOTED_END = /* @__PURE__ */ new Set(["<", ">", '"', "'", "`", "="]);
|
|
5865
|
+
function lenientLinks(html, done, reader) {
|
|
5866
|
+
LENIENT.lastIndex = 0;
|
|
5867
|
+
let match;
|
|
5868
|
+
while (!done() && (match = LENIENT.exec(html)) !== null) {
|
|
5869
|
+
const name = match[1].toLowerCase();
|
|
5870
|
+
let i = LENIENT.lastIndex;
|
|
5871
|
+
let end = i;
|
|
5872
|
+
if (html[i] === '"' || html[i] === "'") {
|
|
5873
|
+
const quote = html[i];
|
|
5874
|
+
i++;
|
|
5875
|
+
end = i;
|
|
5876
|
+
while (end < html.length && html[end] !== quote && html[end] !== "<" && html[end] !== ">") {
|
|
5877
|
+
end++;
|
|
5878
|
+
}
|
|
5879
|
+
if (end < html.length && html[end] !== quote) {
|
|
5880
|
+
const close = html.indexOf(quote, end);
|
|
5881
|
+
if (close !== -1 && close - i <= MAX_LENIENT_VALUE && !reader.spend(close - i)) {
|
|
5882
|
+
linksOf(name, html.slice(i, close), reader);
|
|
5883
|
+
}
|
|
5884
|
+
}
|
|
5885
|
+
} else {
|
|
5886
|
+
while (end < html.length && !isSpace(html.codePointAt(end)) && !UNQUOTED_END.has(html[end])) {
|
|
5887
|
+
end++;
|
|
5888
|
+
}
|
|
5889
|
+
}
|
|
5890
|
+
if (reader.spend(end - i)) {
|
|
5891
|
+
break;
|
|
5892
|
+
}
|
|
5893
|
+
linksOf(name, html.slice(i, end), reader);
|
|
5894
|
+
}
|
|
5895
|
+
}
|
|
5896
|
+
function endsLink2(text, i) {
|
|
5897
|
+
const code = text.codePointAt(i);
|
|
5898
|
+
if (code < 128) {
|
|
5899
|
+
return isSpace(code) || code === 34 || code === 39 || code === 60 || code === 62 || code === 96;
|
|
5900
|
+
}
|
|
5901
|
+
return /\s/u.test(text[i]);
|
|
5902
|
+
}
|
|
5903
|
+
function linkReader(text) {
|
|
5904
|
+
let read = 0;
|
|
5905
|
+
let space = -1;
|
|
5906
|
+
let scheme = -1;
|
|
5907
|
+
return (start) => {
|
|
5908
|
+
for (; read < start; read++) {
|
|
5909
|
+
if (endsLink2(text, read)) {
|
|
5910
|
+
space = read;
|
|
5911
|
+
} else if (text[read] === "/" && text[read - 1] === "/" && text[read - 2] === ":") {
|
|
5912
|
+
scheme = read;
|
|
5913
|
+
}
|
|
5914
|
+
}
|
|
5915
|
+
return scheme > space;
|
|
5916
|
+
};
|
|
5917
|
+
}
|
|
5918
|
+
function isSchemeless(text, start) {
|
|
5919
|
+
if (start < 2 || text[start - 2] !== "/") {
|
|
5920
|
+
return false;
|
|
5921
|
+
}
|
|
5922
|
+
if (start === 2) {
|
|
5923
|
+
return true;
|
|
5924
|
+
}
|
|
5925
|
+
const before = text[start - 3];
|
|
5926
|
+
return !(before === ":" || before === "/" || isHostCode2(text.codePointAt(start - 3)));
|
|
5927
|
+
}
|
|
5928
|
+
function bareName(name) {
|
|
5929
|
+
const labels = name.split(".");
|
|
5930
|
+
const tld = labels.pop();
|
|
5931
|
+
if (!/^(?:[a-z]{2,24}|xn--[a-z\d-]{1,59})$/i.test(tld)) {
|
|
5932
|
+
return null;
|
|
5933
|
+
}
|
|
5934
|
+
const names = [];
|
|
5935
|
+
while (labels.length > 0 && names.length < 11) {
|
|
5936
|
+
const label = labels.pop();
|
|
5937
|
+
if (!/^[a-z\d-]{1,63}$/i.test(label)) {
|
|
5938
|
+
break;
|
|
5939
|
+
}
|
|
5940
|
+
names.unshift(label);
|
|
5941
|
+
}
|
|
5942
|
+
if (names.length === 0) {
|
|
5943
|
+
return null;
|
|
5944
|
+
}
|
|
5945
|
+
names[0] = names[0].replace(/^-+/, "");
|
|
5946
|
+
if (!names[0]) {
|
|
5947
|
+
return null;
|
|
5948
|
+
}
|
|
5949
|
+
return `${names.join(".")}.${tld}`.toLowerCase();
|
|
5950
|
+
}
|
|
5951
|
+
function isRealName(host) {
|
|
5952
|
+
const parsed = parseDomain3(host);
|
|
5953
|
+
return Boolean(parsed.isIcann && parsed.domain);
|
|
5954
|
+
}
|
|
5955
|
+
function bareHosts(text, add, { done, seen, from = 0, cutEnd = false }) {
|
|
5956
|
+
let previous = -1;
|
|
5957
|
+
const inLink = linkReader(text);
|
|
5958
|
+
for (let slash = text.indexOf("/"); slash !== -1; slash = text.indexOf("/", slash + 1)) {
|
|
5959
|
+
if (done()) {
|
|
5960
|
+
return;
|
|
5961
|
+
}
|
|
5962
|
+
const floor = Math.max(previous + 1, slash - MAX_HOST);
|
|
5963
|
+
previous = slash;
|
|
5964
|
+
let end = slash;
|
|
5965
|
+
while (end > floor && text.codePointAt(end - 1) >= 48 && text.codePointAt(end - 1) <= 57) {
|
|
5966
|
+
end--;
|
|
5967
|
+
}
|
|
5968
|
+
end = end > floor && text[end - 1] === ":" ? end - 1 : slash;
|
|
5969
|
+
let start = end;
|
|
5970
|
+
while (start > floor && isHostCode2(text.codePointAt(start - 1))) {
|
|
5971
|
+
start--;
|
|
5972
|
+
}
|
|
5973
|
+
if (start === end || start < from || cutEnd && slash === text.length - 1) {
|
|
5974
|
+
continue;
|
|
5975
|
+
}
|
|
5976
|
+
if (start > 0 && (IN_WORD2.test(text[start - 1]) || text[start - 1] === "/" && !isSchemeless(text, start)) || inLink(start)) {
|
|
5977
|
+
continue;
|
|
5978
|
+
}
|
|
5979
|
+
while (end > start && text[end - 1] === ".") {
|
|
5980
|
+
end--;
|
|
5981
|
+
}
|
|
5982
|
+
const host = bareName(text.slice(start, end));
|
|
5983
|
+
if (!host || seen.has(host)) {
|
|
5984
|
+
continue;
|
|
5985
|
+
}
|
|
5986
|
+
if (seen.size < MAX_SEEN) {
|
|
5987
|
+
seen.add(host);
|
|
5988
|
+
}
|
|
5989
|
+
if (isRealName(host)) {
|
|
5990
|
+
add(`http://${host}/`);
|
|
5991
|
+
}
|
|
5992
|
+
}
|
|
5993
|
+
}
|
|
5994
|
+
function looseHosts(text, add, { from = 0, cutEnd = false, seen, done }) {
|
|
5995
|
+
const inLink = linkReader(text);
|
|
5996
|
+
const { length } = text;
|
|
5997
|
+
let i = 0;
|
|
5998
|
+
while (i < length) {
|
|
5999
|
+
if (!isHostCode2(text.codePointAt(i))) {
|
|
6000
|
+
i++;
|
|
6001
|
+
continue;
|
|
6002
|
+
}
|
|
6003
|
+
const start = i;
|
|
6004
|
+
while (i < length && isHostCode2(text.codePointAt(i))) {
|
|
6005
|
+
i++;
|
|
6006
|
+
}
|
|
6007
|
+
let end = i;
|
|
6008
|
+
if (end - start > MAX_HOST || start < from || cutEnd && end === length) {
|
|
6009
|
+
continue;
|
|
6010
|
+
}
|
|
6011
|
+
while (end > start && text[end - 1] === ".") {
|
|
6012
|
+
end--;
|
|
6013
|
+
}
|
|
6014
|
+
const before = start > 0 ? text[start - 1] : "";
|
|
6015
|
+
const after = end < length ? text[end] : "";
|
|
6016
|
+
if (after === "/" || after === "@" || before === "@") {
|
|
6017
|
+
continue;
|
|
6018
|
+
}
|
|
6019
|
+
if (before && (IN_WORD2.test(before) || before === "/" || before === "\\" || before === "&" || before === "#")) {
|
|
6020
|
+
continue;
|
|
6021
|
+
}
|
|
6022
|
+
if (after && after !== "." && IN_WORD2.test(after)) {
|
|
6023
|
+
continue;
|
|
6024
|
+
}
|
|
6025
|
+
const name = text.slice(start, end);
|
|
6026
|
+
if (!name.includes(".") || inLink(start)) {
|
|
6027
|
+
continue;
|
|
6028
|
+
}
|
|
6029
|
+
const lower = name.toLowerCase();
|
|
6030
|
+
if (seen.has(lower)) {
|
|
6031
|
+
continue;
|
|
6032
|
+
}
|
|
6033
|
+
if (seen.size < MAX_SEEN) {
|
|
6034
|
+
seen.add(lower);
|
|
6035
|
+
}
|
|
6036
|
+
if (done()) {
|
|
6037
|
+
return;
|
|
6038
|
+
}
|
|
6039
|
+
const host = bareName(name);
|
|
6040
|
+
if (host && isRealName(host)) {
|
|
6041
|
+
add(host);
|
|
6042
|
+
}
|
|
6043
|
+
}
|
|
6044
|
+
}
|
|
6045
|
+
function* chunksOf(html) {
|
|
6046
|
+
let start = 0;
|
|
6047
|
+
let cutStart = false;
|
|
6048
|
+
while (start < html.length) {
|
|
6049
|
+
const end = Math.min(html.length, start + DECODE_CHUNK);
|
|
6050
|
+
if (end < html.length) {
|
|
6051
|
+
let cut = end;
|
|
6052
|
+
while (cut > end - 65536 && cut > start) {
|
|
6053
|
+
if (html[cut] === "<" || isSpace(html.codePointAt(cut))) {
|
|
6054
|
+
break;
|
|
6055
|
+
}
|
|
6056
|
+
cut--;
|
|
6057
|
+
}
|
|
6058
|
+
if (cut > start && cut > end - 65536) {
|
|
6059
|
+
yield { text: html.slice(start, cut), cutStart, cutEnd: false };
|
|
6060
|
+
start = cut;
|
|
6061
|
+
cutStart = false;
|
|
6062
|
+
continue;
|
|
6063
|
+
}
|
|
6064
|
+
let hard = end;
|
|
6065
|
+
while (hard > start + CHUNK_OVERLAP + 1 && /[\dA-Za-z#]/.test(html[hard - 1])) {
|
|
6066
|
+
hard--;
|
|
6067
|
+
}
|
|
6068
|
+
if (hard < end && html[hard - 1] === "&" && hard - 1 > start + CHUNK_OVERLAP) {
|
|
6069
|
+
hard--;
|
|
6070
|
+
} else {
|
|
6071
|
+
hard = end;
|
|
6072
|
+
}
|
|
6073
|
+
yield { text: html.slice(start, hard), cutStart, cutEnd: true };
|
|
6074
|
+
start = hard - CHUNK_OVERLAP;
|
|
6075
|
+
cutStart = true;
|
|
6076
|
+
continue;
|
|
6077
|
+
}
|
|
6078
|
+
yield { text: html.slice(start, end), cutStart, cutEnd: false };
|
|
6079
|
+
start = end;
|
|
6080
|
+
}
|
|
6081
|
+
}
|
|
6082
|
+
function withoutNul(text) {
|
|
6083
|
+
return text.includes("\0") ? `${text}
|
|
6084
|
+
${text.replaceAll("\0", "")}` : text;
|
|
6085
|
+
}
|
|
6086
|
+
function hexValue(code) {
|
|
6087
|
+
if (code >= 48 && code <= 57) {
|
|
6088
|
+
return code - 48;
|
|
6089
|
+
}
|
|
6090
|
+
if (code >= 65 && code <= 70) {
|
|
6091
|
+
return code - 55;
|
|
6092
|
+
}
|
|
6093
|
+
if (code >= 97 && code <= 102) {
|
|
6094
|
+
return code - 87;
|
|
6095
|
+
}
|
|
6096
|
+
return -1;
|
|
6097
|
+
}
|
|
6098
|
+
function percentDecoded(text) {
|
|
6099
|
+
const input = Buffer7.from(text);
|
|
6100
|
+
const output = Buffer7.allocUnsafe(input.length);
|
|
6101
|
+
let length = 0;
|
|
6102
|
+
for (let i = 0; i < input.length; i++) {
|
|
6103
|
+
const high = input[i] === 37 ? hexValue(input[i + 1]) : -1;
|
|
6104
|
+
const low = high === -1 ? -1 : hexValue(input[i + 2]);
|
|
6105
|
+
if (low === -1) {
|
|
6106
|
+
output[length++] = input[i];
|
|
6107
|
+
continue;
|
|
6108
|
+
}
|
|
6109
|
+
output[length++] = high * 16 + low;
|
|
6110
|
+
i += 2;
|
|
6111
|
+
}
|
|
6112
|
+
return output.subarray(0, length);
|
|
6113
|
+
}
|
|
6114
|
+
var UTF16LE = /* @__PURE__ */ new Set(["csunicode", "iso-10646-ucs-2", "ucs-2", "unicode", "unicodefeff", "utf-16", "utf-16le"]);
|
|
6115
|
+
var UTF16BE = /* @__PURE__ */ new Set(["unicodefffe", "utf-16be"]);
|
|
6116
|
+
function swapped(bytes) {
|
|
6117
|
+
return Buffer7.from(bytes.subarray(0, bytes.length - bytes.length % 2)).swap16().toString("utf16le");
|
|
6118
|
+
}
|
|
6119
|
+
function decodeText(content, charset = "") {
|
|
6120
|
+
if (content[0] === 255 && content[1] === 254) {
|
|
6121
|
+
return content.subarray(2).toString("utf16le");
|
|
6122
|
+
}
|
|
6123
|
+
if (content[0] === 254 && content[1] === 255) {
|
|
6124
|
+
return swapped(content.subarray(2));
|
|
6125
|
+
}
|
|
6126
|
+
if (UTF16LE.has(charset)) {
|
|
6127
|
+
return content.toString("utf16le");
|
|
6128
|
+
}
|
|
6129
|
+
return UTF16BE.has(charset) ? swapped(content) : content.toString("utf8");
|
|
6130
|
+
}
|
|
6131
|
+
var DATA_DOCUMENT = /^\s*data:\s*(?:text\/html|application\/xhtml\+xml|image\/svg\+xml|text\/xml|application\/xml)\b([^,]*),/i;
|
|
6132
|
+
function dataDocument(value) {
|
|
6133
|
+
const match = DATA_DOCUMENT.exec(value.slice(0, 1024));
|
|
6134
|
+
if (!match) {
|
|
6135
|
+
return null;
|
|
6136
|
+
}
|
|
6137
|
+
const bytes = percentDecoded(value.slice(match[0].length));
|
|
6138
|
+
const content = /;\s*base64\s*$/i.test(match[1]) ? Buffer7.from(bytes.toString("latin1"), "base64") : bytes;
|
|
6139
|
+
const charset = /;\s*charset\s*=\s*"?([^;"\s]+)/i.exec(match[1]);
|
|
6140
|
+
return decodeText(content, charset ? charset[1].toLowerCase() : "");
|
|
6141
|
+
}
|
|
6142
|
+
var LINE_BREAK = /\r\n?|\n/;
|
|
6143
|
+
var FOLD = /(?:\r\n?|\n)[ \t]/g;
|
|
6144
|
+
var QUOTED_PRINTABLE = /;\s*(?:encoding\s*=\s*)?"?quoted-printable"?\s*(?:;|$)/i;
|
|
6145
|
+
var MEDIA_PROPERTIES = /* @__PURE__ */ new Set(["photo", "logo", "sound", "key", "attach"]);
|
|
6146
|
+
var HTML_PROPERTIES = /^(?:x-alt-desc|styled-description|description|comment)[;:]/i;
|
|
6147
|
+
function readEscapes(text, lenient = false) {
|
|
6148
|
+
let index = text.indexOf("\\");
|
|
6149
|
+
if (index === -1) {
|
|
6150
|
+
return text;
|
|
6151
|
+
}
|
|
6152
|
+
let out = "";
|
|
6153
|
+
let parts = [];
|
|
6154
|
+
let start = 0;
|
|
6155
|
+
while (index !== -1 && index + 1 < text.length) {
|
|
6156
|
+
const next = text[index + 1];
|
|
6157
|
+
if (lenient || "nN,;\\".includes(next)) {
|
|
6158
|
+
parts.push(text.slice(start, index), next === "n" || next === "N" ? "\n" : next);
|
|
6159
|
+
start = index + 2;
|
|
6160
|
+
index = text.indexOf("\\", start);
|
|
6161
|
+
if (parts.length >= 65536) {
|
|
6162
|
+
out += parts.join("");
|
|
6163
|
+
parts = [];
|
|
6164
|
+
}
|
|
6165
|
+
} else {
|
|
6166
|
+
index = text.indexOf("\\", index + 1);
|
|
6167
|
+
}
|
|
6168
|
+
}
|
|
6169
|
+
parts.push(text.slice(start));
|
|
6170
|
+
return out + parts.join("");
|
|
6171
|
+
}
|
|
6172
|
+
function valueColon(line) {
|
|
6173
|
+
let colon = line.indexOf(":");
|
|
6174
|
+
let from = 0;
|
|
6175
|
+
for (; ; ) {
|
|
6176
|
+
const quote = line.indexOf('"', from);
|
|
6177
|
+
if (quote === -1 || colon === -1 || colon < quote) {
|
|
6178
|
+
return colon;
|
|
6179
|
+
}
|
|
6180
|
+
const end = line.indexOf('"', quote + 1);
|
|
6181
|
+
if (end === -1) {
|
|
6182
|
+
return -1;
|
|
6183
|
+
}
|
|
6184
|
+
from = end + 1;
|
|
6185
|
+
if (colon < from) {
|
|
6186
|
+
colon = line.indexOf(":", from);
|
|
6187
|
+
}
|
|
6188
|
+
}
|
|
6189
|
+
}
|
|
6190
|
+
function trimSpaceEnd(text) {
|
|
6191
|
+
let end = text.length;
|
|
6192
|
+
while (end > 0 && (text[end - 1] === " " || text[end - 1] === " ")) {
|
|
6193
|
+
end--;
|
|
6194
|
+
}
|
|
6195
|
+
return end === text.length ? text : text.slice(0, end);
|
|
6196
|
+
}
|
|
6197
|
+
function decodeQuotedPrintable(value, charset) {
|
|
6198
|
+
let decoder;
|
|
6199
|
+
try {
|
|
6200
|
+
decoder = new TextDecoder(charset || "utf8");
|
|
6201
|
+
} catch {
|
|
6202
|
+
decoder = new TextDecoder("utf8");
|
|
6203
|
+
}
|
|
6204
|
+
if (!/[\u0080-\u{10FFFF}]/u.test(value)) {
|
|
6205
|
+
const bytes = Buffer7.from(value, "latin1");
|
|
6206
|
+
let length = 0;
|
|
6207
|
+
for (let index = 0; index < bytes.length; index++) {
|
|
6208
|
+
const hex = bytes[index] === 61 ? value.slice(index + 1, index + 3) : "";
|
|
6209
|
+
if (/^[\da-f]{2}$/i.test(hex)) {
|
|
6210
|
+
bytes[length++] = Number.parseInt(hex, 16);
|
|
6211
|
+
index += 2;
|
|
6212
|
+
} else {
|
|
6213
|
+
bytes[length++] = bytes[index];
|
|
6214
|
+
}
|
|
6215
|
+
}
|
|
6216
|
+
return decoder.decode(bytes.subarray(0, length));
|
|
6217
|
+
}
|
|
6218
|
+
return value.replaceAll(/(?:=[\da-f]{2})+/gi, (run) => decoder.decode(Buffer7.from(run.replaceAll("=", ""), "hex")));
|
|
6219
|
+
}
|
|
6220
|
+
function quotedPrintableValues(text) {
|
|
6221
|
+
if (!/quoted-printable/i.test(text)) {
|
|
6222
|
+
return text;
|
|
6223
|
+
}
|
|
6224
|
+
const lines2 = text.split(LINE_BREAK);
|
|
6225
|
+
const out = [];
|
|
6226
|
+
for (let i = 0; i < lines2.length; i++) {
|
|
6227
|
+
const line = lines2[i];
|
|
6228
|
+
const colon = valueColon(line);
|
|
6229
|
+
const parameters = colon === -1 ? "" : line.slice(0, colon);
|
|
6230
|
+
if (colon === -1 || !QUOTED_PRINTABLE.test(parameters)) {
|
|
6231
|
+
out.push(line);
|
|
6232
|
+
continue;
|
|
6233
|
+
}
|
|
6234
|
+
const charset = /;\s*charset\s*=\s*"?([\w.:-]+)/i.exec(parameters)?.[1];
|
|
6235
|
+
const parts = [trimSpaceEnd(line.slice(colon + 1))];
|
|
6236
|
+
while (parts.at(-1).endsWith("=") && i + 1 < lines2.length) {
|
|
6237
|
+
parts[parts.length - 1] = parts.at(-1).slice(0, -1);
|
|
6238
|
+
parts.push(trimSpaceEnd(lines2[++i]));
|
|
6239
|
+
}
|
|
6240
|
+
out.push(line.slice(0, colon + 1) + decodeQuotedPrintable(parts.join(""), charset));
|
|
6241
|
+
}
|
|
6242
|
+
return out.join("\n");
|
|
6243
|
+
}
|
|
6244
|
+
function shownText(text) {
|
|
6245
|
+
const unfolded = text.replaceAll(FOLD, "");
|
|
6246
|
+
const shown = /* @__PURE__ */ new Set([text, readEscapes(unfolded)]);
|
|
6247
|
+
const decoded = quotedPrintableValues(unfolded);
|
|
6248
|
+
if (decoded !== unfolded) {
|
|
6249
|
+
shown.add(readEscapes(decoded));
|
|
6250
|
+
if (unfolded !== text) {
|
|
6251
|
+
shown.add(quotedPrintableValues(text));
|
|
6252
|
+
}
|
|
6253
|
+
}
|
|
6254
|
+
if (/\\[^nN,;\\]/.test(decoded)) {
|
|
6255
|
+
shown.add(readEscapes(decoded, true));
|
|
6256
|
+
}
|
|
6257
|
+
return [...shown].reverse().join("\n");
|
|
6258
|
+
}
|
|
6259
|
+
function htmlDescriptions(text) {
|
|
6260
|
+
if (!/[<&]|altrep/i.test(text)) {
|
|
6261
|
+
return "";
|
|
6262
|
+
}
|
|
6263
|
+
const html = [];
|
|
6264
|
+
for (const line of text.replaceAll(FOLD, "").split(LINE_BREAK)) {
|
|
6265
|
+
const colon = valueColon(line);
|
|
6266
|
+
if (colon === -1) {
|
|
6267
|
+
continue;
|
|
6268
|
+
}
|
|
6269
|
+
const altrep = /;\s*altrep\s*=\s*"([^"]*)"/i.exec(line.slice(0, colon))?.[1];
|
|
6270
|
+
if (altrep) {
|
|
6271
|
+
html.push(`<iframe src="${altrep.replaceAll("&", "&").replaceAll('"', """).replaceAll("<", "<")}"></iframe>`);
|
|
6272
|
+
}
|
|
6273
|
+
if (!HTML_PROPERTIES.test(line)) {
|
|
6274
|
+
continue;
|
|
6275
|
+
}
|
|
6276
|
+
const value = readEscapes(line.slice(colon + 1));
|
|
6277
|
+
if (/[<&]/.test(value)) {
|
|
6278
|
+
html.push(value);
|
|
6279
|
+
}
|
|
6280
|
+
}
|
|
6281
|
+
return html.join("\n");
|
|
6282
|
+
}
|
|
6283
|
+
var BINARY = /;\s*(?:(?:encoding\s*=\s*)?"?(?:b|base64)"?|value\s*=\s*"?binary"?)\s*(?:;|$)/i;
|
|
6284
|
+
function withoutBinaryValues(text) {
|
|
6285
|
+
if (!/base64|binary|encoding\s*=\s*"?b|;\s*b\s*[;:]/i.test(text)) {
|
|
6286
|
+
return text;
|
|
6287
|
+
}
|
|
6288
|
+
const lines2 = text.split(LINE_BREAK);
|
|
6289
|
+
const out = [];
|
|
6290
|
+
for (let i = 0; i < lines2.length; i++) {
|
|
6291
|
+
const line = lines2[i];
|
|
6292
|
+
const colon = valueColon(line);
|
|
6293
|
+
if (colon === -1 || !BINARY.test(line.slice(0, colon))) {
|
|
6294
|
+
out.push(line);
|
|
6295
|
+
continue;
|
|
6296
|
+
}
|
|
6297
|
+
const value = [line.slice(colon + 1)];
|
|
6298
|
+
let next = i + 1;
|
|
6299
|
+
while (next < lines2.length && (lines2[next].startsWith(" ") || lines2[next].startsWith(" "))) {
|
|
6300
|
+
value.push(lines2[next++]);
|
|
6301
|
+
}
|
|
6302
|
+
if (!value.every((part) => /^[\w+/=\s]*$/.test(part))) {
|
|
6303
|
+
for (let index = i; index < next; index++) {
|
|
6304
|
+
out.push(lines2[index]);
|
|
6305
|
+
}
|
|
6306
|
+
i = next - 1;
|
|
6307
|
+
continue;
|
|
6308
|
+
}
|
|
6309
|
+
while (next < lines2.length && lines2[next] !== "" && /^[\w+/=]+$/.test(lines2[next])) {
|
|
6310
|
+
value.push(lines2[next++]);
|
|
6311
|
+
}
|
|
6312
|
+
if (next < lines2.length && lines2[next] === "") {
|
|
6313
|
+
next++;
|
|
6314
|
+
}
|
|
6315
|
+
i = next - 1;
|
|
6316
|
+
const name = line.slice(0, colon).split(";")[0].split(".").pop().trim().toLowerCase();
|
|
6317
|
+
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"));
|
|
6318
|
+
}
|
|
6319
|
+
return out.join("\n");
|
|
6320
|
+
}
|
|
6321
|
+
var HTML_TYPES = /* @__PURE__ */ new Set(["text/html", "application/xhtml+xml", "image/svg+xml", "text/xml", "application/xml"]);
|
|
6322
|
+
var HTML_EXTENSIONS = /* @__PURE__ */ new Set(["html", "htm", "shtml", "xhtml", "xht", "svg", "xml"]);
|
|
6323
|
+
var CARD_TYPES = /* @__PURE__ */ new Set(["text/calendar", "text/x-vcalendar", "application/ics", "text/vcard", "text/x-vcard", "text/directory"]);
|
|
6324
|
+
var CARD_EXTENSIONS = /* @__PURE__ */ new Set(["ics", "ical", "ifb", "vcs", "vcf", "vcard"]);
|
|
6325
|
+
var MESSAGE_TYPES = /* @__PURE__ */ new Set(["message/rfc822", "message/global"]);
|
|
6326
|
+
function extensionOf2(filename) {
|
|
6327
|
+
let end = filename.length;
|
|
6328
|
+
while (end > 0 && (filename[end - 1] === "." || filename[end - 1] === " ")) {
|
|
6329
|
+
end--;
|
|
6330
|
+
}
|
|
6331
|
+
const name = filename.slice(0, end).trim().toLowerCase();
|
|
6332
|
+
const index = name.lastIndexOf(".");
|
|
6333
|
+
return index !== -1 && index < name.length - 1 ? name.slice(index + 1) : null;
|
|
6334
|
+
}
|
|
6335
|
+
function extensionsOf(attachment) {
|
|
6336
|
+
const names = [];
|
|
6337
|
+
if (typeof attachment.filename === "string") {
|
|
6338
|
+
names.push(attachment.filename);
|
|
6339
|
+
}
|
|
6340
|
+
const { headers } = attachment;
|
|
6341
|
+
if (headers && typeof headers.get === "function") {
|
|
6342
|
+
for (const [field, parameter] of [["content-type", "name"], ["content-disposition", "filename"]]) {
|
|
6343
|
+
const value = headers.get(field)?.params?.[parameter];
|
|
6344
|
+
if (typeof value === "string" && value !== "") {
|
|
6345
|
+
names.push(value);
|
|
6346
|
+
}
|
|
6347
|
+
}
|
|
6348
|
+
}
|
|
6349
|
+
return new Set(names.flatMap((name) => [extensionOf2(name), extensionOf(name)]).filter(Boolean));
|
|
6350
|
+
}
|
|
6351
|
+
function charsetOf(attachment) {
|
|
6352
|
+
const { headers } = attachment;
|
|
6353
|
+
const charset = headers && typeof headers.get === "function" ? headers.get("content-type")?.params?.charset : null;
|
|
6354
|
+
return typeof charset === "string" ? charset.toLowerCase() : "";
|
|
6355
|
+
}
|
|
6356
|
+
function kindOf(attachment) {
|
|
6357
|
+
const { content } = attachment ?? {};
|
|
6358
|
+
if (!Buffer7.isBuffer(content) || content.length === 0) {
|
|
6359
|
+
return null;
|
|
6360
|
+
}
|
|
6361
|
+
const type = typeof attachment.contentType === "string" ? attachment.contentType.toLowerCase() : "";
|
|
6362
|
+
const extensions = extensionsOf(attachment);
|
|
6363
|
+
const has = (set) => [...extensions].some((name) => set.has(name));
|
|
6364
|
+
if (MESSAGE_TYPES.has(type) || extensions.has("eml") || extensions.has("u8msg")) {
|
|
6365
|
+
return null;
|
|
6366
|
+
}
|
|
6367
|
+
const isHtml = HTML_TYPES.has(type) || has(HTML_EXTENSIONS);
|
|
6368
|
+
const isCard = !isHtml && (CARD_TYPES.has(type) || has(CARD_EXTENSIONS));
|
|
6369
|
+
const isText = isCard || !isHtml && type === "text/plain";
|
|
6370
|
+
return isHtml || isText ? { isHtml, isText, isCard } : null;
|
|
6371
|
+
}
|
|
6372
|
+
function* documentsOf(attachments, state, late) {
|
|
6373
|
+
const read = [];
|
|
6374
|
+
let bytes = 0;
|
|
6375
|
+
let opened = 0;
|
|
6376
|
+
for (const attachment of attachments) {
|
|
6377
|
+
const kind = kindOf(attachment);
|
|
6378
|
+
if (!kind) {
|
|
6379
|
+
continue;
|
|
6380
|
+
}
|
|
6381
|
+
if (!kind.isText && ++opened > MAX_ATTACHED || bytes + attachment.content.length > MAX_ATTACHED_BYTES) {
|
|
6382
|
+
state.tooComplex = true;
|
|
6383
|
+
break;
|
|
6384
|
+
}
|
|
6385
|
+
bytes += attachment.content.length;
|
|
6386
|
+
read.push({ attachment, kind });
|
|
6387
|
+
}
|
|
6388
|
+
for (const { attachment, kind: { isHtml, isCard } } of read) {
|
|
6389
|
+
if (late()) {
|
|
6390
|
+
state.incomplete = true;
|
|
6391
|
+
return;
|
|
6392
|
+
}
|
|
6393
|
+
const text = decodeText(attachment.content, charsetOf(attachment));
|
|
6394
|
+
if (isHtml) {
|
|
6395
|
+
yield { html: text };
|
|
6396
|
+
continue;
|
|
6397
|
+
}
|
|
6398
|
+
if (!isCard) {
|
|
6399
|
+
yield { text };
|
|
6400
|
+
continue;
|
|
6401
|
+
}
|
|
6402
|
+
let readable = withoutBinaryValues(text.length > MAX_CARD_BYTES ? text.slice(0, MAX_CARD_BYTES) : text);
|
|
6403
|
+
if (text.length > MAX_CARD_BYTES || readable.length > MAX_SHOWN_BYTES) {
|
|
6404
|
+
state.tooComplex = true;
|
|
6405
|
+
readable = readable.slice(0, MAX_SHOWN_BYTES);
|
|
6406
|
+
}
|
|
6407
|
+
if (late()) {
|
|
6408
|
+
state.incomplete = true;
|
|
6409
|
+
return;
|
|
6410
|
+
}
|
|
6411
|
+
yield { text: shownText(readable) };
|
|
6412
|
+
if (late()) {
|
|
6413
|
+
state.incomplete = true;
|
|
6414
|
+
return;
|
|
6415
|
+
}
|
|
6416
|
+
const html = htmlDescriptions(readable);
|
|
6417
|
+
if (html !== "") {
|
|
6418
|
+
yield { html };
|
|
6419
|
+
}
|
|
6420
|
+
}
|
|
6421
|
+
}
|
|
6422
|
+
var LATE_EVERY = 1024;
|
|
6423
|
+
function carriedLinkHosts(mail, {
|
|
6424
|
+
mails = [],
|
|
6425
|
+
attachments = [],
|
|
6426
|
+
maxHosts = 250,
|
|
6427
|
+
hostsInText = false
|
|
6428
|
+
} = {}) {
|
|
6429
|
+
const hosts = /* @__PURE__ */ new Set();
|
|
6430
|
+
const loose = /* @__PURE__ */ new Set();
|
|
6431
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6432
|
+
const seenBare = /* @__PURE__ */ new Set();
|
|
6433
|
+
const seenLoose = /* @__PURE__ */ new Set();
|
|
6434
|
+
let more = false;
|
|
6435
|
+
let full = false;
|
|
6436
|
+
const domains = /* @__PURE__ */ new Set();
|
|
6437
|
+
const state = { tooComplex: false, incomplete: false };
|
|
6438
|
+
const until = Date.now() + MAX_READ_TIME;
|
|
6439
|
+
const late = () => {
|
|
6440
|
+
if (!state.incomplete && Date.now() > until) {
|
|
6441
|
+
state.incomplete = true;
|
|
6442
|
+
}
|
|
6443
|
+
return state.incomplete;
|
|
6444
|
+
};
|
|
6445
|
+
let calls = 0;
|
|
6446
|
+
const done = () => full || state.incomplete || ++calls % LATE_EVERY === 0 && late();
|
|
6447
|
+
const looseDone = () => done() || loose.size > maxHosts;
|
|
6448
|
+
const addLoose = (host) => {
|
|
6449
|
+
if (!looseDone()) {
|
|
6450
|
+
loose.add(host);
|
|
6451
|
+
}
|
|
6452
|
+
};
|
|
6453
|
+
const queue = [mail, ...mails].map((document) => ({ document, depth: 0 }));
|
|
6454
|
+
const carried = documentsOf(attachments, state, late);
|
|
6455
|
+
let depth = 0;
|
|
6456
|
+
let embedded = 0;
|
|
6457
|
+
let embeddedLength = 0;
|
|
6458
|
+
const embeddedSeen = /* @__PURE__ */ new Set();
|
|
6459
|
+
const embed = (html) => {
|
|
6460
|
+
if (html === "" || embeddedSeen.has(html)) {
|
|
6461
|
+
return;
|
|
6462
|
+
}
|
|
6463
|
+
if (depth >= MAX_EMBED_DEPTH || embedded >= MAX_EMBEDDED || embeddedLength + html.length > MAX_EMBEDDED_LENGTH) {
|
|
6464
|
+
state.tooComplex = true;
|
|
6465
|
+
return;
|
|
6466
|
+
}
|
|
6467
|
+
embeddedSeen.add(html);
|
|
6468
|
+
embedded++;
|
|
6469
|
+
embeddedLength += html.length;
|
|
6470
|
+
queue.push({ document: { html }, depth: depth + 1 });
|
|
6471
|
+
};
|
|
6472
|
+
const add = (value) => {
|
|
6473
|
+
if (full || typeof value !== "string" || value === "" || seen.has(value)) {
|
|
6474
|
+
return;
|
|
6475
|
+
}
|
|
6476
|
+
if (seen.size < MAX_SEEN) {
|
|
6477
|
+
seen.add(value);
|
|
6478
|
+
}
|
|
6479
|
+
if (/^\s*data:/i.test(value)) {
|
|
6480
|
+
const html = dataDocument(value);
|
|
6481
|
+
if (html !== null) {
|
|
6482
|
+
embed(html);
|
|
6483
|
+
}
|
|
6484
|
+
return;
|
|
6485
|
+
}
|
|
6486
|
+
const found = hostsOf(value);
|
|
6487
|
+
if (found.unresolved) {
|
|
6488
|
+
state.tooComplex = true;
|
|
6489
|
+
}
|
|
6490
|
+
for (const host of found) {
|
|
6491
|
+
if (hosts.has(host)) {
|
|
6492
|
+
continue;
|
|
6493
|
+
}
|
|
6494
|
+
const domain = registrableDomain(host);
|
|
6495
|
+
if (hosts.size < maxHosts || !domains.has(domain) && domains.size < maxHosts) {
|
|
6496
|
+
hosts.add(host);
|
|
6497
|
+
domains.add(domain);
|
|
6498
|
+
continue;
|
|
6499
|
+
}
|
|
6500
|
+
more = true;
|
|
6501
|
+
if (domains.size >= maxHosts) {
|
|
6502
|
+
full = true;
|
|
6503
|
+
return;
|
|
6504
|
+
}
|
|
6505
|
+
}
|
|
6506
|
+
};
|
|
6507
|
+
let spent = 0;
|
|
6508
|
+
const spend = (length) => {
|
|
6509
|
+
spent += length;
|
|
6510
|
+
if (spent < LONG_VALUE) {
|
|
6511
|
+
return false;
|
|
6512
|
+
}
|
|
6513
|
+
spent = 0;
|
|
6514
|
+
return late();
|
|
6515
|
+
};
|
|
6516
|
+
const reader = {
|
|
6517
|
+
add,
|
|
6518
|
+
embed,
|
|
6519
|
+
spend,
|
|
6520
|
+
cut() {
|
|
6521
|
+
state.tooComplex = true;
|
|
6522
|
+
}
|
|
6523
|
+
};
|
|
6524
|
+
const readText = (source, { cutStart = false, cutEnd = false, exact = false } = {}) => {
|
|
6525
|
+
for (const match of source.matchAll(URL_START)) {
|
|
6526
|
+
if (done()) {
|
|
6527
|
+
break;
|
|
6528
|
+
}
|
|
6529
|
+
if (cutStart && match.index === 0) {
|
|
6530
|
+
continue;
|
|
6531
|
+
}
|
|
6532
|
+
if (cutEnd && match.index + match[0].length === source.length) {
|
|
6533
|
+
if (exact && source.length - match.index < CHUNK_OVERLAP) {
|
|
6534
|
+
continue;
|
|
6535
|
+
}
|
|
6536
|
+
const value = match[0];
|
|
6537
|
+
const at = /^www\./i.test(value) ? 0 : value.indexOf("//") + 2;
|
|
6538
|
+
if (!/[/?#\\]/.test(value.slice(at))) {
|
|
6539
|
+
state.tooComplex = true;
|
|
6540
|
+
continue;
|
|
6541
|
+
}
|
|
6542
|
+
add(value);
|
|
6543
|
+
const url = urlOf(value);
|
|
6544
|
+
if (url && isClickTracker(url)) {
|
|
6545
|
+
state.tooComplex = true;
|
|
6546
|
+
}
|
|
6547
|
+
continue;
|
|
6548
|
+
}
|
|
6549
|
+
add(match[0]);
|
|
6550
|
+
}
|
|
6551
|
+
const from = cutStart ? 1 : 0;
|
|
6552
|
+
bareHosts(source, add, {
|
|
6553
|
+
done,
|
|
6554
|
+
seen: seenBare,
|
|
6555
|
+
from,
|
|
6556
|
+
cutEnd
|
|
6557
|
+
});
|
|
6558
|
+
if (hostsInText && !looseDone()) {
|
|
6559
|
+
looseHosts(source, addLoose, {
|
|
6560
|
+
from,
|
|
6561
|
+
cutEnd,
|
|
6562
|
+
seen: seenLoose,
|
|
6563
|
+
done: looseDone
|
|
6564
|
+
});
|
|
6565
|
+
}
|
|
6566
|
+
};
|
|
6567
|
+
for (let index = 0; ; index++) {
|
|
6568
|
+
if (full || late()) {
|
|
6569
|
+
break;
|
|
6570
|
+
}
|
|
6571
|
+
if (index >= queue.length) {
|
|
6572
|
+
const next = carried.next();
|
|
6573
|
+
if (next.done) {
|
|
6574
|
+
break;
|
|
6575
|
+
}
|
|
6576
|
+
queue.push({ document: next.value, depth: 0 });
|
|
6577
|
+
}
|
|
6578
|
+
({ depth } = queue[index]);
|
|
6579
|
+
const { document } = queue[index];
|
|
6580
|
+
const html = withoutNul(typeof document?.html === "string" ? document.html : "");
|
|
6581
|
+
const text = withoutNul(typeof document?.text === "string" ? document.text : "");
|
|
6582
|
+
for (const [name, value] of attributesOf(html)) {
|
|
6583
|
+
if (done()) {
|
|
6584
|
+
break;
|
|
6585
|
+
}
|
|
6586
|
+
if (!LINK_ATTRIBUTES.has(name)) {
|
|
6587
|
+
continue;
|
|
6588
|
+
}
|
|
6589
|
+
if (spend(value.length)) {
|
|
6590
|
+
break;
|
|
6591
|
+
}
|
|
6592
|
+
linksOf(name, value, reader);
|
|
6593
|
+
}
|
|
6594
|
+
lenientLinks(html, done, reader);
|
|
6595
|
+
for (const pattern of [CSS_URL, CSS_IMPORT]) {
|
|
6596
|
+
for (const [, link] of html.matchAll(pattern)) {
|
|
6597
|
+
if (done() || spend(link.length)) {
|
|
6598
|
+
break;
|
|
6599
|
+
}
|
|
6600
|
+
if (link.length > MAX_VALUE2) {
|
|
6601
|
+
state.tooComplex = true;
|
|
6602
|
+
}
|
|
6603
|
+
add(decodeEntities(link.slice(0, MAX_VALUE2), { attribute: true }));
|
|
6604
|
+
}
|
|
6605
|
+
}
|
|
6606
|
+
for (const chunk2 of chunksOf(text)) {
|
|
6607
|
+
if (full || late()) {
|
|
6608
|
+
break;
|
|
6609
|
+
}
|
|
6610
|
+
readText(chunk2.text, { ...chunk2, exact: true });
|
|
6611
|
+
}
|
|
6612
|
+
for (const chunk2 of chunksOf(html)) {
|
|
6613
|
+
if (full || late()) {
|
|
6614
|
+
break;
|
|
6615
|
+
}
|
|
6616
|
+
const decoded = decodeEntities(chunk2.text);
|
|
6617
|
+
readText(decoded, { ...chunk2, exact: decoded.endsWith(chunk2.text.slice(-CHUNK_OVERLAP)) });
|
|
6618
|
+
}
|
|
6619
|
+
}
|
|
6620
|
+
if (!more && !state.incomplete) {
|
|
6621
|
+
for (const name of loose) {
|
|
6622
|
+
if (hosts.size >= maxHosts) {
|
|
6623
|
+
break;
|
|
6624
|
+
}
|
|
6625
|
+
const [host] = hostsOf(`http://${name}/`);
|
|
6626
|
+
if (host) {
|
|
6627
|
+
hosts.add(host);
|
|
6628
|
+
}
|
|
6629
|
+
}
|
|
6630
|
+
}
|
|
6631
|
+
return {
|
|
6632
|
+
hosts: [...hosts],
|
|
6633
|
+
more,
|
|
6634
|
+
tooComplex: state.tooComplex,
|
|
6635
|
+
incomplete: state.incomplete
|
|
6636
|
+
};
|
|
6637
|
+
}
|
|
6638
|
+
|
|
6639
|
+
// src/is-arbitrary.js
|
|
6640
|
+
import { parse as parseDomain4 } from "tldts";
|
|
5441
6641
|
var GTUBE = "XJS*C4JDBQADN1.NSBN3*2IDNEN*GTUBE-STANDARD-ANTI-UBE-TEST-EMAIL*C.34X";
|
|
5442
6642
|
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
6643
|
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 +6828,13 @@ function joinsDisplay(brand, display, name) {
|
|
|
5628
6828
|
return false;
|
|
5629
6829
|
}
|
|
5630
6830
|
function isOfficial({ brand, official }, domain, display, shared = true) {
|
|
5631
|
-
const parsed =
|
|
6831
|
+
const parsed = parseDomain4(domain);
|
|
5632
6832
|
const name = parsed.domainWithoutSuffix ?? "";
|
|
5633
6833
|
const compact = brand.replaceAll(" ", "");
|
|
5634
6834
|
if (parsed.publicSuffix === compact || compact.length >= 5 && joinsDisplay(brand, display, name) && !JOINED_WORDS.some((word) => name.slice(compact.length).includes(word))) {
|
|
5635
6835
|
return true;
|
|
5636
6836
|
}
|
|
5637
|
-
return official.some((entry) => entry instanceof RegExp ? entry.test(registrableDomain(domain)) : entry.includes(".") ? (shared ||
|
|
6837
|
+
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
6838
|
}
|
|
5639
6839
|
function headerString(mail, name) {
|
|
5640
6840
|
const value = mail.headers?.get?.(name);
|
|
@@ -5736,7 +6936,7 @@ ${withoutHelp(markup)}`) || isInjection(readable) || attributeText(markup).some(
|
|
|
5736
6936
|
}
|
|
5737
6937
|
} else if (claimed && isWholeName(name, claimed)) {
|
|
5738
6938
|
const domain = claimed[1];
|
|
5739
|
-
const brand = MAILBOXES.has(
|
|
6939
|
+
const brand = MAILBOXES.has(parseDomain4(domain).domainWithoutSuffix) ? null : DISPLAY_BRANDS.find((entry) => isOfficial(entry, domain, "", false) && !isOfficial(entry, session.originalFromAddressDomain, ""));
|
|
5740
6940
|
if (brand) {
|
|
5741
6941
|
hit("FROM_NAME_BRAND", 2, `Display name is an address of "${brand.brand}" but the message is from ${fromRoot}`, "PHISHING");
|
|
5742
6942
|
}
|
|
@@ -5771,7 +6971,7 @@ ${withoutHelp(markup)}`) || isInjection(readable) || attributeText(markup).some(
|
|
|
5771
6971
|
}
|
|
5772
6972
|
|
|
5773
6973
|
// src/llm.js
|
|
5774
|
-
import { Buffer as
|
|
6974
|
+
import { Buffer as Buffer8 } from "node:buffer";
|
|
5775
6975
|
import { createHash as createHash2, randomBytes } from "node:crypto";
|
|
5776
6976
|
import os from "node:os";
|
|
5777
6977
|
import process from "node:process";
|
|
@@ -6132,7 +7332,7 @@ function authHeaders(config) {
|
|
|
6132
7332
|
case "basic": {
|
|
6133
7333
|
const user = config.username ?? "";
|
|
6134
7334
|
const pass = config.password ?? config.apiKey ?? "";
|
|
6135
|
-
headers.authorization = `Basic ${
|
|
7335
|
+
headers.authorization = `Basic ${Buffer8.from(`${user}:${pass}`).toString("base64")}`;
|
|
6136
7336
|
break;
|
|
6137
7337
|
}
|
|
6138
7338
|
case "x-api-key": {
|
|
@@ -7078,7 +8278,7 @@ function scoreResults(results, options) {
|
|
|
7078
8278
|
var package_default = {
|
|
7079
8279
|
name: "spamscanner",
|
|
7080
8280
|
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.
|
|
8281
|
+
version: "7.0.12",
|
|
7082
8282
|
author: "Forward Email (https://forwardemail.net)",
|
|
7083
8283
|
bin: {
|
|
7084
8284
|
spamscanner: "./dist/esm/cli.js"
|
|
@@ -7225,7 +8425,7 @@ var package_default = {
|
|
|
7225
8425
|
var VERSION = package_default.version;
|
|
7226
8426
|
|
|
7227
8427
|
// src/headers.js
|
|
7228
|
-
import { Buffer as
|
|
8428
|
+
import { Buffer as Buffer9 } from "node:buffer";
|
|
7229
8429
|
function foldHeader(name, value, newline = "\r\n") {
|
|
7230
8430
|
const pieces = String(value).split(/(?<=[ ,])/);
|
|
7231
8431
|
const lines2 = [];
|
|
@@ -7260,10 +8460,10 @@ function splitMessage(raw) {
|
|
|
7260
8460
|
const match = /^\r?\n|\r?\n\r?\n/.exec(text);
|
|
7261
8461
|
if (!match) {
|
|
7262
8462
|
const newline2 = text.includes("\n") && !text.includes("\r\n") ? "\n" : "\r\n";
|
|
7263
|
-
return { header: text.replace(/\r?\n$/, ""), body:
|
|
8463
|
+
return { header: text.replace(/\r?\n$/, ""), body: Buffer9.alloc(0), newline: newline2 };
|
|
7264
8464
|
}
|
|
7265
8465
|
const newline = match[0].startsWith("\r") ? "\r\n" : "\n";
|
|
7266
|
-
return { header: text.slice(0, match.index), body: raw.subarray(
|
|
8466
|
+
return { header: text.slice(0, match.index), body: raw.subarray(Buffer9.byteLength(text.slice(0, match.index + match[0].length), "latin1")), newline };
|
|
7267
8467
|
}
|
|
7268
8468
|
var ENCODED_BYTES = 39;
|
|
7269
8469
|
var PLAIN_WORD = 64;
|
|
@@ -7279,14 +8479,14 @@ function subjectTagText(tag, spaceAfter = false) {
|
|
|
7279
8479
|
const words2 = [];
|
|
7280
8480
|
let chunk2 = "";
|
|
7281
8481
|
for (const character of spaceAfter ? `${line} ` : line) {
|
|
7282
|
-
if (
|
|
8482
|
+
if (Buffer9.byteLength(chunk2 + character, "utf8") > ENCODED_BYTES) {
|
|
7283
8483
|
words2.push(chunk2);
|
|
7284
8484
|
chunk2 = "";
|
|
7285
8485
|
}
|
|
7286
8486
|
chunk2 += character;
|
|
7287
8487
|
}
|
|
7288
8488
|
words2.push(chunk2);
|
|
7289
|
-
return words2.map((word) => `=?UTF-8?B?${
|
|
8489
|
+
return words2.map((word) => `=?UTF-8?B?${Buffer9.from(word, "utf8").toString("base64")}?=`).join(" ");
|
|
7290
8490
|
}
|
|
7291
8491
|
function taggedSubject(tag, current, newline = "\r\n") {
|
|
7292
8492
|
const unfolded = current.replaceAll(/\r?\n/g, "");
|
|
@@ -7317,7 +8517,7 @@ function taggedSubject(tag, current, newline = "\r\n") {
|
|
|
7317
8517
|
return lines2.join(newline).slice("Subject: ".length);
|
|
7318
8518
|
}
|
|
7319
8519
|
function rewriteMessage(source, headers = [], options = {}) {
|
|
7320
|
-
const raw =
|
|
8520
|
+
const raw = Buffer9.isBuffer(source) ? source : Buffer9.from(String(source), "utf8");
|
|
7321
8521
|
const { header, body, newline } = splitMessage(raw);
|
|
7322
8522
|
const remove = options.remove ?? /^x-spam-/i;
|
|
7323
8523
|
const lines2 = header === "" ? [] : header.split(/\r?\n/);
|
|
@@ -7342,10 +8542,13 @@ function rewriteMessage(source, headers = [], options = {}) {
|
|
|
7342
8542
|
}
|
|
7343
8543
|
}
|
|
7344
8544
|
}
|
|
7345
|
-
const added = headers.map(([name, value]) => foldHeader(name,
|
|
7346
|
-
const mbox = kept.length > 0 && kept[0][0].startsWith("From ") ? kept.shift() : [];
|
|
8545
|
+
const added = headers.map(([name, value]) => foldHeader(name, Buffer9.from(String(value), "utf8").toString("latin1"), newline));
|
|
8546
|
+
const mbox = kept.length > 0 && kept[0][0].startsWith("From ") ? kept.shift().slice(0, 1) : [];
|
|
8547
|
+
if (kept.length > 0 && /^[ \t]/.test(kept[0][0])) {
|
|
8548
|
+
kept.shift();
|
|
8549
|
+
}
|
|
7347
8550
|
const head = [...mbox, ...added, ...kept.map((field) => field.join(newline))].join(newline);
|
|
7348
|
-
return
|
|
8551
|
+
return Buffer9.concat([Buffer9.from(head === "" ? newline : `${head}${newline}${newline}`, "latin1"), body]);
|
|
7349
8552
|
}
|
|
7350
8553
|
|
|
7351
8554
|
// src/train.js
|
|
@@ -7353,7 +8556,7 @@ import { createHash as createHash3 } from "node:crypto";
|
|
|
7353
8556
|
import path3 from "node:path";
|
|
7354
8557
|
|
|
7355
8558
|
// src/sources.js
|
|
7356
|
-
import { Buffer as
|
|
8559
|
+
import { Buffer as Buffer10 } from "node:buffer";
|
|
7357
8560
|
import { createReadStream } from "node:fs";
|
|
7358
8561
|
import fs from "node:fs/promises";
|
|
7359
8562
|
import path2 from "node:path";
|
|
@@ -7533,7 +8736,7 @@ async function* readJsonl(file, options = {}) {
|
|
|
7533
8736
|
}
|
|
7534
8737
|
async function* walk(directory) {
|
|
7535
8738
|
const entries = await fs.readdir(directory, { withFileTypes: true });
|
|
7536
|
-
entries.sort((a, b) =>
|
|
8739
|
+
entries.sort((a, b) => Buffer10.compare(Buffer10.from(a.name), Buffer10.from(b.name)));
|
|
7537
8740
|
for (const entry of entries) {
|
|
7538
8741
|
if (SKIP.has(entry.name) || entry.name.startsWith(".")) {
|
|
7539
8742
|
continue;
|
|
@@ -7558,7 +8761,7 @@ async function* readMessages(source) {
|
|
|
7558
8761
|
const handle = await fs.open(file);
|
|
7559
8762
|
let head;
|
|
7560
8763
|
try {
|
|
7561
|
-
const { buffer, bytesRead } = await handle.read(
|
|
8764
|
+
const { buffer, bytesRead } = await handle.read(Buffer10.alloc(64), 0, 64, 0);
|
|
7562
8765
|
head = buffer.subarray(0, bytesRead).toString("latin1");
|
|
7563
8766
|
} finally {
|
|
7564
8767
|
await handle.close();
|
|
@@ -7682,19 +8885,24 @@ async function evaluate(classifier, examples) {
|
|
|
7682
8885
|
}
|
|
7683
8886
|
|
|
7684
8887
|
// src/milter.js
|
|
7685
|
-
import { Buffer as
|
|
8888
|
+
import { Buffer as Buffer12 } from "node:buffer";
|
|
7686
8889
|
import { EventEmitter } from "node:events";
|
|
7687
8890
|
import net3 from "node:net";
|
|
7688
8891
|
|
|
7689
8892
|
// src/server.js
|
|
7690
|
-
import { Buffer as
|
|
8893
|
+
import { Buffer as Buffer11 } from "node:buffer";
|
|
7691
8894
|
import { timingSafeEqual } from "node:crypto";
|
|
7692
8895
|
import http2 from "node:http";
|
|
7693
8896
|
import net2 from "node:net";
|
|
7694
8897
|
var MAX_MESSAGE_SIZE = 25 * 1024 * 1024;
|
|
7695
8898
|
var MAX_CONNECTIONS = 200;
|
|
7696
8899
|
function serializeResult(result, options = {}) {
|
|
7697
|
-
const {
|
|
8900
|
+
const {
|
|
8901
|
+
mail,
|
|
8902
|
+
tokens,
|
|
8903
|
+
attached,
|
|
8904
|
+
...rest
|
|
8905
|
+
} = result;
|
|
7698
8906
|
const output = {
|
|
7699
8907
|
...rest,
|
|
7700
8908
|
results: { ...result.results }
|
|
@@ -7727,8 +8935,8 @@ function sessionFromQuery(query) {
|
|
|
7727
8935
|
return session;
|
|
7728
8936
|
}
|
|
7729
8937
|
function sameToken(given, expected) {
|
|
7730
|
-
const a =
|
|
7731
|
-
const b =
|
|
8938
|
+
const a = Buffer11.from(String(given));
|
|
8939
|
+
const b = Buffer11.from(String(expected));
|
|
7732
8940
|
return a.length === b.length && timingSafeEqual(a, b);
|
|
7733
8941
|
}
|
|
7734
8942
|
function readBody(request, limit) {
|
|
@@ -7748,7 +8956,7 @@ function readBody(request, limit) {
|
|
|
7748
8956
|
reject(error);
|
|
7749
8957
|
return;
|
|
7750
8958
|
}
|
|
7751
|
-
resolve(
|
|
8959
|
+
resolve(Buffer11.concat(chunks));
|
|
7752
8960
|
});
|
|
7753
8961
|
request.on("error", reject);
|
|
7754
8962
|
});
|
|
@@ -7763,7 +8971,7 @@ function createHttpServer(scanner, options = {}) {
|
|
|
7763
8971
|
let active = 0;
|
|
7764
8972
|
const server = http2.createServer(async (request, response) => {
|
|
7765
8973
|
const send = (status, body, headers = {}) => {
|
|
7766
|
-
const isBuffer =
|
|
8974
|
+
const isBuffer = Buffer11.isBuffer(body);
|
|
7767
8975
|
response.writeHead(status, { "content-type": isBuffer ? "message/rfc822" : "application/json; charset=utf-8", "cache-control": "no-store", ...headers });
|
|
7768
8976
|
response.end(isBuffer ? body : `${JSON.stringify(body)}
|
|
7769
8977
|
`);
|
|
@@ -7843,7 +9051,7 @@ function createTcpServer(scanner, options = {}) {
|
|
|
7843
9051
|
const onEnd = async () => {
|
|
7844
9052
|
socket.setTimeout(0);
|
|
7845
9053
|
try {
|
|
7846
|
-
const result = await scanner.scan(
|
|
9054
|
+
const result = await scanner.scan(Buffer11.concat(chunks));
|
|
7847
9055
|
socket.end(json ? `${JSON.stringify(serializeResult(result))}
|
|
7848
9056
|
` : `${result.isSpam ? "SPAM" : "HAM"} ${result.score.toFixed(1)}/${result.threshold.toFixed(1)} ${result.tests.map((test) => test.name).join(",")}
|
|
7849
9057
|
`);
|
|
@@ -7937,12 +9145,12 @@ var PROTOCOL = {
|
|
|
7937
9145
|
HDR_LEADSPC: 1048576
|
|
7938
9146
|
};
|
|
7939
9147
|
var MAX_PACKET = 1024 * 1024;
|
|
7940
|
-
function packet(command, data =
|
|
7941
|
-
const payload =
|
|
7942
|
-
const header =
|
|
9148
|
+
function packet(command, data = Buffer12.alloc(0)) {
|
|
9149
|
+
const payload = Buffer12.isBuffer(data) ? data : Buffer12.from(data, "latin1");
|
|
9150
|
+
const header = Buffer12.alloc(5);
|
|
7943
9151
|
header.writeUInt32BE(payload.length + 1);
|
|
7944
9152
|
header.write(command, 4, "latin1");
|
|
7945
|
-
return
|
|
9153
|
+
return Buffer12.concat([header, payload]);
|
|
7946
9154
|
}
|
|
7947
9155
|
function cstrings(data) {
|
|
7948
9156
|
const parts = data.toString("latin1").split("\0");
|
|
@@ -7952,7 +9160,7 @@ function cstrings(data) {
|
|
|
7952
9160
|
return parts;
|
|
7953
9161
|
}
|
|
7954
9162
|
function cstring(value) {
|
|
7955
|
-
return
|
|
9163
|
+
return Buffer12.from(`${value}\0`, "latin1");
|
|
7956
9164
|
}
|
|
7957
9165
|
function address(value) {
|
|
7958
9166
|
const text = String(value ?? "").trim();
|
|
@@ -8009,7 +9217,7 @@ var MilterSession = class {
|
|
|
8009
9217
|
if (this.buffered < this.needed) {
|
|
8010
9218
|
return;
|
|
8011
9219
|
}
|
|
8012
|
-
let buffer = this.chunks.length === 1 ? this.chunks[0] :
|
|
9220
|
+
let buffer = this.chunks.length === 1 ? this.chunks[0] : Buffer12.concat(this.chunks);
|
|
8013
9221
|
this.needed = 4;
|
|
8014
9222
|
while (buffer.length >= 4) {
|
|
8015
9223
|
const length = buffer.readUInt32BE(0);
|
|
@@ -8054,7 +9262,7 @@ var MilterSession = class {
|
|
|
8054
9262
|
const protocol = data.readUInt32BE(8);
|
|
8055
9263
|
const wanted = ACTIONS.ADDHDRS | ACTIONS.CHGHDRS | ACTIONS.QUARANTINE;
|
|
8056
9264
|
this.leadingSpace = Boolean(protocol & PROTOCOL.HDR_LEADSPC);
|
|
8057
|
-
const reply2 =
|
|
9265
|
+
const reply2 = Buffer12.alloc(12);
|
|
8058
9266
|
reply2.writeUInt32BE(Math.min(version, 6), 0);
|
|
8059
9267
|
reply2.writeUInt32BE(actions & wanted, 4);
|
|
8060
9268
|
reply2.writeUInt32BE(protocol & (PROTOCOL.HDR_LEADSPC | PROTOCOL.NOUNKNOWN), 8);
|
|
@@ -8119,7 +9327,7 @@ var MilterSession = class {
|
|
|
8119
9327
|
if (this.truncated || this.size + data.length > options.maxSize) {
|
|
8120
9328
|
this.truncated = true;
|
|
8121
9329
|
} else {
|
|
8122
|
-
this.body.push(
|
|
9330
|
+
this.body.push(Buffer12.from(data));
|
|
8123
9331
|
this.size += data.length;
|
|
8124
9332
|
}
|
|
8125
9333
|
this.send(RESPONSES.CONTINUE);
|
|
@@ -8154,7 +9362,7 @@ var MilterSession = class {
|
|
|
8154
9362
|
/** @returns {Buffer} */
|
|
8155
9363
|
rawMessage() {
|
|
8156
9364
|
const head = this.headers.map(([name, value]) => `${name}:${this.leadingSpace || value.startsWith(" ") || value.startsWith(" ") ? "" : " "}${value}`).join("\r\n");
|
|
8157
|
-
return
|
|
9365
|
+
return Buffer12.concat([Buffer12.from(this.headers.length > 0 ? `${head}\r
|
|
8158
9366
|
\r
|
|
8159
9367
|
` : "\r\n", "latin1"), ...this.body]);
|
|
8160
9368
|
}
|
|
@@ -8165,9 +9373,9 @@ var MilterSession = class {
|
|
|
8165
9373
|
const key = name.toLowerCase();
|
|
8166
9374
|
counts.set(key, (counts.get(key) || 0) + 1);
|
|
8167
9375
|
if (key.startsWith("x-spam-")) {
|
|
8168
|
-
const index =
|
|
9376
|
+
const index = Buffer12.alloc(4);
|
|
8169
9377
|
index.writeUInt32BE(counts.get(key));
|
|
8170
|
-
this.send(RESPONSES.CHGHEADER,
|
|
9378
|
+
this.send(RESPONSES.CHGHEADER, Buffer12.concat([index, cstring(name), cstring("")]));
|
|
8171
9379
|
}
|
|
8172
9380
|
}
|
|
8173
9381
|
}
|
|
@@ -8190,7 +9398,7 @@ var MilterSession = class {
|
|
|
8190
9398
|
}
|
|
8191
9399
|
this.server.report(error);
|
|
8192
9400
|
this.removeForgedHeaders();
|
|
8193
|
-
this.send(RESPONSES.ADDHEADER,
|
|
9401
|
+
this.send(RESPONSES.ADDHEADER, Buffer12.concat([cstring("X-Spam-Virus-Scan"), cstring(`${this.leadingSpace ? " " : ""}failed`)]));
|
|
8194
9402
|
this.send(RESPONSES.CONTINUE);
|
|
8195
9403
|
this.resetMessage();
|
|
8196
9404
|
return;
|
|
@@ -8211,15 +9419,15 @@ var MilterSession = class {
|
|
|
8211
9419
|
headers.unshift(["Authentication-Results", formatAuthResultsHeader(result.results.authentication, options.hostname)]);
|
|
8212
9420
|
}
|
|
8213
9421
|
for (const [name, value] of headers) {
|
|
8214
|
-
this.send(RESPONSES.ADDHEADER,
|
|
9422
|
+
this.send(RESPONSES.ADDHEADER, Buffer12.concat([cstring(name), cstring(`${this.leadingSpace ? " " : ""}${value}`.replaceAll("\r\n", "\n"))]));
|
|
8215
9423
|
}
|
|
8216
9424
|
if (result.isSpam && options.subjectTag) {
|
|
8217
9425
|
const subject = this.headers.find(([name]) => name.toLowerCase() === "subject");
|
|
8218
9426
|
const value = taggedSubject(options.subjectTag, subject ? subject[1].trim() : "", "\n");
|
|
8219
9427
|
if (value !== null) {
|
|
8220
|
-
const index =
|
|
9428
|
+
const index = Buffer12.alloc(4);
|
|
8221
9429
|
index.writeUInt32BE(1);
|
|
8222
|
-
this.send(RESPONSES.CHGHEADER,
|
|
9430
|
+
this.send(RESPONSES.CHGHEADER, Buffer12.concat([index, cstring("Subject"), cstring(`${this.leadingSpace ? " " : ""}${value}`.replaceAll("\r\n", "\n"))]));
|
|
8223
9431
|
}
|
|
8224
9432
|
}
|
|
8225
9433
|
if (result.action === "reject" && options.reject) {
|
|
@@ -8322,7 +9530,7 @@ var MilterServer = class extends EventEmitter {
|
|
|
8322
9530
|
};
|
|
8323
9531
|
|
|
8324
9532
|
// src/spamd.js
|
|
8325
|
-
import { Buffer as
|
|
9533
|
+
import { Buffer as Buffer13 } from "node:buffer";
|
|
8326
9534
|
import net4 from "node:net";
|
|
8327
9535
|
var EX_OK = 0;
|
|
8328
9536
|
var EX_USAGE = 64;
|
|
@@ -8353,8 +9561,9 @@ function reply(code, text, headers = [], body = null) {
|
|
|
8353
9561
|
lines2.push(`Content-length: ${body.length}`);
|
|
8354
9562
|
}
|
|
8355
9563
|
lines2.push(...headers.map(([name, value]) => `${name}: ${value}`), "", "");
|
|
8356
|
-
return body === null ?
|
|
9564
|
+
return body === null ? Buffer13.from(lines2.join("\r\n")) : Buffer13.concat([Buffer13.from(lines2.join("\r\n")), body]);
|
|
8357
9565
|
}
|
|
9566
|
+
var oneLine2 = (text) => String(text ?? "").replaceAll(/[\p{Cc}\u061C\u200E\u200F\u2028\u2029\u202A-\u202E\u2066-\u2069]+/gu, " ");
|
|
8358
9567
|
function report(result) {
|
|
8359
9568
|
const lines2 = [
|
|
8360
9569
|
`Spam detection software, running on the system "spamscanner", has ${result.isSpam ? "identified this incoming email as possible spam" : "scanned this incoming email"}.`,
|
|
@@ -8363,7 +9572,7 @@ function report(result) {
|
|
|
8363
9572
|
"",
|
|
8364
9573
|
" pts rule name description",
|
|
8365
9574
|
"---- ---------------------- --------------------------------------------------",
|
|
8366
|
-
...result.tests.map((test) => `${test.score.toFixed(1).padStart(4)} ${test.name.padEnd(22)} ${test.description}`)
|
|
9575
|
+
...result.tests.map((test) => `${test.score.toFixed(1).padStart(4)} ${oneLine2(test.name).padEnd(22)} ${oneLine2(test.description)}`)
|
|
8367
9576
|
];
|
|
8368
9577
|
return `${lines2.join("\r\n")}\r
|
|
8369
9578
|
`;
|
|
@@ -8398,11 +9607,11 @@ async function answer(scanner, request, message, options) {
|
|
|
8398
9607
|
return reply(EX_OK, "EX_OK", [verdict]);
|
|
8399
9608
|
}
|
|
8400
9609
|
case "SYMBOLS": {
|
|
8401
|
-
return reply(EX_OK, "EX_OK", [verdict],
|
|
9610
|
+
return reply(EX_OK, "EX_OK", [verdict], Buffer13.from(result.tests.map((test) => test.name).join(",")));
|
|
8402
9611
|
}
|
|
8403
9612
|
case "REPORT":
|
|
8404
9613
|
case "REPORT_IFSPAM": {
|
|
8405
|
-
return reply(EX_OK, "EX_OK", [verdict], command === "REPORT" || result.isSpam ?
|
|
9614
|
+
return reply(EX_OK, "EX_OK", [verdict], command === "REPORT" || result.isSpam ? Buffer13.from(report(result)) : Buffer13.alloc(0));
|
|
8406
9615
|
}
|
|
8407
9616
|
default: {
|
|
8408
9617
|
const rewritten = rewriteMessage(message, spamHeaders(result, { version: VERSION }), { subjectTag: result.isSpam ? options.subjectTag : null });
|
|
@@ -8410,7 +9619,7 @@ async function answer(scanner, request, message, options) {
|
|
|
8410
9619
|
return reply(EX_OK, "EX_OK", [verdict], rewritten);
|
|
8411
9620
|
}
|
|
8412
9621
|
const { header, newline } = splitMessage(rewritten);
|
|
8413
|
-
return reply(EX_OK, "EX_OK", [verdict],
|
|
9622
|
+
return reply(EX_OK, "EX_OK", [verdict], Buffer13.from(`${header}${newline}${newline}`, "latin1"));
|
|
8414
9623
|
}
|
|
8415
9624
|
}
|
|
8416
9625
|
}
|
|
@@ -8425,7 +9634,7 @@ function createSpamdServer(scanner, options = {}) {
|
|
|
8425
9634
|
...options
|
|
8426
9635
|
};
|
|
8427
9636
|
const server = net4.createServer({ allowHalfOpen: true }, (socket) => {
|
|
8428
|
-
let head =
|
|
9637
|
+
let head = Buffer13.alloc(0);
|
|
8429
9638
|
const chunks = [];
|
|
8430
9639
|
let size = 0;
|
|
8431
9640
|
let request = null;
|
|
@@ -8451,7 +9660,7 @@ function createSpamdServer(scanner, options = {}) {
|
|
|
8451
9660
|
return;
|
|
8452
9661
|
}
|
|
8453
9662
|
if (!request) {
|
|
8454
|
-
head =
|
|
9663
|
+
head = Buffer13.concat([head, chunk2]);
|
|
8455
9664
|
const end = head.indexOf("\r\n\r\n");
|
|
8456
9665
|
if (end === -1) {
|
|
8457
9666
|
if (head.length > 64 * 1024) {
|
|
@@ -8486,7 +9695,7 @@ function createSpamdServer(scanner, options = {}) {
|
|
|
8486
9695
|
chunks.push(chunk2);
|
|
8487
9696
|
size += chunk2.length;
|
|
8488
9697
|
if (request.length === null ? ["PING", "SKIP"].includes(request.command) : size >= request.length) {
|
|
8489
|
-
finish(
|
|
9698
|
+
finish(Buffer13.concat(chunks).subarray(0, request.length ?? 0));
|
|
8490
9699
|
} else if (size > settings.maxSize) {
|
|
8491
9700
|
fail(EX_DATAERR, `Message larger than ${settings.maxSize} bytes`);
|
|
8492
9701
|
}
|
|
@@ -8498,7 +9707,7 @@ function createSpamdServer(scanner, options = {}) {
|
|
|
8498
9707
|
if (!request) {
|
|
8499
9708
|
fail(EX_PROTOCOL, "Incomplete request");
|
|
8500
9709
|
} else if (request.length === null) {
|
|
8501
|
-
finish(
|
|
9710
|
+
finish(Buffer13.concat(chunks));
|
|
8502
9711
|
} else {
|
|
8503
9712
|
fail(EX_PROTOCOL, `Message shorter than its Content-length of ${request.length} bytes`);
|
|
8504
9713
|
}
|
|
@@ -8672,21 +9881,21 @@ async function withTimeout(promise, ms, fallback) {
|
|
|
8672
9881
|
}
|
|
8673
9882
|
}
|
|
8674
9883
|
async function toBuffer(source) {
|
|
8675
|
-
if (
|
|
9884
|
+
if (Buffer14.isBuffer(source)) {
|
|
8676
9885
|
return source;
|
|
8677
9886
|
}
|
|
8678
9887
|
if (typeof source === "string") {
|
|
8679
|
-
return
|
|
9888
|
+
return Buffer14.from(source, "utf8");
|
|
8680
9889
|
}
|
|
8681
9890
|
if (source instanceof Uint8Array) {
|
|
8682
|
-
return
|
|
9891
|
+
return Buffer14.from(source);
|
|
8683
9892
|
}
|
|
8684
9893
|
if (source && typeof source[Symbol.asyncIterator] === "function") {
|
|
8685
9894
|
const chunks = [];
|
|
8686
9895
|
for await (const chunk2 of source) {
|
|
8687
|
-
chunks.push(
|
|
9896
|
+
chunks.push(Buffer14.isBuffer(chunk2) ? chunk2 : Buffer14.from(chunk2));
|
|
8688
9897
|
}
|
|
8689
|
-
return
|
|
9898
|
+
return Buffer14.concat(chunks);
|
|
8690
9899
|
}
|
|
8691
9900
|
throw new TypeError("A message must be a Buffer, a string, a Uint8Array or a readable stream");
|
|
8692
9901
|
}
|
|
@@ -8708,6 +9917,19 @@ var DEFAULTS2 = {
|
|
|
8708
9917
|
cloudflare: true,
|
|
8709
9918
|
adult: true,
|
|
8710
9919
|
maxHosts: 25,
|
|
9920
|
+
// Also read the links of the whole message and of what it carries
|
|
9921
|
+
// (attached messages, HTML, SVG and XML files, text files, invitations
|
|
9922
|
+
// and contact cards, documents in iframes and data: URLs) when link
|
|
9923
|
+
// hosts are looked up, for at most five seconds: off by default, as it
|
|
9924
|
+
// reads all of it, up to 50 MB of attachments.
|
|
9925
|
+
attachments: false,
|
|
9926
|
+
// Milliseconds the link lookups may take in all (default: timeout).
|
|
9927
|
+
timeout: void 0,
|
|
9928
|
+
// Also look up host names written in the text without a link or a path
|
|
9929
|
+
// ("evil.example"), which mail clients make links of, as far as there is
|
|
9930
|
+
// room: off by default, as a quoted reply or a signature may name any
|
|
9931
|
+
// site (they are checked for lookalikes either way).
|
|
9932
|
+
hostsInText: false,
|
|
8711
9933
|
// Characters of text read for links (twice as many of HTML): more than
|
|
8712
9934
|
// for the classifier (maxLength), as a link anywhere in a message may be
|
|
8713
9935
|
// followed; a longer message sets results.linksTruncated.
|
|
@@ -8820,7 +10042,7 @@ function fromDomain(mail) {
|
|
|
8820
10042
|
function withHeader(raw) {
|
|
8821
10043
|
const first = raw.subarray(0, 1e3).toString("latin1");
|
|
8822
10044
|
const headerless = raw.length > 0 && !/^(?:[!-9;-~]+[ \t]*:|From |[\r\n])/.test(first);
|
|
8823
|
-
return headerless ?
|
|
10045
|
+
return headerless ? Buffer14.concat([Buffer14.from("\r\n"), raw]) : raw;
|
|
8824
10046
|
}
|
|
8825
10047
|
var SpamScanner = class {
|
|
8826
10048
|
/**
|
|
@@ -8982,7 +10204,10 @@ var SpamScanner = class {
|
|
|
8982
10204
|
async scanFile(file, options) {
|
|
8983
10205
|
return this.scan(await readFile(file), options);
|
|
8984
10206
|
}
|
|
8985
|
-
|
|
10207
|
+
// (`from`: the sender's domain; `carried`: the link hosts of the whole
|
|
10208
|
+
// message and of what it carries, see carriedLinkHosts)
|
|
10209
|
+
async checkLinks(links, config, hosts = [], options = {}) {
|
|
10210
|
+
const { from = "", carried = null } = typeof options === "string" ? { from: options } : options ?? {};
|
|
8986
10211
|
const phishing = [];
|
|
8987
10212
|
const homographs = [];
|
|
8988
10213
|
const seenHosts = /* @__PURE__ */ new Set();
|
|
@@ -9038,18 +10263,23 @@ var SpamScanner = class {
|
|
|
9038
10263
|
for (const host of hosts) {
|
|
9039
10264
|
lookalike(host, { source: "text" });
|
|
9040
10265
|
}
|
|
9041
|
-
const
|
|
10266
|
+
const carriedHosts = (carried?.hosts ?? []).map((host) => lookupName(host)).filter(Boolean);
|
|
10267
|
+
for (const host of carriedHosts) {
|
|
10268
|
+
seenHosts.add(host);
|
|
10269
|
+
}
|
|
10270
|
+
const own = [...found.filter((link) => link.text), ...found.filter((link) => !link.text), ...bare].map((link) => lookupName(destination(link))).filter(Boolean);
|
|
9042
10271
|
const firsts = /* @__PURE__ */ new Map();
|
|
9043
|
-
for (const host of
|
|
10272
|
+
for (const host of [...own, ...carriedHosts]) {
|
|
9044
10273
|
const domain = registrableDomain(host);
|
|
9045
10274
|
if (!firsts.has(domain)) {
|
|
9046
10275
|
firsts.set(domain, host);
|
|
9047
10276
|
}
|
|
9048
10277
|
}
|
|
9049
|
-
const checked = [.../* @__PURE__ */ new Set([...firsts.values(), ...
|
|
10278
|
+
const checked = [.../* @__PURE__ */ new Set([...firsts.values(), ...own, ...carriedHosts])].slice(0, config.phishing.maxHosts);
|
|
9050
10279
|
const domains = [...new Set(checked.map((host) => registrableDomain(host)))];
|
|
9051
10280
|
const blocked = [];
|
|
9052
10281
|
let answered = 0;
|
|
10282
|
+
const linkTimeout = Number(config.phishing.timeout) > 0 ? Number(config.phishing.timeout) : config.timeout;
|
|
9053
10283
|
const cloudflare = async () => {
|
|
9054
10284
|
await withTimeout(Promise.all(checked.map(async (host) => {
|
|
9055
10285
|
const found2 = await this.dns.checkCloudflare([host], { adult: config.phishing.adult });
|
|
@@ -9057,12 +10287,12 @@ var SpamScanner = class {
|
|
|
9057
10287
|
if (!found2.unanswered?.length) {
|
|
9058
10288
|
answered++;
|
|
9059
10289
|
}
|
|
9060
|
-
})),
|
|
10290
|
+
})), linkTimeout, null);
|
|
9061
10291
|
return { found: [...blocked], unanswered: checked.length - answered };
|
|
9062
10292
|
};
|
|
9063
10293
|
const [cloudflareResult, listed] = await Promise.all([
|
|
9064
10294
|
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),
|
|
10295
|
+
config.dnsbl.domain?.length > 0 && domains.length > 0 ? withTimeout(this.dns.checkDomains(domains, config.dnsbl.domain), linkTimeout, []) : []
|
|
9066
10296
|
]);
|
|
9067
10297
|
for (const { host, category } of cloudflareResult.found) {
|
|
9068
10298
|
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 +10308,8 @@ var SpamScanner = class {
|
|
|
9078
10308
|
return {
|
|
9079
10309
|
phishing,
|
|
9080
10310
|
homographs,
|
|
9081
|
-
|
|
10311
|
+
// (at least one when the message has more hosts than were read)
|
|
10312
|
+
uncheckedHosts: Math.max(seenHosts.size - checked.length, carried?.more ? 1 : 0),
|
|
9082
10313
|
timedOutHosts: cloudflareResult.unanswered
|
|
9083
10314
|
};
|
|
9084
10315
|
}
|
|
@@ -9250,10 +10481,17 @@ ${visibleText(html.slice(0, config.maxLength * 2)).text}`, { maxLength: config.m
|
|
|
9250
10481
|
styled: plainObfuscation.styled || shownObfuscation.styled
|
|
9251
10482
|
};
|
|
9252
10483
|
const unopened = [];
|
|
9253
|
-
const
|
|
10484
|
+
const opened = [];
|
|
10485
|
+
const attached = await expandAttachments(mail.attachments, parseErrors, void 0, { unopened, mails: opened });
|
|
10486
|
+
const carried = config.phishing.attachments === true && (config.phishing.cloudflare || config.dnsbl.domain?.length > 0) ? carriedLinkHosts(mail, {
|
|
10487
|
+
mails: opened,
|
|
10488
|
+
attachments: attached,
|
|
10489
|
+
maxHosts: config.phishing.maxHosts,
|
|
10490
|
+
hostsInText: config.phishing.hostsInText === true
|
|
10491
|
+
}) : null;
|
|
9254
10492
|
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
10493
|
const [links, authentication, checkedReputation, dnsbl, virusScan, toxicity, nsfw] = await Promise.all([
|
|
9256
|
-
this.checkLinks(checkedLinks, config, extracted.hosts, fromDomain(mail)),
|
|
10494
|
+
this.checkLinks(checkedLinks, config, extracted.hosts, { from: fromDomain(mail), carried }),
|
|
9257
10495
|
config.authenticationResults && typeof config.authenticationResults === "object" ? normalizeAuthOutput(config.authenticationResults) : config.authentication && session.remoteAddress ? withTimeout(authenticate(raw, {
|
|
9258
10496
|
timeout: config.timeout,
|
|
9259
10497
|
...typeof config.authentication === "object" ? config.authentication : {},
|
|
@@ -9381,6 +10619,14 @@ ${visibleText(html.slice(0, config.maxLength * 2)).text}`, { maxLength: config.m
|
|
|
9381
10619
|
// Whether the message has more text than phishing.maxLength, whose
|
|
9382
10620
|
// links were not read.
|
|
9383
10621
|
linksTruncated,
|
|
10622
|
+
// Whether a link host of the message or of what it carries was
|
|
10623
|
+
// never read whole (it runs past where long text is cut to be
|
|
10624
|
+
// read, it goes through more security gateways than are
|
|
10625
|
+
// unwrapped, or it is in more documents than are read), and whether
|
|
10626
|
+
// an attachment was left unread for time: in either case not every
|
|
10627
|
+
// link was checked (see phishing.attachments).
|
|
10628
|
+
linksTooComplex: Boolean(carried?.tooComplex),
|
|
10629
|
+
linksIncomplete: Boolean(carried?.incomplete),
|
|
9384
10630
|
idnHomographAttack: {
|
|
9385
10631
|
detected: links.homographs.length > 0,
|
|
9386
10632
|
domains: links.homographs,
|
|
@@ -9392,6 +10638,9 @@ ${visibleText(html.slice(0, config.maxLength * 2)).text}`, { maxLength: config.m
|
|
|
9392
10638
|
language: extracted.language,
|
|
9393
10639
|
tokens: extracted.words,
|
|
9394
10640
|
mail,
|
|
10641
|
+
// (the attachments of the message and of its attached messages, as
|
|
10642
|
+
// read, an attached message's named after it)
|
|
10643
|
+
attached,
|
|
9395
10644
|
version: VERSION,
|
|
9396
10645
|
metrics: { totalTime: time }
|
|
9397
10646
|
};
|