Merge branch 'master' of http://git.tine20.org/git/Syncope
[tine20] / tine20 / library / Syncope / lib / Syncope / Server.php
1 <?php
2
3 class Syncope_Server
4 {
5     protected $_body;
6     
7     /**
8      * informations about the currently device
9      *
10      * @var Syncope_Backend_IDevice
11      */
12     protected $_deviceBackend;
13     
14     /**
15      * @var Zend_Log
16      */
17     protected $_logger;
18     
19     /**
20      * @var Zend_Controller_Request_Http
21      */
22     protected $_request;
23     
24     protected $_userId;
25     
26     public function __construct($userId, Zend_Controller_Request_Http $request = null, $body = null)
27     {
28         if (Syncope_Registry::isRegistered('loggerBackend')) {
29             $this->_logger = Syncope_Registry::get('loggerBackend');
30         }
31         
32         $this->_userId  = $userId;
33         if ($this->_logger instanceof Zend_Log)
34             $this->_logger->debug(__METHOD__ . '::' . __LINE__ . ' REQUEST METHOD: ');
35         $this->_request = $request !== null ? $request : new Zend_Controller_Request_Http();
36         if ($this->_logger instanceof Zend_Log)
37             $this->_logger->debug(__METHOD__ . '::' . __LINE__ . ' REQUEST METHOD: ');
38         $this->_body    = $body    !== null ? $body    : fopen('php://input', 'r');
39         if ($this->_logger instanceof Zend_Log)
40             $this->_logger->debug(__METHOD__ . '::' . __LINE__ . ' REQUEST METHOD: ');
41         
42         $this->_deviceBackend = Syncope_Registry::get('deviceBackend');
43         
44     }
45         
46     public function handle()
47     {
48         if ($this->_logger instanceof Zend_Log)
49             $this->_logger->debug(__METHOD__ . '::' . __LINE__ . ' REQUEST METHOD: ' . $this->_request->getMethod());
50         
51         switch($this->_request->getMethod()) {
52             case 'OPTIONS':
53                 $this->_handleOptions();
54                 break;
55         
56             case 'POST':
57                 $this->_handlePost();
58                 break;
59         
60             case 'GET':
61                 echo "It works!<br>Your username is: {$this->_username} and your IP address is: {$_SERVER['REMOTE_ADDR']}.";
62                 break;
63         }       
64     }
65     
66     /**
67     * handle options request
68     *
69     */
70     protected function _handleOptions()
71     {
72         $command = new Syncope_Command_Options();
73     
74         $command->getResponse();
75     }
76     
77     protected function _handlePost()
78     {
79         if(count($_GET) == 1) {
80             $arrayKeys = array_keys($_GET);
81             $requestParameters = $this->_decodeRequestParameters($arrayKeys[0]);
82         } else {
83             $requestParameters = $this->_getRequestParameters();
84         }
85         
86         if ($this->_logger instanceof Zend_Log) 
87             $this->_logger->debug(__METHOD__ . '::' . __LINE__ . ' REQUEST ' . print_r($requestParameters, true));
88         
89         $userAgent = $this->_request->getServer('HTTP_USER_AGENT', $requestParameters['deviceType']);
90         $policyKey = $this->_request->getServer('HTTP_X_MS_POLICYKEY');
91         
92         if ($this->_logger instanceof Zend_Log) 
93             $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " Agent: $userAgent  PolicyKey: $policyKey ASVersion: {$requestParameters['protocolVersion']} Command: {$requestParameters['command']}");
94         
95         $className = 'Syncope_Command_' . $requestParameters['command'];
96         
97         if(!class_exists($className)) {
98             throw new Syncope_Exception_CommandNotFound('unsupported command ' . $requestParameters['command']);
99         }
100         
101         $device = $this->_deviceBackend->getUserDevice($this->_userId, $requestParameters['deviceId'], $requestParameters['deviceType']);
102         
103         $device->useragent = $userAgent;
104         $device->acsversion = $requestParameters['protocolVersion'];
105         
106         $this->_deviceBackend->update($device);
107         
108         if ($this->_request->getServer('CONTENT_TYPE') == 'application/vnd.ms-sync.wbxml') {
109             // decode wbxml request
110             try {
111                 $decoder = new Wbxml_Decoder($this->_body);
112                 $requestBody = $decoder->decode();
113                 if ($this->_logger instanceof Zend_Log) 
114                     $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " xml request: " . $requestBody->saveXML());
115             } catch(Wbxml_Exception_UnexpectedEndOfFile $e) {
116                 $requestBody = NULL;
117             }
118         } else {
119             $requestBody = $this->_body;
120         }
121         
122         try {
123             $command = new $className($requestBody, $device, $policyKey);
124         
125             $command->handle();
126
127             if (PHP_SAPI !== 'cli') {
128                 header("MS-Server-ActiveSync: 8.3");
129             }
130         
131             $response = $command->getResponse();
132         } catch (Syncope_Exception_PolicyKeyMissing $asepkm) {
133             if ($this->_logger instanceof Zend_Log) 
134                 $this->_logger->warn(__METHOD__ . '::' . __LINE__ . " X-MS-POLICYKEY missing (" . $_command. ')');
135             header("HTTP/1.1 400 header X-MS-POLICYKEY not found");
136             return;
137         } catch (Syncope_Exception_ProvisioningNeeded $asepn) {
138             if ($this->_logger instanceof Zend_Log) 
139                 $this->_logger->info(__METHOD__ . '::' . __LINE__ . " provisioning needed");
140             header("HTTP/1.1 449 Retry after sending a PROVISION command");
141             return;
142         }
143         
144         if ($this->_request->getServer('CONTENT_TYPE') == 'application/vnd.ms-sync.wbxml') {
145             if ($this->_logger instanceof Zend_Log) 
146                 $this->_logger->debug(__METHOD__ . '::' . __LINE__ . " xml response: " . $response->saveXML());
147         
148             $outputStream = fopen("php://temp", 'r+');
149         
150             $encoder = new Wbxml_Encoder($outputStream, 'UTF-8', 3);
151             $encoder->encode($response);
152         
153             header("Content-Type: application/vnd.ms-sync.wbxml");
154         
155             rewind($outputStream);
156             fpassthru($outputStream);
157         }
158     }
159     
160     /**
161      * return request params
162      * 
163      * @return array
164      */
165     protected function _getRequestParameters()
166     {
167         $result = array(
168             'protocolVersion'  => $this->_request->getServer('HTTP_MS_ASPROTOCOLVERSION'),
169             'command'          => $this->_request->getParam('Cmd'),
170             'deviceId'         => $this->_request->getParam('DeviceId'),
171             'deviceType'       => $this->_request->getParam('DeviceType')
172         );
173         
174         return $result;
175     }
176 }