diff --git a/README.md b/README.md
index 1489c46..86813a5 100644
--- a/README.md
+++ b/README.md
@@ -146,6 +146,30 @@ Add the following to your `config.php`:
[BasicAuth_0]: https://en.wikipedia.org/wiki/Basic_access_authentication
+
+XMPP (Prosody)
+----
+Authenticate Nextcloud users against a Prosody XMPP MySQL database.
+Prosody user and password need to be given for the Nextcloud login
+
+
+### Configuration
+Add the following to your `config.php`:
+
+ 'user_backends' => array (
+ 0 => array (
+ 'class' => 'OC_User_XMPP',
+ 'arguments' => array (
+ 0 => 'dbhost',
+ 1 => 'prosodydb',
+ 2 => 'dbuser',
+ 3 => 'dbuserpassword',
+ 4 => 'xmppdomain',
+ ),
+ ),
+ ),
+
+
Alternatives
------------
Other extensions allow connecting to external user databases directly via SQL, which may be faster:
diff --git a/appinfo/app.php b/appinfo/app.php
index f503578..c5b2519 100644
--- a/appinfo/app.php
+++ b/appinfo/app.php
@@ -3,3 +3,4 @@ OC::$CLASSPATH['OC_User_IMAP']='user_external/lib/imap.php';
OC::$CLASSPATH['OC_User_SMB']='user_external/lib/smb.php';
OC::$CLASSPATH['OC_User_FTP']='user_external/lib/ftp.php';
OC::$CLASSPATH['OC_User_BasicAuth']='user_external/lib/basicauth.php';
+OC::$CLASSPATH['OC_User_XMPP']='user_external/lib/xmpp.php';
diff --git a/appinfo/info.xml b/appinfo/info.xml
index b5c5f90..f6bfa1d 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -3,8 +3,8 @@
xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
user_external
External user support
- Use external user authentication methods like IMAP, SMB and FTP
- Use external user authentication methods like IMAP, SMB and FTP
+ Use external user authentication methods like IMAP, SMB, FTP, WebDAV, HTTP BasicAuth and XMPP
+ Use external user authentication methods like IMAP, SMB, FTP, WebDAV, HTTP BasicAuth and XMPP
0.6.0
agpl
Robin Appelman
diff --git a/lib/xmpp.php b/lib/xmpp.php
new file mode 100644
index 0000000..4bfe92c
--- /dev/null
+++ b/lib/xmpp.php
@@ -0,0 +1,102 @@
+
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+/**
+ * User authentication against a XMPP Prosody MySQL database
+ *
+ * @category Apps
+ * @package UserExternal
+ * @author Sebastian Sterk https://wiuwiu.de/Imprint
+ * @license http://www.gnu.org/licenses/agpl AGPL
+ */
+class OC_User_XMPP extends \OCA\user_external\Base {
+ private $host;
+ private $xmppDb;
+ private $xmppDbUser;
+ private $xmppDbPassword;
+ private $xmppDomain;
+
+ public function __construct($host, $xmppDb, $xmppDbUser, $xmppDbPassword, $xmppDomain) {
+ parent::__construct($host);
+ $this->host = $host;
+ $this->xmppDb = $xmppDb;
+ $this->xmppDbUser = $xmppDbUser;
+ $this->xmppDbPassword = $xmppDbPassword;
+ $this->xmppDomain = $xmppDomain;
+ }
+
+ public function hmacSha1($key, $data) {
+ if (strlen($key) > 64) {
+ $key = str_pad(sha1($key, true), 64, chr(0));
+ }
+ if (strlen($key) < 64) {
+ $key = str_pad($key, 64, chr(0));
+ }
+
+ $oPad = str_repeat(chr(0x5C), 64);
+ $iPad = str_repeat(chr(0x36), 64);
+
+ for ($i = 0; $i < strlen($key); $i++) {
+ $oPad[$i] = $oPad[$i] ^ $key[$i];
+ $iPad[$i] = $iPad[$i] ^ $key[$i];
+ }
+ return sha1($oPad.sha1($iPad.$data, true));
+ }
+
+ public function checkPassword($uid, $password){
+ $pdo = new PDO("mysql:host=$this->host;dbname=$this->xmppDb", $this->xmppDbUser, $this->xmppDbPassword);
+ if(isset($uid)
+ && isset($password)) {
+ if(!filter_var($uid, FILTER_VALIDATE_EMAIL)
+ || !strpos($uid, $this->xmppDomain)
+ || substr($uid, -strlen($this->xmppDomain)) !== $this->xmppDomain
+ ) {
+ return false;
+ }
+ $user = explode("@", $uid);
+ $userName = strtolower($user[0]);
+ $submittedPassword = $password;
+ $statement = $pdo->prepare("SELECT * FROM prosody WHERE user = :user AND host = :xmppDomain AND store = 'accounts'");
+ $result = $statement->execute(array(
+ 'user' => $userName,
+ 'xmppDomain' => $this->xmppDomain
+ ));
+ $user = $statement->fetchAll();
+ if(empty($user)) {
+ return false;
+ }
+ foreach ($user as $key){
+ if($key[3] === "salt") {
+ $internalSalt = $key['value'];
+ }
+ if($key[3] === "server_key") {
+ $internalServerKey = $key['value'];
+ }
+ if($key[3] === "stored_key") {
+ $internalStoredKey = $key['value'];
+ }
+ }
+ unset($user);
+ $internalIteration = '4096';
+
+ $newSaltedPassword = hash_pbkdf2('sha1', $submittedPassword, $internalSalt, $internalIteration, 0, true);
+ $newServerKey = $this->hmacSha1($newSaltedPassword, 'Server Key');
+ $newClientKey = $this->hmacSha1($newSaltedPassword, 'Client Key');
+ $newStoredKey = sha1(hex2bin($newClientKey));
+
+ if ($newServerKey === $internalServerKey
+ && $newStoredKey === $internalStoredKey) {
+ $uid = mb_strtolower($uid);
+ $this->storeUser($uid);
+ return $uid;
+ } else {
+ return false;
+ }
+ }
+ }
+}