1 /* CorbalocParser.java -- handles corbaname: urls
2    Copyright (C) 2006 Free Software Foundation, Inc.
3 
4 This file is part of GNU Classpath.
5 
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10 
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20 
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25 
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37 
38 package gnu.javax.naming.giop;
39 
40 import gnu.CORBA.IOR;
41 import gnu.CORBA.Minor;
42 import gnu.CORBA.Unexpected;
43 import gnu.CORBA.Version;
44 import gnu.CORBA.NamingService.NameTransformer;
45 
46 import gnu.java.lang.CPStringBuilder;
47 
48 import java.io.File;
49 import java.io.FileReader;
50 import java.io.IOException;
51 import java.io.InputStreamReader;
52 import java.io.UnsupportedEncodingException;
53 import java.net.MalformedURLException;
54 import java.net.URL;
55 import java.net.URLDecoder;
56 import java.util.StringTokenizer;
57 
58 import javax.naming.InvalidNameException;
59 
60 import org.omg.CORBA.BAD_PARAM;
61 import org.omg.CORBA.DATA_CONVERSION;
62 import org.omg.CORBA.ORB;
63 import org.omg.CORBA.Object;
64 import org.omg.CORBA.ORBPackage.InvalidName;
65 
66 /**
67  * Parses the alternative IOR representations into our IOR structure.
68  *
69  * TODO This parser currently supports only one address per target string. A
70  * string with the multiple addresses will be accepted, but only the last
71  * address will be taken into consideration. The fault tolerance is not yet
72  * implemented.
73  *
74  * The key string is filtered using {@link java.net.URLDecoder} that replaces
75  * the agreed escape sequences by the corresponding non alphanumeric characters.
76  *
77  * @author Audrius Meskauskas (AudriusA@Bioinformatics.org)
78  */
79 public class CorbalocParser
80   extends NameTransformer
81 {
82   /**
83    * The corbaloc prefix.
84    */
85   public static final String pxCORBALOC = "corbaloc";
86 
87   /**
88    * The corbaname prefix.
89    */
90   public static final String pxCORBANAME = "corbaname";
91 
92   /**
93    * The IOR prefix.
94    */
95   public static final String pxIOR = "ior";
96 
97   /**
98    * The file:// prefix.
99    */
100   public static final String pxFILE = "file://";
101 
102   /**
103    * The ftp:// prefix.
104    */
105   public static final String pxFTP = "ftp://";
106 
107   /**
108    * The http:// prefix.
109    */
110   public static final String pxHTTP = "http://";
111 
112   /**
113    * Marks iiop protocol.
114    */
115   public static final String IIOP = "iiop";
116 
117   /**
118    * Marks rir protocol.
119    */
120   public static final String RIR = "rir";
121 
122   /**
123    * The default port value, as specified in OMG documentation.
124    */
125   public static final int DEFAULT_PORT = 2809;
126 
127   /**
128    * The default name.
129    */
130   public static final String DEFAULT_NAME = "NameService";
131 
132   /**
133    * The string to name converter, initialized on demand.
134    */
135   static NameTransformer converter;
136 
137   /**
138    * The current position.
139    */
140   int p;
141 
142   /**
143    * The address being parsed, splitted into tokens.
144    */
145   String[] t;
146 
147   /**
148    * Parse CORBALOC.
149    *
150    * The expected format is: <br>
151    * 1. corbaloc:[iiop][version.subversion@]:host[:port]/key <br>
152    * 2. corbaloc:rir:[/key] <br>
153    * 3. corbaname:[iiop][version.subversion@]:host[:port]/key <br>
154    * 4. corbaname:rir:[/key] <br>
155    * 5. file://[file name]<br>
156    * 6. http://[url]<br>
157    * 7. ftp://[url]<br>
158    *
159    * Protocol defaults to IOP, the object key defaults to the NameService.
160    *
161    * @param corbaloc the string to parse.
162    * @param orb the ORB, needed to create IORs and resolve rir references.
163    *
164    * @return the arrey of strings, first member being the IOR of the
165    * naming service, second member the name in the naming service.
166    */
corbaloc(String corbaloc, ORB orb)167   public synchronized String[] corbaloc(String corbaloc,
168     ORB orb)
169     throws InvalidNameException
170   {
171     return corbaloc(corbaloc, orb, 0);
172   }
173 
174   /**
175    * Parse controlling against the infinite recursion loop.
176    */
corbaloc(String corbaloc, ORB orb, int recursion)177   private String[] corbaloc(String corbaloc,
178     ORB orb, int recursion) throws InvalidNameException
179   {
180     // The used CORBA specification does not state how many times we should to
181     //redirect, but the infinite loop may be used to knock out the system.
182     // by malicious attempt.
183     if (recursion > 10)
184       throw new DATA_CONVERSION("More than 10 redirections");
185 
186     if (corbaloc.startsWith(pxFILE))
187       return corbaloc(readFile(corbaloc.substring(pxFILE.length())), orb, recursion+1);
188     else if (corbaloc.startsWith(pxHTTP))
189       return corbaloc(readUrl(corbaloc), orb, recursion+1);
190     else if (corbaloc.startsWith(pxFTP))
191       return corbaloc(readUrl(corbaloc), orb, recursion+1);
192 
193     // The version numbers with default values.
194     int major = 1;
195     int minor = 0;
196 
197     // The host address.
198     String host;
199 
200     // The port.
201     int port = DEFAULT_PORT;
202 
203     // The object key as string.
204     String key;
205 
206     StringTokenizer st = new StringTokenizer(corbaloc, ":@/.,#", true);
207 
208     t = new String[st.countTokens()];
209 
210     for (int i = 0; i < t.length; i++)
211       {
212         t[i] = st.nextToken();
213       }
214 
215     p = 0;
216 
217     if (!t[p].startsWith(pxCORBANAME))
218       throw new InvalidNameException(corbaloc+" must start with "+pxCORBANAME);
219 
220     p++;
221 
222     if (!t[p++].equals(":"))
223       throw new BAD_PARAM("Syntax (':' expected after name prefix)");
224 
225     // Check for rir:
226     if (t[p].equals(RIR))
227       {
228         p++;
229         if (!t[p++].equals(":"))
230           throw new BAD_PARAM("':' expected after 'rir'");
231 
232         key = readKey("/");
233 
234         Object object;
235         try
236           {
237             object = orb.resolve_initial_references(key);
238             return resolve(orb.object_to_string(object));
239           }
240         catch (InvalidName e)
241           {
242             throw new BAD_PARAM("Unknown initial reference '" + key + "'");
243           }
244       }
245     else
246     // Check for iiop.
247     if (t[p].equals(IIOP) || t[p].equals(":"))
248       {
249         IOR ior = new IOR();
250 
251         Addresses: do
252           { // Read addresses.
253             if (t[p].equals(":"))
254               {
255                 p++;
256               }
257             else
258               {
259                 p++;
260                 if (!t[p++].equals(":"))
261                   throw new BAD_PARAM("':' expected after 'iiop'");
262                 // Check if version is present.
263                 if (t[p + 1].equals("."))
264                   if (t[p + 3].equals("@"))
265                     {
266                       // Version info present.
267                       try
268                         {
269                           major = Integer.parseInt(t[p++]);
270                         }
271                       catch (NumberFormatException e)
272                         {
273                           throw new BAD_PARAM("Major version number '"
274                             + t[p - 1] + "'");
275                         }
276                       p++; // '.' at this point.
277                       try
278                         {
279                           minor = Integer.parseInt(t[p++]);
280                         }
281                       catch (NumberFormatException e)
282                         {
283                           throw new BAD_PARAM("Major version number '"
284                             + t[p - 1] + "'");
285                         }
286                       p++; // '@' at this point.
287                     }
288               }
289 
290             ior.Internet.version = new Version(major, minor);
291 
292             // Then host data goes till '/' or ':'.
293             CPStringBuilder bhost = new CPStringBuilder(corbaloc.length());
294             while (!t[p].equals(":") && !t[p].equals("/") && !t[p].equals(","))
295               bhost.append(t[p++]);
296 
297             host = bhost.toString();
298 
299             ior.Internet.host = host;
300 
301             if (t[p].equals(":"))
302               {
303                 // Port specified.
304                 p++;
305                 try
306                   {
307                     port = Integer.parseInt(t[p++]);
308                   }
309                 catch (NumberFormatException e)
310                   {
311                     throw new BAD_PARAM("Invalid port '" + t[p - 1] + "'");
312                   }
313               }
314 
315             ior.Internet.port = port;
316 
317             // Id is not listed.
318             ior.Id = "";
319 
320             if (t[p].equals(","))
321               p++;
322             else
323               break Addresses;
324           }
325         while (true);
326 
327         key = readKey("/");
328         ior.key = key.getBytes();
329 
330         return resolve(ior.toStringifiedReference());
331       }
332 
333     else
334       throw new InvalidNameException("Unsupported protocol '" + t[p] +
335                                      "' (iiop expected)");
336   }
337 
338   /**
339    * Read IOR from the file in the local file system.
340    */
readFile(String file)341   String readFile(String file)
342   {
343     File f = new File(file);
344     if (!f.exists())
345       {
346         DATA_CONVERSION err = new DATA_CONVERSION(f.getAbsolutePath()
347           + " does not exist.");
348         err.minor = Minor.Missing_IOR;
349       }
350     try
351       {
352         char[] c = new char[(int) f.length()];
353         FileReader fr = new FileReader(f);
354         fr.read(c);
355         fr.close();
356         return new String(c).trim();
357       }
358     catch (IOException ex)
359       {
360         DATA_CONVERSION d = new DATA_CONVERSION();
361         d.initCause(ex);
362         d.minor = Minor.Missing_IOR;
363         throw (d);
364       }
365   }
366 
367   /**
368    * Read IOR from the remote URL.
369    */
readUrl(String url)370   String readUrl(String url)
371   {
372     URL u;
373     try
374       {
375         u = new URL(url);
376       }
377     catch (MalformedURLException mex)
378       {
379         throw new BAD_PARAM("Malformed URL: '" + url + "'");
380       }
381 
382     try
383       {
384         InputStreamReader r = new InputStreamReader(u.openStream());
385 
386         CPStringBuilder b = new CPStringBuilder();
387         int c;
388 
389         while ((c = r.read()) > 0)
390           b.append((char) c);
391 
392         return b.toString().trim();
393       }
394     catch (Exception exc)
395       {
396         DATA_CONVERSION d = new DATA_CONVERSION("Reading " + url + " failed.");
397         d.minor = Minor.Missing_IOR;
398         throw d;
399       }
400   }
401 
resolve(String nsIor)402   private String[] resolve(String nsIor)
403   {
404     String [] n = new String[2];
405     n[0] = nsIor;
406     n[1] = readKey("#");
407     return n;
408   }
409 
readKey(String delimiter)410   private String readKey(String delimiter)
411     throws BAD_PARAM
412   {
413     if (p < t.length)
414       if (!t[p].equals(delimiter))
415         {
416           if (t[p].equals("#"))
417             return DEFAULT_NAME;
418           else
419             throw new BAD_PARAM("'" + delimiter + "String' expected '" + t[p]
420               + "' found");
421         }
422 
423     CPStringBuilder bKey = new CPStringBuilder();
424     p++;
425 
426     while (p < t.length && !t[p].equals("#"))
427       bKey.append(t[p++]);
428 
429     if (bKey.length() == 0)
430       return DEFAULT_NAME;
431 
432     try
433       {
434         return URLDecoder.decode(bKey.toString(), "UTF-8");
435       }
436     catch (UnsupportedEncodingException e)
437       {
438         throw new Unexpected("URLDecoder does not support UTF-8", e);
439       }
440   }
441 }
442