E-Mail Sprachmailbox mit RTPR-Server und Cisco IP Phone

Ich erhalte von meinem Servicerufnummeranbieter E-Mails mit Sprachnachrichten meiner Sprachmailbox, die bei Abwesenheit für mich hinterlassen wurden. In den E-Mails befinden sich .wav Dateien mit den hinterlassenen Nachrichten. Wäre dies eine normale Sprachmailbox, könnte ich die Rufnummer einfach in meinem Cisco Telefon hinterlegen und die Sprachnachrichten per Telefonanruf abhören. Die .wav Dateien mit den Sprachaufzeichnungen aus den E-Mails lassen sich nicht so einfach über das Telefon abhören.

Auslesen des E-Mail Postfachs

Mit dieser PHP Funktion lese ich alle E-Mails in meinem Postfach, die einen bestimmten Betreff „$subject“ besitzen.

function imapMailList($user, $pass, $server, $subject){

	$mailbox = imap_open($server,$user,$pass,NULL, 1, 
			array('DISABLE_AUTHENTICATOR' => 'PLAIN'))or 
		           die(var_dump(imap_errors())); 

	$arr = imap_search($mailbox, $subject, FT_UID); 

	$data = imap_fetch_overview($mailbox, implode(",",$arr), FT_UID);

	for($i=0; $i <count($data); $i++){
		
		$data[$i]->number = getNumber($data[$i]->subject);
	}

	imap_close($mailbox);

	return $data;
}

Anmerkungen:

Der Username „$user“ und das Passwort „$pass“ sind die Postfachzugangsdaten, die auch für eine Einrichtung in einem E-Mail Programm verwendet werden. Der Servername wird wie folge erstellt.

$server = {ihre.server.adresse:993/imap/ssl}INBOX

Ich ermittle hier noch zusätzlich die Telefonnummer „$data[]->number“ über eine Parsingfunkion. Da die E-Mails nicht unbedingt bei jedem identisch aussehen, sollte hier jeder seine eigene Parsingfunktion erstellen. Die Ermittlung der Telefonnummer ist nicht zwingend notwendig, ermöglicht mir aber einen „Redail“ Button anzuzeigen, um den Anrufer direkt nach Abhören der Nachricht zurückzurufen.

Anzeige der E-Mails in einer Liste für die Sprachmailbox

Ich verwende für die Anzeige der Informationen die Templateengine Smarty. Dabei setze ich noch vor der Anzeige meiner Daten über das Template im PHP-Script den Header „header(„Content-type: text/xml“);“

Eine ausführliche Dokumentation der XML Dateien stellt Cisco im Internet bereit Cisco

<?xml version="1.0" encoding="utf-8"?>
<CiscoIPPhoneIconFileMenu>
   <Title>E-Mail Messages</Title>
   <Prompt></Prompt>
{foreach $data as $item}
   <MenuItem>
	<IconIndex>{if $item->seen}0{else}1{/if}</IconIndex>
     <Name>From {$item->number}</Name>
     <URL>http://ihr.raspberry.server/messages.php?a=messages_detail&amp;b={$item->uid}</URL>
   </MenuItem>
{/foreach}
   <IconItem>
     <Index>0</Index>
     <URL>http://ihr.raspberry.server/PNG/letter_open.png</URL>
   </IconItem>
  <IconItem>
     <Index>1</Index>
     <URL>http://ihr.raspberry.server/PNG/letter.png</URL>
   </IconItem>
</CiscoIPPhoneIconFileMenu>

Anmerkungen:

Für gelesene und ungelesene Nachrichten blende ich hier unterschiedliche Symbole ein. Als Überschrift verwende ich die vorher ermittelte Telefonnummer.

Cisco Phone messages Sprachmailbox
Cisco Phone messages Sprachmailbox

Anzeigen einer E-Mail und .wav Datei aus Anhang speichern

Über den „Select“ Button öffnet sich ein neues Fenster auf dem Cisco Phone, das die E-Mail Details anzeigt. Im Hintergrund wird auf dem Raspberry Pi der Anhang der E-Mail gelesen und temporär auf als Datei auf dem Raspberry Pi abgelegt.

function imapMailContent($user, $pass, $server, $messageID){

	$mailbox = imap_open($server,$user,$pass,NULL, 1, 
			array('DISABLE_AUTHENTICATOR' => 'PLAIN'))or 
		           die(var_dump(imap_errors())); 

	$data = imap_fetch_overview($mailbox, $messageID, FT_UID); 

	imapMailAttachment($mailbox, $messageID);

	imap_close($mailbox);

	$data[0]->number = getNumber($data[0]->subject);

	return $data;
}

Anmerkungen:

Die „$massageID“ erhalte ich aus den Übergabeparametern, die ich beim Klicken auf den „Select“ Button über die aufgerufene URL mitgebe.

Den Anhang lese ich über die Funktion „imapMailAttachment“. Wenn ich einen Anhang ermitteln kann, werden im temporären Ordner „temp/“ Alle Dateien mit die mit „tmes“ beginnen gelöscht. Ich benötige für mein Telefon immer nur die aktuelle .wav Datei, daher leere ich das Verzeichnis komplett.

Danach wird der Anhang ausgelesen und in das Verzeichnis „/tmes“ auf dem Raspberry Pi Webserver kopiert. Um die .wav Datei wieder zu erkennen, benenne ich die Datei nach der E-Mail ID.

function imapMailAttachment($mailbox, $messageID) {

    $actMail = imap_fetchstructure($mailbox, $messageID, FT_UID);
    $PartsInActMail = count($actMail->parts);

    if ($PartsInActMail >= 2) 
    {
	for ($p=1; $p<$PartsInActMail; $p++)
	{

		$files = glob('temp/tmes*'); 
		foreach($files as $file){ 
			if(is_file($file))
			unlink($file); 
		}

		$inhalt = base64_decode(imap_fetchbody($mailbox, $messageID, $p+1, FT_UID));
		$fp = fopen("temp/tmes".$messageID.".wav", "w+"); 
		$files [$messageID] = "tmes".$messageID.".wav";
		fwrite ($fp, $inhalt) or die ("Error!"); 
		fclose ($fp);
	}
    }

}

Den Inhalt der E-Mail zeige ich erneut über ein Template an. Zuvor wird wieder der Header „header(„Content-type: text/xml“);“ gesetzt. Für das Abspielen der .wav Datei auf dem Cisco Telefon erstelle ich hier die Buttons „Play“ und „Stop“. Diese senden über „Notify“ nur den Status, dass abgespielt oder gestoppt werden soll an den Raspberry Pi Server.

<?xml version="1.0" encoding="utf-8"?>
<CiscoIPPhoneText>
   <Title>From {$data[0]->number}</Title>
   <Prompt></Prompt>
   <Text>{$data[0]->subject}&#13;&#13;{$data[0]->date|replace:" +0100":""} </Text>
 <SoftKeyItem>
   <Name>Update</Name>
   <URL>SoftKey:Update</URL>
   <Position>1</Position>
 </SoftKeyItem>
<SoftKeyItem>
    <Name>Play</Name>
   <URL>Notify:http:ihr.raspberry.server:80:messages.php?p={$data[0]->uid}</URL>
   <Position>2</Position>
 </SoftKeyItem>
<SoftKeyItem>
    <Name>Exit</Name>
   <URL>SoftKey:Exit</URL>
   <Position>3</Position>
 </SoftKeyItem>
<SoftKeyItem>
    <Name>Redail</Name>
   <URL>Dial:{$data[0]->number}</URL>
   <Position>4</Position>
 </SoftKeyItem>
<SoftKeyItem>
    <Name>Stop</Name>
   <URL>Notify:ihr.raspberry.server:80:messages.php?s={$data[0]->uid}</URL>
   <Position>5</Position>
 </SoftKeyItem>
</CiscoIPPhoneText>

Anmerkungen:

Es wird absichtlich nur ein „Notify“ an den Server gesendet, da hier keine weitere Oberfläche generiert werden muss. Es soll lediglich der Stream zum Telefon gestartet oder gestoppt werden. Der „Notify“ löst im nächsten Schritt den Stream vom Raspberry Pi Server zum Cisco-Telefon aus.

Cisco Phone messages detail  Sprachmailbox
Cisco Phone messages detail Sprachmailbox

Die Cisco Funktion „Play“, die für das Abspielen von Audiodateien standardmäßig verwendet werden kann, ist in der Abspiellänge begrenzt, sodass ich mich für längere Sprachnachrichten für den Stream entschieden habe.

„Play“ oder „Stop“ des Audio Streams RTPR für die Sprachmailbox

Beim Klicken des „Play“ oder „Stop“ Buttons werden auf dem Raspberry Pi zwei Schritte ausgeführt. Der Stream muss gestartet werden und das Cisco-Telefon muss per Push informiert werden, dass es den Stream empfangen soll. Zur Vereinheitlichung habe ich die XML Push Nachrichten über eine zentrale PHP-Funktion gesteuert und benötige nur die Telefon IP Adresse „$phoneIP“, den Benutzernamen „$phoneUser“ und das Passwort „$phonePass“, sowie die Daten „$data“, die gesendet werden sollen.

function pushXMLToCisco( $phoneIP, $phoneUser, $phonePass, $data) {

	header("Content-type: text/xml");

	$auth = base64_encode($phoneUser.":".$phonePass);

	$xml = '<CiscoIPPhoneExecute><ExecuteItem URL="'.$data.'"/></CiscoIPPhoneExecute>';	
	$xml = 'XML='.urlencode($xml);

	$post = "POST /CGI/Execute HTTP/1.0\r\n";
	$post .= "Host: ".$phoneIP."\r\n";
	$post .= "Authorization: Basic $auth\r\n";
	$post .= "Connection: close\r\n";
	$post .= "Content-Type: application/x-www-form-urlencoded\r\n";
	$post .= "Content-Length: ".strlen($xml)."\r\n\r\n";

	$fp = fsockopen ( $phoneIP, 80, $errno, $errstr, 30);

	if(!$fp){ 
		echo "$errstr ($errno)<br>\nIP: $phoneIP<br>\n"; 
	} else {
		fputs($fp, $post.$xml);
		flush();
		$response= "";
		while (!feof($fp)) {

			$response .= fgets($fp, 128);
			flush();
		}
	}
}

Die Steuerung für RTPR „Play“

Zuerst schließe ich die „Notify“ Verbindung vom Telefon, um die weiteren Aufgaben abzuhandeln. Dann sende ich per XML Push den Befehl „RTPRx:“.$serverIP.“:20482:10″ zum Öffnen des Ports für den Stream auf das Telefon. Danach starte ich, wenn die .wav Datei vorhanden ist, den ffmpeg Stream. Falls weitere ffmpeg Streams vorhanden sind, werden diese geschlossen. Ist der Stream abgeschlossen, wird auch auf dem Telefon per XML Push die Übertragung beendet.

if(isset($_GET['p']) {

	ignore_user_abort(true);
	set_time_limit(0);

	ob_start();

	header('Connection: close', true);
	header('Content-Length: 0',true);
	
	flush();
	ob_flush();

	$data = "RTPRx:".$serverIP.":20482:10";
	pushXMLToCisco($phoneIP, $phoneUser, $phonePass, $data);


	if(is_file("temp/tmes".$_GET['p'].".wav")){

		shell_exec('pkill -f ffmpeg');

		shell_exec("ffmpeg -re -i /xxx/xxx/xxx/temp/tmes".$_GET['p'].".wav -filter_complex 'aresample=8000,asetnsamples=n=160' -f rtp rtp://".$phoneIP.":20482");

	}

	$data = "RTPRx:Stop";
	pushXMLToCisco($phoneIP, $phoneUser, $phonePass, $data);
}

Die Steuerung für RTPR „Stop“

Über den „Stop“ Button ist es möglich, den Stream der .wav Datei jederzeit abzubrechen. Hierzu wird auf dem Raspberry Pi Server der ffmpeg Stream abgebrochen und eine XML Push Nachricht an das Cisco Telefon gesendet, um die Übertragung auch dort zu beenden.

if(isset($_GET['s'])) {

	ignore_user_abort(true);
	set_time_limit(0);

	ob_start();

	header('Connection: close', true);
	header('Content-Length: 0',true);
	
	flush();
	ob_flush();

	shell_exec('pkill -f ffmpeg');

	$data = "RTPRx:Stop";
	pushXMLToCisco($phoneIP, $phoneUser, $phonePass, $data);

}