MS Exchange - Contacts lookup

tom.garratt
Posts: 22
Member Since:
2007-08-10

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;
?>



kspare
Posts: 673
Member Since:
2007-02-16
are you using this script on

are you using this script on a phone?



tom.garratt
Posts: 22
Member Since:
2007-08-10
Yes

Yes in deed



kspare
Posts: 673
Member Since:
2007-02-16
which phones?

which phones?



tom.garratt
Posts: 22
Member Since:
2007-08-10
aastra 55i

aastra 55i



kspare
Posts: 673
Member Since:
2007-02-16
Cool. I'll test it out on a

Cool. I'll test it out on a 57i and a 480i see how it works.



tom.garratt
Posts: 22
Member Since:
2007-08-10
you will need

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,"

",$tmp);
if ($tmp2 === false) {return false;}
$row = substr($rawHTML,$tmp,$tmp2-$tmp);
$pattern = "/|| 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),"

$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,"",$idx)+5;
$rowIdx++;

/* Now parse the rest of the rows. */
$tmp = strpos($upperHTML,"

if ($tmp === false) {return false;}
$tmp2 = strpos($upperHTML,"",$idx);
if ($tmp2 === false) {return false;}
$table = substr($rawHTML,$tmp,$tmp2-$tmp);

while ($tmp = strpos(strtoupper($table),"

$tmp2 = strpos(strtoupper($table)," if ($tmp2 === false) {return false;}
$row = substr($table,$tmp,$tmp2-$tmp);

while ($tmp = strpos(strtoupper($row),"

$tmp = strpos(strtoupper($row),">",$tmp);
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\n";
$colIdx = 0;
foreach ($row as $col) {
$xml .= "\t\t
".trim(utf8_encode(htmlspecialchars($col)))."\n";
$colIdx++;
}
$xml .= "\t\n";
$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));
}
}
?>



scorobinso
Posts: 58
Member Since:
2007-03-09
Hmmm...

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.



jahyde
Posts: 2002
Member Since:
2006-06-02
ethan has this built into

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

--

--my PBX is run on 2 V8's



SkykingOH
Posts: 8081
Member Since:
2007-12-17
Thanks Tom

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

--

Scott

aka "Skyking"



tom.garratt
Posts: 22
Member Since:
2007-08-10
This code works with aastra

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));
  }
}
?>


SkykingOH
Posts: 8081
Member Since:
2007-12-17
That was fast

I assume you don't mind me writing up a little documentation and placing your contribution on the wiki?

Thanks....Scott

--

Scott

aka "Skyking"



tom.garratt
Posts: 22
Member Since:
2007-08-10
no problem

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



SkykingOH
Posts: 8081
Member Since:
2007-12-17
I will make sure the

I will make sure the appropriate credit is given and reposting is allowed.

Scott

--

Scott

aka "Skyking"



apple01
Posts: 178
Member Since:
2007-05-17
can this be used with Polycom phones

Is it possible to query Exchange with Polycom phones?



tom.garratt
Posts: 22
Member Since:
2007-08-10
Polycom

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
and Dial: to match the polycom settings

				$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;


Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.