1 package com.ca.jndiproviders.dsml; 2 3 import com.ca.commons.naming.DXNamingEnumeration; 4 import com.ca.commons.naming.DN; 5 import com.ca.commons.cbutil.CBBase64; 6 import com.ca.commons.cbutil.CBBase64EncodingException; 7 8 import javax.naming.directory.*; 9 import javax.naming.*; 10 import java.util.*; 11 import java.util.regex.Matcher ; 12 import java.util.regex.Pattern ; 13 import java.util.logging.Logger ; 14 import java.util.logging.Level ; 15 import java.io.UnsupportedEncodingException ; 16 import java.io.IOException ; 17 18 34 35 42 43 44 46 48 50 public class DsmlContext implements DirContext 51 { 52 53 private static int TABLEN = 4; 55 private static String TAB = " "; 56 private static String TAB2 = TAB + TAB; 57 private static String TAB3 = TAB2 + TAB; 58 private static String TAB4 = TAB3 + TAB; 59 private static String TAB5 = TAB4 + TAB; 60 private static String TAB6 = TAB5 + TAB; 61 62 private static String SOAPHEADER = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + 63 TAB + "<soap-env:Envelope xmlns:soap-env=\"http://schemas.xmlsoap.org/soap/envelope/\">\n" + 64 TAB2 + "<soap-env:Body>\n"; 65 66 private static String SOAPFOOTER = TAB2 + "</soap-env:Body>\n" + 67 TAB + "</soap-env:Envelope>"; 68 69 private static String DSMLHEADER = TAB3 + "<dsml:batchRequest xmlns:dsml=\"urn:oasis:names:tc:DSML:2:0:core\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n"; 70 71 private static String DSMLFOOTER = TAB3 + "</dsml:batchRequest>\n"; 72 73 private static String SEARCHFOOTER = TAB3 + "</dsml:searchRequest>\n"; 74 75 private static String STANDARDHEADER = SOAPHEADER + DSMLHEADER; 76 77 private static String STANDARDFOOTER = DSMLFOOTER + SOAPFOOTER; 78 79 private static String BASEOBJECT = "baseObject"; 81 private static String SINGLELEVEL = "singleLevel"; 82 private static String WHOLESUBTREE = "wholeSubtree"; 83 private static String [] SCOPEOPTIONS = new String []{BASEOBJECT, SINGLELEVEL, WHOLESUBTREE}; 84 85 private static String NEVER = "neverDerefAliases"; 87 private static String SEARCHING = "derefInSearching"; 88 private static String FINDING = "derefFindingBaseObj"; 89 private static String ALWAYS = "derefAlways"; 90 private static String [] ALIASOPTIONS = new String []{NEVER, SEARCHING, FINDING, ALWAYS}; 91 92 private static SearchControls DEFAULT_SEARCH_CONTROLS = new SearchControls(); 94 95 96 protected Hashtable environment; 97 98 private String contextName = ""; 100 101 private static Logger log = Logger.getLogger(DsmlContext.class.getName()); 102 103 { 105 log.setLevel(Level.FINEST); 106 } 107 108 129 private static StringBuffer getSearchRequestHeader(StringBuffer searchHeader, String dn, String scope, String derefAliases, long sizeLimit, int timeLimit, boolean typesOnly) 130 throws NamingException 131 { 132 if (!checkValidity(SCOPEOPTIONS, scope)) 134 throw new NamingException("search scope argument '" + scope + "' is invalid"); 135 136 if (!checkValidity(ALIASOPTIONS, derefAliases)) 137 { 138 if (derefAliases != null) log.info("bad alias option passed '" + derefAliases + "'"); 139 derefAliases = SEARCHING; 140 } 141 142 if (searchHeader == null) 143 searchHeader = new StringBuffer (); 144 145 searchHeader.append(TAB4 + "<dsml:searchRequest dn=\""); searchHeader.append(escapeName(dn)); 147 searchHeader.append("\" scope=\"").append(scope); 148 searchHeader.append("\" derefAliases=\"").append(derefAliases); 149 150 if (sizeLimit > 0) 151 searchHeader.append("\" sizeLimit=\"").append(sizeLimit); 152 153 if (timeLimit > 0) 154 searchHeader.append("\" timeLimit=\"").append(timeLimit); 155 156 if (typesOnly == true) 158 searchHeader.append("\" typesOnly=\"").append(typesOnly); 159 160 searchHeader.append("\">\n"); 161 162 log.finest("created search header: " + searchHeader); 163 164 return searchHeader; 165 } 166 167 168 186 private static StringBuffer getSearchRequestAttributes(StringBuffer searchAttributes, String [] attributes) 187 { 188 if (attributes == null || attributes.length == 0) 189 { 190 return searchAttributes; } 192 193 if (searchAttributes == null) 194 searchAttributes = new StringBuffer (40 + 80 * attributes.length); 195 196 searchAttributes.append(TAB5 + "<dsml:attributes>\n"); 197 198 int len = attributes.length; 199 for (int i = 0; i < len; i++) 200 { 201 searchAttributes.append(TAB6 + "<dsml:attribute name=\"").append(attributes[i]).append("\"/>\n"); 202 } 203 204 searchAttributes.append(TAB5 + "</dsml:attributes>\n"); 205 206 log.finest("created search attribute list: " + searchAttributes); 207 208 return searchAttributes; 209 } 210 211 212 219 private static boolean checkValidity(String [] options, String checkme) 220 { 221 if (checkme != null) 222 { 223 for (int i = 0; i < options.length; i++) 224 if (options[i].equals(checkme)) 225 return true; 226 } 227 return false; 228 } 229 230 237 private NamingEnumeration getTestEnumeration(Name name, int num) 238 { 239 log.finest("generating " + num + " test names from '" + name.toString() + "'"); 240 241 String itemBase = "c=AU"; 242 if (name.size() == 1) 243 itemBase = "o=beta"; 244 else if (name.size() == 2) 245 itemBase = "ou=gamma"; 246 else if (name.size() == 3) 247 itemBase = "ou=delta"; 248 249 DXNamingEnumeration testEnumeration = new DXNamingEnumeration(); 250 for (int i = 0; i < num; i++) 251 { 252 String itemName = itemBase + i; 253 testEnumeration.add(new SearchResult(itemName, null, getTestAttributes(itemName))); 254 } 255 256 return testEnumeration; 257 } 258 259 265 private BasicAttributes getTestAttributes(String name) 266 { 267 log.finest("generating test data from name '" + name + "'"); 268 BasicAttributes testAttributes = new BasicAttributes("cn", name); 269 testAttributes.put(new BasicAttribute("objectClass", "person")); 270 testAttributes.put(new BasicAttribute("sn", "Test")); 271 return testAttributes; 272 } 273 274 private DsmlContext() 275 { 276 } 278 279 private DsmlContext(String baseDN, Hashtable env) 280 { 281 contextName = baseDN==null?"":baseDN; 282 283 environment = env; } 285 286 291 DsmlContext(Hashtable env) 292 { 293 environment = (env == null) ? new Hashtable() : env; 294 log.fine("Created DsmlContext"); 295 } 296 297 305 public Attributes getAttributes(String name) throws NamingException 306 { 307 return getAttributes(name, null); 308 } 309 310 324 public void modifyAttributes(String name, int mod_op, Attributes attrs) throws NamingException 325 { 326 ModificationItem[] mods = new ModificationItem[attrs.size()]; 327 Enumeration attObjects = attrs.getAll(); 328 int i = 0; 329 while (attObjects.hasMoreElements()) 330 { 331 mods[i++] = new ModificationItem(mod_op, (Attribute) attObjects.nextElement()); 332 } 333 334 modifyAttributes(name, mods); 335 336 } 337 338 351 public Attributes getAttributes(Name name) throws NamingException 352 { 353 return getAttributes(name, null); 354 } 355 356 372 public void modifyAttributes(Name name, int mod_op, Attributes attrs) throws NamingException 373 { 374 modifyAttributes(name.toString(), mod_op, attrs); 375 } 376 377 387 public DirContext getSchema(String name) throws NamingException 388 { 389 throw new OperationNotSupportedException("DsmlContext does not support reading schema (yet)"); 390 } 391 392 405 public DirContext getSchemaClassDefinition(String name) throws NamingException 406 { 407 throw new OperationNotSupportedException("DsmlContext does not support reading schema (yet)"); 408 } 409 410 433 public DirContext getSchema(Name name) throws NamingException 434 { 435 throw new OperationNotSupportedException("DsmlContext does not support reading schema (yet)"); 436 } 437 438 470 public DirContext getSchemaClassDefinition(Name name) throws NamingException 471 { 472 throw new OperationNotSupportedException("DsmlContext does not support reading schema (yet)"); 473 } 474 475 487 public void modifyAttributes(String name, ModificationItem[] mods) throws NamingException 488 { 489 log.finest("modify Atts of (" + name + ")"); 490 491 493 StringBuffer modRequestBuffer = constructModRequest(name, mods); 494 495 497 String response = sendDSMLRequest(modRequestBuffer); 498 499 501 parseModResponse(response); 502 503 } 504 505 private StringBuffer constructModRequest(String name, ModificationItem[] mods) 506 throws NamingException 507 { 508 StringBuffer message = new StringBuffer (200); 509 message.append(STANDARDHEADER); 510 511 getModRequestElement(message, name, mods); 512 513 message.append(STANDARDFOOTER); 514 515 return message; 516 } 517 518 523 536 static void getModRequestElement(StringBuffer message, String name, ModificationItem[] mods) 537 throws NamingException 538 { 539 message.append(TAB4).append("<dsml:modifyRequest dn=\"").append(escapeName(name)).append("\">\n"); 540 541 for (int i = 0; i < mods.length; i++) 542 { 543 Attribute att = mods[i].getAttribute(); 544 NamingEnumeration values = att.getAll(); 545 switch (mods[i].getModificationOp()) 546 { 547 case ADD_ATTRIBUTE: 548 549 message.append(TAB5).append("<dsml:modification name=\"").append(att.getID()).append("\" operation=\"add\">\n"); 550 551 while (values.hasMore()) 552 createDsmlValueElement(values.next(), message); 553 554 message.append(TAB5).append("</dsml:modification>\n"); 555 556 break; 557 558 case REPLACE_ATTRIBUTE: 559 560 message.append(TAB5).append("<dsml:modification name=\"").append(att.getID()).append("\" operation=\"replace\">\n"); 561 562 while (values.hasMore()) 563 createDsmlValueElement(values.next(), message); 564 565 message.append(TAB5).append("</dsml:modification>\n"); 566 567 break; 568 569 case REMOVE_ATTRIBUTE: 570 571 message.append(TAB5).append("<dsml:modification name=\"").append(att.getID()).append("\" operation=\"delete\">\n"); 572 573 while (values.hasMore()) 574 createDsmlValueElement(values.next(), message); 575 576 message.append(TAB5).append("</dsml:modification>\n"); 577 578 break; 579 } 580 581 } 582 message.append(TAB4).append("</dsml:modifyRequest>\n"); 583 584 } 585 586 void parseModResponse(String response) 587 throws NamingException 588 { 589 checkForError(response); 591 592 if (response.indexOf("modifyResponse>") == -1) 594 throw new NamingException("Unexpected DSML Response to Modify Request:\n " + response); 595 } 596 597 599 private static Pattern errorResult = Pattern.compile("errorResponse.*?type=\"(.*?)\"", Pattern.DOTALL); 601 private static Pattern errorMessage = Pattern.compile("message>(.*?)</", Pattern.DOTALL); 602 private static Pattern errorDetail = Pattern.compile("detail>(.*?)</", Pattern.DOTALL); 603 604 private static Pattern ldapResultCode = Pattern.compile("code=\"(.*?)\"", Pattern.DOTALL); 605 606 private static Pattern ldapResultDesc = Pattern.compile("resultCode.*?descr=\"(.*?)\"", Pattern.DOTALL); 607 608 private static Pattern ldapResultMsg = Pattern.compile("errorMessage>(.*?)</", Pattern.DOTALL); 609 610 static void checkForError(String response) 611 throws NamingException 612 { 613 Matcher error = errorResult.matcher(response); 614 if (error.find()) 615 { 616 String errorMsg = "Error Processing DSML Request: " + error.group(1); 617 618 Matcher message = errorMessage.matcher(response); 619 if (message.find()) 620 errorMsg = errorMsg + "\n" + message.group(1); 621 622 Matcher detail = errorDetail.matcher(response); 623 if (detail.find()) 624 errorMsg = errorMsg + "\n" + detail.group(1); 625 626 throw new NamingException(errorMsg); 627 } 628 else 629 { 630 try 631 { 632 Matcher resultMatcher = ldapResultCode.matcher(response); 633 resultMatcher.find(); 634 String resultCode = resultMatcher.group(1); 635 636 int i = Integer.parseInt(resultCode); 637 638 if (i == 0) 639 return; 641 Matcher descMatcher = ldapResultDesc.matcher(response); 642 String desc = ""; 643 if (descMatcher.find()) 644 desc = descMatcher.group(1); 645 646 Matcher msgMatcher = ldapResultMsg.matcher(response); 647 String msg = ""; 648 if (msgMatcher.find()) 649 msg = msgMatcher.group(1); 650 651 throw new NamingException(desc + " Exception (LDAP " + resultCode + ")\n" + msg); 652 653 } 654 catch (NumberFormatException e) 655 { 656 throw new NamingException("Unable to parse result code in DSML Response\n" + response); 657 } 658 catch (IllegalStateException e) 659 { 660 throw new NamingException("Unable to find result code in DSML Response\n" + response); 661 } 662 } 663 } 664 665 683 public void modifyAttributes(Name name, ModificationItem[] mods) throws NamingException 684 { 685 modifyAttributes(name.toString(), mods); 686 } 687 688 698 public NamingEnumeration search(String name, Attributes matchingAttributes) throws NamingException 699 { 700 return search(name, matchingAttributes, null); 701 } 702 703 719 public NamingEnumeration search(Name name, Attributes matchingAttributes) throws NamingException 720 { 721 return search(name, matchingAttributes, null); 722 } 723 724 737 public void bind(String name, Object obj, Attributes attrs) throws NamingException 738 { 739 throw new OperationNotSupportedException("binding of java objects is not supported by the DsmlContext"); 740 } 741 742 755 public void rebind(String name, Object obj, Attributes attrs) throws NamingException 756 { 757 throw new OperationNotSupportedException("binding of java objects is not supported by the DsmlContext"); 758 } 759 760 780 781 public void bind(Name name, Object obj, Attributes attrs) throws NamingException 782 { 783 throw new OperationNotSupportedException("binding of java objects is not supported by the DsmlContext"); 784 } 785 786 810 public void rebind(Name name, Object obj, Attributes attrs) throws NamingException 811 { 812 throw new OperationNotSupportedException("binding of java objects is not supported by the DsmlContext"); 813 } 814 815 826 public Attributes getAttributes(String name, String [] attrIds) throws NamingException 827 { 828 log.finest("getAttributes (" + name.toString() + ")"); 829 830 NamingEnumeration en = doDsmlSearch(name.toString(), BASEOBJECT, SEARCHING, 0, 0, false, "(objectClass=*)", attrIds); 831 832 if (en.hasMoreElements() == false) 833 return new BasicAttributes(); 835 SearchResult result = (SearchResult) en.next(); 836 837 return result.getAttributes(); 838 840 } 841 842 865 public Attributes getAttributes(Name name, String [] attrIds) throws NamingException 866 { 867 return getAttributes(name.toString(), attrIds); 868 } 869 870 882 public DirContext createSubcontext(String name, Attributes attrs) throws NamingException 883 { 884 log.finest("createSubcontext (" + name.toString() + ")"); 885 887 StringBuffer addRequestBuffer = constructAddRequest(name, attrs); 888 889 891 String response = sendDSMLRequest(addRequestBuffer); 892 893 895 parseAddResponse(response); 896 897 899 return new DsmlContext(name, environment); 900 } 901 902 void parseAddResponse(String response) 903 throws NamingException 904 { 905 checkForError(response); 906 if (response.indexOf("addResponse>") == -1) 908 throw new NamingException("Unexpected DSML Response to Add Request:\n " + response); 909 910 } 911 912 931 public DirContext createSubcontext(Name name, Attributes attrs) throws NamingException 932 { 933 return createSubcontext(name.toString(), attrs); 934 } 935 936 947 public NamingEnumeration search(String name, Attributes matchingAttributes, String [] attributesToReturn) throws NamingException 948 { 949 951 String filter; 952 filter = getAttributeMatchFilter(matchingAttributes); 953 954 956 return doDsmlSearch(name, WHOLESUBTREE, SEARCHING, 0, 0, false, filter, attributesToReturn); 957 } 958 959 1023 public NamingEnumeration search(Name name, Attributes matchingAttributes, String [] attributesToReturn) throws NamingException 1024 { 1025 return search(name.toString(), matchingAttributes, attributesToReturn); 1026 } 1027 1028 1034 static String getAttributeMatchFilter(Attributes matchingAttributes) 1035 { 1036 String filter; 1037 int numbAtts = (matchingAttributes == null) ? 0 : matchingAttributes.size(); 1038 if (numbAtts == 0) 1039 { 1040 filter = "(objectClass=*)"; 1041 } 1042 else 1043 { 1044 StringBuffer filterBuffer = new StringBuffer (); 1045 if (numbAtts > 1) 1046 filterBuffer.append("(&"); 1047 1048 Enumeration atts = matchingAttributes.getIDs(); while (atts.hasMoreElements()) 1050 { 1051 String att = (String ) atts.nextElement(); 1052 filterBuffer.append("(").append(att).append("=*)"); 1053 } 1054 1055 if (numbAtts > 1) 1056 filterBuffer.append(")"); 1057 filter = filterBuffer.toString(); 1058 } 1059 return filter; 1060 } 1061 1062 1063 1083 public NamingEnumeration search(String name, String filter, SearchControls cons) throws NamingException 1084 { 1085 return search(name, filter, null, cons); } 1087 1088 1181 public NamingEnumeration search(Name name, String filter, SearchControls cons) throws NamingException 1182 { 1183 return search(name, filter, null, cons); 1184 } 1185 1186 1218 public NamingEnumeration search(String name, String filterExpr, Object [] filterArgs, SearchControls cons) throws NamingException 1219 { 1220 1221 if (filterExpr == null) 1222 throw new NamingException("null ldap filter in DsmlContext search"); 1223 1224 if (filterArgs != null && filterArgs.length > 0) 1225 throw new NamingException("filter arguments not implemented in com.ca.jndiproviders.dsml provider"); 1226 1227 log.finest("search III (" + name.toString() + ") + filter: " + filterExpr); 1228 1229 if (cons == null) 1230 cons = DEFAULT_SEARCH_CONTROLS; 1231 1232 String searchScope; 1234 switch (cons.getSearchScope()) 1235 { 1236 case SearchControls.OBJECT_SCOPE: 1237 searchScope = BASEOBJECT; 1238 break; 1239 case SearchControls.ONELEVEL_SCOPE: 1240 searchScope = SINGLELEVEL; 1241 break; 1242 case SearchControls.SUBTREE_SCOPE: 1243 searchScope = WHOLESUBTREE; 1244 break; 1245 default: 1246 throw new NamingException("unexpected error; unknown search scope in SearchControl object: " + cons.getSearchScope()); } 1248 1249 1250 String jndiAliasHandling = (String ) environment.get("java.naming.ldap.derefAliases"); 1252 1253 if (jndiAliasHandling == null) 1254 jndiAliasHandling = "always"; 1255 1256 String aliasHandling = "derefAlways"; 1257 1258 if (jndiAliasHandling.equals("always")) 1259 aliasHandling = ALWAYS; 1260 else if (jndiAliasHandling.equals("never")) 1261 aliasHandling = NEVER; 1262 else if (jndiAliasHandling.equals("finding")) 1263 aliasHandling = FINDING; 1264 else if (jndiAliasHandling.equals("searching")) 1265 aliasHandling = SEARCHING; 1266 1267 1269 NamingEnumeration returnEnum = doDsmlSearch(name, searchScope, aliasHandling, cons.getCountLimit(), cons.getTimeLimit(), false, filterExpr, cons.getReturningAttributes()); 1271 1272 return returnEnum; 1273 } 1274 1275 1369 public NamingEnumeration search(Name name, String filterExpr, Object [] filterArgs, SearchControls cons) throws NamingException 1370 { 1371 return search(name.toString(), filterExpr, filterArgs, cons); 1372 1373 } 1374 1375 1386 public void close() throws NamingException 1387 { 1388 log.finest("close()"); 1389 } 1392 1393 1411 public String getNameInNamespace() throws NamingException 1412 { 1413 log.finest("getNameInNamespace()"); 1414 return contextName; 1415 } 1416 1417 1428 1429 1430 public void destroySubcontext(String name) throws NamingException 1431 { 1432 log.finest("destroySubcontext (" + name.toString() + ")"); 1433 1435 StringBuffer deleteRequestBuffer = constructDeleteRequest(name); 1436 1437 1439 String response = sendDSMLRequest(deleteRequestBuffer); 1440 1441 1443 parseDeleteResponse(response); 1444 } 1445 1446 void parseDeleteResponse(String response) 1447 throws NamingException 1448 { 1449 checkForError(response); 1450 if (response.indexOf("delResponse>") == -1) 1452 throw new NamingException("Unexpected DSML Response to Delete Request:\n " + response); 1453 1454 } 1455 1456 1465 public void unbind(String name) throws NamingException 1466 { 1467 throw new OperationNotSupportedException("DsmlContext does not support storing java objects"); 1468 1469 } 1471 1472 1486 public Hashtable getEnvironment() throws NamingException 1487 { 1488 return (Hashtable) environment.clone(); } 1490 1491 1522 public void destroySubcontext(Name name) throws NamingException 1523 { 1524 destroySubcontext(name.toString()); 1525 } 1527 1528 1549 public void unbind(Name name) throws NamingException 1550 { 1551 throw new OperationNotSupportedException("DsmlContext does not support storing java objects"); 1552 } 1553 1554 1563 public Object lookup(String name) throws NamingException 1564 { 1565 throw new OperationNotSupportedException("binding of java objects is not supported by the DsmlContext"); 1566 } 1567 1568 1579 public Object lookupLink(String name) throws NamingException 1580 { 1581 throw new OperationNotSupportedException("binding of java objects is not supported by the DsmlContext"); 1582 } 1583 1584 1596 public Object removeFromEnvironment(String propName) throws NamingException 1597 { 1598 return environment.remove(propName); 1599 } 1600 1601 1612 public void bind(String name, Object obj) throws NamingException 1613 { 1614 throw new OperationNotSupportedException("binding of java objects is not supported by the DsmlContext"); 1615 } 1616 1617 1627 public void rebind(String name, Object obj) throws NamingException 1628 { 1629 throw new OperationNotSupportedException("binding of java objects is not supported by the DsmlContext"); 1630 } 1631 1632 1646 public Object lookup(Name name) throws NamingException 1647 { 1648 throw new OperationNotSupportedException("binding of java objects is not supported by the DsmlContext"); 1649 } 1650 1651 1664 public Object lookupLink(Name name) throws NamingException 1665 { 1666 throw new OperationNotSupportedException("binding of java objects is not supported by the DsmlContext"); 1667 } 1668 1669 1685 public void bind(Name name, Object obj) throws NamingException 1686 { 1687 throw new OperationNotSupportedException("binding of java objects is not supported by the DsmlContext"); 1688 } 1689 1690 1711 public void rebind(Name name, Object obj) throws NamingException 1712 { 1713 throw new OperationNotSupportedException("binding of java objects is not supported by the DsmlContext"); 1714 } 1715 1716 1726 public void rename(String oldName, String newName) throws NamingException 1727 { 1728 rename(new DN(oldName), new DN(newName)); 1732 } 1733 1734 StringBuffer constructModDNRequest(String oldName, String newName) 1735 throws NamingException 1736 { 1737 StringBuffer message = new StringBuffer (200); 1738 message.append(STANDARDHEADER); 1739 1740 getModDNRequestElement(message, oldName, newName); 1741 1742 message.append(STANDARDFOOTER); 1743 1744 return message; 1745 } 1746 1747 1755 1762 void getModDNRequestElement(StringBuffer message, String oldName, String newRDN) 1763 { 1764 message.append(TAB4).append("<dsml:modDNRequest dn=\"").append(escapeName(oldName)).append("\" newrdn=\"").append(escapeName(newRDN)).append("\" "); 1765 1766 if (environment.get("java.naming.ldap.deleteRDN").toString().equalsIgnoreCase("false")) 1767 message.append("deleteoldrdn=\"false\"/>"); 1768 else 1769 message.append("deleteoldrdn=\"true\"/>"); 1770 1771 } 1772 1773 void parseModDNResponse(String response) 1774 throws NamingException 1775 { 1776 checkForError(response); 1778 1779 if (response.indexOf("modDNResponse>") == -1) 1781 throw new NamingException("Unexpected DSML Response to Modify DN Request:\n " + response); 1782 } 1783 1784 1796 public Context createSubcontext(String name) throws NamingException 1797 { 1798 throw new InvalidAttributesException("cannot create directory object without attributes"); 1799 } 1800 1801 1818 public Context createSubcontext(Name name) throws NamingException 1819 { 1820 throw new InvalidAttributesException("cannot create directory object without attributes"); 1821 } 1822 1823 1838 public void rename(Name oldName, Name rdn) throws NamingException 1839 { 1840 log.finest("rename (" + oldName + " to " + rdn); 1841 1842 String oldNameString = oldName.toString(); 1843 1844 if (rdn.size() != 1) 1845 throw new NamingException("cannot perform rename operation '" + rdn + "' is not an RDN \n" ); 1846 1847 String newRDN = rdn.toString(); 1848 1849 1850 1873 1874 1876 StringBuffer modDNRequestBuffer = constructModDNRequest(oldNameString, newRDN); 1877 1878 1880 String response = sendDSMLRequest(modDNRequestBuffer); 1881 1882 1884 parseModDNResponse(response); 1885 1886 1887 } 1888 1889 1900 public NameParser getNameParser(String name) throws NamingException 1901 { 1902 return DsmlParser.parser; 1903 } 1904 1905 1917 public NameParser getNameParser(Name name) throws NamingException 1918 { 1919 return DsmlParser.parser; 1920 } 1921 1922 1932 public NamingEnumeration list(String name) throws NamingException 1933 { 1934 log.finest("list (" + name.toString() + ")"); 1935 1936 return doDsmlSearch(name, SINGLELEVEL, SEARCHING, 0, 0, false, "(objectClass=*)", null); 1937 } 1938 1939 1951 public NamingEnumeration listBindings(String name) throws NamingException 1952 { 1953 throw new OperationNotSupportedException("binding of java objects is not supported by the DsmlContext"); 1954 } 1955 1956 1973 public NamingEnumeration list(Name name) throws NamingException 1974 { 1975 return list(name.toString()); 1976 1977 } 1978 1979 1997 public NamingEnumeration listBindings(Name name) throws NamingException 1998 { 1999 throw new OperationNotSupportedException("binding of java objects is not supported by the DsmlContext"); 2000 } 2001 2002 2015 public Object addToEnvironment(String propName, Object propVal) throws NamingException 2016 { 2017 log.finest("addToEnvironment (" + propName + ": " + propVal.toString()); 2018 2019 if (propName == null) 2020 return null; 2021 2022 if (propVal == null) 2023 return environment.remove(propName); 2024 else 2025 return environment.put(propName, propVal); 2026 } 2027 2028 2038 public String composeName(String name, String prefix) throws NamingException 2039 { 2040 throw new OperationNotSupportedException("DsmlContext does not support composing names - use DN methods directly"); 2041 } 2042 2043 2054 public Name composeName(Name name, Name prefix) throws NamingException 2055 { 2056 throw new OperationNotSupportedException("DsmlContext does not support composing names - use DN methods directly"); 2057 } 2058 2059 2060 2063 2064 2079 public NamingEnumeration doDsmlSearch(String name, String scope, String derefAliases, long sizeLimit, int timeLimit, boolean typesOnly, String filter, String [] attributesToReturn) 2080 throws NamingException 2081 { 2082 2084 StringBuffer searchRequestBuffer = constructSearchRequest(name, scope, derefAliases, sizeLimit, timeLimit, typesOnly, filter, attributesToReturn); 2085 2086 2088 String response = sendDSMLRequest(searchRequestBuffer); 2089 2090 2092 return parseSearchResponse(response, name); 2093 2094 } 2095 2096 2099 2103 2108 private static Pattern searchResultEntry = Pattern.compile("searchResultEntry.*?dn=\"(.*?)\">(.*?)searchResultEntry>", Pattern.DOTALL); 2109 2110 private static Pattern searchResultAttribute = Pattern.compile("attr name=\"(.*?)\">(.*?)attr>", Pattern.DOTALL); 2111 2112 private static Pattern searchResultAttributeValues = Pattern.compile("value(.*?)>(.*?)<.*?value>", Pattern.DOTALL); 2113 2114 private static Pattern searchTestType = Pattern.compile("type=\"(.*?)\"", Pattern.DOTALL); 2115 2116 2129 static NamingEnumeration parseSearchResponse(String response, String searchBase) 2130 throws NamingException 2131 { 2132 checkForError(response); 2133 2134 DXNamingEnumeration en = new DXNamingEnumeration(); 2135 2136 Matcher responseMatcher = searchResultEntry.matcher(response); 2137 2138 while (responseMatcher.find()) 2140 { 2141 String dn = responseMatcher.group(1); 2142 2143 BasicAttributes atts = new BasicAttributes(); 2144 2145 if (dn.indexOf("requestID") > -1) { 2147 int reqPos = dn.indexOf("requestID"); 2148 int endPos = dn.lastIndexOf('"', reqPos); 2149 dn = dn.substring(0, endPos); 2150 } 2151 2152 if (dn.indexOf("&") > -1) 2153 dn = unescape(dn); 2154 2155 log.finest("Parsing DSML: read DN: " + dn); 2156 2157 2161 dn = new CompositeName(dn).toString(); 2162 2163 log.finest("converted DN to: " + dn); 2164 2165 String attribute = responseMatcher.group(2); 2166 2167 Matcher attributeMatcher = searchResultAttribute.matcher(attribute); 2168 2169 while (attributeMatcher.find()) 2171 { 2172 String attributeName = attributeMatcher.group(1); 2173 2174 BasicAttribute att = new BasicAttribute(attributeName); 2175 2176 2177 String attributeValues = attributeMatcher.group(2); 2178 2179 log.finest("Parsing DSML: Attribute Name " + attributeName + " length: " + attributeValues.length()); 2180 2181 Matcher valueMatcher = searchResultAttributeValues.matcher(attributeValues); 2182 2183 while (valueMatcher.find()) 2184 { 2185 String typeInfo = valueMatcher.group(1); 2186 2187 String type = "string"; 2189 if (typeInfo != null && typeInfo.length() > 6) 2190 { 2191 int typeInfoStart = typeInfo.indexOf("type=\""); 2193 int typeInfoEnd = typeInfo.indexOf('\"', typeInfoStart + 6); 2194 if (typeInfoStart != -1 && typeInfoEnd > typeInfoStart) 2195 { 2196 type = typeInfo.substring(typeInfoStart + 6, typeInfoEnd); if ((typeInfoStart = type.indexOf(':')) > -1) type = type.substring(typeInfoStart + 1); 2199 } 2200 } 2201 String value = valueMatcher.group(2); 2202 2203 if (type.equals("string")) { 2205 if (value != null && value.indexOf('&') > -1) 2206 value = unescape(value); 2207 att.add(value); 2208 } 2209 else if (type.equals("anyURI")) 2210 throw new NamingException("CA JNDI DSML Provider does not support 'anyURI' values"); 2211 else if (type.equals("base64Binary")) 2212 { 2213 try 2214 { 2215 2216 System.out.println("PROCESSING BINARY VALUE " + attributeName); 2217 byte[] data = CBBase64.decode(value); 2218 System.out.println("RAW DATA: " + value.length() + " byte data " + data.length); 2219 att.add(CBBase64.decode(value)); 2220 } 2221 catch (CBBase64EncodingException e) 2222 { 2223 NamingException ne = new NamingException("unable to parse base64 value in entry: " + dn); 2224 ne.setRootCause(e); 2225 throw ne; 2226 } 2227 } 2228 } 2229 atts.put(att); 2230 } 2231 2232 SearchResult currentResult = new SearchResult(dn, null, atts); 2233 2234 if(dn.endsWith(searchBase)) 2239 currentResult.setRelative(false); 2240 2241 en.add(currentResult); 2242 } 2243 2244 log.finest("Parsing DSML: final enumeration - \n" + en.toString()); 2245 2246 return en; 2247 } 2248 2249 2257 private String sendDSMLRequest(StringBuffer searchRequestBuffer) 2258 throws NamingException 2259 { 2260 String response; 2261 2262 try 2263 { 2264 String searchRequest = new String (searchRequestBuffer); 2266 2267 byte[] rawBytes = searchRequest.getBytes("UTF-8"); 2268 2269 String URL = environment.get(PROVIDER_URL).toString(); 2270 2271 log.finest("----SENDING XML OVER WIRE-----\nURL: " + URL + "\nlength: " + rawBytes.length); 2272 2273 response = SoapClient.sendSoapMsg(URL, rawBytes, "#batchRequest"); 2274 2275 } 2276 catch (UnsupportedEncodingException e) { 2278 NamingException ne = new NamingException("unexpected exception encoding UTF-8 string for DSML message"); 2279 ne.setRootCause(e); 2280 throw ne; 2281 } 2282 catch (IOException e) 2283 { 2284 NamingException ne = new NamingException("error contacting DSML Server"); 2285 ne.setRootCause(e); 2286 throw ne; 2287 } 2288 return response; 2289 } 2290 2291 2296 static StringBuffer constructAddRequest(String name, Attributes atts) 2297 throws NamingException 2298 { 2299 StringBuffer message = new StringBuffer (200); 2300 message.append(STANDARDHEADER); 2301 2302 getAddRequestElement(message, name, atts); 2303 2304 message.append(STANDARDFOOTER); 2305 2306 return message; 2307 } 2308 2309 2330 2331 2348 static void getAddRequestElement(StringBuffer message, String name, Attributes atts) 2349 throws NamingException { 2351 message.append(TAB4).append("<dsml:addRequest dn=\"").append(escapeName(name)).append("\">\n"); 2352 2353 NamingEnumeration attEnum = atts.getAll(); 2354 while (attEnum.hasMore()) 2355 { 2356 Attribute att = (Attribute) attEnum.next(); 2357 String attName = att.getID(); 2358 message.append(TAB5).append("<dsml:attr name=\"").append(attName).append("\">\n"); 2359 NamingEnumeration values = att.getAll(); 2360 while (values.hasMore()) 2361 { 2362 Object value = values.next(); 2363 createDsmlValueElement(value, message); 2364 } 2365 message.append(TAB5).append("</dsml:attr>\n"); 2366 } 2367 message.append(TAB4).append("</dsml:addRequest>\n"); 2368 2369 } 2370 2371 2380 static void createDsmlValueElement(Object value, StringBuffer message) 2381 throws NamingException 2382 { 2383 if (value instanceof String ) { 2385 String stringVal = escape((String ) value); 2386 message.append(TAB6).append("<dsml:value>").append(stringVal).append("</dsml:value>\n"); 2387 } 2388 else 2389 { 2390 try 2391 { 2392 byte[] data = (byte[]) value; 2393 String base64Data = new String (CBBase64.encode(data), "US-ASCII"); 2394System.out.println("SENDING BINARY DATA; byte length: " + data.length + " encoded: " + base64Data.length()); 2395 message.append(TAB6).append("<dsml:value xsi:type=\"xsd:base64Binary\">").append(base64Data).append("</dsml:value>\n"); 2396 } 2397 catch (UnsupportedEncodingException e) 2398 { 2399 NamingException ne = new NamingException("unexpected encoding exception adding attribute value of type " + value.getClass()); 2400 ne.setRootCause(e); 2401 throw ne; 2402 } 2403 catch (ClassCastException e) 2404 { 2405 NamingException ne = new NamingException("unexpected error adding attribute value of type " + value.getClass()); 2406 ne.setRootCause(e); 2407 throw ne; 2408 } 2409 } 2410 } 2411 2412 2416 2422 static StringBuffer constructDeleteRequest(String name) 2423 { 2424 StringBuffer message = new StringBuffer (200); 2425 message.append(STANDARDHEADER); 2426 2427 getDeleteRequestElement(message, name); 2428 2429 message.append(STANDARDFOOTER); 2430 2431 return message; 2432 } 2433 2434 2435 2443 static void getDeleteRequestElement(StringBuffer message, String name) 2444 { 2445 message.append(TAB4 + "<dsml:delRequest dn=\"").append(escapeName(name)).append("\"/>\n"); 2446 } 2447 2448 2463 static StringBuffer constructSearchRequest(String name, String scope, String derefAliases, long sizeLimit, int timeLimit, boolean typesOnly, String filter, String [] attributesToReturn) 2464 throws NamingException 2465 { 2466 StringBuffer message = new StringBuffer (500); 2467 message.append(STANDARDHEADER); 2468 2469 getSearchRequestHeader(message, name, scope, derefAliases, sizeLimit, timeLimit, typesOnly); 2470 2471 getSearchRequestFilter(message, filter, TAB4); 2472 2473 getSearchRequestAttributes(message, attributesToReturn); 2474 2475 message.append(SEARCHFOOTER).append(STANDARDFOOTER); 2476 2477 return message; 2478 } 2479 2480 private static Pattern escapeGTPattern = Pattern.compile("(>)", Pattern.DOTALL); 2481 private static Pattern escapeLTPattern = Pattern.compile("(<)", Pattern.DOTALL); 2482 private static Pattern escapeAMPPattern = Pattern.compile("(&)", Pattern.DOTALL); 2483 private static Pattern escapeQUOTPattern = Pattern.compile("(")", Pattern.DOTALL); 2484 private static Pattern escapeAPOSPattern = Pattern.compile("(')", Pattern.DOTALL); 2485 private static Pattern escapeOCTOPattern = Pattern.compile("(&#.*?;)", Pattern.DOTALL); 2486 2487 2494 2495 2498 static String unescape(String value) 2499 { 2500 if (value.indexOf('&') == -1) return value; 2503 value = escapeGTPattern.matcher(value).replaceAll(">"); 2504 value = escapeLTPattern.matcher(value).replaceAll("<"); 2505 value = escapeQUOTPattern.matcher(value).replaceAll("\""); 2506 value = escapeAPOSPattern.matcher(value).replaceAll("'"); 2507 value = escapeAMPPattern.matcher(value).replaceAll("&"); 2508 2509 Matcher OCTOMatcher = escapeOCTOPattern.matcher(value); 2510 2511 while (OCTOMatcher.find()) 2513 { 2514 String escapeChar = OCTOMatcher.group(1); 2515 2516 2521 int len = escapeChar.length(); 2522 char char1 = 0; 2523 char char2 = 0; 2524 2525 if (len >= 5) 2526 { 2527 char1 = escapeChar.charAt(len - 3); 2528 char2 = escapeChar.charAt(len - 2); 2529 } 2530 if (Character.isDigit(char1) && Character.isDigit(char2)) 2531 { 2532 int val = Character.digit(char1, 16) * 16 + Character.digit(char2, 16); 2533 try 2534 { 2535 String replacement = new String (new byte[]{new Integer (val).byteValue()}, "US-ASCII"); 2536 value = OCTOMatcher.replaceFirst(replacement); 2537 } 2538 catch (UnsupportedEncodingException e) { 2540 log.log(Level.SEVERE, "Unexpected exception trying to decode escaped value " + OCTOMatcher.group(1), e); 2541 } 2542 } 2543 else 2544 { 2545 log.info("unable to decode escaped value: " + escapeChar); 2546 break; } 2548 2549 OCTOMatcher = escapeOCTOPattern.matcher(value); 2550 } 2551 2552 return value; 2553 } 2554 2555 2562 static String escapeName(String value) 2563 { 2564 return escape(value); 2565 } 2566 2567 2568 2574 static String escape(String value) 2575 { 2576 int len = value.length(); 2577 2578 2580 boolean needsEscaping = false; 2581 for (int i = 0; i < len; i++) 2582 { 2583 switch (value.charAt(i)) 2584 { 2585 case '>': 2586 needsEscaping = true; 2587 break; 2588 case '<': 2589 needsEscaping = true; 2590 break; 2591 case '&': 2592 needsEscaping = true; 2593 break; 2594 case '\"': 2595 needsEscaping = true; 2596 break; 2597 case '\'': 2598 needsEscaping = true; 2599 break; 2600 } 2601 if (needsEscaping) 2602 i = len; 2603 } 2604 2605 if (!needsEscaping) 2606 return value; 2607 2608 StringBuffer buffy = new StringBuffer (value); 2610 2611 for (int i = len - 1; i >= 0; i--) 2612 { 2613 switch (value.charAt(i)) 2614 { 2615 case '>': 2616 buffy.replace(i, i + 1, ">"); 2617 break; 2618 case '<': 2619 buffy.replace(i, i + 1, "<"); 2620 break; 2621 case '&': 2622 buffy.replace(i, i + 1, "&"); 2623 break; 2624 case '\"': 2625 buffy.replace(i, i + 1, """); 2626 break; 2627 case '\'': 2628 buffy.replace(i, i + 1, "'"); 2629 break; 2630 } 2631 } 2632 2633 return buffy.toString(); 2634 } 2635 2636 2658 2659 static String getSearchRequestFilter(StringBuffer message, String filter, String indent) 2660 throws NamingException 2661 { 2662 if (filter == null) 2663 throw new NamingException("null filter expression in get SearchRequestFilter"); 2664 2665 filter = filter.trim(); 2666 if (filter.length() == 0) 2667 throw new NamingException("empty filter expression in get SearchRequestFilter"); 2668 2669 log.finest("translating ldap filter '" + filter + "'"); 2670 2673 2675 2678 StringBuffer padding = new StringBuffer (indent); 2679 2680 2683 Stack stack = new Stack(); 2684 2685 2688 StringTokenizer filterElements = new StringTokenizer(filter, "()", true); 2689 2690 message.append(padding).append("<dsml:filter>\n"); 2691 2692 while (filterElements.hasMoreTokens()) 2693 { 2694 String filterToken = filterElements.nextToken(); 2695 try 2696 { 2697 if (filterToken.charAt(0) == '(') 2698 { 2699 filterToken = filterElements.nextToken(); 2701 padding.append(TAB); 2702 message.append(padding); 2703 2704 if (filterToken.length() == 1) 2705 { 2706 switch (filterToken.charAt(0)) 2707 { 2708 case '&': 2709 message.append("<dsml:and>\n"); 2710 stack.push("</dsml:and>\n"); 2711 break; 2712 case '|': 2713 message.append("<dsml:or>\n"); 2714 stack.push("</dsml:or>\n"); 2715 break; 2716 case '!': 2717 message.append("<dsml:not>\n"); 2718 stack.push("</dsml:not>\n"); 2719 break; 2720 default: 2721 throw new NamingException("unexpected token '" + filterToken + "' in ldap filter: " + filter); 2722 } 2723 } 2724 else 2725 { 2726 stack.push(""); translateFilterItem(filterToken, padding, message); padding.setLength(padding.length() - TABLEN); 2729 } 2730 } 2731 else if (filterToken.charAt(0) == ')') 2732 { 2733 String closingElement = (String ) stack.pop(); 2734 if (closingElement.length() > 0) { 2736 message.append(padding).append(closingElement); padding.setLength(padding.length() - TABLEN); 2738 } 2739 } 2740 else 2741 { 2742 throw new NamingException("unexpected token '" + filterToken + "' in search filter: " + filter); 2743 } 2744 } 2745 catch (EmptyStackException e) 2746 { 2747 throw new NamingException("unexpected end of ldap search filter (empty or non matching brackets?): " + filter); 2748 } 2749 } 2750 2751 message.append(padding).append("</dsml:filter>\n"); 2752 2753 return message.toString(); 2754 } 2755 2756 static void translateFilterItem(String filterToken, StringBuffer padding, StringBuffer dsmlFilter) 2757 throws NamingException 2758 { 2759 int equalpos = filterToken.indexOf('='); 2760 if (equalpos < 1 || equalpos > filterToken.length() - 2) 2761 throw new NamingException("Unable to parse ldap filter element '" + filterToken + "'"); 2762 2763 String attribute = filterToken.substring(0, equalpos); String expression = filterToken.substring(equalpos + 1); 2766 char endOfAttribute = attribute.charAt(equalpos - 1); 2768 if (endOfAttribute == '<' || endOfAttribute == '>' || endOfAttribute == '~') 2770 { 2771 attribute = attribute.substring(0, equalpos - 1); String value = new StringBuffer (100).append(" name=\"").append(attribute).append("\">").append(padding).append(TAB).append("<dsml:value>").append(escape(expression)).append("</dsml:value>").append(padding).toString(); 2773 2774 switch (endOfAttribute) 2775 { 2776 case '<': 2777 dsmlFilter.append("<dsml:lessOrEqual").append(value).append("</dsml:lessOrEqual>\n"); 2778 break; 2779 case '>': 2780 dsmlFilter.append("<dsml:greaterOrEqual").append(value).append("</dsml:greaterOrEqual>\n"); 2781 break; 2782 case '~': 2783 dsmlFilter.append("<dsml:approxMatch").append(value).append("</dsml:approxMatch>\n"); 2784 break; 2785 } 2786 } 2787 else if (expression.indexOf('*') == -1) 2789 { 2790 dsmlFilter.append("<dsml:equalityMatch name=\"").append(attribute).append("\">").append(padding).append(" ").append("<dsml:value>").append(escape(expression)).append("</dsml:value>").append(padding).append("</dsml:equalityMatch>\n"); 2791 } 2792 else if (expression.equals("*")) 2794 { 2795 dsmlFilter.append("<dsml:present name=\"").append(attribute).append("\"/>\n"); 2796 } 2797 else { 2800 dsmlFilter.append("<dsml:substrings name=\"").append(attribute).append("\">\n"); 2801 padding.append(TAB); 2802 2803 StringTokenizer substringFilter = new StringTokenizer(expression, "*", true); 2805 String initial = substringFilter.nextToken(); 2806 if (initial.equals("*") == false) 2807 dsmlFilter.append(padding).append("<dsml:initial>").append(escape(initial)).append("</dsml:initial>\n"); 2809 while (substringFilter.hasMoreTokens()) 2810 { 2811 String filterElement = substringFilter.nextToken(); 2812 if (filterElement.equals("*") == false) { 2814 if (substringFilter.hasMoreTokens() == false) dsmlFilter.append(padding).append("<dsml:final>").append(escape(filterElement)).append("</dsml:final>\n"); 2816 else 2817 dsmlFilter.append(padding).append("<dsml:any>").append(escape(filterElement)).append("</dsml:any>\n"); 2818 } 2819 } 2820 padding.setLength(padding.length() - TABLEN); 2821 dsmlFilter.append(padding).append("</dsml:substrings>\n"); 2822 2823 } 2824 } 2826} 2827 | Popular Tags |