1 | <?php |
---|
2 | /*********************************************** |
---|
3 | * File : index.php |
---|
4 | * Project : Z-Push |
---|
5 | * Descr : This is the entry point |
---|
6 | * through which all requests |
---|
7 | * are called. |
---|
8 | * |
---|
9 | * Created : 01.10.2007 |
---|
10 | * |
---|
11 | * Zarafa Deutschland GmbH, www.zarafaserver.de |
---|
12 | * This file is distributed under GPL v2. |
---|
13 | * Consult LICENSE file for details |
---|
14 | ************************************************/ |
---|
15 | |
---|
16 | ob_start(false, 1048576); |
---|
17 | |
---|
18 | include_once('zpushdefs.php'); |
---|
19 | include_once("config.php"); |
---|
20 | include_once("proto.php"); |
---|
21 | include_once("request.php"); |
---|
22 | include_once("debug.php"); |
---|
23 | include_once("compat.php"); |
---|
24 | include_once("version.php"); |
---|
25 | |
---|
26 | // Attempt to set maximum execution time |
---|
27 | ini_set('max_execution_time', SCRIPT_TIMEOUT); |
---|
28 | set_time_limit(SCRIPT_TIMEOUT); |
---|
29 | |
---|
30 | debugLog("Start"); |
---|
31 | debugLog("Z-Push version: $zpush_version"); |
---|
32 | debugLog("Client IP: ". $_SERVER['REMOTE_ADDR']); |
---|
33 | |
---|
34 | $input = fopen("php://input", "r"); |
---|
35 | $output = fopen("php://output", "w+"); |
---|
36 | |
---|
37 | // The script must always be called with authorisation info |
---|
38 | if(!isset($_SERVER['PHP_AUTH_PW'])) { |
---|
39 | header("WWW-Authenticate: Basic realm=\"ZPush\""); |
---|
40 | header("HTTP/1.1 401 Unauthorized"); |
---|
41 | print("Access denied. Please send authorisation information"); |
---|
42 | debugLog("Access denied: no password sent."); |
---|
43 | debugLog("end"); |
---|
44 | debugLog("--------"); |
---|
45 | return; |
---|
46 | } |
---|
47 | |
---|
48 | // split username & domain if received as one |
---|
49 | $pos = strrpos($_SERVER['PHP_AUTH_USER'], '\\'); |
---|
50 | if($pos === false){ |
---|
51 | $auth_user = $_SERVER['PHP_AUTH_USER']; |
---|
52 | $auth_domain = ''; |
---|
53 | }else{ |
---|
54 | $auth_domain = substr($_SERVER['PHP_AUTH_USER'],0,$pos); |
---|
55 | $auth_user = substr($_SERVER['PHP_AUTH_USER'],$pos+1); |
---|
56 | } |
---|
57 | $auth_pw = $_SERVER['PHP_AUTH_PW']; |
---|
58 | |
---|
59 | $cmd = $user = $devid = $devtype = ""; |
---|
60 | |
---|
61 | // Parse the standard GET parameters |
---|
62 | if(isset($_GET["Cmd"])) |
---|
63 | $cmd = $_GET["Cmd"]; |
---|
64 | if(isset($_GET["User"])) |
---|
65 | $user = $_GET["User"]; |
---|
66 | if(isset($_GET["DeviceId"])) |
---|
67 | $devid = $_GET["DeviceId"]; |
---|
68 | if(isset($_GET["DeviceType"])) |
---|
69 | $devtype = $_GET["DeviceType"]; |
---|
70 | |
---|
71 | // The GET parameters are required |
---|
72 | if($_SERVER["REQUEST_METHOD"] == "POST") { |
---|
73 | if(!isset($user) || !isset($devid) || !isset($devtype)) { |
---|
74 | print("Your device requested the Z-Push URL without the required GET parameters"); |
---|
75 | return; |
---|
76 | } |
---|
77 | } |
---|
78 | |
---|
79 | // Get the request headers so we can see the versions |
---|
80 | $requestheaders = apache_request_headers(); |
---|
81 | if (isset($requestheaders["Ms-Asprotocolversion"])) $requestheaders["MS-ASProtocolVersion"] = $requestheaders["Ms-Asprotocolversion"]; |
---|
82 | if(isset($requestheaders["MS-ASProtocolVersion"])) { |
---|
83 | global $protocolversion; |
---|
84 | |
---|
85 | $protocolversion = $requestheaders["MS-ASProtocolVersion"]; |
---|
86 | debugLog("Client supports version " . $protocolversion); |
---|
87 | } else { |
---|
88 | global $protocolversion; |
---|
89 | |
---|
90 | $protocolversion = "1.0"; |
---|
91 | } |
---|
92 | |
---|
93 | if (isset($requestheaders["X-Ms-Policykey"])) $requestheaders["X-MS-PolicyKey"] = $requestheaders["X-Ms-Policykey"]; |
---|
94 | if (isset($requestheaders["X-MS-PolicyKey"])) { |
---|
95 | global $policykey; |
---|
96 | $policykey = $requestheaders["X-MS-PolicyKey"]; |
---|
97 | |
---|
98 | } else { |
---|
99 | global $policykey; |
---|
100 | $policykey = 0; |
---|
101 | } |
---|
102 | |
---|
103 | //get user agent |
---|
104 | if (isset($requestheaders["User-Agent"])) { |
---|
105 | global $useragent; |
---|
106 | $useragent = $requestheaders["User-Agent"]; |
---|
107 | } else { |
---|
108 | global $useragent; |
---|
109 | $useragent = "unknown"; |
---|
110 | } |
---|
111 | |
---|
112 | // Load our backend driver |
---|
113 | $backend_dir = opendir(BASE_PATH . "/backend"); |
---|
114 | while($entry = readdir($backend_dir)) { |
---|
115 | if(substr($entry,0,1) == "." || substr($entry,-3) != "php") |
---|
116 | continue; |
---|
117 | |
---|
118 | if (!function_exists("mapi_logon") && ($entry == "ics.php")) |
---|
119 | continue; |
---|
120 | |
---|
121 | include_once(BASE_PATH . "/backend/" . $entry); |
---|
122 | } |
---|
123 | |
---|
124 | // Initialize our backend |
---|
125 | $backend = new $BACKEND_PROVIDER(); |
---|
126 | |
---|
127 | if($backend->Logon($auth_user, $auth_domain, $auth_pw) == false) { |
---|
128 | header("HTTP/1.1 401 Unauthorized"); |
---|
129 | header("WWW-Authenticate: Basic realm=\"ZPush\""); |
---|
130 | print("Access denied. Username or password incorrect."); |
---|
131 | debugLog("Access denied: backend logon failed."); |
---|
132 | debugLog("end"); |
---|
133 | debugLog("--------"); |
---|
134 | return; |
---|
135 | } |
---|
136 | |
---|
137 | // $user is usually the same as the PHP_AUTH_USER. This allows you to sync the 'john' account if you |
---|
138 | // have sufficient privileges as user 'joe'. |
---|
139 | if($backend->Setup($user, $devid, $protocolversion) == false) { |
---|
140 | header("HTTP/1.1 401 Unauthorized"); |
---|
141 | header("WWW-Authenticate: Basic realm=\"ZPush\""); |
---|
142 | print("Access denied or user '$user' unknown."); |
---|
143 | debugLog("Access denied: backend setup failed."); |
---|
144 | debugLog("end"); |
---|
145 | debugLog("--------"); |
---|
146 | return; |
---|
147 | } |
---|
148 | |
---|
149 | // check policy header |
---|
150 | if (PROVISIONING === true && $_SERVER["REQUEST_METHOD"] != 'OPTIONS' && $cmd != 'Ping' && $cmd != 'Provision' && |
---|
151 | $backend->CheckPolicy($policykey, $devid) != SYNC_PROVISION_STATUS_SUCCESS && |
---|
152 | (LOOSE_PROVISIONING === false || |
---|
153 | (LOOSE_PROVISIONING === true && isset($requestheaders["X-MS-PolicyKey"])))) { |
---|
154 | |
---|
155 | header("HTTP/1.1 449 Retry after sending a PROVISION command"); |
---|
156 | header("MS-Server-ActiveSync: 6.5.7638.1"); |
---|
157 | header("MS-ASProtocolVersions: 1.0,2.0,2.1,2.5"); |
---|
158 | header("MS-ASProtocolCommands: Sync,SendMail,SmartForward,SmartReply,GetAttachment,GetHierarchy,CreateCollection,DeleteCollection,MoveCollection,FolderSync,FolderCreate,FolderDelete,FolderUpdate,MoveItems,GetItemEstimate,MeetingResponse,Provision,ResolveRecipients,ValidateCert,Search,Ping"); |
---|
159 | header("Cache-Control: private"); |
---|
160 | debugLog("POST cmd $cmd denied: Retry after sending a PROVISION command"); |
---|
161 | debugLog("end"); |
---|
162 | debugLog("--------"); |
---|
163 | return; |
---|
164 | } |
---|
165 | |
---|
166 | // Do the actual request |
---|
167 | switch($_SERVER["REQUEST_METHOD"]) { |
---|
168 | case 'OPTIONS': |
---|
169 | header("MS-Server-ActiveSync: 6.5.7638.1"); |
---|
170 | header("MS-ASProtocolVersions: 1.0,2.0,2.1,2.5"); |
---|
171 | header("MS-ASProtocolCommands: Sync,SendMail,SmartForward,SmartReply,GetAttachment,GetHierarchy,CreateCollection,DeleteCollection,MoveCollection,FolderSync,FolderCreate,FolderDelete,FolderUpdate,MoveItems,GetItemEstimate,MeetingResponse,ResolveRecipients,ValidateCert,Provision,Search,Ping"); |
---|
172 | debugLog("Options request"); |
---|
173 | break; |
---|
174 | case 'POST': |
---|
175 | header("MS-Server-ActiveSync: 6.5.7638.1"); |
---|
176 | debugLog("POST cmd: $cmd"); |
---|
177 | // Do the actual request |
---|
178 | if(!HandleRequest($backend, $cmd, $devid, $protocolversion)) { |
---|
179 | // Request failed. Try to output some kind of error information. We can only do this if |
---|
180 | // output had not started yet. If it has started already, we can't show the user the error, and |
---|
181 | // the device will give its own (useless) error message. |
---|
182 | if(!headers_sent()) { |
---|
183 | header("Content-type: text/html"); |
---|
184 | print("<BODY>\n"); |
---|
185 | print("<h3>Error</h3><p>\n"); |
---|
186 | print("There was a problem processing the <i>$cmd</i> command from your PDA.\n"); |
---|
187 | print("<p>Here is the debug output:<p><pre>\n"); |
---|
188 | print(getDebugInfo()); |
---|
189 | print("</pre>\n"); |
---|
190 | print("</BODY>\n"); |
---|
191 | } |
---|
192 | } |
---|
193 | break; |
---|
194 | case 'GET': |
---|
195 | header("Content-type: text/html"); |
---|
196 | print("<BODY>\n"); |
---|
197 | print("<h3>GET not supported</h3><p>\n"); |
---|
198 | print("This is the z-push location and can only be accessed by Microsoft ActiveSync-capable devices."); |
---|
199 | print("</BODY>\n"); |
---|
200 | break; |
---|
201 | } |
---|
202 | |
---|
203 | |
---|
204 | $len = ob_get_length(); |
---|
205 | $data = ob_get_contents(); |
---|
206 | |
---|
207 | ob_end_clean(); |
---|
208 | |
---|
209 | // Unfortunately, even though zpush can stream the data to the client |
---|
210 | // with a chunked encoding, using chunked encoding also breaks the progress bar |
---|
211 | // on the PDA. So we de-chunk here and just output a content-length header and |
---|
212 | // send it as a 'normal' packet. If the output packet exceeds 1MB (see ob_start) |
---|
213 | // then it will be sent as a chunked packet anyway because PHP will have to flush |
---|
214 | // the buffer. |
---|
215 | |
---|
216 | header("Content-Length: $len"); |
---|
217 | print $data; |
---|
218 | |
---|
219 | // destruct backend after all data is on the stream |
---|
220 | $backend->Logoff(); |
---|
221 | |
---|
222 | debugLog("end"); |
---|
223 | debugLog("--------"); |
---|
224 | ?> |
---|