Overview

Packages

  • Form
  • None

Classes

  • Horde_Form
  • Horde_Form_Action
  • Horde_Form_Action_conditional_enable
  • Horde_Form_Action_conditional_setvalue
  • Horde_Form_Action_ConditionalEnable
  • Horde_Form_Action_ConditionalSetValue
  • Horde_Form_Action_reload
  • Horde_Form_Action_setcursorpos
  • Horde_Form_Action_submit
  • Horde_Form_Action_sum_fields
  • Horde_Form_Action_updatefield
  • Horde_Form_Renderer
  • Horde_Form_Translation
  • Horde_Form_Type
  • Horde_Form_Type_address
  • Horde_Form_Type_addresslink
  • Horde_Form_Type_assign
  • Horde_Form_Type_boolean
  • Horde_Form_Type_captcha
  • Horde_Form_Type_category
  • Horde_Form_Type_cellphone
  • Horde_Form_Type_colorpicker
  • Horde_Form_Type_countedtext
  • Horde_Form_Type_country
  • Horde_Form_Type_creditcard
  • Horde_Form_Type_date
  • Horde_Form_Type_datetime
  • Horde_Form_Type_dblookup
  • Horde_Form_Type_description
  • Horde_Form_Type_email
  • Horde_Form_Type_emailConfirm
  • Horde_Form_Type_enum
  • Horde_Form_Type_figlet
  • Horde_Form_Type_file
  • Horde_Form_Type_header
  • Horde_Form_Type_hourminutesecond
  • Horde_Form_Type_image
  • Horde_Form_Type_int
  • Horde_Form_Type_intlist
  • Horde_Form_Type_invalid
  • Horde_Form_Type_ip6address
  • Horde_Form_Type_ipaddress
  • Horde_Form_Type_keyval_multienum
  • Horde_Form_Type_link
  • Horde_Form_Type_longtext
  • Horde_Form_Type_matrix
  • Horde_Form_Type_mlenum
  • Horde_Form_Type_monthdayyear
  • Horde_Form_Type_monthyear
  • Horde_Form_Type_multienum
  • Horde_Form_Type_number
  • Horde_Form_Type_obrowser
  • Horde_Form_Type_octal
  • Horde_Form_Type_password
  • Horde_Form_Type_passwordconfirm
  • Horde_Form_Type_pgp
  • Horde_Form_Type_phone
  • Horde_Form_Type_radio
  • Horde_Form_Type_selectfiles
  • Horde_Form_Type_set
  • Horde_Form_Type_smime
  • Horde_Form_Type_sorter
  • Horde_Form_Type_sound
  • Horde_Form_Type_spacer
  • Horde_Form_Type_stringarray
  • Horde_Form_Type_stringlist
  • Horde_Form_Type_tableset
  • Horde_Form_Type_text
  • Horde_Form_Type_time
  • Horde_Form_Variable
  • Overview
  • Package
  • Class
  • Tree
   1: <?php
   2: /**
   3:  * Horde_Form_Type Class
   4:  *
   5:  * @author  Robert E. Coyle <robertecoyle@hotmail.com>
   6:  * @package Form
   7:  */
   8: class Horde_Form_Type
   9: {
  10:     function getProperty($property)
  11:     {
  12:         $prop = '_' . $property;
  13:         return isset($this->$prop) ? $this->$prop : null;
  14:     }
  15: 
  16:     function __get($property)
  17:     {
  18:         return $this->getProperty($property);
  19:     }
  20: 
  21:     function setProperty($property, $value)
  22:     {
  23:         $prop = '_' . $property;
  24:         $this->$prop = $value;
  25:     }
  26: 
  27:     function __set($property, $value)
  28:     {
  29:         return $this->setProperty($property, $value);
  30:     }
  31: 
  32:     function init()
  33:     {
  34:     }
  35: 
  36:     function onSubmit()
  37:     {
  38:     }
  39: 
  40:     function isValid(&$var, &$vars, $value, &$message)
  41:     {
  42:         $message = '<strong>Error:</strong> Horde_Form_Type::isValid() called - should be overridden<br />';
  43:         return false;
  44:     }
  45: 
  46:     function getTypeName()
  47:     {
  48:         return str_replace('horde_form_type_', '', Horde_String::lower(get_class($this)));
  49:     }
  50: 
  51:     function getValues()
  52:     {
  53:         return null;
  54:     }
  55: 
  56:     function getInfo(&$vars, &$var, &$info)
  57:     {
  58:         $info = $var->getValue($vars);
  59:     }
  60: 
  61: }
  62: 
  63: class Horde_Form_Type_spacer extends Horde_Form_Type {
  64: 
  65:     function isValid(&$var, &$vars, $value, &$message)
  66:     {
  67:         return true;
  68:     }
  69: 
  70:     /**
  71:      * Return info about field type.
  72:      */
  73:     function about()
  74:     {
  75:         return array('name' => Horde_Form_Translation::t("Spacer"));
  76:     }
  77: 
  78: }
  79: 
  80: class Horde_Form_Type_header extends Horde_Form_Type {
  81: 
  82:     function isValid(&$var, &$vars, $value, &$message)
  83:     {
  84:         return true;
  85:     }
  86: 
  87:     /**
  88:      * Return info about field type.
  89:      */
  90:     function about()
  91:     {
  92:         return array('name' => Horde_Form_Translation::t("Header"));
  93:     }
  94: 
  95: }
  96: 
  97: class Horde_Form_Type_description extends Horde_Form_Type {
  98: 
  99:     function isValid(&$var, &$vars, $value, &$message)
 100:     {
 101:         return true;
 102:     }
 103: 
 104:     /**
 105:      * Return info about field type.
 106:      */
 107:     function about()
 108:     {
 109:         return array('name' => Horde_Form_Translation::t("Description"));
 110:     }
 111: 
 112: }
 113: 
 114: /**
 115:  * Simply renders its raw value in both active and inactive rendering.
 116:  */
 117: class Horde_Form_Type_html extends Horde_Form_Type {
 118: 
 119:     function isValid(&$var, &$vars, $value, &$message)
 120:     {
 121:         return true;
 122:     }
 123: 
 124:     /**
 125:      * Return info about field type.
 126:      */
 127:     function about()
 128:     {
 129:         return array('name' => Horde_Form_Translation::t("HTML"));
 130:     }
 131: 
 132: }
 133: 
 134: class Horde_Form_Type_number extends Horde_Form_Type {
 135: 
 136:     var $_fraction;
 137: 
 138:     function init($fraction = null)
 139:     {
 140:         $this->_fraction = $fraction;
 141:     }
 142: 
 143:     function isValid(&$var, &$vars, $value, &$message)
 144:     {
 145:         if ($var->isRequired() && empty($value) && ((string)(double)$value !== $value)) {
 146:             $message = Horde_Form_Translation::t("This field is required.");
 147:             return false;
 148:         } elseif (empty($value)) {
 149:             return true;
 150:         }
 151: 
 152:         /* If matched, then this is a correct numeric value. */
 153:         if (preg_match($this->_getValidationPattern(), $value)) {
 154:             return true;
 155:         }
 156: 
 157:         $message = Horde_Form_Translation::t("This field must be a valid number.");
 158:         return false;
 159:     }
 160: 
 161:     function _getValidationPattern()
 162:     {
 163:         static $pattern = '';
 164:         if (!empty($pattern)) {
 165:             return $pattern;
 166:         }
 167: 
 168:         /* Get current locale information. */
 169:         $linfo = Horde_Nls::getLocaleInfo();
 170: 
 171:         /* Build the pattern. */
 172:         $pattern = '(-)?';
 173: 
 174:         /* Only check thousands separators if locale has any. */
 175:         if (!empty($linfo['mon_thousands_sep'])) {
 176:             /* Regex to check for correct thousands separators (if any). */
 177:             $pattern .= '((\d+)|((\d{0,3}?)([' . $linfo['mon_thousands_sep'] . ']\d{3})*?))';
 178:         } else {
 179:             /* No locale thousands separator, check for only digits. */
 180:             $pattern .= '(\d+)';
 181:         }
 182:         /* If no decimal point specified default to dot. */
 183:         if (empty($linfo['mon_decimal_point'])) {
 184:             $linfo['mon_decimal_point'] = '.';
 185:         }
 186:         /* Regex to check for correct decimals (if any). */
 187:         if (empty($this->_fraction)) {
 188:             $fraction = '*';
 189:         } else {
 190:             $fraction = '{0,' . $this->_fraction . '}';
 191:         }
 192:         $pattern .= '([' . $linfo['mon_decimal_point'] . '](\d' . $fraction . '))?';
 193: 
 194:         /* Put together the whole regex pattern. */
 195:         $pattern = '/^' . $pattern . '$/';
 196: 
 197:         return $pattern;
 198:     }
 199: 
 200:     function getInfo(&$vars, &$var, &$info)
 201:     {
 202:         $value = $vars->get($var->getVarName());
 203:         $linfo = Horde_Nls::getLocaleInfo();
 204:         $value = str_replace($linfo['mon_thousands_sep'], '', $value);
 205:         $info = str_replace($linfo['mon_decimal_point'], '.', $value);
 206:     }
 207: 
 208:     /**
 209:      * Return info about field type.
 210:      */
 211:     function about()
 212:     {
 213:         return array('name' => Horde_Form_Translation::t("Number"));
 214:     }
 215: 
 216: }
 217: 
 218: class Horde_Form_Type_int extends Horde_Form_Type {
 219: 
 220:     function isValid(&$var, &$vars, $value, &$message)
 221:     {
 222:         if ($var->isRequired() && empty($value) && ((string)(int)$value !== $value)) {
 223:             $message = Horde_Form_Translation::t("This field is required.");
 224:             return false;
 225:         }
 226: 
 227:         if (empty($value) || preg_match('/^[0-9]+$/', $value)) {
 228:             return true;
 229:         }
 230: 
 231:         $message = Horde_Form_Translation::t("This field may only contain integers.");
 232:         return false;
 233:     }
 234: 
 235:     /**
 236:      * Return info about field type.
 237:      */
 238:     function about()
 239:     {
 240:         return array('name' => Horde_Form_Translation::t("Integer"));
 241:     }
 242: 
 243: }
 244: 
 245: class Horde_Form_Type_octal extends Horde_Form_Type {
 246: 
 247:     function isValid(&$var, &$vars, $value, &$message)
 248:     {
 249:         if ($var->isRequired() && empty($value) && ((string)(int)$value !== $value)) {
 250:             $message = Horde_Form_Translation::t("This field is required.");
 251:             return false;
 252:         }
 253: 
 254:         if (empty($value) || preg_match('/^[0-7]+$/', $value)) {
 255:             return true;
 256:         }
 257: 
 258:         $message = Horde_Form_Translation::t("This field may only contain octal values.");
 259:         return false;
 260:     }
 261: 
 262:     /**
 263:      * Return info about field type.
 264:      */
 265:     function about()
 266:     {
 267:         return array('name' => Horde_Form_Translation::t("Octal"));
 268:     }
 269: 
 270: }
 271: 
 272: class Horde_Form_Type_intlist extends Horde_Form_Type {
 273: 
 274:     function isValid(&$var, &$vars, $value, &$message)
 275:     {
 276:         if (empty($value) && $var->isRequired()) {
 277:             $message = Horde_Form_Translation::t("This field is required.");
 278:             return false;
 279:         }
 280: 
 281:         if (empty($value) || preg_match('/^[0-9 ,]+$/', $value)) {
 282:             return true;
 283:         }
 284: 
 285:         $message = Horde_Form_Translation::t("This field must be a comma or space separated list of integers");
 286:         return false;
 287:     }
 288: 
 289:     /**
 290:      * Return info about field type.
 291:      */
 292:     function about()
 293:     {
 294:         return array('name' => Horde_Form_Translation::t("Integer list"));
 295:     }
 296: 
 297: }
 298: 
 299: class Horde_Form_Type_text extends Horde_Form_Type {
 300: 
 301:     var $_regex;
 302:     var $_size;
 303:     var $_maxlength;
 304: 
 305:     /**
 306:      * The initialisation function for the text variable type.
 307:      *
 308:      * @access private
 309:      *
 310:      * @param string $regex       Any valid PHP PCRE pattern syntax that
 311:      *                            needs to be matched for the field to be
 312:      *                            considered valid. If left empty validity
 313:      *                            will be checked only for required fields
 314:      *                            whether they are empty or not.
 315:      *                            If using this regex test it is advisable
 316:      *                            to enter a description for this field to
 317:      *                            warn the user what is expected, as the
 318:      *                            generated error message is quite generic
 319:      *                            and will not give any indication where
 320:      *                            the regex failed.
 321:      * @param integer $size       The size of the input field.
 322:      * @param integer $maxlength  The max number of characters.
 323:      */
 324:     function init($regex = '', $size = 40, $maxlength = null)
 325:     {
 326:         $this->_regex     = $regex;
 327:         $this->_size      = $size;
 328:         $this->_maxlength = $maxlength;
 329:     }
 330: 
 331:     function isValid(&$var, &$vars, $value, &$message)
 332:     {
 333:         $valid = true;
 334: 
 335:         if (!empty($this->_maxlength) && Horde_String::length($value) > $this->_maxlength) {
 336:             $valid = false;
 337:             $message = sprintf(Horde_Form_Translation::t("Value is over the maximum length of %d."), $this->_maxlength);
 338:         } elseif ($var->isRequired() && empty($this->_regex)) {
 339:             $valid = strlen(trim($value)) > 0;
 340: 
 341:             if (!$valid) {
 342:                 $message = Horde_Form_Translation::t("This field is required.");
 343:             }
 344:         } elseif (!empty($this->_regex)) {
 345:             $valid = preg_match($this->_regex, $value);
 346: 
 347:             if (!$valid) {
 348:                 $message = Horde_Form_Translation::t("You must enter a valid value.");
 349:             }
 350:         }
 351: 
 352:         return $valid;
 353:     }
 354: 
 355:     function getSize()
 356:     {
 357:         return $this->_size;
 358:     }
 359: 
 360:     function getMaxLength()
 361:     {
 362:         return $this->_maxlength;
 363:     }
 364: 
 365:     /**
 366:      * Return info about field type.
 367:      */
 368:     function about()
 369:     {
 370:         return array(
 371:             'name' => Horde_Form_Translation::t("Text"),
 372:             'params' => array(
 373:                 'regex'     => array('label' => Horde_Form_Translation::t("Regex"),
 374:                                      'type'  => 'text'),
 375:                 'size'      => array('label' => Horde_Form_Translation::t("Size"),
 376:                                      'type'  => 'int'),
 377:                 'maxlength' => array('label' => Horde_Form_Translation::t("Maximum length"),
 378:                                      'type'  => 'int')));
 379:     }
 380: 
 381: }
 382: 
 383: class Horde_Form_Type_stringlist extends Horde_Form_Type_text {
 384: 
 385:     /**
 386:      * Return info about field type.
 387:      */
 388:     function about()
 389:     {
 390:         return array(
 391:             'name' => Horde_Form_Translation::t("String list"),
 392:             'params' => array(
 393:                 'regex'     => array('label' => Horde_Form_Translation::t("Regex"),
 394:                                      'type'  => 'text'),
 395:                 'size'      => array('label' => Horde_Form_Translation::t("Size"),
 396:                                      'type'  => 'int'),
 397:                 'maxlength' => array('label' => Horde_Form_Translation::t("Maximum length"),
 398:                                      'type'  => 'int')),
 399:         );
 400:     }
 401: 
 402: }
 403: 
 404: class Horde_Form_Type_stringarray extends Horde_Form_Type_stringlist {
 405: 
 406:     function getInfo(&$vars, &$var, &$info)
 407:     {
 408:         $info = array_map('trim', explode(',', $vars->get($var->getVarName())));
 409:     }
 410: 
 411:     /**
 412:      * Return info about field type.
 413:      */
 414:     function about()
 415:     {
 416:         return array(
 417:             'name' => Horde_Form_Translation::t("String list returning an array"),
 418:             'params' => array(
 419:                 'regex'     => array('label' => Horde_Form_Translation::t("Regex"),
 420:                                      'type'  => 'text'),
 421:                 'size'      => array('label' => Horde_Form_Translation::t("Size"),
 422:                                      'type'  => 'int'),
 423:                 'maxlength' => array('label' => Horde_Form_Translation::t("Maximum length"),
 424:                                      'type'  => 'int')),
 425:         );
 426:     }
 427: 
 428: }
 429: 
 430: class Horde_Form_Type_phone extends Horde_Form_Type {
 431: 
 432:     /**
 433:      * The size of the input field.
 434:      *
 435:      * @var integer
 436:      */
 437:     var $_size;
 438: 
 439:     /**
 440:      * @param integer $size  The size of the input field.
 441:      */
 442:     function init($size = 15)
 443:     {
 444:         $this->_size = $size;
 445:     }
 446: 
 447:     function isValid(&$var, &$vars, $value, &$message)
 448:     {
 449:         if (!strlen(trim($value))) {
 450:             if ($var->isRequired()) {
 451:                 $message = Horde_Form_Translation::t("This field is required.");
 452:                 return false;
 453:             }
 454:         } elseif (!preg_match('/^\+?[\d()\-\/. ]*$/', $value)) {
 455:             $message = Horde_Form_Translation::t("You must enter a valid phone number, digits only with an optional '+' for the international dialing prefix.");
 456:             return false;
 457:         }
 458: 
 459:         return true;
 460:     }
 461: 
 462:     function getSize()
 463:     {
 464:         return $this->_size;
 465:     }
 466: 
 467:     /**
 468:      * Return info about field type.
 469:      */
 470:     function about()
 471:     {
 472:         return array(
 473:             'name' => Horde_Form_Translation::t("Phone number"),
 474:             'params' => array(
 475:                 'size'      => array('label' => Horde_Form_Translation::t("Size"),
 476:                                      'type'  => 'int'),
 477:             ),
 478:         );
 479:     }
 480: 
 481: }
 482: 
 483: class Horde_Form_Type_cellphone extends Horde_Form_Type_phone {
 484: 
 485:     /**
 486:      * Return info about field type.
 487:      */
 488:     function about()
 489:     {
 490:         return array('name' => Horde_Form_Translation::t("Mobile phone number"));
 491:     }
 492: 
 493: }
 494: 
 495: class Horde_Form_Type_ipaddress extends Horde_Form_Type_text {
 496: 
 497:     function isValid(&$var, &$vars, $value, &$message)
 498:     {
 499:         $valid = true;
 500: 
 501:         if (strlen(trim($value)) > 0) {
 502:             $ip = explode('.', $value);
 503:             $valid = (count($ip) == 4);
 504:             if ($valid) {
 505:                 foreach ($ip as $part) {
 506:                     if (!is_numeric($part) ||
 507:                         $part > 255 ||
 508:                         $part < 0) {
 509:                         $valid = false;
 510:                         break;
 511:                     }
 512:                 }
 513:             }
 514: 
 515:             if (!$valid) {
 516:                 $message = Horde_Form_Translation::t("Please enter a valid IP address.");
 517:             }
 518:         } elseif ($var->isRequired()) {
 519:             $valid = false;
 520:             $message = Horde_Form_Translation::t("This field is required.");
 521:         }
 522: 
 523:         return $valid;
 524:     }
 525: 
 526:     /**
 527:      * Return info about field type.
 528:      */
 529:     function about()
 530:     {
 531:         return array('name' => Horde_Form_Translation::t("IP address"));
 532:     }
 533: 
 534: }
 535: 
 536: class Horde_Form_Type_ip6address extends Horde_Form_Type_text {
 537: 
 538:     function isValid(&$var, &$vars, $value, &$message)
 539:     {
 540:         $valid = true;
 541: 
 542:         if (strlen(trim($value)) > 0) {
 543:             $valid = @inet_pton($value);
 544: 
 545:             if ($valid === false) {
 546:                 $message = Horde_Form_Translation::t("Please enter a valid IP address.");
 547:             }
 548:         } elseif ($var->isRequired()) {
 549:             $valid = false;
 550:             $message = Horde_Form_Translation::t("This field is required.");
 551:         }
 552: 
 553:         return true;
 554:     }
 555: 
 556:     /**
 557:      * Return info about field type.
 558:      */
 559:     function about()
 560:     {
 561:         return array('name' => Horde_Form_Translation::t("IPv6 address"));
 562:     }
 563: 
 564: }
 565: 
 566: class Horde_Form_Type_longtext extends Horde_Form_Type_text {
 567: 
 568:     var $_rows;
 569:     var $_cols;
 570:     var $_helper = array();
 571: 
 572:     function init($rows = 8, $cols = 80, $helper = array())
 573:     {
 574:         if (!is_array($helper)) {
 575:             $helper = array($helper);
 576:         }
 577: 
 578:         $this->_rows = $rows;
 579:         $this->_cols = $cols;
 580:         $this->_helper = $helper;
 581:     }
 582: 
 583:     function getRows()
 584:     {
 585:         return $this->_rows;
 586:     }
 587: 
 588:     function getCols()
 589:     {
 590:         return $this->_cols;
 591:     }
 592: 
 593:     function hasHelper($option = '')
 594:     {
 595:         if (empty($option)) {
 596:             /* No option specified, check if any helpers have been
 597:              * activated. */
 598:             return !empty($this->_helper);
 599:         } elseif (empty($this->_helper)) {
 600:             /* No helpers activated at all, return false. */
 601:             return false;
 602:         } else {
 603:             /* Check if given helper has been activated. */
 604:             return in_array($option, $this->_helper);
 605:         }
 606:     }
 607: 
 608:     /**
 609:      * Return info about field type.
 610:      */
 611:     function about()
 612:     {
 613:         return array(
 614:             'name' => Horde_Form_Translation::t("Long text"),
 615:             'params' => array(
 616:                 'rows'   => array('label' => Horde_Form_Translation::t("Number of rows"),
 617:                                   'type'  => 'int'),
 618:                 'cols'   => array('label' => Horde_Form_Translation::t("Number of columns"),
 619:                                   'type'  => 'int'),
 620:                 'helper' => array('label' => Horde_Form_Translation::t("Helpers"),
 621:                                   'type'  => 'stringarray')));
 622:     }
 623: 
 624: }
 625: 
 626: class Horde_Form_Type_countedtext extends Horde_Form_Type_longtext {
 627: 
 628:     var $_chars;
 629: 
 630:     function init($rows = null, $cols = null, $chars = 1000)
 631:     {
 632:         parent::init($rows, $cols);
 633:         $this->_chars = $chars;
 634:     }
 635: 
 636:     function isValid(&$var, &$vars, $value, &$message)
 637:     {
 638:         $valid = true;
 639: 
 640:         $length = Horde_String::length(trim($value));
 641: 
 642:         if ($var->isRequired() && $length <= 0) {
 643:             $valid = false;
 644:             $message = Horde_Form_Translation::t("This field is required.");
 645:         } elseif ($length > $this->_chars) {
 646:             $valid = false;
 647:             $message = sprintf(Horde_Form_Translation::ngettext("There are too many characters in this field. You have entered %d character; ", "There are too many characters in this field. You have entered %d characters; ", $length), $length)
 648:                 . sprintf(Horde_Form_Translation::t("you must enter less than %d."), $this->_chars);
 649:         }
 650: 
 651:         return $valid;
 652:     }
 653: 
 654:     function getChars()
 655:     {
 656:         return $this->_chars;
 657:     }
 658: 
 659:     /**
 660:      * Return info about field type.
 661:      */
 662:     function about()
 663:     {
 664:         return array(
 665:             'name' => Horde_Form_Translation::t("Counted text"),
 666:             'params' => array(
 667:                 'rows'  => array('label' => Horde_Form_Translation::t("Number of rows"),
 668:                                  'type'  => 'int'),
 669:                 'cols'  => array('label' => Horde_Form_Translation::t("Number of columns"),
 670:                                  'type'  => 'int'),
 671:                 'chars' => array('label' => Horde_Form_Translation::t("Number of characters"),
 672:                                  'type'  => 'int')));
 673:     }
 674: 
 675: }
 676: 
 677: class Horde_Form_Type_address extends Horde_Form_Type_longtext {
 678: 
 679:     function parse($address)
 680:     {
 681:         $info = array();
 682:         $aus_state_regex = '(?:ACT|NSW|NT|QLD|SA|TAS|VIC|WA)';
 683: 
 684:         if (preg_match('/(?s)(.*?)(?-s)\r?\n(?:(.*?)\s+)?((?:A[BL]|B[ABDHLNRST]?|C[ABFHMORTVW]|D[ADEGHLNTY]|E[CHNX]?|F[KY]|G[LUY]?|H[ADGPRSUX]|I[GMPV]|JE|K[ATWY]|L[ADELNSU]?|M[EKL]?|N[EGNPRW]?|O[LX]|P[AEHLOR]|R[GHM]|S[AEGKLMNOPRSTWY]?|T[ADFNQRSW]|UB|W[ACDFNRSV]?|YO|ZE)\d(?:\d|[A-Z])? \d[A-Z]{2})/', $address, $addressParts)) {
 685:             /* UK postcode detected. */
 686:             $info = array('country' => 'uk', 'zip' => $addressParts[3]);
 687:             if (!empty($addressParts[1])) {
 688:                 $info['street'] = $addressParts[1];
 689:             }
 690:             if (!empty($addressParts[2])) {
 691:                 $info['city'] = $addressParts[2];
 692:             }
 693:         } elseif (preg_match('/\b' . $aus_state_regex . '\b/', $address)) {
 694:             /* Australian state detected. */
 695:             /* Split out the address, line-by-line. */
 696:             $addressLines = preg_split('/\r?\n/', $address);
 697:             $info = array('country' => 'au');
 698:             for ($i = 0; $i < count($addressLines); $i++) {
 699:                 /* See if it's the street number & name. */
 700:                 if (preg_match('/(\d+\s*\/\s*)?(\d+|\d+[a-zA-Z])\s+([a-zA-Z ]*)/', $addressLines[$i], $lineParts)) {
 701:                     $info['street'] = $addressLines[$i];
 702:                     $info['streetNumber'] = $lineParts[2];
 703:                     $info['streetName'] = $lineParts[3];
 704:                 }
 705:                 /* Look for "Suburb, State". */
 706:                 if (preg_match('/([a-zA-Z ]*),?\s+(' . $aus_state_regex . ')/', $addressLines[$i], $lineParts)) {
 707:                     $info['city'] = $lineParts[1];
 708:                     $info['state'] = $lineParts[2];
 709:                 }
 710:                 /* Look for "State <4 digit postcode>". */
 711:                 if (preg_match('/(' . $aus_state_regex . ')\s+(\d{4})/', $addressLines[$i], $lineParts)) {
 712:                     $info['state'] = $lineParts[1];
 713:                     $info['zip'] = $lineParts[2];
 714:                 }
 715:             }
 716:         } elseif (preg_match('/(?s)(.*?)(?-s)\r?\n(.*)\s*,\s*(\w+)\.?\s+(\d+|[a-zA-Z]\d[a-zA-Z]\s?\d[a-zA-Z]\d)/', $address, $addressParts)) {
 717:             /* American/Canadian address style. */
 718:             $info = array('country' => 'us');
 719:             if (!empty($addressParts[4]) &&
 720:                 preg_match('|[a-zA-Z]\d[a-zA-Z]\s?\d[a-zA-Z]\d|', $addressParts[4])) {
 721:                 $info['country'] = 'ca';
 722:             }
 723:             if (!empty($addressParts[1])) {
 724:                 $info['street'] = $addressParts[1];
 725:             }
 726:             if (!empty($addressParts[2])) {
 727:                 $info['city'] = $addressParts[2];
 728:             }
 729:             if (!empty($addressParts[3])) {
 730:                 $info['state'] = $addressParts[3];
 731:             }
 732:             if (!empty($addressParts[4])) {
 733:                 $info['zip'] = $addressParts[4];
 734:             }
 735:         } elseif (preg_match('/(?:(?s)(.*?)(?-s)(?:\r?\n|,\s*))?(?:([A-Z]{1,3})-)?(\d{4,5})\s+(.*)(?:\r?\n(.*))?/i', $address, $addressParts)) {
 736:             /* European address style. */
 737:             $info = array();
 738:             if (!empty($addressParts[1])) {
 739:                 $info['street'] = $addressParts[1];
 740:             }
 741:             if (!empty($addressParts[2])) {
 742:                 include 'Horde/Nls/Carsigns.php';
 743:                 $country = array_search(Horde_String::upper($addressParts[2]), $carsigns);
 744:                 if ($country) {
 745:                     $info['country'] = $country;
 746:                 }
 747:             }
 748:             if (!empty($addressParts[5])) {
 749:                 include 'Horde/Nls/Countries.php';
 750:                 $country = array_search($addressParts[5], $countries);
 751:                 if ($country) {
 752:                     $info['country'] = Horde_String::lower($country);
 753:                 } elseif (!isset($info['street'])) {
 754:                     $info['street'] = trim($addressParts[5]);
 755:                 } else {
 756:                     $info['street'] .= "\n" . $addressParts[5];
 757:                 }
 758:             }
 759:             if (!empty($addressParts[3])) {
 760:                 $info['zip'] = $addressParts[3];
 761:             }
 762:             if (!empty($addressParts[4])) {
 763:                 $info['city'] = trim($addressParts[4]);
 764:             }
 765:         }
 766: 
 767:         return $info;
 768:     }
 769: 
 770:     /**
 771:      * Return info about field type.
 772:      */
 773:     function about()
 774:     {
 775:         return array(
 776:             'name' => Horde_Form_Translation::t("Address"),
 777:             'params' => array(
 778:                 'rows' => array('label' => Horde_Form_Translation::t("Number of rows"),
 779:                                 'type'  => 'int'),
 780:                 'cols' => array('label' => Horde_Form_Translation::t("Number of columns"),
 781:                                 'type'  => 'int')));
 782:     }
 783: 
 784: }
 785: 
 786: class Horde_Form_Type_addresslink extends Horde_Form_Type_address {
 787: 
 788:     function isValid(&$var, &$vars, $value, &$message)
 789:     {
 790:         return true;
 791:     }
 792: 
 793:     /**
 794:      * Return info about field type.
 795:      */
 796:     function about()
 797:     {
 798:         return array('name' => Horde_Form_Translation::t("Address Link"));
 799:     }
 800: 
 801: }
 802: 
 803: class Horde_Form_Type_pgp extends Horde_Form_Type_longtext {
 804: 
 805:     /**
 806:      * Path to the GnuPG binary.
 807:      *
 808:      * @var string
 809:      */
 810:     var $_gpg;
 811: 
 812:     /**
 813:      * A temporary directory.
 814:      *
 815:      * @var string
 816:      */
 817:     var $_temp;
 818: 
 819:     function init($gpg, $temp_dir = null, $rows = null, $cols = null)
 820:     {
 821:         $this->_gpg = $gpg;
 822:         $this->_temp = $temp_dir;
 823:         parent::init($rows, $cols);
 824:     }
 825: 
 826:     /**
 827:      * Returns a parameter hash for the Horde_Crypt_pgp constructor.
 828:      *
 829:      * @return array  A parameter hash.
 830:      */
 831:     function getPGPParams()
 832:     {
 833:         return array('program' => $this->_gpg, 'temp' => $this->_temp);
 834:     }
 835: 
 836:     /**
 837:      * Return info about field type.
 838:      */
 839:     function about()
 840:     {
 841:         return array(
 842:             'name' => Horde_Form_Translation::t("PGP Key"),
 843:             'params' => array(
 844:                 'gpg'      => array('label' => Horde_Form_Translation::t("Path to the GnuPG binary"),
 845:                                     'type'  => 'string'),
 846:                 'temp_dir' => array('label' => Horde_Form_Translation::t("A temporary directory"),
 847:                                     'type'  => 'string'),
 848:                 'rows'     => array('label' => Horde_Form_Translation::t("Number of rows"),
 849:                                     'type'  => 'int'),
 850:                 'cols'     => array('label' => Horde_Form_Translation::t("Number of columns"),
 851:                                     'type'  => 'int')));
 852:     }
 853: 
 854: }
 855: 
 856: class Horde_Form_Type_smime extends Horde_Form_Type_longtext {
 857: 
 858:     /**
 859:      * A temporary directory.
 860:      *
 861:      * @var string
 862:      */
 863:     var $_temp;
 864: 
 865:     function init($temp_dir = null, $rows = null, $cols = null)
 866:     {
 867:         $this->_temp = $temp_dir;
 868:         parent::init($rows, $cols);
 869:     }
 870: 
 871:     /**
 872:      * Returns a parameter hash for the Horde_Crypt_smime constructor.
 873:      *
 874:      * @return array  A parameter hash.
 875:      */
 876:     function getSMIMEParams()
 877:     {
 878:         return array('temp' => $this->_temp);
 879:     }
 880: 
 881:     /**
 882:      * Return info about field type.
 883:      */
 884:     function about()
 885:     {
 886:         return array(
 887:             'name' => Horde_Form_Translation::t("S/MIME Key"),
 888:             'params' => array(
 889:                 'temp_dir' => array('label' => Horde_Form_Translation::t("A temporary directory"),
 890:                                     'type'  => 'string'),
 891:                 'rows'     => array('label' => Horde_Form_Translation::t("Number of rows"),
 892:                                     'type'  => 'int'),
 893:                 'cols'     => array('label' => Horde_Form_Translation::t("Number of columns"),
 894:                                     'type'  => 'int')));
 895:     }
 896: 
 897: }
 898: 
 899: class Horde_Form_Type_country extends Horde_Form_Type_enum {
 900: 
 901:     function init($prompt = null)
 902:     {
 903:         parent::init(Horde_Nls::getCountryISO(), $prompt);
 904:     }
 905: 
 906:     /**
 907:      * Return info about field type.
 908:      */
 909:     function about()
 910:     {
 911:         return array(
 912:             'name' => Horde_Form_Translation::t("Country drop down list"),
 913:             'params' => array(
 914:                 'prompt' => array('label' => Horde_Form_Translation::t("Prompt text"),
 915:                                   'type'  => 'text')));
 916:     }
 917: 
 918: }
 919: 
 920: class Horde_Form_Type_file extends Horde_Form_Type {
 921: 
 922:     function isValid(&$var, &$vars, $value, &$message)
 923:     {
 924:         if ($var->isRequired()) {
 925:             try {
 926:                 $GLOBALS['browser']->wasFileUploaded($var->getVarName());
 927:             } catch (Horde_Browser_Exception $e) {
 928:                 $message = $e->getMessage();
 929:                 return false;
 930:             }
 931:         }
 932: 
 933:         return true;
 934:     }
 935: 
 936:     function getInfo(&$vars, &$var, &$info)
 937:     {
 938:         $name = $var->getVarName();
 939:         try {
 940:             $GLOBALS['browser']->wasFileUploaded($name);
 941:             $info['name'] = Horde_Util::dispelMagicQuotes($_FILES[$name]['name']);
 942:             $info['type'] = $_FILES[$name]['type'];
 943:             $info['tmp_name'] = $_FILES[$name]['tmp_name'];
 944:             $info['file'] = $_FILES[$name]['tmp_name'];
 945:             $info['error'] = $_FILES[$name]['error'];
 946:             $info['size'] = $_FILES[$name]['size'];
 947:         } catch (Horde_Browser_Exception $e) {}
 948:     }
 949: 
 950:     /**
 951:      * Return info about field type.
 952:      */
 953:     function about()
 954:     {
 955:         return array('name' => Horde_Form_Translation::t("File upload"));
 956:     }
 957: 
 958: }
 959: 
 960: class Horde_Form_Type_image extends Horde_Form_Type {
 961: 
 962:     /**
 963:      * Has a file been uploaded on this form submit?
 964:      *
 965:      * @var boolean
 966:      */
 967:     var $_uploaded = null;
 968: 
 969:     /**
 970:      * Show the upload button?
 971:      *
 972:      * @var boolean
 973:      */
 974:     var $_show_upload = true;
 975: 
 976:     /**
 977:      * Show the option to upload also original non-modified image?
 978:      *
 979:      * @var boolean
 980:      */
 981:     var $_show_keeporig = false;
 982: 
 983:     /**
 984:      * Limit the file size?
 985:      *
 986:      * @var integer
 987:      */
 988:     var $_max_filesize = null;
 989: 
 990:     /**
 991:      * Hash containing the previously uploaded image info.
 992:      *
 993:      * @var array
 994:      */
 995:     var $_img;
 996: 
 997:     /**
 998:      * A random id that identifies the image information in the session data.
 999:      *
1000:      * @var string
1001:      */
1002:     var $_random;
1003: 
1004:     function init($show_upload = true, $show_keeporig = false, $max_filesize = null)
1005:     {
1006:         $this->_show_upload   = $show_upload;
1007:         $this->_show_keeporig = $show_keeporig;
1008:         $this->_max_filesize  = $max_filesize;
1009:     }
1010: 
1011:     function onSubmit(&$var, &$vars)
1012:     {
1013:         /* Are we removing an image? */
1014:         if ($vars->get('remove_' . $var->getVarName())) {
1015:             $GLOBALS['session']->remove('horde', 'form/' . $this->getRandomId());
1016:             $this->_img = null;
1017:             return;
1018:         }
1019: 
1020:         /* Get the upload. */
1021:         $this->getImage($vars, $var);
1022: 
1023:         /* If this was done through the upload button override the submitted
1024:          * value of the form. */
1025:         if ($vars->get('do_' . $var->getVarName())) {
1026:             $var->form->setSubmitted(false);
1027:             if ($this->_uploaded instanceof Horde_Browser_Exception) {
1028:                 $this->_img = array('hash' => $this->getRandomId(),
1029:                                     'error' => $this->_uploaded->getMessage());
1030:             }
1031:         }
1032:     }
1033: 
1034:     function isValid(&$var, &$vars, $value, &$message)
1035:     {
1036: 
1037:         if ($vars->get('remove_' . $var->getVarName())) {
1038:             return true;
1039:         }
1040: 
1041:         /* Get the upload. */
1042:         $this->getImage($vars, $var);
1043:         $field = $vars->get($var->getVarName());
1044: 
1045:         /* The upload generated a PEAR Error. */
1046:         if ($this->_uploaded instanceof Horde_Browser_Exception) {
1047:             /* Not required and no image upload attempted. */
1048:             if (!$var->isRequired() && empty($field['hash']) &&
1049:                 $this->_uploaded->getCode() == UPLOAD_ERR_NO_FILE) {
1050:                 return true;
1051:             }
1052: 
1053:             if (($this->_uploaded->getCode() == UPLOAD_ERR_NO_FILE) &&
1054:                 empty($field['hash'])) {
1055:                 /* Nothing uploaded and no older upload. */
1056:                 $message = Horde_Form_Translation::t("This field is required.");
1057:                 return false;
1058:             } elseif (!empty($field['hash'])) {
1059:                 if ($this->_img && isset($this->_img['error'])) {
1060:                     $message = $this->_img['error'];
1061:                     return false;
1062:                 }
1063:                 /* Nothing uploaded but older upload present. */
1064:                 return true;
1065:             } else {
1066:                 /* Some other error message. */
1067:                 $message = $this->_uploaded->getMessage();
1068:                 return false;
1069:             }
1070:         } elseif (empty($this->_img['img']['size'])) {
1071:             $message = Horde_Form_Translation::t("The image file size could not be determined or it was 0 bytes. The upload may have been interrupted.");
1072:             return false;
1073:         } elseif ($this->_max_filesize &&
1074:                   $this->_img['img']['size'] > $this->_max_filesize) {
1075:             $message = sprintf(Horde_Form_Translation::t("The image file was larger than the maximum allowed size (%d bytes)."), $this->_max_filesize);
1076:             return false;
1077:         }
1078: 
1079:         return true;
1080:     }
1081: 
1082:     function getInfo(&$vars, &$var, &$info)
1083:     {
1084:         /* Get the upload. */
1085:         $this->getImage($vars, $var);
1086: 
1087:         /* Get image params stored in the hidden field. */
1088:         $value = $var->getValue($vars);
1089:         $info = $this->_img['img'];
1090:         if (empty($info['file'])) {
1091:             unset($info['file']);
1092:             return;
1093:         }
1094:         if ($this->_show_keeporig) {
1095:             $info['keep_orig'] = !empty($value['keep_orig']);
1096:         }
1097: 
1098:         /* Set the uploaded value (either true or Horde_Browser_Exception). */
1099:         $info['uploaded'] = &$this->_uploaded;
1100: 
1101:         /* If a modified file exists move it over the original. */
1102:         if ($this->_show_keeporig && $info['keep_orig']) {
1103:             /* Requested the saving of original file also. */
1104:             $info['orig_file'] = Horde::getTempDir() . '/' . $info['file'];
1105:             $info['file'] = Horde::getTempDir() . '/mod_' . $info['file'];
1106:             /* Check if a modified file actually exists. */
1107:             if (!file_exists($info['file'])) {
1108:                 $info['file'] = $info['orig_file'];
1109:                 unset($info['orig_file']);
1110:             }
1111:         } else {
1112:             /* Saving of original not required. */
1113:             $mod_file = Horde::getTempDir() . '/mod_' . $info['file'];
1114:             $info['file'] = Horde::getTempDir() . '/' . $info['file'];
1115: 
1116:             if (file_exists($mod_file)) {
1117:                 /* Unlink first (has to be done on Windows machines?) */
1118:                 unlink($info['file']);
1119:                 rename($mod_file, $info['file']);
1120:             }
1121:         }
1122:     }
1123: 
1124:     /**
1125:      * Gets the upload and sets up the upload data array. Either
1126:      * fetches an upload done with this submit or retries stored
1127:      * upload info.
1128:      */
1129:     function _getUpload(&$vars, &$var)
1130:     {
1131:         global $session;
1132: 
1133:         /* Don't bother with this function if already called and set
1134:          * up vars. */
1135:         if (!empty($this->_img)) {
1136:             return true;
1137:         }
1138: 
1139:         /* Check if file has been uploaded. */
1140:         $varname = $var->getVarName();
1141: 
1142:         try {
1143:             $GLOBALS['browser']->wasFileUploaded($varname . '[new]');
1144:             $this->_uploaded = true;
1145: 
1146:             /* A file has been uploaded on this submit. Save to temp dir for
1147:              * preview work. */
1148:             $this->_img['img']['type'] = $this->getUploadedFileType($varname . '[new]');
1149: 
1150:             /* Get the other parts of the upload. */
1151:             Horde_Array::getArrayParts($varname . '[new]', $base, $keys);
1152: 
1153:             /* Get the temporary file name. */
1154:             $keys_path = array_merge(array($base, 'tmp_name'), $keys);
1155:             $this->_img['img']['file'] = Horde_Array::getElement($_FILES, $keys_path);
1156: 
1157:             /* Get the actual file name. */
1158:             $keys_path = array_merge(array($base, 'name'), $keys);
1159:             $this->_img['img']['name'] = Horde_Array::getElement($_FILES, $keys_path);
1160: 
1161:             /* Get the file size. */
1162:             $keys_path = array_merge(array($base, 'size'), $keys);
1163:             $this->_img['img']['size'] = Horde_Array::getElement($_FILES, $keys_path);
1164: 
1165:             /* Get any existing values for the image upload field. */
1166:             $upload = $vars->get($var->getVarName());
1167:             if (!empty($upload['hash'])) {
1168:                 $upload['img'] = $session->get('horde', 'form/' . $upload['hash']);
1169:                 $session->remove('horde', 'form/' . $upload['hash']);
1170:             }
1171: 
1172:             /* Get the temp file if already one uploaded, otherwise create a
1173:              * new temporary file. */
1174:             if (!empty($upload['img']['file'])) {
1175:                 $tmp_file = Horde::getTempDir() . '/' . $upload['img']['file'];
1176:             } else {
1177:                 $tmp_file = Horde::getTempFile('Horde', false);
1178:             }
1179: 
1180:             /* Move the browser created temp file to the new temp file. */
1181:             move_uploaded_file($this->_img['img']['file'], $tmp_file);
1182:             $this->_img['img']['file'] = basename($tmp_file);
1183:         } catch (Horde_Browser_Exception $e) {
1184:             $this->_uploaded = $e;
1185: 
1186:             /* File has not been uploaded. */
1187:             $upload = $vars->get($var->getVarName());
1188: 
1189:             /* File is explicitly removed */
1190:             if ($vars->get('remove_' . $var->getVarName())) {
1191:                 $this->_img = null;
1192:                 $session->remove('horde', 'form/' . $upload['hash']);
1193:                 return;
1194:             }
1195: 
1196:             if ($this->_uploaded->getCode() == 4 &&
1197:                 !empty($upload['hash']) &&
1198:                 $session->exists('horde', 'form/' . $upload['hash'])) {
1199:                 $this->_img['img'] = $session->get('horde', 'form/' . $upload['hash']);
1200:                 $session->remove('horde', 'form/' . $upload['hash']);
1201:                 if (isset($this->_img['error'])) {
1202:                     $this->_uploaded = PEAR::raiseError($this->_img['error']);
1203:                 }
1204:             }
1205:         }
1206:         if (isset($this->_img['img'])) {
1207:             $session->set('horde', 'form/' . $this->getRandomId(), $this->_img['img']);
1208:         }
1209:     }
1210: 
1211:     function getUploadedFileType($field)
1212:     {
1213:         /* Get any index on the field name. */
1214:         $index = Horde_Array::getArrayParts($field, $base, $keys);
1215: 
1216:         if ($index) {
1217:             /* Index present, fetch the mime type var to check. */
1218:             $keys_path = array_merge(array($base, 'type'), $keys);
1219:             $type = Horde_Array::getElement($_FILES, $keys_path);
1220:             $keys_path = array_merge(array($base, 'tmp_name'), $keys);
1221:             $tmp_name = Horde_Array::getElement($_FILES, $keys_path);
1222:         } else {
1223:             /* No index, simple set up of vars to check. */
1224:             $type = $_FILES[$field]['type'];
1225:             $tmp_name = $_FILES[$field]['tmp_name'];
1226:         }
1227: 
1228:         if (empty($type) || ($type == 'application/octet-stream')) {
1229:             /* Type wasn't set on upload, try analising the upload. */
1230:             if (!($type = Horde_Mime_Magic::analyzeFile($tmp_name, isset($GLOBALS['conf']['mime']['magic_db']) ? $GLOBALS['conf']['mime']['magic_db'] : null))) {
1231:                 if ($index) {
1232:                     /* Get the name value. */
1233:                     $keys_path = array_merge(array($base, 'name'), $keys);
1234:                     $name = Horde_Array::getElement($_FILES, $keys_path);
1235: 
1236:                     /* Work out the type from the file name. */
1237:                     $type = Horde_Mime_Magic::filenameToMime($name);
1238: 
1239:                     /* Set the type. */
1240:                     $keys_path = array_merge(array($base, 'type'), $keys);
1241:                     Horde_Array::getElement($_FILES, $keys_path, $type);
1242:                 } else {
1243:                     /* Work out the type from the file name. */
1244:                     $type = Horde_Mime_Magic::filenameToMime($_FILES[$field]['name']);
1245: 
1246:                     /* Set the type. */
1247:                     $_FILES[$field]['type'] = Horde_Mime_Magic::filenameToMime($_FILES[$field]['name']);
1248:                 }
1249:             }
1250:         }
1251: 
1252:         return $type;
1253:     }
1254: 
1255:     /**
1256:      * Returns the current image information.
1257:      *
1258:      * @return array  The current image hash.
1259:      */
1260:     function getImage($vars, $var)
1261:     {
1262:         $this->_getUpload($vars, $var);
1263:         if (!isset($this->_img)) {
1264:             $image = $vars->get($var->getVarName());
1265:             if ($image) {
1266:                 $this->loadImageData($image);
1267:                 if (isset($image['img'])) {
1268:                     $this->_img = $image;
1269:                     $GLOBALS['session']->set('horde', 'form/' . $this->getRandomId(), $this->_img['img']);
1270:                 }
1271:             }
1272:         }
1273:         return $this->_img;
1274:     }
1275: 
1276:     /**
1277:      * Loads any existing image data into the image field. Requires that the
1278:      * array $image passed to it contains the structure:
1279:      *   $image['load']['file'] - the filename of the image;
1280:      *   $image['load']['data'] - the raw image data.
1281:      *
1282:      * @param array $image  The image array.
1283:      */
1284:     function loadImageData(&$image)
1285:     {
1286:         /* No existing image data to load. */
1287:         if (!isset($image['load'])) {
1288:             return;
1289:         }
1290: 
1291:         /* Save the data to the temp dir. */
1292:         $tmp_file = Horde::getTempDir() . '/' . $image['load']['file'];
1293:         if ($fd = fopen($tmp_file, 'w')) {
1294:             fwrite($fd, $image['load']['data']);
1295:             fclose($fd);
1296:         }
1297: 
1298:         $image['img'] = array('file' => $image['load']['file']);
1299:         unset($image['load']);
1300:     }
1301: 
1302:     function getRandomId()
1303:     {
1304:         if (!isset($this->_random)) {
1305:             $this->_random = uniqid(mt_rand());
1306:         }
1307:         return $this->_random;
1308:     }
1309: 
1310:     /**
1311:      * Return info about field type.
1312:      */
1313:     function about()
1314:     {
1315:         return array(
1316:             'name' => Horde_Form_Translation::t("Image upload"),
1317:             'params' => array(
1318:                 'show_upload'   => array('label' => Horde_Form_Translation::t("Show upload?"),
1319:                                          'type'  => 'boolean'),
1320:                 'show_keeporig' => array('label' => Horde_Form_Translation::t("Show option to keep original?"),
1321:                                          'type'  => 'boolean'),
1322:                 'max_filesize'  => array('label' => Horde_Form_Translation::t("Maximum file size in bytes"),
1323:                                          'type'  => 'int')));
1324:     }
1325: 
1326: }
1327: 
1328: class Horde_Form_Type_boolean extends Horde_Form_Type {
1329: 
1330:     function isValid(&$var, &$vars, $value, &$message)
1331:     {
1332:         return true;
1333:     }
1334: 
1335:     function getInfo(&$vars, &$var, &$info)
1336:     {
1337:         $info = is_bool($var->getValue($vars)) ? $var->getValue($vars) : Horde_String::lower($vars->get($var->getVarName())) == 'on';
1338:     }
1339: 
1340:     /**
1341:      * Return info about field type.
1342:      */
1343:     function about()
1344:     {
1345:         return array('name' => Horde_Form_Translation::t("True or false"));
1346:     }
1347: 
1348: }
1349: 
1350: class Horde_Form_Type_link extends Horde_Form_Type {
1351: 
1352:     /**
1353:      * List of hashes containing link parameters. Possible keys: 'url', 'text',
1354:      * 'target', 'onclick', 'title', 'accesskey'.
1355:      *
1356:      * @var array
1357:      */
1358:     var $values;
1359: 
1360:     function init($values)
1361:     {
1362:         $this->values = $values;
1363:     }
1364: 
1365:     function isValid(&$var, &$vars, $value, &$message)
1366:     {
1367:         return true;
1368:     }
1369: 
1370:     /**
1371:      * Return info about field type.
1372:      */
1373:     function about()
1374:     {
1375:         return array(
1376:             'name' => Horde_Form_Translation::t("Link"),
1377:             'params' => array(
1378:                 'url' => array(
1379:                     'label' => Horde_Form_Translation::t("Link URL"),
1380:                     'type' => 'text'),
1381:                 'text' => array(
1382:                     'label' => Horde_Form_Translation::t("Link text"),
1383:                     'type' => 'text'),
1384:                 'target' => array(
1385:                     'label' => Horde_Form_Translation::t("Link target"),
1386:                     'type' => 'text'),
1387:                 'onclick' => array(
1388:                     'label' => Horde_Form_Translation::t("Onclick event"),
1389:                     'type' => 'text'),
1390:                 'title' => array(
1391:                     'label' => Horde_Form_Translation::t("Link title attribute"),
1392:                     'type' => 'text'),
1393:                 'accesskey' => array(
1394:                     'label' => Horde_Form_Translation::t("Link access key"),
1395:                     'type' => 'text')));
1396:     }
1397: 
1398: }
1399: 
1400: class Horde_Form_Type_email extends Horde_Form_Type {
1401: 
1402:     /**
1403:      * Allow multiple addresses?
1404:      *
1405:      * @var boolean
1406:      */
1407:     var $_allow_multi = false;
1408: 
1409:     /**
1410:      * Protect address from spammers?
1411:      *
1412:      * @var boolean
1413:      */
1414:     var $_strip_domain = false;
1415: 
1416:     /**
1417:      * Link the email address to the compose page when displaying?
1418:      *
1419:      * @var boolean
1420:      */
1421:     var $_link_compose = false;
1422: 
1423:     /**
1424:      * Whether to check the domain's SMTP server whether the address exists.
1425:      *
1426:      * @var boolean
1427:      */
1428:     var $_check_smtp = false;
1429: 
1430:     /**
1431:      * The name to use when linking to the compose page
1432:      *
1433:      * @var boolean
1434:      */
1435:     var $_link_name;
1436: 
1437:     /**
1438:      * A string containing valid delimiters (default is just comma).
1439:      *
1440:      * @var string
1441:      */
1442:     var $_delimiters = ',';
1443: 
1444:     /**
1445:      * The size of the input field.
1446:      *
1447:      * @var integer
1448:      */
1449:     var $_size;
1450: 
1451:     /**
1452:      * @param boolean $allow_multi   Allow multiple addresses?
1453:      * @param boolean $strip_domain  Protect address from spammers?
1454:      * @param boolean $link_compose  Link the email address to the compose page
1455:      *                               when displaying?
1456:      * @param string $link_name      The name to use when linking to the
1457:      *                               compose page.
1458:      * @param string $delimiters     Character to split multiple addresses with.
1459:      * @param integer $size          The size of the input field.
1460:      */
1461:     function init($allow_multi = false, $strip_domain = false,
1462:                   $link_compose = false, $link_name = null,
1463:                   $delimiters = ',', $size = null)
1464:     {
1465:         $this->_allow_multi = $allow_multi;
1466:         $this->_strip_domain = $strip_domain;
1467:         $this->_link_compose = $link_compose;
1468:         $this->_link_name = $link_name;
1469:         $this->_delimiters = $delimiters;
1470:         $this->_size = $size;
1471:     }
1472: 
1473:     /**
1474:      */
1475:     function isValid(&$var, &$vars, $value, &$message)
1476:     {
1477:         // Split into individual addresses.
1478:         $emails = $this->splitEmailAddresses($value);
1479: 
1480:         // Check for too many.
1481:         if (!$this->_allow_multi && count($emails) > 1) {
1482:             $message = Horde_Form_Translation::t("Only one email address is allowed.");
1483:             return false;
1484:         }
1485: 
1486:         // Check for all valid and at least one non-empty.
1487:         $nonEmpty = 0;
1488:         foreach ($emails as $email) {
1489:             if (!strlen($email)) {
1490:                 continue;
1491:             }
1492:             if (!$this->validateEmailAddress($email)) {
1493:                 $message = sprintf(Horde_Form_Translation::t("\"%s\" is not a valid email address."), htmlspecialchars($email));
1494:                 return false;
1495:             }
1496:             ++$nonEmpty;
1497:         }
1498: 
1499:         if (!$nonEmpty && $var->isRequired()) {
1500:             if ($this->_allow_multi) {
1501:                 $message = Horde_Form_Translation::t("You must enter at least one email address.");
1502:             } else {
1503:                 $message = Horde_Form_Translation::t("You must enter an email address.");
1504:             }
1505:             return false;
1506:         }
1507: 
1508:         return true;
1509:     }
1510: 
1511:     /**
1512:      * Explodes an RFC 2822 string, ignoring a delimiter if preceded
1513:      * by a "\" character, or if the delimiter is inside single or
1514:      * double quotes.
1515:      *
1516:      * @param string $string     The RFC 822 string.
1517:      *
1518:      * @return array  The exploded string in an array.
1519:      */
1520:     function splitEmailAddresses($string)
1521:     {
1522:         $quotes = array('"', "'");
1523:         $emails = array();
1524:         $pos = 0;
1525:         $in_quote = null;
1526:         $in_group = false;
1527:         $prev = null;
1528: 
1529:         if (!strlen($string)) {
1530:             return array();
1531:         }
1532: 
1533:         $char = $string[0];
1534:         if (in_array($char, $quotes)) {
1535:             $in_quote = $char;
1536:         } elseif ($char == ':') {
1537:             $in_group = true;
1538:         } elseif (strpos($this->_delimiters, $char) !== false) {
1539:             $emails[] = '';
1540:             $pos = 1;
1541:         }
1542: 
1543:         for ($i = 1, $iMax = strlen($string); $i < $iMax; ++$i) {
1544:             $char = $string[$i];
1545:             if (in_array($char, $quotes)) {
1546:                 if ($prev !== '\\') {
1547:                     if ($in_quote === $char) {
1548:                         $in_quote = null;
1549:                     } elseif (is_null($in_quote)) {
1550:                         $in_quote = $char;
1551:                     }
1552:                 }
1553:             } elseif ($in_group) {
1554:                 if ($char == ';') {
1555:                     $emails[] = substr($string, $pos, $i - $pos + 1);
1556:                     $pos = $i + 1;
1557:                     $in_group = false;
1558:                 }
1559:             } elseif ($char == ':') {
1560:                 $in_group = true;
1561:             } elseif (strpos($this->_delimiters, $char) !== false &&
1562:                       $prev !== '\\' &&
1563:                       is_null($in_quote)) {
1564:                 $emails[] = substr($string, $pos, $i - $pos);
1565:                 $pos = $i + 1;
1566:             }
1567:             $prev = $char;
1568:         }
1569: 
1570:         if ($pos != $i) {
1571:             /* The string ended without a delimiter. */
1572:             $emails[] = substr($string, $pos, $i - $pos);
1573:         }
1574: 
1575:         return $emails;
1576:     }
1577: 
1578:     /**
1579:      * @param string $email An individual email address to validate.
1580:      *
1581:      * @return boolean
1582:      */
1583:     function validateEmailAddress($email)
1584:     {
1585:         $result = $this->_isRfc3696ValidEmailAddress($email);
1586:         if ($result && $this->_check_smtp) {
1587:             $result = $this->validateEmailAddressSmtp($email);
1588:         }
1589: 
1590:         return $result;
1591:     }
1592: 
1593:     /**
1594:      * Attempt partial delivery of mail to an address to validate it.
1595:      *
1596:      * @param string $email An individual email address to validate.
1597:      *
1598:      * @return boolean
1599:      */
1600:     function validateEmailAddressSmtp($email)
1601:     {
1602:         list(, $maildomain) = explode('@', $email, 2);
1603: 
1604:         // Try to get the real mailserver from MX records.
1605:         if (function_exists('getmxrr') &&
1606:             @getmxrr($maildomain, $mxhosts, $mxpriorities)) {
1607:             // MX record found.
1608:             array_multisort($mxpriorities, $mxhosts);
1609:             $mailhost = $mxhosts[0];
1610:         } else {
1611:             // No MX record found, try the root domain as the mail
1612:             // server.
1613:             $mailhost = $maildomain;
1614:         }
1615: 
1616:         $fp = @fsockopen($mailhost, 25, $errno, $errstr, 5);
1617:         if (!$fp) {
1618:             return false;
1619:         }
1620: 
1621:         // Read initial response.
1622:         fgets($fp, 4096);
1623: 
1624:         // HELO
1625:         fputs($fp, "HELO $mailhost\r\n");
1626:         fgets($fp, 4096);
1627: 
1628:         // MAIL FROM
1629:         fputs($fp, "MAIL FROM: <root@example.com>\r\n");
1630:         fgets($fp, 4096);
1631: 
1632:         // RCPT TO - gets the result we want.
1633:         fputs($fp, "RCPT TO: <$email>\r\n");
1634:         $result = trim(fgets($fp, 4096));
1635: 
1636:         // QUIT
1637:         fputs($fp, "QUIT\r\n");
1638:         fgets($fp, 4096);
1639:         fclose($fp);
1640: 
1641:         return substr($result, 0, 1) == '2';
1642:     }
1643: 
1644:     function getSize()
1645:     {
1646:         return $this->_size;
1647:     }
1648: 
1649:     /**
1650:      * Return info about field type.
1651:      */
1652:     function about()
1653:     {
1654:         return array(
1655:             'name' => Horde_Form_Translation::t("Email"),
1656:             'params' => array(
1657:                 'allow_multi' => array(
1658:                     'label' => Horde_Form_Translation::t("Allow multiple addresses?"),
1659:                     'type'  => 'boolean'),
1660:                 'strip_domain' => array(
1661:                     'label' => Horde_Form_Translation::t("Protect address from spammers?"),
1662:                     'type' => 'boolean'),
1663:                 'link_compose' => array(
1664:                     'label' => Horde_Form_Translation::t("Link the email address to the compose page when displaying?"),
1665:                     'type' => 'boolean'),
1666:                 'link_name' => array(
1667:                     'label' => Horde_Form_Translation::t("The name to use when linking to the compose page"),
1668:                     'type' => 'text'),
1669:                 'delimiters' => array(
1670:                     'label' => Horde_Form_Translation::t("Character to split multiple addresses with"),
1671:                     'type' => 'text'),
1672:                 'size' => array(
1673:                     'label' => Horde_Form_Translation::t("Size"),
1674:                     'type'  => 'int'),
1675:             ),
1676:         );
1677:     }
1678: 
1679:     /**
1680:      * RFC3696 Email Parser
1681:      *
1682:      * By Cal Henderson <cal@iamcal.com>
1683:      *
1684:      * This code is dual licensed:
1685:      * CC Attribution-ShareAlike 2.5 - http://creativecommons.org/licenses/by-sa/2.5/
1686:      * GPLv3 - http://www.gnu.org/copyleft/gpl.html
1687:      */
1688:     protected function _isRfc3696ValidEmailAddress($email)
1689:     {
1690:         ####################################################################################
1691:         #
1692:         # NO-WS-CTL       =       %d1-8 /         ; US-ASCII control characters
1693:         #                         %d11 /          ;  that do not include the
1694:         #                         %d12 /          ;  carriage return, line feed,
1695:         #                         %d14-31 /       ;  and white space characters
1696:         #                         %d127
1697:         # ALPHA          =  %x41-5A / %x61-7A   ; A-Z / a-z
1698:         # DIGIT          =  %x30-39
1699: 
1700:         $no_ws_ctl  = "[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]";
1701:         $alpha      = "[\\x41-\\x5a\\x61-\\x7a]";
1702:         $digit      = "[\\x30-\\x39]";
1703:         $cr     = "\\x0d";
1704:         $lf     = "\\x0a";
1705:         $crlf       = "(?:$cr$lf)";
1706: 
1707: 
1708:         ####################################################################################
1709:         #
1710:         # obs-char        =       %d0-9 / %d11 /          ; %d0-127 except CR and
1711:         #                         %d12 / %d14-127         ;  LF
1712:         # obs-text        =       *LF *CR *(obs-char *LF *CR)
1713:         # text            =       %d1-9 /         ; Characters excluding CR and LF
1714:         #                         %d11 /
1715:         #                         %d12 /
1716:         #                         %d14-127 /
1717:         #                         obs-text
1718:         # obs-qp          =       "\" (%d0-127)
1719:         # quoted-pair     =       ("\" text) / obs-qp
1720: 
1721:         $obs_char   = "[\\x00-\\x09\\x0b\\x0c\\x0e-\\x7f]";
1722:         $obs_text   = "(?:$lf*$cr*(?:$obs_char$lf*$cr*)*)";
1723:         $text       = "(?:[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f]|$obs_text)";
1724: 
1725:         #
1726:         # there's an issue with the definition of 'text', since 'obs_text' can
1727:         # be blank and that allows qp's with no character after the slash. we're
1728:         # treating that as bad, so this just checks we have at least one
1729:         # (non-CRLF) character
1730:         #
1731: 
1732:         $text       = "(?:$lf*$cr*$obs_char$lf*$cr*)";
1733:         $obs_qp     = "(?:\\x5c[\\x00-\\x7f])";
1734:         $quoted_pair    = "(?:\\x5c$text|$obs_qp)";
1735: 
1736: 
1737:         ####################################################################################
1738:         #
1739:         # obs-FWS         =       1*WSP *(CRLF 1*WSP)
1740:         # FWS             =       ([*WSP CRLF] 1*WSP) /   ; Folding white space
1741:         #                         obs-FWS
1742:         # ctext           =       NO-WS-CTL /     ; Non white space controls
1743:         #                         %d33-39 /       ; The rest of the US-ASCII
1744:         #                         %d42-91 /       ;  characters not including "(",
1745:         #                         %d93-126        ;  ")", or "\"
1746:         # ccontent        =       ctext / quoted-pair / comment
1747:         # comment         =       "(" *([FWS] ccontent) [FWS] ")"
1748:         # CFWS            =       *([FWS] comment) (([FWS] comment) / FWS)
1749: 
1750:         #
1751:         # note: we translate ccontent only partially to avoid an infinite loop
1752:         # instead, we'll recursively strip *nested* comments before processing
1753:         # the input. that will leave 'plain old comments' to be matched during
1754:         # the main parse.
1755:         #
1756: 
1757:         $wsp        = "[\\x20\\x09]";
1758:         $obs_fws    = "(?:$wsp+(?:$crlf$wsp+)*)";
1759:         $fws        = "(?:(?:(?:$wsp*$crlf)?$wsp+)|$obs_fws)";
1760:         $ctext      = "(?:$no_ws_ctl|[\\x21-\\x27\\x2A-\\x5b\\x5d-\\x7e])";
1761:         $ccontent   = "(?:$ctext|$quoted_pair)";
1762:         $comment    = "(?:\\x28(?:$fws?$ccontent)*$fws?\\x29)";
1763:         $cfws       = "(?:(?:$fws?$comment)*(?:$fws?$comment|$fws))";
1764: 
1765: 
1766:         #
1767:         # these are the rules for removing *nested* comments. we'll just detect
1768:         # outer comment and replace it with an empty comment, and recurse until
1769:         # we stop.
1770:         #
1771: 
1772:         $outer_ccontent_dull    = "(?:$fws?$ctext|$quoted_pair)";
1773:         $outer_ccontent_nest    = "(?:$fws?$comment)";
1774:         $outer_comment      = "(?:\\x28$outer_ccontent_dull*(?:$outer_ccontent_nest$outer_ccontent_dull*)+$fws?\\x29)";
1775: 
1776: 
1777:         ####################################################################################
1778:         #
1779:         # atext           =       ALPHA / DIGIT / ; Any character except controls,
1780:         #                         "!" / "#" /     ;  SP, and specials.
1781:         #                         "$" / "%" /     ;  Used for atoms
1782:         #                         "&" / "'" /
1783:         #                         "*" / "+" /
1784:         #                         "-" / "/" /
1785:         #                         "=" / "?" /
1786:         #                         "^" / "_" /
1787:         #                         "`" / "{" /
1788:         #                         "|" / "}" /
1789:         #                         "~"
1790:         # atom            =       [CFWS] 1*atext [CFWS]
1791: 
1792:         $atext      = "(?:$alpha|$digit|[\\x21\\x23-\\x27\\x2a\\x2b\\x2d\\x2f\\x3d\\x3f\\x5e\\x5f\\x60\\x7b-\\x7e])";
1793:         $atom       = "(?:$cfws?(?:$atext)+$cfws?)";
1794: 
1795: 
1796:         ####################################################################################
1797:         #
1798:         # qtext           =       NO-WS-CTL /     ; Non white space controls
1799:         #                         %d33 /          ; The rest of the US-ASCII
1800:         #                         %d35-91 /       ;  characters not including "\"
1801:         #                         %d93-126        ;  or the quote character
1802:         # qcontent        =       qtext / quoted-pair
1803:         # quoted-string   =       [CFWS]
1804:         #                         DQUOTE *([FWS] qcontent) [FWS] DQUOTE
1805:         #                         [CFWS]
1806:         # word            =       atom / quoted-string
1807: 
1808:         $qtext      = "(?:$no_ws_ctl|[\\x21\\x23-\\x5b\\x5d-\\x7e])";
1809:         $qcontent   = "(?:$qtext|$quoted_pair)";
1810:         $quoted_string  = "(?:$cfws?\\x22(?:$fws?$qcontent)*$fws?\\x22$cfws?)";
1811: 
1812:         #
1813:         # changed the '*' to a '+' to require that quoted strings are not empty
1814:         #
1815: 
1816:         $quoted_string  = "(?:$cfws?\\x22(?:$fws?$qcontent)+$fws?\\x22$cfws?)";
1817:         $word       = "(?:$atom|$quoted_string)";
1818: 
1819: 
1820:         ####################################################################################
1821:         #
1822:         # obs-local-part  =       word *("." word)
1823:         # obs-domain      =       atom *("." atom)
1824: 
1825:         $obs_local_part = "(?:$word(?:\\x2e$word)*)";
1826:         $obs_domain = "(?:$atom(?:\\x2e$atom)*)";
1827: 
1828: 
1829:         ####################################################################################
1830:         #
1831:         # dot-atom-text   =       1*atext *("." 1*atext)
1832:         # dot-atom        =       [CFWS] dot-atom-text [CFWS]
1833: 
1834:         $dot_atom_text  = "(?:$atext+(?:\\x2e$atext+)*)";
1835:         $dot_atom   = "(?:$cfws?$dot_atom_text$cfws?)";
1836: 
1837: 
1838:         ####################################################################################
1839:         #
1840:         # domain-literal  =       [CFWS] "[" *([FWS] dcontent) [FWS] "]" [CFWS]
1841:         # dcontent        =       dtext / quoted-pair
1842:         # dtext           =       NO-WS-CTL /     ; Non white space controls
1843:         #
1844:         #                         %d33-90 /       ; The rest of the US-ASCII
1845:         #                         %d94-126        ;  characters not including "[",
1846:         #                                         ;  "]", or "\"
1847: 
1848:         $dtext      = "(?:$no_ws_ctl|[\\x21-\\x5a\\x5e-\\x7e])";
1849:         $dcontent   = "(?:$dtext|$quoted_pair)";
1850:         $domain_literal = "(?:$cfws?\\x5b(?:$fws?$dcontent)*$fws?\\x5d$cfws?)";
1851: 
1852: 
1853:         ####################################################################################
1854:         #
1855:         # local-part      =       dot-atom / quoted-string / obs-local-part
1856:         # domain          =       dot-atom / domain-literal / obs-domain
1857:         # addr-spec       =       local-part "@" domain
1858: 
1859:         $local_part = "(($dot_atom)|($quoted_string)|($obs_local_part))";
1860:         $domain     = "(($dot_atom)|($domain_literal)|($obs_domain))";
1861:         $addr_spec  = "$local_part\\x40$domain";
1862: 
1863: 
1864: 
1865:         #
1866:         # see http://www.dominicsayers.com/isemail/ for details, but this should probably be 254
1867:         #
1868: 
1869:         if (strlen($email) > 256) return 0;
1870: 
1871: 
1872:         #
1873:         # we need to strip nested comments first - we replace them with a simple comment
1874:         #
1875: 
1876:         $email = $this->_rfc3696StripComments($outer_comment, $email, "(x)");
1877: 
1878: 
1879:         #
1880:         # now match what's left
1881:         #
1882: 
1883:         if (!preg_match("!^$addr_spec$!", $email, $m)){
1884: 
1885:             return 0;
1886:         }
1887: 
1888:         $bits = array(
1889:             'local'         => isset($m[1]) ? $m[1] : '',
1890:             'local-atom'        => isset($m[2]) ? $m[2] : '',
1891:             'local-quoted'      => isset($m[3]) ? $m[3] : '',
1892:             'local-obs'     => isset($m[4]) ? $m[4] : '',
1893:             'domain'        => isset($m[5]) ? $m[5] : '',
1894:             'domain-atom'       => isset($m[6]) ? $m[6] : '',
1895:             'domain-literal'    => isset($m[7]) ? $m[7] : '',
1896:             'domain-obs'        => isset($m[8]) ? $m[8] : '',
1897:         );
1898: 
1899: 
1900:         #
1901:         # we need to now strip comments from $bits[local] and $bits[domain],
1902:         # since we know they're i the right place and we want them out of the
1903:         # way for checking IPs, label sizes, etc
1904:         #
1905: 
1906:         $bits['local']  = $this->_rfc3696StripComments($comment, $bits['local']);
1907:         $bits['domain'] = $this->_rfc3696StripComments($comment, $bits['domain']);
1908: 
1909: 
1910:         #
1911:         # length limits on segments
1912:         #
1913: 
1914:         if (strlen($bits['local']) > 64) return 0;
1915:         if (strlen($bits['domain']) > 255) return 0;
1916: 
1917: 
1918:         #
1919:         # restrictuions on domain-literals from RFC2821 section 4.1.3
1920:         #
1921: 
1922:         if (strlen($bits['domain-literal'])){
1923: 
1924:             $Snum           = "(\d{1,3})";
1925:             $IPv4_address_literal   = "$Snum\.$Snum\.$Snum\.$Snum";
1926: 
1927:             $IPv6_hex       = "(?:[0-9a-fA-F]{1,4})";
1928: 
1929:             $IPv6_full      = "IPv6\:$IPv6_hex(:?\:$IPv6_hex){7}";
1930: 
1931:             $IPv6_comp_part     = "(?:$IPv6_hex(?:\:$IPv6_hex){0,5})?";
1932:             $IPv6_comp      = "IPv6\:($IPv6_comp_part\:\:$IPv6_comp_part)";
1933: 
1934:             $IPv6v4_full        = "IPv6\:$IPv6_hex(?:\:$IPv6_hex){5}\:$IPv4_address_literal";
1935: 
1936:             $IPv6v4_comp_part   = "$IPv6_hex(?:\:$IPv6_hex){0,3}";
1937:             $IPv6v4_comp        = "IPv6\:((?:$IPv6v4_comp_part)?\:\:(?:$IPv6v4_comp_part\:)?)$IPv4_address_literal";
1938: 
1939: 
1940:             #
1941:             # IPv4 is simple
1942:             #
1943: 
1944:             if (preg_match("!^\[$IPv4_address_literal\]$!", $bits['domain'], $m)) {
1945:                 if (intval($m[1]) > 255) return 0;
1946:                 if (intval($m[2]) > 255) return 0;
1947:                 if (intval($m[3]) > 255) return 0;
1948:                 if (intval($m[4]) > 255) return 0;
1949:             } else {
1950:                 #
1951:                 # this should be IPv6 - a bunch of tests are needed here :)
1952:                 #
1953: 
1954:                 while (1) {
1955: 
1956:                     if (preg_match("!^\[$IPv6_full\]$!", $bits['domain'])){
1957:                         break;
1958:                     }
1959: 
1960:                     if (preg_match("!^\[$IPv6_comp\]$!", $bits['domain'], $m)){
1961:                         list($a, $b) = explode('::', $m[1]);
1962:                         $folded = (strlen($a) && strlen($b)) ? "$a:$b" : "$a$b";
1963:                         $groups = explode(':', $folded);
1964:                         if (count($groups) > 6) return 0;
1965:                         break;
1966:                     }
1967: 
1968:                     if (preg_match("!^\[$IPv6v4_full\]$!", $bits['domain'], $m)) {
1969:                         if (intval($m[1]) > 255) return 0;
1970:                         if (intval($m[2]) > 255) return 0;
1971:                         if (intval($m[3]) > 255) return 0;
1972:                         if (intval($m[4]) > 255) return 0;
1973:                         break;
1974:                     }
1975: 
1976:                     if (preg_match("!^\[$IPv6v4_comp\]$!", $bits['domain'], $m)) {
1977:                         list($a, $b) = explode('::', $m[1]);
1978:                         $b = substr($b, 0, -1); # remove the trailing colon before the IPv4 address
1979:                         $folded = (strlen($a) && strlen($b)) ? "$a:$b" : "$a$b";
1980:                         $groups = explode(':', $folded);
1981:                         if (count($groups) > 4) return 0;
1982:                         break;
1983:                     }
1984: 
1985:                     return 0;
1986:                 }
1987:             }
1988:         } else {
1989:             #
1990:             # the domain is either dot-atom or obs-domain - either way, it's
1991:             # made up of simple labels and we split on dots
1992:             #
1993: 
1994:             $labels = explode('.', $bits['domain']);
1995: 
1996: 
1997:             #
1998:             # this is allowed by both dot-atom and obs-domain, but is un-routeable on the
1999:             # public internet, so we'll fail it (e.g. user@localhost)
2000:             #
2001: 
2002:             if (count($labels) == 1) return 0;
2003: 
2004: 
2005:             #
2006:             # checks on each label
2007:             #
2008: 
2009:             foreach ($labels as $label) {
2010:                 if (strlen($label) > 63) return 0;
2011:                 if (substr($label, 0, 1) == '-') return 0;
2012:                 if (substr($label, -1) == '-') return 0;
2013:             }
2014: 
2015: 
2016:             #
2017:             # last label can't be all numeric
2018:             #
2019: 
2020:             if (preg_match('!^[0-9]+$!', array_pop($labels))) return 0;
2021:         }
2022: 
2023:         return 1;
2024:     }
2025: 
2026:     /**
2027:      * RFC3696 Email Parser
2028:      *
2029:      * By Cal Henderson <cal@iamcal.com>
2030:      *
2031:      * This code is dual licensed:
2032:      * CC Attribution-ShareAlike 2.5 - http://creativecommons.org/licenses/by-sa/2.5/
2033:      * GPLv3 - http://www.gnu.org/copyleft/gpl.html
2034:      *
2035:      * $Revision: 5039 $
2036:      */
2037:     protected function _rfc3696StripComments($comment, $email, $replace = '')
2038:     {
2039:         while (1) {
2040:             $new = preg_replace("!$comment!", $replace, $email);
2041:             if (strlen($new) == strlen($email)) {
2042:                 return $email;
2043:             }
2044:             $email = $new;
2045:         }
2046:     }
2047: }
2048: 
2049: class Horde_Form_Type_matrix extends Horde_Form_Type {
2050: 
2051:     var $_cols;
2052:     var $_rows;
2053:     var $_matrix;
2054:     var $_new_input;
2055: 
2056:     /**
2057:      * Initializes the variable.
2058:      *
2059:      * Example:
2060:      * <code>
2061:      * init(array('Column A', 'Column B'),
2062:      *      array(1 => 'Row One', 2 => 'Row 2', 3 => 'Row 3'),
2063:      *      array(array(true, true, false),
2064:      *            array(true, false, true),
2065:      *            array(fasle, true, false)),
2066:      *      array('Row 4', 'Row 5'));
2067:      * </code>
2068:      *
2069:      * @param array $cols               A list of column headers.
2070:      * @param array $rows               A hash with row IDs as the keys and row
2071:      *                                  labels as the values.
2072:      * @param array $matrix             A two dimensional hash with the field
2073:      *                                  values.
2074:      * @param boolean|array $new_input  If true, a free text field to add a new
2075:      *                                  row is displayed on the top, a select
2076:      *                                  box if this parameter is a value.
2077:      */
2078:     function init($cols, $rows = array(), $matrix = array(), $new_input = false)
2079:     {
2080:         $this->_cols       = $cols;
2081:         $this->_rows       = $rows;
2082:         $this->_matrix     = $matrix;
2083:         $this->_new_input  = $new_input;
2084:     }
2085: 
2086:     function isValid(&$var, &$vars, $value, &$message)
2087:     {
2088:         return true;
2089:     }
2090: 
2091:     function getCols()     { return $this->_cols; }
2092:     function getRows()     { return $this->_rows; }
2093:     function getMatrix()   { return $this->_matrix; }
2094:     function getNewInput() { return $this->_new_input; }
2095: 
2096:     function getInfo(&$vars, &$var, &$info)
2097:     {
2098:         $values = $vars->get($var->getVarName());
2099:         if (!empty($values['n']['r']) && isset($values['n']['v'])) {
2100:             $new_row = $values['n']['r'];
2101:             $values['r'][$new_row] = $values['n']['v'];
2102:             unset($values['n']);
2103:         }
2104: 
2105:         $info = (isset($values['r']) ? $values['r'] : array());
2106:     }
2107: 
2108:     function about()
2109:     {
2110:         return array(
2111:             'name' => Horde_Form_Translation::t("Field matrix"),
2112:             'params' => array(
2113:                 'cols' => array('label' => Horde_Form_Translation::t("Column titles"),
2114:                                 'type'  => 'stringarray')));
2115:     }
2116: 
2117: }
2118: 
2119: class Horde_Form_Type_emailConfirm extends Horde_Form_Type {
2120: 
2121:     function isValid(&$var, &$vars, $value, &$message)
2122:     {
2123:         if ($var->isRequired() && empty($value['original'])) {
2124:             $message = Horde_Form_Translation::t("This field is required.");
2125:             return false;
2126:         }
2127: 
2128:         if ($value['original'] != $value['confirm']) {
2129:             $message = Horde_Form_Translation::t("Email addresses must match.");
2130:             return false;
2131:         } else {
2132:             try {
2133:                 $parsed_email = Horde_Mime_Address::parseAddressList($value['original'], array('validate' => true));
2134:             } catch (Horde_Mime_Exception $e) {
2135:                 $message = $e->getMessage();
2136:                 return false;
2137:             }
2138:             if (count($parsed_email) > 1) {
2139:                 $message = Horde_Form_Translation::t("Only one email address allowed.");
2140:                 return false;
2141:             }
2142:             if (empty($parsed_email[0]->mailbox)) {
2143:                 $message = Horde_Form_Translation::t("You did not enter a valid email address.");
2144:                 return false;
2145:             }
2146:         }
2147: 
2148:         return true;
2149:     }
2150: 
2151:     /**
2152:      * Return info about field type.
2153:      */
2154:     function about()
2155:     {
2156:         return array('name' => Horde_Form_Translation::t("Email with confirmation"));
2157:     }
2158: 
2159: }
2160: 
2161: class Horde_Form_Type_password extends Horde_Form_Type {
2162: 
2163:     function isValid(&$var, &$vars, $value, &$message)
2164:     {
2165:         $valid = true;
2166: 
2167:         if ($var->isRequired()) {
2168:             $valid = strlen(trim($value)) > 0;
2169: 
2170:             if (!$valid) {
2171:                 $message = Horde_Form_Translation::t("This field is required.");
2172:             }
2173:         }
2174: 
2175:         return $valid;
2176:     }
2177: 
2178:     /**
2179:      * Return info about field type.
2180:      */
2181:     function about()
2182:     {
2183:         return array('name' => Horde_Form_Translation::t("Password"));
2184:     }
2185: 
2186: }
2187: 
2188: class Horde_Form_Type_passwordconfirm extends Horde_Form_Type {
2189: 
2190:     function isValid(&$var, &$vars, $value, &$message)
2191:     {
2192:         if ($var->isRequired() && empty($value['original'])) {
2193:             $message = Horde_Form_Translation::t("This field is required.");
2194:             return false;
2195:         }
2196: 
2197:         if ($value['original'] != $value['confirm']) {
2198:             $message = Horde_Form_Translation::t("Passwords must match.");
2199:             return false;
2200:         }
2201: 
2202:         return true;
2203:     }
2204: 
2205:     function getInfo(&$vars, &$var, &$info)
2206:     {
2207:         $value = $vars->get($var->getVarName());
2208:         $info = $value['original'];
2209:     }
2210: 
2211:     /**
2212:      * Return info about field type.
2213:      */
2214:     function about()
2215:     {
2216:         return array('name' => Horde_Form_Translation::t("Password with confirmation"));
2217:     }
2218: 
2219: }
2220: 
2221: class Horde_Form_Type_enum extends Horde_Form_Type {
2222: 
2223:     var $_values;
2224:     var $_prompt;
2225: 
2226:     function init($values, $prompt = null)
2227:     {
2228:         $this->setValues($values);
2229: 
2230:         if ($prompt === true) {
2231:             $this->_prompt = Horde_Form_Translation::t("-- select --");
2232:         } else {
2233:             $this->_prompt = $prompt;
2234:         }
2235:     }
2236: 
2237:     function isValid(&$var, &$vars, $value, &$message)
2238:     {
2239:         if ($var->isRequired() && $value == '' && !isset($this->_values[$value])) {
2240:             $message = Horde_Form_Translation::t("This field is required.");
2241:             return false;
2242:         }
2243: 
2244:         if (count($this->_values) == 0 || isset($this->_values[$value]) ||
2245:             ($this->_prompt && empty($value))) {
2246:             return true;
2247:         }
2248: 
2249:         $message = Horde_Form_Translation::t("Invalid data submitted.");
2250:         return false;
2251:     }
2252: 
2253:     function getValues()
2254:     {
2255:         return $this->_values;
2256:     }
2257: 
2258:     function setValues($values)
2259:     {
2260:         $this->_values = $values;
2261:     }
2262: 
2263:     function getPrompt()
2264:     {
2265:         return $this->_prompt;
2266:     }
2267: 
2268:     /**
2269:      * Return info about field type.
2270:      */
2271:     function about()
2272:     {
2273:         return array(
2274:             'name' => Horde_Form_Translation::t("Drop down list"),
2275:             'params' => array(
2276:                 'values' => array('label' => Horde_Form_Translation::t("Values to select from"),
2277:                                   'type'  => 'stringarray'),
2278:                 'prompt' => array('label' => Horde_Form_Translation::t("Prompt text"),
2279:                                   'type'  => 'text')));
2280:     }
2281: 
2282: }
2283: 
2284: class Horde_Form_Type_mlenum extends Horde_Form_Type {
2285: 
2286:     var $_values;
2287:     var $_prompts;
2288: 
2289:     function init(&$values, $prompts = null)
2290:     {
2291:         $this->_values = &$values;
2292: 
2293:         if ($prompts === true) {
2294:             $this->_prompts = array(Horde_Form_Translation::t("-- select --"), Horde_Form_Translation::t("-- select --"));
2295:         } elseif (!is_array($prompts)) {
2296:             $this->_prompts = array($prompts, $prompts);
2297:         } else {
2298:             $this->_prompts = $prompts;
2299:         }
2300:     }
2301: 
2302:     function onSubmit(&$var, &$vars)
2303:     {
2304:         $varname = $var->getVarName();
2305:         $value = $vars->get($varname);
2306: 
2307:         if ($value['1'] != $value['old']) {
2308:             $var->form->setSubmitted(false);
2309:         }
2310:     }
2311: 
2312:     function isValid(&$var, &$vars, $value, &$message)
2313:     {
2314:         if ($var->isRequired() && (empty($value['1']) || empty($value['2']))) {
2315:             $message = Horde_Form_Translation::t("This field is required.");
2316:             return false;
2317:         }
2318: 
2319:         if (!count($this->_values) || isset($this->_values[$value['1']]) ||
2320:             (!empty($this->_prompts) && empty($value['1']))) {
2321:             return true;
2322:         }
2323: 
2324:         $message = Horde_Form_Translation::t("Invalid data submitted.");
2325:         return false;
2326:     }
2327: 
2328:     function getValues()
2329:     {
2330:         return $this->_values;
2331:     }
2332: 
2333:     function getPrompts()
2334:     {
2335:         return $this->_prompts;
2336:     }
2337: 
2338:     function getInfo(&$vars, &$var, &$info)
2339:     {
2340:         $info = $vars->get($var->getVarName());
2341:         return $info['2'];
2342:     }
2343: 
2344:     /**
2345:      * Return info about field type.
2346:      */
2347:     function about()
2348:     {
2349:         return array(
2350:             'name' => Horde_Form_Translation::t("Multi-level drop down lists"),
2351:             'params' => array(
2352:                 'values' => array('label' => Horde_Form_Translation::t("Values to select from"),
2353:                                   'type'  => 'stringarray'),
2354:                 'prompt' => array('label' => Horde_Form_Translation::t("Prompt text"),
2355:                                   'type'  => 'text')));
2356:     }
2357: 
2358: }
2359: 
2360: class Horde_Form_Type_multienum extends Horde_Form_Type_enum {
2361: 
2362:     var $size = 5;
2363: 
2364:     function init($values, $size = null)
2365:     {
2366:         if (!is_null($size)) {
2367:             $this->size = (int)$size;
2368:         }
2369: 
2370:         parent::init($values);
2371:     }
2372: 
2373:     function isValid(&$var, &$vars, $value, &$message)
2374:     {
2375:         if (is_array($value)) {
2376:             foreach ($value as $val) {
2377:                 if (!$this->isValid($var, $vars, $val, $message)) {
2378:                     return false;
2379:                 }
2380:             }
2381:             return true;
2382:         }
2383: 
2384:         if (empty($value) && ((string)(int)$value !== $value)) {
2385:             if ($var->isRequired()) {
2386:                 $message = Horde_Form_Translation::t("This field is required.");
2387:                 return false;
2388:             } else {
2389:                 return true;
2390:             }
2391:         }
2392: 
2393:         if (count($this->_values) == 0 || isset($this->_values[$value])) {
2394:             return true;
2395:         }
2396: 
2397:         $message = Horde_Form_Translation::t("Invalid data submitted.");
2398:         return false;
2399:     }
2400: 
2401:     /**
2402:      * Return info about field type.
2403:      */
2404:     function about()
2405:     {
2406:         return array(
2407:             'name' => Horde_Form_Translation::t("Multiple selection"),
2408:             'params' => array(
2409:                 'values' => array('label' => Horde_Form_Translation::t("Values"),
2410:                                   'type'  => 'stringarray'),
2411:                 'size'   => array('label' => Horde_Form_Translation::t("Size"),
2412:                                   'type'  => 'int'))
2413:         );
2414:     }
2415: 
2416: }
2417: 
2418: class Horde_Form_Type_keyval_multienum extends Horde_Form_Type_multienum {
2419: 
2420:     function getInfo(&$vars, &$var, &$info)
2421:     {
2422:         $value = $vars->get($var->getVarName());
2423:         $info = array();
2424:         foreach ($value as $key) {
2425:             $info[$key] = $this->_values[$key];
2426:         }
2427:     }
2428: 
2429:     /**
2430:      * Return info about field type.
2431:      */
2432:     function about()
2433:     {
2434:         $about = parent::about();
2435:         $about['name'] = Horde_Form_Translation::t("Multiple selection, preserving keys");
2436:     }
2437: 
2438: }
2439: 
2440: class Horde_Form_Type_radio extends Horde_Form_Type_enum {
2441: 
2442:     /* Entirely implemented by Horde_Form_Type_enum; just a different
2443:      * view. */
2444: 
2445:     /**
2446:      * Return info about field type.
2447:      */
2448:     function about()
2449:     {
2450:         return array(
2451:             'name' => Horde_Form_Translation::t("Radio selection"),
2452:             'params' => array(
2453:                 'values' => array('label' => Horde_Form_Translation::t("Values"),
2454:                                   'type'  => 'stringarray')));
2455:     }
2456: 
2457: }
2458: 
2459: class Horde_Form_Type_set extends Horde_Form_Type {
2460: 
2461:     var $_values;
2462:     var $_checkAll = false;
2463: 
2464:     function init($values, $checkAll = false)
2465:     {
2466:         $this->_values = $values;
2467:         $this->_checkAll = $checkAll;
2468:     }
2469: 
2470:     function isValid(&$var, &$vars, $value, &$message)
2471:     {
2472:         if (count($this->_values) == 0 || count($value) == 0) {
2473:             return true;
2474:         }
2475:         foreach ($value as $item) {
2476:             if (!isset($this->_values[$item])) {
2477:                 $error = true;
2478:                 break;
2479:             }
2480:         }
2481:         if (!isset($error)) {
2482:             return true;
2483:         }
2484: 
2485:         $message = Horde_Form_Translation::t("Invalid data submitted.");
2486:         return false;
2487:     }
2488: 
2489:     function getValues()
2490:     {
2491:         return $this->_values;
2492:     }
2493: 
2494:     /**
2495:      * Return info about field type.
2496:      */
2497:     function about()
2498:     {
2499:         return array(
2500:             'name' => Horde_Form_Translation::t("Set"),
2501:             'params' => array(
2502:                 'values' => array('label' => Horde_Form_Translation::t("Values"),
2503:                                   'type'  => 'stringarray')));
2504:     }
2505: 
2506: }
2507: 
2508: class Horde_Form_Type_date extends Horde_Form_Type {
2509: 
2510:     var $_format;
2511: 
2512:     function init($format = '%a %d %B')
2513:     {
2514:         $this->_format = $format;
2515:     }
2516: 
2517:     function isValid(&$var, &$vars, $value, &$message)
2518:     {
2519:         $valid = true;
2520: 
2521:         if ($var->isRequired()) {
2522:             $valid = strlen(trim($value)) > 0;
2523: 
2524:             if (!$valid) {
2525:                 $message = sprintf(Horde_Form_Translation::t("%s is required"), $var->getHumanName());
2526:             }
2527:         }
2528: 
2529:         return $valid;
2530:     }
2531: 
2532:     /**
2533:      * @static
2534:      *
2535:      * @param mixed $date  The date to calculate the difference from. Can be
2536:      *                     either a timestamp integer value, or an array
2537:      *                     with date parts: 'day', 'month', 'year'.
2538:      *
2539:      * @return string
2540:      */
2541:     function getAgo($date)
2542:     {
2543:         if ($date === null) {
2544:             return '';
2545:         }
2546: 
2547:         try {
2548:             $today = new Horde_Date(time());
2549:             $date = new Horde_Date($date);
2550:             $ago = $date->toDays() - $today->toDays();
2551:         } catch (Horde_Date_Exception $e) {
2552:             return '';
2553:         }
2554: 
2555:         if ($ago < -1) {
2556:             return sprintf(Horde_Form_Translation::t(" (%s days ago)"), abs($ago));
2557:         } elseif ($ago == -1) {
2558:             return Horde_Form_Translation::t(" (yesterday)");
2559:         } elseif ($ago == 0) {
2560:             return Horde_Form_Translation::t(" (today)");
2561:         } elseif ($ago == 1) {
2562:             return Horde_Form_Translation::t(" (tomorrow)");
2563:         } else {
2564:             return sprintf(Horde_Form_Translation::t(" (in %s days)"), $ago);
2565:         }
2566:     }
2567: 
2568:     function getFormattedTime($timestamp, $format = null, $showago = true)
2569:     {
2570:         if (empty($format)) {
2571:             $format = $this->_format;
2572:         }
2573:         if (!empty($timestamp)) {
2574:             return strftime($format, $timestamp) . ($showago ? Horde_Form_Type_date::getAgo($timestamp) : '');
2575:         } else {
2576:             return '';
2577:         }
2578:     }
2579: 
2580:     /**
2581:      * Return info about field type.
2582:      */
2583:     function about()
2584:     {
2585:         return array('name' => Horde_Form_Translation::t("Date"));
2586:     }
2587: 
2588: }
2589: 
2590: class Horde_Form_Type_time extends Horde_Form_Type {
2591: 
2592:     function isValid(&$var, &$vars, $value, &$message)
2593:     {
2594:         if ($var->isRequired() && empty($value) && ((string)(double)$value !== $value)) {
2595:             $message = Horde_Form_Translation::t("This field is required.");
2596:             return false;
2597:         }
2598: 
2599:         if (empty($value) || preg_match('/^[0-2]?[0-9]:[0-5][0-9]$/', $value)) {
2600:             return true;
2601:         }
2602: 
2603:         $message = Horde_Form_Translation::t("This field may only contain numbers and the colon.");
2604:         return false;
2605:     }
2606: 
2607:     /**
2608:      * Return info about field type.
2609:      */
2610:     function about()
2611:     {
2612:         return array('name' => Horde_Form_Translation::t("Time"));
2613:     }
2614: 
2615: }
2616: 
2617: class Horde_Form_Type_hourminutesecond extends Horde_Form_Type {
2618: 
2619:     var $_show_seconds;
2620: 
2621:     function init($show_seconds = false)
2622:     {
2623:         $this->_show_seconds = $show_seconds;
2624:     }
2625: 
2626:     function isValid(&$var, &$vars, $value, &$message)
2627:     {
2628:         $time = $vars->get($var->getVarName());
2629:         if (!$this->_show_seconds && count($time) && !isset($time['second'])) {
2630:             $time['second'] = 0;
2631:         }
2632: 
2633:         if (!$this->emptyTimeArray($time) && !$this->checktime($time['hour'], $time['minute'], $time['second'])) {
2634:             $message = Horde_Form_Translation::t("Please enter a valid time.");
2635:             return false;
2636:         } elseif ($this->emptyTimeArray($time) && $var->isRequired()) {
2637:             $message = Horde_Form_Translation::t("This field is required.");
2638:             return false;
2639:         }
2640: 
2641:         return true;
2642:     }
2643: 
2644:     function checktime($hour, $minute, $second)
2645:     {
2646:         if (!isset($hour) || $hour == '' || ($hour < 0 || $hour > 23)) {
2647:             return false;
2648:         }
2649:         if (!isset($minute) || $minute == '' || ($minute < 0 || $minute > 60)) {
2650:             return false;
2651:         }
2652:         if (!isset($second) || $second === '' || ($second < 0 || $second > 60)) {
2653:             return false;
2654:         }
2655: 
2656:         return true;
2657:     }
2658: 
2659:     /**
2660:      * Return the time supplied as a Horde_Date object.
2661:      *
2662:      * @param string $time_in  Date in one of the three formats supported by
2663:      *                         Horde_Form and Horde_Date (ISO format
2664:      *                         YYYY-MM-DD HH:MM:SS, timestamp YYYYMMDDHHMMSS and
2665:      *                         UNIX epoch).
2666:      *
2667:      * @return Horde_Date  The time object.
2668:      */
2669:     function getTimeOb($time_in)
2670:     {
2671:         if (is_array($time_in)) {
2672:             if (!$this->emptyTimeArray($time_in)) {
2673:                 $time_in = sprintf('1970-01-01 %02d:%02d:%02d', $time_in['hour'], $time_in['minute'], $this->_show_seconds ? $time_in['second'] : 0);
2674:             }
2675:         }
2676: 
2677:         return new Horde_Date($time_in);
2678:     }
2679: 
2680:     /**
2681:      * Return the time supplied split up into an array.
2682:      *
2683:      * @param string $time_in  Time in one of the three formats supported by
2684:      *                         Horde_Form and Horde_Date (ISO format
2685:      *                         YYYY-MM-DD HH:MM:SS, timestamp YYYYMMDDHHMMSS and
2686:      *                         UNIX epoch).
2687:      *
2688:      * @return array  Array with three elements - hour, minute and seconds.
2689:      */
2690:     function getTimeParts($time_in)
2691:     {
2692:         if (is_array($time_in)) {
2693:             /* This is probably a failed isValid input so just return the
2694:              * parts as they are. */
2695:             return $time_in;
2696:         } elseif (empty($time_in)) {
2697:             /* This is just an empty field so return empty parts. */
2698:             return array('hour' => '', 'minute' => '', 'second' => '');
2699:         }
2700:         $time = $this->getTimeOb($time_in);
2701:         return array('hour' => $time->hour,
2702:                      'minute' => $time->min,
2703:                      'second' => $time->sec);
2704:     }
2705: 
2706:     function emptyTimeArray($time)
2707:     {
2708:         return (is_array($time)
2709:                 && (!isset($time['hour']) || !strlen($time['hour']))
2710:                 && (!isset($time['minute']) || !strlen($time['minute']))
2711:                 && (!$this->_show_seconds || !strlen($time['second'])));
2712:     }
2713: 
2714:     /**
2715:      * Return info about field type.
2716:      */
2717:     function about()
2718:     {
2719:         return array(
2720:             'name' => Horde_Form_Translation::t("Time selection"),
2721:             'params' => array(
2722:                 'seconds' => array('label' => Horde_Form_Translation::t("Show seconds?"),
2723:                                    'type'  => 'boolean')));
2724:     }
2725: 
2726: }
2727: 
2728: class Horde_Form_Type_monthyear extends Horde_Form_Type {
2729: 
2730:     var $_start_year;
2731:     var $_end_year;
2732: 
2733:     function init($start_year = null, $end_year = null)
2734:     {
2735:         if (empty($start_year)) {
2736:             $start_year = 1920;
2737:         }
2738:         if (empty($end_year)) {
2739:             $end_year = date('Y');
2740:         }
2741: 
2742:         $this->_start_year = $start_year;
2743:         $this->_end_year = $end_year;
2744:     }
2745: 
2746:     function isValid(&$var, &$vars, $value, &$message)
2747:     {
2748:         if (!$var->isRequired()) {
2749:             return true;
2750:         }
2751: 
2752:         if (!$vars->get($this->getMonthVar($var)) ||
2753:             !$vars->get($this->getYearVar($var))) {
2754:             $message = Horde_Form_Translation::t("Please enter a month and a year.");
2755:             return false;
2756:         }
2757: 
2758:         return true;
2759:     }
2760: 
2761:     function getMonthVar($var)
2762:     {
2763:         return $var->getVarName() . '[month]';
2764:     }
2765: 
2766:     function getYearVar($var)
2767:     {
2768:         return $var->getVarName() . '[year]';
2769:     }
2770: 
2771:     /**
2772:      * Return info about field type.
2773:      */
2774:     function about()
2775:     {
2776:         return array('name' => Horde_Form_Translation::t("Month and year"),
2777:                      'params' => array(
2778:                          'start_year' => array('label' => Horde_Form_Translation::t("Start year"),
2779:                                                'type'  => 'int'),
2780:                          'end_year'   => array('label' => Horde_Form_Translation::t("End year"),
2781:                                                'type'  => 'int')));
2782:     }
2783: 
2784: }
2785: 
2786: class Horde_Form_Type_monthdayyear extends Horde_Form_Type {
2787: 
2788:     var $_start_year;
2789:     var $_end_year;
2790:     var $_picker;
2791:     var $_format_in = null;
2792:     var $_format_out = '%x';
2793: 
2794:     /**
2795:      * Return the date supplied as a Horde_Date object.
2796:      *
2797:      * @param integer $start_year  The first available year for input.
2798:      * @param integer $end_year    The last available year for input.
2799:      * @param boolean $picker      Do we show the DHTML calendar?
2800:      * @param integer $format_in   The format to use when sending the date
2801:      *                             for storage. Defaults to Unix epoch.
2802:      *                             Similar to the strftime() function.
2803:      * @param integer $format_out  The format to use when displaying the
2804:      *                             date. Similar to the strftime() function.
2805:      */
2806:     function init($start_year = '', $end_year = '', $picker = true,
2807:                   $format_in = null, $format_out = '%x')
2808:     {
2809:         if (empty($start_year)) {
2810:             $start_year = date('Y');
2811:         }
2812:         if (empty($end_year)) {
2813:             $end_year = date('Y') + 10;
2814:         }
2815: 
2816:         $this->_start_year = $start_year;
2817:         $this->_end_year = $end_year;
2818:         $this->_picker = $picker;
2819:         $this->_format_in = $format_in;
2820:         $this->_format_out = $format_out;
2821:     }
2822: 
2823:     function isValid(&$var, &$vars, $value, &$message)
2824:     {
2825:         $date = $vars->get($var->getVarName());
2826:         $empty = $this->emptyDateArray($date);
2827: 
2828:         if ($empty == 1 && $var->isRequired()) {
2829:             $message = Horde_Form_Translation::t("This field is required.");
2830:             return false;
2831:         } elseif ($empty == 0 && !checkdate($date['month'],
2832:                                             $date['day'],
2833:                                             $date['year'])) {
2834:             $message = Horde_Form_Translation::t("Please enter a valid date, check the number of days in the month.");
2835:             return false;
2836:         } elseif ($empty == -1) {
2837:             $message = Horde_Form_Translation::t("Select all date components.");
2838:             return false;
2839:         }
2840: 
2841:         return true;
2842:     }
2843: 
2844:     /**
2845:      * Determine if the provided date value is completely empty, partially empty
2846:      * or non-empty.
2847:      *
2848:      * @param mixed $date  String or date part array representation of date.
2849:      *
2850:      * @return integer  0 for non-empty, 1 for completely empty or -1 for
2851:      *                  partially empty.
2852:      */
2853:     function emptyDateArray($date)
2854:     {
2855:         if (!is_array($date)) {
2856:             return (int)empty($date);
2857:         }
2858:         $empty = 0;
2859:         /* Check each date array component. */
2860:         foreach (array('day', 'month', 'year') as $key) {
2861:             if (empty($date[$key])) {
2862:                 $empty++;
2863:             }
2864:         }
2865: 
2866:         /* Check state of empty. */
2867:         if ($empty == 0) {
2868:             /* If no empty parts return 0. */
2869:             return 0;
2870:         } elseif ($empty == 3) {
2871:             /* If all empty parts return 1. */
2872:             return 1;
2873:         } else {
2874:             /* If some empty parts return -1. */
2875:             return -1;
2876:         }
2877:     }
2878: 
2879:     /**
2880:      * Return the date supplied split up into an array.
2881:      *
2882:      * @param string $date_in  Date in one of the three formats supported by
2883:      *                         Horde_Form and Horde_Date (ISO format
2884:      *                         YYYY-MM-DD HH:MM:SS, timestamp YYYYMMDDHHMMSS
2885:      *                         and UNIX epoch) plus the fourth YYYY-MM-DD.
2886:      *
2887:      * @return array  Array with three elements - year, month and day.
2888:      */
2889:     function getDateParts($date_in)
2890:     {
2891:         if (is_array($date_in)) {
2892:             /* This is probably a failed isValid input so just return
2893:              * the parts as they are. */
2894:             return $date_in;
2895:         } elseif (empty($date_in)) {
2896:             /* This is just an empty field so return empty parts. */
2897:             return array('year' => '', 'month' => '', 'day' => '');
2898:         }
2899: 
2900:         $date = $this->getDateOb($date_in);
2901:         return array('year' => $date->year,
2902:                      'month' => $date->month,
2903:                      'day' => $date->mday);
2904:     }
2905: 
2906:     /**
2907:      * Return the date supplied as a Horde_Date object.
2908:      *
2909:      * @param string $date_in  Date in one of the three formats supported by
2910:      *                         Horde_Form and Horde_Date (ISO format
2911:      *                         YYYY-MM-DD HH:MM:SS, timestamp YYYYMMDDHHMMSS
2912:      *                         and UNIX epoch) plus the fourth YYYY-MM-DD.
2913:      *
2914:      * @return Horde_Date  The date object.
2915:      */
2916:     function getDateOb($date_in)
2917:     {
2918:         if (is_array($date_in)) {
2919:             /* If passed an array change it to the ISO format. */
2920:             if ($this->emptyDateArray($date_in) == 0) {
2921:                 $date_in = sprintf('%04d-%02d-%02d 00:00:00',
2922:                                    $date_in['year'],
2923:                                    $date_in['month'],
2924:                                    $date_in['day']);
2925:             }
2926:         } elseif (preg_match('/^\d{4}-?\d{2}-?\d{2}$/', $date_in)) {
2927:             /* Fix the date if it is the shortened ISO. */
2928:             $date_in = $date_in . ' 00:00:00';
2929:         }
2930: 
2931:         return new Horde_Date($date_in);
2932:     }
2933: 
2934:     /**
2935:      * Return the date supplied as a Horde_Date object.
2936:      *
2937:      * @param string $date  Either an already set up Horde_Date object or a
2938:      *                      string date in one of the three formats supported
2939:      *                      by Horde_Form and Horde_Date (ISO format
2940:      *                      YYYY-MM-DD HH:MM:SS, timestamp YYYYMMDDHHMMSS and
2941:      *                      UNIX epoch) plus the fourth YYYY-MM-DD.
2942:      *
2943:      * @return string  The date formatted according to the $format_out
2944:      *                 parameter when setting up the monthdayyear field.
2945:      */
2946:     function formatDate($date)
2947:     {
2948:         if (!($date instanceof Horde_Date)) {
2949:             $date = $this->getDateOb($date);
2950:         }
2951: 
2952:         return $date->strftime($this->_format_out);
2953:     }
2954: 
2955:     /**
2956:      * Insert the date input through the form into $info array, in the format
2957:      * specified by the $format_in parameter when setting up monthdayyear
2958:      * field.
2959:      */
2960:     function getInfo(&$vars, &$var, &$info)
2961:     {
2962:         $info = $this->_validateAndFormat($var->getValue($vars), $var);
2963:     }
2964: 
2965:     /**
2966:      * Validate/format a date submission.
2967:      */
2968:     function _validateAndFormat($value, &$var)
2969:     {
2970:         /* If any component is empty consider it a bad date and return the
2971:          * default. */
2972:         if ($this->emptyDateArray($value) == 1) {
2973:             return $var->getDefault();
2974:         } else {
2975:             $date = $this->getDateOb($value);
2976:             if ($this->_format_in === null) {
2977:                 return $date->timestamp();
2978:             } else {
2979:                 return $date->strftime($this->_format_in);
2980:             }
2981:         }
2982:     }
2983: 
2984:     /**
2985:      * Return info about field type.
2986:      */
2987:     function about()
2988:     {
2989:         return array(
2990:             'name' => Horde_Form_Translation::t("Date selection"),
2991:             'params' => array(
2992:                 'start_year' => array('label' => Horde_Form_Translation::t("Start year"),
2993:                                       'type'  => 'int'),
2994:                 'end_year'   => array('label' => Horde_Form_Translation::t("End year"),
2995:                                       'type'  => 'int'),
2996:                 'picker'     => array('label' => Horde_Form_Translation::t("Show picker?"),
2997:                                       'type'  => 'boolean'),
2998:                 'format_in'  => array('label' => Horde_Form_Translation::t("Storage format"),
2999:                                       'type'  => 'text'),
3000:                 'format_out' => array('label' => Horde_Form_Translation::t("Display format"),
3001:                                       'type'  => 'text')));
3002:     }
3003: 
3004: }
3005: 
3006: class Horde_Form_Type_datetime extends Horde_Form_Type {
3007: 
3008:     var $_mdy;
3009:     var $_hms;
3010:     var $_show_seconds;
3011: 
3012:     /**
3013:      * Return the date supplied as a Horde_Date object.
3014:      *
3015:      * @param integer $start_year  The first available year for input.
3016:      * @param integer $end_year    The last available year for input.
3017:      * @param boolean $picker      Do we show the DHTML calendar?
3018:      * @param integer $format_in   The format to use when sending the date
3019:      *                             for storage. Defaults to Unix epoch.
3020:      *                             Similar to the strftime() function.
3021:      * @param integer $format_out  The format to use when displaying the
3022:      *                             date. Similar to the strftime() function.
3023:      * @param boolean $show_seconds Include a form input for seconds.
3024:      */
3025:     function init($start_year = '', $end_year = '', $picker = true,
3026:                   $format_in = null, $format_out = '%x', $show_seconds = false)
3027:     {
3028:         $this->_mdy = new Horde_Form_Type_monthdayyear();
3029:         $this->_mdy->init($start_year, $end_year, $picker, $format_in, $format_out);
3030: 
3031:         $this->_hms = new Horde_Form_Type_hourminutesecond();
3032:         $this->_hms->init($show_seconds);
3033:         $this->_show_seconds = $show_seconds;
3034:     }
3035: 
3036:     function isValid(&$var, &$vars, $value, &$message)
3037:     {
3038:         $date = $vars->get($var->getVarName());
3039:         if (!$this->_show_seconds && !isset($date['second'])) {
3040:             $date['second'] = '';
3041:         }
3042:         $mdy_empty = $this->emptyDateArray($date);
3043:         $hms_empty = $this->emptyTimeArray($date);
3044: 
3045:         $valid = true;
3046: 
3047:         /* Require all fields if one field is not empty */
3048:         if ($var->isRequired() || $mdy_empty != 1 || !$hms_empty) {
3049:             $old_required = $var->required;
3050:             $var->required = true;
3051: 
3052:             $mdy_valid = $this->_mdy->isValid($var, $vars, $value, $message);
3053:             $hms_valid = $this->_hms->isValid($var, $vars, $value, $message);
3054:             $var->required = $old_required;
3055: 
3056:             $valid = $mdy_valid && $hms_valid;
3057:             if ($mdy_valid && !$hms_valid) {
3058:                 $message = Horde_Form_Translation::t("You must choose a time.");
3059:             } elseif ($hms_valid && !$mdy_valid) {
3060:                 $message = Horde_Form_Translation::t("You must choose a date.");
3061:             }
3062:         }
3063: 
3064:         return $valid;
3065:     }
3066: 
3067:     function getInfo(&$vars, &$var, &$info)
3068:     {
3069:         /* If any component is empty consider it a bad date and return the
3070:          * default. */
3071:         $value = $var->getValue($vars);
3072:         if ($this->emptyDateArray($value) == 1 || $this->emptyTimeArray($value)) {
3073:             $info = $var->getDefault();
3074:             return;
3075:         }
3076: 
3077:         $date = $this->getDateOb($value);
3078:         $time = $this->getTimeOb($value);
3079:         $date->hour = $time->hour;
3080:         $date->min = $time->min;
3081:         $date->sec = $time->sec;
3082:         if ($this->getProperty('format_in') === null) {
3083:             $info = $date->timestamp();
3084:         } else {
3085:             $info = $date->strftime($this->getProperty('format_in'));
3086:         }
3087:     }
3088: 
3089:     function getProperty($property)
3090:     {
3091:         if ($property == 'show_seconds') {
3092:             return $this->_hms->getProperty($property);
3093:         } else {
3094:             return $this->_mdy->getProperty($property);
3095:         }
3096:     }
3097: 
3098:     function setProperty($property, $value)
3099:     {
3100:         if ($property == 'show_seconds') {
3101:             $this->_hms->setProperty($property, $value);
3102:         } else {
3103:             $this->_mdy->setProperty($property, $value);
3104:         }
3105:     }
3106: 
3107:     function checktime($hour, $minute, $second)
3108:     {
3109:         return $this->_hms->checktime($hour, $minute, $second);
3110:     }
3111: 
3112:     function getTimeOb($time_in)
3113:     {
3114:         return $this->_hms->getTimeOb($time_in);
3115:     }
3116: 
3117:     function getTimeParts($time_in)
3118:     {
3119:         return $this->_hms->getTimeParts($time_in);
3120:     }
3121: 
3122:     function emptyTimeArray($time)
3123:     {
3124:         return $this->_hms->emptyTimeArray($time);
3125:     }
3126: 
3127:     function emptyDateArray($date)
3128:     {
3129:         return $this->_mdy->emptyDateArray($date);
3130:     }
3131: 
3132:     function getDateParts($date_in)
3133:     {
3134:         return $this->_mdy->getDateParts($date_in);
3135:     }
3136: 
3137:     function getDateOb($date_in)
3138:     {
3139:         return $this->_mdy->getDateOb($date_in);
3140:     }
3141: 
3142:     function formatDate($date)
3143:     {
3144:         if ($this->_mdy->emptyDateArray($date)) {
3145:             return '';
3146:         }
3147:         return $this->_mdy->formatDate($date);
3148:     }
3149: 
3150:     function about()
3151:     {
3152:         return array(
3153:             'name' => Horde_Form_Translation::t("Date and time selection"),
3154:             'params' => array(
3155:                 'start_year' => array('label' => Horde_Form_Translation::t("Start year"),
3156:                                       'type'  => 'int'),
3157:                 'end_year'   => array('label' => Horde_Form_Translation::t("End year"),
3158:                                       'type'  => 'int'),
3159:                 'picker'     => array('label' => Horde_Form_Translation::t("Show picker?"),
3160:                                       'type'  => 'boolean'),
3161:                 'format_in'  => array('label' => Horde_Form_Translation::t("Storage format"),
3162:                                       'type'  => 'text'),
3163:                 'format_out' => array('label' => Horde_Form_Translation::t("Display format"),
3164:                                       'type'  => 'text'),
3165:                 'seconds'    => array('label' => Horde_Form_Translation::t("Show seconds?"),
3166:                                       'type'  => 'boolean')));
3167:     }
3168: 
3169: }
3170: 
3171: class Horde_Form_Type_colorpicker extends Horde_Form_Type {
3172: 
3173:     function isValid(&$var, &$vars, $value, &$message)
3174:     {
3175:         if ($var->isRequired() && empty($value)) {
3176:             $message = Horde_Form_Translation::t("This field is required.");
3177:             return false;
3178:         }
3179: 
3180:         if (empty($value) || preg_match('/^#([0-9a-z]){6}$/i', $value)) {
3181:             return true;
3182:         }
3183: 
3184:         $message = Horde_Form_Translation::t("This field must contain a color code in the RGB Hex format, for example '#1234af'.");
3185:         return false;
3186:     }
3187: 
3188:     /**
3189:      * Return info about field type.
3190:      */
3191:     function about()
3192:     {
3193:         return array('name' => Horde_Form_Translation::t("Colour selection"));
3194:     }
3195: 
3196: }
3197: 
3198: class Horde_Form_Type_sound extends Horde_Form_Type {
3199: 
3200:     var $_sounds = array();
3201: 
3202:     function init()
3203:     {
3204:         $this->_sounds = array_keys(Horde_Themes::soundList());
3205:     }
3206: 
3207:     function getSounds()
3208:     {
3209:         return $this->_sounds;
3210:     }
3211: 
3212:     function isValid(&$var, &$vars, $value, &$message)
3213:     {
3214:         if ($var->isRequired() && empty($value)) {
3215:             $message = Horde_Form_Translation::t("This field is required.");
3216:             return false;
3217:         }
3218: 
3219:         if (empty($value) || in_array($value, $this->_sounds)) {
3220:             return true;
3221:         }
3222: 
3223:         $message = Horde_Form_Translation::t("Please choose a sound.");
3224:         return false;
3225:     }
3226: 
3227:     /**
3228:      * Return info about field type.
3229:      */
3230:     function about()
3231:     {
3232:         return array('name' => Horde_Form_Translation::t("Sound selection"));
3233:     }
3234: 
3235: }
3236: 
3237: class Horde_Form_Type_sorter extends Horde_Form_Type {
3238: 
3239:     var $_instance;
3240:     var $_values;
3241:     var $_size;
3242:     var $_header;
3243: 
3244:     function init($values, $size = 8, $header = '')
3245:     {
3246:         static $horde_sorter_instance = 0;
3247: 
3248:         /* Get the next progressive instance count for the horde
3249:          * sorter so that multiple sorters can be used on one page. */
3250:         $horde_sorter_instance++;
3251:         $this->_instance = 'horde_sorter_' . $horde_sorter_instance;
3252:         $this->_values = $values;
3253:         $this->_size   = $size;
3254:         $this->_header = $header;
3255:     }
3256: 
3257:     function isValid(&$var, &$vars, $value, &$message)
3258:     {
3259:         return true;
3260:     }
3261: 
3262:     function getValues()
3263:     {
3264:         return $this->_values;
3265:     }
3266: 
3267:     function getSize()
3268:     {
3269:         return $this->_size;
3270:     }
3271: 
3272:     function getHeader()
3273:     {
3274:         if (!empty($this->_header)) {
3275:             return $this->_header;
3276:         }
3277:         return '';
3278:     }
3279: 
3280:     function getOptions($keys = null)
3281:     {
3282:         $html = '';
3283:         if ($this->_header) {
3284:             $html .= '<option value="">' . htmlspecialchars($this->_header) . '</option>';
3285:         }
3286: 
3287:         if (empty($keys)) {
3288:             $keys = array_keys($this->_values);
3289:         } else {
3290:             $keys = explode("\t", $keys['array']);
3291:         }
3292:         foreach ($keys as $sl_key) {
3293:             $html .= '<option value="' . $sl_key . '">' . htmlspecialchars($this->_values[$sl_key]) . '</option>';
3294:         }
3295: 
3296:         return $html;
3297:     }
3298: 
3299:     function getInfo(&$vars, &$var, &$info)
3300:     {
3301:         $value = $vars->get($var->getVarName());
3302:         $info = explode("\t", $value['array']);
3303:     }
3304: 
3305:     /**
3306:      * Return info about field type.
3307:      */
3308:     function about()
3309:     {
3310:         return array(
3311:             'name' => Horde_Form_Translation::t("Sort order selection"),
3312:             'params' => array(
3313:                 'values' => array('label' => Horde_Form_Translation::t("Values"),
3314:                                   'type'  => 'stringarray'),
3315:                 'size'   => array('label' => Horde_Form_Translation::t("Size"),
3316:                                   'type'  => 'int'),
3317:                 'header' => array('label' => Horde_Form_Translation::t("Header"),
3318:                                   'type'  => 'text')));
3319:     }
3320: 
3321: }
3322: 
3323: class Horde_Form_Type_selectfiles extends Horde_Form_Type {
3324: 
3325:     /**
3326:      * The text to use in the link.
3327:      *
3328:      * @var string
3329:      */
3330:     var $_link_text;
3331: 
3332:     /**
3333:      * The style to use for the link.
3334:      *
3335:      * @var string
3336:      */
3337:     var $_link_style;
3338: 
3339:     /**
3340:      *  Create the link with an icon instead of text?
3341:      *
3342:      * @var boolean
3343:      */
3344:     var $_icon;
3345: 
3346:     /**
3347:      * Contains gollem selectfile selectionID
3348:      *
3349:      * @var string
3350:      */
3351:     var $_selectid;
3352: 
3353:     function init($selectid, $link_text = null, $link_style = '',
3354:                   $icon = false)
3355:     {
3356:         $this->_selectid = $selectid;
3357:         if (is_null($link_text)) {
3358:             $link_text = Horde_Form_Translation::t("Select Files");
3359:         }
3360:         $this->_link_text = $link_text;
3361:         $this->_link_style = $link_style;
3362:         $this->_icon = $icon;
3363:     }
3364: 
3365:     function isValid(&$var, &$vars, $value, &$message)
3366:     {
3367:         return true;
3368:     }
3369: 
3370:     function getInfo(&$var, &$vars, &$info)
3371:     {
3372:         $value = $vars->getValue($var);
3373:         $info = $GLOBALS['registry']->call('files/selectlistResults', array($value));
3374:     }
3375: 
3376:     function about()
3377:     {
3378:         return array(
3379:             'name' => Horde_Form_Translation::t("File selection"),
3380:             'params' => array(
3381:                 'selectid'   => array('label' => Horde_Form_Translation::t("Id"),
3382:                                       'type' => 'text'),
3383:                 'link_text'  => array('label' => Horde_Form_Translation::t("Link text"),
3384:                                       'type' => 'text'),
3385:                 'link_style' => array('label' => Horde_Form_Translation::t("Link style"),
3386:                                       'type' => 'text'),
3387:                 'icon'       => array('label' => Horde_Form_Translation::t("Show icon?"),
3388:                                       'type' => 'boolean')));
3389:     }
3390: 
3391: }
3392: 
3393: class Horde_Form_Type_assign extends Horde_Form_Type {
3394: 
3395:     var $_leftValues;
3396:     var $_rightValues;
3397:     var $_leftHeader;
3398:     var $_rightHeader;
3399:     var $_size;
3400:     var $_width;
3401: 
3402:     function init($leftValues, $rightValues, $leftHeader = '',
3403:                   $rightHeader = '', $size = 8, $width = '200px')
3404:     {
3405:         $this->_leftValues = $leftValues;
3406:         $this->_rightValues = $rightValues;
3407:         $this->_leftHeader = $leftHeader;
3408:         $this->_rightHeader = $rightHeader;
3409:         $this->_size = $size;
3410:         $this->_width = $width;
3411:     }
3412: 
3413:     function isValid(&$var, &$vars, $value, &$message)
3414:     {
3415:         return true;
3416:     }
3417: 
3418:     function getValues($side)
3419:     {
3420:         return $side ? $this->_rightValues : $this->_leftValues;
3421:     }
3422: 
3423:     function setValues($side, $values)
3424:     {
3425:         if ($side) {
3426:             $this->_rightValues = $values;
3427:         } else {
3428:             $this->_leftValues = $values;
3429:         }
3430:     }
3431: 
3432:     function getHeader($side)
3433:     {
3434:         return $side ? $this->_rightHeader : $this->_leftHeader;
3435:     }
3436: 
3437:     function getSize()
3438:     {
3439:         return $this->_size;
3440:     }
3441: 
3442:     function getWidth()
3443:     {
3444:         return $this->_width;
3445:     }
3446: 
3447:     function getOptions($side, $formname, $varname)
3448:     {
3449:         $html = '';
3450:         $headers = false;
3451:         if ($side) {
3452:             $values = $this->_rightValues;
3453:             if (!empty($this->_rightHeader)) {
3454:                 $values = array('' => $this->_rightHeader) + $values;
3455:                 $headers = true;
3456:             }
3457:         } else {
3458:             $values = $this->_leftValues;
3459:             if (!empty($this->_leftHeader)) {
3460:                 $values = array('' => $this->_leftHeader) + $values;
3461:                 $headers = true;
3462:             }
3463:         }
3464: 
3465:         foreach ($values as $key => $val) {
3466:             $html .= '<option value="' . htmlspecialchars($key) . '"';
3467:             if ($headers) {
3468:                 $headers = false;
3469:             } else {
3470:                 $html .= ' ondblclick="Horde_Form_Assign.move(\'' . $formname . '\', \'' . $varname . '\', ' . (int)$side . ');"';
3471:             }
3472:             $html .= '>' . htmlspecialchars($val) . '</option>';
3473:         }
3474: 
3475:         return $html;
3476:     }
3477: 
3478:     function getInfo(&$vars, &$var, &$info)
3479:     {
3480:         $value = $vars->get($var->getVarName() . '__values');
3481:         if (strpos($value, "\t\t") === false) {
3482:             $left = $value;
3483:             $right = '';
3484:         } else {
3485:             list($left, $right) = explode("\t\t", $value);
3486:         }
3487:         if (empty($left)) {
3488:             $info['left'] = array();
3489:         } else {
3490:             $info['left'] = explode("\t", $left);
3491:         }
3492:         if (empty($right)) {
3493:             $info['right'] = array();
3494:         } else {
3495:             $info['right'] = explode("\t", $right);
3496:         }
3497:     }
3498: 
3499:     /**
3500:      * Return info about field type.
3501:      */
3502:     function about()
3503:     {
3504:         return array(
3505:             'name' => Horde_Form_Translation::t("Assignment columns"),
3506:             'params' => array(
3507:                 'leftValues'  => array('label' => Horde_Form_Translation::t("Left values"),
3508:                                        'type'  => 'stringarray'),
3509:                 'rightValues' => array('label' => Horde_Form_Translation::t("Right values"),
3510:                                        'type'  => 'stringarray'),
3511:                 'leftHeader'  => array('label' => Horde_Form_Translation::t("Left header"),
3512:                                        'type'  => 'text'),
3513:                 'rightHeader' => array('label' => Horde_Form_Translation::t("Right header"),
3514:                                        'type'  => 'text'),
3515:                 'size'        => array('label' => Horde_Form_Translation::t("Size"),
3516:                                        'type'  => 'int'),
3517:                 'width'       => array('label' => Horde_Form_Translation::t("Width in CSS units"),
3518:                                        'type'  => 'text')));
3519:     }
3520: 
3521: }
3522: 
3523: class Horde_Form_Type_creditcard extends Horde_Form_Type {
3524: 
3525:     function isValid(&$var, &$vars, $value, &$message)
3526:     {
3527:         if (empty($value) && $var->isRequired()) {
3528:             $message = Horde_Form_Translation::t("This field is required.");
3529:             return false;
3530:         }
3531: 
3532:         if (!empty($value)) {
3533:             /* getCardType() will also verify the checksum. */
3534:             $type = $this->getCardType($value);
3535:             if ($type === false || $type == 'unknown') {
3536:                 $message = Horde_Form_Translation::t("This does not seem to be a valid card number.");
3537:                 return false;
3538:             }
3539:         }
3540: 
3541:         return true;
3542:     }
3543: 
3544:     function getChecksum($ccnum)
3545:     {
3546:         $len = strlen($ccnum);
3547:         if (!is_long($len / 2)) {
3548:             $weight = 2;
3549:             $digit = $ccnum[0];
3550:         } elseif (is_long($len / 2)) {
3551:             $weight = 1;
3552:             $digit = $ccnum[0] * 2;
3553:         }
3554:         if ($digit > 9) {
3555:             $digit = $digit - 9;
3556:         }
3557:         $i = 1;
3558:         $checksum = $digit;
3559:         while ($i < $len) {
3560:             if ($ccnum[$i] != ' ') {
3561:                 $digit = $ccnum[$i] * $weight;
3562:                 $weight = ($weight == 1) ? 2 : 1;
3563:                 if ($digit > 9) {
3564:                     $digit = $digit - 9;
3565:                 }
3566:                 $checksum += $digit;
3567:             }
3568:             $i++;
3569:         }
3570: 
3571:         return $checksum;
3572:     }
3573: 
3574:     function getCardType($ccnum)
3575:     {
3576:         $sum = $this->getChecksum($ccnum);
3577:         $l = strlen($ccnum);
3578: 
3579:         // Screen checksum.
3580:         if (($sum % 10) != 0) {
3581:             return false;
3582:         }
3583: 
3584:         // Check for Visa.
3585:         if ((($l == 16) || ($l == 13)) &&
3586:             ($ccnum[0] == 4)) {
3587:             return 'visa';
3588:         }
3589: 
3590:         // Check for MasterCard.
3591:         if (($l == 16) &&
3592:             ($ccnum[0] == 5) &&
3593:             ($ccnum[1] >= 1) &&
3594:             ($ccnum[1] <= 5)) {
3595:             return 'mastercard';
3596:         }
3597: 
3598:         // Check for Amex.
3599:         if (($l == 15) &&
3600:             ($ccnum[0] == 3) &&
3601:             (($ccnum[1] == 4) || ($ccnum[1] == 7))) {
3602:             return 'amex';
3603:         }
3604: 
3605:         // Check for Discover (Novus).
3606:         if (strlen($ccnum) == 16 &&
3607:             substr($ccnum, 0, 4) == '6011') {
3608:             return 'discover';
3609:         }
3610: 
3611:         // If we got this far, then no card matched.
3612:         return 'unknown';
3613:     }
3614: 
3615:     /**
3616:      * Return info about field type.
3617:      */
3618:     function about()
3619:     {
3620:         return array('name' => Horde_Form_Translation::t("Credit card number"));
3621:     }
3622: 
3623: }
3624: 
3625: class Horde_Form_Type_obrowser extends Horde_Form_Type {
3626: 
3627:     function isValid(&$var, &$vars, $value, &$message)
3628:     {
3629:         return true;
3630:     }
3631: 
3632:     /**
3633:      * Return info about field type.
3634:      */
3635:     function about()
3636:     {
3637:         return array('name' => Horde_Form_Translation::t("Relationship browser"));
3638:     }
3639: 
3640: }
3641: 
3642: class Horde_Form_Type_dblookup extends Horde_Form_Type_enum {
3643: 
3644:     function init($db, $sql, $prompt = null)
3645:     {
3646:         $values = array();
3647:         try {
3648:             $col = $db->selectValues($sql);
3649:             $values = array_combine($col, $col);
3650:         } catch (Horde_Db_Exception $e) {
3651:         }
3652:         parent::init($values, $prompt);
3653:     }
3654: 
3655:     /**
3656:      * Return info about field type.
3657:      */
3658:     function about()
3659:     {
3660:         return array(
3661:             'name' => Horde_Form_Translation::t("Database lookup"),
3662:             'params' => array(
3663:                 'dsn' => array('label' => Horde_Form_Translation::t("DSN (see http://pear.php.net/manual/en/package.database.db.intro-dsn.php)"),
3664:                                'type'  => 'text'),
3665:                 'sql' => array('label' => Horde_Form_Translation::t("SQL statement for value lookups"),
3666:                                'type'  => 'text'),
3667:                 'prompt' => array('label' => Horde_Form_Translation::t("Prompt text"),
3668:                                   'type'  => 'text'))
3669:             );
3670:     }
3671: 
3672: }
3673: 
3674: class Horde_Form_Type_figlet extends Horde_Form_Type {
3675: 
3676:     var $_text;
3677:     var $_font;
3678: 
3679:     function init($text, $font)
3680:     {
3681:         $this->_text = $text;
3682:         $this->_font = $font;
3683:     }
3684: 
3685:     function isValid(&$var, &$vars, $value, &$message)
3686:     {
3687:         if (empty($value) && $var->isRequired()) {
3688:             $message = Horde_Form_Translation::t("This field is required.");
3689:             return false;
3690:         }
3691: 
3692:         if (Horde_String::lower($value) != Horde_String::lower($this->_text)) {
3693:             $message = Horde_Form_Translation::t("The text you entered did not match the text on the screen.");
3694:             return false;
3695:         }
3696: 
3697:         return true;
3698:     }
3699: 
3700:     function getFont()
3701:     {
3702:         return $this->_font;
3703:     }
3704: 
3705:     function getText()
3706:     {
3707:         return $this->_text;
3708:     }
3709: 
3710:     /**
3711:      * Return info about field type.
3712:      */
3713:     function about()
3714:     {
3715:         return array(
3716:             'name' => Horde_Form_Translation::t("Figlet CAPTCHA"),
3717:             'params' => array(
3718:                 'text' => array('label' => Horde_Form_Translation::t("Text"),
3719:                                 'type'  => 'text'),
3720:                 'font' => array('label' => Horde_Form_Translation::t("Figlet font"),
3721:                                 'type'  => 'text'))
3722:             );
3723:     }
3724: 
3725: }
3726: 
3727: class Horde_Form_Type_captcha extends Horde_Form_Type_figlet {
3728: 
3729:     /**
3730:      * Return info about field type.
3731:      */
3732:     function about()
3733:     {
3734:         return array(
3735:             'name' => Horde_Form_Translation::t("Image CAPTCHA"),
3736:             'params' => array(
3737:                 'text' => array('label' => Horde_Form_Translation::t("Text"),
3738:                                 'type'  => 'text'),
3739:                 'font' => array('label' => Horde_Form_Translation::t("Font"),
3740:                                 'type'  => 'text'))
3741:             );
3742:     }
3743: 
3744: }
3745: 
3746: class Horde_Form_Type_category extends Horde_Form_Type {
3747: 
3748:     function getInfo(&$vars, &$var, &$info)
3749:     {
3750:         $info = $var->getValue($vars);
3751:         if ($info == '*new*') {
3752:             $info = array('new' => true,
3753:                           'value' => $vars->get('new_category'));
3754:         } else {
3755:             $info = array('new' => false,
3756:                           'value' => $info);
3757:         }
3758:     }
3759: 
3760:     /**
3761:      * Return info about field type.
3762:      */
3763:     function about()
3764:     {
3765:         return array('name' => Horde_Form_Translation::t("Category"));
3766:     }
3767: 
3768:     function isValid(&$var, &$vars, $value, &$message)
3769:     {
3770:         if (empty($value) && $var->isRequired()) {
3771:             $message = Horde_Form_Translation::t("This field is required.");
3772:             return false;
3773:         }
3774: 
3775:         return true;
3776:     }
3777: 
3778: }
3779: 
3780: class Horde_Form_Type_invalid extends Horde_Form_Type {
3781: 
3782:     var $message;
3783: 
3784:     function init($message)
3785:     {
3786:         $this->message = $message;
3787:     }
3788: 
3789:     function isValid(&$var, &$vars, $value, &$message)
3790:     {
3791:         return false;
3792:     }
3793: 
3794: }
3795: 
API documentation generated by ApiGen