Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 96x 24x 40x 40x 40x 40x 40x 40x 40x 40x 40x 40x 40x 30x 30x 30x 40x 30x 30x 33x 33x 33x 33x 33x 33x 54x 54x 13x 13x 41x 41x 37x 37x 37x 4x 4x 4x 4x 4x 4x 33x 33x 37x 33x 30x 30x 30x 30x 30x 33x 20x 19x 19x 1x 30x 30x 30x 30x 30x 30x 30x 30x | import { Decoder } from "./2047";
import { Poster } from "./Poster";
import { AntiSpamReport } from "./row/Antispam";
import { ForefrontAntiSpamReport } from "./row/ForefrontAntispam";
import { Header } from "./row/Header";
import { rulesService } from "./rules";
import { ViolationGroup } from "./rules/types/AnalysisTypes";
import { Summary } from "./Summary";
import { Other } from "./table/Other";
import { Received } from "./table/Received";
export class HeaderModel {
public originalHeaders: string;
public summary: Summary;
public receivedHeaders: Received;
public forefrontAntiSpamReport: ForefrontAntiSpamReport;
public antiSpamReport: AntiSpamReport;
public otherHeaders: Other;
public violationGroups: ViolationGroup[];
private hasDataInternal: boolean;
private statusInternal: string;
public get hasData(): boolean { return this.hasDataInternal || !!this.statusInternal; }
public get status(): string { return this.statusInternal; }
public set status(value) { this.statusInternal = value; }
[index: string]: unknown;
private constructor() {
this.summary = new Summary();
this.receivedHeaders = new Received();
this.forefrontAntiSpamReport = new ForefrontAntiSpamReport();
this.antiSpamReport = new AntiSpamReport();
this.otherHeaders = new Other();
this.originalHeaders = "";
this.statusInternal = "";
this.hasDataInternal = false;
this.violationGroups = [];
}
public static async create(headers?: string): Promise<HeaderModel> {
const model = new HeaderModel();
if (headers) {
model.parseHeaders(headers);
await model.analyzeRules();
Poster.postMessageToParent("modelToString", model.toString());
}
return model;
}
private async analyzeRules(): Promise<void> {
const validationResult = await rulesService.analyzeHeaders(this);
this.violationGroups = validationResult.violationGroups;
}
public static getHeaderList(headers: string): Header[] {
// First, break up out input by lines.
// Keep empty lines for recognizing the boundary between the header section & the body.
const lines: string[] = headers.split(/\r\n|\r|\n/);
const headerList: Header[] = [];
let iNextHeader = 0;
let prevHeader: Header | undefined;
let body = false;
headerSection: while (!body) {
unfoldLines: for (let line of lines) {
// Handling empty lines. The body is separated from the header section by an empty line (RFC 5322, 2.1).
// To avoid processing the body as headers we should stop there, as someone might paste an entire message.
// Empty lines at the beginning can be omitted, because that could be a common copy-paste error.
Iif (body) break headerSection;
if (line === "") {
Eif (headerList.length > 0) body = true;
continue unfoldLines;
}
// Recognizing a header:
// - First colon comes before first white space.
// - We're not strictly honoring white space folding because initial white space
// - is commonly lost. Instead, we heuristically assume that space before a colon must have been folded.
// This expression will give us:
// match[1] - everything before the first colon, assuming no spaces (header).
// match[2] - everything after the first colon (value).
const match: RegExpMatchArray | null = line.match(/(^[\w-.]*?): ?(.*)/);
// There's one false positive we might get: if the time in a Received header has been
// folded to the next line, the line might start with something like "16:20:05 -0400".
// This matches our regular expression. The RFC does not preclude such a header, but I've
// never seen one in practice, so we check for and exclude 'headers' that
// consist only of 1 or 2 digits.
if (match && match[1] && !match[1].match(/^\d{1,2}$/)) {
headerList[iNextHeader] = new Header(match[1], match[2] ?? "");
prevHeader = headerList[iNextHeader];
iNextHeader++;
} else {
if (iNextHeader > 0) {
// Tack this line to the previous line
// All folding whitespace should collapse to a single space
line = line.replace(/^[\s]+/, "");
Iif (!line) continue unfoldLines;
Eif (prevHeader) {
const separator: string = prevHeader.value ? " " : "";
prevHeader.value += separator + line;
}
} else E{
// If we didn't have a previous line, go ahead and use this line
if (line.match(/\S/g)) {
headerList[iNextHeader] = new Header("", line);
prevHeader = headerList[iNextHeader];
iNextHeader++;
}
}
}
}
break headerSection;
}
// 2047 decode our headers now
headerList.forEach((header: Header) => {
// Clean 2047 encoding
// Strip nulls
// Strip trailing carriage returns
header.value = Decoder.clean2047Encoding(header.value).replace(/\0/g, "").replace(/[\n\r]+$/, "");
});
return headerList;
}
public parseHeaders(headers: string): void {
// Initialize originalHeaders in case we have parsing problems
// Flatten CRLF to LF to avoid extra blank lines
this.originalHeaders = headers.replace(/(?:\r\n|\r|\n)/g, "\n");
const headerList: Header[] = HeaderModel.getHeaderList(headers);
Eif (headerList.length > 0) {
this.hasDataInternal = true;
}
headerList.forEach((header: Header) => {
// Grab values for our summary pane
if (this.summary.add(header)) return;
// Properties with special parsing
if (this.forefrontAntiSpamReport.add(header)) return;
Iif (this.antiSpamReport.add(header)) return;
if (this.receivedHeaders.add(header)) return;
this.otherHeaders.add(header);
});
this.summary.totalTime = this.receivedHeaders.computeDeltas();
}
public toString(): string {
const ret: string[] = [];
if (this.summary.exists()) ret.push(this.summary.toString());
if (this.receivedHeaders.exists()) ret.push(this.receivedHeaders.toString());
if (this.forefrontAntiSpamReport.exists()) ret.push(this.forefrontAntiSpamReport.toString());
Iif (this.antiSpamReport.exists()) ret.push(this.antiSpamReport.toString());
if (this.otherHeaders.exists()) ret.push(this.otherHeaders.toString());
return ret.join("\n\n");
}
}
|