3 * FusionForge miscellaneous utils
5 * Copyright 1999-2001, VA Linux Systems, Inc.
6 * Copyright 2009, Roland Mas
8 * This file is part of FusionForge.
10 * FusionForge is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published
12 * by the Free Software Foundation; either version 2 of the License,
13 * or (at your option) any later version.
15 * FusionForge is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with FusionForge; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
27 * removeCRLF() - remove any Carriage Return-Line Feed from a string.
28 * That function is useful to remove the possibility of a CRLF Injection when sending mail
29 * All the data that we will send should be passed through that function
31 * @param string The string that we want to empty from any CRLF
33 function util_remove_CRLF($str) {
34 return strtr($str, "\015\012", ' ');
39 * util_check_fileupload() - determines if a filename is appropriate for upload
41 * @param array The uploaded file as returned by getUploadedFile()
43 function util_check_fileupload($filename) {
45 /* Empty file is a valid file.
46 This is because this function should be called
47 unconditionally at the top of submit action processing
48 and many forms have optional file upload. */
49 if ($filename == 'none' || $filename == '') {
53 /* This should be enough... */
54 if (!is_uploaded_file($filename)) {
57 /* ... but we'd rather be paranoic */
58 if (strstr($filename, '..')) {
61 if (!is_file($filename)) {
64 if (!file_exists($filename)) {
67 if ((dirname($filename) != '/tmp') &&
68 (dirname($filename) != "/var/tmp")) {
75 * util_send_message() - Send email
76 * This function should be used in place of the PHP mail() function
78 * @param string The email recipients address
79 * @param string The email subject
80 * @param string The body of the email message
81 * @param string The optional email sender address. Defaults to 'noreply@'
82 * @param string The addresses to blind-carbon-copy this message
83 * @param string The optional email sender name. Defaults to ''
84 * @param boolean Whether to send plain text or html email
87 function util_send_message($to,$subject,$body,$from='',$BCC='',$sendername='',$extra_headers='',$send_html_email=false) {
88 global $sys_bcc_all_email_address,$sys_sendmail_path;
91 $to='noreply@'.$GLOBALS['sys_default_domain'];
94 $from='noreply@'.$GLOBALS['sys_default_domain'];
98 $charset = _('UTF-8');
104 if ($extra_headers) {
105 $body2 .= $extra_headers."\n";
108 "\nFrom: ".util_encode_mailaddr($from,$sendername,$charset);
109 if (!empty($sys_bcc_all_email_address)) {
110 $BCC.=",$sys_bcc_all_email_address";
113 $body2 .= "\nBCC: $BCC";
115 $send_html_email?$type="html":$type="plain";
116 $body2 .= "\nSubject: ".util_encode_mimeheader($subject, $charset).
117 "\nContent-type: text/$type; charset=$charset".
119 util_convert_body($body, $charset);
121 if (!$sys_sendmail_path){
122 $sys_sendmail_path="/usr/sbin/sendmail";
125 exec ("/bin/echo ". util_prep_string_for_sendmail($body2) .
126 " | ".$sys_sendmail_path." -f'$from' -t -i > /dev/null 2>&1 &");
127 // WARNING : popen commented code probably brought some trouble, we will use the pipe method as we were before
128 /*if (!$handle = popen($sys_sendmail_path." -f'$from' -t -i", "w")) {
129 echo "<p>Error: cannot run '$sys_sendmail_path' - mail not sent</p>\n";
131 fwrite($handle, util_prep_string_for_sendmail($body2));
137 * util_encode_mailaddr() - Encode email address to MIME format
139 * @param string The email address
140 * @param string The email's owner name
141 * @param string The converting charset
144 function util_encode_mailaddr($email,$name,$charset) {
145 if (function_exists('mb_convert_encoding') && trim($name) != "") {
146 $name = "=?".$charset."?B?".
147 base64_encode(mb_convert_encoding(
148 $name,$charset,"UTF-8")).
152 return $name." <".$email."> ";
156 * util_encode_mimeheader() - Encode mimeheader
158 * @param string The email subject
159 * @param string The converting charset (like ISO-2022-JP)
160 * @return string The MIME encoded subject
163 function util_encode_mimeheader($str,$charset) {
164 if (!function_exists('mb_convert_encoding')) {
168 return "=?".$charset."?B?".
169 base64_encode(mb_convert_encoding(
170 $str,$charset,"UTF-8")).
175 * util_convert_body() - Convert body of the email message
177 * @param string The body of the email message
178 * @param string The charset of the email message
179 * @return string The converted body of the email message
182 function util_convert_body($str,$charset) {
183 if (!function_exists('mb_convert_encoding') || $charset == 'UTF-8') {
187 return mb_convert_encoding($str,$charset,"UTF-8");
190 function util_send_jabber($to,$subject,$body) {
191 if (!$GLOBALS['sys_use_jabber']) {
194 $JABBER = new Jabber();
195 if (!$JABBER->Connect()) {
196 echo '<br />Unable to connect';
199 //$JABBER->SendAuth();
200 //$JABBER->AccountRegistration();
201 if (!$JABBER->SendAuth()) {
202 echo '<br />Auth Failure';
203 $JABBER->Disconnect();
205 //or die("Couldn't authenticate!");
207 $JABBER->SendPresence(NULL, NULL, "online");
209 $body=htmlspecialchars($body);
210 $to_arr=explode(',',$to);
211 for ($i=0; $i<count($to_arr); $i++) {
213 //echo '<br />Sending Jabbers To: '.$to_arr[$i];
214 if (!$JABBER->SendMessage($to_arr[$i], "normal", NULL, array("body" => $body,"subject"=>$subject))) {
215 echo '<br />Error Sending to '.$to_arr[$i];
220 $JABBER->CruiseControl(2);
221 $JABBER->Disconnect();
225 * util_prep_string_for_sendmail() - Prepares a string to be sent by email
227 * @param string The text to be prepared
228 * @returns The prepared text
231 function util_prep_string_for_sendmail($body) {
232 /*$body=str_replace("`","\\`",$body);
233 $body=str_replace("\"","\\\"",$body);
234 $body=str_replace("\$","\\\$",$body);*/
235 $body = escapeshellarg($body);
240 * util_handle_message() - a convenience wrapper which sends messages
241 * to either a jabber account or email account or both, depending on
244 * @param array array of user_id's from the user table
245 * @param string subject of the message
246 * @param string the message body
247 * @param string a comma-separated list of email address
248 * @param string a comma-separated list of jabber address
249 * @param string From header
251 function util_handle_message($id_arr,$subject,$body,$extra_emails='',$extra_jabbers='',$from='') {
254 if (count($id_arr) < 1) {
257 $res = db_query_params ('SELECT user_id,jabber_address,email,jabber_only FROM users WHERE user_id = ANY ($1)',
258 array (db_int_array_to_any_clause ($id_arr))) ;
259 $rows = db_numrows($res) ;
261 for ($i=0; $i<$rows; $i++) {
262 if (db_result($res, $i, 'user_id') == 100) {
263 // Do not send messages to "Nobody"
267 // Build arrays of the jabber address
269 if (db_result($res,$i,'jabber_address')) {
270 $address['jabber_address'][]=db_result($res,$i,'jabber_address');
271 if (db_result($res,$i,'jabber_only') != 1) {
272 $address['email'][]=db_result($res,$i,'email');
275 $address['email'][]=db_result($res,$i,'email');
278 if (isset ($address['email']) && count($address['email']) > 0) {
279 $extra_emails=implode($address['email'],',').',' . $extra_emails;
281 if (isset ($address['jabber_address']) && count($address['jabber_address']) > 0) {
282 $extra_jabbers=implode($address['jabber_address'],',').','.$extra_jabbers;
286 util_send_message('',$subject,$body,$from,$extra_emails);
288 if ($extra_jabbers) {
289 util_send_jabber($extra_jabbers,$subject,$body);
294 * util_unconvert_htmlspecialchars() - Unconverts a string converted with htmlspecialchars()
295 * This function requires PHP 4.0.3 or greater
297 * @param string The string to unconvert
298 * @returns The unconverted string
301 function util_unconvert_htmlspecialchars($string) {
302 if (strlen($string) < 1) {
305 //$trans = get_html_translation_table(HTMLENTITIES, ENT_QUOTES);
306 $trans = get_html_translation_table(HTML_ENTITIES);
307 $trans = array_flip ($trans);
308 $str = strtr ($string, $trans);
314 * util_result_columns_to_assoc() - Takes a result set and turns the column pair into an associative array
316 * @param string The result set ID
317 * @param int The column key
318 * @param int The optional column value
319 * @returns An associative array
322 function util_result_columns_to_assoc($result, $col_key=0, $col_val=1) {
323 $rows=db_numrows($result);
327 for ($i=0; $i<$rows; $i++) {
328 $arr[db_result($result,$i,$col_key)]=db_result($result,$i,$col_val);
337 * util_result_column_to_array() - Takes a result set and turns the optional column into an array
339 * @param int The result set ID
340 * @param int The column
344 function &util_result_column_to_array($result, $col=0) {
346 Takes a result set and turns the optional column into
349 $rows=db_numrows($result);
353 for ($i=0; $i<$rows; $i++) {
354 $arr[$i]=db_result($result,$i,$col);
363 * util_wrap_find_space() - Find the first space in a string
365 * @param string The string in which to find the space (must be UTF8!)
366 * @param int The number of characters to wrap - Default is 80
367 * @returns The position of the first space
370 function util_wrap_find_space($string,$wrap) {
377 //find the first space starting at $start
378 $pos=@strpos($string,' ',$start);
380 //if that space is too far over, go back and start more to the left
381 if (($pos > ($wrap+5)) || !$pos) {
383 $start=($wrap-($try*5));
384 //if we've gotten so far left , just truncate the line
387 $code = ord(substr($string,$wrap,1));
390 //Here is single byte character
391 //or head of multi byte character
394 //Do not break multi byte character
409 * util_line_wrap() - Automatically linewrap text
411 * @param string The text to wrap
412 * @param int The number of characters to wrap - Default is 80
413 * @param string The line break to use - Default is '\n'
414 * @returns The wrapped text
417 function util_line_wrap ($text, $wrap = 80, $break = "\n") {
418 $paras = explode("\n", $text);
422 while ($i < count($paras)) {
423 if (strlen($paras[$i]) <= $wrap) {
424 $result[] = $paras[$i];
427 $pos=util_wrap_find_space($paras[$i],$wrap);
429 $result[] = substr($paras[$i], 0, $pos);
431 $new = trim(substr($paras[$i], $pos, strlen($paras[$i]) - $pos));
434 $pos=util_wrap_find_space($paras[$i],$wrap);
440 return implode($break, $result);
444 * util_make_links() - Turn URL's into HREF's.
446 * @param string The URL
447 * @returns The HREF'ed URL
450 function util_make_links ($data='') {
454 $lines = split("\n",$data);
456 while ( list ($key,$line) = each ($lines)) {
457 // When we come here, we usually have form input
458 // encoded in entities. Our aim is to NOT include
459 // angle brackets in the URL
460 // (RFC2396; http://www.w3.org/Addressing/URL/5.1_Wrappers.html)
461 $line = str_replace('>', "\1", $line);
462 $line = eregi_replace("([ \t]|^)www\."," http://www.",$line);
463 $text = eregi_replace("([[:alnum:]]+)://([^[:space:]<\1]*)([[:alnum:]#?/&=])", "<a href=\"\\1://\\2\\3\" target=\"_new\">\\1://\\2\\3</a>", $line);
464 $text = eregi_replace("([[:space:]]|^)(([a-z0-9_]|\\-|\\.)+@([^[:space:]]*)([[:alnum:]-]))", "\\1<a href=\"mailto:\\2\" target=\"_new\">\\2</a>", $text);
465 $text = str_replace("\1", '>', $text);
472 * show_priority_colors_key() - Show the priority colors legend
475 function show_priority_colors_key() {
476 echo '<p /><strong> '._('Priority Colors').':</strong><br />
478 <table border="0"><tr>';
480 for ($i=1; $i<6; $i++) {
482 <td class="priority'.$i.'">'.$i.'</td>';
484 echo '</tr></table>';
488 * utils_buildcheckboxarray() - Build a checkbox array
490 * @param int Number of options to be in the array
491 * @param string The name of the checkboxes
492 * @param array An array of boxes to be pre-checked
495 function utils_buildcheckboxarray($options,$name,$checked_array) {
496 $option_count=count($options);
497 $checked_count=count($checked_array);
499 for ($i=1; $i<=$option_count; $i++) {
501 <br /><input type="checkbox" name="'.$name.'" value="'.$i.'"';
502 for ($j=0; $j<$checked_count; $j++) {
503 if ($i == $checked_array[$j]) {
507 echo '> '.$options[$i];
512 * utils_requiredField() - Adds the required field marker
514 * @return a string holding the HTML to mark a required field
516 function utils_requiredField() {
517 return '<span class="requiredfield">*</span>';
521 * GraphResult() - Takes a database result set and builds a graph.
522 * The first column should be the name, and the second column should be the values
523 * Be sure to include HTL_Graphs.php before using this function
525 * @author Tim Perdue tperdue@valinux.com
526 * @param int The databse result set ID
527 * @param string The title of the graph
530 Function GraphResult($result,$title) {
531 $rows=db_numrows($result);
533 if ((!$result) || ($rows < 1)) {
539 for ($j=0; $j<db_numrows($result); $j++) {
540 if (db_result($result, $j, 0) != '' && db_result($result, $j, 1) != '' ) {
541 $names[$j]= db_result($result, $j, 0);
542 $values[$j]= db_result($result, $j, 1);
547 This is another function detailed below
549 GraphIt($names,$values,$title);
554 * GraphIt() - Build a graph
556 * @author Tim Perdue tperdue@valinux.com
557 * @param array An array of names
558 * @param array An array of values
559 * @param string The title of the graph
562 Function GraphIt($name_string,$value_string,$title) {
565 $counter=count($name_string);
568 Can choose any color you wish
572 for ($i = 0; $i < $counter; $i++) {
573 $bars[$i]=$HTML->COLOR_LTBACK1;
576 $counter=count($value_string);
579 Figure the max_value passed in, so scale can be determined
584 for ($i = 0; $i < $counter; $i++) {
585 if ($value_string[$i] > $max_value) {
586 $max_value=$value_string[$i];
590 if ($max_value < 1) {
595 I want my graphs all to be 800 pixels wide, so that is my divisor
598 $scale=(400/$max_value);
601 I create a wrapper table around the graph that holds the title
607 echo $GLOBALS['HTML']->listTableTop ($title_arr);
610 Create an associate array to pass in. I leave most of it blank
639 This is the actual call to the HTML_Graphs class
642 html_graph($name_string,$value_string,$bars,$vals);
646 <!-- end outer graph table -->';
647 echo $GLOBALS['HTML']->listTableBottom();
651 * ShowResultSet() - Show a generic result set
652 * Very simple, plain way to show a generic result set
654 * @param int The result set ID
655 * @param string The title of the result set
656 * @param bool The option to turn URL's into links
657 * @param bool The option to display headers
658 * @param array The db field name -> label mapping
659 * @param array Don't display these cols
662 function ShowResultSet($result,$title='',$linkify=false,$displayHeaders=true,$headerMapping=array(), $excludedCols=array()) {
663 global $group_id,$HTML;
666 $rows = db_numrows($result);
667 $cols = db_numfields($result);
669 echo '<table border="0" width="100%">';
671 /* Create the headers */
672 $headersCellData = array();
673 $colsToKeep = array();
674 for ($i=0; $i < $cols; $i++) {
675 $fieldName = db_fieldname($result, $i);
676 if(in_array($fieldName, $excludedCols)) {
680 if(isset($headerMapping[$fieldName])) {
681 if(is_array($headerMapping[$fieldName])) {
682 $headersCellData[] = $headerMapping[$fieldName];
684 $headersCellData[] = array($headerMapping[$fieldName]);
688 $headersCellData[] = array($fieldName);
692 /* Create the title */
693 if(strlen($title) > 0) {
694 $titleCellData = array();
695 $titleCellData[] = array($title, 'colspan="'.count($headersCellData).'"');
696 echo $HTML->multiTableRow('', $titleCellData, TRUE);
699 /* Display the headers */
700 if($displayHeaders) {
701 echo $HTML->multiTableRow('', $headersCellData, TRUE);
704 /* Create the rows */
705 for ($j = 0; $j < $rows; $j++) {
706 echo '<tr '. $HTML->boxGetAltRowStyle($j) . '>';
707 for ($i = 0; $i < $cols; $i++) {
708 if(in_array($i, $colsToKeep)) {
709 if ($linkify && $i == 0) {
710 $link = '<a href="'.getStringFromServer('PHP_SELF').'?';
712 if ($linkify == "bug_cat") {
713 $link .= 'group_id='.$group_id.'&bug_cat_mod=y&bug_cat_id='.db_result($result, $j, 'bug_category_id').'">';
714 } else if($linkify == "bug_group") {
715 $link .= 'group_id='.$group_id.'&bug_group_mod=y&bug_group_id='.db_result($result, $j, 'bug_group_id').'">';
716 } else if($linkify == "patch_cat") {
717 $link .= 'group_id='.$group_id.'&patch_cat_mod=y&patch_cat_id='.db_result($result, $j, 'patch_category_id').'">';
718 } else if($linkify == "support_cat") {
719 $link .= 'group_id='.$group_id.'&support_cat_mod=y&support_cat_id='.db_result($result, $j, 'support_category_id').'">';
720 } else if($linkify == "pm_project") {
721 $link .= 'group_id='.$group_id.'&project_cat_mod=y&project_cat_id='.db_result($result, $j, 'group_project_id').'">';
723 $link = $linkend = '';
726 $link = $linkend = '';
728 echo '<td>'.$link . db_result($result, $j, $i) . $linkend.'</td>';
740 * validate_email() - Validate an email address
742 * @param string The address string to validate
743 * @returns true on success/false on error
746 function validate_email ($address) {
747 return (ereg('^[-!#$%&\'*+\\./0-9=?A-Z^_`a-z{|}~]+'. '@'. '[-!#$%&\'*+\\/0-9=?A-Z^_`a-z{|}~]+\.' . '[-!#$%&\'*+\\./0-9=?A-Z^_`a-z{|}~]+$', $address) !== false);
751 * validate_emails() - Validate a list of e-mail addresses
753 * @param string E-mail list
754 * @param char Separator
755 * @returns array Array of invalid e-mail addresses (if empty, all addresses are OK)
757 function validate_emails ($addresses, $separator=',') {
758 if (strlen($addresses) == 0) return array();
760 $emails = explode($separator, $addresses);
763 if (is_array($emails)) {
764 foreach ($emails as $email) {
765 $email = trim($email); // This is done so we can validate lists like "a@b.com, c@d.com"
766 if (!validate_email($email)) $ret[] = $email;
775 * util_is_valid_filename() - Verifies whether a file has a valid filename
777 * @param string The file to verify
778 * @returns true on success/false on error
781 function util_is_valid_filename ($file) {
783 $invalidchars = eregi_replace("[-A-Z0-9+_\.]","",$file);
785 if (!empty($invalidchars)) {
788 if (strstr($file,'..')) {
797 * valid_hostname() - Validates a hostname string to make sure it doesn't contain invalid characters
799 * @param string The optional hostname string
800 * @returns true on success/false on failur
803 function valid_hostname ($hostname = "xyz") {
806 $invalidchars = eregi_replace("[-A-Z0-9\.]","",$hostname);
808 if (!empty($invalidchars)) {
812 //double dot, starts with a . or -
813 if (ereg("\.\.",$hostname) || ereg("^\.",$hostname) || ereg("^\-",$hostname)) {
817 $multipoint = explode(".",$hostname);
819 if (!(is_array($multipoint)) || ((count($multipoint) - 1) < 1)) {
829 * human_readable_bytes() - Translates an integer representing bytes to a human-readable format.
831 * Format file size in a human-readable way
832 * such as "xx Megabytes" or "xx Mo"
834 * @author Andrea Paleni <andreaSPAMLESS_AT_SPAMLESScriticalbit.com>
836 * @param int bytes is the size
837 * @param bool base10 enable base 10 representation, otherwise
838 * default base 2 is used
839 * @param int round number of fractional digits
840 * @param array labels strings associated to each 2^10 or
841 * 10^3(base10==true) multiple of base units
843 function human_readable_bytes ($bytes, $base10=false, $round=0, $labels=array(' bytes', ' KB', ' MB', ' GB')) {
844 if ($bytes <= 0 || !is_array($labels) || (count($labels) <= 0)) {
847 $step = $base10 ? 3 : 10;
848 $base = $base10 ? 10 : 2;
849 $log = (int)(log10($bytes)/log10($base));
851 foreach ($labels as $p=>$lab) {
856 if ($lab == " MB" or $lab == " GB") {
859 $text = round($bytes/pow($base,$pow),$round).$lab;
866 * ls - lists a specified directory and returns an array of files
867 * @param string the path of the directory to list
868 * @param boolean whether to filter out directories and illegal filenames
869 * @return array array of file names.
871 function &ls($dir,$filter=false) {
872 exec('ls -c1 '.$dir,$out);
874 for ($i=0; $i<count($out); $i++) {
875 if (util_is_valid_filename($out[$i]) && is_file($dir.'/'.$out[$i])) {
876 $filtered[]=$out[$i];
886 * readfile_chunked() - replacement for readfile
888 * @param string The file path
889 * @param bool Whether to return bytes served or just a bool
892 function readfile_chunked($filename, $returnBytes=true) {
893 $chunksize = 1*(1024*1024); // 1MB chunks
897 $handle = fopen($filename, 'rb');
898 if ($handle === false) {
902 while (!feof($handle)) {
903 $buffer = fread($handle, $chunksize);
908 $byteCounter += strlen($buffer);
911 $status = fclose($handle);
912 if ($returnBytes && $status) {
913 return $byteCounter; // return num. bytes delivered like readfile() does.
919 * util_is_root_dir() - Checks if a directory points to the root dir
920 * @param string Directory
923 function util_is_root_dir($dir) {
924 return !preg_match('/[^\\/]/',$dir);
927 function normalized_urlprefix () {
928 $prefix = $GLOBALS['sys_urlprefix'] ;
929 $prefix = ereg_replace ("^/", "", $prefix) ;
930 $prefix = ereg_replace ("/$", "", $prefix) ;
931 $prefix = "/$prefix/" ;
937 function util_make_url ($path) {
938 if ($GLOBALS['sys_use_ssl'])
943 $url .= $GLOBALS['sys_default_domain'] ;
944 $url .= normalized_urlprefix () ;
945 $path = ereg_replace ("^/", "", $path) ;
950 function util_make_link ($path, $text, $extra_params=false, $absolute=false) {
952 if (is_array($extra_params)) {
953 foreach ($extra_params as $key => $value) {
954 $ep .= "$key=\"$value\" ";
958 return '<a ' . $ep . 'href="' . $path . '">' . $text . '</a>' ;
960 return '<a ' . $ep . 'href="' . util_make_url ($path) . '">' . $text . '</a>' ;
964 function util_make_link_u ($username, $user_id,$text) {
965 return '<a href="' . util_make_url_u ($username, $user_id) . '">' . $text . '</a>' ;
968 function util_make_url_u ($username, $user_id) {
969 if (isset ($GLOBALS['sys_noforcetype']) && $GLOBALS['sys_noforcetype']) {
970 return util_make_url ("/developer/?user_id=$user_id");
972 return util_make_url ("/users/$username/");
976 function util_make_link_g ($groupame, $group_id,$text) {
977 return '<a href="' . util_make_url_g ($groupame, $group_id) . '">' . $text . '</a>' ;
980 function util_make_url_g ($groupame, $group_id) {
981 if (isset ($GLOBALS['sys_noforcetype']) && $GLOBALS['sys_noforcetype']) {
982 return util_make_url ("/project/?group_id=$group_id");
984 return util_make_url ("/projects/$groupame/");
988 function util_ensure_value_in_set ($value, $set) {
989 if (in_array ($value, $set)) {
998 // c-file-style: "bsd"