Release of 1.2.0 complete. Woohoo!
[squirrelmail.git] / functions / strings.php
CommitLineData
59177427 1<?php
7350889b 2
35586184 3/**
4 * strings.php
5 *
15e6162e 6 * Copyright (c) 1999-2002 The SquirrelMail Project Team
35586184 7 * Licensed under the GNU GPL. For full terms see the file COPYING.
8 *
9 * This code provides various string manipulation functions that are
10 * used by the rest of the Squirrelmail code.
11 *
12 * $Id$
13 */
9374671f 14
35586184 15/*****************************************************************/
16/*** THIS FILE NEEDS TO HAVE ITS FORMATTING FIXED!!! ***/
17/*** PLEASE DO SO AND REMOVE THIS COMMENT SECTION. ***/
18/*** + Base level indent should begin at left margin, as ***/
19/*** the comment and $version stuff below. ***/
20/*** + All identation should consist of four space blocks ***/
21/*** + Tab characters are evil. ***/
22/*** + all comments should use "slash-star ... star-slash" ***/
23/*** style -- no pound characters, no slash-slash style ***/
24/*** + FLOW CONTROL STATEMENTS (if, while, etc) SHOULD ***/
25/*** ALWAYS USE { AND } CHARACTERS!!! ***/
26/*** + Please use ' instead of ", when possible. Note " ***/
27/*** should always be used in _( ) function calls. ***/
28/*** Thank you for your help making the SM code more readable. ***/
29/*****************************************************************/
30
31/**
32 * SquirrelMail version number -- DO NOT CHANGE
33 */
34global $version;
987ae64c 35$version = '1.2.1 [cvs]';
d068c0ec 36
23d6bd09 37 /**
38 * Count the number of occurances of $needle are in $haystack.
39 * $needle can be a character or string, and need not occur in $haystack
40 */
3302d0d4 41 function countCharInString($haystack, $needle) {
37a17ace 42 if ($needle == '') return 0;
43 return count(explode($needle, $haystack));
3302d0d4 44 }
45
23d6bd09 46 /**
47 * Read from the back of $haystack until $needle is found, or the begining
48 * of the $haystack is reached. $needle is a single character
49 */
3302d0d4 50 function readShortMailboxName($haystack, $needle) {
8a549df2 51 if ($needle == '') return $haystack;
37a17ace 52 $parts = explode($needle, $haystack);
53 $elem = array_pop($parts);
54 while ($elem == '' && count($parts))
55 {
56 $elem = array_pop($parts);
57 }
58 return $elem;
3302d0d4 59 }
60
23d6bd09 61 /**
62 * Read from the back of $haystack until $needle is found, or the begining
63 * of the $haystack is reached. $needle is a single character
64 */
5bdd7223 65 function readMailboxParent($haystack, $needle) {
37a17ace 66 if ($needle == '') return '';
67 $parts = explode($needle, $haystack);
68 $elem = array_pop($parts);
69 while ($elem == '' && count($parts))
70 {
71 $elem = array_pop($parts);
72 }
73 return join($needle, $parts);
5bdd7223 74 }
75
23d6bd09 76 /**
77 * Searches for the next position in a string minus white space.
78 */
8beafbbc 79 function next_pos_minus_white ($haystack, $pos) {
8a549df2 80 while (substr($haystack, $pos, 1) == ' ' ||
8beafbbc 81 substr($haystack, $pos, 1) == "\t" ||
82 substr($haystack, $pos, 1) == "\n" ||
83 substr($haystack, $pos, 1) == "\r") {
84 if ($pos >= strlen($haystack))
85 return -1;
86 $pos++;
9374671f 87 }
88 return $pos;
8beafbbc 89 }
90
23d6bd09 91 /**
92 * Wraps text at $wrap characters
93 *
94 * Has a problem with special HTML characters, so call this before
95 * you do character translation.
96 *
97 * Specifically, &#039 comes up as 5 characters instead of 1.
98 * This should not add newlines to the end of lines.
99 */
9eea179c 100 function sqWordWrap(&$line, $wrap) {
146e0c45 101 ereg("^([\t >]*)([^\t >].*)?$", $line, $regs);
45f6dd68 102 $beginning_spaces = $regs[1];
65f5b1c4 103 if (isset($regs[2])) {
23fd3c8e 104 $words = explode(' ', $regs[2]);
105 } else {
106 $words = "";
65f5b1c4 107 }
a95681a7 108
109 $i = 0;
110 $line = $beginning_spaces;
9374671f 111
bcad90fe 112 while ($i < count($words)) {
113 // Force one word to be on a line (minimum)
114 $line .= $words[$i];
48f8ce2f 115 $line_len = strlen($beginning_spaces) + strlen($words[$i]) + 2;
65f5b1c4 116 if (isset($words[$i + 1]))
117 $line_len += strlen($words[$i + 1]);
bcad90fe 118 $i ++;
9374671f 119
bcad90fe 120 // Add more words (as long as they fit)
121 while ($line_len < $wrap && $i < count($words)) {
122 $line .= ' ' . $words[$i];
123 $i++;
65f5b1c4 124 if (isset($words[$i]))
48f8ce2f 125 $line_len += strlen($words[$i]) + 1;
65f5b1c4 126 else
127 $line_len += 1;
bcad90fe 128 }
9374671f 129
bcad90fe 130 // Skip spaces if they are the first thing on a continued line
48f8ce2f 131 while (!isset($words[$i]) && $i < count($words)) {
bcad90fe 132 $i ++;
133 }
8ceb637a 134
9374671f 135 // Go to the next line if we have more to process
bcad90fe 136 if ($i < count($words)) {
5be8bbd8 137 $line .= "\n" . $beginning_spaces;
b8ea4ed6 138 }
e550d551 139 }
e550d551 140 }
9374671f 141
142
23d6bd09 143 /**
144 * Does the opposite of sqWordWrap()
145 */
146 function sqUnWordWrap(&$body) {
01aab860 147 $lines = explode("\n", $body);
148 $body = "";
149 $PreviousSpaces = "";
23d6bd09 150 for ($i = 0; $i < count($lines); $i ++) {
146e0c45 151 ereg("^([\t >]*)([^\t >].*)?$", $lines[$i], $regs);
01aab860 152 $CurrentSpaces = $regs[1];
23d6bd09 153 if (isset($regs[2])) {
48f8ce2f 154 $CurrentRest = $regs[2];
23d6bd09 155 }
156
157 if ($i == 0) {
01aab860 158 $PreviousSpaces = $CurrentSpaces;
159 $body = $lines[$i];
23d6bd09 160 } else if (($PreviousSpaces == $CurrentSpaces) // Do the beginnings match
161 && (strlen($lines[$i - 1]) > 65) // Over 65 characters long
162 && strlen($CurrentRest)) { // and there's a line to continue with
01aab860 163 $body .= ' ' . $CurrentRest;
23d6bd09 164 } else {
01aab860 165 $body .= "\n" . $lines[$i];
166 $PreviousSpaces = $CurrentSpaces;
167 }
168 }
169 $body .= "\n";
170 }
9374671f 171
40ee9452 172
23d6bd09 173 /**
174 * Returns an array of email addresses.
175 * Be cautious of "user@host.com"
176 */
40ee9452 177 function parseAddrs($text) {
1d2ce1c3 178 if (trim($text) == "")
c5edd369 179 return array();
8a549df2 180 $text = str_replace(' ', '', $text);
181 $text = ereg_replace('"[^"]*"', '', $text);
5be8bbd8 182 $text = ereg_replace('\\([^\\)]*\\)', '', $text);
8a549df2 183 $text = str_replace(',', ';', $text);
184 $array = explode(';', $text);
1d2ce1c3 185 for ($i = 0; $i < count ($array); $i++) {
65f5b1c4 186 $array[$i] = eregi_replace ("^.*[<]", '', $array[$i]);
187 $array[$i] = eregi_replace ("[>].*$", '', $array[$i]);
188 }
1d2ce1c3 189 return $array;
40ee9452 190 }
191
23d6bd09 192 /**
193 * Returns a line of comma separated email addresses from an array.
194 */
40ee9452 195 function getLineOfAddrs($array) {
b676ba7e 196 if (is_array($array)) {
8a549df2 197 $to_line = implode(', ', $array);
d46b103b 198 $to_line = ereg_replace(', (, )+', ', ', $to_line);
199 $to_line = trim(ereg_replace('^, ', '', $to_line));
200 if( substr( $to_line, -1 ) == ',' )
201 $to_line = substr( $to_line, 0, -1 );
b676ba7e 202 } else {
8a549df2 203 $to_line = '';
40ee9452 204 }
9374671f 205
9374671f 206 return( $to_line );
40ee9452 207 }
7ce342dc 208
9eea179c 209 function translateText(&$body, $wrap_at, $charset) {
9297917e 210 global $where, $what; // from searching
0391864c 211 global $color; // color theme
9297917e 212
0fc2aca0 213 require_once('../functions/url_parser.php');
9374671f 214
a8648d75 215 $body_ary = explode("\n", $body);
8ceb637a 216 $PriorQuotes = 0;
8442ac08 217 for ($i=0; $i < count($body_ary); $i++) {
a8648d75 218 $line = $body_ary[$i];
a37f3771 219 if (strlen($line) - 2 >= $wrap_at) {
9374671f 220 sqWordWrap($line, $wrap_at);
a37f3771 221 }
a95681a7 222 $line = charset_decode($charset, $line);
223 $line = str_replace("\t", ' ', $line);
9374671f 224
9eea179c 225 parseUrl ($line);
9374671f 226
9eea179c 227 $Quotes = 0;
228 $pos = 0;
23d6bd09 229 while (1) {
230 if ($line[$pos] == ' ') {
8ceb637a 231 $pos ++;
23d6bd09 232 } else if (strpos($line, '&gt;', $pos) === $pos) {
9eea179c 233 $pos += 4;
234 $Quotes ++;
23d6bd09 235 } else {
9eea179c 236 break;
237 }
238 }
9374671f 239
0391864c 240 if ($Quotes > 1) {
23d6bd09 241 if (! isset($color[14])) {
24194051 242 $color[14] = '#FF0000';
23d6bd09 243 }
0391864c 244 $line = '<FONT COLOR="' . $color[14] . '">' . $line . '</FONT>';
24194051 245 } elseif ($Quotes) {
23d6bd09 246 if (! isset($color[13])) {
24194051 247 $color[13] = '#800000';
23d6bd09 248 }
24194051 249 $line = '<FONT COLOR="' . $color[13] . '">' . $line . '</FONT>';
0391864c 250 }
e2ef6f4b 251
8ceb637a 252 $body_ary[$i] = $line;
a8648d75 253 }
8a549df2 254 $body = '<pre>' . implode("\n", $body_ary) . '</pre>';
78509c54 255 }
256
d29aac0e 257 function find_mailbox_name ($mailbox) {
f9b3e5d9 258 if (ereg(" *\"([^\r\n\"]*)\"[ \r\n]*$", $mailbox, $regs))
259 return $regs[1];
260 ereg(" *([^ \r\n\"]*)[ \r\n]*$",$mailbox,$regs);
261 return $regs[1];
262
d29aac0e 263 }
264
23d6bd09 265 /**
266 * This determines the location to forward to relative to your server.
267 * If this doesnt work correctly for you (although it should), you can
268 * remove all this code except the last two lines, and change the header()
269 * function to look something like this, customized to the location of
270 * SquirrelMail on your server:
271 *
272 * http://www.myhost.com/squirrelmail/src/login.php
273 */
1195c340 274 function get_location () {
9374671f 275
c731b9f7 276 global $PHP_SELF, $SERVER_NAME, $HTTP_HOST, $SERVER_PORT,
277 $HTTP_SERVER_VARS;
841291c2 278
23d6bd09 279 /* Get the path. */
1195c340 280 $path = substr($PHP_SELF, 0, strrpos($PHP_SELF, '/'));
9374671f 281
23d6bd09 282 /* Check if this is a HTTPS or regular HTTP request. */
8a549df2 283 $proto = 'http://';
23d6bd09 284
285 /*
286 * If you have 'SSLOptions +StdEnvVars' in your apache config
287 * OR if you have HTTPS in your HTTP_SERVER_VARS
288 * OR if you are on port 443
289 */
c731b9f7 290 $getEnvVar = getenv('HTTPS');
291 if ((isset($getEnvVar) && !strcasecmp($getEnvVar, 'on')) ||
292 (isset($HTTP_SERVER_VARS['HTTPS'])) ||
293 (isset($HTTP_SERVER_VARS['SERVER_PORT']) &&
294 $HTTP_SERVER_VARS['SERVER_PORT'] == 443)) {
8a549df2 295 $proto = 'https://';
1195c340 296 }
9374671f 297
1195c340 298 // Get the hostname from the Host header or server config.
8a549df2 299 $host = '';
23d6bd09 300 if (isset($HTTP_HOST) && !empty($HTTP_HOST)) {
b768a8eb 301 $host = $HTTP_HOST;
23d6bd09 302 } else if (isset($SERVER_NAME) && !empty($SERVER_NAME)) {
b768a8eb 303 $host = $SERVER_NAME;
304 }
9374671f 305
b768a8eb 306 $port = '';
23d6bd09 307 if (! strstr($host, ':')) {
b768a8eb 308 if (isset($SERVER_PORT)) {
8a549df2 309 if (($SERVER_PORT != 80 && $proto == 'http://')
310 || ($SERVER_PORT != 443 && $proto == 'https://')) {
b768a8eb 311 $port = sprintf(':%d', $SERVER_PORT);
312 }
313 }
314 }
9374671f 315
23d6bd09 316 /* Fallback is to omit the server name and use a relative */
317 /* URI, although this is not RFC 2616 compliant. */
318 return ($host ? $proto . $host . $port . $path : $path);
9374671f 319 }
7aaa81fc 320
52eefafc 321
23d6bd09 322 /**
323 * These functions are used to encrypt the passowrd before it is
324 * stored in a cookie.
325 */
59edd854 326 function OneTimePadEncrypt ($string, $epad) {
23d6bd09 327 $pad = base64_decode($epad);
328 $encrypted = '';
329 for ($i = 0; $i < strlen ($string); $i++) {
330 $encrypted .= chr (ord($string[$i]) ^ ord($pad[$i]));
331 }
52eefafc 332
23d6bd09 333 return base64_encode($encrypted);
52eefafc 334 }
335
59edd854 336 function OneTimePadDecrypt ($string, $epad) {
337 $pad = base64_decode($epad);
52eefafc 338 $encrypted = base64_decode ($string);
8a549df2 339 $decrypted = '';
52eefafc 340 for ($i = 0; $i < strlen ($encrypted); $i++) {
65f5b1c4 341 $decrypted .= chr (ord($encrypted[$i]) ^ ord($pad[$i]));
52eefafc 342 }
343
344 return $decrypted;
345 }
346
580e1793 347
23d6bd09 348 /**
349 * Randomize the mt_rand() function. Toss this in strings or integers
350 * and it will seed the generator appropriately. With strings, it is
351 * better to get them long. Use md5() to lengthen smaller strings.
352 */
353 function sq_mt_seed($Val) {
dcaf2a49 354 // if mt_getrandmax() does not return a 2^n - 1 number,
355 // this might not work well. This uses $Max as a bitmask.
356 $Max = mt_getrandmax();
9374671f 357
23d6bd09 358 if (! is_int($Val)) {
359 if (function_exists('crc32')) {
dcaf2a49 360 $Val = crc32($Val);
23d6bd09 361 } else {
dcaf2a49 362 $Str = $Val;
363 $Pos = 0;
364 $Val = 0;
365 $Mask = $Max / 2;
366 $HighBit = $Max ^ $Mask;
23d6bd09 367 while ($Pos < strlen($Str)) {
368 if ($Val & $HighBit) {
dcaf2a49 369 $Val = (($Val & $Mask) << 1) + 1;
23d6bd09 370 } else {
dcaf2a49 371 $Val = ($Val & $Mask) << 1;
372 }
373 $Val ^= $Str[$Pos];
374 $Pos ++;
375 }
376 }
377 }
580e1793 378
23d6bd09 379 if ($Val < 0) {
380 $Val *= -1;
381 }
382
383 if ($Val = 0) {
384 return;
385 }
dcaf2a49 386
387 mt_srand(($Val ^ mt_rand(0, $Max)) & $Max);
388 }
9374671f 389
390
23d6bd09 391 /**
392 * This function initializes the random number generator fairly well.
393 * It also only initializes it once, so you don't accidentally get
394 * the same 'random' numbers twice in one session.
395 */
396 function sq_mt_randomize() {
dcaf2a49 397 global $REMOTE_PORT, $REMOTE_ADDR, $UNIQUE_ID;
398 static $randomized;
9374671f 399
23d6bd09 400 if ($randomized) {
401 return;
402 }
9374671f 403
23d6bd09 404 /* Global. */
dcaf2a49 405 sq_mt_seed((int)((double) microtime() * 1000000));
406 sq_mt_seed(md5($REMOTE_PORT . $REMOTE_ADDR . getmypid()));
9374671f 407
23d6bd09 408 /* getrusage */
8a549df2 409 if (function_exists('getrusage')) {
821a8e9c 410 // Avoid warnings with Win32
411 $dat = @getrusage();
23d6bd09 412 if (isset($dat) && is_array($dat)) {
821a8e9c 413 $Str = '';
414 foreach ($dat as $k => $v)
415 {
416 $Str .= $k . $v;
417 }
418 sq_mt_seed(md5($Str));
419 }
dcaf2a49 420 }
9374671f 421
dcaf2a49 422 // Apache-specific
423 sq_mt_seed(md5($UNIQUE_ID));
9374671f 424
dcaf2a49 425 $randomized = 1;
426 }
9374671f 427
dcaf2a49 428 function OneTimePadCreate ($length=100) {
429 sq_mt_randomize();
245a6892 430
8a549df2 431 $pad = '';
52eefafc 432 for ($i = 0; $i < $length; $i++) {
65f5b1c4 433 $pad .= chr(mt_rand(0,255));
52eefafc 434 }
435
59edd854 436 return base64_encode($pad);
52eefafc 437 }
438
23d6bd09 439 /**
440 * Check if we have a required PHP-version. Return TRUE if we do,
441 * or FALSE if we don't.
442 *
443 * To check for 4.0.1, use sqCheckPHPVersion(4,0,1)
444 * To check for 4.0b3, use sqCheckPHPVersion(4,0,-3)
445 *
446 * Does not handle betas like 4.0.1b1 or development versions
447 */
f79af863 448 function sqCheckPHPVersion($major, $minor, $release) {
449
450 $ver = phpversion();
5be8bbd8 451 eregi('^([0-9]+)\\.([0-9]+)(.*)', $ver, $regs);
f79af863 452
23d6bd09 453 /* Parse the version string. */
f79af863 454 $vmajor = strval($regs[1]);
455 $vminor = strval($regs[2]);
456 $vrel = $regs[3];
23d6bd09 457 if($vrel[0] == ".") {
65f5b1c4 458 $vrel = strval(substr($vrel, 1));
23d6bd09 459 }
460 if($vrel[0] == 'b' || $vrel[0] == 'B') {
65f5b1c4 461 $vrel = - strval(substr($vrel, 1));
23d6bd09 462 }
463 if($vrel[0] == 'r' || $vrel[0] == 'R') {
65f5b1c4 464 $vrel = - strval(substr($vrel, 2))/10;
23d6bd09 465 }
9374671f 466
23d6bd09 467 /* Compare major version. */
468 if ($vmajor < $major) { return false; }
469 if ($vmajor > $major) { return true; }
b5afdff0 470
23d6bd09 471 /* Major is the same. Compare minor. */
472 if ($vminor < $minor) { return false; }
473 if ($vminor > $minor) { return true; }
9374671f 474
23d6bd09 475 /* Major and minor is the same as the required one. Compare release */
476 if ($vrel >= 0 && $release >= 0) { // Neither are beta
65f5b1c4 477 if($vrel < $release) return false;
23d6bd09 478 } else if($vrel >= 0 && $release < 0) { // This is not beta, required is beta
65f5b1c4 479 return true;
23d6bd09 480 } else if($vrel < 0 && $release >= 0){ // This is beta, require not beta
65f5b1c4 481 return false;
23d6bd09 482 } else { // Both are beta
65f5b1c4 483 if($vrel > $release) return false;
f79af863 484 }
9374671f 485
f79af863 486 return true;
487 }
9374671f 488
23d6bd09 489 /**
490 * Returns a string showing the size of the message/attachment.
491 */
492 function show_readable_size($bytes) {
6e7468f6 493 $bytes /= 1024;
494 $type = 'k';
9374671f 495
23d6bd09 496 if ($bytes / 1024 > 1) {
6e7468f6 497 $bytes /= 1024;
498 $type = 'm';
499 }
9374671f 500
23d6bd09 501 if ($bytes < 10) {
6e7468f6 502 $bytes *= 10;
8a549df2 503 settype($bytes, 'integer');
6e7468f6 504 $bytes /= 10;
23d6bd09 505 } else {
8a549df2 506 settype($bytes, 'integer');
23d6bd09 507 }
9374671f 508
6e7468f6 509 return $bytes . '<small>&nbsp;' . $type . '</small>';
510 }
f79af863 511
23d6bd09 512 /**
513 * Generates a random string from the caracter set you pass in
1899535f 514 *
515 * Flags:
516 * 1 = add lowercase a-z to $chars
517 * 2 = add uppercase A-Z to $chars
518 * 4 = add numbers 0-9 to $chars
519 */
9374671f 520
23d6bd09 521 function GenerateRandomString($size, $chars, $flags = 0) {
522 if ($flags & 0x1) {
1899535f 523 $chars .= 'abcdefghijklmnopqrstuvwxyz';
23d6bd09 524 }
525 if ($flags & 0x2) {
1899535f 526 $chars .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
23d6bd09 527 }
528 if ($flags & 0x4) {
1899535f 529 $chars .= '0123456789';
23d6bd09 530 }
9374671f 531
23d6bd09 532 if (($size < 1) || (strlen($chars) < 1)) {
8a549df2 533 return '';
23d6bd09 534 }
9374671f 535
1899535f 536 sq_mt_randomize(); // Initialize the random number generator
9374671f 537
65f5b1c4 538 $String = "";
1899535f 539 while (strlen($String) < $size) {
540 $String .= $chars[mt_rand(0, strlen($chars))];
541 }
9374671f 542
1899535f 543 return $String;
544 }
9374671f 545
23d6bd09 546 function quoteIMAP($str) {
704db5b2 547 return ereg_replace('(["\\])', '\\\\1', $str);
548 }
1899535f 549
23d6bd09 550 /**
551 * Trims every element in the array
552 */
553 function TrimArray(&$array) {
554 foreach ($array as $k => $v) {
555 global $$k;
556 if (is_array($$k)) {
557 foreach ($$k as $k2 => $v2) {
558 $$k[$k2] = substr($v2, 1);
559 }
560 } else {
561 $$k = substr($v, 1);
562 }
563
564 /* Re-assign back to array. */
565 $array[$k] = $$k;
566 }
567 }
568
569 /**
570 * Removes slashes from every element in the array
571 */
572 function RemoveSlashes(&$array) {
573 foreach ($array as $k => $v) {
574 global $$k;
575 if (is_array($$k)) {
576 foreach ($$k as $k2 => $v2) {
577 $newArray[stripslashes($k2)] = stripslashes($v2);
578 }
579 $$k = $newArray;
580 } else {
581 $$k = stripslashes($v);
582 }
583
584 /* Re-assign back to the array. */
585 $array[$k] = $$k;
586 }
587 }
588
15e6162e 589?>