1 <?php
4 class mailMethod{
6 /* Allow modification of account_ids for existing mail accounts */
7 protected $modifyableMail = TRUE;
9 /* Allow modification of the mail server attribute existing mail accounts */
10 protected $modifyableServer = TRUE;
12 /* Enforces same value for 'mail' as used for 'cn' */
13 protected $mailEqualsCN = FALSE;
15 /* the attribute used to create accounts */
16 protected $uattrib = "mail"; // Naming attribute for accounts, e.g. imap.
18 /* The account prefixes, keep the '.' here! See FAQ cyrusUseSlashes */
19 protected $user_prefix = "user.";
20 protected $share_prefix = "share.";
22 /* Account ID creation
24 !!Depends on the attributes 'user_prefix'/'share_prefix' and the option 'cyrusUseSlashes'
26 Examples - based on defaults :
27 %PREFIX% => "user." or "user/" (Depending on cyrusUseSlashes=FALSE/TRUE)
28 %CN% => "technik" (The groups cn)
29 %UID% => "herbert" (The users uid)
30 %MAIL% => "herbert@domain.de"(The mail address)
31 %DOMAIN% => "domain.de" (The domain part of the specified mail)
32 %MAILPART% => "herbert" (The mail address without domain)
33 %UATTRIB% => "herbert"/"herbert@domains.de"
34 (Configured in gosa.conf mailAttribute="mail"/"uid")
35 */
36 protected $user_id = "%PREFIX%%UATTRIB%";
37 protected $share_id = "%PREFIX%%UATTRIB%";
39 /* Create accounts in cyrus style with '/' instead of '.' */
40 protected $cyrusUseSlashes= FALSE;
42 /* gosaSharedFolderTarget settings,
43 * E.g.
44 * For an accountID like: 'share/herberts.folder@gonicus.de' the value 'dummy+'
45 * will result in gosaSharedFolderTarget: dummy+share/herberts.folder@gonicus.de
46 */
47 protected $gosaSharedPrefix = '';
49 /* The atribute mapping for this class Source --> Destination */
50 protected $attributes = array();
51 protected $userObjectClasses = array();
52 protected $shareObjectClasses = array();
54 /* Enabled mail domain selection. If enabled getMailDomains() have to return an array
55 * with the domain parts.
56 */
57 protected $enableDomainSelection= FALSE;
58 protected $enableQuota = TRUE;
59 protected $enableSieveManager = FALSE;
60 protected $enableVacationRange = TRUE;
61 protected $enableFolderTypes = FALSE;
63 /* Default values */
64 protected $quotaValue = 0;
65 protected $quotaUsage = 0;
67 /* Method internal */
68 protected $type = "user";
69 protected $account_id = "";
70 protected $initial_account_id = "";
71 protected $connected = FALSE;
72 protected $error = "";
73 protected $parent = NULL;
74 protected $MailServer = "";
76 protected $default_acls = array("__anyone__" => "p", "__member__" => "lrswp");
78 protected $acl_map = array(
79 "lrsw" => "read",
80 "lrswp" => "post",
81 "p" => "external post",
82 "lrswip" => "append",
83 "lrswipcd" => "write",
84 "lrswipcda"=> "admin",
85 " " => "none");
87 protected $acl_mapping = array();
90 /*! \brief Constructs the mail class
91 @param Object Config The GOsa configuration object
92 @param Object Plugin The initator
93 @param String Open "user" or "group" account.
94 */
95 function __construct(&$config, &$parent, $type = "user")
96 {
97 $this->parent = $parent;
98 $this->config = $config;
100 /* Create a refernce to the mail selected server
101 */
102 if(isset($this->parent->gosaMailServer)){
103 $this->MailServer = &$this->parent->gosaMailServer;
104 }else{
105 trigger_error("mailMethod with invalid parent object initialized.");
106 }
108 if(!in_array($this->type,array("user","group"))){
109 trigger_error("Unknown mail class type used '".$type."'.");
110 }else{
111 $this->type = $type;
112 }
114 }
117 /*! \brief Intialize attributes and config settings.
118 */
119 protected function init()
120 {
121 /* Get config value for cyrusUseSlashes
122 */
123 if ($this->config->get_cfg_value("cyrusUseSlashes") == "true"){
124 $this->cyrusUseSlashes = TRUE;
125 @DEBUG (DEBUG_MAIL, __LINE__, __FUNCTION__, __FILE__, "","<b>MAIL:</b> cyrusUseSlashes: <b>Enabled</b>");
126 }else{
127 @DEBUG (DEBUG_MAIL, __LINE__, __FUNCTION__, __FILE__, "","<b>MAIL:</b> cyrusUseSlashes: <b>Disabled</b>");
128 }
130 /* Check if the mail account identification attribute
131 is overridden in the configuration file
132 */
133 if($this->config->get_cfg_value("mailAttribute","mail") != ""){
134 $new_uattrib= strtolower($this->config->get_cfg_value("mailAttribute"));
135 if(in_array($new_uattrib,array("mail","uid"))){
136 $this->uattrib = $new_uattrib;
137 }else{
138 @DEBUG (DEBUG_MAIL, __LINE__, __FUNCTION__, __FILE__, "<b>".$new_uattrib."</b>",
139 "<b>MAIL:</b> Unsupported 'mailAttribute' in gosa configuration specified");
140 msg_dialog::display(_("Configuration error"),
141 sprintf(_("The configured mail attribute '%s' is unsupported!"), $new_uattrib), ERROR_DIALOG);
142 }
143 }
145 /* Create ACL map */
146 foreach($this->acl_map as $acl => $name){
147 $this->acl_mapping[$acl] = _($name);
148 }
150 /* Check if we have an individual user/folder creation syntax
151 */
152 $tmp = $this->config->get_cfg_value("mailUserCreation");
153 if(!empty($tmp)){
154 @DEBUG (DEBUG_MAIL, __LINE__, __FUNCTION__, __FILE__, "<i>".$tmp."</i>",
155 "<b>MAIL:</b> User creation set to");
156 $this->user_id = $tmp;
157 }
158 $tmp = $this->config->get_cfg_value("mailFolderCreation");
159 if(!empty($tmp)){
160 @DEBUG (DEBUG_MAIL, __LINE__, __FUNCTION__, __FILE__, "<i>".$tmp."</i>",
161 "<b>MAIL:</b> Shared folder creation set to");
162 $this->share_id = $tmp;
163 }
165 $this->build_account_id();
166 $this->initial_account_id = $this->account_id;
167 }
170 public function fixAttributesOnLoad()
171 {
172 foreach($this->attributes as $source => $dest){
173 if(isset($this->parent->attrs[$source])){
174 $this->parent->attrs[$dest] = $this->parent->attrs[$source];
175 }
176 if(isset($this->parent->$source)){
177 $this->parent->$dest = $this->parent->$source;
178 }
179 if(isset($this->parent->attrs[$source][0])){
180 $this->parent->saved_attributes[$source] = $this->parent->attrs[$source][0];
181 }
182 }
183 }
186 public function mailEqualsCN()
187 {
188 return($this->mailEqualsCN);
189 }
192 public function fixAttributesOnRemove()
193 {
194 /* Remove objectClasses
195 */
196 if($this->type == "user"){
197 $this->parent->attrs['objectClass'] =
198 array_remove_entries_ics($this->userObjectClasses, $this->parent->attrs['objectClass']);
199 }else{
200 $this->parent->attrs['objectClass'] =
201 array_remove_entries_ics($this->shareObjectClasses, $this->parent->attrs['objectClass']);
202 }
203 foreach($this->attributes as $source => $dest){
204 $this->attrs[$dest] = array();
205 $this->attrs[$source] = array();
206 }
207 }
209 public function fixAttributesOnStore()
210 {
211 foreach($this->attributes as $source => $dest){
212 if(isset($this->parent->attrs[$dest])){
213 $this->parent->attrs[$source] = $this->parent->attrs[$dest ];
214 }
215 if(isset($this->parent->$dest)){
216 $this->parent->$source = $this->parent->$dest;
217 }
218 }
220 if($this->type == "user"){
221 $ocs = $this->userObjectClasses;
222 }else{
223 $ocs = $this->shareObjectClasses;
224 }
225 foreach($ocs as $oc){
226 if(!in_array($oc, $this->parent->attrs['objectClass'])){
227 $this->parent->attrs['objectClass'][] = $oc;
228 }
229 }
231 // Add gosaSharedFolderTarget for groups.
232 if($this->type == "group"){
233 $this->parent->attrs['gosaSharedFolderTarget'] =
234 $this->gosaSharedPrefix.$this->account_id;
235 }
236 }
239 /*! \brief Connect services like imap.
240 Not necessary for the base class.
241 @return Boolean True if this method is connected else false.
242 */
243 public function connect()
244 {
245 $this->reset_error();
246 @DEBUG (DEBUG_MAIL, __LINE__, __FUNCTION__, __FILE__,"","<b>MAIL: Connect method</b>: ".get_class($this));
247 @DEBUG (DEBUG_MAIL, __LINE__, __FUNCTION__, __FILE__,"","<b>MAIL: Current server</b>: ".$this->MailServer);
249 $this->connected = TRUE;
250 return(TRUE);
251 }
254 /*! \brief Returns the connection status of this method.
255 @return Boolean True if this method is connected else false.
256 */
257 public function is_connected()
258 {
259 return($this->connected);
260 }
263 /*! \brief Disconnect this method. Close services like imap connection.
264 Not necessary for the base class.
265 */
266 public function disconnect()
267 {
268 $this->reset_error();
269 if($this->is_connected()){
270 @DEBUG (DEBUG_MAIL, __LINE__, __FUNCTION__, __FILE__,"","<b>MAIL: Disconnect method</b>: ".get_class($this));
271 $this->connected =FALSE;
272 }
273 }
276 /*! \brief Returns true the current object represents a valid account
277 (Some methods may check imap accounts here.)
278 @return Boolean TRUE if this is a valid account else FALSE
279 */
280 public function account_exists()
281 {
282 $this->reset_error();
283 return(TRUE);
284 }
287 /*! \brief Returns the last error occurred
288 @return String The last error message.
289 */
290 public function get_error(){
291 return($this->error);
292 }
295 public function isModifyableMail()
296 {
297 return($this->modifyableMail);
298 }
301 public function isModifyableServer()
302 {
303 return($this->modifyableServer);
304 }
307 /*! \brief Returns TRUE if the action caused an error.
308 @return Boolean TRUE on error else FALSE
309 */
310 public function is_error(){
311 return($this->error != "");
312 }
315 /*! \brief Resets the error message.
316 */
317 public function reset_error(){
318 $this->error = "";
319 }
322 public function get_account_id()
323 {
324 $this->build_account_id();
325 return($this->account_id);
326 }
328 /*! \brief Create a new account id, like 'user/name@domain.com'.
329 */
330 protected function build_account_id()
331 {
332 /* Build account identicator */
333 if($this->type == "user"){
334 $prefix = $this->user_prefix;
335 $acc_string = $this->user_id;
336 }else{
337 $prefix = $this->share_prefix;
338 $acc_string = $this->share_id;
339 }
341 /* Create account prefix and respect "cyrusUseSlashes"
342 Do not replace escaped dots for cyrusUseSlashes.
343 */
344 $uattrib = $this->uattrib;
345 if($this->cyrusUseSlashes){
346 $prefix = preg_replace('/([^\\\\])\./',"\\1/",$prefix);
347 $acc_string = preg_replace('/([^\\\\])\./',"\\1/",$acc_string);
348 }
349 $prefix = preg_replace("/\\\\([\.\/])/","\\1",$prefix);
350 $acc_string = preg_replace("/\\\\([\.\/])/","\\1",$acc_string);
352 $domain = $mailpart = "";
353 $mail = $this->parent->mail;
354 if(preg_match("/\@/",$mail)){
355 list($mailpart,$domain) = split("\@",$mail);
356 }
358 /* Create account_id
359 */
360 $from = array("/%cn%/i","/%uid%/i","/%prefix%/i","/%uattrib%/i","/%domain%/i","/%mailpart%/i","/%mail%/i");
361 $to = array($this->parent->cn,$this->parent->uid,$prefix,$this->parent->$uattrib, $domain, $mailpart,$mail);
362 $acc_id = trim(strtolower(preg_replace($from,$to,$acc_string)));
364 /* Check for not replaced pattern.
365 */
366 if(preg_match("/%/",$acc_id)){
367 $notr = preg_replace("/^[^%]*/","",$acc_id);
368 if(!empty($notr)){
369 @DEBUG (DEBUG_MAIL, __LINE__, __FUNCTION__, __FILE__,"<b>Warning</b>",
370 sprintf("<b>MAIL: WARNING unknown pattern in account creation string '%s' near '%s'</b>", $acc_id, $notr));
372 /* Remove incomprehensible patterns */
373 $acc_id = preg_replace("/%[^%]+%/","",$acc_id);
374 }
375 }
378 if($this->account_id != $acc_id){
379 $this->account_id = $acc_id;
380 @DEBUG (DEBUG_MAIL, __LINE__, __FUNCTION__, __FILE__,"", "<b>MAIL:</b> AccountID generated: <b>".$acc_id."</b>");
381 }
382 }
385 /*! \brief Creates a valid folder id for a given folder name.
386 e.g. $folder_id = "INBOX/test" && $this->account_id = "share/mailbox@domain.de"
387 will result in "share/mailbox/test@domain.de"
388 This function is mainly used to read and write folder permissions.
389 @return String A valid folder id
390 */
391 public function create_folder_id($folder, $type = "")
392 {
394 if(!empty($folder)){
395 $folder = trim(preg_replace("/^INBOX[\.\/]*/i","",$folder));
396 }
397 if(!empty($folder)){
398 $folder = "/".$folder;
399 }
401 /* Build account identicator */
402 if($this->type == "user"){
403 $prefix = $this->user_prefix;
404 $acc_string = $this->user_id;
405 }else{
406 $prefix = $this->share_prefix;
407 $acc_string = $this->share_id;
408 }
410 /* Create account prefix and respect "cyrusUseSlashes"
411 Do not replace escaped dots for cyrusUseSlashes.
412 */
413 $uattrib = $this->uattrib;
414 if($this->cyrusUseSlashes){
415 $prefix = preg_replace('/([^\\\\])\./',"\\1/",$prefix);
416 $acc_string = preg_replace('/([^\\\\])\./',"\\1/",$acc_string);
417 }
418 $prefix = preg_replace("/\\\\([\.\/])/","\\1",$prefix);
419 $acc_string = preg_replace("/\\\\([\.\/])/","\\1",$acc_string);
421 $domain = $mailpart = "";
422 $mail = $this->parent->mail;
423 if(preg_match("/\@/",$mail)){
424 list($mailpart,$domain) = split("\@",$mail);
425 }
427 /* Create account_id
428 */
429 $from = array("/%cn%/i","/%uid%/i","/%prefix%/i","/%uattrib%/i","/%domain%/i","/%mailpart%/i","/%mail%/i");
430 $to = array($this->parent->cn,$this->parent->uid,$prefix,$this->parent->$uattrib, $domain, $mailpart,$mail);
431 $acc_id = trim(strtolower(preg_replace($from,$to,$acc_string)));
433 /* Check for not replaced pattern.
434 */
435 if(preg_match("/%/",$acc_id)){
436 $notr = preg_replace("/^[^%]*/","",$acc_id);
437 if(!empty($notr)){
438 @DEBUG (DEBUG_MAIL, __LINE__, __FUNCTION__, __FILE__,"<b>Warning</b>",
439 sprintf("<b>MAIL: WARNING unknown pattern in account creation string '%s' near '%s'</b>", $acc_id, $notr));
441 /* Remove incomprehensible patterns */
442 $acc_id = preg_replace("/%[^%]+%/","",$acc_id);
443 }
444 }
446 if(preg_match("/\@/",$acc_id)){
447 list($mail,$domain) = split("\@",$acc_id);
448 $str = trim($mail . $folder . "@" . $domain);
449 }else{
450 $str = trim($acc_id . $folder);
451 }
452 return($str) ;
453 }
456 /*! \brief Returns the configured mail method for the given parent object,
457 initialized and read for use.
458 @return mailMethod The configured mailMethod.
459 */
460 public function get_method()
461 {
462 $methods = mailMethod::get_methods();
463 if ($this->config->get_cfg_value("mailmethod") != ""){
464 $method= $this->config->get_cfg_value("mailmethod");
465 $cls = get_correct_class_name("mailMethod$method");
466 if(isset($methods[$cls])){
467 @DEBUG (DEBUG_MAIL, __LINE__, __FUNCTION__, __FILE__,"", "<b>MAIL:</b> Selected mailMethod: <b>".$cls."</b>");
468 $tmp = new $cls($this->config,$this->parent,$this->type);
469 $tmp->init();
470 return($tmp);
471 }else{
472 @DEBUG (DEBUG_MAIL, __LINE__, __FUNCTION__, __FILE__, "","<b>MAIL: Invalid mailMethod defined: ".$cls.
473 " falling back to ".get_class($this)."</b>");
475 /* Print out configuration errors directly, we can't catch them everywhere.
476 */
477 msg_dialog::display(_("Configuration error"),
478 sprintf(_("Mail method '%s' is unknown!"), $method), ERROR_DIALOG);
479 }
480 }
482 /* If no valued mailMethod could be detected, return the base class.
483 */
484 $this->init();
485 return($this);
486 }
489 /*! \brief Saves sieve settings
490 */
491 public function saveSieveSettings()
492 {
493 $this->reset_error();
494 return(TRUE);
495 }
498 /*! \brief Creates or Updates the mailAccount represented by this class.
499 */
500 public function updateMailbox()
501 {
502 $this->reset_error();
503 return(TRUE);
504 }
507 /*! \brief Update shared folder dependencies
508 */
509 public function updateSharedFolder()
510 {
511 $this->reset_error();
512 return(TRUE);
513 }
516 /*! \brief Removes the mailbox represented by this class,
517 and update shared folder ACLs .
518 */
519 public function deleteMailbox()
520 {
521 $this->reset_error();
523 @DEBUG (DEBUG_MAIL, __LINE__, __FUNCTION__, __FILE__, "<b>".$this->account_id."</b>" ,
524 "<b>MAIL: Remove account</b> from server :".$this->MailServer);
526 return(TRUE);
527 }
530 /*! \brief Returns the used mail attribute (mail,uid)
531 @param String One out of 'mail','uid'
532 */
533 public function getUAttrib()
534 {
535 return($this->uattrib);
536 }
539 /*! \brief Returns the used mail attribute (mail,uid)
540 @param String One out of 'mail','uid'
541 */
542 public function getUAttribValue()
543 {
544 $uattrib = $this->getUAttrib();
545 return($this->parent->$uattrib);
546 }
549 /*! \brief Returns whether the quota settings are enabled or not
550 @return Boolean TRUE if enabled else FALSE
551 */
552 public function quotaEnabled()
553 {
554 return($this->enableQuota);
555 }
558 /*! \brief Returns the used quota
559 @return Integer Quota used.
560 */
561 public function getQuotaUsage()
562 {
563 return(-1);
564 }
567 /*! \brief Returns the quota restrictions.
568 @return Integer Quota restrictions.
569 */
570 public function getQuota($quotaValue)
571 {
572 return($quotaValue);
573 }
576 /*! \brief Sets the mail quota
577 */
578 public function setQuota($number)
579 {
580 if(!is_numeric($number)){
581 $number = (int) $number;
582 if(!$number){
583 $number = 0;
584 }
585 }
586 $this->quotaValue = $number;
587 return(TRUE);
588 }
591 /*! \brief Returns true whether the domain is selectable or not
592 */
593 public function domainSelectionEnabled()
594 {
595 return($this->enableDomainSelection);
596 }
599 /*! \brief Returns a list of configured mail domains
600 @return Array A list of mail domains
601 */
602 public function getMailDomains()
603 {
604 return(array("Unconfigured"));
605 }
608 /*! \brief Returns the used Spamlevels for this mailmethod
609 */
610 public function getSpamLevels()
611 {
612 $spamlevel= array();
613 for ($i= 0; $i<21; $i++){
614 $spamlevel[]= $i;
615 }
616 return($spamlevel);
617 }
620 /*! \brief Returns the list of configured mailbox folders
621 @return Array The mailbox folders.
622 */
623 public function getMailboxList()
624 {
625 return(array("INBOX"));
626 }
629 /*! \brief Returns whether the vacation range is selectable or not
630 @return Boolean TRUE, FALSE
631 */
632 public function vacationRangeEnabled()
633 {
634 return($this->enableVacationRange);
635 }
638 /*! \brief Returns true if the sieveManagement is allowed
639 @return Boolean TRUE, FALSE
640 */
641 public function allowSieveManagement()
642 {
643 return($this->enableSieveManager);
644 }
647 /*! \brief Checks dependencies to other GOsa plugins.
648 */
649 public function accountCreateable(&$reason = ""){
650 return(TRUE);
651 }
654 /*! \brief Checks whether this account is removeable or not.
655 There may be some dependencies left, eg. kolab.
656 */
657 public function accountRemoveable(&$reason = ""){
658 return(TRUE);
659 }
662 /*! \brief Returns all mail servers configured in GOsa
663 that are useable with this mailMethod.
664 @return Array All useable mail servers.
665 */
666 public function getMailServers()
667 {
668 $mailserver = array();
669 $ui = get_userinfo();
670 foreach ($this->config->data['SERVERS']['IMAP'] as $key => $val){
671 if( $this->MailServer == $key ||
672 preg_match("/r/",$ui->get_permissions($val['server_dn'],"server/goImapServer",""))){
673 $mailserver[]= $key;
674 }
675 }
676 return($mailserver);
677 }
680 /*! \brief Returns the available mailMethods
681 @return Array A list of all avaialable mailMethods_
682 */
683 static protected function get_methods()
684 {
685 global $class_mapping;
686 $available = array();
687 foreach($class_mapping as $class => $path){
688 if($class == "mailMethod") continue;
689 if(preg_match("/^mailMethod/",$class)){
690 $available[$class] = $class;
691 }
692 }
693 return($available);
694 }
697 /* \brief Some method require special folder types, "kolab" for example.
698 !! Those values are dummy values, the base class doesn't use folder types;
699 @return Array Return folder types.
700 */
701 public function getAvailableFolderTypes()
702 {
703 $ret = array();
704 $ret['CAT'][''] = _("None");
705 $ret['SUB_CAT'][''][''] = _("None");
706 return($ret);
707 }
710 /* \brief Returns the selected folder type.
711 !! Those values are dummy values, the base class doesn't use folder types;
712 @return Array The folde type.
713 */
714 public function getFolderType($default)
715 {
716 return($default);
717 }
720 /* \brief Returns the selected folder type.
721 !! Those values are dummy values, the base class doesn't use folder types;
722 @return Array The folde type.
723 */
724 public function setFolderType($type)
725 {
726 return(TRUE) ;
727 }
730 /*! \brief Returns configured acls
731 */
732 public function getFolderACLs($folder_acls)
733 {
734 /* Merge given ACL with acl mapping
735 This ensures that no ACL will accidentally overwritten by gosa.
736 */
737 foreach($folder_acls as $user => $acl){
738 if(!isset($this->acl_mapping[$acl])){
739 $this->acl_mapping[$acl] = $acl;
740 }
741 }
743 return($folder_acls);
744 }
747 /*! \brief Write ACLs back to imap or what ever
748 */
749 public function setFolderACLs($array)
750 {
751 return(TRUE);
752 }
755 /*! \brief Returns a list of all possible acls.
756 @return Array ACLs.
757 */
758 public function getAclTypes()
759 {
760 return( $this->acl_mapping);
761 }
763 public function folderTypesEnabled()
764 {
765 return($this->enableFolderTypes);
766 }
768 public function allow_remove(&$reason)
769 {
770 return(TRUE);
771 }
774 /*! \brief Returns the configured mailMethod
775 @return String the configured mail method or ""
776 */
777 static public function get_current_method($config)
778 {
779 global $class_mapping;
780 $method= $config->get_cfg_value("mailmethod");
781 $cls = get_correct_class_name("mailMethod$method");
782 foreach($class_mapping as $class => $path){
783 if($class == $cls){
784 return($class);
785 }
786 }
787 return("");
788 }
791 static function quota_to_image($use,$quota)
792 {
793 if($use == -1){
794 return(" "._("Unknown"));
795 }elseif(empty($quota)){
796 return(" "._("Unlimited"));
797 }else{
798 $usage =round(($use/$quota) * 100);
799 return("<img src='progress.php?x=100&y=17&p=$usage'>");
800 }
801 }
804 /*! \brief Returns the default sharedFolder ACLs for this method.
805 @return Array Returns an array containg acls for __member__ and __anyone__
806 */
807 public function getDefaultACLs()
808 {
809 $tmp = $this->default_acls;
810 if(!isset($tmp['__member__'])) $tmp['__member__'] = " ";
811 if(!isset($tmp['__anyone__'])) $tmp['__anyone__'] = " ";
812 return($tmp);
813 }
814 }
817 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
818 ?>