1 /*
   2  * Copyright (c) 1997, 2017, 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 
  26 
  27 package javax.activation;
  28 
  29 import java.util.*;
  30 import java.io.*;
  31 import java.net.*;
  32 import java.security.AccessController;
  33 import java.security.PrivilegedAction;
  34 import com.sun.activation.registries.MailcapFile;
  35 import com.sun.activation.registries.LogSupport;
  36 
  37 /**
  38  * MailcapCommandMap extends the CommandMap
  39  * abstract class. It implements a CommandMap whose configuration
  40  * is based on mailcap files
  41  * (<A HREF="http://www.ietf.org/rfc/rfc1524.txt">RFC 1524</A>).
  42  * The MailcapCommandMap can be configured both programmatically
  43  * and via configuration files.
  44  * <p>
  45  * <b>Mailcap file search order:</b><p>
  46  * The MailcapCommandMap looks in various places in the user's
  47  * system for mailcap file entries. When requests are made
  48  * to search for commands in the MailcapCommandMap, it searches
  49  * mailcap files in the following order:
  50  * <ol>
  51  * <li> Programatically added entries to the MailcapCommandMap instance.
  52  * <li> The file {@code .mailcap} in the user's home directory.
  53  * <li> The file {@code mailcap} in the Java runtime.
  54  * <li> The file or resources named {@code META-INF/mailcap}.
  55  * <li> The file or resource named {@code META-INF/mailcap.default}
  56  * (usually found only in the {@code activation.jar} file).
  57  * </ol>
  58  * <p>
  59  * (The current implementation looks for the {@code mailcap} file
  60  * in the Java runtime in the directory <i>java.home</i>{@code /conf}
  61  * if it exists, and otherwise in the directory
  62  * <i>java.home</i>{@code /lib}, where <i>java.home</i> is the value
  63  * of the "java.home" System property.  Note that the "conf" directory was
  64  * introduced in JDK 9.)
  65  * <p>
  66  * <b>Mailcap file format:</b><p>
  67  *
  68  * Mailcap files must conform to the mailcap
  69  * file specification (RFC 1524, <i>A User Agent Configuration Mechanism
  70  * For Multimedia Mail Format Information</i>).
  71  * The file format consists of entries corresponding to
  72  * particular MIME types. In general, the specification
  73  * specifies <i>applications</i> for clients to use when they
  74  * themselves cannot operate on the specified MIME type. The
  75  * MailcapCommandMap extends this specification by using a parameter mechanism
  76  * in mailcap files that allows JavaBeans(tm) components to be specified as
  77  * corresponding to particular commands for a MIME type.<p>
  78  *
  79  * When a mailcap file is
  80  * parsed, the MailcapCommandMap recognizes certain parameter signatures,
  81  * specifically those parameter names that begin with {@code x-java-}.
  82  * The MailcapCommandMap uses this signature to find
  83  * command entries for inclusion into its registries.
  84  * Parameter names with the form {@code x-java-<name>}
  85  * are read by the MailcapCommandMap as identifying a command
  86  * with the name <i>name</i>. When the <i>name</i> is {@code
  87  * content-handler} the MailcapCommandMap recognizes the class
  88  * signified by this parameter as a <i>DataContentHandler</i>.
  89  * All other commands are handled generically regardless of command
  90  * name. The command implementation is specified by a fully qualified
  91  * class name of a JavaBean(tm) component. For example; a command for viewing
  92  * some data can be specified as: {@code x-java-view=com.foo.ViewBean}.<p>
  93  *
  94  * When the command name is {@code fallback-entry}, the value of
  95  * the command may be {@code true} or {@code false}.  An
  96  * entry for a MIME type that includes a parameter of
  97  * {@code x-java-fallback-entry=true} defines fallback commands
  98  * for that MIME type that will only be used if no non-fallback entry
  99  * can be found.  For example, an entry of the form {@code text/*; ;
 100  * x-java-fallback-entry=true; x-java-view=com.sun.TextViewer}
 101  * specifies a view command to be used for any text MIME type.  This
 102  * view command would only be used if a non-fallback view command for
 103  * the MIME type could not be found.<p>
 104  *
 105  * MailcapCommandMap aware mailcap files have the
 106  * following general form:
 107  * <pre>{@code
 108  * # Comments begin with a '#' and continue to the end of the line.
 109  * <mime type>; ; <parameter list>
 110  * # Where a parameter list consists of one or more parameters,
 111  * # where parameters look like: x-java-view=com.sun.TextViewer
 112  * # and a parameter list looks like:
 113  * text/plain; ; x-java-view=com.sun.TextViewer; x-java-edit=com.sun.TextEdit
 114  * # Note that mailcap entries that do not contain 'x-java' parameters
 115  * # and comply to RFC 1524 are simply ignored:
 116  * image/gif; /usr/dt/bin/sdtimage %s
 117  * }</pre>
 118  *
 119  * @author Bart Calder
 120  * @author Bill Shannon
 121  *
 122  * @since 1.6
 123  */
 124 
 125 public class MailcapCommandMap extends CommandMap {
 126     /*
 127      * We manage a collection of databases, searched in order.
 128      */
 129     private MailcapFile[] DB;
 130     private static final int PROG = 0;  // programmatically added entries
 131 
 132     private static final String confDir;
 133 
 134     static {
 135         String dir = null;
 136         try {
 137             dir = (String)AccessController.doPrivileged(
 138                 new PrivilegedAction() {
 139                     public Object run() {
 140                         String home = System.getProperty("java.home");
 141                         String newdir = home + File.separator + "conf";
 142                         File conf = new File(newdir);
 143                         if (conf.exists())
 144                             return newdir + File.separator;
 145                         else
 146                             return home + File.separator + "lib" + File.separator;
 147                     }
 148                 });
 149         } catch (Exception ex) {
 150             // ignore any exceptions
 151         }
 152         confDir = dir;
 153     }
 154 
 155     /**
 156      * The default Constructor.
 157      */
 158     public MailcapCommandMap() {
 159         super();
 160         List dbv = new ArrayList(5);    // usually 5 or less databases
 161         MailcapFile mf = null;
 162         dbv.add(null);          // place holder for PROG entry
 163 
 164         LogSupport.log("MailcapCommandMap: load HOME");
 165         try {
 166             String user_home = System.getProperty("user.home");
 167 
 168             if (user_home != null) {
 169                 String path = user_home + File.separator + ".mailcap";
 170                 mf = loadFile(path);
 171                 if (mf != null)
 172                     dbv.add(mf);
 173             }
 174         } catch (SecurityException ex) {}
 175 
 176         LogSupport.log("MailcapCommandMap: load SYS");
 177         try {
 178             // check system's home
 179             if (confDir != null) {
 180                 mf = loadFile(confDir + "mailcap");
 181                 if (mf != null)
 182                     dbv.add(mf);
 183             }
 184         } catch (SecurityException ex) {}
 185 
 186         LogSupport.log("MailcapCommandMap: load JAR");
 187         // load from the app's jar file
 188         loadAllResources(dbv, "META-INF/mailcap");
 189 
 190         LogSupport.log("MailcapCommandMap: load DEF");
 191         mf = loadResource("/META-INF/mailcap.default");
 192 
 193         if (mf != null)
 194             dbv.add(mf);
 195 
 196         DB = new MailcapFile[dbv.size()];
 197         DB = (MailcapFile[])dbv.toArray(DB);
 198     }
 199 
 200     /**
 201      * Load from the named resource.
 202      */
 203     private MailcapFile loadResource(String name) {
 204         InputStream clis = null;
 205         try {
 206             clis = SecuritySupport.getResourceAsStream(this.getClass(), name);
 207             if (clis != null) {
 208                 MailcapFile mf = new MailcapFile(clis);
 209                 if (LogSupport.isLoggable())
 210                     LogSupport.log("MailcapCommandMap: successfully loaded " +
 211                         "mailcap file: " + name);
 212                 return mf;
 213             } else {
 214                 if (LogSupport.isLoggable())
 215                     LogSupport.log("MailcapCommandMap: not loading " +
 216                         "mailcap file: " + name);
 217             }
 218         } catch (IOException e) {
 219             if (LogSupport.isLoggable())
 220                 LogSupport.log("MailcapCommandMap: can't load " + name, e);
 221         } catch (SecurityException sex) {
 222             if (LogSupport.isLoggable())
 223                 LogSupport.log("MailcapCommandMap: can't load " + name, sex);
 224         } finally {
 225             try {
 226                 if (clis != null)
 227                     clis.close();
 228             } catch (IOException ex) { }        // ignore it
 229         }
 230         return null;
 231     }
 232 
 233     /**
 234      * Load all of the named resource.
 235      */
 236     private void loadAllResources(List v, String name) {
 237         boolean anyLoaded = false;
 238         try {
 239             URL[] urls;
 240             ClassLoader cld = null;
 241             // First try the "application's" class loader.
 242             cld = SecuritySupport.getContextClassLoader();
 243             if (cld == null)
 244                 cld = this.getClass().getClassLoader();
 245             if (cld != null)
 246                 urls = SecuritySupport.getResources(cld, name);
 247             else
 248                 urls = SecuritySupport.getSystemResources(name);
 249             if (urls != null) {
 250                 if (LogSupport.isLoggable())
 251                     LogSupport.log("MailcapCommandMap: getResources");
 252                 for (int i = 0; i < urls.length; i++) {
 253                     URL url = urls[i];
 254                     InputStream clis = null;
 255                     if (LogSupport.isLoggable())
 256                         LogSupport.log("MailcapCommandMap: URL " + url);
 257                     try {
 258                         clis = SecuritySupport.openStream(url);
 259                         if (clis != null) {
 260                             v.add(new MailcapFile(clis));
 261                             anyLoaded = true;
 262                             if (LogSupport.isLoggable())
 263                                 LogSupport.log("MailcapCommandMap: " +
 264                                     "successfully loaded " +
 265                                     "mailcap file from URL: " +
 266                                     url);
 267                         } else {
 268                             if (LogSupport.isLoggable())
 269                                 LogSupport.log("MailcapCommandMap: " +
 270                                     "not loading mailcap " +
 271                                     "file from URL: " + url);
 272                         }
 273                     } catch (IOException ioex) {
 274                         if (LogSupport.isLoggable())
 275                             LogSupport.log("MailcapCommandMap: can't load " +
 276                                                 url, ioex);
 277                     } catch (SecurityException sex) {
 278                         if (LogSupport.isLoggable())
 279                             LogSupport.log("MailcapCommandMap: can't load " +
 280                                                 url, sex);
 281                     } finally {
 282                         try {
 283                             if (clis != null)
 284                                 clis.close();
 285                         } catch (IOException cex) { }
 286                     }
 287                 }
 288             }
 289         } catch (Exception ex) {
 290             if (LogSupport.isLoggable())
 291                 LogSupport.log("MailcapCommandMap: can't load " + name, ex);
 292         }
 293 
 294         // if failed to load anything, fall back to old technique, just in case
 295         if (!anyLoaded) {
 296             if (LogSupport.isLoggable())
 297                 LogSupport.log("MailcapCommandMap: !anyLoaded");
 298             MailcapFile mf = loadResource("/" + name);
 299             if (mf != null)
 300                 v.add(mf);
 301         }
 302     }
 303 
 304     /**
 305      * Load from the named file.
 306      */
 307     private MailcapFile loadFile(String name) {
 308         MailcapFile mtf = null;
 309 
 310         try {
 311             mtf = new MailcapFile(name);
 312         } catch (IOException e) {
 313             //  e.printStackTrace();
 314         }
 315         return mtf;
 316     }
 317 
 318     /**
 319      * Constructor that allows the caller to specify the path
 320      * of a <i>mailcap</i> file.
 321      *
 322      * @param fileName The name of the <i>mailcap</i> file to open
 323      * @exception       IOException     if the file can't be accessed
 324      */
 325     public MailcapCommandMap(String fileName) throws IOException {
 326         this();
 327 
 328         if (LogSupport.isLoggable())
 329             LogSupport.log("MailcapCommandMap: load PROG from " + fileName);
 330         if (DB[PROG] == null) {
 331             DB[PROG] = new MailcapFile(fileName);
 332         }
 333     }
 334 
 335 
 336     /**
 337      * Constructor that allows the caller to specify an <i>InputStream</i>
 338      * containing a mailcap file.
 339      *
 340      * @param is        InputStream of the <i>mailcap</i> file to open
 341      */
 342     public MailcapCommandMap(InputStream is) {
 343         this();
 344 
 345         LogSupport.log("MailcapCommandMap: load PROG");
 346         if (DB[PROG] == null) {
 347             try {
 348                 DB[PROG] = new MailcapFile(is);
 349             } catch (IOException ex) {
 350                 // XXX - should throw it
 351             }
 352         }
 353     }
 354 
 355     /**
 356      * Get the preferred command list for a MIME Type. The MailcapCommandMap
 357      * searches the mailcap files as described above under
 358      * <i>Mailcap file search order</i>.<p>
 359      *
 360      * The result of the search is a proper subset of available
 361      * commands in all mailcap files known to this instance of
 362      * MailcapCommandMap.  The first entry for a particular command
 363      * is considered the preferred command.
 364      *
 365      * @param mimeType  the MIME type
 366      * @return the CommandInfo objects representing the preferred commands.
 367      */
 368     public synchronized CommandInfo[] getPreferredCommands(String mimeType) {
 369         List cmdList = new ArrayList();
 370         if (mimeType != null)
 371             mimeType = mimeType.toLowerCase(Locale.ENGLISH);
 372 
 373         for (int i = 0; i < DB.length; i++) {
 374             if (DB[i] == null)
 375                 continue;
 376             Map cmdMap = DB[i].getMailcapList(mimeType);
 377             if (cmdMap != null)
 378                 appendPrefCmdsToList(cmdMap, cmdList);
 379         }
 380 
 381         // now add the fallback commands
 382         for (int i = 0; i < DB.length; i++) {
 383             if (DB[i] == null)
 384                 continue;
 385             Map cmdMap = DB[i].getMailcapFallbackList(mimeType);
 386             if (cmdMap != null)
 387                 appendPrefCmdsToList(cmdMap, cmdList);
 388         }
 389 
 390         CommandInfo[] cmdInfos = new CommandInfo[cmdList.size()];
 391         cmdInfos = (CommandInfo[])cmdList.toArray(cmdInfos);
 392 
 393         return cmdInfos;
 394     }
 395 
 396     /**
 397      * Put the commands that are in the hash table, into the list.
 398      */
 399     private void appendPrefCmdsToList(Map cmdHash, List cmdList) {
 400         Iterator verb_enum = cmdHash.keySet().iterator();
 401 
 402         while (verb_enum.hasNext()) {
 403             String verb = (String)verb_enum.next();
 404             if (!checkForVerb(cmdList, verb)) {
 405                 List cmdList2 = (List)cmdHash.get(verb); // get the list
 406                 String className = (String)cmdList2.get(0);
 407                 cmdList.add(new CommandInfo(verb, className));
 408             }
 409         }
 410     }
 411 
 412     /**
 413      * Check the cmdList to see if this command exists, return
 414      * true if the verb is there.
 415      */
 416     private boolean checkForVerb(List cmdList, String verb) {
 417         Iterator ee = cmdList.iterator();
 418         while (ee.hasNext()) {
 419             String enum_verb =
 420                 (String)((CommandInfo)ee.next()).getCommandName();
 421             if (enum_verb.equals(verb))
 422                 return true;
 423         }
 424         return false;
 425     }
 426 
 427     /**
 428      * Get all the available commands in all mailcap files known to
 429      * this instance of MailcapCommandMap for this MIME type.
 430      *
 431      * @param mimeType  the MIME type
 432      * @return the CommandInfo objects representing all the commands.
 433      */
 434     public synchronized CommandInfo[] getAllCommands(String mimeType) {
 435         List cmdList = new ArrayList();
 436         if (mimeType != null)
 437             mimeType = mimeType.toLowerCase(Locale.ENGLISH);
 438 
 439         for (int i = 0; i < DB.length; i++) {
 440             if (DB[i] == null)
 441                 continue;
 442             Map cmdMap = DB[i].getMailcapList(mimeType);
 443             if (cmdMap != null)
 444                 appendCmdsToList(cmdMap, cmdList);
 445         }
 446 
 447         // now add the fallback commands
 448         for (int i = 0; i < DB.length; i++) {
 449             if (DB[i] == null)
 450                 continue;
 451             Map cmdMap = DB[i].getMailcapFallbackList(mimeType);
 452             if (cmdMap != null)
 453                 appendCmdsToList(cmdMap, cmdList);
 454         }
 455 
 456         CommandInfo[] cmdInfos = new CommandInfo[cmdList.size()];
 457         cmdInfos = (CommandInfo[])cmdList.toArray(cmdInfos);
 458 
 459         return cmdInfos;
 460     }
 461 
 462     /**
 463      * Put the commands that are in the hash table, into the list.
 464      */
 465     private void appendCmdsToList(Map typeHash, List cmdList) {
 466         Iterator verb_enum = typeHash.keySet().iterator();
 467 
 468         while (verb_enum.hasNext()) {
 469             String verb = (String)verb_enum.next();
 470             List cmdList2 = (List)typeHash.get(verb);
 471             Iterator cmd_enum = ((List)cmdList2).iterator();
 472 
 473             while (cmd_enum.hasNext()) {
 474                 String cmd = (String)cmd_enum.next();
 475                 cmdList.add(new CommandInfo(verb, cmd));
 476                 // cmdList.add(0, new CommandInfo(verb, cmd));
 477             }
 478         }
 479     }
 480 
 481     /**
 482      * Get the command corresponding to {@code cmdName} for the MIME type.
 483      *
 484      * @param mimeType  the MIME type
 485      * @param cmdName   the command name
 486      * @return the CommandInfo object corresponding to the command.
 487      */
 488     public synchronized CommandInfo getCommand(String mimeType,
 489                                                         String cmdName) {
 490         if (mimeType != null)
 491             mimeType = mimeType.toLowerCase(Locale.ENGLISH);
 492 
 493         for (int i = 0; i < DB.length; i++) {
 494             if (DB[i] == null)
 495                 continue;
 496             Map cmdMap = DB[i].getMailcapList(mimeType);
 497             if (cmdMap != null) {
 498                 // get the cmd list for the cmd
 499                 List v = (List)cmdMap.get(cmdName);
 500                 if (v != null) {
 501                     String cmdClassName = (String)v.get(0);
 502 
 503                     if (cmdClassName != null)
 504                         return new CommandInfo(cmdName, cmdClassName);
 505                 }
 506             }
 507         }
 508 
 509         // now try the fallback list
 510         for (int i = 0; i < DB.length; i++) {
 511             if (DB[i] == null)
 512                 continue;
 513             Map cmdMap = DB[i].getMailcapFallbackList(mimeType);
 514             if (cmdMap != null) {
 515                 // get the cmd list for the cmd
 516                 List v = (List)cmdMap.get(cmdName);
 517                 if (v != null) {
 518                     String cmdClassName = (String)v.get(0);
 519 
 520                     if (cmdClassName != null)
 521                         return new CommandInfo(cmdName, cmdClassName);
 522                 }
 523             }
 524         }
 525         return null;
 526     }
 527 
 528     /**
 529      * Add entries to the registry.  Programmatically
 530      * added entries are searched before other entries.<p>
 531      *
 532      * The string that is passed in should be in mailcap
 533      * format.
 534      *
 535      * @param mail_cap a correctly formatted mailcap string
 536      */
 537     public synchronized void addMailcap(String mail_cap) {
 538         // check to see if one exists
 539         LogSupport.log("MailcapCommandMap: add to PROG");
 540         if (DB[PROG] == null)
 541             DB[PROG] = new MailcapFile();
 542 
 543         DB[PROG].appendToMailcap(mail_cap);
 544     }
 545 
 546     /**
 547      * Return the DataContentHandler for the specified MIME type.
 548      *
 549      * @param mimeType  the MIME type
 550      * @return          the DataContentHandler
 551      */
 552     public synchronized DataContentHandler createDataContentHandler(
 553                                                         String mimeType) {
 554         if (LogSupport.isLoggable())
 555             LogSupport.log(
 556                 "MailcapCommandMap: createDataContentHandler for " + mimeType);
 557         if (mimeType != null)
 558             mimeType = mimeType.toLowerCase(Locale.ENGLISH);
 559 
 560         for (int i = 0; i < DB.length; i++) {
 561             if (DB[i] == null)
 562                 continue;
 563             if (LogSupport.isLoggable())
 564                 LogSupport.log("  search DB #" + i);
 565             Map cmdMap = DB[i].getMailcapList(mimeType);
 566             if (cmdMap != null) {
 567                 List v = (List)cmdMap.get("content-handler");
 568                 if (v != null) {
 569                     String name = (String)v.get(0);
 570                     DataContentHandler dch = getDataContentHandler(name);
 571                     if (dch != null)
 572                         return dch;
 573                 }
 574             }
 575         }
 576 
 577         // now try the fallback entries
 578         for (int i = 0; i < DB.length; i++) {
 579             if (DB[i] == null)
 580                 continue;
 581             if (LogSupport.isLoggable())
 582                 LogSupport.log("  search fallback DB #" + i);
 583             Map cmdMap = DB[i].getMailcapFallbackList(mimeType);
 584             if (cmdMap != null) {
 585                 List v = (List)cmdMap.get("content-handler");
 586                 if (v != null) {
 587                     String name = (String)v.get(0);
 588                     DataContentHandler dch = getDataContentHandler(name);
 589                     if (dch != null)
 590                         return dch;
 591                 }
 592             }
 593         }
 594         return null;
 595     }
 596 
 597     private DataContentHandler getDataContentHandler(String name) {
 598         if (LogSupport.isLoggable())
 599             LogSupport.log("    got content-handler");
 600         if (LogSupport.isLoggable())
 601             LogSupport.log("      class " + name);
 602         try {
 603             ClassLoader cld = null;
 604             // First try the "application's" class loader.
 605             cld = SecuritySupport.getContextClassLoader();
 606             if (cld == null)
 607                 cld = this.getClass().getClassLoader();
 608             Class cl = null;
 609             try {
 610                 cl = cld.loadClass(name);
 611             } catch (Exception ex) {
 612                 // if anything goes wrong, do it the old way
 613                 cl = Class.forName(name);
 614             }
 615             return (DataContentHandler) cl.newInstance();
 616         } catch (IllegalAccessException e) {
 617             if (LogSupport.isLoggable())
 618                 LogSupport.log("Can't load DCH " + name, e);
 619         } catch (ClassNotFoundException e) {
 620             if (LogSupport.isLoggable())
 621                 LogSupport.log("Can't load DCH " + name, e);
 622         } catch (InstantiationException e) {
 623             if (LogSupport.isLoggable())
 624                 LogSupport.log("Can't load DCH " + name, e);
 625         }
 626         return null;
 627     }
 628 
 629     /**
 630      * Get all the MIME types known to this command map.
 631      *
 632      * @return          array of MIME types as strings
 633      * @since   1.6, JAF 1.1
 634      */
 635     public synchronized String[] getMimeTypes() {
 636         List mtList = new ArrayList();
 637 
 638         for (int i = 0; i < DB.length; i++) {
 639             if (DB[i] == null)
 640                 continue;
 641             String[] ts = DB[i].getMimeTypes();
 642             if (ts != null) {
 643                 for (int j = 0; j < ts.length; j++) {
 644                     // eliminate duplicates
 645                     if (!mtList.contains(ts[j]))
 646                         mtList.add(ts[j]);
 647                 }
 648             }
 649         }
 650 
 651         String[] mts = new String[mtList.size()];
 652         mts = (String[])mtList.toArray(mts);
 653 
 654         return mts;
 655     }
 656 
 657     /**
 658      * Get the native commands for the given MIME type.
 659      * Returns an array of strings where each string is
 660      * an entire mailcap file entry.  The application
 661      * will need to parse the entry to extract the actual
 662      * command as well as any attributes it needs. See
 663      * <A HREF="http://www.ietf.org/rfc/rfc1524.txt">RFC 1524</A>
 664      * for details of the mailcap entry syntax.  Only mailcap
 665      * entries that specify a view command for the specified
 666      * MIME type are returned.
 667      *
 668      * @param   mimeType        the MIME type
 669      * @return          array of native command entries
 670      * @since   1.6, JAF 1.1
 671      */
 672     public synchronized String[] getNativeCommands(String mimeType) {
 673         List cmdList = new ArrayList();
 674         if (mimeType != null)
 675             mimeType = mimeType.toLowerCase(Locale.ENGLISH);
 676 
 677         for (int i = 0; i < DB.length; i++) {
 678             if (DB[i] == null)
 679                 continue;
 680             String[] cmds = DB[i].getNativeCommands(mimeType);
 681             if (cmds != null) {
 682                 for (int j = 0; j < cmds.length; j++) {
 683                     // eliminate duplicates
 684                     if (!cmdList.contains(cmds[j]))
 685                         cmdList.add(cmds[j]);
 686                 }
 687             }
 688         }
 689 
 690         String[] cmds = new String[cmdList.size()];
 691         cmds = (String[])cmdList.toArray(cmds);
 692 
 693         return cmds;
 694     }
 695 
 696     /**
 697      * for debugging...
 698      *
 699     public static void main(String[] argv) throws Exception {
 700         MailcapCommandMap map = new MailcapCommandMap();
 701         CommandInfo[] cmdInfo;
 702 
 703         cmdInfo = map.getPreferredCommands(argv[0]);
 704         System.out.println("Preferred Commands:");
 705         for (int i = 0; i < cmdInfo.length; i++)
 706             System.out.println("Command " + cmdInfo[i].getCommandName() + " [" +
 707                                             cmdInfo[i].getCommandClass() + "]");
 708         cmdInfo = map.getAllCommands(argv[0]);
 709         System.out.println();
 710         System.out.println("All Commands:");
 711         for (int i = 0; i < cmdInfo.length; i++)
 712             System.out.println("Command " + cmdInfo[i].getCommandName() + " [" +
 713                                             cmdInfo[i].getCommandClass() + "]");
 714         DataContentHandler dch = map.createDataContentHandler(argv[0]);
 715         if (dch != null)
 716             System.out.println("DataContentHandler " +
 717                                                 dch.getClass().toString());
 718         System.exit(0);
 719     }
 720     */
 721 }