1 17 package scriptella.driver.ldap.ldif; 18 19 import scriptella.util.StringUtils; 20 21 import javax.naming.directory.DirContext ; 22 import javax.naming.ldap.BasicControl ; 23 import javax.naming.ldap.Control ; 24 import java.io.BufferedReader ; 25 import java.io.IOException ; 26 import java.io.InputStream ; 27 import java.io.Reader ; 28 import java.io.StringReader ; 29 import java.io.UnsupportedEncodingException ; 30 import java.net.URL ; 31 import java.util.ArrayList ; 32 import java.util.Iterator ; 33 import java.util.List ; 34 import java.util.NoSuchElementException ; 35 import java.util.regex.Matcher ; 36 import java.util.regex.Pattern ; 37 38 133 public class LdifReader implements Iterator <Entry>, Iterable <Entry> { 134 135 138 private final List <String > lines=new ArrayList <String >(); 139 140 143 private static final int DEFAULT_VERSION = 1; 144 145 148 private int version=DEFAULT_VERSION; 149 150 153 private static final int ENTRY = 0; 154 155 private static final int CHANGE = 1; 156 157 private static final int UNKNOWN = 2; 158 159 162 private long sizeLimit = SIZE_LIMIT_DEFAULT; 163 164 167 private static final long SIZE_LIMIT_DEFAULT = 1024000; 168 169 172 private static final int MOD_SPEC = 0; 173 174 private static final int ATTRVAL_SPEC = 1; 175 176 private static final int ATTRVAL_SPEC_OR_SEP = 2; 177 178 181 private Entry prefetched; 182 183 186 private Reader in; 187 188 191 private boolean containsEntries; 192 193 196 private boolean containsChanges; 197 198 199 202 public LdifReader() { 203 } 204 205 private void init(BufferedReader in) throws LdifParseException { 206 this.in = in; 207 208 version = parseVersion(); 210 prefetched = parseEntry(); 211 } 212 213 219 public LdifReader(Reader in) { 220 if (in instanceof BufferedReader ) { init((BufferedReader ) in); 222 } else { 223 init(new BufferedReader (in)); 224 } 225 } 226 227 230 public int getVersion() { 231 return version; 232 } 233 234 237 public long getSizeLimit() { 238 return sizeLimit; 239 } 240 241 246 public void setSizeLimit(long sizeLimit) { 247 this.sizeLimit = sizeLimit; 248 } 249 250 251 257 private int parseChangeType(String line) { 258 int operation = Entry.ADD; 259 260 String modOp = line.substring("changetype:".length() + 1).trim(); 261 262 if ("add".equalsIgnoreCase(modOp)) { 263 operation = Entry.ADD; 264 } else if ("delete".equalsIgnoreCase(modOp)) { 265 operation = Entry.DELETE; 266 } else if ("modify".equalsIgnoreCase(modOp)) { 267 operation = Entry.MODIFY; 268 } else if ("moddn".equalsIgnoreCase(modOp)) { 269 operation = Entry.MODDN; 270 } else if ("modrdn".equalsIgnoreCase(modOp)) { 271 operation = Entry.MODRDN; 272 } 273 274 return operation; 275 } 276 277 283 private String parseDn(String line) { 284 String dn = null; 285 286 String lowerLine = line.toLowerCase(); 287 288 if (lowerLine.startsWith("dn:") || lowerLine.startsWith("DN:")) { 289 int length = line.length(); 291 292 if (length == 3) { 293 throw new LdifParseException("No DN for entry", line); 295 } else if (line.charAt(3) == ':') { 296 if (length > 4) { 297 String trimmedLine = line.substring(4).trim(); 299 300 try { 301 dn = new String (Utils.base64Decode(trimmedLine.toCharArray()), "UTF-8"); 302 } 303 catch (UnsupportedEncodingException uee) { 304 throw new LdifParseException("Invalid base 64 encoded DN",line); 306 } 307 } else { 308 throw new LdifParseException("No DN for entry", line); 310 } 311 } else { 312 dn = line.substring(3).trim(); 313 } 314 } else { 315 throw new LdifParseException("No DN for entry", line); 316 } 317 318 return dn; 319 } 320 321 329 private Object parseValue(String line, int pos) { 330 if (line.length() > pos + 1) { 331 char c = line.charAt(pos + 1); 332 333 if (c == ':') { 334 String value = line.substring(pos + 2).trim(); 335 336 return Utils.base64Decode(value.toCharArray()); 337 } else if (c == '<') { 338 String urlName = line.substring(pos + 2).trim(); 339 try { 340 return Utils.toByteArray(getUriStream(urlName), sizeLimit); 341 } catch (IOException e) { 342 throw new LdifParseException("Failed to read \""+urlName+"\" file content",line,e); 343 } 344 } else { 345 return line.substring(pos + 1).trim(); 346 } 347 } else { 348 return null; 349 } 350 } 351 352 359 protected InputStream getUriStream(String uri) throws IOException { 360 return new URL (uri).openStream(); 361 } 362 363 377 private Control parseControl(String line) { 378 String lowerLine = line.toLowerCase().trim(); 379 char[] controlValue = line.trim().toCharArray(); 380 int pos = 0; 381 int length = controlValue.length; 382 383 if (pos > length) { 385 throw new LdifParseException("Bad control, no oid", line); 387 } 388 389 int initPos = pos; 390 391 while (Utils.isCharASCII(controlValue, pos, '.') || Utils.isDigit(controlValue, pos)) { 392 pos++; 393 } 394 395 if (pos == initPos) { 396 throw new LdifParseException("Bad control, no oid", line); 398 } 399 400 401 String oid = lowerLine.substring(0, pos); 402 boolean criticality=false; 403 byte[] controlBytes = null; 404 405 406 407 while (Utils.isCharASCII(controlValue, pos, ' ')) { 410 pos++; 411 } 412 413 int criticalPos = lowerLine.indexOf(':'); 415 416 int criticalLength = 0; 417 418 if (criticalPos == -1) { 419 criticalLength = length - pos; 420 } else { 421 criticalLength = criticalPos - pos; 422 } 423 424 if ((criticalLength == 4) && ("true".equalsIgnoreCase(lowerLine.substring(pos, pos + 4)))) { 425 criticality=true; 426 } else if ((criticalLength == 5) && ("false".equalsIgnoreCase(lowerLine.substring(pos, pos + 5)))) { 427 criticality=false; 428 } else if (criticalLength != 0) { 429 throw new LdifParseException("Bad control criticality", line); 432 } 433 434 if (criticalPos > 0) { 435 if (Utils.isCharASCII(controlValue, criticalPos + 1, ':')) { 438 controlBytes = Utils.base64Decode(line.substring(criticalPos + 2).toCharArray()); 440 } else if (Utils.isCharASCII(controlValue, criticalPos + 1, '<')) { 441 } else { 443 byte[] value = new byte[length - criticalPos - 1]; 445 446 for (int i = 0; i < length - criticalPos - 1; i++) { 447 value[i] = (byte) controlValue[i + criticalPos + 1]; 448 } 449 450 controlBytes=value; 451 } 452 } 453 454 return new BasicControl (oid, criticality, controlBytes); 455 } 456 457 458 464 public void parseAttributeValue(Entry entry, String line) { 465 int colonIndex = line.indexOf(':'); 466 467 String attributeType = line.substring(0, colonIndex); 468 469 if (attributeType.equalsIgnoreCase("dn")) { 471 throw new LdifParseException("A ldif entry should not have two DN", line); 472 } 473 474 Object attributeValue = parseValue(line, colonIndex); 475 476 entry.addAttribute(attributeType, attributeValue); 478 } 479 480 486 private void parseModRdn(Entry entry, Iterator iter) { 487 if (iter.hasNext()) { 490 String line = (String ) iter.next(); 491 String lowerLine = line.toLowerCase(); 492 493 if (lowerLine.startsWith("newrdn::") || lowerLine.startsWith("newrdn:")) { 494 int colonIndex = line.indexOf(':'); 495 Object attributeValue = parseValue(line, colonIndex); 496 entry.setNewRdn(attributeValue instanceof String ? (String ) attributeValue : Utils 497 .utf8ToString((byte[]) attributeValue)); 498 } else { 499 throw new LdifParseException("Bad modrdn operation", line); 500 } 501 502 } else { 503 throw new LdifParseException("Bad modrdn operation, no newrdn"); 504 } 505 506 if (iter.hasNext()) { 507 String line = (String ) iter.next(); 508 String lowerLine = line.toLowerCase(); 509 510 if (lowerLine.startsWith("deleteoldrdn:")) { 511 int colonIndex = line.indexOf(':'); 512 Object attributeValue = parseValue(line, colonIndex); 513 entry.setDeleteOldRdn("1".equals(attributeValue)); 514 } else { 515 throw new LdifParseException("Bad modrdn operation, no deleteoldrdn", line); 516 } 517 } else { 518 throw new LdifParseException("Bad modrdn operation, no deleteoldrdn"); 519 } 520 521 } 522 523 537 private void parseModify(Entry entry, Iterator iter) { 538 int state = MOD_SPEC; 539 String modified = null; 540 int modification = 0; 541 542 boolean isEmptyValue = true; 544 545 while (iter.hasNext()) { 546 String line = (String ) iter.next(); 547 String lowerLine = line.toLowerCase(); 548 549 if (lowerLine.startsWith("-")) { 550 if (state != ATTRVAL_SPEC_OR_SEP) { 551 throw new LdifParseException("Bad modify separator", line); 552 } else { 553 if (isEmptyValue) { 554 entry.addModificationItem(modification, modified, null); 556 } 557 558 state = MOD_SPEC; 559 isEmptyValue = true; 560 continue; 561 } 562 } else if (lowerLine.startsWith("add:")) { 563 if ((state != MOD_SPEC) && (state != ATTRVAL_SPEC)) { 564 throw new LdifParseException("Bad modify state", line); 565 } 566 567 modified = line.substring("add:".length()).trim(); 568 modification = DirContext.ADD_ATTRIBUTE; 569 570 state = ATTRVAL_SPEC; 571 } else if (lowerLine.startsWith("delete:")) { 572 if ((state != MOD_SPEC) && (state != ATTRVAL_SPEC)) { 573 throw new LdifParseException("Bad modify state", line); 574 } 575 576 modified = line.substring("delete:".length()).trim(); 577 modification = DirContext.REMOVE_ATTRIBUTE; 578 579 state = ATTRVAL_SPEC_OR_SEP; 580 } else if (lowerLine.startsWith("replace:")) { 581 if ((state != MOD_SPEC) && (state != ATTRVAL_SPEC)) { 582 throw new LdifParseException("Bad modify state", line); 583 } 584 585 modified = line.substring("replace:".length()).trim(); 586 modification = DirContext.REPLACE_ATTRIBUTE; 587 588 state = ATTRVAL_SPEC_OR_SEP; 589 } else { 590 if ((state != ATTRVAL_SPEC) && (state != ATTRVAL_SPEC_OR_SEP)) { 591 throw new LdifParseException("Bad modify state", line); 592 } 593 594 int colonIndex = line.indexOf(':'); 596 597 String attributeType = line.substring(0, colonIndex); 598 599 if (!attributeType.equals(modified)) { 600 throw new LdifParseException("Bad modify attribute", line); 601 } 602 603 if (attributeType.equals("dn")) { 605 throw new LdifParseException("A ldif entry should not have two DN", line); 606 } 607 608 Object attributeValue = parseValue(line, colonIndex); 609 610 entry.addModificationItem(modification, attributeType, attributeValue); 612 isEmptyValue = false; 613 614 state = ATTRVAL_SPEC_OR_SEP; 615 } 616 } 617 } 618 619 645 private void parseChange(Entry entry, Iterator iter, int operation, Control control) { 646 entry.setChangeType(operation); 648 649 switch (operation) { 650 case Entry.DELETE: 651 return; 654 655 case Entry.ADD: 656 while (iter.hasNext()) { 658 String line = (String ) iter.next(); 659 parseAttributeValue(entry, line); 660 } 661 662 return; 663 664 case Entry.MODIFY: 665 parseModify(entry, iter); 666 return; 667 668 case Entry.MODRDN: case Entry.MODDN: 670 parseModRdn(entry, iter); 672 673 if (iter.hasNext()) { 675 String line = (String ) iter.next(); 676 String lowerLine = line.toLowerCase(); 677 678 if (lowerLine.startsWith("newsuperior:")) { 679 int colonIndex = line.indexOf(':'); 680 Object attributeValue = parseValue(line, colonIndex); 681 entry.setNewSuperior(attributeValue instanceof String ? (String ) attributeValue : Utils 682 .utf8ToString((byte[]) attributeValue)); 683 } else { 684 if (operation == Entry.MODDN) { 685 throw new LdifParseException("Bad moddn operation, no newsuperior", line); 686 } 687 } 688 } else { 689 if (operation == Entry.MODDN) { 690 throw new LdifParseException("Bad moddn operation, no newsuperior"); 691 } 692 } 693 694 return; 695 696 default: 697 throw new LdifParseException("Bad operation"); 699 } 700 } 701 702 712 private Entry parseEntry() { 713 if ((lines == null) || (lines.size() == 0)) { 714 return null; 715 } 716 717 String line = lines.get(0); 719 720 String dn = parseDn(line); 721 722 Entry entry = new Entry(); 724 entry.setDn(dn); 725 726 lines.remove(0); 728 729 Iterator iter = lines.iterator(); 731 732 int type = UNKNOWN; 734 735 boolean controlSeen = false; 738 739 boolean changeTypeSeen = false; 742 743 int operation = Entry.ADD; 744 String lowerLine = null; 745 Control control = null; 746 747 while (iter.hasNext()) { 748 line = (String ) iter.next(); 751 lowerLine = line.toLowerCase(); 752 753 if (lowerLine.startsWith("control:")) { 758 if (containsEntries) { 759 throw new LdifParseException("No changes withing entries", line); 760 } 761 762 containsChanges = true; 763 764 if (controlSeen) { 765 throw new LdifParseException("Control misplaced", line); 766 } 767 768 control = parseControl(line.substring("control:".length())); 770 entry.setControl(control); 771 772 } else if (lowerLine.startsWith("changetype:")) { 773 if (containsEntries) { 774 throw new LdifParseException("No changes withing entries", line); 775 } 776 777 containsChanges = true; 778 779 if (changeTypeSeen) { 780 throw new LdifParseException("ChangeType misplaced", line); 781 } 782 783 type = CHANGE; 785 controlSeen = true; 786 787 operation = parseChangeType(line); 788 789 parseChange(entry, iter, operation, control); 791 changeTypeSeen = true; 792 } else if (line.indexOf(':') > 0) { 793 if (containsChanges) { 794 throw new LdifParseException("No entries within changes", line); 795 } 796 797 containsEntries = true; 798 799 if (controlSeen || changeTypeSeen) { 800 throw new LdifParseException("AttributeType misplaced", line); 801 } 802 803 parseAttributeValue(entry, line); 804 type = ENTRY; 805 } else { 806 throw new LdifParseException("Bad attribute", line); 808 } 809 } 810 811 if (type == CHANGE) { 812 entry.setChangeType(operation); 813 } 814 815 return entry; 816 } 817 818 821 static final Pattern VERSION_PATTERN = Pattern.compile("[ ]*version\\:[ ]*(\\d+)[ ]*"); 822 823 827 828 static final Pattern VERSION_PATTERN_LINE1 = Pattern.compile("[ ]*version\\:[ ]*"); 829 static final Pattern VERSION_PATTERN_LINE2 = Pattern.compile("[ ]\\d+"); 830 831 836 private int parseVersion() { 837 838 readLines(); 840 841 if (lines.size() == 0) { 842 return DEFAULT_VERSION; 843 } 844 845 String line = lines.get(0); 847 848 849 Matcher versionMatcher = VERSION_PATTERN.matcher(line); 850 String versionStr = null; 851 852 if (versionMatcher.matches()) { 853 versionStr=versionMatcher.group(1); 854 lines.remove(0); 856 } else { 857 versionMatcher = VERSION_PATTERN_LINE1.matcher(line); 858 if (versionMatcher.matches()) { 859 lines.remove(0); 860 if (!lines.isEmpty()) { 861 versionMatcher = VERSION_PATTERN_LINE2.matcher(lines.get(1)); 862 if (versionMatcher.matches()) { 863 versionStr=versionMatcher.group(1); 864 } 865 lines.remove(0); 866 } 867 868 } 869 870 } 871 872 if (versionStr!=null) { 873 try { 874 return Integer.parseInt(versionStr.trim()); 875 } catch (NumberFormatException e) { 876 throw new LdifParseException("Invalid LDIF version number "+versionStr, line); 877 } 878 } else { 879 return DEFAULT_VERSION; 880 } 881 } 882 883 890 private void readLines() { 891 String line; 892 boolean insideComment = true; 893 boolean isFirstLine = true; 894 895 lines.clear(); 896 StringBuilder sb = new StringBuilder (128); 897 898 try { 899 while ((line = ((BufferedReader ) in).readLine()) != null) { if (StringUtils.isAsciiWhitespacesOnly(line)) { if (isFirstLine) { 902 continue; 903 } else { 904 insideComment = false; 906 if (lines.isEmpty()) { continue; 908 } else { break; 910 } 911 } 912 } 913 914 isFirstLine = false; 915 916 switch (line.charAt(0)) { 918 case '#': 919 insideComment = true; 920 break; 921 922 case ' ': 923 if (insideComment) { 924 continue; 925 } else if (sb.length() == 0) { 926 throw new LdifParseException("Ldif Parsing error: Cannot have an empty continuation line"); 927 } else { 928 sb.append(line.substring(1)); 929 } 930 931 insideComment = false; 932 break; 933 934 default: 935 if (sb.length() != 0) { 938 lines.add(sb.toString()); 939 } 940 941 sb = new StringBuilder (line); 942 insideComment = false; 943 break; 944 } 945 } 946 } 947 catch (IOException ioe) { 948 throw new LdifParseException("Error while reading ldif lines"); 949 } 950 951 if (sb.length() != 0) { 953 lines.add(sb.toString()); 954 } 955 956 } 957 958 959 963 968 public Entry next() { 969 if (!hasNext()) { 970 throw new NoSuchElementException ("No LDIF entries to read. Use hasNext()."); 971 } 972 Entry res = prefetched; 973 prefetched=null; 974 return res; 975 } 976 977 982 public boolean hasNext() { 983 if (prefetched==null) { 984 readLines(); 985 prefetched = parseEntry(); 986 } 987 return null != prefetched; 988 } 989 990 995 public void remove() { 996 throw new UnsupportedOperationException (); 997 } 998 999 1002 public Iterator <Entry> iterator() { 1003 return this; 1004 } 1005 1006 1007 List parseLdif(String s) { 1008 return parseLdif(new BufferedReader (new StringReader (s))); 1009 } 1010 1011 1019 public List <Entry> parseLdif(BufferedReader in) { 1020 init(in); 1021 List <Entry> entries = new ArrayList <Entry>(); 1023 1024 while (hasNext()) { 1026 Entry entry = next(); 1027 entries.add(entry); 1028 } 1029 1030 return entries; 1031 } 1032 1033 1037 public boolean containsEntries() { 1038 return containsEntries; 1039 } 1040 1041 1042 1043 1044 1045} | Popular Tags |