<?php
/*
 * Copyright 2005 - 2009  Zarafa B.V.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License, version 3, 
 * as published by the Free Software Foundation with the following additional 
 * term according to sec. 7:
 *  
 * According to sec. 7 of the GNU Affero General Public License, version
 * 3, the terms of the AGPL are supplemented with the following terms:
 * 
 * "Zarafa" is a registered trademark of Zarafa B.V. The licensing of
 * the Program under the AGPL does not imply a trademark license.
 * Therefore any rights, title and interest in our trademarks remain
 * entirely with us.
 * 
 * However, if you propagate an unmodified version of the Program you are
 * allowed to use the term "Zarafa" to indicate that you distribute the
 * Program. Furthermore you may use our trademarks where it is necessary
 * to indicate the intended purpose of a product or service provided you
 * use it in accordance with honest practices in industrial or commercial
 * matters.  If you want to propagate modified versions of the Program
 * under the name "Zarafa" or "Zarafa Server", you may only do so if you
 * have a written permission by Zarafa B.V. (to acquire a permission
 * please contact Zarafa at trademark@zarafa.com).
 * 
 * The interactive user interface of the software displays an attribution
 * notice containing the term "Zarafa" and/or the logo of Zarafa.
 * Interactive user interfaces of unmodified and modified versions must
 * display Appropriate Legal Notices according to sec. 5 of the GNU
 * Affero General Public License, version 3, when you propagate
 * unmodified or modified versions of the Program. In accordance with
 * sec. 7 b) of the GNU Affero General Public License, version 3, these
 * Appropriate Legal Notices must retain the logo of Zarafa or display
 * the words "Initial Development by Zarafa" if the display of the logo
 * is not reasonably feasible for technical reasons."
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *  
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */

?>
<?php
	/**
	 * Addressbook Module
	 */
	class AddressbookListModule extends ListModule
	{	
		/**
		 * Constructor
		 * @param int $id unique id.
		 * @param array $data list of all actions.
		 */
		function AddressbookListModule($id, $data)
		{
			$this->properties = $GLOBALS["properties"]->getContactABProperties();
		
			// Default Columns
			$this->tablecolumns = array();
			$this->addColumn($this->tablecolumns, "icon_index", true, 0, _("Icon"), _("Sort on Icon"));
			$this->addColumn($this->tablecolumns, "fileas", true, 1, _("File as"), _("Sort on File As"), "30%");
			$this->addColumn($this->tablecolumns, "display_name", true, 2, _("Display Name"), _("Sort on Display Name"), "35%");
			$this->addColumn($this->tablecolumns, "email_address", true, 3, _("Email Address"), _("Sort on Email Address"), PERCENTAGE);
			$this->addColumn($this->tablecolumns, "entryid", false);
			$this->addColumn($this->tablecolumns, "email_address_display_name_1", false);
			$this->addColumn($this->tablecolumns, "email_address_2", false);
			$this->addColumn($this->tablecolumns, "email_address_display_name_2", false);
			$this->addColumn($this->tablecolumns, "email_address_3", false);
			$this->addColumn($this->tablecolumns, "email_address_display_name_3", false);
			$this->addColumn($this->tablecolumns, "message_class", false);

			parent::ListModule($id, $data, array());

			$this->sort = array();
			$this->sort[$this->properties["fileas"]] = TABLE_SORT_ASCEND;
		}
		
		/**
		 * Executes all the actions in the $data variable.
		 * @return boolean true on success of false on fialure.
		 */
		function execute()
		{
			$result = false;
			
			foreach($this->data as $action)
			{
				if(isset($action["attributes"]) && isset($action["attributes"]["type"])) {
					$store = $this->getActionStore($action);
					$parententryid = $this->getActionParentEntryID($action);
					$entryid = $this->getActionEntryID($action);
					switch($action["attributes"]["type"])
					{
						case "hierarchy":
							$result = $this->getHierarchy($action);
							break;
						case "contacts":
							$result = $this->messageList($store, $entryid, $action);
							break;
						case "globaladdressbook":
							$result = $this->GABUsers($action);
							break;
					}
				}
			}
			
			return $result;
		}
		
		/**
		 * Function which retrieves a list of contacts in a contact folder
		 * @param object $store MAPI Message Store Object
		 * @param string $entryid entryid of the folder
		 * @param array $action the action data, sent by the client
		 * @return boolean true on success or false on failure		 		 
		 */
		function messageList($store, $entryid, $action)
		{
			$result = false;
			$searchPerformed = false;				// flag to check the request is for search or not
			$disableFullContactlist = false;		// flag for disabling the contact list

			if(!$entryid) {
				$rootcontainer = mapi_msgstore_openentry($store);
				if($rootcontainer) {
					$props = mapi_getprops($rootcontainer, array(PR_IPM_CONTACT_ENTRYID));
					
					if(isset($props[PR_IPM_CONTACT_ENTRYID])) {
						$entryid = $props[PR_IPM_CONTACT_ENTRYID];
					}
				}
			}

			if($store && $entryid) {
				$restriction = array(RES_AND, // get contacts when having one or more email adress
									array(
										array(RES_PROPERTY,
											array(	RELOP=>RELOP_EQ,
													ULPROPTAG=>PR_MESSAGE_CLASS,
													VALUE => array(PR_MESSAGE_CLASS=>"IPM.Contact")
											)
										),
										array(RES_PROPERTY,
											array(	RELOP => RELOP_GT,
													ULPROPTAG => $this->properties["address_book_long"],
													VALUE => array($this->properties["address_book_long"] => 0)
											)
										)
									)
								);

				// include distribution lists when requested
				if (isset($action["groups"]) && $action["groups"] != "no"){
					$restriction = 	array(RES_OR,
										array(
											array(RES_PROPERTY, // get distribution lists
												array(	RELOP=>RELOP_EQ,
														ULPROPTAG=>PR_MESSAGE_CLASS,
														VALUE => array(PR_MESSAGE_CLASS=>"IPM.DistList")
												)
											),
											$restriction,
										)
									);
				}

				if(isset($action["restriction"])) {
					$searchPerformed = true;
					if(isset($action["restriction"]["name"])) {
						$restrictions = array();
						$props = array($this->properties["fileas"], 
									   $this->properties["display_name"],
									   $this->properties["email_address_1"],
									   $this->properties["email_address_display_name_1"],
									   $this->properties["email_address_2"],
									   $this->properties["email_address_display_name_2"],
									   $this->properties["email_address_3"],
									   $this->properties["email_address_display_name_3"]);
						
						foreach($props as $property)
						{
							array_push($restrictions, array(RES_CONTENT,
															array(FUZZYLEVEL => FL_SUBSTRING|FL_IGNORECASE,
																ULPROPTAG => $property,
																VALUE => u2w($action["restriction"]["name"]))));
						}

						$restrictions = array(RES_OR, $restrictions);
						$restriction = array(RES_AND, array($restriction, $restrictions));
					}
				}
				
				if(isset($action["sort"]) && isset($action["sort"]["column"])) {
					$this->sort = array();
					
					foreach($action["sort"]["column"] as $column)
					{
						if(isset($column["attributes"]) && isset($column["attributes"]["direction"])) {
							switch(strtolower($column["attributes"]["direction"]))
							{
								case "asc":
									if(isset($this->properties[$column["_content"]])) {
										$this->sort[$this->properties[$column["_content"]]] = TABLE_SORT_ASCEND;
									}
									break;
								case "desc":
									if(isset($this->properties[$column["_content"]])) {
										$this->sort[$this->properties[$column["_content"]]] = TABLE_SORT_DESCEND;
									}
									break;
							}
						}
					}
				}
				
				// We map the 'email_address' column to 'email_address_1' for sorting. This is a bit of a hack
				// because we are showing either email_address_1 or _2 or _3 ...
				
				$map = array();
				$map["email_address"] = $this->properties["email_address_1"];
				
				$this->parseSortOrder($action, $map, true);

				$data = array();
				$data["attributes"] = array("type" => "list");
				$data["column"] = $this->tablecolumns;
				$data["sort"] = $this->generateSortOrder($map);

				// open contacts folder
				$folder = mapi_msgstore_openentry($store, $entryid);
				if(mapi_last_hresult() == NOERROR) {
					$contentsTable = mapi_folder_getcontentstable($folder, MAPI_DEFERRED_ERRORS);

					// apply restriction & sorting
					mapi_table_restrict($contentsTable, $restriction, TBL_BATCH);
					mapi_table_sort($contentsTable, $this->sort, TBL_BATCH);

					/**
					 * Check whether we have to disable the full contactlist listing using the config: 
					 * DISABLE_FULL_CONTACTLIST_THRESHOLD. we are checking here if in case 
					 * mapi_table_getrowcount returns number greater then this config value then we
					 * don't have to do any processing, so it will lessen processing time
					 */
					if(defined('DISABLE_FULL_CONTACTLIST_THRESHOLD') && DISABLE_FULL_CONTACTLIST_THRESHOLD != -1 && !$searchPerformed) {
						if(DISABLE_FULL_CONTACTLIST_THRESHOLD >= 0 && mapi_table_getrowcount($contentsTable) > DISABLE_FULL_CONTACTLIST_THRESHOLD) {
							$disableFullContactlist = true;
						}
					}

					if(!$disableFullContactlist) {
						// Get all rows
						/**
						 * we can also use mapi_table_queryallrows but it internally calls queryrows with 
						 * start as 0 and end as 0x7fffffff, so we are directly calling mapi_table_queryrows
						 */
						$rows = mapi_table_queryrows($contentsTable, $this->properties, 0, 0x7fffffff);

						// Search for distribution lists and get all email adresses in the distribution lists.
						$items = array();
						foreach($rows as $row)
						{
							$item = Conversion::mapMAPI2XML($this->properties, $row);

							if (is_array($item["entryid"])){
								$item["entryid"] = $item["entryid"]["_content"];
							}

							$item["message_flags"] = MSGFLAG_READ; // in the addressbook we don't use message flags, but we want to show them as "read"
							if(isset($item["message_class"])) {
								switch($item["message_class"])
								{
									case "IPM.Contact":
										if(isset($item["address_book_mv"]) && is_array($item["address_book_mv"])){
											$email_addresses = $item["address_book_mv"];
											$entryid = $item["entryid"];

											foreach($email_addresses as $email_address)
											{
												$email_address += 1; // address_book_mv starts with 0, so we add '1' here

												if(isset($item["email_address_" . $email_address])) {
													$item["entryid"] = $entryid . "_" . $email_address;
													$item["email_address"] = $item["email_address_" . $email_address];
													$item["display_name"] = $item["email_address_display_name_" . $email_address];
													$item["addrtype"] = $item["email_address_type_" . $email_address];
													$item["email_address_number"] = $email_address;
													array_push($items, $item);
												}
											}
										}
										break;
									case "IPM.DistList":
										$entryid = hex2bin($item["entryid"]); // need real entryid here
										$item["email_address"] = $item["display_name"];
										$item["members"] = array("member"=>$this->expandDistributionList($store, $entryid));

										array_push($items, $item);
										break;
								}
							}
						}

						/**
						 * Check whether we have to disable the full contactlist listing using the config: 
						 * DISABLE_FULL_CONTACTLIST_THRESHOLD. We check this after we have looped through all the
						 * items (code above) becuase it could happen then after expanding the groups the number
						 * of rows crosses the limit. This is because we need to know how many rows the contactlist
						 * will contain. Also note that when the user conducts a search the list should be displayed 
						 * regardless of the number of items returned.
						 */
						if(defined('DISABLE_FULL_CONTACTLIST_THRESHOLD') && DISABLE_FULL_CONTACTLIST_THRESHOLD != -1 && !$searchPerformed){
							if(DISABLE_FULL_CONTACTLIST_THRESHOLD >= 0 && count($items) > DISABLE_FULL_CONTACTLIST_THRESHOLD){
								$disableFullContactlist = true;
							}
						}
					}

					if(!$disableFullContactlist){
						$data = array_merge($data, array("item" => $items));
					}else{
						// Provide clue that full contactlist is disabled.
						$disable_clist_treshold = array();
						$disable_clist_treshold["value"] = true;
						$data = array_merge($data, array("disable_full_gab"=>$disable_clist_treshold));
					}

					array_push($this->responseData["action"], $data);
					$GLOBALS["bus"]->addData($this->responseData);

					$result = true;
				}
			}

			return $result;
		}
		
		/**
		 * Function which retrieves the list of system users in Zarafa.
		 * @param object $store MAPI Message Store Object
		 * @param array $action the action data, sent by the client
		 * @return boolean true on success or false on failure		 		 
		 */
		function GABUsers($action)
		{
			$name = "";
			if(isset($action["restriction"])) {
				if(isset($action["restriction"]["name"])) {
					$name = $action["restriction"]["name"];
				}
			}

			$items = array();

			$data["page"] = array();
			$data["page"]["start"] = 0;
			$data["page"]["rowcount"] = 0;
			$data["page"]["totalrowcount"] = 0;

			$data = array();
			$data["attributes"] = array("type" => "list");

			$columns = array();
			$this->addColumn($columns, "icon_index", true, 0, _("Icon"), _("Sort on Icon"));
			$this->addColumn($columns, "fileas", true, 1, _("File as"), _("Sort on File As"), "30%");
			$this->addColumn($columns, "display_name", true, 2, _("Display Name"), _("Sort on Display Name"), "35%");
			$this->addColumn($columns, "smtp_address", true, 3, _("Email Address"), _("Sort on Email Address"), PERCENTAGE);
			$this->addColumn($columns, "entryid", false);
			$this->addColumn($columns, "message_class", false);

			$data["column"] = $columns;

			$this->sort = array();
			$this->sort[PR_ACCOUNT] = TABLE_SORT_ASCEND;
			
			$firstSortColumn = array_shift(array_keys($this->sort));

			$map = array();
			$map["fileas"] = PR_ACCOUNT;
			
			// Parse incoming sort order
			$this->parseSortOrder($action, $map, true);
			
			// Generate output sort
			$data["sort"] = array();
			$data["sort"] = $this->generateSortOrder($map);

			if(!DISABLE_FULL_GAB || strlen($name) > 0) {
				
				$ab = $GLOBALS["mapisession"]->getAddressbook();

				if (isset($action["entryid"]) && $action["entryid"] != "") {
					$entryid = hex2bin($action["entryid"]);
				}else{
					$entryid = mapi_ab_getdefaultdir($ab);
				}

				$dir = mapi_ab_openentry($ab,$entryid);
				if (mapi_last_hresult()){
					return false;
				}

				$table = mapi_folder_getcontentstable($dir);

				$restriction = false;
				if(strlen($name) > 0){
					// only return users from who the displayName or the username starts with $name
					// TODO: use PR_ANR for this restriction instead of PR_DISPLAY_NAME and PR_ACCOUNT
					$restriction = 	array(RES_OR, 
										array(
											array(
												RES_CONTENT,
													array(FUZZYLEVEL => FL_SUBSTRING|FL_IGNORECASE,
														ULPROPTAG=>PR_DISPLAY_NAME,
														VALUE=>u2w($name)
													)
												),
											array(
												RES_CONTENT,
													array(FUZZYLEVEL => FL_SUBSTRING|FL_IGNORECASE,
														ULPROPTAG=>PR_ACCOUNT,
														VALUE=>u2w($name)
													)
												),
										),
									);
				}
				if(isset($action["groups"]) && $action["groups"]=="no"){
					// filter out all groups
					$restrictionGroups = array(RES_AND,
										array(
											array(
												RES_PROPERTY,
													array(RELOP => RELOP_NE,
														ULPROPTAG=>PR_OBJECT_TYPE,
														VALUE=>array(
																PR_OBJECT_TYPE=>MAPI_DISTLIST
														)
													)
												),
												// Here comes the name restriction if it is set.
											)
									);
					// When we have a restriction to search on name add it in the no-groups restriction.
					if($restriction){
						$restrictionGroups[1][] = $restriction;
					}
					$restriction = $restrictionGroups;
				}

				// todo: fix paging stuff

				$data["page"]["start"] = 0;
				$data["page"]["rowcount"] = mapi_table_getrowcount($table);
				$data["page"]["totalrowcount"] = $data["page"]["rowcount"];
				
				// Only add restriction when it is used
				mapi_table_restrict($table, ($restriction)?$restriction:Array());
				mapi_table_sort($table, $this->sort);
				
				$rows = mapi_table_queryrows($table, array(PR_ACCOUNT, PR_DISPLAY_NAME, PR_ENTRYID, PR_OBJECT_TYPE, PR_EMAIL_ADDRESS, PR_SMTP_ADDRESS, PR_ADDRTYPE, PR_DISPLAY_TYPE, PR_SEARCH_KEY), 0, 0x7fffffff);
				
                for ($i = 0; $i < count($rows); $i++) {
                    $user_data = $rows[$i];
                    $item = array();
                    $item["entryid"] = bin2hex($user_data[PR_ENTRYID]);
                    $item["display_name"] = w2u($user_data[PR_DISPLAY_NAME]);
                    $item["fileas"] = w2u($user_data[PR_ACCOUNT]);

                    switch($user_data[PR_DISPLAY_TYPE]){
                        case DT_ORGANIZATION:
                            $item["icon_index"] = 0;
                            $item["message_class"] = "IPM.DistList.Organization";
                            $item["email_address"] = w2u($user_data[PR_ACCOUNT]);
                            $item["message_flags"] = 0; // FIXME: we want companies to show "bold", so in fact this is some kind of a hack ;)

                            // FIXME: this must be at the end of the list because of an issue with getElementsByTagName()
                            $item["members"] = array("member"=>$this->expandGroup($user_data[PR_ENTRYID]));
                            break;

                        case DT_DISTLIST:
                            $item["icon_index"] = 514;
                            $item["message_class"] = "IPM.DistList";
                            $item["email_address"] = w2u($user_data[PR_ACCOUNT]);
                            $item["message_flags"] = 0; // FIXME: we want dist lists to show "bold", so in fact this is some kind of a hack ;)

                            // FIXME: this must be at the end of the list because of an issue with getElementsByTagName()
                            $item["members"] = array("member"=>$this->expandGroup($user_data[PR_ENTRYID]));
                            break;

                        case DT_MAILUSER:
                        default:
                            $item["icon_index"] = 512;
                            $item["message_class"] = "IPM.Contact";
                            $item["email_address"] = w2u($user_data[PR_EMAIL_ADDRESS]);
                            $item["smtp_address"] = w2u($user_data[PR_SMTP_ADDRESS]);
                            $item["search_key"] = bin2hex($user_data[PR_SEARCH_KEY]);
                            $item["addrtype"] = w2u($user_data[PR_ADDRTYPE]);
                            $item["message_flags"] = MSGFLAG_READ; // FIXME: setting message_flags to read, to fix the view
                            break;
                    }

                    array_push($items, $item);
                    
                }
            } else {
	           	// Provide clue that full GAB is disabled.
            	$disable_GAB = array();
            	$disable_GAB["value"] = DISABLE_FULL_GAB;
            	$data = array_merge($data, array("disable_full_gab"=>$disable_GAB));
            }
			$data = array_merge($data, array("item"=>$items));

			array_push($this->responseData["action"], $data);
			$GLOBALS["bus"]->addData($this->responseData);
			return true;
		}
		
		function getHierarchy($action)
		{
			$data = array();
			$data["attributes"] = array("type" => "hierarchy");

			$storeslist = false;
			if (isset($action["contacts"])){
				if (isset($action["contacts"]["stores"]["store"]) && !is_array($action["contacts"]["stores"]["store"])){
					$action["contacts"]["stores"]["store"] = array($action["contacts"]["stores"]["store"]);
				}
				if (isset($action["contacts"]["stores"]["folder"]) && !is_array($action["contacts"]["stores"]["folder"])){
					$action["contacts"]["stores"]["folder"] = array($action["contacts"]["stores"]["folder"]);
				}
				$storeslist = $action["contacts"]["stores"];
			}
			
			$folders = $GLOBALS["operations"]->getAddressbookHierarchy($storeslist);

			$data = array_merge($data, array("folder"=>$folders));
			array_push($this->responseData["action"], $data);
			$GLOBALS["bus"]->addData($this->responseData);
			return true;
		}


		function expandGroup($entryid)
		{
			$result = array();
			$group = mapi_ab_openentry($GLOBALS["mapisession"]->getAddressbook(), $entryid);
			$table = mapi_folder_getcontentstable($group);
			$items = mapi_table_queryallrows($table,array(PR_ACCOUNT, PR_ENTRYID, PR_DISPLAY_NAME, PR_ADDRTYPE, PR_SMTP_ADDRESS));
			foreach($items as $item){
				if (isset($item[PR_SMTP_ADDRESS])){
					$result[] = array(
							"entryid"=>array("attributes"=>array("type"=>"binary"),"_content"=>bin2hex($item[PR_ENTRYID])),
							"fileas"=>w2u($item[PR_ACCOUNT]),
							"display_name"=>w2u($item[PR_DISPLAY_NAME]),
							"addrtype"=>w2u($item[PR_ADDRTYPE]),
							"email_address"=>w2u($item[PR_SMTP_ADDRESS]),
						);
				}
			}
			return $result;
		}


		/**
		 * Function which expands a distribution list
		 * @param object $store MAPI Message Store Object
		 * @param string $entryid entryid of the distribution list
		 * @param array $listEntryid used to prevent looping with multiple dist.lists
		 */
		function expandDistributionList($store, $entryid, $listEntryIDs = array())
		{
			if (in_array($entryid, $listEntryIDs)){ // don't expand a distlist that is already expanded
				return array();
			}

			$listEntryIDs[] = $entryid;

			$message = mapi_msgstore_openentry($store, $entryid);
			
			if($message) {
				$props = mapi_getprops($message, array($this->properties["oneoff_members"], $this->properties["members"]));
	
				if(isset($props[$this->properties["oneoff_members"]])) {
					$members = $props[$this->properties["members"]];
		
					// parse oneoff members
					$oneoffmembers = array();
					foreach($props[$this->properties["oneoff_members"]] as $key=>$item){
						$oneoffmembers[$key] = mapi_parseoneoff($item);
					}

					$items = array();

					foreach($members as $key=>$item){
						$parts = unpack("Vnull/A16guid/Ctype/A*entryid", $item);
						
						if ($parts["guid"]==hex2bin("812b1fa4bea310199d6e00dd010f5402")){ // custom e-mail address (no user or contact)
							$oneoff = mapi_parseoneoff($item);
							$item = array();
							$item["fileas"] = w2u($oneoff["name"]);
							$item["display_name"] = $item["fileas"];
							$item["addrtype"] = w2u($oneoff["type"]);
							$item["email_address"] = w2u($oneoff["address"]);
							$items[] = $item;
						}else{
							$item = array();
							switch($parts["type"]){
								case DL_USER: // contact
									$msg = mapi_msgstore_openentry($store, $parts["entryid"]);
									if (mapi_last_hresult()!=NOERROR) // contact could be deleted, skip item
										continue;
									$msgProps = mapi_getprops($msg, $this->properties);
									$item = array();
									$item["entryid"] = bin2hex($parts["entryid"]);
									$item["fileas"] = w2u($msgProps[$this->properties["fileas"]]);
									$item["display_name"] = w2u($msgProps[$this->properties["display_name"]]);

									// use the email adress from the OneOff entry	
									$item["addrtype"] = w2u($oneoffmembers[$key]["type"]);
									$item["email_address"] = w2u($oneoffmembers[$key]["address"]);

									$items[] = $item;		
									break;
								case DL_USER_AB: // user
									$ab = $GLOBALS["mapisession"]->getAddressbook();
									$msg = mapi_ab_openentry($ab,$parts["entryid"]);
									if (mapi_last_hresult()!=NOERROR) // user could be deleted, skip item
										continue;
									$msgProps = mapi_getprops($msg, array(PR_ENTRYID, PR_ACCOUNT, PR_DISPLAY_NAME, PR_SMTP_ADDRESS));
									$item = array();
									$item["entryid"] = bin2hex($parts["entryid"]);
									$item["fileas"] = w2u($msgProps[PR_ACCOUNT]);
									$item["display_name"] = w2u($msgProps[PR_DISPLAY_NAME]);

									// don't use the email adress from the OneOff entry because it will give a ZARAFA mail address, and in the webaccess we currently only support SMTP addresses
									$item["addrtype"] = "SMTP";
									$item["email_address"] = w2u($msgProps[PR_SMTP_ADDRESS]);

									$items[] = $item;
									break;

								case DL_DIST: // dist list
									$items = array_merge($items, $this->expandDistributionList($store, $parts["entryid"], $listEntryIDs));
									break;

								case DL_DIST_AB: // group
									$items = $items + $this->expandGroup($parts["entryid"]);
									break;
							}
						}
					}
					return $items;
				}
			}
			return array();
		}
	}

?>
