1 package net.trajano.wagon.git;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.net.HttpURLConnection;
6 import java.net.URI;
7 import java.net.URISyntaxException;
8 import java.util.ResourceBundle;
9 import java.util.logging.Level;
10 import java.util.logging.Logger;
11 import java.util.regex.Matcher;
12 import java.util.regex.Pattern;
13
14 import net.trajano.wagon.git.internal.AbstractGitWagon;
15 import net.trajano.wagon.git.internal.GitUri;
16
17 import org.apache.maven.wagon.ResourceDoesNotExistException;
18 import org.apache.maven.wagon.Wagon;
19 import org.codehaus.plexus.component.annotations.Component;
20 import org.eclipse.jgit.api.Git;
21 import org.eclipse.jgit.api.errors.GitAPIException;
22 import org.xbill.DNS.CNAMERecord;
23 import org.xbill.DNS.Lookup;
24 import org.xbill.DNS.TextParseException;
25 import org.xbill.DNS.Type;
26
27
28
29
30 @Component(role = Wagon.class, hint = "github", instantiationStrategy = "per-lookup")
31 public class GitHubPagesWagon extends AbstractGitWagon {
32
33
34
35
36 private static final Pattern GITHUB_PAGES_HOST_PATTERN = Pattern
37 .compile("([a-z]+)\\.github\\.io.?");
38
39
40
41
42 private static final Pattern GITHUB_PAGES_PATH_PATTERN = Pattern
43 .compile("/([^/]+)(/.*)?");
44
45
46
47
48 private static final Logger LOG;
49
50
51
52
53 private static final String MESSAGES = "META-INF/Messages";
54
55
56
57
58 private static final ResourceBundle R;
59
60 static {
61 LOG = Logger.getLogger("net.trajano.wagon.git", MESSAGES);
62 R = ResourceBundle.getBundle(MESSAGES);
63 }
64
65
66
67
68
69
70 @Override
71 public GitUri buildGitUri(final URI uri) throws IOException,
72 URISyntaxException {
73 final URI finalUri;
74
75 if ("http".equals(uri.getScheme()) || "https".equals(uri.getScheme())) {
76 final HttpURLConnection urlConnection = (HttpURLConnection) uri
77 .toURL().openConnection();
78 urlConnection.connect();
79 urlConnection.getResponseCode();
80 finalUri = urlConnection.getURL().toURI();
81 urlConnection.disconnect();
82 } else {
83 finalUri = uri;
84 }
85 final Matcher m = GITHUB_PAGES_HOST_PATTERN.matcher(finalUri.getHost());
86 final String username;
87 if (m.matches()) {
88 username = m.group(1);
89 } else {
90 final String cnameHost = getCnameForHost(finalUri.getHost());
91 final Matcher m2 = GITHUB_PAGES_HOST_PATTERN.matcher(cnameHost);
92 if (!m2.matches()) {
93 throw new RuntimeException(String.format(
94 R.getString("invalidGitHubPagesHost"), uri));
95 }
96 username = m2.group(1);
97 }
98
99 if ("".equals(finalUri.getPath()) || "/".equals(finalUri.getPath())) {
100 return buildRootUri(username);
101 } else {
102 return buildProjectUri(username, finalUri.getPath());
103 }
104 }
105
106
107
108
109
110
111
112
113
114
115 private GitUri buildProjectUri(final String username, final String path) {
116 final Matcher pathMatcher = GITHUB_PAGES_PATH_PATTERN.matcher(path);
117 pathMatcher.matches();
118 final String resource = pathMatcher.group(2);
119 return new GitUri("ssh://git@github.com/" + username + "/"
120 + pathMatcher.group(1) + ".git", "gh-pages", resource);
121 }
122
123
124
125
126
127
128
129
130 private GitUri buildRootUri(final String username) {
131 return new GitUri("ssh://git@github.com/" + username + "/" + username
132 + ".github.io.git", "master", "/");
133 }
134
135
136
137
138
139
140
141
142
143 private String getCnameForHost(final String host) throws TextParseException {
144 final Lookup lookup = new Lookup(host, Type.CNAME);
145 lookup.run();
146 if (lookup.getAnswers().length == 0) {
147 LOG.log(Level.SEVERE, "unableToFindCNAME", new Object[] { host });
148 return null;
149 }
150 return ((CNAMERecord) lookup.getAnswers()[0]).getTarget().toString();
151 }
152
153
154
155
156
157
158
159
160
161 @Override
162 public File getFileForResource(final String resourceName)
163 throws GitAPIException, IOException, URISyntaxException {
164
165
166 final GitUri resolved = buildGitUri(URI.create(
167 URI.create(getRepository().getUrl()).getSchemeSpecificPart())
168 .resolve(resourceName.replace(" ", "%20")));
169 Git resourceGit;
170 try {
171 resourceGit = getGit(resolved.getGitRepositoryUri());
172 } catch (final ResourceDoesNotExistException e) {
173 return null;
174 }
175
176 final File workTree = resourceGit.getRepository().getWorkTree();
177 final File resolvedFile = new File(workTree, resolved.getResource());
178 if (!resolvedFile.getCanonicalPath().startsWith(
179 workTree.getCanonicalPath())) {
180 throw new IOException(String.format(
181 "The resolved file '%s' is not in work tree '%s'",
182 resolvedFile, workTree));
183 }
184 return resolvedFile;
185 }
186 }