001/** 002 * Java Web Archive Toolkit - Software to read and validate ARC, WARC 003 * and GZip files. (http://jwat.org/) 004 * Copyright 2011-2012 Netarkivet.dk (http://netarkivet.dk/) 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.jwat.common; 019 020import java.io.IOException; 021import java.io.InputStream; 022import java.io.PushbackInputStream; 023 024/** 025 * Advanced header/line reader which can be configured into difference modes. 026 * The reader can either read normal lines or header lines. 027 * Supported encodings are raw, US-ASCII, ISO-8859-1 and UTF-8. 028 * 029 * Furthermore header lines can employ linear white space (LWS), quoted text 030 * and encoded words. 031 * 032 * After calling the readLine method additional information is available from 033 * public fields on the reader. 034 * 035 * @author nicl 036 */ 037public class HeaderLineReader { 038 039 /* 040 * Internal states. 041 */ 042 043 /** Initial state for reading a normal line. */ 044 protected static final int S_LINE = 0; 045 /** Initial state for reading a header line. */ 046 protected static final int S_NAME = 1; 047 /** State for reading a header value. */ 048 protected static final int S_VALUE = 2; 049 /** State for reading a LWS character sequence. */ 050 protected static final int S_LWS = 3; 051 /** State for reading a quoted string. */ 052 protected static final int S_QUOTED_TEXT = 4; 053 /** State for reading a quoted pair character. */ 054 protected static final int S_QUOTED_PAIR = 5; 055 /** State for reading a quoted LWS character sequence. */ 056 protected static final int S_QUOTED_LWS = 6; 057 /** Status for reading an encoded word character sequence. */ 058 //protected static final int S_ENCODED_WORD_EQ = 7; 059 060 /* 061 * 8-bit character characteristics. 062 */ 063 064 /** Control character characteristic. */ 065 protected static final int CC_CONTROL = 1; 066 /** Separator character characteristic. */ 067 protected static final int CC_SEPARATOR_WS = 2; 068 069 /** rfc2616 separator characters. */ 070 public static final String separatorsWs = "()<>@,;:\\\"/[]?={} \t"; 071 072 /** Table of separator characters. */ 073 public static final byte[] charCharacteristicsTab = new byte[256]; 074 075 /** 076 * Populate table of separators. 077 */ 078 static { 079 for (int i=0; i<32; ++i) { 080 if (i != '\t') { 081 charCharacteristicsTab[i] |= CC_CONTROL; 082 } 083 } 084 charCharacteristicsTab[127] |= CC_CONTROL; 085 for (int i=0; i<separatorsWs.length(); ++i) { 086 charCharacteristicsTab[separatorsWs.charAt(i)] |= CC_SEPARATOR_WS; 087 } 088 } 089 090 /* 091 * Encoding. 092 */ 093 094 /** Raw encoding identifier. */ 095 public static final int ENC_RAW = 0; 096 /** US-ASCII encoding identifier. */ 097 public static final int ENC_US_ASCII = 1; 098 /** ISO-8859-1 encoding identifier. */ 099 public static final int ENC_ISO8859_1 = 2; 100 /** UTF-8 encoding identifier. */ 101 public static final int ENC_UTF8 = 3; 102 103 /** Reusable UTF-8 validation object instance. */ 104 protected final UTF8 utf8 = new UTF8(); 105 106 /* 107 * EOL. 108 */ 109 110 /** LF end of line identifier. */ 111 public static final int EOL_LF = 0; 112 /** CRLF end of line identifier. */ 113 public static final int EOL_CRLF = 1; 114 115 /* 116 * Configuration. 117 */ 118 119 /** Parses a headerline if true and a normal line if false. */ 120 public boolean bNameValue; 121 /** Identifier for the expected character encoding. */ 122 public int encoding = ENC_RAW; 123 /** Identifier for the Expected end of line character sequence. */ 124 public int eol = EOL_CRLF; 125 /** Support LWS. */ 126 public boolean bLWS; 127 /** Support quoted text. */ 128 public boolean bQuotedText; 129 /** Support encoded words. */ 130 public boolean bEncodedWords; 131 132 /** Reusable <code>StringBuffer</code> for lines. */ 133 protected final StringBuffer lineSb = new StringBuffer(); 134 /** Reusable <code>StringBuffer</code> for name/value strings. */ 135 protected final StringBuffer nvSb = new StringBuffer(); 136 /** Stream used to record the raw characters read by the parser. */ 137 protected ByteArrayOutputStreamWithUnread bytesOut = new ByteArrayOutputStreamWithUnread(); 138 139 /* 140 * Error reporting. 141 */ 142 143 /** Bit denoting unexpected EOF. */ 144 public static final int E_BIT_EOF = 1 << 0; 145 /** Bit denoting a misplaced CR. */ 146 public static final int E_BIT_MISPLACED_CR = 1 << 1; 147 /** Bit denoting a missing CR. */ 148 public static final int E_BIT_MISSING_CR = 1 << 2; 149 /** Bit denoting an unexpected CR. */ 150 public static final int E_BIT_UNEXPECTED_CR = 1 << 3; 151 /** Bit denoting an invalid UTF-8 encoded character. */ 152 public static final int E_BIT_INVALID_UTF8_ENCODING = 1 << 4; 153 /** Bit denoting an invalid US-ASCII character. */ 154 public static final int E_BIT_INVALID_US_ASCII_CHAR = 1 << 5; 155 /** Bit denoting an invalid control character. */ 156 public static final int E_BIT_INVALID_CONTROL_CHAR = 1 << 6; 157 /** Bit denoting an invalid separator character. */ 158 public static final int E_BIT_INVALID_SEPARATOR_CHAR = 1 << 7; 159 /** Bit denoting a missing quote character. */ 160 public static final int E_BIT_MISSING_QUOTE = 1 << 8; 161 /** Bit denoting a missing quoted pair character. */ 162 public static final int E_BIT_MISSING_QUOTED_PAIR_CHAR = 1 << 9; 163 /** Bit denoting an invalid quoted pair character. */ 164 public static final int E_BIT_INVALID_QUOTED_PAIR_CHAR = 1 << 10; 165 /** Bit denoting an invalid encoding. */ 166 public static final int E_BIT_INVALID_CHARSET = 1 << 11; 167 168 /* 169 * Internal state. 170 */ 171 172 /** True if the previous character was a CR. */ 173 protected boolean bCr = false; 174 175 /** Used by decode method to indicated valid or non valid character. */ 176 protected boolean bValidChar; 177 178 /* 179 * Exposed state. 180 */ 181 182 /** Boolean indicating whether or not EOF has been reached on stream. */ 183 public boolean bEof; 184 185 /** Bit field of errors encountered while attempting to read a line. */ 186 public int bfErrors; 187 188 /** 189 * Prohibit public construction. 190 */ 191 protected HeaderLineReader() { 192 } 193 194 /** 195 * Returns a reader with default configuration values. 196 * @return a reader with default configuration values 197 */ 198 public static HeaderLineReader getReader() { 199 return new HeaderLineReader(); 200 } 201 202 /** 203 * Returns a reader initialized to read normal lines. 204 * Normal lines being lines with no LWS or key:value headers. 205 * The reader is configured to expect US-ASCII characters. 206 * @return a reader to read normal lines 207 */ 208 public static HeaderLineReader getLineReader() { 209 HeaderLineReader hlr = new HeaderLineReader(); 210 hlr.bNameValue = false; 211 hlr.encoding = ENC_US_ASCII; 212 return hlr; 213 } 214 215 /** 216 * Returns a reader initialized to read header lines. 217 * The reader is configured to expect ISO-8859-1 encoding, LWS, 218 * quoted text and encoded words. Besides reading key:value headers it will 219 * also read and return normal lines as defined in the method above. 220 * @return a reader to read header lines 221 */ 222 public static HeaderLineReader getHeaderLineReader() { 223 HeaderLineReader hlr = new HeaderLineReader(); 224 hlr.bNameValue = true; 225 hlr.encoding = ENC_ISO8859_1; 226 hlr.eol = EOL_CRLF; 227 hlr.bLWS = true; 228 hlr.bQuotedText = true; 229 hlr.bEncodedWords = true; 230 return hlr; 231 } 232 233 /** 234 * Reads a header/line according to the configuration. 235 * After calling the readLine method additional information is available 236 * from public fields on the reader. 237 * @param in <code>InputStream</code> with characters 238 * @return result wrapped in a <code>HeaderLine</code> object 239 * @throws IOException if an I/O error occurs in the underlying input stream 240 */ 241 public HeaderLine readLine(PushbackInputStream in) throws IOException { 242 HeaderLine headerLine = new HeaderLine(); 243 int state; 244 if (!bNameValue) { 245 state = S_LINE; 246 } else { 247 state = S_NAME; 248 } 249 lineSb.setLength(0); 250 nvSb.setLength(0); 251 bytesOut = new ByteArrayOutputStreamWithUnread(); 252 bfErrors = 0; 253 int c; 254 bCr = false; 255 boolean bLoop = true; 256 while (bLoop) { 257 c = in.read(); 258 if (c != -1) { 259 bytesOut.write(c); 260 } 261 switch (state) { 262 case S_LINE: 263 switch (c) { 264 case -1: 265 // EOF. 266 bfErrors |= E_BIT_EOF; 267 headerLine.type = HeaderLine.HLT_LINE; 268 headerLine.line = lineSb.toString(); 269 lineSb.setLength(0); 270 bLoop = false; 271 break; 272 case '\r': 273 bCr = true; 274 break; 275 case '\n': 276 headerLine.type = HeaderLine.HLT_LINE; 277 headerLine.line = lineSb.toString(); 278 lineSb.setLength(0); 279 // Check EOL. 280 check_eol(); 281 bLoop = false; 282 break; 283 default: 284 if (bCr) { 285 // Misplaced CR. 286 bfErrors |= E_BIT_MISPLACED_CR; 287 bCr = false; 288 } 289 // Decode character. 290 c = decode(c, in); 291 if (c == -1) { 292 // EOF. 293 bfErrors |= E_BIT_EOF; 294 headerLine.type = HeaderLine.HLT_LINE; 295 headerLine.line = lineSb.toString(); 296 lineSb.setLength(0); 297 bLoop = false; 298 } else { 299 if (bValidChar && encoding != ENC_RAW) { 300 if (c < 256 && ((charCharacteristicsTab[c] & CC_CONTROL) == CC_CONTROL)) { 301 bValidChar = false; 302 // Invalid control char 303 bfErrors |= E_BIT_INVALID_CONTROL_CHAR; 304 } 305 } 306 if (bValidChar) { 307 lineSb.append((char) c); 308 } 309 } 310 break; 311 } 312 break; 313 case S_NAME: 314 switch (c) { 315 case -1: 316 // EOF. 317 bfErrors |= E_BIT_EOF; 318 headerLine.type = HeaderLine.HLT_LINE; 319 headerLine.line = lineSb.toString(); 320 lineSb.setLength(0); 321 nvSb.setLength(0); 322 bLoop = false; 323 break; 324 case '\r': 325 bCr = true; 326 break; 327 case '\n': 328 headerLine.type = HeaderLine.HLT_LINE; 329 headerLine.line = lineSb.toString(); 330 lineSb.setLength(0); 331 nvSb.setLength(0); 332 // Check EOL. 333 check_eol(); 334 bLoop = false; 335 break; 336 case ':': 337 headerLine.type = HeaderLine.HLT_HEADERLINE; 338 headerLine.name = nvSb.toString(); 339 lineSb.setLength(0); 340 nvSb.setLength(0); 341 if (bCr) { 342 // Misplaced CR. 343 bfErrors |= E_BIT_MISPLACED_CR; 344 bCr = false; 345 } 346 state = S_VALUE; 347 break; 348 default: 349 if (bCr) { 350 // Misplaced CR. 351 bfErrors |= E_BIT_MISPLACED_CR; 352 bCr = false; 353 } 354 // Decode character. 355 c = decode(c, in); 356 if (c == -1) { 357 // EOF. 358 bfErrors |= E_BIT_EOF; 359 headerLine.type = HeaderLine.HLT_LINE; 360 headerLine.line = lineSb.toString(); 361 lineSb.setLength(0); 362 nvSb.setLength(0); 363 bLoop = false; 364 } else { 365 if (bValidChar && encoding != ENC_RAW) { 366 if (c < 256 && ((charCharacteristicsTab[c] & CC_CONTROL) == CC_CONTROL)) { 367 bValidChar = false; 368 // Invalid control char 369 bfErrors |= E_BIT_INVALID_CONTROL_CHAR; 370 } 371 } 372 if (bValidChar) { 373 lineSb.append((char) c); 374 if (c < 256 && ((charCharacteristicsTab[c] & CC_SEPARATOR_WS) == CC_SEPARATOR_WS)) { 375 bValidChar = false; 376 // Invalid separator in name 377 bfErrors |= E_BIT_INVALID_SEPARATOR_CHAR; 378 } 379 } 380 if (bValidChar) { 381 nvSb.append((char) c); 382 } 383 } 384 break; 385 } 386 break; 387 case S_VALUE: 388 switch (c) { 389 case -1: 390 // EOF. 391 bfErrors |= E_BIT_EOF; 392 headerLine.value = trim(nvSb); 393 nvSb.setLength(0); 394 bLoop = false; 395 break; 396 case '\r': 397 bCr = true; 398 break; 399 case '\n': 400 // Check EOL. 401 check_eol(); 402 if (bLWS) { 403 state = S_LWS; 404 } else { 405 headerLine.value = trim(nvSb); 406 nvSb.setLength(0); 407 bLoop = false; 408 } 409 break; 410 default: 411 if (bCr) { 412 // Misplaced CR. 413 bfErrors |= E_BIT_MISPLACED_CR; 414 bCr = false; 415 } 416 // Decode character. 417 c = decode(c, in); 418 if (c == -1) { 419 // EOF. 420 bfErrors |= E_BIT_EOF; 421 headerLine.value = trim(nvSb); 422 nvSb.setLength(0); 423 bLoop = false; 424 } else { 425 if (bValidChar && encoding != ENC_RAW) { 426 if (c < 256 && ((charCharacteristicsTab[c] & CC_CONTROL) == CC_CONTROL)) { 427 bValidChar = false; 428 // Invalid control char 429 bfErrors |= E_BIT_INVALID_CONTROL_CHAR; 430 } 431 } 432 if (bValidChar) { 433 switch (c) { 434 case '\"': 435 nvSb.append((char)c); 436 if (bQuotedText) { 437 state = S_QUOTED_TEXT; 438 } 439 break; 440/* 441 case '=': 442 if (bEncodedWords) { 443 state = S_ENCODED_WORD_EQ; 444 } else { 445 nvSb.append((char)c); 446 } 447 break; 448*/ 449 default: 450 nvSb.append((char)c); 451 break; 452 } 453 } 454 } 455 break; 456 } 457 break; 458 case S_LWS: 459 switch (c) { 460 case -1: 461 // EOF. 462 //bfErrors |= E_BIT_EOF; 463 headerLine.value = trim(nvSb); 464 nvSb.setLength(0); 465 bLoop = false; 466 break; 467 case ' ': 468 case '\t': 469 nvSb.append(" "); 470 state = S_VALUE; 471 break; 472 default: 473 in.unread(c); 474 bytesOut.unread(c); 475 headerLine.value = trim(nvSb); 476 nvSb.setLength(0); 477 bLoop = false; 478 break; 479 } 480 break; 481 case S_QUOTED_TEXT: 482 switch (c) { 483 case -1: 484 // EOF. 485 bfErrors |= E_BIT_MISSING_QUOTE | E_BIT_EOF; 486 headerLine.value = trim(nvSb); 487 nvSb.setLength(0); 488 bLoop = false; 489 break; 490 case '\"': 491 if (bCr) { 492 // Misplaced CR. 493 bfErrors |= E_BIT_MISPLACED_CR; 494 bCr = false; 495 } 496 nvSb.append((char)c); 497 state = S_VALUE; 498 break; 499 case '\\': 500 if (bCr) { 501 // Misplaced CR. 502 bfErrors |= E_BIT_MISPLACED_CR; 503 bCr = false; 504 } 505 state = S_QUOTED_PAIR; 506 break; 507 case '\r': 508 bCr = true; 509 break; 510 case '\n': 511 // Check EOL. 512 check_eol(); 513 if (bLWS) { 514 state = S_QUOTED_LWS; 515 } else { 516 headerLine.value = trim(nvSb); 517 nvSb.setLength(0); 518 bLoop = false; 519 } 520 break; 521 default: 522 if (bCr) { 523 // Misplaced CR. 524 bfErrors |= E_BIT_MISPLACED_CR; 525 bCr = false; 526 } 527 // Decode character. 528 c = decode(c, in); 529 if (c == -1) { 530 // EOF. 531 bfErrors |= E_BIT_MISSING_QUOTE | E_BIT_EOF; 532 headerLine.value = trim(nvSb); 533 nvSb.setLength(0); 534 bLoop = false; 535 } else { 536 if (bValidChar && encoding != ENC_RAW) { 537 if (c < 256 && ((charCharacteristicsTab[c] & CC_CONTROL) == CC_CONTROL)) { 538 bValidChar = false; 539 // Invalid control char 540 bfErrors |= E_BIT_INVALID_CONTROL_CHAR; 541 } 542 } 543 if (bValidChar) { 544 nvSb.append((char)c); 545 } 546 } 547 break; 548 } 549 break; 550 case S_QUOTED_PAIR: 551 switch (c) { 552 case -1: 553 nvSb.append('\\'); 554 // EOF. 555 bfErrors |= E_BIT_MISSING_QUOTED_PAIR_CHAR | E_BIT_MISSING_QUOTE | E_BIT_EOF; 556 headerLine.value = trim(nvSb); 557 nvSb.setLength(0); 558 bLoop = false; 559 break; 560 default: 561 // Decode character. 562 c = decode(c, in); 563 if (c == -1) { 564 // EOF. 565 bfErrors |= E_BIT_MISSING_QUOTED_PAIR_CHAR | E_BIT_MISSING_QUOTE | E_BIT_EOF; 566 headerLine.value = trim(nvSb); 567 nvSb.setLength(0); 568 bLoop = false; 569 } else { 570 nvSb.append('\\'); 571 nvSb.append((char)c); 572 if (!bValidChar) { 573 bfErrors |= E_BIT_INVALID_QUOTED_PAIR_CHAR; 574 } 575 state = S_QUOTED_TEXT; 576 } 577 break; 578 } 579 break; 580 case S_QUOTED_LWS: 581 switch (c) { 582 case -1: 583 // EOF. 584 bfErrors |= E_BIT_MISSING_QUOTE; 585 headerLine.value = trim(nvSb); 586 nvSb.setLength(0); 587 bLoop = false; 588 break; 589 case ' ': 590 case '\t': 591 nvSb.append(" "); 592 state = S_QUOTED_TEXT; 593 break; 594 default: 595 in.unread(c); 596 bytesOut.unread(c); 597 bfErrors |= E_BIT_MISSING_QUOTE; 598 headerLine.value = trim(nvSb); 599 nvSb.setLength(0); 600 bLoop = false; 601 break; 602 } 603 break; 604/* 605 case S_ENCODED_WORD_EQ: 606 switch (c) { 607 case -1: 608 nvSb.append('='); 609 // EOF. 610 bfErrors |= E_BIT_EOF; 611 headerLine.value = trim(nvSb); 612 nvSb.setLength(0); 613 bLoop = false; 614 break; 615 case '?': 616 // Unread "=?", so it can be parsed as an EncodedWord which always starts with "=?" 617 in.unread('?'); 618 in.unread('='); 619 bytesOut.unread('?'); 620 bytesOut.unread('='); 621 EncodedWords ew = EncodedWords.parseEncodedWords(in, true); 622*/ 623 /* 624 if (!ew.bIsValid) { 625 // TODO Decide whether to report encoded word errors or interpret as non encoded words. 626 } 627 */ 628/* 629 if (!ew.bValidCharset) { 630 // In case of invalid charset errors, try to report. 631 bfErrors |= E_BIT_INVALID_CHARSET; 632 // Possible message : "Invalid charset : " + ew.charsetStr; 633 } 634 nvSb.append("=?"); 635 in.unread(ew.line, 2, ew.line.length - 2); 636 bytesOut.write("=?".getBytes()); 637 state = S_VALUE; 638 break; 639 default: 640 nvSb.append('='); 641 in.unread(c); 642 bytesOut.unread(c); 643 state = S_VALUE; 644 break; 645 } 646 break; 647*/ 648 } 649 } 650 headerLine.raw = bytesOut.toByteArray(); 651 headerLine.bfErrors = bfErrors; 652 bEof = (headerLine.raw.length == 0); 653 return headerLine; 654 } 655 656 /** 657 * Decode a character according to the expected encoding. 658 * @param c first character of the possibly encoded character sequence 659 * @param in <code>InputStream</code> with possible extra encoded characters. 660 * @return decoded character 661 * @throws IOException if an I/O error occurs in the underlying input stream 662 */ 663 protected int decode(int c, InputStream in) throws IOException { 664 switch (encoding) { 665 case ENC_UTF8: 666 c = utf8.readUtf8(c, in); 667 bytesOut.write(utf8.chars_read); 668 bValidChar = utf8.bValidChar; 669 if (c != -1) { 670 if (!bValidChar) { 671 // Invalid UTF-8 char 672 bfErrors |= E_BIT_INVALID_UTF8_ENCODING; 673 } 674 } 675 break; 676 case ENC_US_ASCII: 677 bValidChar = (c <= 127); 678 if (!bValidChar) { 679 // Invalid US-ASCII char 680 bfErrors |= E_BIT_INVALID_US_ASCII_CHAR; 681 } 682 break; 683 case ENC_ISO8859_1: 684 // ISO-8859-1 utilizes all 8-bits and requires no decoding. 685 case ENC_RAW: 686 // Raw 8-bit character needs no decoding. 687 default: 688 bValidChar = true; 689 break; 690 } 691 return c; 692 } 693 694 /** 695 * Check and report whether the line ended as expected. 696 */ 697 protected void check_eol() { 698 switch (eol) { 699 case EOL_LF: 700 if (!bCr) { 701 // Unexpected CR. 702 bfErrors |= E_BIT_UNEXPECTED_CR; 703 } 704 break; 705 case EOL_CRLF: 706 if (!bCr) { 707 // Missing CR. 708 bfErrors |= E_BIT_MISSING_CR; 709 } 710 break; 711 } 712 bCr = false; 713 } 714 715 /** 716 * Trims the whitespace characters found in the beginning and end of a 717 * string. Differs from the String method in that it leaves control 718 * characters. 719 * @param sb <code>StringBuffer</code> to be trimmed 720 * @return trimmed string 721 */ 722 public static String trim(StringBuffer sb) { 723 int sIdx = 0; 724 int eIdx = sb.length(); 725 while (sIdx < eIdx && sb.charAt(sIdx) == ' ') { 726 ++sIdx; 727 } 728 while (eIdx > sIdx && sb.charAt(eIdx - 1) == ' ') { 729 --eIdx; 730 } 731 return sb.substring(sIdx, eIdx); 732 } 733 734 /** 735 * Report bit field errors as diagnoses. 736 * @param bfErrors bit field with indicated errors 737 * @param diagnostics diagnostics object used to report diagnoses 738 */ 739 public static void report_error(int bfErrors, Diagnostics diagnostics) { 740 if (diagnostics == null) { 741 throw new IllegalArgumentException("'diagnostics' argument is null"); 742 } 743 if ((bfErrors & E_BIT_EOF) != 0) { 744 diagnostics.addError(new Diagnosis(DiagnosisType.ERROR, "header/line", "Unexpected EOF")); 745 } 746 if ((bfErrors & E_BIT_MISPLACED_CR) != 0) { 747 diagnostics.addError(new Diagnosis(DiagnosisType.ERROR, "header/line", "Misplaced CR")); 748 } 749 if ((bfErrors & E_BIT_MISSING_CR) != 0) { 750 diagnostics.addError(new Diagnosis(DiagnosisType.ERROR, "header/line", "Missing CR")); 751 } 752 if ((bfErrors & E_BIT_UNEXPECTED_CR) != 0) { 753 diagnostics.addError(new Diagnosis(DiagnosisType.ERROR, "header/line", "Unexpected CR")); 754 } 755 if ((bfErrors & E_BIT_INVALID_UTF8_ENCODING) != 0) { 756 diagnostics.addError(new Diagnosis(DiagnosisType.ERROR, "header/line", "Invalid UTF-8 encoded character")); 757 } 758 if ((bfErrors & E_BIT_INVALID_US_ASCII_CHAR) != 0) { 759 diagnostics.addError(new Diagnosis(DiagnosisType.ERROR, "header/line", "Invalid US-ASCII character")); 760 } 761 if ((bfErrors & E_BIT_INVALID_CONTROL_CHAR) != 0) { 762 diagnostics.addError(new Diagnosis(DiagnosisType.ERROR, "header/line", "Invalid control character")); 763 } 764 if ((bfErrors & E_BIT_INVALID_SEPARATOR_CHAR) != 0) { 765 diagnostics.addError(new Diagnosis(DiagnosisType.ERROR, "header/line", "Invalid separator character")); 766 } 767 if ((bfErrors & E_BIT_MISSING_QUOTE) != 0) { 768 diagnostics.addError(new Diagnosis(DiagnosisType.ERROR, "header/line", "Missing quote character")); 769 } 770 if ((bfErrors & E_BIT_MISSING_QUOTED_PAIR_CHAR) != 0) { 771 diagnostics.addError(new Diagnosis(DiagnosisType.ERROR, "header/line", "Missing quoted pair character")); 772 } 773 if ((bfErrors & E_BIT_INVALID_QUOTED_PAIR_CHAR) != 0) { 774 diagnostics.addError(new Diagnosis(DiagnosisType.ERROR, "header/line", "Invalid quoted pair character")); 775 } 776 if ((bfErrors & E_BIT_INVALID_CHARSET) != 0) { 777 diagnostics.addError(new Diagnosis(DiagnosisType.ERROR, "header/line", "Invalid charset")); 778 } 779 } 780 781}