KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > HTTPClient > AuthorizationInfo


1 /*
2  * @(#)AuthorizationInfo.java 0.3-2 18/06/1999
3  *
4  * This file is part of the HTTPClient package
5  * Copyright (C) 1996-1999 Ronald Tschalär
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free
19  * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
20  * MA 02111-1307, USA
21  *
22  * For questions, suggestions, bug-reports, enhancement-requests etc.
23  * I may be contacted at:
24  *
25  * ronald@innovation.ch
26  *
27  */

28
29 package HTTPClient;
30
31
32 import java.io.IOException JavaDoc;
33 import java.net.ProtocolException JavaDoc;
34 import java.util.Vector JavaDoc;
35 import java.util.Hashtable JavaDoc;
36 import java.util.Enumeration JavaDoc;
37
38
39 /**
40  * Holds the information for an authorization response.
41  *
42  * <P>There are 7 fields which make up this class: host, port, scheme,
43  * realm, cookie, params, and extra_info. The host and port select which
44  * server the info will be sent to. The realm is server specified string
45  * which groups various URLs under a given server together and which is
46  * used to select the correct info when a server issues an auth challenge;
47  * for schemes which don't use a realm (such as "NTLM", "PEM", and
48  * "Kerberos") the realm must be the empty string (""). The scheme is the
49  * authorization scheme used (such as "Basic" or "Digest").
50  *
51  * <P>There are basically two formats used for the Authorization header,
52  * the one used by the "Basic" scheme and derivatives, and the one used by
53  * the "Digest" scheme and derivatives. The first form contains just the
54  * the scheme and a "cookie":
55  *
56  * <PRE> Authorization: Basic aGVsbG86d29ybGQ=</PRE>
57  *
58  * The second form contains the scheme followed by a number of parameters
59  * in the form of name=value pairs:
60  *
61  * <PRE> Authorization: Digest username="hello", realm="test", nonce="42", ...</PRE>
62  *
63  * The two fields "cookie" and "params" correspond to these two forms.
64  * <A HREF="#toString()">toString()</A> is used by the AuthorizationModule
65  * when generating the Authorization header and will format the info
66  * accordingly. Note that "cookie" and "params" are mutually exclusive: if
67  * the cookie field is non-null then toString() will generate the first
68  * form; otherwise it will generate the second form.
69  *
70  * <P>In some schemes "extra" information needs to be kept which doesn't
71  * appear directly in the Authorization header. An example of this are the
72  * A1 and A2 strings in the Digest scheme. Since all elements in the params
73  * field will appear in the Authorization header this field can't be used
74  * for storing such info. This is what the extra_info field is for. It is
75  * an arbitrary object which can be manipulated by the corresponding
76  * setExtraInfo() and getExtraInfo() methods, but which will not be printed
77  * by toString().
78  *
79  * <P>The addXXXAuthorization(), removeXXXAuthorization(), and
80  * getAuthorization() methods manipulate and query an internal list of
81  * AuthorizationInfo instances. There can be only one instance per host,
82  * port, scheme, and realm combination (see <A HREF="#equals">equals()</A>).
83  *
84  * @version 0.3-2 18/06/1999
85  * @author Ronald Tschalär
86  * @since V0.1
87  */

88
89 public class AuthorizationInfo implements GlobalConstants, Cloneable JavaDoc
90 {
91     // class fields
92

93     /** Holds the list of lists of authorization info structures */
94     private static Hashtable JavaDoc CntxtList = new Hashtable JavaDoc();
95
96     /** A pointer to the handler to be called when we need authorization info */
97     private static AuthorizationHandler
98                  AuthHandler = new DefaultAuthHandler();
99
100     static
101     {
102     CntxtList.put(HTTPConnection.getDefaultContext(), new Hashtable JavaDoc());
103     }
104
105
106     // the instance oriented stuff
107

108     /** the host (lowercase) */
109     private String JavaDoc host;
110
111     /** the port */
112     private int port;
113
114     /** the scheme. (e.g. "Basic")
115      * Note: don't lowercase because some buggy servers use a case-sensitive
116      * match */

117     private String JavaDoc scheme;
118
119     /** the realm */
120     private String JavaDoc realm;
121
122     /** the string used for the "Basic", "NTLM", and other authorization
123      * schemes which don't use parameters */

124     private String JavaDoc cookie;
125
126     /** any parameters */
127     private NVPair[] auth_params = new NVPair[0];
128
129     /** additional info which won't be displayed in the toString() */
130     private Object JavaDoc extra_info = null;
131
132     /** a list of paths where this realm has been known to be required */
133     private String JavaDoc[] paths = new String JavaDoc[0];
134
135
136     // Constructors
137

138     /**
139      * Creates an new info structure for the specified host and port.
140      *
141      * @param host the host
142      * @param port the port
143      */

144     AuthorizationInfo(String JavaDoc host, int port)
145     {
146     this.host = host.trim().toLowerCase();
147     this.port = port;
148     }
149
150
151     /**
152      * Creates a new info structure for the specified host and port with the
153      * specified scheme, realm, params. The cookie is set to null.
154      *
155      * @param host the host
156      * @param port the port
157      * @param scheme the scheme
158      * @param realm the realm
159      * @param params the parameters as an array of name/value pairs, or null
160      * @param info arbitrary extra info, or null
161      */

162     public AuthorizationInfo(String JavaDoc host, int port, String JavaDoc scheme,
163                  String JavaDoc realm, NVPair params[], Object JavaDoc info)
164     {
165     this.scheme = scheme.trim();
166     this.host = host.trim().toLowerCase();
167     this.port = port;
168     this.realm = realm;
169     this.cookie = null;
170
171     if (params != null)
172         auth_params = Util.resizeArray(params, params.length);
173
174     this.extra_info = info;
175     }
176
177
178     /**
179      * Creates a new info structure for the specified host and port with the
180      * specified scheme, realm and cookie. The params is set to a zero-length
181      * array, and the extra_info is set to null.
182      *
183      * @param host the host
184      * @param port the port
185      * @param scheme the scheme
186      * @param realm the realm
187      * @param cookie for the "Basic" scheme this is the base64-encoded
188      * username/password; for the "NTLM" scheme this is the
189      * base64-encoded username/password message.
190      */

191     public AuthorizationInfo(String JavaDoc host, int port, String JavaDoc scheme,
192                  String JavaDoc realm, String JavaDoc cookie)
193     {
194     this.scheme = scheme.trim();
195     this.host = host.trim().toLowerCase();
196     this.port = port;
197     this.realm = realm;
198     if (cookie != null)
199         this.cookie = cookie.trim();
200     else
201         this.cookie = null;
202     }
203
204
205     /**
206      * Creates a new copy of the given AuthorizationInfo.
207      *
208      * @param templ the info to copy
209      */

210     AuthorizationInfo(AuthorizationInfo templ)
211     {
212     this.scheme = templ.scheme;
213     this.host = templ.host;
214     this.port = templ.port;
215     this.realm = templ.realm;
216     this.cookie = templ.cookie;
217
218     this.auth_params =
219         Util.resizeArray(templ.auth_params, templ.auth_params.length);
220
221     this.extra_info = templ.extra_info;
222     }
223
224
225     // Class Methods
226

227     /**
228      * Set's the authorization handler. This handler is called whenever
229      * the server requests authorization and no entry for the requested
230      * scheme and realm can be found in the list. The handler must implement
231      * the AuthorizationHandler interface.
232      * <BR>If no handler is set then a default handler is used. This handler
233      * currently only handles the "Basic" scheme and brings up a popup which
234      * prompts for the username and password.
235      * <BR>The default handler can be disabled by setting the auth handler
236      * to <var>null</var>.
237      *
238      * @param handler the new authorization handler
239      * @return the old authorization handler
240      * @see AuthorizationHandler
241      */

242     public static AuthorizationHandler
243             setAuthHandler(AuthorizationHandler handler)
244     {
245     AuthorizationHandler tmp = AuthHandler;
246     AuthHandler = handler;
247
248     return tmp;
249     }
250
251
252     /**
253      * Get's the current authorization handler.
254      *
255      * @return the current authorization handler, or null if none is set.
256      * @see AuthorizationHandler
257      */

258     public static AuthorizationHandler getAuthHandler()
259     {
260     return AuthHandler;
261     }
262
263
264     /**
265      * Searches for the authorization info using the given host, port,
266      * scheme and realm. The context is the default context.
267      *
268      * @param host the host
269      * @param port the port
270      * @param scheme the scheme
271      * @param realm the realm
272      * @return a pointer to the authorization data or null if not found
273      */

274     public static AuthorizationInfo getAuthorization(
275                         String JavaDoc host, int port,
276                         String JavaDoc scheme, String JavaDoc realm)
277     {
278     return getAuthorization(host, port, scheme, realm,
279                 HTTPConnection.getDefaultContext());
280     }
281
282
283     /**
284      * Searches for the authorization info in the given context using the
285      * given host, port, scheme and realm.
286      *
287      * @param host the host
288      * @param port the port
289      * @param scheme the scheme
290      * @param realm the realm
291      * @param context the context this info is associated with
292      * @return a pointer to the authorization data or null if not found
293      */

294     public static synchronized AuthorizationInfo getAuthorization(
295                         String JavaDoc host, int port,
296                         String JavaDoc scheme, String JavaDoc realm,
297                         Object JavaDoc context)
298     {
299     Hashtable JavaDoc AuthList = Util.getList(CntxtList, context);
300
301     AuthorizationInfo auth_info =
302         new AuthorizationInfo(host.trim(), port, scheme.trim(),
303                   realm, (NVPair[]) null, null);
304
305     return (AuthorizationInfo) AuthList.get(auth_info);
306     }
307
308
309     /**
310      * Queries the AuthHandler for authorization info. It also adds this
311      * info to the list.
312      *
313      * @param auth_info any info needed by the AuthHandler; at a minimum the
314      * host, scheme and realm should be set.
315      * @param req the request which initiated this query
316      * @param resp the full response
317      * @return a structure containing the requested info, or null if either
318      * no AuthHandler is set or the user canceled the request.
319      * @exception AuthSchemeNotImplException if this is thrown by
320      * the AuthHandler.
321      */

322     static AuthorizationInfo queryAuthHandler(AuthorizationInfo auth_info,
323                           RoRequest req, RoResponse resp)
324     throws AuthSchemeNotImplException
325     {
326     if (AuthHandler == null)
327         return null;
328
329     AuthorizationInfo new_info =
330             AuthHandler.getAuthorization(auth_info, req, resp);
331     if (new_info != null)
332     {
333         if (req != null)
334         addAuthorization((AuthorizationInfo) new_info.clone(),
335                  req.getConnection().getContext());
336         else
337         addAuthorization((AuthorizationInfo) new_info.clone(),
338                  HTTPConnection.getDefaultContext());
339     }
340
341     return new_info;
342     }
343
344
345     /**
346      * Searches for the authorization info using the host, port, scheme and
347      * realm from the given info struct. If not found it queries the
348      * AuthHandler (if set).
349      *
350      * @param auth_info the AuthorizationInfo
351      * @param request the request which initiated this query
352      * @param resp the full response
353      * @param query_auth_h if true, query the auth-handler if no info found.
354      * @return a pointer to the authorization data or null if not found
355      * @exception AuthSchemeNotImplException If thrown by the AuthHandler.
356      */

357     static synchronized AuthorizationInfo getAuthorization(
358                     AuthorizationInfo auth_info, RoRequest req,
359                     RoResponse resp, boolean query_auth_h)
360     throws AuthSchemeNotImplException
361     {
362     Hashtable JavaDoc AuthList;
363     if (req != null)
364         AuthList = Util.getList(CntxtList, req.getConnection().getContext());
365     else
366         AuthList = Util.getList(CntxtList, HTTPConnection.getDefaultContext());
367
368     AuthorizationInfo new_info =
369         (AuthorizationInfo) AuthList.get(auth_info);
370
371     if (new_info == null && query_auth_h)
372         new_info = queryAuthHandler(auth_info, req, resp);
373
374     return new_info;
375     }
376
377
378     /**
379      * Searches for the authorization info given a host, port, scheme and
380      * realm. Queries the AuthHandler if not found in list.
381      *
382      * @param host the host
383      * @param port the port
384      * @param scheme the scheme
385      * @param realm the realm
386      * @param query_auth_h if true, query the auth-handler if no info found.
387      * @return a pointer to the authorization data or null if not found
388      * @exception AuthSchemeNotImplException If thrown by the AuthHandler.
389      */

390     static AuthorizationInfo getAuthorization(String JavaDoc host, int port,
391                           String JavaDoc scheme, String JavaDoc realm,
392                           boolean query_auth_h)
393     throws AuthSchemeNotImplException
394     {
395     return getAuthorization(new AuthorizationInfo(host.trim(), port,
396                 scheme.trim(), realm, (NVPair[]) null, null),
397                 null, null, query_auth_h);
398     }
399
400
401     /**
402      * Adds an authorization entry to the list using the default context.
403      * If an entry for the specified scheme and realm already exists then
404      * its cookie and params are replaced with the new data.
405      *
406      * @param auth_info the AuthorizationInfo to add
407      */

408     public static void addAuthorization(AuthorizationInfo auth_info)
409     {
410     addAuthorization(auth_info, HTTPConnection.getDefaultContext());
411     }
412
413
414     /**
415      * Adds an authorization entry to the list. If an entry for the
416      * specified scheme and realm already exists then its cookie and
417      * params are replaced with the new data.
418      *
419      * @param auth_info the AuthorizationInfo to add
420      * @param context the context to associate this info with
421      */

422     public static void addAuthorization(AuthorizationInfo auth_info,
423                     Object JavaDoc context)
424     {
425     Hashtable JavaDoc AuthList = Util.getList(CntxtList, context);
426
427     // merge path list
428
AuthorizationInfo old_info =
429                 (AuthorizationInfo) AuthList.get(auth_info);
430     if (old_info != null)
431     {
432         int ol = old_info.paths.length,
433         al = auth_info.paths.length;
434
435         if (al == 0)
436         auth_info.paths = old_info.paths;
437         else
438         {
439         auth_info.paths = Util.resizeArray(auth_info.paths, al+ol);
440         System.arraycopy(old_info.paths, 0, auth_info.paths, al, ol);
441         }
442     }
443
444     AuthList.put(auth_info, auth_info);
445     }
446
447
448     /**
449      * Adds an authorization entry to the list using the default context.
450      * If an entry for the specified scheme and realm already exists then
451      * its cookie and params are replaced with the new data.
452      *
453      * @param host the host
454      * @param port the port
455      * @param scheme the scheme
456      * @param realm the realm
457      * @param cookie the cookie
458      * @param params an array of name/value pairs of parameters
459      * @param info arbitrary extra auth info
460      */

461     public static void addAuthorization(String JavaDoc host, int port, String JavaDoc scheme,
462                     String JavaDoc realm, String JavaDoc cookie,
463                     NVPair params[], Object JavaDoc info)
464     {
465     addAuthorization(host, port, scheme, realm, cookie, params, info,
466              HTTPConnection.getDefaultContext());
467     }
468
469
470     /**
471      * Adds an authorization entry to the list. If an entry for the
472      * specified scheme and realm already exists then its cookie and
473      * params are replaced with the new data.
474      *
475      * @param host the host
476      * @param port the port
477      * @param scheme the scheme
478      * @param realm the realm
479      * @param cookie the cookie
480      * @param params an array of name/value pairs of parameters
481      * @param info arbitrary extra auth info
482      * @param context the context to associate this info with
483      */

484     public static void addAuthorization(String JavaDoc host, int port, String JavaDoc scheme,
485                     String JavaDoc realm, String JavaDoc cookie,
486                     NVPair params[], Object JavaDoc info,
487                     Object JavaDoc context)
488     {
489     AuthorizationInfo auth =
490         new AuthorizationInfo(host, port, scheme, realm, cookie);
491     if (params != null && params.length > 0)
492         auth.auth_params = Util.resizeArray(params, params.length);
493     auth.extra_info = info;
494
495     addAuthorization(auth, context);
496     }
497
498
499     /**
500      * Adds an authorization entry for the "Basic" authorization scheme to
501      * the list using the default context. If an entry already exists for
502      * the "Basic" scheme and the specified realm then it is overwritten.
503      *
504      * @param host the host
505      * @param port the port
506      * @param realm the realm
507      * @param user the username
508      * @param passwd the password
509      */

510     public static void addBasicAuthorization(String JavaDoc host, int port,
511                          String JavaDoc realm, String JavaDoc user,
512                          String JavaDoc passwd)
513     {
514     addAuthorization(host, port, "Basic", realm,
515              Codecs.base64Encode(user + ":" + passwd),
516              (NVPair[]) null, null);
517     }
518
519
520     /**
521      * Adds an authorization entry for the "Basic" authorization scheme to
522      * the list. If an entry already exists for the "Basic" scheme and the
523      * specified realm then it is overwritten.
524      *
525      * @param host the host
526      * @param port the port
527      * @param realm the realm
528      * @param user the username
529      * @param passwd the password
530      * @param context the context to associate this info with
531      */

532     public static void addBasicAuthorization(String JavaDoc host, int port,
533                          String JavaDoc realm, String JavaDoc user,
534                          String JavaDoc passwd, Object JavaDoc context)
535     {
536     addAuthorization(host, port, "Basic", realm,
537              Codecs.base64Encode(user + ":" + passwd),
538              (NVPair[]) null, null, context);
539     }
540
541
542     /**
543      * Adds an authorization entry for the "Digest" authorization scheme to
544      * the list using the default context. If an entry already exists for the
545      * "Digest" scheme and the specified realm then it is overwritten.
546      *
547      * @param host the host
548      * @param port the port
549      * @param realm the realm
550      * @param user the username
551      * @param passwd the password
552      */

553     public static void addDigestAuthorization(String JavaDoc host, int port,
554                           String JavaDoc realm, String JavaDoc user,
555                           String JavaDoc passwd)
556     {
557     addDigestAuthorization(host, port, realm, user, passwd,
558                    HTTPConnection.getDefaultContext());
559     }
560
561
562     /**
563      * Adds an authorization entry for the "Digest" authorization scheme to
564      * the list. If an entry already exists for the "Digest" scheme and the
565      * specified realm then it is overwritten.
566      *
567      * @param host the host
568      * @param port the port
569      * @param realm the realm
570      * @param user the username
571      * @param passwd the password
572      * @param context the context to associate this info with
573      */

574     public static void addDigestAuthorization(String JavaDoc host, int port,
575                           String JavaDoc realm, String JavaDoc user,
576                           String JavaDoc passwd, Object JavaDoc context)
577     {
578     AuthorizationInfo prev =
579             getAuthorization(host, port, "Digest", realm, context);
580     NVPair[] params;
581
582     if (prev == null)
583     {
584         params = new NVPair[4];
585         params[0] = new NVPair("username", user);
586         params[1] = new NVPair("uri", "");
587         params[2] = new NVPair("nonce", "");
588         params[3] = new NVPair("response", "");
589     }
590     else
591     {
592         params = prev.getParams();
593         for (int idx=0; idx<params.length; idx++)
594         {
595         if (params[idx].getName().equalsIgnoreCase("username"))
596         {
597             params[idx] = new NVPair("username", user);
598             break;
599         }
600         }
601     }
602
603     String JavaDoc[] extra = { new MD5(user + ":" + realm + ":" + passwd).asHex(),
604                null };
605
606     addAuthorization(host, port, "Digest", realm, null, params, extra,
607              context);
608     }
609
610
611     /**
612      * Removes an authorization entry from the list using the default context.
613      * If no entry for the specified host, port, scheme and realm exists then
614      * this does nothing.
615      *
616      * @param auth_info the AuthorizationInfo to remove
617      */

618     public static void removeAuthorization(AuthorizationInfo auth_info)
619     {
620     removeAuthorization(auth_info, HTTPConnection.getDefaultContext());
621     }
622
623
624     /**
625      * Removes an authorization entry from the list. If no entry for the
626      * specified host, port, scheme and realm exists then this does nothing.
627      *
628      * @param auth_info the AuthorizationInfo to remove
629      * @param context the context this info is associated with
630      */

631     public static void removeAuthorization(AuthorizationInfo auth_info,
632                        Object JavaDoc context)
633     {
634     Hashtable JavaDoc AuthList = Util.getList(CntxtList, context);
635     AuthList.remove(auth_info);
636     }
637
638
639     /**
640      * Removes an authorization entry from the list using the default context.
641      * If no entry for the specified host, port, scheme and realm exists then
642      * this does nothing.
643      *
644      * @param host the host
645      * @param port the port
646      * @param scheme the scheme
647      * @param realm the realm
648      */

649     public static void removeAuthorization(String JavaDoc host, int port, String JavaDoc scheme,
650                        String JavaDoc realm)
651     {
652     removeAuthorization(
653         new AuthorizationInfo(host, port, scheme, realm, (NVPair[]) null,
654                   null));
655     }
656
657
658     /**
659      * Removes an authorization entry from the list. If no entry for the
660      * specified host, port, scheme and realm exists then this does nothing.
661      *
662      * @param host the host
663      * @param port the port
664      * @param scheme the scheme
665      * @param realm the realm
666      * @param context the context this info is associated with
667      */

668     public static void removeAuthorization(String JavaDoc host, int port, String JavaDoc scheme,
669                        String JavaDoc realm, Object JavaDoc context)
670     {
671     removeAuthorization(
672         new AuthorizationInfo(host, port, scheme, realm, (NVPair[]) null,
673                   null), context);
674     }
675
676
677     /**
678      * Tries to find the candidate in the current list of auth info for the
679      * given request. The paths associated with each auth info are examined,
680      * and the one with either the nearest direct parent or child is chosen.
681      * This is used for preemptively sending auth info.
682      *
683      * @param req the Request
684      * @return an AuthorizationInfo containing the info for the best match,
685      * or null if none found.
686      */

687     static AuthorizationInfo findBest(RoRequest req)
688     {
689     String JavaDoc path = Util.getPath(req.getRequestURI());
690     String JavaDoc host = req.getConnection().getHost();
691     int port = req.getConnection().getPort();
692
693
694     // First search for an exact match
695

696     Hashtable JavaDoc AuthList =
697             Util.getList(CntxtList, req.getConnection().getContext());
698     Enumeration JavaDoc list = AuthList.elements();
699     while (list.hasMoreElements())
700     {
701         AuthorizationInfo info = (AuthorizationInfo) list.nextElement();
702
703         if (!info.host.equals(host) || info.port != port)
704         continue;
705
706         String JavaDoc[] paths = info.paths;
707         for (int idx=0; idx<paths.length; idx++)
708         {
709         if (path.equals(paths[idx]))
710             return info;
711         }
712     }
713
714
715     // Now find the closest parent or child
716

717     AuthorizationInfo best = null;
718     String JavaDoc base = path.substring(0, path.lastIndexOf('/')+1);
719     int min = Integer.MAX_VALUE;
720
721     list = AuthList.elements();
722     while (list.hasMoreElements())
723     {
724         AuthorizationInfo info = (AuthorizationInfo) list.nextElement();
725
726         if (!info.host.equals(host) || info.port != port)
727         continue;
728
729         String JavaDoc[] paths = info.paths;
730         for (int idx=0; idx<paths.length; idx++)
731         {
732         // strip the last path segment, leaving a trailing "/"
733
String JavaDoc ibase =
734             paths[idx].substring(0, paths[idx].lastIndexOf('/')+1);
735
736         if (base.equals(ibase))
737             return info;
738
739         if (base.startsWith(ibase)) // found a parent
740
{
741             int num_seg = 0, pos = ibase.length()-1;
742             while ((pos = base.indexOf('/', pos+1)) != -1) num_seg++;
743
744             if (num_seg < min)
745             {
746             min = num_seg;
747             best = info;
748             }
749         }
750         else if (ibase.startsWith(base)) // found a child
751
{
752             int num_seg = 0, pos = base.length();
753             while ((pos = ibase.indexOf('/', pos+1)) != -1) num_seg++;
754
755             if (num_seg < min)
756             {
757             min = num_seg;
758             best = info;
759             }
760         }
761         }
762     }
763
764     return best;
765     }
766
767
768     /**
769      * Adds the path from the given resource to our path list. The path
770      * list is used for deciding when to preemptively send auth info.
771      *
772      * @param resource the resource from which to extract the path
773      */

774     public synchronized void addPath(String JavaDoc resource)
775     {
776     String JavaDoc path = Util.getPath(resource);
777
778     // First check that we don't already have this one
779
for (int idx=0; idx<paths.length; idx++)
780         if (paths[idx].equals(path)) return;
781
782     // Ok, add it
783
paths = Util.resizeArray(paths, paths.length+1);
784     paths[paths.length-1] = path;
785     }
786
787
788     /**
789      * Parses the authentication challenge(s) into an array of new info
790      * structures for the specified host and port.
791      *
792      * @param challenge a string containing authentication info. This must
793      * have the same format as value part of a
794      * WWW-authenticate response header field, and may
795      * contain multiple authentication challenges.
796      * @param req the original request.
797      * @exception ProtocolException if any error during the parsing occurs.
798      */

799     static AuthorizationInfo[] parseAuthString(String JavaDoc challenge, RoRequest req,
800                            RoResponse resp)
801         throws ProtocolException JavaDoc
802     {
803     int beg = 0,
804            end = 0;
805     char[] buf = challenge.toCharArray();
806     char ch;
807     int len = buf.length;
808
809     AuthorizationInfo auth_arr[] = new AuthorizationInfo[0],
810               curr;
811
812     while (Character.isSpace(buf[len-1])) len--;
813
814     while (true) // get all challenges
815
{
816         // get scheme
817
beg = Util.skipSpace(buf, beg);
818         if (beg == len) break;
819
820         end = Util.findSpace(buf, beg+1);
821
822         int sts;
823         try
824         { sts = resp.getStatusCode(); }
825         catch (IOException JavaDoc ioe)
826         { throw new ProtocolException JavaDoc(ioe.toString()); }
827         if (sts == 401)
828         curr = new AuthorizationInfo(req.getConnection().getHost(),
829                          req.getConnection().getPort());
830         else
831         curr = new AuthorizationInfo(req.getConnection().getProxyHost(),
832                         req.getConnection().getProxyPort());
833         curr.scheme = challenge.substring(beg, end);
834
835         // get auth-parameters
836
boolean first = true;
837         Vector JavaDoc params = new Vector JavaDoc();
838         while (true)
839         {
840         beg = Util.skipSpace(buf, end);
841         if (beg == len) break;
842
843         if (!first) // expect ","
844
{
845             if (buf[beg] != ',')
846             throw new ProtocolException JavaDoc("Bad Authentication header "
847                             + "format: '" + challenge +
848                             "'\nExpected \",\" at position "+
849                             beg);
850
851             beg = Util.skipSpace(buf, beg+1); // find param name
852
if (beg == len) break;
853             if (buf[beg] == ',') // skip empty params
854
{
855             end = beg;
856             continue;
857             }
858         }
859
860         int pstart = beg;
861
862         // extract name
863
end = beg + 1;
864         while (end < len && !Character.isSpace(buf[end]) &&
865                buf[end] != '=' && buf[end] != ',')
866             end++;
867
868         // hack to deal with schemes which use cookies in challenge
869
if (first &&
870             (end == len || buf[end] == '=' &&
871             (end+1 == len || (buf[end+1] == '=' && end+2 == len))))
872         {
873             curr.cookie = challenge.substring(beg, len);
874             beg = len;
875             break;
876         }
877
878         String JavaDoc param_name = challenge.substring(beg, end),
879                param_value;
880
881         beg = Util.skipSpace(buf, end); // find "=" or ","
882

883         if (beg < len && buf[beg] != '=' && buf[beg] != ',')
884         { // It's not a param, but another challenge
885
beg = pstart;
886             break;
887         }
888
889
890         if (buf[beg] == '=') // we have a value
891
{
892             beg = Util.skipSpace(buf, beg+1);
893             if (beg == len)
894             throw new ProtocolException JavaDoc("Bad Authentication header "
895                             + "format: " + challenge +
896                             "\nUnexpected EOL after token" +
897                             " at position " + (end-1));
898             if (buf[beg] != '"') // it's a token
899
{
900             end = Util.skipToken(buf, beg);
901             if (end == beg)
902                 throw new ProtocolException JavaDoc("Bad Authentication header "
903                 + "format: " + challenge + "\nToken expected at " +
904                 "position " + beg);
905             param_value = challenge.substring(beg, end);
906             }
907             else // it's a quoted-string
908
{
909             end = beg++;
910             do
911                 end = challenge.indexOf('"', end+1);
912             while (end != -1 && challenge.charAt(end-1) == '\\');
913             if (end == -1)
914                 throw new ProtocolException JavaDoc("Bad Authentication header "
915                 + "format: " + challenge + "\nClosing <\"> for "
916                 + "quoted-string starting at position " + beg
917                 + " not found");
918             param_value =
919                 Util.dequoteString(challenge.substring(beg, end));
920             end++;
921             }
922         }
923         else // this is not strictly allowed
924
param_value = null;
925
926         if (param_name.equalsIgnoreCase("realm"))
927             curr.realm = param_value;
928         else
929             params.addElement(new NVPair(param_name, param_value));
930
931         first = false;
932         }
933
934         if (!params.isEmpty())
935         {
936         curr.auth_params = new NVPair[params.size()];
937         params.copyInto(curr.auth_params);
938         }
939
940         if (curr.realm == null)
941         /* Can't do this if we're supposed to allow for broken schemes
942          * such as NTLM, Kerberos, and PEM.
943          *
944         throw new ProtocolException("Bad Authentication header "
945             + "format: " + challenge + "\nNo realm value found");
946          */

947         curr.realm = "";
948
949         auth_arr = Util.resizeArray(auth_arr, auth_arr.length+1);
950         auth_arr[auth_arr.length-1] = curr;
951     }
952
953     return auth_arr;
954     }
955
956
957     // Instance Methods
958

959     /**
960      * Get the host.
961      *
962      * @return a string containing the host name.
963      */

964     public final String JavaDoc getHost()
965     {
966     return host;
967     }
968
969
970     /**
971      * Get the port.
972      *
973      * @return an int containing the port number.
974      */

975     public final int getPort()
976     {
977     return port;
978     }
979
980
981     /**
982      * Get the scheme.
983      *
984      * @return a string containing the scheme.
985      */

986     public final String JavaDoc getScheme()
987     {
988     return scheme;
989     }
990
991
992     /**
993      * Get the realm.
994      *
995      * @return a string containing the realm.
996      */

997     public final String JavaDoc getRealm()
998     {
999     return realm;
1000    }
1001
1002
1003    /**
1004     * Get the cookie
1005     *
1006     * @return the cookie String
1007     * @since V0.3-1
1008     */

1009    public final String JavaDoc getCookie()
1010    {
1011    return cookie;
1012    }
1013
1014
1015    /**
1016     * Set the cookie
1017     *
1018     * @param cookie the new cookie
1019     * @since V0.3-1
1020     */

1021    public final void setCookie(String JavaDoc cookie)
1022    {
1023    this.cookie = cookie;
1024    }
1025
1026
1027    /**
1028     * Get the authentication parameters.
1029     *
1030     * @return an array of name/value pairs.
1031     */

1032    public final NVPair[] getParams()
1033    {
1034    return Util.resizeArray(auth_params, auth_params.length);
1035    }
1036
1037
1038    /**
1039     * Set the authentication parameters.
1040     *
1041     * @param an array of name/value pairs.
1042     */

1043    public final void setParams(NVPair[] params)
1044    {
1045    if (params != null)
1046        auth_params = Util.resizeArray(params, params.length);
1047    else
1048        auth_params = new NVPair[0];
1049    }
1050
1051
1052    /**
1053     * Get the extra info.
1054     *
1055     * @return the extra_info object
1056     */

1057    public final Object JavaDoc getExtraInfo()
1058    {
1059    return extra_info;
1060    }
1061
1062
1063    /**
1064     * Set the extra info.
1065     *
1066     * @param info the extra info
1067     */

1068    public final void setExtraInfo(Object JavaDoc info)
1069    {
1070    extra_info = info;
1071    }
1072
1073
1074    /**
1075     * Constructs a string containing the authorization info. The format
1076     * is that of the http Authorization header.
1077     *
1078     * @return a String containing all info.
1079     */

1080    public String JavaDoc toString()
1081    {
1082    StringBuffer JavaDoc field = new StringBuffer JavaDoc(100);
1083
1084    field.append(scheme);
1085    field.append(" ");
1086
1087    if (cookie != null)
1088    {
1089        field.append(cookie);
1090    }
1091    else
1092    {
1093        if (realm.length() > 0)
1094        {
1095        field.append("realm=\"");
1096        field.append(Util.quoteString(realm, "\\\""));
1097        field.append('"');
1098        }
1099
1100        for (int idx=0; idx<auth_params.length; idx++)
1101        {
1102        field.append(',');
1103        field.append(auth_params[idx].getName());
1104        field.append("=\"");
1105        field.append(
1106            Util.quoteString(auth_params[idx].getValue(), "\\\""));
1107        field.append('"');
1108        }
1109    }
1110
1111    return field.toString();
1112    }
1113
1114
1115    /**
1116     * Produces a hash code based on host, scheme and realm. Port is not
1117     * included for simplicity (and because it probably won't make much
1118     * difference). Used in the AuthorizationInfo.AuthList hash table.
1119     *
1120     * @return the hash code
1121     */

1122    public int hashCode()
1123    {
1124    return (host+scheme.toLowerCase()+realm).hashCode();
1125    }
1126
1127    /**
1128     * Two AuthorizationInfos are considered equal if their host, port,
1129     * scheme and realm match. Used in the AuthorizationInfo.AuthList hash
1130     * table.
1131     *
1132     * @param obj another AuthorizationInfo against which this one is
1133     * to be compared.
1134     * @return true if they match in the above mentioned fields; false
1135     * otherwise.
1136     */

1137    public boolean equals(Object JavaDoc obj)
1138    {
1139    if ((obj != null) && (obj instanceof AuthorizationInfo))
1140    {
1141        AuthorizationInfo auth = (AuthorizationInfo) obj;
1142        if (host.equals(auth.host) &&
1143        (port == auth.port) &&
1144        scheme.equalsIgnoreCase(auth.scheme) &&
1145        realm.equals(auth.realm))
1146            return true;
1147    }
1148    return false;
1149    }
1150
1151
1152    /**
1153     * @return a clone of this AuthorizationInfo using a deep copy
1154     */

1155    public Object JavaDoc clone()
1156    {
1157    AuthorizationInfo ai;
1158    try
1159    {
1160        ai = (AuthorizationInfo) super.clone();
1161        ai.auth_params = Util.resizeArray(auth_params, auth_params.length);
1162        try
1163        {
1164        // ai.extra_info = extra_info.clone();
1165
ai.extra_info = extra_info.getClass().getMethod("clone", null).
1166                invoke(extra_info, null);
1167        }
1168        catch (Throwable JavaDoc t)
1169        { }
1170        ai.paths = new String JavaDoc[paths.length];
1171        System.arraycopy(paths, 0, ai.paths, 0, paths.length);
1172    }
1173    catch (CloneNotSupportedException JavaDoc cnse)
1174        { throw new InternalError JavaDoc(cnse.toString()); /* shouldn't happen */ }
1175
1176    return ai;
1177    }
1178}
1179
1180
Popular Tags