diff --git a/apps/contacts/appinfo/app.php b/apps/contacts/appinfo/app.php
new file mode 100644
index 0000000000..f38a45f279
--- /dev/null
+++ b/apps/contacts/appinfo/app.php
@@ -0,0 +1,18 @@
+ 10,
+ 'id' => 'contacts',
+ 'name' => 'Contacts' ));
+
+OC_App::addNavigationEntry( array(
+ 'id' => 'contacts_index',
+ 'order' => 10,
+ 'href' => OC_Helper::linkTo( 'contacts', 'index.php' ),
+ 'icon' => OC_Helper::imagePath( 'contacts', 'icon.png' ),
+ 'name' => 'Addressbook' ));
+
+?>
diff --git a/apps/contacts/appinfo/info.xml b/apps/contacts/appinfo/info.xml
new file mode 100644
index 0000000000..93463b9cf0
--- /dev/null
+++ b/apps/contacts/appinfo/info.xml
@@ -0,0 +1,9 @@
+
+
+ contacts
+ Contacts
+ 0.1
+ AGPL
+ Jakob Sack
+ 2
+
diff --git a/apps/contacts/carddav.php b/apps/contacts/carddav.php
new file mode 100644
index 0000000000..ae2c5b9736
--- /dev/null
+++ b/apps/contacts/carddav.php
@@ -0,0 +1,28 @@
+setBaseUri($WEBROOT.'/apps/contacts/carddav.php');
+// Add plugins
+$server->addPlugin(new Sabre_DAV_Auth_Plugin($authBackend,'ownCloud'));
+$server->addPlugin(new Sabre_CardDAV_Plugin());
+$server->addPlugin(new Sabre_DAVACL_Plugin());
+
+// And off we go!
+$server->exec();
diff --git a/apps/contacts/css/styles.css b/apps/contacts/css/styles.css
new file mode 100644
index 0000000000..8d1c8b69c3
--- /dev/null
+++ b/apps/contacts/css/styles.css
@@ -0,0 +1 @@
+
diff --git a/apps/contacts/details.php b/apps/contacts/details.php
new file mode 100644
index 0000000000..7ab1b64de2
--- /dev/null
+++ b/apps/contacts/details.php
@@ -0,0 +1,57 @@
+.
+ *
+ */
+
+// Init owncloud
+require_once('../../lib/base.php');
+
+$id = $_GET['id'];
+
+$l10n = new OC_L10N('contacts');
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+ echo $l10n->t('You need to log in!');
+ exit();
+}
+
+
+$card = OC_Contacts_Addressbook::findCard( $id );
+if( $card === false ){
+ echo json_encode( array( 'status' => 'error', 'data' => array( 'message' => $l10n->t('Can not find Contact!'))));
+ exit();
+}
+
+$addressbook = OC_Contacts_Addressbook::findAddressbook( $card['addressbookid'] );
+if( $addressbook === false || $addressbook['userid'] != OC_USER::getUser()){
+ echo json_encode( array( 'status' => 'error', 'data' => array( 'message' => $l10n->t('This is not your contact!'))));
+ exit();
+}
+
+$vcard = Sabre_VObject_Reader::read($card['carddata']);
+$details = OC_Contacts_Addressbook::structureContact($vcard);
+
+$tmpl = new OC_Template('contacts','_details');
+$tmpl->assign('details',$details);
+$tmpl->assign('id',$id);
+$page = $tmpl->fetchPage();
+
+echo json_encode( array( 'status' => 'success', 'data' => array( 'page' => $page )));
diff --git a/apps/contacts/img/icon.png b/apps/contacts/img/icon.png
new file mode 100644
index 0000000000..ea2ed9e333
Binary files /dev/null and b/apps/contacts/img/icon.png differ
diff --git a/apps/contacts/index.php b/apps/contacts/index.php
new file mode 100644
index 0000000000..2d5bcefd87
--- /dev/null
+++ b/apps/contacts/index.php
@@ -0,0 +1,68 @@
+.
+ *
+ */
+
+function contactsort($a,$b){
+ return strcmp($a['name'],$b['name']);
+}
+
+// Init owncloud
+require_once('../../lib/base.php');
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+ header( 'Location: '.OC_Helper::linkTo( '', 'index.php' ));
+ exit();
+}
+
+// Load the files we need
+OC_App::setActiveNavigationEntry( 'contacts_index' );
+
+// Load a specific user?
+$id = isset( $_GET['id'] ) ? $_GET['id'] : null;
+
+// Addressbooks to load
+$openaddressbooks = explode(';',OC_Preferences::getValue(OC_User::getUser(),'contacts','openaddressbooks',null));
+
+$contacts = array();
+foreach( $openaddressbooks as $addressbook ){
+ $addressbookcontacts = OC_Contacts_Addressbook::allCards($addressbook);
+ foreach( $addressbookcontacts as $contact ){
+ $contacts[] = array( 'name' => $contact['fullname'], 'id' => $contact['id'] );
+ }
+}
+
+
+usort($contacts,'contactsort');
+$details = array();
+
+if( !is_null($id) || count($contacts)){
+ $contact = OC_Contacts_Addressbook::findCard(is_null($id)?$contacts[0]['id']:$id);
+ $vcard = Sabre_VObject_Reader::read($contact['carddata']);
+ $details = OC_Contacts_Addressbook::structureContact($vcard);
+}
+
+// Process the template
+$tmpl = new OC_Template( 'contacts', 'index', 'user' );
+$tmpl->assign('contacts', $contacts);
+$tmpl->assign('details', $details );
+$tmpl->assign('id',$id);
+$tmpl->printPage();
diff --git a/apps/contacts/js/interface.js b/apps/contacts/js/interface.js
new file mode 100644
index 0000000000..045562b496
--- /dev/null
+++ b/apps/contacts/js/interface.js
@@ -0,0 +1,14 @@
+$(document).ready(function(){
+ $('.contacts_contacts').find('li').live('click',function(){
+ var id = $(this).attr('x-id');
+ $.getJSON('details.php',{'id':id},function(jsondata){
+ if(jsondata.status == 'success'){
+ $('.contacts_details').html(jsondata.data.page);
+ }
+ else{
+ alert(jsondata.data.message);
+ }
+ });
+ return false;
+ });
+});
diff --git a/apps/contacts/lib/addressbook.php b/apps/contacts/lib/addressbook.php
new file mode 100644
index 0000000000..c0f26c7df5
--- /dev/null
+++ b/apps/contacts/lib/addressbook.php
@@ -0,0 +1,287 @@
+.
+ *
+ */
+/*
+ *
+ * The following SQL statement is just a help for developers and will not be
+ * executed!
+ *
+ * CREATE TABLE contacts_addressbooks (
+ * id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ * userid VARCHAR(255) NOT NULL,
+ * displayname VARCHAR(255),
+ * uri VARCHAR(100),
+ * description TEXT,
+ * ctag INT(11) UNSIGNED NOT NULL DEFAULT '1'
+ * );
+ *
+ * CREATE TABLE contacts_cards (
+ * id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
+ * addressbookid INT(11) UNSIGNED NOT NULL,
+ * fullname VARCHAR(255),
+ * carddata TEXT,
+ * uri VARCHAR(100),
+ * lastmodified INT(11) UNSIGNED
+ * );
+ */
+
+/**
+ * This class manages our addressbooks.
+ */
+class OC_Contacts_Addressbook{
+ public static function allAddressbooks($uid){
+ $stmt = OC_DB::prepare( 'SELECT * FROM *PREFIX*contacts_addressbooks WHERE userid = ?' );
+ $result = $stmt->execute(array($uid));
+
+ $addressbooks = array();
+ while( $row = $result->fetchRow()){
+ $addressbooks[] = $row;
+ }
+
+ return $addressbooks;
+ }
+
+ public static function allAddressbooksWherePrincipalURIIs($principaluri){
+ $uid = self::extractUserID($principaluri);
+ return self::allAddressbooks($uid);
+ }
+
+ public static function findAddressbook($id){
+ $stmt = OC_DB::prepare( 'SELECT * FROM *PREFIX*contacts_addressbooks WHERE id = ?' );
+ $result = $stmt->execute(array($id));
+
+ return $result->fetchRow();
+ }
+
+ public static function addAddressbook($userid,$name,$description){
+ $all = self::allAddressbooks($userid);
+ $uris = array();
+ foreach($all as $i){
+ $uris[] = $i['uri'];
+ }
+
+ $uri = self::createURI('name', $uris );
+
+ $stmt = OC_DB::prepare( 'INSERT INTO *PREFIX*contacts_addressbooks (userid,displayname,uri,description,ctag) VALUES(?,?,?,?,?)' );
+ $result = $stmt->execute(array($userid,$name,$uri,$description,1));
+
+ return OC_DB::insertid();
+ }
+
+ public static function addAddressbookFromDAVData($principaluri,$uri,$name,$description){
+ $userid = self::extractUserID($principaluri);
+
+ $stmt = OC_DB::prepare( 'INSERT INTO *PREFIX*contacts_addressbooks (userid,displayname,uri,description,ctag) VALUES(?,?,?,?,?)' );
+ $result = $stmt->execute(array($userid,$name,$uri,$description,1));
+
+ return OC_DB::insertid();
+ }
+
+ public static function editAddressbook($id,$name,$description){
+ // Need these ones for checking uri
+ $addressbook = self::find($id);
+
+ if(is_null($name)){
+ $name = $addressbook['name'];
+ }
+ if(is_null($description)){
+ $description = $addressbook['description'];
+ }
+
+ $stmt = OC_DB::prepare( 'UPDATE *PREFIX*contacts_addressbooks SET displayname=?,description=?, ctag=ctag+1 WHERE id=?' );
+ $result = $stmt->execute(array($name,$description,$id));
+
+ return true;
+ }
+
+ public static function touchAddressbook($id){
+ $stmt = OC_DB::prepare( 'UPDATE *PREFIX*contacts_addressbooks SET ctag = ctag + 1 WHERE id = ?' );
+ $stmt->execute(array($id));
+
+ return true;
+ }
+
+ public static function deleteAddressbook($id){
+ $stmt = OC_DB::prepare( 'DELETE FROM *PREFIX*contacts_cards WHERE id = ?' );
+ $stmt->execute(array($id));
+
+ $stmt = OC_DB::prepare( 'DELETE FROM *PREFIX*contacts_addressbooks WHERE addressbookid = ?' );
+ $stmt->execute(array($id));
+
+ return true;
+ }
+
+ public static function allCards($id){
+ $stmt = OC_DB::prepare( 'SELECT * FROM *PREFIX*contacts_cards WHERE addressbookid = ?' );
+ $result = $stmt->execute(array($id));
+
+ $addressbooks = array();
+ while( $row = $result->fetchRow()){
+ $addressbooks[] = $row;
+ }
+
+ return $addressbooks;
+ }
+
+ public static function findCard($id){
+ $stmt = OC_DB::prepare( 'SELECT * FROM *PREFIX*contacts_cards WHERE id = ?' );
+ $result = $stmt->execute(array($id));
+
+ return $result->fetchRow();
+ }
+
+ public static function findCardWhereDAVDataIs($aid,$uri){
+ $stmt = OC_DB::prepare( 'SELECT * FROM *PREFIX*contacts_cards WHERE addressbookid = ? AND uri = ?' );
+ $result = $stmt->execute(array($aid,$uri));
+
+ return $result->fetchRow();
+ }
+
+ public static function addCard($id,$data){
+ $fn = null;
+ $uri = null;
+ $card = Sabre_VObject_Reader::read($data);
+ foreach($card->children as $property){
+ if($property->name == 'FN'){
+ $fn = $property->value;
+ }
+ elseif(is_null($uri) && $property->name == 'UID' ){
+ $uri = $property->value.'.vcf';
+ }
+ }
+ $uri = self::createUID().'.vcf';
+
+ $stmt = OC_DB::prepare( 'INSERT INTO *PREFIX*contacts_cards (addressbookid,fullname,carddata,uri,lastmodified) VALUES(?,?,?,?,?)' );
+ $result = $stmt->execute(array($id,$fn,$data,$uri,time()));
+
+ self::touch($id);
+
+ return OC_DB::insertid;
+ }
+
+ public static function addCardFromDAVData($id,$uri,$data){
+ $fn = null;
+ $card = Sabre_VObject_Reader::read($data);
+ foreach($card->children as $property){
+ if($property->name == 'FN'){
+ $fn = $property->value;
+ }
+ }
+
+ $stmt = OC_DB::prepare( 'INSERT INTO *PREFIX*contacts_cards (addressbookid,fullname,carddata,uri,lastmodified) VALUES(?,?,?,?,?)' );
+ $result = $stmt->execute(array($id,$fn,$data,$uri,time()));
+
+ self::touch($id);
+
+ return OC_DB::insertid;
+ }
+
+ public static function editCard($id, $data){
+ $oldcard = self::findCard($id,$aid,$uri);
+ $fn = null;
+ $card = Sabre_VObject_Reader::read($data);
+ foreach($card->children as $property){
+ if($property->name == 'FN'){
+ $fn = $property->value;
+ }
+ }
+
+ $stmt = OC_DB::prepare( 'UPDATE *PREFIX*contacts_cards SET fullname = ?,carddata = ?, lastmodified = ? WHERE id = ?' );
+ $result = $stmt->execute(array($fn,$data,time(),$id));
+
+ self::touch($oldcard['addressbookid']);
+
+ return true;
+ }
+
+ public static function editCardFromDAVData($aid,$uri,$data){
+ $oldcard = self::findCardWhereDAVDataIs($aid,$uri);
+
+ $fn = null;
+ $card = Sabre_VObject_Reader::read($data);
+ foreach($card->children as $property){
+ if($property->name == 'FN'){
+ $fn = $property->value;
+ }
+ }
+
+ $stmt = OC_DB::prepare( 'UPDATE *PREFIX*contacts_cards SET fullname = ?,carddata = ?, lastmodified = ? WHERE id = ?' );
+ $result = $stmt->execute(array($fn,$data,time(),$oldcard['id']));
+
+ self::touch($oldcard['addressbookid']);
+
+ return true;
+ }
+
+ public static function deleteCard($id){
+ $stmt = OC_DB::prepare( 'DELETE FROM *PREFIX*contacts_addressbooks WHERE id = ?' );
+ $stmt->execute(array($id));
+
+ return true;
+ }
+
+ public static function deleteCardFromDAVData($aid,$uri){
+ $stmt = OC_DB::prepare( 'DELETE FROM *PREFIX*contacts_addressbooks WHERE addressbookid = ? AND uri=?' );
+ $stmt->execute(array($aid,$uri));
+
+ return true;
+ }
+
+ public static function createURI($name,$existing){
+ $name = strtolower($name);
+ $newname = $name;
+ $i = 1;
+ while(in_array($newname,$existing)){
+ $newname = $name.$i;
+ $i = $i + 1;
+ }
+ return $newname;
+ }
+
+ public static function createUID(){
+ return substr(md5(rand().time()),0,10);
+ }
+
+ public static function extractUserID($principaluri){
+ list($prefix,$userid) = Sabre_DAV_URLUtil::splitPath($principaluri);
+ return $userid;
+ }
+
+ public static function structureContact($object){
+ $details = array();
+ foreach($object->children as $property){
+ $temp = array(
+ 'name' => $property->name,
+ 'value' => ($property->name == 'PHOTO' || $property->name == 'LOGO' ? null : $property->value ),
+ 'parameters' => array());
+ foreach($property->parameters as $parameter){
+ $temp['parameters'][] = array( 'name' => $parameter->name, 'value' => $parameter->value);
+ }
+ if(array_key_exists($property->name,$details)){
+ $details[$property->name][] = $temp;
+ }
+ else{
+ $details[$property->name] = array($temp);
+ }
+ }
+ return $details;
+ }
+}
diff --git a/apps/contacts/lib/connector_sabre.php b/apps/contacts/lib/connector_sabre.php
new file mode 100644
index 0000000000..98e2598b3a
--- /dev/null
+++ b/apps/contacts/lib/connector_sabre.php
@@ -0,0 +1,186 @@
+ $i['id'],
+ 'uri' => $i['uri'],
+ 'principaluri' => 'principals/'.$i['userid'],
+ '{DAV:}displayname' => $i['displayname'],
+ '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' => $i['description'],
+ '{http://calendarserver.org/ns/}getctag' => $i['ctag'],
+ );
+ }
+
+ return $addressbooks;
+ }
+
+
+ /**
+ * Updates an addressbook's properties
+ *
+ * See Sabre_DAV_IProperties for a description of the mutations array, as
+ * well as the return value.
+ *
+ * @param mixed $addressbookid
+ * @param array $mutations
+ * @see Sabre_DAV_IProperties::updateProperties
+ * @return bool|array
+ */
+ public function updateAddressBook($addressbookid, array $mutations) {
+ $name = null;
+ $description = null;
+
+ foreach($mutations as $property=>$newvalue) {
+ switch($property) {
+ case '{DAV:}displayname' :
+ $name = $newvalue;
+ break;
+ case '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' :
+ $description = $newvalue;
+ break;
+ default :
+ // If any unsupported values were being updated, we must
+ // let the entire request fail.
+ return false;
+ }
+ }
+
+ OC_Contacts_Addressbook::editAddressbook($addressbookid,$name,$description);
+
+ return true;
+
+ }
+
+ /**
+ * Creates a new address book
+ *
+ * @param string $principaluri
+ * @param string $url Just the 'basename' of the url.
+ * @param array $properties
+ * @return void
+ */
+ public function createAddressBook($principaluri, $url, array $properties) {
+
+ $displayname = null;
+ $description = null;
+
+ foreach($properties as $property=>$newvalue) {
+
+ switch($property) {
+ case '{DAV:}displayname' :
+ $displayname = $newvalue;
+ break;
+ case '{' . Sabre_CardDAV_Plugin::NS_CARDDAV . '}addressbook-description' :
+ $description = $newvalue;
+ break;
+ default :
+ throw new Sabre_DAV_Exception_BadRequest('Unknown property: ' . $property);
+ }
+
+ }
+
+ OC_Contacts_Addressbook::addAddressbookFromDAVData($principaluri,$url,$name,$description);
+ }
+
+ /**
+ * Deletes an entire addressbook and all its contents
+ *
+ * @param int $addressbookid
+ * @return void
+ */
+ public function deleteAddressBook($addressbookid) {
+ OC_Contacts_Addressbook::deleteAddressbook($addressbookid);
+ }
+
+ /**
+ * Returns all cards for a specific addressbook id.
+ *
+ * @param mixed $addressbookid
+ * @return array
+ */
+ public function getCards($addressbookid) {
+ $data = OC_Contacts_Addressbook::allCards($addressbookid);
+ $cards = array();
+ foreach($data as $i){
+ $cards[] = array(
+ 'id' => $i['id'],
+ 'carddata' => $i['carddata'],
+ 'uri' => $i['uri'],
+ 'lastmodified' => $i['lastmodified'] );
+ }
+
+ return $cards;
+ }
+
+ /**
+ * Returns a specfic card
+ *
+ * @param mixed $addressbookid
+ * @param string $carduri
+ * @return array
+ */
+ public function getCard($addressbookid, $carduri) {
+ return OC_Contacts_Addressbook::findCardWhereDAVDataIs($addressbookid,$carduri);
+
+ }
+
+ /**
+ * Creates a new card
+ *
+ * @param mixed $addressbookid
+ * @param string $carduri
+ * @param string $carddata
+ * @return bool
+ */
+ public function createCard($addressbookid, $carduri, $carddata) {
+ OC_Contacts_Addressbook::addCardFromDAVData($addressbookid, $carduri, $carddata);
+ return true;
+ }
+
+ /**
+ * Updates a card
+ *
+ * @param mixed $addressbookid
+ * @param string $carduri
+ * @param string $carddata
+ * @return bool
+ */
+ public function updateCard($addressbookid, $carduri, $carddata) {
+ return OC_Contacts_Addressbook::editCardFromDAVData($addressbookid, $carduri, $carddata);
+ }
+
+ /**
+ * Deletes a card
+ *
+ * @param mixed $addressbookid
+ * @param string $carduri
+ * @return bool
+ */
+ public function deleteCard($addressbookid, $carduri) {
+ return OC_Contacts_Addressbook::deleteCardFromDAVData($addressbookid, $carduri);
+ }
+}
diff --git a/apps/contacts/photo.php b/apps/contacts/photo.php
new file mode 100644
index 0000000000..62386421cd
--- /dev/null
+++ b/apps/contacts/photo.php
@@ -0,0 +1,85 @@
+.
+ *
+ */
+
+// Init owncloud
+require_once('../../lib/base.php');
+
+$id = $_GET['id'];
+
+$l10n = new OC_L10N('contacts');
+
+// Check if we are a user
+if( !OC_User::isLoggedIn()){
+ echo $l10n->t('You need to log in!');
+ exit();
+}
+
+
+$card = OC_Contacts_Addressbook::findCard( $id );
+if( $card === false ){
+ echo $l10n->t('Can not find Contact!');
+ exit();
+}
+
+$addressbook = OC_Contacts_Addressbook::findAddressbook( $card['addressbookid'] );
+if( $addressbook === false || $addressbook['userid'] != OC_USER::getUser()){
+ echo $l10n->t('This is not your contact!');
+ exit();
+}
+
+$content = Sabre_VObject_Reader::read($card['carddata']);
+
+// Photo :-)
+foreach($content->children as $child){
+ if($child->name == 'PHOTO'){
+ $mime = 'image/jpeg';
+ foreach($child->parameters as $parameter){
+ if( $parameter->name == 'TYPE' ){
+ $mime = $parameter->value;
+ }
+ }
+ $photo = base64_decode($child->value);
+ header('Content-Type: '.$mime);
+ header('Content-Length: ' . strlen($photo));
+ echo $photo;
+ exit();
+ }
+}
+// Logo :-/
+foreach($content->children as $child){
+ if($child->name == 'PHOTO'){
+ $mime = 'image/jpeg';
+ foreach($child->parameters as $parameter){
+ if($parameter->name == 'TYPE'){
+ $mime = $parameter->value;
+ }
+ }
+ $photo = base64_decode($child->value());
+ header('Content-Type: '.$mime);
+ header('Content-Length: ' . strlen($photo));
+ echo $photo;
+ exit();
+ }
+}
+
+// Not found :-(
+echo $l10n->t('This card does not contain photo data!');
diff --git a/apps/contacts/templates/_contacts.php b/apps/contacts/templates/_contacts.php
new file mode 100644
index 0000000000..bf633b79b0
--- /dev/null
+++ b/apps/contacts/templates/_contacts.php
@@ -0,0 +1,3 @@
+
+
+
diff --git a/apps/contacts/templates/_details.php b/apps/contacts/templates/_details.php
new file mode 100644
index 0000000000..e27b17ef2e
--- /dev/null
+++ b/apps/contacts/templates/_details.php
@@ -0,0 +1,4 @@
+Name
+
+
+
\ No newline at end of file
diff --git a/apps/contacts/templates/index.php b/apps/contacts/templates/index.php
new file mode 100644
index 0000000000..0cd214bfb1
--- /dev/null
+++ b/apps/contacts/templates/index.php
@@ -0,0 +1,13 @@
+
+
+
+
+ inc("_details"); ?>
+