diff --git a/adapters/oidc/js/src/main/resources/keycloak.js b/adapters/oidc/js/src/main/resources/keycloak.js index c6adb9b67a..44a7e75729 100755 --- a/adapters/oidc/js/src/main/resources/keycloak.js +++ b/adapters/oidc/js/src/main/resources/keycloak.js @@ -154,7 +154,7 @@ return; } else if (initOptions) { if (initOptions.token || initOptions.refreshToken) { - setToken(initOptions.token, initOptions.refreshToken, initOptions.idToken, false); + setToken(initOptions.token, initOptions.refreshToken, initOptions.idToken); kc.timeSkew = initOptions.timeSkew || 0; if (loginIframe.enable) { @@ -406,10 +406,10 @@ timeLocal = (timeLocal + new Date().getTime()) / 2; var tokenResponse = JSON.parse(req.responseText); - setToken(tokenResponse['access_token'], tokenResponse['refresh_token'], tokenResponse['id_token'], true); - kc.timeSkew = Math.floor(timeLocal / 1000) - kc.tokenParsed.iat; + setToken(tokenResponse['access_token'], tokenResponse['refresh_token'], tokenResponse['id_token']); + kc.onAuthRefreshSuccess && kc.onAuthRefreshSuccess(); for (var p = refreshQueue.pop(); p != null; p = refreshQueue.pop()) { p.setSuccess(true); @@ -444,7 +444,7 @@ kc.clearToken = function() { if (kc.token) { - setToken(null, null, null, true); + setToken(null, null, null); kc.onAuthLogout && kc.onAuthLogout(); if (kc.loginRequired) { kc.login(); @@ -525,7 +525,7 @@ function authSuccess(accessToken, refreshToken, idToken, fulfillPromise) { timeLocal = (timeLocal + new Date().getTime()) / 2; - setToken(accessToken, refreshToken, idToken, true); + setToken(accessToken, refreshToken, idToken); if ((kc.tokenParsed && kc.tokenParsed.nonce != oauth.storedNonce) || (kc.refreshTokenParsed && kc.refreshTokenParsed.nonce != oauth.storedNonce) || @@ -609,7 +609,7 @@ return promise.promise; } - function setToken(token, refreshToken, idToken, useTokenTime) { + function setToken(token, refreshToken, idToken) { if (kc.tokenTimeoutHandle) { clearTimeout(kc.tokenTimeoutHandle); kc.tokenTimeoutHandle = null; @@ -629,9 +629,12 @@ kc.resourceAccess = kc.tokenParsed.resource_access; if (kc.onTokenExpired) { - var start = useTokenTime ? kc.tokenParsed.iat : (new Date().getTime() / 1000); - var expiresIn = kc.tokenParsed.exp - start; - kc.tokenTimeoutHandle = setTimeout(kc.onTokenExpired, expiresIn * 1000); + var expiresIn = (kc.tokenParsed['exp'] - (new Date().getTime() / 1000) + kc.timeSkew) * 1000; + if (expiresIn <= 0) { + kc.onTokenExpired(); + } else { + kc.tokenTimeoutHandle = setTimeout(kc.onTokenExpired, expiresIn); + } } } else { diff --git a/dependencies/server-all/pom.xml b/dependencies/server-all/pom.xml index 809890a03d..8922532ad2 100755 --- a/dependencies/server-all/pom.xml +++ b/dependencies/server-all/pom.xml @@ -83,7 +83,6 @@ org.keycloak keycloak-kerberos-federation - org.keycloak @@ -110,6 +109,12 @@ + + + org.keycloak + keycloak-sssd-federation + + org.keycloak diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/federation-sssd-setup.sh b/distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/federation-sssd-setup.sh new file mode 100644 index 0000000000..6a0eae2130 --- /dev/null +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/federation-sssd-setup.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +# Setup for SSSD +SSSD_FILE="/etc/sssd/sssd.conf" + +if [ -f "$SSSD_FILE" ]; +then + sed -i '/ldap_tls_cacert/a ldap_user_extra_attrs = mail:mail, sn:sn, givenname:givenname, telephoneNumber:telephoneNumber' $SSSD_FILE + sed -i 's/nss, sudo, pam/nss, sudo, pam, ifp/' $SSSD_FILE + sed -i '/\[ifp\]/a allowed_uids = root\nuser_attributes = +mail, +telephoneNumber, +givenname, +sn' $SSSD_FILE + systemctl restart sssd +else + echo "Please make sure you have $SSSD_FILE into your system! Aborting." + exit 1 +fi + +# Setup for PAM +PAM_FILE="/etc/pam.d/keycloak" + +if [ ! -f "$PAM_FILE" ]; +then +cat < $PAM_FILE + auth required pam_sss.so + account required pam_sss.so +EOF +else + echo "$PAM_FILE already exists. Skipping it..." + exit 0 +fi + + diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml index de03ed8e8b..8cf1cde89b 100755 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-services/main/module.xml @@ -35,6 +35,7 @@ + diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-sssd-federation/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-sssd-federation/main/module.xml new file mode 100644 index 0000000000..ad16f3c4dd --- /dev/null +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/keycloak/org/keycloak/keycloak-sssd-federation/main/module.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/federation/pom.xml b/federation/pom.xml index 663f857a0b..dc702ccfab 100755 --- a/federation/pom.xml +++ b/federation/pom.xml @@ -35,6 +35,7 @@ ldap kerberos + sssd diff --git a/federation/sssd/pom.xml b/federation/sssd/pom.xml new file mode 100644 index 0000000000..7d109e82a5 --- /dev/null +++ b/federation/sssd/pom.xml @@ -0,0 +1,56 @@ + + + + keycloak-parent + org.keycloak + 2.2.0-SNAPSHOT + ../../pom.xml + + 4.0.0 + + keycloak-sssd-federation + Keycloak SSSD Federation + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${maven.compiler.source} + ${maven.compiler.target} + + + + + + + + junit + junit + test + + + net.java.dev.jna + jna + + + org.keycloak + keycloak-core + provided + + + org.keycloak + keycloak-server-spi + provided + + + org.jboss.logging + jboss-logging + provided + + + + diff --git a/federation/sssd/src/main/java/cx/ath/matthew/LibraryLoader.java b/federation/sssd/src/main/java/cx/ath/matthew/LibraryLoader.java new file mode 100644 index 0000000000..ffdf02dd6e --- /dev/null +++ b/federation/sssd/src/main/java/cx/ath/matthew/LibraryLoader.java @@ -0,0 +1,49 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package cx.ath.matthew; + +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * @author Bruno Oliveira. + */ +public class LibraryLoader { + + private static final Logger LOGGER = Logger.getLogger(LibraryLoader.class.getSimpleName()); + + private static final String[] PATHS = {"/usr/lib/", "/usr/lib64/", "/usr/local/lib/", "/opt/local/lib/"}; + private static final String LIBRARY_NAME = "libunix_dbus_java"; + private static final String VERSION = "0.0.8"; + private static boolean loadSucceeded; + + public static void load() { + for (String path : PATHS) { + try { + System.load(String.format("%s/%s.so.%s", path, LIBRARY_NAME, VERSION)); + loadSucceeded = true; + break; + } catch (UnsatisfiedLinkError e) { + loadSucceeded = false; + } + + } + + if (!loadSucceeded) LOGGER.log(Level.WARNING, "libunix_dbus_java not found\n" + + "Please, make sure you have the package libunix-dbus-java installed."); + } +} diff --git a/federation/sssd/src/main/java/cx/ath/matthew/debug/Debug.java b/federation/sssd/src/main/java/cx/ath/matthew/debug/Debug.java new file mode 100644 index 0000000000..30c4d8549d --- /dev/null +++ b/federation/sssd/src/main/java/cx/ath/matthew/debug/Debug.java @@ -0,0 +1,671 @@ +/* Copyright (C) 1991-2015 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ +/* This header is separate from features.h so that the compiler can + include it implicitly at the start of every compilation. It must + not itself include or any other header that includes + because the implicit include comes before any feature + test macros that may be defined in a source file before it first + explicitly includes a system header. GCC knows the name of this + header in order to preinclude it. */ +/* glibc's intent is to support the IEC 559 math functionality, real + and complex. If the GCC (4.9 and later) predefined macros + specifying compiler intent are available, use them to determine + whether the overall intent is to support these features; otherwise, + presume an older compiler has intent to support these features and + define these macros by default. */ +/* wchar_t uses Unicode 7.0.0. Version 7.0 of the Unicode Standard is + synchronized with ISO/IEC 10646:2012, plus Amendments 1 (published + on April, 2013) and 2 (not yet published as of February, 2015). + Additionally, it includes the accelerated publication of U+20BD + RUBLE SIGN. Therefore Unicode 7.0.0 is between 10646:2012 and + 10646:2014, and so we use the date ISO/IEC 10646:2012 Amd.1 was + published. */ +/* We do not support C11 . */ +/* + * Java Debug Library + * + * Copyright (c) Matthew Johnson 2005 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * To Contact the author, please email src@matthew.ath.cx + * + */ +package cx.ath.matthew.debug; + +import cx.ath.matthew.utils.Hexdump; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Properties; + +/** + * Add debugging to your program, has support for large projects with multiple + * classes and debug levels per class. Supports optional enabling of debug + * per-level per-class and debug targets of files, Streams or stderr. + * Also supports timing between debug outputs, printing of stack traces for Throwables + * and files/line numbers on each message. + *

+ * Debug now automatically figures out which class it was called from, so all + * methods passing in the calling class are deprecated. + *

+ *

+ * The defaults are to print all messages to stderr with class and method name. + *

+ *

+ * Should be called like this: + *

+ *
+ * if (Debug.debug) Debug.print(Debug.INFO, "Debug Message");
+ * 
+ */ +public class Debug { + /** + * This interface can be used to provide custom printing filters + * for certain classes. + */ + public static interface FilterCommand { + /** + * Called to print debug messages with a custom filter. + * + * @param output The PrintStream to output to. + * @param level The debug level of this message. + * @param location The textual location of the message. + * @param extra Extra information such as timing details. + * @param message The debug message. + * @param lines Other lines of a multiple-line debug message. + */ + public void filter(PrintStream output, int level, String location, String extra, String message, String[] lines); + } + + /** + * Highest priority messages + */ + public static final int CRIT = 1; + /** + * Error messages + */ + public static final int ERR = 2; + /** + * Warnings + */ + public static final int WARN = 3; + /** + * Information + */ + public static final int INFO = 4; + /** + * Debug messages + */ + public static final int DEBUG = 5; + /** + * Verbose debug messages + */ + public static final int VERBOSE = 6; + /** + * Set this to false to disable compilation of Debug statements + */ + public static final boolean debug = false; + /** + * The current output stream (defaults to System.err) + */ + public static PrintStream debugout = System.err; + private static Properties prop = null; + private static boolean timing = false; + private static boolean ttrace = false; + private static boolean lines = false; + private static boolean hexdump = false; + private static long last = 0; + private static int balen = 36; + private static int bawidth = 80; + private static Class saveclass = null; + //TODO: 1.5 private static Map, FilterCommand> filterMap = new HashMap, FilterCommand>(); + private static Map filterMap = new HashMap(); + + /** + * Set properties to configure debugging. + * Format of properties is class => level, e.g. + *
+     * cx.ath.matthew.io.TeeOutputStream = INFO
+     * cx.ath.matthew.io.DOMPrinter = DEBUG
+     * 
+ * The debug level can be one of CRIT, ERR, WARN, INFO, DEBUG or VERBOSE which + * correspond to all messages up to that level. The special words YES, ALL and TRUE + * cause all messages to be printed regardless of level. All other terms disable + * messages for that class. CRIT and ERR messages are always printed if debugging is enabled + * unless explicitly disabled. + * The special class name ALL can be used to set the default level for all classes. + * + * @param prop Properties object to use. + */ + public static void setProperties(Properties prop) { + Debug.prop = prop; + } + + /** + * Read which class to debug on at which level from the given File. + * Syntax the same as Java Properties files: + *
+     * <class> = <debuglevel>
+     * 
+ * E.G. + *
+     * cx.ath.matthew.io.TeeOutputStream = INFO
+     * cx.ath.matthew.io.DOMPrinter = DEBUG
+     * 
+ * The debug level can be one of CRIT, ERR, WARN, INFO, DEBUG or VERBOSE which + * correspond to all messages up to that level. The special words YES, ALL and TRUE + * cause all messages to be printed regardless of level. All other terms disable + * messages for that class. CRIT and ERR messages are always printed if debugging is enabled + * unless explicitly disabled. + * The special class name ALL can be used to set the default level for all classes. + * + * @param f File to read from. + */ + public static void loadConfig(File f) throws IOException { + prop = new Properties(); + prop.load(new FileInputStream(f)); + } + + /** + * @deprecated In Java 1.5 calling class is automatically identified, no need to pass it in. + */ + //TODO: 1.5 @Deprecated() + public static boolean debugging(Class c, int loglevel) { + if (debug) { + if (null == c) return true; + return debugging(c.getName(), loglevel); + } + return false; + } + + public static boolean debugging(String s, int loglevel) { + if (debug) { + try { + if (null == s) return true; + if (null == prop) return loglevel <= DEBUG; + String d = prop.getProperty(s); + if (null == d || "".equals(d)) d = prop.getProperty("ALL"); + if (null == d) return loglevel <= ERR; + if ("".equals(d)) return loglevel <= ERR; + d = d.toLowerCase(); + if ("true".equals(d)) return true; + if ("yes".equals(d)) return true; + if ("all".equals(d)) return true; + if ("verbose".equals(d)) return loglevel <= VERBOSE; + if ("debug".equals(d)) return loglevel <= DEBUG; + if ("info".equals(d)) return loglevel <= INFO; + if ("warn".equals(d)) return loglevel <= WARN; + if ("err".equals(d)) return loglevel <= ERR; + if ("crit".equals(d)) return loglevel <= CRIT; + int i = Integer.parseInt(d); + return i >= loglevel; + } catch (Exception e) { + return false; + } + } + return false; + } + + /** + * Output to the given Stream + */ + public static void setOutput(PrintStream p) throws IOException { + debugout = p; + } + + /** + * Output to the given file + */ + public static void setOutput(String filename) throws IOException { + debugout = new PrintStream(new FileOutputStream(filename, true)); + } + + /** + * Output to the default debug.log + */ + public static void setOutput() throws IOException { + setOutput("./debug.log"); + } + + /** + * Log at DEBUG + * + * @param d The object to log + */ + public static void print(Object d) { + if (debug) { + if (d instanceof String) + print(DEBUG, (String) d); + else if (d instanceof Throwable) + print(DEBUG, (Throwable) d); + else if (d instanceof byte[]) + print(DEBUG, (byte[]) d); + else if (d instanceof Map) + printMap(DEBUG, (Map) d); + else print(DEBUG, d); + } + } + + /** + * Log at DEBUG + * + * @param o The object doing the logging + * @param d The object to log + * @deprecated In Java 1.5 calling class is automatically identified, no need to pass it in. + */ + //TODO: 1.5 @Deprecated() + public static void print(Object o, Object d) { + if (debug) { + if (o instanceof Class) + saveclass = (Class) o; + else + saveclass = o.getClass(); + print(d); + } + } + + /** + * Log an Object + * + * @param o The object doing the logging + * @param loglevel The level to log at (DEBUG, WARN, etc) + * @param d The object to log with d.toString() + * @deprecated In Java 1.5 calling class is automatically identified, no need to pass it in. + */ + //TODO: 1.5 @Deprecated() + public static void print(Object o, int loglevel, Object d) { + if (debug) { + if (o instanceof Class) + saveclass = (Class) o; + else + saveclass = o.getClass(); + print(loglevel, d); + } + } + + /** + * Log a String + * + * @param o The object doing the logging + * @param loglevel The level to log at (DEBUG, WARN, etc) + * @param s The log message + * @deprecated In Java 1.5 calling class is automatically identified, no need to pass it in. + */ + //TODO: 1.5 @Deprecated() + public static void print(Object o, int loglevel, String s) { + if (debug) { + if (o instanceof Class) + saveclass = (Class) o; + else + saveclass = o.getClass(); + print(loglevel, s); + } + } + + /** + * Log a Throwable + * + * @param o The object doing the logging + * @param loglevel The level to log at (DEBUG, WARN, etc) + * @param t The throwable to log with .toString and .printStackTrace + * @deprecated In Java 1.5 calling class is automatically identified, no need to pass it in. + */ + //TODO: 1.5 @Deprecated() + public static void print(Object o, int loglevel, Throwable t) { + if (debug) { + if (o instanceof Class) + saveclass = (Class) o; + else + saveclass = o.getClass(); + print(loglevel, t); + } + } + + /** + * Log a Throwable + * + * @param c The class doing the logging + * @param loglevel The level to log at (DEBUG, WARN, etc) + * @param t The throwable to log with .toString and .printStackTrace + * @deprecated In Java 1.5 calling class is automatically identified, no need to pass it in. + */ + //TODO: 1.5 @Deprecated() + public static void print(Class c, int loglevel, Throwable t) { + if (debug) { + saveclass = c; + print(loglevel, t); + } + } + + /** + * Log a Throwable + * + * @param loglevel The level to log at (DEBUG, WARN, etc) + * @param t The throwable to log with .toString and .printStackTrace + * @see #setThrowableTraces to turn on stack traces. + */ + public static void print(int loglevel, Throwable t) { + if (debug) { + String timestr = ""; + String[] data = getTraceElements(); + if (debugging(data[0], loglevel)) { + if (timing) { + long now = System.currentTimeMillis(); + timestr = "{" + (now - last) + "} "; + last = now; + } + String[] lines = null; + if (ttrace) { + StackTraceElement[] ste = t.getStackTrace(); + lines = new String[ste.length]; + for (int i = 0; i < ste.length; i++) + lines[i] = "\tat " + ste[i].toString(); + } + _print(t.getClass(), loglevel, data[0] + "." + data[1] + "()" + data[2], timestr, t.toString(), lines); + } + } + } + + /** + * Log a byte array + * + * @param loglevel The level to log at (DEBUG, WARN, etc) + * @param b The byte array to print. + * @see #setHexDump to enable hex dumping. + * @see #setByteArrayCount to change how many bytes are printed. + * @see #setByteArrayWidth to change the formatting width of hex. + */ + public static void print(int loglevel, byte[] b) { + if (debug) { + String timestr = ""; + String[] data = getTraceElements(); + if (debugging(data[0], loglevel)) { + if (timing) { + long now = System.currentTimeMillis(); + timestr = "{" + (now - last) + "} "; + last = now; + } + String[] lines = null; + if (hexdump) { + if (balen >= b.length) + lines = Hexdump.format(b, bawidth).split("\n"); + else { + byte[] buf = new byte[balen]; + System.arraycopy(b, 0, buf, 0, balen); + lines = Hexdump.format(buf, bawidth).split("\n"); + } + } + _print(b.getClass(), loglevel, data[0] + "." + data[1] + "()" + data[2], timestr, b.length + " bytes", lines); + } + } + } + + /** + * Log a String + * + * @param loglevel The level to log at (DEBUG, WARN, etc) + * @param s The string to log with d.toString() + */ + public static void print(int loglevel, String s) { + if (debug) + print(loglevel, (Object) s); + } + + /** + * Log an Object + * + * @param c The class doing the logging + * @param loglevel The level to log at (DEBUG, WARN, etc) + * @param d The object to log with d.toString() + * @deprecated In Java 1.5 calling class is automatically identified, no need to pass it in. + */ + //TODO: 1.5 @Deprecated() + public static void print(Class c, int loglevel, Object d) { + if (debug) { + saveclass = c; + print(loglevel, d); + } + } + + /** + * Log a String + * + * @param c The class doing the logging + * @param loglevel The level to log at (DEBUG, WARN, etc) + * @param s The log message + * @deprecated In Java 1.5 calling class is automatically identified, no need to pass it in. + */ + //TODO: 1.5 @Deprecated() + public static void print(Class c, int loglevel, String s) { + if (debug) { + saveclass = c; + print(loglevel, s); + } + } + + private static String[] getTraceElements() { + String[] data = new String[]{"", "", ""}; + try { + Method m = Thread.class.getMethod("getStackTrace", new Class[0]); + StackTraceElement[] stes = (StackTraceElement[]) m.invoke(Thread.currentThread(), new Object[0]); + for (StackTraceElement ste : stes) { + if (Debug.class.getName().equals(ste.getClassName())) continue; + if (Thread.class.getName().equals(ste.getClassName())) continue; + if (Method.class.getName().equals(ste.getClassName())) continue; + if (ste.getClassName().startsWith("sun.reflect")) continue; + data[0] = ste.getClassName(); + data[1] = ste.getMethodName(); + if (lines) + data[2] = " " + ste.getFileName() + ":" + ste.getLineNumber(); + break; + } + } catch (NoSuchMethodException NSMe) { + if (null != saveclass) + data[0] = saveclass.getName(); + } catch (IllegalAccessException IAe) { + } catch (InvocationTargetException ITe) { + } + return data; + } + + /** + * Log an Object + * + * @param loglevel The level to log at (DEBUG, WARN, etc) + * @param o The object to log + */ + public static void print(int loglevel, Object o) { + if (debug) { + String timestr = ""; + String[] data = getTraceElements(); + if (debugging(data[0], loglevel)) { + if (timing) { + long now = System.currentTimeMillis(); + timestr = "{" + (now - last) + "} "; + last = now; + } + _print(o.getClass(), loglevel, data[0] + "." + data[1] + "()" + data[2], timestr, o.toString(), null); + } + } + } + + /** + * Log a Map + * + * @param o The object doing the logging + * @param loglevel The level to log at (DEBUG, WARN, etc) + * @param m The Map to print out + * @deprecated In Java 1.5 calling class is automatically identified, no need to pass it in. + */ + //TODO: 1.5 @Deprecated() + public static void printMap(Object o, int loglevel, Map m) { + if (debug) { + if (o instanceof Class) + saveclass = (Class) o; + else + saveclass = o.getClass(); + printMap(loglevel, m); + } + } + + /** + * Log a Map + * + * @param c The class doing the logging + * @param loglevel The level to log at (DEBUG, WARN, etc) + * @param m The Map to print out + * @deprecated In Java 1.5 calling class is automatically identified, no need to pass it in. + */ + //TODO: 1.5 @Deprecated() + public static void printMap(Class c, int loglevel, Map m) { + if (debug) { + saveclass = c; + printMap(loglevel, m); + } + } + + /** + * Log a Map at DEBUG log level + * + * @param m The Map to print out + */ + public static void printMap(Map m) { + printMap(DEBUG, m); + } + + /** + * Log a Map + * + * @param loglevel The level to log at (DEBUG, WARN, etc) + * @param m The Map to print out + */ + public static void printMap(int loglevel, Map m) { + if (debug) { + String timestr = ""; + String[] data = getTraceElements(); + if (debugging(data[0], loglevel)) { + if (timing) { + long now = System.currentTimeMillis(); + timestr = "{" + (now - last) + "} "; + last = now; + } + Iterator i = m.keySet().iterator(); + String[] lines = new String[m.size()]; + int j = 0; + while (i.hasNext()) { + Object key = i.next(); + lines[j++] = "\t\t- " + key + " => " + m.get(key); + } + _print(m.getClass(), loglevel, data[0] + "." + data[1] + "()" + data[2], timestr, "Map:", lines); + } + } + } + + /** + * Enable or disable stack traces in Debuging throwables. + */ + public static void setThrowableTraces(boolean ttrace) { + Debug.ttrace = ttrace; + } + + /** + * Enable or disable timing in Debug messages. + */ + public static void setTiming(boolean timing) { + Debug.timing = timing; + } + + /** + * Enable or disable line numbers. + */ + public static void setLineNos(boolean lines) { + Debug.lines = lines; + } + + /** + * Enable or disable hexdumps. + */ + public static void setHexDump(boolean hexdump) { + Debug.hexdump = hexdump; + } + + /** + * Set the size of hexdumps. + * (Default: 36) + */ + public static void setByteArrayCount(int count) { + Debug.balen = count; + } + + /** + * Set the formatted width of hexdumps. + * (Default: 80 chars) + */ + public static void setByteArrayWidth(int width) { + Debug.bawidth = width; + } + + /** + * Add a filter command for a specific type. + * This command will be called with the output stream + * and the text to be sent. It should perform any + * changes necessary to the text and then print the + * result to the output stream. + */ + public static void addFilterCommand(Class c, FilterCommand f) + //TODO 1.5: public static void addFilterCommand(Class c, FilterCommand f) + { + filterMap.put(c, f); + } + + private static void _print(Class c, int level, String loc, String extra, String message, String[] lines) { + //TODO 1.5: FilterCommand f = filterMap.get(c); + FilterCommand f = (FilterCommand) filterMap.get(c); + if (null == f) { + debugout.println("[" + loc + "] " + extra + message); + if (null != lines) + for (String s : lines) + debugout.println(s); + } else + f.filter(debugout, level, loc, extra, message, lines); + } +} diff --git a/federation/sssd/src/main/java/cx/ath/matthew/unix/NotConnectedException.java b/federation/sssd/src/main/java/cx/ath/matthew/unix/NotConnectedException.java new file mode 100644 index 0000000000..836f5d6229 --- /dev/null +++ b/federation/sssd/src/main/java/cx/ath/matthew/unix/NotConnectedException.java @@ -0,0 +1,35 @@ +/* + * Java Unix Sockets Library + * + * Copyright (c) Matthew Johnson 2004 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * To Contact the author, please email src@matthew.ath.cx + * + */ +package cx.ath.matthew.unix; + +import java.net.SocketException; + +public class NotConnectedException extends SocketException { + public NotConnectedException() { + super("The Socket is Not Connected"); + } +} diff --git a/federation/sssd/src/main/java/cx/ath/matthew/unix/USInputStream.java b/federation/sssd/src/main/java/cx/ath/matthew/unix/USInputStream.java new file mode 100644 index 0000000000..eb143fe6a9 --- /dev/null +++ b/federation/sssd/src/main/java/cx/ath/matthew/unix/USInputStream.java @@ -0,0 +1,94 @@ +/* + * Java Unix Sockets Library + * + * Copyright (c) Matthew Johnson 2004 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * To Contact the author, please email src@matthew.ath.cx + * + */ +package cx.ath.matthew.unix; + +import java.io.IOException; +import java.io.InputStream; + +public class USInputStream extends InputStream { + public static final int MSG_DONTWAIT = 0x40; + + private native int native_recv(int sock, byte[] b, int off, int len, int flags, int timeout) throws IOException; + + private int sock; + boolean closed = false; + private byte[] onebuf = new byte[1]; + private UnixSocket us; + private boolean blocking = true; + private int flags = 0; + private int timeout = 0; + + public USInputStream(int sock, UnixSocket us) { + this.sock = sock; + this.us = us; + } + + public void close() throws IOException { + closed = true; + us.close(); + } + + public boolean markSupported() { + return false; + } + + public int read() throws IOException { + int rv = 0; + while (0 >= rv) rv = read(onebuf); + if (-1 == rv) return -1; + return 0 > onebuf[0] ? -onebuf[0] : onebuf[0]; + } + + public int read(byte[] b, int off, int len) throws IOException { + if (closed) throw new NotConnectedException(); + int count = native_recv(sock, b, off, len, flags, timeout); + /* Yes, I really want to do this. Recv returns 0 for 'connection shut down'. + * read() returns -1 for 'end of stream. + * Recv returns -1 for 'EAGAIN' (all other errors cause an exception to be raised) + * whereas read() returns 0 for '0 bytes read', so yes, I really want to swap them here. + */ + if (0 == count) return -1; + else if (-1 == count) return 0; + else return count; + } + + public boolean isClosed() { + return closed; + } + + public UnixSocket getSocket() { + return us; + } + + public void setBlocking(boolean enable) { + flags = enable ? 0 : MSG_DONTWAIT; + } + + public void setSoTimeout(int timeout) { + this.timeout = timeout; + } +} diff --git a/federation/sssd/src/main/java/cx/ath/matthew/unix/USOutputStream.java b/federation/sssd/src/main/java/cx/ath/matthew/unix/USOutputStream.java new file mode 100644 index 0000000000..d8c85a7718 --- /dev/null +++ b/federation/sssd/src/main/java/cx/ath/matthew/unix/USOutputStream.java @@ -0,0 +1,78 @@ +/* + * Java Unix Sockets Library + * + * Copyright (c) Matthew Johnson 2004 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * To Contact the author, please email src@matthew.ath.cx + * + */ +package cx.ath.matthew.unix; + +import java.io.IOException; +import java.io.OutputStream; + +public class USOutputStream extends OutputStream { + private native int native_send(int sock, byte[] b, int off, int len) throws IOException; + + private native int native_send(int sock, byte[][] b) throws IOException; + + private int sock; + boolean closed = false; + private byte[] onebuf = new byte[1]; + private UnixSocket us; + + public USOutputStream(int sock, UnixSocket us) { + this.sock = sock; + this.us = us; + } + + public void close() throws IOException { + closed = true; + us.close(); + } + + public void flush() { + } // no-op, we do not buffer + + public void write(byte[][] b) throws IOException { + if (closed) throw new NotConnectedException(); + native_send(sock, b); + } + + public void write(byte[] b, int off, int len) throws IOException { + if (closed) throw new NotConnectedException(); + native_send(sock, b, off, len); + } + + public void write(int b) throws IOException { + onebuf[0] = (byte) (b % 0x7F); + if (1 == (b % 0x80)) onebuf[0] = (byte) -onebuf[0]; + write(onebuf); + } + + public boolean isClosed() { + return closed; + } + + public UnixSocket getSocket() { + return us; + } +} diff --git a/federation/sssd/src/main/java/cx/ath/matthew/unix/UnixIOException.java b/federation/sssd/src/main/java/cx/ath/matthew/unix/UnixIOException.java new file mode 100644 index 0000000000..24fd20cd1f --- /dev/null +++ b/federation/sssd/src/main/java/cx/ath/matthew/unix/UnixIOException.java @@ -0,0 +1,43 @@ +/* + * Java Unix Sockets Library + * + * Copyright (c) Matthew Johnson 2004 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * To Contact the author, please email src@matthew.ath.cx + * + */ +package cx.ath.matthew.unix; + +import java.io.IOException; + +/** + * An IO Exception which occurred during UNIX Socket IO + */ +public class UnixIOException extends IOException { + private int no; + private String message; + + public UnixIOException(int no, String message) { + super(message); + this.message = message; + this.no = no; + } +} diff --git a/federation/sssd/src/main/java/cx/ath/matthew/unix/UnixSocket.java b/federation/sssd/src/main/java/cx/ath/matthew/unix/UnixSocket.java new file mode 100644 index 0000000000..8851637436 --- /dev/null +++ b/federation/sssd/src/main/java/cx/ath/matthew/unix/UnixSocket.java @@ -0,0 +1,350 @@ +/* + * Java Unix Sockets Library + * + * Copyright (c) Matthew Johnson 2004 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * To Contact the author, please email src@matthew.ath.cx + * + */ +package cx.ath.matthew.unix; + +import cx.ath.matthew.LibraryLoader; +import cx.ath.matthew.debug.Debug; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * Represents a UnixSocket. + */ +public class UnixSocket { + static { + LibraryLoader.load(); + } + + private native void native_set_pass_cred(int sock, boolean passcred) throws IOException; + + private native int native_connect(String address, boolean abs) throws IOException; + + private native void native_close(int sock) throws IOException; + + private native int native_getPID(int sock); + + private native int native_getUID(int sock); + + private native int native_getGID(int sock); + + private native void native_send_creds(int sock, byte data) throws IOException; + + private native byte native_recv_creds(int sock, int[] creds) throws IOException; + + private UnixSocketAddress address = null; + private USOutputStream os = null; + private USInputStream is = null; + private boolean closed = false; + private boolean connected = false; + private boolean passcred = false; + private int sock = 0; + private boolean blocking = true; + private int uid = -1; + private int pid = -1; + private int gid = -1; + + UnixSocket(int sock, UnixSocketAddress address) { + this.sock = sock; + this.address = address; + this.connected = true; + this.os = new USOutputStream(sock, this); + this.is = new USInputStream(sock, this); + } + + /** + * Create an unconnected socket. + */ + public UnixSocket() { + } + + /** + * Create a socket connected to the given address. + * + * @param address The Unix Socket address to connect to + */ + public UnixSocket(UnixSocketAddress address) throws IOException { + connect(address); + } + + /** + * Create a socket connected to the given address. + * + * @param address The Unix Socket address to connect to + */ + public UnixSocket(String address) throws IOException { + this(new UnixSocketAddress(address)); + } + + /** + * Connect the socket to this address. + * + * @param address The Unix Socket address to connect to + */ + public void connect(UnixSocketAddress address) throws IOException { + if (connected) close(); + this.sock = native_connect(address.path, address.abs); + this.os = new USOutputStream(this.sock, this); + this.is = new USInputStream(this.sock, this); + this.address = address; + this.connected = true; + this.closed = false; + this.is.setBlocking(blocking); + } + + /** + * Connect the socket to this address. + * + * @param address The Unix Socket address to connect to + */ + public void connect(String address) throws IOException { + connect(new UnixSocketAddress(address)); + } + + public void finalize() { + try { + close(); + } catch (IOException IOe) { + } + } + + /** + * Closes the connection. + */ + public synchronized void close() throws IOException { + if (Debug.debug) Debug.print(Debug.INFO, "Closing socket"); + native_close(sock); + sock = 0; + this.closed = true; + this.connected = false; + os = null; + is = null; + } + + /** + * Returns an InputStream for reading from the socket. + * + * @return An InputStream connected to this socket. + */ + public InputStream getInputStream() { + return is; + } + + /** + * Returns an OutputStream for writing to the socket. + * + * @return An OutputStream connected to this socket. + */ + public OutputStream getOutputStream() { + return os; + } + + /** + * Returns the address this socket is connected to. + * Returns null if the socket is unconnected. + * + * @return The UnixSocketAddress the socket is connected to + */ + public UnixSocketAddress getAddress() { + return address; + } + + /** + * Send a single byte of data with credentials. + * (Works on BSDs) + * + * @param data The byte of data to send. + */ + public void sendCredentialByte(byte data) throws IOException { + if (!connected) throw new NotConnectedException(); + native_send_creds(sock, data); + } + + /** + * Receive a single byte of data, with credentials. + * (Works on BSDs) + * + * @param data The byte of data to send. + * @see getPeerUID + * @see getPeerPID + * @see getPeerGID + */ + public byte recvCredentialByte() throws IOException { + if (!connected) throw new NotConnectedException(); + int[] creds = new int[]{-1, -1, -1}; + byte data = native_recv_creds(sock, creds); + pid = creds[0]; + uid = creds[1]; + gid = creds[2]; + return data; + } + + /** + * Get the credential passing status. + * (only effective on linux) + * + * @return The current status of credential passing. + * @see setPassCred + */ + public boolean getPassCred() { + return passcred; + } + + /** + * Return the uid of the remote process. + * Some data must have been received on the socket to do this. + * Either setPassCred must be called on Linux first, or recvCredentialByte + * on BSD. + * + * @return the UID or -1 if it is not available + */ + public int getPeerUID() { + if (-1 == uid) + uid = native_getUID(sock); + return uid; + } + + /** + * Return the gid of the remote process. + * Some data must have been received on the socket to do this. + * Either setPassCred must be called on Linux first, or recvCredentialByte + * on BSD. + * + * @return the GID or -1 if it is not available + */ + public int getPeerGID() { + if (-1 == gid) + gid = native_getGID(sock); + return gid; + } + + /** + * Return the pid of the remote process. + * Some data must have been received on the socket to do this. + * Either setPassCred must be called on Linux first, or recvCredentialByte + * on BSD. + * + * @return the PID or -1 if it is not available + */ + public int getPeerPID() { + if (-1 == pid) + pid = native_getPID(sock); + return pid; + } + + /** + * Set the credential passing status. + * (Only does anything on linux, for other OS, you need + * to use send/recv credentials) + * + * @param enable Set to true for credentials to be passed. + */ + public void setPassCred(boolean enable) throws IOException { + native_set_pass_cred(sock, enable); + passcred = enable; + } + + /** + * Get the blocking mode. + * + * @return true if reads are blocking. + * @see setBlocking + */ + public boolean getBlocking() { + return blocking; + } + + /** + * Set the blocking mode. + * + * @param enable Set to false for non-blocking reads. + */ + public void setBlocking(boolean enable) { + blocking = enable; + if (null != is) is.setBlocking(enable); + } + + /** + * Check the socket status. + * + * @return true if closed. + */ + public boolean isClosed() { + return closed; + } + + /** + * Check the socket status. + * + * @return true if connected. + */ + public boolean isConnected() { + return connected; + } + + /** + * Check the socket status. + * + * @return true if the input stream has been shutdown + */ + public boolean isInputShutdown() { + return is.isClosed(); + } + + /** + * Check the socket status. + * + * @return true if the output stream has been shutdown + */ + public boolean isOutputShutdown() { + return os.isClosed(); + } + + /** + * Shuts down the input stream. + * Subsequent reads on the associated InputStream will fail. + */ + public void shutdownInput() { + is.closed = true; + } + + /** + * Shuts down the output stream. + * Subsequent writes to the associated OutputStream will fail. + */ + public void shutdownOutput() { + os.closed = true; + } + + /** + * Set timeout of read requests. + */ + public void setSoTimeout(int timeout) { + is.setSoTimeout(timeout); + } +} diff --git a/federation/sssd/src/main/java/cx/ath/matthew/unix/UnixSocketAddress.java b/federation/sssd/src/main/java/cx/ath/matthew/unix/UnixSocketAddress.java new file mode 100644 index 0000000000..0baba479bb --- /dev/null +++ b/federation/sssd/src/main/java/cx/ath/matthew/unix/UnixSocketAddress.java @@ -0,0 +1,86 @@ +/* + * Java Unix Sockets Library + * + * Copyright (c) Matthew Johnson 2004 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * To Contact the author, please email src@matthew.ath.cx + * + */ +package cx.ath.matthew.unix; + +/** + * Represents an address for a Unix Socket + */ +public class UnixSocketAddress { + String path; + boolean abs; + + /** + * Create the address. + * + * @param path The path to the Unix Socket. + * @param abs True if this should be an abstract socket. + */ + public UnixSocketAddress(String path, boolean abs) { + this.path = path; + this.abs = abs; + } + + /** + * Create the address. + * + * @param path The path to the Unix Socket. + */ + public UnixSocketAddress(String path) { + this.path = path; + this.abs = false; + } + + /** + * Return the path. + */ + public String getPath() { + return path; + } + + /** + * Returns true if this an address for an abstract socket. + */ + public boolean isAbstract() { + return abs; + } + + /** + * Return the Address as a String. + */ + public String toString() { + return "unix" + (abs ? ":abstract" : "") + ":path=" + path; + } + + public boolean equals(Object o) { + if (!(o instanceof UnixSocketAddress)) return false; + return ((UnixSocketAddress) o).path.equals(this.path); + } + + public int hashCode() { + return path.hashCode(); + } +} diff --git a/federation/sssd/src/main/java/cx/ath/matthew/utils/Hexdump.java b/federation/sssd/src/main/java/cx/ath/matthew/utils/Hexdump.java new file mode 100644 index 0000000000..63f37194db --- /dev/null +++ b/federation/sssd/src/main/java/cx/ath/matthew/utils/Hexdump.java @@ -0,0 +1,147 @@ +/* + * Java Hexdump Library + * + * Copyright (c) Matthew Johnson 2005 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * To Contact the author, please email src@matthew.ath.cx + * + */ + +package cx.ath.matthew.utils; + +import java.io.PrintStream; + +public class Hexdump { + public static final char[] hexchars = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + + public static String toHex(byte[] buf) { + return toHex(buf, 0, buf.length); + } + + public static String toHex(byte[] buf, int ofs, int len) { + StringBuffer sb = new StringBuffer(); + int j = ofs + len; + for (int i = ofs; i < j; i++) { + if (i < buf.length) { + sb.append(hexchars[(buf[i] & 0xF0) >> 4]); + sb.append(hexchars[buf[i] & 0x0F]); + sb.append(' '); + } else { + sb.append(' '); + sb.append(' '); + sb.append(' '); + } + } + return sb.toString(); + } + + public static String toAscii(byte[] buf) { + return toAscii(buf, 0, buf.length); + } + + public static String toAscii(byte[] buf, int ofs, int len) { + StringBuffer sb = new StringBuffer(); + int j = ofs + len; + for (int i = ofs; i < j; i++) { + if (i < buf.length) { + if (20 <= buf[i] && 126 >= buf[i]) + sb.append((char) buf[i]); + else + sb.append('.'); + } else + sb.append(' '); + } + return sb.toString(); + } + + public static String format(byte[] buf) { + return format(buf, 80); + } + + public static String format(byte[] buf, int width) { + int bs = (width - 8) / 4; + int i = 0; + StringBuffer sb = new StringBuffer(); + do { + for (int j = 0; j < 6; j++) { + sb.append(hexchars[(i << (j * 4) & 0xF00000) >> 20]); + } + sb.append('\t'); + sb.append(toHex(buf, i, bs)); + sb.append(' '); + sb.append(toAscii(buf, i, bs)); + sb.append('\n'); + i += bs; + } while (i < buf.length); + return sb.toString(); + } + + public static void print(byte[] buf) { + print(buf, System.err); + } + + public static void print(byte[] buf, int width) { + print(buf, width, System.err); + } + + public static void print(byte[] buf, int width, PrintStream out) { + out.print(format(buf, width)); + } + + public static void print(byte[] buf, PrintStream out) { + out.print(format(buf)); + } + + /** + * Returns a string which can be written to a Java source file as part + * of a static initializer for a byte array. + * Returns data in the format 0xAB, 0xCD, .... + * use like: + * javafile.print("byte[] data = {") + * javafile.print(Hexdump.toByteArray(data)); + * javafile.println("};"); + */ + public static String toByteArray(byte[] buf) { + return toByteArray(buf, 0, buf.length); + } + + /** + * Returns a string which can be written to a Java source file as part + * of a static initializer for a byte array. + * Returns data in the format 0xAB, 0xCD, .... + * use like: + * javafile.print("byte[] data = {") + * javafile.print(Hexdump.toByteArray(data)); + * javafile.println("};"); + */ + public static String toByteArray(byte[] buf, int ofs, int len) { + StringBuffer sb = new StringBuffer(); + for (int i = ofs; i < len && i < buf.length; i++) { + sb.append('0'); + sb.append('x'); + sb.append(hexchars[(buf[i] & 0xF0) >> 4]); + sb.append(hexchars[buf[i] & 0x0F]); + if ((i + 1) < len && (i + 1) < buf.length) + sb.append(','); + } + return sb.toString(); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/DBus.java b/federation/sssd/src/main/java/org/freedesktop/DBus.java new file mode 100644 index 0000000000..1aa180a4ab --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/DBus.java @@ -0,0 +1,530 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop; + +import org.freedesktop.dbus.DBusInterface; +import org.freedesktop.dbus.DBusSignal; +import org.freedesktop.dbus.Position; +import org.freedesktop.dbus.Struct; +import org.freedesktop.dbus.Tuple; +import org.freedesktop.dbus.UInt16; +import org.freedesktop.dbus.UInt32; +import org.freedesktop.dbus.UInt64; +import org.freedesktop.dbus.Variant; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.exceptions.DBusExecutionException; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.List; +import java.util.Map; + +public interface DBus extends DBusInterface { + public static final int DBUS_NAME_FLAG_ALLOW_REPLACEMENT = 0x01; + public static final int DBUS_NAME_FLAG_REPLACE_EXISTING = 0x02; + public static final int DBUS_NAME_FLAG_DO_NOT_QUEUE = 0x04; + public static final int DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER = 1; + public static final int DBUS_REQUEST_NAME_REPLY_IN_QUEUE = 2; + public static final int DBUS_REQUEST_NAME_REPLY_EXISTS = 3; + public static final int DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER = 4; + public static final int DBUS_RELEASE_NAME_REPLY_RELEASED = 1; + public static final int DBUS_RELEASE_NAME_REPLY_NON_EXISTANT = 2; + public static final int DBUS_RELEASE_NAME_REPLY_NOT_OWNER = 3; + public static final int DBUS_START_REPLY_SUCCESS = 1; + public static final int DBUS_START_REPLY_ALREADY_RUNNING = 2; + + /** + * All DBus Applications should respond to the Ping method on this interface + */ + public interface Peer extends DBusInterface { + public void Ping(); + } + + /** + * Objects can provide introspection data via this interface and method. + * See the Introspection Format. + */ + public interface Introspectable extends DBusInterface { + /** + * @return The XML introspection data for this object + */ + public String Introspect(); + } + + /** + * A standard properties interface. + */ + public interface Properties extends DBusInterface { + /** + * Get the value for the given property. + * + * @param interface_name The interface this property is associated with. + * @param property_name The name of the property. + * @return The value of the property (may be any valid DBus type). + */ + public A Get(String interface_name, String property_name); + + /** + * Set the value for the given property. + * + * @param interface_name The interface this property is associated with. + * @param property_name The name of the property. + * @param value The new value of the property (may be any valid DBus type). + */ + public void Set(String interface_name, String property_name, A value); + + /** + * Get all properties and values. + * + * @param interface_name The interface the properties is associated with. + * @return The properties mapped to their values. + */ + public Map GetAll(String interface_name); + } + + /** + * Messages generated locally in the application. + */ + public interface Local extends DBusInterface { + public class Disconnected extends DBusSignal { + public Disconnected(String path) throws DBusException { + super(path); + } + } + } + + /** + * Initial message to register ourselves on the Bus. + * + * @return The unique name of this connection to the Bus. + */ + public String Hello(); + + /** + * Lists all connected names on the Bus. + * + * @return An array of all connected names. + */ + public String[] ListNames(); + + /** + * Determine if a name has an owner. + * + * @param name The name to query. + * @return true if the name has an owner. + */ + public boolean NameHasOwner(String name); + + /** + * Get the connection unique name that owns the given name. + * + * @param name The name to query. + * @return The connection which owns the name. + */ + public String GetNameOwner(String name); + + /** + * Get the Unix UID that owns a connection name. + * + * @param connection_name The connection name. + * @return The Unix UID that owns it. + */ + public UInt32 GetConnectionUnixUser(String connection_name); + + /** + * Start a service. If the given service is not provided + * by any application, it will be started according to the .service file + * for that service. + * + * @param name The service name to start. + * @param flags Unused. + * @return DBUS_START_REPLY constants. + */ + public UInt32 StartServiceByName(String name, UInt32 flags); + + /** + * Request a name on the bus. + * + * @param name The name to request. + * @param flags DBUS_NAME flags. + * @return DBUS_REQUEST_NAME_REPLY constants. + */ + public UInt32 RequestName(String name, UInt32 flags); + + /** + * Release a name on the bus. + * + * @param name The name to release. + * @return DBUS_RELEASE_NAME_REPLY constants. + */ + public UInt32 ReleaseName(String name); + + /** + * Add a match rule. + * Will cause you to receive messages that aren't directed to you which + * match this rule. + * + * @param matchrule The Match rule as a string. Format Undocumented. + */ + public void AddMatch(String matchrule) throws Error.MatchRuleInvalid; + + /** + * Remove a match rule. + * Will cause you to stop receiving messages that aren't directed to you which + * match this rule. + * + * @param matchrule The Match rule as a string. Format Undocumented. + */ + public void RemoveMatch(String matchrule) throws Error.MatchRuleInvalid; + + /** + * List the connections currently queued for a name. + * + * @param name The name to query + * @return A list of unique connection IDs. + */ + public String[] ListQueuedOwners(String name); + + /** + * Returns the proccess ID associated with a connection. + * + * @param connection_name The name of the connection + * @return The PID of the connection. + */ + public UInt32 GetConnectionUnixProcessID(String connection_name); + + /** + * Does something undocumented. + */ + public Byte[] GetConnectionSELinuxSecurityContext(String a); + + /** + * Does something undocumented. + */ + public void ReloadConfig(); + + /** + * Signal sent when the owner of a name changes + */ + public class NameOwnerChanged extends DBusSignal { + public final String name; + public final String old_owner; + public final String new_owner; + + public NameOwnerChanged(String path, String name, String old_owner, String new_owner) throws DBusException { + super(path, new Object[]{name, old_owner, new_owner}); + this.name = name; + this.old_owner = old_owner; + this.new_owner = new_owner; + } + } + + /** + * Signal sent to a connection when it loses a name + */ + public class NameLost extends DBusSignal { + public final String name; + + public NameLost(String path, String name) throws DBusException { + super(path, name); + this.name = name; + } + } + + /** + * Signal sent to a connection when it aquires a name + */ + public class NameAcquired extends DBusSignal { + public final String name; + + public NameAcquired(String path, String name) throws DBusException { + super(path, name); + this.name = name; + } + } + + /** + * Contains standard errors that can be thrown from methods. + */ + public interface Error { + /** + * Thrown if the method called was unknown on the remote object + */ + @SuppressWarnings("serial") + public class UnknownMethod extends DBusExecutionException { + public UnknownMethod(String message) { + super(message); + } + } + + /** + * Thrown if the object was unknown on a remote connection + */ + @SuppressWarnings("serial") + public class UnknownObject extends DBusExecutionException { + public UnknownObject(String message) { + super(message); + } + } + + /** + * Thrown if the requested service was not available + */ + @SuppressWarnings("serial") + public class ServiceUnknown extends DBusExecutionException { + public ServiceUnknown(String message) { + super(message); + } + } + + /** + * Thrown if the match rule is invalid + */ + @SuppressWarnings("serial") + public class MatchRuleInvalid extends DBusExecutionException { + public MatchRuleInvalid(String message) { + super(message); + } + } + + /** + * Thrown if there is no reply to a method call + */ + @SuppressWarnings("serial") + public class NoReply extends DBusExecutionException { + public NoReply(String message) { + super(message); + } + } + + /** + * Thrown if a message is denied due to a security policy + */ + @SuppressWarnings("serial") + public class AccessDenied extends DBusExecutionException { + public AccessDenied(String message) { + super(message); + } + } + } + + /** + * Description of the interface or method, returned in the introspection data + */ + @Retention(RetentionPolicy.RUNTIME) + public @interface Description { + String value(); + } + + /** + * Indicates that a DBus interface or method is deprecated + */ + @Retention(RetentionPolicy.RUNTIME) + public @interface Deprecated { + } + + /** + * Contains method-specific annotations + */ + public interface Method { + /** + * Methods annotated with this do not send a reply + */ + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + public @interface NoReply { + } + + /** + * Give an error that the method can return + */ + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + public @interface Error { + String value(); + } + } + + /** + * Contains GLib-specific annotations + */ + public interface GLib { + /** + * Define a C symbol to map to this method. Used by GLib only + */ + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + public @interface CSymbol { + String value(); + } + } + + /** + * Contains Binding-test interfaces + */ + public interface Binding { + public interface SingleTests extends DBusInterface { + @Description("Returns the sum of the values in the input list") + public UInt32 Sum(byte[] a); + } + + public interface TestClient extends DBusInterface { + @Description("when the trigger signal is received, this method should be called on the sending process/object.") + public void Response(UInt16 a, double b); + + @Description("Causes a callback") + public static class Trigger extends DBusSignal { + public final UInt16 a; + public final double b; + + public Trigger(String path, UInt16 a, double b) throws DBusException { + super(path, a, b); + this.a = a; + this.b = b; + } + } + + } + + public interface Tests extends DBusInterface { + @Description("Returns whatever it is passed") + public Variant Identity(Variant input); + + @Description("Returns whatever it is passed") + public byte IdentityByte(byte input); + + @Description("Returns whatever it is passed") + public boolean IdentityBool(boolean input); + + @Description("Returns whatever it is passed") + public short IdentityInt16(short input); + + @Description("Returns whatever it is passed") + public UInt16 IdentityUInt16(UInt16 input); + + @Description("Returns whatever it is passed") + public int IdentityInt32(int input); + + @Description("Returns whatever it is passed") + public UInt32 IdentityUInt32(UInt32 input); + + @Description("Returns whatever it is passed") + public long IdentityInt64(long input); + + @Description("Returns whatever it is passed") + public UInt64 IdentityUInt64(UInt64 input); + + @Description("Returns whatever it is passed") + public double IdentityDouble(double input); + + @Description("Returns whatever it is passed") + public String IdentityString(String input); + + @Description("Returns whatever it is passed") + public Variant[] IdentityArray(Variant[] input); + + @Description("Returns whatever it is passed") + public byte[] IdentityByteArray(byte[] input); + + @Description("Returns whatever it is passed") + public boolean[] IdentityBoolArray(boolean[] input); + + @Description("Returns whatever it is passed") + public short[] IdentityInt16Array(short[] input); + + @Description("Returns whatever it is passed") + public UInt16[] IdentityUInt16Array(UInt16[] input); + + @Description("Returns whatever it is passed") + public int[] IdentityInt32Array(int[] input); + + @Description("Returns whatever it is passed") + public UInt32[] IdentityUInt32Array(UInt32[] input); + + @Description("Returns whatever it is passed") + public long[] IdentityInt64Array(long[] input); + + @Description("Returns whatever it is passed") + public UInt64[] IdentityUInt64Array(UInt64[] input); + + @Description("Returns whatever it is passed") + public double[] IdentityDoubleArray(double[] input); + + @Description("Returns whatever it is passed") + public String[] IdentityStringArray(String[] input); + + @Description("Returns the sum of the values in the input list") + public long Sum(int[] a); + + @Description("Given a map of A => B, should return a map of B => a list of all the As which mapped to B") + public Map> InvertMapping(Map a); + + @Description("This method returns the contents of a struct as separate values") + public Triplet DeStruct(TestStruct a); + + @Description("Given any compound type as a variant, return all the primitive types recursively contained within as an array of variants") + public List> Primitize(Variant a); + + @Description("inverts it's input") + public boolean Invert(boolean a); + + @Description("triggers sending of a signal from the supplied object with the given parameter") + public void Trigger(String a, UInt64 b); + + @Description("Causes the server to exit") + public void Exit(); + } + + public interface TestSignals extends DBusInterface { + @Description("Sent in response to a method call") + public static class Triggered extends DBusSignal { + public final UInt64 a; + + public Triggered(String path, UInt64 a) throws DBusException { + super(path, a); + this.a = a; + } + } + } + + public final class Triplet extends Tuple { + @Position(0) + public final A a; + @Position(1) + public final B b; + @Position(2) + public final C c; + + public Triplet(A a, B b, C c) { + this.a = a; + this.b = b; + this.c = c; + } + } + + public final class TestStruct extends Struct { + @Position(0) + public final String a; + @Position(1) + public final UInt32 b; + @Position(2) + public final Short c; + + public TestStruct(String a, UInt32 b, Short c) { + this.a = a; + this.b = b; + this.c = c; + } + } + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/AbstractConnection.java b/federation/sssd/src/main/java/org/freedesktop/dbus/AbstractConnection.java new file mode 100644 index 0000000000..d1d7f51fb5 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/AbstractConnection.java @@ -0,0 +1,1059 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import cx.ath.matthew.debug.Debug; +import org.freedesktop.DBus; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.exceptions.DBusExecutionException; +import org.freedesktop.dbus.exceptions.FatalDBusException; +import org.freedesktop.dbus.exceptions.FatalException; +import org.freedesktop.dbus.exceptions.NotConnected; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.text.MessageFormat; +import java.text.ParseException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.Properties; +import java.util.Vector; +import java.util.regex.Pattern; + +import static org.freedesktop.dbus.Gettext.getString; + + +/** + * Handles a connection to DBus. + */ +public abstract class AbstractConnection { + protected class FallbackContainer { + private Map fallbacks = new HashMap(); + + public synchronized void add(String path, ExportedObject eo) { + if (Debug.debug) Debug.print(Debug.DEBUG, "Adding fallback on " + path + " of " + eo); + fallbacks.put(path.split("/"), eo); + } + + public synchronized void remove(String path) { + if (Debug.debug) Debug.print(Debug.DEBUG, "Removing fallback on " + path); + fallbacks.remove(path.split("/")); + } + + public synchronized ExportedObject get(String path) { + int best = 0; + int i = 0; + ExportedObject bestobject = null; + String[] pathel = path.split("/"); + for (String[] fbpath : fallbacks.keySet()) { + if (Debug.debug) + Debug.print(Debug.VERBOSE, "Trying fallback path " + Arrays.deepToString(fbpath) + " to match " + Arrays.deepToString(pathel)); + for (i = 0; i < pathel.length && i < fbpath.length; i++) + if (!pathel[i].equals(fbpath[i])) break; + if (i > 0 && i == fbpath.length && i > best) + bestobject = fallbacks.get(fbpath); + if (Debug.debug) Debug.print(Debug.VERBOSE, "Matches " + i + " bestobject now " + bestobject); + } + if (Debug.debug) Debug.print(Debug.DEBUG, "Found fallback for " + path + " of " + bestobject); + return bestobject; + } + } + + protected class _thread extends Thread { + public _thread() { + setName("DBusConnection"); + } + + public void run() { + try { + Message m = null; + while (_run) { + m = null; + + // read from the wire + try { + // this blocks on outgoing being non-empty or a message being available. + m = readIncoming(); + if (m != null) { + if (Debug.debug) Debug.print(Debug.VERBOSE, "Got Incoming Message: " + m); + synchronized (this) { + notifyAll(); + } + + if (m instanceof DBusSignal) + handleMessage((DBusSignal) m); + else if (m instanceof MethodCall) + handleMessage((MethodCall) m); + else if (m instanceof MethodReturn) + handleMessage((MethodReturn) m); + else if (m instanceof Error) + handleMessage((Error) m); + + m = null; + } + } catch (Exception e) { + if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); + if (e instanceof FatalException) { + disconnect(); + } + } + + } + synchronized (this) { + notifyAll(); + } + } catch (Exception e) { + if (Debug.debug && EXCEPTION_DEBUG) Debug.print(Debug.ERR, e); + } + } + } + + private class _globalhandler implements org.freedesktop.DBus.Peer, org.freedesktop.DBus.Introspectable { + private String objectpath; + + public _globalhandler() { + this.objectpath = null; + } + + public _globalhandler(String objectpath) { + this.objectpath = objectpath; + } + + public boolean isRemote() { + return false; + } + + public void Ping() { + return; + } + + public String Introspect() { + String intro = objectTree.Introspect(objectpath); + if (null == intro) { + ExportedObject eo = fallbackcontainer.get(objectpath); + if (null != eo) intro = eo.introspectiondata; + } + if (null == intro) + throw new DBus.Error.UnknownObject("Introspecting on non-existant object"); + else return + "\n" + intro; + } + } + + protected class _workerthread extends Thread { + private boolean _run = true; + + public void halt() { + _run = false; + } + + public void run() { + while (_run) { + Runnable r = null; + synchronized (runnables) { + while (runnables.size() == 0 && _run) + try { + runnables.wait(); + } catch (InterruptedException Ie) { + } + if (runnables.size() > 0) + r = runnables.removeFirst(); + } + if (null != r) r.run(); + } + } + } + + private class _sender extends Thread { + public _sender() { + setName("Sender"); + } + + public void run() { + Message m = null; + + if (Debug.debug) Debug.print(Debug.INFO, "Monitoring outbound queue"); + // block on the outbound queue and send from it + while (_run) { + if (null != outgoing) synchronized (outgoing) { + if (Debug.debug) Debug.print(Debug.VERBOSE, "Blocking"); + while (outgoing.size() == 0 && _run) + try { + outgoing.wait(); + } catch (InterruptedException Ie) { + } + if (Debug.debug) Debug.print(Debug.VERBOSE, "Notified"); + if (outgoing.size() > 0) + m = outgoing.remove(); + if (Debug.debug) Debug.print(Debug.DEBUG, "Got message: " + m); + } + if (null != m) + sendMessage(m); + m = null; + } + + if (Debug.debug) Debug.print(Debug.INFO, "Flushing outbound queue and quitting"); + // flush the outbound queue before disconnect. + if (null != outgoing) do { + EfficientQueue ogq = outgoing; + synchronized (ogq) { + outgoing = null; + } + if (!ogq.isEmpty()) + m = ogq.remove(); + else m = null; + sendMessage(m); + } while (null != m); + + // close the underlying streams + } + } + + /** + * Timeout in us on checking the BUS for incoming messages and sending outgoing messages + */ + protected static final int TIMEOUT = 100000; + /** + * Initial size of the pending calls map + */ + private static final int PENDING_MAP_INITIAL_SIZE = 10; + static final String BUSNAME_REGEX = "^[-_a-zA-Z][-_a-zA-Z0-9]*(\\.[-_a-zA-Z][-_a-zA-Z0-9]*)*$"; + static final String CONNID_REGEX = "^:[0-9]*\\.[0-9]*$"; + static final String OBJECT_REGEX = "^/([-_a-zA-Z0-9]+(/[-_a-zA-Z0-9]+)*)?$"; + static final byte THREADCOUNT = 4; + static final int MAX_ARRAY_LENGTH = 67108864; + static final int MAX_NAME_LENGTH = 255; + protected Map exportedObjects; + private ObjectTree objectTree; + private _globalhandler _globalhandlerreference; + protected Map importedObjects; + protected Map>> handledSignals; + protected EfficientMap pendingCalls; + protected Map> pendingCallbacks; + protected Map> pendingCallbackReplys; + protected LinkedList runnables; + protected LinkedList<_workerthread> workers; + protected FallbackContainer fallbackcontainer; + protected boolean _run; + EfficientQueue outgoing; + LinkedList pendingErrors; + private static final Map infomap = new HashMap(); + protected _thread thread; + protected _sender sender; + protected Transport transport; + protected String addr; + protected boolean weakreferences = false; + static final Pattern dollar_pattern = Pattern.compile("[$]"); + public static final boolean EXCEPTION_DEBUG; + static final boolean FLOAT_SUPPORT; + protected boolean connected = false; + + static { + FLOAT_SUPPORT = (null != System.getenv("DBUS_JAVA_FLOATS")); + EXCEPTION_DEBUG = (null != System.getenv("DBUS_JAVA_EXCEPTION_DEBUG")); + if (EXCEPTION_DEBUG) { + Debug.print("Debugging of internal exceptions enabled"); + Debug.setThrowableTraces(true); + } + if (Debug.debug) { + File f = new File("debug.conf"); + if (f.exists()) { + Debug.print("Loading debug config file: " + f); + try { + Debug.loadConfig(f); + } catch (IOException IOe) { + } + } else { + Properties p = new Properties(); + p.setProperty("ALL", "INFO"); + Debug.print("debug config file " + f + " does not exist, not loading."); + } + Debug.setHexDump(true); + } + } + + protected AbstractConnection(String address) throws DBusException { + exportedObjects = new HashMap(); + importedObjects = new HashMap(); + _globalhandlerreference = new _globalhandler(); + synchronized (exportedObjects) { + exportedObjects.put(null, new ExportedObject(_globalhandlerreference, weakreferences)); + } + handledSignals = new HashMap>>(); + pendingCalls = new EfficientMap(PENDING_MAP_INITIAL_SIZE); + outgoing = new EfficientQueue(PENDING_MAP_INITIAL_SIZE); + pendingCallbacks = new HashMap>(); + pendingCallbackReplys = new HashMap>(); + pendingErrors = new LinkedList(); + runnables = new LinkedList(); + workers = new LinkedList<_workerthread>(); + objectTree = new ObjectTree(); + fallbackcontainer = new FallbackContainer(); + synchronized (workers) { + for (int i = 0; i < THREADCOUNT; i++) { + _workerthread t = new _workerthread(); + t.start(); + workers.add(t); + } + } + _run = true; + addr = address; + } + + protected void listen() { + // start listening + thread = new _thread(); + thread.start(); + sender = new _sender(); + sender.start(); + } + + /** + * Change the number of worker threads to receive method calls and handle signals. + * Default is 4 threads + * + * @param newcount The new number of worker Threads to use. + */ + public void changeThreadCount(byte newcount) { + synchronized (workers) { + if (workers.size() > newcount) { + int n = workers.size() - newcount; + for (int i = 0; i < n; i++) { + _workerthread t = workers.removeFirst(); + t.halt(); + } + } else if (workers.size() < newcount) { + int n = newcount - workers.size(); + for (int i = 0; i < n; i++) { + _workerthread t = new _workerthread(); + t.start(); + workers.add(t); + } + } + } + } + + private void addRunnable(Runnable r) { + synchronized (runnables) { + runnables.add(r); + runnables.notifyAll(); + } + } + + String getExportedObject(DBusInterface i) throws DBusException { + synchronized (exportedObjects) { + for (String s : exportedObjects.keySet()) + if (i.equals(exportedObjects.get(s).object.get())) + return s; + } + + String s = importedObjects.get(i).objectpath; + if (null != s) return s; + + throw new DBusException("Not an object exported or imported by this connection"); + } + + abstract DBusInterface getExportedObject(String source, String path) throws DBusException; + + /** + * Returns a structure with information on the current method call. + * + * @return the DBusCallInfo for this method call, or null if we are not in a method call. + */ + public static DBusCallInfo getCallInfo() { + DBusCallInfo info; + synchronized (infomap) { + info = infomap.get(Thread.currentThread()); + } + return info; + } + + /** + * If set to true the bus will not hold a strong reference to exported objects. + * If they go out of scope they will automatically be unexported from the bus. + * The default is to hold a strong reference, which means objects must be + * explicitly unexported before they will be garbage collected. + */ + public void setWeakReferences(boolean weakreferences) { + this.weakreferences = weakreferences; + } + + /** + * Export an object so that its methods can be called on DBus. + * + * @param objectpath The path to the object we are exposing. MUST be in slash-notation, like "/org/freedesktop/Local", + * and SHOULD end with a capitalised term. Only one object may be exposed on each path at any one time, but an object + * may be exposed on several paths at once. + * @param object The object to export. + * @throws DBusException If the objectpath is already exporting an object. + * or if objectpath is incorrectly formatted, + */ + public void exportObject(String objectpath, DBusInterface object) throws DBusException { + if (null == objectpath || "".equals(objectpath)) + throw new DBusException(getString("missingObjectPath")); + if (!objectpath.matches(OBJECT_REGEX) || objectpath.length() > MAX_NAME_LENGTH) + throw new DBusException(getString("invalidObjectPath") + objectpath); + synchronized (exportedObjects) { + if (null != exportedObjects.get(objectpath)) + throw new DBusException(getString("objectAlreadyExported")); + ExportedObject eo = new ExportedObject(object, weakreferences); + exportedObjects.put(objectpath, eo); + objectTree.add(objectpath, eo, eo.introspectiondata); + } + } + + /** + * Export an object as a fallback object. + * This object will have it's methods invoked for all paths starting + * with this object path. + * + * @param objectprefix The path below which the fallback handles calls. + * MUST be in slash-notation, like "/org/freedesktop/Local", + * @param object The object to export. + * @throws DBusException If the objectpath is incorrectly formatted, + */ + public void addFallback(String objectprefix, DBusInterface object) throws DBusException { + if (null == objectprefix || "".equals(objectprefix)) + throw new DBusException(getString("missingObjectPath")); + if (!objectprefix.matches(OBJECT_REGEX) || objectprefix.length() > MAX_NAME_LENGTH) + throw new DBusException(getString("invalidObjectPath") + objectprefix); + ExportedObject eo = new ExportedObject(object, weakreferences); + fallbackcontainer.add(objectprefix, eo); + } + + /** + * Remove a fallback + * + * @param objectprefix The prefix to remove the fallback for. + */ + public void removeFallback(String objectprefix) { + fallbackcontainer.remove(objectprefix); + } + + /** + * Stop Exporting an object + * + * @param objectpath The objectpath to stop exporting. + */ + public void unExportObject(String objectpath) { + synchronized (exportedObjects) { + exportedObjects.remove(objectpath); + objectTree.remove(objectpath); + } + } + /** + * Return a reference to a remote object. + * This method will resolve the well known name (if given) to a unique bus name when you call it. + * This means that if a well known name is released by one process and acquired by another calls to + * objects gained from this method will continue to operate on the original process. + * @param busname The bus name to connect to. Usually a well known bus name in dot-notation (such as "org.freedesktop.local") + * or may be a DBus address such as ":1-16". + * @param objectpath The path on which the process is exporting the object.$ + * @param type The interface they are exporting it on. This type must have the same full class name and exposed method signatures + * as the interface the remote object is exporting. + * @return A reference to a remote object. + * @throws ClassCastException If type is not a sub-type of DBusInterface + * @throws DBusException If busname or objectpath are incorrectly formatted or type is not in a package. + */ + /** + * Send a signal. + * + * @param signal The signal to send. + */ + public void sendSignal(DBusSignal signal) { + queueOutgoing(signal); + } + + void queueOutgoing(Message m) { + synchronized (outgoing) { + if (null == outgoing) return; + outgoing.add(m); + if (Debug.debug) Debug.print(Debug.DEBUG, "Notifying outgoing thread"); + outgoing.notifyAll(); + } + } + + /** + * Remove a Signal Handler. + * Stops listening for this signal. + * + * @param type The signal to watch for. + * @throws DBusException If listening for the signal on the bus failed. + * @throws ClassCastException If type is not a sub-type of DBusSignal. + */ + public void removeSigHandler(Class type, DBusSigHandler handler) throws DBusException { + if (!DBusSignal.class.isAssignableFrom(type)) throw new ClassCastException(getString("notDBusSignal")); + removeSigHandler(new DBusMatchRule(type), handler); + } + + /** + * Remove a Signal Handler. + * Stops listening for this signal. + * + * @param type The signal to watch for. + * @param object The object emitting the signal. + * @throws DBusException If listening for the signal on the bus failed. + * @throws ClassCastException If type is not a sub-type of DBusSignal. + */ + public void removeSigHandler(Class type, DBusInterface object, DBusSigHandler handler) throws DBusException { + if (!DBusSignal.class.isAssignableFrom(type)) throw new ClassCastException(getString("notDBusSignal")); + String objectpath = importedObjects.get(object).objectpath; + if (!objectpath.matches(OBJECT_REGEX) || objectpath.length() > MAX_NAME_LENGTH) + throw new DBusException(getString("invalidObjectPath") + objectpath); + removeSigHandler(new DBusMatchRule(type, null, objectpath), handler); + } + + protected abstract void removeSigHandler(DBusMatchRule rule, DBusSigHandler handler) throws DBusException; + + /** + * Add a Signal Handler. + * Adds a signal handler to call when a signal is received which matches the specified type and name. + * + * @param type The signal to watch for. + * @param handler The handler to call when a signal is received. + * @throws DBusException If listening for the signal on the bus failed. + * @throws ClassCastException If type is not a sub-type of DBusSignal. + */ + @SuppressWarnings("unchecked") + public void addSigHandler(Class type, DBusSigHandler handler) throws DBusException { + if (!DBusSignal.class.isAssignableFrom(type)) throw new ClassCastException(getString("notDBusSignal")); + addSigHandler(new DBusMatchRule(type), (DBusSigHandler) handler); + } + + /** + * Add a Signal Handler. + * Adds a signal handler to call when a signal is received which matches the specified type, name and object. + * + * @param type The signal to watch for. + * @param object The object from which the signal will be emitted + * @param handler The handler to call when a signal is received. + * @throws DBusException If listening for the signal on the bus failed. + * @throws ClassCastException If type is not a sub-type of DBusSignal. + */ + @SuppressWarnings("unchecked") + public void addSigHandler(Class type, DBusInterface object, DBusSigHandler handler) throws DBusException { + if (!DBusSignal.class.isAssignableFrom(type)) throw new ClassCastException(getString("notDBusSignal")); + String objectpath = importedObjects.get(object).objectpath; + if (!objectpath.matches(OBJECT_REGEX) || objectpath.length() > MAX_NAME_LENGTH) + throw new DBusException(getString("invalidObjectPath") + objectpath); + addSigHandler(new DBusMatchRule(type, null, objectpath), (DBusSigHandler) handler); + } + + protected abstract void addSigHandler(DBusMatchRule rule, DBusSigHandler handler) throws DBusException; + + protected void addSigHandlerWithoutMatch(Class signal, DBusSigHandler handler) throws DBusException { + DBusMatchRule rule = new DBusMatchRule(signal); + SignalTuple key = new SignalTuple(rule.getInterface(), rule.getMember(), rule.getObject(), rule.getSource()); + synchronized (handledSignals) { + Vector> v = handledSignals.get(key); + if (null == v) { + v = new Vector>(); + v.add(handler); + handledSignals.put(key, v); + } else + v.add(handler); + } + } + + /** + * Disconnect from the Bus. + */ + public void disconnect() { + connected = false; + if (Debug.debug) Debug.print(Debug.INFO, "Sending disconnected signal"); + try { + handleMessage(new org.freedesktop.DBus.Local.Disconnected("/")); + } catch (Exception ee) { + if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, ee); + } + + if (Debug.debug) Debug.print(Debug.INFO, "Disconnecting Abstract Connection"); + // run all pending tasks. + while (runnables.size() > 0) + synchronized (runnables) { + runnables.notifyAll(); + } + + // stop the main thread + _run = false; + + // unblock the sending thread. + synchronized (outgoing) { + outgoing.notifyAll(); + } + + // disconnect from the trasport layer + try { + if (null != transport) { + transport.disconnect(); + transport = null; + } + } catch (IOException IOe) { + if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, IOe); + } + + // stop all the workers + synchronized (workers) { + for (_workerthread t : workers) + t.halt(); + } + + // make sure none are blocking on the runnables queue still + synchronized (runnables) { + runnables.notifyAll(); + } + } + + public void finalize() { + disconnect(); + } + + /** + * Return any DBus error which has been received. + * + * @return A DBusExecutionException, or null if no error is pending. + */ + public DBusExecutionException getError() { + synchronized (pendingErrors) { + if (pendingErrors.size() == 0) return null; + else + return pendingErrors.removeFirst().getException(); + } + } + + /** + * Call a method asynchronously and set a callback. + * This handler will be called in a separate thread. + * + * @param object The remote object on which to call the method. + * @param m The name of the method on the interface to call. + * @param callback The callback handler. + * @param parameters The parameters to call the method with. + */ + @SuppressWarnings("unchecked") + public void callWithCallback(DBusInterface object, String m, CallbackHandler callback, Object... parameters) { + if (Debug.debug) Debug.print(Debug.VERBOSE, "callWithCallback(" + object + "," + m + ", " + callback); + Class[] types = new Class[parameters.length]; + for (int i = 0; i < parameters.length; i++) + types[i] = parameters[i].getClass(); + RemoteObject ro = importedObjects.get(object); + + try { + Method me; + if (null == ro.iface) + me = object.getClass().getMethod(m, types); + else + me = ro.iface.getMethod(m, types); + RemoteInvocationHandler.executeRemoteMethod(ro, me, this, RemoteInvocationHandler.CALL_TYPE_CALLBACK, callback, parameters); + } catch (DBusExecutionException DBEe) { + if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBEe); + throw DBEe; + } catch (Exception e) { + if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); + throw new DBusExecutionException(e.getMessage()); + } + } + + /** + * Call a method asynchronously and get a handle with which to get the reply. + * + * @param object The remote object on which to call the method. + * @param m The name of the method on the interface to call. + * @param parameters The parameters to call the method with. + * @return A handle to the call. + */ + @SuppressWarnings("unchecked") + public DBusAsyncReply callMethodAsync(DBusInterface object, String m, Object... parameters) { + Class[] types = new Class[parameters.length]; + for (int i = 0; i < parameters.length; i++) + types[i] = parameters[i].getClass(); + RemoteObject ro = importedObjects.get(object); + + try { + Method me; + if (null == ro.iface) + me = object.getClass().getMethod(m, types); + else + me = ro.iface.getMethod(m, types); + return (DBusAsyncReply) RemoteInvocationHandler.executeRemoteMethod(ro, me, this, RemoteInvocationHandler.CALL_TYPE_ASYNC, null, parameters); + } catch (DBusExecutionException DBEe) { + if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBEe); + throw DBEe; + } catch (Exception e) { + if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); + throw new DBusExecutionException(e.getMessage()); + } + } + + private void handleMessage(final MethodCall m) throws DBusException { + if (Debug.debug) Debug.print(Debug.DEBUG, "Handling incoming method call: " + m); + + ExportedObject eo = null; + Method meth = null; + Object o = null; + + if (null == m.getInterface() || + m.getInterface().equals("org.freedesktop.DBus.Peer") || + m.getInterface().equals("org.freedesktop.DBus.Introspectable")) { + synchronized (exportedObjects) { + eo = exportedObjects.get(null); + } + if (null != eo && null == eo.object.get()) { + unExportObject(null); + eo = null; + } + if (null != eo) { + meth = eo.methods.get(new MethodTuple(m.getName(), m.getSig())); + } + if (null != meth) + o = new _globalhandler(m.getPath()); + else + eo = null; + } + if (null == o) { + // now check for specific exported functions + + synchronized (exportedObjects) { + eo = exportedObjects.get(m.getPath()); + } + if (null != eo && null == eo.object.get()) { + if (Debug.debug) Debug.print(Debug.INFO, "Unexporting " + m.getPath() + " implicitly"); + unExportObject(m.getPath()); + eo = null; + } + + if (null == eo) { + eo = fallbackcontainer.get(m.getPath()); + } + + if (null == eo) { + try { + queueOutgoing(new Error(m, new DBus.Error.UnknownObject(m.getPath() + getString("notObjectProvidedByProcess")))); + } catch (DBusException DBe) { + } + return; + } + if (Debug.debug) { + Debug.print(Debug.VERBOSE, "Searching for method " + m.getName() + " with signature " + m.getSig()); + Debug.print(Debug.VERBOSE, "List of methods on " + eo + ":"); + for (MethodTuple mt : eo.methods.keySet()) + Debug.print(Debug.VERBOSE, " " + mt + " => " + eo.methods.get(mt)); + } + meth = eo.methods.get(new MethodTuple(m.getName(), m.getSig())); + if (null == meth) { + try { + queueOutgoing(new Error(m, new DBus.Error.UnknownMethod(MessageFormat.format(getString("methodDoesNotExist"), new Object[]{m.getInterface(), m.getName()})))); + } catch (DBusException DBe) { + } + return; + } + o = eo.object.get(); + } + + // now execute it + final Method me = meth; + final Object ob = o; + final boolean noreply = (1 == (m.getFlags() & Message.Flags.NO_REPLY_EXPECTED)); + final DBusCallInfo info = new DBusCallInfo(m); + final AbstractConnection conn = this; + if (Debug.debug) Debug.print(Debug.VERBOSE, "Adding Runnable for method " + meth); + addRunnable(new Runnable() { + private boolean run = false; + + public synchronized void run() { + if (run) return; + run = true; + if (Debug.debug) Debug.print(Debug.DEBUG, "Running method " + me + " for remote call"); + try { + Type[] ts = me.getGenericParameterTypes(); + m.setArgs(Marshalling.deSerializeParameters(m.getParameters(), ts, conn)); + if (Debug.debug) + Debug.print(Debug.VERBOSE, "Deserialised " + Arrays.deepToString(m.getParameters()) + " to types " + Arrays.deepToString(ts)); + } catch (Exception e) { + if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); + try { + conn.queueOutgoing(new Error(m, new DBus.Error.UnknownMethod(getString("deSerializationFailure") + e))); + } catch (DBusException DBe) { + } + return; + } + + try { + synchronized (infomap) { + infomap.put(Thread.currentThread(), info); + } + Object result; + try { + if (Debug.debug) + Debug.print(Debug.VERBOSE, "Invoking Method: " + me + " on " + ob + " with parameters " + Arrays.deepToString(m.getParameters())); + result = me.invoke(ob, m.getParameters()); + } catch (InvocationTargetException ITe) { + if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, ITe.getCause()); + throw ITe.getCause(); + } + synchronized (infomap) { + infomap.remove(Thread.currentThread()); + } + if (!noreply) { + MethodReturn reply; + if (Void.TYPE.equals(me.getReturnType())) + reply = new MethodReturn(m, null); + else { + StringBuffer sb = new StringBuffer(); + for (String s : Marshalling.getDBusType(me.getGenericReturnType())) + sb.append(s); + Object[] nr = Marshalling.convertParameters(new Object[]{result}, new Type[]{me.getGenericReturnType()}, conn); + + reply = new MethodReturn(m, sb.toString(), nr); + } + conn.queueOutgoing(reply); + } + } catch (DBusExecutionException DBEe) { + if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBEe); + try { + conn.queueOutgoing(new Error(m, DBEe)); + } catch (DBusException DBe) { + } + } catch (Throwable e) { + if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); + try { + conn.queueOutgoing(new Error(m, new DBusExecutionException(MessageFormat.format(getString("errorExecutingMethod"), new Object[]{m.getInterface(), m.getName(), e.getMessage()})))); + } catch (DBusException DBe) { + } + } + } + }); + } + + @SuppressWarnings({"unchecked", "deprecation"}) + private void handleMessage(final DBusSignal s) { + if (Debug.debug) Debug.print(Debug.DEBUG, "Handling incoming signal: " + s); + Vector> v = new Vector>(); + synchronized (handledSignals) { + Vector> t; + t = handledSignals.get(new SignalTuple(s.getInterface(), s.getName(), null, null)); + if (null != t) v.addAll(t); + t = handledSignals.get(new SignalTuple(s.getInterface(), s.getName(), s.getPath(), null)); + if (null != t) v.addAll(t); + t = handledSignals.get(new SignalTuple(s.getInterface(), s.getName(), null, s.getSource())); + if (null != t) v.addAll(t); + t = handledSignals.get(new SignalTuple(s.getInterface(), s.getName(), s.getPath(), s.getSource())); + if (null != t) v.addAll(t); + } + if (0 == v.size()) return; + final AbstractConnection conn = this; + for (final DBusSigHandler h : v) { + if (Debug.debug) Debug.print(Debug.VERBOSE, "Adding Runnable for signal " + s + " with handler " + h); + addRunnable(new Runnable() { + private boolean run = false; + + public synchronized void run() { + if (run) return; + run = true; + try { + DBusSignal rs; + if (s instanceof DBusSignal.internalsig || s.getClass().equals(DBusSignal.class)) + rs = s.createReal(conn); + else + rs = s; + ((DBusSigHandler) h).handle(rs); + } catch (DBusException DBe) { + if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); + try { + conn.queueOutgoing(new Error(s, new DBusExecutionException("Error handling signal " + s.getInterface() + "." + s.getName() + ": " + DBe.getMessage()))); + } catch (DBusException DBe2) { + } + } + } + }); + } + } + + private void handleMessage(final Error err) { + if (Debug.debug) Debug.print(Debug.DEBUG, "Handling incoming error: " + err); + MethodCall m = null; + if (null == pendingCalls) return; + synchronized (pendingCalls) { + if (pendingCalls.contains(err.getReplySerial())) + m = pendingCalls.remove(err.getReplySerial()); + } + if (null != m) { + m.setReply(err); + CallbackHandler cbh = null; + DBusAsyncReply asr = null; + synchronized (pendingCallbacks) { + cbh = pendingCallbacks.remove(m); + if (Debug.debug) Debug.print(Debug.VERBOSE, cbh + " = pendingCallbacks.remove(" + m + ")"); + asr = pendingCallbackReplys.remove(m); + } + // queue callback for execution + if (null != cbh) { + final CallbackHandler fcbh = cbh; + if (Debug.debug) Debug.print(Debug.VERBOSE, "Adding Error Runnable with callback handler " + fcbh); + addRunnable(new Runnable() { + private boolean run = false; + + public synchronized void run() { + if (run) return; + run = true; + try { + if (Debug.debug) Debug.print(Debug.VERBOSE, "Running Error Callback for " + err); + DBusCallInfo info = new DBusCallInfo(err); + synchronized (infomap) { + infomap.put(Thread.currentThread(), info); + } + + fcbh.handleError(err.getException()); + synchronized (infomap) { + infomap.remove(Thread.currentThread()); + } + + } catch (Exception e) { + if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); + } + } + }); + } + + } else + synchronized (pendingErrors) { + pendingErrors.addLast(err); + } + } + + @SuppressWarnings("unchecked") + private void handleMessage(final MethodReturn mr) { + if (Debug.debug) Debug.print(Debug.DEBUG, "Handling incoming method return: " + mr); + MethodCall m = null; + if (null == pendingCalls) return; + synchronized (pendingCalls) { + if (pendingCalls.contains(mr.getReplySerial())) + m = pendingCalls.remove(mr.getReplySerial()); + } + if (null != m) { + m.setReply(mr); + mr.setCall(m); + CallbackHandler cbh = null; + DBusAsyncReply asr = null; + synchronized (pendingCallbacks) { + cbh = pendingCallbacks.remove(m); + if (Debug.debug) Debug.print(Debug.VERBOSE, cbh + " = pendingCallbacks.remove(" + m + ")"); + asr = pendingCallbackReplys.remove(m); + } + // queue callback for execution + if (null != cbh) { + final CallbackHandler fcbh = cbh; + final DBusAsyncReply fasr = asr; + if (Debug.debug) + Debug.print(Debug.VERBOSE, "Adding Runnable for method " + fasr.getMethod() + " with callback handler " + fcbh); + addRunnable(new Runnable() { + private boolean run = false; + + public synchronized void run() { + if (run) return; + run = true; + try { + if (Debug.debug) Debug.print(Debug.VERBOSE, "Running Callback for " + mr); + DBusCallInfo info = new DBusCallInfo(mr); + synchronized (infomap) { + infomap.put(Thread.currentThread(), info); + } + + fcbh.handle(RemoteInvocationHandler.convertRV(mr.getSig(), mr.getParameters(), fasr.getMethod(), fasr.getConnection())); + synchronized (infomap) { + infomap.remove(Thread.currentThread()); + } + + } catch (Exception e) { + if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); + } + } + }); + } + + } else + try { + queueOutgoing(new Error(mr, new DBusExecutionException(getString("spuriousReply")))); + } catch (DBusException DBe) { + } + } + + protected void sendMessage(Message m) { + try { + if (!connected) throw new NotConnected(getString("disconnected")); + if (m instanceof DBusSignal) + ((DBusSignal) m).appendbody(this); + + if (m instanceof MethodCall) { + if (0 == (m.getFlags() & Message.Flags.NO_REPLY_EXPECTED)) + if (null == pendingCalls) + ((MethodCall) m).setReply(new Error("org.freedesktop.DBus.Local", "org.freedesktop.DBus.Local.disconnected", 0, "s", new Object[]{getString("disconnected")})); + else synchronized (pendingCalls) { + pendingCalls.put(m.getSerial(), (MethodCall) m); + } + } + + transport.mout.writeMessage(m); + + } catch (Exception e) { + if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); + if (m instanceof MethodCall && e instanceof NotConnected) + try { + ((MethodCall) m).setReply(new Error("org.freedesktop.DBus.Local", "org.freedesktop.DBus.Local.disconnected", 0, "s", new Object[]{getString("disconnected")})); + } catch (DBusException DBe) { + } + if (m instanceof MethodCall && e instanceof DBusExecutionException) + try { + ((MethodCall) m).setReply(new Error(m, e)); + } catch (DBusException DBe) { + } + else if (m instanceof MethodCall) + try { + if (Debug.debug) Debug.print(Debug.INFO, "Setting reply to " + m + " as an error"); + ((MethodCall) m).setReply(new Error(m, new DBusExecutionException(getString("messageFailedSend") + e.getMessage()))); + } catch (DBusException DBe) { + } + else if (m instanceof MethodReturn) + try { + transport.mout.writeMessage(new Error(m, e)); + } catch (IOException IOe) { + if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, IOe); + } catch (DBusException IOe) { + if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); + } + if (e instanceof IOException) disconnect(); + } + } + + private Message readIncoming() throws DBusException { + if (!connected) throw new NotConnected(getString("missingTransport")); + Message m = null; + try { + m = transport.min.readMessage(); + } catch (IOException IOe) { + throw new FatalDBusException(IOe.getMessage()); + } + return m; + } + + /** + * Returns the address this connection is connected to. + */ + public BusAddress getAddress() throws ParseException { + return new BusAddress(addr); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/ArrayFrob.java b/federation/sssd/src/main/java/org/freedesktop/dbus/ArrayFrob.java new file mode 100644 index 0000000000..86dfa5282c --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/ArrayFrob.java @@ -0,0 +1,173 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import cx.ath.matthew.debug.Debug; + +import java.lang.reflect.Array; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.List; + +import static org.freedesktop.dbus.Gettext.getString; + +class ArrayFrob { + static Hashtable, Class> primitiveToWrapper = new Hashtable, Class>(); + static Hashtable, Class> wrapperToPrimitive = new Hashtable, Class>(); + + static { + primitiveToWrapper.put(Boolean.TYPE, Boolean.class); + primitiveToWrapper.put(Byte.TYPE, Byte.class); + primitiveToWrapper.put(Short.TYPE, Short.class); + primitiveToWrapper.put(Character.TYPE, Character.class); + primitiveToWrapper.put(Integer.TYPE, Integer.class); + primitiveToWrapper.put(Long.TYPE, Long.class); + primitiveToWrapper.put(Float.TYPE, Float.class); + primitiveToWrapper.put(Double.TYPE, Double.class); + wrapperToPrimitive.put(Boolean.class, Boolean.TYPE); + wrapperToPrimitive.put(Byte.class, Byte.TYPE); + wrapperToPrimitive.put(Short.class, Short.TYPE); + wrapperToPrimitive.put(Character.class, Character.TYPE); + wrapperToPrimitive.put(Integer.class, Integer.TYPE); + wrapperToPrimitive.put(Long.class, Long.TYPE); + wrapperToPrimitive.put(Float.class, Float.TYPE); + wrapperToPrimitive.put(Double.class, Double.TYPE); + + } + + @SuppressWarnings("unchecked") + public static T[] wrap(Object o) throws IllegalArgumentException { + Class ac = o.getClass(); + if (!ac.isArray()) throw new IllegalArgumentException(getString("invalidArray")); + Class cc = ac.getComponentType(); + Class ncc = primitiveToWrapper.get(cc); + if (null == ncc) throw new IllegalArgumentException(getString("notPrimitiveType")); + T[] ns = (T[]) Array.newInstance(ncc, Array.getLength(o)); + for (int i = 0; i < ns.length; i++) + ns[i] = (T) Array.get(o, i); + return ns; + } + + @SuppressWarnings("unchecked") + public static Object unwrap(T[] ns) throws IllegalArgumentException { + Class ac = (Class) ns.getClass(); + Class cc = (Class) ac.getComponentType(); + Class ncc = wrapperToPrimitive.get(cc); + if (null == ncc) throw new IllegalArgumentException(getString("invalidWrapperType")); + Object o = Array.newInstance(ncc, ns.length); + for (int i = 0; i < ns.length; i++) + Array.set(o, i, ns[i]); + return o; + } + + public static List listify(T[] ns) throws IllegalArgumentException { + return Arrays.asList(ns); + } + + @SuppressWarnings("unchecked") + public static List listify(Object o) throws IllegalArgumentException { + if (o instanceof Object[]) return listify((T[]) o); + if (!o.getClass().isArray()) throw new IllegalArgumentException(getString("invalidArray")); + List l = new ArrayList(Array.getLength(o)); + for (int i = 0; i < Array.getLength(o); i++) + l.add((T) Array.get(o, i)); + return l; + } + + @SuppressWarnings("unchecked") + public static T[] delist(List l, Class c) throws IllegalArgumentException { + return l.toArray((T[]) Array.newInstance(c, 0)); + } + + public static Object delistprimitive(List l, Class c) throws IllegalArgumentException { + Object o = Array.newInstance(c, l.size()); + for (int i = 0; i < l.size(); i++) + Array.set(o, i, l.get(i)); + return o; + } + + @SuppressWarnings("unchecked") + public static Object convert(Object o, Class c) throws IllegalArgumentException { + /* Possible Conversions: + * + ** List -> List + ** List -> int[] + ** List -> Integer[] + ** int[] -> int[] + ** int[] -> List + ** int[] -> Integer[] + ** Integer[] -> Integer[] + ** Integer[] -> int[] + ** Integer[] -> List + */ + try { + // List -> List + if (List.class.equals(c) + && o instanceof List) + return o; + + // int[] -> List + // Integer[] -> List + if (List.class.equals(c) + && o.getClass().isArray()) + return listify(o); + + // int[] -> int[] + // Integer[] -> Integer[] + if (o.getClass().isArray() + && c.isArray() + && o.getClass().getComponentType().equals(c.getComponentType())) + return o; + + // int[] -> Integer[] + if (o.getClass().isArray() + && c.isArray() + && o.getClass().getComponentType().isPrimitive()) + return wrap(o); + + // Integer[] -> int[] + if (o.getClass().isArray() + && c.isArray() + && c.getComponentType().isPrimitive()) + return unwrap((Object[]) o); + + // List -> int[] + if (o instanceof List + && c.isArray() + && c.getComponentType().isPrimitive()) + return delistprimitive((List) o, (Class) c.getComponentType()); + + // List -> Integer[] + if (o instanceof List + && c.isArray()) + return delist((List) o, (Class) c.getComponentType()); + + if (o.getClass().isArray() + && c.isArray()) + return type((Object[]) o, (Class) c.getComponentType()); + + } catch (Exception e) { + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); + throw new IllegalArgumentException(e); + } + + throw new IllegalArgumentException(MessageFormat.format(getString("convertionTypeNotExpected"), new Object[]{o.getClass(), c})); + } + + public static Object[] type(Object[] old, Class c) { + Object[] ns = (Object[]) Array.newInstance(c, old.length); + for (int i = 0; i < ns.length; i++) + ns[i] = old[i]; + return ns; + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/BusAddress.java b/federation/sssd/src/main/java/org/freedesktop/dbus/BusAddress.java new file mode 100644 index 0000000000..3848951f39 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/BusAddress.java @@ -0,0 +1,52 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import cx.ath.matthew.debug.Debug; + +import java.text.ParseException; +import java.util.HashMap; +import java.util.Map; + +import static org.freedesktop.dbus.Gettext.getString; + +public class BusAddress { + private String type; + private Map parameters; + + public BusAddress(String address) throws ParseException { + if (null == address || "".equals(address)) throw new ParseException(getString("busAddressBlank"), 0); + if (Debug.debug) Debug.print(Debug.VERBOSE, "Parsing bus address: " + address); + String[] ss = address.split(":", 2); + if (ss.length < 2) throw new ParseException(getString("busAddressInvalid") + address, 0); + type = ss[0]; + if (Debug.debug) Debug.print(Debug.VERBOSE, "Transport type: " + type); + String[] ps = ss[1].split(","); + parameters = new HashMap(); + for (String p : ps) { + String[] kv = p.split("=", 2); + parameters.put(kv[0], kv[1]); + } + if (Debug.debug) Debug.print(Debug.VERBOSE, "Transport options: " + parameters); + } + + public String getType() { + return type; + } + + public String getParameter(String key) { + return parameters.get(key); + } + + public String toString() { + return type + ": " + parameters; + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/CallbackHandler.java b/federation/sssd/src/main/java/org/freedesktop/dbus/CallbackHandler.java new file mode 100644 index 0000000000..0e2a81e77a --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/CallbackHandler.java @@ -0,0 +1,22 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import org.freedesktop.dbus.exceptions.DBusExecutionException; + +/** + * Interface for callbacks in async mode + */ +public interface CallbackHandler { + public void handle(ReturnType r); + + public void handleError(DBusExecutionException e); +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/Container.java b/federation/sssd/src/main/java/org/freedesktop/dbus/Container.java new file mode 100644 index 0000000000..39fd245527 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/Container.java @@ -0,0 +1,93 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import java.lang.reflect.Field; +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * This class is the super class of both Structs and Tuples + * and holds common methods. + */ +abstract class Container { + private static Map typecache = new HashMap(); + + static void putTypeCache(Type k, Type[] v) { + typecache.put(k, v); + } + + static Type[] getTypeCache(Type k) { + return typecache.get(k); + } + + private Object[] parameters = null; + + public Container() { + } + + private void setup() { + Field[] fs = getClass().getDeclaredFields(); + Object[] args = new Object[fs.length]; + + int diff = 0; + for (Field f : fs) { + Position p = f.getAnnotation(Position.class); + if (null == p) { + diff++; + continue; + } + try { + args[p.value()] = f.get(this); + } catch (IllegalAccessException IAe) { + } + } + + this.parameters = new Object[args.length - diff]; + System.arraycopy(args, 0, parameters, 0, parameters.length); + } + + /** + * Returns the struct contents in order. + * + * @throws DBusException If there is a problem doing this. + */ + public final Object[] getParameters() { + if (null != parameters) return parameters; + setup(); + return parameters; + } + + /** + * Returns this struct as a string. + */ + public final String toString() { + String s = getClass().getName() + "<"; + if (null == parameters) + setup(); + if (0 == parameters.length) + return s + ">"; + for (Object o : parameters) + s += o + ", "; + return s.replaceAll(", $", ">"); + } + + public final boolean equals(Object other) { + if (other instanceof Container) { + Container that = (Container) other; + if (this.getClass().equals(that.getClass())) + return Arrays.equals(this.getParameters(), that.getParameters()); + else return false; + } else return false; + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/DBusAsyncReply.java b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusAsyncReply.java new file mode 100644 index 0000000000..51c2bfd831 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusAsyncReply.java @@ -0,0 +1,117 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import cx.ath.matthew.debug.Debug; +import org.freedesktop.DBus.Error.NoReply; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.exceptions.DBusExecutionException; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; + +import static org.freedesktop.dbus.Gettext.getString; + +/** + * A handle to an asynchronous method call. + */ +public class DBusAsyncReply { + /** + * Check if any of a set of asynchronous calls have had a reply. + * + * @param replies A Collection of handles to replies to check. + * @return A Collection only containing those calls which have had replies. + */ + public static Collection> hasReply(Collection> replies) { + Collection> c = new ArrayList>(replies); + Iterator> i = c.iterator(); + while (i.hasNext()) + if (!i.next().hasReply()) i.remove(); + return c; + } + + private ReturnType rval = null; + private DBusExecutionException error = null; + private MethodCall mc; + private Method me; + private AbstractConnection conn; + + DBusAsyncReply(MethodCall mc, Method me, AbstractConnection conn) { + this.mc = mc; + this.me = me; + this.conn = conn; + } + + @SuppressWarnings("unchecked") + private synchronized void checkReply() { + if (mc.hasReply()) { + Message m = mc.getReply(); + if (m instanceof Error) + error = ((Error) m).getException(); + else if (m instanceof MethodReturn) { + try { + rval = (ReturnType) RemoteInvocationHandler.convertRV(m.getSig(), m.getParameters(), me, conn); + } catch (DBusExecutionException DBEe) { + error = DBEe; + } catch (DBusException DBe) { + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); + error = new DBusExecutionException(DBe.getMessage()); + } + } + } + } + + /** + * Check if we've had a reply. + * + * @return True if we have a reply + */ + public boolean hasReply() { + if (null != rval || null != error) return true; + checkReply(); + return null != rval || null != error; + } + + /** + * Get the reply. + * + * @return The return value from the method. + * @throws DBusExecutionException if the reply to the method was an error. + * @throws NoReply if the method hasn't had a reply yet + */ + public ReturnType getReply() throws DBusExecutionException { + if (null != rval) return rval; + else if (null != error) throw error; + checkReply(); + if (null != rval) return rval; + else if (null != error) throw error; + else throw new NoReply(getString("asyncCallNoReply")); + } + + public String toString() { + return getString("waitingFor") + mc; + } + + Method getMethod() { + return me; + } + + AbstractConnection getConnection() { + return conn; + } + + MethodCall getCall() { + return mc; + } +} + diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/DBusCallInfo.java b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusCallInfo.java new file mode 100644 index 0000000000..1c9d143154 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusCallInfo.java @@ -0,0 +1,79 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +/** + * Holds information on a method call + */ +public class DBusCallInfo { + /** + * Indicates the caller won't wait for a reply (and we won't send one). + */ + public static final int NO_REPLY = Message.Flags.NO_REPLY_EXPECTED; + public static final int ASYNC = 0x100; + private String source; + private String destination; + private String objectpath; + private String iface; + private String method; + private int flags; + + DBusCallInfo(Message m) { + this.source = m.getSource(); + this.destination = m.getDestination(); + this.objectpath = m.getPath(); + this.iface = m.getInterface(); + this.method = m.getName(); + this.flags = m.getFlags(); + } + + /** + * Returns the BusID which called the method + */ + public String getSource() { + return source; + } + + /** + * Returns the name with which we were addressed on the Bus + */ + public String getDestination() { + return destination; + } + + /** + * Returns the object path used to call this method + */ + public String getObjectPath() { + return objectpath; + } + + /** + * Returns the interface this method was called with + */ + public String getInterface() { + return iface; + } + + /** + * Returns the method name used to call this method + */ + public String getMethod() { + return method; + } + + /** + * Returns any flags set on this method call + */ + public int getFlags() { + return flags; + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/DBusConnection.java b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusConnection.java new file mode 100644 index 0000000000..45e33a4674 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusConnection.java @@ -0,0 +1,794 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import cx.ath.matthew.debug.Debug; +import org.freedesktop.DBus; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.exceptions.DBusExecutionException; +import org.freedesktop.dbus.exceptions.NotConnected; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.lang.reflect.Proxy; +import java.text.MessageFormat; +import java.text.ParseException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.Vector; + +import static org.freedesktop.dbus.Gettext.getString; + +/** + * Handles a connection to DBus. + *

+ * This is a Singleton class, only 1 connection to the SYSTEM or SESSION busses can be made. + * Repeated calls to getConnection will return the same reference. + *

+ *

+ * Signal Handlers and method calls from remote objects are run in their own threads, you MUST handle the concurrency issues. + *

+ */ +public class DBusConnection extends AbstractConnection { + /** + * Add addresses of peers to a set which will watch for them to + * disappear and automatically remove them from the set. + */ + public class PeerSet implements Set, DBusSigHandler { + private Set addresses; + + public PeerSet() { + addresses = new TreeSet(); + try { + addSigHandler(new DBusMatchRule(DBus.NameOwnerChanged.class, null, null), this); + } catch (DBusException DBe) { + if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); + } + } + + public void handle(DBus.NameOwnerChanged noc) { + if (Debug.debug) + Debug.print(Debug.DEBUG, "Received NameOwnerChanged(" + noc.name + "," + noc.old_owner + "," + noc.new_owner + ")"); + if ("".equals(noc.new_owner) && addresses.contains(noc.name)) + remove(noc.name); + } + + public boolean add(String address) { + if (Debug.debug) + Debug.print(Debug.DEBUG, "Adding " + address); + synchronized (addresses) { + return addresses.add(address); + } + } + + public boolean addAll(Collection addresses) { + synchronized (this.addresses) { + return this.addresses.addAll(addresses); + } + } + + public void clear() { + synchronized (addresses) { + addresses.clear(); + } + } + + public boolean contains(Object o) { + return addresses.contains(o); + } + + public boolean containsAll(Collection os) { + return addresses.containsAll(os); + } + + public boolean equals(Object o) { + if (o instanceof PeerSet) + return ((PeerSet) o).addresses.equals(addresses); + else return false; + } + + public int hashCode() { + return addresses.hashCode(); + } + + public boolean isEmpty() { + return addresses.isEmpty(); + } + + public Iterator iterator() { + return addresses.iterator(); + } + + public boolean remove(Object o) { + if (Debug.debug) + Debug.print(Debug.DEBUG, "Removing " + o); + synchronized (addresses) { + return addresses.remove(o); + } + } + + public boolean removeAll(Collection os) { + synchronized (addresses) { + return addresses.removeAll(os); + } + } + + public boolean retainAll(Collection os) { + synchronized (addresses) { + return addresses.retainAll(os); + } + } + + public int size() { + return addresses.size(); + } + + public Object[] toArray() { + synchronized (addresses) { + return addresses.toArray(); + } + } + + public T[] toArray(T[] a) { + synchronized (addresses) { + return addresses.toArray(a); + } + } + } + + private class _sighandler implements DBusSigHandler { + public void handle(DBusSignal s) { + if (s instanceof org.freedesktop.DBus.Local.Disconnected) { + if (Debug.debug) Debug.print(Debug.WARN, "Handling disconnected signal from bus"); + try { + Error err = new Error( + "org.freedesktop.DBus.Local", "org.freedesktop.DBus.Local.disconnected", 0, "s", new Object[]{getString("disconnected")}); + if (null != pendingCalls) synchronized (pendingCalls) { + long[] set = pendingCalls.getKeys(); + for (long l : set) + if (-1 != l) { + MethodCall m = pendingCalls.remove(l); + if (null != m) + m.setReply(err); + } + } + synchronized (pendingErrors) { + pendingErrors.add(err); + } + } catch (DBusException DBe) { + } + } else if (s instanceof org.freedesktop.DBus.NameAcquired) { + busnames.add(((org.freedesktop.DBus.NameAcquired) s).name); + } + } + } + + /** + * System Bus + */ + public static final int SYSTEM = 0; + /** + * Session Bus + */ + public static final int SESSION = 1; + + public static final String DEFAULT_SYSTEM_BUS_ADDRESS = "unix:path=/var/run/dbus/system_bus_socket"; + + private List busnames; + + private static final Map conn = new HashMap(); + private int _refcount = 0; + private Object _reflock = new Object(); + private DBus _dbus; + + /** + * Connect to the BUS. If a connection already exists to the specified Bus, a reference to it is returned. + * + * @param address The address of the bus to connect to + * @throws DBusException If there is a problem connecting to the Bus. + */ + public static DBusConnection getConnection(String address) throws DBusException { + synchronized (conn) { + DBusConnection c = conn.get(address); + if (null != c) { + synchronized (c._reflock) { + c._refcount++; + } + return c; + } else { + c = new DBusConnection(address); + conn.put(address, c); + return c; + } + } + } + + /** + * Connect to the BUS. If a connection already exists to the specified Bus, a reference to it is returned. + * + * @param bustype The Bus to connect to. + * @throws DBusException If there is a problem connecting to the Bus. + * @see #SYSTEM + * @see #SESSION + */ + public static DBusConnection getConnection(int bustype) throws DBusException { + synchronized (conn) { + String s = null; + switch (bustype) { + case SYSTEM: + s = System.getenv("DBUS_SYSTEM_BUS_ADDRESS"); + if (null == s) s = DEFAULT_SYSTEM_BUS_ADDRESS; + break; + case SESSION: + s = System.getenv("DBUS_SESSION_BUS_ADDRESS"); + if (null == s) { + // address gets stashed in $HOME/.dbus/session-bus/`dbus-uuidgen --get`-`sed 's/:\(.\)\..*/\1/' <<< $DISPLAY` + String display = System.getenv("DISPLAY"); + if (null == display) throw new DBusException(getString("cannotResolveSessionBusAddress")); + File uuidfile = new File("/var/lib/dbus/machine-id"); + if (!uuidfile.exists()) throw new DBusException(getString("cannotResolveSessionBusAddress")); + try { + BufferedReader r = new BufferedReader(new FileReader(uuidfile)); + String uuid = r.readLine(); + String homedir = System.getProperty("user.home"); + File addressfile = new File(homedir + "/.dbus/session-bus", + uuid + "-" + display.replaceAll(":([0-9]*)\\..*", "$1")); + if (!addressfile.exists()) + throw new DBusException(getString("cannotResolveSessionBusAddress")); + r = new BufferedReader(new FileReader(addressfile)); + String l; + while (null != (l = r.readLine())) { + if (Debug.debug) Debug.print(Debug.VERBOSE, "Reading D-Bus session data: " + l); + if (l.matches("DBUS_SESSION_BUS_ADDRESS.*")) { + s = l.replaceAll("^[^=]*=", ""); + if (Debug.debug) Debug.print(Debug.VERBOSE, "Parsing " + l + " to " + s); + } + } + if (null == s || "".equals(s)) + throw new DBusException(getString("cannotResolveSessionBusAddress")); + if (Debug.debug) + Debug.print(Debug.INFO, "Read bus address " + s + " from file " + addressfile.toString()); + } catch (Exception e) { + if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); + throw new DBusException(getString("cannotResolveSessionBusAddress")); + } + } + break; + default: + throw new DBusException(getString("invalidBusType") + bustype); + } + DBusConnection c = conn.get(s); + if (Debug.debug) Debug.print(Debug.VERBOSE, "Getting bus connection for " + s + ": " + c); + if (null != c) { + synchronized (c._reflock) { + c._refcount++; + } + return c; + } else { + if (Debug.debug) Debug.print(Debug.DEBUG, "Creating new bus connection to: " + s); + c = new DBusConnection(s); + conn.put(s, c); + return c; + } + } + } + + @SuppressWarnings("unchecked") + private DBusConnection(String address) throws DBusException { + super(address); + busnames = new Vector(); + + synchronized (_reflock) { + _refcount = 1; + } + + try { + transport = new Transport(addr, AbstractConnection.TIMEOUT); + connected = true; + } catch (IOException IOe) { + if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, IOe); + disconnect(); + throw new DBusException(getString("connectionFailure") + IOe.getMessage()); + } catch (ParseException Pe) { + if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, Pe); + disconnect(); + throw new DBusException(getString("connectionFailure") + Pe.getMessage()); + } + + // start listening for calls + listen(); + + // register disconnect handlers + DBusSigHandler h = new _sighandler(); + addSigHandlerWithoutMatch(org.freedesktop.DBus.Local.Disconnected.class, h); + addSigHandlerWithoutMatch(org.freedesktop.DBus.NameAcquired.class, h); + + // register ourselves + _dbus = getRemoteObject("org.freedesktop.DBus", "/org/freedesktop/DBus", DBus.class); + try { + busnames.add(_dbus.Hello()); + } catch (DBusExecutionException DBEe) { + if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBEe); + throw new DBusException(DBEe.getMessage()); + } + } + + @SuppressWarnings("unchecked") + DBusInterface dynamicProxy(String source, String path) throws DBusException { + if (Debug.debug) + Debug.print(Debug.INFO, "Introspecting " + path + " on " + source + " for dynamic proxy creation"); + try { + DBus.Introspectable intro = getRemoteObject(source, path, DBus.Introspectable.class); + String data = intro.Introspect(); + if (Debug.debug) Debug.print(Debug.VERBOSE, "Got introspection data: " + data); + String[] tags = data.split("[<>]"); + Vector ifaces = new Vector(); + for (String tag : tags) { + if (tag.startsWith("interface")) { + ifaces.add(tag.replaceAll("^interface *name *= *['\"]([^'\"]*)['\"].*$", "$1")); + } + } + Vector> ifcs = new Vector>(); + for (String iface : ifaces) { + if (Debug.debug) Debug.print(Debug.DEBUG, "Trying interface " + iface); + int j = 0; + while (j >= 0) { + try { + Class ifclass = Class.forName(iface); + if (!ifcs.contains(ifclass)) + ifcs.add(ifclass); + break; + } catch (Exception e) { + } + j = iface.lastIndexOf("."); + char[] cs = iface.toCharArray(); + if (j >= 0) { + cs[j] = '$'; + iface = String.valueOf(cs); + } + } + } + + if (ifcs.size() == 0) throw new DBusException(getString("interfaceToCastNotFound")); + + RemoteObject ro = new RemoteObject(source, path, null, false); + DBusInterface newi = (DBusInterface) + Proxy.newProxyInstance(ifcs.get(0).getClassLoader(), + ifcs.toArray(new Class[0]), + new RemoteInvocationHandler(this, ro)); + importedObjects.put(newi, ro); + return newi; + } catch (Exception e) { + if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); + throw new DBusException(MessageFormat.format(getString("createProxyExportFailure"), new Object[]{path, source, e.getMessage()})); + } + } + + DBusInterface getExportedObject(String source, String path) throws DBusException { + ExportedObject o = null; + synchronized (exportedObjects) { + o = exportedObjects.get(path); + } + if (null != o && null == o.object.get()) { + unExportObject(path); + o = null; + } + if (null != o) return o.object.get(); + if (null == source) throw new DBusException(getString("objectNotExportedNoRemoteSpecified")); + return dynamicProxy(source, path); + } + + /** + * Release a bus name. + * Releases the name so that other people can use it + * + * @param busname The name to release. MUST be in dot-notation like "org.freedesktop.local" + * @throws DBusException If the busname is incorrectly formatted. + */ + public void releaseBusName(String busname) throws DBusException { + if (!busname.matches(BUSNAME_REGEX) || busname.length() > MAX_NAME_LENGTH) + throw new DBusException(getString("invalidBusName")); + synchronized (this.busnames) { + UInt32 rv; + try { + rv = _dbus.ReleaseName(busname); + } catch (DBusExecutionException DBEe) { + if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBEe); + throw new DBusException(DBEe.getMessage()); + } + this.busnames.remove(busname); + } + } + + /** + * Request a bus name. + * Request the well known name that this should respond to on the Bus. + * + * @param busname The name to respond to. MUST be in dot-notation like "org.freedesktop.local" + * @throws DBusException If the register name failed, or our name already exists on the bus. + * or if busname is incorrectly formatted. + */ + public void requestBusName(String busname) throws DBusException { + if (!busname.matches(BUSNAME_REGEX) || busname.length() > MAX_NAME_LENGTH) + throw new DBusException(getString("invalidBusName")); + synchronized (this.busnames) { + UInt32 rv; + try { + rv = _dbus.RequestName(busname, + new UInt32(DBus.DBUS_NAME_FLAG_REPLACE_EXISTING | + DBus.DBUS_NAME_FLAG_DO_NOT_QUEUE)); + } catch (DBusExecutionException DBEe) { + if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBEe); + throw new DBusException(DBEe.getMessage()); + } + switch (rv.intValue()) { + case DBus.DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER: + break; + case DBus.DBUS_REQUEST_NAME_REPLY_IN_QUEUE: + throw new DBusException(getString("dbusRegistrationFailure")); + case DBus.DBUS_REQUEST_NAME_REPLY_EXISTS: + throw new DBusException(getString("dbusRegistrationFailure")); + case DBus.DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER: + break; + default: + break; + } + this.busnames.add(busname); + } + } + + /** + * Returns the unique name of this connection. + */ + public String getUniqueName() { + return busnames.get(0); + } + + /** + * Returns all the names owned by this connection. + */ + public String[] getNames() { + Set names = new TreeSet(); + names.addAll(busnames); + return names.toArray(new String[0]); + } + + public I getPeerRemoteObject(String busname, String objectpath, Class type) throws DBusException { + return getPeerRemoteObject(busname, objectpath, type, true); + } + + /** + * Return a reference to a remote object. + * This method will resolve the well known name (if given) to a unique bus name when you call it. + * This means that if a well known name is released by one process and acquired by another calls to + * objects gained from this method will continue to operate on the original process. + *

+ * This method will use bus introspection to determine the interfaces on a remote object and so + * may block and may fail. The resulting proxy object will, however, be castable + * to any interface it implements. It will also autostart the process if applicable. Also note + * that the resulting proxy may fail to execute the correct method with overloaded methods + * and that complex types may fail in interesting ways. Basically, if something odd happens, + * try specifying the interface explicitly. + * + * @param busname The bus name to connect to. Usually a well known bus name in dot-notation (such as "org.freedesktop.local") + * or may be a DBus address such as ":1-16". + * @param objectpath The path on which the process is exporting the object.$ + * @return A reference to a remote object. + * @throws ClassCastException If type is not a sub-type of DBusInterface + * @throws DBusException If busname or objectpath are incorrectly formatted. + */ + public DBusInterface getPeerRemoteObject(String busname, String objectpath) throws DBusException { + if (null == busname) throw new DBusException(getString("nullBusName")); + + if ((!busname.matches(BUSNAME_REGEX) && !busname.matches(CONNID_REGEX)) + || busname.length() > MAX_NAME_LENGTH) + throw new DBusException(getString("invalidBusName") + busname); + + String unique = _dbus.GetNameOwner(busname); + + return dynamicProxy(unique, objectpath); + } + + /** + * Return a reference to a remote object. + * This method will always refer to the well known name (if given) rather than resolving it to a unique bus name. + * In particular this means that if a process providing the well known name disappears and is taken over by another process + * proxy objects gained by this method will make calls on the new proccess. + *

+ * This method will use bus introspection to determine the interfaces on a remote object and so + * may block and may fail. The resulting proxy object will, however, be castable + * to any interface it implements. It will also autostart the process if applicable. Also note + * that the resulting proxy may fail to execute the correct method with overloaded methods + * and that complex types may fail in interesting ways. Basically, if something odd happens, + * try specifying the interface explicitly. + * + * @param busname The bus name to connect to. Usually a well known bus name name in dot-notation (such as "org.freedesktop.local") + * or may be a DBus address such as ":1-16". + * @param objectpath The path on which the process is exporting the object. + * @return A reference to a remote object. + * @throws ClassCastException If type is not a sub-type of DBusInterface + * @throws DBusException If busname or objectpath are incorrectly formatted. + */ + public DBusInterface getRemoteObject(String busname, String objectpath) throws DBusException { + if (null == busname) throw new DBusException(getString("nullBusName")); + if (null == objectpath) throw new DBusException(getString("nullObjectPath")); + + if ((!busname.matches(BUSNAME_REGEX) && !busname.matches(CONNID_REGEX)) + || busname.length() > MAX_NAME_LENGTH) + throw new DBusException(getString("invalidBusName") + busname); + + if (!objectpath.matches(OBJECT_REGEX) || objectpath.length() > MAX_NAME_LENGTH) + throw new DBusException(getString("invalidObjectPath") + objectpath); + + return dynamicProxy(busname, objectpath); + } + + /** + * Return a reference to a remote object. + * This method will resolve the well known name (if given) to a unique bus name when you call it. + * This means that if a well known name is released by one process and acquired by another calls to + * objects gained from this method will continue to operate on the original process. + * + * @param busname The bus name to connect to. Usually a well known bus name in dot-notation (such as "org.freedesktop.local") + * or may be a DBus address such as ":1-16". + * @param objectpath The path on which the process is exporting the object.$ + * @param type The interface they are exporting it on. This type must have the same full class name and exposed method signatures + * as the interface the remote object is exporting. + * @param autostart Disable/Enable auto-starting of services in response to calls on this object. + * Default is enabled; when calling a method with auto-start enabled, if the destination is a well-known name + * and is not owned the bus will attempt to start a process to take the name. When disabled an error is + * returned immediately. + * @return A reference to a remote object. + * @throws ClassCastException If type is not a sub-type of DBusInterface + * @throws DBusException If busname or objectpath are incorrectly formatted or type is not in a package. + */ + public I getPeerRemoteObject(String busname, String objectpath, Class type, boolean autostart) throws DBusException { + if (null == busname) throw new DBusException(getString("nullBusName")); + + if ((!busname.matches(BUSNAME_REGEX) && !busname.matches(CONNID_REGEX)) + || busname.length() > MAX_NAME_LENGTH) + throw new DBusException(getString("invalidBusName") + busname); + + String unique = _dbus.GetNameOwner(busname); + + return getRemoteObject(unique, objectpath, type, autostart); + } + + /** + * Return a reference to a remote object. + * This method will always refer to the well known name (if given) rather than resolving it to a unique bus name. + * In particular this means that if a process providing the well known name disappears and is taken over by another process + * proxy objects gained by this method will make calls on the new proccess. + * + * @param busname The bus name to connect to. Usually a well known bus name name in dot-notation (such as "org.freedesktop.local") + * or may be a DBus address such as ":1-16". + * @param objectpath The path on which the process is exporting the object. + * @param type The interface they are exporting it on. This type must have the same full class name and exposed method signatures + * as the interface the remote object is exporting. + * @return A reference to a remote object. + * @throws ClassCastException If type is not a sub-type of DBusInterface + * @throws DBusException If busname or objectpath are incorrectly formatted or type is not in a package. + */ + public I getRemoteObject(String busname, String objectpath, Class type) throws DBusException { + return getRemoteObject(busname, objectpath, type, true); + } + + /** + * Return a reference to a remote object. + * This method will always refer to the well known name (if given) rather than resolving it to a unique bus name. + * In particular this means that if a process providing the well known name disappears and is taken over by another process + * proxy objects gained by this method will make calls on the new proccess. + * + * @param busname The bus name to connect to. Usually a well known bus name name in dot-notation (such as "org.freedesktop.local") + * or may be a DBus address such as ":1-16". + * @param objectpath The path on which the process is exporting the object. + * @param type The interface they are exporting it on. This type must have the same full class name and exposed method signatures + * as the interface the remote object is exporting. + * @param autostart Disable/Enable auto-starting of services in response to calls on this object. + * Default is enabled; when calling a method with auto-start enabled, if the destination is a well-known name + * and is not owned the bus will attempt to start a process to take the name. When disabled an error is + * returned immediately. + * @return A reference to a remote object. + * @throws ClassCastException If type is not a sub-type of DBusInterface + * @throws DBusException If busname or objectpath are incorrectly formatted or type is not in a package. + */ + @SuppressWarnings("unchecked") + public I getRemoteObject(String busname, String objectpath, Class type, boolean autostart) throws DBusException { + if (null == busname) throw new DBusException(getString("nullBusName")); + if (null == objectpath) throw new DBusException(getString("nullObjectPath")); + if (null == type) throw new ClassCastException(getString("notDBusInterface")); + + if ((!busname.matches(BUSNAME_REGEX) && !busname.matches(CONNID_REGEX)) + || busname.length() > MAX_NAME_LENGTH) + throw new DBusException(getString("invalidBusName") + busname); + + if (!objectpath.matches(OBJECT_REGEX) || objectpath.length() > MAX_NAME_LENGTH) + throw new DBusException(getString("invalidObjectPath") + objectpath); + + if (!DBusInterface.class.isAssignableFrom(type)) throw new ClassCastException(getString("notDBusInterface")); + + // don't let people import things which don't have a + // valid D-Bus interface name + if (type.getName().equals(type.getSimpleName())) + throw new DBusException(getString("interfaceNotAllowedOutsidePackage")); + + RemoteObject ro = new RemoteObject(busname, objectpath, type, autostart); + I i = (I) Proxy.newProxyInstance(type.getClassLoader(), + new Class[]{type}, new RemoteInvocationHandler(this, ro)); + importedObjects.put(i, ro); + return i; + } + + /** + * Remove a Signal Handler. + * Stops listening for this signal. + * + * @param type The signal to watch for. + * @param source The source of the signal. + * @throws DBusException If listening for the signal on the bus failed. + * @throws ClassCastException If type is not a sub-type of DBusSignal. + */ + public void removeSigHandler(Class type, String source, DBusSigHandler handler) throws DBusException { + if (!DBusSignal.class.isAssignableFrom(type)) throw new ClassCastException(getString("notDBusSignal")); + if (source.matches(BUSNAME_REGEX)) throw new DBusException(getString("cannotWatchSignalsWellKnownBussName")); + if (!source.matches(CONNID_REGEX) || source.length() > MAX_NAME_LENGTH) + throw new DBusException(getString("invalidBusName") + source); + removeSigHandler(new DBusMatchRule(type, source, null), handler); + } + + /** + * Remove a Signal Handler. + * Stops listening for this signal. + * + * @param type The signal to watch for. + * @param source The source of the signal. + * @param object The object emitting the signal. + * @throws DBusException If listening for the signal on the bus failed. + * @throws ClassCastException If type is not a sub-type of DBusSignal. + */ + public void removeSigHandler(Class type, String source, DBusInterface object, DBusSigHandler handler) throws DBusException { + if (!DBusSignal.class.isAssignableFrom(type)) throw new ClassCastException(getString("notDBusSignal")); + if (source.matches(BUSNAME_REGEX)) throw new DBusException(getString("cannotWatchSignalsWellKnownBussName")); + if (!source.matches(CONNID_REGEX) || source.length() > MAX_NAME_LENGTH) + throw new DBusException(getString("invalidBusName") + source); + String objectpath = importedObjects.get(object).objectpath; + if (!objectpath.matches(OBJECT_REGEX) || objectpath.length() > MAX_NAME_LENGTH) + throw new DBusException(getString("invalidObjectPath") + objectpath); + removeSigHandler(new DBusMatchRule(type, source, objectpath), handler); + } + + protected void removeSigHandler(DBusMatchRule rule, DBusSigHandler handler) throws DBusException { + + SignalTuple key = new SignalTuple(rule.getInterface(), rule.getMember(), rule.getObject(), rule.getSource()); + synchronized (handledSignals) { + Vector> v = handledSignals.get(key); + if (null != v) { + v.remove(handler); + if (0 == v.size()) { + handledSignals.remove(key); + try { + _dbus.RemoveMatch(rule.toString()); + } catch (NotConnected NC) { + if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, NC); + } catch (DBusExecutionException DBEe) { + if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBEe); + throw new DBusException(DBEe.getMessage()); + } + } + } + } + } + + /** + * Add a Signal Handler. + * Adds a signal handler to call when a signal is received which matches the specified type, name and source. + * + * @param type The signal to watch for. + * @param source The process which will send the signal. This MUST be a unique bus name and not a well known name. + * @param handler The handler to call when a signal is received. + * @throws DBusException If listening for the signal on the bus failed. + * @throws ClassCastException If type is not a sub-type of DBusSignal. + */ + @SuppressWarnings("unchecked") + public void addSigHandler(Class type, String source, DBusSigHandler handler) throws DBusException { + if (!DBusSignal.class.isAssignableFrom(type)) throw new ClassCastException(getString("notDBusSignal")); + if (source.matches(BUSNAME_REGEX)) throw new DBusException(getString("cannotWatchSignalsWellKnownBussName")); + if (!source.matches(CONNID_REGEX) || source.length() > MAX_NAME_LENGTH) + throw new DBusException(getString("invalidBusName") + source); + addSigHandler(new DBusMatchRule(type, source, null), (DBusSigHandler) handler); + } + + /** + * Add a Signal Handler. + * Adds a signal handler to call when a signal is received which matches the specified type, name, source and object. + * + * @param type The signal to watch for. + * @param source The process which will send the signal. This MUST be a unique bus name and not a well known name. + * @param object The object from which the signal will be emitted + * @param handler The handler to call when a signal is received. + * @throws DBusException If listening for the signal on the bus failed. + * @throws ClassCastException If type is not a sub-type of DBusSignal. + */ + @SuppressWarnings("unchecked") + public void addSigHandler(Class type, String source, DBusInterface object, DBusSigHandler handler) throws DBusException { + if (!DBusSignal.class.isAssignableFrom(type)) throw new ClassCastException(getString("notDBusSignal")); + if (source.matches(BUSNAME_REGEX)) throw new DBusException(getString("cannotWatchSignalsWellKnownBussName")); + if (!source.matches(CONNID_REGEX) || source.length() > MAX_NAME_LENGTH) + throw new DBusException(getString("invalidBusName") + source); + String objectpath = importedObjects.get(object).objectpath; + if (!objectpath.matches(OBJECT_REGEX) || objectpath.length() > MAX_NAME_LENGTH) + throw new DBusException(getString("invalidObjectPath") + objectpath); + addSigHandler(new DBusMatchRule(type, source, objectpath), (DBusSigHandler) handler); + } + + protected void addSigHandler(DBusMatchRule rule, DBusSigHandler handler) throws DBusException { + try { + _dbus.AddMatch(rule.toString()); + } catch (DBusExecutionException DBEe) { + if (EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBEe); + throw new DBusException(DBEe.getMessage()); + } + SignalTuple key = new SignalTuple(rule.getInterface(), rule.getMember(), rule.getObject(), rule.getSource()); + synchronized (handledSignals) { + Vector> v = handledSignals.get(key); + if (null == v) { + v = new Vector>(); + v.add(handler); + handledSignals.put(key, v); + } else + v.add(handler); + } + } + + /** + * Disconnect from the Bus. + * This only disconnects when the last reference to the bus has disconnect called on it + * or has been destroyed. + */ + public void disconnect() { + synchronized (conn) { + synchronized (_reflock) { + if (0 == --_refcount) { + if (Debug.debug) Debug.print(Debug.INFO, "Disconnecting DBusConnection"); + // Set all pending messages to have an error. + try { + Error err = new Error( + "org.freedesktop.DBus.Local", "org.freedesktop.DBus.Local.disconnected", 0, "s", new Object[]{getString("disconnected")}); + synchronized (pendingCalls) { + long[] set = pendingCalls.getKeys(); + for (long l : set) + if (-1 != l) { + MethodCall m = pendingCalls.remove(l); + if (null != m) + m.setReply(err); + } + pendingCalls = null; + } + synchronized (pendingErrors) { + pendingErrors.add(err); + } + } catch (DBusException DBe) { + } + + conn.remove(addr); + super.disconnect(); + } + } + } + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/DBusInterface.java b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusInterface.java new file mode 100644 index 0000000000..b9e404c100 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusInterface.java @@ -0,0 +1,31 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +/** + * Denotes a class as exportable or a remote interface which can be called. + *

+ * Any interface which should be exported or imported should extend this + * interface. All public methods from that interface are exported/imported + * with the given method signatures. + *

+ *

+ * All method calls on exported objects are run in their own threads. + * Application writers are responsible for any concurrency issues. + *

+ */ +public interface DBusInterface { + /** + * Returns true on remote objects. + * Local objects implementing this interface MUST return false. + */ + public boolean isRemote(); +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/DBusInterfaceName.java b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusInterfaceName.java new file mode 100644 index 0000000000..0fc705685a --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusInterfaceName.java @@ -0,0 +1,28 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Force the interface name to be different to the Java class name. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface DBusInterfaceName { + /** + * The replacement interface name. + */ + String value(); +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/DBusMap.java b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusMap.java new file mode 100644 index 0000000000..6590e62448 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusMap.java @@ -0,0 +1,150 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.Vector; + +class DBusMap implements Map { + Object[][] entries; + + public DBusMap(Object[][] entries) { + this.entries = entries; + } + + class Entry implements Map.Entry, Comparable { + private int entry; + + public Entry(int i) { + this.entry = i; + } + + public boolean equals(Object o) { + if (null == o) return false; + if (!(o instanceof DBusMap.Entry)) return false; + return this.entry == ((Entry) o).entry; + } + + @SuppressWarnings("unchecked") + public K getKey() { + return (K) entries[entry][0]; + } + + @SuppressWarnings("unchecked") + public V getValue() { + return (V) entries[entry][1]; + } + + public int hashCode() { + return entries[entry][0].hashCode(); + } + + public V setValue(V value) { + throw new UnsupportedOperationException(); + } + + public int compareTo(Entry e) { + return entry - e.entry; + } + } + + public void clear() { + throw new UnsupportedOperationException(); + } + + public boolean containsKey(Object key) { + for (int i = 0; i < entries.length; i++) + if (key == entries[i][0] || (key != null && key.equals(entries[i][0]))) + return true; + return false; + } + + public boolean containsValue(Object value) { + for (int i = 0; i < entries.length; i++) + if (value == entries[i][1] || (value != null && value.equals(entries[i][1]))) + return true; + return false; + } + + public Set> entrySet() { + Set> s = new TreeSet>(); + for (int i = 0; i < entries.length; i++) + s.add(new Entry(i)); + return s; + } + + @SuppressWarnings("unchecked") + public V get(Object key) { + for (int i = 0; i < entries.length; i++) + if (key == entries[i][0] || (key != null && key.equals(entries[i][0]))) + return (V) entries[i][1]; + return null; + } + + public boolean isEmpty() { + return entries.length == 0; + } + + @SuppressWarnings("unchecked") + public Set keySet() { + Set s = new TreeSet(); + for (Object[] entry : entries) + s.add((K) entry[0]); + return s; + } + + public V put(K key, V value) { + throw new UnsupportedOperationException(); + } + + public void putAll(Map t) { + throw new UnsupportedOperationException(); + } + + public V remove(Object key) { + throw new UnsupportedOperationException(); + } + + public int size() { + return entries.length; + } + + @SuppressWarnings("unchecked") + public Collection values() { + List l = new Vector(); + for (Object[] entry : entries) + l.add((V) entry[1]); + return l; + } + + public int hashCode() { + return Arrays.deepHashCode(entries); + } + + @SuppressWarnings("unchecked") + public boolean equals(Object o) { + if (null == o) return false; + if (!(o instanceof Map)) return false; + return ((Map) o).entrySet().equals(entrySet()); + } + + public String toString() { + String s = "{ "; + for (int i = 0; i < entries.length; i++) + s += entries[i][0] + " => " + entries[i][1] + ","; + return s.replaceAll(".$", " }"); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/DBusMatchRule.java b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusMatchRule.java new file mode 100644 index 0000000000..fa886c0cf9 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusMatchRule.java @@ -0,0 +1,151 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.exceptions.DBusExecutionException; + +import java.util.HashMap; + +import static org.freedesktop.dbus.Gettext.getString; + +public class DBusMatchRule { + /* signal, error, method_call, method_reply */ + private String type; + private String iface; + private String member; + private String object; + private String source; + private static HashMap> signalTypeMap = + new HashMap>(); + + static Class getCachedSignalType(String type) { + return signalTypeMap.get(type); + } + + public DBusMatchRule(String type, String iface, String member) { + this.type = type; + this.iface = iface; + this.member = member; + } + + public DBusMatchRule(DBusExecutionException e) throws DBusException { + this(e.getClass()); + member = null; + type = "error"; + } + + public DBusMatchRule(Message m) { + iface = m.getInterface(); + member = m.getName(); + if (m instanceof DBusSignal) + type = "signal"; + else if (m instanceof Error) { + type = "error"; + member = null; + } else if (m instanceof MethodCall) + type = "method_call"; + else if (m instanceof MethodReturn) + type = "method_reply"; + } + + public DBusMatchRule(Class c, String method) throws DBusException { + this(c); + member = method; + type = "method_call"; + } + + public DBusMatchRule(Class c, String source, String object) throws DBusException { + this(c); + this.source = source; + this.object = object; + } + + @SuppressWarnings("unchecked") + public DBusMatchRule(Class c) throws DBusException { + if (DBusInterface.class.isAssignableFrom(c)) { + if (null != c.getAnnotation(DBusInterfaceName.class)) + iface = c.getAnnotation(DBusInterfaceName.class).value(); + else + iface = AbstractConnection.dollar_pattern.matcher(c.getName()).replaceAll("."); + if (!iface.matches(".*\\..*")) + throw new DBusException(getString("interfaceMustBeDefinedPackage")); + member = null; + type = null; + } else if (DBusSignal.class.isAssignableFrom(c)) { + if (null == c.getEnclosingClass()) + throw new DBusException(getString("signalsMustBeMemberOfClass")); + else if (null != c.getEnclosingClass().getAnnotation(DBusInterfaceName.class)) + iface = c.getEnclosingClass().getAnnotation(DBusInterfaceName.class).value(); + else + iface = AbstractConnection.dollar_pattern.matcher(c.getEnclosingClass().getName()).replaceAll("."); + // Don't export things which are invalid D-Bus interfaces + if (!iface.matches(".*\\..*")) + throw new DBusException(getString("interfaceMustBeDefinedPackage")); + if (c.isAnnotationPresent(DBusMemberName.class)) + member = c.getAnnotation(DBusMemberName.class).value(); + else + member = c.getSimpleName(); + signalTypeMap.put(iface + '$' + member, (Class) c); + type = "signal"; + } else if (Error.class.isAssignableFrom(c)) { + if (null != c.getAnnotation(DBusInterfaceName.class)) + iface = c.getAnnotation(DBusInterfaceName.class).value(); + else + iface = AbstractConnection.dollar_pattern.matcher(c.getName()).replaceAll("."); + if (!iface.matches(".*\\..*")) + throw new DBusException(getString("interfaceMustBeDefinedPackage")); + member = null; + type = "error"; + } else if (DBusExecutionException.class.isAssignableFrom(c)) { + if (null != c.getClass().getAnnotation(DBusInterfaceName.class)) + iface = c.getClass().getAnnotation(DBusInterfaceName.class).value(); + else + iface = AbstractConnection.dollar_pattern.matcher(c.getClass().getName()).replaceAll("."); + if (!iface.matches(".*\\..*")) + throw new DBusException(getString("interfaceMustBeDefinedPackage")); + member = null; + type = "error"; + } else + throw new DBusException(getString("invalidTypeMatchRule") + c); + } + + public String toString() { + String s = null; + if (null != type) s = null == s ? "type='" + type + "'" : s + ",type='" + type + "'"; + if (null != member) s = null == s ? "member='" + member + "'" : s + ",member='" + member + "'"; + if (null != iface) s = null == s ? "interface='" + iface + "'" : s + ",interface='" + iface + "'"; + if (null != source) s = null == s ? "sender='" + source + "'" : s + ",sender='" + source + "'"; + if (null != object) s = null == s ? "path='" + object + "'" : s + ",path='" + object + "'"; + return s; + } + + public String getType() { + return type; + } + + public String getInterface() { + return iface; + } + + public String getMember() { + return member; + } + + public String getSource() { + return source; + } + + public String getObject() { + return object; + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/DBusMemberName.java b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusMemberName.java new file mode 100644 index 0000000000..25d30d7e37 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusMemberName.java @@ -0,0 +1,29 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Force the member (method/signal) name on the bus to be different to the Java name. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE, ElementType.METHOD}) +public @interface DBusMemberName { + /** + * The replacement member name. + */ + String value(); +} + diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/DBusSerializable.java b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusSerializable.java new file mode 100644 index 0000000000..8d9fea58ae --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusSerializable.java @@ -0,0 +1,39 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import org.freedesktop.dbus.exceptions.DBusException; + +/** + * Custom classes may be sent over DBus if they implement this interface. + *

+ * In addition to the serialize method, classes MUST implement + * a deserialize method which returns null and takes as it's arguments + * all the DBus types the class will be serialied to in order and + * with type parameterisation. They MUST also provide a + * zero-argument constructor. + *

+ *

+ * The serialize method should return the class properties you wish to + * serialize, correctly formatted for the wire + * (DBusConnection.convertParameters() can help with this), in order in an + * Object array. + *

+ *

+ * The deserialize method will be called once after the zero-argument + * constructor. This should contain all the code to initialise the object + * from the types. + *

+ */ +public interface DBusSerializable { + public Object[] serialize() throws DBusException; +} + diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/DBusSigHandler.java b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusSigHandler.java new file mode 100644 index 0000000000..6e40a82059 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusSigHandler.java @@ -0,0 +1,27 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +/** + * Handle a signal on DBus. + * All Signal handlers are run in their own Thread. + * Application writers are responsible for managing any concurrency issues. + */ +public interface DBusSigHandler { + /** + * Handle a signal. + * + * @param s The signal to handle. If such a class exists, the + * signal will be an instance of the class with the correct type signature. + * Otherwise it will be an instance of DBusSignal + */ + public void handle(T s); +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/DBusSignal.java b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusSignal.java new file mode 100644 index 0000000000..96ba6b3e70 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/DBusSignal.java @@ -0,0 +1,259 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import cx.ath.matthew.debug.Debug; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.exceptions.MessageFormatException; + +import java.lang.reflect.Constructor; +import java.lang.reflect.GenericDeclaration; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Vector; + +import static org.freedesktop.dbus.Gettext.getString; + +public class DBusSignal extends Message { + DBusSignal() { + } + + public DBusSignal(String source, String path, String iface, String member, String sig, Object... args) throws DBusException { + super(Message.Endian.BIG, Message.MessageType.SIGNAL, (byte) 0); + + if (null == path || null == member || null == iface) + throw new MessageFormatException(getString("missingPathInterfaceSignal")); + headers.put(Message.HeaderField.PATH, path); + headers.put(Message.HeaderField.MEMBER, member); + headers.put(Message.HeaderField.INTERFACE, iface); + + Vector hargs = new Vector(); + hargs.add(new Object[]{Message.HeaderField.PATH, new Object[]{ArgumentType.OBJECT_PATH_STRING, path}}); + hargs.add(new Object[]{Message.HeaderField.INTERFACE, new Object[]{ArgumentType.STRING_STRING, iface}}); + hargs.add(new Object[]{Message.HeaderField.MEMBER, new Object[]{ArgumentType.STRING_STRING, member}}); + + if (null != source) { + headers.put(Message.HeaderField.SENDER, source); + hargs.add(new Object[]{Message.HeaderField.SENDER, new Object[]{ArgumentType.STRING_STRING, source}}); + } + + if (null != sig) { + hargs.add(new Object[]{Message.HeaderField.SIGNATURE, new Object[]{ArgumentType.SIGNATURE_STRING, sig}}); + headers.put(Message.HeaderField.SIGNATURE, sig); + setArgs(args); + } + + blen = new byte[4]; + appendBytes(blen); + append("ua(yv)", ++serial, hargs.toArray()); + pad((byte) 8); + + long c = bytecounter; + if (null != sig) append(sig, args); + marshallint(bytecounter - c, blen, 0, 4); + bodydone = true; + } + + static class internalsig extends DBusSignal { + public internalsig(String source, String objectpath, String type, String name, String sig, Object[] parameters, long serial) throws DBusException { + super(source, objectpath, type, name, sig, parameters, serial); + } + } + + private static Map, Type[]> typeCache = new HashMap, Type[]>(); + private static Map> classCache = new HashMap>(); + private static Map, Constructor> conCache = new HashMap, Constructor>(); + private static Map signames = new HashMap(); + private static Map intnames = new HashMap(); + private Class c; + private boolean bodydone = false; + private byte[] blen; + + static void addInterfaceMap(String java, String dbus) { + intnames.put(dbus, java); + } + + static void addSignalMap(String java, String dbus) { + signames.put(dbus, java); + } + + static DBusSignal createSignal(Class c, String source, String objectpath, String sig, long serial, Object... parameters) throws DBusException { + String type = ""; + if (null != c.getEnclosingClass()) { + if (null != c.getEnclosingClass().getAnnotation(DBusInterfaceName.class)) + type = c.getEnclosingClass().getAnnotation(DBusInterfaceName.class).value(); + else + type = AbstractConnection.dollar_pattern.matcher(c.getEnclosingClass().getName()).replaceAll("."); + + } else + throw new DBusException(getString("signalsMustBeMemberOfClass")); + DBusSignal s = new internalsig(source, objectpath, type, c.getSimpleName(), sig, parameters, serial); + s.c = c; + return s; + } + + @SuppressWarnings("unchecked") + private static Class createSignalClass(String intname, String signame) throws DBusException { + String name = intname + '$' + signame; + Class c = classCache.get(name); + if (null == c) c = DBusMatchRule.getCachedSignalType(name); + if (null != c) return c; + do { + try { + c = (Class) Class.forName(name); + } catch (ClassNotFoundException CNFe) { + } + name = name.replaceAll("\\.([^\\.]*)$", "\\$$1"); + } while (null == c && name.matches(".*\\..*")); + if (null == c) + throw new DBusException(getString("cannotCreateClassFromSignal") + intname + '.' + signame); + classCache.put(name, c); + return c; + } + + @SuppressWarnings("unchecked") + DBusSignal createReal(AbstractConnection conn) throws DBusException { + String intname = intnames.get(getInterface()); + String signame = signames.get(getName()); + if (null == intname) intname = getInterface(); + if (null == signame) signame = getName(); + if (null == c) + c = createSignalClass(intname, signame); + if (Debug.debug) Debug.print(Debug.DEBUG, "Converting signal to type: " + c); + Type[] types = typeCache.get(c); + Constructor con = conCache.get(c); + if (null == types) { + con = (Constructor) c.getDeclaredConstructors()[0]; + conCache.put(c, con); + Type[] ts = con.getGenericParameterTypes(); + types = new Type[ts.length - 1]; + for (int i = 1; i < ts.length; i++) + if (ts[i] instanceof TypeVariable) + for (Type b : ((TypeVariable) ts[i]).getBounds()) + types[i - 1] = b; + else + types[i - 1] = ts[i]; + typeCache.put(c, types); + } + + try { + DBusSignal s; + Object[] args = Marshalling.deSerializeParameters(getParameters(), types, conn); + if (null == args) s = (DBusSignal) con.newInstance(getPath()); + else { + Object[] params = new Object[args.length + 1]; + params[0] = getPath(); + System.arraycopy(args, 0, params, 1, args.length); + + if (Debug.debug) + Debug.print(Debug.DEBUG, "Creating signal of type " + c + " with parameters " + Arrays.deepToString(params)); + s = (DBusSignal) con.newInstance(params); + } + s.headers = headers; + s.wiredata = wiredata; + s.bytecounter = wiredata.length; + return s; + } catch (Exception e) { + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); + throw new DBusException(e.getMessage()); + } + } + + /** + * Create a new signal. + * This contructor MUST be called by all sub classes. + * + * @param objectpath The path to the object this is emitted from. + * @param args The parameters of the signal. + * @throws DBusException This is thrown if the subclass is incorrectly defined. + */ + @SuppressWarnings("unchecked") + protected DBusSignal(String objectpath, Object... args) throws DBusException { + super(Message.Endian.BIG, Message.MessageType.SIGNAL, (byte) 0); + + if (!objectpath.matches(AbstractConnection.OBJECT_REGEX)) + throw new DBusException(getString("invalidObjectPath") + objectpath); + + Class tc = getClass(); + String member; + if (tc.isAnnotationPresent(DBusMemberName.class)) + member = tc.getAnnotation(DBusMemberName.class).value(); + else + member = tc.getSimpleName(); + String iface = null; + Class enc = tc.getEnclosingClass(); + if (null == enc || + !DBusInterface.class.isAssignableFrom(enc) || + enc.getName().equals(enc.getSimpleName())) + throw new DBusException(getString("signalsMustBeMemberOfClass")); + else if (null != enc.getAnnotation(DBusInterfaceName.class)) + iface = enc.getAnnotation(DBusInterfaceName.class).value(); + else + iface = AbstractConnection.dollar_pattern.matcher(enc.getName()).replaceAll("."); + + headers.put(Message.HeaderField.PATH, objectpath); + headers.put(Message.HeaderField.MEMBER, member); + headers.put(Message.HeaderField.INTERFACE, iface); + + Vector hargs = new Vector(); + hargs.add(new Object[]{Message.HeaderField.PATH, new Object[]{ArgumentType.OBJECT_PATH_STRING, objectpath}}); + hargs.add(new Object[]{Message.HeaderField.INTERFACE, new Object[]{ArgumentType.STRING_STRING, iface}}); + hargs.add(new Object[]{Message.HeaderField.MEMBER, new Object[]{ArgumentType.STRING_STRING, member}}); + + String sig = null; + if (0 < args.length) { + try { + Type[] types = typeCache.get(tc); + if (null == types) { + Constructor con = (Constructor) tc.getDeclaredConstructors()[0]; + conCache.put(tc, con); + Type[] ts = con.getGenericParameterTypes(); + types = new Type[ts.length - 1]; + for (int i = 1; i <= types.length; i++) + if (ts[i] instanceof TypeVariable) + types[i - 1] = ((TypeVariable) ts[i]).getBounds()[0]; + else + types[i - 1] = ts[i]; + typeCache.put(tc, types); + } + sig = Marshalling.getDBusType(types); + hargs.add(new Object[]{Message.HeaderField.SIGNATURE, new Object[]{ArgumentType.SIGNATURE_STRING, sig}}); + headers.put(Message.HeaderField.SIGNATURE, sig); + setArgs(args); + } catch (Exception e) { + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); + throw new DBusException(getString("errorAddSignalParameters") + e.getMessage()); + } + } + + blen = new byte[4]; + appendBytes(blen); + append("ua(yv)", ++serial, hargs.toArray()); + pad((byte) 8); + } + + void appendbody(AbstractConnection conn) throws DBusException { + if (bodydone) return; + + Type[] types = typeCache.get(getClass()); + Object[] args = Marshalling.convertParameters(getParameters(), types, conn); + setArgs(args); + String sig = getSig(); + + long c = bytecounter; + if (null != args && 0 < args.length) append(sig, args); + marshallint(bytecounter - c, blen, 0, 4); + bodydone = true; + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/EfficientMap.java b/federation/sssd/src/main/java/org/freedesktop/dbus/EfficientMap.java new file mode 100644 index 0000000000..32cc7bb5e2 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/EfficientMap.java @@ -0,0 +1,122 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +/** + * Provides a long => MethodCall map which doesn't allocate objects + * on insertion/removal. Keys must be inserted in ascending order. + */ +class EfficientMap { + private long[] kv; + private MethodCall[] vv; + private int start; + private int end; + private int init_size; + + public EfficientMap(int initial_size) { + init_size = initial_size; + shrink(); + } + + private void grow() { + // create new vectors twice as long + long[] oldkv = kv; + kv = new long[oldkv.length * 2]; + MethodCall[] oldvv = vv; + vv = new MethodCall[oldvv.length * 2]; + + // copy start->length to the start of the new vector + System.arraycopy(oldkv, start, kv, 0, oldkv.length - start); + System.arraycopy(oldvv, start, vv, 0, oldvv.length - start); + // copy 0->end to the next part of the new vector + if (end != (oldkv.length - 1)) { + System.arraycopy(oldkv, 0, kv, oldkv.length - start, end + 1); + System.arraycopy(oldvv, 0, vv, oldvv.length - start, end + 1); + } + // reposition pointers + start = 0; + end = oldkv.length; + } + + // create a new vector with just the valid keys in and return it + public long[] getKeys() { + int size; + if (start < end) size = end - start; + else size = kv.length - (start - end); + long[] lv = new long[size]; + int copya; + if (size > kv.length - start) copya = kv.length - start; + else copya = size; + System.arraycopy(kv, start, lv, 0, copya); + if (copya < size) { + System.arraycopy(kv, 0, lv, copya, size - copya); + } + return lv; + } + + private void shrink() { + if (null != kv && kv.length == init_size) return; + // reset to original size + kv = new long[init_size]; + vv = new MethodCall[init_size]; + start = 0; + end = 0; + } + + public void put(long l, MethodCall m) { + // put this at the end + kv[end] = l; + vv[end] = m; + // move the end + if (end == (kv.length - 1)) end = 0; + else end++; + // if we are out of space, grow. + if (end == start) grow(); + } + + public MethodCall remove(long l) { + // find the item + int pos = find(l); + // if we don't have it return null + if (-1 == pos) return null; + // get the value + MethodCall m = vv[pos]; + // set it as unused + vv[pos] = null; + kv[pos] = -1; + // move the pointer to the first full element + while (-1 == kv[start]) { + if (start == (kv.length - 1)) start = 0; + else start++; + // if we have emptied the list, shrink it + if (start == end) { + shrink(); + break; + } + } + return m; + } + + public boolean contains(long l) { + // check if find succeeds + return -1 != find(l); + } + + /* could binary search, but it's probably the first one */ + private int find(long l) { + int i = start; + while (i != end && kv[i] != l) + if (i == (kv.length - 1)) i = 0; + else i++; + if (i == end) return -1; + return i; + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/EfficientQueue.java b/federation/sssd/src/main/java/org/freedesktop/dbus/EfficientQueue.java new file mode 100644 index 0000000000..a60c88735c --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/EfficientQueue.java @@ -0,0 +1,109 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import cx.ath.matthew.debug.Debug; + +/** + * Provides a Message queue which doesn't allocate objects + * on insertion/removal. + */ +class EfficientQueue { + private Message[] mv; + private int start; + private int end; + private int init_size; + + public EfficientQueue(int initial_size) { + init_size = initial_size; + shrink(); + } + + private void grow() { + if (Debug.debug) Debug.print(Debug.DEBUG, "Growing"); + // create new vectors twice as long + Message[] oldmv = mv; + mv = new Message[oldmv.length * 2]; + + // copy start->length to the start of the new vector + System.arraycopy(oldmv, start, mv, 0, oldmv.length - start); + // copy 0->end to the next part of the new vector + if (end != (oldmv.length - 1)) { + System.arraycopy(oldmv, 0, mv, oldmv.length - start, end + 1); + } + // reposition pointers + start = 0; + end = oldmv.length; + } + + // create a new vector with just the valid keys in and return it + public Message[] getKeys() { + if (start == end) return new Message[0]; + Message[] lv; + if (start < end) { + int size = end - start; + lv = new Message[size]; + System.arraycopy(mv, start, lv, 0, size); + } else { + int size = mv.length - start + end; + lv = new Message[size]; + System.arraycopy(mv, start, lv, 0, mv.length - start); + System.arraycopy(mv, 0, lv, mv.length - start, end); + } + return lv; + } + + private void shrink() { + if (Debug.debug) Debug.print(Debug.DEBUG, "Shrinking"); + if (null != mv && mv.length == init_size) return; + // reset to original size + mv = new Message[init_size]; + start = 0; + end = 0; + } + + public void add(Message m) { + if (Debug.debug) Debug.print(Debug.DEBUG, "Enqueueing Message " + m); + // put this at the end + mv[end] = m; + // move the end + if (end == (mv.length - 1)) end = 0; + else end++; + // if we are out of space, grow. + if (end == start) grow(); + } + + public Message remove() { + if (start == end) return null; + // find the item + int pos = start; + // get the value + Message m = mv[pos]; + // set it as unused + mv[pos] = null; + if (start == (mv.length - 1)) start = 0; + else start++; + if (Debug.debug) Debug.print(Debug.DEBUG, "Dequeueing " + m); + return m; + } + + public boolean isEmpty() { + // check if find succeeds + return start == end; + } + + public int size() { + if (end >= start) + return end - start; + else + return mv.length - start + end; + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/Error.java b/federation/sssd/src/main/java/org/freedesktop/dbus/Error.java new file mode 100644 index 0000000000..3350e9584a --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/Error.java @@ -0,0 +1,144 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import cx.ath.matthew.debug.Debug; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.exceptions.DBusExecutionException; +import org.freedesktop.dbus.exceptions.MessageFormatException; +import org.freedesktop.dbus.exceptions.NotConnected; + +import java.lang.reflect.Constructor; +import java.util.Vector; + +import static org.freedesktop.dbus.Gettext.getString; + +/** + * Error messages which can be sent over the bus. + */ +public class Error extends Message { + Error() { + } + + public Error(String dest, String errorName, long replyserial, String sig, Object... args) throws DBusException { + this(null, dest, errorName, replyserial, sig, args); + } + + public Error(String source, String dest, String errorName, long replyserial, String sig, Object... args) throws DBusException { + super(Message.Endian.BIG, Message.MessageType.ERROR, (byte) 0); + + if (null == errorName) + throw new MessageFormatException(getString("missingErrorName")); + headers.put(Message.HeaderField.REPLY_SERIAL, replyserial); + headers.put(Message.HeaderField.ERROR_NAME, errorName); + + Vector hargs = new Vector(); + hargs.add(new Object[]{Message.HeaderField.ERROR_NAME, new Object[]{ArgumentType.STRING_STRING, errorName}}); + hargs.add(new Object[]{Message.HeaderField.REPLY_SERIAL, new Object[]{ArgumentType.UINT32_STRING, replyserial}}); + + if (null != source) { + headers.put(Message.HeaderField.SENDER, source); + hargs.add(new Object[]{Message.HeaderField.SENDER, new Object[]{ArgumentType.STRING_STRING, source}}); + } + + if (null != dest) { + headers.put(Message.HeaderField.DESTINATION, dest); + hargs.add(new Object[]{Message.HeaderField.DESTINATION, new Object[]{ArgumentType.STRING_STRING, dest}}); + } + + if (null != sig) { + hargs.add(new Object[]{Message.HeaderField.SIGNATURE, new Object[]{ArgumentType.SIGNATURE_STRING, sig}}); + headers.put(Message.HeaderField.SIGNATURE, sig); + setArgs(args); + } + + byte[] blen = new byte[4]; + appendBytes(blen); + append("ua(yv)", serial, hargs.toArray()); + pad((byte) 8); + + long c = bytecounter; + if (null != sig) append(sig, args); + marshallint(bytecounter - c, blen, 0, 4); + } + + public Error(String source, Message m, Throwable e) throws DBusException { + this(source, m.getSource(), AbstractConnection.dollar_pattern.matcher(e.getClass().getName()).replaceAll("."), m.getSerial(), "s", e.getMessage()); + } + + public Error(Message m, Throwable e) throws DBusException { + this(m.getSource(), AbstractConnection.dollar_pattern.matcher(e.getClass().getName()).replaceAll("."), m.getSerial(), "s", e.getMessage()); + } + + @SuppressWarnings("unchecked") + private static Class createExceptionClass(String name) { + if (name == "org.freedesktop.DBus.Local.disconnected") return NotConnected.class; + Class c = null; + do { + try { + c = (Class) Class.forName(name); + } catch (ClassNotFoundException CNFe) { + } + name = name.replaceAll("\\.([^\\.]*)$", "\\$$1"); + } while (null == c && name.matches(".*\\..*")); + return c; + } + + /** + * Turns this into an exception of the correct type + */ + public DBusExecutionException getException() { + try { + Class c = createExceptionClass(getName()); + if (null == c || !DBusExecutionException.class.isAssignableFrom(c)) c = DBusExecutionException.class; + Constructor con = c.getConstructor(String.class); + DBusExecutionException ex; + Object[] args = getParameters(); + if (null == args || 0 == args.length) + ex = con.newInstance(""); + else { + String s = ""; + for (Object o : args) + s += o + " "; + ex = con.newInstance(s.trim()); + } + ex.setType(getName()); + return ex; + } catch (Exception e) { + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug && null != e.getCause()) + Debug.print(Debug.ERR, e.getCause()); + DBusExecutionException ex; + Object[] args = null; + try { + args = getParameters(); + } catch (Exception ee) { + } + if (null == args || 0 == args.length) + ex = new DBusExecutionException(""); + else { + String s = ""; + for (Object o : args) + s += o + " "; + ex = new DBusExecutionException(s.trim()); + } + ex.setType(getName()); + return ex; + } + } + + /** + * Throw this as an exception of the correct type + */ + public void throwException() throws DBusExecutionException { + throw getException(); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/ExportedObject.java b/federation/sssd/src/main/java/org/freedesktop/dbus/ExportedObject.java new file mode 100644 index 0000000000..b9719a1515 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/ExportedObject.java @@ -0,0 +1,165 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.exceptions.DBusExecutionException; + +import java.lang.annotation.Annotation; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; + +import static org.freedesktop.dbus.Gettext.getString; + +class ExportedObject { + @SuppressWarnings("unchecked") + private String getAnnotations(AnnotatedElement c) { + String ans = ""; + for (Annotation a : c.getDeclaredAnnotations()) { + Class t = a.annotationType(); + String value = ""; + try { + Method m = t.getMethod("value"); + value = m.invoke(a).toString(); + } catch (NoSuchMethodException NSMe) { + } catch (InvocationTargetException ITe) { + } catch (IllegalAccessException IAe) { + } + + ans += " \n"; + } + return ans; + } + + @SuppressWarnings("unchecked") + private Map getExportedMethods(Class c) throws DBusException { + if (DBusInterface.class.equals(c)) return new HashMap(); + Map m = new HashMap(); + for (Class i : c.getInterfaces()) + if (DBusInterface.class.equals(i)) { + // add this class's public methods + if (null != c.getAnnotation(DBusInterfaceName.class)) { + String name = ((DBusInterfaceName) c.getAnnotation(DBusInterfaceName.class)).value(); + introspectiondata += " \n"; + DBusSignal.addInterfaceMap(c.getName(), name); + } else { + // don't let people export things which don't have a + // valid D-Bus interface name + if (c.getName().equals(c.getSimpleName())) + throw new DBusException(getString("interfaceNotAllowedOutsidePackage")); + if (c.getName().length() > DBusConnection.MAX_NAME_LENGTH) + throw new DBusException(getString("introspectInterfaceExceedCharacters") + c.getName()); + else + introspectiondata += " \n"; + } + introspectiondata += getAnnotations(c); + for (Method meth : c.getDeclaredMethods()) + if (Modifier.isPublic(meth.getModifiers())) { + String ms = ""; + String name; + if (meth.isAnnotationPresent(DBusMemberName.class)) + name = meth.getAnnotation(DBusMemberName.class).value(); + else + name = meth.getName(); + if (name.length() > DBusConnection.MAX_NAME_LENGTH) + throw new DBusException(getString("introspectMethodExceedCharacters") + name); + introspectiondata += " \n"; + introspectiondata += getAnnotations(meth); + for (Class ex : meth.getExceptionTypes()) + if (DBusExecutionException.class.isAssignableFrom(ex)) + introspectiondata += + " \n"; + for (Type pt : meth.getGenericParameterTypes()) + for (String s : Marshalling.getDBusType(pt)) { + introspectiondata += " \n"; + ms += s; + } + if (!Void.TYPE.equals(meth.getGenericReturnType())) { + if (Tuple.class.isAssignableFrom((Class) meth.getReturnType())) { + ParameterizedType tc = (ParameterizedType) meth.getGenericReturnType(); + Type[] ts = tc.getActualTypeArguments(); + + for (Type t : ts) + if (t != null) + for (String s : Marshalling.getDBusType(t)) + introspectiondata += " \n"; + } else if (Object[].class.equals(meth.getGenericReturnType())) { + throw new DBusException(getString("cannotIntrospectReturnType")); + } else + for (String s : Marshalling.getDBusType(meth.getGenericReturnType())) + introspectiondata += " \n"; + } + introspectiondata += " \n"; + m.put(new MethodTuple(name, ms), meth); + } + for (Class sig : c.getDeclaredClasses()) + if (DBusSignal.class.isAssignableFrom(sig)) { + String name; + if (sig.isAnnotationPresent(DBusMemberName.class)) { + name = ((DBusMemberName) sig.getAnnotation(DBusMemberName.class)).value(); + DBusSignal.addSignalMap(sig.getSimpleName(), name); + } else + name = sig.getSimpleName(); + if (name.length() > DBusConnection.MAX_NAME_LENGTH) + throw new DBusException(getString("introspectSignalExceedCharacters") + name); + introspectiondata += " \n"; + Constructor con = sig.getConstructors()[0]; + Type[] ts = con.getGenericParameterTypes(); + for (int j = 1; j < ts.length; j++) + for (String s : Marshalling.getDBusType(ts[j])) + introspectiondata += " \n"; + introspectiondata += getAnnotations(sig); + introspectiondata += " \n"; + + } + introspectiondata += " \n"; + } else { + // recurse + m.putAll(getExportedMethods(i)); + } + return m; + } + + Map methods; + Reference object; + String introspectiondata; + + public ExportedObject(DBusInterface object, boolean weakreferences) throws DBusException { + if (weakreferences) + this.object = new WeakReference(object); + else + this.object = new StrongReference(object); + introspectiondata = ""; + methods = getExportedMethods(object.getClass()); + introspectiondata += + " \n" + + " \n" + + " \n" + + " \n" + + " \n"; + introspectiondata += + " \n" + + " \n" + + " \n" + + " \n"; + } +} + + diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/Gettext.java b/federation/sssd/src/main/java/org/freedesktop/dbus/Gettext.java new file mode 100644 index 0000000000..a519a6f9a3 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/Gettext.java @@ -0,0 +1,30 @@ +/* + * Pescetti Pseudo-Duplimate Generator + * + * Copyright (C) 2007 Matthew Johnson + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License Version 2 as published by + * the Free Software Foundation. This program is distributed in the hope that + * it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. You should have received a + * copy of the GNU Lesser General Public License along with this program; if not, + * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * To Contact me, please email src@matthew.ath.cx + * + */ +package org.freedesktop.dbus; + +import java.util.ResourceBundle; + +public class Gettext { + private static ResourceBundle myResources = + ResourceBundle.getBundle("en_US"); + + public static String getString(String s) { + return myResources.getString(s); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/InternalSignal.java b/federation/sssd/src/main/java/org/freedesktop/dbus/InternalSignal.java new file mode 100644 index 0000000000..0a3072d477 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/InternalSignal.java @@ -0,0 +1,20 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import org.freedesktop.dbus.exceptions.DBusException; + +class InternalSignal extends DBusSignal { + public InternalSignal(String source, String objectpath, String name, String iface, String sig, long serial, Object... parameters) throws DBusException { + super(objectpath, iface, name, sig, parameters); + this.serial = serial; + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/Marshalling.java b/federation/sssd/src/main/java/org/freedesktop/dbus/Marshalling.java new file mode 100644 index 0000000000..9e88309b10 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/Marshalling.java @@ -0,0 +1,629 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import cx.ath.matthew.debug.Debug; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.types.DBusListType; +import org.freedesktop.dbus.types.DBusMapType; +import org.freedesktop.dbus.types.DBusStructType; + +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Vector; + +import static org.freedesktop.dbus.Gettext.getString; + +/** + * Contains static methods for marshalling values. + */ +public class Marshalling { + private static Map typeCache = new HashMap(); + + /** + * Will return the DBus type corresponding to the given Java type. + * Note, container type should have their ParameterizedType not their + * Class passed in here. + * + * @param c The Java types. + * @return The DBus types. + * @throws DBusException If the given type cannot be converted to a DBus type. + */ + public static String getDBusType(Type[] c) throws DBusException { + StringBuffer sb = new StringBuffer(); + for (Type t : c) + for (String s : getDBusType(t)) + sb.append(s); + return sb.toString(); + } + + /** + * Will return the DBus type corresponding to the given Java type. + * Note, container type should have their ParameterizedType not their + * Class passed in here. + * + * @param c The Java type. + * @return The DBus type. + * @throws DBusException If the given type cannot be converted to a DBus type. + */ + public static String[] getDBusType(Type c) throws DBusException { + String[] cached = typeCache.get(c); + if (null != cached) return cached; + cached = getDBusType(c, false); + typeCache.put(c, cached); + return cached; + } + + /** + * Will return the DBus type corresponding to the given Java type. + * Note, container type should have their ParameterizedType not their + * Class passed in here. + * + * @param c The Java type. + * @param basic If true enforces this to be a non-compound type. (compound types are Maps, Structs and Lists/arrays). + * @return The DBus type. + * @throws DBusException If the given type cannot be converted to a DBus type. + */ + public static String[] getDBusType(Type c, boolean basic) throws DBusException { + return recursiveGetDBusType(c, basic, 0); + } + + private static StringBuffer[] out = new StringBuffer[10]; + + @SuppressWarnings("unchecked") + public static String[] recursiveGetDBusType(Type c, boolean basic, int level) throws DBusException { + if (out.length <= level) { + StringBuffer[] newout = new StringBuffer[out.length]; + System.arraycopy(out, 0, newout, 0, out.length); + out = newout; + } + if (null == out[level]) out[level] = new StringBuffer(); + else out[level].delete(0, out[level].length()); + + if (basic && !(c instanceof Class)) + throw new DBusException(c + getString("notBasicType")); + + if (c instanceof TypeVariable) out[level].append((char) Message.ArgumentType.VARIANT); + else if (c instanceof GenericArrayType) { + out[level].append((char) Message.ArgumentType.ARRAY); + String[] s = recursiveGetDBusType(((GenericArrayType) c).getGenericComponentType(), false, level + 1); + if (s.length != 1) throw new DBusException(getString("multiValuedArrayNotPermitted")); + out[level].append(s[0]); + } else if ((c instanceof Class && + DBusSerializable.class.isAssignableFrom((Class) c)) || + (c instanceof ParameterizedType && + DBusSerializable.class.isAssignableFrom((Class) ((ParameterizedType) c).getRawType()))) { + // it's a custom serializable type + Type[] newtypes = null; + if (c instanceof Class) { + for (Method m : ((Class) c).getDeclaredMethods()) + if (m.getName().equals("deserialize")) + newtypes = m.getGenericParameterTypes(); + } else + for (Method m : ((Class) ((ParameterizedType) c).getRawType()).getDeclaredMethods()) + if (m.getName().equals("deserialize")) + newtypes = m.getGenericParameterTypes(); + + if (null == newtypes) throw new DBusException(getString("mustImplementDeserializeMethod")); + + String[] sigs = new String[newtypes.length]; + for (int j = 0; j < sigs.length; j++) { + String[] ss = recursiveGetDBusType(newtypes[j], false, level + 1); + if (1 != ss.length) throw new DBusException(getString("mustSerializeNativeDBusTypes")); + sigs[j] = ss[0]; + } + return sigs; + } else if (c instanceof ParameterizedType) { + ParameterizedType p = (ParameterizedType) c; + if (p.getRawType().equals(Map.class)) { + out[level].append("a{"); + Type[] t = p.getActualTypeArguments(); + try { + String[] s = recursiveGetDBusType(t[0], true, level + 1); + if (s.length != 1) throw new DBusException(getString("multiValuedArrayNotPermitted")); + out[level].append(s[0]); + s = recursiveGetDBusType(t[1], false, level + 1); + if (s.length != 1) throw new DBusException(getString("multiValuedArrayNotPermitted")); + out[level].append(s[0]); + } catch (ArrayIndexOutOfBoundsException AIOOBe) { + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, AIOOBe); + throw new DBusException(getString("mapParameters")); + } + out[level].append('}'); + } else if (List.class.isAssignableFrom((Class) p.getRawType())) { + for (Type t : p.getActualTypeArguments()) { + if (Type.class.equals(t)) + out[level].append((char) Message.ArgumentType.SIGNATURE); + else { + String[] s = recursiveGetDBusType(t, false, level + 1); + if (s.length != 1) throw new DBusException(getString("multiValuedArrayNotPermitted")); + out[level].append((char) Message.ArgumentType.ARRAY); + out[level].append(s[0]); + } + } + } else if (p.getRawType().equals(Variant.class)) { + out[level].append((char) Message.ArgumentType.VARIANT); + } else if (DBusInterface.class.isAssignableFrom((Class) p.getRawType())) { + out[level].append((char) Message.ArgumentType.OBJECT_PATH); + } else if (Tuple.class.isAssignableFrom((Class) p.getRawType())) { + Type[] ts = p.getActualTypeArguments(); + Vector vs = new Vector(); + for (Type t : ts) + for (String s : recursiveGetDBusType(t, false, level + 1)) + vs.add(s); + return vs.toArray(new String[0]); + } else + throw new DBusException(getString("nonExportableParameterizedType") + c); + } else if (c.equals(Byte.class)) out[level].append((char) Message.ArgumentType.BYTE); + else if (c.equals(Byte.TYPE)) out[level].append((char) Message.ArgumentType.BYTE); + else if (c.equals(Boolean.class)) out[level].append((char) Message.ArgumentType.BOOLEAN); + else if (c.equals(Boolean.TYPE)) out[level].append((char) Message.ArgumentType.BOOLEAN); + else if (c.equals(Short.class)) out[level].append((char) Message.ArgumentType.INT16); + else if (c.equals(Short.TYPE)) out[level].append((char) Message.ArgumentType.INT16); + else if (c.equals(UInt16.class)) out[level].append((char) Message.ArgumentType.UINT16); + else if (c.equals(Integer.class)) out[level].append((char) Message.ArgumentType.INT32); + else if (c.equals(Integer.TYPE)) out[level].append((char) Message.ArgumentType.INT32); + else if (c.equals(UInt32.class)) out[level].append((char) Message.ArgumentType.UINT32); + else if (c.equals(Long.class)) out[level].append((char) Message.ArgumentType.INT64); + else if (c.equals(Long.TYPE)) out[level].append((char) Message.ArgumentType.INT64); + else if (c.equals(UInt64.class)) out[level].append((char) Message.ArgumentType.UINT64); + else if (c.equals(Double.class)) out[level].append((char) Message.ArgumentType.DOUBLE); + else if (c.equals(Double.TYPE)) out[level].append((char) Message.ArgumentType.DOUBLE); + else if (c.equals(Float.class) && AbstractConnection.FLOAT_SUPPORT) + out[level].append((char) Message.ArgumentType.FLOAT); + else if (c.equals(Float.class)) out[level].append((char) Message.ArgumentType.DOUBLE); + else if (c.equals(Float.TYPE) && AbstractConnection.FLOAT_SUPPORT) + out[level].append((char) Message.ArgumentType.FLOAT); + else if (c.equals(Float.TYPE)) out[level].append((char) Message.ArgumentType.DOUBLE); + else if (c.equals(String.class)) out[level].append((char) Message.ArgumentType.STRING); + else if (c.equals(Variant.class)) out[level].append((char) Message.ArgumentType.VARIANT); + else if (c instanceof Class && + DBusInterface.class.isAssignableFrom((Class) c)) + out[level].append((char) Message.ArgumentType.OBJECT_PATH); + else if (c instanceof Class && + Path.class.equals((Class) c)) + out[level].append((char) Message.ArgumentType.OBJECT_PATH); + else if (c instanceof Class && + ObjectPath.class.equals((Class) c)) + out[level].append((char) Message.ArgumentType.OBJECT_PATH); + else if (c instanceof Class && + ((Class) c).isArray()) { + if (Type.class.equals(((Class) c).getComponentType())) + out[level].append((char) Message.ArgumentType.SIGNATURE); + else { + out[level].append((char) Message.ArgumentType.ARRAY); + String[] s = recursiveGetDBusType(((Class) c).getComponentType(), false, level + 1); + if (s.length != 1) throw new DBusException(getString("multiValuedArrayNotPermitted")); + out[level].append(s[0]); + } + } else if (c instanceof Class && + Struct.class.isAssignableFrom((Class) c)) { + out[level].append((char) Message.ArgumentType.STRUCT1); + Type[] ts = Container.getTypeCache(c); + if (null == ts) { + Field[] fs = ((Class) c).getDeclaredFields(); + ts = new Type[fs.length]; + for (Field f : fs) { + Position p = f.getAnnotation(Position.class); + if (null == p) continue; + ts[p.value()] = f.getGenericType(); + } + Container.putTypeCache(c, ts); + } + + for (Type t : ts) + if (t != null) + for (String s : recursiveGetDBusType(t, false, level + 1)) + out[level].append(s); + out[level].append(')'); + } else { + throw new DBusException(getString("nonExportableType") + c); + } + + if (Debug.debug) Debug.print(Debug.VERBOSE, "Converted Java type: " + c + " to D-Bus Type: " + out[level]); + + return new String[]{out[level].toString()}; + } + + /** + * Converts a dbus type string into Java Type objects, + * + * @param dbus The DBus type or types. + * @param rv Vector to return the types in. + * @param limit Maximum number of types to parse (-1 == nolimit). + * @return number of characters parsed from the type string. + */ + public static int getJavaType(String dbus, List rv, int limit) throws DBusException { + if (null == dbus || "".equals(dbus) || 0 == limit) return 0; + + try { + int i = 0; + for (; i < dbus.length() && (-1 == limit || limit > rv.size()); i++) + switch (dbus.charAt(i)) { + case Message.ArgumentType.STRUCT1: + int j = i + 1; + for (int c = 1; c > 0; j++) { + if (')' == dbus.charAt(j)) c--; + else if (Message.ArgumentType.STRUCT1 == dbus.charAt(j)) c++; + } + + Vector contained = new Vector(); + int c = getJavaType(dbus.substring(i + 1, j - 1), contained, -1); + rv.add(new DBusStructType(contained.toArray(new Type[0]))); + i = j; + break; + case Message.ArgumentType.ARRAY: + if (Message.ArgumentType.DICT_ENTRY1 == dbus.charAt(i + 1)) { + contained = new Vector(); + c = getJavaType(dbus.substring(i + 2), contained, 2); + rv.add(new DBusMapType(contained.get(0), contained.get(1))); + i += (c + 2); + } else { + contained = new Vector(); + c = getJavaType(dbus.substring(i + 1), contained, 1); + rv.add(new DBusListType(contained.get(0))); + i += c; + } + break; + case Message.ArgumentType.VARIANT: + rv.add(Variant.class); + break; + case Message.ArgumentType.BOOLEAN: + rv.add(Boolean.class); + break; + case Message.ArgumentType.INT16: + rv.add(Short.class); + break; + case Message.ArgumentType.BYTE: + rv.add(Byte.class); + break; + case Message.ArgumentType.OBJECT_PATH: + rv.add(DBusInterface.class); + break; + case Message.ArgumentType.UINT16: + rv.add(UInt16.class); + break; + case Message.ArgumentType.INT32: + rv.add(Integer.class); + break; + case Message.ArgumentType.UINT32: + rv.add(UInt32.class); + break; + case Message.ArgumentType.INT64: + rv.add(Long.class); + break; + case Message.ArgumentType.UINT64: + rv.add(UInt64.class); + break; + case Message.ArgumentType.DOUBLE: + rv.add(Double.class); + break; + case Message.ArgumentType.FLOAT: + rv.add(Float.class); + break; + case Message.ArgumentType.STRING: + rv.add(String.class); + break; + case Message.ArgumentType.SIGNATURE: + rv.add(Type[].class); + break; + case Message.ArgumentType.DICT_ENTRY1: + rv.add(Map.Entry.class); + contained = new Vector(); + c = getJavaType(dbus.substring(i + 1), contained, 2); + i += c + 1; + break; + default: + throw new DBusException(MessageFormat.format(getString("parseDBusSignatureFailure"), new Object[]{dbus, dbus.charAt(i)})); + } + return i; + } catch (IndexOutOfBoundsException IOOBe) { + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, IOOBe); + throw new DBusException(getString("parseDBusTypeSignatureFailure") + dbus); + } + } + + /** + * Recursively converts types for serialization onto DBus. + * + * @param parameters The parameters to convert. + * @param types The (possibly generic) types of the parameters. + * @return The converted parameters. + * @throws DBusException Thrown if there is an error in converting the objects. + */ + @SuppressWarnings("unchecked") + public static Object[] convertParameters(Object[] parameters, Type[] types, AbstractConnection conn) throws DBusException { + if (null == parameters) return null; + for (int i = 0; i < parameters.length; i++) { + if (Debug.debug) + Debug.print(Debug.VERBOSE, "Converting " + i + " from " + parameters[i] + " to " + types[i]); + if (null == parameters[i]) continue; + + if (parameters[i] instanceof DBusSerializable) { + for (Method m : parameters[i].getClass().getDeclaredMethods()) + if (m.getName().equals("deserialize")) { + Type[] newtypes = m.getParameterTypes(); + Type[] expand = new Type[types.length + newtypes.length - 1]; + System.arraycopy(types, 0, expand, 0, i); + System.arraycopy(newtypes, 0, expand, i, newtypes.length); + System.arraycopy(types, i + 1, expand, i + newtypes.length, types.length - i - 1); + types = expand; + Object[] newparams = ((DBusSerializable) parameters[i]).serialize(); + Object[] exparams = new Object[parameters.length + newparams.length - 1]; + System.arraycopy(parameters, 0, exparams, 0, i); + System.arraycopy(newparams, 0, exparams, i, newparams.length); + System.arraycopy(parameters, i + 1, exparams, i + newparams.length, parameters.length - i - 1); + parameters = exparams; + } + i--; + } else if (parameters[i] instanceof Tuple) { + Type[] newtypes = ((ParameterizedType) types[i]).getActualTypeArguments(); + Type[] expand = new Type[types.length + newtypes.length - 1]; + System.arraycopy(types, 0, expand, 0, i); + System.arraycopy(newtypes, 0, expand, i, newtypes.length); + System.arraycopy(types, i + 1, expand, i + newtypes.length, types.length - i - 1); + types = expand; + Object[] newparams = ((Tuple) parameters[i]).getParameters(); + Object[] exparams = new Object[parameters.length + newparams.length - 1]; + System.arraycopy(parameters, 0, exparams, 0, i); + System.arraycopy(newparams, 0, exparams, i, newparams.length); + System.arraycopy(parameters, i + 1, exparams, i + newparams.length, parameters.length - i - 1); + parameters = exparams; + if (Debug.debug) + Debug.print(Debug.VERBOSE, "New params: " + Arrays.deepToString(parameters) + " new types: " + Arrays.deepToString(types)); + i--; + } else if (types[i] instanceof TypeVariable && + !(parameters[i] instanceof Variant)) + // its an unwrapped variant, wrap it + parameters[i] = new Variant(parameters[i]); + else if (parameters[i] instanceof DBusInterface) + parameters[i] = conn.getExportedObject((DBusInterface) parameters[i]); + } + return parameters; + } + + @SuppressWarnings("unchecked") + static Object deSerializeParameter(Object parameter, Type type, AbstractConnection conn) throws Exception { + if (Debug.debug) + Debug.print(Debug.VERBOSE, "Deserializing from " + parameter.getClass() + " to " + type.getClass()); + if (null == parameter) + return null; + + // its a wrapped variant, unwrap it + if (type instanceof TypeVariable + && parameter instanceof Variant) { + parameter = ((Variant) parameter).getValue(); + } + + // Turn a signature into a Type[] + if (type instanceof Class + && ((Class) type).isArray() + && ((Class) type).getComponentType().equals(Type.class) + && parameter instanceof String) { + Vector rv = new Vector(); + getJavaType((String) parameter, rv, -1); + parameter = rv.toArray(new Type[0]); + } + + // its an object path, get/create the proxy + if (parameter instanceof ObjectPath) { + if (type instanceof Class && DBusInterface.class.isAssignableFrom((Class) type)) + parameter = conn.getExportedObject( + ((ObjectPath) parameter).source, + ((ObjectPath) parameter).path); + else + parameter = new Path(((ObjectPath) parameter).path); + } + + // it should be a struct. create it + if (parameter instanceof Object[] && + type instanceof Class && + Struct.class.isAssignableFrom((Class) type)) { + if (Debug.debug) Debug.print(Debug.VERBOSE, "Creating Struct " + type + " from " + parameter); + Type[] ts = Container.getTypeCache(type); + if (null == ts) { + Field[] fs = ((Class) type).getDeclaredFields(); + ts = new Type[fs.length]; + for (Field f : fs) { + Position p = f.getAnnotation(Position.class); + if (null == p) continue; + ts[p.value()] = f.getGenericType(); + } + Container.putTypeCache(type, ts); + } + + // recurse over struct contents + parameter = deSerializeParameters((Object[]) parameter, ts, conn); + for (Constructor con : ((Class) type).getDeclaredConstructors()) { + try { + parameter = con.newInstance((Object[]) parameter); + break; + } catch (IllegalArgumentException IAe) { + } + } + } + + // recurse over arrays + if (parameter instanceof Object[]) { + Type[] ts = new Type[((Object[]) parameter).length]; + Arrays.fill(ts, parameter.getClass().getComponentType()); + parameter = deSerializeParameters((Object[]) parameter, + ts, conn); + } + if (parameter instanceof List) { + Type type2; + if (type instanceof ParameterizedType) + type2 = ((ParameterizedType) type).getActualTypeArguments()[0]; + else if (type instanceof GenericArrayType) + type2 = ((GenericArrayType) type).getGenericComponentType(); + else if (type instanceof Class && ((Class) type).isArray()) + type2 = ((Class) type).getComponentType(); + else + type2 = null; + if (null != type2) + parameter = deSerializeParameters((List) parameter, type2, conn); + } + + // correct floats if appropriate + if (type.equals(Float.class) || type.equals(Float.TYPE)) + if (!(parameter instanceof Float)) + parameter = ((Number) parameter).floatValue(); + + // make sure arrays are in the correct format + if (parameter instanceof Object[] || + parameter instanceof List || + parameter.getClass().isArray()) { + if (type instanceof ParameterizedType) + parameter = ArrayFrob.convert(parameter, + (Class) ((ParameterizedType) type).getRawType()); + else if (type instanceof GenericArrayType) { + Type ct = ((GenericArrayType) type).getGenericComponentType(); + Class cc = null; + if (ct instanceof Class) + cc = (Class) ct; + if (ct instanceof ParameterizedType) + cc = (Class) ((ParameterizedType) ct).getRawType(); + Object o = Array.newInstance(cc, 0); + parameter = ArrayFrob.convert(parameter, + o.getClass()); + } else if (type instanceof Class && + ((Class) type).isArray()) { + Class cc = ((Class) type).getComponentType(); + if ((cc.equals(Float.class) || cc.equals(Float.TYPE)) + && (parameter instanceof double[])) { + double[] tmp1 = (double[]) parameter; + float[] tmp2 = new float[tmp1.length]; + for (int i = 0; i < tmp1.length; i++) + tmp2[i] = (float) tmp1[i]; + parameter = tmp2; + } + Object o = Array.newInstance(cc, 0); + parameter = ArrayFrob.convert(parameter, + o.getClass()); + } + } + if (parameter instanceof DBusMap) { + if (Debug.debug) Debug.print(Debug.VERBOSE, "Deserializing a Map"); + DBusMap dmap = (DBusMap) parameter; + Type[] maptypes = ((ParameterizedType) type).getActualTypeArguments(); + for (int i = 0; i < dmap.entries.length; i++) { + dmap.entries[i][0] = deSerializeParameter(dmap.entries[i][0], maptypes[0], conn); + dmap.entries[i][1] = deSerializeParameter(dmap.entries[i][1], maptypes[1], conn); + } + } + return parameter; + } + + static List deSerializeParameters(List parameters, Type type, AbstractConnection conn) throws Exception { + if (Debug.debug) Debug.print(Debug.VERBOSE, "Deserializing from " + parameters + " to " + type); + if (null == parameters) return null; + for (int i = 0; i < parameters.size(); i++) { + if (null == parameters.get(i)) continue; + + /* DO NOT DO THIS! IT'S REALLY NOT SUPPORTED! + * if (type instanceof Class && + DBusSerializable.class.isAssignableFrom((Class) types[i])) { + for (Method m: ((Class) types[i]).getDeclaredMethods()) + if (m.getName().equals("deserialize")) { + Type[] newtypes = m.getGenericParameterTypes(); + try { + Object[] sub = new Object[newtypes.length]; + System.arraycopy(parameters, i, sub, 0, newtypes.length); + sub = deSerializeParameters(sub, newtypes, conn); + DBusSerializable sz = (DBusSerializable) ((Class) types[i]).newInstance(); + m.invoke(sz, sub); + Object[] compress = new Object[parameters.length - newtypes.length + 1]; + System.arraycopy(parameters, 0, compress, 0, i); + compress[i] = sz; + System.arraycopy(parameters, i + newtypes.length, compress, i+1, parameters.length - i - newtypes.length); + parameters = compress; + } catch (ArrayIndexOutOfBoundsException AIOOBe) { + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, AIOOBe); + throw new DBusException("Not enough elements to create custom object from serialized data ("+(parameters.size()-i)+" < "+(newtypes.length)+")"); + } + } + } else*/ + parameters.set(i, deSerializeParameter(parameters.get(i), type, conn)); + } + return parameters; + } + + @SuppressWarnings("unchecked") + static Object[] deSerializeParameters(Object[] parameters, Type[] types, AbstractConnection conn) throws Exception { + if (Debug.debug) + Debug.print(Debug.VERBOSE, "Deserializing from " + Arrays.deepToString(parameters) + " to " + Arrays.deepToString(types)); + if (null == parameters) return null; + + if (types.length == 1 && types[0] instanceof ParameterizedType + && Tuple.class.isAssignableFrom((Class) ((ParameterizedType) types[0]).getRawType())) { + types = ((ParameterizedType) types[0]).getActualTypeArguments(); + } + + for (int i = 0; i < parameters.length; i++) { + // CHECK IF ARRAYS HAVE THE SAME LENGTH <-- has to happen after expanding parameters + if (i >= types.length) { + if (Debug.debug) { + for (int j = 0; j < parameters.length; j++) { + Debug.print(Debug.ERR, String.format("Error, Parameters difference (%1d, '%2s')", j, parameters[j].toString())); + } + } + throw new DBusException(getString("errorDeserializingMessage")); + } + if (null == parameters[i]) continue; + + if ((types[i] instanceof Class && + DBusSerializable.class.isAssignableFrom((Class) types[i])) || + (types[i] instanceof ParameterizedType && + DBusSerializable.class.isAssignableFrom((Class) ((ParameterizedType) types[i]).getRawType()))) { + Class dsc; + if (types[i] instanceof Class) + dsc = (Class) types[i]; + else + dsc = (Class) ((ParameterizedType) types[i]).getRawType(); + for (Method m : dsc.getDeclaredMethods()) + if (m.getName().equals("deserialize")) { + Type[] newtypes = m.getGenericParameterTypes(); + try { + Object[] sub = new Object[newtypes.length]; + System.arraycopy(parameters, i, sub, 0, newtypes.length); + sub = deSerializeParameters(sub, newtypes, conn); + DBusSerializable sz = dsc.newInstance(); + m.invoke(sz, sub); + Object[] compress = new Object[parameters.length - newtypes.length + 1]; + System.arraycopy(parameters, 0, compress, 0, i); + compress[i] = sz; + System.arraycopy(parameters, i + newtypes.length, compress, i + 1, parameters.length - i - newtypes.length); + parameters = compress; + } catch (ArrayIndexOutOfBoundsException AIOOBe) { + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, AIOOBe); + throw new DBusException(MessageFormat.format(getString("notEnoughElementsToCreateCustomObject"), + new Object[]{parameters.length - i, newtypes.length})); + } + } + } else + parameters[i] = deSerializeParameter(parameters[i], types[i], conn); + } + return parameters; + } +} + + diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/Message.java b/federation/sssd/src/main/java/org/freedesktop/dbus/Message.java new file mode 100644 index 0000000000..01f00e8823 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/Message.java @@ -0,0 +1,1216 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import cx.ath.matthew.debug.Debug; +import cx.ath.matthew.utils.Hexdump; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.exceptions.MarshallingException; +import org.freedesktop.dbus.exceptions.UnknownTypeCodeException; + +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Array; +import java.lang.reflect.Type; +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Vector; + +import static org.freedesktop.dbus.Gettext.getString; + +/** + * Superclass of all messages which are sent over the Bus. + * This class deals with all the marshalling to/from the wire format. + */ +public class Message { + /** + * Defines constants representing the endianness of the message. + */ + public static interface Endian { + public static final byte BIG = 'B'; + public static final byte LITTLE = 'l'; + } + + /** + * Defines constants representing the flags which can be set on a message. + */ + public static interface Flags { + public static final byte NO_REPLY_EXPECTED = 0x01; + public static final byte NO_AUTO_START = 0x02; + public static final byte ASYNC = 0x40; + } + + /** + * Defines constants for each message type. + */ + public static interface MessageType { + public static final byte METHOD_CALL = 1; + public static final byte METHOD_RETURN = 2; + public static final byte ERROR = 3; + public static final byte SIGNAL = 4; + } + + /** + * The current protocol major version. + */ + public static final byte PROTOCOL = 1; + + /** + * Defines constants for each valid header field type. + */ + public static interface HeaderField { + public static final byte PATH = 1; + public static final byte INTERFACE = 2; + public static final byte MEMBER = 3; + public static final byte ERROR_NAME = 4; + public static final byte REPLY_SERIAL = 5; + public static final byte DESTINATION = 6; + public static final byte SENDER = 7; + public static final byte SIGNATURE = 8; + } + + /** + * Defines constants for each argument type. + * There are two constants for each argument type, + * as a byte or as a String (the _STRING version) + */ + public static interface ArgumentType { + public static final String BYTE_STRING = "y"; + public static final String BOOLEAN_STRING = "b"; + public static final String INT16_STRING = "n"; + public static final String UINT16_STRING = "q"; + public static final String INT32_STRING = "i"; + public static final String UINT32_STRING = "u"; + public static final String INT64_STRING = "x"; + public static final String UINT64_STRING = "t"; + public static final String DOUBLE_STRING = "d"; + public static final String FLOAT_STRING = "f"; + public static final String STRING_STRING = "s"; + public static final String OBJECT_PATH_STRING = "o"; + public static final String SIGNATURE_STRING = "g"; + public static final String ARRAY_STRING = "a"; + public static final String VARIANT_STRING = "v"; + public static final String STRUCT_STRING = "r"; + public static final String STRUCT1_STRING = "("; + public static final String STRUCT2_STRING = ")"; + public static final String DICT_ENTRY_STRING = "e"; + public static final String DICT_ENTRY1_STRING = "{"; + public static final String DICT_ENTRY2_STRING = "}"; + + public static final byte BYTE = 'y'; + public static final byte BOOLEAN = 'b'; + public static final byte INT16 = 'n'; + public static final byte UINT16 = 'q'; + public static final byte INT32 = 'i'; + public static final byte UINT32 = 'u'; + public static final byte INT64 = 'x'; + public static final byte UINT64 = 't'; + public static final byte DOUBLE = 'd'; + public static final byte FLOAT = 'f'; + public static final byte STRING = 's'; + public static final byte OBJECT_PATH = 'o'; + public static final byte SIGNATURE = 'g'; + public static final byte ARRAY = 'a'; + public static final byte VARIANT = 'v'; + public static final byte STRUCT = 'r'; + public static final byte STRUCT1 = '('; + public static final byte STRUCT2 = ')'; + public static final byte DICT_ENTRY = 'e'; + public static final byte DICT_ENTRY1 = '{'; + public static final byte DICT_ENTRY2 = '}'; + } + + /** + * Keep a static reference to each size of padding array to prevent allocation. + */ + private static byte[][] padding; + + static { + padding = new byte[][]{ + null, + new byte[1], + new byte[2], + new byte[3], + new byte[4], + new byte[5], + new byte[6], + new byte[7]}; + } + + /** + * Steps to increment the buffer array. + */ + private static final int BUFFERINCREMENT = 20; + + private boolean big; + protected byte[][] wiredata; + protected long bytecounter; + protected Map headers; + protected static long globalserial = 0; + protected long serial; + protected byte type; + protected byte flags; + protected byte protover; + private Object[] args; + private byte[] body; + private long bodylen = 0; + private int preallocated = 0; + private int paofs = 0; + private byte[] pabuf; + private int bufferuse = 0; + + /** + * Returns the name of the given header field. + */ + public static String getHeaderFieldName(byte field) { + switch (field) { + case HeaderField.PATH: + return "Path"; + case HeaderField.INTERFACE: + return "Interface"; + case HeaderField.MEMBER: + return "Member"; + case HeaderField.ERROR_NAME: + return "Error Name"; + case HeaderField.REPLY_SERIAL: + return "Reply Serial"; + case HeaderField.DESTINATION: + return "Destination"; + case HeaderField.SENDER: + return "Sender"; + case HeaderField.SIGNATURE: + return "Signature"; + default: + return "Invalid"; + } + } + + /** + * Create a message; only to be called by sub-classes. + * + * @param endian The endianness to create the message. + * @param type The message type. + * @param flags Any message flags. + */ + protected Message(byte endian, byte type, byte flags) throws DBusException { + wiredata = new byte[BUFFERINCREMENT][]; + headers = new HashMap(); + big = (Endian.BIG == endian); + bytecounter = 0; + synchronized (Message.class) { + serial = ++globalserial; + } + if (Debug.debug) Debug.print(Debug.DEBUG, "Creating message with serial " + serial); + this.type = type; + this.flags = flags; + preallocate(4); + append("yyyy", endian, type, flags, Message.PROTOCOL); + } + + /** + * Create a blank message. Only to be used when calling populate. + */ + protected Message() { + wiredata = new byte[BUFFERINCREMENT][]; + headers = new HashMap(); + bytecounter = 0; + } + + /** + * Create a message from wire-format data. + * + * @param msg D-Bus serialized data of type yyyuu + * @param headers D-Bus serialized data of type a(yv) + * @param body D-Bus serialized data of the signature defined in headers. + */ + @SuppressWarnings("unchecked") + void populate(byte[] msg, byte[] headers, byte[] body) throws DBusException { + big = (msg[0] == Endian.BIG); + type = msg[1]; + flags = msg[2]; + protover = msg[3]; + wiredata[0] = msg; + wiredata[1] = headers; + wiredata[2] = body; + this.body = body; + bufferuse = 3; + bodylen = ((Number) extract(Message.ArgumentType.UINT32_STRING, msg, 4)[0]).longValue(); + serial = ((Number) extract(Message.ArgumentType.UINT32_STRING, msg, 8)[0]).longValue(); + bytecounter = msg.length + headers.length + body.length; + if (Debug.debug) Debug.print(Debug.VERBOSE, headers); + Object[] hs = extract("a(yv)", headers, 0); + if (Debug.debug) Debug.print(Debug.VERBOSE, Arrays.deepToString(hs)); + for (Object o : (Vector) hs[0]) { + this.headers.put((Byte) ((Object[]) o)[0], ((Variant) ((Object[]) o)[1]).getValue()); + } + } + + /** + * Create a buffer of num bytes. + * Data is copied to this rather than added to the buffer list. + */ + private void preallocate(int num) { + preallocated = 0; + pabuf = new byte[num]; + appendBytes(pabuf); + preallocated = num; + paofs = 0; + } + + /** + * Ensures there are enough free buffers. + * + * @param num number of free buffers to create. + */ + private void ensureBuffers(int num) { + int increase = num - wiredata.length + bufferuse; + if (increase > 0) { + if (increase < BUFFERINCREMENT) increase = BUFFERINCREMENT; + if (Debug.debug) Debug.print(Debug.VERBOSE, "Resizing " + bufferuse); + byte[][] temp = new byte[wiredata.length + increase][]; + System.arraycopy(wiredata, 0, temp, 0, wiredata.length); + wiredata = temp; + } + } + + /** + * Appends a buffer to the buffer list. + */ + protected void appendBytes(byte[] buf) { + if (null == buf) return; + if (preallocated > 0) { + if (paofs + buf.length > pabuf.length) + throw new ArrayIndexOutOfBoundsException(MessageFormat.format(getString("arrayOutOfBounds"), new Object[]{paofs, pabuf.length, buf.length})); + System.arraycopy(buf, 0, pabuf, paofs, buf.length); + paofs += buf.length; + preallocated -= buf.length; + } else { + if (bufferuse == wiredata.length) { + if (Debug.debug) Debug.print(Debug.VERBOSE, "Resizing " + bufferuse); + byte[][] temp = new byte[wiredata.length + BUFFERINCREMENT][]; + System.arraycopy(wiredata, 0, temp, 0, wiredata.length); + wiredata = temp; + } + wiredata[bufferuse++] = buf; + bytecounter += buf.length; + } + } + + /** + * Appends a byte to the buffer list. + */ + protected void appendByte(byte b) { + if (preallocated > 0) { + pabuf[paofs++] = b; + preallocated--; + } else { + if (bufferuse == wiredata.length) { + if (Debug.debug) Debug.print(Debug.VERBOSE, "Resizing " + bufferuse); + byte[][] temp = new byte[wiredata.length + BUFFERINCREMENT][]; + System.arraycopy(wiredata, 0, temp, 0, wiredata.length); + wiredata = temp; + } + wiredata[bufferuse++] = new byte[]{b}; + bytecounter++; + } + } + + /** + * Demarshalls an integer of a given width from a buffer. + * Endianness is determined from the format of the message. + * + * @param buf The buffer to demarshall from. + * @param ofs The offset to demarshall from. + * @param width The byte-width of the int. + */ + public long demarshallint(byte[] buf, int ofs, int width) { + return big ? demarshallintBig(buf, ofs, width) : demarshallintLittle(buf, ofs, width); + } + + /** + * Demarshalls an integer of a given width from a buffer. + * + * @param buf The buffer to demarshall from. + * @param ofs The offset to demarshall from. + * @param endian The endianness to use in demarshalling. + * @param width The byte-width of the int. + */ + public static long demarshallint(byte[] buf, int ofs, byte endian, int width) { + return endian == Endian.BIG ? demarshallintBig(buf, ofs, width) : demarshallintLittle(buf, ofs, width); + } + + /** + * Demarshalls an integer of a given width from a buffer using big-endian format. + * + * @param buf The buffer to demarshall from. + * @param ofs The offset to demarshall from. + * @param width The byte-width of the int. + */ + public static long demarshallintBig(byte[] buf, int ofs, int width) { + long l = 0; + for (int i = 0; i < width; i++) { + l <<= 8; + l |= (buf[ofs + i] & 0xFF); + } + return l; + } + + /** + * Demarshalls an integer of a given width from a buffer using little-endian format. + * + * @param buf The buffer to demarshall from. + * @param ofs The offset to demarshall from. + * @param width The byte-width of the int. + */ + public static long demarshallintLittle(byte[] buf, int ofs, int width) { + long l = 0; + for (int i = (width - 1); i >= 0; i--) { + l <<= 8; + l |= (buf[ofs + i] & 0xFF); + } + return l; + } + + /** + * Marshalls an integer of a given width and appends it to the message. + * Endianness is determined from the message. + * + * @param l The integer to marshall. + * @param width The byte-width of the int. + */ + public void appendint(long l, int width) { + byte[] buf = new byte[width]; + marshallint(l, buf, 0, width); + appendBytes(buf); + } + + /** + * Marshalls an integer of a given width into a buffer. + * Endianness is determined from the message. + * + * @param l The integer to marshall. + * @param buf The buffer to marshall to. + * @param ofs The offset to marshall to. + * @param width The byte-width of the int. + */ + public void marshallint(long l, byte[] buf, int ofs, int width) { + if (big) marshallintBig(l, buf, ofs, width); + else marshallintLittle(l, buf, ofs, width); + if (Debug.debug) Debug.print(Debug.VERBOSE, "Marshalled int " + l + " to " + Hexdump.toHex(buf, ofs, width)); + } + + /** + * Marshalls an integer of a given width into a buffer using big-endian format. + * + * @param l The integer to marshall. + * @param buf The buffer to marshall to. + * @param ofs The offset to marshall to. + * @param width The byte-width of the int. + */ + public static void marshallintBig(long l, byte[] buf, int ofs, int width) { + for (int i = (width - 1); i >= 0; i--) { + buf[i + ofs] = (byte) (l & 0xFF); + l >>= 8; + } + } + + /** + * Marshalls an integer of a given width into a buffer using little-endian format. + * + * @param l The integer to marshall. + * @param buf The buffer to demarshall to. + * @param ofs The offset to demarshall to. + * @param width The byte-width of the int. + */ + public static void marshallintLittle(long l, byte[] buf, int ofs, int width) { + for (int i = 0; i < width; i++) { + buf[i + ofs] = (byte) (l & 0xFF); + l >>= 8; + } + } + + public byte[][] getWireData() { + return wiredata; + } + + /** + * Formats the message in a human-readable format. + */ + public String toString() { + StringBuffer sb = new StringBuffer(); + sb.append(getClass().getSimpleName()); + sb.append('('); + sb.append(flags); + sb.append(','); + sb.append(serial); + sb.append(')'); + sb.append(' '); + sb.append('{'); + sb.append(' '); + if (headers.size() == 0) + sb.append('}'); + else { + for (Byte field : headers.keySet()) { + sb.append(getHeaderFieldName(field)); + sb.append('='); + sb.append('>'); + sb.append(headers.get(field).toString()); + sb.append(','); + sb.append(' '); + } + sb.setCharAt(sb.length() - 2, ' '); + sb.setCharAt(sb.length() - 1, '}'); + } + sb.append(' '); + sb.append('{'); + sb.append(' '); + Object[] args = null; + try { + args = getParameters(); + } catch (DBusException DBe) { + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); + } + if (null == args || 0 == args.length) + sb.append('}'); + else { + for (Object o : args) { + if (o instanceof Object[]) + sb.append(Arrays.deepToString((Object[]) o)); + else if (o instanceof byte[]) + sb.append(Arrays.toString((byte[]) o)); + else if (o instanceof int[]) + sb.append(Arrays.toString((int[]) o)); + else if (o instanceof short[]) + sb.append(Arrays.toString((short[]) o)); + else if (o instanceof long[]) + sb.append(Arrays.toString((long[]) o)); + else if (o instanceof boolean[]) + sb.append(Arrays.toString((boolean[]) o)); + else if (o instanceof double[]) + sb.append(Arrays.toString((double[]) o)); + else if (o instanceof float[]) + sb.append(Arrays.toString((float[]) o)); + else + sb.append(o.toString()); + sb.append(','); + sb.append(' '); + } + sb.setCharAt(sb.length() - 2, ' '); + sb.setCharAt(sb.length() - 1, '}'); + } + return sb.toString(); + } + + /** + * Returns the value of the header field of a given field. + * + * @param type The field to return. + * @return The value of the field or null if unset. + */ + public Object getHeader(byte type) { + return headers.get(type); + } + + /** + * Appends a value to the message. + * The type of the value is read from a D-Bus signature and used to marshall + * the value. + * + * @param sigb A buffer of the D-Bus signature. + * @param sigofs The offset into the signature corresponding to this value. + * @param data The value to marshall. + * @return The offset into the signature of the end of this value's type. + */ + @SuppressWarnings("unchecked") + private int appendone(byte[] sigb, int sigofs, Object data) throws DBusException { + try { + int i = sigofs; + if (Debug.debug) Debug.print(Debug.VERBOSE, (Object) bytecounter); + if (Debug.debug) Debug.print(Debug.VERBOSE, "Appending type: " + ((char) sigb[i]) + " value: " + data); + + // pad to the alignment of this type. + pad(sigb[i]); + switch (sigb[i]) { + case ArgumentType.BYTE: + appendByte(((Number) data).byteValue()); + break; + case ArgumentType.BOOLEAN: + appendint(((Boolean) data).booleanValue() ? 1 : 0, 4); + break; + case ArgumentType.DOUBLE: + long l = Double.doubleToLongBits(((Number) data).doubleValue()); + appendint(l, 8); + break; + case ArgumentType.FLOAT: + int rf = Float.floatToIntBits(((Number) data).floatValue()); + appendint(rf, 4); + break; + case ArgumentType.UINT32: + appendint(((Number) data).longValue(), 4); + break; + case ArgumentType.INT64: + appendint(((Number) data).longValue(), 8); + break; + case ArgumentType.UINT64: + if (big) { + appendint(((UInt64) data).top(), 4); + appendint(((UInt64) data).bottom(), 4); + } else { + appendint(((UInt64) data).bottom(), 4); + appendint(((UInt64) data).top(), 4); + } + break; + case ArgumentType.INT32: + appendint(((Number) data).intValue(), 4); + break; + case ArgumentType.UINT16: + appendint(((Number) data).intValue(), 2); + break; + case ArgumentType.INT16: + appendint(((Number) data).shortValue(), 2); + break; + case ArgumentType.STRING: + case ArgumentType.OBJECT_PATH: + // Strings are marshalled as a UInt32 with the length, + // followed by the String, followed by a null byte. + String payload = data.toString(); + byte[] payloadbytes = null; + try { + payloadbytes = payload.getBytes("UTF-8"); + } catch (UnsupportedEncodingException UEe) { + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(UEe); + throw new DBusException(getString("utf8NotSupported")); + } + if (Debug.debug) Debug.print(Debug.VERBOSE, "Appending String of length " + payloadbytes.length); + appendint(payloadbytes.length, 4); + appendBytes(payloadbytes); + appendBytes(padding[1]); + //pad(ArgumentType.STRING);? do we need this? + break; + case ArgumentType.SIGNATURE: + // Signatures are marshalled as a byte with the length, + // followed by the String, followed by a null byte. + // Signatures are generally short, so preallocate the array + // for the string, length and null byte. + if (data instanceof Type[]) + payload = Marshalling.getDBusType((Type[]) data); + else + payload = (String) data; + byte[] pbytes = payload.getBytes(); + preallocate(2 + pbytes.length); + appendByte((byte) pbytes.length); + appendBytes(pbytes); + appendByte((byte) 0); + break; + case ArgumentType.ARRAY: + // Arrays are given as a UInt32 for the length in bytes, + // padding to the element alignment, then elements in + // order. The length is the length from the end of the + // initial padding to the end of the last element. + if (Debug.debug) { + if (data instanceof Object[]) + Debug.print(Debug.VERBOSE, "Appending array: " + Arrays.deepToString((Object[]) data)); + } + + byte[] alen = new byte[4]; + appendBytes(alen); + pad(sigb[++i]); + long c = bytecounter; + + // optimise primatives + if (data.getClass().isArray() && + data.getClass().getComponentType().isPrimitive()) { + byte[] primbuf; + int algn = getAlignment(sigb[i]); + int len = Array.getLength(data); + switch (sigb[i]) { + case ArgumentType.BYTE: + primbuf = (byte[]) data; + break; + case ArgumentType.INT16: + case ArgumentType.INT32: + case ArgumentType.INT64: + primbuf = new byte[len * algn]; + for (int j = 0, k = 0; j < len; j++, k += algn) + marshallint(Array.getLong(data, j), primbuf, k, algn); + break; + case ArgumentType.BOOLEAN: + primbuf = new byte[len * algn]; + for (int j = 0, k = 0; j < len; j++, k += algn) + marshallint(Array.getBoolean(data, j) ? 1 : 0, primbuf, k, algn); + break; + case ArgumentType.DOUBLE: + primbuf = new byte[len * algn]; + if (data instanceof float[]) + for (int j = 0, k = 0; j < len; j++, k += algn) + marshallint(Double.doubleToRawLongBits(((float[]) data)[j]), + primbuf, k, algn); + else + for (int j = 0, k = 0; j < len; j++, k += algn) + marshallint(Double.doubleToRawLongBits(((double[]) data)[j]), + primbuf, k, algn); + break; + case ArgumentType.FLOAT: + primbuf = new byte[len * algn]; + for (int j = 0, k = 0; j < len; j++, k += algn) + marshallint( + Float.floatToRawIntBits(((float[]) data)[j]), + primbuf, k, algn); + break; + default: + throw new MarshallingException(getString("arraySentAsNonPrimitive")); + } + appendBytes(primbuf); + } else if (data instanceof List) { + Object[] contents = ((List) data).toArray(); + int diff = i; + ensureBuffers(contents.length * 4); + for (Object o : contents) + diff = appendone(sigb, i, o); + i = diff; + } else if (data instanceof Map) { + int diff = i; + ensureBuffers(((Map) data).size() * 6); + for (Map.Entry o : ((Map) data).entrySet()) + diff = appendone(sigb, i, o); + if (i == diff) { + // advance the type parser even on 0-size arrays. + Vector temp = new Vector(); + byte[] temp2 = new byte[sigb.length - diff]; + System.arraycopy(sigb, diff, temp2, 0, temp2.length); + String temp3 = new String(temp2); + int temp4 = Marshalling.getJavaType(temp3, temp, 1); + diff += temp4; + } + i = diff; + } else { + Object[] contents = (Object[]) data; + ensureBuffers(contents.length * 4); + int diff = i; + for (Object o : contents) + diff = appendone(sigb, i, o); + i = diff; + } + if (Debug.debug) + Debug.print(Debug.VERBOSE, "start: " + c + " end: " + bytecounter + " length: " + (bytecounter - c)); + marshallint(bytecounter - c, alen, 0, 4); + break; + case ArgumentType.STRUCT1: + // Structs are aligned to 8 bytes + // and simply contain each element marshalled in order + Object[] contents; + if (data instanceof Container) + contents = ((Container) data).getParameters(); + else + contents = (Object[]) data; + ensureBuffers(contents.length * 4); + int j = 0; + for (i++; sigb[i] != ArgumentType.STRUCT2; i++) + i = appendone(sigb, i, contents[j++]); + break; + case ArgumentType.DICT_ENTRY1: + // Dict entries are the same as structs. + if (data instanceof Map.Entry) { + i++; + i = appendone(sigb, i, ((Map.Entry) data).getKey()); + i++; + i = appendone(sigb, i, ((Map.Entry) data).getValue()); + i++; + } else { + contents = (Object[]) data; + j = 0; + for (i++; sigb[i] != ArgumentType.DICT_ENTRY2; i++) + i = appendone(sigb, i, contents[j++]); + } + break; + case ArgumentType.VARIANT: + // Variants are marshalled as a signature + // followed by the value. + if (data instanceof Variant) { + Variant var = (Variant) data; + appendone(new byte[]{ArgumentType.SIGNATURE}, 0, var.getSig()); + appendone((var.getSig()).getBytes(), 0, var.getValue()); + } else if (data instanceof Object[]) { + contents = (Object[]) data; + appendone(new byte[]{ArgumentType.SIGNATURE}, 0, contents[0]); + appendone(((String) contents[0]).getBytes(), 0, contents[1]); + } else { + String sig = Marshalling.getDBusType(data.getClass())[0]; + appendone(new byte[]{ArgumentType.SIGNATURE}, 0, sig); + appendone((sig).getBytes(), 0, data); + } + break; + } + return i; + } catch (ClassCastException CCe) { + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, CCe); + throw new MarshallingException(MessageFormat.format(getString("unconvertableType"), new Object[]{data.getClass().getName(), sigb[sigofs]})); + } + } + + /** + * Pad the message to the proper alignment for the given type. + */ + public void pad(byte type) { + if (Debug.debug) Debug.print(Debug.VERBOSE, "padding for " + (char) type); + int a = getAlignment(type); + if (Debug.debug) Debug.print(Debug.VERBOSE, preallocated + " " + paofs + " " + bytecounter + " " + a); + int b = (int) ((bytecounter - preallocated) % a); + if (0 == b) return; + a = (a - b); + if (preallocated > 0) { + paofs += a; + preallocated -= a; + } else + appendBytes(padding[a]); + if (Debug.debug) Debug.print(Debug.VERBOSE, preallocated + " " + paofs + " " + bytecounter + " " + a); + } + + /** + * Return the alignment for a given type. + */ + public static int getAlignment(byte type) { + switch (type) { + case 2: + case ArgumentType.INT16: + case ArgumentType.UINT16: + return 2; + case 4: + case ArgumentType.BOOLEAN: + case ArgumentType.FLOAT: + case ArgumentType.INT32: + case ArgumentType.UINT32: + case ArgumentType.STRING: + case ArgumentType.OBJECT_PATH: + case ArgumentType.ARRAY: + return 4; + case 8: + case ArgumentType.INT64: + case ArgumentType.UINT64: + case ArgumentType.DOUBLE: + case ArgumentType.STRUCT: + case ArgumentType.DICT_ENTRY: + case ArgumentType.STRUCT1: + case ArgumentType.DICT_ENTRY1: + case ArgumentType.STRUCT2: + case ArgumentType.DICT_ENTRY2: + return 8; + case 1: + case ArgumentType.BYTE: + case ArgumentType.SIGNATURE: + case ArgumentType.VARIANT: + default: + return 1; + } + } + + /** + * Append a series of values to the message. + * + * @param sig The signature(s) of the value(s). + * @param data The value(s). + */ + public void append(String sig, Object... data) throws DBusException { + if (Debug.debug) Debug.print(Debug.DEBUG, "Appending sig: " + sig + " data: " + Arrays.deepToString(data)); + byte[] sigb = sig.getBytes(); + int j = 0; + for (int i = 0; i < sigb.length; i++) { + if (Debug.debug) Debug.print(Debug.VERBOSE, "Appending item: " + i + " " + ((char) sigb[i]) + " " + j); + i = appendone(sigb, i, data[j++]); + } + } + + /** + * Align a counter to the given type. + * + * @param current The current counter. + * @param type The type to align to. + * @return The new, aligned, counter. + */ + public int align(int current, byte type) { + if (Debug.debug) Debug.print(Debug.VERBOSE, "aligning to " + (char) type); + int a = getAlignment(type); + if (0 == (current % a)) return current; + return current + (a - (current % a)); + } + + /** + * Demarshall one value from a buffer. + * + * @param sigb A buffer of the D-Bus signature. + * @param buf The buffer to demarshall from. + * @param ofs An array of two ints, the offset into the signature buffer + * and the offset into the data buffer. These values will be + * updated to the start of the next value ofter demarshalling. + * @param contained converts nested arrays to Lists + * @return The demarshalled value. + */ + private Object extractone(byte[] sigb, byte[] buf, int[] ofs, boolean contained) throws DBusException { + if (Debug.debug) + Debug.print(Debug.VERBOSE, "Extracting type: " + ((char) sigb[ofs[0]]) + " from offset " + ofs[1]); + Object rv = null; + ofs[1] = align(ofs[1], sigb[ofs[0]]); + switch (sigb[ofs[0]]) { + case ArgumentType.BYTE: + rv = buf[ofs[1]++]; + break; + case ArgumentType.UINT32: + rv = new UInt32(demarshallint(buf, ofs[1], 4)); + ofs[1] += 4; + break; + case ArgumentType.INT32: + rv = (int) demarshallint(buf, ofs[1], 4); + ofs[1] += 4; + break; + case ArgumentType.INT16: + rv = (short) demarshallint(buf, ofs[1], 2); + ofs[1] += 2; + break; + case ArgumentType.UINT16: + rv = new UInt16((int) demarshallint(buf, ofs[1], 2)); + ofs[1] += 2; + break; + case ArgumentType.INT64: + rv = demarshallint(buf, ofs[1], 8); + ofs[1] += 8; + break; + case ArgumentType.UINT64: + long top; + long bottom; + if (big) { + top = demarshallint(buf, ofs[1], 4); + ofs[1] += 4; + bottom = demarshallint(buf, ofs[1], 4); + } else { + bottom = demarshallint(buf, ofs[1], 4); + ofs[1] += 4; + top = demarshallint(buf, ofs[1], 4); + } + rv = new UInt64(top, bottom); + ofs[1] += 4; + break; + case ArgumentType.DOUBLE: + long l = demarshallint(buf, ofs[1], 8); + ofs[1] += 8; + rv = Double.longBitsToDouble(l); + break; + case ArgumentType.FLOAT: + int rf = (int) demarshallint(buf, ofs[1], 4); + ofs[1] += 4; + rv = Float.intBitsToFloat(rf); + break; + case ArgumentType.BOOLEAN: + rf = (int) demarshallint(buf, ofs[1], 4); + ofs[1] += 4; + rv = (1 == rf) ? Boolean.TRUE : Boolean.FALSE; + break; + case ArgumentType.ARRAY: + long size = demarshallint(buf, ofs[1], 4); + if (Debug.debug) Debug.print(Debug.VERBOSE, "Reading array of size: " + size); + ofs[1] += 4; + byte algn = (byte) getAlignment(sigb[++ofs[0]]); + ofs[1] = align(ofs[1], sigb[ofs[0]]); + int length = (int) (size / algn); + if (length > DBusConnection.MAX_ARRAY_LENGTH) + throw new MarshallingException(getString("arrayMustNotExceed") + DBusConnection.MAX_ARRAY_LENGTH); + // optimise primatives + switch (sigb[ofs[0]]) { + case ArgumentType.BYTE: + rv = new byte[length]; + System.arraycopy(buf, ofs[1], rv, 0, length); + ofs[1] += size; + break; + case ArgumentType.INT16: + rv = new short[length]; + for (int j = 0; j < length; j++, ofs[1] += algn) + ((short[]) rv)[j] = (short) demarshallint(buf, ofs[1], algn); + break; + case ArgumentType.INT32: + rv = new int[length]; + for (int j = 0; j < length; j++, ofs[1] += algn) + ((int[]) rv)[j] = (int) demarshallint(buf, ofs[1], algn); + break; + case ArgumentType.INT64: + rv = new long[length]; + for (int j = 0; j < length; j++, ofs[1] += algn) + ((long[]) rv)[j] = demarshallint(buf, ofs[1], algn); + break; + case ArgumentType.BOOLEAN: + rv = new boolean[length]; + for (int j = 0; j < length; j++, ofs[1] += algn) + ((boolean[]) rv)[j] = (1 == demarshallint(buf, ofs[1], algn)); + break; + case ArgumentType.FLOAT: + rv = new float[length]; + for (int j = 0; j < length; j++, ofs[1] += algn) + ((float[]) rv)[j] = + Float.intBitsToFloat((int) demarshallint(buf, ofs[1], algn)); + break; + case ArgumentType.DOUBLE: + rv = new double[length]; + for (int j = 0; j < length; j++, ofs[1] += algn) + ((double[]) rv)[j] = + Double.longBitsToDouble(demarshallint(buf, ofs[1], algn)); + break; + case ArgumentType.DICT_ENTRY1: + if (0 == size) { + // advance the type parser even on 0-size arrays. + Vector temp = new Vector(); + byte[] temp2 = new byte[sigb.length - ofs[0]]; + System.arraycopy(sigb, ofs[0], temp2, 0, temp2.length); + String temp3 = new String(temp2); + // ofs[0] gets incremented anyway. Leave one character on the stack + int temp4 = Marshalling.getJavaType(temp3, temp, 1) - 1; + ofs[0] += temp4; + if (Debug.debug) + Debug.print(Debug.VERBOSE, "Aligned type: " + temp3 + " " + temp4 + " " + ofs[0]); + } + int ofssave = ofs[0]; + long end = ofs[1] + size; + Vector entries = new Vector(); + while (ofs[1] < end) { + ofs[0] = ofssave; + entries.add((Object[]) extractone(sigb, buf, ofs, true)); + } + rv = new DBusMap(entries.toArray(new Object[0][])); + break; + default: + if (0 == size) { + // advance the type parser even on 0-size arrays. + Vector temp = new Vector(); + byte[] temp2 = new byte[sigb.length - ofs[0]]; + System.arraycopy(sigb, ofs[0], temp2, 0, temp2.length); + String temp3 = new String(temp2); + // ofs[0] gets incremented anyway. Leave one character on the stack + int temp4 = Marshalling.getJavaType(temp3, temp, 1) - 1; + ofs[0] += temp4; + if (Debug.debug) + Debug.print(Debug.VERBOSE, "Aligned type: " + temp3 + " " + temp4 + " " + ofs[0]); + } + ofssave = ofs[0]; + end = ofs[1] + size; + Vector contents = new Vector(); + while (ofs[1] < end) { + ofs[0] = ofssave; + contents.add(extractone(sigb, buf, ofs, true)); + } + rv = contents; + } + if (contained && !(rv instanceof List) && !(rv instanceof Map)) + rv = ArrayFrob.listify(rv); + break; + case ArgumentType.STRUCT1: + Vector contents = new Vector(); + while (sigb[++ofs[0]] != ArgumentType.STRUCT2) + contents.add(extractone(sigb, buf, ofs, true)); + rv = contents.toArray(); + break; + case ArgumentType.DICT_ENTRY1: + Object[] decontents = new Object[2]; + if (Debug.debug) + Debug.print(Debug.VERBOSE, "Extracting Dict Entry (" + Hexdump.toAscii(sigb, ofs[0], sigb.length - ofs[0]) + ") from: " + Hexdump.toHex(buf, ofs[1], buf.length - ofs[1])); + ofs[0]++; + decontents[0] = extractone(sigb, buf, ofs, true); + ofs[0]++; + decontents[1] = extractone(sigb, buf, ofs, true); + ofs[0]++; + rv = decontents; + break; + case ArgumentType.VARIANT: + int[] newofs = new int[]{0, ofs[1]}; + String sig = (String) extract(ArgumentType.SIGNATURE_STRING, buf, newofs)[0]; + newofs[0] = 0; + rv = new Variant(extract(sig, buf, newofs)[0], sig); + ofs[1] = newofs[1]; + break; + case ArgumentType.STRING: + length = (int) demarshallint(buf, ofs[1], 4); + ofs[1] += 4; + try { + rv = new String(buf, ofs[1], length, "UTF-8"); + } catch (UnsupportedEncodingException UEe) { + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(UEe); + throw new DBusException(getString("utf8NotSupported")); + } + ofs[1] += length + 1; + break; + case ArgumentType.OBJECT_PATH: + length = (int) demarshallint(buf, ofs[1], 4); + ofs[1] += 4; + rv = new ObjectPath(getSource(), new String(buf, ofs[1], length)); + ofs[1] += length + 1; + break; + case ArgumentType.SIGNATURE: + length = (buf[ofs[1]++] & 0xFF); + rv = new String(buf, ofs[1], length); + ofs[1] += length + 1; + break; + default: + throw new UnknownTypeCodeException(sigb[ofs[0]]); + } + if (Debug.debug) if (rv instanceof Object[]) + Debug.print(Debug.VERBOSE, "Extracted: " + Arrays.deepToString((Object[]) rv) + " (now at " + ofs[1] + ")"); + else + Debug.print(Debug.VERBOSE, "Extracted: " + rv + " (now at " + ofs[1] + ")"); + return rv; + } + + /** + * Demarshall values from a buffer. + * + * @param sig The D-Bus signature(s) of the value(s). + * @param buf The buffer to demarshall from. + * @param ofs The offset into the data buffer to start. + * @return The demarshalled value(s). + */ + public Object[] extract(String sig, byte[] buf, int ofs) throws DBusException { + return extract(sig, buf, new int[]{0, ofs}); + } + + /** + * Demarshall values from a buffer. + * + * @param sig The D-Bus signature(s) of the value(s). + * @param buf The buffer to demarshall from. + * @param ofs An array of two ints, the offset into the signature + * and the offset into the data buffer. These values will be + * updated to the start of the next value ofter demarshalling. + * @return The demarshalled value(s). + */ + public Object[] extract(String sig, byte[] buf, int[] ofs) throws DBusException { + if (Debug.debug) + Debug.print(Debug.VERBOSE, "extract(" + sig + ",#" + buf.length + ", {" + ofs[0] + "," + ofs[1] + "}"); + Vector rv = new Vector(); + byte[] sigb = sig.getBytes(); + for (int[] i = ofs; i[0] < sigb.length; i[0]++) { + rv.add(extractone(sigb, buf, i, false)); + } + return rv.toArray(); + } + + /** + * Returns the Bus ID that sent the message. + */ + public String getSource() { + return (String) headers.get(HeaderField.SENDER); + } + + /** + * Returns the destination of the message. + */ + public String getDestination() { + return (String) headers.get(HeaderField.DESTINATION); + } + + /** + * Returns the interface of the message. + */ + public String getInterface() { + return (String) headers.get(HeaderField.INTERFACE); + } + + /** + * Returns the object path of the message. + */ + public String getPath() { + Object o = headers.get(HeaderField.PATH); + if (null == o) return null; + return o.toString(); + } + + /** + * Returns the member name or error name this message represents. + */ + public String getName() { + if (this instanceof Error) + return (String) headers.get(HeaderField.ERROR_NAME); + else + return (String) headers.get(HeaderField.MEMBER); + } + + /** + * Returns the dbus signature of the parameters. + */ + public String getSig() { + return (String) headers.get(HeaderField.SIGNATURE); + } + + /** + * Returns the message flags. + */ + public int getFlags() { + return flags; + } + + /** + * Returns the message serial ID (unique for this connection) + * + * @return the message serial. + */ + public long getSerial() { + return serial; + } + + /** + * If this is a reply to a message, this returns its serial. + * + * @return The reply serial, or 0 if it is not a reply. + */ + public long getReplySerial() { + Number l = (Number) headers.get(HeaderField.REPLY_SERIAL); + if (null == l) return 0; + return l.longValue(); + } + + /** + * Parses and returns the parameters to this message as an Object array. + */ + public Object[] getParameters() throws DBusException { + if (null == args && null != body) { + String sig = (String) headers.get(HeaderField.SIGNATURE); + if (null != sig && 0 != body.length) { + args = extract(sig, body, 0); + } else args = new Object[0]; + } + return args; + } + + protected void setArgs(Object[] args) { + this.args = args; + } + + /** + * Warning, do not use this method unless you really know what you are doing. + */ + public void setSource(String source) throws DBusException { + if (null != body) { + wiredata = new byte[BUFFERINCREMENT][]; + bufferuse = 0; + bytecounter = 0; + preallocate(12); + append("yyyyuu", big ? Endian.BIG : Endian.LITTLE, type, flags, protover, bodylen, serial); + headers.put(HeaderField.SENDER, source); + Object[][] newhead = new Object[headers.size()][]; + int i = 0; + for (Byte b : headers.keySet()) { + newhead[i] = new Object[2]; + newhead[i][0] = b; + newhead[i][1] = headers.get(b); + i++; + } + append("a(yv)", (Object) newhead); + pad((byte) 8); + appendBytes(body); + } + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/MessageReader.java b/federation/sssd/src/main/java/org/freedesktop/dbus/MessageReader.java new file mode 100644 index 0000000000..4dbea003c9 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/MessageReader.java @@ -0,0 +1,194 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import cx.ath.matthew.debug.Debug; +import cx.ath.matthew.utils.Hexdump; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.exceptions.MessageProtocolVersionException; +import org.freedesktop.dbus.exceptions.MessageTypeException; + +import java.io.BufferedInputStream; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.net.SocketTimeoutException; +import java.text.MessageFormat; + +import static org.freedesktop.dbus.Gettext.getString; + +public class MessageReader { + private InputStream in; + private byte[] buf = null; + private byte[] tbuf = null; + private byte[] header = null; + private byte[] body = null; + private int[] len = new int[4]; + + public MessageReader(InputStream in) { + this.in = new BufferedInputStream(in); + } + + public Message readMessage() throws IOException, DBusException { + int rv; + /* Read the 12 byte fixed header, retrying as neccessary */ + if (null == buf) { + buf = new byte[12]; + len[0] = 0; + } + if (len[0] < 12) { + try { + rv = in.read(buf, len[0], 12 - len[0]); + } catch (SocketTimeoutException STe) { + return null; + } + if (-1 == rv) throw new EOFException(getString("transportReturnedEOF")); + len[0] += rv; + } + if (len[0] == 0) return null; + if (len[0] < 12) { + if (Debug.debug) Debug.print(Debug.DEBUG, "Only got " + len[0] + " of 12 bytes of header"); + return null; + } + + /* Parse the details from the header */ + byte endian = buf[0]; + byte type = buf[1]; + byte protover = buf[3]; + if (protover > Message.PROTOCOL) { + buf = null; + throw new MessageProtocolVersionException(MessageFormat.format(getString("protocolVersionUnsupported"), new Object[]{protover})); + } + + /* Read the length of the variable header */ + if (null == tbuf) { + tbuf = new byte[4]; + len[1] = 0; + } + if (len[1] < 4) { + try { + rv = in.read(tbuf, len[1], 4 - len[1]); + } catch (SocketTimeoutException STe) { + return null; + } + if (-1 == rv) throw new EOFException(getString("transportReturnedEOF")); + len[1] += rv; + } + if (len[1] < 4) { + if (Debug.debug) Debug.print(Debug.DEBUG, "Only got " + len[1] + " of 4 bytes of header"); + return null; + } + + /* Parse the variable header length */ + int headerlen = 0; + if (null == header) { + headerlen = (int) Message.demarshallint(tbuf, 0, endian, 4); + if (0 != headerlen % 8) + headerlen += 8 - (headerlen % 8); + } else + headerlen = header.length - 8; + + /* Read the variable header */ + if (null == header) { + header = new byte[headerlen + 8]; + System.arraycopy(tbuf, 0, header, 0, 4); + len[2] = 0; + } + if (len[2] < headerlen) { + try { + rv = in.read(header, 8 + len[2], headerlen - len[2]); + } catch (SocketTimeoutException STe) { + return null; + } + if (-1 == rv) throw new EOFException(getString("transportReturnedEOF")); + len[2] += rv; + } + if (len[2] < headerlen) { + if (Debug.debug) Debug.print(Debug.DEBUG, "Only got " + len[2] + " of " + headerlen + " bytes of header"); + return null; + } + + /* Read the body */ + int bodylen = 0; + if (null == body) bodylen = (int) Message.demarshallint(buf, 4, endian, 4); + if (null == body) { + body = new byte[bodylen]; + len[3] = 0; + } + if (len[3] < body.length) { + try { + rv = in.read(body, len[3], body.length - len[3]); + } catch (SocketTimeoutException STe) { + return null; + } + if (-1 == rv) throw new EOFException(getString("transportReturnedEOF")); + len[3] += rv; + } + if (len[3] < body.length) { + if (Debug.debug) Debug.print(Debug.DEBUG, "Only got " + len[3] + " of " + body.length + " bytes of body"); + return null; + } + + Message m; + switch (type) { + case Message.MessageType.METHOD_CALL: + m = new MethodCall(); + break; + case Message.MessageType.METHOD_RETURN: + m = new MethodReturn(); + break; + case Message.MessageType.SIGNAL: + m = new DBusSignal(); + break; + case Message.MessageType.ERROR: + m = new Error(); + break; + default: + throw new MessageTypeException(MessageFormat.format(getString("messageTypeUnsupported"), new Object[]{type})); + } + if (Debug.debug) { + Debug.print(Debug.VERBOSE, Hexdump.format(buf)); + Debug.print(Debug.VERBOSE, Hexdump.format(tbuf)); + Debug.print(Debug.VERBOSE, Hexdump.format(header)); + Debug.print(Debug.VERBOSE, Hexdump.format(body)); + } + try { + m.populate(buf, header, body); + } catch (DBusException DBe) { + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); + buf = null; + tbuf = null; + body = null; + header = null; + throw DBe; + } catch (RuntimeException Re) { + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, Re); + buf = null; + tbuf = null; + body = null; + header = null; + throw Re; + } + if (Debug.debug) { + Debug.print(Debug.INFO, "=> " + m); + } + buf = null; + tbuf = null; + body = null; + header = null; + return m; + } + + public void close() throws IOException { + if (Debug.debug) Debug.print(Debug.INFO, "Closing Message Reader"); + in.close(); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/MessageWriter.java b/federation/sssd/src/main/java/org/freedesktop/dbus/MessageWriter.java new file mode 100644 index 0000000000..45e8cb75f5 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/MessageWriter.java @@ -0,0 +1,67 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import cx.ath.matthew.debug.Debug; +import cx.ath.matthew.unix.USOutputStream; +import cx.ath.matthew.utils.Hexdump; + +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +public class MessageWriter { + private OutputStream out; + private boolean isunix; + + public MessageWriter(OutputStream out) { + this.out = out; + this.isunix = false; + try { + if (out instanceof USOutputStream) + this.isunix = true; + } catch (Throwable t) { + } + if (!this.isunix) + this.out = new BufferedOutputStream(this.out); + } + + public void writeMessage(Message m) throws IOException { + if (Debug.debug) { + Debug.print(Debug.INFO, "<= " + m); + } + if (null == m) return; + if (null == m.getWireData()) { + if (Debug.debug) Debug.print(Debug.WARN, "Message " + m + " wire-data was null!"); + return; + } + if (isunix) { + if (Debug.debug) { + Debug.print(Debug.DEBUG, "Writing all " + m.getWireData().length + " buffers simultaneously to Unix Socket"); + for (byte[] buf : m.getWireData()) + Debug.print(Debug.VERBOSE, "(" + buf + "):" + (null == buf ? "" : Hexdump.format(buf))); + } + ((USOutputStream) out).write(m.getWireData()); + } else + for (byte[] buf : m.getWireData()) { + if (Debug.debug) + Debug.print(Debug.VERBOSE, "(" + buf + "):" + (null == buf ? "" : Hexdump.format(buf))); + if (null == buf) break; + out.write(buf); + } + out.flush(); + } + + public void close() throws IOException { + if (Debug.debug) Debug.print(Debug.INFO, "Closing Message Writer"); + out.close(); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/MethodCall.java b/federation/sssd/src/main/java/org/freedesktop/dbus/MethodCall.java new file mode 100644 index 0000000000..72242ccb9a --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/MethodCall.java @@ -0,0 +1,137 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import cx.ath.matthew.debug.Debug; +import cx.ath.matthew.utils.Hexdump; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.exceptions.MessageFormatException; + +import java.util.Vector; + +import static org.freedesktop.dbus.Gettext.getString; + +public class MethodCall extends Message { + MethodCall() { + } + + public MethodCall(String dest, String path, String iface, String member, byte flags, String sig, Object... args) throws DBusException { + this(null, dest, path, iface, member, flags, sig, args); + } + + public MethodCall(String source, String dest, String path, String iface, String member, byte flags, String sig, Object... args) throws DBusException { + super(Message.Endian.BIG, Message.MessageType.METHOD_CALL, flags); + + if (null == member || null == path) + throw new MessageFormatException(getString("missingDestinationPathFunction")); + headers.put(Message.HeaderField.PATH, path); + headers.put(Message.HeaderField.MEMBER, member); + + Vector hargs = new Vector(); + + hargs.add(new Object[]{Message.HeaderField.PATH, new Object[]{ArgumentType.OBJECT_PATH_STRING, path}}); + + if (null != source) { + headers.put(Message.HeaderField.SENDER, source); + hargs.add(new Object[]{Message.HeaderField.SENDER, new Object[]{ArgumentType.STRING_STRING, source}}); + } + + if (null != dest) { + headers.put(Message.HeaderField.DESTINATION, dest); + hargs.add(new Object[]{Message.HeaderField.DESTINATION, new Object[]{ArgumentType.STRING_STRING, dest}}); + } + + if (null != iface) { + hargs.add(new Object[]{Message.HeaderField.INTERFACE, new Object[]{ArgumentType.STRING_STRING, iface}}); + headers.put(Message.HeaderField.INTERFACE, iface); + } + + hargs.add(new Object[]{Message.HeaderField.MEMBER, new Object[]{ArgumentType.STRING_STRING, member}}); + + if (null != sig) { + if (Debug.debug) Debug.print(Debug.DEBUG, "Appending arguments with signature: " + sig); + hargs.add(new Object[]{Message.HeaderField.SIGNATURE, new Object[]{ArgumentType.SIGNATURE_STRING, sig}}); + headers.put(Message.HeaderField.SIGNATURE, sig); + setArgs(args); + } + + byte[] blen = new byte[4]; + appendBytes(blen); + append("ua(yv)", serial, hargs.toArray()); + pad((byte) 8); + + long c = bytecounter; + if (null != sig) append(sig, args); + if (Debug.debug) + Debug.print(Debug.DEBUG, "Appended body, type: " + sig + " start: " + c + " end: " + bytecounter + " size: " + (bytecounter - c)); + marshallint(bytecounter - c, blen, 0, 4); + if (Debug.debug) Debug.print("marshalled size (" + blen + "): " + Hexdump.format(blen)); + } + + private static long REPLY_WAIT_TIMEOUT = 20000; + + /** + * Set the default timeout for method calls. + * Default is 20s. + * + * @param timeout New timeout in ms. + */ + public static void setDefaultTimeout(long timeout) { + REPLY_WAIT_TIMEOUT = timeout; + } + + Message reply = null; + + public synchronized boolean hasReply() { + return null != reply; + } + + /** + * Block (if neccessary) for a reply. + * + * @param timeout The length of time to block before timing out (ms). + * @return The reply to this MethodCall, or null if a timeout happens. + */ + public synchronized Message getReply(long timeout) { + if (Debug.debug) Debug.print(Debug.VERBOSE, "Blocking on " + this); + if (null != reply) return reply; + try { + wait(timeout); + return reply; + } catch (InterruptedException Ie) { + return reply; + } + } + + /** + * Block (if neccessary) for a reply. + * Default timeout is 20s, or can be configured with setDefaultTimeout() + * + * @return The reply to this MethodCall, or null if a timeout happens. + */ + public synchronized Message getReply() { + if (Debug.debug) Debug.print(Debug.VERBOSE, "Blocking on " + this); + if (null != reply) return reply; + try { + wait(REPLY_WAIT_TIMEOUT); + return reply; + } catch (InterruptedException Ie) { + return reply; + } + } + + protected synchronized void setReply(Message reply) { + if (Debug.debug) Debug.print(Debug.VERBOSE, "Setting reply to " + this + " to " + reply); + this.reply = reply; + notifyAll(); + } + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/MethodReturn.java b/federation/sssd/src/main/java/org/freedesktop/dbus/MethodReturn.java new file mode 100644 index 0000000000..35f4d0d745 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/MethodReturn.java @@ -0,0 +1,77 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import org.freedesktop.dbus.exceptions.DBusException; + +import java.util.Vector; + +public class MethodReturn extends Message { + MethodReturn() { + } + + public MethodReturn(String dest, long replyserial, String sig, Object... args) throws DBusException { + this(null, dest, replyserial, sig, args); + } + + public MethodReturn(String source, String dest, long replyserial, String sig, Object... args) throws DBusException { + super(Message.Endian.BIG, Message.MessageType.METHOD_RETURN, (byte) 0); + + headers.put(Message.HeaderField.REPLY_SERIAL, replyserial); + + Vector hargs = new Vector(); + hargs.add(new Object[]{Message.HeaderField.REPLY_SERIAL, new Object[]{ArgumentType.UINT32_STRING, replyserial}}); + + if (null != source) { + headers.put(Message.HeaderField.SENDER, source); + hargs.add(new Object[]{Message.HeaderField.SENDER, new Object[]{ArgumentType.STRING_STRING, source}}); + } + + if (null != dest) { + headers.put(Message.HeaderField.DESTINATION, dest); + hargs.add(new Object[]{Message.HeaderField.DESTINATION, new Object[]{ArgumentType.STRING_STRING, dest}}); + } + + if (null != sig) { + hargs.add(new Object[]{Message.HeaderField.SIGNATURE, new Object[]{ArgumentType.SIGNATURE_STRING, sig}}); + headers.put(Message.HeaderField.SIGNATURE, sig); + setArgs(args); + } + + byte[] blen = new byte[4]; + appendBytes(blen); + append("ua(yv)", serial, hargs.toArray()); + pad((byte) 8); + + long c = bytecounter; + if (null != sig) append(sig, args); + marshallint(bytecounter - c, blen, 0, 4); + } + + public MethodReturn(MethodCall mc, String sig, Object... args) throws DBusException { + this(null, mc, sig, args); + } + + public MethodReturn(String source, MethodCall mc, String sig, Object... args) throws DBusException { + this(source, mc.getSource(), mc.getSerial(), sig, args); + this.call = mc; + } + + MethodCall call; + + public MethodCall getCall() { + return call; + } + + protected void setCall(MethodCall call) { + this.call = call; + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/MethodTuple.java b/federation/sssd/src/main/java/org/freedesktop/dbus/MethodTuple.java new file mode 100644 index 0000000000..d135714edd --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/MethodTuple.java @@ -0,0 +1,37 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import cx.ath.matthew.debug.Debug; + +class MethodTuple { + String name; + String sig; + + public MethodTuple(String name, String sig) { + this.name = name; + if (null != sig) + this.sig = sig; + else + this.sig = ""; + if (Debug.debug) Debug.print(Debug.VERBOSE, "new MethodTuple(" + this.name + ", " + this.sig + ")"); + } + + public boolean equals(Object o) { + return o.getClass().equals(MethodTuple.class) + && ((MethodTuple) o).name.equals(this.name) + && ((MethodTuple) o).sig.equals(this.sig); + } + + public int hashCode() { + return name.hashCode() + sig.hashCode(); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/ObjectPath.java b/federation/sssd/src/main/java/org/freedesktop/dbus/ObjectPath.java new file mode 100644 index 0000000000..5ea11583c3 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/ObjectPath.java @@ -0,0 +1,22 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +class ObjectPath extends Path { + public String source; + + // public DBusConnection conn; + public ObjectPath(String source, String path/*, DBusConnection conn*/) { + super(path); + this.source = source; + // this.conn = conn; + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/ObjectTree.java b/federation/sssd/src/main/java/org/freedesktop/dbus/ObjectTree.java new file mode 100644 index 0000000000..0332e6a33f --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/ObjectTree.java @@ -0,0 +1,158 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import cx.ath.matthew.debug.Debug; + +import java.util.regex.Pattern; + +/** + * Keeps track of the exported objects for introspection data + */ +class ObjectTree { + class TreeNode { + String name; + ExportedObject object; + String data; + TreeNode right; + TreeNode down; + + public TreeNode(String name) { + this.name = name; + } + + public TreeNode(String name, ExportedObject object, String data) { + this.name = name; + this.object = object; + this.data = data; + } + } + + private TreeNode root; + + public ObjectTree() { + root = new TreeNode(""); + } + + public static final Pattern slashpattern = Pattern.compile("/"); + + private TreeNode recursiveFind(TreeNode current, String path) { + if ("/".equals(path)) return current; + String[] elements = path.split("/", 2); + // this is us or a parent node + if (path.startsWith(current.name)) { + // this is us + if (path.equals(current.name)) { + return current; + } + // recurse down + else { + if (current.down == null) + return null; + else return recursiveFind(current.down, elements[1]); + } + } else if (current.right == null) { + return null; + } else if (0 > current.right.name.compareTo(elements[0])) { + return null; + } + // recurse right + else { + return recursiveFind(current.right, path); + } + } + + private TreeNode recursiveAdd(TreeNode current, String path, ExportedObject object, String data) { + String[] elements = slashpattern.split(path, 2); + // this is us or a parent node + if (path.startsWith(current.name)) { + // this is us + if (1 == elements.length || "".equals(elements[1])) { + current.object = object; + current.data = data; + } + // recurse down + else { + if (current.down == null) { + String[] el = elements[1].split("/", 2); + current.down = new TreeNode(el[0]); + } + current.down = recursiveAdd(current.down, elements[1], object, data); + } + } + // need to create a new sub-tree on the end + else if (current.right == null) { + current.right = new TreeNode(elements[0]); + current.right = recursiveAdd(current.right, path, object, data); + } + // need to insert here + else if (0 > current.right.name.compareTo(elements[0])) { + TreeNode t = new TreeNode(elements[0]); + t.right = current.right; + current.right = t; + current.right = recursiveAdd(current.right, path, object, data); + } + // recurse right + else { + current.right = recursiveAdd(current.right, path, object, data); + } + return current; + } + + public void add(String path, ExportedObject object, String data) { + if (Debug.debug) Debug.print(Debug.DEBUG, "Adding " + path + " to object tree"); + root = recursiveAdd(root, path, object, data); + } + + public void remove(String path) { + if (Debug.debug) Debug.print(Debug.DEBUG, "Removing " + path + " from object tree"); + TreeNode t = recursiveFind(root, path); + t.object = null; + t.data = null; + } + + public String Introspect(String path) { + TreeNode t = recursiveFind(root, path); + if (null == t) return null; + StringBuilder sb = new StringBuilder(); + sb.append("\n"); + if (null != t.data) sb.append(t.data); + t = t.down; + while (null != t) { + sb.append("\n"); + t = t.right; + } + sb.append(""); + return sb.toString(); + } + + private String recursivePrint(TreeNode current) { + String s = ""; + if (null != current) { + s += current.name; + if (null != current.object) + s += "*"; + if (null != current.down) + s += "/{" + recursivePrint(current.down) + "}"; + if (null != current.right) + s += ", " + recursivePrint(current.right); + } + return s; + } + + public String toString() { + return recursivePrint(root); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/Path.java b/federation/sssd/src/main/java/org/freedesktop/dbus/Path.java new file mode 100644 index 0000000000..91a5a6d716 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/Path.java @@ -0,0 +1,39 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +public class Path implements Comparable { + protected String path; + + public Path(String path) { + this.path = path; + } + + public String getPath() { + return path; + } + + public String toString() { + return path; + } + + public boolean equals(Object other) { + return (other instanceof Path) && path.equals(((Path) other).path); + } + + public int hashCode() { + return path.hashCode(); + } + + public int compareTo(Path that) { + return path.compareTo(that.path); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/Position.java b/federation/sssd/src/main/java/org/freedesktop/dbus/Position.java new file mode 100644 index 0000000000..ed0bb11652 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/Position.java @@ -0,0 +1,29 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Position annotation, to annotate Struct fields + * to be sent over DBus. + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface Position { + /** + * The order of this field in the Struct. + */ + int value(); +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/RemoteInvocationHandler.java b/federation/sssd/src/main/java/org/freedesktop/dbus/RemoteInvocationHandler.java new file mode 100644 index 0000000000..f6b7867859 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/RemoteInvocationHandler.java @@ -0,0 +1,187 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import cx.ath.matthew.debug.Debug; +import org.freedesktop.DBus; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.exceptions.DBusExecutionException; +import org.freedesktop.dbus.exceptions.NotConnected; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.lang.reflect.Type; +import java.text.MessageFormat; +import java.util.Arrays; + +import static org.freedesktop.dbus.Gettext.getString; + +class RemoteInvocationHandler implements InvocationHandler { + public static final int CALL_TYPE_SYNC = 0; + public static final int CALL_TYPE_ASYNC = 1; + public static final int CALL_TYPE_CALLBACK = 2; + + public static Object convertRV(String sig, Object[] rp, Method m, AbstractConnection conn) throws DBusException { + Class c = m.getReturnType(); + + if (null == rp) { + if (null == c || Void.TYPE.equals(c)) return null; + else throw new DBusExecutionException(getString("voidReturnType")); + } else { + try { + if (Debug.debug) + Debug.print(Debug.VERBOSE, "Converting return parameters from " + Arrays.deepToString(rp) + " to type " + m.getGenericReturnType()); + rp = Marshalling.deSerializeParameters(rp, + new Type[]{m.getGenericReturnType()}, conn); + } catch (Exception e) { + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); + throw new DBusExecutionException(MessageFormat.format(getString("invalidReturnType"), new Object[]{e.getMessage()})); + } + } + + switch (rp.length) { + case 0: + if (null == c || Void.TYPE.equals(c)) + return null; + else throw new DBusExecutionException(getString("voidReturnType")); + case 1: + return rp[0]; + default: + + // check we are meant to return multiple values + if (!Tuple.class.isAssignableFrom(c)) + throw new DBusExecutionException(getString("tupleReturnType")); + + Constructor cons = c.getConstructors()[0]; + try { + return cons.newInstance(rp); + } catch (Exception e) { + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); + throw new DBusException(e.getMessage()); + } + } + } + + @SuppressWarnings("unchecked") + public static Object executeRemoteMethod(RemoteObject ro, Method m, AbstractConnection conn, int syncmethod, CallbackHandler callback, Object... args) throws DBusExecutionException { + Type[] ts = m.getGenericParameterTypes(); + String sig = null; + if (ts.length > 0) try { + sig = Marshalling.getDBusType(ts); + args = Marshalling.convertParameters(args, ts, conn); + } catch (DBusException DBe) { + throw new DBusExecutionException(getString("contructDBusTypeFailure") + DBe.getMessage()); + } + MethodCall call; + byte flags = 0; + if (!ro.autostart) flags |= Message.Flags.NO_AUTO_START; + if (syncmethod == CALL_TYPE_ASYNC) flags |= Message.Flags.ASYNC; + if (m.isAnnotationPresent(DBus.Method.NoReply.class)) flags |= Message.Flags.NO_REPLY_EXPECTED; + try { + String name; + if (m.isAnnotationPresent(DBusMemberName.class)) + name = m.getAnnotation(DBusMemberName.class).value(); + else + name = m.getName(); + if (null == ro.iface) + call = new MethodCall(ro.busname, ro.objectpath, null, name, flags, sig, args); + else { + if (null != ro.iface.getAnnotation(DBusInterfaceName.class)) { + call = new MethodCall(ro.busname, ro.objectpath, ro.iface.getAnnotation(DBusInterfaceName.class).value(), name, flags, sig, args); + } else + call = new MethodCall(ro.busname, ro.objectpath, AbstractConnection.dollar_pattern.matcher(ro.iface.getName()).replaceAll("."), name, flags, sig, args); + } + } catch (DBusException DBe) { + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); + throw new DBusExecutionException(getString("constructOutgoingMethodCallFailure") + DBe.getMessage()); + } + if (null == conn.outgoing) throw new NotConnected(getString("notConnected")); + + switch (syncmethod) { + case CALL_TYPE_ASYNC: + conn.queueOutgoing(call); + return new DBusAsyncReply(call, m, conn); + case CALL_TYPE_CALLBACK: + synchronized (conn.pendingCallbacks) { + if (Debug.debug) Debug.print(Debug.VERBOSE, "Queueing Callback " + callback + " for " + call); + conn.pendingCallbacks.put(call, callback); + conn.pendingCallbackReplys.put(call, new DBusAsyncReply(call, m, conn)); + } + conn.queueOutgoing(call); + return null; + case CALL_TYPE_SYNC: + conn.queueOutgoing(call); + break; + } + + // get reply + if (m.isAnnotationPresent(DBus.Method.NoReply.class)) return null; + + Message reply = call.getReply(); + if (null == reply) throw new DBus.Error.NoReply(getString("notReplyWithSpecifiedTime")); + + if (reply instanceof Error) + ((Error) reply).throwException(); + + try { + return convertRV(reply.getSig(), reply.getParameters(), m, conn); + } catch (DBusException e) { + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, e); + throw new DBusExecutionException(e.getMessage()); + } + } + + AbstractConnection conn; + RemoteObject remote; + + public RemoteInvocationHandler(AbstractConnection conn, RemoteObject remote) { + this.remote = remote; + this.conn = conn; + } + + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (method.getName().equals("isRemote")) return true; + else if (method.getName().equals("clone")) return null; + else if (method.getName().equals("equals")) { + try { + if (1 == args.length) + return new Boolean(remote.equals(((RemoteInvocationHandler) Proxy.getInvocationHandler(args[0])).remote)); + } catch (IllegalArgumentException IAe) { + return Boolean.FALSE; + } + } else if (method.getName().equals("finalize")) return null; + else if (method.getName().equals("getClass")) return DBusInterface.class; + else if (method.getName().equals("hashCode")) return remote.hashCode(); + else if (method.getName().equals("notify")) { + remote.notify(); + return null; + } else if (method.getName().equals("notifyAll")) { + remote.notifyAll(); + return null; + } else if (method.getName().equals("wait")) { + if (0 == args.length) remote.wait(); + else if (1 == args.length + && args[0] instanceof Long) remote.wait((Long) args[0]); + else if (2 == args.length + && args[0] instanceof Long + && args[1] instanceof Integer) + remote.wait((Long) args[0], (Integer) args[1]); + if (args.length <= 2) + return null; + } else if (method.getName().equals("toString")) + return remote.toString(); + + return executeRemoteMethod(remote, method, conn, CALL_TYPE_SYNC, null, args); + } +} + diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/RemoteObject.java b/federation/sssd/src/main/java/org/freedesktop/dbus/RemoteObject.java new file mode 100644 index 0000000000..c157d12b1d --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/RemoteObject.java @@ -0,0 +1,67 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +class RemoteObject { + String busname; + String objectpath; + Class iface; + boolean autostart; + + public RemoteObject(String busname, String objectpath, Class iface, boolean autostart) { + this.busname = busname; + this.objectpath = objectpath; + this.iface = iface; + this.autostart = autostart; + } + + public boolean equals(Object o) { + if (!(o instanceof RemoteObject)) return false; + RemoteObject them = (RemoteObject) o; + + if (!them.objectpath.equals(this.objectpath)) return false; + + if (null == this.busname && null != them.busname) return false; + if (null != this.busname && null == them.busname) return false; + if (null != them.busname && !them.busname.equals(this.busname)) return false; + + if (null == this.iface && null != them.iface) return false; + if (null != this.iface && null == them.iface) return false; + if (null != them.iface && !them.iface.equals(this.iface)) return false; + + return true; + } + + public int hashCode() { + return (null == busname ? 0 : busname.hashCode()) + objectpath.hashCode() + + (null == iface ? 0 : iface.hashCode()); + } + + public boolean autoStarting() { + return autostart; + } + + public String getBusName() { + return busname; + } + + public String getObjectPath() { + return objectpath; + } + + public Class getInterface() { + return iface; + } + + public String toString() { + return busname + ":" + objectpath + ":" + iface; + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/SignalTuple.java b/federation/sssd/src/main/java/org/freedesktop/dbus/SignalTuple.java new file mode 100644 index 0000000000..29ec987d2b --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/SignalTuple.java @@ -0,0 +1,51 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +class SignalTuple { + String type; + String name; + String object; + String source; + + public SignalTuple(String type, String name, String object, String source) { + this.type = type; + this.name = name; + this.object = object; + this.source = source; + } + + public boolean equals(Object o) { + if (!(o instanceof SignalTuple)) return false; + SignalTuple other = (SignalTuple) o; + if (null == this.type && null != other.type) return false; + if (null != this.type && !this.type.equals(other.type)) return false; + if (null == this.name && null != other.name) return false; + if (null != this.name && !this.name.equals(other.name)) return false; + if (null == this.object && null != other.object) return false; + if (null != this.object && !this.object.equals(other.object)) return false; + if (null == this.source && null != other.source) return false; + if (null != this.source && !this.source.equals(other.source)) return false; + return true; + } + + public int hashCode() { + return (null == type ? 0 : type.hashCode()) + + (null == name ? 0 : name.hashCode()) + + (null == source ? 0 : source.hashCode()) + + (null == object ? 0 : object.hashCode()); + } + + public String toString() { + return "SignalTuple(" + type + "," + name + "," + object + "," + source + ")"; + } +} + diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/StrongReference.java b/federation/sssd/src/main/java/org/freedesktop/dbus/StrongReference.java new file mode 100644 index 0000000000..d5358eca5d --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/StrongReference.java @@ -0,0 +1,42 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import java.lang.ref.WeakReference; + +/** + * An alternative to a WeakReference when you don't want + * that behaviour. + */ +public class StrongReference extends WeakReference { + T referant; + + public StrongReference(T referant) { + super(referant); + this.referant = referant; + } + + public void clear() { + referant = null; + } + + public boolean enqueue() { + return false; + } + + public T get() { + return referant; + } + + public boolean isEnqueued() { + return false; + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/Struct.java b/federation/sssd/src/main/java/org/freedesktop/dbus/Struct.java new file mode 100644 index 0000000000..18667e58ec --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/Struct.java @@ -0,0 +1,23 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +/** + * This class should be extended to create Structs. + * Any such class may be sent over DBus to a method which takes a Struct. + * All fields in the Struct which you wish to be serialized and sent to the + * remote method should be annotated with the org.freedesktop.dbus.Position + * annotation, in the order they should appear in to Struct to DBus. + */ +public abstract class Struct extends Container { + public Struct() { + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/Transport.java b/federation/sssd/src/main/java/org/freedesktop/dbus/Transport.java new file mode 100644 index 0000000000..1745bcfec2 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/Transport.java @@ -0,0 +1,835 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import cx.ath.matthew.debug.Debug; +import cx.ath.matthew.unix.UnixSocket; +import cx.ath.matthew.unix.UnixSocketAddress; +import cx.ath.matthew.utils.Hexdump; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.lang.reflect.Method; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.text.Collator; +import java.text.ParseException; +import java.util.Arrays; +import java.util.Random; +import java.util.Vector; + +import static org.freedesktop.dbus.Gettext.getString; + +public class Transport { + public static class SASL { + public static class Command { + private int command; + private int mechs; + private String data; + private String response; + + public Command() { + } + + public Command(String s) throws IOException { + String[] ss = s.split(" "); + if (Debug.debug) Debug.print(Debug.VERBOSE, "Creating command from: " + Arrays.toString(ss)); + if (0 == col.compare(ss[0], "OK")) { + command = COMMAND_OK; + data = ss[1]; + } else if (0 == col.compare(ss[0], "AUTH")) { + command = COMMAND_AUTH; + if (ss.length > 1) { + if (0 == col.compare(ss[1], "EXTERNAL")) + mechs = AUTH_EXTERNAL; + else if (0 == col.compare(ss[1], "DBUS_COOKIE_SHA1")) + mechs = AUTH_SHA; + else if (0 == col.compare(ss[1], "ANONYMOUS")) + mechs = AUTH_ANON; + } + if (ss.length > 2) + data = ss[2]; + } else if (0 == col.compare(ss[0], "DATA")) { + command = COMMAND_DATA; + data = ss[1]; + } else if (0 == col.compare(ss[0], "REJECTED")) { + command = COMMAND_REJECTED; + for (int i = 1; i < ss.length; i++) + if (0 == col.compare(ss[i], "EXTERNAL")) + mechs |= AUTH_EXTERNAL; + else if (0 == col.compare(ss[i], "DBUS_COOKIE_SHA1")) + mechs |= AUTH_SHA; + else if (0 == col.compare(ss[i], "ANONYMOUS")) + mechs |= AUTH_ANON; + } else if (0 == col.compare(ss[0], "BEGIN")) { + command = COMMAND_BEGIN; + } else if (0 == col.compare(ss[0], "CANCEL")) { + command = COMMAND_CANCEL; + } else if (0 == col.compare(ss[0], "ERROR")) { + command = COMMAND_ERROR; + data = ss[1]; + } else { + throw new IOException(getString("invalidCommand") + ss[0]); + } + if (Debug.debug) Debug.print(Debug.VERBOSE, "Created command: " + this); + } + + public int getCommand() { + return command; + } + + public int getMechs() { + return mechs; + } + + public String getData() { + return data; + } + + public String getResponse() { + return response; + } + + public void setResponse(String s) { + response = s; + } + + public String toString() { + return "Command(" + command + ", " + mechs + ", " + data + ", " + null + ")"; + } + } + + private static Collator col = Collator.getInstance(); + + static { + col.setDecomposition(Collator.FULL_DECOMPOSITION); + col.setStrength(Collator.PRIMARY); + } + + public static final int LOCK_TIMEOUT = 1000; + public static final int NEW_KEY_TIMEOUT_SECONDS = 60 * 5; + public static final int EXPIRE_KEYS_TIMEOUT_SECONDS = NEW_KEY_TIMEOUT_SECONDS + (60 * 2); + public static final int MAX_TIME_TRAVEL_SECONDS = 60 * 5; + public static final int COOKIE_TIMEOUT = 240; + public static final String COOKIE_CONTEXT = "org_freedesktop_java"; + + private String findCookie(String context, String ID) throws IOException { + String homedir = System.getProperty("user.home"); + File f = new File(homedir + "/.dbus-keyrings/" + context); + BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(f))); + String s = null; + String cookie = null; + long now = System.currentTimeMillis() / 1000; + while (null != (s = r.readLine())) { + String[] line = s.split(" "); + long timestamp = Long.parseLong(line[1]); + if (line[0].equals(ID) && (!(timestamp < 0 || + (now + MAX_TIME_TRAVEL_SECONDS) < timestamp || + (now - EXPIRE_KEYS_TIMEOUT_SECONDS) > timestamp))) { + cookie = line[2]; + break; + } + } + r.close(); + return cookie; + } + + private void addCookie(String context, String ID, long timestamp, String cookie) throws IOException { + String homedir = System.getProperty("user.home"); + File keydir = new File(homedir + "/.dbus-keyrings/"); + File cookiefile = new File(homedir + "/.dbus-keyrings/" + context); + File lock = new File(homedir + "/.dbus-keyrings/" + context + ".lock"); + File temp = new File(homedir + "/.dbus-keyrings/" + context + ".temp"); + + // ensure directory exists + if (!keydir.exists()) keydir.mkdirs(); + + // acquire lock + long start = System.currentTimeMillis(); + while (!lock.createNewFile() && LOCK_TIMEOUT > (System.currentTimeMillis() - start)) ; + + // read old file + Vector lines = new Vector(); + if (cookiefile.exists()) { + BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(cookiefile))); + String s = null; + while (null != (s = r.readLine())) { + String[] line = s.split(" "); + long time = Long.parseLong(line[1]); + // expire stale cookies + if ((timestamp - time) < COOKIE_TIMEOUT) + lines.add(s); + } + r.close(); + } + + // add cookie + lines.add(ID + " " + timestamp + " " + cookie); + + // write temp file + PrintWriter w = new PrintWriter(new FileOutputStream(temp)); + for (String l : lines) + w.println(l); + w.close(); + + // atomically move to old file + if (!temp.renameTo(cookiefile)) { + cookiefile.delete(); + temp.renameTo(cookiefile); + } + + // remove lock + lock.delete(); + } + + /** + * Takes the string, encodes it as hex and then turns it into a string again. + * No, I don't know why either. + */ + private String stupidlyEncode(String data) { + return Hexdump.toHex(data.getBytes()).replaceAll(" ", ""); + } + + private String stupidlyEncode(byte[] data) { + return Hexdump.toHex(data).replaceAll(" ", ""); + } + + private byte getNibble(char c) { + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return (byte) (c - '0'); + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + return (byte) (c - 'A' + 10); + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + return (byte) (c - 'a' + 10); + default: + return 0; + } + } + + private String stupidlyDecode(String data) { + char[] cs = new char[data.length()]; + char[] res = new char[cs.length / 2]; + data.getChars(0, data.length(), cs, 0); + for (int i = 0, j = 0; j < res.length; i += 2, j++) { + int b = 0; + b |= getNibble(cs[i]) << 4; + b |= getNibble(cs[i + 1]); + res[j] = (char) b; + } + return new String(res); + } + + public static final int MODE_SERVER = 1; + public static final int MODE_CLIENT = 2; + + public static final int AUTH_NONE = 0; + public static final int AUTH_EXTERNAL = 1; + public static final int AUTH_SHA = 2; + public static final int AUTH_ANON = 4; + + public static final int COMMAND_AUTH = 1; + public static final int COMMAND_DATA = 2; + public static final int COMMAND_REJECTED = 3; + public static final int COMMAND_OK = 4; + public static final int COMMAND_BEGIN = 5; + public static final int COMMAND_CANCEL = 6; + public static final int COMMAND_ERROR = 7; + + public static final int INITIAL_STATE = 0; + public static final int WAIT_DATA = 1; + public static final int WAIT_OK = 2; + public static final int WAIT_REJECT = 3; + public static final int WAIT_AUTH = 4; + public static final int WAIT_BEGIN = 5; + public static final int AUTHENTICATED = 6; + public static final int FAILED = 7; + + public static final int OK = 1; + public static final int CONTINUE = 2; + public static final int ERROR = 3; + public static final int REJECT = 4; + + public Command receive(InputStream s) throws IOException { + StringBuffer sb = new StringBuffer(); + top: + while (true) { + int c = s.read(); + switch (c) { + case -1: + throw new IOException("Stream unexpectedly short (broken pipe)"); + case 0: + case '\r': + continue; + case '\n': + break top; + default: + sb.append((char) c); + } + } + if (Debug.debug) Debug.print(Debug.VERBOSE, "received: " + sb); + try { + return new Command(sb.toString()); + } catch (Exception e) { + if (Debug.debug && AbstractConnection.EXCEPTION_DEBUG) Debug.print(Debug.ERR, e); + return new Command(); + } + } + + public void send(OutputStream out, int command, String... data) throws IOException { + StringBuffer sb = new StringBuffer(); + switch (command) { + case COMMAND_AUTH: + sb.append("AUTH"); + break; + case COMMAND_DATA: + sb.append("DATA"); + break; + case COMMAND_REJECTED: + sb.append("REJECTED"); + break; + case COMMAND_OK: + sb.append("OK"); + break; + case COMMAND_BEGIN: + sb.append("BEGIN"); + break; + case COMMAND_CANCEL: + sb.append("CANCEL"); + break; + case COMMAND_ERROR: + sb.append("ERROR"); + break; + default: + return; + } + for (String s : data) { + sb.append(' '); + sb.append(s); + } + sb.append('\r'); + sb.append('\n'); + if (Debug.debug) Debug.print(Debug.VERBOSE, "sending: " + sb); + out.write(sb.toString().getBytes()); + } + + public int do_challenge(int auth, Command c) throws IOException { + switch (auth) { + case AUTH_SHA: + String[] reply = stupidlyDecode(c.getData()).split(" "); + if (Debug.debug) Debug.print(Debug.VERBOSE, Arrays.toString(reply)); + if (3 != reply.length) { + if (Debug.debug) Debug.print(Debug.DEBUG, "Reply is not length 3"); + return ERROR; + } + String context = reply[0]; + String ID = reply[1]; + String serverchallenge = reply[2]; + MessageDigest md = null; + try { + md = MessageDigest.getInstance("SHA"); + } catch (NoSuchAlgorithmException NSAe) { + if (Debug.debug && AbstractConnection.EXCEPTION_DEBUG) Debug.print(Debug.ERR, NSAe); + return ERROR; + } + byte[] buf = new byte[8]; + Message.marshallintBig(System.currentTimeMillis(), buf, 0, 8); + String clientchallenge = stupidlyEncode(md.digest(buf)); + md.reset(); + long start = System.currentTimeMillis(); + String cookie = null; + while (null == cookie && (System.currentTimeMillis() - start) < LOCK_TIMEOUT) + cookie = findCookie(context, ID); + if (null == cookie) { + if (Debug.debug) + Debug.print(Debug.DEBUG, "Did not find a cookie in context " + context + " with ID " + ID); + return ERROR; + } + String response = serverchallenge + ":" + clientchallenge + ":" + cookie; + buf = md.digest(response.getBytes()); + if (Debug.debug) + Debug.print(Debug.VERBOSE, "Response: " + response + " hash: " + Hexdump.format(buf)); + response = stupidlyEncode(buf); + c.setResponse(stupidlyEncode(clientchallenge + " " + response)); + return OK; + default: + if (Debug.debug) Debug.print(Debug.DEBUG, "Not DBUS_COOKIE_SHA1 authtype."); + return ERROR; + } + } + + public String challenge = ""; + public String cookie = ""; + + public int do_response(int auth, String Uid, String kernelUid, Command c) { + MessageDigest md = null; + try { + md = MessageDigest.getInstance("SHA"); + } catch (NoSuchAlgorithmException NSAe) { + if (Debug.debug && AbstractConnection.EXCEPTION_DEBUG) Debug.print(Debug.ERR, NSAe); + return ERROR; + } + switch (auth) { + case AUTH_NONE: + switch (c.getMechs()) { + case AUTH_ANON: + return OK; + case AUTH_EXTERNAL: + if (0 == col.compare(Uid, c.getData()) && + (null == kernelUid || 0 == col.compare(Uid, kernelUid))) + return OK; + else + return ERROR; + case AUTH_SHA: + String context = COOKIE_CONTEXT; + long id = System.currentTimeMillis(); + byte[] buf = new byte[8]; + Message.marshallintBig(id, buf, 0, 8); + challenge = stupidlyEncode(md.digest(buf)); + Random r = new Random(); + r.nextBytes(buf); + cookie = stupidlyEncode(md.digest(buf)); + try { + addCookie(context, "" + id, id / 1000, cookie); + } catch (IOException IOe) { + if (Debug.debug && AbstractConnection.EXCEPTION_DEBUG) Debug.print(Debug.ERR, IOe); + } + if (Debug.debug) + Debug.print(Debug.DEBUG, "Sending challenge: " + context + ' ' + id + ' ' + challenge); + c.setResponse(stupidlyEncode(context + ' ' + id + ' ' + challenge)); + return CONTINUE; + default: + return ERROR; + } + case AUTH_SHA: + String[] response = stupidlyDecode(c.getData()).split(" "); + if (response.length < 2) return ERROR; + String cchal = response[0]; + String hash = response[1]; + String prehash = challenge + ":" + cchal + ":" + cookie; + byte[] buf = md.digest(prehash.getBytes()); + String posthash = stupidlyEncode(buf); + if (Debug.debug) + Debug.print(Debug.DEBUG, "Authenticating Hash; data=" + prehash + " remote hash=" + hash + " local hash=" + posthash); + if (0 == col.compare(posthash, hash)) + return OK; + else + return ERROR; + default: + return ERROR; + } + } + + public String[] getTypes(int types) { + switch (types) { + case AUTH_EXTERNAL: + return new String[]{"EXTERNAL"}; + case AUTH_SHA: + return new String[]{"DBUS_COOKIE_SHA1"}; + case AUTH_ANON: + return new String[]{"ANONYMOUS"}; + case AUTH_SHA + AUTH_EXTERNAL: + return new String[]{"EXTERNAL", "DBUS_COOKIE_SHA1"}; + case AUTH_SHA + AUTH_ANON: + return new String[]{"ANONYMOUS", "DBUS_COOKIE_SHA1"}; + case AUTH_EXTERNAL + AUTH_ANON: + return new String[]{"ANONYMOUS", "EXTERNAL"}; + case AUTH_EXTERNAL + AUTH_ANON + AUTH_SHA: + return new String[]{"ANONYMOUS", "EXTERNAL", "DBUS_COOKIE_SHA1"}; + default: + return new String[]{}; + } + } + + /** + * performs SASL auth on the given streams. + * Mode selects whether to run as a SASL server or client. + * Types is a bitmask of the available auth types. + * Returns true if the auth was successful and false if it failed. + */ + @SuppressWarnings("unchecked") + public boolean auth(int mode, int types, String guid, OutputStream out, InputStream in, UnixSocket us) throws IOException { + String username = System.getProperty("user.name"); + String Uid = null; + String kernelUid = null; + try { + Class c = Class.forName("com.sun.security.auth.module.UnixSystem"); + Method m = c.getMethod("getUid"); + Object o = c.newInstance(); + long uid = (Long) m.invoke(o); + Uid = stupidlyEncode("" + uid); + } catch (Exception e) { + Uid = stupidlyEncode(username); + } + Command c; + int failed = 0; + int current = 0; + int state = INITIAL_STATE; + + while (state != AUTHENTICATED && state != FAILED) { + if (Debug.debug) Debug.print(Debug.VERBOSE, "AUTH state: " + state); + switch (mode) { + case MODE_CLIENT: + switch (state) { + case INITIAL_STATE: + if (null == us) + out.write(new byte[]{0}); + else + us.sendCredentialByte((byte) 0); + send(out, COMMAND_AUTH); + state = WAIT_DATA; + break; + case WAIT_DATA: + c = receive(in); + switch (c.getCommand()) { + case COMMAND_DATA: + switch (do_challenge(current, c)) { + case CONTINUE: + send(out, COMMAND_DATA, c.getResponse()); + break; + case OK: + send(out, COMMAND_DATA, c.getResponse()); + state = WAIT_OK; + break; + case ERROR: + send(out, COMMAND_ERROR, c.getResponse()); + break; + } + break; + case COMMAND_REJECTED: + failed |= current; + int available = c.getMechs() & (~failed); + if (0 != (available & AUTH_EXTERNAL)) { + send(out, COMMAND_AUTH, "EXTERNAL", Uid); + current = AUTH_EXTERNAL; + } else if (0 != (available & AUTH_SHA)) { + send(out, COMMAND_AUTH, "DBUS_COOKIE_SHA1", Uid); + current = AUTH_SHA; + } else if (0 != (available & AUTH_ANON)) { + send(out, COMMAND_AUTH, "ANONYMOUS"); + current = AUTH_ANON; + } else state = FAILED; + break; + case COMMAND_ERROR: + send(out, COMMAND_CANCEL); + state = WAIT_REJECT; + break; + case COMMAND_OK: + send(out, COMMAND_BEGIN); + state = AUTHENTICATED; + break; + default: + send(out, COMMAND_ERROR, "Got invalid command"); + break; + } + break; + case WAIT_OK: + c = receive(in); + switch (c.getCommand()) { + case COMMAND_OK: + send(out, COMMAND_BEGIN); + state = AUTHENTICATED; + break; + case COMMAND_ERROR: + case COMMAND_DATA: + send(out, COMMAND_CANCEL); + state = WAIT_REJECT; + break; + case COMMAND_REJECTED: + failed |= current; + int available = c.getMechs() & (~failed); + state = WAIT_DATA; + if (0 != (available & AUTH_EXTERNAL)) { + send(out, COMMAND_AUTH, "EXTERNAL", Uid); + current = AUTH_EXTERNAL; + } else if (0 != (available & AUTH_SHA)) { + send(out, COMMAND_AUTH, "DBUS_COOKIE_SHA1", Uid); + current = AUTH_SHA; + } else if (0 != (available & AUTH_ANON)) { + send(out, COMMAND_AUTH, "ANONYMOUS"); + current = AUTH_ANON; + } else state = FAILED; + break; + default: + send(out, COMMAND_ERROR, "Got invalid command"); + break; + } + break; + case WAIT_REJECT: + c = receive(in); + switch (c.getCommand()) { + case COMMAND_REJECTED: + failed |= current; + int available = c.getMechs() & (~failed); + if (0 != (available & AUTH_EXTERNAL)) { + send(out, COMMAND_AUTH, "EXTERNAL", Uid); + current = AUTH_EXTERNAL; + } else if (0 != (available & AUTH_SHA)) { + send(out, COMMAND_AUTH, "DBUS_COOKIE_SHA1", Uid); + current = AUTH_SHA; + } else if (0 != (available & AUTH_ANON)) { + send(out, COMMAND_AUTH, "ANONYMOUS"); + current = AUTH_ANON; + } else state = FAILED; + break; + default: + state = FAILED; + break; + } + break; + default: + state = FAILED; + } + break; + case MODE_SERVER: + switch (state) { + case INITIAL_STATE: + byte[] buf = new byte[1]; + if (null == us) { + in.read(buf); + } else { + buf[0] = us.recvCredentialByte(); + int kuid = us.getPeerUID(); + if (kuid >= 0) + kernelUid = stupidlyEncode("" + kuid); + } + if (0 != buf[0]) state = FAILED; + else state = WAIT_AUTH; + break; + case WAIT_AUTH: + c = receive(in); + switch (c.getCommand()) { + case COMMAND_AUTH: + if (null == c.getData()) { + send(out, COMMAND_REJECTED, getTypes(types)); + } else { + switch (do_response(current, Uid, kernelUid, c)) { + case CONTINUE: + send(out, COMMAND_DATA, c.getResponse()); + current = c.getMechs(); + state = WAIT_DATA; + break; + case OK: + send(out, COMMAND_OK, guid); + state = WAIT_BEGIN; + current = 0; + break; + case REJECT: + send(out, COMMAND_REJECTED, getTypes(types)); + current = 0; + break; + } + } + break; + case COMMAND_ERROR: + send(out, COMMAND_REJECTED, getTypes(types)); + break; + case COMMAND_BEGIN: + state = FAILED; + break; + default: + send(out, COMMAND_ERROR, "Got invalid command"); + break; + } + break; + case WAIT_DATA: + c = receive(in); + switch (c.getCommand()) { + case COMMAND_DATA: + switch (do_response(current, Uid, kernelUid, c)) { + case CONTINUE: + send(out, COMMAND_DATA, c.getResponse()); + state = WAIT_DATA; + break; + case OK: + send(out, COMMAND_OK, guid); + state = WAIT_BEGIN; + current = 0; + break; + case REJECT: + send(out, COMMAND_REJECTED, getTypes(types)); + current = 0; + break; + } + break; + case COMMAND_ERROR: + case COMMAND_CANCEL: + send(out, COMMAND_REJECTED, getTypes(types)); + state = WAIT_AUTH; + break; + case COMMAND_BEGIN: + state = FAILED; + break; + default: + send(out, COMMAND_ERROR, "Got invalid command"); + break; + } + break; + case WAIT_BEGIN: + c = receive(in); + switch (c.getCommand()) { + case COMMAND_ERROR: + case COMMAND_CANCEL: + send(out, COMMAND_REJECTED, getTypes(types)); + state = WAIT_AUTH; + break; + case COMMAND_BEGIN: + state = AUTHENTICATED; + break; + default: + send(out, COMMAND_ERROR, "Got invalid command"); + break; + } + break; + default: + state = FAILED; + } + break; + default: + return false; + } + } + + return state == AUTHENTICATED; + } + } + + public MessageReader min; + public MessageWriter mout; + + public Transport() { + } + + public static String genGUID() { + Random r = new Random(); + byte[] buf = new byte[16]; + r.nextBytes(buf); + String guid = Hexdump.toHex(buf); + return guid.replaceAll(" ", ""); + } + + public Transport(BusAddress address) throws IOException { + connect(address); + } + + public Transport(String address) throws IOException, ParseException { + connect(new BusAddress(address)); + } + + public Transport(String address, int timeout) throws IOException, ParseException { + connect(new BusAddress(address), timeout); + } + + public void connect(String address) throws IOException, ParseException { + connect(new BusAddress(address), 0); + } + + public void connect(String address, int timeout) throws IOException, ParseException { + connect(new BusAddress(address), timeout); + } + + public void connect(BusAddress address) throws IOException { + connect(address, 0); + } + + public void connect(BusAddress address, int timeout) throws IOException { + if (Debug.debug) Debug.print(Debug.INFO, "Connecting to " + address); + OutputStream out = null; + InputStream in = null; + UnixSocket us = null; + Socket s = null; + int mode = 0; + int types = 0; + if ("unix".equals(address.getType())) { + types = SASL.AUTH_EXTERNAL; + mode = SASL.MODE_CLIENT; + us = new UnixSocket(); + if (null != address.getParameter("abstract")) + us.connect(new UnixSocketAddress(address.getParameter("abstract"), true)); + else if (null != address.getParameter("path")) + us.connect(new UnixSocketAddress(address.getParameter("path"), false)); + us.setPassCred(true); + in = us.getInputStream(); + out = us.getOutputStream(); + } else if ("tcp".equals(address.getType())) { + types = SASL.AUTH_SHA; + if (null != address.getParameter("listen")) { + mode = SASL.MODE_SERVER; + ServerSocket ss = new ServerSocket(); + ss.bind(new InetSocketAddress(address.getParameter("host"), Integer.parseInt(address.getParameter("port")))); + s = ss.accept(); + } else { + mode = SASL.MODE_CLIENT; + s = new Socket(); + s.connect(new InetSocketAddress(address.getParameter("host"), Integer.parseInt(address.getParameter("port")))); + } + in = s.getInputStream(); + out = s.getOutputStream(); + } else { + throw new IOException(getString("unknownAddress") + address.getType()); + } + + if (!(new SASL()).auth(mode, types, address.getParameter("guid"), out, in, us)) { + out.close(); + throw new IOException(getString("errorAuth")); + } + if (null != us) { + if (Debug.debug) Debug.print(Debug.VERBOSE, "Setting timeout to " + timeout + " on Socket"); + if (timeout == 1) + us.setBlocking(false); + else + us.setSoTimeout(timeout); + } + if (null != s) { + if (Debug.debug) Debug.print(Debug.VERBOSE, "Setting timeout to " + timeout + " on Socket"); + s.setSoTimeout(timeout); + } + mout = new MessageWriter(out); + min = new MessageReader(in); + } + + public void disconnect() throws IOException { + if (Debug.debug) Debug.print(Debug.INFO, "Disconnecting Transport"); + min.close(); + mout.close(); + } +} + + diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/Tuple.java b/federation/sssd/src/main/java/org/freedesktop/dbus/Tuple.java new file mode 100644 index 0000000000..5fc13d5fb4 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/Tuple.java @@ -0,0 +1,24 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +/** + * This class should be extended to create Tuples. + * Any such class may be used as the return type for a method + * which returns multiple values. + * All fields in the Tuple which you wish to be serialized and sent to the + * remote method should be annotated with the org.freedesktop.dbus.Position + * annotation, in the order they should appear to DBus. + */ +public abstract class Tuple extends Container { + public Tuple() { + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/TypeSignature.java b/federation/sssd/src/main/java/org/freedesktop/dbus/TypeSignature.java new file mode 100644 index 0000000000..eaad1667ad --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/TypeSignature.java @@ -0,0 +1,37 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import org.freedesktop.dbus.exceptions.DBusException; + +import java.lang.reflect.Type; + +public class TypeSignature { + String sig; + + public TypeSignature(String sig) { + this.sig = sig; + } + + public TypeSignature(Type[] types) throws DBusException { + StringBuffer sb = new StringBuffer(); + for (Type t : types) { + String[] ts = Marshalling.getDBusType(t); + for (String s : ts) + sb.append(s); + } + this.sig = sb.toString(); + } + + public String getSig() { + return sig; + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/UInt16.java b/federation/sssd/src/main/java/org/freedesktop/dbus/UInt16.java new file mode 100644 index 0000000000..a8f0600116 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/UInt16.java @@ -0,0 +1,122 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import java.text.MessageFormat; + +import static org.freedesktop.dbus.Gettext.getString; + +/** + * Class to represent 16-bit unsigned integers. + */ +@SuppressWarnings("serial") +public class UInt16 extends Number implements Comparable { + /** + * Maximum possible value. + */ + public static final int MAX_VALUE = 65535; + /** + * Minimum possible value. + */ + public static final int MIN_VALUE = 0; + private int value; + + /** + * Create a UInt16 from an int. + * + * @param value Must be within MIN_VALUE–MAX_VALUE + * @throws NumberFormatException if value is not between MIN_VALUE and MAX_VALUE + */ + public UInt16(int value) { + if (value < MIN_VALUE || value > MAX_VALUE) + throw new NumberFormatException(MessageFormat.format(getString("isNotBetween"), new Object[]{value, MIN_VALUE, MAX_VALUE})); + this.value = value; + } + + /** + * Create a UInt16 from a String. + * + * @param value Must parse to a valid integer within MIN_VALUE–MAX_VALUE + * @throws NumberFormatException if value is not an integer between MIN_VALUE and MAX_VALUE + */ + public UInt16(String value) { + this(Integer.parseInt(value)); + } + + /** + * The value of this as a byte. + */ + public byte byteValue() { + return (byte) value; + } + + /** + * The value of this as a double. + */ + public double doubleValue() { + return (double) value; + } + + /** + * The value of this as a float. + */ + public float floatValue() { + return (float) value; + } + + /** + * The value of this as a int. + */ + public int intValue() { + return /*(int)*/ value; + } + + /** + * The value of this as a long. + */ + public long longValue() { + return (long) value; + } + + /** + * The value of this as a short. + */ + public short shortValue() { + return (short) value; + } + + /** + * Test two UInt16s for equality. + */ + public boolean equals(Object o) { + return o instanceof UInt16 && ((UInt16) o).value == this.value; + } + + public int hashCode() { + return /*(int)*/ value; + } + + /** + * Compare two UInt16s. + * + * @return 0 if equal, -ve or +ve if they are different. + */ + public int compareTo(UInt16 other) { + return /*(int)*/ (this.value - other.value); + } + + /** + * The value of this as a string. + */ + public String toString() { + return "" + value; + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/UInt32.java b/federation/sssd/src/main/java/org/freedesktop/dbus/UInt32.java new file mode 100644 index 0000000000..465191934d --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/UInt32.java @@ -0,0 +1,122 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import java.text.MessageFormat; + +import static org.freedesktop.dbus.Gettext.getString; + +/** + * Class to represent unsigned 32-bit numbers. + */ +@SuppressWarnings("serial") +public class UInt32 extends Number implements Comparable { + /** + * Maximum allowed value + */ + public static final long MAX_VALUE = 4294967295L; + /** + * Minimum allowed value + */ + public static final long MIN_VALUE = 0; + private long value; + + /** + * Create a UInt32 from a long. + * + * @param value Must be a valid integer within MIN_VALUE–MAX_VALUE + * @throws NumberFormatException if value is not between MIN_VALUE and MAX_VALUE + */ + public UInt32(long value) { + if (value < MIN_VALUE || value > MAX_VALUE) + throw new NumberFormatException(MessageFormat.format(getString("isNotBetween"), new Object[]{value, MIN_VALUE, MAX_VALUE})); + this.value = value; + } + + /** + * Create a UInt32 from a String. + * + * @param value Must parse to a valid integer within MIN_VALUE–MAX_VALUE + * @throws NumberFormatException if value is not an integer between MIN_VALUE and MAX_VALUE + */ + public UInt32(String value) { + this(Long.parseLong(value)); + } + + /** + * The value of this as a byte. + */ + public byte byteValue() { + return (byte) value; + } + + /** + * The value of this as a double. + */ + public double doubleValue() { + return (double) value; + } + + /** + * The value of this as a float. + */ + public float floatValue() { + return (float) value; + } + + /** + * The value of this as a int. + */ + public int intValue() { + return (int) value; + } + + /** + * The value of this as a long. + */ + public long longValue() { + return /*(long)*/ value; + } + + /** + * The value of this as a short. + */ + public short shortValue() { + return (short) value; + } + + /** + * Test two UInt32s for equality. + */ + public boolean equals(Object o) { + return o instanceof UInt32 && ((UInt32) o).value == this.value; + } + + public int hashCode() { + return (int) value; + } + + /** + * Compare two UInt32s. + * + * @return 0 if equal, -ve or +ve if they are different. + */ + public int compareTo(UInt32 other) { + return (int) (this.value - other.value); + } + + /** + * The value of this as a string + */ + public String toString() { + return "" + value; + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/UInt64.java b/federation/sssd/src/main/java/org/freedesktop/dbus/UInt64.java new file mode 100644 index 0000000000..fde2cf0260 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/UInt64.java @@ -0,0 +1,203 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import java.math.BigInteger; +import java.text.MessageFormat; + +import static org.freedesktop.dbus.Gettext.getString; + +/** + * Class to represent unsigned 64-bit numbers. + * Warning: Any functions which take or return a long + * are restricted to the range of a signed 64bit number. + * Use the BigInteger methods if you wish access to the full + * range. + */ +@SuppressWarnings("serial") +public class UInt64 extends Number implements Comparable { + /** + * Maximum allowed value (when accessed as a long) + */ + public static final long MAX_LONG_VALUE = Long.MAX_VALUE; + /** + * Maximum allowed value (when accessed as a BigInteger) + */ + public static final BigInteger MAX_BIG_VALUE = new BigInteger("18446744073709551615"); + /** + * Minimum allowed value + */ + public static final long MIN_VALUE = 0; + private BigInteger value; + private long top; + private long bottom; + + /** + * Create a UInt64 from a long. + * + * @param value Must be a valid integer within MIN_VALUE–MAX_VALUE + * @throws NumberFormatException if value is not between MIN_VALUE and MAX_VALUE + */ + public UInt64(long value) { + if (value < MIN_VALUE || value > MAX_LONG_VALUE) + throw new NumberFormatException(MessageFormat.format(getString("isNotBetween"), new Object[]{value, MIN_VALUE, MAX_LONG_VALUE})); + this.value = new BigInteger("" + value); + this.top = this.value.shiftRight(32).and(new BigInteger("4294967295")).longValue(); + this.bottom = this.value.and(new BigInteger("4294967295")).longValue(); + } + + /** + * Create a UInt64 from two longs. + * + * @param top Most significant 4 bytes. + * @param bottom Least significant 4 bytes. + */ + public UInt64(long top, long bottom) { + BigInteger a = new BigInteger("" + top); + a = a.shiftLeft(32); + a = a.add(new BigInteger("" + bottom)); + if (0 > a.compareTo(BigInteger.ZERO)) + throw new NumberFormatException(MessageFormat.format(getString("isNotBetween"), new Object[]{a, MIN_VALUE, MAX_BIG_VALUE})); + if (0 < a.compareTo(MAX_BIG_VALUE)) + throw new NumberFormatException(MessageFormat.format(getString("isNotBetween"), new Object[]{a, MIN_VALUE, MAX_BIG_VALUE})); + this.value = a; + this.top = top; + this.bottom = bottom; + } + + /** + * Create a UInt64 from a BigInteger + * + * @param value Must be a valid BigInteger between MIN_VALUE–MAX_BIG_VALUE + * @throws NumberFormatException if value is not an integer between MIN_VALUE and MAX_BIG_VALUE + */ + public UInt64(BigInteger value) { + if (null == value) + throw new NumberFormatException(MessageFormat.format(getString("isNotBetween"), new Object[]{value, MIN_VALUE, MAX_BIG_VALUE})); + if (0 > value.compareTo(BigInteger.ZERO)) + throw new NumberFormatException(MessageFormat.format(getString("isNotBetween"), new Object[]{value, MIN_VALUE, MAX_BIG_VALUE})); + if (0 < value.compareTo(MAX_BIG_VALUE)) + throw new NumberFormatException(MessageFormat.format(getString("isNotBetween"), new Object[]{value, MIN_VALUE, MAX_BIG_VALUE})); + this.value = value; + this.top = this.value.shiftRight(32).and(new BigInteger("4294967295")).longValue(); + this.bottom = this.value.and(new BigInteger("4294967295")).longValue(); + } + + /** + * Create a UInt64 from a String. + * + * @param value Must parse to a valid integer within MIN_VALUE–MAX_BIG_VALUE + * @throws NumberFormatException if value is not an integer between MIN_VALUE and MAX_BIG_VALUE + */ + public UInt64(String value) { + if (null == value) + throw new NumberFormatException(MessageFormat.format(getString("isNotBetween"), new Object[]{value, MIN_VALUE, MAX_BIG_VALUE})); + BigInteger a = new BigInteger(value); + if (0 > a.compareTo(BigInteger.ZERO)) + throw new NumberFormatException(MessageFormat.format(getString("isNotBetween"), new Object[]{value, MIN_VALUE, MAX_BIG_VALUE})); + if (0 < a.compareTo(MAX_BIG_VALUE)) + throw new NumberFormatException(MessageFormat.format(getString("isNotBetween"), new Object[]{value, MIN_VALUE, MAX_BIG_VALUE})); + this.value = a; + this.top = this.value.shiftRight(32).and(new BigInteger("4294967295")).longValue(); + this.bottom = this.value.and(new BigInteger("4294967295")).longValue(); + } + + /** + * The value of this as a BigInteger. + */ + public BigInteger value() { + return value; + } + + /** + * The value of this as a byte. + */ + public byte byteValue() { + return value.byteValue(); + } + + /** + * The value of this as a double. + */ + public double doubleValue() { + return value.doubleValue(); + } + + /** + * The value of this as a float. + */ + public float floatValue() { + return value.floatValue(); + } + + /** + * The value of this as a int. + */ + public int intValue() { + return value.intValue(); + } + + /** + * The value of this as a long. + */ + public long longValue() { + return value.longValue(); + } + + /** + * The value of this as a short. + */ + public short shortValue() { + return value.shortValue(); + } + + /** + * Test two UInt64s for equality. + */ + public boolean equals(Object o) { + return o instanceof UInt64 && this.value.equals(((UInt64) o).value); + } + + public int hashCode() { + return value.hashCode(); + } + + /** + * Compare two UInt32s. + * + * @return 0 if equal, -ve or +ve if they are different. + */ + public int compareTo(UInt64 other) { + return this.value.compareTo(other.value); + } + + /** + * The value of this as a string. + */ + public String toString() { + return value.toString(); + } + + /** + * Most significant 4 bytes. + */ + public long top() { + return top; + } + + /** + * Least significant 4 bytes. + */ + public long bottom() { + return bottom; + } +} + diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/Variant.java b/federation/sssd/src/main/java/org/freedesktop/dbus/Variant.java new file mode 100644 index 0000000000..8fe21a592e --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/Variant.java @@ -0,0 +1,136 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import cx.ath.matthew.debug.Debug; +import org.freedesktop.dbus.exceptions.DBusException; + +import java.lang.reflect.Type; +import java.text.MessageFormat; +import java.util.Vector; + +import static org.freedesktop.dbus.Gettext.getString; + +/** + * A Wrapper class for Variant values. + * A method on DBus can send or receive a Variant. + * This will wrap another value whose type is determined at runtime. + * The Variant may be parameterized to restrict the types it may accept. + */ +public class Variant { + private final T o; + private final Type type; + private final String sig; + + /** + * Create a Variant from a basic type object. + * + * @param o The wrapped value. + * @throws IllegalArugmentException If you try and wrap Null or an object of a non-basic type. + */ + public Variant(T o) throws IllegalArgumentException { + if (null == o) throw new IllegalArgumentException(getString("cannotWrapNullInVariant")); + type = o.getClass(); + try { + String[] ss = Marshalling.getDBusType(o.getClass(), true); + if (ss.length != 1) + throw new IllegalArgumentException(getString("cannotWrapMultiValuedInVariant") + type); + this.sig = ss[0]; + } catch (DBusException DBe) { + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); + throw new IllegalArgumentException(MessageFormat.format(getString("cannotWrapUnqualifiedVariant"), new Object[]{o.getClass(), DBe.getMessage()})); + } + this.o = o; + } + + /** + * Create a Variant. + * + * @param o The wrapped value. + * @param type The explicit type of the value. + * @throws IllegalArugmentException If you try and wrap Null or an object which cannot be sent over DBus. + */ + public Variant(T o, Type type) throws IllegalArgumentException { + if (null == o) throw new IllegalArgumentException(getString("cannotWrapNullInVariant")); + this.type = type; + try { + String[] ss = Marshalling.getDBusType(type); + if (ss.length != 1) + throw new IllegalArgumentException(getString("cannotWrapMultiValuedInVariant") + type); + this.sig = ss[0]; + } catch (DBusException DBe) { + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); + throw new IllegalArgumentException(MessageFormat.format(getString("cannotWrapUnqualifiedVariant"), new Object[]{type, DBe.getMessage()})); + } + this.o = o; + } + + /** + * Create a Variant. + * + * @param o The wrapped value. + * @param sig The explicit type of the value, as a dbus type string. + * @throws IllegalArugmentException If you try and wrap Null or an object which cannot be sent over DBus. + */ + public Variant(T o, String sig) throws IllegalArgumentException { + if (null == o) throw new IllegalArgumentException(getString("cannotWrapNullInVariant")); + this.sig = sig; + try { + Vector ts = new Vector(); + Marshalling.getJavaType(sig, ts, 1); + if (ts.size() != 1) + throw new IllegalArgumentException(getString("cannotWrapNoTypesInVariant") + sig); + this.type = ts.get(0); + } catch (DBusException DBe) { + if (AbstractConnection.EXCEPTION_DEBUG && Debug.debug) Debug.print(Debug.ERR, DBe); + throw new IllegalArgumentException(MessageFormat.format(getString("cannotWrapUnqualifiedVariant"), new Object[]{sig, DBe.getMessage()})); + } + this.o = o; + } + + /** + * Return the wrapped value. + */ + public T getValue() { + return o; + } + + /** + * Return the type of the wrapped value. + */ + public Type getType() { + return type; + } + + /** + * Return the dbus signature of the wrapped value. + */ + public String getSig() { + return sig; + } + + /** + * Format the Variant as a string. + */ + public String toString() { + return "[" + o + "]"; + } + + /** + * Compare this Variant with another by comparing contents + */ + @SuppressWarnings("unchecked") + public boolean equals(Object other) { + if (null == other) return false; + if (!(other instanceof Variant)) return false; + return this.o.equals(((Variant) other).o); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/DBusException.java b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/DBusException.java new file mode 100644 index 0000000000..d9c7e93e6a --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/DBusException.java @@ -0,0 +1,24 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus.exceptions; + +/** + * An exception within DBus. + */ +@SuppressWarnings("serial") +public class DBusException extends Exception { + /** + * Create an exception with the specified message + */ + public DBusException(String message) { + super(message); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/DBusExecutionException.java b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/DBusExecutionException.java new file mode 100644 index 0000000000..641a96729d --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/DBusExecutionException.java @@ -0,0 +1,39 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus.exceptions; + +/** + * An exception while running a remote method within DBus. + */ +@SuppressWarnings("serial") +public class DBusExecutionException extends RuntimeException { + private String type; + + /** + * Create an exception with the specified message + */ + public DBusExecutionException(String message) { + super(message); + } + + public void setType(String type) { + this.type = type; + } + + /** + * Get the DBus type of this exception. Use if this + * was an exception we don't have a class file for. + */ + public String getType() { + if (null == type) return getClass().getName(); + else return type; + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/FatalDBusException.java b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/FatalDBusException.java new file mode 100644 index 0000000000..90002de5a3 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/FatalDBusException.java @@ -0,0 +1,18 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus.exceptions; + +@SuppressWarnings("serial") +public class FatalDBusException extends DBusException implements FatalException { + public FatalDBusException(String message) { + super(message); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/FatalException.java b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/FatalException.java new file mode 100644 index 0000000000..58e3220428 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/FatalException.java @@ -0,0 +1,14 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus.exceptions; + +public interface FatalException { +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/InternalMessageException.java b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/InternalMessageException.java new file mode 100644 index 0000000000..ab9ecc1b13 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/InternalMessageException.java @@ -0,0 +1,18 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus.exceptions; + +@SuppressWarnings("serial") +public class InternalMessageException extends DBusExecutionException implements NonFatalException { + public InternalMessageException(String message) { + super(message); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/MarshallingException.java b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/MarshallingException.java new file mode 100644 index 0000000000..3635456488 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/MarshallingException.java @@ -0,0 +1,18 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus.exceptions; + +@SuppressWarnings("serial") +public class MarshallingException extends DBusException implements NonFatalException { + public MarshallingException(String message) { + super(message); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/MessageFormatException.java b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/MessageFormatException.java new file mode 100644 index 0000000000..e8a19389f7 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/MessageFormatException.java @@ -0,0 +1,21 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus.exceptions; + +/** + * Thrown if a message is formatted incorrectly. + */ +@SuppressWarnings("serial") +public class MessageFormatException extends DBusException implements NonFatalException { + public MessageFormatException(String message) { + super(message); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/MessageProtocolVersionException.java b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/MessageProtocolVersionException.java new file mode 100644 index 0000000000..c093b41ccc --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/MessageProtocolVersionException.java @@ -0,0 +1,20 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus.exceptions; + +import java.io.IOException; + +@SuppressWarnings("serial") +public class MessageProtocolVersionException extends IOException implements FatalException { + public MessageProtocolVersionException(String message) { + super(message); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/MessageTypeException.java b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/MessageTypeException.java new file mode 100644 index 0000000000..21028e107e --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/MessageTypeException.java @@ -0,0 +1,20 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus.exceptions; + +import java.io.IOException; + +@SuppressWarnings("serial") +public class MessageTypeException extends IOException implements NonFatalException { + public MessageTypeException(String message) { + super(message); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/NonFatalException.java b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/NonFatalException.java new file mode 100644 index 0000000000..e6a0f36d3f --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/NonFatalException.java @@ -0,0 +1,14 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus.exceptions; + +public interface NonFatalException { +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/NotConnected.java b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/NotConnected.java new file mode 100644 index 0000000000..0610c2065e --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/NotConnected.java @@ -0,0 +1,21 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus.exceptions; + +/** + * Thrown if a DBus action is called when not connected to the Bus. + */ +@SuppressWarnings("serial") +public class NotConnected extends DBusExecutionException implements FatalException { + public NotConnected(String message) { + super(message); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/UnknownTypeCodeException.java b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/UnknownTypeCodeException.java new file mode 100644 index 0000000000..af350758ed --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/exceptions/UnknownTypeCodeException.java @@ -0,0 +1,20 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus.exceptions; + +import static org.freedesktop.dbus.Gettext.getString; + +@SuppressWarnings("serial") +public class UnknownTypeCodeException extends DBusException implements NonFatalException { + public UnknownTypeCodeException(byte code) { + super(getString("invalidDBusCode") + code); + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/types/DBusListType.java b/federation/sssd/src/main/java/org/freedesktop/dbus/types/DBusListType.java new file mode 100644 index 0000000000..a00c3076d1 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/types/DBusListType.java @@ -0,0 +1,44 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus.types; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.List; + +/** + * The type of a list. + * Should be used whenever you need a Type variable for a list. + */ +public class DBusListType implements ParameterizedType { + private Type v; + + /** + * Create a List type. + * + * @param v Type of the list contents. + */ + public DBusListType(Type v) { + this.v = v; + } + + public Type[] getActualTypeArguments() { + return new Type[]{v}; + } + + public Type getRawType() { + return List.class; + } + + public Type getOwnerType() { + return null; + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/types/DBusMapType.java b/federation/sssd/src/main/java/org/freedesktop/dbus/types/DBusMapType.java new file mode 100644 index 0000000000..754e0a2982 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/types/DBusMapType.java @@ -0,0 +1,47 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus.types; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Map; + +/** + * The type of a map. + * Should be used whenever you need a Type variable for a map. + */ +public class DBusMapType implements ParameterizedType { + private Type k; + private Type v; + + /** + * Create a map type. + * + * @param k The type of the keys. + * @param v The type of the values. + */ + public DBusMapType(Type k, Type v) { + this.k = k; + this.v = v; + } + + public Type[] getActualTypeArguments() { + return new Type[]{k, v}; + } + + public Type getRawType() { + return Map.class; + } + + public Type getOwnerType() { + return null; + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/dbus/types/DBusStructType.java b/federation/sssd/src/main/java/org/freedesktop/dbus/types/DBusStructType.java new file mode 100644 index 0000000000..fda2065601 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/dbus/types/DBusStructType.java @@ -0,0 +1,45 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus.types; + +import org.freedesktop.dbus.Struct; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +/** + * The type of a struct. + * Should be used whenever you need a Type variable for a struct. + */ +public class DBusStructType implements ParameterizedType { + private Type[] contents; + + /** + * Create a struct type. + * + * @param contents The types contained in this struct. + */ + public DBusStructType(Type... contents) { + this.contents = contents; + } + + public Type[] getActualTypeArguments() { + return contents; + } + + public Type getRawType() { + return Struct.class; + } + + public Type getOwnerType() { + return null; + } +} diff --git a/federation/sssd/src/main/java/org/freedesktop/sssd/infopipe/Cache.java b/federation/sssd/src/main/java/org/freedesktop/sssd/infopipe/Cache.java new file mode 100644 index 0000000000..1a45839579 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/sssd/infopipe/Cache.java @@ -0,0 +1,33 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.freedesktop.sssd.infopipe; + +import org.freedesktop.dbus.DBusInterface; + +import java.util.List; + +/** + * @author Bruno Oliveira. + */ +public interface Cache extends DBusInterface { + + List List(); + + List ListByDomain(String domain_name); + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/sssd/infopipe/InfoPipe.java b/federation/sssd/src/main/java/org/freedesktop/sssd/infopipe/InfoPipe.java new file mode 100644 index 0000000000..8791170caf --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/sssd/infopipe/InfoPipe.java @@ -0,0 +1,42 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.freedesktop.sssd.infopipe; + +import org.freedesktop.dbus.DBusInterface; +import org.freedesktop.dbus.DBusInterfaceName; +import org.freedesktop.dbus.DBusMemberName; +import org.freedesktop.dbus.Variant; + +import java.util.List; +import java.util.Map; + +/** + * @author Bruno Oliveira. + */ +@DBusInterfaceName("org.freedesktop.sssd.infopipe") +public interface InfoPipe extends DBusInterface { + + String OBJECTPATH = "/org/freedesktop/sssd/infopipe"; + + @DBusMemberName("GetUserAttr") + Map getUserAttributes(String user, List attr); + + @DBusMemberName("GetUserGroups") + List getUserGroups(String user); + +} diff --git a/federation/sssd/src/main/java/org/freedesktop/sssd/infopipe/User.java b/federation/sssd/src/main/java/org/freedesktop/sssd/infopipe/User.java new file mode 100644 index 0000000000..896b80a0f6 --- /dev/null +++ b/federation/sssd/src/main/java/org/freedesktop/sssd/infopipe/User.java @@ -0,0 +1,35 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.freedesktop.sssd.infopipe; + +import org.freedesktop.dbus.DBusInterface; +import org.freedesktop.dbus.DBusInterfaceName; +import org.freedesktop.dbus.DBusMemberName; + +/** + * @author Bruno Oliveira. + */ +@DBusInterfaceName("org.freedesktop.sssd.infopipe.Users") +public interface User extends DBusInterface { + + String OBJECTPATH = "/org/freedesktop/sssd/infopipe/Users"; + + @DBusMemberName("FindByCertificate") + DBusInterface findByCertificate(String pem_cert); + +} diff --git a/federation/sssd/src/main/java/org/jvnet/libpam/PAM.java b/federation/sssd/src/main/java/org/jvnet/libpam/PAM.java new file mode 100644 index 0000000000..7de5f902c1 --- /dev/null +++ b/federation/sssd/src/main/java/org/jvnet/libpam/PAM.java @@ -0,0 +1,188 @@ +/* + * The MIT License + * + * Copyright (c) 2009, Sun Microsystems, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.jvnet.libpam; + +import com.sun.jna.Pointer; +import com.sun.jna.ptr.PointerByReference; +import org.jboss.logging.Logger; +import org.jvnet.libpam.impl.CLibrary.passwd; +import org.jvnet.libpam.impl.PAMLibrary.pam_conv; +import org.jvnet.libpam.impl.PAMLibrary.pam_conv.PamCallback; +import org.jvnet.libpam.impl.PAMLibrary.pam_handle_t; +import org.jvnet.libpam.impl.PAMLibrary.pam_message; +import org.jvnet.libpam.impl.PAMLibrary.pam_response; + +import java.util.Set; + +import static com.sun.jna.Native.POINTER_SIZE; +import static org.jvnet.libpam.impl.CLibrary.libc; +import static org.jvnet.libpam.impl.PAMLibrary.PAM_CONV_ERR; +import static org.jvnet.libpam.impl.PAMLibrary.PAM_PROMPT_ECHO_OFF; +import static org.jvnet.libpam.impl.PAMLibrary.PAM_SUCCESS; +import static org.jvnet.libpam.impl.PAMLibrary.PAM_USER; +import static org.jvnet.libpam.impl.PAMLibrary.libpam; + +/** + * PAM authenticator. + *

+ *

+ * Instances are thread unsafe and non reentrant. An instace cannot be reused + * to authenticate multiple users. + *

+ *

+ * For an overview of PAM programming, refer to the following resources: + *

+ *

+ * + * @author Kohsuke Kawaguchi + */ +public class PAM { + private pam_handle_t pht; + private int ret; + + /** + * Temporarily stored to pass a value from {@link #authenticate(String, String...)} + * to {@link pam_conv}. + */ + private String[] factors; + + /** + * Creates a new authenticator. + * + * @param serviceName PAM service name. This corresponds to the service name that shows up + * in the PAM configuration, + */ + public PAM(String serviceName) throws PAMException { + pam_conv conv = new pam_conv(new PamCallback() { + public int callback(int num_msg, Pointer msg, Pointer resp, Pointer _) { + LOGGER.debug("pam_conv num_msg=" + num_msg); + if (factors == null) + return PAM_CONV_ERR; + + // allocates pam_response[num_msg]. the caller will free this + Pointer m = libc.calloc(pam_response.SIZE, num_msg); + resp.setPointer(0, m); + + for (int i = 0; i < factors.length; i++) { + pam_message pm = new pam_message(msg.getPointer(POINTER_SIZE * i)); + LOGGER.debug(pm.msg_style + ":" + pm.msg); + if (pm.msg_style == PAM_PROMPT_ECHO_OFF) { + pam_response r = new pam_response(m.share(pam_response.SIZE * i)); + r.setResp(factors[i]); + r.write(); // write to (*resp)[i] + } + } + + return PAM_SUCCESS; + } + }); + + PointerByReference phtr = new PointerByReference(); + check(libpam.pam_start(serviceName, null, conv, phtr), "pam_start failed"); + pht = new pam_handle_t(phtr.getValue()); + } + + private void check(int ret, String msg) throws PAMException { + this.ret = ret; + if (ret != 0) { + if (pht != null) + throw new PAMException(msg + " : " + libpam.pam_strerror(pht, ret)); + else + throw new PAMException(msg); + } + } + + /** + * Authenticate the user with a password. + * + * @return Upon a successful authentication, return information about the user. + * @throws PAMException If the authentication fails. + */ + public UnixUser authenticate(String username, String... factors) throws PAMException { + this.factors = factors; + try { + check(libpam.pam_set_item(pht, PAM_USER, username), "pam_set_item failed"); + check(libpam.pam_authenticate(pht, 0), "pam_authenticate failed"); + check(libpam.pam_setcred(pht, 0), "pam_setcred failed"); + // several different error code seem to be used to represent authentication failures +// check(libpam.pam_acct_mgmt(pht,0),"pam_acct_mgmt failed"); + + PointerByReference r = new PointerByReference(); + check(libpam.pam_get_item(pht, PAM_USER, r), "pam_get_item failed"); + String userName = r.getValue().getString(0); + passwd pwd = libc.getpwnam(userName); + if (pwd == null) + throw new PAMException("Authentication succeeded but no user information is available"); + return new UnixUser(userName, pwd); + } finally { + this.factors = null; + } + } + + /** + * Returns the groups a user belongs to + * + * @param username + * @return Set of group names + * @throws PAMException + * @deprecated Pointless and ugly convenience method. + */ + public Set getGroupsOfUser(String username) throws PAMException { + return new UnixUser(username).getGroups(); + } + + /** + * After a successful authentication, call this method to obtain the effective user name. + * This can be different from the user name that you passed to the {@link #authenticate(String, String)} + * method. + */ + + /** + * Performs an early disposal of the object, instead of letting this GC-ed. + * Since PAM may hold on to native resources that don't put pressure on Java GC, + * doing this is a good idea. + *

+ *

+ * This method is called by {@link #finalize()}, too, so it's not required + * to call this method explicitly, however. + */ + public void dispose() { + if (pht != null) { + libpam.pam_end(pht, ret); + pht = null; + } + } + + + @Override + protected void finalize() throws Throwable { + super.finalize(); + dispose(); + } + + private static final Logger LOGGER = Logger.getLogger(PAM.class.getName()); +} diff --git a/federation/sssd/src/main/java/org/jvnet/libpam/PAMException.java b/federation/sssd/src/main/java/org/jvnet/libpam/PAMException.java new file mode 100644 index 0000000000..d26faf74eb --- /dev/null +++ b/federation/sssd/src/main/java/org/jvnet/libpam/PAMException.java @@ -0,0 +1,48 @@ +/* + * The MIT License + * + * Copyright (c) 2009, Sun Microsystems, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.jvnet.libpam; + +/** + * Exception in PAM invoactions. + * + * @author Kohsuke Kawaguchi + */ +public class PAMException extends Exception { + public PAMException() { + } + + public PAMException(String message) { + super(message); + } + + public PAMException(String message, Throwable cause) { + super(message, cause); + } + + public PAMException(Throwable cause) { + super(cause); + } + + private static final long serialVersionUID = 1L; +} diff --git a/federation/sssd/src/main/java/org/jvnet/libpam/UnixUser.java b/federation/sssd/src/main/java/org/jvnet/libpam/UnixUser.java new file mode 100644 index 0000000000..bc1ceab8e1 --- /dev/null +++ b/federation/sssd/src/main/java/org/jvnet/libpam/UnixUser.java @@ -0,0 +1,159 @@ +/* + * The MIT License + * + * Copyright (c) 2009, Sun Microsystems, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.jvnet.libpam; + +import com.sun.jna.Memory; +import com.sun.jna.ptr.IntByReference; +import org.jvnet.libpam.impl.CLibrary.group; +import org.jvnet.libpam.impl.CLibrary.passwd; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import static org.jvnet.libpam.impl.CLibrary.libc; + +/** + * Represents an Unix user. Immutable. + * + * @author Kohsuke Kawaguchi + */ +public class UnixUser { + private final String userName, gecos, dir, shell; + private final int uid, gid; + private final Set groups; + + /*package*/ UnixUser(String userName, passwd pwd) throws PAMException { + this.userName = userName; + this.gecos = pwd.getPwGecos(); + this.dir = pwd.getPwDir(); + this.shell = pwd.getPwShell(); + this.uid = pwd.getPwUid(); + this.gid = pwd.getPwGid(); + + int sz = 4; /*sizeof(gid_t)*/ + + int ngroups = 64; + Memory m = new Memory(ngroups * sz); + IntByReference pngroups = new IntByReference(ngroups); + try { + if (libc.getgrouplist(userName, pwd.getPwGid(), m, pngroups) < 0) { + // allocate a bigger memory + m = new Memory(pngroups.getValue() * sz); + if (libc.getgrouplist(userName, pwd.getPwGid(), m, pngroups) < 0) + // shouldn't happen, but just in case. + throw new PAMException("getgrouplist failed"); + } + ngroups = pngroups.getValue(); + } catch (LinkageError e) { + // some platform, notably Solaris, doesn't have the getgrouplist function + ngroups = libc._getgroupsbymember(userName, m, ngroups, 0); + if (ngroups < 0) + throw new PAMException("_getgroupsbymember failed"); + } + + groups = new HashSet(); + for (int i = 0; i < ngroups; i++) { + int gid = m.getInt(i * sz); + group grp = libc.getgrgid(gid); + if (grp == null) { + continue; + } + groups.add(grp.gr_name); + } + } + + public UnixUser(String userName) throws PAMException { + this(userName, passwd.loadPasswd(userName)); + } + + /** + * Copy constructor for mocking. Not intended for regular use. Only for testing. + * This signature may change in the future. + */ + protected UnixUser(String userName, String gecos, String dir, String shell, int uid, int gid, Set groups) { + this.userName = userName; + this.gecos = gecos; + this.dir = dir; + this.shell = shell; + this.uid = uid; + this.gid = gid; + this.groups = groups; + } + + /** + * Gets the unix account name. Never null. + */ + public String getUserName() { + return userName; + } + + /** + * Gets the UID of this user. + */ + public int getUID() { + return uid; + } + + /** + * Gets the GID of this user. + */ + public int getGID() { + return gid; + } + + /** + * Gets the gecos (the real name) of this user. + */ + public String getGecos() { + return gecos; + } + + /** + * Gets the home directory of this user. + */ + public String getDir() { + return dir; + } + + /** + * Gets the shell of this user. + */ + public String getShell() { + return shell; + } + + /** + * Gets the groups that this user belongs to. + * + * @return never null. + */ + public Set getGroups() { + return Collections.unmodifiableSet(groups); + } + + public static boolean exists(String name) { + return libc.getpwnam(name) != null; + } +} diff --git a/federation/sssd/src/main/java/org/jvnet/libpam/impl/BSDCLibrary.java b/federation/sssd/src/main/java/org/jvnet/libpam/impl/BSDCLibrary.java new file mode 100644 index 0000000000..0de8a2a7e0 --- /dev/null +++ b/federation/sssd/src/main/java/org/jvnet/libpam/impl/BSDCLibrary.java @@ -0,0 +1,34 @@ +/* + * The MIT License + * + * Copyright 2011, Sun Microsystems, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package org.jvnet.libpam.impl; + +/** + * @author Sebastian Sdorra + */ +public interface BSDCLibrary extends CLibrary { + + BSDPasswd getpwnam(String username); + +} diff --git a/federation/sssd/src/main/java/org/jvnet/libpam/impl/BSDPasswd.java b/federation/sssd/src/main/java/org/jvnet/libpam/impl/BSDPasswd.java new file mode 100644 index 0000000000..2e437bb660 --- /dev/null +++ b/federation/sssd/src/main/java/org/jvnet/libpam/impl/BSDPasswd.java @@ -0,0 +1,93 @@ +/* + * The MIT License + * + * Copyright 2011, Sun Microsystems, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package org.jvnet.libpam.impl; + +import org.jvnet.libpam.impl.CLibrary.passwd; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * FreeeBSD, OpenBSD and MacOS passwd + *

+ * struct passwd { + * char *pw_name; + * char *pw_passwd; + * uid_t pw_uid; + * gid_t pw_gid; + * time_t pw_change; + * char *pw_class; + * char *pw_gecos; + * char *pw_dir; + * char *pw_shell; + * time_t pw_expire; + * }; + * + * @author Sebastian Sdorra + */ +public class BSDPasswd extends passwd { + /* password change time */ + public long pw_change; + + /* user access class */ + public String pw_class; + + /* Honeywell login info */ + public String pw_gecos; + + /* home directory */ + public String pw_dir; + + /* default shell */ + public String pw_shell; + + /* account expiration */ + public long pw_expire; + + @Override + public String getPwGecos() { + return pw_gecos; + } + + @Override + public String getPwDir() { + return pw_dir; + } + + @Override + public String getPwShell() { + return pw_shell; + } + + @Override + protected List getFieldOrder() { + List fieldOrder = new ArrayList(super.getFieldOrder()); + fieldOrder.addAll(Arrays.asList("pw_change", "pw_class", "pw_gecos", + "pw_dir", "pw_shell", "pw_expire")); + return fieldOrder; + } + +} diff --git a/federation/sssd/src/main/java/org/jvnet/libpam/impl/CLibrary.java b/federation/sssd/src/main/java/org/jvnet/libpam/impl/CLibrary.java new file mode 100644 index 0000000000..e3b610536a --- /dev/null +++ b/federation/sssd/src/main/java/org/jvnet/libpam/impl/CLibrary.java @@ -0,0 +1,154 @@ +/* + * The MIT License + * + * Copyright (c) 2009, Sun Microsystems, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.jvnet.libpam.impl; + +import com.sun.jna.Library; +import com.sun.jna.Memory; +import com.sun.jna.Native; +import com.sun.jna.Platform; +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.ptr.IntByReference; +import org.jvnet.libpam.PAMException; + +import java.util.Arrays; +import java.util.List; + +/** + * @author Kohsuke Kawaguchi + */ +public interface CLibrary extends Library { + /** + * Comparing http://linux.die.net/man/3/getpwnam + * and my Mac OS X reveals that the structure of this field isn't very portable. + * In particular, we cannot read the real name reliably. + */ + public class passwd extends Structure { + /** + * User name. + */ + public String pw_name; + /** + * Encrypted password. + */ + public String pw_passwd; + public int pw_uid; + public int pw_gid; + + // ... there are a lot more fields + + public static passwd loadPasswd(String userName) throws PAMException { + passwd pwd = libc.getpwnam(userName); + if (pwd == null) { + throw new PAMException("No user information is available"); + } + return pwd; + } + + public String getPwName() { + return pw_name; + } + + public String getPwPasswd() { + return pw_passwd; + } + + public int getPwUid() { + return pw_uid; + } + + public int getPwGid() { + return pw_gid; + } + + public String getPwGecos() { + return null; + } + + public String getPwDir() { + return null; + } + + public String getPwShell() { + return null; + } + + protected List getFieldOrder() { + return Arrays.asList("pw_name", "pw_passwd", "pw_uid", "pw_gid"); + } + } + + public class group extends Structure { + public String gr_name; + // ... the rest of the field is not interesting for us + + protected List getFieldOrder() { + return Arrays.asList("gr_name"); + } + } + + Pointer calloc(int count, int size); + + Pointer strdup(String s); + + passwd getpwnam(String username); + + /** + * Lists up group IDs of the given user. On Linux and most BSDs, but not on Solaris. + * See http://www.gnu.org/software/hello/manual/gnulib/getgrouplist.html + */ + int getgrouplist(String user, int/*gid_t*/ group, Memory groups, IntByReference ngroups); + + /** + * getgrouplist equivalent on Solaris. + * See http://mail.opensolaris.org/pipermail/sparks-discuss/2008-September/000528.html + */ + int _getgroupsbymember(String user, Memory groups, int maxgids, int numgids); + + group getgrgid(int/*gid_t*/ gid); + + group getgrnam(String name); + + // other user/group related functions that are likely useful + // see http://www.gnu.org/software/libc/manual/html_node/Users-and-Groups.html#Users-and-Groups + + + public static final CLibrary libc = Instance.init(); + + static class Instance { + private static CLibrary init() { + if (Platform.isMac() || Platform.isOpenBSD()) { + return (CLibrary) Native.loadLibrary("c", BSDCLibrary.class); + } else if (Platform.isFreeBSD()) { + return (CLibrary) Native.loadLibrary("c", FreeBSDCLibrary.class); + } else if (Platform.isSolaris()) { + return (CLibrary) Native.loadLibrary("c", SolarisCLibrary.class); + } else if (Platform.isLinux()) { + return (CLibrary) Native.loadLibrary("c", LinuxCLibrary.class); + } else { + return (CLibrary) Native.loadLibrary("c", CLibrary.class); + } + } + } +} diff --git a/federation/sssd/src/main/java/org/jvnet/libpam/impl/FreeBSDCLibrary.java b/federation/sssd/src/main/java/org/jvnet/libpam/impl/FreeBSDCLibrary.java new file mode 100644 index 0000000000..638aec2e5c --- /dev/null +++ b/federation/sssd/src/main/java/org/jvnet/libpam/impl/FreeBSDCLibrary.java @@ -0,0 +1,34 @@ +/* + * The MIT License + * + * Copyright 2014, R. Tyler Croy, Sun Microsystems, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package org.jvnet.libpam.impl; + +/** + * @author R. Tyler Croy + */ +public interface FreeBSDCLibrary extends CLibrary { + + FreeBSDPasswd getpwnam(String username); + +} diff --git a/federation/sssd/src/main/java/org/jvnet/libpam/impl/FreeBSDPasswd.java b/federation/sssd/src/main/java/org/jvnet/libpam/impl/FreeBSDPasswd.java new file mode 100644 index 0000000000..28e519b561 --- /dev/null +++ b/federation/sssd/src/main/java/org/jvnet/libpam/impl/FreeBSDPasswd.java @@ -0,0 +1,98 @@ +/* + * The MIT License + * + * Copyright 2014, R. Tyler Croy, Sun Microsystems, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package org.jvnet.libpam.impl; + +import org.jvnet.libpam.impl.CLibrary.passwd; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * FreeeBSD + *

+ * struct passwd { + * char *pw_name; + * char *pw_passwd; + * uid_t pw_uid; + * gid_t pw_gid; + * time_t pw_change; + * char *pw_class; + * char *pw_gecos; + * char *pw_dir; + * char *pw_shell; + * time_t pw_expire; + * int pw_fields; + * }; + * + * @author R. Tyler Croy + */ + +public class FreeBSDPasswd extends passwd { + /* password change time */ + public long pw_change; + + /* user access class */ + public String pw_class; + + /* Honeywell login info */ + public String pw_gecos; + + /* home directory */ + public String pw_dir; + + /* default shell */ + public String pw_shell; + + /* account expiration */ + public long pw_expire; + + /* internal on FreeBSD? */ + public int pw_fields; + + @Override + public String getPwGecos() { + return pw_gecos; + } + + @Override + public String getPwDir() { + return pw_dir; + } + + @Override + public String getPwShell() { + return pw_shell; + } + + @Override + protected List getFieldOrder() { + List fieldOrder = new ArrayList(super.getFieldOrder()); + fieldOrder.addAll(Arrays.asList("pw_change", "pw_class", "pw_gecos", + "pw_dir", "pw_shell", "pw_expire", "pw_fields")); + return fieldOrder; + } + +} diff --git a/federation/sssd/src/main/java/org/jvnet/libpam/impl/LinuxCLibrary.java b/federation/sssd/src/main/java/org/jvnet/libpam/impl/LinuxCLibrary.java new file mode 100644 index 0000000000..2525bbe64a --- /dev/null +++ b/federation/sssd/src/main/java/org/jvnet/libpam/impl/LinuxCLibrary.java @@ -0,0 +1,34 @@ +/* + * The MIT License + * + * Copyright 2011, Sun Microsystems, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package org.jvnet.libpam.impl; + +/** + * @author Sebastian Sdorra + */ +public interface LinuxCLibrary extends CLibrary { + + LinuxPasswd getpwnam(String username); + +} diff --git a/federation/sssd/src/main/java/org/jvnet/libpam/impl/LinuxPasswd.java b/federation/sssd/src/main/java/org/jvnet/libpam/impl/LinuxPasswd.java new file mode 100644 index 0000000000..c1b167877c --- /dev/null +++ b/federation/sssd/src/main/java/org/jvnet/libpam/impl/LinuxPasswd.java @@ -0,0 +1,81 @@ +/* + * The MIT License + * + * Copyright 2011, Sun Microsystems, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package org.jvnet.libpam.impl; + +import org.jvnet.libpam.impl.CLibrary.passwd; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Linux passwd + *

+ * ?struct passwd + * { + * char *pw_name; + * char *pw_passwd; + * __uid_t pw_uid; + * __gid_t pw_gid; + * char *pw_gecos; + * char *pw_dir; + * char *pw_shell; + * }; + * + * @author Sebastian Sdorra + */ +public class LinuxPasswd extends passwd { + /* Honeywell login info */ + public String pw_gecos; + + /* home directory */ + public String pw_dir; + + /* default shell */ + public String pw_shell; + + + public String getPwGecos() { + return pw_gecos; + } + + @Override + public String getPwDir() { + return pw_dir; + } + + @Override + public String getPwShell() { + return pw_shell; + } + + @Override + protected List getFieldOrder() { + List fieldOrder = new ArrayList(super.getFieldOrder()); + fieldOrder.addAll(Arrays.asList("pw_gecos", "pw_dir", "pw_shell")); + return fieldOrder; + } + +} diff --git a/federation/sssd/src/main/java/org/jvnet/libpam/impl/PAMLibrary.java b/federation/sssd/src/main/java/org/jvnet/libpam/impl/PAMLibrary.java new file mode 100644 index 0000000000..a34ec3184a --- /dev/null +++ b/federation/sssd/src/main/java/org/jvnet/libpam/impl/PAMLibrary.java @@ -0,0 +1,163 @@ +/* + * The MIT License + * + * Copyright (c) 2009, Sun Microsystems, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.jvnet.libpam.impl; + +import com.sun.jna.Callback; +import com.sun.jna.Library; +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.PointerType; +import com.sun.jna.Structure; +import com.sun.jna.ptr.PointerByReference; + +import java.util.Arrays; +import java.util.List; + +import static org.jvnet.libpam.impl.CLibrary.libc; + +/** + * libpam.so binding. + *

+ * See http://www.opengroup.org/onlinepubs/008329799/apdxa.htm + * for the online reference of pam_appl.h + * + * @author Kohsuke Kawaguchi + */ +public interface PAMLibrary extends Library { + class pam_handle_t extends PointerType { + public pam_handle_t() { + } + + public pam_handle_t(Pointer pointer) { + super(pointer); + } + } + + class pam_message extends Structure { + public int msg_style; + public String msg; + + /** + * Attach to the memory region pointed by the given pointer. + */ + public pam_message(Pointer src) { + useMemory(src); + read(); + } + + protected List getFieldOrder() { + return Arrays.asList("msg_style", "msg"); + } + } + + class pam_response extends Structure { + /** + * This is really a string, but this field needs to be malloc-ed by the conversation + * method, and to be freed by the caler, so I bind it to {@link Pointer} here. + *

+ * The man page doesn't say that, but see + * http://www.netbsd.org/docs/guide/en/chap-pam.html#pam-sample-conv + * This behavior is confirmed with a test, too; if I don't do strdup, + * libpam crashes. + */ + public Pointer resp; + public int resp_retcode; + + /** + * Attach to the memory region pointed by the given memory. + */ + public pam_response(Pointer src) { + useMemory(src); + read(); + } + + public pam_response() { + } + + /** + * Sets the response code. + */ + public void setResp(String msg) { + this.resp = libc.strdup(msg); + } + + protected List getFieldOrder() { + return Arrays.asList("resp", "resp_retcode"); + } + + public static final int SIZE = new pam_response().size(); + } + + class pam_conv extends Structure { + public interface PamCallback extends Callback { + /** + * According to http://www.netbsd.org/docs/guide/en/chap-pam.html#pam-sample-conv, + * resp and its member string both needs to be allocated by malloc, + * to be freed by the caller. + */ + int callback(int num_msg, Pointer msg, Pointer resp, Pointer _); + } + + public PamCallback conv; + public Pointer _; + + public pam_conv(PamCallback conv) { + this.conv = conv; + } + + protected List getFieldOrder() { + return Arrays.asList("conv", "_"); + } + } + + int pam_start(String service, String user, pam_conv conv, PointerByReference/* pam_handle_t** */ pamh_p); + + int pam_end(pam_handle_t handle, int pam_status); + + int pam_set_item(pam_handle_t handle, int item_type, String item); + + int pam_get_item(pam_handle_t handle, int item_type, PointerByReference item); + + int pam_authenticate(pam_handle_t handle, int flags); + + int pam_setcred(pam_handle_t handle, int flags); + + int pam_acct_mgmt(pam_handle_t handle, int flags); + + String pam_strerror(pam_handle_t handle, int pam_error); + + final int PAM_USER = 2; + + // error code + final int PAM_SUCCESS = 0; + final int PAM_CONV_ERR = 6; + + + final int PAM_PROMPT_ECHO_OFF = 1; /* Echo off when getting response */ + final int PAM_PROMPT_ECHO_ON = 2; /* Echo on when getting response */ + final int PAM_ERROR_MSG = 3; /* Error message */ + final int PAM_TEXT_INFO = 4; /* Textual information */ + + public static final PAMLibrary libpam = (PAMLibrary) Native.loadLibrary("pam", PAMLibrary.class); +} diff --git a/federation/sssd/src/main/java/org/jvnet/libpam/impl/SolarisCLibrary.java b/federation/sssd/src/main/java/org/jvnet/libpam/impl/SolarisCLibrary.java new file mode 100644 index 0000000000..6b538a2407 --- /dev/null +++ b/federation/sssd/src/main/java/org/jvnet/libpam/impl/SolarisCLibrary.java @@ -0,0 +1,35 @@ +/* + * The MIT License + * + * Copyright (c) 2009, Sun Microsystems, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +package org.jvnet.libpam.impl; + +/** + * @author Sebastian Sdorra + */ +public interface SolarisCLibrary extends CLibrary { + + SolarisPasswd getpwnam(String username); + +} diff --git a/federation/sssd/src/main/java/org/jvnet/libpam/impl/SolarisPasswd.java b/federation/sssd/src/main/java/org/jvnet/libpam/impl/SolarisPasswd.java new file mode 100644 index 0000000000..37ee749230 --- /dev/null +++ b/federation/sssd/src/main/java/org/jvnet/libpam/impl/SolarisPasswd.java @@ -0,0 +1,86 @@ +/* + * The MIT License + * + * Copyright (c) 2009, Sun Microsystems, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + + +package org.jvnet.libpam.impl; + +import org.jvnet.libpam.impl.CLibrary.passwd; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Solaris passwd + *

+ * struct passwd { + * char *pw_name; + * char *pw_passwd; + * uid_t pw_uid; + * gid_t pw_gid; + * char *pw_age; + * char *pw_comment; + * char *pw_gecos; + * char *pw_dir; + * char *pw_shell; + * }; + * + * @author Sebastian Sdorra + */ +public class SolarisPasswd extends passwd { + public String pw_age; + + public String pw_comment; + + public String pw_gecos; + + public String pw_dir; + + public String pw_shell; + + + @Override + public String getPwGecos() { + return pw_gecos; + } + + @Override + public String getPwDir() { + return pw_dir; + } + + @Override + public String getPwShell() { + return pw_shell; + } + + @Override + protected List getFieldOrder() { + List fieldOrder = new ArrayList(super.getFieldOrder()); + fieldOrder.addAll(Arrays.asList("pw_age", "pw_comment", "pw_gecos", + "pw_dir", "pw_shell")); + return fieldOrder; + } + +} diff --git a/federation/sssd/src/main/java/org/keycloak/federation/sssd/ReadonlySSSDUserModelDelegate.java b/federation/sssd/src/main/java/org/keycloak/federation/sssd/ReadonlySSSDUserModelDelegate.java new file mode 100755 index 0000000000..52061c9d18 --- /dev/null +++ b/federation/sssd/src/main/java/org/keycloak/federation/sssd/ReadonlySSSDUserModelDelegate.java @@ -0,0 +1,77 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.federation.sssd; + +import org.keycloak.models.ModelReadOnlyException; +import org.keycloak.models.UserCredentialModel; +import org.keycloak.models.UserCredentialValueModel; +import org.keycloak.models.UserModel; +import org.keycloak.models.utils.UserModelDelegate; + +/** + * Readonly proxy for a SSSD UserModel that prevents attributes from being updated. + * + * @author Bill Burke + * @author Bruno Oliveira + * @version $Revision: 1 $ + */ +public class ReadonlySSSDUserModelDelegate extends UserModelDelegate implements UserModel { + + private final SSSDFederationProvider provider; + + public ReadonlySSSDUserModelDelegate(UserModel delegate, SSSDFederationProvider provider) { + super(delegate); + this.provider = provider; + } + + @Override + public void setUsername(String username) { + throw new ModelReadOnlyException("Federated storage is not writable"); + } + + @Override + public void setLastName(String lastName) { + throw new ModelReadOnlyException("Federated storage is not writable"); + } + + @Override + public void setFirstName(String first) { + throw new ModelReadOnlyException("Federated storage is not writable"); + } + + @Override + public void updateCredentialDirectly(UserCredentialValueModel cred) { + if (cred.getType().equals(UserCredentialModel.PASSWORD)) { + throw new IllegalStateException("Federated storage is not writable"); + } + super.updateCredentialDirectly(cred); + } + + @Override + public void updateCredential(UserCredentialModel cred) { + if (provider.getSupportedCredentialTypes(delegate).contains(cred.getType())) { + throw new ModelReadOnlyException("Federated storage is not writable"); + } + delegate.updateCredential(cred); + } + + @Override + public void setEmail(String email) { + throw new ModelReadOnlyException("Federated storage is not writable"); + } +} diff --git a/federation/sssd/src/main/java/org/keycloak/federation/sssd/SSSDFederationProvider.java b/federation/sssd/src/main/java/org/keycloak/federation/sssd/SSSDFederationProvider.java new file mode 100755 index 0000000000..a9089ec0b1 --- /dev/null +++ b/federation/sssd/src/main/java/org/keycloak/federation/sssd/SSSDFederationProvider.java @@ -0,0 +1,224 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.federation.sssd; + +import org.freedesktop.dbus.Variant; +import org.jboss.logging.Logger; +import org.keycloak.federation.sssd.api.Sssd; +import org.keycloak.federation.sssd.impl.PAMAuthenticator; +import org.keycloak.models.CredentialValidationOutput; +import org.keycloak.models.GroupModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.RealmModel; +import org.keycloak.models.RoleModel; +import org.keycloak.models.UserCredentialModel; +import org.keycloak.models.UserFederationProvider; +import org.keycloak.models.UserFederationProviderModel; +import org.keycloak.models.UserModel; +import org.keycloak.models.utils.KeycloakModelUtils; +import org.keycloak.services.managers.UserManager; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * SPI provider implementation to retrieve data from SSSD and authenticate + * against PAM + * + * @author Bruno Oliveira + * @version $Revision: 1 $ + */ +public class SSSDFederationProvider implements UserFederationProvider { + + private static final Logger logger = Logger.getLogger(SSSDFederationProvider.class); + + protected static final Set supportedCredentialTypes = new HashSet<>(); + private final SSSDFederationProviderFactory factory; + protected KeycloakSession session; + protected UserFederationProviderModel model; + + public SSSDFederationProvider(KeycloakSession session, UserFederationProviderModel model, SSSDFederationProviderFactory sssdFederationProviderFactory) { + this.session = session; + this.model = model; + this.factory = sssdFederationProviderFactory; + } + + static { + supportedCredentialTypes.add(UserCredentialModel.PASSWORD); + } + + + @Override + public UserModel getUserByUsername(RealmModel realm, String username) { + return findOrCreateAuthenticatedUser(realm, username); + } + + /** + * Called after successful authentication + * + * @param realm realm + * @param username username without realm prefix + * @return user if found or successfully created. Null if user with same username already exists, but is not linked to this provider + */ + protected UserModel findOrCreateAuthenticatedUser(RealmModel realm, String username) { + UserModel user = session.userStorage().getUserByUsername(username, realm); + if (user != null) { + logger.debug("SSSD authenticated user " + username + " found in Keycloak storage"); + + if (!model.getId().equals(user.getFederationLink())) { + logger.warn("User with username " + username + " already exists, but is not linked to provider [" + model.getDisplayName() + "]"); + return null; + } else { + UserModel proxied = validateAndProxy(realm, user); + if (proxied != null) { + return proxied; + } else { + logger.warn("User with username " + username + " already exists and is linked to provider [" + model.getDisplayName() + + "] but principal is not correct."); + logger.warn("Will re-create user"); + new UserManager(session).removeUser(realm, user, session.userStorage()); + } + } + } + + logger.debug("SSSD authenticated user " + username + " not in Keycloak storage. Creating..."); + return importUserToKeycloak(realm, username); + } + + protected UserModel importUserToKeycloak(RealmModel realm, String username) { + Sssd sssd = new Sssd(username); + Map sssdUser = sssd.getUserAttributes(); + logger.debugf("Creating SSSD user: %s to local Keycloak storage", username); + UserModel user = session.userStorage().addUser(realm, username); + user.setEnabled(true); + user.setEmail(Sssd.getRawAttribute(sssdUser.get("mail"))); + user.setFirstName(Sssd.getRawAttribute(sssdUser.get("givenname"))); + user.setLastName(Sssd.getRawAttribute(sssdUser.get("sn"))); + for (String s : sssd.getUserGroups()) { + GroupModel group = KeycloakModelUtils.findGroupByPath(realm, "/" + s); + if (group == null) { + group = session.realms().createGroup(realm, s); + } + user.joinGroup(group); + } + user.setFederationLink(model.getId()); + return validateAndProxy(realm, user); + } + + @Override + public UserModel getUserByEmail(RealmModel realm, String email) { + return null; + } + + @Override + public List searchByAttributes(Map attributes, RealmModel realm, int maxResults) { + return Collections.emptyList(); + } + + @Override + public List getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) { + return Collections.emptyList(); + } + + @Override + public void preRemove(RealmModel realm) { + // complete We don't care about the realm being removed + } + + @Override + public void preRemove(RealmModel realm, RoleModel role) { + // complete we dont'care if a role is removed + + } + + @Override + public void preRemove(RealmModel realm, GroupModel group) { + // complete we dont'care if a role is removed + + } + + @Override + public boolean isValid(RealmModel realm, UserModel local) { + Map attributes = new Sssd(local.getUsername()).getUserAttributes(); + return Sssd.getRawAttribute(attributes.get("mail")).equalsIgnoreCase(local.getEmail()); + } + + @Override + public Set getSupportedCredentialTypes(UserModel user) { + return supportedCredentialTypes; + } + + @Override + public Set getSupportedCredentialTypes() { + return supportedCredentialTypes; + } + + @Override + public boolean validCredentials(RealmModel realm, UserModel user, List input) { + for (UserCredentialModel cred : input) { + if (cred.getType().equals(UserCredentialModel.PASSWORD)) { + PAMAuthenticator pam = factory.createPAMAuthenticator(user.getUsername(), cred.getValue()); + return (pam.authenticate() != null); + } + } + return false; + } + + @Override + public boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input) { + return validCredentials(realm, user, Arrays.asList(input)); + } + + @Override + public CredentialValidationOutput validCredentials(RealmModel realm, UserCredentialModel credential) { + return CredentialValidationOutput.failed(); + } + + @Override + public UserModel validateAndProxy(RealmModel realm, UserModel local) { + if (isValid(realm, local)) { + return new ReadonlySSSDUserModelDelegate(local, this); + } else { + return null; + } + } + + @Override + public boolean synchronizeRegistrations() { + return false; + } + + @Override + public UserModel register(RealmModel realm, UserModel user) { + throw new IllegalStateException("Registration not supported"); + } + + @Override + public boolean removeUser(RealmModel realm, UserModel user) { + return true; + } + + @Override + public void close() { + Sssd.disconnect(); + } +} diff --git a/federation/sssd/src/main/java/org/keycloak/federation/sssd/SSSDFederationProviderFactory.java b/federation/sssd/src/main/java/org/keycloak/federation/sssd/SSSDFederationProviderFactory.java new file mode 100755 index 0000000000..6a287a7ad8 --- /dev/null +++ b/federation/sssd/src/main/java/org/keycloak/federation/sssd/SSSDFederationProviderFactory.java @@ -0,0 +1,102 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.federation.sssd; + +import org.jboss.logging.Logger; +import org.keycloak.Config; +import org.keycloak.federation.sssd.impl.PAMAuthenticator; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.models.UserFederationProvider; +import org.keycloak.models.UserFederationProviderFactory; +import org.keycloak.models.UserFederationProviderModel; +import org.keycloak.models.UserFederationSyncResult; + +import java.util.Date; +import java.util.HashSet; +import java.util.Set; + +/** + * @author Bruno Oliveira + * @version $Revision: 1 $ + */ +public class SSSDFederationProviderFactory implements UserFederationProviderFactory { + + private static final String PROVIDER_NAME = "sssd"; + private static final Logger logger = Logger.getLogger(SSSDFederationProvider.class); + + static final Set configOptions = new HashSet(); + + @Override + public String getId() { + return PROVIDER_NAME; + } + + @Override + public UserFederationProvider getInstance(KeycloakSession session, UserFederationProviderModel model) { + return new SSSDFederationProvider(session, model, this); + } + + /** + * List the configuration options to render and display in the admin console's generic management page for this + * plugin + * + * @return + */ + @Override + public Set getConfigurationOptions() { + return configOptions; + } + + @Override + public UserFederationProvider create(KeycloakSession session) { + return null; + } + + @Override + public void init(Config.Scope config) { + + } + + @Override + public void postInit(KeycloakSessionFactory factory) { + + } + + @Override + public void close() { + + } + + @Override + public UserFederationSyncResult syncAllUsers(KeycloakSessionFactory sessionFactory, final String realmId, final UserFederationProviderModel model) { + logger.info("Sync users not supported for this provider"); + return UserFederationSyncResult.empty(); + } + + @Override + public UserFederationSyncResult syncChangedUsers(KeycloakSessionFactory sessionFactory, final String realmId, final UserFederationProviderModel model, Date lastSync) { + logger.info("Sync users not supported for this provider"); + return UserFederationSyncResult.empty(); + } + + protected PAMAuthenticator createPAMAuthenticator(String username, String... factors) { + return new PAMAuthenticator(username, factors); + } + +} diff --git a/federation/sssd/src/main/java/org/keycloak/federation/sssd/api/Sssd.java b/federation/sssd/src/main/java/org/keycloak/federation/sssd/api/Sssd.java new file mode 100644 index 0000000000..a5bb57a308 --- /dev/null +++ b/federation/sssd/src/main/java/org/keycloak/federation/sssd/api/Sssd.java @@ -0,0 +1,111 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.federation.sssd.api; + +import org.freedesktop.dbus.DBusConnection; +import org.freedesktop.dbus.Variant; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.sssd.infopipe.InfoPipe; +import org.freedesktop.sssd.infopipe.User; +import org.jboss.logging.Logger; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Vector; + +/** + * @author Bruno Oliveira + * @version $Revision: 1 $ + */ +public class Sssd { + + public static final String BUSNAME = "org.freedesktop.sssd.infopipe"; + + public static User user() { + return SingletonHolder.USER_OBJECT; + } + + public static InfoPipe infopipe() { + return SingletonHolder.INFOPIPE_OBJECT; + } + + public static void disconnect() { + SingletonHolder.DBUS_CONNECTION.disconnect(); + } + + private String username; + private static final Logger logger = Logger.getLogger(Sssd.class); + + private Sssd() { + } + + public Sssd(String username) { + this.username = username; + } + + private static final class SingletonHolder { + private static InfoPipe INFOPIPE_OBJECT; + private static User USER_OBJECT; + private static DBusConnection DBUS_CONNECTION; + + static { + try { + DBUS_CONNECTION = DBusConnection.getConnection(DBusConnection.SYSTEM); + INFOPIPE_OBJECT = DBUS_CONNECTION.getRemoteObject(BUSNAME, InfoPipe.OBJECTPATH, InfoPipe.class); + USER_OBJECT = DBUS_CONNECTION.getRemoteObject(BUSNAME, User.OBJECTPATH, User.class); + } catch (DBusException e) { + e.printStackTrace(); + } + } + } + + public static String getRawAttribute(Variant variant) { + if (variant != null) { + Vector value = (Vector) variant.getValue(); + if (value.size() >= 1) { + return value.get(0).toString(); + } + } + return null; + } + + public Map getUserAttributes() { + String[] attr = {"mail", "givenname", "sn", "telephoneNumber"}; + Map attributes = null; + try { + InfoPipe infoPipe = infopipe(); + attributes = infoPipe.getUserAttributes(username, Arrays.asList(attr)); + } catch (Exception e) { + logger.error("Failed to retrieve user's attributes from SSSD", e); + } + + return attributes; + } + + public List getUserGroups() { + List userGroups = null; + try { + InfoPipe infoPipe = Sssd.infopipe(); + userGroups = infoPipe.getUserGroups(username); + } catch (Exception e) { + logger.error("Failed to retrieve user's groups from SSSD", e); + } + return userGroups; + } +} diff --git a/federation/sssd/src/main/java/org/keycloak/federation/sssd/impl/PAMAuthenticator.java b/federation/sssd/src/main/java/org/keycloak/federation/sssd/impl/PAMAuthenticator.java new file mode 100644 index 0000000000..f9822014c0 --- /dev/null +++ b/federation/sssd/src/main/java/org/keycloak/federation/sssd/impl/PAMAuthenticator.java @@ -0,0 +1,62 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.federation.sssd.impl; + +import org.jboss.logging.Logger; +import org.jvnet.libpam.PAM; +import org.jvnet.libpam.PAMException; +import org.jvnet.libpam.UnixUser; + +/** + * PAMAuthenticator for Unix users + * + * @author Bruno Oliveira + * @version $Revision: 1 $ + */ +public class PAMAuthenticator { + + private static final String PAM_SERVICE = "keycloak"; + private static final Logger logger = Logger.getLogger(PAMAuthenticator.class); + private final String username; + private final String[] factors; + + public PAMAuthenticator(String username, String... factors) { + this.username = username; + this.factors = factors; + } + + /** + * Returns true if user was successfully authenticated against PAM + * + * @return UnixUser object if user was successfully authenticated + */ + public UnixUser authenticate() { + PAM pam = null; + UnixUser user = null; + try { + pam = new PAM(PAM_SERVICE); + user = pam.authenticate(username, factors); + } catch (PAMException e) { + logger.error("Authentication failed", e); + e.printStackTrace(); + } finally { + pam.dispose(); + } + return user; + } +} diff --git a/federation/sssd/src/main/resources/DBUS-JAVA-AUTHORS b/federation/sssd/src/main/resources/DBUS-JAVA-AUTHORS new file mode 100644 index 0000000000..9b7699ed2e --- /dev/null +++ b/federation/sssd/src/main/resources/DBUS-JAVA-AUTHORS @@ -0,0 +1,37 @@ +The D-Bus Java implementation was written by: + +Matthew Johnson + +Bug fixes/reports and other suggestions from: + +Remi Emonet +Simon McVittie +Dick Hollenbeck +Joshua Nichols +Ralf Kistner +Henrik Petander +Luigi Paioro +Roberto Francisco Arroyo Moreno +Steve Crane +Philippe Marschall +Daniel Machado +Anibal Sanchez +Jan Kümmel +Johannes Felten +Tom Walsh +Ed Wei +Sveinung Kvilhaugsvik +Hugues Moreau +Viktar Vauchkevich +Serkan Kaba +Adam Bennett +Frank Benoit +Gunnar Aastrand Grimnes + +The included Viewer application was originally written by: + +Peter Cox + +with patches from: + +Zsombor Gegesy diff --git a/federation/sssd/src/main/resources/DBUS-JAVA-LICENSE b/federation/sssd/src/main/resources/DBUS-JAVA-LICENSE new file mode 100644 index 0000000000..d651143606 --- /dev/null +++ b/federation/sssd/src/main/resources/DBUS-JAVA-LICENSE @@ -0,0 +1,680 @@ +The D-Bus Java implementation is licensed to you under your choice of the +Academic Free License version 2.1, or the GNU Lesser/Library General Public License +version 2. Both licenses are included here. Each source code file is marked +with the proper copyright information. + +The Academic Free License +v. 2.1 + +This Academic Free License (the "License") applies to any original work of +authorship (the "Original Work") whose owner (the "Licensor") has placed the +following notice immediately following the copyright notice for the Original +Work: + +Licensed under the Academic Free License version 2.1 + +1) Grant of Copyright License. Licensor hereby grants You a world-wide, +royalty-free, non-exclusive, perpetual, sublicenseable license to do the +following: + +a) to reproduce the Original Work in copies; + +b) to prepare derivative works ("Derivative Works") based upon the Original +Work; + +c) to distribute copies of the Original Work and Derivative Works to the +public; + +d) to perform the Original Work publicly; and + +e) to display the Original Work publicly. + +2) Grant of Patent License. Licensor hereby grants You a world-wide, +royalty-free, non-exclusive, perpetual, sublicenseable license, under patent +claims owned or controlled by the Licensor that are embodied in the Original +Work as furnished by the Licensor, to make, use, sell and offer for sale the +Original Work and Derivative Works. + +3) Grant of Source Code License. The term "Source Code" means the preferred +form of the Original Work for making modifications to it and all available +documentation describing how to modify the Original Work. Licensor hereby +agrees to provide a machine-readable copy of the Source Code of the Original +Work along with each copy of the Original Work that Licensor distributes. +Licensor reserves the right to satisfy this obligation by placing a +machine-readable copy of the Source Code in an information repository +reasonably calculated to permit inexpensive and convenient access by You for as +long as Licensor continues to distribute the Original Work, and by publishing +the address of that information repository in a notice immediately following +the copyright notice that applies to the Original Work. + +4) Exclusions From License Grant. Neither the names of Licensor, nor the names +of any contributors to the Original Work, nor any of their trademarks or +service marks, may be used to endorse or promote products derived from this +Original Work without express prior written permission of the Licensor. Nothing +in this License shall be deemed to grant any rights to trademarks, copyrights, +patents, trade secrets or any other intellectual property of Licensor except as +expressly stated herein. No patent license is granted to make, use, sell or +offer to sell embodiments of any patent claims other than the licensed claims +defined in Section 2. No right is granted to the trademarks of Licensor even if +such marks are included in the Original Work. Nothing in this License shall be +interpreted to prohibit Licensor from licensing under different terms from this +License any Original Work that Licensor otherwise would have a right to +license. + +5) This section intentionally omitted. + +6) Attribution Rights. You must retain, in the Source Code of any Derivative +Works that You create, all copyright, patent or trademark notices from the +Source Code of the Original Work, as well as any notices of licensing and any +descriptive text identified therein as an "Attribution Notice." You must cause +the Source Code for any Derivative Works that You create to carry a prominent +Attribution Notice reasonably calculated to inform recipients that You have +modified the Original Work. + +7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that +the copyright in and to the Original Work and the patent rights granted herein +by Licensor are owned by the Licensor or are sublicensed to You under the terms +of this License with the permission of the contributor(s) of those copyrights +and patent rights. Except as expressly stated in the immediately proceeding +sentence, the Original Work is provided under this License on an "AS IS" BASIS +and WITHOUT WARRANTY, either express or implied, including, without limitation, +the warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. +This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No +license to Original Work is granted hereunder except under this disclaimer. + +8) Limitation of Liability. Under no circumstances and under no legal theory, +whether in tort (including negligence), contract, or otherwise, shall the +Licensor be liable to any person for any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License +or the use of the Original Work including, without limitation, damages for loss +of goodwill, work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses. This limitation of liability shall not +apply to liability for death or personal injury resulting from Licensor's +negligence to the extent applicable law prohibits such limitation. Some +jurisdictions do not allow the exclusion or limitation of incidental or +consequential damages, so this exclusion and limitation may not apply to You. + +9) Acceptance and Termination. If You distribute copies of the Original Work or +a Derivative Work, You must make a reasonable effort under the circumstances to +obtain the express assent of recipients to the terms of this License. Nothing +else but this License (or another written agreement between Licensor and You) +grants You permission to create Derivative Works based upon the Original Work +or to exercise any of the rights granted in Section 1 herein, and any attempt +to do so except under the terms of this License (or another written agreement +between Licensor and You) is expressly prohibited by U.S. copyright law, the +equivalent laws of other countries, and by international treaty. Therefore, by +exercising any of the rights granted to You in Section 1 herein, You indicate +Your acceptance of this License and all of its terms and conditions. + +10) Termination for Patent Action. This License shall terminate automatically +and You may no longer exercise any of the rights granted to You by this License +as of the date You commence an action, including a cross-claim or counterclaim, +against Licensor or any licensee alleging that the Original Work infringes a +patent. This termination provision shall not apply for an action alleging +patent infringement by combinations of the Original Work with other software or +hardware. + +11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this +License may be brought only in the courts of a jurisdiction wherein the +Licensor resides or in which Licensor conducts its primary business, and under +the laws of that jurisdiction excluding its conflict-of-law provisions. The +application of the United Nations Convention on Contracts for the International +Sale of Goods is expressly excluded. Any use of the Original Work outside the +scope of this License or after its termination shall be subject to the +requirements and penalties of the U.S. Copyright Act, 17 U.S.C. § 101 et seq., +the equivalent laws of other countries, and international treaty. This section +shall survive the termination of this License. + +12) Attorneys Fees. In any action to enforce the terms of this License or +seeking damages relating thereto, the prevailing party shall be entitled to +recover its costs and expenses, including, without limitation, reasonable +attorneys' fees and costs incurred in connection with such action, including +any appeal of such action. This section shall survive the termination of this +License. + +13) Miscellaneous. This License represents the complete agreement concerning +the subject matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent necessary to +make it enforceable. + +14) Definition of "You" in This License. "You" throughout this License, whether +in upper or lower case, means an individual or a legal entity exercising rights +under, and complying with all of the terms of, this License. For legal +entities, "You" includes any entity that controls, is controlled by, or is +under common control with you. For purposes of this definition, "control" means +(i) the power, direct or indirect, to cause the direction or management of such +entity, whether by contract or otherwise, or (ii) ownership of fifty percent +(50%) or more of the outstanding shares, or (iii) beneficial ownership of such +entity. + +15) Right to Use. You may use the Original Work in all ways not otherwise +restricted or conditioned by this License or by law, and Licensor promises not +to interfere with or be responsible for such uses by You. + +This license is Copyright (C) 2003-2004 Lawrence E. Rosen. All rights reserved. +Permission is hereby granted to copy and distribute this license without +modification. This license may not be modified without the express written +permission of its copyright owner. + + +-- +END OF ACADEMIC FREE LICENSE. The following is intended to describe the +essential differences between the Academic Free License (AFL) version 1.0 and +other open source licenses: + +The Academic Free License is similar to the BSD, MIT, UoI/NCSA and Apache +licenses in many respects but it is intended to solve a few problems with those +licenses. + +* The AFL is written so as to make it clear what software is being +licensed (by the inclusion of a statement following the copyright notice in the +software). This way, the license functions better than a template license. The +BSD, MIT and UoI/NCSA licenses apply to unidentified software. + +* The AFL contains a complete copyright grant to the software. The BSD +and Apache licenses are vague and incomplete in that respect. + +* The AFL contains a complete patent grant to the software. The BSD, MIT, +UoI/NCSA and Apache licenses rely on an implied patent license and contain no +explicit patent grant. + +* The AFL makes it clear that no trademark rights are granted to the +licensor's trademarks. The Apache license contains such a provision, but the +BSD, MIT and UoI/NCSA licenses do not. + +* The AFL includes the warranty by the licensor that it either owns the +copyright or that it is distributing the software under a license. None of the +other licenses contain that warranty. All other warranties are disclaimed, as +is the case for the other licenses. + +* The AFL is itself copyrighted (with the right granted to copy and distribute +without modification). This ensures that the owner of the copyright to the +license will control changes. The Apache license contains a copyright notice, +but the BSD, MIT and UoI/NCSA licenses do not. + +-- +START OF GNU LIBRARY GENERAL PUBLIC LICENSE +-- + + GNU LIBRARY GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the library GPL. It is + numbered 2 because it goes with version 2 of the ordinary GPL.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Library General Public License, applies to some +specially designated Free Software Foundation software, and to any +other libraries whose authors decide to use it. You can use it for +your libraries, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if +you distribute copies of the library, or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link a program with the library, you must provide +complete object files to the recipients so that they can relink them +with the library, after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + Our method of protecting your rights has two steps: (1) copyright +the library, and (2) offer you this license which gives you legal +permission to copy, distribute and/or modify the library. + + Also, for each distributor's protection, we want to make certain +that everyone understands that there is no warranty for this free +library. If the library is modified by someone else and passed on, we +want its recipients to know that what they have is not the original +version, so that any problems introduced by others will not reflect on +the original authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that companies distributing free +software will individually obtain patent licenses, thus in effect +transforming the program into proprietary software. To prevent this, +we have made it clear that any patent must be licensed for everyone's +free use or not licensed at all. + + Most GNU software, including some libraries, is covered by the ordinary +GNU General Public License, which was designed for utility programs. This +license, the GNU Library General Public License, applies to certain +designated libraries. This license is quite different from the ordinary +one; be sure to read it in full, and don't assume that anything in it is +the same as in the ordinary license. + + The reason we have a separate public license for some libraries is that +they blur the distinction we usually make between modifying or adding to a +program and simply using it. Linking a program with a library, without +changing the library, is in some sense simply using the library, and is +analogous to running a utility program or application program. However, in +a textual and legal sense, the linked executable is a combined work, a +derivative of the original library, and the ordinary General Public License +treats it as such. + + Because of this blurred distinction, using the ordinary General +Public License for libraries did not effectively promote software +sharing, because most developers did not use the libraries. We +concluded that weaker conditions might promote sharing better. + + However, unrestricted linking of non-free programs would deprive the +users of those programs of all benefit from the free status of the +libraries themselves. This Library General Public License is intended to +permit developers of non-free programs to use free libraries, while +preserving your freedom as a user of such programs to change the free +libraries that are incorporated in them. (We have not seen how to achieve +this as regards changes in header files, but we have achieved it as regards +changes in the actual functions of the Library.) The hope is that this +will lead to faster development of free libraries. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, while the latter only +works together with the library. + + Note that it is possible for a library to be covered by the ordinary +General Public License rather than by this special one. + + GNU LIBRARY GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library which +contains a notice placed by the copyright holder or other authorized +party saying it may be distributed under the terms of this Library +General Public License (also called "this License"). Each licensee is +addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also compile or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + c) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + d) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the source code distributed need not include anything that is normally +distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Library General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/federation/sssd/src/main/resources/META-INF/services/org.keycloak.models.UserFederationProviderFactory b/federation/sssd/src/main/resources/META-INF/services/org.keycloak.models.UserFederationProviderFactory new file mode 100644 index 0000000000..d06ece1bec --- /dev/null +++ b/federation/sssd/src/main/resources/META-INF/services/org.keycloak.models.UserFederationProviderFactory @@ -0,0 +1,18 @@ +# +# Copyright 2016 Red Hat, Inc. and/or its affiliates +# and other contributors as indicated by the @author tags. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +org.keycloak.federation.sssd.SSSDFederationProviderFactory \ No newline at end of file diff --git a/federation/sssd/src/main/resources/en_US.properties b/federation/sssd/src/main/resources/en_US.properties new file mode 100644 index 0000000000..31bc06c941 --- /dev/null +++ b/federation/sssd/src/main/resources/en_US.properties @@ -0,0 +1,82 @@ +#java-format +notBasicType=" is not a basic type" +notObjectProvidedByProcess=" is not an object provided by this process." +arrayOutOfBounds="Array index out of bounds, paofs={0}, pabuf.length={1}, buf.length={2}." +arrayMustNotExceed="Arrays must not exceed " +asyncCallNoReply="Async call has not had a reply" +busAddressBlank="Bus address is blank" +busAddressInvalid="Bus address is invalid: " +cannotWrapNullInVariant="Can't wrap Null in a Variant" +cannotWrapMultiValuedInVariant="Can't wrap a multi-valued type in a Variant: " +cannotWrapNoTypesInVariant="Can't wrap multiple or no types in a Variant: " +cannotWrapUnqualifiedVariant="Can't wrap {0} in an unqualified Variant ({1})." +cannotResolveSessionBusAddress="Cannot Resolve Session Bus Address" +cannotWatchSignalsWellKnownBussName="Cannot watch for signals based on well known bus name as source, only unique names." +cannotCreateClassFromSignal="Could not create class from signal " +interfaceToCastNotFound="Could not find an interface to cast to" +interfaceNotAllowedOutsidePackage="DBusInterfaces cannot be declared outside a package" +interfaceMustBeDefinedPackage="DBusInterfaces must be defined in a package." +disconnected="Disconnected" +errorExecutingMethod="Error Executing Method {0}.{1}: {2}" +errorDeserializingMessage="Error deserializing message: number of parameters didn't match receiving signature" +nonExportableParameterizedType="Exporting non-exportable parameterized type " +nonExportableType "Exporting non-exportable type " +errorAddSignalParameters="Failed to add signal parameters: " +errorAuth="Failed to auth" +connectionFailure="Failed to connect to bus " +contructDBusTypeFailure="Failed to construct D-Bus type: " +constructOutgoingMethodCallFailure="Failed to construct outgoing method call: " +createProxyExportFailure="Failed to create proxy object for {0} exported by {1}. Reason: {2}" +createProxyFailure="Failed to create proxy object for {0}; reason: {1}." +parseDBusTypeSignatureFailure="Failed to parse DBus type signature: " +parseDBusSignatureFailure="Failed to parse DBus type signature: {0} ({1})." +dbusRegistrationFailure="Failed to register bus name" +deSerializationFailure="Failure in de-serializing message: " +introspectInterfaceExceedCharacters="Introspected interface name exceeds 255 characters. Cannot export objects of type " +introspectMethodExceedCharacters="Introspected method name exceeds 255 characters. Cannot export objects with method " +introspectSignalExceedCharacters="Introspected signal name exceeds 255 characters. Cannot export objects with signals of type " +invalidBusType="Invalid Bus Type: " +invalidCommand="Invalid Command " +invalidBusName="Invalid bus name" +nullBusName="Invalid bus name: null" +invalidObjectPath="Invalid object path: " +nullObjectPath="Invalid object path: null" +invalidTypeMatchRule="Invalid type for match rule: " +mapParameters="Map must have 2 parameters" +messageFailedSend="Message Failed to Send: " +messageTypeUnsupported="Message type {0} unsupported" +multiValuedArrayNotPermitted="Multi-valued array types not permitted" +missingObjectPath="Must Specify an Object Path" +missingDestinationPathFunction="Must specify destination, path and function name to MethodCalls." +missingErrorName="Must specify error name to Errors." +missingPathInterfaceSignal="Must specify object path, interface and signal name to Signals." +notReplyWithSpecifiedTime="No reply within specified time" +missingTransport="No transport present" +notDBusInterface="Not A DBus Interface" +notDBusSignal="Not A DBus Signal" +convertionTypeNotExpected="Not An Expected Convertion type from {0} to {1}" +notConnected="Not Connected" +notPrimitiveType="Not a primitive type" +invalidDBusCode="Not a valid D-Bus type code: " +invalidWrapperType="Not a wrapper type" +invalidArray="Not an array" +objectNotExportedNoRemoteSpecified="Not an object exported by this connection and no remote specified" +notEnoughElementsToCreateCustomObject="Not enough elements to create custom object from serialized data ({0} < {1})." +objectAlreadyExported="Object already exported" +arraySentAsNonPrimitive="Primative array being sent as non-primative array." +protocolVersionUnsupported="Protocol version {0} is unsupported" +cannotIntrospectReturnType="Return type of Object[] cannot be introspected properly" +mustImplementDeserializeMethod="Serializable classes must implement a deserialize method" +mustSerializeNativeDBusTypes="Serializable classes must serialize to native DBus types" +signalsMustBeMemberOfClass="Signals must be declared as a member of a class implementing DBusInterface which is the member of a package." +spuriousReply="Spurious reply. No message with the given serial id was awaiting a reply." +utf8NotSupported="System does not support UTF-8 encoding" +methodDoesNotExist="The method `{0}.{1}' does not exist on this object." +unconvertableType="Trying to marshall to unconvertable type (from {0} to {1})." +transportReturnedEOF="Underlying transport returned EOF" +waitingFor="Waiting for: " +invalidReturnType="Wrong return type (failed to de-serialize correct types: {0} )" +voidReturnType="Wrong return type (got void, expected a value)" +tupleReturnType="Wrong return type (not expecting Tuple)" +unknownAddress="unknown address type " +isNotBetween="{0} is not between {1} and {2}." diff --git a/federation/sssd/src/test/java/cx/ath/matthew/unix/testclient.java b/federation/sssd/src/test/java/cx/ath/matthew/unix/testclient.java new file mode 100644 index 0000000000..2acef18b84 --- /dev/null +++ b/federation/sssd/src/test/java/cx/ath/matthew/unix/testclient.java @@ -0,0 +1,49 @@ +/* + * Java Unix Sockets Library + * + * Copyright (c) Matthew Johnson 2005 + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * To Contact the author, please email src@matthew.ath.cx + * + */ + +package cx.ath.matthew.unix; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintWriter; + +public class testclient { + public static void main(String args[]) throws IOException { + UnixSocket s = new UnixSocket(new UnixSocketAddress("testsock", true)); + OutputStream os = s.getOutputStream(); + PrintWriter o = new PrintWriter(os); + BufferedReader r = new BufferedReader(new InputStreamReader(System.in)); + String l; + while (null != (l = r.readLine())) { + byte[] buf = (l + "\n").getBytes(); + os.write(buf, 0, buf.length); + } + s.close(); + } +} diff --git a/federation/sssd/src/test/java/org/freedesktop/dbus/ProfileStruct.java b/federation/sssd/src/test/java/org/freedesktop/dbus/ProfileStruct.java new file mode 100644 index 0000000000..b86c137e54 --- /dev/null +++ b/federation/sssd/src/test/java/org/freedesktop/dbus/ProfileStruct.java @@ -0,0 +1,26 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +public final class ProfileStruct extends Struct { + @Position(0) + public final String a; + @Position(1) + public final UInt32 b; + @Position(2) + public final long c; + + public ProfileStruct(String a, UInt32 b, long c) { + this.a = a; + this.b = b; + this.c = c; + } +} diff --git a/federation/sssd/src/test/java/org/freedesktop/dbus/Profiler.java b/federation/sssd/src/test/java/org/freedesktop/dbus/Profiler.java new file mode 100644 index 0000000000..cd549d5de8 --- /dev/null +++ b/federation/sssd/src/test/java/org/freedesktop/dbus/Profiler.java @@ -0,0 +1,44 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import org.freedesktop.dbus.exceptions.DBusException; + +import java.util.List; +import java.util.Map; + +public interface Profiler extends DBusInterface { + public class ProfileSignal extends DBusSignal { + public ProfileSignal(String path) throws DBusException { + super(path); + } + } + + public void array(int[] v); + + public void stringarray(String[] v); + + public void map(Map m); + + public void list(List l); + + public void bytes(byte[] b); + + public void struct(ProfileStruct ps); + + public void string(String s); + + public void NoReply(); + + public void Pong(); +} + + diff --git a/federation/sssd/src/test/java/org/freedesktop/dbus/ProfilerInstance.java b/federation/sssd/src/test/java/org/freedesktop/dbus/ProfilerInstance.java new file mode 100644 index 0000000000..530901f08b --- /dev/null +++ b/federation/sssd/src/test/java/org/freedesktop/dbus/ProfilerInstance.java @@ -0,0 +1,56 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import java.util.List; +import java.util.Map; + +public class ProfilerInstance implements Profiler { + public boolean isRemote() { + return false; + } + + public void array(int[] v) { + return; + } + + public void stringarray(String[] v) { + return; + } + + public void map(Map m) { + return; + } + + public void list(List l) { + return; + } + + public void bytes(byte[] b) { + return; + } + + public void struct(ProfileStruct ps) { + return; + } + + public void string(String s) { + return; + } + + public void NoReply() { + return; + } + + public void Pong() { + return; + } +} diff --git a/federation/sssd/src/test/java/org/freedesktop/dbus/TestException.java b/federation/sssd/src/test/java/org/freedesktop/dbus/TestException.java new file mode 100644 index 0000000000..98335c8a14 --- /dev/null +++ b/federation/sssd/src/test/java/org/freedesktop/dbus/TestException.java @@ -0,0 +1,22 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import org.freedesktop.DBus.Description; +import org.freedesktop.dbus.exceptions.DBusExecutionException; + +@Description("A test exception to throw over DBus") +@SuppressWarnings("serial") +public class TestException extends DBusExecutionException { + public TestException(String message) { + super(message); + } +} diff --git a/federation/sssd/src/test/java/org/freedesktop/dbus/TestNewInterface.java b/federation/sssd/src/test/java/org/freedesktop/dbus/TestNewInterface.java new file mode 100644 index 0000000000..63957878a7 --- /dev/null +++ b/federation/sssd/src/test/java/org/freedesktop/dbus/TestNewInterface.java @@ -0,0 +1,24 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import org.freedesktop.DBus.Description; + +/** + * A sample remote interface which exports one method. + */ +public interface TestNewInterface extends DBusInterface { + /** + * A simple method with no parameters which returns a String + */ + @Description("Simple test method") + public String getName(); +} diff --git a/federation/sssd/src/test/java/org/freedesktop/dbus/TestRemoteInterface.java b/federation/sssd/src/test/java/org/freedesktop/dbus/TestRemoteInterface.java new file mode 100644 index 0000000000..6fec592a2a --- /dev/null +++ b/federation/sssd/src/test/java/org/freedesktop/dbus/TestRemoteInterface.java @@ -0,0 +1,68 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import org.freedesktop.DBus.Description; +import org.freedesktop.DBus.Method; + +import java.lang.reflect.Type; +import java.util.List; +import java.util.Map; + +/** + * A sample remote interface which exports one method. + */ +public interface TestRemoteInterface extends DBusInterface { + /** + * A simple method with no parameters which returns a String + */ + @Description("Simple test method") + public String getName(); + + public String getNameAndThrow(); + + @Description("Test of nested maps") + public int frobnicate(List n, Map> m, T v); + + @Description("Throws a TestException when called") + public void throwme() throws TestException; + + @Description("Waits then doesn't return") + @Method.NoReply() + public void waitawhile(); + + @Description("Interface-overloaded method") + public int overload(); + + @Description("Testing Type Signatures") + public void sig(Type[] s); + + @Description("Testing object paths as Path objects") + public void newpathtest(Path p); + + @Description("Testing the float type") + public float testfloat(float[] f); + + @Description("Testing structs of structs") + public int[][] teststructstruct(TestStruct3 in); + + @Description("Regression test for #13291") + public void reg13291(byte[] as, byte[] bs); + + public Map svm(); + + /* test lots of things involving Path */ + public Path pathrv(Path a); + + public List pathlistrv(List a); + + public Map pathmaprv(Map a); +} diff --git a/federation/sssd/src/test/java/org/freedesktop/dbus/TestRemoteInterface2.java b/federation/sssd/src/test/java/org/freedesktop/dbus/TestRemoteInterface2.java new file mode 100644 index 0000000000..7039fe13ae --- /dev/null +++ b/federation/sssd/src/test/java/org/freedesktop/dbus/TestRemoteInterface2.java @@ -0,0 +1,62 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import org.freedesktop.DBus.Description; + +import java.util.List; + +@Description("An example remote interface") +@DBusInterfaceName("org.freedesktop.dbus.test.AlternateTestInterface") +public interface TestRemoteInterface2 extends DBusInterface { + @Description("Test multiple return values and implicit variant parameters.") + public TestTuple, Boolean> show(A in); + + @Description("Test passing structs and explicit variants, returning implicit variants") + public T dostuff(TestStruct foo); + + @Description("Test arrays, boxed arrays and lists.") + public List sampleArray(List l, Integer[] is, long[] ls); + + @Description("Test passing objects as object paths.") + public DBusInterface getThis(DBusInterface t); + + @Description("Test bools work") + @DBusMemberName("checkbool") + public boolean check(); + + @Description("Test Serializable Object") + public TestSerializable testSerializable(byte b, TestSerializable s, int i); + + @Description("Call another method on itself from within a call") + public String recursionTest(); + + @Description("Parameter-overloaded method (string)") + public int overload(String s); + + @Description("Parameter-overloaded method (byte)") + public int overload(byte b); + + @Description("Parameter-overloaded method (void)") + public int overload(); + + @Description("Nested List Check") + public List> checklist(List> lli); + + @Description("Get new objects as object paths.") + public TestNewInterface getNew(); + + @Description("Test Complex Variants") + public void complexv(Variant v); + + @Description("Test Introspect on a different interface") + public String Introspect(); +} diff --git a/federation/sssd/src/test/java/org/freedesktop/dbus/TestSerializable.java b/federation/sssd/src/test/java/org/freedesktop/dbus/TestSerializable.java new file mode 100644 index 0000000000..0a8275aae8 --- /dev/null +++ b/federation/sssd/src/test/java/org/freedesktop/dbus/TestSerializable.java @@ -0,0 +1,57 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import org.freedesktop.dbus.exceptions.DBusException; + +import java.util.List; +import java.util.Vector; + +public class TestSerializable implements DBusSerializable { + private int a; + private String b; + private Vector c; + + public TestSerializable(int a, A b, Vector c) { + this.a = a; + this.b = b.toString(); + this.c = c; + } + + public TestSerializable() { + } + + public void deserialize(int a, String b, List c) { + this.a = a; + this.b = b; + this.c = new Vector(c); + } + + public Object[] serialize() throws DBusException { + return new Object[]{a, b, c}; + } + + public int getInt() { + return a; + } + + public String getString() { + return b; + } + + public Vector getVector() { + return c; + } + + public String toString() { + return "TestSerializable{" + a + "," + b + "," + c + "}"; + } +} diff --git a/federation/sssd/src/test/java/org/freedesktop/dbus/TestSignalInterface.java b/federation/sssd/src/test/java/org/freedesktop/dbus/TestSignalInterface.java new file mode 100644 index 0000000000..518a5db47d --- /dev/null +++ b/federation/sssd/src/test/java/org/freedesktop/dbus/TestSignalInterface.java @@ -0,0 +1,89 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import org.freedesktop.DBus.Description; +import org.freedesktop.dbus.exceptions.DBusException; + +import java.util.List; +import java.util.Map; + +/** + * A sample signal with two parameters + */ +@Description("Test interface containing signals") +public interface TestSignalInterface extends DBusInterface { + @Description("Test basic signal") + public static class TestSignal extends DBusSignal { + public final String value; + public final UInt32 number; + + /** + * Create a signal. + */ + public TestSignal(String path, String value, UInt32 number) throws DBusException { + super(path, value, number); + this.value = value; + this.number = number; + } + } + + public static class StringSignal extends DBusSignal { + public final String aoeu; + + public StringSignal(String path, String aoeu) throws DBusException { + super(path, aoeu); + this.aoeu = aoeu; + } + } + + public static class EmptySignal extends DBusSignal { + public EmptySignal(String path) throws DBusException { + super(path); + } + } + + @Description("Test signal with arrays") + public static class TestArraySignal extends DBusSignal { + public final List v; + public final Map m; + + public TestArraySignal(String path, List v, Map m) throws DBusException { + super(path, v, m); + this.v = v; + this.m = m; + } + } + + @Description("Test signal sending an object path") + @DBusMemberName("TestSignalObject") + public static class TestObjectSignal extends DBusSignal { + public final DBusInterface otherpath; + + public TestObjectSignal(String path, DBusInterface otherpath) throws DBusException { + super(path, otherpath); + this.otherpath = otherpath; + } + } + + public static class TestPathSignal extends DBusSignal { + public final Path otherpath; + public final List pathlist; + public final Map pathmap; + + public TestPathSignal(String path, Path otherpath, List pathlist, Map pathmap) throws DBusException { + super(path, otherpath, pathlist, pathmap); + this.otherpath = otherpath; + this.pathlist = pathlist; + this.pathmap = pathmap; + } + } +} diff --git a/federation/sssd/src/test/java/org/freedesktop/dbus/TestSignalInterface2.java b/federation/sssd/src/test/java/org/freedesktop/dbus/TestSignalInterface2.java new file mode 100644 index 0000000000..9324b40b12 --- /dev/null +++ b/federation/sssd/src/test/java/org/freedesktop/dbus/TestSignalInterface2.java @@ -0,0 +1,36 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import org.freedesktop.DBus.Description; +import org.freedesktop.dbus.exceptions.DBusException; + +/** + * A sample signal with two parameters + */ +@Description("Test interface containing signals") +@DBusInterfaceName("some.other.interface.Name") +public interface TestSignalInterface2 extends DBusInterface { + @Description("Test basic signal") + public static class TestRenamedSignal extends DBusSignal { + public final String value; + public final UInt32 number; + + /** + * Create a signal. + */ + public TestRenamedSignal(String path, String value, UInt32 number) throws DBusException { + super(path, value, number); + this.value = value; + this.number = number; + } + } +} diff --git a/federation/sssd/src/test/java/org/freedesktop/dbus/TestStruct.java b/federation/sssd/src/test/java/org/freedesktop/dbus/TestStruct.java new file mode 100644 index 0000000000..09f42fca89 --- /dev/null +++ b/federation/sssd/src/test/java/org/freedesktop/dbus/TestStruct.java @@ -0,0 +1,26 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +public final class TestStruct extends Struct { + @Position(0) + public final String a; + @Position(1) + public final UInt32 b; + @Position(2) + public final Variant c; + + public TestStruct(String a, UInt32 b, Variant c) { + this.a = a; + this.b = b; + this.c = c; + } +} diff --git a/federation/sssd/src/test/java/org/freedesktop/dbus/TestStruct2.java b/federation/sssd/src/test/java/org/freedesktop/dbus/TestStruct2.java new file mode 100644 index 0000000000..efd0d878b2 --- /dev/null +++ b/federation/sssd/src/test/java/org/freedesktop/dbus/TestStruct2.java @@ -0,0 +1,27 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import org.freedesktop.dbus.exceptions.DBusException; + +import java.util.List; + +public final class TestStruct2 extends Struct { + @Position(0) + public final List a; + @Position(1) + public final Variant b; + + public TestStruct2(List a, Variant b) throws DBusException { + this.a = a; + this.b = b; + } +} diff --git a/federation/sssd/src/test/java/org/freedesktop/dbus/TestStruct3.java b/federation/sssd/src/test/java/org/freedesktop/dbus/TestStruct3.java new file mode 100644 index 0000000000..d457053630 --- /dev/null +++ b/federation/sssd/src/test/java/org/freedesktop/dbus/TestStruct3.java @@ -0,0 +1,27 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import org.freedesktop.dbus.exceptions.DBusException; + +import java.util.List; + +public final class TestStruct3 extends Struct { + @Position(0) + public final TestStruct2 a; + @Position(1) + public final List> b; + + public TestStruct3(TestStruct2 a, List> b) throws DBusException { + this.a = a; + this.b = b; + } +} diff --git a/federation/sssd/src/test/java/org/freedesktop/dbus/TestTuple.java b/federation/sssd/src/test/java/org/freedesktop/dbus/TestTuple.java new file mode 100644 index 0000000000..4adb0d8643 --- /dev/null +++ b/federation/sssd/src/test/java/org/freedesktop/dbus/TestTuple.java @@ -0,0 +1,26 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +public final class TestTuple extends Tuple { + @Position(0) + public final A a; + @Position(1) + public final B b; + @Position(2) + public final C c; + + public TestTuple(A a, B b, C c) { + this.a = a; + this.b = b; + this.c = c; + } +} diff --git a/federation/sssd/src/test/java/org/freedesktop/dbus/TwoPartInterface.java b/federation/sssd/src/test/java/org/freedesktop/dbus/TwoPartInterface.java new file mode 100644 index 0000000000..242dcb2fce --- /dev/null +++ b/federation/sssd/src/test/java/org/freedesktop/dbus/TwoPartInterface.java @@ -0,0 +1,26 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import org.freedesktop.dbus.exceptions.DBusException; + +public interface TwoPartInterface extends DBusInterface { + public TwoPartObject getNew(); + + public class TwoPartSignal extends DBusSignal { + public final TwoPartObject o; + + public TwoPartSignal(String path, TwoPartObject o) throws DBusException { + super(path, o); + this.o = o; + } + } +} diff --git a/federation/sssd/src/test/java/org/freedesktop/dbus/TwoPartObject.java b/federation/sssd/src/test/java/org/freedesktop/dbus/TwoPartObject.java new file mode 100644 index 0000000000..8f03bbc7ff --- /dev/null +++ b/federation/sssd/src/test/java/org/freedesktop/dbus/TwoPartObject.java @@ -0,0 +1,15 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +public interface TwoPartObject extends DBusInterface { + public String getName(); +} diff --git a/federation/sssd/src/test/java/org/freedesktop/dbus/cross_test_client.java b/federation/sssd/src/test/java/org/freedesktop/dbus/cross_test_client.java new file mode 100644 index 0000000000..b71033dbf0 --- /dev/null +++ b/federation/sssd/src/test/java/org/freedesktop/dbus/cross_test_client.java @@ -0,0 +1,511 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import cx.ath.matthew.debug.Debug; +import org.freedesktop.DBus; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.exceptions.DBusExecutionException; +import org.freedesktop.dbus.types.DBusMapType; + +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import java.util.TreeSet; +import java.util.Vector; + +public class cross_test_client implements DBus.Binding.TestClient, DBusSigHandler { + private DBusConnection conn; + private static Set passed = new TreeSet(); + private static Map> failed = new HashMap>(); + private static cross_test_client ctc; + + static { + List l = new Vector(); + l.add("Signal never arrived"); + failed.put("org.freedesktop.DBus.Binding.TestSignals.Triggered", l); + l = new Vector(); + l.add("Method never called"); + failed.put("org.freedesktop.DBus.Binding.TestClient.Response", l); + } + + public cross_test_client(DBusConnection conn) { + this.conn = conn; + } + + public boolean isRemote() { + return false; + } + + public void handle(DBus.Binding.TestSignals.Triggered t) { + failed.remove("org.freedesktop.DBus.Binding.TestSignals.Triggered"); + if (new UInt64(21389479283L).equals(t.a) && "/Test".equals(t.getPath())) + pass("org.freedesktop.DBus.Binding.TestSignals.Triggered"); + else if (!new UInt64(21389479283L).equals(t.a)) + fail("org.freedesktop.DBus.Binding.TestSignals.Triggered", "Incorrect signal content; expected 21389479283 got " + t.a); + else if (!"/Test".equals(t.getPath())) + fail("org.freedesktop.DBus.Binding.TestSignals.Triggered", "Incorrect signal source object; expected /Test got " + t.getPath()); + } + + public void Response(UInt16 a, double b) { + failed.remove("org.freedesktop.DBus.Binding.TestClient.Response"); + if (a.equals(new UInt16(15)) && (b == 12.5)) + pass("org.freedesktop.DBus.Binding.TestClient.Response"); + else + fail("org.freedesktop.DBus.Binding.TestClient.Response", "Incorrect parameters; expected 15, 12.5 got " + a + ", " + b); + } + + public static void pass(String test) { + passed.add(test.replaceAll("[$]", "")); + } + + public static void fail(String test, String reason) { + test = test.replaceAll("[$]", ""); + List reasons = failed.get(test); + if (null == reasons) { + reasons = new Vector(); + failed.put(test, reasons); + } + reasons.add(reason); + } + + @SuppressWarnings("unchecked") + public static void test(Class iface, Object proxy, String method, Object rv, Object... parameters) { + try { + Method[] ms = iface.getMethods(); + Method m = null; + for (Method t : ms) { + if (t.getName().equals(method)) + m = t; + } + Object o = m.invoke(proxy, parameters); + + String msg = "Incorrect return value; sent ( "; + if (null != parameters) + for (Object po : parameters) + if (null != po) + msg += collapseArray(po) + ","; + msg = msg.replaceAll(".$", ");"); + msg += " expected " + collapseArray(rv) + " got " + collapseArray(o); + + if (null != rv && rv.getClass().isArray()) { + compareArray(iface.getName() + "" + method, rv, o); + } else if (rv instanceof Map) { + if (o instanceof Map) { + Map a = (Map) o; + Map b = (Map) rv; + if (a.keySet().size() != b.keySet().size()) { + fail(iface.getName() + "" + method, msg); + } else for (Object k : a.keySet()) + if (a.get(k) instanceof List) { + if (b.get(k) instanceof List) + if (setCompareLists((List) a.get(k), (List) b.get(k))) + ; + else + fail(iface.getName() + "" + method, msg); + else + fail(iface.getName() + "" + method, msg); + } else if (!a.get(k).equals(b.get(k))) { + fail(iface.getName() + "" + method, msg); + return; + } + pass(iface.getName() + "" + method); + } else + fail(iface.getName() + "" + method, msg); + } else { + if (o == rv || (o != null && o.equals(rv))) + pass(iface.getName() + "" + method); + else + fail(iface.getName() + "" + method, msg); + } + } catch (DBusExecutionException DBEe) { + DBEe.printStackTrace(); + fail(iface.getName() + "" + method, "Error occurred during execution: " + DBEe.getClass().getName() + " " + DBEe.getMessage()); + } catch (InvocationTargetException ITe) { + ITe.printStackTrace(); + fail(iface.getName() + "" + method, "Error occurred during execution: " + ITe.getCause().getClass().getName() + " " + ITe.getCause().getMessage()); + } catch (Exception e) { + e.printStackTrace(); + fail(iface.getName() + "" + method, "Error occurred during execution: " + e.getClass().getName() + " " + e.getMessage()); + } + } + + @SuppressWarnings("unchecked") + public static String collapseArray(Object array) { + if (null == array) return "null"; + if (array.getClass().isArray()) { + String s = "{ "; + for (int i = 0; i < Array.getLength(array); i++) + s += collapseArray(Array.get(array, i)) + ","; + s = s.replaceAll(".$", " }"); + return s; + } else if (array instanceof List) { + String s = "{ "; + for (Object o : (List) array) + s += collapseArray(o) + ","; + s = s.replaceAll(".$", " }"); + return s; + } else if (array instanceof Map) { + String s = "{ "; + for (Object o : ((Map) array).keySet()) + s += collapseArray(o) + " => " + collapseArray(((Map) array).get(o)) + ","; + s = s.replaceAll(".$", " }"); + return s; + } else return array.toString(); + } + + public static boolean setCompareLists(List a, List b) { + if (a.size() != b.size()) return false; + for (Object v : a) + if (!b.contains(v)) return false; + return true; + } + + @SuppressWarnings("unchecked") + public static List> PrimitizeRecurse(Object a, Type t) { + List> vs = new Vector>(); + if (t instanceof ParameterizedType) { + Class c = (Class) ((ParameterizedType) t).getRawType(); + if (List.class.isAssignableFrom(c)) { + Object os; + if (a instanceof List) + os = ((List) a).toArray(); + else + os = a; + Type[] ts = ((ParameterizedType) t).getActualTypeArguments(); + for (int i = 0; i < Array.getLength(os); i++) + vs.addAll(PrimitizeRecurse(Array.get(os, i), ts[0])); + } else if (Map.class.isAssignableFrom(c)) { + Object[] os = ((Map) a).keySet().toArray(); + Object[] ks = ((Map) a).values().toArray(); + Type[] ts = ((ParameterizedType) t).getActualTypeArguments(); + for (int i = 0; i < ks.length; i++) + vs.addAll(PrimitizeRecurse(ks[i], ts[0])); + for (int i = 0; i < os.length; i++) + vs.addAll(PrimitizeRecurse(os[i], ts[1])); + } else if (Struct.class.isAssignableFrom(c)) { + Object[] os = ((Struct) a).getParameters(); + Type[] ts = ((ParameterizedType) t).getActualTypeArguments(); + for (int i = 0; i < os.length; i++) + vs.addAll(PrimitizeRecurse(os[i], ts[i])); + + } else if (Variant.class.isAssignableFrom(c)) { + vs.addAll(PrimitizeRecurse(((Variant) a).getValue(), ((Variant) a).getType())); + } + } else if (Variant.class.isAssignableFrom((Class) t)) + vs.addAll(PrimitizeRecurse(((Variant) a).getValue(), ((Variant) a).getType())); + else if (t instanceof Class && ((Class) t).isArray()) { + Type t2 = ((Class) t).getComponentType(); + for (int i = 0; i < Array.getLength(a); i++) + vs.addAll(PrimitizeRecurse(Array.get(a, i), t2)); + } else vs.add(new Variant(a)); + + return vs; + } + + @SuppressWarnings("unchecked") + public static List> Primitize(Variant a) { + return PrimitizeRecurse(a.getValue(), a.getType()); + } + + @SuppressWarnings("unchecked") + public static void primitizeTest(DBus.Binding.Tests tests, Object input) { + Variant in = new Variant(input); + List> vs = Primitize(in); + List> res; + + try { + + res = tests.Primitize(in); + if (setCompareLists(res, vs)) + pass("org.freedesktop.DBus.Binding.Tests.Primitize"); + else + fail("org.freedesktop.DBus.Binding.Tests.Primitize", "Wrong Return Value; expected " + collapseArray(vs) + " got " + collapseArray(res)); + + } catch (Exception e) { + if (Debug.debug) Debug.print(e); + fail("org.freedesktop.DBus.Binding.Tests.Primitize", "Exception occurred during test: (" + e.getClass().getName() + ") " + e.getMessage()); + } + } + + public static void doTests(DBus.Peer peer, DBus.Introspectable intro, DBus.Introspectable rootintro, DBus.Binding.Tests tests, DBus.Binding.SingleTests singletests) { + Random r = new Random(); + int i; + test(DBus.Peer.class, peer, "Ping", null); + + try { + if (intro.Introspect().startsWith("(new Integer(1)), new Variant(new Integer(1))); + test(DBus.Binding.Tests.class, tests, "Identity", new Variant("Hello"), new Variant("Hello")); + + test(DBus.Binding.Tests.class, tests, "IdentityBool", false, false); + test(DBus.Binding.Tests.class, tests, "IdentityBool", true, true); + + test(DBus.Binding.Tests.class, tests, "Invert", false, true); + test(DBus.Binding.Tests.class, tests, "Invert", true, false); + + test(DBus.Binding.Tests.class, tests, "IdentityByte", (byte) 0, (byte) 0); + test(DBus.Binding.Tests.class, tests, "IdentityByte", (byte) 1, (byte) 1); + test(DBus.Binding.Tests.class, tests, "IdentityByte", (byte) -1, (byte) -1); + test(DBus.Binding.Tests.class, tests, "IdentityByte", Byte.MAX_VALUE, Byte.MAX_VALUE); + test(DBus.Binding.Tests.class, tests, "IdentityByte", Byte.MIN_VALUE, Byte.MIN_VALUE); + i = r.nextInt(); + test(DBus.Binding.Tests.class, tests, "IdentityByte", (byte) i, (byte) i); + + test(DBus.Binding.Tests.class, tests, "IdentityInt16", (short) 0, (short) 0); + test(DBus.Binding.Tests.class, tests, "IdentityInt16", (short) 1, (short) 1); + test(DBus.Binding.Tests.class, tests, "IdentityInt16", (short) -1, (short) -1); + test(DBus.Binding.Tests.class, tests, "IdentityInt16", Short.MAX_VALUE, Short.MAX_VALUE); + test(DBus.Binding.Tests.class, tests, "IdentityInt16", Short.MIN_VALUE, Short.MIN_VALUE); + i = r.nextInt(); + test(DBus.Binding.Tests.class, tests, "IdentityInt16", (short) i, (short) i); + + test(DBus.Binding.Tests.class, tests, "IdentityInt32", 0, 0); + test(DBus.Binding.Tests.class, tests, "IdentityInt32", 1, 1); + test(DBus.Binding.Tests.class, tests, "IdentityInt32", -1, -1); + test(DBus.Binding.Tests.class, tests, "IdentityInt32", Integer.MAX_VALUE, Integer.MAX_VALUE); + test(DBus.Binding.Tests.class, tests, "IdentityInt32", Integer.MIN_VALUE, Integer.MIN_VALUE); + i = r.nextInt(); + test(DBus.Binding.Tests.class, tests, "IdentityInt32", i, i); + + + test(DBus.Binding.Tests.class, tests, "IdentityInt64", (long) 0, (long) 0); + test(DBus.Binding.Tests.class, tests, "IdentityInt64", (long) 1, (long) 1); + test(DBus.Binding.Tests.class, tests, "IdentityInt64", (long) -1, (long) -1); + test(DBus.Binding.Tests.class, tests, "IdentityInt64", Long.MAX_VALUE, Long.MAX_VALUE); + test(DBus.Binding.Tests.class, tests, "IdentityInt64", Long.MIN_VALUE, Long.MIN_VALUE); + i = r.nextInt(); + test(DBus.Binding.Tests.class, tests, "IdentityInt64", (long) i, (long) i); + + test(DBus.Binding.Tests.class, tests, "IdentityUInt16", new UInt16(0), new UInt16(0)); + test(DBus.Binding.Tests.class, tests, "IdentityUInt16", new UInt16(1), new UInt16(1)); + test(DBus.Binding.Tests.class, tests, "IdentityUInt16", new UInt16(UInt16.MAX_VALUE), new UInt16(UInt16.MAX_VALUE)); + test(DBus.Binding.Tests.class, tests, "IdentityUInt16", new UInt16(UInt16.MIN_VALUE), new UInt16(UInt16.MIN_VALUE)); + i = r.nextInt(); + i = i > 0 ? i : -i; + test(DBus.Binding.Tests.class, tests, "IdentityUInt16", new UInt16(i % UInt16.MAX_VALUE), new UInt16(i % UInt16.MAX_VALUE)); + + test(DBus.Binding.Tests.class, tests, "IdentityUInt32", new UInt32(0), new UInt32(0)); + test(DBus.Binding.Tests.class, tests, "IdentityUInt32", new UInt32(1), new UInt32(1)); + test(DBus.Binding.Tests.class, tests, "IdentityUInt32", new UInt32(UInt32.MAX_VALUE), new UInt32(UInt32.MAX_VALUE)); + test(DBus.Binding.Tests.class, tests, "IdentityUInt32", new UInt32(UInt32.MIN_VALUE), new UInt32(UInt32.MIN_VALUE)); + i = r.nextInt(); + i = i > 0 ? i : -i; + test(DBus.Binding.Tests.class, tests, "IdentityUInt32", new UInt32(i % UInt32.MAX_VALUE), new UInt32(i % UInt32.MAX_VALUE)); + + test(DBus.Binding.Tests.class, tests, "IdentityUInt64", new UInt64(0), new UInt64(0)); + test(DBus.Binding.Tests.class, tests, "IdentityUInt64", new UInt64(1), new UInt64(1)); + test(DBus.Binding.Tests.class, tests, "IdentityUInt64", new UInt64(UInt64.MAX_LONG_VALUE), new UInt64(UInt64.MAX_LONG_VALUE)); + test(DBus.Binding.Tests.class, tests, "IdentityUInt64", new UInt64(UInt64.MAX_BIG_VALUE), new UInt64(UInt64.MAX_BIG_VALUE)); + test(DBus.Binding.Tests.class, tests, "IdentityUInt64", new UInt64(UInt64.MIN_VALUE), new UInt64(UInt64.MIN_VALUE)); + i = r.nextInt(); + i = i > 0 ? i : -i; + test(DBus.Binding.Tests.class, tests, "IdentityUInt64", new UInt64(i % UInt64.MAX_LONG_VALUE), new UInt64(i % UInt64.MAX_LONG_VALUE)); + + test(DBus.Binding.Tests.class, tests, "IdentityDouble", 0.0, 0.0); + test(DBus.Binding.Tests.class, tests, "IdentityDouble", 1.0, 1.0); + test(DBus.Binding.Tests.class, tests, "IdentityDouble", -1.0, -1.0); + test(DBus.Binding.Tests.class, tests, "IdentityDouble", Double.MAX_VALUE, Double.MAX_VALUE); + test(DBus.Binding.Tests.class, tests, "IdentityDouble", Double.MIN_VALUE, Double.MIN_VALUE); + i = r.nextInt(); + test(DBus.Binding.Tests.class, tests, "IdentityDouble", (double) i, (double) i); + + test(DBus.Binding.Tests.class, tests, "IdentityString", "", ""); + test(DBus.Binding.Tests.class, tests, "IdentityString", "The Quick Brown Fox Jumped Over The Lazy Dog", "The Quick Brown Fox Jumped Over The Lazy Dog"); + test(DBus.Binding.Tests.class, tests, "IdentityString", "ひらがなゲーム - かなぶん", "ひらがなゲーム - かなぶん"); + + testArray(DBus.Binding.Tests.class, tests, "IdentityBoolArray", Boolean.TYPE, null); + testArray(DBus.Binding.Tests.class, tests, "IdentityByteArray", Byte.TYPE, null); + testArray(DBus.Binding.Tests.class, tests, "IdentityInt16Array", Short.TYPE, null); + testArray(DBus.Binding.Tests.class, tests, "IdentityInt32Array", Integer.TYPE, null); + testArray(DBus.Binding.Tests.class, tests, "IdentityInt64Array", Long.TYPE, null); + testArray(DBus.Binding.Tests.class, tests, "IdentityDoubleArray", Double.TYPE, null); + + testArray(DBus.Binding.Tests.class, tests, "IdentityArray", Variant.class, new Variant("aoeu")); + testArray(DBus.Binding.Tests.class, tests, "IdentityUInt16Array", UInt16.class, new UInt16(12)); + testArray(DBus.Binding.Tests.class, tests, "IdentityUInt32Array", UInt32.class, new UInt32(190)); + testArray(DBus.Binding.Tests.class, tests, "IdentityUInt64Array", UInt64.class, new UInt64(103948)); + testArray(DBus.Binding.Tests.class, tests, "IdentityStringArray", String.class, "asdf"); + + int[] is = new int[0]; + test(DBus.Binding.Tests.class, tests, "Sum", 0L, is); + r = new Random(); + int len = (r.nextInt() % 100) + 15; + len = (len < 0 ? -len : len) + 15; + is = new int[len]; + long result = 0; + for (i = 0; i < len; i++) { + is[i] = r.nextInt(); + result += is[i]; + } + test(DBus.Binding.Tests.class, tests, "Sum", result, is); + + byte[] bs = new byte[0]; + test(DBus.Binding.SingleTests.class, singletests, "Sum", new UInt32(0), bs); + len = (r.nextInt() % 100); + len = (len < 0 ? -len : len) + 15; + bs = new byte[len]; + int res = 0; + for (i = 0; i < len; i++) { + bs[i] = (byte) r.nextInt(); + res += (bs[i] < 0 ? bs[i] + 256 : bs[i]); + } + test(DBus.Binding.SingleTests.class, singletests, "Sum", new UInt32(res % (UInt32.MAX_VALUE + 1)), bs); + + test(DBus.Binding.Tests.class, tests, "DeStruct", new DBus.Binding.Triplet("hi", new UInt32(12), new Short((short) 99)), new DBus.Binding.TestStruct("hi", new UInt32(12), new Short((short) 99))); + + Map in = new HashMap(); + Map> out = new HashMap>(); + test(DBus.Binding.Tests.class, tests, "InvertMapping", out, in); + + in.put("hi", "there"); + in.put("to", "there"); + in.put("from", "here"); + in.put("in", "out"); + List l = new Vector(); + l.add("hi"); + l.add("to"); + out.put("there", l); + l = new Vector(); + l.add("from"); + out.put("here", l); + l = new Vector(); + l.add("in"); + out.put("out", l); + test(DBus.Binding.Tests.class, tests, "InvertMapping", out, in); + + primitizeTest(tests, new Integer(1)); + primitizeTest(tests, + new Variant>>>( + new Variant>>( + new Variant>( + new Variant("Hi"))))); + primitizeTest(tests, new Variant>(in, new DBusMapType(String.class, String.class))); + + test(DBus.Binding.Tests.class, tests, "Trigger", null, "/Test", new UInt64(21389479283L)); + + try { + ctc.conn.sendSignal(new DBus.Binding.TestClient.Trigger("/Test", new UInt16(15), 12.5)); + } catch (DBusException DBe) { + if (Debug.debug) Debug.print(DBe); + throw new DBusExecutionException(DBe.getMessage()); + } + + try { + Thread.sleep(10000); + } catch (InterruptedException Ie) { + } + + test(DBus.Binding.Tests.class, tests, "Exit", null); + } + + public static void testArray(Class iface, Object proxy, String method, Class arrayType, Object content) { + Object array = Array.newInstance(arrayType, 0); + test(iface, proxy, method, array, array); + Random r = new Random(); + int l = (r.nextInt() % 100); + array = Array.newInstance(arrayType, (l < 0 ? -l : l) + 15); + if (null != content) + Arrays.fill((Object[]) array, content); + test(iface, proxy, method, array, array); + } + + public static void compareArray(String test, Object a, Object b) { + if (!a.getClass().equals(b.getClass())) { + fail(test, "Incorrect return type; expected " + a.getClass() + " got " + b.getClass()); + return; + } + boolean pass = false; + + if (a instanceof Object[]) + pass = Arrays.equals((Object[]) a, (Object[]) b); + else if (a instanceof byte[]) + pass = Arrays.equals((byte[]) a, (byte[]) b); + else if (a instanceof boolean[]) + pass = Arrays.equals((boolean[]) a, (boolean[]) b); + else if (a instanceof int[]) + pass = Arrays.equals((int[]) a, (int[]) b); + else if (a instanceof short[]) + pass = Arrays.equals((short[]) a, (short[]) b); + else if (a instanceof long[]) + pass = Arrays.equals((long[]) a, (long[]) b); + else if (a instanceof double[]) + pass = Arrays.equals((double[]) a, (double[]) b); + + if (pass) + pass(test); + else { + String s = "Incorrect return value; expected "; + s += collapseArray(a); + s += " got "; + s += collapseArray(b); + fail(test, s); + } + } + + public static void main(String[] args) { + try { + /* init */ + DBusConnection conn = DBusConnection.getConnection(DBusConnection.SESSION); + ctc = new cross_test_client(conn); + conn.exportObject("/Test", ctc); + conn.addSigHandler(DBus.Binding.TestSignals.Triggered.class, ctc); + DBus.Binding.Tests tests = conn.getRemoteObject("org.freedesktop.DBus.Binding.TestServer", "/Test", DBus.Binding.Tests.class); + DBus.Binding.SingleTests singletests = conn.getRemoteObject("org.freedesktop.DBus.Binding.TestServer", "/Test", DBus.Binding.SingleTests.class); + DBus.Peer peer = conn.getRemoteObject("org.freedesktop.DBus.Binding.TestServer", "/Test", DBus.Peer.class); + DBus.Introspectable intro = conn.getRemoteObject("org.freedesktop.DBus.Binding.TestServer", "/Test", DBus.Introspectable.class); + + DBus.Introspectable rootintro = conn.getRemoteObject("org.freedesktop.DBus.Binding.TestServer", "/", DBus.Introspectable.class); + + doTests(peer, intro, rootintro, tests, singletests); + + /* report results */ + for (String s : passed) + System.out.println(s + " pass"); + int i = 1; + for (String s : failed.keySet()) + for (String r : failed.get(s)) { + System.out.println(s + " fail " + i); + System.out.println("report " + i + ": " + r); + i++; + } + + conn.disconnect(); + } catch (DBusException DBe) { + DBe.printStackTrace(); + System.exit(1); + } + } +} diff --git a/federation/sssd/src/test/java/org/freedesktop/dbus/cross_test_server.java b/federation/sssd/src/test/java/org/freedesktop/dbus/cross_test_server.java new file mode 100644 index 0000000000..61bba4a6d5 --- /dev/null +++ b/federation/sssd/src/test/java/org/freedesktop/dbus/cross_test_server.java @@ -0,0 +1,342 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import org.freedesktop.DBus; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.exceptions.DBusExecutionException; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.Vector; + +public class cross_test_server implements DBus.Binding.Tests, DBus.Binding.SingleTests, DBusSigHandler { + private DBusConnection conn; + boolean run = true; + private Set done = new TreeSet(); + private Set notdone = new TreeSet(); + + { + notdone.add("org.freedesktop.DBus.Binding.Tests.Identity"); + notdone.add("org.freedesktop.DBus.Binding.Tests.IdentityByte"); + notdone.add("org.freedesktop.DBus.Binding.Tests.IdentityBool"); + notdone.add("org.freedesktop.DBus.Binding.Tests.IdentityInt16"); + notdone.add("org.freedesktop.DBus.Binding.Tests.IdentityUInt16"); + notdone.add("org.freedesktop.DBus.Binding.Tests.IdentityInt32"); + notdone.add("org.freedesktop.DBus.Binding.Tests.IdentityUInt32"); + notdone.add("org.freedesktop.DBus.Binding.Tests.IdentityInt64"); + notdone.add("org.freedesktop.DBus.Binding.Tests.IdentityUInt64"); + notdone.add("org.freedesktop.DBus.Binding.Tests.IdentityDouble"); + notdone.add("org.freedesktop.DBus.Binding.Tests.IdentityString"); + notdone.add("org.freedesktop.DBus.Binding.Tests.IdentityArray"); + notdone.add("org.freedesktop.DBus.Binding.Tests.IdentityByteArray"); + notdone.add("org.freedesktop.DBus.Binding.Tests.IdentityBoolArray"); + notdone.add("org.freedesktop.DBus.Binding.Tests.IdentityInt16Array"); + notdone.add("org.freedesktop.DBus.Binding.Tests.IdentityUInt16Array"); + notdone.add("org.freedesktop.DBus.Binding.Tests.IdentityInt32Array"); + notdone.add("org.freedesktop.DBus.Binding.Tests.IdentityUInt32Array"); + notdone.add("org.freedesktop.DBus.Binding.Tests.IdentityInt64Array"); + notdone.add("org.freedesktop.DBus.Binding.Tests.IdentityUInt64Array"); + notdone.add("org.freedesktop.DBus.Binding.Tests.IdentityDoubleArray"); + notdone.add("org.freedesktop.DBus.Binding.Tests.IdentityStringArray"); + notdone.add("org.freedesktop.DBus.Binding.Tests.Sum"); + notdone.add("org.freedesktop.DBus.Binding.SingleTests.Sum"); + notdone.add("org.freedesktop.DBus.Binding.Tests.InvertMapping"); + notdone.add("org.freedesktop.DBus.Binding.Tests.DeStruct"); + notdone.add("org.freedesktop.DBus.Binding.Tests.Primitize"); + notdone.add("org.freedesktop.DBus.Binding.Tests.Invert"); + notdone.add("org.freedesktop.DBus.Binding.Tests.Trigger"); + notdone.add("org.freedesktop.DBus.Binding.Tests.Exit"); + notdone.add("org.freedesktop.DBus.Binding.TestClient.Trigger"); + } + + public cross_test_server(DBusConnection conn) { + this.conn = conn; + } + + public boolean isRemote() { + return false; + } + + @SuppressWarnings("unchecked") + @DBus.Description("Returns whatever it is passed") + public Variant Identity(Variant input) { + done.add("org.freedesktop.DBus.Binding.Tests.Identity"); + notdone.remove("org.freedesktop.DBus.Binding.Tests.Identity"); + return new Variant(input.getValue()); + } + + @DBus.Description("Returns whatever it is passed") + public byte IdentityByte(byte input) { + done.add("org.freedesktop.DBus.Binding.Tests.IdentityByte"); + notdone.remove("org.freedesktop.DBus.Binding.Tests.IdentityByte"); + return input; + } + + @DBus.Description("Returns whatever it is passed") + public boolean IdentityBool(boolean input) { + done.add("org.freedesktop.DBus.Binding.Tests.IdentityBool"); + notdone.remove("org.freedesktop.DBus.Binding.Tests.IdentityBool"); + return input; + } + + @DBus.Description("Returns whatever it is passed") + public short IdentityInt16(short input) { + done.add("org.freedesktop.DBus.Binding.Tests.IdentityInt16"); + notdone.remove("org.freedesktop.DBus.Binding.Tests.IdentityInt16"); + return input; + } + + @DBus.Description("Returns whatever it is passed") + public UInt16 IdentityUInt16(UInt16 input) { + done.add("org.freedesktop.DBus.Binding.Tests.IdentityUInt16"); + notdone.remove("org.freedesktop.DBus.Binding.Tests.IdentityUInt16"); + return input; + } + + @DBus.Description("Returns whatever it is passed") + public int IdentityInt32(int input) { + done.add("org.freedesktop.DBus.Binding.Tests.IdentityInt32"); + notdone.remove("org.freedesktop.DBus.Binding.Tests.IdentityInt32"); + return input; + } + + @DBus.Description("Returns whatever it is passed") + public UInt32 IdentityUInt32(UInt32 input) { + done.add("org.freedesktop.DBus.Binding.Tests.IdentityUInt32"); + notdone.remove("org.freedesktop.DBus.Binding.Tests.IdentityUInt32"); + return input; + } + + @DBus.Description("Returns whatever it is passed") + public long IdentityInt64(long input) { + done.add("org.freedesktop.DBus.Binding.Tests.IdentityInt64"); + notdone.remove("org.freedesktop.DBus.Binding.Tests.IdentityInt64"); + return input; + } + + @DBus.Description("Returns whatever it is passed") + public UInt64 IdentityUInt64(UInt64 input) { + done.add("org.freedesktop.DBus.Binding.Tests.IdentityUInt64"); + notdone.remove("org.freedesktop.DBus.Binding.Tests.IdentityUInt64"); + return input; + } + + @DBus.Description("Returns whatever it is passed") + public double IdentityDouble(double input) { + done.add("org.freedesktop.DBus.Binding.Tests.IdentityDouble"); + notdone.remove("org.freedesktop.DBus.Binding.Tests.IdentityDouble"); + return input; + } + + @DBus.Description("Returns whatever it is passed") + public String IdentityString(String input) { + done.add("org.freedesktop.DBus.Binding.Tests.IdentityString"); + notdone.remove("org.freedesktop.DBus.Binding.Tests.IdentityString"); + return input; + } + + @DBus.Description("Returns whatever it is passed") + public Variant[] IdentityArray(Variant[] input) { + done.add("org.freedesktop.DBus.Binding.Tests.IdentityArray"); + notdone.remove("org.freedesktop.DBus.Binding.Tests.IdentityArray"); + return input; + } + + @DBus.Description("Returns whatever it is passed") + public byte[] IdentityByteArray(byte[] input) { + done.add("org.freedesktop.DBus.Binding.Tests.IdentityByteArray"); + notdone.remove("org.freedesktop.DBus.Binding.Tests.IdentityByteArray"); + return input; + } + + @DBus.Description("Returns whatever it is passed") + public boolean[] IdentityBoolArray(boolean[] input) { + done.add("org.freedesktop.DBus.Binding.Tests.IdentityBoolArray"); + notdone.remove("org.freedesktop.DBus.Binding.Tests.IdentityBoolArray"); + return input; + } + + @DBus.Description("Returns whatever it is passed") + public short[] IdentityInt16Array(short[] input) { + done.add("org.freedesktop.DBus.Binding.Tests.IdentityInt16Array"); + notdone.remove("org.freedesktop.DBus.Binding.Tests.IdentityInt16Array"); + return input; + } + + @DBus.Description("Returns whatever it is passed") + public UInt16[] IdentityUInt16Array(UInt16[] input) { + done.add("org.freedesktop.DBus.Binding.Tests.IdentityUInt16Array"); + notdone.remove("org.freedesktop.DBus.Binding.Tests.IdentityUInt16Array"); + return input; + } + + @DBus.Description("Returns whatever it is passed") + public int[] IdentityInt32Array(int[] input) { + done.add("org.freedesktop.DBus.Binding.Tests.IdentityInt32Array"); + notdone.remove("org.freedesktop.DBus.Binding.Tests.IdentityInt32Array"); + return input; + } + + @DBus.Description("Returns whatever it is passed") + public UInt32[] IdentityUInt32Array(UInt32[] input) { + done.add("org.freedesktop.DBus.Binding.Tests.IdentityUInt32Array"); + notdone.remove("org.freedesktop.DBus.Binding.Tests.IdentityUInt32Array"); + return input; + } + + @DBus.Description("Returns whatever it is passed") + public long[] IdentityInt64Array(long[] input) { + done.add("org.freedesktop.DBus.Binding.Tests.IdentityInt64Array"); + notdone.remove("org.freedesktop.DBus.Binding.Tests.IdentityInt64Array"); + return input; + } + + @DBus.Description("Returns whatever it is passed") + public UInt64[] IdentityUInt64Array(UInt64[] input) { + done.add("org.freedesktop.DBus.Binding.Tests.IdentityUInt64Array"); + notdone.remove("org.freedesktop.DBus.Binding.Tests.IdentityUInt64Array"); + return input; + } + + @DBus.Description("Returns whatever it is passed") + public double[] IdentityDoubleArray(double[] input) { + done.add("org.freedesktop.DBus.Binding.Tests.IdentityDoubleArray"); + notdone.remove("org.freedesktop.DBus.Binding.Tests.IdentityDoubleArray"); + return input; + } + + @DBus.Description("Returns whatever it is passed") + public String[] IdentityStringArray(String[] input) { + done.add("org.freedesktop.DBus.Binding.Tests.IdentityStringArray"); + notdone.remove("org.freedesktop.DBus.Binding.Tests.IdentityStringArray"); + return input; + } + + @DBus.Description("Returns the sum of the values in the input list") + public long Sum(int[] a) { + done.add("org.freedesktop.DBus.Binding.Tests.Sum"); + notdone.remove("org.freedesktop.DBus.Binding.Tests.Sum"); + long sum = 0; + for (int b : a) sum += b; + return sum; + } + + @DBus.Description("Returns the sum of the values in the input list") + public UInt32 Sum(byte[] a) { + done.add("org.freedesktop.DBus.Binding.SingleTests.Sum"); + notdone.remove("org.freedesktop.DBus.Binding.SingleTests.Sum"); + int sum = 0; + for (byte b : a) sum += (b < 0 ? b + 256 : b); + return new UInt32(sum % (UInt32.MAX_VALUE + 1)); + } + + @DBus.Description("Given a map of A => B, should return a map of B => a list of all the As which mapped to B") + public Map> InvertMapping(Map a) { + done.add("org.freedesktop.DBus.Binding.Tests.InvertMapping"); + notdone.remove("org.freedesktop.DBus.Binding.Tests.InvertMapping"); + HashMap> m = new HashMap>(); + for (String s : a.keySet()) { + String b = a.get(s); + List l = m.get(b); + if (null == l) { + l = new Vector(); + m.put(b, l); + } + l.add(s); + } + return m; + } + + @DBus.Description("This method returns the contents of a struct as separate values") + public DBus.Binding.Triplet DeStruct(DBus.Binding.TestStruct a) { + done.add("org.freedesktop.DBus.Binding.Tests.DeStruct"); + notdone.remove("org.freedesktop.DBus.Binding.Tests.DeStruct"); + return new DBus.Binding.Triplet(a.a, a.b, a.c); + } + + @DBus.Description("Given any compound type as a variant, return all the primitive types recursively contained within as an array of variants") + @SuppressWarnings("unchecked") + public List> Primitize(Variant a) { + done.add("org.freedesktop.DBus.Binding.Tests.Primitize"); + notdone.remove("org.freedesktop.DBus.Binding.Tests.Primitize"); + return cross_test_client.PrimitizeRecurse(a.getValue(), a.getType()); + } + + @DBus.Description("inverts it's input") + public boolean Invert(boolean a) { + done.add("org.freedesktop.DBus.Binding.Tests.Invert"); + notdone.remove("org.freedesktop.DBus.Binding.Tests.Invert"); + return !a; + } + + @DBus.Description("triggers sending of a signal from the supplied object with the given parameter") + public void Trigger(String a, UInt64 b) { + done.add("org.freedesktop.DBus.Binding.Tests.Trigger"); + notdone.remove("org.freedesktop.DBus.Binding.Tests.Trigger"); + try { + conn.sendSignal(new DBus.Binding.TestSignals.Triggered(a, b)); + } catch (DBusException DBe) { + throw new DBusExecutionException(DBe.getMessage()); + } + } + + public void Exit() { + done.add("org.freedesktop.DBus.Binding.Tests.Exit"); + notdone.remove("org.freedesktop.DBus.Binding.Tests.Exit"); + run = false; + synchronized (this) { + notifyAll(); + } + } + + public void handle(DBus.Binding.TestClient.Trigger t) { + done.add("org.freedesktop.DBus.Binding.TestClient.Trigger"); + notdone.remove("org.freedesktop.DBus.Binding.TestClient.Trigger"); + try { + DBus.Binding.TestClient cb = conn.getRemoteObject(t.getSource(), "/Test", DBus.Binding.TestClient.class); + cb.Response(t.a, t.b); + } catch (DBusException DBe) { + throw new DBusExecutionException(DBe.getMessage()); + } + } + + public static void main(String[] args) { + try { + DBusConnection conn = DBusConnection.getConnection(DBusConnection.SESSION); + conn.requestBusName("org.freedesktop.DBus.Binding.TestServer"); + cross_test_server cts = new cross_test_server(conn); + conn.addSigHandler(DBus.Binding.TestClient.Trigger.class, cts); + conn.exportObject("/Test", cts); + synchronized (cts) { + while (cts.run) { + try { + cts.wait(); + } catch (InterruptedException Ie) { + } + } + } + for (String s : cts.done) + System.out.println(s + " ok"); + for (String s : cts.notdone) + System.out.println(s + " untested"); + conn.disconnect(); + System.exit(0); + } catch (DBusException DBe) { + DBe.printStackTrace(); + System.exit(1); + } + } +} + diff --git a/federation/sssd/src/test/java/org/freedesktop/dbus/profile.java b/federation/sssd/src/test/java/org/freedesktop/dbus/profile.java new file mode 100644 index 0000000000..e6866a7041 --- /dev/null +++ b/federation/sssd/src/test/java/org/freedesktop/dbus/profile.java @@ -0,0 +1,378 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import org.freedesktop.DBus.Introspectable; +import org.freedesktop.DBus.Peer; + +import java.util.HashMap; +import java.util.Random; +import java.util.Vector; + +class ProfileHandler implements DBusSigHandler { + public int c = 0; + + public void handle(Profiler.ProfileSignal s) { + if (0 == (c++ % profile.SIGNAL_INNER)) System.out.print("-"); + } +} + +/** + * Profiling tests. + */ +public class profile { + public static final int SIGNAL_INNER = 100; + public static final int SIGNAL_OUTER = 100; + public static final int PING_INNER = 100; + public static final int PING_OUTER = 100; + public static final int BYTES = 2000000; + public static final int INTROSPECTION_OUTER = 100; + public static final int INTROSPECTION_INNER = 10; + public static final int STRUCT_OUTER = 100; + public static final int STRUCT_INNER = 10; + public static final int LIST_OUTER = 100; + public static final int LIST_INNER = 10; + public static final int LIST_LENGTH = 100; + public static final int MAP_OUTER = 100; + public static final int MAP_INNER = 10; + public static final int MAP_LENGTH = 100; + public static final int ARRAY_OUTER = 100; + public static final int ARRAY_INNER = 10; + public static final int ARRAY_LENGTH = 1000; + public static final int STRING_ARRAY_OUTER = 10; + public static final int STRING_ARRAY_INNER = 1; + public static final int STRING_ARRAY_LENGTH = 20000; + + public static class Log { + private long last; + private int[] deltas; + private int current = 0; + + public Log(int size) { + deltas = new int[size]; + } + + public void start() { + last = System.currentTimeMillis(); + } + + public void stop() { + deltas[current] = (int) (System.currentTimeMillis() - last); + current++; + } + + public double mean() { + if (0 == current) return 0; + long sum = 0; + for (int i = 0; i < current; i++) + sum += deltas[i]; + return sum /= current; + } + + public long min() { + int m = Integer.MAX_VALUE; + for (int i = 0; i < current; i++) + if (deltas[i] < m) m = deltas[i]; + return m; + } + + public long max() { + int m = 0; + for (int i = 0; i < current; i++) + if (deltas[i] > m) m = deltas[i]; + return m; + } + + public double stddev() { + double mean = mean(); + double sum = 0; + for (int i = 0; i < current; i++) + sum += (deltas[i] - mean) * (deltas[i] - mean); + return Math.sqrt(sum / (current - 1)); + } + } + + public static void main(String[] args) { + try { + if (0 == args.length) { + System.out.println("You must specify a profile type."); + System.out.println("Syntax: profile "); + System.exit(1); + } + DBusConnection conn = DBusConnection.getConnection(DBusConnection.SESSION); + conn.requestBusName("org.freedesktop.DBus.java.profiler"); + if ("pings".equals(args[0])) { + int count = PING_INNER * PING_OUTER; + System.out.print("Sending " + count + " pings..."); + Peer p = conn.getRemoteObject("org.freedesktop.DBus.java.profiler", "/Profiler", Peer.class); + Log l = new Log(count); + long t = System.currentTimeMillis(); + for (int i = 0; i < PING_OUTER; i++) { + for (int j = 0; j < PING_INNER; j++) { + l.start(); + p.Ping(); + l.stop(); + } + System.out.print(""); + } + t = System.currentTimeMillis() - t; + System.out.println(" done."); + System.out.println("min/max/avg (ms): " + l.min() + "/" + l.max() + "/" + l.mean()); + System.out.println("deviation: " + l.stddev()); + System.out.println("Total time: " + t + "ms"); + } else if ("strings".equals(args[0])) { + int count = STRING_ARRAY_INNER * STRING_ARRAY_OUTER; + System.out.print("Sending array of " + STRING_ARRAY_LENGTH + " strings " + count + " times."); + ProfilerInstance pi = new ProfilerInstance(); + conn.exportObject("/Profiler", pi); + Profiler p = conn.getRemoteObject("org.freedesktop.DBus.java.profiler", "/Profiler", Profiler.class); + String[] v = new String[STRING_ARRAY_LENGTH]; + Random r = new Random(); + for (int i = 0; i < STRING_ARRAY_LENGTH; i++) v[i] = "" + r.nextInt(); + Log l = new Log(count); + long t = System.currentTimeMillis(); + for (int i = 0; i < STRING_ARRAY_OUTER; i++) { + for (int j = 0; j < STRING_ARRAY_INNER; j++) { + l.start(); + p.stringarray(v); + l.stop(); + } + System.out.print(""); + } + t = System.currentTimeMillis() - t; + System.out.println(" done."); + System.out.println("min/max/avg (ms): " + l.min() + "/" + l.max() + "/" + l.mean()); + System.out.println("deviation: " + l.stddev()); + System.out.println("Total time: " + t + "ms"); + } else if ("arrays".equals(args[0])) { + int count = ARRAY_INNER * ARRAY_OUTER; + System.out.print("Sending array of " + ARRAY_LENGTH + " ints " + count + " times."); + ProfilerInstance pi = new ProfilerInstance(); + conn.exportObject("/Profiler", pi); + Profiler p = conn.getRemoteObject("org.freedesktop.DBus.java.profiler", "/Profiler", Profiler.class); + int[] v = new int[ARRAY_LENGTH]; + Random r = new Random(); + for (int i = 0; i < ARRAY_LENGTH; i++) v[i] = r.nextInt(); + Log l = new Log(count); + long t = System.currentTimeMillis(); + for (int i = 0; i < ARRAY_OUTER; i++) { + for (int j = 0; j < ARRAY_INNER; j++) { + l.start(); + p.array(v); + l.stop(); + } + System.out.print(""); + } + t = System.currentTimeMillis() - t; + System.out.println(" done."); + System.out.println("min/max/avg (ms): " + l.min() + "/" + l.max() + "/" + l.mean()); + System.out.println("deviation: " + l.stddev()); + System.out.println("Total time: " + t + "ms"); + } else if ("maps".equals(args[0])) { + int count = MAP_INNER * MAP_OUTER; + System.out.print("Sending map of " + MAP_LENGTH + " string=>strings " + count + " times."); + ProfilerInstance pi = new ProfilerInstance(); + conn.exportObject("/Profiler", pi); + Profiler p = conn.getRemoteObject("org.freedesktop.DBus.java.profiler", "/Profiler", Profiler.class); + HashMap m = new HashMap(); + for (int i = 0; i < MAP_LENGTH; i++) + m.put("" + i, "hello"); + Log l = new Log(count); + long t = System.currentTimeMillis(); + for (int i = 0; i < MAP_OUTER; i++) { + for (int j = 0; j < MAP_INNER; j++) { + l.start(); + p.map(m); + l.stop(); + } + System.out.print(""); + } + t = System.currentTimeMillis() - t; + System.out.println(" done."); + System.out.println("min/max/avg (ms): " + l.min() + "/" + l.max() + "/" + l.mean()); + System.out.println("deviation: " + l.stddev()); + System.out.println("Total time: " + t + "ms"); + } else if ("lists".equals(args[0])) { + int count = LIST_OUTER * LIST_INNER; + System.out.print("Sending list of " + LIST_LENGTH + " strings " + count + " times."); + ProfilerInstance pi = new ProfilerInstance(); + conn.exportObject("/Profiler", pi); + Profiler p = conn.getRemoteObject("org.freedesktop.DBus.java.profiler", "/Profiler", Profiler.class); + Vector v = new Vector(); + for (int i = 0; i < LIST_LENGTH; i++) + v.add("hello " + i); + Log l = new Log(count); + long t = System.currentTimeMillis(); + for (int i = 0; i < LIST_OUTER; i++) { + for (int j = 0; j < LIST_INNER; j++) { + l.start(); + p.list(v); + l.stop(); + } + System.out.print(""); + } + t = System.currentTimeMillis() - t; + System.out.println(" done."); + System.out.println("min/max/avg (ms): " + l.min() + "/" + l.max() + "/" + l.mean()); + System.out.println("deviation: " + l.stddev()); + System.out.println("Total time: " + t + "ms"); + } else if ("structs".equals(args[0])) { + int count = STRUCT_OUTER * STRUCT_INNER; + System.out.print("Sending a struct " + count + " times."); + ProfilerInstance pi = new ProfilerInstance(); + conn.exportObject("/Profiler", pi); + Profiler p = conn.getRemoteObject("org.freedesktop.DBus.java.profiler", "/Profiler", Profiler.class); + ProfileStruct ps = new ProfileStruct("hello", new UInt32(18), 500L); + Log l = new Log(count); + long t = System.currentTimeMillis(); + for (int i = 0; i < STRUCT_OUTER; i++) { + for (int j = 0; j < STRUCT_INNER; j++) { + l.start(); + p.struct(ps); + l.stop(); + } + System.out.print(""); + } + t = System.currentTimeMillis() - t; + System.out.println(" done."); + System.out.println("min/max/avg (ms): " + l.min() + "/" + l.max() + "/" + l.mean()); + System.out.println("deviation: " + l.stddev()); + System.out.println("Total time: " + t + "ms"); + } else if ("introspect".equals(args[0])) { + int count = INTROSPECTION_OUTER * INTROSPECTION_INNER; + System.out.print("Recieving introspection data " + count + " times."); + ProfilerInstance pi = new ProfilerInstance(); + conn.exportObject("/Profiler", pi); + Introspectable is = conn.getRemoteObject("org.freedesktop.DBus.java.profiler", "/Profiler", Introspectable.class); + Log l = new Log(count); + long t = System.currentTimeMillis(); + String s = null; + for (int i = 0; i < INTROSPECTION_OUTER; i++) { + for (int j = 0; j < INTROSPECTION_INNER; j++) { + l.start(); + s = is.Introspect(); + l.stop(); + } + System.out.print(""); + } + t = System.currentTimeMillis() - t; + System.out.println(" done."); + System.out.println("min/max/avg (ms): " + l.min() + "/" + l.max() + "/" + l.mean()); + System.out.println("deviation: " + l.stddev()); + System.out.println("Total time: " + t + "ms"); + System.out.println("Introspect data: " + s); + } else if ("bytes".equals(args[0])) { + System.out.print("Sending " + BYTES + " bytes"); + ProfilerInstance pi = new ProfilerInstance(); + conn.exportObject("/Profiler", pi); + Profiler p = conn.getRemoteObject("org.freedesktop.DBus.java.profiler", "/Profiler", Profiler.class); + byte[] bs = new byte[BYTES]; + for (int i = 0; i < BYTES; i++) + bs[i] = (byte) i; + long t = System.currentTimeMillis(); + p.bytes(bs); + System.out.println(" done in " + (System.currentTimeMillis() - t) + "ms."); + } else if ("rate".equals(args[0])) { + ProfilerInstance pi = new ProfilerInstance(); + conn.exportObject("/Profiler", pi); + Profiler p = conn.getRemoteObject("org.freedesktop.DBus.java.profiler", "/Profiler", Profiler.class); + Peer peer = conn.getRemoteObject("org.freedesktop.DBus.java.profiler", "/Profiler", Peer.class); + conn.changeThreadCount((byte) 1); + + long start = System.currentTimeMillis(); + int count = 0; + do { + p.Pong(); + count++; + } while (count < 10000); + long end = System.currentTimeMillis(); + System.out.println("No payload: " + ((count * 1000) / (end - start)) + " RT/second"); + start = System.currentTimeMillis(); + count = 0; + do { + p.Pong(); + count++; + } while (count < 10000); + peer.Ping(); + end = System.currentTimeMillis(); + System.out.println("No payload, One way: " + ((count * 1000) / (end - start)) + " /second"); + int len = 256; + while (len <= 32768) { + byte[] bs = new byte[len]; + count = 0; + start = System.currentTimeMillis(); + do { + p.bytes(bs); + count++; + } while (count < 1000); + end = System.currentTimeMillis(); + long ms = end - start; + double cps = (count * 1000) / ms; + double rate = (len * cps) / (1024.0 * 1024.0); + System.out.println(len + " byte array) " + (count * len) + " bytes in " + ms + "ms (in " + count + " calls / " + (int) cps + " CPS): " + rate + "MB/s"); + len <<= 1; + } + len = 256; + while (len <= 32768) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < len; i++) sb.append('a'); + String s = sb.toString(); + end = System.currentTimeMillis() + 500; + count = 0; + do { + p.string(s); + count++; + } while (count < 1000); + long ms = end - start; + double cps = (count * 1000) / ms; + double rate = (len * cps) / (1024.0 * 1024.0); + System.out.println(len + " string) " + (count * len) + " bytes in " + ms + "ms (in " + count + " calls / " + (int) cps + " CPS): " + rate + "MB/s"); + len <<= 1; + } + } else if ("signals".equals(args[0])) { + int count = SIGNAL_OUTER * SIGNAL_INNER; + System.out.print("Sending " + count + " signals"); + ProfileHandler ph = new ProfileHandler(); + conn.addSigHandler(Profiler.ProfileSignal.class, ph); + Log l = new Log(count); + Profiler.ProfileSignal ps = new Profiler.ProfileSignal("/"); + long t = System.currentTimeMillis(); + for (int i = 0; i < SIGNAL_OUTER; i++) { + for (int j = 0; j < SIGNAL_INNER; j++) { + l.start(); + conn.sendSignal(ps); + l.stop(); + } + System.out.print(""); + } + t = System.currentTimeMillis() - t; + System.out.println(" done."); + System.out.println("min/max/avg (ms): " + l.min() + "/" + l.max() + "/" + l.mean()); + System.out.println("deviation: " + l.stddev()); + System.out.println("Total time: " + t + "ms"); + while (ph.c < count) try { + Thread.sleep(100); + } catch (InterruptedException Ie) { + } + ; + } else { + conn.disconnect(); + System.out.println("Invalid profile ``" + args[0] + "''."); + System.out.println("Syntax: profile "); + System.exit(1); + } + conn.disconnect(); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + } +} diff --git a/federation/sssd/src/test/java/org/freedesktop/dbus/test.java b/federation/sssd/src/test/java/org/freedesktop/dbus/test.java new file mode 100644 index 0000000000..af6a736623 --- /dev/null +++ b/federation/sssd/src/test/java/org/freedesktop/dbus/test.java @@ -0,0 +1,991 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import org.freedesktop.DBus; +import org.freedesktop.DBus.Error.MatchRuleInvalid; +import org.freedesktop.DBus.Error.ServiceUnknown; +import org.freedesktop.DBus.Error.UnknownObject; +import org.freedesktop.DBus.Introspectable; +import org.freedesktop.DBus.Peer; +import org.freedesktop.DBus.Properties; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.exceptions.DBusExecutionException; +import org.freedesktop.dbus.exceptions.NotConnected; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.text.Collator; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Vector; + +class testnewclass implements TestNewInterface { + public boolean isRemote() { + return false; + } + + public String getName() { + return toString(); + } +} + +class testclass implements TestRemoteInterface, TestRemoteInterface2, TestSignalInterface, TestSignalInterface2, Properties { + private DBusConnection conn; + + public testclass(DBusConnection conn) { + this.conn = conn; + } + + public String Introspect() { + return "Not XML"; + } + + public int[][] teststructstruct(TestStruct3 in) { + List> lli = in.b; + int[][] out = new int[lli.size()][]; + for (int j = 0; j < out.length; j++) { + out[j] = new int[lli.get(j).size()]; + for (int k = 0; k < out[j].length; k++) + out[j][k] = lli.get(j).get(k); + } + return out; + } + + public float testfloat(float[] f) { + if (f.length < 4 || + f[0] != 17.093f || + f[1] != -23f || + f[2] != 0.0f || + f[3] != 31.42f) + test.fail("testfloat got incorrect array"); + return f[0]; + } + + public void newpathtest(Path p) { + if (!p.toString().equals("/new/path/test")) + test.fail("new path test got wrong path"); + } + + public void waitawhile() { + System.out.println("Sleeping."); + try { + Thread.sleep(1000); + } catch (InterruptedException Ie) { + } + System.out.println("Done sleeping."); + } + + public TestTuple, Boolean> show(A in) { + System.out.println("Showing Stuff: " + in.getClass() + "(" + in + ")"); + if (!(in instanceof Integer) || ((Integer) in).intValue() != 234) + test.fail("show received the wrong arguments"); + DBusCallInfo info = DBusConnection.getCallInfo(); + List l = new Vector(); + l.add(1953); + return new TestTuple, Boolean>(info.getSource(), l, true); + } + + @SuppressWarnings("unchecked") + public T dostuff(TestStruct foo) { + System.out.println("Doing Stuff " + foo); + System.out.println(" -- (" + foo.a.getClass() + ", " + foo.b.getClass() + ", " + foo.c.getClass() + ")"); + if (!(foo instanceof TestStruct) || + !(foo.a instanceof String) || + !(foo.b instanceof UInt32) || + !(foo.c instanceof Variant) || + !"bar".equals(foo.a) || + foo.b.intValue() != 52 || + !(foo.c.getValue() instanceof Boolean) || + ((Boolean) foo.c.getValue()).booleanValue() != true) + test.fail("dostuff received the wrong arguments"); + return (T) foo.c.getValue(); + } + + /** + * Local classes MUST implement this to return false + */ + public boolean isRemote() { + return false; + } + + /** + * The method we are exporting to the Bus. + */ + public List sampleArray(List ss, Integer[] is, long[] ls) { + System.out.println("Got an array:"); + for (String s : ss) + System.out.println("--" + s); + if (ss.size() != 5 || + !"hi".equals(ss.get(0)) || + !"hello".equals(ss.get(1)) || + !"hej".equals(ss.get(2)) || + !"hey".equals(ss.get(3)) || + !"aloha".equals(ss.get(4))) + test.fail("sampleArray, String array contents incorrect"); + System.out.println("Got an array:"); + for (Integer i : is) + System.out.println("--" + i); + if (is.length != 4 || + is[0].intValue() != 1 || + is[1].intValue() != 5 || + is[2].intValue() != 7 || + is[3].intValue() != 9) + test.fail("sampleArray, Integer array contents incorrect"); + System.out.println("Got an array:"); + for (long l : ls) + System.out.println("--" + l); + if (ls.length != 4 || + ls[0] != 2 || + ls[1] != 6 || + ls[2] != 8 || + ls[3] != 12) + test.fail("sampleArray, Integer array contents incorrect"); + Vector v = new Vector(); + v.add(-1); + v.add(-5); + v.add(-7); + v.add(-12); + v.add(-18); + return v; + } + + public String getName() { + return "This Is A UTF-8 Name: س !!"; + } + + public String getNameAndThrow() throws TestException { + throw new TestException("test"); + } + + public boolean check() { + System.out.println("Being checked"); + return false; + } + + public int frobnicate(List n, Map> m, T v) { + if (null == n) + test.fail("List was null"); + if (n.size() != 3) + test.fail("List was wrong size (expected 3, actual " + n.size() + ")"); + if (n.get(0) != 2L || + n.get(1) != 5L || + n.get(2) != 71L) + test.fail("List has wrong contents"); + if (!(v instanceof Integer)) + test.fail("v not an Integer"); + if (((Integer) v) != 13) + test.fail("v is incorrect"); + if (null == m) + test.fail("Map was null"); + if (m.size() != 1) + test.fail("Map was wrong size"); + if (!m.keySet().contains("stuff")) + test.fail("Incorrect key"); + Map mus = m.get("stuff"); + if (null == mus) + test.fail("Sub-Map was null"); + if (mus.size() != 3) + test.fail("Sub-Map was wrong size"); + if (!(new Short((short) 5).equals(mus.get(new UInt16(4))))) + test.fail("Sub-Map has wrong contents"); + if (!(new Short((short) 6).equals(mus.get(new UInt16(5))))) + test.fail("Sub-Map has wrong contents"); + if (!(new Short((short) 7).equals(mus.get(new UInt16(6))))) + test.fail("Sub-Map has wrong contents"); + return -5; + } + + public DBusInterface getThis(DBusInterface t) { + if (!t.equals(this)) + test.fail("Didn't get this properly"); + return this; + } + + public void throwme() throws TestException { + throw new TestException("test"); + } + + public TestSerializable testSerializable(byte b, TestSerializable s, int i) { + System.out.println("Recieving TestSerializable: " + s); + if (b != 12 + || i != 13 + || !(s.getInt() == 1) + || !(s.getString().equals("woo")) + || !(s.getVector().size() == 3) + || !(s.getVector().get(0) == 1) + || !(s.getVector().get(1) == 2) + || !(s.getVector().get(2) == 3)) + test.fail("Error in recieving custom synchronisation"); + return s; + } + + public String recursionTest() { + try { + TestRemoteInterface tri = conn.getRemoteObject("foo.bar.Test", "/Test", TestRemoteInterface.class); + return tri.getName(); + } catch (DBusException DBe) { + test.fail("Failed with error: " + DBe); + return ""; + } + } + + public int overload(String s) { + return 1; + } + + public int overload(byte b) { + return 2; + } + + public int overload() { + DBusCallInfo info = DBusConnection.getCallInfo(); + if ("org.freedesktop.dbus.test.AlternateTestInterface".equals(info.getInterface())) + return 3; + else if ("org.freedesktop.dbus.test.TestRemoteInterface".equals(info.getInterface())) + return 4; + else + return -1; + } + + public List> checklist(List> lli) { + return lli; + } + + public TestNewInterface getNew() { + testnewclass n = new testnewclass(); + try { + conn.exportObject("/new", n); + } catch (DBusException DBe) { + throw new DBusExecutionException(DBe.getMessage()); + } + return n; + } + + public void sig(Type[] s) { + if (s.length != 2 + || !s[0].equals(Byte.class) + || !(s[1] instanceof ParameterizedType) + || !Map.class.equals(((ParameterizedType) s[1]).getRawType()) + || ((ParameterizedType) s[1]).getActualTypeArguments().length != 2 + || !String.class.equals(((ParameterizedType) s[1]).getActualTypeArguments()[0]) + || !Integer.class.equals(((ParameterizedType) s[1]).getActualTypeArguments()[1])) + test.fail("Didn't send types correctly"); + } + + @SuppressWarnings("unchecked") + public void complexv(Variant v) { + if (!"a{ss}".equals(v.getSig()) + || !(v.getValue() instanceof Map) + || ((Map) v.getValue()).size() != 1 + || !"moo".equals(((Map) v.getValue()).get("cow"))) + test.fail("Didn't send variant correctly"); + } + + public void reg13291(byte[] as, byte[] bs) { + if (as.length != bs.length) test.fail("didn't receive identical byte arrays"); + for (int i = 0; i < as.length; i++) + if (as[i] != bs[i]) test.fail("didn't receive identical byte arrays"); + } + + @SuppressWarnings("unchecked") + public A Get(String interface_name, String property_name) { + return (A) new Path("/nonexistant/path"); + } + + public void Set(String interface_name, String property_name, A value) { + } + + public Map GetAll(String interface_name) { + return new HashMap(); + } + + public Path pathrv(Path a) { + return a; + } + + public List pathlistrv(List a) { + return a; + } + + public Map pathmaprv(Map a) { + return a; + } + + @SuppressWarnings("unchecked") + public Map svm() { + HashMap properties = new HashMap(); + HashMap> parameters = new HashMap>(); + + parameters.put("Name", new Variant("Joe")); + parameters.put("Password", new Variant("abcdef")); + + properties.put("Parameters", new Variant(parameters, "a{sv}")); + return (Map) properties; + } +} + +/** + * Typed signal handler for renamed signal + */ +class renamedsignalhandler implements DBusSigHandler { + /** + * Handling a signal + */ + public void handle(TestSignalInterface2.TestRenamedSignal t) { + if (false == test.done5) { + test.done5 = true; + } else { + test.fail("SignalHandler R has been run too many times"); + } + System.out.println("SignalHandler R Running"); + System.out.println("string(" + t.value + ") int(" + t.number + ")"); + if (!"Bar".equals(t.value) || !(new UInt32(42)).equals(t.number)) + test.fail("Incorrect TestRenamedSignal parameters"); + } +} + +/** + * Empty signal handler + */ +class emptysignalhandler implements DBusSigHandler { + /** + * Handling a signal + */ + public void handle(TestSignalInterface.EmptySignal t) { + if (false == test.done7) { + test.done7 = true; + } else { + test.fail("SignalHandler E has been run too many times"); + } + System.out.println("SignalHandler E Running"); + } +} + +/** + * Disconnect handler + */ +class disconnecthandler implements DBusSigHandler { + private DBusConnection conn; + private renamedsignalhandler sh; + + public disconnecthandler(DBusConnection conn, renamedsignalhandler sh) { + this.conn = conn; + this.sh = sh; + } + + /** + * Handling a signal + */ + public void handle(DBus.Local.Disconnected t) { + if (false == test.done6) { + test.done6 = true; + System.out.println("Handling disconnect, unregistering handler"); + try { + conn.removeSigHandler(TestSignalInterface2.TestRenamedSignal.class, sh); + } catch (DBusException DBe) { + DBe.printStackTrace(); + test.fail("Disconnect handler threw an exception: " + DBe); + } + } + } +} + + +/** + * Typed signal handler + */ +class pathsignalhandler implements DBusSigHandler { + /** + * Handling a signal + */ + public void handle(TestSignalInterface.TestPathSignal t) { + System.out.println("Path sighandler: " + t); + } +} + +/** + * Typed signal handler + */ +class signalhandler implements DBusSigHandler { + /** + * Handling a signal + */ + public void handle(TestSignalInterface.TestSignal t) { + if (false == test.done1) { + test.done1 = true; + } else { + test.fail("SignalHandler 1 has been run too many times"); + } + System.out.println("SignalHandler 1 Running"); + System.out.println("string(" + t.value + ") int(" + t.number + ")"); + if (!"Bar".equals(t.value) || !(new UInt32(42)).equals(t.number)) + test.fail("Incorrect TestSignal parameters"); + } +} + +/** + * Untyped signal handler + */ +class arraysignalhandler implements DBusSigHandler { + /** + * Handling a signal + */ + public void handle(TestSignalInterface.TestArraySignal t) { + try { + if (false == test.done2) { + test.done2 = true; + } else { + test.fail("SignalHandler 2 has been run too many times"); + } + System.out.println("SignalHandler 2 Running"); + if (t.v.size() != 1) + test.fail("Incorrect TestArraySignal array length: should be 1, actually " + t.v.size()); + System.out.println("Got a test array signal with Parameters: "); + for (String str : t.v.get(0).a) + System.out.println("--" + str); + System.out.println(t.v.get(0).b.getType()); + System.out.println(t.v.get(0).b.getValue()); + if (!(t.v.get(0).b.getValue() instanceof UInt64) || + 567L != ((UInt64) t.v.get(0).b.getValue()).longValue() || + t.v.get(0).a.size() != 5 || + !"hi".equals(t.v.get(0).a.get(0)) || + !"hello".equals(t.v.get(0).a.get(1)) || + !"hej".equals(t.v.get(0).a.get(2)) || + !"hey".equals(t.v.get(0).a.get(3)) || + !"aloha".equals(t.v.get(0).a.get(4))) + test.fail("Incorrect TestArraySignal parameters"); + + if (t.m.keySet().size() != 2) + test.fail("Incorrect TestArraySignal map size: should be 2, actually " + t.m.keySet().size()); + if (!(t.m.get(new UInt32(1)).b.getValue() instanceof UInt64) || + 678L != ((UInt64) t.m.get(new UInt32(1)).b.getValue()).longValue() || + !(t.m.get(new UInt32(42)).b.getValue() instanceof UInt64) || + 789L != ((UInt64) t.m.get(new UInt32(42)).b.getValue()).longValue()) + test.fail("Incorrect TestArraySignal parameters"); + + } catch (Exception e) { + e.printStackTrace(); + test.fail("SignalHandler 2 threw an exception: " + e); + } + } +} + +/** + * Object path signal handler + */ +class objectsignalhandler implements DBusSigHandler { + public void handle(TestSignalInterface.TestObjectSignal s) { + if (false == test.done3) { + test.done3 = true; + } else { + test.fail("SignalHandler 3 has been run too many times"); + } + System.out.println(s.otherpath); + } +} + +/** + * handler which should never be called + */ +class badarraysignalhandler implements DBusSigHandler { + /** + * Handling a signal + */ + public void handle(T s) { + test.fail("This signal handler shouldn't be called"); + } +} + +/** + * Callback handler + */ +class callbackhandler implements CallbackHandler { + public void handle(String r) { + System.out.println("Handling callback: " + r); + Collator col = Collator.getInstance(); + col.setDecomposition(Collator.FULL_DECOMPOSITION); + col.setStrength(Collator.PRIMARY); + if (0 != col.compare("This Is A UTF-8 Name: ﺱ !!", r)) + test.fail("call with callback, wrong return value"); + if (test.done4) test.fail("Already ran callback handler"); + test.done4 = true; + } + + public void handleError(DBusExecutionException e) { + System.out.println("Handling error callback: " + e + " message = '" + e.getMessage() + "'"); + if (!(e instanceof TestException)) test.fail("Exception is of the wrong sort"); + Collator col = Collator.getInstance(); + col.setDecomposition(Collator.FULL_DECOMPOSITION); + col.setStrength(Collator.PRIMARY); + if (0 != col.compare("test", e.getMessage())) + test.fail("Exception has the wrong message"); + if (test.done8) test.fail("Already ran callback error handler"); + test.done8 = true; + } +} + +/** + * This is a test program which sends and recieves a signal, implements, exports and calls a remote method. + */ +public class test { + public static boolean done1 = false; + public static boolean done2 = false; + public static boolean done3 = false; + public static boolean done4 = false; + public static boolean done5 = false; + public static boolean done6 = false; + public static boolean done7 = false; + public static boolean done8 = false; + + public static void fail(String message) { + System.out.println("Test Failed: " + message); + System.err.println("Test Failed: " + message); + if (null != serverconn) serverconn.disconnect(); + if (null != clientconn) clientconn.disconnect(); + System.exit(1); + } + + static DBusConnection serverconn = null; + static DBusConnection clientconn = null; + + @SuppressWarnings("unchecked") + public static void main(String[] args) { + try { + System.out.println("Creating Connection"); + serverconn = DBusConnection.getConnection(DBusConnection.SESSION); + clientconn = DBusConnection.getConnection(DBusConnection.SESSION); + serverconn.setWeakReferences(true); + clientconn.setWeakReferences(true); + + System.out.println("Registering Name"); + serverconn.requestBusName("foo.bar.Test"); + + /** This gets a remote object matching our bus name and exported object path. */ + Peer peer = clientconn.getRemoteObject("foo.bar.Test", "/Test", Peer.class); + DBus dbus = clientconn.getRemoteObject("org.freedesktop.DBus", "/org/freedesktop/DBus", DBus.class); + + System.out.print("Listening for signals..."); + signalhandler sigh = new signalhandler(); + renamedsignalhandler rsh = new renamedsignalhandler(); + try { + /** This registers an instance of the test class as the signal handler for the TestSignal class. */ + clientconn.addSigHandler(TestSignalInterface.EmptySignal.class, new emptysignalhandler()); + clientconn.addSigHandler(TestSignalInterface.TestSignal.class, sigh); + clientconn.addSigHandler(TestSignalInterface2.TestRenamedSignal.class, rsh); + clientconn.addSigHandler(DBus.Local.Disconnected.class, new disconnecthandler(clientconn, rsh)); + String source = dbus.GetNameOwner("foo.bar.Test"); + clientconn.addSigHandler(TestSignalInterface.TestArraySignal.class, source, peer, new arraysignalhandler()); + clientconn.addSigHandler(TestSignalInterface.TestObjectSignal.class, new objectsignalhandler()); + clientconn.addSigHandler(TestSignalInterface.TestPathSignal.class, new pathsignalhandler()); + badarraysignalhandler bash = new badarraysignalhandler(); + clientconn.addSigHandler(TestSignalInterface.TestSignal.class, bash); + clientconn.removeSigHandler(TestSignalInterface.TestSignal.class, bash); + System.out.println("done"); + } catch (MatchRuleInvalid MRI) { + test.fail("Failed to add handlers: " + MRI.getMessage()); + } catch (DBusException DBe) { + test.fail("Failed to add handlers: " + DBe.getMessage()); + } + + System.out.println("Listening for Method Calls"); + testclass tclass = new testclass(serverconn); + testclass tclass2 = new testclass(serverconn); + /** This exports an instance of the test class as the object /Test. */ + serverconn.exportObject("/Test", tclass); + serverconn.exportObject("/BadTest", tclass); + serverconn.exportObject("/BadTest2", tclass2); + serverconn.addFallback("/FallbackTest", tclass); + + // explicitly unexport object + serverconn.unExportObject("/BadTest"); + // implicitly unexport object + tclass2 = null; + System.gc(); + System.runFinalization(); + System.gc(); + System.runFinalization(); + System.gc(); + System.runFinalization(); + + System.out.println("Sending Signal"); + /** This creates an instance of the Test Signal, with the given object path, signal name and parameters, and broadcasts in on the Bus. */ + serverconn.sendSignal(new TestSignalInterface.TestSignal("/foo/bar/Wibble", "Bar", new UInt32(42))); + serverconn.sendSignal(new TestSignalInterface.EmptySignal("/foo/bar/Wibble")); + serverconn.sendSignal(new TestSignalInterface2.TestRenamedSignal("/foo/bar/Wibble", "Bar", new UInt32(42))); + + System.out.println("These things are on the bus:"); + String[] names = dbus.ListNames(); + for (String name : names) + System.out.println("\t" + name); + + System.out.println("Getting our introspection data"); + /** This gets a remote object matching our bus name and exported object path. */ + Introspectable intro = clientconn.getRemoteObject("foo.bar.Test", "/", Introspectable.class); + /** Get introspection data */ + String data;/* = intro.Introspect(); + if (null == data || !data.startsWith(" peers = serverconn.new PeerSet(); + peers.add("org.freedesktop.DBus"); + clientconn.requestBusName("test.testclient"); + peers.add("test.testclient"); + clientconn.releaseBusName("test.testclient"); + + System.out.println("Pinging ourselves"); + /** Call ping. */ + for (int i = 0; i < 10; i++) { + long then = System.currentTimeMillis(); + peer.Ping(); + long now = System.currentTimeMillis(); + System.out.println("Ping returned in " + (now - then) + "ms."); + } + + System.out.println("Calling Method0/1"); + /** This gets a remote object matching our bus name and exported object path. */ + TestRemoteInterface tri = (TestRemoteInterface) clientconn.getPeerRemoteObject("foo.bar.Test", "/Test"); + System.out.println("Got Remote Object: " + tri); + /** Call the remote object and get a response. */ + String rname = tri.getName(); + System.out.println("Got Remote Name: " + rname); + + Map svmmap = tri.svm(); + System.out.println(svmmap.toString()); + if (!"{ Parameters => [{ Name => [Joe],Password => [abcdef] }] }".equals(svmmap.toString())) + fail("incorrect reply from svm"); + + Path path = new Path("/nonexistantwooooooo"); + Path p = tri.pathrv(path); + System.out.println(path.toString() + " => " + p.toString()); + if (!path.equals(p)) fail("pathrv incorrect"); + List paths = new Vector(); + paths.add(path); + List ps = tri.pathlistrv(paths); + System.out.println(paths.toString() + " => " + ps.toString()); + if (!paths.equals(ps)) fail("pathlistrv incorrect"); + Map pathm = new HashMap(); + pathm.put(path, path); + Map pm = tri.pathmaprv(pathm); + System.out.println(pathm.toString() + " => " + pm.toString()); + System.out.println(pm.containsKey(path) + " " + pm.get(path) + " " + path.equals(pm.get(path))); + System.out.println(pm.containsKey(p) + " " + pm.get(p) + " " + p.equals(pm.get(p))); + for (Path q : pm.keySet()) { + System.out.println(q); + System.out.println(pm.get(q)); + } + if (!pm.containsKey(path) || !path.equals(pm.get(path))) fail("pathmaprv incorrect"); + + serverconn.sendSignal(new TestSignalInterface.TestPathSignal("/Test", path, paths, pathm)); + + Collator col = Collator.getInstance(); + col.setDecomposition(Collator.FULL_DECOMPOSITION); + col.setStrength(Collator.PRIMARY); + if (0 != col.compare("This Is A UTF-8 Name: ﺱ !!", rname)) + fail("getName return value incorrect"); + System.out.println("sending it to sleep"); + tri.waitawhile(); + System.out.println("testing floats"); + if (17.093f != tri.testfloat(new float[]{17.093f, -23f, 0.0f, 31.42f})) + fail("testfloat returned the wrong thing"); + System.out.println("Structs of Structs"); + List> lli = new Vector>(); + List li = new Vector(); + li.add(1); + li.add(2); + li.add(3); + lli.add(li); + lli.add(li); + lli.add(li); + TestStruct3 ts3 = new TestStruct3(new TestStruct2(new Vector(), new Variant(0)), lli); + int[][] out = tri.teststructstruct(ts3); + if (out.length != 3) fail("teststructstruct returned the wrong thing: " + Arrays.deepToString(out)); + for (int[] o : out) + if (o.length != 3 + || o[0] != 1 + || o[1] != 2 + || o[2] != 3) fail("teststructstruct returned the wrong thing: " + Arrays.deepToString(out)); + + System.out.println("frobnicating"); + List ls = new Vector(); + ls.add(2L); + ls.add(5L); + ls.add(71L); + Map mus = new HashMap(); + mus.put(new UInt16(4), (short) 5); + mus.put(new UInt16(5), (short) 6); + mus.put(new UInt16(6), (short) 7); + Map> msmus = new HashMap>(); + msmus.put("stuff", mus); + int rint = tri.frobnicate(ls, msmus, 13); + if (-5 != rint) + fail("frobnicate return value incorrect"); + + System.out.println("Doing stuff asynchronously with callback"); + clientconn.callWithCallback(tri, "getName", new callbackhandler()); + System.out.println("Doing stuff asynchronously with callback, which throws an error"); + clientconn.callWithCallback(tri, "getNameAndThrow", new callbackhandler()); + + /** call something that throws */ + try { + System.out.println("Throwing stuff"); + tri.throwme(); + test.fail("Method Execution should have failed"); + } catch (TestException Te) { + System.out.println("Remote Method Failed with: " + Te.getClass().getName() + " " + Te.getMessage()); + if (!Te.getMessage().equals("test")) + test.fail("Error message was not correct"); + } + + /* Test type signatures */ + Vector ts = new Vector(); + Marshalling.getJavaType("ya{si}", ts, -1); + tri.sig(ts.toArray(new Type[0])); + + tri.newpathtest(new Path("/new/path/test")); + + /** Try and call an invalid remote object */ + try { + System.out.println("Calling Method2"); + tri = clientconn.getRemoteObject("foo.bar.NotATest", "/Moofle", TestRemoteInterface.class); + System.out.println("Got Remote Name: " + tri.getName()); + test.fail("Method Execution should have failed"); + } catch (ServiceUnknown SU) { + System.out.println("Remote Method Failed with: " + SU.getClass().getName() + " " + SU.getMessage()); + } + + /** Try and call an invalid remote object */ + try { + System.out.println("Calling Method3"); + tri = clientconn.getRemoteObject("foo.bar.Test", "/Moofle", TestRemoteInterface.class); + System.out.println("Got Remote Name: " + tri.getName()); + test.fail("Method Execution should have failed"); + } catch (UnknownObject UO) { + System.out.println("Remote Method Failed with: " + UO.getClass().getName() + " " + UO.getMessage()); + } + + /** Try and call an explicitly unexported object */ + try { + System.out.println("Calling Method4"); + tri = clientconn.getRemoteObject("foo.bar.Test", "/BadTest", TestRemoteInterface.class); + System.out.println("Got Remote Name: " + tri.getName()); + test.fail("Method Execution should have failed"); + } catch (UnknownObject UO) { + System.out.println("Remote Method Failed with: " + UO.getClass().getName() + " " + UO.getMessage()); + } + + /** Try and call an implicitly unexported object */ + try { + System.out.println("Calling Method5"); + tri = clientconn.getRemoteObject("foo.bar.Test", "/BadTest2", TestRemoteInterface.class); + System.out.println("Got Remote Name: " + tri.getName()); + test.fail("Method Execution should have failed"); + } catch (UnknownObject UO) { + System.out.println("Remote Method Failed with: " + UO.getClass().getName() + " " + UO.getMessage()); + } + + System.out.println("Calling Method6"); + tri = clientconn.getRemoteObject("foo.bar.Test", "/FallbackTest/0/1", TestRemoteInterface.class); + intro = clientconn.getRemoteObject("foo.bar.Test", "/FallbackTest/0/4", Introspectable.class); + System.out.println("Got Fallback Name: " + tri.getName()); + System.out.println("Fallback Introspection Data: \n" + intro.Introspect()); + + System.out.println("Testing Properties returning Paths"); + Properties prop = clientconn.getRemoteObject("foo.bar.Test", "/Test", Properties.class); + Path prv = (Path) prop.Get("foo.bar", "foo"); + System.out.println("Got path " + prv); + System.out.println("Calling Method7--9"); + /** This gets a remote object matching our bus name and exported object path. */ + TestRemoteInterface2 tri2 = clientconn.getRemoteObject("foo.bar.Test", "/Test", TestRemoteInterface2.class); + System.out.print("Calling the other introspect method: "); + String intro2 = tri2.Introspect(); + System.out.println(intro2); + if (0 != col.compare("Not XML", intro2)) + fail("Introspect return value incorrect"); + + /** Call the remote object and get a response. */ + TestTuple, Boolean> rv = tri2.show(234); + System.out.println("Show returned: " + rv); + if (!serverconn.getUniqueName().equals(rv.a) || + 1 != rv.b.size() || + 1953 != rv.b.get(0) || + true != rv.c.booleanValue()) + fail("show return value incorrect (" + rv.a + "," + rv.b + "," + rv.c + ")"); + + System.out.println("Doing stuff asynchronously"); + DBusAsyncReply stuffreply = (DBusAsyncReply) clientconn.callMethodAsync(tri2, "dostuff", new TestStruct("bar", new UInt32(52), new Variant(new Boolean(true)))); + + System.out.println("Checking bools"); + if (tri2.check()) fail("bools are broken"); + + List l = new Vector(); + l.add("hi"); + l.add("hello"); + l.add("hej"); + l.add("hey"); + l.add("aloha"); + System.out.println("Sampling Arrays:"); + List is = tri2.sampleArray(l, new Integer[]{1, 5, 7, 9}, new long[]{2, 6, 8, 12}); + System.out.println("sampleArray returned an array:"); + for (Integer i : is) + System.out.println("--" + i); + if (is.size() != 5 || + is.get(0).intValue() != -1 || + is.get(1).intValue() != -5 || + is.get(2).intValue() != -7 || + is.get(3).intValue() != -12 || + is.get(4).intValue() != -18) + fail("sampleArray return value incorrect"); + + System.out.println("Get This"); + if (!tclass.equals(tri2.getThis(tri2))) + fail("Didn't get the correct this"); + + Boolean b = stuffreply.getReply(); + System.out.println("Do stuff replied " + b); + if (true != b.booleanValue()) + fail("dostuff return value incorrect"); + + System.out.print("Sending Array Signal..."); + /** This creates an instance of the Test Signal, with the given object path, signal name and parameters, and broadcasts in on the Bus. */ + List tsl = new Vector(); + tsl.add(new TestStruct2(l, new Variant(new UInt64(567)))); + Map tsm = new HashMap(); + tsm.put(new UInt32(1), new TestStruct2(l, new Variant(new UInt64(678)))); + tsm.put(new UInt32(42), new TestStruct2(l, new Variant(new UInt64(789)))); + serverconn.sendSignal(new TestSignalInterface.TestArraySignal("/Test", tsl, tsm)); + + System.out.println("done"); + + System.out.print("testing custom serialization..."); + Vector v = new Vector(); + v.add(1); + v.add(2); + v.add(3); + TestSerializable s = new TestSerializable(1, "woo", v); + s = tri2.testSerializable((byte) 12, s, 13); + System.out.print("returned: " + s); + if (s.getInt() != 1 || + !s.getString().equals("woo") || + s.getVector().size() != 3 || + s.getVector().get(0) != 1 || + s.getVector().get(1) != 2 || + s.getVector().get(2) != 3) + fail("Didn't get back the same TestSerializable"); + + System.out.println("done"); + + System.out.print("testing complex variants..."); + Map m = new HashMap(); + m.put("cow", "moo"); + tri2.complexv(new Variant(m, "a{ss}")); + System.out.println("done"); + + System.out.print("testing recursion..."); + + if (0 != col.compare("This Is A UTF-8 Name: ﺱ !!", tri2.recursionTest())) fail("recursion test failed"); + + System.out.println("done"); + + System.out.print("testing method overloading..."); + tri = clientconn.getRemoteObject("foo.bar.Test", "/Test", TestRemoteInterface.class); + if (1 != tri2.overload("foo")) test.fail("wrong overloaded method called"); + if (2 != tri2.overload((byte) 0)) test.fail("wrong overloaded method called"); + if (3 != tri2.overload()) test.fail("wrong overloaded method called"); + if (4 != tri.overload()) test.fail("wrong overloaded method called"); + System.out.println("done"); + + System.out.print("reg13291..."); + byte[] as = new byte[10]; + for (int i = 0; i < 10; i++) + as[i] = (byte) (100 - i); + tri.reg13291(as, as); + System.out.println("done"); + + System.out.print("Testing nested lists..."); + lli = new Vector>(); + li = new Vector(); + li.add(1); + lli.add(li); + List> reti = tri2.checklist(lli); + if (reti.size() != 1 || + reti.get(0).size() != 1 || + reti.get(0).get(0) != 1) + test.fail("Failed to check nested lists"); + System.out.println("done"); + + System.out.print("Testing dynamic object creation..."); + TestNewInterface tni = tri2.getNew(); + System.out.print(tni.getName() + " "); + System.out.println("done"); + + /* send an object in a signal */ + serverconn.sendSignal(new TestSignalInterface.TestObjectSignal("/foo/bar/Wibble", tclass)); + + /** Pause while we wait for the DBus messages to go back and forth. */ + Thread.sleep(1000); + + // check that bus name set has been trimmed + if (peers.size() != 1) fail("peers hasn't been trimmed"); + if (!peers.contains("org.freedesktop.DBus")) fail("peers contains the wrong name"); + + System.out.println("Checking for outstanding errors"); + DBusExecutionException DBEe = serverconn.getError(); + if (null != DBEe) throw DBEe; + DBEe = clientconn.getError(); + if (null != DBEe) throw DBEe; + + System.out.println("Disconnecting"); + /** Disconnect from the bus. */ + clientconn.disconnect(); + serverconn.disconnect(); + + System.out.println("Trying to do things after disconnection"); + + /** Remove sig handler */ + clientconn.removeSigHandler(TestSignalInterface.TestSignal.class, sigh); + + /** Call a method when disconnected */ + try { + System.out.println("getName() suceeded and returned: " + tri.getName()); + fail("Should not succeed when disconnected"); + } catch (NotConnected NC) { + System.out.println("getName() failed with exception " + NC); + } + clientconn = null; + serverconn = null; + + if (!done1) fail("Signal handler 1 failed to be run"); + if (!done2) fail("Signal handler 2 failed to be run"); + if (!done3) fail("Signal handler 3 failed to be run"); + if (!done4) fail("Callback handler failed to be run"); + if (!done5) fail("Signal handler R failed to be run"); + if (!done6) fail("Disconnect handler failed to be run"); + if (!done7) fail("Signal handler E failed to be run"); + if (!done8) fail("Error callback handler failed to be run"); + + } catch (Exception e) { + e.printStackTrace(); + fail("Unexpected Exception Occurred: " + e); + } + } +} diff --git a/federation/sssd/src/test/java/org/freedesktop/dbus/test_low_level.java b/federation/sssd/src/test/java/org/freedesktop/dbus/test_low_level.java new file mode 100644 index 0000000000..71699eb0a4 --- /dev/null +++ b/federation/sssd/src/test/java/org/freedesktop/dbus/test_low_level.java @@ -0,0 +1,50 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +import cx.ath.matthew.debug.Debug; + +public class test_low_level { + public static void main(String[] args) throws Exception { + Debug.setHexDump(true); + String addr = System.getenv("DBUS_SESSION_BUS_ADDRESS"); + Debug.print(addr); + BusAddress address = new BusAddress(addr); + Debug.print(address); + + Transport conn = new Transport(address); + + Message m = new MethodCall("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "Hello", (byte) 0, null); + conn.mout.writeMessage(m); + m = conn.min.readMessage(); + Debug.print(m.getClass()); + Debug.print(m); + m = conn.min.readMessage(); + Debug.print(m.getClass()); + Debug.print(m); + m = conn.min.readMessage(); + Debug.print("" + m); + m = new MethodCall("org.freedesktop.DBus", "/", null, "Hello", (byte) 0, null); + conn.mout.writeMessage(m); + m = conn.min.readMessage(); + Debug.print(m); + + m = new MethodCall("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "RequestName", (byte) 0, "su", "org.testname", 0); + conn.mout.writeMessage(m); + m = conn.min.readMessage(); + Debug.print(m); + m = new DBusSignal(null, "/foo", "org.foo", "Foo", null); + conn.mout.writeMessage(m); + m = conn.min.readMessage(); + Debug.print(m); + conn.disconnect(); + } +} diff --git a/federation/sssd/src/test/java/org/freedesktop/dbus/two_part_test_client.java b/federation/sssd/src/test/java/org/freedesktop/dbus/two_part_test_client.java new file mode 100644 index 0000000000..75f4bd843a --- /dev/null +++ b/federation/sssd/src/test/java/org/freedesktop/dbus/two_part_test_client.java @@ -0,0 +1,43 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +public class two_part_test_client { + public static class two_part_test_object implements TwoPartObject { + public boolean isRemote() { + return false; + } + + public String getName() { + System.out.println("client name"); + return toString(); + } + } + + public static void main(String[] args) throws Exception { + System.out.println("get conn"); + DBusConnection conn = DBusConnection.getConnection(DBusConnection.SESSION); + System.out.println("get remote"); + TwoPartInterface remote = conn.getRemoteObject("org.freedesktop.dbus.test.two_part_server", "/", TwoPartInterface.class); + System.out.println("get object"); + TwoPartObject o = remote.getNew(); + System.out.println("get name"); + System.out.println(o.getName()); + two_part_test_object tpto = new two_part_test_object(); + conn.exportObject("/TestObject", tpto); + conn.sendSignal(new TwoPartInterface.TwoPartSignal("/FromObject", tpto)); + try { + Thread.sleep(1000); + } catch (InterruptedException Ie) { + } + conn.disconnect(); + } +} diff --git a/federation/sssd/src/test/java/org/freedesktop/dbus/two_part_test_server.java b/federation/sssd/src/test/java/org/freedesktop/dbus/two_part_test_server.java new file mode 100644 index 0000000000..c0b1ded1eb --- /dev/null +++ b/federation/sssd/src/test/java/org/freedesktop/dbus/two_part_test_server.java @@ -0,0 +1,62 @@ +/* + D-Bus Java Implementation + Copyright (c) 2005-2006 Matthew Johnson + + This program is free software; you can redistribute it and/or modify it + under the terms of either the GNU Lesser General Public License Version 2 or the + Academic Free Licence Version 2.1. + + Full licence texts are included in the COPYING file with this program. +*/ +package org.freedesktop.dbus; + +public class two_part_test_server implements TwoPartInterface, DBusSigHandler { + public class two_part_test_object implements TwoPartObject { + public boolean isRemote() { + return false; + } + + public String getName() { + System.out.println("give name"); + return toString(); + } + } + + private DBusConnection conn; + + public two_part_test_server(DBusConnection conn) { + this.conn = conn; + } + + public boolean isRemote() { + return false; + } + + public TwoPartObject getNew() { + TwoPartObject o = new two_part_test_object(); + System.out.println("export new"); + try { + conn.exportObject("/12345", o); + } catch (Exception e) { + } + System.out.println("give new"); + return o; + } + + public void handle(TwoPartInterface.TwoPartSignal s) { + System.out.println("Got: " + s.o); + } + + public static void main(String[] args) throws Exception { + DBusConnection conn = DBusConnection.getConnection(DBusConnection.SESSION); + conn.requestBusName("org.freedesktop.dbus.test.two_part_server"); + two_part_test_server server = new two_part_test_server(conn); + conn.exportObject("/", server); + conn.addSigHandler(TwoPartInterface.TwoPartSignal.class, server); + while (true) try { + Thread.sleep(10000); + } catch (InterruptedException Ie) { + } + } +} + diff --git a/federation/sssd/src/test/java/org/jvnet/libpam/InteractiveTester.java b/federation/sssd/src/test/java/org/jvnet/libpam/InteractiveTester.java new file mode 100644 index 0000000000..47fef180b5 --- /dev/null +++ b/federation/sssd/src/test/java/org/jvnet/libpam/InteractiveTester.java @@ -0,0 +1,95 @@ +/* + * The MIT License + * + * Copyright (c) 2009, Sun Microsystems, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.jvnet.libpam; + +import junit.framework.TestCase; + +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +public class InteractiveTester extends TestCase { + public InteractiveTester(String testName) { + super(testName); + } + + public void testPositiveCase() throws Exception { + for (int i = 0; i < 1000; i++) + testOne(); + } + + public void testOne() throws Exception { + UnixUser u = new PAM("sshd").authenticate(System.getProperty("user.name"), System.getProperty("password")); + if (!printOnce) { + System.out.println(u.getUID()); + System.out.println(u.getGroups()); + printOnce = true; + } + } + + public void testGetGroups() throws Exception { + System.out.println(new PAM("sshd").getGroupsOfUser(System.getProperty("user.name"))); + } + + public void testConcurrent() throws Exception { + ExecutorService es = Executors.newFixedThreadPool(10); + Set> result = new HashSet>(); + for (int i = 0; i < 1000; i++) { + result.add(es.submit(new Callable() { + public Object call() throws Exception { + testOne(); + return null; + } + })); + } + // wait for completion + for (Future f : result) { + f.get(); + } + es.shutdown(); + } + + public void testNegative() throws Exception { + try { + new PAM("sshd").authenticate("bogus", "bogus"); + fail("expected a failure"); + } catch (PAMException e) { + // yep + } + } + + public static void main(String[] args) throws Exception { + UnixUser u = new PAM("sshd").authenticate(args[0], args[1]); + System.out.println(u.getUID()); + System.out.println(u.getGroups()); + System.out.println(u.getGecos()); + System.out.println(u.getDir()); + System.out.println(u.getShell()); + } + + private boolean printOnce; +} diff --git a/federation/sssd/src/test/java/org/jvnet/libpam/UnixUserTest.java b/federation/sssd/src/test/java/org/jvnet/libpam/UnixUserTest.java new file mode 100644 index 0000000000..4b3e5f5672 --- /dev/null +++ b/federation/sssd/src/test/java/org/jvnet/libpam/UnixUserTest.java @@ -0,0 +1,52 @@ +/* + * The MIT License + * + * Copyright (c) 2014, R. Tyler Croy + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.jvnet.libpam; + +import junit.framework.Assert; +import org.junit.Before; +import org.junit.Test; + +public class UnixUserTest { + private UnixUser user = null; + + @Before + public void setUp() throws PAMException { + user = new UnixUser("root"); + } + + @Test + public void testGetUserName() { + Assert.assertEquals("root", user.getUserName()); + } + + @Test + public void testGetDir() { + Assert.assertNotNull(user.getDir()); + } + + @Test + public void testGetUID() { + Assert.assertEquals(0, user.getUID()); + } +} diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java index 2ea12fd839..49bcac633b 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java @@ -125,29 +125,32 @@ public class UserAdapter implements UserModel, JpaModel { @Override public void setSingleAttribute(String name, String value) { - boolean found = false; + String firstExistingAttrId = null; List toRemove = new ArrayList<>(); for (UserAttributeEntity attr : user.getAttributes()) { if (attr.getName().equals(name)) { - if (!found) { + if (firstExistingAttrId == null) { attr.setValue(value); - found = true; + firstExistingAttrId = attr.getId(); } else { toRemove.add(attr); } } } - for (UserAttributeEntity attr : toRemove) { - em.remove(attr); - user.getAttributes().remove(attr); - } + if (firstExistingAttrId != null) { + // Remove attributes through HQL to avoid StaleUpdateException + Query query = em.createNamedQuery("deleteUserAttributesOtherThan"); + query.setParameter("attrId", firstExistingAttrId); + query.setParameter("userId", user.getId()); + int numUpdated = query.executeUpdate(); - if (found) { - return; - } + // Remove attribute from local entity + user.getAttributes().removeAll(toRemove); + } else { - persistAttributeValue(name, value); + persistAttributeValue(name, value); + } } @Override @@ -178,6 +181,15 @@ public class UserAdapter implements UserModel, JpaModel { query.setParameter("name", name); query.setParameter("userId", user.getId()); int numUpdated = query.executeUpdate(); + + // KEYCLOAK-3494 : Also remove attributes from local user entity + List toRemove = new ArrayList<>(); + for (UserAttributeEntity attr : user.getAttributes()) { + if (attr.getName().equals(name)) { + toRemove.add(attr); + } + } + user.getAttributes().removeAll(toRemove); } @Override diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserAttributeEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserAttributeEntity.java index 2b5d35cc63..16b155b75b 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserAttributeEntity.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserAttributeEntity.java @@ -43,6 +43,7 @@ import java.util.Set; @NamedQuery(name="getAttributesByNameAndValue", query="select attr from UserAttributeEntity attr where attr.name = :name and attr.value = :value"), @NamedQuery(name="deleteUserAttributesByRealm", query="delete from UserAttributeEntity attr where attr.user IN (select u from UserEntity u where u.realmId=:realmId)"), @NamedQuery(name="deleteUserAttributesByNameAndUser", query="delete from UserAttributeEntity attr where attr.user.id = :userId and attr.name = :name"), + @NamedQuery(name="deleteUserAttributesOtherThan", query="delete from UserAttributeEntity attr where attr.user.id = :userId and attr.id <> :attrId"), @NamedQuery(name="deleteUserAttributesByRealmAndLink", query="delete from UserAttributeEntity attr where attr.user IN (select u from UserEntity u where u.realmId=:realmId and u.federationLink=:link)") }) @Table(name="USER_ATTRIBUTE") diff --git a/pom.xml b/pom.xml index 7208837e46..48e8a65d03 100755 --- a/pom.xml +++ b/pom.xml @@ -94,6 +94,7 @@ 1.3.7 1.0.2.Final 4.0.4 + 4.1.0 1.3.1b @@ -685,6 +686,17 @@ keycloak-kerberos-federation ${project.version} + + + org.keycloak + keycloak-sssd-federation + ${project.version} + + + net.java.dev.jna + jna + ${jna.version} + org.keycloak keycloak-ldap-federation diff --git a/server-spi/src/main/java/org/keycloak/models/Constants.java b/server-spi/src/main/java/org/keycloak/models/Constants.java index 916565aaa4..42982f6ace 100755 --- a/server-spi/src/main/java/org/keycloak/models/Constants.java +++ b/server-spi/src/main/java/org/keycloak/models/Constants.java @@ -50,5 +50,5 @@ public interface Constants { String KEY = "key"; // Prefix for user attributes used in various "context"data maps - public static final String USER_ATTRIBUTES_PREFIX = "user.attributes."; + String USER_ATTRIBUTES_PREFIX = "user.attributes."; } diff --git a/services/src/main/java/org/keycloak/forms/account/freemarker/model/AccountBean.java b/services/src/main/java/org/keycloak/forms/account/freemarker/model/AccountBean.java index acb1514388..857bfc04d9 100755 --- a/services/src/main/java/org/keycloak/forms/account/freemarker/model/AccountBean.java +++ b/services/src/main/java/org/keycloak/forms/account/freemarker/model/AccountBean.java @@ -18,6 +18,7 @@ package org.keycloak.forms.account.freemarker.model; import org.jboss.logging.Logger; +import org.keycloak.models.Constants; import org.keycloak.models.UserModel; import javax.ws.rs.core.MultivaluedMap; @@ -55,8 +56,8 @@ public class AccountBean { if (profileFormData != null) { for (String key : profileFormData.keySet()) { - if (key.startsWith("user.attributes.")) { - String attribute = key.substring("user.attributes.".length()); + if (key.startsWith(Constants.USER_ATTRIBUTES_PREFIX)) { + String attribute = key.substring(Constants.USER_ATTRIBUTES_PREFIX.length()); attributes.put(attribute, profileFormData.getFirst(key)); } } diff --git a/testsuite/integration-arquillian/servers/auth-server/jboss/common/keycloak-server-subsystem.xsl b/testsuite/integration-arquillian/servers/auth-server/jboss/common/keycloak-server-subsystem.xsl index f32c036600..664eecb1ae 100644 --- a/testsuite/integration-arquillian/servers/auth-server/jboss/common/keycloak-server-subsystem.xsl +++ b/testsuite/integration-arquillian/servers/auth-server/jboss/common/keycloak-server-subsystem.xsl @@ -36,6 +36,11 @@ + + + org.keycloak.testsuite.integration-arquillian-testsuite-providers + + @@ -46,6 +51,14 @@ module:org.keycloak.testsuite.integration-arquillian-testsuite-providers + + + + + + + + diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/keycloak-themes.json b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/keycloak-themes.json new file mode 100644 index 0000000000..03978db442 --- /dev/null +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/META-INF/keycloak-themes.json @@ -0,0 +1,6 @@ +{ + "themes": [{ + "name" : "address", + "types": [ "admin", "account", "login" ] + }] +} \ No newline at end of file diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/theme/address/account/account.ftl b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/theme/address/account/account.ftl new file mode 100755 index 0000000000..d2a6af16e0 --- /dev/null +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/theme/address/account/account.ftl @@ -0,0 +1,114 @@ +<#import "template.ftl" as layout> +<@layout.mainLayout active='account' bodyClass='user'; section> + +
+
+

${msg("editAccountHtmlTtile")}

+
+
+ * ${msg("requiredFields")} +
+
+ +
+ + + +
+
+ <#if realm.editUsernameAllowed>* +
+ +
+ disabled="disabled" value="${(account.username!'')?html}"/> +
+
+ +
+
+ * +
+ +
+ +
+
+ +
+
+ * +
+ +
+ +
+
+ +
+
+ * +
+ +
+ +
+
+ +
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+ + + + + \ No newline at end of file diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/theme/address/account/theme.properties b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/theme/address/account/theme.properties new file mode 100644 index 0000000000..3e50437b9a --- /dev/null +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/theme/address/account/theme.properties @@ -0,0 +1,18 @@ +# +# Copyright 2016 Red Hat, Inc. and/or its affiliates +# and other contributors as indicated by the @author tags. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +parent=keycloak \ No newline at end of file diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/theme/address/admin/resources/partials/user-attributes.html b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/theme/address/admin/resources/partials/user-attributes.html new file mode 100755 index 0000000000..af512de26e --- /dev/null +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/theme/address/admin/resources/partials/user-attributes.html @@ -0,0 +1,72 @@ + + +
+ + + + +
+
+ +
+ +
+ Street address. +
+
+ +
+ +
+ City or locality. +
+
+ +
+ +
+ State, province, prefecture, or region. +
+
+ +
+ +
+ Zip code or postal code. +
+
+ +
+ +
+ Country name. +
+ +
+
+ + +
+
+
+
+ + diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/theme/address/admin/theme.properties b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/theme/address/admin/theme.properties new file mode 100644 index 0000000000..3e50437b9a --- /dev/null +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/theme/address/admin/theme.properties @@ -0,0 +1,18 @@ +# +# Copyright 2016 Red Hat, Inc. and/or its affiliates +# and other contributors as indicated by the @author tags. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +parent=keycloak \ No newline at end of file diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/theme/address/login/login-update-profile.ftl b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/theme/address/login/login-update-profile.ftl new file mode 100755 index 0000000000..e02a340405 --- /dev/null +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/theme/address/login/login-update-profile.ftl @@ -0,0 +1,95 @@ +<#import "template.ftl" as layout> +<@layout.registrationLayout; section> + <#if section = "title"> + ${msg("loginProfileTitle")} + <#elseif section = "header"> + ${msg("loginProfileTitle")} + <#elseif section = "form"> +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+ + +
+
+
+
+
+ +
+ +
+
+
+ + \ No newline at end of file diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/theme/address/login/register.ftl b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/theme/address/login/register.ftl new file mode 100755 index 0000000000..3247305cf9 --- /dev/null +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/theme/address/login/register.ftl @@ -0,0 +1,131 @@ +<#import "template.ftl" as layout> +<@layout.registrationLayout; section> + <#if section = "title"> + ${msg("registerWithTitle",(realm.name!''))} + <#elseif section = "header"> + ${msg("registerWithTitleHtml",(realm.name!''))} + <#elseif section = "form"> +
+ <#if !realm.registrationEmailAsUsername> +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + <#if passwordRequired> +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+
+
+ +
+ +
+ +
+
+ <#if recaptchaRequired??> +
+
+
+
+
+ + +
+ + +
+ +
+
+
+ + \ No newline at end of file diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/theme/address/login/theme.properties b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/theme/address/login/theme.properties new file mode 100644 index 0000000000..3e50437b9a --- /dev/null +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/resources/theme/address/login/theme.properties @@ -0,0 +1,18 @@ +# +# Copyright 2016 Red Hat, Inc. and/or its affiliates +# and other contributors as indicated by the @author tags. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +parent=keycloak \ No newline at end of file diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountUpdateProfilePage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountUpdateProfilePage.java index 2159c2f6ff..2a77847fac 100755 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountUpdateProfilePage.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountUpdateProfilePage.java @@ -17,7 +17,9 @@ package org.keycloak.testsuite.pages; +import org.keycloak.models.Constants; import org.keycloak.services.resources.RealmsResource; +import org.openqa.selenium.By; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; @@ -96,6 +98,14 @@ public class AccountUpdateProfilePage extends AbstractAccountPage { submitButton.click(); } + public void updateAttribute(String attrName, String attrValue) { + WebElement attrElement = findAttributeInputElement(attrName); + attrElement.clear(); + attrElement.sendKeys(attrValue); + submitButton.click(); + } + + public void clickCancel() { cancelButton.click(); } @@ -117,6 +127,11 @@ public class AccountUpdateProfilePage extends AbstractAccountPage { return emailInput.getAttribute("value"); } + public String getAttribute(String attrName) { + WebElement attrElement = findAttributeInputElement(attrName); + return attrElement.getAttribute("value"); + } + public boolean isCurrent() { return driver.getTitle().contains("Account Management") && driver.getPageSource().contains("Edit Account"); } @@ -140,4 +155,9 @@ public class AccountUpdateProfilePage extends AbstractAccountPage { public boolean isPasswordUpdateSupported() { return driver.getPageSource().contains(getPath() + "/password"); } + + private WebElement findAttributeInputElement(String attrName) { + String attrId = Constants.USER_ATTRIBUTES_PREFIX + attrName; + return driver.findElement(By.id(attrId)); + } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/custom/CustomThemeTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/custom/CustomThemeTest.java new file mode 100644 index 0000000000..810d9c2084 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/custom/CustomThemeTest.java @@ -0,0 +1,86 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.account.custom; + +import org.jboss.arquillian.graphene.page.Page; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.keycloak.events.Details; +import org.keycloak.events.EventType; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.representations.idm.UserRepresentation; +import org.keycloak.testsuite.AssertEvents; +import org.keycloak.testsuite.TestRealmKeycloakTest; +import org.keycloak.testsuite.account.AccountTest; +import org.keycloak.testsuite.pages.AccountUpdateProfilePage; +import org.keycloak.testsuite.pages.LoginPage; +import org.keycloak.testsuite.util.RealmBuilder; +import org.keycloak.testsuite.util.UserBuilder; + +/** + * @author Marek Posolda + */ +public class CustomThemeTest extends TestRealmKeycloakTest { + + @Override + public void configureTestRealm(RealmRepresentation testRealm) { + testRealm.setAccountTheme("address"); + + UserRepresentation user2 = UserBuilder.create() + .enabled(true) + .username("test-user-no-access@localhost") + .email("test-user-no-access@localhost") + .password("password") + .build(); + + RealmBuilder.edit(testRealm) + .user(user2); + } + + @Rule + public AssertEvents events = new AssertEvents(this); + + @Page + protected LoginPage loginPage; + + @Page + protected AccountUpdateProfilePage profilePage; + + // KEYCLOAK-3494 + @Test + public void changeProfile() throws Exception { + profilePage.open(); + loginPage.login("test-user@localhost", "password"); + + events.expectLogin().client("account").detail(Details.REDIRECT_URI, AccountTest.ACCOUNT_REDIRECT).assertEvent(); + + Assert.assertEquals("test-user@localhost", profilePage.getEmail()); + Assert.assertEquals("", profilePage.getAttribute("street")); + + profilePage.updateAttribute("street", "Elm 1"); + Assert.assertEquals("Elm 1", profilePage.getAttribute("street")); + + profilePage.updateAttribute("street", "Elm 2"); + Assert.assertEquals("Elm 2", profilePage.getAttribute("street")); + + events.expectAccount(EventType.UPDATE_PROFILE).assertEvent(); + } + + +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationTest.java index 8cef8a1bbc..7a939c3518 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/UserFederationTest.java @@ -51,7 +51,7 @@ public class UserFederationTest extends AbstractAdminTest { @Test public void testProviderFactories() { List providerFactories = userFederation().getProviderFactories(); - Assert.assertNames(providerFactories, "ldap", "kerberos", "dummy", "dummy-configurable"); + Assert.assertNames(providerFactories, "ldap", "kerberos", "dummy", "dummy-configurable", "sssd"); // Builtin provider without properties UserFederationProviderFactoryRepresentation ldapProvider = userFederation().getProviderFactory("ldap"); diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml index aca1d78278..92187bc456 100755 --- a/testsuite/integration/pom.xml +++ b/testsuite/integration/pom.xml @@ -166,6 +166,14 @@ org.keycloak federation-properties-example
+ + + + org.keycloak.testsuite + integration-arquillian-testsuite-providers + ${project.version} + + org.jboss.logging jboss-logging diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/DummyUserFederationProvider.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/DummyUserFederationProvider.java deleted file mode 100755 index 0669da47db..0000000000 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/DummyUserFederationProvider.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.keycloak.testsuite; - -import org.keycloak.models.CredentialValidationOutput; -import org.keycloak.models.GroupModel; -import org.keycloak.models.RealmModel; -import org.keycloak.models.RoleModel; -import org.keycloak.models.UserCredentialModel; -import org.keycloak.models.UserFederationProvider; -import org.keycloak.models.UserModel; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * @author Bill Burke - * @version $Revision: 1 $ - */ -public class DummyUserFederationProvider implements UserFederationProvider { - - private final Map users; - - public DummyUserFederationProvider(Map users) { - this.users = users; - } - - @Override - public UserModel validateAndProxy(RealmModel realm, UserModel local) { - return local; - } - - @Override - public boolean synchronizeRegistrations() { - return true; - } - - @Override - public UserModel register(RealmModel realm, UserModel user) { - users.put(user.getUsername(), user); - return user; - } - - @Override - public boolean removeUser(RealmModel realm, UserModel user) { - return users.remove(user.getUsername()) != null; - } - - @Override - public UserModel getUserByUsername(RealmModel realm, String username) { - return users.get(username); - } - - @Override - public UserModel getUserByEmail(RealmModel realm, String email) { - return null; - } - - @Override - public List searchByAttributes(Map attributes, RealmModel realm, int maxResults) { - return Collections.emptyList(); - } - - @Override - public List getGroupMembers(RealmModel realm, GroupModel group, int firstResult, int maxResults) { - return Collections.emptyList(); - } - - @Override - public void preRemove(RealmModel realm) { - - } - - @Override - public void preRemove(RealmModel realm, RoleModel role) { - - } - - @Override - public void preRemove(RealmModel realm, GroupModel group) { - - } - - @Override - public boolean isValid(RealmModel realm, UserModel local) { - String username = local.getUsername(); - return users.containsKey(username); - } - - @Override - public Set getSupportedCredentialTypes(UserModel user) { - // Just user "test-user" is able to validate password with this federationProvider - if (user.getUsername().equals("test-user")) { - return Collections.singleton(UserCredentialModel.PASSWORD); - } else { - return Collections.emptySet(); - } - } - - @Override - public Set getSupportedCredentialTypes() { - return Collections.singleton(UserCredentialModel.PASSWORD); - } - - @Override - public boolean validCredentials(RealmModel realm, UserModel user, List input) { - if (user.getUsername().equals("test-user") && input.size() == 1) { - UserCredentialModel password = input.get(0); - if (password.getType().equals(UserCredentialModel.PASSWORD)) { - return "secret".equals(password.getValue()); - } - } - return false; - } - - @Override - public boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input) { - return validCredentials(realm, user, Arrays.asList(input)); - } - - @Override - public CredentialValidationOutput validCredentials(RealmModel realm, UserCredentialModel credential) { - return CredentialValidationOutput.failed(); - } - - @Override - public void close() { - - } -} diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/DummyUserFederationProviderFactory.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/DummyUserFederationProviderFactory.java deleted file mode 100755 index 4b49499137..0000000000 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/DummyUserFederationProviderFactory.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.keycloak.testsuite; - -import org.jboss.logging.Logger; -import org.keycloak.Config; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; -import org.keycloak.models.UserFederationProvider; -import org.keycloak.models.UserFederationProviderFactory; -import org.keycloak.models.UserFederationProviderModel; -import org.keycloak.models.UserFederationSyncResult; -import org.keycloak.models.UserModel; -import org.keycloak.provider.ConfiguredProvider; -import org.keycloak.provider.ProviderConfigProperty; - -import java.util.*; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * @author Bill Burke - * @version $Revision: 1 $ - */ -public class DummyUserFederationProviderFactory implements UserFederationProviderFactory, ConfiguredProvider { - - private static final Logger logger = Logger.getLogger(DummyUserFederationProviderFactory.class); - public static final String PROVIDER_NAME = "dummy"; - - private AtomicInteger fullSyncCounter = new AtomicInteger(); - private AtomicInteger changedSyncCounter = new AtomicInteger(); - - private Map users = new HashMap(); - - @Override - public UserFederationProvider getInstance(KeycloakSession session, UserFederationProviderModel model) { - return new DummyUserFederationProvider(users); - } - - @Override - public Set getConfigurationOptions() { - Set list = new HashSet(); - list.add("important.config"); - return list; - } - - @Override - public UserFederationProvider create(KeycloakSession session) { - return new DummyUserFederationProvider(users); - } - - @Override - public void init(Config.Scope config) { - - } - - @Override - public void postInit(KeycloakSessionFactory factory) { - - } - - @Override - public void close() { - - } - - @Override - public String getId() { - return PROVIDER_NAME; - } - - @Override - public UserFederationSyncResult syncAllUsers(KeycloakSessionFactory sessionFactory, String realmId, UserFederationProviderModel model) { - logger.info("syncAllUsers invoked"); - fullSyncCounter.incrementAndGet(); - return UserFederationSyncResult.empty(); - } - - @Override - public UserFederationSyncResult syncChangedUsers(KeycloakSessionFactory sessionFactory, String realmId, UserFederationProviderModel model, Date lastSync) { - logger.info("syncChangedUsers invoked"); - changedSyncCounter.incrementAndGet(); - return UserFederationSyncResult.empty(); - } - - public int getFullSyncCounter() { - return fullSyncCounter.get(); - } - - public int getChangedSyncCounter() { - return changedSyncCounter.get(); - } - - @Override - public String getHelpText() { - return "Dummy User Federation Provider Help Text"; - } - - @Override - public List getConfigProperties() { - - ProviderConfigProperty prop1 = new ProviderConfigProperty(); - prop1.setName("prop1"); - prop1.setLabel("Prop1"); - prop1.setDefaultValue("prop1Default"); - prop1.setHelpText("Prop1 HelpText"); - prop1.setType(ProviderConfigProperty.STRING_TYPE); - - ProviderConfigProperty prop2 = new ProviderConfigProperty(); - prop2.setName("prop2"); - prop2.setLabel("Prop2"); - prop2.setDefaultValue("true"); - prop2.setHelpText("Prop2 HelpText"); - prop2.setType(ProviderConfigProperty.BOOLEAN_TYPE); - - return Arrays.asList(prop1, prop2); - } -} diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractKeycloakIdentityProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractKeycloakIdentityProviderTest.java index 7683d0c0f4..f6a85e46d1 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractKeycloakIdentityProviderTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractKeycloakIdentityProviderTest.java @@ -45,8 +45,8 @@ import org.keycloak.models.UserFederationProviderModel; import org.keycloak.models.UserModel; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.services.Urls; -import org.keycloak.testsuite.DummyUserFederationProviderFactory; import org.keycloak.testsuite.broker.util.UserSessionStatusServlet; +import org.keycloak.testsuite.federation.DummyUserFederationProviderFactory; import org.openqa.selenium.By; import org.openqa.selenium.NoSuchElementException; diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/sync/SyncDummyUserFederationProviderFactory.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/sync/SyncDummyUserFederationProviderFactory.java index 5d327f4bbd..3831787aa1 100644 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/sync/SyncDummyUserFederationProviderFactory.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/sync/SyncDummyUserFederationProviderFactory.java @@ -32,7 +32,7 @@ import org.keycloak.models.UserFederationProviderModel; import org.keycloak.models.UserFederationSyncResult; import org.keycloak.models.UserModel; import org.keycloak.models.utils.KeycloakModelUtils; -import org.keycloak.testsuite.DummyUserFederationProviderFactory; +import org.keycloak.testsuite.federation.DummyUserFederationProviderFactory; /** * @author Marek Posolda diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/sync/SyncFederationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/sync/SyncFederationTest.java index 84a901a171..efa688c5a2 100644 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/sync/SyncFederationTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/sync/SyncFederationTest.java @@ -36,7 +36,7 @@ import org.keycloak.models.UserFederationProviderModel; import org.keycloak.models.UserFederationSyncResult; import org.keycloak.services.managers.RealmManager; import org.keycloak.services.managers.UsersSyncManager; -import org.keycloak.testsuite.DummyUserFederationProviderFactory; +import org.keycloak.testsuite.federation.DummyUserFederationProviderFactory; import org.keycloak.testsuite.rule.KeycloakRule; import org.keycloak.timer.TimerProvider; diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ConcurrentTransactionsTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ConcurrentTransactionsTest.java index b3ad0c4b5b..2fb2aaf7eb 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ConcurrentTransactionsTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ConcurrentTransactionsTest.java @@ -17,6 +17,7 @@ package org.keycloak.testsuite.model; +import java.util.Arrays; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReference; @@ -151,14 +152,17 @@ public class ConcurrentTransactionsTest extends AbstractModelTest { } - // KEYCLOAK-3296 + // KEYCLOAK-3296 , KEYCLOAK-3494 @Test public void removeUserAttribute() throws Exception { RealmModel realm = realmManager.createRealm("original"); KeycloakSession session = realmManager.getSession(); - UserModel user = session.users().addUser(realm, "john"); - user.setSingleAttribute("foo", "val1"); + UserModel john = session.users().addUser(realm, "john"); + john.setSingleAttribute("foo", "val1"); + + UserModel john2 = session.users().addUser(realm, "john2"); + john2.setAttribute("foo", Arrays.asList("val1", "val2")); final KeycloakSessionFactory sessionFactory = session.getKeycloakSessionFactory(); commit(); @@ -182,12 +186,18 @@ public class ConcurrentTransactionsTest extends AbstractModelTest { UserModel john = session.users().getUserByUsername("john", realm); String attrVal = john.getFirstAttribute("foo"); + UserModel john2 = session.users().getUserByUsername("john2", realm); + String attrVal2 = john2.getFirstAttribute("foo"); + // Wait until it's read in both threads readAttrLatch.countDown(); readAttrLatch.await(); - // Remove user attribute in both threads + // KEYCLOAK-3296 : Remove user attribute in both threads john.removeAttribute("foo"); + + // KEYCLOAK-3494 : Set single attribute in both threads + john2.setSingleAttribute("foo", "bar"); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserModelTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserModelTest.java index 4b3f78ef12..e2af2416d3 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserModelTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserModelTest.java @@ -211,6 +211,31 @@ public class UserModelTest extends AbstractModelTest { Assert.assertEquals("val23", attrVals.get(0)); } + // KEYCLOAK-3494 + @Test + public void testUpdateUserAttribute() throws Exception { + RealmModel realm = realmManager.createRealm("original"); + UserModel user = session.users().addUser(realm, "user"); + + user.setSingleAttribute("key1", "value1"); + + commit(); + + realm = realmManager.getRealmByName("original"); + user = session.users().getUserByUsername("user", realm); + + // Update attribute + List attrVals = new ArrayList<>(Arrays.asList( "val2" )); + user.setAttribute("key1", attrVals); + Map> allAttrVals = user.getAttributes(); + + // Ensure same transaction is able to see updated value + Assert.assertEquals(1, allAttrVals.size()); + Assert.assertEquals(allAttrVals.get("key1"), Arrays.asList("val2")); + + commit(); + } + @Test public void testSearchByString() { RealmModel realm = realmManager.createRealm("original"); diff --git a/testsuite/integration/src/test/resources/META-INF/services/org.keycloak.models.UserFederationProviderFactory b/testsuite/integration/src/test/resources/META-INF/services/org.keycloak.models.UserFederationProviderFactory index 1b4de7323d..d4a16d36c8 100755 --- a/testsuite/integration/src/test/resources/META-INF/services/org.keycloak.models.UserFederationProviderFactory +++ b/testsuite/integration/src/test/resources/META-INF/services/org.keycloak.models.UserFederationProviderFactory @@ -15,5 +15,4 @@ # limitations under the License. # -org.keycloak.testsuite.DummyUserFederationProviderFactory org.keycloak.testsuite.federation.sync.SyncDummyUserFederationProviderFactory \ No newline at end of file