1/*
2 * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package com.sun.jndi.ldap;
27
28import java.security.AccessControlContext;
29import java.security.AccessController;
30import java.security.PrivilegedActionException;
31import java.security.PrivilegedExceptionAction;
32import java.util.Vector;
33import javax.naming.*;
34import javax.naming.directory.*;
35import javax.naming.spi.*;
36import javax.naming.ldap.*;
37import javax.naming.ldap.LdapName;
38
39import com.sun.jndi.toolkit.ctx.Continuation;
40
41final class LdapSearchEnumeration
42        extends AbstractLdapNamingEnumeration<SearchResult> {
43
44    private Name startName;             // prefix of names of search results
45    private LdapCtx.SearchArgs searchArgs = null;
46
47    private final AccessControlContext acc = AccessController.getContext();
48
49    LdapSearchEnumeration(LdapCtx homeCtx, LdapResult search_results,
50        String starter, LdapCtx.SearchArgs args, Continuation cont)
51        throws NamingException {
52
53        super(homeCtx, search_results,
54              args.name, /* listArg */
55              cont);
56
57        // fully qualified name of starting context of search
58        startName = new LdapName(starter);
59        searchArgs = args;
60    }
61
62    @Override
63    protected SearchResult createItem(String dn, Attributes attrs,
64                                      Vector<Control> respCtls)
65            throws NamingException {
66
67        Object obj = null;
68
69        String relStart;         // name relative to starting search context
70        String relHome;          // name relative to homeCtx.currentDN
71        boolean relative = true; // whether relative to currentDN
72
73        // need to strip off all but lowest component of dn
74        // so that is relative to current context (currentDN)
75
76        try {
77            Name parsed = new LdapName(dn);
78            // System.err.println("dn string: " + dn);
79            // System.err.println("dn name: " + parsed);
80
81            if (startName != null && parsed.startsWith(startName)) {
82                relStart = parsed.getSuffix(startName.size()).toString();
83                relHome = parsed.getSuffix(homeCtx.currentParsedDN.size()).toString();
84            } else {
85                relative = false;
86                relHome = relStart =
87                    LdapURL.toUrlString(homeCtx.hostname, homeCtx.port_number,
88                    dn, homeCtx.hasLdapsScheme);
89            }
90        } catch (NamingException e) {
91            // could not parse name
92            relative = false;
93            relHome = relStart =
94                LdapURL.toUrlString(homeCtx.hostname, homeCtx.port_number,
95                dn, homeCtx.hasLdapsScheme);
96        }
97
98        // Name relative to search context
99        CompositeName cn = new CompositeName();
100        if (!relStart.equals("")) {
101            cn.add(relStart);
102        }
103
104        // Name relative to homeCtx
105        CompositeName rcn = new CompositeName();
106        if (!relHome.equals("")) {
107            rcn.add(relHome);
108        }
109        //System.err.println("relStart: " + cn);
110        //System.err.println("relHome: " + rcn);
111
112        // Fix attributes to be able to get schema
113        homeCtx.setParents(attrs, rcn);
114
115        // only generate object when requested
116        if (searchArgs.cons.getReturningObjFlag()) {
117
118            if (attrs.get(Obj.JAVA_ATTRIBUTES[Obj.CLASSNAME]) != null) {
119                // Entry contains Java-object attributes (ser/ref object)
120                // serialized object or object reference
121                try {
122                    obj = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
123                        @Override
124                        public Object run() throws NamingException {
125                            return Obj.decodeObject(attrs);
126                        }
127                    }, acc);
128                } catch (PrivilegedActionException e) {
129                    throw (NamingException)e.getException();
130                }
131            }
132            if (obj == null) {
133                obj = new LdapCtx(homeCtx, dn);
134            }
135
136            // Call getObjectInstance before removing unrequested attributes
137            try {
138                // rcn is either relative to homeCtx or a fully qualified DN
139                obj = DirectoryManager.getObjectInstance(
140                    obj, rcn, (relative ? homeCtx : null),
141                    homeCtx.envprops, attrs);
142            } catch (NamingException e) {
143                throw e;
144            } catch (Exception e) {
145                NamingException ne =
146                    new NamingException(
147                            "problem generating object using object factory");
148                ne.setRootCause(e);
149                throw ne;
150            }
151
152            // remove Java attributes from result, if necessary
153            // Even if CLASSNAME attr not there, there might be some
154            // residual attributes
155
156            String[] reqAttrs;
157            if ((reqAttrs = searchArgs.reqAttrs) != null) {
158                // create an attribute set for those requested
159                Attributes rattrs = new BasicAttributes(true); // ignore case
160                for (int i = 0; i < reqAttrs.length; i++) {
161                    rattrs.put(reqAttrs[i], null);
162                }
163                for (int i = 0; i < Obj.JAVA_ATTRIBUTES.length; i++) {
164                    // Remove Java-object attributes if not requested
165                    if (rattrs.get(Obj.JAVA_ATTRIBUTES[i]) == null) {
166                        attrs.remove(Obj.JAVA_ATTRIBUTES[i]);
167                    }
168                }
169            }
170
171        }
172
173        /*
174         * name in search result is either the stringified composite name
175         * relative to the search context that can be passed directly to
176         * methods of the search context, or the fully qualified DN
177         * which can be used with the initial context.
178         */
179        SearchResult sr;
180        if (respCtls != null) {
181            sr = new SearchResultWithControls(
182                (relative ? cn.toString() : relStart), obj, attrs,
183                relative, homeCtx.convertControls(respCtls));
184        } else {
185            sr = new SearchResult(
186                (relative ? cn.toString() : relStart),
187                obj, attrs, relative);
188        }
189        sr.setNameInNamespace(dn);
190        return sr;
191    }
192
193    @Override
194    public void appendUnprocessedReferrals(LdapReferralException ex) {
195
196        // a referral has been followed so do not create relative names
197        startName = null;
198        super.appendUnprocessedReferrals(ex);
199    }
200
201    @Override
202    protected AbstractLdapNamingEnumeration<? extends NameClassPair> getReferredResults(
203            LdapReferralContext refCtx) throws NamingException {
204        // repeat the original operation at the new context
205        return (AbstractLdapNamingEnumeration<? extends NameClassPair>)refCtx.search(
206                searchArgs.name, searchArgs.filter, searchArgs.cons);
207    }
208
209    @Override
210    protected void update(AbstractLdapNamingEnumeration<? extends NameClassPair> ne) {
211        super.update(ne);
212
213        // Update search-specific variables
214        LdapSearchEnumeration se = (LdapSearchEnumeration)ne;
215        startName = se.startName;
216    }
217
218    void setStartName(Name nm) {
219        startName = nm;
220    }
221}
222