1 | <?php |
---|
2 | /** |
---|
3 | * Utility functions of a general nature which are used by |
---|
4 | * most AWL library classes. |
---|
5 | * |
---|
6 | * @package awl |
---|
7 | * @subpackage Utilities |
---|
8 | * @author Andrew McMillan <andrew@mcmillan.net.nz> |
---|
9 | * @copyright Catalyst IT Ltd, Morphoss Ltd <http://www.morphoss.com/> |
---|
10 | * @license http://www.gnu.org/licenses/lgpl-3.0.txt GNU LGPL version 3 or later |
---|
11 | */ |
---|
12 | |
---|
13 | if ( !function_exists('dbg_error_log') ) { |
---|
14 | /** |
---|
15 | * Writes a debug message into the error log using printf syntax. If the first |
---|
16 | * parameter is "ERROR" then the message will _always_ be logged. |
---|
17 | * Otherwise, the first parameter is a "component" name, and will only be logged |
---|
18 | * if $c->dbg["component"] is set to some non-null value. |
---|
19 | * |
---|
20 | * If you want to see every log message then $c->dbg["ALL"] can be set, to |
---|
21 | * override the debugging status of the individual components. |
---|
22 | * |
---|
23 | * @var string $component The component to identify itself, or "ERROR", or "LOG:component" |
---|
24 | * @var string $format A format string for the log message |
---|
25 | * @var [string $parameter ...] Parameters for the format string. |
---|
26 | */ |
---|
27 | function dbg_error_log() { |
---|
28 | global $c; |
---|
29 | $args = func_get_args(); |
---|
30 | $type = "DBG"; |
---|
31 | $component = array_shift($args); |
---|
32 | if ( substr( $component, 0, 3) == "LOG" ) { |
---|
33 | // Special escape case for stuff that always gets logged. |
---|
34 | $type = 'LOG'; |
---|
35 | $component = substr($component,4); |
---|
36 | } |
---|
37 | else if ( $component == "ERROR" ) { |
---|
38 | $type = "***"; |
---|
39 | } |
---|
40 | else if ( isset($c->dbg["ALL"]) ) { |
---|
41 | $type = "ALL"; |
---|
42 | } |
---|
43 | else if ( !isset($c->dbg[strtolower($component)]) ) return; |
---|
44 | |
---|
45 | $argc = func_num_args(); |
---|
46 | if ( 2 <= $argc ) { |
---|
47 | $format = array_shift($args); |
---|
48 | } |
---|
49 | else { |
---|
50 | $format = "%s"; |
---|
51 | } |
---|
52 | @error_log( $c->sysabbr.": $type: $component:". vsprintf( $format, $args ) ); |
---|
53 | } |
---|
54 | } |
---|
55 | |
---|
56 | |
---|
57 | if ( !function_exists('fatal') ) { |
---|
58 | function fatal() { |
---|
59 | global $c; |
---|
60 | $args = func_get_args(); |
---|
61 | $argc = func_num_args(); |
---|
62 | if ( 2 <= $argc ) { |
---|
63 | $format = array_shift($args); |
---|
64 | } |
---|
65 | else { |
---|
66 | $format = "%s"; |
---|
67 | } |
---|
68 | @error_log( $c->sysabbr.": FATAL: $component:". vsprintf( $format, $args ) ); |
---|
69 | |
---|
70 | @error_log( "================= Stack Trace ===================" ); |
---|
71 | |
---|
72 | $trace = array_reverse(debug_backtrace()); |
---|
73 | array_pop($trace); |
---|
74 | foreach( $trace AS $k => $v ) { |
---|
75 | @error_log( sprintf(" ===> %s[%d] calls %s%s%s()", |
---|
76 | $v['file'], |
---|
77 | $v['line'], |
---|
78 | (isset($v['class'])?$v['class']:''), |
---|
79 | (isset($v['type'])?$v['type']:''), |
---|
80 | (isset($v['function'])?$v['function']:'') |
---|
81 | )); |
---|
82 | } |
---|
83 | echo "Fatal Error"; |
---|
84 | exit(); |
---|
85 | } |
---|
86 | } |
---|
87 | |
---|
88 | |
---|
89 | if ( !function_exists('trace_bug') ) { |
---|
90 | /** |
---|
91 | * Not as sever as a fatal() call, but we want to log and trace it |
---|
92 | */ |
---|
93 | function trace_bug() { |
---|
94 | global $c; |
---|
95 | $args = func_get_args(); |
---|
96 | $argc = func_num_args(); |
---|
97 | if ( 2 <= $argc ) { |
---|
98 | $format = array_shift($args); |
---|
99 | } |
---|
100 | else { |
---|
101 | $format = "%s"; |
---|
102 | } |
---|
103 | @error_log( $c->sysabbr.": BUG: $component:". vsprintf( $format, $args ) ); |
---|
104 | |
---|
105 | @error_log( "================= Stack Trace ===================" ); |
---|
106 | |
---|
107 | $trace = array_reverse(debug_backtrace()); |
---|
108 | array_pop($trace); |
---|
109 | foreach( $trace AS $k => $v ) { |
---|
110 | @error_log( sprintf(" ===> %s[%d] calls %s%s%s()", |
---|
111 | $v['file'], |
---|
112 | $v['line'], |
---|
113 | (isset($v['class'])?$v['class']:''), |
---|
114 | (isset($v['type'])?$v['type']:''), |
---|
115 | (isset($v['function'])?$v['function']:'') |
---|
116 | )); |
---|
117 | } |
---|
118 | } |
---|
119 | } |
---|
120 | |
---|
121 | |
---|
122 | if ( !function_exists('apache_request_headers') ) { |
---|
123 | /** |
---|
124 | * Compatibility so we can use the apache function name and still work with CGI |
---|
125 | * @package awl |
---|
126 | */ |
---|
127 | eval(' |
---|
128 | function apache_request_headers() { |
---|
129 | foreach($_SERVER as $key=>$value) { |
---|
130 | if (substr($key,0,5)=="HTTP_") { |
---|
131 | $key=str_replace(" ","-",ucwords(strtolower(str_replace("_"," ",substr($key,5))))); |
---|
132 | $out[$key]=$value; |
---|
133 | } |
---|
134 | } |
---|
135 | return $out; |
---|
136 | } |
---|
137 | '); |
---|
138 | } |
---|
139 | |
---|
140 | |
---|
141 | |
---|
142 | if ( !function_exists('dbg_log_array') ) { |
---|
143 | /** |
---|
144 | * Function to dump an array to the error log, possibly recursively |
---|
145 | * |
---|
146 | * @var string $component Which component should this log message identify itself from |
---|
147 | * @var string $name What name should this array dump identify itself as |
---|
148 | * @var array $arr The array to be dumped. |
---|
149 | * @var boolean $recursive Should the dump recurse into arrays/objects in the array |
---|
150 | */ |
---|
151 | function dbg_log_array( $component, $name, $arr, $recursive = false ) { |
---|
152 | if ( !isset($arr) || (gettype($arr) != 'array' && gettype($arr) != 'object') ) { |
---|
153 | dbg_error_log( $component, "%s: array is not set, or is not an array!", $name); |
---|
154 | return; |
---|
155 | } |
---|
156 | foreach ($arr as $key => $value) { |
---|
157 | dbg_error_log( $component, "%s: >>%s<< = >>%s<<", $name, $key, |
---|
158 | (gettype($value) == 'array' || gettype($value) == 'object' ? gettype($value) : $value) ); |
---|
159 | if ( $recursive && (gettype($value) == 'array' || (gettype($value) == 'object' && "$key" != 'self' && "$key" != 'parent') ) ) { |
---|
160 | dbg_log_array( $component, "$name"."[$key]", $value, $recursive ); |
---|
161 | } |
---|
162 | } |
---|
163 | } |
---|
164 | } |
---|
165 | |
---|
166 | |
---|
167 | |
---|
168 | if ( !function_exists("session_simple_md5") ) { |
---|
169 | /** |
---|
170 | * Make a plain MD5 hash of a string, identifying the type of hash it is |
---|
171 | * |
---|
172 | * @param string $instr The string to be salted and MD5'd |
---|
173 | * @return string The *MD5* and the MD5 of the string |
---|
174 | */ |
---|
175 | function session_simple_md5( $instr ) { |
---|
176 | global $c; |
---|
177 | if ( isset($c->dbg['password']) ) dbg_error_log( "Login", "Making plain MD5: instr=$instr, md5($instr)=".md5($instr) ); |
---|
178 | return ( '*MD5*'. md5($instr) ); |
---|
179 | } |
---|
180 | } |
---|
181 | |
---|
182 | |
---|
183 | |
---|
184 | if ( !function_exists("session_salted_md5") ) { |
---|
185 | /** |
---|
186 | * Make a salted MD5 string, given a string and (possibly) a salt. |
---|
187 | * |
---|
188 | * If no salt is supplied we will generate a random one. |
---|
189 | * |
---|
190 | * @param string $instr The string to be salted and MD5'd |
---|
191 | * @param string $salt Some salt to sprinkle into the string to be MD5'd so we don't get the same PW always hashing to the same value. |
---|
192 | * @return string The salt, a * and the MD5 of the salted string, as in SALT*SALTEDHASH |
---|
193 | */ |
---|
194 | function session_salted_md5( $instr, $salt = "" ) { |
---|
195 | if ( $salt == "" ) $salt = substr( md5(rand(100000,999999)), 2, 8); |
---|
196 | global $c; |
---|
197 | if ( isset($c->dbg['password']) ) dbg_error_log( "Login", "Making salted MD5: salt=$salt, instr=$instr, md5($salt$instr)=".md5($salt . $instr) ); |
---|
198 | return ( sprintf("*%s*%s", $salt, md5($salt . $instr) ) ); |
---|
199 | } |
---|
200 | } |
---|
201 | |
---|
202 | |
---|
203 | |
---|
204 | if ( !function_exists("session_salted_sha1") ) { |
---|
205 | /** |
---|
206 | * Make a salted SHA1 string, given a string and (possibly) a salt. PHP5 only (although it |
---|
207 | * could be made to work on PHP4 (@see http://www.openldap.org/faq/data/cache/347.html). The |
---|
208 | * algorithm used here is compatible with OpenLDAP so passwords generated through this function |
---|
209 | * should be able to be migrated to OpenLDAP by using the part following the second '*', i.e. |
---|
210 | * the '{SSHA}....' part. |
---|
211 | * |
---|
212 | * If no salt is supplied we will generate a random one. |
---|
213 | * |
---|
214 | * @param string $instr The string to be salted and SHA1'd |
---|
215 | * @param string $salt Some salt to sprinkle into the string to be SHA1'd so we don't get the same PW always hashing to the same value. |
---|
216 | * @return string A *, the salt, a * and the SHA1 of the salted string, as in *SALT*SALTEDHASH |
---|
217 | */ |
---|
218 | function session_salted_sha1( $instr, $salt = "" ) { |
---|
219 | if ( $salt == "" ) $salt = substr( str_replace('*','',base64_encode(sha1(rand(100000,9999999),true))), 2, 9); |
---|
220 | global $c; |
---|
221 | if ( isset($c->dbg['password']) ) dbg_error_log( "Login", "Making salted SHA1: salt=$salt, instr=$instr, encoded($instr$salt)=".base64_encode(sha1($instr . $salt, true).$salt) ); |
---|
222 | return ( sprintf("*%s*{SSHA}%s", $salt, base64_encode(sha1($instr.$salt, true) . $salt ) ) ); |
---|
223 | } |
---|
224 | } |
---|
225 | |
---|
226 | |
---|
227 | if ( !function_exists("session_validate_password") ) { |
---|
228 | /** |
---|
229 | * Checks what a user entered against the actual password on their account. |
---|
230 | * @param string $they_sent What the user entered. |
---|
231 | * @param string $we_have What we have in the database as their password. Which may (or may not) be a salted MD5. |
---|
232 | * @return boolean Whether or not the users attempt matches what is already on file. |
---|
233 | */ |
---|
234 | function session_validate_password( $they_sent, $we_have ) { |
---|
235 | if ( preg_match('/^\*\*.+$/', $we_have ) ) { |
---|
236 | // The "forced" style of "**plaintext" to allow easier admin setting |
---|
237 | return ( "**$they_sent" == $we_have ); |
---|
238 | } |
---|
239 | |
---|
240 | if ( preg_match('/^\*(.+)\*{[A-Z]+}.+$/', $we_have, $regs ) ) { |
---|
241 | if ( function_exists("session_salted_sha1") ) { |
---|
242 | // A nicely salted sha1sum like "*<salt>*{SSHA}<salted_sha1>" |
---|
243 | $salt = $regs[1]; |
---|
244 | $sha1_sent = session_salted_sha1( $they_sent, $salt ) ; |
---|
245 | return ( $sha1_sent == $we_have ); |
---|
246 | } |
---|
247 | else { |
---|
248 | dbg_error_log( "ERROR", "Password is salted SHA-1 but you are using PHP4!" ); |
---|
249 | echo <<<EOERRMSG |
---|
250 | <html> |
---|
251 | <head> |
---|
252 | <title>Salted SHA1 Password format not supported with PHP4</title> |
---|
253 | </head> |
---|
254 | <body> |
---|
255 | <h1>Salted SHA1 Password format not supported with PHP4</h1> |
---|
256 | <p>At some point you have used PHP5 to set the password for this user and now you are |
---|
257 | using PHP4. You will need to assign a new password to this user using PHP4, or ensure |
---|
258 | you use PHP5 everywhere (recommended).</p> |
---|
259 | <p>AWL has now switched to using salted SHA-1 passwords by preference in a format |
---|
260 | compatible with OpenLDAP.</p> |
---|
261 | </body> |
---|
262 | </html> |
---|
263 | EOERRMSG; |
---|
264 | exit; |
---|
265 | } |
---|
266 | } |
---|
267 | |
---|
268 | if ( preg_match('/^\*MD5\*.+$/', $we_have, $regs ) ) { |
---|
269 | // A crappy unsalted md5sum like "*MD5*<md5>" |
---|
270 | $md5_sent = session_simple_md5( $they_sent ) ; |
---|
271 | return ( $md5_sent == $we_have ); |
---|
272 | } |
---|
273 | else if ( preg_match('/^\*(.+)\*.+$/', $we_have, $regs ) ) { |
---|
274 | // A nicely salted md5sum like "*<salt>*<salted_md5>" |
---|
275 | $salt = $regs[1]; |
---|
276 | $md5_sent = session_salted_md5( $they_sent, $salt ) ; |
---|
277 | return ( $md5_sent == $we_have ); |
---|
278 | } |
---|
279 | |
---|
280 | // Anything else is bad |
---|
281 | return false; |
---|
282 | |
---|
283 | } |
---|
284 | } |
---|
285 | |
---|
286 | |
---|
287 | |
---|
288 | if ( !function_exists("replace_uri_params") ) { |
---|
289 | /** |
---|
290 | * Given a URL (presumably the current one) and a parameter, replace the value of parameter, |
---|
291 | * extending the URL as necessary if the parameter is not already there. |
---|
292 | * @param string $uri The URI we will be replacing parameters in. |
---|
293 | * @param array $replacements An array of replacement pairs array( "replace_this" => "with this" ) |
---|
294 | * @return string The URI with the replacements done. |
---|
295 | */ |
---|
296 | function replace_uri_params( $uri, $replacements ) { |
---|
297 | $replaced = $uri; |
---|
298 | foreach( $replacements AS $param => $new_value ) { |
---|
299 | $rxp = preg_replace( '/([\[\]])/', '\\\\$1', $param ); // Some parameters may be arrays. |
---|
300 | $regex = "/([&?])($rxp)=([^&]+)/"; |
---|
301 | dbg_error_log("core", "Looking for [%s] to replace with [%s] regex is %s and searching [%s]", $param, $new_value, $regex, $replaced ); |
---|
302 | if ( preg_match( $regex, $replaced ) ) |
---|
303 | $replaced = preg_replace( $regex, "\$1$param=$new_value", $replaced); |
---|
304 | else |
---|
305 | $replaced .= "&$param=$new_value"; |
---|
306 | } |
---|
307 | if ( ! preg_match( '/\?/', $replaced ) ) { |
---|
308 | $replaced = preg_replace("/&(.+)$/", "?\$1", $replaced); |
---|
309 | } |
---|
310 | $replaced = str_replace("&", "--AmPeRsAnD--", $replaced); |
---|
311 | $replaced = str_replace("&", "&", $replaced); |
---|
312 | $replaced = str_replace("--AmPeRsAnD--", "&", $replaced); |
---|
313 | dbg_error_log("core", "URI <<$uri>> morphed to <<$replaced>>"); |
---|
314 | return $replaced; |
---|
315 | } |
---|
316 | } |
---|
317 | |
---|
318 | |
---|
319 | if ( !function_exists("uuid") ) { |
---|
320 | /** |
---|
321 | * Generates a Universally Unique IDentifier, version 4. |
---|
322 | * |
---|
323 | * RFC 4122 (http://www.ietf.org/rfc/rfc4122.txt) defines a special type of Globally |
---|
324 | * Unique IDentifiers (GUID), as well as several methods for producing them. One |
---|
325 | * such method, described in section 4.4, is based on truly random or pseudo-random |
---|
326 | * number generators, and is therefore implementable in a language like PHP. |
---|
327 | * |
---|
328 | * We choose to produce pseudo-random numbers with the Mersenne Twister, and to always |
---|
329 | * limit single generated numbers to 16 bits (ie. the decimal value 65535). That is |
---|
330 | * because, even on 32-bit systems, PHP's RAND_MAX will often be the maximum *signed* |
---|
331 | * value, with only the equivalent of 31 significant bits. Producing two 16-bit random |
---|
332 | * numbers to make up a 32-bit one is less efficient, but guarantees that all 32 bits |
---|
333 | * are random. |
---|
334 | * |
---|
335 | * The algorithm for version 4 UUIDs (ie. those based on random number generators) |
---|
336 | * states that all 128 bits separated into the various fields (32 bits, 16 bits, 16 bits, |
---|
337 | * 8 bits and 8 bits, 48 bits) should be random, except : (a) the version number should |
---|
338 | * be the last 4 bits in the 3rd field, and (b) bits 6 and 7 of the 4th field should |
---|
339 | * be 01. We try to conform to that definition as efficiently as possible, generating |
---|
340 | * smaller values where possible, and minimizing the number of base conversions. |
---|
341 | * |
---|
342 | * @copyright Copyright (c) CFD Labs, 2006. This function may be used freely for |
---|
343 | * any purpose ; it is distributed without any form of warranty whatsoever. |
---|
344 | * @author David Holmes <dholmes@cfdsoftware.net> |
---|
345 | * |
---|
346 | * @return string A UUID, made up of 32 hex digits and 4 hyphens. |
---|
347 | */ |
---|
348 | |
---|
349 | function uuid() { |
---|
350 | |
---|
351 | // The field names refer to RFC 4122 section 4.1.2 |
---|
352 | |
---|
353 | return sprintf('%04x%04x-%04x-%03x4-%04x-%04x%04x%04x', |
---|
354 | mt_rand(0, 65535), mt_rand(0, 65535), // 32 bits for "time_low" |
---|
355 | mt_rand(0, 65535), // 16 bits for "time_mid" |
---|
356 | mt_rand(0, 4095), // 12 bits before the 0100 of (version) 4 for "time_hi_and_version" |
---|
357 | bindec(substr_replace(sprintf('%016b', mt_rand(0, 65535)), '01', 6, 2)), |
---|
358 | // 8 bits, the last two of which (positions 6 and 7) are 01, for "clk_seq_hi_res" |
---|
359 | // (hence, the 2nd hex digit after the 3rd hyphen can only be 1, 5, 9 or d) |
---|
360 | // 8 bits for "clk_seq_low" |
---|
361 | mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535) // 48 bits for "node" |
---|
362 | ); |
---|
363 | } |
---|
364 | } |
---|
365 | |
---|
366 | if ( !function_exists("translate") ) { |
---|
367 | require_once ROOTPATH.'/plugins/davicalCliente/Translation.php'; |
---|
368 | |
---|
369 | } |
---|
370 | |
---|
371 | if ( !function_exists("clone") && version_compare(phpversion(), '5.0') < 0) { |
---|
372 | /** |
---|
373 | * PHP5 screws with the assignment operator changing so that $a = $b means that |
---|
374 | * $a becomes a reference to $b. There is a clone() that we can use in PHP5, so |
---|
375 | * we have to emulate that for PHP4. Bleargh. |
---|
376 | */ |
---|
377 | eval( 'function clone($object) { return $object; }' ); |
---|
378 | } |
---|
379 | |
---|
380 | if ( !function_exists("quoted_printable_encode") ) { |
---|
381 | /** |
---|
382 | * Process a string to fit the requirements of RFC2045 section 6.7. Note that |
---|
383 | * this works, but replaces more characters than the minimum set. For readability |
---|
384 | * the spaces aren't encoded as =20 though. |
---|
385 | */ |
---|
386 | function quoted_printable_encode($string) { |
---|
387 | return preg_replace('/[^\r\n]{73}[^=\r\n]{2}/', "$0=\r\n", str_replace("%","=",str_replace("%20"," ",rawurlencode($string)))); |
---|
388 | } |
---|
389 | } |
---|
390 | |
---|
391 | |
---|
392 | if ( !function_exists("check_by_regex") ) { |
---|
393 | /** |
---|
394 | * Verify a value is OK by testing a regex against it. If it is an array apply it to |
---|
395 | * each element in the array recursively. If it is an object we don't mess |
---|
396 | * with it. |
---|
397 | */ |
---|
398 | function check_by_regex( $val, $regex ) { |
---|
399 | if ( is_null($val) ) return null; |
---|
400 | switch( $regex ) { |
---|
401 | case 'int': $regex = '#^\d+$#'; break; |
---|
402 | } |
---|
403 | if ( is_array($val) ) { |
---|
404 | foreach( $val AS $k => $v ) { |
---|
405 | $val[$k] = check_by_regex($v,$regex); |
---|
406 | } |
---|
407 | } |
---|
408 | else if ( ! is_object($val) ) { |
---|
409 | if ( preg_match( $regex, $val, $matches) ) { |
---|
410 | $val = $matches[0]; |
---|
411 | } |
---|
412 | else { |
---|
413 | $val = ''; |
---|
414 | } |
---|
415 | } |
---|
416 | return $val; |
---|
417 | } |
---|
418 | } |
---|
419 | |
---|
420 | |
---|
421 | if ( !function_exists("param_to_global") ) { |
---|
422 | /** |
---|
423 | * Convert a parameter to a global. We first look in _POST and then in _GET, |
---|
424 | * and if they passed in a bunch of valid characters, we will make sure the |
---|
425 | * incoming is cleaned to only match that set. |
---|
426 | * |
---|
427 | * @param string $varname The name of the global variable to put the answer in |
---|
428 | * @param string $match_regex The part of the parameter matching this regex will be returned |
---|
429 | * @param string $alias1 An alias for the name that we should look for first. |
---|
430 | * @param " ... More aliases, in the order which they should be examined. $varname will be appended to the end. |
---|
431 | */ |
---|
432 | function param_to_global( ) { |
---|
433 | $args = func_get_args(); |
---|
434 | |
---|
435 | $varname = array_shift($args); |
---|
436 | $GLOBALS[$varname] = null; |
---|
437 | |
---|
438 | $match_regex = null; |
---|
439 | $argc = func_num_args(); |
---|
440 | if ( $argc > 1 ) { |
---|
441 | $match_regex = array_shift($args); |
---|
442 | } |
---|
443 | |
---|
444 | $args[] = $varname; |
---|
445 | foreach( $args AS $k => $name ) { |
---|
446 | if ( isset($_POST[$name]) ) { |
---|
447 | $result = $_POST[$name]; |
---|
448 | break; |
---|
449 | } |
---|
450 | else if ( isset($_GET[$name]) ) { |
---|
451 | $result = $_GET[$name]; |
---|
452 | break; |
---|
453 | } |
---|
454 | } |
---|
455 | if ( !isset($result) ) return null; |
---|
456 | |
---|
457 | if ( isset($match_regex) ) { |
---|
458 | $result = check_by_regex( $result, $match_regex ); |
---|
459 | } |
---|
460 | |
---|
461 | $GLOBALS[$varname] = $result; |
---|
462 | return $result; |
---|
463 | } |
---|
464 | } |
---|
465 | |
---|
466 | |
---|
467 | if ( !function_exists("get_fields") ) { |
---|
468 | /** |
---|
469 | * @var array $_AWL_field_cache is a cache of the field names for a table |
---|
470 | */ |
---|
471 | $_AWL_field_cache = array(); |
---|
472 | |
---|
473 | /** |
---|
474 | * Get the names of the fields for a particular table |
---|
475 | * @param string $tablename The name of the table. |
---|
476 | * @return array of string The public fields in the table. |
---|
477 | */ |
---|
478 | function get_fields( $tablename ) { |
---|
479 | global $_AWL_field_cache; |
---|
480 | |
---|
481 | if ( !isset($_AWL_field_cache[$tablename]) ) { |
---|
482 | dbg_error_log( "core", ":get_fields: Loading fields for table '$tablename'" ); |
---|
483 | $qry = new AwlQuery(); |
---|
484 | $db = $qry->GetConnection(); |
---|
485 | $qry->SetSQL($db->GetFields($tablename)); |
---|
486 | $qry->Exec("core"); |
---|
487 | $fields = array(); |
---|
488 | while( $row = $qry->Fetch() ) { |
---|
489 | $fields[$row->fieldname] = $row->typename . ($row->precision >= 0 ? sprintf('(%d)',$row->precision) : ''); |
---|
490 | } |
---|
491 | $_AWL_field_cache[$tablename] = $fields; |
---|
492 | } |
---|
493 | return $_AWL_field_cache[$tablename]; |
---|
494 | } |
---|
495 | } |
---|
496 | |
---|
497 | |
---|
498 | if ( !function_exists("force_utf8") ) { |
---|
499 | function define_byte_mappings() { |
---|
500 | global $byte_map, $nibble_good_chars; |
---|
501 | |
---|
502 | # Needed for using Grant McLean's byte mappings code |
---|
503 | $ascii_char = '[\x00-\x7F]'; |
---|
504 | $cont_byte = '[\x80-\xBF]'; |
---|
505 | |
---|
506 | $utf8_2 = '[\xC0-\xDF]' . $cont_byte; |
---|
507 | $utf8_3 = '[\xE0-\xEF]' . $cont_byte . '{2}'; |
---|
508 | $utf8_4 = '[\xF0-\xF7]' . $cont_byte . '{3}'; |
---|
509 | $utf8_5 = '[\xF8-\xFB]' . $cont_byte . '{4}'; |
---|
510 | |
---|
511 | $nibble_good_chars = "/^($ascii_char+|$utf8_2|$utf8_3|$utf8_4|$utf8_5)(.*)$/s"; |
---|
512 | |
---|
513 | # From http://unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1252.TXT |
---|
514 | $byte_map = array( |
---|
515 | "\x80" => "\xE2\x82\xAC", # EURO SIGN |
---|
516 | "\x82" => "\xE2\x80\x9A", # SINGLE LOW-9 QUOTATION MARK |
---|
517 | "\x83" => "\xC6\x92", # LATIN SMALL LETTER F WITH HOOK |
---|
518 | "\x84" => "\xE2\x80\x9E", # DOUBLE LOW-9 QUOTATION MARK |
---|
519 | "\x85" => "\xE2\x80\xA6", # HORIZONTAL ELLIPSIS |
---|
520 | "\x86" => "\xE2\x80\xA0", # DAGGER |
---|
521 | "\x87" => "\xE2\x80\xA1", # DOUBLE DAGGER |
---|
522 | "\x88" => "\xCB\x86", # MODIFIER LETTER CIRCUMFLEX ACCENT |
---|
523 | "\x89" => "\xE2\x80\xB0", # PER MILLE SIGN |
---|
524 | "\x8A" => "\xC5\xA0", # LATIN CAPITAL LETTER S WITH CARON |
---|
525 | "\x8B" => "\xE2\x80\xB9", # SINGLE LEFT-POINTING ANGLE QUOTATION MARK |
---|
526 | "\x8C" => "\xC5\x92", # LATIN CAPITAL LIGATURE OE |
---|
527 | "\x8E" => "\xC5\xBD", # LATIN CAPITAL LETTER Z WITH CARON |
---|
528 | "\x91" => "\xE2\x80\x98", # LEFT SINGLE QUOTATION MARK |
---|
529 | "\x92" => "\xE2\x80\x99", # RIGHT SINGLE QUOTATION MARK |
---|
530 | "\x93" => "\xE2\x80\x9C", # LEFT DOUBLE QUOTATION MARK |
---|
531 | "\x94" => "\xE2\x80\x9D", # RIGHT DOUBLE QUOTATION MARK |
---|
532 | "\x95" => "\xE2\x80\xA2", # BULLET |
---|
533 | "\x96" => "\xE2\x80\x93", # EN DASH |
---|
534 | "\x97" => "\xE2\x80\x94", # EM DASH |
---|
535 | "\x98" => "\xCB\x9C", # SMALL TILDE |
---|
536 | "\x99" => "\xE2\x84\xA2", # TRADE MARK SIGN |
---|
537 | "\x9A" => "\xC5\xA1", # LATIN SMALL LETTER S WITH CARON |
---|
538 | "\x9B" => "\xE2\x80\xBA", # SINGLE RIGHT-POINTING ANGLE QUOTATION MARK |
---|
539 | "\x9C" => "\xC5\x93", # LATIN SMALL LIGATURE OE |
---|
540 | "\x9E" => "\xC5\xBE", # LATIN SMALL LETTER Z WITH CARON |
---|
541 | "\x9F" => "\xC5\xB8", # LATIN CAPITAL LETTER Y WITH DIAERESIS |
---|
542 | ); |
---|
543 | |
---|
544 | for( $i=160; $i < 256; $i++ ) { |
---|
545 | $ch = chr($i); |
---|
546 | $byte_map[$ch] = iconv('ISO-8859-1', 'UTF-8', $ch); |
---|
547 | } |
---|
548 | } |
---|
549 | define_byte_mappings(); |
---|
550 | |
---|
551 | function force_utf8( $input ) { |
---|
552 | global $byte_map, $nibble_good_chars; |
---|
553 | |
---|
554 | $output = ''; |
---|
555 | $char = ''; |
---|
556 | $rest = ''; |
---|
557 | while( $input != '' ) { |
---|
558 | if ( preg_match( $nibble_good_chars, $input, $matches ) ) { |
---|
559 | $output .= $matches[1]; |
---|
560 | $rest = $matches[2]; |
---|
561 | } |
---|
562 | else { |
---|
563 | preg_match( '/^(.)(.*)$/s', $input, $matches ); |
---|
564 | $char = $matches[1]; |
---|
565 | $rest = $matches[2]; |
---|
566 | if ( isset($byte_map[$char]) ) { |
---|
567 | $output .= $byte_map[$char]; |
---|
568 | } |
---|
569 | else { |
---|
570 | # Must be valid UTF8 already |
---|
571 | $output .= $char; |
---|
572 | } |
---|
573 | } |
---|
574 | $input = $rest; |
---|
575 | } |
---|
576 | return $output; |
---|
577 | } |
---|
578 | |
---|
579 | } |
---|
580 | |
---|
581 | |
---|
582 | /** |
---|
583 | * Try and extract something like "Pacific/Auckland" or "America/Indiana/Indianapolis" if possible. |
---|
584 | */ |
---|
585 | function olson_from_tzstring( $tzstring ) { |
---|
586 | global $c; |
---|
587 | |
---|
588 | if ( function_exists('timezone_identifiers_list') && in_array($tzstring,timezone_identifiers_list()) ) return $tzstring; |
---|
589 | if ( preg_match( '{((Antarctica|America|Africa|Atlantic|Asia|Australia|Indian|Europe|Pacific)/(([^/]+)/)?[^/]+)$}', $tzstring, $matches ) ) { |
---|
590 | // dbg_error_log( 'INFO', 'Found timezone "%s" from string "%s"', $matches[1], $tzstring ); |
---|
591 | return $matches[1]; |
---|
592 | } |
---|
593 | switch( $tzstring ) { |
---|
594 | case 'New Zealand Standard Time': case 'New Zealand Daylight Time': |
---|
595 | return 'Pacific/Auckland'; |
---|
596 | break; |
---|
597 | case 'Central Standard Time': case 'Central Daylight Time': case 'US/Central': |
---|
598 | return 'America/Chicago'; |
---|
599 | break; |
---|
600 | case 'Eastern Standard Time': case 'Eastern Daylight Time': case 'US/Eastern': |
---|
601 | case '(UTC-05:00) Eastern Time (US & Canada)': |
---|
602 | return 'America/New_York'; |
---|
603 | break; |
---|
604 | case 'Pacific Standard Time': case 'Pacific Daylight Time': case 'US/Pacific': |
---|
605 | return 'America/Los_Angeles'; |
---|
606 | break; |
---|
607 | case 'Mountain Standard Time': case 'Mountain Daylight Time': case 'US/Mountain': case 'Mountain Time': |
---|
608 | return 'America/Denver'; |
---|
609 | // The US 'Mountain Time' can in fact be America/(Denver|Boise|Phoenix|Shiprock) which |
---|
610 | // all vary to some extent due to differing DST rules. |
---|
611 | break; |
---|
612 | case '(GMT-07.00) Arizona': |
---|
613 | return 'America/Phoenix'; |
---|
614 | break; |
---|
615 | default: |
---|
616 | if ( isset($c->timezone_translations) && is_array($c->timezone_translations) |
---|
617 | && !empty($c->timezone_translations[$tzstring]) ) |
---|
618 | return $c->timezone_translations[$tzstring]; |
---|
619 | } |
---|
620 | return null; |
---|
621 | } |
---|
622 | |
---|
623 | if ( !function_exists("deprecated") ) { |
---|
624 | function deprecated( $method ) { |
---|
625 | global $c; |
---|
626 | if ( isset($c->dbg['ALL']) || isset($c->dbg['deprecated']) ) { |
---|
627 | $stack = debug_backtrace(); |
---|
628 | array_shift($stack); |
---|
629 | if ( preg_match( '{/inc/iCalendar.php$}', $stack[0]['file'] ) && $stack[0]['line'] > __LINE__ ) return; |
---|
630 | @error_log( sprintf( $c->sysabbr.':DEPRECATED: Call to deprecated method "%s"', $method)); |
---|
631 | foreach( $stack AS $k => $v ) { |
---|
632 | @error_log( sprintf( $c->sysabbr.': ==> called from line %4d of %s', $v['line'], $v['file'])); |
---|
633 | } |
---|
634 | } |
---|
635 | } |
---|
636 | } |
---|
637 | |
---|
638 | |
---|
639 | if ( !function_exists("gzdecode") ) { |
---|
640 | function gzdecode( $instring ) { |
---|
641 | global $c; |
---|
642 | if ( !isset($c->use_pipe_gunzip) || $c->use_pipe_gunzip ) { |
---|
643 | $descriptorspec = array( |
---|
644 | 0 => array("pipe", "r"), // stdin is a pipe that the child will read from |
---|
645 | 1 => array("pipe", "w"), // stdout is a pipe that the child will write to |
---|
646 | 2 => array("file", "/dev/null", "a") // stderr is discarded |
---|
647 | ); |
---|
648 | $process = proc_open('gunzip',$descriptorspec, $pipes); |
---|
649 | if ( is_resource($process) ) { |
---|
650 | fwrite($pipes[0],$instring); |
---|
651 | fclose($pipes[0]); |
---|
652 | |
---|
653 | $outstring = stream_get_contents($pipes[1]); |
---|
654 | fclose($pipes[1]); |
---|
655 | |
---|
656 | proc_close($process); |
---|
657 | return $outstring; |
---|
658 | } |
---|
659 | return ''; |
---|
660 | } |
---|
661 | else { |
---|
662 | $g=tempnam('./','gz'); |
---|
663 | file_put_contents($g,$instring); |
---|
664 | ob_start(); |
---|
665 | readgzfile($g); |
---|
666 | $d=ob_get_clean(); |
---|
667 | unlink($g); |
---|
668 | return $d; |
---|
669 | } |
---|
670 | } |
---|
671 | } |
---|
672 | |
---|
673 | /** |
---|
674 | * Return the AWL version |
---|
675 | */ |
---|
676 | function awl_version() { |
---|
677 | global $c; |
---|
678 | $c->awl_library_version = 0.49; |
---|
679 | return $c->awl_library_version; |
---|
680 | } |
---|