replaced <code></code> with more concise {@code}
[idea/community.git] / plugins / git4idea / rt / src / org / jetbrains / git4idea / http / GitAskPassApp.java
1 /*
2  * Copyright 2000-2016 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package org.jetbrains.git4idea.http;
17
18 import com.intellij.openapi.util.Pair;
19 import com.intellij.openapi.util.text.StringUtilRt;
20 import org.jetbrains.annotations.NotNull;
21 import org.jetbrains.git4idea.GitExternalApp;
22
23 /**
24  * <p>This is a program that would be called by Git when an HTTP connection is needed, that requires authorization,
25  *    and if {@code GIT_ASKPASS} variable is set to the script that invokes this program.</p>
26  * <p>The program is called separately for each authorization aspect.
27  *    I. e. if no username is specified, then it is started and queried for the username, and then started once again for the password.</p>
28  * <p>Since Git 1.7.9 the query format is the following:
29  *    <ul>
30  *      <li>{@code Username for 'https://bitbucket.org':}</li>
31  *      <li>{@code Password for 'https://bitbucket.org':}</li>
32  *      <li>{@code Password for 'https://username@bitbucket.org':}</li>
33  *    </ul>
34  * </p>
35  * <p>Before Git 1.7.9 the query didn't contain the URL:
36  *   <ul>
37  *     <li>{@code Username: }</li>
38  *     <li>{@code Password: }</li>
39  *   </ul>
40  * </p>
41  * <p>Git expects the reply from the program's standard output.</p>
42  *
43  * @author Kirill Likhodedov
44  */
45 public class GitAskPassApp implements GitExternalApp {
46
47   // STDOUT is used to provide credentials to Git process; STDERR is used to print error message to the main IDEA command line.
48   @SuppressWarnings("UseOfSystemOutOrSystemErr")
49   public static void main(String[] args) {
50     try {
51       if (args.length < 1) {
52         throw new IllegalArgumentException("No arguments specified!");
53       }
54
55       Pair<Boolean, String> arguments = parseArguments(args[0]);
56       boolean usernameNeeded = arguments.getFirst();
57       String url = arguments.getSecond();
58
59       String token = getNotNull(GitAskPassXmlRpcHandler.GIT_ASK_PASS_HANDLER_ENV);
60       int xmlRpcPort = Integer.parseInt(getNotNull(GitAskPassXmlRpcHandler.GIT_ASK_PASS_PORT_ENV));
61       GitAskPassXmlRpcClient xmlRpcClient = new GitAskPassXmlRpcClient(xmlRpcPort);
62
63       if (usernameNeeded) {
64         String username = xmlRpcClient.askUsername(token, url);
65         System.out.println(username);
66       }
67       else {
68         String pass = xmlRpcClient.askPassword(token, url);
69         System.out.println(pass);
70       }
71     }
72     catch (Throwable t) {
73       System.err.println(t.getMessage());
74       t.printStackTrace(System.err);
75     }
76   }
77
78   @NotNull
79   private static String getNotNull(@NotNull String env) {
80     String value = System.getenv(env);
81     if (value == null) {
82       throw new IllegalStateException(env + " environment variable is not defined!");
83     }
84     return value;
85   }
86
87   @NotNull
88   private static Pair<Boolean, String> parseArguments(@NotNull String arg) {
89     boolean username = StringUtilRt.startsWithIgnoreCase(arg, "username");
90     String url;
91     String[] split = arg.split(" ");
92     if (split.length > 2) {
93       url = parseUrl(split[2]);
94     }
95     else {
96       url = ""; // XML RPC doesn't like nulls
97     }
98     return Pair.create(username, url);
99   }
100
101   private static String parseUrl(@NotNull String urlArg) {
102     // un-quote and remove the trailing colon
103     String url = urlArg;
104     if (url.startsWith("'")) {
105       url = url.substring(1);
106     }
107     if (url.endsWith(":")) {
108       url = url.substring(0, url.length() - 1);
109     }
110     if (url.endsWith("'")) {
111       url = url.substring(0, url.length() - 1);
112     }
113     return url;
114   }
115
116 }