diff --git a/hsarjcli/.classpath b/hsarjcli/.classpath
new file mode 100644
index 0000000..18d70f0
--- /dev/null
+++ b/hsarjcli/.classpath
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/hsarjcli/.project b/hsarjcli/.project
new file mode 100644
index 0000000..528bfcc
--- /dev/null
+++ b/hsarjcli/.project
@@ -0,0 +1,17 @@
+
+
+ hsarjcli
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/hsarjcli/.settings/org.eclipse.jdt.core.prefs b/hsarjcli/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..bca7901
--- /dev/null
+++ b/hsarjcli/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,12 @@
+#Mon May 16 15:05:41 CEST 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/hsarjcli/src/de/hsadmin/cli/Base64.java b/hsarjcli/src/de/hsadmin/cli/Base64.java
new file mode 100644
index 0000000..afa5cc1
--- /dev/null
+++ b/hsarjcli/src/de/hsadmin/cli/Base64.java
@@ -0,0 +1,244 @@
+/*
+ * @(#)Base64.java 1.5 03/12/19
+ *
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+ */
+
+package de.hsadmin.cli;
+
+/**
+ * Static methods for translating Base64 encoded strings to byte arrays
+ * and vice-versa.
+ *
+ * @author Josh Bloch
+ * @version 1.5, 12/19/03
+ * @see Preferences
+ * @since 1.4
+ */
+class Base64 {
+ /**
+ * Translates the specified byte array into a Base64 string as per
+ * Preferences.put(byte[]).
+ */
+ static String byteArrayToBase64(byte[] a) {
+ return byteArrayToBase64(a, false);
+ }
+
+ /**
+ * Translates the specified byte array into an "aternate representation"
+ * Base64 string. This non-standard variant uses an alphabet that does
+ * not contain the uppercase alphabetic characters, which makes it
+ * suitable for use in situations where case-folding occurs.
+ */
+ static String byteArrayToAltBase64(byte[] a) {
+ return byteArrayToBase64(a, true);
+ }
+
+ private static String byteArrayToBase64(byte[] a, boolean alternate) {
+ int aLen = a.length;
+ int numFullGroups = aLen/3;
+ int numBytesInPartialGroup = aLen - 3*numFullGroups;
+ int resultLen = 4*((aLen + 2)/3);
+ StringBuffer result = new StringBuffer(resultLen);
+ char[] intToAlpha = (alternate ? intToAltBase64 : intToBase64);
+
+ // Translate all full groups from byte array elements to Base64
+ int inCursor = 0;
+ for (int i=0; i> 2]);
+ result.append(intToAlpha[(byte0 << 4)&0x3f | (byte1 >> 4)]);
+ result.append(intToAlpha[(byte1 << 2)&0x3f | (byte2 >> 6)]);
+ result.append(intToAlpha[byte2 & 0x3f]);
+ }
+
+ // Translate partial group if present
+ if (numBytesInPartialGroup != 0) {
+ int byte0 = a[inCursor++] & 0xff;
+ result.append(intToAlpha[byte0 >> 2]);
+ if (numBytesInPartialGroup == 1) {
+ result.append(intToAlpha[(byte0 << 4) & 0x3f]);
+ result.append("==");
+ } else {
+ // assert numBytesInPartialGroup == 2;
+ int byte1 = a[inCursor++] & 0xff;
+ result.append(intToAlpha[(byte0 << 4)&0x3f | (byte1 >> 4)]);
+ result.append(intToAlpha[(byte1 << 2)&0x3f]);
+ result.append('=');
+ }
+ }
+ // assert inCursor == a.length;
+ // assert result.length() == resultLen;
+ return result.toString();
+ }
+
+ /**
+ * This array is a lookup table that translates 6-bit positive integer
+ * index values into their "Base64 Alphabet" equivalents as specified
+ * in Table 1 of RFC 2045.
+ */
+ private static final char intToBase64[] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
+ 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+ 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
+ };
+
+ /**
+ * This array is a lookup table that translates 6-bit positive integer
+ * index values into their "Alternate Base64 Alphabet" equivalents.
+ * This is NOT the real Base64 Alphabet as per in Table 1 of RFC 2045.
+ * This alternate alphabet does not use the capital letters. It is
+ * designed for use in environments where "case folding" occurs.
+ */
+ private static final char intToAltBase64[] = {
+ '!', '"', '#', '$', '%', '&', '\'', '(', ')', ',', '-', '.', ':',
+ ';', '<', '>', '@', '[', ']', '^', '`', '_', '{', '|', '}', '~',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
+ 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '?'
+ };
+
+ /**
+ * Translates the specified Base64 string (as per Preferences.get(byte[]))
+ * into a byte array.
+ *
+ * @throw IllegalArgumentException if s is not a valid Base64
+ * string.
+ */
+ static byte[] base64ToByteArray(String s) {
+ return base64ToByteArray(s, false);
+ }
+
+ /**
+ * Translates the specified "aternate representation" Base64 string
+ * into a byte array.
+ *
+ * @throw IllegalArgumentException or ArrayOutOfBoundsException
+ * if s is not a valid alternate representation
+ * Base64 string.
+ */
+ static byte[] altBase64ToByteArray(String s) {
+ return base64ToByteArray(s, true);
+ }
+
+ private static byte[] base64ToByteArray(String s, boolean alternate) {
+ byte[] alphaToInt = (alternate ? altBase64ToInt : base64ToInt);
+ int sLen = s.length();
+ int numGroups = sLen/4;
+ if (4*numGroups != sLen)
+ throw new IllegalArgumentException(
+ "String length must be a multiple of four.");
+ int missingBytesInLastGroup = 0;
+ int numFullGroups = numGroups;
+ if (sLen != 0) {
+ if (s.charAt(sLen-1) == '=') {
+ missingBytesInLastGroup++;
+ numFullGroups--;
+ }
+ if (s.charAt(sLen-2) == '=')
+ missingBytesInLastGroup++;
+ }
+ byte[] result = new byte[3*numGroups - missingBytesInLastGroup];
+
+ // Translate all full groups from base64 to byte array elements
+ int inCursor = 0, outCursor = 0;
+ for (int i=0; i> 4));
+ result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2));
+ result[outCursor++] = (byte) ((ch2 << 6) | ch3);
+ }
+
+ // Translate partial group, if present
+ if (missingBytesInLastGroup != 0) {
+ int ch0 = base64toInt(s.charAt(inCursor++), alphaToInt);
+ int ch1 = base64toInt(s.charAt(inCursor++), alphaToInt);
+ result[outCursor++] = (byte) ((ch0 << 2) | (ch1 >> 4));
+
+ if (missingBytesInLastGroup == 1) {
+ int ch2 = base64toInt(s.charAt(inCursor++), alphaToInt);
+ result[outCursor++] = (byte) ((ch1 << 4) | (ch2 >> 2));
+ }
+ }
+ // assert inCursor == s.length()-missingBytesInLastGroup;
+ // assert outCursor == result.length;
+ return result;
+ }
+
+ /**
+ * Translates the specified character, which is assumed to be in the
+ * "Base 64 Alphabet" into its equivalent 6-bit positive integer.
+ *
+ * @throw IllegalArgumentException or ArrayOutOfBoundsException if
+ * c is not in the Base64 Alphabet.
+ */
+ private static int base64toInt(char c, byte[] alphaToInt) {
+ int result = alphaToInt[c];
+ if (result < 0)
+ throw new IllegalArgumentException("Illegal character " + c);
+ return result;
+ }
+
+ /**
+ * This array is a lookup table that translates unicode characters
+ * drawn from the "Base64 Alphabet" (as specified in Table 1 of RFC 2045)
+ * into their 6-bit positive integer equivalents. Characters that
+ * are not in the Base64 alphabet but fall within the bounds of the
+ * array are translated to -1.
+ */
+ private static final byte base64ToInt[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34,
+ 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
+ };
+
+ /**
+ * This array is the analogue of base64ToInt, but for the nonstandard
+ * variant that avoids the use of uppercase alphabetic characters.
+ */
+ private static final byte altBase64ToInt[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1,
+ 2, 3, 4, 5, 6, 7, 8, -1, 62, 9, 10, 11, -1 , 52, 53, 54, 55, 56, 57,
+ 58, 59, 60, 61, 12, 13, 14, -1, 15, 63, 16, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 17, -1, 18, 19, 21, 20, 26, 27, 28, 29, 30, 31, 32, 33,
+ 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
+ 51, 22, 23, 24, 25
+ };
+
+ public static void main(String args[]) {
+ int numRuns = Integer.parseInt(args[0]);
+ int numBytes = Integer.parseInt(args[1]);
+ java.util.Random rnd = new java.util.Random();
+ for (int i=0; i> responseHeaders;
+ private StringBuilder requestData;
+ private StringBuilder responseData;
+ private String password;
+ private String username;
+ private String responseMessage;
+
+ public HttpRequest(String url)
+ throws MalformedURLException
+ {
+ this.url = new URL( url );
+ }
+
+ public int post() throws IOException
+ {
+ return doRequest("POST");
+ }
+
+ public int put() throws IOException
+ {
+ return doRequest("PUT");
+ }
+
+ public int doRequest(String method) throws IOException
+ {
+ echoVerbose(method + ": " + url.toString() );
+
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setDoOutput(true);
+ conn.setDoInput(true);
+ conn.setUseCaches(false);
+ conn.setAllowUserInteraction(false);
+ conn.setRequestProperty("Content-type", "application/x-www-form-urlencoded; charset=" + "UTF-8");
+ if ( username != null )
+ conn.setRequestProperty("Authorization", createAuthorizationHeader(username, password) );
+ conn.setRequestMethod(method);
+ OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream(), "UTF-8");
+ if ( requestData == null )
+ {
+ if ( username != null )
+ addParam("username", username);
+ if ( password != null )
+ addParam("password", password);
+ }
+ if ( requestData != null )
+ wr.write(requestData.toString());
+ wr.flush();
+
+ responseHeaders = conn.getHeaderFields();
+ if (verbosity > 1)
+ echoResponseHeaders(conn);
+
+ responseData = new StringBuilder();
+ try
+ {
+ BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream(), "ISO-8859-1"));
+ String line;
+ while ((line = rd.readLine()) != null) {
+ responseData.append(line + "\n");
+ }
+ rd.close();
+ }
+ catch ( IOException exc )
+ {
+ }
+ wr.close();
+
+ responseMessage = conn.getResponseMessage();
+ return conn.getResponseCode();
+ }
+
+ private String createAuthorizationHeader(String username, String password) throws IOException
+ {
+ if ( password == null )
+ password = getPassWord();
+ String authValue = username + ":" + password;
+ String encodedAuth = Base64.byteArrayToBase64(authValue.getBytes());
+ return "Basic " + encodedAuth;
+ }
+
+ /// printing the response header (TODO: just for debugging - remove)
+ private void echoResponseHeaders(HttpURLConnection conn) throws IOException
+ {
+ System.out.println("ResponseCode: " + conn.getResponseCode());
+ System.out.println("ResponseMessage: " + conn.getResponseMessage());
+ for ( String key: responseHeaders.keySet() )
+ {
+ System.out.println( key + ": " + responseHeaders.get(key) );
+ }
+ }
+
+ public void addParam(String name, String value) throws UnsupportedEncodingException
+ {
+ if ( requestData == null )
+ requestData = new StringBuilder();
+ else
+ requestData.append("&");
+ requestData.append(
+ URLEncoder.encode(name, "UTF-8") + "=" + URLEncoder.encode(value, "UTF-8") );
+ }
+
+ public String getResponseData()
+ {
+ return responseData.toString();
+ }
+
+ public void setUsername(String username)
+ {
+ this.username = username;
+ }
+
+ public void setPassword(String password)
+ {
+ this.password = password;
+ }
+
+ public List getResponseHeader(String key)
+ {
+ return responseHeaders.get(key);
+ }
+
+ public void setRequestData(String data)
+ {
+ requestData = new StringBuilder(data);
+ }
+
+ public String getResponseMessage()
+ {
+ return responseMessage;
+ }
+ }
+
+ /// returns the TGT, either from a special user config file or from stdin
+ private static String getTGT() throws IOException
+ {
+ HttpRequest postReq = new HttpRequest( loginURL );
+ echoVerbose("using userName=" + userName + " for login");
+
+ postReq.setUsername(userName);
+ String pw = getPassWord();
+ postReq.setPassword(pw);
+ if ( 201 == postReq.post() )
+ {
+ String tgt = postReq.getResponseHeader("Location").get(0);
+ echoVerbose("TGT: " + tgt);
+ return tgt;
+ }
+ else
+ return null;
+ }
+
+ /// retrieves a new session ticket using the given ticket granting ticket
+ private static String getST(String tgt)
+ {
+ String st = null;
+ try
+ {
+ HttpRequest httpReq = new HttpRequest( tgt );
+ httpReq.setRequestData( "service=" + backendURL );
+ if ( 200 == httpReq.post() )
+ st = httpReq.getResponseData();
+ }
+ catch ( Exception exc )
+ { /* ignore */ }
+
+ echoVerbose("ST: " + st);
+ return st;
+ }
+
+ /// returns the password, either from parameter, user config file or from stdin
+ private static String getPassWord() throws IOException
+ {
+ if ( passWord == null )
+ passWord = config.getProperty( "passWord." + userName, config.getProperty("passWord") );
+
+ if ( passWord == null )
+ {
+ Console console = System.console();
+ if ( console != null )
+ {
+ char [] input = console.readPassword("Password: ");
+ passWord = new String(input);
+ }
+ else
+ {
+ System.out.print("Password: ");
+ System.out.flush();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
+
+ PasswordMaskingThread thread = new PasswordMaskingThread();
+ thread.start();
+ passWord = reader.readLine();
+ thread.interrupt();
+ }
+ }
+ return passWord;
+ }
+
+ /// calls the remote service using the given service ticket 'st'
+ private static String exec(String st, String[] args) throws IOException
+ {
+ HttpRequest httpReq = new HttpRequest( servletURL );
+ echoVerbose("using userName=" + userName + " for exec");
+ httpReq.setUsername(userName);
+ httpReq.setPassword(st);
+ StringBuilder argsAsString = new StringBuilder();
+ for ( String arg: args )
+ argsAsString.append(arg + "\n");
+ echoVerbose("--- remote args: ---\n" + argsAsString.toString() + "\n--- end of args. ---");
+ httpReq.setRequestData(argsAsString.toString());
+
+ int code = httpReq.put();
+ List hsadminErrors = httpReq.getResponseHeader("X-hsadmin-error");
+ if ( hsadminErrors != null && hsadminErrors.size() > 0 && hsadminErrors.get(0) != null )
+ throw new RuntimeException( hsadminErrors.get(0) );
+ else if ( 200 == code )
+ return httpReq.getResponseData();
+ throw new RuntimeException( "HTTP error #" + code + ": " + httpReq.getResponseMessage() );
+ }
+
+ /// reads session information (in users home dir)
+ private static String loadTGT(String user, String fileName) throws IOException
+ {
+ Properties properties = new Properties();
+ try
+ {
+ properties.load( new FileInputStream( new File( System.getProperty("user.home"), fileName)) );
+ return properties.getProperty(user);
+ }
+ catch ( FileNotFoundException exc )
+ {
+ return null;
+ }
+ }
+
+ /// stores session information (in users home dir)
+ private static void storeTGT(String fileName, String user, String tgt) throws IOException
+ {
+ Properties properties = new Properties();
+ try
+ {
+ properties.load( new FileInputStream( new File( System.getProperty("user.home"), fileName)) );
+ }
+ catch ( Exception exc )
+ { /* ignore */ }
+ properties.setProperty(user, tgt);
+ properties.store( new FileOutputStream( new File( System.getProperty("user.home"), fileName)), "" );
+ }
+
+ /// reads the settings from the given config file (looked up in users home dir)
+ private static void readUserConfig(String string) throws IOException
+ {
+ // Read properties file.
+ config = new Properties();
+ try {
+ config.load(new FileInputStream( new File(System.getProperty("user.home"), ".hsadmin.conf")));
+ } catch (FileNotFoundException e) {
+ }
+
+ userName = config.getProperty("userName", System.getenv("USER"));
+ loginURL = config.getProperty("loginURL", loginURL);
+ servletURL = config.getProperty("servletURL", servletURL);
+ backendURL = config.getProperty("backendURL", backendURL);
+ verbosity = Integer.parseInt( config.getProperty("verbosity", "0") );
+ }
+
+ /// echoes the string to stdout depending on verbosity level
+ private static void echoVerbose(String string)
+ {
+ if (verbosity > 0)
+ System.out.println("]" + string);
+ }
+
+ private static void printHelp()
+ {
+ System.out.println("syntax: hsadmin [-h|--help] [-V (0|1|2)|--verbosity=(0|1|2)] [-v|--version]\n" +
+ " [-u USER|--user=USER] [-|REMOTE-ARGUMENTS]");
+ System.out.println("sample: hsadmin -V 0 -u xyz00 -c user.search");
+ System.out.println("version: " + version);
+ System.out.println("");
+ System.out.println("REMOTE-ARGUMENTS ('-' on command line => read from stdin):");
+ }
+
+ /// reads remote arguments from stdin into 'remoteArgs'
+ private static void readArgsFromStdIn(ArrayList remoteArgs) throws IOException
+ {
+ echoVerbose("READING ARGS FROM STDIN");
+ BufferedReader rd = new BufferedReader(new InputStreamReader(System.in));
+ String line;
+ while ((line = rd.readLine()) != null) {
+ echoVerbose("ADDING REMOTE ARG:" + line);
+ remoteArgs.add(line);
+ }
+ rd.close();
+ }
+
+ private static String createSessionTicket() throws IOException
+ {
+ String tgt = loadTGT(userName, ".hsadmin.tgt");
+ String st = getST(tgt);
+ if ( st == null)
+ {
+ tgt = getTGT();
+ if ( tgt == null )
+ {
+ System.err.println("ERROR: login failure - cannot get ticket granting ticket");
+ System.exit(1);
+ }
+ storeTGT(".hsadmin.tgt", userName, tgt );
+ st = getST(tgt);
+ }
+ return st;
+ }
+
+ private static ArrayList createRemoteArgs(String[] args) throws IOException
+ {
+ ArrayList remoteArgs = new ArrayList();
+ for ( int n = 0; n < args.length; ++n )
+ {
+ String arg = args[n];
+ if ( arg.equals("-u") )
+ {
+ // TODO: out of bounds error
+ userName = args[++n];
+ }
+ else if ( arg.startsWith("--user=") || arg.startsWith("--user:") )
+ {
+ userName = args[n].substring(7);
+ }
+ else if ( arg.equals("-V") )
+ {
+ // TODO: out of bounds error
+ verbosity = Integer.parseInt(args[++n]);
+ }
+ else if ( arg.startsWith("--verbosity:") || arg.startsWith("--verbosity=") )
+ {
+ verbosity = Integer.parseInt(args[n].substring(12));
+ }
+ else if ( arg.equals("-v") )
+ {
+ System.out.println(version);
+ remoteArgs = new ArrayList();
+ break;
+ }
+ else if ( arg.startsWith("--version") )
+ {
+ System.out.println(version);
+ remoteArgs = new ArrayList();
+ break;
+ }
+ else if ( arg.equals("-h") )
+ {
+ // TODO: out of bounds error
+ printHelp();
+ remoteArgs = new ArrayList();
+ remoteArgs.add("-h");
+ break;
+ }
+ else if ( arg.startsWith("--help") )
+ {
+ printHelp();
+ remoteArgs = new ArrayList();
+ remoteArgs.add("-h");
+ break;
+ }
+ else if ( arg.equals("-") )
+ readArgsFromStdIn(remoteArgs);
+ else
+ {
+ echoVerbose("ADDING REMOTE ARG:" + args[n]);
+ remoteArgs.add(args[n]);
+ }
+ }
+
+ return remoteArgs;
+ }
+
+ /// main program
+ public static void main(String[] args) throws IOException
+ {
+ readUserConfig(".hsadmin.conf");
+
+ ArrayList remoteArgs = createRemoteArgs(args);
+
+ if ( remoteArgs.size() > 0 )
+ {
+ try {
+ String st = createSessionTicket();
+
+ String response = exec(st, remoteArgs.toArray(new String[remoteArgs.size()]));
+ if ( response != null )
+ System.out.print(response);
+ }
+ catch ( Exception exc )
+ {
+ System.err.println(exc.getMessage());
+ System.exit(1); // TODO: be more specific, like: 1=unknown, 2=remoting, 3=functional
+ }
+ }
+ }
+
+}