fixing DOM for new hooks + hook dom_del_mx in m_mail

This commit is contained in:
Benjamin Sonntag 2012-08-25 14:04:38 +00:00
parent e75fb7a79a
commit d4be305c6f
5 changed files with 95 additions and 113 deletions

View File

@ -117,7 +117,7 @@ if (!$res=$mail->get_details($mail_id)) {
} }
if (!$error) $error=_("Your email has been edited successfully"); if (!$error || !trim($error,"<br />")) $error=_("Your email has been edited successfully");
$_REQUEST["domain_id"]=$dom->get_domain_byname($res["domain"]); $_REQUEST["domain_id"]=$dom->get_domain_byname($res["domain"]);
include("mail_list.php"); include("mail_list.php");

View File

@ -106,7 +106,7 @@ if (isset($error)) {
<tr><td style="width: 50%; text-align: justify"><label for="recipients"><?php __("If you want to send emails received on this address to other addresses, even outside this server, enter those recipients here."); ?></label></td><td>(<?php __("one recipient per line"); ?>)<br /><textarea class="int" cols="32" rows="5" name="recipients" id="recipients"><?php echo $recipients; ?></textarea></td></tr> <tr><td style="width: 50%; text-align: justify"><label for="recipients"><?php __("If you want to send emails received on this address to other addresses, even outside this server, enter those recipients here."); ?></label></td><td>(<?php __("one recipient per line"); ?>)<br /><textarea class="int" cols="32" rows="5" name="recipients" id="recipients"><?php echo $recipients; ?></textarea></td></tr>
<?php <?php
$html=$hooks->invoke("mail_edit_html",array($mail_id,$type)); $html=$hooks->invoke("hook_mail_edit_html",array($mail_id,$type));
foreach($html as $h) echo $h; foreach($html as $h) echo $h;
?> ?>

View File

@ -1,10 +1,9 @@
<?php <?php
/* /*
$Id: m_admin.php,v 1.16 2006/02/09 20:12:22 benjamin Exp $
---------------------------------------------------------------------- ----------------------------------------------------------------------
AlternC - Web Hosting System AlternC - Web Hosting System
Copyright (C) 2006 Le réseau Koumbit Inc. Copyright (C) 2000-2012 by the AlternC Development Team.
http://koumbit.org/ https://alternc.org/
---------------------------------------------------------------------- ----------------------------------------------------------------------
LICENSE LICENSE
@ -20,10 +19,10 @@
To read the license please visit http://www.gnu.org/copyleft/gpl.html To read the license please visit http://www.gnu.org/copyleft/gpl.html
---------------------------------------------------------------------- ----------------------------------------------------------------------
Original Author of file: Benjamin Sonntag
Purpose of file: Administrate members and rights. Purpose of file: Administrate members and rights.
---------------------------------------------------------------------- ----------------------------------------------------------------------
*/ */
/* ----------------------------------------------------------------- */ /* ----------------------------------------------------------------- */
/** /**
* Classe de gestion de l'administration du serveur par les super-admin. * Classe de gestion de l'administration du serveur par les super-admin.
@ -49,14 +48,7 @@ class m_admin {
* the authorized TLDs. It's an array of strings explaining the current state of the TLD. * the authorized TLDs. It's an array of strings explaining the current state of the TLD.
*/ */
var $tldmode=array( public $tldmode=array();
0 => "This TLD is forbidden",
1 => "primary DNS is checked in WHOIS db",
2 => "primary & secondary DNS are checked in WHOIS db",
3 => "Domain must exist, but don't do any DNS check",
4 => "Domain can be installed, no check at all",
5 => "Domain can be installed, force NO DNS hosting",
);
/* ----------------------------------------------------------------- */ /* ----------------------------------------------------------------- */
/** /**
@ -67,6 +59,15 @@ class m_admin {
$db->query("SELECT su FROM membres WHERE uid='$cuid';"); $db->query("SELECT su FROM membres WHERE uid='$cuid';");
$db->next_record(); $db->next_record();
$this->enabled=$db->f("su"); $this->enabled=$db->f("su");
$this->tldmode=array(
0 => _("This TLD is forbidden"),
1 => _("primary DNS is checked in WHOIS db"),
2 => _("primary & secondary DNS are checked in WHOIS db"),
3 => _("Domain must exist, but don't do any DNS check"),
4 => _("Domain can be installed, no check at all"),
5 => _("Domain can be installed, force NO DNS hosting"),
);
} }
/* ----------------------------------------------------------------- */ /* ----------------------------------------------------------------- */
@ -1260,7 +1261,7 @@ EOF;
$logins[]=$login; $logins[]=$login;
foreach($logins as $l) { foreach($logins as $l) {
if (strpos($password,$l)!==false) { if (strpos($password,$l)!==false) {
$err->raise("admin",17); $err->raise("admin",_("The password policy prevents you to use your login name inside your password"));
return false; return false;
} }
} }

View File

@ -1,5 +1,9 @@
<?php <?php
/* /*
----------------------------------------------------------------------
AlternC - Web Hosting System
Copyright (C) 2000-2012 by the AlternC Development Team.
https://alternc.org/
---------------------------------------------------------------------- ----------------------------------------------------------------------
LICENSE LICENSE
@ -190,15 +194,9 @@ class m_dom {
$err->raise("dom", 26); $err->raise("dom", 26);
return false; return false;
} }
$name=mysql_real_escape_string($name); $name=mysql_real_escape_string($name); $description=mysql_real_escape_string($description); $target=mysql_real_escape_string($target);
$description=mysql_real_escape_string($description); $entry=mysql_real_escape_string($entry); $compatibility=mysql_real_escape_string($compatibility); $enable=mysql_real_escape_string($enable);
$target=mysql_real_escape_string($target); $only_dns=intval($only_dns); $need_dns=intval($need_dns); $advanced=intval($advanced);
$entry=mysql_real_escape_string($entry);
$compatibility=mysql_real_escape_string($compatibility);
$enable=mysql_real_escape_string($enable);
$only_dns=intval($only_dns);
$need_dns=intval($need_dns);
$advanced=intval($advanced);
$db->query("UPDATE domaines_type SET description='$description', target='$target', entry='$entry', compatibility='$compatibility', enable='$enable', need_dns=$need_dns, only_dns=$only_dns, advanced='$advanced' where name='$name';"); $db->query("UPDATE domaines_type SET description='$description', target='$target', entry='$entry', compatibility='$compatibility', enable='$enable', need_dns=$need_dns, only_dns=$only_dns, advanced='$advanced' where name='$name';");
return true; return true;
} }
@ -262,35 +260,26 @@ class m_dom {
* @return boolean Retourne FALSE si une erreur s'est produite, TRUE sinon. * @return boolean Retourne FALSE si une erreur s'est produite, TRUE sinon.
*/ */
function del_domain($dom) { function del_domain($dom) {
global $db,$err,$classes,$cuid; global $db,$err,$classes,$cuid,$hooks;
$err->log("dom","del_domain",$dom); $err->log("dom","del_domain",$dom);
$dom=strtolower($dom); $dom=strtolower($dom);
$db->query("SELECT * FROM domaines WHERE domaine='$dom';");
if ($db->num_rows()==0) { if (!$r=$this->get_domain_all($dom)) {
$err->raise("dom",1,$dom);
return false;
}
$db->next_record();
if ($db->f("compte")!=$cuid) {
$err->raise("dom",2,$dom);
return false; return false;
} }
// Call Hooks to delete the domain and the MX management:
// TODO : the 2 calls below are using an OLD hook call, FIXME: remove them when unused
$hooks->invoke("alternc_del_domain",array($dom));
$hooks->invoke("alternc_del_mx_domain",array($dom));
// New hook calls:
$hooks->invoke("hook_dom_del_domain",array($r["id"]));
$hooks->invoke("hook_dom_del_mx_domain",array($r["id"]));
// Now mark the domain for deletion:
$db->query("UPDATE sub_domaines SET web_action='DELETE' WHERE domaine='$dom';"); $db->query("UPDATE sub_domaines SET web_action='DELETE' WHERE domaine='$dom';");
$db->query("UPDATE domaines SET dns_action='DELETE' WHERE domaine='$dom';"); $db->query("UPDATE domaines SET dns_action='DELETE' WHERE domaine='$dom';");
// DEPENDANCE :
// Lancement de del_dom sur les classes domain_sensitive :
// Declenchons les autres classes.
foreach($classes as $c) {
if (method_exists($GLOBALS[$c],"alternc_del_domain")) {
$GLOBALS[$c]->alternc_del_domain($dom);
}
}
foreach($classes as $c) {
if (method_exists($GLOBALS[$c],"alternc_del_mx_domain")) {
$GLOBALS[$c]->alternc_del_mx_domain($dom);
}
}
return true; return true;
} }
@ -316,7 +305,7 @@ class m_dom {
$ @return boolean Retourne FALSE si une erreur s'est produite, TRUE sinon. $ @return boolean Retourne FALSE si une erreur s'est produite, TRUE sinon.
*/ */
function add_domain($domain,$dns,$noerase=0,$force=0,$isslave=0,$slavedom="") { function add_domain($domain,$dns,$noerase=0,$force=0,$isslave=0,$slavedom="") {
global $db,$err,$quota,$classes,$L_MX,$L_FQDN,$tld,$cuid,$bro; global $db,$err,$quota,$classes,$L_MX,$L_FQDN,$tld,$cuid,$bro,$hooks;
$err->log("dom","add_domain",$domain); $err->log("dom","add_domain",$domain);
// Locked ? // Locked ?
@ -332,7 +321,7 @@ class m_dom {
return false; return false;
} }
// Interdit les domaines clés (table forbidden_domains) sauf en cas FORCE // Interdit les domaines clés (table forbidden_domains) sauf en cas FORCE
$db->query("select domain from forbidden_domains where domain='$domain'"); $db->query("SELECT domain FROM forbidden_domains WHERE domain='$domain'");
if ($db->num_rows() && !$force) { if ($db->num_rows() && !$force) {
$err->raise("dom",22); $err->raise("dom",22);
return false; return false;
@ -386,7 +375,11 @@ class m_dom {
return false; return false;
} }
if ($noerase) $noerase="1"; else $noerase="0"; if ($noerase) $noerase="1"; else $noerase="0";
$db->query("insert into domaines (compte,domaine,gesdns,gesmx,noerase,dns_action) values ('$cuid','$domain','$dns','1','$noerase','UPDATE');"); $db->query("INSERT INTO domaines (compte,domaine,gesdns,gesmx,noerase,dns_action) VALUES ('$cuid','$domain','$dns','1','$noerase','UPDATE');");
if (!($id=$db->lastid())) {
$err->raise("dom",_("An unexpected error occured when creating the domain"));
return false;
}
if ($isslave) { if ($isslave) {
$isslave=true; $isslave=true;
@ -426,29 +419,22 @@ class m_dom {
$this->set_sub_domain($domain, 'mail', $this->type_webmail, ''); $this->set_sub_domain($domain, 'mail', $this->type_webmail, '');
} }
// DEPENDANCE : // TODO: Old hooks, FIXME: when unused remove them
// Lancement de add_dom sur les classes domain_sensitive : $hooks->invoke("alternc_add_domain",array($domain));
// Declenchons les autres classes. $hooks->invoke("alternc_add_mx_domain",array($domain));
foreach($classes as $c) {
if (method_exists($GLOBALS[$c],"alternc_add_domain")) {
$GLOBALS[$c]->alternc_add_domain($domain);
}
}
foreach($classes as $c) {
if (method_exists($GLOBALS[$c],"alternc_add_mx_domain")) {
$GLOBALS[$c]->alternc_add_mx_domain($domain);
}
}
if ($isslave) { if ($isslave) {
foreach($classes as $c) { $hooks->invoke("alternc_add_slave_domain",array($domain));
if (method_exists($GLOBALS[$c],"alternc_add_slave_domain")) {
$GLOBALS[$c]->alternc_add_slave_domain($domain,$slavedom);
}
} }
// New Hooks:
$hooks->invoke("hook_dom_add_domain",array($id));
$hooks->invoke("hook_dom_add_mx_domain",array($id));
if ($isslave) {
$hooks->invoke("hook_dom_add_slave_domain",array($id));
} }
return true; return true;
} }
/* ----------------------------------------------------------------- */ /* ----------------------------------------------------------------- */
/** /**
* Retourne les entrées DNS du domaine $domain issues du WHOIS. * Retourne les entrées DNS du domaine $domain issues du WHOIS.
@ -604,12 +590,12 @@ class m_dom {
} }
} // whois } // whois
/* ----------------------------------------------------------------- */ /* ----------------------------------------------------------------- */
/** /**
* vérifie la presence d'un champs mx valide sur un serveur DNS * vérifie la presence d'un champs mx valide sur un serveur DNS
* *
*/ */
function checkmx($domaine,$mx) { function checkmx($domaine,$mx) {
//initialise variables //initialise variables
$mxhosts = array(); $mxhosts = array();
@ -685,11 +671,11 @@ class m_dom {
return false; return false;
} }
$db->next_record(); $db->next_record();
$r["id"]=$db->Record["id"];
$r["dns"]=$db->Record["gesdns"]; $r["dns"]=$db->Record["gesdns"];
$r["dns_action"]=$db->Record["dns_action"]; $r["dns_action"]=$db->Record["dns_action"];
$r["dns_result"]=$db->Record["dns_result"]; $r["dns_result"]=$db->Record["dns_result"];
$r["mail"]=$db->Record["gesmx"]; $r["mail"]=$db->Record["gesmx"];
// $r["mx"]=$db->Record["mx"]; // le champs mx n'existe plus dans domaines
$r['noerase']=$db->Record['noerase']; $r['noerase']=$db->Record['noerase'];
$db->free(); $db->free();
$db->query("select count(*) as cnt from sub_domaines where compte='$cuid' and domaine='$dom'"); $db->query("select count(*) as cnt from sub_domaines where compte='$cuid' and domaine='$dom'");
@ -698,7 +684,6 @@ class m_dom {
$db->free(); $db->free();
$db->query("select sd.*, dt.description as type_desc, dt.only_dns from sub_domaines sd, domaines_type dt where compte='$cuid' and domaine='$dom' and upper(dt.name)=upper(sd.type) order by sd.sub,sd.type"); $db->query("select sd.*, dt.description as type_desc, dt.only_dns from sub_domaines sd, domaines_type dt where compte='$cuid' and domaine='$dom' and upper(dt.name)=upper(sd.type) order by sd.sub,sd.type");
// Pas de webmail, on le cochera si on le trouve. // Pas de webmail, on le cochera si on le trouve.
$this->webmail=0;
for($i=0;$i<$r["nsub"];$i++) { for($i=0;$i<$r["nsub"];$i++) {
$db->next_record(); $db->next_record();
$r["sub"][$i]=array(); $r["sub"][$i]=array();
@ -709,12 +694,6 @@ class m_dom {
$r["sub"][$i]["type_desc"]=$db->Record["type_desc"]; $r["sub"][$i]["type_desc"]=$db->Record["type_desc"];
$r["sub"][$i]["only_dns"]=$db->Record["only_dns"]; $r["sub"][$i]["only_dns"]=$db->Record["only_dns"];
$r["sub"][$i]["web_action"]=$db->Record["web_action"]; $r["sub"][$i]["web_action"]=$db->Record["web_action"];
/*
if ($db->Record["type"]==3) { // Webmail
$this->webmail=1;
$r["sub"][$i]["dest"]=_("Webmail access");
}
*/
} }
$db->free(); $db->free();
return $r; return $r;
@ -748,14 +727,6 @@ class m_dom {
$err->raise("dom",3+$t); $err->raise("dom",3+$t);
return false; return false;
} }
/*
if ( ! empty($value)) {
$type = " and valeur=\"".mysql_real_escape_string($value)."\"";
}
if ( ! empty($type)) {
$type = " and type=\"".mysql_real_escape_string($type)."\"";
}
*/
$db->query("select sd.*, dt.description as type_desc, dt.only_dns from sub_domaines sd, domaines_type dt where compte='$cuid' and domaine='$dom' and sub='$sub' and ( length('$type')=0 or type='$type') and (length('$value')=0 or '$value'=valeur) and upper(dt.name)=upper(sd.type);"); $db->query("select sd.*, dt.description as type_desc, dt.only_dns from sub_domaines sd, domaines_type dt where compte='$cuid' and domaine='$dom' and sub='$sub' and ( length('$type')=0 or type='$type') and (length('$value')=0 or '$value'=valeur) and upper(dt.name)=upper(sd.type);");
if ($db->num_rows()==0) { if ($db->num_rows()==0) {
$err->raise("dom",14); $err->raise("dom",14);
@ -778,7 +749,6 @@ class m_dom {
global $db,$err,$cuid; global $db,$err,$cuid;
// check the type we can have in domaines_type.target // check the type we can have in domaines_type.target
switch ($this->domains_type_target_values($type)) { switch ($this->domains_type_target_values($type)) {
case 'NONE': case 'NONE':
if (empty($value) or is_null($value)) {return true;} if (empty($value) or is_null($value)) {return true;}
@ -846,7 +816,7 @@ class m_dom {
return true; return true;
} }
// /* ----------------------------------------------------------------- */ /* ----------------------------------------------------------------- */
/** /**
* Modifier les information du sous-domaine demandé. * Modifier les information du sous-domaine demandé.
* *
@ -952,6 +922,7 @@ class m_dom {
return true; return true;
} // del_sub_domain } // del_sub_domain
/* ----------------------------------------------------------------- */ /* ----------------------------------------------------------------- */
/** /**
* Modifie les information du domaine précisé. * Modifie les information du domaine précisé.
@ -966,7 +937,7 @@ class m_dom {
* *
*/ */
function edit_domain($dom,$dns,$gesmx,$force=0) { function edit_domain($dom,$dns,$gesmx,$force=0) {
global $db,$err,$L_MX,$classes,$cuid; global $db,$err,$L_MX,$classes,$cuid,$hooks;
$err->log("dom","edit_domain",$dom."/".$dns."/".$gesmx); $err->log("dom","edit_domain",$dom."/".$dns."/".$gesmx);
// Locked ? // Locked ?
if (!$this->islocked && !$force) { if (!$this->islocked && !$force) {
@ -1022,24 +993,18 @@ class m_dom {
} }
} }
// OK, des modifs ont été faites, on valide : if ($gesmx && !$r["mail"]) {
// DEPENDANCE : // TODO: old hooks, FIXME: remove when unused
if ($gesmx && !$r["mail"]) { // on a associé le MX : on cree donc l'entree dans MySQL $hooks->invoke("alternc_add_mx_domain",array($domain));
// Lancement de add_dom sur les classes domain_sensitive : // New Hooks:
foreach($classes as $c) { $hooks->invoke("hook_dom_add_mx_domain",array($r["id"]));
if (method_exists($GLOBALS[$c],"alternc_add_mx_domain")) {
$GLOBALS[$c]->alternc_add_mx_domain($dom);
}
}
} }
if (!$gesmx && $r["mail"]) { // on a dissocié le MX : on détruit donc l'entree dans LDAP if (!$gesmx && $r["mail"]) { // on a dissocié le MX : on détruit donc l'entree dans LDAP
// Lancement de del_dom sur les classes domain_sensitive : // TODO: old hooks, FIXME: remove when unused
foreach($classes as $c) { $hooks->invoke("alternc_del_mx_domain",array($domain));
if (method_exists($GLOBALS[$c],"alternc_del_mx_domain")) { // New Hooks:
$GLOBALS[$c]->alternc_del_mx_domain($dom); $hooks->invoke("hook_dom_del_mx_domain",array($r["id"]));
}
}
} }
$db->query("UPDATE domaines SET gesdns='$dns', gesmx='$gesmx' WHERE domaine='$dom'"); $db->query("UPDATE domaines SET gesdns='$dns', gesmx='$gesmx' WHERE domaine='$dom'");

View File

@ -124,7 +124,7 @@ class m_mail {
* @param $dom_id integer the domain id. * @param $dom_id integer the domain id.
* @param $search string search that string in recipients or address. * @param $search string search that string in recipients or address.
* @param $offset integer skip THAT much emails in the result. * @param $offset integer skip THAT much emails in the result.
* @param $count integer return no more than THAT much emails. * @param $count integer return no more than THAT much emails. -1 for ALL. Offset is ignored then.
* @result an array of each mail hosted under the domain. * @result an array of each mail hosted under the domain.
*/ */
function enum_domain_mails($dom_id = null, $search="", $offset=0, $count=30){ function enum_domain_mails($dom_id = null, $search="", $offset=0, $count=30){
@ -138,11 +138,10 @@ class m_mail {
$db->query("SELECT count(a.id) AS total FROM address a LEFT JOIN recipient r ON r.address_id=a.id WHERE $where;"); $db->query("SELECT count(a.id) AS total FROM address a LEFT JOIN recipient r ON r.address_id=a.id WHERE $where;");
$db->next_record(); $db->next_record();
$this->total=$db->f("total"); $this->total=$db->f("total");
if ($count!=-1) $limit="LIMIT $offset,$count"; else $limit="";
$db->query("SELECT a.id, a.address, a.password, a.`enabled`, a.mail_action, d.domaine AS domain, m.quota, m.quota*1024*1024 AS quotabytes, m.bytes AS used, NOT ISNULL(m.id) AS islocal, a.type, r.recipients, m.lastlogin $db->query("SELECT a.id, a.address, a.password, a.`enabled`, a.mail_action, d.domaine AS domain, m.quota, m.quota*1024*1024 AS quotabytes, m.bytes AS used, NOT ISNULL(m.id) AS islocal, a.type, r.recipients, m.lastlogin
FROM (address a LEFT JOIN mailbox m ON m.address_id=a.id) LEFT JOIN recipient r ON r.address_id=a.id, domaines d FROM (address a LEFT JOIN mailbox m ON m.address_id=a.id) LEFT JOIN recipient r ON r.address_id=a.id, domaines d
WHERE $where AND d.id=a.domain_id WHERE $where AND d.id=a.domain_id $limit;");
LIMIT $offset,$count;");
if (! $db->next_record()) { if (! $db->next_record()) {
$err->raise("mail",_("No mail found for this query")); $err->raise("mail",_("No mail found for this query"));
return false; return false;
@ -152,7 +151,7 @@ class m_mail {
$details=$db->Record; $details=$db->Record;
// if necessary, fill the typedata with data from hooks ... // if necessary, fill the typedata with data from hooks ...
if ($details["type"]) { if ($details["type"]) {
$result=$hooks->invoke("mail_get_details",array($details["id"])); // Will fill typedata if necessary $result=$hooks->invoke("hook_mail_get_details",array($details["id"])); // Will fill typedata if necessary
$details["typedata"]=implode("<br />",$result); $details["typedata"]=implode("<br />",$result);
} }
$res[]=$details; $res[]=$details;
@ -192,7 +191,7 @@ class m_mail {
} }
// Call other classes to check we can create it: // Call other classes to check we can create it:
$cancreate=$hooks->invoke('hooks_mail_cancreate',array($dom_id,$domain,$mail)); $cancreate=$hooks->invoke("hook_mail_cancreate",array($dom_id,$mail));
if (in_array(false,$cancreate,true)) { if (in_array(false,$cancreate,true)) {
return false; return false;
} }
@ -239,7 +238,7 @@ class m_mail {
$details=$db->Record; $details=$db->Record;
// if necessary, fill the typedata with data from hooks ... // if necessary, fill the typedata with data from hooks ...
if ($details["type"]) { if ($details["type"]) {
$result=$hooks->invoke("mail_get_details",array($mail_id)); // Will fill typedata if necessary $result=$hooks->invoke("hook_mail_get_details",array($mail_id)); // Will fill typedata if necessary
$details["typedata"]=implode("<br />",$result); $details["typedata"]=implode("<br />",$result);
} }
return $details; return $details;
@ -271,6 +270,22 @@ class m_mail {
} }
/* ----------------------------------------------------------------- */
/** Hook called when the DOMAIN class will delete a domain.
*
* @param $dom integer the number of the email to delete
* @return true if the email has been properly deleted
* or false if an error occured ($err is filled accordingly)
*/
function hook_dom_del_mx_domain($dom_id) {
$list=$this->enum_domain_mails($dom_id,"",0,-1);
foreach($list as $one) {
$this->delete($one["id"]);
}
return true;
}
/* ----------------------------------------------------------------- */ /* ----------------------------------------------------------------- */
/** Function used to delete a mail from the db /** Function used to delete a mail from the db
* should be used by the web interface, not by third-party programs. * should be used by the web interface, not by third-party programs.
@ -478,7 +493,8 @@ class m_mail {
$red=""; $red="";
foreach($r as $m) { foreach($r as $m) {
$m=trim($m); $m=trim($m);
if ($m && filter_var($m,FILTER_VALIDATE_EMAIL)) { if ($m && filter_var($m,FILTER_VALIDATE_EMAIL) // Recipient Email is valid
&& $m!=($me["address"]."@".$me["domain"])) { // And not myself (no loop allowed easily ;) )
$red.=$m."\n"; $red.=$m."\n";
} }
} }