CardDav zu Snom CSV

Tobias Bauer, , Lesedauer 5 Minuten.

Auch Snom Telefone der D3XX Reihe haben nur LDAP als zentrale Adressbuchlösung. Mit meinem CardDAV Adressbüchern habe ich hier verloren. Jedoch besitzen auch diese Telefone ein lokales Adressbuch sowie eine entsprechende Importfunktion über eine CSV-Datei.

Mein CardDAV-Adressbuch habe ich aus Thunderbird mit Hilfe von CardBook (welches ich als Adressbuch-Plugin verwende) in eine vcf-Datei exportiert. Um diese nun in das entsprechende Format zu konvertieren habe ich mit PHP eine kleinen Konverterskript geschrieben, und dieses in Gist-Skript zur Verfügung gestellt.

#!/bin/php
<?php

/*****************************************************************
 * VCF 2 Snom CSV Converter written in PHP
 *
 * Generate a CSV File for Snom IP Phones from a VCF (VCard) File
 * e. g. gernerated from Thunderbirds CardBook Plugin.
 *
 * Ussage: ./vcf2-snom <vcf-file>
 *
 * Generates a tbook.csv file. Change the variables in the top 
 * section to your own requirements. VIP, Groups etc. are controlled
 * by the VCF Groups.
 *
 * Attention! This scrips need the VCard Library from Jeroen 
 * Desloovere which can be found here:
 *  https://github.com/jeroendesloovere/vcard
 * 
 * */

// import VCardParser
require_once __DIR__ . '/src/VCardParser.php';

// init Vars
$csv_out_file = 'tbook.csv';
$counter_ok = 0;            // Counter Lines OK
$counter_nok = 0;           // Counter Linex not OK
// Define the groups which are VIP
$vip_groups = array('Familie');
// Define the gruops which are in Deny
$deny_groups = array();
// Group of Favourites
$fav_groups = array('Familie', 'Frequent');
// Contactgrups
$con_family = array('Familie');
$con_work = array('Arbeit', 'Lieferanten');
$con_friends = array('Freunde');

// no Parameter set
if(!isset($argv[1])) {
    echo('Need file as Parameter!'."\n");
    echo('   Usage: '.$argv[0].' <vCard-file>');
    echo PHP_EOL;
    exit(1);
} elseif(!file_exists($argv[1])) {
    echo('The given file does not exist!');
    echo PHP_EOL;
    exit(1);
} elseif(!is_file($argv[1]) || !is_readable($argv[1])) {
    echo('Given filename is not a file or not readable!');
    echo PHP_EOL;
    exit(1);
}

// create Parser
use JeroenDesloovere\VCard\VCardParser;
$parser = VCardParser::parseFromFile($argv[1]);
// open CSV file for write
$fp = fopen($csv_out_file, 'w');
// add BOM to fix UTF-8
fputs($fp, $bom =( chr(0xEF) . chr(0xBB) . chr(0xBF) ));

// run throu file
foreach($parser as $vcard) {
    // CSV Array
    $csv = array("nickname" => "", 
             "phonenumber" => "",
             "type" => "none",
             "active" => "",
             "firstname" => "",
             "lastname" => "",
             "title" => "",
             "organization" => "",
             "email" => "",
             "note" => "",
             "birthday" => "",
             "group" => "",
             "favourite" => "false",
             "filler" => "",
             "phone_type" => "sip",
         );
    // fill fields
    $csv['lastname'] = $vcard->lastname;
    $csv['firstname'] = $vcard->firstname;
    // Organisation
    if(isset($vcard->organization)) {
        $csv['organization'] = $vcard->organization;
        if(empty($csv['firstname']) && empty($csv['lastname']))
            $csv['nickname'] = $csv['organization'];
    }
    // E-Mail
    if(isset($vcard->email)) {
        if(array_key_exists('HOME', $vcard->email)) {
            $csv['email'] = $vcard->email['HOME'][0];
        } elseif(array_key_exists('WORK', $vcard->email)) {
            $csv['email'] = $vcard->email['WORK'][0];
        }
    }
    // Note -> not used because of multiline
    //if(isset($vcard->note)) $csv['note'] = $vcard->note;
    // Birthday
    if(isset($vcard->birthday)) $csv['birthday'] = $vcard->birthday->format('Y-m-d');
    // Groupfunctions
    if(count(array_intersect_assoc($vip_groups, $vcard->categories)) > 0) {
        $csv['type'] = 'vip';
    }
    if(count(array_intersect_assoc($deny_groups, $vcard->categories)) > 0) {
        $csv['type'] = 'deny';
    }
    // Contact Groups
    if(count(array_intersect_assoc($con_work, $vcard->categories)) > 0) {
        $csv['group'] = 'work';
    }
    if(count(array_intersect_assoc($con_friends, $vcard->categories)) > 0) {
        $csv['group'] = 'friends';
    }
    if(count(array_intersect_assoc($con_family, $vcard->categories)) > 0) {
        $csv['group'] = 'family';
    }
    // Favouriten
    if(count(array_intersect_assoc($fav_groups, $vcard->categories)) > 0) {
        $csv['favourite'] = 'true';
    }

    // No phone number
    if(!isset($vcard->phone) || count($vcard->phone) == 0) {
        continue;
    // only one phone number
    } elseif(count($vcard->phone) == 1) {
        $csv['phonenumber'] = preg_replace("/[^0-9\+]/", "", array_values($vcard->phone)[0][0]);
        $csv['phone_type'] = getPhoneType(array_keys($vcard->phone)[0]);
        // write line
        $ret = fputs($fp, implode(',', array_map("encodeFunc", $csv))."\r\n");
        if($ret !== false) {
            $counter_ok++;
        } else {
            $counter_nok++;
        }
    // multiple phone numbers
    } else {
        // write Master Line
        $master_csv = $csv;
        $master_phone_number = preg_replace("/[^0-9\+]/", "", array_values($vcard->phone)[0][0]);
        $master_csv['type'] = 'MASTER';
        $master_csv['phonenumber'] = $master_phone_number;
        $master_csv['phone_type'] = getPhoneType(array_keys($vcard->phone)[0]);
        $ret = fputs($fp, implode(',', array_map("encodeFunc", $master_csv))."\r\n");

        // change Name in child records
        $csv['lastname'] = $master_phone_number;
        $csv['firstname'] = 'Member_Alias';

        // all phone numbers
        foreach($vcard->phone as $key=>$value) {
            // ignore Fax numbers
            if(stristr($key, 'FAX')) continue;
            // take values
            $csv['phonenumber'] = preg_replace("/[^0-9\+]/", "", $value[0]);
            $csv['phone_type'] = getPhoneType($key);
            // write lines
            $ret = fputs($fp, implode(',', array_map("encodeFunc", $csv))."\r\n");
            if($ret !== false) {
                $counter_ok++;
            } else {
                $counter_nok++;
            }
        }

        // delete vars
        unset($master_csv);
        unset($master_phone_number);
    }
}

// Close file
fclose($fp);

// Output Summery
echo('Ready!!!'."\n");
echo('   Write '.$counter_ok.' Lines'."\n");
echo('   '.$counter_nok.' Lines with error!');  
echo PHP_EOL;

exit(0);


/***
 * @param $value array
 * @return string array values enclosed in quotes every time.
 */
function encodeFunc($value) {
    //remove any ESCAPED double quotes within string.
    $value = str_replace('\\"','"',$value);
    //then force escape these same double quotes And Any UNESCAPED Ones.
    $value = str_replace('"','\"',$value);
    //force wrap value in quotes and return
     return '"'.$value.'"';
}


/***
 * @param $value string
 * @return string value of phone type
 */
function getPhoneType($value) {
    // remove PREF
    $value = str_replace('PREF;', '', strtoupper($value));
    switch($value) {
    case 'WORK':
        $value = 'business';
        break;
    case 'HOME':
        $value = 'fixed';
        break;
    case 'CELL':
        $value = 'mobile';
        break;
    default:
        $value = 'sip';
        break;
    }
    return $value;
}

?>

Es benötigt die VCard Librare von Jeroen Desloovere, welche kostenlos aus GitHub heruntergeladen werden kann. Wirklich relevant ist das „src“ Verzeichnis mit den drei darin enthaltenen PHP-Dateien.

Der Konverter ist so aufgebaut, dass die entsprechenden Personen über die Gruppen die entsprechenden Eigenschaften erhalten. Diese können einfach an die eigenen Bedürfnisse angepasst werden.

Aufgerufen wird das Skript einfach mit folgendem Aufruf:

$ vcf2Snom Kontakte.vcf

Hierbei muss man natürlich den korrekten Namen der vcf-Datei einfügen. Erstellt wird dann eine „tbook.csv“, welche einfach in das Snom-Telefon importiert werden kann. Natürlich wird berücksichtigt, dass eine Person mehrere Nummern haben kann und diese entsprechend als Untergeordnete Nummer zugeteilt.