<?php

require_once(HORDE_BASE . '/lib/Kolab.php');

class Turba_Driver_kolab extends Turba_Driver {

    var $_folder;
    var $_server;
    var $imap;

    function Turba_Driver_kolab($params)
    {
        $this->type = 'kolab';
        $this->params = $params;
        $this->_folder = $params['folder'];
        $this->_server = $params['server'];

        $this->imap = Kolab::OpenImapConnection($this->_server, $this->_folder);
        if (!$this->imap) return PEAR::raiseError('Unable to open IMAP connection.');
    }

    function close()
    {
        return Kolab::CloseIMAPConnection($this->imap);
    }

    function search($criteria, $fields, $strict_fields = array(), $match = TURBA_SEARCH_AND)
    {

      $results = array();
      /* Get how many messages is in folder */
      $msgs = imap_num_msg( $this->imap );
      $sort = imap_sort( $this->imap, SORTDATE, 0 );
      $entry = array();
      for ( $i = 0; $i < $msgs; $i++) {
        $vcard = imap_body( $this->imap, $sort[$i] );
        $vc = $this->loadVCard( $vcard );
        if (count($criteria) > 0 ) {
          foreach( $criteria as $key => $val ) {
            /* This needs work to do advance searches */
            /* filter for each criteria */
            if( preg_match("/" . $val . "/i", $vc[$key] ) ) {
              foreach($fields as $field) {
                $entry[$field] = $vc[$field];
              }
            }
          }
        }
        else {
          foreach($fields as $field) {
            $entry[$field] = $vc[$field];
          }
        }
        $results[] = $entry;
      }
		

      return $results;
    }

  /**
   * Read the given data from the Kolab Contacts Folder and returns
   * the result's fields.
   *
   * @param $criteria      Search criteria.
   * @param $id            Data identifier.
   * @param $fields        List of fields to return.
   *
   * @return               Hash containing the search results.
   */
  function read($criteria, $id, $fields)
    {

      $result = array();

      $sort = imap_sort($this->imap, SORTDATE, 0);
      $msgs = imap_num_msg( $this->imap );
      $entry = array();
      // Go through each message to search for the types and values
      for ( $i = 0; $i < $msgs; $i++) {
        $vcard = imap_body( $this->imap, $sort[$i]);
        $vc = $this->loadVCard( $vcard );
        if ( preg_match( "/" . $id . "/", $vc[$criteria] ) ) {
          foreach($fields as $field) {
            $entry[$field] = $vc[$field];
          }
	
          $result[] = $entry;
        }
      }

      return $result;
			

    }

  /**
   * Adds the specified object to the Kolab Contacts Folder.
   */
  function addObject($attributes)
    {
    	$nl = "\r\n";
      if ( $attributes['fn'] == "" )
        return new PEAR_Error("Must specify a Name");

      $vcard = $this->createVCard( $attributes );
	
      // Create message header and add vCard
      $userID = Auth::getAuth();
      $msg = 'Content-Type: Text/X-VCard; charset="utf-8"'.$nl
	      .'From: '.$userID.$nl
	      .'Reply-To:'.$nl
	      .'To: '.$attributes['fn'].$nl
	      .'Subject: Contact: '.$attributes['fn'].$nl
	      .'User-Agent: Horde/Turba/Kolab'.$nl
	      .'Date: '.date('r').$nl
	      .$nl
	      .$vcard;

      $connstr = '{' . $this->_server . ':143/imap}' . $this->_folder;
      if (!imap_append( $this->imap, $connstr, $msg )){ 
        return new PEAR_Error( 'Could not save message in Contacts folder! because, '.imap_last_error() );
      }
      else {
        return true;
      }
    }

  /**
   * Deletes the specified object from the Kolab Contacts Folder.
     
  */
  function removeObject($object_key, $object_id)
    {
    	// Find message with UID and delete and expunge the mailbox
      $msg = imap_search( $this->imap, "BODY \"".strtoupper($object_key).":".$object_id."\"" );
      if ( count($msg) == 0 || count($msg) > 1 )
        return new PEAR_Error('Could not delete, there are none found or no many found');
      else
        {
          if(!imap_delete( $this->imap, $msg[0] ))
            return new PEAR_Error('Could not delete, because: '.imap_last_error() );
          else
            imap_expunge( $this->imap );
        }
      return true;
    }

  /**
   * Saves the specified object in the Kolab Contacts Folder.
   */
  function setObject($object_key, $object_id, $attributes)
    {
      // Find message that contains the UID
    	$msg = imap_search( $this->imap, "BODY \"".strtoupper($object_key).":".$object_id."\"" );
      if ( count($msg) == 0 || count($msg) > 1 )
        return new PEAR_Error('Could not edit, there are none found or no many found');
	
      // Save original to get the types not supported by us
      $orig_vcard = $this->loadVCard(imap_body( $this->imap, $msg[0] ));
      // Get original header, we don't change header info on already existing contacts
      $header = imap_fetchheader( $this->imap, $msg[0], FT_INTERNAL );
      $rev = date("Ymd\THis\Z"); // create the REV: type so that other clients can see this contact has changed
      $vcard = $this->createVCard( $attributes, $rev, $orig_vcard['otherstuff'] );
		
      $m = $header . $vcard;
      $connstr = '{' . $this->server . ':143/imap}' . $this->folder;
      // Delete message and re-create it
      if (!imap_append( $this->imap, $connstr, $m))
        return new PEAR_Error( 'Could not save message! Because: '.imap_last_error());
      else{
        imap_delete( $this->imap, $msg[0] );
        imap_expunge($this->imap );
        return true;
      }

    }

  /**
   * Create an object key for a new object.
   *
   * @param array $attributes  The attributes (in driver keys) of the
   *                           object being added.
   *
   * @return string  A unique ID for the new object.
   */
  function makeKey($attributes)
    {
      return md5(uniqid(rand()));
    }

  /**
   * Loads a vCard message into an array.
   *
   * @vcard string The vCard string message.
   *
   */
  function loadVCard( $vcard )
    {
      // Put whole message in nice array
      $lines = explode( "\n", $vcard );
      $data = array();
      foreach( $lines as $line )
        {
          $k = substr($line, 0, strpos($line,':'));
          $v = substr($line, strpos($line,':')+1);
          $attributes = explode(';', $k);
          $k = array_shift($attributes);
          if ( strncmp($k,"X-",2) != 0 )
            $k = strtolower($k);
          foreach($attributes as $attr)
            {
              $k .= ';' . strtolower($attr);
            }
          $data[$k] = $v;
        }

      // Create the expected array that the driver needs
      $vc = array();
      $vc['uid'] = trim($data['uid'],"\r\n");
      $vc['fn'] = $data['fn'];
      $vc['nickname'] = $data['nickname'];
      $vc['email'] = $data['email'];
      $vc['org'] = $data['org'];
      $address = explode(';',$data['adr;type=home']);
      /* KMail don't seem to split the street address with ',' which
       * is explained in the RFC2426, so I added extra replace for
       * new lines.
       */
      $vc['home_street'] = str_replace('\n',"\n",$address[2]);
      $vc['home_street'] = str_replace(',',"\n",$vc['home_street']);
      $vc['home_postalbox'] = $address[0];
      $vc['home_locality'] = $address[3];
      $vc['home_region'] = $address[4];
      $vc['home_code'] = $address[5];
      $vc['home_country'] = $address[6];
      $address = explode(';',$data['adr;type=work']);
      // KMail See above
      $vc['work_street'] = str_replace('\n', "\n", $address[2]);
      $vc['work_street'] = str_replace(',',"\n", $vc['work_street']);
      $vc['work_postalbox'] = $address[0];
      $vc['work_locality'] = $address[3];
      $vc['work_region'] = $address[4];
      $vc['work_code'] = $address[5];
      $vc['work_country'] = $address[6];
      $vc['work_phone'] = $data['tel;type=work'];
      $vc['home_phone'] = $data['tel;type=home'];
      $vc['cell_phone'] = $data['tel;type=cell'];
      $vc['fax'] = $data['tel;type=fax'];
      $vc['note'] = $data['note'];
	
      // All the other types that we don't support
      $vc['otherstuff'] = array();
      foreach($data as $key => $value )
        {
          switch($key){
          case "uid":
          case "fn":
          case "n":
          case "nickname":
          case "email":
          case "org":
          case "adr;type=home":
          case "adr;type=work":
          case "tel;type=work":
          case "tel;type=home":
          case "tel;type=cell":
          case "tel;type=fax":
          case "note":
          case "name":
          case "version":
          case "begin":
          case "rev":
          case "class":
          case "end":
          case "":
            break;
          default:
            $vc['otherstuff'][$key] = trim($value,"\r\n");
            break;
          }
        }

      return $vc;
    }
    
  /**
   * Creates a vCard body message.
   *
   * @attributes array  The attributes to be converted to msgbody.
   *
   * @rev string The REV tag date
   *
   * @otherstuff array An Array of types that was not created with
   *			 this Kolab/Turba client.
   */
  function createVCard( $attributes, $rev = "", $otherstuff = array() )
    {
      $nl = "\r\n";
      if ( $attributes['fn'] == "" )
        return new PEAR_Error("Must specify a Name");

      $vcard = 'BEGIN:VCARD'.$nl
        .'VERSION: 3.0'.$nl
        .'NAME:'.$attributes['fn'].$nl
        .'UID:'.trim($attributes['uid'],$nl).$nl
        .'FN:'.$attributes['fn'].$nl;


      if ( $attributes['email'] != "" )
        $vcard .= 'EMAIL:'.$attributes['email'].$nl;
	
	
      if(!strrchr($attributes['fn']," ")){
        $surname = "";
        $name = $attributes['fn'];
      }
      else {
        $surname = substr( strrchr($attributes['fn']," "),1);
        $name = substr( $attributes['fn'], 0, strpos($attributes['fn']," "));
      }
		
      $vcard .= 'N:'.$surname.';'.$name.';;;'.$nl;
      $vcard .= $attributes['nickname'] != "" ? 'NICKNAME:'.$attributes['nickname'].$nl : null;
	
      $vcard .= $attributes['org'] != "" ? 'ORG:'.$attributes['org'].$nl : "";
      $vcard .= $attributes['note'] != "" ? 'NOTE:'.$attributes['note'].$nl : "";
		 
      $homeaddy = $attributes['home_postalbox'].';;'.str_replace("\r\n",',',$attributes['home_street'])
		    .';'.$attributes['home_locality'].';'.$attributes['home_region'].';'.$attributes['home_code']
		    .';'.$attributes['home_country'];
      $vcard .= trim($homeaddy,';') != "" ? 'ADR;TYPE=home:'.$homeaddy.$nl : "";
		
      $workaddy = $attributes['work_postalbox'].';;'.str_replace("\r\n",',',$attributes['work_street'])
		    .';'.$attributes['work_locality'].';'.$attributes['work_region'].';'.$attributes['work_code']
		    .';'.$attributes['work_country'];
      $vcard .= trim($workaddy,';') != "" ? 'ADR;TYPE=work:'.$workaddy.$nl : "";
      $vcard .= $attributes['work_phone'] != "" ? 'TEL;TYPE=work:'.$attributes['work_phone'].$nl : "";
      $vcard .= $attributes['home_phone'] != "" ? 'TEL;TYPE=home:'.$attributes['home_phone'].$nl : "";
      $vcard .= $attributes['cell_phone'] != "" ? 'TEL;TYPE=cell:'.$attributes['cell_phone'].$nl : "";
      $vcard .= $attributes['fax'] != "" ? 'TEL;TYPE=fax:'.$attributes['fax'].$nl : "";
      $vcard .= $rev != "" ? 'REV:'.$rev.$nl : "";

      // Add the types that we did not create
      foreach( $otherstuff as $key => $value ){
        if ( strncmp($key, "X-",2) != 0 )
          $vcard .= strtoupper($key) . ":" . $value . $nl;
        else
          $vcard .= $key . ":" . $value . $nl;
      }

      $vcard .= 'CLASS:PRIVATE'.$nl.'END:VCARD'.$nl;
	
      return $vcard;
    }
}
