Overview

Packages

  • None
  • Rpc

Classes

  • Horde_Rpc
  • Horde_Rpc_ActiveSync
  • Horde_Rpc_Exception
  • Horde_Rpc_Phpgw
  • Horde_Rpc_Soap
  • Horde_Rpc_Syncml
  • Horde_Rpc_Syncml_Wbxml
  • Horde_Rpc_Translation
  • Horde_Rpc_Webdav
  • Horde_Rpc_Webdav2
  • Horde_Rpc_Xmlrpc
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * Copyright 2009-2012 Horde LLC (http://www.horde.org/)
  4:  *
  5:  * See the enclosed file COPYING for license information (LGPL). If you
  6:  * did not receive this file, see http://www.horde.org/licenses/lgpl21.
  7:  *
  8:  * @author   Michael J. Rubinsky <mrubinsk@horde.org>
  9:  *
 10:  * @category Horde
 11:  * @package  Rpc
 12:  */
 13: class Horde_Rpc_ActiveSync extends Horde_Rpc
 14: {
 15:     /**
 16:      * Holds the request's GET variables
 17:      *
 18:      * @var array
 19:      */
 20:     private $_get;
 21: 
 22:     /**
 23:      * The ActiveSync server object
 24:      *
 25:      * @var Horde_ActiveSync
 26:      */
 27:     private $_server;
 28: 
 29:     /**
 30:      * ActiveSync's backend target (the datastore it syncs the PDA with)
 31:      *
 32:      * @var Horde_ActiveSync_Driver
 33:      */
 34:     private $_backend;
 35: 
 36:     /**
 37:      * Constructor.
 38:      * Parameters in addition to Horde_Rpc's:
 39:      *   (required) 'backend'      = Horde_ActiveSync_Driver
 40:      *   (required) 'server'       = Horde_ActiveSync
 41:      *   (optional) 'provisioning' = Require device provisioning?
 42:      *
 43:      * @param Horde_Controller_Request_Http  The request object.
 44:      * @param array $config  A hash containing any additional configuration or
 45:      *                       connection parameters this class might need.
 46:      */
 47:     public function __construct(Horde_Controller_Request_Http $request, array $params = array())
 48:     {
 49:         parent::__construct($request, $params);
 50: 
 51:         // Check for requirements
 52:         $this->_get = $request->getGetVars();
 53:         if ($request->getMethod() == 'POST' &&
 54:             (empty($this->_get['Cmd']) || empty($this->_get['DeviceId']) || empty($this->_get['DeviceType']))) {
 55: 
 56:             $this->_logger->err('Missing required parameters.');
 57:             throw new Horde_Rpc_Exception('Your device requested the ActiveSync URL wihtout required parameters.');
 58:         }
 59: 
 60:         // Some devices (incorrectly) only send the username in the httpauth
 61:         if ($request->getMethod() == 'POST' &&  empty($this->_get['User'])) {
 62:             $serverVars = $this->_request->getServerVars();
 63:             if ($serverVars['PHP_AUTH_USER']) {
 64:                 $this->_get['User'] = $serverVars['PHP_AUTH_USER'];
 65:             } elseif ($serverVars['Authorization']) {
 66:                 $hash = str_replace('Basic ', '', $serverVars['Authorization']);
 67:                 $hash = base64_decode($hash);
 68:                 if (strpos($hash, ':') !== false) {
 69:                     list($this->_get['User'], $pass) = explode(':', $hash, 2);
 70:                 }
 71:             }
 72:             if (empty($this->_get['User'])) {
 73:                 $this->_logger->err('Missing required parameters.');
 74:                 throw new Horde_Rpc_Exception('Your device requested the ActiveSync URL wihtout required parameters.');
 75:             }
 76:         }
 77: 
 78:         $this->_backend = $params['backend'];
 79:         $this->_server = $params['server'];
 80:         $this->_server->setProvisioning(empty($params['provisioning']) ? false : $params['provisioning']);
 81:     }
 82: 
 83:     /**
 84:      * Returns the Content-Type of the response.
 85:      *
 86:      * @return string  The MIME Content-Type of the RPC response.
 87:      */
 88:     public function getResponseContentType()
 89:     {
 90:         return 'application/vnd.ms-sync.wbxml';
 91:     }
 92: 
 93:     /**
 94:      * Horde_ActiveSync will read the input stream directly, do not access
 95:      * it here.
 96:      *
 97:      * @see framework/Rpc/lib/Horde/Horde_Rpc#getInput()
 98:      */
 99:     public function getInput()
100:     {
101:         return null;
102:     }
103: 
104:     /**
105:      * Sends an RPC request to the server and returns the result.
106:      *
107:      * @param string $request  PHP input stream (ignored).
108:      *
109:      * @return void
110:      */
111:     public function getResponse($request)
112:     {
113:         /* Not sure about this, but it's what zpush did so... */
114:         ob_start(null, 1048576);
115:         $serverVars = $this->_request->getServerVars();
116:         switch ($serverVars['REQUEST_METHOD']) {
117:         case 'OPTIONS':
118:         case 'GET':
119:             if ($serverVars['REQUEST_METHOD'] == 'GET' &&
120:                 $this->_get['Cmd'] != 'OPTIONS') {
121:                 throw new Horde_Rpc_Exception('Trying to access the ActiveSync endpoint from a browser. Not Supported.');
122:             }
123:             $this->_logger->debug('Horde_Rpc_ActiveSync::getResponse() starting for OPTIONS');
124:             try {
125:                 $this->_server->handleRequest('Options', null, null);
126:             } catch (Horde_ActiveSync_Exception $e) {
127:                 $this->_handleError($e);
128:             }
129:             break;
130: 
131:         case 'POST':
132:             Horde_ActiveSync::activeSyncHeader();
133:             $this->_logger->debug('Horde_Rpc_ActiveSync::getResponse() starting for ' . $this->_get['Cmd']);
134:             try {
135:                 $this->_server->handleRequest($this->_get['Cmd'], $this->_get['DeviceId']);
136:             } catch (Horde_ActiveSync_Exception $e) {
137:                 $this->_handleError($e);
138:             }
139:             break;
140:         }
141:     }
142: 
143:     /**
144:      *
145:      * @see framework/Rpc/lib/Horde/Horde_Rpc#sendOutput($output)
146:      */
147:     public function sendOutput($output)
148:     {
149:         // Unfortunately, even though zpush can stream the data to the client
150:         // with a chunked encoding, using chunked encoding also breaks the
151:         // progress bar on the PDA. So we de-chunk here and just output a
152:         // content-length header and send it as a 'normal' packet. If the output
153:         // packet exceeds 1MB (see ob_start) then it will be sent as a chunked
154:         // packet anyway because PHP will have to flush the buffer.
155:         $len = ob_get_length();
156:         $data = ob_get_contents();
157:         ob_end_clean();
158: 
159:         // TODO: Figure this out...Z-Push had two possible paths for outputting
160:         // to the client, 1) if the ob reached it's capacity, and here...but
161:         // it didn't originally output the Content-Type header
162:         header('Content-Type: application/vnd.ms-sync.wbxml');
163:         header('Content-Length: ' . $len);
164:         echo $data;
165:     }
166: 
167:     /**
168:      * Check authentication. Different backends may handle
169:      * authentication in different ways. The base class implementation
170:      * checks for HTTP Authentication against the Horde auth setup.
171:      *
172:      * @TODO should the realm be configurable - since Horde is only one of the
173:      * possible backends?
174:      *
175:      * @return boolean  Returns true if authentication is successful.
176:      *                  Should send appropriate "not authorized" headers
177:      *                  or other response codes/body if auth fails,
178:      *                  and take care of exiting.
179:      */
180:     public function authorize()
181:     {
182:         $this->_logger->debug('Horde_Rpc_ActiveSync::authorize() starting');
183:         if (!$this->_requireAuthorization) {
184:             return true;
185:         }
186: 
187:         /* Get user and password */
188:         $serverVars = $this->_request->getServerVars();
189:         $user = $pass = '';
190:         if (!empty($serverVars['PHP_AUTH_USER'])) {
191:             $user = $serverVars['PHP_AUTH_USER'];
192:             $pass = $serverVars['PHP_AUTH_PW'];
193:         } elseif (!empty($serverVars['Authorization'])) {
194:             $hash = str_replace('Basic ', '', $serverVars['Authorization']);
195:             $hash = base64_decode($hash);
196:             if (strpos($hash, ':') !== false) {
197:                 list($user, $pass) = explode(':', $hash, 2);
198:             }
199:         }
200: 
201:         /* Get possibly domain */
202:         $pos = strrpos($user, '\\');
203:         if ($pos !== false) {
204:             $domain = substr($user, 0, $pos);
205:             $user = substr($user, $pos + 1);
206:         } else {
207:             $domain = null;
208:         }
209: 
210:         /* Attempt to auth to backend */
211:         $results = $this->_backend->logon($user, $pass, $domain);
212:         if (!$results && empty($this->_policykey)) {
213:             header('HTTP/1.1 401 Unauthorized');
214:             header('WWW-Authenticate: Basic realm="Horde RPC"');
215:             $this->_logger->info('Access denied for user: ' . $user . '. Username or password incorrect.');
216:         }
217: 
218:         /* Successfully authenticated to backend, try to setup the backend */
219:         if (empty($this->_get['User'])) {
220:             return false;
221:         }
222:         $results = $this->_backend->setup($this->_get['User']);
223:         if (!$results) {
224:             header('HTTP/1.1 401 Unauthorized');
225:             header('WWW-Authenticate: Basic realm="Horde RPC"');
226:             echo 'Access denied or user ' . $this->_get['User'] . ' unknown.';
227:         }
228: 
229:         $this->_logger->debug('Horde_Rpc_ActiveSync::authorize() exiting');
230: 
231:         return true;
232:     }
233: 
234:     /**
235:      * Output exception information to the logger.
236:      *
237:      * @param Exception $e  The exception
238:      *
239:      * @throws Horde_Rpc_Exception $e
240:      */
241:     protected function _handleError($e)
242:     {
243:         $trace = $e->getTraceAsString();
244:         $m = $e->getMessage();
245:         $buffer = ob_get_clean();
246: 
247:         $this->_logger->err('Error in communicating with ActiveSync server: ' . $m);
248:         $this->_logger->err($trace);
249:         $this->_logger->err('Buffer contents: ' . $buffer);
250:         throw new Horde_Rpc_Exception($e);
251:     }
252: 
253: }
254: 
API documentation generated by ApiGen