001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.maven.scm.provider.svn.repository;
020
021import org.apache.maven.scm.provider.ScmProviderRepository;
022import org.apache.maven.scm.provider.ScmProviderRepositoryWithHost;
023import org.apache.maven.scm.provider.svn.SvnTagBranchUtils;
024
025/**
026 * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a>
027 */
028public class SvnScmProviderRepository extends ScmProviderRepositoryWithHost {
029    private String url;
030
031    private String protocol;
032
033    /**
034     * The base directory for any tags. Can be relative to the repository URL or an absolute URL.
035     */
036    private String tagBase;
037
038    /**
039     * The base directory for any branches. Can be relative to the repository URL or an absolute URL.
040     */
041    private String branchBase;
042
043    public SvnScmProviderRepository(String url) {
044        parseUrl(url);
045
046        tagBase = SvnTagBranchUtils.resolveTagBase(url);
047
048        branchBase = SvnTagBranchUtils.resolveBranchBase(url);
049    }
050
051    public SvnScmProviderRepository(String url, String user, String password) {
052        this(url);
053
054        setUser(user);
055
056        setPassword(password);
057    }
058
059    public String getUrl() {
060        return url;
061    }
062
063    /**
064     * Returns the url/directory to be used when tagging this repository.
065     */
066    public String getTagBase() {
067        return tagBase;
068    }
069
070    /**
071     * Sets the url/directory to be used when tagging this repository.
072     * The TagBase is a way to override the default tag location for the
073     * repository.  The default tag location is automatically determined
074     * for repositories in the standard subversion layout (with /tags /branches /trunk).
075     * Specify this value only if the repository is using a directory other than "/tags" for tagging.
076     *
077     * @param tagBase an absolute or relative url to the base directory to create tags in.
078     *                URL should be in a format that svn client understands, not the scm url format.
079     */
080    public void setTagBase(String tagBase) {
081        this.tagBase = tagBase;
082    }
083
084    /**
085     * Returns the url/directory to be used when tagging this repository.
086     */
087    public String getBranchBase() {
088        return branchBase;
089    }
090
091    /**
092     * Sets the url/directory to be used when branching this repository.
093     * The BranchBase is a way to override the default branch location for the
094     * repository.  The default branch location is automatically determined
095     * for repositories in the standard subversion layout (with /tags /branches /trunk).
096     * Specify this value only if the repository is using a directory other than "/branches" for branching.
097     *
098     * @param branchBase an absolute or relative url to the base directory to create branch in.
099     *                   URL should be in a format that svn client understands, not the scm url format.
100     */
101    public void setBranchBase(String branchBase) {
102        this.branchBase = branchBase;
103    }
104
105    private void setProtocol(String protocol) {
106        this.protocol = protocol;
107    }
108
109    /**
110     * Get the protocol used in this repository (file://, http://, https://,...)
111     *
112     * @return the protocol
113     */
114    public String getProtocol() {
115        return protocol;
116    }
117
118    private void parseUrl(String url) {
119        if (url.startsWith("file")) {
120            setProtocol("file://");
121        } else if (url.startsWith("https")) {
122            setProtocol("https://");
123        } else if (url.startsWith("http")) {
124            setProtocol("http://");
125        } else if (url.startsWith("svn+")) {
126            setProtocol(url.substring(0, url.indexOf("://") + 3));
127        } else if (url.startsWith("svn")) {
128            setProtocol("svn://");
129        }
130
131        if (getProtocol() == null) {
132            return;
133        }
134
135        String urlPath = url.substring(getProtocol().length());
136
137        int indexAt = urlPath.indexOf('@');
138
139        // a file:// URL may contain userinfo according to RFC 8089, but our implementation is broken
140        // extract user information, broken see SCM-909
141        if (indexAt > 0 && !getProtocol().startsWith("svn+") && !getProtocol().equals("file://")) {
142            String userPassword = urlPath.substring(0, indexAt);
143            if (userPassword.indexOf(':') < 0) {
144                setUser(userPassword);
145            } else {
146                setUser(userPassword.substring(0, userPassword.indexOf(':')));
147                setPassword(userPassword.substring(userPassword.indexOf(':') + 1));
148            }
149
150            urlPath = urlPath.substring(indexAt + 1);
151
152            this.url = getProtocol() + urlPath;
153        } else {
154            this.url = getProtocol() + urlPath;
155        }
156
157        if (!"file://".equals(getProtocol())) {
158            int indexSlash = urlPath.indexOf('/');
159
160            String hostPort = urlPath;
161
162            if (indexSlash > 0) {
163                hostPort = urlPath.substring(0, indexSlash);
164            }
165
166            int indexColon = hostPort.indexOf(':');
167
168            if (indexColon > 0) {
169                setHost(hostPort.substring(0, indexColon));
170                setPort(Integer.parseInt(hostPort.substring(indexColon + 1)));
171            } else {
172                setHost(hostPort);
173            }
174        }
175    }
176
177    /**
178     * {@inheritDoc}
179     */
180    public ScmProviderRepository getParent() {
181        String newUrl = getUrl().substring(getProtocol().length());
182
183        while (newUrl.endsWith("/.")) {
184            newUrl = newUrl.substring(0, newUrl.length() - 2);
185        }
186
187        while (newUrl.endsWith("/")) {
188            newUrl = newUrl.substring(0, newUrl.length() - 1);
189        }
190
191        int i = newUrl.lastIndexOf('/');
192
193        if (i < 0) {
194            return null;
195        }
196        newUrl = newUrl.substring(0, i);
197
198        return new SvnScmProviderRepository(getProtocol() + newUrl, getUser(), getPassword());
199    }
200
201    /**
202     * {@inheritDoc}
203     */
204    public String getRelativePath(ScmProviderRepository ancestor) {
205        if (ancestor instanceof SvnScmProviderRepository) {
206            SvnScmProviderRepository svnAncestor = (SvnScmProviderRepository) ancestor;
207
208            String path = getUrl().replaceFirst(svnAncestor.getUrl() + "/", "");
209
210            if (!path.equals(getUrl())) {
211                return path;
212            }
213        }
214        return null;
215    }
216
217    /**
218     * {@inheritDoc}
219     */
220    public String toString() {
221        return getUrl();
222    }
223}