diff -p -N -U 4 -r horde-2.1/lib/Data.php horde-2.1UM/lib/Data.php
--- horde-2.1/lib/Data.php	Fri Apr 19 11:22:07 2002
+++ horde-2.1UM/lib/Data.php	Mon Apr 22 15:27:58 2002
@@ -20,11 +20,21 @@ define('IMPORT_ICALENDAR', 5);
 
 /** @constant IMPORT_VCARD Import vCards. */
 define('IMPORT_VCARD', 6);
 
+/** @constant IMPORT_TSV Import generic tsv data. */
+define('IMPORT_TSV', 7);
+
+/** @constant IMPORT_MULBERRY Import Mulberry addressbook data */
+define('IMPORT_MULBERRY', 8);
+
+/** @constant IMPORT_PINE Import Pine addressbook data. */
+define('IMPORT_PINE', 9);
+
 define('EXPORT_CSV', 100);
 define('EXPORT_ICALENDAR', 101);
 define('EXPORT_VCARD', 102);
+define('EXPORT_TSV', 103);
 
 /**
  * Abstract class to allow data exchange between the Horde applications and
  * external sources
@@ -338,8 +348,102 @@ class Data_rfc2425 extends Data {
             $date_arr['sec'] = $times[2];
         }
 
         return $date_arr;
+    }
+}
+
+class Data_tsv extends Data {
+
+    function importFile($filename, $header=false, $delimiter="\t")
+    {
+        $fp = fopen($filename, 'r');
+        if (!$fp) return false;
+
+		$contents = preg_split( "/(\n|\r|\r\n)/",
+			rtrim(fread($fp, filesize($filename))));
+
+		fclose($fp);
+
+        $data = array();
+
+		if ($header) 
+			$head = explode($delimiter, array_shift($contents));
+
+        foreach ($contents as $line) {
+			$line = explode($delimiter, $line);
+            if (!isset($head)) $data[] = $line;
+            else {
+                $newline = array();
+                for ($i=0; $i<count($head); $i++) {
+                    //if (!empty($line[$i]))
+                    $newline[$head[$i]] = empty($line[$i]) ? '' : $line[$i];
+                }
+                $data[] = $newline;
+            }
+        }
+
+        return $data;
+    }
+
+    function import()
+	{
+    }
+
+    function export()
+    {
+    }
+
+    function exportFile($filename, $data, $header=false, $delimiter="\t")
+    {
+        if (!is_array($data) || count($data) == 0) {
+            return;
+        }
+
+        include_once HORDE_BASE . '/lib/Browser.php';
+        $browser = new Browser();
+
+        $export = '';
+
+        if ($header) {
+            $head = current($data);
+            while (list($key,) = each($head)) {
+                if (!empty($key)) {
+                    $export .= '"' . addslashes($key) . '"';
+                }
+                $export .= "\t";
+            }
+            $export = substr($export, 0, -1) . "\n";
+        }
+
+        foreach ($data as $row) {
+            foreach ($row as $cell) {
+                if (!empty($cell) || $cell === 0) {
+                    $export .= '"' . addslashes($cell) . '"';
+                }
+                $export .= "\t";
+            }
+            $export = substr($export, 0, -1) . "\n";
+        }
+
+        header('Content-Length: ' . strlen($export));
+        header('Content-Type: application/csv');
+
+        /* This should force a save file dialog.  According to a note
+         * in MSDN, the suggested filename should NOT be in quotes.
+         *
+         * For IE 5.5, we break the header in a special way that makes
+         * things work. I don't really want to know. */
+        if ($browser->hasQuirk('break_disposition_header')) {
+            header('Content-Disposition: filename=' . $filename);
+        } else {
+            header('Content-Disposition: attachment; filename=' . $filename);
+        }
+
+        echo $export;
+    }
+    function mapDate()
+    {
     }
 
 }
 ?>
diff -p -N -U 4 -r horde-2.1/turba/data.php horde-2.1UM/turba/data.php
--- horde-2.1/turba/data.php	Fri Apr 19 11:22:13 2002
+++ horde-2.1UM/turba/data.php	Mon Apr 22 18:40:28 2002
@@ -134,8 +134,11 @@ if ($actionID == HORDE_IMPORT) {
                 switch ($import_type) {
                 case IMPORT_CSV:
                     $type = "CSV";
                     break;
+                case IMPORT_TSV:
+                    $type = "TSV";
+                    break;
                 case IMPORT_OUTLOOK:
                     $type = "Outlook";
                     break;
                 }
@@ -188,8 +191,37 @@ if ($actionID == HORDE_IMPORT) {
                     }
                 }
                 
                 break;
+
+            case IMPORT_TSV:
+                $tsv = new Data_tsv();
+                if (!isset($header)) {
+                    $header = Horde::getFormData('header');
+                }
+                $importData = $tsv->importFile($HTTP_POST_FILES['import_file']['tmp_name'], $header, "\t");
+
+                if (!isset($importData) || !is_array($importData)) {
+                    Horde::raiseMessage(_("There was an error importing the uploaded file"), HORDE_ERROR);
+                    $error = true;
+                } else {
+                    $dest = Horde::getFormData('dest');
+                    include_once HORDE_BASE . '/lib/SessionCache.php';
+                    $cache = new SessionCache();
+                    $cacheID = $cache->putObject(array($importData, $dest));
+                    $actionUrl = Horde::applicationUrl('data.php');
+
+                    $appFields = array();
+                    reset($cfgSources[$dest]['map']);
+                    while (list($key, ) = each($cfgSources[$dest]['map'])) {
+                        if (substr($key, 0, 2) != '__') {
+                            $appFields[$key] = $attributes[$key]['desc'];
+                        }
+                    }
+                }
+
+                break;
+
             
             case IMPORT_VCARD:
                 $vcf = new Data_rfc2425();
                 $data = $vcf->importFile($HTTP_POST_FILES['import_file']['tmp_name']);
@@ -279,8 +311,108 @@ if ($actionID == HORDE_IMPORT) {
                     Horde::raiseMessage(sprintf(_("%s file successfully imported"), 'vCard'), HORDE_SUCCESS);
                 }
                 
                 break;
+
+					case IMPORT_MULBERRY:
+						$mulberry = new Data_tsv();
+						$importData = $mulberry->importFile($HTTP_POST_FILES['import_file']['tmp_name']);
+						$dest = Horde::getFormData('dest');
+						$driver = &Turba_Source::singleton($dest, $cfgSources[$dest]);
+						if (!isset($importData) || !is_array($importData)) {
+							Horde::raiseMessage(_("There was an error importing the uploaded file"), HORDE_ERROR);
+							$error = true;
+						} elseif (PEAR::isError($driver)) {
+							Horde::raiseMessage(_("Failed to connect to the specified directory."), HORDE_ERROR);
+							$error = true;
+						} else {
+							include_once HORDE_BASE . '/lib/SessionCache.php';
+							$cache = new SessionCache();
+							$cacheID = $cache->putObject(array($importData, $dest));
+							if (!isset($cacheID) || !(list($importData, $dest) = $cache->getObject($cacheID))) {
+								Horde::raiseMessage(_("The data got lost."), HORDE_ERROR);
+								$error = true;
+							} else {
+								$import_type = Horde::getFormData('import_type');
+								$appKeys = array('alias', 'name', 'email', 'company', 'workAddress', 'workPhone', 'homePhone', 'fax', 'urls', 'notes');
+								$dataKeys = array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
+								$map = array();
+								foreach ($appKeys as $key => $app) {
+									$map[$app] = $dataKeys[$key];
+								}
+								foreach ($importData as $row) {
+									$hash = array();
+									reset($cfgSources[$dest]['map']);
+									while (list($key, ) = each($cfgSources[$dest]['map'])) {
+										if (isset($map[$key]) && !empty($row[$map[$key]])) {
+											$hash[$key] = $row[$map[$key]];
+										}
+									}
+									$hash['__owner'] = Auth::getAuth();
+									if (!$driver->addObject($hash)) {
+										$error = true;
+									}
+								}
+							}
+						}
+
+						if ($error) {
+							Horde::raiseMessage(_("There was an error importing the data."), HORDE_ERROR);
+						} else {
+							Horde::raiseMessage(_("Mulberry addressbook sucessfully imported"), HORDE_SUCCESS);
+						}
+
+					break;
+
+					case IMPORT_PINE:
+						$pine = new Data_tsv();
+						$importData = $pine->importFile($HTTP_POST_FILES['import_file']['tmp_name']);
+						$dest = Horde::getFormData('dest');
+						$driver = &Turba_Source::singleton($dest, $cfgSources[$dest]);
+						if (!isset($importData) || !is_array($importData)) {
+							Horde::raiseMessage(_("There was an error importing the uploaded file"), HORDE_ERROR);
+							$error = true;
+						} elseif (PEAR::isError($driver)) {
+							Horde::raiseMessage(_("Failed to connect to the specified directory."), HORDE_ERROR);
+							$error = true;
+						} else {
+							include_once HORDE_BASE . '/lib/SessionCache.php';
+							$cache = new SessionCache();
+							$cacheID = $cache->putObject(array($importData, $dest));
+							if (!isset($cacheID) || !(list($importData, $dest) = $cache->getObject($cacheID))) {
+								Horde::raiseMessage(_("The data got lost."), HORDE_ERROR);
+								$error = true;
+							} else {
+								$import_type = Horde::getFormData('import_type');
+								$appKeys = array('alias', 'name', 'email', 'notes');
+								$dataKeys = array(0, 1, 2, 4);
+								$map = array();
+								foreach ($appKeys as $key => $app) {
+									$map[$app] = $dataKeys[$key];
+								}
+								foreach ($importData as $row) {
+									$hash = array();
+									reset($cfgSources[$dest]['map']);
+									while (list($key, ) = each($cfgSources[$dest]['map'])) {
+										if (isset($map[$key]) && !empty($row[$map[$key]])) {
+											$hash[$key] = $row[$map[$key]];
+										}
+									}
+									$hash['__owner'] = Auth::getAuth();
+									if (!$driver->addObject($hash)) {
+										$error = true;
+									}
+								}
+							}
+						}
+
+						if ($error) {
+							Horde::raiseMessage(_("There was an error importing the data."), HORDE_ERROR);
+						} else {
+							Horde::raiseMessage(_("Pine addressbook sucessfully imported"), HORDE_SUCCESS);
+						}
+
+					break;
                 
             }
             
         } else {
@@ -327,10 +459,47 @@ if ($actionID == HORDE_IMPORT) {
                 exit;
             }
         }
         break;
+
+
+    case EXPORT_TSV:
+        $source = Horde::getFormData('source');
+        if (!isset($source) && isset($cfgSources) && is_array($cfgSources) && count($cfgSources) > 0) {
+            reset($cfgSources);
+            $source = key($cfgSources);
+        }
+        
+        $driver = &Turba_Source::singleton($source, $cfgSources[$source]);
+        if (PEAR::isError($driver)) {
+            Horde::raiseMessage(_("Failed to connect to the specified directory."), HORDE_ERROR);
+            $error = true;
+        } else {
+            if (get_class($addresses = $driver->search(array())) != 'turba_list') {
+                Horde::raiseMessage(sprintf(_("Failed to search the directory: %s"), $driver->error()), HORDE_ERROR);
+                $error = true;
+            } else {
+                $data = array();
+                while ($ob = $addresses->next()) {
+                    $tmp = $ob->getAttributes();
+                    $row = array();
+                    foreach ($tmp as $key => $attribute) {
+                        if (substr($key, 0, 2) != '__') {
+                            $row[$key] = $attribute;
+                        }
+                    }
+                    $data[] = $row;
+                }
+                
+                $tsv = new Data_tsv();
+                $tsv->exportFile('turba.tsv', $data, true);
+                exit;
+            }
+        }
+        break;
         
     }
+        
 }
     
 $title = _("Import/Export Addressbooks");
 $js_onLoad = null;
@@ -367,11 +536,12 @@ if ($actionID == NO_ACTION || $error) {
     switch ($importID) {
     
     case IMPORT_OUTLOOK:
     case IMPORT_CSV:
+    case IMPORT_TSV:
         include $registry->getParam('templates', 'horde') . '/data/csvmap.inc';
         break;
-    
+
     case IMPORT_MAPPED:
         if (count($dates) > 0) {
             $actionUrl = Horde::applicationUrl('data.php');
             include $registry->getParam('templates', 'horde') . '/data/datemap.inc';
diff -p -N -U 4 -r horde-2.1/turba/templates/data/export.inc horde-2.1UM/turba/templates/data/export.inc
--- horde-2.1/turba/templates/data/export.inc	Fri Apr 19 11:22:18 2002
+++ horde-2.1UM/turba/templates/data/export.inc	Fri Apr 19 11:49:14 2002
@@ -13,8 +13,9 @@
     <table border="0"><tr><td>
         <?= _("Select the format to export to:") ?><br />
         <select name="exportID">
             <option value="<?= EXPORT_CSV ?>">CSV</option>
+            <option value="<?= EXPORT_TSV ?>">TSV</option>
             <!-- not (yet) implemented -->
             <!--<option value="<?= EXPORT_VCARD ?>">vCard</option>-->
         </select><br /><br />
         <?= _("Select the source to export from:") ?><br />
diff -p -N -U 4 -r horde-2.1/turba/templates/data/import.inc horde-2.1UM/turba/templates/data/import.inc
--- horde-2.1/turba/templates/data/import.inc	Fri Apr 19 11:22:18 2002
+++ horde-2.1UM/turba/templates/data/import.inc	Mon Apr 22 10:56:51 2002
@@ -14,10 +14,13 @@
     <table border="0"><tr><td>
         <?= _("Select the format to import from:") ?><br />
         <select name="importID" onchange="if (this.value == '<?= IMPORT_OUTLOOK ?>') document.import_form.header.checked=true;">
             <option value="<?= IMPORT_CSV ?>">CSV</option>
+            <option value="<?= IMPORT_TSV ?>">TSV</option>
             <option value="<?= IMPORT_OUTLOOK ?>">Outlook</option>
             <option value="<?= IMPORT_VCARD ?>">vCard</option>
+			<option value="<?= IMPORT_MULBERRY ?>">Mulberry Addressbook</option>
+			<option value="<?= IMPORT_PINE ?>">Pine Addressbook</option>
         </select><br />
         <input type="checkbox" name="header" value="1" />
         <?= _("If importing CSV data: does the first row contain the field names?") ?><br /><br />
         <?= _("Select the destination to import to:") ?><br />
