1<?php 2/** 3 * functions.php - Addressbook Import-Export functions 4 * 5 * Copyright (c) 1999-2006 The SquirrelMail Project Team 6 * Licensed under the GNU GPL. For full terms see the file COPYING. 7 * 8 * Uses standard plugin format to create a couple of forms to 9 * enable import/export of CSV files to/from the address book. 10 * @version $Id: functions.php,v 1.24 2006/07/22 16:45:08 tokul Exp $ 11 * @package sm-plugins 12 * @subpackage abook_import_export 13 */ 14 15/** @ignore */ 16if (!defined('SM_PATH')) define('SM_PATH','../../'); 17 18/** load sqm_baseuri() function */ 19include_once(SM_PATH . 'functions/display_messages.php'); 20/** load form functions */ 21include_once(SM_PATH . 'functions/forms.php'); 22 23/** set configuration globals */ 24global $aie_csv_maxsize, $aie_input_charsets, $aie_hide_upload_error; 25 26/** load default configuration*/ 27if (file_exists(SM_PATH . 'plugins/abook_import_export/config_default.php')) { 28 include_once(SM_PATH . 'plugins/abook_import_export/config_default.php'); 29} else { 30 // set default config values inside script, if file is removed. 31 $aie_csv_maxsize=5120; 32 // input character sets 33 $aie_input_charsets = array( 34 'windows-1250', 35 'windows-1251', 36 'windows-1252', 37 'windows-1253', 38 'windows-1254', 39 'windows-1255', 40 'windows-1256', 41 'windows-1257', 42 'windows-1258', 43 'cp855', 44 'cp866', 45 'iso-8859-10', 46 'iso-8859-11', 47 'iso-8859-13', 48 'iso-8859-14', 49 'iso-8859-15', 50 'iso-8859-16', 51 'iso-8859-1', 52 'iso-8859-2', 53 'iso-8859-3', 54 'iso-8859-4', 55 'iso-8859-5', 56 'iso-8859-6', 57 'iso-8859-7', 58 'iso-8859-8', 59 'iso-8859-9', 60 'iso-ir-111', 61 'koi8-r', 62 'koi8-u', 63 'ns_4551_1', 64 'tis-620', 65 'us_ascii', 66 'utf-8'); 67 $aie_hide_upload_error=false; 68} 69 70/** site configuration */ 71if (file_exists(SM_PATH . 'config/abook_import_export_config.php')) { 72 include_once(SM_PATH . 'config/abook_import_export_config.php'); 73} elseif (file_exists(SM_PATH . 'plugins/abook_import_export/config.php')) { 74 include_once(SM_PATH . 'plugins/abook_import_export/config.php'); 75} 76 77/* Sort input character sets */ 78natsort($aie_input_charsets); 79 80 81/** 82 * Add import/export form 83 * (internal function) 84 */ 85function aie_create_form() { 86 global $color,$aie_csv_maxsize, $aie_input_charsets, $default_charset, $aie_hide_upload_error, 87 $squirrelmail_language; 88 89 // switch domain 90 bindtextdomain('abook_import_export',SM_PATH . 'locale'); 91 textdomain('abook_import_export'); 92 93 if (function_exists('bind_textdomain_codeset')) { 94 if ($squirrelmail_language == 'ja_JP') { 95 bind_textdomain_codeset ('abook_import_export', 'EUC-JP'); 96 } else { 97 bind_textdomain_codeset ('abook_import_export', $default_charset ); 98 } 99 } 100 101 $aie_delimiter = array("'"=>_("Single quotes (')"), 102 '"'=>_("Double quotes (\")"), 103 ','=>_("Comma (,)"), 104 ';'=>_("Semicolon (;)"), 105 'custom'=>_("Custom delimiter")); 106 107 // using php for html generation, because formating of mixed php/html code is not good. 108 109 if ((bool)ini_get('file_uploads')) { 110 echo html_tag( 'table', 111 html_tag( 'tr', 112 html_tag( 'td', '<strong>' . _("Address book import") . '</strong>' . "\n", 'center', $color[0] ) 113 ), 114 'center', '', 'width="95%"' ); 115 116 echo "<!-- begin csv import form -->\n"; 117 // don't use MAX_FILE_SIZE input field or don't rely on it. 118 // Size can't be controlled in place that can be modified by end user. 119 echo '<form enctype="multipart/form-data" action="' 120 .sqm_baseuri() . 'plugins/abook_import_export/address_book_import.php' 121 .'" method="post">' . "\n"; 122 123 echo '<table width="90%" border="0" cellpadding="1" cellspacing="0" align="center">' . "\n"; 124 echo html_tag('tr', 125 html_tag('td',_("Select file:"),'right')."\n". 126 html_tag('td',addHidden('MAX_FILE_SIZE',$aie_csv_maxsize). 127 '<input name="smusercsv" type="file" />','left')); 128 129 echo html_tag('tr', 130 html_tag('td',_("Max:"),'right') ."\n". 131 html_tag('td',aie_display_size($aie_csv_maxsize),'left')); 132 133 echo html_tag('tr', 134 html_tag('td',_("Input character set:"),'right')."\n". 135 html_tag('td',addSelect('input_charset',$aie_input_charsets,$default_charset),'left')); 136 137 echo html_tag('tr', 138 html_tag('td',_("Field delimiter:"),'right')."\n". 139 html_tag('td',addSelect('field_delimiter',$aie_delimiter,',',true),'left')); 140 141 echo html_tag('tr', 142 html_tag('td',_("Custom field delimiter:"),'right')."\n". 143 html_tag('td',addInput('custom_field_delimiter',',',1,1),'left')); 144 145 /* fgetcsv enclosure option is available since 4.3.0 */ 146 if (check_php_version(4,3,0)) { 147 echo html_tag('tr', 148 html_tag('td',_("Text delimiter:"),'right')."\n". 149 html_tag('td',addSelect('text_delimiter',$aie_delimiter,'"',true),'left')); 150 151 echo html_tag('tr', 152 html_tag('td',_("Custom text delimiter:"),'right')."\n". 153 html_tag('td',addInput('custom_text_delimiter','"',1,1),'left')); 154 } 155 156 echo html_tag('tr', 157 html_tag('td',addSubmit(_("Import CSV File")),'center','','colspan="2"')); 158 159 echo "</table>\n"; 160 echo "</form>\n"; 161 echo "<!-- end csv import form -->\n"; 162 } elseif (!$aie_hide_upload_error) { 163 echo html_tag('table', 164 html_tag('tr', 165 html_tag('td', '<font color="'.$color[2].'"><strong>' . _("ERROR") . '</strong></font>', 'center', $color[0] ) ). 166 html_tag('tr', 167 html_tag('td', _("Address book uploads are disabled."), 'center' ) ), 168 'center', '', 'width="95%"' ); 169 } 170 171 echo "<br />\n"; 172 173 echo html_tag( 'table', 174 html_tag( 'tr', 175 html_tag( 'td', '<strong>' . _("Address book export") . '</strong>' . "\n", 'center', $color[0] ) 176 ), 177 'center', '', 'width="95%"' ); 178 179 echo "<!-- begin csv export form -->\n"; 180 echo '<form ENCTYPE="multipart/form-data" action="' 181 .sqm_baseuri() . 'plugins/abook_import_export/address_book_export.php' 182 .'" method="post">'; 183 184 echo '<table width="90%" border="0" cellpadding="1" cellspacing="0" align="center">' . "\n"; 185 186 echo html_tag('tr', 187 html_tag('td',_("Field delimiter:"),'right')."\n". 188 html_tag('td',addSelect('field_delimiter',$aie_delimiter,',',true),'left')); 189 190 echo html_tag('tr', 191 html_tag('td',_("Custom field delimiter:"),'right')."\n". 192 html_tag('td',addInput('custom_field_delimiter',',',1,1),'left')); 193 194 /** 195 * fgetcsv enclosure option is available since 4.3.0. 196 * plugin uses code that doesn't depend on 4.3+ functions. 197 * we leave same options in order to create backwards compatible exports. 198 */ 199 if (check_php_version(4,3,0)) { 200 echo html_tag('tr', 201 html_tag('td',_("Text delimiter:"),'right')."\n". 202 html_tag('td',addSelect('text_delimiter',$aie_delimiter,'"',true),'left')); 203 204 echo html_tag('tr', 205 html_tag('td',_("Custom text delimiter:"),'right')."\n". 206 html_tag('td',addInput('custom_text_delimiter','"',1,1),'left')); 207 } 208 209 $form=aie_select_backend('list',$bcount); 210 if ($bcount>1) { 211 echo html_tag('tr', 212 html_tag('td',_("Use address book:"),'right')."\n". 213 html_tag('td',$form,'left')); 214 } else { 215 echo $form; 216 } 217 218 echo html_tag('tr', 219 html_tag('td',addSubmit(_("Export to CSV File")),'center','','colspan="2"')); 220 221 echo "</table>"; 222 echo "</form>\n"; 223 echo "<!-- end csv export form -->\n"; 224 225 // revert domain 226 textdomain('squirrelmail'); 227} 228 229/** 230 * returns size integer formated in bytes, Kbytes or Mbytes 231 * @param integer $size size in bytes 232 * @return string formated size string 233 */ 234function aie_display_size($size) { 235 // make sure that it is integer. 236 $size=(int) $size; 237 238 $ret=''; 239 240 if ($size >= (1024*1024)) { 241 $ret = sprintf(_("%s MB"),round($size/(1024*1024),1)); 242 } elseif ($size >= 1024) { 243 $ret = sprintf(_("%s KB"),round($size/1024,1)); 244 } else { 245 $ret = sprintf(_("%s B"),$size); 246 } 247 return $ret; 248} 249 250/** 251 * Prints selection boxes in imported data table headers 252 * 253 * Send the field numbers entered in the text boxes by the user back to 254 * this script for more processing 255 * email is handled differently, not being an array 256 * @param integer $csvmax max number of columns 257 * @param integer $column column number 258 */ 259function aie_create_Select($csvmax,$column) { 260 // $column is the one that should be selected out of the bunch 261 echo "<select name=\"COL$column\">\n"; 262 263 if($column > 5) 264 $column = 5; // So we have only our normal choices. 265 266 for($temp = 0; $temp <= 5; $temp++) { 267 echo "<option value=\"$temp\""; 268 if ($column==$temp) 269 echo " selected"; 270 if ($temp == 0) 271 echo '>' . _("Nickname") . "</option>\n"; 272 if ($temp == 1) 273 echo '>' . _("First Name") . "</option>\n"; 274 if ($temp == 2) 275 echo '>' . _("Last Name") . "</option>\n"; 276 if ($temp == 3) 277 echo '>' . _("Email") . "</option>\n"; 278 if ($temp == 4) 279 echo '>' . _("Additional Info") . "</option>\n"; 280 if ($temp == 5) 281 echo '>' . _("Do Not Include") . "</option>\n"; 282 } 283 echo "</select>\n"; 284} 285 286/** 287 * @param string $row 288 * @param string $text_delimiter (since 1.0) gets POST parameter to fix 289 * escaped text delimiters. Works only in PHP 4.3.0+ 290 * @param array $csvorder (since 1.0) controls order imported address book fields 291 * @return mixed if string is returned - it contains fatal processing error. 292 * if array - processed csv data. Array keys should be counted. If count = 1, 293 * possible processing error. 294 */ 295function aie_CSVProcess($row,$text_delimiter, &$csvorder) { 296 global $aie_input_charsets, $default_charset; 297 298 // convert character set 299 if (sqgetGlobalVar('input_charset',$input_charset,SQ_POST) && 300 function_exists('charset_convert') && 301 $input_charset != $default_charset && 302 in_array($input_charset,$aie_input_charsets)) { 303 foreach($row as $key => $value) { 304 $row[$key] = charset_convert($input_charset,$value,$default_charset,false); 305 } 306 } 307 308 // undo escaped text delimiters 309 if (check_php_version(4,3,0)) { 310 foreach($row as $key => $value) { 311 $row[$key] = str_replace('\\' . $text_delimiter, $text_delimiter, $value); 312 } 313 } 314 315 // Make sure that it is not LDIF (use 'objectclass' attribute for detection) 316 if (preg_match("/^objectclass(?:)$/",trim($row[0])) || 317 preg_match("/^objectclass:.*$/",trim($row[0]))) { 318 return _("LDIF import is not supported."); 319 } 320 321 // detect header row 322 if (preg_grep("/((?:First Name)|(?:Last Name)|(?:E-mail Address))/",$row)) { 323 foreach($row as $key => $value) { 324 if ($value == "First Name" ) { 325 if(isset($csvorder[$key])) { 326 $csvorder[1] = $csvorder[$key]; 327 } else { 328 $csvorder[1]= $key; 329 } 330 } 331 if ($value == "Last Name" ) { 332 if(isset($csvorder[$key])) { 333 $csvorder[2] = $csvorder[$key]; 334 } else { 335 $csvorder[2]= $key; 336 } 337 } 338 if ($value == "E-mail Address" ) { 339 if(isset($csvorder[$key])) { 340 $csvorder[3] = $csvorder[$key]; 341 } else { 342 $csvorder[3]= $key; 343 } 344 } 345 } 346 return array(); 347 } 348 349 if (count($csvorder) > 0) { 350 // This is swapping elements to make firstname, last name, and email be in the 1,2,3 spot, respectively 351 foreach($csvorder as $key => $value) { 352 // check if field is set (maybe csv has less fields). 353 $temp = (isset($row[$key]) ? $row[$key] : ''); 354 $row[$key] = $row[$value]; 355 $row[$value] = $temp; 356 } 357 return $row; 358 } 359 return $row; 360} 361 362/** 363 * fgetcsv wrapper to solve differences between 4.3.0+ and older 364 * @param resource $handle 365 * @param integer $length 366 * @param string $delimiter 367 * @param string $enclosure 368 * @since 1.0 369 */ 370function aie_fgetcsv($handle,$length,$delimiter,$enclosure) { 371 if (check_php_version(4,3,0)) { 372 return fgetcsv($handle,$length,$delimiter,$enclosure); 373 } else { 374 return fgetcsv($handle,$length,$delimiter); 375 } 376} 377 378/** 379 * Creates address book selection options 380 * 381 * Tags use 'backend' input field. 382 * 383 * Backend ($v-bname, $v->listing and $v-writeable) specifics: 384 * 385 * local_file - writeable parameter is available. listing parameter 386 * is available since 1.5.1. Older listing behavior defaults to 387 * true 388 * 389 * global_file - backend is merged with local_file in 1.4.4 and 1.5.1. 390 * writeable parameter is available. listing parameter is 391 * not available and defaults to true. 392 * 393 * database - writeable parameter is available. listing parameter 394 * is available since 1.4.4 and 1.5.1. Older listing behavior 395 * defaults to true. 396 * 397 * ldap_server - writeable parameter is not available and backend is 398 * read only. listing parameter is available since 1.5.1. Older 399 * listing behavior defaults to false. number of returned results 400 * can be limited by backend options. backend can be uninitialized 401 * in some cases. 402 * 403 * Listing is evaluated by list_addr() function behavior. In some cases 404 * backends might allow listing with wide search in search() method, but 405 * such backend behavior is treated as unsupported and might be removed 406 * in some SquirrelMail version. 407 * @param string $listing_type all, write or list 408 * @param integer $backend_count returns number of available backends. 409 * It allows to detect which forms tags are used. 0 = empty string, 410 * 1 = hidden input, 2 or more = select box 411 * 412 * abook_import_export gettext domain must be initialized before calling 413 * this function, but code can use any domain 414 * @return string html form tags (select or hidden input) 415 * @since 1.0 416 */ 417function aie_select_backend($listing_type, &$backend_count) { 418 global $abook; 419 420 // save current gettext domain 421 $current_textdomain=textdomain(''); 422 423 if (empty($abook) || 424 ! is_object($abook) || 425 strtolower(get_class($abook))!='addressbook') { 426 427 if ($current_textdomain!='squirrelmail') { 428 /** 429 * switch domain. use short switch because plugin 430 * depends on php gettext or 1.5.1 gettext implementation 431 * and short switches work in both 432 */ 433 textdomain('squirrelmail'); 434 } 435 // init local and remote backends. don't show errors 436 $abook = addressbook_init(false); 437 } 438 439 // address book init failed. 440 if ($abook==false) { 441 // restore domain 442 textdomain($current_textdomain); 443 // inform about backend counter 444 $backend_count = 0; 445 return ''; 446 } 447 448 $available_abook = $abook->localbackend; 449 if ( $abook->numbackends > 1 ) { 450 $backends = $abook->get_backend_list(); 451 452 while (list($undef,$v) = each($backends)) { 453 switch ($v->bname) { 454 case 'ldap_server': 455 $writing_enabled = (isset($v->writeable) ? $v->writeable : false); 456 $listing_enabled = (isset($v->listing) ? $v->listing : false); 457 break; 458 default: 459 $writing_enabled = (isset($v->writeable) ? $v->writeable : true); 460 $listing_enabled = (isset($v->listing) ? $v->listing : true); 461 } 462 463 switch ($listing_type) { 464 case 'write': 465 if ($writing_enabled) { 466 // add each backend to array 467 $available_abooks[$v->bnum]=$v->sname; 468 // save backend number 469 $available_abook=$v->bnum; 470 } 471 break; 472 case 'list': 473 if ($listing_enabled) { 474 // add each backend to array 475 $available_abooks[$v->bnum]=$v->sname; 476 // save backend number 477 $available_abook=$v->bnum; 478 } 479 break; 480 default: 481 // add each backend to array 482 $available_abooks[$v->bnum]=$v->sname; 483 // save backend number 484 $available_abook=$v->bnum; 485 break; 486 } 487 } 488 if (count($available_abooks)>1) { 489 // restore domain 490 textdomain($current_textdomain); 491 // inform about backend counter 492 $backend_count = count($available_abooks); 493 // we have more than one writeable backend 494 return addSelect('backend',$available_abooks,null,true); 495 } 496 } 497 // restore domain 498 textdomain($current_textdomain); 499 // inform about backend counter 500 $backend_count = 1; 501 // Only one backend exists or is writeable. 502 return addHidden('backend', $available_abook); 503} 504 505/** 506 * Own error message function 507 * 508 * Function provides better controls than internal SquirrelMail 509 * error_box() function. 510 * @param string $error_msg 511 * @param string $error_title 512 * @param boolean $close_html 513 * @since 1.0 514 */ 515function aie_error_box($error_msg,$error_title='',$close_html=false) { 516 global $pageheader_sent, $color; 517 518 if ( !isset( $color ) ) { 519 $color = array(); 520 $color[0] = '#dcdcdc'; /* light gray TitleBar */ 521 $color[1] = '#800000'; /* red */ 522 $color[2] = '#cc0000'; /* light red Warning/Error Messages */ 523 $color[4] = '#ffffff'; /* white Normal Background */ 524 $color[7] = '#0000cc'; /* blue Links */ 525 $color[8] = '#000000'; /* black Normal text */ 526 $color[9] = '#ababab'; /* mid-gray Darker version of #0 */ 527 } 528 529 if (empty($error_title)) $error_title = _("ERROR"); 530 531 /* check if the page header has been sent; if not, send it! */ 532 if(!isset($pageheader_sent) && !$pageheader_sent) { 533 textdomain('squirrelmail'); 534 displayPageHeader($color,'None'); 535 $pageheader_sent = true; 536 echo "<body text=\"$color[8]\" bgcolor=\"$color[4]\" link=\"$color[7]\" vlink=\"$color[7]\" alink=\"$color[7]\">\n\n"; 537 } 538 539 echo '<table cellpadding="1" cellspacing="0" align="center" border="0" bgcolor="'.$color[9].'">'. 540 '<tr><td>'. 541 '<table width="100%" cellpadding="0" cellspacing="0" align="center" border="0" bgcolor="'.$color[4].'">'. 542 '<tr><td align="center" bgcolor="'.$color[0].'">'. 543 '<font color="'.$color[2].'"><b>' . $error_title . '</b></font>'. 544 '</td></tr><tr><td>'. 545 '<table cellpadding="1" cellspacing="5" align="center" border="0">'. 546 '<tr>' . html_tag( 'td', $error_msg."\n", 'left') . '</tr></table>'. 547 '</td></tr></table></td></tr></table>'; 548 549 if ($close_html) { 550 die('</body></html>'); 551 } else { 552 // revert domain 553 textdomain('abook_import_export'); 554 } 555} 556 557if (! function_exists('dgettext')) { 558 /** 559 * dgettext replacement for broken setups. 560 * @ignore 561 */ 562 function dgettext($domain,$str) { 563 return $str; 564 } 565} 566