Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
[FIXED JENKINS-20684] Parser for .netrc file format
This path implements a parser for the .netrc file supporting various formats. It is not restricted to having a machine definition on a single line but can parse any valid format. The one exception is that it does *not* cope with `macdef` definitions. handle `macdef` definitons in the netrc file to the parser. Anything following a `macdef` keyword will be ignored up to the next empty line.
- Loading branch information
Showing
6 changed files
with
571 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
165 changes: 165 additions & 0 deletions
165
src/main/java/org/jenkinsci/plugins/gitclient/Netrc.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
package org.jenkinsci.plugins.gitclient; | ||
|
||
import edu.umd.cs.findbugs.annotations.NonNull; | ||
import hudson.plugins.git.GitException; | ||
import hudson.util.IOUtils; | ||
|
||
import java.io.BufferedReader; | ||
import java.io.File; | ||
import java.io.FileReader; | ||
import java.io.IOException; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.regex.Matcher; | ||
import java.util.regex.Pattern; | ||
|
||
import org.apache.commons.httpclient.Credentials; | ||
import org.apache.commons.httpclient.UsernamePasswordCredentials; | ||
|
||
class Netrc { | ||
private static final Pattern NETRC_TOKEN = Pattern.compile("(\\S+)"); | ||
|
||
private enum ParseState { | ||
START, REQ_KEY, REQ_VALUE, MACHINE, LOGIN, PASSWORD, MACDEF, END; | ||
}; | ||
|
||
|
||
private File netrc; | ||
private long lastModified; | ||
private Map<String,UsernamePasswordCredentials> hosts = new HashMap<String,UsernamePasswordCredentials>(); | ||
|
||
|
||
|
||
public static Netrc getInstance() { | ||
File netrc = getDefaultFile(); | ||
return netrc.exists() ? getInstance(netrc) : null; | ||
} | ||
|
||
public static Netrc getInstance(@NonNull String netrcPath) { | ||
File netrc = new File(netrcPath); | ||
return netrc.exists() ? getInstance(new File(netrcPath)) : null; | ||
} | ||
|
||
public static Netrc getInstance(File netrc) { | ||
return new Netrc(netrc).parse(); | ||
} | ||
|
||
private static File getDefaultFile() { | ||
File home = new File(System.getProperty("user.home")); | ||
File netrc = new File(home, ".netrc"); | ||
if (!netrc.exists()) netrc = new File(home, "_netrc"); // windows variant | ||
return netrc; | ||
} | ||
|
||
|
||
public Credentials getCredentials(String host) { | ||
if (!this.netrc.exists()) return null; | ||
if (this.lastModified != this.netrc.lastModified()) parse(); | ||
return this.hosts.get(host); | ||
} | ||
|
||
private Netrc(File netrc) { | ||
this.netrc = netrc; | ||
} | ||
|
||
synchronized private Netrc parse() { | ||
if (!netrc.exists()) return null; | ||
|
||
this.hosts.clear(); | ||
this.lastModified = this.netrc.lastModified(); | ||
|
||
BufferedReader r = null; | ||
try { | ||
r = new BufferedReader(new FileReader(netrc)); | ||
String line = null; | ||
String machine = null; | ||
String login = null; | ||
String password = null; | ||
|
||
ParseState state = ParseState.START; | ||
Matcher matcher = NETRC_TOKEN.matcher(""); | ||
while ((line = r.readLine()) != null) { | ||
line = line.trim(); | ||
if (line.isEmpty()) { | ||
if (state == ParseState.MACDEF) { | ||
state = ParseState.REQ_KEY; | ||
} | ||
continue; | ||
} | ||
|
||
matcher.reset(line); | ||
while (matcher.find()) { | ||
String match = matcher.group(); | ||
switch (state) { | ||
case START: | ||
if ("machine".equals(match)) { | ||
state = ParseState.MACHINE; | ||
} | ||
break; | ||
|
||
case REQ_KEY: | ||
if ("login".equals(match)) { | ||
state = ParseState.LOGIN; | ||
} | ||
else if ("password".equals(match)) { | ||
state = ParseState.PASSWORD; | ||
} | ||
else if ("macdef".equals(match)) { | ||
state = ParseState.MACDEF; | ||
} | ||
else if ("machine".equals(match)) { | ||
state = ParseState.MACHINE; | ||
} | ||
else { | ||
state = ParseState.REQ_VALUE; | ||
} | ||
break; | ||
|
||
case REQ_VALUE: | ||
state = ParseState.REQ_KEY; | ||
break; | ||
|
||
case MACHINE: | ||
if (machine != null) { | ||
if (login != null && password != null) { | ||
this.hosts.put(machine, new UsernamePasswordCredentials(login, password)); | ||
} | ||
} | ||
machine = match; | ||
login = null; | ||
password = null; | ||
state = ParseState.REQ_KEY; | ||
break; | ||
|
||
case LOGIN: | ||
login = match; | ||
state = ParseState.REQ_KEY; | ||
break; | ||
|
||
case PASSWORD: | ||
password = match; | ||
state = ParseState.REQ_KEY; | ||
break; | ||
|
||
case MACDEF: | ||
// Only way out is an empty line, handled before the find() loop. | ||
break; | ||
} | ||
} | ||
} | ||
if (machine != null) { | ||
if (login != null && password != null) { | ||
this.hosts.put(machine, new UsernamePasswordCredentials(login, password)); | ||
} | ||
} | ||
|
||
} catch (IOException e) { | ||
throw new GitException("Invalid netrc file: '" + this.netrc.getAbsolutePath() + "'", e); | ||
} finally { | ||
IOUtils.closeQuietly(r); | ||
} | ||
|
||
return this; | ||
} | ||
|
||
} |
Oops, something went wrong.
3430d92
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Florian, I have enabled findbugs in #95 . One of the warnings from findbugs is that there is inconsistent synchronization in one area of the Netrc.java file. Is that inconsistent synchronization pattern intentional? If so, then it is trivial to add it as an exclusion to the exclusions file.
The findbugs report says:
Netrc.java:57, IS2_INCONSISTENT_SYNC, Priority: Normal
Inconsistent synchronization of org.jenkinsci.plugins.gitclient.Netrc.lastModified; locked 50% of time
The fields of this class appear to be accessed inconsistently with respect to synchronization. This bug report indicates that the bug pattern detector judged that
The class contains a mix of locked and unlocked accesses,
At least one locked access was performed by one of the class's own methods, and
The number of unsynchronized field accesses (reads and writes) was no more than one third of all accesses, with writes being weighed twice as high as reads
A typical bug matching this bug pattern is forgetting to synchronize one of the methods in a class that is intended to be thread-safe.
You can select the nodes labeled "Unsynchronized access" to show the code locations where the detector believed that a field was accessed without synchronization.
Note that there are various sources of inaccuracy in this detector; for example, the detector cannot statically detect all situations in which a lock is held. Also, even when the detector is accurate in distinguishing locked vs. unlocked accesses, the code in question may still be correct.