vcard.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. <?php
  2. /**
  3. * ownCloud - Addressbook
  4. *
  5. * @author Jakob Sack
  6. * @copyright 2011 Jakob Sack mail@jakobsack.de
  7. * @copyright 2012 Thomas Tanghus <thomas@tanghus.net>
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  11. * License as published by the Free Software Foundation; either
  12. * version 3 of the License, or any later version.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
  18. *
  19. * You should have received a copy of the GNU Affero General Public
  20. * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  21. *
  22. */
  23. /*
  24. *
  25. * The following SQL statement is just a help for developers and will not be
  26. * executed!
  27. *
  28. * CREATE TABLE contacts_cards (
  29. * id INT(11) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
  30. * addressbookid INT(11) UNSIGNED NOT NULL,
  31. * fullname VARCHAR(255),
  32. * carddata TEXT,
  33. * uri VARCHAR(100),
  34. * lastmodified INT(11) UNSIGNED
  35. * );
  36. */
  37. /**
  38. * This class manages our vCards
  39. */
  40. class OC_Contacts_VCard{
  41. /**
  42. * @brief Returns all cards of an address book
  43. * @param integer $id
  44. * @return array
  45. *
  46. * The cards are associative arrays. You'll find the original vCard in
  47. * ['carddata']
  48. */
  49. public static function all($id){
  50. $result = null;
  51. if(is_array($id) && count($id) > 1) {
  52. $id_sql = join(',', array_fill(0, count($id), '?'));
  53. $prep = 'SELECT * FROM `*PREFIX*contacts_cards` WHERE `addressbookid` IN ('.$id_sql.') ORDER BY `fullname`';
  54. try {
  55. $stmt = OCP\DB::prepare( $prep );
  56. $result = $stmt->execute($id);
  57. } catch(Exception $e) {
  58. OCP\Util::writeLog('contacts','OC_Contacts_VCard:all:, exception: '.$e->getMessage(),OCP\Util::ERROR);
  59. OCP\Util::writeLog('contacts','OC_Contacts_VCard:all, ids: '.count($id).' '.join(',', $id),OCP\Util::DEBUG);
  60. OCP\Util::writeLog('contacts','SQL:'.$prep,OCP\Util::DEBUG);
  61. }
  62. } elseif($id) {
  63. if(is_array($id)) {
  64. if(count($id) == 0) {
  65. return array();
  66. }
  67. $id = $id[0];
  68. }
  69. try {
  70. $stmt = OCP\DB::prepare( 'SELECT * FROM `*PREFIX*contacts_cards` WHERE `addressbookid` = ? ORDER BY `fullname`' );
  71. $result = $stmt->execute(array($id));
  72. } catch(Exception $e) {
  73. OCP\Util::writeLog('contacts','OC_Contacts_VCard:all:, exception: '.$e->getMessage(),OCP\Util::ERROR);
  74. OCP\Util::writeLog('contacts','OC_Contacts_VCard:all, id: '. $id,OCP\Util::DEBUG);
  75. }
  76. } else {
  77. OCP\Util::writeLog('contacts','OC_Contacts_VCard:all: No ID given.',OCP\Util::ERROR);
  78. return array();
  79. }
  80. $cards = array();
  81. if(!is_null($result)) {
  82. while( $row = $result->fetchRow()){
  83. $cards[] = $row;
  84. }
  85. }
  86. return $cards;
  87. }
  88. /**
  89. * @brief Returns a card
  90. * @param integer $id
  91. * @return associative array
  92. */
  93. public static function find($id){
  94. $stmt = OCP\DB::prepare( 'SELECT * FROM `*PREFIX*contacts_cards` WHERE `id` = ?' );
  95. $result = $stmt->execute(array($id));
  96. return $result->fetchRow();
  97. }
  98. /**
  99. * @brief finds a card by its DAV Data
  100. * @param integer $aid Addressbook id
  101. * @param string $uri the uri ('filename')
  102. * @return associative array
  103. */
  104. public static function findWhereDAVDataIs($aid,$uri){
  105. $stmt = OCP\DB::prepare( 'SELECT * FROM `*PREFIX*contacts_cards` WHERE `addressbookid` = ? AND `uri` = ?' );
  106. $result = $stmt->execute(array($aid,$uri));
  107. return $result->fetchRow();
  108. }
  109. /**
  110. * @brief Format property TYPE parameters for upgrading from v. 2.1
  111. * @param $property Reference to a Sabre_VObject_Property.
  112. * In version 2.1 e.g. a phone can be formatted like: TEL;HOME;CELL:123456789
  113. * This has to be changed to either TEL;TYPE=HOME,CELL:123456789 or TEL;TYPE=HOME;TYPE=CELL:123456789 - both are valid.
  114. */
  115. public static function formatPropertyTypes(&$property) {
  116. foreach($property->parameters as $key=>&$parameter){
  117. $types = OC_Contacts_App::getTypesOfProperty($property->name);
  118. if(is_array($types) && in_array(strtoupper($parameter->name), array_keys($types)) || strtoupper($parameter->name) == 'PREF') {
  119. $property->parameters[] = new Sabre_VObject_Parameter('TYPE', $parameter->name);
  120. }
  121. unset($property->parameters[$key]);
  122. }
  123. }
  124. /**
  125. * @brief Decode properties for upgrading from v. 2.1
  126. * @param $property Reference to a Sabre_VObject_Property.
  127. * The only encoding allowed in version 3.0 is 'b' for binary. All encoded strings
  128. * must therefor be decoded and the parameters removed.
  129. */
  130. public static function decodeProperty(&$property) {
  131. // Check out for encoded string and decode them :-[
  132. foreach($property->parameters as $key=>&$parameter){
  133. if(strtoupper($parameter->name) == 'ENCODING') {
  134. if(strtoupper($parameter->value) == 'QUOTED-PRINTABLE') { // what kind of other encodings could be used?
  135. $property->value = quoted_printable_decode($property->value);
  136. unset($property->parameters[$key]);
  137. }
  138. } elseif(strtoupper($parameter->name) == 'CHARSET') {
  139. unset($property->parameters[$key]);
  140. }
  141. }
  142. }
  143. /**
  144. * @brief Checks if a contact with the same UID already exist in the address book.
  145. * @param $aid Address book ID.
  146. * @param $uid UID (passed by reference).
  147. * @returns true if the UID has been changed.
  148. */
  149. protected static function trueUID($aid, &$uid) {
  150. $stmt = OCP\DB::prepare( 'SELECT * FROM `*PREFIX*contacts_cards` WHERE `addressbookid` = ? AND `uri` = ?' );
  151. $uri = $uid.'.vcf';
  152. $result = $stmt->execute(array($aid,$uri));
  153. if($result->numRows() > 0){
  154. while(true) {
  155. $tmpuid = substr(md5(rand().time()),0,10);
  156. $uri = $tmpuid.'.vcf';
  157. $result = $stmt->execute(array($aid,$uri));
  158. if($result->numRows() > 0){
  159. continue;
  160. } else {
  161. $uid = $tmpuid;
  162. return true;
  163. }
  164. }
  165. } else {
  166. return false;
  167. }
  168. }
  169. /**
  170. * @brief Tries to update imported VCards to adhere to rfc2426 (VERSION: 3.0) and add mandatory fields if missing.
  171. * @param aid Address book id.
  172. * @param vcard An OC_VObject of type VCARD (passed by reference).
  173. */
  174. protected static function updateValuesFromAdd($aid, &$vcard) { // any suggestions for a better method name? ;-)
  175. $stringprops = array('N', 'FN', 'ORG', 'NICK', 'ADR', 'NOTE');
  176. $typeprops = array('ADR', 'TEL', 'EMAIL');
  177. $upgrade = false;
  178. $fn = $n = $uid = $email = $org = null;
  179. $version = $vcard->getAsString('VERSION');
  180. // Add version if needed
  181. if($version && $version < '3.0') {
  182. $upgrade = true;
  183. OCP\Util::writeLog('contacts','OC_Contacts_VCard::updateValuesFromAdd. Updating from version: '.$version,OCP\Util::DEBUG);
  184. }
  185. foreach($vcard->children as &$property){
  186. // Decode string properties and remove obsolete properties.
  187. if($upgrade && in_array($property->name, $stringprops)) {
  188. self::decodeProperty($property);
  189. }
  190. $property->value = str_replace("\r\n", "\n", iconv(mb_detect_encoding($property->value, 'UTF-8, ISO-8859-1'), 'utf-8', $property->value));
  191. if(in_array($property->name, $stringprops)) {
  192. $property->value = strip_tags($property->value);
  193. }
  194. // Fix format of type parameters.
  195. if($upgrade && in_array($property->name, $typeprops)) {
  196. OCP\Util::writeLog('contacts','OC_Contacts_VCard::updateValuesFromAdd. before: '.$property->serialize(),OCP\Util::DEBUG);
  197. self::formatPropertyTypes($property);
  198. OCP\Util::writeLog('contacts','OC_Contacts_VCard::updateValuesFromAdd. after: '.$property->serialize(),OCP\Util::DEBUG);
  199. }
  200. if($property->name == 'FN'){
  201. $fn = $property->value;
  202. }
  203. if($property->name == 'N'){
  204. $n = $property->value;
  205. }
  206. if($property->name == 'UID'){
  207. $uid = $property->value;
  208. }
  209. if($property->name == 'ORG'){
  210. $org = $property->value;
  211. }
  212. if($property->name == 'EMAIL' && is_null($email)){ // only use the first email as substitute for missing N or FN.
  213. $email = $property->value;
  214. }
  215. }
  216. // Check for missing 'N', 'FN' and 'UID' properties
  217. if(!$fn) {
  218. if($n && $n != ';;;;'){
  219. $fn = join(' ', array_reverse(array_slice(explode(';', $n), 0, 2)));
  220. } elseif($email) {
  221. $fn = $email;
  222. } elseif($org) {
  223. $fn = $org;
  224. } else {
  225. $fn = 'Unknown Name';
  226. }
  227. $vcard->setString('FN', $fn);
  228. OCP\Util::writeLog('contacts','OC_Contacts_VCard::updateValuesFromAdd. Added missing \'FN\' field: '.$fn,OCP\Util::DEBUG);
  229. }
  230. if(!$n || $n == ';;;;'){ // Fix missing 'N' field. Ugly hack ahead ;-)
  231. $slice = array_reverse(array_slice(explode(' ', $fn), 0, 2)); // Take 2 first name parts of 'FN' and reverse.
  232. if(count($slice) < 2) { // If not enought, add one more...
  233. $slice[] = "";
  234. }
  235. $n = implode(';', $slice).';;;';
  236. $vcard->setString('N', $n);
  237. OCP\Util::writeLog('contacts','OC_Contacts_VCard::updateValuesFromAdd. Added missing \'N\' field: '.$n,OCP\Util::DEBUG);
  238. }
  239. if(!$uid) {
  240. $vcard->setUID();
  241. $uid = $vcard->getAsString('UID');
  242. OCP\Util::writeLog('contacts','OC_Contacts_VCard::updateValuesFromAdd. Added missing \'UID\' field: '.$uid,OCP\Util::DEBUG);
  243. }
  244. if(self::trueUID($aid, $uid)) {
  245. $vcard->setString('UID', $uid);
  246. }
  247. $now = new DateTime;
  248. $vcard->setString('REV', $now->format(DateTime::W3C));
  249. }
  250. /**
  251. * @brief Adds a card
  252. * @param integer $aid Addressbook id
  253. * @param OC_VObject $card vCard file
  254. * @param string $uri the uri of the card, default based on the UID
  255. * @return insertid on success or null if no card.
  256. */
  257. public static function add($aid, OC_VObject $card, $uri=null, $isnew=false){
  258. if(is_null($card)){
  259. OCP\Util::writeLog('contacts','OC_Contacts_VCard::add. No vCard supplied', OCP\Util::ERROR);
  260. return null;
  261. };
  262. if(!$isnew) {
  263. OC_Contacts_App::loadCategoriesFromVCard($card);
  264. self::updateValuesFromAdd($aid, $card);
  265. }
  266. $card->setString('VERSION','3.0');
  267. // Add product ID is missing.
  268. $prodid = trim($card->getAsString('PRODID'));
  269. if(!$prodid) {
  270. $appinfo = OCP\App::getAppInfo('contacts');
  271. $appversion = OCP\App::getAppVersion('contacts');
  272. $prodid = '-//ownCloud//NONSGML '.$appinfo['name'].' '.$appversion.'//EN';
  273. $card->setString('PRODID', $prodid);
  274. }
  275. $fn = $card->getAsString('FN');
  276. if (empty($fn)) {
  277. $fn = '';
  278. }
  279. if (!$uri) {
  280. $uid = $card->getAsString('UID');
  281. $uri = $uid.'.vcf';
  282. }
  283. $data = $card->serialize();
  284. $stmt = OCP\DB::prepare( 'INSERT INTO `*PREFIX*contacts_cards` (`addressbookid`,`fullname`,`carddata`,`uri`,`lastmodified`) VALUES(?,?,?,?,?)' );
  285. $result = $stmt->execute(array($aid,$fn,$data,$uri,time()));
  286. $newid = OCP\DB::insertid('*PREFIX*contacts_cards');
  287. OC_Contacts_Addressbook::touch($aid);
  288. return $newid;
  289. }
  290. /**
  291. * @brief Adds a card with the data provided by sabredav
  292. * @param integer $id Addressbook id
  293. * @param string $uri the uri the card will have
  294. * @param string $data vCard file
  295. * @return insertid
  296. */
  297. public static function addFromDAVData($id,$uri,$data){
  298. $card = OC_VObject::parse($data);
  299. return self::add($id, $card, $uri);
  300. }
  301. /**
  302. * @brief Mass updates an array of cards
  303. * @param array $objects An array of [id, carddata].
  304. */
  305. public static function updateDataByID($objects){
  306. $stmt = OCP\DB::prepare( 'UPDATE `*PREFIX*contacts_cards` SET `carddata` = ?, `lastmodified` = ? WHERE `id` = ?' );
  307. $now = new DateTime;
  308. foreach($objects as $object) {
  309. $vcard = OC_VObject::parse($object[1]);
  310. if(!is_null($vcard)){
  311. $vcard->setString('REV', $now->format(DateTime::W3C));
  312. $data = $vcard->serialize();
  313. try {
  314. $result = $stmt->execute(array($data,time(),$object[0]));
  315. //OCP\Util::writeLog('contacts','OC_Contacts_VCard::updateDataByID, id: '.$object[0].': '.$object[1],OCP\Util::DEBUG);
  316. } catch(Exception $e) {
  317. OCP\Util::writeLog('contacts','OC_Contacts_VCard::updateDataByID:, exception: '.$e->getMessage(),OCP\Util::DEBUG);
  318. OCP\Util::writeLog('contacts','OC_Contacts_VCard::updateDataByID, id: '.$object[0],OCP\Util::DEBUG);
  319. }
  320. }
  321. }
  322. }
  323. /**
  324. * @brief edits a card
  325. * @param integer $id id of card
  326. * @param OC_VObject $card vCard file
  327. * @return boolean
  328. */
  329. public static function edit($id, OC_VObject $card){
  330. $oldcard = self::find($id);
  331. if(is_null($card)) {
  332. return false;
  333. }
  334. OC_Contacts_App::loadCategoriesFromVCard($card);
  335. $fn = $card->getAsString('FN');
  336. if (empty($fn)) {
  337. $fn = null;
  338. }
  339. $now = new DateTime;
  340. $card->setString('REV', $now->format(DateTime::W3C));
  341. $data = $card->serialize();
  342. $stmt = OCP\DB::prepare( 'UPDATE `*PREFIX*contacts_cards` SET `fullname` = ?,`carddata` = ?, `lastmodified` = ? WHERE `id` = ?' );
  343. $result = $stmt->execute(array($fn,$data,time(),$id));
  344. OC_Contacts_Addressbook::touch($oldcard['addressbookid']);
  345. return true;
  346. }
  347. /**
  348. * @brief edits a card with the data provided by sabredav
  349. * @param integer $id Addressbook id
  350. * @param string $uri the uri of the card
  351. * @param string $data vCard file
  352. * @return boolean
  353. */
  354. public static function editFromDAVData($aid,$uri,$data){
  355. $oldcard = self::findWhereDAVDataIs($aid,$uri);
  356. $card = OC_VObject::parse($data);
  357. if(!$card) {
  358. OCP\Util::writeLog('contacts','OC_Contacts_VCard::editFromDAVData. Unable to parse VCARD, uri: '.$uri,OCP\Util::ERROR);
  359. return false;
  360. }
  361. return self::edit($oldcard['id'], $card);
  362. }
  363. /**
  364. * @brief deletes a card
  365. * @param integer $id id of card
  366. * @return boolean
  367. */
  368. public static function delete($id){
  369. // FIXME: Add error checking.
  370. $stmt = OCP\DB::prepare( 'DELETE FROM `*PREFIX*contacts_cards` WHERE `id` = ?' );
  371. $stmt->execute(array($id));
  372. return true;
  373. }
  374. /**
  375. * @brief deletes a card with the data provided by sabredav
  376. * @param integer $aid Addressbook id
  377. * @param string $uri the uri of the card
  378. * @return boolean
  379. */
  380. public static function deleteFromDAVData($aid,$uri){
  381. // FIXME: Add error checking. Deleting a card gives an Kontact/Akonadi error.
  382. $stmt = OCP\DB::prepare( 'DELETE FROM `*PREFIX*contacts_cards` WHERE `addressbookid` = ? AND `uri`=?' );
  383. $stmt->execute(array($aid,$uri));
  384. OC_Contacts_Addressbook::touch($aid);
  385. return true;
  386. }
  387. /**
  388. * @brief Escapes delimiters from an array and returns a string.
  389. * @param array $value
  390. * @param char $delimiter
  391. * @return string
  392. */
  393. public static function escapeDelimiters($value, $delimiter=';') {
  394. foreach($value as &$i ) {
  395. $i = implode("\\$delimiter", explode($delimiter, $i));
  396. }
  397. return implode($delimiter, $value);
  398. }
  399. /**
  400. * @brief Creates an array out of a multivalue property
  401. * @param string $value
  402. * @param char $delimiter
  403. * @return array
  404. */
  405. public static function unescapeDelimiters($value, $delimiter=';') {
  406. $array = explode($delimiter,$value);
  407. for($i=0;$i<count($array);$i++) {
  408. if(substr($array[$i],-1,1)=="\\") {
  409. if(isset($array[$i+1])) {
  410. $array[$i] = substr($array[$i],0,count($array[$i])-2).$delimiter.$array[$i+1];
  411. unset($array[$i+1]);
  412. } else {
  413. $array[$i] = substr($array[$i],0,count($array[$i])-2).$delimiter;
  414. }
  415. $i = $i - 1;
  416. }
  417. }
  418. $array = array_map('trim', $array);
  419. return $array;
  420. }
  421. /**
  422. * @brief Data structure of vCard
  423. * @param object $property
  424. * @return associative array
  425. *
  426. * look at code ...
  427. */
  428. public static function structureContact($object){
  429. $details = array();
  430. foreach($object->children as $property){
  431. $temp = self::structureProperty($property);
  432. if(!is_null($temp)) {
  433. if(array_key_exists($property->name,$details)){
  434. $details[$property->name][] = $temp;
  435. }
  436. else{
  437. $details[$property->name] = array($temp);
  438. }
  439. }
  440. }
  441. return $details;
  442. }
  443. /**
  444. * @brief Data structure of properties
  445. * @param object $property
  446. * @return associative array
  447. *
  448. * returns an associative array with
  449. * ['name'] name of property
  450. * ['value'] htmlspecialchars escaped value of property
  451. * ['parameters'] associative array name=>value
  452. * ['checksum'] checksum of whole property
  453. * NOTE: $value is not escaped anymore. It shouldn't make any difference
  454. * but we should look out for any problems.
  455. */
  456. public static function structureProperty($property){
  457. $value = $property->value;
  458. //$value = htmlspecialchars($value);
  459. if($property->name == 'ADR' || $property->name == 'N'){
  460. $value = self::unescapeDelimiters($value);
  461. } elseif($property->name == 'BDAY') {
  462. if(strpos($value, '-') === false) {
  463. if(strlen($value) >= 8) {
  464. $value = substr($value, 0, 4).'-'.substr($value, 4, 2).'-'.substr($value, 6, 2);
  465. } else {
  466. return null; // Badly malformed :-(
  467. }
  468. }
  469. }
  470. $temp = array(
  471. 'name' => $property->name,
  472. 'value' => $value,
  473. 'parameters' => array(),
  474. 'checksum' => md5($property->serialize()));
  475. foreach($property->parameters as $parameter){
  476. // Faulty entries by kaddressbook
  477. // Actually TYPE=PREF is correct according to RFC 2426
  478. // but this way is more handy in the UI. Tanghus.
  479. if($parameter->name == 'TYPE' && $parameter->value == 'PREF'){
  480. $parameter->name = 'PREF';
  481. $parameter->value = '1';
  482. }
  483. // NOTE: Apparently Sabre_VObject_Reader can't always deal with value list parameters
  484. // like TYPE=HOME,CELL,VOICE. Tanghus.
  485. if (in_array($property->name, array('TEL', 'EMAIL')) && $parameter->name == 'TYPE'){
  486. if (isset($temp['parameters'][$parameter->name])){
  487. $temp['parameters'][$parameter->name][] = $parameter->value;
  488. }
  489. else{
  490. $temp['parameters'][$parameter->name] = array($parameter->value);
  491. }
  492. }
  493. else{
  494. $temp['parameters'][$parameter->name] = $parameter->value;
  495. }
  496. }
  497. return $temp;
  498. }
  499. /**
  500. * @brief Move card(s) to an address book
  501. * @param integer $aid Address book id
  502. * @param $id Array or integer of cards to be moved.
  503. * @return boolean
  504. *
  505. */
  506. public static function moveToAddressBook($aid, $id){
  507. OC_Contacts_App::getAddressbook($aid); // check for user ownership.
  508. if(is_array($id)) {
  509. $id_sql = join(',', array_fill(0, count($id), '?'));
  510. $prep = 'UPDATE `*PREFIX*contacts_cards` SET `addressbookid` = ? WHERE `id` IN ('.$id_sql.')';
  511. try {
  512. $stmt = OCP\DB::prepare( $prep );
  513. //$aid = array($aid);
  514. $vals = array_merge((array)$aid, $id);
  515. $result = $stmt->execute($vals);
  516. } catch(Exception $e) {
  517. OCP\Util::writeLog('contacts','OC_Contacts_VCard::moveToAddressBook:, exception: '.$e->getMessage(),OCP\Util::DEBUG);
  518. OCP\Util::writeLog('contacts','OC_Contacts_VCard::moveToAddressBook, ids: '.join(',', $vals),OCP\Util::DEBUG);
  519. OCP\Util::writeLog('contacts','SQL:'.$prep,OCP\Util::DEBUG);
  520. return false;
  521. }
  522. } else {
  523. try {
  524. $stmt = OCP\DB::prepare( 'UPDATE `*PREFIX*contacts_cards` SET `addressbookid` = ? WHERE `id` = ?' );
  525. $result = $stmt->execute(array($aid, $id));
  526. } catch(Exception $e) {
  527. OCP\Util::writeLog('contacts','OC_Contacts_VCard::moveToAddressBook:, exception: '.$e->getMessage(),OCP\Util::DEBUG);
  528. OCP\Util::writeLog('contacts','OC_Contacts_VCard::moveToAddressBook, id: '.$id,OCP\Util::DEBUG);
  529. return false;
  530. }
  531. }
  532. OC_Contacts_Addressbook::touch($aid);
  533. return true;
  534. }
  535. }