MS Exchange - Contacts lookup
Hi,
I've been playing around with few bits of code and have managed to mash them together so I can access MS exchange contacts (mine) with a push of a button. The main file that does the talking to Exchange relies on "Exchange Server name", "Username" and "Password" being stored in the file. Can anyone suggests of a way of encrypting the information and stored some where other then at the head of the php page?
<?
#####################################################################
# Asterisk Directory for Exchange
#####################################################################
// Modify the paths to these class files as needed.
require_once("class_http.php");
require_once("class_xml.php");
// Change these values for your Exchange Server.
$exchange_server = "http://xxxx";
$exchange_username = "xxxx";
$exchange_password = "xxxx";
// We use Troy's http class object to send the XML-formatted WebDAV request
// to the Exchange Server and to receive the response from the Exchange Server.
// The response is also XML-formatted.
$h = new http();
$h->headers["Content-Type"] = 'text/xml;charset="UTF-8"';
$h->headers["Depth"] = "0";
$h->headers["Translate"] = "f";
// Find all the contacts for a specific company in a specific folder.
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/e2k3/e2...
$h->xmlrequest = '<?xml version="1.0"?>';
$h->xmlrequest .= <<
SELECT "a:href"
,"urn:schemas:contacts:o"
,"urn:schemas:contacts:cn"
,"urn:schemas:contacts:fileas"
,"urn:schemas:contacts:title"
,"urn:schemas:contacts:email1"
,"urn:schemas:contacts:telephoneNumber"
FROM "$exchange_server/exchange/xxxx/contacts/"
ORDER BY "urn:schemas:contacts:cn"
END;
// IMPORTANT -- The END line above must be completely left-aligned. No white-space.
// The 'fetch'method does the work of sending and receiving the request.
// NOTICE the last parameter passed--'SEARCH'in this example. That is the
// HTTP verb that you must correctly set according to the type of WebDAV request
// you are making. The examples on this page use either 'PROPFIND'or 'SEARCH'.
if (!$h->fetch($exchange_server."/exchange/xxxx/contacts/", 0, null, $exchange_username, $exchange_password, "SEARCH")) {
echo "There is a problem with the http request!";
echo $h->log;
exit();
}
// The assumption now is that we've got an XML result back from the Exchange
// Server, so let's parse the XML into an object we can more easily access.
// For this task, we'll use Troy's xml class object.
$x = new xml();
if (!$x->fetch($h->body)) {
$output = "There was a problem parsing your XML!";
$output .="".$h->log."\n";
$output .="".$h->header."\n";
$output .="".$h->body."\n";
$output .="".$x->log."\n";
echo $output;
exit();
}
foreach($x->data->A_MULTISTATUS[0]->A_RESPONSE as $idx=>$contact) {
$directory[] = "".$contact->A_PROPSTAT[0]->A_PROP[0]->E_CN[0]->_text."\n"."Dial:".$contact->A_PROPSTAT[0]->A_PROP[0]->E_TELEPHONENUMBER[0]->_text."\n";
#$directory[] = "".$callerid."\n";
}
# Display Page
$output ="";
$output .= "Directory ($page/$last)\n";
foreach ($directory as $v)
{
$output .= "\n";
$output .= $v;
$output .= "\n";
}
# Dial button
$output .= "\n";
$output .= "Dial\n";
$output .= "SoftKey:Select\n";
$output .= "\n";
# End of the object
$output .= "\n";
# HTTP header and output
header("Content-Type: text/xml");
header("Content-Length: ".strlen($output));
echo $output;
?>
are you using this script on a phone?
Yes in deed
which phones?
aastra 55i
Cool. I'll test it out on a 57i and a 480i see how it works.
class_http.php
<?php
/*
* Filename.......: class_http.php
* Author.........: Troy Wolf [troy@troywolf.com]
* Last Modified..: Date: 2006/03/06 10:15:00
* Description....: Screen-scraping class with caching. Includes image_cache.php
companion script. Includes static methods to extract data
out of HTML tables into arrays or XML. Now supports sending
XML requests and custom verbs with support for making
WebDAV requests to Microsoft Exchange Server.
*/
class http {
var $log;
var $dir;
var $name;
var $filename;
var $url;
var $port;
var $verb;
var $status;
var $header;
var $body;
var $ttl;
var $headers;
var $postvars;
var $xmlrequest;
var $connect_timeout;
var $data_ts;
/*
The class constructor. Configure defaults.
*/
function http() {
$this->log = "New http() object instantiated.
\n";
/*
Seconds to attempt socket connection before giving up.
*/
$this->connect_timeout = 30;
/*
Set the 'dir' property to the directory where you want to store the cached
content. I suggest a folder that is not web-accessible.
End this value with a "/".
*/
$this->dir = realpath("./")."/";//Default to current dir.
$this->clean();
return true;
}
/*
fetch() method to get the content. fetch() will use 'ttl' property to
determine whether to get the content from the url or the cache.
*/
function fetch($url="", $ttl=0, $name="", $user="", $pwd="", $verb="GET") {
$this->log .= "--------------------------------
fetch() called
\n";
$this->log .= "url: ".$url."
\n";
$this->status = "";
$this->header = "";
$this->body = "";
if (!$url) {
$this->log .= "OOPS: You need to pass a URL!
";
return false;
}
$this->url = $url;
$this->ttl = $ttl;
$this->name = $name;
$need_to_save = false;
if ($this->ttl == "0") {
if (!$fh = $this->getFromUrl($url, $user, $pwd, $verb)) {
return false;
}
}else {
if (strlen(trim($this->name)) == 0) {$this->name = MD5($url);}
$this->filename = $this->dir."http_".$this->name;
$this->log .= "Filename: ".$this->filename."
";
$this->getFile_ts();
if ($this->ttl == "daily") {
if (date('Y-m-d',$this->data_ts) != date('Y-m-d',time())) {
$this->log .= "cache has expired
";
if (!$fh = $this->getFromUrl($url, $user, $pwd, $verb)) {
return false;
}
$need_to_save = true;
if ($this->getFromUrl()) {return $this->saveToCache();}
}else {
if (!$fh = $this->getFromCache()) {
return false;
}
}
}else {
if ((time() - $this->data_ts) >= $this->ttl) {
$this->log .= "cache has expired
";
if (!$fh = $this->getFromUrl($url, $user, $pwd, $verb)) {
return false;
}
$need_to_save = true;
}else {
if (!$fh = $this->getFromCache()) {
return false;
}
}
}
}
/*
Get response header.
*/
$this->header = fgets($fh, 1024);
$this->status = substr($this->header,9,3);
while ((trim($line = fgets($fh, 1024)) != "") && (!feof($fh))) {
$this->header .= $line;
if ($this->status=="401" and strpos($line,"WWW-Authenticate: Basic realm=\"")===0) {
fclose($fh);
$this->log .= "Could not authenticate
\n";
return FALSE;
}
}
/*
Get response body.
*/
while (!feof($fh)) {
$this->body .= fgets($fh, 1024);
}
fclose($fh);
if ($need_to_save) {$this->saveToCache();}
return $this->status;
}
/*
PRIVATE getFromUrl() method to scrape content from url.
*/
function getFromUrl($url, $user="", $pwd="", $verb="GET") {
$this->log .= "getFromUrl() called
";
preg_match("~([a-z]*://)?([^:^/]*)(:([0-9]{1,5}))?(/.*)?~i", $url, $parts);
$protocol = $parts[1];
$server = $parts[2];
$port = $parts[4];
$path = $parts[5];
if ($port == "") {
if (strtolower($protocol) == "https://") {
$port = "443";
}else {
$port = "80";
}
}
if ($path == "") {$path = "/";}
if (!$sock = @fsockopen(((strtolower($protocol) == "https://")?"ssl://":"").$server, $port, $errno, $errstr, $this->connect_timeout)) {
$this->log .= "Could not open connection. Error "
.$errno.": ".$errstr."
\n";
return false;
}
$this->headers["Host"] = $server.":".$port;
if ($user != "" && $pwd != "") {
$this->log .= "Authentication will be attempted
\n";
$this->headers["Authorization"] = "Basic ".base64_encode($user.":".$pwd);
}
if (count($this->postvars) > 0) {
$this->log .= "Variables will be POSTed
\n";
$request = "POST ".$path." HTTP/1.0\r\n";
$post_string = "";
foreach ($this->postvars as $key=>$value) {
$post_string .= "&".urlencode($key)."=".urlencode($value);
}
$post_string = substr($post_string,1);
$this->headers["Content-Type"] = "application/x-www-form-urlencoded";
$this->headers["Content-Length"] = strlen($post_string);
}elseif (strlen($this->xmlrequest) > 0) {
$this->log .= "XML request will be sent
\n";
$request = $verb." ".$path." HTTP/1.0\r\n";
$this->headers["Content-Length"] = strlen($this->xmlrequest);
}else {
$request = $verb." ".$path." HTTP/1.0\r\n";
}
#echo "
request: ".$request;
if (fwrite($sock, $request) === FALSE) {
fclose($sock);
$this->log .= "Error writing request type to socket
\n";
return false;
}
foreach ($this->headers as $key=>$value) {
if (fwrite($sock, $key.": ".$value."\r\n") === FALSE) {
fclose($sock);
$this->log .= "Error writing headers to socket
\n";
return false;
}
}
if (fwrite($sock, "\r\n") === FALSE) {
fclose($sock);
$this->log .= "Error writing end-of-line to socket
\n";
return false;
}
#echo "
post_string: ".$post_string;
if (count($this->postvars) > 0) {
if (fwrite($sock, $post_string."\r\n") === FALSE) {
fclose($sock);
$this->log .= "Error writing POST string to socket
\n";
return false;
}
}elseif (strlen($this->xmlrequest) > 0) {
if (fwrite($sock, $this->xmlrequest."\r\n") === FALSE) {
fclose($sock);
$this->log .= "Error writing xml request string to socket
\n";
return false;
}
}
return $sock;
}
/*
PRIVATE clean() method to reset the instance back to mostly new state.
*/
function clean()
{
$this->status = "";
$this->header = "";
$this->body = "";
$this->headers = array();
$this->postvars = array();
/*
Try to use user agent of the user making this request. If not available,
default to IE6.0 on WinXP, SP1.
*/
if (isset($_SERVER['HTTP_USER_AGENT'])) {
$this->headers["User-Agent"] = $_SERVER['HTTP_USER_AGENT'];
}else {
$this->headers["User-Agent"] = "Mozilla/4.0 (compatible;MSIE 6.0;Windows NT 5.1;SV1)";
}
/*
Set referrer to the current script since in essence, it is the referring
page.
*/
if (substr($_SERVER['SERVER_PROTOCOL'],0,5) == "HTTPS") {
$this->headers["Referer"] = "https://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
}else {
$this->headers["Referer"] = "http://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
}
}
/*
PRIVATE getFromCache() method to retrieve content from cache file.
*/
function getFromCache() {
$this->log .= "getFromCache() called
";
//create file pointer
if (!$fp=@fopen($this->filename,"r")) {
$this->log .= "Could not open ".$this->filename."
";
return false;
}
return $fp;
}
/*
PRIVATE saveToCache() method to save content to cache file.
*/
function saveToCache() {
$this->log .= "saveToCache() called
";
//create file pointer
if (!$fp=@fopen($this->filename,"w")) {
$this->log .= "Could not open ".$this->filename."
";
return false;
}
//write to file
if (!@fwrite($fp,$this->header."\r\n".$this->body)) {
$this->log .= "Could not write to ".$this->filename."
";
fclose($fp);
return false;
}
//close file pointer
fclose($fp);
return true;
}
/*
PRIVATE getFile_ts() method to get cache file modified date.
*/
function getFile_ts() {
$this->log .= "getFile_ts() called
";
if (!file_exists($this->filename)) {
$this->data_ts = 0;
$this->log .= $this->filename." does not exist
";
return false;
}
$this->data_ts = filemtime($this->filename);
return true;
}
/*
Static method table_into_array()
Generic function to return data array from HTML table data
rawHTML: the page source
needle: optional string to start parsing source from
needle_within: 0 = needle is BEFORE table, 1 = needle is within table
allowed_tags: list of tags to NOT strip from data, e.g. ""
*/
function table_into_array($rawHTML,$needle="",$needle_within=0,$allowed_tags="") {
$upperHTML = strtoupper($rawHTML);
$idx = 0;
if (strlen($needle) > 0) {
$needle = strtoupper($needle);
$idx = strpos($upperHTML,$needle);
if ($idx === false) {return false;}
if ($needle_within == 1) {
$cnt = 0;
while(($cnt < 100) && (substr($upperHTML,$idx,6) != "
$idx = strrpos(substr($upperHTML,0,$idx-1),"<");
$cnt++;
}
}
}
$aryData = array();
$rowIdx = 0;
/* If this table has a header row, it may use TD or TH, so
check special for this first row. */
$tmp = strpos($upperHTML,"
if ($tmp === false) {return false;}
$tmp2 = strpos($upperHTML,"
if ($tmp2 === false) {return false;}
$row = substr($rawHTML,$tmp,$tmp2-$tmp);
$pattern = "/
$hdrTag = $matches[0];
while ($tmp = strpos(strtoupper($row),$hdrTag) !== false) {
$tmp = strpos(strtoupper($row),">",$tmp);
if ($tmp === false) {return false;}
$tmp++;
$tmp2 = strpos(strtoupper($row),"
$row = substr($row,$tmp2+5);
preg_match($pattern,strtoupper($row),$matches);
$hdrTag = $matches[0];
}
$idx = strpos($upperHTML,"",$idx)+5;
$rowIdx++;
/* Now parse the rest of the rows. */
$tmp = strpos($upperHTML,"
$tmp2 = strpos($upperHTML,"",$idx);
if ($tmp2 === false) {return false;}
$table = substr($rawHTML,$tmp,$tmp2-$tmp);
while ($tmp = strpos(strtoupper($table),"
$row = substr($table,$tmp,$tmp2-$tmp);
while ($tmp = strpos(strtoupper($row),"
if ($tmp === false) {return false;}
$tmp++;
$tmp2 = strpos(strtoupper($row)," $aryData[$rowIdx][] = trim(strip_tags(substr($row,$tmp,$tmp2-$tmp),$allowed_tags));
$row = substr($row,$tmp2+5);
}
$table = substr($table,strpos(strtoupper($table),"")+5);
$rowIdx++;
}
return $aryData;
}
/*
Static method table_into_xml()
Generic function to return xml dataset from HTML table data
rawHTML: the page source
needle: optional string to start parsing source from
allowedTags: list of tags to NOT strip from data, e.g. ""
*/
function table_into_xml($rawHTML,$needle="",$needle_within=0,$allowedTags="") {
if (!$aryTable = http::table_into_array($rawHTML,$needle,$needle_within,$allowedTags)) {return false;}
$xml = "<?xml version=\"1.0\" standalone=\"yes\" \?\>\n";
$xml .= "\n";
";
$rowIdx = 0;
foreach ($aryTable as $row) {
$xml .= "\t
$colIdx = 0;
foreach ($row as $col) {
$xml .= "\t\t
$colIdx++;
}
$xml .= "\t
$rowIdx++;
}
$xml .= "
return $xml;
}
}
?>
class_xml.php
<?php
/*
* Filename.......: class_xml.php
* Author.........: Troy Wolf [troy@troywolf.com] with major sections ripped from
Paul Rose.
* Last Modified..: Date: 2006/03/06 15:30:00
* Description....: XML parsing class.
Modified to replace ":" in object names with "_". This was to
support Exchange WebDAV stuff.
*/
class xml {
var $log;
var $data;
var $parser;
var $stack;
var $index;
/*
The class constructor. Configure defaults.
*/
function xml() {
$this->log = "New xml() object instantiated.
\n";
}
function fetch($raw_xml) {
$this->log .= "fetch() called.
\n";
$this->index = 0;
$this->data = null;
$this->stack = array();
$this->stack[] = &$this->data;
$this->parser = xml_parser_create ("UTF-8");
xml_set_object($this->parser,$this);
xml_set_element_handler($this->parser, "tag_open", "tag_close");
xml_set_character_data_handler($this->parser, "cdata");
xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, "UTF-8");
if (!$parsed_xml = xml_parse($this->parser,$raw_xml, true )) {
$this->log .= sprintf("XML error: %s at line %d",
xml_error_string(xml_get_error_code($this->parser)),
xml_get_current_line_number($this->parser));
return false;
}
xml_parser_free($this->parser);
return true;
}
function tag_open($parser, $tag, $attrs) {
$tag = str_replace("-", "_", $tag);
$tag = str_replace(":", "_", $tag);
foreach($attrs as $key => $val) {
$key = str_replace("-", "_", $key);
$key = str_replace(":", "_", $key);
$value = $this->clean($val);
$object->_attr->$key = $value;
}
$temp = &$this->stack[$this->index]->$tag;
$temp[] = $object; // push $object onto $tag
$size = sizeof($temp);
$this->stack[] = &$temp[$size-1];
$this->index++;
}
function tag_close($parser, $tag) {
array_pop($this->stack);
$this->index--;
}
function cdata($parser, $data) {
if(trim($data)){
$this->stack[$this->index]->_text .= $data;
}
}
function clean($string){
return utf8_decode(trim($string));
}
}
?>
Instead of needing credentials to do lookups in the GAL, I would think you can just have the phone or phones in question do a lookup to an LDAP server? I haven't done this with phones per-say, but just about everything else in my office does lokoups off an LDAP server (usually the exchange server). Dunno, just thinking it might be easier, and no messing with credentials. Just a thought.
ethan has this built into his voice rec system, you might want to collaborate with himhttp://trixbox.org/forums/trixbox-forums/open-discussion/major-announcement-asterisk-speech-recognition-magic-button#new
Tom,
Appreciate the great contribution. Just to make it clear this code should run on the trixbox Apache server and is formatted for a 55i/57i?
With just a quick look it seems that both the original and your updates are in the post. To make it simple can you post one more time with the finished code and use the code tags to preserve formatting? There is a BB-CODE help in the input format at the bottom of a message screen. I can't post the code tags inside a message or I would.
With your permission I will document and place in the wiki so others can find it easier.
Regards....Scott
This code works with aastra 55i and exchange 2003 using webdav. I put the files in /var/www/html/aastra/asterisk, then I programmed a button to aastra2exchange.php
You will need the following three files:
Asstra2Exchange.php
<?
#####################################################################
# Asterisk Directory for Exchange
#####################################################################
// Modify the paths to these class files as needed.
require_once("class_http.php");
require_once("class_xml.php");
// Change these values for your Exchange Server.
$exchange_server = "http://exchange";
$exchange_username = "username";
$exchange_password = "password";
// We use Troy's http class object to send the XML-formatted WebDAV request
// to the Exchange Server and to receive the response from the Exchange Server.
// The response is also XML-formatted.
$h = new http();
$h->headers["Content-Type"] = 'text/xml;charset="UTF-8"';
$h->headers["Depth"] = "0";
$h->headers["Translate"] = "f";
// Find all the contacts for a specific company in a specific folder.
// <a href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/e2k3/e2k3/_exch2k_urn_content-classes_person.asp" title="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/e2k3/e2k3/_exch2k_urn_content-classes_person.asp">http://msdn.microsoft.com/library/default.asp?url=/library/en-us/...</a>
$h->xmlrequest = '<?xml version="1.0"?>';
$h->xmlrequest .= <<<END
<a:searchrequest xmlns:a="DAV:">
<a:sql>
SELECT "a:href"
,"urn:schemas:contacts:o"
,"urn:schemas:contacts:cn"
,"urn:schemas:contacts:fileas"
,"urn:schemas:contacts:title"
,"urn:schemas:contacts:email1"
,"urn:schemas:contacts:telephoneNumber"
FROM "$exchange_server/exchange/username/contacts/"
ORDER BY "urn:schemas:contacts:cn"
</a:sql>
</a:searchrequest>
END;
// IMPORTANT -- The END line above must be completely left-aligned. No white-space.
// The 'fetch'method does the work of sending and receiving the request.
// NOTICE the last parameter passed--'SEARCH'in this example. That is the
// HTTP verb that you must correctly set according to the type of WebDAV request
// you are making. The examples on this page use either 'PROPFIND'or 'SEARCH'.
if (!$h->fetch($exchange_server."/exchange/username/contacts/", 0, null, $exchange_username, $exchange_password, "SEARCH")) {
echo "<h2>There is a problem with the http request!</h2>";
echo $h->log;
exit();
}
// The assumption now is that we've got an XML result back from the Exchange
// Server, so let's parse the XML into an object we can more easily access.
// For this task, we'll use Troy's xml class object.
$x = new xml();
if (!$x->fetch($h->body)) {
$output = "<h2>There was a problem parsing your XML!</h2>";
$output .="<pre>".$h->log."</pre><hr />\n";
$output .="<pre>".$h->header."</pre><hr />\n";
$output .="<pre>".$h->body."</pre><hr />\n";
$output .="<pre>".$x->log."</pre><hr />\n";
echo $output;
exit();
}
foreach($x->data->A_MULTISTATUS[0]->A_RESPONSE as $idx=>$contact) {
$directory[] = "<Prompt>".$contact->A_PROPSTAT[0]->A_PROP[0]->E_CN[0]->_text."</Prompt>\n"."<URI>Dial:".$contact->A_PROPSTAT[0]->A_PROP[0]->E_TELEPHONENUMBER[0]->_text."</URI>\n";
#$directory[] = "<Prompt>".$callerid."</Prompt>\n";
}
# Display Page
$output ="<AastraIPPhoneTextMenu destroyOnExit=\"yes\">";
$output .= "<Title>Directory ($page/$last)</Title>\n";
foreach ($directory as $v)
{
$output .= "<MenuItem>\n";
$output .= $v;
$output .= "</MenuItem>\n";
}
# Dial button
$output .= "<SoftKey index=\"1\">\n";
$output .= "<Label>Dial</Label>\n";
$output .= "<URI>SoftKey:Select</URI>\n";
$output .= "</SoftKey>\n";
# End of the object
$output .= "</AastraIPPhoneTextMenu>\n";
# HTTP header and output
header("Content-Type: text/xml");
header("Content-Length: ".strlen($output));
echo $output;
?>
class_http.php
<?php
/*
* Filename.......: class_http.php
* Author.........: Troy Wolf [troy@troywolf.com]
* Last Modified..: Date: 2006/03/06 10:15:00
* Description....: Screen-scraping class with caching. Includes image_cache.php
companion script. Includes static methods to extract data
out of HTML tables into arrays or XML. Now supports sending
XML requests and custom verbs with support for making
WebDAV requests to Microsoft Exchange Server.
*/
class http {
var $log;
var $dir;
var $name;
var $filename;
var $url;
var $port;
var $verb;
var $status;
var $header;
var $body;
var $ttl;
var $headers;
var $postvars;
var $xmlrequest;
var $connect_timeout;
var $data_ts;
/*
The class constructor. Configure defaults.
*/
function http() {
$this->log = "New http() object instantiated.<br />\n";
/*
Seconds to attempt socket connection before giving up.
*/
$this->connect_timeout = 30;
/*
Set the 'dir' property to the directory where you want to store the cached
content. I suggest a folder that is not web-accessible.
End this value with a "/".
*/
$this->dir = realpath("./")."/";//Default to current dir.
$this->clean();
return true;
}
/*
fetch() method to get the content. fetch() will use 'ttl' property to
determine whether to get the content from the url or the cache.
*/
function fetch($url="", $ttl=0, $name="", $user="", $pwd="", $verb="GET") {
$this->log .= "--------------------------------<br />fetch() called<br />\n";
$this->log .= "url: ".$url."<br />\n";
$this->status = "";
$this->header = "";
$this->body = "";
if (!$url) {
$this->log .= "OOPS: You need to pass a URL!<br />";
return false;
}
$this->url = $url;
$this->ttl = $ttl;
$this->name = $name;
$need_to_save = false;
if ($this->ttl == "0") {
if (!$fh = $this->getFromUrl($url, $user, $pwd, $verb)) {
return false;
}
}else {
if (strlen(trim($this->name)) == 0) {$this->name = MD5($url);}
$this->filename = $this->dir."http_".$this->name;
$this->log .= "Filename: ".$this->filename."<br />";
$this->getFile_ts();
if ($this->ttl == "daily") {
if (date('Y-m-d',$this->data_ts) != date('Y-m-d',time())) {
$this->log .= "cache has expired<br />";
if (!$fh = $this->getFromUrl($url, $user, $pwd, $verb)) {
return false;
}
$need_to_save = true;
if ($this->getFromUrl()) {return $this->saveToCache();}
}else {
if (!$fh = $this->getFromCache()) {
return false;
}
}
}else {
if ((time() - $this->data_ts) >= $this->ttl) {
$this->log .= "cache has expired<br />";
if (!$fh = $this->getFromUrl($url, $user, $pwd, $verb)) {
return false;
}
$need_to_save = true;
}else {
if (!$fh = $this->getFromCache()) {
return false;
}
}
}
}
/*
Get response header.
*/
$this->header = fgets($fh, 1024);
$this->status = substr($this->header,9,3);
while ((trim($line = fgets($fh, 1024)) != "") && (!feof($fh))) {
$this->header .= $line;
if ($this->status=="401" and strpos($line,"WWW-Authenticate: Basic realm=\"")===0) {
fclose($fh);
$this->log .= "Could not authenticate<br />\n";
return FALSE;
}
}
/*
Get response body.
*/
while (!feof($fh)) {
$this->body .= fgets($fh, 1024);
}
fclose($fh);
if ($need_to_save) {$this->saveToCache();}
return $this->status;
}
/*
PRIVATE getFromUrl() method to scrape content from url.
*/
function getFromUrl($url, $user="", $pwd="", $verb="GET") {
$this->log .= "getFromUrl() called<br />";
preg_match("~([a-z]*://)?([^:^/]*)(:([0-9]{1,5}))?(/.*)?~i", $url, $parts);
$protocol = $parts[1];
$server = $parts[2];
$port = $parts[4];
$path = $parts[5];
if ($port == "") {
if (strtolower($protocol) == "https://") {
$port = "443";
}else {
$port = "80";
}
}
if ($path == "") {$path = "/";}
if (!$sock = @fsockopen(((strtolower($protocol) == "https://")?"ssl://":"").$server, $port, $errno, $errstr, $this->connect_timeout)) {
$this->log .= "Could not open connection. Error "
.$errno.": ".$errstr."<br />\n";
return false;
}
$this->headers["Host"] = $server.":".$port;
if ($user != "" && $pwd != "") {
$this->log .= "Authentication will be attempted<br />\n";
$this->headers["Authorization"] = "Basic ".base64_encode($user.":".$pwd);
}
if (count($this->postvars) > 0) {
$this->log .= "Variables will be POSTed<br />\n";
$request = "POST ".$path." HTTP/1.0\r\n";
$post_string = "";
foreach ($this->postvars as $key=>$value) {
$post_string .= "&".urlencode($key)."=".urlencode($value);
}
$post_string = substr($post_string,1);
$this->headers["Content-Type"] = "application/x-www-form-urlencoded";
$this->headers["Content-Length"] = strlen($post_string);
}elseif (strlen($this->xmlrequest) > 0) {
$this->log .= "XML request will be sent<br />\n";
$request = $verb." ".$path." HTTP/1.0\r\n";
$this->headers["Content-Length"] = strlen($this->xmlrequest);
}else {
$request = $verb." ".$path." HTTP/1.0\r\n";
}
#echo "<br />request: ".$request;
if (fwrite($sock, $request) === FALSE) {
fclose($sock);
$this->log .= "Error writing request type to socket<br />\n";
return false;
}
foreach ($this->headers as $key=>$value) {
if (fwrite($sock, $key.": ".$value."\r\n") === FALSE) {
fclose($sock);
$this->log .= "Error writing headers to socket<br />\n";
return false;
}
}
if (fwrite($sock, "\r\n") === FALSE) {
fclose($sock);
$this->log .= "Error writing end-of-line to socket<br />\n";
return false;
}
#echo "<br />post_string: ".$post_string;
if (count($this->postvars) > 0) {
if (fwrite($sock, $post_string."\r\n") === FALSE) {
fclose($sock);
$this->log .= "Error writing POST string to socket<br />\n";
return false;
}
}elseif (strlen($this->xmlrequest) > 0) {
if (fwrite($sock, $this->xmlrequest."\r\n") === FALSE) {
fclose($sock);
$this->log .= "Error writing xml request string to socket<br />\n";
return false;
}
}
return $sock;
}
/*
PRIVATE clean() method to reset the instance back to mostly new state.
*/
function clean()
{
$this->status = "";
$this->header = "";
$this->body = "";
$this->headers = array();
$this->postvars = array();
/*
Try to use user agent of the user making this request. If not available,
default to IE6.0 on WinXP, SP1.
*/
if (isset($_SERVER['HTTP_USER_AGENT'])) {
$this->headers["User-Agent"] = $_SERVER['HTTP_USER_AGENT'];
}else {
$this->headers["User-Agent"] = "Mozilla/4.0 (compatible;MSIE 6.0;Windows NT 5.1;SV1)";
}
/*
Set referrer to the current script since in essence, it is the referring
page.
*/
if (substr($_SERVER['SERVER_PROTOCOL'],0,5) == "HTTPS") {
$this->headers["Referer"] = "https://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
}else {
$this->headers["Referer"] = "http://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
}
}
/*
PRIVATE getFromCache() method to retrieve content from cache file.
*/
function getFromCache() {
$this->log .= "getFromCache() called<br />";
//create file pointer
if (!$fp=@fopen($this->filename,"r")) {
$this->log .= "Could not open ".$this->filename."<br />";
return false;
}
return $fp;
}
/*
PRIVATE saveToCache() method to save content to cache file.
*/
function saveToCache() {
$this->log .= "saveToCache() called<br />";
//create file pointer
if (!$fp=@fopen($this->filename,"w")) {
$this->log .= "Could not open ".$this->filename."<br />";
return false;
}
//write to file
if (!@fwrite($fp,$this->header."\r\n".$this->body)) {
$this->log .= "Could not write to ".$this->filename."<br />";
fclose($fp);
return false;
}
//close file pointer
fclose($fp);
return true;
}
/*
PRIVATE getFile_ts() method to get cache file modified date.
*/
function getFile_ts() {
$this->log .= "getFile_ts() called<br />";
if (!file_exists($this->filename)) {
$this->data_ts = 0;
$this->log .= $this->filename." does not exist<br />";
return false;
}
$this->data_ts = filemtime($this->filename);
return true;
}
/*
Static method table_into_array()
Generic function to return data array from HTML table data
rawHTML: the page source
needle: optional string to start parsing source from
needle_within: 0 = needle is BEFORE table, 1 = needle is within table
allowed_tags: list of tags to NOT strip from data, e.g. "<a><b>"
*/
function table_into_array($rawHTML,$needle="",$needle_within=0,$allowed_tags="") {
$upperHTML = strtoupper($rawHTML);
$idx = 0;
if (strlen($needle) > 0) {
$needle = strtoupper($needle);
$idx = strpos($upperHTML,$needle);
if ($idx === false) {return false;}
if ($needle_within == 1) {
$cnt = 0;
while(($cnt < 100) && (substr($upperHTML,$idx,6) != "<TABLE")) {
$idx = strrpos(substr($upperHTML,0,$idx-1),"<");
$cnt++;
}
}
}
$aryData = array();
$rowIdx = 0;
/* If this table has a header row, it may use TD or TH, so
check special for this first row. */
$tmp = strpos($upperHTML,"<TR",$idx);
if ($tmp === false) {return false;}
$tmp2 = strpos($upperHTML,"</TR>",$tmp);
if ($tmp2 === false) {return false;}
$row = substr($rawHTML,$tmp,$tmp2-$tmp);
$pattern = "/<TH>|<TH\ |<TD>|<TD\ /";
preg_match($pattern,strtoupper($row),$matches);
$hdrTag = $matches[0];
while ($tmp = strpos(strtoupper($row),$hdrTag) !== false) {
$tmp = strpos(strtoupper($row),">",$tmp);
if ($tmp === false) {return false;}
$tmp++;
$tmp2 = strpos(strtoupper($row),"</T");
$aryData[$rowIdx][] = trim(strip_tags(substr($row,$tmp,$tmp2-$tmp),$allowed_tags));
$row = substr($row,$tmp2+5);
preg_match($pattern,strtoupper($row),$matches);
$hdrTag = $matches[0];
}
$idx = strpos($upperHTML,"</TR>",$idx)+5;
$rowIdx++;
/* Now parse the rest of the rows. */
$tmp = strpos($upperHTML,"<TR",$idx);
if ($tmp === false) {return false;}
$tmp2 = strpos($upperHTML,"</TABLE>",$idx);
if ($tmp2 === false) {return false;}
$table = substr($rawHTML,$tmp,$tmp2-$tmp);
while ($tmp = strpos(strtoupper($table),"<TR") !== false) {
$tmp2 = strpos(strtoupper($table),"</TR");
if ($tmp2 === false) {return false;}
$row = substr($table,$tmp,$tmp2-$tmp);
while ($tmp = strpos(strtoupper($row),"<TD") !== false) {
$tmp = strpos(strtoupper($row),">",$tmp);
if ($tmp === false) {return false;}
$tmp++;
$tmp2 = strpos(strtoupper($row),"</TD");
$aryData[$rowIdx][] = trim(strip_tags(substr($row,$tmp,$tmp2-$tmp),$allowed_tags));
$row = substr($row,$tmp2+5);
}
$table = substr($table,strpos(strtoupper($table),"</TR>")+5);
$rowIdx++;
}
return $aryData;
}
/*
Static method table_into_xml()
Generic function to return xml dataset from HTML table data
rawHTML: the page source
needle: optional string to start parsing source from
allowedTags: list of tags to NOT strip from data, e.g. "<a><b>"
*/
function table_into_xml($rawHTML,$needle="",$needle_within=0,$allowedTags="") {
if (!$aryTable = http::table_into_array($rawHTML,$needle,$needle_within,$allowedTags)) {return false;}
$xml = "<?xml version=\"1.0\" standalone=\"yes\" \?\>\n";
$xml .= "<TABLE>\n";
$rowIdx = 0;
foreach ($aryTable as $row) {
$xml .= "\t<ROW id=\"".$rowIdx."\">\n";
$colIdx = 0;
foreach ($row as $col) {
$xml .= "\t\t<COL id=\"".$colIdx."\">".trim(utf8_encode(htmlspecialchars($col)))."</COL>\n";
$colIdx++;
}
$xml .= "\t</ROW>\n";
$rowIdx++;
}
$xml .= "</TABLE>";
return $xml;
}
}
?>
class_xml.php
<?php
/*
* Filename.......: class_xml.php
* Author.........: Troy Wolf [troy@troywolf.com] with major sections ripped from
Paul Rose.
* Last Modified..: Date: 2006/03/06 15:30:00
* Description....: XML parsing class.
Modified to replace ":" in object names with "_". This was to
support Exchange WebDAV stuff.
*/
class xml {
var $log;
var $data;
var $parser;
var $stack;
var $index;
/*
The class constructor. Configure defaults.
*/
function xml() {
$this->log = "New xml() object instantiated.<br />\n";
}
function fetch($raw_xml) {
$this->log .= "fetch() called.<br />\n";
$this->index = 0;
$this->data = null;
$this->stack = array();
$this->stack[] = &$this->data;
$this->parser = xml_parser_create ("UTF-8");
xml_set_object($this->parser,$this);
xml_set_element_handler($this->parser, "tag_open", "tag_close");
xml_set_character_data_handler($this->parser, "cdata");
xml_parser_set_option($this->parser, XML_OPTION_TARGET_ENCODING, "UTF-8");
if (!$parsed_xml = xml_parse($this->parser,$raw_xml, true )) {
$this->log .= sprintf("XML error: %s at line %d",
xml_error_string(xml_get_error_code($this->parser)),
xml_get_current_line_number($this->parser));
return false;
}
xml_parser_free($this->parser);
return true;
}
function tag_open($parser, $tag, $attrs) {
$tag = str_replace("-", "_", $tag);
$tag = str_replace(":", "_", $tag);
foreach($attrs as $key => $val) {
$key = str_replace("-", "_", $key);
$key = str_replace(":", "_", $key);
$value = $this->clean($val);
$object->_attr->$key = $value;
}
$temp = &$this->stack[$this->index]->$tag;
$temp[] = $object; // push $object onto $tag
$size = sizeof($temp);
$this->stack[] = &$temp[$size-1];
$this->index++;
}
function tag_close($parser, $tag) {
array_pop($this->stack);
$this->index--;
}
function cdata($parser, $data) {
if(trim($data)){
$this->stack[$this->index]->_text .= $data;
}
}
function clean($string){
return utf8_decode(trim($string));
}
}
?>
I assume you don't mind me writing up a little documentation and placing your contribution on the wiki?
Thanks....Scott
No Problem- although I did not write class_xml.php or class_html.php, i found them here http://www.troywolf.com/articles/php/exchange_webdav_examples.php
I will make sure the appropriate credit is given and reposting is allowed.
Scott
Is it possible to query Exchange with Polycom phones?
if Polycom can understand php file then it should work, you will have to change the follow lines in aastra2exchange.php
You will need o chnage the
$directory[] = "<Prompt>".$contact->A_PROPSTAT[0]->A_PROP[0]->E_CN[0]->_text."</Prompt>\n"."<URI>Dial:".$contact->A_PROPSTAT[0]->A_PROP[0]->E_TELEPHONENUMBER[0]->_text."</URI>\n";
Again you will need to change this to match polycom settings
# Display Page
$output ="<AastraIPPhoneTextMenu destroyOnExit=\"yes\">";
$output .= "<Title>Directory ($page/$last)</Title>\n";
foreach ($directory as $v)
{
$output .= "<MenuItem>\n";
$output .= $v;
$output .= "</MenuItem>\n";
}
# Dial button
$output .= "<SoftKey index=\"1\">\n";
$output .= "<Label>Dial</Label>\n";
$output .= "<URI>SoftKey:Select</URI>\n";
$output .= "</SoftKey>\n";
# End of the object
$output .= "</AastraIPPhoneTextMenu>\n";
# HTTP header and output
header("Content-Type: text/xml");
header("Content-Length: ".strlen($output));
echo $output; 

Member Since:
2007-08-10