1
0
Zachary Kotlarek 14 лет назад
Сommit
1d7980ee43
8 измененных файлов с 677 добавлено и 0 удалено
  1. 21 0
      httpd/04-myPlex.conf
  2. 54 0
      httpd/myplex-key.pem
  3. 125 0
      httpd/myplex.pem
  4. 249 0
      myMyPlex.inc
  5. 1 0
      pms/playlists/queue
  6. 82 0
      pms/system/library/sections.php
  7. 101 0
      servers.xml
  8. 44 0
      users/sign_in.xml

+ 21 - 0
httpd/04-myPlex.conf

@@ -0,0 +1,21 @@
+<VirtualHost *:443>
+	# Default document root
+	ServerName my.plexapp.com
+	DocumentRoot /mnt/web/myMyPlex
+
+	# MyPlex
+	<Directory "/mnt/web/myMyPlex">
+		Options MultiViews FollowSymlinks
+		AllowOverride None
+		Order allow,deny
+		Allow from all
+		AddType application/x-httpd-php .xml
+	</Directory>
+
+	# SSL Config
+	SSLEngine on
+	SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL
+	SSLCertificateFile /etc/httpd/ssl/myplex.pem
+	SSLCertificateKeyFile /etc/httpd/ssl/myplex-key.pem
+	SetEnvIf User-Agent ".*MSIE.*" nokeepalive ssl-unclean-shutdown downgrade-1.0 force-response-1.0
+</VirtualHost>

+ 54 - 0
httpd/myplex-key.pem

@@ -0,0 +1,54 @@
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: AES-256-CBC,830541AB8323309E1687235F7B136050
+
+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
+-----END RSA PRIVATE KEY-----

+ 125 - 0
httpd/myplex.pem

@@ -0,0 +1,125 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 22 (0x16)
+        Signature Algorithm: sha1WithRSAEncryption
+        Issuer: C=US, ST=Iowa, L=Ankeny, O=Zachary P. Kotlarek, CN=uberzach.com/emailAddress=zach@kotlarek.com
+        Validity
+            Not Before: Nov  4 22:41:50 2011 GMT
+            Not After : Nov  2 22:41:50 2016 GMT
+        Subject: C=US, ST=Washington, L=Seattle, O=Zachary P. Kotlarek, CN=my.plexapp.com/emailAddress=zach@kotlarek.com
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+            RSA Public Key: (4096 bit)
+                Modulus (4096 bit):
+                    00:ac:5c:5c:66:67:2b:6e:03:2b:f8:c0:88:b9:9d:
+                    4d:a7:a8:97:6b:eb:2f:32:fc:60:95:42:68:39:87:
+                    d0:6a:d2:c3:03:f0:2e:0c:e4:da:fa:12:7d:2b:01:
+                    ac:d1:09:42:18:73:60:6b:60:ae:75:60:99:11:06:
+                    06:37:bc:cc:bb:c4:a2:8f:00:76:f8:46:ac:43:b3:
+                    70:e9:23:d9:53:9a:2b:92:ad:44:cc:f5:04:99:57:
+                    fa:5f:b2:e6:5b:28:8e:3f:79:7e:c0:4a:c8:0d:bc:
+                    71:59:5d:af:d5:9a:7f:74:4e:12:7a:de:14:17:aa:
+                    d9:c5:b6:e6:0a:78:07:64:d3:81:2f:4b:1a:24:e0:
+                    17:07:d0:af:4f:58:8b:7b:77:ed:79:82:7d:dc:a0:
+                    93:b0:67:8a:70:10:58:ee:99:84:50:0d:41:8d:f8:
+                    73:e6:46:ad:ae:9f:54:77:73:a2:47:42:4b:8f:1f:
+                    c7:55:cc:f0:d2:b7:15:ec:a2:1a:44:25:cf:8b:dd:
+                    ef:e8:87:af:f0:7a:32:f2:4d:e2:1c:8e:6a:76:37:
+                    a5:d9:45:c1:86:9c:3b:79:81:62:8b:e1:7b:bf:62:
+                    36:36:ce:7b:0c:c6:63:2a:32:a0:de:88:33:e5:90:
+                    c1:94:42:37:1f:80:12:da:b6:10:1d:32:61:3b:fd:
+                    05:ea:c2:1e:46:a1:fc:07:99:35:f9:15:65:10:69:
+                    fb:3b:0f:de:21:56:a7:b5:5f:25:e0:e9:c0:f6:ec:
+                    76:af:28:da:d2:b1:9a:0c:73:df:76:cd:00:ac:25:
+                    d2:19:0f:2e:d6:39:cd:af:7a:8a:86:c5:70:16:29:
+                    f6:0a:da:cb:ee:8f:d6:7f:bb:31:14:ac:c5:17:6c:
+                    d4:eb:97:93:60:d6:ae:a7:c1:e9:15:4b:f3:be:75:
+                    6f:8e:97:10:a2:87:34:37:4d:4c:1c:8f:d2:73:5b:
+                    bc:0b:54:64:8a:4c:51:97:03:cc:ef:3e:ef:7d:4e:
+                    a8:f0:68:fe:27:15:3f:c0:9c:d3:83:0b:2d:ab:15:
+                    c0:c9:de:e9:6d:1b:9e:44:7b:47:07:9b:97:98:9a:
+                    74:56:17:09:a4:fb:1b:b8:3e:a0:18:30:fe:c9:9c:
+                    55:ca:9d:9d:87:0e:46:12:12:a3:13:72:33:6e:01:
+                    c6:98:17:9a:e9:8e:79:cd:4a:86:0a:83:3e:f7:45:
+                    cd:6a:39:bd:27:94:18:18:d5:5e:20:ea:d8:36:cc:
+                    71:64:ef:8c:c0:64:48:de:e6:45:67:62:0a:ee:50:
+                    62:e1:27:e7:f2:e4:19:d0:d2:26:38:47:8c:a5:01:
+                    7b:97:19:dd:bd:0c:cd:65:33:8e:0a:70:2c:d0:ee:
+                    13:5c:b1
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            Netscape Comment: 
+                OpenSSL Generated Certificate
+            X509v3 Subject Key Identifier: 
+                AA:D2:D2:13:5E:E4:A3:5A:53:26:A3:D0:95:EF:48:11:A8:33:BA:E4
+            X509v3 Authority Key Identifier: 
+                keyid:29:4E:12:85:5C:67:DC:2D:B8:7A:31:6A:18:6A:AC:D6:1C:AB:53:F5
+
+    Signature Algorithm: sha1WithRSAEncryption
+        76:30:8b:cd:2d:c4:6f:dc:a5:2c:b0:95:ec:8a:a1:a6:4b:99:
+        c2:76:61:3b:c2:07:23:e4:b7:95:75:93:91:80:26:c1:c5:a3:
+        c6:ad:c3:c6:d9:01:48:b1:ad:ac:a1:2e:9f:dc:19:76:e4:46:
+        4b:5c:aa:bc:67:8b:78:6d:1b:74:ba:68:eb:17:1c:6f:ab:86:
+        8a:3f:df:84:1a:71:53:7a:8e:5c:35:7d:12:1c:95:b2:8c:7b:
+        a2:4a:91:23:1e:0d:21:02:23:a4:d0:17:05:50:08:5a:bf:a4:
+        61:ee:fa:71:ad:d1:19:4f:f5:fd:59:1a:75:94:81:68:64:69:
+        1d:ac:06:58:94:72:e8:18:3d:97:c8:e9:3a:34:44:85:40:cd:
+        65:05:f4:8c:19:c7:5f:99:ca:30:58:03:99:1d:2e:4f:71:29:
+        47:8b:94:77:b6:91:2b:91:ab:11:e5:f6:bd:9e:1b:b6:41:74:
+        fa:d7:e6:ec:57:b9:84:2d:07:b7:ff:19:82:8a:8e:d6:f9:60:
+        a8:4c:c0:0b:46:19:27:0d:cf:de:cf:ee:46:d5:b8:75:8a:1b:
+        56:e2:9e:58:50:d3:d7:f5:2d:ed:1d:48:92:9c:bd:3d:b7:7f:
+        f2:83:44:79:33:e0:58:4b:3c:55:84:f2:2a:2d:6d:f1:f0:80:
+        7f:6b:19:db:a5:4d:f8:57:cf:24:48:fe:38:f9:31:0d:01:26:
+        10:22:c9:97:3c:c3:86:21:91:0b:4a:40:de:f8:d5:52:a6:f0:
+        1c:fc:9c:ec:f0:70:8d:16:f5:60:4a:ad:bc:4f:2c:33:c8:de:
+        16:f2:d4:f0:c4:e0:e1:81:32:eb:91:d4:2c:38:2b:61:1b:8e:
+        82:36:71:61:d4:57:9e:9a:ad:8b:3d:df:81:c9:50:41:bb:94:
+        af:1b:b5:09:91:fe:cb:c7:76:54:f1:9a:71:82:27:ab:74:0e:
+        e2:1e:66:91:15:6c:a2:89:7e:fe:41:1d:fe:91:48:e8:26:b6:
+        6e:f3:81:34:f7:a6:72:ac:76:20:ec:13:06:9f:6b:a1:a0:d3:
+        c0:0c:2b:86:06:77:5b:7e:b4:f2:f2:51:12:c1:be:4a:02:92:
+        a7:cc:4e:4b:62:51:f4:25:c7:2e:db:f1:1e:ec:25:86:6b:3a:
+        f5:c0:70:02:29:5a:71:e8:95:68:f1:9d:c9:e3:86:73:86:78:
+        34:ca:df:2c:2e:f1:4f:46:02:82:6f:ae:2d:5a:ef:76:91:8f:
+        bc:ec:1e:76:f3:01:ba:dc:95:e4:26:35:d7:b7:03:9a:8b:e4:
+        36:77:86:ce:96:e3:c5:32:ff:e2:29:da:28:c2:02:78:e3:62:
+        2b:23:4b:f1:57:aa:84:43
+-----BEGIN CERTIFICATE-----
+MIIGCTCCA/GgAwIBAgIBFjANBgkqhkiG9w0BAQUFADCBhDELMAkGA1UEBhMCVVMx
+DTALBgNVBAgTBElvd2ExDzANBgNVBAcTBkFua2VueTEcMBoGA1UEChMTWmFjaGFy
+eSBQLiBLb3RsYXJlazEVMBMGA1UEAxMMdWJlcnphY2guY29tMSAwHgYJKoZIhvcN
+AQkBFhF6YWNoQGtvdGxhcmVrLmNvbTAeFw0xMTExMDQyMjQxNTBaFw0xNjExMDIy
+MjQxNTBaMIGNMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G
+A1UEBxMHU2VhdHRsZTEcMBoGA1UEChMTWmFjaGFyeSBQLiBLb3RsYXJlazEXMBUG
+A1UEAxMObXkucGxleGFwcC5jb20xIDAeBgkqhkiG9w0BCQEWEXphY2hAa290bGFy
+ZWsuY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEArFxcZmcrbgMr
++MCIuZ1Np6iXa+svMvxglUJoOYfQatLDA/AuDOTa+hJ9KwGs0QlCGHNga2CudWCZ
+EQYGN7zMu8SijwB2+EasQ7Nw6SPZU5orkq1EzPUEmVf6X7LmWyiOP3l+wErIDbxx
+WV2v1Zp/dE4Set4UF6rZxbbmCngHZNOBL0saJOAXB9CvT1iLe3fteYJ93KCTsGeK
+cBBY7pmEUA1Bjfhz5katrp9Ud3OiR0JLjx/HVczw0rcV7KIaRCXPi93v6Iev8Hoy
+8k3iHI5qdjel2UXBhpw7eYFii+F7v2I2Ns57DMZjKjKg3ogz5ZDBlEI3H4AS2rYQ
+HTJhO/0F6sIeRqH8B5k1+RVlEGn7Ow/eIVantV8l4OnA9ux2ryja0rGaDHPfds0A
+rCXSGQ8u1jnNr3qKhsVwFin2CtrL7o/Wf7sxFKzFF2zU65eTYNaup8HpFUvzvnVv
+jpcQooc0N01MHI/Sc1u8C1RkikxRlwPM7z7vfU6o8Gj+JxU/wJzTgwstqxXAyd7p
+bRueRHtHB5uXmJp0VhcJpPsbuD6gGDD+yZxVyp2dhw5GEhKjE3IzbgHGmBea6Y55
+zUqGCoM+90XNajm9J5QYGNVeIOrYNsxxZO+MwGRI3uZFZ2IK7lBi4Sfn8uQZ0NIm
+OEeMpQF7lxndvQzNZTOOCnAs0O4TXLECAwEAAaN7MHkwCQYDVR0TBAIwADAsBglg
+hkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0O
+BBYEFKrS0hNe5KNaUyaj0JXvSBGoM7rkMB8GA1UdIwQYMBaAFClOEoVcZ9wtuHox
+ahhqrNYcq1P1MA0GCSqGSIb3DQEBBQUAA4ICAQB2MIvNLcRv3KUssJXsiqGmS5nC
+dmE7wgcj5LeVdZORgCbBxaPGrcPG2QFIsa2soS6f3Bl25EZLXKq8Z4t4bRt0umjr
+Fxxvq4aKP9+EGnFTeo5cNX0SHJWyjHuiSpEjHg0hAiOk0BcFUAhav6Rh7vpxrdEZ
+T/X9WRp1lIFoZGkdrAZYlHLoGD2XyOk6NESFQM1lBfSMGcdfmcowWAOZHS5PcSlH
+i5R3tpErkasR5fa9nhu2QXT61+bsV7mELQe3/xmCio7W+WCoTMALRhknDc/ez+5G
+1bh1ihtW4p5YUNPX9S3tHUiSnL09t3/yg0R5M+BYSzxVhPIqLW3x8IB/axnbpU34
+V88kSP44+TENASYQIsmXPMOGIZELSkDe+NVSpvAc/Jzs8HCNFvVgSq28TywzyN4W
+8tTwxODhgTLrkdQsOCthG46CNnFh1Feemq2LPd+ByVBBu5SvG7UJkf7Lx3ZU8Zpx
+gierdA7iHmaRFWyiiX7+QR3+kUjoJrZu84E096ZyrHYg7BMGn2uhoNPADCuGBndb
+frTy8lESwb5KApKnzE5LYlH0Jccu2/Ee7CWGazr1wHACKVpx6JVo8Z3J44Zzhng0
+yt8sLvFPRgKCb64tWu92kY+87B528wG63JXkJjXXtwOai+Q2d4bOluPFMv/iKdoo
+wgJ442IrI0vxV6qEQw==
+-----END CERTIFICATE-----

+ 249 - 0
myMyPlex.inc

@@ -0,0 +1,249 @@
+<?php
+
+# Test params
+$_SERVER['PHP_AUTH_USER'] = 'profplump';
+
+#=========================================
+# Parameters/Globals
+#=========================================
+$USER_DB = 'sqlite:/var/state/myMyPlex/db.sqlite';
+$DBH = NULL;
+
+# Prevent PHP from whining - the actual timezone you set is irrelevent
+date_default_timezone_set('UTC');
+
+
+#=========================================
+# Error Handler
+#=========================================
+function error($errno, $errstr, $errfile = '', $errline = '') {
+	header('HTTP/1.1 500 myMyPlex Processing Error');
+	$msg = 'myMyPlex Processing Error';
+	if ($errno) {
+		$msg .= ' ' . $errno;
+	}
+	$msg .= ' (' . $errstr . ')';
+	if ($errfile) {
+		$msg .= ' in ' . $errfile;
+	}
+	if ($errline) {
+		$msg .= ' at ' . $errline;
+	}
+	die($msg . "\n");
+}
+set_error_handler('error');
+
+
+#=========================================
+# DB Conneciton/Table Init
+#=========================================
+function get_dbh() {
+	global $USER_DB;
+	global $DBH;
+
+	# If we haven't already connected in this session
+	if (! $DBH) {
+		# Connect
+		$dbh = new PDO($USER_DB);
+		if (! $dbh) {
+			error(0, 'Unable to connect to DB');
+		}
+		$DBH = $dbh;
+
+		# Create the user table if needed
+		$dbh->query('
+			CREATE TABLE IF NOT EXISTS users 
+			(username VARCHAR(64), auth CHAR(32),
+			PRIMARY KEY(username), UNIQUE(auth))
+		');
+
+		# Create the server table if needed
+		$dbh->query('
+			CREATE TABLE IF NOT EXISTS servers 
+			(username VARCHAR(64), name VARCHAR(64),
+			address CHAR(15), port INT, id CHAR(40),
+			version VARCHAR(32),
+			PRIMARY KEY(username, name), UNIQUE(id))
+		');
+	}
+
+	# Return either a new or existing DB ref
+	return $DBH;
+}
+
+
+#=========================================
+# Get User
+#=========================================
+function get_user($username = '', $auth = '') {
+	$user = array(
+		'username' => '',
+		'auth' => ''
+	);
+
+	try {
+		# Query for the requested user
+		$dbh = get_dbh();
+		if (strlen($username)) {
+			$stmt = $dbh->prepare('SELECT username, auth FROM users WHERE username = ?');
+			$stmt->execute(array($username));
+		} else {
+			$stmt = $dbh->prepare('SELECT username, auth FROM users WHERE auth = ?');
+			$stmt->execute(array($auth));
+		}
+		$row = $stmt->fetch(PDO::FETCH_ASSOC);
+
+		# If the users exists, map it into our local array
+		if ($row) {
+			foreach (array_keys($user) as $key) {
+				$user[$key] = $row[$key];
+			}
+		}
+	} catch (PDOException $e) {
+		error(0, 'DB error in get_user: ' . $e->getMessage());
+	}
+
+	return $user;
+}
+
+#=========================================
+# Save User
+#=========================================
+function save_user($user) {
+	try {
+		# Get connected
+		$dbh = get_dbh();
+
+		# Create the user if necessary
+		$stmt = $dbh->prepare('SELECT username FROM users WHERE username = ?');
+		$stmt->execute(array($user['username']));
+		if (! is_array($stmt->fetch())) {
+			$stmt = $dbh->prepare('INSERT INTO users (username) VALUES (?)');
+			$stmt->execute(array($user['username']));
+		}
+
+		# Update the DB from our local array
+		$stmt = $dbh->prepare('UPDATE users SET auth = ? WHERE username = ?');
+		$stmt->execute(array($user['auth'], $user['username']));
+	} catch (PDOException $e) {
+		error(0, 'DB error in save_user: ' . $e->getMessage());
+	}
+
+	return TRUE;
+}
+
+
+#=========================================
+# Get Server
+#=========================================
+function get_server($username, $name) {
+	$server = array(
+		'username' => '',
+		'name' => '',
+		'address' => '',
+		'port' => '',
+		'id' => '',
+		'version' => '',
+	);
+
+	try {
+		# Query for the requested user
+		$dbh = get_dbh();
+		$stmt = $dbh->prepare('
+			SELECT username, name, address, port, id, version
+			FROM servers WHERE username = ? AND name = ?
+		');
+		$stmt->execute(array($username, $name));
+		$row = $stmt->fetch(PDO::FETCH_ASSOC);
+
+		# If the users exists, map it into our local array
+		if ($row) {
+			foreach (array_keys($server) as $key) {
+				$server[$key] = $row[$key];
+			}
+		}
+	} catch (PDOException $e) {
+		error(0, 'DB error in get_server: ' . $e->getMessage());
+	}
+
+	return $server;
+}
+
+
+#=========================================
+# Save Server
+#=========================================
+function save_server($server) {
+	try {
+		# Get connected
+		$dbh = get_dbh();
+
+		# Create the server if necessary
+		$stmt = $dbh->prepare('SELECT name FROM servers WHERE username = ? AND name = ?');
+		$stmt->execute(array($server['username'], $server['name']));
+		if (! is_array($stmt->fetch())) {
+			$stmt = $dbh->prepare('INSERT INTO servers (username, name, id) VALUES (?, ?, ?)');
+			$stmt->execute(array($server['username'], $server['name']));
+		}
+
+		# Update the DB from our local array
+		$stmt = $dbh->prepare('UPDATE servers SET address = ?, port = ?, id = ? WHERE username = ? AND name = ?');
+		$stmt->execute(array($server['address'], $server['port'], $server['id'], $server['username'], $server['name']));
+	} catch (PDOException $e) {
+		error(0, 'DB error in save_server: ' . $e->getMessage());
+	}
+
+	return TRUE;
+}
+
+
+#=========================================
+# Delete Server
+#=========================================
+function delete_server($id) {
+	try {
+		$dbh = get_dbh();
+		$stmt = $dbh->prepare('DELETE FROM servers WHERE id = ?');
+		$stmt->execute(array($id));
+	} catch (PDOException $e) {
+		error(0, 'DB error in save_server: ' . $e->getMessage());
+	}
+
+	return TRUE;
+}
+
+
+#=========================================
+# Get Server List
+#=========================================
+function get_server_list($username) {
+	$servers = array();
+
+	try {
+		# Query for the requested user
+		$dbh = get_dbh();
+		$stmt = $dbh->prepare('SELECT name FROM servers WHERE username = ?');
+		$stmt->execute(array($username));
+
+		$rs = $stmt->fetchAll(PDO::FETCH_ASSOC);
+		foreach ($rs as $row) {
+			$servers[] = $row['name'];
+		}
+	} catch (PDOException $e) {
+		error(0, 'DB error in get_server_list: ' . $e->getMessage());
+	}
+
+	return $servers;
+}
+
+
+#=========================================
+# Generate Auth Tokens
+#=========================================
+function generate_auth() {
+	return sprintf('%08x%08x%08x%08x',
+		mt_rand(0, 0xffffffff), mt_rand(0, 0xffffffff),
+		mt_rand(0, 0xffffffff), mt_rand(0, 0xffffffff));
+}
+
+?>

+ 1 - 0
pms/playlists/queue

@@ -0,0 +1 @@
+<MediaContainer identifier="com.plexapp.plugins.myplex" title1="My Queue" content="secondary" friendlyName="myMyPlex" size="0" machineIdentifier="1" />

+ 82 - 0
pms/system/library/sections.php

@@ -0,0 +1,82 @@
+<?php
+require_once '../../../myMyPlex.inc';
+
+# Bail if we're not happy with the auth token in the URL
+$auth = '';
+if (array_key_exists('auth_token', $_GET) && strlen($_GET['auth_token'])) {
+	$auth = preg_replace('/\W/', '', $_GET['auth_token']);
+}
+if ($auth == '' || $auth != $_GET['auth_token']) {
+	header('HTTP/1.1 401 Authorization Required');
+	die("Invalid auth token\n");
+}
+
+# Lookup the user
+$user = get_user('', $auth);
+if (! $user['username']) {
+	header('HTTP/1.1 401 Authorization Required');
+	die("Unknown auth token\n");
+}
+
+# Lookup all the user's servers
+$servers = get_server_list($user['username']);
+
+# Lookup all the sections in those servers
+$sections = array();
+foreach ($servers as $name) {
+	$server = get_server($user['username'], $name);
+	$sections[] = array(
+		'version' => $server['version'],
+		'title' => 'myMyMovies',
+		'port' => $server['port'],
+		'lang' => 'en',
+		'server' => $server['name'],
+		'id' => $server['id'],
+		'type' => 'movie',
+		'agent' => 'com.plexapp.agents.imdb',
+		'path' => '/library/sections/1',
+		'address' => $server['address'],
+		'scanner' => 'Plex Movie Scanner',	
+	);
+}
+
+# Build an XML response
+$doc = new DomDocument('1.0');
+$root = $doc->createElement('MediaContainer');
+$root = $doc->appendChild($root);
+$root->setAttribute('title1', 'myMyPlex Library');
+$root->setAttribute('identifier', 'com.plexapp.plugins.myplex');
+$root->setAttribute('friendlyName', 'myMyPlex');
+$root->setAttribute('size', count($sections));
+$root->setAttribute('machineIdentifier', '1');
+
+foreach ($sections as $sec) {
+	$elm = $doc->createElement('Directory');
+	$elm->setAttribute('address', $sec['address']);
+	$elm->setAttribute('local', '0');
+	$elm->setAttribute('serverVersion', $sec['version']);
+	$elm->setAttribute('accessToken', '');
+	$elm->setAttribute('title', $sec['title']);
+	$elm->setAttribute('updatedAt', date('U'));
+	$elm->setAttribute('port', $sec['port']);
+	$elm->setAttribute('art', 'http://' . $sec['address'] . ':' . $sec['port'] . '/:/resources/show-fanart.jpg');
+	$elm->setAttribute('language', $sec['lang']);
+	$elm->setAttribute('serverName', $sec['server']);
+	$elm->setAttribute('machineIdentifier', $sec['id']);
+	$elm->setAttribute('type', $sec['type']);
+	$elm->setAttribute('agent', $sec['agent']);
+	$elm->setAttribute('path', $sec['path']);
+	$elm->setAttribute('owned', '1');
+	$elm->setAttribute('host', $sec['address']);
+	$elm->setAttribute('unique', '0');
+	$elm->setAttribute('key', 'http://' . $sec['address'] . ':' . $sec['port'] . $sec['path']);
+	$elm->setAttribute('scanner', $sec['scanner']);
+
+	$elm = $root->appendChild($elm);
+}
+
+# Send our XML to the client
+header('HTTP/1.1 200 OK');
+print $doc->saveXML();
+
+?>

+ 101 - 0
servers.xml

@@ -0,0 +1,101 @@
+<?php
+require_once 'myMyPlex.inc';
+
+# Bail if we're not happy with the auth token in the URL
+$auth = '';
+if (array_key_exists('auth_token', $_GET) && strlen($_GET['auth_token'])) {
+	$auth = preg_replace('/\W/', '', $_GET['auth_token']);
+}
+if ($auth == '' || $auth != $_GET['auth_token']) {
+	header('HTTP/1.1 401 Authorization Required');
+	die("Invalid auth token\n");
+}
+
+# Lookup the user
+$user = get_user('', $auth);
+if (! $user['username']) {
+	header('HTTP/1.1 401 Authorization Required');
+	die("Unknown auth token\n");
+}
+
+# Handle deletes
+if ($_SERVER['REQUEST_METHOD'] == 'DELETE') {
+	if (preg_match('/\/(\w+)\.xml\?/', $_SERVER['REQUEST_URI'], $parts)) {
+		delete_server($parts[1]);
+		header('HTTP/1.1 200 OK');
+		exit(0);
+	} else {
+		header('HTTP/1.1 422 Unprocessable Entity');
+		die("Invalid machine ID\n");
+	}
+}
+
+# Parse the input XML
+$doc = new DOMDocument();
+$doc->loadXML(file_get_contents('php://input'));
+$elm = $doc->getElementsByTagName('MediaContainer')->item(0);
+$elm = $elm->getElementsByTagName('Server')->item(0);
+$server = array();
+foreach (array('name', 'host', 'address', 'port', 'machineIdentifier', 'version') as $key) {
+	$server[$key] = $elm->getAttribute($key);
+
+	# Only keep valid data
+	if (preg_replace('/[^\w\.\-]/', '', $server[$key]) != $server[$key]) {
+		$server[$key] = '';
+	}
+}
+
+# Ensure the data is reasonable
+$server['port'] = intval($server['port']);
+if ($server['port'] < 1 || $server['port'] > 65535) {
+	$server['port'] = '';
+}
+preg_match('/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/', $server['address'], $ip_parts);
+if (count($ip_parts) == 5) {
+	unset($ip_parts[0]);
+	foreach ($ip_parts as $part) {
+		$part = intval($part);
+		if ($part < 1 || $part > 254) {
+			$server['address'] = '';
+		}
+	}
+} else {
+	$server['address'] = '';
+}
+if (
+	strlen($server['name']) < 1 ||
+	strlen($server['address']) < 1 ||
+	strlen($server['port']) < 1 ||
+	strlen($server['version']) < 1 ||
+	strlen($server['machineIdentifier']) < 1
+) {
+	header('HTTP/1.1 422 Unprocessable Entity');
+	die("Invalid name, address, port, ID, or version\n");
+}
+
+# Register the server
+$server['username'] = $user['username'];
+$server['id'] = $server['machineIdentifier'];
+save_server($server);
+
+# Build an XML response
+$doc = new DomDocument('1.0');
+$root = $doc->createElement('MediaContainer');
+$root = $doc->appendChild($root);
+
+$elm = $doc->createElement('Server');
+$elm->setAttribute('name', $server['name']);
+$elm->setAttribute('address', $server['address']);
+$elm->setAttribute('port', $server['port']);
+$elm->setAttribute('updatedAt', date('U'));
+$elm->setAttribute('machineIdentifier', $server['machineIdentifier']);
+$elm->setAttribute('createdAt', date('U'));
+$elm->setAttribute('owned', '1');
+$elm->setAttribute('host', $server['host']);
+$elm = $root->appendChild($elm);
+
+# Send our XML to the PMS
+header('HTTP/1.1 201 Created');
+print $doc->saveXML();
+
+?>

+ 44 - 0
users/sign_in.xml

@@ -0,0 +1,44 @@
+<?php
+require_once '../myMyPlex.inc';
+
+# Bail if we're not happy with the auth provied by the HTTP server
+$username = '';
+if (array_key_exists('PHP_AUTH_USER', $_SERVER) && strlen($_SERVER['PHP_AUTH_USER'])) {
+	$username = preg_replace('/\W/', '', $_SERVER['PHP_AUTH_USER']);
+}
+if ($username == '' || $username != $_SERVER['PHP_AUTH_USER']) {
+	header('HTTP/1.1 401 Authorization Required');
+	die("Invalid username/password\n");
+}
+
+# Lookup or generate an auth token
+$user = get_user($username);
+if (! $user['auth']) {
+	$user['username'] = $username;
+	$user['auth'] = generate_auth();
+	save_user($user);
+}
+
+# Build an XML document
+$doc = new DomDocument('1.0');
+$root = $doc->createElement('user');
+$root = $doc->appendChild($root);
+	
+$elm = $doc->createElement('joined-at', date('c'));
+$elm->setAttribute('type', 'datetime');
+$elm = $root->appendChild($elm);
+
+$elm = $doc->createElement('username', $username);
+$elm = $root->appendChild($elm);
+
+$elm = $doc->createElement('email', $username . '@localhost');
+$elm = $root->appendChild($elm);
+
+$elm = $doc->createElement('authentication-token', $user['auth']);
+$elm = $root->appendChild($elm);
+
+# Send our XML to the PMS
+header('HTTP/1.1 201 Created');
+print $doc->saveXML();
+
+?>