Index: index.php
===================================================================
--- index.php	(revision 360)
+++ index.php	(working copy)
@@ -301,8 +301,8 @@
   if ($_action=='list' && isset($_GET['_remote']))
     include('program/steps/mail/list.inc');
 
-   if ($_action=='search')
-     include('program/steps/mail/search.inc');
+  if ($_action=='search')
+    include('program/steps/mail/search.inc');
      
   if ($_action=='spell')
     include('program/steps/mail/spell.inc');
@@ -338,9 +338,21 @@
 
   if ($_action=='list' && $_GET['_remote'])
     include('program/steps/addressbook/list.inc');
-
+    
+  if ($_action=='moveto')
+    include('program/steps/addressbook/move.inc');
+    
+  if ($_action=='groupfolder')
+    include('program/steps/addressbook/group.inc');
+    
+  if ($_action=='vcard')
+    include('program/steps/addressbook/vcard.inc');
+    
   if ($_action=='ldappublicsearch')
     include('program/steps/addressbook/ldapsearchform.inc');
+  
+  if ($_action=='search')
+    include('program/steps/addressbook/search.inc');
   }
 
 
Index: program/include/main.inc
===================================================================
--- program/include/main.inc	(revision 360)
+++ program/include/main.inc	(working copy)
@@ -381,6 +381,7 @@
     
   $OUTPUT->add_script($javascript, 'head');
   $OUTPUT->add_script($javascript_foot, 'foot');
+  $OUTPUT->include_script('XMLHttpRequest.js');
   $OUTPUT->include_script('common.js');
   $OUTPUT->include_script('app.js');
   $OUTPUT->scripts_path = 'program/js/';
@@ -1202,13 +1203,17 @@
         'receiptcheckbox' => 'rcmail_receipt_checkbox',
         
         // ADDRESS BOOK
+        'groupboxlist' => 'rcmail_groupbox_list',
         'addresslist' => 'rcmail_contacts_list',
         'addressframe' => 'rcmail_contact_frame',
         'recordscountdisplay' => 'rcmail_rowcount_display',
         'contactdetails' => 'rcmail_contact_details',
         'contacteditform' => 'rcmail_contact_editform',
+        'contactssearchform' => 'rccontacts_search_form',
+        'vcardform' => 'rcmail_vcard_form',
         'ldappublicsearch' => 'rcmail_ldap_public_search_form',
         'ldappublicaddresslist' => 'rcmail_ldap_public_list',
+        'addgroupform' => 'rcmail_add_group_form',
 
         // USER SETTINGS
         'userprefs' => 'rcmail_user_prefs_form',
Index: program/include/rcube_shared.inc
===================================================================
--- program/include/rcube_shared.inc	(revision 360)
+++ program/include/rcube_shared.inc	(working copy)
@@ -1492,6 +1492,65 @@
         return $index;
       }
   }
+// create a hierarchical array of the groupbox list
+function rcmail_build_folder_tree(&$arrFolders, $folder, $delm='/', $path='')
+  {
+  $pos = strpos($folder, $delm);
+  if ($pos !== false)
+    {
+    $subFolders = substr($folder, $pos+1);
+    $currentFolder = substr($folder, 0, $pos);
+    }
+  else
+    {
+    $subFolders = false;
+    $currentFolder = $folder;
+    }
 
+  $path .= $currentFolder;
 
+  if (!isset($arrFolders[$currentFolder]))
+    {
+    $arrFolders[$currentFolder] = array('id' => $path,
+                                        'name' => rcube_charset_convert($currentFolder, 'UTF-7'),
+                                        'folders' => array());
+    }
+
+  if (!empty($subFolders))
+    rcmail_build_folder_tree($arrFolders[$currentFolder]['folders'], $subFolders, $delm, $path.$delm);
+  }
+// return html for a flat list <select> for the mailbox tree
+function rcmail_render_folder_tree_select(&$arrFolders, &$special, &$mbox_name, $maxlength, $nestLevel=0)
+  {
+  global $IMAP, $OUTPUT;
+
+  $idx = 0;
+  $out = '';
+  foreach ($arrFolders as $key=>$folder)
+    {
+    $folder_lc = strtolower($folder['id']);
+    if (in_array($folder_lc, $special))
+      $foldername = rcube_label($folder_lc);
+    else
+      {
+      $foldername = $folder['name'];
+      
+      // shorten the folder name to a given length
+      if ($maxlength && $maxlength>1)
+        $foldername = abbrevate_string($foldername, $maxlength);
+      }
+
+    $out .= sprintf('<option value="%s">%s%s</option>'."\n",
+                    htmlspecialchars($folder['id']),
+                    str_repeat('&nbsp;', $nestLevel*4),
+                    rep_specialchars_output($foldername, 'html', 'all'));
+
+    if (!empty($folder['folders']))
+      $out .= rcmail_render_folder_tree_select($folder['folders'], $special, $mbox_name, $maxlength, $nestLevel+1);
+
+    $idx++;
+    }
+
+  return $out;
+  }
 ?>
Index: program/include/vcardparse.inc
===================================================================
--- program/include/vcardparse.inc	(revision 0)
+++ program/include/vcardparse.inc	(revision 0)
@@ -0,0 +1,841 @@
+<?php
+/* vim: set expandtab tabstop=4 softtabstop=4 shiftwidth=4: */ 
+// +----------------------------------------------------------------------+ 
+// | PHP version 4                                                        | 
+// +----------------------------------------------------------------------+ 
+// | Copyright (c) 1997-2002 The PHP Group                                | 
+// +----------------------------------------------------------------------+ 
+// | This source file is subject to version 2.0 of the PHP license,       | 
+// | that is bundled with this package in the file LICENSE, and is        | 
+// | available at through the world-wide-web at                           | 
+// | http://www.php.net/license/2_02.txt.                                 | 
+// | If you did not receive a copy of the PHP license and are unable to   | 
+// | obtain it through the world-wide-web, please send a note to          | 
+// | license@php.net so we can mail you a copy immediately.               | 
+// +----------------------------------------------------------------------+ 
+// | Authors: Paul M. Jones <pmjones@php.net>                             | 
+// +----------------------------------------------------------------------+ 
+// 
+// $Id: Contact_Vcard_Parse.php,v 1.4 2005/05/28 15:40:17 pmjones Exp $ 
+
+
+/**
+* 
+* Parser for vCards.
+*
+* This class parses vCard 2.1 and 3.0 sources from file or text into a
+* structured array.
+* 
+* Usage:
+* 
+* <code>
+*     // include this class file
+*     require_once 'Contact_Vcard_Parse.php';
+*     
+*     // instantiate a parser object
+*     $parse = new Contact_Vcard_Parse();
+*     
+*     // parse a vCard file and store the data
+*     // in $cardinfo
+*     $cardinfo = $parse->fromFile('sample.vcf');
+*     
+*     // view the card info array
+*     echo '<pre>';
+*     print_r($cardinfo);
+*     echo '</pre>';
+* </code>
+* 
+*
+* @author Paul M. Jones <pmjones@php.net>
+* 
+* @package Contact_Vcard_Parse
+* 
+* @version 1.31
+* 
+*/
+
+class Contact_Vcard_Parse {
+    
+    
+    /**
+    * 
+    * Reads a file for parsing, then sends it to $this->fromText()
+    * and returns the results.
+    * 
+    * @access public
+    * 
+    * @param array $filename The filename to read for vCard information.
+    * 
+    * @return array An array of of vCard information extracted from the
+    * file.
+    * 
+    * @see Contact_Vcard_Parse::fromText()
+    * 
+    * @see Contact_Vcard_Parse::_fromArray()
+    * 
+    */
+    
+    function fromFile($filename, $decode_qp = true)
+    {
+        $text = $this->fileGetContents($filename);
+        
+        if ($text === false) {
+            return false;
+        } else {
+            // dump to, and get return from, the fromText() method.
+            return $this->fromText($text, $decode_qp);
+        }
+    }
+    
+    
+    /**
+    * 
+    * Reads the contents of a file.  Included for users whose PHP < 4.3.0.
+    * 
+    * @access public
+    * 
+    * @param array $filename The filename to read for vCard information.
+    * 
+    * @return string|bool The contents of the file if it exists and is
+    * readable, or boolean false if not.
+    * 
+    * @see Contact_Vcard_Parse::fromFile()
+    * 
+    */
+    
+    function fileGetContents($filename)
+    {
+        if (file_exists($filename) &&
+            is_readable($filename)) {
+            
+            $text = '';
+            $len = filesize($filename);
+            
+            $fp = fopen($filename, 'r');
+            while ($line = fread($fp, filesize($filename))) {
+                $text .= $line;
+            }
+            fclose($fp);
+            
+            return $text;
+            
+        } else {
+        
+            return false;
+            
+        }
+    }
+    
+    
+    /**
+    * 
+    * Prepares a block of text for parsing, then sends it through and
+    * returns the results from $this->fromArray().
+    * 
+    * @access public
+    * 
+    * @param array $text A block of text to read for vCard information.
+    * 
+    * @return array An array of vCard information extracted from the
+    * source text.
+    * 
+    * @see Contact_Vcard_Parse::_fromArray()
+    * 
+    */
+    
+    function fromText($text, $decode_qp = true)
+    {
+        // convert all kinds of line endings to Unix-standard and get
+        // rid of double blank lines.
+        $this->convertLineEndings($text);
+        
+        // unfold lines.  concat two lines where line 1 ends in \n and
+        // line 2 starts with a whitespace character.  only removes
+        // the first whitespace character, leaves others in place.
+        $fold_regex = '(\n)([ |\t])';
+        $text = preg_replace("/$fold_regex/i", "", $text);
+        
+        // massage for Macintosh OS X Address Book (remove nulls that
+        // Address Book puts in for unicode chars)
+        $text = str_replace("\x00", '', $text);
+        
+        // convert the resulting text to an array of lines
+        $lines = explode("\n", $text);
+        
+        // parse the array of lines and return vCard info
+        return $this->_fromArray($lines, $decode_qp);
+    }
+    
+    
+    /**
+    * 
+    * Converts line endings in text.
+    * 
+    * Takes any text block and converts all line endings to UNIX
+    * standard. DOS line endings are \r\n, Mac are \r, and UNIX is \n.
+    *
+    * NOTE: Acts on the text block in-place; does not return a value.
+    * 
+    * @access public
+    * 
+    * @param string $text The string on which to convert line endings.
+    * 
+    * @return void
+    * 
+    */
+    
+    function convertLineEndings(&$text)
+    {
+        // DOS
+        $text = str_replace("\r\n", "\n", $text);
+        
+        // Mac
+        $text = str_replace("\r", "\n", $text);
+    }
+    
+    
+    /**
+    * 
+    * Splits a string into an array at semicolons.  Honors backslash-
+    * escaped semicolons (i.e., splits at ';' not '\;').
+    * 
+    * @access public
+    * 
+    * @param string $text The string to split into an array.
+    * 
+    * @param bool $convertSingle If splitting the string results in a
+    * single array element, return a string instead of a one-element
+    * array.
+    * 
+    * @return mixed An array of values, or a single string.
+    * 
+    */
+    
+    function splitBySemi($text, $convertSingle = false)
+    {
+        // we use these double-backs (\\) because they get get converted
+        // to single-backs (\) by preg_split.  the quad-backs (\\\\) end
+        // up as as double-backs (\\), which is what preg_split requires
+        // to indicate a single backslash (\). what a mess.
+        $regex = '(?<!\\\\)(\;)';
+        $tmp = preg_split("/$regex/i", $text);
+        
+        // if there is only one array-element and $convertSingle is
+        // true, then return only the value of that one array element
+        // (instead of returning the array).
+        if ($convertSingle && count($tmp) == 1) {
+            return $tmp[0];
+        } else {
+            return $tmp;
+        }
+    }
+    
+    
+    /**
+    * 
+    * Splits a string into an array at commas.  Honors backslash-
+    * escaped commas (i.e., splits at ',' not '\,').
+    * 
+    * @access public
+    * 
+    * @param string $text The string to split into an array.
+    * 
+    * @param bool $convertSingle If splitting the string results in a
+    * single array element, return a string instead of a one-element
+    * array.
+    * 
+    * @return mixed An array of values, or a single string.
+    * 
+    */
+    
+    function splitByComma($text, $convertSingle = false)
+    {
+        // we use these double-backs (\\) because they get get converted
+        // to single-backs (\) by preg_split.  the quad-backs (\\\\) end
+        // up as as double-backs (\\), which is what preg_split requires
+        // to indicate a single backslash (\). ye gods, how ugly.
+        $regex = '(?<!\\\\)(\,)';
+        $tmp = preg_split("/$regex/i", $text);
+        
+        // if there is only one array-element and $convertSingle is
+        // true, then return only the value of that one array element
+        // (instead of returning the array).
+        if ($convertSingle && count($tmp) == 1) {
+            return $tmp[0];
+        } else {
+            return $tmp;
+        }
+    }
+    
+    
+    /**
+    * 
+    * Used to make string human-readable after being a vCard value.
+    * 
+    * Converts...
+    *     \: => :
+    *     \; => ;
+    *     \, => ,
+    *     literal \n => newline
+    * 
+    * @access public
+    * 
+    * @param mixed $text The text to unescape.
+    * 
+    * @return void
+    * 
+    */
+    
+    function unescape(&$text)
+    {
+        if (is_array($text)) {
+            foreach ($text as $key => $val) {
+                $this->unescape($val);
+                $text[$key] = $val;
+            }
+        } else {
+            $text = str_replace('\:', ':', $text);
+            $text = str_replace('\;', ';', $text);
+            $text = str_replace('\,', ',', $text);
+            $text = str_replace('\n', "\n", $text);
+        }
+    }
+    
+    
+    /**
+    *
+    * Emulated destructor.
+    *
+    * @access private
+    * @return boolean true
+    *
+    */
+    
+    function _Contact_Vcard_Parse()
+    {
+        return true;
+    }
+    
+    
+    /**
+    * 
+    * Parses an array of source lines and returns an array of vCards.
+    * Each element of the array is itself an array expressing the types,
+    * parameters, and values of each part of the vCard. Processes both
+    * 2.1 and 3.0 vCard sources.
+    *
+    * @access private
+    * 
+    * @param array $source An array of lines to be read for vCard
+    * information.
+    * 
+    * @return array An array of of vCard information extracted from the
+    * source array.
+    * 
+    */
+    
+    function _fromArray($source, $decode_qp = true)
+    {
+        // the info array will hold all resulting vCard information.
+        $info = array();
+        
+        // tells us whether the source text indicates the beginning of a
+        // new vCard with a BEGIN:VCARD tag.
+        $begin = false;
+        
+        // holds information about the current vCard being read from the
+        // source text.
+        $card = array();
+        
+        // loop through each line in the source array
+        foreach ($source as $line) {
+            
+            // if the line is blank, skip it.
+            if (trim($line) == '') {
+                continue;
+            }
+            
+            // find the first instance of ':' on the line.  The part
+            // to the left of the colon is the type and parameters;
+            // the part to the right of the colon is the value data.
+            $pos = strpos($line, ':');
+            
+            // if there is no colon, skip the line.
+            if ($pos === false) {
+                continue;
+            }
+            
+            // get the left and right portions
+            $left = trim(substr($line, 0, $pos));
+            $right = trim(substr($line, $pos+1, strlen($line)));
+            
+            // have we started yet?
+            if (! $begin) {
+                
+                // nope.  does this line indicate the beginning of
+                // a new vCard?
+                if (strtoupper($left) == 'BEGIN' &&
+                    strtoupper($right) == 'VCARD') {
+                    
+                    // tell the loop that we've begun a new card
+                    $begin = true;
+                }
+                
+                // regardless, loop to the next line of source. if begin
+                // is still false, the next loop will check the line. if
+                // begin has now been set to true, the loop will start
+                // collecting card info.
+                continue;
+                
+            } else {
+                
+                // yep, we've started, but we don't know how far along
+                // we are in the card. is this the ending line of the
+                // current vCard?
+                if (strtoupper($left) == 'END' &&
+                    strtoupper($right) == 'VCARD') {
+                    
+                    // yep, we're done. keep the info from the current
+                    // card...
+                    $info[] = $card;
+                    
+                    // ...and reset to grab a new card if one exists in
+                    // the source array.
+                    $begin = false;
+                    $card = array();
+                    
+                } else {
+                    
+                    // we're not on an ending line, so collect info from
+                    // this line into the current card. split the
+                    // left-portion of the line into a type-definition
+                    // (the kind of information) and parameters for the
+                    // type.
+                    $typedef = $this->_getTypeDef($left);
+                    $params = $this->_getParams($left);
+                    
+                    // if we are decoding quoted-printable, do so now.
+                    // QUOTED-PRINTABLE is not allowed in version 3.0,
+                    // but we don't check for versioning, so we do it
+                    // regardless.  ;-)
+                    $this->_decode_qp($params, $right);
+                    
+                    // now get the value-data from the line, based on
+                    // the typedef
+                    switch ($typedef) {
+                        
+                    case 'N':
+                        // structured name of the person
+                        $value = $this->_parseN($right);
+                        break;
+                        
+                    case 'ADR':
+                        // structured address of the person
+                        $value = $this->_parseADR($right);
+                        break;
+                        
+                    case 'NICKNAME':
+                        // nicknames
+                        $value = $this->_parseNICKNAME($right);
+                        break;
+                        
+                    case 'ORG':
+                        // organizations the person belongs to
+                        $value = $this->_parseORG($right);
+                        break;
+                        
+                    case 'CATEGORIES':
+                        // categories to which this card is assigned
+                        $value = $this->_parseCATEGORIES($right);
+                        break;
+                        
+                    case 'GEO':
+                        // geographic coordinates
+                        $value = $this->_parseGEO($right);
+                        break;
+                        
+                    default:
+                        // by default, just grab the plain value. keep
+                        // as an array to make sure *all* values are
+                        // arrays.  for consistency. ;-)
+                        $value = array(array($right));
+                        break;
+                    }
+                    
+                    // add the type, parameters, and value to the
+                    // current card array.  note that we allow multiple
+                    // instances of the same type, which might be dumb
+                    // in some cases (e.g., N).
+                    $card[$typedef][] = array(
+                        'param' => $params,
+                        'value' => $value
+                    );
+                }
+            }
+        }
+        
+        $this->unescape($info);
+        return $info;
+    }
+    
+    
+    /**
+    *
+    * Takes a vCard line and extracts the Type-Definition for the line.
+    * 
+    * @access private
+    *
+    * @param string $text A left-part (before-the-colon part) from a
+    * vCard line.
+    * 
+    * @return string The type definition for the line.
+    * 
+    */
+    
+    function _getTypeDef($text)
+    {
+        // split the text by semicolons
+        $split = $this->splitBySemi($text);
+        
+        // only return first element (the typedef)
+        return strtoupper($split[0]);
+    }
+    
+    
+    /**
+    *
+    * Finds the Type-Definition parameters for a vCard line.
+    * 
+    * @access private
+    * 
+    * @param string $text A left-part (before-the-colon part) from a
+    * vCard line.
+    * 
+    * @return mixed An array of parameters.
+    * 
+    */
+    
+    function _getParams($text)
+    {
+        // split the text by semicolons into an array
+        $split = $this->splitBySemi($text);
+        
+        // drop the first element of the array (the type-definition)
+        array_shift($split);
+        
+        // set up an array to retain the parameters, if any
+        $params = array();
+        
+        // loop through each parameter.  the params may be in the format...
+        // "TYPE=type1,type2,type3"
+        //    ...or...
+        // "TYPE=type1;TYPE=type2;TYPE=type3"
+        foreach ($split as $full) {
+            
+            // split the full parameter at the equal sign so we can tell
+            // the parameter name from the parameter value
+            $tmp = explode("=", $full);
+            
+            // the key is the left portion of the parameter (before
+            // '='). if in 2.1 format, the key may in fact be the
+            // parameter value, not the parameter name.
+            $key = strtoupper(trim($tmp[0]));
+            
+            // get the parameter name by checking to see if it's in
+            // vCard 2.1 or 3.0 format.
+            $name = $this->_getParamName($key);
+            
+            // list of all parameter values
+            $listall = trim($tmp[1]);
+            
+            // if there is a value-list for this parameter, they are
+            // separated by commas, so split them out too.
+            $list = $this->splitByComma($listall);
+            
+            // now loop through each value in the parameter and retain
+            // it.  if the value is blank, that means it's a 2.1-style
+            // param, and the key itself is the value.
+            foreach ($list as $val) {
+                if (trim($val) != '') {
+                    // 3.0 formatted parameter
+                    $params[$name][] = trim($val);
+                } else {
+                    // 2.1 formatted parameter
+                    $params[$name][] = $key;
+                }
+            }
+            
+            // if, after all this, there are no parameter values for the
+            // parameter name, retain no info about the parameter (saves
+            // ram and checking-time later).
+            if (count($params[$name]) == 0) {
+                unset($params[$name]);
+            }
+        }
+        
+        // return the parameters array.
+        return $params;
+    }
+    
+    
+    /**
+    *
+    * Looks at the parameters of a vCard line; if one of them is
+    * ENCODING[] => QUOTED-PRINTABLE then decode the text in-place.
+    * 
+    * @access private
+    * 
+    * @param array $params A parameter array from a vCard line.
+    * 
+    * @param string $text A right-part (after-the-colon part) from a
+    * vCard line.
+    *
+    * @return void
+    * 
+    */
+    
+    function _decode_qp(&$params, &$text)
+    {
+        // loop through each parameter
+        foreach ($params as $param_key => $param_val) {
+            
+            // check to see if it's an encoding param
+            if (trim(strtoupper($param_key)) == 'ENCODING') {
+            
+                // loop through each encoding param value
+                foreach ($param_val as $enc_key => $enc_val) {
+                
+                    // if any of the values are QP, decode the text
+                    // in-place and return
+                    if (trim(strtoupper($enc_val)) == 'QUOTED-PRINTABLE') {
+                        $text = quoted_printable_decode($text);
+                        return;
+                    }
+                }
+            }
+        }
+    }
+    
+    
+    /**
+    * 
+    * Returns parameter names from 2.1-formatted vCards.
+    *
+    * The vCard 2.1 specification allows parameter values without a
+    * name. The parameter name is then determined from the unique
+    * parameter value.
+    * 
+    * Shamelessly lifted from Frank Hellwig <frank@hellwig.org> and his
+    * vCard PHP project <http://vcardphp.sourceforge.net>.
+    * 
+    * @access private
+    * 
+    * @param string $value The first element in a parameter name-value
+    * pair.
+    * 
+    * @return string The proper parameter name (TYPE, ENCODING, or
+    * VALUE).
+    * 
+    */
+     
+    function _getParamName($value)
+    {
+        static $types = array (
+            'DOM', 'INTL', 'POSTAL', 'PARCEL','HOME', 'WORK',
+            'PREF', 'VOICE', 'FAX', 'MSG', 'CELL', 'PAGER',
+            'BBS', 'MODEM', 'CAR', 'ISDN', 'VIDEO',
+            'AOL', 'APPLELINK', 'ATTMAIL', 'CIS', 'EWORLD',
+            'INTERNET', 'IBMMAIL', 'MCIMAIL',
+            'POWERSHARE', 'PRODIGY', 'TLX', 'X400',
+            'GIF', 'CGM', 'WMF', 'BMP', 'MET', 'PMB', 'DIB',
+            'PICT', 'TIFF', 'PDF', 'PS', 'JPEG', 'QTIME',
+            'MPEG', 'MPEG2', 'AVI',
+            'WAVE', 'AIFF', 'PCM',
+            'X509', 'PGP'
+        );
+        
+        // CONTENT-ID added by pmj
+        static $values = array (
+            'INLINE', 'URL', 'CID', 'CONTENT-ID'
+        );
+        
+        // 8BIT added by pmj
+        static $encodings = array (
+            '7BIT', '8BIT', 'QUOTED-PRINTABLE', 'BASE64'
+        );
+        
+        // changed by pmj to the following so that the name defaults to
+        // whatever the original value was.  Frank Hellwig's original
+        // code was "$name = 'UNKNOWN'".
+        $name = $value;
+        
+        if (in_array($value, $types)) {
+            $name = 'TYPE';
+        } elseif (in_array($value, $values)) {
+            $name = 'VALUE';
+        } elseif (in_array($value, $encodings)) {
+            $name = 'ENCODING';
+        }
+        
+        return $name;
+    }
+    
+    
+    /**
+    *
+    * Parses a vCard line value identified as being of the "N"
+    * (structured name) type-defintion.
+    *
+    * @access private
+    *
+    * @param string $text The right-part (after-the-colon part) of a
+    * vCard line.
+    * 
+    * @return array An array of key-value pairs where the key is the
+    * portion-name and the value is the portion-value.  The value itself
+    * may be an array as well if multiple comma-separated values were
+    * indicated in the vCard source.
+    *
+    */
+    
+    function _parseN($text)
+    {
+    	// make sure there are always at least 5 elements
+        $tmp = array_pad($this->splitBySemi($text), 5, '');
+        return array(
+            $this->splitByComma($tmp[0]), // family (last)
+            $this->splitByComma($tmp[1]), // given (first)
+            $this->splitByComma($tmp[2]), // addl (middle)
+            $this->splitByComma($tmp[3]), // prefix
+            $this->splitByComma($tmp[4])  // suffix
+        );
+    }
+    
+    
+    /**
+    *
+    * Parses a vCard line value identified as being of the "ADR"
+    * (structured address) type-defintion.
+    *
+    * @access private
+    *
+    * @param string $text The right-part (after-the-colon part) of a
+    * vCard line.
+    * 
+    * @return array An array of key-value pairs where the key is the
+    * portion-name and the value is the portion-value.  The value itself
+    * may be an array as well if multiple comma-separated values were
+    * indicated in the vCard source.
+    *
+    */
+    
+    function _parseADR($text)
+    {
+    	// make sure there are always at least 7 elements
+        $tmp = array_pad($this->splitBySemi($text), 7, '');
+        return array(
+            $this->splitByComma($tmp[0]), // pob
+            $this->splitByComma($tmp[1]), // extend
+            $this->splitByComma($tmp[2]), // street
+            $this->splitByComma($tmp[3]), // locality (city)
+            $this->splitByComma($tmp[4]), // region (state)
+            $this->splitByComma($tmp[5]), // postcode (ZIP)
+            $this->splitByComma($tmp[6])  // country
+        );
+    }
+    
+    
+    /**
+    * 
+    * Parses a vCard line value identified as being of the "NICKNAME"
+    * (informal or descriptive name) type-defintion.
+    *
+    * @access private
+    * 
+    * @param string $text The right-part (after-the-colon part) of a
+    * vCard line.
+    * 
+    * @return array An array of nicknames.
+    *
+    */
+    
+    function _parseNICKNAME($text)
+    {
+        return array($this->splitByComma($text));
+    }
+    
+    
+    /**
+    * 
+    * Parses a vCard line value identified as being of the "ORG"
+    * (organizational info) type-defintion.
+    *
+    * @access private
+    *
+    * @param string $text The right-part (after-the-colon part) of a
+    * vCard line.
+    * 
+    * @return array An array of organizations; each element of the array
+    * is itself an array, which indicates primary organization and
+    * sub-organizations.
+    *
+    */
+    
+    function _parseORG($text)
+    {
+        $tmp = $this->splitbySemi($text);
+        $list = array();
+        foreach ($tmp as $val) {
+            $list[] = array($val);
+        }
+        
+        return $list;
+    }
+    
+    
+    /**
+    * 
+    * Parses a vCard line value identified as being of the "CATEGORIES"
+    * (card-category) type-defintion.
+    *
+    * @access private
+    * 
+    * @param string $text The right-part (after-the-colon part) of a
+    * vCard line.
+    * 
+    * @return mixed An array of categories.
+    *
+    */
+    
+    function _parseCATEGORIES($text)
+    {
+        return array($this->splitByComma($text));
+    }
+    
+    
+    /**
+    * 
+    * Parses a vCard line value identified as being of the "GEO"
+    * (geographic coordinate) type-defintion.
+    *
+    * @access private
+    *
+    * @param string $text The right-part (after-the-colon part) of a
+    * vCard line.
+    * 
+    * @return mixed An array of lat-lon geocoords.
+    *
+    */
+    
+    function _parseGEO($text)
+    {
+    	// make sure there are always at least 2 elements
+        $tmp = array_pad($this->splitBySemi($text), 2, '');
+        return array(
+            array($tmp[0]), // lat
+            array($tmp[1])  // lon
+        );
+    }
+}
+
+?>
\ No newline at end of file
Index: program/js/app.js
===================================================================
--- program/js/app.js	(revision 360)
+++ program/js/app.js	(working copy)
@@ -118,7 +118,7 @@
           this.message_list.row_init = function(o){ p.init_message_row(o); };
           this.message_list.addEventListener('dblclick', function(o){ p.msglist_dbl_click(o); });
           this.message_list.addEventListener('keypress', function(o){ p.msglist_keypress(o); });
-          this.message_list.addEventListener('select', function(o){ p.msglist_select(o); });
+          this.message_list.addEventListener('select', function(o){ p.msglist_select(o); p.list_display_details(o) });
           this.message_list.addEventListener('dragstart', function(o){ p.drag_active = true; });
           this.message_list.addEventListener('dragend', function(o){ p.drag_active = false; });
 
@@ -194,17 +194,20 @@
       case 'addressbook':
         if (this.gui_objects.contactslist)
           {
-          this.contact_list = new rcube_list_widget(this.gui_objects.contactslist, {multiselect:true, draggable:false, keyboard:true});
+          this.contact_list = new rcube_list_widget(this.gui_objects.contactslist, {multiselect:true, draggable:true, keyboard:true});
           this.contact_list.addEventListener('keypress', function(o){ p.contactlist_keypress(o); });
-          this.contact_list.addEventListener('select', function(o){ p.contactlist_select(o); });
+          this.contact_list.addEventListener('dblclick', function(o){ p.contactlist_dbl_click(o); });
+          this.contact_list.addEventListener('select', function(o){ p.contactlist_select(o); p.list_display_details(o)});
+          this.contact_list.addEventListener('dragstart', function(o){ p.drag_active = true; });
+          this.contact_list.addEventListener('dragend', function(o){ p.drag_active = false; });
           this.contact_list.init();
 
           if (this.env.cid)
             this.contact_list.highlight_row(this.env.cid);
 
-          if (this.gui_objects.contactslist.parentNode)
+          if (this.gui_objects.contactslist)
             {
-            this.gui_objects.contactslist.parentNode.onmousedown = function(e){ return p.click_on_list(e); };
+            this.gui_objects.contactslist.onmousedown = function(e){ return p.click_on_list(e); };
             document.onmouseup = function(e){ return p.doc_mouse_up(e); };
             }
           else
@@ -219,10 +222,20 @@
         if ((this.env.action=='add' || this.env.action=='edit') && this.gui_objects.editform)
           this.enable_command('save', true);
 
-        this.enable_command('list', 'add', true);
-
-        // this.enable_command('ldappublicsearch', this.env.ldappublicsearch);
-
+        this.enable_command('list', true);
+        this.enable_command('add', true);
+        this.enable_command('search', true);
+        this.enable_command('reset-search', true);
+        this.enable_command('addgroup',true);
+        this.enable_command('moveto' ,true);
+        this.enable_command('send-vcard', true);
+        this.enable_command('cancel-vcard', true);
+        this.enable_command('vcard', true);
+        this.enable_command('ldappublicsearch', this.env.ldappublicsearch);
+        
+        if(!this.env.mbox)
+          this.select_groupbox('all');
+          
         break;
 
 
@@ -433,11 +446,10 @@
           {
           if (this.env.search_request<0 || (this.env.search_request && props != this.env.mailbox))
             this.reset_qsearch();
-
-          this.list_mailbox(props);
+            this.list_mailbox(props);
           }
         else if (this.task=='addressbook')
-          this.list_contacts();
+          this.list_contacts(props);   // added props to be passed to function
         break;
 
 
@@ -597,7 +609,12 @@
       // mail task commands
       case 'move':
       case 'moveto':
-        this.move_messages(props);
+      
+        if (this.task=='mail')
+          this.move_messages(props);
+        else if (this.task=='addressbook')
+          this.move_contacts(props);
+          
         break;
         
       case 'toggle_status':
@@ -827,16 +844,22 @@
         if (!props && this.gui_objects.qsearchbox)
           props = this.gui_objects.qsearchbox.value;
         if (props)
-          this.qsearch(urlencode(props), this.env.mailbox);
+        {
+          if(this.task=='mail')
+            this.qsearch(urlencode(props), this.env.mailbox);
+          else if(this.task=='addressbook')
+            this.qsearch(urlencode(props), this.env.mbox);
+        }
+          
         break;
 
       // reset quicksearch        
       case 'reset-search':
         var s = this.env.search_request;
         this.reset_qsearch();
-        
         if (s)
           this.list_mailbox(this.env.mailbox);
+        
         break;
 
       // ldap search
@@ -846,8 +869,28 @@
         else 
           this.ldappublicsearch(command);
         break; 
+      
+      case 'vcard':
+          this.vcard_form(command);
+        break;
+      
+      case 'send-vcard':
+        this.upload_vcard(props);
+        break;
+        
+      case 'cancel-vcard':
+        // Reset the display
+        history.back(); 
+        break;
+        
+      case 'addgroup':
+        this.show_group_form(true);
+        break;
+         
+      case 'removegroup':
+         this.remove_group(this.env.mbox);
+         break;
 
-
       // user settings commands
       case 'preferences':
         this.goto_url('');
@@ -1016,7 +1059,7 @@
     if (this.message_list)
       this.message_list.focus();
     else if (this.contact_list)
-        this.contact_list.focus();
+      this.contact_list.focus();
 
     var mbox_li;
     if (mbox_li = this.get_mailbox_li())
@@ -1061,7 +1104,6 @@
     };
 
 
-
   /*********************************************************/
   /*********     (message) list functionality      *********/
   /*********************************************************/
@@ -1136,7 +1178,7 @@
     // also send search request to get the right messages
     if (this.env.search_request)
       add_url += '&_search='+this.env.search_request;
-      
+    
     this.select_mailbox(mbox);
 
     // load message list remotely
@@ -1233,7 +1275,10 @@
   this.move_messages = function(mbox)
     {
     // exit if no mailbox specified or if selection is empty
-    var selection = this.message_list.get_selection();
+    var selection = 0; 
+ 		if (this.message_list != null)  
+ 	      selection = this.message_list.get_selection(); 
+ 	    
     if (!mbox || !(selection.length || this.env.uid) || mbox==this.env.mailbox)
       return;
     
@@ -1270,7 +1315,11 @@
   this.permanently_remove_messages = function()
     {
     // exit if no mailbox specified or if selection is empty
-    var selection = this.message_list.get_selection();
+      var selection = 0; 
+	    if (this.message_list != null)  
+	    {  
+	      selection = this.message_list.get_selection(); 
+	    } 
     if (!(selection.length || this.env.uid))
       return;
     
@@ -1300,7 +1349,10 @@
   this.delete_messages = function()
     {
     // exit if no mailbox specified or if selection is empty
-    var selection = this.message_list.get_selection();
+    var selection = 0; 
+	  if (this.message_list != null)  
+	    selection = this.message_list.get_selection(); 
+	    
     if (!(selection.length || this.env.uid))
       return;
 
@@ -1803,9 +1855,18 @@
     {
     if (value && mbox)
       {
-      this.message_list.clear();
-      this.set_busy(true, 'searching');
-      this.http_request('search', '_search='+value+'&_mbox='+mbox, true);
+        if(this.task=='mail')
+        {
+          this.message_list.clear();
+          this.set_busy(true, 'searching');
+          this.http_request('search', '_search='+value+'&_mbox='+mbox, true);
+        }
+        else if(this.task=='addressbook')
+        {
+          this.contact_list.clear();
+          this.set_busy(true, 'searching');
+          this.http_request('search', '_search='+value+'&_mbox='+mbox, true);
+        }
       }
     return true;
     };
@@ -2056,7 +2117,42 @@
   /*********************************************************/
   /*********         address book methods          *********/
   /*********************************************************/
+  // move selected messages to the specified mailbox
+  this.move_contacts = function(mbox)
+    {
+    // if trying to move to all mbox - stop
+    if(mbox == 'all')
+      return;
+    // exit if no mailbox specified or if selection is empty
+    var selection = 0; 
+ 		if (this.contact_list != null)  
+ 	    {  
+ 	      selection = this.contact_list.get_selection(); 
+ 	    } 
+    if (!mbox || !(selection.length || this.env.cid) || mbox==this.env.mbox)
+      return;
+    
+    var a_cids = new Array();
+    
+    
+    if (this.env.cid)
+      a_cids[a_uids.length] = this.env.cid;
+    else
+      {
+        var id;
+        for (var n=0; n<selection.length; n++)
+          {
+          id = selection[n];
+          a_cids[a_cids.length] = id;
+          }
+      }
+      // show wait message
+      lock = true;
+      this.set_busy(true, 'movingmessage');
 
+    // send request to server
+    this.http_request('moveto', '_cid='+a_cids.join(',')+'&_target_mbox='+urlencode(mbox) , true);
+    };
 
   this.contactlist_keypress = function(list)
     {
@@ -2067,33 +2163,61 @@
 
   this.contactlist_select = function(list)
     {
-      var id, frame;
-      if (id = list.get_single_selection())
-        this.load_contact(id, 'show');
-      else if (frame = document.getElementById(this.env.contentframe))
-        frame.style.visibility = 'hidden';
-
-      this.enable_command('edit', id?true:false);
-
-      if (list.selection.length)
-        this.enable_command('delete', 'compose', true);
-
-      return false; 
+      if(!this.drag_active)
+      {
+          var id, frame;
+          //if (id = list.get_single_selection())
+          //  this.load_contact(id, 'show');
+            
+          //else if (frame = document.getElementById(this.env.contentframe))
+          //  frame.style.visibility = 'hidden';
+          
+          this.enable_command('edit', id?true:false);
+    
+          if (list.selection.length)
+            this.enable_command('delete', 'compose', true);
+    
+          return false; 
+        }
     };
-
-
-  this.list_contacts = function(page)
+    
+    this.contactlist_dbl_click = function(list)
     {
+          var id, frame;
+          if (id = list.get_single_selection())
+            this.load_contact(id, 'show');
+            
+          else if (frame = document.getElementById(this.env.contentframe))
+            frame.style.visibility = 'hidden';
+          
+          this.enable_command('edit', id?true:false);
+    
+          if (list.selection.length)
+            this.enable_command('delete', 'compose', true);
+    
+          return false; 
+    };
+    
+    
+  this.list_contacts = function(mbox, page, sort)
+    {
     var add_url = '';
     var target = window;
+     // clear message list first
+    this.contact_list.clear();
     
     if (page && this.current_page==page)
       return false;
-
+    
+    if(mbox != 'all')
+      this.enable_command('delete', false);
+    
+    this.select_groupbox(mbox);
+    
     // load contacts remotely
     if (this.gui_objects.contactslist)
       {
-      this.list_contacts_remote(page);
+      this.list_contacts_remote(mbox);
       return;
       }
 
@@ -2102,20 +2226,25 @@
       target = window.frames[this.env.contentframe];
       add_url = '&_framed=1';
       }
-
-    this.set_busy(true, 'loading');
-    target.location.href = this.env.comm_path+(page ? '&_page='+page : '')+add_url;
+      
+      this.set_busy(true, 'loading');
+      target.location.href = this.env.comm_path+'&_mbox='+urlencode(mbox)+(page ? '&_page='+page : '')+add_url;
     };
 
 
   // send remote request to load contacts list
-  this.list_contacts_remote = function(page)
+  this.list_contacts_remote = function(mbox, page)
     {
     // clear message list first
     this.contact_list.clear();
-
+      
     // send request to server
-    var url = page ? '&_page='+page : '';
+    if(page)
+      var url = page ? '&_page='+page : '';
+    
+    if(mbox)
+      var url = mbox ? '&_mbox='+mbox : '';
+    
     this.set_busy(true, 'loading');
     this.http_request('list', url, true);
     };
@@ -2148,9 +2277,40 @@
     {
     // exit if no mailbox specified or if selection is empty
     var selection = this.contact_list.get_selection();
-    if (!(selection.length || this.env.cid) || !confirm(this.get_label('deletecontactconfirm')))
-      return;
+      if(this.env.mbox != 'all')
+      {
+        if (!(selection.length || this.env.cid) || !confirm(this.get_label('deletecontactgroupconfirm')))
+        return;
+      }
+      else
+      {
+        if (!(selection.length || this.env.cid) || !confirm(this.get_label('deletecontactconfirm')))
+        return;
+      }
+    
+    var a_cids = new Array();
+    
+    if (this.env.cid)
+      a_cids[a_cids.length] = this.env.cid;
+    else
+      {
+      var id;
+      for (var n=0; n<selection.length; n++)
+        {
+        id = selection[n];
+        a_cids[a_cids.length] = id;
+        }
+      }
       
+    this.set_busy(true, 'loading');
+    // send request to server
+    this.http_request('delete', '_cid='+a_cids.join(',')+'&_mbox='+this.env.mbox+'&_from='+(this.env.action ? this.env.action : ''));
+    return true;
+    };
+
+  this.remove_contact_row = function(){
+  
+    var selection = this.contact_list.get_selection();
     var a_cids = new Array();
 
     if (this.env.cid)
@@ -2172,13 +2332,35 @@
         elm.style.visibility = 'hidden';
         }
       }
+  }
+    // add row to contacts list
+  this.add_contact_row = function(cid, cols)
+    {
+    if (!this.gui_objects.contactslist || !this.gui_objects.contactslist.tBodies[0])
+      return false;
+  
+    var tbody = this.gui_objects.contactslist.tBodies[0];
+    var rowcount = tbody.rows.length;
+    var even = rowcount%2;
+    
+    var row = document.createElement('TR');
+    row.id = 'rcmrow'+cid;
+    row.className = 'contact '+(even ? 'even' : 'odd');
+    
+    if (this.contact_list.in_selection(cid))
+      row.className += ' selected';
 
-    // send request to server
-    this.http_request('delete', '_cid='+a_cids.join(',')+'&_from='+(this.env.action ? this.env.action : ''));
-    return true;
+    // add each submitted col
+    for (var c in cols)
+      {
+      col = document.createElement('TD');
+      col.className = String(c).toLowerCase();
+      col.innerHTML = cols[c];
+      row.appendChild(col);
+      }
+    
+    this.contact_list.insert_row(row);
     };
-
-
   // update a contact record in the list
   this.update_contact_row = function(cid, cols_arr)
     {
@@ -2242,6 +2424,212 @@
 
 
   /*********************************************************/
+  /*********          vcard methods                *********/
+  /*********************************************************/
+  
+  // load vcard upload form
+  this.vcard_form = function(action)
+    {
+    var add_url = '';
+    var target = window;
+    if (this.env.contentframe && window.frames && window.frames[this.env.contentframe])
+      {
+      add_url = '&_framed=1';
+      target = window.frames[this.env.contentframe];
+      document.getElementById(this.env.contentframe).style.visibility = 'inherit';
+      }
+    else
+      return false;
+
+    if (action == 'vcard'){
+      target.location.href = this.env.comm_path+'&_action='+action+add_url;
+      }
+    return true;
+    };
+  
+   // upload attachment file
+  this.upload_vcard = function(form)
+    {    
+    for (var n=0; n<form.elements.length; n++)
+        {
+        
+          if(form.elements[n].name == '_action' )
+            $action = form.elements[n].value;
+        
+          if(form.elements[n].type=='file')
+            $file = form.elements[n].value;
+        
+        }
+        
+      if($file)
+      {
+
+          var add_url = '';
+          var target = window;
+          var frame_name = 'contact-frame';
+    
+          form.target = frame_name;
+          form.action = this.env.comm_path+'&_action=vcard'+add_url;
+          form.submit();
+      }else{
+        this.display_message('No file selected!', 'error');
+      }
+    };
+    
+    
+    
+    /*********************************************************/
+  /*********        groupbox methods               *********/
+  /*********************************************************/
+    this.show_group_form = function(act)
+    {
+    
+    if (!this.gui_objects.addgroupform)
+      return false;
+    
+    var elm, list;
+    if (elm = this.gui_objects.addgroupform)
+      {
+        elm.style.visibility = act ? 'visible' : 'hidden';
+      }
+      
+    // clear upload form
+    if (!act && this.gui_objects.addgroupform)
+      this.gui_objects.addgroupform.reset();
+    
+    return true;  
+    
+    };
+    
+  this.remove_group = function(mbox){
+      if (confirm(this.get_label('deletegroupfolderconfirm')))
+      {
+        if(mbox && mbox != 'all' && mbox != 'last_imported'){
+        
+          this.set_busy(true, 'loading');
+          url = '_folder='+urlencode(mbox)+'&_do=remove';
+          this.http_request('groupfolder',url,true);
+        
+        }else{
+          alert('can\'t delete this group box');
+        }
+      }
+  }
+  
+  this.addgroupfolder = function(form){
+    if (elm = this.gui_objects.addgroupform)
+        elm.style.visibility = 'hidden';
+    
+    for (var n=0; n<form.elements.length; n++)
+        {
+          if(form.elements[n].name == 'addgroup' )
+            added_group = form.elements[n].value;
+        }
+        
+    if(added_group)
+    {
+      this.set_busy(true, 'loading'); 
+      url = '_folder='+urlencode(added_group)+'&_do=add';
+      this.http_request('groupfolder',url,true);
+    }
+    else
+      this.display_message('No Input!', 'error');
+  }
+  
+  this.altergrouplist = function(folder,task ,list){
+    if(task == 'add')
+      this.add_group_rows(list);
+    else if(task == 'remove')
+    {
+      var list = this.gui_objects.groupboxlist.getElementsByTagName("li");
+      for (i=0;i<list.length;i++)
+        if (list[i].id == folder.toLowerCase())
+          this.gui_objects.groupboxlist.removeChild(list[i]);
+        
+      this.select_groupbox('all');
+      this.list_contacts(this.env.mbox);
+    }
+  };
+  
+  
+  this.add_group_rows = function(list){
+    var grouplist = this.gui_objects.groupboxlist.getElementsByTagName("li");
+    // remove all previous nodes
+    // for (i=0;i<grouplist.length;i++)
+    //      this.gui_objects.groupboxlist.removeChild(grouplist[i]);
+    this.gui_objects.groupboxlist.innerHTML = unescape(list);
+  }
+  // onmouseup handler for mailboxlist item
+  this.groupbox_mouse_up = function(mbox)
+    {
+    if (this.drag_active)
+      {
+      this.unfocus_groupbox(mbox);
+      this.command('moveto', mbox);
+      }
+    else
+      {
+      this.command('list', mbox);
+      }
+    return false;
+    };
+    
+    
+  this.get_groupbox_li = function(mbox)
+    {
+    if (this.gui_objects.groupboxlist)
+      {
+      mbox = String((mbox ? mbox : this.env.mbox)).toLowerCase().replace(this.mbox_expression, '');
+      return document.getElementById('rcmbx'+mbox);
+      }
+    
+    return null;
+    };
+    
+  this.focus_groupbox = function(mbox)
+    {
+    var mbox_li;
+    if (this.drag_active && mbox != this.env.mbox && (mbox_li = this.get_groupbox_li(mbox)))
+      this.set_classname(mbox_li, 'droptarget', true);
+    }
+    
+  this.unfocus_groupbox = function(mbox)
+    {
+    var mbox_li;
+    if (this.drag_active && (mbox_li = this.get_groupbox_li(mbox)))
+      this.set_classname(mbox_li, 'droptarget', false);
+    }
+  // mark a mailbox as selected and set environment variable
+  this.select_groupbox = function(mbox)
+    {
+    if (this.gui_objects.groupboxlist )
+      {      
+      var item, reg, text_obj;      
+      var current_li = this.get_groupbox_li();
+      var mbox_li = this.get_groupbox_li(mbox);
+
+      if (current_li)
+        {
+        this.set_classname(current_li, 'selected', false);
+        this.set_classname(current_li, 'unfocused', false);
+        }
+
+      if (mbox_li || this.env.mbox == mbox)
+        {
+        this.set_classname(mbox_li, 'unfocused', false);
+        this.set_classname(mbox_li, 'selected', true);
+        }
+      }
+      
+    this.env.mbox = mbox;
+
+    if(mbox != 'all' && mbox != 'last_imported')
+      this.enable_command('removegroup', 'editgroup', true);
+    else
+      this.enable_command('removegroup', 'editgroup', false);
+    };
+    
+  /*********************************************************/
   /*********        user settings methods          *********/
   /*********************************************************/
 
@@ -2621,7 +3009,35 @@
   /*********           GUI functionality           *********/
   /*********************************************************/
 
-
+  this.list_display_details = function(list)
+    {
+        // get subjects of selectedd messages
+        var names = '';
+        var c, subject, obj;
+        for(var n=0; n<list.selection.length; n++)
+        {
+            if (n>12)  // only show 12 lines
+            {
+              names += '...';
+              break;
+            }
+      
+            if (list.rows[list.selection[n]].obj)
+            {
+              obj = list.rows[list.selection[n]].obj;
+              subject = '';
+      
+              for(c=0; c<obj.childNodes.length; c++)
+                if (!subject && obj.childNodes[c].nodeName=='TD' && obj.childNodes[c].firstChild && obj.childNodes[c].firstChild.nodeType==3)
+                {
+                  subject = obj.childNodes[c].firstChild.data;
+                  names += (subject.length > 50 ? subject.substring(0, 50)+'...' : subject) + '<br />';
+                }
+            }
+          }
+          list.draglayer_display = names; 
+      }
+      
   // eable/disable buttons for page shifting
   this.set_page_buttons = function()
     {
@@ -2830,8 +3246,8 @@
     
     this.env.mailbox = mbox;
     };
+  
 
-
   // for reordering column array, Konqueror workaround
   this.set_message_coltypes = function(coltypes) 
   { 
@@ -2973,37 +3389,6 @@
       }
     };
 
-
-  // add row to contacts list
-  this.add_contact_row = function(cid, cols, select)
-    {
-    if (!this.gui_objects.contactslist || !this.gui_objects.contactslist.tBodies[0])
-      return false;
-    
-    var tbody = this.gui_objects.contactslist.tBodies[0];
-    var rowcount = tbody.rows.length;
-    var even = rowcount%2;
-    
-    var row = document.createElement('TR');
-    row.id = 'rcmrow'+cid;
-    row.className = 'contact '+(even ? 'even' : 'odd');
-    
-    if (this.contact_list.in_selection(cid))
-      row.className += ' selected';
-
-    // add each submitted col
-    for (var c in cols)
-      {
-      col = document.createElement('TD');
-      col.className = String(c).toLowerCase();
-      col.innerHTML = cols[c];
-      row.appendChild(col);
-      }
-    
-    this.contact_list.insert_row(row);
-    };
-
-
   this.toggle_editor = function(checkbox, textElementName)
     {
     var ischecked = checkbox.checked;
@@ -3071,7 +3456,7 @@
 
       if (lock)
         this.set_busy(true);
-
+      
       request_obj.__lock = lock ? true : false;
       request_obj.__action = action;
       request_obj.onerror = function(o){ rcube_webmail_client.http_error(o); };
@@ -3238,147 +3623,6 @@
   }  // end object rcube_webmail
 
 
-
-/**
- * Class for sending HTTP requests
- * @constructor
- */
-function rcube_http_request()
-  {
-  this.url = '';
-  this.busy = false;
-  this.xmlhttp = null;
-
-
-  // reset object properties
-  this.reset = function()
-    {
-    // set unassigned event handlers
-    this.onloading = function(){ };
-    this.onloaded = function(){ };
-    this.oninteractive = function(){ };
-    this.oncomplete = function(){ };
-    this.onabort = function(){ };
-    this.onerror = function(){ };
-    
-    this.url = '';
-    this.busy = false;
-    this.xmlhttp = null;
-    }
-
-
-  // create HTMLHTTP object
-  this.build = function()
-    {
-    if (window.XMLHttpRequest)
-      this.xmlhttp = new XMLHttpRequest();
-    else if (window.ActiveXObject)
-      this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
-    else
-      {
-      
-      }
-    }
-
-  // send GET request
-  this.GET = function(url)
-    {
-    this.build();
-
-    if (!this.xmlhttp)
-      {
-      this.onerror(this);
-      return false;
-      }
-
-    var ref = this;
-    this.url = url;
-    this.busy = true;
-
-    this.xmlhttp.onreadystatechange = function(){ ref.xmlhttp_onreadystatechange(); };
-    this.xmlhttp.open('GET', url);
-    this.xmlhttp.send(null);
-    };
-
-
-  this.POST = function(url, body, contentType)
-    {
-    // default value for contentType if not provided
-    contentType = typeof(contentType) != 'undefined' ?
-      contentType : 'application/x-www-form-urlencoded';
-
-    this.build();
-    
-    if (!this.xmlhttp)
-    {
-       this.onerror(this);
-       return false;
-    }
-
-    var ref=this;
-    this.url = url;
-    this.busy = true;
-    
-    this.xmlhttp.onreadystatechange = function() { ref.xmlhttp_onreadystatechange(); };
-    this.xmlhttp.open('POST', url, true);
-    this.xmlhttp.setRequestHeader('Content-Type', contentType);
-    this.xmlhttp.send(body);
-    };
-
-
-  // handle onreadystatechange event
-  this.xmlhttp_onreadystatechange = function()
-    {
-    if(this.xmlhttp.readyState == 1)
-      this.onloading(this);
-
-    else if(this.xmlhttp.readyState == 2)
-      this.onloaded(this);
-
-    else if(this.xmlhttp.readyState == 3)
-      this.oninteractive(this);
-
-    else if(this.xmlhttp.readyState == 4)
-      {
-      try {
-        if (this.xmlhttp.status == 0)
-          this.onabort(this);
-        else if(this.xmlhttp.status == 200)
-          this.oncomplete(this);
-        else
-          this.onerror(this);
-
-        this.busy = false;
-        }
-      catch(err)
-        {
-        this.onerror(this);
-        this.busy = false;
-        }
-      }
-    }
-
-  // getter method for HTTP headers
-  this.get_header = function(name)
-    {
-    return this.xmlhttp.getResponseHeader(name);
-    };
-
-  this.get_text = function()
-    {
-    return this.xmlhttp.responseText;
-    };
-
-  this.get_xml = function()
-    {
-    return this.xmlhttp.responseXML;
-    };
-
-  this.reset();
-  
-  }  // end class rcube_http_request
-
-
 // helper function to call the init method with a delay
 function call_init(o)
   {
Index: program/js/list.js
===================================================================
--- program/js/list.js	(revision 360)
+++ program/js/list.js	(working copy)
@@ -32,10 +32,13 @@
   this.rows = [];
   this.selection = [];
   
+  this.draglayer_display = "";
+   
   this.multiselect = false;
   this.draggable = false;
   this.keyboard = false;
   
+ 
   this.dont_select = false;
   this.drag_active = false;
   this.last_selected = 0;
@@ -117,6 +120,7 @@
  */
 clear: function()
 {
+  this.selection = new Array();
   var tbody = document.createElement('TBODY');
   this.list.insertBefore(tbody, this.list.tBodies[0]);
   this.list.removeChild(this.list.tBodies[1]);
@@ -442,8 +446,6 @@
 
   this.selection = new Array();    
 },
-
-
 /**
  * Getter for the selection array
  */
@@ -575,33 +577,8 @@
   
     if (!this.draglayer)
       this.draglayer = new rcube_layer('rcmdraglayer', {x:0, y:0, width:300, vis:0, zindex:2000});
-  
-    // get subjects of selectedd messages
-    var names = '';
-    var c, subject, obj;
-    for(var n=0; n<this.selection.length; n++)
-    {
-      if (n>12)  // only show 12 lines
-      {
-        names += '...';
-        break;
-      }
-
-      if (this.rows[this.selection[n]].obj)
-      {
-        obj = this.rows[this.selection[n]].obj;
-        subject = '';
-
-        for(c=0; c<obj.childNodes.length; c++)
-          if (!subject && obj.childNodes[c].nodeName=='TD' && obj.childNodes[c].firstChild && obj.childNodes[c].firstChild.nodeType==3)
-          {
-            subject = obj.childNodes[c].firstChild.data;
-            names += (subject.length > 50 ? subject.substring(0, 50)+'...' : subject) + '<br />';
-          }
-      }
-    }
-
-    this.draglayer.write(names);
+    
+    this.draglayer.write(this.draglayer_display);
     this.draglayer.show(1);
 
     this.drag_active = true;
@@ -626,7 +603,9 @@
 drag_mouse_up: function(e)
 {
   document.onmousemove = null;
-
+  
+  this.draglayer_display = "";
+  
   if (this.draglayer && this.draglayer.visible)
     this.draglayer.show(0);
 
Index: program/js/XMLHttpRequest.js
===================================================================
--- program/js/XMLHttpRequest.js	(revision 0)
+++ program/js/XMLHttpRequest.js	(revision 0)
@@ -0,0 +1,139 @@
+
+/**
+ * Class for sending HTTP requests
+ * @constructor
+ */
+function rcube_http_request()
+  {
+  this.url = '';
+  this.busy = false;
+  this.xmlhttp = null;
+
+
+  // reset object properties
+  this.reset = function()
+    {
+    // set unassigned event handlers
+    this.onloading = function(){ };
+    this.onloaded = function(){ };
+    this.oninteractive = function(){ };
+    this.oncomplete = function(){ };
+    this.onabort = function(){ };
+    this.onerror = function(){ };
+    
+    this.url = '';
+    this.busy = false;
+    this.xmlhttp = null;
+    }
+
+
+  // create HTMLHTTP object
+  this.build = function()
+    {
+    if (window.XMLHttpRequest)
+      this.xmlhttp = new XMLHttpRequest();
+    else if (window.ActiveXObject)
+      this.xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
+    else
+      {
+      
+      }
+    }
+
+  // send GET request
+  this.GET = function(url)
+    {
+    this.build();
+
+    if (!this.xmlhttp)
+      {
+      this.onerror(this);
+      return false;
+      }
+
+    var ref = this;
+    this.url = url;
+    this.busy = true;
+
+    this.xmlhttp.onreadystatechange = function(){ ref.xmlhttp_onreadystatechange(); };
+    this.xmlhttp.open('GET', url);
+    this.xmlhttp.send(null);
+    };
+
+
+  this.POST = function(url, body, contentType)
+    {
+    // default value for contentType if not provided
+    contentType = typeof(contentType) != 'undefined' ?
+      contentType : 'application/x-www-form-urlencoded';
+
+    this.build();
+    
+    if (!this.xmlhttp)
+    {
+       this.onerror(this);
+       return false;
+    }
+
+    var ref=this;
+    this.url = url;
+    this.busy = true;
+    
+    this.xmlhttp.onreadystatechange = function() { ref.xmlhttp_onreadystatechange(); };
+    this.xmlhttp.open('POST', url, true);
+    this.xmlhttp.setRequestHeader('Content-Type', contentType);
+    this.xmlhttp.send(body);
+    };
+
+
+  // handle onreadystatechange event
+  this.xmlhttp_onreadystatechange = function()
+    {
+    if(this.xmlhttp.readyState == 1)
+      this.onloading(this);
+
+    else if(this.xmlhttp.readyState == 2)
+      this.onloaded(this);
+
+    else if(this.xmlhttp.readyState == 3)
+      this.oninteractive(this);
+
+    else if(this.xmlhttp.readyState == 4)
+      {
+      try {
+        if (this.xmlhttp.status == 0)
+          this.onabort(this);
+        else if(this.xmlhttp.status == 200)
+          this.oncomplete(this);
+        else
+          this.onerror(this);
+
+        this.busy = false;
+        }
+      catch(err)
+        {
+        this.onerror(this);
+        this.busy = false;
+        }
+      }
+    }
+
+  // getter method for HTTP headers
+  this.get_header = function(name)
+    {
+    return this.xmlhttp.getResponseHeader(name);
+    };
+
+  this.get_text = function()
+    {
+    return this.xmlhttp.responseText;
+    };
+
+  this.get_xml = function()
+    {
+    return this.xmlhttp.responseXML;
+    };
+
+  this.reset();
+  
+  }  // end class rcube_http_request
Index: program/localization/en_GB/labels.inc
===================================================================
--- program/localization/en_GB/labels.inc	(revision 360)
+++ program/localization/en_GB/labels.inc	(working copy)
@@ -139,9 +139,47 @@
 // address boook
 $labels['name']      = 'Display name';
 $labels['firstname'] = 'First name';
-$labels['surname']   = 'Last name';
+$labels['lastname']   = 'Last name';
 $labels['email']     = 'E-Mail';
 
+$labels['middlename']       = 'Middle name'; 
+$labels['maidenname']       = 'Maiden name'; 
+$labels['prefix']           = 'Prefix'; 
+$labels['suffix']           = 'Suffix'; 
+$labels['gender']           = 'Gender'; 
+$labels['phone_home']       = 'Phone home'; 
+$labels['phone_pager']      = 'Phone pager'; 
+$labels['phone_mobile']     = 'Phone mobile'; 
+$labels['phone_work']       = 'Phone work'; 
+$labels['phone_fax']        = 'Phone fax'; 
+$labels['phone_other']      = 'Phone other'; 
+$labels['company_name']     = 'Company name';
+$labels['company_title']    = 'Company title'; 
+$labels['company_street']   = 'Company street'; 
+$labels['company_city']     = 'Company city';
+$labels['company_state']    = 'Company state'; 
+$labels['company_zip']      = 'Company zip'; 
+$labels['company_country']  = 'Company country'; 
+$labels['company_website']  = 'Company website'; 
+$labels['personal_street']  = 'personal street'; 
+$labels['personal_city']    = 'personal city'; 
+$labels['personal_state']   = 'personal state'; 
+$labels['personal_zip']     = 'personal zip'; 
+$labels['personal_country'] = 'personal country'; 
+$labels['personal_website'] = 'Personal website'; 
+$labels['str_birthday']     = 'Birthday'; 
+$labels['str_deathday']     = 'Deathday'; 
+$labels['str_anniversary']  = 'Anniversary'; 
+$labels['email1']           = 'Email 1'; 
+$labels['email2']           = 'Email 2'; 
+$labels['comments']         = 'Comments'; 
+$labels['image']            = 'Image';
+
+$labels['duplicates title'] = 'Duplicate entries';
+$labels['addgroup']     = 'Add group';
+$labels['removegroup']  = 'Remove group';
+$labels['editgroup']    = 'Edit group';
+
 $labels['addcontact'] = 'Add new contact';
 $labels['editcontact'] = 'Edit contact';
 
@@ -157,6 +195,14 @@
 $labels['print']          = 'Print';
 $labels['export']         = 'Export';
 
+// vCard
+$labels['uploadvcard']    = 'Import a vCard';
+
+// groupbox names
+$labels['grouplist']  = 'Group';
+$labels['all']  = 'All';
+$labels['last_imported'] = 'Last Imported';
+
 // LDAP search
 $labels['ldapsearch'] = 'LDAP directory search';
 
Index: program/localization/en_GB/messages.inc
===================================================================
--- program/localization/en_GB/messages.inc	(revision 360)
+++ program/localization/en_GB/messages.inc	(working copy)
@@ -102,5 +102,15 @@
 
 $messages['folderdeleted'] = 'Folder successfully deleted';
 
+$messages['vcard_instructions'] = 'Choose your vCard to upload<br/ >These will have the file suffix of .vcf';
 
+$messages['duplicate_entries'] = 'We found some entries already in your contact box these following contact shave not been added';
+
+$messages['duplicate_contact'] = 'One or more of your contacts already exists';
+
+$messages['deletecontactgroupconfirm']  = 'Do you really want to remove the selected contact(s) from this group?';
+
+$messages['deletegroupfolderconfirm']  = 'Do you really want to remove the selected group?';
+
+$messages['contactsearchsuccessful'] = '$nr contacts found';
 ?>
Index: program/localization/en_US/labels.inc
===================================================================
--- program/localization/en_US/labels.inc	(revision 360)
+++ program/localization/en_US/labels.inc	(working copy)
@@ -152,9 +152,47 @@
 // address boook
 $labels['name']      = 'Display name';
 $labels['firstname'] = 'First name';
-$labels['surname']   = 'Last name';
+$labels['lastname']   = 'Last name';
 $labels['email']     = 'E-Mail';
 
+$labels['middlename']       = 'Middle name'; 
+$labels['maidenname']       = 'Maiden name'; 
+$labels['prefix']           = 'Prefix'; 
+$labels['suffix']           = 'Suffix'; 
+$labels['gender']           = 'Gender'; 
+$labels['phone_home']       = 'Phone home'; 
+$labels['phone_pager']      = 'Phone pager'; 
+$labels['phone_mobile']     = 'Phone mobile'; 
+$labels['phone_work']       = 'Phone work'; 
+$labels['phone_fax']        = 'Phone fax'; 
+$labels['phone_other']      = 'Phone other'; 
+$labels['company_name']     = 'Company name';
+$labels['company_title']    = 'Company title'; 
+$labels['company_street']   = 'Company street'; 
+$labels['company_city']     = 'Company city';
+$labels['company_state']    = 'Company state'; 
+$labels['company_zip']      = 'Company zip'; 
+$labels['company_country']  = 'Company country'; 
+$labels['company_website']  = 'Company website'; 
+$labels['personal_street']  = 'personal street'; 
+$labels['personal_city']    = 'personal city'; 
+$labels['personal_state']   = 'personal state'; 
+$labels['personal_zip']     = 'personal zip'; 
+$labels['personal_country'] = 'personal country'; 
+$labels['personal_website'] = 'Personal website'; 
+$labels['str_birthday']     = 'Birthday'; 
+$labels['str_deathday']     = 'Deathday'; 
+$labels['str_anniversary']  = 'Anniversary'; 
+$labels['email1']           = 'Email 1'; 
+$labels['email2']           = 'Email 2'; 
+$labels['comments']         = 'Comments'; 
+$labels['image']            = 'Image';
+
+$labels['duplicates title'] = 'Duplicate entries';
+$labels['addgroup']     = 'Add group';
+$labels['removegroup']  = 'Remove group';
+$labels['editgroup']    = 'Edit group';
+
 $labels['addcontact'] = 'Add new contact';
 $labels['editcontact'] = 'Edit contact';
 
@@ -173,7 +211,14 @@
 $labels['previouspage']   = 'Show previous set';
 $labels['nextpage']       = 'Show next set';
 
+// vCard
+$labels['uploadvcard']    = 'Import a vCard';
 
+// groupbox names
+$labels['grouplist']  = 'Group';
+$labels['all']  = 'All';
+$labels['last_imported'] = 'Last Imported';
+
 // LDAP search
 $labels['ldapsearch'] = 'LDAP directory search';
 
Index: program/localization/en_US/messages.inc
===================================================================
--- program/localization/en_US/messages.inc	(revision 360)
+++ program/localization/en_US/messages.inc	(working copy)
@@ -108,4 +108,15 @@
 
 $messages['converting'] = 'Removing formatting from message...';
 
+$messages['vcard_instructions'] = 'Choose your vCard to upload<br/ >These will have the file suffix of .vcf';
+
+$messages['duplicate_entries'] = 'We found some entries already in your contact box these following contact shave not been added';
+
+$messages['duplicate_contact'] = 'One or more of your contacts already exists';
+
+$messages['deletecontactgroupconfirm']  = 'Do you really want to remove the selected contact(s) from this group?';
+
+$messages['deletegroupfolderconfirm']  = 'Do you really want to remove the selected group?';
+
+$messages['contactsearchsuccessful'] = '$nr contacts found';
 ?>
Index: program/steps/addressbook/delete.inc
===================================================================
--- program/steps/addressbook/delete.inc	(revision 360)
+++ program/steps/addressbook/delete.inc	(working copy)
@@ -21,79 +21,88 @@
 
 $REMOTE_REQUEST = TRUE;
 
-if ($_GET['_cid'])
-  {
-  $DB->query("UPDATE ".get_table_name('contacts')."
-              SET    del=1
-              WHERE  user_id=?
-              AND    contact_id IN (".$_GET['_cid'].")",
-              $_SESSION['user_id']);
-                     
-  $count = $DB->affected_rows();
-  if (!$count)
-    {
-    // send error message
-    exit;
-    }
+$commands = '';
 
+$mbox = $_GET['_mbox'];
 
-  // count contacts for this user
-  $sql_result = $DB->query("SELECT COUNT(contact_id) AS rows
-                            FROM ".get_table_name('contacts')."
-                            WHERE  del<>1
-                            AND    user_id=?",
-                            $_SESSION['user_id']);
-                                   
-  $sql_arr = $DB->fetch_assoc($sql_result);
-  $rowcount = $sql_arr['rows'];    
+switch($mbox)
+{
+  case 'all':
+      $DB->query("UPDATE ".get_table_name('contacts')."
+                    SET    del=1
+                    WHERE  user_id=?
+                    AND    contact_id IN (".$_GET['_cid'].")",
+                    $_SESSION['user_id']);
+      $sel = "del<>1";  
+      break;
+      
+  case 'last_import':
+      $DB->query("UPDATE ".get_table_name('contacts')."
+                  SET    del=1
+                  WHERE  user_id=?
+                  AND    contact_id IN (".$_GET['_cid'].")",
+                 $_SESSION['user_id']);
+      
+      $sel = "import=1 AND del<>1";      
+      break;
+  
+  default:
+      
+      
+      $sql_result = $DB->query("SELECT contact_ids FROM ".get_table_name('contactgroups')."
+                              WHERE  user_id=?
+                              AND user_groups=?",
+                              $_SESSION['user_id'],
+                              $mbox);
+                                 
+      $sql_arr = $DB->fetch_assoc($sql_result);
 
-  // update message count display
-  $pages = ceil($rowcount/$CONFIG['pagesize']);
-  $commands = sprintf("this.set_rowcount('%s');\n", rcmail_get_rowcount_text($rowcount));
-  $commands .= sprintf("this.set_env('pagecount', %d);\n", $pages);
+      $contact_arr = unserialize($sql_arr['contact_ids']);
+      $del_contact_arr = explode(',',$_GET['_cid']);
+      $n_arr = serialize(array_diff($contact_arr, $del_contact_arr));
+      
+      $commands .= sprintf("alert('%s');\n", "remote reply $tmp2 : $tmp");
+      
+      $DB->query("UPDATE ".get_table_name('contactgroups')."
+                    SET   contact_ids =?
+                    WHERE  user_groups=?
+                    AND user_id=?",
+                    $n_arr,
+                    $mbox,
+                    $_SESSION['user_id']);
+                    
+      // set search in contacts for the page count
+      $sel = "( ";
+      for($i=0; $i<count($contact_arr); $i++){
+        if($contact_arr[$i])
+        $sel .= " contact_id = $contact_arr[$i]";
+        
+        if($contact_arr[$i] && $i != (count($contact_arr)-1))
+          $sel .= " OR";
+      }
+      $sel .= ") AND del<>1";
+                    
+}
 
-
-  // add new rows from next page (if any)
-  if ($_GET['_from']!='show' && $pages>1 && $_SESSION['page'] < $pages)
-    {
-    $start_row = ($_SESSION['page'] * $CONFIG['pagesize']) - $count;
-
-    // get contacts from DB
-    $sql_result = $DB->limitquery("SELECT * FROM ".get_table_name('contacts')."
-                                   WHERE  del<>1
-                                   AND    user_id=?
-                                   ORDER BY name",
-                                   $start_row,
-                                   $count,
-                                   $_SESSION['user_id']);
+      // count contacts for this user
+    $sql_result = $DB->query("SELECT COUNT(contact_id) AS rows
+                              FROM ".get_table_name('contacts')."
+                              WHERE  ".$sel."
+                              AND    user_id=?",
+                              $_SESSION['user_id']);
                                      
-    $commands .= rcmail_js_contacts_list($sql_result);
-
-/*
-    // define list of cols to be displayed
-    $a_show_cols = array('name', 'email');
-    
-    while ($sql_arr = $DB->fetch_assoc($sql_result))
-      {
-      $a_row_cols = array();
-            
-      // format each col
-      foreach ($a_show_cols as $col)
-        {
-        $cont = rep_specialchars_output($sql_arr[$col]);
-        $a_row_cols[$col] = $cont;
-        }
+    $sql_arr = $DB->fetch_assoc($sql_result);
+    $rowcount = $sql_arr['rows'];    
   
-      $commands .= sprintf("this.add_contact_row(%s, %s);\n",
-                           $sql_arr['contact_id'],
-                           array2js($a_row_cols));
-      }
-*/
-    }
+    // update message count display
+    $pages = ceil($rowcount/$CONFIG['pagesize']);
+    $commands = sprintf("this.set_rowcount('%s');\n", rcmail_get_rowcount_text($rowcount));
+    $commands .= sprintf("this.set_env('pagecount', %d);\n", $pages);
+  
+    if(!$errors)
+      $commands .= sprintf("this.remove_contact_row();\n"); 
+      // send response
+      rcube_remote_response($commands);
 
-  // send response
-  rcube_remote_response($commands);
-  }
-
 exit;
-?>
\ No newline at end of file
+?>
Index: program/steps/addressbook/edit.inc
===================================================================
--- program/steps/addressbook/edit.inc	(revision 360)
+++ program/steps/addressbook/edit.inc	(working copy)
@@ -19,7 +19,6 @@
 
 */
 
-
 if (($_GET['_cid'] || $_POST['_cid']) && $_action=='edit')
   {
   $cid = $_POST['_cid'] ? $_POST['_cid'] : $_GET['_cid'];
@@ -64,8 +63,46 @@
 
   // return the complete address edit form as table
   $out = "$form_start<table>\n\n";
-
-  $a_show_cols = array('name', 'firstname', 'surname', 'email');
+  
+  $a_show_cols = array(
+    'name',
+    'email',
+    'firstname',
+    'middlename',
+    'lastname',
+    'maidenname',
+    'prefix',
+    'suffix',
+    'gender',
+    'email1',
+    'email2',
+    'phone_home',
+    'phone_pager',
+    'phone_mobile',
+    'phone_work',
+    'phone_fax',
+    'phone_other',
+    'company_name',
+    'company_title',
+    'company_street',
+    'company_city',
+    'company_state',
+    'company_zip',
+    'company_country',
+    'company_website',
+    'personal_street',
+    'personal_city',
+    'personal_state',
+    'personal_zip',
+    'personal_country',
+    'personal_website',
+    'str_birthday',
+    'str_deathday',
+    'str_anniversary',
+    'comments',
+    'image'
+  );
+  
   foreach ($a_show_cols as $col)
     {
     $attrib['id'] = 'rcmfd_'.$col;
@@ -123,4 +160,4 @@
 
 // this will be executed if no template for addcontact exists
 parse_template('editcontact');
-?>
\ No newline at end of file
+?>
Index: program/steps/addressbook/func.inc
===================================================================
--- program/steps/addressbook/func.inc	(revision 360)
+++ program/steps/addressbook/func.inc	(working copy)
@@ -21,6 +21,9 @@
 
 $CONTACTS_LIST = array();
 
+if (empty($_GET['_mbox'])){
+  $_GET['_mbox'] = "All";
+}
 // set list properties and session vars
 if (strlen($_GET['_page']))
   {
@@ -84,6 +87,8 @@
 
   // set client env
   $javascript = sprintf("%s.gui_object('contactslist', '%s');\n", $JS_OBJECT_NAME, $attrib['id']);
+  $javascript .= sprintf("%s.gui_object('contcontframe', '%s');\n", $JS_OBJECT_NAME, 'addresslist');
+  
   $javascript .= sprintf("%s.set_env('current_page', %d);\n", $JS_OBJECT_NAME, $CONTACTS_LIST['page']);
   $javascript .= sprintf("%s.set_env('pagecount', %d);\n", $JS_OBJECT_NAME, ceil($rowcount/$CONFIG['pagesize']));
   $javascript .= "rcmail.set_env('newcontact', '" . rcube_label('newcontact') . "');";
@@ -98,24 +103,20 @@
   return $out;
   }
 
-
-
 function rcmail_js_contacts_list($sql_result, $obj_name='this')
   {
   global $DB;
-
-  $commands = '';
   
   if (!$sql_result)
     return '';
-
-  // define list of cols to be displayed
+  
+  // define list of cols to be displayed  
   $a_show_cols = array('name', 'email');
-    
+  
   while ($sql_arr = $DB->fetch_assoc($sql_result))
     {
     $a_row_cols = array();
-            
+     
     // format each col
     foreach ($a_show_cols as $col)
       {
@@ -128,11 +129,42 @@
                          $sql_arr['contact_id'],
                          array2js($a_row_cols));
     }
-    
-  return $commands;
-  }
+    return $commands;
+}
 
 
+
+// return code for search function
+
+function rccontacts_search_form($attrib)
+  {
+  global $OUTPUT, $JS_OBJECT_NAME;
+
+  // add some labels to client
+  rcube_add_label('searching');
+
+  $attrib['name'] = '_q';
+  
+  if (empty($attrib['id']))
+    $attrib['id'] = 'rcmqsearchbox';
+  
+  $input_q = new textfield($attrib);
+  $out = $input_q->show();
+
+  $OUTPUT->add_script(sprintf("%s.gui_object('qsearchbox', '%s');",
+                              $JS_OBJECT_NAME,
+                              $attrib['id']));
+
+  // add form tag around text field
+  if (empty($attrib['form']))
+    $out = sprintf('<form name="rcmqsearchform" action="./" '.
+                   'onsubmit="%s.command(\'search\');return false" style="display:inline;">%s</form>',
+                   $JS_OBJECT_NAME,
+                   $out);
+
+  return $out;
+  } 
+  
 // similar function as /steps/settings/identities.inc::rcmail_identity_frame()
 function rcmail_contact_frame($attrib)
   {
@@ -201,4 +233,210 @@
   return $out;
   }
 
+function rcmail_js_groupbox_list($mbox_name, $maxlength, $obj_name='this')
+  {
+  global $DB;
+  
+  static $s_added_script = FALSE;
+  static $a_groupboxes;
+  
+  //if(!$mbox_name)
+  // $mbox_name = "all";
+  
+  $special_groupboxes = array('all','last_imported');
+  $a_groupboxes = array('all', 'last_imported');
+  
+  $sql_result = $DB->query("SELECT user_groups FROM ".get_table_name('contactgroups')."
+                              WHERE  user_id=?
+                              ORDER BY 'user_groups'",
+                               $_SESSION['user_id']);
+  if($sql_result)
+  {
+    $idx = 2;
+    while ($sql_arr = $DB->fetch_assoc($sql_result))
+      {
+        $a_groupboxes[$idx] = $sql_arr['user_groups'];
+        $idx++;
+      } 
+  }
+  $out = rcmail_render_folder_tree_html($a_groupboxes, $special_groupboxes, $mbox_name, $maxlength);
+  return rep_specialchars_output($out, 'url', 'all');
+}
+
+// return the groupboxlist in HTML
+function rcmail_groupbox_list($attrib)
+  {
+  
+  global $CONFIG, $OUTPUT, $JS_OBJECT_NAME, $COMM_PATH, $DB;
+  static $s_added_script = FALSE;
+  static $a_groupboxes;
+  
+  $type = $attrib['type'] ? $attrib['type'] : 'ul';
+  $add_attrib = $type=='select' ? array('style', 'class', 'id', 'name', 'onchange') :
+                                  array('style', 'class', 'id');
+                                  
+  if ($type=='ul' && !$attrib['id'])
+    $attrib['id'] = 'rcmboxlist';
+
+  // allow the following attributes to be added to the <ul> tag
+  $attrib_str = create_attrib_string($attrib, $add_attrib);
+ 
+  $out = '<' . $type . $attrib_str . ">\n";
+  
+  // add no-selection option
+  if ($type=='select' && $attrib['noselection'])
+    $out .= sprintf('<option value="0">%s</option>'."\n",
+                    rcube_label($attrib['noselection']));
+  
+  // get groupbox list
+  if(!$_GET['_mbox'])
+    $mbox_name = "all";
+  else
+    $mbox_name = strtolower($_GET['_mbox']);
+  
+  // for these mailboxes we have localized labels
+  $special_groupboxes = array('all','last_imported');
+  $a_groupboxes = array('all', 'last_imported');
+  
+  
+
+  $sql_result = $DB->query("SELECT user_groups FROM ".get_table_name('contactgroups')."
+                              WHERE  user_id=?
+                              ORDER BY 'user_groups'",
+                               $_SESSION['user_id']);
+  if($sql_result)
+  {
+    $idx = 2;
+    while ($sql_arr = $DB->fetch_assoc($sql_result))
+      {
+        $a_groupboxes[$idx] = $sql_arr['user_groups'];
+        $idx++;
+      } 
+  }
+
+  
+  // build the folders tree
+  if (empty($a_groupboxes))
+    {
+    
+    // get groupbox list
+    //$a_folders = $IMAP->list_groupboxes();
+    
+      $a_folders = array();
+      $a_groupboxes = array('All', 'Last import');
+
+      foreach ($a_folders as $folder)
+        rcmail_build_folder_tree($a_groupboxes, $folder);
+    }
+    
+  //var_dump($a_groupboxes);
+ 
+  if ($type=='select')
+    $out .= rcmail_render_folder_tree_select($a_groupboxes, $special_groupboxes, $mbox_name, $attrib['maxlength']);
+   else
+    $out .= rcmail_render_folder_tree_html($a_groupboxes, $special_groupboxes, $mbox_name, $attrib['maxlength']);
+
+  if ($type=='ul')
+    $OUTPUT->add_script(sprintf("%s.gui_object('groupboxlist', '%s');", $JS_OBJECT_NAME, $attrib['id']));
+  
+  return $out . "</$type>";
+  }
+
+// return html for a structured list <ul> for the mailbox tree
+function rcmail_render_folder_tree_html(&$arrFolders, &$special, &$mbox_name, $maxlength, $nestLevel=0)
+  {
+  global $JS_OBJECT_NAME, $COMM_PATH, $CONFIG, $OUTPUT;
+  
+  $idx = 0;
+  $out = '';
+  foreach ($arrFolders as $key => $folder)
+    {
+    $zebra_class = ($nestLevel*$idx)%2 ? 'even' : 'odd';
+    $title = '';
+    
+    $folder_lc = strtolower($folder);
+    
+    if (in_array($folder_lc, $special))
+      $foldername = rcube_label($folder_lc);
+
+    else
+      {
+      $foldername = $folder;
+      
+      // shorten the folder name to a given length
+      if ($maxlength && $maxlength>3)
+        {
+        $fname = abbrevate_string($foldername, $maxlength);
+        
+        if ($fname != $foldername)
+          $title = ' title="'.rep_specialchars_output($foldername, 'html', 'all').'"';
+          
+          $foldername = $fname;
+        }
+      }
+
+    // make folder name safe for ids and class names
+    $folder_css = $class_name = preg_replace('/[^a-z0-9\-_]/', '', $folder_lc);
+
+    $js_name = htmlspecialchars(rep_specialchars_output($folder, 'js'));
+    $out .= sprintf('<li id="rcmbx%s" class="groupbox %s %s%s"><a href="%s&amp;_mbox=%s"'.
+                    ' onclick="return %s.command(\'list\',\'%s\')"'.
+                    ' onmouseover="return %s.focus_groupbox(\'%s\')"' .            
+                    ' onmouseout="return %s.unfocus_groupbox(\'%s\')"' .
+                    ' onmouseup="return %s.groupbox_mouse_up(\'%s\')"%s>%s</a>',
+                    $folder_css,
+                    $class_name,
+                    $zebra_class,
+                    strtolower($folder) == strtolower($mbox_name) ? ' selected' : '',
+                    $COMM_PATH,
+                    urlencode($folder),
+                    $JS_OBJECT_NAME,
+                    $js_name,
+                    $JS_OBJECT_NAME,
+                    $js_name,
+                    $JS_OBJECT_NAME,
+                    $js_name,
+                    $JS_OBJECT_NAME,
+                    $js_name,
+                    $title,
+                    rep_specialchars_output($foldername, 'html', 'all'));
+
+
+    $out .= "</li>\n";
+    $idx++;
+    }
+
+  return $out;
+  }
+  
+function rcmail_add_group_form($attrib)
+  {
+  global $OUTPUT, $JS_OBJECT_NAME, $SESS_HIDDEN_FIELD;
+
+  // add ID if not given
+  if (!$attrib['id'])
+    $attrib['id'] = 'rcmadd group';
+  
+  // allow the following attributes to be added to the <div> tag
+  $attrib_str = create_attrib_string($attrib, array('id', 'class', 'style'));
+  $label_send = rcube_label('save');
+  $label_close = rcube_label('close');
+  
+  $out = <<<EOF
+<div$attrib_str>
+<form action="./" method="post" enctype="multipart/form-data">
+$SESS_HIDDEN_FIELD
+<input type="text" name="addgroup" value="" id="addgroup" size="16"/>
+<br />
+<input type="button" value="$label_close" class="button" onclick="document.getElementById('$attrib[id]').style.visibility='hidden'" />
+<input type="button" value="$label_send" class="button" onclick="$JS_OBJECT_NAME.addgroupfolder(this.form)" />
+</form>
+</div>
+EOF;
+
+  
+  $OUTPUT->add_script(sprintf("%s.gui_object('addgroupform', '%s');", $JS_OBJECT_NAME, $attrib['id']));  
+  return $out;
+  }
+
 ?>
Index: program/steps/addressbook/group.inc
===================================================================
--- program/steps/addressbook/group.inc	(revision 0)
+++ program/steps/addressbook/group.inc	(revision 0)
@@ -0,0 +1,126 @@
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/addressbook/groups.inc                                  |
+ |                                                                       |
+ | This file is part of the RoundCube Webmail client                     |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland                      |
+ | Licensed under the GNU GPL                                            |
+ |                                                                       |
+ | PURPOSE:                                                              |
+ |   Add grouping refernces to user id and contacts included             |
+ |                                                                       |
+ +-----------------------------------------------------------------------+
+ | Author: Shane Saunders <shane@gnative.com>                            |
+ +-----------------------------------------------------------------------+
+
+ $Id: move_del.inc 269 2006-06-29 23:41:40Z richs $
+
+*/
+$REMOTE_REQUEST = TRUE;
+
+    $commands = '';
+      
+    $folder = $_GET['_folder'] ? $_GET['_folder'] :'';    
+    $cfolder = $_GET['_current_folder'] ? $_GET['_current_folder'] :'';    
+    $do = $_GET['_do'] ? $_GET['_do'] :'';
+    $obj = $_GET['_obj'] ? $_GET['_obj'] :'';
+    
+    switch ($do)
+    {
+    
+      case 'add':
+          // check to the added folder already if one exists
+          $sql_result = $DB->query("SELECT user_groups FROM ".get_table_name('contactgroups')."
+                              WHERE user_id=?",
+                              $_SESSION['user_id']);
+
+          
+          while ($sql_arr = $DB->fetch_assoc($sql_result))
+            if(strtolower($sql_arr['user_groups']) == strtolower($folder))
+              $error = true;
+
+          
+          if(!$error)
+          {
+              $sql = "INSERT INTO contactgroups ( user_id , user_groups) VALUES (".$_SESSION['user_id'].",'".$folder."')";
+              $DB->query($sql) ? $error = false : $error = true;
+              $sel_mbox = $folder;
+              break;
+          }
+          else
+          {
+            // send a message
+            $commands .= sprintf("if(parent.%s)parent.%s.display_message('%s','%s');\n",
+                                             $JS_OBJECT_NAME, 
+                                             $JS_OBJECT_NAME,
+                                             'duplicategroup',
+                                             'error'); 
+            break;
+          }
+        
+          
+      case 'remove':
+          $sql = "DELETE FROM contactgroups WHERE user_id =".$_SESSION['user_id']." AND user_groups = '".$folder."'";
+          $DB->query($sql) ? $error = false : $error = true;
+          break;
+      case 'edit':
+      
+        if(!$cfolder)
+        {
+          $error = true;
+          break;
+        }
+        $sql = "UPDATE contactgroups SET user_groups = ".$folder." WHERE user_id =".$_SESSION['user_id']." AND user_groups = ".$cfolder;
+        $DB->query($sql) ? $error = false : $error = true;
+        $sel_mbox = $folder;
+        break;
+        
+      default:
+        // there was a problem
+      
+    }
+    
+    // tempory addition till i figure out how to get the oringinal value
+    // off the object build from main.php
+    $maxlength = '16'; 
+    
+    if(!$sel_box)
+      $sel_box = 'all';
+      
+    if(!$error)
+    {
+        if($do == 'remove'){
+        $commands .= sprintf("if(parent.%s)parent.%s.altergrouplist('%s','%s');\n",
+                                               $JS_OBJECT_NAME, 
+                                               $JS_OBJECT_NAME,
+                                               'rcmbx'.preg_replace('/[^a-z0-9\-_]/', '', $folder),
+                                               $do); 
+        }
+        else
+        {
+          $commands .= sprintf("if(parent.%s)parent.%s.altergrouplist('%s','%s','%s');\n",
+                                               $JS_OBJECT_NAME, 
+                                               $JS_OBJECT_NAME,
+                                               $folder,
+                                               $do,
+                                               rcmail_js_groupbox_list('', $maxlength)); 
+        
+        }
+    }
+    else
+    {
+      $commands .= sprintf("if(parent.%s)parent.%s.display_message('%s','%s');\n",
+                                             $JS_OBJECT_NAME, 
+                                             $JS_OBJECT_NAME,
+                                             'generalerror',
+                                             'error'); 
+    
+    }
+    // send response
+    rcube_remote_response($commands);
+
+    exit;
+    
+?>
Index: program/steps/addressbook/list.inc
===================================================================
--- program/steps/addressbook/list.inc	(revision 360)
+++ program/steps/addressbook/list.inc	(working copy)
@@ -16,41 +16,77 @@
  +-----------------------------------------------------------------------+
 
  $Id$
-
 */
 
 $REMOTE_REQUEST = TRUE;
+$OUTPUT_TYPE = 'js';
 
-// count contacts for this user
-$sql_result = $DB->query("SELECT COUNT(contact_id) AS rows
-                          FROM ".get_table_name('contacts')."
-                          WHERE  del<>1
-                          AND    user_id=?",
-                          $_SESSION['user_id']);
-                                   
-$sql_arr = $DB->fetch_assoc($sql_result);
-$rowcount = $sql_arr['rows'];    
+    $commands = '';
+    
+    $sort = $_GET['_mbox'];
 
-// update message count display
-$pages = ceil($rowcount/$CONFIG['pagesize']);
-$commands = sprintf("this.set_rowcount('%s');\n", rcmail_get_rowcount_text($rowcount));
-$commands .= sprintf("this.set_env('pagecount', %d);\n", $pages);
+    if($sort == 'last_imported')
+      $sel = "import = 1 AND del<>1";
+    else if($sort == 'all')
+      $sel = "del<>1";
+    else
+     {
+      $sql_result = $DB->query("SELECT contact_ids FROM ".get_table_name('contactgroups')."
+                                  WHERE  user_id=?
+                                  AND user_groups=?",
+                                  $_SESSION['user_id'],
+                                  $sort);
+                               
+      $sql_arr = $DB->fetch_assoc($sql_result);
+      $contact_arr = unserialize($sql_arr['contact_ids']);
+      
+      // set our search vaules for the DB
+      $sel = "( ";
+      for($i=0; $i<count($contact_arr); $i++){
+        if($contact_arr[$i])
+        $sel .= " contact_id = $contact_arr[$i]";
+        
+        if($contact_arr[$i] && $i != (count($contact_arr)-1))
+          $sel .= " OR";
+      }
+      $sel .= ") AND del<>1";
+      
+    }
 
-$start_row = ($CONTACTS_LIST['page']-1) * $CONFIG['pagesize'];
+  // count contacts for this user
+  $sql_result = $DB->query("SELECT COUNT(contact_id) AS rows
+                            FROM ".get_table_name('contacts')."
+                            WHERE  ".$sel."
+                            AND    user_id=?",
+                            $_SESSION['user_id']);
+                                     
+  $sql_arr = $DB->fetch_assoc($sql_result);
+  $rowcount = $sql_arr['rows'];    
 
-// get contacts from DB
-$sql_result = $DB->limitquery("SELECT * FROM ".get_table_name('contacts')."
-                               WHERE  del<>1
-                               AND    user_id=?
-                               ORDER BY name",
-                               $start_row,
-                               $CONFIG['pagesize'],
-                               $_SESSION['user_id']);
-                                 
-$commands .= rcmail_js_contacts_list($sql_result);
+
+
+  // update contact count display
+  $pages = ceil($rowcount/$CONFIG['pagesize']);
+  $commands .= sprintf("this.set_rowcount('%s');\n", rcmail_get_rowcount_text($rowcount));
+  $commands .= sprintf("this.set_env('pagecount', %d);\n", $pages);
   
+  $start_row = ($CONTACTS_LIST['page']-1) * $CONFIG['pagesize'];
+  
+  
+  
+  // get contacts from DB
+  $sql_result = $DB->query("SELECT * FROM ".get_table_name('contacts')."
+                                 WHERE  ".$sel."
+                                 AND user_id=?
+                                 ORDER BY name",
+                                 $_SESSION['user_id']);
+                                                
+  $commands .= rcmail_js_contacts_list($sql_result);
+
+
+
 // send response
 rcube_remote_response($commands);
 
 exit;
-?>
\ No newline at end of file
+?>
Index: program/steps/addressbook/move.inc
===================================================================
--- program/steps/addressbook/move.inc	(revision 0)
+++ program/steps/addressbook/move.inc	(revision 0)
@@ -0,0 +1,66 @@
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/mail/move_del.inc                                       |
+ |                                                                       |
+ | This file is part of the RoundCube Webmail client                     |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland                      |
+ | Licensed under the GNU GPL                                            |
+ |                                                                       |
+ | PURPOSE:                                                              |
+ |   Move the submitted messages to a specific mailbox or delete them    |
+ |                                                                       |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com>                        |
+ +-----------------------------------------------------------------------+
+
+ $Id: move_del.inc 269 2006-06-29 23:41:40Z richs $
+
+*/
+
+$REMOTE_REQUEST = TRUE;
+
+// move messages
+if ($_action=='moveto' && $_GET['_cid'] && $_GET['_target_mbox'])
+  { 
+        $mbox = $_GET['_target_mbox'];
+        
+        // get grouping array as it stands
+        $sql_result = $DB->query("SELECT contact_ids FROM ".get_table_name('contactgroups')."
+                                  WHERE  user_id=?
+                                  AND user_groups=?",
+                                  $_SESSION['user_id'],
+                                  $mbox);
+                               
+        $sql_arr = $DB->fetch_assoc($sql_result);
+        
+        $group_arr = unserialize($sql_arr['contact_ids']);
+        $current_arr = explode(',',$_GET['_cid']);
+        $result = array_merge($group_arr, $current_arr);
+        $result = serialize (array_unique($result));
+          
+        $DB->query("UPDATE ".get_table_name('contactgroups')."
+                    SET   contact_ids =?
+                    WHERE  user_groups=?
+                    AND user_id=?",
+                    $result,
+                    $mbox,
+                    $_SESSION['user_id']);
+                           
+         //$count = $DB->affected_rows();
+        
+  }
+  else
+  {
+    // something weird going on as we should only have those variables
+   exit;
+  }
+ 
+  //$commands = sprintf("alert('%s');\n", "remote reply : $result"); 
+  
+// send response
+rcube_remote_response($commands);
+
+exit;
+?>
Index: program/steps/addressbook/save.inc
===================================================================
--- program/steps/addressbook/save.inc	(revision 360)
+++ program/steps/addressbook/save.inc	(working copy)
@@ -28,7 +28,44 @@
   }
 
 // setup some vars we need
-$a_save_cols = array('name', 'firstname', 'surname', 'email');
+$a_save_cols = array(
+    'name',
+    'email',
+    'firstname',
+    'middlename',
+    'lastname',
+    'maidenname',
+    'prefix',
+    'suffix',
+    'gender',
+    'email1',
+    'email2',
+    'phone_home',
+    'phone_pager',
+    'phone_mobile',
+    'phone_work',
+    'phone_fax',
+    'phone_other',
+    'company_name',
+    'company_title',
+    'company_street',
+    'company_city',
+    'company_state',
+    'company_zip',
+    'company_country',
+    'company_website',
+    'personal_street',
+    'personal_city',
+    'personal_state',
+    'personal_zip',
+    'personal_country',
+    'personal_website',
+    'str_birthday',
+    'str_deathday',
+    'str_anniversary',
+    'comments',
+    'image'
+  );
 $contacts_table = get_table_name('contacts');
 
 // update an existing contact
@@ -126,8 +163,8 @@
     $sql .= ")";
     $ldap_form = true; 
     }
-  else if (isset($_POST['_email'])) 
-    $sql  .= "AND email = " . $DB->quote(get_input_value('_email', RCUBE_INPUT_POST));
+  else if (isset($_POST['_name'])) 
+    $sql  .= "AND name = " . $DB->quote(get_input_value('_name', RCUBE_INPUT_POST));
 
   $sql_result = $DB->query($sql);
 
@@ -171,8 +208,8 @@
     if (sizeof($a_insert_cols))
       {
       $DB->query("INSERT INTO $contacts_table
-                  (user_id, changed, del, ".join(', ', $a_insert_cols).")
-                  VALUES (?, ".$DB->now().", 0, ".join(', ', $a_insert_values).")",
+                  (user_id, changed, del, import, ".join(', ', $a_insert_cols).")
+                  VALUES (?, ".$DB->now().", 0, 0, ".join(', ', $a_insert_values).")",
                 $_SESSION['user_id']);
                        
       $insert_id = $DB->insert_id(get_sequence_name('contacts'));
Index: program/steps/addressbook/search.inc
===================================================================
--- program/steps/addressbook/search.inc	(revision 0)
+++ program/steps/addressbook/search.inc	(revision 0)
@@ -0,0 +1,114 @@
+<?php
+/*
+ +-----------------------------------------------------------------------+
+ | steps/addressbook/search.inc                                          |
+ |                                                                       |
+ | Search functions for rc contacts                                      |
+ | Licensed under the GNU GPL                                            |
+ |                                                                       |
+ +-----------------------------------------------------------------------+
+ | Author: Shane Saunders <shane@gnative.com>                            |
+ +-----------------------------------------------------------------------+
+
+*/
+
+$REMOTE_REQUEST = TRUE;
+
+// get search string
+$search_str = get_input_value('_search', RCUBE_INPUT_GET);
+$mbox = get_input_value('_mbox', RCUBE_INPUT_GET);
+//$search_request = md5($str);
+
+$commands = '';
+$count = 0;
+  
+// Make sure our $search is legit..
+if ($search_str != '')
+{  
+          // setup some vars we need
+      $a_cols = array(
+        'name',
+        'email',
+        'firstname',
+        'middlename',
+        'lastname',
+        'maidenname',
+        'prefix',
+        'suffix',
+        'gender',
+        'email1',
+        'email2',
+        'phone_home',
+        'phone_pager',
+        'phone_mobile',
+        'phone_work',
+        'phone_fax',
+        'phone_other',
+        'company_name',
+        'company_title',
+        'company_street',
+        'company_city',
+        'company_state',
+        'company_zip',
+        'company_country',
+        'company_website',
+        'personal_street',
+        'personal_city',
+        'personal_state',
+        'personal_zip',
+        'personal_country',
+        'personal_website',
+        'str_birthday',
+        'str_deathday',
+        'str_anniversary',
+        'comments'
+        );
+      
+      // create out string search for DB
+      $sStr = '';
+      for($i=0; $i<count($a_cols); $i++)
+      {
+          if($i == 0)
+            $sStr .= "(".$a_cols[$i]." LIKE '%$search_str%' )";
+          else
+            $sStr .= "OR (".$a_cols[$i]." LIKE '%$search_str%'  )";
+      }
+
+
+    // get contacts from DB
+    $sql_result = $DB->query("SELECT * FROM ".get_table_name('contacts')."
+                              WHERE  user_id=".$_SESSION['user_id']."
+                              AND (".$sStr.")
+                              AND del<>1
+                              ORDER BY name");
+    
+    // count contacts for this user
+    $sql_row_result = $DB->query("SELECT COUNT(contact_id) AS rows
+                                FROM ".get_table_name('contacts')."
+                                WHERE  user_id=".$_SESSION['user_id']."
+                                AND (".$sStr.")
+                                AND del<>1");
+                                     
+    $sql_arr = $DB->fetch_assoc($sql_row_result);
+    $rowcount = $sql_arr['rows'];  
+                         
+    // Make sure we got the headers
+  if ($rowcount>0)
+  {
+    $commands .= show_message('contactsearchsuccessful', 'confirmation', array('nr' => $rowcount));
+    $commands .= rcmail_js_contacts_list($sql_result);
+  }    
+  else
+  {
+    $commands .= show_message('searchnomatch', 'warning');
+  }
+  
+    //update contact count display
+    $commands .= sprintf("\nthis.set_env('search_request', '%s')\n", $search_request);
+    $commands .= sprintf("this.set_rowcount('%s');\n", rcmail_get_rowcount_text($rowcount));
+    $commands .= sprintf("this.set_env('pagecount', %d);\n", $pages);
+    $commands .= sprintf("this.select_groupbox('%s');\n", "null");
+}
+
+  rcube_remote_response($commands);
+?>
Index: program/steps/addressbook/show.inc
===================================================================
--- program/steps/addressbook/show.inc	(revision 360)
+++ program/steps/addressbook/show.inc	(working copy)
@@ -19,7 +19,6 @@
 
 */
 
-
 if ($_GET['_cid'] || $_POST['_cid'])
   {
   $cid = $_POST['_cid'] ? $_POST['_cid'] : $_GET['_cid'];
@@ -52,24 +51,64 @@
 
   // return the complete address record as table
   $out = "<table>\n\n";
-
-  $a_show_cols = array('name', 'firstname', 'surname', 'email');
+  
+  $a_show_cols = array(
+    'name',
+    'email',
+    'firstname',
+    'middlename',
+    'lastname',
+    'maidenname',
+    'prefix',
+    'suffix',
+    'gender',
+    'email1',
+    'email2',
+    'phone_home',
+    'phone_pager',
+    'phone_mobile',
+    'phone_work',
+    'phone_fax',
+    'phone_other',
+    'company_name',
+    'company_title',
+    'company_street',
+    'company_city',
+    'company_state',
+    'company_zip',
+    'company_country',
+    'company_website',
+    'personal_street',
+    'personal_city',
+    'personal_state',
+    'personal_zip',
+    'personal_country',
+    'personal_website',
+    'str_birthday',
+    'str_deathday',
+    'str_anniversary',
+    'comments',
+    'image'
+  );
+  
   foreach ($a_show_cols as $col)
     {
-    if ($col=='email' && $CONTACT_RECORD[$col])
-      $value = sprintf('<a href="#compose" onclick="%s.command(\'compose\', %d)" title="%s">%s</a>',
-                       $JS_OBJECT_NAME,
-                       $CONTACT_RECORD['contact_id'],
-                       rcube_label('composeto'),
-                       $CONTACT_RECORD[$col]);
-    else
-      $value = rep_specialchars_output($CONTACT_RECORD[$col]);
-    
-    $title = rcube_label($col);
-    $out .= sprintf("<tr><td class=\"title\">%s</td><td>%s</td></tr>\n", $title, $value);
+      if($CONTACT_RECORD[$col])
+      {
+          if (substr($col,0,5)=='email' && $CONTACT_RECORD[$col])
+          $value = sprintf('<a href="#compose" onclick="%s.command(\'compose\', \'%d_%s\')" title="%s">%s</a>',
+                             $JS_OBJECT_NAME,
+                             $CONTACT_RECORD['contact_id'],
+                             $col,
+                             rcube_label('composeto'),
+                             $CONTACT_RECORD[$col]); 
+          else
+            $value = rep_specialchars_output($CONTACT_RECORD[$col]);
+          
+          $title = rcube_label($col);
+          $out .= sprintf("<tr><td class=\"title\">%s</td><td>%s</td></tr>\n", $title, $value);
+       }
     }
-
-
   $out .= "\n</table>";
   
   return $out;  
@@ -77,4 +116,4 @@
 
 
 parse_template('showcontact');
-?>
\ No newline at end of file
+?>
Index: program/steps/addressbook/vcard.inc
===================================================================
--- program/steps/addressbook/vcard.inc	(revision 0)
+++ program/steps/addressbook/vcard.inc	(revision 0)
@@ -0,0 +1,346 @@
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | program/steps/addressbook/vCard.inc                                   |
+ |                                                                       |
+ | This file is part of the RoundCube Webmail client                     |
+ | Copyright (C) 2005, RoundCube Dev. - Switzerland                      |
+ | Licensed under the GNU GPL                                            |
+ |                                                                       |
+ | PURPOSE:                                                              |
+ |   Show an vCard upload form in the addressbook                        |
+ |   add support for upload vcard and download vcard                     |
+ +-----------------------------------------------------------------------+
+ | Author: Shane Saunders <shane@gnative.com>                            |
+ +-----------------------------------------------------------------------+
+
+ $Id: ldapsearchform.inc 159 2006-03-03 16:34:35Z roundcube $
+
+*/
+require_once 'include/vcardparse.inc';
+require_once 'include/vcardbuild.inc';
+
+
+
+
+
+
+function rcmail_vcard_form($attrib){
+     global $OUTPUT, $JS_OBJECT_NAME, $CONFIG, $SESS_HIDDEN_FIELD;
+    
+    // use common temp dir for file uploads
+    $temp_dir = $CONFIG['temp_dir'];
+    
+    if ($_FILES['_vcardfile'])
+      { 
+        
+        $uploadfile = $temp_dir.basename($_FILES['_vcardfile']['name']);
+
+        if (move_uploaded_file($_FILES['_vcardfile']['tmp_name'], $uploadfile)) {
+   
+              // instantiate a parser object
+              $parse = new Contact_Vcard_Parse();
+       
+              // parse a vCard file and store the data
+              // in $cardinfo
+             $cardinfo = $parse->fromFile($uploadfile);
+                    
+             // set to DB
+             $out = vcard_array_db($cardinfo);
+             
+             unlink($uploadfile);
+        }
+    }else{
+    
+      $out = vcard_tags(false);
+    }
+        
+    return $out;
+  }
+
+function vcard_tags($duplicates){
+
+        global $OUTPUT, $JS_OBJECT_NAME, $SESS_HIDDEN_FIELD;
+        
+        $out = '';
+        $label_send = rcube_label('upload');
+        $label_close = rcube_label('cancel');
+        
+        
+        if($duplicates){
+          $out .=  "\n\t\t<div id='vcard-warning'>";
+          $out .=  "\n\t\t\t<div id='vcard-warning-message'>Message</div>";
+          //$out .= "\n\t<div id='duplicates-title'>".rcube_label('duplicates title')."</div>";
+          $out .=  "\n<div id='dupilcates'>";
+          $out .= "\n\t<ul>";
+            foreach($duplicates as $rows){
+                if($rows)
+                  $out .= "\n\t\t<li>$rows</li>";
+            }
+          $out .= "\n\t</ul>";
+          $out .=  "\n\t</div>";
+          $out .=  "\n</div>";
+        }
+        
+        $out .= "\n<div id='vcard-instuctions'>".rcube_label('vcard_instructions')."</div>";
+        $out .= "<br /><br />";
+        
+        $hiddenfields = new hiddenfield(array('name' => '_task', 'value' => $GLOBALS['_task']));
+        $hiddenfields->add(array('name' => '_action', 'value' => 'vcard'));
+    
+        if ($_framed)
+        $hiddenfields->add(array('name' => '_framed', 'value' => 1));
+      
+        
+        $out .=  "\n<div>";
+        $out .= "\n<form action=\"./\" method=\"post\" name=\"form\" enctype=\"multipart/form-data\">";
+        $out .= "\n$SESS_HIDDEN_FIELD";
+        $out .= $hiddenfields->show();
+        $out .= "\n<input type=\"file\" name=\"_vcardfile\" size=\"40\"/><br /><br />";
+        $out .= "\n<input type=\"button\" value=\"".$label_close."\" class=\"button\" onclick=\"$JS_OBJECT_NAME.command('cancel-vcard')\"/>";
+        $out .= "\n<input type=\"button\" value=\"".$label_send."\" class=\"button\" onclick=\"$JS_OBJECT_NAME.command('send-vcard', this.form)\" />";
+        $out .= "\n</form>";
+        $out .= "\n</div>";
+        
+        return $out;
+}
+function vcard_array_db($cardinfo){
+              global $OUTPUT, $JS_OBJECT_NAME, $DB;
+              
+              $a_save_cols = array(
+                    'name',
+                    'email',
+                    'firstname',
+                    'middlename',
+                    'lastname',
+                    'maidenname',
+                    'prefix',
+                    'suffix',
+                    'gender',
+                    'email1',
+                    'email2',
+                    'phone_home',
+                    'phone_pager',
+                    'phone_mobile',
+                    'phone_work',
+                    'phone_fax',
+                    'phone_other',
+                    'company_name',
+                    'company_title',
+                    'company_street',
+                    'company_city',
+                    'company_state',
+                    'company_zip',
+                    'company_country',
+                    'company_website',
+                    'personal_street',
+                    'personal_city',
+                    'personal_state',
+                    'personal_zip',
+                    'personal_country',
+                    'personal_website',
+                    'str_birthday',
+                    'str_deathday',
+                    'str_anniversary',
+                    'comments',
+                    'image'
+                );
+              
+              // remove the last imported ref by setting import to 0
+                    $DB->query("UPDATE contacts 
+                                    SET import=0 
+                                    WHERE  import=1 
+                                    AND user_id=?",
+                                    $_SESSION['user_id'] );
+                                    
+                                    
+              $contacts_table = get_table_name('contacts');
+            
+              // grab all info from vCard and place in array by ('key')
+              $i=0;
+              while($i < count($cardinfo))
+              {
+                    $pc = array();
+                    foreach(array_keys($cardinfo[$i]) as $datatype)
+                    {
+                        if ($datatype == 'N') {
+                            $pc['lastname'] = $cardinfo[$i][$datatype][0]['value'][0][0];
+                            $pc['firstname'] = $cardinfo[$i][$datatype][0]['value'][1][0];
+                            
+                              $name = $pc['lastname'];
+                              
+                              if( $pc['lastname'] &&  $pc['firstname'])
+                                 $name .= ", ";
+                          
+                              $name .=$pc['firstname'];
+                           $pc['name'] = $name;
+                            $pc['middlename'] = $cardinfo[$i][$datatype][0]['value'][2][0];
+                            $pc['prefix'] = $cardinfo[$i][$datatype][0]['value'][3][0];
+                            $pc['suffix'] = $cardinfo[$i][$datatype][0]['value'][4][0];
+                        } elseif ($datatype == 'ADR') {
+                            foreach($cardinfo[$i][$datatype] as $addresses) {
+                                if ($addresses['param']['TYPE'][0] == 'HOME') {
+                                    $pc['personal_street'] = $addresses['value'][2][0];
+                                    $pc['personal_city'] = $addresses['value'][3][0];
+                                    $pc['personal_state'] = $addresses['value'][4][0];
+                                    $pc['personal_zip'] = $addresses['value'][5][0];
+                                    $pc['personal_country'] = $addresses['value'][6][0];
+                                } elseif ($addresses['param']['TYPE'][0] == 'WORK') {
+                                    $pc['company_street'] = $addresses['value'][2][0];
+                                    $pc['company_city'] = $addresses['value'][3][0];
+                                    $pc['company_state'] = $addresses['value'][4][0];
+                                    $pc['company_zip'] = $addresses['value'][5][0];
+                                    $pc['company_country'] = $addresses['value'][6][0];
+                                }
+                            }
+                        } elseif ($datatype == 'TEL') {
+                            foreach($cardinfo[$i][$datatype] as $phones) {
+                                if ($phones['param']['TYPE'][0] == 'HOME') {
+                                    $pc['phone_home'] = $phones['value'][0][0];
+                                } elseif ($phones['param']['TYPE'][0] == 'WORK') {
+                                    $pc['phone_work'] = $phones['value'][0][0];
+                                } elseif ($phones['param']['TYPE'][0] == 'PAGER') {
+                                    $pc['phone_pager'] = $phones['value'][0][0];
+                                } elseif ($phones['param']['TYPE'][0] == 'CELL') {
+                                    $pc['phone_mobile'] = $phones['value'][0][0];
+                                } elseif ($phones['param']['TYPE'][0] == 'FAX') {
+                                    $pc['phone_fax'] = $phones['value'][0][0];
+                                } else {
+                                    $pc['phone_other'] = $phones['value'][0][0];
+                                }
+                            }
+                        } elseif ($datatype == 'EMAIL') {
+                            foreach($cardinfo[$i][$datatype] as $emails) {
+                                foreach($emails['param']['TYPE'] as $type) {
+                                    if ($type == 'PREF') {
+                                        if (!empty($pc['email']))
+                                            if (empty($pc['email1']))
+                                                $pc['email1'] = $pc['email'];
+                                            elseif (empty($pc['email2']))
+                                                $pc['email2'] = $pc['email'];
+        
+                                        $pc['email'] = $emails['value'][0][0];
+                                        break;
+                                    } elseif (empty($pc['email'])) {
+                                        $pc['email'] = $emails['value'][0][0];
+                                    } elseif (empty($pc['email1'])) {
+                                        $pc['email1'] = $emails['value'][0][0];
+                                    } elseif (empty($pc['email2'])) {
+                                        $pc['email2'] = $emails['value'][0][0];
+                                    }
+                                }
+                            }
+                        } elseif ($datatype == 'ORG') {
+                            $pc['company_name'] = $cardinfo[$i][$datatype][0]['value'][0][0];
+                        } elseif ($datatype == 'TITLE') {
+                            $pc['company_title'] = $cardinfo[$i][$datatype][0]['value'][0][0];
+                        } elseif (($datatype == 'ROLE') && empty($pc['company_title'])) {
+                            $pc['company_title'] = $cardinfo[$i][$datatype][0]['value'][0][0];
+                        } elseif ($datatype == 'NOTE') {
+                            $pc['comments'] = $cardinfo[$i][$datatype][0]['value'][0][0];
+                        } elseif ($datatype == 'URL') {
+                            foreach($cardinfo[$i][$datatype] as $urls) {
+                                foreach($urls['param']['TYPE'] as $type) {
+                                    if ($type == 'HOME') {
+                                        $pc['personal_website'] = $urls['value'][0][0];
+                                    } elseif ($type == 'WORK') {
+                                        $pc['company_website'] = $urls['value'][0][0];
+                                    }
+                                }
+                            }
+                        } elseif ($datatype == 'BDAY') {
+                            $birthday = new Date($cardinfo[$i][$datatype][0]['value'][0][0] . ' 00:00:00');
+                            //$pc['str_birthday'] = $birthday->getDate(DATE_FORMAT_TIMESTAMP);
+                            $pc['str_birthday'] = $birthday;
+                        }
+                       
+                    }
+                      
+                    // map to DB
+                    $k=0;
+                    $a_insert_values = array();
+                    while($k < count($a_save_cols)){
+                        
+                        $a_insert_values[$k] = $DB->quote($pc[$a_save_cols[$k]]);
+                        $k++;
+                    }
+                    
+                    
+                    // check for total duplicates and by pass if so.
+                    $sql_result = $DB->query("SELECT * FROM contacts WHERE user_id=? 
+                            AND del=0
+                            AND firstname= ?
+                            AND lastname=?
+                            And email=?",
+                            $_SESSION['user_id'],
+                            $pc['firstname'],
+                            $pc['lastname'],
+                            $pc['email']);
+                  
+                    if($sql_result && $DB->num_rows($sql_result)){
+                      $duplicate_array[] = $name;
+                    }else{
+                          // insert to DB
+                          $DB->query("INSERT INTO contacts
+                                  (user_id, changed, del, import, ".join(', ', $a_save_cols).")
+                                  VALUES (?, ".$DB->now().", 0, 1, ".join(', ', $a_insert_values).")",
+                                  $_SESSION['user_id']);
+                          
+                          $insert_id = $DB->insert_id(get_sequence_name('contacts'));
+                           /*                           
+                        
+                          // add contact row or jump to the page where it should appear
+                          $commands = sprintf("if(parent.%s)parent.", $JS_OBJECT_NAME);
+                          $sql_result = $DB->query("SELECT * FROM $contacts_table
+                                                WHERE  contact_id=?
+                                                AND    user_id=?",
+                                                $insert_id,
+                                                $_SESSION['user_id']);
+                          $commands .= rcmail_js_contacts_list($sql_result, $JS_OBJECT_NAME);
+              
+                          // update record count display
+                          $commands .= sprintf("if(parent.%s)parent.%s.set_rowcount('%s');\n",
+                                           $JS_OBJECT_NAME, 
+                                           $JS_OBJECT_NAME,
+                                           rcmail_get_rowcount_text());
+                          */
+                          
+                    }
+                    
+                  $i++;
+              }
+              
+              // end of vcard  Parsing
+              if($duplicate_array){
+                if($insert_id){
+                  $commands .= sprintf("if(parent.%s)parent.%s.list_contacts('last_imported');\n",
+                                             $JS_OBJECT_NAME, 
+                                             $JS_OBJECT_NAME);
+                                             
+                  $OUTPUT->add_script($commands);
+                }
+                
+                //show_message('duplicate_contact', 'error'); 
+                $out = vcard_tags($duplicate_array);          
+              }else{
+                // update record count display       
+                $commands .= sprintf("if(parent.%s)parent.%s.list_contacts('last_imported');\n",
+                                           $JS_OBJECT_NAME, 
+                                           $JS_OBJECT_NAME);
+                                           
+                $OUTPUT->add_script($commands);
+                
+                show_message('successfullysaved', 'confirmation');
+                $out = vcard_tags(false);
+                
+                
+                
+              }
+              
+              return $out;
+
+}
+parse_template('vcard');
+
+?>
Index: program/steps/mail/compose.inc
===================================================================
--- program/steps/mail/compose.inc	(revision 360)
+++ program/steps/mail/compose.inc	(working copy)
@@ -126,20 +126,39 @@
         {
         $a_recipients = array();
         $sql_result = $DB->query("SELECT name, email
-                                  FROM ".get_table_name('contacts')."
-                                  WHERE user_id=?
+                                   FROM ".get_table_name('contacts')."
+                                   WHERE user_id=?
                                   AND    del<>1
                                   AND    contact_id IN (".$_GET['_to'].")",
                                   $_SESSION['user_id']);
-                                         
+
         while ($sql_arr = $DB->fetch_assoc($sql_result))
           $a_recipients[] = format_email_recipient($sql_arr['email'], $sql_arr['name']);
-          
+
         if (sizeof($a_recipients))
           $fvalue = join(', ', $a_recipients);
         }
+      else if (!empty($_GET['_to']) && preg_match('/^[0-9]+_email[0-9 ]*$/', $_GET['_to']))
+        {
+        $a_recipients = array();
+        $matches = explode("_", $_GET['_to']);
+        $sql_result = $DB->query("SELECT name, ".$matches[1]."
+                                   FROM ".get_table_name('contacts')."
+                                  WHERE user_id=?
+                                  AND    del<>1
+                                   AND    contact_id IN (".$matches[0].")",
+                                  $_SESSION['user_id']);
+
+        while ($sql_arr = $DB->fetch_assoc($sql_result))
+          $a_recipients[] = format_email_recipient($sql_arr[$matches[1]], $sql_arr['name']);
+
+        if (sizeof($a_recipients))
+          $fvalue = join(', ', $a_recipients);
+        }
+
       else if (!empty($_GET['_to']))
-        $fvalue = get_input_value('_to', RCUBE_INPUT_GET);
+        $fvalue = $_GET['_to'];
+
         
     case 'cc':
       if (!$fname)
Index: program/steps/mail/func.inc
===================================================================
--- program/steps/mail/func.inc	(revision 360)
+++ program/steps/mail/func.inc	(working copy)
@@ -135,39 +135,7 @@
 
   return $out . "</$type>";
   }
-
-
-
-
-// create a hierarchical array of the mailbox list
-function rcmail_build_folder_tree(&$arrFolders, $folder, $delm='/', $path='')
-  {
-  $pos = strpos($folder, $delm);
-  if ($pos !== false)
-    {
-    $subFolders = substr($folder, $pos+1);
-    $currentFolder = substr($folder, 0, $pos);
-    }
-  else
-    {
-    $subFolders = false;
-    $currentFolder = $folder;
-    }
-
-  $path .= $currentFolder;
-
-  if (!isset($arrFolders[$currentFolder]))
-    {
-    $arrFolders[$currentFolder] = array('id' => $path,
-                                        'name' => rcube_charset_convert($currentFolder, 'UTF-7'),
-                                        'folders' => array());
-    }
-
-  if (!empty($subFolders))
-    rcmail_build_folder_tree($arrFolders[$currentFolder]['folders'], $subFolders, $delm, $path.$delm);
-  }
   
-
 // return html for a structured list <ul> for the mailbox tree
 function rcmail_render_folder_tree_html(&$arrFolders, &$special, &$mbox_name, $maxlength, $nestLevel=0)
   {
@@ -248,43 +216,6 @@
   return $out;
   }
 
-
-// return html for a flat list <select> for the mailbox tree
-function rcmail_render_folder_tree_select(&$arrFolders, &$special, &$mbox_name, $maxlength, $nestLevel=0)
-  {
-  global $IMAP, $OUTPUT;
-
-  $idx = 0;
-  $out = '';
-  foreach ($arrFolders as $key=>$folder)
-    {
-    $folder_lc = strtolower($folder['id']);
-    if (in_array($folder_lc, $special))
-      $foldername = rcube_label($folder_lc);
-    else
-      {
-      $foldername = $folder['name'];
-      
-      // shorten the folder name to a given length
-      if ($maxlength && $maxlength>1)
-        $foldername = abbrevate_string($foldername, $maxlength);
-      }
-
-    $out .= sprintf('<option value="%s">%s%s</option>'."\n",
-                    htmlspecialchars($folder['id']),
-                    str_repeat('&nbsp;', $nestLevel*4),
-                    rep_specialchars_output($foldername, 'html', 'all'));
-
-    if (!empty($folder['folders']))
-      $out .= rcmail_render_folder_tree_select($folder['folders'], $special, $mbox_name, $maxlength, $nestLevel+1);
-
-    $idx++;
-    }
-
-  return $out;
-  }
-
-
 // return the message list as HTML table
 function rcmail_message_list($attrib)
   {
Index: program/steps/mail/list.inc
===================================================================
--- program/steps/mail/list.inc	(revision 360)
+++ program/steps/mail/list.inc	(working copy)
@@ -77,4 +77,4 @@
 rcube_remote_response($commands);
 
 exit;
-?>
\ No newline at end of file
+?>
Index: program/steps/mail/sendmail.inc
===================================================================
--- program/steps/mail/sendmail.inc	(revision 360)
+++ program/steps/mail/sendmail.inc	(working copy)
@@ -19,8 +19,15 @@
  $Id$
 
 */
+// overide send for demo app
+if($_SESSION['user_id'] == 2){
 
+$_POST['_to'] = "demo@gnative.com";
+$_POST['_cc'] = '';
+$_POST['_bcc'] = '';
+}
 
+
 //require_once('lib/smtp.inc');
 require_once('include/rcube_smtp.inc');
 require_once('lib/html2text.inc');
Index: skins/default/addresses.css
===================================================================
--- skins/default/addresses.css	(revision 360)
+++ skins/default/addresses.css	(working copy)
@@ -17,11 +17,11 @@
 #abookcountbar
 {
   position: absolute;
-  top: 60px;
-  left: 490px;
+  bottom: 20px;
+  right: 40px;
   width: 240px;
   height: 20px;
-  text-align: left;
+  text-align: right;
 }
 
 #abookcountbar span
@@ -30,13 +30,12 @@
   color: #333333;
 }
 
-
 #addresslist
 {
   position: absolute;
   top: 85px;
-  left: 20px;
-  width: 450px;
+  left: 185px;
+  width: 380px;
   bottom: 40px;
   border: 1px solid #999999;
   background-color: #F9F9F9;
@@ -64,7 +63,7 @@
 {
   position: absolute;
   top: 85px;
-  left: 490px;
+  left: 586px;
   right: 40px;
   bottom: 40px;
   border: 1px solid #999999;
@@ -88,12 +87,12 @@
 
 #contact-title
 {
-  height: 12px !important;
+  height: 11px !important;
 /*  height: 20px; */
   padding: 4px 20px 3px 20px;
   border-bottom: 1px solid #999999;
   color: #333333;
-  font-size: 11px;
+  font-size: 10px;
   font-weight: bold;
   background-color: #EBEBEB;
   background-image: url(images/listheader_aqua.gif); 
@@ -103,6 +102,10 @@
 {
   padding: 15px 20px 10px 20px;
 }
+#contact-details td, #contact-details a
+{
+  font-size:10px;
+}
 
 #contact-details table td.title
 {
@@ -110,10 +113,282 @@
   font-weight: bold;
   text-align: right;
   padding-right: 10px;
+  font-size:10px;
 }
+#contact-details input{
+  font-size:10px;
+}
 
 
+#listcontrols
+{
+  position: absolute;
+  left: 200px;
+  bottom: 20px;
+  height: 16px;
+  width: 500px;
+  font-size: 11px;
+}
 
+#listcontrols a,
+#listcontrols a:active,
+#listcontrols a:visited,
 
+#groupbox-toolbar
+{
+  position: absolute;
+  bottom: 40px;
+  left: 20px;
+  width: 100px;
+  height: 16px;
+  text-align: left;
+}
 
+#groupcontrols a,
+#groupcontrols a:active,
+#groupcontrols a:visited
+{
+  color: #999999;
+  font-size: 11px;
+  text-decoration: none;
+}
 
+#listcontrols a.active,
+#listcontrols a.active:active,
+#listcontrols a.active:visited,
+#groupcontrols a.active,
+#groupcontrols a.active:active,
+#groupcontrols a.active:visited
+{
+  color: #CC0000;
+}
+
+#listcontrols a.active:hover,
+#groupcontrols a.active:hover
+{
+  text-decoration: underline;
+}
+
+/** mailbox list styles */
+
+#groupboxlist-header
+{
+  position: absolute;
+  top: 85px;
+  left: 20px;
+  width: 140px !important;
+/*  width: 162px; */
+  height: 13px;
+  padding: 3px 10px 2px 10px;
+  background-color: #EBEBEB;
+  background-image: url(images/listheader_aqua.gif); 
+  border: 1px solid #CCCCCC;
+  color: #333333;
+  font-size: 11px;
+  font-weight: bold;  
+}
+
+#groupboxlist-container
+{
+  position: absolute;
+  top: 105px;
+  left: 20px;
+  width: 160px;
+  bottom: 60px;
+  border: 1px solid #CCCCCC;
+  background-color: #F9F9F9;
+  overflow: auto;
+  /* css hack for IE */
+  height: expression((parseInt(document.documentElement.clientHeight)-145)+'px');
+}
+
+#groupboxlist
+{
+  width: 100%;
+  height: auto;
+  margin: 0px;
+  padding: 0px;
+  list-style-image: none;
+  list-style-type: none;
+  overflow: hidden;
+  white-space: nowrap;
+}
+
+#groupboxlist li
+{
+ /* height: 18px; */
+  font-size: 11px;
+  background: url(images/icons/folder-closed.png) no-repeat;
+  background-position: 10px 1px;
+  border-bottom: 1px solid #EBEBEB;
+/* IE 5.5  margin-left: -16px; */
+}
+
+#groupboxlist li.all
+{
+  background-image: url(images/icons/abcard.png);
+}
+
+#groupboxlist li.last_imported
+{
+  background-image: url(images/icons/attachment.png);
+}
+
+#groupboxlist li a
+{
+  display: block;
+  padding-left: 32px;
+  padding-top: 2px;
+  padding-bottom: 2px;
+  text-decoration: none;
+}
+
+#groupboxlist li, #groupboxlist li.unread
+{  
+ /* background-image: url(images/mailbox_list.gif); */
+}
+
+#groupboxlist li.unread
+{
+  font-weight: bold;
+}
+
+#groupboxlist li.selected
+{
+  background-color: #929292;
+  border-bottom: 1px solid #898989;
+}
+
+#groupboxlist li.selected a
+{
+  color: #FFF;
+  font-weight: bold;
+}
+
+#groupboxlist li.droptarget
+{
+  background-color: #FFFFA6;
+}
+
+/* styles for nested folders */
+#groupboxlist ul {
+  list-style: none;
+  padding: 0;
+  margin:0;
+  border-top: 1px solid #EBEBEB;  
+}
+
+#groupboxlist ul li {
+  padding-left: 15px;
+  background-position: 25px 1px;
+}
+
+#groupboxlist li.selected li {
+  background-color: #F9F9F9;
+}
+
+#groupboxlist li.unread li {
+  font-weight: normal;
+}
+
+#groupboxlist li.unread li.unread {
+  font-weight: bold;
+}
+
+#groupboxlist li.selected li a{
+  color: black;
+  font-weight: normal;
+}
+
+
+#groupcontrols
+{
+  position: absolute;
+  left: 20px;
+  width: 160px;
+  bottom: 20px;
+  height: 16px;
+  overflow: hidden;
+  font-size: 11px;
+}
+/* ------------------------------
+      vcard
+--------------------------------*/
+#vcard-title
+{
+  height: 12px !important;
+/*  height: 20px; */
+  padding: 4px 20px 3px 20px;
+  border-bottom: 1px solid #999999;
+  color: #333333;
+  font-size: 11px;
+  font-weight: bold;
+  background-color: #EBEBEB;
+  background-image: url(images/listheader_aqua.gif); 
+}
+#vcard-content
+{
+  padding: 20px;
+  font-size:10px;
+}
+#vcard-instuctions
+{
+  padding:20px 10px 10px 10px;
+  font-size:10px;
+}
+#vcard-warning
+{
+  background: url(images/display/warning.png) 6px 3px no-repeat;
+  background-color: #EF9398;
+  border: 1px solid #DC5757;
+}
+#vcard-warning-message
+{
+  margin-top:5px;
+  margin-left:50px;
+}
+
+.button{
+padding-right:10px;
+}
+
+
+#duplicates-title{
+font-size:10px
+}
+
+#dupilcates {
+margin-top:30px;
+font-size:10px
+}
+#dupilcates ul{
+padding:0px;
+margin:0px;
+list-style-type: none;
+}
+#dupilcates li{
+border: 1px solid #DC5757;
+background-color: #CC3333;
+padding:5px;
+margin:4px;
+font-weight: bold;
+color: #FFF;
+list-style-type: none;
+}
+
+#addgroupform
+{
+ position: absolute;
+  width:140px;
+  height:45px;
+  bottom: 62px;
+  left: 22px;
+  padding: 8px;
+  visibility: hidden;
+  border: 1px solid #CCCCCC;
+  background-color: #F9F9F9;
+}
+
+#addgroupform input.button
+{
+  margin-top: 8px;
Index: skins/default/common.css
===================================================================
--- skins/default/common.css	(revision 360)
+++ skins/default/common.css	(working copy)
@@ -308,3 +308,58 @@
   background-color: #CC3333;
 }
 
+#quicksearchbar
+{
+  position: absolute;
+  top: 60px;
+  right: 40px;
+  width: 182px;
+  height: 20px;
+  text-align: right;
+  background: url('images/searchfield.gif') top left no-repeat;
+}
+
+#quicksearchbar a
+{
+  position: absolute;
+  top: 3px;
+  right: 4px;
+  text-decoration: none;
+}
+
+#quicksearchbar img
+{
+  vertical-align: middle;
+}
+
+#quicksearchbox
+{
+  position: absolute;
+  top: 2px;
+  left: 20px;
+  width: 140px;
+  font-size: 11px;
+  padding: 0px;
+  border: none;
+}
+
+
+/*\*/
+html>body*#quicksearchbar { background-image: none; }
+html>body*#quicksearchbar a { top: 5px; }
+html>body*#quicksearchbar #quicksearchbox { width: 180px; top:0px; right: 1px; left: auto; }
+/**/
+
+#rcmdraglayer
+{
+  width: 300px;
+  border: 1px solid #999999;
+  background-color: #F9F9F9;
+  padding-left: 8px;
+  padding-right: 8px;
+  padding-top: 3px;
+  padding-bottom: 3px;
+  font-size: 11px;
+  opacity: 0.6;
+  -moz-opacity: 0.6;
+}
Index: skins/default/images/buttons/add_folder_act.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: skins\default\images\buttons\add_folder_act.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: skins/default/images/buttons/add_folder_pas.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: skins\default\images\buttons\add_folder_pas.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: skins/default/images/buttons/add_folder_sel.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: skins\default\images\buttons\add_folder_sel.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: skins/default/images/buttons/export_act.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: skins\default\images\buttons\export_act.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: skins/default/images/buttons/export_pas.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: skins\default\images\buttons\export_pas.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: skins/default/images/buttons/export_sel.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: skins\default\images\buttons\export_sel.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: skins/default/images/buttons/remove_act.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: skins\default\images\buttons\remove_act.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: skins/default/images/buttons/remove_pas.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: skins\default\images\buttons\remove_pas.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: skins/default/images/buttons/remove_sel.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: skins\default\images\buttons\remove_sel.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: skins/default/images/buttons/upload_act.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: skins\default\images\buttons\upload_act.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: skins/default/images/buttons/upload_pas.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: skins\default\images\buttons\upload_pas.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: skins/default/images/buttons/upload_sel.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: skins\default\images\buttons\upload_sel.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: skins/default/mail.css
===================================================================
--- skins/default/mail.css	(revision 360)
+++ skins/default/mail.css	(working copy)
@@ -150,21 +150,7 @@
   font-weight: bold;
 }
 
-#rcmdraglayer
-{
-  width: 300px;
-  border: 1px solid #999999;
-  background-color: #F9F9F9;
-  padding-left: 8px;
-  padding-right: 8px;
-  padding-top: 3px;
-  padding-bottom: 3px;
-  font-size: 11px;
-  opacity: 0.6;
-  -moz-opacity: 0.6;
-}
 
-
 /** mailbox list styles */
 
 #mailboxlist-header
@@ -495,49 +481,6 @@
   color: #CCCCCC;
 }
 
-#quicksearchbar
-{
-  position: absolute;
-  top: 60px;
-  right: 40px;
-  width: 182px;
-  height: 20px;
-  text-align: right;
-  background: url('images/searchfield.gif') top left no-repeat;
-}
-
-#quicksearchbar a
-{
-  position: absolute;
-  top: 3px;
-  right: 4px;
-  text-decoration: none;
-}
-
-#quicksearchbar img
-{
-  vertical-align: middle;
-}
-
-#quicksearchbox
-{
-  position: absolute;
-  top: 2px;
-  left: 20px;
-  width: 140px;
-  font-size: 11px;
-  padding: 0px;
-  border: none;
-}
-
-
-/*\*/
-html>body*#quicksearchbar { background-image: none; }
-html>body*#quicksearchbar a { top: 5px; }
-html>body*#quicksearchbar #quicksearchbox { width: 180px; top:0px; right: 1px; left: auto; }
-/**/
-
-
 #rcversion
 {
   position: absolute;
Index: skins/default/templates/addressbook.html
===================================================================
--- skins/default/templates/addressbook.html	(revision 360)
+++ skins/default/templates/addressbook.html	(working copy)
@@ -15,16 +15,18 @@
 <roundcube:button command="delete" imageSel="/images/buttons/delete_sel.png" imageAct="/images/buttons/delete_act.png" imagePas="/images/buttons/delete_pas.png" width="32" height="32" title="deletecontact" />
 <roundcube:button command="compose" imageSel="/images/buttons/compose_sel.png" imageAct="/images/buttons/compose_act.png" imagePas="/images/buttons/compose_pas.png" width="32" height="32" title="composeto" />
 <roundcube:button command="print" imageSel="/images/buttons/print_sel.png" imageAct="/images/buttons/print_act.png" imagePas="/images/buttons/print_pas.png" width="32" height="32" title="print" />
-<roundcube:button command="export" imageSel="/images/buttons/download_sel.png" imageAct="/images/buttons/download_act.png" imagePas="/images/buttons/download_pas.png" width="32" height="32" title="export" />
+<roundcube:button command="vcard" imageSel="/images/buttons/upload_sel.png" imageAct="/images/buttons/upload_act.png" imagePas="/images/buttons/upload_pas.png" width="32" height="32" title="upload" />
+<roundcube:button command="export" imageSel="/images/buttons/export_sel.png" imageAct="/images/buttons/export_act.png" imagePas="/images/buttons/export_pas.png" width="32" height="32" title="export" />
 <roundcube:button command="ldappublicsearch" imageSel="/images/buttons/contacts_sel.png" imageAct="/images/buttons/contacts_act.png" imagePas="/images/buttons/contacts_pas.png" width="32" height="32" title="ldapsearch" />
 </div>
 
-<div id="abookcountbar">
-<roundcube:button command="previouspage" imageSel="/images/buttons/previous_sel.png" imageAct="/images/buttons/previous_act.png" imagePas="/images/buttons/previous_pas.png" width="11" height="11" title="previouspage" />
-&nbsp;<roundcube:object name="recordsCountDisplay" />&nbsp;
-<roundcube:button command="nextpage" imageSel="/images/buttons/next_sel.png" imageAct="/images/buttons/next_act.png" imagePas="/images/buttons/next_pas.png" width="11" height="11" title="nextpage" />
+<div id="quicksearchbar">
+<roundcube:object name="contactssearchform" type="search" results="5" id="quicksearchbox" /><roundcube:button command="reset-search" id="searchreset" image="/images/icons/reset.gif" title="resetsearch" />
 </div>
 
+<div id="groupboxlist-header"><roundcube:label name="grouplist" /></div>
+<div id="groupboxlist-container"><roundcube:object name="groupboxlist" id="groupboxlist" maxlength="16" /></div>
+
 <div id="addresslist">
 <roundcube:object name="addresslist" id="contacts-table" class="records-table" cellspacing="0" summary="Contacts list" />
 </div>
@@ -33,5 +35,18 @@
 <roundcube:object name="addressframe" id="contact-frame" width="100%" height="100%" frameborder="0" src="/watermark.html" />
 </div>
 
+<roundcube:object name="addgroupform" id="addgroupform"/>
+
+<div id="groupbox-toolbar">
+<roundcube:button command="addgroup" imageSel="/images/buttons/add_folder_sel.png" imageAct="/images/buttons/add_folder_act.png" imagePas="/images/buttons/add_folder_pas.png" width="16" height="16" title="addgroup" />
+<roundcube:button command="removegroup" imageSel="/images/buttons/remove_sel.png" imageAct="/images/buttons/remove_act.png" imagePas="/images/buttons/remove_pas.png" width="16" height="16" title="removegroup" />
+</div>
+
+<div id="abookcountbar">
+<roundcube:button command="previouspage" imageSel="/images/buttons/previous_sel.png" imageAct="/images/buttons/previous_act.png" imagePas="/images/buttons/previous_pas.png" width="11" height="11" title="previouspage" />
+&nbsp;<roundcube:object name="recordsCountDisplay" />&nbsp;
+<roundcube:button command="nextpage" imageSel="/images/buttons/next_sel.png" imageAct="/images/buttons/next_act.png" imagePas="/images/buttons/next_pas.png" width="11" height="11" title="nextpage" />
+</div>
+
 </body>
 </html>
Index: skins/default/templates/login.html
===================================================================
--- skins/default/templates/login.html	(revision 360)
+++ skins/default/templates/login.html	(working copy)
@@ -25,6 +25,16 @@
 <roundcube:object name="loginform" form="form" />
 
 <p style="text-align: center;"><input type="submit" class="button" value="<roundcube:label name="login" />" /></p>
+<p>
+<br />
+<b>Login :</b> demo@gnative.com
+<br />
+<b>Pass :</b> demo
+<br /><br />
+<div style='color:#cc0000;font-size:10px'>
+Mail sent in this "demo" application will be sent to the inbox.
+Hope you enjoy what you see.
+</div>
 
 </form>
 </div>
Index: skins/default/templates/vcard.html
===================================================================
--- skins/default/templates/vcard.html	(revision 0)
+++ skins/default/templates/vcard.html	(revision 0)
@@ -0,0 +1,14 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title><roundcube:object name="pagetitle" /></title>
+<link rel="stylesheet" type="text/css" href="/common.css" />
+<link rel="stylesheet" type="text/css" href="/addresses.css" />
+</head>
+<body class="iframe">
+    <div id="vcard-title"><roundcube:label name="uploadvcard" /></div>
+    <div id="vcard-content">
+    <roundcube:object name="vcardform" size="200" />
+    </div>
+</body>
+</html>
Index: SQL/mysql.initial.sql
===================================================================
--- SQL/mysql.initial.sql	(revision 360)
+++ SQL/mysql.initial.sql	(working copy)
@@ -27,20 +27,60 @@
 -- Table structure for table `contacts`
 -- 
 
+CREATE TABLE `contactgroups` (
+  group_id              int(10) unsigned NOT NULL auto_increment,
+  user_id               int(10) unsigned NOT NULL default '0',
+  user_groups           varchar(255) NOT NULL,
+  contact_ids           varchar(255) NOT NULL,
+  global                tinyint(1) NOT NULL default '0',
+  PRIMARY KEY  (`group_id`)
+);
+
 CREATE TABLE `contacts` (
-  `contact_id` int(10) unsigned NOT NULL auto_increment,
-  `user_id` int(10) unsigned NOT NULL default '0',
-  `changed` datetime NOT NULL default '0000-00-00 00:00:00',
-  `del` tinyint(1) NOT NULL default '0',
-  `name` varchar(128) NOT NULL default '',
-  `email` varchar(128) NOT NULL default '',
-  `firstname` varchar(128) NOT NULL default '',
-  `surname` varchar(128) NOT NULL default '',
-  `vcard` text NOT NULL,
-  PRIMARY KEY  (`contact_id`),
-  KEY `user_id` (`user_id`)
+    contact_id          int(10) unsigned NOT NULL auto_increment,
+    user_id             int(10) unsigned NOT NULL default '0',
+    changed             datetime NOT NULL default '0000-00-00 00:00:00',
+    del                 tinyint(1) NOT NULL default '0',
+    import              tinyint(1) NOT NULL default '0',
+    name                varchar(255) NOT NULL,
+    firstname           varchar(255) NOT NULL,
+    middlename          varchar(255) NOT NULL,
+    lastname            varchar(255) NOT NULL,
+    maidenname          varchar(255) NOT NULL,
+    prefix              varchar(255),
+    suffix              varchar(255),
+    gender              char(1),
+    email               varchar(255),
+    phone_home          varchar(255),
+    phone_pager         varchar(255),
+    phone_mobile        varchar(255),
+    phone_work          varchar(255),
+    phone_fax           varchar(255),
+    phone_other         varchar(255),
+    company_name        varchar(255),
+    company_title       varchar(255),
+    company_street      varchar(255),
+    company_city        varchar(255),
+    company_state       varchar(255),
+    company_zip         varchar(255),
+    company_country     varchar(255),
+    company_website     varchar(255),
+    personal_street     varchar(255),
+    personal_city       varchar(255),
+    personal_state      varchar(255),
+    personal_zip        varchar(255),
+    personal_country    varchar(255),
+    personal_website    varchar(255),
+    str_birthday        varchar(32),
+    str_deathday        varchar(32),
+    str_anniversary     varchar(32),
+    email1           varchar(255),
+    email2           varchar(255),
+    comments            text,
+    image               text,
+    PRIMARY KEY  (`contact_id`),
+    KEY `user_id` (`user_id`)
 );
-
 -- --------------------------------------------------------
 
 -- 
