NetRC.java

  1. /*
  2.  * Copyright (C) 2014, Alexey Kuznetsov <axet@me.com>
  3.  *
  4.  * This program and the accompanying materials are made available
  5.  * under the terms of the Eclipse Distribution License v1.0 which
  6.  * accompanies this distribution, is reproduced below, and is
  7.  * available at http://www.eclipse.org/org/documents/edl-v10.php
  8.  *
  9.  * All rights reserved.
  10.  *
  11.  * Redistribution and use in source and binary forms, with or
  12.  * without modification, are permitted provided that the following
  13.  * conditions are met:
  14.  *
  15.  * - Redistributions of source code must retain the above copyright
  16.  *   notice, this list of conditions and the following disclaimer.
  17.  *
  18.  * - Redistributions in binary form must reproduce the above
  19.  *   copyright notice, this list of conditions and the following
  20.  *   disclaimer in the documentation and/or other materials provided
  21.  *   with the distribution.
  22.  *
  23.  * - Neither the name of the Eclipse Foundation, Inc. nor the
  24.  *   names of its contributors may be used to endorse or promote
  25.  *   products derived from this software without specific prior
  26.  *   written permission.
  27.  *
  28.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  29.  * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  30.  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  31.  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  32.  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  33.  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  34.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  35.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  36.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  37.  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  38.  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  39.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  40.  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  41.  */

  42. package org.eclipse.jgit.transport;

  43. import static java.nio.charset.StandardCharsets.UTF_8;

  44. import java.io.BufferedReader;
  45. import java.io.File;
  46. import java.io.FileInputStream;
  47. import java.io.IOException;
  48. import java.io.InputStreamReader;
  49. import java.time.Instant;
  50. import java.util.Collection;
  51. import java.util.HashMap;
  52. import java.util.Locale;
  53. import java.util.Map;
  54. import java.util.TreeMap;
  55. import java.util.regex.Matcher;
  56. import java.util.regex.Pattern;

  57. import org.eclipse.jgit.util.FS;

  58. /**
  59.  * NetRC file parser.
  60.  *
  61.  * @since 3.5
  62.  */
  63. public class NetRC {
  64.     static final Pattern NETRC = Pattern.compile("(\\S+)"); //$NON-NLS-1$

  65.     /**
  66.      * 'default' netrc entry. This is the same as machine name except that
  67.      * default matches any name. There can be only one default token, and it
  68.      * must be after all machine tokens.
  69.      */
  70.     static final String DEFAULT_ENTRY = "default"; //$NON-NLS-1$

  71.     /**
  72.      * .netrc file entry
  73.      */
  74.     public static class NetRCEntry {
  75.         /**
  76.          * login netrc entry
  77.          */
  78.         public String login;

  79.         /**
  80.          * password netrc entry
  81.          */
  82.         public char[] password;

  83.         /**
  84.          * machine netrc entry
  85.          */
  86.         public String machine;

  87.         /**
  88.          * account netrc entry
  89.          */
  90.         public String account;

  91.         /**
  92.          * macdef netrc entry. Defines a macro. This token functions like the
  93.          * ftp macdef command functions. A macro is defined with the specified
  94.          * name; its contents begins with the next .netrc line and continues
  95.          * until a null line (consecutive new-line characters) is encountered.
  96.          * If a macro named init is defined, it is automatically executed as the
  97.          * last step in the auto-login process.
  98.          */
  99.         public String macdef;

  100.         /**
  101.          * macro script body of macdef entry.
  102.          */
  103.         public String macbody;

  104.         /**
  105.          * Default constructor
  106.          */
  107.         public NetRCEntry() {
  108.         }

  109.         boolean complete() {
  110.             return login != null && password != null && machine != null;
  111.         }
  112.     }

  113.     private File netrc;

  114.     private Instant lastModified;

  115.     private Map<String, NetRCEntry> hosts = new HashMap<>();

  116.     private static final TreeMap<String, State> STATE = new TreeMap<>() {
  117.         private static final long serialVersionUID = -4285910831814853334L;
  118.         {
  119.             put("machine", State.MACHINE); //$NON-NLS-1$
  120.             put("login", State.LOGIN); //$NON-NLS-1$
  121.             put("password", State.PASSWORD); //$NON-NLS-1$
  122.             put(DEFAULT_ENTRY, State.DEFAULT);
  123.             put("account", State.ACCOUNT); //$NON-NLS-1$
  124.             put("macdef", State.MACDEF); //$NON-NLS-1$
  125.         }
  126.     };

  127.     enum State {
  128.         COMMAND, MACHINE, LOGIN, PASSWORD, DEFAULT, ACCOUNT, MACDEF
  129.     }

  130.     /**
  131.      * <p>Constructor for NetRC.</p>
  132.      */
  133.     public NetRC() {
  134.         netrc = getDefaultFile();
  135.         if (netrc != null)
  136.             parse();
  137.     }

  138.     /**
  139.      * <p>Constructor for NetRC.</p>
  140.      *
  141.      * @param netrc
  142.      *            the .netrc file
  143.      */
  144.     public NetRC(File netrc) {
  145.         this.netrc = netrc;
  146.         parse();
  147.     }

  148.     private static File getDefaultFile() {
  149.         File home = FS.DETECTED.userHome();
  150.         File netrc = new File(home, ".netrc"); //$NON-NLS-1$
  151.         if (netrc.exists())
  152.             return netrc;

  153.         netrc = new File(home, "_netrc"); //$NON-NLS-1$
  154.         if (netrc.exists())
  155.             return netrc;

  156.         return null;
  157.     }

  158.     /**
  159.      * Get entry by host name
  160.      *
  161.      * @param host
  162.      *            the host name
  163.      * @return entry associated with host name or null
  164.      */
  165.     public NetRCEntry getEntry(String host) {
  166.         if (netrc == null)
  167.             return null;

  168.         if (!this.lastModified
  169.                 .equals(FS.DETECTED.lastModifiedInstant(this.netrc))) {
  170.             parse();
  171.         }

  172.         NetRCEntry entry = this.hosts.get(host);

  173.         if (entry == null)
  174.             entry = this.hosts.get(DEFAULT_ENTRY);

  175.         return entry;
  176.     }

  177.     /**
  178.      * Get all entries collected from .netrc file
  179.      *
  180.      * @return all entries collected from .netrc file
  181.      */
  182.     public Collection<NetRCEntry> getEntries() {
  183.         return hosts.values();
  184.     }

  185.     private void parse() {
  186.         this.hosts.clear();
  187.         this.lastModified = FS.DETECTED.lastModifiedInstant(this.netrc);

  188.         try (BufferedReader r = new BufferedReader(
  189.                 new InputStreamReader(new FileInputStream(netrc), UTF_8))) {
  190.             String line = null;

  191.             NetRCEntry entry = new NetRCEntry();

  192.             State state = State.COMMAND;

  193.             String macbody = ""; //$NON-NLS-1$

  194.             Matcher matcher = NETRC.matcher(""); //$NON-NLS-1$
  195.             while ((line = r.readLine()) != null) {

  196.                 // reading macbody
  197.                 if (entry.macdef != null && entry.macbody == null) {
  198.                     if (line.length() == 0) {
  199.                         entry.macbody = macbody;
  200.                         macbody = ""; //$NON-NLS-1$
  201.                         continue;
  202.                     }
  203.                     macbody += line + "\n"; //$NON-NLS-1$;
  204.                     continue;
  205.                 }

  206.                 matcher.reset(line);
  207.                 while (matcher.find()) {
  208.                     String command = matcher.group().toLowerCase(Locale.ROOT);
  209.                     if (command.startsWith("#")) { //$NON-NLS-1$
  210.                         matcher.reset(""); //$NON-NLS-1$
  211.                         continue;
  212.                     }
  213.                     state = STATE.get(command);
  214.                     if (state == null)
  215.                         state = State.COMMAND;

  216.                     switch (state) {
  217.                     case COMMAND:
  218.                         break;
  219.                     case ACCOUNT:
  220.                         if (entry.account != null && entry.complete()) {
  221.                             hosts.put(entry.machine, entry);
  222.                             entry = new NetRCEntry();
  223.                         }
  224.                         if (matcher.find())
  225.                             entry.account = matcher.group();
  226.                         state = State.COMMAND;
  227.                         break;
  228.                     case LOGIN:
  229.                         if (entry.login != null && entry.complete()) {
  230.                             hosts.put(entry.machine, entry);
  231.                             entry = new NetRCEntry();
  232.                         }
  233.                         if (matcher.find())
  234.                             entry.login = matcher.group();
  235.                         state = State.COMMAND;
  236.                         break;
  237.                     case PASSWORD:
  238.                         if (entry.password != null && entry.complete()) {
  239.                             hosts.put(entry.machine, entry);
  240.                             entry = new NetRCEntry();
  241.                         }
  242.                         if (matcher.find())
  243.                             entry.password = matcher.group().toCharArray();
  244.                         state = State.COMMAND;
  245.                         break;
  246.                     case DEFAULT:
  247.                         if (entry.machine != null && entry.complete()) {
  248.                             hosts.put(entry.machine, entry);
  249.                             entry = new NetRCEntry();
  250.                         }
  251.                         entry.machine = DEFAULT_ENTRY;
  252.                         state = State.COMMAND;
  253.                         break;
  254.                     case MACDEF:
  255.                         if (entry.macdef != null && entry.complete()) {
  256.                             hosts.put(entry.machine, entry);
  257.                             entry = new NetRCEntry();
  258.                         }
  259.                         if (matcher.find())
  260.                             entry.macdef = matcher.group();
  261.                         state = State.COMMAND;
  262.                         break;
  263.                     case MACHINE:
  264.                         if (entry.machine != null && entry.complete()) {
  265.                             hosts.put(entry.machine, entry);
  266.                             entry = new NetRCEntry();
  267.                         }
  268.                         if (matcher.find())
  269.                             entry.machine = matcher.group();
  270.                         state = State.COMMAND;
  271.                         break;
  272.                     }
  273.                 }
  274.             }

  275.             // reading macbody on EOF
  276.             if (entry.macdef != null && entry.macbody == null)
  277.                 entry.macbody = macbody;

  278.             if (entry.complete())
  279.                 hosts.put(entry.machine, entry);
  280.         } catch (IOException e) {
  281.             throw new RuntimeException(e);
  282.         }
  283.     }
  284. }